본문 바로가기
FrontEnd/React.js

[React.js] 제로초 슬랙 클론코딩 - 1. 회원가입/로그인 페이지 (SWR)

by Chaedie 2022. 12. 12.
728x90

강의를 통해 배운 점

SWR을 활용해서 data fetching을 효과적으로 관리할 수 있다. 비동기 처리 뿐 아니라 다양한 방식으로 전역상태를 관리할 수 있는데, 이름 자체가 stale-while-revalidate 인것처럼 api 호출을 효율적으로 관리해주는 것이 가장 큰 장점인것 같다.

SWR이 좋다는 이야기도 많이 들었고, 캐싱 기능 등이 강력하고 Next.js와 함께 사용하면 엄청 좋다는 이야기를 많이 들었는데, 이번 강의를 통해 사용해볼수 있어서 좋았다.

로그인, 회원가입, 로그아웃 시 유저정보가 있는지에 따라 Redirecting을 해주는 방법을 배웠다. 이전에 프로젝트를 진행할 땐 스토리지에 토큰이 있는지 없는지를 확인해서 리다이렉션을 진행했는데 이번엔 유저 정보를 받으면 로그인이 되어있다는 것으로 처리하여 리다이렉션을 진행했다.

또한 쿠키를 통해 세션ID를 받아 로그인이 유지되도록 백엔드코드가 지정되어 있어서 처음으로 쿠키방식의 인증을 경험해볼수 있었다.


Rerendering에 대해

리렌더링이라는 것 자체가 화면을 새로 그렸다는 말이 아니다. 리렌더링이 될때 깜빡거리는건 해당 부분이 달라졌는지 확인했다는 의미이다.


useInput

ReturnTypes type을 지정해주어 가독성을 높혀줄수 있다.

제너릭을 사용해서 다양한 인풋에 대비할 수 있다. (any를 넣는것에 비해 Input에 따라 다른 타입도 자동으로 지정되도록 할 수 있다는 장점이 있다.)

e.target.value는 React.ChangeEvent<HTMLInputElement>에 의해 string으로 고정되어 있다. 하지만 위에선 T로 저너릭으로 지정되어 있어 타입이 충돌이 일어 난다. 따라서 as unknown as T라는 놈으로 as any처럼 에러를 피할수있다. 다만 any에 비해 안정적이라고 한다. (물론 HTMLInput Change는 무조건 string이 들어 오는거라 사실 크게 상관 없을듯하다. 강좌에선 as unknown as T로 하면 된다고 했지만 as any로 해도 무조건 string으로 들어오니까 사실 상관없지 않나? 하는 생각이 있다.)

import { ChangeEvent, Dispatch, SetStateAction, useCallback, useState } from 'react';

type ReturnTypes<T = any> = [T, (e: ChangeEvent<HTMLInputElement>) => void, Dispatch<SetStateAction<T>>];

const useInput = <T = any>(initialData: T): ReturnTypes => {
  const [value, setValue] = useState(initialData);
  const handler = useCallback((e: ChangeEvent<HTMLInputElement>) => {
    setValue(e.target.value as unknown as T);
  }, []);

  return [value, handler, setValue];
};

export default useInput;

CORS 에러 발생 시

origin이 다른 백엔드 서버로 요청을 하기 때문에 CORS가 발생한다. 이 때 해결 방법은 크게 2가지다.

  1. 백엔드 코드에서 CORS설정 하기
  2. PROXY설정 하기

여기서 2) Proxy설정은 webpack dev server에서 설정할 수 있다. 둘다 로컬호스트일 경우에 이 방법으로 해결이 가능하다. 개발환경에서 CORS에러를 피하기 위해 Proxy를 통해 CORS를 우회하는것이기에, 배포환경에선 이렇게 CORS를 우회하진 않는다.

// @webpack.config.ts
devServer: {
    historyApiFallback: true, // react router
    port: 3090,
    devMiddleware: { publicPath: '/dist/' },
    static: { directory: path.resolve(__dirname) },
    proxy: {
      '/api/': {
        target: '<http://localhost:3095>',
        changeOrigin: true,
      },
    },
  },

CORS 이외에 하나 더

백-프 주소가 다르면 CORS이외에 쿠키전달이 안된다는 문제점도 생긴다. 로그인이 안된다는 이야기다. 이 때 withcredential: true를 해주면 해결이 된다.


비동기 요청 하기 전에…

비동기 요청 후 결과값 등을 setState로 넣는 경우, 요청 이전에 state를 초기화 시켜주는 것이 좋다. 이렇게 하면 여러번 요청이 갈 떄 이전 요청결과값이 남아있는 에러를 방지할 수 있다.

비동기 요청은 보통 아래와 같이 3단계로 이루어져있다.

  1. 로딩, 2) 성공, 3) 에러
const onSubmit = useCallback(
  (e: FormEvent) => {
    e.preventDefault();
    if (!mismatchError) {
      setSignUpError('');
      setSignUpSuccess(false);
      console.log('서버로 회원가입하기');
      axios
        .post('<http://localhost:3095/api/users>', {
          email,
          nickname,
          password,
        })
        .then((res) => {
          console.log(res);
          setSignUpSuccess(true);
        })
        .catch((error) => {
          console.log(error.response);
          setSignUpError(error.response.data);
        })
        .finally(() => {});
    }
  },
  [email, nickname, password, passwordCheck, mismatchError],
);

[SWR] 로그인 후 로그인 정보를 가지고 있으려면

로그인 후 프론트에서 로그인 정보를 가지고 있으려면 전역 상태관리가 필요하다. 슬리액 프로젝트에선 SWR을 사용한다.

SWR은 Get요청 데이터를 관리하는 라이브러리이다. 따라서 POST Login으로 로그인을 한 뒤에 get요청을 한번 더 해서 데이터 패칭을 할 거다.

SWR과 리액트 쿼리 사용법은 굉장히 비슷하기에 하나만 배워두면 다른 하나는 덤이다.

useSWR을 사용해 (url, fetcher)를 인자로 넣어준다.

const LogIn = () => {
  const { data, error } = useSWR('<http://localhost:3095/api/users>', fetcher);

fetcher는 어떤 실행을 할것인가? 하는 함수인데, get요청을 넣어주면 간단하게 data, error를 받을 수 있다.

import axios from 'axios';

const fetcher = (url: string) =>
  axios
    .get(url, {
      withCredentials: true,
    })
    .then((res) => res.data);

export default fetcher;

SWR은 vercell에서 제공하는 전역상태관리 라이브러리인데, 탭을 이동하고 오면 패쳐를 실행하는 등 다양한 기능들이 있다. 이는 따로 공부해볼만하다.

SWR은 패칭을 자주 하기 때문에 직접 설정을 통해 조정할 필요가 있다.

[SWR] Mutate : 비동기 실행 후 특정 시점에 useSWR data를 변경하기

revalidate라는 함수가 deprecated되었다. 따라서 mutate()를 사용하고, options 객체에 revalidate:false를 통해 한번 더 재검증 (API 재 호출)을 끄면 재호출이 되지 않게 제어가 가능한다.

const { data, error, mutate } = useSWR('<http://localhost:3095/api/users>', fetcher, {
    dedupingInterval: 100000,
  });
const onSubmit = useCallback(
    (e: FormEvent) => {
      e.preventDefault();
      console.log('서버로 로그인하기');
      axios
        .post(
          '<http://localhost:3095/api/users/login>',
          {
            email,
            password,
          },
          {
            withCredentials: true,
          },
        )
        .then((res) => {
          console.log(res);
          mutate(res.data, { revalidate: false });
					//* OPTIMISTIC UI (낙관적 UI) (revalidate: true일 경우)
          // ex) 인스타 좋아요를 눌렀는데, 실상은 DB에서 좋아요 처리되는게 시간이 걸리겠지만,
          // UI적으로는 좋아요가 성공할것으로 낙관하고 UI상으로 좋아요처리가 완료된것처럼 보여준다.
          // 반댓말) Pessimistic UI (별 다른 처리 없이 코딩하면 비관적 UI)
        })
        .catch((error) => {
          console.log(error.response);
        })
        .finally(() => {});
    },
    [email, password],
  );

SWR은 GET요청이 아니라도 data를 받아 오는 모든 동작에서 사용이 가능하다. 각각의 fetcher를 만들어서 자유자재로 사용하면 된다.

또한 비동기 요청 이외에 localStorage get, set과 관련한 함수를 넣어서 사용해도 된다. ⇒ 전역 데이터 저장소로 사용 가능하다.

비동기 요청만을 위해 SWR을 활용한다면 SWR을 100% 활용하지 못하는 것이다. 서버에서 주는 데이터를 그대로 SWR에 저장하지말고 다양한 fetcher를 만들어서 데이터를 가공해서 저장하며 사용하면 활용도가 높아진다.

useSWR API 명세서 : https://swr.vercel.app/ko/docs/api

mutate 공식 문서 : https://swr.vercel.app/ko/docs/mutation#global-mutate


React-router-dom @6 에서 Redirect 방법

react-router-dom@6 부터 <Redirect>에서 <Navigate>로 변경되었다. 따라서 자동 로그인 처리는 Navigate 컴포넌트로 처리해주면된다.

if (data === undefined) {
  return <div>로딩중...</div>;
}

if (data) {
  return <Navigate to={'/workspace/channel'} />;
}

이렇게 swr을 이용한 패치 data가 있으면 return Navigate를 하면 redirecting 된다.

댓글