Skip to content

Commit 5159cf6

Browse files
authored
Merge pull request #164 from ASAP-Lettering/feat/#163
[Design & Refactor] OAuth 통합 레이아웃, 로그인 홈 버튼 UI
2 parents 02a5f2b + 11f8908 commit 5159cf6

File tree

7 files changed

+231
-71
lines changed

7 files changed

+231
-71
lines changed

public/assets/icons/ic_google.svg

Lines changed: 6 additions & 0 deletions
Loading
Lines changed: 4 additions & 0 deletions
Loading

public/assets/icons/ic_naver.svg

Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 51 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,30 @@
1-
"use client";
1+
'use client';
22

3-
import { login } from "@/api/login/user";
4-
import Loader from "@/components/common/Loader";
5-
import { signupState } from "@/recoil/signupStore";
6-
import {
7-
clearLetterUrl,
8-
getLetterUrl,
9-
setOnboarding,
10-
setTokens,
11-
} from "@/utils/storage";
12-
import axios from "axios";
13-
import { useRouter } from "next/navigation";
14-
import { useEffect, useState } from "react";
15-
import { useRecoilState } from "recoil";
16-
import styled from "styled-components";
3+
import { login } from '@/api/login/user';
4+
import Loader from '@/components/common/Loader';
5+
import { signupState } from '@/recoil/signupStore';
6+
import { clearLetterUrl, setOnboarding, setTokens } from '@/utils/storage';
7+
import axios from 'axios';
8+
import { useRouter } from 'next/navigation';
9+
import { useEffect, useState } from 'react';
10+
import { useRecoilState } from 'recoil';
11+
import styled from 'styled-components';
1712

1813
const Auth = () => {
1914
const [registerToken, setRegisterToken] = useRecoilState(signupState);
2015
const router = useRouter();
2116
const REST_API_KEY = process.env.NEXT_PUBLIC_REST_API_KEY;
22-
const [absoluteUrl, setAbsoluteUrl] = useState("");
23-
const [storeUrl, setstoreUrl] = useState("");
17+
const [absoluteUrl, setAbsoluteUrl] = useState('');
18+
const [storeUrl, setstoreUrl] = useState('');
19+
const [type, setType] = useState('');
2420

2521
useEffect(() => {
26-
if (typeof window !== "undefined") {
27-
const url = `${window.location.protocol}//${window.location.host}/login/kakao`;
28-
const letterId = localStorage.getItem("letter_url");
22+
if (typeof window !== 'undefined') {
23+
const params = new URL(window.location.href).searchParams;
24+
const typeParam = params.get('type');
25+
setType(typeParam);
26+
const url = `${window.location.protocol}//${window.location.host}/login/auth?type=${typeParam}`;
27+
const letterId = localStorage.getItem('letter_url');
2928
setAbsoluteUrl(url);
3029
if (letterId) setstoreUrl(letterId);
3130
}
@@ -37,47 +36,62 @@ const Auth = () => {
3736
}
3837
const getToken = async () => {
3938
const AUTHORIZATION_CODE = new URL(window.location.href).searchParams.get(
40-
"code"
39+
'code'
4140
);
41+
const TYPE = new URL(window.location.href).searchParams.get('type');
4242

43-
if (!AUTHORIZATION_CODE) {
44-
console.error("Authorization Code is missing");
43+
let tokenUrl = '';
44+
let provider: 'KAKAO' | 'GOOGLE' | 'NAVER';
45+
46+
if (!AUTHORIZATION_CODE || !TYPE) {
47+
console.error('Authorization Code or Type is missing');
4548
return;
4649
}
4750

51+
switch (TYPE) {
52+
case 'kakao':
53+
tokenUrl = `https://kauth.kakao.com/oauth/token?grant_type=authorization_code&client_id=${REST_API_KEY}&redirect_uri=${absoluteUrl}&code=${AUTHORIZATION_CODE}`;
54+
provider = 'KAKAO';
55+
break;
56+
case 'google':
57+
break;
58+
case 'naver':
59+
break;
60+
default:
61+
console.error('Unknown OAuth type:', TYPE);
62+
return;
63+
}
64+
4865
try {
49-
const response = await axios.post(
50-
`https://kauth.kakao.com/oauth/token?grant_type=authorization_code&client_id=${REST_API_KEY}&redirect_uri=${absoluteUrl}&code=${AUTHORIZATION_CODE}`,
51-
{
52-
headers: { "Content-Type": "application/json" },
53-
}
54-
);
66+
const response = await axios.post(tokenUrl, {
67+
headers: { 'Content-Type': 'application/json' }
68+
});
69+
70+
const oauthAccessToken = response.data.access_token;
5571

56-
const kakao_accessToken = response.data.access_token;
57-
console.log(kakao_accessToken);
58-
if (kakao_accessToken) {
59-
login("KAKAO", kakao_accessToken)
72+
if (oauthAccessToken) {
73+
login(provider, oauthAccessToken)
6074
.then((res) => {
61-
console.log("accessToken", res.data.accessToken);
75+
console.log('accessToken', res.data.accessToken);
6276
setTokens(res.data.accessToken, res.data.refreshToken);
6377
/* 온보딩 여부 저장 */
6478
setOnboarding(res.data.isProcessedOnboarding);
6579
if (storeUrl) {
6680
router.push(`/verify/letter?url=${storeUrl}`);
6781
clearLetterUrl();
6882
} else {
69-
router.push("/planet");
83+
router.push('/planet');
7084
}
7185
})
7286
.catch((error) => {
7387
if (error.response && error.response.status === 401) {
74-
console.log("registerToken", error.response.data.registerToken);
88+
console.log('registerToken', error.response.data.registerToken);
7589
setRegisterToken(error.response.data.registerToken);
7690
if (storeUrl) {
7791
router.push(`/signup/step1?url=${storeUrl}`);
7892
clearLetterUrl();
7993
} else {
80-
router.push("/signup/step1");
94+
router.push('/signup/step1');
8195
}
8296
}
8397
});

src/app/login/page.tsx

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1-
"use client";
1+
'use client';
22

3-
import SocialKakao from "@/components/signup/SocialKakao";
4-
import { theme } from "@/styles/theme";
5-
import styled from "styled-components";
3+
import SocialKakao from '@/components/signup/SocialKakao';
4+
import SocialGoogle from '@/components/signup/SocialGoogle';
5+
import { theme } from '@/styles/theme';
6+
import styled from 'styled-components';
7+
import Image from 'next/image';
8+
9+
interface OauthButtonProps {
10+
bgColor: string;
11+
}
612

713
export default function Login() {
814
return (
@@ -11,9 +17,23 @@ export default function Login() {
1117
<LogoTitle data="/assets/login/login_text.svg" />
1218
<LogoText>편지로 수놓는 나의 스페이스</LogoText>
1319
<LogoImage src="/assets/login/login_logo.png" />
14-
<SocialKakaoWrapper>
15-
<SocialKakao />
16-
</SocialKakaoWrapper>
20+
<OauthWrapper>
21+
<OauthButton bgColor="#03CF5D">
22+
<Image
23+
src="/assets/icons/ic_naver.svg"
24+
alt="Naver"
25+
width={26}
26+
height={26}
27+
/>
28+
</OauthButton>
29+
<OauthButton bgColor="#FFFFFF">
30+
<SocialGoogle />
31+
</OauthButton>
32+
<OauthButton bgColor="#FEE500">
33+
<SocialKakao />
34+
</OauthButton>
35+
</OauthWrapper>
36+
<LetterBtnText>로그인 없이 편지 작성해보기</LetterBtnText>
1737
</ImageWrapper>
1838
</Container>
1939
);
@@ -25,7 +45,7 @@ const Container = styled.div`
2545
height: 100vh;
2646
flex-direction: column;
2747
justify-content: space-between;
28-
background-image: url("/assets/login/login_bg.png");
48+
background-image: url('/assets/login/login_bg.png');
2949
background-size: cover;
3050
background-position: center;
3151
background-repeat: no-repeat;
@@ -104,13 +124,35 @@ const ImageWrapper = styled.div`
104124
overflow: hidden;
105125
`;
106126

107-
const SocialKakaoWrapper = styled.div`
127+
const OauthWrapper = styled.div`
108128
width: 100%;
109129
max-width: 393px;
110-
padding: 0 4px;
130+
gap: 24px;
131+
display: flex;
132+
position: absolute;
133+
bottom: 123px;
134+
justify-content: center;
135+
`;
136+
137+
const OauthButton = styled.button<OauthButtonProps>`
138+
width: 69px;
139+
height: 69px;
140+
border-radius: 50%;
141+
border: none;
142+
background-color: ${({ bgColor }) => bgColor};
111143
display: flex;
144+
align-items: center;
145+
overflow: hidden;
146+
justify-content: center;
147+
cursor: pointer;
148+
transition: background-color 0.3s;
149+
`;
150+
151+
const LetterBtnText = styled.div`
152+
${(props) => props.theme.fonts.caption02};
153+
color: ${theme.colors.gray400};
112154
position: absolute;
113-
bottom: 60px;
114-
left: 50%;
115-
transform: translate(-50%);
155+
bottom: 69px;
156+
text-decoration-line: underline;
157+
cursor: pointer;
116158
`;
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import React, { Suspense, useEffect, useState } from 'react';
2+
import Image from 'next/image';
3+
import styled from 'styled-components';
4+
import { useSearchParams } from 'next/navigation';
5+
import { setLetterUrl } from '@/utils/storage';
6+
import Loader, { LoaderContainer } from '../common/Loader';
7+
8+
const SocialGoogle = () => {
9+
const searchParams = useSearchParams();
10+
const url = searchParams.get('url');
11+
const REST_API_KEY = process.env.NEXT_PUBLIC_REST_API_KEY;
12+
const GOOGLE_URL = '/login/google';
13+
const [absoluteUrl, setabsoluteUrl] = useState('');
14+
15+
useEffect(() => {
16+
if (typeof window !== 'undefined') {
17+
setabsoluteUrl(
18+
window.location.protocol + '//' + window.location.host + '/login/kakao'
19+
);
20+
}
21+
}, []);
22+
23+
const handleLogin = () => {
24+
//이때 localStorage에 저장된 accessToken이 만료되었는지 확인해야함.
25+
// if (accessToken) {
26+
// if (url) {
27+
// router.push(`/verify?url=${url}`);
28+
// } else {
29+
// router.push("/");
30+
// }
31+
// setAccessToken(accessToken);
32+
// } else {
33+
//받은 편지를 통해 들어올 경우 url를 저장한다.
34+
if (url) {
35+
setLetterUrl(url);
36+
}
37+
window.location.href = GOOGLE_URL;
38+
};
39+
40+
return (
41+
<SocialLoginBox onClick={handleLogin}>
42+
<StyledImage
43+
src="/assets/icons/ic_google.svg"
44+
width={33}
45+
height={33}
46+
alt="google"
47+
/>
48+
</SocialLoginBox>
49+
);
50+
};
51+
52+
export default function SocialGooglePaging() {
53+
return (
54+
<Suspense
55+
fallback={
56+
<LoaderContainer>
57+
<Loader />
58+
</LoaderContainer>
59+
}
60+
>
61+
<SocialGoogle />
62+
</Suspense>
63+
);
64+
}
65+
66+
const SocialLoginBox = styled.div`
67+
display: flex;
68+
box-sizing: border-box;
69+
width: 100%;
70+
cursor: pointer;
71+
display: flex;
72+
justify-content: center;
73+
align-items: center;
74+
`;
75+
76+
const StyledImage = styled(Image)`
77+
width: 100%;
78+
height: 100%;
79+
padding: 0 16px;
80+
object-fit: contain;
81+
`;

0 commit comments

Comments
 (0)