Skip to content

Custom Hook을 이용한 토큰 재발급 ⏳

오지훈 edited this page Dec 20, 2020 · 2 revisions

바쁘신 분들을 위한 3줄 요약!

  • 토큰 만료 시 자동으로 토큰을 재발급받고 이전의 요청을 재요청하는 useCustomQuery 커스텀 훅 구현
  • 함수 내부에서 훅을 사용하기 위한 refetch 함수를 커스텀한 callQuery 함수 구현
  • 커스텀 훅에서 서버에서 받은 에러를 핸들링하여 상황에 맞는 동작을 하도록 구현

UseCustomQuery

useCustomQuery 는 apollo hooks의 useQuery에 토큰 재발급 요청과 이전의 요청을 재요청하는 기능을 자동화 시킨 커스텀 훅입니다.

useCustomQuery에서 눈여겨 봐야 할 것은 queryResult callQuery errorHandler 입니다.

하나씩 설명해보도록 하겠습니다.

첫 번째로 queryResult의 동작 방식에에 대해 설명하겠습니다.

  1. 기존의 useQuery의 사용법과 같이 인자로 query와 option을 받습니다.
  2. useCustomQuery 내부에서 useQuery함수에 인자로 받은 query를 넣어 함수를 실행합니다.
  3. useQuery를 통해 서버에 쿼리 요청이 가고, 토큰이 유효하다면 queryResult에 응답받은 데이터가 들어갑니다.
  4. 토큰이 유효하지 않다면 onError에서 errorHandler함수가 실행되어 토큰 재발급 로직을 수행하고 이전의 요청을 재요청하게 됩니다. (onError의 errorHandler함수는 아래에서 자세히 설명하겠습니다)
  5. errorHandler에서 리턴된 값(reQueryResult)은 토큰을 재발급받고 이전의 요청을 재요청하여 받은 정상적인 응답 데이터입니다.
  6. onStartCompleted함수는 정상적인 응답 데이터를 인자로 받고 useQuery의 onCompleted옵션(useQuery 정상적으로 동작했을 때 실행되는 함수)에 데이터를 넣습니다.

자세한 코드가 궁금하시다면 여기❗️를 클릭해주세요 😊

두 번째로 callQuery에 대해 설명하겠습니다.

먼저 오더 조회 버튼을 클릭하는 상황을 예시로 들어보겠습니다. 조회 버튼을 클릭하면 서버에서 데이터를 가져와 클라이언트로 응답하게 됩니다.

Apollo, Graphql을 쓰고 있는 우리는 apollo hooks의 useQuery를 통해 데이터를 조회할 수 있습니다.

그럼 버튼 클릭 시 발생하는 onClick 함수에서 useQuery를 사용하면 되지 않나요?

위처럼 구현하면 에러가 발생합니다. 그 이유는 react Hooks의 규칙 때문입니다.

react는 훅의 명확한 순서를 보장하기 위해 두 가지 규칙을 따르고 있습니다. useQuery도 Hooks이기 때문에 이러한 규칙을 따라야 합니다.

최상위에서 호출해야 하는 Hooks를 함수 내부에서 호출하고 있으므로 에러가 발생합니다.

  1. 컴포넌트의 최상위(Top Level)에서만 Hook을 호출해야 한다.

  2. 오직 React 함수 내에서 Hook을 호출해야 한다.

그럼 어떻게 해야 하나요?

저희가 해결한 첫 번째 방법은 useQuery의 refetch 함수를 이용하는 것입니다. refetch함수는 useQuery의 리턴 값으로 제공되는 함수로 요청한 쿼리를 다시 요청할 수 있는 함수입니다.

useQuery는 최상위 컴포넌트에 선언하고 결과로 refetch를 받아 onClick 함수 내부에 refetch함수를 통해 데이터를 가져올 수 있었습니다.

하지만 refetch함수만으론 토큰 재발급을 자동화하기 어려웠습니다

그래서 저희가 생각해낸 방법은 refetch 함수를 한 번 더 wrapping하여 토큰 재발급을 자동으로 해주는 로직이 추가된 새로운 refetch 함수를 만드는 것입니다.

그게 바로 이제부터 설명할 callQuery함수입니다.

callQuery 의 동작방식은 다음과 같습니다.

  1. callQuery 호출 시 refetch 함수를 통해 쿼리를 실행시킵니다.
  2. 토큰이 유효하다면 응답받은 정상적인 데이터 onStartCompleted 함수 인자로 넣어 함수를 호출합니다.
  3. 만약 토큰이 유효하지 않다면 catch 구문 안의 errorHandler 함수를 이용하여 토큰을 재발급받고 이전의 쿼리 요청을 재요청합니다.
  4. 재요청 후 받은 정상적인 데이터를 onStartCompleted 함수 인자로 넣어 함수를 호출합니다.
  5. 재요청 시 받은 정상적인 데이터를 return합니다.

자세한 코드가 궁금하시다면 여기❗️를 클릭해주세요 😊

위에서 errorHandler함수를 통해 토큰을 재발급받고 이전 요청을 다시 보낸다는데 어떻게 동작하는 건가요?

errorHandler 의 동작 방식에 대해 설명하겠습니다.

  1. 첫 번째 인자로 받은 error의 상태 코드를 확인하여 401일 경우 토큰 재발급을 수행, 아닐 경우 해당 error 발생시킵니다.

  2. 두 번째 인자로 받은 apolloClient를 이용하여 토큰 재발급 Mutaion을 발생시킵니다.

  3. 세 번째 인자로 받은 request(이전에 했던 요청의 refetch함수)를 이용하여 이전의 요청을 재요청합니다.

  4. 재요청으로 받은 정상적인 데이터를 리턴합니다.

자세한 코드가 궁금하시다면 여기❗️를 클릭해주세요 😊

결론

서버에 요청을 보낼 때마다 서버의 응답으로 토큰이 만료되었는지, 정상적으로 데이터가 들어왔는지, 또 다른 에러가 발생했는지에 따른 모든 처리를 해줘야 하는 번거로움을 useCustomQuery 커스텀 훅을 통해 해결하였습니다.

useCustomQuery를 통해 요청마다 처리해야 하는 중복되는 로직을 없앨 수 있었고, 토큰을 재발급을 받고 이전의 요청을 보내는 흐름을 useCustomQuery 위임하고 오로지 기능구현에만 집중할 수 있었습니다.

Clone this wiki locally