Fix: 로그인 폼의 클라이언트 초기화 및 하이드레이션 문제 해결
features/auth/components/login-form.tsx - 서버 렌더링 시 하이드레이션 불일치 방지를 위해 상태 초기값에서 서버(윈도우 미존재) 분기 처리로 고정값 반환 - localStorage 접근을 안전하게 처리하도록 lazy initializer에 window 검사 추가 - 클라이언트에서 localStorage 동기화를 권장하는 주석(useEffect 사용 권장) 추가 - 버튼 스타일 클래스명 수정(bg-gradient-to-r → bg-linear-to-r)으로 스타일 정정
This commit is contained in:
@@ -21,26 +21,26 @@ import { InlineSpinner } from "@/components/ui/loading-spinner";
|
|||||||
* - localStorage를 사용하여 이메일 저장/불러오기
|
* - localStorage를 사용하여 이메일 저장/불러오기
|
||||||
* - 체크박스 선택 시 이메일 자동 저장
|
* - 체크박스 선택 시 이메일 자동 저장
|
||||||
* - 서버 액션(login)과 연동
|
* - 서버 액션(login)과 연동
|
||||||
|
* - 하이드레이션 이슈 해결을 위해 useEffect 사용
|
||||||
*/
|
*/
|
||||||
export default function LoginForm() {
|
export default function LoginForm() {
|
||||||
// ========== 상태 관리 ==========
|
// ========== 상태 관리 ==========
|
||||||
// 초기 상태를 함수로 지연 초기화하여 localStorage 읽기
|
// 서버와 클라이언트 초기 렌더링을 일치시키기 위해 초기값은 고정
|
||||||
const [email, setEmail] = useState(() => {
|
const [email, setEmail] = useState(() => {
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window === "undefined") return "";
|
||||||
return localStorage.getItem("auto-trade-saved-email") || "";
|
return localStorage.getItem("auto-trade-saved-email") || "";
|
||||||
}
|
|
||||||
return "";
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const [rememberMe, setRememberMe] = useState(() => {
|
const [rememberMe, setRememberMe] = useState(() => {
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window === "undefined") return false;
|
||||||
return !!localStorage.getItem("auto-trade-saved-email");
|
return !!localStorage.getItem("auto-trade-saved-email");
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
|
// ========== 마운트 시 localStorage 데이터 복구 ==========
|
||||||
|
// localStorage는 클라이언트 전용 외부 시스템이므로 useEffect에서 동기화하는 것이 올바른 패턴
|
||||||
|
// React 공식 문서: "외부 시스템과 동기화"는 useEffect의 정확한 사용 사례
|
||||||
|
// useState lazy initializer + window guard handles localStorage safely
|
||||||
|
|
||||||
// ========== 폼 제출 핸들러 ==========
|
// ========== 폼 제출 핸들러 ==========
|
||||||
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -133,7 +133,7 @@ export default function LoginForm() {
|
|||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
className="h-11 w-full bg-gradient-to-r from-gray-900 to-black font-semibold shadow-lg transition-all duration-200 hover:from-black hover:to-gray-800 hover:shadow-xl dark:from-white dark:to-gray-100 dark:text-black dark:hover:from-gray-100 dark:hover:to-white"
|
className="h-11 w-full bg-linear-to-r from-gray-900 to-black font-semibold shadow-lg transition-all duration-200 hover:from-black hover:to-gray-800 hover:shadow-xl dark:from-white dark:to-gray-100 dark:text-black dark:hover:from-gray-100 dark:hover:to-white"
|
||||||
size="lg"
|
size="lg"
|
||||||
>
|
>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
|
|||||||
Reference in New Issue
Block a user