Post

원티드 프리온보딩 - 로그인 기능 구현하기 1

원티드 프리온보딩 - 로그인 기능 구현하기 1강 복습

실습 코드

로그인이란?

사용자가 시스템에 접근하거나 동작을 수행하는 것을 제어하고 기록하기 위한 컴퓨터 보안 절차

로그인이 필요한 이유와 구현을 위해 해야하는 것

1. 사용자 식별

  • 해당 페이지에 접근하는 사용자가 누구인지 구분하기 위해

2. 접근 및 동작 제어

2-1. 권한이 없는 자원에 접근하지 않는 구조 만들기

  • 관리자 전용 페이지와 데이터를 일반 유저가 접근하지 못하도록
  • github repo의 setting 버튼이 관리자를 제외하고는 보여지지 않도록 설정

2-2. 권한이 없는 자원의 존재를 모르도록 하기

  • 관리자 페이지의 주소를 일반 유저가 직접 입력했을 때 오류 페이지를 보여줌으로써 권한이 없는 페이지&데이터를 숨기기

3. 인증 정보 관리하기

  • 로그인이 계속 유지될 수 있도록 token을 쿠키, 로컬스토리지, 섹션스토리지에 저장
  • 인증 정보를 저장해두지 않는다면 장바구니에 추가할 때도 로그인을 해야하고, 장바구니로 이동할 때도 로그인을 해야함

실제 서비스를 구성하는 페이지들

로그인이 필요한 페이지

1
2
3
4
5
6
7
8
9
10
11
12
const pageWithLogin = () => {
  const { isLogged, routeToLoginPage } = useLoginState();

  // 로그인이 되지 않은 경우에는 로그인 페이지로 이동
  if (!isLogged) {
    routeToLoginPage();
    // 라우팅 되기 전 잠깐이라도 로그인이 필요한 컨텐츠를 보여주지 않도록 설정
    return <> 로그인 페이지로 이동합니다...</>;
  }

  return <div>로그인이 필요한 페이지 코드</div>;
};

로그인이 필요하지 않은 페이지

1
2
3
const pageWithoutLogin = () => {
  return <div>로그인이 필요 없는 페이지 코드</div>;
};

로그인 여부 확인 로직 모듈화 Authorization

  • 매번 로그인 여부를 확인할 코드를 작성하는 것 대신 모듈화 해서 관리
1
2
3
4
5
6
7
8
9
10
11
12
const Authorization = ({ children }) => {
  const { isLogged, routeToLoginPage } = useLoginState();

  // 로그인이 되지 않은 경우에는 로그인 페이지로 이동
  if (!isLogged) {
    routeToLoginPage();
    // 라우팅 되기 전 잠깐이라도 로그인이 필요한 컨텐츠를 보여주지 않도록 설정
    return <> 로그인 페이지로 이동합니다...</>;
  }

  return <>{children}</>;
};

로그인 화면 만들기

1. username, password를 입력받을 입력창 만들기

2. 입력한 정보를 받아올 버튼 만들기

3. 로그인 목업 API

4. 유저 정보 받아오기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import React from "react";
import { useState } from "react";

// 임의로 만든 더미
const users = [
  {
    username: "blueStragglr",
    password: "1234",
    userInfo: { name: "blueStragglr" }
  },
  { username: "hyemin", password: "1234", userInfo: { name: "hyemin" } }
];

// 임의로 만든 token
const _secret = "1234qwer!@#$";

const login = async (username, password) => {
  const user = users.find(
    (user) => user.username === username && user.password === password
  );

  return user
    ? {
        message: "SUCCESS",
        token: JSON.stringify({ username: user.username, secret: _secret })
      }
    : null;
};

const getUserInfo = async (token) => {
  const parsedToken = JSON.parse(token);

  if (!parsedToken?.secret || parsedToken.secret !== _secret) return null;

  const loggedUser = users.find((user) => {
    if (user.userInfo.name === parsedToken.user.name) return user;
  });

  return loggedUser ? loggedUser.userInfo : null;
};

const LoginWithMockAPI = () => {
  const [userInfo, setUserInfo] = useState(null);

  const loginSubmitHandler = async (event) => {
    event.preventDefault();

    const formData = new FormData(event.currentTarget);

    const loginResonse = await login(
      formData.get("username"),
      formData.get("password")
    );
    if (!loginResonse) return;

    const userInfo = await getUserInfo(loginResonse.token);
    if (!userInfo) return;

    setUserInfo(userInfo);
  };

  return (
    <div>
      <h1>Login with Mock API</h1>
      <form onSubmit={loginSubmitHandler}>
        <label className="label">
          Username:
          <input type="text" name="username" />
        </label>

        <label className="label">
          Password:
          <input type="password" name="password" />
        </label>

        <button type="submit" value="Submit">
          Submit
        </button>
      </form>
      <div>
        <h2>User info</h2>
        {JSON.stringify(userInfo)}
      </div>
    </div>
  );
};

export default LoginWithMockAPI;

form Data 받아오는 방법

1. new FormData 사용하기

  • 사용자가 입력할 때마다 렌더링이 발생하지 않음
  • 단순한 값을 받아올 때 주로 사용
  • 실시간으로 제어하기 어려움
  • .get(name)으로 값 가져와서 사용
1
2
3
4
5
6
7
8
9
10
11
12
const loginSubmitHandler = async (event) => {
  event.preventDefault();

  const formData = new FormData(event.currentTarget);

  const loginResonse = await login(
    formData.get("username"),
    formData.get("password")
  );

  // ...
};

2. useState 사용하기

  • 사용자가 입력할 때마다 렌더링이 발생
  • 실시간으로 제어할 수 있음 (특수문자, 이메일주소, 핸드폰번호 체크 등)
  • 부모컴포넌트 전체가 렌더링돼서 부담될 때는 input만 따로 컴포넌트로 만들고 input 컴포넌트 안에서 useState 사용해서 전체 렌더링 막기
    • 자식 컴포넌트로 관리하면 부모 컴포넌트는 렌더링이 되지 않고 자식 컴포넌트만 렌더링되기때문

느낀점

formData를 이용해 제출된 값을 가져올 수 있다는 점을 배웠고,
loginSubmitHandler, 로그인 함수까지는 작성을 했으나 getUserInfo 함수는 왜 사용되는지 무슨 역할을 하는지 알지 못하여 작성하지 못하였다. getUserInfo()가 무슨 역할을 하는지 따로 공부해야겠다.

This post is licensed under CC BY 4.0 by the author.