PostgreSQL 와 Redis 를 동시에 학습하고 싶어서 멀티 모듈을 구성해보았다.
처음엔 "폴더만 나누면 되겠지?"라고 생각했는데, 생각보다 훨씬 복잡하고 배울 게 많았다.
자바에서 Module(모듈)
은 독립적으로 배포될 수 있는 코드의 단위를 말한다.
패키지의 한 단계 위의 집합체이며, 서로 밀접하게 연관된 패키지들과 리소스들의 그룹을 의미한다.
각 모듈은 독립적으로 개발, 빌드, 테스트, 배포 가 가능하다.
멀티모듈의 핵심 개념
PostgreSQL과 Redis를 각각 학습하고 싶었다. 하지만 따로따로 프로젝트를 만들기엔 뭔가 아쉬웠다.
실무에서는 PostgreSQL과 Redis를 연동한 패턴을 많이 사용한다고 들어서 나중에 통합을 생각했다.
목표: 하나의 프로젝트에서 둘 다 학습 및 실습 해보고 나중에 통합된 연동 패턴을 실습해보자
처음엔 멀티 모듈을 단순하게 생각했다.
내가 처음 이해한 멀티 모듈:
실제 멀티 모듈:
생각보다 훨씬 체계적이고 복잡했다.
postgres-redis-practice/
├── postgres/ # PostgreSQL만 다루는 곳
├── redis/ # Redis만 다루는 곳
└── integration/ # 둘 다 쓰는 곳
위처럼 폴더만 나누고, 각각에 Spring Boot를 적용시켰다.
./gradlew :postgres:bootRun
# Task ':postgres:bootRun' not found
뭔가 Gradle이 postgres를 모듈로 인식을 못하고 있었다.
// postgres/build.gradle
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// redis/build.gradle
implementation 'org.springframework.boot:spring-boot-starter-web' // 중복!
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
똑같은 설정을 모든 모듈에 반복하고 있었다.
// settings.gradle
rootProject.name = 'postgres-redis-practice'
include 'postgres'
include 'redis'
include 'integration'
위 설정이 있어야 Gradle이 각 폴더를 모듈로 인식한다.
처음엔 각 모듈이 완전히 분리되어야 한다고 생각했다. 하지만 실제로는:
# postgres/application.yml
server:
port: 8082
# redis/application.yml
server:
port: 8083
# integration/application.yml
server:
port: 8080
각 모듈마다 포트를 분리 설정해 충돌을 막았다.
// 루트 build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '3.5.3' apply false
id 'io.spring.dependency-management' version '1.1.7' apply false
}
// 모든 서브 프로젝트에 공통 적용 (Ex. Spring Boot)
subprojects {
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
repositories {
mavenCentral()
}
// 공통 의존성
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
}
// 각 모듈별 특화 의존성
project(':postgres') {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'
runtimeOnly 'org.postgresql:postgresql'
}
}
project(':redis') {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.springframework.boot:spring-boot-starter-cache'
}
}
project(':integration') {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.springframework.boot:spring-boot-starter-cache'
runtimeOnly 'org.postgresql:postgresql'
}
}
모듈별 공통 의존성은 root build.gradle
안 dependencies{ }
안에 설정하고 추가&특화 의존성은 위 처럼 따로 설정해 주었다.
implementation project(':postgres')
다른 모듈에서 위 한줄로 postgres
모듈의 모든 Entity와 Repository를 사용할 수 있다.
학습 환경의 일관성을 위해 Docker Compose를 활용했다.
# docker-compose.yml
version: '3.8'
services:
postgres:
image: postgres:15-alpine
container_name: postgres-practice
environment:
POSTGRES_DB: practice_db
POSTGRES_USER: daniel
POSTGRES_PASSWORD: password123
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
container_name: redis-practice
ports:
- "6379:6379"
command: redis-server --appendonly yes
volumes:
postgres_data:
# 1. 환경 구축 (2초)
docker-compose up -d
# 2. 각 모듈 독립 실행 (학습 단계)
./gradlew :postgres:bootRun # PostgreSQL 패턴 학습
./gradlew :redis:bootRun # Redis 패턴 학습
# 3. 통합 실행 (응용 단계)
./gradlew :integration:bootRun # 학습한 패턴들 통합 활용
# 4. 전체 빌드 및 테스트
./gradlew build
./gradlew test
// integration/build.gradle
dependencies {
implementation project(':postgres') // postgres 모듈의 Entity와 Repository
implementation project(':redis') // redis 모듈의 캐시 서비스
// 필요한 추가 의존성들
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
}
이렇게 하면 integration 모듈에서:
User
Entity를 그대로 사용UserCacheService
를 그대로 활용// postgres 모듈에서 정의한 Entity
public class User { ... }
// redis 모듈에서 정의한 캐시 서비스
public class UserCacheService { ... }
// integration 모듈에서 두 개를 조합
@Service
public class IntegratedUserService {
// 별도 코드 작성 없이 기존 모듈들의 기능 활용
}
처음에는 기술별로 나누는 것이 당연하다고 생각했다. 하지만 실제로는 학습 목적과 활용 방식이 더 중요한 기준이었다.
postgres
모듈: "PostgreSQL을 어떻게 쓸 것인가"redis
모듈: "Redis를 어떻게 쓸 것인가" integration
모듈: "둘을 어떻게 함께 쓸 것인가"멀티 모듈에서 가장 어려운 부분은 의존성 관리였다.
배운 것들:
dependencyManagement
활용이제 PostgreSQL과 Redis를 실습해보자..!