- app.tsx에서 세팅해주기
모든페이지에서 사용하도록 하려면
글로벌 스테이트에 저장해야함!https://velog.io/@zitto/TIL-0411-글로벌-스테이트
- src/components/commons/apollo/index.tsx
import { atom } from "recoil"; export const accessTokenState = atom({ key: "accessTokenState", default: "", });
- login 폴더의 index.tsx 화면 그려주기
global state(Recoil)에 accessToken저장하기
import { gql, useMutation } from "@apollo/client"; import { Modal } from "antd"; import { useRouter } from "next/router"; import { ChangeEvent, useState } from "react"; import { useRecoilState } from "recoil"; import { accessTokenState } from "../../../src/commons/stores"; import type { IMutation, IMutationLoginUserArgs, } from "../../../src/commons/types/generated/types"; const LOGIN_USER = gql` mutation loginUser($email: String!, $password: String!) { # 언더페칭문제해결하기 위해 loginUser(email: $email, password: $password) { accessToken } } `; export default function LoginPage(): JSX.Element { const router = useRouter(); const [, setAccessToken] = useRecoilState(accessTokenState); //초기값 줄 수 없음 //구조분해할당으로 잠시 앞은 비우기!(쉼표는있어야함) const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [loginUser] = useMutation< Pick<IMutation, "loginUser">, IMutationLoginUserArgs (LOGIN_USER); const onChangeEmail = (event: ChangeEvent<HTMLInputElement>): void => { setEmail(event.currentTarget.value); }; const onChangePassword = (event: ChangeEvent<HTMLInputElement>): void => { setPassword(event.currentTarget.value); }; const onClickLogin = async (): Promise<void> => { // 1. login뮤테이션 날려서 accessToken 받아오기 과정 try { const result = await loginUser({ variables: { email, password, }, }); const accessToken = result.data?.loginUser.accessToken; console.log(accessToken); //받아와서 로컬이나 변수에 저장하기! 보안에 안전한 변수에 저장 (새로고침하면 사라짐) // 2. 받아온 accessToken을 모든컴포넌트에서 적용할 수 있도록 글로벌스테이트에 저장 recoil! if (accessToken === undefined) { alert("login failed, try again!!"); return; } //내려오면 accessToken 무조건 있다라는 것! setAccessToken(accessToken); void router.push("/section23/23-01-login-success"); // 3. 로그인 성공페이지로 이동하기 } catch (error) { // Modal.error({content : error.message}) // const error = new Error("error!!!"); if (error instanceof Error) alert(error.message); } }; return ( <> 이메일 : <input type="text" onChange={onChangeEmail} /> 비밀번호 : <input type="password" onChange={onChangePassword} /> <button onClick={onClickLogin}>Login</button> </> ); }
loginUser같은 경우엔 타입 추론이 불가능 하다.
이럴때앞에는 받아올 타입을 , 뒤에는 보내줄 타입 적어준다.입력하는 방법으로는 IMutation에서 로그인 유저의 타입을 Pick!
pick과 같이 쓰이는 타입 omit과 patial
→ pick, omit, patial 은 모두 utility 타입
- omit : 특정 데이터를 빼고 모두 데려와주는 역할
- patial은 모두 가지고 오지만 모두 ?를 붙여서 가지고 오는 것
catch부분에 에러가 난다면❗️
→ 타입스크립트의 버전에 따라 에러가 날수도 있고, 안날수도 있다.에러가 뜬다면 아래 코드를 적용해 볼 것
...코드 catch(error){ if(error instanceof Error)Modal.error({content : error.message}) }
- loginsuccess 폴더의 index.tsx
import { gql, useQuery } from "@apollo/client"; import { IQuery } from "../../../src/commons/types/generated/types"; const FETCH_USER_LOGGED_IN = gql` query { fetchUserLoggedIn { email name } } `; export default function LoginSuccessPage(): JSX.Element { const { data } = useQuery<Pick<IQuery, "fetchUserLoggedIn">>(FETCH_USER_LOGGED_IN); return <>WELCOME {data?.fetchUserLoggedIn.name}</>; }
- global state에 저장된 accessToken header 연동하기
import { ApolloClient, ApolloLink, ApolloProvider, InMemoryCache, } from "@apollo/client"; import { createUploadLink } from "apollo-upload-client"; import { useRecoilState } from "recoil"; import { accessTokenState } from "../../../commons/stores"; const GLOBAL_STATE = new InMemoryCache(); //아래쪽에서 리렌더되든말든 얘는 계속 유지됨 interface IApolloSettingProps { children: JSX.Element; } export default function ApolloSetting(props: IApolloSettingProps): JSX.Element { const [accessToken] = useRecoilState(accessTokenState); //뒤에 있는 콤마는 지워도 됨, 앞에는 안됨 const uploadLink = createUploadLink({ uri: "http://backend-practice.codebootcamp.co.kr/graphql", headers: { Authorization: `Bearer ${accessToken}`, }, }); //기본세팅 const client = new ApolloClient({ link: ApolloLink.from([uploadLink]), //확장방식 설정 (이미지주고받기 기능 추가됨) cache: GLOBAL_STATE, //컴퓨터의 메모리(rem)에 백엔드에서 받아온 데이터 모두 임시저장! usequery로 받아온 데이터 여기있음 ,모든페이지에서 접근할 수 있는globalstate }); return ( <> <div>Hi</div> <ApolloProvider client={client}> {props.children} </ApolloProvider> <div>Hi</div> </> ); }
uploadLink에 저 토큰을 추가했다는 것은 ,
모든 컴포넌트에서 로그인 관련 토큰을 추가해서 보내주도록 만든 것이다.로그인을 안한사람은 토큰 자리에 토큰이 없고, 로그인을 한 사람은 토큰을 가지고 있다.
그럼 저 토큰자리에 어떻게 토큰을 채워 줄 수 있을까?
global state를 통해 Recoil에 accessToken을 저장해두고 사용하고싶은 컴포넌트 전체를 감싸주고 필요한 곳에서 꺼내서 사용하는 것!