Frontend : React Native/Axios

[React Native] 서버 요청 정리(1) (비동기, Axios, Config)

0BigLife 2022. 3. 28. 16:46
728x90

서버 요청 정리 화이팅

 여태 공부했던 서버 요청에 대한 모든 것들을 정리하려고 한다. 일단 서버와의 통신을 위해 axios를 쭉 사용해왔는데 가장 먼저 왜 'axios'인지부터 시작해서 .get/.post에 대한 구현 방법을 다루고, 두 번째 글에서는 모듈화를 위한 react-query, custom Hook 작성까지 다뤄볼 예정이다. (axios.create로 인스턴스를 만들어 간단한 모듈화 진행은 다음 링크를 타고 가면 볼 수 있다.)

  앱 개발자에게 있어 굉장히 중요한 주제들 중 하나라고 생각한 만큼 이번 글은 길어질 것 같으니 공부하시는 분들에게 필요한 개념만 쇽-쇽 찾아가길 바란다. // if(도움이 된다면...) 

 

왜 Axios 인가

 서버와의 통신을 위해 어떤 기술을 써야하는가가 독학 초반에서의 가장 큰 궁금증이었다. 처음엔 Fetch로 구현을 해보다가 Axios로 갈아타게 된 이유는 다음과 같다. 기존에 swift로 iOS 앱 개발을 할 때에는 Alamofire, Moya를 사용하여 모듈화 방법까지 공부했었는데 그 형태가 가장 유사한 것이 axios라고 판단하여 선택한 것도 크다. 또한, Axios의 자료들이 풍부하다는 것에서 그 망설임은 더욱 줄어들었다. 무턱대고 axios를 공부하면서 react-native와 친해졌으며, 모듈화까지 공부해본 현재까지도 앞으로 문제만 없다면 쭉 Axios를 활용하지 않을까 싶다.

 공식 문서에 따르면 Axios의 정의는 다음과 같다. Axios는 "Promise based HTTP client for browser and node.js"이다. 즉, Axios는 node.js와 브라우저를 위한 HTTP 통신 라이브러리인데, 가장 큰 장점은 비동기 HTTP 통신을 가능하게 해주며, 그에 대한 리턴 값을 promise 객체로 반환해주기 때문에 요청에 대한 응답(response)을 다루기 쉽다. 리턴 값은 json 타입으로 받는다. 

 이 외에도 Axios와 Fetch 간의 비교에 대한 부연설명은 하단 링크를 참고하길 바란다. 
 Axios vs Fetch : https://www.geeksforgeeks.org/difference-between-fetch-and-axios-js-for-making-http-requests/

 

Difference between Fetch and Axios.js for making http requests - GeeksforGeeks

A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.

www.geeksforgeeks.org

 


Axios 사용을 위한 개념

 Axios 요청을 보내는 방식은 이해만 한다면 굉장히 구현이 간단하고, 직접 몇 번만 구현하더라도 금방 손에 익게 된다. 하지만 '00을 할 때 이렇게 해야한다' 라고 할 때 '이러이러하기 때문에 사용해야 한다'라는 설명이 생략되면 자꾸 '그래서 이걸 왜 써야 하지?' 생각이 들면서 기능 구현이 완성되어도 찝찝하기만 하다. 대표적으로는 비동기 통신이 왜 필요하며, 어떤 게 비동기 통신인지 공부하며 이해했다. 필요성이 있기 때문에 이 기능이 효율적임을 판단하는 것인데 그 과정이 해소되어야 한다는 의미이다. (사실 이 부분 정리가 필요하여 이번 글을 작성하였다.) 비동기 통신 다음으로 궁금했던 것은 개념과 연계된 async/await, 그리고 구현할 때에는 왜 try-catch문을 써야하는가이다. 이러한 의문점들을 다음 순서대로 간단히 정리해보겠다.

 비동기, 동기 모두 통신 관련 용어이며, 동기는 직관적인 작업 과정으로 하나의 작업(task)이 마치고 결과가 나온 뒤에 그 다음 작업을 실행을 하기 때문에 순차적으로 실행되며 그렇기 때문에 다른 작업들은 blocking 된다. 반면, 비동기 작업은 작업이 마치지 않아도 그 다음 작업이 시작되기 때문에 동시에 여러 요청들을 수행할 수 있고, 기다리는 과정에서도 다른 함수를 호출할 수 있다. 

P..Promise 객체..

 이러한 비동기 작업이 언제 끝나는지 알려주는 객체가 바로 Promise 객체이다. Promise 객체는 대기, 이행, 거부 세 가지 상태를 가지며 이는 각각 작업 완료 전, 작업 완료 후, 에러 발생으로 나뉜다.(그래서 try-catch를 쓰는 것인가?) 이 Promise 객체를, 즉 비동기를 더욱 쉽게 사용할 수 있도록 해주는 문법(ES2017, ES8)이 async/await이다.! 이 문법은 간단히 말하면 함수 앞부분에 async 키워드를 넣어주고, 함수 내부에서는 Promise 앞부분에 await를 써줌으로써 그에 대한 리턴 값으로 Promise를 반환한다.

async function TestFunction() {
    try{
      const response = await fetch('...url...');
        // task..
    } catch (error) {
        // handle error
    }
}

  이렇게 훑어보면 try-catch문을 쓰는 것은 필수적인 것처럼 보인다. try-catch 문의 가장 큰 필요성은 '에러 핸들링'에 있다. 요청에 대한 성공은 try안에서 비동기 작업을 위한 await를 언급해주면서 사용할 수 있고, 예외 상황에 대한 처리를 catch 안에서 잡아주어 예상된 에러/예상되지 못한 에러 처리를 할 수 있다. 
  한 문장으로 정리하면 ! 서버와의 통신을 위해서는 비동기 작업이 필요하고, 이를 위해 async/await 문법을 사용하고 에러 핸들링을 위하여 try-catch문을 작성한다 ! 

 

Axios 적용

 자! 이제 기다리고 기다리던 Axios 구현 파트이다. 우선 코드 작성 전, 설치 방법인데 이는 간단하기 때문에 링크로만 남기겠다. 
https://github.com/axios/axios

 

GitHub - axios/axios: Promise based HTTP client for the browser and node.js

Promise based HTTP client for the browser and node.js - GitHub - axios/axios: Promise based HTTP client for the browser and node.js

github.com

구현 방식은 현재 github에 작성해둔 코드를 가져왔다. 

.get 방식

useEffect(() => {
    const getData = async () => {
      const response = await axios.get<{data: string}>(
        `${url}/get`,
        {
          headers: {
            authorization: `Bearer ${accessToken}`,
          },
        },
      );
    };
    getData();
  }, []);

 해당 방식은 get요청을 보낸 형태이며, 앞서 설명한 것처럼 비동기 통신을 위해 getData() 함수에 async를 명시해주고 함수 내부에는 await를 써줌으로써 Promise 객체를 반환하도록 해주었다. 선언된 response 변수는 axios 라이브러리의 get 방식을 채택하고 타입스크립트를 사용하기 때문에 받아오는 타입형도 generic을 넣어주었다. get method는 첫 번째 인자로 요청하는 path를 넣어주며, 두 번째 인자로는 header를 같이 넣어 요청할 수 있다. 
 첫 번째 인자 : url 변수 + 백엔드에서 넘겨줄 api 주소
 두 번째 인자 : redux store에 넣어둔 accessToken을 헤더에 포함시켜 전송

 추가적으로, useEffect 는 async를 허용하지 않기 때문에 내부에 선언과 호출을 동시에 해줘야 한다. 

이로써, 위 코드는 해당 코드가 들어간 화면이 나올 시 서버로부터 특정 데이터를 가져오게 된다.

 

.post 방식

 다음은 입력받은 데이터를 서버로 보내주는 post 방식이다.

const LoginTapped = useCallback(async () => {
    if (loading) {
      return;
    }
    // 입력된 텍스트 검토하는 로직..
    try {
      setLoading(true);
      const response = await axios.post(`${Config.API_URL}/login`, {
        email,
        password,
      });
      //가져온 데이터 store에 넣어주는 코드..
      //가져온 토큰은 보안을 위해 Encrypted-Storage에 넣어주는 코드..
    } catch (error) {
      const errorResponse = (error as AxiosError).response;
      if (errorResponse) {
        Alert.alert(errorResponse.data.message);
      }
    } finally {
      setLoading(false);
    }
  }, [email, loading, password]);

 LoginTapped 함수는 로그인 화면에서 사용자가 이메일, 비밀번호를 입력하고 나서 그 정보를 서버로 보내기 위하여 버튼을 누를 때 작동하는 함수다. get에 대해 작성한 코드처럼 axios와 관련된 로직만 봐주길 바란다. get 방식과 동일하고 path를 넣어주고 두 번째 인자로는 전송해야 하는 사용자의 정보를 파라미터로 넣었다. 이 경우에는, 토큰을 보낼 필요가 없다. 
 서버 요청에 대한 에러 핸들링을 위해 try-catch-finally 문을 사용했고, 받아온 error는 axios에서 지원해주는 AxiosError 형으로 바꿔서 if문과 Alert함수로 화면에 띄워주었다. 

 추가적으로, 중요한 점을 배웠는데 useState으로 [loading, setLoading]을 제어하는 것이 얼마나 중요한지 배웠다. 요청에 대한 잠시 대기하는 순간을 loading 변수로 잡아주고 이 경우에는 로딩 화면 또는 스플래쉬 스크린을 뛰워줄 것이다!

<LoginButton
  style={{
    backgroundColor: isActive ? 'gray' : 'lightgray',
        }}
  disabled={!isActive || loading}
  onPress={onSubmit}>
    <ButtonText>Login</ButtonText>
</LoginButton>

 

 그리고 사용자가 버튼을 연타할 시 로그인이 동시다발적으로 발생하는 것을 고려하여 disabled에 loading도 넣어줘야한다.!

 

react-native-config 

 이 라이브러리는 프로젝트가 여러 환경에서 빌드 또는 런될 때를 대비하여 필요한 라이브러리이다. 개발 환경과 운영(production) 환경을 포함한 다양한 환경으로 분리하여 작성될 수 있으나, 필자는 간단한 연습을 위해 .env 파일만 작성하여 사용했다. 설치 및 사용 방법은 공식 링크에 들어가면 쉽게 사용해볼 수 있다. 
link : https://github.com/luggit/react-native-config 

 

GitHub - luggit/react-native-config: Bring some 12 factor love to your mobile apps!

Bring some 12 factor love to your mobile apps! Contribute to luggit/react-native-config development by creating an account on GitHub.

github.com

 

 

.env file

코드에는 라이브러리로부터 Config를 불러와서 axios 요청에서 path 설정을 위한 변수 선언에 넣어주었다.

const Function = useCallback(async () => {
    try {
      await axios.post(`${Config.API_URL}/complete`, {
        headers: {
          authorization: `Bearer ${accessToken}`,
        },
      });
      //추가 로직..
    } catch (error) {
      const errorResponse = (error as AxiosError).response;
      if (errorResponse) {
        Alert.alert('알림', errorResponse.data.message);
      }
    }
  }, [// ... //]);

 

 이상으로 필자가 Axios를 사용함에 있어서 궁금했던 점들과 구현한 코드를 간단히 정리해보았습니다. 두 번째 글에서는 이러한 요청들이 많아졌을 때 어떻게 하면 좀 더 효율적으로 처리할지에 대해 고민한 것들을 정리해보려고 합니다. 다소 부족한 부분과 심지어 틀린 부분도 있을 것이라 우려가 되는데 이러한 부분은 댓글로 남겨주면 너무너무 도움이 될 것 같습니다 : ) 

728x90