Skip to content

Commit

Permalink
♻️ SessionContext 리팩토링 (#280)
Browse files Browse the repository at this point in the history
  • Loading branch information
yeolyi authored Nov 30, 2024
1 parent 9365705 commit a727e0a
Show file tree
Hide file tree
Showing 10 changed files with 102 additions and 56 deletions.
28 changes: 22 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

![](https://github.com/user-attachments/assets/39a28dbf-8ce8-4c3c-9222-abdddd22b934)

![Next JS](https://img.shields.io/badge/Next-black?style=for-the-badge&logo=next.js&logoColor=white) ![React](https://img.shields.io/badge/react-%2320232a.svg?style=for-the-badge&logo=react&logoColor=%2361DAFB) ![TypeScript](https://img.shields.io/badge/typescript-%23007ACC.svg?style=for-the-badge&logo=typescript&logoColor=white) ![TailwindCSS](https://img.shields.io/badge/tailwindcss-%2338B2AC.svg?style=for-the-badge&logo=tailwind-css&logoColor=white) ![Express.js](https://img.shields.io/badge/express.js-%23404d59.svg?style=for-the-badge&logo=express&logoColor=%2361DAFB)

안녕하세요👋 서울대학교 컴퓨터공학부 홈페이지의 프론트엔드 레포입니다.

- 2023.07: 🎉 CSEREAL 프로젝트 시작
Expand All @@ -12,18 +14,16 @@

### 준비

`nvm use``.nvmrc`에 명시된 노드 버전을 사용합니다. 해당 노드 버전이 설치되어있지 않다면 `nvm install`로 설치합니다.

이후 `npm install`로 패키지를 설치하고 husky를 초기화합니다.

```sh
git clone https://github.com/wafflestudio/csereal-web
cd csereal-web
nvm use
npm install
```

### Phase
### 빌드/실행

3개의 phase로 관리합니다.
csereal-web은 총 3개의 phase로 관리됩니다.

- prod
- https://cse.snu.ac.kr
Expand All @@ -36,6 +36,22 @@ npm install
- local
- 로컬 개발/테스트 전용

원하는 phase의 build/start npm 명령어를 실행합니다:

```sh
# local
npm run build:local
npm run start:local
# beta
npm run build:beta
npm run start:beta
# prod
npm run build:prod
npm run start:prod
```

⚠️ prod와 beta phase는 localhost에 서버가 있음을 전제로 빌드됩니다.

### 카카오 지도

[찾아오는 길](https://cse.snu.ac.kr/about/directions) 페이지에서 카카오 지도를 사용합니다. 카카오 지도가 올바르게 표시되려면 API Key를 추가해야합니다.
Expand Down
21 changes: 7 additions & 14 deletions actions/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,19 @@

import { cookies } from 'next/headers';

import { getRequest } from '@/apis';
import { getMockLogin } from '@/apis/v1/mock-login';
import { getIsStaff } from '@/apis/v1/user/is-staff';
import { COOKIE_SESSION_ID } from '@/constants/network';
import { UserState } from '@/contexts/SessionContext';

export const getMockAuth = async () => {
const resp = await fetch(`https://cse-dev-waffle.bacchus.io/api/v1/mock-login`, {
method: 'GET',
cache: 'no-store',
});

export const setAuthCookie = async () => {
const resp = await getMockLogin();
const cookie = resp.headers.getSetCookie()[0];
const value = cookie.split(/=|;/)[1];
cookies().set(COOKIE_SESSION_ID, value, { httpOnly: true, sameSite: 'strict' });
};

export const removeAuth = async () => {
export const removeAuthCookie = async () => {
cookies().delete(COOKIE_SESSION_ID);
};

Expand All @@ -26,14 +23,10 @@ export const getUserState = async (): Promise<UserState> => {
if (id === undefined) return 'logout';

try {
const resp = await getRequest<{ isStaff: boolean }>('/user/is-staff', undefined, {
cache: 'no-store',
jsessionID: true,
});

const resp = await getIsStaff();
return resp.isStaff ? 'staff' : 'non-staff';
} catch {
removeAuth();
removeAuthCookie();
return 'logout';
}
};
5 changes: 5 additions & 0 deletions apis/v1/mock-login.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const getMockLogin = () =>
fetch(`https://cse-dev-waffle.bacchus.io/api/v1/mock-login`, {
method: 'GET',
cache: 'no-store',
});
7 changes: 7 additions & 0 deletions apis/v1/user/is-staff.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { getRequest } from '@/apis';

export const getIsStaff = () =>
getRequest<{ isStaff: boolean }>('/v1/user/is-staff', undefined, {
cache: 'no-store',
jsessionID: true,
});
2 changes: 1 addition & 1 deletion app/.internal/InternalContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export default function InternalContent({ description }: { description: string }
) : (
<>
<LoginVisible staff>
<GrayButton title="편집" onClick={toggleEditMode} disabled={false} />
<GrayButton title="편집" onClick={toggleEditMode} />
</LoginVisible>
<HTMLViewer htmlContent={description} />
</>
Expand Down
20 changes: 13 additions & 7 deletions app/.internal/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ export const dynamic = 'force-dynamic';

import '@/styles/globals.css';

import { NextIntlClientProvider } from 'next-intl';
import { getMessages } from 'next-intl/server';
import { Toaster } from 'react-hot-toast';

import ModalContainer from '@/components/modal/ModalContainer';
Expand All @@ -15,16 +17,20 @@ export default async function RootLayout({
children: React.ReactNode;
params: { locale: string };
}) {
const messages = await getMessages();

return (
<html lang={params.locale} className="font-normal sm:min-w-[1000px]">
<body>
<SessionContextProvider>
<ModalContextProvider>
{children}
<ModalContainer />
<Toaster />
</ModalContextProvider>
</SessionContextProvider>
<NextIntlClientProvider messages={messages}>
<SessionContextProvider>
<ModalContextProvider>
{children}
<ModalContainer />
<Toaster />
</ModalContextProvider>
</SessionContextProvider>
</NextIntlClientProvider>
</body>
</html>
);
Expand Down
14 changes: 7 additions & 7 deletions app/[locale]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,13 @@ async function ContextProviders({ locale, children }: { locale: string; children
const messages = await getMessages();

return (
<SessionContextProvider>
<ModalContextProvider>
<NavbarContextProvider>
<NextIntlClientProvider messages={messages}>{children}</NextIntlClientProvider>
</NavbarContextProvider>
</ModalContextProvider>
</SessionContextProvider>
<NextIntlClientProvider messages={messages}>
<SessionContextProvider>
<ModalContextProvider>
<NavbarContextProvider>{children}</NavbarContextProvider>
</ModalContextProvider>
</SessionContextProvider>
</NextIntlClientProvider>
);
}

Expand Down
28 changes: 14 additions & 14 deletions contexts/SessionContext.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
'use client';

// SessionContext는 next-intl에 의존적일 필요 없으므로 next의 useRouter를 사용한다.
// eslint-disable-next-line no-restricted-imports
import { usePathname, useRouter } from 'next/navigation';
import {
createContext,
PropsWithChildren,
Expand All @@ -9,9 +12,9 @@ import {
useState,
} from 'react';

import { getMockAuth, getUserState, removeAuth } from '@/actions/session';
import { getUserState, removeAuthCookie, setAuthCookie } from '@/actions/session';
import { isDev } from '@/constants/env';
import { LOGIN_URL, LOGOUT_URL } from '@/constants/network';
import { useRouter } from '@/i18n/routing';

export type UserState = 'logout' | 'non-staff' | 'staff';

Expand All @@ -32,35 +35,32 @@ export const useSessionContext = () => useContext(SessionContext);
export default function SessionContextProvider({ children }: PropsWithChildren) {
const [state, setState] = useState<UserState>('logout');
const router = useRouter();
const pathname = usePathname();

const refresh = useCallback(async () => {
const resp = await getUserState();
setState(resp);
setState(await getUserState());
}, []);

useEffect(() => {
refresh();
}, [refresh]);
}, [pathname, refresh]);

const login = useCallback(async () => {
if (process.env.NODE_ENV === 'development') {
await getMockAuth();
if (isDev) {
await setAuthCookie();
await refresh();
} else {
router.push(LOGIN_URL);
}

await refresh();
}, [refresh, router]);

const logout = useCallback(async () => {
if (process.env.NODE_ENV === 'development') {
removeAuth();
router.push('/');
if (isDev) {
removeAuthCookie();
await refresh();
} else {
router.push(LOGOUT_URL);
}

await refresh();
}, [refresh, router]);

return (
Expand Down
28 changes: 23 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "PHASE=local node server.mjs",
"dev": "PHASE=local NODE_ENV=development node server.mjs",
"build:local": "PHASE=local next build",
"start:local": "PHASE=local NODE_ENV=production node server.mjs",
"build:beta": "PHASE=beta next build",
Expand Down Expand Up @@ -34,6 +34,7 @@
"postcss": "8.4.24",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.53.2",
"react-hot-toast": "^2.4.1",
"sharp": "^0.32.5",
"suneditor": "^2.46.3",
Expand All @@ -47,7 +48,7 @@
"@types/express": "^4.17.21",
"eslint": "^9.15.0",
"eslint-plugin-react": "^7.37.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-simple-import-sort": "^12.1.1",
"globals": "^15.12.0",
"husky": "^9.1.7",
Expand Down

0 comments on commit a727e0a

Please sign in to comment.