Files
invyone/db/migrations/RUN_083_MIGRATION.md
T
gbpark 68f85f3736 회사 관리 기능 확장 + 테넌트/비번 보안 하드닝
- 첫 로그인 비번 강제 변경 (RUN_082): FORCE_PASSWORD_CHANGE 컬럼,
  ForcePasswordChangeGuardFilter, /auth/change-password API + 페이지
- 테넌트 일관성 가드: TenantConsistencyGuardFilter 로 JWT.company_code
  ↔ 서브도메인 company_code 대조, CompanyResolver 가 (db_name, company_code)
  동시 반환
- 회사 관리 확장 (RUN_083 audit log, RUN_084 lifecycle 컬럼):
  CompanyAdmin/Members/Templates/Lifecycle/AuditLog 서비스 +
  CompanyMgmtController + SuperAdminGuard
- 회사 관리 UI: CompanyAccordionRow 탭화 + 모달 4종
  (AdminInfo/Deactivate/Delete/RecopyTemplates) + AuditLogDrawer + csvExport
- 프로비저닝 마법사: force_password_change 토글 반영
- 프론트 인증: storage 이벤트 멀티탭 동기화, 403 errorCode
  (PASSWORD_CHANGE_REQUIRED / CROSS_TENANT_REJECTED / TENANT_NOT_RESOLVED)
  전역 리다이렉트
- 기타: StartupSchemaMigrator, OS별 도커 기동 스크립트, CLAUDE.md 트래킹

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

3.3 KiB

083 마이그레이션 — 회사 관리 감사 로그 테이블

작성일: 2026-04-24 작성자: gbpark 관련: 회사 관리 화면 (/admin/sysMng/subdomainList) 라이프사이클 액션 감사 로그

목적

회사 관리 화면에서 수행되는 SUPER_ADMIN 레벨 액션을 메타 DB 에 추적 가능하게 기록.

기록 대상 액션

action 기록 지점
COMPANY_CREATE 프로비저닝 성공 시 (FINALIZE)
COMPANY_CREATE_FAILED 프로비저닝 실패 시 (자동 롤백 후)
COMPANY_DEACTIVATE DB_STATUS=suspended 변경 시
COMPANY_REACTIVATE suspended → active 복귀 시
COMPANY_DELETE 영구 삭제 (DROP DATABASE + row DELETE) 시
ADMIN_PASSWORD_RESET 관리자 계정 비번 재설정 시
TEMPLATES_RECOPY 템플릿 재복제 시

추가 테이블

CREATE TABLE IF NOT EXISTS COMPANY_AUDIT_LOG (
    ID             BIGSERIAL    PRIMARY KEY,
    COMPANY_CODE   VARCHAR(30)  NOT NULL,
    ACTOR_USER_ID  VARCHAR(50),                  -- 수행자 (JWT 에서 뽑아낸 user_id). 개발모드는 NULL 가능
    ACTION         VARCHAR(40)  NOT NULL,        -- 위 표의 action 값
    TARGET         VARCHAR(100),                 -- 영향받은 대상 (db_name, admin user_id 등)
    DETAILS        JSONB,                        -- 추가 컨텍스트 (reason, before/after 값 등)
    SUCCESS        BOOLEAN      DEFAULT TRUE,
    ERROR_MESSAGE  TEXT,                         -- 실패 시 에러 메시지
    CREATED_AT     TIMESTAMP    DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS IDX_AUDIT_COMPANY_CODE
    ON COMPANY_AUDIT_LOG (COMPANY_CODE, CREATED_AT DESC);

CREATE INDEX IF NOT EXISTS IDX_AUDIT_CREATED
    ON COMPANY_AUDIT_LOG (CREATED_AT DESC);

CREATE INDEX IF NOT EXISTS IDX_AUDIT_ACTION
    ON COMPANY_AUDIT_LOG (ACTION, CREATED_AT DESC);

실행 (메타 DB 만)

이 테이블은 메타 DB (invyone) 에만 존재해야 한다. 테넌트 DB 는 복제 대상 아님. SchemaCopier 는 메타 DB 를 pg_dump --schema-only 로 복제하므로, 새 테넌트 DB 에 이 테이블이 딸려가는 걸 막으려면 이 테이블은 COMPANY_MNG 와 같이 "메타 전용" 취급.

SchemaCopier 의 --exclude-table 처리 확인 필요

현재 구현은 public 스키마 전체를 복제. COMPANY_AUDIT_LOG 가 테넌트 DB 에도 생성되지만 어차피 쓰지 않으므로 빈 채로 남는다. 용량 문제 없음 → MVP 로는 허용. 추후 정리.

psql -h 183.99.177.40 -U postgres -d invyone <<'SQL'
CREATE TABLE IF NOT EXISTS COMPANY_AUDIT_LOG (
    ID             BIGSERIAL    PRIMARY KEY,
    COMPANY_CODE   VARCHAR(30)  NOT NULL,
    ACTOR_USER_ID  VARCHAR(50),
    ACTION         VARCHAR(40)  NOT NULL,
    TARGET         VARCHAR(100),
    DETAILS        JSONB,
    SUCCESS        BOOLEAN      DEFAULT TRUE,
    ERROR_MESSAGE  TEXT,
    CREATED_AT     TIMESTAMP    DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS IDX_AUDIT_COMPANY_CODE ON COMPANY_AUDIT_LOG (COMPANY_CODE, CREATED_AT DESC);
CREATE INDEX IF NOT EXISTS IDX_AUDIT_CREATED      ON COMPANY_AUDIT_LOG (CREATED_AT DESC);
CREATE INDEX IF NOT EXISTS IDX_AUDIT_ACTION       ON COMPANY_AUDIT_LOG (ACTION, CREATED_AT DESC);
SQL

롤백

DROP TABLE IF EXISTS COMPANY_AUDIT_LOG;