refreshToken은 accessToken을 1시간마다 자동으로 인가해준다.
accessToken을 만드는 과정에서 accessToken과 똑같지만, 2주~1개월 정도의 기간을 유지하는 refreshToken을 생성한다.
로컬/세션 스토리지의 경우 보안에 취약하기 때문에 토큰을 취급할 때에는 사용하지 않고, 쿠키에 RefreshToken을 담아서 저장해준다.
accessToken은 payload로 전달, refreshToekn은 브라우저 cookie에 저장
-> 브라우저 cookie에 refreshToken이 자동으로 저장됨
-> accessToken은 변수에 직접 저장
쿠키에 저장하는 이유 - 쿠키의 secure / httpOnly 옵션
쿠키라고 해서 매우 안전한 것은 아니지만, 로컬/세션 스토리지와는 다르게 secure, httpOnly 등의 옵션을 설정할 수 있다.
- httpOnly : 브라우저에서 Javascript를 이용해 쿠키에 접근할 수 없고, 통신으로만 해당 데이터를 주고받을 수 있다. (javascript로 확인할 수 없음)
- secure : https 통신 시에만 해당 쿠키를 받아올 수 있다.
fetchUser시 header에 accessToken을 보내고, 성공 / 실패 복호화
-> 1시간이 지나면 토큰 만료
AccessToken 만료 후 인가 요청
브라우저에서는 토큰 만료로 로그인이 실패했을 때, 에러를 잡는 if문을 작성해서 실행시켜주어야 함
RefreshToken으로 accessToken 재발급 요청 -> 변수에 저장
쿠키에 있는 refreshToken을 restoreAccessToken API를 통해 백엔드로 보내서 refreshToken 인가를 함
-> 인가(복호화) 성공 시 새로운 accessToken을 만들어 다시 브라우저로 돌려줌
브라우저에서 변수에 저장된 accessToken을 새로운 accessToken으로 바꾸어 주어야 함
(state에 재저장)
실패 쿼리 재시도 (accessToken값을 변경해서 fetchUser를 다시 시도함)
유저 입장에서는 실패하고 토큰을 재발급 받고, 실패 쿼리를 재시도하는 과정이 보이지 않음
=> 조용한 인증 (SilentAuth)
AuthService / ResourceService
- AuthService : 인증 과정
- ResourceService : 인증 후 데이터를 받아오는 과정
백엔드에서 각 API마다 폴더를 만들어, 한 API에서 에러 발생 시 다른 곳에 영향이 가지 않도록 구현해준다. (MSA)
소셜 로그인 - Open Authentication (OA)
구글 / 네이버 / 카카오 등 자회사의 DB를 바탕으로 AuthService를 제공하며, 사용자가 소셜 로그인을 할 수 있다.
libraries 폴더로 이동해서 getAccessToken.ts 라는 이름의 파일을 생성한다.
mutation이 안되므로 graphql-request에서 graphqlClient
를 import해서 accessToken을 재발급 받는 코드를 별도 함수로 분리하여 입력해준다.
uploadLink의 요청을 보낼때 uri 경로를 http에서 https로 바꾸고, 민감한 정보 포함을 승인한다는 뜻의 credentials: “include”
옵션을 추가해주어야 한다.
만일, credentials: “include” 이 없다면 refreshToken을 쿠키에 못담을 뿐 만아니라 쿠키에 담겨있는것들도 백엔드로 전송이 되지 않는다.
refreshToken을 사용하기 위해서는 graphQL 요청을 보내야 하는데, errorLink를 생성하는 코드는 ApolloProvider 바깥에 있기 때문에 useQuery나 useApolloClient등을 이용해 graphQL 요청을 보낼 수 없다.
이러한 문제를 해결하기 위해서 graphql-request
라이브러리를 설치해준다.
graphql-resquest 설치
yarn add graphql-request
apollosetting 부분에서 에러를 잡아주는 link 작성
import { onError } from "@apollo/client/link/error";
ApolloClient link에 errorLink
도 연결해준다. 이때, 링크 연결 순서에 따라 적용되니까 순서에 유의!
stores 폴더에 accessToken을 재발급 받는 글로벌 함수를 만들어준다.
const aaa = useRecoilValueLoadable(restoreAccessTokenLoadable); // api 통합
새로고침 시 토큰을 유지하도록 해준다.
app에 적용된 useEffect에 getAccessToken 함수를 실행하고,
권한분기로 권한이 필요한 페이지에서 새로고침 시 getAccessToken이 두 번 실행되서 비효율적이다.
-> 두개를 그룹핑해서 사용해준다.
-> recoil에 있는 setAccessToken state 재사용
페이지네이션에서 마지막에 클릭한 페이지를 요청하면, 이전에 클릭해서 요청한 것은 취소되고 리렌더가 되면 안된다.
예를 들어, 3번페이지를 요청했다가 빠르게 5번 페이지를 요청했을 경우 3번 페이지 요청을 취소 후 5번 페이지를 보내줘야 하는데, 백엔드에서는 3번페이지를 보여주게 된다.
이런경우에는 3번 페이지 요청을 취소해야 한다. 그렇지 않으면 사용자의 불편한 경험을 초해 할 수 있기 때문이다.
Promise는 await해야 하므로 이런 방법이 불가능하다.
Observable
은 연속적인 페이지 클릭 / 연속적인 검색어 변경 시 사용한다.
-> 반응형 프로그래밍
함수형 프로그래밍 / 반응형 프로그래밍
- 함수형 프로그래밍 : 함수들을 체이닝해서 코드 작성 가능([].filter().map().forEach())
- 반응형 프로그래밍 : 연속적인 기능들에 반응해서 사용하는 것
-> reactive X (rx.js) / zen-observable : 반응형 프로그래밍 라이브러리
apollo에서는 observable 기반, zen-observable 사용
zen-observable 설치
yarn add zen-observable
yarn add --dev @types/zen-observable
fromPromise
onError 라는 함수는 return타입으로 Observable타입을 받는다.
그러나, 우리가 리턴해주는 값은 promise이기 때문에 Observable타입으로 바꿔줄 도구가 필요하다.
fromPromise 는 이때 사용하며, promise타입을 Observable타입으로 바꿔주는 도구이다.