giftcon_dev/docs/product_table/pfy_phase1_schema.md
sungro815 b0545ab5b9 관리자 상품관리 완료
웹사이트 상품리스트 상세보기 작업중
2026-02-20 18:11:03 +09:00

22 KiB

Phase 1 DB 스키마 (상품등록/전시 + SKU + 판매채널 + 결제수단 + 자사핀재고 + 이미지)

  • 신규 테이블은 모두 pfy_ 접두사 사용
  • MariaDB (InnoDB, utf8mb4) 기준
  • 레거시/기존 회원/관리자 테이블과 FK는 1단계에서는 걸지 않음(컬럼만 준비)
    → 2단계(주문/결제)부터 FK/연동 강화

1) pfy_categories : 1차/2차 카테고리 트리

  • parent_id 로 1차/2차 구성
  • sort / is_active 로 전시 제어
CREATE TABLE IF NOT EXISTS pfy_categories (
                                              id            BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'PK',
                                              parent_id     BIGINT UNSIGNED NULL COMMENT '상위 카테고리 ID (1차는 NULL, 2차는 1차의 id)',
                                              name          VARCHAR(100) NOT NULL COMMENT '카테고리명',
                                              slug          VARCHAR(120) NOT NULL COMMENT 'URL/식별용 슬러그(유니크 권장)',
                                              sort          INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '정렬값(작을수록 먼저)',
                                              is_active     TINYINT(1) NOT NULL DEFAULT 1 COMMENT '노출 여부(1=노출,0=숨김)',
                                              icon_path     VARCHAR(255) NULL COMMENT '아이콘 경로(선택)',
                                              banner_path   VARCHAR(255) NULL COMMENT '배너 경로(선택)',
                                              desc_short    VARCHAR(255) NULL COMMENT '카테고리 짧은 설명(선택)',
                                              created_at    DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '생성일',
                                              updated_at    DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일',
                                              PRIMARY KEY (id),
                                              UNIQUE KEY uk_pfy_categories_slug (slug),
                                              KEY idx_pfy_categories_parent_sort (parent_id, sort)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
    COMMENT='[PFY] 카테고리(1차/2차 트리)';

2) pfy_media_files : 업로드된 파일(상품 이미지 등) 메타데이터

  • 업로드된 이미지를 “선택”해서 상품에 연결하는 용도
CREATE TABLE IF NOT EXISTS pfy_media_files (
                                               id                BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'PK',
                                               kind              ENUM('image','file') NOT NULL DEFAULT 'image' COMMENT '파일 종류',
                                               disk              VARCHAR(40) NOT NULL DEFAULT 'public' COMMENT '스토리지 디스크명(Laravel disks)',
                                               path              VARCHAR(500) NOT NULL COMMENT '스토리지 상대 경로',
                                               original_name     VARCHAR(255) NULL COMMENT '원본 파일명',
                                               mime              VARCHAR(120) NULL COMMENT 'MIME 타입',
                                               size_bytes        BIGINT UNSIGNED NULL COMMENT '파일 크기(bytes)',
                                               width             INT UNSIGNED NULL COMMENT '이미지 폭(px, 이미지인 경우)',
                                               height            INT UNSIGNED NULL COMMENT '이미지 높이(px, 이미지인 경우)',
                                               checksum_sha256   CHAR(64) NULL COMMENT '파일 무결성 체크섬(선택)',
                                               uploaded_by_admin_id BIGINT UNSIGNED NULL COMMENT '업로드 관리자 ID(레거시/기존 admin_users 연동 예정)',
                                               created_at        DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '생성일',
                                               updated_at        DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일',
                                               PRIMARY KEY (id),
                                               KEY idx_pfy_media_files_kind (kind),
                                               KEY idx_pfy_media_files_created (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
    COMMENT='[PFY] 업로드 파일 메타(상품이미지 선택/재사용)';

3) pfy_products : 상품(전시 단위)

  • 상품명/타입/판매기간/상태/대표이미지/매입가능 여부
CREATE TABLE IF NOT EXISTS pfy_products (
                                            id              BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'PK',
                                            category_id     BIGINT UNSIGNED NOT NULL COMMENT '2차 카테고리 ID(pfy_categories.id)',
                                            name            VARCHAR(160) NOT NULL COMMENT '상품명(수기)',
                                            type            ENUM('online','delivery') NOT NULL DEFAULT 'online' COMMENT '상품타입(온라인/배송)',
                                            is_buyback_allowed ENUM('Y','N') NOT NULL DEFAULT 'Y' COMMENT '매입가능여부(Y=가능,N=불가)',
                                            sale_period_type ENUM('always','ranged') NOT NULL DEFAULT 'always' COMMENT '판매기간 타입(상시/기간설정)',
                                            sale_start_at   DATETIME NULL COMMENT '판매 시작일(기간설정일 때)',
                                            sale_end_at     DATETIME NULL COMMENT '판매 종료일(기간설정일 때)',
                                            status          ENUM('active','hidden','soldout') NOT NULL DEFAULT 'active' COMMENT '노출상태(노출/숨김/품절)',
                                            main_image_id   BIGINT UNSIGNED NULL COMMENT '대표 이미지(pfy_media_files.id)',
                                            created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '생성일',
                                            updated_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일',
                                            PRIMARY KEY (id),
                                            KEY idx_pfy_products_category (category_id),
                                            KEY idx_pfy_products_status (status),
                                            KEY idx_pfy_products_sale_period (sale_period_type, sale_start_at, sale_end_at),
                                            CONSTRAINT fk_pfy_products_category
                                                FOREIGN KEY (category_id) REFERENCES pfy_categories(id)
                                                    ON UPDATE CASCADE ON DELETE RESTRICT,
                                            CONSTRAINT fk_pfy_products_main_image
                                                FOREIGN KEY (main_image_id) REFERENCES pfy_media_files(id)
                                                    ON UPDATE CASCADE ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
    COMMENT='[PFY] 상품(전시 단위)';

4) pfy_product_contents : 상품 상세 에디터 3종(1:1)

  • 상세설명/이용안내/주의사항 HTML 저장
CREATE TABLE IF NOT EXISTS pfy_product_contents (
                                                    product_id     BIGINT UNSIGNED NOT NULL COMMENT 'PK & FK(pfy_products.id)',
                                                    detail_html    LONGTEXT NULL COMMENT '상세설명(HTML)',
                                                    guide_html     LONGTEXT NULL COMMENT '이용안내(HTML)',
                                                    caution_html   LONGTEXT NULL COMMENT '주의사항(HTML)',
                                                    created_at     DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '생성일',
                                                    updated_at     DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일',
                                                    PRIMARY KEY (product_id),
                                                    CONSTRAINT fk_pfy_product_contents_product
                                                        FOREIGN KEY (product_id) REFERENCES pfy_products(id)
                                                            ON UPDATE CASCADE ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
    COMMENT='[PFY] 상품 에디터 컨텐츠(상세/안내/주의)';

5) pfy_product_skus : 권종/가격 단위(SKU)

  • 정상가/할인율/판매가(스냅샷 계산 저장 추천)
  • 재고는 stock_mode 로 (연동판매는 infinite, 자사핀은 limited)
CREATE TABLE `pfy_product_skus` (
                                    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'PK',
                                    `product_id` bigint(20) unsigned NOT NULL COMMENT '상품 ID (pfy_products.id)',

                                    `sku_code` varchar(60) DEFAULT NULL COMMENT '내부 SKU 코드(선택). 운영상 필요 시 사용',

                                    `face_value` int(10) unsigned NOT NULL COMMENT '권면가(원) 예: 10000',
                                    `normal_price` int(10) unsigned NOT NULL COMMENT '정상가(원)',
                                    `discount_rate` decimal(5,2) NOT NULL DEFAULT 0.00 COMMENT '할인율(%) 예: 3.50',
                                    `sale_price` int(10) unsigned NOT NULL COMMENT '판매가(원) = floor(normal_price*(100-discount_rate)/100)',

                                    `status` enum('active','hidden') NOT NULL DEFAULT 'active' COMMENT '노출상태(active=노출, hidden=숨김)',
                                    `sort` int(10) unsigned NOT NULL DEFAULT 0 COMMENT '정렬(작을수록 우선)',

                                    `created_admin_id` bigint(20) unsigned DEFAULT NULL COMMENT '등록 관리자 ID',
                                    `updated_admin_id` bigint(20) unsigned DEFAULT NULL COMMENT '수정 관리자 ID',

                                    `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '생성일시',
                                    `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일시',

                                    PRIMARY KEY (`id`),

                                    UNIQUE KEY `uniq_product_face` (`product_id`, `face_value`),
                                    KEY `idx_product` (`product_id`),
                                    KEY `idx_status_sort` (`status`, `sort`, `id`)

    /* FK를 쓰는 운영정책이면 아래 주석 해제 (기존 DB가 FK 거의 없으면 주석 유지 추천)
    , CONSTRAINT `fk_pfy_skus_product`
        FOREIGN KEY (`product_id`) REFERENCES `pfy_products` (`id`)
        ON DELETE RESTRICT ON UPDATE CASCADE
    */
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='PFY 상품 SKU(금액권/가격)';

6) pfy_sku_sale_channels : SKU 판매방식/연동채널 (기본: 1 SKU = 1 판매채널)

sale_mode

  • SELF_PIN : 자사핀 재고 판매
  • VENDOR_API_SHOW : 연동발행 후 우리 사이트에서 핀 표시
  • VENDOR_SMS : 업체가 SMS로 즉시 발송(핀 저장 최소화 가능)

vendor

  • DANAL / KORCULTURE / KPREPAID (필요 시 확장)
CREATE TABLE IF NOT EXISTS pfy_sku_sale_channels (
                                                     id                BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'PK',
                                                     sku_id            BIGINT UNSIGNED NOT NULL COMMENT 'SKU ID(pfy_product_skus.id)',
                                                     sale_mode         ENUM('SELF_PIN','VENDOR_API_SHOW','VENDOR_SMS') NOT NULL COMMENT '판매모드',
                                                     vendor            ENUM('NONE','DANAL','KORCULTURE','KPREPAID') NOT NULL DEFAULT 'NONE' COMMENT '연동업체(없으면 NONE)',
                                                     vendor_product_code VARCHAR(40) NULL COMMENT '업체 상품코드(예: DANAL CULTURE, KPREPAID 1231 등)',
                                                     pg                ENUM('NONE','DANAL') NOT NULL DEFAULT 'DANAL' COMMENT '결제 PG(현재 DANAL, 추후 확장)',
                                                     is_active         TINYINT(1) NOT NULL DEFAULT 1 COMMENT '사용여부(1=사용,0=중지)',
                                                     created_at        DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '생성일',
                                                     updated_at        DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일',
                                                     PRIMARY KEY (id),
                                                     UNIQUE KEY uk_pfy_sale_channels_sku (sku_id),
                                                     KEY idx_pfy_sale_channels_vendor (vendor, vendor_product_code),
                                                     CONSTRAINT fk_pfy_sale_channels_sku
                                                         FOREIGN KEY (sku_id) REFERENCES pfy_product_skus(id)
                                                             ON UPDATE CASCADE ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
    COMMENT='[PFY] SKU 판매채널/연동 설정';

7) pfy_payment_methods : 결제수단 마스터

  • code 를 PK로 사용(안정적, pivot에 쓰기 편함)
CREATE TABLE IF NOT EXISTS pfy_payment_methods (
                                                   code        VARCHAR(30) NOT NULL COMMENT '결제수단 코드(PK)',
                                                   label       VARCHAR(60) NOT NULL COMMENT '표시명(관리자/회원)',
                                                   is_active   TINYINT(1) NOT NULL DEFAULT 1 COMMENT '사용여부',
                                                   sort        INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '노출 정렬',
                                                   created_at  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '생성일',
                                                   updated_at  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일',
                                                   PRIMARY KEY (code),
                                                   KEY idx_pfy_payment_methods_active (is_active, sort)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
    COMMENT='[PFY] 결제수단 마스터';

8) pfy_sku_payment_methods : SKU별 허용 결제수단(pivot)

  • SKU마다 가능한 결제수단 체크박스 용도
CREATE TABLE IF NOT EXISTS pfy_sku_payment_methods (
                                                       sku_id              BIGINT UNSIGNED NOT NULL COMMENT 'SKU ID(pfy_product_skus.id)',
                                                       payment_method_code VARCHAR(30) NOT NULL COMMENT '결제수단 코드(pfy_payment_methods.code)',
                                                       is_enabled          TINYINT(1) NOT NULL DEFAULT 1 COMMENT '허용 여부(1=허용,0=비허용)',
                                                       created_at          DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '생성일',
                                                       updated_at          DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일',
                                                       PRIMARY KEY (sku_id, payment_method_code),
                                                       KEY idx_pfy_sku_pm_method (payment_method_code, is_enabled),
                                                       CONSTRAINT fk_pfy_sku_pm_sku
                                                           FOREIGN KEY (sku_id) REFERENCES pfy_product_skus(id)
                                                               ON UPDATE CASCADE ON DELETE CASCADE,
                                                       CONSTRAINT fk_pfy_sku_pm_method
                                                           FOREIGN KEY (payment_method_code) REFERENCES pfy_payment_methods(code)
                                                               ON UPDATE CASCADE ON DELETE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
    COMMENT='[PFY] SKU별 허용 결제수단 매핑';

9) pfy_pin_batches : 자사핀 입력 작업 단위(업로드/수기 등록)

CREATE TABLE IF NOT EXISTS pfy_pin_batches (
                                               id                 BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'PK',
                                               sku_id             BIGINT UNSIGNED NOT NULL COMMENT 'SKU ID(pfy_product_skus.id)',
                                               source_type        ENUM('upload','manual') NOT NULL COMMENT '핀 등록 방식(파일/수기)',
                                               original_filename  VARCHAR(255) NULL COMMENT '업로드 파일명(업로드일 때)',
                                               total_count        INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '배치 총 핀 개수',
                                               uploaded_by_admin_id BIGINT UNSIGNED NULL COMMENT '업로드 관리자 ID(레거시/기존 admin_users 연동 예정)',
                                               created_at         DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '생성일',
                                               updated_at         DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일',
                                               PRIMARY KEY (id),
                                               KEY idx_pfy_pin_batches_sku (sku_id, created_at),
                                               CONSTRAINT fk_pfy_pin_batches_sku
                                                   FOREIGN KEY (sku_id) REFERENCES pfy_product_skus(id)
                                                       ON UPDATE CASCADE ON DELETE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
    COMMENT='[PFY] 자사핀 입력 배치(업로드/수기)';

10) pfy_pin_items : 핀 1건 단위 재고(암호화 저장)

  • pin_enc : 쌍방향 암호화 저장값(복호화는 권한 통과한 구매자만)
  • pin_fingerprint : 중복핀 방지(유니크)

status

  • available : 사용가능(재고)
  • reserved : 결제 대기/재고 예약
  • sold : 판매 완료(주문 라인에 귀속될 예정)
  • revoked : 폐기/무효
  • recalled : 회수(미사용 회수 프로세스에서 사용)

1단계에서는 주문 테이블이 없으므로 reserved_order_id, sold_order_item_id 는 FK 없이 컬럼만 준비

CREATE TABLE IF NOT EXISTS pfy_pin_items (
                                             id                 BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'PK',
                                             batch_id           BIGINT UNSIGNED NOT NULL COMMENT '배치 ID(pfy_pin_batches.id)',
                                             sku_id             BIGINT UNSIGNED NOT NULL COMMENT 'SKU ID(pfy_product_skus.id)',
                                             pin_enc            TEXT NOT NULL COMMENT '핀 암호문(쌍방향 암호화 결과)',
                                             pin_fingerprint    CHAR(64) NOT NULL COMMENT '중복 방지 해시(sha256 등) - UNIQUE',
                                             status             ENUM('available','reserved','sold','revoked','recalled') NOT NULL DEFAULT 'available' COMMENT '재고 상태',
                                             reserved_order_id  BIGINT UNSIGNED NULL COMMENT '예약된 주문 ID(2단계에서 orders FK 예정)',
                                             reserved_until     DATETIME NULL COMMENT '예약 만료 시각(스케줄러로 자동 해제)',
                                             sold_order_item_id BIGINT UNSIGNED NULL COMMENT '판매된 주문아이템 ID(2단계에서 order_items FK 예정)',
                                             created_at         DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '생성일',
                                             updated_at         DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일',
                                             PRIMARY KEY (id),
                                             UNIQUE KEY uk_pfy_pin_items_fingerprint (pin_fingerprint),
                                             KEY idx_pfy_pin_items_sku_status (sku_id, status),
                                             KEY idx_pfy_pin_items_batch (batch_id),
                                             KEY idx_pfy_pin_items_reserved_until (status, reserved_until),
                                             CONSTRAINT fk_pfy_pin_items_batch
                                                 FOREIGN KEY (batch_id) REFERENCES pfy_pin_batches(id)
                                                     ON UPDATE CASCADE ON DELETE RESTRICT,
                                             CONSTRAINT fk_pfy_pin_items_sku
                                                 FOREIGN KEY (sku_id) REFERENCES pfy_product_skus(id)
                                                     ON UPDATE CASCADE ON DELETE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
    COMMENT='[PFY] 자사핀 재고(암호화 저장)';

(선택) 결제수단 초기 데이터 예시

INSERT INTO pfy_payment_methods(code,label,sort) VALUES
('CARD_GENERAL','신용카드(일반)',10),
('CARD_CASHBACK','신용카드(환급성)',11),
('MOBILE','휴대폰',20),
('VBANK','무통장입금',30),
('KBANKPAY','케이뱅크페이',40),
('PAYCOIN','페이코인',50);