🍃SpringBoot

Spring Boot: Docker Compose와 application.yml 설정 기준

limdaeil 2025. 10. 21. 15:29

질문의 배경

Docker Compose와 application-dev.yml 파일에서 둘 다 설정이 가능한 Character Set, Collation, TimeZone, SQL Mode 등의 코드를 중복되게 작성한 코드를 보면서 문득 궁금증이 생겼습니다. Docker Compose와 application.yml에서 설정은 "무엇을 기준으로 설정할까?"에 대해 스스로 질문을 답변하는 정리 내용입니다.

1. 설정 기준

Docker Compose와 application.yml의 설정 기준을 크게 바라보면 "무엇을 제어하는가"에 따라서 명확하게 구분합니다.

  • Docker Compose: 컨테이너(인프라) 환경을 설정
  • application.yml: 해당 컨테이너에서 실행되는 애플리케이션의 동작을 설정

1.1 Docker Compose (docker-compose.yml)는 인프라 담당자 🏠

docker-compose.yml 파일은 애플리케이션이 살아갈 '집'을 짓고 꾸미는 역할을 합니다.
데이터베이스, 메시지 큐 등 애플리케이션이 의존하는 서비스들의 실행 환경을 정의하고 구성합니다.

집 = Docker Compose, 집 안의 방 = Service

 

docker-compose.yml에서 서비스들의 핵심 설정 대상은 다음과 같습니다.

1. 어떤 소프트웨어를 사용할 것인가?

    사용할 이미지와 버전을 지정합니다.

2. 컨테이너 기본 환경 설정

    컨테이너가 처음 시작될 때 필요한 환경 변수를 설정합니다.
    MySQL의 경우, 초기 데이터베이스 생성, 유저 및 비밀번호 설정 등이 여기에 해당합니다.

3. 네트워킹 및 포트

     컨테이너 외부 또는 다른 컨테이너와 어떻게 통신할지 정의합니다.

4. 데이터 영속성

     컨테이너가 사라져도 데이터가 유지되도록 볼륨을 설정합니다.

5. 서버 레벨의 동작 제어

     데이터베이스 서버 자체의 동작 방식을 제어하는 명령어를 설정합니다.
     command: MySQL 서버의 기본 캐릭터셋을 utf8mb4로 설정하는 등의 서버 기본 동작을 제어합니다.

     docker-compose.yml의 설정은 애플리케이션 코드와는 독립적입니다.
모두 설정된 MySQL 컨테이너는 어떤 언어의 애플리케이션과도 연결될 수 있는 독립적인 데이터베이스 서버입니다.

1.2 Application YAML (application.yml): 애플리케이션 전문가 👨‍💻

application.yml 파일은 집 안에서 살아가는 '사람(애플리케이션)'이 어떻게 행동할지를 결정합니다.
애플리케이션의 내부 로직, 외부 서비스와의 연결 방법 등을 상세하게 설정합니다.

방 안의 Service

 

application.yml에서 서비스들의 핵심 설정 대상은 다음과 같습니다.

1. 데이터베이스 연결 정보

  • spring.datasource.url: 접속할 DB의 주소입니다.
  • spring.datasource.username, spring.datasource.password: 접속에 사용할 계정 정보입니다.
    애플리케이션이 어떤 데이터베이스에, 어떤 계정으로 접속할지를 명시합니다.

2. 애플리케이션 동작 설정

  • JPA/Hibernate 설정: DDL(Data Definition Language) 자동 생성 전략, Naming 전략 등 ORM 동작 방식을 제어합니다.
  • 커넥션 풀(Connection Pool) 설정: 데이터베이스 연결을 몇 개까지 유지하고 어떻게 관리할지 등을 설정합니다.

3. 로깅, 캐싱 등 기타 애플리케이션 로직에 필요한 모든 설정을 담당

  • application.yml의 설정은 애플리케이션이 어떻게 '동작'할 것인가에 초점을 맞춥니다.
  • 데이터베이스가 Docker 컨테이너인지, 클라우드 서비스인지는 신경 쓰지 않습니다. 오직 접속 정보(주소, 계정)만 알면 됩니다.

1.3 예시로 비교

docker-compose.yml로 MySQL 실행 구성한 예제 코드로 역할 구분을 살펴보겠습니다.

docker-compose.yml 예제 코드

services:
  # 애플리케이션 서비스 (예: Spring Boot)
  my-app:
    build: .
    ports:
      - "8080:8080"
    depends_on:
      - mysql-db
    environment:
      # application.yml의 값을 여기서 오버라이드 할 수도 있습니다.
      - SPRING_DATASOURCE_URL=jdbc:mysql://mysql-db:3306/mydb
      - SPRING_DATASOURCE_USERNAME=myuser
      - SPRING_DATASOURCE_PASSWORD=mypassword

  # MySQL 데이터베이스 서비스
  mysql-db:
    image: mysql:8.0
    container_name: mysql-container
    ports:
      - "3306:3306"
    volumes:
      - ./db/data:/var/lib/mysql # 데이터 영속성
    environment:
      # MySQL 컨테이너 자체를 초기화하는 설정
      - MYSQL_ROOT_PASSWORD=rootpassword
      - MYSQL_DATABASE=mydb
      - MYSQL_USER=myuser
      - MYSQL_PASSWORD=mypassword
    command:
      # MySQL 서버의 동작 방식을 설정
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci

mysql-db 서비스mysql:8.0 이미지를 사용해 컨테이너를 생성합니다. 컨테이너가 처음 실행될 때 mydb라는 데이터베이스와 myuser라는 사용자를 mypassword라는 비밀번호로 생성하도록 환경(environment)을 설정했습니다. 또한, 서버의 기본 문자 인코딩을 utf8mb4로 지정하는 명령(command)을 내렸습니다.

 

application.yml 예제 코드

spring:
  datasource:
    # Docker Compose의 서비스 이름(mysql-db)을 호스트 이름으로 사용
    url: jdbc:mysql://mysql-db:3306/mydb?useSSL=false&allowPublicKeyRetrieval=true
    username: myuser
    password: mypassword
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: update # 애플리케이션 실행 시 DDL을 어떻게 처리할지
    show-sql: true # SQL 쿼리 로깅 여부

spring.datasource 설정으로 애플리케이션이 mysql-db라는 호스트에 있는 mydb 데이터베이스에 myuser/mypassword 계정으로 접속하도록 연결 정보를 설정했습니다. spring.jpa 설정은 애플리케이션이 JPA를 통해 데이터베이스와 상호작용하는 방식을 설정했습니다.

 

이처럼 두 파일의 역할을 명확히 분리하면, 인프라 환경(Docker)과 애플리케이션 코드를 독립적으로 관리할 수 있어 훨씬 유연하고 확장성 있는 개발이 가능해집니다.

구분 docker-compose.yml (인프라) application.yml (애플리케이션)
관점 "어떤 환경에서 실행될 것인가?" "어떻게 동작할 것인가?"
역할 컨테이너 생성, 네트워크 설정, 볼륨 연결, 환경 변수 주입 DB 연결, 비즈니스 로직 제어, 외부 API 연동
MySQL 예시 MySQL 버전, 초기 DB/유저 생성, 포트 매핑, 캐릭터셋 설정 DB 접속 URL, 유저/비밀번호, 커넥션 풀, JPA 설정

2. 설정이 겹치는 경우

Character Set, Collation, TimeZone, SQL Mode 등의 설정은 docker-compose.yml, application.yml에서 모두 가능합니다.

이때의 핵심 원칙은 "설정의 영향 범위를 어디까지로 할 것인가?" 입니다.
결론부터 말하면, 대부분의 경우 docker-compose.yml에서 설정하는 것이 정답에 가깝습니다.

2.1 가장 넓고 근본적인(Global) 레벨에서 설정

docker-compose.yml서버 자체의 기본 동작(Global Default)을 정의합니다. 반면 application.yml의 JDBC URL 파라미터 등은 해당 애플리케이션이 만드는 개별 연결(Session)에만 적용되는 지역 설정(Session Level)입니다. 정말 중요한 개념입니다!

서버의 기본값이 잘 설정되어 있으면, 어떤 클라이언트(내 애플리케이션, 다른 마이크로서비스, 데이터베이스 관리 툴 등)가 접속하더라도 일관된 환경에서 동작하는 것을 보장할 수 있습니다. 이것이 훨씬 안정적이고 예측 가능한 방법입니다. 각각의 경우를 살펴보겠습니다.

문자셋 (Character Set) / 콜레이션 (Collation)

  • 권장 위치: docker-compose.yml
  • 설정 방법: command 옵션 사용
# docker-compose.yml
services:
  mysql-db:
    image: mysql:8.0
    # ...
    command:
      - --character-set-server=utf8mb4 # 문자셋
      - --collation-server=utf8mb4_unicode_ci # 콜레이션

문자셋은 데이터베이스에 데이터가 저장되는 방식 그 자체를 결정하는 매우 근본적인 설정입니다. 만약 application.yml의 JDBC URL에만 문자셋(?characterEncoding=UTF-8)을 설정하면, 해당 애플리케이션의 '연결'에만 적용됩니다.

 

이 경우, 다른 툴(e.g., DBeaver, DataGrip)로 DB에 직접 접속하거나, 다른 애플리케이션이 접속했을 때 서버의 기본값(기본적으로 latin1)을 따르게 되어 데이터가 깨지거나 정렬이 이상하게 되는 심각한 문제가 발생할 수 있습니다.

 

따라서 docker-compose.yml에서 서버의 기본 문자셋을 utf8mb4로 명확히 지정하여, 누가 접속하든 동일한 기준으로 데이터가 처리되도록 해야 합니다.

타임존 (Timezone)

  • 권장 위치: docker-compose.yml
  • 설정 방법: environment 옵션 사용
# docker-compose.yml
services:
  mysql-db:
    image: mysql:8.0
    # ...
    environment:
      - TZ=Asia/Seoul

데이터베이스 서버의 타임존은 NOW(), CURRENT_TIMESTAMP 같은 함수가 어떤 시간을 기준으로 동작할지를 결정합니다.

이것 역시 서버 전체에 일관되게 적용되어야 하는 글로벌 설정입니다. 특정 애플리케이션의 연결에만 타임존이 다르게 적용되면, 같은 함수를 호출해도 결과가 달라져 데이터 정합성에 큰 혼란을 초래합니다.

 

애플리케이션 단에서는 서버에 저장된 시간(주로 UTC 또는 서버 표준시)을 가져와 사용자의 지역에 맞게 변환하여 보여주는 역할을 담당하는 것이 올바른 역할 분리입니다.

 

SQL 모드 (SQL Mode)

  • 권장 위치: docker-compose.yml
  • 설정 방법: command 옵션 사용
# docker-compose.yml
services:
  mysql-db:
    image: mysql:8.0
    # ...
    command:
      # 기존 command에 추가
      - --sql-mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION

SQL 모드는 MySQL이 SQL 쿼리를 얼마나 엄격하게 해석하고 검증할지를 결정하는 규칙입니다. (ex GROUP BY 정책, 날짜 형식 검증 등)

이 규칙은 데이터 무결성과 직결되므로, 특정 애플리케이션에만 국한되어서는 안 됩니다. 모든 클라이언트가 동일한 SQL 규칙의 적용을 받아야 데이터의 일관성과 무결성을 보장할 수 있습니다.

서버 전체의 SQL 모드를 docker-compose.yml에서 설정하여 데이터 검증 정책을 통일하는 것이 필수적입니다.

설정 항목 권장 위치 이유
문자셋/콜레이션 docker-compose.yml 데이터 저장 방식의 근본. 모든 클라이언트에 동일하게 적용되어야 함
타임존 docker-compose.yml NOW() 등 시간 함수의 기준. 서버 전체에 일관성 보장
SQL 모드 docker-compose.yml 데이터 유효성 검사 규칙. 데이터 무결성을 위해 서버 전체에 통일

"서버의 환경은 docker-compose.yml에서 확정하고, 애플리케이션은 그 환경에 접속해서 사용한다" 는 대원칙을 기억하시면 혼란 없이 적절한 위치에 설정을 배치할 수 있습니다.

2.2 예외: application.yml에서 설정하는 경우

그렇다면 application.yml의 연결 파라미터는 언제 사용할까요?

서버의 글로벌 설정을 특정 세션에서만 일시적으로 변경해야 할 때 사용합니다.

예를 들어, 평소에는 utf8mb4를 사용하는 것이 기본이지만, 아주 특수한 경우로 latin1으로 인코딩된 데이터를 가진 외부 레거시 DB와 연동해야 하는 '읽기 전용' 연결이 필요할 수 있습니다. 이때는 해당 데이터소스(DataSource) 설정에만 JDBC URL 파라미터로 ?characterEncoding=latin1을 명시하여 임시로 규칙을 오버라이드 할 수 있습니다.

3. 실전 코드

실전 코드를 살펴보기 전에 다시 docker-compose.yml, application.yml 파일 설정 기준을 복습하겠습니다.

  • Docker Compose: 컨테이너(인프라) 환경을 설정
  • application.yml: 해당 컨테이너에서 실행되는 애플리케이션의 동작을 설정

docker-compose.yml서버 자체의 기본 동작(Global Default)을 정의합니다. 반면 application.yml의 JDBC URL 파라미터 등은 해당 애플리케이션이 만드는 개별 연결(Session)에만 적용되는 지역 설정(Session Level)입니다.

이제 개인 프로젝트에서 작성된 docker-compose.yml, application.yml 파일 안의 일부 코드로 어떻게 적용하는지 살펴보겠습니다. 파일 안의 설정에서 다양한 옵션들이 존재하는 옵션들을 깊게 보는 것 보다 어떠한 항목을 작성했는지를 중점적으로 학습합니다!

3.1 docker-compose.yml

services:
  mysql:
    image: mysql:8.4 # MySQL 8.4 LTS 공식 이미지
    container_name: shopping-mysql # 컨테이너명
    restart: unless-stopped # 장애/부팅 시 자동 재시작
    ports: # 포트
      - "${MYSQL_PORT:-3333}:3306" 
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
      TZ: "Asia/Seoul" # 컨테이너 타임존
    command: # 서버 레벨 설정
      - --default-time-zone=+09:00 # 세션/서버 기본 타임존
      - --character-set-server=utf8mb4  # 문자셋
      - --collation-server=utf8mb4_0900_ai_ci # 콜레이션
      - --sql-mode=STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION # SQL 모드
      - --max_allowed_packet=256M # 최대 허용 패킷 크기
    volumes:
      - shopping_mysql_data:/var/lib/mysql # 데이터 영속화
    healthcheck: # 서비스 체크
      test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-uroot", "-p${MYSQL_ROOT_PASSWORD}"]
      interval: 10s
      timeout: 5s
      retries: 10
      start_period: 20s

volumes:
  shopping_mysql_data:

image: mysql:8.4

  • 설명: 컨테이너를 생성할 Docker 이미지를 지정합니다. MySQL 8.4 LTS(장기 지원) 버전을 사용합니다.
  • 기본값: 없음 (필수 항목).
  • Trade-off:
    • 장점: latest 대신 8.4와 같이 구체적인 버전을 명시하는 것은 매우 중요합니다. 이를 통해 다른 환경에서도 동일한 버전의 MySQL을 설치하여 예측 가능성을 높이고, 갑작스러운 메이저 버전 업데이트로 인한 문제를 방지할 수 있습니다. 특히 LTS 버전은 안정성이 검증되어 프로덕션 환경에 적합합니다.
    • 단점: 마이너 보안 패치가 자동으로 적용되지 않으므로, 주기적으로 이미지 버전을 수동으로 업데이트해야 합니다.

container_name: shopping-mysql

  • 설명: 생성될 컨테이너에 shopping-mysql이라는 고정된 이름을 부여합니다.
  • 기본값: Docker가 프로젝트명_서비스명_숫자 형태의 임의의 이름을 생성합니다. (예: myproject_mysql_1)
  • Trade-off:
    • 장점: 이름이 고정되어 docker logs shopping-mysql처럼 명령어 라인에서 다루기 편리합니다. 개발 환경에서 특히 유용합니다.
    • 단점: 이름이 중복될 수 없으므로, docker-compose up --scale mysql=2와 같이 서비스를 확장(scaling)할 수 없습니다. 단일 인스턴스로 운영할 서비스에 적합합니다.

restart: unless-stopped

  • 설명: 컨테이너가 수동으로 중지(docker stop)되지 않는 한, 오류로 종료되거나 시스템이 재부팅될 때마다 항상 자동으로 재시작합니다.
  • no: 기본값으로, 재시작하지 않습니다. 컨테이너가 중지되면 다시 시작되지 않으며, 수동으로 재실행해야 합니다.
  • on-failure: 컨테이너가 비정상 종료(에러 코드 반환)된 경우에만 재시작합니다.
  • always: 컨테이너가 중지될 경우 항상 재시작하며, 시스템 재부팅 시에도 자동으로 재시작됩니다.
  • Trade-off:
    • 장점: 안정성과 가용성을 크게 향상시킵니다. 예상치 못한 서버 장애나 오류 발생 시 자동으로 서비스를 복구해 줍니다. always와 달리, 점검 등을 위해 수동으로 중지시킨 컨테이너가 저절로 다시 켜지는 것을 방지하여 운영 편의성이 높습니다. 대부분의 운영 환경에서 가장 이상적인 정책입니다.
    • 단점: 명확한 단점은 없으나, 컨테이너가 계속 비정상적으로 재시작된다면 근본 원인을 파악하기 위해 로그를 반드시 확인해야 합니다.

ports: - "${MYSQL_PORT:-3333}:3306"

  • 설명: 호스트(PC)의 포트와 컨테이너의 포트를 연결합니다. 여기서는 컨테이너의 3306 포트를 호스트의 MYSQL_PORT 환경 변수 값으로 연결하고, 변수가 없다면 3333번 포트를 기본값으로 사용합니다.
  • 기본값: 외부 포트를 노출하지 않음. (컨테이너 간 통신은 가능)
  • Trade-off:
    • 장점: DBeaver나 DataGrip 같은 외부 데이터베이스 관리 툴에서 접속할 수 있게 해줍니다. 환경 변수와 기본값을 활용하여 포트 충돌을 유연하게 피할 수 있는 좋은 설정 방식입니다.
    • 단점: 데이터베이스 포트를 외부에 노출하는 것은 보안상 위험 요소가 될 수 있습니다. 운영 환경에서는 방화벽을 통해 특정 IP에서만 접근하도록 제한하는 것이 필수적입니다.

environment:

  • 설명: 컨테이너 내부에 환경 변수를 설정합니다. MySQL 이미지의 경우, 이 변수들을 사용해 최초 실행 시 데이터베이스, 사용자, 비밀번호 등을 초기화합니다.
  • 기본값: 설정된 변수 없음.
  • Trade-off:
    • 장점: .env 파일과 연동하여 민감한 정보(비밀번호 등)를 docker-compose.yml 파일에서 분리할 수 있어 보안에 유리합니다. (${MYSQL_PASSWORD} 구문)
    • TZ: "Asia/Seoul": 컨테이너의 OS 레벨 타임존을 설정합니다. 로그 파일의 시간 기록 등을 한국 시간으로 맞춰 디버깅에 용이합니다.

command:

  • 설명: 컨테이너 시작 시 실행될 기본 명령어를 덮어쓰거나 추가 옵션을 전달합니다. 여기서는 mysqld 서버의 글로벌 기본 동작을 설정하는 데 사용됩니다.
  • 기본값: 이미지에 내장된 기본 명령어 (mysqld).
  • Trade-off:
    • 장점: 모든 클라이언트 연결에 일관된 서버 정책(문자셋, 타임존, SQL 모드 등)을 적용하여 데이터 정합성과 예측 가능성을 보장합니다. 애플리케이션 연결(Session)마다 설정을 따로 하는 것보다 훨씬 안정적입니다.
    • --default-time-zone=+09:00: MySQL 서버 자체의 타임존을 설정합니다. NOW() 함수 등이 Asia/Seoul 기준(+09:00)으로 동작하게 됩니다. TZ와 함께 설정하여 OS와 DB의 시간대를 일치시키는 것이 좋습니다.
    • --character-set-server, --collation-server: utf8mb4는 이모지까지 포함하는 모든 문자를 지원하는 가장 표준적인 문자셋입니다. utf8mb4_0900_ai_ci는 MySQL 8.0 이상에서 성능과 정확도가 향상된 기본 콜레이션(정렬 규칙)입니다. 한글 데이터 처리 시 필수적인 설정입니다.
    • --sql-mode: 데이터 유효성 검사를 엄격하게 만들어 데이터 무결성을 높입니다. (예: 0으로 나누기 시도 시 경고 대신 에러 발생)
    • --max_allowed_packet=256M: 한 번의 쿼리로 전송할 수 있는 데이터의 최대 크기를 늘립니다. 기본값은 수 메가바이트(MB) 수준으로, 대용량 이미지나 파일을 BLOB, TEXT 타입으로 저장할 때 발생할 수 있는 오류를 방지합니다. 메모리 사용량은 소폭 증가하지만 안정성이 높아집니다.

volumes: - shopping_mysql_data:/var/lib/mysql

  • 설명: 컨테이너 내부의 데이터 경로(/var/lib/mysql)를 Docker가 관리하는 shopping_mysql_data라는 이름의 볼륨에 연결하여 데이터를 영속화합니다.
  • 기본값: 데이터가 컨테이너 내부에만 저장되어, 컨테이너 삭제 시 함께 사라집니다.
  • Trade-off:
    • 장점: 데이터베이스 운영에 있어 필수적인 설정입니다. 컨테이너를 삭제하거나 업데이트해도 데이터는 안전하게 보존됩니다. Docker가 관리하는 이름 지정 볼륨(Named Volume)은 호스트 경로를 직접 지정하는 방식(Bind Mount)보다 이식성과 성능 면에서 권장됩니다.
    • 단점: 명확한 단점은 없습니다.

healthcheck:

  • 설명: Docker가 주기적으로 컨테이너 내부에서 명령(mysqladmin ping)을 실행하여 서비스가 정상적으로 응답하는지 확인합니다.
  • 기본값: 헬스체크를 수행하지 않음.
  • Trade-off:
    • 장점: 컨테이너의 상태를 healthy 또는 unhealthy로 명확하게 파악할 수 있습니다. depends_on 같은 옵션과 연계하여 DB가 완전히 준비된 이후에 애플리케이션 컨테이너를 시작하도록 제어할 수 있어, 서비스 간 의존성 관리가 훨씬 안정적으로 이루어집니다.
    • 단점: 아주 약간의 시스템 부하가 발생하지만, 얻는 이점에 비하면 무시할 수 있는 수준입니다.

3.2 application-dev.yml

spring:
  datasource:
    # JDBC 접속 URL: 호스트/포트/DB명은 환경변수로 주입
    url: jdbc:mysql://${MYSQL_HOST}:3333/${MYSQL_DATABASE}
      ?useSSL=false # TLS 미사용(내부망/개발용이 아니면 권장 X → 운영은 useSSL=true + 인증서 구성 권장)
      &serverTimezone=Asia/Seoul # 드라이버가 서버 타임존을 Asia/Seoul로 해석(저장은 UTC 권장, 아래 참고)
      &allowPublicKeyRetrieval=true # RSA 공개키 자동 조회 허용(편의성↑, 보안성↓) → 운영은 가급적 비활성 또는 고정 키/CA 설정
      &rewriteBatchedStatements=true # 대량 insert 시 배치 성능 향상(VALUES 묶음 전송)
      &cachePrepStmts=true # PreparedStatement 캐시 사용으로 파싱/플랜 캐시 효과
      &prepStmtCacheSize=250 # PS 캐시 엔트리 개수
      &prepStmtCacheSqlLimit=2048 # 캐시 가능한 SQL 최대 길이(문자 수)
      &useServerPrepStmts=true # 서버 사이드 프리페어드 스테이트먼트 사용(네트워크 왕복↓, 서버 자원↑) – 워크로드에 따라 이득 상이

    driver-class-name: com.mysql.cj.jdbc.Driver
    username: ${MYSQL_USER}
    password: ${MYSQL_PASSWORD}

    hikari:
      pool-name: shopping-dev # HikariCP 풀 이름(모니터링 시 식별용)
      maximum-pool-size: 10 # 동시 연결 최대 수(서버 max_connections, 쿼리 지연, CPU/메모리와 함께 조정)
      minimum-idle: 1 # 유휴 커넥션 최소 유지 수(저부하일 때도 미리 워밍업)
      leak-detection-threshold: 60000 # 1분. 커넥션이 대여 중인데 반환되지 않으면 N ms 후 경고 로그(메모리/리소스 누수 탐지)
      connection-timeout: 3000 # 3초. 커넥션 획득 대기 시간(ms). 3초 내 못 얻으면 예외 발생 → 트래픽 급증 시 실패 빠르게 감지
      max-lifetime: 1800000   # 30분. 커넥션의 최대 수명(ms). DB의 wait_timeout보다 짧게 권장(서버가 먼저 끊지 않도록)
      idle-timeout: 300000    # 5분. 유휴 커넥션을 풀에서 제거하기 전 대기(ms)

  jpa:
    defer-datasource-initialization: true # Data.sql/schema.sql 초기화 순서를 DataSource 이후로 지연(테스트/개발에서 편리)
    hibernate:
      ddl-auto: create
    properties:
      hibernate:
        format_sql: true # 로그에서 SQL 가독성 향상(운영에서는 로그량 주의)
        highlight_sql: true # ANSI 색상으로 SQL 하이라이트(로컬/개발 콘솔 가독성↑)
        show_sql: false # 콘솔에 SQL 직접 출력 여부(JPA show_sql). 보통 로거(TRACE/DEBUG)로 대체해 false 유지 권장
        jdbc.time_zone: Asia/Seoul # JDBC 레벨에서 타임존 지정. 서버/드라이버/애플리케이션의 TZ 일관성 확보
        generate_statistics: true # Hibernate 통계 활성화(캐시/쿼리 히트 등). 성능 분석 시 유용, 운영 상시 활성화는 오버헤드 가능
        session.events.log.LOG_QUERIES_SLOWER_THAN_MS: 300 # N ms보다 느린 쿼리를 이벤트 로그로 기록(슬로우 쿼리 관찰에 유용)
    open-in-view: false     # OSIV 비활성화: 웹 계층에서 영속성 컨텍스트 사용 지양(서비스/도메인 계층에서 트랜잭션 명확화)

 

 

url

  • 설명: 데이터베이스 연결을 위한 JDBC URL 문자열입니다. 접속할 DB의 주소, 데이터베이스 이름, 그리고 다양한 연결 옵션을 포함합니다.
  • 기본값: 없음 (필수 항목).
  • docker-compose.yml과의 중복:
    • serverTimezone=Asia/Seoul: 이 옵션은 docker-compose.yml의 설정과 중복됩니다. Docker Compose의 TZ=Asia/Seoulcommand--default-time-zone=+09:00 설정은 서버 자체의 기본 타임존을 Asia/Seoul로 이미 지정했습니다. 따라서 서버에 접속하는 모든 클라이언트(세션)는 기본적으로 이 설정을 따릅니다.
    • 권장 사항: 서버 레벨(docker-compose.yml)에서 설정하는 것이 가장 좋은 방법입니다. URL의 이 옵션은 제거해도 동일하게 동작하며, 설정을 한곳으로 집중시켜 혼란을 줄일 수 있습니다.
  • 기타 연결 옵션 (성능 최적화):
    • rewriteBatchedStatements=true: 여러 개의 INSERT 또는 UPDATE 쿼리를 하나의 쿼리로 재작성하여 서버에 보내는 배치(batch) 작업 성능을 극적으로 향상시킵니다.
    • cachePrepStmts=true ~ useServerPrepStmts=true: 자주 사용되는 SQL 쿼리(PreparedStatement)를 캐싱하는 설정입니다. 매번 쿼리를 분석하고 컴파일하는 과정을 생략하여 반복적인 쿼리의 실행 속도를 높여줍니다. 일반적인 웹 애플리케이션 환경에서 권장되는 성능 최적화 옵션입니다.

 

driver-class-name, username, password

  • 설명: 사용할 JDBC 드라이버 클래스와 DB 접속 계정 정보를 지정합니다. ${...} 문법을 사용하여 환경 변수나 .env 파일로부터 값을 주입받는 방식은 보안을 위한 좋은 습관입니다.

hikari (HikariCP 커넥션 풀 설정)

요청이 있을 때마다 DB 연결을 새로 만드는 것은 비용이 매우 큽니다.
HikariCP는 미리 정해진 개수의 연결을 만들어두고(Connection Pool) 필요할 때마다 빌려주는 고성능 라이브러리입니다.

  • pool-name: JMX 등 모니터링 시 커넥션 풀을 식별하기 위한 이름입니다.
  • maximum-pool-size: 10:
    • 설명: 커넥션 풀이 가질 수 있는 최대 연결 개수입니다.
    • 기본값: 10.
    • Trade-off: 무조건 크다고 좋은 것이 아닙니다. DB가 감당할 수 있는 수준을 넘어서면 오히려 성능이 저하됩니다. CPU 코어 수 등을 고려한 복잡한 계산식이 있지만, 대부분의 웹 애플리케이션에서는 작은 크기의 풀(10~20개)로도 충분한 성능을 냅니다.
  • minimum-idle: 1:
    • 설명: 트래픽이 없어도 풀이 최소한으로 유지하는 유휴(idle) 연결 개수입니다.
    • 기본값: maximum-pool-size와 동일.
    • Trade-off: 기본값은 항상 최대 성능을 낼 수 있도록 준비하지만 DB 리소스를 계속 점유합니다. 1로 낮추면 유휴 상태일 때 DB 리소스를 절약할 수 있지만, 갑작스러운 트래픽 증가 시 새 연결을 만드는 데 약간의 지연이 발생할 수 있습니다.
  • leak-detection-threshold: 60000: 60초(60000ms) 이상 반환되지 않은 연결이 있으면 로그에 경고를 남겨 '커넥션 누수(leak)'를 찾는 데 도움을 줍니다. 개발 환경에서 유용하며, 기본값은 0(비활성화)입니다.
  • connection-timeout, max-lifetime, idle-timeout: 커넥션 풀의 생명주기 관리 설정입니다. 각각 풀에서 연결을 얻기까지 기다리는 시간, 연결의 최대 수명, 유휴 연결이 풀에서 제거되기까지의 시간을 의미하며, 일반적인 권장 값들로 설정되어 있습니다.

spring.jpa (JPA 및 Hibernate 설정)

JPA 구현체인 Hibernate가 데이터베이스와 어떻게 상호작용할지 상세하게 제어합니다.

  • defer-datasource-initialization: true: schema.sql보다 data.sql을 나중에 실행하도록 순서를 조정합니다. Hibernate가 스키마를 모두 생성한 후에 데이터 초기화 스크립트가 실행되도록 보장합니다.
  • hibernate.ddl-auto: create:
    • 설명: 애플리케이션 시작 시점에 기존 테이블을 모두 삭제(drop)하고 엔티티 클래스에 맞춰 새로 생성(create)합니다.
    • Trade-off: 개발 및 테스트 환경에서 매우 편리하지만, 운영 환경에서는 절대 사용하면 안 됩니다. 모든 데이터가 사라지기 때문입니다. 운영 환경에서는 보통 validate(엔티티와 스키마 일치 여부만 검증) 또는 none(아무것도 안 함)을 사용하고, 스키마 변경은 Flyway나 Liquibase 같은 전문 툴로 관리합니다.
  • properties.hibernate:
    • format_sql: true, highlight_sql: true: JPA가 실행하는 SQL 쿼리를 보기 좋게 정렬하고 색을 입혀 로그 가독성을 높입니다.
    • show_sql: false: SQL 쿼리를 콘솔에 출력할지 여부입니다. true로 하면 디버깅에 유용하지만, 보통 로깅 프레임워크(Logback 등)를 통해 제어하는 것이 더 유연합니다.
    • jdbc.time_zone: Asia/Seoul: 이 또한 docker-compose.yml과 중복되는 설정입니다. Hibernate가 LocalDateTime 같은 시간 관련 타입을 처리할 때 기준으로 삼을 타임존을 지정합니다. 서버 타임존이 이미 서울로 설정되어 있으므로, 이 설정은 불필요합니다. 설정의 단일화를 위해 제거하는 것을 권장합니다.
    • generate_statistics: true: Hibernate의 세션 팩토리 통계(캐시 히트율, 쿼리 실행 시간 등)를 생성합니다. JMX 등을 통해 모니터링하여 성능 분석 및 튜닝에 매우 유용합니다. 약간의 오버헤드가 있어 운영 환경에서는 필요에 따라 활성화합니다.
    • session.events.log.LOG_QUERIES_SLOWER_THAN_MS: 300: 300ms보다 오래 걸리는 슬로우 쿼리(slow query)를 로그로 남겨 성능 병목 지점을 찾는 데 큰 도움을 줍니다.
  • open-in-view: false:
    • 설명: OSIV(Open Session In View) 전략을 비활성화합니다.
    • 기본값: true.
    • Trade-off: false로 설정하는 것은 성능과 안정성을 위한 강력한 모범 사례입니다. 기본값인 true는 DB 커넥션을 요청 처리의 전 과정(뷰 렌더링까지) 동안 계속 열어두어 커넥션 풀 고갈의 원인이 될 수 있고, 의도치 않은 쿼리를 유발할 수 있습니다. false로 두면 트랜잭션 범위 내에서만 영속성 컨텍스트가 열리도록 강제하여 더 예측 가능하고 안정적인 코드를 작성하게 합니다.

 

마지막으로 정리하자면,

Docker Compose 쪽 설정“컨테이너와 MySQL 서버( mysqld )의 전역 동작”을 바꿉니다.
(포트/볼륨/리소스, 전역 시스템 변수, 초기 계정·DB 생성, 스토리지엔진/버퍼풀 같은 서버 옵션)

 

application.yml(Spring Boot) 쪽 설정은 “애플리케이션이 DB에 어떻게 연결하고 쓰는지”를 바꿉니다.
(JDBC URL, 커넥션 풀, 트랜잭션, Hibernate/Flyway, 세션 변수 등)