Projects/[Spring] Ticketing App Project

[Ticketing App Project] 우당탕탕 프로파일 분리와 파라미터 스토어 연결 성공기

montmer27 2026. 3. 15. 00:58
요약
지난 3일간, 프로파일 분리도 모르던 내가 프로파일을 3개(공통, 로컬, 배포)로 분리하고, 부분적이지만 ci 파이프라인을 구축하고, 로컬 개발 환경을 도커로 띄우는 법을 배우고, 배포 환경에서 파라미터 스토어로부터 값을 읽어올 수 있기까지의 여정이다.

 

1. 발단

이번 프로젝트에서는 내가 인프라 구축을 맡았다. "aws만 잘 공부하면 되는 거 아니야?"라고 생각했던 것은 큰 오산이었다(참고: 오산은 대도시가 아니다). 

 

2. 전개

1. Docker Compose로 컨테이너 생성하기
난이도: 🔥

우선 모든 환경에서 동일하게 실행할 수 있도록 도커를 구축하는 것이 첫째였다. 우리 팀은 Redis와 MySQL을 사용하고 있었으므로 Redis, MySQL, Spring Boot 서버 컨테이너를 올바른 순서대로 만들어 주고 같은 네트워크 안에 묶어주는 Docker Compose부터 구현해야 했다.

그러던 중, docker compose가 이미지를 빌드하기 위해 필요한 jar 파일을 생성하는 bootJar 명령에서 계속 에러가 발생했다. 알아보니 bootJar 과정에서 테스트를 실행하는데, 테스트가 Spring Context를 띄우려고 하는 상황에서 로컬에서 DB를 찾지 못해 모든 테스트가 contextLoads() 실패하면서 연쇄적으로 에러가 터진 것이었다.

그래서 build.gradle에 h2 의존성을 추가하고, test를 위한 application.yml 파일을 다음과 같이 추가하면서 해결했다.

dependencies { testImplementation 'com.h2database:h2' }
spring:
  config:
    import: "optional:aws-parameterstore:"
  datasource:
    url: jdbc:h2:mem:testdb
    driver-class-name: org.h2.Driver
    username: sa
    password:
  jpa:
    hibernate:
      ddl-auto: create-drop
    database-platform: org.hibernate.dialect.H2Dialect

# 테스트용 더미값 추가
jwt:
  secret:
    key: test-secret-key-for-junit-test-only
  expiration: 1800000

# AWS 설정 추가
cloud:
  aws:
    credentials:
      access-key: test
      secret-key: test
    region:
      static: ap-northeast-2
    stack:
      auto: false

 

2. 프로파일별로 yml 분리하기
난이도: 🔥

배포 환경을 위한 yaml을 만들기 전, 기존 yml에서 로컬과 배포 환경 모두에 적용될 설정과 로컬에만 적용될 설정들을 분리해야 했다. 프로파일을 지정하는 순간 로컬 환경에선 공통 yml(application.yml)과 해당 프로파일(예: application-local.yml) 모두 적용되기 때문이다. 이에 공통과 로컬 환경의 yml 파일을 다음과 같이 분리했다.

(공통) application.yml

spring:
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    properties:
      hibernate:
        format_sql: true
    open-in-view: false
  jackson:
    time-zone: Asia/Seoul

server:
  port: 8080

management:
  endpoints:
    web:
      exposure:
        include: health, info

jwt:
  expiration: 1800000

logging:
  level:
    org.hibernate.SQL: debug
    org.hibernate.orm.jdbc.bind: trace
    org.springframework.security: debug


(로컬) application-local.yml

spring:
  datasource:
    url: ${DB_URL}
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}
    driver-class-name: com.mysql.cj.jdbc.Driver
  data:
    redis:
      host: redis
      port: 6379

jwt:
  secret:
    key: ${JWT_SECRET}
  expiration: 1800000


3. actuator 추가하여 서버 모니터링하기
난이도: 🔥

로컬과 배포 환경 모두 서버가 정상적으로 띄워졌는지 간편하게 확인하기 위해 Spring actuator 의존성을 추가했다.

# build.gradle
implementation 'org.springframework.boot:spring-boot-starter-actuator'

서버는 시작됐는데, 엔드포인트를 올바르게 입력했음에도 헬스체크가 되지 않았다. 알고 보니 SecurityConfig에서 actuator 경로의 권한검사 면제를 설정하지 않았던 것이 원인이었다. 그래서 다음과 같이 헬스체크 경로를 권한검사에서 제외시켰다.

# SecurityConfig.java
   .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/auth/**").permitAll()
                        .requestMatchers("/actuator/**").permitAll()
                        .requestMatchers("/admin/**").hasRole("ADMIN")
                        .anyRequest().authenticated()
                )

 

3. 위기

4. ci 파이프라인 작성과 끝없는 테스트 실패
난이도: 🔥 🔥 🔥

뭔가 이상하다. ci 파이프라인 작성까진 순조로웠다. push를 하면 테스트 코드를 돌리면서 데이터 무결성을 검증해야 하는데, 로컬 환경에선 계속 성공하던 테스트가 계속 실패하는 것이었다. 

어떻게 이런 일이 있을 수 있을까 알아보니, 테스트 코드 중 ReservationService.java의 테스트 코드에서 일부 필드가 stub되지 않아 NPE가 발생했다고 한다. 그래서 테스트 코드를 수정했지만, 에러는 여전했다.다시 알아보니, 배포 환경 설정 파일(application-prod.yml)을 만들면서 추가했던 aws parameter store 의존성 때문에 테스트 시 자동으로 aws 연결을 시도하는 것이었다. 그런데 aws 리전 설정 자체가 아무데도 없어서 sdk가 결국 못 찾고 에러를 던진 것이었다. 그래서 application.yml에 다음 값을 직접 추가했다.

cloud:
  aws:
    region:
      static: ap-northeast-2
    stack:
      auto: false

 

5. 영 말을 듣지 않는 배포 환경에서(profile = prod) 실행 성공시키기
난이도: 🔥🔥🔥

알고 보니 접속을 시도할 때 redis:7.0이라는 호스트로 접속을 시도한 게 문제였다. 버전명은 호스트명에 포함되면 안 된단다. 그래서 application-prod.yml을 다음과 같이 수정했다.

spring:
  data:
    redis:
      host: redis      # redis:7.0 → redis (도커 컴포즈 서비스명)
      port: 6379

그런데 여전히 실행은 되지 않았다. 알고 봤더니 프로파일만 prod로 바꾸고 실행하는 건 docker-compose를 통해 실행하는 것이 아닌 호스트에서 직접 실행하는 것이기 때문에, 설정을 redis가 아닌 localhost로 다시 바꾼 뒤 포트 매핑도 다르게 해줘야 하는 것이었다. 나 미쳐버려

그래서 임시로 다음과 같이 바꾸고 실행했더니 정상적으로 실행도 잘 되고, 헬스체크도 잘 됐다!
(그런데 이제 실제 배포 환경용으로 사용하려면 저걸 또 redis로 바꿔야 한다는...)

data:
  redis:
    host: localhost
    port: 6379

 

아무튼, 오늘 너무 스트레스 많이 받았지만 그래도 어떻게든 결말을 봤다!
파라미터 스토어도 정상적으로 연결됐고, 끝내 "Hello From Parameter Store~!"이라는 메시지를 받아냈다.

뿌듯.