개발 기록
axios.post()만 써도 충분한가요? async/await로 감싸는 이유와 API 구조화 패턴 비교
JeongPark
2025. 7. 2. 16:33
728x90
프론트엔드에서 API를 호출할 때 가장 많이 쓰는 패턴은 아래와 같다.
axios.post('/auth/login', { email, password });
하지만 실제 운영 환경에서는 다음과 같은 고민이 생긴다.
- 왜 async/await로 한 번 더 감싸야 하지?
- 에러 핸들링은 어디까지 하는 게 좋을까?
- res.data.data.data 같은 코드, 어떻게 해결하지?
- API 클라이언트를 어떻게 구성해야 유지보수가 쉬울까?
이 글에서는 위와 같은 질문에 답하면서 현업에서 쓰는 API 호출 방식의 구조화된 패턴을 소개한다.
1. 기존 방식 – 단순 호출
export const login = (email: string, password: string) => {
return axios.post('/auth/login', { email, password });
};
단순 호출은 작성은 빠르지만 다음과 같은 문제가 생긴다.
- 호출 측에서 매번 try/catch를 직접 작성해야 함
- 에러가 발생했을 때 정확한 원인 파악이 어려움
- 응답 구조가 중첩되어 있어 data.data.data 같은 형태가 되기 쉬움
try {
const res = await login('a@a.com', '1234');
const data = res.data.data.data;
} catch (err) {
// 에러 핸들링이 불명확
}
2. 개선 방식 – async/await + try/catch + 응답 구조 정리
export const login = async (email: string, password: string) => {
try {
const res = await axios.post('/auth/login', { email, password });
return res.data.data; // 중첩 구조를 평탄화해서 리턴
} catch (err) {
throw handleAxiosError(err);
}
};
이렇게 구성하면 클라이언트 단에서 불필요한 .data.data 접근 없이 바로 사용할 수 있다.
3. 공통 에러 처리기 예시
function handleAxiosError(error: unknown): Error {
if (axios.isAxiosError(error)) {
if (error.response) {
return new Error(error.response.data.message ?? '서버 응답 오류');
}
return new Error('서버 응답이 없습니다.');
}
return new Error('알 수 없는 오류가 발생했습니다.');
}
4. 유지보수 관점에서 비교
항목 | 단순 호출 방식 | async/await 래핑 방식 |
코드 작성 간결성 | 높음 | 보통 |
에러 추적 및 구분 | 어려움 | 쉬움 |
응답 구조 통일 | 없음 | 있음 |
확장성 (인터셉터 등) | 낮음 | 높음 |
5. 보너스 – axios 인터셉터로 전역 에러 처리
axios.interceptors.response.use(
res => res,
err => {
if (err.response?.status === 401) {
window.location.href = '/login';
}
return Promise.reject(err);
}
);
6. 결론 및 추천 패턴
- API 함수는 async/await + try/catch로 구성하자.
- 에러 핸들링 로직은 공통 함수로 위임하자.
- res.data.data처럼 중첩된 응답 구조는 평탄화해서 리턴하자.
- axios 인스턴스와 인터셉터를 이용해 전역 에러 처리까지 고려하자.
7. 다음 글 예고
- 중첩된 응답 구조(response.data.data)를 평탄화하는 유틸 함수 만들기
- API 요청 모듈을 하나로 통합하는 방법
- 사용자에게 친절한 에러 메시지 구성 전략
반응형