@@ -3,101 +3,22 @@ import { useNavigate } from "react-router-dom";
33import logo from "/assets/logo.png" ;
44import styles from "@styles/Home.module.css" ;
55
6- const {
7- VITE_KAKAO_REST_API_KEY ,
8- VITE_KAKAO_REDIRECT_URI ,
9- VITE_GOOGLE_CLIENT_ID ,
10- VITE_GOOGLE_REDIRECT_URI ,
11- VITE_NAVER_CLIENT_ID ,
12- VITE_NAVER_REDIRECT_URI ,
13- } = import . meta. env ;
6+ const API_URL = import . meta. env . VITE_API_URL || "" ;
147
15- /** base64url(+) */
16- function base64UrlEncode ( bytes : ArrayBuffer ) {
17- const uint8 = new Uint8Array ( bytes ) ;
18- let bin = "" ;
19- for ( const b of uint8 ) bin += String . fromCharCode ( b ) ;
20- return btoa ( bin ) . replace ( / \+ / g, "-" ) . replace ( / \/ / g, "_" ) . replace ( / = + $ / , "" ) ;
21- }
22-
23- /** sha256 */
24- async function sha256 ( text : string ) {
25- const data = new TextEncoder ( ) . encode ( text ) ;
26- return crypto . subtle . digest ( "SHA-256" , data ) ;
27- }
28-
29- /** PKCE code_verifier (43~128 chars 권장) */
30- function makeCodeVerifier ( len = 64 ) {
31- const chars =
32- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~" ;
33- const bytes = crypto . getRandomValues ( new Uint8Array ( len ) ) ;
34- return Array . from ( bytes , ( b ) => chars [ b % chars . length ] ) . join ( "" ) ;
35- }
8+ const SOCIAL_LOGIN_ENTRY = {
9+ google : `${ API_URL } /auth/login/google` ,
10+ kakao : `${ API_URL } /auth/login/kakao` ,
11+ naver : `${ API_URL } /auth/login/naver` ,
12+ } as const ;
3613
3714export default function Home ( ) {
3815 const navigate = useNavigate ( ) ;
3916
4017 const toLogin = ( ) => navigate ( "/auth/login" ) ;
4118 const toSignUp = ( ) => navigate ( "/auth/signup" ) ;
4219
43- // ✅ Kakao: state 생성/저장 + URLSearchParams
44- const handleKakaoLogin = ( ) => {
45- const state = crypto . randomUUID ( ) ;
46- sessionStorage . setItem ( "kakao_oauth_state" , state ) ;
47-
48- const url = new URL ( "https://kauth.kakao.com/oauth/authorize" ) ;
49- url . search = new URLSearchParams ( {
50- client_id : VITE_KAKAO_REST_API_KEY ,
51- redirect_uri : VITE_KAKAO_REDIRECT_URI ,
52- response_type : "code" ,
53- state,
54- } ) . toString ( ) ;
55-
56- window . location . href = url . toString ( ) ;
57- } ;
58-
59- // ✅ Google: PKCE + state 생성/저장 + URLSearchParams
60- const handleGoogleLogin = async ( ) => {
61- const state = crypto . randomUUID ( ) ;
62- const codeVerifier = makeCodeVerifier ( 64 ) ;
63- const digest = await sha256 ( codeVerifier ) ;
64- const codeChallenge = base64UrlEncode ( digest ) ;
65-
66- sessionStorage . setItem ( "google_oauth_state" , state ) ;
67- sessionStorage . setItem ( "google_code_verifier" , codeVerifier ) ;
68-
69- const url = new URL ( "https://accounts.google.com/o/oauth2/v2/auth" ) ;
70- url . search = new URLSearchParams ( {
71- client_id : VITE_GOOGLE_CLIENT_ID ,
72- redirect_uri : VITE_GOOGLE_REDIRECT_URI ,
73- response_type : "code" ,
74- scope : "openid email profile" ,
75- include_granted_scopes : "true" ,
76- state,
77- code_challenge : codeChallenge ,
78- code_challenge_method : "S256" ,
79- // 필요하면 추가:
80- // access_type: "offline",
81- // prompt: "consent",
82- } ) . toString ( ) ;
83-
84- window . location . href = url . toString ( ) ;
85- } ;
86-
87- // ✅ Naver: 클릭 시점에서 state 생성/저장
88- const handleNaverLogin = ( ) => {
89- const state = crypto . randomUUID ( ) ;
90- sessionStorage . setItem ( "naver_oauth_state" , state ) ;
91-
92- const url = new URL ( "https://nid.naver.com/oauth2.0/authorize" ) ;
93- url . search = new URLSearchParams ( {
94- response_type : "code" ,
95- client_id : VITE_NAVER_CLIENT_ID ,
96- redirect_uri : VITE_NAVER_REDIRECT_URI ,
97- state,
98- } ) . toString ( ) ;
99-
100- window . location . href = url . toString ( ) ;
20+ const moveToSocialLogin = ( provider : keyof typeof SOCIAL_LOGIN_ENTRY ) => {
21+ window . location . href = SOCIAL_LOGIN_ENTRY [ provider ] ;
10122 } ;
10223
10324 return (
@@ -117,7 +38,7 @@ export default function Home() {
11738 className = { `${ styles . btn } ${ styles . social } ` }
11839 data-provider = "google"
11940 type = "button"
120- onClick = { handleGoogleLogin }
41+ onClick = { ( ) => moveToSocialLogin ( "google" ) }
12142 >
12243 < span > 구글 계정으로 계속하기</ span >
12344 </ button >
@@ -126,7 +47,7 @@ export default function Home() {
12647 className = { `${ styles . btn } ${ styles . social } ` }
12748 data-provider = "kakao"
12849 type = "button"
129- onClick = { handleKakaoLogin }
50+ onClick = { ( ) => moveToSocialLogin ( "kakao" ) }
13051 >
13152 < span > 카카오톡 계정으로 계속하기</ span >
13253 </ button >
@@ -135,7 +56,7 @@ export default function Home() {
13556 className = { `${ styles . btn } ${ styles . social } ` }
13657 data-provider = "naver"
13758 type = "button"
138- onClick = { handleNaverLogin }
59+ onClick = { ( ) => moveToSocialLogin ( "naver" ) }
13960 >
14061 < span > 네이버 계정으로 계속하기</ span >
14162 </ button >
0 commit comments