diff --git a/src/pages/Login/LoginPage.tsx b/src/pages/Login/LoginPage.tsx index 2845eee..b9227bf 100644 --- a/src/pages/Login/LoginPage.tsx +++ b/src/pages/Login/LoginPage.tsx @@ -10,16 +10,14 @@ declare global { } } +// Swagger 실제 응답 스키마에 맞춰 data 내부 필드를 최소화합니다. interface KakaoUserInfoResponse { success: boolean; message: string; data: { - accessToken: string; + accessToken: string; // 백엔드가 내려주는 JWT 토큰 tokenType: string; expiresIn: number; - id?: number; - has_signed_up?: boolean; - properties?: { nickname: string }; }; } @@ -27,27 +25,27 @@ const LoginPage: React.FC = () => { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); - // 이미 jwtToken이 있으면 /login(id === undefined)이 아니라 바로 홈으로 + // 이미 jwtToken이 있으면 id(param)가 undefined인 순수 로그인 화면이 아니라 곧바로 홈으로 리다이렉트 useEffect(() => { const token = localStorage.getItem("jwtToken"); if (!id && token) { window.location.href = "/"; } - }, [id, navigate]); + }, [id]); - // 카카오 SDK 초기화 + // 카카오 SDK 초기화 (페이지 로드 시 한 번만 수행) useEffect(() => { if (window.Kakao && !window.Kakao.isInitialized()) { window.Kakao.init(import.meta.env.VITE_KAKAO_JS_KEY); } }, []); + // 회원가입 1단계(닉네임 입력)과 2단계(팀 선택)를 위한 상태 const [nickname, setNickname] = useState(""); - const [nicknameCheckResult, setNicknameCheckResult] = useState< - "available" | "duplicate" | null - >(null); + const [nicknameCheckResult, setNicknameCheckResult] = useState<"available" | "duplicate" | null>(null); const [selectedTeam, setSelectedTeam] = useState(null); + // 카카오 로그인 버튼 클릭 시 실행될 함수 const kakaoLogin = () => { if (!window.Kakao) { return alert("카카오 SDK가 로드되지 않았습니다."); @@ -58,26 +56,27 @@ const LoginPage: React.FC = () => { throughTalk: false, success: async (authObj: any) => { try { - // 1) 카카오 accessToken으로 백엔드 로그인 + // 1) 카카오 access_token을 백엔드로 전달하여 우리 서버에서 JWT 발급받기 const res = await fetch( `${import.meta.env.VITE_API_URL}/api/kakao/user-info`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ accessToken: authObj.access_token }), - }, + } ); - if (!res.ok) throw new Error(`HTTP ${res.status}`); + if (!res.ok) { + throw new Error(`HTTP ${res.status}`); + } + + // 2) 백엔드가 내려준 JSON 파싱 (응답 스키마에 따라 interface를 수정했기 때문에 data 내부에는 accessToken, tokenType, expiresIn만 있음) const resp = (await res.json()) as KakaoUserInfoResponse; - // 2) JWT 저장 + // 3) JWT를 로컬스토리지에 저장 const jwt = resp.data.accessToken; localStorage.setItem("jwtToken", jwt); - if (resp.data.id !== undefined) { - localStorage.setItem("userIdx", String(resp.data.id)); - } - // 3) 가입 여부 조회 (/login/hasSignedIn) + // 4) 가입 여부 조회 (hasSignedIn) const hasRes = await fetch( `${import.meta.env.VITE_API_URL}/login/hasSignedIn`, { @@ -86,25 +85,24 @@ const LoginPage: React.FC = () => { "Content-Type": "application/json", Authorization: `Bearer ${jwt}`, }, - }, + } ); - if (!hasRes.ok) + if (!hasRes.ok) { throw new Error(`hasSignedIn 호출 실패: ${hasRes.status}`); - const hasJson = (await hasRes.json()) as { + } + + const hasJson = await hasRes.json() as { success: boolean; message: string; data: { hasSignedIn: boolean }; }; - // 4) 분기 처리 + // 5) 분기 처리 if (hasJson.data.hasSignedIn) { - // 이미 모두 완료된 회원: 홈으로 + // 이미 가입된 회원: 홈으로 이동 window.location.href = "/"; } else { - // 닉네임/응원팀 설정 필요 - if (resp.data.properties?.nickname) { - setNickname(resp.data.properties.nickname); - } + // 신규 회원: 닉네임/응원팀 설정 단계로 이동 navigate("/signup/1"); } } catch (err) { @@ -119,30 +117,37 @@ const LoginPage: React.FC = () => { }); }; + // 회원 가입 단계별 “다음” 버튼 핸들러 const goStep2 = () => { - if (nicknameCheckResult === "available") navigate("/signup/2"); - else alert("닉네임 중복 체크를 완료해주세요!"); + if (nicknameCheckResult === "available") { + navigate("/signup/2"); + } else { + alert("닉네임 중복 체크를 완료해주세요!"); + } }; const goStep3 = () => { - if (selectedTeam) navigate("/signup/3"); - else alert("응원팀을 선택해주세요!"); + if (selectedTeam) { + navigate("/signup/3"); + } else { + alert("응원팀을 선택해주세요!"); + } }; - // id 파라미터 없으면 순수 로그인 화면 + // id 파라미터가 없으면 순수 로그인 화면을 렌더링 if (!id) { return (

야구를 더 가까이, 더 즐겁게

DUGOUT

+
- - SNS로 간편 로그인 - + SNS로 간편 로그인
+
+

계속 진행 시{" "}