Skip to content

Conversation

@leeleeleeleejun
Copy link
Member

@leeleeleeleejun leeleeleeleejun commented Oct 11, 2025

#️⃣연관된 이슈

📝작업 내용

admin 메인 등록 조회 상세 페이지 퍼블리싱

admin과 web에서 공통으로 사용되는 로직들 packages폴더로 이동

  • Banner component를 packages프로젝트(디자인 시스템)으로 이동 967898b
    • catalog로 버전 의존성 관리
  • @repo/utils 생성 및 toLatLng.ts를 @repo/utils으로 이동 e232559
  • api 관련 로직은 추후 예정

스크린샷 (선택)

스크린샷 2025-10-11 오후 4 17 14 스크린샷 2025-10-11 오후 4 17 18

💬리뷰 요구사항(선택)

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

ex) 메서드 XXX의 이름을 더 잘 짓고 싶은데 혹시 좋은 명칭이 있을까요?

Summary by CodeRabbit

  • 신기능

    • 관리자: 캠퍼스별 요청 목록 페이지와 요청 상세 페이지 추가(메뉴/소개/위치 지도/등록·거절 액션, 거절 사유 모달).
    • 네이버 지도 연동을 위한 프로바이더 도입.
  • 스타일

    • 관리자: Pretendard 로컬 폰트 적용 및 레이아웃 개편(Hero UI 테마/컴포넌트 적용).
    • 공용 UI 배너 스타일 스코프(ui: 프리픽스)로 정리.
  • 리팩터

    • 배너 컴포넌트를 공용 UI로 통합, 관련 임포트 경로 정리.
  • 잡무

    • 맵/테마 관련 의존성 추가·정리, Web 앱의 Keen Slider 제거 및 워크스페이스 카탈로그 갱신.

- 요청에 대한 등록, 거절 기능 버튼들
@coderabbitai
Copy link

coderabbitai bot commented Oct 11, 2025

Walkthrough

Admin 앱에 HeroUI와 Naver Map을 도입하고 레이아웃/글꼴을 교체했다. 요청 상세 페이지와 관련 컴포넌트/타입/유틸을 신규 추가했다. Web 앱은 Banner import 경로 정리와 Keen Slider CSS/의존성 정리를 수행했다. UI 패키지는 Banner 스타일을 prefix화하고 keen-slider 의존성을 이동했다. 워크스페이스 카탈로그를 갱신했다.

Changes

Cohort / File(s) Summary
Admin deps & global setup
apps/admin/package.json, apps/admin/src/app/globals.css, apps/admin/src/app/hero.ts, apps/admin/src/app/NaverMapProvider.tsx, apps/admin/src/app/layout.tsx
HeroUI/Theme 추가 및 Tailwind 플러그인/소스 등록, Pretendard 로컬 폰트로 교체, HeroUIProvider/NaverMapProvider 도입, Hero UI 테마 설정 모듈 추가, react-naver-maps 의존성 및 타입 추가
Admin 요청 상세 기능
apps/admin/src/app/request/[id]/page.tsx, .../RequestDetailPage.tsx, .../_components/ActionButtonGroup/*, .../_components/RejectModal/*, .../_components/Location/*, .../_components/Menus/*, .../_components/Description/*, .../_api/types.ts, .../_utils/toLatLng.ts
요청 상세 페이지 및 하위 컴포넌트(액션 버튼, 거절 모달, 위치 맵, 메뉴 리스트, 소개)와 타입/유틸 신규 추가
Admin 메인 페이지/리스트 컴포넌트
apps/admin/src/app/page.tsx, apps/admin/src/app/_api/types.ts, apps/admin/src/app/_components/RequestListItem/*, apps/admin/src/app/_components/RequestsByCampus/*
메인 페이지 구조 개편(캠퍼스별 요청 섹션), 요청 리스트 아이템/캠퍼스별 리스트 컴포넌트 및 Request 타입 추가
Admin 공용 상수/경로
apps/admin/src/consts/campus.ts, apps/admin/src/consts/path.ts
캠퍼스 상수/타입 추가, 서버/클라이언트 경로 빌더 및 BASE_URL 상수 추가
Web 앱 정리 및 경로 수정
apps/web/app/globals.css, apps/web/app/layout.tsx, apps/web/app/map/_components/CampusButtom/CampusButtonBax.tsx, apps/web/app/map/_hooks/useCenterMapToCurrentLocation.ts, apps/web/app/page.tsx, apps/web/app/places/[id]/PlaceDetailPage.tsx, apps/web/app/places/new/_components/Step/PlacePreview/PlacePreview.tsx, apps/web/app/requests/[id]/RequestDetailPage.tsx, apps/web/next-env.d.ts
Keen Slider CSS 제거, Banner import를 @repo/ui로 통일, 내부 util 경로 상대화, routes 타입 레퍼런스 추가
UI 패키지 업데이트
packages/ui/package.json, packages/ui/src/components/Banner/Banner.tsx, packages/ui/tsconfig.json
keen-slider 의존성 UI 패키지로 이동, Banner 클래스에 ui: prefix 적용 및 import 정리, TS rootDir 지정
Workspace 카탈로그
pnpm-workspace.yaml
catalog에 keen-slider/react-naver-maps/@types/navermaps 버전 추가/정의

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User
  participant R as Next.js Router
  participant L as RootLayout
  participant H as HeroUIProvider
  participant N as NaverMapProvider
  participant P as Page (/request/[id])

  U->>R: 요청 상세 URL 접속
  R->>L: RootLayout 렌더
  L->>H: HeroUIProvider 래핑
  H->>N: NaverMapProvider(ncpKeyId)
  N->>P: RequestDetailPage 렌더
  note over N,P: Navermaps SDK 컨텍스트 제공
Loading
sequenceDiagram
  autonumber
  participant D as RequestDetailPage
  participant A as ActionButtonGroup
  participant M as RejectModal
  participant Map as Location (NaverMap)

  D->>Map: NaverMap 컨테이너 마운트
  D->>A: onOpen 핸들러 전달
  A->>D: 거절 버튼 클릭(onOpen)
  D->>M: isOpen=true로 모달 표시
  M->>M: 사유 입력 (로컬 state)
  M-->>D: onOpenChange 호출로 모달 닫기
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

✨ Feature, ⚙️ Chore

Poem

(\_/) 깡충—
( •ᴗ•) 새 지도가 뜨고, 배너는 반짝—
Hero의 빛 아래 레이아웃 착!
요청들 줄지어, 캠퍼스별 착착—
거절 모달 톡, 등록 버튼 찰칵!
오늘도 깔끔 커밋, 당근 한 입 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed PR 제목은 admin 관련 메인 페이지와 등록 조회 상세 페이지 UI 작업이라는 핵심 변경 사항을 구체적으로 요약하고 있어 변경의 주된 내용을 명확히 전달합니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#48

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 and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

Caution

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

⚠️ Outside diff range comments (1)
packages/ui/src/components/Banner/Banner.tsx (1)

91-119: Tailwind config에 prefix: 'ui:' 추가
packages/ui/tailwind.config.ts에 prefix: 'ui:' 옵션을 설정하세요. 현재 접두사가 없어 ui: 접두사가 붙은 클래스가 적용되지 않습니다.

🧹 Nitpick comments (8)
apps/admin/src/app/request/[id]/page.tsx (1)

3-9: 불필요한 Fragment 제거 권장

단일 자식 컴포넌트만 반환하는 경우 Fragment 래퍼가 불필요합니다. 코드를 더 간결하게 만들 수 있습니다.

다음 diff를 적용하여 Fragment를 제거하세요:

 const Page = () => {
-  return (
-    <>
-      <RequestDetailPage />
-    </>
-  )
+  return <RequestDetailPage />
 }
apps/admin/src/app/request/[id]/_components/Location/Location.tsx (1)

16-16: 주석 처리된 코드 제거 또는 구현 필요

PlaceMarker가 주석 처리되어 있습니다. 향후 구현 예정이라면 TODO 주석을 추가하고, 그렇지 않다면 제거하는 것이 좋습니다.

이 기능을 구현하거나 관련 이슈를 생성하는 것을 도와드릴까요?

apps/admin/src/components/RequestsByCampus/RequestsByCampus.tsx (1)

5-5: 타입 import 경로 일관성 확인
Request 타입은 apps/admin/src/app/request/[id]/_api/types.ts에 정의된 단일 진실 공급원입니다. 해당 파일에서 직접 import하도록 수정하세요:

import { type Request } from '@/app/request/[id]/_api/types'
apps/admin/src/app/request/[id]/RequestDetailPage.tsx (1)

36-67: 반복되는 mock 데이터 제거 권장

동일한 메뉴 데이터가 6번 반복되고 있습니다. API 연동 전까지는 더 다양한 mock 데이터를 사용하거나, 최소한의 샘플 데이터로 줄이는 것을 고려해보세요.

다음과 같이 개선할 수 있습니다:

-          <Menus
-            menus={[
-              {
-                name: '짬뽕',
-                price: 20000,
-                isRecommended: false,
-              },
-              {
-                name: '짬뽕',
-                price: 20000,
-                isRecommended: false,
-              },
-              {
-                name: '짬뽕',
-                price: 20000,
-                isRecommended: false,
-              },
-              {
-                name: '짬뽕',
-                price: 20000,
-                isRecommended: false,
-              },
-              {
-                name: '짬뽕',
-                price: 20000,
-                isRecommended: false,
-              },
-              {
-                name: '짬뽕',
-                price: 20000,
-                isRecommended: false,
-              },
-            ]}
-          />
+          <Menus
+            menus={[
+              {
+                name: '짬뽕',
+                price: 20000,
+                isRecommended: true,
+              },
+              {
+                name: '짜장면',
+                price: 15000,
+                isRecommended: false,
+              },
+              {
+                name: '탕수육',
+                price: 25000,
+                isRecommended: false,
+              },
+            ]}
+          />
apps/admin/src/app/page.tsx (2)

4-4: 타입 import 위치 개선 권장

Request 타입을 RequestListItem 컴포넌트에서 import하고 있습니다. 타입은 별도의 types 파일에서 import하는 것이 더 명확한 구조입니다.

// apps/admin/src/app/request/[id]/_api/types.ts에서 import
import { type Request } from '@/app/request/[id]/_api/types'

6-25: 다양한 mock 데이터 사용 권장

모든 항목이 "짬뽕집"으로 동일합니다. 개발 중 UI 테스트를 위해 더 다양한 샘플 데이터를 사용하는 것을 고려해보세요.

apps/admin/src/app/request/[id]/_components/Menus/Menus.tsx (1)

12-22: 메뉴 파티셔닝 로직을 메모이제이션하여 성능을 개선하세요.

현재 reduce가 매 렌더링마다 실행되어 새 배열을 생성합니다. menus prop이 변경될 때만 재계산하도록 useMemo를 사용하는 것을 권장합니다.

다음과 같이 useMemo를 적용하세요:

+import { useMemo } from 'react'
 
 export const Menus = ({ menus }: Props) => {
-  const { recommendedMenu, unRecommendedMenu } = menus.reduce(
+  const { recommendedMenu, unRecommendedMenu } = useMemo(() => menus.reduce(
     (acc, menu) => {
       if (menu.isRecommended) acc.recommendedMenu.push(menu)
       else acc.unRecommendedMenu.push(menu)
       return acc
     },
     {
       recommendedMenu: [] as typeof menus,
       unRecommendedMenu: [] as typeof menus,
     },
-  )
+  ), [menus])
apps/admin/src/app/request/[id]/_api/types.ts (1)

4-18: 내부 타입 export 여부 검토

  • MenuMenus.tsx/Menu.tsx에서 RequestDetail['menus'][0] 방식으로 사용 중이며, 명시적 참조가 필요하다면 export type Menu을 추가하세요.
  • Photo, Category, Tag는 현재 직접 참조되지 않으므로, 추후 사용 시에만 export를 고려하면 됩니다.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4c87fd5 and 5038e66.

⛔ Files ignored due to path filters (2)
  • apps/admin/public/fonts/PretendardVariable.woff2 is excluded by !**/*.woff2
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (45)
  • apps/admin/package.json (2 hunks)
  • apps/admin/src/app/NaverMapProvider.tsx (1 hunks)
  • apps/admin/src/app/globals.css (1 hunks)
  • apps/admin/src/app/hero.ts (1 hunks)
  • apps/admin/src/app/layout.tsx (1 hunks)
  • apps/admin/src/app/page.tsx (1 hunks)
  • apps/admin/src/app/request/[id]/RequestDetailPage.tsx (1 hunks)
  • apps/admin/src/app/request/[id]/_api/types.ts (1 hunks)
  • apps/admin/src/app/request/[id]/_components/ActionButtonGroup/ActionButtonGroup.tsx (1 hunks)
  • apps/admin/src/app/request/[id]/_components/ActionButtonGroup/index.tsx (1 hunks)
  • apps/admin/src/app/request/[id]/_components/Description/Description.tsx (1 hunks)
  • apps/admin/src/app/request/[id]/_components/Description/index.tsx (1 hunks)
  • apps/admin/src/app/request/[id]/_components/Location/Location.tsx (1 hunks)
  • apps/admin/src/app/request/[id]/_components/Location/index.tsx (1 hunks)
  • apps/admin/src/app/request/[id]/_components/Menus/Menu.tsx (1 hunks)
  • apps/admin/src/app/request/[id]/_components/Menus/Menus.tsx (1 hunks)
  • apps/admin/src/app/request/[id]/_components/Menus/index.tsx (1 hunks)
  • apps/admin/src/app/request/[id]/_components/RejectModal/RejectModal.tsx (1 hunks)
  • apps/admin/src/app/request/[id]/_components/RejectModal/index.tsx (1 hunks)
  • apps/admin/src/app/request/[id]/page.tsx (1 hunks)
  • apps/admin/src/components/RequestListItem/RequestListItem.tsx (1 hunks)
  • apps/admin/src/components/RequestListItem/index.tsx (1 hunks)
  • apps/admin/src/components/RequestsByCampus/RequestsByCampus.tsx (1 hunks)
  • apps/admin/src/components/RequestsByCampus/index.tsx (1 hunks)
  • apps/admin/src/consts/campus.ts (1 hunks)
  • apps/admin/src/consts/path.ts (1 hunks)
  • apps/web/app/globals.css (0 hunks)
  • apps/web/app/layout.tsx (0 hunks)
  • apps/web/app/map/MapPage.tsx (1 hunks)
  • apps/web/app/map/_components/CampusButtom/CampusButtonBax.tsx (1 hunks)
  • apps/web/app/map/_components/Marker/Marker.tsx (1 hunks)
  • apps/web/app/map/_hooks/useCenterMapToCurrentLocation.ts (1 hunks)
  • apps/web/app/page.tsx (1 hunks)
  • apps/web/app/places/[id]/PlaceDetailPage.tsx (1 hunks)
  • apps/web/app/places/[id]/_components/Location/Location.tsx (1 hunks)
  • apps/web/app/places/new/_components/Step/PlacePreview/PlacePreview.tsx (1 hunks)
  • apps/web/app/requests/[id]/RequestDetailPage.tsx (1 hunks)
  • apps/web/next-env.d.ts (1 hunks)
  • apps/web/package.json (2 hunks)
  • packages/ui/package.json (1 hunks)
  • packages/ui/src/components/Banner/Banner.tsx (4 hunks)
  • packages/utils/eslint.config.js (1 hunks)
  • packages/utils/package.json (1 hunks)
  • packages/utils/toLatLng/index.ts (1 hunks)
  • pnpm-workspace.yaml (1 hunks)
💤 Files with no reviewable changes (2)
  • apps/web/app/layout.tsx
  • apps/web/app/globals.css
🧰 Additional context used
🧬 Code graph analysis (20)
apps/web/app/requests/[id]/RequestDetailPage.tsx (1)
apps/web/app/_components/Banner/Banner.tsx (1)
  • Props (33-79)
apps/admin/src/app/request/[id]/page.tsx (1)
apps/admin/src/app/request/[id]/RequestDetailPage.tsx (1)
  • RequestDetailPage (17-82)
apps/admin/src/app/request/[id]/_components/ActionButtonGroup/ActionButtonGroup.tsx (2)
packages/ui/src/utils/cn.ts (1)
  • cn (19-21)
packages/ui/src/consts/colorVariant.ts (1)
  • COLOR_VARIANTS (1-26)
apps/admin/src/app/layout.tsx (2)
apps/admin/src/app/NaverMapProvider.tsx (1)
  • NaverMapProvider (6-10)
packages/ui/src/components/Layout/Column/Column.tsx (1)
  • restProps (4-16)
apps/admin/src/app/request/[id]/RequestDetailPage.tsx (3)
apps/admin/src/app/request/[id]/_components/ActionButtonGroup/ActionButtonGroup.tsx (1)
  • ActionButtonGroup (9-35)
apps/admin/src/app/request/[id]/_components/RejectModal/RejectModal.tsx (1)
  • RejectModal (18-46)
apps/web/app/_components/HeaderBackButton/HeaderBackButton.tsx (1)
  • useRouter (6-14)
apps/admin/src/app/request/[id]/_components/Description/Description.tsx (2)
packages/ui/src/components/Text/Text.stories.tsx (1)
  • div (63-76)
apps/web/app/_components/PlaceListItem/PlaceListItem.tsx (1)
  • Props (12-45)
packages/ui/src/components/Banner/Banner.tsx (2)
packages/ui/src/components/Layout/Layout.stories.tsx (1)
  • args (15-21)
packages/ui/src/components/Chip/Chip.stories.tsx (1)
  • Flex (43-49)
packages/ui/package.json (1)
apps/web/app/_components/Banner/Banner.tsx (2)
  • slider (39-66)
  • slider (48-50)
apps/admin/src/app/request/[id]/_components/RejectModal/RejectModal.tsx (2)
packages/ui/src/utils/cn.ts (1)
  • cn (19-21)
packages/ui/src/consts/colorVariant.ts (1)
  • COLOR_VARIANTS (1-26)
apps/admin/src/consts/path.ts (4)
apps/web/app/_mocks/handlers/placeHandlers.ts (1)
  • path (7-9)
apps/web/app/_mocks/handlers/categoryHandlers.ts (1)
  • path (7-9)
apps/web/app/_constants/path.ts (3)
  • id (16-16)
  • sort (7-7)
  • id (6-6)
apps/web/app/_components/BottomNavigation/TabItem.tsx (1)
  • TabItemProps (20-42)
packages/utils/eslint.config.js (1)
packages/ui/src/components/Textarea/Textarea.stories.tsx (1)
  • props (17-26)
apps/admin/src/app/request/[id]/_api/types.ts (1)
packages/utils/toLatLng/toLatLng.ts (1)
  • Coord (1-1)
apps/admin/package.json (1)
packages/ui/src/components/Icon/assets/icons/navigation/map.tsx (1)
  • props (3-22)
apps/web/app/page.tsx (1)
apps/web/app/_components/Banner/Banner.tsx (2)
  • Props (33-79)
  • slider (39-66)
apps/admin/src/app/request/[id]/_components/Menus/Menus.tsx (2)
packages/ui/src/utils/cn.ts (1)
  • cn (19-21)
apps/admin/src/app/request/[id]/_components/Menus/Menu.tsx (1)
  • Menu (10-27)
apps/web/app/places/[id]/PlaceDetailPage.tsx (1)
apps/web/app/_components/Banner/Banner.tsx (6)
  • Props (33-79)
  • slider (52-62)
  • slider (39-66)
  • content (72-76)
  • slider (48-50)
  • mouseOver (53-56)
apps/web/app/places/new/_components/Step/PlacePreview/PlacePreview.tsx (1)
apps/web/app/_components/Banner/Banner.tsx (1)
  • Props (33-79)
apps/admin/src/components/RequestsByCampus/RequestsByCampus.tsx (2)
apps/admin/src/consts/campus.ts (2)
  • CampusType (7-7)
  • CAMPUS (1-5)
apps/admin/src/components/RequestListItem/RequestListItem.tsx (1)
  • RequestListItem (7-30)
apps/admin/src/components/RequestListItem/RequestListItem.tsx (2)
apps/admin/src/app/request/[id]/_api/types.ts (1)
  • Request (20-25)
packages/ui/src/utils/cn.ts (1)
  • cn (19-21)
apps/admin/src/app/page.tsx (1)
apps/admin/src/components/RequestsByCampus/RequestsByCampus.tsx (1)
  • RequestsByCampus (12-23)
🔇 Additional comments (43)
packages/ui/package.json (1)

40-41: 의존성 추가가 적절합니다.

keen-slider가 Banner 컴포넌트에서 사용되며, catalog를 통한 버전 관리가 적절하게 설정되어 있습니다.

apps/web/next-env.d.ts (1)

3-3: Next.js 타입 라우트 기능이 올바르게 설정되었습니다.

Next.js 15의 타입 안전 라우팅을 위한 표준 설정입니다.

packages/utils/toLatLng/index.ts (1)

1-1: 모듈화가 잘 되어있습니다.

배럴 익스포트 패턴이 적절하게 적용되었습니다.

apps/web/app/page.tsx (1)

14-14: 공유 컴포넌트로의 전환이 적절합니다.

Banner 컴포넌트가 @repo/ui로 이동되었으며, 사용 방식은 동일하게 유지됩니다. 컴포넌트가 정상적으로 작동하는지 확인해 주세요.

packages/ui/src/components/Banner/Banner.tsx (1)

7-7: 임포트 경로 개선이 적절합니다.

상대 경로를 사용한 Layout 임포트가 더 명확합니다.

apps/admin/src/consts/path.ts (1)

3-16: 경로 상수 구조가 잘 설계되었습니다.

as const 어서션을 사용한 타입 안전한 경로 관리와 동적 경로 함수가 적절하게 구현되었습니다.

apps/web/app/requests/[id]/RequestDetailPage.tsx (1)

10-10: 공유 컴포넌트 임포트 경로가 일관성있게 적용되었습니다.

Banner 컴포넌트의 임포트 경로가 @repo/ui로 통일되어 코드베이스 전체에서 일관성이 유지됩니다.

apps/admin/src/app/hero.ts (1)

1-4: heroui() 반환 타입이 TailwindCSS Config와 호환되는지 확인하세요. 타입 단언(as Config) 대신 필요한 경우 정확한 타입 선언이나 설정으로 대체해야 합니다.

apps/web/app/map/_components/Marker/Marker.tsx (1)

3-3: LGTM!

유틸리티 중앙화를 위한 임포트 경로 업데이트가 올바르게 적용되었습니다.

apps/web/app/places/[id]/_components/Location/Location.tsx (1)

3-3: LGTM!

임포트 경로가 중앙 유틸리티 패키지로 올바르게 업데이트되었습니다.

apps/web/app/map/_hooks/useCenterMapToCurrentLocation.ts (1)

1-1: LGTM!

임포트 경로 리팩토링이 일관성 있게 적용되었습니다.

apps/web/app/map/MapPage.tsx (1)

13-13: LGTM!

유틸리티 중앙화를 위한 임포트 경로 변경이 올바르게 적용되었습니다.

apps/admin/src/app/request/[id]/_components/Description/index.tsx (1)

1-1: LGTM!

배럴 익스포트 패턴이 프로젝트의 다른 컴포넌트 구조와 일관성 있게 적용되었습니다.

apps/admin/src/components/RequestListItem/index.tsx (1)

1-1: LGTM!

배럴 익스포트가 프로젝트의 표준 패턴에 따라 올바르게 구현되었습니다.

apps/admin/src/app/globals.css (1)

2-4: Hero UI 플러그인 구성 확인됨: apps/admin/src/app/hero.ts 파일이 존재하며 heroui()로 올바른 Tailwind Config를 내보내고 있습니다.

apps/web/app/places/new/_components/Step/PlacePreview/PlacePreview.tsx (1)

6-6: Banner 컴포넌트 기능 일치 확인 완료 새 Banner는 keen-slider 기반 자동 슬라이드와 마우스 오버 시 일시정지 기능을 포함해 원본 Banner 기능을 모두 제공합니다.

apps/admin/src/components/RequestsByCampus/index.tsx (1)

1-1: 배럴 내보내기 패턴이 올바르게 적용되었습니다.

컴포넌트를 깔끔하게 재내보내기하는 표준 패턴입니다.

apps/web/app/map/_components/CampusButtom/CampusButtonBax.tsx (1)

10-10: 공유 유틸리티 패키지로의 임포트 경로 변경이 적절합니다.

toLatLng 유틸리티를 중앙 집중화된 @repo/utils 패키지로 이동하는 리팩토링의 일부입니다. 로직 변경 없이 모듈 해석 경로만 업데이트되었습니다.

apps/admin/src/app/request/[id]/_components/Location/index.tsx (1)

1-1: 배럴 내보내기 패턴이 올바르게 적용되었습니다.

Location 컴포넌트를 재내보내기하는 표준 패턴입니다.

apps/web/app/places/[id]/PlaceDetailPage.tsx (1)

6-6: 공유 UI 패키지로의 임포트 경로 변경이 적절합니다.

Banner 컴포넌트를 디자인 시스템(@repo/ui)으로 이동하는 리팩토링의 일부입니다. 로직 변경 없이 모듈 해석 경로만 업데이트되었습니다.

apps/admin/src/app/request/[id]/_components/Menus/index.tsx (1)

1-1: 배럴 내보내기 패턴이 올바르게 적용되었습니다.

Menus 컴포넌트를 재내보내기하는 표준 패턴입니다.

pnpm-workspace.yaml (1)

68-74: 워크스페이스 카탈로그 항목 추가가 적절합니다.

새로운 의존성이 올바르게 추가되었습니다:

  • keen-slider: Banner 컴포넌트에서 사용 (UI 패키지로 이동됨)
  • react-naver-maps, @types/navermaps: Naver Maps 통합을 위한 패키지

다음 사항을 확인하세요:

  • 모든 패키지 버전(keen-slider@6.8.6, react-naver-maps@0.1.4, @types/navermaps@3.9.1) 존재 확인됨
  • npm audit로 알려진 취약점이 없는지 검증
apps/admin/src/app/request/[id]/_components/RejectModal/index.tsx (1)

1-1: LGTM!

표준 barrel export 패턴이 올바르게 적용되었습니다.

apps/admin/src/app/request/[id]/_components/ActionButtonGroup/index.tsx (1)

1-1: LGTM!

표준 barrel export 패턴이 올바르게 적용되었습니다.

apps/admin/src/app/request/[id]/_components/Location/Location.tsx (1)

9-9: 사용되지 않는 map 상태 확인 필요

setMap을 통해 map 인스턴스가 저장되지만, 상태 값이 실제로 사용되지 않습니다 (구조 분해 시 _로 무시됨). 향후 map 인스턴스를 사용할 계획이 있는지, 아니면 이 상태를 완전히 제거할 수 있는지 확인하세요.

apps/admin/package.json (2)

17-22: LGTM!

HeroUI 컴포넌트, Naver Maps, 그리고 유틸리티 패키지 의존성이 올바르게 추가되었습니다. catalog 기반 버전 관리를 사용하는 것은 좋은 방법입니다.


34-34: LGTM!

Naver Maps 타입 정의가 devDependencies에 올바르게 추가되었습니다.

packages/utils/eslint.config.js (1)

1-12: LGTM!

ESLint 설정이 올바르게 구성되었습니다. TypeScript 파일에 대해 react/prop-types를 비활성화하는 것은 TypeScript가 타입 체킹을 제공하므로 적절한 선택입니다.

apps/admin/src/app/request/[id]/RequestDetailPage.tsx (3)

29-29: 하드코딩된 제목을 동적 데이터로 교체 필요

현재 제목이 "우돈탄 다산본점"으로 하드코딩되어 있습니다. API 작업 시 동적 데이터로 교체하시기 바랍니다.


32-32: 빈 배너 contents 검증 필요

Banner 컴포넌트에 빈 배열이 전달되고 있습니다. API 연동 시 실제 배너 이미지 데이터가 제공되는지 확인하시기 바랍니다.


17-82: UI 구성 잘 되어 있습니다

컴포넌트 구조가 명확하고, useDisclosure를 통한 모달 제어가 적절하게 구현되어 있습니다. 헤더, 스크롤 영역, 액션 버튼의 레이아웃이 잘 구성되어 있습니다.

apps/admin/src/app/request/[id]/_components/Menus/Menu.tsx (1)

10-27: LGTM!

메뉴 아이템 컴포넌트가 깔끔하게 구현되어 있습니다. 가격 포맷팅과 추천 메뉴 표시 로직이 적절합니다.

apps/admin/src/app/page.tsx (1)

27-38: 레이아웃 구성 잘 되어 있습니다

헤더와 스크롤 영역을 사용한 레이아웃이 적절하게 구성되어 있으며, 캠퍼스별로 요청 목록을 그룹화한 구조가 명확합니다.

apps/admin/src/app/request/[id]/_components/ActionButtonGroup/ActionButtonGroup.tsx (2)

23-33: 등록 버튼 핸들러 구현 필요

등록 버튼에 onClick 핸들러가 없습니다. API 연동 작업 시 등록 로직을 구현하시기 바랍니다.


9-35: 버튼 그룹 UI 구현 잘 되어 있습니다

COLOR_VARIANTS를 활용한 일관된 스타일링과 cn 유틸리티를 통한 클래스 조합이 적절합니다.

apps/admin/src/app/request/[id]/_components/Description/Description.tsx (2)

17-19: 하드코딩된 태그를 동적 데이터로 교체 필요

"가성비 좋은" 태그가 하드코딩되어 있습니다. API 연동 시 RequestDetail 타입에 tags 필드를 추가하고 동적으로 렌더링하도록 개선하시기 바랍니다.


10-22: 설명 섹션 구현 적절합니다

whitespace-pre-wrap을 사용하여 줄바꿈을 적절히 처리하고 있으며, 컴포넌트 구조가 명확합니다.

apps/web/package.json (1)

24-24: 의존성 변경 사항 적절합니다

@repo/utils 추가 및 catalog를 통한 버전 관리로의 전환이 모노레포 구조에 적합합니다. keen-slider 제거가 의도된 변경인지 확인하시기 바랍니다.

Also applies to: 36-36, 52-52

apps/admin/src/components/RequestListItem/RequestListItem.tsx (1)

7-30: LGTM!

리스트 아이템 컴포넌트가 깔끔하게 구현되어 있습니다. 네이티브 <a> 태그를 사용하여 접근성을 고려한 점이 좋습니다.

apps/admin/src/app/layout.tsx (2)

14-18: 로컬 폰트 설정 적절합니다

Pretendard 가변 폰트를 로컬에서 로드하는 설정이 적절하게 구성되어 있습니다. display: 'swap'을 사용하여 폰트 로딩 중 텍스트가 보이지 않는 문제를 방지하고 있습니다.


20-40: 레이아웃 구조 잘 구성되어 있습니다

HeroUIProvider와 NaverMapProvider를 적절히 조합하여 앱 전역 설정을 제공하고 있으며, 웹 앱과 일관된 레이아웃 구조를 유지하고 있습니다.

apps/admin/src/app/request/[id]/_components/Menus/Menus.tsx (1)

24-46: LGTM!

빈 메뉴 상태 처리와 조건부 렌더링 로직이 잘 구현되어 있습니다.

apps/admin/src/app/request/[id]/_api/types.ts (1)

1-38: LGTM!

타입 정의가 명확하고 일관성 있게 구조화되어 있습니다. type-only import 사용도 적절합니다.

import { ReactNode } from 'react'

export const NaverMapProvider = ({ children }: { children: ReactNode }) => {
const ncpKeyId = process.env.NEXT_PUBLIC_NAVER_MAP_ID || ''
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

환경 변수 누락 시 처리 검증 필요

NEXT_PUBLIC_NAVER_MAP_ID가 설정되지 않은 경우 빈 문자열이 전달됩니다. Naver Maps API가 빈 문자열을 허용하는지, 아니면 런타임 오류가 발생할 수 있는지 확인하세요. 필요한 경우 환경 변수가 누락되었을 때 경고를 표시하거나 에러를 던지는 것을 고려하세요.

다음 스크립트로 환경 변수 설정을 확인할 수 있습니다:


🏁 Script executed:

#!/bin/bash
# Description: Check for NEXT_PUBLIC_NAVER_MAP_ID usage and .env configuration

# Search for environment variable references
rg -n "NEXT_PUBLIC_NAVER_MAP_ID" --type-add 'env:*.env*' --type env

# Check if .env files exist and contain the variable
fd -e env -e env.local -e env.development --exec cat {}

Length of output: 148


다음 스크립트로 코드와 .env* 파일에서 NEXT_PUBLIC_NAVER_MAP_ID 사용 및 정의 여부를 확인하세요.


🏁 Script executed:

#!/bin/bash
echo "1) 코드에서 process.env.NEXT_PUBLIC_NAVER_MAP_ID 사용 검색"
rg -nP "process\.env\.NEXT_PUBLIC_NAVER_MAP_ID"

echo "2) 코드에서 NEXT_PUBLIC_NAVER_MAP_ID 문자 검색"
rg -n "NEXT_PUBLIC_NAVER_MAP_ID"

echo "3) 리포지토리 루트 및 하위에서 숨김 .env* 파일 목록"
fd --hidden --glob ".env*" --max-depth 2

echo "4) .env* 파일에서 변수 정의 검색"
fd --hidden --glob ".env*" --max-depth 2 -x grep -Hn "NEXT_PUBLIC_NAVER_MAP_ID" {}

Length of output: 550


환경 변수 필수 설정 및 누락 처리
현재 process.env.NEXT_PUBLIC_NAVER_MAP_ID.env*에 정의되어 있지 않아 빈 문자열이 기본값으로 할당됩니다. 빌드/배포 환경에서 반드시 해당 변수를 설정하도록 보장하거나, 누락 시 예외를 던지거나 명확한 에러 메시지를 표시하도록 처리하세요.

🤖 Prompt for AI Agents
In apps/admin/src/app/NaverMapProvider.tsx around line 7, the assignment const
ncpKeyId = process.env.NEXT_PUBLIC_NAVER_MAP_ID || '' silently falls back to an
empty string when the env var is missing; change this to validate presence at
startup and fail fast: read process.env.NEXT_PUBLIC_NAVER_MAP_ID, and if it's
undefined/empty throw a clear Error (or process.exit with a logged error) that
includes the variable name and instructions to set it, so builds/deploys will
fail when the required env var is not provided.

Comment on lines +53 to +54
{recommendedMenu.map((menu, index) => (
<Menu key={index} menu={menu} />
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

배열 인덱스를 key로 사용하지 마세요.

index를 key로 사용하면 리스트의 항목이 추가/제거/재정렬될 때 React가 컴포넌트를 올바르게 식별하지 못해 렌더링 버그나 성능 문제가 발생할 수 있습니다.

메뉴 객체에 고유 식별자가 없다면 nameprice를 조합하여 key를 생성하거나, 백엔드 API에 고유 ID 필드 추가를 요청하세요:

-{recommendedMenu.map((menu, index) => (
-  <Menu key={index} menu={menu} />
+{recommendedMenu.map((menu) => (
+  <Menu key={`${menu.name}-${menu.price}`} menu={menu} />
 ))}

-{unRecommendedMenu.map((menu, index) => (
-  <Menu key={index} menu={menu} />
+{unRecommendedMenu.map((menu) => (
+  <Menu key={`${menu.name}-${menu.price}`} menu={menu} />
 ))}

더 나은 해결책은 Menu 타입에 id 또는 menuId 필드를 추가하는 것입니다.

Also applies to: 59-60

🤖 Prompt for AI Agents
In apps/admin/src/app/request/[id]/_components/Menus/Menus.tsx around lines
53-54 (also applies to 59-60): the code uses the array index as React key which
can cause rendering bugs when items change; replace index keys with a stable
unique key sourced from the menu object (preferably add/use menu.id or menuId),
and if no id exists generate a deterministic key from stable fields such as
`${menu.name}-${menu.price}`; update both map calls to use that stable key and,
if possible, request/extend the Menu type and backend to include a proper unique
identifier.

Comment on lines +13 to +16
type Props = {
isOpen: boolean
onOpenChange: VoidFunction
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

onOpenChange 타입이 HeroUI 모달 API와 일치하지 않습니다.

HeroUI의 Modal 컴포넌트는 onOpenChange에 새로운 open 상태를 boolean 인자로 전달합니다. VoidFunction 대신 (isOpen: boolean) => void를 사용해야 합니다.

다음 diff를 적용하여 타입을 수정하세요:

 type Props = {
   isOpen: boolean
-  onOpenChange: VoidFunction
+  onOpenChange: (isOpen: boolean) => void
 }
📝 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
type Props = {
isOpen: boolean
onOpenChange: VoidFunction
}
type Props = {
isOpen: boolean
onOpenChange: (isOpen: boolean) => void
}
🤖 Prompt for AI Agents
apps/admin/src/app/request/[id]/_components/RejectModal/RejectModal.tsx around
lines 13 to 16: the Props type currently declares onOpenChange as VoidFunction
but HeroUI's Modal passes the new open state (boolean) to its onOpenChange
handler; change the type to (isOpen: boolean) => void and update any local
handler signatures/usages to accept the boolean parameter accordingly.

Comment on lines +18 to +19
export const RejectModal = ({ isOpen, onOpenChange }: Props) => {
const [value, setValue] = useState('')
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

모달이 닫힐 때 입력 상태를 초기화하세요.

현재 구현에서는 모달을 닫았다가 다시 열면 이전 입력값이 그대로 남아있습니다. 사용자 경험을 위해 모달이 닫힐 때 상태를 초기화해야 합니다.

다음과 같이 useEffect로 초기화 로직을 추가하세요:

+import { useState, useEffect } from 'react'
-import { useState } from 'react'

 export const RejectModal = ({ isOpen, onOpenChange }: Props) => {
   const [value, setValue] = useState('')
+
+  useEffect(() => {
+    if (!isOpen) {
+      setValue('')
+    }
+  }, [isOpen])
📝 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
export const RejectModal = ({ isOpen, onOpenChange }: Props) => {
const [value, setValue] = useState('')
import { useState, useEffect } from 'react'
export const RejectModal = ({ isOpen, onOpenChange }: Props) => {
const [value, setValue] = useState('')
useEffect(() => {
if (!isOpen) {
setValue('')
}
}, [isOpen])
// …rest of the component
}
🤖 Prompt for AI Agents
In apps/admin/src/app/request/[id]/_components/RejectModal/RejectModal.tsx
around lines 18-19, the input state is not cleared when the modal closes, so add
a useEffect that watches the isOpen prop and calls setValue('') when isOpen
becomes false (i.e., on modal close) to reset the input; implement the effect at
the top level of the component, import useEffect if needed, and ensure it only
resets on close (not on open) to preserve expected behavior.

Comment on lines +29 to +31
<button
// Todo: 등록 거절 api 연결
onClick={() => {}}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

API 연동 전 필수 개선사항을 추가하세요.

onClick 핸들러는 UI 작업 단계에서 예상되는 부분이지만, API 연동 시 다음 사항들이 필요합니다:

  • 빈 입력값 검증 (사용자가 사유 없이 제출하는 것을 방지)
  • 로딩 상태 관리
  • 에러 핸들링
  • 성공 시 모달 닫기 및 상태 초기화

API 연동 시 다음과 같은 구조를 고려하세요:

const [isSubmitting, setIsSubmitting] = useState(false)

const handleSubmit = async () => {
  if (!value.trim()) {
    // 빈 값 검증
    return
  }
  
  try {
    setIsSubmitting(true)
    // await rejectRequest(value)
    setValue('') // 성공 시 초기화
    onOpenChange(false)
  } catch (error) {
    // 에러 처리
  } finally {
    setIsSubmitting(false)
  }
}
🤖 Prompt for AI Agents
In apps/admin/src/app/request/[id]/_components/RejectModal/RejectModal.tsx
around lines 29-31, the button has an empty onClick handler; implement a
handleSubmit that validates non-empty reason (trim check and early return with
user feedback), manages a local isSubmitting state (set true before API call,
false in finally), calls the reject API (await rejectRequest(value)), handles
errors in catch (show toast/log), and on success resets the input value and
closes the modal via onOpenChange(false); wire button onClick to this handler
and disable the button when isSubmitting.

</Text>
<ul>
{requestList.map((request) => (
<RequestListItem key={request.placeName} {...request} />
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

고유하지 않을 수 있는 key 사용

placeName을 key로 사용하고 있는데, 이름이 중복될 수 있어 React의 key prop 요구사항을 위반할 수 있습니다. placeId가 고유 식별자라면 이를 key로 사용하는 것이 더 적합합니다.

다음 diff를 적용하여 placeId를 key로 사용하세요:

-        <RequestListItem key={request.placeName} {...request} />
+        <RequestListItem key={request.placeId} {...request} />
📝 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
<RequestListItem key={request.placeName} {...request} />
<RequestListItem key={request.placeId} {...request} />
🤖 Prompt for AI Agents
In apps/admin/src/components/RequestsByCampus/RequestsByCampus.tsx around line
19, the code uses request.placeName as the React key which may not be unique;
replace it with request.placeId (the unique identifier) as the key to satisfy
React's key requirements and prevent rendering issues—ensure placeId is present
on request (or use a stable fallback only if absolutely necessary).

Comment on lines +1 to +7
export const CAMPUS = {
SINGWAN: '신관',
CHEANAN: '천안',
YESAN: '예산',
} as const

export type CampusType = keyof typeof CAMPUS
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

캠퍼스 상수 정의가 적절합니다.

as const를 사용한 타입 안전성 확보와 keyof typeof를 활용한 타입 추론이 잘 적용되었습니다.

다음 스크립트로 web 앱의 캠퍼스 상수와 일관성을 확인하는 것을 권장합니다:


🏁 Script executed:

#!/bin/bash
# web 앱과 admin 앱의 캠퍼스 상수 비교
echo "=== Admin campus constants ==="
cat apps/admin/src/consts/campus.ts

echo -e "\n=== Web campus constants ==="
rg -n "CAMPUS.*=" apps/web --type ts -A 5

Length of output: 3233


캠퍼스 상수 키 불일치

  • apps/admin/src/consts/campus.ts 에서 CHEANAN, apps/web/app/_constants/campus.ts 에서 cheonan 철자·대소문자 불일치
    상수 정의를 동일하게 조정하세요.
🤖 Prompt for AI Agents
In apps/admin/src/consts/campus.ts lines 1-7, the CAMPUS key is misspelled/cased
as CHEANAN while apps/web/app/_constants/campus.ts uses cheonan; rename the
CAMPUS key to match the canonical spelling/casing used elsewhere (cheonan) so
the exported keys are identical across projects, update the CampusType if
necessary, and search & update any code references/imports that use the old
CHEANAN key to the new cheonan key to avoid type errors and runtime mismatches.

@@ -0,0 +1,16 @@
export const BASE_URL = process.env.NEXT_PUBLIC_API_URL || ''
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

BASE_URL 폴백 처리가 위험합니다.

BASE_URL이 빈 문자열로 폴백되면 API 호출이 실패하거나 예상치 못한 상대 경로를 사용할 수 있습니다. 환경 변수가 없을 때 적절한 에러 처리나 기본값 설정이 필요합니다.

다음과 같이 개선하는 것을 권장합니다:

-export const BASE_URL = process.env.NEXT_PUBLIC_API_URL || ''
+export const BASE_URL = process.env.NEXT_PUBLIC_API_URL
+
+if (!BASE_URL) {
+  throw new Error('NEXT_PUBLIC_API_URL environment variable is required')
+}

또는 개발 환경에서 사용할 기본 URL을 제공:

-export const BASE_URL = process.env.NEXT_PUBLIC_API_URL || ''
+export const BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000'
📝 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
export const BASE_URL = process.env.NEXT_PUBLIC_API_URL || ''
export const BASE_URL = process.env.NEXT_PUBLIC_API_URL
if (!BASE_URL) {
throw new Error('NEXT_PUBLIC_API_URL environment variable is required')
}
🤖 Prompt for AI Agents
In apps/admin/src/consts/path.ts around line 1, BASE_URL currently falls back to
an empty string which can cause failed or unexpected relative API calls; change
the logic to either throw a clear error during initialization when
NEXT_PUBLIC_API_URL is not set, or supply a safe default only in development
(e.g., use process.env.NEXT_PUBLIC_API_URL if present, otherwise if
process.env.NODE_ENV === 'development' set a localhost dev URL, else throw an
error). Ensure the module exports a valid URL string or fails fast with a
descriptive error so callers never receive an empty string.

Comment on lines 1 to 16
{
"name": "@repo/utils",
"version": "0.0.0",
"scripts": {
"lint": "eslint . --max-warnings 0"
},
"peerDependencies": {
"react": "^19"
},
"devDependencies": {
"@repo/eslint-config": "workspace:*",
"@repo/typescript-config": "workspace:*",
"eslint": "catalog:",
"typescript": "catalog:"
}
} No newline at end of file
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

패키지 엔트리 포인트 필드 확인이 필요합니다.

패키지 구성은 전반적으로 적절하나, main, types, 또는 exports 필드가 정의되지 않았습니다. 이는 모듈 해석에 영향을 줄 수 있습니다.

다음 스크립트로 package.json에 적절한 엔트리 포인트가 설정되어 있는지 확인하세요:


🏁 Script executed:

#!/bin/bash
# packages/utils의 package.json에서 main, types, exports 필드 확인
cat packages/utils/package.json | jq '{main, types, exports, name}'

# packages/utils의 실제 내보내기 파일 구조 확인
fd -t f -e ts -e tsx . packages/utils --exec echo "File: {}"

Length of output: 160


main·types·exports 필드 추가 필요
packages/utils/package.json에 main, types, exports를 정의해 모듈 진입점을 명시하세요.

🤖 Prompt for AI Agents
In packages/utils/package.json lines 1-16, the package is missing main, types
and exports fields which declare the module entry points; add a "main" pointing
to the CJS build (e.g. "dist/index.cjs.js"), add "types" pointing to the
declaration file (e.g. "dist/index.d.ts"), and add an "exports" map that exposes
both import and require entrypoints plus the types (e.g. "." -> { "import":
"./dist/index.esm.js", "require": "./dist/index.cjs.js", "types":
"./dist/index.d.ts" }); adjust filenames to match your build output and ensure
these fields are added alongside existing fields in package.json.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5038e66 and 4eaefa3.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (4)
  • apps/admin/src/app/request/[id]/_api/types.ts (1 hunks)
  • apps/admin/src/app/request/[id]/_utils/toLatLng.ts (1 hunks)
  • apps/web/app/map/_components/CampusButtom/CampusButtonBax.tsx (1 hunks)
  • apps/web/app/map/_hooks/useCenterMapToCurrentLocation.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/admin/src/app/request/[id]/_api/types.ts (1)
apps/admin/src/app/request/[id]/_utils/toLatLng.ts (1)
  • Coord (1-1)
🔇 Additional comments (2)
apps/web/app/map/_hooks/useCenterMapToCurrentLocation.ts (1)

1-1: 절대 경로에서 상대 경로로 변경된 import를 확인하세요.

이 파일도 toLatLng import 경로가 절대 경로에서 상대 경로로 변경되었습니다. 앞서 언급한 검증 스크립트를 통해 일관성을 확인하세요.

apps/admin/src/app/request/[id]/_api/types.ts (1)

1-38: 타입 정의가 잘 구성되어 있습니다.

타입 정의가 명확하고 적절하게 구조화되어 있습니다:

  • type-only import를 올바르게 사용
  • export할 타입(Request, RequestDetail)과 내부 타입(Photo, Menu, Category, Tag)을 적절히 분리
  • 필드명이 명확하고 일관성 있음

다만, Line 2의 Coord import는 toLatLng.ts에 대한 의존성을 가지는데, 해당 유틸리티가 공유 패키지로 이동되면 이 import 경로도 함께 업데이트해야 합니다.

Comment on lines +1 to +7
export type Coord = { latitude: number; longitude: number }
type LatLng = { lat: number; lng: number }

export const toLatLng = ({ latitude, longitude }: Coord): LatLng => ({
lat: latitude,
lng: longitude,
})
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

코드 중복 및 PR 설명 불일치를 확인하세요.

이 유틸리티는 간단하고 올바르게 구현되었지만 다음 문제들이 있습니다:

  1. 코드 중복: toLatLng 유틸리티가 web 앱과 admin 앱 모두에 존재하는 것으로 보입니다. 이는 중복 코드이며 유지보수성을 저하시킵니다.
  2. PR 설명 불일치: PR 설명에서는 "toLatLng.ts를 @repo/utils로 이동"했다고 명시되어 있지만, 실제로는 apps/admin/src/app/request/[id]/_utils/에 위치하고 있습니다.

다음 스크립트를 실행하여 toLatLng 유틸리티의 중복 여부를 확인하세요:

PR 설명에 따라 이 유틸리티를 @repo/utils로 이동하고 admin과 web 앱 모두에서 공유하는 것을 권장합니다.


🏁 Script executed:

#!/bin/bash
# Description: toLatLng 유틸리티의 중복 구현 확인

# 모든 toLatLng 파일 검색
fd -t f "toLatLng.ts" 

# 각 toLatLng 함수 구현 검색
ast-grep --pattern $'export const toLatLng = ($$$) => $$$'

# @repo/utils에 toLatLng가 있는지 확인
fd -t f "toLatLng.ts" packages/

Length of output: 86


공통 유틸리티로 toLatLng 이동 및 중복 제거

  • apps/web와 apps/admin에 중복된 toLatLng.ts를 packages/utils(또는 @repo/utils)로 옮기고 각 앱에서 해당 모듈을 참조하도록 수정하세요.
  • PR 설명 “@repo/utils로 이동”과 실제 위치 불일치도 함께 정정 필요합니다.
🤖 Prompt for AI Agents
In apps/admin/src/app/request/[id]/_utils/toLatLng.ts lines 1-7: this toLatLng
utility is duplicated across apps/web and apps/admin — move the function and the
Coord/LatLng types into the shared utils package (packages/utils or
@repo/utils), delete the duplicate files in both apps, and update all imports in
apps/admin and apps/web to import from the shared package (e.g. from
"@repo/utils"). Also update the PR description to match the actual move location
(mention @repo/utils or packages/utils explicitly) so it matches the code
changes.

CAMPUS_LOCATION,
} from '@/_constants/campus'
import { toLatLng } from '@/map/_utils/toLatLng'
import { toLatLng } from '../../_utils/toLatLng'
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

절대 경로에서 상대 경로로 변경된 import를 확인하세요.

import 경로가 절대 경로(@/map/_utils/toLatLng)에서 상대 경로(../../_utils/toLatLng)로 변경되었습니다. 이 변경이 의도적이며 프로젝트 전반에 걸쳐 일관되게 적용되었는지 확인이 필요합니다.

다음 스크립트를 실행하여 toLatLng 유틸리티의 모든 import가 일관되게 업데이트되었는지 확인하세요:


🏁 Script executed:

#!/bin/bash
# Description: toLatLng import 경로의 일관성 확인

# apps/web 내의 모든 toLatLng import 검색
rg -n "import.*toLatLng" --type=ts --type=tsx -g "apps/web/**"

Length of output: 27


다음 스크립트를 실행하여 toLatLng import 경로 일관성을 다시 확인하세요:


🏁 Script executed:

#!/bin/bash
# Description: toLatLng import 경로의 일관성 재확인

rg -n "import.*toLatLng" -g "apps/web/**/*.{ts,tsx}"

Length of output: 527


toLatLng import 경로 스타일 일관화 필요.

프로젝트 전반에 절대(@/map/_utils/toLatLng)와 상대(../../_utils/toLatLng) 경로가 혼용되고 있습니다. 다음 파일의 import를 모두 상대 경로로 변경하세요:

  • apps/web/app/map/_components/Marker/Marker.tsx:3
  • apps/web/app/places/[id]/_components/Location/Location.tsx:3

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (3)
apps/admin/src/app/_components/RequestsByCampus/RequestsByCampus.tsx (1)

18-20: 고유하지 않을 수 있는 key 사용

placeName을 key로 사용하고 있는데, 이는 중복될 수 있어 React의 key 요구사항을 위반할 수 있습니다. 실제로 page.tsx의 MOCK_DATA를 보면 동일한 placeName 값('짬뽕집')이 여러 번 사용되고 있습니다. placeId가 고유 식별자이므로 이를 key로 사용해야 합니다.

다음 diff를 적용하여 placeId를 key로 사용하세요:

-        <RequestListItem key={request.placeName} {...request} />
+        <RequestListItem key={request.placeId} {...request} />
apps/web/app/map/_components/CampusButtom/CampusButtonBax.tsx (1)

10-10: 이전 리뷰에서 지적된 import 경로 불일치 문제가 부분적으로 해결되었습니다.

이 파일의 import는 상대 경로로 변경되었으나, 이전 리뷰에서 언급된 다른 파일들(Marker.tsx, Location.tsx)도 동일하게 업데이트되었는지 확인이 필요합니다.

다음 스크립트로 남아있는 절대 경로 import를 확인하세요:

#!/bin/bash
# toLatLng 절대 경로 import 확인
rg -n "from ['\"]@/map/_utils/toLatLng['\"]" -g "apps/web/**/*.{ts,tsx}"
apps/admin/src/app/request/[id]/_utils/toLatLng.ts (1)

1-7: 이전 리뷰에서 지적된 코드 중복 및 PR 설명 불일치 문제가 아직 해결되지 않았습니다.

이전 리뷰에서 언급된 문제가 여전히 남아있습니다:

  1. toLatLng 유틸리티가 web과 admin 앱에 모두 존재하여 중복됩니다.
  2. PR 설명에는 "@repo/utils로 이동"했다고 명시되어 있으나, 실제로는 apps/admin 내부에 위치합니다.

공유 패키지(packages/utils 또는 @repo/utils)로 이동하여 중복을 제거하는 것을 권장합니다.

🧹 Nitpick comments (1)
apps/admin/src/app/page.tsx (1)

6-25: LGTM!

UI 테스트를 위한 목 데이터가 적절하게 구성되어 있습니다. 각 항목이 고유한 placeId를 가지고 있어 식별 가능합니다.

더 나은 UI 테스트를 위해 다양한 placeNameicon 값을 사용하는 것을 고려해보세요:

 const MOCK_DATA: Request[] = [
   {
     placeId: '1',
-    placeName: '짬뽕집',
-    icon: 'chinese',
+    placeName: '맛있는 짬뽕집',
+    icon: 'chinese',
     requestDate: '2025-10-29',
   },
   {
     placeId: '2',
-    placeName: '짬뽕집',
-    icon: 'chinese',
+    placeName: '피자 레스토랑',
+    icon: 'western',
     requestDate: '2025-10-29',
   },
   {
     placeId: '3',
-    placeName: '짬뽕집',
-    icon: 'chinese',
+    placeName: '김밥천국',
+    icon: 'korean',
     requestDate: '2025-10-29',
   },
 ]
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4eaefa3 and 6cd082b.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (13)
  • apps/admin/package.json (2 hunks)
  • apps/admin/src/app/_api/types.ts (1 hunks)
  • apps/admin/src/app/_components/RequestListItem/RequestListItem.tsx (1 hunks)
  • apps/admin/src/app/_components/RequestListItem/index.tsx (1 hunks)
  • apps/admin/src/app/_components/RequestsByCampus/RequestsByCampus.tsx (1 hunks)
  • apps/admin/src/app/_components/RequestsByCampus/index.tsx (1 hunks)
  • apps/admin/src/app/page.tsx (1 hunks)
  • apps/admin/src/app/request/[id]/_api/types.ts (1 hunks)
  • apps/admin/src/app/request/[id]/_utils/toLatLng.ts (1 hunks)
  • apps/web/app/map/_components/CampusButtom/CampusButtonBax.tsx (1 hunks)
  • apps/web/app/map/_hooks/useCenterMapToCurrentLocation.ts (1 hunks)
  • apps/web/package.json (2 hunks)
  • packages/ui/tsconfig.json (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/admin/src/app/_components/RequestsByCampus/index.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/admin/src/app/request/[id]/_api/types.ts
  • apps/admin/package.json
🧰 Additional context used
🧬 Code graph analysis (3)
apps/admin/src/app/_components/RequestListItem/RequestListItem.tsx (2)
apps/admin/src/app/_api/types.ts (1)
  • Request (3-8)
packages/ui/src/utils/cn.ts (1)
  • cn (19-21)
apps/admin/src/app/_components/RequestsByCampus/RequestsByCampus.tsx (2)
apps/admin/src/app/_api/types.ts (1)
  • Request (3-8)
apps/admin/src/app/_components/RequestListItem/RequestListItem.tsx (1)
  • RequestListItem (8-31)
apps/admin/src/app/page.tsx (2)
apps/admin/src/app/_api/types.ts (1)
  • Request (3-8)
apps/admin/src/app/_components/RequestsByCampus/RequestsByCampus.tsx (1)
  • RequestsByCampus (12-23)
🔇 Additional comments (7)
packages/ui/tsconfig.json (1)

4-5: rootDir 설정이 적절합니다.

src를 기준으로 선언 파일이 정리되면서 dist 산출물이 깔끔해질 것으로 보입니다. 추가 고려사항은 없습니다.

apps/web/package.json (1)

35-35: 버전 소스 일원화 👍

catalog: 태그로 전환해 워크스페이스 전반에서 버전 일관성을 유지하기 쉬워졌습니다. pnpm 카탈로그 정의만 최신으로 맞춰주시면 될 것 같아요.

Also applies to: 51-51

apps/admin/src/app/_components/RequestListItem/index.tsx (1)

1-1: LGTM!

표준 배럴 export 패턴이 올바르게 적용되었습니다.

apps/admin/src/app/_api/types.ts (1)

1-8: LGTM!

Request 타입 정의가 명확하고 올바릅니다. 관리자 요청 데이터를 표현하는데 필요한 필드들이 적절하게 정의되어 있습니다.

apps/admin/src/app/page.tsx (1)

29-36: LGTM!

페이지 구조가 올바르게 구성되어 있으며, 헤더와 스크롤 영역이 적절하게 배치되어 있습니다.

apps/admin/src/app/_components/RequestListItem/RequestListItem.tsx (1)

8-31: LGTM!

컴포넌트 구조가 올바르게 구현되어 있습니다. placeId를 사용한 링크 생성, 레이아웃 구성, 그리고 스타일링이 모두 적절합니다.

apps/web/app/map/_hooks/useCenterMapToCurrentLocation.ts (1)

1-1: LGTM!

import 경로가 상대 경로로 올바르게 변경되었습니다.

@leeleeleeleejun leeleeleeleejun merged commit 9852f45 into develop Oct 12, 2025
1 check passed
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.

[Feature] admin 관련 메인 페이지 및 등록 조회 상세 페이지 작업

2 participants