반응형
오늘 해야할 것.
1) 로그인 컴포넌트(컴포넌트가 교체되면서 새로 출력하는 부분 만들 것) - 완료
2) 상세페이지 사진 hover시에만 떠야하는데 안떠서 스타일 컴포넌트로 통일 -예정
3) 상세페이지 호텔 설명 slice함수를 통해서 배열로 만들고 이를 map함수를 통해 출력하기 = 예정
4) 상세페이지 이미지(slick slider) 구현 - 예정
로그인 컴포넌트를 고쳤다. 우오오~(Modalhandler 구조는 아직도 이해가 안됨;;; 리덕스 같은데, 리덕스는 아니라고 하는데 함수를 조금더 공부해봐야 할듯 싶다.)
모든 로그인 컴포넌트를 컨트롤하는건 nav 컴포넌트였던 것이다.
이것이 꺼지도록 애초에 nav에서 설정한다면, 그것을 props로 하위 컴포넌트로 보내버리면 되는 것이었다.
import { withRouter } from "react-router-dom";
import SocialLogin from "../Components/LoginComponent/SocialLogin";
import EmailLogin from "../Components/LoginComponent/EmailLogin"
import SignUp from "../Components/LoginComponent/Signup"
//함수형 컴포넌트 위치 {
const [isLoginModalOn ,setIsLoginModalOn] = useState(false);
const [loginMode ,setLoginMode] =useState("social")
const openLoginHadler =() => {
setIsLoginModalOn(true)
};
const closeModal =()=>{
setIsLoginModalOn(false)
setLoginMode("social")
}
const modalHandler =(mode)=>{
switch(mode){
case "social":
return <SocialLogin setMode={setLoginMode} closeModal={closeModal}/>
case "email":
return <EmailLogin setMode={setLoginMode} closeModal={closeModal}/>
case "signup":
return <SignUp setMode={setLoginMode} closeModal={closeModal}/>
default:
return <SocialLogin setMode={setLoginMode} closeModal={closeModal}/>
}
}
<LoginBtn onClick={openLoginHadler}>로그인</LoginBtn>
</BtnWrap>
</NavWrap>
{isLoginModalOn? modalHandler(loginMode):null}
저 로그인 버튼으로 해당 이벤트가 불러오면 setLoginModalon이 작동되어 true로 바뀌면
nav창 위에서 해당 컴포넌트가 불러지는 방식이다.
기존에는 페이지 이동인 path로 컨트롤했는데, 모달의 경우 컴포넌트를 해당 컴포넌트위에서 뿌려지는 것이라
페이지 이동이 아니라는 것을 배웠다.
마지막으로 최상단 Nav에서 뿌려지는 자료만 이용하여, 하부 컴포넌트에서는 props를 통해서 따로 호출이나 함수를 만들지 않고도 통제가 가능하는 것을 보면서 상위 컴포넌트에서 하위 컴포넌트의 시작과 종료를 통제하는 것이 인상 깊었다.
import React, { useState } from "react";
import styled from "styled-components";
import KakaoLogin from "react-kakao-login";
import { GoogleLogin } from "react-google-login";
import { withRouter } from "react-router-dom";
import { Apiresource } from "../../Config";
import { ClientId } from "../../Config";
import { Jskey } from "../../Config";
function SocialLogin(props, visible) {
const [ id, setId ] = useState('');
const [ name, setName ] = useState('');
// 구글 로그인
const clickGoogleBtn = (res) => {
fetch(`${Apiresource}/sign-in/google`, {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify({
email: res.profileObj.email,
name: res.profileObj.name,
})
})
.then(res => res.json())
.then(res => {
if (res) {
localStorage.setItem('kakao-token', res.token)
alert('로그인을 환영합니다')
props.history.push('/main')
} else {
alert('아이디와 비밀번호를 확인해주세요.')
}
})
}
// 카카오 로그인
const clickKakaoBtn = (res) => {
fetch(`${Apiresource}/account/sign-in/kakao`, {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify({
access_token : res.response.access_token
})
})
.then(res => res.json())
.then(res => {
if (res.access_token) {
localStorage.setItem('kakao-token', res.access_token)
alert('로그인을 환영합니다')
props.history.push('/main')
} else {
alert('아이디와 비밀번호를 확인해주세요.')
}
})
}
const responseFail = (err) => {
console.error(err)
};
return(
<>
<ModalOverlay visible={visible} />
<ModalWrapper tabIndex="-1" visible={visible}>
<ModalIner tabIndex="0">
<div>
<LoginImageContainer>
<LoginImgtag src="https://static.tacdn.com/img2/brand_refresh/Tripadvisor_lockup_horizontal_registered.svg" />
</LoginImageContainer>
<LogInnput>
<LoginInputMargin>
<div className="centerMargin">
<GoogleBtn
clientId={`${ClientId}`}
onSuccess={clickGoogleBtn}
onFailure={responseFail}
>
<Socialspan>Google로 계속하기</Socialspan>
</GoogleBtn>
<KakaoBtn
jsKey={`${Jskey}`}
onSuccess={clickKakaoBtn}
onFailure={responseFail}
getProfile="true"
>
<SocialImg src="https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcTI2AI6Bz3gkrH3VCq4azeIg-CXoxxvsar2Og&usqp=CAU" />
<Socialspan>카카오톡 계정으로 계속하기</Socialspan>
</KakaoBtn>
<OR>
<BorderLine>
<Topspan>또는</Topspan>
</BorderLine>
</OR>
<SocialBtn onClick={()=>props.setMode("email")}>
<SocialImg src="https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcQvsq86NX1qN68M4GUeh5k8h6KM4pADF86mEw&usqp=CAU" />
<Socialspan>
이메일로 계속하기
</Socialspan>
</SocialBtn>
</div>
<ContiNue>
계속 진행할 경우, 트립어드바이저의
<span className="textDeco">개인정보 취급방침</span>
및
<span className="textDeco">쿠키 정책</span>
에 동의한 것으로 간주됩니다.
</ContiNue>
</LoginInputMargin>
</LogInnput>
</div>
<button onClick={props.closeModal}
className="returnmain"
tabIndex="0">
X
</button>
</ModalIner>
</ModalWrapper>
</>
);
}
const ModalOverlay = styled.div`
box-sizing: border-box;
display: ${(props) => (props.visible ? 'block' : 'none')};
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.6);
z-index: 999;
`;
const ModalWrapper = styled.div`
box-sizing: border-box;
display: ${(props) => (props.visible ? 'block' : 'none')};
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1000;
overflow: auto;
outline: 0;
display: flex;
justify-content: center;
align-items: center;
`;
const ModalIner = styled.div`
width: 420px;
height: 666px;
padding: 40px 45px 30px;
box-sizing: border-box;
position: relative;
box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.5);
background-color: white;
.returnmain {
position: absolute;
z-index: 1;
top: 0;
right: 0;
text-align: center;
width: 48px;
height: 48px;
font-size: 25px;
cursor: pointer;
}
`;
const LoginImageContainer = styled.div`
width:328px;
`;
const LoginImgtag = styled.img`
width: 210px;
height: 32px;
margin-right: 20px;
`;
const LogInnput = styled.div`
width: 329px;
height: 467px;
`;
const LoginInputMargin = styled.div`
width:328px;
height:48px;
.centerMargin{
margin: 50px 0;
position: relative;
}
`;
const GoogleBtn = styled(GoogleLogin)`
width: 328px !important;
height: 48px !important;
margin-bottom: 16px !important;
border-radius: 12px !important;
box-shadow: none !important;
border : 2px solid rgba(0,0,0,.2) !important;
color: black !important;
background-color: 'white' !important;
display:flex !important;
align-items: center !important;
div{
width: 20px;
height: 20px;
margin: 0 0 0 24px !important;
padding: 0 !important;
}
span {
padding : 0 !important;
}
`;
const KakaoBtn = styled(KakaoLogin)`
width: 328px;
height: 48px;
margin-bottom: 16px;
border-radius: 12px;
box-shadow: none;
border : 2px solid rgba(0,0,0,.2);
color: black;
background-color: 'white';
display:flex;
align-items: center;
`;
const SocialBtn = styled.div`
width: 328px;
height: 48px;
margin-bottom: 16px;
border-radius: 12px;
box-shadow: none;
border : 2px solid rgba(0,0,0,.2);
color: black;
background-color: 'white';
display:flex;
align-items: center;
`;
const SocialImg = styled.img`
width: 20px;
height: 20px;
margin-left: 24px;
`;
const Socialspan = styled.span`
color : black;
width: auto;
font-size: 14px;
font-family: '굴림, gulim, sans-serif';
margin: 0 12px 0 50px;
`;
const OR = styled.div`
padding : 8px 0;
text-align: center;
`;
const BorderLine = styled.div`
border-top : 1px solid #e0e0e0;
content: none;
display: block;
`;
const Topspan = styled.span`
position: relative;
top: -8px;
font-weight:600;
font-size: 10px;
line-height: 14px;
color: #8c8c8c;
padding: 0 6px;
background-color: white;
`;
const ContiNue = styled.div`
width: 328px;
height: 32px;
font-size: 12px;
font-family: '굴림, gulim, sans-serif';
.textDeco {
text-decoration: underline;
color: black;
text-decoration-style: solid;
font-weight: 600;
line-height: 16px;
}
`;
export default withRouter(SocialLogin);
이메일로그인 컴포넌트
import React, { useState, useEffect } from "react";
import styled from "styled-components";
import { withRouter } from "react-router-dom";
import { Apiresource } from "../../Config.js";
function EmailLogin(props, visible) {
const [ userInfo, setName ] = useState({email:"", password:""});
const inputValuedetector = (e) => {
const { name, value} = e.target
setName({...userInfo, [ name ] : value })
};
const submit = () => {
const { email, password } = userInfo
fetch(`${Apiresource}/account/sign-in`, {
method: 'POST',
body: JSON.stringify({ email, password })
})
.then(res => res.json())
.then(res => {
if (res.token) {
localStorage.setItem("tripadvisor-token", res.token)
alert("로그인을 환영합니다")
props.history.push('/main')
} else {
alert('이메일과 비밀번호를 확인해주십시오')
};
})
}
const isValidEmail = !userInfo.email.length || (userInfo.email.length && userInfo.email.includes("@"));
return(
<>
<ModalOverlay visible={visible} />
<ModalWrapper tabIndex="-1" visible={visible}>
<ModalIner tabIndex="0">
<div className="modalOutline">
<LoginImageContainer>
<LoginImgtag src="https://static.tacdn.com/img2/brand_refresh/Tripadvisor_lockup_horizontal_registered.svg" />
</LoginImageContainer>
<SignInput>
<div className="signupContainer">
<div className="welcome">환영합니다 - 로그인하세요!</div>
<label className="welcome label">이메일 주소</label>
<input onChange={inputValuedetector}
className="inputValue"
type='text'
name="email"
placeholder="이메일" />
<label className="welcome label">패스워드</label>
<input onChange={inputValuedetector}
className="inputValue"
type='password'
name="password"
placeholder="비밀번호" />
<div onClick={submit}
className="inputValue logIntitle"
type="buttton">
로그인
</div>
<div className="passWordFindertitle">
<span className="passwordfinder">
패스워드 찾기
</span>
</div>
<div className="passWordFindertitle">
계정이 없으신가요?
<span onClick={()=>props.setMode("signup")}
className="passwordfinder">
가입하기
</span>
</div>
<div className="passWordFindertitle">
대신 카카오톡이나 Google을 사용하고 싶으세요?
<span onClick={()=>props.setMode("social")}
className="passwordfinder">
돌아가기
</span>
</div>
</div>
<ContiNue>
계속 진행할 경우, 트립어드바이저의
<span className="textDeco">
개인정보 취급방침
</span>
및
<span className="textDeco Cookie">
쿠키 정책
</span>
에 동의한 것으로 간주됩니다.
</ContiNue>
</SignInput>
<div className={ isValidEmail
? 'noneinputError'
: 'inputError'
} >
이메일 주소로써 유효하지 않거나 저희쪽에서 메일을 발송할 수 없는 문자열을 포함하고있습니다.
</div>
</div>
<button onClick={props.closeModal}
className="returnmain"
tabIndex="0">
X
</button>
</ModalIner>
</ModalWrapper>
</>
)
}
const ModalOverlay = styled.div`
box-sizing: border-box;
display: ${(props) => (props.visible ? 'block' : 'none')};
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.6);
z-index: 999;
`;
const ModalWrapper = styled.div`
box-sizing: border-box;
display: ${(props) => (props.visible ? 'block' : 'none')};
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1000;
overflow: auto;
outline: 0;
display: flex;
justify-content: center;
align-items: center;
`;
const ModalIner = styled.div`
width: 420px;
height: 666px;
padding: 40px 45px 30px;
box-sizing: border-box;
position: relative;
box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.5);
background-color: white;
.modalOutline {
width: 100%;
.inputError {
position: absolute;
display: block;
color: red;
background-color: white;
font-family:굴림, gulim, sans-serif;
font-size: 12px;
border: 1px solid #e0e0e0;
box-shadow: 0 2px 12px rgba(0,0,0,.35);
padding: 18px;
line-height: 16px;
width: 316px;
height: 68px;
z-index: 5;
left: 51px;
right: auto;
top: 113px;
bottom: auto;;
}
.noneinputError {
display: none;
color: white;
}
}
.returnmain {
position: absolute;
z-index: 1;
top: 0;
right: 0;
text-align: center;
width: 48px;
height: 48px;
font-size: 25px;
cursor: pointer;
}
`;
const LoginImageContainer = styled.div`
width:328px;
`;
const LoginImgtag = styled.img`
width: 210px;
height: 32px;
margin-right: 20px;
`;
const SignInput = styled.div`
.signupContainer {
width: 328px;
height: 440px;
display: flex;
flex-direction: column;
.welcome {
color : black;
font-size: 16px;
font-family: '굴림, gulim, sans-serif';
margin: 36px 0 25px;
}
.label {
font-size: 14px;
font-weight: 600;
line-height: 18px;
text-align: left;
margin: 0;
}
.inputValue {
background-color: white;
height: 48px;
margin: 8px 0 16px;
padding : 4px 4px 4px 8px;
border: 2px solid #e0e0e0;
border-radius: 12px;
}
.logIntitle {
color : white;
background: black;
width: 100%;
font-size: 14px;
font-weight: 700;
margin: 20px 0;
padding: 14px 16px 8px;
border: 1px solid transparent;
line-height: 18px;
text-align : center;
&:hover {
background: #525252;
}
}
.passWordFindertitle {
margin-bottom: 12px;
.passwordfinder {
font-size: 12px;
font-family: '굴림, gulim, sans-serif';
font-weight: 600;
color: black;
line-height: 16px;
margin-left: 5px;
&:hover{
text-decoration: underline;
}
}
}
}
`;
const ContiNue = styled.div`
width: 328px;
height: 32px;
font-size: 12px;
font-family: '굴림, gulim, sans-serif';
.textDeco {
text-decoration: underline;
color: black;
text-decoration-style: solid;
font-weight: 600;
line-height: 16px;
margin: 0 5px;
}
.Cookie {
margin: 0 0 0 5px;
}
`;
export default withRouter(EmailLogin);
회원가입 컴포넌트
import React, { useState } from "react";
import styled from "styled-components";
import { withRouter } from "react-router-dom";
import { Apiresource } from "../../Config";
function Signup(props, visible) {
const [ userInfo, setName ] = useState({email:"",password:"",name:""});
const inputValuedetector = (e) => {
const { name, value } = e.target
setName({...userInfo, [ name ] : value});
};
const submit = () => {
const {email, password, name } = userInfo
fetch(`${Apiresource}/account/sign-up`, {
method: 'POST',
body: JSON.stringify({ email, password, name })
})
.then(res => res.json)
.then(res => {
if (res) {
alert("회원가입을 환영합니다")
props.history.push('/main')
} else {
alert('이메일과 비밀번호를 확인해주십시오')
};
})
}
const isValidEmail = !userInfo.email.length || (userInfo.email.length && userInfo.email.includes("@"));
return(
<>
<ModalOverlay visible={visible} />
<ModalWrapper tabIndex="-1" visible={visible}>
<ModalIner tabIndex="0">
<div className="modalOutline">
<LoginImageContainer>
<LoginImgtag src="https://static.tacdn.com/img2/brand_refresh/Tripadvisor_lockup_horizontal_registered.svg" />
</LoginImageContainer>
<SignInput>
<div className="signupContainer">
<div className="signupBody">
<div className="welcome">
바로 회원가입하기 - 무료입니다!
</div>
<label className="welcome label">
이메일 주소
</label>
<input onChange={inputValuedetector}
className="email_input"
type='text'
name="email"
placeholder="이메일" />
<label className="welcome label">
패스워드 생성하기
</label>
<input onChange={inputValuedetector}
className="email_input"
type='password'
name="password"
placeholder="비밀번호" />
<label className="welcome label">
닉네임 생성하기
</label>
<input onChange={inputValuedetector}
className="email_input"
type='text'
name="name"
placeholder="닉네임" />
<div onClick={submit}
className="logIntitle"
type="button">
가입하기
</div>
</div>
<div className="passWordFindertitle">
이미 계정이 있으신가요?
<span onClick={()=>props.setMode("email")}
className="passwordfinder">
로그인
</span>
</div>
<div className="passWordFindertitle">
대신 카카오톡이나 Google을 사용하고 싶으세요?
<span onClick={()=>props.setMode("social")}
className="passwordfinder">
돌아가기
</span>
</div>
</div>
<div className="inputboxTitle">
<input className="inputbot" type="checkbox"></input>
<label>
트립어드바이저의 여행 특가, 팁, 새로운 기능에 대한 정보 메일을 수신하겠습니다. 수신을 원치 않을 때는 해지할 수 있습니다.
</label>
</div>
</SignInput>
<div className={ isValidEmail
? 'noneinputError'
: 'inputError'
} >
이메일 주소로써 유효하지 않거나 저희쪽에서 메일을 발송할 수 없는 문자열을 포함하고있습니다.
</div>
</div>
<button onClick={props.closeModal}
className="returnmain"
tabIndex="0">
X
</button>
</ModalIner>
</ModalWrapper>
</>
)
}
const ModalOverlay = styled.div`
box-sizing: border-box;
display: ${(props) => (props.visible ? 'block' : 'none')};
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.6);
z-index: 999;
`;
const ModalWrapper = styled.div`
box-sizing: border-box;
display: ${(props) => (props.visible ? 'block' : 'none')};
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1000;
overflow: auto;
outline: 0;
display: flex;
justify-content: center;
align-items: center;
`;
const ModalIner = styled.div`
width: 420px;
height: 666px;
padding: 40px 45px 30px;
box-sizing: border-box;
position: relative;
box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.5);
background-color: white;
.modalOutline {
width: 100%;
.inputError {
position: absolute;
display: block;
color: red;
background-color: white;
font-family: '굴림, gulim, sans-serif';
font-size: 12px;
border: 1px solid #e0e0e0;
box-shadow: 0 2px 12px rgba(0,0,0,.35);
padding: 18px;
line-height: 16px;
width: 316px;
height: 68px;
z-index: 5;
left: 51px;
right: auto;
top: 113px;
bottom: auto;;
}
.noneinputError {
display: none;
color: white;
}
}
.returnmain {
position: absolute;
z-index: 1;
top: 0;
right: 0;
text-align: center;
width: 48px;
height: 48px;
font-size: 25px;
cursor: pointer;
}
`;
const LoginImageContainer = styled.div`
width:328px;
`;
const LoginImgtag = styled.img`
width: 210px;
height: 32px;
margin-right: 20px;
`;
const SignInput = styled.div`
.signupContainer {
width: 100%;
height: 440px;
.signupBody {
display: flex;
flex-direction: column;
width: 100%;
height: 380px;
.welcome {
color : black;
font-size: 16px;
font-family: '굴림, gulim, sans-serif';
margin: 36px 0 25px;
}
.label {
font-size: 14px;
font-weight: 600;
line-height: 18px;
text-align: left;
margin: 0;
}
.email_input {
background-color: white;
height: 50px;
margin: 8px 0 16px;
padding : 4px 4px 4px 8px;
border: 2px solid #e0e0e0;
border-radius: 12px;
}
.logIntitle {
color : white;
background: black;
width: 100%;
height: 48px;
font-size: 14px;
font-weight: 700;
margin: 20px 0;
padding: 14px 16px 8px;
border-radius: 12px;
border: 1px solid transparent;
line-height: 18px;
text-align : center;
&:hover {
background: #525252;
}
}
}
.passWordFindertitle {
margin-bottom: 12px;
.passwordfinder {
font-size: 12px;
font-family: '굴림, gulim, sans-serif';
font-weight: 600;
color: black;
line-height: 16px;
margin-left: 5px;
&:hover{
text-decoration: underline;
}
}
}
}
`;
export default withRouter(Signup);
728x90
'코딩 > 위코드 코딩학습' 카테고리의 다른 글
[위코드] TIL(Today I am Learned) -28(anser) (0) | 2020.08.20 |
---|---|
[위코드] TIL(Today I am learned) -27(2차프로젝트 트립어드바이저)(후기) (0) | 2020.08.17 |
[위코드] TIL(Today I am learned) -25 (0) | 2020.08.12 |
[위코드] TIL(Today I am learned) -24(위코드의 마지막 주말) (0) | 2020.08.09 |
[위코드] TIL(Today I am learned) -23 (0) | 2020.08.07 |