Skip to content

Commit

Permalink
[Feat]회원가입 로그인 develop
Browse files Browse the repository at this point in the history
회원가입 및 로그인 에러 발생시 모달 창에 띄우도록 구현
OAuth로그인 시 로그인 확인 모달창 띄우고 확인 버튼 클릭 시 비로소 완전히 로그인이 되도록 변경
로그인 확인 모달창에서 확인 버튼 클릭 시 토큰을 스토리지에 저장
메인페이지 안에서 로그인 상태가 유지되도록 토큰명 에러 수정
Issues #15
  • Loading branch information
김병현 authored and 김병현 committed Sep 16, 2023
1 parent f273193 commit b311505
Show file tree
Hide file tree
Showing 9 changed files with 179 additions and 100 deletions.
39 changes: 20 additions & 19 deletions client/src/components/Logins/EmailLogin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import React, { useState } from "react";
import { setLoginState } from "../../reducer/member/loginSlice";
import { useDispatch } from "react-redux";

// 이메일 로그인 모달 컴포넌트
const EmailLoginModal: React.FC<EmailLoginModalProps> = ({ onClose, onLogin }) => {
// 상수 문자열 정의
const titleText = "이메일로 로그인";
const emailLabelText = "이메일";
const passwordLabelText = "비밀번호";
Expand All @@ -15,55 +13,51 @@ const EmailLoginModal: React.FC<EmailLoginModalProps> = ({ onClose, onLogin }) =
const noAccountText = "계정이 없습니까?";
const registerButtonText = "회원가입하기";

//디스패치 함수 가져오기
const dispatch = useDispatch();

// 상태 변수 정의
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [generalError, setGeneralError] = useState<string | null>(null);

// 이메일 변경 핸들러
const handleEmailChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setEmail(event.target.value);
};

// 비밀번호 변경 핸들러
const handlePasswordChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setPassword(event.target.value);
};

// 로그인 버튼 클릭 핸들러
const handleLoginClick = async () => {
try {
const response = await axios.post("http://ec2-13-125-246-160.ap-northeast-2.compute.amazonaws.com:8080/members/login", {
email,
password,
});
const response = await axios.post(
"http://ec2-13-125-246-160.ap-northeast-2.compute.amazonaws.com:8080/members/login",
{ email, password },
{ validateStatus: (status) => status >= 200 && status < 600 }
);

if (response.status === 200) {
const accessToken = response.headers["authorization"];
console.log(accessToken);

const refreshToken = response.headers["refresh"];

// 로그인 상태로 만들기
dispatch(setLoginState());

// 토큰들을 로컬 스토리지에 저장
if (accessToken) localStorage.setItem("accessToken", accessToken);
if (refreshToken) localStorage.setItem("refreshToken", refreshToken);

onLogin();
onClose();
} else {
console.error("로그인 중 오류 발생");
setGeneralError(response.data.message || JSON.stringify(response.data));
}
} catch (error) {
console.error("로그인 중 오류:", error);
if (axios.isAxiosError(error) && error.response) {
setGeneralError(error.response.data.message || JSON.stringify(error.response.data));
} else {
console.error("로그인 중 오류:", error);
}
}
};

return (
// 모달 레이아웃
<ModalBackground>
<ModalContainer>
<CloseButton onClick={onClose}>&times;</CloseButton>
Expand All @@ -72,6 +66,7 @@ const EmailLoginModal: React.FC<EmailLoginModalProps> = ({ onClose, onLogin }) =
<Input type="email" placeholder="이메일을 입력하세요" value={email} onChange={handleEmailChange} />
<Label>{passwordLabelText}</Label>
<Input type="password" placeholder="비밀번호를 입력하세요" value={password} onChange={handlePasswordChange} />
{generalError && <ErrorMessage>{generalError}</ErrorMessage>}
<RightAlignedButton>{findPasswordText}</RightAlignedButton>
<LoginButton onClick={handleLoginClick}>{loginButtonText}</LoginButton>
<BottomText>
Expand Down Expand Up @@ -174,3 +169,9 @@ const RegisterButton = styled.button`
color: slategray;
cursor: pointer;
`;
const ErrorMessage = styled.p`
color: red;
margin-top: 5px;
margin-bottom: 10px;
font-size: 0.8rem;
`;
7 changes: 5 additions & 2 deletions client/src/components/Logins/GoogleLoginButton.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
// GoogleLoginButton.tsx

import React from 'react';
import { useDispatch } from 'react-redux';
import { setLoginState } from '../../reducer/member/loginSlice';

interface Props {
backendURL: string;
}

const GoogleLoginButton: React.FC<Props> = ({ backendURL }) => {
const dispatch = useDispatch();

const handleLoginClick = () => {
window.location.href = `${backendURL}`;
dispatch(setLoginState()); // 로그인 상태를 변경합니다.
};

return (
Expand Down
5 changes: 5 additions & 0 deletions client/src/components/Logins/KakaoLoginButton.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
// KakaoLoginButton.tsx

import React from 'react';
import { useDispatch } from 'react-redux';
import { setLoginState } from '../../reducer/member/loginSlice';

interface Props {
backendURL: string;
}

const KakaoLoginButton: React.FC<Props> = ({ backendURL }) => {
const dispatch = useDispatch();

const handleLoginClick = () => {
window.location.href = `${backendURL}`;
dispatch(setLoginState()); // 로그인 상태를 변경합니다.
};

return (
Expand Down
5 changes: 3 additions & 2 deletions client/src/components/Logins/LoginConfirmatationModal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import styled from 'styled-components';

import TokenHandler from './TokenHandler';

const LoginConfirmationModal: React.FC<LoginConfirmationProps> = ({ onClose }) => {
const messageText = "로그인이 성공적으로 완료되었습니다!";
Expand All @@ -9,7 +9,8 @@ const LoginConfirmationModal: React.FC<LoginConfirmationProps> = ({ onClose }) =
return (
<ModalBackground>
<ModalContainer>
<Message>{messageText}</Message>
<TokenHandler />
<Message>{messageText}</Message>
<ConfirmButton onClick={onClose}>{confirmText}</ConfirmButton>
</ModalContainer>
</ModalBackground>
Expand Down
10 changes: 6 additions & 4 deletions client/src/components/Logins/OAuthLogin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,30 @@ import GoogleLoginButton from './GoogleLoginButton';
import KakaoLoginButton from './KakaoLoginButton';
import { useSelector } from 'react-redux';
import { RootState } from '../../store/config';
import TokenHandler from './TokenHandler';

const OAuthLoginModal: React.FC<LoginModalProps> = ({ onClose, onEmailLoginClick, onEmailSignupClick }) => {

const OAuthLoginModal: React.FC<LoginModalProps> = ({ onClose, onEmailLoginClick, onEmailSignupClick, onLoginSuccess }) => {
const titleText = "로그인";
const orText = "또는";
const emailLoginText = "이메일로 로그인";
const emailSignupText = "이메일로 회원가입";

const loginState = useSelector((state: RootState) => state.login);
console.log("Login State:", loginState);




useEffect(() => {
if (loginState === 1) {
onLoginSuccess();
onClose();
}
}, [loginState, onClose]);

return (
<ModalBackground>
<ModalContainer>
<TokenHandler />

<CloseButton onClick={onClose}>&times;</CloseButton>
<Title>{titleText}</Title>
<GoogleLoginButton backendURL="http://ec2-13-125-246-160.ap-northeast-2.compute.amazonaws.com:8080/oauth2/authorization/google" />
Expand All @@ -45,6 +46,7 @@ interface LoginModalProps {
onClose: () => void;
onEmailLoginClick: () => void;
onEmailSignupClick: () => void;
onLoginSuccess: () => void; // 추가된 부분
onWatchListClick: () => void;
onHoldingsClick: () => void;
}
Expand Down
47 changes: 31 additions & 16 deletions client/src/components/Signups/EmailCertify.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,50 +15,63 @@ const strings = {

// 이메일 인증 모달 컴포넌트
const EmailVerificationModal: React.FC<EmailVerificationModalProps> = ({ onClose, onNextStep, initialEmail }) => {
// 이메일 및 인증코드에 대한 상태를 선언합니다.
const [email, setEmail] = useState(initialEmail);
const [verificationCode, setVerificationCode] = useState('');

// 동의 상태와 알림 상태를 선언합니다.
const [hasAgreed, setHasAgreed] = useState(false);
const [showAgreementError, setShowAgreementError] = useState(false);
const [errorMessage, setErrorMessage] = useState<string | null>(null);

// 이메일 입력값을 처리하는 함수
const handleEmailChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setEmail(event.target.value);
};

// 인증코드 입력값을 처리하는 함수
const handleVerificationCodeChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setVerificationCode(event.target.value);
};

// 체크박스의 변경을 감지하는 핸들러
const handleAgreementChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setHasAgreed(event.target.checked);
setShowAgreementError(false); // 알림을 숨깁니다.
};

// 다음 단계 버튼 클릭시 이메일 인증을 처리하는 함수
const handleNextStepClick = async () => {
// 동의 확인
if (!hasAgreed) {
setShowAgreementError(true); // 알림을 표시합니다.
setShowAgreementError(true);
return;
}

try {
const response = await axios.post('http://ec2-13-125-246-160.ap-northeast-2.compute.amazonaws.com:8080/email/confirm', { email, code: verificationCode });
const response = await axios.post(
'http://ec2-13-125-246-160.ap-northeast-2.compute.amazonaws.com:8080/email/confirm',
{ email, code: verificationCode },
{
validateStatus: function (status) {
return status >= 200 && status < 600;
}
}
);

if (response.status === 200) {
onNextStep();
} else {
} else if (response.status === 400) {
setErrorMessage(response.data.message);
} else if (response.status === 500) {
setErrorMessage(JSON.stringify(response.data));
} else {
console.error('Error during email confirmation');
}
} catch (error) {
console.error('Error during email confirmation:', error);
if (axios.isAxiosError(error)) {
console.error('Error during email confirmation:', error);
if (error.response) {
console.error('Detailed server response:', error.response.data);
}
} else {
console.error('An unknown error occurred:', error);
}
}
};

return (
<ModalBackground>
<ModalContainer>
Expand All @@ -73,17 +86,19 @@ const EmailVerificationModal: React.FC<EmailVerificationModalProps> = ({ onClose
<input type="checkbox" id="terms" onChange={handleAgreementChange} />
<label htmlFor="terms">{strings.termsText}</label>
</TermsCheckbox>

{showAgreementError && <ErrorMessage>동의하지 않으면 진행할 수 없습니다</ErrorMessage>}
<SignupButton onClick={handleNextStepClick}>
{strings.nextStepText}
</SignupButton>
{errorMessage && <ErrorMessage>{errorMessage}</ErrorMessage>}
</ModalContainer>
</ModalBackground>
);
};
};

export default EmailVerificationModal;

export default EmailVerificationModal;

// 이메일 인증 모달의 Props 타입
type EmailVerificationModalProps = {
Expand Down
40 changes: 28 additions & 12 deletions client/src/components/Signups/EmailSignup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,42 +15,58 @@ const strings = {

const EmailSignupModal: React.FC<EmailSignupModalProps> = ({ onClose, onRequestVerification }) => {
const dispatch = useDispatch();
// 사용자가 입력한 이메일 상태
const [email, setEmail] = useState('');
const [isInvalidEmail, setIsInvalidEmail] = useState(false); // 이메일 유효성 상태

const [isInvalidEmail, setIsInvalidEmail] = useState(false);
const [errorMessage, setErrorMessage] = useState<string | null>(null);

// 이메일 입력 핸들러
const handleEmailChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setEmail(event.target.value);
setIsInvalidEmail(false);
};

//이메일 유효성 검사
const validateEmail = (email: string) => {
return email.includes('@') && email.includes('.com');
};

// 이메일 인증 요청 핸들러
const handleVerificationRequest = async () => {
if (!validateEmail(email)) {
setIsInvalidEmail(true);
return;
}

try {
const response = await axios.post('http://ec2-13-125-246-160.ap-northeast-2.compute.amazonaws.com:8080/email/send', { email });
const response = await axios.post(
'http://ec2-13-125-246-160.ap-northeast-2.compute.amazonaws.com:8080/email/send',
{ email },
{
validateStatus: function (status) {
return status >= 200 && status < 600; // Reject only if status code is greater than or equal to 600
}
}
);

if (response.status === 200) {
// 여기서 Redux store의 emailForVerification에 데이터 저장
dispatch(setEmailForVerification(email));
onRequestVerification(email);
} else if (response.status === 400) {
setErrorMessage(response.data.message);
} else if (response.status === 500) {
setErrorMessage(JSON.stringify(response.data));
} else {
console.error('Error sending verification email');
}
} catch (error) {
console.error('Error sending verification email:', error);
if (axios.isAxiosError(error)) {
console.error('Error sending verification email:', error);
if (error.response) {
console.error('Detailed server response:', error.response.data);
}
} else {
console.error('An unknown error occurred:', error);
}
}
};


return (
<ModalBackground>
Expand All @@ -63,17 +79,17 @@ const EmailSignupModal: React.FC<EmailSignupModalProps> = ({ onClose, onRequestV
<SignupButton onClick={handleVerificationRequest}>
{strings.requestVerificationText}
</SignupButton>
{errorMessage && <ErrorMessage>{errorMessage}</ErrorMessage>}
</ModalContainer>
</ModalBackground>
);
};

export default EmailSignupModal;

// 프롭 타입 정의
type EmailSignupModalProps = {
onClose: () => void;
onRequestVerification: (email: string) => void;
onRequestVerification: (email: string) => void;
};


Expand Down
Loading

0 comments on commit b311505

Please sign in to comment.