[TIL 0412] 로그인과 Recoil 연결

zitto·2023년 4월 12일
0

TIL

목록 보기
44/77
post-thumbnail

[실습]

  • 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!

💡 tip

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을 저장해두고 사용하고싶은 컴포넌트 전체를 감싸주고 필요한 곳에서 꺼내서 사용하는 것!

profile
JUST DO WHATEVER

0개의 댓글