Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion components/Auth/SnsLogin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ const SnsLogin = () => {
<div className="flex items-center justify-between bg-gray300 rounded-lg px-6 py-3 mt-8">
<span>소셜 로그인</span>
<div className="flex gap-4">
<Link href="https://accounts.google.com/o/oauth2/v2/auth?scope=openid%20profile%20email&response_type=token&redirect_uri=http://localhost:3000/google&client_id=1079911783112-7rg5ecp9ia9lorm7pit0m2nb2ti1rpt0.apps.googleusercontent.com">
<Link
href={`https://accounts.google.com/o/oauth2/v2/auth?scope=https://www.googleapis.com/auth/userinfo.profile&response_type=code&redirect_uri=http://localhost:3000/api/auth/sign-in/google&client_id=${process.env.GOOGLE_CLIENT_ID}`}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GOOGLE_CLIENT_ID는 .env 파일에서 관리하는거죠? 한번 공유 주시면 좋겠습니다 : )

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아이디 생성 과정에 대해서 말씀하시는거죠?? 팀 미팅때 공유해드리겠습니당~

>
<Image src="/icons/google.svg" width="42" height="42" alt="구글" />
</Link>
<Image
Expand Down
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"dependencies": {
"axios": "^1.7.7",
"cookie": "^1.0.1",
"jwt-decode": "^4.0.0",
"next": "15.0.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
124 changes: 124 additions & 0 deletions pages/api/auth/sign-in/google.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import axios from "axios";
import axiosInstance from "@/lib/api/axiosInstanceApi";
import { NextApiRequest, NextApiResponse } from "next";
import { serialize } from "cookie";
import { jwtDecode } from "jwt-decode";

interface GoogleUserInfo {
name: string;
}

const handler = async (req: NextApiRequest, res: NextApiResponse) => {
try {
const { code } = req.query;
if (!code) {
return res.status(400).json({ message: "인증 코드가 없습니다." });
}

const clientId = process.env.GOOGLE_CLIENT_ID;
const clientSecret = process.env.GOOGLE_CLIENT_SECRET;
const redirectUri =
process.env.GOOGLE_REDIRECT_URI || "http://localhost:3000/";

if (!clientId || !clientSecret) {
return res
.status(500)
.json({ message: "Google API 클라이언트 정보가 설정되지 않았습니다." });
}

// 토큰 요청
const tokenUrl = "https://oauth2.googleapis.com/token";
const params = {
code: code as string,
client_id: clientId,
client_secret: clientSecret,
redirect_uri: redirectUri,
grant_type: "authorization_code",
};

const tokenResponse = await axios.post(tokenUrl, params);
const { id_token } = tokenResponse.data;
if (!id_token) {
return res
.status(401)
.json({ message: "ID 토큰을 가져오지 못했습니다." });
}

// Google ID 토큰에서 사용자 정보 추출
const userInfo: GoogleUserInfo = jwtDecode(id_token);
const { name } = userInfo;
console.log(name);

// 이미 회원인지 체크
try {
const loginResponse = await axiosInstance.post("/auth/sign-in/google", {
token: id_token,
redirectUri,
});

const accessToken = loginResponse.data.access_token;
if (accessToken) {
res.setHeader(
"Set-Cookie",
serialize("accessToken", accessToken, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
maxAge: 60 * 60 * 24,
path: "/",
})
);
return res.redirect("http://localhost:3000");
}
} catch (loginError: any) {
console.error(
"로그인 실패:",
loginError.response?.data || loginError.message
);

// 로그인 실패 시 회원가입 시도
try {
const signUpResponse = await axiosInstance.post(
"/auth/sign-up/google",
{
name: name || "사용자",
token: id_token,
redirectUri: "http://localhost:3000",
}
);

const accessToken = signUpResponse.data.access_token;
if (accessToken) {
res.setHeader(
"Set-Cookie",
serialize("accessToken", accessToken, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
maxAge: 60 * 60 * 24,
path: "/",
})
);
return res.redirect("http://localhost:3000");
}
} catch (signUpError: any) {
console.error(
"회원가입 실패:",
signUpError.response?.data || signUpError.message
);
return res.status(500).json({
message: "회원가입 중 오류가 발생했습니다.",
error: signUpError.response?.data || signUpError.message,
});
}
}
} catch (error: any) {
console.error("Error:", error.response?.data || error.message);
return res.status(500).json({
message: "서버 오류",
error: error.response?.data || error.message,
});
}
};

export default handler;
File renamed without changes.
File renamed without changes.
13 changes: 6 additions & 7 deletions pages/favorite/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { GetServerSideProps, GetServerSidePropsContext } from "next";
import { proxy } from "@/lib/api/axiosInstanceApi";
import axiosInstance, { proxy } from "@/lib/api/axiosInstanceApi";
import LinkCard from "@/components/LinkCard";
import CardsLayout from "@/components/Layout/CardsLayout";
import Container from "@/components/Layout/Container";
import { parse } from "cookie";

interface FavoriteDataType {
id: number;
Expand All @@ -24,13 +25,11 @@ export const getServerSideProps: GetServerSideProps = async (
) => {
// 클라이언트의 쿠키 가져오기
const { req } = context;
const cookies = req.headers.cookie || "";

const cookies = parse(req.headers.cookie || "");
const accessToken = cookies.accessToken;
try {
const res = await proxy.get("/api/favorites", {
headers: {
Cookie: cookies,
},
const res = await axiosInstance.get("/favorites?page=1&pageSize=10", {
headers: { Authorization: `Bearer ${accessToken}` },
});

const { list, totalCount } = res.data || { list: [], totalCount: 0 };
Expand Down
50 changes: 42 additions & 8 deletions pages/google.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,55 @@
import { proxy } from "@/lib/api/axiosInstanceApi";
import axios from "axios";
import { useEffect } from "react";

const Google = () => {
useEffect(() => {
const hash = window.location.hash;
const params = new URLSearchParams(hash.substring(1));
const token = params.get("access_token");
console.log(token);
const idToken = params.get("id_token"); // Google에서 전달받은 id_token
const accessToken = params.get("access_token"); // Google에서 전달받은 id_token

axios
.get(`https://www.googleapis.com/oauth2/v2/userinfo`, {
headers: { Authorization: `Bearer ${token}` },
})
.then((res) => console.log(res));
if (accessToken) {
console.log(" Access Token:", accessToken);
proxy
.post("/api/auth/sign-up/google", {
name: "박문균",
token: accessToken,
redirectUri: "http://localhost:3000/google",
})
Comment on lines +15 to +19
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테스트 하기 위해 name 을 고정으로 넣어보신 건가욧!?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 이 페이지는 없애야하는 데 깜빡했네여 merge하기 전에 수정해서 push할게욥

.then((response) => {
console.log("Authentication Success:", response.data);
// 성공적인 로그인 처리 후, 리다이렉션 등의 작업을 할 수 있습니다.
})
.catch((error) => {
console.error("Authentication Error:", error);
});
} else {
console.error("Access Token이 없습니다.");
}

if (idToken) {
console.log("Id Token:", idToken);

// 서버로 id_token을 전달하여 인증을 요청합니다.
proxy
.post("/api/auth/sign-in/google", {
token: idToken,
redirectUri: "http://localhost:3000/google",
})
.then((response) => {
console.log("Authentication Success:", response.data);
// 성공적인 로그인 처리 후, 리다이렉션 등의 작업을 할 수 있습니다.
})
.catch((error) => {
console.error("Authentication Error:", error);
});
} else {
console.error("Id Token이 없습니다.");
}
}, []);

return <div>안녕</div>;
return <div>Google OAuth 로그인 완료</div>;
};

export default Google;
Loading