레거시 웹 시스템에 다국어를 입히기
하나의 locale에 묶여 있던 3개의 축을 분리하다
레이아웃과 시스템 골조를 거의 건드리지 않고, 레거시 물류 웹 시스템에 다국어(i18n)를 적용한 과정을 정리한 글입니다. AI 개발 도구(Kiro)를 어떻게 활용해 시스템을 분석하고 신규 로직을 안전하게 얹었는지에 초점을 맞췄습니다.
목차
- 배경: 다국어인데, 레이아웃은 건드리면 안 된다
- 문제 정의: 하나의
locale값에 3가지 책임이 묶여 있었다 - 시스템 분석: 얽혀 있던 3개의 축을 분리하다
lang_flag의 역할과Z플래그의 발명- 설계 원칙: 골조 불변 + 기존 메커니즘 재사용
- Kiro를 어떻게 썼나 (이 글의 핵심)
- 시행착오와 반전: “전면 영어”에서 “국내 레이아웃 + 영문 텍스트”로
- 회귀 방지: 바꾸지 않은 것을 증명하기
- 마치며: 레거시에 신규 로직을 얹는 태도
1. 배경: 다국어인데, 레이아웃은 건드리면 안 된다
오래 운영된 엔터프라이즈 웹 시스템에 다국어 요구가 들어오면, 보통 두 가지 상반된 압력을 동시에 받습니다.
- 요구: 화면 텍스트를 여러 언어(한국어/영어/중국어/일본어)로 보여줘야 한다.
- 제약: 이미 수백 개의 화면과 업무 로직이 안정적으로 돌고 있어서, 레이아웃과 시스템 골조를 크게 바꾸는 순간 회귀 위험이 폭발한다.
즉 “텍스트만 언어를 바꾸고 싶다”는 단순한 요구인데, 실제 시스템에서는 언어를 바꾸는 스위치가 레이아웃, 업무 기능, 메뉴 구성까지 통째로 함께 바꿔 버리는 구조였습니다. 이 글은 그 얽힘을 어떻게 분석해서 풀어냈는지에 대한 기록입니다.
2. 문제 정의: 하나의 locale 값에 3가지 책임이 묶여 있었다
분석을 시작하면서 가장 먼저 드러난 사실은, 시스템의 “언어/지역” 개념이 사실상 하나의 로케일 값으로 표현되고 있었고, 그 값 하나가 서로 다른 세 가지 책임을 동시에 지고 있었다는 점입니다.
사용자가 언어를 바꾸면 다음이 한꺼번에 바뀌었습니다.
- 화면에 렌더링되는 텍스트 언어
- 어떤 업무 화면/컬럼/기능을 보여줄지 결정하는 레이아웃 프로파일
- 좌측 메뉴의 구성과 메뉴명
그 결과, “메뉴 구조와 업무 기능은 국내(한국) 기준 그대로 두고, 화면 텍스트만 영어로 보고 싶다”는 지극히 현실적인 요구를 기존 구조로는 표현할 수 없었습니다. 언어를 영어로 바꾸는 순간 해외(EN) 프로파일로 전환되면서 일부 국내 전용 기능과 메뉴가 사라졌기 때문입니다.
3. 시스템 분석: 얽혀 있던 3개의 축을 분리하다
핵심 통찰은 하나였습니다. “하나의 값이 세 가지 일을 하고 있으니, 세 개의 독립된 축으로 쪼개자.” 코드를 추적해 보니, 실제로는 이미 서로 다른 변수가 존재했는데도 관행적으로 함께 움직이도록 세팅되고 있었습니다. 이를 다음과 같이 정리했습니다.
| 축 | 무엇을 결정하나 | 기준 변수 (일반화) |
|---|---|---|
| ① 표시 언어 | 화면 텍스트/라벨/메시지의 렌더 언어 | 세션 Locale + LOCALE (ko/en/zh/ja) |
| ② 레이아웃·업무 프로파일 | 콘텐츠 화면의 기능 분기 + 셸 레이아웃 | LOCALE_CD (KOR/CHN 등) |
| ③ 메뉴 조회 기준 | 메뉴 구성(노출/순서/그룹) + 메뉴명 | lang_flag (K/E/C/J/Z) |
분리의 요점은 이렇습니다.
- 텍스트는 ①, 레이아웃은 ②, 메뉴는 ③ 이 각각 독립적으로 결정한다.
LOCALE(표시 언어)과LOCALE_CD(레이아웃 프로파일)는 이름은 비슷하지만 서로 다른 축이다. 이 둘을 혼동하는 것이 회귀의 가장 흔한 원인이었다.
이 분리 덕분에 비로소 다음과 같은 “조합”이 표현 가능해졌습니다.
- 메뉴: 한국어 구성 + 영문 메뉴명
- 레이아웃: 국내(KOR) 풀기능
- 텍스트: 영어
기존 구조에서는 상상할 수 없던 조합인데, 축을 나누고 나니 각 축에 서로 다른 값을 꽂는 것만으로 자연스럽게 만들어졌습니다. “레이아웃을 건드리지 않는다” 는 요구는, 곧 축 ② (LOCALE_CD)를 국내 값으로 고정한 채 ①과 ③만 움직인다는 뜻으로 번역되었습니다.
4. lang_flag의 역할과 Z 플래그의 발명
세 축 중에서 가장 흥미로운 것이 세 번째, lang_flag입니다. 이 값은 “화면 텍스트를 무슨 언어로 그릴까”가 아니라, “메뉴를 어떤 기준으로 조회할까” 를 결정하는 축입니다.
원래 lang_flag는 언어별로 K(한국어) / E(영어) / C(중국어) / J(일본어) 값을 가졌고, 이 값에 따라 메뉴 조회 프로시저가 서로 다른 메뉴 구성과 메뉴명을 반환했습니다. 문제는 E를 주면 “영문 메뉴 구성”이 통째로 딸려오면서 국내 전용 메뉴가 빠진다는 것.
그래서 새 값 하나를 발명했습니다. 바로 Z 플래그입니다.
Z의 의미: 한국어 메뉴 구성을 그대로 쓰되, 메뉴명만 영문으로 조인한다.- 대응하는 영문 메뉴명이 없는 국내 전용 메뉴는 한글명으로 자연스럽게 폴백한다.
Z는 “국내 구성 + 영문 표기”라는 제3의 상태를 메뉴 축에 새로 추가한 것입니다. 기존 K/E/C/J 사용자에게는 아무 영향이 없고, 오직 지정된 사용자군(예: 국내 시스템을 쓰는 해외 대리점 사용자)만 Z로 조회되도록 서버 세션 단계에서 판별했습니다.
여기서 중요한 설계 결정이 있었습니다.
- 판별은 서버 세션 단계에서 수행하고, 결과를 세션 플래그(예:
is_z_user)로 관리한다. - 사용자 식별 기준은 세션에 이미 존재하는 값들의 조합(고객 번호 + 국가 코드 + 지역 플래그 등)으로 정의한다.
- 식별에 필요한 값이 세션에 저장되어 있지 않다면, 로그인 흐름에 세션 저장 로직을 최소한으로 추가한다.
즉 Z는 “메뉴 조회 SQL의 파라미터 하나”에서 출발한 아주 국소적인 변경이었지만, 축을 분리해 둔 덕분에 레이아웃·텍스트 축을 전혀 건드리지 않고도 원하는 조합을 완성할 수 있었습니다.
5. 설계 원칙: 골조 불변 + 기존 메커니즘 재사용
신규 로직을 얹을 때 우리가 스스로에게 건 제약은 명확했습니다.
원칙 1. 신규 메커니즘을 도입하지 않는다. 프레임워크(WebSquare 계열)가 이미 쓰고 있던 다국어 메커니즘을 그대로 재사용했습니다. 화면의 고정 문자열을 리소스 키로 치환하고, 각 키의 언어별 값을 메시지 번들에 등록하는 방식입니다. 리소스 키는 다음과 같은 형태입니다.
1
<w2:textbox label="!~hp2905.blNo~!" ... />
여기서 !~ ... ~! 안의 문자열이 리소스 키이고, 실행 시 현재 표시 언어에 맞는 값으로 치환됩니다. 언어별 값은 로케일별 properties 번들에 등록합니다.
1
src/main/resources/static/lang/{ko,en,zh,ja}/{언어}.properties
원칙 2. 표시 텍스트만 바꾼다. 위젯 id, 데이터 바인딩, submission, 이벤트 핸들러, 분기 조건 등 동작과 구조는 절대 바꾸지 않았습니다. 바뀌는 것은 오직 “사람 눈에 보이는 문자열” 뿐입니다.
원칙 3. 진입점만 최소로 손댄다. 개별 업무 화면(수백 개에 달하는 콘텐츠 화면)은 원칙적으로 건드리지 않고, 축을 세팅하는 소수의 진입점만 수정했습니다.
- 로그인 초기화 지점: 사용자군 판별 후 각 축의 값 세팅
- 세션 전처리(interceptor) 지점: 렌더 로케일 강제/해제
- 셸 레이아웃 화면: 국내/해외 레이아웃 분기에 사용자군 가드 추가
- 콘텐츠 로더: 표시 언어 축과 레이아웃 축을 각각 별도 속성으로 전달
- 메뉴 조회 흐름:
lang_flag값 전달
원칙 4. 코드성/약어는 건드리지 않는다. “VGM”, “CBM”, “B/L”, “PORT”처럼 어느 언어에서나 동일하게 표기되는 약어·코드성 텍스트는 리소스 키로 만들지 않고 원문 그대로 두었습니다. 4개 언어 값이 어차피 같아지기 때문에 키를 만드는 것 자체가 불필요한 노이즈입니다.
6. Kiro를 어떻게 썼나 (이 글의 핵심)
여기까지가 “무엇을 했는가”라면, 지금부터는 “어떻게 안전하게 해냈는가”입니다. 이 작업의 난이도는 코드 자체보다 “방대한 레거시에서 건드릴 곳과 건드리면 안 될 곳을 정확히 가려내는 것” 에 있었습니다. AI 개발 도구인 Kiro를 이 지점에서 집중적으로 활용했습니다.
6.1 시스템 분석: 어느 축의 문제인지부터 규명
가장 먼저 한 일은 “지금 겪는 증상이 세 축 중 어디에 속하는가”를 규명하는 것이었습니다. Kiro에게 코드베이스를 탐색시켜 다음을 지도로 만들었습니다.
- 서버 렌더 텍스트는 어디서 로케일을 읽는가 (세션 Locale)
- 하위 페이지 텍스트는 어떤 요청 파라미터로 언어가 정해지는가 (
LOCALE) - 콘텐츠 화면의 기능 분기는 어떤 변수를 보는가 (
LOCALE_CD) - 메뉴 조회는 어떤 플래그로 실행되는가 (
lang_flag)
이 “구동 변수 맵”이 없으면 수정은 곧 도박이 됩니다. 예를 들어 텍스트만 영어로 바꾸려고 LOCALE을 건드려야 하는데 실수로 LOCALE_CD를 바꾸면, 텍스트는 그대로인데 레이아웃이 해외 프로파일로 튀어 버립니다. 이런 혼동 지점을 사전에 문서화해 두는 것이 회귀 방지의 출발점이었습니다.
6.2 Spec 기반 진행: 요구사항 → 설계 → 작업
Kiro의 Spec 기능을 써서 각 화면/기능 단위를 요구사항 → 설계 → 구현 작업의 3단계로 쪼개 진행했습니다. 화면 하나를 다국어로 전환하는 작업조차도, 바로 코드부터 고치지 않고 다음을 먼저 확정했습니다.
- 요구사항: 이 화면에서 전환 대상(정적 UI 텍스트)과 제외 대상(동적 데이터, 코드성 약어)을 명확히 구분
- 설계: 신규 리소스 키의 이름과 4개 언어 값 표(카탈로그)를 먼저 설계
- 작업: 키 치환 + 번들 등록을 작은 단위로 나눠 순차 실행
이 방식의 장점은 “무엇을 바꾸고 무엇을 바꾸지 않을지”가 코드를 만지기 전에 문서로 합의된다는 점입니다. 덕분에 리뷰어도 diff가 아니라 의도를 먼저 볼 수 있었습니다.
6.3 Steering 가이드로 방법론을 축적
화면을 하나씩 전환하다 보면 같은 판단이 반복됩니다. “이건 키로 바꿔야 하나, 코드성 텍스트라 두어야 하나?”, “이 리소스 키 형태가 맞나?”, “이 세팅 경로에서 치환이 되나?”
이런 반복 판단을 매번 새로 하지 않도록, Kiro의 Steering(가이드) 문서에 방법론을 축적했습니다. 화면별로 흩어져 있던 규칙을 두 개의 통합 가이드로 모았습니다.
- 다국어 전환 가이드: 치환 관례, 키 명명 규약, 전환 제외 규칙, 검증 전략
- 로케일 축 분리 가이드: 3개 축의 정의, 구동 변수 맵, 사용자군 식별, 회귀 방지 원칙
새 화면 작업을 시작할 때 해당 가이드를 참조로 불러오면, 그동안의 결정과 함정이 곧바로 컨텍스트에 들어옵니다. 이는 팀 지식을 “사람의 기억”이 아니라 “도구가 읽는 문서”로 남기는 방식이었고, 담당자가 바뀌어도 같은 기준이 유지되었습니다.
6.4 “바꾸지 않았음”을 명세로 못 박기: 정확성 속성
레거시 작업에서 진짜 어려운 것은 “무엇을 바꿨는가”가 아니라 “무엇을 바꾸지 않았는가를 증명하는 것” 입니다. 이를 위해 검증 가능한 정확성 속성(correctness property) 세트를 정의하고, 모든 화면 작업에 공통 적용했습니다. 대표적인 항목입니다.
- 완전성: 화면이 참조하는 모든 신규 키가 4개 언어 번들에 값으로 존재한다.
- 재사용 무결성: 다른 화면이 함께 쓰는 재사용 키의 값이 바이트 단위로 그대로다.
- 구조 불변성: 위젯/컬럼 id, 데이터 바인딩, 분기 조건, 핸들러 시그니처, submission 이름이 원본과 동일하다.
- 축 분리: 로케일 분기로 노출되는 텍스트가 레이아웃 축이 아니라 표시 언어 축 기준 으로 렌더링된다.
- 하드코딩 소거: 대상 화면에 사용자 노출 하드코딩 문자열이 남지 않는다(제외 항목 예외).
이 속성들은 “잘 됐겠지”라는 느낌을 “이 조건이 참인가”라는 검사로 바꿔 주었습니다.
6.5 검증 자동화와 Hook으로 실수를 원천 차단
정확성 속성은 사람 눈으로만 확인하면 결국 놓칩니다. 그래서 두 겹의 자동화를 걸었습니다.
검증 스크립트: 화면/스크립트에서 참조된 리소스 키를 추출해 (1) 4개 언어 번들에 모두 등록되었는지, (2) 재사용 키 값이 변하지 않았는지, (3) 코드성 텍스트가 실수로 키로 치환되지는 않았는지를 자동으로 대조했습니다. 스크립트는 멱등하게 작성해 언제든 다시 돌려 검증에 재사용했습니다.
Hook으로 안티패턴 차단: 이 환경에는 특정 다국어 API(예: 런타임에 메시지를 코드로 가져오는 방식)가 실제로는 치환되지 않는 함정이 있었습니다. 이런 금지된 호출이 새로 들어오면 파일 저장 시점에 자동으로 검출되도록 Kiro Hook을 걸어 두었습니다. 사람이 리뷰에서 잡는 대신, 저장하는 순간 도구가 먼저 막아 주는 구조입니다.
정리하면, Kiro는 이 작업에서 (a) 시스템을 분석해 축을 규명하는 도구, (b) 결정을 문서로 합의시키는 도구, (c) 팀 방법론을 축적하는 도구, (d) “안 바꿈”을 자동 검증하는 도구로 동시에 쓰였습니다.
7. 시행착오와 반전: “전면 영어”에서 “국내 레이아웃 + 영문 텍스트”로
솔직하게 말하면, 이 작업은 한 번에 정답에 도달하지 못했습니다. 정의가 두 번 바뀌었고, 그중 한 번은 이전 결정을 정면으로 뒤집는 반전이었습니다. 이 과정 자체가 축 분리가 왜 중요한지를 잘 보여줍니다.
1단계 — 도입기. 표시 언어와 메뉴 조회 기준을 분리하고 Z 플래그를 도입했습니다. “한국어 메뉴 구성 + 영문 메뉴명”이라는 조합이 처음으로 가능해졌습니다.
2단계 — 전면 영어기. 대상 사용자가 어떤 언어로 로그인하든 화면 환경 전체를 영어로 통일하기로 했습니다. 서버에서 렌더 로케일을 영어로 강제하고, 셸 레이아웃도 해외(영문) 레이아웃을 타도록 가드를 넣었습니다. 언뜻 깔끔해 보였습니다.
3단계 — 반전. 그런데 “전면 영어”는 텍스트뿐 아니라 레이아웃까지 해외 프로파일로 바꾸는 결정이었습니다. 결국 대상 사용자는 국내 업무 기능이 필요한데도 해외 레이아웃에 갇히는 문제가 다시 불거졌습니다. 그래서 2단계를 의도적으로 뒤집어, 최종 정의를 “국내(KOR) 레이아웃 + 영문(EN) 텍스트” 로 재정의했습니다.
최종 상태는 세 축에 각각 다른 값을 꽂은 것으로 요약됩니다.
- 텍스트(①): 세션 Locale = 영어,
LOCALE = en→ 서버/화면 텍스트는 영어 - 레이아웃(②):
LOCALE_CD = KOR→ 국내 풀기능 유지 - 메뉴(③):
lang_flag = Z→ 한국어 구성 + 영문 메뉴명 - 셸 레이아웃 분기는
(LOCALE == "ko" || isZUser)형태로 바꿔, 영어 텍스트를 쓰는 대상 사용자도 국내 셸(푸터/헬프데스크/국내 퀵메뉴)을 그대로 보게 했다.
교훈: 만약 축을 분리해 두지 않았다면 이 반전은 대공사였을 겁니다. 하지만 축이 나뉘어 있었기에, 반전은 “레이아웃 축 값을 EN에서 KOR로 되돌리고 셸 분기 조건 한 줄을 고치는” 국소 변경으로 끝났습니다. 잘 나눠 둔 경계가 방향 전환의 비용을 결정합니다.
8. 회귀 방지: 바꾸지 않은 것을 증명하기
신규 로직은 언제나 “기존 사용자에게 아무 일도 일어나지 않을 것”을 함께 보장해야 합니다. 우리가 지킨 회귀 방지 원칙은 다음과 같습니다.
- 비대상 사용자 불변: 기존
K/E/C/J사용자의 메뉴 매핑, 렌더링, 언어 선택 UI, 레이아웃 분기는 그대로 유지한다. 신규(Z) 분기는 비대상 사용자에서 절대 실행되지 않도록 가드한다. - 로그아웃/세션 만료 시 초기화: 세션 무효화로 사용자군 플래그와 강제 로케일이 초기화되어, 다음 사용자의 언어 설정이 오염되지 않게 한다.
- 강제 로직은 대상에만: 텍스트 로케일 강제가 비대상 사용자에게 새어나가지 않도록 진입점마다 가드를 건다.
- 두 로케일 축 혼동 금지:
LOCALE(텍스트)과LOCALE_CD(레이아웃)를 반드시 구분해 수정한다. 이 한 가지 규칙이 회귀의 대부분을 예방했다.
부수적으로, 영문 메뉴명이 기존 한글명보다 길어지면서 드롭다운 메뉴가 고정 높이를 넘어 잘리는 UI 버그도 있었습니다. 이 역시 “콘텐츠 높이가 박스 높이를 초과할 때만” 확장하도록 조건을 좁혀, 기존 언어들의 외형은 그대로 보존했습니다.
또 하나의 함정은 alert/confirm 메시지였습니다. 다국어 리소스 값에 줄바꿈을 주려고 <br /> 태그를 넣었는데, 실제 화면에서는 HTML로 렌더되지 않고 이스케이프된 문자열 (<br />)이 그대로 보였습니다. 메시지가 태그가 아니라 텍스트로 취급된 것이죠. 해결책으로, 이스케이프된 엔티티를 원래 태그로 되돌리는 유틸을 정의해 <br />가 포함된 모든 메시지에 태워 주었습니다.
1
2
3
4
5
6
7
com.convString2Html = function (str) {
return str.replace(/[\t\n\r]/g, '')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/&/g, '&')
.replace(/"/g, '"');
};
사소해 보이지만, 이런 표시 계층의 함정은 “텍스트만 바꾼다”는 원칙을 지킬 때 오히려 자주 튀어나옵니다. 값은 맞는데 렌더 방식이 어긋나는 경우죠.
9. 마치며: 레거시에 신규 로직을 얹는 태도
이 작업에서 얻은 결론을 세 문장으로 압축하면 이렇습니다.
- 얽힌 책임은 축으로 분리한다. “언어”라는 하나의 개념 뒤에 텍스트·레이아웃·메뉴라는 세 가지 책임이 숨어 있었고, 이를 독립 축으로 나눈 순간 대부분의 문제가 풀렸습니다.
- 골조는 건드리지 않고 진입점만 만진다. 수백 개 화면을 고치는 대신, 축을 세팅하는 소수의 지점만 수정하고 나머지는 기존 메커니즘(리소스 키 + 번들)에 맡겼습니다.
- “안 바꿈”을 자동으로 증명한다. 정확성 속성 → 검증 스크립트 → 저장 시 Hook으로 이어지는 안전망이, 대담한 변경을 안심하고 시도할 수 있게 해 주었습니다.
Kiro는 이 과정에서 코드를 대신 타이핑해 주는 도구를 넘어, 시스템을 분석하고, 결정을 문서로 합의시키고, 방법론을 팀 자산으로 축적하고, 하지 말아야 할 일을 자동으로 막아 주는 협업 파트너로 기능했습니다. 레거시에 신규 로직을 얹는 일의 본질은 결국 경계를 잘 긋는 것 이고, 그 경계를 문서와 자동 검증으로 지켜낸 것이 이 작업의 핵심이었습니다.
부록: 바로 써먹는 Kiro 아티팩트
아래 세 가지는 이 작업에서 실제로 쓴 것을 외부 공개용으로 일반화한 것입니다. 프로젝트에 맞게 변수명/경로만 바꿔 그대로 재사용할 수 있습니다.
부록 A. 프롬프트 스크립트 3종
작업을 “분석 → 설계/구현 → 검증”의 3박자로 나누고, 각 단계마다 아래 프롬프트를 썼습니다. 핵심은 바로 코드를 고치라고 시키지 않고, 먼저 지도를 그리게 하는 것입니다.
A-1. 시스템 분석 (어느 축의 문제인지 규명)
1
2
3
4
5
6
7
8
9
이 시스템에서 "언어를 바꾸면 무엇이 함께 바뀌는지" 코드를 추적해서
아래 3개 축으로 분류한 표를 만들어줘. 아직 코드는 고치지 마.
- 표시 언어 축: 화면 텍스트가 어떤 변수/세션 값으로 렌더 언어를 정하는가
- 레이아웃 축: 화면 기능/컬럼/셸 레이아웃 분기가 어떤 변수를 보는가
- 메뉴 축: 메뉴 조회가 어떤 플래그로 실행되는가
각 축의 기준 변수, 그 변수를 세팅하는 지점(파일/함수),
그리고 서로 혼동하기 쉬운 지점을 함께 정리해줘.
A-2. 다국어 전환 Spec 시작 (구현 전 합의)
1
2
3
4
5
6
7
8
9
10
이 화면을 다국어로 전환하려고 해. 코드부터 고치지 말고 먼저 아래를 정리해줘.
1. 전환 대상: 사용자에게 보이는 정적 텍스트(라벨/버튼/컬럼 헤더/안내 문구/placeholder)
2. 전환 제외: 서버가 내려주는 동적 데이터, 그리고 모든 언어에서 동일 표기되는
코드성/약어(예: VGM, B/L, PORT) — 키를 만들지 말고 원문 유지
3. 재사용 가능한 기존 공통 키가 있으면 우선 매핑, 없을 때만 신규 키 제안
4. 신규 키의 이름(화면 접두사 + 점 표기)과 ko/en/zh/ja 4개 언어 값을 표로 설계
위젯 id, 데이터 바인딩, 이벤트 핸들러, 분기 조건은 절대 바꾸지 마.
바뀌는 건 사람 눈에 보이는 문자열뿐이어야 해.
A-3. 검증 (바꾸지 않았음을 대조)
1
2
3
4
5
6
7
8
방금 전환한 화면을 아래 기준으로 검증하는 멱등 스크립트를 만들고 실행해줘.
1. 완전성: 화면에서 참조하는 모든 신규 키가 ko/en/zh/ja 4개 번들에 값으로 존재하는가
2. 재사용 무결성: 다른 화면과 공유하는 재사용 키의 값이 변경 전과 동일한가(바이트 단위)
3. 제외 무결성: 코드성/약어 텍스트가 실수로 키로 치환되지 않고 원문으로 남아 있는가
그리고 위젯 id, 데이터 바인딩, 분기 조건, submission 이름이 원본과 동일한지
diff로 확인해서, 표시 문자열 외의 변경이 없다는 걸 보고해줘.
부록 B. Steering 가이드 스켈레톤
.kiro/steering/ 아래에 두고, 새 작업을 시작할 때 참조로 불러오는 방법론 문서의 골격입니다. inclusion: manual로 두면 평소엔 로드되지 않고, 필요할 때만 불러올 수 있습니다. 프로젝트의 실제 변수명으로 바꿔 쓰세요.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
---
inclusion: manual
---
# 로케일 축 분리 · 다국어 전환 가이드
## 1. 3개 축의 정의
| 축 | 무엇을 결정 | 기준 변수 |
|---|---|---|
| ① 표시 언어 | 텍스트 렌더 언어 | LOCALE (ko/en/zh/ja) |
| ② 레이아웃 | 기능 분기 + 셸 레이아웃 | LOCALE_CD (KOR/CHN...) |
| ③ 메뉴 | 메뉴 구성 + 메뉴명 | lang_flag (K/E/C/J/Z) |
## 2. 구동 변수 맵 (혼동 금지)
- LOCALE 과 LOCALE_CD 는 이름만 비슷한 서로 다른 축이다.
- 텍스트만 바꾸려면 ①만, 레이아웃은 ②만 건드린다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
## 3. 전환 대상 / 제외 규칙
- 대상: 정적 UI 텍스트(라벨/버튼/헤더/안내/placeholder)
- 제외: 서버 동적 데이터, 코드성/약어(4개 언어 값이 같아지는 텍스트)
- 기존 공통 키 우선 재사용, 없을 때만 신규 키 생성
## 4. 회귀 방지 원칙
- 비대상 사용자(K/E/C/J) 불변: 신규(Z) 분기는 비대상에서 실행 금지
- 로그아웃/세션 만료 시 사용자군 플래그·강제 로케일 초기화
- 구조 불변: 위젯 id / 바인딩 / 분기 조건 / submission 유지
## 5. 새 작업 체크리스트
- [ ] 어느 축 문제인지 규명했는가
- [ ] 기준 변수를 정확히 지목했는가 (LOCALE vs LOCALE_CD vs lang_flag)
- [ ] 진입점만 최소 수정, 개별 화면 불변인가
- [ ] 4개 언어 번들 완전성 + 재사용 키 불변을 검증했는가
부록 C. 안티패턴 차단 Hook
특정 API가 “쓸 수는 있는데 실제로는 치환되지 않는” 함정일 때, 사람이 리뷰에서 잡는 대신 저장 시점에 자동으로 잡도록 Hook을 겁니다. .kiro/hooks/ 아래에 JSON으로 둡니다.
1
2
3
4
5
6
7
8
9
10
11
12
{
"version": "v1",
"hooks": [{
"name": "Forbid non-substituting i18n API",
"trigger": "PostFileSave",
"matcher": "\\.(xml|js)$",
"action": {
"type": "command",
"command": "grep -n \"language.getMessage(\" \"$FILE\" && echo '금지된 다국어 API 사용: !~키~! 리소스 키 방식으로 바꾸세요' && exit 2 || exit 0"
}
}]
}
동작은 단순합니다. XML/JS 파일을 저장할 때 금지 API 호출이 들어 있으면 경고 메시지를 띄우고 non-zero로 종료해, 잘못된 패턴이 조용히 스며드는 것을 막습니다. 정규식 matcher와 검사 명령만 바꾸면 팀의 어떤 안티패턴에도 응용할 수 있습니다.
팁: 검사 대상이 늘어나면 명령을 인라인 grep 대신 별도 검증 스크립트로 빼고, Hook은 그 스크립트를 호출만 하도록 두는 편이 유지보수에 유리합니다.