Contents
see ListJava 서비스를 배포했는데 외부 API 주소가 비어 있거나 데이터베이스 비밀번호가 잘못 들어가 장애가 나는 문제는 코드보다 설정값 관리에서 시작되는 경우가 많습니다. 결론은 간단합니다. 실행 시점에 필요한 값을 즉시 검증하고, 환경별 값과 비밀값을 분리하며, 운영 로그에는 값 자체를 남기지 않아야 합니다. 애플리케이션이 요청을 받기 전에 잘못된 설정으로 중단되면 원인을 빠르게 확인할 수 있고, 빈 문자열이나 기본값 때문에 잘못된 외부 시스템으로 연결되는 상황도 막을 수 있습니다.
설정값의 출처와 우선순위를 먼저 정합니다
JVM은 -D이름=값 형태의 시스템 속성과 운영체제 환경변수를 모두 읽을 수 있습니다. 두 경로를 섞어 쓰더라도 우선순위는 한 가지로 고정해야 합니다. 예를 들어 개발자가 로컬에서만 시스템 속성을 주고, 운영 환경에서는 환경변수로 주입하는 규칙을 정할 수 있습니다. 설정 파일에 비밀번호를 넣어 저장소에 커밋하는 방식은 피하고, 비밀값은 배포 환경의 Secret 관리 기능 또는 접근 권한이 제한된 주입 경로로 전달합니다.
- 일반 설정:
APP_PORT,API_BASE_URL,REQUEST_TIMEOUT_MS - 비밀 설정:
DB_PASSWORD,PAYMENT_API_KEY,JWT_SIGNING_KEY - 환경 구분: 개발·검증·운영별 주소와 타임아웃은 분리하고, 키 이름은 동일하게 유지
- 금지 항목: 비밀번호, 토큰, 전체 연결 문자열을 로그나 예외 메시지에 출력
컨테이너 환경에서는 배포 정의의 environment 또는 Secret 참조를 사용하고, Java 실행 옵션으로 모든 비밀값을 넘기지 않는 편이 좋습니다. 운영체제에서 실행 명령이 보이는 환경이라면 명령행 인수는 다른 사용자에게 노출될 수 있기 때문입니다. 어떤 전달 방식을 쓰더라도 애플리케이션에는 최종적으로 이름과 값만 전달되므로, Java 코드에서는 한 곳에서 읽고 검증하는 구조가 유지됩니다.
시작 단계에서 빈 값과 형식을 동시에 검증합니다
환경변수가 존재해도 공백 문자열일 수 있고, 숫자 설정에는 문자가 들어갈 수 있습니다. 요청 처리 중에 NullPointerException이나 NumberFormatException이 발생하게 두지 말고, 시작 단계에서 설정 오류라는 메시지와 함께 종료합니다. 다음 예시는 시스템 속성을 먼저 읽고 없으면 환경변수를 읽는 간단한 설정 객체입니다.
import java.net.URI;
public record AppConfig(URI apiBaseUrl, int timeoutMs, String dbPassword) {
public static AppConfig load() {
String baseUrl = required("API_BASE_URL");
int timeoutMs = positiveInt("REQUEST_TIMEOUT_MS", 100, 60_000);
String dbPassword = required("DB_PASSWORD");
return new AppConfig(URI.create(baseUrl), timeoutMs, dbPassword);
}
private static String required(String key) {
String value = System.getProperty(key);
if (value == null || value.isBlank()) value = System.getenv(key);
if (value == null || value.isBlank()) {
throw new IllegalStateException("필수 설정 누락: " + key);
}
return value.trim();
}
private static int positiveInt(String key, int min, int max) {
int value = Integer.parseInt(required(key));
if (value < min || value > max) {
throw new IllegalStateException(key + " 범위 오류: " + min + "~" + max);
}
return value;
}
}
이 예시에서 타임아웃은 100밀리초에서 60,000밀리초 사이만 허용합니다. 이 범위는 서비스 요구에 맞게 조정해야 하지만, 0·음수·지나치게 큰 값을 그대로 허용하지 않는 원칙은 유지해야 합니다. URL은 URI.create에서 문법을 확인하므로 잘못된 값은 시작 시점에 드러납니다. 실제 서비스에서는 포트 번호, 연결 풀 크기, 허용된 도메인 목록도 같은 방식으로 타입과 범위를 검증합니다.
배포 전과 장애 시에는 값이 아니라 상태를 확인합니다
설정을 주입한 뒤에는 값 전체를 출력하지 않고 필수 항목이 준비됐는지만 로그로 남깁니다. 예를 들어 API_BASE_URL은 호스트명까지 필요하다면 별도 정책에 따라 마스킹하고, DB_PASSWORD와 API 키는 길이조차 기록하지 않는 것이 안전합니다. 애플리케이션 시작 로그에는 config loaded: apiBaseUrl=true, timeoutMs=true, dbPassword=true처럼 존재 여부만 남기면 운영자가 누락을 구분할 수 있습니다.
- 배포 전: 필수 키 목록, 허용 범위, 환경별 기본값 사용 여부를 체크리스트로 확인
- 시작 직후: 설정 검증 성공 여부와 애플리케이션 버전을 확인
- 연결 실패 시: 비밀값을 재출력하지 말고 DNS, 네트워크, 권한, 타임아웃 순서로 점검
- 변경 시: 키 이름을 바꾸기 전에 이전 키 지원 기간과 제거 날짜를 배포 문서에 기록
설정 변경은 코드 배포와 분리해 추적합니다
설정 키를 추가하거나 제한 범위를 바꿀 때는 애플리케이션 버전, 변경한 키 이름, 적용 환경, 적용 시각을 함께 기록합니다. 운영 환경의 비밀값 자체는 기록하지 않습니다. 새 키를 도입할 때는 먼저 검증 환경에 주입하고 애플리케이션이 정상 기동하는지 확인한 뒤 운영에 반영합니다. 이전 키를 즉시 삭제하면 롤백 과정에서 기동 실패가 생길 수 있으므로, 호환 기간 동안에는 새 키 우선·이전 키 보조 규칙을 명확히 두고 제거 일정을 관리해야 합니다.
Java 설정값 관리는 편의용 유틸리티가 아니라 서비스가 어떤 조건에서 실행되는지 보장하는 운영 경계입니다. 필수값 검증, 타입·범위 검사, 비밀값 분리, 상태 중심 로그를 함께 적용하면 배포 직후의 설정 오류를 요청 처리 단계까지 끌고 가지 않을 수 있습니다. 소프트모아의 서비스와 포트폴리오는 softmoa.com에서 확인할 수 있습니다.
소프트모아는 해당 시스템을 구축합니다. 문의하기
java
| No | 작성일 | Title |
|---|---|---|
| 3482 | 2026. 09. 16. | Java 환경변수 설정 오류를 줄이는 방법: 필수값 검증과 비밀값 분리 |
| 3451 | 2026. 09. 08. | Java 서버 메모리 사용량이 계속 늘어날 때: JFR과 힙 덤프로 원인 찾는 실전 가이드 |
| 3420 | 2026. 08. 30. | Java 가상 스레드 도입 전 확인할 것: 동시성 제한과 외부 API 백프레셔 설계 |
| 3388 | 2026. 08. 22. | Java CompletableFuture로 외부 API 병렬 호출을 안전하게 구성하는 방법 |
| 3356 | 2026. 08. 14. | Java record와 sealed interface로 안전한 API 응답 모델 만들기 |
| 3324 | 2026. 08. 05. | Java 가상 스레드 실전 적용 가이드: I/O 중심 API의 동시성 구조 바꾸기 |
| 3292 | 2026. 07. 28. | Java 메모리 사용량이 계속 늘어날 때: Heap Dump와 JFR로 누수 원인 찾기 |
| 3261 | 2026. 07. 20. | Java record로 API DTO를 단순하게 만드는 방법: 검증·변환·직렬화 실전 적용 |
| 3235 | 2026. 07. 12. | Java API 동시 호출 폭주 대응: Thread Pool, timeout, 장애 격리로 안정성 올리기 |
| 3178 | 2026. 07. 03. | Java 메모리 누수 진단 가이드: jcmd와 JFR로 원인 좁히기 |