로그인기능 직접 구현

foundation 기간이나 1차프로젝트 기간동안 사실상 로그인, 회원가입 기능은 거의 안해봐서 이번에 해보고 싶었다

1차프로젝트와는 다르게 2차 프로젝트에서는
function형 컴포넌트 및 styled-component를 이용했다!

로그인페이지 전체 레이아웃

// 로그인페이지 전체 레이아웃
<Whole>
  <Logo>
    <img src="./images/logo.png" alt="제발나와라" />
  </Logo>

  <LoginContainer>
    <Title>로그인</Title>
      {LoginDatas.map((LoginData, idx) => {
        return <LoginInput key={idx} data={LoginData} />;
       })}
    <Btn
     backColor="red"
     textColor="white"
     disabled={!check}
     onClick={() => handleBtn()} >
     로그인
    </Btn>
    
    <Help>
      <a href="https://www.netflix.com/kr/LoginHelp">
      도움이 필요하신가요?
      </a>
    </Help>
    
    <Footer>
      <Btn backColor="transparent" onClick={handleKakao}>
        <img src="./images/kakaoBtn.png" alt="kakaoBtn" />
      </Btn>
          
      <p>
      Flix 회원이 아니신가요?{" "}
      <White onClick={() => history.push("/signup")}>
      지금 가입하세요
      </White>
      </p>
      
      <p>이 페이지에 안들어가면 후회하실겁니다!</p>
    </Footer>
  </LoginContainer>
</Whole>

이렇게 보면 태그들의 형태가 모두 심상치 않은데, 그 이유가 바로 styled-component를 사용했기 때문이다

styled-component 사용하기

  1. 해당 터미널에서 styled-component 패키지 설치
$ npm install styled-component
  1. Login.js 상단에서 import하기
import styled from "styled-components";
  1. 스타일을 지정할 컴포넌트 만들기 (지정하기)
  • Lodin 컴포넌트가 모두 렌더된 다음, 하단에 적어주는 것이 구조를 파악하는 데에 있어 도움이 될 것 같다
  • 2번에서 import한 styled + . + 지정하고 싶은 태그이름
    ~~Whole 컴포넌트는 div태그로 컴포넌트로 만들고 싶어서 아래와 같이 [const Whole = styled.div``] 으로 적어주었다
  • 지정해주고 싶은 스타일 속성은 백틱 사이에 적어주면 된다
    - scss와 달리 css 문법을 유지할 수 있는 장점이 있다
const Whole = styled.div`
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100vw;
  height: 100vh;
  background-image: url("https://images.unsplash.com/photo-1545630478-cf62cdd247d1?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8MjR8fG1vdmllc3xlbnwwfHwwfHw%3D&auto=format&fit=crop&w=800&q=60");
`;

내용 자체는 잘 안보이겠지만
Login 컴포넌트 내부에서 사용하는 styled-component 자체가 적지 않기 때문에
만약 컴포넌트를 위에 선언하다보면 rendering되는 중요한 부분을 확인하기 힘들 것이다

function형 자식컴포넌트에 props 전달하는 방법

// 배열 LoginDatas
const LoginDatas = [
    {
        name: userEmail,
        title: "이메일주소",
        type: "email",
        validation: emailCheck,
        inputAlert: "정확한 이메일 주소으로 입력해주세요.",
        setName: setUserEmail
    }, {
        name: userPw,
        title: "비밀번호",
        type: "password",
        validation: pwCheck,
        inputAlert: "8자 이상 영문 대 소문자, 숫자, 특수문자를 사용하세요.",
        setName: setUserPw
    }
];


// 아이디, 비밀번호 입력하는 Input창
{LoginDatas.map((LoginData, idx) => {
  return <LoginInput key={idx} data={LoginData} />;
})}


// 자식 컴포넌트인 LoginInput.js
const { name, title, type, validation,
       inputAlert, setName }= props.data;

  return (
    <InputContainer>
      <PlaceHolder>{title}</PlaceHolder>
      <Input name={name} type={type}
        onChange={e => setName(e.target.value)} />
      {validation && <InputAlert>{inputAlert}</InputAlert>}
    </InputContainer>
  );

배열 LoginDatas에서 map()함수를 이용해서 각각의 LoginInput 컴포넌트 생성

{LoginDatas.map((LoginData, idx) => {
  return <LoginInput key={idx} data={LoginData} />;
})}
  1. 자식컴포넌트에게 props 전달하는 방법

이전의 class형 컴포넌트에서 자식 컴포넌트에게 props를 전달했을 때
자식컴포넌트에서는 this.props 로 확인했어야 했지만,

이번의 function형 컴포넌트에서 자식 컴포넌트에게 props를 전달했을 때
자식컴포넌트에서는 props.data로 확인한다

로그인 버튼을 눌렀을 때 서버와 통신하기

const handleBtn = () => {
  fetch("http://10.58.7.101:8000/users/sign-in", {
    method: "POST",
    body: JSON.stringify({
      email: userEmail,
      password: userPw,
      signup_type: "flix"})
    })
   .then((res) => res.json())
   .then((result) => {
     if (result.message === "LOGIN_SUCCESS") {
       alert(`Flix의 재미를 느껴보세요!`);
       localStorage.setItem("token", result.token);
       history.push("/");
     } else {
       alert("이메일 및 비밀번호를 올바르게 기입해주세요.");
     }
  });
};

<Btn
  backColor="red"
  textColor="white"
  disabled={!check}
  onClick={() => handleBtn()}
>
  로그인
</Btn>;

로그인버튼이 눌렸을 때 handleBtn 함수가 실행되는데

사실 이렇게 함수 하나만 호출하는 거면 이런식으로 작성하는 게 더 깔끔해 보인다

onClick={handleBtn}
// fetch() 함수
fetch("http://10.58.7.101:8000/users/sign-in", {
    method: "POST",
    body: JSON.stringify({
      email: userEmail,
      password: userPw,
      signup_type: "flix"})
    })
  1. 첫 번째 인자 : 서버 API의 URL
  2. 두 번째 인자 : request 보낼 때 같이 줄 내용으로
    • POST방식의 method
    • email,password, signup_type의 내용이 담긴 body

http request 구조
이 블로그를 참고하면 좋을 것같다

.then((res) => res.json())
.then((result) => {
  if (result.message === "LOGIN_SUCCESS") {
    alert(`Flix의 재미를 느껴보세요!`);
    localStorage.setItem("token", result.token);
    history.push("/");
  } else {
    alert("이메일 및 비밀번호를 올바르게 기입해주세요.");
  } 
});
  1. 서버와의 통신을 통해 받은 response를 내가 가공해서 사용하기 위해 json형식으로 변환하고
  2. 로그인 통신에서 성공하면 status는 200이 되고, response로 주는 result의 message가 "LOGIN_SUCCESS"이 된다
    • "Flix의 재미를 느껴보세요!" 라는 alert를 띄우고
    • localStorage 라는 저장소에 result의 token키값을 "token" 이라는 키값으로 저장하고
    • main 페이지로 이동하려고 하는데, function형 컴포넌트에서는 this.props.history.push() 메서드를 사용할 수 없어서 아래와 같은방법으로 페이지 이동 로직을 구현했다

function형 컴포넌트 조건에 따라 페이지이동

import { useHistory } from "react-router-dom";
const history = useHistory();

  history.push("/");

react-router-dom 패키지에서 useHistory() 메서드를 import한 다음
useHistory() 메서드를 history 라고 지정해서
history.push() 메서드를 사용했다

react-router-dom의 Hook 더 알아보기
이 블로그를 참고해보자!

좋은 웹페이지 즐겨찾기