[프로젝트] 코드리뷰-client 02
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
소셜로그인 기능을 봐보자