서버 에러 통합
15.3.1. 백엔드 에러 응답 형식
TQS 백엔드 표준 에러 응답은 다음 구조를 따릅니다. 프론트엔드는 이 형식에 맞춰 에러를 처리해야 합니다.
typescript
// src/types/api.ts
interface ApiErrorResponse {
errorCode: string // 에러 코드 (예: 'VALIDATION_ERROR', 'CONFLICT')
message: string // 사용자 대상 메시지
fieldErrors?: { // 필드별 검증 에러 (선택)
field: string
message: string
}[]
}| 필드 | 필수 여부 | 설명 |
|---|---|---|
errorCode | 필수 | 에러 분류 코드 |
message | 필수 | 전역 에러 메시지 |
fieldErrors | 선택 | 필드 단위 검증 실패 목록 |
fieldErrors는 폼 제출 시 서버 측 검증 실패인 경우에만 포함됩니다.errorCode는 프론트엔드에서 에러 유형별 분기 처리에 사용합니다.
15.3.2. 필드별 에러 매핑
서버에서 반환된 fieldErrors는 폼의 errors 상태에 매핑합니다.
typescript
// src/utils/mapServerErrors.ts
import type { ApiErrorResponse } from '@/types/api'
export function mapFieldErrors(
response: ApiErrorResponse
): Record<string, string> {
const errors: Record<string, string> = {}
if (response.fieldErrors) {
for (const fieldError of response.fieldErrors) {
errors[fieldError.field] = fieldError.message
}
}
return errors
}폼 제출 핸들러에서 다음과 같이 적용합니다.
typescript
import { mapFieldErrors } from '@/utils/mapServerErrors'
import axios from 'axios'
async function handleSubmit() {
isSubmitting.value = true
errors.value = {}
try {
await createUser(form.value)
} catch (e) {
if (axios.isAxiosError(e) && e.response?.data) {
const serverError = e.response.data as ApiErrorResponse
errors.value = mapFieldErrors(serverError)
}
} finally {
isSubmitting.value = false
}
}- 서버의
field값은 폼 객체의 키와 일치해야 합니다. - 백엔드와 프론트엔드 간 필드명 규칙을 사전에 합의합니다.
15.3.3. 글로벌 에러 처리
fieldErrors에 매핑되지 않는 에러는 글로벌 에러로 처리합니다. 토스트 알림 또는 폼 상단 배너로 표시합니다.
typescript
import { useToast } from '@/composables/useToast'
const { showToast } = useToast()
async function handleSubmit() {
isSubmitting.value = true
errors.value = {}
try {
await createUser(form.value)
} catch (e) {
if (axios.isAxiosError(e) && e.response?.data) {
const serverError = e.response.data as ApiErrorResponse
const fieldErrors = mapFieldErrors(serverError)
if (Object.keys(fieldErrors).length > 0) {
errors.value = fieldErrors
} else {
showToast({ type: 'error', message: serverError.message })
}
} else {
showToast({ type: 'error', message: '요청 처리 중 오류가 발생했습니다.' })
}
} finally {
isSubmitting.value = false
}
}| 에러 유형 | 표시 방식 | 예시 |
|---|---|---|
| 필드 검증 에러 | 필드 하단 인라인 | "이미 사용 중인 이메일입니다." |
| 비즈니스 로직 에러 | 토스트 알림 | "일일 등록 한도를 초과했습니다." |
| 네트워크/서버 에러 | 토스트 알림 | "요청 처리 중 오류가 발생했습니다." |
15.3.4. 에러 초기화
사용자가 폼을 수정하면 관련 에러를 자동으로 초기화합니다. 재제출 시에는 전체 에러를 초기화합니다.
typescript
import { watch } from 'vue'
// 개별 필드 변경 시 해당 에러 초기화
watch(
() => form.value.email,
() => {
if (errors.value.email) {
delete errors.value.email
}
}
)
// 범용 초기화 — 폼 전체 감시
watch(
form,
() => {
errors.value = {}
},
{ deep: true }
)- 필드 단위 초기화는 특정 필드의 에러만 제거하여 다른 필드의 에러 메시지를 유지합니다.
- 폼 전체 감시는 모든 에러를 일괄 제거하므로 간결하지만, 다른 필드의 에러도 사라지는 점에 유의합니다.
- 프로젝트 요구사항에 따라 적절한 전략을 선택합니다.