find-id 디자인 수정중1

This commit is contained in:
wallace
2025-11-25 12:38:11 +09:00
parent 53dea825b0
commit 4c52627c2c
4 changed files with 85 additions and 45 deletions

View File

@@ -152,14 +152,23 @@ export default function NavBar() {
<Link <Link
role="menuitem" role="menuitem"
href="/menu/account" href="/menu/account"
className="block w-full h-10 px-2 rounded-lg text-left text-[#333C47] text-[16px] font-medium leading-normal hover:bg-[rgba(236,240,255,0.5)] focus:bg-[rgba(236,240,255,0.5)] outline-none" className="flex items-center w-[136px] h-10 px-2 rounded-lg text-left text-[#333C47] text-[16px] font-medium leading-normal hover:bg-[rgba(236,240,255,0.5)] focus:bg-[rgba(236,240,255,0.5)] outline-nonq"
onClick={() => setIsUserMenuOpen(false)} onClick={() => setIsUserMenuOpen(false)}
> >
</Link> </Link>
<button <button
role="menuitem" role="menuitem"
className="w-full h-10 px-2 rounded-lg text-left text-[#333C47] text-[16px] font-medium leading-normal hover:bg-[rgba(236,240,255,0.5)] focus:bg-[rgba(236,240,255,0.5)] outline-none" className="flex items-center w-[136px] h-10 px-2 rounded-lg text-left text-[#333C47] text-[16px] font-medium leading-normal hover:bg-[rgba(236,240,255,0.5)] focus:bg-[rgba(236,240,255,0.5)] outline-none"
onClick={() => {
// 로컬 스토리지에서 토큰 제거
localStorage.removeItem('token');
localStorage.removeItem('user');
// 쿠키에서 토큰 제거 (미들웨어에서 확인하므로)
document.cookie = 'token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
// 로그인 페이지로 리다이렉트
window.location.href = '/login';
}}
> >
</button> </button>

View File

@@ -111,7 +111,7 @@ export default function FindIdPage() {
onFocus={() => setFocused((p) => ({ ...p, name: true }))} onFocus={() => setFocused((p) => ({ ...p, name: true }))}
onBlur={() => setFocused((p) => ({ ...p, name: false }))} onBlur={() => setFocused((p) => ({ ...p, name: false }))}
placeholder="이름을 입력해 주세요." placeholder="이름을 입력해 주세요."
className="h-[40px] px-[12px] py-[7px] w-full rounded-[8px] border border-neutral-40 focus:outline-none focus:border-neutral-700 text-[18px] text-neutral-700 placeholder:text-input-placeholder-text pr-[40px]" className="h-[40px] px-[12px] py-[7px] w-full rounded-[8px] mt-3 border border-neutral-40 focus:outline-none focus:border-neutral-700 text-[18px] text-neutral-700 placeholder:text-input-placeholder-text pr-[40px]"
/> />
{name.trim().length > 0 && focused.name && ( {name.trim().length > 0 && focused.name && (
<button <button
@@ -144,7 +144,7 @@ export default function FindIdPage() {
onFocus={() => setFocused((p) => ({ ...p, phone: true }))} onFocus={() => setFocused((p) => ({ ...p, phone: true }))}
onBlur={() => setFocused((p) => ({ ...p, phone: false }))} onBlur={() => setFocused((p) => ({ ...p, phone: false }))}
placeholder="-없이 입력해 주세요." placeholder="-없이 입력해 주세요."
className="h-[40px] px-[12px] py-[7px] w-full rounded-[8px] border border-neutral-40 focus:outline-none focus:border-neutral-700 text-[18px] text-neutral-700 placeholder:text-input-placeholder-text pr-[40px]" className="h-[40px] px-[12px] py-[7px] w-full rounded-[8px] border mt-3 border-neutral-40 focus:outline-none focus:border-neutral-700 text-[18px] text-neutral-700 placeholder:text-input-placeholder-text pr-[40px]"
/> />
{phone.trim().length > 0 && focused.phone && ( {phone.trim().length > 0 && focused.phone && (
<button <button
@@ -164,7 +164,7 @@ export default function FindIdPage() {
<button <button
type="submit" type="submit"
className={`h-[40px] w-full rounded-[12px] text-[18px] font-semibold text-white ${canSubmit ? "bg-active-button" : "bg-inactive-button"} cursor-pointer`}> className={`h-[56px] w-full rounded-[12px] text-[18px] mt-[30px] font-semibold text-white hover:bg-[#1F2B91] ${canSubmit ? "bg-active-button" : "bg-inactive-button"} cursor-pointer`}>
</button> </button>

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import { useState } from "react"; import { useState, useEffect } from "react";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import Link from "next/link"; import Link from "next/link";
import MainLogo from "@/app/svgs/mainlogosvg" import MainLogo from "@/app/svgs/mainlogosvg"
@@ -22,9 +22,43 @@ export default function LoginPage() {
const [idError, setIdError] = useState(""); const [idError, setIdError] = useState("");
const [passwordError, setPasswordError] = useState(""); const [passwordError, setPasswordError] = useState("");
// 컴포넌트 마운트 시 저장된 아이디 불러오기
useEffect(() => {
const savedId = localStorage.getItem('savedUserId');
if (savedId) {
setUserId(savedId);
setRememberId(true);
}
}, []);
// 아이디 기억하기 상태나 아이디가 변경될 때마다 저장 처리
useEffect(() => {
if (rememberId && userId.trim()) {
localStorage.setItem('savedUserId', userId);
} else if (!rememberId) {
localStorage.removeItem('savedUserId');
}
}, [rememberId, userId]);
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) { async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault(); e.preventDefault();
if (userId.trim().length === 0 || password.trim().length === 0) {
// 에러 초기화
setIdError("");
setPasswordError("");
// 입력 검증
let hasError = false;
if (userId.trim().length === 0) {
setIdError("아이디를 입력해 주세요.");
hasError = true;
}
if (password.trim().length === 0) {
setPasswordError("비밀번호를 입력해 주세요.");
hasError = true;
}
if (hasError) {
return; return;
} }
@@ -74,6 +108,7 @@ export default function LoginPage() {
// 토큰이 없어도 로그인은 성공했으므로 진행 // 토큰이 없어도 로그인은 성공했으므로 진행
} }
// 리다이렉트 경로 확인 // 리다이렉트 경로 확인
const searchParams = new URLSearchParams(window.location.search); const searchParams = new URLSearchParams(window.location.search);
const redirectPath = searchParams.get('redirect') || '/'; const redirectPath = searchParams.get('redirect') || '/';
@@ -112,7 +147,7 @@ export default function LoginPage() {
</div> </div>
{/* 폼 */} {/* 폼 */}
<form onSubmit={handleSubmit} className="space-y-5"> <form onSubmit={handleSubmit} className="space-y-4">
<div className="space-y-4"> <div className="space-y-4">
{/* 아이디 */} {/* 아이디 */}
<div className="relative"> <div className="relative">
@@ -123,17 +158,14 @@ export default function LoginPage() {
id="userId" id="userId"
name="userId" name="userId"
value={userId} value={userId}
onChange={(e) => setUserId(e.target.value)} onChange={(e) => {
setUserId(e.target.value);
if (idError) setIdError("");
}}
onFocus={() => setIsUserIdFocused(true)} onFocus={() => setIsUserIdFocused(true)}
onBlur={() => setIsUserIdFocused(false)} onBlur={() => setIsUserIdFocused(false)}
placeholder="아이디(이메일)" placeholder="아이디(이메일)"
className=" className={`h-[56px] px-[12px] py-[7px] w-full rounded-[8px] border focus:outline-none focus:ring-0 focus:ring-offset-0 focus:shadow-none focus:appearance-none text-[18px] text-neutral-700 font-normal leading-[150%] placeholder:text-input-placeholder-text pr-[40px] ${idError ? 'border-error' : 'border-neutral-40 focus:border-neutral-700'}`}
h-[40px] px-[12px] py-[7px] w-full rounded-[8px] border border-neutral-40
focus:outline-none focus:ring-0 focus:ring-offset-0 focus:shadow-none
focus:appearance-none focus:border-neutral-700
text-[18px] text-neutral-700 font-normal leading-[150%] placeholder:text-input-placeholder-text
pr-[40px]
"
/> />
{userId.trim().length > 0 && isUserIdFocused && ( {userId.trim().length > 0 && isUserIdFocused && (
<button <button
@@ -149,6 +181,7 @@ export default function LoginPage() {
</button> </button>
)} )}
</div> </div>
{idError && <p className="text-error text-[13px] leading-tight mt-[10px]">{idError}</p>}
{/* 비밀번호 */} {/* 비밀번호 */}
<div className="relative"> <div className="relative">
<label htmlFor="password" className="sr-only"> <label htmlFor="password" className="sr-only">
@@ -159,17 +192,14 @@ export default function LoginPage() {
name="password" name="password"
type="password" type="password"
value={password} value={password}
onChange={(e) => setPassword(e.target.value)} onChange={(e) => {
setPassword(e.target.value);
if (passwordError) setPasswordError("");
}}
onFocus={() => setIsPasswordFocused(true)} onFocus={() => setIsPasswordFocused(true)}
onBlur={() => setIsPasswordFocused(false)} onBlur={() => setIsPasswordFocused(false)}
placeholder="비밀번호" placeholder="비밀번호 입력"
className=" className={`h-[56px] px-[12px] py-[7px] rounded-[8px] w-full border focus:outline-none focus:ring-0 focus:ring-offset-0 focus:shadow-none focus:appearance-none text-[18px] text-neutral-700 font-normal leading-[150%] placeholder:text-input-placeholder-text pr-[40px] ${passwordError ? 'border-error' : 'border-neutral-40 focus:border-neutral-700'}`}
h-[40px] px-[12px] py-[7px] rounded-[8px] w-full border border-neutral-40
focus:outline-none focus:ring-0 focus:ring-offset-0 focus:shadow-none
focus:appearance-none focus:border-neutral-700
text-[18px] text-neutral-700 font-normal leading-[150%] placeholder:text-input-placeholder-text
pr-[40px]
"
/> />
{password.trim().length > 0 && isPasswordFocused && ( {password.trim().length > 0 && isPasswordFocused && (
<button <button
@@ -185,6 +215,7 @@ export default function LoginPage() {
</button> </button>
)} )}
</div> </div>
{passwordError && <p className="text-error text-[13px] leading-tight mt-[4px]">{passwordError}</p>}
</div> </div>
{/* 체크박스들 */} {/* 체크박스들 */}
@@ -222,7 +253,7 @@ export default function LoginPage() {
{/* 로그인 버튼 */} {/* 로그인 버튼 */}
<button <button
type="submit" type="submit"
className={`h-[40px] w-full rounded-lg text-[16px] font-semibold text-white transition-opacity cursor-pointer mb-3 ${userId.trim().length > 0 && password.trim().length > 0 ? "bg-active-button" : "bg-inactive-button"}`} className={`h-[56px] w-full rounded-lg text-[16px] font-semibold text-white transition-opacity cursor-pointer mb-3 hover:bg-[#1F2B91] ${userId.trim().length > 0 && password.trim().length > 0 ? "bg-active-button" : "bg-inactive-button"}`}
> >
</button> </button>

View File

@@ -290,7 +290,7 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
onFocus={() => setFocused((p) => ({ ...p, name: true }))} onFocus={() => setFocused((p) => ({ ...p, name: true }))}
onBlur={() => setFocused((p) => ({ ...p, name: false }))} onBlur={() => setFocused((p) => ({ ...p, name: false }))}
placeholder="이름을 입력해 주세요." placeholder="이름을 입력해 주세요."
className="h-[40px] px-[12px] py-[7px] w-full rounded-[8px] border border-neutral-40 focus:outline-none focus:border-neutral-700 text-[18px] text-neutral-700 placeholder:text-input-placeholder-text pr-[40px]" className={`h-[40px] px-[12px] py-[7px] w-full rounded-[8px] border focus:outline-none text-[18px] text-neutral-700 placeholder:text-input-placeholder-text pr-[40px] ${errors.name ? 'border-error' : 'border-neutral-40 focus:border-neutral-700'}`}
/> />
{name.trim().length > 0 && focused.name && ( {name.trim().length > 0 && focused.name && (
<button <button
@@ -303,7 +303,7 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
</button> </button>
)} )}
</div> </div>
{errors.name && <p className="text-error text-[13px] leading-tight">{errors.name}</p>} {errors.name && <p className="text-error text-[13px] leading-tight mt-[10px]">{errors.name}</p>}
</div> </div>
{/* 휴대폰 번호 */} {/* 휴대폰 번호 */}
@@ -320,7 +320,7 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
onChange={(e) => setPhone(e.target.value.replace(/[^0-9]/g, ""))} onChange={(e) => setPhone(e.target.value.replace(/[^0-9]/g, ""))}
onFocus={() => setFocused((p) => ({ ...p, phone: true }))} onFocus={() => setFocused((p) => ({ ...p, phone: true }))}
onBlur={() => setFocused((p) => ({ ...p, phone: false }))} onBlur={() => setFocused((p) => ({ ...p, phone: false }))}
className="h-[40px] px-[12px] py-[7px] w-full rounded-[8px] border border-neutral-40 focus:outline-none focus:border-neutral-700 text-[18px] text-neutral-700 placeholder:text-input-placeholder-text pr-[40px]" className={`h-[40px] px-[12px] py-[7px] w-full rounded-[8px] border focus:outline-none text-[18px] text-neutral-700 placeholder:text-input-placeholder-text pr-[40px] ${errors.phone ? 'border-error' : 'border-neutral-40 focus:border-neutral-700'}`}
/> />
{phone.trim().length > 0 && focused.phone && ( {phone.trim().length > 0 && focused.phone && (
<button <button
@@ -333,7 +333,7 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
</button> </button>
)} )}
</div> </div>
{errors.phone && <p className="text-error text-[13px] leading-tight">{errors.phone}</p>} {errors.phone && <p className="text-error text-[13px] leading-tight mt-[10px]">{errors.phone}</p>}
</div> </div>
{/* 아이디(이메일) + 인증번호 전송 */} {/* 아이디(이메일) + 인증번호 전송 */}
@@ -361,7 +361,7 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
onBlur={() => setFocused((p) => ({ ...p, email: false }))} onBlur={() => setFocused((p) => ({ ...p, email: false }))}
placeholder="이메일을 입력해 주세요." placeholder="이메일을 입력해 주세요."
disabled={emailCodeVerified} disabled={emailCodeVerified}
className="h-[40px] px-[12px] py-[7px] w-full rounded-[8px] border border-neutral-40 focus:outline-none focus:border-neutral-700 text-[18px] text-neutral-700 placeholder:text-input-placeholder-text pr-[40px] disabled:bg-gray-100 disabled:cursor-not-allowed" className={`h-[40px] px-[12px] py-[7px] w-full rounded-[8px] border focus:outline-none text-[18px] text-neutral-700 placeholder:text-input-placeholder-text pr-[40px] disabled:bg-gray-100 disabled:cursor-not-allowed ${emailCodeVerified ? '' : (errors.email ? 'border-error' : 'border-neutral-40 focus:border-neutral-700')}`}
/> />
{email.trim().length > 0 && focused.email && !emailCodeVerified && ( {email.trim().length > 0 && focused.email && !emailCodeVerified && (
<button <button
@@ -383,7 +383,7 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
{emailCodeSent && !emailCodeVerified ? "인증번호 재전송" : "인증번호 전송"} {emailCodeSent && !emailCodeVerified ? "인증번호 재전송" : "인증번호 전송"}
</button> </button>
</div> </div>
{errors.email && <p className="text-error text-[13px] leading-tight">{errors.email}</p>} {errors.email && <p className="text-error text-[13px] leading-tight mt-[10px]">{errors.email}</p>}
{emailCodeSent && ( {emailCodeSent && (
<div className="space-y-2" aria-expanded={emailCodeSent}> <div className="space-y-2" aria-expanded={emailCodeSent}>
<label htmlFor="emailCode" className="sr-only"></label> <label htmlFor="emailCode" className="sr-only"></label>
@@ -452,7 +452,7 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
onFocus={() => setFocused((p) => ({ ...p, password: true }))} onFocus={() => setFocused((p) => ({ ...p, password: true }))}
onBlur={() => setFocused((p) => ({ ...p, password: false }))} onBlur={() => setFocused((p) => ({ ...p, password: false }))}
placeholder="8~16자의 영문/숫자/특수문자를 조합해서 입력해 주세요." placeholder="8~16자의 영문/숫자/특수문자를 조합해서 입력해 주세요."
className="h-[40px] px-[12px] py-[7px] w-full rounded-[8px] border border-neutral-40 focus:outline-none focus:border-neutral-700 text-[18px] text-neutral-700 placeholder:text-input-placeholder-text pr-[40px]" className={`h-[40px] px-[12px] py-[7px] w-full rounded-[8px] border focus:outline-none text-[18px] text-neutral-700 placeholder:text-input-placeholder-text pr-[40px] ${errors.password ? 'border-error' : 'border-neutral-40 focus:border-neutral-700'}`}
/> />
{password.trim().length > 0 && focused.password && ( {password.trim().length > 0 && focused.password && (
<button <button
@@ -465,7 +465,7 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
</button> </button>
)} )}
</div> </div>
{errors.password && <p className="text-error text-[13px] leading-tight">{errors.password}</p>} {errors.password && <p className="text-error text-[13px] leading-tight mt-[10px]">{errors.password}</p>}
</div> </div>
{/* 비밀번호 확인 */} {/* 비밀번호 확인 */}
@@ -481,7 +481,7 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
onFocus={() => setFocused((p) => ({ ...p, passwordConfirm: true }))} onFocus={() => setFocused((p) => ({ ...p, passwordConfirm: true }))}
onBlur={() => setFocused((p) => ({ ...p, passwordConfirm: false }))} onBlur={() => setFocused((p) => ({ ...p, passwordConfirm: false }))}
placeholder="비밀번호를 다시 입력해 주세요." placeholder="비밀번호를 다시 입력해 주세요."
className="h-[40px] px-[12px] py-[7px] w-full rounded-[8px] border border-neutral-40 focus:outline-none focus:border-neutral-700 text-[18px] text-neutral-700 placeholder:text-input-placeholder-text pr-[40px]" className={`h-[40px] px-[12px] py-[7px] w-full rounded-[8px] border focus:outline-none text-[18px] text-neutral-700 placeholder:text-input-placeholder-text pr-[40px] ${errors.passwordConfirm ? 'border-error' : 'border-neutral-40 focus:border-neutral-700'}`}
/> />
{passwordConfirm.trim().length > 0 && focused.passwordConfirm && ( {passwordConfirm.trim().length > 0 && focused.passwordConfirm && (
<button <button
@@ -494,7 +494,7 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
</button> </button>
)} )}
</div> </div>
{errors.passwordConfirm && <p className="text-error text-[13px] leading-tight">{errors.passwordConfirm}</p>} {errors.passwordConfirm && <p className="text-error text-[13px] leading-tight mt-[10px]">{errors.passwordConfirm}</p>}
</div> </div>
{/* 성별 */} {/* 성별 */}
@@ -530,7 +530,7 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
</label> </label>
</div> </div>
{errors.gender && <p className="text-error text-[13px] leading-tight">{errors.gender}</p>} {errors.gender && <p className="text-error text-[13px] leading-tight mt-[10px]">{errors.gender}</p>}
</div> </div>
{/* 생년월일 */} {/* 생년월일 */}
@@ -561,7 +561,7 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
} }
}} }}
placeholder="생년월일" placeholder="생년월일"
className="h-[40px] px-[12px] w-full rounded-[8px] border border-neutral-40 focus:outline-none focus:border-neutral-700 text-[18px] text-neutral-700 placeholder:text-input-placeholder-text pr-[40px] flex items-center" className={`h-[40px] px-[12px] w-full rounded-[8px] border focus:outline-none text-[18px] text-neutral-700 placeholder:text-input-placeholder-text pr-[40px] flex items-center ${errors.birthdate ? 'border-error' : 'border-neutral-40 focus:border-neutral-700'}`}
/> />
<button <button
type="button" type="button"
@@ -586,7 +586,7 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
className="absolute right-50 top-1 h-full w-[20px] opacity-0 cursor-pointer" className="absolute right-50 top-1 h-full w-[20px] opacity-0 cursor-pointer"
/> />
</div> </div>
{errors.birthdate && <p className="text-error text-[13px] leading-tight">{errors.birthdate}</p>} {errors.birthdate && <p className="text-error text-[13px] leading-tight mt-[10px]">{errors.birthdate}</p>}
</div> </div>
{/* 약관 동의 */} {/* 약관 동의 */}
@@ -640,11 +640,11 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
</span> </span>
</label> </label>
</div> </div>
{errors.agreements && <p className="text-error text-[13px] leading-tight">{errors.agreements}</p>} {errors.agreements && <p className="text-error text-[13px] leading-tight mt-[10px]">{errors.agreements}</p>}
</div> </div>
{/* 액션 버튼 */} {/* 액션 버튼 */}
<div className="flex gap-3"> <div className="flex gap-3 pt-[60px]">
<Link <Link
href="/login" href="/login"
className="h-[40px] flex-1 rounded-[12px] bg-[#f1f3f5] text-[18px] font-semibold text-center flex items-center justify-center text-basic-text" className="h-[40px] flex-1 rounded-[12px] bg-[#f1f3f5] text-[18px] font-semibold text-center flex items-center justify-center text-basic-text"