diff --git a/src/app/(auth)/signup/applicant/page.tsx b/src/app/(auth)/signup/applicant/page.tsx
index c79e0d31..807fbf9f 100644
--- a/src/app/(auth)/signup/applicant/page.tsx
+++ b/src/app/(auth)/signup/applicant/page.tsx
@@ -109,14 +109,14 @@ export default function ApplicantSignupPage() {
diff --git a/src/app/(auth)/signup/owner/page.tsx b/src/app/(auth)/signup/owner/page.tsx
index aed11e2b..a55cf24a 100644
--- a/src/app/(auth)/signup/owner/page.tsx
+++ b/src/app/(auth)/signup/owner/page.tsx
@@ -131,14 +131,14 @@ export default function OwnerSignupPage() {
diff --git a/src/app/api/oauth/callback/google/route.ts b/src/app/api/oauth/callback/google/route.ts
index 3fa90861..64d21043 100644
--- a/src/app/api/oauth/callback/google/route.ts
+++ b/src/app/api/oauth/callback/google/route.ts
@@ -1,33 +1,27 @@
import { NextRequest, NextResponse } from "next/server";
import axios from "axios";
import { decodeJwt } from "@/middleware";
-import { OauthUser } from "@/types/oauth/oauthReq";
import apiClient from "@/lib/apiClient";
+import { OauthLoginUser, OauthResponse, OauthSignupUser } from "@/types/oauth/oauth";
+import { cookies } from "next/headers";
-export const GET = async (req: NextRequest) => {
- const searchParams = req.nextUrl.searchParams;
+export const GET = async (request: NextRequest) => {
+ const searchParams = request.nextUrl.searchParams;
const code = searchParams.get("code");
const state = searchParams.get("state");
- if (!code) {
- return NextResponse.json({ message: "Code not found" }, { status: 400 });
+ if (!code || !state) {
+ return NextResponse.json({ message: `${!code ? "Code" : "State"} not found` }, { status: 400 });
}
- if (!state) {
- return NextResponse.json({ message: "State not found" }, { status: 400 });
- }
-
- // `state`를 JSON으로 파싱
let parsedState;
try {
parsedState = JSON.parse(decodeURIComponent(state));
- } catch (error) {
- console.error("Failed to parse state:", error);
+ } catch {
return NextResponse.json({ message: "Invalid state format" }, { status: 400 });
}
- const { provider, role } = parsedState;
-
+ const { provider, action, role } = parsedState;
const GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token";
const clientId = process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID;
const clientSecret = process.env.NEXT_PUBLIC_GOOGLE_SECRET;
@@ -38,8 +32,8 @@ export const GET = async (req: NextRequest) => {
}
try {
- // Access Token 요청
- const tokenResponse = await axios.post(GOOGLE_TOKEN_URL, null, {
+ // Google Access Token 요청
+ const { data: tokenResponse } = await axios.post(GOOGLE_TOKEN_URL, null, {
params: {
code,
client_id: clientId,
@@ -49,41 +43,76 @@ export const GET = async (req: NextRequest) => {
},
});
- const { id_token } = tokenResponse.data;
-
- // id_token 디코딩
+ const { id_token } = tokenResponse;
const decodedIdToken = decodeJwt(id_token);
if (!decodedIdToken) {
return NextResponse.json({ message: "Invalid ID token" }, { status: 400 });
}
- const googleUser: OauthUser = {
- role: role,
- name: decodedIdToken.name,
- token: id_token,
+ const googleUser: { signup: OauthSignupUser; login: OauthLoginUser } = {
+ signup: {
+ role,
+ name: decodedIdToken.name,
+ token: id_token,
+ },
+ login: {
+ token: id_token,
+ redirectUri,
+ },
};
- console.log("Google user:", googleUser);
- // OAuth 회원가입 API로 회원가입 요청
- try {
- const googleSignupResponse = await apiClient.post(`/oauth/sign-up/${provider}`, googleUser);
- console.log("구글 회원가입 성공:", googleSignupResponse.data);
- } catch (error) {
- const errorMessage = (error as any).response?.data;
- console.error("구글 회원가입 에러:", errorMessage);
- }
- // 사용자 정보를 클라이언트에 반환
- const response = NextResponse.redirect("http://localhost:3000");
- response.cookies.set("user", JSON.stringify(googleUser), {
- httpOnly: true,
- secure: process.env.NODE_ENV === "production",
- sameSite: "strict",
- maxAge: 60 * 60 * 24, // 1일
- path: "/",
- });
- return response;
- } catch (error) {
- console.error("Google login error:", error);
- return NextResponse.json({ message: "서버에러" }, { status: 500 });
+ const processUser = async () => {
+ if (action === "signup") {
+ try {
+ const response = await apiClient.post(`/oauth/sign-up/${provider}`, googleUser.signup);
+ console.log("구글 회원가입 성공:", response.data);
+ } catch (error: any) {
+ if (error.response?.status === 400) {
+ console.log("이미 등록된 사용자입니다. 로그인 시도 중...");
+ await loginUser();
+ } else {
+ throw new Error("회원가입 중 서버 오류");
+ }
+ }
+ } else if (action === "login") {
+ await loginUser();
+ } else {
+ throw new Error("잘못된 작업 에러");
+ }
+ };
+
+ const loginUser = async () => {
+ const { data: loginResponse } = await axios.post(
+ `${process.env.NEXT_PUBLIC_DOMAIN_URL}/api/oauth/login/${provider}`,
+ googleUser.login
+ );
+ console.log("구글 로그인 성공:", loginResponse);
+
+ // 쿠키 저장
+ const { accessToken, refreshToken } = loginResponse;
+ setCookies(accessToken, refreshToken);
+ };
+
+ const setCookies = (accessToken: string, refreshToken: string) => {
+ cookies().set("accessToken", accessToken, {
+ httpOnly: true,
+ secure: process.env.NODE_ENV === "production",
+ sameSite: "lax",
+ path: "/",
+ });
+ cookies().set("refreshToken", refreshToken, {
+ httpOnly: true,
+ secure: process.env.NODE_ENV === "production",
+ sameSite: "lax",
+ path: "/",
+ });
+ };
+
+ await processUser();
+ } catch (error: any) {
+ console.error("OAuth 처리 중 오류:", error.message || error);
+ return NextResponse.json({ message: error.message || "서버 오류" }, { status: 500 });
}
+
+ return NextResponse.redirect(new URL("/", request.url));
};
diff --git a/src/app/api/oauth/callback/kakao/route.ts b/src/app/api/oauth/callback/kakao/route.ts
index 1694d590..bc7a322a 100644
--- a/src/app/api/oauth/callback/kakao/route.ts
+++ b/src/app/api/oauth/callback/kakao/route.ts
@@ -1,70 +1,102 @@
import { NextRequest, NextResponse } from "next/server";
-import { OauthUser } from "@/types/oauth/oauthReq";
+import axios from "axios";
import apiClient from "@/lib/apiClient";
+import { OauthLoginUser, OauthResponse, OauthSignupUser } from "@/types/oauth/oauth";
+import { cookies } from "next/headers";
-export const GET = async (req: NextRequest) => {
- const searchParams = req.nextUrl.searchParams;
+export const GET = async (request: NextRequest) => {
+ const searchParams = request.nextUrl.searchParams;
const code = searchParams.get("code");
const state = searchParams.get("state");
- if (!code) {
- return NextResponse.json({ message: "Code not found" }, { status: 400 });
- }
-
- if (!state) {
- return NextResponse.json({ message: "State not found" }, { status: 400 });
+ if (!code || !state) {
+ return NextResponse.json({ message: `${!code ? "Code" : "State"} not found` }, { status: 400 });
}
let parsedState;
try {
parsedState = JSON.parse(decodeURIComponent(state));
- } catch (error) {
- console.error("Failed to parse state:", error);
+ } catch {
return NextResponse.json({ message: "Invalid state format" }, { status: 400 });
}
- const { provider, role } = parsedState;
- const clientId = process.env.NEXT_PUBLIC_KAKAO_REST_API_KEY;
+ const { provider, action, role } = parsedState;
const redirectUri = process.env.NEXT_PUBLIC_KAKAO_REDIRECT_URI;
- if (!clientId || !redirectUri) {
+ if (!redirectUri) {
return NextResponse.json({ message: "Environment variables not set" }, { status: 500 });
}
- const kakaoUser: OauthUser = {
- role: role,
- name: "", // 기본값 설정 (빈 문자열)
- token: code, // 인가코드 그대로 전달
- redirectUri: redirectUri,
+ const kakaoUser: { signup: OauthSignupUser; login: OauthLoginUser } = {
+ signup: {
+ role: role || "user", // 기본 역할 설정
+ name: "", // Kakao는 이름을 제공하지 않으므로 기본값
+ token: code, // 인가 코드 전달
+ },
+ login: {
+ token: code, // 인가 코드 전달
+ redirectUri, // 리다이렉트 URI 포함
+ },
};
- try {
- // 인가코드를 포함한 데이터를 백엔드로 전달
- const kakaoSignupResponse = await apiClient.post(`/oauth/sign-up/${provider}`, kakaoUser);
- console.log("카카오 회원가입 성공:", kakaoSignupResponse.data);
+ const processUser = async () => {
+ if (action === "signup") {
+ try {
+ const response = await apiClient.post(`/oauth/sign-up/${provider}`, kakaoUser.signup);
+ console.log("카카오 회원가입 성공:", response.data);
+ } catch (error: any) {
+ if (error.response?.status === 400) {
+ console.log("이미 등록된 사용자입니다. 로그인 시도 중...");
+ await loginUser();
+ } else {
+ throw new Error("회원가입 중 서버 오류");
+ }
+ }
+ } else if (action === "login") {
+ await loginUser();
+ } else {
+ throw new Error("Invalid action");
+ }
+ };
- // 사용자 정보를 클라이언트에 반환
- // return NextResponse.json(kakaoSignupResponse.data);
- } catch (error: any) {
- // 에러 타입 명시
- console.error("카카오 회원가입 에러:", error.response?.data || error.message);
+ const loginUser = async () => {
+ try {
+ const { data: loginResponse } = await axios.post(
+ `${process.env.NEXT_PUBLIC_DOMAIN_URL}/api/oauth/login/${provider}`,
+ kakaoUser.login
+ );
+ console.log("카카오 로그인 성공:", loginResponse);
- // return NextResponse.json({ message: error.response?.data || "Error during Kakao signup" }, { status: 500 });
- }
+ // 쿠키 저장
+ const { accessToken, refreshToken } = loginResponse;
+ setCookies(accessToken, refreshToken);
+ } catch (error: any) {
+ console.error("카카오 로그인 중 오류:", error.message || error);
+ throw new Error("로그인 중 서버 오류");
+ }
+ };
- try {
- // 사용자 정보를 클라이언트에 반환
- const response = NextResponse.redirect("http://localhost:3000");
- response.cookies.set("user", JSON.stringify(kakaoUser), {
+ const setCookies = (accessToken: string, refreshToken: string) => {
+ cookies().set("accessToken", accessToken, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
- sameSite: "strict",
- maxAge: 60 * 60 * 24, // 1일
+ sameSite: "lax",
path: "/",
});
- return response;
+ cookies().set("refreshToken", refreshToken, {
+ httpOnly: true,
+ secure: process.env.NODE_ENV === "production",
+ sameSite: "lax",
+ path: "/",
+ });
+ };
+
+ try {
+ await processUser();
} catch (error: any) {
- console.error("카카오 회원가입 에러:", error.response?.data || error.message);
- return NextResponse.json({ message: error.response?.data || "서버에러" }, { status: 500 });
+ console.error("OAuth 처리 중 오류:", error.message || error);
+ return NextResponse.json({ message: error.message || "서버 오류" }, { status: 500 });
}
+
+ return NextResponse.redirect(new URL("/", request.url));
};
diff --git a/src/app/api/oauth/login/[provider]/route.ts b/src/app/api/oauth/login/[provider]/route.ts
index b766e3ea..08bfc0db 100644
--- a/src/app/api/oauth/login/[provider]/route.ts
+++ b/src/app/api/oauth/login/[provider]/route.ts
@@ -6,6 +6,7 @@ import apiClient from "@/lib/apiClient";
// OAuth 로그인 API
export async function POST(request: Request, { params }: { params: { provider: string } }) {
try {
+ console.log("/api/oauth/login");
const provider = params.provider;
// provider 유효성 검사
@@ -15,28 +16,10 @@ export async function POST(request: Request, { params }: { params: { provider: s
// 요청 본문 파싱
const body = await request.json();
-
+ console.log("Received body:", body); // 요청 본문 로그 출력
// OAuth 로그인 요청
const response = await apiClient.post(`/oauth/sign-in/${provider}`, body);
- // 응답에서 토큰 추출
- const { accessToken, refreshToken } = response.data;
-
- // 쿠키에 토큰 저장
- cookies().set("accessToken", accessToken, {
- httpOnly: true,
- secure: process.env.NODE_ENV === "production",
- sameSite: "lax",
- path: "/",
- });
-
- cookies().set("refreshToken", refreshToken, {
- httpOnly: true,
- secure: process.env.NODE_ENV === "production",
- sameSite: "lax",
- path: "/",
- });
-
return NextResponse.json(response.data);
} catch (error: unknown) {
if (error instanceof AxiosError) {
@@ -45,6 +28,6 @@ export async function POST(request: Request, { params }: { params: { provider: s
return NextResponse.json({ message: error.response.data.message }, { status: error.response.status });
}
}
- return NextResponse.json({ message: "Internal Server Error" }, { status: 500 });
+ return NextResponse.json({ message: "서버오류" }, { status: 500 });
}
}
diff --git a/src/types/oauth/oauth.d.ts b/src/types/oauth/oauth.d.ts
new file mode 100644
index 00000000..ca4ca5a8
--- /dev/null
+++ b/src/types/oauth/oauth.d.ts
@@ -0,0 +1,22 @@
+export interface OauthSignupUser {
+ location?: string;
+ phoneNumber?: string;
+ storePhoneNumber?: string;
+ storeName?: string;
+ role: string;
+ nickname?: string;
+ name: string;
+ redirectUri?: string;
+ token: string;
+}
+
+export interface OauthLoginUser {
+ redirectUri: string;
+ token: string;
+}
+
+export interface OauthResponse {
+ use: KakaoSignupUser;
+ refreshToken: string;
+ accessToken: string;
+}
diff --git a/src/types/oauth/oauthReq.ts b/src/types/oauth/oauthReq.ts
deleted file mode 100644
index 10df9aa6..00000000
--- a/src/types/oauth/oauthReq.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-export interface OauthUser {
- location?: string;
- phoneNumber?: string;
- storePhoneNumber?: string;
- storeName?: string;
- role: string;
- nickname?: string;
- name: string;
- redirectUri?: string;
- token: string;
-}
diff --git a/tsconfig.json b/tsconfig.json
index 69d63471..89c2d401 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -24,5 +24,5 @@
"typeRoots": ["./node_modules/@types"]
},
"include": ["next-env.d.ts", "src/**/*.ts", "src/**/*.tsx", ".next/types/**/*.ts", "custom.d.ts"],
- "exclude": ["node_modules"]
+ "exclude": ["node_modules", ".next", ".storybook", "storybook-static"]
}