Skip to content

Commit

Permalink
feat: 카카오 로그인 기능 추가 (#833)
Browse files Browse the repository at this point in the history
* feat: kakao 이미지 추가

* feat: 정산 시작하기 로그인 페이지로 이동 및 로그인 페이지 제작

* feat: 카카오 로그인 기능 구현

* feat: Amplitude 로그인 한 유저와 아닌 유저 구분하는 기능 구현

* fix: get 응답이 없는 함수를 생성해서 json 오류가 나지 않도록 설정

* test: 카카오 로그인 핸들러 설정

* fix: redirect 설정오류 수정

* feat: tanstack query로 카카오 로그인 불러서 에러처리 되도록 설정

* feat: 이미지 width 설정해서 layout shift 생기지 않도록 설정

* fix: 정산 시작하기 버튼 누를 때 플로우가 바뀌어 cypress 코드 수정

* style: primitive kakao 값 semantic으로 이동

* fix: svg fill currentColor로 변경해서 Icon 컴포넌트에서 색상을 넣어줄 수 있도록 변경

* fix: 카카오 아이콘 색상 변경

* feat: 로그인 유도 멘트 변경

* refactor: redirect uri 도메인 오리진을 얻어서 붙이는 방식으로 변경
  • Loading branch information
jinhokim98 committed Dec 11, 2024
1 parent c66c5ac commit 839fe4e
Show file tree
Hide file tree
Showing 26 changed files with 256 additions and 42 deletions.
2 changes: 2 additions & 0 deletions client/cypress/e2e/createEvent.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ describe('Flow: 랜딩 페이지에서부터 이벤트를 생성 완료하는 fl
it('랜딩페이지에서 "정산 시작하기" 버튼을 눌러 행사 이름 입력 페이지로 이동해야 한다.', () => {
cy.visit('/');
cy.get('button').contains('정산 시작하기').click();
cy.url().should('include', ROUTER_URLS.login);
cy.get('button').contains('비회원으로 진행하기').click();
cy.url().should('include', ROUTER_URLS.createEvent);
});

Expand Down
13 changes: 13 additions & 0 deletions client/src/apis/fetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,19 @@ export const requestGet = async <T>({
return data;
};

export const requestGetWithoutResponse = async ({
headers = {},
errorHandlingStrategy,
...args
}: WithErrorHandlingStrategy<RequestMethodProps>) => {
await request({
...args,
method: 'GET',
headers,
errorHandlingStrategy,
});
};

export const requestPatch = ({headers = {}, ...args}: RequestMethodProps) => {
return request({method: 'PATCH', headers, ...args});
};
Expand Down
20 changes: 19 additions & 1 deletion client/src/apis/request/auth.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import {BASE_URL} from '@apis/baseUrl';
import {ADMIN_API_PREFIX, USER_API_PREFIX} from '@apis/endpointPrefix';
import {requestPostWithoutResponse} from '@apis/fetcher';
import {requestGet, requestGetWithoutResponse, requestPostWithoutResponse} from '@apis/fetcher';
import {WithEventId} from '@apis/withId.type';

import getKakaoRedirectUrl from '@utils/getKakaoRedirectUrl';

export const requestPostAuthentication = async ({eventId}: WithEventId) => {
await requestPostWithoutResponse({
baseUrl: BASE_URL.HD,
Expand All @@ -23,3 +25,19 @@ export const requestPostToken = async ({eventId, password}: WithEventId<RequestP
},
});
};

export const requestKakaoClientId = async () => {
return await requestGet<{clientId: string}>({
baseUrl: BASE_URL.HD,
endpoint: '/api/kakao-client-id',
});
};

export const requestGetKakaoLogin = async (code: string) => {
await requestGetWithoutResponse({
baseUrl: BASE_URL.HD,
endpoint: `/api/login/kakao?code=${code}&redirect_uri=${getKakaoRedirectUrl()}`,
});

return null;
};
3 changes: 3 additions & 0 deletions client/src/assets/image/kakao.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ const getButtonVariantsStyle = (variants: ButtonVariants, theme: Theme) => {
}),
getHoverAndActiveBackground(theme.colors.tertiary),
],
kakao: [
css({
backgroundColor: theme.colors.kakao,
color: theme.colors.onKakao,
}),
],
};

return style[variants];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {Theme} from '@theme/theme.type';

export type ButtonSize = 'small' | 'medium' | 'semiLarge' | 'large';
export type ButtonVariants = 'primary' | 'secondary' | 'tertiary' | 'destructive' | 'loading';
export type ButtonVariants = 'primary' | 'secondary' | 'tertiary' | 'destructive' | 'loading' | 'kakao';

export interface ButtonStyleProps {
variants?: ButtonVariants;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@ const getFixedButtonVariantsStyle = (variants: ButtonVariants, theme: Theme) =>
}),
getHoverAndActiveBackground(theme.colors.tertiary),
],
kakao: [
css({
backgroundColor: theme.colors.kakao,
color: theme.colors.onKakao,
}),
],
};

return style[variants];
Expand Down
1 change: 1 addition & 0 deletions client/src/components/Design/components/Icon/Icon.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const ICON_DEFAULT_COLOR: Record<IconType, IconColor> = {
heundeut: 'gray',
photoButton: 'white',
chevronDown: 'tertiary',
kakao: 'onKakao',
};

export const iconStyle = ({iconType, theme, iconColor}: IconStylePropsWithTheme) => {
Expand Down
2 changes: 2 additions & 0 deletions client/src/components/Design/components/Icon/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import InputDelete from '@assets/image/inputDelete.svg';
import Error from '@assets/image/error.svg';
import Confirm from '@assets/image/confirm.svg';
import Kakao from '@assets/image/kakao.svg';
import Trash from '@assets/image/trash.svg';
import TrashMini from '@assets/image/trash_mini.svg';
import Search from '@assets/image/search.svg';
Expand Down Expand Up @@ -41,6 +42,7 @@ export const ICON = {
),
photoButton: <PhotoButton />,
chevronDown: <ChevronDownLarge />,
kakao: <Kakao />,
} as const;

export const Icon = ({iconColor, iconType, ...htmlProps}: IconProps) => {
Expand Down
7 changes: 6 additions & 1 deletion client/src/components/Design/token/colors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ export type ColorKeys =
| 'errorContainer'
| 'onErrorContainer'
| 'warn'
| 'complete';
| 'complete'
| 'kakao'
| 'onKakao';
export type ColorTokens = Record<ColorKeys, Color>;

// TODO: (@soha) 대괄호 사용에 대해 논의
Expand All @@ -106,6 +108,9 @@ export const COLORS: ColorTokens = {
onErrorContainer: PRIMITIVE_COLORS.pink[300],
warn: PRIMITIVE_COLORS.yellow[400],
complete: PRIMITIVE_COLORS.green[300],

kakao: '#FEE500',
onKakao: '#181600',
};

export const PRIMARY_COLORS = PRIMITIVE_COLORS.purple;
2 changes: 2 additions & 0 deletions client/src/constants/queryKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ const QUERY_KEYS = {
reports: 'reports',
billDetails: 'billDetails',
images: 'images',
kakaoClientId: 'kakao-client-id',
kakaoLogin: 'kakao-login',
};

export default QUERY_KEYS;
1 change: 1 addition & 0 deletions client/src/constants/routerUrls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ export const ROUTER_URLS = {
addImages: '/event/:eventId/admin/add-images',
send: 'event/:eventId/:memberId/send',
qrCode: 'event/:eventId/qrcode',
login: '/login',
};
1 change: 1 addition & 0 deletions client/src/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ declare namespace NodeJS {
readonly API_BASE_URL: string;
readonly AMPLITUDE_KEY: string;
readonly KAKAO_JAVASCRIPT_KEY: string;
readonly KAKAO_REDIRECT_URI: string;
readonly IMAGE_URL: string;
}
}
20 changes: 20 additions & 0 deletions client/src/hooks/queries/auth/useRequestGetKakaoClientId.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {useQuery} from '@tanstack/react-query';

import {requestKakaoClientId} from '@apis/request/auth';

import QUERY_KEYS from '@constants/queryKeys';

const useRequestGetKakaoClientId = () => {
const {refetch, ...rest} = useQuery({
queryKey: [QUERY_KEYS.kakaoClientId],
queryFn: requestKakaoClientId,
enabled: false,
});

return {
requestGetClientId: refetch,
...rest,
};
};

export default useRequestGetKakaoClientId;
22 changes: 22 additions & 0 deletions client/src/hooks/queries/auth/useRequestGetKakaoLogin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {useQuery} from '@tanstack/react-query';

import {requestGetKakaoLogin} from '@apis/request/auth';

import QUERY_KEYS from '@constants/queryKeys';

const useRequestGetKakaoLogin = () => {
const code = new URLSearchParams(location.search).get('code');

const {refetch, ...rest} = useQuery({
queryKey: [QUERY_KEYS.kakaoLogin, code],
queryFn: () => requestGetKakaoLogin(code ?? ''),
enabled: false,
});

return {
requestGetKakaoLogin: refetch,
...rest,
};
};

export default useRequestGetKakaoLogin;
6 changes: 4 additions & 2 deletions client/src/hooks/useAmplitude.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ const useAmplitude = () => {
});
};

const trackStartCreateEvent = () => {
track('정산 시작하기 버튼 클릭');
const trackStartCreateEvent = ({login}: {login: boolean}) => {
track('정산 시작하기 버튼 클릭', {
login,
});
};

const trackCompleteCreateEvent = (eventUniqueData: EventUniqueData) => {
Expand Down
58 changes: 58 additions & 0 deletions client/src/hooks/useLoginPage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import {useEffect} from 'react';
import {useLocation, useNavigate} from 'react-router-dom';

import {useAuthStore} from '@store/authStore';

import getKakaoRedirectUrl from '@utils/getKakaoRedirectUrl';

import {ROUTER_URLS} from '@constants/routerUrls';

import useRequestGetKakaoClientId from './queries/auth/useRequestGetKakaoClientId';
import useAmplitude from './useAmplitude';
import useRequestGetKakaoLogin from './queries/auth/useRequestGetKakaoLogin';

const useLoginPage = () => {
const navigate = useNavigate();
const location = useLocation();
const {trackStartCreateEvent} = useAmplitude();
const {updateAuth} = useAuthStore();
const {requestGetKakaoLogin} = useRequestGetKakaoLogin();

const {requestGetClientId} = useRequestGetKakaoClientId();

const goKakaoLogin = async () => {
const queryResult = await requestGetClientId();
const clientId = queryResult.data?.clientId;

const link = `https://kauth.kakao.com/oauth/authorize?client_id=${clientId}&redirect_uri=${getKakaoRedirectUrl()}&response_type=code`;
window.location.href = link;
};

const goNonLoginCreateEvent = () => {
trackStartCreateEvent({login: false});
navigate(ROUTER_URLS.createEvent);
};

useEffect(() => {
if (location.search === '') return;

const code = new URLSearchParams(location.search).get('code');

const kakaoLogin = async () => {
if (code) {
await requestGetKakaoLogin();
updateAuth(true);

// 추후에 업데이트 하는 로직 필요
trackStartCreateEvent({login: true});
navigate(ROUTER_URLS.createEvent);
}
};

kakaoLogin();
}, [location.search]);

return {goKakaoLogin, goNonLoginCreateEvent};
};

export default useLoginPage;
16 changes: 0 additions & 16 deletions client/src/hooks/useMainSection.ts

This file was deleted.

4 changes: 4 additions & 0 deletions client/src/mocks/handlers/authHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ export const authHandler = [
return new HttpResponse(null, {status: 200});
}),

http.get(`${MOCK_API_PREFIX}/api/login/kakao`, () => {
return new HttpResponse(null, {status: 200});
}),

// POST /api/eventId/login (requestPostToken)
http.post<{eventId: string}, {password: string}>(
`${MOCK_API_PREFIX}${USER_API_PREFIX}/:eventId/login`,
Expand Down
11 changes: 11 additions & 0 deletions client/src/pages/LoginPage/LoginPage.style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {css} from '@emotion/react';

import {Theme} from '@components/Design/theme/theme.type';

export const hrStyle = (theme: Theme) =>
css({
width: '100%',
height: 1,

backgroundColor: theme.colors.tertiary,
});
49 changes: 49 additions & 0 deletions client/src/pages/LoginPage/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import Image from '@components/Design/components/Image/Image';

import useLoginPage from '@hooks/useLoginPage';

import {Button, Flex, FunnelLayout, Icon, MainLayout, Text, TopNav, useTheme} from '@components/Design';

import getImageUrl from '@utils/getImageUrl';

import {hrStyle} from './LoginPage.style';

const LOGIN_COMMENT = `로그인을 하면 계좌번호를 저장하고\n이전 행사들을 쉽게 볼 수 있어요.`;

const LoginPage = () => {
const {theme} = useTheme();

const {goKakaoLogin, goNonLoginCreateEvent} = useLoginPage();

return (
<MainLayout backgroundColor="white">
<TopNav>
<TopNav.Item displayName="뒤로가기" noEmphasis routePath="-1" />
</TopNav>
<FunnelLayout>
<Flex flexDirection="column" justifyContent="spaceBetween" height="100%">
<Flex flexDirection="column" justifyContent="center" alignItems="center" gap="1rem" margin="0 0 6rem 0">
<Image src={getImageUrl('heundeut', 'webp')} fallbackSrc={getImageUrl('heundeut', 'png')} width={109} />
<Text size="bodyBold" css={{whiteSpace: 'pre-line', textAlign: 'center'}}>
{LOGIN_COMMENT}
</Text>
</Flex>
<Flex flexDirection="column" gap="1rem" width="100%" padding="0 2rem" paddingInline="auto">
<Button variants="kakao" size="large" onClick={goKakaoLogin}>
<Flex alignItems="center" gap="0.625rem">
<Icon iconType="kakao" />
카카오 로그인
</Flex>
</Button>
<hr css={hrStyle(theme)} />
<Button variants="secondary" size="large" onClick={goNonLoginCreateEvent}>
비회원으로 진행하기
</Button>
</Flex>
</Flex>
</FunnelLayout>
</MainLayout>
);
};

export default LoginPage;
7 changes: 2 additions & 5 deletions client/src/pages/MainPage/MainPage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import Image from '@components/Design/components/Image/Image';

import useAmplitude from '@hooks/useAmplitude';
import usePageBackground from '@hooks/usePageBackground';

import getImageUrl from '@utils/getImageUrl';
Expand All @@ -13,16 +12,14 @@ import {backgroundImageStyle, backgroundStyle, mainContainer} from './MainPage.s
import CreatorSection from './Section/CreatorSection/CreatorSection';

const MainPage = () => {
const {trackStartCreateEvent} = useAmplitude();
const {isVisible} = usePageBackground();
return (
<div css={mainContainer}>
<Nav trackStartCreateEvent={trackStartCreateEvent} />
<MainSection trackStartCreateEvent={trackStartCreateEvent} />
<Nav />
<MainSection />
<DescriptionSection />
<FeatureSection />
<CreatorSection />

<div css={backgroundStyle}>
<Image
css={backgroundImageStyle(isVisible)}
Expand Down
Loading

0 comments on commit 839fe4e

Please sign in to comment.