Feat/#48 admin 관련 메인 페이지 및 등록 조회 상세 페이지 UI 작업#49
Conversation
- catalog로 버전 의존성 관리
- placeId 필드 추가
…n과 pnpm-workspace.yaml을 수정
- 요청에 대한 등록, 거절 기능 버튼들
- 거절 사유 등록
WalkthroughAdmin 앱에 HeroUI와 Naver Map을 도입하고 레이아웃/글꼴을 교체했다. 요청 상세 페이지와 관련 컴포넌트/타입/유틸을 신규 추가했다. Web 앱은 Banner import 경로 정리와 Keen Slider CSS/의존성 정리를 수행했다. UI 패키지는 Banner 스타일을 prefix화하고 keen-slider 의존성을 이동했다. 워크스페이스 카탈로그를 갱신했다. Changes
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 컨텍스트 제공
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 호출로 모달 닫기
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 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가 매 렌더링마다 실행되어 새 배열을 생성합니다.menusprop이 변경될 때만 재계산하도록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 여부 검토
Menu는Menus.tsx/Menu.tsx에서RequestDetail['menus'][0]방식으로 사용 중이며, 명시적 참조가 필요하다면export type Menu을 추가하세요.Photo,Category,Tag는 현재 직접 참조되지 않으므로, 추후 사용 시에만 export를 고려하면 됩니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
apps/admin/public/fonts/PretendardVariable.woff2is excluded by!**/*.woff2pnpm-lock.yamlis 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()반환 타입이 TailwindCSSConfig와 호환되는지 확인하세요. 타입 단언(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 || '' |
There was a problem hiding this comment.
🛠️ 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.
| {recommendedMenu.map((menu, index) => ( | ||
| <Menu key={index} menu={menu} /> |
There was a problem hiding this comment.
배열 인덱스를 key로 사용하지 마세요.
index를 key로 사용하면 리스트의 항목이 추가/제거/재정렬될 때 React가 컴포넌트를 올바르게 식별하지 못해 렌더링 버그나 성능 문제가 발생할 수 있습니다.
메뉴 객체에 고유 식별자가 없다면 name과 price를 조합하여 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.
| type Props = { | ||
| isOpen: boolean | ||
| onOpenChange: VoidFunction | ||
| } |
There was a problem hiding this comment.
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.
| 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.
| export const RejectModal = ({ isOpen, onOpenChange }: Props) => { | ||
| const [value, setValue] = useState('') |
There was a problem hiding this comment.
🛠️ 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.
| 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.
| <button | ||
| // Todo: 등록 거절 api 연결 | ||
| onClick={() => {}} |
There was a problem hiding this comment.
🛠️ 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} /> |
There was a problem hiding this comment.
고유하지 않을 수 있는 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.
| <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).
| export const CAMPUS = { | ||
| SINGWAN: '신관', | ||
| CHEANAN: '천안', | ||
| YESAN: '예산', | ||
| } as const | ||
|
|
||
| export type CampusType = keyof typeof CAMPUS |
There was a problem hiding this comment.
🧩 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 5Length 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 || '' | |||
There was a problem hiding this comment.
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.
| 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.
packages/utils/package.json
Outdated
| { | ||
| "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 |
There was a problem hiding this comment.
🧩 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.
There was a problem hiding this comment.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis 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의
Coordimport는toLatLng.ts에 대한 의존성을 가지는데, 해당 유틸리티가 공유 패키지로 이동되면 이 import 경로도 함께 업데이트해야 합니다.
| export type Coord = { latitude: number; longitude: number } | ||
| type LatLng = { lat: number; lng: number } | ||
|
|
||
| export const toLatLng = ({ latitude, longitude }: Coord): LatLng => ({ | ||
| lat: latitude, | ||
| lng: longitude, | ||
| }) |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
코드 중복 및 PR 설명 불일치를 확인하세요.
이 유틸리티는 간단하고 올바르게 구현되었지만 다음 문제들이 있습니다:
- 코드 중복:
toLatLng유틸리티가 web 앱과 admin 앱 모두에 존재하는 것으로 보입니다. 이는 중복 코드이며 유지보수성을 저하시킵니다. - 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' |
There was a problem hiding this comment.
🛠️ 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
4eaefa3 to
5038e66
Compare
- toLatLng 함수 이동
There was a problem hiding this comment.
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 설명 불일치 문제가 아직 해결되지 않았습니다.이전 리뷰에서 언급된 문제가 여전히 남아있습니다:
toLatLng유틸리티가 web과 admin 앱에 모두 존재하여 중복됩니다.- 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 테스트를 위해 다양한
placeName과icon값을 사용하는 것을 고려해보세요: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
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis 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 경로가 상대 경로로 올바르게 변경되었습니다.
#️⃣연관된 이슈
📝작업 내용
admin 메인 등록 조회 상세 페이지 퍼블리싱
admin과 web에서 공통으로 사용되는 로직들 packages폴더로 이동
스크린샷 (선택)
💬리뷰 요구사항(선택)
Summary by CodeRabbit
신기능
스타일
리팩터
잡무