공부/Spring Boot

[AWS + Spring Boot ] 스프링부트 QueryDSL 연동 배포

jihyee 2021. 10. 31. 03:10

드디어 spring boot 에 querydsl 적용하고 배포까지 구현했다.

 

 

 

 

 

나중에 또 하면 1시간 넘게 헤맬 수 있으니 기억이 온전할 때 바로 내용 정리해놓고 두고두고 공부해야겠다.

 

 

 

 

๑・̑◡・̑๑

 

 

 

querydsl 이란?

객체 지향 언어는 엔티티 타고 타고 들어가는게 가능한데 (객체 지향의 장점 중 하나...)

관계형 데이터베이스는 그런 기능은 지원하지 않는다.

 

 

결국 검색 조건을 넣으려면 모든 DB 데이터를 엔티티화 해야 한다는 뜻인데 그건 불가능할 수야 있겠지만.. 

그렇기 때문에 애플리케이션이 필요한 데이터만 DB에서 불러오려면 결국 검색 조건이 포함된 SQL이 필요하다.

 

다행히 (?) JPA는 SQL을 추상화한 JPQL (객체 지향 쿼리 언어)이 있는데 JPQL를 쉽게 만들어주는 방식 중 하나가 QueryDSL이다.

 

  • JPQL
  • JPA Criteria
  • QueryDSL
  • 네이티브 SQL
  • JDBC API 직접 사용, MyBatis, SpringJdbcTemplate 함께 사용

 

 

 

 

querydsl 사용하기

사용 전 몇 가지 설정이 선행되어야 한다. 

maven 기반 레퍼런스가 압도적으로 많아서 gradle 설정 맞추기가 쉽진 않았었다.. ㅎ 

 

 

build.gradle 설정

plugins {
	id 'org.springframework.boot' version '2.5.4'
	id 'io.spring.dependency-management' version '1.0.11.RELEASE'
	id 'java'
	id 'idea'
	id 'com.ewerk.gradle.plugins.querydsl' version '1.0.10'
}

group = {프로젝트 그룹명}
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
	querydsl {
		extendsFrom compileClasspath
	}
}

def queryDslDir = "$buildDir/generated/sources/annotationProcessor/java/main"

querydsl {
	jpa = true
	querydslSourcesDir = queryDslDir
}

sourceSets {
	main {
		java {
			srcDir queryDslDir
		}
	}
}

compileQuerydsl {
	options.annotationProcessorPath = configurations.querydsl
}

repositories {
	mavenCentral()
}
dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-jdbc'
	implementation 'org.springframework.boot:spring-boot-starter-mustache'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation group: 'org.modelmapper', name: 'modelmapper', version: '2.3.8'
	implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
	implementation 'org.springframework.boot:spring-boot-starter-validation'
	implementation 'junit:junit:4.13.1'
	compileOnly 'org.projectlombok:lombok'
	developmentOnly 'org.springframework.boot:spring-boot-devtools'
	runtimeOnly 'mysql:mysql-connector-java'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	implementation 'io.jsonwebtoken:jjwt-api:0.11.2'
	implementation 'io.jsonwebtoken:jjwt-jackson:0.11.2'
	runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.2'
	annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
	annotationProcessor 'javax.annotation:javax.annotation-api:1.3.1'
	implementation 'com.querydsl:querydsl-jpa'
	implementation 'com.querydsl:querydsl-apt'
	annotationProcessor 'com.querydsl:querydsl-apt'
}

test {
	useJUnitPlatform()
}
ext {
	querydslSrcDir = 'src/main/generated'
	queryDslVersion = '4.1.4' }
configurations {
	querydsl.extendsFrom compileClasspath
}
querydsl {
	library = "com.querydsl:querydsl-apt"
	querydslSourcesDir = 'src/main/generated'
	jpa = true
	querydslDefault = true
}
sourceSets {
	main {
		java {
			srcDirs += file(querydslSrcDir)
		}
	}
}
idea {
	module {
		generatedSourceDirs += file(querydslSrcDir)
	}
}
compileQuerydsl {
	options.annotationProcessorPath = configurations.querydsl
}

 

 

config 파일 생성

package SKRookie.moamoa.config;

import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Configuration
public class QueryDslConfig {
    @PersistenceContext
    private EntityManager entityManager;

    @Bean
    public JPAQueryFactory jpaQueryFactory() {
        return new JPAQueryFactory(entityManager);
    }
}

 

이렇게 설정하고 compileQuerydsl 해주면 자동으로 Q(쿼리 타입)가 생성된다.

 

 

엔티티 앞에 Q가 붙고 내가 만들지 않은 파일들이 생성됐다 ? → QueryDSL

 

 

import static SKRookie.moamoa.api.entity.study.QStudy.study;

@Override
    public Page<Study> search(StudySearchCondition condition, Pageable pageable) {
        JPQLQuery<Study> query = jpaQueryFactory       // 1)
                .selectFrom(study)
                .where(
                        userIdEq(condition.getUserSeq()),
                        studyTypeEq(condition.getStudyType())
                );
        return pagingUtil.getPageImpl(pageable, query, Study.class);
    }

 

생성된 쿼리 타입 엔티티를 불러와서 사용하면 되는데 위 예시와 같이 study라는 쿼리 타입 엔티티를 가져와서 검색 기능을 구현한다. 

 

위 예시에는 select 랑 where 정도만 사용했지만 복잡한 SQL과 동일하게 order by, group by, join 등도 사용 가능하다.

 

 

querydsl 배포시 주의할 점

신나게 QueryDSL을 사용은 했는데

 

했는데...

이제 사용하면 되는데...

 

 

배포에서 계속 오류가 났음...

 

 

ĭ˄ĭ ĭ˄ĭ ĭ˄ĭ ĭ˄ĭ

 

 

계속 고치다가 포기하고 QueryDSL 쓰지 말아야겠다 했는데 이렇게 빨리 포기하는 건 아닌 것 같아서 침착하게 다시 시도 (침착.. 할 수 있다...)

 

결국 해결했다... 

 

 

 

(งᐖ)ว

 

 

문제는 compileQuerydsl 이었는데 앞서 말했듯이 querydsl은 쿼리 타입 엔티티를 컴파일 시 생성해서 저장해두므로 그 부분을 배포할 때 인지하고 있어야 한다. 만약 쿼리 타입이 생성되었는데 compileQuerydsl을 다시 하면 build 자체가 안 되는 문제 때문에 배포할 때 계속 오류로 멈췄던 것이다.

 

 

문제를 해결하기 위해 cleanQuerydsl을 build 하기 전에 하는 걸로 코드를 고쳤다. 

 

 

deploy.sh 내부 코드를 수정해서

 

build하기 전에 cleanQuerydslSourceDir을 먼저 할 수 있도록 바꿔주면 빌드 전에 생성된 쿼리 엔티티들 다 지우기 때문에 문제없이 컴파일 빌드된다. 

 

./gradlew cleanQuerydslSourceDir
// 위 코드 추가!!
./gradlew build

 

 

 

 

 

여러모로 querydsl 쓰고 싶었는데 배포까지 잘 해결돼서 사용할 수 있어 너무 다행....

 

 

 

 

spring boot는 진짜 배워도 배워도 끝이 없는 것 같다.

어디까지 공부해야 하니?

 

 

 

 

 

참고

https://ict-nroo.tistory.com/116 [개발자의 기록습관]

자바 ORM 표준 JPA 프로그래밍 [김영한]