Skip to content

멘토링 등록 저장 흐름, 공개 노출 분기, 핵심키워드 정책 안정화#439

Merged
Hyeonjun0527 merged 1 commit into
developfrom
refactor/mentoring-registration-sqaush
Mar 23, 2026
Merged

멘토링 등록 저장 흐름, 공개 노출 분기, 핵심키워드 정책 안정화#439
Hyeonjun0527 merged 1 commit into
developfrom
refactor/mentoring-registration-sqaush

Conversation

@Hyeonjun0527

@Hyeonjun0527 Hyeonjun0527 commented Mar 23, 2026

Copy link
Copy Markdown
Member

배경

멘토링 등록 화면, 저장 후 안내/이동, 공개 상세/목록 노출 기준에서 상태 불일치와 UX 회귀가 누적되어 있었습니다.
이번 PR은 멘토링 등록을 백엔드 계약에 맞게 다시 정렬하고, 저장 후 공개 노출 분기와 화면 표시 기준을 일관되게 맞추는 데 초점을 맞췄습니다.

주요 변경 사항

1. 핵심키워드 저장 계약과 등록 옵션 역할 정리

  • 등록 화면에서 사용하는 selectable 핵심키워드와 공개 화면에서 소비하는 실제 태그를 분리했습니다.
  • PREDEFINED / CUSTOM 저장 계약을 프론트 전반에 맞췄고, 잘못된 핵심키워드 응답과 옵션 drift를 더 안전하게 처리하도록 정리했습니다.
  • 커스텀 핵심키워드 길이 제한을 13자로 낮추고 입력 컷오프와 폼 검증 메시지가 같은 source of truth를 보도록 맞췄습니다.

2. 저장 흐름과 공개 노출 분기 안정화

  • 저장 후에는 항상 안내 모달을 거치도록 통일했습니다.
  • 목록 노출 여부에 따라 저장 후 멘토 상세 또는 멘토링 목록으로 이동하도록 분기했습니다.
  • 저장 중 중복 피드백, 목록 비노출 저장 후 공개 상세 404 이동, 저장 직후 잘못된 후속 분기 같은 회귀를 정리했습니다.

3. 멘토 상세/목록/프로필 동기화 개선

  • 멘토 소개가 미완성이어도 기본정보와 주요 이력은 공개 상세에서 유지하고, 소개/상담 전 준비사항/상담 방식만 준비 상태로 처리합니다.
  • 프로필 수정 후 멘토링 목록/상세에 프로필 이미지가 정상 반영되도록 URL 정규화와 캐시 갱신을 보강했습니다.
  • 멘토 상세 및 목록 렌더에서 발생하던 중복 key 경고와 접근성 경고를 정리했습니다.

4. 등록 폼과 에디터 UX 보강

  • 가격/시간 스텝을 스케줄 설정 앞에 배치해 상담 방식에 따라 스케줄 필요 여부를 먼저 판단할 수 있게 했습니다.
  • 저장 가능 조건과 실제 submit 경계가 같은 기준을 보도록 정리했습니다.
  • 멘토 소개 에디터에서 코드블록 입력, 언어 선택 위치, 모달/미리보기 세부 UX를 다듬었습니다.

검증

  • yarn lint --fix
  • yarn prettier:fix
  • yarn typecheck
  • 로컬 Playwright 검증
    • 프로필 이미지 수정 후 멘토링 목록/상세 반영 확인
    • 목록 노출 true/false 저장 후 이동 경로 확인
    • 저장 안내 모달 단일 노출 및 후속 라우팅 확인
    • 이상 입력 차단과 핵심키워드 저장 동작 확인

리뷰 포인트

  • selectable 핵심키워드와 공개 태그를 분리한 현재 경계가 제품 의도와 맞는지
  • listVisible=false 및 상세 소개 미완성 상태에서의 공개 상세/목록 노출 기준이 맞는지
  • 저장 후 항상 안내 모달을 거치는 현재 흐름과 후속 이동 분기가 운영 의도와 맞는지

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 멘토 경력 항목(careerEntries) 등록·편집 및 경력 패널 추가
    • 등록 폼 다단계(step) 흐름, 초안(스케줄 초안) 저장 및 미리보기 개선
    • 핵심 키워드(선택형/맞춤) 처리 및 서버 연동 로직 개선
  • UI/UX 개선

    • 멘토 상세/목록 레이아웃·메타 표시 개선, 경력·리뷰 표시 개선
    • 스케줄 편집 가독성·입력 제약·미리보기 품질 향상
    • 깊이 상담 옵션에 90분 추가
  • 기능 제어

    • 쪽지상담 경로/CTA 비활성화(노출되지 않음)
  • 접근성·소소한 개선

    • 모달 타이틀 접근성 개선 및 드롭다운 aria-label 지원

@vercel

vercel Bot commented Mar 23, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
study-platform-client-dev Error Error Mar 23, 2026 5:29pm

@coderabbitai

coderabbitai Bot commented Mar 23, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

멘토링 기능 전반을 대규모로 개편: 단계형 멘토 등록 흐름 도입, 경력 항목 및 스케줄 텍스트 드래프트 추가, 핵심 키워드 정규화·계약 도입, 공개 준비도 로직과 관련 UI/라우팅·검증/저장 흐름을 대거 변경·추가함.

Changes

Cohort / File(s) Summary
API 오류·로깅
src/api/client/api-error.ts, src/utils/error-handler.ts
ApiError에 선택적 detail?: unknown 추가 및 에러 추출 함수(extractApiErrorDetail)와 Sentry 전송 시 detail 포함 처리.
멘토 API DTO·계약·매퍼
src/features/mentoring/api/mentor-api.types.ts, src/features/mentoring/api/mentor-core-keyword-contract.ts, src/features/mentoring/api/mentor-api.ts, src/features/mentoring/api/mentor-profile.mapper.ts, src/features/mentoring/api/mentor-settings.mapper.ts
멘토 DTO(경력항목, 저장된 핵심키워드 등) 및 요청/응답 타입 확장, 핵심키워드 계약·정규화 모듈 추가, 빌드/매핑 로직(이미지 URL/마크다운/경력/키워드)과 upsert 시그니처 변경.
도메인 타입·스키마
src/types/mentoring/*, src/types/schemas/mentor-registration-schema.ts, src/types/schemas/mentoring-admin-schema.ts, src/types/mentoring/registration-options.ts
경력입력·스케줄 드래프트 타입 추가, settlement 드래프트 제거, 스키마 제약 상수화 및 core keyword → selectableCoreKeywords 등 타입/필드명 변경.
모델·유틸리티(스케줄·경력·키워드·준비도)
src/features/mentoring/model/mentor-settings.ts, src/features/mentoring/model/mentor-public-readiness.ts, src/features/mentoring/model/*registration-*.ts, src/features/mentoring/model/use-floating-panel-scroll-nudge.ts
스케줄 텍스트 파싱/드래프트 처리, 경력 항목 생성·정규화·라벨링, 공개준비도 계산 모듈 추가, 키워드 정규화/lookup, 플로팅 패널 스크롤 너지 훅 추가.
등록 컨트롤러·프리뷰·패널
src/features/mentoring/model/use-mentor-registration-controller.ts, src/features/mentoring/model/use-mentor-registration-preview-model.ts, src/features/mentoring/model/use-mentor-registration-preview-panel.ts
단계형 등록 컨트롤러·세션화된 버전ed 드래프트 저장/복원, preview/sizing/overflow 및 persistedPredefinedCoreKeywords 연동, 저장/에러/내비게이션 로직 대규모 변경.
UI: 등록 폼·에디터·에디터 관련 컴포넌트
src/features/mentoring/ui/registration/*, src/features/mentoring/ui/registration/mentor-markdown-editor.tsx, src/features/mentoring/ui/registration/mentor-markdown-content.tsx, src/features/mentoring/ui/registration/mentor-career-entries-editor.tsx
멘토 등록을 단계별 UI로 전환, 마크다운 에디터 개선(코드블록/이미지/정규화), 경력 편집기·인터뷰 질문 버퍼링·스케줄 드래프트 연동 등 재구성.
UI: 상세·사이드바·카드·디렉토리
src/features/mentoring/ui/detail/*, src/features/mentoring/ui/common/mentor-profile-meta-list.tsx, src/features/mentoring/ui/mentor-directory/mentor-card.tsx
공개준비도 기반 잠금/신청 가드 추가, 경력 이력 패널·프로필 메타 리스트 컴포넌트 추가, 카드/디렉토리 표시 로직(배지·카운트·가격 표시) 변경.
UI: 신청·플로우·관리자 관련
src/features/mentoring/ui/apply/*, src/features/mentoring/ui/common/mentoring-guide-modal.tsx, src/app/(admin)/admin/mentoring/layout.tsx, src/features/admin/mentoring/*
신청 라우트에 공개준비도 게이트 적용, 쪽지상담·관리자 모의 플래그 비활성화 처리(항상 false), 관리자 텍스트·문구 일부 수정.
스케줄 그리드·정산 모달
src/features/mentoring/ui/settings/weekly-schedule-grid.tsx, src/features/mentoring/ui/settings/settlement-register-modal.tsx
스케줄 드래프트 초기값·검증·커밋 흐름 도입, 정산 모달 단순화(인증 제거, 계좌 입력만 처리) 및 isSubmitting 지원.
공통 컴포넌트·접근성·스타일
src/components/common/ui/badge/index.tsx, src/components/common/ui/dropdown/single.tsx, src/components/common/ui/text-action-button.tsx, src/app/global.css, src/components/common/modals/study-completion-modal.tsx
Badge에 rose 색상 추가, Dropdown에 ariaLabel prop 추가, TextActionButton에 forwardRef 적용, 글로벌 유틸/애니메이션·패딩 변경, 모달에 sr-only 타이틀 추가.
훅·스토어·쿼리 변경
src/hooks/queries/*, src/stores/useMentorDirectoryStore.ts
usePhoneVerificationStatus 마운트 게이팅 도입, 쿼리 훅에 enabled 인자 추가, 로그아웃 시 멘토 관련 스토어 리셋 추가, 디렉토리 스토어 직렬화/정규화 변경.
서버 검증 에러 매핑
src/features/mentoring/model/mentor-registration-server-error.ts
서버 validation detail 파싱 및 paramName → form FieldPath/step 매핑 로직 추가.
기타 소규모 변경
src/app/(service)/(my)/note-consultation/page.tsx, src/features/mentoring/ui/pages/my-mentoring-*.tsx, src/features/mentoring/const/*
쪽지상담 플래그에 따른 CTA 조건부 렌더링, 문구/토스트 키 변경 등.

Sequence Diagram(s)

sequenceDiagram
    participant User as 사용자
    participant UI as 등록 UI (Form)
    participant Controller as RegistrationController
    participant Mapper as SettingsMapper
    participant API as MentorAPI
    participant Server as 서버

    User->>UI: 단계별 입력 및 저장 요청
    UI->>Controller: onSubmit(values)
    Controller->>Mapper: buildMentorSettingsUpsertRequest({ values, registrationOptions, persistedPredefinedCoreKeywords })
    Mapper->>Mapper: normalize careerEntries, core keywords, scheduleDrafts
    Mapper->>API: upsertMyMentorSettings({ values, registrationOptions, persistedPredefinedCoreKeywords })
    API->>Server: PUT /mentors/me (payload)
    Server-->>API: 200 / validation error (detail)
    
    alt 성공
        API-->>Controller: result
        Controller->>UI: show welcome modal / update state
    else 검증오류
        API-->>Controller: error(detail)
        Controller->>Controller: resolveMentorRegistrationServerErrorTarget(detail)
        Controller->>UI: focus invalid step / show field errors
    end
Loading
sequenceDiagram
    participant Client as 멘토 상세 페이지
    participant API as Mentor API
    participant Readiness as PublicReadiness 모듈
    participant UI as 상세 UI

    Client->>API: GET mentor profile
    API-->>Client: profile + settings
    Client->>Readiness: getMentorPublicReadiness(mentor)
    Readiness->>Readiness: 체크 필수 항목(정보, 스케줄, 가격 등)
    Readiness-->>Client: readiness 결과
    alt 상세 잠김
        Client->>UI: 렌더 잠금 오버레이 및 신청 비활성화
    else 상세 공개
        alt 신청 불가
            Client->>UI: 신청 비활성화 안내
        else 신청 가능
            Client->>UI: 신청 CTA 활성화
        end
    end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Poem

🐰 멘토링 정원에서
단계마다 씨앗 심고 경력 잎을 달고,
키워드들 정리해 빛나게 하니,
드래프트 바람에 스케줄이 춤추고,
준비도 높아지면 문이 활짝 열리네.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/mentoring-registration-sqaush

@Hyeonjun0527 Hyeonjun0527 self-assigned this Mar 23, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 20

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/utils/error-handler.ts (1)

500-531: ⚠️ Potential issue | 🟠 Major

detail 원문을 그대로 Sentry에 싣는 건 위험합니다.

Line 500과 Line 531은 백엔드가 임의 구조로 채우는 detail을 그대로 로그/Sentry extra로 보냅니다. 이 필드에는 검증 실패 payload, 식별자, 내부 진단값이 섞일 수 있어서 개인정보 유출과 이벤트 크기 초과를 동시에 만들 수 있습니다. 허용한 키만 추리거나 마스킹/길이 제한 후 전송해 주세요.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/error-handler.ts` around lines 500 - 531, The code currently
forwards errorInfo-derived detail (from extractApiErrorDetail) directly into
logData and Sentry extras; instead, implement a sanitizer function (e.g.,
sanitizeErrorDetail) that accepts the raw detail and returns a safe object by
whitelisting allowed keys, masking sensitive values (IDs, PII), and truncating
long strings/arrays to a max length, then replace usages of detail in the
logData construction and inside Sentry.withScope (where scope.setExtra('detail',
... ) is called) with the sanitized result; ensure extractApiErrorDetail and
errorInfo remain inputs but never send the raw detail to Sentry or external
logs.
src/features/mentoring/ui/registration/mentor-markdown-editor.tsx (1)

694-694: ⚠️ Potential issue | 🟡 Minor

임의의 Tailwind 값 사용이 금지되어 있습니다.

w-[180px]는 임의의 Tailwind 값입니다. 프로젝트 디자인 토큰을 사용하세요.

As per coding guidelines: "Forbidden: Tailwind arbitrary values (e.g., p-[4px], w-[320px]). Use only project custom design tokens."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/ui/registration/mentor-markdown-editor.tsx` at line
694, Replace the forbidden Tailwind arbitrary value w-[180px] in the className
on the element inside mentor-markdown-editor.tsx with the appropriate project
design token class; locate the className string that currently reads
"accent-background-brand-default w-[180px]" and swap w-[180px] for the
corresponding design token (e.g., the project's width token such as w-44 / token
name used across the codebase) or a utility class mapped from your design tokens
so no arbitrary bracketed value remains.
🧹 Nitpick comments (23)
src/features/mentoring/ui/common/mentoring-guide-modal.tsx (1)

21-24: GUIDE_CONTENT의 description과 런타임 조건부 텍스트 간 중복이 있습니다.

GUIDE_CONTENT[1].description(line 23)에 텍스트를 정의했지만, 렌더링 시(line 82-84) 해당 항목의 item.description을 사용하지 않고 noteConsultationGuideLine을 포함한 별도의 템플릿 문자열로 완전히 대체됩니다. 이로 인해 line 23의 description은 실질적으로 사용되지 않는 dead code가 됩니다.

가독성과 유지보수성을 위해 description을 placeholder로 단순화하거나, 조건부 로직을 GUIDE_CONTENT 생성 시점으로 옮기는 것을 고려해 주세요.

♻️ 제안: GUIDE_CONTENT를 함수로 변경하여 일관성 유지
-const GUIDE_CONTENT = [
+const getGuideContent = (noteConsultationEnabled: boolean) => {
+  const noteConsultationLine = noteConsultationEnabled
+    ? '- /note-consultation'
+    : '쪽지상담함은 현재 제공하지 않습니다.';
+
+  return [
   {
     title: '멘토링이 뭔가요?',
     description:
       '멘토와 멘티를 1:1로 연결해 약속을 잡아주는 기능입니다...',
   },
   {
     title: '관리 페이지는 어떻게 구분되나요?',
     description:
-      '유저(멘티) 관점 관리: 내가 신청한 멘토링 내역과 후기 작성을 관리합니다.\n- /my-study-review\n\n쪽지상담함과 멘토 운영 관리 화면은 현재 제공하지 않습니다.',
+      `유저(멘티) 관점 관리: 내가 신청한 멘토링 내역과 후기 작성을 관리합니다.\n- /my-study-review\n\n${noteConsultationLine}\n멘토 운영 관리 화면도 현재 제공하지 않습니다.`,
   },
   // ... 나머지 항목들
-];
+  ];
+};

그 후 렌더링 시 조건부 로직 제거:

-                  {item.title === '관리 페이지는 어떻게 구분되나요?'
-                    ? `유저(멘티) 관점 관리:...\n${noteConsultationGuideLine}\n...`
-                    : item.description}
+                  {item.description}

Also applies to: 57-59, 82-84

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/ui/common/mentoring-guide-modal.tsx` around lines 21 -
24, GUIDE_CONTENT currently contains dead/duplicated description text because
the render logic replaces item.description with a conditional template using
noteConsultationGuideLine; fix by making GUIDE_CONTENT a function (e.g.,
getGuideContent) that composes each entry's description up front using
noteConsultationGuideLine (or leaves a simple placeholder if you prefer), then
update the render to use item.description directly; ensure references to
GUIDE_CONTENT, getGuideContent (or your chosen function name), item.description
and noteConsultationGuideLine are updated so conditional logic lives in data
creation, not in the render.
src/features/admin/mentoring/model/use-admin-mentoring-overview-query.ts (1)

306-314: React Query 유사 인터페이스 유지에 대한 참고사항.

isLoading, isFetching, isError, error 등 React Query 스타일의 반환 구조를 유지하여 하위 호환성을 보장하고 있습니다. isFetching: false 하드코딩은 실제 비동기 fetch가 없으므로 의미론적으로 정확합니다. 다만, 향후 이 훅이 확장되어 실제 fetching이 필요해지면 인터페이스 재설계가 필요할 수 있습니다.

훅 이름의 "Query" 접미사가 React Query 사용을 암시하지만 실제로는 파생 상태입니다. 코드베이스 전반의 네이밍 컨벤션과 일관성을 유지한다면 현재대로 유지해도 무방하나, 혼동을 줄이기 위해 useAdminMentoringOverview와 같은 이름을 고려해 볼 수 있습니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/admin/mentoring/model/use-admin-mentoring-overview-query.ts`
around lines 306 - 314, The hook's name implies a React Query hook while it
actually returns derived state; rename the exported hook function from
useAdminMentoringOverviewQuery to useAdminMentoringOverview (and update its
default/named export and all import sites) to avoid confusion, keep the returned
React Query-like shape (hasHydrated, isLoading, isFetching, isError, error,
mentors, metrics) as-is for backward compatibility, and add a brief inline
comment near hasHydrated/overviewState explaining that isFetching is
intentionally static because no async fetch occurs.
src/components/my-page/profile.tsx (1)

104-127: 전화번호 블록의 로딩/에러 상태 체크 일관성 검토 권장

인증 배지(lines 96-101)와 미인증 프롬프트(lines 216-219)는 !resolvedIsLoading && !resolvedIsError 조건을 체크하지만, 전화번호 블록은 이 조건이 없습니다.

로딩 중일 때 캐시된 전화번호를 표시하는 것이 의도된 UX라면 현재 구현이 맞습니다. 다만 배지와 동일한 조건을 적용하면 더 일관된 상태 표시가 가능합니다.

🔧 일관성을 위한 선택적 수정
 {!hidePhoneNumber &&
+  !resolvedIsLoading &&
+  !resolvedIsError &&
   resolvedIsVerified &&
   resolvedPhoneNumber && (
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/my-page/profile.tsx` around lines 104 - 127, The phone-number
render condition should match the badge/prompt loading/error checks for
consistency: update the JSX conditional that currently uses {!hidePhoneNumber &&
resolvedIsVerified && resolvedPhoneNumber} to also require !resolvedIsLoading &&
!resolvedIsError so it becomes {!hidePhoneNumber && !resolvedIsLoading &&
!resolvedIsError && resolvedIsVerified && resolvedPhoneNumber}; locate this in
the component where formatPhoneNumber(resolvedPhoneNumber) and
setIsVerificationModalOpen are used and apply the same gating as the 인증 배지/미인증
프롬프트.
src/features/admin/mentoring/ui/mentor-applications-page-client.tsx (1)

287-293: 서버 계약 완료 시 업데이트 추적 필요

정산 정보 인증 필드가 정적 플레이스홀더("서버 정산 readiness 계약 필요")로 변경되었습니다. 서버 계약이 완료되면 이 부분을 실제 로직으로 대체해야 하므로, TODO 주석이나 이슈로 추적하는 것을 권장합니다.

💡 TODO 주석 추가 제안
                   <KeyValueRow
                     label="정산 정보 인증"
                     columnsClassName="grid-cols-[110px_minmax(0,1fr)]"
                     valueAs="p"
                   >
+                    {/* TODO: 서버 정산 readiness 계약 완료 후 실제 verified 상태 표시로 대체 */}
                     서버 정산 readiness 계약 필요
                   </KeyValueRow>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/admin/mentoring/ui/mentor-applications-page-client.tsx` around
lines 287 - 293, The static placeholder "서버 정산 readiness 계약 필요" inside the
KeyValueRow used in the MentorApplicationsPageClient component should be made
explicit as a TODO and wired for future dynamic data: replace the hardcoded text
with a clear TODO comment (e.g., // TODO: replace with server-provided
settlementVerification/status when server contract completes) and return a
sensible fallback UI (e.g., "미인증" or a loading/placeholder state) while awaiting
actual data; keep the KeyValueRow props (label="정산 정보 인증", columnsClassName,
valueAs="p") unchanged and ensure the unique identifier KeyValueRow and the
containing component (MentorApplicationsPageClient) are referenced in the
comment so future work can find and implement the server-driven logic.
src/features/admin/mentoring/model/mock-seed.ts (1)

60-65: seed의 schedulescheduleDrafts를 같은 상태로 맞춰 주세요.

Line 60-65에서 schedule.weekly는 채워져 있는데 scheduleDrafts는 항상 빈 값입니다. 이 함수가 server-like 값을 만드는 용도라면, draft를 source of truth로 읽는 폼/미리보기 경로에서 저장된 슬롯이 없는 것처럼 보여 실제 서버 상태와 다른 동작을 만들 수 있습니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/admin/mentoring/model/mock-seed.ts` around lines 60 - 65, The
seed currently sets schedule.weekly but leaves scheduleDrafts as
createEmptyMentorScheduleDrafts(), causing mismatched server vs draft state;
update the seeding so scheduleDrafts reflects the same data as schedule (e.g.,
populate scheduleDrafts from schedule.weekly) rather than always using
createEmptyMentorScheduleDrafts(); locate the schedule and scheduleDrafts fields
in the mock-seed and replace the empty-draft creation with a
draft-created-from-schedule approach (use the existing weekly data or a helper
to convert schedule.weekly into the draft shape).
src/features/mentoring/ui/apply/mentoring-apply-route-client.tsx (1)

18-25: 사용되지 않는 prop을 제거하세요.

selectedType prop이 인터페이스에 정의되어 있지만 컴포넌트에서 사용되지 않습니다. 컴포넌트 구조분해에서 제외되었으므로 인터페이스에서도 제거해야 합니다.

♻️ 미사용 prop 제거
 interface MentoringApplyRouteClientProps {
   mentorId: number;
-  selectedType?: MentoringMethodType;
 }

MentoringMethodType import도 더 이상 필요하지 않다면 함께 제거하세요.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/ui/apply/mentoring-apply-route-client.tsx` around
lines 18 - 25, Remove the unused prop from the props interface: delete
selectedType from interface MentoringApplyRouteClientProps and remove the
MentoringMethodType import if it is no longer used elsewhere; leave the
MentoringApplyRouteClient signature using only mentorId so the interface matches
the component's destructuring.
src/features/admin/mentoring/ui/mentor-registration-detail.tsx (2)

291-298: 중복 문자열 키 충돌 가능성이 있습니다.

동일한 질문이 여러 개 존재할 경우 key={question}은 키 충돌을 일으킬 수 있습니다. 코딩 가이드라인에 따라 인덱스를 포함한 키를 사용하세요.

♻️ 키 패턴 개선 제안
-              {settings.interviewQuestions.map((question) => (
+              {settings.interviewQuestions.map((question, index) => (
                 <p
-                  key={question}
+                  key={`${question}-${index}`}
                   className="font-designer-14r text-text-default"
                 >

As per coding guidelines: "For optional ID fields in React keys, always use nullish coalescing with index fallback."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/admin/mentoring/ui/mentor-registration-detail.tsx` around lines
291 - 298, The mapped list uses key={question} which can collide if duplicate
questions exist; update the map over settings.interviewQuestions in the
MentorRegistrationDetail component (mentor-registration-detail.tsx) to include
the index (e.g., map((question, index) => ...)) and set the key using a
nullish-coalescing index fallback (use question ?? index or a combined string
using question with index) so keys are stable and unique per list item.

223-233: 중복된 조건 검사를 단순화하세요.

hasContactInfo가 이미 contactPhone.length > 0으로 정의되어 있으므로, 225번 라인의 내부 조건 검사는 항상 true입니다.

♻️ 단순화 제안
       {hasContactInfo && (
         <Section title="연락처">
-          {contactPhone.length > 0 && (
-            <KeyValueRow
-              label="전화"
-              columnsClassName="grid-cols-[120px_minmax(0,1fr)]"
-            >
-              {`${settings?.contactCountryCode ?? ''} ${contactPhone}`.trim()}
-            </KeyValueRow>
-          )}
+          <KeyValueRow
+            label="전화"
+            columnsClassName="grid-cols-[120px_minmax(0,1fr)]"
+          >
+            {`${settings?.contactCountryCode ?? ''} ${contactPhone}`.trim()}
+          </KeyValueRow>
         </Section>
       )}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/admin/mentoring/ui/mentor-registration-detail.tsx` around lines
223 - 233, The nested condition is redundant because hasContactInfo is derived
from contactPhone.length > 0; remove the inner check and render the KeyValueRow
directly when hasContactInfo is true (or alternatively remove hasContactInfo and
use contactPhone.length > 0 at the Section level). Update the block that uses
hasContactInfo, KeyValueRow, and contactPhone so only a single conditional is
used (keep Section and KeyValueRow as-is and use hasContactInfo to guard
rendering the phone row).
src/features/mentoring/ui/settings/weekly-schedule-grid.tsx (1)

128-131: 불필요한 삼항 연산자

timezoneslotUnitMinutes의 삼항 연산자가 항상 동일한 값을 반환합니다. 이는 의도된 방어적 코드일 수 있지만, 현재 상태에서는 불필요합니다.

♻️ 단순화 제안
   return {
-    timezone: value.timezone === 'Asia/Seoul' ? 'Asia/Seoul' : 'Asia/Seoul',
-    slotUnitMinutes: value.slotUnitMinutes === 30 ? 30 : 30,
+    timezone: 'Asia/Seoul',
+    slotUnitMinutes: 30,
     weekly: createWeekdayRecord((day) => {

향후 다른 timezone/slotUnit 지원이 필요하면 그때 분기를 추가하면 됩니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/ui/settings/weekly-schedule-grid.tsx` around lines 128
- 131, The ternary expressions for timezone and slotUnitMinutes always return
the same value; remove the redundant conditional logic in the return object and
set timezone to 'Asia/Seoul' and slotUnitMinutes to 30 directly (inside the same
object that uses createWeekdayRecord for weekly) so the code reads simply and
clearly; if you later need defensive branching, add explicit cases around the
timezone/slotUnitMinutes assignments or prior validation before this return.
src/features/mentoring/ui/mentor-directory/mentor-card.tsx (2)

158-158: 금지된 Tailwind 임의값 사용

min-h-[56px]는 임의값입니다. 프로젝트 커스텀 디자인 토큰을 사용해야 합니다.

As per coding guidelines: "Forbidden: Tailwind arbitrary values."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/ui/mentor-directory/mentor-card.tsx` at line 158, The
div uses a forbidden Tailwind arbitrary value "min-h-[56px]"; replace that with
the project's design token class (e.g., the equivalent token like "min-h-14" or
the team's custom token class) inside the MentorCard component (look for the div
with className "mb-125 min-h-[56px]") so it uses the approved design token
instead of an arbitrary value.

125-125: 금지된 하드코딩 색상 사용

border-[#E5E7EB]는 하드코딩된 색상값입니다. 프로젝트 디자인 토큰을 사용해야 합니다.

♻️ 디자인 토큰 대체 제안
-        'h-full cursor-pointer overflow-hidden border border-[`#E5E7EB`] bg-white',
+        'h-full cursor-pointer overflow-hidden border border-border-subtle bg-white',

As per coding guidelines: "Forbidden: Hard-coded colors and spacing values. Use only @theme inline tokens from global.css."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/ui/mentor-directory/mentor-card.tsx` at line 125, The
class string in mentor-card.tsx currently uses a hard-coded color token
('border-[`#E5E7EB`]'); update the MentorCard component's className to use the
project design token from global.css instead of the literal hex value (replace
the hard-coded border color with the appropriate `@theme` inline token for
borders, e.g., the theme border token used across the app), ensuring the rest of
the utility classes ('h-full cursor-pointer overflow-hidden border bg-white')
remain unchanged.
src/features/mentoring/api/mentor-profile.mapper.ts (2)

195-203: enum-like 문자열 검증에 in 연산자 사용 권장

코딩 가이드라인에 따르면 백엔드의 enum-like 문자열 타입에는 in 연산자 가드를 사용해야 합니다.

♻️ `in` 연산자 사용 제안
+const VALID_PUBLIC_READINESS_STAGES = {
+  DETAIL_PREPARING: true,
+  APPLY_PREPARING: true,
+  APPLY_READY: true,
+} as const;
+
 const normalizePublicReadinessStage = (
   value: unknown,
 ): MentorProfile['publicReadinessStage'] => {
-  return value === 'DETAIL_PREPARING' ||
-    value === 'APPLY_PREPARING' ||
-    value === 'APPLY_READY'
-    ? value
+  return typeof value === 'string' && value in VALID_PUBLIC_READINESS_STAGES
+    ? (value as MentorProfile['publicReadinessStage'])
     : undefined;
 };

As per coding guidelines: "For enum-like string types from backend, use in operator guard with fallback instead of simple as type assertion."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/api/mentor-profile.mapper.ts` around lines 195 - 203,
The current normalizePublicReadinessStage function uses direct equality checks;
replace this with an 'in' operator guard against a defined enum-like object
(e.g. const PublicReadinessStage = { DETAIL_PREPARING: 'DETAIL_PREPARING',
APPLY_PREPARING: 'APPLY_PREPARING', APPLY_READY: 'APPLY_READY' }) and then in
normalizePublicReadinessStage check typeof value === 'string' && value in
PublicReadinessStage and return value (cast to
MentorProfile['publicReadinessStage'] if needed) otherwise return undefined;
update the function name normalizePublicReadinessStage to use that guard and
reference MentorProfile['publicReadinessStage'] as the return type.

38-42: API 기본 경로 형식에 대한 의존성 검토 필요

MENTOR_ASSET_BASE_URL이 정규표현식 /\/api\/v1\/?$/를 사용하여 /api/v1 경로를 제거합니다. API 기본 URL의 형식 변경(예: /api/v2/로의 업그레이드)이 발생하면 이 로직이 제대로 작동하지 않을 수 있습니다. 유사한 패턴이 src/app/api/auth/clear-session/route.ts에도 존재하므로, API 버전 관리 전략 변경 시 이러한 모든 위치를 함께 검토하세요.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/api/mentor-profile.mapper.ts` around lines 38 - 42,
MENTOR_ASSET_BASE_URL currently strips only a trailing "/api/v1" which will
break if the API version path changes; update the logic that builds
MENTOR_ASSET_BASE_URL (the constant using
process.env.NEXT_PUBLIC_API_PROD_BASE_URL / NEXT_PUBLIC_API_BASE_URL) to remove
any trailing "/api" optionally followed by a version segment (e.g., use a
pattern like "/api(/v\\d+)?/?$" or parse the URL and drop the pathname
segments), and make the same change in the similar occurrence in
src/app/api/auth/clear-session/route.ts so both places remain correct when API
versioning changes.
src/stores/useMentorDirectoryStore.ts (2)

59-68: 매직 넘버 기반 리뷰 식별 로직의 취약성

review.id === mentorId * 10은 리뷰 ID 생성 규칙에 의존하는 취약한 패턴입니다. ID 생성 로직이 변경되면 이 검사가 실패할 수 있습니다.

♻️ 명시적 마커 사용 제안
+const GENERATED_WELCOME_REVIEW_MARKER = 'GENERATED_WELCOME';
+
 const isGeneratedWelcomeReview = (
   mentorId: number,
   review: MentorProfile['reviews'][number],
 ) => {
   return (
-    review.id === mentorId * 10 &&
+    (review.id === mentorId * 10 || review.id === GENERATED_WELCOME_REVIEW_MARKER) &&
     review.authorName === 'ZERO-ONE' &&
     review.content === GENERATED_WELCOME_REVIEW_CONTENT
   );
 };

향후 리뷰 생성 시 명시적 마커를 사용하거나, authorNamecontent 조합만으로 식별하는 것이 더 안정적입니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/stores/useMentorDirectoryStore.ts` around lines 59 - 68, The current
isGeneratedWelcomeReview relies on a fragile magic-number check (review.id ===
mentorId * 10); change it to detect generated welcome reviews using an explicit
marker or, if no marker exists, fall back to matching authorName and
GENERATED_WELCOME_REVIEW_CONTENT. Update the isGeneratedWelcomeReview function
to first look for a dedicated flag (e.g., review.isGeneratedWelcome or
review.metadata?.generatedWelcome) and return true if present and truthy; if
that field is absent, return true only when review.authorName === 'ZERO-ONE' &&
review.content === GENERATED_WELCOME_REVIEW_CONTENT. Ensure you reference and
preserve the existing constant GENERATED_WELCOME_REVIEW_CONTENT and the function
name isGeneratedWelcomeReview when making the change.

400-405: partializemigrate에서 normalizePersistedMentor 중복 호출 제거 권장

partialize에서 저장 전 멘토를 정규화하고(402줄), migrate에서 로드 후 다시 정규화(normalizePersistedState → 306줄)하고 있습니다. migrate는 모든 데이터 로드 시 항상 실행되므로 partialize에서의 정규화는 중복입니다. 정규화를 한 곳으로 통합하세요. (일반적으로 migrate에 유지하는 것을 권장)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/stores/useMentorDirectoryStore.ts` around lines 400 - 405, The partialize
function currently calls normalizePersistedMentor before persisting, causing
duplicate normalization because migrate/normalizePersistedState also normalizes
on load; remove the normalizePersistedMentor mapping from partialize and let
migrate/normalizePersistedState be the single normalization point so
PersistedMentorDirectoryState is only normalized during migrate; update
partialize to return memberId, createdMentors (unchanged array),
mentorIdByMember, and nextMentorId without calling normalizePersistedMentor.
src/features/mentoring/ui/registration/mentor-markdown-content.tsx (1)

246-248: 금지된 Tailwind 임의값 사용

max-h-[400px], max-w-[min(100%,400px)], py-[2px]는 코딩 가이드라인에서 금지하는 임의값입니다. 프로젝트 커스텀 디자인 토큰을 사용해야 합니다.

♻️ 디자인 토큰 대체 제안
-        '[&_img]:rounded-100 [&_img]:border-border-subtle [&_img]:mb-150 [&_img]:block [&_img]:h-auto [&_img]:max-h-[400px] [&_img]:max-w-[min(100%,400px)] [&_img]:border [&_img]:object-contain',
-        '[&_code]:rounded-50 [&_code]:bg-background-alternative [&_code]:font-designer-13r [&_code]:px-75 [&_code]:py-[2px]',
+        '[&_img]:rounded-100 [&_img]:border-border-subtle [&_img]:mb-150 [&_img]:block [&_img]:h-auto [&_img]:max-h-400 [&_img]:max-w-full [&_img]:border [&_img]:object-contain',
+        '[&_code]:rounded-50 [&_code]:bg-background-alternative [&_code]:font-designer-13r [&_code]:px-75 [&_code]:py-25',

As per coding guidelines: "Forbidden: Tailwind arbitrary values (e.g., p-[4px], w-[320px]). Use only project custom design tokens."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/ui/registration/mentor-markdown-content.tsx` around
lines 246 - 248, The class string array in mentor-markdown-content.tsx contains
forbidden Tailwind arbitrary values (specifically "max-h-[400px]",
"max-w-[min(100%,400px)]" and "py-[2px]")—replace those with the project's
custom design tokens (use the corresponding max-height, max-width and padding
token classes from your design system) in the same class list so the styling
remains equivalent; update any token names to match your Tailwind/token config
and run the linter to ensure no arbitrary-value patterns remain.
src/features/mentoring/ui/registration/mentor-career-entries-editor.tsx (2)

259-266: watchedCareerEntries[index] undefined 접근 가능성

useWatch의 반환값이 배열 동기화 타이밍에 따라 undefined일 수 있습니다. 현재 currentEntry?. 옵셔널 체이닝으로 일부 보호되어 있지만, 더 명확한 가드가 좋습니다.

♻️ 명시적 가드 추가
           {typedFields.map((field, index) => {
             const entryErrors = careerEntryErrors[index];
             const currentEntry = watchedCareerEntries[index];
+            if (!currentEntry) {
+              return null;
+            }
             const startParts = splitCareerEntryMonth(currentEntry?.startMonth);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/ui/registration/mentor-career-entries-editor.tsx`
around lines 259 - 266, The map over typedFields can access
watchedCareerEntries[index] before useWatch has a value, so add an explicit
guard for currentEntry to avoid passing undefined into splitCareerEntryMonth: in
the mapping callback (where typedFields.map is used) compute const currentEntry
= watchedCareerEntries?.[index]; if currentEntry is falsy, return a safe
fallback UI (or continue) instead of calling splitCareerEntryMonth; otherwise
call splitCareerEntryMonth(currentEntry.startMonth) and
splitCareerEntryMonth(currentEntry.endMonth) and proceed to read
entryErrors/descriptionError/startMonthError/endMonthError—this prevents
undefined access and keeps the rest of the logic in the same block (referencing
typedFields, careerEntryErrors, watchedCareerEntries, splitCareerEntryMonth, and
entryErrors).

376-376: 금지된 Tailwind 임의값 사용

grid-cols-[minmax(0,1fr)_120px]는 임의값을 사용하고 있습니다. 동일한 패턴이 Lines 376, 427에서 반복됩니다.

As per coding guidelines: "Forbidden: Tailwind arbitrary values. Use only project custom design tokens."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/ui/registration/mentor-career-entries-editor.tsx` at
line 376, The Tailwind arbitrary class "grid grid-cols-[minmax(0,1fr)_120px]
gap-75" (used in the MentorCareerEntriesEditor JSX) violates the forbidden
arbitrary-values rule; replace it with a project-approved solution by either
using existing design-token utility classes or adding a small CSS module/class
(e.g., twoColumnGrid) that sets grid-template-columns: minmax(0, 1fr) 120px and
gap using the project's gap token, then swap both occurrences (the two places in
MentorCareerEntriesEditor where that class appears) to use the new token-based
utility or CSS class instead of the bracketed arbitrary Tailwind value.
src/features/mentoring/ui/detail/mentor-detail-header-section.tsx (1)

77-82: 헤더 레이아웃도 임의 Tailwind 값 대신 토큰 클래스로 맞춰주세요.

sm:text-[30px]xl:grid-cols-[...]는 repo 금지 패턴입니다. 여기서 허용하면 상세 쪽의 예외가 계속 퍼집니다.

As per coding guidelines "Forbidden: Tailwind arbitrary values (e.g., p-[4px], w-[320px]). Use only project custom design tokens."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/ui/detail/mentor-detail-header-section.tsx` around
lines 77 - 82, The header uses forbidden Tailwind arbitrary values: remove
sm:text-[30px] from the h1 and xl:grid-cols-[minmax(0,0.9fr)_minmax(0,1.1fr)]
from the grid div and replace them with your project's design-token classes (the
same kind as font-designer-24b and text-text-strong) that provide the required
responsive font size and layout breakpoints; update the h1 (className on the
element rendering mentoringTitleLabel) to use the approved small-screen text
token and update the surrounding grid div (className on the element starting the
grid) to use the approved xl grid/layout token(s) defined in the repo so no
arbitrary bracketed Tailwind values remain.
src/types/mentoring/settings.ts (1)

54-54: 레코드 shape는 type보다 interface로 맞춰주세요.

MentorScheduleTextDrafts는 객체 구조를 표현하므로 이 repo의 TypeScript 규칙과 어긋납니다.

🧩 제안 수정
-export type MentorScheduleTextDrafts = Record<WeekdayKey, string[]>;
+export interface MentorScheduleTextDrafts
+  extends Record<WeekdayKey, string[]> {}
As per coding guidelines "Use `interface` for defining object shapes in TypeScript, not `type` aliases, when representing object structures."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/types/mentoring/settings.ts` at line 54, Replace the type alias
MentorScheduleTextDrafts with an interface that models the object shape;
specifically, change the declaration of MentorScheduleTextDrafts to an interface
that maps each WeekdayKey to a string[] (use a mapped property signature over
WeekdayKey) so it follows the repo rule of using interface for object shapes and
keeps the same runtime shape and typings.
src/features/mentoring/ui/registration/mentor-registration-form.tsx (1)

159-165: step UI에 Tailwind arbitrary value가 다시 들어왔습니다.

max-w-[240px], min-w-[132px], mt-[2px], min-h-[420px]는 새 화면 구조를 디자인 토큰 밖으로 빼냅니다. 프로젝트 규칙상 이런 값은 커스텀 토큰/유틸로 승격하거나 style로 옮겨서 JSX 클래스는 토큰 기반으로 유지하는 편이 맞습니다.
As per coding guidelines, **/*.{tsx,jsx,ts,js}: Forbidden: Tailwind arbitrary values ... Forbidden: Hard-coded colors and spacing values. Use only project custom design tokens.

Also applies to: 482-482, 615-615, 1913-1915

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/ui/registration/mentor-registration-form.tsx` around
lines 159 - 165, The Tooltip usage is using Tailwind arbitrary values
(max-w-[240px], min-w-[132px], mt-[2px], min-h-[420px]) which violates project
rules; update the Tooltip props (contentClassName and any wrapper className) to
use existing design tokens or project utility classes (create/choose matching
custom tokens like token-max-w-sm, token-min-w-xs, token-mt-xxs, token-min-h-lg)
or move the specific sizes into a style prop (e.g., style={{ maxWidth: ...,
minWidth: ..., marginTop: ..., minHeight: ... }}) while keeping JSX class names
token-based; apply the same replacement for the other occurrences in this file
where those exact arbitrary classes appear (search for max-w-[240px],
min-w-[132px], mt-[2px], min-h-[420px]) and ensure any new token names follow
the project’s design token conventions.
src/features/mentoring/ui/registration/mentor-registration-page-view.tsx (1)

28-29: 프리뷰 레이아웃에 토큰 밖 임의 값이 다시 들어왔습니다.

Line 28-29의 108/60 오프셋과 여기서 추가된 xl:grid-cols-[…], w-[16px], min-w-[360px], w-[520px]는 모두 디자인 토큰 스케일을 우회합니다. 이 레이아웃이 꼭 필요해도 JSX 클래스는 토큰 기반으로 두고, 가변 값은 style이나 로컬 CSS 유틸로 분리하는 쪽이 레포 규칙과 유지보수성에 맞습니다.
As per coding guidelines, **/*.{tsx,jsx,ts,js}: Forbidden: Tailwind arbitrary values ... Forbidden: Hard-coded colors and spacing values. Use only project custom design tokens.

Also applies to: 158-178, 243-246, 291-291, 363-368

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/ui/registration/mentor-registration-page-view.tsx`
around lines 28 - 29, The diff introduces hard-coded spacing and Tailwind
arbitrary values that bypass design tokens (e.g., PREVIEW_PANEL_TOP_OFFSET,
PREVIEW_PANEL_BOTTOM_OFFSET and classes like xl:grid-cols-[…], w-[16px],
min-w-[360px], w-[520px]); replace these by using existing design tokens or move
variable values into local CSS utilities/CSS variables or inline style
constants: convert PREVIEW_PANEL_TOP_OFFSET and PREVIEW_PANEL_BOTTOM_OFFSET into
token-based values or CSS variables and swap any w-[…], min-w-[…], and
xl:grid-cols-[…] usages in the JSX to corresponding token classes (or reference
the new CSS variables/local utility classes) so the JSX class list only uses
project tokens and the arbitrary/flexible measurements live in a scoped style or
style prop.
src/features/mentoring/model/use-mentor-registration-controller.ts (1)

139-152: 회사 분류 상수 하드코딩 - 유지보수 취약점

normalizeDraftCompanyCategory에서 회사 분류 값이 직접 하드코딩되어 있습니다. COMPANY_CATEGORY_OPTIONS가 이미 @/types/mentoring/settings에서 export되고 있으므로, 해당 상수를 참조하여 단일 소스를 유지하는 것이 좋습니다.

♻️ 상수 참조로 변경 제안
+import { COMPANY_CATEGORY_OPTIONS } from '@/types/mentoring/settings';
+
+type CompanyCategory = (typeof COMPANY_CATEGORY_OPTIONS)[number];
+
 const normalizeDraftCompanyCategory = (
   value: unknown,
 ): MentorRegistrationFormInputValues['companyCategory'] | undefined => {
-  if (
-    value !== '네카라쿠배' &&
-    value !== 'IT 유니콘' &&
-    value !== '창업' &&
-    value !== '기타'
-  ) {
+  if (
+    typeof value !== 'string' ||
+    !COMPANY_CATEGORY_OPTIONS.includes(value as CompanyCategory)
+  ) {
     return undefined;
   }
 
-  return value;
+  return value as MentorRegistrationFormInputValues['companyCategory'];
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/model/use-mentor-registration-controller.ts` around
lines 139 - 152, normalizeDraftCompanyCategory currently hardcodes company
category strings; import COMPANY_CATEGORY_OPTIONS from
'@/types/mentoring/settings' and use it instead: update the function
(normalizeDraftCompanyCategory) to check the incoming value against the
set/array derived from COMPANY_CATEGORY_OPTIONS (e.g., map to option.value or
the correct key) and return undefined when not present, and add the import for
COMPANY_CATEGORY_OPTIONS at the top of the module.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2c0559af-348e-4687-8290-949a98673048

📥 Commits

Reviewing files that changed from the base of the PR and between 371e07d and d25a41a.

📒 Files selected for processing (76)
  • src/api/client/api-error.ts
  • src/api/openapi/models/mentor-settings-upsert-request.ts
  • src/app/(admin)/admin/mentoring/layout.tsx
  • src/app/(service)/(my)/note-consultation/page.tsx
  • src/app/global.css
  • src/components/common/modals/study-completion-modal.tsx
  • src/components/common/ui/badge/index.tsx
  • src/components/common/ui/dropdown/single.tsx
  • src/components/common/ui/form/multi-item-selector.tsx
  • src/components/common/ui/text-action-button.tsx
  • src/components/my-page/profile.tsx
  • src/features/admin/mentoring/model/mock-seed.ts
  • src/features/admin/mentoring/model/use-admin-mentoring-overview-query.ts
  • src/features/admin/mentoring/ui/mentor-applications-page-client.tsx
  • src/features/admin/mentoring/ui/mentor-operations-page-client.tsx
  • src/features/admin/mentoring/ui/mentor-registration-detail.tsx
  • src/features/admin/mentoring/ui/mentoring-dashboard-page-client.tsx
  • src/features/admin/mentoring/ui/mentoring-flow-guide.tsx
  • src/features/mentoring/api/mentor-api.ts
  • src/features/mentoring/api/mentor-api.types.ts
  • src/features/mentoring/api/mentor-core-keyword-contract.ts
  • src/features/mentoring/api/mentor-profile.mapper.ts
  • src/features/mentoring/api/mentor-settings.mapper.ts
  • src/features/mentoring/const/mentor-registration-labels.ts
  • src/features/mentoring/const/mentoring-list-labels.ts
  • src/features/mentoring/model/mentor-profile-utils.ts
  • src/features/mentoring/model/mentor-public-readiness.ts
  • src/features/mentoring/model/mentor-registration-draft-storage.ts
  • src/features/mentoring/model/mentor-registration-entry-onboarding.ts
  • src/features/mentoring/model/mentor-registration-keywords.ts
  • src/features/mentoring/model/mentor-registration-preview.ts
  • src/features/mentoring/model/mentor-registration-server-error.ts
  • src/features/mentoring/model/mentor-settings.ts
  • src/features/mentoring/model/mentoring-feature-flag.ts
  • src/features/mentoring/model/use-floating-panel-scroll-nudge.ts
  • src/features/mentoring/model/use-mentor-registration-controller.ts
  • src/features/mentoring/model/use-mentor-registration-preview-model.ts
  • src/features/mentoring/model/use-mentor-registration-preview-panel.ts
  • src/features/mentoring/model/use-upsert-my-mentor-settings-mutation.ts
  • src/features/mentoring/ui/apply/mentoring-apply-route-client.tsx
  • src/features/mentoring/ui/common/mentor-profile-meta-list.tsx
  • src/features/mentoring/ui/common/mentoring-guide-modal.tsx
  • src/features/mentoring/ui/detail/mentor-career-history-panel.tsx
  • src/features/mentoring/ui/detail/mentor-detail-header-section.tsx
  • src/features/mentoring/ui/detail/mentor-detail-method-section.tsx
  • src/features/mentoring/ui/detail/mentor-detail-page.tsx
  • src/features/mentoring/ui/detail/mentor-detail-review-section.tsx
  • src/features/mentoring/ui/detail/mentor-detail-sidebar-cta.tsx
  • src/features/mentoring/ui/detail/mentor-route-fallback.tsx
  • src/features/mentoring/ui/detail/review-stars.tsx
  • src/features/mentoring/ui/mentor-directory/mentor-card.tsx
  • src/features/mentoring/ui/mentor-directory/mentor-directory-filters.tsx
  • src/features/mentoring/ui/pages/my-mentoring-detail-page.tsx
  • src/features/mentoring/ui/pages/my-mentoring-page.tsx
  • src/features/mentoring/ui/registration/mentor-career-entries-editor.tsx
  • src/features/mentoring/ui/registration/mentor-markdown-content.tsx
  • src/features/mentoring/ui/registration/mentor-markdown-editor.tsx
  • src/features/mentoring/ui/registration/mentor-registration-form.tsx
  • src/features/mentoring/ui/registration/mentor-registration-header.tsx
  • src/features/mentoring/ui/registration/mentor-registration-page-view.tsx
  • src/features/mentoring/ui/settings/settlement-register-modal.tsx
  • src/features/mentoring/ui/settings/weekly-schedule-grid.tsx
  • src/features/mentoring/ui/temp/contact-seed-actions.tsx
  • src/hooks/queries/settlement-account-api.ts
  • src/hooks/queries/use-auth-mutation.ts
  • src/hooks/queries/use-phone-verification-status.ts
  • src/hooks/queries/use-update-user-profile-mutation.ts
  • src/stores/useMentorDirectoryStore.ts
  • src/types/mentoring/domain.ts
  • src/types/mentoring/markdown.ts
  • src/types/mentoring/registration-options.ts
  • src/types/mentoring/registration-view.ts
  • src/types/mentoring/settings.ts
  • src/types/schemas/mentor-registration-schema.ts
  • src/types/schemas/mentoring-admin-schema.ts
  • src/utils/error-handler.ts

Comment thread src/components/common/ui/form/multi-item-selector.tsx
Comment thread src/features/mentoring/api/mentor-settings.mapper.ts
Comment thread src/features/mentoring/model/mentor-public-readiness.ts
Comment thread src/features/mentoring/model/mentor-public-readiness.ts
Comment thread src/features/mentoring/ui/registration/mentor-registration-form.tsx Outdated
Comment thread src/features/mentoring/ui/registration/mentor-registration-form.tsx
Comment on lines +100 to 110
try {
await onSubmit({
bankName: draft.bankName.trim(),
accountNumber: draft.accountNumber.trim(),
accountHolder: draft.accountHolder.trim(),
});
closeModal();
} catch {
// Errors are surfaced by the caller toast. Keep the modal open so the
// user can retry without re-entering the values.
}

@coderabbitai coderabbitai Bot Mar 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

저장 실패를 토스트에만 맡기면 모달 안 재시도 맥락이 사라집니다.

catch에서 아무 상태도 갱신하지 않아 실패 후에도 모달 본문에는 이유가 남지 않습니다. 이미 inline error 슬롯이 있으니 analyzeError() 기반 메시지를 errorMessage에 먼저 넣고, Toast는 보조 피드백으로만 남겨 주세요.

As per coding guidelines "Error handling uses analyzeError() from src/utils/error-handler.ts for classification." and "For recoverable failures in UI: use inline error display first, Toast as secondary."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/ui/settings/settlement-register-modal.tsx` around
lines 100 - 110, The catch block currently swallows errors so the modal loses
inline context; update the try/catch around the onSubmit call (the block that
calls onSubmit({ bankName: draft.bankName.trim(), accountNumber:
draft.accountNumber.trim(), accountHolder: draft.accountHolder.trim() }) and
closeModal()) to catch the thrown error as e, compute an inline message via
analyzeError(e) and set the component's errorMessage state so the inline error
slot shows the failure, keep the modal open (do not call closeModal on failure),
and still trigger the toast as secondary feedback if desired.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반영완료

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rate Limit Exceeded

@Hyeonjun0527 have exceeded the limit for the number of chat messages per hour. Please wait 1 minutes and 9 seconds before sending another message.

Comment thread src/features/mentoring/ui/settings/settlement-register-modal.tsx
Comment thread src/types/mentoring/markdown.ts
@Hyeonjun0527 Hyeonjun0527 force-pushed the refactor/mentoring-registration-sqaush branch from d25a41a to a4312d2 Compare March 23, 2026 13:45

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/features/admin/mentoring/model/use-admin-mentoring-overview-query.ts (1)

3-3: ⚠️ Potential issue | 🟠 Major

실패를 빈 오버뷰로 치환하면 관리자 지표가 실제 0건과 구분되지 않습니다.

현재 catch가 모든 예외에서 EMPTY_ADMIN_MENTORING_OVERVIEW를 반환해서, 계약 파싱 실패나 런타임 오류가 나도 화면에는 정상적인 빈 상태처럼 mentors/metrics가 내려갑니다. isError를 별도로 처리하지 않는 소비처에서는 지표를 0건으로 오인할 수 있으니, 마지막 정상 스냅샷을 유지하거나 data 자체를 분리하는 편이 안전합니다. hasHydrated 전에는 계산을 건너뛰는 것도 같이 묶는 편이 좋습니다.

예시 수정안
-import { useMemo } from 'react';
+import { useMemo, useRef } from 'react';
...
+  const lastSuccessfulOverviewRef = useRef<AdminMentoringOverviewQueryResult>(
+    EMPTY_ADMIN_MENTORING_OVERVIEW,
+  );
+
   const overviewState = useMemo(() => {
+    if (!hasHydrated) {
+      return {
+        data: lastSuccessfulOverviewRef.current,
+        error: undefined,
+      } as const;
+    }
+
     try {
+      const data = getAdminMentoringOverview({
+        createdMentors,
+        mentorIdByMember,
+        requestsByMentor,
+        sessionsByMentor,
+        reviewsByMentor,
+        screeningByMentor,
+        operationByMentor,
+      });
+      lastSuccessfulOverviewRef.current = data;
+
       return {
-        data: getAdminMentoringOverview({
-          createdMentors,
-          mentorIdByMember,
-          requestsByMentor,
-          sessionsByMentor,
-          reviewsByMentor,
-          screeningByMentor,
-          operationByMentor,
-        }),
+        data,
         error: undefined,
       } as const;
     } catch (error) {
       return {
-        data: EMPTY_ADMIN_MENTORING_OVERVIEW,
+        data: lastSuccessfulOverviewRef.current,
         error: normalizeAdminMentoringOverviewQueryError(error),
       } as const;
     }
   }, [
+    hasHydrated,
     createdMentors,
     mentorIdByMember,
     operationByMentor,
     requestsByMentor,
     reviewsByMentor,
     screeningByMentor,
     sessionsByMentor,
   ]);

Also applies to: 276-313

src/utils/error-handler.ts (1)

500-531: ⚠️ Potential issue | 🟠 Major

detail 전체를 그대로 Sentry에 실으면 개인 입력값까지 적재될 수 있습니다.

ApiError.detail은 검증 실패 payload처럼 임의의 응답 본문을 담을 수 있는데, 지금은 redaction 없이 개발 로그와 Sentry extra에 모두 실립니다. 멘토링 등록 폼의 이메일/전화번호/상세소개 같은 사용자 입력이 섞일 수 있으니, 안전한 키만 화이트리스트하거나 요약된 값으로 축약해서 보내는 편이 안전합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/error-handler.ts` around lines 500 - 531, The current code sends
the raw detail (from extractApiErrorDetail and stored in variable detail) to
both console.error and Sentry (inside Sentry.withScope via scope.setExtra),
which may include sensitive user input; replace usage of the raw detail with a
sanitized or whitelisted summary before logging and reporting: implement or call
a sanitizer (e.g., sanitizeApiErrorDetail(detail) or adjust
extractApiErrorDetail to return only safe keys like error type, validation keys,
and truncated/hashed values) and use that sanitizedDetail in the development
console output and in Sentry.withScope (replace scope.setExtra('detail', detail)
with scope.setExtra('detail', sanitizedDetail)); ensure errorInfo, stack, and
other non-sensitive fields remain unchanged.
src/features/admin/mentoring/model/mock-seed.ts (1)

60-65: ⚠️ Potential issue | 🔴 Critical

scheduleDraftsschedule으로부터 생성하도록 수정하세요.

src/features/mentoring/api/mentor-settings.mapper.ts(460줄)에서 서버는 createMentorScheduleTextDrafts(schedule)로 draft를 생성합니다. createServerLikeMentorRegistrationValues라는 함수명에 맞게 서버 동작을 정확히 모방하려면, 현재 createEmptyMentorScheduleDrafts()를 다음과 같이 변경하세요:

src/features/mentoring/model/mentor-settings.ts에서 createMentorScheduleTextDrafts를 import한 후:

scheduleDrafts: createMentorScheduleTextDrafts(schedule),

이렇게 하면 mock 데이터가 실제 서버 응답과 일치하게 됩니다. 현재 form의 fallback(773-774줄)이 빈 draft를 허용하지만, mock이 "server-like"라면 이 일관성을 유지하는 것이 중요합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/admin/mentoring/model/mock-seed.ts` around lines 60 - 65, The
mock seed currently uses createEmptyMentorScheduleDrafts() for scheduleDrafts;
update it to mirror server behavior by importing createMentorScheduleTextDrafts
from src/features/mentoring/model/mentor-settings.ts and replace the value with
createMentorScheduleTextDrafts(schedule) inside
createServerLikeMentorRegistrationValues so scheduleDrafts is derived from the
schedule object (the variable named schedule) instead of being empty.
♻️ Duplicate comments (5)
src/features/mentoring/ui/settings/settlement-register-modal.tsx (2)

108-118: ⚠️ Potential issue | 🟡 Minor

실패 이유를 모달 안에도 남겨 주세요.

지금 catch가 비어 있어서 재시도 맥락이 토스트에만 남습니다. 이미 inline error 슬롯이 있으니 analyzeError(error) 결과를 errorMessage에 먼저 넣어 두는 편이, 실패 후 바로 원인을 수정하기 쉽습니다.

As per coding guidelines "Error handling uses analyzeError() from src/utils/error-handler.ts for classification." and "For recoverable failures in UI: use inline error display first, Toast as secondary."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/ui/settings/settlement-register-modal.tsx` around
lines 108 - 118, The catch block for the submission flow currently swallows
errors; update the catch to accept the error, run analyzeError(error) and set
the component's inline error state (e.g., errorMessage) with that result so the
modal shows the failure reason while keeping the modal open for retry; keep the
existing caller toast behavior but ensure you call analyzeError inside the catch
and assign its output to the error property used by the inline error slot
(referencing onSubmit, draft, closeModal, analyzeError and the inline
errorMessage state).

125-125: 🛠️ Refactor suggestion | 🟠 Major

max-w-[720px]는 저장소 규칙상 금지된 Tailwind arbitrary value입니다.

여기서는 프로젝트 width 토큰/유틸리티 클래스로 치환해 주세요.

As per coding guidelines "Forbidden: Tailwind arbitrary values (e.g., p-[4px], w-[320px]). Use only project custom design tokens."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/ui/settings/settlement-register-modal.tsx` at line
125, The Modal.Content currently uses a forbidden Tailwind arbitrary value class
"max-w-[720px]"; replace that class with the project's approved width
token/utility (i.e., remove "max-w-[720px]" from the className on Modal.Content
in SettlementRegisterModal and swap in the corresponding design-system token
class such as the project's container/width utility like "max-w-container" or
the appropriate "max-w-<design-token>" used elsewhere in the codebase) so that
no arbitrary Tailwind values remain.
src/features/mentoring/ui/detail/mentor-detail-header-section.tsx (1)

77-81: 🛠️ Refactor suggestion | 🟠 Major

헤더 레이아웃에 금지된 arbitrary value가 남아 있습니다.

sm:text-[30px]xl:grid-cols-[...]는 둘 다 저장소 규칙 위반입니다. 상세 본문에서처럼 프로젝트 토큰/유틸리티 클래스로 바꿔 주세요.

As per coding guidelines "Forbidden: Tailwind arbitrary values (e.g., p-[4px], w-[320px]). Use only project custom design tokens."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/ui/detail/mentor-detail-header-section.tsx` around
lines 77 - 81, The header component in mentor-detail-header-section.tsx contains
forbidden Tailwind arbitrary values: replace the sm:text-[30px] on the <h1> and
the xl:grid-cols-[minmax(0,0.9fr)_minmax(0,1.1fr)] on the surrounding <div> with
the project's design-token/utility classes (use the same token names/pattern
used elsewhere in the detail UI), e.g., swap sm:text-[30px] for the
corresponding responsive text token utility and swap the xl:grid-cols[...] for
the predefined grid-column token or utility that expresses the 0.9/1.1 split;
update className strings in the MentorDetailHeaderSection (or the component that
renders the shown <h1> and <div>) to use those token utilities instead of
arbitrary values.
src/features/mentoring/model/mentor-registration-server-error.ts (1)

167-191: ⚠️ Potential issue | 🟠 Major

methods[0].enabled 류의 bracket 경로는 아직 step 매핑에서 빠집니다.

정규식은 price/durationMinutes만 처리하고, 아래 fallback은 methods.만 검사합니다. 그래서 methods[0].enabled 같은 서버 오류는 {}로 떨어져 저장 실패 후에도 가격/시간 step으로 이동하지 못합니다.

🔧 제안 수정
-  if (paramName === 'methods' || paramName.startsWith('methods.')) {
+  if (
+    paramName === 'methods' ||
+    paramName.startsWith('methods.') ||
+    paramName.startsWith('methods[')
+  ) {
     return {
       stepId: MENTOR_REGISTRATION_STEP_IDS.pricingAndTime,
     };
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/model/mentor-registration-server-error.ts` around
lines 167 - 191, The logic that maps server paramName to the pricing/time step
misses bracketed properties like "methods[0].enabled": update the methodMatch
detection to capture property names beyond "price" and "durationMinutes" (e.g.,
include "enabled") or make the regex/property check generic for any property
after methods[...] (change
/^methods(?:\[(\d+)\]|\.(\d+))(?:\.(price|durationMinutes))?$/ to allow other
property names), update the methodField union/handling in the methodMatch branch
(methodField can be 'price'|'durationMinutes'|'enabled'|undefined) and ensure
getMethodFieldPath is only called for fields that need a fieldPath (leave
fieldPath undefined for "enabled"), and also expand the fallback condition to
treat "methods[" prefix the same as "methods." so
paramName.startsWith('methods[') routes to
MENTOR_REGISTRATION_STEP_IDS.pricingAndTime.
src/features/mentoring/ui/detail/mentor-detail-sidebar-cta.tsx (1)

221-239: 🛠️ Refactor suggestion | 🟠 Major

mt-[2px] 두 군데 모두 금지된 arbitrary value입니다.

아이콘 수직 정렬은 토큰 class나 기존 정렬 유틸리티로 맞춰 주세요. 임의 값을 남기면 같은 예외가 계속 늘어납니다.

As per coding guidelines "Forbidden: Tailwind arbitrary values (e.g., p-[4px], w-[320px]). Use only project custom design tokens."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/ui/detail/mentor-detail-sidebar-cta.tsx` around lines
221 - 239, Replace the forbidden arbitrary Tailwind value mt-[2px] on the two
Info components with a sanctioned spacing token or utility and/or adjust layout
alignment; locate the two Info usages (the one inside the !isApplicationReady
conditional and the one above acceptancePolicy.title/description) and remove
mt-[2px], then use an approved class such as a project spacing token or a
standard Tailwind utility (e.g., mt-2 or items-center on the parent container)
to achieve the same vertical alignment without arbitrary values.
🧹 Nitpick comments (7)
src/features/mentoring/ui/common/mentoring-guide-modal.tsx (1)

21-23: GUIDE_CONTENT.description과 실제 렌더링 내용 사이에 불일치가 있습니다.

Line 23의 description 값이 정의되어 있지만, Lines 82-84에서 해당 항목을 렌더링할 때 item.description을 무시하고 새로운 문자열을 직접 구성하고 있습니다. 이로 인해:

  1. Line 23의 description이 사실상 dead code가 됩니다.
  2. 매직 스트링 '관리 페이지는 어떻게 구분되나요?' 비교는 title 변경 시 조용히 실패할 수 있습니다.
♻️ 제안: GUIDE_CONTENT에 `id` 필드 추가 또는 description을 함수로 변경
+const getManagementPageDescription = (noteEnabled: boolean) => {
+  const noteConsultationLine = noteEnabled
+    ? '- /note-consultation'
+    : '쪽지상담함은 현재 제공하지 않습니다.';
+  return `유저(멘티) 관점 관리: 내가 신청한 멘토링 내역과 후기 작성을 관리합니다.\n- /my-study-review\n\n${noteConsultationLine}\n멘토 운영 관리 화면도 현재 제공하지 않습니다.`;
+};
+
 const GUIDE_CONTENT = [
   {
+    id: 'what-is-mentoring',
     title: '멘토링이 뭔가요?',
     description:
       '멘토와 멘티를 1:1로 연결해 약속을 잡아주는 기능입니다...',
   },
   {
+    id: 'management-page',
     title: '관리 페이지는 어떻게 구분되나요?',
-    description:
-      '유저(멘티) 관점 관리: 내가 신청한 멘토링 내역과 후기 작성을 관리합니다.\n- /my-study-review\n\n쪽지상담함과 멘토 운영 관리 화면은 현재 제공하지 않습니다.',
+    description: null, // dynamic content
   },
   // ...
 ];

렌더링 시:

-{item.title === '관리 페이지는 어떻게 구분되나요?'
-  ? `유저(멘티) 관점 관리:...`
-  : item.description}
+{item.id === 'management-page'
+  ? getManagementPageDescription(isNoteConsultationEnabled)
+  : item.description}

Also applies to: 82-84

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/ui/common/mentoring-guide-modal.tsx` around lines 21 -
23, GUIDE_CONTENT entries currently have a static description that is ignored by
the renderer: the render code builds a hardcoded string and compares title text
instead of using item.description, making the declared descriptions dead code
and fragile to title changes; update GUIDE_CONTENT by either adding a stable id
field to each entry (e.g., id: 'management-pages') or change description to be a
function (e.g., description: () => '...') and then modify the rendering logic in
MentoringGuideModal to use item.description (or call item.description()) and to
switch on item.id instead of comparing the title string, removing the
magic-string comparison and ensuring the displayed text comes from
GUIDE_CONTENT.
src/types/mentoring/registration-options.ts (1)

34-35: 호환용 별칭도 interface로 맞춰주세요.

같은 객체 shape를 다시 노출하는 용도라면 type 별칭보다 interface extends ...가 저장소 규칙과 더 잘 맞습니다.

♻️ 제안 코드
-export type MentorRegistrationCoreKeywordOption =
-  MentorRegistrationSelectableCoreKeywordOption;
+export interface MentorRegistrationCoreKeywordOption
+  extends MentorRegistrationSelectableCoreKeywordOption {}

As per coding guidelines: "Use interface for defining object shapes in TypeScript, not type aliases, when representing object structures."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/types/mentoring/registration-options.ts` around lines 34 - 35, Change the
exported alias MentorRegistrationCoreKeywordOption from a type alias to an
interface that extends MentorRegistrationSelectableCoreKeywordOption so the
compatibility alias uses interface syntax; locate the declaration of
MentorRegistrationCoreKeywordOption and replace the "export type
MentorRegistrationCoreKeywordOption =
MentorRegistrationSelectableCoreKeywordOption" with an "export interface
MentorRegistrationCoreKeywordOption extends
MentorRegistrationSelectableCoreKeywordOption" declaration to match the
repository's object-shape convention.
src/features/mentoring/model/use-mentor-registration-controller.ts (1)

920-928: 드래프트 persistence 디바운스 타이머가 매우 짧습니다.

180ms 디바운스는 빠른 타이핑 시 sessionStorage에 과도한 쓰기를 유발할 수 있습니다. 일반적으로 300-500ms가 더 적절합니다. 현재 값이 의도적이라면 무시해도 됩니다.

♻️ 디바운스 시간 조정 제안
           persistTimer = setTimeout(() => {
             persistDraft();
-          }, 180);
+          }, 300);
         },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/model/use-mentor-registration-controller.ts` around
lines 920 - 928, The debounce timeout used to persist draft is too short (180ms)
and can cause excessive sessionStorage writes; update the timer in the persist
logic where persistTimer and persistDraft are used (the setTimeout(...) block)
to a more reasonable value such as 300–500ms (e.g., replace 180 with 300 or 500)
so quick typing batches into fewer writes, and ensure existing
clearTimeout(persistTimer) logic remains unchanged.
src/features/mentoring/api/mentor-api.types.ts (1)

367-371: MentorCoreKeywordRequestDto에서 code와 label이 모두 optional입니다.

타입에 따라 code (PREDEFINED) 또는 label (CUSTOM) 중 하나는 필수여야 하지만, 현재 타입 정의는 둘 다 optional로 되어 있어 런타임에 잘못된 요청이 생성될 수 있습니다. discriminated union을 사용하면 타입 안전성이 향상됩니다.

♻️ Discriminated union 적용 제안
-export interface MentorCoreKeywordRequestDto {
-  type: 'PREDEFINED' | 'CUSTOM';
-  code?: string;
-  label?: string;
-}
+export type MentorCoreKeywordRequestDto =
+  | { type: 'PREDEFINED'; code: string }
+  | { type: 'CUSTOM'; label: string };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/api/mentor-api.types.ts` around lines 367 - 371,
MentorCoreKeywordRequestDto currently has both code and label optional which
allows invalid combos; change it to a discriminated union by replacing
MentorCoreKeywordRequestDto with two specific interfaces (e.g.,
MentorCoreKeywordPredefined and MentorCoreKeywordCustom) where type:
'PREDEFINED' requires code and type: 'CUSTOM' requires label, then export
MentorCoreKeywordRequestDto = MentorCoreKeywordPredefined |
MentorCoreKeywordCustom so TypeScript enforces the correct required field based
on the type discriminator.
src/features/mentoring/api/mentor-core-keyword-contract.ts (1)

202-222: flatMap 사용이 불필요하게 복잡합니다.

flatMap을 사용하여 단일 요소 배열을 반환하는 것보다 map을 직접 사용하는 것이 더 명확합니다. 현재 로직은 항상 정확히 하나의 요소를 반환하므로 flatMap의 이점이 없습니다.

♻️ map 사용으로 단순화
-  return normalizedProfileKeywords.flatMap<MentorCoreKeywordRequestDto>(
-    (profileKeyword) => {
+  return normalizedProfileKeywords.map<MentorCoreKeywordRequestDto>(
+    (profileKeyword): MentorCoreKeywordRequestDto => {
       const predefinedCode = predefinedCodeMap.get(
         profileKeyword.trim().toLowerCase(),
       );

-      return predefinedCode
-        ? [
-            {
-              type: 'PREDEFINED',
-              code: predefinedCode,
-            },
-          ]
-        : [
-            {
-              type: 'CUSTOM',
-              label: profileKeyword,
-            },
-          ];
+      return predefinedCode
+        ? { type: 'PREDEFINED', code: predefinedCode }
+        : { type: 'CUSTOM', label: profileKeyword };
     },
   );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/api/mentor-core-keyword-contract.ts` around lines 202
- 222, The use of normalizedProfileKeywords.flatMap(...) is unnecessary because
each input always maps to exactly one MentorCoreKeywordRequestDto; replace the
flatMap with normalizedProfileKeywords.map(...) and return a single object
(either { type: 'PREDEFINED', code: predefinedCode } or { type: 'CUSTOM', label:
profileKeyword }) instead of an array, keeping the existing lookup of
predefinedCodeMap and trimming/lowercasing logic intact.
src/features/mentoring/ui/registration/mentor-registration-form.tsx (1)

1850-1857: 스케줄 필요 여부와 관계없이 draft 에러가 표시될 수 있습니다.

Line 1850의 조건 (needsSchedule || !isScheduleDraftValid)needsSchedule이 false이고 draft가 invalid할 때도 에러를 표시합니다. 그러나 scheduleDraftMessages는 Line 1127에서 needsSchedule이 false면 빈 배열이 됩니다. 따라서 실제로는 문제가 없지만, 조건이 약간 혼란스럽습니다.

♻️ 조건 단순화 제안
-            {(needsSchedule || !isScheduleDraftValid) && (
+            {scheduleDraftMessages.length > 0 && (
               <FieldErrorText
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/ui/registration/mentor-registration-form.tsx` around
lines 1850 - 1857, The conditional rendering for the schedule error is confusing
because scheduleDraftMessages is empty when needsSchedule is false; change the
condition so the error is only shown when a schedule is required and the draft
is invalid by replacing the existing condition `(needsSchedule ||
!isScheduleDraftValid)` with a check that both require a schedule and the draft
is invalid (i.e., `needsSchedule && !isScheduleDraftValid`) where FieldErrorText
is rendered using scheduleDraftMessages[0] ?? errors.schedule?.message; this
keeps behavior correct and avoids showing draft errors when schedules are not
required.
src/features/mentoring/ui/registration/mentor-registration-page-view.tsx (1)

363-376: 모달 title/description이 undefined일 수 있습니다.

welcomeOnboarding이 truthy일 때만 모달이 열리지만, TypeScript 관점에서 welcomeOnboarding?.titlewelcomeOnboarding?.description이 undefined를 반환할 수 있습니다. 현재 ?? '' fallback으로 처리되어 있어 기능적으로는 문제없지만, 모달이 열린 상태에서 빈 제목이 표시될 수 있습니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/ui/registration/mentor-registration-page-view.tsx`
around lines 363 - 376, The modal can open when welcomeOnboarding is truthy but
its title/description may still be undefined, causing an empty header; update
the open condition to only open when required fields exist (e.g., change
Modal.Root open to Boolean(welcomeOnboarding && welcomeOnboarding.title &&
welcomeOnboarding.description)) and/or provide explicit meaningful fallbacks
when rendering Modal.Title and the description (replace welcomeOnboarding?.title
?? '' and welcomeOnboarding?.description ?? '' with either non-null asserted
values from welcomeOnboarding (welcomeOnboarding!.title) after the stronger
guard or with explicit strings like '환영합니다'/'설명이 없습니다' to ensure the modal never
shows an empty header). Ensure references: welcomeOnboarding, Modal.Root,
Modal.Title, and Modal.Content are updated accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/features/admin/mentoring/ui/mentor-applications-page-client.tsx`:
- Line 292: The hardcoded text "서버 정산 readiness 계약 필요" replaced the dynamic
display for the "정산 정보 인증" field, removing the actual completed/incomplete
status; in MentorApplicationsPageClient
(src/features/admin/mentoring/ui/mentor-applications-page-client.tsx) restore
conditional rendering: read the settlement / payment verification status from
the application object (e.g., the property used previously like
settlementVerified, payAuthStatus, or similar) and render a completed/incomplete
label based on that value, falling back to the contractual message only when the
status is undefined or null; update the JSX for the "정산 정보 인증" label to use this
conditional logic instead of the static string.

In `@src/features/mentoring/model/mentor-profile-utils.ts`:
- Around line 145-147: Replace the raw pass-through of skillTags with the same
normalization used for interviewQuestions: call toStringArray(source?.skillTags)
(and ensure it falls back to an empty array if that returns falsy) so
MentorSettings receives only normalized string entries; update the object
property in mentor-profile-utils.ts where interviewQuestions, preNotice,
skillTags are set to use toStringArray for skillTags instead of
source?.skillTags.

In `@src/features/mentoring/model/mentor-registration-preview.ts`:
- Around line 286-294: The preview highlight logic is missing company visibility
changes: update the conditional that currently checks prev.mentoringTitle,
prev.appealLine, prev.jobGroup, prev.jobTitle, prev.careerYears,
isSameCareerEntries(...), and prev.listVisible to also compare prev.companyName
!== next.companyName and prev.hideCompanyName !== next.hideCompanyName so that
changes to company name or the hideCompanyName flag trigger the same "headline"
preview update; locate the condition using prev/...next and isSameCareerEntries
to add these two comparisons.

In `@src/features/mentoring/model/use-mentor-registration-preview-panel.ts`:
- Around line 222-277: The pointer event cleanup in onPreviewResizeStart is
incomplete: only handlePointerUp removes listeners and commits state, leaving
pointermove/pointerup handlers and isResizing set if a pointercancel occurs or
the component unmounts. Fix by adding a handlePointerCancel (and/or a shared
cleanup function) that mirrors handlePointerUp's teardown (release pointer
capture via event.currentTarget.releasePointerCapture(event.pointerId) or
document-level equivalent, setIsResizing(false), commit refs via
setCommittedFormOverflowWidth/setCommittedPanelWidth/setCommittedPanelOverflowWidth)
and removes all three listeners, register
document.addEventListener('pointercancel', handlePointerCancel) alongside
pointermove/pointerup, and ensure both handlePointerUp and handlePointerCancel
call the same cleanup function so listeners are always removed and state
committed.

In `@src/features/mentoring/ui/apply/mentoring-apply-route-client.tsx`:
- Around line 80-87: The component currently always returns
MentorRouteUnavailableState (with MENTOR_APPLY_UNSUPPORTED_MESSAGE and
ctaHref={`/mentoring/${mentor.id}`}), which blocks the real apply UI even when
isApplicationReady is true; change the control flow in the
mentoring-apply-route-client component so that when isApplicationReady is true
you render the actual application UI (the application form/submit flow) instead
of returning MentorRouteUnavailableState, and only return
MentorRouteUnavailableState when isApplicationReady is false (keep the existing
message/cta for the unavailable branch).

In `@src/features/mentoring/ui/detail/mentor-detail-page.tsx`:
- Around line 122-133: Compute a single source-of-truth "activeMethod" and use
it for both state update and rendering: derive activeMethod =
enabledMethods.includes(selectedMethod) ? selectedMethod : (enabledMethods[0] ??
'note'), then update the effect to call setSelectedMethod(activeMethod) only
when it differs, and replace selectedOption to use mentor.methods[activeMethod]
(instead of mentor.methods[selectedMethod]) so the UI immediately reflects the
enabled fallback.

In `@src/features/mentoring/ui/mentor-directory/mentor-card.tsx`:
- Line 158: Replace the Tailwind arbitrary values used in the mentor card UI:
change the div with className "mb-125 min-h-[56px]" to use a project design
token or custom utility instead of min-h-[56px], and likewise replace the other
occurrence of "max-w-[130px]" with the appropriate custom token/utility (e.g.,
an existing min-h- or max-w- token such as min-h-14/max-w-sm or your project's
equivalent). Update the className strings for the elements that currently
include "min-h-[56px]" and "max-w-[130px]" to reference the approved design
tokens/utilities so no arbitrary bracketed values remain.

In `@src/features/mentoring/ui/registration/mentor-career-entries-editor.tsx`:
- Line 376: The grid uses Tailwind arbitrary value
"grid-cols-[minmax(0,1fr)_120px]" (seen in the MentorCareerEntriesEditor
component's className) which violates design-token rules; fix by adding a
token-based utility CSS class (e.g., .grid-cols-main-fixed) in the global styles
that sets grid-template-columns: minmax(0,1fr) var(--size-120) (use the
project’s existing design token variable name instead of --size-120), then
replace both occurrences of "grid-cols-[minmax(0,1fr)_120px]" with
"grid-cols-main-fixed" (keeping the surrounding "grid" and gap classes) so the
layout uses a reusable token-based class rather than an arbitrary Tailwind
value.

In `@src/types/schemas/mentor-registration-schema.ts`:
- Around line 293-307: The skillTags array schema allows duplicate tags, causing
duplicate-key warnings and repeated UI tags; update the skillTags Zod schema
(symbol: skillTags) to enforce uniqueness by adding a .refine that compares the
array length to the Set of trimmed values (so "a" and " a " count as same) and
returns a clear error message (e.g., '핵심 키워드는 중복될 수 없습니다.'). Keep the existing
string trim/min/max checks (symbols: MENTOR_SKILL_TAG_MIN_LENGTH,
MENTOR_SKILL_TAG_MAX_LENGTH) and apply the uniqueness .refine after the array
validation so duplicates are rejected at schema-validation time.
- Around line 140-194: The current superRefine block allows entries with
periodEnabled=true where startMonth is provided but endMonth is empty and
isCurrent=false to pass validation; add a check inside the same superRefine that
when entry.periodEnabled is true and hasStartMonth (entry.startMonth.length>0)
and !hasEndMonth (entry.endMonth.length===0) and entry.isCurrent===false, call
ctx.addIssue with code z.ZodIssueCode.custom, path ['endMonth'], and a message
like '종료 기간을 입력해주세요.' so incomplete ranges are rejected before saving.

---

Outside diff comments:
In `@src/features/admin/mentoring/model/mock-seed.ts`:
- Around line 60-65: The mock seed currently uses
createEmptyMentorScheduleDrafts() for scheduleDrafts; update it to mirror server
behavior by importing createMentorScheduleTextDrafts from
src/features/mentoring/model/mentor-settings.ts and replace the value with
createMentorScheduleTextDrafts(schedule) inside
createServerLikeMentorRegistrationValues so scheduleDrafts is derived from the
schedule object (the variable named schedule) instead of being empty.

In `@src/utils/error-handler.ts`:
- Around line 500-531: The current code sends the raw detail (from
extractApiErrorDetail and stored in variable detail) to both console.error and
Sentry (inside Sentry.withScope via scope.setExtra), which may include sensitive
user input; replace usage of the raw detail with a sanitized or whitelisted
summary before logging and reporting: implement or call a sanitizer (e.g.,
sanitizeApiErrorDetail(detail) or adjust extractApiErrorDetail to return only
safe keys like error type, validation keys, and truncated/hashed values) and use
that sanitizedDetail in the development console output and in Sentry.withScope
(replace scope.setExtra('detail', detail) with scope.setExtra('detail',
sanitizedDetail)); ensure errorInfo, stack, and other non-sensitive fields
remain unchanged.

---

Duplicate comments:
In `@src/features/mentoring/model/mentor-registration-server-error.ts`:
- Around line 167-191: The logic that maps server paramName to the pricing/time
step misses bracketed properties like "methods[0].enabled": update the
methodMatch detection to capture property names beyond "price" and
"durationMinutes" (e.g., include "enabled") or make the regex/property check
generic for any property after methods[...] (change
/^methods(?:\[(\d+)\]|\.(\d+))(?:\.(price|durationMinutes))?$/ to allow other
property names), update the methodField union/handling in the methodMatch branch
(methodField can be 'price'|'durationMinutes'|'enabled'|undefined) and ensure
getMethodFieldPath is only called for fields that need a fieldPath (leave
fieldPath undefined for "enabled"), and also expand the fallback condition to
treat "methods[" prefix the same as "methods." so
paramName.startsWith('methods[') routes to
MENTOR_REGISTRATION_STEP_IDS.pricingAndTime.

In `@src/features/mentoring/ui/detail/mentor-detail-header-section.tsx`:
- Around line 77-81: The header component in mentor-detail-header-section.tsx
contains forbidden Tailwind arbitrary values: replace the sm:text-[30px] on the
<h1> and the xl:grid-cols-[minmax(0,0.9fr)_minmax(0,1.1fr)] on the surrounding
<div> with the project's design-token/utility classes (use the same token
names/pattern used elsewhere in the detail UI), e.g., swap sm:text-[30px] for
the corresponding responsive text token utility and swap the xl:grid-cols[...]
for the predefined grid-column token or utility that expresses the 0.9/1.1
split; update className strings in the MentorDetailHeaderSection (or the
component that renders the shown <h1> and <div>) to use those token utilities
instead of arbitrary values.

In `@src/features/mentoring/ui/detail/mentor-detail-sidebar-cta.tsx`:
- Around line 221-239: Replace the forbidden arbitrary Tailwind value mt-[2px]
on the two Info components with a sanctioned spacing token or utility and/or
adjust layout alignment; locate the two Info usages (the one inside the
!isApplicationReady conditional and the one above
acceptancePolicy.title/description) and remove mt-[2px], then use an approved
class such as a project spacing token or a standard Tailwind utility (e.g., mt-2
or items-center on the parent container) to achieve the same vertical alignment
without arbitrary values.

In `@src/features/mentoring/ui/settings/settlement-register-modal.tsx`:
- Around line 108-118: The catch block for the submission flow currently
swallows errors; update the catch to accept the error, run analyzeError(error)
and set the component's inline error state (e.g., errorMessage) with that result
so the modal shows the failure reason while keeping the modal open for retry;
keep the existing caller toast behavior but ensure you call analyzeError inside
the catch and assign its output to the error property used by the inline error
slot (referencing onSubmit, draft, closeModal, analyzeError and the inline
errorMessage state).
- Line 125: The Modal.Content currently uses a forbidden Tailwind arbitrary
value class "max-w-[720px]"; replace that class with the project's approved
width token/utility (i.e., remove "max-w-[720px]" from the className on
Modal.Content in SettlementRegisterModal and swap in the corresponding
design-system token class such as the project's container/width utility like
"max-w-container" or the appropriate "max-w-<design-token>" used elsewhere in
the codebase) so that no arbitrary Tailwind values remain.

---

Nitpick comments:
In `@src/features/mentoring/api/mentor-api.types.ts`:
- Around line 367-371: MentorCoreKeywordRequestDto currently has both code and
label optional which allows invalid combos; change it to a discriminated union
by replacing MentorCoreKeywordRequestDto with two specific interfaces (e.g.,
MentorCoreKeywordPredefined and MentorCoreKeywordCustom) where type:
'PREDEFINED' requires code and type: 'CUSTOM' requires label, then export
MentorCoreKeywordRequestDto = MentorCoreKeywordPredefined |
MentorCoreKeywordCustom so TypeScript enforces the correct required field based
on the type discriminator.

In `@src/features/mentoring/api/mentor-core-keyword-contract.ts`:
- Around line 202-222: The use of normalizedProfileKeywords.flatMap(...) is
unnecessary because each input always maps to exactly one
MentorCoreKeywordRequestDto; replace the flatMap with
normalizedProfileKeywords.map(...) and return a single object (either { type:
'PREDEFINED', code: predefinedCode } or { type: 'CUSTOM', label: profileKeyword
}) instead of an array, keeping the existing lookup of predefinedCodeMap and
trimming/lowercasing logic intact.

In `@src/features/mentoring/model/use-mentor-registration-controller.ts`:
- Around line 920-928: The debounce timeout used to persist draft is too short
(180ms) and can cause excessive sessionStorage writes; update the timer in the
persist logic where persistTimer and persistDraft are used (the setTimeout(...)
block) to a more reasonable value such as 300–500ms (e.g., replace 180 with 300
or 500) so quick typing batches into fewer writes, and ensure existing
clearTimeout(persistTimer) logic remains unchanged.

In `@src/features/mentoring/ui/common/mentoring-guide-modal.tsx`:
- Around line 21-23: GUIDE_CONTENT entries currently have a static description
that is ignored by the renderer: the render code builds a hardcoded string and
compares title text instead of using item.description, making the declared
descriptions dead code and fragile to title changes; update GUIDE_CONTENT by
either adding a stable id field to each entry (e.g., id: 'management-pages') or
change description to be a function (e.g., description: () => '...') and then
modify the rendering logic in MentoringGuideModal to use item.description (or
call item.description()) and to switch on item.id instead of comparing the title
string, removing the magic-string comparison and ensuring the displayed text
comes from GUIDE_CONTENT.

In `@src/features/mentoring/ui/registration/mentor-registration-form.tsx`:
- Around line 1850-1857: The conditional rendering for the schedule error is
confusing because scheduleDraftMessages is empty when needsSchedule is false;
change the condition so the error is only shown when a schedule is required and
the draft is invalid by replacing the existing condition `(needsSchedule ||
!isScheduleDraftValid)` with a check that both require a schedule and the draft
is invalid (i.e., `needsSchedule && !isScheduleDraftValid`) where FieldErrorText
is rendered using scheduleDraftMessages[0] ?? errors.schedule?.message; this
keeps behavior correct and avoids showing draft errors when schedules are not
required.

In `@src/features/mentoring/ui/registration/mentor-registration-page-view.tsx`:
- Around line 363-376: The modal can open when welcomeOnboarding is truthy but
its title/description may still be undefined, causing an empty header; update
the open condition to only open when required fields exist (e.g., change
Modal.Root open to Boolean(welcomeOnboarding && welcomeOnboarding.title &&
welcomeOnboarding.description)) and/or provide explicit meaningful fallbacks
when rendering Modal.Title and the description (replace welcomeOnboarding?.title
?? '' and welcomeOnboarding?.description ?? '' with either non-null asserted
values from welcomeOnboarding (welcomeOnboarding!.title) after the stronger
guard or with explicit strings like '환영합니다'/'설명이 없습니다' to ensure the modal never
shows an empty header). Ensure references: welcomeOnboarding, Modal.Root,
Modal.Title, and Modal.Content are updated accordingly.

In `@src/types/mentoring/registration-options.ts`:
- Around line 34-35: Change the exported alias
MentorRegistrationCoreKeywordOption from a type alias to an interface that
extends MentorRegistrationSelectableCoreKeywordOption so the compatibility alias
uses interface syntax; locate the declaration of
MentorRegistrationCoreKeywordOption and replace the "export type
MentorRegistrationCoreKeywordOption =
MentorRegistrationSelectableCoreKeywordOption" with an "export interface
MentorRegistrationCoreKeywordOption extends
MentorRegistrationSelectableCoreKeywordOption" declaration to match the
repository's object-shape convention.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2e27ceda-54db-4ebb-b13c-0dd7f304fb9d

📥 Commits

Reviewing files that changed from the base of the PR and between d25a41a and a4312d2.

📒 Files selected for processing (76)
  • src/api/client/api-error.ts
  • src/api/openapi/models/mentor-settings-upsert-request.ts
  • src/app/(admin)/admin/mentoring/layout.tsx
  • src/app/(service)/(my)/note-consultation/page.tsx
  • src/app/global.css
  • src/components/common/modals/study-completion-modal.tsx
  • src/components/common/ui/badge/index.tsx
  • src/components/common/ui/dropdown/single.tsx
  • src/components/common/ui/form/multi-item-selector.tsx
  • src/components/common/ui/text-action-button.tsx
  • src/components/my-page/profile.tsx
  • src/features/admin/mentoring/model/mock-seed.ts
  • src/features/admin/mentoring/model/use-admin-mentoring-overview-query.ts
  • src/features/admin/mentoring/ui/mentor-applications-page-client.tsx
  • src/features/admin/mentoring/ui/mentor-operations-page-client.tsx
  • src/features/admin/mentoring/ui/mentor-registration-detail.tsx
  • src/features/admin/mentoring/ui/mentoring-dashboard-page-client.tsx
  • src/features/admin/mentoring/ui/mentoring-flow-guide.tsx
  • src/features/mentoring/api/mentor-api.ts
  • src/features/mentoring/api/mentor-api.types.ts
  • src/features/mentoring/api/mentor-core-keyword-contract.ts
  • src/features/mentoring/api/mentor-profile.mapper.ts
  • src/features/mentoring/api/mentor-settings.mapper.ts
  • src/features/mentoring/const/mentor-registration-labels.ts
  • src/features/mentoring/const/mentoring-list-labels.ts
  • src/features/mentoring/model/mentor-profile-utils.ts
  • src/features/mentoring/model/mentor-public-readiness.ts
  • src/features/mentoring/model/mentor-registration-draft-storage.ts
  • src/features/mentoring/model/mentor-registration-entry-onboarding.ts
  • src/features/mentoring/model/mentor-registration-keywords.ts
  • src/features/mentoring/model/mentor-registration-preview.ts
  • src/features/mentoring/model/mentor-registration-server-error.ts
  • src/features/mentoring/model/mentor-settings.ts
  • src/features/mentoring/model/mentoring-feature-flag.ts
  • src/features/mentoring/model/use-floating-panel-scroll-nudge.ts
  • src/features/mentoring/model/use-mentor-registration-controller.ts
  • src/features/mentoring/model/use-mentor-registration-preview-model.ts
  • src/features/mentoring/model/use-mentor-registration-preview-panel.ts
  • src/features/mentoring/model/use-upsert-my-mentor-settings-mutation.ts
  • src/features/mentoring/ui/apply/mentoring-apply-route-client.tsx
  • src/features/mentoring/ui/common/mentor-profile-meta-list.tsx
  • src/features/mentoring/ui/common/mentoring-guide-modal.tsx
  • src/features/mentoring/ui/detail/mentor-career-history-panel.tsx
  • src/features/mentoring/ui/detail/mentor-detail-header-section.tsx
  • src/features/mentoring/ui/detail/mentor-detail-method-section.tsx
  • src/features/mentoring/ui/detail/mentor-detail-page.tsx
  • src/features/mentoring/ui/detail/mentor-detail-review-section.tsx
  • src/features/mentoring/ui/detail/mentor-detail-sidebar-cta.tsx
  • src/features/mentoring/ui/detail/mentor-route-fallback.tsx
  • src/features/mentoring/ui/detail/review-stars.tsx
  • src/features/mentoring/ui/mentor-directory/mentor-card.tsx
  • src/features/mentoring/ui/mentor-directory/mentor-directory-filters.tsx
  • src/features/mentoring/ui/pages/my-mentoring-detail-page.tsx
  • src/features/mentoring/ui/pages/my-mentoring-page.tsx
  • src/features/mentoring/ui/registration/mentor-career-entries-editor.tsx
  • src/features/mentoring/ui/registration/mentor-markdown-content.tsx
  • src/features/mentoring/ui/registration/mentor-markdown-editor.tsx
  • src/features/mentoring/ui/registration/mentor-registration-form.tsx
  • src/features/mentoring/ui/registration/mentor-registration-header.tsx
  • src/features/mentoring/ui/registration/mentor-registration-page-view.tsx
  • src/features/mentoring/ui/settings/settlement-register-modal.tsx
  • src/features/mentoring/ui/settings/weekly-schedule-grid.tsx
  • src/features/mentoring/ui/temp/contact-seed-actions.tsx
  • src/hooks/queries/settlement-account-api.ts
  • src/hooks/queries/use-auth-mutation.ts
  • src/hooks/queries/use-phone-verification-status.ts
  • src/hooks/queries/use-update-user-profile-mutation.ts
  • src/stores/useMentorDirectoryStore.ts
  • src/types/mentoring/domain.ts
  • src/types/mentoring/markdown.ts
  • src/types/mentoring/registration-options.ts
  • src/types/mentoring/registration-view.ts
  • src/types/mentoring/settings.ts
  • src/types/schemas/mentor-registration-schema.ts
  • src/types/schemas/mentoring-admin-schema.ts
  • src/utils/error-handler.ts
✅ Files skipped from review due to trivial changes (18)
  • src/features/mentoring/ui/detail/mentor-detail-method-section.tsx
  • src/features/admin/mentoring/ui/mentor-operations-page-client.tsx
  • src/app/(admin)/admin/mentoring/layout.tsx
  • src/components/common/modals/study-completion-modal.tsx
  • src/features/mentoring/ui/registration/mentor-registration-header.tsx
  • src/features/mentoring/ui/pages/my-mentoring-page.tsx
  • src/components/common/ui/badge/index.tsx
  • src/features/mentoring/model/mentoring-feature-flag.ts
  • src/features/admin/mentoring/ui/mentoring-flow-guide.tsx
  • src/types/mentoring/domain.ts
  • src/components/common/ui/dropdown/single.tsx
  • src/features/mentoring/const/mentoring-list-labels.ts
  • src/features/mentoring/model/mentor-registration-draft-storage.ts
  • src/features/mentoring/ui/mentor-directory/mentor-directory-filters.tsx
  • src/features/admin/mentoring/ui/mentoring-dashboard-page-client.tsx
  • src/stores/useMentorDirectoryStore.ts
  • src/features/mentoring/model/mentor-settings.ts
  • src/features/mentoring/model/use-mentor-registration-preview-model.ts
🚧 Files skipped from review as they are similar to previous changes (22)
  • src/hooks/queries/use-auth-mutation.ts
  • src/features/mentoring/ui/detail/review-stars.tsx
  • src/features/mentoring/model/mentor-registration-entry-onboarding.ts
  • src/app/(service)/(my)/note-consultation/page.tsx
  • src/api/client/api-error.ts
  • src/hooks/queries/settlement-account-api.ts
  • src/types/schemas/mentoring-admin-schema.ts
  • src/features/mentoring/model/use-upsert-my-mentor-settings-mutation.ts
  • src/api/openapi/models/mentor-settings-upsert-request.ts
  • src/features/mentoring/const/mentor-registration-labels.ts
  • src/features/mentoring/ui/detail/mentor-route-fallback.tsx
  • src/features/mentoring/ui/common/mentor-profile-meta-list.tsx
  • src/features/mentoring/model/mentor-registration-keywords.ts
  • src/features/mentoring/ui/temp/contact-seed-actions.tsx
  • src/components/my-page/profile.tsx
  • src/features/mentoring/ui/detail/mentor-career-history-panel.tsx
  • src/features/admin/mentoring/ui/mentor-registration-detail.tsx
  • src/features/mentoring/api/mentor-settings.mapper.ts
  • src/features/mentoring/model/mentor-public-readiness.ts
  • src/hooks/queries/use-update-user-profile-mutation.ts
  • src/features/mentoring/model/use-floating-panel-scroll-nudge.ts
  • src/types/mentoring/settings.ts

?.verified
? '완료'
: '미완료'}
서버 정산 readiness 계약 필요

@coderabbitai coderabbitai Bot Mar 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

정산 인증 상태가 고정 문구로 대체되어 심사 판단 정보가 사라집니다.

Line 292에서 상태값을 정적 문자열로 고정하면, 정산 정보 인증 필드가 실제 완료/미완료를 보여주지 못해 기능 회귀가 됩니다. 상태 기반 표시 + 계약 미연동 fallback으로 유지하는 편이 안전합니다.

제안 수정안
-                    서버 정산 readiness 계약 필요
+                    {selectedMentor.mentor.mentorSettings?.settlementDraft
+                      ?.verified === true
+                      ? '완료'
+                      : selectedMentor.mentor.mentorSettings?.settlementDraft
+                            ?.verified === false
+                        ? '미완료'
+                        : '정산 readiness 계약 미연동'}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
서버 정산 readiness 계약 필요
{selectedMentor.mentor.mentorSettings?.settlementDraft
?.verified === true
? '완료'
: selectedMentor.mentor.mentorSettings?.settlementDraft
?.verified === false
? '미완료'
: '정산 readiness 계약 미연동'}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/admin/mentoring/ui/mentor-applications-page-client.tsx` at line
292, The hardcoded text "서버 정산 readiness 계약 필요" replaced the dynamic display for
the "정산 정보 인증" field, removing the actual completed/incomplete status; in
MentorApplicationsPageClient
(src/features/admin/mentoring/ui/mentor-applications-page-client.tsx) restore
conditional rendering: read the settlement / payment verification status from
the application object (e.g., the property used previously like
settlementVerified, payAuthStatus, or similar) and render a completed/incomplete
label based on that value, falling back to the contractual message only when the
status is undefined or null; update the JSX for the "정산 정보 인증" label to use this
conditional logic instead of the static string.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

• 아래는 지금 보이는 CodeRabbit actionable 리뷰 10개를 순서대로 번호 붙여서, 리뷰 내용 / 내 평
가 페어로 정리한 겁니다.
전제는 방금 말한 운영 의도대로 반영했습니다.

  • 멘토링 신청은 아직 의도적으로 막힘
  • 정산정보는 아직 개발중
  1. 정산 인증 상태가 고정 문구로 대체되어 회귀라는 리뷰
    리뷰: src/features/admin/mentoring/ui/mentor-applications-page-client.tsx:287
    내 평가: 틀림
    이건 지금 정산 계약이 아직 없다는 제품 전제를 무시한 지적입니다. 문구가 거칠 수는 있어도,
    “완료/미완료를 보여줘야 한다”는 리뷰는 현재 단계 기준으로 성립하지 않습니다.
  2. skillTags만 raw로 통과시켜 정규화 경계를 비껴간다는 리뷰
    리뷰: src/features/mentoring/model/mentor-profile-utils.ts:147
    내 평가: 부분적으로 맞음
    이 줄만 보면 toStringArray를 안 써서 일관성은 떨어집니다. 다만 실제 API 응답은 이미 src/
    features/mentoring/api/mentor-profile.mapper.ts:350, src/features/mentoring/api/mentor-
    settings.mapper.ts:404 에서 먼저 정리하고 들어옵니다. 그래서 “잘못된 응답이 그대로 들어온
    다”는 표현은 과장입니다.
  3. 회사명/비공개 여부 변경이 headline 하이라이트에서 빠졌다는 리뷰
    리뷰: src/features/mentoring/model/mentor-registration-preview.ts:286
    내 평가: 맞음
    실제 헤드라인은 src/features/mentoring/ui/detail/mentor-detail-page.tsx:79 의
    companyLabel에 영향을 받는데, 변경 감지에서는 companyName/hideCompanyName를 빠뜨렸습니다.
    다만 코멘트에 섞인 companyCategory까지는 지금 헤드라인 영향이 없습니다.
  4. preview panel resize의 pointer cleanup이 불완전하다는 리뷰
    리뷰: src/features/mentoring/model/use-mentor-registration-preview-panel.ts:222
    내 평가: 맞음
    현재는 pointerup에서만 리스너를 제거합니다. pointercancel이나 unmount cleanup이 없어 리스
    너/isResizing 상태가 남을 수 있습니다.
  5. /apply 경로가 어떤 상태에서도 실제 신청 화면에 도달하지 않는다는 리뷰
    리뷰: src/features/mentoring/ui/apply/mentoring-apply-route-client.tsx:80
    내 평가: 틀림
    지금 신청 기능은 의도적으로 막혀 있습니다. src/features/mentoring/ui/detail/mentor-
    detail-sidebar-cta.tsx:211 도 같은 전제를 따르고 있어서, 이건 버그 지적이 아니라 제품 정
    책 미반영 리뷰입니다.
  6. selectedOption fallback이 실제로는 동작하지 않는다는 리뷰
    리뷰: src/features/mentoring/ui/detail/mentor-detail-page.tsx:130
    내 평가: 맞음
    mentor.methods는 src/types/mentoring/domain.ts:44 상 모든 key를 갖는 Record라서 ??
    fallback이 사실상 죽어 있습니다. 비활성화 직후 한 렌더 동안 stale 값이 보일 수 있습니다.
  7. mentor-card에 arbitrary value가 새로 들어왔다는 리뷰
    리뷰: src/features/mentoring/ui/mentor-directory/mentor-card.tsx:158, src/features/
    mentoring/ui/mentor-directory/mentor-card.tsx:214
    내 평가: 맞음
    저장소 스타일 규칙 기준으로는 맞습니다. 기능 버그는 아니고 디자인 토큰 규칙 위반입니다.
  8. mentor-career-entries-editor의 grid-cols-[...]가 arbitrary value라는 리뷰
    리뷰: src/features/mentoring/ui/registration/mentor-career-entries-editor.tsx:376, src/
    features/mentoring/ui/registration/mentor-career-entries-editor.tsx:427
    내 평가: 맞음
    이것도 기능 버그보다는 스타일 규칙 위반입니다.
  9. 시작 월만 입력한 이력이 그대로 통과한다는 리뷰
    리뷰: src/types/schemas/mentor-registration-schema.ts:140
    내 평가: 맞음
    핵심은 isCurrent=false인데 startMonth만 있고 endMonth=''인 상태입니다. 재직중이면
    isCurrent=true로 표현해야 하는데, 지금은 그 미완성 상태가 schema를 통과하고 요청 빌더에서
    도 src/features/mentoring/api/mentor-settings.mapper.ts:810 startMonth만 실려 나갈 수 있
    습니다.
  10. skillTags 중복을 schema에서 막아야 한다는 리뷰
    리뷰: src/types/schemas/mentor-registration-schema.ts:293
    내 평가: 부분적으로 맞음
    방어선으로는 좋은 제안입니다. 하지만 현재 폼은 이미 src/features/mentoring/model/mentor-
    registration-keywords.ts:77, src/features/mentoring/ui/registration/mentor-registration-
    form.tsx:1221 에서 중복 제거를 하고 있어 must-fix 급이라고 보긴 어렵습니다.

한 줄로 압축하면:

  • 맞음: 3, 4, 6, 7, 8, 9
  • 부분적으로 맞음: 2, 10
  • 틀림: 1, 5

작업완료

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Hyeonjun0527, 상세한 평가 공유해 주셔서 감사합니다. 이 코멘트(#1)에 대해서는 제가 제품 단계 전제를 고려하지 않은 지적이었음을 인정합니다 — 정산 계약이 아직 미연동 상태라면 settlementDraft?.verified 기반의 완료/미완료 표시는 현 시점에서 성립하지 않습니다. "서버 정산 readiness 계약 필요" 문구는 내부 관리자 화면에서 현재 상태를 명시적으로 알리는 의도된 표현으로 이해했습니다.

나머지 평가 요약도 잘 확인했습니다:

  • 맞음 (3, 4, 6, 7, 8, 9): 해당 코멘트들이 타당한 지적이라고 평가해 주셨으므로 각 스레드에서 별도로 확인하겠습니다.
  • 부분적으로 맞음 (2, 10): upstream mapper에서 이미 정규화가 이루어지고 있다는 점, 그리고 폼 레이어에서 이미 중복 제거가 된다는 점을 감안한 평가로 이해했습니다.
  • 틀림 (5): /apply 경로 차단은 의도된 제품 정책임을 확인했습니다.

✏️ Learnings added
Learnt from: Hyeonjun0527
Repo: code-zero-to-one/study-platform-client PR: 439
File: src/features/admin/mentoring/ui/mentor-applications-page-client.tsx:292-292
Timestamp: 2026-03-23T14:42:50.316Z
Learning: In `code-zero-to-one/study-platform-client`, the settlement (정산) contract/API integration is intentionally not yet implemented. The `settlementDraft` field and its `verified` status on `MentorSettings` should not be treated as a source of truth for settlement readiness. The admin UI in `src/features/admin/mentoring/ui/mentor-applications-page-client.tsx` intentionally shows a placeholder message ("서버 정산 readiness 계약 필요") for the settlement verification field until the server-side settlement contract is established. Do not flag this as a regression.

Learnt from: Hyeonjun0527
Repo: code-zero-to-one/study-platform-client PR: 439
File: src/features/admin/mentoring/ui/mentor-applications-page-client.tsx:292-292
Timestamp: 2026-03-23T14:42:50.316Z
Learning: In `code-zero-to-one/study-platform-client`, the mentoring apply (`/apply`) flow is intentionally blocked at the product level in the current development stage. `src/features/mentoring/ui/apply/mentoring-apply-route-client.tsx` always renders `MentorRouteUnavailableState` regardless of readiness state, and `src/features/mentoring/ui/detail/mentor-detail-sidebar-cta.tsx` follows the same policy. Do not flag this as a functional regression or bug — it is an intentional product policy.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: HA-SEUNG-JEONG
Repo: code-zero-to-one/study-platform-client PR: 379
File: src/features/study/group/ui/step/step1-group.tsx:61-65
Timestamp: 2026-02-05T02:17:57.573Z
Learning: In the repository `code-zero-to-one/study-platform-client`, there are existing database records where `classification='GROUP_STUDY'` and `type='MENTORING'`. When editing such records in `src/features/study/group/ui/step/step1-group.tsx`, the form must handle the case where the loaded type value is filtered out from the available options.

Learnt from: HA-SEUNG-JEONG
Repo: code-zero-to-one/study-platform-client PR: 415
File: src/components/common/layout/page-container.tsx:14-14
Timestamp: 2026-03-08T13:57:25.733Z
Learning: In this project, treat 'w-7xl' as a valid Tailwind CSS v4 width utility (from the container scale) equivalent to 80rem (1280px). Do not flag 'w-7xl' as non-existent; it is a semantic, preferred replacement for 'w-[1280px]'. Apply this guidance to TSX files across the codebase.

Learnt from: HA-SEUNG-JEONG
Repo: code-zero-to-one/study-platform-client PR: 427
File: src/components/common/ui/avatar-stack.tsx:75-75
Timestamp: 2026-03-16T03:02:43.294Z
Learning: In TSX files, do not flag Tailwind class usages that map to project CSS tokens defined in the global CSS (e.g., --color-gray-200: `#e9eaeb` in src/app/global.css) such as border-gray-200, bg-gray-200, or text-gray-200, since they are intentional custom tokens. Treat these as valid project token usages and avoid false positives. If enforcing, document this as a repository-wide exception and consider an automated check that recognizes token-based class names when a corresponding CSS variable exists in global.css.

Comment thread src/features/mentoring/model/mentor-profile-utils.ts Outdated
Comment thread src/features/mentoring/model/mentor-registration-preview.ts
Comment thread src/features/mentoring/model/use-mentor-registration-preview-panel.ts Outdated
Comment thread src/features/mentoring/ui/apply/mentoring-apply-route-client.tsx
Comment thread src/features/mentoring/ui/detail/mentor-detail-page.tsx
Comment thread src/features/mentoring/ui/mentor-directory/mentor-card.tsx Outdated
Comment thread src/features/mentoring/ui/registration/mentor-career-entries-editor.tsx Outdated
Comment thread src/types/schemas/mentor-registration-schema.ts
Comment thread src/types/schemas/mentor-registration-schema.ts Outdated
commit 7b8ddddae021998b7b54ccf5048d4c9c2a52e077
Author: Hyeonjun0527 <osumaniaddict527@gmail.com>
Date:   Mon Mar 23 21:00:02 2026 +0900

    수정: 멘토링 등록 저장 흐름과 공개 노출 분기를 안정화

    멘토링 등록 화면에서 핵심키워드 정책을 백엔드 계약에 맞게 정리하고 selectable 키워드와 공개 태그를 분리했다. 기존 predefined 키워드 스냅샷 보존, 잘못된 키워드 응답 처리, 커스텀 키워드 길이 13자 제한까지 한 경계에서 맞췄다.

    저장 후에는 항상 안내 모달을 거치도록 통일하고 목록 노출 여부에 따라 상세 또는 멘토링 목록으로 이동하도록 정리했다. 저장 중복 피드백, 목록 비노출 저장 후 404 이동, 미완성 상세 가리기, 프로필 이미지 반영 누락, 에디터와 모달의 세부 UX 회귀도 함께 바로잡았다.

    추가로 AGENTS 규칙에 커밋 메시지는 반드시 한국어로 작성하도록 명시해 이후 커밋 규칙을 문서와 실제 작업이 일치하도록 맞췄다.

commit 0d9657b28e49aed2fce6108b43f264f6ae7ac76d
Author: Hyeonjun0527 <osumaniaddict527@gmail.com>
Date:   Sat Mar 21 19:29:45 2026 +0900

    feat: 멘토링등록

commit 0f42eca344f58b0450450a8782e5d5ac3d6b8b7c
Author: Hyeonjun0527 <osumaniaddict527@gmail.com>
Date:   Fri Mar 20 18:58:15 2026 +0900

    refactor: 멘토링기능리팩토링

fix: 멘토링 등록 저장 경계와 공개 준비 판정을 note-only·legacy 데이터 기준에 맞게 보정

fix: 멘토링 등록 경력 검증과 상세 미리보기 상태 동기화를 보강
@Hyeonjun0527 Hyeonjun0527 force-pushed the refactor/mentoring-registration-sqaush branch from a4312d2 to dd1c6a6 Compare March 23, 2026 14:44

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/features/mentoring/ui/registration/mentor-markdown-editor.tsx (1)

694-694: ⚠️ Potential issue | 🟡 Minor

임의의 Tailwind 값 w-[180px]이 사용되었습니다.

코딩 가이드라인에 따르면 w-[180px]와 같은 임의의 Tailwind 값은 금지됩니다. 프로젝트 디자인 토큰을 사용하세요.

♻️ 디자인 토큰 사용 제안
           <input
             type="range"
             min={MENTOR_MARKDOWN_IMAGE_MIN_WIDTH}
             max={MENTOR_MARKDOWN_IMAGE_MAX_WIDTH}
             step={MENTOR_MARKDOWN_IMAGE_WIDTH_STEP}
             value={selectedImageWidth}
             onChange={(event) => {
               handleImageWidthChange(Number(event.target.value));
             }}
-            className="accent-background-brand-default w-[180px]"
+            className="accent-background-brand-default w-1800"
           />

Note: 프로젝트의 global.css에 정의된 적절한 width 토큰을 확인하여 사용하세요.

As per coding guidelines: "Forbidden: Tailwind arbitrary values (e.g., p-[4px], w-[320px]). Use only project custom design tokens."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/ui/registration/mentor-markdown-editor.tsx` at line
694, The class uses a forbidden Tailwind arbitrary width value "w-[180px]" in
the MentorMarkdownEditor JSX; replace this arbitrary value with the appropriate
project width design token (e.g., a token from global.css like w-tokenName or a
utility such as w-{token}) wherever "className=\"accent-background-brand-default
w-[180px]\"" appears in the MentorMarkdownEditor component so the component uses
the project's custom design token instead of an arbitrary value.
🧹 Nitpick comments (9)
src/hooks/queries/use-auth-mutation.ts (1)

74-75: cleanup 성격 로직은 onSettled로 분리하는 쪽이 더 일관됩니다.

추가된 reset 호출 자체는 맞지만, 현재처럼 성공 콜백에 cleanup을 두면 실패 시 정리 경로가 분기됩니다. 이 블록(reset/clear/removeItem)은 onSettled로 이동하고, onSuccess는 성공 전용 동작(이벤트/라우팅)만 남기는 구성을 권장합니다.

♻️ 제안 diff
   return useMutation({
     mutationFn: logout,
     onSuccess: () => {
       const memberId = getCookie('memberId');
       if (memberId)
         sendGTMEvent({
           event: 'custom_member_logout',
           dl_timestamp: new Date().toISOString(),
           dl_member_id: hashValue(memberId),
         });
-
-      clearClientSession();
-      localStorage.removeItem(VIEWED_ARCHIVES_KEY);
-
-      resetUserStore();
-      resetPhoneVerification();
-      resetMentorDirectory();
-      resetMentoringManagement();
-      resetMentorScreening();
-      resetMentorOperation();
-      queryClient.clear();
-
       router.push('/home');
       router.refresh();
     },
+    onSettled: () => {
+      clearClientSession();
+      localStorage.removeItem(VIEWED_ARCHIVES_KEY);
+
+      resetUserStore();
+      resetPhoneVerification();
+      resetMentorDirectory();
+      resetMentoringManagement();
+      resetMentorScreening();
+      resetMentorOperation();
+      queryClient.clear();
+    },
   });

As per coding guidelines "useMutation hooks는 onSuccess(성공 전용), onError(에러 처리), onSettled(정리 작업)로 역할을 분리"해야 합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/queries/use-auth-mutation.ts` around lines 74 - 75, Move cleanup
calls currently placed in the mutation's onSuccess handler into onSettled so
cleanup always runs regardless of success or failure: locate the useMutation
call in use-auth-mutation.ts and remove resetMentorScreening() and
resetMentorOperation() (and any clear/removeItem logic nearby) from onSuccess,
then add those cleanup calls to the onSettled handler; keep onSuccess limited to
success-only actions (events, routing, analytics) and leave error handling in
onError.
src/types/schemas/mentor-registration-schema.ts (1)

67-123: weeklyScheduleSchema의 반복적인 max 검증을 리팩토링할 수 있습니다.

각 요일별로 동일한 max(SCHEDULE_DAY_MAX_SLOT_COUNT, ...) 검증이 반복됩니다. 헬퍼 함수나 루프를 통해 코드 중복을 줄일 수 있습니다.

♻️ 선택적 리팩토링 제안
const daySlotArraySchema = z
  .array(timeSlotSchema)
  .max(
    SCHEDULE_DAY_MAX_SLOT_COUNT,
    '하루 스케줄은 최대 48개 슬롯까지 입력할 수 있습니다.',
  );

const weeklyScheduleSchema = z
  .object(
    Object.fromEntries(
      WEEKDAY_KEYS.map((day) => [day, daySlotArraySchema]),
    ) as Record<(typeof WEEKDAY_KEYS)[number], typeof daySlotArraySchema>,
  )
  .superRefine(/* ... */);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/types/schemas/mentor-registration-schema.ts` around lines 67 - 123, The
weeklyScheduleSchema repeats the same
.array(timeSlotSchema).max(SCHEDULE_DAY_MAX_SLOT_COUNT, ...) for each weekday;
refactor by extracting that repeated schema into a single reusable variable
(e.g., daySlotArraySchema) and build the object shape dynamically from
WEEKDAY_KEYS (e.g., Object.fromEntries(WEEKDAY_KEYS.map(day => [day,
daySlotArraySchema]))) before applying .superRefine that still checks
duplicates; update references to timeSlotSchema, SCHEDULE_DAY_MAX_SLOT_COUNT,
WEEKDAY_KEYS and keep the existing superRefine logic intact.
src/features/admin/mentoring/ui/mentor-registration-detail.tsx (1)

199-201: 동일 항목에 대해 formatMentorCareerEntryPeriodLabel이 두 번 호출됩니다.

조건부 렌더링과 실제 렌더링에서 같은 entry에 대해 함수가 두 번 호출됩니다. 변수에 저장하여 재사용하면 효율적입니다.

♻️ 변수 캐싱 제안
 {majorHistoryEntries.map((entry, index) => {
+  const periodLabel = formatMentorCareerEntryPeriodLabel(entry);
+
   return (
     <div
       key={`${entry.description}-${index}`}
       className="flex items-start gap-75"
     >
       <span className="font-designer-14r text-text-default">-</span>
       <div className="min-w-0 flex-1">
-        {formatMentorCareerEntryPeriodLabel(entry) && (
+        {periodLabel && (
           <p className="font-designer-12r text-text-subtle mb-25">
-            {formatMentorCareerEntryPeriodLabel(entry)}
+            {periodLabel}
           </p>
         )}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/admin/mentoring/ui/mentor-registration-detail.tsx` around lines
199 - 201, The code calls formatMentorCareerEntryPeriodLabel(entry) twice for
the same entry (once in the conditional and once in the JSX); compute and cache
the result in a local variable (e.g., const periodLabel =
formatMentorCareerEntryPeriodLabel(entry)) inside the scope where entry is used
(such as within the map or component render) and then use periodLabel for both
the conditional and the paragraph content to avoid redundant calls.
src/features/mentoring/ui/settings/weekly-schedule-grid.tsx (1)

128-131: 항등 조건문이 항상 같은 값을 반환합니다.

timezone === 'Asia/Seoul' ? 'Asia/Seoul' : 'Asia/Seoul'slotUnitMinutes === 30 ? 30 : 30는 항상 동일한 값을 반환하므로 조건 분기가 의미 없습니다. 타입 좁히기가 목적이라면 type assertion이나 상수로 대체할 수 있습니다.

♻️ 간소화 제안
 const normalizeWeeklySchedule = (
   value: MentorWeeklySchedule | undefined,
 ): MentorWeeklySchedule => {
   if (!value) {
     return createFallbackSchedule();
   }

   return {
-    timezone: value.timezone === 'Asia/Seoul' ? 'Asia/Seoul' : 'Asia/Seoul',
-    slotUnitMinutes: value.slotUnitMinutes === 30 ? 30 : 30,
+    timezone: 'Asia/Seoul',
+    slotUnitMinutes: 30,
     weekly: createWeekdayRecord((day) => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/ui/settings/weekly-schedule-grid.tsx` around lines 128
- 131, The ternary expressions in the object returned by the function are
redundant—replace timezone: value.timezone === 'Asia/Seoul' ? 'Asia/Seoul' :
'Asia/Seoul' and slotUnitMinutes: value.slotUnitMinutes === 30 ? 30 : 30 with
direct values or appropriate type assertions; e.g., set timezone to the desired
constant (or assert value.timezone as the narrowed type) and set slotUnitMinutes
to 30 (or assert value.slotUnitMinutes as 30) so the returned object (the
timezone and slotUnitMinutes properties used alongside createWeekdayRecord) is
simplified and type-safe.
src/features/mentoring/ui/apply/mentoring-apply-route-client.tsx (1)

10-21: 사용되지 않는 import와 파라미터가 있습니다.

MentoringMethodType import와 selectedType 파라미터가 선언되어 있지만 컴포넌트 본문에서 사용되지 않습니다. 멘토링 신청 기능이 의도적으로 비활성화되어 있으므로 이 코드는 향후 기능 활성화를 위해 남겨둔 것으로 보이지만, 현재는 데드 코드입니다.

🧹 선택적 정리 제안
-import type { MentoringMethodType } from '@/types/mentoring/domain';
 import {
   MentorNotFoundState,
   MentorRouteErrorState,
   MentorRouteLoading,
   MentorRouteUnavailableState,
 } from '../detail/mentor-route-fallback';

 interface MentoringApplyRouteClientProps {
   mentorId: number;
-  selectedType?: MentoringMethodType;
 }

 export default function MentoringApplyRouteClient({
   mentorId,
 }: MentoringApplyRouteClientProps) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/ui/apply/mentoring-apply-route-client.tsx` around
lines 10 - 21, Remove the dead import and unused prop: delete the
MentoringMethodType import and remove the selectedType property from the
MentoringApplyRouteClientProps interface (and from the MentoringApplyRouteClient
component signature if present), leaving only mentorId; ensure there are no
remaining references to selectedType or MentoringMethodType in this file and run
the linter/TS check to confirm no unused-symbol errors remain.
src/app/global.css (1)

429-447: h-650w-650 유틸리티 네이밍이 혼동될 수 있습니다.

h-650w-650이 모두 52px로 정의되어 있는데, 숫자 650이 52와 연관성이 없어 네이밍 컨벤션이 일관되지 않습니다. 다른 spacing 토큰들은 --spacing-650이 52px라면 이해가 되지만, spacing 스케일에 650이 정의되어 있지 않습니다. 향후 유지보수를 위해 h-52 또는 의미 있는 이름(예: h-panel-header)을 고려해 볼 수 있습니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/global.css` around lines 429 - 447, The h-650 and w-650 utilities are
misleading because they set height/width to 52px while the name implies 650;
rename these utilities to reflect the actual value (e.g., h-52 and w-52 or a
semantic name like h-panel-header/w-panel-header) and update all usages
accordingly, or alternatively align the value with the naming by mapping to an
existing spacing token (e.g., --spacing-650) if that token truly represents
52px; update the utility declarations (h-650, w-650) and every reference to them
(search for "h-650" and "w-650") to keep naming consistent with the measured
value or token.
src/features/mentoring/ui/registration/mentor-registration-page-view.tsx (2)

24-26: 상수에 Tailwind 임의 값이 포함되어 있습니다.

PAGE_CONTAINER_CLASSmax-w-[1280px]가 포함되어 있습니다. 프로젝트에서 max-w-page 또는 max-w-7xl 같은 커스텀 토큰을 사용하는 것이 가이드라인에 부합합니다.

♻️ 제안 수정
 const PAGE_CONTAINER_CLASS =
-  'mx-auto w-full max-w-[1280px] px-150 py-400 sm:px-300 xl:px-400 xl:py-500';
+  'mx-auto w-full max-w-page px-150 py-400 sm:px-300 xl:px-400 xl:py-500';

As per coding guidelines: "Forbidden: Tailwind arbitrary values (e.g., p-[4px], w-[320px]). Use only project custom design tokens."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/ui/registration/mentor-registration-page-view.tsx`
around lines 24 - 26, PAGE_CONTAINER_CLASS contains a Tailwind arbitrary value
max-w-[1280px] which violates the project's design-token rule; update
PAGE_CONTAINER_CLASS to use the project's custom max-width token (e.g., replace
max-w-[1280px] with the approved token such as max-w-page or max-w-7xl) and keep
PREVIEW_PANEL_EXTRA_WIDTH unchanged, ensuring the chosen token exists in the
design system and maintaining the rest of the class string.

289-292: min-w-[360px] 임의 값이 사용되었습니다.

Line 291의 min-w-[360px]는 코딩 가이드라인을 위반합니다. 디자인 토큰으로 교체하거나 커스텀 유틸리티로 정의해 주세요.

♻️ 제안 수정
-              <div className="min-w-[360px] [&_a]:pointer-events-none">
+              <div className="min-w-360 [&_a]:pointer-events-none">

또는 global.css--spacing-360: 360px와 같은 토큰을 정의하여 사용하세요.

As per coding guidelines: "Forbidden: Tailwind arbitrary values."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/ui/registration/mentor-registration-page-view.tsx`
around lines 289 - 292, The className uses a Tailwind arbitrary value
"min-w-[360px]" which violates the guideline; replace it by using a design token
or a custom utility instead: define a spacing token (e.g., --spacing-360: 360px)
in global CSS or add a custom Tailwind utility/class (e.g., .min-w-360) in your
Tailwind config, then update the element wrapping MentorDetailPage (the element
rendered when state.isPreviewOpen is true) to use the token/utility instead of
"min-w-[360px]".
src/features/mentoring/ui/detail/mentor-detail-sidebar-cta.tsx (1)

221-231: 금지된 Tailwind 임의 값 mt-[2px]이 사용되었습니다.

Lines 223, 231에서 mt-[2px]가 사용되고 있습니다. 코딩 가이드라인에 따르면 임의 값 대신 프로젝트 디자인 토큰만 사용해야 합니다. mt-25 또는 가장 가까운 토큰으로 교체해 주세요.

♻️ 제안 수정
-              <Info className="text-text-subtlest mt-[2px] h-14 w-14 shrink-0" />
+              <Info className="text-text-subtlest mt-25 h-14 w-14 shrink-0" />
-              <Info className="text-text-subtlest mt-[2px] h-14 w-14 shrink-0" />
+              <Info className="text-text-subtlest mt-25 h-14 w-14 shrink-0" />

As per coding guidelines: "Forbidden: Tailwind arbitrary values (e.g., p-[4px], w-[320px]). Use only project custom design tokens."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/ui/detail/mentor-detail-sidebar-cta.tsx` around lines
221 - 231, The review flags usage of forbidden Tailwind arbitrary value
`mt-[2px]` in the MentorDetailSidebar CTA rendering; locate the two className
strings on the Info component instances in the JSX where `isApplicationReady`
and the subsequent block are rendered and replace `mt-[2px]` with the nearest
approved design token (e.g., `mt-25`) so the classes become compliant with
project design tokens, ensuring both occurrences (the Info above the
applyUnavailableMessage and the second Info) are updated consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/features/mentoring/ui/detail/mentor-detail-header-section.tsx`:
- Around line 76-79: The h1 in mentor-detail-header-section.tsx uses a forbidden
Tailwind arbitrary value "sm:text-[30px]"; replace that class with the project's
design token utility (or add a new utility in global.css) and apply it to the h1
(look for the <h1> element rendering mentoringTitleLabel in
MentorDetailHeaderSection). Either swap "sm:text-[30px]" for the existing token
(e.g., a font-designer-30b-style utility) or define a new utility in global.css
as described and use that class name instead of the arbitrary value.

In `@src/features/mentoring/ui/mentor-directory/mentor-card.tsx`:
- Line 158: The component mentor-card.tsx uses the CSS utility class "min-h-700"
but that utility is not defined in global.css; add an explicit `@utility` entry
for min-h-700 in global.css following the existing pattern (e.g., mirror
min-h-280/min-h-260/min-h-200) so the class sets min-height: 700px, or
alternatively set min-height to var(--spacing-700) if you prefer to reuse the
spacing token; ensure the utility name exactly matches "min-h-700" so the div
className="mb-125 min-h-700" gets the intended height.

---

Outside diff comments:
In `@src/features/mentoring/ui/registration/mentor-markdown-editor.tsx`:
- Line 694: The class uses a forbidden Tailwind arbitrary width value
"w-[180px]" in the MentorMarkdownEditor JSX; replace this arbitrary value with
the appropriate project width design token (e.g., a token from global.css like
w-tokenName or a utility such as w-{token}) wherever
"className=\"accent-background-brand-default w-[180px]\"" appears in the
MentorMarkdownEditor component so the component uses the project's custom design
token instead of an arbitrary value.

---

Nitpick comments:
In `@src/app/global.css`:
- Around line 429-447: The h-650 and w-650 utilities are misleading because they
set height/width to 52px while the name implies 650; rename these utilities to
reflect the actual value (e.g., h-52 and w-52 or a semantic name like
h-panel-header/w-panel-header) and update all usages accordingly, or
alternatively align the value with the naming by mapping to an existing spacing
token (e.g., --spacing-650) if that token truly represents 52px; update the
utility declarations (h-650, w-650) and every reference to them (search for
"h-650" and "w-650") to keep naming consistent with the measured value or token.

In `@src/features/admin/mentoring/ui/mentor-registration-detail.tsx`:
- Around line 199-201: The code calls formatMentorCareerEntryPeriodLabel(entry)
twice for the same entry (once in the conditional and once in the JSX); compute
and cache the result in a local variable (e.g., const periodLabel =
formatMentorCareerEntryPeriodLabel(entry)) inside the scope where entry is used
(such as within the map or component render) and then use periodLabel for both
the conditional and the paragraph content to avoid redundant calls.

In `@src/features/mentoring/ui/apply/mentoring-apply-route-client.tsx`:
- Around line 10-21: Remove the dead import and unused prop: delete the
MentoringMethodType import and remove the selectedType property from the
MentoringApplyRouteClientProps interface (and from the MentoringApplyRouteClient
component signature if present), leaving only mentorId; ensure there are no
remaining references to selectedType or MentoringMethodType in this file and run
the linter/TS check to confirm no unused-symbol errors remain.

In `@src/features/mentoring/ui/detail/mentor-detail-sidebar-cta.tsx`:
- Around line 221-231: The review flags usage of forbidden Tailwind arbitrary
value `mt-[2px]` in the MentorDetailSidebar CTA rendering; locate the two
className strings on the Info component instances in the JSX where
`isApplicationReady` and the subsequent block are rendered and replace
`mt-[2px]` with the nearest approved design token (e.g., `mt-25`) so the classes
become compliant with project design tokens, ensuring both occurrences (the Info
above the applyUnavailableMessage and the second Info) are updated consistently.

In `@src/features/mentoring/ui/registration/mentor-registration-page-view.tsx`:
- Around line 24-26: PAGE_CONTAINER_CLASS contains a Tailwind arbitrary value
max-w-[1280px] which violates the project's design-token rule; update
PAGE_CONTAINER_CLASS to use the project's custom max-width token (e.g., replace
max-w-[1280px] with the approved token such as max-w-page or max-w-7xl) and keep
PREVIEW_PANEL_EXTRA_WIDTH unchanged, ensuring the chosen token exists in the
design system and maintaining the rest of the class string.
- Around line 289-292: The className uses a Tailwind arbitrary value
"min-w-[360px]" which violates the guideline; replace it by using a design token
or a custom utility instead: define a spacing token (e.g., --spacing-360: 360px)
in global CSS or add a custom Tailwind utility/class (e.g., .min-w-360) in your
Tailwind config, then update the element wrapping MentorDetailPage (the element
rendered when state.isPreviewOpen is true) to use the token/utility instead of
"min-w-[360px]".

In `@src/features/mentoring/ui/settings/weekly-schedule-grid.tsx`:
- Around line 128-131: The ternary expressions in the object returned by the
function are redundant—replace timezone: value.timezone === 'Asia/Seoul' ?
'Asia/Seoul' : 'Asia/Seoul' and slotUnitMinutes: value.slotUnitMinutes === 30 ?
30 : 30 with direct values or appropriate type assertions; e.g., set timezone to
the desired constant (or assert value.timezone as the narrowed type) and set
slotUnitMinutes to 30 (or assert value.slotUnitMinutes as 30) so the returned
object (the timezone and slotUnitMinutes properties used alongside
createWeekdayRecord) is simplified and type-safe.

In `@src/hooks/queries/use-auth-mutation.ts`:
- Around line 74-75: Move cleanup calls currently placed in the mutation's
onSuccess handler into onSettled so cleanup always runs regardless of success or
failure: locate the useMutation call in use-auth-mutation.ts and remove
resetMentorScreening() and resetMentorOperation() (and any clear/removeItem
logic nearby) from onSuccess, then add those cleanup calls to the onSettled
handler; keep onSuccess limited to success-only actions (events, routing,
analytics) and leave error handling in onError.

In `@src/types/schemas/mentor-registration-schema.ts`:
- Around line 67-123: The weeklyScheduleSchema repeats the same
.array(timeSlotSchema).max(SCHEDULE_DAY_MAX_SLOT_COUNT, ...) for each weekday;
refactor by extracting that repeated schema into a single reusable variable
(e.g., daySlotArraySchema) and build the object shape dynamically from
WEEKDAY_KEYS (e.g., Object.fromEntries(WEEKDAY_KEYS.map(day => [day,
daySlotArraySchema]))) before applying .superRefine that still checks
duplicates; update references to timeSlotSchema, SCHEDULE_DAY_MAX_SLOT_COUNT,
WEEKDAY_KEYS and keep the existing superRefine logic intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0c03ad72-ef91-45f1-b0f1-a54d23c6ea00

📥 Commits

Reviewing files that changed from the base of the PR and between a4312d2 and dd1c6a6.

📒 Files selected for processing (76)
  • src/api/client/api-error.ts
  • src/api/openapi/models/mentor-settings-upsert-request.ts
  • src/app/(admin)/admin/mentoring/layout.tsx
  • src/app/(service)/(my)/note-consultation/page.tsx
  • src/app/global.css
  • src/components/common/modals/study-completion-modal.tsx
  • src/components/common/ui/badge/index.tsx
  • src/components/common/ui/dropdown/single.tsx
  • src/components/common/ui/form/multi-item-selector.tsx
  • src/components/common/ui/text-action-button.tsx
  • src/components/my-page/profile.tsx
  • src/features/admin/mentoring/model/mock-seed.ts
  • src/features/admin/mentoring/model/use-admin-mentoring-overview-query.ts
  • src/features/admin/mentoring/ui/mentor-applications-page-client.tsx
  • src/features/admin/mentoring/ui/mentor-operations-page-client.tsx
  • src/features/admin/mentoring/ui/mentor-registration-detail.tsx
  • src/features/admin/mentoring/ui/mentoring-dashboard-page-client.tsx
  • src/features/admin/mentoring/ui/mentoring-flow-guide.tsx
  • src/features/mentoring/api/mentor-api.ts
  • src/features/mentoring/api/mentor-api.types.ts
  • src/features/mentoring/api/mentor-core-keyword-contract.ts
  • src/features/mentoring/api/mentor-profile.mapper.ts
  • src/features/mentoring/api/mentor-settings.mapper.ts
  • src/features/mentoring/const/mentor-registration-labels.ts
  • src/features/mentoring/const/mentoring-list-labels.ts
  • src/features/mentoring/model/mentor-profile-utils.ts
  • src/features/mentoring/model/mentor-public-readiness.ts
  • src/features/mentoring/model/mentor-registration-draft-storage.ts
  • src/features/mentoring/model/mentor-registration-entry-onboarding.ts
  • src/features/mentoring/model/mentor-registration-keywords.ts
  • src/features/mentoring/model/mentor-registration-preview.ts
  • src/features/mentoring/model/mentor-registration-server-error.ts
  • src/features/mentoring/model/mentor-settings.ts
  • src/features/mentoring/model/mentoring-feature-flag.ts
  • src/features/mentoring/model/use-floating-panel-scroll-nudge.ts
  • src/features/mentoring/model/use-mentor-registration-controller.ts
  • src/features/mentoring/model/use-mentor-registration-preview-model.ts
  • src/features/mentoring/model/use-mentor-registration-preview-panel.ts
  • src/features/mentoring/model/use-upsert-my-mentor-settings-mutation.ts
  • src/features/mentoring/ui/apply/mentoring-apply-route-client.tsx
  • src/features/mentoring/ui/common/mentor-profile-meta-list.tsx
  • src/features/mentoring/ui/common/mentoring-guide-modal.tsx
  • src/features/mentoring/ui/detail/mentor-career-history-panel.tsx
  • src/features/mentoring/ui/detail/mentor-detail-header-section.tsx
  • src/features/mentoring/ui/detail/mentor-detail-method-section.tsx
  • src/features/mentoring/ui/detail/mentor-detail-page.tsx
  • src/features/mentoring/ui/detail/mentor-detail-review-section.tsx
  • src/features/mentoring/ui/detail/mentor-detail-sidebar-cta.tsx
  • src/features/mentoring/ui/detail/mentor-route-fallback.tsx
  • src/features/mentoring/ui/detail/review-stars.tsx
  • src/features/mentoring/ui/mentor-directory/mentor-card.tsx
  • src/features/mentoring/ui/mentor-directory/mentor-directory-filters.tsx
  • src/features/mentoring/ui/pages/my-mentoring-detail-page.tsx
  • src/features/mentoring/ui/pages/my-mentoring-page.tsx
  • src/features/mentoring/ui/registration/mentor-career-entries-editor.tsx
  • src/features/mentoring/ui/registration/mentor-markdown-content.tsx
  • src/features/mentoring/ui/registration/mentor-markdown-editor.tsx
  • src/features/mentoring/ui/registration/mentor-registration-form.tsx
  • src/features/mentoring/ui/registration/mentor-registration-header.tsx
  • src/features/mentoring/ui/registration/mentor-registration-page-view.tsx
  • src/features/mentoring/ui/settings/settlement-register-modal.tsx
  • src/features/mentoring/ui/settings/weekly-schedule-grid.tsx
  • src/features/mentoring/ui/temp/contact-seed-actions.tsx
  • src/hooks/queries/settlement-account-api.ts
  • src/hooks/queries/use-auth-mutation.ts
  • src/hooks/queries/use-phone-verification-status.ts
  • src/hooks/queries/use-update-user-profile-mutation.ts
  • src/stores/useMentorDirectoryStore.ts
  • src/types/mentoring/domain.ts
  • src/types/mentoring/markdown.ts
  • src/types/mentoring/registration-options.ts
  • src/types/mentoring/registration-view.ts
  • src/types/mentoring/settings.ts
  • src/types/schemas/mentor-registration-schema.ts
  • src/types/schemas/mentoring-admin-schema.ts
  • src/utils/error-handler.ts
✅ Files skipped from review due to trivial changes (18)
  • src/features/admin/mentoring/ui/mentor-operations-page-client.tsx
  • src/components/common/modals/study-completion-modal.tsx
  • src/features/admin/mentoring/ui/mentoring-dashboard-page-client.tsx
  • src/api/client/api-error.ts
  • src/components/common/ui/dropdown/single.tsx
  • src/features/admin/mentoring/ui/mentoring-flow-guide.tsx
  • src/features/mentoring/ui/pages/my-mentoring-page.tsx
  • src/features/mentoring/model/mentor-registration-draft-storage.ts
  • src/features/mentoring/ui/registration/mentor-registration-header.tsx
  • src/types/mentoring/domain.ts
  • src/api/openapi/models/mentor-settings-upsert-request.ts
  • src/hooks/queries/use-phone-verification-status.ts
  • src/features/mentoring/model/mentoring-feature-flag.ts
  • src/components/common/ui/badge/index.tsx
  • src/features/mentoring/ui/common/mentor-profile-meta-list.tsx
  • src/features/mentoring/model/use-floating-panel-scroll-nudge.ts
  • src/features/mentoring/api/mentor-core-keyword-contract.ts
  • src/types/mentoring/settings.ts
🚧 Files skipped from review as they are similar to previous changes (29)
  • src/features/mentoring/ui/detail/mentor-detail-method-section.tsx
  • src/features/mentoring/const/mentoring-list-labels.ts
  • src/app/(service)/(my)/note-consultation/page.tsx
  • src/features/mentoring/ui/detail/review-stars.tsx
  • src/app/(admin)/admin/mentoring/layout.tsx
  • src/features/admin/mentoring/ui/mentor-applications-page-client.tsx
  • src/hooks/queries/use-update-user-profile-mutation.ts
  • src/features/mentoring/ui/mentor-directory/mentor-directory-filters.tsx
  • src/types/schemas/mentoring-admin-schema.ts
  • src/utils/error-handler.ts
  • src/features/mentoring/ui/common/mentoring-guide-modal.tsx
  • src/features/mentoring/model/use-upsert-my-mentor-settings-mutation.ts
  • src/features/mentoring/ui/pages/my-mentoring-detail-page.tsx
  • src/hooks/queries/settlement-account-api.ts
  • src/features/mentoring/ui/detail/mentor-route-fallback.tsx
  • src/features/mentoring/ui/detail/mentor-detail-review-section.tsx
  • src/features/mentoring/api/mentor-api.ts
  • src/types/mentoring/registration-options.ts
  • src/types/mentoring/markdown.ts
  • src/features/mentoring/model/mentor-registration-keywords.ts
  • src/components/common/ui/text-action-button.tsx
  • src/features/mentoring/ui/detail/mentor-career-history-panel.tsx
  • src/features/mentoring/model/mentor-registration-entry-onboarding.ts
  • src/components/my-page/profile.tsx
  • src/features/mentoring/ui/registration/mentor-career-entries-editor.tsx
  • src/features/mentoring/ui/temp/contact-seed-actions.tsx
  • src/stores/useMentorDirectoryStore.ts
  • src/features/mentoring/ui/settings/settlement-register-modal.tsx
  • src/features/mentoring/ui/registration/mentor-registration-form.tsx

Comment on lines +76 to +79
<>
<h1 className="font-designer-24b text-text-strong mb-300 leading-snug sm:text-[30px]">
{mentoringTitleLabel}
</h1>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

sm:text-[30px] arbitrary value가 사용되었습니다.

코딩 가이드라인에 따르면 Tailwind arbitrary values (예: p-[4px], w-[320px])는 금지되어 있습니다. sm:text-[30px]를 프로젝트 디자인 토큰으로 교체해 주세요.

🔧 수정 제안
-          <h1 className="font-designer-24b text-text-strong mb-300 leading-snug sm:text-[30px]">
+          <h1 className="font-designer-24b text-text-strong mb-300 leading-snug sm:font-designer-28b">

또는 30px가 필요하다면 global.css에 새 유틸리티를 정의하세요:

`@utility` font-designer-30b {
  font-size: 30px;
  font-weight: var(--font-weight-bold);
  line-height: 45px;
  letter-spacing: 0;
}

As per coding guidelines: "Forbidden: Tailwind arbitrary values (e.g., p-[4px], w-[320px]). Use only project custom design tokens."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<>
<h1 className="font-designer-24b text-text-strong mb-300 leading-snug sm:text-[30px]">
{mentoringTitleLabel}
</h1>
<>
<h1 className="font-designer-24b text-text-strong mb-300 leading-snug sm:font-designer-28b">
{mentoringTitleLabel}
</h1>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/ui/detail/mentor-detail-header-section.tsx` around
lines 76 - 79, The h1 in mentor-detail-header-section.tsx uses a forbidden
Tailwind arbitrary value "sm:text-[30px]"; replace that class with the project's
design token utility (or add a new utility in global.css) and apply it to the h1
(look for the <h1> element rendering mentoringTitleLabel in
MentorDetailHeaderSection). Either swap "sm:text-[30px]" for the existing token
(e.g., a font-designer-30b-style utility) or define a new utility in global.css
as described and use that class name instead of the arbitrary value.

</div>
)}
<div className="mb-125">
<div className="mb-125 min-h-700">

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# min-h-700 사용처와 정의 여부 확인
echo "=== Checking min-h-700 definition in global.css ==="
rg -n 'min-h-700' src/app/global.css

echo "=== Checking spacing-700 definition ==="
rg -n 'spacing-700' src/app/global.css

echo "=== Checking other min-h utilities defined ==="
rg -n '@utility min-h-' src/app/global.css

Repository: code-zero-to-one/study-platform-client

Length of output: 322


🏁 Script executed:

#!/bin/bash
# Check all usages of min-h-700 in the codebase
echo "=== All usages of min-h-700 ==="
rg -n 'min-h-700' --type tsx --type ts

echo "=== Check if min-h-700 should be in global.css but missing ==="
echo "Current min-h definitions in global.css:"
rg -n '@utility min-h-' src/app/global.css -A 2

Repository: code-zero-to-one/study-platform-client

Length of output: 436


🏁 Script executed:

#!/bin/bash
# Search for min-h-700 more broadly without file type restrictions
echo "=== Searching for min-h-700 in entire codebase ==="
rg 'min-h-700' --no-heading

echo "=== Verify the specific file and line ==="
sed -n '155,165p' src/features/mentoring/ui/mentor-directory/mentor-card.tsx | cat -n

echo "=== Check all min-h- custom utilities defined in global.css ==="
rg '@utility min-h-' src/app/global.css

Repository: code-zero-to-one/study-platform-client

Length of output: 817


global.cssmin-h-700 유틸리티를 명시적으로 정의해야 합니다.

min-h-700은 파일에서 사용 중이지만 global.css에 명시적으로 정의되어 있지 않습니다. 프로젝트의 기존 패턴(min-h-280, min-h-260, min-h-200 등)을 따라 명시적으로 정의하세요:

`@utility` min-h-700 {
  min-height: 700px;
}

또는 --spacing-700: 56px를 활용하려면:

`@utility` min-h-700 {
  min-height: var(--spacing-700);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/mentoring/ui/mentor-directory/mentor-card.tsx` at line 158, The
component mentor-card.tsx uses the CSS utility class "min-h-700" but that
utility is not defined in global.css; add an explicit `@utility` entry for
min-h-700 in global.css following the existing pattern (e.g., mirror
min-h-280/min-h-260/min-h-200) so the class sets min-height: 700px, or
alternatively set min-height to var(--spacing-700) if you prefer to reuse the
spacing token; ensure the utility name exactly matches "min-h-700" so the div
className="mb-125 min-h-700" gets the intended height.

@Hyeonjun0527 Hyeonjun0527 merged commit b1c7b10 into develop Mar 23, 2026
9 of 10 checks passed
@Hyeonjun0527 Hyeonjun0527 deleted the refactor/mentoring-registration-sqaush branch March 23, 2026 15:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant