1. Gradle 빌드 설정 (build.gradle)
// [1] 플러그인 선언: Spring Boot 3.4.1 및 의존성 관리 플러그인
plugins {
id 'java'
id 'org.springframework.boot' version '3.4.1'
id 'io.spring.dependency-management' version '1.1.7'
}
// [2] 프로젝트 메타데이터
group = 'com.example'
version = '0.0.1-SNAPSHOT'
description = 'spring-second-hand-engineering'
// [3] Java 17 설정
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
// [4] 컴파일 설정 (롬복 어노테이션 프로세서)
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
// [5] Maven Central 저장소
repositories {
mavenCentral()
}
// [6] 의존성 정의 - 카테고리별 정리
dependencies {
// --- Spring Boot Starters ---
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-aop'
// --- Persistence & Database ---
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-data-mongodb' // [7] MongoDB 추가
runtimeOnly 'com.mysql:mysql-connector-j'
// --- Querydsl ---
implementation 'com.querydsl:querydsl-jpa:5.1.0:jakarta'
annotationProcessor "com.querydsl:querydsl-apt:5.1.0:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"
// --- Security & Authentication ---
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
// --- JWT ---
implementation 'io.jsonwebtoken:jjwt-api:0.12.5' // [8] JJWT 0.12.5 버전
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.5'
// --- WebSocket ---
implementation 'org.springframework.boot:spring-boot-starter-websocket' // [9] WebSocket 추가
// --- Monitoring & Metrics ---
implementation 'org.springframework.boot:spring-boot-starter-actuator'
runtimeOnly 'io.micrometer:micrometer-registry-prometheus'
// --- Utils ---
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
implementation 'com.fasterxml.jackson.core:jackson-databind'
// --- Testing ---
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
// --- Testcontainers ---
testImplementation "org.testcontainers:testcontainers:1.19.0"
testImplementation "org.testcontainers:junit-jupiter:1.19.0"
testImplementation "org.testcontainers:mysql:1.19.0"
}
// [10] Querydsl QClass 생성 위치 설정
def querydslDir = layout.buildDirectory.dir("generated/querydsl").get().asFile
sourceSets {
main.java.srcDirs += [ querydslDir ]
}
tasks.withType(JavaCompile).configureEach {
options.getGeneratedSourceOutputDirectory().set(querydslDir)
}
clean {
delete querydslDir
}
// [11] JUnit Platform 테스트 설정
tasks.named('test') {
useJUnitPlatform()
}
주석 설명
[1] Gradle 빌드 스크립트의 플러그인 선언부이다. org.springframework.boot 플러그인은 Spring Boot 애플리케이션을 패키징하고 실행하는 기능을 제공한다. io.spring.dependency-management 플러그인은 Spring Boot의 BOM(Bill of Materials)을 사용하여 의존성 버전을 자동으로 관리한다.
[2] 프로젝트의 기본 메타데이터를 정의한다. group은 패키지 네임스페이스, version은 프로젝트 버전, description은 프로젝트 설명이다.
[3] Java 17 툴체인을 사용하도록 설정한다. Java 17은 Spring Boot 3.x의 최소 요구사항이며, 장기 지원(LTS) 버전이다.
[4] 컴파일 설정에서 compileOnly 설정이 annotationProcessor 설정을 상속받도록 한다. 이 설정은 롬복(Lombok) 어노테이션 프로세서가 컴파일 타임에 작동하도록 한다.
[5] 의존성을 다운로드할 저장소를 Maven Central로 지정한다. Maven Central은 가장 큰 Java 라이브러리 저장소이다.
[6] 프로젝트 의존성 정의부로, 카테고리별로 정리되어 있다:
- Spring Boot Starters: 웹, 검증, AOP 등 기본 기능
- Persistence & Database: JPA, Redis, MySQL 데이터베이스 연동
- Querydsl: 타입 안전한 동적 쿼리 생성을 위한 라이브러리
- Security: 인증, 권한 부여, OAuth 2.0 클라이언트
- Monitoring & Metrics: 액추에이터와 Prometheus 연동
- Utils: 롬복(보일러플레이트 코드 제거), Jackson(JSON 처리)
- Testing: Spring Boot 테스트, 보안 테스트
- Testcontainers: 통합 테스트를 위한 도커 컨테이너 관리
[7] Querydsl의 QClass(쿼리 타입 클래스) 생성 위치를 설정한다. build/generated/querydsl 디렉토리에 QClass가 생성되며, 이를 메인 소스 디렉토리에 포함시킨다. 이 설정은 IntelliJ IDEA와 Gradle 빌드 시스템 간의 충돌을 방지한다.
[8] 테스트 작업이 JUnit Platform을 사용하도록 설정한다. JUnit 5는 모듈화된 테스트 플랫폼으로, Testcontainers와의 통합을 지원한다.
2. Docker Compose 인프라 설정 (docker-compose.yaml)
# [1] Docker Compose 버전 정의
version: '3.8'
services:
# [2] MySQL 데이터베이스 서비스
db:
image: mysql:8.0 # MySQL 8.0 공식 이미지 사용
container_name: secondhand-db # 컨테이너 이름 지정
environment:
MYSQL_DATABASE: secondhand # 생성할 데이터베이스 이름
MYSQL_USER: secondhand_user # 애플리케이션용 사용자
MYSQL_PASSWORD: password1234 # 사용자 비밀번호
MYSQL_ROOT_PASSWORD: password1234 # root 비밀번호
ports:
- "3307:3306" # [3] 호스트 3307포트를 컨테이너 3306포트에 매핑
volumes:
- mysql_data:/var/lib/mysql # [4] 영구 데이터 저장을 위한 볼륨
healthcheck:
test: [ "CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "secondhand_user", "--password=password1234" ]
interval: 10s # 10초마다 헬스 체크
timeout: 5s # 5초 대기
retries: 5 # 5번 재시도
deploy:
resources:
limits:
cpus: "1.0" # 최대 CPU 1코어
memory: 1GB # 최대 메모리 1GB
# [5] Redis 캐시 서비스
redis:
image: redis:7.0 # Redis 7.0 공식 이미지
container_name: secondhand-redis
ports:
- "6379:6379" # Redis 기본 포트 매핑
volumes:
- redis_data:/data # Redis 데이터 영구 저장
deploy:
resources:
limits:
cpus: "0.5" # 최대 CPU 0.5코어
memory: 512MB # 최대 메모리 512MB
# [6] Redis Exporter 서비스
redis_exporter:
image: oliver006/redis_exporter # Redis 메트릭 수집기 이미지
container_name: secondhand-redis-exporter
command: ["--redis.addr=redis://redis:6379"] # [7] Redis 서비스에 연결
depends_on:
- redis # Redis 서비스 시작 후 실행
ports:
- "9121:9121" # Prometheus가 접근할 포트
# [8] Prometheus 모니터링 서비스
prometheus:
image: prom/prometheus:latest # Prometheus 최신 이미지
container_name: secondhand-prometheus
volumes:
- ./monitoring/prometheus.yaml:/etc/prometheus/prometheus.yml # [9] 설정 파일 마운트
ports:
- "9090:9090" # Prometheus 웹 UI 포트
depends_on:
- db
- redis # 의존 서비스들
# [10] Grafana 시각화 서비스
grafana:
image: grafana/grafana:latest # Grafana 최신 이미지
container_name: secondhand-grafana
environment:
- GF_SECURITY_ADMIN_USER=admin # 기본 관리자 계정
- GF_SECURITY_ADMIN_PASSWORD=admin
ports:
- "3001:3000" # Grafana 웹 UI 포트
volumes:
- grafana_data:/var/lib/grafana # 대시보드 데이터 저장
depends_on:
- prometheus # Prometheus 이후 실행
# [11] 영구 데이터 볼륨 정의
volumes:
mysql_data: # MySQL 데이터 저장소
redis_data: # Redis 데이터 저장소
grafana_data: # Grafana 설정 및 대시보드 저장소
주석 설명
[1] Docker Compose 파일 버전 3.8을 사용한다. 이 버전은 여러 컨테이너를 정의하고 관리하기 위한 최신 기능을 지원한다.
[2] MySQL 8.0 데이터베이스 서비스를 정의한다. secondhand 데이터베이스를 자동으로 생성하고, 애플리케이션에서 사용할 secondhand_user 계정을 설정한다.
[3] "3307:3306" 포트 매핑은 호스트 머신의 3307 포트를 컨테이너의 3306 포트(MySQL 기본 포트)에 연결한다. 이 설정은 애플리케이션 설정(application.yaml)의 localhost:3307과 일치해야 한다.
[4] mysql_data:/var/lib/mysql는 Docker 볼륨을 사용하여 MySQL 데이터를 영구적으로 저장한다. 컨테이너가 삭제되어도 데이터는 유지된다.
[5] Redis 7.0 서비스를 정의한다. Redis는 세션 저장, 캐싱, 메시지 브로커 등으로 사용된다. 6379 포트는 Redis의 기본 포트이다.
[6] Redis Exporter는 Redis의 메트릭(메모리 사용량, 연결 수, 명령 실행 수 등)을 수집하여 Prometheus 형식으로 제공하는 도구이다.
[7] --redis.addr=redis://redis:6379 명령어 인자는 Redis Exporter가 redis 서비스(도커 내부 네트워크 이름)의 6379 포트에 연결하도록 지시한다.
[8] Prometheus는 메트릭 수집 및 모니터링 시스템이다. 이 서비스는 Spring Boot 애플리케이션과 Redis Exporter로부터 메트릭을 수집한다.
[9] ./monitoring/prometheus.yaml:/etc/prometheus/prometheus.yml는 호스트의 monitoring/prometheus.yaml 파일을 컨테이너의 /etc/prometheus/prometheus.yml 경로에 마운트한다. 이렇게 하면 설정 파일을 호스트에서 편집할 수 있다.
[10] Grafana는 Prometheus에서 수집한 메트릭을 시각화하는 대시보드 도구이다. 기본 관리자 계정으로 접속할 수 있으며, 3001 포트로 웹 UI에 접근할 수 있다.
[11] volumes 섹션은 세 가지 영구 데이터 저장소를 정의한다. Docker 볼륨을 사용하면 컨테이너 재시작 시에도 데이터가 유지된다.
3. Prometheus 모니터링 설정 (monitoring/prometheus.yaml)
# [1] 전역 설정: Prometheus의 기본 동작 방식을 정의한다
global:
scrape_interval: 5s # 각 타겟으로부터 메트릭을 수집하는 주기
evaluation_interval: 5s # 경고 규칙(alerting rules)을 평가하는 주기
# [2] 스크래핑 설정: 메트릭을 수집할 대상들을 정의한다
scrape_configs:
# [3] Spring Boot 애플리케이션 메트릭 수집 작업
- job_name: 'spring-boot-app' # 작업 이름, Prometheus UI에서 식별용
metrics_path: '/actuator/prometheus' # Spring Boot Actuator의 Prometheus 엔드포인트
static_configs:
- targets: ['host.docker.internal:8080'] # [4] 도커 컨테이너 내부에서 호스트 머신 접근
# [5] Redis 메트릭 수집 작업
- job_name: 'redis'
static_configs:
- targets: ['redis_exporter:9121'] # [6] Redis 메트릭을 Prometheus 형식으로 변환해주는 exporter
주석 설명
[1] Prometheus의 글로벌 설정 부분이다. scrape_interval은 Prometheus가 각 타겟(애플리케이션, 데이터베이스 등)으로부터 메트릭을 수집하는 빈도를 정의한다. 5초 간격은 개발 환경에서 빠른 피드백을 얻기 위한 설정이다. evaluation_interval은 경고 규칙을 얼마나 자주 평가할지를 결정한다.
[2] scrape_configs는 실제로 메트릭을 수집할 대상들에 대한 정의를 포함한다. 각 항목을 'job'이라고 부르며, 여러 개의 job을 정의할 수 있다.
[3] Spring Boot 애플리케이션의 메트릭을 수집하기 위한 job 설정이다. Spring Boot는 Actuator 의존성을 추가하면 /actuator/prometheus 경로에서 Prometheus 형식의 메트릭을 제공한다.
[4] host.docker.internal은 도커 컨테이너 내부에서 호스트 머신(로컬 컴퓨터)을 가리키는 특수 도메인이다. 이 설정은 도커로 실행된 Prometheus가 호스트에서 실행 중인 Spring Boot 애플리케이션(IntelliJ에서 실행)에 접근하기 위해 사용된다.
[5] Redis 데이터베이스의 메트릭을 수집하기 위한 job이다. Redis 자체는 Prometheus 형식의 메트릭을 제공하지 않으므로 중간 변환기가 필요하다.
[6] redis_exporter는 Redis의 상태 정보를 수집하여 Prometheus가 이해할 수 있는 형식으로 변환해주는 도구이다. :9121 포트에서 메트릭을 제공한다.
4. 애플리케이션 설정 (src/main/resources/application.yaml)
# [1] 서버 기본 설정
server:
port: 8000 # 애플리케이션이 사용할 HTTP 포트
# [2] Spring 애플리케이션 설정
spring:
application:
name: secondhand-market # 애플리케이션 이름, 마이크로서비스 환경에서 식별용
# [3] JWT(JSON Web Token) 설정
jwt:
secret: vfr79d8f9q327498fh398fhw98fh3298fh9382hf9832hf98 # 토큰 서명에 사용할 비밀키
access_expiration_time: 3600000 # 액세스 토큰 유효시간 (1시간, 밀리초 단위)
refresh_expiration_time: 604800000 # 리프레시 토큰 유효시간 (7일)
# [4] 데이터베이스 연결 설정
datasource:
url: jdbc:mysql://localhost:3307/secondhand?useSSL=false&allowPublicKeyRetrieval=true&characterEncoding=UTF-8&serverTimezone=Asia/Seoul&rewriteBatchedStatements=true
username: secondhand_user # 데이터베이스 사용자명
password: password1234 # 데이터베이스 비밀번호
driver-class-name: com.mysql.cj.jdbc.Driver # MySQL JDBC 드라이버
hikari:
maximum-pool-size: 32 # [5] 데이터베이스 커넥션 풀 최대 크기
# [6] JPA(Java Persistence API) 설정
jpa:
hibernate:
ddl-auto: update # 엔티티 변경 시 데이터베이스 스키마 자동 업데이트
show-sql: true # 실행되는 SQL 쿼리를 콘솔에 출력
properties:
hibernate:
format_sql: true # SQL 쿼리를 읽기 쉽게 포맷팅
default_batch_fetch_size: 100 # [7] N+1 문제 해결을 위한 배치 페치 사이즈
open-in-view: false # [8] OSIV(Open Session In View) 비활성화
# [9] Redis 캐시 설정
data:
redis:
host: localhost # Redis 서버 호스트
port: 6379 # Redis 서버 포트
# [10] Spring Security OAuth 2.0 설정
security:
oauth2:
client:
registration:
# [11] Google OAuth 2.0 설정
google:
client-id: # 클라이언트 id (보안상 비워둠)
client-secret: # 클라이언트 비밀키 (보안상 비워둠)
redirect-uri:
scope: # 요청할 권한 범위
- email
- profile
# [12] Kakao OAuth 2.0 설정
kakao:
client-id: # 클라이언트 id (보안상 비워둠)
client-secret: # 클라이언트 id (보안상 비워둠)
redirect-uri:
authorization-grant-type: authorization_code # OAuth 2.0 권한 부여 타입
scope: # 카카오에서 요청할 권한
- profile_nickname
- account_email
client-name: Kakao # 클라이언트 식별 이름
client-authentication-method: client_secret_post # [13] 인증 방식
provider:
kakao:
authorization-uri: <https://kauth.kakao.com/oauth/authorize> # 인증 요청 URL
token-uri: <https://kauth.kakao.com/oauth/token> # 토큰 교환 URL
user-info-uri: <https://kapi.kakao.com/v2/user/me> # 사용자 정보 조회 URL
user-name-attribute: id # 사용자 식별에 사용할 속성
####################################################################################################
# [14] 애플리케이션 모니터링 설정
management:
endpoints:
web:
exposure:
include: "health,metrics,info,prometheus" # [15] 외부에 노출할 엔드포인트
endpoint:
prometheus:
access: read_only # Prometheus 엔드포인트 활성화
health:
show-details: always # 헬스 체크 상세 정보 항상 표시
# [16] 로깅 레벨 설정
logging:
level:
com.example.secondhandmarket: debug # 프로젝트 패키지 디버그 로깅
org.hibernate.SQL: debug # Hibernate SQL 쿼리 디버그 로깅
주석 설명
[1] 서버 기본 설정으로, 애플리케이션이 8000번 포트에서 HTTP 요청을 수신한다.
[2] Spring Boot 애플리케이션의 이름을 정의한다. 이 이름은 마이크로서비스 환경에서 서비스 디스커버리, 로깅, 모니터링에서 식별자로 사용된다.
[3] JWT(JSON Web Token) 설정이다. JWT는 사용자 인증 정보를 포함하는 암호화된 토큰으로, 서버에서 사용자 세션을 관리하지 않고도 인증을 구현할 수 있다. 액세스 토큰은 1시간, 리프레시 토큰은 7일간 유효하다.
[4] MySQL 데이터베이스 연결 설정이다. URL 파라미터로 여러 옵션을 설정했다: SSL 비활성화, UTF-8 문자 인코딩, 서울 타임존, 배치 문장 최적화 등.
[5] HikariCP 커넥션 풀의 최대 크기를 32로 설정했다. 커넥션 풀은 데이터베이스 연결을 재사용하여 성능을 향상시킨다.
[6] JPA 설정으로 데이터베이스와의 상호작용을 관리한다. ddl-auto: update는 엔티티 클래스 변경 시 데이터베이스 스키마를 자동으로 업데이트한다.
[7] default_batch_fetch_size: 100은 N+1 문제를 해결하기 위한 설정이다. 연관된 엔티티를 조회할 때 한 번에 최대 100개씩 배치로 가져온다.
[8] open-in-view: false는 OSIV 패턴을 비활성화한다. OSIV를 비활성화하면 트랜잭션 범위 외에서 지연 로딩이 불가능해지지만, 커넥션을 장시간 점유하는 문제를 방지할 수 있다.
[9] Redis는 인메모리 데이터 저장소로, 세션 저장, 캐싱, 메시지 브로커 등으로 사용된다. 로컬호스트의 6379 포트에 연결한다.
[10] Spring Security의 OAuth 2.0 클라이언트 설정이다. 구글과 카카오 소셜 로그인을 지원한다.
[11] Google OAuth 2.0 설정으로, 사용자의 이메일과 프로필 정보를 요청하는 권한을 가진다.
[12] Kakao OAuth 2.0 설정으로, 닉네임과 이메일 정보를 요청한다. 권한 부여 타입은 authorization_code 방식을 사용한다.
[13] client-authentication-method: client_secret_post는 클라이언트 인증 시 비밀키를 HTTP POST 본문으로 전송하는 방식이다. 카카오는 이 방식을 요구한다.
[14] Spring Boot Actuator 설정으로, 애플리케이션의 모니터링과 관리를 위한 엔드포인트를 제공한다.
[15] include: "health,metrics,info,prometheus"는 네 가지 엔드포인트를 외부에 노출한다: 헬스 체크, 메트릭, 애플리케이션 정보, Prometheus 형식 메트릭.
[16] 로깅 레벨 설정으로, 프로젝트 코드와 Hibernate SQL 쿼리에 대해 디버그 수준의 로깅을 활성화한다.
5. 통합 테스트 어노테이션 (src/test/java/com/example/secondhandmarket/config/IntegrationTest.java)
package com.example.secondhandmarket.config;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// [1] 커스텀 어노테이션 정의
@Target(ElementType.TYPE) // 클래스에 적용 가능
@Retention(RetentionPolicy.RUNTIME) // 런타임까지 유지
@SpringBootTest // [2] Spring Boot 통합 테스트 활성화
@Import({TestDatabaseConfig.class, TestRedisConfig.class}) // [3] 테스트 설정 클래스들 임포트
public @interface IntegrationTest {
// [4] 통합 테스트를 위한 메타 어노테이션
}
주석 설명
[1] 사용자 정의 어노테이션을 정의한다. @Target(ElementType.TYPE)은 이 어노테이션이 클래스에만 적용될 수 있음을 의미한다. @Retention(RetentionPolicy.RUNTIME)은 어노테이션이 런타임까지 유지되어 리플렉션을 통해 접근 가능함을 의미한다.
[2] @SpringBootTest는 Spring Boot의 통합 테스트를 활성화한다. 이 어노테이션은 전체 애플리케이션 컨텍스트를 로드하고, 모든 빈을 등록하며, 실제 서버 환경과 유사한 상태에서 테스트를 실행한다.
[3] @Import는 지정된 설정 클래스들을 테스트 컨텍스트에 포함시킨다. TestDatabaseConfig와 TestRedisConfig는 각각 테스트용 데이터베이스와 Redis 설정을 제공한다.
[4] @IntegrationTest는 메타 어노테이션 패턴의 예시이다. 반복적으로 사용되는 여러 어노테이션을 하나의 커스텀 어노테이션으로 묶어 코드의 가독성과 재사용성을 높인다.
6. 테스트 데이터베이스 설정 (src/test/java/com/example/secondhandmarket/config/TestDatabaseConfig.java)
package com.example.secondhandmarket.config;
import jakarta.annotation.PreDestroy;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.junit.jupiter.Container;
@TestConfiguration // [1] 테스트 전용 설정 클래스 표시
public class TestDatabaseConfig {
// [2] Testcontainers MySQL 컨테이너 선언
@Container
private static final MySQLContainer<?> mysqlContainer;
// [3] 정적 초기화 블록: 클래스 로딩 시 실행
static {
// 실제 운영 환경과 동일한 버전의 MySQL 이미지 사용
mysqlContainer = new MySQLContainer<>("mysql:8.0.33")
.withDatabaseName("testdb") // 테스트 데이터베이스 이름
.withUsername("test") // 테스트 사용자명
.withPassword("test"); // 테스트 비밀번호
mysqlContainer.start(); // [4] MySQL 컨테이너 시작
// [5] 컨테이너의 동적 JDBC URL 및 접속 정보를 시스템 프로퍼티에 주입
System.setProperty("spring.datasource.url", mysqlContainer.getJdbcUrl() + "?rewriteBatchedStatements=true");
System.setProperty("spring.datasource.username", mysqlContainer.getUsername());
System.setProperty("spring.datasource.password", mysqlContainer.getPassword());
}
@Bean // [6] MySQL 컨테이너를 Spring 빈으로 등록
public MySQLContainer<?> mySQLContainer() {
return mysqlContainer;
}
@PreDestroy // [7] 애플리케이션 종료 시 실행되는 메서드
public void stop() {
if (mysqlContainer != null && mysqlContainer.isRunning()) {
mysqlContainer.stop(); // [8] 테스트 종료 시 MySQL 컨테이너 정지
}
}
}
주석 설명
[1] @TestConfiguration은 테스트 환경에서만 사용되는 설정 클래스임을 나타낸다. 이 클래스에 정의된 빈들은 테스트 컨텍스트에만 등록된다.
[2] @Container는 Testcontainers 라이브러리에서 컨테이너 인스턴스를 관리하기 위한 어노테이션이다. static으로 선언되어 클래스 수명 주기와 함께한다.
[3] 정적 초기화 블록은 클래스가 JVM에 로드될 때 한 번 실행된다. 여기서 MySQL 컨테이너를 생성하고 시작한다.
[4] mysqlContainer.start()는 실제 도커 컨테이너를 시작한다. 이 메서드는 컨테이너 이미지를 풀(pull)하고, 컨테이너를 실행하며, 필요한 포트를 바인딩한다.
[5] System.setProperty()를 사용하여 동적으로 생성된 데이터베이스 연결 정보를 시스템 속성으로 설정한다. 이렇게 하면 Spring Boot의 application.yaml 설정보다 우선순위가 높아 테스트 시 이 값들이 사용된다.
[6] @Bean 메서드는 MySQL 컨테이너 인스턴스를 Spring 빈으로 등록한다. 이렇게 하면 테스트에서 @Autowired로 주입받아 사용할 수 있다.
[7] @PreDestroy는 Spring 빈이 소멸되기 직전에 호출되는 메서드에 붙이는 어노테이션이다. 테스트가 종료되면 자동으로 호출된다.
[8] mysqlContainer.stop()은 실행 중인 도커 컨테이너를 정지하고 제거한다. 이로 인해 테스트 간 격리가 보장되고 자원이 정리된다.
7. 테스트 Redis 설정 (src/test/java/com/example/secondhandmarket/config/TestRedisConfig.java)
package com.example.secondhandmarket.config;
import jakarta.annotation.PreDestroy;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.junit.jupiter.Container;
@TestConfiguration // 테스트 전용 설정 클래스
public class TestRedisConfig {
// [1] Testcontainers Redis 컨테이너 선언
@Container
private static final GenericContainer<?> redisContainer;
// 정적 초기화 블록
static {
redisContainer = new GenericContainer<>("redis:7.0") // Redis 7.0 버전 사용
.withExposedPorts(6379); // [2] 컨테이너 내부의 6379 포트 노출
redisContainer.start(); // Redis 컨테이너 시작
// [3] 동적으로 할당된 호스트와 포트 번호를 Spring 설정에 주입
String host = redisContainer.getHost(); // 컨테이너 호스트(도커 데몬 호스트)
Integer port = redisContainer.getMappedPort(6379); // [4] 호스트에 매핑된 포트
System.setProperty("spring.data.redis.host", host);
System.setProperty("spring.data.redis.port", port.toString());
}
@Bean
public GenericContainer<?> redisContainer() {
return redisContainer;
}
@PreDestroy
public void stop() {
if (redisContainer.isRunning()) {
redisContainer.stop(); // 테스트 종료 시 Redis 컨테이너 정지
}
}
}
주석 설명
[1] GenericContainer는 Testcontainers의 범용 컨테이너 클래스로, 다양한 도커 이미지를 실행할 수 있다. 여기서는 Redis 이미지를 실행한다.
[2] withExposedPorts(6379)는 컨테이너 내부의 6379 포트를 호스트에 노출시킨다. Testcontainers는 이 포트를 호스트의 사용 가능한 포트에 자동으로 매핑한다.
[3] Redis 컨테이너가 시작된 후, 동적으로 할당된 호스트 주소와 포트 번호를 시스템 속성으로 설정한다. 이 값들은 Spring의 Redis 자동 설정에서 사용된다.
[4] getMappedPort(6379)는 컨테이너 내부의 6379 포트가 호스트의 어떤 포트에 매핑되었는지 반환한다. Testcontainers는 충돌을 피하기 위해 매번 다른 포트를 할당한다.
8. 구성 파일들의 상호 연관성
이 다섯 개의 구성 파일은 Secondhand Market 프로젝트의 핵심 인프라 설정을 정의한다:
- Prometheus 설정 (prometheus.yaml): 프로덕션/개발 환경에서 애플리케이션과 Redis의 메트릭을 수집한다.
- 애플리케이션 설정 (application.yaml): 런타임 환경에서 데이터베이스, Redis, OAuth, 모니터링 등을 구성한다.
- 통합 테스트 어노테이션 (IntegrationTest.java): 테스트 코드의 가독성과 재사용성을 높이는 메타 어노테이션을 제공한다.
- 테스트 데이터베이스 설정 (TestDatabaseConfig.java): Testcontainers를 이용해 실제 MySQL 데이터베이스를 테스트 환경에서 실행한다.
- 테스트 Redis 설정 (TestRedisConfig.java): Testcontainers를 이용해 실제 Redis를 테스트 환경에서 실행한다.
이러한 설정들은 다음과 같은 이점을 제공한다:
- 환경 일관성: 개발, 테스트, 프로덕션 환경에서 동일한 데이터베이스와 Redis 버전 사용
- 테스트 신뢰성: 인메모리 데이터베이스(H2) 대신 실제 MySQL과 Redis를 테스트
- 모니터링 통합: Prometheus를 통한 실시간 애플리케이션 모니터링
- 보안 표준 준수: OAuth 2.0을 통한 안전한 소셜 로그인 구현
- 성능 최적화: 데이터베이스 커넥션 풀, 배치 처리, N+1 문제 해결 등의 설정
이 구조는 현대적인 Spring Boot 애플리케이션의 모범 사례를 따르며, 마이크로서비스 아키텍처로의 확장을 고려한 설계이다.
'Project > Secondhand Market' 카테고리의 다른 글
| [6] 성능 최적화(4): Async 적용 (0) | 2026.01.25 |
|---|---|
| [5] 성능 최적화(3): N+1 (0) | 2026.01.25 |
| [4] 성능 최적화(2): Lock (0) | 2026.01.25 |
| [3] 성능 최적화(1): 복합 Index & FullText Index (1) | 2026.01.21 |
| [1] 프로젝트 소개 (0) | 2026.01.10 |
