프로젝트

[프로젝트] 코드리뷰-client 02

이채야채 2022. 2. 3. 16:09

 


Login.js


import { Link, useNavigate } from "react-router-dom";
import React, { useEffect, useState } from "react";
import styled from "styled-components";
import { useMediaQuery } from "react-responsive";
import axios from "axios";
import { postLogin, postSocialLogin } from "../api";
import { useDispatch } from "react-redux";
import { setLogin, setUserId, setSocialType } from "../action";

axios.defaults.withCredentials = true;

const Container = styled.div`
  padding-top: 100px;
  height: 100%;
`;

const WelcomeHeader = styled.div`
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  margin: 30px 0px;

  height: 100px;
`;

const WelcomeTitle = styled.div`
  text-align: center;
  width: 390px;
  font-size: 25px;
  margin: 10px 0px;
`;

const WelcomeText = styled.div`
  width: 60%;
  color: rgba(0, 0, 0, 0.8);
  font-size: 15px;
`;

const Button = styled.button`
  margin: 40px 10px 10px 10px;
  width: 70px;
  height: 30px;
  border-radius: 10%;
  background-color: ${(props) => (props.pc ? "white" : "#fed969")};
  cursor: grab;
  transition: all 0.3s ease-in-out;
  &:hover {
    background-color: #eeeeee;
  }
`;

const Screen = styled.div`
  width: 100%;
  display: flex;
  justify-content: center;
`;
const LoginForm = styled.form`
  display: flex;
  flex-direction: column;
  align-items: center;
  margin: 0px 40px;
  width: 700px;
  border-radius: 10px;
  background-color: white;
`;
const LoginInput = styled.input`
  width: 60%;
  text-decoration: none;
  border: none;
  padding: 20px 0px;
  font-size: 15px;
  border-bottom: ${(props) => (props.fullfilled ? "2px solid rgba(0, 0, 0, 0.2)" : "2px solid red")};
  margin-bottom: 15px;
  &:focus {
    outline: none;
  }
`;

const SignUp = styled.div`
  text-align: center;
  font-size: 13px;
  margin-bottom: 30px;
`;

const SignUpLink = styled(Link)`
  text-decoration: none;
  cursor: grab;
`;

const ButtonForm = styled.div`
  display: flex;
  height: 100%;
  width: 100%;
  justify-content: center;
  align-items: center;
  padding: 10px;
`;

const ButtonContainer = styled.div`
  max-width: 280px;
  width: 100%;
  height: 200px;
  display: flex;
  justify-content: space-evenly;
  flex-direction: column;
`;

const OauthButton = styled.a`
  all: unset;
  color: black;
  cursor: pointer;
  background-color: #ef463a;
  height: 50px;
  display: flex;
  justify-content: center;
  align-items: center;
  font-weight: bold;
  transition: 0.4s;
  color: black;
  border-radius: 5px;
  i {
    padding: 10px;
  }
  &:hover {
    background-color: rgb(239, 70, 58, 0.4);
  }

  &:last-child {
    color: rgb(242, 242, 242);
    background-color: #181516;
    &:hover {
      background-color: rgb(25, 25, 25, 0.4);
    }
  }
`;

const Messagebox = styled.div`
  font-size: 14px;
  color: red;
`;

const Login = () => {
  const dispatch = useDispatch();
  const navigate = useNavigate();
  const [isfullfilled, setIsFullfiled] = useState(true);
  const [invalid, setInvalid] = useState(true);

  const [loginInfo, setLoginInfo] = useState({
    email: "",
    password: "",
  });

  const url = new URL(window.location.href);
  const code = url.searchParams.get("code");
  const getUser = async () => {
    const { email, password } = loginInfo;
    const {
      data,
      data: {
        userInfo: { id },
      },
    } = await postLogin({ email, password });
    localStorage.setItem("userId", id);
    dispatch(setUserId(id));
    return data;
  };

  const handleInputValue = (key) => (e) => {
    setLoginInfo({ ...loginInfo, [key]: e.target.value });
  };

  const handleResponseSuccess = () => {
    dispatch(setLogin(true));
    localStorage.setItem("isLogin", true);
    navigate("/");
  };
  const handleLogin = async (e) => {
    e.preventDefault();

    if (!loginInfo.email || !loginInfo.password) {
      return setIsFullfiled(false);
    }

    try {
      const data = await getUser();
      if (data) {
        handleResponseSuccess(data);
      }
    } catch (err) {
      console.log(err.response);
      setInvalid(false);
    }
  };

  const isPc = useMediaQuery({ query: "(min-width: 768px)" });

  const getSocial = async () => {
    const id = await postSocialLogin(localStorage.getItem("socialType"), code);

    localStorage.setItem("userId", id);
    localStorage.setItem("isLogin", true);
    dispatch(setUserId(id));
    dispatch(setLogin(true));
    navigate("/drink");
  };

  useEffect(() => {
    if (code) {
      getSocial();
    }
  }, []);

  return (
    <Container>
      <WelcomeHeader>
        <WelcomeTitle>Welcome to SSOKBEER !</WelcomeTitle>
        <WelcomeText>쏙비어 회원이신분들은</WelcomeText>
        <WelcomeText>아이디와 비밀번호를 입력해주세요</WelcomeText>
      </WelcomeHeader>
      <Screen>
        <LoginForm>
          <LoginInput
            type='text'
            placeholder={isfullfilled ? "Email" : "이메일을 입력해주세요"}
            fullfilled={isfullfilled}
            onChange={handleInputValue("email")}></LoginInput>
          {invalid ? null : <Messagebox>이메일을 다시 확인해주세요</Messagebox>}
          <LoginInput
            type='password'
            placeholder={isfullfilled ? "password" : "비밀번호를 입력해주세요"}
            fullfilled={isfullfilled}
            onChange={handleInputValue("password")}></LoginInput>
          {invalid ? null : <Messagebox>비밀번호를 다시 확인해주세요</Messagebox>}
          <Button type='submit' onClick={handleLogin} pc={isPc}>
            로그인
          </Button>
          <SignUpLink to='/signup'>
            <SignUp>아직 회원이 아니신가요?</SignUp>
          </SignUpLink>
        </LoginForm>
      </Screen>
      <ButtonForm>
        <ButtonContainer>
          <OauthButton
            onClick={() => {
              localStorage.setItem("socialType", "google");
              dispatch(setSocialType("google"));
            }}
            href='https://accounts.google.com/o/oauth2/v2/auth?client_id=849456230902-bbj8hno72k1hhlciunde3nc0knp6i28m.apps.googleusercontent.com&redirect_uri=http://ssokbeer-bucket-depoly.s3-website.ap-northeast-2.amazonaws.com/login&response_type=code&scope=https://www.googleapis.com/auth/userinfo.email'>
            <i className='fab fa-google'></i>
            Google로 로그인
          </OauthButton>
          <OauthButton
            onClick={() => {
              localStorage.setItem("socialType", "github");
              dispatch(setSocialType("github"));
            }}
            href='https://github.com/login/oauth/authorize?client_id=8ab7b64fccca8e5e12c7&scope=user:email'>
            <i className='fab fa-github'></i>
            Github로 로그인
          </OauthButton>
        </ButtonContainer>
      </ButtonForm>
    </Container>
  );
};

export default Login;

 

로그인 함수위, 리턴 윗 부분부터 살펴보자.

 

const dispatch = useDispatch();
  const navigate = useNavigate();
  const [isfullfilled, setIsFullfiled] = useState(true);
  const [invalid, setInvalid] = useState(true);

  const [loginInfo, setLoginInfo] = useState({
    email: "",
    password: "",
  });

  const url = new URL(window.location.href);
  const code = url.searchParams.get("code");
  const getUser = async () => {
    const { email, password } = loginInfo;
    const {
      data,
      data: {
        userInfo: { id },
      },
    } = await postLogin({ email, password });
    localStorage.setItem("userId", id);
    dispatch(setUserId(id));
    return data;
  };

  const handleInputValue = (key) => (e) => {
    setLoginInfo({ ...loginInfo, [key]: e.target.value });
  };

  const handleResponseSuccess = () => {
    dispatch(setLogin(true));
    localStorage.setItem("isLogin", true);
    navigate("/");
  };
  const handleLogin = async (e) => {
    e.preventDefault();

    if (!loginInfo.email || !loginInfo.password) {
      return setIsFullfiled(false);
    }

    try {
      const data = await getUser();
      if (data) {
        handleResponseSuccess(data);
      }
    } catch (err) {
      console.log(err.response);
      setInvalid(false);
    }
  };

  const isPc = useMediaQuery({ query: "(min-width: 768px)" });

  const getSocial = async () => {
    const id = await postSocialLogin(localStorage.getItem("socialType"), code);

    localStorage.setItem("userId", id);
    localStorage.setItem("isLogin", true);
    dispatch(setUserId(id));
    dispatch(setLogin(true));
    navigate("/drink");
  };

  useEffect(() => {
    if (code) {
      getSocial();
    }
  }, []);

1.

const dispatch = useDispatch(); -> 리덕스로 상태를 바꿔줄것들을 위해 선언한다.

const navigate = useNavigate(); -> 페이지 이동을 위해 선언해준다.

const [isfullfilled, setisfullfilled] = useState(true) -> 이메일, 비번중 하나라도 입력을 하지않았다면 Placeholder부분의 구문이 바뀐다. 이것을 위해 상태관리

const [invalid , setInvalid] = useState(true) -> 만약에 없는 비번이나 이메일이 틀리면 메세지박스가 뜨게하기위한 장치

 

const [loginInfo, setLoginInfo] = useState({

email :""

password: ""

}) -> 치는 값을 value에 저장하여 이값들의 상태를 바꿔주고 상태를 기억해둬야할게 필요하다. axios요청을 보내야한다.

const handleInputValue = (key) => (e) => {

setLoginInfo({...loginInfo,[key] = e.target.value})

} -> 들어오는 값을 변화시키는것. 구조분해할다으로 email password부분 객체르르 풀어주고 그부분을 덮어씌워준다.

 

리턴아래부분

 

일반로그인,  소셜로그인부분(oath) 두가지 기능이있다.

  <Container>
      <WelcomeHeader>
        <WelcomeTitle>Welcome to SSOKBEER !</WelcomeTitle>
        <WelcomeText>쏙비어 회원이신분들은</WelcomeText>
        <WelcomeText>아이디와 비밀번호를 입력해주세요</WelcomeText>
      </WelcomeHeader>
      <Screen>
        <LoginForm>
          <LoginInput
            type='text'
            placeholder={isfullfilled ? "Email" : "이메일을 입력해주세요"}
            fullfilled={isfullfilled}
            onChange={handleInputValue("email")}></LoginInput>
          {invalid ? null : <Messagebox>이메일을 다시 확인해주세요</Messagebox>}
          <LoginInput
            type='password'
            placeholder={isfullfilled ? "password" : "비밀번호를 입력해주세요"}
            fullfilled={isfullfilled}
            onChange={handleInputValue("password")}></LoginInput>
          {invalid ? null : <Messagebox>비밀번호를 다시 확인해주세요</Messagebox>}
          <Button type='submit' onClick={handleLogin} pc={isPc}>
            로그인
          </Button>
          <SignUpLink to='/signup'>
            <SignUp>아직 회원이 아니신가요?</SignUp>
          </SignUpLink>
        </LoginForm>
      </Screen>
      <ButtonForm>
        <ButtonContainer>
          <OauthButton
            onClick={() => {
              localStorage.setItem("socialType", "google");
              dispatch(setSocialType("google"));
            }}
            href='https://accounts.google.com/o/oauth2/v2/auth?client_id=849456230902-bbj8hno72k1hhlciunde3nc0knp6i28m.apps.googleusercontent.com&redirect_uri=http://ssokbeer-bucket-depoly.s3-website.ap-northeast-2.amazonaws.com/login&response_type=code&scope=https://www.googleapis.com/auth/userinfo.email'>
            <i className='fab fa-google'></i>
            Google로 로그인
          </OauthButton>
          <OauthButton
            onClick={() => {
              localStorage.setItem("socialType", "github");
              dispatch(setSocialType("github"));
            }}
            href='https://github.com/login/oauth/authorize?client_id=8ab7b64fccca8e5e12c7&scope=user:email'>
            <i className='fab fa-github'></i>
            Github로 로그인
          </OauthButton>
        </ButtonContainer>
      </ButtonForm>
    </Container>
  );
};

onChange함수를 사용하여 이메일, 패스워드등의 상태를 변화시킨다.

버튼을 클릭시 handleLogin이 실행된다.

 

const handleLogin = async (e) => {
    e.preventDefault();

    if (!loginInfo.email || !loginInfo.password) {
      return setIsFullfiled(false);
    }

    try {
      const data = await getUser();
      if (data) {
        handleResponseSuccess(data);
      }
    } catch (err) {
      console.log(err.response);
      setInvalid(false);
    }
  };

 

1. e.prevent.default -> html에서 a태그나 submit태그는 고유의 동작이 있다. 페이지를 이동시킨다거나, form안에 input전송등. 이런것들을 막기위해 사용한다.(동작 중단)

2. 이메일이나 패스워드가없으면? onchange값이 변화가되지않았으면, setIsFullfiled(false)로 상태변화를 시켜주고, 함수는 그대로 끝나버린다. 

3. 충족이되었다면? try{}catch{}문 안으로 들어간다. 

const data = await getUser(); 를 실행한다. getUser함수로 이동을해보자.

  const getUser = async () => {
    const { email, password } = loginInfo;
    const {
      data,
      data: {
        userInfo: { id },
      },
    } = await postLogin({ email, password });
    localStorage.setItem("userId", id);
    dispatch(setUserId(id));
    return data;
  };
  
  
  
  //api에 있는 postlogin
  
  export const postLogin = async (body) => {
  const data = await axios.post(`${URL}/user/login`, body, cookieOption);
  return data;
};

1. 구조분해할당을 통해서 loginInfo의 이메일 패스워드부분을 가져왔다.

2. postLogin은 백엔드팀에서 만들어준 깃북을 통해서 body에 무엇을 보내야하는지, 어떻게 정보가 들어오는지 알수있다.

3. 통째로받아오면 -> 꺼내쓸수있고, 아니면 해당하는거만 사용하는것

4. userId -> 내글에만 권한이 있어야하기에 localstorage에 셋팅해주는것

try catch문으로 돌아와

 

데이터가 있으면 유저정보가있기에, handlesuccesslogin함수가 작동하고 아니면 setinvalid로 상태를 변화시켜준다.

 

 const handleResponseSuccess = () => {
    dispatch(setLogin(true));
    localStorage.setItem("isLogin", true);
    navigate("/");
  };

로그인이되는 상태로 변화를 시켜야한다.

localStorage에 저장해준다. 로그인 셋팅

 

https://developer.mozilla.org/en-US/docs/Web/API/Storage/getItem

https://developer.mozilla.org/ko/docs/Web/API/Storage/removeItem

 

소셜로그인 기능을 봐보자