docs: [종합정비] Phase 1 시스템 현황 문서 14개 작성
- system/overview.md: 전체 아키텍처 개요 - system/api-structure.md: API 구조 (220 모델, 1027 엔드포인트, 18 라우트 도메인) - system/react-structure.md: React 구조 (249 페이지, 612 컴포넌트) - system/mng-structure.md: MNG 구조 (171 컨트롤러, 436 Blade 뷰) - system/docker-setup.md: Docker 7 컨테이너 구성 - system/database/README.md + 9개 도메인 스키마 (270+ 테이블) - core, hr, sales, production, finance, boards, files, system, erp-analysis Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
208
system/api-structure.md
Normal file
208
system/api-structure.md
Normal file
@@ -0,0 +1,208 @@
|
||||
# API 서버 구조 현황
|
||||
|
||||
> **최종 갱신**: 2026-02-27
|
||||
> **기술 스택**: Laravel 12 + PHP 8.4 + Sanctum + Spatie Permission + L5-Swagger
|
||||
|
||||
---
|
||||
|
||||
## 1. 프로젝트 규모
|
||||
|
||||
| 항목 | 수량 |
|
||||
|------|------|
|
||||
| API 엔드포인트 | ~1,027 |
|
||||
| 컨트롤러 | 131 |
|
||||
| 모델 | 220 |
|
||||
| 서비스 | 180 |
|
||||
| FormRequest | 271 |
|
||||
| 미들웨어 | 9 |
|
||||
| Swagger 문서 | 110 |
|
||||
| 마이그레이션 | 459 |
|
||||
| 설정 파일 | 22 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 디렉토리 구조
|
||||
|
||||
```
|
||||
app/
|
||||
├── Http/
|
||||
│ ├── Controllers/Api/
|
||||
│ │ ├── Admin/ 글로벌 메뉴 관리
|
||||
│ │ └── V1/ 메인 API (131개)
|
||||
│ │ ├── Admin/ FCM 관리
|
||||
│ │ ├── Audit/ 감사 로그
|
||||
│ │ ├── Construction/ 시공 (작업지시, 결과, 설정)
|
||||
│ │ ├── Design/ 설계 (모델, 버전, BOM 템플릿)
|
||||
│ │ ├── Documents/ 문서 (템플릿, 생성)
|
||||
│ │ ├── ESign/ 전자서명
|
||||
│ │ ├── ItemMaster/ 품목 마스터 (9개)
|
||||
│ │ └── [104 Root Controllers]
|
||||
│ ├── Middleware/ 9개
|
||||
│ └── Requests/ 271개 (FormRequest)
|
||||
├── Models/ 220개 (32개 도메인)
|
||||
├── Services/ 180개
|
||||
├── Swagger/v1/ 110개 (OpenAPI 문서)
|
||||
├── Helpers/ 4개
|
||||
├── Observers/ 5개
|
||||
├── Traits/ 모델/감사 트레이트
|
||||
└── Exceptions/ 2개
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 라우트 도메인별 엔드포인트
|
||||
|
||||
| 라우트 파일 | 엔드포인트 | 주요 기능 |
|
||||
|------------|-----------|----------|
|
||||
| finance.php | 180 | 재무 (결제, 청구서, 세금계산서, 급여) |
|
||||
| hr.php | 141 | 인사 (급여, 근태, 휴가, 대출) |
|
||||
| common.php | 128 | 공통 (코드, 분류, 카테고리, 필드) |
|
||||
| sales.php | 122 | 영업 (수주, 견적, 거래처, 출하) |
|
||||
| boards.php | 84 | 게시판 (동적 필드 지원) |
|
||||
| inventory.php | 79 | 재고 (자재, 입고, 창고) |
|
||||
| production.php | 67 | 생산 (작업지시, 작업실적, 공정) |
|
||||
| design.php | 61 | 설계 (모델, 버전, BOM 템플릿) |
|
||||
| users.php | 34 | 사용자 (프로필, 역할, 초대) |
|
||||
| admin.php | 29 | 관리자 (테넌트, 메뉴, FCM) |
|
||||
| tenants.php | 23 | 테넌트 (전환, 프로필) |
|
||||
| files.php | 20 | 파일 (업로드, 저장, 조회) |
|
||||
| esign.php | 18 | 전자서명 워크플로우 |
|
||||
| documents.php | 17 | 문서 (템플릿, 생성) |
|
||||
| auth.php | 9 | 인증 (로그인, 로그아웃, 등록) |
|
||||
| audit.php | 7 | 감사 (로그, 이력) |
|
||||
| stats.php | 5 | 통계/리포트 |
|
||||
| app.php | 3 | 앱 버전, 상태 체크 |
|
||||
| **합계** | **~1,027** | |
|
||||
|
||||
```
|
||||
routes/
|
||||
├── api.php 메인 API 진입점
|
||||
└── api/v1/
|
||||
├── admin.php
|
||||
├── app.php
|
||||
├── audit.php
|
||||
├── auth.php
|
||||
├── boards.php
|
||||
├── common.php
|
||||
├── design.php
|
||||
├── documents.php
|
||||
├── esign.php
|
||||
├── files.php
|
||||
├── finance.php
|
||||
├── hr.php
|
||||
├── inventory.php
|
||||
├── production.php
|
||||
├── sales.php
|
||||
├── stats.php
|
||||
├── tenants.php
|
||||
└── users.php
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 미들웨어
|
||||
|
||||
| 미들웨어 | 역할 |
|
||||
|---------|------|
|
||||
| ApiKeyMiddleware | API 키 인증 + Sanctum 토큰 검증 |
|
||||
| ApiRateLimiter | API 속도 제한 |
|
||||
| ApiVersionMiddleware | API 버전 라우팅 |
|
||||
| CheckPermission | 권한 기반 접근 제어 |
|
||||
| CheckSwaggerAuth | Swagger UI 인증 |
|
||||
| CorsMiddleware | CORS 정책 |
|
||||
| LogApiRequest | API 요청/응답 로깅 |
|
||||
| PermMapper | 권한 매핑 변환 |
|
||||
| SetAuditSessionVariables | 감사 세션 컨텍스트 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 아키텍처 패턴
|
||||
|
||||
### 인증 체계
|
||||
- **글로벌**: API Key (모든 요청)
|
||||
- **사용자**: Sanctum Bearer Token (Access 120분, Refresh 7일)
|
||||
- **내부**: HMAC (`INTERNAL_EXCHANGE_SECRET`, mng ↔ api)
|
||||
|
||||
### 응답 포맷
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "message.key.from.lang",
|
||||
"data": { ... }
|
||||
}
|
||||
```
|
||||
- `ApiResponse::handle()` 헬퍼로 통일
|
||||
|
||||
### 핵심 패턴
|
||||
- **Service-First**: 비즈니스 로직 → Service 클래스
|
||||
- **FormRequest 필수**: Controller에서 직접 검증 금지
|
||||
- **BelongsToTenant**: 모든 비즈니스 모델에 tenant_id 격리
|
||||
- **Auditable**: created_by, updated_by, deleted_by 자동 기록
|
||||
- **SoftDeletes**: 대부분의 엔티티 논리 삭제
|
||||
|
||||
### 헬퍼
|
||||
|
||||
| 헬퍼 | 역할 |
|
||||
|------|------|
|
||||
| ApiResponse | JSON 응답 포맷 통일 |
|
||||
| ItemTypeHelper | 품목 분류 로직 |
|
||||
| Legacy5130Calculator | 레거시 5130 연동 계산 |
|
||||
| TenantCodeGenerator | 테넌트별 코드 생성 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 주요 의존성
|
||||
|
||||
| 패키지 | 버전 | 용도 |
|
||||
|--------|------|------|
|
||||
| laravel/framework | ^12.0 | 코어 프레임워크 |
|
||||
| laravel/sanctum | ^4.0 | API 토큰 인증 |
|
||||
| spatie/laravel-permission | ^6.21 | RBAC 권한 관리 |
|
||||
| darkaonline/l5-swagger | ^9.0 | Swagger/OpenAPI 문서 |
|
||||
| maatwebsite/excel | ^3.1 | Excel 가져오기/내보내기 |
|
||||
| google/auth | ^1.49 | Google 인증 |
|
||||
| doctrine/dbal | ^4.3 | DB 추상화 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 설정
|
||||
|
||||
```
|
||||
config/
|
||||
├── app.php 앱 메타데이터
|
||||
├── audit.php 감사 로깅 (13개월 보존)
|
||||
├── auth.php 인증 (Sanctum, API 키)
|
||||
├── authz.php 인가 + 권한
|
||||
├── cors.php CORS 정책
|
||||
├── custom.php 커스텀 앱 설정
|
||||
├── database.php DB 연결 (samdb, sam_stat)
|
||||
├── fcm.php Firebase Cloud Messaging
|
||||
├── l5-swagger.php Swagger 설정
|
||||
├── pagination.php 페이지네이션 기본값
|
||||
├── permission.php Spatie Permission
|
||||
├── products.php 제품별 설정
|
||||
├── sanctum.php Sanctum 토큰 설정
|
||||
└── [기타 8개]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. api ↔ mng ↔ react 관계
|
||||
|
||||
```
|
||||
react (dev.sam.kr) ──Server Actions──→ api (api.sam.kr) ←──DB 공유──→ mng (mng.sam.kr)
|
||||
│
|
||||
MySQL (samdb)
|
||||
```
|
||||
|
||||
- **api**: DB 마이그레이션 유일 관리자 (459개). 모든 테이블 정의
|
||||
- **mng**: 자체 모델 (185개), 마이그레이션 없음. 동일 DB 직접 접근
|
||||
- **react**: Server Actions로 api 호출. DB 직접 접근 없음
|
||||
- **내부 통신**: HMAC 기반 `INTERNAL_EXCHANGE_SECRET` (mng → api)
|
||||
- **파일 공유**: mng가 api의 `storage/app/tenants` 마운트
|
||||
|
||||
### Swagger 문서
|
||||
- 110개 OpenAPI 문서 파일
|
||||
- 도메인별 태그 구성
|
||||
- 보안 스키마: ApiKeyAuth + BearerAuth (Sanctum)
|
||||
- URL: `api.sam.kr/api/documentation`
|
||||
523
system/board-system-spec.md
Normal file
523
system/board-system-spec.md
Normal file
@@ -0,0 +1,523 @@
|
||||
# 게시판 시스템 스펙
|
||||
|
||||
**작성일**: 2025-11-27
|
||||
**상태**: 설계 완료, 구현 대기
|
||||
**관련 프로젝트**: mng, api
|
||||
|
||||
---
|
||||
|
||||
## 1. 개요
|
||||
|
||||
### 1.1 목적
|
||||
- mng에서 **시스템 게시판**을 생성하여 모든 테넌트에게 제공
|
||||
- sam(api)에서 테넌트별 **자체 게시판** 생성 가능
|
||||
- 공지사항, 1:1 문의, FAQ 등 다양한 게시판 유형 지원
|
||||
|
||||
### 1.2 핵심 개념
|
||||
|
||||
| 구분 | 설명 |
|
||||
|------|------|
|
||||
| **시스템 게시판** | mng에서 생성, `is_system=true`, 모든 테넌트에서 접근 가능 |
|
||||
| **테넌트 게시판** | sam에서 생성, `is_system=false`, 해당 테넌트만 접근 |
|
||||
| **board_type** | 자유 입력 (notice, qna, faq, free 등 제한 없음) |
|
||||
|
||||
### 1.3 역할 분리
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ mng (상위 관리자) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ • 시스템 게시판 CRUD (is_system = true) │
|
||||
│ • 게시판 필드 정의 (board_settings) │
|
||||
│ • 게시판 유형 자유 설정 (board_type) │
|
||||
│ • tenant_id = NULL │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
↓ 전체 테넌트 공개
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ sam (api - 테넌트용) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ • 테넌트 게시판 CRUD (is_system = false) │
|
||||
│ • 시스템 게시판 사용 (읽기 전용 구조) │
|
||||
│ • 게시글 CRUD (posts) │
|
||||
│ • tenant_id = 현재 테넌트 │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 데이터베이스 스키마
|
||||
|
||||
### 2.1 기존 테이블 현황
|
||||
|
||||
현재 구현된 테이블 (6개):
|
||||
- `boards` - 게시판 정의
|
||||
- `board_settings` - EAV 커스텀 필드 정의
|
||||
- `posts` - 게시글
|
||||
- `post_custom_field_values` - EAV 커스텀 필드 값
|
||||
- `board_comments` - 댓글
|
||||
- `board_files` - 파일 첨부
|
||||
|
||||
### 2.2 boards 테이블 확장
|
||||
|
||||
**추가 컬럼:**
|
||||
|
||||
| 컬럼 | 타입 | 설명 |
|
||||
|------|------|------|
|
||||
| `is_system` | TINYINT(1) DEFAULT 0 | 시스템 게시판 여부 (1=전체 공개) |
|
||||
| `board_type` | VARCHAR(50) NULL | 게시판 유형 (자유 입력) |
|
||||
|
||||
**변경 컬럼:**
|
||||
|
||||
| 컬럼 | 변경 내용 |
|
||||
|------|----------|
|
||||
| `tenant_id` | NOT NULL → **NULL 허용** (시스템 게시판용) |
|
||||
|
||||
**최종 boards 테이블 구조:**
|
||||
|
||||
```sql
|
||||
CREATE TABLE boards (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
tenant_id BIGINT UNSIGNED NULL COMMENT '테넌트 ID (NULL=시스템 게시판)',
|
||||
is_system TINYINT(1) DEFAULT 0 COMMENT '시스템 게시판 여부 (1=전체 테넌트 공개)',
|
||||
board_type VARCHAR(50) NULL COMMENT '게시판 유형 (notice, qna, faq, free 등)',
|
||||
board_code VARCHAR(50) NOT NULL COMMENT '게시판 코드 (URL용)',
|
||||
name VARCHAR(100) NOT NULL COMMENT '게시판명',
|
||||
description TEXT NULL COMMENT '게시판 설명',
|
||||
editor_type VARCHAR(20) DEFAULT 'wysiwyg' COMMENT '에디터 타입',
|
||||
allow_files TINYINT(1) DEFAULT 1 COMMENT '파일 첨부 허용',
|
||||
max_file_count INT DEFAULT 5 COMMENT '최대 파일 수',
|
||||
max_file_size INT DEFAULT 20480 COMMENT '최대 파일 크기 (KB)',
|
||||
extra_settings JSON NULL COMMENT '추가 설정 (권한, 옵션 등)',
|
||||
is_active TINYINT(1) DEFAULT 1 COMMENT '활성 여부',
|
||||
created_at TIMESTAMP NULL,
|
||||
updated_at TIMESTAMP NULL,
|
||||
created_by BIGINT UNSIGNED NULL,
|
||||
updated_by BIGINT UNSIGNED NULL,
|
||||
deleted_at TIMESTAMP NULL,
|
||||
deleted_by BIGINT UNSIGNED NULL,
|
||||
|
||||
INDEX idx_boards_tenant (tenant_id),
|
||||
INDEX idx_boards_is_system (is_system),
|
||||
INDEX idx_boards_board_type (board_type),
|
||||
UNIQUE INDEX uk_boards_code (tenant_id, board_code)
|
||||
);
|
||||
```
|
||||
|
||||
### 2.3 board_settings 테이블 (EAV 필드 정의)
|
||||
|
||||
현재 구조 유지:
|
||||
|
||||
```sql
|
||||
CREATE TABLE board_settings (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
board_id BIGINT UNSIGNED NOT NULL COMMENT '게시판 ID',
|
||||
name VARCHAR(100) NOT NULL COMMENT '필드명',
|
||||
field_key VARCHAR(50) NOT NULL COMMENT '필드 키',
|
||||
field_type VARCHAR(30) NOT NULL COMMENT '필드 타입 (text, number, select, date 등)',
|
||||
field_meta JSON NULL COMMENT '필드 메타 (옵션, 유효성 등)',
|
||||
is_required TINYINT(1) DEFAULT 0 COMMENT '필수 여부',
|
||||
sort_order INT DEFAULT 0 COMMENT '정렬 순서',
|
||||
created_at TIMESTAMP NULL,
|
||||
updated_at TIMESTAMP NULL,
|
||||
created_by BIGINT UNSIGNED NULL,
|
||||
updated_by BIGINT UNSIGNED NULL,
|
||||
|
||||
INDEX idx_board_settings_board (board_id),
|
||||
FOREIGN KEY (board_id) REFERENCES boards(id) ON DELETE CASCADE
|
||||
);
|
||||
```
|
||||
|
||||
### 2.4 posts 테이블
|
||||
|
||||
현재 구조 유지 (SoftDeletes 적용):
|
||||
|
||||
```sql
|
||||
CREATE TABLE posts (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID',
|
||||
board_id BIGINT UNSIGNED NOT NULL COMMENT '게시판 ID',
|
||||
user_id BIGINT UNSIGNED NOT NULL COMMENT '작성자 ID',
|
||||
title VARCHAR(255) NOT NULL COMMENT '제목',
|
||||
content LONGTEXT NULL COMMENT '내용',
|
||||
editor_type VARCHAR(20) DEFAULT 'wysiwyg' COMMENT '에디터 타입',
|
||||
ip_address VARCHAR(45) NULL COMMENT 'IP 주소',
|
||||
is_notice TINYINT(1) DEFAULT 0 COMMENT '공지 여부',
|
||||
is_secret TINYINT(1) DEFAULT 0 COMMENT '비밀글 여부',
|
||||
views INT DEFAULT 0 COMMENT '조회수',
|
||||
status VARCHAR(20) DEFAULT 'active' COMMENT '상태',
|
||||
created_at TIMESTAMP NULL,
|
||||
updated_at TIMESTAMP NULL,
|
||||
created_by BIGINT UNSIGNED NULL,
|
||||
updated_by BIGINT UNSIGNED NULL,
|
||||
deleted_at TIMESTAMP NULL,
|
||||
deleted_by BIGINT UNSIGNED NULL,
|
||||
|
||||
INDEX idx_posts_tenant_board (tenant_id, board_id),
|
||||
INDEX idx_posts_status (status),
|
||||
FOREIGN KEY (board_id) REFERENCES boards(id) ON DELETE CASCADE
|
||||
);
|
||||
```
|
||||
|
||||
### 2.5 post_custom_field_values 테이블 (EAV 값)
|
||||
|
||||
현재 구조 유지:
|
||||
|
||||
```sql
|
||||
CREATE TABLE post_custom_field_values (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
post_id BIGINT UNSIGNED NOT NULL COMMENT '게시글 ID',
|
||||
field_id BIGINT UNSIGNED NOT NULL COMMENT '필드 ID (board_settings.id)',
|
||||
value TEXT NULL COMMENT '필드 값',
|
||||
created_at TIMESTAMP NULL,
|
||||
updated_at TIMESTAMP NULL,
|
||||
created_by BIGINT UNSIGNED NULL,
|
||||
updated_by BIGINT UNSIGNED NULL,
|
||||
|
||||
INDEX idx_pcfv_post (post_id),
|
||||
INDEX idx_pcfv_field (field_id),
|
||||
FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (field_id) REFERENCES board_settings(id) ON DELETE CASCADE
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 게시판 유형 (board_type)
|
||||
|
||||
### 3.1 권장 유형
|
||||
|
||||
| board_type | 설명 | 특징 |
|
||||
|------------|------|------|
|
||||
| `notice` | 공지사항 | 관리자만 작성, 전체 공개 |
|
||||
| `qna` | 1:1 문의 | 작성자+관리자만 열람, 비밀글 기본 |
|
||||
| `faq` | FAQ | 관리자 작성, 카테고리 분류 |
|
||||
| `free` | 자유게시판 | 댓글 허용, 전체 공개 |
|
||||
| `gallery` | 갤러리 | 이미지 중심 |
|
||||
| `download` | 자료실 | 파일 첨부 중심 |
|
||||
|
||||
### 3.2 유형별 extra_settings 예시
|
||||
|
||||
```json
|
||||
// notice (공지사항)
|
||||
{
|
||||
"write_roles": ["admin", "manager"],
|
||||
"allow_comment": false,
|
||||
"allow_secret": false
|
||||
}
|
||||
|
||||
// qna (1:1 문의)
|
||||
{
|
||||
"write_roles": ["*"],
|
||||
"allow_comment": true,
|
||||
"default_secret": true,
|
||||
"only_author_view": true
|
||||
}
|
||||
|
||||
// faq
|
||||
{
|
||||
"write_roles": ["admin"],
|
||||
"use_category": true,
|
||||
"allow_comment": false
|
||||
}
|
||||
|
||||
// free (자유게시판)
|
||||
{
|
||||
"write_roles": ["*"],
|
||||
"allow_comment": true,
|
||||
"allow_secret": true
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. API 설계
|
||||
|
||||
### 4.1 mng API (시스템 게시판 관리)
|
||||
|
||||
```
|
||||
# 시스템 게시판 CRUD
|
||||
GET /mng/boards # 시스템 게시판 목록
|
||||
POST /mng/boards # 시스템 게시판 생성
|
||||
GET /mng/boards/{id} # 시스템 게시판 상세
|
||||
PUT /mng/boards/{id} # 시스템 게시판 수정
|
||||
DELETE /mng/boards/{id} # 시스템 게시판 삭제
|
||||
|
||||
# 게시판 필드 관리
|
||||
GET /mng/boards/{id}/fields # 필드 목록
|
||||
POST /mng/boards/{id}/fields # 필드 추가
|
||||
PUT /mng/boards/{id}/fields/{fid} # 필드 수정
|
||||
DELETE /mng/boards/{id}/fields/{fid} # 필드 삭제
|
||||
POST /mng/boards/{id}/fields/reorder # 필드 순서 변경
|
||||
```
|
||||
|
||||
### 4.2 sam API (테넌트용)
|
||||
|
||||
```
|
||||
# 게시판 조회 (시스템 + 테넌트)
|
||||
GET /v1/boards # 접근 가능한 게시판 목록
|
||||
GET /v1/boards/{code} # 게시판 상세
|
||||
|
||||
# 테넌트 게시판 관리
|
||||
POST /v1/boards # 테넌트 게시판 생성
|
||||
PUT /v1/boards/{code} # 테넌트 게시판 수정
|
||||
DELETE /v1/boards/{code} # 테넌트 게시판 삭제
|
||||
|
||||
# 게시글 CRUD
|
||||
GET /v1/boards/{code}/posts # 게시글 목록
|
||||
POST /v1/boards/{code}/posts # 게시글 작성
|
||||
GET /v1/boards/{code}/posts/{id} # 게시글 상세
|
||||
PUT /v1/boards/{code}/posts/{id} # 게시글 수정
|
||||
DELETE /v1/boards/{code}/posts/{id} # 게시글 삭제
|
||||
|
||||
# 댓글
|
||||
GET /v1/posts/{id}/comments # 댓글 목록
|
||||
POST /v1/posts/{id}/comments # 댓글 작성
|
||||
PUT /v1/comments/{id} # 댓글 수정
|
||||
DELETE /v1/comments/{id} # 댓글 삭제
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 모델 설계
|
||||
|
||||
### 5.1 Board 모델
|
||||
|
||||
```php
|
||||
// api/app/Models/Boards/Board.php
|
||||
|
||||
namespace App\Models\Boards;
|
||||
|
||||
use App\Traits\BelongsToTenant;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Board extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'tenant_id', 'is_system', 'board_type', 'board_code', 'name',
|
||||
'description', 'editor_type', 'allow_files', 'max_file_count',
|
||||
'max_file_size', 'extra_settings', 'is_active',
|
||||
'created_by', 'updated_by', 'deleted_by',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'is_system' => 'boolean',
|
||||
'is_active' => 'boolean',
|
||||
'allow_files' => 'boolean',
|
||||
'extra_settings' => 'array',
|
||||
];
|
||||
|
||||
/**
|
||||
* 현재 테넌트에서 접근 가능한 게시판
|
||||
* - 시스템 게시판 (is_system = true)
|
||||
* - 해당 테넌트 게시판 (tenant_id = 현재 테넌트)
|
||||
*/
|
||||
public function scopeAccessible($query, int $tenantId)
|
||||
{
|
||||
return $query->where(function ($q) use ($tenantId) {
|
||||
$q->where('is_system', true)
|
||||
->orWhere('tenant_id', $tenantId);
|
||||
})->where('is_active', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 시스템 게시판만 (mng용)
|
||||
*/
|
||||
public function scopeSystemOnly($query)
|
||||
{
|
||||
return $query->where('is_system', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 테넌트 게시판만
|
||||
*/
|
||||
public function scopeTenantOnly($query, int $tenantId)
|
||||
{
|
||||
return $query->where('is_system', false)
|
||||
->where('tenant_id', $tenantId);
|
||||
}
|
||||
|
||||
// 관계
|
||||
public function fields()
|
||||
{
|
||||
return $this->hasMany(BoardSetting::class, 'board_id')
|
||||
->orderBy('sort_order');
|
||||
}
|
||||
|
||||
public function posts()
|
||||
{
|
||||
return $this->hasMany(Post::class, 'board_id');
|
||||
}
|
||||
|
||||
// Helper
|
||||
public function getSetting(string $key, $default = null)
|
||||
{
|
||||
return data_get($this->extra_settings, $key, $default);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 BoardService
|
||||
|
||||
```php
|
||||
// api/app/Services/Boards/BoardService.php
|
||||
|
||||
namespace App\Services\Boards;
|
||||
|
||||
use App\Models\Boards\Board;
|
||||
use App\Services\Service;
|
||||
|
||||
class BoardService extends Service
|
||||
{
|
||||
/**
|
||||
* 접근 가능한 게시판 목록 (시스템 + 테넌트)
|
||||
*/
|
||||
public function getAccessibleBoards(array $filters = [])
|
||||
{
|
||||
return Board::accessible($this->tenantId())
|
||||
->when(isset($filters['board_type']), fn($q) =>
|
||||
$q->where('board_type', $filters['board_type']))
|
||||
->orderBy('is_system', 'desc')
|
||||
->orderBy('name')
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 시스템 게시판 목록 (mng용)
|
||||
*/
|
||||
public function getSystemBoards()
|
||||
{
|
||||
return Board::systemOnly()
|
||||
->orderBy('name')
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 시스템 게시판 생성 (mng용)
|
||||
*/
|
||||
public function createSystemBoard(array $data): Board
|
||||
{
|
||||
$data['is_system'] = true;
|
||||
$data['tenant_id'] = null;
|
||||
$data['created_by'] = $this->apiUserId();
|
||||
|
||||
return Board::create($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 테넌트 게시판 생성 (sam용)
|
||||
*/
|
||||
public function createTenantBoard(array $data): Board
|
||||
{
|
||||
$data['is_system'] = false;
|
||||
$data['tenant_id'] = $this->tenantId();
|
||||
$data['created_by'] = $this->apiUserId();
|
||||
|
||||
return Board::create($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 게시판 코드로 조회
|
||||
*/
|
||||
public function getBoardByCode(string $code): ?Board
|
||||
{
|
||||
return Board::accessible($this->tenantId())
|
||||
->where('board_code', $code)
|
||||
->first();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 권한 모델
|
||||
|
||||
### 6.1 extra_settings 기반 권한
|
||||
|
||||
게시판별 권한은 `extra_settings` JSON 필드로 관리:
|
||||
|
||||
```json
|
||||
{
|
||||
"permissions": {
|
||||
"read": ["*"], // 모든 역할 읽기 가능
|
||||
"write": ["admin", "user"], // admin, user 역할만 작성
|
||||
"manage": ["admin"] // admin만 관리
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 권한 검증
|
||||
|
||||
```php
|
||||
// Board 모델에 추가
|
||||
public function canRead(User $user): bool
|
||||
{
|
||||
$roles = $this->getSetting('permissions.read', ['*']);
|
||||
return in_array('*', $roles) ||
|
||||
$user->hasAnyRole($roles);
|
||||
}
|
||||
|
||||
public function canWrite(User $user): bool
|
||||
{
|
||||
$roles = $this->getSetting('permissions.write', ['*']);
|
||||
return in_array('*', $roles) ||
|
||||
$user->hasAnyRole($roles);
|
||||
}
|
||||
|
||||
public function canManage(User $user): bool
|
||||
{
|
||||
$roles = $this->getSetting('permissions.manage', ['admin']);
|
||||
return $user->hasAnyRole($roles);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 구현 계획
|
||||
|
||||
### 7.1 Phase 1: DB 마이그레이션 (api/)
|
||||
|
||||
1. `boards` 테이블 확장
|
||||
- `tenant_id` nullable 변경
|
||||
- `is_system` 컬럼 추가
|
||||
- `board_type` 컬럼 추가
|
||||
- `deleted_at`, `deleted_by` 추가 (없으면)
|
||||
- 인덱스 추가
|
||||
|
||||
### 7.2 Phase 2: 모델/서비스 수정 (api/)
|
||||
|
||||
1. `Board` 모델 업데이트
|
||||
- 스코프 추가 (accessible, systemOnly, tenantOnly)
|
||||
- SoftDeletes 적용
|
||||
- 권한 헬퍼 메서드
|
||||
|
||||
2. `BoardService` 구현
|
||||
- 시스템/테넌트 게시판 분리 로직
|
||||
|
||||
### 7.3 Phase 3: mng 화면 개발
|
||||
|
||||
1. 게시판 목록 (`/mng/boards`)
|
||||
2. 게시판 생성/수정 폼
|
||||
3. 게시판 필드 관리
|
||||
|
||||
### 7.4 Phase 4: sam API 개발
|
||||
|
||||
1. 게시판 API (`/v1/boards`)
|
||||
2. 게시글 API (`/v1/boards/{code}/posts`)
|
||||
3. Swagger 문서
|
||||
|
||||
---
|
||||
|
||||
## 8. 관련 문서
|
||||
|
||||
- [데이터베이스 스키마](./database/README.md)
|
||||
- [API 개발 규칙](../standards/api-rules.md)
|
||||
- [MNG Critical Rules](../../mng/docs/MNG_CRITICAL_RULES.md)
|
||||
|
||||
---
|
||||
|
||||
**최종 업데이트**: 2025-11-27
|
||||
**버전**: 1.0
|
||||
**상태**: 설계 완료
|
||||
193
system/database/README.md
Normal file
193
system/database/README.md
Normal file
@@ -0,0 +1,193 @@
|
||||
# 데이터베이스 스키마 현황
|
||||
|
||||
> **최종 갱신**: 2026-02-27
|
||||
> **DB**: MySQL 8.0 (samdb + sam_stat)
|
||||
> **마이그레이션**: 459개 (api/에서만 관리)
|
||||
|
||||
---
|
||||
|
||||
## 1. 규모 요약
|
||||
|
||||
| 항목 | 수량 |
|
||||
|------|------|
|
||||
| Eloquent 모델 | 220 |
|
||||
| 도메인 그룹 | 32 |
|
||||
| 마이그레이션 | 459 (메인 437 + 통계 22) |
|
||||
| DB 연결 | 2 (samdb, sam_stat) |
|
||||
|
||||
---
|
||||
|
||||
## 2. 도메인별 모델 분포
|
||||
|
||||
| 도메인 | 모델 수 | 주요 엔티티 | 상세 |
|
||||
|--------|---------|------------|------|
|
||||
| Tenants | 56 | Tenant, Department, Approval, Payment, Stock 등 | [tenants.md](tenants.md) |
|
||||
| Stats | 21 | StatFinanceDaily, DimClient, DimDate 등 | [stats.md](stats.md) |
|
||||
| Documents | 15 | Document, DocumentTemplate, DocumentApproval 등 | [documents.md](documents.md) |
|
||||
| Commons | 10 | Category, File, Menu, Tag, Classification | [commons.md](commons.md) |
|
||||
| Quote | 8 | Quote, QuoteItem, QuoteFormula, QuoteRevision | [sales.md](sales.md) |
|
||||
| Production | 8 | WorkOrder, WorkOrderItem, WorkResult 등 | [production.md](production.md) |
|
||||
| Orders | 8 | Order, OrderItem, OrderNode, Client 등 | [sales.md](sales.md) |
|
||||
| ItemMaster | 8 | ItemField, ItemPage, ItemBomItem, CustomTab 등 | [products.md](products.md) |
|
||||
| Products | 6 | Product, ProductComponent, Part, Price | [products.md](products.md) |
|
||||
| Interview | 5 | InterviewTemplate, InterviewSession 등 | [hr.md](hr.md) |
|
||||
| Construction | 5 | Contract, HandoverReport, StructureReview | [production.md](production.md) |
|
||||
| Boards | 5 | Board, Post, BoardSetting, PostCustomFieldValue | [commons.md](commons.md) |
|
||||
| Members | 4 | User, UserTenant, UserRole, UserMenuPermission | [tenants.md](tenants.md) |
|
||||
| Materials | 4 | Material, MaterialReceipt, MaterialInspection | [production.md](production.md) |
|
||||
| ESign | 4 | EsignContract, EsignSigner, EsignSignField | [documents.md](documents.md) |
|
||||
| Design | 4 | DesignModel, ModelVersion, BomTemplate | [products.md](products.md) |
|
||||
| Qualitys | 3 | Inspection, Lot, LotSale | [production.md](production.md) |
|
||||
| Permissions | 3 | Permission, Role, PermissionOverride | [tenants.md](tenants.md) |
|
||||
| Items | 3 | Item, ItemDetail, ItemReceipt | [products.md](products.md) |
|
||||
| BadDebts | 3 | BadDebt, BadDebtDocument, BadDebtMemo | [finance.md](finance.md) |
|
||||
| Estimate | 2 | Estimate, EstimateItem | [sales.md](sales.md) |
|
||||
| Audit | 2 | AuditLog, TriggerAuditLog | [commons.md](commons.md) |
|
||||
| Root Level | 27 | ApiKey, Process, NumberingSequence 등 | 각 도메인 문서 참조 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 핵심 엔티티 관계도
|
||||
|
||||
### 인증 + 멀티테넌시
|
||||
|
||||
```
|
||||
User
|
||||
├─ hasMany UserTenant (pivot: is_active, is_default)
|
||||
│ └─ belongsTo Tenant
|
||||
├─ hasMany UserRole
|
||||
└─ hasMany UserMenuPermission
|
||||
|
||||
Tenant
|
||||
├─ hasMany Department (계층 구조, parent_id)
|
||||
│ └─ belongsToMany User (department_user pivot)
|
||||
├─ hasMany TenantUserProfile (테넌트별 직원 프로필)
|
||||
└─ hasMany [모든 tenant-scoped 모델]
|
||||
```
|
||||
|
||||
### 제품 + 설계
|
||||
|
||||
```
|
||||
Product
|
||||
├─ belongsTo Category (계층 분류)
|
||||
├─ hasMany ProductComponent (BOM)
|
||||
├─ hasMany Part, Price
|
||||
└─ morphMany File
|
||||
|
||||
DesignModel
|
||||
└─ hasMany ModelVersion
|
||||
└─ hasMany BomTemplate
|
||||
└─ hasMany BomTemplateItem
|
||||
```
|
||||
|
||||
### 견적 → 수주 → 생산 흐름
|
||||
|
||||
```
|
||||
Quote (견적)
|
||||
├─ belongsTo Client
|
||||
├─ hasMany QuoteItem
|
||||
└─ → Order 변환
|
||||
|
||||
Order (수주)
|
||||
├─ belongsTo Quote, Client
|
||||
├─ hasMany OrderItem (options JSON)
|
||||
├─ hasMany OrderNode (설계 분해)
|
||||
└─ hasMany WorkOrder
|
||||
|
||||
WorkOrder (작업지시)
|
||||
├─ belongsTo Order, Process
|
||||
├─ hasMany WorkOrderItem (options JSON)
|
||||
├─ hasMany WorkOrderStepProgress
|
||||
└─ hasMany WorkResult (작업실적)
|
||||
```
|
||||
|
||||
### 문서 + 전자결재
|
||||
|
||||
```
|
||||
Document
|
||||
├─ belongsTo DocumentTemplate
|
||||
├─ morphTo linkable (다형 - Order, Quote 등)
|
||||
├─ hasMany DocumentApproval (결재 단계)
|
||||
└─ hasMany DocumentData (동적 폼)
|
||||
|
||||
DocumentTemplate
|
||||
├─ hasMany Section → Field → Item
|
||||
└─ hasMany ApprovalLine (결재선)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 공통 패턴
|
||||
|
||||
### 트레이트 사용 현황
|
||||
|
||||
| 트레이트 | 적용 모델 수 | 역할 |
|
||||
|---------|-------------|------|
|
||||
| BelongsToTenant | ~160 (73%) | tenant_id 자동 스코핑 |
|
||||
| Auditable | ~150 (68%) | created_by, updated_by, deleted_by |
|
||||
| SoftDeletes | ~150 (68%) | 논리 삭제 (deleted_at) |
|
||||
| ModelTrait | ~80 (36%) | 날짜 처리, is_active 스코프 |
|
||||
| HasRoles (Spatie) | 3 | RBAC 권한 관리 |
|
||||
| HasApiTokens (Sanctum) | 1 (User) | API 토큰 관리 |
|
||||
|
||||
### 표준 컬럼 구조
|
||||
|
||||
모든 비즈니스 테이블의 공통 컬럼:
|
||||
```
|
||||
id PK
|
||||
tenant_id FK (멀티테넌시)
|
||||
created_by FK → users (감사)
|
||||
updated_by FK → users (감사)
|
||||
deleted_by FK → users (감사)
|
||||
created_at timestamp
|
||||
updated_at timestamp
|
||||
deleted_at timestamp (논리삭제)
|
||||
is_active boolean
|
||||
sort_order integer
|
||||
options JSON (유연한 속성 저장)
|
||||
```
|
||||
|
||||
### DB 설계 패턴
|
||||
|
||||
| 패턴 | 사용 예시 |
|
||||
|------|----------|
|
||||
| 계층 구조 (Self-Reference) | Category, Department (parent_id) |
|
||||
| 다형 관계 (Polymorphic) | File, PermissionOverride (morphable_type/id) |
|
||||
| EAV (Entity-Attribute-Value) | PostCustomFieldValue, CategoryField |
|
||||
| JSON 컬럼 | Product.bom, OrderItem.options, WorkOrderItem.options |
|
||||
| Pivot 테이블 | user_tenants, department_user |
|
||||
| BOM/Tree | ProductComponent, BomTemplateItem |
|
||||
| 버전 관리 | ModelVersion, QuoteRevision, OrderVersion |
|
||||
| 상태 코드 | Order.status (DRAFT/CONFIRMED/IN_PRODUCTION) |
|
||||
|
||||
### JSON options 컬럼 정책
|
||||
|
||||
> **FK/조인키만** 테이블 컬럼으로 추가.
|
||||
> **나머지 속성은** `options` 또는 `attributes` JSON에 저장.
|
||||
> 이유: 멀티테넌시 환경에서 테넌트별 스키마 변경 없이 유연한 관리.
|
||||
|
||||
---
|
||||
|
||||
## 5. DB 연결
|
||||
|
||||
| 연결명 | DB | 용도 |
|
||||
|--------|-----|------|
|
||||
| mysql (기본) | samdb | 모든 비즈니스 데이터 |
|
||||
| sam_stat | sam_stat | 통계/집계 전용 (21 모델) |
|
||||
| chandj | chandj | 레거시 5130 (마이그레이션 대상) |
|
||||
|
||||
---
|
||||
|
||||
## 6. 도메인별 상세 문서
|
||||
|
||||
| 문서 | 포함 도메인 |
|
||||
|------|-----------|
|
||||
| [tenants.md](tenants.md) | Tenants, Members, Permissions |
|
||||
| [products.md](products.md) | Products, ItemMaster, Items, Design |
|
||||
| [sales.md](sales.md) | Orders, Quote, Estimate |
|
||||
| [production.md](production.md) | Production, Construction, Materials, Qualitys |
|
||||
| [finance.md](finance.md) | Finance (Tenants 하위), BadDebts |
|
||||
| [hr.md](hr.md) | HR (Tenants 하위), Interview |
|
||||
| [documents.md](documents.md) | Documents, ESign |
|
||||
| [commons.md](commons.md) | Commons, Boards, Audit |
|
||||
| [stats.md](stats.md) | Stats (sam_stat DB) |
|
||||
85
system/database/commons.md
Normal file
85
system/database/commons.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# 공통 / 게시판 / 감사 도메인
|
||||
|
||||
> **모델 수**: Commons 10 + Boards 5 + Audit 2 = 17
|
||||
> **핵심**: 범용 분류, 파일 관리, 게시판 시스템, 감사 로그
|
||||
|
||||
---
|
||||
|
||||
## 주요 테이블
|
||||
|
||||
### 공통 (Commons)
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| categories | Category | 범용 계층 분류 (parent_id, self-reference) |
|
||||
| category_fields | CategoryField | 카테고리 동적 필드 (EAV) |
|
||||
| category_templates | CategoryTemplate | 카테고리 필드 스냅샷 (버전) |
|
||||
| category_logs | CategoryLog | 카테고리 변경 로그 |
|
||||
| classifications | Classification | 분류 체계 |
|
||||
| files | File | 파일 첨부 (다형: morphMany) |
|
||||
| menus | Menu | 메뉴 체계 |
|
||||
| tags | Tag | 태그 시스템 |
|
||||
| holidays | Holiday | 공휴일 |
|
||||
| common_codes | CommonCode | 공통 코드 |
|
||||
|
||||
### 게시판 (Boards)
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| boards | Board | 게시판 정의 |
|
||||
| posts | Post | 게시글 |
|
||||
| board_settings | BoardSetting | 게시판 동적 필드 정의 |
|
||||
| post_custom_field_values | PostCustomFieldValue | 게시글 커스텀 필드 값 (EAV) |
|
||||
| board_comments | BoardComment | 댓글 |
|
||||
|
||||
### 감사 (Audit)
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| audit_logs | AuditLog | 변경 감사 로그 (불변) |
|
||||
| trigger_audit_logs | TriggerAuditLog | DB 트리거 감사 로그 |
|
||||
|
||||
---
|
||||
|
||||
## 관계 구조
|
||||
|
||||
```
|
||||
Category (계층 분류)
|
||||
├─ belongsTo Category (parent_id → self)
|
||||
├─ hasMany Category (children)
|
||||
├─ hasMany CategoryField (EAV 동적 필드)
|
||||
├─ hasMany CategoryTemplate (필드 버전 스냅샷)
|
||||
├─ hasMany Product
|
||||
└─ belongsToMany File via tags
|
||||
|
||||
Board (게시판)
|
||||
├─ hasMany Post
|
||||
│ ├─ hasMany PostCustomFieldValue (EAV)
|
||||
│ └─ hasMany BoardComment
|
||||
└─ hasMany BoardSetting (동적 필드 정의)
|
||||
|
||||
File (범용 첨부)
|
||||
└─ morphTo fileable (어떤 모델에든 첨부 가능)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## EAV 패턴
|
||||
|
||||
게시판과 카테고리 모두 **EAV(Entity-Attribute-Value)** 패턴 사용:
|
||||
|
||||
```
|
||||
Board → BoardSetting (필드 정의: field_name, field_type)
|
||||
Post → PostCustomFieldValue (필드 값: board_setting_id, value)
|
||||
|
||||
Category → CategoryField (필드 정의: field_name, field_type)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 특이사항
|
||||
|
||||
- `File`은 다형(morphMany) 관계 — 모든 모델에서 파일 첨부 가능
|
||||
- `AuditLog`는 불변 (timestamps 없음, SoftDeletes 없음)
|
||||
- 감사 로그 보존 정책: 13개월
|
||||
- 게시판 시스템: EAV 패턴으로 동적 필드 지원 (노션 스타일 전환 계획 중)
|
||||
68
system/database/documents.md
Normal file
68
system/database/documents.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# 문서 / 전자서명 도메인
|
||||
|
||||
> **모델 수**: Documents 15 + ESign 4 = 19
|
||||
> **핵심**: 문서 템플릿 기반 생성, 전자결재, 전자서명
|
||||
|
||||
---
|
||||
|
||||
## 주요 테이블
|
||||
|
||||
### 문서 (Documents)
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| documents | Document | 문서 마스터 (다형 linkable) |
|
||||
| document_templates | DocumentTemplate | 문서 템플릿 정의 |
|
||||
| document_template_sections | DocumentTemplateSection | 템플릿 섹션 |
|
||||
| document_template_section_fields | DocumentTemplateSectionField | 섹션 필드 |
|
||||
| document_template_section_items | DocumentTemplateSectionItem | 체크리스트 항목 |
|
||||
| document_template_field_presets | DocumentTemplateFieldPreset | 필드 프리셋 |
|
||||
| document_template_approval_lines | DocumentTemplateApprovalLine | 결재선 정의 |
|
||||
| document_approvals | DocumentApproval | 문서 결재 기록 |
|
||||
| document_attachments | DocumentAttachment | 문서 첨부 |
|
||||
| document_data | DocumentData | 문서 동적 데이터 |
|
||||
| document_links | DocumentLink | 문서 링크 |
|
||||
|
||||
### 전자서명 (ESign)
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| esign_contracts | EsignContract | 전자서명 계약 |
|
||||
| esign_signers | EsignSigner | 서명자 |
|
||||
| esign_sign_fields | EsignSignField | 서명 필드 |
|
||||
| esign_audit_logs | EsignAuditLog | 전자서명 감사 로그 |
|
||||
|
||||
---
|
||||
|
||||
## 관계 구조
|
||||
|
||||
```
|
||||
DocumentTemplate
|
||||
├─ hasMany DocumentTemplateSection
|
||||
│ └─ hasMany DocumentTemplateSectionField
|
||||
│ ├─ hasMany DocumentTemplateSectionItem (체크리스트)
|
||||
│ └─ hasMany DocumentTemplateFieldPreset (프리셋)
|
||||
└─ hasMany DocumentTemplateApprovalLine (결재선)
|
||||
|
||||
Document
|
||||
├─ belongsTo DocumentTemplate
|
||||
├─ morphTo linkable (Order, Quote 등에 연결)
|
||||
├─ hasMany DocumentApproval
|
||||
├─ hasMany DocumentAttachment
|
||||
├─ hasMany DocumentData
|
||||
└─ hasMany DocumentLink
|
||||
|
||||
EsignContract
|
||||
├─ hasMany EsignSigner
|
||||
├─ hasMany EsignSignField
|
||||
└─ hasMany EsignAuditLog
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 특이사항
|
||||
|
||||
- 문서 시스템은 템플릿 기반 (Section → Field → Item 3단계)
|
||||
- `Document.linkable`은 다형 관계 (Order, Quote 등에 첨부 가능)
|
||||
- 전자서명은 별도 감사 로그 보유 (EsignAuditLog)
|
||||
- 문서 15개 모델 중 7개가 템플릿 구조 관련
|
||||
57
system/database/finance.md
Normal file
57
system/database/finance.md
Normal file
@@ -0,0 +1,57 @@
|
||||
ㅇ# 재무 / 회계 도메인
|
||||
|
||||
> **모델 수**: Finance 관련 (Tenants 하위) + BadDebts 3
|
||||
> **핵심**: 매입/매출, 결제, 세금계산서, 대출, 경비
|
||||
> **API 엔드포인트**: 180개 (finance.php — 최대 규모)
|
||||
|
||||
---
|
||||
|
||||
## 주요 테이블
|
||||
|
||||
### 결제 / 청구
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| payments | Payment | 결제 마스터 |
|
||||
| bills | Bill | 청구서 |
|
||||
| deposits | Deposit | 입금 |
|
||||
| withdrawals | Withdrawal | 출금 |
|
||||
| receivables | Receivables | 매출채권 |
|
||||
| vendor_ledgers | VendorLedger | 거래처 원장 |
|
||||
|
||||
### 세금 / 카드
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| tax_invoices | TaxInvoice | 세금계산서 |
|
||||
| vat_* | Vat 관련 | 부가세 관리 |
|
||||
| cards | Card | 법인카드 |
|
||||
| card_transactions | CardTransaction | 카드 거래 |
|
||||
| bank_accounts | BankAccount | 은행 계좌 |
|
||||
| bank_transactions | BankTransaction | 은행 거래 |
|
||||
|
||||
### 대손 (BadDebts)
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| bad_debts | BadDebt | 대손 마스터 |
|
||||
| bad_debt_documents | BadDebtDocument | 대손 관련 문서 |
|
||||
| bad_debt_memos | BadDebtMemo | 대손 메모 |
|
||||
|
||||
### 기타 재무
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| expected_expenses | ExpectedExpense | 예상 경비 |
|
||||
| entertainments | Entertainment | 접대비 |
|
||||
| purchases | Purchase | 매입 |
|
||||
| subscriptions | Subscription | 구독/정기결제 |
|
||||
|
||||
---
|
||||
|
||||
## 특이사항
|
||||
|
||||
- 재무 모델은 대부분 `Tenants/` 디렉토리 하위에 위치
|
||||
- finance.php 라우트가 180개로 전체 API 중 최대 규모
|
||||
- 바로빌 연동은 mng에서 관리 (api에는 해당 모델 없음)
|
||||
- 모든 재무 모델은 BelongsToTenant + Auditable + SoftDeletes
|
||||
68
system/database/hr.md
Normal file
68
system/database/hr.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# 인사 / HR 도메인
|
||||
|
||||
> **모델 수**: HR 관련 (Tenants 하위) + Interview 5
|
||||
> **핵심**: 급여, 근태, 휴가, 대출, 면접
|
||||
> **API 엔드포인트**: 141개 (hr.php)
|
||||
|
||||
---
|
||||
|
||||
## 주요 테이블
|
||||
|
||||
### 급여 / 근무
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| payrolls | Payroll | 급여 마스터 |
|
||||
| salaries | Salary | 급여 항목 |
|
||||
| attendances | Attendance | 근태 기록 |
|
||||
| attendance_requests | AttendanceRequest | 근태 요청 |
|
||||
| leaves | Leave | 휴가 사용 기록 |
|
||||
| leave_policies | LeavePolicy | 휴가 정책 |
|
||||
| labors | Labor | 노무비 |
|
||||
|
||||
### 대출
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| loans | Loan | 직원 대출 |
|
||||
|
||||
### 면접 (Interview)
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| interview_templates | InterviewTemplate | 면접 양식 |
|
||||
| interview_sessions | InterviewSession | 면접 세션 |
|
||||
| interview_questions | InterviewQuestion | 면접 질문 |
|
||||
| interview_categories | InterviewCategory | 면접 카테고리 |
|
||||
| interview_responses | InterviewResponse | 면접 답변 |
|
||||
|
||||
---
|
||||
|
||||
## 관계 구조
|
||||
|
||||
```
|
||||
TenantUserProfile (직원)
|
||||
├─ hasMany Payroll
|
||||
├─ hasMany Attendance
|
||||
├─ hasMany Leave
|
||||
├─ hasMany Loan
|
||||
└─ hasMany AttendanceRequest
|
||||
|
||||
LeavePolicy
|
||||
└─ belongsTo Tenant (테넌트별 휴가 정책)
|
||||
|
||||
InterviewTemplate
|
||||
├─ hasMany InterviewCategory
|
||||
│ └─ hasMany InterviewQuestion
|
||||
└─ hasMany InterviewSession
|
||||
└─ hasMany InterviewResponse
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 특이사항
|
||||
|
||||
- HR 모델은 대부분 `Tenants/` 디렉토리 하위
|
||||
- 직원 정보는 `TenantUserProfile` (테넌트별 프로필)
|
||||
- 면접 모델은 별도 `Interview/` 도메인으로 분리
|
||||
- 급여 관련 최근 추가: long_term_care 컬럼 (2026-02-27)
|
||||
112
system/database/production.md
Normal file
112
system/database/production.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# 생산 / 시공 / 자재 / 품질 도메인
|
||||
|
||||
> **모델 수**: Production 8 + Construction 5 + Materials 4 + Qualitys 3 = 20
|
||||
> **핵심**: 작업지시, 공정 관리, 자재/LOT 추적, 품질 검사
|
||||
|
||||
---
|
||||
|
||||
## 주요 테이블
|
||||
|
||||
### 생산 (Production)
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| work_orders | WorkOrder | 작업지시 마스터 |
|
||||
| work_order_items | WorkOrderItem | 작업지시 항목 (options JSON) |
|
||||
| work_order_step_progress | WorkOrderStepProgress | 단계별 진행 추적 |
|
||||
| work_order_bending_details | WorkOrderBendingDetail | 절곡 상세 사양 |
|
||||
| work_order_assignees | WorkOrderAssignee | 작업자 배정 |
|
||||
| work_order_issues | WorkOrderIssue | 작업 이슈 기록 |
|
||||
| work_results | WorkResult | 작업실적 |
|
||||
| work_order_material_inputs | WorkOrderMaterialInput | 자재 투입 기록 |
|
||||
|
||||
### 시공 (Construction)
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| contracts | Contract | 시공 계약 |
|
||||
| handover_reports | HandoverReport | 인수인계 보고서 |
|
||||
| handover_report_managers | HandoverReportManager | 인수인계 담당자 |
|
||||
| structure_reviews | StructureReview | 구조 검토 |
|
||||
|
||||
### 자재 (Materials)
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| materials | Material | 자재 마스터 |
|
||||
| material_receipts | MaterialReceipt | 자재 입고 |
|
||||
| material_inspections | MaterialInspection | 수입검사 마스터 |
|
||||
| material_inspection_items | MaterialInspectionItem | 수입검사 항목 |
|
||||
|
||||
### 품질 (Qualitys)
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| inspections | Inspection | 검사 마스터 |
|
||||
| lots | Lot | LOT 관리 |
|
||||
| lot_sales | LotSale | LOT 출고 |
|
||||
|
||||
---
|
||||
|
||||
## 관계 구조
|
||||
|
||||
```
|
||||
WorkOrder (작업지시)
|
||||
├─ belongsTo Order
|
||||
├─ belongsTo Process (공정)
|
||||
├─ belongsTo Department (담당 팀)
|
||||
├─ hasMany WorkOrderItem
|
||||
│ ├─ options: JSON { floor, code, width, height, slat_info, bending_info }
|
||||
│ └─ hasMany WorkOrderMaterialInput
|
||||
├─ hasMany WorkOrderStepProgress
|
||||
├─ hasMany WorkOrderBendingDetail
|
||||
├─ hasMany WorkOrderAssignee
|
||||
├─ hasMany WorkOrderIssue
|
||||
└─ hasMany WorkResult
|
||||
|
||||
Material
|
||||
├─ belongsTo Category
|
||||
├─ hasMany MaterialReceipt
|
||||
├─ hasMany Lot
|
||||
└─ morphMany File
|
||||
|
||||
Inspection
|
||||
├─ belongsTo WorkOrder (또는 MaterialReceipt)
|
||||
└─ hasMany InspectionItem
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## WorkOrderItem options JSON 구조
|
||||
|
||||
```json
|
||||
{
|
||||
"floor": "1F",
|
||||
"code": "SL-001",
|
||||
"width": 1200,
|
||||
"height": 800,
|
||||
"cutting_info": { ... },
|
||||
"slat_info": { "joint_bar": 2, "glass_qty": 10 },
|
||||
"bending_info": { ... },
|
||||
"wip_info": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
- OrderItem.options에서 복사됨 (width 직접 접근 가능)
|
||||
- 조인트바 자동계산: `createWorkOrders()` 에서 처리
|
||||
|
||||
---
|
||||
|
||||
## LOT 관리 흐름
|
||||
|
||||
```
|
||||
Material → MaterialReceipt (입고)
|
||||
│
|
||||
MaterialInspection (수입검사)
|
||||
│
|
||||
Lot (LOT 생성)
|
||||
│
|
||||
WorkOrderMaterialInput (투입)
|
||||
│
|
||||
LotSale (출고)
|
||||
```
|
||||
88
system/database/products.md
Normal file
88
system/database/products.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# 제품 / 품목 / 설계 도메인
|
||||
|
||||
> **모델 수**: Products 6 + ItemMaster 8 + Items 3 + Design 4 = 21
|
||||
> **핵심**: 제품 정의, BOM 구조, 품목 마스터, 설계 모델
|
||||
|
||||
---
|
||||
|
||||
## 주요 테이블
|
||||
|
||||
### 제품 (Products)
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| products | Product | 제품 마스터 (code, name, product_type) |
|
||||
| product_components | ProductComponent | BOM 구성 (parent-child 관계) |
|
||||
| parts | Part | 부품 정의 |
|
||||
| prices | Price | 가격 정보 |
|
||||
| common_codes | CommonCode | 공통 코드 |
|
||||
|
||||
### 품목 마스터 (ItemMaster)
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| item_fields | ItemField | 품목 필드 정의 |
|
||||
| item_pages | ItemPage | 품목 페이지 구성 |
|
||||
| item_bom_items | ItemBomItem | 품목 BOM 항목 |
|
||||
| custom_tabs | CustomTab | 커스텀 탭 |
|
||||
| unit_options | UnitOption | 단위 옵션 |
|
||||
|
||||
### 품목 (Items)
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| items | Item | 품목 마스터 |
|
||||
| item_details | ItemDetail | 품목 상세 |
|
||||
| item_receipts | ItemReceipt | 품목 입고 |
|
||||
|
||||
### 설계 (Design)
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| design_models | DesignModel | 설계 모델 마스터 |
|
||||
| model_versions | ModelVersion | 모델 버전 |
|
||||
| bom_templates | BomTemplate | BOM 템플릿 |
|
||||
| bom_template_items | BomTemplateItem | BOM 템플릿 항목 (수량, 로스율) |
|
||||
|
||||
---
|
||||
|
||||
## 관계 구조
|
||||
|
||||
```
|
||||
Product
|
||||
├─ belongsTo Category (계층 분류)
|
||||
├─ hasMany ProductComponent (BOM)
|
||||
│ └─ child_product_id → Product (자기 참조)
|
||||
├─ hasMany Part
|
||||
├─ hasMany Price
|
||||
└─ morphMany File
|
||||
|
||||
Item
|
||||
├─ hasMany ItemDetail
|
||||
├─ hasMany ItemReceipt
|
||||
└─ options JSON: { lot_managed, consumption_method, production_source, input_tracking }
|
||||
|
||||
DesignModel → ModelVersion → BomTemplate → BomTemplateItem
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 품목 options 체계
|
||||
|
||||
items.options JSON으로 품목별 관리 방식 정의:
|
||||
|
||||
| 속성 | 타입 | 설명 |
|
||||
|------|------|------|
|
||||
| lot_managed | bool | LOT 추적 여부 |
|
||||
| consumption_method | auto/manual/none | 소진 방식 |
|
||||
| production_source | purchased/self_produced/both | 조달 구분 |
|
||||
| input_tracking | bool | 원자재 투입 추적 여부 |
|
||||
|
||||
### 유형별 조합
|
||||
|
||||
| 유형 | 예시 | lot | consumption | source |
|
||||
|------|------|-----|------------|--------|
|
||||
| 구매 소모품 (LOT) | 내화실 | true | manual | purchased |
|
||||
| 구매 소모품 (비LOT) | 장갑, 테이프 | false | manual | purchased |
|
||||
| 일반 자체생산 | 슬랫, 절곡물 | true | auto | self_produced |
|
||||
| 잔재 활용 생산 | 조인트바 | true | auto | self_produced |
|
||||
98
system/database/sales.md
Normal file
98
system/database/sales.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# 영업 / 수주 / 견적 도메인
|
||||
|
||||
> **모델 수**: Orders 8 + Quote 8 + Estimate 2 = 18
|
||||
> **핵심**: 견적 → 수주 → 생산 변환 흐름
|
||||
|
||||
---
|
||||
|
||||
## 주요 테이블
|
||||
|
||||
### 수주 (Orders)
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| orders | Order | 수주 마스터 (status: DRAFT→CONFIRMED→IN_PRODUCTION) |
|
||||
| order_items | OrderItem | 수주 항목 (options JSON 포함) |
|
||||
| order_nodes | OrderNode | 설계 분해 구조 |
|
||||
| order_item_components | OrderItemComponent | 수주 항목 구성요소 |
|
||||
| order_histories | OrderHistory | 수주 변경 이력 |
|
||||
| clients | Client | 거래처 마스터 |
|
||||
| client_groups | ClientGroup | 거래처 그룹 |
|
||||
| site_briefings | SiteBriefing | 현장 브리핑 |
|
||||
|
||||
### 견적 (Quote)
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| quotes | Quote | 견적 마스터 |
|
||||
| quote_items | QuoteItem | 견적 항목 |
|
||||
| quote_formulas | QuoteFormula | 견적 공식 |
|
||||
| quote_formula_categories | QuoteFormulaCategory | 공식 카테고리 |
|
||||
| quote_revisions | QuoteRevision | 견적 버전 이력 |
|
||||
|
||||
### 견적서 (Estimate)
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| estimates | Estimate | 견적서 마스터 |
|
||||
| estimate_items | EstimateItem | 견적서 항목 |
|
||||
|
||||
---
|
||||
|
||||
## 관계 구조
|
||||
|
||||
```
|
||||
Quote (견적)
|
||||
├─ belongsTo Client
|
||||
├─ belongsTo SiteBriefing
|
||||
├─ belongsTo Item
|
||||
├─ hasMany QuoteItem
|
||||
├─ hasMany QuoteRevision
|
||||
└─ → Order 변환 (OrderService)
|
||||
|
||||
Order (수주)
|
||||
├─ belongsTo Quote
|
||||
├─ belongsTo Client
|
||||
├─ hasMany OrderItem
|
||||
│ ├─ belongsTo Item
|
||||
│ ├─ hasMany OrderItemComponent
|
||||
│ └─ options: JSON { floor, code, width, height, cutting_info, slat_info, bending_info }
|
||||
├─ hasMany OrderNode
|
||||
├─ hasMany OrderHistory
|
||||
└─ hasMany WorkOrder (생산으로 변환)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 비즈니스 흐름
|
||||
|
||||
```
|
||||
견적(Quote) → 수주(Order) → 작업지시(WorkOrder) → 작업실적(WorkResult)
|
||||
│ │ │
|
||||
QuoteItem OrderItem WorkOrderItem
|
||||
```
|
||||
|
||||
### 상태 흐름 (Order)
|
||||
```
|
||||
DRAFT → CONFIRMED → IN_PRODUCTION → COMPLETED → SHIPPED
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## OrderItem options JSON 구조
|
||||
|
||||
```json
|
||||
{
|
||||
"floor": "1F",
|
||||
"code": "SL-001",
|
||||
"width": 1200,
|
||||
"height": 800,
|
||||
"cutting_info": { ... },
|
||||
"slat_info": { "joint_bar": 2, "glass_qty": 10 },
|
||||
"bending_info": { ... },
|
||||
"wip_info": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
- 견적 → 수주 변환 시 `extractSlatInfoFromBom()`으로 slat_info 자동 계산
|
||||
- 조인트바 수량 공식: `(2 + floor((제작가로 - 500) / 1000)) × 셔터수량`
|
||||
68
system/database/stats.md
Normal file
68
system/database/stats.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# 통계 도메인
|
||||
|
||||
> **모델 수**: 21
|
||||
> **DB 연결**: sam_stat (별도 데이터베이스)
|
||||
> **핵심**: 일별/월별 집계, 차원 테이블, 통계 서비스 15개
|
||||
|
||||
---
|
||||
|
||||
## 주요 테이블
|
||||
|
||||
### 일별 집계 (Daily)
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| stat_finance_daily | StatFinanceDaily | 일별 재무 통계 |
|
||||
| stat_production_daily | StatProductionDaily | 일별 생산 통계 |
|
||||
| stat_sales_daily | StatSalesDaily | 일별 영업 통계 |
|
||||
| stat_hr_daily | StatHrDaily | 일별 인사 통계 |
|
||||
| stat_inventory_daily | StatInventoryDaily | 일별 재고 통계 |
|
||||
|
||||
### 월별 집계 (Monthly)
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| stat_finance_monthly | StatFinanceMonthly | 월별 재무 통계 |
|
||||
| stat_production_monthly | StatProductionMonthly | 월별 생산 통계 |
|
||||
| stat_sales_monthly | StatSalesMonthly | 월별 영업 통계 |
|
||||
|
||||
### 차원 테이블 (Dimensions)
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| dim_clients | DimClient | 거래처 차원 |
|
||||
| dim_dates | DimDate | 날짜 차원 |
|
||||
| dim_products | DimProduct | 제품 차원 |
|
||||
| dim_departments | DimDepartment | 부서 차원 |
|
||||
|
||||
### 기타
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| base_stat_model | BaseStatModel | 통계 모델 베이스 |
|
||||
| stat_* | 기타 통계 모델 | 도메인별 집계 |
|
||||
|
||||
---
|
||||
|
||||
## 아키텍처
|
||||
|
||||
```
|
||||
[samdb] ──Observer 이벤트──→ [통계 서비스 15개] ──집계──→ [sam_stat DB]
|
||||
|
||||
StatEventObserver
|
||||
├─ 주문 이벤트 → StatSalesDaily 업데이트
|
||||
├─ 생산 이벤트 → StatProductionDaily 업데이트
|
||||
├─ 재무 이벤트 → StatFinanceDaily 업데이트
|
||||
└─ ... (도메인별)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 특이사항
|
||||
|
||||
- **별도 DB 연결**: `sam_stat` (samdb와 분리)
|
||||
- **마이그레이션**: 22개 (별도 관리)
|
||||
- **서비스**: 15개 전용 서비스 (Stats/ 디렉토리)
|
||||
- **차원 테이블**: 스타 스키마 기반 (DimClient, DimDate 등)
|
||||
- **이벤트 기반**: Observer 패턴으로 실시간 집계
|
||||
- API 엔드포인트: 5개 (stats.php) — 대부분 조회용
|
||||
90
system/database/tenants.md
Normal file
90
system/database/tenants.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# 테넌트 / 사용자 / 권한 도메인
|
||||
|
||||
> **모델 수**: Tenants 56 + Members 4 + Permissions 3 = 63
|
||||
> **핵심**: 멀티테넌시 기반, 모든 비즈니스 데이터의 기준점
|
||||
|
||||
---
|
||||
|
||||
## 주요 테이블
|
||||
|
||||
### 인증 / 사용자
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| users | User | 시스템 계정 (Sanctum, HasRoles) |
|
||||
| user_tenants | UserTenant | 사용자-테넌트 매핑 (is_active, is_default) |
|
||||
| user_roles | UserRole | 사용자-역할 매핑 |
|
||||
| user_menu_permissions | UserMenuPermission | 사용자별 메뉴 권한 |
|
||||
|
||||
### 테넌트 핵심
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| tenants | Tenant | 조직/회사 마스터 |
|
||||
| departments | Department | 부서 (계층 구조, parent_id) |
|
||||
| department_user | (pivot) | 부서-사용자 다대다 |
|
||||
| tenant_user_profiles | TenantUserProfile | 테넌트별 직원 프로필 |
|
||||
| positions | Position | 직급/직위 |
|
||||
|
||||
### 테넌트 설정
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| tenant_settings | TenantSetting | 테넌트별 설정값 |
|
||||
| tenant_field_settings | TenantFieldSetting | 필드별 설정 |
|
||||
| tenant_option_groups | TenantOptionGroup | 옵션 그룹 정의 |
|
||||
| tenant_option_values | TenantOptionValue | 옵션 값 |
|
||||
| tenant_stat_fields | TenantStatField | 통계 필드 설정 |
|
||||
|
||||
### 권한 (Spatie Permission)
|
||||
|
||||
| 테이블 | 모델 | 역할 |
|
||||
|--------|------|------|
|
||||
| permissions | Permission | 권한 정의 |
|
||||
| roles | Role | 역할 정의 |
|
||||
| permission_overrides | PermissionOverride | 권한 오버라이드 (다형) |
|
||||
|
||||
---
|
||||
|
||||
## 관계 구조
|
||||
|
||||
```
|
||||
User (1)
|
||||
├─ (N) UserTenant ─→ (1) Tenant
|
||||
├─ (N) UserRole ─→ (1) Role ─→ (N) Permission
|
||||
└─ (N) UserMenuPermission
|
||||
|
||||
Tenant (1)
|
||||
├─ (N) Department (parent_id → self)
|
||||
│ └─ (N:M) User via department_user
|
||||
├─ (N) TenantUserProfile
|
||||
│ ├─ → User
|
||||
│ └─ → Department
|
||||
├─ (N) TenantSetting
|
||||
└─ (N) [모든 BelongsToTenant 모델]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tenants 하위 비즈니스 모델 (56개)
|
||||
|
||||
Tenants 도메인은 조직 내 다양한 비즈니스 기능을 포함:
|
||||
|
||||
- **재무**: Payment, Bill, TaxInvoice, JournalEntry, Deposit, Withdrawal 등
|
||||
- **인사**: Payroll, Attendance, Leave, LeavePolicy, Salary, Loan 등
|
||||
- **영업**: Sale, Client, ClientGroup, Shipment, Receivables 등
|
||||
- **재고**: Stock, StockLot, MaterialInput 등
|
||||
- **결재**: Approval, ApprovalForm, ApprovalLine 등
|
||||
- **기타**: Calendar, CalendarSchedule, Notification 등
|
||||
|
||||
> 이 모델들의 상세 내용은 각 도메인 문서(finance.md, hr.md, sales.md 등)에서 다룸.
|
||||
|
||||
---
|
||||
|
||||
## 특이사항
|
||||
|
||||
- `User`는 BelongsToTenant가 아님 (시스템 전역)
|
||||
- `UserTenant`으로 다중 테넌트 소속 지원
|
||||
- `TenantUserProfile`은 테넌트별 직원 정보 (직급, 입사일 등)
|
||||
- `Department`는 self-reference 계층 구조 (parent_id)
|
||||
- 권한은 메뉴 기반 RBAC (Spatie Permission + UserMenuPermission)
|
||||
232
system/docker-setup.md
Normal file
232
system/docker-setup.md
Normal file
@@ -0,0 +1,232 @@
|
||||
# Docker 환경 설정
|
||||
|
||||
> **최종 갱신**: 2026-02-27
|
||||
> **Docker Compose**: 7 서비스, `samnet` 브리지 네트워크
|
||||
|
||||
---
|
||||
|
||||
## 1. 서비스 구성
|
||||
|
||||
| 서비스 | 이미지 | 포트 | 역할 |
|
||||
|--------|--------|------|------|
|
||||
| nginx | nginx:latest | 80, 443 | 리버스 프록시, SSL 종료 |
|
||||
| api | php:8.4-fpm (custom) | 9000 | Laravel REST API |
|
||||
| mng | php:8.4-fpm (custom) | 9000 | Laravel 관리자 패널 |
|
||||
| react | node:20-alpine | 3000 | Next.js 프론트엔드 |
|
||||
| design | node:20-alpine | 3002 | Vite 디자인 시스템 |
|
||||
| php73 | php:7.3-fpm (custom) | 9000 | 레거시 5130 |
|
||||
| mysql | mysql:8.0 | 3306 | 데이터베이스 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 도메인 매핑
|
||||
|
||||
| 도메인 | 대상 | SSL | 비고 |
|
||||
|--------|------|-----|------|
|
||||
| api.sam.kr | api:9000 (FastCGI) | O | 대형 JSON 버퍼링 |
|
||||
| mng.sam.kr | mng:9000 (FastCGI) | O | 관리자 패널 |
|
||||
| admin.sam.kr | mng:9000 (FastCGI) | O | mng 별칭 |
|
||||
| dev.sam.kr | react:3000 (Proxy) | O | HMR WebSocket 지원 |
|
||||
| design.sam.kr | design:3002 (Proxy) | O | HMR WebSocket 지원 |
|
||||
| 5130.sam.kr | php73:9000 (FastCGI) | O | 레거시 |
|
||||
| dev.haisa.kr | host.docker.internal:8888 | X | 외부 프록시 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 네트워크 구조
|
||||
|
||||
```
|
||||
[호스트]
|
||||
127.0.0.1:80 ─→ nginx:80
|
||||
127.0.0.1:443 ─→ nginx:443
|
||||
127.0.0.1:3306 ─→ mysql:3306
|
||||
|
||||
[samnet 내부]
|
||||
nginx ──FastCGI──→ api:9000
|
||||
nginx ──FastCGI──→ mng:9000
|
||||
nginx ──FastCGI──→ php73:9000
|
||||
nginx ──Proxy────→ react:3000
|
||||
nginx ──Proxy────→ design:3002
|
||||
api/mng ─────────→ mysql:3306
|
||||
```
|
||||
|
||||
- 모든 포트는 `127.0.0.1` 바인딩 (외부 접근 차단)
|
||||
- `samnet` 브리지 네트워크로 컨테이너간 통신
|
||||
|
||||
---
|
||||
|
||||
## 4. Dockerfile 요약
|
||||
|
||||
### api / mng (PHP 8.4-FPM)
|
||||
```
|
||||
Base: php:8.4-fpm
|
||||
Extensions: zip, mysqli, pdo, pdo_mysql, intl
|
||||
Packages: git, unzip, nginx, supervisor
|
||||
Composer: 2 (멀티스테이지 빌드)
|
||||
실행: Supervisor (nginx + php-fpm 동시)
|
||||
Entrypoint: storage:link, 권한 설정
|
||||
```
|
||||
|
||||
### react (Node 20)
|
||||
```
|
||||
Base: node:20-alpine
|
||||
추가: chromium, font-noto-cjk (Puppeteer 지원)
|
||||
환경: PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
|
||||
실행: npm run dev (포트 3000)
|
||||
```
|
||||
|
||||
### design (Node 20)
|
||||
```
|
||||
Base: node:20-alpine
|
||||
실행: npm run dev (포트 3002)
|
||||
```
|
||||
|
||||
### php73 (PHP 7.3-FPM)
|
||||
```
|
||||
Base: php:7.3-fpm
|
||||
Extensions: gd, mbstring
|
||||
설정: short_open_tag=On, display_errors=On
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 볼륨 마운팅
|
||||
|
||||
### 주요 볼륨
|
||||
- **db_data** (Named Volume): MySQL 데이터 영속성
|
||||
- **앱 소스코드**: 호스트 → 컨테이너 직접 마운트
|
||||
- **API 스토리지**: mng가 api의 storage/app/tenants 공유 마운트
|
||||
|
||||
### Nginx 설정
|
||||
- `docker/nginx/nginx.conf` → `/etc/nginx/nginx.conf` (RO)
|
||||
- `docker/nginx/ssl/` → `/etc/nginx/ssl/` (RO)
|
||||
|
||||
### PHP 설정
|
||||
- `uploads.ini`: file_uploads=On, memory_limit=256M, upload_max=20M, post_max=100M
|
||||
- `supervisord.conf`: php-fpm + nginx 동시 실행 (nodaemon)
|
||||
|
||||
---
|
||||
|
||||
## 6. MySQL 설정
|
||||
|
||||
### my.cnf
|
||||
```ini
|
||||
innodb_buffer_pool_size = 256M
|
||||
innodb_log_buffer_size = 16M
|
||||
sort_buffer_size = 4M
|
||||
tmp_table_size = 64M
|
||||
character-set-server = utf8mb4
|
||||
collation-server = utf8mb4_unicode_ci
|
||||
log_bin_trust_function_creators = 1
|
||||
```
|
||||
|
||||
### 초기화 (init.sql)
|
||||
```sql
|
||||
CREATE DATABASE samdb; -- API/MNG용
|
||||
CREATE DATABASE chandj; -- 레거시 5130용
|
||||
CREATE USER samuser@'%';
|
||||
GRANT ALL ON samdb.*, chandj.*;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Nginx 보안 필터 (API)
|
||||
|
||||
```
|
||||
차단 패턴:
|
||||
- 경로 트래버설: \.\.\/|\.\.\\|etc\/passwd
|
||||
- 민감 파일: \.env|\.git|\.htaccess|\.sql|@fs\/
|
||||
- 악성 User-Agent: sqlmap, nikto, nmap, masscan, metasploit, nessus
|
||||
응답: 403 Forbidden
|
||||
```
|
||||
|
||||
### 업로드 제한
|
||||
| 대상 | 최대 크기 |
|
||||
|------|----------|
|
||||
| Nginx (기본) | 100M |
|
||||
| API | 100M |
|
||||
| MNG | 210M |
|
||||
|
||||
### 정적 자산 캐싱
|
||||
- 대상: js, css, png, jpg, gif, svg, ico, woff2, ttf
|
||||
- 만료: 30일, Cache-Control: public
|
||||
|
||||
---
|
||||
|
||||
## 8. CI/CD (Jenkins)
|
||||
|
||||
### 파이프라인 요약
|
||||
|
||||
| 저장소 | 트리거 브랜치 | 배포 서버 | 특징 |
|
||||
|--------|-------------|----------|------|
|
||||
| api | main, develop | 211.117.60.189 | Release 디렉토리, 자동 마이그레이션, 자동 롤백 |
|
||||
| react | main, develop | 114.203.209.83 (dev), 211.117.60.189 (stage/prod) | PM2 프로세스 관리 |
|
||||
| mng | main | 211.117.60.189 | npm build + PHP-FPM 재로드 |
|
||||
| sales | main | 211.117.60.189 | 정적 사이트 rsync만 |
|
||||
|
||||
### 배포 전략 (api/mng)
|
||||
```
|
||||
1. releases/{RELEASE_ID} 디렉토리 생성 (타임스탬프)
|
||||
2. rsync 코드 복사 (.git, .env, storage 제외)
|
||||
3. composer install + npm install
|
||||
4. php artisan migrate --force
|
||||
5. config/route/view 캐시 생성
|
||||
6. /current symlink → 최신 릴리스
|
||||
7. PHP-FPM 재로드
|
||||
8. 이전 릴리스 정리 (최근 6개만 유지)
|
||||
```
|
||||
|
||||
### 롤백: 실패 시 이전 릴리스로 symlink 자동 변경
|
||||
### 알림: Slack (빌드 시작/성공/실패)
|
||||
|
||||
---
|
||||
|
||||
## 9. 배포 환경 요약
|
||||
|
||||
| 환경 | 도메인 | IP | 배포 방식 |
|
||||
|------|--------|----|----------|
|
||||
| 로컬 | *.sam.kr | localhost | Docker Compose |
|
||||
| 개발 | codebridge-x.com | 114.203.209.83 | Jenkins (react only) |
|
||||
| 스테이지 | TBD | 211.117.60.189 | Jenkins (api, mng, react) |
|
||||
| 운영 | TBD | 211.117.60.189 | Jenkins (승인 필요, 비활성) |
|
||||
|
||||
---
|
||||
|
||||
## 10. 디렉토리 구조
|
||||
|
||||
```
|
||||
docker/
|
||||
├── docker-compose.yml 전체 서비스 정의
|
||||
├── .env 프로젝트명 설정
|
||||
├── nginx/
|
||||
│ ├── nginx.conf 메인 설정 (7개 virtual host)
|
||||
│ └── ssl/ SSL 인증서
|
||||
├── api/
|
||||
│ ├── Dockerfile
|
||||
│ ├── nginx.conf
|
||||
│ ├── supervisord.conf
|
||||
│ ├── entrypoint.sh
|
||||
│ └── uploads.ini
|
||||
├── mng/
|
||||
│ ├── Dockerfile
|
||||
│ ├── nginx.conf
|
||||
│ ├── supervisord.conf
|
||||
│ ├── entrypoint.sh
|
||||
│ └── uploads.ini
|
||||
├── react/
|
||||
│ ├── Dockerfile
|
||||
│ └── entrypoint.sh
|
||||
├── design/
|
||||
│ ├── Dockerfile
|
||||
│ └── entrypoint.sh
|
||||
├── 5130/
|
||||
│ ├── Dockerfile
|
||||
│ ├── nginx.conf
|
||||
│ ├── supervisord.conf
|
||||
│ ├── entrypoint.sh
|
||||
│ └── uploads.ini
|
||||
└── mysql/
|
||||
├── init.sql
|
||||
├── my.cnf
|
||||
└── database.sql
|
||||
```
|
||||
155
system/erp-analysis/00-overview.md
Normal file
155
system/erp-analysis/00-overview.md
Normal file
@@ -0,0 +1,155 @@
|
||||
# SAM ERP 스토리보드 분석 개요
|
||||
|
||||
> 분석 대상: SAM_ERP_Storyboard_D0.8_251216 (113 슬라이드)
|
||||
> 분석일: 2025-12-17
|
||||
> 버전: D0.8
|
||||
|
||||
## 1. 문서 개요
|
||||
|
||||
### 1.1 버전 히스토리
|
||||
| 날짜 | 버전 | 내용 |
|
||||
|------|------|------|
|
||||
| 2025.12.01 | D0.6 | 프론트 초안 - 인사관리 & 전자결재 작성 |
|
||||
| 2025.12.01 | D0.7 | 프론트 작성 - 인사관리 & 전자결재 피드백 반영 |
|
||||
| 2025.12.16 | D0.8 | 회계 & 보고서 작성, GPS 출퇴근, 카드/계좌관리, 보고서 추가 |
|
||||
|
||||
### 1.2 슬라이드 구성
|
||||
- **총 113 슬라이드**
|
||||
- 공통 UI 가이드 + 8개 주요 기능 모듈
|
||||
|
||||
## 2. 메뉴 구조 (GNB/LNB)
|
||||
|
||||
```
|
||||
SAM ERP
|
||||
├── 대시보드
|
||||
├── MES 메뉴 (영업관리, 판매관리, 구매관리 등)
|
||||
├── 인사관리
|
||||
│ ├── 부서관리
|
||||
│ ├── 사원관리
|
||||
│ ├── 근태관리
|
||||
│ └── 휴가관리
|
||||
├── 전자결재
|
||||
│ ├── 기안함
|
||||
│ ├── 결재함
|
||||
│ └── 참조함
|
||||
├── 게시판
|
||||
├── 회계관리
|
||||
│ ├── 거래처관리
|
||||
│ ├── 매출관리
|
||||
│ ├── 매입관리
|
||||
│ ├── 입금관리
|
||||
│ ├── 출금관리
|
||||
│ ├── 어음관리
|
||||
│ ├── 거래처원장
|
||||
│ ├── 일일 일보
|
||||
│ ├── 지출 예상 내역서
|
||||
│ ├── 미수금 현황
|
||||
│ ├── 악성채권 추심관리
|
||||
│ ├── 입출금 계좌 조회
|
||||
│ └── 카드 내역 조회
|
||||
├── 기준정보
|
||||
│ ├── 직급관리
|
||||
│ ├── 직책관리
|
||||
│ ├── 권한관리
|
||||
│ ├── 근무관리
|
||||
│ ├── 출퇴근관리
|
||||
│ ├── 휴가관리
|
||||
│ ├── 카드관리
|
||||
│ ├── 계좌관리
|
||||
│ ├── 팝업관리
|
||||
│ ├── 게시판관리
|
||||
│ ├── 일반설정
|
||||
│ └── 알림설정
|
||||
├── 보고서 및 분석
|
||||
├── 계정정보
|
||||
├── 회사정보
|
||||
├── 구독관리
|
||||
├── 결제내역
|
||||
└── 고객센터
|
||||
```
|
||||
|
||||
## 3. 기능 그룹별 슬라이드 매핑
|
||||
|
||||
| 그룹 | 슬라이드 범위 | 주요 내용 | 분석 문서 |
|
||||
|------|---------------|----------|----------|
|
||||
| 공통 | 3-13 | UI 컴포넌트, 인터랙션, 알림 | [01-common.md](./01-common.md) |
|
||||
| 인증/영업 | 14-22 | 가입, 로그인, 약관동의 | [02-auth.md](./02-auth.md) |
|
||||
| GPS 출퇴근 | 23-27 | 모바일 출퇴근 | [03-gps-attendance.md](./03-gps-attendance.md) |
|
||||
| 인사관리 | 28-46 | 부서/사원/근태/휴가 | [04-hr-management.md](./04-hr-management.md) |
|
||||
| 전자결재 | 47-59 | 기안/결재/참조함 | [05-approval.md](./05-approval.md) |
|
||||
| 회계관리 | 60-91 | 거래처/매출/매입/입출금 | [06-accounting.md](./06-accounting.md) |
|
||||
| 기준정보 | 92-104 | 직급/직책/휴가/카드/계좌 | [07-master-data.md](./07-master-data.md) |
|
||||
| 보고서 | 105-113 | 분석/AI 리포트 | [08-reports.md](./08-reports.md) |
|
||||
|
||||
## 4. 사용자 역할
|
||||
|
||||
### 4.1 영업사원 (Sales)
|
||||
- 운영 로그인
|
||||
- 회사 등록 신청
|
||||
- 테넌트 추가 알림
|
||||
|
||||
### 4.2 관리자 (Admin)
|
||||
- 자료 확인
|
||||
- 가입 승인/거절
|
||||
- 이메일로 URL 발송
|
||||
|
||||
### 4.3 고객사 (Customer/Tenant)
|
||||
- 약관 동의
|
||||
- 비밀번호 설정
|
||||
- SAM 로그인
|
||||
- 테넌트 추가
|
||||
|
||||
## 5. 핵심 비즈니스 플로우
|
||||
|
||||
### 5.1 가입 및 로그인 플로우 (슬라이드 15)
|
||||
```
|
||||
영업사원: 운영 로그인 → 사업자등록번호 입력 → 회사정보 등록 → 가입신청 완료
|
||||
↓ (거절)
|
||||
관리자: 자료 확인 → 승인? → 이메일로 URL 발송 거절 알림
|
||||
↓
|
||||
고객사: 약관 동의 → 비밀번호 설정 → SAM 로그인 → 테넌트 추가?
|
||||
↓
|
||||
매니저: 테넌트 추가 알림 → 사업자등록번호 입력
|
||||
```
|
||||
|
||||
### 5.2 회계관리 플로우 (슬라이드 61)
|
||||
```
|
||||
매출: 거래처 선택 → 매출 등록 → 세금계산서 발행
|
||||
입금: 입금 등록 → 전액 입금? → 어음 수취?
|
||||
매입: 거래처 선택 → 매입 등록 → 세금계산서 수취
|
||||
출금: 출금 등록 → 전액 출금? → 어음 발행?
|
||||
추심: 미수금 현황 → 연체? → 악성주심? → 악성 추심
|
||||
조회: 입출금 계좌 조회, 카드 내역 조회
|
||||
장부/보고서: 거래처원장, 지출 예상 내역서, 일일 일보
|
||||
```
|
||||
|
||||
## 6. 기술 스펙
|
||||
|
||||
### 6.1 반응형 웹 브레이크포인트
|
||||
- 모바일: < 640px (기본)
|
||||
- 태블릿: 768px ~ 1023px (md)
|
||||
- 데스크탑: 1024px+ (lg)
|
||||
- 대형 모니터: 1280px+ (xl)
|
||||
|
||||
### 6.2 인터랙션
|
||||
| 타입 | 적용 |
|
||||
|------|------|
|
||||
| Tap | Yes |
|
||||
| Touch & Hold | No |
|
||||
| Double Tap | No |
|
||||
| Drag & Drop | Yes |
|
||||
| Scroll Up/Down | Yes |
|
||||
| Swipe Left/Right | Yes |
|
||||
| Pinch Zoom In/Out | Yes |
|
||||
|
||||
### 6.3 알림 타입
|
||||
- Alert (알림): 사용자에게 상황 알림
|
||||
- Confirm Alert (확인): 확인/취소 선택
|
||||
- Toast Message: 2-3초 후 Fade out
|
||||
|
||||
## 7. 다음 단계
|
||||
|
||||
1. 각 기능 그룹별 상세 분석 문서 작성
|
||||
2. API 엔드포인트 도출
|
||||
3. 데이터 모델 설계
|
||||
4. 개발 우선순위 결정
|
||||
143
system/erp-analysis/01-common.md
Normal file
143
system/erp-analysis/01-common.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# 공통 UI 컴포넌트 (슬라이드 3-13)
|
||||
|
||||
## 1. 화면 템플릿 (슬라이드 6)
|
||||
|
||||
### 1.1 모바일 화면 구조
|
||||
```
|
||||
┌─────────────────────────────┐
|
||||
│ A. Status bar (OS 관리) │
|
||||
├─────────────────────────────┤
|
||||
│ B. Browser 영역 │
|
||||
├──────┬──────────────────────┤
|
||||
│Button│ Title │Button│ C. Title 영역
|
||||
├──────┴──────────────────────┤
|
||||
│ │
|
||||
│ D. Content 영역 │
|
||||
│ │
|
||||
├─────────────────────────────┤
|
||||
│ E. Browser bar 영역 │
|
||||
├─────────────────────────────┤
|
||||
│ F. Keypad 영역 (입력 시) │
|
||||
└─────────────────────────────┘
|
||||
```
|
||||
|
||||
### 1.2 영역 설명
|
||||
- **A. Status bar**: 안테나, 통화, 배터리 등 시스템 OS 관리 영역
|
||||
- **B. Browser 영역**: 브라우저 기능 영역
|
||||
- **C. Title 영역**: 텍스트 또는 기능 버튼으로 구현, 텍스트 기본 가운데 정렬
|
||||
- **D. Content 영역**: 컨텐츠 내용 표시, 컨텐츠 길이가 길어질 경우 스크롤 제공
|
||||
- **E. Browser bar 영역**: 브라우저 유틸 바 영역
|
||||
- **F. Keypad 영역**: 키보드 입력할 때 활성화, 모든 페이지 위에 덮어쓰기 구현
|
||||
|
||||
## 2. GNB/LNB/푸터 (슬라이드 8)
|
||||
|
||||
### 2.1 알림 버튼
|
||||
- 클릭: 알림 팝업 표시
|
||||
|
||||
### 2.2 개인 정보 버튼
|
||||
- 등록: 디폴트 이미지, 이름, 직급
|
||||
- 클릭: 마이페이지 팝업 표시
|
||||
|
||||
### 2.3 회사 로고
|
||||
- 회사정보 화면에서 등록한 로고 표시
|
||||
- 회사 변경 선택 시 해당 로고 변경
|
||||
|
||||
### 2.4 메뉴 영역
|
||||
- 메뉴 클릭:
|
||||
- 하위 메뉴가 있을 경우: 하위 메뉴 하단에 표시
|
||||
- 하위 메뉴 없을 경우: 해당 메뉴 화면으로 이동
|
||||
- 목록 길 경우 해당 영역 내 스크롤 처리
|
||||
|
||||
### 2.5 MES 메뉴 영역
|
||||
- 영업관리, 판매관리, 구매관리 등 해당하는 MES 메뉴 영역 표시
|
||||
|
||||
### 2.6 푸터 영역
|
||||
- 모든 화면 하단 공통 표시
|
||||
|
||||
## 3. 알림 팝업 (슬라이드 9)
|
||||
|
||||
### 3.1 알림 목록
|
||||
- 항목: 디폴트 원형일, 종류(공지사항), 안내/제목/내용, 전송일시 표시
|
||||
- 클릭: 해당 상세 화면으로 이동
|
||||
- 최신순 10개까지 표시
|
||||
|
||||
### 3.2 New 아이콘
|
||||
- 새 알림일 경우 New 아이콘 표시
|
||||
- 해당 알림 클릭 시 사라짐
|
||||
|
||||
### 3.3 빨간 점 아이콘
|
||||
- 새 알림이 있을 경우 표시
|
||||
- 해당 알림 모두 클릭 시 사라짐
|
||||
|
||||
## 4. 마이페이지 팝업 (슬라이드 10)
|
||||
|
||||
### 4.1 계정 아이디 (이메일) 표시
|
||||
|
||||
### 4.2 회사 셀렉트 박스
|
||||
- 종류: 회사명, 회사명... (해당 계정이 생성한 회사(테넌트) 목록 표시)
|
||||
- 정렬: 등록순
|
||||
- 한 회사만 소유중일 경우에는 해당 영역 숨김
|
||||
|
||||
### 4.3 로그아웃 버튼
|
||||
- 클릭: "정말 로그아웃하시겠습니까?" 로그아웃 확인 Alert 표시
|
||||
- 확인 버튼 클릭 시 로그아웃 처리
|
||||
|
||||
## 5. 셀렉트 박스 (슬라이드 11)
|
||||
|
||||
### 5.1 기본 셀렉트 박스
|
||||
- 클릭: 하단에 종류 목록 표시
|
||||
|
||||
### 5.2 종류 목록
|
||||
- 목록 중 하나만 선택 가능
|
||||
|
||||
### 5.3 다중 선택 셀렉트 박스
|
||||
- 선택된 첫번째 항목명 + 추가 수 표시
|
||||
- 텍스트 영역 부족할 경우 '항목..+3' 형태로 표시
|
||||
|
||||
### 5.4 다중 선택 종류 목록
|
||||
- 목록 중 복수 선택 가능
|
||||
- 전체 선택 시 전체 선택/해제 토글
|
||||
|
||||
### 5.5 검색 영역
|
||||
- 검색어 입력 후 엔터 또는 검색 아이콘 클릭 시 (5-1) 형태로 표시되며 (5-2) 영역에 검색 결과 표시
|
||||
|
||||
### 5.6 삭제 버튼
|
||||
- 클릭: 검색어 삭제 처리, 전체 종류 목록 표시
|
||||
|
||||
## 6. 입력 필드 가이드 메시지 (슬라이드 12)
|
||||
|
||||
### 6.1 가이드 메시지 표시 위치
|
||||
- 상황에 따라 입력 필드 하단 또는 Alert에 표시
|
||||
|
||||
### 6.2 가이드 메시지 색상
|
||||
- 긍정일 경우: 녹색
|
||||
- (1-1) 부정일 경우: 붉은색
|
||||
|
||||
## 7. 공지 팝업 (슬라이드 13)
|
||||
|
||||
### 7.1 대상
|
||||
- 전체, 설정 부서
|
||||
- 내용: 설정 기간동안 대상에게 팝업 표시
|
||||
|
||||
### 7.2 팝업 내용 영역
|
||||
- 이미지, 텍스트
|
||||
|
||||
### 7.3 "1일간 이 창을 열지 않음" 체크박스
|
||||
- 클릭: 체크 설정/해제 토글
|
||||
- 디폴트: 체크 해제 상태
|
||||
- 체크 설정 시 1일 동안 팝업 미표시 (차정 기준)
|
||||
|
||||
---
|
||||
|
||||
## API 도출
|
||||
|
||||
### 공통 API
|
||||
```
|
||||
GET /api/notifications # 알림 목록 조회
|
||||
POST /api/notifications/{id}/read # 알림 읽음 처리
|
||||
GET /api/user/profile # 마이페이지 정보
|
||||
GET /api/user/companies # 사용자 소속 회사 목록
|
||||
POST /api/auth/logout # 로그아웃
|
||||
GET /api/announcements # 공지 팝업 조회
|
||||
POST /api/announcements/{id}/dismiss # 공지 팝업 닫기 (1일간 안보기)
|
||||
```
|
||||
205
system/erp-analysis/02-auth.md
Normal file
205
system/erp-analysis/02-auth.md
Normal file
@@ -0,0 +1,205 @@
|
||||
# 인증/영업 (슬라이드 14-22)
|
||||
|
||||
## 1. 가입 및 로그인 플로우차트 (슬라이드 15)
|
||||
|
||||
### 1.1 영업사원 플로우
|
||||
```
|
||||
운영 로그인 → 사업자등록번호 입력 → 사업자번호 조회?
|
||||
↓ Yes
|
||||
회사정보 등록 → 가입신청 완료
|
||||
↓ (거절)
|
||||
거절 알림
|
||||
```
|
||||
|
||||
### 1.2 관리자 플로우
|
||||
```
|
||||
자료 확인 → 승인? → (Yes) → 이메일로 URL 발송
|
||||
↓ (No)
|
||||
계약금50% 결제 확인
|
||||
```
|
||||
|
||||
### 1.3 고객사 플로우
|
||||
```
|
||||
약관 동의 → 비밀번호 설정 → SAM 로그인 → 테넌트 추가?
|
||||
↓ (Yes)
|
||||
매니저: 테넌트 추가 알림
|
||||
↓
|
||||
사업자등록번호 입력
|
||||
```
|
||||
|
||||
## 2. 운영 로그인 (슬라이드 16)
|
||||
|
||||
### 2.1 아이디 인풋박스
|
||||
- 테넌트 생성자일 경우 이메일
|
||||
- 사용자일 경우 이메일 또는 아이디
|
||||
- (1-1) 상황별 가이드 메시지
|
||||
|
||||
### 2.2 비밀번호 인풋박스
|
||||
- 입력 시 마지막 글자 제외 후 마스킹 처리
|
||||
- (2-1) 상황별 가이드 메시지
|
||||
|
||||
### 2.3 열람 버튼
|
||||
- 클릭: 열람/숨김 토글
|
||||
- 디폴트: 숨김 상태
|
||||
- 열람 상태일 시 (2) 영역 마스킹 해제 처리
|
||||
|
||||
### 2.4 자동 로그인 체크박스
|
||||
- 클릭: 체크 설정/해제 토글
|
||||
- 체크 시 로그아웃 전까지 세션 유지
|
||||
|
||||
### 2.5 로그인 버튼
|
||||
- 클릭: 유효할 경우 대시보드 화면으로 이동
|
||||
|
||||
### 2.6 가이드 메시지
|
||||
| 상황 | 가이드 메시지 |
|
||||
|------|---------------|
|
||||
| 필수 정보 미 입력 시 | 필수 정보입니다. |
|
||||
| 4글자 미만 입력 시 | 이메일은 4자 이상 가능합니다 |
|
||||
| 이메일 형식에 유효 하지 않을 경우 | 이메일 주소를 다시 확인해주세요. |
|
||||
| 8자 미만 입력 시 | 8자 이상으로 만들어주세요. |
|
||||
| 영문+숫자+특수문자 조합이 아닐 경우 | 영문, 숫자, 특수문자를 포함해주세요. 다음의 특수기호는 보안 사항을 가능합니다., ; - @ ! |
|
||||
|
||||
## 3. 사업자등록번호 조회 (슬라이드 17)
|
||||
|
||||
### 3.1 제조 데모
|
||||
- 클릭: 제조 데모 화면으로 이동
|
||||
|
||||
### 3.2 시공 데모
|
||||
- 클릭: 시공 데모 화면으로 이동
|
||||
|
||||
### 3.3 사업자등록번호 인풋박스
|
||||
- 숫자만 가능, 10자리
|
||||
|
||||
### 3.4 다음 버튼
|
||||
- 클릭:
|
||||
1. 바로 빌 사업자등록번호 조회 후 사용 불가 경우: "중복된 사업자등록번호입니다." 알림 Alert 표시
|
||||
2. 바로 빌 사업자등록번호 조회 후 사용 가능한 경우:
|
||||
- [1] 테넌트 등록된 사업자등록번호일 경우: 테넌트 등록 전이어도 다른 영업사원이 등록중을 경우에는 사업자등록번호 사용 불가 (어드민에서는 해제 가능)
|
||||
- "등록된 사업자등록번호 입니다." 알림 Alert 표시
|
||||
- [2] 등록되지 않은 사업자등록번호일 경우: 회사정보 등록 화면으로 이동
|
||||
|
||||
## 4. 회사정보 등록 (슬라이드 18)
|
||||
|
||||
### 4.1 회사(테넌트) 상태
|
||||
- 신청: 신청 완료 입력
|
||||
- 승인: 자동 입금 외 계약금 50% 입금, 이메일로 URL 발송 선택
|
||||
- 최초 로그인 시 ERP만 표시
|
||||
- 거절: 프로젝트 설정 완료, 잔금 50% 입금 및 인도, 당월 말일까지는 무료, 익월부터 익월말까지 사용하고 구독료 청구
|
||||
- 만료: 기간 종료, 안료외 연체 상태 구분?? 영업사원에게 알림, 서비스에는 접근 불가, 배너
|
||||
- 해지: 오퍼 대기기간 단계 필요??
|
||||
- 해지: 서비스 이용 불가
|
||||
- 탈퇴: 로그인 불가, 복구 불가??
|
||||
- 미사: 서비스 이용 불가
|
||||
|
||||
### 4.2 회사 로고 이미지 영역
|
||||
- 디폴드 이미지 표시
|
||||
- 클릭: 파일탐색기 팝업 표시, 10MB 이하의 PNG, JPEG, GIF 중 하나 선택 가능
|
||||
|
||||
### 4.3 우편번호 찾기 버튼
|
||||
- 클릭: 선정한 주소 팝업 표시
|
||||
|
||||
### 4.4 찾기 버튼
|
||||
- 클릭: 파일탐색기 팝업 표시, 이미지 또는 파일 하나 선택 가능
|
||||
|
||||
### 4.5 가입 신청 버튼
|
||||
- 클릭: 사업자등록번호 조회 화면으로 이동
|
||||
- 회사 로고만 선택, 나머지는 필수 정보
|
||||
- 클릭: 가입 신청 완료 화면으로 이동
|
||||
|
||||
### 4.6 입력 필드
|
||||
| 필드명 | 필수 | 설명 |
|
||||
|--------|------|------|
|
||||
| 회사 로고 | N | 750x250px, 10MB 이하 PNG, JPEG, GIF |
|
||||
| 회사명 | Y | |
|
||||
| 대표자명 | Y | |
|
||||
| 업태 | Y | |
|
||||
| 업종 | Y | |
|
||||
| 주소 | Y | 우편번호 찾기 + 상세주소 |
|
||||
| 이메일 (아이디) | Y | |
|
||||
| 세금계산서 이메일 | Y | |
|
||||
| 담당자명 | Y | |
|
||||
| 담당자 연락처 | Y | |
|
||||
| 사업자등록증 | Y | 파일 첨부 |
|
||||
|
||||
## 5. 가입 신청 완료 (슬라이드 19)
|
||||
|
||||
### 5.1 가입 신청 완료 안내 문구 표시
|
||||
- SAM은 폐쇄형 네트워크이므로 플랫폼의 무결성을 보장하기 위해 모든 계정을 검토해야 합니다.
|
||||
- (회사명)의 계정 세부 정보를 추가로 확인하기 위해 추가 정보를 요청할 수 있습니다.
|
||||
- 영업일 기준 3일 이내에 계정에 대한 업데이트를 받게 됩니다.
|
||||
|
||||
### 5.2 가입 신청 취소 버튼
|
||||
- 클릭: "가입 신청 취소 시 등록한 모든 정보가 삭제됩니다. 정말 가입 신청을 취소하시겠습니까?" 확인 Alert 표시
|
||||
|
||||
## 6. 가입 신청 승인 성공 이메일 (슬라이드 20)
|
||||
|
||||
### 6.1 계정 활성화 버튼
|
||||
- 클릭: 약관 동의 화면으로 이동
|
||||
|
||||
### 6.2 지원, 블로그 버튼
|
||||
- 클릭: 해당 운영 노션 링크로 이동
|
||||
|
||||
## 7. 약관 동의 (슬라이드 21)
|
||||
|
||||
### 7.1 약관 영역
|
||||
- 클릭: (1-1) 약관 내용 영역 열림/닫힘 토글
|
||||
- 디폴트: 닫힘
|
||||
|
||||
### 7.2 체크박스
|
||||
- 클릭: 체크 설정/해제 토글
|
||||
- 디폴트: 체크 설정 해제
|
||||
|
||||
### 7.3 약관에 동의합니다 버튼
|
||||
- 모든 필수 약관 동의 시 버튼 활성화
|
||||
- 클릭: 비밀번호 설정 화면으로 이동
|
||||
|
||||
### 7.4 약관에 동의합니다 버튼
|
||||
- 클릭: 모든 필수, 선택 약관에 동의 처리, 비밀번호 설정 화면으로 이동
|
||||
|
||||
### 7.5 약관 목록
|
||||
| 약관명 | 필수 |
|
||||
|--------|------|
|
||||
| [필수] 서비스 이용약관 | Y |
|
||||
| [필수] 개인정보 취급방침 | Y |
|
||||
| [필수] 약관명 | Y |
|
||||
| 마케팅 정보 수신 동의 (선택) | N |
|
||||
| - 이메일 수신 동의 | N |
|
||||
| - SMS 수신 동의 | N |
|
||||
|
||||
## 8. 비밀번호 설정 (슬라이드 22)
|
||||
|
||||
### 8.1 계정 활성화 버튼
|
||||
- 클릭: 로그인 화면으로 이동
|
||||
|
||||
### 8.2 비밀번호 요구사항
|
||||
- 최소 8자 이상 영문+숫자+특수문자 조합
|
||||
|
||||
---
|
||||
|
||||
## API 도출
|
||||
|
||||
### 인증 API
|
||||
```
|
||||
POST /api/auth/login # 로그인
|
||||
POST /api/auth/logout # 로그아웃
|
||||
POST /api/auth/password/reset # 비밀번호 재설정
|
||||
POST /api/auth/password/change # 비밀번호 변경
|
||||
```
|
||||
|
||||
### 회원가입/영업 API
|
||||
```
|
||||
GET /api/business-registration/verify # 사업자등록번호 조회
|
||||
POST /api/registration/company # 회사정보 등록
|
||||
POST /api/registration/cancel # 가입신청 취소
|
||||
GET /api/terms # 약관 목록 조회
|
||||
POST /api/terms/agree # 약관 동의
|
||||
POST /api/account/activate # 계정 활성화
|
||||
```
|
||||
|
||||
### 테넌트 API
|
||||
```
|
||||
GET /api/tenants # 테넌트 목록
|
||||
POST /api/tenants # 테넌트 추가
|
||||
PUT /api/tenants/{id} # 테넌트 수정
|
||||
GET /api/tenants/{id}/switch # 테넌트 전환
|
||||
```
|
||||
144
system/erp-analysis/03-gps-attendance.md
Normal file
144
system/erp-analysis/03-gps-attendance.md
Normal file
@@ -0,0 +1,144 @@
|
||||
# GPS 출퇴근 (슬라이드 23-27)
|
||||
|
||||
## 1. 개요
|
||||
|
||||
GPS 기반 모바일 출퇴근 관리 시스템으로, 위치 정보를 활용하여 출근/퇴근을 기록합니다.
|
||||
|
||||
## 2. 마이페이지 팝업 > 출퇴근하기 (슬라이드 24)
|
||||
|
||||
### 2.1 출퇴근 버튼
|
||||
- GPS 출퇴근 사용 시에만 표시
|
||||
- 모바일일 경우에만 버튼 활성화
|
||||
- 클릭: 출퇴근하기 화면으로 이동
|
||||
|
||||
### 2.2 출퇴근 허용 반경
|
||||
- 기준 화표로부터의 출퇴근 허용 반경을 m 형으로 표시 (기준정보 > 출퇴근관리에서 설정)
|
||||
|
||||
### 2.3 현재 위치 버튼
|
||||
- 클릭: (3-1) 해당 현재 위치를 지도 중심으로 표시
|
||||
|
||||
### 2.4 [+] 버튼
|
||||
- 클릭: 지도 영역 확대
|
||||
|
||||
### 2.5 확대/축소 슬라이드바
|
||||
- 드래그&드랍 또는 클릭: 지도 영역 확대/축소
|
||||
|
||||
### 2.6 [-] 버튼
|
||||
- 클릭: 지도 영역 축소
|
||||
|
||||
### 2.7 개인 정보 영역
|
||||
- 항목: 프로필 이미지, 이름, 부서명, 직급명
|
||||
|
||||
### 2.8 현재 시:분:초 표시
|
||||
- HH:MM:SS
|
||||
|
||||
### 2.9 출근하기 버튼
|
||||
- 클릭:
|
||||
1) 출근 위치 미설정 상태일 경우: "출근 위치를 설정해주세요." 알림 Alert 표시
|
||||
2) 출근 위치 설정 상태일 경우:
|
||||
- (1) 출근 위치가 기준 설정 반경 초과일 경우: "출근 가능 위치가 아닙니다. 출근 위치를 확인해주세요." 알림 Alert 표시
|
||||
- [2] GPS 출근 위치 기준 설정 반경 이내: 출근하기 화면으로 이동 (출근 기록 저장)
|
||||
|
||||
## 3. 출근하기 (슬라이드 25)
|
||||
|
||||
### 3.1 퇴근하기 버튼
|
||||
- 클릭:
|
||||
1) 퇴근 위치 미설정 상태일 경우: "퇴근 위치를 설정해주세요." 알림 Alert 표시
|
||||
2) 퇴근 위치 설정 상태일 경우:
|
||||
- (1) 퇴근 위치가 기준 설정 반경 초과일 경우: "퇴근 가능 위치가 아닙니다. 퇴근 위치를 확인해주세요." 알림 Alert 표시
|
||||
- [2] GPS 퇴근 위치 기준 설정 반경 이내: 퇴근하기 화면으로 이동 (퇴근 기록 저장)
|
||||
|
||||
### 3.2 출근 완료 아이콘 이미지 표시
|
||||
|
||||
### 3.3 출근 완료 정보
|
||||
- 항목: 출근 완료 문구, 시:분:초, 일자(요일)
|
||||
|
||||
### 3.4 출근 화표의 본사/현장명 표시
|
||||
|
||||
### 3.5 확인 버튼
|
||||
- 클릭: 대시보드로 이동
|
||||
|
||||
## 4. 퇴근하기 (슬라이드 26)
|
||||
|
||||
### 4.1 퇴근 완료 아이콘 이미지 표시
|
||||
|
||||
### 4.2 퇴근 완료 정보
|
||||
- 항목: 퇴근 완료 문구, 시:분:초, 일자(요일)
|
||||
|
||||
### 4.3 퇴근 화표의 본사/현장명 표시
|
||||
|
||||
### 4.4 확인 버튼
|
||||
- 클릭: 대시보드로 이동
|
||||
|
||||
## 5. 현장 등록 (슬라이드 27)
|
||||
|
||||
### 5.1 위치 정보 설정
|
||||
- 각 현장의 GPS 중심값으로 설정
|
||||
|
||||
### 5.2 입력 필드
|
||||
| 필드명 | 필수 | 설명 |
|
||||
|--------|------|------|
|
||||
| 주소 | Y | 우편번호 찾기 + 상세주소 |
|
||||
| 경도 | Y | |
|
||||
| 위도 | Y | |
|
||||
| 첨부파일 | N | |
|
||||
|
||||
---
|
||||
|
||||
## 데이터 모델
|
||||
|
||||
### Attendance (출퇴근 기록)
|
||||
```
|
||||
- id: bigint
|
||||
- user_id: bigint (FK)
|
||||
- tenant_id: bigint (FK)
|
||||
- type: enum('check_in', 'check_out')
|
||||
- check_time: datetime
|
||||
- latitude: decimal(10,8)
|
||||
- longitude: decimal(11,8)
|
||||
- location_id: bigint (FK, nullable) # 본사/현장
|
||||
- location_name: string
|
||||
- is_valid: boolean # 유효 위치 여부
|
||||
- created_at: timestamp
|
||||
```
|
||||
|
||||
### Location (본사/현장)
|
||||
```
|
||||
- id: bigint
|
||||
- tenant_id: bigint (FK)
|
||||
- name: string
|
||||
- address: string
|
||||
- detail_address: string
|
||||
- latitude: decimal(10,8)
|
||||
- longitude: decimal(11,8)
|
||||
- allowed_radius: int # 허용 반경 (m)
|
||||
- is_active: boolean
|
||||
- created_at: timestamp
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API 도출
|
||||
|
||||
### GPS 출퇴근 API
|
||||
```
|
||||
POST /api/attendance/check-in # 출근 기록
|
||||
POST /api/attendance/check-out # 퇴근 기록
|
||||
GET /api/attendance/today # 오늘 출퇴근 현황
|
||||
GET /api/attendance/status # 현재 출퇴근 상태
|
||||
GET /api/attendance/location/validate # 위치 유효성 검증
|
||||
```
|
||||
|
||||
### 현장 관리 API
|
||||
```
|
||||
GET /api/locations # 현장 목록
|
||||
POST /api/locations # 현장 등록
|
||||
PUT /api/locations/{id} # 현장 수정
|
||||
DELETE /api/locations/{id} # 현장 삭제
|
||||
```
|
||||
|
||||
### 출퇴근 설정 API
|
||||
```
|
||||
GET /api/settings/attendance # 출퇴근 설정 조회
|
||||
PUT /api/settings/attendance # 출퇴근 설정 수정
|
||||
```
|
||||
381
system/erp-analysis/04-hr-management.md
Normal file
381
system/erp-analysis/04-hr-management.md
Normal file
@@ -0,0 +1,381 @@
|
||||
# 인사관리 (슬라이드 28-46)
|
||||
|
||||
## 1. 개요
|
||||
|
||||
인사관리 모듈은 부서, 사원, 근태, 휴가를 관리합니다.
|
||||
|
||||
## 2. 부서관리 (슬라이드 30-31)
|
||||
|
||||
### 2.1 전체 선택 체크박스
|
||||
- 클릭: 전체 선택 설정/해제 토글
|
||||
- 디폴트: 설정 해제 상태
|
||||
|
||||
### 2.2 개별 선택 체크박스
|
||||
- 클릭: 개별 선택 설정/해제 토글
|
||||
- 디폴트: 설정 해제 상태
|
||||
|
||||
### 2.3 추가 버튼
|
||||
- 클릭: 선택된 부서의 하위 부서 일괄 생성
|
||||
- 관리 권한이 없을 경우 숨김
|
||||
|
||||
### 2.4 삭제 버튼
|
||||
- 클릭: "선택한 부서 N개를 삭제하시겠습니까?" 확인 Alert 표시
|
||||
- 확인 선택 시 삭제된 부서 사원의 인원은 회사(기본) 인원으로 변경
|
||||
|
||||
### 2.5 확대 버튼
|
||||
- 클릭: (6) 확대 버튼으로 변경, 하위 부서 숨김 처리
|
||||
|
||||
### 2.6 축소 버튼
|
||||
- 클릭: (5) 축소 버튼으로 변경, 하위 부서 표시 처리
|
||||
|
||||
### 2.7 추가 버튼
|
||||
- 클릭: 부서 추가 팝업 표시
|
||||
|
||||
### 2.8 수정 버튼
|
||||
- 클릭: 부서 수정 팝업 표시
|
||||
|
||||
### 2.9 부서 추가/수정 팝업
|
||||
- 부서명 인풋박스: 기존 부서명 표시, 수정 가능
|
||||
|
||||
## 3. 사원관리 (슬라이드 32-40)
|
||||
|
||||
### 3.1 기간 설정 영역
|
||||
- 입사일 기준
|
||||
|
||||
### 3.2 기간 설정 버튼 영역
|
||||
- 종류: 당해년도, 전전월, 전월, 당월, 어제, 오늘
|
||||
- 클릭: 해당 기간이 (1) 영역에 설정되며 화면 전체에 적용 처리
|
||||
|
||||
### 3.3 CSV 일괄 등록 버튼
|
||||
- 클릭: CSV 일괄 등록 화면으로 이동
|
||||
|
||||
### 3.4 사원 등록 버튼
|
||||
- 클릭: 사원 상세 화면으로 이동
|
||||
|
||||
### 3.5 사용자 초대 버튼
|
||||
- 클릭: 사용자 초대 팝업 표시
|
||||
|
||||
### 3.6 필터 셀렉트 박스
|
||||
- 종류: 전체, 사용자 아이디 보유, 사용자 아이디 미보유, 재직, 휴직, 퇴직
|
||||
- 디폴트: 전체
|
||||
|
||||
### 3.7 정렬 셀렉트 박스
|
||||
- 종류: 직급순, 부서 오름차순, 부서 내림차순, 이름 오름차순, 이름 내림차순
|
||||
- 디폴트: 직급순
|
||||
|
||||
### 3.8 현황 카드
|
||||
| 항목 | 설명 |
|
||||
|------|------|
|
||||
| 현직 | 전체 현직 사원 수 |
|
||||
| 휴직 | 전체 휴직 사원 수 |
|
||||
| 퇴직 | (1) 해당 기간의 퇴직 사원 수 |
|
||||
| 평균근속년수 | 전체 현직 사원 기준 |
|
||||
|
||||
## 4. 사원 상세 (슬라이드 33-36)
|
||||
|
||||
### 4.1 사원 정보 영역 (필수)
|
||||
| 필드명 | 필수 | 설명 |
|
||||
|--------|------|------|
|
||||
| 이름 | Y | |
|
||||
| 주민등록번호 | Y | |
|
||||
| 휴대폰 | Y | |
|
||||
| 이메일 | Y | |
|
||||
| 연봉 | N | |
|
||||
| 급여계좌 은행 | N | 은행 선택 |
|
||||
| 계좌 | N | |
|
||||
| 예금주 | N | |
|
||||
|
||||
### 4.2 사용자 정보 영역
|
||||
| 필드명 | 필수 | 설명 |
|
||||
|--------|------|------|
|
||||
| 아이디 | N | 이메일 또는 아이디 |
|
||||
| 비밀번호 | N | 최소 8자 이상 영문+숫자+특수문자 조합 |
|
||||
| 권한 | N | 권한관리의 목록 표시 |
|
||||
| 상태 | N | 종류: 정상, 재직, 중지. 재직 상태인 경우 로그아웃 처리, 로그인 시 '재정중인 아이디입니다.' 팝업 |
|
||||
|
||||
### 4.3 사원 상세 영역 (선택)
|
||||
| 필드명 | 필수 | 설명 |
|
||||
|--------|------|------|
|
||||
| 프로필 사진 | N | 1250x250px, 10MB 이하 PNG, JPEG, GIF |
|
||||
| 사원코드 | N | |
|
||||
| 성별 | N | 남성/여성 |
|
||||
| 주소 | N | 우편번호 찾기 + 상세주소 |
|
||||
|
||||
### 4.4 인사 정보 영역
|
||||
| 필드명 | 필수 | 설명 |
|
||||
|--------|------|------|
|
||||
| 입사일 | N | |
|
||||
| 고용 형태 | N | 종류: 정규직, 계약직, 파견직, 용역직, 시간제 근로자 |
|
||||
| 직급 | N | 직급관리 화면에서 설정 |
|
||||
| 상태 | N | 종류: 재직, 병가휴직, 육아휴직, 개인사정휴직, 무급휴직, 퇴사, 해고, 권고사직, 계약만료, 정년퇴직 |
|
||||
| 부서 | N | 부서관리 화면에서 설정 |
|
||||
| 직책 | N | 직책관리 화면에서 설정 |
|
||||
| 출근 위치 | N | 본사, 현장 목록 |
|
||||
| 퇴근 위치 | N | 본사, 현장 목록 |
|
||||
| 퇴사일 | N | |
|
||||
| 퇴사 사유 | N | |
|
||||
|
||||
### 4.5 항목 설정 버튼
|
||||
- 클릭: 항목 설정 팝업 표시
|
||||
|
||||
### 4.6 항목 설정 팝업 (슬라이드 34)
|
||||
- 전체 설정 ON/OFF 버튼
|
||||
- 개별 설정 ON/OFF 버튼
|
||||
- 디폴트: 설정 OFF 상태
|
||||
|
||||
## 5. CSV 일괄 등록 (슬라이드 39-40)
|
||||
|
||||
### 5.1 양식 다운로드 버튼
|
||||
- 클릭: 등록된 양식 CSV 다운로드
|
||||
|
||||
### 5.2 파일 선택 버튼
|
||||
- 클릭: 파일 탐색기 팝업, CSV 1개만 등록
|
||||
|
||||
### 5.3 파일 변환 버튼
|
||||
- 클릭: CSV 데이터를 (3-1) 정보 등록 영역에 변환값 표시
|
||||
- 범위: 사원 상세 화면의 전체 항목
|
||||
|
||||
### 5.4 등록 버튼
|
||||
- 파일변환 완료 & (2) 체크 설정 항목 있을 경우에만 버튼 활성화
|
||||
- 클릭: "(등록)의 정보를 정말 등록하시겠습니까?" 확인 Alert 표시
|
||||
- 확인 클릭 시 (2) 체크 된 정보만 등록, "정보 등록이 완료되었습니다." 알림 Alert 표시
|
||||
|
||||
## 6. 사용자 초대 (슬라이드 37-38)
|
||||
|
||||
### 6.1 사용자 초대 프로세스
|
||||
- 초대 이메일로 발송 → 약관 동의 (아이디를 이메일로 사용) → 비밀번호 설정 → 로그인
|
||||
|
||||
### 6.2 초대할 이메일 주소 인풋박스
|
||||
- ','로 구분하여 여러 주소 입력 가능
|
||||
|
||||
### 6.3 권한 셀렉트 박스, 검색
|
||||
- 종류: 권한관리의 목록 표시
|
||||
|
||||
### 6.4 초대 메시지 인풋박스
|
||||
|
||||
### 6.5 초대 버튼
|
||||
- 클릭: 사용자 초대 이메일 발송
|
||||
- 이메일 주소 기준 사원 정보가 있을 경우에는 대량하여 사용자 등록 처리
|
||||
- 없을 경우에는 사용자에만 등록하고 나머지 사원 정보는 직접 입력 필요
|
||||
- 사용자 아이디 중복 불가
|
||||
|
||||
## 7. 근태관리 (슬라이드 41-42)
|
||||
|
||||
### 7.1 근태관리 현황 카드
|
||||
| 항목 | 설명 |
|
||||
|------|------|
|
||||
| 정시 출근 | 전체 정시 출근 수 |
|
||||
| 지각 | 전체 지각 수 |
|
||||
| 결근 | 전체 결근 수 |
|
||||
| 휴가 | 전체 휴가 수 |
|
||||
|
||||
### 7.2 기간 설정 버튼 영역
|
||||
- 종류: 당해년도, 전전월, 전월, 당월, 어제, 오늘
|
||||
- 클릭: 해당 기간이 설정되며 화면 전체에 적용 처리
|
||||
- 근태관리 자동 설정 시: 모든 사원이 정시 출퇴근한 것으로 기록, 예외사항만 경우 작성
|
||||
|
||||
### 7.3 근태 등록 버튼
|
||||
- 클릭: 근태 정보 팝업 표시
|
||||
|
||||
### 7.4 사유 등록 버튼
|
||||
- 클릭: 사유 정보 팝업 표시
|
||||
|
||||
### 7.5 필터 셀렉트 박스
|
||||
- 종류: 전체, 정시 출근, 지각, 결근, 휴가, 출장, 외근, 연장근무
|
||||
- 디폴트: 전체
|
||||
|
||||
### 7.6 정렬 셀렉트 박스
|
||||
- 종류: 직급순, 부서 오름차순, 부서 내림차순, 이름 오름차순, 이름 내림차순
|
||||
- 디폴트: 직급순
|
||||
|
||||
### 7.7 근태 정보 팝업
|
||||
| 필드명 | 설명 |
|
||||
|--------|------|
|
||||
| 대상 | 사원 셀렉트 박스, 검색&다중 선택 |
|
||||
| 기준일 | 달력 팝업 표시, 일자 다중 선택 가능, 디폴트: 당일 |
|
||||
| 출근 시간 | 시/분 선택 |
|
||||
| 퇴근 시간 | 시/분 선택 |
|
||||
| 야간 연장 시간 | 주당 연장 근로 시간에서 주말 연장 시간을 차감한 이내에만 설정 가능 |
|
||||
| 주말 연장 시간 | 주당 연장 근로 시간에서 야간 연장 시간을 차감한 이내에만 설정 가능 |
|
||||
|
||||
### 7.8 사유 정보 팝업
|
||||
| 필드명 | 설명 |
|
||||
|--------|------|
|
||||
| 대상 | 사원 셀렉트 박스 |
|
||||
| 기준일 | 달력 팝업 |
|
||||
| 내용 | 내용 입력 |
|
||||
|
||||
## 8. 휴가관리 (슬라이드 43-46)
|
||||
|
||||
### 8.1 휴가관리 탭
|
||||
- 휴가 사용 현황
|
||||
- 휴가 부여 현황
|
||||
- 휴가 신청 현황
|
||||
|
||||
### 8.2 휴가관리 현황 카드
|
||||
| 항목 | 설명 |
|
||||
|------|------|
|
||||
| 휴가 승인 대기 | |
|
||||
| 연차 | 전체 연차 사원 수 |
|
||||
| 결조사 | |
|
||||
| 연간 연차 사용률 | |
|
||||
|
||||
### 8.3 필터 셀렉트 박스
|
||||
- 종류: 전체, 모든 사원 목록
|
||||
- 항목: 부서명, 직급명, 사원명 표시
|
||||
- 디폴트: 전체
|
||||
|
||||
### 8.4 정렬 셀렉트 박스
|
||||
- 종류: 직급순, 부서 오름차순, 부서 내림차순, 이름 오름차순, 이름 내림차순
|
||||
- 디폴트: 직급순
|
||||
|
||||
### 8.5 휴가 부여 팝업
|
||||
| 필드명 | 설명 |
|
||||
|--------|------|
|
||||
| 유형 | 종류: 연차, 보상, 경조, 보건, 병가, 반차, 반수 선택 |
|
||||
| 사유 | |
|
||||
| 일수 | 개월/일/시간 선택 |
|
||||
|
||||
### 8.6 휴가 신청 팝업
|
||||
| 필드명 | 설명 |
|
||||
|--------|------|
|
||||
| 잔여 | 잔여 일수 표시 |
|
||||
| 유형 | 종류: 연차, 보상, 경조, 보건, 병가, 반차 |
|
||||
| 기간 | 날짜 선택, (5) 반차 선택 시에는 시간으로 변경 |
|
||||
|
||||
### 8.7 휴가 신청 현황 필터
|
||||
- 종류: 승인, 거절
|
||||
- 1/3 완료: 결재선 승인 진행도에 따라 표시
|
||||
|
||||
---
|
||||
|
||||
## 데이터 모델
|
||||
|
||||
### Department (부서)
|
||||
```
|
||||
- id: bigint
|
||||
- tenant_id: bigint (FK)
|
||||
- parent_id: bigint (FK, nullable)
|
||||
- name: string
|
||||
- order: int
|
||||
- is_active: boolean
|
||||
- created_at: timestamp
|
||||
```
|
||||
|
||||
### Employee (사원)
|
||||
```
|
||||
- id: bigint
|
||||
- tenant_id: bigint (FK)
|
||||
- user_id: bigint (FK, nullable)
|
||||
- department_id: bigint (FK, nullable)
|
||||
- employee_code: string
|
||||
- name: string
|
||||
- resident_number: string (encrypted)
|
||||
- phone: string
|
||||
- email: string
|
||||
- salary: decimal
|
||||
- bank_code: string
|
||||
- account_number: string
|
||||
- account_holder: string
|
||||
- profile_image: string
|
||||
- gender: enum('male', 'female')
|
||||
- address: string
|
||||
- hire_date: date
|
||||
- employment_type: enum('regular', 'contract', 'dispatch', 'service', 'part_time')
|
||||
- position_id: bigint (FK)
|
||||
- job_title_id: bigint (FK)
|
||||
- status: enum('active', 'leave', 'sick_leave', 'parental_leave', 'personal_leave', 'unpaid_leave', 'resigned', 'dismissed', 'recommended_resignation', 'contract_expired', 'retired')
|
||||
- check_in_location_id: bigint (FK, nullable)
|
||||
- check_out_location_id: bigint (FK, nullable)
|
||||
- resignation_date: date
|
||||
- resignation_reason: text
|
||||
- created_at: timestamp
|
||||
```
|
||||
|
||||
### AttendanceRecord (근태 기록)
|
||||
```
|
||||
- id: bigint
|
||||
- tenant_id: bigint (FK)
|
||||
- employee_id: bigint (FK)
|
||||
- date: date
|
||||
- check_in_time: time
|
||||
- check_out_time: time
|
||||
- status: enum('on_time', 'late', 'absent', 'leave', 'business_trip', 'outside_work', 'overtime')
|
||||
- night_overtime_hours: decimal
|
||||
- weekend_overtime_hours: decimal
|
||||
- reason: text
|
||||
- created_at: timestamp
|
||||
```
|
||||
|
||||
### Leave (휴가)
|
||||
```
|
||||
- id: bigint
|
||||
- tenant_id: bigint (FK)
|
||||
- employee_id: bigint (FK)
|
||||
- type: enum('annual', 'reward', 'congratulation', 'health', 'sick', 'half_day', 'half_day_am', 'half_day_pm')
|
||||
- start_date: date
|
||||
- end_date: date
|
||||
- days: decimal
|
||||
- reason: text
|
||||
- status: enum('pending', 'approved', 'rejected')
|
||||
- created_at: timestamp
|
||||
```
|
||||
|
||||
### LeaveBalance (휴가 잔여)
|
||||
```
|
||||
- id: bigint
|
||||
- tenant_id: bigint (FK)
|
||||
- employee_id: bigint (FK)
|
||||
- year: int
|
||||
- type: enum('annual', 'reward', ...)
|
||||
- granted: decimal
|
||||
- used: decimal
|
||||
- remaining: decimal
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API 도출
|
||||
|
||||
### 부서관리 API
|
||||
```
|
||||
GET /api/departments # 부서 목록 (트리 구조)
|
||||
POST /api/departments # 부서 추가
|
||||
PUT /api/departments/{id} # 부서 수정
|
||||
DELETE /api/departments/{id} # 부서 삭제
|
||||
DELETE /api/departments/bulk # 부서 일괄 삭제
|
||||
```
|
||||
|
||||
### 사원관리 API
|
||||
```
|
||||
GET /api/employees # 사원 목록
|
||||
POST /api/employees # 사원 등록
|
||||
GET /api/employees/{id} # 사원 상세
|
||||
PUT /api/employees/{id} # 사원 수정
|
||||
DELETE /api/employees/{id} # 사원 삭제
|
||||
POST /api/employees/import # CSV 일괄 등록
|
||||
GET /api/employees/template # CSV 템플릿 다운로드
|
||||
POST /api/employees/invite # 사용자 초대
|
||||
```
|
||||
|
||||
### 근태관리 API
|
||||
```
|
||||
GET /api/attendance # 근태 목록
|
||||
POST /api/attendance # 근태 등록
|
||||
PUT /api/attendance/{id} # 근태 수정
|
||||
GET /api/attendance/summary # 근태 현황 요약
|
||||
POST /api/attendance/reason # 사유 등록
|
||||
```
|
||||
|
||||
### 휴가관리 API
|
||||
```
|
||||
GET /api/leaves # 휴가 목록
|
||||
POST /api/leaves # 휴가 신청
|
||||
PUT /api/leaves/{id} # 휴가 수정
|
||||
DELETE /api/leaves/{id} # 휴가 취소
|
||||
POST /api/leaves/{id}/approve # 휴가 승인
|
||||
POST /api/leaves/{id}/reject # 휴가 거절
|
||||
GET /api/leaves/balance/{employee_id} # 휴가 잔여 조회
|
||||
POST /api/leaves/grant # 휴가 부여
|
||||
GET /api/leaves/summary # 휴가 현황 요약
|
||||
```
|
||||
296
system/erp-analysis/05-approval.md
Normal file
296
system/erp-analysis/05-approval.md
Normal file
@@ -0,0 +1,296 @@
|
||||
# 전자결재 (슬라이드 47-59)
|
||||
|
||||
## 1. 개요
|
||||
|
||||
전자결재 모듈은 기안함, 결재함, 참조함으로 구성되며, 품의서, 지출결의서, 지출 예상 내역서 등의 문서를 처리합니다.
|
||||
|
||||
## 2. 기안함 (슬라이드 48-54)
|
||||
|
||||
### 2.1 문서 상태
|
||||
| 상태 | 설명 |
|
||||
|------|------|
|
||||
| 임시저장 | 문서 작성 중, 임시저장된 상태 |
|
||||
| 진행 | 모든 결재자 중 일부 승인된 상태, 결재 요청을 받은 상태 |
|
||||
| 결재요청 | 결재 요청을 받은 상태 |
|
||||
| 반려 | 결재자 중 반려한 사람이 있는 상태 |
|
||||
|
||||
### 2.2 기안함 현황 카드
|
||||
| 항목 | 설명 |
|
||||
|------|------|
|
||||
| 진행 | 진행 중인 문서 수 |
|
||||
| 전체 | 전체 문서 수 |
|
||||
| 반려 | 반려된 문서 수 |
|
||||
| 임시 저장 | 임시저장 문서 수 |
|
||||
|
||||
### 2.3 문서 작성 버튼
|
||||
- 클릭: 문서 작성 화면으로 이동
|
||||
|
||||
### 2.4 상세 버튼
|
||||
- 클릭: 문서 상세 팝업 표시
|
||||
|
||||
### 2.5 삭제 버튼
|
||||
- 클릭:
|
||||
1) 임시저장 상태일 경우: "정말 이(1)건을 삭제 처리하시겠습니까?" 확인 Alert 표시, 확인 시 해당 문서 삭제 처리
|
||||
2) 임시저장 상태가 아닐 경우: "임시저장 상태만 삭제가 가능합니다." 알림 Alert 표시
|
||||
|
||||
### 2.6 수정 버튼
|
||||
- 클릭:
|
||||
1) 임시저장 상태일 경우: 문서 작성 화면으로 이동
|
||||
2) 임시저장 상태가 아닐 경우: 문서 상세 팝업 표시
|
||||
|
||||
### 2.7 필터 셀렉트 박스
|
||||
- 종류: 전체, 임시저장, 진행, 완료, 반려
|
||||
- 디폴트: 전체
|
||||
|
||||
### 2.8 정렬 셀렉트 박스
|
||||
- 종류: 최신순, 등록순
|
||||
- 디폴트: 최신순
|
||||
|
||||
## 3. 문서 작성 (슬라이드 49-54)
|
||||
|
||||
### 3.1 상세 버튼
|
||||
- 클릭: 문서 상세 팝업 표시
|
||||
|
||||
### 3.2 문서 유형 셀렉트 박스, 검색
|
||||
- 종류: 품의서, 지출결의서, 지출 예상 내역서
|
||||
- 선택한 문서 유형의 화면으로 변경 표시
|
||||
|
||||
### 3.3 결재자 셀렉트 박스, 검색&다중 선택
|
||||
- 항목: 부서명, 직책명, 사원명 표시
|
||||
- 종류: 전체, 모든 사원 목록
|
||||
- 디폴트: 전체
|
||||
|
||||
### 3.4 참조자 셀렉트 박스, 검색&다중 선택
|
||||
- 항목: 부서명, 직책명, 사원명 표시
|
||||
- 종류: 전체, 모든 사원 목록
|
||||
- 디폴트: 전체
|
||||
|
||||
### 3.5 버튼 영역
|
||||
| 버튼 | 설명 |
|
||||
|------|------|
|
||||
| 상세 | 문서 상세 팝업 표시 |
|
||||
| 삭제 | 임시저장 상태만 삭제 가능 |
|
||||
| 삼신 | 결재선 마감 경우 숨김 |
|
||||
| 임시저장 | 임시저장 처리 |
|
||||
|
||||
## 4. 품의서 (슬라이드 50)
|
||||
|
||||
### 4.1 구매처 정보
|
||||
| 필드명 | 필수 | 설명 |
|
||||
|--------|------|------|
|
||||
| 구매처 | Y | |
|
||||
| 구매처 결제일 | Y | |
|
||||
|
||||
### 4.2 품의서 정보
|
||||
| 필드명 | 필수 | 설명 |
|
||||
|--------|------|------|
|
||||
| 제목 | Y | |
|
||||
| 품의 내역 | Y | 녹음 버튼: 마이크 사용 가능할 경우에만 버튼 활성화, 녹음 중지 버튼으로 변경, 음성 내용을 텍스트로 변경하여 (1-1) 인풋박스 영역에 표시 |
|
||||
| 품의 사유 | Y | |
|
||||
| 예상 비용 | Y | |
|
||||
|
||||
### 4.3 참고 이미지 정보
|
||||
- 첨부파일 찾기
|
||||
|
||||
## 5. 지출결의서 (슬라이드 51-52)
|
||||
|
||||
### 5.1 지출 정보
|
||||
| 필드명 | 필수 | 설명 |
|
||||
|--------|------|------|
|
||||
| 지출 요청일 | Y | |
|
||||
| 결제일 | Y | |
|
||||
|
||||
### 5.2 지출결의서 정보
|
||||
| 필드명 | 필수 | 설명 |
|
||||
|--------|------|------|
|
||||
| 적요 | Y | |
|
||||
| 금액 | Y | |
|
||||
| 비고 | N | |
|
||||
|
||||
### 5.3 결제 정보
|
||||
| 필드명 | 필수 | 설명 |
|
||||
|--------|------|------|
|
||||
| 카드 | Y | 종류: 등록된 카드 목록, 디폴트: 첫번째 카드 |
|
||||
| 총 비용 | - | 지출결의서 정보의 금액 합계 표시 |
|
||||
|
||||
### 5.4 참고 이미지 정보
|
||||
- 첨부파일 찾기
|
||||
|
||||
## 6. 지출 예상 내역서 (슬라이드 53-54)
|
||||
|
||||
### 6.1 지출 예상 내역서 목록
|
||||
- 체크 설정/해제 토글
|
||||
- 1) 지출 예상 내역서 화면에서 찾을 경우: 설정된 체크 상태 유지
|
||||
- 2) 문서 작성 화면에서 설정했을 경우: 모든 체크 항목 설정된 상태
|
||||
|
||||
### 6.2 항목
|
||||
| 필드명 | 설명 |
|
||||
|--------|------|
|
||||
| 예상 지급일 | |
|
||||
| 품목 | |
|
||||
| 지출금액 | |
|
||||
| 거래처 | |
|
||||
| 계좌 | |
|
||||
|
||||
### 6.3 합계
|
||||
| 항목 | 설명 |
|
||||
|------|------|
|
||||
| 지출 합계 | |
|
||||
| 계좌 잔액 | |
|
||||
| 최종 차액 | |
|
||||
|
||||
## 7. 결재함 (슬라이드 55-58)
|
||||
|
||||
### 7.1 상태
|
||||
| 상태 | 설명 |
|
||||
|------|------|
|
||||
| 진행 | 결재 하위 상태 |
|
||||
| 예정 | 결재 순번에 의한 대기 |
|
||||
| 결재요청 | 결재 요청을 받은 상태 |
|
||||
|
||||
### 7.2 결재함 현황 카드
|
||||
| 항목 | 설명 |
|
||||
|------|------|
|
||||
| 결재 요청 | 결재 요청 문서 수 |
|
||||
| 완료 | 완료 문서 수 |
|
||||
| 반려 | 반려 문서 수 |
|
||||
| 예정 | 예정 문서 수 |
|
||||
|
||||
### 7.3 승인 버튼
|
||||
- 클릭: "정말 (1)건을 승인하시겠습니까?" 확인 Alert 표시
|
||||
- 확인 버튼 클릭 시 "승인이 완료되었습니다." 알림 Alert 표시
|
||||
|
||||
### 7.4 반려 버튼
|
||||
- 클릭: "정말 (1)건을 반려하시겠습니까?" 확인 Alert 표시
|
||||
- 확인 버튼 클릭 시 "반려가 완료되었습니다." 알림 Alert 표시
|
||||
|
||||
### 7.5 필터 셀렉트 박스
|
||||
- 종류: 전체, 결재 요청, 예정, 완료, 반려
|
||||
- 1/3 완료: 결재선 승인 진행도에 따라 표시
|
||||
- 디폴트: 전체
|
||||
|
||||
### 7.6 수정 버튼
|
||||
- 클릭: 문서 상세 팝업 표시
|
||||
|
||||
## 8. 참조함 (슬라이드 59)
|
||||
|
||||
### 8.1 열람 버튼
|
||||
- 클릭: "정말 (1)건을 열람 처리하시겠습니까?" 확인 Alert 표시
|
||||
- 확인 버튼 클릭 시 "열람 처리가 완료되었습니다." 알림 Alert 표시
|
||||
|
||||
### 8.2 미열람 버튼
|
||||
- 클릭: "정말 (1)건을 미열람 처리하시겠습니까?" 확인 Alert 표시
|
||||
- 확인 버튼 클릭 시 "미열람 처리가 완료되었습니다." 알림 Alert 표시
|
||||
|
||||
### 8.3 필터 셀렉트 박스
|
||||
- 종류: 전체, 열람, 미열람
|
||||
- 디폴트: 전체
|
||||
|
||||
## 9. 문서 상세 팝업 (슬라이드 56-58)
|
||||
|
||||
### 9.1 버튼 영역
|
||||
| 버튼 | 설명 |
|
||||
|------|------|
|
||||
| 복제 | 문서 작성 화면으로 이동 (새글) |
|
||||
| 수정 | 결재선 중에서는 누구나 수정 가능, 해당 문서 작성 화면으로 이동 |
|
||||
| 반려 | 결재선 마감 경우 숨김 |
|
||||
| 승인 | 결재선 마감 경우 숨김 |
|
||||
| 인쇄 | |
|
||||
| 공유 | (5-1) 팝업 표시 |
|
||||
| 닫기 | |
|
||||
|
||||
### 9.2 결재선 영역
|
||||
- 승인/반려 시 해당 아이콘 표시
|
||||
|
||||
### 9.3 공유 버튼
|
||||
| 공유 방식 | 설명 |
|
||||
|-----------|------|
|
||||
| PDF | |
|
||||
| 이메일 | |
|
||||
| 카카오톡 | |
|
||||
|
||||
---
|
||||
|
||||
## 데이터 모델
|
||||
|
||||
### ApprovalDocument (결재 문서)
|
||||
```
|
||||
- id: bigint
|
||||
- tenant_id: bigint (FK)
|
||||
- document_number: string
|
||||
- document_type: enum('request', 'expense', 'expense_estimate')
|
||||
- title: string
|
||||
- status: enum('draft', 'pending', 'in_progress', 'approved', 'rejected')
|
||||
- drafter_id: bigint (FK) # 기안자
|
||||
- content: json # 문서 내용
|
||||
- attachments: json
|
||||
- created_at: timestamp
|
||||
- submitted_at: timestamp
|
||||
```
|
||||
|
||||
### ApprovalLine (결재선)
|
||||
```
|
||||
- id: bigint
|
||||
- document_id: bigint (FK)
|
||||
- approver_id: bigint (FK)
|
||||
- order: int
|
||||
- status: enum('pending', 'approved', 'rejected')
|
||||
- comment: text
|
||||
- approved_at: timestamp
|
||||
```
|
||||
|
||||
### ApprovalReference (참조자)
|
||||
```
|
||||
- id: bigint
|
||||
- document_id: bigint (FK)
|
||||
- referee_id: bigint (FK)
|
||||
- is_read: boolean
|
||||
- read_at: timestamp
|
||||
```
|
||||
|
||||
### ExpenseItem (지출 항목)
|
||||
```
|
||||
- id: bigint
|
||||
- document_id: bigint (FK)
|
||||
- description: string
|
||||
- amount: decimal
|
||||
- note: text
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API 도출
|
||||
|
||||
### 기안함 API
|
||||
```
|
||||
GET /api/approvals/drafts # 기안 목록
|
||||
POST /api/approvals # 문서 작성
|
||||
PUT /api/approvals/{id} # 문서 수정
|
||||
DELETE /api/approvals/{id} # 문서 삭제
|
||||
POST /api/approvals/{id}/submit # 문서 제출 (결재 요청)
|
||||
POST /api/approvals/{id}/save-draft # 임시저장
|
||||
GET /api/approvals/{id} # 문서 상세
|
||||
GET /api/approvals/drafts/summary # 기안함 현황
|
||||
```
|
||||
|
||||
### 결재함 API
|
||||
```
|
||||
GET /api/approvals/inbox # 결재함 목록
|
||||
POST /api/approvals/{id}/approve # 승인
|
||||
POST /api/approvals/{id}/reject # 반려
|
||||
GET /api/approvals/inbox/summary # 결재함 현황
|
||||
```
|
||||
|
||||
### 참조함 API
|
||||
```
|
||||
GET /api/approvals/references # 참조함 목록
|
||||
POST /api/approvals/{id}/mark-read # 열람 처리
|
||||
POST /api/approvals/{id}/mark-unread # 미열람 처리
|
||||
```
|
||||
|
||||
### 문서 공유 API
|
||||
```
|
||||
GET /api/approvals/{id}/pdf # PDF 다운로드
|
||||
POST /api/approvals/{id}/share/email # 이메일 공유
|
||||
POST /api/approvals/{id}/share/kakao # 카카오톡 공유
|
||||
```
|
||||
427
system/erp-analysis/06-accounting.md
Normal file
427
system/erp-analysis/06-accounting.md
Normal file
@@ -0,0 +1,427 @@
|
||||
# 회계관리 (슬라이드 60-91)
|
||||
|
||||
## 1. 개요
|
||||
|
||||
회계관리 모듈은 거래처, 매출, 매입, 입금, 출금, 어음, 거래처원장, 미수금 현황, 입출금 계좌 조회 등을 관리합니다.
|
||||
|
||||
## 2. 회계관리 플로우차트 (슬라이드 61)
|
||||
|
||||
### 2.1 매출 플로우
|
||||
```
|
||||
거래처 선택 → 매출 등록 → 세금계산서 발행
|
||||
```
|
||||
|
||||
### 2.2 입금 플로우
|
||||
```
|
||||
입금 등록 → 전액 입금? → 어음 수취?
|
||||
↓ ↓
|
||||
입출금 계좌 조회 어음관리
|
||||
```
|
||||
|
||||
### 2.3 매입 플로우
|
||||
```
|
||||
거래처 선택 → 매입 등록 → 세금계산서 수취
|
||||
```
|
||||
|
||||
### 2.4 출금 플로우
|
||||
```
|
||||
출금 등록 → 전액 출금? → 어음 발행?
|
||||
↓ ↓
|
||||
입출금 계좌 조회 어음관리
|
||||
```
|
||||
|
||||
### 2.5 추심 플로우
|
||||
```
|
||||
미수금 현황 → 연체? → 악성주심? → 악성 추심
|
||||
↓
|
||||
미지급 알림
|
||||
```
|
||||
|
||||
### 2.6 조회
|
||||
- 입출금 계좌 조회
|
||||
- 카드 내역 조회
|
||||
|
||||
### 2.7 장부/보고서
|
||||
- 거래처원장
|
||||
- 지출 예상 내역서
|
||||
- 일일 일보
|
||||
|
||||
## 3. 거래처관리 (슬라이드 62-65)
|
||||
|
||||
### 3.1 현황 카드
|
||||
| 항목 | 설명 |
|
||||
|------|------|
|
||||
| 전체 거래처 | |
|
||||
| 매출 거래처 | |
|
||||
| 매입 거래처 | |
|
||||
|
||||
### 3.2 삭제 버튼
|
||||
- 관리 권한이 없을 경우 숨김
|
||||
- 클릭: "선택한 거래처 N개를 삭제하시겠습니까?" 확인 Alert 표시
|
||||
- 확인 선택 시 삭제
|
||||
|
||||
### 3.3 구분 필터 셀렉트 박스
|
||||
- 종류: 전체, 매출, 매입, 매입매출
|
||||
- 디폴트: 전체
|
||||
|
||||
### 3.4 신용등급 필터 셀렉트 박스
|
||||
- 종류: 전체, AAA, AA, A, BBB, BB, B, CCC, CC, C, D
|
||||
- 디폴트: 전체
|
||||
|
||||
### 3.5 거래등급 필터 셀렉트 박스
|
||||
- 종류: 전체, A(우수), B(양호), C(보통), D(주의), E(위험)
|
||||
- 디폴트: 전체
|
||||
|
||||
### 3.6 약정체결 필터 셀렉트 박스
|
||||
- 종류: 전체, 약정체결, 정상
|
||||
- 디폴트: 전체
|
||||
|
||||
### 3.7 정렬 셀렉트 박스
|
||||
- 종류: 최신순, 등록순, 거래처명 오름차순, 거래처명 내림차순, 미수금 높은순, 미수금 낮은순
|
||||
- 디폴트: 최신순
|
||||
|
||||
## 4. 거래처 상세 (슬라이드 63-65)
|
||||
|
||||
### 4.1 기본 정보
|
||||
| 필드명 | 필수 | 설명 |
|
||||
|--------|------|------|
|
||||
| 사업자등록번호 | Y | |
|
||||
| 거래처 코드 | N | |
|
||||
| 거래처명 | Y | |
|
||||
| 대표자명 | Y | |
|
||||
| 거래처 유형 | Y | 매출매입 선택 |
|
||||
| 업태 | N | |
|
||||
| 업종 | N | |
|
||||
|
||||
### 4.2 연락처 정보
|
||||
| 필드명 | 필수 | 설명 |
|
||||
|--------|------|------|
|
||||
| 주소 | N | 우편번호 찾기 + 상세주소 |
|
||||
| 전화번호 | N | |
|
||||
| 모바일 | N | |
|
||||
| 팩스 | N | |
|
||||
| 이메일 | N | |
|
||||
|
||||
### 4.3 담당자 정보
|
||||
| 필드명 | 필수 | 설명 |
|
||||
|--------|------|------|
|
||||
| 담당자명 | N | |
|
||||
| 담당자 전화 | N | |
|
||||
|
||||
### 4.4 시스템 관리자
|
||||
| 필드명 | 필수 | 설명 |
|
||||
|--------|------|------|
|
||||
| 관리자명 | N | |
|
||||
|
||||
### 4.5 회사 정보
|
||||
| 필드명 | 필수 | 설명 |
|
||||
|--------|------|------|
|
||||
| 회사 로고 | N | 750x250px, 10MB 이하 PNG, JPEG, GIF |
|
||||
|
||||
### 4.6 결제 정보
|
||||
| 필드명 | 필수 | 설명 |
|
||||
|--------|------|------|
|
||||
| 매입 결제일 | N | 종류: 1일~31일, 말일. 디폴트: 10일. 거래처 유형이 '매입' 또는 '매입매출'일 경우 해당 |
|
||||
| 매출 결제일 | N | 종류: 1일~31일, 말일. 디폴트: 15일. 거래처 유형이 '매출' 또는 '매입매출'일 경우 표시 |
|
||||
|
||||
### 4.7 신용 정보
|
||||
| 필드명 | 필수 | 설명 |
|
||||
|--------|------|------|
|
||||
| 신용등급 | N | 외부 신용평가 등급 표시. 예: AAA, AA, A, BBB, BB, B, CCC, CC, C, D |
|
||||
| 거래등급 | N | 종류: A(우수), B(양호), C(보통), D(주의), E(위험). 디폴트: A(우수). 자사 기준 거래처 평가 등급 |
|
||||
|
||||
### 4.8 계좌 정보
|
||||
| 필드명 | 필수 | 설명 |
|
||||
|--------|------|------|
|
||||
| 세금계산서 이메일 | N | |
|
||||
| 입금계좌 은행 | N | 은행 선택 |
|
||||
| 계좌 | N | |
|
||||
| 예금주 | N | |
|
||||
|
||||
### 4.9 추가 정보
|
||||
| 필드명 | 설명 |
|
||||
|--------|------|
|
||||
| 미수금 | 해당 거래처의 현재 미수금 잠게 표시. 읽기 전용 |
|
||||
| 연체 | - ON: 연체 상태로 표시, 연체일수 표시 |
|
||||
| | - OFF: 정상 상태 |
|
||||
| | - 거래처 상세에서 연체 설정과 연동 |
|
||||
| | - (4-1) 연체 등록 이후부터 경과일 표시 |
|
||||
| 미지급 | 해당 거래처에 대한 미지급금 잠게 표시. 읽기 전용 |
|
||||
| 악성채권 | - ON: 악성채권으로 등록, 악성채권 추심관리 목록에 표시 |
|
||||
| | - OFF: 정상 상태 |
|
||||
| | - 디폴트: OFF |
|
||||
| 메모 | 추가 버튼 클릭 시 목록 최상단에 추가 |
|
||||
|
||||
## 5. 매출관리 (슬라이드 66-71)
|
||||
|
||||
### 5.1 매출 유형
|
||||
- 필드 매출 시 매출 직접 등록 (삭제 가능)
|
||||
- 필드 매출: 용역 매출, 공사 매출, 임대 수익, 기타 매출
|
||||
|
||||
### 5.2 매출 상세
|
||||
| 필드명 | 필수 | 설명 |
|
||||
|--------|------|------|
|
||||
| 매출번호 | - | 자동 채번 |
|
||||
| 매출일 | Y | |
|
||||
| 거래처명 | Y | 거래처 선택 |
|
||||
| 매출 유형 | Y | 선택 |
|
||||
|
||||
### 5.3 품목 정보
|
||||
| 필드명 | 필수 | 설명 |
|
||||
|--------|------|------|
|
||||
| 품목명 | Y | |
|
||||
| 수량 | Y | |
|
||||
| 단가 | Y | |
|
||||
| 공급가액 | - | 자동 계산 |
|
||||
| 부가세 | - | 자동 계산 |
|
||||
| 적요 | N | |
|
||||
|
||||
### 5.4 세금계산서
|
||||
| 필드명 | 설명 |
|
||||
|--------|------|
|
||||
| 세금계산서 발행 | 토글 |
|
||||
|
||||
### 5.5 거래명세서
|
||||
| 필드명 | 설명 |
|
||||
|--------|------|
|
||||
| 거래명세서 | 토글 |
|
||||
|
||||
## 6. 입금관리 (슬라이드 74-76)
|
||||
|
||||
### 6.1 입금 유형
|
||||
- 종류: 매출대금, 선수금, 가수금, 입대수익, 미자수익, 보증금 반환, 차입금, 자본금, 부가세 환급, 기타, 미상정
|
||||
|
||||
### 6.2 입금 상세
|
||||
| 필드명 | 필수 | 설명 |
|
||||
|--------|------|------|
|
||||
| 입금일 | Y | |
|
||||
| 입금계좌 | Y | 국민은행 1234 (계좌명) |
|
||||
| 입금자명 | Y | |
|
||||
| 입금금액 | Y | |
|
||||
| 적요 | N | |
|
||||
| 거래처 | N | 선택 |
|
||||
| 입금 유형 | Y | |
|
||||
|
||||
## 7. 출금관리 (슬라이드 77-79)
|
||||
|
||||
### 7.1 출금 유형
|
||||
- 종류: 매입대금, 선급금, 가지급금, 임대비용, 보증금 지급, 차입금 상환, 배당금 지급, 세금, 공과금, 경비, 4대보험, 급여
|
||||
|
||||
### 7.2 출금 상세
|
||||
| 필드명 | 필수 | 설명 |
|
||||
|--------|------|------|
|
||||
| 출금일 | Y | |
|
||||
| 출금계좌 | Y | |
|
||||
| 받는 분 | Y | |
|
||||
| 출금금액 | Y | |
|
||||
| 적요 | N | |
|
||||
| 거래처 | N | |
|
||||
| 출금 유형 | Y | |
|
||||
|
||||
## 8. 거래처원장 (슬라이드 80)
|
||||
|
||||
### 8.1 현황 카드
|
||||
| 항목 | 설명 |
|
||||
|------|------|
|
||||
| 전기 이월 | |
|
||||
| 매출 | |
|
||||
| 수금 | |
|
||||
| 잔액 | |
|
||||
|
||||
### 8.2 거래처원장 목록
|
||||
- 거래처별 기간별 합계 금액 표시
|
||||
- 클릭: 거래처원장 상세 화면으로 이동
|
||||
|
||||
### 8.3 엑셀 다운로드 버튼
|
||||
- 클릭: 엑셀 파일 다운로드
|
||||
|
||||
## 9. 미수금 현황 (슬라이드 85)
|
||||
|
||||
### 9.1 미수금 현황 목록
|
||||
- 거래처별 월별 미수금 현황 (매출, 입금, 어음, 미수금, 메모)
|
||||
|
||||
### 9.2 수취 어음 등록 시 표시
|
||||
- 회계에는 미반영
|
||||
|
||||
### 9.3 연체 표시
|
||||
- ON: 연체 상태로 표시, 연체일수 표시
|
||||
- OFF: 정상 상태
|
||||
- 거래처 상세에서 연체 설정과 연동
|
||||
|
||||
### 9.4 엑셀 다운로드 버튼
|
||||
|
||||
### 9.5 저장 버튼
|
||||
|
||||
## 10. 입출금 계좌 조회 (슬라이드 90)
|
||||
|
||||
### 10.1 설명
|
||||
- 기준 정보 > 계좌 관리에 등록된 계좌의 자동 입출금 내역 수집
|
||||
|
||||
### 10.2 새로고침 버튼
|
||||
- 클릭: 은행 계좌 입출금 내역 최신 데이터 조회
|
||||
- 바로빌 API 연동 시 실시간 조회
|
||||
|
||||
### 10.3 구분 필터 셀렉트 박스
|
||||
- 종류: 전체, 출금, 입금
|
||||
- 디폴트: 전체
|
||||
|
||||
### 10.4 계정과목 필터 셀렉트 박스, 검색&다중 선택
|
||||
- (2) 선택에 따른 계정과목 목록 표시
|
||||
- 입금 종류: 전체, 매출대금, 선수금, 가수금, 임대수익, 이자수익, 보증금 반환, 차입금, 자본금, 부가세 환급, 기타, 미상정
|
||||
- 출금 종류: 전체, 매입대금, 선급금, 가지급금, 임대비용, 이자비용, 보증금 지급, 차입금 상환, 배당금 지급, 세금, 공과금, 경비, 4대보험, 급여
|
||||
- 디폴트: 전체
|
||||
|
||||
### 10.5 정렬 셀렉트 박스
|
||||
- 종류: 최신순, 등록순, 금액순
|
||||
- 디폴트: 최신순
|
||||
|
||||
### 10.6 수정 버튼
|
||||
- 클릭: 해당 입금/출금 상세 화면으로 이동
|
||||
|
||||
---
|
||||
|
||||
## 데이터 모델
|
||||
|
||||
### Vendor (거래처)
|
||||
```
|
||||
- id: bigint
|
||||
- tenant_id: bigint (FK)
|
||||
- business_number: string
|
||||
- vendor_code: string
|
||||
- name: string
|
||||
- representative: string
|
||||
- type: enum('sales', 'purchase', 'both')
|
||||
- business_type: string
|
||||
- business_category: string
|
||||
- address: string
|
||||
- phone: string
|
||||
- mobile: string
|
||||
- fax: string
|
||||
- email: string
|
||||
- manager_name: string
|
||||
- manager_phone: string
|
||||
- logo: string
|
||||
- purchase_payment_day: int
|
||||
- sales_payment_day: int
|
||||
- credit_rating: enum('AAA', 'AA', 'A', 'BBB', 'BB', 'B', 'CCC', 'CC', 'C', 'D')
|
||||
- trade_rating: enum('A', 'B', 'C', 'D', 'E')
|
||||
- tax_invoice_email: string
|
||||
- bank_code: string
|
||||
- account_number: string
|
||||
- account_holder: string
|
||||
- is_overdue: boolean
|
||||
- overdue_start_date: date
|
||||
- is_bad_debt: boolean
|
||||
- notes: json
|
||||
- created_at: timestamp
|
||||
```
|
||||
|
||||
### Sales (매출)
|
||||
```
|
||||
- id: bigint
|
||||
- tenant_id: bigint (FK)
|
||||
- vendor_id: bigint (FK)
|
||||
- sales_number: string
|
||||
- sales_date: date
|
||||
- sales_type: enum('product', 'service', 'construction', 'rental', 'other')
|
||||
- total_amount: decimal
|
||||
- tax_amount: decimal
|
||||
- is_tax_invoice_issued: boolean
|
||||
- is_statement_issued: boolean
|
||||
- created_at: timestamp
|
||||
```
|
||||
|
||||
### SalesItem (매출 품목)
|
||||
```
|
||||
- id: bigint
|
||||
- sales_id: bigint (FK)
|
||||
- item_name: string
|
||||
- quantity: int
|
||||
- unit_price: decimal
|
||||
- supply_amount: decimal
|
||||
- tax_amount: decimal
|
||||
- description: string
|
||||
```
|
||||
|
||||
### Deposit (입금)
|
||||
```
|
||||
- id: bigint
|
||||
- tenant_id: bigint (FK)
|
||||
- vendor_id: bigint (FK, nullable)
|
||||
- account_id: bigint (FK)
|
||||
- deposit_date: date
|
||||
- depositor_name: string
|
||||
- amount: decimal
|
||||
- description: string
|
||||
- deposit_type: enum('sales', 'advance', 'suspense', 'rental', 'interest', 'deposit_return', 'loan', 'capital', 'vat_refund', 'other', 'unidentified')
|
||||
- created_at: timestamp
|
||||
```
|
||||
|
||||
### Withdrawal (출금)
|
||||
```
|
||||
- id: bigint
|
||||
- tenant_id: bigint (FK)
|
||||
- vendor_id: bigint (FK, nullable)
|
||||
- account_id: bigint (FK)
|
||||
- withdrawal_date: date
|
||||
- recipient_name: string
|
||||
- amount: decimal
|
||||
- description: string
|
||||
- withdrawal_type: enum('purchase', 'advance', 'suspense', 'rental', 'interest', 'deposit', 'loan_repayment', 'dividend', 'tax', 'utility', 'expense', 'insurance', 'salary')
|
||||
- created_at: timestamp
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API 도출
|
||||
|
||||
### 거래처 API
|
||||
```
|
||||
GET /api/vendors # 거래처 목록
|
||||
POST /api/vendors # 거래처 등록
|
||||
GET /api/vendors/{id} # 거래처 상세
|
||||
PUT /api/vendors/{id} # 거래처 수정
|
||||
DELETE /api/vendors/{id} # 거래처 삭제
|
||||
DELETE /api/vendors/bulk # 거래처 일괄 삭제
|
||||
GET /api/vendors/summary # 거래처 현황
|
||||
```
|
||||
|
||||
### 매출 API
|
||||
```
|
||||
GET /api/sales # 매출 목록
|
||||
POST /api/sales # 매출 등록
|
||||
GET /api/sales/{id} # 매출 상세
|
||||
PUT /api/sales/{id} # 매출 수정
|
||||
DELETE /api/sales/{id} # 매출 삭제
|
||||
POST /api/sales/{id}/tax-invoice # 세금계산서 발행
|
||||
GET /api/sales/summary # 매출 현황
|
||||
```
|
||||
|
||||
### 입금 API
|
||||
```
|
||||
GET /api/deposits # 입금 목록
|
||||
POST /api/deposits # 입금 등록
|
||||
GET /api/deposits/{id} # 입금 상세
|
||||
PUT /api/deposits/{id} # 입금 수정
|
||||
DELETE /api/deposits/{id} # 입금 삭제
|
||||
```
|
||||
|
||||
### 출금 API
|
||||
```
|
||||
GET /api/withdrawals # 출금 목록
|
||||
POST /api/withdrawals # 출금 등록
|
||||
GET /api/withdrawals/{id} # 출금 상세
|
||||
PUT /api/withdrawals/{id} # 출금 수정
|
||||
DELETE /api/withdrawals/{id} # 출금 삭제
|
||||
```
|
||||
|
||||
### 장부/보고서 API
|
||||
```
|
||||
GET /api/ledger/vendor # 거래처원장
|
||||
GET /api/ledger/vendor/{vendor_id} # 거래처원장 상세
|
||||
GET /api/ledger/vendor/export # 거래처원장 엑셀 다운로드
|
||||
GET /api/receivables # 미수금 현황
|
||||
GET /api/receivables/export # 미수금 현황 엑셀 다운로드
|
||||
GET /api/bank-transactions # 입출금 계좌 조회
|
||||
POST /api/bank-transactions/sync # 입출금 내역 동기화
|
||||
```
|
||||
332
system/erp-analysis/07-master-data.md
Normal file
332
system/erp-analysis/07-master-data.md
Normal file
@@ -0,0 +1,332 @@
|
||||
# 기준정보 (슬라이드 92-104)
|
||||
|
||||
## 1. 개요
|
||||
|
||||
기준정보 모듈은 직급, 직책, 권한, 근무, 출퇴근, 휴가, 카드, 계좌, 팝업, 게시판, 일반설정, 알림설정을 관리합니다.
|
||||
|
||||
## 2. 직급관리 (슬라이드 93, 95)
|
||||
|
||||
### 2.1 직급 인풋박스
|
||||
- 직급 입력 후 추가 버튼 클릭
|
||||
|
||||
### 2.2 추가 버튼
|
||||
- 클릭: (2-1) 직급 목록 최하단에 표시
|
||||
|
||||
### 2.3 직급 목록
|
||||
- 디폴트: 사원, 대리, 과장, 차장, 부장, 이사, 상무, 전무, 부사장, 사장
|
||||
|
||||
### 2.4 순서 변경 버튼
|
||||
- 드래그&드랍: 해당 위치로 순서 변경
|
||||
|
||||
### 2.5 수정 버튼
|
||||
- 클릭: 직급 수정 팝업 표시
|
||||
|
||||
### 2.6 삭제 버튼
|
||||
- 클릭:
|
||||
1) 해당 직급으로 사원 설정된 경우: "'직급명'을 사용하고 있는 사원이 있습니다. 다 변경 후 삭제가 가능합니다." 알림 Alert 표시
|
||||
2) 해당 직급으로 사원 미설정된 경우: "정말 삭제하시겠습니까?" 확인 Alert 표시, 확인 클릭 시 "삭제가 완료되었습니다." 알림 Alert 표시
|
||||
|
||||
### 2.7 직급 수정 팝업
|
||||
- 직급명 인풋박스: 기존 직급명 표시, 수정 가능
|
||||
|
||||
## 3. 직책관리 (슬라이드 94-95)
|
||||
|
||||
### 3.1 기능
|
||||
- 직급관리와 동일한 구조
|
||||
|
||||
### 3.2 디폴트 직책
|
||||
- (없음)
|
||||
|
||||
### 3.3 직책 수정 팝업
|
||||
- 직책명 인풋박스: 기존 직책명 표시, 수정 가능
|
||||
|
||||
## 4. 권한관리 (슬라이드 96)
|
||||
|
||||
### 4.1 관리자
|
||||
- 디폴트: 모든 메뉴 권한 설정
|
||||
- 수정 불가, 삭제 불가
|
||||
|
||||
### 4.2 일반
|
||||
- 디폴트: 대시보드 제외 모든 메뉴 접근 불가
|
||||
|
||||
### 4.3 추가 버튼
|
||||
- 클릭: 권한 추가 팝업 표시
|
||||
|
||||
### 4.4 삭제 버튼
|
||||
- 클릭:
|
||||
1) 해당 권한으로 사원 설정된 경우: "'권한명'을 사용하고 있는 사원이 있습니다. 다 변경 후 삭제가 가능합니다." 알림 Alert 표시
|
||||
2) 해당 권한으로 사원 미설정된 경우: "정말 삭제하시겠습니까?" 확인 Alert 표시
|
||||
|
||||
### 4.5 체크박스
|
||||
- 클릭: 체크 설정/해제 토글
|
||||
- 디폴트: 체크 해제 상태
|
||||
|
||||
### 4.6 관리
|
||||
- 읽기 및 수정 권한
|
||||
|
||||
### 4.7 읽기
|
||||
- 읽기만 권한
|
||||
|
||||
## 5. 근무관리 (슬라이드 97)
|
||||
|
||||
### 5.1 근무 정보
|
||||
| 필드명 | 설명 |
|
||||
|--------|------|
|
||||
| 근무 유형 | 종류: 고정형, 변형, 맞춤형 |
|
||||
| 기본 소정 근로 시간 | 주 00시간 |
|
||||
| 연장 근로 시간 | 주 00시간 |
|
||||
| 연장 근로 한도 | 52시간 미만 |
|
||||
| 근무 요일 설정 | 클릭: 설정된 체크 활성/비활성 토글, 디폴트: 월~금 활성 |
|
||||
|
||||
### 5.2 출근 시간
|
||||
- 09:00
|
||||
|
||||
### 5.3 퇴근 시간
|
||||
- 18:00
|
||||
|
||||
### 5.4 휴게 시간
|
||||
| 필드명 | 설명 |
|
||||
|--------|------|
|
||||
| 총 휴게 시간 | 1시간 |
|
||||
| 휴게 시간 | 12:00 ~ 13:00 |
|
||||
|
||||
## 6. 출퇴근관리 (슬라이드 99)
|
||||
|
||||
### 6.1 출퇴근 설정
|
||||
| 필드명 | 설명 |
|
||||
|--------|------|
|
||||
| GPS 출퇴근 | 토글: 기준 좌표로 정해진 거리 기준 이내에 있을 때만 출근/퇴근 등록 가능 |
|
||||
| 허용 반경 | 주 00m |
|
||||
|
||||
### 6.2 본사 위치 정보
|
||||
| 필드명 | 설명 |
|
||||
|--------|------|
|
||||
| 본사 주소 | 우편번호 찾기 + 상세주소 |
|
||||
| 경도 | |
|
||||
| 위도 | |
|
||||
|
||||
### 6.3 현장 목록
|
||||
- 추가 버튼: 현장 등록 팝업 표시
|
||||
- 현장명, 주소, 삭제 버튼
|
||||
|
||||
## 7. 휴가관리 (슬라이드 100)
|
||||
|
||||
### 7.1 기준 셀렉트 박스
|
||||
- 종류: 회계연도, 입사일
|
||||
- 디폴트: 회계연도
|
||||
- 입사일 선택 시 (2) 영역 비활성화
|
||||
- 회계연도 기준: 회사의 회계연도를 기준으로 휴가를 부여하고 조회할 수 있습니다.
|
||||
- 입사일 기준: 사원의 입사일 회계연도 기준으로 휴가를 부여하고 조회할 수 있습니다.
|
||||
|
||||
### 7.2 기준일 월/일 설정 영역
|
||||
- 회계연도 기준 시에만 활성화
|
||||
|
||||
### 7.3 기본 연차 설정
|
||||
- 1년간 출근율 80% 이상이면 15일
|
||||
- 3년 이상 근속 시 2년에 1일 추가 (최대 25일)
|
||||
- 1년 미만 또는 출근율 80% 이하인 경우 1일
|
||||
- 입사일~회계연도 기준일 사정: 회년도 출근율로 판정 80% 이상이면 15일
|
||||
- (3-1) 연차+1년+1일 시작, 이후 2년에 1일 추가
|
||||
- 입사일~회계연도 기준으로 전환할 때는 취업규칙 변경, 노사 의견수렴, 전환 시 충북 연차 정산(입사일 기준 vs 회계연도 기준 비교 후 부족분 보전)을 반드시 검토 필요
|
||||
|
||||
## 8. 카드관리 (슬라이드 101-102)
|
||||
|
||||
### 8.1 카드 목록
|
||||
| 필드명 | 설명 |
|
||||
|--------|------|
|
||||
| 카드사 | |
|
||||
| 카드번호 | 앞4자리, 끝4자리 표시 |
|
||||
| 카드명 | |
|
||||
| 상태 | |
|
||||
| 사용자 | 부서명 / 이름 / 직책 |
|
||||
| 작업 | 상세 버튼 |
|
||||
|
||||
### 8.2 필터
|
||||
- 종류: 전체, 사용, 정지
|
||||
- 디폴트: 전체
|
||||
|
||||
### 8.3 카드 상세
|
||||
| 필드명 | 필수 | 설명 |
|
||||
|--------|------|------|
|
||||
| 카드사 | Y | 카드사명 선택 |
|
||||
| 카드번호 | Y | 1234-1234-1234-1234 |
|
||||
| 유효기간 | Y | MM/YY |
|
||||
| 카드 비밀번호 앞 2자리 | Y | 입력 시 마스킹 처리 |
|
||||
| 카드명 | Y | |
|
||||
| 상태 | Y | 종류: 사용, 정지. 정지 시 해당 카드의 자동 조회 중단 |
|
||||
| 사용자 | N | 부서명 / 이름 / 직책. 선택 시 해당 카드의 사용자로 설정 |
|
||||
|
||||
## 9. 계좌관리 (슬라이드 103-104)
|
||||
|
||||
### 9.1 계좌 목록
|
||||
| 필드명 | 설명 |
|
||||
|--------|------|
|
||||
| 은행 | |
|
||||
| 계좌번호 | |
|
||||
| 예금주 | |
|
||||
| 상태 | |
|
||||
| 사용자 | 부서명 / 이름 / 직책 |
|
||||
| 작업 | 상세 버튼 |
|
||||
|
||||
### 9.2 필터
|
||||
- 종류: 전체, 사용, 정지
|
||||
- 디폴트: 전체
|
||||
|
||||
### 9.3 계좌 상세
|
||||
| 필드명 | 필수 | 설명 |
|
||||
|--------|------|------|
|
||||
| 은행 | Y | 은행 선택 |
|
||||
| 계좌번호 | Y | |
|
||||
| 예금주 | Y | |
|
||||
| 계좌명 | Y | |
|
||||
| 상태 | Y | 종류: 사용, 정지. 정지 시 해당 계좌의 자동 조회 중단 |
|
||||
| 사용자 | N | 부서명 / 이름 / 직책. 선택 시 해당 계좌의 사용자로 설정 |
|
||||
|
||||
---
|
||||
|
||||
## 데이터 모델
|
||||
|
||||
### Position (직급)
|
||||
```
|
||||
- id: bigint
|
||||
- tenant_id: bigint (FK)
|
||||
- name: string
|
||||
- order: int
|
||||
- created_at: timestamp
|
||||
```
|
||||
|
||||
### JobTitle (직책)
|
||||
```
|
||||
- id: bigint
|
||||
- tenant_id: bigint (FK)
|
||||
- name: string
|
||||
- order: int
|
||||
- created_at: timestamp
|
||||
```
|
||||
|
||||
### Role (권한)
|
||||
```
|
||||
- id: bigint
|
||||
- tenant_id: bigint (FK)
|
||||
- name: string
|
||||
- is_system: boolean # 시스템 기본 권한 여부
|
||||
- permissions: json # 메뉴별 권한 설정
|
||||
- created_at: timestamp
|
||||
```
|
||||
|
||||
### WorkSetting (근무 설정)
|
||||
```
|
||||
- id: bigint
|
||||
- tenant_id: bigint (FK)
|
||||
- work_type: enum('fixed', 'flexible', 'custom')
|
||||
- standard_hours: int # 주당 소정 근로 시간
|
||||
- overtime_hours: int # 주당 연장 근로 시간
|
||||
- overtime_limit: int # 연장 근로 한도
|
||||
- work_days: json # ['mon', 'tue', 'wed', 'thu', 'fri']
|
||||
- start_time: time
|
||||
- end_time: time
|
||||
- break_hours: int
|
||||
- break_start: time
|
||||
- break_end: time
|
||||
```
|
||||
|
||||
### AttendanceSetting (출퇴근 설정)
|
||||
```
|
||||
- id: bigint
|
||||
- tenant_id: bigint (FK)
|
||||
- use_gps: boolean
|
||||
- allowed_radius: int # 허용 반경 (m)
|
||||
- headquarters_address: string
|
||||
- headquarters_latitude: decimal(10,8)
|
||||
- headquarters_longitude: decimal(11,8)
|
||||
```
|
||||
|
||||
### LeaveSetting (휴가 설정)
|
||||
```
|
||||
- id: bigint
|
||||
- tenant_id: bigint (FK)
|
||||
- base_type: enum('fiscal_year', 'hire_date')
|
||||
- fiscal_start_month: int
|
||||
- fiscal_start_day: int
|
||||
```
|
||||
|
||||
### Card (카드)
|
||||
```
|
||||
- id: bigint
|
||||
- tenant_id: bigint (FK)
|
||||
- card_company: string
|
||||
- card_number: string (encrypted)
|
||||
- expiry_date: string
|
||||
- card_password: string (encrypted)
|
||||
- card_name: string
|
||||
- status: enum('active', 'inactive')
|
||||
- user_id: bigint (FK, nullable)
|
||||
- created_at: timestamp
|
||||
```
|
||||
|
||||
### BankAccount (계좌)
|
||||
```
|
||||
- id: bigint
|
||||
- tenant_id: bigint (FK)
|
||||
- bank_code: string
|
||||
- account_number: string
|
||||
- account_holder: string
|
||||
- account_name: string
|
||||
- status: enum('active', 'inactive')
|
||||
- user_id: bigint (FK, nullable)
|
||||
- created_at: timestamp
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API 도출
|
||||
|
||||
### 직급/직책 API
|
||||
```
|
||||
GET /api/positions # 직급 목록
|
||||
POST /api/positions # 직급 추가
|
||||
PUT /api/positions/{id} # 직급 수정
|
||||
DELETE /api/positions/{id} # 직급 삭제
|
||||
PUT /api/positions/reorder # 직급 순서 변경
|
||||
|
||||
GET /api/job-titles # 직책 목록
|
||||
POST /api/job-titles # 직책 추가
|
||||
PUT /api/job-titles/{id} # 직책 수정
|
||||
DELETE /api/job-titles/{id} # 직책 삭제
|
||||
PUT /api/job-titles/reorder # 직책 순서 변경
|
||||
```
|
||||
|
||||
### 권한 API
|
||||
```
|
||||
GET /api/roles # 권한 목록
|
||||
POST /api/roles # 권한 추가
|
||||
GET /api/roles/{id} # 권한 상세
|
||||
PUT /api/roles/{id} # 권한 수정
|
||||
DELETE /api/roles/{id} # 권한 삭제
|
||||
GET /api/menus # 메뉴 목록 (권한 설정용)
|
||||
```
|
||||
|
||||
### 설정 API
|
||||
```
|
||||
GET /api/settings/work # 근무 설정 조회
|
||||
PUT /api/settings/work # 근무 설정 수정
|
||||
GET /api/settings/attendance # 출퇴근 설정 조회
|
||||
PUT /api/settings/attendance # 출퇴근 설정 수정
|
||||
GET /api/settings/leave # 휴가 설정 조회
|
||||
PUT /api/settings/leave # 휴가 설정 수정
|
||||
```
|
||||
|
||||
### 카드/계좌 API
|
||||
```
|
||||
GET /api/cards # 카드 목록
|
||||
POST /api/cards # 카드 등록
|
||||
GET /api/cards/{id} # 카드 상세
|
||||
PUT /api/cards/{id} # 카드 수정
|
||||
DELETE /api/cards/{id} # 카드 삭제
|
||||
|
||||
GET /api/bank-accounts # 계좌 목록
|
||||
POST /api/bank-accounts # 계좌 등록
|
||||
GET /api/bank-accounts/{id} # 계좌 상세
|
||||
PUT /api/bank-accounts/{id} # 계좌 수정
|
||||
DELETE /api/bank-accounts/{id} # 계좌 삭제
|
||||
```
|
||||
225
system/erp-analysis/08-reports.md
Normal file
225
system/erp-analysis/08-reports.md
Normal file
@@ -0,0 +1,225 @@
|
||||
# 보고서 및 분석 (슬라이드 105-113)
|
||||
|
||||
## 1. 개요
|
||||
|
||||
보고서 및 분석 모듈은 일일 일보, 지출 예상 내역서, 가지급금 인정이자 계산, AI 리포트 생성 등을 제공합니다.
|
||||
|
||||
## 2. 일일 일보 (슬라이드 106-107)
|
||||
|
||||
### 2.1 일일 일보 조회
|
||||
- 매일 전일의 입출금 및 매출 매입 현황 자동 집계
|
||||
|
||||
### 2.2 현황 카드
|
||||
| 항목 | 설명 |
|
||||
|------|------|
|
||||
| 전일 잔액 | 조회 기준일 전일 잔액 |
|
||||
| 당일 입금액 | 전일 입금 합계 |
|
||||
| 당일 출금액 | 전일 출금 합계 |
|
||||
| 당일 잔액 | 조회 기준일 잔액 |
|
||||
|
||||
### 2.3 일일 일보 목록
|
||||
| 필드명 | 설명 |
|
||||
|--------|------|
|
||||
| 구분 | 입금/출금 |
|
||||
| 거래처명 | |
|
||||
| 계정과목 | |
|
||||
| 입금액 | |
|
||||
| 출금액 | |
|
||||
| 적요 | |
|
||||
|
||||
### 2.4 엑셀 다운로드 버튼
|
||||
|
||||
## 3. 지출 예상 내역서 (슬라이드 108-109)
|
||||
|
||||
### 3.1 지출 예상 내역서 조회
|
||||
- 예상 지출 금액 및 일정 조회
|
||||
|
||||
### 3.2 현황 카드
|
||||
| 항목 | 설명 |
|
||||
|------|------|
|
||||
| 예상 지출 합계 | 월별 예상 지출 합계 |
|
||||
| 계좌 잔액 | 현재 계좌 잔액 |
|
||||
| 예상 잔액 | 계좌 잔액 - 예상 지출 합계 |
|
||||
|
||||
### 3.3 지출 예상 내역서 목록
|
||||
| 필드명 | 설명 |
|
||||
|--------|------|
|
||||
| 예상 지급일 | |
|
||||
| 품목 | |
|
||||
| 지출금액 | |
|
||||
| 거래처 | |
|
||||
| 계좌 | |
|
||||
|
||||
### 3.4 엑셀 다운로드 버튼
|
||||
|
||||
### 3.5 월별 합계
|
||||
| 항목 | 설명 |
|
||||
|------|------|
|
||||
| 2025/11 계 | 11월 지출 합계 |
|
||||
| 2025/12 계 | 12월 지출 합계 |
|
||||
| 지출 합계 | 전체 지출 합계 |
|
||||
| 계좌 잔액 | |
|
||||
| 최종 차액 | |
|
||||
|
||||
## 4. 가지급금 인정이자 계산 (슬라이드 110-112)
|
||||
|
||||
### 4.1 가지급금 인정이자 계산 예시 (2024년 기준)
|
||||
- 인정이자율 4.6% (당좌대출이자율 기준, 매년 고시)
|
||||
|
||||
### 4.2 계산 예시
|
||||
| 항목 | 금액 |
|
||||
|------|------|
|
||||
| 가지급금 잔액 | 15,200,000원 |
|
||||
| 인정이자 | 699,200원 |
|
||||
| 법인세 추가 (19%) | 132,848원 |
|
||||
| 대표자 소득세 추가 (35%) | 244,720원 |
|
||||
| 대표자 지방소득세 (10%) | 24,472원 |
|
||||
| **총 세금 부담** | **402,040원** |
|
||||
|
||||
### 4.3 계산식
|
||||
```
|
||||
잔액 × 0.046 = 인정이자
|
||||
인정이자 × 0.19 = 법인세 추가
|
||||
인정이자 × 0.35 = 대표자 소득세 추가
|
||||
```
|
||||
|
||||
### 4.4 기본 정산 공식
|
||||
```
|
||||
정산차액 = 가지급금 총액 - 실사용 총액
|
||||
```
|
||||
|
||||
### 4.5 인정이자 계산 공식 (법인세법 기준)
|
||||
```
|
||||
경과일수 = 정산일 - 지급일
|
||||
일이자율 = 연이자율 ÷ 365
|
||||
인정이자 = 가지급금 × 일이자율 × 경과일수
|
||||
```
|
||||
|
||||
## 5. AI 리포트 생성 (슬라이드 113)
|
||||
|
||||
### 5.1 AI 리포트 생성 프롬프트
|
||||
|
||||
#### 작성 규칙
|
||||
1. 문장은 간결하고 명확하게 작성
|
||||
2. 숫자는 읽기 쉽게 "3,123,000원", "15%" 형식 사용
|
||||
3. 계정과목명, 거래처명은 구체적으로 명시
|
||||
4. 조치가 필요한 경우 구체적인 행동 권한 포함
|
||||
5. 긍정적 변화도 반드시 실상 포함
|
||||
6. 법인세, 소득세 영향이 있는 경우 세무 리스크 명시
|
||||
|
||||
#### 키워드 강조 규칙
|
||||
출력 메시지 내 다음 키워드는 프론트엔드에서 색상 강조됩니다:
|
||||
- **빨간색(경고)**: 초과, 증가, 발생, 필요, 불가
|
||||
- **주황색(주의)**: 점검, 확인, 주의, 검토
|
||||
- **녹색(긍정)**: 감소, 완료, 정상
|
||||
- **파란색(양호)**: 여유, 적정, 양호
|
||||
|
||||
#### 예시 출력
|
||||
입력 데이터 예시에 대한 출력:
|
||||
|
||||
```json
|
||||
{"리포트": [
|
||||
{"영역": "지출분석", "상태": "경고", "메시지": "이번 달 예상 지출이 전월 대비 15% 증가했습니다.", "상세": "매입 비용 증가가 주요 원인입니다."},
|
||||
{"영역": "가지급금", "상태": "주의", "메시지": "50일 이상 잔기 미수금 3건(2,500만원) 발생.", "상세": "회수 조치가 필요합니다."},
|
||||
{"영역": "카드/계좌", "상태": "경고", "메시지": "법인카드 사용 한도 85% 도달, 잔여 한도 600만원입니다.", "상세": "사용 계획을 점검해 주세요."},
|
||||
{"영역": "미수금", "상태": "주의", "메시지": "미수금에 대한 관리가 필요한 상태입니다.", "상세": ""}
|
||||
],
|
||||
"요약": "지출 증가와 정기 미수금에 대한 관리가 필요한 상태입니다."}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 데이터 모델
|
||||
|
||||
### DailyReport (일일 일보)
|
||||
```
|
||||
- id: bigint
|
||||
- tenant_id: bigint (FK)
|
||||
- report_date: date
|
||||
- previous_balance: decimal
|
||||
- daily_deposit: decimal
|
||||
- daily_withdrawal: decimal
|
||||
- current_balance: decimal
|
||||
- details: json # 입출금 상세 내역
|
||||
- created_at: timestamp
|
||||
```
|
||||
|
||||
### ExpenseEstimate (지출 예상 내역서)
|
||||
```
|
||||
- id: bigint
|
||||
- tenant_id: bigint (FK)
|
||||
- expected_date: date
|
||||
- item_name: string
|
||||
- amount: decimal
|
||||
- vendor_id: bigint (FK, nullable)
|
||||
- account_id: bigint (FK, nullable)
|
||||
- created_at: timestamp
|
||||
```
|
||||
|
||||
### LoanInterestCalculation (가지급금 인정이자 계산)
|
||||
```
|
||||
- id: bigint
|
||||
- tenant_id: bigint (FK)
|
||||
- calculation_date: date
|
||||
- loan_balance: decimal
|
||||
- interest_rate: decimal
|
||||
- recognized_interest: decimal
|
||||
- corporate_tax_addition: decimal
|
||||
- income_tax_addition: decimal
|
||||
- local_tax_addition: decimal
|
||||
- total_tax_burden: decimal
|
||||
- created_at: timestamp
|
||||
```
|
||||
|
||||
### AIReport (AI 리포트)
|
||||
```
|
||||
- id: bigint
|
||||
- tenant_id: bigint (FK)
|
||||
- report_date: date
|
||||
- report_type: string
|
||||
- content: json # 리포트 내용
|
||||
- summary: text
|
||||
- created_at: timestamp
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API 도출
|
||||
|
||||
### 일일 일보 API
|
||||
```
|
||||
GET /api/reports/daily # 일일 일보 조회
|
||||
GET /api/reports/daily/export # 일일 일보 엑셀 다운로드
|
||||
```
|
||||
|
||||
### 지출 예상 내역서 API
|
||||
```
|
||||
GET /api/reports/expense-estimate # 지출 예상 내역서 조회
|
||||
POST /api/reports/expense-estimate # 지출 예상 내역 등록
|
||||
PUT /api/reports/expense-estimate/{id}# 지출 예상 내역 수정
|
||||
DELETE /api/reports/expense-estimate/{id}# 지출 예상 내역 삭제
|
||||
GET /api/reports/expense-estimate/export # 지출 예상 내역서 엑셀 다운로드
|
||||
```
|
||||
|
||||
### 가지급금 인정이자 API
|
||||
```
|
||||
GET /api/reports/loan-interest # 가지급금 인정이자 계산 조회
|
||||
POST /api/reports/loan-interest/calculate # 가지급금 인정이자 계산 실행
|
||||
```
|
||||
|
||||
### AI 리포트 API
|
||||
```
|
||||
GET /api/reports/ai # AI 리포트 목록
|
||||
POST /api/reports/ai/generate # AI 리포트 생성
|
||||
GET /api/reports/ai/{id} # AI 리포트 상세
|
||||
DELETE /api/reports/ai/{id} # AI 리포트 삭제
|
||||
```
|
||||
|
||||
### 대시보드/분석 API
|
||||
```
|
||||
GET /api/dashboard/summary # 대시보드 요약
|
||||
GET /api/dashboard/charts # 대시보드 차트 데이터
|
||||
GET /api/analytics/sales # 매출 분석
|
||||
GET /api/analytics/expense # 지출 분석
|
||||
GET /api/analytics/receivables # 미수금 분석
|
||||
```
|
||||
1048
system/erp-analysis/99-gap-analysis.md
Normal file
1048
system/erp-analysis/99-gap-analysis.md
Normal file
File diff suppressed because it is too large
Load Diff
661
system/item-master-integration.md
Normal file
661
system/item-master-integration.md
Normal file
@@ -0,0 +1,661 @@
|
||||
# ItemMaster 연동 설계서
|
||||
|
||||
**작성일**: 2025-12-05
|
||||
**최종 수정**: 2025-12-08
|
||||
**버전**: 1.1
|
||||
**상태**: Draft
|
||||
|
||||
---
|
||||
|
||||
## 1. 개요
|
||||
|
||||
### 1.1 목적
|
||||
품목기준관리(ItemMaster)에서 정의한 필드와 실제 엔티티 데이터를 연동하여, 동적 필드 정의 및 값 저장을 가능하게 한다.
|
||||
|
||||
### 1.2 설계 원칙
|
||||
- **기존 테이블 활용**: 신규 테이블 추가 없이 기존 `attributes` JSON 컬럼 활용
|
||||
- **범용성**: 품목(products, materials) 외에도 다른 엔티티(orders, clients 등) 확장 가능
|
||||
- **성능**: JOIN 없이 단일 쿼리로 조회 가능
|
||||
- **유연성**: 테넌트/그룹별 다른 필드 구성 지원
|
||||
|
||||
---
|
||||
|
||||
## 2. 현재 구조
|
||||
|
||||
### 2.1 ItemMaster 테이블 구조
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ item_pages (페이지 정의) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ id, tenant_id, page_name, item_type, source_table, │
|
||||
│ is_active │
|
||||
│ │
|
||||
│ item_type: FG(완제품), PT(반제품), SM(부자재), │
|
||||
│ RM(원자재), CS(소모품) │
|
||||
│ │
|
||||
│ source_table: 실제 저장 테이블명 │
|
||||
│ - 'products' (FG, PT) │
|
||||
│ - 'materials' (SM, RM, CS) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
│ 1:N
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ item_sections (섹션 정의) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ id, tenant_id, page_id, title, type, order_no │
|
||||
│ │
|
||||
│ type: fields(필드형), bom(BOM형) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
│ 1:N
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ item_fields (필드 정의) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ id, tenant_id, group_id, section_id (nullable) │
|
||||
│ field_key ← attributes JSON 키와 매핑 │
|
||||
│ field_name ← 화면 표시명 │
|
||||
│ field_type ← textbox, number, dropdown, checkbox... │
|
||||
│ is_required ← 필수 여부 │
|
||||
│ default_value ← 기본값 │
|
||||
│ placeholder ← 입력 힌트 │
|
||||
│ validation_rules ← 검증 규칙 JSON │
|
||||
│ options ← 선택 옵션 JSON │
|
||||
│ properties ← 추가 속성 JSON │
|
||||
│ category ← 필드 카테고리 │
|
||||
│ is_common ← 공통 필드 여부 │
|
||||
│ is_active ← 활성 여부 │
|
||||
│ │
|
||||
│ [내부용 매핑 컬럼 - API 응답에서 hidden] │
|
||||
│ source_table ← 원본 테이블명 (products, materials 등) │
|
||||
│ source_column ← 원본 컬럼명 (code, name 등) │
|
||||
│ storage_type ← 저장방식 (column=DB컬럼, json=JSON) │
|
||||
│ json_path ← JSON 저장 경로 (예: attributes.size) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.2 엔티티 테이블 구조
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ products │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ [고정 필드] │
|
||||
│ id, tenant_id, code, name, unit, category_id │
|
||||
│ product_type, is_active, is_sellable, is_purchasable... │
|
||||
│ │
|
||||
│ [동적 필드] │
|
||||
│ attributes JSON ← ItemMaster 필드 값 저장 │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ materials │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ [고정 필드] │
|
||||
│ id, tenant_id, material_code, name, unit, category_id │
|
||||
│ material_type, is_active... │
|
||||
│ │
|
||||
│ [동적 필드] │
|
||||
│ attributes JSON ← ItemMaster 필드 값 저장 │
|
||||
│ options JSON ← 추가 옵션 저장 │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 연동 설계
|
||||
|
||||
### 3.1 매핑 규칙
|
||||
|
||||
```
|
||||
ItemMaster Entity.attributes
|
||||
┌──────────────────────┐ ┌──────────────────────┐
|
||||
│ group_id: 1 │ │ │
|
||||
│ field_key: "color" │ ◀═══매핑═══▶ │ {"color": "빨강"} │
|
||||
│ field_key: "weight" │ ◀═══매핑═══▶ │ {"weight": 1.5} │
|
||||
│ field_key: "spec" │ ◀═══매핑═══▶ │ {"spec": "10x20"} │
|
||||
└──────────────────────┘ └──────────────────────┘
|
||||
|
||||
핵심: item_fields.field_key = attributes JSON의 key
|
||||
```
|
||||
|
||||
### 3.2 Group ID 정의
|
||||
|
||||
| group_id | 엔티티 | 대상 테이블 | 비고 |
|
||||
|----------|--------|-------------|------|
|
||||
| 1 | 품목-제품 | products | product_type: FG, PT |
|
||||
| 2 | 품목-자재 | materials | material_type: SM, RM, CS |
|
||||
| 3 | 주문 | orders | 향후 확장 |
|
||||
| 4 | 고객 | clients | 향후 확장 |
|
||||
| ... | ... | ... | 필요 시 추가 |
|
||||
|
||||
> **참고**: group_id는 `common_codes` 테이블에서 관리하거나, 별도 enum으로 정의 가능
|
||||
|
||||
### 3.3 데이터 흐름
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 1. 관리자: ItemMaster에서 필드 정의 │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ POST /api/v1/item-master/fields │
|
||||
│ { │
|
||||
│ "group_id": 1, │
|
||||
│ "field_key": "color", │
|
||||
│ "field_name": "색상", │
|
||||
│ "field_type": "dropdown", │
|
||||
│ "is_required": true, │
|
||||
│ "options": [ │
|
||||
│ {"label": "빨강", "value": "red"}, │
|
||||
│ {"label": "파랑", "value": "blue"} │
|
||||
│ ] │
|
||||
│ } │
|
||||
│ │
|
||||
│ → item_fields 테이블에 저장 │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 2. 사용자: 품목 등록 화면 진입 │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ GET /api/v1/item-master/fields?group_id=1 │
|
||||
│ │
|
||||
│ → 정의된 필드 목록 반환 │
|
||||
│ → 프론트엔드가 동적 폼 렌더링 │
|
||||
│ │
|
||||
│ ┌────────────────────────────────────┐ │
|
||||
│ │ [색상 ▼] ← dropdown으로 표시 │ │
|
||||
│ │ 빨강 │ │
|
||||
│ │ 파랑 │ │
|
||||
│ └────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 3. 사용자: 품목 저장 │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ POST /api/v1/products │
|
||||
│ { │
|
||||
│ "code": "P-001", ← 고정 필드 │
|
||||
│ "name": "티셔츠", │
|
||||
│ "unit": "EA", │
|
||||
│ "product_type": "FG", │
|
||||
│ "attributes": { ← 동적 필드 │
|
||||
│ "color": "red", (field_key: value) │
|
||||
│ "size": "XL" │
|
||||
│ } │
|
||||
│ } │
|
||||
│ │
|
||||
│ → products 테이블에 저장 │
|
||||
│ → attributes JSON에 동적 필드 값 포함 │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 4. 사용자: 품목 조회 │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ GET /api/v1/products/1 │
|
||||
│ │
|
||||
│ { │
|
||||
│ "id": 1, │
|
||||
│ "code": "P-001", │
|
||||
│ "name": "티셔츠", │
|
||||
│ "attributes": { │
|
||||
│ "color": "red", │
|
||||
│ "size": "XL" │
|
||||
│ } │
|
||||
│ } │
|
||||
│ │
|
||||
│ → JOIN 없이 한 번에 조회! │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. API 설계
|
||||
|
||||
### 4.1 ItemMaster API (기존)
|
||||
|
||||
| Method | Endpoint | 설명 |
|
||||
|--------|----------|------|
|
||||
| GET | `/api/v1/item-master/fields` | 필드 목록 조회 |
|
||||
| GET | `/api/v1/item-master/fields/{id}` | 필드 상세 조회 |
|
||||
| POST | `/api/v1/item-master/fields` | 필드 생성 |
|
||||
| PUT | `/api/v1/item-master/fields/{id}` | 필드 수정 |
|
||||
| DELETE | `/api/v1/item-master/fields/{id}` | 필드 삭제 |
|
||||
|
||||
**필터 파라미터**:
|
||||
- `group_id`: 엔티티 그룹 필터
|
||||
- `section_id`: 섹션 필터
|
||||
- `is_active`: 활성 필터
|
||||
- `is_common`: 공통 필드 필터
|
||||
|
||||
### 4.2 엔티티 API 수정
|
||||
|
||||
#### 4.2.1 Products API
|
||||
|
||||
**저장 시 attributes 포함**:
|
||||
```json
|
||||
POST /api/v1/products
|
||||
{
|
||||
"code": "P-001",
|
||||
"name": "제품명",
|
||||
"unit": "EA",
|
||||
"product_type": "FG",
|
||||
"attributes": {
|
||||
"color": "red",
|
||||
"weight": 1.5,
|
||||
"custom_field": "value"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**조회 시 필드 메타데이터 포함 (선택)**:
|
||||
```
|
||||
GET /api/v1/products/1?include_field_meta=true
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"code": "P-001",
|
||||
"name": "제품명",
|
||||
"attributes": {
|
||||
"color": "red",
|
||||
"weight": 1.5
|
||||
},
|
||||
"field_meta": [
|
||||
{
|
||||
"field_key": "color",
|
||||
"field_name": "색상",
|
||||
"field_type": "dropdown",
|
||||
"value": "red",
|
||||
"options": [...]
|
||||
},
|
||||
{
|
||||
"field_key": "weight",
|
||||
"field_name": "중량",
|
||||
"field_type": "number",
|
||||
"value": 1.5
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 검증 로직
|
||||
|
||||
### 5.1 저장 시 검증 흐름
|
||||
|
||||
```php
|
||||
class ItemFieldValidationService
|
||||
{
|
||||
/**
|
||||
* attributes 값을 ItemMaster 기준으로 검증
|
||||
*/
|
||||
public function validate(int $groupId, array $attributes): array
|
||||
{
|
||||
$errors = [];
|
||||
|
||||
// 1. 해당 그룹의 필드 정의 조회
|
||||
$fields = ItemField::where('group_id', $groupId)
|
||||
->where('is_active', true)
|
||||
->get()
|
||||
->keyBy('field_key');
|
||||
|
||||
// 2. 필수 필드 체크
|
||||
foreach ($fields->where('is_required', true) as $field) {
|
||||
if (!isset($attributes[$field->field_key])) {
|
||||
$errors[$field->field_key] = "{$field->field_name}은(는) 필수입니다.";
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 타입별 검증
|
||||
foreach ($attributes as $key => $value) {
|
||||
if (!$fields->has($key)) {
|
||||
continue; // 정의되지 않은 필드는 스킵 (또는 에러)
|
||||
}
|
||||
|
||||
$field = $fields->get($key);
|
||||
$fieldError = $this->validateFieldValue($field, $value);
|
||||
if ($fieldError) {
|
||||
$errors[$key] = $fieldError;
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* 필드 타입별 값 검증
|
||||
*/
|
||||
private function validateFieldValue(ItemField $field, mixed $value): ?string
|
||||
{
|
||||
return match($field->field_type) {
|
||||
'number' => $this->validateNumber($field, $value),
|
||||
'dropdown' => $this->validateDropdown($field, $value),
|
||||
'date' => $this->validateDate($field, $value),
|
||||
'checkbox' => $this->validateCheckbox($field, $value),
|
||||
default => null
|
||||
};
|
||||
}
|
||||
|
||||
private function validateNumber(ItemField $field, mixed $value): ?string
|
||||
{
|
||||
if (!is_numeric($value)) {
|
||||
return "{$field->field_name}은(는) 숫자여야 합니다.";
|
||||
}
|
||||
|
||||
$rules = $field->validation_rules ?? [];
|
||||
if (isset($rules['min']) && $value < $rules['min']) {
|
||||
return "{$field->field_name}은(는) {$rules['min']} 이상이어야 합니다.";
|
||||
}
|
||||
if (isset($rules['max']) && $value > $rules['max']) {
|
||||
return "{$field->field_name}은(는) {$rules['max']} 이하여야 합니다.";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function validateDropdown(ItemField $field, mixed $value): ?string
|
||||
{
|
||||
$options = $field->options ?? [];
|
||||
$validValues = array_column($options, 'value');
|
||||
|
||||
if (!in_array($value, $validValues)) {
|
||||
return "{$field->field_name}의 값이 유효하지 않습니다.";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 Controller에서 사용
|
||||
|
||||
```php
|
||||
class ProductsController extends Controller
|
||||
{
|
||||
public function store(ProductStoreRequest $request)
|
||||
{
|
||||
$validated = $request->validated();
|
||||
|
||||
// attributes 검증 (선택적)
|
||||
if (isset($validated['attributes'])) {
|
||||
$groupId = 1; // 품목-제품 그룹
|
||||
$errors = $this->fieldValidationService->validate(
|
||||
$groupId,
|
||||
$validated['attributes']
|
||||
);
|
||||
|
||||
if (!empty($errors)) {
|
||||
return ApiResponse::error('검증 실패', $errors, 422);
|
||||
}
|
||||
}
|
||||
|
||||
$product = $this->productService->create($validated);
|
||||
|
||||
return ApiResponse::success($product, __('message.created'));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 프론트엔드 연동
|
||||
|
||||
### 6.1 동적 폼 렌더링 흐름
|
||||
|
||||
```
|
||||
1. 페이지 로드 시
|
||||
GET /api/v1/item-master/fields?group_id=1
|
||||
|
||||
2. 필드 정의 기반 폼 컴포넌트 렌더링
|
||||
field_type: textbox → <Input />
|
||||
field_type: number → <InputNumber />
|
||||
field_type: dropdown → <Select options={field.options} />
|
||||
field_type: checkbox → <Checkbox />
|
||||
field_type: date → <DatePicker />
|
||||
field_type: textarea → <Textarea />
|
||||
|
||||
3. 저장 시 attributes 객체 구성
|
||||
{
|
||||
[field_key]: value,
|
||||
[field_key]: value,
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 React 컴포넌트 예시
|
||||
|
||||
```tsx
|
||||
interface ItemField {
|
||||
id: number;
|
||||
field_key: string;
|
||||
field_name: string;
|
||||
field_type: 'textbox' | 'number' | 'dropdown' | 'checkbox' | 'date' | 'textarea';
|
||||
is_required: boolean;
|
||||
default_value?: string;
|
||||
placeholder?: string;
|
||||
options?: Array<{ label: string; value: string }>;
|
||||
validation_rules?: Record<string, any>;
|
||||
}
|
||||
|
||||
function DynamicFieldRenderer({ field, value, onChange }: Props) {
|
||||
switch (field.field_type) {
|
||||
case 'textbox':
|
||||
return (
|
||||
<Input
|
||||
value={value}
|
||||
onChange={(e) => onChange(field.field_key, e.target.value)}
|
||||
placeholder={field.placeholder}
|
||||
required={field.is_required}
|
||||
/>
|
||||
);
|
||||
|
||||
case 'number':
|
||||
return (
|
||||
<InputNumber
|
||||
value={value}
|
||||
onChange={(val) => onChange(field.field_key, val)}
|
||||
min={field.validation_rules?.min}
|
||||
max={field.validation_rules?.max}
|
||||
required={field.is_required}
|
||||
/>
|
||||
);
|
||||
|
||||
case 'dropdown':
|
||||
return (
|
||||
<Select
|
||||
value={value}
|
||||
onChange={(val) => onChange(field.field_key, val)}
|
||||
options={field.options}
|
||||
required={field.is_required}
|
||||
/>
|
||||
);
|
||||
|
||||
// ... 기타 타입
|
||||
}
|
||||
}
|
||||
|
||||
function ProductForm() {
|
||||
const [fields, setFields] = useState<ItemField[]>([]);
|
||||
const [attributes, setAttributes] = useState<Record<string, any>>({});
|
||||
|
||||
useEffect(() => {
|
||||
// 필드 정의 로드
|
||||
fetch('/api/v1/item-master/fields?group_id=1')
|
||||
.then(res => res.json())
|
||||
.then(data => setFields(data.data));
|
||||
}, []);
|
||||
|
||||
const handleFieldChange = (key: string, value: any) => {
|
||||
setAttributes(prev => ({ ...prev, [key]: value }));
|
||||
};
|
||||
|
||||
return (
|
||||
<form>
|
||||
{/* 고정 필드 */}
|
||||
<Input name="code" label="품목코드" required />
|
||||
<Input name="name" label="품목명" required />
|
||||
|
||||
{/* 동적 필드 */}
|
||||
{fields.map(field => (
|
||||
<DynamicFieldRenderer
|
||||
key={field.id}
|
||||
field={field}
|
||||
value={attributes[field.field_key]}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
))}
|
||||
</form>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 확장 가이드
|
||||
|
||||
### 7.1 새 엔티티 추가 시
|
||||
|
||||
1. **group_id 정의**: 새 그룹 ID 할당
|
||||
2. **테이블 확인**: `attributes` JSON 컬럼 존재 확인 (없으면 추가)
|
||||
3. **ItemMaster 필드 정의**: 해당 group_id로 필드 생성
|
||||
4. **API 수정**: 저장/조회 시 attributes 처리 로직 추가
|
||||
|
||||
### 7.2 예시: 주문(orders) 연동
|
||||
|
||||
```sql
|
||||
-- 1. orders 테이블에 attributes 컬럼 추가 (없는 경우)
|
||||
ALTER TABLE orders ADD COLUMN attributes JSON DEFAULT NULL COMMENT '동적 필드';
|
||||
|
||||
-- 2. ItemMaster에 주문용 필드 정의
|
||||
INSERT INTO item_fields (tenant_id, group_id, field_key, field_name, field_type, ...)
|
||||
VALUES (1, 3, 'urgency', '긴급도', 'dropdown', ...);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 모델 헬퍼 메서드
|
||||
|
||||
### 8.1 ItemPage 모델
|
||||
|
||||
```php
|
||||
class ItemPage extends Model
|
||||
{
|
||||
/**
|
||||
* source_table에 해당하는 모델 클래스명 반환
|
||||
*/
|
||||
public function getTargetModelClass(): ?string
|
||||
{
|
||||
$mapping = [
|
||||
'products' => \App\Models\Product::class,
|
||||
'materials' => \App\Models\Material::class,
|
||||
];
|
||||
return $mapping[$this->source_table] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 제품 페이지인지 확인
|
||||
*/
|
||||
public function isProductPage(): bool
|
||||
{
|
||||
return $this->source_table === 'products';
|
||||
}
|
||||
|
||||
/**
|
||||
* 자재 페이지인지 확인
|
||||
*/
|
||||
public function isMaterialPage(): bool
|
||||
{
|
||||
return $this->source_table === 'materials';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 8.2 ItemField 모델
|
||||
|
||||
```php
|
||||
class ItemField extends Model
|
||||
{
|
||||
/**
|
||||
* 시스템 필드 여부 확인 (DB 컬럼과 매핑된 필드)
|
||||
*/
|
||||
public function isSystemField(): bool
|
||||
{
|
||||
return !is_null($this->source_table) && !is_null($this->source_column);
|
||||
}
|
||||
|
||||
/**
|
||||
* 컬럼 저장 방식 여부 확인
|
||||
*/
|
||||
public function isColumnStorage(): bool
|
||||
{
|
||||
return $this->storage_type === 'column';
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON 저장 방식 여부 확인
|
||||
*/
|
||||
public function isJsonStorage(): bool
|
||||
{
|
||||
return $this->storage_type === 'json';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 8.3 필드 저장 방식 판단
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ storage_type 판단 로직 │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ if (source_table && source_column) { │
|
||||
│ // 시스템 필드 (기존 DB 컬럼과 매핑) │
|
||||
│ if (storage_type === 'column') { │
|
||||
│ → products.{source_column} 또는 │
|
||||
│ materials.{source_column} 에서 직접 읽기/쓰기 │
|
||||
│ } else if (storage_type === 'json') { │
|
||||
│ → {json_path} 경로로 JSON 내 읽기/쓰기 │
|
||||
│ } │
|
||||
│ } else { │
|
||||
│ // 커스텀 필드 (동적 정의) │
|
||||
│ → attributes.{field_key} 에 저장 │
|
||||
│ } │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 구현 계획
|
||||
|
||||
| 순서 | 작업 | 담당 | 예상 공수 |
|
||||
|------|------|------|----------|
|
||||
| 1 | group_id 코드 정의 | BE | 0.5일 |
|
||||
| 2 | ItemFieldValidationService 구현 | BE | 1일 |
|
||||
| 3 | ProductsController 수정 (검증 연동) | BE | 0.5일 |
|
||||
| 4 | MaterialsController 수정 (검증 연동) | BE | 0.5일 |
|
||||
| 5 | API 응답에 field_meta 포함 옵션 | BE | 0.5일 |
|
||||
| 6 | DynamicFieldRenderer 컴포넌트 | FE | 2일 |
|
||||
| 7 | 품목 등록/수정 폼 연동 | FE | 1일 |
|
||||
| 8 | 테스트 및 QA | 공통 | 1일 |
|
||||
|
||||
**총 예상 공수: 7일**
|
||||
|
||||
---
|
||||
|
||||
## 10. 변경 이력
|
||||
|
||||
| 날짜 | 버전 | 변경 내용 | 작성자 |
|
||||
|------|------|----------|--------|
|
||||
| 2025-12-05 | 1.0 | 최초 작성 | - |
|
||||
| 2025-12-08 | 1.1 | source_table/source_column 매핑 컬럼 추가, 모델 헬퍼 메서드 문서화 | - |
|
||||
137
system/mng-structure.md
Normal file
137
system/mng-structure.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# MNG 관리자 패널 구조 현황
|
||||
|
||||
> **최종 갱신**: 2026-02-27
|
||||
> **기술 스택**: Laravel 12 + HTMX 2 + DaisyUI 5 + Tailwind 3
|
||||
|
||||
---
|
||||
|
||||
## 1. 프로젝트 규모
|
||||
|
||||
| 항목 | 수량 |
|
||||
|------|------|
|
||||
| 컨트롤러 | 171 |
|
||||
| 블레이드 템플릿 | 436 |
|
||||
| 모델 | 185 |
|
||||
| 서비스 | 98 |
|
||||
| FormRequest | 37 |
|
||||
| 미들웨어 | 4 |
|
||||
| Console Commands | 4 |
|
||||
| Traits | 4 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 디렉토리 구조
|
||||
|
||||
```
|
||||
app/
|
||||
├── Http/Controllers/
|
||||
│ ├── Api/Admin/ 관리자 API (바로빌, HR, PM, 견적)
|
||||
│ ├── Auth/ 인증
|
||||
│ ├── Barobill/ 바로빌 연동
|
||||
│ ├── Credit/ 신용조회
|
||||
│ ├── DevTools/ API Explorer, Flow Tester
|
||||
│ ├── ESign/ 전자서명
|
||||
│ ├── Finance/ 재무
|
||||
│ ├── HR/ 인사
|
||||
│ ├── Lab/ 실험/테스트
|
||||
│ ├── Sales/ 영업
|
||||
│ ├── Stats/ 통계
|
||||
│ ├── System/ 시스템 설정
|
||||
│ └── Video/ 영상/튜토리얼
|
||||
├── Models/ api/ 모델과 별도 (동일 DB 사용)
|
||||
│ ├── Admin/ PM 관리 (프로젝트, 태스크, 이슈)
|
||||
│ ├── Barobill/ 바로빌
|
||||
│ ├── Commons/ 공통 (파일, 메뉴)
|
||||
│ ├── Documents/ 문서
|
||||
│ ├── Equipment/ 설비
|
||||
│ ├── ESign/ 전자서명
|
||||
│ ├── Finance/ 재무
|
||||
│ ├── HR/ 인사
|
||||
│ ├── Items/ 품목
|
||||
│ ├── Quote/ 견적
|
||||
│ ├── Sales/ 영업
|
||||
│ ├── Stats/ 통계
|
||||
│ └── Tenants/ 테넌트
|
||||
├── Services/ (98개)
|
||||
│ ├── ApiExplorer/ API 탐색기
|
||||
│ ├── Barobill/ 바로빌 연동
|
||||
│ ├── FlowTester/ API 플로우 테스터
|
||||
│ ├── HR/ 인사
|
||||
│ ├── ProjectManagement/ PM
|
||||
│ ├── Quote/ 견적
|
||||
│ ├── Sales/ 영업
|
||||
│ └── Video/ 영상
|
||||
└── Traits/
|
||||
├── BelongsToTenant
|
||||
├── HasTenantFilter
|
||||
├── ModelTrait
|
||||
└── UppercaseAttributes
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 뷰(블레이드) 도메인 구조
|
||||
|
||||
| 도메인 | 주요 뷰 디렉토리 | 기능 |
|
||||
|--------|-----------------|------|
|
||||
| barobill | bank-account, billing, card-usage, etax, hometax, kakaotalk, sms | 바로빌 연동 전체 |
|
||||
| finance | accounts, fund-schedules, settlement, sales-commission | 재무/회계 |
|
||||
| hr | attendances, employees, leaves, payrolls, employee-tenure | 인사관리 |
|
||||
| sales | admin-prospects, business-cards, dashboard, interviews, products, prospects | 영업관리 |
|
||||
| project-management | projects, tasks, issues | 프로젝트 관리 |
|
||||
| dev-tools | api-explorer, flow-tester | 개발 도구 |
|
||||
| system | ai-config, ai-token-usage, alerts, holidays | 시스템 설정 |
|
||||
| equipment | inspections, repairs | 설비관리 |
|
||||
| esign | sign/ | 전자서명 |
|
||||
| video | tutorial, veo3 | 영상 관리 |
|
||||
| quote-formulas | categories | 견적 공식 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 아키텍처 패턴
|
||||
|
||||
### 프론트엔드
|
||||
- **HTMX 2**: SPA 프레임워크 없이 서버 렌더링 + 부분 업데이트
|
||||
- **DaisyUI 5** + Tailwind 3: UI 컴포넌트 + 스타일
|
||||
- **Axios**: AJAX 호출 (HTMX 보조)
|
||||
- **Vite 7**: 빌드 도구
|
||||
|
||||
### 백엔드
|
||||
- **Service-First**: 비즈니스 로직 → Service 클래스
|
||||
- **BelongsToTenant**: Multi-tenant 데이터 격리
|
||||
- **FormRequest**: 입력 검증 (37개)
|
||||
- **api와 동일 DB**: `samdb` 공유, 자체 모델 보유
|
||||
|
||||
### api ↔ mng 관계
|
||||
- 동일 MySQL DB (`samdb`) 공유
|
||||
- 각자 독립적인 모델 보유 (api: 205개, mng: 185개)
|
||||
- **DB 마이그레이션은 api에서만** — mng에 migrations 없음
|
||||
- 내부 통신: HMAC 기반 `INTERNAL_EXCHANGE_SECRET`
|
||||
- 파일 공유: mng가 api의 `storage/app/tenants` 마운트
|
||||
|
||||
### 주요 기능 (mng 전용)
|
||||
- **API Explorer**: API 엔드포인트 탐색/테스트 도구
|
||||
- **Flow Tester**: API 호출 시나리오 테스트
|
||||
- **바로빌 관리**: 세금계산서, 카드매입, 홈택스 연동
|
||||
- **프로젝트 관리**: PM 도구 (프로젝트, 태스크, 이슈, 일일 로그)
|
||||
- **영상 관리**: 튜토리얼, Veo3 영상 생성
|
||||
- **AI 설정**: AI 토큰 사용량, 음성 녹음, 가격 설정
|
||||
|
||||
---
|
||||
|
||||
## 5. 라우트
|
||||
|
||||
| 파일 | 설명 |
|
||||
|------|------|
|
||||
| `routes/web.php` | 메인 웹 라우트 (Blade 뷰) |
|
||||
| `routes/api.php` | 내부 API (mng 전용) |
|
||||
| `routes/console.php` | Artisan 커맨드 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 도메인
|
||||
|
||||
| 도메인 | URL |
|
||||
|--------|-----|
|
||||
| `mng.sam.kr` | 메인 관리자 |
|
||||
| `admin.sam.kr` | 동일 서버 (nginx alias) |
|
||||
159
system/overview.md
Normal file
159
system/overview.md
Normal file
@@ -0,0 +1,159 @@
|
||||
# SAM 시스템 개요
|
||||
|
||||
> **최종 갱신**: 2026-02-27
|
||||
> **상태**: Phase 1-B 작성
|
||||
|
||||
---
|
||||
|
||||
## 1. 프로젝트 개요
|
||||
|
||||
**SAM** (Smart Automation Management) — 블라인드/스크린 제조업체용 ERP/MES 통합 시스템
|
||||
|
||||
| 항목 | 내용 |
|
||||
|------|------|
|
||||
| 서비스명 | SAM |
|
||||
| 회사명 | (주)코드브릿지엑스 |
|
||||
| 아키텍처 | Multi-tenant (tenant_id 기반 데이터 격리) |
|
||||
| DB | MySQL 8.0 (단일 DB `samdb`, 통계 `sam_stat`) |
|
||||
| 레거시 | 5130.co.kr (PHP 7.3) → SAM으로 마이그레이션 중 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 어플리케이션 구조
|
||||
|
||||
```
|
||||
SAM/
|
||||
├── api/ Laravel 12 REST API (PHP 8.4)
|
||||
├── mng/ Laravel 12 관리자 패널 (PHP 8.4, HTMX + DaisyUI)
|
||||
├── react/ Next.js 15 사용자 프론트엔드 (React 19)
|
||||
├── design/ 디자인 시스템 (Vite, Storybook)
|
||||
├── 5130/ 레거시 시스템 (PHP 7.3)
|
||||
├── sales/ 영업자 사이트 (추후 개발)
|
||||
├── docs/ 기술 문서
|
||||
├── planning/ 기획 문서
|
||||
└── docker/ Docker 설정
|
||||
```
|
||||
|
||||
### 앱별 역할
|
||||
|
||||
| 앱 | 역할 | 기술 스택 | Git 저장소 |
|
||||
|----|------|----------|-----------|
|
||||
| **api** | REST API 서버, DB 마이그레이션 관리 | Laravel 12, PHP 8.4, Sanctum, Swagger | 독립 |
|
||||
| **mng** | 관리자 패널 (admin.sam.kr 포함) | Laravel 12, PHP 8.4, HTMX, DaisyUI, Vite 7 | 독립 |
|
||||
| **react** | 사용자 프론트엔드 | Next.js 15, React 19, Tailwind v4, Zustand | 독립 |
|
||||
| **design** | 디자인 시스템/컴포넌트 | Vite | 독립 |
|
||||
|
||||
### 앱 간 관계
|
||||
|
||||
```
|
||||
react (dev.sam.kr) ──API 호출──→ api (api.sam.kr) ←──DB 공유──→ mng (mng.sam.kr)
|
||||
│
|
||||
MySQL (samdb)
|
||||
│
|
||||
5130 (chandj DB)
|
||||
```
|
||||
|
||||
- **api**: DB 마이그레이션 유일 관리자. 모든 테이블 정의는 api에서만.
|
||||
- **mng**: 자체 모델 보유 (185개), DB 마이그레이션 없음. api와 동일 DB 사용.
|
||||
- **react**: Server Actions로 api 호출. DB 직접 접근 없음.
|
||||
|
||||
---
|
||||
|
||||
## 3. 기술 스택 상세
|
||||
|
||||
### api/ (REST API)
|
||||
|
||||
| 항목 | 상세 |
|
||||
|------|------|
|
||||
| Framework | Laravel 12 |
|
||||
| PHP | 8.4+ |
|
||||
| 인증 | Sanctum (Access 120분, Refresh 7일) |
|
||||
| 권한 | Spatie Permission (RBAC) |
|
||||
| API 문서 | L5-Swagger (OpenAPI) |
|
||||
| 큐 | Database Driver |
|
||||
| 캐시 | Database Driver |
|
||||
| 규모 | 모델 205, 서비스 179, 컨트롤러 131, FormRequest 271 |
|
||||
| 마이그레이션 | 458개 (메인 437 + 통계 21) |
|
||||
| API 도메인 | 18개 라우트 파일, ~876 엔드포인트 |
|
||||
|
||||
### react/ (프론트엔드)
|
||||
|
||||
| 항목 | 상세 |
|
||||
|------|------|
|
||||
| Framework | Next.js 15 (App Router, Turbopack) |
|
||||
| React | 19 |
|
||||
| 스타일 | Tailwind CSS v4 |
|
||||
| UI | shadcn/ui (Radix 기반) 55개 컴포넌트 |
|
||||
| 상태관리 | Zustand 5 (13 stores) |
|
||||
| 폼 | React Hook Form + Zod |
|
||||
| i18n | next-intl (ko, en, ja) |
|
||||
| 차트 | Recharts |
|
||||
| PDF | jspdf + Puppeteer |
|
||||
| 규모 | 페이지 249, 컴포넌트 612, Server Actions 91 |
|
||||
|
||||
### mng/ (관리자 패널)
|
||||
|
||||
| 항목 | 상세 |
|
||||
|------|------|
|
||||
| Framework | Laravel 12 (Blade + HTMX) |
|
||||
| PHP | 8.4+ |
|
||||
| 프론트 | HTMX 2 + DaisyUI 5 + Tailwind 3 |
|
||||
| 빌드 | Vite 7 |
|
||||
| PDF | TCPDF + FPDI |
|
||||
| Excel | PhpSpreadsheet |
|
||||
| 규모 | 컨트롤러 171, 블레이드 436, 모델 185, 서비스 98 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 핵심 아키텍처 패턴
|
||||
|
||||
### Multi-tenancy
|
||||
- 모든 테넌트 데이터는 `tenant_id`로 격리
|
||||
- `BelongsToTenant` 트레이트/스코프 필수
|
||||
- 글로벌 테이블 (users, plans 등)은 예외
|
||||
|
||||
### Service-First
|
||||
- 비즈니스 로직은 반드시 Service 클래스에
|
||||
- Controller는 라우팅 + FormRequest 검증만 담당
|
||||
- `ApiResponse::handle()` 통일 응답 포맷
|
||||
|
||||
### API 인증
|
||||
- 글로벌: API Key (모든 요청)
|
||||
- 사용자: Sanctum Bearer Token
|
||||
- 내부: HMAC (mng ↔ api 내부 통신)
|
||||
|
||||
### FormRequest
|
||||
- Controller에서 직접 검증 금지
|
||||
- 모든 입력 검증은 FormRequest 클래스에서
|
||||
|
||||
---
|
||||
|
||||
## 5. 환경 구성
|
||||
|
||||
| 환경 | 도메인 | 위치 | 비고 |
|
||||
|------|--------|------|------|
|
||||
| **로컬** | *.sam.kr | Docker (macOS) | 개발용 |
|
||||
| **개발** | codebridge-x.com | 114.203.209.83 | react만 |
|
||||
| **스테이지+운영** | TBD | 211.117.60.189 | api, mng, react |
|
||||
|
||||
### 로컬 도메인
|
||||
|
||||
| 도메인 | 서비스 | 포트 |
|
||||
|--------|--------|------|
|
||||
| `dev.sam.kr` | react (Next.js) | 3000 |
|
||||
| `api.sam.kr` | api (Laravel) | 9000 (FastCGI) |
|
||||
| `mng.sam.kr` | mng (Laravel) | 9000 (FastCGI) |
|
||||
| `admin.sam.kr` | mng (동일) | 9000 (FastCGI) |
|
||||
| `design.sam.kr` | design (Vite) | 3002 |
|
||||
| `5130.sam.kr` | 5130 (레거시) | 9000 (FastCGI) |
|
||||
|
||||
---
|
||||
|
||||
## 관련 문서
|
||||
|
||||
- **DB 스키마**: [database/README.md](database/README.md)
|
||||
- **API 구조**: [api-structure.md](api-structure.md)
|
||||
- **React 구조**: [react-structure.md](react-structure.md)
|
||||
- **MNG 구조**: [mng-structure.md](mng-structure.md)
|
||||
- **Docker 설정**: [docker-setup.md](docker-setup.md)
|
||||
- **보안 정책**: [security-policy.md](security-policy.md)
|
||||
140
system/react-structure.md
Normal file
140
system/react-structure.md
Normal file
@@ -0,0 +1,140 @@
|
||||
# React 프론트엔드 구조 현황
|
||||
|
||||
> **최종 갱신**: 2026-02-27
|
||||
> **기술 스택**: Next.js 15 + React 19 + Tailwind v4 + Zustand 5
|
||||
|
||||
---
|
||||
|
||||
## 1. 프로젝트 규모
|
||||
|
||||
| 항목 | 수량 |
|
||||
|------|------|
|
||||
| 페이지 (page.tsx) | 249 |
|
||||
| Server Actions (actions.ts) | 91 |
|
||||
| 컴포넌트 (.tsx) | 612 |
|
||||
| Custom Hooks | 25 |
|
||||
| Zustand Stores | 13 |
|
||||
| Lib/유틸리티 | 70 |
|
||||
| TypeScript 타입 | 7 |
|
||||
| React Context | 6 |
|
||||
| i18n 언어 | 3 (ko, en, ja) |
|
||||
|
||||
---
|
||||
|
||||
## 2. 디렉토리 구조
|
||||
|
||||
```
|
||||
src/
|
||||
├── app/[locale]/
|
||||
│ ├── (protected)/ 인증 필요 라우트
|
||||
│ │ ├── accounting/ 회계 (32p)
|
||||
│ │ ├── approval/ 전자결재 (5p)
|
||||
│ │ ├── board/ 게시판 (12p)
|
||||
│ │ ├── company-info/ 회사정보
|
||||
│ │ ├── construction/ 시공 (57p)
|
||||
│ │ ├── customer-center/ 고객센터 (10p)
|
||||
│ │ ├── dashboard/ 대시보드 (5p)
|
||||
│ │ ├── hr/ 인사 (16p)
|
||||
│ │ ├── master-data/ 기준정보 (14p)
|
||||
│ │ ├── material/ 자재 (6p)
|
||||
│ │ ├── outbound/ 출고 (7p)
|
||||
│ │ ├── production/ 생산 (12p)
|
||||
│ │ ├── quality/ 품질 (6p)
|
||||
│ │ ├── reports/ 리포트 (2p)
|
||||
│ │ ├── sales/ 영업 (20p)
|
||||
│ │ ├── settings/ 설정 (20p)
|
||||
│ │ └── vehicle-management/ 차량 (12p)
|
||||
│ ├── login/ 로그인 (공개)
|
||||
│ └── signup/ 회원가입 (공개)
|
||||
├── components/
|
||||
│ ├── ui/ shadcn/ui 프리미티브 (55)
|
||||
│ ├── atoms/ Atomic Design - 원자 (3)
|
||||
│ ├── molecules/ Atomic Design - 분자 (11)
|
||||
│ ├── organisms/ Atomic Design - 유기체 (12)
|
||||
│ ├── templates/ 페이지 템플릿 (13)
|
||||
│ └── [domain]/ 도메인별 컴포넌트 (~532)
|
||||
├── hooks/ 커스텀 훅 (25)
|
||||
├── stores/ Zustand 스토어 (13)
|
||||
├── lib/ 유틸리티, API 레이어 (70)
|
||||
├── types/ TypeScript 타입 (7)
|
||||
├── contexts/ React Context (6)
|
||||
├── constants/ 상수
|
||||
├── i18n/ next-intl 설정
|
||||
├── layouts/ 레이아웃
|
||||
├── messages/ i18n JSON (ko, en, ja)
|
||||
└── middleware.ts 인증 + i18n 미들웨어
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 도메인별 페이지 수
|
||||
|
||||
| 도메인 | 페이지 | 주요 기능 |
|
||||
|--------|--------|----------|
|
||||
| construction | 57 | 시공관리, 계약, 인수인계, 구조검토 |
|
||||
| accounting | 32 | 매입/매출, 급여, 대출, 경비 |
|
||||
| sales | 20 | 견적, 수주, 납품, 입찰, 단가 |
|
||||
| settings | 20 | 사용자, 권한, 메뉴, 기본설정 |
|
||||
| hr | 16 | 직원, 근태, 휴가, 급여 |
|
||||
| master-data | 14 | 품목, BOM, 분류 |
|
||||
| board | 12 | 게시판, 공지, 자유게시판 |
|
||||
| vehicle-management | 12 | 법인차량, 운행일지, 정비 |
|
||||
| production | 12 | 작업지시, 공정, 검사 |
|
||||
| customer-center | 10 | 거래처, 현장, 브리핑 |
|
||||
| outbound | 7 | 출고, 물류 |
|
||||
| quality | 6 | 수입검사, LOT, 품질 |
|
||||
| material | 6 | 자재입고, 재고 |
|
||||
| dashboard | 5 | 대시보드 (4종) |
|
||||
| approval | 5 | 전자결재 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 아키텍처 패턴
|
||||
|
||||
### API 연동
|
||||
- **Server Actions** (`actions.ts`): 페이지와 co-located
|
||||
- 공유 유틸: `executeServerAction()`, `executePaginatedAction()`, `buildApiUrl()`
|
||||
- 모든 API 호출은 서버 사이드에서 실행 (클라이언트 직접 호출 없음)
|
||||
|
||||
### 인증
|
||||
- HttpOnly 쿠키 기반 (Next.js API Route를 통한 프록시)
|
||||
- `NEXT_PUBLIC_AUTH_MODE=sanctum`
|
||||
- 모든 페이지 `'use client'` (Server Component 미사용)
|
||||
|
||||
### 상태관리
|
||||
- **글로벌**: Zustand (13 stores) + Immer
|
||||
- **폼**: React Hook Form + Zod
|
||||
- **서버 상태**: Server Actions (SWR/React Query 미사용)
|
||||
|
||||
### 컴포넌트 아키텍처
|
||||
- **Atomic Design** 적용: atoms → molecules → organisms → templates → pages
|
||||
- **shadcn/ui** 55개 프리미티브 컴포넌트
|
||||
- **도메인별 폴더**: `components/[domain]/` (~532개)
|
||||
|
||||
### 주요 의존성
|
||||
|
||||
| 패키지 | 버전 | 용도 |
|
||||
|--------|------|------|
|
||||
| next | 15.5.9 | 프레임워크 |
|
||||
| react | 19.2.3 | UI |
|
||||
| tailwindcss | 4.x | 스타일 |
|
||||
| zustand | 5.0.9 | 상태관리 |
|
||||
| zod | 4.1.12 | 스키마 검증 |
|
||||
| react-hook-form | 7.66.0 | 폼 |
|
||||
| recharts | 3.4.1 | 차트 |
|
||||
| next-intl | 4.4.0 | i18n |
|
||||
| @tiptap/* | - | 리치 텍스트 |
|
||||
| @capacitor/core | 8.0.0 | 모바일 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 빌드 설정
|
||||
|
||||
| 설정 | 값 |
|
||||
|------|------|
|
||||
| Turbopack | 활성화 |
|
||||
| reactStrictMode | false (중복 API 요청 방지) |
|
||||
| serverActions.bodySizeLimit | 10mb |
|
||||
| Puppeteer | serverExternalPackages로 분리 |
|
||||
| PostCSS | @tailwindcss/postcss (v4 방식) |
|
||||
| Path Alias | `@/*` → `./src/*` |
|
||||
248
system/remote-work-setup.md
Normal file
248
system/remote-work-setup.md
Normal file
@@ -0,0 +1,248 @@
|
||||
# 다른 장소에서 작업 환경 구축 가이드
|
||||
|
||||
> ⚠️ **DEPRECATED**: 이 문서는 2025-09-19에 작성되었으며, 현재 프로젝트 구조와 맞지 않습니다.
|
||||
> 최신 정보는 `docs/system/overview.md` 및 `docs/system/docker-setup.md`를 참조하세요.
|
||||
|
||||
**생성일**: 2025-09-19 21:50 KST
|
||||
**목적**: 다른 장소에서 동일한 개발 환경으로 작업 재개
|
||||
**상태**: ⚠️ DEPRECATED (2025-12-26)
|
||||
|
||||
## 🚀 빠른 시작 (5분 내 완료)
|
||||
|
||||
### 1단계: 저장소 최신 동기화
|
||||
```bash
|
||||
# 모든 저장소에서 실행
|
||||
git pull origin develop
|
||||
|
||||
# 예상 결과: "Already up to date" 또는 새로운 커밋 다운로드
|
||||
```
|
||||
|
||||
### 2단계: Docker 서비스 실행
|
||||
```bash
|
||||
# Docker 데스크톱 실행 또는
|
||||
docker-compose up -d
|
||||
|
||||
# 확인
|
||||
docker ps # MySQL, Redis 등 실행 확인
|
||||
```
|
||||
|
||||
### 3단계: API 서버 상태 확인
|
||||
```bash
|
||||
cd api
|
||||
php artisan migrate:status # DB 상태 확인
|
||||
php artisan serve # 개발 서버 실행
|
||||
```
|
||||
|
||||
### 4단계: 환경 검증
|
||||
- 브라우저에서 `http://localhost:8000` 접속 확인
|
||||
- 데이터베이스 연결 상태 확인
|
||||
|
||||
## 📋 상세 환경 구축 절차
|
||||
|
||||
### Git 저장소 상태 확인
|
||||
```bash
|
||||
# 각 저장소에서 실행
|
||||
cd /path/to/SAM/api
|
||||
git status && git log --oneline -3
|
||||
|
||||
cd /path/to/SAM/front/www
|
||||
git status && git log --oneline -3
|
||||
|
||||
cd /path/to/SAM/admin
|
||||
git status && git log --oneline -3
|
||||
|
||||
cd /path/to/SAM/shared
|
||||
git status && git log --oneline -3
|
||||
```
|
||||
|
||||
**기대값**:
|
||||
- **API**: `3f30c5d` - 프로젝트 체크포인트 및 완전한 문서화 시스템 구축
|
||||
- **Frontend**: `ec18d70` - 화면 생성 - 수주관리
|
||||
- **Admin**: `0624422` - 빈디렉토리 설정
|
||||
- **Shared**: `015b3dc` - Filament BOARD, TENANT 추가
|
||||
|
||||
### 환경 파일 확인
|
||||
```bash
|
||||
cd api
|
||||
ls -la .env # API 환경 설정
|
||||
cat .env | grep DB_ # 데이터베이스 설정 확인
|
||||
|
||||
cd ../admin
|
||||
ls -la .env # Admin 환경 설정
|
||||
|
||||
cd ../front/www
|
||||
ls -la application/config/database.php # CodeIgniter DB 설정
|
||||
```
|
||||
|
||||
### 데이터베이스 상태 검증
|
||||
```bash
|
||||
cd api
|
||||
php artisan migrate:status
|
||||
|
||||
# 예상 결과: Batch 11까지 실행됨
|
||||
# 최종 마이그레이션: 2025_09_11_000100_create_audit_logs_table
|
||||
```
|
||||
|
||||
### 의존성 설치 (필요시)
|
||||
```bash
|
||||
# API 저장소
|
||||
cd api
|
||||
composer install # PHP 의존성
|
||||
npm install # Node.js 의존성
|
||||
|
||||
# Admin 저장소
|
||||
cd ../admin
|
||||
composer install
|
||||
npm install
|
||||
|
||||
# Frontend 저장소
|
||||
cd ../front/www
|
||||
composer install
|
||||
```
|
||||
|
||||
## 🔍 문제 해결 가이드
|
||||
|
||||
### Docker 연결 문제
|
||||
```bash
|
||||
# Docker 상태 확인
|
||||
docker ps
|
||||
|
||||
# MySQL 컨테이너 재시작
|
||||
docker-compose restart mysql
|
||||
|
||||
# 전체 재시작
|
||||
docker-compose down && docker-compose up -d
|
||||
```
|
||||
|
||||
### 데이터베이스 연결 실패
|
||||
```bash
|
||||
# 연결 테스트
|
||||
cd api
|
||||
php artisan tinker
|
||||
> DB::select('SHOW DATABASES');
|
||||
|
||||
# 마이그레이션 재실행 (필요시)
|
||||
php artisan migrate
|
||||
```
|
||||
|
||||
### Git 동기화 문제
|
||||
```bash
|
||||
# 충돌 발생시
|
||||
git stash # 로컬 변경사항 저장
|
||||
git pull origin develop # 최신 코드 받기
|
||||
git stash pop # 로컬 변경사항 복원
|
||||
|
||||
# 강제 동기화 (주의: 로컬 변경사항 손실)
|
||||
git reset --hard origin/develop
|
||||
```
|
||||
|
||||
### 권한 문제 (macOS/Linux)
|
||||
```bash
|
||||
# 로그 디렉토리 권한
|
||||
chmod -R 775 storage/
|
||||
chmod -R 775 bootstrap/cache/
|
||||
|
||||
# Composer 캐시 정리
|
||||
composer clear-cache
|
||||
```
|
||||
|
||||
## 📄 참조 문서들
|
||||
|
||||
### 프로젝트 가이드
|
||||
- **`CLAUDE.md`**: 전체 프로젝트 구조 및 워크플로우
|
||||
- **`CURRENT_WORKS.md`**: 최근 작업 현황 및 변경사항
|
||||
- **`CHECKPOINT_2025-09-19.md`**: 복원 지점 및 복구 방법
|
||||
- **`DATABASE_SCHEMA_2025-09-19.md`**: DB 스키마 상세 분석
|
||||
|
||||
### 개발 명령어
|
||||
```bash
|
||||
# API 개발 서버 실행
|
||||
cd api && php artisan serve
|
||||
|
||||
# 프론트엔드 개발 서버
|
||||
cd front/www && php -S localhost:8080
|
||||
|
||||
# Admin 개발 서버
|
||||
cd admin && php artisan serve --port=8001
|
||||
|
||||
# 전체 서비스 실행 (API)
|
||||
cd api && composer dev # Laravel + Queue + Log viewer + Vite
|
||||
```
|
||||
|
||||
## ⚡ 성능 최적화 팁
|
||||
|
||||
### IDE 설정
|
||||
```bash
|
||||
# PHPStorm
|
||||
cd api
|
||||
php artisan ide-helper:generate
|
||||
php artisan ide-helper:models
|
||||
|
||||
# VS Code
|
||||
# - PHP Intelephense 확장 설치
|
||||
# - Laravel Extension Pack 설치
|
||||
```
|
||||
|
||||
### 개발 도구
|
||||
```bash
|
||||
# API 문서 확인
|
||||
open http://localhost:8000/api-docs
|
||||
|
||||
# 로그 실시간 모니터링
|
||||
cd api && php artisan pail --timeout=0
|
||||
|
||||
# 큐 워커 실행
|
||||
cd api && php artisan queue:listen --tries=1
|
||||
```
|
||||
|
||||
## 🔄 작업 완료 후 동기화
|
||||
|
||||
### 세션 종료 시
|
||||
```bash
|
||||
# 1. 모든 변경사항 커밋
|
||||
git add . && git commit -m "작업 내용 설명"
|
||||
|
||||
# 2. 원격 저장소에 푸시
|
||||
git push origin develop
|
||||
|
||||
# 3. 작업 내용 문서화
|
||||
# CURRENT_WORKS.md 업데이트
|
||||
|
||||
# 4. 임시 파일 정리
|
||||
find . -name ".DS_Store" -delete
|
||||
rm -f storage/logs/laravel.log
|
||||
```
|
||||
|
||||
### 체크포인트 생성 (중요 작업 후)
|
||||
```bash
|
||||
# 새로운 체크포인트 파일 생성
|
||||
cp CHECKPOINT_2025-09-19.md CHECKPOINT_$(date +%Y-%m-%d).md
|
||||
|
||||
# 현재 상태로 업데이트
|
||||
# - Git 커밋 해시 업데이트
|
||||
# - 마이그레이션 상태 업데이트
|
||||
# - 변경사항 문서화
|
||||
```
|
||||
|
||||
## 🆘 긴급 복원 (문제 발생시)
|
||||
|
||||
### 완전 복원
|
||||
```bash
|
||||
# CHECKPOINT_2025-09-19.md 파일 참조
|
||||
# 1. 데이터베이스 롤백
|
||||
cd api && php artisan migrate:rollback --step=7
|
||||
|
||||
# 2. Git 리셋
|
||||
git reset --hard 3f30c5d # API
|
||||
cd ../front/www && git reset --hard ec18d70
|
||||
cd ../../admin && git reset --hard 0624422
|
||||
cd ../shared && git reset --hard 015b3dc
|
||||
|
||||
# 3. 마이그레이션 재실행
|
||||
cd ../api && php artisan migrate
|
||||
```
|
||||
|
||||
---
|
||||
**가이드 작성**: Claude Code
|
||||
**검증 완료**: ✅ 모든 단계 테스트됨
|
||||
**업데이트**: 새로운 환경에서 문제 발생시 이 문서 개선 필요
|
||||
214
system/scaling-roadmap.md
Normal file
214
system/scaling-roadmap.md
Normal file
@@ -0,0 +1,214 @@
|
||||
# SAM 10,000 테넌트 스케일링 로드맵
|
||||
|
||||
> **작성일**: 2026-02-22
|
||||
> **성격**: 가상 시나리오 — 세계 최고 수준 엔지니어링 팀이 설계한다는 가정
|
||||
|
||||
---
|
||||
|
||||
## 1. 현재 상태 진단
|
||||
|
||||
### 1.1 현재 아키텍처 요약
|
||||
|
||||
```
|
||||
브라우저 → Nginx → PHP-FPM(20 workers) → Laravel → MySQL 8.0 (단일)
|
||||
서버: 2코어/3.8GB RAM | 배포: 수동 git pull | 모니터링: 없음 | 캐시: 없음
|
||||
```
|
||||
|
||||
### 1.2 핵심 병목 지점
|
||||
|
||||
| 영역 | 현재 | 10,000 테넌트 시 문제 | 심각도 |
|
||||
|------|------|---------------------|--------|
|
||||
| DB | MySQL 단일 | 커넥션 폭발, 슬로우 쿼리 | 치명적 |
|
||||
| 컴퓨팅 | FPM 20워커 | 동시 요청 20개 제한 | 치명적 |
|
||||
| 캐시 | 없음 | 모든 요청이 DB 직행 | 치명적 |
|
||||
| 큐 | DB 드라이버 | 큐 자체가 DB 압박 | 심각 |
|
||||
| 검색 | SQL LIKE | 219개 테이블 풀스캔 | 심각 |
|
||||
| 배포 | 수동 git pull | 다운타임, 롤백 불가 | 높음 |
|
||||
| 모니터링 | 없음 | 장애 인지 불가 | 높음 |
|
||||
|
||||
### 1.3 가장 먼저 죽는 곳
|
||||
|
||||
- 동시 사용자 **20명** → FPM 워커 전부 점유 → 504 Timeout
|
||||
- 동시 사용자 **200명** → MySQL `max_connections`(151) 고갈
|
||||
- 테넌트 **1,000개** → 권한 UNION 3개 쿼리가 매 요청마다 실행
|
||||
|
||||
---
|
||||
|
||||
## 2. 5단계 로드맵 개요
|
||||
|
||||
```
|
||||
Phase 1 (0~3개월) 캐시 + 모니터링 + 서버 업그레이드 → 100 테넌트
|
||||
Phase 2 (3~6개월) DB 복제 + K8s + S3 + 검색 엔진 → 1,000 테넌트
|
||||
Phase 3 (6~9개월) 마이크로서비스 + 이벤트 아키텍처 → 3,000 테넌트
|
||||
Phase 4 (9~12개월) DB 샤딩 + 테넌트 티어링 + 멀티리전 → 10,000 테넌트
|
||||
Phase 5 (12~18개월) 관측성 고도화 + 카오스 엔지니어링 → 10,000+ 테넌트
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Phase 1: 기초 체력 (0~3개월) → 100 테넌트
|
||||
|
||||
### 3.1 Redis 도입 (최우선)
|
||||
|
||||
| 대상 | 캐시 TTL | 효과 |
|
||||
|------|---------|------|
|
||||
| 세션 저장소 | 2시간 | DB 세션 테이블 부하 제거 |
|
||||
| 권한 캐시 | 5분 | UNION 3개 쿼리 제거 (매 요청) |
|
||||
| 메뉴 트리 | 10분 | 중첩 쿼리 제거 |
|
||||
| 공통 코드 | 1시간 | `common_codes` 반복 조회 제거 |
|
||||
| Laravel 큐 | - | `database` → `redis` 드라이버 전환 |
|
||||
|
||||
**예상 효과**: DB 쿼리 **60~70% 감소**, 응답 시간 **3~5배 개선**.
|
||||
|
||||
### 3.2 모니터링 구축
|
||||
|
||||
Grafana + Prometheus + Laravel Telescope + MySQL slow_log.
|
||||
|
||||
**핵심 알림**: FPM 워커 사용률 >80%, MySQL 커넥션 >100, 응답 시간 >2초, 큐 적체 >1000건 → Slack 알림.
|
||||
|
||||
### 3.3 서버 업그레이드 + CI/CD
|
||||
|
||||
| 항목 | 현재 | Phase 1 |
|
||||
|------|------|---------|
|
||||
| CPU/RAM | 2코어/3.8GB | 8코어/32GB |
|
||||
| 스토리지 | HDD | NVMe SSD |
|
||||
| FPM | 20 워커 | 100 워커 |
|
||||
| 배포 | 수동 git pull | Jenkins CI/CD (무중단 rolling) |
|
||||
|
||||
---
|
||||
|
||||
## 4. Phase 2: 수평 확장 (3~6개월) → 1,000 테넌트
|
||||
|
||||
### 4.1 아키텍처
|
||||
|
||||
```
|
||||
브라우저 → LB(L7) → App Server ×N → Redis Cluster
|
||||
→ MySQL Primary + Replica ×2
|
||||
→ S3 + CDN (정적/업로드 파일)
|
||||
```
|
||||
|
||||
### 4.2 핵심 변경
|
||||
|
||||
| 영역 | 변경 | 효과 |
|
||||
|------|------|------|
|
||||
| **K8s** | HPA 기반 오토스케일링 (API 3~10 pods) | 트래픽에 따라 자동 확장 |
|
||||
| **DB R/W 분리** | `config/database.php`에 read/write 설정 | 읽기 부하 80% Replica로 분산 |
|
||||
| **파일 → S3** | Laravel Filesystem → S3 + CloudFront | 서버 간 파일 공유, CDN 가속 |
|
||||
| **검색 엔진** | SQL LIKE → Meilisearch | 밀리초 응답, 형태소 분석, 오타 허용 |
|
||||
|
||||
---
|
||||
|
||||
## 5. Phase 3: 마이크로서비스 (6~9개월) → 3,000 테넌트
|
||||
|
||||
### 5.1 서비스 분리
|
||||
|
||||
```
|
||||
모놀리스(sam-api) → Auth | Product | Order | MES | Finance | Notification
|
||||
```
|
||||
|
||||
**분리 순서**: ① 알림 (독립적, 비동기) → ② MES (변경 빈도 높음) → ③ 인증 (가용성 최우선)
|
||||
|
||||
### 5.2 이벤트 기반 전환
|
||||
|
||||
동기 호출 체인을 이벤트 발행으로 전환한다. 주문 생성 시 재고 차감/알림/회계를 비동기 처리.
|
||||
|
||||
**메시지 브로커**: Redis Streams 또는 RabbitMQ. 응답 시간 2초 → 200ms.
|
||||
|
||||
### 5.3 API Gateway
|
||||
|
||||
Kong/Traefik으로 테넌트별 Rate Limiting, 인증 검증, 요청 라우팅, 응답 캐싱 통합.
|
||||
|
||||
---
|
||||
|
||||
## 6. Phase 4: 대규모 멀티테넌시 (9~12개월) → 10,000 테넌트
|
||||
|
||||
### 6.1 DB 샤딩
|
||||
|
||||
```
|
||||
Shard Router (tenant_id 기반) → Shard 0 | Shard 1 | Shard 2 ...
|
||||
각 Shard = Primary + Replica
|
||||
```
|
||||
|
||||
`tenant_id`가 파티션 키이므로 크로스 샤드 조인 불필요 — SAM의 강점.
|
||||
|
||||
### 6.2 테넌트 티어링
|
||||
|
||||
| Tier | 자원 | SLA | Rate Limit |
|
||||
|------|------|-----|-----------|
|
||||
| Enterprise | 전용 DB/Redis/Pod | 99.99% | 1000/분 |
|
||||
| Business | 공유 (높은 우선순위) | 99.9% | 300/분 |
|
||||
| Standard | 공유 | 99.5% | 60/분 |
|
||||
|
||||
### 6.3 멀티리전
|
||||
|
||||
한국(Primary) + 일본/동남아(Secondary) 리전. DB 복제 + Global Load Balancer로 지리적 라우팅.
|
||||
|
||||
---
|
||||
|
||||
## 7. Phase 5: 엔터프라이즈 성숙 (12~18개월) → 10,000+
|
||||
|
||||
| 영역 | 도입 | 목적 |
|
||||
|------|------|------|
|
||||
| **관측성** | Jaeger(분산추적) + Loki(중앙로그) + PagerDuty | 전체 서비스 체인 추적 |
|
||||
| **카오스 엔지니어링** | DB 다운, Pod Kill, 네트워크 지연 주입 | 복원력 검증 |
|
||||
| **데이터 파이프라인** | CDC(Debezium) → 데이터 웨어하우스 | 운영 DB에서 분석 쿼리 분리 |
|
||||
| **배포** | Blue-Green + Canary (5% → 100%) | 30초 이내 롤백 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 기술 스택 진화 요약
|
||||
|
||||
| 영역 | 현재 | Phase 1 | Phase 2 | Phase 4 |
|
||||
|------|------|---------|---------|---------|
|
||||
| 서버 | 단일 2코어 | 단일 8코어 | K8s 3+ 노드 | 멀티리전 |
|
||||
| DB | MySQL 단일 | + 쿼리 최적화 | Primary + Replica ×2 | Shard ×N |
|
||||
| 캐시 | 없음 | Redis 단일 | Redis Cluster | 테넌트별 격리 |
|
||||
| 큐 | DB | Redis | Redis | 티어별 큐 |
|
||||
| 배포 | 수동 | Jenkins CI/CD | K8s Rolling | Canary |
|
||||
| 모니터링 | 없음 | Grafana | + Telescope | + 카오스 |
|
||||
|
||||
---
|
||||
|
||||
## 9. 가장 중요한 3가지
|
||||
|
||||
**1. Redis (=산소)**: 캐시 없이 10,000 테넌트는 **절대 불가능**. 권한 UNION 쿼리 3개 + 메뉴 + 세션이 매 요청마다 실행된다. Redis 하나로 DB 부하 60% 감소.
|
||||
|
||||
**2. DB R/W 분리 (=심장)**: ERP 읽기:쓰기 = 8:2. Replica 2대 추가로 DB 부하 1/3 분산. Laravel `config/database.php` 변경만으로 적용.
|
||||
|
||||
**3. 관측성 (=눈)**: 모니터링 없이 스케일링은 눈 감고 운전하는 것. 슬로우 쿼리 + 응답 시간 + 알림부터 시작.
|
||||
|
||||
---
|
||||
|
||||
## 10. SAM 특수 고려사항
|
||||
|
||||
### 10.1 tenant_id 기반 격리
|
||||
|
||||
- **강점**: `BelongsToTenant` 스코프 일관 적용, 크로스 테넌트 조인 불필요 → 샤딩에 유리
|
||||
- **개선 필요**: `tenant_id` 인덱스 첫 번째 컬럼 여부 전수 검사, `deleted_at` 누적 데이터 파티셔닝
|
||||
|
||||
### 10.2 권한 시스템 최적화
|
||||
|
||||
3중 UNION 쿼리가 매 요청 실행된다. 개선: ① Redis 권한 캐시(TTL 5분) → ② 변경 시 해당 사용자 캐시만 무효화 → ③ JWT 클레임에 권한 포함(DB 조회 0회).
|
||||
|
||||
### 10.3 219개 테이블 샤딩 분류
|
||||
|
||||
| 분류 | 예시 | 처리 |
|
||||
|------|------|------|
|
||||
| 테넌트 데이터 | orders, products | 샤딩 대상 |
|
||||
| 시스템 공통 | permissions, common_codes | 공유 DB 유지 |
|
||||
| 감사 로그 | audit_logs | 별도 시계열 DB |
|
||||
|
||||
---
|
||||
|
||||
## 관련 문서
|
||||
|
||||
| 문서 | 설명 |
|
||||
|------|------|
|
||||
| [overview.md](overview.md) | 현재 시스템 아키텍처 |
|
||||
| [security-policy.md](security-policy.md) | 현재 보안 구조 |
|
||||
| [docker-setup.md](docker-setup.md) | 현재 Docker 구성 |
|
||||
| [server-how-it-works.md](../guides/server-how-it-works.md) | 서버 동작 원리 |
|
||||
|
||||
---
|
||||
|
||||
**최종 업데이트**: 2026-02-22
|
||||
784
system/security-policy.md
Normal file
784
system/security-policy.md
Normal file
@@ -0,0 +1,784 @@
|
||||
# SAM API 보안 가이드
|
||||
|
||||
## 개요
|
||||
|
||||
SAM API는 다층 보안 구조를 통해 무단 접근과 악의적 공격으로부터 시스템을 보호합니다.
|
||||
|
||||
**최종 업데이트:** 2025-12-26
|
||||
|
||||
---
|
||||
|
||||
## 보안 아키텍처
|
||||
|
||||
### 다층 방어 구조 (Defense in Depth)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Layer 1: Nginx (L7 Application Layer) │
|
||||
│ - 악의적 경로 패턴 차단 │
|
||||
│ - 의심스러운 User-Agent 차단 │
|
||||
│ - Rate Limiting (Nginx 레벨) │
|
||||
└─────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Layer 2: Laravel Rate Limiting │
|
||||
│ - IP 기반 속도 제한 (10회/분) │
|
||||
│ - API Key 없는 요청 차단 │
|
||||
└─────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Layer 3: API Key 검증 (글로벌 미들웨어) │
|
||||
│ - 모든 요청 API Key 필수 │
|
||||
│ - 화이트리스트 라우트 제외 │
|
||||
│ - 보안 로그 자동 기록 │
|
||||
└─────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Layer 4: Sanctum 토큰 인증 │
|
||||
│ - Bearer 토큰 검증 │
|
||||
│ - 사용자 컨텍스트 설정 │
|
||||
│ - 테넌트 격리 │
|
||||
└─────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Layer 5: 권한 검증 (Permission Check) │
|
||||
│ - 메뉴 기반 권한 체크 │
|
||||
│ - Role 기반 접근 제어 │
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Layer 1: Nginx 보안
|
||||
|
||||
### 악의적 경로 패턴 차단
|
||||
|
||||
**위치:** `docker/nginx/nginx.conf`
|
||||
|
||||
```nginx
|
||||
# 경로 탐색 공격 차단
|
||||
if ($request_uri ~* "(\.\.\/|\.\.\\|etc\/passwd|\.env|\.git|\.htaccess|\.sql|@fs\/)") {
|
||||
return 403;
|
||||
}
|
||||
```
|
||||
|
||||
**차단 패턴:**
|
||||
- `../`, `..\` - 디렉토리 탐색 공격
|
||||
- `etc/passwd` - 시스템 파일 접근 시도
|
||||
- `.env` - 환경 변수 파일 접근
|
||||
- `.git`, `.htaccess`, `.sql` - 민감한 파일 접근
|
||||
- `@fs/` - Vite 경로 탐색 공격
|
||||
|
||||
**응답:** 403 Forbidden
|
||||
|
||||
### 의심스러운 User-Agent 차단
|
||||
|
||||
```nginx
|
||||
# 보안 스캔 도구 차단
|
||||
if ($http_user_agent ~* "(sqlmap|nikto|nmap|masscan|metasploit|nessus)") {
|
||||
return 403;
|
||||
}
|
||||
```
|
||||
|
||||
**차단 도구:**
|
||||
- sqlmap - SQL 인젝션 스캐너
|
||||
- nikto - 웹 서버 스캐너
|
||||
- nmap - 포트 스캐너
|
||||
- masscan - 대량 포트 스캐너
|
||||
- metasploit - 침투 테스트 프레임워크
|
||||
- nessus - 취약점 스캐너
|
||||
|
||||
**응답:** 403 Forbidden
|
||||
|
||||
---
|
||||
|
||||
## Layer 2: Rate Limiting
|
||||
|
||||
### Laravel Rate Limiter
|
||||
|
||||
**위치:** `app/Http/Middleware/ApiRateLimiter.php`
|
||||
|
||||
```php
|
||||
// IP 기반 속도 제한
|
||||
$key = 'api-key-attempts:' . $request->ip();
|
||||
|
||||
if ($this->limiter->tooManyAttempts($key, 10)) {
|
||||
return response()->json([
|
||||
'message' => 'Too many attempts. Please try again later.',
|
||||
'retry_after' => $seconds,
|
||||
], 429);
|
||||
}
|
||||
|
||||
$this->limiter->hit($key, 60); // 1분 동안 유지
|
||||
```
|
||||
|
||||
**설정:**
|
||||
- **제한:** 10회/분 (IP별)
|
||||
- **대상:** API Key 없는 요청
|
||||
- **유지 시간:** 60초
|
||||
- **응답 코드:** 429 Too Many Requests
|
||||
|
||||
**로그:**
|
||||
```php
|
||||
Log::warning('API Rate Limit Exceeded', [
|
||||
'ip' => $request->ip(),
|
||||
'uri' => $request->getRequestUri(),
|
||||
'retry_after' => $seconds,
|
||||
]);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Layer 3: API Key 인증
|
||||
|
||||
### 글로벌 미들웨어
|
||||
|
||||
**위치:** `bootstrap/app.php`
|
||||
|
||||
```php
|
||||
$middleware->append(ApiRateLimiter::class); // 1. Rate Limiting
|
||||
$middleware->append(ApiKeyMiddleware::class); // 2. API Key 검증
|
||||
```
|
||||
|
||||
**실행 순서:** Rate Limiting → API Key 검증
|
||||
|
||||
### API Key 검증 로직
|
||||
|
||||
**위치:** `app/Http/Middleware/ApiKeyMiddleware.php`
|
||||
|
||||
```php
|
||||
// 1. 화이트리스트 체크
|
||||
$publicRoutes = [
|
||||
'api/v1/login',
|
||||
'api/v1/signup',
|
||||
'api/v1/register',
|
||||
'api/v1/refresh',
|
||||
'api/v1/debug-apikey',
|
||||
'api-docs', // Swagger UI
|
||||
'api-docs/*', // Swagger 하위 경로
|
||||
'docs/api-docs.json', // Swagger JSON
|
||||
'up', // Health check
|
||||
];
|
||||
|
||||
// 2. API Key 검증
|
||||
$apiKey = $request->header('X-API-KEY');
|
||||
$validApiKey = DB::table('api_keys')
|
||||
->where('key', $apiKey)
|
||||
->where('is_active', true)
|
||||
->exists();
|
||||
|
||||
// 3. 보안 로그 기록
|
||||
if (!$validApiKey) {
|
||||
Log::warning('Unauthorized API access attempt', [
|
||||
'ip' => $request->ip(),
|
||||
'uri' => $request->getRequestUri(),
|
||||
'method' => $request->method(),
|
||||
'user_agent' => $request->userAgent(),
|
||||
]);
|
||||
}
|
||||
```
|
||||
|
||||
### 화이트리스트 (인증 제외 라우트)
|
||||
|
||||
**공개 엔드포인트:**
|
||||
- `api/v1/login` - 로그인
|
||||
- `api/v1/signup` - 회원가입
|
||||
- `api/v1/register` - 테넌트 등록
|
||||
- `api/v1/refresh` - 토큰 갱신
|
||||
- `api/v1/debug-apikey` - API Key 디버깅
|
||||
- `api-docs/*` - Swagger UI
|
||||
- `docs/api-docs.json` - Swagger JSON
|
||||
- `up` - Health check
|
||||
|
||||
**특징:**
|
||||
- 와일드카드 지원 (`fnmatch()` 사용)
|
||||
- 공개 라우트는 로깅 제외
|
||||
- API Key 검증 스킵
|
||||
|
||||
### 보안 로그
|
||||
|
||||
**로그 레벨:**
|
||||
- **Log::info** - 정상 API 요청
|
||||
- **Log::warning** - 무단 접근 시도
|
||||
|
||||
**로그 내용:**
|
||||
```json
|
||||
{
|
||||
"ip": "213.136.76.215",
|
||||
"uri": "/@fs/etc/passwd",
|
||||
"method": "GET",
|
||||
"user_agent": "Mozilla/5.0 ..."
|
||||
}
|
||||
```
|
||||
|
||||
**민감 정보 제외:**
|
||||
- `password`
|
||||
- `password_confirmation`
|
||||
|
||||
---
|
||||
|
||||
## Layer 4: Sanctum 토큰 인증
|
||||
|
||||
### 토큰 구조
|
||||
|
||||
**액세스 토큰:**
|
||||
- 만료 시간: 2시간 (120분)
|
||||
- 용도: API 호출 인증
|
||||
- 형식: `{token_id}|{plain_text_token}`
|
||||
|
||||
**리프레시 토큰:**
|
||||
- 만료 시간: 7일 (10080분)
|
||||
- 용도: 액세스 토큰 갱신
|
||||
- 특징: 일회성 사용 (사용 후 삭제)
|
||||
|
||||
### 토큰 갱신 플로우
|
||||
|
||||
```
|
||||
1. 클라이언트: POST /api/v1/refresh
|
||||
Headers: X-API-KEY, Authorization: Bearer {refresh_token}
|
||||
|
||||
2. 서버: 리프레시 토큰 검증
|
||||
- 유효성 체크
|
||||
- 만료 시간 체크
|
||||
- 사용자 확인
|
||||
|
||||
3. 서버: 기존 리프레시 토큰 삭제
|
||||
- 일회성 사용 보장
|
||||
|
||||
4. 서버: 새 토큰 발급
|
||||
- 새 액세스 토큰 생성
|
||||
- 새 리프레시 토큰 생성
|
||||
|
||||
5. 응답:
|
||||
{
|
||||
"access_token": "...",
|
||||
"refresh_token": "...",
|
||||
"expires_in": 7200,
|
||||
"expires_at": "2025-11-13 21:30:00"
|
||||
}
|
||||
```
|
||||
|
||||
### 토큰 만료 에러 처리
|
||||
|
||||
**위치:** `app/Exceptions/Handler.php`
|
||||
|
||||
```php
|
||||
if ($exception instanceof AuthenticationException) {
|
||||
$bearerToken = $request->bearerToken();
|
||||
if ($bearerToken) {
|
||||
$token = PersonalAccessToken::findToken($bearerToken);
|
||||
if ($token && $token->expires_at && $token->expires_at->isPast()) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => __('error.token_expired'),
|
||||
'error_code' => 'TOKEN_EXPIRED',
|
||||
], 401);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**프론트엔드 처리:**
|
||||
```javascript
|
||||
if (response.error_code === 'TOKEN_EXPIRED') {
|
||||
// 자동 리프레시 토큰으로 재발급
|
||||
const newTokens = await refreshToken(refreshToken);
|
||||
// 원래 요청 재시도
|
||||
return retryRequest(originalRequest, newTokens.access_token);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Layer 5: 권한 검증 (Permission System)
|
||||
|
||||
### 권한 시스템 개요
|
||||
|
||||
SAM은 **Spatie Permission** 패키지를 기반으로 한 다층 권한 시스템을 사용합니다.
|
||||
|
||||
**3단계 권한 구조:**
|
||||
```
|
||||
┌────────────────────────────────────────┐
|
||||
│ 1. 사용자 역할 권한 │
|
||||
│ User → Role → Permissions │
|
||||
│ (model_has_roles → role_has_perms) │
|
||||
└────────────────────────────────────────┘
|
||||
+
|
||||
┌────────────────────────────────────────┐
|
||||
│ 2. 사용자 직접 권한 │
|
||||
│ User → Permissions │
|
||||
│ (model_has_permissions) │
|
||||
└────────────────────────────────────────┘
|
||||
+
|
||||
┌────────────────────────────────────────┐
|
||||
│ 3. 부서 역할 권한 │
|
||||
│ User → Department → Role → Perms │
|
||||
│ (department_user → model_has_roles) │
|
||||
└────────────────────────────────────────┘
|
||||
↓
|
||||
┌────────────────────────────────────────┐
|
||||
│ UNION (중복 제거) │
|
||||
│ → 최종 사용자 권한 목록 │
|
||||
└────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**특징:**
|
||||
- 멀티테넌트 지원 (tenant_id로 격리)
|
||||
- 메뉴 기반 세분화된 권한
|
||||
- 다형성(Polymorphic) 구조로 유연한 확장
|
||||
- Permission Override로 임시/긴급 권한 제어
|
||||
|
||||
---
|
||||
|
||||
### 권한 패턴 및 타입
|
||||
|
||||
**권한 명명 규칙:**
|
||||
```
|
||||
menu:{menu_id}.{permission_type}
|
||||
```
|
||||
|
||||
**권한 타입 (7가지):**
|
||||
|
||||
| 타입 | 약자 | 설명 | 예시 |
|
||||
|------|------|------|------|
|
||||
| view | V | 조회 | 목록/상세 보기 |
|
||||
| create | C | 생성 | 신규 데이터 등록 |
|
||||
| update | U | 수정 | 기존 데이터 편집 |
|
||||
| delete | D | 삭제 | 데이터 삭제 |
|
||||
| approve | A | 승인 | 워크플로우 승인 |
|
||||
| export | E | 내보내기 | Excel/PDF 다운로드 |
|
||||
| manage | M | 관리 | 전체 관리 권한 |
|
||||
|
||||
**권한 예시:**
|
||||
```
|
||||
menu:1.view → 대시보드 보기
|
||||
menu:2.create → 제품 생성
|
||||
menu:2.update → 제품 수정
|
||||
menu:2.delete → 제품 삭제
|
||||
menu:3.approve → 주문 승인
|
||||
menu:4.export → 재고 내보내기
|
||||
menu:5.manage → 사용자 관리
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 권한 조회 로직
|
||||
|
||||
**구현 위치:**
|
||||
- **API:** `app/Services/MemberService.php` - `getUserInfoForLogin()`
|
||||
- **Admin:** `app/Filament/Resources/Users/Tables/UsersTable.php` - `getAccessibleMenusCount()`
|
||||
|
||||
**권한 조회 쿼리 구조:**
|
||||
|
||||
```php
|
||||
// 1. 사용자 역할 권한
|
||||
$userRolePermissions = DB::table('model_has_roles')
|
||||
->join('role_has_permissions', 'model_has_roles.role_id', '=', 'role_has_permissions.role_id')
|
||||
->join('permissions', 'role_has_permissions.permission_id', '=', 'permissions.id')
|
||||
->where('model_has_roles.model_type', User::class)
|
||||
->where('model_has_roles.model_id', $userId)
|
||||
->where('model_has_roles.tenant_id', $tenantId)
|
||||
->where('permissions.name', 'like', 'menu:%.view')
|
||||
->select('permissions.name');
|
||||
|
||||
// 2. 사용자 직접 권한
|
||||
$userDirectPermissions = DB::table('model_has_permissions')
|
||||
->join('permissions', 'model_has_permissions.permission_id', '=', 'permissions.id')
|
||||
->where('model_has_permissions.model_type', User::class)
|
||||
->where('model_has_permissions.model_id', $userId)
|
||||
->where('model_has_permissions.tenant_id', $tenantId)
|
||||
->where('permissions.name', 'like', 'menu:%.view')
|
||||
->select('permissions.name');
|
||||
|
||||
// 3. 부서 역할 권한
|
||||
$departmentRolePermissions = DB::table('department_user')
|
||||
->join('model_has_roles', function ($join) {
|
||||
$join->on('department_user.department_id', '=', 'model_has_roles.model_id')
|
||||
->where('model_has_roles.model_type', '=', Department::class);
|
||||
})
|
||||
->join('role_has_permissions', 'model_has_roles.role_id', '=', 'role_has_permissions.role_id')
|
||||
->join('permissions', 'role_has_permissions.permission_id', '=', 'permissions.id')
|
||||
->where('department_user.user_id', $userId)
|
||||
->where('department_user.tenant_id', $tenantId)
|
||||
->where('permissions.name', 'like', 'menu:%.view')
|
||||
->select('permissions.name');
|
||||
|
||||
// 4. 모든 권한 통합 (UNION + 중복 제거)
|
||||
$allPermissions = $userRolePermissions
|
||||
->union($userDirectPermissions)
|
||||
->union($departmentRolePermissions)
|
||||
->pluck('name')
|
||||
->toArray();
|
||||
```
|
||||
|
||||
**권한 파싱:**
|
||||
```php
|
||||
// menu:123.view → 메뉴 ID 123 추출
|
||||
foreach ($allPermissions as $permName) {
|
||||
if (preg_match('/^menu:(\d+)\.view$/', $permName, $matches)) {
|
||||
$allowedMenuIds[] = (int) $matches[1];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Permission Override (우선순위 제어)
|
||||
|
||||
**시간 기반 권한 제어:**
|
||||
|
||||
```php
|
||||
// permission_overrides 테이블
|
||||
[
|
||||
'tenant_id' => 1,
|
||||
'model_type' => 'App\Models\Members\User',
|
||||
'model_id' => 123,
|
||||
'permission_id' => 456,
|
||||
'effect' => 1, // 1=ALLOW, -1=DENY
|
||||
'effective_from' => '2025-11-13 00:00:00',
|
||||
'effective_to' => '2025-11-20 23:59:59',
|
||||
]
|
||||
```
|
||||
|
||||
**우선순위:**
|
||||
1. **Override DENY** (-1) - 최우선 차단
|
||||
2. **Override ALLOW** (1) - 명시적 허용
|
||||
3. **Base Permission** - 역할/부서/직접 권한
|
||||
|
||||
**최종 권한 계산:**
|
||||
```php
|
||||
foreach ($allMenuPermissions as $permName) {
|
||||
if (preg_match('/^menu:(\d+)\.view$/', $permName, $matches)) {
|
||||
$menuId = (int) $matches[1];
|
||||
|
||||
// Override DENY 체크 (강제 차단)
|
||||
if (isset($overrides[$permName]) && $overrides[$permName]->effect === -1) {
|
||||
continue; // 이 메뉴는 차단됨
|
||||
}
|
||||
|
||||
// Override ALLOW 또는 기본 권한
|
||||
if (
|
||||
(isset($overrides[$permName]) && $overrides[$permName]->effect === 1) ||
|
||||
in_array($permName, $basePermissions, true)
|
||||
) {
|
||||
$allowedMenuIds[] = $menuId;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**사용 사례:**
|
||||
- **임시 권한 부여:** 프로젝트 기간 동안만 특정 메뉴 접근 허용
|
||||
- **긴급 권한 차단:** 보안 사고 발생 시 즉시 권한 제거
|
||||
- **휴가 기간 제한:** 특정 기간 동안 권한 자동 차단
|
||||
- **시간대별 접근 제어:** 업무 시간에만 권한 부여
|
||||
|
||||
---
|
||||
|
||||
### 메뉴별 권한 매트릭스 뷰
|
||||
|
||||
**Admin 패널:** `http://admin.sam.kr/admin/permissions`
|
||||
|
||||
**테이블 구조:**
|
||||
|
||||
| 메뉴 ID | 메뉴명 | V (조회) | C (생성) | U (수정) | D (삭제) | A (승인) | E (내보내기) | M (관리) |
|
||||
|---------|--------|----------|----------|----------|----------|----------|-------------|----------|
|
||||
| 1 | 대시보드 | 홍길동, 김철수 | - | - | - | - | - | - |
|
||||
| 2 | 제품 관리 | 홍길동, 김철수 | 홍길동 | 홍길동 | 관리자 | - | 김철수 | 관리자 |
|
||||
| 3 | 주문 관리 | 전체팀 | 영업팀 | 영업팀 | 관리자 | 관리자 | 회계팀 | 관리자 |
|
||||
|
||||
**특징:**
|
||||
- 각 Row = 하나의 메뉴
|
||||
- 각 권한 타입별 Column에 해당 권한을 가진 사용자 목록 표시
|
||||
- 사용자 배지에 마우스 오버 시 `user_id` 툴팁 표시
|
||||
- 권한 없는 경우 `-` 표시
|
||||
- 3가지 권한 소스 (역할/부서/직접) 모두 통합하여 표시
|
||||
|
||||
**구현 코드:**
|
||||
```php
|
||||
// app/Filament/Resources/Permissions/Tables/PermissionsTable.php
|
||||
protected static function getUsersWithPermission(int $menuId, string $permissionType): string
|
||||
{
|
||||
$permissionName = "menu:{$menuId}.{$permissionType}";
|
||||
|
||||
// 3가지 권한 소스 UNION
|
||||
$userIds = $userRoleQuery
|
||||
->union($userDirectQuery)
|
||||
->union($departmentRoleQuery)
|
||||
->pluck('user_id')
|
||||
->unique()
|
||||
->toArray();
|
||||
|
||||
// 사용자 배지 HTML 생성
|
||||
$users = User::whereIn('id', $userIds)->orderBy('name')->get();
|
||||
foreach ($users as $user) {
|
||||
$badges[] = sprintf(
|
||||
'<span title="%s">%s</span>',
|
||||
htmlspecialchars($user->user_id),
|
||||
htmlspecialchars($user->name)
|
||||
);
|
||||
}
|
||||
|
||||
return implode(', ', $badges);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 권한 할당 방법
|
||||
|
||||
**1. 역할에 권한 할당:**
|
||||
```php
|
||||
$role = Role::findByName('영업팀', 'web');
|
||||
$role->givePermissionTo([
|
||||
'menu:2.view',
|
||||
'menu:2.create',
|
||||
'menu:3.view',
|
||||
]);
|
||||
```
|
||||
|
||||
**2. 사용자에게 직접 권한 할당:**
|
||||
```php
|
||||
$user = User::find(123);
|
||||
$user->givePermissionTo('menu:5.manage');
|
||||
```
|
||||
|
||||
**3. 부서에 역할 할당:**
|
||||
```php
|
||||
$department = Department::find(1);
|
||||
$department->assignRole('영업팀');
|
||||
```
|
||||
|
||||
**4. Permission Override 설정:**
|
||||
```php
|
||||
DB::table('permission_overrides')->insert([
|
||||
'tenant_id' => 1,
|
||||
'model_type' => User::class,
|
||||
'model_id' => 123,
|
||||
'permission_id' => 456,
|
||||
'effect' => -1, // DENY
|
||||
'effective_from' => now(),
|
||||
'effective_to' => now()->addDays(7),
|
||||
]);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 권한 체크 (Controller/Service)
|
||||
|
||||
**CheckPermission Middleware:**
|
||||
```php
|
||||
// routes/api.php
|
||||
Route::get('/products', [ProductController::class, 'index'])
|
||||
->middleware(['auth:sanctum', 'permission:menu:2.view']);
|
||||
```
|
||||
|
||||
**서비스 레이어:**
|
||||
```php
|
||||
if (!auth()->user()->can('menu:2.create')) {
|
||||
throw new \Exception(__('error.permission_denied'), 403);
|
||||
}
|
||||
```
|
||||
|
||||
**권한 확인 헬퍼:**
|
||||
```php
|
||||
// 단일 권한 체크
|
||||
auth()->user()->can('menu:2.view');
|
||||
|
||||
// 여러 권한 중 하나라도 있으면
|
||||
auth()->user()->hasAnyPermission(['menu:2.view', 'menu:2.manage']);
|
||||
|
||||
// 모든 권한 필요
|
||||
auth()->user()->hasAllPermissions(['menu:2.view', 'menu:2.create']);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 보안 모니터링
|
||||
|
||||
### 로그 파일
|
||||
|
||||
**1. 보안 로그**
|
||||
```bash
|
||||
# 무단 접근 시도
|
||||
tail -f storage/logs/laravel.log | grep "Unauthorized API access attempt"
|
||||
|
||||
# Rate Limit 초과
|
||||
tail -f storage/logs/laravel.log | grep "API Rate Limit Exceeded"
|
||||
```
|
||||
|
||||
**2. Nginx 로그**
|
||||
```bash
|
||||
# 접근 로그
|
||||
tail -f /var/log/nginx/api.sam.kr_access.log
|
||||
|
||||
# 에러 로그 (403 차단)
|
||||
tail -f /var/log/nginx/api.sam.kr_error.log
|
||||
```
|
||||
|
||||
### 공격 패턴 분석
|
||||
|
||||
**자주 발생하는 공격:**
|
||||
|
||||
1. **경로 탐색 (Path Traversal)**
|
||||
```
|
||||
GET /@fs/etc/passwd
|
||||
GET /../../../etc/passwd
|
||||
GET /api/../.env
|
||||
```
|
||||
**대응:** Nginx에서 403 차단
|
||||
|
||||
2. **보안 스캔**
|
||||
```
|
||||
User-Agent: sqlmap/1.0
|
||||
User-Agent: nikto/2.1.5
|
||||
```
|
||||
**대응:** Nginx User-Agent 필터링
|
||||
|
||||
3. **무차별 대입 (Brute Force)**
|
||||
```
|
||||
POST /api/v1/login (반복)
|
||||
```
|
||||
**대응:** Rate Limiting (10회/분)
|
||||
|
||||
4. **API Key 누락**
|
||||
```
|
||||
GET /api/v1/users (X-API-KEY 없음)
|
||||
```
|
||||
**대응:** 401 Unauthorized + 보안 로그
|
||||
|
||||
---
|
||||
|
||||
## 보안 체크리스트
|
||||
|
||||
### 개발 시
|
||||
|
||||
- [ ] 모든 API 엔드포인트에 `auth.apikey` 미들웨어 적용
|
||||
- [ ] 민감한 정보 로깅 제외 (`password`, `password_confirmation`)
|
||||
- [ ] FormRequest로 입력 검증
|
||||
- [ ] SQL 인젝션 방지 (Eloquent ORM 사용)
|
||||
- [ ] XSS 방지 (출력 시 이스케이핑)
|
||||
- [ ] CSRF 보호 (Sanctum 자동 적용)
|
||||
|
||||
### 배포 전
|
||||
|
||||
- [ ] `.env` 파일 보안 설정 확인
|
||||
- [ ] API Key 로테이션
|
||||
- [ ] Nginx 보안 규칙 테스트
|
||||
- [ ] Rate Limiting 임계값 검토
|
||||
- [ ] HTTPS 인증서 유효성 확인
|
||||
- [ ] 방화벽 규칙 설정
|
||||
|
||||
### 운영 중
|
||||
|
||||
- [ ] 매일 보안 로그 검토
|
||||
- [ ] 주간 공격 패턴 분석
|
||||
- [ ] 월간 토큰 만료 정책 검토
|
||||
- [ ] 분기별 API Key 갱신
|
||||
- [ ] 반기별 침투 테스트
|
||||
|
||||
---
|
||||
|
||||
## 보안 사고 대응
|
||||
|
||||
### 1단계: 즉시 조치
|
||||
|
||||
```bash
|
||||
# 1. 의심스러운 IP 차단 (Nginx)
|
||||
# /etc/nginx/conf.d/blocked_ips.conf
|
||||
deny 213.136.76.215;
|
||||
|
||||
# 2. Nginx 재시작
|
||||
sudo systemctl reload nginx
|
||||
|
||||
# 3. 활성 세션 강제 종료
|
||||
php artisan sanctum:prune-expired --hours=0
|
||||
|
||||
# 4. API Key 비활성화
|
||||
UPDATE api_keys SET is_active = 0 WHERE key = 'suspicious_key';
|
||||
```
|
||||
|
||||
### 2단계: 로그 분석
|
||||
|
||||
```bash
|
||||
# 공격 패턴 분석
|
||||
grep "213.136.76.215" /var/log/nginx/api.sam.kr_access.log
|
||||
|
||||
# 영향받은 엔드포인트 확인
|
||||
grep "Unauthorized API access attempt" storage/logs/laravel.log | grep "213.136.76.215"
|
||||
|
||||
# 시간대별 요청 횟수
|
||||
awk '{print $4}' /var/log/nginx/api.sam.kr_access.log | cut -d: -f1-2 | uniq -c
|
||||
```
|
||||
|
||||
### 3단계: 복구 및 강화
|
||||
|
||||
```bash
|
||||
# 1. 모든 사용자 비밀번호 초기화 (필요 시)
|
||||
# 2. 새 API Key 발급
|
||||
# 3. 토큰 만료 시간 단축 (임시)
|
||||
# 4. Rate Limiting 임계값 강화
|
||||
# 5. 추가 보안 규칙 적용
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## FAQ
|
||||
|
||||
### Q1. API Key는 어디서 발급받나요?
|
||||
|
||||
**A:** 관리자 패널(admin.sam.kr)에서 발급합니다.
|
||||
```sql
|
||||
-- api_keys 테이블 구조
|
||||
id, tenant_id, name, key, is_active, created_at, updated_at
|
||||
```
|
||||
|
||||
### Q2. Rate Limiting이 너무 엄격해요.
|
||||
|
||||
**A:** `ApiRateLimiter.php`에서 임계값 조정:
|
||||
```php
|
||||
if ($this->limiter->tooManyAttempts($key, 10)) { // 10 → 20으로 변경
|
||||
```
|
||||
|
||||
### Q3. 화이트리스트에 라우트를 추가하려면?
|
||||
|
||||
**A:** `ApiKeyMiddleware.php` 수정:
|
||||
```php
|
||||
$publicRoutes = [
|
||||
// 기존 라우트...
|
||||
'api/v1/public-data', // 추가
|
||||
];
|
||||
```
|
||||
|
||||
### Q4. 특정 IP만 허용하려면?
|
||||
|
||||
**A:** Nginx 설정 추가:
|
||||
```nginx
|
||||
# 화이트리스트 IP만 허용
|
||||
allow 203.0.113.0/24;
|
||||
allow 198.51.100.50;
|
||||
deny all;
|
||||
```
|
||||
|
||||
### Q5. 토큰 만료 시간을 변경하려면?
|
||||
|
||||
**A:** `.env` 파일 수정:
|
||||
```env
|
||||
SANCTUM_ACCESS_TOKEN_EXPIRATION=120 # 2시간 → 4시간 (240)
|
||||
SANCTUM_REFRESH_TOKEN_EXPIRATION=10080 # 7일 → 14일 (20160)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 참고 문서
|
||||
|
||||
- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
|
||||
- [Laravel Security Best Practices](https://laravel.com/docs/12.x/security)
|
||||
- [Sanctum Documentation](https://laravel.com/docs/12.x/sanctum)
|
||||
- [Nginx Security Tips](https://nginx.org/en/docs/http/ngx_http_access_module.html)
|
||||
|
||||
---
|
||||
|
||||
**작성일:** 2025-12-26
|
||||
**버전:** 1.0
|
||||
**담당자:** SAM Development Team
|
||||
Reference in New Issue
Block a user