givepro

Spring boot QureyDsl 설정 (멀티 모듈) 본문

백엔드/SpringBoot

Spring boot QureyDsl 설정 (멀티 모듈)

givepro 2022. 10. 25. 17:04
반응형

프로젝트를 진행하다보면 멀티 모듈로 분리되어있는 프로젝트를 개발/운영하는 경우가 있다.
멀티모듈의 경우 설정파일이 각 모듈 마다 내용이 다르므로 초기 셋팅에 어려움이 있었다.

나의 경우 기존 프로젝트에서 querydsl 적용 시 오류가 있어서 수정하는 케이스였는데
진행하면서 이 부분은 정리를 해야겠다고 생각이 들어서 끄적끄적,, 🥲

그러면 시작전에 QueryDsl에 대해서 간략하게 설명을 먼저 해보겠다.

QureyDsl이란?

Spring Data JPA가 기본적으로 제공해주는 CRUD 메서드 및 쿼리 메서드 기능을 사용
→ 원하는 조건의 데이터를 수집하기 위해서는 필연적으로 JPQL을 작성
→ 간단한 로직을 작성하는데 큰 문제는 없으나, 복잡한 로직의 경우 개행이 포함된 쿼리 문자열이 많아짐

 

더보기

JPQL이란?

  • JPA에서 제공하는 쿼리 방법중의 하나이다.
  • SQL을 추상화한 객체지향쿼리 언어이다.

JPQL 문법 예시
select m from Member as m where m.age > 18

  • 엔터티와 속성은 대소문자를 구분하지만 JPQL 키워드는 대소문자를 구분하지 않는다.
  • 테이블 이름이 아닌 엔티티 이름(Member)을 사용한다.
  • 별칭(m)은 필수이다. as는 생략가능하다.

두 개의 큰 차이점으로 쿼리 문법 오류를 JPQL은 실행 시점에 발견할 수 있으며,
Querydsl은 컴파일 시점에 발견할 수 있다.

 

JPQL 문자열에 오타 혹은 문법적인 오류가 존재하는 경우, 정적 쿼리라면 어플리케이션 로딩 시점에 이를 발견할 수 있으나

그 외는 런타임 시점에서 에러가 발생합니다.

 

이러한 문제를 어느 정도 해소하는데 기여하는 프레임워크가 바로 QueryDSL입니다.
QueryDSL은 정적 타입을 이용해서 SQL 등의 쿼리를 생성해주는 오픈소스 프레임워크입니다.

 

요약하면

쿼리를 자바코드로 작성할 수 있게 도와주는 프레임워크
Spring Data JPA로 해결하지 못하는 복잡한 쿼리/동적 쿼리를 해결
자바코드로 작성하기 때문에 문법오류를 컴파일 시점에 잡아낼 수 있다.

 


QureyDsl 적용

개발환경
jdk 1.8
gradle 4.10.2
spring boot 2.1.3

전체 프로젝트의 구조는 아래와 같다.

Project Structure
common
- build.gradle
api
- build.gradle
admin
- build.gradle
build.gradle
gradle.properties
settings.gradle

common module의 경우 entity, repository, util 등 공통적인 내용을 작성하고
api, admin은 생략합니다.

buildscript {
	repositories {
		mavenCentral()
		google()
	}
	dependencies {
		classpath("io.spring.gradle:dependency-management-plugin:1.0.6.RELEASE")
		classpath "org.springframework.boot:spring-boot-gradle-plugin:2.1.0.RELEASE"
		classpath ('software.amazon.awssdk:bom:2.15.0')
		classpath("gradle.plugin.com.ewerk.gradle.plugins:querydsl-plugin:1.0.10")
	}
}

plugins {
	id "ua.eshepelyuk.ManifestClasspath" version "1.0.0"
	id "io.franzbecker.gradle-lombok" version "3.0.0"
}

allprojects {
	apply plugin: 'idea'
	apply plugin: 'eclipse'
	...
}

subprojects { subproject ->
	apply plugin: 'java'
	apply plugin: 'eclipse-wtp'
	apply plugin: 'org.springframework.boot'
	apply plugin: 'io.spring.dependency-management'

	compileJava.options.encoding = 'UTF-8'

	sourceCompatibility = PROJECT_JAVA_VERSION
	targetCompatibility = PROJECT_JAVA_VERSION

	repositories {
		mavenLocal()
		mavenCentral()
		google()
		maven { url 'https://oss.jfrog.org/libs-snapshot' }
	}
    
    ...
}

프로젝트 최상단의 build.gradle 파일입니다.
여기서 핵심은 buildscript의 dependencies에 아래와 같이 추가

classpath("gradle.plugin.com.ewerk.gradle.plugins:querydsl-plugin:1.0.10")
jar {
	baseName = 'common'
	version = '1.0.0-SNAPSHOT'
	enabled = true
}

bootJar {
	enabled = false
}

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

apply plugin: "com.ewerk.gradle.plugins.querydsl"

dependencies {
	compile("org.springframework.boot:spring-boot-starter-aop")
	compile("org.springframework.boot:spring-boot-starter-data-jpa")
	compile("org.springframework.boot:spring-boot-starter-web")
	compile('org.projectlombok:lombok:1.18.6')
	annotationProcessor("org.projectlombok:lombok")
    
    ...

	//querydsl
	implementation 'com.querydsl:querydsl-jpa'
	implementation 'com.querydsl:querydsl-apt'

}

test {
	useJUnitPlatform()
}

// QueryDSL 설정
def querydslDir = "$buildDir/generated/querydsl"

querydsl {
	library = "com.querydsl:querydsl-apt"
	jpa = true
	querydslSourcesDir = querydslDir
}

sourceSets {
	main {
		java {
			srcDirs = ['src/main/java', querydslDir]
		}
	}
}

compileQuerydsl{
	options.annotationProcessorPath = configurations.querydsl
}

configurations {
	querydsl.extendsFrom compileClasspath
}

compileQuerydsl {
	if(file(querydslDir).exists() )
		delete(file(querydslDir))
}

 

이 부분은 common의 build.gradle입니다.
querydsl로 추가된 부분은 아래와 같습니다.

apply plugin: "com.ewerk.gradle.plugins.querydsl"
//querydsl
implementation 'com.querydsl:querydsl-jpa'
implementation 'com.querydsl:querydsl-apt'
// QueryDSL 설정
def querydslDir = "$buildDir/generated/querydsl"

querydsl {
	library = "com.querydsl:querydsl-apt"
	jpa = true
	querydslSourcesDir = querydslDir
}

sourceSets {
	main {
		java {
			srcDirs = ['src/main/java', querydslDir]
		}
	}
}

compileQuerydsl{
	options.annotationProcessorPath = configurations.querydsl
}

configurations {
	querydsl.extendsFrom compileClasspath
}

compileQuerydsl {
	if(file(querydslDir).exists() )
		delete(file(querydslDir))
}

마지막으로 api, admin의 build.gradle은 아래와 같은 형식으로 사용합니다.

dependencies {
    compile project(':common')
    compile("org.projectlombok:lombok:1.18.6")
    annotationProcessor("org.projectlombok:lombok")
    
    ...

    testCompile('org.springframework.boot:spring-boot-starter-test')
}

핵심은 common module을 컴파일 한다는 것!

p.s 어느정도 생략된 부분들이 있는데 프로젝트에 설정하는데 크게 영향이 없는 부분이므로 생략했습니다.

Comments