필요한 이유
1. 반복 코드 제거
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON,stringify(data)
});
- 매번 이렇게 fetch()를 작성하면 중복 코드가 많고, 실수 가능성도 증가
2. 변경에 강한 코드 구조
- 나중에 fetch -> aixos 또는 axios -> bxios 등으로 변경하게 되면, 전역적으로 fetch가 쓰인 모든 곳을 수정해야 함
- 레퍼를 이용해 중앙 레퍼만 바꾸면 전체 API 호출 방식이 바뀜
3. 공통 처리 (관심사 분리)
- 인증 토큰 삽입, 오류 처리, 응답 파싱 등을 중앙에서 한 번만 처리 가능
- 로그인한 유저라면 요청마다 Authorization 헤더 붙여야 함
4. 로깅, 캐시, 리트라이 정책, 인터셉터 등
실무에서의 공통 로직을 래핑하면 쉽게 추가 가능
- 로딩 인디케이터 처리
- 에러 로깅
- 요청 캐싱
- 실패 시 재시도
fetch 기반 래퍼 ApiClient
export default class ApiClient {
static async get(endpoint, headers = {}) {
return this.request('GET', endpoint, null, headers);
}
static async post(endpoint, body, headers = {}) {
return this.request('POST', endpoint, body, headers);
}
static async put(endpoint, body, headers = {}) {
return this.request('PUT', endpoint, body, headers);
}
static async delete(endpoint, headers = {}) {
return this.request('DELETE', endpoint, null, headers);
}
...
- get, post, put, delete는 각 HTTP method에 맞는 요청을 만들어주는 헬퍼
- 실질적 호출은 request()가 담당
- 모든 API 호출을 답당하는 정적 유틸 클래스
- static 메서드만 사용하므로 인스턴스 생성 불필요
- newApiClient() 말고 ApiClient.get(...)처럼 사용 가능
static async request(method, endpoint, body = null, headers = {}) {
const url = `${BASE_URL}${endpoint}`;
const options = {
method,
headers: {
'Content-Type': 'application/json',
...headers
},
body: body ? JSON.stringify(body) : null
};
try {
const response = await fetch(url, options);
const data = await response.json();
if (!response.ok) {
throw new Error(data.message || 'error occured');
}
return data;
} catch (error) {
throw error;
}
}
- BASE_URL을 기준으로 모든 요청 URL을 구성함
- 공통 headers 처리 (Content-Type, 인증 등)
- 필요시 추가 headers 병합
- 응답 오류 처리 (response.ok 체크)
- 에러 발생 시 메시지를 추출해 예외 throw
사용 예시
const user = await ApiClient.get('/users/123');
const created = await ApiClient.post('/users', { name: 'John' });
const updated = await ApiClient.put('/users/123', { name: 'Jane' });
await ApiClient.delete('/users/123');
axios 기반 래퍼 initInstance
const initInstance = (config: CreateAxiosDefaults): AxiosInstance => {
const instance = axios.create({
timeout: 5000,
headers: {
'Content-Type': 'application/json',
...config.headers,
},
...config,
});
instance.interceptors.request.use(config => {
const authToken = userInfoStorage.get()?.authToken;
if(authToken) {
config.headers.Authorization = authToken;
}
return config;
});
return instance;
};
- axios.create()를 통해 커스텀 인스턴스를 생성
- 기본 timeout, 공통 headers 설정
- 인터셉터(request interceptor)를 통해 자동으로 Authorization 헤더 삽입
- 인스턴스만 사용하면 항상 인증 토큰 붙어 있음
- 응답 인터셉터도 추가 가능
axios.create()
- 새로운 axios 인스턴스를 생성함
interceptors.request.use()
- 요청이 서버에 보내지기 전에 요청 설정을 가로채는 기능
- 토큰 자동 삽입
CreateAxiosDefaults
- axios 기본 설정 타입 (baseURL, headers 등)
AxiosInstance
- 커스터마이즈된 axios 객체의 타입
const initInstance = (config: CreateAxiosDefaults): AxiosInstance => {...}
- config : 호출 시 전달되는 사용자 정의 설정
- 반환값은 AxiosInstance : 우리가 만든 설정을 적용한 axios 객체
const instance = axios.create({
timeout: 5000,
headers: {
'Content-Type': 'application/json',
...config.headers,
},
...config,
});
- timeout: 5000 : 5초 내 응답이 없으면 요청 실패
- headers : 기본적으로 JSON 타입의 본문 사용
- ...config.headers : 외부에서 전달한 헤더 설정 병합
- ...config : 그 외 전달된 설정도 포함 (baseURL, withCredentials 등)
- ...config는 마지막에 두어야 외부 설정이 우선됨
instance.interceptors.request.use(config => {
const authToken = userInfoStorage.get()?.authToken;
if (authToken) {
config.headers.Authorization = authToken;
}
return config;
});
- 요청이 서버로 전송되기 직전에 이 함수가 실행됨
- authToken을 꺼내서 요청 헤더에 자동으로 붙임
const userInfoStorage = {
get: () => JSON.parse(localStorage.getItem('userInfo') || '{}'),
set: data => localStorage.setItem('userInfo', JSON.stringify(data))
}
사용 예시
export const apiInstance = initInstance({
baseURL: url,
});
const user = await apiInstance.get('/users/me');
const updated = await apiInstance.post('/posts', { title: 'Hello' });
'카테캠 > 2단계' 카테고리의 다른 글
| [7/22] 무한 스크롤 옵저버 (0) | 2025.07.22 |
|---|---|
| [7/21] Intersection Observer (1) | 2025.07.22 |
| [7/17] local storage (0) | 2025.07.18 |
| [7/17] 로그인 axios post (3) | 2025.07.18 |
| [7/16] Axios (4) | 2025.07.16 |