에러 처리
SDK 의 에러 모양
Hey API client-fetch 는 { data, error } discriminated union 반환:
const res = await BackOfficeTenant.list({ query })
if (res.error) {
// res.error: { code: string, message: string, ... }
// 백엔드의 ApiException JSON body 가 그대로 들어옴
} else {
res.data // typed response
}
또는 throw 모드 (throwOnError: true config) — 그러면 try/catch.
에러 코드 매핑
백엔드의 ApiException 이 다음 shape JSON 으로 응답:
{
"code": "user/UserIdDuplicated",
"message": "userId 'windy' is already used in this tenant"
}
code 를 '<domain>/<reason>' 컨벤션. UI 메시지 매핑 예시:
const MESSAGE_MAP: Record<string, string> = {
// auth
'auth/InvalidCredentials': '로그인 정보가 올바르지 않습니다',
'auth/InvalidToken': '세션이 만료됐습니다. 다시 로그인해주세요',
// user
'user/UserIdDuplicated': '이미 사용 중인 사용자 ID 입니다',
'user/NotFound': '사용자를 찾을 수 없습니다',
'user/NotManager': '매니저가 아닌 계정입니다',
// tenant
'tenant/NotFound': '존재하지 않는 회사입니다',
'tenant/KeyDuplicated': '이미 사용 중인 회사 ID 입니다',
// sign-up
'signUp/EmailDomainNotAllowed': '회사의 허용 이메일 도메인이 아닙니다',
// email verification
'emailVerification/InvalidCode': '인증 코드가 만료됐거나 잘못됐습니다',
'emailVerification/CooldownActive': '잠시 후 다시 시도해주세요 (60초 대기)',
// password reset
'passwordReset/InvalidCode': '재설정 코드가 만료됐거나 잘못됐습니다',
// refresh token
'refreshToken/Invalid': '세션이 만료됐습니다. 다시 로그인해주세요',
}
function toUserMessage(error: { code?: string; message?: string }): string {
return (error.code && MESSAGE_MAP[error.code]) ?? error.message ?? '일시적인 오류입니다'
}
자세한 코드 list — API Reference 의 각 endpoint 의 Responses 섹션.
try/catch 또는 mutation onError 패턴
React Query mutation 패턴
const { message } = App.useApp()
const mutation = useMutation({
mutationFn: async (input) => {
const res = await ClientAuth.signUp({ body: input })
if (res.error) throw res.error // ApiException JSON
return res.data!
},
onError: (err: any) => message.error(toUserMessage(err)),
})
일반 try/catch
try {
const res = await ClientAuth.signUp({ body: input })
if (res.error) {
message.error(toUserMessage(res.error))
return
}
// success
} catch (e) {
// network error / timeout / abort
message.error('네트워크 오류입니다. 잠시 후 다시 시도해주세요')
}
자동 처리되는 에러 — frontend 가 신경 X
| 상황 | SDK 자동 처리 |
|---|---|
| 401 access expired | refresh 호출 + 원 request retry. 성공 시 frontend 는 모름. |
| 401 refresh 도 실패 | hooks.onAuthFailure() 호출 — frontend 는 redirect 만 정의. |
| 5xx / 408 / 429 / network | exponential backoff retry (max 3). 모두 실패해야 caller 로. |
| timeout (30초) | AbortController abort. caller 에 AbortError 또는 같은 5xx-like 처리. |
| Multi-tab refresh 동시 | Web Locks + BroadcastChannel — 한 tab 만 실제 호출. |
→ frontend 가 직접 다루는 에러는 주로 business validation (4xx, code 별 메시지) 와 network 최종 실패 두 카테고리.
디버깅 — DevTools
Network탭에서 401 / 응답 body 의code확인Application > Cookies에서service_refresh쿠키 존재 / Domain / Path / HttpOnly 확인Application > Session Storage에서app.access만료 시각 확인- BroadcastChannel 의 message 는
chrome://inspect또는 SDK 의 debug 로그 (별도 PR 에서 추가 예정)