과거 프로그래밍 자료들/Javascript&typescript

[TS] Axios 타입 분석(get, post, error)

평부 2022. 9. 21. 13:37

 

 

 

출처 : https://github.com/ZeroCho/ts-all-in-one

 

GitHub - ZeroCho/ts-all-in-one

Contribute to ZeroCho/ts-all-in-one development by creating an account on GitHub.

github.com

 

 

* axios

- axios = fetch + 여러가지 기능(XHMLHttpRequest)

- 서버 응답, 요청 시 많이 사용

 

 

* axios 사용방법(크게 3가지)

1. new axios();
2. axios(); //함수처럼
3. axios.get()

 

 

* import 방식여부 index.d.ts 파일 내용 따라 다름

//index.d.ts 파일 내에서
//마지막 줄에 export defaul axios 같은 게 있는지 찾아보기(가장 중요함)
import axios from 'axios'

// common.js 모듈의 경우
import axios = require('axios')

// common.js 모듈이어도 es.internal인 경우
 import * as axios from 'axios'
//(* as 제거 가능) 
import axios from 'axios'

 

[index.d.ts]

export interface AxiosInstance extends Axios { //interface도 무조건 객체가 아니라 함수가 있으면 함수로 가능
  (config: AxiosRequestConfig): AxiosPromise; //함수
  (url: string, config?: AxiosRequestConfig): AxiosPromise; //함수
}

export interface AxiosStatic extends AxiosInstance { //얘는 객체 => 함수를 객체가 상속받음
  create(config?: AxiosRequestConfig): AxiosInstance;
  Cancel: CancelStatic;
  CancelToken: CancelTokenStatic;
  Axios: typeof Axios;
  readonly VERSION: string;
  isCancel(value: any): boolean;
  all<T>(values: Array<T | Promise<T>>): Promise<T[]>;
  spread<T, R>(callback: (...args: T[]) => R): (array: T[]) => R;
  isAxiosError(payload: any): payload is AxiosError;
}

// export interface AxiosStatic extends AxiosInstance 예시
const a = () => {}

//함수 안에 함수.속성으로 또 다른 함수를 넣음
a.create = () => {};
a.isAxiosError = () => {};
a.z = '123';

a();
a.create()
a.isAxiosError()

export class Axios { //클래스면서 함수면서 객체
  constructor(config?: AxiosRequestConfig);
  defaults: AxiosDefaults;
  interceptors: {
    request: AxiosInterceptorManager<AxiosRequestConfig>;
    response: AxiosInterceptorManager<AxiosResponse>;
  };
  getUri(config?: AxiosRequestConfig): string;
  request<T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig<D>): Promise<R>;
  get<T = any, R = AxiosResponse<T>, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
  delete<T = any, R = AxiosResponse<T>, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
  head<T = any, R = AxiosResponse<T>, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
  options<T = any, R = AxiosResponse<T>, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
  post<T = any, R = AxiosResponse<T>, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<R>;
  put<T = any, R = AxiosResponse<T>, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<R>;
  patch<T = any, R = AxiosResponse<T>, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<R>;
  postForm<T = any, R = AxiosResponse<T>, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<R>;
  putForm<T = any, R = AxiosResponse<T>, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<R>;
  patchForm<T = any, R = AxiosResponse<T>, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<R>;
}

//axios.get()
 get<T = any, R = AxiosResponse<T>, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
// url이 첫 번째 매개변수 
// R(AxiosRespone)

export interface AxiosResponse<T = any, D = any>  {
  data: T; //데이터의 타입을 T로 넣음
  status: number;
  statusText: string;
  headers: AxiosResponseHeaders;
  config: AxiosRequestConfig<D>;
  request?: any;
  
  
   isAxiosError(payload: any): payload is AxiosError;
export class AxiosError<T = unknown, D = any> extends Error {
  constructor(
    message?: string,
    code?: string,
    config?: AxiosRequestConfig<D>,
    request?: any,
    response?: AxiosResponse<T, D>
  );

  config: AxiosRequestConfig<D>;
  code?: string;
  request?: any;
  response?: AxiosResponse<T, D>;
  isAxiosError: boolean;
  status?: string;
  toJSON: () => object;
  static readonly ERR_FR_TOO_MANY_REDIRECTS = "ERR_FR_TOO_MANY_REDIRECTS";
  static readonly ERR_BAD_OPTION_VALUE = "ERR_BAD_OPTION_VALUE";
  static readonly ERR_BAD_OPTION = "ERR_BAD_OPTION";
  static readonly ERR_NETWORK = "ERR_NETWORK";
  static readonly ERR_DEPRECATED = "ERR_DEPRECATED";
  static readonly ERR_BAD_RESPONSE = "ERR_BAD_RESPONSE";
  static readonly ERR_BAD_REQUEST = "ERR_BAD_REQUEST";
  static readonly ERR_CANCELED = "ERR_CANCELED";
  static readonly ECONNABORTED = "ECONNABORTED";
  static readonly ETIMEDOUT = "ETIMEDOUT";
}

}

 

 

* TS 파일 실행 시 JS로 변환해야 함 => 매번 변경하기 번거로움

 npm i -g ts-node //ts-node -g로 설치 시 명령어로 이용 가능
 
//일반적인 터미널에서 사용하는 경우
ts-node axios.ts
//powershell에서 사용하는 경우
npx ts-node axios.ts

 

 

* [Axios.get] 해당 코드는 문제 없는 가? 

▶ 실제로 오류는 안 나지만 문제 있는 코드

▶ 왜 문제가 있는가? = 모든 타입이 any로 되어 있음

▶ 주소는 더미데이터 확인하는 API 주소 : https://jsonplaceholder.typicode.com/guide/

 

JSONPlaceholder - Guide

Guide Below you'll find examples using Fetch API but you can JSONPlaceholder with any other language. You can copy paste the code in your browser console to quickly test JSONPlaceholder. Getting a resource fetch('https://jsonplaceholder.typicode.com/posts/

jsonplaceholder.typicode.com

(async () => {
  try {//현재 모든 타입이 any로 되어 있음
    const response = await axios.get(
      "https://jsonplaceholder.typicode.com/posts/1"
    );
    console.log(response.data);
    console.log(response.data.userId);
    console.log(response.data.id);
    console.log(response.data.title);
  } catch (error) {}
})();

 

 

*  [Axios.get]  수정한 코드

import axios from "axios";

//type, interface 둘 중 어느 것으로 해도 상관 없음
type Post = {userId: number, id: number, title: string, body: string}
//interface Post {userId: number, id: number, title: string, body: string} 

(async () => {//getResponse, getResponse2 둘 다 상관 없음
  try {
    // get<T = any, R = AxiosResponse<T>, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
    const getResponse = await axios.get<Post>(
      "https://jsonplaceholder.typicode.com/posts/1"
    );
    const getResponse2 = await axios.get<Post, AxiosResponse<Post>>(
      "https://jsonplaceholder.typicode.com/posts/1"
    );
    console.log(response); //const response: AxiosResponse<Post, any>
    console.log(response.data.id) //(property) AxiosResponse<Post, any>.data: Post
  } catch (error) {}
})();

export interface AxiosResponse<T = any, D = any>  {
  data: T; //이 코드에서 data는 Post
  status: number;
  statusText: string;
  headers: AxiosResponseHeaders;
  config: AxiosRequestConfig<D>;
  request?: any;
}

 

 

*  [Axios.post]  

▶ 주로 postResponse를 많이 쓰기 때문에 이 방법으로 진행할 것

//index.d.ts
//post<T = any, R = AxiosResponse<T>, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<R>;
//첫 번째 자리는 url, 두 번째 자리는 data(당장 쓰지 않는데 왜 받는지?)

//postResponse에서는 data 타입 D는 당장 쓰지 않으나 postResponse2에서는 data 타입 D를 받음
const postResponse = await axios.post('https://jsonplaceholder.typicode.com/posts', {
        title: 'foo',
        body: 'bar',
        userId: 1
    })
    
const postResponse2 = await axios({
        method: "post",
        url: "https://jsonplaceholder.typicode.com/posts",
        data: {
            title: 'foo',
            body: 'bar',
            userId: 1,
        }
    })

 

 

*  [Axios.post]  type이 붙을 경우

▶ Created : AxiosResponse<T>에서 T, post 요청에 대한 응답의 데이터 타입

▶ axios.post 설명 (출처 : https://velog.io/@richard/project)

 

project | Axios post 요청

project | Axios post 요청

velog.io

import axios, {Axios, AxiosResponse} from "axios";

interface Post  {userId: number, id: number, title: string, body: string}
interface Created {} //post 요청에 대한 응답의 데이터 타입
interface Data  {title: string, body: string, userId: 1}

(async () => {
  try {//현재 모든 타입이 any로 되어 있음
    const response = await axios.get<Post, AxiosResponse<Post>>(
      "https://jsonplaceholder.typicode.com/posts/1"
    );
    // post<T = any, R = AxiosResponse<T>, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<R>;
    const response2 = await axios.post<Created, AxiosResponse<Created>, Data>('https://jsonplaceholder.typicode.com/posts', {
        title: 'foo',
        body: 'bar',
        userId: 1
    })
   console.log(response2) //const response2: AxiosResponse<Created, any>
  } catch (error) {}
})();

 

 

*  Axios Error 처리 방법

* 참고 : https://ba-gotocode131.tistory.com/219?category=987174 

 

[TS] 에러 처리법(interface, class 차이 비교)

출처 : https://github.com/ZeroCho/ts-all-in-one GitHub - ZeroCho/ts-all-in-one Contribute to ZeroCho/ts-all-in-one development by creating an account on GitHub. github.com * interface와 class를 각..

ba-gotocode131.tistory.com

▶ 타입가드 사용

import axios, {Axios, AxiosError, AxiosResponse} from "axios";

interface Post  {userId: number, id: number, title: string, body: string}
interface Created {}
interface Data  {title: string, body: string, userId: 1}
(async () => {
  try {
  		...
    })
  } catch (error) { 
  	// isAxiosError(payload: any): payload is AxiosError;
   	if(axios.isAxiosError(error)) { //커스텀 타입가드
      console.error(error.response?.data)
    }
  }
})();

 

 

*  (오류) Axios Error 처리 방법 : 메세지를 넣고 싶다면?

▶ 제네릭으로 message를 넣어도 console.error에서 기억하지 못함

import axios, {Axios, AxiosError, AxiosResponse} from "axios";

interface Post  {userId: number, id: number, title: string, body: string}
interface Created {}
interface Data  {title: string, body: string, userId: 1}
(async () => {
  try {
  		...
    })
  } catch (error) { 
  	 if(error instanceof AxiosError<{message: string}>) {
        //{message: "서버 장애입니다. 다시 시도해주세요"}
        console.error(error.response?.data.message) //response가 여전히 any가 나옴
    }
  }
})();

 

 

*  (해결법) Axios Error 처리 방법 : AxiosResponse 이용

import axios, {Axios, AxiosError, AxiosResponse} from "axios";

interface Post  {userId: number, id: number, title: string, body: string}
interface Created {}
interface Data  {title: string, body: string, userId: 1}
(async () => {
  try {
  		...
    })
  } catch (error) { 
  	 if(axios.isAxiosError(error)) {
        //{message: "서버 장애입니다. 다시 시도해주세요"}
       console.error((error.response as AxiosResponse<{message: string}>)?.data.message)
       //(property) AxiosResponse<{ message: string; }, any>.data: {message: string;}
    }
  }
})();


export interface AxiosResponse<T = any, D = any>  {
  data: T;
  status: number;
  statusText: string;
  headers: AxiosResponseHeaders;
  config: AxiosRequestConfig<D>;
  request?: any;
}