diff --git a/.claude/rules/checklist.md b/.claude/rules/checklist.md index 21c2ea3..1d73b0d 100644 --- a/.claude/rules/checklist.md +++ b/.claude/rules/checklist.md @@ -40,8 +40,10 @@ - [ ] `backend/src/common/errors/error-codes.ts` 수정 (코드는 `DOMAIN_NNN`, 번호 재사용 금지) - [ ] `cd backend && npm run sync:error-codes` 실행 → 클라이언트 사전 자동 생성 (`frontend/src/shared/constants/error-codes.generated.ts`, `flutter/lib/core/api/error_lexicon.g.dart` — **직접 수정 금지**) -- [ ] 세 스택의 코드 목록 테스트에 새 코드 반영 후 통과 확인 - (`error-codes.spec.ts` · `useApi.spec.ts` · `error_lexicon_test.dart`) +- [ ] 세 스택 테스트 통과 확인 (`error-codes.spec.ts` · `useApi.spec.ts` · `error_lexicon_test.dart`) + — 계약 테스트는 **클라이언트가 실제로 참조하는 코드의 존재**와 네이밍 규약만 검사한다. + 도메인 코드를 추가했다고 테스트를 고칠 필요는 없다. + 단, 클라이언트가 그 코드로 분기한다면 해당 목록에 추가한다. - [ ] 생성 파일까지 함께 커밋 (CI 의 `error-codes-sync` 잡이 `sync` 재실행 후 `git diff` 로 드리프트를 검사하므로, 빠뜨리면 PR 이 실패한다) diff --git a/docker-compose.yml b/docker-compose.yml index 5a0fac9..79419d2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -55,14 +55,14 @@ services: - PORT=${BACKEND_PORT} - WEBSOCKET_PORT=${WEBSOCKET_PORT} - CORS_ORIGIN=${CORS_ORIGIN} - - THROTTLE_TTL_MS=${THROTTLE_TTL_MS} - - THROTTLE_LIMIT=${THROTTLE_LIMIT} + - THROTTLE_TTL_MS=${THROTTLE_TTL_MS:-60000} + - THROTTLE_LIMIT=${THROTTLE_LIMIT:-100} - DB_HOST=db - DB_PORT=5432 - DB_USER=${DB_USER} - DB_PASSWORD=${DB_PASSWORD} - DB_NAME=${DB_NAME} - - DB_SYNCHRONIZE=${DB_SYNCHRONIZE} + - DB_SYNCHRONIZE=${DB_SYNCHRONIZE:-false} - UPLOAD_DIR=/app/uploads - UPLOAD_MAX_SIZE_MB=${UPLOAD_MAX_SIZE_MB:-5} - REDIS_HOST=redis diff --git a/flutter/test/error_lexicon_test.dart b/flutter/test/error_lexicon_test.dart index e43fc31..1998e5f 100644 --- a/flutter/test/error_lexicon_test.dart +++ b/flutter/test/error_lexicon_test.dart @@ -18,9 +18,13 @@ void main() { ); }); - test('세 플랫폼 공유 에러 코드가 모두 등록되어 있다', () { - // backend error-codes.ts 레지스트리와 동일한 목록이어야 한다. - const expected = [ + test('클라이언트 로직이 참조하는 코드가 사전에 존재한다', () { + // dio_client 의 상태코드 역매핑과 세션 만료 승격이 이 코드들을 직접 사용한다. + // + // ⚠️ 전체 목록을 비교하지 않는 이유: 세 플랫폼의 사전은 backend 레지스트리에서 + // 생성되므로 목록 일치는 구조적으로 보장되고, CI 가 드리프트를 검사한다. + // 전수 비교를 하면 도메인 코드를 추가할 때마다 무의미하게 깨진다. + const usedByClient = [ 'SYS_001', 'REQ_001', 'AUTH_001', @@ -31,7 +35,17 @@ void main() { 'BIZ_001', ]; - expect(ErrorLexicon.codes.keys.toSet(), expected.toSet()); + for (final code in usedByClient) { + expect(ErrorLexicon.contains(code), isTrue, reason: '$code 누락'); + } + }); + + test('모든 코드는 DOMAIN_NNN 네이밍 규약을 따른다', () { + final pattern = RegExp(r'^[A-Z]+_\d{3}$'); + + for (final code in ErrorLexicon.codes.keys) { + expect(pattern.hasMatch(code), isTrue, reason: '규약 위반: $code'); + } }); }); diff --git a/frontend/src/composables/useApi.spec.ts b/frontend/src/composables/useApi.spec.ts index b00f5d8..fae1dcb 100644 --- a/frontend/src/composables/useApi.spec.ts +++ b/frontend/src/composables/useApi.spec.ts @@ -19,9 +19,14 @@ describe('에러 코드 사전', () => { ) }) - it('세 플랫폼 공유 에러 코드가 모두 등록되어 있다', () => { - // backend error-codes.ts 레지스트리와 동일한 목록이어야 한다. - const expected = [ + it('클라이언트 로직이 참조하는 코드가 사전에 존재한다', () => { + // useApi 의 상태코드 역매핑과 세션 만료 승격이 이 코드들을 직접 사용한다. + // 사전에서 빠지면 폴백 메시지가 나가므로 반드시 존재해야 한다. + // + // ⚠️ 전체 목록을 비교하지 않는 이유: 세 플랫폼의 사전은 backend 레지스트리에서 + // 생성되므로 목록 일치는 구조적으로 보장되고, CI 가 드리프트를 검사한다. + // 여기서 전수 비교를 하면 도메인 코드를 추가할 때마다 무의미하게 깨진다. + const usedByClient = [ 'SYS_001', 'REQ_001', 'AUTH_001', @@ -32,7 +37,9 @@ describe('에러 코드 사전', () => { 'BIZ_001', ] - expect(Object.keys(ERROR_CODE_MESSAGES).sort()).toEqual(expected.sort()) + for (const code of usedByClient) { + expect(ERROR_CODE_MESSAGES).toHaveProperty(code) + } }) it('모든 코드는 DOMAIN_NNN 네이밍 규약을 따른다', () => {