[2] 프로젝트 모니터링 설정과 테스트 환경 구축

2026. 1. 10. 17:05·Project/Secondhand Market

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 프로젝트의 핵심 인프라 설정을 정의한다:

  1. Prometheus 설정 (prometheus.yaml): 프로덕션/개발 환경에서 애플리케이션과 Redis의 메트릭을 수집한다.
  2. 애플리케이션 설정 (application.yaml): 런타임 환경에서 데이터베이스, Redis, OAuth, 모니터링 등을 구성한다.
  3. 통합 테스트 어노테이션 (IntegrationTest.java): 테스트 코드의 가독성과 재사용성을 높이는 메타 어노테이션을 제공한다.
  4. 테스트 데이터베이스 설정 (TestDatabaseConfig.java): Testcontainers를 이용해 실제 MySQL 데이터베이스를 테스트 환경에서 실행한다.
  5. 테스트 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
'Project/Secondhand Market' 카테고리의 다른 글
  • [5] 성능 최적화(3): N+1
  • [4] 성능 최적화(2): Lock
  • [3] 성능 최적화(1): 복합 Index & FullText Index
  • [1] 프로젝트 소개
h6bro
h6bro
백엔드 개발자의 기술 블로그
  • h6bro
    Jun's Tech Blog
    h6bro
  • 전체
    오늘
    어제
    • 분류 전체보기 (250) N
      • Java (18)
        • Core (9)
        • Design Pattern (9)
      • Spring (80)
        • Core (24)
        • MVC (6)
        • DB (10)
        • JPA (26)
        • Monitoring (3)
        • Security (11)
        • WebSocket (0)
      • Database (33)
        • Redis (15)
        • MySQL (18)
      • MSA (25) N
        • MSA 기본 (11)
        • MSA 아키텍처 (14) N
      • Kafka (30)
        • Core (18)
        • Connect (12)
      • ElasticSearch (11)
        • Search (11)
        • Logging (0)
      • Test (4)
        • k6 (4)
      • Docker (9)
      • CI&CD (10)
        • GitHub Actions (6)
        • ArgoCD (4)
      • Kubernetes (18)
        • Core (12)
        • Ops (6)
      • Cloud Engineering (4)
        • AWS Infrastructure (3)
        • AWS EKS (1)
        • Terraform (0)
      • Project (8)
        • LinkFolio (1)
        • Secondhand Market (7)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

    • Cloud Engineering 포스팅 정리
  • 인기 글

  • 태그

    ㅈ
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
h6bro
[2] 프로젝트 모니터링 설정과 테스트 환경 구축
상단으로

티스토리툴바