Skip to content

Commit 4e88453

Browse files
authored
Merge pull request #35 from codeit-sprint-part4-8-1/F81-72-Root-컨텍스트
feat: F81-72 Root Context 제작 및 헤더 수정
2 parents 4abbfc3 + 5dce130 commit 4e88453

File tree

6 files changed

+105
-25
lines changed

6 files changed

+105
-25
lines changed

src/components/@Shared/GNB.tsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
1-
import { useState } from 'react';
1+
import { useEffect, useState } from 'react';
22
import { Menu } from '@headlessui/react';
33
import Image from 'next/image';
44
import LogoButton from './Buttons/LogoButton';
5-
5+
import { useRoot } from '@/hook/useRoot';
66
const GNB = () => {
77
// 로그인 상태 관리
88
const [isLoggedIn, setIsLoggedIn] = useState(false);
9+
const { userData, setUserData } = useRoot();
910

11+
useEffect(()=>{
12+
if(userData) return setIsLoggedIn(true);
13+
if(!userData) return setIsLoggedIn(false);
14+
},[userData])
1015
return (
1116
<nav className="bg-white shadow-md">
1217
<div className="container mx-auto px-4 py-4 flex justify-between items-center">
@@ -18,8 +23,10 @@ const GNB = () => {
1823
{/* 알림 버튼 */}
1924
<button className="text-gray-700 hover:text-blue-500">
2025
<Image
21-
src="/Ico_notification.svg"
26+
src="/ico/ico_notification.svg"
2227
alt="종모양 아이콘(알림)"
28+
width={10}
29+
height={10}
2330
className="w-6 h-6"
2431
/>
2532
</button>
@@ -50,7 +57,11 @@ const GNB = () => {
5057
<Menu.Item>
5158
{({ active }) => (
5259
<button
53-
onClick={() => setIsLoggedIn(false)} // 로그아웃 처리
60+
onClick={() => {
61+
setUserData(undefined)
62+
localStorage.removeItem('accessToken');
63+
localStorage.removeItem('refreshToken');
64+
} } // 로그아웃 처리
5465
className={`block w-full text-left px-4 py-2 text-sm ${
5566
active ? 'bg-gray-100' : ''
5667
}`}

src/components/auth/AuthDtos.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@ export interface signUpFormData {
33
nickname: string;
44
password: string;
55
}
6+
7+
export interface UserData {
8+
id: number;
9+
email: string;
10+
nickname: string;
11+
profileImageUrl: string | null;
12+
createdAt: string;
13+
updatedAt: string;
14+
}
615
export interface LoginFormData {
716
email: string;
817
password: string;

src/components/auth/LoginForm.tsx

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
import { FC, useState } from 'react';
22
import { Button } from "@/components/@Shared/Buttons/Button";
33
import { useForm } from 'react-hook-form';
4-
import { axiosInstance } from '@/apis/instance/axiosInstance';
54
import AuthInput from './AuthInput';
65
import clsx from 'clsx';
7-
86
import createValidations from './Validations';
97
import { LoginFormData } from './AuthDtos';
108
import Modal from './Modal';
119
import useModalClose from './modalClose';
1210
import axios from 'axios';
11+
import { useRoot } from '@/hook/useRoot';
1312

1413

1514
const LoginForm: FC = () => {
1615
const { register, handleSubmit, watch, trigger, formState: { errors } } = useForm<LoginFormData>({ mode: 'onBlur' });
17-
const [isButtonValid, setIsButtonValid] = useState<boolean>(false);
16+
const [ isButtonValid, setIsButtonValid] = useState<boolean>(false);
1817
const [ modalOpen, setModalOpen ] = useState<boolean>(false);
1918
const [ modalMessage, setModalMessage ] = useState<string>('');
2019
const [ isLoginSuccess, setIsLoginSuccess ] = useState<boolean>(true);
20+
const { useLogin } = useRoot();
2121
const Validations = createValidations(watch('password'));
2222
const modalClose = useModalClose();
2323
const handleBlur = async (name: keyof LoginFormData) => {
@@ -28,14 +28,7 @@ const LoginForm: FC = () => {
2828

2929
const onSubmit = async (data: LoginFormData) => {
3030
try {
31-
const response = await axiosInstance.post('/auth/login', data);
32-
console.log(response.data);
33-
const {accessToken, refreshToken } = response.data;
34-
35-
console.log(`엑세스토큰, 리프레시토큰 로컬스토리지에 저장`);
36-
localStorage.setItem('accessToken', accessToken);
37-
localStorage.setItem('refreshToken', refreshToken);
38-
31+
await useLogin(data);
3932
setIsLoginSuccess(true);
4033
setModalMessage(`로그인 성공`);
4134
setModalOpen(true);
@@ -63,7 +56,6 @@ const LoginForm: FC = () => {
6356
<AuthInput label="이메일" register={register} name="email" validation={Validations.email} onBlur={() => handleBlur('email')} errors={errors} type='email' />
6457
<AuthInput label="비밀번호" register={register} name="password" validation={Validations.password} onBlur={() => handleBlur('password')} errors={errors} type="password"/>
6558
<Button variant="solid" label="로그인 하기" type="submit" disabled={!isButtonValid} className='h-[48px]' />
66-
6759
</form>
6860
);
6961
};

src/hook/useRoot.tsx

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { axiosInstance } from "@/apis/instance/axiosInstance";
2+
import { LoginFormData as LoginRequestData, UserData } from "@/components/auth/AuthDtos";
3+
import { createContext, Dispatch, ReactNode, SetStateAction, useContext, useState } from "react"
4+
5+
interface rootTypes {
6+
userData : UserData | undefined;
7+
refreshUser : () => void;
8+
useLogin : (data : LoginRequestData) => void;
9+
setUserData: Dispatch<SetStateAction<UserData | undefined>>;
10+
}
11+
const rootContext = createContext<rootTypes>({
12+
userData : undefined,
13+
refreshUser : () => {},
14+
useLogin : () => {},
15+
setUserData : ()=> {},
16+
});
17+
18+
export const RootProvider : React.FC<{children: ReactNode}>= ({children})=> {
19+
const [ userData, setUserData ] = useState<UserData | undefined>(undefined);
20+
21+
const useLogin = async (data : LoginRequestData) => {
22+
// 로그인 api 보내기.
23+
try{
24+
const rs = await axiosInstance.post('auth/login',data);
25+
const { user, refreshToken, accessToken } = rs.data;
26+
setUserData(user);
27+
// 데이터 받아서 user 에 데이터 넣기
28+
localStorage.setItem('accessToken', accessToken);
29+
localStorage.setItem('refreshToken', refreshToken);
30+
// 엑세스토큰, 리프레시토큰 로컬스토리지에 넣기
31+
}catch(error){
32+
return Promise.reject(error);
33+
// 에러 그대로 반환하기
34+
}
35+
}
36+
37+
const refreshUser = async ()=> {
38+
try{
39+
const rs = await axiosInstance.get('user/me');
40+
const data = rs.data;
41+
setUserData(data);
42+
} catch(error){
43+
console.log(error);
44+
}
45+
// axios get /user/me 로 내정보 fetch 후 const rs에 넣음
46+
// userData 에 덮어쓰기
47+
// 에러 있을 시 error console에 띄움
48+
}
49+
return(
50+
<rootContext.Provider value={{userData, setUserData, useLogin, refreshUser}}>
51+
{children}
52+
</rootContext.Provider>
53+
)
54+
}
55+
56+
57+
export const useRoot = ()=> {
58+
const context = useContext(rootContext);
59+
if(context === undefined) {
60+
throw new Error('RootContext 는 RootProvider 안에서만 사용할 수 있습니다.');
61+
}
62+
63+
return context;
64+
}
65+
66+

src/pages/_app.tsx

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
55
import GNB from '@/components/@Shared/GNB';
66
import Container from '@/components/@Shared/Container';
77
import Footer from '@/components/@Shared/Footer';
8+
import { RootProvider } from '@/hook/useRoot';
89

910
export default function App({ Component, pageProps }: AppProps) {
1011
const router = useRouter();
@@ -19,14 +20,16 @@ export default function App({ Component, pageProps }: AppProps) {
1920

2021
return (
2122
<div className="flex flex-col min-h-screen">
22-
<GNB />
23-
{/* 컨테이너 컴포넌트에 fullWidth prop을 전달 */}
24-
<Container fullWidth={isMainPage}>
25-
<QueryClientProvider client={queryClient}>
26-
<Component {...pageProps} />
27-
</QueryClientProvider>
28-
</Container>
29-
<Footer className="mt-auto" />
23+
<RootProvider>
24+
<GNB />
25+
{/* 컨테이너 컴포넌트에 fullWidth prop을 전달 */}
26+
<Container fullWidth={isMainPage}>
27+
<QueryClientProvider client={queryClient}>
28+
<Component {...pageProps} />
29+
</QueryClientProvider>
30+
</Container>
31+
<Footer className="mt-auto" />
32+
</RootProvider>
3033
</div>
3134
);
3235
}

src/pages/login/index.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import AuthContainer from "@/components/auth/AuthContainer";
2-
import AuthLogo from "@/components/auth/AuthLogo";
32
import LoginForm from "@/components/auth/LoginForm";
43

54
const Login = ()=> {

0 commit comments

Comments
 (0)