Skip to content

프로퍼티 설정 백엔드 참고

범위

Office가 업무·권한 범위의 정본이고, 프로퍼티는 Office의 건물 기본정보와 선택한 data.go.kr 건축물대장 HUB 출처 스냅샷을 확장 저장한다. 별도 프로퍼티 레코드를 중복 생성하지 않는다.

스키마

Migration: supabase/migrations/20260713190000_office_property_profile.sql

public.offices 추가 컬럼:

컬럼타입의미
property_external_codetext건축물대장 관리 PK mgmBldrgstPk
property_data_sourcetext허용값 DATA_GO_KR_BUILDING_HUB
property_data_checked_attimestamptz선택 결과를 마지막 저장한 서버 시각
property_datajsonb표제부·필지·면적 유형·세대별 면적의 출처 스냅샷

이름·주소·호실 수는 기존 display_name, address, unit_count가 정본이다.

쓰기 계약

RPC update_company_office_property(uuid, text, text, text, integer, text, text, jsonb):

  • 호출자는 대상 Office Company의 활성 owner | contract_admin | company_admin 멤버여야 한다.
  • 이름은 필수, 호실 수는 0 이상이다.
  • 외부 건축물대장 PK가 있으면 출처는 DATA_GO_KR_BUILDING_HUB여야 한다.
  • 관리코드 offices.code는 변경하지 않는다.
  • 외부 PK가 있으면 property_data_checked_at=now()를 서버에서 기록한다.
  • security definer와 고정 search_path를 사용하고 authenticated만 실행할 수 있다.

공공데이터 함수

Edge Function: supabase/functions/property-public-data/index.ts

  • 인증된 Supabase 사용자만 호출한다.
  • Secret: DATA_GO_KR_SERVICE_KEY. 브라우저나 VITE_* 환경변수에 두지 않는다.
  • mode=health: 키 구성 여부와 공급자 식별자만 반환한다.
  • mode=search: { name, address }를 받아 아래 순서로 조회한다.

조회 순서:

  1. 주소 괄호의 법정동을 추출한다.
  2. StanReginCd/getStanReginCdList로 10자리 법정동코드를 확인한다.
  3. 코드 앞 5자리를 sigunguCd, 뒤 5자리를 bjdongCd로 나눈다.
  4. BldRgstHubService/getBrTitleInfo를 페이지 순회하고 건물명·도로명주소로 표제부 후보를 채점한다.
  5. 후보 필지의 sigunguCd, bjdongCd, platGbCd, bun, jigetBrExposPubuseAreaInfo를 전 페이지 조회한다.
  6. 전유공용면적 행은 세대 대장의 mgmBldrgstPk로 묶고 exposPubuseGbCdNm의 전유/공용 면적을 합산한다. PK가 없는 레거시 응답만 dongNm + hoNm을 fallback으로 사용한다. 공용면적 행의 층은 세대 식별자에 넣지 않는다.
  7. 세대 용도는 전유부 행의 etcPurps 또는 mainPurpsCdNm만 사용한다. 공용부 용도 문자열은 면적 분류에만 사용하며 세대 용도나 면적 유형 키에 섞지 않는다. 공용면적은 etcPurps, mainPurpsCdNm, mainAtchGbCdNm으로 주거공용과 기타공용을 분류한다. 주차·기계·전기·관리·경비 등은 기타공용, 복도·계단·승강기·ELEV·홀·현관·벽체공용·화장실 등은 주거공용이다. 명확한 용도가 없으면 주거공용 임시 분류와 경고를 남긴다.
  8. 다음 A~E를 소수점 넷째 자리까지 계산한다.
    • A exclusiveArea
    • B residentialCommonArea
    • C otherCommonArea
    • D supplyArea = A + B
    • E contractArea = A + B + C
  9. 동일한 용도·A~E 조합을 면적 유형으로 묶는다.

공식 API:

  • https://apis.data.go.kr/1741000/StanReginCd/getStanReginCdList
  • https://apis.data.go.kr/1613000/BldRgstHubService/getBrTitleInfo
  • https://apis.data.go.kr/1613000/BldRgstHubService/getBrExposPubuseAreaInfo

응답과 스냅샷

후보 응답은 externalCode, name, roadAddress, lotAddress, unitCount, approvalDate, propertyType, legalDongCode, source, areaSummary, snapshot을 포함한다.

snapshot은 다음을 보존한다.

  • source, 실제 조회 endpoint, 공급·계약면적 산식
  • 법정동 코드와 필지 요청 파라미터
  • 선택한 표제부 원본
  • 면적 유형별 호실 수와 면적
  • 세대별 dongName, unitName, floorName, usage, A~E 면적 — 후보 단계 UI가 호실 목록 disclosure로 그대로 노출하므로 계약 필드다(이름·순서 변경 시 프론트 동반 수정)
  • 공용면적 분류 근거와 임시 분류 경고 여부

API 키와 전체 HTTP 오류 본문은 저장하거나 고객에게 반환하지 않는다. 장애 추적에는 traceId만 노출한다.

건축HUB의 mgmBldrgstPk는 JavaScript 안전 정수 범위를 넘을 수 있다. parsePublicDataJson()은 JSON 파싱 전에 대형 PK 필드를 문자열로 보존한다. UI는 이 내부 식별자를 노출하지 않고 서버 연결·감사 추적에만 사용한다.

불변식과 엣지케이스

  • K-apt 단지 API는 이 흐름에서 사용하지 않는다.
  • 도로명주소만으로 법정동을 확정할 수 없으므로 현재 계약은 괄호 법정동을 요구한다.
  • 공공데이터 조회 실패 시 기존 Office 정보는 바뀌지 않는다.
  • 면적 행에 hoNm이 없거나 전유/공용 구분을 판별할 수 없으면 세대 합산에서 제외한다.
  • 공용면적의 주거/기타 분리는 건축물대장 용도 문자열 기반 판정이다. 임시 분류 건수는 classificationWarningCount로 UI에 노출한다.
  • 관리비 부과 면적은 exclusiveArea 또는 contractArea만 사용한다. 분양면적 파생값은 저장하지 않는다.
  • 운영 클라이언트는 property_data.units[]만 부과 유닛으로 승격한다. areaTypes[].count나 데모 시드를 운영 유닛으로 간주하지 않는다.
  • 프로퍼티 유닛 목록과 부과 배분값 화면은 별도 목록 데이터를 만들지 않고 동일한 property_data.units[]를 읽는다. 고객 표시명은 동-층-호 파생값이며 원문 unitCode와 Office 이름은 고유 식별용으로만 보존한다.
  • 면적 조회 결과가 0호실이거나 areaSummary.unitCount가 Office unit_count와 다른 후보는 UI에서 경고하고 연결을 비활성화한다. 클라이언트 이벤트에서도 같은 검사를 반복해 우회 연결을 막는다.
  • 개발계정 호출량을 보호하기 위해 사용자의 명시적 조회 액션에서만 페이지 순회를 수행한다.

배포 순서

  1. Migration 적용.
  2. Edge Function 배포.
  3. Supabase Secret DATA_GO_KR_SERVICE_KEY 설정.
  4. health 인증 호출, 법정동 코드 확인, 실제 주소의 표제부와 466호실 전유공용면적 검증.
  5. 연결 후 property_data.units[] 466건과 offices.unit_count=466의 일치 및 데모 배분라인 0건을 확인한다.