본문 바로가기
dev/프로젝트

[프로젝트] QueryDSL 적용

by dev-everyday 2025. 1. 29.
반응형

1. QueryDSL 적용하기

QueryDSL은 자바에서 동적으로 타입 안전한 SQL 쿼리를 생성하는 라이브러리다.

이는 주로 JPA와 함께 사용되며 기존의 JPQL을 대체하거나 보완하는 역할을 한다.

QueryDSL은 컴파일 타입에 문법 오류를 검출할 수 있어서 코드를 안전하게 작성할 수 있다.

이 라이브러리는 동적인 쿼리 생성, JOIN, 조건문 등을 유연하게 작성하게 해준다.

정리하자면, QueryDSL을 사용하는 이유는 타입 안전성과 동적 쿼리 생성, 유연한 쿼리 작성이라고 할 수 있다.

 

1) QueryDSL 설정

먼저 build.gradle에 QueryDSL 의존성을 추가한다.

annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"

implementation "com.querydsl:querydsl-jpa:5.0.0:jakarta"

 

그리고 아래 clean과 tasks.withType을 설정하자.

def querydslSrcDir = 'src/main/generated'

tasks.withType(JavaCompile) {
    options.generatedSourceOutputDirectory = file(querydslSrcDir)
}

clean {
    delete file('src/main/generated')
}

2) JPA 엔티티 클래스와 Q타입 생성

QueryDSL은 Q타입을 자동으로 생성한다.

User라는 엔티티가 있으면 QUser라는 클래스가 자동으로 생성된다.

이 클래스는 User 엔티티의 각 필드를 쿼리에서 사용할 수 있는 객체로 제공한다.

나는 QUser를 인식을 못해서 import 에러가 발생했는데 아래와 같이 해결하였다.

1) File > Invalidate Caches / Restart 선택 > IntelliJ 재시작 후 프로젝트 Rebuild하기

2) File > Project Structure > Source > build/generated 클릭 > Source로 설정하기

3) Repository에서 QueryDSL 사용

먼저 UserRepositoryCustom 인터페이스를 작성한다.

package com.fortune.app.user.repository;

import com.fortune.app.user.entity.User;

import java.util.Optional;

public interface UserRepositoryCustom {

    Optional<User> findByUserIdQueryDSL(Long userId);

    Optional<User> findByEmailQueryDSL(String email);

    boolean existsByNicknameQueryDSL(String nickname);

    boolean existsByUserIdQueryDSL(Long userId);

    void deleteByUserIdQueryDSL(Long userId);
}

 

그리고 구현체인 UserRepositoryCustomImpl을 생성한다.

package com.fortune.app.user.repository;

import com.fortune.app.user.entity.QUser;
import com.fortune.app.user.entity.User;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import jakarta.transaction.Transactional;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public class UserRepositoryCustomImpl implements UserRepositoryCustom {

    private final JPAQueryFactory queryFactory;

    public UserRepositoryCustomImpl(EntityManager entityManager) {
        this.queryFactory = new JPAQueryFactory(entityManager);
    }

    @Override
    public Optional<User> findByUserIdQueryDSL(Long userId) {
        QUser user = QUser.user;
        return Optional.ofNullable(
                queryFactory.selectFrom(user)
                        .where(user.userId.eq(userId))
                        .fetchOne()
        );
    }

    @Override
    public boolean existsByNicknameQueryDSL(String nickname) {
        QUser user = QUser.user;
        return queryFactory.select(user.userId)
                .from(user)
                .where(user.nickname.eq(nickname))
                .fetchFirst() != null;
    }

    @Override
    public Optional<User> findByEmailQueryDSL(String email) {
        QUser user = QUser.user;
        return Optional.ofNullable(
                queryFactory.selectFrom(user)
                        .where(user.email.eq(email))
                        .fetchOne()
        );
    }

    @Override
    public boolean existsByUserIdQueryDSL(Long userId) {
        QUser user = QUser.user;
        return queryFactory.select(user.userId)
                .from(user)
                .where(user.userId.eq(userId))
                .fetchFirst() != null;
    }

    @Override
    @Transactional
    public void deleteByUserIdQueryDSL(Long userId) {
        QUser user = QUser.user;
        queryFactory.delete(user)
                .where(user.userId.eq(userId))
                .execute();
    }
}

4) Service에서 Repository 호출

기존 서비스에서 사용하던 부분들을 queryDSL로 변경해준다.

Optional<User> oauthUser = userRepository.findByUserIdQueryDSL(dto.getUserId());

 

2. JPQL vs QueryDSL

JPQL(Java Persistence Query Language)은 JPA에서 지원하는 SQL과 유사한 쿼리 언어로 엔티티 객체를 대상으로 하는 쿼리다.

테이블이 아닌 엔티티를 대상의로 query를 생성하는 방식이다.

@Query("SELECT u FROM User u WHERE u.email = :email")
Optional<User> findByEmail(@Param("email") String email);

 

QueryDSL은 Java Java 코드 기반으로 타입 세이프한 방식으로 JPA 쿼리를 작성할 수 있도록 지원하는 라이브러리다

즉, JPQL을 SQL처럼 문자열로 작성하는 것이 아니라, Java 코드로 쿼리를 생성한다.

QUser user = QUser.user;

Optional<User> findByEmailQueryDSL(String email) {
    return Optional.ofNullable(
        queryFactory.selectFrom(user)
                    .where(user.email.eq(email))
                    .fetchOne()
    );
}

QUser 클래스를 사용하여 객체지향적인 방식으로 쿼리를 작성한다.

QueryDSL을 사용하는 이유는 문자열이 아닌 Java 코드 기반이므로 타입 안정성이 높고 유지보수성이 좋고 동적 쿼리 작성이 쉽고, 조건을 쉽게 추가할 수 있고 컴파일 타임에 문법 오류를 잡을 수 있기 때문이다.

반응형