diff --git a/public/assets/icons/ic_google.svg b/public/assets/icons/ic_google.svg
new file mode 100644
index 00000000..f742d521
--- /dev/null
+++ b/public/assets/icons/ic_google.svg
@@ -0,0 +1,6 @@
+
diff --git a/public/assets/icons/ic_kakaotalk.svg b/public/assets/icons/ic_kakaotalk.svg
new file mode 100644
index 00000000..0e6423de
--- /dev/null
+++ b/public/assets/icons/ic_kakaotalk.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/assets/icons/ic_naver.svg b/public/assets/icons/ic_naver.svg
new file mode 100644
index 00000000..c67628c6
--- /dev/null
+++ b/public/assets/icons/ic_naver.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/app/login/kakao/page.tsx b/src/app/login/auth/page.tsx
similarity index 54%
rename from src/app/login/kakao/page.tsx
rename to src/app/login/auth/page.tsx
index 5d97135c..21499ca4 100644
--- a/src/app/login/kakao/page.tsx
+++ b/src/app/login/auth/page.tsx
@@ -1,31 +1,30 @@
-"use client";
+'use client';
-import { login } from "@/api/login/user";
-import Loader from "@/components/common/Loader";
-import { signupState } from "@/recoil/signupStore";
-import {
- clearLetterUrl,
- getLetterUrl,
- setOnboarding,
- setTokens,
-} from "@/utils/storage";
-import axios from "axios";
-import { useRouter } from "next/navigation";
-import { useEffect, useState } from "react";
-import { useRecoilState } from "recoil";
-import styled from "styled-components";
+import { login } from '@/api/login/user';
+import Loader from '@/components/common/Loader';
+import { signupState } from '@/recoil/signupStore';
+import { clearLetterUrl, setOnboarding, setTokens } from '@/utils/storage';
+import axios from 'axios';
+import { useRouter } from 'next/navigation';
+import { useEffect, useState } from 'react';
+import { useRecoilState } from 'recoil';
+import styled from 'styled-components';
const Auth = () => {
const [registerToken, setRegisterToken] = useRecoilState(signupState);
const router = useRouter();
const REST_API_KEY = process.env.NEXT_PUBLIC_REST_API_KEY;
- const [absoluteUrl, setAbsoluteUrl] = useState("");
- const [storeUrl, setstoreUrl] = useState("");
+ const [absoluteUrl, setAbsoluteUrl] = useState('');
+ const [storeUrl, setstoreUrl] = useState('');
+ const [type, setType] = useState('');
useEffect(() => {
- if (typeof window !== "undefined") {
- const url = `${window.location.protocol}//${window.location.host}/login/kakao`;
- const letterId = localStorage.getItem("letter_url");
+ if (typeof window !== 'undefined') {
+ const params = new URL(window.location.href).searchParams;
+ const typeParam = params.get('type');
+ setType(typeParam);
+ const url = `${window.location.protocol}//${window.location.host}/login/auth?type=${typeParam}`;
+ const letterId = localStorage.getItem('letter_url');
setAbsoluteUrl(url);
if (letterId) setstoreUrl(letterId);
}
@@ -37,28 +36,43 @@ const Auth = () => {
}
const getToken = async () => {
const AUTHORIZATION_CODE = new URL(window.location.href).searchParams.get(
- "code"
+ 'code'
);
+ const TYPE = new URL(window.location.href).searchParams.get('type');
- if (!AUTHORIZATION_CODE) {
- console.error("Authorization Code is missing");
+ let tokenUrl = '';
+ let provider: 'KAKAO' | 'GOOGLE' | 'NAVER';
+
+ if (!AUTHORIZATION_CODE || !TYPE) {
+ console.error('Authorization Code or Type is missing');
return;
}
+ switch (TYPE) {
+ case 'kakao':
+ tokenUrl = `https://kauth.kakao.com/oauth/token?grant_type=authorization_code&client_id=${REST_API_KEY}&redirect_uri=${absoluteUrl}&code=${AUTHORIZATION_CODE}`;
+ provider = 'KAKAO';
+ break;
+ case 'google':
+ break;
+ case 'naver':
+ break;
+ default:
+ console.error('Unknown OAuth type:', TYPE);
+ return;
+ }
+
try {
- const response = await axios.post(
- `https://kauth.kakao.com/oauth/token?grant_type=authorization_code&client_id=${REST_API_KEY}&redirect_uri=${absoluteUrl}&code=${AUTHORIZATION_CODE}`,
- {
- headers: { "Content-Type": "application/json" },
- }
- );
+ const response = await axios.post(tokenUrl, {
+ headers: { 'Content-Type': 'application/json' }
+ });
+
+ const oauthAccessToken = response.data.access_token;
- const kakao_accessToken = response.data.access_token;
- console.log(kakao_accessToken);
- if (kakao_accessToken) {
- login("KAKAO", kakao_accessToken)
+ if (oauthAccessToken) {
+ login(provider, oauthAccessToken)
.then((res) => {
- console.log("accessToken", res.data.accessToken);
+ console.log('accessToken', res.data.accessToken);
setTokens(res.data.accessToken, res.data.refreshToken);
/* 온보딩 여부 저장 */
setOnboarding(res.data.isProcessedOnboarding);
@@ -66,18 +80,18 @@ const Auth = () => {
router.push(`/verify/letter?url=${storeUrl}`);
clearLetterUrl();
} else {
- router.push("/planet");
+ router.push('/planet');
}
})
.catch((error) => {
if (error.response && error.response.status === 401) {
- console.log("registerToken", error.response.data.registerToken);
+ console.log('registerToken', error.response.data.registerToken);
setRegisterToken(error.response.data.registerToken);
if (storeUrl) {
router.push(`/signup/step1?url=${storeUrl}`);
clearLetterUrl();
} else {
- router.push("/signup/step1");
+ router.push('/signup/step1');
}
}
});
diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx
index cb27b349..b19a2296 100644
--- a/src/app/login/page.tsx
+++ b/src/app/login/page.tsx
@@ -1,8 +1,14 @@
-"use client";
+'use client';
-import SocialKakao from "@/components/signup/SocialKakao";
-import { theme } from "@/styles/theme";
-import styled from "styled-components";
+import SocialKakao from '@/components/signup/SocialKakao';
+import SocialGoogle from '@/components/signup/SocialGoogle';
+import { theme } from '@/styles/theme';
+import styled from 'styled-components';
+import Image from 'next/image';
+
+interface OauthButtonProps {
+ bgColor: string;
+}
export default function Login() {
return (
@@ -11,9 +17,23 @@ export default function Login() {
편지로 수놓는 나의 스페이스
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+ 로그인 없이 편지 작성해보기
);
@@ -25,7 +45,7 @@ const Container = styled.div`
height: 100vh;
flex-direction: column;
justify-content: space-between;
- background-image: url("/assets/login/login_bg.png");
+ background-image: url('/assets/login/login_bg.png');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
@@ -104,13 +124,35 @@ const ImageWrapper = styled.div`
overflow: hidden;
`;
-const SocialKakaoWrapper = styled.div`
+const OauthWrapper = styled.div`
width: 100%;
max-width: 393px;
- padding: 0 4px;
+ gap: 24px;
+ display: flex;
+ position: absolute;
+ bottom: 123px;
+ justify-content: center;
+`;
+
+const OauthButton = styled.button`
+ width: 69px;
+ height: 69px;
+ border-radius: 50%;
+ border: none;
+ background-color: ${({ bgColor }) => bgColor};
display: flex;
+ align-items: center;
+ overflow: hidden;
+ justify-content: center;
+ cursor: pointer;
+ transition: background-color 0.3s;
+`;
+
+const LetterBtnText = styled.div`
+ ${(props) => props.theme.fonts.caption02};
+ color: ${theme.colors.gray400};
position: absolute;
- bottom: 60px;
- left: 50%;
- transform: translate(-50%);
+ bottom: 69px;
+ text-decoration-line: underline;
+ cursor: pointer;
`;
diff --git a/src/components/signup/SocialGoogle.tsx b/src/components/signup/SocialGoogle.tsx
new file mode 100644
index 00000000..ee8a6ae0
--- /dev/null
+++ b/src/components/signup/SocialGoogle.tsx
@@ -0,0 +1,81 @@
+import React, { Suspense, useEffect, useState } from 'react';
+import Image from 'next/image';
+import styled from 'styled-components';
+import { useSearchParams } from 'next/navigation';
+import { setLetterUrl } from '@/utils/storage';
+import Loader, { LoaderContainer } from '../common/Loader';
+
+const SocialGoogle = () => {
+ const searchParams = useSearchParams();
+ const url = searchParams.get('url');
+ const REST_API_KEY = process.env.NEXT_PUBLIC_REST_API_KEY;
+ const GOOGLE_URL = '/login/google';
+ const [absoluteUrl, setabsoluteUrl] = useState('');
+
+ useEffect(() => {
+ if (typeof window !== 'undefined') {
+ setabsoluteUrl(
+ window.location.protocol + '//' + window.location.host + '/login/kakao'
+ );
+ }
+ }, []);
+
+ const handleLogin = () => {
+ //이때 localStorage에 저장된 accessToken이 만료되었는지 확인해야함.
+ // if (accessToken) {
+ // if (url) {
+ // router.push(`/verify?url=${url}`);
+ // } else {
+ // router.push("/");
+ // }
+ // setAccessToken(accessToken);
+ // } else {
+ //받은 편지를 통해 들어올 경우 url를 저장한다.
+ if (url) {
+ setLetterUrl(url);
+ }
+ window.location.href = GOOGLE_URL;
+ };
+
+ return (
+
+
+
+ );
+};
+
+export default function SocialGooglePaging() {
+ return (
+
+
+
+ }
+ >
+
+
+ );
+}
+
+const SocialLoginBox = styled.div`
+ display: flex;
+ box-sizing: border-box;
+ width: 100%;
+ cursor: pointer;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+`;
+
+const StyledImage = styled(Image)`
+ width: 100%;
+ height: 100%;
+ padding: 0 16px;
+ object-fit: contain;
+`;
diff --git a/src/components/signup/SocialKakao.tsx b/src/components/signup/SocialKakao.tsx
index 073a790b..6128cb43 100644
--- a/src/components/signup/SocialKakao.tsx
+++ b/src/components/signup/SocialKakao.tsx
@@ -1,26 +1,24 @@
-import React, { Suspense, useEffect, useState } from "react";
-import Image from "next/image";
-import styled from "styled-components";
-import { useRouter, useSearchParams } from "next/navigation";
-import { useRecoilState } from "recoil";
-import { accessState } from "@/recoil/accessStore";
-import { getAccessToken, setLetterUrl } from "@/utils/storage";
-import Loader, { LoaderContainer } from "../common/Loader";
+import React, { Suspense, useEffect, useState } from 'react';
+import Image from 'next/image';
+import styled from 'styled-components';
+import { useSearchParams } from 'next/navigation';
+import { setLetterUrl } from '@/utils/storage';
+import Loader, { LoaderContainer } from '../common/Loader';
const SocialKakao = () => {
- const router = useRouter();
const searchParams = useSearchParams();
- const url = searchParams.get("url");
+ const url = searchParams.get('url');
const REST_API_KEY = process.env.NEXT_PUBLIC_REST_API_KEY;
- const accessToken = getAccessToken();
- const [absoluteUrl, setabsoluteUrl] = useState("");
- const KAKAO_URL = `https://kauth.kakao.com/oauth/authorize?client_id=${REST_API_KEY}&redirect_uri=${absoluteUrl}&response_type=code&url=${url}`;
- const [localAccessToken, setAccessToken] = useRecoilState(accessState);
+ const [redirectUrl, setRedirectUrl] = useState('');
+ const KAKAO_URL = `https://kauth.kakao.com/oauth/authorize?client_id=${REST_API_KEY}&redirect_uri=${redirectUrl}&response_type=code&url=${url}`;
useEffect(() => {
- if (typeof window !== "undefined") {
- setabsoluteUrl(
- window.location.protocol + "//" + window.location.host + "/login/kakao"
+ if (typeof window !== 'undefined') {
+ setRedirectUrl(
+ window.location.protocol +
+ '//' +
+ window.location.host +
+ '/login/auth?type=kakao'
);
}
}, []);
@@ -35,18 +33,30 @@ const SocialKakao = () => {
// }
// setAccessToken(accessToken);
// } else {
+ //받은 편지를 통해 들어올 경우 url를 저장한다.
if (url) {
setLetterUrl(url);
}
+ // redirectUri가 준비된 뒤에 인가 URL 생성
+ const params = new URLSearchParams({
+ client_id: REST_API_KEY,
+ redirect_uri: redirectUrl,
+ response_type: 'code'
+ });
+ if (url) {
+ params.set('url', url);
+ }
+
+ window.location.href = `https://kauth.kakao.com/oauth/authorize?${params.toString()}`;
window.location.href = KAKAO_URL;
};
return (
@@ -80,6 +90,6 @@ const SocialLoginBox = styled.div`
const StyledImage = styled(Image)`
width: 100%;
height: 100%;
- padding: 0 20px;
+ padding: 0 16px;
object-fit: contain;
`;