Skip to content

Comments

20260115 #131 장바구니 빈 페이지 개발#134

Merged
Chuseok22 merged 10 commits intomainfrom
20260115_#131_장바구니_빈_페이지_개발
Jan 16, 2026

Hidden character warning

The head ref may contain hidden characters: "20260115_#131_\uc7a5\ubc14\uad6c\ub2c8_\ube48_\ud398\uc774\uc9c0_\uac1c\ubc1c"
Merged

20260115 #131 장바구니 빈 페이지 개발#134
Chuseok22 merged 10 commits intomainfrom
20260115_#131_장바구니_빈_페이지_개발

Conversation

@Chuseok22
Copy link
Member

@Chuseok22 Chuseok22 commented Jan 16, 2026

✨ 변경 사항


✅ 테스트


  • 수동 테스트 완료
  • 테스트 코드 완료

Summary by CodeRabbit

  • 새로운 기능

    • 주문 페이지 추가
    • 장바구니에 총액을 표시하는 결제 버튼 추가
    • 장바구니 상품 목록이 동적으로 렌더링되도록 개선
  • UI/스타일 개선

    • 결제 바 및 장바구니 목록 레이아웃/스타일 추가
    • 항목 간 구분선 등 목록 가독성 향상
  • 동작 개선

    • 장바구니로 이동할 때 브라우저 히스토리 동작 변경(내비게이션 방식 조정)

✏️ Tip: You can customize this high-level summary in your review settings.

@Chuseok22 Chuseok22 self-assigned this Jan 16, 2026
@Chuseok22 Chuseok22 linked an issue Jan 16, 2026 that may be closed by this pull request
@coderabbitai
Copy link

coderabbitai bot commented Jan 16, 2026

Walkthrough

인증, 장바구니, 메뉴 모듈을 클라이언트/서버 변형(.client / .server)으로 분리하고, 장바구니 페이지를 비동기화하여 서버에서 cart 정보를 가져와 Empty/Cart 뷰를 조건부로 렌더링합니다. 또한 주문 페이지와 여러 장바구니 관련 컴포넌트 및 타입 파일이 추가·이동되었습니다.

Changes

Cohort / File(s) 변경 요약
인증 API (클라이언트화)
src/features/auth/api/loginApi.client.ts, src/features/auth/api/logoutApi.client.ts
"use client" 지시문 추가로 클라이언트 실행 컨텍스트로 전환
인증 폼 import 업데이트
src/features/auth/components/form/LoginForm.tsx
loginApiloginApi.client로 import 경로 변경
메뉴 서비스 타입 분리
src/features/menu/types/hakgwanType.ts, src/features/menu/services/hakgwanMenuService.server.ts, src/app/(header-nav)/hakgwan/page.tsx, src/features/menu/components/MenuPageContainer.tsx
HakgwanMenuData를 새 types/hakgwanType로 이동 및 서비스 import 경로를 .server 변형으로 변경
메뉴 API 서버 경계
src/features/menu/api/cafeteriaApi.server.ts, src/features/menu/api/categoryApi.server.ts, src/features/menu/api/menuApi.server.ts
server-only 추가로 서버 전용 모듈 지정
장바구니 API 분리
src/features/cart/api/cartApi.client.ts, src/features/cart/api/cartApi.server.ts
클라이언트용 cartApi.client"use client" 추가 및 서버용 getCart() 구현 파일 추가
장바구니 서비스 분리
src/features/cart/services/cartService.client.ts, src/features/cart/services/cartService.server.ts
클라이언트/서버 서비스 분리: 클라이언트는 .client API 사용, 서버는 getCartInfo() 추가
훅 및 정렬 로직
src/features/cart/hooks/useCart.ts
cartServicecartService.client로 변경, useMemo로 정렬된 cartInfo 생성 및 의존성 업데이트
장바구니 페이지 리팩토링
src/app/(header-only)/cart/page.tsx, src/features/cart/components/view/CartView.tsx, src/features/cart/components/list/CartMenuList.tsx, src/features/cart/components/bar/PaymentBar.tsx
CartPage를 async로 변경해 getCartInfo()를 호출하고 빈 상태/실제 뷰를 조건부 렌더링; CartView, CartMenuList, PaymentBar 컴포넌트 추가
장바구니 스타일 추가/변경
src/features/cart/components/view/CartView.module.css, src/features/cart/components/list/CartMenuList.module.css, src/features/cart/components/bar/PaymentBar.module.css, src/features/cart/components/card/CartMenuCard.module.css
새로운 CSS 모듈 추가 및 CartMenuCard에서 border 제거
새 페이지 추가
src/app/(header-only)/order/page.tsx
클라이언트 페이지 OrderPage 추가 (홈으로 이동하는 버튼 포함)
내비게이션 동작 변경
src/features/menu/components/bar/OrderSummaryBar.tsx, src/app/(header-nav)/my/page.tsx
router.replace()router.push() 변경 및 logoutApi import를 .client 변형으로 변경

Sequence Diagram(s)

sequenceDiagram
  participant Client as Browser (Client)
  participant CartService as cartService.server
  participant API as /api/cart (apiServer)

  Client->>CartService: 호출 (getCartInfo)
  CartService->>API: GET /api/cart
  API-->>CartService: CartApiResponse
  CartService-->>Client: CartInfo (toCartInfo 변환)
  Client->>Client: CartView 렌더링 (EmptyCartView 또는 CartMenuList + PaymentBar)
  Client->>Router: PaymentBar 클릭 → /order 네비게이션
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

feat

Poem

🐰
바삭한 코드로 밤을 새워,
클라이언트와 서버 길 나눴네.
장바구니 불러와 홉! 띄우면,
결제 버튼은 폴짝 /order로—
토끼가 기뻐 춤춰요 🥕✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning 제목은 빈 장바구니 페이지 개발을 언급하지만, 실제 변경사항은 클라이언트/서버 모듈 분리, 타입 정리, 라우팅 개선 등 훨씬 광범위합니다. 제목을 변경사항의 실제 범위를 반영하도록 수정하세요. 예: '클라이언트/서버 모듈 분리 및 장바구니 페이지 개발' 또는 더 구체적인 주요 변경사항을 포함하세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 6.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

🧹 Recent nitpick comments
src/features/menu/components/MenuPageContainer.tsx (1)

12-12: 이전 리뷰 이슈 해결 확인 - import type 사용 권장

.server 모듈 import 문제가 해결되었습니다. 타입만 사용하므로 import type을 사용하면 번들 최적화와 의도 명확화에 도움이 됩니다.

♻️ 제안된 수정
-import { HakgwanMenuData } from "@/features/menu/types/hakgwanType";
+import type { HakgwanMenuData } from "@/features/menu/types/hakgwanType";
src/app/(header-nav)/hakgwan/page.tsx (1)

3-5: LGTM! 서버/클라이언트 분리 패턴이 올바르게 적용되었습니다.

서버 컴포넌트에서 .server 모듈을 import하고, 타입은 공유 타입 모듈에서 가져오는 패턴이 올바릅니다. 타입 import에도 import type을 사용하면 일관성이 향상됩니다.

♻️ 제안된 수정
 import { getHakgwanMenus } from "@/features/menu/services/hakgwanMenuService.server";
 import MenuPageContainer from "@/features/menu/components/MenuPageContainer";
-import { HakgwanMenuData } from "@/features/menu/types/hakgwanType";
+import type { HakgwanMenuData } from "@/features/menu/types/hakgwanType";

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between da93da1 and ef27a7e.

📒 Files selected for processing (4)
  • src/app/(header-nav)/hakgwan/page.tsx
  • src/features/menu/components/MenuPageContainer.tsx
  • src/features/menu/services/hakgwanMenuService.server.ts
  • src/features/menu/types/hakgwanType.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/features/menu/services/hakgwanMenuService.server.ts
🧰 Additional context used
🧬 Code graph analysis (1)
src/features/menu/types/hakgwanType.ts (2)
src/features/menu/types/categoryType.ts (1)
  • CategoryItem (7-10)
src/features/menu/types/menuType.ts (1)
  • MenuItem (12-20)
🔇 Additional comments (1)
src/features/menu/types/hakgwanType.ts (1)

1-7: LGTM! 타입 중앙화 잘 처리되었습니다.

이전 리뷰에서 지적된 .server 모듈에서 클라이언트 컴포넌트로 타입을 import하는 문제를 해결하기 위해 HakgwanMenuData를 별도의 타입 모듈로 분리한 것은 올바른 접근입니다. 서버/클라이언트 경계를 넘어 타입을 공유할 수 있게 되었습니다.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


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

🤖 Fix all issues with AI agents
In `@src/features/menu/components/MenuPageContainer.tsx`:
- Line 3: The client component MenuPageContainer.tsx is importing
HakgwanMenuData from hakgwanMenuService.server which causes a "Server-only
module imported in Client Component" build error; extract the HakgwanMenuData
interface into a shared types module (e.g., menuType.ts), remove the type
definition from hakgwanMenuService.server, and update MenuPageContainer.tsx to
use an import type { HakgwanMenuData } from the new types module so the client
only imports a type.
🧹 Nitpick comments (7)
src/features/cart/services/cartService.server.ts (1)

1-9: LGTM!

서버 서비스 모듈이 올바르게 구현되었습니다. "server-only" import로 서버 전용 코드임을 명확히 하고, cartApi.server에서 데이터를 가져와 toCartInfo로 변환하는 흐름이 적절합니다.

선택적 개선: 함수 시그니처에서 콜론 뒤에 공백을 추가하면 일관성이 향상됩니다.

♻️ 스타일 개선 제안
-export async function getCartInfo():Promise<CartInfo> {
+export async function getCartInfo(): Promise<CartInfo> {
src/features/cart/components/bar/PaymentBar.tsx (1)

18-23: 버튼에 type="button" 속성 추가를 권장합니다.

버튼의 기본 type은 "submit"이므로, 폼 내부에서 사용될 경우 의도치 않은 폼 제출이 발생할 수 있습니다. 명시적으로 type="button"을 지정하면 이런 문제를 방지할 수 있습니다.

♻️ 제안된 수정
      <button
+       type="button"
        onClick={onClick}
        className={styles.button}
      >
src/features/cart/hooks/useCart.ts (1)

65-68: getMenuQuantity에서 sortedCartInfo 사용은 불필요합니다.

getMenuQuantitymenuId로 아이템을 찾는 함수이므로 정렬 여부와 무관합니다. cartInfo를 직접 사용해도 동일한 결과를 얻을 수 있으며, 이 경우 sortedCartInfo가 변경될 때마다 불필요한 getMenuQuantity 재생성을 피할 수 있습니다.

다만, 현재 구현도 기능적으로 문제는 없으므로 선택적으로 고려해 주세요.

src/app/(header-only)/order/page.tsx (1)

5-22: 임시 페이지 - 스타일 일관성 및 접근성 개선을 권장합니다.

임시 주문 페이지로 보이지만, 몇 가지 개선점이 있습니다:

  1. 스타일 일관성: 프로젝트 내 다른 컴포넌트는 CSS 모듈을 사용하는데, 이 페이지는 인라인 Tailwind 클래스(bg-blue-400)를 사용하고 있습니다.
  2. 접근성: 버튼에 type="button" 속성 추가를 권장합니다.
  3. 불필요한 세미콜론: Line 22의 함수 끝 세미콜론은 불필요합니다.
♻️ 제안된 수정
-export default function OrderPage() {
+export default function OrderPage() {
   const router = useRouter();

   const onClick = () => {
     router.replace("/");
-  }
+  };
+
   return (
     <div>
       주문 페이지 입니다
       <button
+        type="button"
         className="bg-blue-400"
         onClick={onClick}
       >
         홈 화면 이동
       </button>
     </div>
   );
-};
+}
src/features/cart/components/view/CartView.module.css (1)

1-20: 모바일 안전 영역 고려를 추가하면 더 견고합니다.

홈 인디케이터 영역이 있는 기기(iOS 등)에서 바가 겹치지 않도록 safe-area inset을 반영하는 편이 좋습니다.

♻️ 제안 diff
 .container {
   display: flex;
   flex-direction: column;
   justify-content: center;
   width: 100%;

   padding-inline: 1.25rem;
   padding-top: 0.75rem;
-  padding-bottom: var(--payment-bar-height);
+  padding-bottom: calc(var(--payment-bar-height) + env(safe-area-inset-bottom));
 }

 .paymentBarWrapper {
   position: fixed;
-  bottom: 0;
+  bottom: env(safe-area-inset-bottom);
   left: 50%;
   transform: translateX(-50%);

   width: 100%;
   max-width: var(--container-3xl);
 }
src/app/(header-only)/cart/page.tsx (1)

7-10: CartView에 초기 cartInfo 전달 검토

서버에서 cartInfo를 가져오지만 CartView에는 전달하지 않아 클라이언트에서 다시 요청할 가능성이 있습니다. 중복 호출/일시적 불일치(서버 판정과 클라이언트 상태 차이)를 피하려면 초기 데이터를 CartView에 넘기거나 공용 상태로 재사용하는 방식을 고려해주세요.

src/features/cart/components/list/CartMenuList.tsx (1)

16-33: 리스트 key로 cartItemId 사용 권장

동일 메뉴가 옵션 등으로 여러 개 담길 가능성이 있으면 menuId는 중복될 수 있어 리렌더/상태 꼬임 위험이 있습니다. cartItemId가 제공되므로 더 안정적인 key로 사용하는 편이 안전합니다.

가능한 수정 예시
-        <React.Fragment key={item.menuId}>
+        <React.Fragment key={item.cartItemId}>
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 43c87e1 and da93da1.

📒 Files selected for processing (25)
  • src/app/(header-nav)/hakgwan/page.tsx
  • src/app/(header-nav)/my/page.tsx
  • src/app/(header-only)/cart/page.tsx
  • src/app/(header-only)/order/page.tsx
  • src/features/auth/api/loginApi.client.ts
  • src/features/auth/api/logoutApi.client.ts
  • src/features/auth/components/form/LoginForm.tsx
  • src/features/cart/api/cartApi.client.ts
  • src/features/cart/api/cartApi.server.ts
  • src/features/cart/components/bar/PaymentBar.module.css
  • src/features/cart/components/bar/PaymentBar.tsx
  • src/features/cart/components/card/CartMenuCard.module.css
  • src/features/cart/components/list/CartMenuList.module.css
  • src/features/cart/components/list/CartMenuList.tsx
  • src/features/cart/components/view/CartView.module.css
  • src/features/cart/components/view/CartView.tsx
  • src/features/cart/hooks/useCart.ts
  • src/features/cart/services/cartService.client.ts
  • src/features/cart/services/cartService.server.ts
  • src/features/menu/api/cafeteriaApi.server.ts
  • src/features/menu/api/categoryApi.server.ts
  • src/features/menu/api/menuApi.server.ts
  • src/features/menu/components/MenuPageContainer.tsx
  • src/features/menu/components/bar/OrderSummaryBar.tsx
  • src/features/menu/services/hakgwanMenuService.server.ts
💤 Files with no reviewable changes (1)
  • src/features/cart/components/card/CartMenuCard.module.css
🧰 Additional context used
🧬 Code graph analysis (20)
src/features/cart/components/list/CartMenuList.module.css (2)
src/features/menu/components/list/MenuList.tsx (3)
  • MenuList (12-40)
  • item (18-37)
  • MenuListProps (7-10)
src/features/cart/components/card/CartMenuCard.tsx (2)
  • CartMenuCard (21-61)
  • CartMenuCardProps (11-19)
src/features/cart/api/cartApi.client.ts (3)
src/features/cart/api/cartApi.ts (2)
  • upsertCart (16-18)
  • getCart (8-10)
src/features/cart/types/cartType.ts (4)
  • CartInfo (31-36)
  • CartApiResponse (15-20)
  • CartItemInfo (22-29)
  • CartItemApiResponse (6-13)
src/shared/lib/api/apiClient.ts (1)
  • ApiClient (8-75)
src/features/menu/components/MenuPageContainer.tsx (1)
src/features/menu/services/hakgwanMenuService.ts (1)
  • HakgwanMenuData (10-13)
src/features/menu/services/hakgwanMenuService.server.ts (2)
src/features/menu/services/hakgwanMenuService.ts (2)
  • getHakgwanMenus (15-32)
  • HakgwanMenuData (10-13)
src/features/menu/api/menuApi.ts (1)
  • getAllMenuByCafeteriaId (4-8)
src/features/auth/components/form/LoginForm.tsx (2)
src/features/auth/api/loginApi.ts (1)
  • login (4-6)
src/features/auth/types/loginTypes.ts (2)
  • LoginApiResponse (6-13)
  • LoginRequest (1-4)
src/app/(header-only)/cart/page.tsx (2)
src/features/cart/components/view/EmptyCartView.tsx (1)
  • EmptyCartView (5-38)
src/features/cart/services/cartService.ts (1)
  • getCartInfo (5-8)
src/features/auth/api/loginApi.client.ts (6)
src/features/auth/api/loginApi.ts (1)
  • login (4-6)
src/shared/lib/api/apiClient.ts (1)
  • ApiClient (8-75)
src/features/auth/types/loginTypes.ts (3)
  • LoginApiResponse (6-13)
  • LoginRequest (1-4)
  • LoginResponse (15-19)
src/features/auth/api/logoutApi.ts (1)
  • logout (3-5)
src/app/(plain)/login/page.tsx (1)
  • LoginPage (4-13)
src/app/api/auth/login/route.ts (1)
  • POST (9-52)
src/features/cart/api/cartApi.server.ts (5)
src/features/cart/api/cartApi.client.ts (1)
  • getCart (9-11)
src/features/cart/types/cartType.ts (3)
  • CartApiResponse (15-20)
  • CartInfo (31-36)
  • CartItemApiResponse (6-13)
src/shared/lib/api/apiServer.ts (1)
  • apiServer (160-160)
src/features/cart/api/cartApi.ts (2)
  • getCart (8-10)
  • upsertCart (16-18)
src/features/cart/services/cartService.ts (1)
  • upsertCartInfo (10-13)
src/features/cart/components/view/CartView.tsx (5)
src/features/cart/hooks/useCart.ts (1)
  • useCart (24-174)
src/features/cart/components/view/EmptyCartView.tsx (1)
  • EmptyCartView (5-38)
src/features/cart/components/list/CartMenuList.tsx (1)
  • CartMenuList (11-36)
src/features/cart/components/bar/PaymentBar.tsx (1)
  • PaymentBar (9-26)
src/features/cart/components/card/CartMenuCard.tsx (2)
  • CartMenuCard (21-61)
  • CartMenuCardProps (11-19)
src/features/cart/services/cartService.server.ts (3)
src/features/cart/types/cartType.ts (2)
  • CartInfo (31-36)
  • CartApiResponse (15-20)
src/features/cart/api/cartApi.server.ts (1)
  • getCart (9-11)
src/features/cart/utils/cartMapper.ts (2)
  • toCartInfo (3-12)
  • toCartItemInfo (14-23)
src/features/menu/api/menuApi.server.ts (3)
src/features/menu/api/menuApi.ts (1)
  • getAllMenuByCafeteriaId (4-8)
src/features/menu/types/menuType.ts (1)
  • MenuApiResponse (3-10)
src/shared/lib/api/apiServer.ts (1)
  • ApiServer (21-157)
src/features/auth/api/logoutApi.client.ts (3)
src/features/auth/api/logoutApi.ts (1)
  • logout (3-5)
src/app/api/auth/logout/route.ts (1)
  • POST (7-29)
src/shared/lib/api/apiClient.ts (1)
  • ApiClient (8-75)
src/features/cart/components/bar/PaymentBar.tsx (1)
src/shared/utils/number/utils.ts (1)
  • formatNumberWithComma (4-8)
src/features/cart/hooks/useCart.ts (1)
src/features/cart/types/cartType.ts (2)
  • CartItemInfo (22-29)
  • CartInfo (31-36)
src/features/cart/services/cartService.client.ts (3)
src/features/cart/api/cartApi.ts (2)
  • upsertCart (16-18)
  • getCart (8-10)
src/features/cart/services/cartService.ts (2)
  • upsertCartInfo (10-13)
  • getCartInfo (5-8)
src/features/cart/types/cartType.ts (1)
  • CartInfo (31-36)
src/features/menu/api/cafeteriaApi.server.ts (7)
src/features/menu/types/cafeteriaType.ts (1)
  • CafeteriaApiResponse (1-6)
src/features/menu/api/menuApi.ts (1)
  • getAllMenuByCafeteriaId (4-8)
src/features/menu/api/cafeteriaApi.ts (1)
  • getAllCafeteria (4-6)
src/features/menu/types/categoryType.ts (1)
  • CategoryApiResponse (1-5)
src/features/menu/services/hakgwanMenuService.ts (2)
  • cafeteria (18-18)
  • getHakgwanMenus (15-32)
src/features/menu/types/menuType.ts (1)
  • MenuApiResponse (3-10)
src/shared/lib/api/apiServer.ts (1)
  • ApiServer (21-157)
src/features/menu/components/bar/OrderSummaryBar.tsx (4)
src/features/cart/components/view/EmptyCartView.tsx (1)
  • EmptyCartView (5-38)
src/shared/components/header/Header.tsx (1)
  • Header (5-18)
src/features/cart/components/card/CartMenuCard.tsx (1)
  • CartMenuCard (21-61)
src/features/menu/components/button/CartButton.tsx (1)
  • CartButton (10-32)
src/app/(header-nav)/my/page.tsx (2)
src/features/auth/api/logoutApi.ts (1)
  • logout (3-5)
src/app/api/auth/logout/route.ts (1)
  • POST (7-29)
src/features/cart/components/list/CartMenuList.tsx (4)
src/features/cart/types/cartType.ts (2)
  • CartInfo (31-36)
  • CartItemInfo (22-29)
src/features/cart/components/card/CartMenuCard.tsx (2)
  • CartMenuCard (21-61)
  • CartMenuCardProps (11-19)
src/shared/components/bar/SeparationBar.tsx (1)
  • SeparationBar (3-7)
src/features/menu/components/list/MenuList.tsx (3)
  • MenuList (12-40)
  • item (18-37)
  • MenuListProps (7-10)
src/features/menu/api/categoryApi.server.ts (4)
src/features/menu/types/categoryType.ts (2)
  • CategoryApiResponse (1-5)
  • CategoryItem (7-10)
src/features/menu/types/menuType.ts (1)
  • MenuApiResponse (3-10)
src/features/menu/services/hakgwanMenuService.ts (1)
  • HakgwanMenuData (10-13)
src/shared/lib/api/apiServer.ts (1)
  • ApiServer (21-157)
🔇 Additional comments (18)
src/features/cart/api/cartApi.client.ts (1)

1-19: LGTM!

클라이언트 API 모듈로 올바르게 분리되었습니다. "use client" 지시문이 파일 상단에 적절히 배치되었고, apiClient를 사용하여 장바구니 API 호출을 수행합니다.

src/features/cart/services/cartService.client.ts (1)

1-14: LGTM!

클라이언트 서비스 모듈이 올바르게 구성되었습니다. "use client" 지시문과 함께 cartApi.client에서 API 함수를 import하여 클라이언트/서버 경계가 일관되게 유지됩니다.

src/features/menu/components/bar/OrderSummaryBar.tsx (1)

21-23: LGTM!

router.replace에서 router.push로의 변경이 적절합니다. 사용자가 장바구니 페이지에서 브라우저 뒤로 가기 버튼을 통해 메뉴 페이지로 돌아갈 수 있어 UX가 개선됩니다.

src/features/cart/api/cartApi.server.ts (1)

1-11: LGTM!

서버 전용 API 모듈이 올바르게 구성되었습니다. "server-only" import와 apiServer 사용이 적절합니다.

참고: 클라이언트 API(cartApi.client.ts)에는 upsertCart 함수가 포함되어 있지만 서버 API에는 getCart만 있습니다. 장바구니 수정이 클라이언트에서만 발생한다면 현재 구조가 적절하지만, 향후 서버 사이드에서 장바구니 수정이 필요할 경우 추가를 고려해 주세요.

src/features/cart/components/bar/PaymentBar.tsx (1)

9-25: LGTM!

컴포넌트 구조가 깔끔하고, props 인터페이스가 잘 정의되어 있습니다. formatNumberWithComma를 활용한 가격 포맷팅도 적절합니다.

src/features/cart/components/bar/PaymentBar.module.css (2)

1-39: LGTM!

스타일 정의가 깔끔하고, 디자인 시스템 변수를 일관성 있게 활용하고 있습니다.


41-43: @theme 블록 사용은 유효합니다.

Tailwind CSS v4에서 도입된 공식 문법이며, 프로젝트 전체에서 일관되게 사용되고 있습니다 (colors.css, motion.css, layouts.css 등). 코드에 문제가 없습니다.

src/features/cart/hooks/useCart.ts (1)

40-50: LGTM!

useMemo를 활용한 정렬 로직이 적절합니다. 원본 배열을 변경하지 않고 spread operator로 새 배열을 생성한 후 정렬하는 방식이 올바릅니다.

src/features/auth/api/logoutApi.client.ts (1)

1-6: LGTM!

클라이언트/서버 API 분리 패턴에 맞게 "use client" 지시자가 올바르게 추가되었습니다. 다른 클라이언트 API 파일들(loginApi.client.ts, cartApi.client.ts)과 일관된 구조입니다.

src/features/auth/api/loginApi.client.ts (1)

1-7: 클라이언트 모듈 분리 방향과 잘 맞습니다.

"use client" 추가로 실행 환경이 명확해져 좋아요.

src/app/(header-nav)/my/page.tsx (1)

3-5: 임포트 경로 변경이 의도와 일치합니다.

클라이언트 전용 API 모듈로의 전환이 자연스럽습니다.

src/features/auth/components/form/LoginForm.tsx (1)

10-15: 클라이언트 API 분리와 일관됩니다.

로직 변경 없이 경로만 정리되어 안정적입니다.

src/features/cart/components/view/CartView.tsx (1)

1-39: 장바구니 비어있음/채워짐 분기 처리가 명확합니다.

뷰 구성과 결제 바 노출 흐름이 이해하기 좋습니다.

src/features/menu/api/categoryApi.server.ts (1)

1-6: 서버 전용 경계 명확화 👍

server-only 추가로 서버 실행 맥락이 명확해졌고, 기존 API 호출 로직도 그대로라 문제 없어 보입니다.

src/features/menu/api/menuApi.server.ts (1)

1-8: 서버 전용 모듈 정렬 OK

server-only 추가로 서버 경계가 명확해졌고, requireAuth 설정도 그대로 유지되어 안정적입니다.

src/features/menu/api/cafeteriaApi.server.ts (1)

1-6: 서버 전용화 반영 확인 완료

서버 전용 지시자 추가 외에 동작 변화가 없어 안전한 변경입니다.

src/app/(header-nav)/hakgwan/page.tsx (1)

3-3: 서버 전용 서비스로의 import 전환 OK

페이지가 서버 전용 서비스에 직접 의존하도록 정리되어 일관성이 좋아졌습니다.

src/features/menu/services/hakgwanMenuService.server.ts (1)

1-3: 서버 API 모듈 통일 처리 완료

server 전용 API로의 import 정리가 잘 되어 있습니다. 로직 영향 없이 경계만 명확화되었습니다.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

"use client";

import { HakgwanMenuData } from "@/features/menu/services/hakgwanMenuService";
import { HakgwanMenuData } from "@/features/menu/services/hakgwanMenuService.server";
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

🌐 Web query:

Next.js Client Component importing .server module behavior error

💡 Result:

Short answer: Next.js prevents importing a .server (server-only) module into a Client Component — that pattern is unsupported and triggers a build/runtime error because client code cannot include server-only logic or secrets. See Next.js docs and guidance. [1][2]

Why this happens (brief):

  • Server components can rely on Node/server-only APIs and secrets; bundling them into client code would leak or fail. Next enforces a one-way dependency: Server → Client, not Client → Server. [1][2]
  • Next.js also provides the server-only package to "poison" modules and produce a clear build-time error if a server-only module is imported into client code. [1][2]

How to fix / recommended patterns (pick one):

  • Move the import up into a Server Component (wrap the Client Component with a Server parent) and pass needed data/props down to the Client Component. [2]
  • Keep the module server-only (import 'server-only') so accidental client imports fail fast. [1][2]
  • If you must run client-side logic that depends on server data, fetch the data via an API route or fetch from the client (or use a server Action) and pass results into the client component. [2]
  • Don’t mark server-only modules with 'use client'; instead refactor to separate client and server responsibilities. [2][3]

References:

  • Next.js docs — Server and Client Components (preventing environment poisoning / server-only). [1]
  • Next.js docs — Composition patterns / unsupported pattern: importing Server into Client. [2]
  • Community discussion showing examples and rationale. [3]

If you want, share the exact error message and a short code snippet and I’ll point to the precise change to fix it.


🏁 Script executed:

# Check the actual file being reviewed
cat -n src/features/menu/components/MenuPageContainer.tsx

Repository: CampusTable/campus-table-fe

Length of output: 3495


🏁 Script executed:

# Look for the hakgwanMenuService.server module
find . -name "*hakgwanMenuService*" -type f

Repository: CampusTable/campus-table-fe

Length of output: 128


🏁 Script executed:

# Search for how HakgwanMenuData is used in the file
rg "HakgwanMenuData" src/features/menu/components/MenuPageContainer.tsx -A 3 -B 1

Repository: CampusTable/campus-table-fe

Length of output: 461


🏁 Script executed:

# Check what's exported from the server module
head -50 src/features/menu/services/hakgwanMenuService.server.ts

Repository: CampusTable/campus-table-fe

Length of output: 1648


🏁 Script executed:

# Check the existing types module
cat -n src/features/menu/types/menuType.ts

Repository: CampusTable/campus-table-fe

Length of output: 549


클라이언트 컴포넌트에서 .server 모듈 import는 빌드 에러 발생

"use client" 파일에서 .server 모듈을 가져오면 Next.js에서 빌드 시점에 "Server-only module imported in Client Component" 오류가 발생합니다. HakgwanMenuData는 타입으로만 사용되므로, 공유 타입 모듈로 옮기고 import type으로 분리해야 합니다.

🛠️ 수정 방법
-import { HakgwanMenuData } from "@/features/menu/services/hakgwanMenuService.server";
+import type { HakgwanMenuData } from "@/features/menu/types/menuType";

HakgwanMenuData 인터페이스를 src/features/menu/types/menuType.ts에 추가하고, 기존 hakgwanMenuService.server.ts에서는 타입 정의를 제거하세요.

🤖 Prompt for AI Agents
In `@src/features/menu/components/MenuPageContainer.tsx` at line 3, The client
component MenuPageContainer.tsx is importing HakgwanMenuData from
hakgwanMenuService.server which causes a "Server-only module imported in Client
Component" build error; extract the HakgwanMenuData interface into a shared
types module (e.g., menuType.ts), remove the type definition from
hakgwanMenuService.server, and update MenuPageContainer.tsx to use an import
type { HakgwanMenuData } from the new types module so the client only imports a
type.

@Chuseok22 Chuseok22 merged commit 2773031 into main Jan 16, 2026
2 checks 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.

⚙️ [기능추가][UI] 장바구니 빈 페이지 개발

1 participant