Skip to content

Commit 22bf211

Browse files
authored
Merge pull request #88 from FE9-2/feat/oauth
feat: oauth 카카오 간편 로그인 user 정보 불러오기
2 parents 5ce16f5 + 560b7df commit 22bf211

File tree

9 files changed

+116
-27
lines changed

9 files changed

+116
-27
lines changed
Lines changed: 14 additions & 0 deletions
Loading
Lines changed: 12 additions & 0 deletions
Loading

src/app/(auth)/login/page.tsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { useAuth } from "@/hooks/useAuth";
33
import { type LoginSchema, loginSchema } from "@/schemas/authSchema";
44
import { zodResolver } from "@hookform/resolvers/zod";
5+
import Image from "next/image";
56
import Link from "next/link";
67
import { useForm } from "react-hook-form";
78

@@ -20,12 +21,12 @@ export default function LoginPage() {
2021
};
2122

2223
return (
23-
<div className="flex min-h-screen items-center justify-center bg-gradient-to-r from-blue-500 to-purple-600 px-4 py-12 sm:px-6 lg:px-8">
24-
<div className="w-full max-w-md space-y-8 rounded-lg bg-white p-8 shadow-lg">
24+
<div className="flex min-h-screen items-center justify-center px-4 py-12 sm:px-6 lg:px-8">
25+
<div className="w-full max-w-md space-y-8 rounded-lg bg-white p-8">
2526
<div>
2627
<h2 className="text-grayscale-900 mt-6 text-center text-3xl font-bold tracking-tight">로그인</h2>
2728
<p className="text-grayscale-600 mt-2 text-center text-sm">
28-
아직 계정이 없으신가요?{" "}
29+
아직 계정이 없으신가요?
2930
<Link href="/signup" className="font-medium text-blue-600 hover:text-blue-500">
3031
회원가입하기
3132
</Link>
@@ -57,11 +58,21 @@ export default function LoginPage() {
5758
<button
5859
type="submit"
5960
disabled={isLoginPending}
60-
className="group relative flex w-full justify-center rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:bg-blue-400"
61+
className="group relative flex w-full justify-center rounded-lg bg-primary-orange-300 px-4 py-2 text-sm font-medium text-white hover:bg-primary-orange-200 focus:outline-none focus:ring-2 focus:ring-primary-orange-300 focus:ring-offset-2 disabled:bg-grayscale-100"
6162
>
6263
{isLoginPending ? "로그인 중..." : "로그인"}
6364
</button>
6465
</div>
66+
<div className="flex">
67+
<button>
68+
<Image src="/icons/social/social_google.svg" width={72} height={72} alt="구글 로그인" />
69+
</button>
70+
<Link
71+
href={`https://kauth.kakao.com/oauth/authorize?client_id=${process.env.NEXT_PUBLIC_KAKAO_REST_API_KEY}&redirect_uri=${process.env.NEXT_PUBLIC_KAKAO_REDIRECT_URI}&response_type=code`}
72+
>
73+
<Image src="/icons/social/social_kakao.svg" width={72} height={72} alt="카카오 로그인" />
74+
</Link>
75+
</div>
6576
</form>
6677
</div>
6778
</div>

src/app/(auth)/signup/page.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
77
import Link from "next/link";
88
import { FieldErrors, useForm } from "react-hook-form";
99
import { useEffect } from "react";
10+
import Image from "next/image";
1011

1112
export default function SignupPage() {
1213
const { signup, isSignupPending } = useAuth();
@@ -197,6 +198,16 @@ export default function SignupPage() {
197198
{isSignupPending ? "회원가입 중..." : "회원가입"}
198199
</button>
199200
</div>
201+
<div className="flex">
202+
<button>
203+
<Image src="/icons/social/social_google.svg" width={72} height={72} alt="구글 로그인" />
204+
</button>
205+
<Link
206+
href={`https://kauth.kakao.com/oauth/authorize?client_id=${process.env.NEXT_PUBLIC_KAKAO_REST_API_KEY}&redirect_uri=${process.env.NEXT_PUBLIC_KAKAO_REDIRECT_URI}&response_type=code`}
207+
>
208+
<Image src="/icons/social/social_kakao.svg" width={72} height={72} alt="카카오 로그인" />
209+
</Link>
210+
</div>
200211
</form>
201212
</div>
202213
</div>

src/app/api/oauth/apps/route.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1+
import { NextRequest, NextResponse } from "next/server";
12
import { AxiosError } from "axios";
2-
import { cookies } from "next/headers";
3-
import { NextResponse } from "next/server";
43
import apiClient from "@/lib/apiClient";
4+
import { cookies } from "next/headers";
55

66
// OAuth 앱 등록/수정 API
7-
export async function POST(request: Request) {
7+
export async function POST(request: NextRequest) {
88
try {
99
// 쿠키에서 액세스 토큰 가져오기
1010
const accessToken = cookies().get("accessToken")?.value;
@@ -23,7 +23,11 @@ export async function POST(request: Request) {
2323
},
2424
});
2525

26-
return NextResponse.json(response.data);
26+
// 응답 데이터 처리
27+
const responseData = response.data;
28+
console.log("OAuth 앱 등록/수정 성공:", responseData);
29+
30+
return NextResponse.json(responseData);
2731
} catch (error: unknown) {
2832
if (error instanceof AxiosError) {
2933
console.error("POST /api/oauth/apps error:", error);
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { NextRequest, NextResponse } from "next/server";
2+
import axios from "axios";
3+
4+
export const GET = async (req: NextRequest) => {
5+
const searchParams = req.nextUrl.searchParams;
6+
const code = searchParams.get("code");
7+
8+
if (!code) {
9+
return NextResponse.json({ message: "Code not found" }, { status: 400 });
10+
}
11+
12+
// const KAKAO_TOKEN_URL = "https://kauth.kakao.com/oauth/token";
13+
// const params = new URLSearchParams();
14+
// params.append("grant_type", "authorization_code");
15+
// const clientId = process.env.NEXT_PUBLIC_KAKAO_REST_API_KEY;
16+
// const redirectUri = process.env.NEXT_PUBLIC_KAKAO_REDIRECT_URI;
17+
18+
// if (!clientId || !redirectUri) {
19+
// return NextResponse.json({ message: "Environment variables not set" }, { status: 500 });
20+
// }
21+
22+
// params.append("client_id", clientId); // 카카오 REST API 키
23+
// params.append("redirect_uri", redirectUri); // Redirect URI
24+
// params.append("code", code);
25+
26+
// try {
27+
// // 액세스 토큰 요청
28+
// const response = await axios.post(KAKAO_TOKEN_URL, params);
29+
// const { access_token } = response.data;
30+
// console.log("Kakao access token:", access_token);
31+
32+
// // 액세스 토큰을 사용하여 사용자 정보 요청
33+
// const userInfoResponse = await axios.get("https://kapi.kakao.com/v2/user/me", {
34+
// headers: {
35+
// Authorization: `Bearer ${access_token}`,
36+
// },
37+
// });
38+
39+
// const user = userInfoResponse.data;
40+
// console.log("Kakao user:", user);
41+
42+
// // 사용자 정보를 클라이언트에 반환
43+
// return NextResponse.json(user);
44+
// } catch (error) {
45+
// console.error("Kakao login error:", error);
46+
// return NextResponse.json({ message: "Internal Server Error" }, { status: 500 });
47+
// }
48+
};

src/app/components/card/cardList/RecruitCond.tsx renamed to src/app/components/card/cardList/RecruitCondition.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ import React from "react";
33
import { FormDetailResponse } from "@/types/response/form";
44
import { cn } from "@/lib/tailwindUtil";
55

6-
interface RecruitCondProps {
6+
interface RecruitConditionProps {
77
recruitData: FormDetailResponse;
88
}
99

1010
// 모집조건 카드 컴포넌트
11-
const RecruitCond = ({ recruitData }: RecruitCondProps) => {
11+
const RecruitCondition = ({ recruitData }: RecruitConditionProps) => {
1212
const titleStyle = "text-black-200 min-w-[64px] md:min-w-[120px]";
1313
return (
1414
<div className="flex w-[327px] flex-col gap-5 rounded-lg border border-line-100 bg-white p-4 text-xs md:w-[640px] md:p-6 md:text-lg">
@@ -43,4 +43,4 @@ const RecruitCond = ({ recruitData }: RecruitCondProps) => {
4343
);
4444
};
4545

46-
export default RecruitCond;
46+
export default RecruitCondition;

src/app/stories/design-system/components/card/cardList/RecruitCond.stories.tsx renamed to src/app/stories/design-system/components/card/cardList/RecruitCondition.stories.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
import { Meta, StoryObj } from "@storybook/react";
2-
import RecruitCond from "@/app/components/card/cardList/RecruitCond";
32
import { FormDetailResponse } from "@/types/response/form";
3+
import RecruitCondition from "@/app/components/card/cardList/RecruitCondition";
44

5-
const meta: Meta<typeof RecruitCond> = {
6-
title: "Design System/Components/Card/CardList/RecruitCond",
7-
component: RecruitCond,
5+
const meta: Meta<typeof RecruitCondition> = {
6+
title: "Design System/Components/Card/CardList/RecruitCondition",
7+
component: RecruitCondition,
88
parameters: {
99
layout: "centered",
1010
},
1111
};
1212

1313
export default meta;
14-
type Story = StoryObj<typeof RecruitCond>;
14+
type Story = StoryObj<typeof RecruitCondition>;
1515

1616
const mockRecruitData: FormDetailResponse = {
1717
updatedAt: new Date(),

src/app/stories/design-system/components/card/cardList/SubmitContCard.stories.tsx

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,3 @@ export const Default: Story = {
3333
submitContData: mockData,
3434
},
3535
};
36-
37-
// 추가 스토리 정의 가능
38-
export const Alternative: Story = {
39-
args: {
40-
submitContData: {
41-
...mockData,
42-
name: "김철수", // 다른 이름으로 변경
43-
phoneNumber: "010-9876-5432", // 다른 연락처로 변경
44-
},
45-
},
46-
};

0 commit comments

Comments
 (0)