[React] 로그인 기능 구현하기

hoonie·2020년 11월 27일
3
post-thumbnail

안녕하세요. 이번 시간에는 이전 포스팅 글에서 생성한 로컬API를 이용하여 fetch 통신과 함께 로그인 기능을 구현해보겠습니다.

우선 아래와 같이 로그인 폼 레이아웃을 만들어줍니다.

로그인폼 레이아웃 만들기



import React from "react";
import styled from "styled-components";

const Container = styled.div`
  margin-top: 100px;
  padding: 20px;
`;

const Input = styled.input`
  position: relative;
  overflow: hidden;
  width: 100%;
  height: 40px;
  margin: 0 0 8px;
  padding: 5px 39px 5px 11px;
  border: solid 1px #dadada;
  background: #fff;
  box-sizing: border-box;
`;

const Button = styled.div`
  font-size: 18px;
  font-weight: 700;
  line-height: 49px;
  display: block;
  width: 100%;
  height: 49px;
  margin: 16px 0 7px;
  cursor: pointer;
  text-align: center;
  color: #fff;
  border: none;
  border-radius: 0;
  background-color: #03c75a;
  ${({ disabled }) =>
    disabled &&
    `
    background-color: #efefef;
  `}
`;
//아디 비번 값 받기
//값없으면 disabled
function LoginForm() {
  return (
    <Container>
      <Input id="id" name="id" placeholder="아이디를 입력해주세요" />
      <Input
        id="password"
        name="password"
        type="password"
        placeholder="비밀번호를 입력해주세요"
      />
      <Button>로그인</Button>
    </Container>
  );
}

export default LoginForm;


DB가 있는 가짜 서버 만들기

우선 가상의 db를 만들기 위한 db.json 을 만들고 안에 user의 객체들을 만듭니다


{
  "users": [
    {
      "id": "react_hoon",
      "password": "1234",
      "name": "훈훈한훈이"
    }
  ]
}

이런 데이터 형식을 db.json에 만들고 해당 데이터를 불러야하는 로컬 API 서버를 만듭니다
잘모르시겠다면 https://velog.io/@hoon_dev/Json-API%EB%A5%BC-%EB%B0%9B%EA%B8%B0-%EC%9C%84%ED%95%9C-%EB%A1%9C%EC%BB%AC%EC%84%9C%EB%B2%84-%EB%A7%8C%EB%93%A4%EA%B8%B0 을 참고해주세요.

만드신후 해당 주소로 들어가면 이런식으로 json이 출력됩니다.

로그인 서버 통신하여 데이터 받고 처리하기

//동기식 방식 ( async await 사용!!!!!)
export const fetchLogin = async ({ id, password }) => {
  const response = await fetch("http://localhost:8888/users");

  if (response.ok) {
      //서버통신이 성공적으로 이루어지면 users에 json값 대입
    const users = await response.json();

    //users안 객체들을 순회하면서 그 객체들의 id값과 form 컴포넌트에서 받음 account의 id값과 비교
    //서로 일치하는 것만 user에 대입
    const user = users.find((user) => user.id === id);
    //일치하는 user가 없거나, 비밀번호가 틀리면 해당 에러 생성
    if (!user || user.password !== password) {
      throw new Error("아이디 또는 비밀번호가 일치하지 않습니다.");
    }

    //모든게 일치하면 그 user 정보 return -> 이 return값이 form 컴포넌트 내 fetchLogin 함수 값으로 출력되는것
    //form 컴포넌트에서 setUser값에 넣어야함
    return user;
  }

  //서버 통신이 안이루어졌을떄
  throw new Error("서버 통신이 원할하지 않습니다.");
};

여기서 주의해야할점은 동기식으로 통신하여한다는 점입니다.


동기, 비동기 통신이란?

프론트사이드 처리 방법이 동기와 비동기라는 방식으로 크게 두가지로 나뉩니다

동기는 해당 함수의 처리가 안끝나면 다음 코드들을 실행하지 않습니다(순차적으로 실행)

하지만, 비동기는 유도리있게(?) 순서가 딱 정해져있지않고 현재 처리하고 있는게 굳이 안끝나도 다음 일을 처리 할 수 있으면 처리를 합니다.

여기서 로그인 처리는 우선 로그인 서버 통신이 우선이고 그 일이 먼저 진행되어야 다음꺼를 진행할 수 있기때문에 컴퓨터한테 무조건 이거 먼저 처리하고 다음일 진행해! 라는 명령어를 주는 것입니다.

동기,비동기 참고자료

https://poiemaweb.com/js-async


위 처럼 fetchLogin이라는 동기식 통신 함수를 만드셨다면, 로그인 버튼을 눌렀을때 이 함수를 사용하여 통신해서 원하는 값을 만들어내야합니다.


import React, { useState } from "react";
import styled from "styled-components";
import { useUserContext } from "./user-context";
import { fetchLogin } from "./service";
import { useHistory } from "react-router-dom";

const Container = styled.div`
  margin-top: 100px;
  padding: 20px;
`;

const Input = styled.input`
  position: relative;
  overflow: hidden;
  width: 100%;
  height: 40px;
  margin: 0 0 8px;
  padding: 5px 39px 5px 11px;
  border: solid 1px #dadada;
  background: #fff;
  box-sizing: border-box;
`;

const Button = styled.div`
  font-size: 18px;
  font-weight: 700;
  line-height: 49px;
  display: block;
  width: 100%;
  height: 49px;
  margin: 16px 0 7px;
  cursor: pointer;
  text-align: center;
  color: #fff;
  border: none;
  border-radius: 0;
  background-color: #03c75a;
`;

function LoginForm() {
//글로벌 전역 상태값 setUser를 받아옴
//로그인이 성공적으로 이루어지면 user에 상태값을 넣어줘야지 나중에 다른 컴포넌트에서도 user값을 이용하여 다른 것들을 할 수 있음
  const { setUser } = useUserContext();

  //url 이동을 위한 useHistory
  const history = useHistory();

  //input에서 입력한 아이디와 비밀번호 정보를 담기위한 state
  const [account, setAccount] = useState({
    id: "",
    password: "",
  });

  //input에 입력하면 자동적으로 account state값 변경
  const onChangeAccount = (e) => {
      //...을 이용하여 account의 복사본을 만들고
      //input에 지정한 네임 속성에 해당 value 값을 넣어 오버라이딩!
      //console.log(account)를 찍어보고 입력한 값들이 account에 출력되면 성공!!
    setAccount({
      ...account,
      [e.target.name]: e.target.value,
    });
  };

  //동기식으로 로그인정보를 통신하여 출력
  const onSubmitAccount = async () => {
    try {
      const user = await fetchLogin(account);

      //성공하면 해당 user 아이디 패스워드값 셋팅
      setUser(user);
      //성공하면 해당 url로 이동(main페이지로)
      history.replace("/");
    } catch (error) {

        //실패하면 throw new Error("") 값 출력
      window.alert(error);
    }
  };
  return (
    <Container>
      <Input
        id="id"
        name="id"
        placeholder="아이디를 입력해주세요"
        onChange={onChangeAccount}
      />
      <Input
        id="password"
        name="password"
        type="password"
        placeholder="비밀번호를 입력해주세요"
        onChange={onChangeAccount}
      />
      <Button onClick={onSubmitAccount}>로그인</Button>
    </Container>
  );
}

export default LoginForm;

위 코드 처럼
input이 입력될때마다 account에 값을 지정해주고
그 account값을 서버로 보내 매칭을 시킨 후,
만약 값이 일치하면 로그인을 시키고 원하는 url로 보내는 것이고,

정보가 일치하지않거나, 서버 통신이 정상적으로 이루어지지 않으면 에러메세지를 출력하게 만들어주면 됩니다

3개의 댓글

comment-user-thumbnail
2021년 9월 27일

마지막 LoginForm에서 useUserContext 코드는 어디서 볼 수 있을까요?

답글 달기
comment-user-thumbnail
2022년 4월 28일

저도 useUserContext 코드를 찾을 수가 없어서 문의립니다 ㅠㅠ

답글 달기
comment-user-thumbnail
2022년 6월 17일

저도 알고 싶군요...

답글 달기