Files
dbx-main/docs/DATABASES.md
T
king f137f2a6a6 feat(malaysia): 말레이시아 창고 재고관리 모듈 추가
- 신규 모듈 app/modules/malaysia (prefix /malaysia, 권한키 malaysia)
- malaysia_stock_db: warehouses/malaysia_items/set_bom/stock_movement/
  daily_stocktake/daily_stocktake_line (멱등 init SQL)
- 낱개(MT/MX/MZ) 입출고·조정 movement, 세트(MY) BOM 관리, 세트 출고 BOM 분해
- 일일 재고조사: 세트→낱개 자동 분해(direct+from_set=total), 확정 시 차이만 STOCKTAKE
- prefix 검증 이중화(DB CHECK + store.py 순수함수), MD- 전면 제외
- 상품명은 itemcode_db 읽기 전용 재사용(중복 마스터 없음)
- 화면 4종 + JSON API, 순수로직 테스트 14건, README/docs 갱신

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 13:34:31 +09:00

12 KiB

Databases

PostgreSQL DB 목록

DB명 용도
itemcode_db 상품코드, 단품/세트 구성, 채널 ↔ 사내 코드 매칭
orderlist_db 주문 수집·분석·관리 (구 orderlist_app)
return_db 반품·교환·CS 데이터
expense_db 개인경비 / 법인카드 사용내역 / 정산
cupang_db 쿠팡 밀크런 출고 묶음 / 출고 라인 / 입고센터 / 박스 입수량 규칙
malaysia_stock_db 말레이시아 창고 재고관리 — 창고/아이템/세트 BOM/입출고 이력/일일 재고조사

명명 규칙

  • 모든 DB 이름은 소문자 + 언더스코어, _db로 끝낸다.
    • inventory_db, cs_db
    • inventoryApp, cs-database
  • 신규 DB가 필요하면 사용자 승인 후 생성한다.
  • 신규 DB 생성 시 함께 정리할 항목:
    • 용도와 책임 모듈
    • 소유자(OWNER) 계정
    • 백업 주기와 위치
    • .env의 연결 정보 변수명

레거시 이름 매핑

예전 이름 현재 기준
orderlist_app orderlist_db

코드/문서/설정에서 orderlist_app을 발견하면 orderlist_db로 수정한다 (수정 전 영향 범위 확인).


DB 작업 원칙

  1. 작업 전 백업 우선. 백업 없는 변경은 진행하지 않는다.
  2. 테이블 owner, 권한, sequence 권한을 확인한다.
  3. 운영 DB의 DROP, TRUNCATE, 조건 없는 대량 DELETE/UPDATE사용자 확인 없이 실행 금지.
  4. 스키마 변경은 마이그레이션 스크립트(scripts/ 또는 alembic 등)로 관리한다.
  5. 운영 DB와 개발 DB의 접속 정보를 혼동하지 않는다 (.env로 분리).

위험 명령 (사용자 승인 필수)

명령 비고
DROP DATABASE 복구 불가. 백업 없으면 절대 실행 금지
DROP TABLE / DROP SCHEMA 의존 객체 확인 필수
TRUNCATE FK CASCADE 시 광범위 삭제 위험
조건 없는 DELETE / UPDATE WHERE 없는 문 차단
docker volume rm <postgres_volume> 운영 데이터 영구 손실
docker compose down -v 볼륨까지 제거. 운영에서 금지

실행 전 반드시:

  1. 백업 확인 (pg_dump, 컨테이너 외부 마운트)
  2. 영향 범위 설명
  3. 사용자 명시 승인

자주 쓰는 점검 명령

# 컨테이너/네트워크
docker ps
docker network ls
docker inspect <postgres_container_name>

# DB 목록 / 접속
sudo -u postgres psql -l
docker exec -it <postgres_container_name> psql -U postgres -l

# 특정 DB 접속
docker exec -it <postgres_container_name> psql -U <user> -d itemcode_db

# 테이블/권한 확인
\dt
\dn+
\du
\z <table_name>

expense_db 스키마 / 초기화

DDL: scripts/sql/expense_db_init.sql (멱등). DB·역할·테이블·인덱스·트리거를 한 번에 생성.

테이블 expense_items

컬럼 타입 비고
id TEXT PK 12자 hex (uuid4 앞 12자)
owner TEXT 소유자 email (소문자)
spent_at DATE 사용일
category TEXT 식대/교통/숙박/비품/접대/통신/기타
method TEXT 법인카드/개인지출/현금
merchant TEXT 가맹점
amount BIGINT 원 단위, ≥ 0
memo TEXT 비고
status TEXT 작성중/제출/승인/반려/정산완료
approver_email TEXT 결재자 email (승인/반려/정산 시 기록)
decided_at TIMESTAMPTZ 결재 시점
reject_reason TEXT 반려 사유
created_at / updated_at TIMESTAMPTZ 트리거로 자동 갱신

인덱스: (owner, spent_at DESC), (status), (created_at DESC).

테이블 expense_attachments

영수증/기타파일 메타데이터. 실제 파일은 DATA_DIR/uploads/expense/{item_id}/ 에 저장.

컬럼 타입 비고
id TEXT PK 12자 hex
item_id TEXT FK expense_items(id) ON DELETE CASCADE
owner TEXT 업로드한 사용자 email
kind TEXT receipt 또는 other (CHECK)
filename TEXT 원본 파일명
stored_path TEXT 디스크 경로 (절대)
content_type TEXT MIME
size_bytes BIGINT 바이트
uploaded_at TIMESTAMPTZ 업로드 시각

인덱스: (item_id).

결재 워크플로

작성중 ─submit──▶ 제출 ─approve──▶ 승인 ─settle──▶ 정산완료
   ▲                │
   └─revert/reject──┴─reject──▶ 반려 ─revert──▶ 작성중
  • submit/revert: owner 본인
  • approve/reject/settle: expense_approver 또는 admin
  • 첨부 추가/항목 수정/삭제: 작성중 또는 반려 상태에서만

마이그레이션

기존 운영 DB 에 신규 컬럼/테이블 적용:

docker exec -i postgres-db psql -U postgres -d expense_db \
  < scripts/sql/expense_db_002_workflow_attachments.sql

신규 설치는 expense_db_init.sql 하나로 충분 (둘 다 멱등).

운영 서버 초기화 (1회)

# 1) 비밀번호 변수 준비 (셸 히스토리에 남지 않게 환경변수 사용)
read -s -p "expense_app password: " APP_PWD; echo

# 2) PostgreSQL 컨테이너에 DDL 적용
docker exec -i postgres-db psql -U postgres \
  -v app_password="$APP_PWD" \
  < scripts/sql/expense_db_init.sql

# 3) main-app .env 에 EXPENSE_DB_URL 추가
#    EXPENSE_DB_URL=postgresql://expense_app:<APP_PWD>@postgres-db:5432/expense_db

# 4) main-app 재기동
docker compose up -d --build

JSON → DB 마이그레이션

docker exec -e EXPENSE_DB_URL="$EXPENSE_DB_URL" -it dbx-main \
  python scripts/migrate_expense_json_to_db.py \
    --json /data/expense.json --dry-run
# 결과 확인 후
docker exec -e EXPENSE_DB_URL="$EXPENSE_DB_URL" -it dbx-main \
  python scripts/migrate_expense_json_to_db.py --json /data/expense.json

멱등 INSERT(ON CONFLICT DO NOTHING). 원본 JSON 은 건드리지 않는다.


cupang_db 스키마 / 초기화

DDL: scripts/sql/cupang_db_init.sql (멱등). DB·역할(cupang_app)·테이블·인덱스·트리거·센터 seed 를 한 번에 생성. JSON 폴백 없음CUPANG_DB_URL 미설정 시 모듈이 "설정 필요" 안내만 표시.

테이블:

테이블 용도
cupang_centers 입고센터. active=false 로 비활성화(사용 중이면 hard delete 금지)
cupang_box_rules 제품코드별 박스당 입수량(units_per_box). product_code UNIQUE
cupang_shipments 출고 묶음 헤더 (작성일/출고일/센터입고일/센터/출고방식/상태/작업자/메모)
cupang_shipment_lines 출고 라인. shipment_id FK ON DELETE CASCADE. UNIQUE(shipment_id, line_no)

status 허용값: 작성중, 출고준비, 출고완료, 센터입고완료, 취소. 삭제는 기본 soft delete(status='취소').

박스 계산은 서버(store.compute_boxes)에서 재계산: required_boxes = ceil(quantity / units_per_box). 클라이언트 계산은 미리보기용.

상품은 cupang_db 에 복제 저장하지 않는다. 라인에는 product_code + product_name_snapshot 만 보존(과거 명칭 보존). 상품 검색은 itemcode_db 읽기 전용(ITEMCODE_DB_URL, 미설정 시 수동 입력).

운영 서버 초기화 (1회, 사용자 승인 후)

read -s -p "cupang_app password: " APP_PWD; echo
docker exec -i postgres-db psql -U postgres \
  -v app_password="$APP_PWD" \
  < scripts/sql/cupang_db_init.sql
# main-app .env 에 추가:
#   CUPANG_DB_URL=postgresql://cupang_app:<APP_PWD>@postgres-db:5432/cupang_db
cd /opt/www/main && docker compose up -d --build

멱등 스크립트. 기존 DB 가 있으면 DROP 하지 않음. itemcode_db 는 건드리지 않음.


vacation_db 스키마 / 초기화

DDL: scripts/sql/vacation_db_init.sql (멱등). DB·역할(vacation_app)·테이블·인덱스·트리거·2026 공휴일 seed 를 한 번에 생성. JSON 폴백 없음VACATION_DB_URL 미설정 시 모듈이 "설정 필요" 안내만 표시.

테이블:

테이블 용도
vacation_requests 휴가 신청(헤더). 종류/기간/시작·종료 구분(full/am/pm)/일수/사유/상태/승인자/반려사유
vacation_holidays 공휴일(holiday_date UNIQUE). is_red=true 면 달력 빨강 + 일수 계산 제외. 관리자가 settings 에서 추가/수정/삭제
vacation_balances 사용자별 연차(UNIQUE(user_email, year)). total_days 설정, 사용일수는 승인 휴가 합계로 자동 계산

status 허용값: 작성중, 제출, 승인, 반려, 취소. 워크플로: 작성중/반려 → 제출 → 승인|반려. 삭제는 기본 soft delete(status='취소'). 수정은 작성중/반려 상태에서 본인만.

휴가 일수는 서버(store.compute_days)에서 재계산: 주말 + vacation_holidays(is_red) 제외, 오전/오후 반차 0.5일, 시작/종료 반차는 각 0.5 차감. 클라이언트 계산은 미리보기(주말만 제외)용.

권한: vacation(접근) / vacation_approver(승인·반려). admin 은 항상 통과. 공휴일·연차 설정은 admin 전용.

운영 서버 초기화 (1회, 사용자 승인 후)

read -s -p "vacation_app password: " APP_PWD; echo
docker exec -i postgres-db psql -U postgres \
  -v app_password="$APP_PWD" \
  < scripts/sql/vacation_db_init.sql
# main-app .env 에 추가:
#   VACATION_DB_URL=postgresql://vacation_app:<APP_PWD>@postgres-db:5432/vacation_db
cd /opt/www/main && docker compose up -d --build

멱등 스크립트. 기존 DB 가 있으면 DROP 하지 않음. 공휴일은 연도별로 다르므로 settings 화면에서 추가/수정.


malaysia_stock_db 스키마 / 초기화

DDL: scripts/sql/malaysia_stock_db_init.sql (멱등). DB·역할(malaysia_app)·테이블·인덱스·트리거·창고/아이템 seed 를 한 번에 생성. JSON 폴백 없음MALAYSIA_STOCK_DB_URL 미설정 시 모듈이 "설정 필요" 안내만 표시. 상품명은 itemcode_db 읽기 전용 재사용.

테이블:

테이블 용도
warehouses 창고(warehouse_code UNIQUE). seed: MY-WH-01 / Malaysia Warehouse
malaysia_items 관리 대상 코드 스코프 + 종류(individual/set) + 이름 스냅샷. prefix CHECK. seed: 낱개 12 + 세트 5
set_bom 세트 구성표. set_code(MY-)·component_code(MT/MX/MZ) prefix CHECK, UNIQUE(set_code, component_code)
stock_movement 입고/출고/조정/조사 이력. item_code 낱개만(CHECK), movement_type IN/OUT/ADJUST/STOCKTAKE. ref_type·ref_no(Shopee/Lazada 연동용)
daily_stocktake 재고조사 헤더. status draft/finalized/cancelled. 같은 날짜+창고 finalized 1건(부분 유니크 인덱스)
daily_stocktake_line 조사 라인. sku_code 낱개+세트 허용(MD- 금지), qty>=0, UNIQUE(stocktake_id, sku_code)

현재고 = SUM(IN) - SUM(OUT) + SUM(ADJUST) + SUM(STOCKTAKE). 재고조사 확정 시 시스템 재고와 조사 최종치의 차이만 STOCKTAKE movement 로 기록. 세트는 movement 불가, 재고조사/BOM 계산 전용. MD- 뚜껑은 전 영역 제외.

운영 서버 초기화 (1회, 사용자 승인 후)

read -s -p "malaysia_app password: " APP_PWD; echo
docker exec -i postgres-db psql -U postgres \
  -v app_password="$APP_PWD" \
  < scripts/sql/malaysia_stock_db_init.sql
# main-app .env 에 추가:
#   MALAYSIA_STOCK_DB_URL=postgresql://malaysia_app:<APP_PWD>@postgres-db:5432/malaysia_stock_db
cd /opt/www/main && docker compose up -d --build

멱등 스크립트. 기존 DB 가 있으면 DROP 하지 않음. itemcode_db 는 건드리지 않음.


백업 / 복구 (안전 절차)

백업

# 단일 DB 덤프 (운영 권장)
docker exec -t <postgres_container_name> \
  pg_dump -U <user> -F c -d orderlist_db \
  > /var/backups/postgres/orderlist_db_$(date +%F).dump

복구 (덮어쓰기 위험 → 사용자 승인 필수)

# 1) 신규 DB로 먼저 복구해 검증
docker exec -i <postgres_container_name> \
  pg_restore -U <user> -d <new_db_name> < backup.dump

# 2) 검증 완료 후 운영 DB 교체 (필요 시)

pg_restore --clean은 기존 객체를 삭제한다. 운영 대상 DB에서 절대 무단 실행 금지.


.env 관련

  • DB 접속 정보(*_HOST, *_PORT, *_USER, *_PASSWORD, *_NAME)는 모두 .env로 관리한다.
  • .envGit에 올리지 않는다. .env.example만 커밋한다.
  • 비밀값 유출이 의심되면 즉시 회전(비밀번호/키 변경)을 진행한다.