Skip to content

Commit

Permalink
Feat: 로그인 팝업창 및 서버 구현 (#40)
Browse files Browse the repository at this point in the history
* feat: 회원 가입 라우팅 설정

* feat: 공통 컴포넌트 반영

* feat:바텀 버튼 공통 컴포넌트 구현

* refactor: 마이페이지에 공통 버튼 컴포넌트 적용

* feat: Header 공통 컴포넌트 생성

* feat: 회원 가입 플로우에 헤더 적용

* chore: 오타 수정

* refactor: 선택지 컴포넌트 분리

* chore: 변수명 변경

* refactor: 마이페이지 공통 컴포넌트 적용 및 상태 리팩토링

* refactor: 공통 컴포넌트 지역 선택 드롭다운 리팩토링

* feat:사용자의  클릭 영억에 따른 기능 추가

* refacotr:중복되는 코드 단축

* feat:네비게이션 설정

* feat:여행자 유형 설정 상태 관리

* feat: 회원가입 플로우 여행자 유형 설정

* chore: 빼먹은 코드 추가 및 불필요한 코드 삭제

* feat:로그인 모달에 로그인 함수 추가

* design: 바텀 버튼 레이아웃 수정

* design: 디자인 요소 수정

* feat:context api 생성

* feat: 지역 상태 저장

* feat: 여행자 유형 상태 관리

* fix: 여행자 유형 상태 관리 수정

* feat: 지역 입력 추상화

* feat: 메인 페이지에 모달 붙이기

* feat: 로그인 추가 정보 받아오기

* chore: 파일 위치 변경

* feat: 로그인 분기처리

* chore: 에러 메시지 추가

* feat: supabse에 회원 정보 전송

* feat: 메인 분기 처리

* fix:로그인 오류 수정

* fix:회원 가입 직후 에러 케이스 수정

* refactor: 렌더링 로직 수정
  • Loading branch information
aazkgh authored Sep 25, 2024
1 parent f8f6426 commit dbdd134
Show file tree
Hide file tree
Showing 18 changed files with 298 additions and 73 deletions.
5 changes: 5 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ module.exports = {
browser: true,
es2021: true,
},
settings: {
react: {
version: 'detect', // 사용자가 설치한 버전을 자동으로 선택
},
},
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
Expand Down
25 changes: 25 additions & 0 deletions src/api/getKaKaoInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { KakaoResProps } from '../views/Login/types/loginType';

const getKaKaoInfo = async () => {
const response: KakaoResProps = await window.Kakao.API.request({
url: '/v2/user/me',
data: {
property_keys: ['kakao_account.profile'],
},
});

if (!response) {
throw new Error('응답이 없습니다');
}

const {
id,
kakao_account: {
profile: { nickname, thumbnail_image_url },
},
} = response;

return { id, nickname, thumbnail_image_url };
};

export default getKaKaoInfo;
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const fetchSupabaseLogin = async (token: string) => {
});

if (!session) {
console.log('로그인 실패');
throw new Error('supabase 로그인 실패');
}
};

Expand Down
16 changes: 16 additions & 0 deletions src/api/supabase/getUserExistence.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { unitripSupabase } from '@/utils/supabaseClient';

const getUserExistence = async (kakaoId: number) => {
const { count, error } = await unitripSupabase
.from('USER')
.select('id', { count: 'exact' })
.eq('kakao_id', kakaoId);

if (error) {
throw new Error('서버에 문제가 있습니다');
}

return count; // 데이터가 존재하면 true, 없으면 false
};

export default getUserExistence;
40 changes: 40 additions & 0 deletions src/api/supabase/postAddUser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Region } from '@/components/SelectRegion';
import { KakaoUserDataProps } from '@/types/type';
import { unitripSupabase } from '@/utils/supabaseClient';

interface AddUserProps {
userData: KakaoUserDataProps;
region: Region;
travelerType: string[];
}

const postAddUser = async ({
userData,
region,
travelerType,
}: AddUserProps) => {
const { id, nickname, thumbnail_image_url } = userData;

const { data, error } = await unitripSupabase
.from('USER')
.insert([
{
kakao_id: id,
name: nickname,
profile: thumbnail_image_url,
universal_type: travelerType,
region: `${region.city} ${region.town}`,
},
])
.select();

if (data) {
sessionStorage.setItem('kakao_id', String(id));
}

if (error) {
throw new Error('회원가입 과정에 오류가 있습니다');
}
};

export default postAddUser;
23 changes: 23 additions & 0 deletions src/api/supabase/useGetUserData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { UserDataProps } from '@/types/type';
import { unitripSupabase } from '@/utils/supabaseClient';

const getUserData = async (kakaoId: number) => {
const { data, error } = await unitripSupabase
.from('USER')
.select('*')
.eq('kakao_id', kakaoId);

if (error) {
throw new Error('서버에 문제가 있습니다');
}

/* kakao_id에 해당하는 행이 존재하는지 확인 */
if (data.length) {
const response: UserDataProps = data[0];
return response;
} else {
return null;
}
};

export default getUserData;
60 changes: 60 additions & 0 deletions src/api/usePostKakaoLogin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { useNavigate } from 'react-router-dom';

import getKaKaoInfo from './getKaKaoInfo';
import fetchSupabaseLogin from './supabase/fetchSupabaseLogin';
import getUserExistence from './supabase/getUserExistence';

const usePostKakaoLogin = async () => {
const navigate = useNavigate();
/* 인가 코드 받기 */
const KAKAO_CODE = new URL(window.location.href).searchParams.get('code');

if (KAKAO_CODE) {
const tokenResponse = await fetch('https://kauth.kakao.com/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
},
body: new URLSearchParams({
grant_type: 'authorization_code',
client_id: import.meta.env.VITE_CLIENT_ID,
redirect_uri:
import.meta.env.VITE_LOCAL_REDIRECT_URI ||
import.meta.env.VITE_REDIRECT_URI,
code: KAKAO_CODE,
client_secret: import.meta.env.VITE_CLIENT_SECRET,
}),
});

/* token 데이터 받기 */
const tokenData = await tokenResponse.json();
const { access_token, id_token } = tokenData;

/* id 토큰으로 회원 가입 */
if (id_token) {
await fetchSupabaseLogin(id_token);
}

/*
* Access 토큰으로 회원 정보 불러오기
* JS SDK는 리프레쉬 토큰 별도로 사용하지 않음
*/
if (access_token) {
window.Kakao.Auth.setAccessToken(`${access_token}`);
const { id, nickname, thumbnail_image_url } = await getKaKaoInfo();

//로그인 분기 처리
const registered = await getUserExistence(id);
if (!registered) {
navigate(`/sign-up`, { state: { id, nickname, thumbnail_image_url } });
} else {
sessionStorage.setItem('kakao_id', String(id));
navigate(`/`);
}
} else {
throw new Error('카카오 코드가 존재하지 않습니다.');
}
}
};

export default usePostKakaoLogin;
16 changes: 13 additions & 3 deletions src/components/LoginModal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { css } from '@emotion/react';
import { createPortal } from 'react-dom';
import { Link } from 'react-router-dom';

import { KakaoTalkIcon, XMonoIcon } from '@/assets/icon';
import { COLORS, FONTS } from '@/styles/constants';
Expand All @@ -16,15 +15,26 @@ interface LoginModalProps {
const LoginModal = (props: LoginModalProps) => {
const { onClick } = props;

const handleLogin = () => {
if (window.Kakao && window.Kakao.Auth) {
window.Kakao.Auth.authorize({
redirectUri:
import.meta.env.VITE_LOCAL_REDIRECT_URI ||
import.meta.env.VITE_REDIRECT_URI,
scope: 'profile_nickname, profile_image, account_email',
});
}
};

const portalContent = (
<div css={backgroundCss}>
<div css={container}>
<p css={titleCss}>카카오톡 로그인</p>
<p css={descriptionCss}>서비스 이용을 위해 로그인이 필요해요.</p>
<Link to="/login" css={linkCss}>
<button type="button" css={linkCss} onClick={handleLogin}>
<KakaoTalkIcon />
카카오톡 로그인
</Link>
</button>
<button type="button" css={closeButtonCss} onClick={onClick}>
<XMonoIcon />
</button>
Expand Down
11 changes: 10 additions & 1 deletion src/types/globals.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,16 @@ declare global {
init: (appKey: string) => void;
isInitialized: () => boolean;
Auth: {
authorize(options: { redirectUri: string }): void;
authorize(options: { redirectUri: string; scope: string }): void;
setAccessToken(
token: string,
): Promise<ShippingAddressResponse | ShippingAddressError>;
};
API: {
request: (settings: {
url: string;
data: { property_keys: string[] };
}) => Promise;
};
};

Expand Down
12 changes: 12 additions & 0 deletions src/types/type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export interface UserDataProps {
name: string;
region: string;
universal_type: string[];
favorite_list: number[];
}

export interface KakaoUserDataProps {
id: number;
nickname: string;
thumbnail_image_url: string;
}
4 changes: 2 additions & 2 deletions src/views/Login/components/LoginCallBack.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import fetchKakaoLogin from '../hooks/fetchKakaoLogin';
import usePostKakaoLogin from '../../../api/usePostKakaoLogin';

const LoginCallBack = () => {
fetchKakaoLogin();
usePostKakaoLogin();
return <></>;
};

Expand Down
15 changes: 11 additions & 4 deletions src/views/Login/components/UserType.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
import { css } from '@emotion/react';
import { useNavigate } from 'react-router-dom';

import postAddUser from '@/api/supabase/postAddUser';
import BottomButton from '@/components/BottomButton';
import TravelerType from '@/components/TravelerType';
import { COLORS, FONTS } from '@/styles/constants';
import { KakaoUserDataProps } from '@/types/type';

import { useSignUpContext } from './SignUpContext';

const UserType = () => {
interface UserTypeProps {
userData: KakaoUserDataProps;
}

const UserType = ({ userData }: UserTypeProps) => {
const navigate = useNavigate();

const { travelerType, setTravelerType } = useSignUpContext();
const { region, travelerType, setTravelerType } = useSignUpContext();

const moveNext = () => {
const submitSignUp = async () => {
await postAddUser({ userData, region, travelerType });
navigate(`/`);
};

Expand All @@ -32,7 +39,7 @@ const UserType = () => {

<BottomButton
text="확인"
clickedFn={moveNext}
clickedFn={submitSignUp}
disabled={!travelerType.length}
/>
</>
Expand Down
40 changes: 0 additions & 40 deletions src/views/Login/hooks/fetchKakaoLogin.ts

This file was deleted.

6 changes: 4 additions & 2 deletions src/views/Login/pages/SignUpPage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { css } from '@emotion/react';
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useLocation, useNavigate } from 'react-router-dom';

import { HeaderBackIcon } from '@/assets/icon';
import Header from '@/components/Header';
Expand All @@ -12,6 +12,8 @@ import UserType from '../components/UserType';
const SignUpPage = () => {
const [step, setStep] = useState('지역 설정');

const { state } = useLocation();

const navigate = useNavigate();

const moveBack = () => {
Expand All @@ -28,7 +30,7 @@ const SignUpPage = () => {
return <Region setStep={setStep} />;
}
if (step === '여행자 유형 설정') {
return <UserType />;
return <UserType userData={state} />;
}
};

Expand Down
23 changes: 23 additions & 0 deletions src/views/Login/types/loginType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export interface KakaoResProps {
id: number;
kakao_account: {
// 프로필 또는 닉네임 동의항목 필요
profile_nickname_needs_agreement: boolean;
// 프로필 또는 프로필 사진 동의항목 필요
profile_image_needs_agreement: boolean;
profile: {
// 프로필 또는 닉네임 동의항목 필요
nickname: string;
// 프로필 또는 프로필 사진 동의항목 필요
thumbnail_image_url: string;
profile_image_url: string;
is_default_image: boolean;
is_default_nickname: boolean;
};
/*
출생 연도 동의항목 추후 추가 예정
*/
// birthyear_needs_agreement: boolean;
// birthyear: string;
};
}
Loading

0 comments on commit dbdd134

Please sign in to comment.