Layout SystemP1 본문

레이아웃 시스템

4·8·12컬럼 반응형 그리드, 6단계 브레이크포인트, 컨테이너 규칙, 8px 스페이싱 리듬, 그리고 표준 화면 레이아웃 패턴을 정의합니다.

마지막 업데이트 2026-06-12

레이아웃 원칙#

  • 화이트스페이스가 위계를 만든다 — 구분선과 박스보다 여백을 먼저 사용합니다
  • 8px 그리드 — 모든 간격은 --wds-space-* 토큰의 배수입니다
  • 콘텐츠 폭 우선 — 화면이 넓어져도 본문 가독 폭을 유지합니다
  • 모바일에서 깨지지 않는다 — 320px이 최소 보장 폭입니다

레이아웃 프리미티브#

문서가 정의한 레이아웃을 재사용 가능한 토큰 백킹 컴포넌트로 제공합니다 — 모두 무상태(RSC 가능)입니다.

  • <Stack> — flex 1D 적층. gap--wds-space-* 스케일, direction·align·justify·wrap.
  • <Container> — 중앙 정렬 폭 제한. size(content 1200 · prose 760 · full) + padding(거터).
  • <Grid> / <GridItem> — 12컬럼(기본) 그리드. GridItemspan·start로 컬럼 점유.
  • <Split> — 52/48 분할. compact(600px 미만)에서 세로로 적층.
direction="vertical"gap=3 · 12px
요소 A
요소 B
요소 C
direction="horizontal"gap=4 · 16px
요소 A
요소 B
요소 C

박스는 자리표시용 자식입니다 — 보이는 건 박스가 아니라 그 사이 간격이며, 값은 --wds-space-* 토큰으로 정합니다.

Stack — 자식을 1D로 쌓고, 사이 간격은 gap 토큰으로

반응형 그리드#

컬럼 수는 Material window size class(ADR-012)와 정합합니다 — compact 4 · medium 8 · expanded 12. 1280px 미만에서는 마진이 고정되고 컨테이너가 유동하며, 1280px부터는 컨테이너가 1200px로 고정되고 마진이 가변합니다.

구간컬럼거터마진컨테이너
compact600px 미만416px16px 고정유동
medium600–840px824px24px 고정유동
expanded840px 이상1224px24px 고정유동 (1200px까지)
데스크톱1280px 이상1224px가변1200px 고정

핸들을 직접 끌어 보세요 — 뷰포트가 320↔1920px로 연속 변하며 컬럼이 4 → 8 → 12로 reflow되고 모든 수치가 실시간 갱신됩니다. 아래 실제 적용은 같은 뷰포트로 실물 Grid/GridItem 카드 레이아웃이 재배치되는 미리보기입니다 — 각 카드의 span이 size class별 규칙(stat 3→4→4 · main 8→8→4 · aside 4→8→4)으로 살아서 바뀝니다. size class 판정은 라이브러리 실물(windowSizeClass)을 그대로 사용합니다.

1280px viewportexpanded컬럼 12 × 78px거터 24px마진 40px 가변컨테이너 1200px 고정
실제 적용 — Grid · GridItem 실물 reflow
그리드 리사이즈 랩 — 드래그·←/→(±10px)·Shift(±100px)·마크 점프 + 실물 Grid 적용

기준 폭별 정적 스펙은 디바이스 탭으로 확인합니다.

1280px 이상컬럼 12 × 78px거터 24px컨테이너 1200px 고정 · 마진 가변
wide · 12801280px viewport
1200px · 12 cols
desktop · 14401440px viewport
120px
1200px · 12 cols
120px
max · 19201920px viewport
360px
1200px · 12 cols
360px
컬럼 4 · 8 · 12 거터 16 · 24px 마진 고정 → 1280부터 가변
반응형 그리드 스펙 — 디바이스별 컬럼(4·8·12) · 마진 · 거터 측정

위 리사이즈 랩의 적용 미리보기가 실물 Grid/GridItem을 그대로 렌더하며, 각 카드의 span 라벨(예: span 8 = 12칸 중 8칸 점유)이 size class별로 재배치됩니다.

레이아웃 토큰은 SSOT에서 자동 생성된 표로 확인합니다.

토큰설명
문서 본문 가독 폭 — 한글 약 38~42자 (07 레이아웃)
medium·expanded 거터
expanded(≥840) 컬럼 수
medium·expanded 고정 마진 — 1280 미만 유동 컨테이너 구간. ≥1280은 마진 가변(컨테이너 1200 고정)
compact(<600) 컬럼 수 — Material window size class 정합 (ADR-012)
compact 거터
compact 고정 마진
medium(600–840) 컬럼 수
비주얼·콘텐츠
폼·액션

브레이크포인트#

6단계 기준 폭에서 시각 검증을 수행합니다. 이 포털 셸의 반응형 규칙이 기준 구현(Reference)이며, 경계값은 SSOT(tokens/core/breakpoint.json)에서 파생됩니다.

기준 폭구간포털 셸 동작
320px모바일 최소단일 컬럼 · 드로어 내비 · 아이콘 검색
380px모바일헤더 검색이 아이콘 → 풀 입력으로 전환
768px태블릿단일 컬럼 유지 · 헤더 풀 구성
1024px데스크톱 시작좌측 사이드바 고정 노출
1280px와이드 데스크톱우측 TOC 노출 · 본문 폭 고정
1920px최대셸 폭 1920px 캡 — 콘텐츠가 무한히 늘어나지 않음

미디어 쿼리는 모바일 우선이 아닌 구간 명시 방식을 사용합니다 — 숨김/노출이 일어나는 경계(1024 · 1280)를 코드에서 그대로 읽을 수 있어야 합니다. 셸 CSS의 너비 미디어쿼리 리터럴이 이 SSOT와 어긋나면 build-tokens 드리프트 가드(breakpoint-css-parity.test)가 빌드를 실패시킵니다 — 경계는 브레이크포인트 토큰 한 곳에서만 바뀝니다.

셸 전환점 6개와 적응형 window size class 경계 2개(600 · 840)를 하나의 룰러에서 확인합니다 — 두 체계는 서로 다른 레이어입니다.

적응형 window size class — ADR-012
compact · 4컬럼
medium · 8컬럼
expanded · 12컬럼
600px840px
셸 레이아웃 전환점 — 6단계
브레이크포인트 룰러 — 셸 전환점 6 · 적응형 경계 2 (0–1920px)

컨테이너와 콘텐츠 폭#

용도근거
마케팅/홈 컨테이너--wds-container-max (1200px)12컬럼 그리드 기준 폭
문서 본문(prose)760px한글 기준 약 38–42자 — 가독 최적
셸 전체1920px 캡와이드 모니터에서 과확장 방지

본문이 760px을 넘는 요소(코드 블록, 넓은 표)는 가로 스크롤 래퍼로 감쌉니다 — 컨테이너를 깨뜨리지 않습니다.

셸 캡 1920px — 와이드 모니터 과확장 방지
컨테이너 1200px — 12컬럼 기준 폭

prose · 760px한 줄에 한글 약 38–42자 — 문서 본문의 가독 최적 폭입니다.

컨테이너 폭 스케일 — 셸 캡 1920 ⊃ 컨테이너 1200 ⊃ prose 760

스페이싱 리듬#

간격은 의미 단위로 선택합니다 — 요소 내부(1–3) · 요소 사이(4–6) · 블록 사이(8–12) · 섹션 사이(16–32).

요소 내부 · 1–3
요소 사이 · 4–6
블록 사이 · 8–12
섹션 사이 · 16–32

4px 베이스 유닛 — 모든 토큰은 4의 배수이며, space-4(16px)가 본문 리듬의 기준 한 칸입니다. 결번(5·7·9 …)은 의도입니다 — 위로 갈수록 성기게 증가해 선택을 단순하게 만듭니다.

8pt 스페이싱 스케일 — 4px 베이스 유닛

원시 토큰 값은 SSOT에서 자동 생성된 표로 확인합니다.

토큰설명
4px
8px
12px
16px
20px
24px
32px
40px
48px
64px
80px
96px
128px

표준 레이아웃 패턴#

사이드바 레이아웃#

문서·관리 화면의 기본형 — 고정 사이드바(264px) + 유동 콘텐츠. 이 포털이 기준 구현입니다. 1024px 미만에서 사이드바는 드로어로 전환됩니다.

분할 레이아웃 (52 / 48)#

로그인·온보딩 화면의 기본형 — 좌측 비주얼·콘텐츠 52%(--wds-split-left), 우측 폼·액션 48%(--wds-split-right). 기존 로그인 화면이 이 비율의 정본이며, P4 Patterns의 Login 패턴으로 재구현됩니다.

디바이스 탭으로 각 뷰포트에서 52/48 비율·px·거터와 compact 적층을 측정값으로 확인합니다 — 위 반응형 그리드 스펙과 같은 블루프린트 방식입니다.

1280px 이상좌 52% · 우 48%split-left / split-right 토큰Feature / Form 역할
wide · 12801280px viewport
비주얼·콘텐츠52%
폼·액션48%
desktop · 14401440px viewport
비주얼·콘텐츠52%
폼·액션48%
비주얼·콘텐츠 52% 폼·액션 48% 거터 12px
분할 레이아웃 스펙 — 디바이스별 52/48 비율 · px · 거터 · compact 적층 측정

비주얼·콘텐츠 · 52%

폼·액션 · 48%

52/48 분할 — 실제 Split 프리미티브 (compact 600px 미만에서 세로 적층)

대시보드 그리드#

12컬럼 위에 카드 위젯을 배치하는 패턴 — 상세 규칙과 라이브 데모는 P4 패턴 섹션에서 정의됩니다.

구성과 정렬#

나란히 놓인 표면은 폭만 같아서는 정렬된 것이 아닙니다. 바깥 박스가 같은 높이여도 내부 콘텐츠 블록이 서로 다른 높이에서 시작·종료하면 시선이 흔들리고, 짧은 카드에 죽은 여백이 생깁니다. 같은 행의 형제(카드·패널·셀)는 바깥 높이뿐 아니라 앵커 지점·내부 리듬·정보 밀도까지 정렬되어야 합니다. 기준 구현은 플레이그라운드 셸(playground.module.css.stage)로, grid-template-columns: minmax(0, 1fr) minmax(16rem, 18rem)align-items를 두지 않아 기본 stretch(동일 높이)를 얻습니다 — 이 관습을 규칙으로 고정합니다.

동일 높이#

  • 규칙 — 한 행에 나란히 놓인 패널·카드는 같은 높이를 가집니다 — Grid/Flex 기본 stretch에 맡기고, align-items: start로 높이를 끊지 않습니다.
  • 근거 — 들쭉날쭉한 바닥선은 "정렬되지 않음"으로 읽힙니다. 같은 높이는 형제가 같은 위계임을 시각적으로 선언합니다.
  • 기법 — 그리드 컨테이너에 align-items명시하지 않습니다(기본값 stretch). 유일한 예외는 내재 크기가 의미인 칩·뱃지·태그 행으로, 이때만 align-items: center로 콘텐츠 크기를 보존합니다.
  • 내부 박스까지 동일 크기 — 동일 높이는 바깥 카드에서 끝나지 않습니다. 형제 카드 안의 눈에 띄는 내부 박스(명세 박스·데이터 패널·내부 카드)도 콘텐츠 양(행 수 등)이 달라 크기가 갈리면 통일성이 깨집니다. 박스를 콘텐츠에 맞춰 줄이지 말고, 그 박스 자체에 flex: 1을 주어 짧은 쪽을 늘려 형제와 같은 크기로 맞춥니다 — flex: 1은 가변 텍스트가 아니라 크기를 맞출 박스에 둡니다(짧은 카드는 박스 안 아래 여백이 생기는 게 정답).
  • 별도 셀의 하위 블록 — 형제의 하위 영역이 각자 다른 그리드 셀에 있으면(예: 카드마다의 컬러 씬) 묶을 공유 행이 없어 stretch가 닿지 않습니다. 이때는 가장 큰 콘텐츠에 맞춘 공유 min-height 바닥값으로 높이를 맞추되, 더 큰 콘텐츠가 추가되면 바닥값을 재보정해야 하는 제약을 코드에 명시합니다.

앵커 정렬#

  • 규칙 — 형제 카드의 길이가 가변일 때, 구조적 앵커(메타데이터 푸터·레시피 라인·기본 CTA)는 카드 간 같은 선에서 정렬됩니다.
  • 근거 — 같은 높이여도 앵커가 카드 중간에 떠 있으면 비교가 깨집니다. 사용자는 "이 줄은 모든 카드에서 같은 위치"라는 암묵 계약을 읽습니다.
  • 기법 — 카드를 flex 세로 컬럼으로 만들고, 가변 영역(설명문)에 flex: 1 을 주어 빈 공간을 흡수시킵니다. 고정 앵커는 자연스럽게 카드 하단에 핀되어 형제 간 같은 선에 정렬됩니다. 전체 메타 박스에만 flex: 1을 주면 내부 블록이 위로 적층돼 설명 길이차로 푸터가 카드마다 다른 높이에 뜹니다.
  • 한계 — 진짜로 핀되는 건 맨 아래 블록뿐입니다. 그 위의 고정 블록(명세 박스 등)은 자기 아래 콘텐츠(노트 등)의 높이가 형제 간 같을 때만 함께 정렬됩니다 — 그래서 콘텐츠 패리티(노트를 평행한 길이로 유지)가 장식이 아니라 앵커 정렬을 지탱하는 조건입니다. 특정 블록을 반드시 정렬하려면 그 블록을 맨 아래에 두세요.

내부 리듬 일관성#

  • 규칙 — 형제 카드는 같은 내부 섹션 순서, 같은 gap 스케일, 같은 패딩을 공유합니다 — 바깥 크기뿐 아니라 구조의 패리티까지 일치시킵니다.
  • 근거 — 한 카드는 space-3, 옆 카드는 space-4로 호흡하면 같은 행인데 다른 리듬으로 읽혀 정렬감이 무너집니다.
  • 기법 — 형제 카드는 같은 클래스로 gap·padding을 단일 출처에 고정합니다 — 카드별 인라인 오버라이드를 금지합니다. 간격은 스페이싱 리듬(요소 내부 1–3 · 블록 사이 8–12)을 따라 카드 전체가 같은 단계를 씁니다.

콘텐츠 패리티#

  • 규칙 — 형제 카드는 비교 가능한 정보 밀도를 가집니다 — 한 카드가 거의 비어 있고 옆 카드는 가득 찬 상태를 두지 않으며, 카피는 구조적으로 평행합니다.
  • 근거 — 같은 골격·같은 앵커여도 한쪽이 빈약하면 짝이 깨져 보입니다. 정렬은 픽셀만의 문제가 아니라 내용의 무게가 평행할 때 완성됩니다.
  • 기법 — 데이터 모델에서 형제가 같은 필드 집합을 채우도록 강제하고(빈 슬롯은 플레이스홀더로 자리를 지킴), 카피는 같은 문형으로 평행하게 작성합니다.

온-스케일 치수#

  • 규칙 — 높이·여백·간격은 --wds-space-*(4px 스케일) 토큰에서 고릅니다. 임의 px(104px·176px 같은)는 같은 종류의 표면끼리 리듬을 깹니다.
  • 기법 — 같은 측정 표면(예: 스펙 바)은 하나의 높이 토큰을 공유합니다 — 그리드 스펙 바와 분할 스펙 바가 같은 행 높이(80px)를 쓰듯. 한쪽만 104px이면 나란히 봤을 때 어긋납니다.

컨테이너 채움 (shrink-wrap 금지)#

  • 규칙 — flex/grid 자식이 가운데 정렬(justify-content: center) 컨테이너 안에서 의도한 폭으로 렌더되려면 width: 100%(또는 align-self: stretch)가 필요합니다. 없으면 콘텐츠 폭으로 쪼그라들어(shrink-wrap) 작게 중앙에 박힙니다.
  • 기법 — 데모 캔버스처럼 자식을 가운데 정렬하는 부모에 폭이 콘텐츠 의존인 컴포넌트(Split·Carousel 등)를 넣을 땐 폭 100% 래퍼로 감쌉니다 — 실제로 두 컴포넌트 데모가 이 누락으로 깨진 적이 있습니다.

표현 일관성#

  • 규칙 — 같은 개념을 두 번 표현하면(측정 목업 + 라이브 데모) 색·치수·마커 언어를 통일합니다. 한쪽은 primary 틴트, 한쪽은 다른 회색이면 같은 것으로 안 읽힙니다.
  • 기법 — 역할별 색은 목업·라이브가 같은 토큰을 씁니다 — 예: 비주얼·콘텐츠=primary-subtle·폼·액션=surface-muted를 측정 스펙과 라이브 데모 양쪽 모두.

인접 컨트롤 크롬#

  • 규칙 — 나란히 놓인 컨트롤(필드·버튼·셀렉트)은 같은 크롬(테두리·표면·높이·반경 토큰)을 공유합니다. 채워진 필드 옆의 보조 버튼이 테두리·표면 없는 맨 글리프면 짝이 안 맞아 빈약·미완성으로 읽힙니다.
  • 근거 — 짝 컨트롤이 다른 무게(채운 면 vs 투명 글리프)로 보이면 의도된 그룹이 아니라 어긋난 요소로 읽힙니다.
  • 기법 — 보조 IconButton에 옆 필드와 같은 border·background: surface·border-radius: control을 부여하거나 같은 채움 variant를 씁니다 — 채워진 필드 옆 ghost 단독은 금지합니다.

캡션·문단 정렬#

  • 규칙 — 다줄 캡션·설명문은 의도된 정렬을 갖습니다 — 본문성 텍스트는 좌측 정렬(가운데 정렬 금지), 문단 사이는 토큰 간격으로 분리합니다.
  • 근거 — 가운데 정렬한 다줄 텍스트는 줄 길이가 들쭉날쭉(고아 단어)해 "정렬 안 됨"으로 읽힙니다.
  • 기법text-align: left + text-wrap: pretty(고아 줄 회피), 문단 분리는 --wds-space-*에서 — 2px 같은 비-토큰 간격을 쓰지 않습니다.

컨트롤 경계 수용#

  • 규칙 — 인터랙티브 컨트롤(캐러셀 화살표·슬라이더 핸들 등)은 섹션 콘텐츠 박스 안에 머뭅니다. 섹션 가장자리에 닿거나 넘으면 깨진 것으로 읽힙니다.
  • 근거padding-inline: calc(50% - X) 같은 % 중앙정렬 패딩은 containing block 기준이라, flex 형제(화살표)가 폭을 뺏는 좁은 컨테이너에서 트랙의 최소폭이 가용폭을 초과해 컨트롤을 바깥으로 밀어냅니다.
  • 기법 — 거터 컨트롤을 position: absolute 오버레이로 두어 트랙이 풀폭이 되게 하면 50%가 정확히 해석됩니다 — 또는 % 패딩을 형제 폭만큼 보정합니다. 좁은 컨테이너에서 컨트롤 가장자리가 섹션 안인지 프리뷰로 실측합니다.

인터랙션 일관성#

  • 규칙 — 같은 동작(복사·확장·선택 등)은 시스템 전역에서 같은 패턴·프리미티브로 구현합니다 — 표면마다 다른 방식(한쪽 hover 아이콘, 한쪽 클릭+툴팁)을 두지 않습니다.
  • 근거 — 같은 의미의 동작이 표면마다 다르게 작동하면 학습 비용이 늘고 "한 시스템"으로 안 읽힙니다 — 표현 일관성의 인터랙션 판본입니다.
  • 기법 — 복사는 공유 CopyTile(또는 useCopyToClipboard + copyTip, 타일 클릭 → 복사 + "복사됨" 툴팁)을 씁니다 — 새 표면이 ad-hoc 복사 UI(hover 아이콘 등)를 만들지 않게 합니다.
규칙적용안티패턴
동일 높이행 안의 형제는 Grid/Flex 기본 stretch로 같은 높이 (.stage 기준); 내부 박스도 flex: 1로 동일 크기align-items: start로 바닥선을 끊거나, 내부 박스를 콘텐츠에 맞춰 줄여 형제와 크기 불일치
앵커 정렬카드를 flex 컬럼으로, 가변 본문에 flex: 1 → 푸터·CTA가 카드 간 같은 선에 핀메타 박스 전체에만 flex: 1 → 설명 길이차로 앵커가 카드 중간에 떠다님
내부 리듬 일관성형제가 같은 섹션 순서·같은 gap·같은 패딩(단일 출처) 공유카드별 인라인 gap/padding 오버라이드로 리듬이 어긋남
콘텐츠 패리티형제가 같은 필드 집합·평행한 카피·비교 가능한 밀도한 카드는 가득, 옆 카드는 거의 비어 짝이 깨짐
온-스케일 치수height·여백을 --wds-space-*에서; 같은 종류 표면은 같은 높이 토큰임의 px(104·176)로 행끼리 리듬 깨짐
컨테이너 채움center 컨테이너의 자식은 width:100%/stretchshrink-wrap으로 중앙에 작게 박힘
표현 일관성목업·라이브가 같은 색·치수·마커 토큰같은 것을 다른 색/치수로 표현
인접 컨트롤 크롬나란한 컨트롤이 같은 테두리·표면·높이·반경 토큰 공유채운 필드 옆 ghost 맨 글리프로 짝이 빈약
캡션·문단 정렬다줄 캡션 좌측 정렬 + text-wrap:pretty, 문단 토큰 간격text-align:center로 줄 길이 들쭉날쭉(고아 단어)
컨트롤 경계 수용캐러셀 화살표 등은 섹션 박스 안(오버레이 또는 % 보정)calc(50%-X) 패딩이 좁은 폭서 컨트롤을 섹션 밖으로 밀어냄
인터랙션 일관성복사 등은 공유 프리미티브(CopyTile 클릭→복사+툴팁)표면마다 다른 방식(hover 아이콘 vs 클릭)

이 규칙의 기준 적용은 머티리얼 테마 갤러리 카드입니다 — 두 카드의 설명 길이가 달라도 명세·노트가 같은 선에 핀됩니다.

레이어 위계 (z-index)#

떠 있는 표면의 쌓임 순서는 토큰으로 고정합니다 — 표면이 어느 레이어를 쓰는지가 계약입니다.

1600
--wds-z-skip-link본문 건너뛰기 링크 — 접근성 최상위
1500
--wds-z-tooltipTooltip
1400
--wds-z-toastToast
1300
--wds-z-modalModal · Drawer · BottomSheet · 셸 검색
1200
--wds-z-overlay스크림 · 셸 드로어 내비
1100
--wds-z-sticky셸 헤더
1000
--wds-z-dropdownSelect · MultiSelect · Autocomplete · DropdownMenu · Popover · 날짜/시간 피커

컴포넌트 내부 로컬 스태킹(자체 컨텍스트 안 1–3)은 토큰 비대상 — 1000 미만의 임의 전역 z-index는 금지합니다.

레이어 스택 — 위일수록 높은 z, 실제 사용 표면 매핑
레이어z사용 표면
skip-link1600본문 건너뛰기 링크 (접근성 최상위)
tooltip1500Tooltip
toast1400Toast
modal1300Modal · Drawer · BottomSheet · 셸 검색
overlay1200스크림 · 셸 드로어 내비
sticky1100셸 헤더
dropdown1000Select · MultiSelect · Autocomplete · DropdownMenu · Popover · 날짜/시간 피커

컴포넌트 내부 로컬 스태킹(자체 스태킹 컨텍스트 안의 1–3, 예: DataGrid 스티키 셀, RangeSlider 썸)은 토큰 비대상입니다 — 단, 1000 미만의 임의 전역 z-index는 금지합니다.

토큰설명
앵커 팝업 — Select·DropdownMenu·Popover·날짜/시간 피커 등
고정 크롬 — 셸 헤더
스크림/드로어 내비
Modal·Drawer·BottomSheet·셸 검색
본문 건너뛰기 링크 — 접근성 최상위. 어떤 떠 있는 표면보다 위