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 코드 기반이므로 타입 안정성이 높고 유지보수성이 좋고 동적 쿼리 작성이 쉽고, 조건을 쉽게 추가할 수 있고 컴파일 타임에 문법 오류를 잡을 수 있기 때문이다.
'dev > 프로젝트' 카테고리의 다른 글
[프로젝트] JWT + Redis 전환 (4) | 2025.02.06 |
---|---|
[프로젝트] 테스트 코드 적용 (5) | 2025.01.30 |
[프로젝트] README 적용 (4) | 2025.01.28 |
[프로젝트] Spring Security (2) | 2025.01.28 |
[프로젝트] CICD (Github Actions, EC2, Docker, Putty, SSH) 적용 (9) | 2025.01.23 |