Skip to content

[feat] 사장님 구인구직 UI 구현 및 API 연동 - #60

Open
limtjdghks wants to merge 27 commits into
devfrom
feat/ALT-274
Open

[feat] 사장님 구인구직 UI 구현 및 API 연동#60
limtjdghks wants to merge 27 commits into
devfrom
feat/ALT-274

Conversation

@limtjdghks

@limtjdghks limtjdghks commented Jul 29, 2026

Copy link
Copy Markdown
Member

ID

  • ALT-274

변경 내용

  • 사장님(매니저) 구인구직 플로우 화면 6종 신규 구현 (내 공고 목록 / 공고 등록 / 공고 수정 / 공고 상세 / 지원자 목록 / 지원자 상세)
  • 위 화면 전체를 MANAGER - 업장 관리자 공고 관리 API에 연동 (조회·등록·수정·마감·채용 결정)
  • Docbar에 사장님 전용 내 공고 / 지원자 탭 추가 및 라우트 등록
  • Toast / SelectDropdown / Skeleton 공용 컴포넌트, 커서 페이징·시간 포맷 유틸을 shared 계층에 추가
  • 매니저 홈의 "진행 중인 공고"가 항상 0건으로 표시되던 버그 수정

구현 사항

화면 · 라우팅

  • features/manager/posting 슬라이스 신규 구성 (api / types / lib / hooks / ui)
  • 정적 세그먼트가 :postingId보다 먼저 매칭되도록 라우트 순서 배치
  • 지원자 목록은 Docbar 탭과 공고 상세 두 경로로 진입하며, 진입 경로에 따라 뒤로가기 노출을 분기
  • 근무일정의 시간 입력을 네이티브 type="time" 대신 기존 휠 픽커(WorkTimePickerDrawer)로 통일

API 연동

  • 공고 목록 / 지원자 목록은 useInfiniteQuery + IntersectionObserver 커서 무한스크롤
  • 공고 수정은 폼의 단일 스케줄 배열을 원본 스케줄 ID와 비교해 createSchedules / updateSchedules / deleteScheduleIds 3분할로 변환 (lib/buildPostingRequest.ts)
  • 서버 DTO → UI 모델 변환을 types/dto.ts의 어댑터로 일원화. DescribedEnumDto 래퍼(status.value) 해제, contact→phoneNumber·birthday→birthDate·GENDER_MALE→남성·자격증 필드명 매핑 처리
  • 뮤테이션 실패 시 에러 코드(B007/B012/B017/B018/B019/B020)를 사용자 문구로 변환해 토스트 노출
  • 쿼리 키를 queryKeys.posting 하위로 통일해, 공고 변경 시 목록·상세·홈 카드를 한 번에 무효화

shared 승격 / 버그 수정

  • 커서 페이징 응답의 CommonApiResponse 래핑 여부가 엔드포인트마다 달라, 두 형태를 모두 정규화하는 unwrapCursorPage 추가
  • 매니저 홈이 위 응답을 wrapped로 단정해 목록을 항상 빈 배열로 읽던 문제 수정 (해당 업장에 공고 3건이 있어도 "진행 중인 공고 0건"으로 표시됨)
  • 서버가 근무 시간을 HH:mm:ss로 내려주어 화면에 07:00:00~12:00:00으로 노출되고 시간 픽커 초기값이 깨지던 문제를 toTimeOfDay로 정규화
  • 요일·급여·상대시간 포맷터와 ApplicationApiStatus 타입을 shared로 추출해 유저/매니저 양측에서 재사용

구현 시연 (필요 시)

480.mov

Summary by CodeRabbit

새로운 기능

  • 매니저가 공고를 등록·수정·조회하고 모집을 마감할 수 있습니다.
  • 공고와 지원자 목록을 필터링하고 무한 스크롤로 확인할 수 있습니다.
  • 지원자 상세 정보 확인 및 채용 상태 변경을 지원합니다.
  • 근무 일정, 급여, 지원 상태를 편리하게 입력하고 확인할 수 있습니다.
  • 작업 결과와 오류를 토스트 알림으로 안내합니다.

개선

  • 매니저 전용 내비게이션 탭과 관련 화면 경로가 추가되었습니다.
  • 공고 및 지원자 상태가 배지와 안내 문구로 명확하게 표시됩니다.
  • 지원 제출 실패 시 재시도 가능 여부와 안내 문구를 제공합니다.
  • 처리 중인 확인 창은 작업이 완료될 때까지 닫히지 않습니다.

@limtjdghks
limtjdghks requested a review from kim3360 July 29, 2026 11:21
@limtjdghks limtjdghks self-assigned this Jul 29, 2026
@vercel

vercel Bot commented Jul 29, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
alter-client Ready Ready Preview Aug 4, 2026 12:24pm

Request Review

@limtjdghks limtjdghks changed the title Feat/alt 274 [feat] 사장님 구인구직 UI 구현 및 API 연동 Jul 29, 2026
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 7cc15f53-4810-4bc9-b21f-1e8df0b7b94d

📥 Commits

Reviewing files that changed from the base of the PR and between 84c06a9 and 65ea555.

📒 Files selected for processing (3)
  • src/pages/manager/posting-create/index.tsx
  • src/shared/ui/common/ConfirmModal.tsx
  • storybook/stories/ConfirmModal.stories.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/pages/manager/posting-create/index.tsx

📝 Walkthrough

Walkthrough

매니저용 공고·지원자 관리 기능이 추가됐다. 커서 페이지네이션 API, 생성·수정·마감 및 지원자 상태 변경 흐름, 목록·상세·폼 화면, 매니저 전용 라우팅과 Docbar 탭, 토스트·공통 입력 UI가 구현됐다.

Changes

매니저 공고·지원자 관리

Layer / File(s) Summary
공유 계약과 공고·지원자 API
src/shared/constants/*, src/shared/lib/*, src/features/manager/home/..., src/features/manager/posting/api/*, src/features/manager/posting/types/*
공유 결제·요일·시간·상태 타입, 커서 페이지 응답, 공고·지원자 DTO와 API 클라이언트가 추가됐다. 기존 공고 목록 응답 해석도 커서 구조에 맞게 변경됐다.
쿼리·뮤테이션·공고 폼 상태
src/features/manager/posting/hooks/*, src/features/manager/posting/lib/*
무한 스크롤 목록·상세 조회, 워크스페이스 필터, 생성·수정·마감·지원 상태 변경 뮤테이션, 공고 폼 검증과 요청 변환이 구현됐다.
공고·지원자 UI와 공통 입력 컴포넌트
src/features/manager/posting/ui/*, src/shared/ui/common/SelectDropdown.tsx, src/shared/ui/common/WheelPicker.tsx
목록 카드, 필터, 상태 배지, 채용 액션, 공고 입력 및 일정·시간 편집 UI가 추가됐다.
매니저 페이지와 라우팅
src/pages/manager/*, src/app/App.tsx, src/shared/constants/routes.ts
공고·지원자 목록, 생성·수정·상세 페이지와 MANAGER 보호 라우트 및 경로 빌더가 추가됐다.
매니저 탭과 공통 상태 처리
src/shared/ui/common/Docbar.tsx, src/shared/stores/*, src/shared/types/*, src/features/job-lookup-map/*, tailwind.config.js
지원자 탭과 매니저 라벨 오버라이드가 추가됐다. 토스트 표시와 지원 오류 재시도 처리가 구현됐다. 공통 오류·근무시간 타입도 정리됐다.
검증과 Storybook 지원
src/features/manager/posting/test/*, src/features/job-lookup-map/test/*, storybook/stories/*, src/shared/ui/common/ConfirmModal.tsx
공고 요청 변환, 오류 매핑, DTO 어댑터, 카드·일정 편집·Docbar·ConfirmModal 동작 검증이 추가됐다.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested reviewers: dohy-eon

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed ID, 변경 내용, 구현 사항, 구현 시연을 모두 포함하며 PR 목적과 변경 범위를 구체적으로 설명합니다.
Title check ✅ Passed 매니저 구인구직 UI 구현과 API 연동이라는 PR의 핵심 변경을 짧고 명확하게 설명합니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ALT-274

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@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

🧹 Nitpick comments (3)
src/app/App.tsx (1)

33-38: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

신규 매니저 공고 페이지 6종에 lazy loading 미적용.

SignupPage는 이미 Suspense+lazy 패턴을 쓰고 있는데, 이번에 추가된 매니저 공고·지원자 페이지 6개(ManagerPostingListPage, ManagerPostingCreatePage, ManagerPostingDetailPage, ManagerPostingEditPage, ManagerApplicationListPage, ManagerApplicationDetailPage)는 정적 import라 초기 번들에 그대로 포함됩니다. 매니저 전용 대규모 기능이므로 코드 스플리팅 대상으로 적합합니다.

♻️ 제안: React.lazy + Suspense 적용
-import { ManagerPostingListPage } from '`@/pages/manager/posting-list`'
-import { ManagerPostingCreatePage } from '`@/pages/manager/posting-create`'
-import { ManagerPostingDetailPage } from '`@/pages/manager/posting-detail`'
-import { ManagerPostingEditPage } from '`@/pages/manager/posting-edit`'
-import { ManagerApplicationListPage } from '`@/pages/manager/application-list`'
-import { ManagerApplicationDetailPage } from '`@/pages/manager/application-detail`'
+const ManagerPostingListPage = lazy(() => import('`@/pages/manager/posting-list`'))
+const ManagerPostingCreatePage = lazy(() => import('`@/pages/manager/posting-create`'))
+const ManagerPostingDetailPage = lazy(() => import('`@/pages/manager/posting-detail`'))
+const ManagerPostingEditPage = lazy(() => import('`@/pages/manager/posting-edit`'))
+const ManagerApplicationListPage = lazy(() => import('`@/pages/manager/application-list`'))
+const ManagerApplicationDetailPage = lazy(() => import('`@/pages/manager/application-detail`'))

<HomeRouteGuard> 내부를 <Suspense fallback={null}>로 감싸주세요.

As per path instructions, src/app/**: "라우팅 구조가 lazy loading을 활용하는지 확인" 항목에 따라 지적합니다.

Also applies to: 171-211, 256-271

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/App.tsx` around lines 33 - 38, Replace the six static manager page
imports with React.lazy imports for ManagerPostingListPage,
ManagerPostingCreatePage, ManagerPostingDetailPage, ManagerPostingEditPage,
ManagerApplicationListPage, and ManagerApplicationDetailPage, following the
existing SignupPage pattern. Wrap each corresponding HomeRouteGuard route
element in Suspense with a null fallback, preserving the current route
configuration and guards.

Source: Path instructions

src/pages/manager/posting-list/index.tsx (1)

50-69: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

무한스크롤 sentinel 로직이 두 페이지에 중복 구현됨. sentinelRef + IntersectionObserver 셋업/해제 로직이 두 파일에서 변수명까지 동일하게 반복됩니다. 근본 원인은 공유 훅 부재이므로 하나로 추출하는 것을 권장합니다.

  • src/pages/manager/posting-list/index.tsx#L50-L69: 이 블록을 useInfiniteScrollSentinel({ hasNextPage, isFetchingNextPage, fetchNextPage, isLoading, count: postings.length }) 같은 공유 훅으로 추출하고 sentinelRef만 반환받아 사용.
  • src/pages/manager/application-list/index.tsx#L56-L75: 동일한 공유 훅을 재사용해 중복 블록 제거(count: applications.length로 전달).
♻️ 제안: 공유 훅 추출 예시
// src/shared/lib/useInfiniteScrollSentinel.ts
export function useInfiniteScrollSentinel({
  hasNextPage,
  isFetchingNextPage,
  fetchNextPage,
  isLoading,
  count,
}: {
  hasNextPage: boolean
  isFetchingNextPage: boolean
  fetchNextPage: () => void
  isLoading: boolean
  count: number
}) {
  const sentinelRef = useRef<HTMLDivElement>(null)

  useEffect(() => {
    const el = sentinelRef.current
    if (!el) return

    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting && hasNextPage && !isFetchingNextPage) {
          fetchNextPage()
        }
      },
      { threshold: 0.1 }
    )

    observer.observe(el)
    return () => observer.disconnect()
  }, [hasNextPage, isFetchingNextPage, fetchNextPage, isLoading, count])

  return sentinelRef
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/manager/posting-list/index.tsx` around lines 50 - 69, Extract the
duplicated sentinelRef and IntersectionObserver setup from
src/pages/manager/posting-list/index.tsx lines 50-69 into a shared
useInfiniteScrollSentinel hook, accepting hasNextPage, isFetchingNextPage,
fetchNextPage, isLoading, and count, then return the ref for rendering. Replace
the duplicate block in src/pages/manager/application-list/index.tsx lines 56-75
with the same hook, passing applications.length as count; both sites should
otherwise preserve the existing fetch and cleanup behavior.
src/shared/ui/common/WheelPicker.tsx (1)

87-129: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

선택값이 스크린리더에 노출되지 않아요.

컨테이너에 role="listbox"만 있고 개별 항목엔 role/aria-selected가 없으며, 값 변경 시(ArrowUp/ArrowDown) 포커스나 aria-activedescendant 갱신도 없습니다. 근무시간처럼 필수 값을 고르는 컨트롤인데, 스크린리더 사용자는 현재 선택값을 전혀 알 수 없어 폼 작성을 완료하기 어렵습니다. listbox 대신 값-휠 특성에 맞는 slider/spinbutton + aria-valuenow/aria-valuetext 패턴이 더 적합합니다.

♿ 제안: spinbutton 시맨틱 추가
     <div
       data-vaul-no-drag
       className={cn(
         'relative touch-none select-none overflow-hidden focus:outline-none',
         className
       )}
       style={{ height: itemHeight * visibleCount }}
       aria-label={ariaLabel}
-      role="listbox"
+      role="slider"
+      aria-valuemin={0}
+      aria-valuemax={items.length - 1}
+      aria-valuenow={selectedIndex}
+      aria-valuetext={items[selectedIndex]}
       tabIndex={0}

**/*.tsx: "a11y: 서비스를 막는 수준(의미 있는 버튼/폼/이미지, 키보드 트랩 등)만" 지침에 근거해 필수 값-선택 컨트롤의 선택값 미노출을 지적합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/shared/ui/common/WheelPicker.tsx` around lines 87 - 129, Update the
WheelPicker container’s accessibility semantics from a bare listbox to a
value-selection control such as spinbutton, exposing the current selection
through aria-valuenow and aria-valuetext. Ensure handleKeyDown and any
pointer-driven value updates keep these attributes synchronized so screen
readers announce the selected item, while preserving the existing interaction
and visual behavior.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/features/manager/posting/types/dto.ts`:
- Around line 231-240: Update adaptApplicant so a missing dto.gender does not
default to ‘남성’; preserve ‘여성’ for GENDER_FEMALE and map absent gender to the
same placeholder convention used for other missing applicant fields, such as
‘-’.

In `@src/features/manager/posting/ui/PostingFormFields.tsx`:
- Around line 77-88: 공고 제목, 급여, 상세내용 입력 요소에 접근 가능한 이름을 추가하세요. PostingFormFields의
해당 input/textarea 각각에 화면에 표시되는 Section 라벨과 일치하는 aria-label을 지정하고, 기존 값·변경 처리·오류
스타일은 유지하세요.

---

Nitpick comments:
In `@src/app/App.tsx`:
- Around line 33-38: Replace the six static manager page imports with React.lazy
imports for ManagerPostingListPage, ManagerPostingCreatePage,
ManagerPostingDetailPage, ManagerPostingEditPage, ManagerApplicationListPage,
and ManagerApplicationDetailPage, following the existing SignupPage pattern.
Wrap each corresponding HomeRouteGuard route element in Suspense with a null
fallback, preserving the current route configuration and guards.

In `@src/pages/manager/posting-list/index.tsx`:
- Around line 50-69: Extract the duplicated sentinelRef and IntersectionObserver
setup from src/pages/manager/posting-list/index.tsx lines 50-69 into a shared
useInfiniteScrollSentinel hook, accepting hasNextPage, isFetchingNextPage,
fetchNextPage, isLoading, and count, then return the ref for rendering. Replace
the duplicate block in src/pages/manager/application-list/index.tsx lines 56-75
with the same hook, passing applications.length as count; both sites should
otherwise preserve the existing fetch and cleanup behavior.

In `@src/shared/ui/common/WheelPicker.tsx`:
- Around line 87-129: Update the WheelPicker container’s accessibility semantics
from a bare listbox to a value-selection control such as spinbutton, exposing
the current selection through aria-valuenow and aria-valuetext. Ensure
handleKeyDown and any pointer-driven value updates keep these attributes
synchronized so screen readers announce the selected item, while preserving the
existing interaction and visual behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0826f7f3-5fd7-49f8-8037-ab543b407274

📥 Commits

Reviewing files that changed from the base of the PR and between 2859c2d and b7f14d4.

⛔ Files ignored due to path filters (11)
  • src/assets/icons/doc/Applicant.svg is excluded by !**/*.svg
  • src/assets/icons/posting/Alert.svg is excluded by !**/*.svg
  • src/assets/icons/posting/Calendar.svg is excluded by !**/*.svg
  • src/assets/icons/posting/CheckCircle.svg is excluded by !**/*.svg
  • src/assets/icons/posting/ChevronRight.svg is excluded by !**/*.svg
  • src/assets/icons/posting/Clock.svg is excluded by !**/*.svg
  • src/assets/icons/posting/Close.svg is excluded by !**/*.svg
  • src/assets/icons/posting/Lock.svg is excluded by !**/*.svg
  • src/assets/icons/posting/Person.svg is excluded by !**/*.svg
  • src/assets/icons/posting/Plus.svg is excluded by !**/*.svg
  • src/assets/icons/posting/Write.svg is excluded by !**/*.svg
📒 Files selected for processing (67)
  • src/app/App.tsx
  • src/features/manager/api/posting.ts
  • src/features/manager/home/hooks/useManagedPostingsViewModel.ts
  • src/features/manager/home/types/posting.ts
  • src/features/manager/posting/api/application.ts
  • src/features/manager/posting/api/posting.ts
  • src/features/manager/posting/hooks/mutation/useClosePostingMutation.ts
  • src/features/manager/posting/hooks/mutation/useCreatePostingMutation.ts
  • src/features/manager/posting/hooks/mutation/useUpdateApplicationStatusMutation.ts
  • src/features/manager/posting/hooks/mutation/useUpdatePostingMutation.ts
  • src/features/manager/posting/hooks/query/useManagerPostingDetailQuery.ts
  • src/features/manager/posting/hooks/query/useManagerPostingsQuery.ts
  • src/features/manager/posting/hooks/query/usePostingApplicationDetailQuery.ts
  • src/features/manager/posting/hooks/query/usePostingApplicationsQuery.ts
  • src/features/manager/posting/hooks/query/useWorkspaceFilterOptions.ts
  • src/features/manager/posting/hooks/useApplicationDetailViewModel.ts
  • src/features/manager/posting/hooks/useApplicationListViewModel.ts
  • src/features/manager/posting/hooks/usePostingDetailViewModel.ts
  • src/features/manager/posting/hooks/usePostingForm.ts
  • src/features/manager/posting/hooks/usePostingListViewModel.ts
  • src/features/manager/posting/lib/applicationStatus.ts
  • src/features/manager/posting/lib/buildPostingRequest.ts
  • src/features/manager/posting/lib/postingErrorMessage.ts
  • src/features/manager/posting/lib/postingStatus.ts
  • src/features/manager/posting/types/dto.ts
  • src/features/manager/posting/types/posting.ts
  • src/features/manager/posting/ui/ApplicantCard.tsx
  • src/features/manager/posting/ui/FilterBar.tsx
  • src/features/manager/posting/ui/HiringActionBar.tsx
  • src/features/manager/posting/ui/ManagerApplicationStatusBadge.tsx
  • src/features/manager/posting/ui/ManagerPostingStatusBadge.tsx
  • src/features/manager/posting/ui/PostingFormFields.tsx
  • src/features/manager/posting/ui/PostingListCard.tsx
  • src/features/manager/posting/ui/ScheduleEditor.tsx
  • src/features/user/home/applied-stores/types/application.ts
  • src/pages/manager/application-detail/index.tsx
  • src/pages/manager/application-list/index.tsx
  • src/pages/manager/posting-create/index.tsx
  • src/pages/manager/posting-detail/index.tsx
  • src/pages/manager/posting-edit/index.tsx
  • src/pages/manager/posting-list/index.tsx
  • src/pages/manager/worker-schedule/components/FixedScheduleDateSection.tsx
  • src/pages/manager/worker-schedule/components/GeneralScheduleDateSection.tsx
  • src/pages/manager/worker-schedule/components/WorkTimeRangeField.tsx
  • src/shared/constants/payment.ts
  • src/shared/constants/routes.ts
  • src/shared/constants/workingDays.ts
  • src/shared/lib/cursorPage.ts
  • src/shared/lib/formatKoreanWorkTime.ts
  • src/shared/lib/formatRelativeTime.ts
  • src/shared/lib/queryKeys.ts
  • src/shared/lib/toTimeOfDay.ts
  • src/shared/stores/useDocStore.ts
  • src/shared/stores/useToastStore.ts
  • src/shared/types/applicationStatus.ts
  • src/shared/types/tab.ts
  • src/shared/types/workTime.ts
  • src/shared/ui/common/Docbar.tsx
  • src/shared/ui/common/Navbar.tsx
  • src/shared/ui/common/SelectDropdown.tsx
  • src/shared/ui/common/Skeleton.tsx
  • src/shared/ui/common/Toast.tsx
  • src/shared/ui/common/ToastViewport.tsx
  • src/shared/ui/common/WheelPicker.tsx
  • src/shared/ui/common/WorkTimePickerDrawer.tsx
  • storybook/stories/Docbar.stories.tsx
  • tailwind.config.js

Comment thread src/features/manager/posting/types/dto.ts
Comment on lines +77 to +88
<Section label="공고 제목" required error={errors.title}>
<input
type="text"
value={values.title}
placeholder="예: 주말 홀서빙 구합니다"
onChange={e => form.setTitle(e.target.value)}
className={cn(
inputClassName,
errors.title ? 'border-error' : 'border-line-1 focus:border-main'
)}
/>
</Section>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

필수 입력 필드에 접근성 이름이 없어요.

공고 제목, 급여, 상세내용 섹션의 input/textarea는 Section의 시각적 <h2> 라벨과 프로그램적으로 연결되어 있지 않습니다(htmlFor/id 없음, aria-label도 없음). 스크린리더 사용자는 어떤 값을 입력해야 하는지 알 수 없어 공고 등록/수정을 완료하지 못할 수 있습니다. 반면 업장 선택SelectDropdownariaLabel이 있어 문제없습니다.

♿ 최소 수정안 (각 input에 aria-label 추가)
       <Section label="공고 제목" required error={errors.title}>
         <input
           type="text"
+          aria-label="공고 제목"
           value={values.title}
           <input
             type="text"
             inputMode="numeric"
+            aria-label="급여"
             value={
         <textarea
           value={values.description}
           rows={5}
+          aria-label="상세내용"
           placeholder="근무 조건, 우대 사항 등을 자유롭게 작성해 주세요"

**/*.tsx: "a11y: 서비스를 막는 수준(의미 있는 버튼/폼/이미지, 키보드 트랩 등)만." 지침에 근거해 필수 폼 입력의 이름 부재를 지적합니다.

Also applies to: 116-139, 141-154

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/manager/posting/ui/PostingFormFields.tsx` around lines 77 - 88,
공고 제목, 급여, 상세내용 입력 요소에 접근 가능한 이름을 추가하세요. PostingFormFields의 해당 input/textarea
각각에 화면에 표시되는 Section 라벨과 일치하는 aria-label을 지정하고, 기존 값·변경 처리·오류 스타일은 유지하세요.

Source: Path instructions

@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: 1

Caution

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

⚠️ Outside diff range comments (1)
src/features/manager/posting/ui/ScheduleEditor.tsx (1)

216-229: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

정수가 아닌 모집 인원을 다른 값으로 변환하지 마세요.

type="number"1.5, 1e2, -1 같은 값을 입력할 수 있습니다. Line 226의 정규식은 이를 각각 15, 12, 1로 바꿉니다. 사용자가 의도하지 않은 positionsNeeded가 폼 상태에 저장됩니다.

정수만 허용한다면 type="text"inputMode="numeric"을 사용하세요. 또는 원본 값을 정수로 검증하고 유효하지 않으면 상태를 갱신하지 마세요.

수정 예시
-            type="number"
-            min={1}
+            type="text"
+            inputMode="numeric"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/manager/posting/ui/ScheduleEditor.tsx` around lines 216 - 229,
Update the positionsNeeded input handler in ScheduleEditor so invalid or
non-integer values are not transformed into different numbers and stored in form
state. Validate the original input as a non-negative integer, and only call
form.updateSchedule when it is valid; preserve the existing empty-display
behavior for zero.
🧹 Nitpick comments (3)
src/features/job-lookup-map/test/lib/applyPostingError.test.ts (1)

24-36: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

HTTP 429 분기를 독립적으로 검증하세요.

이 fixture는 status: 429code: 'E001'을 함께 설정합니다. 두 조건 모두 retryable: true를 만들기 때문에 HTTP 429 조건이 제거되어도 테스트가 통과합니다. code를 비재시도 코드로 변경하거나 생략하세요.

수정 예시
         axiosError(429, {
-          code: 'E001',
+          code: 'B001',
           message: '요청이 너무 많습니다. 잠시 후 다시 시도해주세요.',
         })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/job-lookup-map/test/lib/applyPostingError.test.ts` around lines
24 - 36, Update the HTTP 429 test for resolveApplyPostingError so its fixture
does not also use retryable code E001; omit the code or use a non-retryable
code, while preserving status 429 and the expected retryable: true result to
verify the HTTP status branch independently.
storybook/stories/PostingListCard.stories.tsx (1)

4-5: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

기능 공개 API를 통해 가져오세요.

이 스토리는 feature 외부에 있습니다. ui/PostingListCardtypes/posting을 직접 import하면 내부 경로가 외부 계약이 됩니다.

src/features/manager/posting/index.ts에서 필요한 심볼을 export한 뒤, 이 스토리는 공개 API만 import하세요.

수정 예시
-import { PostingListCard } from '../../src/features/manager/posting/ui/PostingListCard'
-import type { PostingListItem } from '../../src/features/manager/posting/types/posting'
+import {
+  PostingListCard,
+  type PostingListItem,
+} from '../../src/features/manager/posting'

As per path instructions, src/features/**는 외부에 index.ts 공개 API를 통해서만 노출해야 합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@storybook/stories/PostingListCard.stories.tsx` around lines 4 - 5, Update the
posting feature’s public API in src/features/manager/posting/index.ts to export
PostingListCard and PostingListItem, then change the PostingListCard story
imports to use that feature entry point instead of ui/PostingListCard and
types/posting internal paths.

Source: Path instructions

src/pages/manager/posting-create/index.tsx (1)

6-6: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

공고 생성 흐름을 feature의 public API로 이동하세요.

Line 6은 page가 feature 내부 lib 모듈을 직접 import하게 합니다. Lines 37-42는 서버 오류를 공고 폼 오류 상태로 변환합니다. 이 작업은 page의 조합 책임을 넘습니다.

useCreatePostingFlow 같은 feature hook을 만들고 index.ts에서만 노출하세요. 이 hook이 mutation callback, resolvePostingFormError, setServerErrors를 관리하게 하세요. page는 성공 후 이동 처리와 UI 조합만 담당해야 합니다.

경로 지침의 “Public API(index.ts)를 통해서만 외부에 노출” 및 “페이지 컴포넌트가 비즈니스 로직 없이 조합(Composition)만 하는지” 규칙을 적용했습니다.

Also applies to: 37-44

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/manager/posting-create/index.tsx` at line 6, Move the
posting-creation mutation and server-error handling from the page into a feature
hook such as useCreatePostingFlow, including resolvePostingFormError and
setServerErrors. Export the hook through the feature’s index.ts public API,
remove the page’s direct lib import, and leave the page responsible only for
invoking the flow, handling success navigation, and composing the UI.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/pages/manager/posting-create/index.tsx`:
- Around line 37-44: Prevent stale server errors from being applied by updating
the submission flow around the onError handler and form state management: either
keep the confirmation modal and form editing locked until the request completes,
or capture the form version at submission and call
form.setServerErrors(fieldErrors) only when that version still matches the
current form version; preserve showing the resolved error message via showToast.

---

Outside diff comments:
In `@src/features/manager/posting/ui/ScheduleEditor.tsx`:
- Around line 216-229: Update the positionsNeeded input handler in
ScheduleEditor so invalid or non-integer values are not transformed into
different numbers and stored in form state. Validate the original input as a
non-negative integer, and only call form.updateSchedule when it is valid;
preserve the existing empty-display behavior for zero.

---

Nitpick comments:
In `@src/features/job-lookup-map/test/lib/applyPostingError.test.ts`:
- Around line 24-36: Update the HTTP 429 test for resolveApplyPostingError so
its fixture does not also use retryable code E001; omit the code or use a
non-retryable code, while preserving status 429 and the expected retryable: true
result to verify the HTTP status branch independently.

In `@src/pages/manager/posting-create/index.tsx`:
- Line 6: Move the posting-creation mutation and server-error handling from the
page into a feature hook such as useCreatePostingFlow, including
resolvePostingFormError and setServerErrors. Export the hook through the
feature’s index.ts public API, remove the page’s direct lib import, and leave
the page responsible only for invoking the flow, handling success navigation,
and composing the UI.

In `@storybook/stories/PostingListCard.stories.tsx`:
- Around line 4-5: Update the posting feature’s public API in
src/features/manager/posting/index.ts to export PostingListCard and
PostingListItem, then change the PostingListCard story imports to use that
feature entry point instead of ui/PostingListCard and types/posting internal
paths.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d869e1b9-5b31-4d27-b90c-a5bb87a7e025

📥 Commits

Reviewing files that changed from the base of the PR and between b7f14d4 and 84c06a9.

📒 Files selected for processing (30)
  • src/features/job-lookup-map/hooks/useApplyPosting.ts
  • src/features/job-lookup-map/lib/applyPostingError.ts
  • src/features/job-lookup-map/test/lib/applyPostingError.test.ts
  • src/features/manager/posting/api/application.ts
  • src/features/manager/posting/hooks/mutation/useCreatePostingMutation.ts
  • src/features/manager/posting/hooks/mutation/useUpdatePostingMutation.ts
  • src/features/manager/posting/hooks/query/usePostingApplicationsQuery.ts
  • src/features/manager/posting/hooks/useApplicationListViewModel.ts
  • src/features/manager/posting/hooks/usePostingForm.ts
  • src/features/manager/posting/lib/postingErrorMessage.ts
  • src/features/manager/posting/test/api/application.test.ts
  • src/features/manager/posting/test/lib/buildPostingRequest.test.ts
  • src/features/manager/posting/test/lib/postingErrorMessage.test.ts
  • src/features/manager/posting/test/types/dto.test.ts
  • src/features/manager/posting/types/dto.ts
  • src/features/manager/posting/types/posting.ts
  • src/features/manager/posting/ui/FilterBar.tsx
  • src/features/manager/posting/ui/PostingFormFields.tsx
  • src/features/manager/posting/ui/PostingListCard.tsx
  • src/features/manager/posting/ui/ScheduleEditor.tsx
  • src/pages/manager/application-list/index.tsx
  • src/pages/manager/posting-create/index.tsx
  • src/pages/manager/posting-detail/index.tsx
  • src/pages/manager/posting-edit/index.tsx
  • src/pages/user/job-lookup-map-apply/index.tsx
  • src/shared/lib/queryKeys.ts
  • src/shared/lib/utils/errorUtils.ts
  • src/shared/types/common.ts
  • storybook/stories/PostingListCard.stories.tsx
  • storybook/stories/ScheduleEditor.stories.tsx
💤 Files with no reviewable changes (2)
  • src/features/manager/posting/hooks/mutation/useCreatePostingMutation.ts
  • src/features/manager/posting/hooks/mutation/useUpdatePostingMutation.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • src/features/manager/posting/ui/PostingListCard.tsx
  • src/pages/manager/posting-edit/index.tsx
  • src/features/manager/posting/types/posting.ts
  • src/pages/manager/application-list/index.tsx
  • src/features/manager/posting/ui/PostingFormFields.tsx
  • src/features/manager/posting/types/dto.ts
  • src/features/manager/posting/ui/FilterBar.tsx

Comment thread src/pages/manager/posting-create/index.tsx
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.

2 participants