[Sleact] 슬랙 클론 코딩 - 로그인

로그인 페이지

pages/Login/index.tsx

import useInput from '@hooks/useInput';
import { Success, Form, Error, Label, Input, LinkContainer, Button, Header } from '@pages/SignUp/styles';
// import fetcher from '@utils/fetcher';
import axios from 'axios';
import React, { useCallback, useState } from 'react';
import { Link, Redirect } from 'react-router-dom';
import useSWR from 'swr';

const LogIn = () => {
  const [logInError, setLogInError] = useState(false);
  const [email, onChangeEmail] = useInput('');
  const [password, onChangePassword] = useInput('');
  const onSubmit = useCallback(
    (e) => {
      e.preventDefault();
      setLogInError(false);
      // 프록시 설정을 껐다
      axios
        .post('http://localhost:3095/api/users/login', { email, password }, { withCredentials: true })
        .then(() => {})
        .catch((error) => {
          setLogInError(error.response?.data?.statusCode === 401);
        });
    },
    [email, password],
  );

  return (
    <div id="container">
      <Header>Sleact</Header>
      <Form onSubmit={onSubmit}>
        <Label id="email-label">
          <span>이메일 주소</span>
          <div>
            <Input type="email" id="email" name="email" value={email} onChange={onChangeEmail} />
          </div>
        </Label>
        <Label id="password-label">
          <span>비밀번호</span>
          <div>
            <Input type="password" id="password" name="password" value={password} onChange={onChangePassword} />
          </div>
          {logInError && <Error>이메일과 비밀번호 조합이 일치하지 않습니다.</Error>}
        </Label>
        <Button type="submit">로그인</Button>
      </Form>
      <LinkContainer>
        아직 회원이 아니신가요?&nbsp;
        <Link to="/signup">회원가입 하러가기</Link>
      </LinkContainer>
    </div>
  );
};

export default LogIn;

로그인을 하면 다음과 같이 쿠키를 확인할 수 있다. 로그아웃을 한다는 것을 이 쿠키를 지운다는 뜻과 같다. 그렇다면 프론트엔드 입장에서 내가 로그인에 성공했다는 것을 어떻게 알 수 있을까?

로그인 요청에 성공한다면 서버에서 프론트로 내 정보를 보내주는데 이걸 저장하고 있다가 로그아웃할 때 그 정보를 버리면 될 것이다.

로그인 정보를 저장하고 있으려면 전역 데이터로 저장하고 있어야한다. 이 프로젝트에서는 리덕스를 사용하지 않고 SWR 을 사용하도록 한다.

SWR 사용하기

pages/LogIn/index.tsx

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

useSWR 자체는 아무 기능을 하지 않고 urlfetcher 함수에 넘겨준다. 이 fetcher 함수는 utils/fetcher.ts 파일을 생성해서 그 곳에서 관리하도록 한다.

utils/fetcher.ts

import axios from 'axios';

const fetcher = (url: string) => axios.get(url).then((response) => response.data);

export default fetcher;


이렇게 로그인이 성공적으로 이루어졌는데

users 요청에는 로그인 정보가 들어있지 않다. 이는 프론트 서버 주소와 백엔드 서버 주소가 다르기 때문에 쿠키가 전달되지 않기 때문이다. 프론트 서버와 백 서버가 도메인이 다르면 백 서버가 프론트 서버로 쿠키를 생성해줄 수도 없고, 프론트에서 백으로 쿠키를 보내줄 수도 없다.

이를 해결하기 위한 설정이 바로 withCredentials 라는 속성이다.
utils/fetcher.ts

import axios from 'axios';

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

export default fetcher;

pages/LogIn/index.tsx

const LogIn = () => {
  const { data, error } = useSWR('http://localhost:3095/api/users', fetcher);
  const [logInError, setLogInError] = useState(false);
  const [email, onChangeEmail] = useInput('');
  const [password, onChangePassword] = useInput('');
  const onSubmit = useCallback(
    (e) => {
      e.preventDefault();
      setLogInError(false);
      axios
        .post('http://localhost:3095/api/users/login', { email, password }, { withCredentials: true })
        .then(() => {})
        .catch((error) => {
          setLogInError(error.response?.data?.statusCode === 401);
        });
    },
    [email, password],
  );
  ...
}

get 요청에서는 두번째 자리, post 요청에서는 세번째 자리에 withCredentials 속성이 들어갈 수 있다.

그 이후에 이렇게 쿠키가 정상적으로 생성된 것을 확인할 수 있다. 쿠키는 항상 백에서 생성을 하고 프론트 브라우저에서 기억할 수 있게 만들어주고 프론트에서는 한번 기억한 쿠키를 요청마다 백으로 보내준다.

SWR 설정 살펴보기

SWR 을 사용하다가 잘못하다가 해커가 되는 수가 있다. 셀프 디도스 공격을 하는 것인데 이를 방지하기 위해 설정을 해둘 수가 있다.

우선 revalidate 를 사용하면 revalidate 할 때마다 해당 요청이 실행되게 할 수 있다. 또한 dedupingInterval 설정을 통해 요청 주기를 조절할 수 있다.

const LogIn = () => {
  const { data, error, revalidate } = useSWR('http://localhost:3095/api/users', fetcher, { dedupingInterval: 100000 });
  ...
  const onSubmit = useCallback(
    (e) => {
      e.preventDefault();
      setLogInError(false);
      axios
        .post('http://localhost:3095/api/users/login', { email, password }, { withCredentials: true })
        .then(() => {
          revalidate();
        })
        .catch((error) => {
          setLogInError(error.response?.data?.statusCode === 401);
        });
    },
    [email, password],
  );
  ...
}

더 자세한 메서드들은 SWR document에서 확인할 수 있다.

좋은 웹페이지 즐겨찾기