Files
baron_qa_write/docs/관리페이지 md 파일/local-userfeedback-erd.md
T
root 3c10478482
Deploy EG-BIM QA Gateway / deploy (push) Successful in 2m4s
관리페이지 데이터 전송 구현
2026-09-21 14:46:36 +09:00

25 KiB

로컬 userfeedback DB ERD

  • 추출 기준: 2026-09-17
  • 대상: abc-user-feedback-mysql-1 / userfeedback
  • 테이블: 33개
  • UUID 저장 형식: BINARY(16) (애플리케이션 경계에서는 UUID v7 문자열)
  • 관계 기준: MySQL information_schema.KEY_COLUMN_USAGE에 등록된 물리 FK

1. 테넌트·사용자·프로젝트·채널

%%{init: {"themeVariables": {"fontSize": "20px"}, "er": {"fontSize": 20, "minEntityWidth": 220, "minEntityHeight": 120, "entityPadding": 18, "diagramPadding": 24}}}%%
erDiagram
    TENANT o|--o{ PROJECTS : "tenant_id"
    PROJECTS o|--o{ CHANNELS : "project_id"
    PROJECTS o|--o{ ROLES : "project_id"
    USERS o|--o{ MEMBERS : "user_id"
    ROLES o|--o{ MEMBERS : "role_id"
    PROJECTS ||--o{ CATEGORIES : "project_id"
    CHANNELS o|--o{ FIELDS : "channel_id"
    FIELDS o|--o{ OPTIONS : "field_id"
    PROJECTS o|--o{ API_KEYS : "project_id"
    PROJECTS o|--o| ISSUE_TRACKERS : "project_id"
    PROJECTS o|--o{ WEBHOOKS : "project_id"
    WEBHOOKS o|--o{ EVENTS : "webhook_id"
    EVENTS ||--o{ EVENTS_CHANNELS_CHANNELS : "events_id"
    CHANNELS ||--o{ EVENTS_CHANNELS_CHANNELS : "channels_id"

    TENANT {
        uuid id PK
        string site_name
        string description
        bool use_email
        text allow_domains
        bool use_o_auth
        json oauth_config
        datetime created_at
        datetime updated_at
        datetime deleted_at
    }

    USERS {
        uuid id PK
        string email "UK with sign_up_method"
        string name
        string department
        enum state
        string hash_password
        enum type
        enum sign_up_method
        string oauth_subject "UK with tenant and method"
        string oauth_tenant_id
        json secondary_emails
        string phone_number
        datetime created_at
        datetime updated_at
        datetime deleted_at
    }

    PROJECTS {
        uuid id PK
        uuid tenant_id FK
        string name UK
        string description
        string timezone
        json issue_admin_user_ids
        bool show_feedback_comment_author_name
        datetime created_at
        datetime updated_at
        datetime deleted_at
    }

    CHANNELS {
        uuid id PK
        uuid project_id FK
        string name "UK with project_id"
        string description
        json image_config
        int feedback_search_max_days
        datetime created_at
        datetime updated_at
        datetime deleted_at
    }

    ROLES {
        uuid id PK
        uuid project_id FK
        string name
        text permissions
        datetime created_at
        datetime updated_at
        datetime deleted_at
    }

    MEMBERS {
        uuid id PK
        uuid role_id FK "UK with user_id"
        uuid user_id FK "UK with role_id"
        datetime created_at
        datetime updated_at
        datetime deleted_at
    }

    CATEGORIES {
        uuid id PK
        uuid project_id FK
        string name "UK with project_id"
        datetime created_at
        datetime updated_at
        datetime deleted_at
    }

    FIELDS {
        uuid id PK
        uuid channel_id FK
        uuid ai_field_template_id FK
        string name "UK with channel_id"
        string key "UK with channel_id"
        string description
        enum format
        enum status
        enum property
        int order
        json ai_field_target_keys
        bool ai_field_auto_processing
        datetime created_at
        datetime updated_at
        datetime deleted_at
    }

    OPTIONS {
        uuid id PK
        uuid field_id FK
        string name
        string key
        datetime created_at
        datetime updated_at
        datetime deleted_at
    }

    API_KEYS {
        uuid id PK
        uuid project_id FK
        string value UK
        datetime created_at
        datetime updated_at
        datetime deleted_at
    }

    ISSUE_TRACKERS {
        uuid id PK
        uuid project_id FK,UK
        json data
        datetime created_at
        datetime updated_at
        datetime deleted_at
    }

    WEBHOOKS {
        uuid id PK
        uuid project_id FK
        string name
        string url
        string token
        enum status
        datetime created_at
        datetime updated_at
        datetime deleted_at
    }

    EVENTS {
        uuid id PK
        uuid webhook_id FK
        enum status
        enum type
        datetime created_at
        datetime updated_at
        datetime deleted_at
    }

    EVENTS_CHANNELS_CHANNELS {
        uuid events_id PK,FK
        uuid channels_id PK,FK
    }

2. 피드백·댓글·이슈

%%{init: {"themeVariables": {"fontSize": "20px"}, "er": {"fontSize": 20, "minEntityWidth": 220, "minEntityHeight": 120, "entityPadding": 18, "diagramPadding": 24}}}%%
erDiagram
    CHANNELS o|--o{ FEEDBACKS : "channel_id"
    FEEDBACKS ||--o{ FEEDBACK_COMMENTS : "feedback_id"
    FEEDBACK_COMMENTS ||--o{ FEEDBACK_COMMENT_ATTACHMENTS : "comment_id"
    PROJECTS o|--o{ ISSUES : "project_id"
    CATEGORIES o|--o{ ISSUES : "category_id"
    FEEDBACKS ||--o{ FEEDBACKS_ISSUES_ISSUES : "feedbacks_id"
    ISSUES ||--o{ FEEDBACKS_ISSUES_ISSUES : "issues_id"
    ISSUES ||--o{ ISSUE_INTERNAL_MEMOS : "issue_id"
    CHANNELS o|--o{ FEEDBACK_STATISTICS : "channel_id"
    PROJECTS o|--o{ ISSUE_STATISTICS : "project_id"
    ISSUES o|--o{ FEEDBACK_ISSUE_STATISTICS : "issue_id"

    FEEDBACKS {
        uuid id PK
        uuid channel_id FK
        json data
        datetime admin_first_read_at
        uuid admin_first_read_by "logical users.id"
        string source_namespace "UK with source_record_id"
        string source_record_id
        datetime created_at
        datetime updated_at
        datetime deleted_at
    }

    FEEDBACK_COMMENTS {
        uuid id PK
        uuid feedback_id FK
        string author_id
        string author_tenant_id
        string author_name
        string author_type
        text content
        bool is_internal
        string comment_type
        string idempotency_key "UK with feedback_id"
        string source_namespace "UK with feedback_id and record"
        string source_record_id
        datetime created_at
        datetime updated_at
        datetime deleted_at
    }

    FEEDBACK_COMMENT_ATTACHMENTS {
        uuid id PK
        uuid comment_id FK
        string original_file_name
        string storage_key "UK with storage_bucket"
        string storage_bucket
        string mime_type
        bigint file_size
        string source_namespace "UK with source_record_id"
        string source_record_id
        datetime created_at
        datetime updated_at
        datetime deleted_at
    }

    ISSUES {
        uuid id PK
        uuid project_id FK
        uuid category_id FK
        uuid issue_admin_user_id "logical users.id"
        uuid admin_first_read_by "logical users.id"
        uuid hold_set_by "logical users.id"
        uuid hold_released_by "logical users.id"
        string name "UK with project_id"
        string description
        enum status
        string external_issue_id "UK with provider and project"
        string external_issue_provider
        string external_issue_url
        string external_issue_status
        string external_issue_sync_status
        text external_issue_sync_error
        datetime external_issue_synced_at
        int feedback_count
        text internal_memo
        datetime admin_first_read_at
        text hold_reason
        datetime hold_set_at
        datetime hold_released_at
        string hold_resume_status
        datetime completed_at
        datetime created_at
        datetime updated_at
        datetime deleted_at
    }

    FEEDBACKS_ISSUES_ISSUES {
        uuid feedbacks_id PK,FK
        uuid issues_id PK,FK
    }

    ISSUE_INTERNAL_MEMOS {
        uuid id PK
        uuid issue_id FK
        uuid author_id "logical users.id"
        uuid deleted_by_user_id "logical users.id"
        string author_name
        text content
        string deleted_by_name
        datetime created_at
        datetime updated_at
        datetime deleted_at
    }

    FEEDBACK_STATISTICS {
        uuid id PK
        uuid channel_id FK
        date date "UK with channel_id"
        int count
    }

    ISSUE_STATISTICS {
        uuid id PK
        uuid project_id FK
        date date "UK with project_id"
        int count
    }

    FEEDBACK_ISSUE_STATISTICS {
        uuid id PK
        uuid issue_id FK
        date date "UK with issue_id"
        int feedback_count
    }

    CHANNELS {
        uuid id PK
    }

    PROJECTS {
        uuid id PK
    }

    CATEGORIES {
        uuid id PK
    }

3. AI·이력·멱등성·운영

%%{init: {"themeVariables": {"fontSize": "20px"}, "er": {"fontSize": 20, "minEntityWidth": 220, "minEntityHeight": 120, "entityPadding": 18, "diagramPadding": 24}}}%%
erDiagram
    PROJECTS ||--o{ AI_FIELD_TEMPLATES : "project_id"
    PROJECTS ||--o| AI_INTEGRATIONS : "project_id"
    PROJECTS ||--o{ AI_USAGES : "project_id"
    CHANNELS ||--o{ AI_ISSUE_TEMPLATES : "channel_id"
    AI_FIELD_TEMPLATES o|--o{ FIELDS : "ai_field_template_id"

    AI_FIELD_TEMPLATES {
        uuid id PK
        uuid project_id FK
        string title
        text prompt
        string model
        float temperature
        datetime created_at
        datetime updated_at
        datetime deleted_at
    }

    AI_INTEGRATIONS {
        uuid id PK
        uuid project_id FK,UK
        enum provider
        string api_key
        string endpoint_url
        text system_prompt
        int token_threshold
        float notification_threshold
        datetime created_at
        datetime updated_at
        datetime deleted_at
    }

    AI_ISSUE_TEMPLATES {
        uuid id PK
        uuid channel_id FK
        json target_field_keys
        text prompt
        bool is_enabled
        string model
        float temperature
        int data_reference_amount
        datetime created_at
        datetime updated_at
        datetime deleted_at
    }

    AI_USAGES {
        uuid id PK
        uuid project_id FK
        int year
        int month
        int day
        enum category
        enum provider
        int used_tokens
        datetime created_at
        datetime updated_at
        datetime deleted_at
    }

    HISTORIES {
        uuid id PK
        uuid user_id "logical users.id"
        enum entity_name
        string entity_id
        enum action
        json entity
        datetime created_at
        datetime updated_at
        datetime deleted_at
    }

    IDEMPOTENCY_RECORDS {
        uuid id PK
        string consumer "UK with idempotency_key"
        string idempotency_key
        string request_hash
        string resource_type
        uuid resource_id "logical polymorphic id"
        datetime expires_at
        datetime created_at
        datetime updated_at
        datetime deleted_at
    }

    NOTIFICATION_DELIVERIES {
        uuid id PK
        string idempotency_key UK
        string event_type
        string event_id
        enum status
        int attempts
        text last_error
        datetime sent_at
        string target_type
        string target_id
        uuid project_id "logical projects.id"
        uuid channel_id "logical channels.id"
        uuid feedback_id "logical feedbacks.id"
        datetime created_at
        datetime updated_at
        datetime deleted_at
    }

    CODES {
        uuid id PK
        enum type
        string key
        string code
        string data
        bool is_verified
        datetime expired_at
        int try_count
        datetime created_at
        datetime updated_at
        datetime deleted_at
    }

    SCHEDULER_LOCKS {
        enum lock_type PK
        string server_id
        datetime timestamp
    }

    MIGRATIONS {
        int id PK
        bigint timestamp
        string name
    }

    PROJECTS {
        uuid id PK
    }

    CHANNELS {
        uuid id PK
    }

    FIELDS {
        uuid id PK
    }

4. 주요 물리 Unique Key

테이블 컬럼 조합
projects name
channels name, project_id
categories project_id, name
fields key, channel_id; name, channel_id
members role_id, user_id
users email, sign_up_method; oauth_subject, oauth_tenant_id, sign_up_method
feedbacks source_namespace, source_record_id
feedback_comments feedback_id, idempotency_key; feedback_id, source_namespace, source_record_id
feedback_comment_attachments storage_bucket, storage_key; source_namespace, source_record_id
issues name, project_id; external_issue_provider, project_id, external_issue_id
idempotency_records consumer, idempotency_key
ai_integrations project_id
issue_trackers project_id

5. 물리 FK가 없는 논리 참조

다음 컬럼은 UUID 또는 식별자를 저장하지만 현재 MySQL FK 제약은 없다.

  • feedbacks.admin_first_read_by → users.id
  • issues.issue_admin_user_id → users.id
  • issues.admin_first_read_by → users.id
  • issues.hold_set_by → users.id
  • issues.hold_released_by → users.id
  • issue_internal_memos.author_id → users.id
  • issue_internal_memos.deleted_by_user_id → users.id
  • histories.user_id → users.id
  • idempotency_records.resource_id는 다형성 리소스 참조
  • notification_deliveries.project_id/channel_id/feedback_id는 이벤트 스냅샷 참조

6. DB 설계 이유

6.1 현재 규모에서는 단일 MySQL이 가장 단순하고 안정적이다

예상 데이터가 댓글과 피드백을 합쳐 연간 약 5만 건이라면, 단일 MySQL 인스턴스가 처리하기에 충분히 작은 규모다. 이 단계에서 물리적 샤딩이나 여러 DB로 데이터를 나누면 성능 이득보다 분산 트랜잭션, 데이터 정합성, 장애 복구 및 운영 복잡성이 더 커진다.

따라서 현재 설계는 다음 원칙을 사용한다.

  • 업무 데이터의 최종 권위는 MySQL 하나로 유지한다.
  • Redis는 캐시·멱등성 선점·일시적 잠금에 사용하되 최종 정합성의 기준으로 사용하지 않는다.
  • 실제 CPU, 디스크 I/O, Buffer Pool, Lock wait 또는 목록 조회 p95가 허용 범위를 지속해서 넘을 때 읽기 복제본이나 기능별 분리를 검토한다.
  • UUID를 사용해 향후 여러 서버에서 동시에 데이터를 생성하더라도 PK 발급을 중앙 DB의 AUTO_INCREMENT에 의존하지 않는다.

즉, 분산 환경에서 사용할 수 있는 식별자와 중복 방지 구조는 미리 갖추되, 데이터 저장소 자체는 현재 규모에 맞게 단순하게 유지한 설계다.

6.2 UUID v7을 모든 주요 엔티티의 PK로 사용한다

tenant, projects, channels, feedbacks, issues, 댓글과 첨부파일 등 주요 엔티티의 PK는 UUID v7이며 MySQL에는 BINARY(16)으로 저장한다.

이 방식을 선택한 이유는 다음과 같다.

  • 여러 API 인스턴스가 DB 왕복 없이 충돌 가능성이 매우 낮은 ID를 직접 생성할 수 있다.
  • UUID v7은 시간 순서 특성이 있어 UUID v4보다 B-Tree 인덱스의 무작위 페이지 분할과 단편화를 줄일 수 있다.
  • 외부 URL과 API에서 동일 UUID를 사용하므로 내부 ID와 외부 ID를 변환하는 조회 로직이 필요 없다.
  • 프로젝트나 채널을 다른 DB 또는 서비스로 이동하더라도 ID 충돌 없이 그대로 유지할 수 있다.
  • MySQL에서는 문자열 CHAR(36) 대신 BINARY(16)을 사용해 PK 및 FK 인덱스 크기를 줄인다.

UUID는 DB 관리 도구에서 깨진 문자처럼 보일 수 있지만 데이터 손상이 아니라 16바이트 바이너리를 문자로 표시해서 발생하는 현상이다. 조회할 때는 BIN_TO_UUID(id)를 사용해야 한다.

6.3 tenant → project → channel 계층으로 데이터 소유 범위를 구분한다

데이터의 기본 소유 계층은 다음과 같다.

Tenant
└── Project
    ├── Channel
    │   ├── Field / Option
    │   └── Feedback
    ├── Role / Member
    ├── Category / Issue
    ├── API Key
    └── Webhook / Integration
  • tenant는 고객 또는 조직의 최상위 경계다.
  • projects는 권한, API Key, 이슈 및 외부 연동의 관리 단위다.
  • channels는 프로젝트 안에서 피드백 입력 양식과 피드백 데이터를 분리하는 단위다.
  • 프로젝트별 역할은 roles, 사용자 할당은 members로 분리해 한 사용자가 여러 프로젝트에서 서로 다른 권한을 가질 수 있다.

이 계층은 현재 단일 DB에서도 데이터 격리를 명확하게 하고, 향후 필요하면 tenant_id 또는 project_id를 기준으로 데이터 배치와 분할 범위를 결정할 수 있게 한다.

6.4 피드백 본문은 JSON, 검색·관계 기준은 정규 컬럼으로 분리한다

채널마다 입력 필드가 다르므로 feedbacks.data는 JSON으로 저장한다. 필드 정의는 fields, 선택지는 options에서 관리한다.

모든 입력값을 고정 컬럼으로 만들지 않은 이유는 다음과 같다.

  • 채널별 양식 변경 시 매번 DB migration을 만들 필요가 없다.
  • 사용자 정의 필드와 AI 생성 필드를 동일한 구조로 처리할 수 있다.
  • 기존 피드백 데이터를 유지하면서 새 필드를 추가하거나 비활성화할 수 있다.

반면 검색과 무결성에 자주 사용하는 값은 JSON에 넣지 않고 정규 컬럼으로 둔다.

  • id, channel_id, created_at, deleted_at
  • 외부 중복 방지용 source_namespace, source_record_id
  • 관리자 최초 열람 정보

특정 JSON 필드 검색이 반복적으로 느려질 때만 Generated Column과 인덱스를 추가한다. 연간 5만 건 수준에서 사용 여부가 확인되지 않은 모든 JSON 키를 미리 인덱싱하면 쓰기 비용과 운영 복잡성만 증가하기 때문이다.

6.5 댓글과 첨부파일을 별도 엔티티로 분리한다

댓글은 feedback_comments, 댓글 첨부파일은 feedback_comment_attachments로 분리했다.

  • 하나의 피드백에 여러 댓글을 시간순으로 저장할 수 있다.
  • 공개 댓글과 내부 댓글을 is_internal로 구분할 수 있다.
  • 첨부파일 메타데이터만 DB에 두고 실제 파일은 로컬 볼륨 또는 Object Storage에 저장할 수 있다.
  • 댓글 삭제와 첨부파일 정리 정책을 피드백 본문과 독립적으로 적용할 수 있다.
  • 댓글 및 첨부파일도 각각 외부 원본 식별자를 가져 재전송 중복을 막을 수 있다.

파일 자체를 DB BLOB으로 저장하지 않는 것은 DB 백업 크기와 I/O 부하를 줄이고, 향후 R2/S3 같은 Object Storage로 전환하기 쉽게 하기 위한 선택이다.

6.6 피드백과 이슈는 N:M 관계로 설계한다

feedbacks_issues_issues 연결 테이블을 두어 하나의 피드백을 여러 이슈와 연결하고, 하나의 이슈가 여러 피드백을 묶을 수 있게 했다.

예를 들어 여러 사용자가 같은 장애를 각각 제보하면 피드백은 개별 기록으로 유지하면서 하나의 이슈로 묶어 처리할 수 있다. 연결 테이블의 복합 PK (feedbacks_id, issues_id)는 동일 연결이 중복 저장되는 것을 DB 차원에서 방지한다.

이슈의 외부 연동 식별자는 다음 조합으로 유일성을 보장한다.

external_issue_provider + project_id + external_issue_id

따라서 Gitea, Jira, GitHub가 같은 숫자 이슈 ID를 사용해도 서로 충돌하지 않으며 프로젝트가 다르면 독립적으로 관리된다.

6.7 중복 요청 방지는 업무 Unique Key와 멱등성 레코드를 함께 사용한다

네트워크 재시도나 외부 시스템의 중복 전송으로 같은 데이터가 여러 번 생성되지 않도록 두 계층으로 방어한다.

  1. 업무 원본 중복 방지
    • source_namespace + source_record_id
    • 원본 시스템 안에서 같은 레코드를 다시 보내면 기존 데이터를 식별한다.
  2. HTTP 요청 재시도 방지
    • consumer + idempotency_key
    • 같은 소비자의 동일 요청 키는 idempotency_records에서 한 번만 처리한다.

Redis가 사용 가능한 경우 짧은 선점 잠금으로 동시 요청을 빠르게 차단하지만, 최종 중복 방지는 MySQL Unique Key가 담당한다. 따라서 Redis가 재시작되거나 일시적으로 중단되어도 중복 데이터가 확정 저장되지 않는다.

6.8 Soft Delete와 이력 테이블을 사용한다

대부분의 업무 테이블에는 created_at, updated_at, deleted_at이 있다.

  • 일반 삭제는 deleted_at을 기록하는 Soft Delete로 처리해 실수로 삭제한 데이터를 복구할 수 있다.
  • FK의 ON DELETE CASCADE는 프로젝트나 피드백을 실제로 물리 삭제하는 명시적 정리 작업에서 하위 데이터를 함께 제거한다.
  • histories는 엔티티 종류, 동작, 당시 데이터를 JSON으로 기록해 관리자 변경 이력을 추적한다.

Soft Delete만 적용하면 Unique Key의 재사용 정책이 복잡해질 수 있으므로 프로젝트명, 채널명처럼 재사용 여부가 중요한 값은 운영 정책과 함께 관리해야 한다.

6.9 집계 통계는 원본 테이블과 분리한다

대시보드가 매번 전체 피드백과 이슈를 집계하지 않도록 다음 통계 테이블을 둔다.

  • feedback_statistics: 채널·일자별 피드백 수
  • issue_statistics: 프로젝트·일자별 이슈 수
  • feedback_issue_statistics: 이슈·일자별 연결 피드백 수

원본 데이터는 feedbacksissues이며 통계 테이블은 재생성 가능한 파생 데이터다. scheduler_locks는 여러 API 인스턴스가 같은 통계 작업을 동시에 실행하는 것을 막는다.

연간 5만 건에서는 원본 직접 집계도 가능하지만, 대시보드 요청이 늘어날 때의 반복 스캔을 줄이고 응답시간을 안정적으로 유지하기 위해 집계 구조를 분리했다.

6.10 AI와 외부 연동 설정은 업무 데이터와 분리한다

AI 기능은 다음과 같이 역할별로 분리했다.

  • ai_integrations: 프로젝트별 AI 공급자와 연결 설정
  • ai_field_templates: AI 필드 생성 프롬프트
  • ai_issue_templates: 채널별 이슈 추천 설정
  • ai_usages: 프로젝트별 사용량

웹훅도 webhooks, events, events_channels_channels로 분리해 하나의 웹훅에 여러 이벤트와 채널을 연결할 수 있다. 설정과 실행 데이터를 분리하면 공급자 변경, 기능 비활성화, 사용량 집계가 피드백 원본 구조에 영향을 주지 않는다.

6.11 일부 사용자·이벤트 참조에 물리 FK를 두지 않은 이유

관리자 사용자, 이력 작성자, 알림 대상처럼 삭제 이후에도 당시 값을 보존해야 하거나 여러 엔티티를 참조할 수 있는 컬럼에는 물리 FK가 없다.

  • 사용자가 삭제되어도 과거 이력과 처리 담당자 기록은 남아야 한다.
  • idempotency_records.resource_id는 피드백, 댓글 등 여러 리소스를 가리킬 수 있어 단일 FK를 만들 수 없다.
  • notification_deliveries는 발송 당시의 이벤트 스냅샷이므로 원본 삭제가 알림 이력을 삭제하게 만들지 않는다.

이 선택은 보존성과 결합도 측면에서는 유리하지만 고아 참조가 생길 수 있다. 따라서 애플리케이션 검증과 정기 무결성 점검이 필요하다. 반드시 강한 정합성이 필요한 소유 관계에는 계속 물리 FK를 사용한다.

6.12 인덱스는 조회 패턴과 유일성 중심으로 구성한다

현재 인덱스는 다음 용도에 집중한다.

  • FK 조인: project_id, channel_id, feedback_id, issue_id
  • 목록 조회: 생성일, 삭제 여부, 상태
  • 이름 중복 방지: 프로젝트명, 프로젝트 내 채널·카테고리·이슈명
  • 외부 원본 중복 방지: source identity
  • 외부 이슈 중복 방지: provider/project/external ID
  • 요청 재시도 방지: idempotency key

UUID v7과 (created_at, id) 조합은 같은 시각에 생성된 데이터도 안정적으로 정렬하고 cursor pagination의 마지막 위치를 정확하게 표현한다. 데이터가 증가한 뒤에는 추측으로 인덱스를 늘리기보다 Slow Query Log와 실제 실행 계획을 기준으로 추가한다.

7. 재추출 필요 조건

아래 작업 후에는 이 문서를 다시 생성해야 한다.

  1. TypeORM UUID migration 적용 또는 변경
  2. Secretary Alembic migration 적용으로 support_* 테이블 생성
  3. FK·Unique Key·인덱스 변경
  4. 스테이징/운영 스키마와 로컬 스키마 비교