πŸ’» API λͺ¨λ“ˆν™”ν•˜κΈ° (feat. axios μΈμŠ€ν„΄μŠ€) + ν”„λ‘œμ νŠΈ 이슈 ν•΄κ²°

waterglassesΒ·2022λ…„ 6μ›” 14일
10

λ¦¬νŒ©ν† λ§

λͺ©λ‘ 보기
6/6
post-thumbnail

⚠️ μ •λ¦¬ν•œ λ‚΄μš©μ€ μ˜€νƒ€λ‚˜ 잘λͺ»λœ 정보가 μžˆμ„ 수 μžˆμŠ΅λ‹ˆλ‹€. λŒ“κΈ€λ‘œ μ•Œλ €μ£Όμ‹œλ©΄ κ°μ‚¬ν•˜κ² μŠ΅λ‹ˆλ‹€.

πŸ“ƒ API λͺ¨λ“ˆν™”ν•˜κΈ°

APIλ₯Ό μ—¬κΈ°μ €κΈ°μ„œ μΌμ •ν•œ ν˜•μ‹ 없이 뢈러였고 μž¬μ‚¬μš©μ„±μ΄ 많이 λΆ€μ‘±ν–ˆλ˜ μ½”λ“œλ₯Ό 많이 μž‘μ„±ν–ˆμ—ˆμŠ΅λ‹ˆλ‹€. λ˜ν•œ μ‹€μ œ μ‚¬μš©ν•˜λŠ” νŒŒμΌμ—μ„œ λ§Žμ€ λ‘œμ§μ„ κ΅¬ν˜„ν•˜λ‹€λ³΄λ‹ˆ μ½”λ“œμ˜ 가독성을 ν•΄μΉ˜λŠ” 것 κ°™μ•„μ„œ λͺ¨λ“ˆν™”λ₯Ό μ§„ν–‰ν•œ 후에 ν”„λ‘œμ νŠΈλ₯Ό μ‹œμž‘ν•˜λ©΄ 쒋을 것 κ°™μ•„μ„œ APIλ₯Ό λͺ¨λ“ˆν™” ν•΄λ³΄μ•˜μŠ΅λ‹ˆλ‹€!

λ¦¬νŒ©ν† λ§_API λͺ¨λ“ˆ 뢄리 λΈ”λ‘œκ·Έλ₯Ό μ°Έκ³ ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

λͺ¨λ“ˆ λΆ„λ¦¬ν•œ 폴더 ꡬ쑰

πŸ“¦ apis
┣ πŸ“‚ api(api μš”μ²­/응닡 μ½”λ“œλ§Œ μž‘μ„±)
┃ β”— πŸ“œuser.js
┣ πŸ“‚ services(데이터λ₯Ό μ •μ œν•΄μ£ΌλŠ” ν•¨μˆ˜ λͺ¨μŒ)
┃ β”— πŸ“œuser.js
┣ πŸ“‚ utils(μΈμŠ€ν„΄μŠ€/곡톡 ν•¨μˆ˜ μž‘μ„±)
┃ β”— πŸ“œinstance.js
β”— πŸ“œ index.js(λͺ¨λ“ˆ export)

Axios μΈμŠ€ν„΄μŠ€

μΈμŠ€ν„΄μŠ€ 생성

.create(config)λ©”μ„œλ“œλ₯Ό μ΄μš©ν•΄μ„œ μ‚¬μš©μž μ •μ˜ ꡬ성을 μ‚¬μš©ν•˜λŠ” axios μΈμŠ€ν„΄μŠ€λ₯Ό 생성할 수 μžˆμŠ΅λ‹ˆλ‹€.

const instance = axios.create({
  baseURL: 'https://some-domain.com/api/',
  timeout: 1000,
  headers: {'X-Custom-Header': 'foobar'}
});

μΈμŠ€ν„΄μŠ€μ™€ config

μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“€κΈ° μœ„ν•΄μ„œ create μ•ˆμ— 넣을 configλ₯Ό μ§€μ •ν•΄μ£Όμ—‡μŠ΅λ‹ˆλ‹€. μ—¬κΈ°μ„œ autorization에 토큰값을 λ„£κΈ° μœ„ν•΄ μΈμŠ€ν„΄μŠ€λ₯Ό baseInstance와 authInstance둜 λΆ„λ¦¬ν•˜μ—¬ μž‘μ—…ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

import axios from 'axios'
import { getItem } from '@utils/storage'

const baseAPI = (url, options) => {
  return axios.create({ baseURL: url, ...options })
}

const authAPI = (url, options) => {
  const token = getItem('jwt_token')
  return axios.create({
    baseURL: url,
    headers: {
      Authorization: `bearer ${token}`,
    },
    ...options,
  })
}

export const baseInstance = baseAPI(REACT_APP_BASE_URL)
export const authInstance = authAPI(REACT_APP_BASE_URL)

api μ—°κ²°

κΈ°λ³Έ apiλŠ” baseInstanceλ₯Ό μ‚¬μš©ν•˜κ³  인증이 ν•„μš”ν•œ apiλŠ” authInstanceλ₯Ό μ‚¬μš©ν•΄μ„œ κ΅¬ν˜„ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

// login κ΅¬ν˜„
import { baseInstance, authInstance } from '../utils/instance'

const login = async (userInfo) => {
  try {
    const { data } = await baseInstance.post(`/login`, userInfo)
    return data
  } catch (error) {
    console.error(error)
  }
}

servicesμ—μ„œ 데이터 κ°’ μ •μ œν•΄μ£ΌκΈ°

api μš”μ²­ μ‹œ λ°›μ•„μ˜¨ λͺ¨λ“  λ°μ΄ν„°μ—μ„œ ν•„μš”ν•œ λ°μ΄ν„°λ§Œ λ”°λ‘œ μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„œ μ μš©μ‹œμΌ°μŠ΅λ‹ˆλ‹€. μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” 데이터듀을 ν•„ν„°λ§ν•΄μ„œ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

// κ°„λ‹¨ν•˜κ²Œ 보여주기 μœ„ν•΄μ„œ μž‘μ„±ν•΄λ³΄μ•˜μŠ΅λ‹ˆλ‹€.
const getCleanUserInfo = (rawUserInfo) => {
  const { email, userName, notifications } = rawUserInfo.user
  return {
    email,
    name: userName,
    notifications: notifications.length > 0 ? notifications : null,
  }
}

export { getCleanUserInfo }

axios μ‚¬μš©ν•˜κΈ°

μ‚¬μš©ν•˜λŠ” 방법은 μ‹€μ œλ‘œ μ‚¬μš©ν•˜λŠ” μ„œλΉ„μŠ€λ§ˆλ‹€ λ‹€λ₯΄κ²Œ 적용될 것 κ°™μŠ΅λ‹ˆλ‹€. μ €λŠ” μ•„λž˜μ²˜λŸΌ κ΅¬ν˜„ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

const [userInfo, setUserInfo] = useState({})

const login = async () => {
	const rawUserData = await login(userInfo)
    const userData = await getCleanUserInfo(rawUserData)
    setUserInfo(userData)
}

πŸ₯² μ•„μ‰¬μš΄μ 

axios interceptorλ₯Ό λ°˜μ˜ν•˜μ§€ λͺ»ν–ˆλ˜ 점이 μ•„μ‰½μŠ΅λ‹ˆλ‹€. λͺ¨λ“  apiμ—μ„œ try, catch문을 μ‚¬μš©ν•΄μ•Όν–ˆκ³  μ—λŸ¬ 처리λ₯Ό λ””ν…ŒμΌν•˜κ²Œ μ²˜λ¦¬ν–ˆμœΌλ©΄ μ’‹μ•˜μ„ 것 κ°™μŠ΅λ‹ˆλ‹€! λ¦¬νŒ©ν† λ§ ν•  λ•Œ κΌ­ axios 인터셉터λ₯Ό μ‚¬μš©ν•΄μ„œ μ μš©ν•΄λ³΄λ €κ³  ν•©λ‹ˆλ‹€.

πŸ”₯ λŠλ‚€μ 

axios μΈμŠ€ν„΄μŠ€λΌλŠ” 것을 처음 μ•Œμ•˜μŠ΅λ‹ˆλ‹€. ν™•μ‹€νžˆ μ μš©ν•˜κ³  λ‚˜μ„œ μž¬μ‚¬μš©μ„±μ΄ μ—„μ²­ ν™•μž₯λ˜μ—ˆλ‹€κ³  λŠκΌˆμŠ΅λ‹ˆλ‹€. μ΄λž˜μ„œ λ‹€λ“€ λͺ¨λ“ˆν™”λ₯Ό ν•˜κ³  μž¬μ‚¬μš©μ„±μ„ κ³ λ―Όν•˜λŠ” κ΅¬λ‚˜ κΉ¨λ‹¬μ•˜μŠ΅λ‹ˆλ‹€.

이 뿐만 μ•„λ‹ˆλΌ λ‹€λ₯Έ μ»΄ν¬λ„ŒνŠΈμ—μ„œλ„ 계속 μž¬μ‚¬μš©μ„ κ³ λ―Όν•˜κ²Œ 되고 μ–΄λ–»κ²Œ λͺ¨λ“ˆ 뢄리λ₯Ό 할지 μƒκ°ν•˜κ²Œ λ˜λŠ” 것 κ°™μŠ΅λ‹ˆλ‹€!! μ½”λ“œλ₯Ό μž‘μ„±ν•˜λŠ” 것에 λ§Žμ€ 생각을 ν•˜κ²Œ λ˜λ©΄μ„œ 쒋은 μͺ½μœΌλ‘œ λ°œμ „ν•˜κ³  μžˆλ‹€κ³  λŠκ»΄μ§‘λ‹ˆλ‹€!!

μ§€κΈˆκΉŒμ§€ ν”„λ‘œμ νŠΈ 기간이 μ ˆλ°˜μ •λ„ μ§€λ‚¬λŠ”λ° λ„ˆλ¬΄λ„ˆλ¬΄ 잘 따라와주고 μžˆλŠ” νŒ€μ›λ“€κ»˜ λ„ˆλ¬΄ κ°μ‚¬ν•˜λ‹€κ³  λ§μ”€λ“œλ¦¬κ³  μ‹Άμ–΄μš”! 첫 νŒ€μž₯μ΄μ–΄μ„œ λ§Žμ€ 뢀담이 μžˆμ—ˆλŠ”λ° 항상 μœ„λ‘œ, 응원, 칭찬을 ν•΄μ£Όμ…”μ„œ 더 μ•žμœΌλ‘œ λ‚˜μ•„κ°€κ³  μžˆλŠ” 것 κ°™μŠ΅λ‹ˆλ‹€! 1μ£ΌμΌμ΄λΌλŠ” μ‹œκ°„λ™μ•ˆ 정말 많이 배우고 μžˆμŠ΅λ‹ˆλ‹€. κ°μ‚¬ν•©λ‹ˆλ‹€.

πŸ‘Ώ λ’€λŠ¦κ²Œ 발견된 API 문제점

μ‚¬μš©μž 정보λ₯Ό contextμ—μ„œ 가져와 μˆ˜μ •μ„ ν•  λ•Œ apiμ—μ„œ ν† ν°μœΌλ‘œ μ‚¬μš©μž 정보 μš”μ²­μ„ ν•˜λ©΄ 계속 κ·Έ 전에 μ ‘μ†ν–ˆλ˜ μ‚¬μš©μžμ˜ 정보λ₯Ό κ°€μ Έμ˜€λŠ” μ΄μŠˆκ°€ λ°œμƒν•˜μ˜€μŠ΅λ‹ˆλ‹€.

μš”μ²­μ„ ν•  λ•Œλ§ˆλ‹€ μƒˆ μœ μ €μ˜ 토큰을 κ°€μ Έμ™€μ„œ 인증 μš”μ²­μ„ ν•œ 후에 μœ μ €λ₯Ό 전역에 μ €μž₯ν•˜κ³  정보λ₯Ό κ°€μ Έμ™€μ„œ μ‚¬μš©ν•œλ‹€κ³  μƒκ°ν•˜μ˜€κΈ° λ•Œλ¬Έμ— 도무지 이해가 가지 μ•Šμ•˜μŠ΅λ‹ˆλ‹€. λ”°λΌμ„œ contextλΆ€ν„°, λ Œλ”λ§ μ „/ν›„λ‘œ λͺ¨λ‘ 디버깅을 ν•΄λ³΄μ•˜μ§€λ§Œ 이 둜직 μ•ˆμ—μ„œλŠ” 원인을 찾을 수 μ—†μ—ˆμŠ΅λ‹ˆλ‹€.

κ²°κ΅­, apiκΉŒμ§€ 디버깅을 돌렀본 후에 원인을 λ°œκ²¬ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

μœ„μ˜ authAPIλ₯Ό λ‹€μ‹œ μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

const authAPI = (url, options) => {
  const token = getItem('jwt_token')
  return axios.create({
    baseURL: url,
    headers: {
      Authorization: `bearer ${token}`,
    },
    ...options,
  })
}

이 λ‘œμ§μ—μ„œ 치λͺ…적인 단점이 μ‘΄μž¬ν•©λ‹ˆλ‹€..γ… γ…  그것은 토큰을 μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“œλŠ” μ‹œμ μ— κ°€μ Έμ˜¨λ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€.

λ”°λΌμ„œ μΈμŠ€ν„΄μŠ€κ°€ λ§Œλ“€μ–΄μ§ˆ λ•Œμ—λŠ” 아직 μ‚¬μš©μž 인증 μš”μ²­μ„ ν•˜μ§€ μ•Šμ•˜κΈ° λ•Œλ¬Έμ— 이런 상황이 λ§Œλ“€μ–΄μ‘ŒμŠ΅λ‹ˆλ‹€. μ €λŠ” 이 상황을 ν•΄κ²°ν•˜κΈ° μœ„ν•΄μ„œ κ²°κ΅­ μœ„μ— μ•„μ‰¬μš΄ 점에 μ μ—ˆλ˜ 뢀뢄을 μ—¬κΈ°μ„œ μ μš©ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

πŸ˜ƒ axios interceptor의 적용

κΈ°λ³Έ url둜 μš°μ„  μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“€μ—ˆμŠ΅λ‹ˆλ‹€.


const authAPI = (url, options) => {  
  const instance = axios.create({ baseURL: url, ...options })
  interceptors(instance)
  return instance
}

μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“€κ³  interceptor에 instanceλ₯Ό μ „λ‹¬μ‹œμΌ°μŠ΅λ‹ˆλ‹€. κ·Έ 후에 μ•„λž˜μ™€κ°™μ΄ 토큰을 APλ₯Ό μš”μ²­ ν•  λ•Œ localstorageμ—μ„œ 가져와 μ μš©μ‹œν‚¬ 수 μžˆλ„λ‘ κ΅¬ν˜„ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

import { getItem } from '@utils/storage'

export const interceptors = (instance) => {
  instance.interceptors.request.use(
    (config) => {
      const token = getItem('jwt_token')

      config.headers = {
        authorization: token ? `bearer ${token}` : null,
      }
      return config
    },
    (error) => Promise.reject(error.response)
  )
  return instance
}

μ—λŸ¬ 처리λ₯Ό ν•˜λŠ” μΈν„°μ…‰ν„°λŠ” μ•„λ‹ˆμ§€λ§Œ 쀑간에 μš”μ²­μ„ κ°€λ‘œμ±„μ„œ μ²˜λ¦¬ν•˜λŠ” 방법을 μ μš©μ‹œμΌ°μŠ΅λ‹ˆλ‹€.
μ΄λ ‡κ²Œ 큰 이슈λ₯Ό λ§Œλ‚¬κ³  디버깅을 ν•˜λŠ” 방법도 λ°°μš°λŠ” κ²½ν—˜μ΄ λ˜μ—ˆμŠ΅λ‹ˆλ‹€. 이 μ—λŸ¬λ§Œ 무렀 ν•˜λ£¨λ₯Ό λ„˜κ²Œ ν—€λ§€μ„œ μ§„μ§œ νž˜λ“€μ—ˆμ§€λ§Œ ν•΄κ²° ν›„μ—λŠ” 정말 λΏŒλ“―ν•˜μ˜€μŠ΅λ‹ˆλ‹€.
μ—λŸ¬ 처리λ₯Ό μΈν„°μ…‰ν„°λ‘œ μ μš©ν•œ 것은 μ•„λ‹ˆμ—ˆμ§€λ§Œ μ΄λ ‡κ²ŒλΌλ„ μ‚¬μš©ν•˜κ²Œ λ˜μ–΄ 쒋은 κ²½ν—˜μ΄ λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

ref

λ¦¬νŒ°ν† λ§_API λͺ¨λ“ˆ 뢄리
Axios μΈμŠ€ν„΄μŠ€
ν”„λ‘œκ·Έλž˜λ¨ΈμŠ€ λ°λΈŒμ½”μŠ€

profile
맀 μˆœκ°„ μ„±μž₯ν•˜λŠ” κ°œλ°œμžκ°€ 되렀고 λ…Έλ ₯ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

1개의 λŒ“κΈ€

comment-user-thumbnail
2023λ…„ 1μ›” 31일

ν˜Ήμ‹œ μ—λŸ¬μ— λŒ€ν•œ 데이터 μ •μ œλŠ” μ–΄λ–»κ²Œ ν•˜μ…¨λ‚˜μš”?

λ‹΅κΈ€ 달기