[Java] Spring Boot 2.x + Java 11 환경에서 JPA Native Query와 DTO 매핑 문제 해결 기록

2025. 8. 3. 04:30·개발 (Development)/Java

개요

Spring Boot 2.x와 Java 11 환경에서 JPA와 MyBatis를 병행 사용하던 중, Native Query 처리 과정에서 다양한 오류가 발생했습니다. 특히 JPA 엔티티 인식 오류, Optional<Object[]> 처리의 불편함, 패키지 스캔 설정 문제, QueryDSL 관련 빈 생성 오류 등이 복합적으로 발생했습니다. 본 글에서는 이러한 문제들의 원인을 차례대로 분석하고 해결 방법을 정리합니다.

엔티티가 Not a managed type 오류 발생

서비스 클래스에서 JPA Repository를 주입하려 할 때 다음과 같은 오류가 발생했습니다.

Not a managed type: class com.example.domain.entity.ScoreEntity

이 오류는 Spring이 해당 엔티티 클래스를 JPA에서 관리되는 대상으로 인식하지 못할 때 발생합니다.

해결 방법

  1. 엔티티 클래스에 @Entity와 @Table을 선언하고, import는 반드시 javax.persistence.*로 해야 합니다. (Spring Boot 2.x 기준)
import javax.persistence.*;

@Entity
@Table(name = "scores")
public class ScoreEntity {
    @Id
    private Long id;

    @Column(name = "score")
    private Double score;

    // 필요한 컬럼만 선언 가능
}
  1. @EntityScan 설정을 통해 엔티티 패키지가 스캔 대상에 포함되도록 합니다.
@SpringBootApplication
@EnableJpaRepositories(basePackages = "com.example.repository")
@EntityScan(basePackages = "com.example.domain.entity")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

테이블 컬럼을 전부 매핑해야 하는가?

JPA에서는 엔티티 클래스에 테이블의 모든 컬럼을 매핑할 필요는 없습니다. 필요한 컬럼만 골라 선언하면 됩니다. 단, 컬럼을 사용하는 쿼리나 DML에서 누락된 컬럼이 필요할 경우 오류가 발생할 수 있으므로 주의가 필요합니다.

또한, 컬럼 자료형은 가급적 데이터베이스와 일치시켜야 합니다. 예를 들어 DOUBLE 타입은 Double, VARCHAR는 String, TIMESTAMP는 LocalDateTime으로 매핑하는 것이 일반적입니다.

Optional<Object[]> 방식의 단점

Native Query를 사용할 때 결과를 Optional<Object[]>로 받는 방식은 매우 단순하지만 다음과 같은 문제가 있습니다.

  • 컬럼 순서에 의존하며, 인덱스 접근 실수가 발생하기 쉬움
  • 자료형 캐스팅 실수 가능
  • 유지보수가 어려움
Object[] result = repository.findTopScore().get();
String name = (String) result[0];
Double score = (Double) result[1];

DTO 매핑으로 대체하기

보다 안전하고 명확한 방식으로는 DTO 매핑이 있습니다. JPA에서는 세 가지 방식으로 DTO 매핑을 지원합니다.

1. 생성자 기반 DTO 매핑 (JPQL)

public class ScoreDto {
    private String name;
    private Double score;

    public ScoreDto(String name, Double score) {
        this.name = name;
        this.score = score;
    }

    // getter 생략
}
@Query("SELECT new com.example.dto.ScoreDto(s.name, s.score) FROM ScoreEntity s WHERE s.score > :threshold")
Optional<ScoreDto> findTopScore(@Param("threshold") Double threshold);

2. 인터페이스 기반 Projection

public interface ScoreProjection {
    String getName();
    Double getScore();
}
@Query("SELECT s.name AS name, s.score AS score FROM ScoreEntity s WHERE s.score > :threshold")
Optional<ScoreProjection> findTopScore(@Param("threshold") Double threshold);

3. Native Query와 DTO 매핑 (복잡한 경우)

Native Query를 사용할 경우 DTO 매핑을 하려면 @SqlResultSetMapping과 @NamedNativeQuery를 사용하는 방식도 있습니다. 그러나 설정이 복잡하고 유지보수가 어려워 실무에서는 생성자 기반 또는 Projection 방식이 더 많이 사용됩니다.

jakarta.persistence.* vs javax.persistence.*

Spring Boot 2.x에서는 반드시 javax.persistence.*를 사용해야 합니다. jakarta.persistence.*는 Spring Boot 3.x 이상에서 사용하는 네임스페이스로, Java EE → Jakarta EE로 넘어가면서 변경된 것입니다. Spring Boot 2.x에서는 jakarta.*를 사용할 경우 인식되지 않아 오류가 발생합니다.

// 잘못된 예시 (Spring Boot 2.x)
import jakarta.persistence.*;

// 올바른 예시
import javax.persistence.*;

JPA와 MyBatis 병행 시 주의사항

MyBatis가 JPA Repository를 잘못 스캔하게 되면 다음과 같은 오류가 발생할 수 있습니다.

Invalid bound statement (not found): com.example.repository.ScoreRepository.findTopScore

이는 @MapperScan이 JPA Repository까지 스캔하면서 발생한 문제입니다. 이를 해결하려면 MyBatis와 JPA의 스캔 대상을 명확히 분리해야 합니다.

@MapperScan(basePackages = "com.example.mapper")
@EnableJpaRepositories(basePackages = "com.example.repository")

QueryDSL 사용하지 않는데 오류 발생

Spring Boot 프로젝트에서 querydslPredicateArgumentResolver 관련 오류가 발생할 경우, QueryDSL 의존성이 누락되었거나, 필요하지 않은데 Spring이 자동으로 설정하려는 경우입니다.

QueryDSL을 사용하지 않는다면 관련 설정 및 의존성을 제거하는 것이 가장 깔끔합니다. 사용하고자 한다면 의존성 추가 및 Q 클래스 생성, annotation processing 활성화가 필요합니다.

마무리 정리

문제 상황 주요 원인 해결 방법
엔티티 인식 실패 @Entity 누락, jakarta import javax.persistence 사용, @EntityScan 설정
Optional<Object[]> 불편 인덱스 접근, 타입 불명확 DTO 또는 Projection 매핑 사용
컬럼 일부 누락 가능 여부 가능 자료형 정확히 맞춰서 필요한 컬럼만 선언
JPA + MyBatis 충돌 @MapperScan 범위 겹침 스캔 경로 명확히 분리
QueryDSL 관련 오류 자동 설정 충돌 의존성 제거 또는 설정 보완
반응형

'개발 (Development) > Java' 카테고리의 다른 글

[Java] MyBatis만 쓰던 내가 JPA를 처음 접했을 때 이해한 구조 정리  (0) 2025.08.03
[Java] Spring Boot 프로젝트에서 MyBatis와 JPA를 함께 사용하는 방법과 오류 해결 과정  (0) 2025.08.03
[Java] PostgreSQL의 timestamptz를 Java MyBatis에서 Instant로 받는 방법  (2) 2025.07.28
[Java] ISO 8601 형식의 시간 출력하기 (현재 시간과 과거 시간 구하기)  (2) 2025.07.20
[Java] MyBatis foreach에서 빈 배열이 들어올 경우 예외를 방지하는 방법  (2) 2025.07.20
'개발 (Development)/Java' 카테고리의 다른 글
  • [Java] MyBatis만 쓰던 내가 JPA를 처음 접했을 때 이해한 구조 정리
  • [Java] Spring Boot 프로젝트에서 MyBatis와 JPA를 함께 사용하는 방법과 오류 해결 과정
  • [Java] PostgreSQL의 timestamptz를 Java MyBatis에서 Instant로 받는 방법
  • [Java] ISO 8601 형식의 시간 출력하기 (현재 시간과 과거 시간 구하기)
LoopThinker
LoopThinker
모르는 것을 알아가고, 아는 것을 더 깊게 파고드는 공간
  • LoopThinker
    CodeMemoir
    LoopThinker
  • 전체
    오늘
    어제
    • 분류 전체보기 (234) N
      • 개발 (Development) (167) N
        • Algorithm (1)
        • Angular (1)
        • AWS (7) N
        • DeepSeek (2)
        • Docker (7)
        • Git (3)
        • Java (34)
        • JavaScript (4)
        • Kafka (5)
        • Kubernetes (4)
        • Linux (7)
        • PostgreSQL (38)
        • Python (32) N
        • React (3)
        • TypeScript (3)
        • Vue.js (5)
        • General (11)
      • 데이터 분석 (Data Analysis) (1)
      • 알고리즘 문제 풀이 (Problem Solving.. (27)
      • 자격증 (Certifications) (24)
        • ADsP (14)
        • 정보처리기사 (4)
        • Linux Master (5)
        • SQLD (1)
      • 기술 동향 (Tech Trends) (12) N
      • 기타 (Others) (3)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    백준
    리눅스 마스터 2급
    백준알고리즘
    PostgreSQL
    python
    Linux master
    AWS
    timescaledb
    Kubernetes
    오답노트
    백준온라인저지
    Linux
    백준자바
    MyBatis
    Vue.js
    자바
    pandas
    JPA
    springboot
    파이썬
    데이터분석
    java
    리눅스 마스터 2급 2차
    javascript
    ADsP
    DevOps
    JSON
    Kafka
    deepseek
    docker
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
LoopThinker
[Java] Spring Boot 2.x + Java 11 환경에서 JPA Native Query와 DTO 매핑 문제 해결 기록
상단으로

티스토리툴바