Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
| * 이재준 작성 - 인증되지 않은 사용자가 대시보드 편집 페이지에 접근할 때 로그인 페이지로 리다이렉트하기 위해 추가 | ||
| */ | ||
| export const getServerSideProps: GetServerSideProps = async (context) => { | ||
| const { req } = context; | ||
|
|
||
| const accessToken = req.cookies.access_token; | ||
|
|
||
| if (!accessToken) { | ||
| return { | ||
| redirect: { | ||
| destination: `/login`, | ||
| permanent: false, | ||
| }, | ||
| }; | ||
| } | ||
|
|
||
| return { props: {} }; | ||
| }; |
There was a problem hiding this comment.
이 부분이 여러번 반복되고 있는 것 같습니다! middleware.ts로 대체해도 좋을 것 같아요
// middleware.ts 예제
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const response = NextResponse.next();
// 요청에 따른 조건부 로직 처리
if (request.nextUrl.pathname.startsWith('/admin')) {
if (!request.cookies.get('auth')) {
return NextResponse.redirect(new URL('/login', request.url));
}
}
return response;
}| /** | ||
| * 서버 사이드에서 실행되는 함수 - 페이지 렌더링 전에 로그인 상태 확인 | ||
| */ | ||
| export const getServerSideProps: GetServerSideProps = async (context) => { |
There was a problem hiding this comment.
getServerSideProps()를 대부분 메인 컴포넌트 밑에 정의를 해주셨는데, 위에 정의할지 아래에 정의할지 이야기 해보는 것도 좋을 것 같아요! 저는 개인적으로 props가 전달되는 관계라서 위에서 아래로 읽는게 편한 것 같습니다.
| MyPage.getLayout = function getLayout(page: ReactNode) { | ||
| return page; | ||
| }; |
There was a problem hiding this comment.
위 return문 에서 DashboardLayout 를 그대로 감싸는 방식을 사용하셨는데,
return (
<DashboardLayout>
<div
....공통된 코드유지를 위해서 해당 패턴을 사용하는게 어떨까요.?>
| MyPage.getLayout = function getLayout(page: ReactNode) { | |
| return page; | |
| }; | |
| MyPage.getLayout = function getLayout(page: ReactNode) { | |
| return <DashboardLayout>{page}</DashboardLayout> | |
| }; |
| {/* 이재준 작성 - 프로필 드롭다운 메뉴 추가 (로그아웃, 내 정보, 내 대시보드) */} | ||
| {open && ( | ||
| <div | ||
| role='menu' | ||
| aria-label='사용자 메뉴' | ||
| className='shadow-1 absolute top-full right-6 mt-2 w-44 overflow-hidden rounded-md border border-gray-200 bg-white shadow' | ||
| > |
There was a problem hiding this comment.
헤더에 드롭다운 부분은 빼서 공통컴포넌트로 다시 만들게요!
| export default function handler( | ||
| req: NextApiRequest, | ||
| res: NextApiResponse | ||
| ): void { | ||
| if (req.method !== 'GET') { | ||
| res.status(405).json({ message: 'Method not allowed' }); | ||
|
|
||
| return; | ||
| } | ||
|
|
||
| // HttpOnly 쿠키에서 access_token 확인 | ||
| const accessToken = req.cookies.access_token; | ||
|
|
||
| if (!accessToken) { | ||
| res.status(401).json({ message: 'Unauthorized' }); | ||
|
|
||
| return; | ||
| } | ||
|
|
||
| // TODO: 실제로는 accessToken을 검증하고 사용자 정보를 가져와야 함 | ||
| // 현재는 임시로 성공 응답만 반환 |
There was a problem hiding this comment.
accessToken을 검증하는 로직이 Taskify 서버에 이미 있을텐데 Next api를 정의해서 쿠키를 발급하는 방식이 맞는건지 조금 의아합니다, 이렇게 발급한 쿠키로 다른 API 호출도 잘 되는 것일까요?
| try { | ||
| // 로그인 API 호출 | ||
| const loginParams: LoginParams = { | ||
| email, | ||
| password, | ||
| }; | ||
|
|
||
| const response = await login(loginParams); | ||
|
|
||
| // accessToken을 HttpOnly 쿠키로 설정 | ||
| try { | ||
| const sessionResponse = await fetch('/api/session', { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| body: JSON.stringify({ | ||
| accessToken: response.accessToken, | ||
| }), | ||
| }); | ||
|
|
||
| if (!sessionResponse.ok) { | ||
| throw new Error('Session creation failed'); | ||
| } | ||
|
|
||
| // 리다이렉트 경로 결정: 대시보드 경로로 향하던 경우에도 디폴트는 mydashboard | ||
| const nextParam = router.query.next as string | undefined; | ||
| const nextPath = | ||
| nextParam && !nextParam.startsWith('/dashboard') | ||
| ? nextParam | ||
| : '/mydashboard'; | ||
|
|
||
| router.push(nextPath); | ||
| } catch { | ||
| setModalMessage( | ||
| '로그인 처리 중 오류가 발생했습니다. 다시 시도해주세요.' | ||
| ); | ||
| setShowModal(true); | ||
| } | ||
| } catch (error: unknown) { | ||
| // 에러 메시지 처리 | ||
| const errorMessage = | ||
| error instanceof Error ? error.message : '알 수 없는 오류'; | ||
|
|
||
| if (errorMessage.includes('[400]')) { | ||
| setModalMessage('비밀번호가 일치하지 않습니다.'); | ||
| setShowModal(true); | ||
| } else if (errorMessage.includes('[404]')) { | ||
| setModalMessage('존재하지 않는 유저입니다.'); | ||
| setShowModal(true); | ||
| } else { | ||
| setModalMessage('로그인에 실패했습니다. 다시 시도해주세요.'); | ||
| setShowModal(true); | ||
| } | ||
| } finally { | ||
| setIsLoading(false); | ||
| } | ||
| }, | ||
| [email, password, router] | ||
| ); |
There was a problem hiding this comment.
로그인을 처리하는 로직이 굉장히 길고 try-catch도 이중으로 있어서 이후에 정리해봐도 좋을 것 같아요!
fetch하는 부분과 에러처리를 한 파일로 분리하고 에러 메시지를 throw하는 방식으로 구현하면 어떨까요?
API에러 처리에 대한 부분은 저도 구현을 해야해서, 나중에 같이 이야기해봐도 좋을것 같네요~
| const baseValid = baseValidation.isFormValid(values); | ||
|
|
||
| // skipConfirmPassword가 true이거나 비밀번호가 비어있으면 확인 비밀번호 검증을 건너뛰기 | ||
| const confirmValid = | ||
| skipConfirmPassword || !values.password || !values.confirmPassword | ||
| ? true | ||
| : validateConfirmPasswordField( | ||
| values.password, | ||
| values.confirmPassword | ||
| ); | ||
|
|
||
| return baseValid && confirmValid; |
There was a problem hiding this comment.
boolean으로 평가되는 값은 'is', 'has'를 접두사로 붙이는 것을 권장드립니다
더불어 복잡한 조건은 변수로 한번 감싸주면 읽기 쉬운 코드가 될 것같아요.
| const baseValid = baseValidation.isFormValid(values); | |
| // skipConfirmPassword가 true이거나 비밀번호가 비어있으면 확인 비밀번호 검증을 건너뛰기 | |
| const confirmValid = | |
| skipConfirmPassword || !values.password || !values.confirmPassword | |
| ? true | |
| : validateConfirmPasswordField( | |
| values.password, | |
| values.confirmPassword | |
| ); | |
| return baseValid && confirmValid; | |
| const isFormValid = baseValidation.isFormValid(values); | |
| // skipConfirmPassword가 true이거나 비밀번호가 비어있으면 확인 비밀번호 검증을 건너뛰기 | |
| const isPasswordValid = | |
| skipConfirmPassword || !values.password || !values.confirmPassword; | |
| const isConfirmValid = isPasswordValid ? true : validateConfirmPasswordField( | |
| values.password, | |
| values.confirmPassword | |
| ); | |
| return isFormValid && isConfirmValid; |
There was a problem hiding this comment.
(아마도 나중에 알아서 잘 분리하실 것 같지만👾) 이 파일도 fetch 하는 부분들, 긴 핸들러등 이후 리팩토링 해보시면 좋을 것 같아요~~
Squashed commit of the following: commit 931bfcc Author: jaejoonLee <dlfhdlwm12@naver.com> Date: Wed Sep 3 20:49:18 2025 +0900 fix: eslint, 컴포넌트 분해 commit 53786a0 Author: jaejoon <dlfhdlwm12@naver.com> Date: Wed Sep 3 11:49:21 2025 +0900 fix: 무한렌더링,회원가입페이지 오류 정정 commit 16960a8 Author: jaejoon <dlfhdlwm12@naver.com> Date: Wed Sep 3 04:13:50 2025 +0900 feat:계정관리 페이지 ui및 기능 일부 구현 commit 90a2c39 Merge: b40e377 b773c2c Author: jaejoon <dlfhdlwm12@naver.com> Date: Tue Sep 2 23:51:02 2025 +0900 merge: common-components와 merge commit b40e377 Author: jaejoon <dlfhdlwm12@naver.com> Date: Tue Sep 2 23:31:10 2025 +0900 feat: 미들웨어 쿠기 기능 구현 commit b773c2c Author: geha <ospac111@gmail.com> Date: Tue Sep 2 23:05:59 2025 +0900 fix: side-bar padding 변경(#12) commit f321d3e Author: geha <ospac111@gmail.com> Date: Tue Sep 2 23:00:07 2025 +0900 feat: dashbaord-layout 추가 + 모든 button active, hover 효과 추가 (#12) commit 5d83855 Author: geha <ospac111@gmail.com> Date: Tue Sep 2 20:05:48 2025 +0900 fix: webpack/svgr 삭제(svg 컴포넌트화 형식으로 변경), 버그 수정 (#12) commit a8d1cec Author: geha <ospac111@gmail.com> Date: Tue Sep 2 18:31:18 2025 +0900 feat: side-menu ui 완료, story 추가 (#12) commit 99e2aa6 Author: geha <ospac111@gmail.com> Date: Tue Sep 2 16:27:46 2025 +0900 feat: dashboard-header story 추가, font 및 color 수정 (#12) commit 82f0949 Author: geha <ospac111@gmail.com> Date: Tue Sep 2 16:02:02 2025 +0900 feat: dashboard-header ui 완료 + svgr 세팅 (#12) commit e79437f Author: geha <ospac111@gmail.com> Date: Tue Sep 2 13:38:52 2025 +0900 fix: eslint error 수정 및 eslint.config 업데이트 (#12) commit fe7d5df Author: jaejoon <dlfhdlwm12@naver.com> Date: Tue Sep 2 05:02:39 2025 +0900 feat: api 및 토큰 기능 구현 commit e70b025 Merge: ac55efe ac3492a Author: jaejoon <dlfhdlwm12@naver.com> Date: Tue Sep 2 03:24:33 2025 +0900 chore: develop 브랜치 최신 이력 반영 commit ac55efe Author: jaejoon <dlfhdlwm12@naver.com> Date: Tue Sep 2 02:42:50 2025 +0900 feat: auth페이지 모달창 구현 commit 0129508 Author: jaejoon <dlfhdlwm12@naver.com> Date: Tue Sep 2 02:23:26 2025 +0900 chore: visibility변경 및 auth페이지 폴더 및 파일 정리 commit 3fc8bf1 Author: jaejoon <dlfhdlwm12@naver.com> Date: Tue Sep 2 01:58:00 2025 +0900 feat: 로그인페이지 및 회원가입페이지 반응형 구현 commit 54d6b2a Author: jaejoon <dlfhdlwm12@naver.com> Date: Tue Sep 2 01:08:12 2025 +0900 feat:랜딩페이지 반응형 구현 commit 6dccaf3 Author: jaejoon <dlfhdlwm12@naver.com> Date: Mon Sep 1 18:54:22 2025 +0900 feat:랜딩페이지 ui 구현 commit 6374029 Author: geha <ospac111@gmail.com> Date: Mon Sep 1 17:12:41 2025 +0900 feat(공통): chip-profile, tag, state 완료 (#12) commit 4e0e93d Author: geha <ospac111@gmail.com> Date: Mon Sep 1 15:55:06 2025 +0900 feat(공통): chip-state, chip-tag 완료 (#12) commit 63d6f83 Author: geha <ospac111@gmail.com> Date: Mon Sep 1 14:56:43 2025 +0900 docs: storybook 설치 설정(#12) commit b06fdc6 Author: geha <ospac111@gmail.com> Date: Mon Sep 1 14:55:24 2025 +0900 feat: button 완료(#12) commit 26d0d1f Author: geha <ospac111@gmail.com> Date: Sun Aug 31 14:29:22 2025 +0900 feat: twMerge, clsx를 쓰기위한 cn util 함수 추가 (#12) commit d8cd312 Author: geha <ospac111@gmail.com> Date: Sun Aug 31 14:20:44 2025 +0900 feat: storybook 설치 및 설정 (#12)
#️⃣ Related Issue
#25
📝 Problem
제가 맡은 파트 구현.
(헤더 드롭아웃 기본 구현)
✅ Solving
📌 프로젝트 구조 및 인증 흐름 정리
1. middleware
2. public/auth
3. components
4. hooks
5. lib
6. pages
7. styles
🔑 최종 인증 흐름
📚 Attachment
📋내 파트 외 추가된 코드 정리
1. src/pages/mydashboard/index.tsx
✅ 추가된 코드:
2. src/pages/dashboard/[dashboardId].tsx
✅ 추가된 코드:
3. src/pages/dashboard/[dashboardId]/edit.tsx
✅ 추가된 코드:
4. src/components/ui/dashboard-header/index.tsx
✅ 추가된 코드:
✅ 추가된 코드:
Pn Rule
Note