Files
chocoadmin/docs/phase/phase16.md
T

9.4 KiB

Phase 16: 반응형 웹 디자인 (Responsive Web Design)

목표

모바일 화면(360~430px 기준)에서도 관리자 패널을 정상적으로 사용할 수 있도록 세 가지 영역을 개선한다.

  1. 사이드바 — 모바일 햄버거 메뉴 + 슬라이드인 패널
  2. 표 — 섹션 경계 내에서 가로 스크롤되도록 헤더 레이아웃 수정
  3. 페이지네이션 — 모바일에서도 오른쪽 정렬 유지

1. 모바일 사이드바

현재 상태

app/(admin)/layout.tsx<aside>hidden md:block으로 모바일에서 완전히 숨겨진다. 헤더에 햄버거 버튼이 없어 모바일에서 내비게이션 자체에 접근할 수 없다.

layout.tsx는 서버 컴포넌트(await auth() 사용)이므로 useState를 직접 쓸 수 없다.

구현 방식

추천 닫기 UI: 배경 오버레이(backdrop) + 사이드바 내 X 버튼 조합

  • 오버레이: 사이드바 외부 어디든 탭하면 닫힘 → 모바일 터치 UX에 가장 자연스러움
  • X 버튼: 사이드바 상단에 명시적으로 표시 → 처음 쓰는 사용자도 직관적으로 이해
  • ESC 키: useEffectkeydown 리스너 추가 → 접근성 향상

두 가지 닫기 수단을 모두 제공해 사용자가 선호하는 방식으로 닫을 수 있다.

생성할 파일

app/(admin)/MobileNav.tsx (신규, "use client")

"use client";
// useState(isOpen), useEffect(ESC 키)
// 렌더링:
//   1. <button> 햄버거 (md:hidden — 데스크탑에서 숨김)
//   2. isOpen일 때: fixed inset-0 z-20 bg-black/50 backdrop (클릭 시 닫힘)
//   3. 슬라이드인 패널: fixed inset-y-0 left-0 z-30 w-64
//      - 상단: "chocoadmin" 로고 + X 버튼 (오른쪽)
//      - 내비게이션 링크 목록 (navItems 배열)
//      - 링크 클릭 시 자동 닫힘 (useRouter 또는 onClick)
//   4. 애니메이션: translate-x-0 ↔ -translate-x-full, transition-transform duration-200

패널이 열릴 때 document.bodyoverflow: hidden을 걸어 배경 스크롤을 막는다.

수정할 파일

app/(admin)/layout.tsx

+ import { MobileNav } from "./MobileNav";

  <header className="sticky top-0 z-10 flex h-16 items-center justify-between border-b bg-background px-5">
+   <MobileNav navItems={navItems} />
    <div>
      <p className="text-sm font-medium">{session.user.name}</p>
      ...
    </div>
    <form action={logoutAction}>...</form>
  </header>
  • 데스크탑 <aside>는 그대로 유지 (hidden md:block)
  • MobileNav가 렌더링하는 햄버거 버튼은 md:hidden이므로 데스크탑에서는 보이지 않음
  • 헤더 왼쪽: 햄버거(모바일) | 사용자 정보(기존) → justify-between으로 양 끝 배치 유지

2. 표 — 섹션 경계 내 가로 스크롤

원인 분석

왜 StudentsSection / LogsSection은 정상 동작하는가

두 섹션 모두 "use client" 컴포넌트로, useEffect 안에서 데이터를 직접 fetch한다.

  • SSR 시점: data === null → 로딩 중... <div class="h-24 ..."> 렌더
  • 섹션 너비가 뷰포트 기준으로 확정된 뒤, 클라이언트에서 데이터를 받아 표를 삽입
  • 표가 나타날 때 이미 섹션 너비가 확정되어 있으므로 overflow-x-auto가 스크롤을 정상 처리

왜 ExtensionRequestsSection / UpgradeRequestsSection은 섹션이 늘어나는가

두 섹션도 "use client"이지만, 데이터를 서버에서 props로 받아 SSR 초기 렌더에 표가 포함된다.

  • SSR 시점: extensionRequests / upgradeRequests props가 이미 채워진 상태로 렌더
  • 초기 레이아웃 계산 시점에 min-w-[560px] 표가 DOM에 존재
  • section 요소에 overflow 설정이 없어(overflow: visible 기본값) 표의 너비가 섹션을 밀어냄
  • 섹션 자체가 560px+ 로 확장 → div.overflow-x-auto도 그 너비를 그대로 가져가므로 스크롤이 생기지 않음

핵심 수정: sectionoverflow-hidden 추가

section 요소에 overflow: hidden을 적용하면 BFC(Block Formatting Context)가 확립되어 어떤 자식도 섹션 너비를 밀어낼 수 없다. 섹션은 항상 뷰포트 기준 너비로 고정되고, 표는 div.overflow-x-auto 안에서 가로 스크롤된다.

- <section className="rounded-lg border bg-background p-5">
+ <section className="overflow-hidden rounded-lg border bg-background p-5">

overflow: hidden은 섹션의 가로 팽창만 막는다. 섹션의 heightauto이므로 세로로는 내용 크기에 맞게 늘어나며 내용이 잘리지 않는다. 또한 div.overflow-x-auto의 스크롤바는 섹션 내부에 위치하므로 정상 표시된다.

추가 수정: 섹션 헤더 레이아웃 (UX 목적)

overflow-hidden으로 표 팽창은 막을 수 있지만, 헤더의 버튼/셀렉트 영역이 모바일 뷰포트보다 넓을 경우 overflow: hidden에 의해 잘려 보이지 않는다. 버튼 접근성을 위해 헤더도 모바일에서 세로 스택으로 변경한다.

수정할 파일

app/(admin)/maestros/[maestroId]/ExtensionRequestsSection.tsx

- <section className="rounded-lg border bg-background p-5">
+ <section className="overflow-hidden rounded-lg border bg-background p-5">

- <div className="flex items-start justify-between gap-2">
+ <div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
    <div>
      <h2 ...>연장 신청 이력</h2>
      <p ...>전체 N건 중 최근 20건</p>
    </div>
-   <div className="flex flex-col items-end gap-1">
+   <div className="flex flex-col items-start gap-1 sm:items-end">
      <Button ...>연장</Button>
      ...
    </div>
  </div>

app/(admin)/maestros/[maestroId]/UpgradeRequestsSection.tsx

- <section className="rounded-lg border bg-background p-5">
+ <section className="overflow-hidden rounded-lg border bg-background p-5">

- <div className="flex items-start justify-between gap-2">
+ <div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
    <div>
      <h2 ...>업그레이드 신청 이력</h2>
      <p ...>전체 N건 중 최근 20건</p>
    </div>
-   <div className="flex flex-col items-end gap-1">
+   <div className="flex flex-col items-start gap-1 sm:items-end">
-     <div className="flex items-center gap-2">
+     <div className="flex flex-wrap items-center gap-2">
        <select ...>...</select>
        <Button ...>업그레이드</Button>
      </div>
      ...
    </div>
  </div>

StudentsSection / LogsSection

수정 불필요. 클라이언트 fetch 방식으로 인해 SSR 초기 렌더에 표가 없어 섹션 너비가 먼저 확정되므로 현재 구조 그대로 정상 동작한다.


3. 페이지네이션 오른쪽 정렬

현재 상태

모든 페이지네이션 컨테이너:

flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between

모바일(flex-col)에서 nav(페이지 버튼)는 페이지 크기 선택 아래에 위치하며 왼쪽 정렬된다.

수정 방법

<nav> 요소에 self-end 추가, 데스크탑에서는 sm:self-auto로 복원:

  <nav
    aria-label="..."
-   className="flex flex-wrap items-center gap-1.5"
+   className="self-end flex flex-wrap items-center gap-1.5 sm:self-auto"
  >
  • 모바일(flex-col): align-self: flex-end → nav가 오른쪽으로 붙음
  • 데스크탑(flex-row + justify-between): sm:self-auto → 기존 items-center 적용, 수직 중앙 정렬 유지

페이지 크기 선택 요소("페이지당 N개 표시")는 변경하지 않아 왼쪽에 그대로 유지.

수정 대상 파일 및 위치

파일 nav aria-label
app/(admin)/extension-requests/page.tsx "연장 신청 목록 페이지"
app/(admin)/upgrade-requests/page.tsx "업그레이드 신청 목록 페이지"
app/(admin)/maestros/page.tsx "마에스트로 목록 페이지"
app/(admin)/maestros/[maestroId]/StudentsSection.tsx "학생 목록 페이지"
app/(admin)/maestros/[maestroId]/LogsSection.tsx "처리 로그 페이지"

작업 순서

  1. MobileNav.tsx 생성 → layout.tsx 수정 (사이드바)
  2. ExtensionRequestsSection.tsx: sectionoverflow-hidden 추가 + 헤더 flex-col 수정
  3. UpgradeRequestsSection.tsx: sectionoverflow-hidden 추가 + 헤더 flex-col 수정
  4. 페이지네이션 nav 5개 파일 수정

검증 포인트

  • 모바일(375px)에서 햄버거 버튼 표시 확인
  • 사이드바 슬라이드인/아웃 애니메이션 확인
  • 오버레이 탭 → 사이드바 닫힘 확인
  • X 버튼 탭 → 사이드바 닫힘 확인
  • 데스크탑(≥768px)에서 햄버거 버튼 미표시 확인
  • ExtensionRequestsSection 섹션이 모바일 뷰포트 너비 안에 머무르는지 확인
  • ExtensionRequestsSection 표가 섹션 내부에서 가로 스크롤되는지 확인
  • ExtensionRequestsSection 헤더 버튼이 모바일에서 잘리지 않고 표시되는지 확인
  • UpgradeRequestsSection 섹션이 모바일 뷰포트 너비 안에 머무르는지 확인
  • UpgradeRequestsSection 표가 섹션 내부에서 가로 스크롤되는지 확인
  • UpgradeRequestsSection 요금제 셀렉트 + 버튼이 모바일에서 접근 가능한지 확인
  • 모든 페이지네이션 nav가 모바일에서 오른쪽 정렬 확인
  • 데스크탑에서 기존 레이아웃 회귀 없음 확인