1
0
forked from baron/baron-sso
Files
baron-sso/docs/wiki-error-handling-policy-backend-log-update.md
2026-04-01 20:32:09 +09:00

17 KiB

Wiki Update Draft: Error-Handling-Policy

대상 위키 페이지:

  • Error-Handling-Policy

이 문서는 위키에 바로 반영할 수 있도록, 기존 Error Handling Policy 원문 구조를 유지하면서 최근 backend 로그 정책과 headless login 디버그 규칙까지 함께 정리한 초안입니다.

반영 의도:

  • 기존 whitelist 중심의 prod 에러 노출 정책을 유지합니다.
  • 최근 추가된 headless login 세분화 오류 코드와 backend debug log 규칙을 같은 문맥에서 관리합니다.
  • UI 노출 정책과 backend API 응답 계약을 분리해 해석합니다.

Error Handling Policy

본 문서는 이슈 #164([UserFront] 에러 노출 whitelist 정의 및 적용)를 기준으로 정리한 프로덕션 에러 노출 정책입니다.

0) 범위와 해석 기준

  • 본 정책은 userfront, adminfront, devfront, backend가 공통으로 참고하는 에러 노출 기준입니다.
  • Backend는 기계 판독 가능한 code와 안전한 짧은 error 문자열을 내려주는 책임을 가집니다.
  • Frontcode를 기준으로 사용자 문구를 번역/매핑해 표시합니다.
  • 따라서 아래 표에서 한국어 문구는 최종 사용자 노출 기준, 영문 error 문자열은 backend 응답 예시로 해석합니다.

1) 기본 원칙

  • 프로덕션은 whitelist 방식만 허용합니다.
  • whitelist에 없는 에러는 일반 오류 메시지로 대체합니다.
  • 상세 원문 메시지는 프로덕션에서 비노출합니다.
  • Ory Stack(Kratos/Hydra/Oathkeeper)에서 발생한 에러 코드는 그대로 pass-through 합니다.
  • Custom 에러만 whitelist로 관리합니다. (Ory 코드 제외)

2) 노출 정책 및 Ory 에러 처리 (통합)

2.1 Ory 에러 pass-through 원칙

  • Ory Stack(Kratos/Hydra/Oathkeeper)에서 발생한 에러 코드는 그대로 pass-through 합니다.
  • Ory 에러는 whitelist 대상에서 제외합니다.
  • 단, 보안/UX 관점의 blacklist 후보는 예외적으로 unknown_error로 치환할 수 있습니다.

2.2 Custom whitelist (Ory 매핑 없음)

Ory 스택에서 발생하지 않으며 매핑 대상이 없는 Custom 에러만 관리합니다.
이 중 HTTP 상태 코드와 일치하는 항목은 별도 표로 구분합니다.

2.2.1 HTTP status와 일치하는 Custom 에러

| error_code | http_status | 사용자 메시지 (기본) | Backend error 예시 | 설명 | |---|---:|---|---| | not_found | 404 | 요청한 페이지를 찾을 수 없습니다. | Not found | 경로 오류 | | rate_limited | 429 | 요청이 많습니다. 잠시 후 다시 시도해 주세요. | Too many requests | 제한 초과 |

2.2.2 HTTP status와 무관한 Custom 에러

| error_code | 사용자 메시지 (기본) | Backend error 예시 | 설명 | |---|---|---| | password_or_email_mismatch | 이메일 혹은 비밀번호가 일치하지 않습니다. | Invalid credentials | 비밀번호 입력 오류 | | invalid_client_assertion_parse | 클라이언트 인증 정보 형식이 올바르지 않습니다. | Client assertion format is invalid | headless login assertion 형식 오류 | | invalid_client_assertion_signature | 클라이언트 인증 정보 검증에 실패했습니다. | Client assertion signature verification failed | headless login assertion 서명 검증 실패 | | invalid_client_assertion_iss_sub | 클라이언트 인증 정보 발급 주체가 올바르지 않습니다. | Client assertion issuer or subject mismatch | headless login assertion iss/sub 불일치 | | invalid_client_assertion_expired | 클라이언트 인증 정보가 만료되었습니다. | Client assertion has expired | headless login assertion 만료 | | invalid_client_assertion_not_before | 클라이언트 인증 정보가 아직 활성 상태가 아닙니다. | Client assertion is not active yet | headless login assertion 활성 시각 전 | | invalid_client_assertion_iat_future | 클라이언트 인증 정보 발급 시각이 올바르지 않습니다. | Client assertion issued-at time is invalid | headless login assertion iat 미래 시각 | | invalid_client_assertion_audience | 클라이언트 인증 정보 대상이 일치하지 않습니다. | Client assertion audience mismatch | headless login assertion aud 불일치 | | invalid_client_assertion_jwks_load | 클라이언트 공개키 검증에 실패했습니다. | Headless login jwks verification failed | headless login jwksUri 조회/파싱 실패 계열 |

2.3 HTTP status 핸들링 정책

  • Ory error 코드가 존재하면 pass-through 우선합니다.
  • Ory error 코드가 없고, HTTP status가 404/429인 경우:
    • 404 -> not_found
    • 429 -> rate_limited
  • 위 조건에 해당하지 않는 Custom 에러는 **기본 정책(unknown_error)**을 적용합니다.

2.4 비노출(기본) 에러 처리

whitelist에 없는 모든 Custom 에러는 아래 공통 처리 규칙을 따릅니다.

  • 사용자 메시지: "일시적인 오류가 발생했습니다. 잠시 후 다시 시도해 주세요."
  • 오류 종류는 unknown_error로 고정합니다.
  • 상세 원문 메시지는 사용자에게 표시하지 않습니다.

2.5 Ory 에러 blacklist 후보 (검토용)

Ory 에러를 pass-through 하더라도, 아래 유형은 보안/UX 관점에서 숨김 처리(blacklist) 후보입니다.

  • security_csrf_violation
  • security_identity_mismatch
  • browser_location_change_required
  • server_error
  • temporarily_unavailable

제안: 위 코드는 prod에서 unknown_error로 치환하고, log/audit에만 원문을 남기는 방식이 안전합니다.

2.6 Oathkeeper 경유 에러 처리

현재 설정(docker/ory/oathkeeper/oathkeeper.yml)에는 에러 변환 로직이 없고, errors.fallback: json만 정의되어 있습니다.
즉, Oathkeeper는 에러 코드를 변환하지 않고 JSON으로 그대로 반환합니다.
따라서 Ory Stack 에러는 Oathkeeper를 통과하더라도 그대로 유지된다고 가정합니다.

3) UI 정책

  • 공통 에러 화면에는 아래 항목을 표시합니다.
    • 제목
    • 사용자 메시지
    • 오류 종류(error_code)
    • 홈으로 이동 버튼
  • error_id가 있는 경우에만 표시합니다.
  • Backend의 error 문자열은 최종 사용자 문구의 Source of Truth가 아닙니다.
  • Front는 가능하면 code 기준으로 번역 리소스를 선택하고, error는 fallback 또는 운영 진단 보조 텍스트로만 사용합니다.

4) 구현 가이드

  • 에러 표시 로직은 whitelist 검사 후 결정합니다.
  • 예시:
    • if error_code in whitelist: message = whitelist_message
    • else: error_code = "unknown_error", message = default_message

5) 프로덕션 전용 동작 및 테스트 요구사항

5.1 프로덕션 전용 동작

  • whitelist 적용(비노출/unknown_error 치환)은 프로덕션에서만 동작해야 합니다.
  • Ory Stack 에러는 prod에서도 pass-through 합니다. (단, blacklist 후보는 예외 처리 가능)
  • 프로덕션 판정 기준:
    • Front: APP_ENVprod 또는 production일 때만 활성화
    • 테스트/로컬: override 옵션을 사용해 프로덕션/비프로덕션 동작을 강제할 수 있어야 합니다.

5.2 테스트 요구사항

최소 아래 케이스를 자동 테스트로 보장합니다.

  • Prod + whitelist 코드: 사용자 메시지는 whitelist 메시지, error_code는 원래 코드 유지
  • Prod + 비-whitelist 코드: 사용자 메시지는 기본 메시지, error_codeunknown_error
  • Prod + error_id 없음: error_id 표시 없음
  • Non-prod + error_code 존재: 원본 에러 코드/설명 표시
  • Non-prod + description 없음: 기본 설명 노출

권장 테스트 위치:

  • userfront/test/error_screen_test.dart

권장 테스트 방식:

  • ErrorScreen(isProdOverride: true/false, ...)로 환경을 강제하여 동작 검증
  • AuthProxyService.isProdEnvAPP_ENV에 의존하므로, 테스트에서 직접 환경 변수에 의존하지 않도록 override 사용

5.3 재현 테스트 우선 원칙

  • 에러 처리 로직 변경 시, 먼저 재현 테스트를 작성합니다.
  • 테스트는 최소한 status, code, error 응답 계약을 검증해야 합니다.
  • 사용자 노출 메시지는 번역 리소스로 처리하고, API는 기계 판독 가능한 code를 우선 계약으로 유지합니다.
  • 원문(한글/영문) 에러 문자열이 바뀌어도 code 기반 동작은 깨지지 않아야 합니다.
  • 운영 이슈에서 확보한 req_id, 로그 패턴, 실제 응답 payload는 가능하면 테스트 케이스 설명에 남겨 회귀 근거를 보존합니다.

5.4 회귀 테스트 기준

  • 인증 실패(예: password mismatch)에서 code가 기대값으로 반환되는지 검증합니다.
  • 4xx/5xx 주요 에러 경로에 대해 최소 1개 이상의 핸들러 테스트를 유지합니다.
  • 운영 이슈로 확인된 에러 케이스는 반드시 회귀 테스트 케이스로 승격합니다.

5.5 Headless Login 실패 코드 회귀 기준

Headless login 경로는 기존 generic invalid_client_assertion만으로는 운영 진단이 느렸기 때문에, 아래 코드를 별도 회귀 대상으로 유지합니다.

  • invalid_client_assertion_parse
  • invalid_client_assertion_signature
  • invalid_client_assertion_iss_sub
  • invalid_client_assertion_expired
  • invalid_client_assertion_not_before
  • invalid_client_assertion_iat_future
  • invalid_client_assertion_audience
  • invalid_client_assertion_jwks_load
  • password_or_email_mismatch

권장 테스트 위치:

  • backend/internal/handler/auth_handler_login_test.go

최소 검증 항목:

  • 응답 status
  • 응답 code
  • 응답 error
  • debug 레벨에서만 진단 필드가 로그에 포함되는지 여부

6) 변경 관리

  • 에러 코드 추가/삭제는 이슈 등록 후 반영합니다.
  • 사용자 메시지는 제품 문구 기준에 따라 수정합니다.

7) Ory 에러 코드(참고)

아래는 Ory(Kratos/Hydra)에서 기본 제공되는 에러 코드를 참고용으로 정리합니다.

7.1 Ory Kratos error.id

Kratos Self-Service Flow의 error.id는 다음 코드들이 공식 문서/SDK에 명시되어 있습니다.

  • session_inactive
  • session_already_available
  • session_aal1_required
  • session_refresh_required
  • security_csrf_violation
  • security_identity_mismatch
  • browser_location_change_required

7.2 Ory Hydra / OAuth2·OIDC 표준 에러

Hydra는 OAuth2/OIDC 표준 에러 코드(error 필드)를 사용합니다.

  • OAuth2 표준:
    • invalid_request
    • unauthorized_client
    • access_denied
    • unsupported_response_type
    • invalid_scope
    • server_error
    • temporarily_unavailable
  • OIDC 표준:
    • consent_required

8) 에러 코드 관리 위치 제안 (아키텍처 기준)

에러 코드는 Backend에서 표준화하고, Front에서 사용자 문구로 매핑하는 구조가 가장 안전합니다.

8.1 Backend (단일 진입점, 표준화의 Source of Truth)

  • 외부(Ory Kratos/Hydra/Oathkeeper) 및 내부 에러를 표준 error_code로 변환하는 로직을 Backend에 둡니다.
  • 권장 위치:
    • backend/internal/handler/ 또는 backend/internal/service/ 하위에 error_mapper.go 성격의 모듈
    • 예시: backend/internal/service/error_mapper.go
  • Backend 응답은 다음을 보장합니다.
    • error_codeerror_id를 일관 포맷으로 내려줌
    • whitelist 외 코드는 unknown_error로 치환 (프로덕션 기준)

8.2 Front (문구 매핑, 표현 계층)

  • 사용자 메시지와 UI 표시는 Front에서 담당합니다.
  • 현재 위치(유지 또는 통합 권장):
    • userfront/lib/core/constants/error_whitelist.dart
    • userfront/lib/features/auth/presentation/error_screen.dart
  • Admin/DevFront에서도 같은 whitelist를 사용해야 하므로, 아래 중 하나로 통일을 권장합니다.
    1. Backend에서 error whitelist 리스트를 내려주는 API 제공
    2. 공용 패키지/공용 파일로 관리 후 각 Front에서 참조

8.3 Ory Stack / Gateway

  • Ory(Kratos/Hydra)와 Oathkeeper는 원문 에러만 발생시키고 표준화는 하지 않음을 원칙으로 합니다.
  • Gateway/Proxy 레이어는 에러 코드를 변환하지 않음이 안전합니다.

9) Backend Log Level Policy

에러 응답 정책과 운영 디버깅 정책은 분리하되, 실제 운영에서 함께 보게 되는 경우가 많으므로 backend 로그 레벨 규칙을 같이 관리합니다.

9.1 기준 변수

  • APP_ENV
  • BACKEND_LOG_LEVEL (optional override)

9.2 기본 규칙

  • APP_ENV=dev|local|development
    • backend slog 기본 레벨은 debug
    • text handler 사용
  • 그 외 환경(stage, production, prod 등)
    • backend slog 기본 레벨은 info
    • JSON handler 사용

9.3 운영 override

  • 운영/스테이징에서 장애 분석이 필요한 경우에만 BACKEND_LOG_LEVEL=debug를 일시적으로 설정합니다.
  • 허용 값:
    • debug
    • info
    • warn
    • error

예시:

APP_ENV=stage
BACKEND_LOG_LEVEL=debug

9.4 Headless Login 디버그 필드

  • headless login 경로는 기본적으로 reason_code 중심으로 실패 원인을 기록합니다.
  • debug 레벨일 때만 추가 진단 필드를 남깁니다.
    • expected_audiences
    • received_audiences
    • received_kid
    • claim_issuer
    • claim_subject
    • claim_expires_at
    • claim_not_before
    • claim_issued_at
    • login_challenge_prefix

9.5 응답과 로그의 역할 분리

  • API 응답은 2번 정책에 따라 code + 짧은 안전 메시지까지만 포함합니다.
  • 상세 실패 원인은 구조화 로그에서 확인합니다.
  • 같은 실패라도 응답에는 축약된 정보만, debug 로그에는 운영 진단용 필드를 남기는 것이 기본 원칙입니다.

9.6 민감 정보 비노출 원칙

  • 아래 값은 로그에 직접 남기지 않습니다.
    • raw client_assertion
    • password
    • session token
    • cookie

9.7 운영 메모

  • 운영에서는 기본적으로 info를 유지합니다.
  • 장애 분석이 끝나면 BACKEND_LOG_LEVEL override는 즉시 제거합니다.
  • 클라이언트 로그 정책(CLIENT_LOG_DEBUG)과 backend logger 정책(BACKEND_LOG_LEVEL)은 별도입니다.

10) 부록: Ory UI Error Codes 처리 원칙 (요약)

Ory Kratos UI 문서의 원칙을 기반으로, Baron 정책에 반영해야 할 핵심 처리 방침을 요약합니다.

  • 메시지는 root / method / field 레벨에 붙을 수 있으며, UI에서 범위를 고려해 표시해야 합니다.
  • UI 메시지는 id, text, type, context 형태로 전달되며, id고정된 값입니다.
  • 메시지 id는 **7자리 규칙(xyyzzzz)**을 따릅니다.
    • x: 메시지 타입 (1=info, 4=input validation error, 5=generic error)
    • yy: 모듈/플로우 (01=login, 02=logout, 03=MFA, 04=registration, 05=settings, 06=recovery, 07=verification)
    • zzzz: 구체 메시지 ID
  • SPA/Native UI에서는 Ory가 에러 응답을 직접 반환하는 경우가 있으므로, UserFront에 에러 ID별 처리 로직이 필요합니다. (예: flow 만료/재시작, 인증 단계 재진입 등)
  • Ory는 React 레퍼런스 구현에서 에러 처리 로직 예시를 제공합니다.
  • UI 메시지 목록은 machine readable JSON으로 제공합니다.

11) References


로컬 참조 문서

  • docs/backend-log-policy.md
  • docs/client-log-policy.md
  • docs/test-plan/backend-test-inventory.md