logo

DowanKim

2. API 커스텀 훅 구현 코드 리뷰

2025년 9월 10일

이게머니

Apis 사전세팅, 커스텀 훅 폴더에 대한 리뷰를 멘토님께 받을 수 있었습니다.


리뷰한 폴더 파일 구조

구현 내용

1.API 커스텀 훅src/Apis/

├── config.ts # 환경별 설정 관리

├── axios.ts # Axios 인스턴스 설정

├── auth.ts # 인증 및 쿠키 관리

├── queryClient.ts # React Query 설정 + 에러 처리

├── types.ts # 모든 타입 정의

├── useQueryApi.ts # GET 요청 커스텀 훅

├── useMutationApi.ts # POST/PUT/DELETE/PATCH 커스텀 훅

├── QueryProvider.tsx # React Query 프로바이더

└── index.ts # 통합 export

config.ts에서 api 관련 환경별 설정 관리 - 환경 변수 기반 URL, 하드 코딩 제거, 설정 중앙화

axios.ts에서 HTTP클라이언트 설정 - axios 인스턴스, 인터셉트 설정, 쿠키 자동 전송, 에러는 커스텀 훅에서 처리

auth.ts에서 인증 및 쿠키 관리 - 인증 상태 확인, 로그아웃, 쿠키 CRUD, 보안 강화(SameSite=Strict, Secure플래그), 환경별 보안 설정(HTTPS에서 Secure), 완전한 쿠키 삭제, 실제 토큰 값 유효성 검증

queryClient.ts 에서 탠스택 쿼리 클라이언트 설정 및 공통 에러 처리 - 에러 처리 (전역/개별), 401 자동 리다이렉트, 공통 에러처리 중복 함수사용으로 제거

useQueryAPi.ts 에서 GET 요청 훅 - 팩토리 패턴, 커스텀 헤더, 중복 기본값 제거

useMutationApi.ts에서 POST/PUT/DELETE/PATCH 훅 - 편의 함수, 타입 안정성

index.ts에서 export 가능 - 배럴 패턴, 모든 기능 처리

Request Changes

1. auth.ts에서 setCookie와 deleteCookie에 isSecure와 secureFlag가 중복됨. getSecureFlag함수로 따로 빼기

const isSecure = window.location.protocol === 'https:'; const secureFlag = isSecure ? ';Secure' : '';

답변 : 아래와 같이 함수화

const getSecureFlag = (): string => { const isSecure = window.location.protocol === 'https:'; return isSecure ? ';Secure' : ''; }; export const setCookie = (name: string, value: string, days: number = 7): void => { const expires = new Date(); expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000); const secureFlag = getSecureFlag(); document.cookie = `${name}=${value};expires=${expires.toUTCString()};path=/;SameSite=Strict${secureFlag}`; }; export const deleteCookie = (name: string): void => { const secureFlag = getSecureFlag(); document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/;SameSite=Strict${secureFlag}`; document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/;domain=${window.location.hostname};SameSite=Strict${secureFlag}`; };
  1. auth.ts에서 유효성 검사 로직을 isValidCookieValue 함수로 상단에 분리하기
return ( !!(token && token !== 'null' && token !== 'undefined') || !!(auth && auth !== 'null' && auth !== 'undefined') );

답변: 함수 분리

const isValidCookieValue = (value: string | null): boolean => { return !!(value && value !== 'null' && value !== 'undefined'); }; export const isAuthenticated = (): boolean => { const token = getCookie('token'); const auth = getCookie('auth'); return isValidCookieValue(token) || isValidCookieValue(auth); };

2. config.ts에서

- ApiConfig interface를 정의해서 타입 안정성 강화하기
- getBaseURL의 로직을 createBaseURL로 따로 분리하기
- timeout 상수 따로 정의

답변: 분리 진행, DEFAULT_API_TIMEOUT으로 상수 정의

const createBaseURL = (): string => { const envUrl = import.meta.env.VITE_API_URL; if (envUrl) { return envUrl; } console.warn('VITE_API_URL 환경 변수가 설정되지 않았습니다. 개발 환경 기본값을 사용합니다.'); return '/api'; }; interface ApiConfig { getBaseURL: () => string; timeout: number; withCredentials: boolean; defaultHeaders: { 'Content-Type': string; }; } export const apiConfig: ApiConfig = { getBaseURL: createBaseURL, timeout: 10000, withCredentials: true, defaultHeaders: { 'Content-Type': 'application/json', }, };

3. useMutationApi에서 코드 중복 제거

export const usePostApi = <TData, TVariables = void>( url: string, options?: MutationApiOptions<TData, TVariables>, ) => { return useMutationApi<TData, TVariables>('post', url, options); }; export const usePutApi = <TData, TVariables = void>( url: string, options?: MutationApiOptions<TData, TVariables>, ) => { return useMutationApi<TData, TVariables>('put', url, options); }; export const usePatchApi = <TData, TVariables = void>( url: string, options?: MutationApiOptions<TData, TVariables>, ) => { return useMutationApi<TData, TVariables>('patch', url, options); }; export const useDeleteApi = <TData, TVariables = void>( url: string, options?: MutationApiOptions<TData, TVariables>, ) => { return useMutationApi<TData, TVariables>('delete', url, options); };

답변:

생각해보니 재사용이 된 코드가 굉장히 많은 것 같습니다.. 이번 리뷰 이후 팀원들과 Api 기본 세팅 폴더의 파일들에 대해 하나하나 서로의 의견을 주고 받는 시간을 가지도록 하겠습니다. 리뷰 감사합니다.

createMethodHook을 통해 반영 완료하였습니다.

const createMethodHook = <TData, TVariables = void>(method: HttpMethod) => { return (url: string, options?: MutationApiOptions<TData, TVariables>) => useMutationApi<TData, TVariables>(method, url, options); }; export const usePostApi = createMethodHook('post'); export const usePutApi = createMethodHook('put'); export const usePatchApi = createMethodHook('patch'); export const useDeleteApi = createMethodHook('delete');
  1. useMutationApi.ts에서 HttpMethod 타입 별도 정의

답변 : 타입 파일에 따로 분리

type HttpMethod = 'post' | 'put' | 'delete' | 'patch';

TODO

API 사전 세팅 파일 작성 관련하여, 제대로된 이해도 없이 코드를 작성하고 단순히 실행이 되게 하는 것에 매몰되어있던 한주였던 것 같습니다. 이 코드를 혼자서 작성하면서도 스스로 이해가 어려웠던것을 고려하여, 이번주 모각코 때 팀원들과 Api 폴더의 파일들에 대해 흐름과 그 의미들을 서로 이야기하고 되짚어보는 시간을 가져야 팀원들도 API 커스텀 훅을 사용할 때 제대로 이해할 수 있다고 생각하여, 팀원들과 일정 잡고 진행해 보도록 하겠습니다. 항상 자세하고 의미있는 리뷰 감사드립니다 :)