From 8cb15cf3c4dad80fafbb51a16eeef02511bbdd9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Mon, 2 Mar 2026 17:15:25 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20[guides]=20=ED=85=8C=EC=9D=B4=EB=B8=94?= =?UTF-8?q?=20=EC=84=A4=EA=B3=84=20=EA=B0=80=EC=9D=B4=EB=93=9C=20=EB=B9=84?= =?UTF-8?q?=EC=A0=84=EB=AC=B8=EA=B0=80=EC=9A=A9=20=EB=AC=B8=EC=84=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - options JSON 컬럼 패턴을 엑셀 비유로 쉽게 설명 - 멀티테넌시(tenant_id) 개념 해설 - 실제 SAM 테이블 예시 (주문, 입고, 공정) - FAQ 5개, 판단 흐름도, 한 장 요약 포함 - INDEX.md에 문서 등록 --- sam/docs/INDEX.md | 1 + sam/docs/guides/table-design-guide.md | 486 ++++++++++++++++++++++++++ 2 files changed, 487 insertions(+) create mode 100644 sam/docs/guides/table-design-guide.md diff --git a/sam/docs/INDEX.md b/sam/docs/INDEX.md index cb0af59..7152079 100644 --- a/sam/docs/INDEX.md +++ b/sam/docs/INDEX.md @@ -165,6 +165,7 @@ docs/ | [auto-login-guide.md](guides/auto-login-guide.md) | MNG→DEV 자동 로그인 | 자동 로그인 구현 시 | | [erp-api-list.md](guides/erp-api-list.md) | ERP API 목록 (List vs Detail 구분) | 프론트 API 연동 시 | | [erp-api-detail.md](guides/erp-api-detail.md) | ERP API 상세 스펙 | 프론트 API 연동 시 | +| [table-design-guide.md](guides/table-design-guide.md) | 테이블 설계 가이드 (비전문가용, options JSON 패턴) | 테이블 구조 이해 시 | | [item-master-guide.md](guides/item-master-guide.md) | 품목기준관리 페이지-섹션-필드 구조 | 품목 UI 구현 시 | | [item-master-items-api.md](guides/item-master-items-api.md) | ItemMaster & Items API 문서 | 품목 API 연동 시 | diff --git a/sam/docs/guides/table-design-guide.md b/sam/docs/guides/table-design-guide.md new file mode 100644 index 0000000..a920f08 --- /dev/null +++ b/sam/docs/guides/table-design-guide.md @@ -0,0 +1,486 @@ +# SAM 테이블 설계 가이드 — 비전문가용 + +> **작성일**: 2026-03-02 +> **대상 독자**: 개발자, 기획자, 관리자 — 데이터베이스를 잘 모르는 분도 읽을 수 있습니다 +> **관련 정책**: `standards/options-column-policy.md` (개발자 전용 상세 규칙) + +--- + +## 1. 이 문서는 왜 필요한가? + +SAM은 **여러 회사가 하나의 시스템을 공유**하는 구조입니다. +A회사, B회사, C회사가 모두 같은 프로그램을 쓰지만, 각 회사가 필요한 정보는 다릅니다. + +이 문서는 SAM에서 **데이터를 어떻게 저장하는지**, 그 설계 철학을 누구나 이해할 수 있도록 설명합니다. + +--- + +## 2. 기본 개념: 데이터베이스 테이블이란? + +데이터베이스 테이블은 **엑셀 시트**와 같습니다. + +``` +"주문" 테이블 (= 엑셀 시트) + + 열(컬럼) → 주문번호 │ 고객명 │ 금액 │ 상태 + ───────────────────────────────────────────────────────── + 행(레코드) 1 → ORD-001 │ 김철수 │ 500,000 │ 완료 + 행(레코드) 2 → ORD-002 │ 이영희 │ 300,000 │ 진행중 + 행(레코드) 3 → ORD-003 │ 박민수 │ 800,000 │ 대기 +``` + +- **열(컬럼)** = 정보의 종류 (주문번호, 고객명, 금액...) +- **행(레코드)** = 실제 데이터 한 건 (주문 1건) + +--- + +## 3. 문제: 회사마다 필요한 정보가 다르다 + +SAM은 여러 회사가 같은 테이블을 공유합니다. + +``` +같은 "주문" 테이블을 쓰는데... + + 🏭 A회사 (블라인드 제조) + → "절곡 각도", "날개 수" 정보가 필요해요 + + 🏭 B회사 (스크린 제조) + → "메시 밀도", "소재 종류" 정보가 필요해요 + + 🏭 C회사 (셔터 제조) + → "날개 간격", "색상 코드" 정보가 필요해요 +``` + +--- + +### 3.1 전통적인 해결 방법 (SAM은 이렇게 안 합니다) + +필요할 때마다 엑셀에 열을 추가하는 것처럼, 테이블에 컬럼을 추가합니다. + +``` +"주문" 테이블 — 전통적 방식 + + 주문번호 │ 고객명 │ 금액 │ 절곡각도 │ 날개수 │ 메시밀도 │ 소재 │ 날개간격 │ 색상코드 + ───────────────────────────────────────────────────────────────────────────────── + ORD-001 │ 김철수 │ 50만 │ 45도 │ 12개 │ (빈칸) │ (빈칸) │ (빈칸) │ (빈칸) ← A회사 + ORD-002 │ 이영희 │ 30만 │ (빈칸) │ (빈칸)│ 18 │ 폴리 │ (빈칸) │ (빈칸) ← B회사 + ORD-003 │ 박민수 │ 80만 │ (빈칸) │ (빈칸)│ (빈칸) │ (빈칸) │ 25mm │ #FF0000 ← C회사 +``` + +**문제점:** + +- 회사가 100개면? 열이 수백 개로 늘어남 +- 각 회사는 자기 것 빼고 전부 빈칸 +- 새 회사가 들어올 때마다 시스템 전체를 수정해야 함 +- 열 추가 = 시스템 중단 위험이 있는 작업 + +--- + +### 3.2 SAM의 해결 방법: "메모칸(options)" 하나로 통합 + +**핵심 열만 남기고**, 나머지는 **메모칸 하나**에 자유롭게 적습니다. + +``` +"주문" 테이블 — SAM 방식 + + 주문번호 │ 고객명 │ 금액 │ 상태 │ options (메모칸) + ──────────────────────────────────────────────────────────────────────── + ORD-001 │ 김철수 │ 50만 │ 완료 │ {"절곡각도": 45, "날개수": 12} ← A회사 + ORD-002 │ 이영희 │ 30만 │ 진행 │ {"메시밀도": 18, "소재": "폴리에스터"} ← B회사 + ORD-003 │ 박민수 │ 80만 │ 대기 │ {"날개간격": 25, "색상코드": "#FF0000"} ← C회사 + ORD-004 │ 최지은 │ 40만 │ 대기 │ null ← 메모 없음 +``` + +**`options`** = JSON이라는 형식의 메모칸. `{ }` 안에 자유롭게 정보를 넣을 수 있습니다. + +--- + +## 4. 어떤 정보를 열(컬럼)로 만들고, 어떤 정보를 메모칸(options)에 넣나? + +이것이 SAM 테이블 설계의 **가장 중요한 판단 기준**입니다. + +### 4.1 판단 흐름 (5가지 질문) + +새로운 정보를 저장해야 할 때, 아래 질문에 답합니다. + +``` +질문 1. 이 정보로 다른 테이블의 데이터를 연결(참조)하나? + 예: 고객ID로 고객 테이블을 찾는다 + → YES: 일반 컬럼 + +질문 2. 이 정보로 자주 검색(필터)하나? + 예: "완료" 상태인 주문만 보여줘 + → YES: 일반 컬럼 + +질문 3. 이 정보로 정렬하나? + 예: 최신 주문부터 보여줘 + → YES: 일반 컬럼 + +질문 4. 이 정보가 절대 중복되면 안 되나? + 예: 주문번호는 세상에 하나뿐이어야 한다 + → YES: 일반 컬럼 + +질문 5. 이 정보로 합계/평균을 계산하나? + 예: 이번 달 매출 합계 + → YES: 일반 컬럼 + +질문 1~5 전부 NO → options 메모칸에 저장 +``` + +### 4.2 실생활 예시로 비교 + +#### 예시 1: "주문" 테이블 + +| 정보 | 어디에 저장? | 이유 | +|------|:-----------:|------| +| 주문번호 | **일반 컬럼** | 중복 불가 + 검색 필수 | +| 고객 ID | **일반 컬럼** | 고객 테이블과 연결 | +| 금액 | **일반 컬럼** | 합계 계산 필요 | +| 상태 (진행/완료) | **일반 컬럼** | 필터(검색) 필수 | +| 생성일 | **일반 컬럼** | 정렬 필요 | +| 배송지 주소 | **options** | 부가 정보, 검색 안 함 | +| 수신자 이름 | **options** | 부가 정보 | +| 수신자 연락처 | **options** | 부가 정보 | +| 특이사항 메모 | **options** | 있어도 되고 없어도 됨 | + +**실제 SAM 코드에서 주문(Order) 테이블:** + +``` +일반 컬럼: id, tenant_id, order_number, client_id, total_amount, status, created_at +options: {"shipping_cost_code":"착불", "receiver":"홍길동", + "receiver_contact":"010-1234-5678", + "shipping_address":"서울 강남구 역삼동 123"} +``` + +#### 예시 2: "입고검사" 테이블 + +| 정보 | 어디에 저장? | 이유 | +|------|:-----------:|------| +| 품목 ID | **일반 컬럼** | 품목 테이블과 연결 | +| 수량 | **일반 컬럼** | 합계 계산 | +| 입고일 | **일반 컬럼** | 정렬 + 검색 | +| 제조사 | **options** | 모든 입고에 있지는 않음 | +| 검사 결과 (합격/불합격) | **options** | 검사를 안 하는 회사도 있음 | +| 검사일 | **options** | 선택적 정보 | + +**실제 SAM 코드에서 입고(Receiving) 테이블:** + +``` +일반 컬럼: id, tenant_id, item_id, quantity, received_at, status +options: {"manufacturer":"삼성전자", + "inspection_status":"합격", + "inspection_date":"2026-03-01"} +``` + +> 검사 결과가 options에 있는 이유: **모든 회사가 입고검사를 하는 것은 아닙니다.** +> A회사는 검사를 하고, B회사는 안 합니다. 이걸 일반 컬럼으로 만들면 B회사에겐 항상 빈칸입니다. + +#### 예시 3: "공정" 테이블 + +| 정보 | 어디에 저장? | 이유 | +|------|:-----------:|------| +| 공정 코드 | **일반 컬럼** | 중복 불가 + 검색 | +| 공정명 | **일반 컬럼** | 검색 + 표시 | +| 담당 부서 | **일반 컬럼** | 필터 | +| 작업일지 필요 여부 | **options** | 회사별로 다름 | +| 검사 필요 여부 | **options** | 회사별로 다름 | + +``` +일반 컬럼: id, tenant_id, process_code, process_name, department +options: {"needs_work_log": true, "needs_inspection": false} +``` + +--- + +## 5. 메모칸(options)의 실제 모습: JSON이란? + +`options`에 저장되는 데이터 형식은 **JSON**입니다. +JSON은 프로그래밍 세계의 "구조화된 메모장"이라고 생각하면 됩니다. + +### 5.1 JSON 기본 문법 + +``` +{ ← 시작 + "키": "값", ← 문자(텍스트) + "이름": "홍길동", + "나이": 30, ← 숫자 (따옴표 없음) + "합격": true, ← 참/거짓 (따옴표 없음) + "메모": null ← 값 없음 +} ← 끝 +``` + +### 5.2 중첩(nested) — 메모 안의 메모 + +``` +{ + "배송": { ← 배송 관련 정보를 묶음 + "주소": "서울 강남구 역삼동", + "수신자": "홍길동", + "연락처": "010-1234-5678" + }, + "검사": { ← 검사 관련 정보를 묶음 + "결과": "합격", + "검사일": "2026-03-01", + "검사자ID": 5 + } +} +``` + +### 5.3 목록(배열) — 여러 개를 나열 + +``` +{ + "선택지": [ ← 대괄호 [ ] = 목록 + {"label": "블라인드", "value": "blind"}, + {"label": "스크린", "value": "screen"}, + {"label": "셔터", "value": "shutter"} + ] +} +``` + +> 이 형태는 드롭다운 메뉴의 선택지 목록을 저장할 때 사용합니다. + +--- + +## 6. 멀티테넌시란? — 여러 회사가 하나의 시스템을 쓰는 구조 + +### 6.1 개념 + +``` +┌──────────────────────────────────────────────┐ +│ SAM 시스템 (하나의 프로그램) │ +│ │ +│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ +│ │ A회사 │ │ B회사 │ │ C회사 │ │ +│ │ tenant=1 │ │ tenant=2 │ │ tenant=3 │ │ +│ │ │ │ │ │ │ │ +│ │ 블라인드 │ │ 스크린 │ │ 셔터 │ │ +│ │ 제조 │ │ 제조 │ │ 제조 │ │ +│ └──────────┘ └──────────┘ └──────────┘ │ +│ │ +│ 같은 테이블을 쓰지만, │ +│ tenant_id로 데이터가 완전히 분리됨 │ +└──────────────────────────────────────────────┘ +``` + +### 6.2 tenant_id = 회사 식별 번호 + +모든 테이블의 모든 행에 `tenant_id`(회사 번호)가 붙어 있습니다. + +``` +"주문" 테이블 + + id │ tenant_id │ 주문번호 │ 금액 │ options + ─────────────────────────────────────────────────────────── + 1 │ 1 │ ORD-001 │ 50만 │ {"절곡각도": 45} ← A회사 데이터 + 2 │ 1 │ ORD-002 │ 30만 │ {"절곡각도": 90} ← A회사 데이터 + 3 │ 2 │ ORD-001 │ 80만 │ {"메시밀도": 18} ← B회사 데이터 + 4 │ 3 │ ORD-001 │ 40만 │ {"날개간격": 25} ← C회사 데이터 +``` + +**A회사가 로그인하면** → 시스템이 자동으로 `tenant_id = 1`인 데이터만 보여줌 +**B회사가 로그인하면** → 시스템이 자동으로 `tenant_id = 2`인 데이터만 보여줌 + +> A회사는 B회사의 데이터를 절대 볼 수 없습니다. 시스템이 자동으로 차단합니다. + +### 6.3 options + tenant_id = 강력한 조합 + +이 두 가지가 합쳐지면: + +``` +같은 테이블, 같은 컬럼 구조인데 + ✅ 회사마다 다른 데이터 (tenant_id로 분리) + ✅ 회사마다 다른 속성 (options로 유연하게) + ✅ 시스템 수정 없이 확장 가능 +``` + +--- + +## 7. SAM 테이블의 표준 구조 + +SAM에서 새 테이블을 만들면 항상 이 구조를 따릅니다. + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ SAM 표준 테이블 구조 │ +│ │ +│ ① 식별자 │ +│ id — 자동 생성 번호 (1, 2, 3...) │ +│ tenant_id — 어느 회사의 데이터인지 │ +│ │ +│ ② 핵심 정보 (검색/정렬/연결에 쓰는 것만) │ +│ code — 코드 (중복 불가) │ +│ status — 상태 (검색용) │ +│ is_active — 사용 여부 │ +│ sort_order — 표시 순서 │ +│ (+ FK 컬럼들) — 다른 테이블 연결 │ +│ │ +│ ③ 메모칸 │ +│ options — 나머지 전부 (JSON) │ +│ │ +│ ④ 감사 기록 (자동) │ +│ created_by — 누가 만들었나 │ +│ updated_by — 누가 수정했나 │ +│ deleted_by — 누가 삭제했나 │ +│ created_at — 언제 만들었나 │ +│ updated_at — 언제 수정했나 │ +│ deleted_at — 언제 삭제했나 (휴지통 개념) │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### 영역별 설명 + +| 영역 | 역할 | 비유 | +|------|------|------| +| ① 식별자 | "이 데이터가 누구 것인지" 구분 | 우편물의 받는 사람 + 주소 | +| ② 핵심 정보 | 검색, 정렬, 집계에 꼭 필요한 정보 | 엑셀의 고정 열 | +| ③ options | 회사마다 다른 부가 정보 | 엑셀의 "비고" 칸 (자유 서식) | +| ④ 감사 기록 | 언제 누가 뭘 했는지 자동 추적 | CCTV 기록 | + +--- + +## 8. 실제 SAM에서 options를 쓰는 테이블들 (22개) + +현재 SAM에서 options 메모칸을 사용하는 주요 테이블입니다. + +| 테이블 | 한글명 | options에 저장하는 정보 예시 | +|--------|--------|--------------------------| +| `orders` | 주문 | 배송지, 수신자, 연락처, 담당자 | +| `quotes` | 견적 | 견적 요약, 비용 항목, 가격 조정 | +| `receivings` | 입고 | 제조사, 검사 결과, 검사일 | +| `work_orders` | 작업지시 | 절곡 정보 (bending_info) | +| `work_order_items` | 작업지시 항목 | 작업 결과, 양품/불량 수량, LOT번호 | +| `processes` | 공정 | 작업일지 필요 여부, 검사 필요 여부 | +| `order_nodes` | 주문 노드 | 위치, 구역, 층, 실 (트리 구조) | +| `products` | 제품 | 동적 옵션 (라벨, 값, 단위) | +| `items` | 품목 | 품목별 동적 속성 | +| `materials` | 자재 | 자재 추가 속성 | +| `menus` | 메뉴 | 섹션, 메뉴 타입, 필요 권한 | +| `users` | 사용자 | 개인 설정/환경설정 | +| `tenants` | 회사(테넌트) | 회사 규모, 업종 | +| `document_template_section_fields` | 문서 양식 필드 | 선택지 목록, API 경로 | +| `item_fields` | 품목 필드 정의 | 필드별 세부 설정 | + +--- + +## 9. 자주 묻는 질문 (FAQ) + +### Q1. options에 넣으면 검색이 안 되나요? + +**아닙니다.** MySQL 8.0은 JSON 내부도 검색할 수 있습니다. + +``` +일반 컬럼 검색: "상태가 '완료'인 주문 찾아줘" → 매우 빠름 +options 검색: "제조사가 '삼성'인 입고 찾아줘" → 가능하지만 조금 느림 +``` + +다만, **매일 수천 번 검색하는 정보**라면 일반 컬럼으로 승격하는 것이 맞습니다. +가끔 검색하는 정보라면 options로 충분합니다. + +### Q2. options에 아무 정보나 마음대로 넣을 수 있나요? + +기술적으로는 가능하지만, 개발팀 내부에서 **어떤 키를 쓸지 미리 약속**합니다. + +``` +✅ 약속된 키: {"manufacturer": "삼성", "inspection_status": "합격"} +❌ 멋대로: {"asdf": 123, "temp_data": "???"} +``` + +코드에서 상수로 정의하여 일관성을 유지합니다. + +### Q3. 전통적 방식보다 뭐가 좋은 건가요? + +| 비교 항목 | 전통적 방식 (열 추가) | SAM 방식 (options JSON) | +|----------|:------------------:|:---------------------:| +| 새 정보 추가 시 | 시스템 수정 필요 | 코드만 변경 | +| 다른 회사에 영향 | 있음 (전체 구조 변경) | 없음 | +| 빈칸(null) 낭비 | 많음 | 없음 | +| 검색 속도 | 빠름 | 조금 느림 (충분히 실용적) | +| 유연성 | 낮음 | 높음 | +| 시스템 중단 위험 | 있음 (대형 테이블 수정 시) | 없음 | + +### Q4. 그럼 모든 정보를 options에 넣으면 되지 않나요? + +**아닙니다.** 핵심 정보는 반드시 일반 컬럼으로 만들어야 합니다. + +``` +❌ 나쁜 예: 모든 것을 options에 + + id │ tenant_id │ options + ────────────────────────────────────────────────────────────── + 1 │ 1 │ {"주문번호":"ORD-001", "금액":500000, "상태":"완료", ...} + + → 주문번호 검색 느림, 금액 합계 계산 불가, 중복 방지 불가 +``` + +``` +✅ 좋은 예: 핵심은 컬럼, 부가는 options + + id │ tenant_id │ order_number │ amount │ status │ options + ────────────────────────────────────────────────────────────── + 1 │ 1 │ ORD-001 │ 500000 │ 완료 │ {"배송지":"서울..."} + + → 검색 빠름, 합계 가능, 중복 방지 가능, 부가 정보도 유연 +``` + +### Q5. options 데이터는 화면에서 어떻게 보이나요? + +사용자 화면에서는 options 안에 있는지, 일반 컬럼인지 **구분할 수 없습니다**. +프로그램이 자동으로 꺼내서 보여줍니다. + +``` +화면에 보이는 모습: + + ┌─────────────────────────────────┐ + │ 입고 상세 정보 │ + │ │ + │ 품목: SUS304 스틸 │ ← 일반 컬럼 + │ 수량: 100개 │ ← 일반 컬럼 + │ 입고일: 2026-03-01 │ ← 일반 컬럼 + │ 제조사: 삼성전자 │ ← options에서 꺼냄 + │ 검사결과: 합격 │ ← options에서 꺼냄 + │ 검사일: 2026-03-01 │ ← options에서 꺼냄 + └─────────────────────────────────┘ + + 사용자는 어디에 저장되어 있는지 알 필요 없음! +``` + +--- + +## 10. 한 장 요약 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ │ +│ SAM 테이블 설계 = "핵심만 컬럼, 나머진 메모칸(options)" │ +│ │ +│ ┌──────────────────────────────────────────────────────────┐ │ +│ │ tenant_id → 어느 회사 것인지 (자동 격리) │ │ +│ │ 핵심 컬럼들 → 검색/정렬/연결/집계에 쓰는 필수 정보 │ │ +│ │ options → 나머지 전부 (회사마다 다른 부가 정보) │ │ +│ │ 감사 컬럼들 → 누가/언제 만들고/수정하고/삭제했는지 │ │ +│ └──────────────────────────────────────────────────────────┘ │ +│ │ +│ 이렇게 하면: │ +│ ✅ 회사 추가해도 테이블 구조 안 바꿈 │ +│ ✅ 새 정보 추가해도 시스템 수정 최소화 │ +│ ✅ 회사마다 다른 정보를 유연하게 저장 │ +│ ✅ 데이터 보안 (회사 간 완전 분리) │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 관련 문서 + +| 문서 | 설명 | 대상 | +|------|------|------| +| [options-column-policy.md](../standards/options-column-policy.md) | 개발자용 상세 정책 (코드 규칙, 마이그레이션 패턴) | 개발자 | +| [database/README.md](../system/database/README.md) | DB 스키마 전체 현황 (220개 모델) | 개발자 | +| [PROJECT_DEVELOPMENT_POLICY.md](PROJECT_DEVELOPMENT_POLICY.md) | 개발 공통 정책 (테이블 생성 절차) | 개발자 | +| [system/overview.md](../system/overview.md) | SAM 시스템 전체 아키텍처 | 전체 | + +--- + +**최종 업데이트**: 2026-03-02