데이터베이스 명세

데이터베이스 명세 문서

CheonYakPlanet 백엔드 관련 문서입니다.

데이터베이스 명세


📊 데이터베이스 개요


청약플래닛은 체계적인 데이터 관리를 위해 카탈로그 분리(`catalog = "planet"`)가 적용된 MySQL 데이터베이스를 사용합니다. 모든 엔티티는 포괄적인 감사 추적 및 소프트 삭제 패턴을 구현합니다.


🏗️ 데이터베이스 아키텍처


기본 패턴


📋 테이블 명세


👤 사용자 관리


user_info

CREATE TABLE planet.user_info (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    email VARCHAR(255) NOT NULL UNIQUE,
    password VARCHAR(255) NOT NULL,
    username VARCHAR(100) NOT NULL,
    phone_number VARCHAR(20),
    role ENUM('USER', 'ADMIN') DEFAULT 'USER',
    status ENUM('ACTIVE', 'WITHDRAWN') DEFAULT 'ACTIVE',

    -- 청약 자격을 위한 재정 프로필
    property DOUBLE COMMENT '부동산 자산 (만원)',
    income INTEGER COMMENT '월소득 (만원)',
    is_married BOOLEAN COMMENT '결혼 여부',
    num_child INTEGER COMMENT '자녀 수',
    num_house INTEGER COMMENT '주택 보유 수',

    -- 관심 지역 (최대 5개)
    interest_local_1 VARCHAR(100) COMMENT '관심지역 1',
    interest_local_2 VARCHAR(100) COMMENT '관심지역 2',
    interest_local_3 VARCHAR(100) COMMENT '관심지역 3',
    interest_local_4 VARCHAR(100) COMMENT '관심지역 4',
    interest_local_5 VARCHAR(100) COMMENT '관심지역 5',

    -- Auditing 필드
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    created_by VARCHAR(255),
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    updated_by VARCHAR(255),
    deleted_at DATETIME,
    deleted_by VARCHAR(255)
);

인덱스:


user_token

CREATE TABLE planet.user_token (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    email VARCHAR(255) NOT NULL,
    access_token TEXT NOT NULL,
    refresh_token TEXT NOT NULL,
    access_token_expiry DATETIME NOT NULL,
    refresh_token_expiry DATETIME NOT NULL,
    blacklisted BOOLEAN DEFAULT FALSE,

    -- Auditing 필드
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

인덱스:


🏠 청약 관리


subscription_info (주요 청약 데이터)

CREATE TABLE planet.subscription_info (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,

    -- 기본 정보
    house_nm VARCHAR(255) COMMENT '주택명',
    house_manage_no VARCHAR(50) UNIQUE COMMENT '주택관리번호',
    pblanc_no VARCHAR(50) COMMENT '공고번호',

    -- 위치 정보
    hssply_adres TEXT COMMENT '공급위치',
    region VARCHAR(50) COMMENT '시/도',
    city VARCHAR(50) COMMENT '시/군/구',
    district VARCHAR(50) COMMENT '읍/면/동',

    -- 접수 기간
    rcept_bgnde DATE COMMENT '청약접수시작일',
    rcept_endde DATE COMMENT '청약접수종료일',
    spsply_rcept_bgnde DATE COMMENT '특별공급접수시작일',
    spsply_rcept_endde DATE COMMENT '특별공급접수종료일',

    -- 순위 시스템 날짜
    gnrl_rnk1_crsparea_rcptde DATE COMMENT '1순위해당지역접수일',
    gnrl_rnk1_etc_area_rcptde DATE COMMENT '1순위기타지역접수일',
    gnrl_rnk2_crsparea_rcptde DATE COMMENT '2순위해당지역접수일',
    gnrl_rnk2_etc_area_rcptde DATE COMMENT '2순위기타지역접수일',

    -- 당첨자 발표 및 계약
    przwner_presnatn_de DATE COMMENT '당첨자발표일',
    cntrct_cncls_bgnde DATE COMMENT '계약체결시작일',
    cntrct_cncls_endde DATE COMMENT '계약체결종료일',

    -- 사업 정보
    bsns_mby_nm VARCHAR(255) COMMENT '사업주체명',
    cnstrct_entrps_nm VARCHAR(255) COMMENT '건설업체명',
    mdhs_telno VARCHAR(20) COMMENT '문의처전화번호',
    hmpg_adres TEXT COMMENT '홈페이지주소',

    -- 규제 정보
    parcprc_uls_at CHAR(1) COMMENT '분양가상한제적용여부',
    speclt_rdn_earth_at CHAR(1) COMMENT '투기과열지구여부',
    public_house_earth_at CHAR(1) COMMENT '공공주택지구여부',
    lrscl_bldlnd_at CHAR(1) COMMENT '대규모택지개발지구여부',

    -- 좌표 (지오코딩으로 추가됨)
    latitude DOUBLE COMMENT '위도',
    longitude DOUBLE COMMENT '경도',

    -- Auditing 필드
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    created_by VARCHAR(255),
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    updated_by VARCHAR(255),
    deleted_at DATETIME,
    deleted_by VARCHAR(255)
);

인덱스:


subscription_price_info

CREATE TABLE planet.subscription_price_info (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    subscription_info_id BIGINT NOT NULL,

    housing_type VARCHAR(100) COMMENT '주택유형',
    supply_area DECIMAL(10,4) COMMENT '공급면적',
    supply_price BIGINT COMMENT '공급가격',
    supply_count INTEGER COMMENT '공급세대수',
    spsply_count INTEGER COMMENT '특별공급세대수',
    gnrl_spsply_count INTEGER COMMENT '일반공급세대수',

    -- 순위 우선 지불
    gnrl_rnk1_etc_area_apt_cnt INTEGER COMMENT '1순위기타지역세대수',
    gnrl_rnk2_etc_area_apt_cnt INTEGER COMMENT '2순위기타지역세대수',

    -- 지불 정보
    lfe_frst_lwet_top_amount BIGINT COMMENT '생애최초저소득층상위소득',
    old_parnts_suport_spsply_top_amount BIGINT COMMENT '노부모부양특별공급상위소득',
    nwwds_spsply_top_amount BIGINT COMMENT '신혼부부특별공급상위소득',

    -- Auditing 필드
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    created_by VARCHAR(255),
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    updated_by VARCHAR(255),
    deleted_at DATETIME,
    deleted_by VARCHAR(255),

    FOREIGN KEY (subscription_info_id) REFERENCES subscription_info(id)
);

subscription_special_supply_target

CREATE TABLE planet.subscription_special_supply_target (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    subscription_info_id BIGINT NOT NULL,

    -- 특별 공급 유형
    supply_count_multichild INTEGER COMMENT '다자녀가구공급세대수',
    supply_count_newlywed INTEGER COMMENT '신혼부부공급세대수',
    supply_count_first INTEGER COMMENT '생애최초공급세대수',
    supply_count_old_parent INTEGER COMMENT '노부모부양공급세대수',
    supply_count_institution_recommend INTEGER COMMENT '기관추천공급세대수',
    supply_count_etc INTEGER COMMENT '기타공급세대수',
    supply_count_disabled INTEGER COMMENT '장애인공급세대수',
    supply_count_youth INTEGER COMMENT '청년공급세대수',
    supply_count_newborn INTEGER COMMENT '신생아공급세대수',

    -- Auditing 필드
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    created_by VARCHAR(255),
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    updated_by VARCHAR(255),
    deleted_at DATETIME,
    deleted_by VARCHAR(255),

    FOREIGN KEY (subscription_info_id) REFERENCES subscription_info(id)
);

subscription_like

CREATE TABLE planet.subscription_like (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    subscription_id BIGINT NOT NULL,
    user_email VARCHAR(255) NOT NULL,

    -- 빠른 액세스를 위한 캐시된 청약 데이터
    house_nm VARCHAR(255),
    hssply_adres TEXT,
    region VARCHAR(50),
    rcept_bgnde DATE,

    -- Auditing 필드
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    created_by VARCHAR(255),
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    updated_by VARCHAR(255),
    deleted_at DATETIME,
    deleted_by VARCHAR(255),

    UNIQUE KEY uk_subscription_user (subscription_id, user_email, deleted_at)
);

💬 커뮤니티 플랫폼


Post

CREATE TABLE planet.Post (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(100) NOT NULL,
    title VARCHAR(255) NOT NULL,
    content TEXT NOT NULL,
    views BIGINT DEFAULT 0,
    likes INTEGER DEFAULT 0,
    dislikes INTEGER DEFAULT 0,
    is_blind BOOLEAN DEFAULT FALSE,
    category ENUM('REVIEW', 'SUBSCRIPTION_INFO', 'SUBSCRIPTION_INQUIRY', 'INFO_SHARE', 'FREE_TALK') NOT NULL,

    -- Auditing 필드
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    created_by VARCHAR(255),
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    updated_by VARCHAR(255),
    deleted_at DATETIME,
    deleted_by VARCHAR(255)
);

인덱스:


Comment

CREATE TABLE planet.Comment (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    post_id BIGINT NOT NULL,
    username VARCHAR(100) NOT NULL,
    content TEXT NOT NULL,

    -- Auditing 필드
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    created_by VARCHAR(255),
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    updated_by VARCHAR(255),
    deleted_at DATETIME,
    deleted_by VARCHAR(255),

    FOREIGN KEY (post_id) REFERENCES Post(id)
);

Reply

CREATE TABLE planet.Reply (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    comment_id BIGINT NOT NULL,
    username VARCHAR(100) NOT NULL,
    content TEXT NOT NULL,

    -- Auditing 필드
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    created_by VARCHAR(255),
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    updated_by VARCHAR(255),
    deleted_at DATETIME,
    deleted_by VARCHAR(255),

    FOREIGN KEY (comment_id) REFERENCES Comment(id)
);

post_reaction

CREATE TABLE planet.post_reaction (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    post_id BIGINT NOT NULL,
    email VARCHAR(255) NOT NULL,
    reaction_type ENUM('LIKE', 'DISLIKE') NOT NULL,

    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,

    FOREIGN KEY (post_id) REFERENCES Post(id),
    UNIQUE KEY uk_post_user_reaction (post_id, email)
);

💰 금융 상품


houseloan_product

CREATE TABLE planet.houseloan_product (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,

    -- 상품 정보
    fin_co_nm VARCHAR(255) COMMENT '금융회사명',
    fin_prdt_nm VARCHAR(255) COMMENT '금융상품명',
    join_way TEXT COMMENT '가입방법',
    loan_inci_expn TEXT COMMENT '대출부대비용',
    erly_rpay_fee TEXT COMMENT '중도상환수수료',
    dly_rate TEXT COMMENT '연체이자율',
    loan_lmt TEXT COMMENT '대출한도',

    -- 금리
    lend_rate_type_nm VARCHAR(100) COMMENT '대출금리유형명',
    lend_rate_min DOUBLE COMMENT '대출최저금리',
    lend_rate_max DOUBLE COMMENT '대출최고금리',
    lend_rate_avg DOUBLE COMMENT '대출평균금리',

    -- Auditing 필드
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    created_by VARCHAR(255),
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    updated_by VARCHAR(255),
    deleted_at DATETIME,
    deleted_by VARCHAR(255)
);

mortgage_product

CREATE TABLE planet.mortgage_product (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,

    -- 상품 정보
    fin_co_nm VARCHAR(255) COMMENT '금융회사명',
    fin_prdt_nm VARCHAR(255) COMMENT '금융상품명',
    mrtg_type VARCHAR(50) COMMENT '담보유형코드',
    mrtg_type_nm VARCHAR(100) COMMENT '담보유형명',

    -- 금리 및 약관에 대한 houseloan_product와 동일한 구조
    join_way TEXT COMMENT '가입방법',
    loan_inci_expn TEXT COMMENT '대출부대비용',
    erly_rpay_fee TEXT COMMENT '중도상환수수료',
    dly_rate TEXT COMMENT '연체이자율',
    loan_lmt TEXT COMMENT '대출한도',

    lend_rate_type_nm VARCHAR(100) COMMENT '대출금리유형명',
    lend_rate_min DOUBLE COMMENT '대출최저금리',
    lend_rate_max DOUBLE COMMENT '대출최고금리',
    lend_rate_avg DOUBLE COMMENT '대출평균금리',

    -- Auditing 필드
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    created_by VARCHAR(255),
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    updated_by VARCHAR(255),
    deleted_at DATETIME,
    deleted_by VARCHAR(255)
);

🏫 인프라 데이터


school

CREATE TABLE planet.school (
    school_id VARCHAR(50) PRIMARY KEY COMMENT '학교ID',
    school_name VARCHAR(255) NOT NULL COMMENT '학교명',
    category VARCHAR(50) COMMENT '학교급 (초등학교, 중학교, 고등학교)',
    type1 VARCHAR(50) COMMENT '설립구분 (국립, 공립, 사립)',
    type2 VARCHAR(50) COMMENT '운영구분',
    status VARCHAR(50) COMMENT '운영상태',
    address TEXT COMMENT '주소',
    latitude DOUBLE COMMENT '위도',
    longitude DOUBLE COMMENT '경도',

    -- Auditing 필드
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

인덱스:


station

CREATE TABLE planet.station (
    number VARCHAR(50) PRIMARY KEY COMMENT '역번호',
    operator VARCHAR(100) COMMENT '운영회사',
    line VARCHAR(100) COMMENT '노선명',
    type VARCHAR(50) COMMENT '교통수단 (지하철, 기차, 버스)',
    nm_kor VARCHAR(255) NOT NULL COMMENT '한국어역명',
    nm_eng VARCHAR(255) COMMENT '영어역명',
    is_transfer CHAR(1) COMMENT '환승역여부',
    platform TEXT COMMENT '플랫폼정보',
    longitude DOUBLE COMMENT '경도',
    latitude DOUBLE COMMENT '위도',
    addr_nm TEXT COMMENT '주소',

    -- Auditing 필드
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

public_facility

CREATE TABLE planet.public_facility (
    present_sn VARCHAR(50) PRIMARY KEY COMMENT '시설고유번호',
    lclas_cl VARCHAR(100) COMMENT '대분류',
    mlsfc_cl VARCHAR(100) COMMENT '중분류',
    sclas_cl VARCHAR(100) COMMENT '소분류',
    dgm_nm VARCHAR(255) COMMENT '시설명',
    dgm_ar DOUBLE COMMENT '시설면적',
    signgu_se VARCHAR(100) COMMENT '시군구구분',
    geometry TEXT COMMENT 'GIS좌표정보',
    latlon_geometry TEXT COMMENT '위경도좌표',
    longitude DOUBLE COMMENT '경도',
    latitude DOUBLE COMMENT '위도'
);

🏘️ 행정 데이터


sggcode

CREATE TABLE planet.sggcode (
    sgg_cd5 INTEGER PRIMARY KEY COMMENT '5자리시군구코드',
    sgg_cd_nm TEXT COMMENT '시군구명',
    sgg_cd_nm_region VARCHAR(100) COMMENT '시도명',
    sgg_cd_nm_city VARCHAR(100) COMMENT '시군구명'
);

📊 부동산 가격 데이터


real_estate_price

CREATE TABLE planet.real_estate_price (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    region VARCHAR(50) COMMENT '시도',
    sgg_cd VARCHAR(10) COMMENT '시군구코드',
    umd_nm VARCHAR(100) COMMENT '읍면동명',
    apt_nm VARCHAR(255) COMMENT '아파트명',
    deal_amount BIGINT COMMENT '거래금액',
    exclu_use_ar DECIMAL(10,4) COMMENT '전용면적',
    deal_date DATE COMMENT '거래일',
    floor_info VARCHAR(10) COMMENT '층정보',
    build_year INTEGER COMMENT '건축년도',

    -- Auditing 필드
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

real_estate_price_summary

CREATE TABLE planet.real_estate_price_summary (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    region VARCHAR(50) COMMENT '시도',
    sgg_cd_nm VARCHAR(100) COMMENT '시군구명',
    deal_year INTEGER COMMENT '거래년도',
    deal_month INTEGER COMMENT '거래월',
    deal_count INTEGER COMMENT '거래건수',
    price_per_ar BIGINT COMMENT '면적당평균가격',

    UNIQUE KEY uk_region_year_month (region, sgg_cd_nm, deal_year, deal_month)
);

🔗 엔티티 관계


주요 관계

  • `사용자` ← 1:N → `게시물` (사용자 이름 경유)
  • `게시물` ← 1:N → `댓글` ← 1:N → `답글`
  • `사용자` ← 1:N → `청약 좋아요` ← N:1 → `청약 정보`
  • `사용자` ← 1:N → `게시물 반응` ← N:1 → `게시물`
  • `청약 정보` ← 1:N → `청약 가격 정보`
  • `청약 정보` ← 1:N → `청약 특별 공급 대상`

  • 참조 무결성

  • **소프트 삭제**: 관련 엔티티는 소프트 삭제 후에도 참조를 유지합니다.
  • **종속 규칙**: 댓글 및 답글은 부모 게시물과 함께 계단식으로 삭제됩니다.
  • **외래 키 제약 조건**: 적절한 경우 데이터베이스 수준에서 강제됩니다.

  • 🚀 성능 고려 사항


    인덱싱 전략

  • **복합 인덱스**: 자주 쿼리되는 조합 (지역 + 도시, 날짜 범위)
  • **부분 인덱스**: 활성 레코드의 `deleted_at IS NULL`

  • 쿼리 최적화

  • **소프트 삭제 쿼리**: 항상 WHERE 절에 `deleted_at IS NULL`을 포함합니다.
  • **페이지네이션**: 대규모 결과 집합에 커서 기반 페이지네이션을 사용합니다.
  • **일괄 작업**: 정부 API 데이터 업데이트를 위한 대량 삽입

  • 🔧 유지보수 절차


    데이터 정리


    백업 전략




    최종 업데이트: 2025-06-26

    스키마 버전: 1.0

    데이터베이스 엔진: MySQL 8.0 이상