꽤 여러번 투두리스트를 만들어봤지지만 axios 통신 로직은 항상 함수로만 작성했다.
원티드에서 강의를 듣고 클래스에 대해 고민하게 되었다.
클래스는 객체 형태로 state를 저장할 수 있기 때문에 state를 가진 모듈이라면 클래스로 작성하는 것이 좋다고 한다.
로그인/회원가입을 구현하는 로직은 url과 token을 가지고 있기 때문에 클래스로 작성을 하기로 결정했다.
이번에 본격적으로 구글링을 하는 연습을 하기 위해서 영어로 된 문서도 적극적으로 참고했다.
import axios, { AxiosInstance, AxiosRequestConfig } from 'axios'
import { getLocalStorageToken } from '../store/localStorage'
declare module 'axios' {
type AxiosRequest<T = any> = Promise<T>
} // 0
abstract class HttpClient { // 1
protected readonly instance: AxiosInstance
public constructor(baseURL: string) { // 2
this.instance = axios.create({
baseURL,
})
this._initializeRequestInterceptor() // 3
}
private _initializeRequestInterceptor = () => { // 4
this.instance.interceptors.request.use(this._handleRequest)
}
private _handleRequest = (config: AxiosRequestConfig | any) => {
const accessToken = getLocalStorageToken()
if (accessToken && config.headers) {
config.headers.Authorization = `Bearer ${accessToken}`
}
return config
}
}
export default HttpClient
타입스크립트를 사용하고 있기 때문에 AxiosRequest
가 무슨일을 하는지 알려줘야 한다. 이렇게 하면 response를 순수 data로 사용할때 타입스크립트가 에러를 내지 않는다고 한다. 사실 이부분은 이해가 잘 안가서 공부를 해봐야 할 부분이다.
우선, HttpClient라는 추상 클래스를 만들어 instance를 생성할 수 없게 만들어주었다. instance를 생성할 클래스는 기능별로 따로 만들었기 때문이다.
axios-instance
를 생성하는 constructor이다. constructor로부터 base URL을 얻을 수 있다. axios.create
는 protected 멤버로 설정된 instance에 저장되어 외부 클래스에서 사용할 수 없게 했다. 하지만 상속받은 클래스에서는 접근할 수 있다.
데이터를 요청하기 전 토큰이 있는지 확인해야 하기 때문에 axios의 interceptor
메소드를 활용할 것이다. _
는 private 메소드라고 암시하는 뜻이다.
interceptor
메소드는 _handleRequest
라는 콜백함수를 가진다. axios 요청시 로컬스토리지에 있는 값을 꺼내서 헤더에 Authorization
키를 설정할 수 있다.
import { AxiosResponse } from 'axios'
import { SIGNIN_URL, SIGNUP_URL } from '../../constants/constants'
import HttpClient from '../httpClient'
import { UserParam } from '../../types/auth/UserInterface'
class AuthAPI extends HttpClient {
public constructor() {
super('https://pre-onboarding-selection-task.shop')
this._initializeResponseInterceptor()
}
private _initializeResponseInterceptor = () => {
this.instance.interceptors.response.use(this._handleResponse, this._handleError)
}
private _handleResponse = (response: AxiosResponse) => {
const responseCode = response.status
switch (responseCode) {
case 200:
alert('로그인되었습니다.')
break
case 201:
alert('회원가입에 성공했습니다. 로그인해주세요.')
break
default:
alert('로그인/회원가입 요청이 거절되었습니다. 네트워크를 확인하거나 관리자에게 문의해주세요.')
}
return response
}
protected _handleError = (error: any) => {
const { response: errorResponse } = error
const errorCode = errorResponse.status
switch (errorCode) {
case 400:
alert('동일한 이메일이 이미 존재합니다.')
break
case 401:
alert('아이디와 비밀번호를 확인해주세요.')
break
case 404:
alert('해당 사용자가 존재하지 않습니다. 회원가입을 진행해주세요.')
break
default:
alert('로그인/회원가입 요청이 거절되었습니다. 네트워크를 확인하거나 관리자에게 문의해주세요.')
}
}
public SignIn = (data: UserParam) => this.instance.post(SIGNIN_URL, data)
public SignUp = (data: UserParam) => this.instance.post(SIGNUP_URL, data)
}
const authApi = new AuthAPI()
export { authApi }