Skip to content

Commit

Permalink
Refactor: 인증 인가 서버 route 처리 및 서버 컴포넌트 가딩 처리 1단계 작업 (#82)
Browse files Browse the repository at this point in the history
* WIP: social-login server component 단 처리중

* Fix: social-login server route 처리 및 예외 처리 추가
  • Loading branch information
minchodang authored Sep 19, 2024
1 parent e6b5d5e commit 2772a12
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 40 deletions.
2 changes: 1 addition & 1 deletion src/app/(verify_guard)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const reissueToken = async () => {

const checkToken = async (accessToken: string | undefined, refreshToken: string | undefined) => {
try {
await axios(`${process.env.NEXT_PUBLIC_META_TEST_SERVER_HOST_URL}/v1/me`, {
await axios(`${process.env.NEXT_PUBLIC_META_TEST_SERVER_HOST_URL}/users`, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
Expand Down
17 changes: 9 additions & 8 deletions src/app/api/auth/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ export async function POST(request: NextRequest) {
);
console.log(res.data);

const { accessToken, refreshToken } = res.data;
const { refresh_token } = res.data;
const accessToken = res.headers.access_token;

// Refresh Token을 HttpOnly 쿠키로 설정 (기간 2주)
cookies().set('rtk', refreshToken, {
cookies().set('rtk', refresh_token, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
maxAge: 60 * 60 * 24 * 14, // 2주
Expand Down Expand Up @@ -57,22 +58,22 @@ export async function PUT(request: NextRequest) {
const res = await axios.post(
`${process.env.NEXT_PUBLIC_META_TEST_SERVER_HOST_URL}/auth/token/refresh`,
{
refreshToken,
accessToken,
refresh_Token: refreshToken,
},
);

const newResponse = res;
const newAccessToken = res.headers.access_token;
const newRefreshToken = res.data.refresh_token;

// 새로운 Refresh Token을 HttpOnly 쿠키로 설정
cookies().set('rtk', newResponse.data.refreshToken, {
cookies().set('rtk', newRefreshToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
maxAge: 60 * 60 * 24 * 14, // 2주
path: '/',
sameSite: 'strict',
});
cookies().set('atk', newResponse.data.accessToken, {
cookies().set('atk', newAccessToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
maxAge: 60 * 30, // 30분
Expand All @@ -82,7 +83,7 @@ export async function PUT(request: NextRequest) {

// 새로운 Access Token을 응답 헤더로 클라이언트에 전달
const response = NextResponse.json({ success: true });
response.headers.set('Authorization', `Bearer ${newResponse.data.accessToken}`);
response.headers.set('Authorization', `Bearer ${newAccessToken}`);
return response;
} catch (error) {
console.log(error, '리이슈');
Expand Down
60 changes: 60 additions & 0 deletions src/app/api/social-login/route.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { SocialLoginRequestParameter } from '@src/app/auth/page';
import axios, { isAxiosError } from 'axios';
import { cookies } from 'next/headers';
import { NextRequest, NextResponse } from 'next/server';

const metaTestServerHost = process.env.NEXT_PUBLIC_META_TEST_SERVER_HOST_URL;

export async function POST(request: NextRequest) {
const { code, socialType }: Omit<SocialLoginRequestParameter, 'loginPath'> =
await request.json();

if (!code || !socialType) {
return NextResponse.json({ error: 'Bad Request' }, { status: 400 });
}

try {
const serverResponse = await axios.get(`${metaTestServerHost}/auth/login/${socialType}`, {
params: {
code,
},
});

const accessToken = serverResponse.headers.access_token;
const refreshToken = serverResponse.headers.refresh_token;

// NextResponse에 쿠키 설정
const response = NextResponse.json({ success: true });

// Refresh Token을 HttpOnly 쿠키로 설정 (기간 2주)
cookies().set('rtk', refreshToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
maxAge: 60 * 60 * 24 * 14, // 2주
path: '/',
sameSite: 'strict',
});

// Access Token을 HttpOnly 쿠키로 설정 (기간 30분)
cookies().set('atk', accessToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
maxAge: 60 * 30, // 30분
path: '/',
sameSite: 'strict',
});
// 새로운 Access Token을 응답 헤더로 클라이언트에 전달
response.headers.set('Authorization', `Bearer ${accessToken}`);
return response;
} catch (error) {
if (isAxiosError(error) && error.response?.data) {
return NextResponse.json(
{ error: error.response.data.message },
{
status: error.response?.status || 500,
},
);
}
return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 });
}
}
81 changes: 78 additions & 3 deletions src/app/auth/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,85 @@
'use client';

import useSocialLogin from '@src/hooks/useSocialLogin';
import { ToastService } from '@src/service/ToastService';
import axios from 'axios';
import { useRouter, useSearchParams } from 'next/navigation';
import { useEffect, useState } from 'react';
import { useCookies } from 'react-cookie';

type SocialLoginType = 'google' | 'kakao';

interface SocialLoginInfo {
loginPath: string;
socialType: SocialLoginType;
}

export interface SocialLoginRequestParameter {
loginPath: string;
code: string;
socialType: SocialLoginType;
}

const toastService = ToastService.getInstance();

const processSocialLogin = async ({ code, socialType, loginPath }: SocialLoginRequestParameter) => {
try {
const { data } = await axios.post(
`${process.env.NEXT_PUBLIC_MATE_TEST_WEB_HOST_URL}/api/social-login`,
{
code,
socialType,
},
);
return data;
} catch (error) {
throw error;
}
};

const AuthPage = () => {
useSocialLogin();
return <div />;
const router = useRouter();
const searchParams = useSearchParams();
const [loading, setLoading] = useState(true);
const [cookies] = useCookies(['social-login-info']);
const socialLoginInfo = cookies['social-login-info'];

useEffect(() => {
const handleLogin = async () => {
const code = searchParams.get('code');

if (!socialLoginInfo || !code) {
router.replace('/');
return;
}

try {
const { loginPath, socialType } = socialLoginInfo as SocialLoginInfo;
const data = await processSocialLogin({ code, socialType, loginPath });

if (data) {
toastService.addToast('로그인 되었습니다.');
router.replace(loginPath ?? '/');
} else {
toastService.addToast('로그인에 실패하였습니다.');
router.replace(loginPath ?? '/');
}
} catch (error) {
console.log(error);
toastService.addToast('로그인 중 오류가 발생했습니다.');
router.replace('/');
} finally {
setLoading(false);
}
};

handleLogin();
}, [searchParams, router, socialLoginInfo]);

if (loading) {
return <p>로그인 처리 중...</p>;
}

return <></>;
};

export default AuthPage;
56 changes: 28 additions & 28 deletions src/hooks/useSocialLogin.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { useEffect, useMemo } from 'react';
import { API_GET_GOOGLE_LOGIN, getGoogleLogin } from '@src/api/getGoogleLogin';
import { API_GET_KAKAKO_LOGIN, getKakaoLogin } from '@src/api/getKakaoLogin';
import defaultRequest from '@src/lib/axios/defaultRequest';
import { ToastService } from '@src/service/ToastService';
import { useQuery } from '@tanstack/react-query';
import { useRouter, useSearchParams } from 'next/navigation';
import { useMemo } from 'react';
import { useCookies } from 'react-cookie';

export type SocialType = 'google' | 'kakao';
Expand All @@ -20,6 +19,7 @@ const useSocialLogin = () => {
const toastService = ToastService.getInstance();
const code = get('code');
const socialInfo = getCookie['social-login-info'];
console.log(socialInfo, '소셜 로그인 인포!');

const socialInfoObj: SocialLoginInformationType = useMemo(() => {
if (socialInfo) return socialInfo;
Expand All @@ -37,32 +37,32 @@ const useSocialLogin = () => {
enabled: !!code && socialInfoObj?.socialType === 'kakao',
});

useEffect(() => {
async function kakaoLoginProcess() {
if (kakaoLogin.data) {
const accessToken = kakaoLogin.data.headers.access_token;
const refreshToken = kakaoLogin.data.headers.refresh_token;
defaultRequest.defaults.headers.common.Authorization = `Bearer ${accessToken}`;
await setCookie('refreshToken', refreshToken);
toastService.addToast('로그인 되었습니다.');
push(socialInfoObj.loginPath ?? '/');
}
}
kakaoLoginProcess();
}, [kakaoLogin.data, push, setCookie, socialInfoObj?.loginPath, toastService]);
useEffect(() => {
async function googleLoginProcess() {
if (googleLogin.data) {
const accessToken = googleLogin.data.headers.access_token;
const refreshToken = googleLogin.data.headers.refresh_token;
defaultRequest.defaults.headers.common.Authorization = `Bearer ${accessToken}`;
await setCookie('refreshToken', refreshToken);
toastService.addToast('로그인 되었습니다.');
push(socialInfoObj.loginPath ?? '/');
}
}
googleLoginProcess();
}, [googleLogin.data, push, setCookie, socialInfoObj?.loginPath, toastService]);
// useEffect(() => {
// async function kakaoLoginProcess() {
// if (kakaoLogin.data) {
// const accessToken = kakaoLogin.data.headers.access_token;
// const refreshToken = kakaoLogin.data.headers.refresh_token;
// defaultRequest.defaults.headers.common.Authorization = `Bearer ${accessToken}`;
// await setCookie('refreshToken', refreshToken);
// toastService.addToast('로그인 되었습니다.');
// push(socialInfoObj.loginPath ?? '/');
// }
// }
// kakaoLoginProcess();
// }, [kakaoLogin.data, push, setCookie, socialInfoObj?.loginPath, toastService]);
// useEffect(() => {
// async function googleLoginProcess() {
// if (googleLogin.data) {
// const accessToken = googleLogin.data.headers.access_token;
// const refreshToken = googleLogin.data.headers.refresh_token;
// defaultRequest.defaults.headers.common.Authorization = `Bearer ${accessToken}`;
// await setCookie('refreshToken', refreshToken);
// toastService.addToast('로그인 되었습니다.');
// push(socialInfoObj.loginPath ?? '/');
// }
// }
// googleLoginProcess();
// }, [googleLogin.data, push, setCookie, socialInfoObj?.loginPath, toastService]);
};

export default useSocialLogin;

0 comments on commit 2772a12

Please sign in to comment.