Skip to content

Commit f02c0f5

Browse files
authored
Merge pull request #166 from ASAP-Lettering/feat/#163
[Feat] 구글 로그인
2 parents 5159cf6 + 86d90b8 commit f02c0f5

4 files changed

Lines changed: 174 additions & 76 deletions

File tree

src/app/login/auth/page.tsx

Lines changed: 86 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@ const Auth = () => {
1717
const [absoluteUrl, setAbsoluteUrl] = useState('');
1818
const [storeUrl, setstoreUrl] = useState('');
1919
const [type, setType] = useState('');
20+
const [oauthAccessToken, setOauthAccessToken] = useState('');
21+
const [provider, setProvider] = useState('');
2022

2123
useEffect(() => {
2224
if (typeof window !== 'undefined') {
25+
//oauth 타입을 state에 저장
2326
const params = new URL(window.location.href).searchParams;
2427
const typeParam = params.get('type');
2528
setType(typeParam);
@@ -31,79 +34,117 @@ const Auth = () => {
3134
}, []);
3235

3336
useEffect(() => {
34-
if (!absoluteUrl) {
35-
return;
36-
}
37+
if (!absoluteUrl || !type) return;
3738
const getToken = async () => {
3839
const AUTHORIZATION_CODE = new URL(window.location.href).searchParams.get(
3940
'code'
4041
);
41-
const TYPE = new URL(window.location.href).searchParams.get('type');
4242

43-
let tokenUrl = '';
44-
let provider: 'KAKAO' | 'GOOGLE' | 'NAVER';
43+
const TYPE = new URL(window.location.href).searchParams.get('type');
4544

4645
if (!AUTHORIZATION_CODE || !TYPE) {
4746
console.error('Authorization Code or Type is missing');
4847
return;
4948
}
5049

50+
//type에 따라 다른 토큰 url 지정
5151
switch (TYPE) {
5252
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';
53+
setProvider('KAKAO');
54+
try {
55+
const response = await axios.post(
56+
'https://kauth.kakao.com/oauth/token',
57+
new URLSearchParams({
58+
grant_type: 'authorization_code',
59+
client_id: REST_API_KEY,
60+
redirect_uri: absoluteUrl,
61+
code: AUTHORIZATION_CODE
62+
}),
63+
{
64+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
65+
}
66+
);
67+
setOauthAccessToken(response.data.access_token);
68+
} catch (error) {
69+
console.error(error);
70+
clearLetterUrl();
71+
return;
72+
}
73+
5574
break;
5675
case 'google':
76+
setProvider('GOOGLE');
77+
try {
78+
const body = new URLSearchParams({
79+
grant_type: 'authorization_code',
80+
client_id: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID!,
81+
client_secret: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_SECRET!,
82+
redirect_uri: absoluteUrl,
83+
code: AUTHORIZATION_CODE
84+
});
85+
86+
const response = await axios.post(
87+
'https://oauth2.googleapis.com/token',
88+
body.toString(),
89+
{
90+
headers: {
91+
'Content-Type': 'application/x-www-form-urlencoded'
92+
}
93+
}
94+
);
95+
setOauthAccessToken(response.data.access_token);
96+
} catch (error) {
97+
console.error('Unsupported OAuth type:', type);
98+
clearLetterUrl();
99+
return;
100+
}
57101
break;
58102
case 'naver':
59103
break;
60104
default:
61105
console.error('Unknown OAuth type:', TYPE);
62106
return;
63107
}
108+
};
109+
getToken();
110+
}, [absoluteUrl, type]);
64111

65-
try {
66-
const response = await axios.post(tokenUrl, {
67-
headers: { 'Content-Type': 'application/json' }
68-
});
69-
70-
const oauthAccessToken = response.data.access_token;
71-
72-
if (oauthAccessToken) {
73-
login(provider, oauthAccessToken)
74-
.then((res) => {
75-
console.log('accessToken', res.data.accessToken);
76-
setTokens(res.data.accessToken, res.data.refreshToken);
77-
/* 온보딩 여부 저장 */
78-
setOnboarding(res.data.isProcessedOnboarding);
112+
useEffect(() => {
113+
try {
114+
if (oauthAccessToken) {
115+
login(provider, oauthAccessToken)
116+
.then((res) => {
117+
console.log('accessToken', res.data.accessToken);
118+
setTokens(res.data.accessToken, res.data.refreshToken);
119+
/* 온보딩 여부 저장 */
120+
setOnboarding(res.data.isProcessedOnboarding);
121+
if (storeUrl) {
122+
router.push(`/verify/letter?url=${storeUrl}`);
123+
clearLetterUrl();
124+
} else {
125+
router.push('/planet');
126+
}
127+
})
128+
.catch((error) => {
129+
if (error.response && error.response.status === 401) {
130+
console.log('registerToken', error.response.data.registerToken);
131+
setRegisterToken(error.response.data.registerToken);
79132
if (storeUrl) {
80-
router.push(`/verify/letter?url=${storeUrl}`);
133+
router.push(`/signup/step1?url=${storeUrl}`);
81134
clearLetterUrl();
82135
} else {
83-
router.push('/planet');
136+
router.push('/signup/step1');
84137
}
85-
})
86-
.catch((error) => {
87-
if (error.response && error.response.status === 401) {
88-
console.log('registerToken', error.response.data.registerToken);
89-
setRegisterToken(error.response.data.registerToken);
90-
if (storeUrl) {
91-
router.push(`/signup/step1?url=${storeUrl}`);
92-
clearLetterUrl();
93-
} else {
94-
router.push('/signup/step1');
95-
}
96-
}
97-
});
98-
}
99-
} catch (error) {
100-
console.error(error);
101-
clearLetterUrl();
102-
return;
138+
}
139+
});
103140
}
104-
};
105-
getToken();
106-
}, [absoluteUrl]);
141+
} catch (error) {
142+
console.log('oauth token 에러');
143+
console.error(error);
144+
clearLetterUrl();
145+
return;
146+
}
147+
}, [oauthAccessToken]);
107148

108149
return (
109150
<Container>

src/app/login/page.tsx

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,36 @@ interface OauthButtonProps {
1010
bgColor: string;
1111
}
1212

13+
const notReady = () => {
14+
alert('준비 중입니다.');
15+
};
16+
17+
const oauthButtons = [
18+
{
19+
key: 'naver',
20+
bgColor: '#03CF5D',
21+
component: (
22+
<Image
23+
src="/assets/icons/ic_naver.svg"
24+
alt="Naver"
25+
width={26}
26+
height={26}
27+
onClick={() => alert('준비 중입니다.')}
28+
/>
29+
)
30+
},
31+
{
32+
key: 'google',
33+
bgColor: '#FFFFFF',
34+
component: <SocialGoogle />
35+
},
36+
{
37+
key: 'kakao',
38+
bgColor: '#FEE500',
39+
component: <SocialKakao />
40+
}
41+
];
42+
1343
export default function Login() {
1444
return (
1545
<Container>
@@ -18,22 +48,15 @@ export default function Login() {
1848
<LogoText>편지로 수놓는 나의 스페이스</LogoText>
1949
<LogoImage src="/assets/login/login_logo.png" />
2050
<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>
51+
{oauthButtons.map(({ key, bgColor, component }) => (
52+
<OauthButton key={key} bgColor={bgColor}>
53+
{component}
54+
</OauthButton>
55+
))}
3556
</OauthWrapper>
36-
<LetterBtnText>로그인 없이 편지 작성해보기</LetterBtnText>
57+
<LetterBtnText onClick={notReady}>
58+
로그인 없이 편지 작성해보기
59+
</LetterBtnText>
3760
</ImageWrapper>
3861
</Container>
3962
);

src/app/mypage/page.tsx

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ const MyPage = () => {
1717
const [email, setEmail] = useState('');
1818
const [planetCount, setPlanetCount] = useState(0);
1919
const [letterCount, setLetterCount] = useState(0);
20+
const [platform, setPlatform] = useState('');
21+
2022
const [loading, setLoading] = useState(true);
2123

2224
useEffect(() => {
@@ -62,7 +64,8 @@ const MyPage = () => {
6264
const response = await getUserInfo();
6365
setName(response.data.name);
6466
setEmail(response.data.email);
65-
console.log('회원정보 조회 성공:', response.data);
67+
setPlatform(response.data.socialPlatform);
68+
// console.log('회원정보 조회 성공:', response.data);
6669
} catch (error) {
6770
console.error('회원정보 조회 실패:', error);
6871
}
@@ -78,6 +81,19 @@ const MyPage = () => {
7881
}
7982
};
8083

84+
const EmailType = (platform): string => {
85+
switch (platform) {
86+
case 'GOOGLE':
87+
return '/assets/icons/ic_google.svg';
88+
case 'KAKAO':
89+
return '/assets/icons/ic_kakao_profile.svg';
90+
case 'NAVER':
91+
return '/assets/icons/ic_naver.svg';
92+
default:
93+
return '';
94+
}
95+
};
96+
8197
return (
8298
<Container>
8399
{loading ? (
@@ -96,7 +112,11 @@ const MyPage = () => {
96112
<ProfileInfo>
97113
<ProfileName>{name}님의 스페이스</ProfileName>
98114
<ProfileEmail>
99-
<img src="/assets/icons/ic_kakao_profile.svg" />
115+
<StyledIcon
116+
src={EmailType(platform)}
117+
alt="emailIcon"
118+
platform={platform as keyof typeof iconSizes}
119+
/>
100120
<div>{email}</div>
101121
</ProfileEmail>
102122
<CountRaw>
@@ -238,6 +258,17 @@ const ProfileImage = styled.img`
238258
}
239259
`;
240260

261+
const iconSizes = {
262+
GOOGLE: 20,
263+
KAKAO: 20,
264+
NAVER: 20
265+
} as const;
266+
267+
const StyledIcon = styled.img<{ platform: keyof typeof iconSizes }>`
268+
width: ${({ platform }) => iconSizes[platform]}px;
269+
height: ${({ platform }) => iconSizes[platform]}px;
270+
`;
271+
241272
const ProfileInfo = styled.div`
242273
display: flex;
243274
flex-direction: column;

src/components/signup/SocialGoogle.tsx

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,36 @@ import Loader, { LoaderContainer } from '../common/Loader';
88
const SocialGoogle = () => {
99
const searchParams = useSearchParams();
1010
const url = searchParams.get('url');
11-
const REST_API_KEY = process.env.NEXT_PUBLIC_REST_API_KEY;
12-
const GOOGLE_URL = '/login/google';
1311
const [absoluteUrl, setabsoluteUrl] = useState('');
1412

1513
useEffect(() => {
1614
if (typeof window !== 'undefined') {
1715
setabsoluteUrl(
18-
window.location.protocol + '//' + window.location.host + '/login/kakao'
16+
window.location.protocol +
17+
'//' +
18+
window.location.host +
19+
'/login/auth?type=google'
1920
);
2021
}
2122
}, []);
2223

2324
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를 저장한다.
3425
if (url) {
3526
setLetterUrl(url);
3627
}
37-
window.location.href = GOOGLE_URL;
28+
const GOOGLE_CLIENT_ID = process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID;
29+
const scope = 'openid profile email';
30+
31+
const authUrl = [
32+
'https://accounts.google.com/o/oauth2/v2/auth',
33+
`?client_id=${GOOGLE_CLIENT_ID}`,
34+
`&redirect_uri=${encodeURIComponent(absoluteUrl)}`,
35+
`&response_type=code`,
36+
`&scope=${encodeURIComponent(scope)}`,
37+
`&access_type=offline`,
38+
`&prompt=consent`
39+
].join('');
40+
window.location.href = authUrl;
3841
};
3942

4043
return (

0 commit comments

Comments
 (0)