Files
dbx-main/docs/DATABASES.md
T
king 550867ba0c feat(expense+admin): 결재 워크플로 + 첨부 + 집계 + 엑셀 + 사용자 직접 등록
[admin]
- POST /api/users 사용자 직접 등록 (이메일만으로). UserStore.create_user 추가
- admin.html: 등록 폼 + 삭제 버튼 + 한글 라벨 + 신규 권한 표시
- 신규 권한 키: expense_approver, vacation_approver (APPROVER_KEYS)
- is_approver(user_rec, kind) 헬퍼

[expense 워크플로]
- 상태 전이: 작성중 → 제출 → 승인/반려 → 정산완료
- API: /api/items/{id}/{submit,revert,approve,reject,settle}
- 권한: submit/revert=owner, approve/reject/settle=expense_approver 또는 admin
- expense_items 컬럼: approver_email, decided_at, reject_reason
- 작성중/반려 상태에서만 수정/삭제/첨부 가능

[expense 첨부]
- 영수증/기타 (kind: receipt|other). 최대 20MB, 확장자 화이트리스트
- 저장: DATA_DIR/uploads/expense/{item_id}/{att_id}_{filename}
- expense_attachments 테이블 (CASCADE on item delete)
- API: 업로드/목록/다운로드/삭제

[expense 집계]
- /expense/reports: 연도별 월×카테고리 피벗 표

[expense 엑셀]
- GET /expense/api/export.xlsx?from&to&scope=mine|all
- scope=all 은 승인자/관리자만

[DDL]
- expense_db_init.sql: 신규 컬럼/테이블 포함 (멱등)
- expense_db_002_workflow_attachments.sql: 운영 DB 마이그레이션용

[deps]
- openpyxl>=3.1, python-multipart>=0.0.20

[docs]
- DATABASES.md: 워크플로 다이어그램, attachments 테이블, 마이그레이션
- PROJECT_OVERVIEW.md: 권한 키 표
- DEPLOYMENT.md: DATA_DIR/uploads 안내
2026-05-29 02:52:44 +09:00

6.6 KiB

Databases

PostgreSQL DB 목록

DB명 용도
itemcode_db 상품코드, 단품/세트 구성, 채널 ↔ 사내 코드 매칭
orderlist_db 주문 수집·분석·관리 (구 orderlist_app)
return_db 반품·교환·CS 데이터
expense_db 개인경비 / 법인카드 사용내역 / 정산

명명 규칙

  • 모든 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 은 건드리지 않는다.


백업 / 복구 (안전 절차)

백업

# 단일 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만 커밋한다.
  • 비밀값 유출이 의심되면 즉시 회전(비밀번호/키 변경)을 진행한다.