Files
sam-docs/frontend/v1/06-styling-guide.md
유병철 8f939d3609 docs: [frontend] 프론트엔드 아키텍처/가이드 문서 v1 작성
- _index.md: 문서 목록 및 버전 관리
- 01~09: 아키텍처, API패턴, 컴포넌트, 폼, 스타일, 인증, 대시보드, 컨벤션
- 10: 문서 API 연동 스펙 (api-specs에서 이관)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 10:24:25 +09:00

5.1 KiB

06. 스타일링 가이드

대상: 프론트엔드 개발자, 디자이너 버전: 1.0.0 최종 수정: 2026-03-09


1. 기술 스택

도구 역할
Tailwind CSS 4 유틸리티 클래스 기반 스타일링
shadcn/ui Radix UI 기반 컴포넌트 라이브러리
CSS Variables 테마 토큰 (다크모드 대비)
lucide-react 아이콘

2. 기본 규칙

사용

// ✅ Tailwind 클래스 사용
<div className="flex items-center gap-2 p-4 bg-muted rounded-lg">

// ✅ shadcn/ui 컴포넌트
import { Button } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card';

금지

// ❌ 인라인 스타일
<div style={{ display: 'flex', padding: '16px' }}>

// ❌ CSS 모듈 / styled-components
import styles from './Component.module.css';

// ❌ 전역 CSS (globals.css 외)

3. 레이아웃 패딩 규칙

AuthenticatedLayout (<main>) → 패딩 없음
  └── PageLayout → p-0 md:space-y-6 (패딩 담당)
       └── 콘텐츠 영역

핵심: page.tsx에서 추가 패딩 래퍼 금지 (이중 패딩 방지)

// ✅ 올바름
<PageLayout>
  <PageHeader title="..." />
  <Card>...</Card>
</PageLayout>

// ❌ 이중 패딩
<div className="p-6">  {/* ← 금지 */}
  <PageLayout>
    ...
  </PageLayout>
</div>

4. 간격 시스템

용도 클래스
섹션 간 간격 space-y-6 24px
카드 내부 간격 space-y-4 16px
인라인 요소 간격 gap-2 8px
폼 필드 간격 space-y-4 16px
그리드 갭 gap-4 또는 gap-6 16px / 24px

5. 반응형 패턴

// 그리드: 모바일 1열 → 데스크톱 2~4열
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">

// 숨기기/보이기
<div className="hidden md:block">  {/* 데스크톱만 */}
<div className="block md:hidden">  {/* 모바일만 */}

// 폰트 크기 반응형
<h1 className="text-lg md:text-2xl font-bold">

브레이크포인트

접두사 최소 너비 대상
(없음) 0px 모바일
sm: 640px 소형 태블릿
md: 768px 태블릿
lg: 1024px 데스크톱
xl: 1280px 넓은 화면

6. 색상 시스템

shadcn/ui CSS 변수 기반 — 다크모드 자동 대응:

용도 클래스 설명
배경 bg-background 페이지 배경
카드 bg-card 카드 배경
강조 bg-muted 연한 배경 (테이블 호버 등)
텍스트 text-foreground 기본 텍스트
보조 텍스트 text-muted-foreground 설명, 플레이스홀더
테두리 border 기본 테두리
위험 text-destructive 삭제, 에러

상태 색상

상태 텍스트 배경
입금/증가 text-blue-600 bg-blue-50
출금/감소 text-red-600 bg-red-50
성공/완료 text-green-600 bg-green-50
경고/대기 text-orange-500 bg-orange-50
비활성 text-gray-400 bg-gray-50

7. 컴포넌트 스타일 규칙

버튼

// 주요 액션
<Button size="sm">등록</Button>

// 보조 액션
<Button variant="outline" size="sm">취소</Button>

// 위험 액션 (삭제)
<Button variant="destructive" size="sm">삭제</Button>

테이블

// 기본 셀 정렬
<TableCell className="text-center">  {/* 날짜, 상태, 번호 */}
<TableCell className="text-right">   {/* 금액 */}
<TableCell className="text-left">    {/* 텍스트 (기본) */}

// 합계 행
<TableRow className="bg-muted/50 font-medium">

Badge

<Badge variant="outline">기본</Badge>
<Badge variant="default">활성</Badge>
<Badge variant="destructive">에러</Badge>

8. 팝업/모달 규칙

용도 컴포넌트 비고
확인/취소 AlertDialog (Radix) alert(), confirm() 금지
데이터 입력 Dialog (Radix) prompt() 금지
알림 toast (sonner) 성공/에러 피드백
검색+선택 SearchableSelectionModal 커스텀 Dialog 조합 금지
// ✅ 토스트 사용
import { toast } from 'sonner';
toast.success('저장되었습니다.');
toast.error('저장에 실패했습니다.');

// ❌ alert 금지
alert('저장되었습니다.');

9. 아이콘

lucide-react 사용:

import { FileText, Settings, Search, Plus, Trash2 } from 'lucide-react';

// 인라인 아이콘
<FileText className="h-4 w-4" />

// 버튼 내 아이콘
<Button size="sm">
  <Plus className="h-4 w-4 mr-1" />
  등록
</Button>

10. 금액 표시

import { formatNumber } from '@/lib/utils/amount';

formatNumber(1234567)    // "1,234,567"
formatNumber(0)          // "0"
formatNumber(undefined)  // "0"

테이블에서:

<TableCell className="text-right text-blue-600">
  {formatNumber(item.amount)}
</TableCell>