본문 바로가기
SW개발/JAVA

[DB연동] Spring Data JPA 기본부터 실전 적용까지 정리 (feat. QueryDsl)

by ICT 인사이트 2025. 4. 16.
728x90

복잡한 SQL 없이 객체로 DB를 다루고 싶으신가요? Spring Data JPA와 QueryDsl을 잘 활용하면, 실무 생산성과 유지보수성이 모두 올라갑니다.

안녕하세요, Java 11과 Spring Boot 기반으로 백엔드 개발을 하고 있는 실무 개발자입니다. 이번 포스팅에서는 많은 백엔드 개발자들이 사용하는 ORM(Object Relational Mapping) 프레임워크인 Spring Data JPA의 핵심 개념부터 실무 적용까지 체계적으로 정리해드리겠습니다.

또한 정적 타입 기반의 동적 쿼리 작성이 가능한 QueryDsl까지 함께 다루며, 기본적인 CRUD뿐만 아니라 복잡한 검색 조건도 깔끔하게 처리하는 실전 예시까지 함께 소개합니다.

1. JPA란 무엇인가? – ORM 기본 개념

JPA(Java Persistence API)는 자바 객체와 데이터베이스 테이블 간의 매핑을 자동으로 처리해주는 ORM(Object-Relational Mapping) 기술입니다. SQL을 직접 작성하지 않고, 자바 코드로 데이터를 저장, 조회, 수정, 삭제할 수 있어 생산성과 유지보수성이 매우 높습니다.

Spring Boot에서는 기본적으로 Hibernate를 JPA 구현체로 사용하며, @Entity, JpaRepository, @Id 등의 애노테이션 기반으로 간단하게 설정할 수 있습니다.

환한 미소로 강의하는 20대 한국 여성 전문강사의 생동감 있는 모습
환한 미소로 Java 열강중인 강사의 생동감 있는 모습

2. Spring Boot에서 JPA 설정과 연동

Spring Boot에서는 spring-boot-starter-data-jpa만 의존성에 추가하면 기본적인 설정이 자동으로 이루어집니다. application.yml 또는 application.properties에 다음과 같이 데이터베이스 설정을 추가하면 됩니다.


spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: root
    password: yourpassword
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    properties:
      hibernate:
        format_sql: true

ddl-auto 옵션은 개발 단계에서는 update, 운영에서는 validate 또는 none 사용을 권장합니다.

여기까지 완료되면 Spring Boot 애플리케이션에서 JPA를 사용할 준비가 된 것입니다. 이제 Entity를 정의하고, Repository를 통해 데이터를 쉽게 다룰 수 있습니다.

3. 기본 CRUD 예제와 Repository 사용

Spring Data JPA는 JpaRepository를 상속하는 인터페이스만 정의해도 자동으로 CRUD 기능을 제공합니다. 다음은 회원 엔티티에 대한 예제입니다.

@Entity
public class Member {
    @Id
    @GeneratedValue
    private Long id;

    private String name;
    private int age;
}
public interface MemberRepository extends JpaRepository<Member, Long> {
    List<Member> findByName(String name);
}

위와 같이 설정하면 기본적인 save(), findById(), delete() 등의 기능은 물론, 메서드 이름 기반 쿼리 생성도 가능합니다.

4. QueryDsl 도입 및 동적 검색 구현

복잡한 조건 검색이 필요한 경우, QueryDsl을 통해 정적 타입 기반으로 안전한 쿼리를 만들 수 있습니다. 의존성을 추가하고, Q클래스를 생성하여 다음과 같이 사용합니다.

@Autowired
private JPAQueryFactory queryFactory;

public List<Member> search(String name, int age) {
    QMember m = QMember.member;
    return queryFactory
            .selectFrom(m)
            .where(
                m.name.eq(name),
                m.age.goe(age)
            )
            .fetch();
}

✅ QueryDsl은 타입 안정성동적 쿼리 구현에 강력한 장점을 제공하며, 실무에서 매우 유용하게 사용됩니다.

5. 자주 묻는 질문 (FAQ)

Q Spring Data JPA는 Hibernate와 다른 건가요?

Spring Data JPA는 JPA를 쉽게 사용하기 위한 추상화 도구이고, Hibernate는 JPA의 구현체입니다. 즉, Spring Data JPA는 Hibernate를 내부적으로 사용합니다.

Q QueryDsl을 쓰려면 Q클래스는 어떻게 생성하나요?

Gradle이나 Maven 빌드 시 annotationProcessor를 이용해 자동 생성합니다. 예: implementation 'com.querydsl:querydsl-jpa' + annotationProcessor 설정 필요.

Q JPA를 쓰면 꼭 DTO와 Entity를 나눠야 하나요?

네, 유지보수성과 계층 간 분리를 위해 DTO를 별도로 사용하는 것이 권장됩니다. 특히 QueryDsl의 프로젝션 기능으로 DTO에 직접 조회도 가능합니다.

Q @Query 어노테이션과 QueryDsl, 어떤 걸 선택해야 하나요?

간단한 정적 쿼리는 @Query도 충분히 좋습니다. 하지만 조건이 많아지거나 복잡해질 경우, 타입 안정성과 유지보수성을 위해 QueryDsl을 사용하는 것이 훨씬 유리합니다.

6. 실무에서 겪은 JPA 성능 이슈 & 대응법

JPA는 매우 편리하지만, 무분별하게 사용하거나 내부 동작을 이해하지 못하면 심각한 성능 저하가 발생할 수 있습니다. 실무에서 자주 겪는 성능 이슈와 그 해결 방안을 소개합니다.

1) N+1 문제

지연 로딩(LAZY) 상태에서 연관 객체를 순회하며 조회할 경우, 쿼리가 예상보다 많이 나가는 현상입니다.


List members = memberRepository.findAll();
for (Member m : members) {
    System.out.println(m.getTeam().getName()); // 여기서 팀 정보 조회 시 쿼리 반복 발생
}

✅ 해결: fetch join 사용


@Query("select m from Member m join fetch m.team")
List findAllWithTeam();

2) 트랜잭션 내 불필요한 flush/clear

EntityManager의 flush/clear를 반복 호출할 경우, 불필요한 SQL 동기화가 일어나 성능이 저하됩니다.

💡 해결: flush/clear는 대량 처리 시점에만 제한적으로 호출하고, 트랜잭션 범위를 넓게 잡는 것이 좋습니다.

3) OneToMany 컬렉션 조회 시 비효율 쿼리

컬렉션을 fetch join으로 조회하면 데이터가 중복되어 반환될 수 있으므로, distinct 사용 또는 별도 DTO 분리 필요

📌 JPA는 강력하지만, 반드시 내부 동작을 이해하고 쿼리 튜닝을 병행해야 실무에서 안정적으로 운용할 수 있습니다.

 

Spring Data JPA는 자바 백엔드 개발에서 가장 널리 쓰이는 ORM 프레임워크 중 하나입니다. 기본적인 CRUD는 물론, QueryDsl을 함께 활용하면 유지보수가 편리하고 성능이 좋은 애플리케이션을 만들 수 있습니다. 이번 글을 통해 기본 개념부터 실전까지 하나씩 익히셨다면, 이제 실무에서 바로 적용해보시기 바랍니다.

728x90