이미 끝난 프로젝트 인 워크듀오를 멀티모듈로 바꿔보고자 한다. 전회사에서 프로젝트를 보면서 멀티모듈을 처음 알게 되었다.
모듈 별로 도메인 그리고 각 특정 서비스를 분류해서 한 프로젝트 내에서 여러 개의 서버를 띄우는 게 가능했었다.
정말 좋다고 느꼈고, 특히나 프로젝트의 어떤 부분을 보고 코드를 확인해야 하는지 에 대한 명확성이 너무나 도 좋은 부분이라고 생각된다.
우리 프로젝트에 접목하지 않을 이유가 없다고 생각하여 분리를 해보고자 한다.
첫 번째로 멀티모듈 이란 하나의 프로젝트 내에서 다양한 모듈을 구성하고 공통점을 묶어서 하나의 모듈에서 관리하는 기능을 의미한다.
즉 다시 말해서 우리 워크듀오는 크게 멤버, 그룹의 형태로 서비스가 나뉜다. 이들의 공통점을 하나로 묶어서 관리한다면?
만약 서버를 분리한다고 가정해 보자. 각각 의 서버에서 우리는 멤버를 그룹에서 호출하기도 하고 그룹을 멤버에서 호출하기도 한다. 그렇다면? 복사 붙여 넣기가 필연적으로 생길 수밖에 없다. 그렇다면 어떻게 해결해야 할까?
- 첫 번째 이런 공통된 부분을 각 프로젝트에서 호출이 가능해야 하며,
- 두 번째 빌듯이 에 공통된 부분을 포함시켜 주어야 한다.
최초 모듈 구성을 core/ member/ group으로 구성하였으며 코어는 말 그대로 멤버와 그룹이 컴파일될 시에 포함될 메인 소스 가 된다.
위와 같은 부분으로 변화시키고자 한다.
auth는 지금 고민 중이고 테스트 중인 부분이라서 언급하지 않았다.
Core에서 는 위의 사진과 같이 공통으로 관리되는 AWS 혹은 컨피그 파일, 그리고 모든 도메인을 코어에서 관리한다.
우리는 현재 에러를 커스텀으로 작성하여 핸들링되기 때문에 공통으로 사용되는 에러코드 또한 코어에 위치시켰다.
그룹 같은 경우는 config 상에 레디스를 추가했다. 그룹상 에서만 사용되기 때문에 위와 같이 분리했다.
잡설은 그만하고 어떻게 하면 될까 먼저 module 두 개를 만들어 주자. (Gradle version 7.5)
위와 같은 방식으로 기본모듈에 코어를 생성해 주자. 자 이렇게 생성이 완료되면
settings.gradle 파일을 확인해 보자. (https://docs.gradle.org/current/userguide/multi_project_builds.html)
모듈을 생성한다면 아래와 같이 자동 기입되어 있는 것이 보일 것이다.
rootProject.name = 'workduo'
include 'workduo_core'
이 세팅 그래들이 존재하기 때문에 우리는 그대를 멀티 프로젝트를 구성할 수 있는 부분이니 확인하고 넘어가자. 우리 똑순이 인텔리제이 그래 들 을 눌러보면 위와 같이 코끼리 하나 더 생겼다 즉 하위 프로젝트가 생성된 부분을 확인할 수 있다.
대망의 build.gradle을 보러 가자.
현재 워크듀오 의 빌드 그래 들 이다. 위에서부터 차근차근 알아보자.
(https://docs.gradle.org/current/javadoc/org/gradle/api/initialization/dsl/ScriptHandler.html)
buildscript 블록 안에서는 어떤 태스크를 부여할지, 어떤 플러그인을 사용할지, 어떤 클래스가 나머지 빌드 스크립트 안에 포함되어야 하는지 를 결정할 수 있는 공간이다. 또한 추가적인 외부 서드파티 라이브러리를 사용할 때 클래스 패스를 기입해 주기 위해서 작성하기도 한다.
어떻게 보면 글로벌 레벨 에서의 디펜던시 혹은 레퍼지토리 의 섹션이라고 볼 수도 있다.
buildscript {
ext {
queryDslVersion = "5.0.0"
}
}
현재 워크듀오에서는 이렇게 스크립트를 작성해서 쓰고 있다. ext는 변수명을 위와 같이 선언해 줄 수 있고 아래 스크립트에서 이 변수를 호출할 수 있다.
plugins(https://docs.gradle.org/current/userguide/what_is_gradle.html)
플러그인 블록 안에서는 프로젝트에 플러그인을 적용하면 플러그인이 프로젝트의 기능을 확장할 수 있습니다.라고 간단하게 정리할 수 있다. 특히나 저 공홈 문서를 보면 플러그인을 프로젝트 로직을 추가하는 대신 플러그인 사용 시 이점에 대해 아래와 같이 설명한다.
- 재사용을 촉진하고 여러 프로젝트에서 유사한 논리를 유지 관리하는 오버헤드를 줄입니다.
- 더 높은 수준의 모듈화를 허용하여 이해력과 구성을 향상합니다.
- 명령형 논리를 캡슐화하고 빌드 스크립트가 가능한 한 선언적일 수 있도록 합니다.
종류 또한 두 가지 가 존재한다.
바이너리 플러그인 (gradle dsl 언어로 작성, plugin interface를 구현한 방식),
plugins {
id 'com.jfrog.bintray' version '1.8.5'
}
스크립트 플러그인
apply from: 'other.gradle'
차이점이라면 플러그인은 종종 스크립트 플러그인으로 시작하고(작성하기 쉽기 때문에) 코드의 가치가 높아짐에 따라 쉽게 테스트하고 여러 프로젝트 또는 조직 간에 공유할 수 있는 바이너리 플러그인으로 마이그레이션 됩니다.
이중 우리는 바이너리 플러그인을 사용하며, 버전이 명시된 건 커뮤니티 버전, 버전이 없다면? 코어를 사용하는 플러그인이다.
이렇게 작성된 플러그인 들은 프로젝트를 빌드할 때 먼저 그래 들은 플러그인에서 주어지는 값을 해석해 jar 파일 스크립트 클래스 경로에 추가하는 방식이다.
repositories(https://docs.gradle.org/current/userguide/declaring_repositories.html)
Gradle은 Maven, Ivy 또는 플랫 디렉터리 형식을 기반으로 하는 하나 이상의 레포지토리에서 종속성을 해결할 수 있습니다. 자세한 내용은 모든 유형의 레포지토리에 대한 전체 참조를 확인하십시오.
통상 메이븐 센트럴을 사용하나 구글을 선택적으로 선택이 가능하기도 하다.
repositories {
mavenCentral()
maven {
url "https://repo.spring.io/release"
}
maven {
url "https://repository.jboss.org/maven2"
}
}
이런 방식의 여러 레포지토리 도 다음과 같이 작성해 빌드스크립트 안에 명시 가 가능하다.
dependencies(https://docs.gradle.org/current/userguide/dependency_management_for_java_projects.html)
위에서 레포지토리 대상을 정했으면 어떤 것을 들고 올지 정해주는 부분이다.
다시 말해 여기 지정되는 대상이 위에서 지정한 레포지토리에서 조회 되어야 오류 없이 빌드가 가능하다.
가끔 가다 org. 모시꺵이 하면서 찾을 수 없다는 에러가 보인다면 조회가 안된다는 의미이니 오타 혹은 직접 주소를 가보자.
그렇다면 이런 대상을 들고 올 때 앞에 써주는 키워드 들을 여러 개 보았을 것이다. 당장 스프링. io에서 만들더라도 디펜던시를 확인하면 다양하게 확인이 가능하다.
현재워크듀오 의 디펜던시 일부다.
dependencies{
"""생략"""
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
implementation 'org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4.1:1.16'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'mysql:mysql-connector-java'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
여기 보이는 것처럼 다양하게 있는데 한번 다이브 해보자(https://docs.gradle.org/current/userguide/java_library_plugin.html)
- 오직 초록색 만이 유저가 사용할 수 있는 부분이다.
- 핑크색 은 컴포넌트가 컴파일될 당시에 한번 사용되며, 라이브러리에 대해 사용된다.
- 파랑이는 자체적으로 실행될 때 사용되는 부분이다.
1. api : 종속적이지 않으며 소비하는 곳에 의해 호출되며 런타임, 컴파일 시점에 호출된다.
- 과거 compie() 이 deprecate 되고 현재 api()로 대체되어 사용 중이다.
- 또한 많이 추천하지 않는다 api를 호출하는 클래스 안에 다른 클래스를 호출하게 된다면 내가 부르는 시점에서 이거는 두 개다 호출하는 경우가 생기기 때문에 의도치 않는 import 가 발생되기에 추천하지 않는다고 한다.
2. implementation : 위의 발생되는 문제점을 없애기 위해 생긴 게 이 키워드이다. 종속적이지 않기에 compile에서 잘 작동되던 자바 애플리케이션 중 compile() 키워드를 -> implementation 으로 바꾼다면 호출 못하는 코드들이 생길 수도 있다. 왜? 의도치 않은 임포트를 이용해서 사용하고 있을 수 있기 때문에
3. compileOnly: 컴파일 타임에는 필요하지만 런타임에는 필요 없는 것을 의미하는 키워드이다. 위에서는 롬복이 해당되는데 컴파일 시점에 롬복 어노테이션을 이용해서 클래스를 만든 이후 런타임 시점에서는 필요 없는 경우에 해당되기에 이 키워드를 사용한다.
4. compileOnlyApi : 3번과 동일하다.
5. runtimeOnly : 3,4번과 는 반대로 컴파일 시점에는 필요하지 않으나 런타임에 필요한 종속성에 표기하는 키워드이다. 대표적인 예로는 로거를 많이 든다.
configurations(https://docs.gradle.org/current/dsl/org.gradle.api.artifacts.Configuration.html)
configuration 블록이 사실 제일 이해가 안 간다. 설명되는 내용만 봐도 그룹과 그에 따른 종속성을 나타낸다는 데 잘 모르겠다 왜 쓰여야 하는지 조차.
compileQuerydsl {
compileOnly {
extendsFrom annotationProcessor
}
querydsl.extendsFrom compileClasspath
}
워크듀오 의 컨피규레이션이다. 저 주소로 찾아가서 검색해 보면 컴파일 시점에는 어노테이션 프로세스를 확장하고 , 쿼리디에스엘 은 컴파일 클래스 패스를 확장한다. 왜 필요할까. from의 대상으로부터 확장되는 것인데 쿼리디에스 엘은쓸떄 우리는 따로 컴파일 쿼리디에스엘을 누르고 자바를 빌드한다. 이때 빌드할 때 쿼리디에스엘에서 먼저 컴파일된 클래스 패스를 가져오기 위해 사용된다고 추측된다. 실제로 빼고 돌려보면 컴파일 조차 되지 않는다. 왜? 컴파일되지 않았기 때문에 Q클래스 가 존재할 수가 없는 이유이다.
다음 2편에서는 실제로 build.gradle을 세팅해 보자. 이 쿼리디에스 엘 설정이 계속 괴롭힐 예정이다 ㅠ.ㅠ
'사이드 프로젝트 > 워크듀오-개발일지' 카테고리의 다른 글
Token 암호화 활용해보기 (해쉬, 대칭 암호화,비대칭 암호화) (1) | 2024.01.19 |
---|---|
멤버 일정 달력, 일정 목록 API (2) | 2022.09.30 |
멤버 피드 댓글 삭제 API (1) | 2022.09.28 |
멤버 피드 댓글 업데이트 API (0) | 2022.09.28 |
멤버 피드 댓글 조회 API (1) | 2022.09.28 |