diff --git a/api/auth.ts b/api/auth.ts index 8b54065f9..b0701907f 100644 --- a/api/auth.ts +++ b/api/auth.ts @@ -49,3 +49,21 @@ export const postSignUp = async ({ email, password }: { email: string; password: return token; }; + +export const getUser = async () => { + const token = window.localStorage.getItem('accessToken'); + + const response = await fetch(`${BASE_URL}/users`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + const body = await response.json(); + + if (!response?.ok) { + throw new Error(body.message); + } + + return {...body[0], imageSource: body[0]['image_source']}; +}; diff --git a/components/layout/header/Header.tsx b/components/layout/header/Header.tsx index 4980e984c..21383de3f 100644 --- a/components/layout/header/Header.tsx +++ b/components/layout/header/Header.tsx @@ -1,30 +1,15 @@ import Link from 'next/link'; import styles from './Header.module.css'; -import { useEffect, useState } from 'react'; +import { useContext } from 'react'; import { useRouter } from 'next/router'; -import type { User } from '@/types/types'; -import { getUser } from '@/api/api'; import Image from 'next/image'; +import { authContext } from '@/context/authProvider'; function Header() { - const [user, setUser] = useState(null); + const { user } = useContext(authContext); const { pathname } = useRouter(); const headerPosition = pathname === '/folder' ? styles.static : ''; - useEffect(() => { - const fetchUser = async () => { - try { - const user = await getUser(); - setUser(user); - } catch (err) { - const error = err as Error; - console.error(error.message); - } - }; - - fetchUser(); - }, []); - return (
diff --git a/context/authProvider.tsx b/context/authProvider.tsx new file mode 100644 index 000000000..58b2a5492 --- /dev/null +++ b/context/authProvider.tsx @@ -0,0 +1,61 @@ +import { getUser, postSignIn, postSignUp } from '@/api/auth'; +import { PropsWithChildren, createContext, useEffect, useState } from 'react'; + +interface User { + id: number; + name: string; + imageSource: string; + email: string; +} + +interface AuthContext { + user?: User; + signIn: (data: any) => void; + signUp: (data: any) => void; + signOut: () => void; +} + +export const authContext = createContext({ + user: undefined, + signIn: () => {}, + signUp: () => {}, + signOut: () => {}, +}); + +const AuthProvider = ({ children }: PropsWithChildren) => { + const [user, setUser] = useState(); + + const signIn = async (data: any) => { + const { accessToken, refreshToken } = await postSignIn(data); + + window.localStorage.setItem('accessToken', accessToken); + window.localStorage.setItem('refreshToken', refreshToken); + }; + const signUp = async (data: any) => { + const { accessToken, refreshToken } = await postSignUp(data); + + window.localStorage.setItem('accessToken', accessToken); + window.localStorage.setItem('refreshToken', refreshToken); + }; + + const signOut = () => { + // TODO: 로그아웃 기능 구현 + }; + + useEffect(() => { + const fetchUser = async () => { + try { + const user = await getUser(); + setUser(user); + } catch (error) { + console.error((error as Error).message); + } + }; + + fetchUser(); + }, []); + + return {children}; +}; + +export default AuthProvider; diff --git a/hooks/useAuth.ts b/hooks/useAuth.ts new file mode 100644 index 000000000..ed499bd2a --- /dev/null +++ b/hooks/useAuth.ts @@ -0,0 +1,11 @@ +import { authContext } from '@/context/authProvider'; +import { useContext } from 'react'; + +export const useAuth = () => { + const context = useContext(authContext); + if (!context) { + throw new Error('반드시 AuthProvider 안에서 사용해야 합니다.'); + } + + return context; +}; diff --git a/next.config.js b/next.config.js index 542f8ca62..a622e53db 100644 --- a/next.config.js +++ b/next.config.js @@ -12,6 +12,12 @@ const nextConfig = { port: '', pathname: '/badges/**', }, + { + protocol: 'https', + hostname: 'avatars.githubusercontent.com', + port: '', + pathname: '/u/**', + }, ], }, }; diff --git a/pages/_app.tsx b/pages/_app.tsx index 7e4c56d3d..c9aae58da 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -2,6 +2,7 @@ import '@/styles/globals.css'; import { Noto_Sans_KR } from 'next/font/google'; import type { AppProps } from 'next/app'; import Layout from '@/components/layout/Layout'; +import AuthProvider from '@/context/authProvider'; const notoSansKR = Noto_Sans_KR({ subsets: ['latin'], @@ -11,9 +12,11 @@ const notoSansKR = Noto_Sans_KR({ export default function App({ Component, pageProps }: AppProps) { return (
- - - + + + + +
); } diff --git a/pages/signin.tsx b/pages/signin.tsx index 98d1f16b9..9ff353797 100644 --- a/pages/signin.tsx +++ b/pages/signin.tsx @@ -1,6 +1,7 @@ import { postSignIn } from '@/api/auth'; import InputGroup from '@/components/pages/sign/InputGroup'; import { ERROR_MESSAGE, INPUT_INFO } from '@/constants/sign'; +import { useAuth } from '@/hooks/useAuth'; import styles from '@/styles/sign.module.css'; import Image from 'next/image'; import Link from 'next/link'; @@ -10,14 +11,11 @@ import { FormProvider, useForm } from 'react-hook-form'; export default function SignIn() { const methods = useForm(); const router = useRouter(); + const { signIn } = useAuth(); const onSubmit = async (data: any) => { try { - const { accessToken, refreshToken } = await postSignIn(data); - - window.localStorage.setItem('accessToken', accessToken); - window.localStorage.setItem('refreshToken', refreshToken); - + signIn(data); router.push('/folder'); } catch { methods.setError(INPUT_INFO.email.id, { diff --git a/pages/signup.tsx b/pages/signup.tsx index 157f91b96..5c7002cfe 100644 --- a/pages/signup.tsx +++ b/pages/signup.tsx @@ -1,6 +1,7 @@ import { postCheckEmail, postSignUp } from '@/api/auth'; import InputGroup from '@/components/pages/sign/InputGroup'; import { INPUT_INFO } from '@/constants/sign'; +import { useAuth } from '@/hooks/useAuth'; import styles from '@/styles/sign.module.css'; import Image from 'next/image'; import Link from 'next/link'; @@ -9,6 +10,7 @@ import { useState } from 'react'; import { FormProvider, useForm } from 'react-hook-form'; export default function SignIn() { + const { signUp } = useAuth(); const methods = useForm(); const [emailCheckFailed, setEmailCheckFailed] = useState(false); const router = useRouter(); @@ -33,10 +35,7 @@ export default function SignIn() { } try { - const { accessToken, refreshToken } = await postSignUp({ email, password }); - - window.localStorage.setItem('accessToken', accessToken); - window.localStorage.setItem('refreshToken', refreshToken); + signUp({ email, password }); router.push('/folder'); } catch (error) {