application.yml에서 실무 서버가 실제로 손대는 키만 모았습니다. 서버·종료·프로필·DB 커넥션 풀·JPA·MyBatis·로깅·액추에이터·Tomcat 스레드·업로드·Jackson·캐시 순서이고, 각 표는 키·뜻·운영 권장값 세 칸입니다. 설정 우선순위와 프로필 분리, 리눅스 운영 묶음의conf/외부 설정과 연결되는 부분도 함께 둡니다.
server:
port: 8080
shutdown: graceful
spring:
profiles:
active: prod
lifecycle:
timeout-per-shutdown-phase: 20s
datasource:
url: jdbc:oracle:thin:@db-prod:1521/ORCL
username: app
password: ${DB_PASSWORD}
hikari:
maximum-pool-size: 20
connection-timeout: 3000
jackson:
time-zone: Asia/Seoul
logging:
level:
root: INFO
com.shop: DEBUG
file:
name: /opt/myapp/logs/app.log
management:
endpoints:
web:
exposure:
include: health,info,metrics높은 것이 낮은 것을 덮습니다. 같은 키가 여러 곳에 있을 때 어느 값이 이기는지가 운영 장애의 단골 원인입니다.
| 순위 | 출처 | 예 |
|---|---|---|
| 1 | 명령줄 인자 | --server.port=8081 |
| 2 | 환경 변수 | SERVER_PORT=8081 |
| 3 | 실행 폴더 config/ |
/opt/myapp/config/application.yml |
| 4 | 실행 폴더 | /opt/myapp/application.yml |
| 5 | jar 안 프로필 파일 | application-prod.yml |
| 6 | jar 안 기본 | application.yml |
환경 변수는 점을 밑줄로, 대문자로 씁니다. spring.datasource.url 은 SPRING_DATASOURCE_URL 입니다. 리눅스 운영 묶음 04 의 conf/env 에 이 형식으로 두면 jar 를 건드리지 않고 서버별 값을 줍니다.
java -jar app.jar --spring.config.additional-location=/opt/myapp/conf/ # 폴더 추가
java -Dspring.profiles.active=prod -jar app.jar
SPRING_PROFILES_ACTIVE=prod java -jar app.jar| 키 | 뜻 | 권장 |
|---|---|---|
spring.profiles.active |
활성 프로필 | 서버에서 지정, yml 에 고정 금지 |
spring.profiles.group.prod |
프로필 묶음 | prod-db,prod-log |
spring.config.import |
다른 파일 포함 | optional:file:./conf/secret.yml |
--- 구분 + spring.config.activate.on-profile |
한 파일 안 프로필 분리 | 작은 프로젝트 |
spring:
config:
activate:
on-profile: prod
server:
port: 8080
---
spring:
config:
activate:
on-profile: local
server:
port: 8081application-{프로필}.yml 파일 분리가 읽기 쉽습니다. active 를 yml 안에 적어 두면 개발 설정으로 운영이 뜨는 사고가 나므로 서버의 환경 변수나 유닛 파일에서만 정합니다.
| 키 | 뜻 | 권장 |
|---|---|---|
server.port |
포트 | 8080, nginx 뒤 |
server.address |
바인드 IP | 생략 또는 127.0.0.1 (nginx 뒤) |
server.shutdown |
완전 종료 | graceful |
spring.lifecycle.timeout-per-shutdown-phase |
종료 대기 | 20s |
server.servlet.context-path |
컨텍스트 경로 | /api 등 |
server.forward-headers-strategy |
프록시 헤더 반영 | native |
server.error.include-stacktrace |
에러 응답에 스택 | never |
server.error.include-message |
에러 메시지 | always 또는 never |
graceful 은 TERM 을 받으면 새 요청을 막고 처리 중인 것을 마친 뒤 종료합니다. 리눅스 운영 묶음 03 의 TimeoutStopSec 이 이 대기 시간보다 길어야 합니다. forward-headers-strategy 는 05 의 nginx 프록시 헤더를 읽게 합니다.
| 키 | 뜻 | 기본 | 권장 |
|---|---|---|---|
server.tomcat.threads.max |
최대 작업 스레드 | 200 | 200~400 |
server.tomcat.threads.min-spare |
대기 스레드 | 10 | 20 |
server.tomcat.max-connections |
최대 연결 | 8192 | 기본 |
server.tomcat.accept-count |
대기 큐 | 100 | 100~200 |
server.tomcat.connection-timeout |
연결 대기 | 60s | 20s |
server.tomcat.max-http-form-post-size |
폼 크기 | 2MB | 필요 시 |
server.tomcat.accesslog.enabled |
접근 로그 | false | true |
server.tomcat.accesslog.directory |
로그 폴더 | logs | /opt/myapp/logs |
스레드가 전부 바쁘면 요청이 큐에 쌓이고 큐가 차면 거부됩니다. 운영 묶음 09 에서 스레드 덤프의 RUNNABLE 수가 threads.max 와 같으면 풀 고갈이라고 본 이유입니다. DB 풀보다 스레드가 훨씬 많으면 DB 커넥션 대기가 생깁니다.
| 키 | 뜻 | 권장 |
|---|---|---|
spring.datasource.url |
JDBC URL | 서버별 외부 설정 |
spring.datasource.username password |
계정 | ${DB_PASSWORD} 환경 변수 |
spring.datasource.driver-class-name |
드라이버 | 대개 자동 |
spring.datasource.hikari.maximum-pool-size |
풀 최대 | 10~20 |
spring.datasource.hikari.minimum-idle |
유휴 최소 | 최대와 같게 |
spring.datasource.hikari.connection-timeout |
커넥션 대기 ms | 3000 |
spring.datasource.hikari.idle-timeout |
유휴 반납 ms | 600000 |
spring.datasource.hikari.max-lifetime |
커넥션 수명 ms | 1800000, DB 보다 짧게 |
spring.datasource.hikari.validation-timeout |
검증 대기 | 1000 |
spring.datasource.hikari.connection-test-query |
검증 쿼리 | Oracle SELECT 1 FROM DUAL |
spring.datasource.hikari.pool-name |
풀 이름 | 앱 이름, 로그 구분 |
spring.datasource.hikari.leak-detection-threshold |
누수 감지 ms | 개발 60000 |
풀 크기는 크다고 좋지 않습니다. DB 코어 수의 2배 정도가 출발점이고 앱 인스턴스 수를 곱한 값이 DB 의 세션 한도를 넘지 않아야 합니다. max-lifetime 은 방화벽·DB 의 유휴 끊김 시간보다 짧게 둬야 끊긴 커넥션을 쓰는 오류가 없습니다.
| 키 | 뜻 | 운영 권장 |
|---|---|---|
spring.jpa.hibernate.ddl-auto |
스키마 자동 처리 | none 또는 validate |
spring.jpa.show-sql |
SQL 출력 | false, 로거로 대신 |
spring.jpa.properties.hibernate.format_sql |
SQL 정렬 | 개발만 |
spring.jpa.open-in-view |
OSIV | false |
spring.jpa.properties.hibernate.default_batch_fetch_size |
N+1 완화 | 100 |
spring.jpa.properties.hibernate.jdbc.batch_size |
배치 insert | 50~100 |
spring.jpa.properties.hibernate.dialect |
방언 | 대개 자동 |
ddl-auto: update 로 운영을 띄우면 엔티티 변경이 테이블을 바꿉니다. create 면 데이터가 사라집니다. 운영은 none, 검증 서버는 validate 입니다. open-in-view: false 로 두면 커넥션을 요청 내내 잡지 않아 풀 고갈이 줄어듭니다.
| 키 | 뜻 | 권장 |
|---|---|---|
mybatis.mapper-locations |
매퍼 XML 위치 | classpath:mapper/**/*.xml |
mybatis.type-aliases-package |
별칭 패키지 | com.shop.dto |
mybatis.configuration.map-underscore-to-camel-case |
컬럼→카멜 | true |
mybatis.configuration.default-fetch-size |
조회 페치 | 100~500 |
mybatis.configuration.default-statement-timeout |
쿼리 제한 초 | 30 |
mybatis.configuration.jdbc-type-for-null |
null 타입 | Oracle NULL |
mybatis.configuration.call-setters-on-nulls |
null 도 세터 | true |
mybatis.config-location |
XML 설정 파일 | 복잡할 때 |
default-statement-timeout 이 없으면 느린 쿼리가 스레드를 무한히 잡습니다. Oracle 은 jdbc-type-for-null: NULL 이 없으면 null 바인딩에서 오류가 납니다. 매퍼 XML 의 SQL 로깅은 로깅 절의 패키지 레벨로 켭니다.
| 키 | 뜻 | 권장 |
|---|---|---|
logging.level.root |
기본 레벨 | INFO |
logging.level.com.shop |
앱 패키지 | INFO, 조사 시 DEBUG |
logging.level.org.hibernate.SQL |
SQL 로그 | 개발 DEBUG |
logging.level.com.shop.mapper |
MyBatis SQL | 개발 DEBUG |
logging.file.name |
파일 경로 | /opt/myapp/logs/app.log |
logging.logback.rollingpolicy.max-file-size |
파일 크기 | 100MB |
logging.logback.rollingpolicy.max-history |
보관 일수 | 30 |
logging.logback.rollingpolicy.total-size-cap |
총량 | 3GB |
logging.pattern.console |
콘솔 형식 | MDC 포함 |
logging.config |
logback 파일 지정 | file:/opt/myapp/conf/logback.xml |
파일 경로는 절대 경로로 두고 리눅스 운영 묶음 06 의 정리 정책과 맞춥니다. 롤링 키를 쓰면 logback 이 스스로 백업(압축·삭제)하므로 logrotate 대상에서 뺍니다. 레벨은 재시작 없이 액추에이터 loggers 엔드포인트로 바꿀 수 있습니다.
| 키 | 뜻 | 권장 |
|---|---|---|
management.endpoints.web.exposure.include |
노출 엔드포인트 | health,info,metrics,loggers |
management.endpoint.health.show-details |
상세 표시 | when-authorized |
management.endpoint.health.probes.enabled |
liveness·readiness | true |
management.server.port |
관리 포트 분리 | 9090, 내부만 |
management.endpoints.web.base-path |
경로 | /actuator |
management.health.db.enabled |
DB 헬스 포함 | true |
management.info.env.enabled |
info 에 env | true + info.app.version |
/actuator/health 가 리눅스 운영 묶음 04·07 의 헬스 체크 URL 입니다. 관리 포트를 분리하면 nginx 가 외부에 노출하지 않으면서 서버 안에서 curl localhost:9090/actuator/health 로 확인합니다. loggers 를 열면 curl -X POST .../loggers/com.shop -d '{"configuredLevel":"DEBUG"}' 로 레벨을 바꿉니다.
| 키 | 뜻 | 권장 |
|---|---|---|
spring.servlet.multipart.max-file-size |
파일 하나 | 50MB |
spring.servlet.multipart.max-request-size |
요청 전체 | 100MB |
spring.servlet.multipart.location |
임시 폴더 | /opt/myapp/tmp |
spring.jackson.time-zone |
JSON 시간대 | Asia/Seoul |
spring.jackson.date-format |
날짜 형식 | yyyy-MM-dd HH:mm:ss |
spring.jackson.serialization.write-dates-as-timestamps |
epoch 대신 문자열 | false |
spring.jackson.default-property-inclusion |
null 제외 | non_null |
server.servlet.encoding.charset |
응답 인코딩 | UTF-8 |
server.servlet.encoding.force |
강제 | true |
nginx 앞단이 있으면 client_max_body_size 도 같이 올려야 413 이 안 납니다. 시간대는 JVM -Duser.timezone 과 DB 시간대와 셋이 같아야 날짜가 하루씩 밀리지 않습니다.
| 키 | 뜻 | 권장 |
|---|---|---|
spring.cache.type |
캐시 구현 | caffeine, redis |
spring.cache.caffeine.spec |
크기·만료 | maximumSize=1000,expireAfterWrite=10m |
spring.task.scheduling.pool.size |
스케줄 스레드 | 작업 수만큼 |
spring.task.execution.pool.core-size |
@Async 스레드 |
8 |
spring.task.execution.pool.max-size |
최대 | 16 |
spring.task.execution.pool.queue-capacity |
큐 | 100 |
spring.task.execution.thread-name-prefix |
이름 | async- |
스케줄 풀 기본이 1 이라 @Scheduled 작업 하나가 길어지면 나머지가 밀립니다. 실무 확장 09 레슨의 겹침 문제가 이 키로 해결됩니다. 스레드 이름 접두어를 주면 운영 묶음 09 의 스레드 덤프에서 구분됩니다.
| 키·설정 | 뜻 | 권장 |
|---|---|---|
spring.mail.host port |
SMTP | 사내 릴레이 |
spring.data.redis.host port timeout |
Redis | timeout: 2s |
RestClient·RestTemplate 타임아웃 |
코드에서 설정 | 연결 3s, 읽기 10s |
-Dhttp.proxyHost 계열 |
폐쇄망 프록시 | JVM 옵션 |
spring.ssl.bundle |
인증서 묶음 | 사내 CA |
HTTP 클라이언트 타임아웃은 yml 키가 없어 빈 설정 코드에서 줍니다. 실무 확장 11 레슨의 재시도·서킷 브레이커와 리눅스 운영 묶음 07 의 프록시 설정이 여기와 이어집니다.
# application-prod.yml
server:
port: 8080
shutdown: graceful
forward-headers-strategy: native
tomcat:
threads:
max: 300
accesslog:
enabled: true
directory: /opt/myapp/logs
spring:
lifecycle:
timeout-per-shutdown-phase: 20s
datasource:
url: ${DB_URL}
username: ${DB_USER}
password: ${DB_PASSWORD}
hikari:
maximum-pool-size: 20
minimum-idle: 20
connection-timeout: 3000
max-lifetime: 1800000
jpa:
hibernate:
ddl-auto: none
open-in-view: false
jackson:
time-zone: Asia/Seoul
date-format: yyyy-MM-dd HH:mm:ss
mybatis:
configuration:
default-statement-timeout: 30
jdbc-type-for-null: 'NULL'
logging:
level:
root: INFO
com.shop: INFO
file:
name: /opt/myapp/logs/app.log
logback:
rollingpolicy:
max-file-size: 100MB
max-history: 30
total-size-cap: 3GB
management:
server:
port: 9090
endpoints:
web:
exposure:
include: health,info,metrics,loggers
endpoint:
health:
show-details: when-authorized비밀은 전부 ${...} 로 환경 변수에서 받습니다. 리눅스 운영 묶음 04 의 conf/env 에 DB_PASSWORD=... 를 두고 권한 600 으로 보호하는 것과 짝입니다.
curl -s localhost:9090/actuator/health # UP
curl -s localhost:9090/actuator/env/server.port # 적용된 값과 출처
curl -s localhost:9090/actuator/configprops | head -c 500 # 바인딩된 속성
curl -s localhost:9090/actuator/loggers/com.shop # 현재 레벨
curl -s -X POST -H 'Content-Type: application/json' -d '{"configuredLevel":"DEBUG"}' localhost:9090/actuator/loggers/com.shop
jcmd $(pgrep -f app.jar) VM.system_properties | grep -i 'spring\|timezone'env/키 가 "이 값이 어느 파일에서 왔나" 를 알려 줍니다. 설정이 안 먹을 때 우선순위 표와 이 출력을 대조하면 원인이 나옵니다. env·configprops 는 비밀이 보이므로 관리 포트를 내부에만 열고 운영에서는 include 에서 빼는 것이 안전합니다.
active 는 yml 이 아니라 서버 환경 변수·유닛 파일에서만 지정합니다.${DB_PASSWORD} 로 받고 파일은 600, jar 와 git 에는 넣지 않습니다.server.shutdown: graceful 과 timeout-per-shutdown-phase 를 TimeoutStopSec 보다 짧게 둡니다.max-lifetime 은 방화벽 유휴 시간보다 짧게 둡니다.ddl-auto: none, open-in-view: false, show-sql: false 입니다.default-statement-timeout 과 Oracle 의 jdbc-type-for-null 을 잊지 않습니다.health·info·metrics·loggers 만 노출합니다.-Duser.timezone, DB 세 곳을 같게 합니다.actuator/env/키 로 적용된 값과 출처를 봅니다.