axios instance 그리고 async await promise

poogie·2022년 6월 16일
0

.시작하기전에

내가 만든 Spring Boot RESTapi를 클라이언트에 연결하다가 axios를 채택하기는 했으나 깔끔하게 쓸 수 있는 방법에 대해 생각해보았다.

.Axios Instance

import axios from "axios";

위 코드는 기존에 axios를 사용하기 위해 임포트하는 가장 기본적인 형태이다.
하지만 기능별로 분리하기 위해 아래와 같이 로직을 사용하면서 더 좋은 방법을 찾고싶었다.

import axios from "axios";

const ip = "http://localhost:8080/user";

export function signUp(email: string, password: string, name: string) {
	axios({
		url: ip + "/sign-up",
		method: "post",
		data: {
			"email": email,
			"password": password,
			"name": name
		}
	});
}

굉장히 아쉬운점이 많은 코드라고 생각된다.
일단 ip를 문자열을 매번 적는 방법이 아닌 상수로 분리한것, 기능별로 분리한것...
1차원 적으로 생각했을때 의도는 좋았다.
하지만 api를 더욱만들어 같은 url로 접근하는 api가 10개 또는 100개가 된다고 생각하면 여전히 불필요한 재사용코드라는 생각도 든다.

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

그래서 찾은 것이 위 처럼 사용하는 Axios Instance 이다.

위처럼 사용했을때에는 다음과 같은 사용이 가능하다.

const axios: AxiosInstance = Axios.create({
    baseURL: "http://localhost:8080/user",
});

export function signUp(email: string, password: string, name: string): {
    return axios.post("/sign-up", {
            email: email,
            password: password,
            name: name,
	});
}

일단 거슬렸던 ip상수를 baseURL을 통해서 아예 없애버릴 수 있다.
그리고 일단 러프하게 토이프로젝트의 틀을 잡고있어서 적용하지 않았지만 다양한 커스텀을 통해 좀더 고차원적으로 axios를 사용할 수 있다.

.Promise

기본적인 axios틀은 잡혔지만 아직 붙여야할 살이 많다.
그중 하나가 반환값의 타입정의 이다.
일단 typescript를 사용하고 있기때문에 리턴값의 타입을 선언해주지않으면 정상적인 사용이 불가능하고 만약 js를 사용해서 가능하다하더라도 type safe하지 못하다.

일단 typescript로 아까 예시로 들었던 함수에 axios 반환값의 타입을 정의해보자.

export interface UserType {
    readonly id: number;
    readonly email: string;
    readonly password: string;
    readonly name: string;
    readonly isDeleted: string;
    readonly createDate: Date;
    readonly updateDate: Date;
}

일단 interface를 통해 리턴값들의 타입을 하나로 정의한다.
이제 이걸 typescirpt문법을 통해서 리턴값을 정의해주면 된다.

export async function signUp(
    email: string,
    password: string,
    name: string,
): Promise<UserType> {
    return await axios
        .post("/sign-up", {
            email: email,
            password: password,
            name: name,
        })
        .then((res) => res.data);
}

아래 사진은 axios의 코드로 보면 알 수 있지만 내리는 값은 무조건 promise 타입이다.

그래서 위 코드와 같이 Promise로 interface를 한번 감싸주면 타입선언이 끝난다.

참고로 then부분은 response를 받은 후의 로직인데 위코드 처럼 작성하면 response의 data값을 짧고 명료하게 내려줄수있다.

.async await이란

async await에 대해 언급하고 마무리하려고 한다.
왜 axios 커스텀과 함께 묶어서 글을 올렸을까 이해못할 수 있는데 axios를 커스텀하기 위해서 구글링하던중 대부분의 블로그에서 찾았던 문법이다.

일단 async await은 기존에 있었던 promise를 통한 비동기 로직의 단점을 보완하기 위해 개발된 새 비동기 문법이다.

앞선 코드에서도 나왔지만 promise의 단점중 하나는 then()이다.
then을 통해 promise가 뱉어낸 결과를 사용하는 promise로직 등의 callback, then 지옥에 빠지는 것이었는데 이 코드가 아래 사진처럼 굉장히 난잡하다.

사실 이렇게까지 콜백지옥에 빠져본적은 경험이 없지만 그 심각성이 보인다.
둘다 비동기 처리인지라 성능에 큰차이가 있을지는 모르겠지만 가독성에는 엄청난 차이를 보여줄것 같다.

.async awiat 적용

앞서 async await이 생긴 이유를 설명하였지만 사실 내가 사용하는 이유는 다르다.
아래 코드를 한번 확인해보자.

export async function signUp(
    email: string,
    password: string,
    name: string,
): Promise<UserType> {
    return await axios
        .post("/sign-up", {
            email: email,
            password: password,
            name: name,
        })
        .then((res) => res.data);
}

솔직히 이 코드를 적용하면서 promise를 내려주는데 왜 굳이 async await을 또 적용하는지 의문이 생겼다.
그래서 구글링을 해보았으나 의문을 해결해줄 글을 찾지 못하였고 추리해본 결과는 다음과 같다.
function에 async await을 달아놓으면 함수 자체가 비동기로 동작할 것이라는 건데 axios에 접근하는 함수를 연달아 불러올때 앞서 호출의 결과를 기다리지 않고 다음 함수 호출이 동작될 것이다.
사실 1차원적인 비동기의 존재 이유이나 충분히 사용이유가 될 수 있다고 판단하여 적용하였다.

.마무리

로직을 효율과 가독성을 고려하며 작업하는 것은 이번 프로젝트가 처음이라 구글링 해야할 것도 많고 부족한 점도 많다.
하지만 함수에 async await을 왜 적용하는지 찾지못했던 것처럼 해결하지 못하는 부분도 많다.

앞으로 더욱 코드의 효율과 가독성처럼 퀄리티에 신경쓰며 검색스킬을 발전시켜보도록한다.

profile
불안한 개발자

0개의 댓글