refactor: 에러 코드 문구 해요체로 통일

세 플랫폼이 공유하는 에러 코드 맵의 문장을 딱딱한 합니다체에서 해요체로
바꿨다. 화면 문구는 실제로 쓰는 말로만 쓴다는 기준에 맞춘 것이다.

- SYS_001 "일시적인 시스템 오류가 발생했습니다." → "일시적인 오류가 생겼어요."
- AUTH_003 "해당 메뉴나 기능에 접근할 수 있는 권한이 없습니다."
  → "이 메뉴나 기능을 이용할 권한이 없어요."
- VAL_001 의 "(필수값 누락 또는 형식 오류)" 는 개발자 말이라 문장으로 풀었다.
- main.ts 의 Rate Limit 응답도 같은 문장을 쓰고 있어 "요청이 너무 많아요" 로
  함께 고쳤다.

SSOT 3개 파일(backend 필터 · frontend ErrorCodeLexicon · flutter ErrorLexicon)과
규칙 문서, 그리고 문구를 인용하던 화면·테스트를 함께 갱신했다.

frontend lint · type-check · 테스트 11개 통과. flutter analyze 에서
error_lexicon.dart 는 문제 없음. backend 는 node_modules 가 비어 있어
로컬에서 lint/test 를 돌리지 못했다(문자열 리터럴만 변경).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0128kgT8ycCATfJVFULQMa9b
This commit is contained in:
2026-09-11 01:43:07 +09:00
parent e5de79a669
commit 4ef3deb87e
9 changed files with 38 additions and 36 deletions
@@ -1,5 +1,7 @@
<script setup lang="ts">
// 섹션 본문의 상태 분기 — 로딩(스켈레톤) · 실패 · 비어 있음 · 정상.
// 뿌리가 상태마다 갈리는 프래그먼트라 이 컴포넌트에 class 를 넘겨도 DOM 에 닿지 않는다.
// 여백·테두리는 부르는 쪽에서 감싸개 요소에 준다.
// 섹션마다 이 네 상태를 모두 가진다. 한 섹션이 실패해도 그 섹션만 다시 부른다.
import SectionStateBlock from '@/components/common/SectionStateBlock.vue'
import type { IconName } from '@/components/common/app-icon'
@@ -19,7 +21,7 @@ interface Props {
}
withDefaults(defineProps<Props>(), {
errorMessage: '일시적인 시스템 오류가 발생했습니다. 잠시 후 다시 시도해 주세요.',
errorMessage: '일시적인 오류가 생겼어요. 잠시 후 다시 시도해 주세요.',
minHeight: '15rem',
emptyDescription: undefined,
emptyLinkLabel: undefined,
+5 -5
View File
@@ -6,24 +6,24 @@ describe('resolveErrorMessage', () => {
it('서버가 내려준 에러 코드의 문장을 쓴다', () => {
const error = { response: { status: 400, data: { success: false, error: { code: 'AUTH_003', message: '' } } } }
expect(resolveErrorMessage(error)).toBe('해당 메뉴나 기능에 접근할 수 있는 권한이 없습니다.')
expect(resolveErrorMessage(error)).toBe(' 메뉴나 기능을 이용할 권한이 없어요.')
})
it('에러 코드가 없으면 HTTP 상태로 코드를 정한다', () => {
expect(resolveErrorMessage({ response: { status: 401 } })).toBe(
'로그인이 필요한 서비스입니다. 로그인 후 이용해 주세요.',
'로그인이 필요한 기능이에요. 로그인 후 이용해 주세요.',
)
expect(resolveErrorMessage({ response: { status: 422 } })).toBe(
'입력하신 정보를 다시 확인해 주세요. (필수값 누락 또는 형식 오류)',
'입력하신 내용을 다시 확인해 주세요. 빠진 항목이 있거나 형식이 맞지 않아요.',
)
expect(resolveErrorMessage({ response: { status: 404 } })).toBe(
'요청하신 정보나 페이지를 찾을 수 없습니다.',
'찾으시는 정보나 페이지가 없어요.',
)
})
it('모르는 코드나 응답이 없는 에러는 SYS_001 문장으로 떨어진다', () => {
const unknownCode = { response: { status: 500, data: { success: false, error: { code: 'NOPE_999', message: '' } } } }
const fallbackMessage = '일시적인 시스템 오류가 발생했습니다. 잠시 후 다시 시도해 주세요.'
const fallbackMessage = '일시적인 오류가 생겼어요. 잠시 후 다시 시도해 주세요.'
expect(resolveErrorMessage(unknownCode)).toBe(fallbackMessage)
expect(resolveErrorMessage(new Error('네트워크 끊김'))).toBe(fallbackMessage)
+7 -7
View File
@@ -25,13 +25,13 @@ declare module 'axios' {
// 에러 코드 사전 — CLAUDE.md / 백엔드 HttpExceptionFilter 와 1:1 매핑
// SYS_001 은 폴백 메시지로 항상 존재함을 타입으로 보장 (noUncheckedIndexedAccess 대응)
const ErrorCodeLexicon: Record<string, string> & { SYS_001: string } = {
SYS_001: '일시적인 시스템 오류가 발생했습니다. 잠시 후 다시 시도해 주세요.',
AUTH_001: '로그인이 필요한 서비스입니다. 로그인 후 이용해 주세요.',
AUTH_002: '안전을 위해 로그아웃 되었습니다. 다시 로그인해 주세요.',
AUTH_003: '해당 메뉴나 기능에 접근할 수 있는 권한이 없습니다.',
VAL_001: '입력하신 정보를 다시 확인해 주세요. (필수값 누락 또는 형식 오류)',
RES_001: '요청하신 정보나 페이지를 찾을 수 없습니다.',
BIZ_001: '요청하신 작업을 완료하지 못했습니다. 다시 시도해 주세요.',
SYS_001: '일시적인 오류가 생겼어요. 잠시 후 다시 시도해 주세요.',
AUTH_001: '로그인이 필요한 기능이에요. 로그인 후 이용해 주세요.',
AUTH_002: '안전을 위해 로그아웃했어요. 다시 로그인해 주세요.',
AUTH_003: ' 메뉴나 기능을 이용할 권한이 없어요.',
VAL_001: '입력하신 내용을 다시 확인해 주세요. 빠진 항목이 있거나 형식이 맞지 않아요.',
RES_001: '찾으시는 정보나 페이지가 없어요.',
BIZ_001: '요청하신 작업을 끝내지 못했어요. 다시 시도해 주세요.',
}
// 사용자 노출용 알림 — Toast 라이브러리 도입 시 이 함수만 교체하면 됨
@@ -35,7 +35,7 @@ describe('useSectionResource', () => {
await resource.load()
expect(resource.status.value).toBe('error')
expect(resource.errorMessage.value).toBe('요청하신 정보나 페이지를 찾을 수 없습니다.')
expect(resource.errorMessage.value).toBe('찾으시는 정보나 페이지가 없어요.')
expect(resource.data.value).toBeNull()
})
+1 -1
View File
@@ -8,7 +8,7 @@
role="alert"
>
<h1>404</h1>
<p>요청하신 정보나 페이지 찾을 습니다.</p>
<p>찾으시는 정보나 페이지 어요.</p>
<RouterLink to="/">
홈으로 돌아가기
</RouterLink>