Files
ga-commission-system/docs/09_UI_DESIGN.md
T
GA Pro 5b024d9b9c docs: 09_UI_DESIGN.md 추가 (디자인 시스템) + 참조 갱신
- 09_UI_DESIGN.md 신규: Ant Design 테마 토큰, 글로벌 CSS 변수,
  레이아웃 규격(Header 48px / Sider 220px), 페이지 표준 3종(목록/상세/대시보드),
  공통 컴포넌트 스타일(CodeBadge/MoneyText/SearchForm/DataTable/DataGrid),
  등록·수정 폼 규칙(Drawer), 화면별 와이어프레임 6종, 신입 체크리스트
- 00_PROMPTS.md: 프롬프트 3 frontend-developer 가 09_UI_DESIGN.md 도 함께 참조
- 06_FRONTEND_SPEC.md: 상단에 디자인 시스템 참조 추가

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 21:21:13 +09:00

626 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# UI 디자인 시스템 (UI_DESIGN.md)
> GA 수수료 정산 솔루션의 UI 표준. 모든 화면은 이 규칙을 따른다.
> 기술: React 18 + Ant Design 5.x + AG Grid Community
> 신입 개발자가 복붙으로 일관된 UI를 만들 수 있도록 설계.
---
## 1. Ant Design 테마 커스터마이징
### ConfigProvider 설정 (App.tsx)
```tsx
import { ConfigProvider } from 'antd';
import koKR from 'antd/locale/ko_KR';
const theme = {
token: {
// 브랜드 컬러
colorPrimary: '#1677FF',
colorSuccess: '#0F6E56',
colorWarning: '#BA7517',
colorError: '#E24B4A',
colorInfo: '#1677FF',
// 폰트
fontFamily: "'Pretendard', -apple-system, sans-serif",
fontSize: 13,
fontSizeHeading1: 22,
fontSizeHeading2: 18,
fontSizeHeading3: 16,
// 라운딩
borderRadius: 6,
borderRadiusLG: 8,
// 간격
padding: 16,
paddingLG: 24,
margin: 16,
// 배경
colorBgContainer: '#ffffff',
colorBgLayout: '#f5f5f5',
colorBgElevated: '#ffffff',
// 테이블
tablePaddingHorizontal: 12,
tablePaddingVertical: 10,
tableHeaderBg: '#fafafa',
tableRowHoverBg: '#f5f7fa',
tableBorderColor: '#f0f0f0',
},
components: {
// 사이드 메뉴
Menu: {
itemHeight: 40,
itemMarginInline: 8,
itemBorderRadius: 6,
subMenuItemBg: 'transparent',
itemSelectedBg: '#e6f1fb',
itemSelectedColor: '#185fa5',
},
// 테이블
Table: {
headerBg: '#fafafa',
rowHoverBg: '#f5f7fa',
cellPaddingBlock: 10,
cellPaddingInline: 12,
headerSplitColor: '#f0f0f0',
},
// 버튼
Button: {
controlHeight: 32,
controlHeightSM: 28,
paddingInline: 16,
},
// 입력
Input: {
controlHeight: 32,
paddingInline: 10,
},
Select: {
controlHeight: 32,
},
// 카드
Card: {
paddingLG: 16,
},
},
};
<ConfigProvider theme={theme} locale={koKR}>
<App />
</ConfigProvider>
```
### 폰트 설치 (index.html)
```html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard.css" />
```
### 아이콘: Tabler Icons
```bash
pnpm add @tabler/icons-react
```
```tsx
import { IconUsers, IconCalculator, IconDownload } from '@tabler/icons-react';
<IconUsers size={16} />
```
---
## 2. 글로벌 CSS 변수 (styles/variables.css)
```css
:root {
/* 브랜드 */
--ga-primary: #1677FF;
--ga-primary-light: #E6F1FB;
--ga-primary-dark: #0C447C;
/* 상태 색상 (CodeBadge 등) */
--ga-success: #0F6E56;
--ga-success-bg: #E1F5EE;
--ga-warning: #BA7517;
--ga-warning-bg: #FAEEDA;
--ga-danger: #E24B4A;
--ga-danger-bg: #FCEBEB;
--ga-info: #185FA5;
--ga-info-bg: #E6F1FB;
/* 금액 */
--ga-money-plus: #0F6E56;
--ga-money-minus: #E24B4A;
/* 레이아웃 */
--ga-sider-width: 220px;
--ga-sider-collapsed-width: 64px;
--ga-header-height: 48px;
--ga-content-padding: 20px;
--ga-page-max-width: 1400px;
/* 간격 단위 */
--ga-spacing-xs: 4px;
--ga-spacing-sm: 8px;
--ga-spacing-md: 12px;
--ga-spacing-lg: 16px;
--ga-spacing-xl: 24px;
/* 그림자 (최소한만 사용) */
--ga-shadow-card: 0 1px 2px rgba(0,0,0,0.03);
--ga-shadow-dropdown: 0 4px 12px rgba(0,0,0,0.08);
}
```
---
## 3. 레이아웃 규칙
### MainLayout 구조
```
┌─────────────────────────────────────────────┐
│ Header (48px) │
│ [☰] GA 정산시스템 [2026-05 ▼] [🔔3] [관리자]│
├──────┬──────────────────────────────────────┤
│Sider │ Content │
│220px │ Breadcrumb │
│ │ ┌─────────────────────────────────┐ │
│ 메뉴 │ │ 페이지 내용 │ │
│ │ │ │ │
│ │ └─────────────────────────────────┘ │
└──────┴──────────────────────────────────────┘
```
### Header
- 높이: 48px, 배경: 흰색, 하단 border
- 왼쪽: 햄버거 메뉴(접기/펼치기) + 로고 "GA 정산시스템"
- 오른쪽: 정산월 MonthPicker + 알림벨(미읽음 숫자뱃지) + 사용자 Dropdown
### Sider (사이드 메뉴)
- 너비: 220px (접으면 64px, 아이콘만)
- 배경: 흰색
- 메뉴 아이템: 높이 40px, 좌측 아이콘 16px + 텍스트 13px
- 활성 메뉴: 배경 #E6F1FB, 텍스트 #185FA5, font-weight 500
- 서브메뉴: 좌측 30px 인덴트
- 메뉴 데이터: API에서 동적 로드 (GET /api/menus/my)
### Content
- Breadcrumb: 상단 (홈 > 조직관리 > 설계사목록)
- 패딩: 20px
- 최대 너비: 1400px (중앙 정렬)
---
## 4. 페이지 표준 레이아웃 (3가지)
### 타입 A: 목록 페이지 (가장 많이 사용)
```
┌──── 검색 폼 (SearchForm) ─────────────────┐
│ [설계사명 ___] [상태 ▼전체] [기간 ___~___] [조회] [초기화] │
└────────────────────────────────────────────┘
┌──── 버튼 영역 ─────────────────────────────┐
│ [+ 등록] [⬇ 엑셀] │
└────────────────────────────────────────────┘
┌──── 데이터 테이블 ─────────────────────────┐
│ □ 설계사명 소속 직급 수수료합계 상태│
│ □ 김영수 서울1팀 수석 12,450,000 위촉│
│ □ 이미영 경기2팀 설계사 8,320,000 위촉│
├────────────────────────────────────────────┤
│ 총 1,234건 1 2 3 ...
└────────────────────────────────────────────┘
```
- 검색 폼: 회색 배경(#fafafa), border-radius 8px, padding 12px 16px
- 검색 조건: 한 줄에 3~4개, 버튼은 우측
- 버튼 영역: 우측 정렬, 테이블 바로 위 margin-bottom 12px
- 등록 버튼: Primary(파란색), 엑셀: Default(흰색)
- 테이블: antd Table 또는 AG Grid
### 타입 B: 상세 페이지
```
┌──── 기본 정보 카드 ─────────────────────────┐
│ 설계사명: 김영수 소속: 서울1팀 상태: [위촉] │
│ 전화번호: 010-****-5678 이메일: kim@ga.com │
│ [수정] [삭제] │
└─────────────────────────────────────────────┘
┌──── 탭 영역 ────────────────────────────────┐
│ [기본정보] [실적현황] [소속이력] [변경이력] │
├─────────────────────────────────────────────┤
│ 탭 내용... │
└─────────────────────────────────────────────┘
```
- 상단 카드: 흰색 배경, border, border-radius 8px
- 탭: antd Tabs, 카드 안에 포함
- 버튼: 카드 우상단
### 타입 C: 대시보드
```
┌──── 지표 카드 (4열) ───────────────────────┐
│ [모집 2.4억] [유지 8,200만] [환수 1,350만] [순 3.1억]│
└────────────────────────────────────────────┘
┌─── 차트 (2:1) ──────┬─── 현황 ────────────┐
│ 월별 추이 (recharts) │ 배치 진행 상태 │
│ ▆▆▆▇█ │ ● 수신 완료 │
│ │ ● 계산 진행중... │
└─────────────────────┴──────────────────────┘
┌──── 알림/오류 ──────────────────────────────┐
│ [대사불일치] 삼성생명 5월 3건 차이 10분 전 │
│ [파싱에러] 한화생명 12행 형식 오류 1시간 전 │
└────────────────────────────────────────────┘
```
- 지표 카드: 배경 #fafafa, border-radius 8px, padding 12px
- 숫자: 20~22px, weight 500
- 증감: ▲ 초록(양수), ▼ 빨강(음수)
- 차트: recharts, 높이 200~240px
---
## 5. 공통 컴포넌트 스타일 규칙
### CodeBadge (상태 뱃지)
```tsx
// 상태값 → 색상 자동 매핑
const COLOR_MAP = {
// 초록 (긍정적 완료 상태)
CONFIRMED: 'success', APPROVED: 'success', ACTIVE: 'success',
PAID: 'success', COMPLETED: 'success', MATCH: 'success',
// 파랑 (진행 중)
CALCULATED: 'info', PENDING: 'info', PROCESSING: 'info',
REVIEWED: 'info', NEW: 'info',
// 주황 (주의)
HOLD: 'warning', SUSPEND: 'warning', MISMATCH: 'warning',
PARTIAL: 'warning',
// 빨강 (부정적)
REJECTED: 'danger', LEAVE: 'danger', CANCEL: 'danger',
FAILED: 'danger', LAPSED: 'danger',
// 회색 (기본)
DRAFT: 'default', READY: 'default',
};
// 렌더링
<Tag color={COLOR_MAP[value] || 'default'}>{codeName}</Tag>
```
### MoneyText (금액 표시)
```tsx
// 양수: 초록, 음수: 빨강, 0: 회색
// 항상 천 단위 콤마, 정수 표시
<span style={{ color: amount > 0 ? '#0F6E56' : amount < 0 ? '#E24B4A' : '#888' }}>
{amount >= 0 ? '' : '-'}{Math.abs(amount).toLocaleString()}
</span>
```
### SearchForm (검색 폼)
```tsx
// 스타일 규칙
// 배경: #fafafa, border-radius: 8px, padding: 12px 16px
// 조건: Row + Col (span 기준)
// - 텍스트 입력: span=6 (4개/행)
// - Select: span=4 (6개/행까지)
// - DateRange: span=8
// - 버튼: 우측 정렬
<div style={{
background: '#fafafa',
borderRadius: 8,
padding: '12px 16px',
marginBottom: 16,
}}>
<Row gutter={[12, 12]} align="bottom">
<Col span={6}>
<div style={{ fontSize: 12, color: '#999', marginBottom: 4 }}></div>
<Input placeholder="이름 검색" />
</Col>
<Col span={4}>
<div style={{ fontSize: 12, color: '#999', marginBottom: 4 }}></div>
<CodeSelect groupCode="AGENT_STATUS" allowClear placeholder="전체" />
</Col>
<Col span={4} style={{ marginLeft: 'auto' }}>
<Space>
<Button type="primary" icon={<IconSearch size={14}/>}></Button>
<Button type="link"></Button>
</Space>
</Col>
</Row>
</div>
```
### DataTable (목록 테이블)
```tsx
// 스타일 규칙
// 행 높이: 42px (compact)
// 헤더: 배경 #fafafa, font-weight 500, font-size 12px
// 본문: font-size 13px
// hover: 배경 #f5f7fa
// 금액 컬럼: 우측 정렬, MoneyText
// 상태 컬럼: 중앙 정렬, CodeBadge
// 이름 컬럼: font-weight 500, 클릭 시 상세 이동
// 페이지네이션: 우측, "총 N건" 좌측
<Table
size="small"
rowSelection={{ type: 'checkbox' }}
pagination={{
showTotal: (total) => `${total.toLocaleString()}`,
showSizeChanger: true,
pageSizeOptions: [20, 50, 100],
defaultPageSize: 20,
}}
/>
```
### DataGrid (AG Grid 래퍼)
```tsx
// AG Grid 공통 설정
const defaultColDef = {
sortable: true,
resizable: true,
filter: true,
minWidth: 80,
};
const gridOptions = {
rowHeight: 36,
headerHeight: 38,
// 한글 로케일
localeText: AG_GRID_LOCALE_KO,
// 합계행
pinnedBottomRowData: [summaryRow],
// 셀 스타일
getRowStyle: (params) => {
if (params.node.rowPinned) return { background: '#fafafa', fontWeight: 500 };
},
};
// AG Grid 사용 화면: 원장관리, 정산결과, 수수료율편집, 대사현황
// 나머지는 Ant Design Table
```
---
## 6. 등록/수정 폼 규칙
```tsx
// 폼 레이아웃 규칙
// 2열 배치 (Row > Col span=12)
// 레이블: 12px, color #999, 필수 표시 빨간 *
// 입력: height 32px
// Select: 공통코드는 CodeSelect 사용
// 하단 버튼: 우측 정렬 [취소] [저장]
<Card>
<Form layout="vertical" form={form}>
<Row gutter={16}>
<Col span={12}>
<Form.Item label="설계사명" name="agentName" rules={[{ required: true }]}>
<Input />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="소속" name="orgId" rules={[{ required: true }]}>
<TreeSelect treeData={orgTree} />
</Form.Item>
</Col>
</Row>
<Row gutter={16}>
<Col span={12}>
<Form.Item label="직급" name="gradeId">
<CodeSelect groupCode="GRADE" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="상태" name="status">
<CodeSelect groupCode="AGENT_STATUS" />
</Form.Item>
</Col>
</Row>
<div style={{ textAlign: 'right', marginTop: 16 }}>
<Space>
<Button onClick={onCancel}></Button>
<Button type="primary" onClick={onSubmit}></Button>
</Space>
</div>
</Form>
</Card>
```
---
## 7. 모달/드로워 규칙
```
// 등록/수정 폼 → Drawer (우측에서 슬라이드)
// 너비: 600px (2열 폼), 400px (단순 폼)
// 제목: "설계사 등록" / "설계사 수정"
// 하단: 취소 + 저장 버튼
// 상세 보기 → Modal
// 너비: 720px
// Descriptions 컴포넌트로 상세 표시
// 확인/경고 → Modal.confirm()
// "정산을 확정하시겠습니까?" [취소] [확정]
// 삭제 → Modal.confirm({ type: 'warning' })
// "삭제하면 복구할 수 없습니다" [취소] [삭제]
```
---
## 8. 화면별 상세 와이어프레임
### 8.1 대시보드 (/)
```
레이아웃: 타입 C (대시보드)
├── 지표 카드 4열: 모집수수료, 유지수수료, 환수금, 순지급액
│ - 이번 달 금액 (억/만 단위 자동)
│ - 전월 대비 증감률 (▲초록/▼빨강)
│ - 카드 클릭 → 해당 원장 페이지 이동
├── 2열 영역
│ ├── 왼쪽(60%): 월별 추이 차트 (recharts BarChart)
│ │ - X축: 최근 6개월, Y축: 금액
│ │ - 시리즈: 모집(파랑), 유지(초록), 환수(빨강)
│ └── 오른쪽(40%): 배치 현황 카드
│ - Step별 상태: ● 완료(초록), ● 진행중(파랑+pulse), ○ 대기(회색)
│ - 각 Step: 이름 + 완료시간/진행시간
│ - 에러 시: ● 빨강 + 에러 메시지
└── 알림/오류 패널
- 대사 불일치, 파싱 에러, 승인 대기 등
- 뱃지 색상: 에러=빨강, 경고=주황, 정보=파랑
- 클릭 → 해당 화면 이동
```
### 8.2 조직관리 (/org)
```
레이아웃: 2열 (좌 300px 트리 + 우 나머지 목록)
├── 좌측: 조직 트리 (antd Tree)
│ - 본사 > 지역본부 > 지점 > 팀
│ - 드래그앤드롭 정렬 (관리자만)
│ - 노드 클릭 → 우측 설계사 목록 필터링
│ - 우클릭 → 추가/수정/삭제 컨텍스트메뉴
└── 우측: 설계사 목록 (타입 A)
- 검색: 이름, 상태, 직급
- 테이블 컬럼: 설계사명, 직급, 상태, 위촉일, 수수료합계
- 행 클릭 → 상세 (타입 B, 탭: 기본정보/실적/이력)
```
### 8.3 원장관리 (/ledger)
```
레이아웃: 탭 3개 (모집/유지/예외)
├── 공통 검색: 정산월, 보험사, 설계사, 상태
├── AG Grid (DataGrid) 사용 ← 대량 데이터
│ - 서버사이드 페이징/정렬/필터
│ - 컬럼 고정: 왼쪽(증권번호, 설계사명), 오른쪽(수수료합계)
│ - 합계행: 하단 고정 (pinnedBottomRow)
│ - 금액 셀: 우측 정렬, MoneyText 스타일
│ - 상태 셀: CodeBadge
├── 예외금액 탭 추가 기능:
│ - [+ 등록] → Drawer (예외유형, 금액, 방향, 설명)
│ - 승인 워크플로우: 작성→대기→승인/반려
│ - 승인 버튼: PermissionButton(permCode="APPROVE")
└── 엑셀 다운로드: ExcelExportButton (100만건, 진행률)
```
### 8.4 정산관리 (/settle)
```
레이아웃: 탭 4개 (배치실행/정산결과/정산상세/대사)
[배치실행 탭]
├── 정산월 선택 + [정산실행] 버튼
├── 진행 상태: Steps 컴포넌트 (8단계)
│ - 각 Step: 이름 + 처리건수 + 소요시간 + 상태아이콘
│ - 진행 중: 폴링으로 실시간 업데이트 (3초 간격)
│ - 에러: Step 클릭 → 에러 상세 모달
└── 완료 시: 결과 요약 카드 (총건수, 성공, 에러, 소요시간)
[정산결과 탭]
├── AG Grid: 설계사별 정산 결과
│ - 컬럼: 설계사명, 소속, 모집, 유지, 예외가산, 예외차감, 오버라이드, 환수, 세전, 세금, 순지급
│ - 합계행
│ - 상태별 필터: 계산완료/검토/확정/지급/보류
│ - 행 클릭 → 정산상세 탭으로 이동
└── 버튼: [일괄확정] [엑셀다운로드]
[정산상세 탭]
├── 설계사 선택 (상단)
├── 요약 카드 4열: 모집합계, 유지합계, 예외합계, 순지급액
└── 탭 5개: 모집수수료 / 유지수수료 / 예외금액 / 오버라이드 / 환수
- 각 탭 AG Grid로 상세 내역 표시
[대사 탭]
├── 보험사별 대사 결과 테이블
│ - 보험사, 보험사건수, 시스템건수, 차이, 보험사금액, 시스템금액, 차이, 상태
│ - 상태: MATCH(초록), MISMATCH(빨강)
└── 불일치 행 클릭 → 상세 모달 (건별 차이 목록)
```
### 8.5 수수료규정 (/rules)
```
레이아웃: 탭 4개 (수수료율/지급률/오버라이드/환수)
각 탭 공통:
├── 검색: 보험사/보험종류/적용기간
├── AG Grid: 셀 편집 가능 (인라인 수정)
│ - 수정된 셀: 노란색 배경 강조
│ - 하단: [저장] [취소] 버튼
├── 버전 관리:
│ - 규정 수정 시 → version 자동 증가 + 기존 규정 effective_to 설정
│ - [이력보기] 버튼 → 버전별 변경 내역 타임라인
└── 신규 등록: [+ 규정 등록] → Drawer
```
### 8.6 시스템관리 (/system)
#### 역할관리 (/system/roles)
```
├── 역할 목록 테이블
└── 역할 클릭 → 권한 매트릭스
┌──────────┬──────┬──────┬──────┬──────┬──────┬──────┐
│ 메뉴 │ 조회 │ 등록 │ 수정 │ 삭제 │ 승인 │ 엑셀 │
├──────────┼──────┼──────┼──────┼──────┼──────┼──────┤
│ 조직관리 │ ☑ │ ☑ │ ☑ │ ☐ │ ☐ │ ☑ │
│ 정산관리 │ ☑ │ ☐ │ ☐ │ ☐ │ ☑ │ ☑ │
│ 시스템관리 │ ☑ │ ☑ │ ☑ │ ☑ │ ☐ │ ☐ │
└──────────┴──────┴──────┴──────┴──────┴──────┴──────┘
[전체선택/해제 토글] [저장]
```
#### 공통코드관리 (/system/codes)
```
2열 레이아웃:
├── 좌측 (300px): 코드 그룹 리스트
│ - 선택 시 우측에 상세 코드 표시
│ - 시스템 코드: 🔒 아이콘 + 수정 불가
└── 우측: 상세 코드 테이블
- 코드값, 코드명, 영문명, 정렬순서, 활성여부
- 인라인 편집 가능 (시스템코드 제외)
```
#### 업로드 템플릿 관리 (/system/upload-templates)
```
2열 레이아웃:
├── 좌측 (300px): 템플릿 목록 (카테고리별 필터)
└── 우측: 컬럼 매핑 편집기
- 각 행: [엑셀 컬럼 입력] → [DB 필드 Select] → [변환규칙 Select] → [파라미터]
- 드래그앤드롭 순서 변경
- [+ 행 추가] [미리보기] [저장]
- 미리보기: 샘플 엑셀 업로드 → 첫 5행 매핑 결과 테이블
```
---
## 9. 반응형 규칙
```
// 이 솔루션은 데스크톱 전용 (1280px 이상)
// 모바일 대응 불필요 (사내 관리 시스템)
// 최소 해상도: 1280 x 720
// 권장 해상도: 1920 x 1080
// 1280px 미만: Sider 자동 접힘 (아이콘만)
// 1920px 이상: Content 최대 너비 1400px 중앙 정렬
```
---
## 10. 신입 개발자 UI 체크리스트
새 화면 만들 때 아래 항목 확인:
- [ ] 검색 폼: SearchForm 컴포넌트 사용, 배경 #fafafa
- [ ] 목록: 단순이면 DataTable, 대량/편집이면 DataGrid
- [ ] 상태 컬럼: CodeBadge 사용 (하드코딩 색상 금지)
- [ ] 금액 컬럼: MoneyText 사용 (양수 초록, 음수 빨강)
- [ ] Select: CodeSelect 사용 (공통코드 연동)
- [ ] 버튼: PermissionButton 사용 (권한 제어)
- [ ] 엑셀: ExcelExportButton 사용
- [ ] 등록/수정: Drawer 사용 (모달 아님)
- [ ] 삭제: Modal.confirm 사용
- [ ] 폼: antd Form + 2열(Col span=12) + layout="vertical"
- [ ] 금액 입력: InputNumber + formatter (천단위 콤마)
- [ ] 날짜: DatePicker + dayjs
- [ ] 정산월: MonthPicker 공통 컴포넌트