문제 상황
이 시스템의 언어는 브라우저가 보내는 Accept-Language만으로 결정되지 않았다. 오랫동안 사용된 URL의 경로 구분과 법인 환경이 언어 계약이었고, 기존 업무 시스템과 메일 링크도 그 규칙을 사용했다.
내부 코드를 Spring Boot로 전환하면서 URL 규칙을 바꾸면 호출 시스템과 과거 링크까지 깨질 수 있었다. 따라서 Locale을 언제 결정하고, 레거시 화면 Forward와 Controller·Service·예외 처리·검증 메시지가 그 값을 어떻게 공유할지 함께 설계해야 했다.
공개에서 제거한 URL과 내부 이름
실제 언어 경로, 레거시 확장자별 내부 이동 경로, 필터·Resolver·Context 클래스명은 공개하지 않는다. 공개 글에는 다음 업무 규칙만 남긴다.
- 특정 해외법인 환경은 URL보다 법인 언어 정책을 우선한다.
- 본사 환경은 레거시 URL의 정확한 경로 세그먼트로 언어를 구분한다.
- 정의되지 않은 경로는 안전한 기본 언어를 사용한다.
- 레거시 화면 요청을 내부 Controller로 Forward하기 전에 Locale을 확정한다.
Spring 기본 방식이 그대로 맞지 않았던 이유
| 대안 | 맞지 않았던 이유 |
|---|---|
Accept-Language 사용 | 같은 브라우저에서도 URL과 법인에 따라 언어가 달라짐 |
| Session·Cookie 저장 | 세션 없이 직접 진입하는 링크의 언어를 보장하지 못함 |
| URL을 신규 규칙으로 변경 | 기존 시스템과 과거 메일 링크를 함께 수정해야 함 |
| Controller에서 매번 판정 | Service·예외 처리·Validation보다 늦고 중복됨 |
Forward된 URI만 보고 언어를 결정하면 원 요청 경로의 의미를 잃을 수 있었다. 따라서 Locale은 Controller보다 앞선 요청 초기 단계에서 확정해야 했다.
선택한 구조: Forward보다 먼저 Locale을 고정한다
아래 코드는 문제 해결 구조를 설명하기 위한 pseudocode이며 실제 클래스·테이블·설정 이름과는 무관합니다.
1. 요청 식별자 설정
2. 요청값 보안 처리
3. 원 URL과 법인 정책으로 Locale 확정
4. 레거시 화면 요청을 내부 Controller로 Forward
5. 이미 확정된 Locale로 업무 처리Locale 계층의 책임은 번역문을 직접 가져오는 것이 아니다. 변경하기 어려운 URL·법인 규칙을 Spring이 이해할 수 있는 요청 Locale로 변환하는 것이다.
아래 코드는 문제 해결 구조를 설명하기 위한 pseudocode이며 실제 클래스·테이블·설정 이름과는 무관합니다.
Locale selected = tenantPolicy.forceEnglish()
? Locale.ENGLISH
: localePathPolicy.resolve(request.getRequestURI());
requestLocaleStore.set(selected);
LocaleContextHolder.setLocale(selected);
filterChain.doFilter(request, response);DB 메시지와 Bean Validation을 같은 Locale에 연결했다
기존 업무 시스템의 DB 기반 메시지 체계를 유지하고, Controller·Service·공통 예외 처리와 Bean Validation이 같은 요청 Locale을 사용하게 했다. 실제 Bean 이름, 메시지 코드와 캐시 설정 키는 공개하지 않는다.
아래 코드는 문제 해결 구조를 설명하기 위한 pseudocode이며 실제 클래스·테이블·설정 이름과는 무관합니다.
원 요청 URL·법인 정책
→ 요청 Locale 확정
→ Spring Locale Context
→ DB MessageSource
→ Validation / Controller / Service / 오류 응답운영에서는 메시지를 미리 적재하는 방식, 개발 환경에서는 요청된 메시지를 필요할 때 읽는 방식으로 구성했다. 공개 글에서는 실제 모드 이름과 설정 키를 제거하고 “환경별 캐시 정책”으로만 설명한다.
검증 범위
Locale 처리만을 위한 별도 테스트 건수는 산출물에서 확인되지 않았다. 다음 조건은 다운로드 시스템 전체 기능테스트 범위의 점검 항목으로 확인했다.
- 기존 한국어·영어 계열 URL이 변경 없이 동작하는지
- 레거시 화면 Forward 뒤에도 원 요청 언어가 유지되는지
- 해외법인 정책이 URL보다 우선하는지
- Controller 성공 메시지와 Service·예외 메시지의 언어가 일치하는지
- Bean Validation이 같은 DB 메시지 체계와 Locale을 사용하는지
- 메시지 캐시 갱신 경로가 동작하는지
기능테스트 20개 시나리오·557개 테스트 케이스는 Locale만의 성과가 아니라 URL·파일·DB 접근·예외 응답을 포함한 DNA2 기능테스트 범위다. 최초 548건 성공·9건 실패 후 결함을 보완해 최종 557건 전체 통과를 확인했다. 본사·유럽법인 통합테스트는 별도로 수행했으며 557개 수치와 연결하지 않는다.
현재 소스 기준 추가 개선 후보
현재 소스에서는 커스텀 요청 상태를 종료 시 정리하는 로직은 확인되지만, Spring의 표준 Locale Context를 명시적으로 초기화하는 호출은 직접 확인되지 않는다.
이는 구현 완료 내용이 아니라 코드 리뷰 관점의 추가 개선 후보다. 요청 종료 시 표준 Locale Context 정리 책임까지 필터 경계에 명시할 필요가 있는지 검토할 수 있다. 현재 동작이 요청 처리기의 복원 동작에 의존하는지, 비동기 처리와 오류 경로에서도 격리가 유지되는지 확인한 뒤 적용 여부를 결정해야 한다.
아래 코드는 문제 해결 구조를 설명하기 위한 pseudocode이며 실제 클래스·테이블·설정 이름과는 무관합니다.
try {
filterChain.doFilter(request, response);
} finally {
requestLocaleStore.clear();
// 표준 Locale Context 정리는 추가 검토 후보
}정리
레거시 현대화에서 URL은 단순 라우팅 문자열이 아니라 업무 계약일 수 있다. 핵심은 특정 필터명이 아니라 원 요청에서 판정 → Spring Context에 전달 → 모든 계층이 재사용 → 요청 종료 시 정리 책임 검토라는 생명주기를 한 경계에서 다룬 것이다.