-
Notifications
You must be signed in to change notification settings - Fork 40
[신휘철] Sprint11 #332
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The head ref may contain hidden characters: "Next-\uC2E0\uD718\uCCA0-sprint11"
[신휘철] Sprint11 #332
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,183 @@ | ||
| import StyledInner from "@/src/layouts/StyledInner.style"; | ||
| import Image from "next/image"; | ||
| import Link from "next/link"; | ||
| import Logo from "@/src/assets/login/logo.svg"; | ||
| import GoogleLogin from "@/src/assets/login/easyLogin_google.svg"; | ||
| import KakaoLogin from "@/src/assets/login/easyLogin_kakao.svg"; | ||
| import EyeIcon from "@/src/assets/login/eye_icon.svg"; | ||
| import EyeShowIcon from "@/src/assets/login/eye_show_icon.svg"; | ||
| import { ChangeEvent, FocusEvent, FormEvent, useState } from "react"; | ||
| import { useRouter } from "next/router"; | ||
|
|
||
| type ValuesProps = { | ||
| email: string; | ||
| password: string; | ||
| }; | ||
|
|
||
| export default function Login() { | ||
| return <StyledInner>Login</StyledInner>; | ||
| const [loginInfo, setLoginInfo] = useState<ValuesProps>({ | ||
| email: "", | ||
| password: "", | ||
| }); | ||
| const [isPasswordHidden, setIsPasswordHidden] = useState(false); | ||
| const [emailErrorMessage, setEmailErrorMessage] = useState(""); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 에러가 없는 경우에 빈 문자열 보다는 null 같은 값으로 명시적으로 표시해주시는 것도 좋을 것 같습니다. |
||
| const [passwordErrorMessage, setPasswordErrorMessage] = useState(""); | ||
| const router = useRouter(); | ||
|
|
||
| const pattern = /^[A-Za-z0-9]+@[A-Za-z0-9]+\.[A-Za-z0-9]+/; | ||
|
|
||
| const loginValid = | ||
| pattern.test(loginInfo.email.trim()) && loginInfo.password.length >= 8; | ||
|
|
||
| const handleInputBlur = (e: ChangeEvent<HTMLInputElement>) => { | ||
| if (!e.target.value.trim()) { | ||
| setEmailErrorMessage("이메일을 입력해 주세요."); | ||
| } else if (!pattern.test(e.target.value.trim())) { | ||
| setEmailErrorMessage("잘못된 이메일 형식입니다."); | ||
| } else { | ||
| setEmailErrorMessage(""); | ||
| } | ||
| }; | ||
|
|
||
| const handleChange = (e: ChangeEvent<HTMLInputElement>) => { | ||
| const { name, value } = e.target; | ||
|
|
||
| setLoginInfo((prevLoginInfo) => ({ | ||
| ...prevLoginInfo, | ||
| [name]: value, | ||
| })); | ||
|
|
||
| if (name === "password") { | ||
| if (loginInfo.password.length < 7) { | ||
| setPasswordErrorMessage("비밀번호를 8자 이상 입력해 주세요."); | ||
| } else { | ||
| setPasswordErrorMessage(""); | ||
| } | ||
| } | ||
|
Comment on lines
+44
to
+55
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. password 상태가 반영된 후에 에러가 뜰 것 같아서 에러를 먼저 처리해주시고 상태가 반영되게 하는건 어떨까요? |
||
| }; | ||
|
|
||
| const handlePasswordBlur = (e: FocusEvent<HTMLInputElement>) => { | ||
| if (e.target.value.trim() === "") { | ||
| setPasswordErrorMessage("비밀번호를 입력해 주세요."); | ||
| } | ||
| }; | ||
|
|
||
| const handleClick = () => { | ||
| setIsPasswordHidden(!isPasswordHidden); | ||
| }; | ||
|
|
||
| const handleSubmit = async (e: FormEvent<HTMLFormElement>) => { | ||
| e.preventDefault(); | ||
|
|
||
| const res = await fetch("https://panda-market-api.vercel.app/auth/signIn", { | ||
| method: "POST", | ||
| headers: { | ||
| "Content-type": "application/json", | ||
| }, | ||
| body: JSON.stringify(loginInfo), | ||
| }); | ||
|
|
||
| const data = await res.json(); | ||
|
|
||
| if (res.ok) { | ||
| localStorage.setItem("accessToken", data.accessToken); | ||
| router.push("/"); | ||
| } | ||
| }; | ||
|
|
||
| return ( | ||
| <div className="wrapper login"> | ||
| <div className="inner"> | ||
| <h1> | ||
| <Link className="logo" href={"/"}> | ||
| <Image src={Logo} alt="logo" /> | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. next/image는 width / height나 fill이 없으면 잘 동작하지 않을텐데 괜찮나요? |
||
| </Link> | ||
| </h1> | ||
|
|
||
| <div className="loginWrap"> | ||
| <form onSubmit={handleSubmit}> | ||
| <div className="input"> | ||
| <label htmlFor="email-input">이메일</label> | ||
| <input | ||
| name="email" | ||
| id="email-input" | ||
| type="text" | ||
| placeholder="이메일을 입력해주세요." | ||
| autoComplete="username" | ||
| className={emailErrorMessage ? "error" : ""} | ||
| value={loginInfo.email} | ||
| onChange={handleChange} | ||
| onBlur={handleInputBlur} | ||
| /> | ||
|
|
||
| <span className="errorMessage">{emailErrorMessage}</span> | ||
| </div> | ||
|
|
||
| <div className="input"> | ||
| <label htmlFor="password-input">비밀번호</label> | ||
|
|
||
| <div className="password"> | ||
| <input | ||
| name="password" | ||
| id="password-input" | ||
| type={isPasswordHidden ? "text" : "password"} | ||
| placeholder="비밀번호를 입력해주세요" | ||
| autoComplete="current-password" | ||
| value={loginInfo.password} | ||
| onChange={handleChange} | ||
| onBlur={handlePasswordBlur} | ||
| /> | ||
| <button | ||
| className="passwordToggleBtn" | ||
| type="button" | ||
| onClick={handleClick} | ||
| > | ||
| {isPasswordHidden ? ( | ||
| <Image src={EyeShowIcon} alt="" /> | ||
| ) : ( | ||
| <Image src={EyeIcon} alt="" /> | ||
| )} | ||
| </button> | ||
| </div> | ||
|
|
||
| <span className="errorMessage">{passwordErrorMessage}</span> | ||
| </div> | ||
|
|
||
| <button className="loginBtn" type="submit" disabled={!loginValid}> | ||
| 로그인 | ||
| </button> | ||
| </form> | ||
| </div> | ||
|
|
||
| <div className="easyLogin"> | ||
| <p>간편 로그인하기</p> | ||
|
|
||
| <ul> | ||
| <li> | ||
| <Link | ||
| className="google" | ||
| href="https://google.com" | ||
| target="_blank" | ||
| > | ||
| <Image src={GoogleLogin} alt="구글 로그인" /> | ||
| </Link> | ||
| </li> | ||
|
|
||
| <li> | ||
| <Link | ||
| className="kakao" | ||
| href="https://www.kakaocorp.com/page/" | ||
| target="_blank" | ||
| > | ||
| <Image src={KakaoLogin} alt="카카오 로그인" /> | ||
| </Link> | ||
| </li> | ||
| </ul> | ||
| </div> | ||
|
|
||
| <p className="signup"> | ||
| 판다마켓이 처음이신가요? <Link href={"/signup"}>회원가입</Link> | ||
| </p> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저번에 말씀드렸던 getLayout 기능을 활용해서 페이지 별로 Layout을 지정해보시는 것도 좋을 것 같습니다.