[REFACTOR] 온보딩 페이지 리팩토링#35
Conversation
📝 WalkthroughWalkthrough라우트 기반 단계별 signup 페이지를 제거하고, Changes회원가입 퍼널 리팩토링
역할 기반 접근 제어 미들웨어 및 Header 인증 상태
Sequence Diagram(s)sequenceDiagram
participant 사용자
participant SignupFunnel
participant StepComponents
participant proxy
사용자->>SignupFunnel: /signup 진입
SignupFunnel->>StepComponents: UserTypeStep 렌더링
사용자->>StepComponents: 역할 선택 후 다음 클릭
StepComponents->>SignupFunnel: onNext(selectedRole)
SignupFunnel->>StepComponents: TermsProfileStep 렌더링 (progressIcon, terms, onPrev/onNext)
사용자->>StepComponents: 약관 동의 후 다음 클릭
StepComponents->>SignupFunnel: onNext()
SignupFunnel->>StepComponents: AccountStep 렌더링
사용자->>StepComponents: 계정 입력 후 다음 클릭
StepComponents->>SignupFunnel: onNext()
Note over SignupFunnel: designer인 경우 DesignerAdditionalStep 렌더링
SignupFunnel->>사용자: /login 라우팅 (완료)
사용자->>proxy: /designer/* 또는 /instructor/* 접근
proxy->>proxy: accessToken 쿠키 확인
proxy->>proxy: parseJwtPayload → getUserRole
proxy->>사용자: 역할 불일치 시 /login 또는 ROLE_HOME_PATH 리다이렉트
proxy->>사용자: 역할 일치 시 NextResponse.next()
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
src/proxy.ts (1)
4-4: ⚡ Quick win
UserRole타입과normalizeRole함수가 Header.tsx와 중복됩니다.동일한 코드가
src/shared/ui/Header.tsx에도 존재합니다. 공유 위치(예:src/shared/model/auth.ts)로 추출하여 일관성을 유지하고 향후 변경 시 동기화 문제를 방지하세요.Also applies to: 14-22
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/proxy.ts` at line 4, The UserRole type definition and normalizeRole function are duplicated between src/proxy.ts (lines 4-22) and src/shared/ui/Header.tsx. Extract both the UserRole type and normalizeRole function to a new shared module at src/shared/model/auth.ts, then update the import statements in both src/proxy.ts and Header.tsx to import these shared definitions instead of maintaining duplicate definitions. This ensures consistency across the codebase and prevents synchronization issues when these definitions need to be updated in the future.src/shared/ui/Header.tsx (1)
9-14: ⚡ Quick win
UserRole타입과normalizeRole함수가 proxy.ts와 중복됩니다.
src/proxy.ts에 동일한 코드가 있습니다. 공유 모듈로 추출하여 중복을 제거하세요.// src/shared/model/auth.ts export type UserRole = "designer" | "instructor"; export const normalizeRole = (role?: string): UserRole | null => { const normalizedRole = role?.toLowerCase(); if (normalizedRole === "designer" || normalizedRole === "instructor") { return normalizedRole; } return null; };Also applies to: 34-42
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/shared/ui/Header.tsx` around lines 9 - 14, The UserRole type and normalizeRole function are duplicated across Header.tsx and proxy.ts, which violates the DRY principle. Create a new shared module file to centralize these definitions, then move the UserRole type definition and the normalizeRole function implementation to this shared module and export both from there. Finally, update the import statements in both Header.tsx and proxy.ts to import UserRole and normalizeRole from the new shared module instead of defining them locally.src/app/signup/page.tsx (1)
3-7: ⚡ Quick win서버 페이지 컴포넌트 명을 소문자
page로 맞춰주세요.현재 선언은 동작에는 문제없지만, 이 저장소의 페이지 네이밍 규칙과 다릅니다.
🔧 제안 수정
-const Page = () => { +const page = () => { return <SignupFunnel />; }; -export default Page; +export default page;As per coding guidelines,
"use client" 지시어가 없는 페이지: 소문자 page로 선언해야 합니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/app/signup/page.tsx` around lines 3 - 7, The component declaration uses uppercase `Page` but according to the repository's naming conventions, server page components without a "use client" directive should be declared with lowercase `page`. Rename the const declaration from `Page` to `page` in the Page component definition and update the export statement from `export default Page` to `export default page` to align with the repository's coding guidelines.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/proxy.ts`:
- Around line 24-38: The parseJwtPayload function decodes the JWT payload
without verifying the JWT signature, creating a security vulnerability where
malicious users can forge JWTs with arbitrary role values to bypass access
control. Implement JWT signature verification in the parseJwtPayload function
using a library like jose to verify the token signature against your server's
secret or public key before parsing and returning the payload. This ensures that
only valid, server-signed tokens are trusted for role-based access control
decisions.
- Around line 63-92: Rename the file from proxy.ts to middleware.ts and change
the exported function name from proxy to middleware to comply with Next.js
middleware standards. Next.js only recognizes middleware files named
middleware.ts with a function named middleware, so the current naming convention
causes the middleware to be ignored by the routing system. Keep the config
object with the matcher array as-is when moving the code to the new
middleware.ts file.
---
Nitpick comments:
In `@src/app/signup/page.tsx`:
- Around line 3-7: The component declaration uses uppercase `Page` but according
to the repository's naming conventions, server page components without a "use
client" directive should be declared with lowercase `page`. Rename the const
declaration from `Page` to `page` in the Page component definition and update
the export statement from `export default Page` to `export default page` to
align with the repository's coding guidelines.
In `@src/proxy.ts`:
- Line 4: The UserRole type definition and normalizeRole function are duplicated
between src/proxy.ts (lines 4-22) and src/shared/ui/Header.tsx. Extract both the
UserRole type and normalizeRole function to a new shared module at
src/shared/model/auth.ts, then update the import statements in both src/proxy.ts
and Header.tsx to import these shared definitions instead of maintaining
duplicate definitions. This ensures consistency across the codebase and prevents
synchronization issues when these definitions need to be updated in the future.
In `@src/shared/ui/Header.tsx`:
- Around line 9-14: The UserRole type and normalizeRole function are duplicated
across Header.tsx and proxy.ts, which violates the DRY principle. Create a new
shared module file to centralize these definitions, then move the UserRole type
definition and the normalizeRole function implementation to this shared module
and export both from there. Finally, update the import statements in both
Header.tsx and proxy.ts to import UserRole and normalizeRole from the new shared
module instead of defining them locally.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: c7629e85-c839-4211-88ba-dea24d234c3a
⛔ Files ignored due to path filters (5)
src/shared/assets/icons/icon_step_one_designer.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_step_one_instructor.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_step_three_designer.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_step_two_designer.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_step_two_instructor.svgis excluded by!**/*.svg
📒 Files selected for processing (16)
src/app/signup/designer/step2/page.tsxsrc/app/signup/instructor/step1/page.tsxsrc/app/signup/page.tsxsrc/features/signup/config/signupFunnel.tssrc/features/signup/index.tssrc/features/signup/model/signup.tssrc/features/signup/ui/AccountStep.tsxsrc/features/signup/ui/DesignerAdditionalStep.tsxsrc/features/signup/ui/SignupProgressIcon.tsxsrc/features/signup/ui/TermsProfileStep.tsxsrc/features/signup/ui/UserTypeStep.tsxsrc/proxy.tssrc/shared/assets/icons/index.tssrc/shared/ui/Header.tsxsrc/widgets/signup/index.tssrc/widgets/signup/ui/SignupFunnel.tsx
💤 Files with no reviewable changes (3)
- src/shared/assets/icons/index.ts
- src/app/signup/designer/step2/page.tsx
- src/app/signup/instructor/step1/page.tsx
| const parseJwtPayload = (token: string): Record<string, unknown> | null => { | ||
| const [, payload] = token.split("."); | ||
|
|
||
| if (payload == null) return null; | ||
|
|
||
| try { | ||
| const base64 = payload.replace(/-/g, "+").replace(/_/g, "/"); | ||
| const paddedBase64 = base64.padEnd(base64.length + ((4 - (base64.length % 4)) % 4), "="); | ||
| const decodedPayload = atob(paddedBase64); | ||
|
|
||
| return JSON.parse(decodedPayload) as Record<string, unknown>; | ||
| } catch { | ||
| return null; | ||
| } | ||
| }; |
There was a problem hiding this comment.
JWT 서명 검증 없이 페이로드를 파싱하고 있습니다.
parseJwtPayload는 JWT 서명을 검증하지 않고 페이로드만 디코딩합니다. 악의적인 사용자가 원하는 role 값을 가진 JWT를 위조하여 쿠키에 설정하면 접근 제어를 우회할 수 있습니다.
미들웨어에서 역할 기반 라우트 보호를 수행하려면 서버 측에서 JWT 서명 검증이 필요합니다. API 통합 시 백엔드에서 토큰 검증을 수행하거나, 미들웨어에서 jose 같은 라이브러리로 서명을 검증하는 것을 권장합니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/proxy.ts` around lines 24 - 38, The parseJwtPayload function decodes the
JWT payload without verifying the JWT signature, creating a security
vulnerability where malicious users can forge JWTs with arbitrary role values to
bypass access control. Implement JWT signature verification in the
parseJwtPayload function using a library like jose to verify the token signature
against your server's secret or public key before parsing and returning the
payload. This ensures that only valid, server-signed tokens are trusted for
role-based access control decisions.
| export function proxy(request: NextRequest) { | ||
| const { pathname } = request.nextUrl; | ||
| const requiredRole = getRequiredRole(pathname); | ||
|
|
||
| if (requiredRole == null) { | ||
| return NextResponse.next(); | ||
| } | ||
|
|
||
| const accessToken = request.cookies.get(ACCESS_TOKEN_COOKIE_NAME)?.value; | ||
|
|
||
| if (accessToken == null) { | ||
| return redirect("/login", request); | ||
| } | ||
|
|
||
| const userRole = getUserRole(request, accessToken); | ||
|
|
||
| if (userRole == null) { | ||
| return redirect("/login", request); | ||
| } | ||
|
|
||
| if (userRole !== requiredRole) { | ||
| return redirect(ROLE_HOME_PATH[userRole], request); | ||
| } | ||
|
|
||
| return NextResponse.next(); | ||
| } | ||
|
|
||
| export const config = { | ||
| matcher: ["/designer/:path*", "/instructor/:path*"], | ||
| }; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if there's a middleware.ts file that imports proxy
fd -e ts -e js "middleware" --max-depth 3
# If middleware.ts exists, check its content
if [ -f "src/middleware.ts" ]; then
cat src/middleware.ts
elif [ -f "middleware.ts" ]; then
cat middleware.ts
fiRepository: Ditda-Official/Ditda-Frontend
Length of output: 55
🏁 Script executed:
# Search for imports of proxy function
rg "import.*proxy|from.*proxy" --type ts --type js
# Check for any next.config files
fd -e js -e ts "next.config"
# Look for any middleware setup
rg "middleware" --type ts --type js -i
# Check the project structure
git ls-files | head -20Repository: Ditda-Official/Ditda-Frontend
Length of output: 487
🏁 Script executed:
# More specific search for proxy imports
rg "from.*proxy|import.*proxy" --type ts --type js -i
# Search for any middleware references
rg "middleware" --type ts --type js
# Check the src directory structure
find src -type f -name "*.ts" -o -name "*.js" | head -20
# Check next.config.ts
cat next.config.tsRepository: Ditda-Official/Ditda-Frontend
Length of output: 1190
🏁 Script executed:
# Look for all TypeScript files in src root
ls -la src/*.ts
# Check if proxy is exported as middleware or default export
cat src/proxy.ts
# Look for any references to src/proxy in entire codebase
rg "proxy" --type ts --type jsRepository: Ditda-Official/Ditda-Frontend
Length of output: 2717
src/proxy.ts 파일명과 함수명을 Next.js 미들웨어 표준에 맞춰 변경해야 합니다.
Next.js는 미들웨어 파일을 반드시 middleware.ts로 명명하고, 내보내는 함수를 middleware로 명명해야만 인식합니다. 현재 src/proxy.ts에서 proxy 함수를 내보내고 있어 Next.js 라우팅 시스템에서 완전히 무시되며 미들웨어가 실행되지 않습니다.
- 파일명:
src/proxy.ts→src/middleware.ts로 변경 - 함수명:
export function proxy→export function middleware로 변경 config객체와matcher는middleware.ts파일에 유지
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/proxy.ts` around lines 63 - 92, Rename the file from proxy.ts to
middleware.ts and change the exported function name from proxy to middleware to
comply with Next.js middleware standards. Next.js only recognizes middleware
files named middleware.ts with a function named middleware, so the current
naming convention causes the middleware to be ignored by the routing system.
Keep the config object with the matcher array as-is when moving the code to the
new middleware.ts file.
📢 PR 유형
어떤 변경 사항이 있었나요?
📌 관련 이슈번호
✅ Key Changes
회원가입 파일 구조 수정
유저 유형에 따른 URL 접근 제한 및 헤더 적용
src/proxy.ts추가 및 유저 유형 기반 페이지 접근 제한 구조 구성/designer,/instructor하위 페이지에 직접 접근 시,/login으로 redirectdesigner사용자의/instructor접근과instructor사용자의/designer접근 제한accessToken과 유효한userRole이 모두 있을 때만 “내 계정” 표시/instructor/my, 디자이너 - 임시로/designer연결단계별 인디케이터 이미지 제거 및 컴포넌트화
SignupProgressIcon컴포넌트로 전환features/signup/ui에 배치currentStep/totalSteps기반으로 디자이너 3단계, 강사 2단계 플로우를 재사용 가능하게 구성했습니다.📸 스크린샷 or 실행영상
2026-06-19.02.12.06.mov
🎸 기타 사항 or 추가 코멘트
Summary by CodeRabbit
Release Notes
New Features
Refactor