청약한눈에 로고
청약한눈에

서비스 소개와 데이터 파이프라인

1. 서비스 소개

청약 공고는 LH 청약플러스, 청약홈, 마이홈포털, GH와 SH 홈페이지에 따로 올라와서, 무엇이 떴는지 확인하려면 사이트를 하나씩 돌아야 한다. 청약한눈에는 이 9개 소스를 매일 자동으로 수집해 하나의 목록, 검색, 캘린더로 제공한다. 이 페이지는 그 데이터가 수집부터 공개까지 지나는 경로와 현재 운영 상태를 그대로 보여준다.

2. 데이터 파이프라인

원천 사이트의 응답이 공개 목록에 닿기까지 거치는 단계.

수집 소스 9개공공 API 7 / HTML 파싱 2
  • LH 청약플러스
    공공데이터포털 임대공고 목록
    공공 API
  • 마이홈포털
    임대, 분양 목록 2개 엔드포인트 병합
    공공 API
  • 청약홈 APT
    odcloud 분양정보
    공공 API
  • 청약홈 오피스텔
    odcloud 분양정보
    공공 API
  • 청약홈 무순위
    odcloud 분양정보
    공공 API
  • 청약홈 공공임대
    odcloud 분양정보
    공공 API
  • 청약홈 임의공급
    odcloud 분양정보
    공공 API
  • GH 경기주택도시공사
    공식 홈페이지 게시판
    HTML 파싱
  • SH 서울주택도시공사
    공식 홈페이지 게시판
    HTML 파싱

GitHub Actions cron이 매일 06시와 18시(KST)에 수집 라우트를 기관별로 나눠 호출하고, 이어서 요약 라우트를 대기열이 빌 때까지(호출 20회 상한) 반복 호출한다.

  1. 1

    검증 게이트

    HTTP 200이어도 차단 페이지나 토막 응답이면 성공으로 치지 않는다. 응답을 ok, empty, blocked, error 4가지로 판정하고, 정상 0건(empty)을 실패와 분리해 빈 결과가 조용한 성공으로 흘러가는 것을 막는다.

  2. 2

    공통 스키마 정규화

    기관마다 다른 응답 구조를 하나의 공고 스키마(제목, 공고일, 접수기간, 공급기관, 플랫폼, 원문 링크, 첨부파일)로 변환한다. 값 검증은 Zod 스키마가 담당한다.

  3. 3

    상세 본문 보강

    GH와 SH는 목록에 본문이 없어 상세 페이지를 따로 읽어 붙인다. 이미 본문이 있는 항목은 건너뛰고, 실행당 조회 5건과 20초 예산에 도달하면 멈춘다. 개별 상세 실패는 기관 수집 전체를 실패로 만들지 않는다.

  4. 4

    저장 시점 중복 판별

    플랫폼과 플랫폼 공고 ID를 키로 이미 있으면 갱신, 없으면 삽입한다. ID가 없는 공고는 청약홈 주택관리번호, 그다음 제목과 공고일 순서로 판별한다. 같은 키의 동시 삽입은 DB 부분 유니크 인덱스가 막는다.

  5. 5

    AI 요약 대기열

    저장된 공고는 요약 상태 pending으로 큐에 들어간다. 요약 여부와 무관하게 공고는 즉시 노출되고, 요약 실패는 재시도 횟수 3회까지 큐에 남았다가 error로 격리된다.

  6. 6

    공개

    홈, 캘린더, 지역 허브는 미리 렌더해 5분마다 재생성하고, 검색은 질의마다 서버에서 렌더한다. 요약이 완료되면 해당 공고의 캐시 태그를 무효화해 바로 반영한다.

3. 라이브 운영 상태

아래 값은 운영 DB에서 직접 읽어 5분 단위로 갱신된다. 고정된 예시가 아니다.

적재된 공고
863건
published 여부와 무관한 전체 적재량
AI 요약 완료
658 / 863
요약 완료율 76%

공급기관별 분포

  • 한국토지주택공사411건
  • 민간건설사291건
  • 서울주택도시공사65건
  • 기타57건
  • 경기주택도시공사33건
  • 인천도시공사5건
  • 부산도시공사1건

최근 수집 실행 이력

기관별 실행마다 한 행이 기록된다. 목록 총건수는 원천이 알려온 전체 건수, 신규는 이번 실행에서 새로 저장된 건수다.

기관상태목록 총건수신규실행 시각(KST)
LH정상10002026. 8. 17. 22:38
LH정상10002026. 8. 17. 22:38
LH정상10002026. 8. 17. 22:37
LH정상10002026. 8. 17. 22:37
APPLYHOME_OPT정상61802026. 8. 17. 22:36
APPLYHOME_PUBLIC_RENT정상17802026. 8. 17. 22:36
APPLYHOME_REMNDR정상1,64702026. 8. 17. 22:36
APPLYHOME_URBAN정상60702026. 8. 17. 22:36
APPLYHOME_APT정상2,83602026. 8. 17. 22:36

4. 기술 스택과 선택 근거

  • Next.js 16 (App Router)

    공개 페이지를 서버에서 렌더하고 5분 단위로 재생성해, 요청마다 DB를 다시 읽지 않고도 목록이 최신 상태를 유지한다.

  • Supabase PostgreSQL + Drizzle ORM

    테이블 정의와 마이그레이션을 TypeScript 코드로 관리해 스키마 변경이 저장소 이력에 남는다.

  • Zod

    외부 API 응답, 캐시에서 되살린 값, LLM 출력을 같은 스키마로 런타임 검증해 형식이 어긋난 데이터가 화면까지 흘러가지 않게 한다.

  • cheerio

    공개 API가 없는 GH, SH 게시판은 HTML을 직접 파싱해야 목록을 얻을 수 있다.

  • Luxon

    배포 런타임이 UTC라 시간대를 고정하지 않으면 사용자에게 보이는 날짜와 마감 판정이 하루 어긋난다. 날짜 처리를 KST로 고정한다.

  • Tailwind CSS 4 + shadcn/ui

    색을 시맨틱 토큰으로 정의해 라이트와 다크 테마를 한 번에 대응한다.

  • Anthropic API SDK

    structured outputs로 요약을 고정 스키마 JSON으로 받고, 받은 값을 서비스의 Zod 스키마로 한 번 더 검증한다.

  • Vitest

    GH, SH 매퍼에 골든 스냅샷 회귀 테스트를 걸어 원천 사이트 구조가 바뀌면 CI에서 먼저 드러나게 한다.

  • Biome

    린트와 포매터를 한 도구로 합쳐 CI 게이트를 단순하게 유지한다.

  • GitHub Actions

    별도 서버 없이 스케줄 실행과 CI 게이트를 같은 저장소에서 운용한다.

5. 설계 결정

결정 기록(ADR) 중 이 서비스의 동작을 가장 크게 가른 네 가지.

  • 공개 페이지는 SSR 대신 ISR로 (ADR-002)

    공고는 하루 2회 수집으로 갱신되므로 요청마다 DB를 다시 읽을 이유가 없다. 페이지와 조회 함수를 모두 5분 재검증으로 맞추고, 관리자 편집이나 요약 완료처럼 즉시 반영이 필요한 시점에는 캐시 태그로 무효화한다.

  • 요약은 공고 노출을 막지 않는다 (ADR-003)

    요약이 붙지 않아도 공고는 수집 즉시 공개한다. 요약 상태를 공고 레코드의 컬럼(summaryStatus, retryCount, lastError)에 두어 요약 실패가 수집 실패로 번지지 않게 분리했다.

  • 요약 LLM은 상용 API를 먼저 쓴다 (ADR-011)

    원계획은 로컬 LLM이었으나 요약 품질이 미실측이었고 개인 장비의 상시 가동에 의존했다. 요약은 공고당 1회만 돌고 결과를 DB에 저장하므로 비용이 사용자 수가 아니라 공고 수에 비례한다. 적재분 전량을 채우는 초기 1회 작업은 개발 도구로 실행했고, 이후 신규분은 스케줄 경로가 API로 처리한다. 로컬 전환은 폐기가 아니라 실측 후 재결정으로 보류했다.

  • 스케줄러는 GitHub Actions cron (ADR-012)

    요약이 상용 API로 바뀌면서 오프라인 큐를 폴링할 오케스트레이터가 필요 없어졌다. 새 인프라를 설치하는 대신 워크플로 1개로 수집과 요약을 잇고, 실패하면 워크플로가 실패로 표시되어 GitHub 알림이 통지 역할을 한다.

6. 알려진 한계와 다음 단계

현재 해결되지 않은 항목을 그대로 적는다.

  • 신규 공고의 요약은 아직 자동으로 붙지 않는다

    적재된 863건 중 658건에 요약이 있다. 이 요약은 초기 1회 작업으로 채운 것이고, 개발 도구로 실행했다. 앞으로 수집되는 신규 공고에 요약이 자동으로 붙으려면 요약 라우트에 LLM API 키가 등록돼야 한다. 키가 없는 동안 라우트는 503을 반환하고 스케줄 워크플로는 수집만 마친 뒤 정상 종료하며, 요약이 없는 공고는 준비중 표시로 노출된다. 현재 대기열은 205건이다.

    다음 단계 — API 키를 배포 환경변수에 등록한다. 등록되면 매일 수집 직후 워크플로가 호출당 2건씩 최대 20회 반복해 신규분을 처리한다.

  • 같은 LH 공고가 목록에 두 번 보인다

    LH 공고 일부는 LH 청약플러스와 마이홈포털에 모두 게시된다. 중복 판별 키가 모두 플랫폼을 포함해서, 양쪽에 제목이 완전히 같은 공고 제목 66개가 플랫폼마다 별도 공고로 저장되고, 목록에는 최소 그 2배 건수로 나타난다.

    다음 단계 — 정규화한 제목과 공고일을 묶은 교차 플랫폼 판별 키를 추가해 대표 1건으로 합친다.

  • GH, SH 목록에 비주택 게시물이 섞인다

    두 기관은 청약 전용 API가 없어 공식 홈페이지 게시판을 그대로 읽는다. 그래서 상가 임대, 토지 공급, 채용 공고처럼 주택 청약이 아닌 게시물도 함께 들어온다.

    다음 단계 — 제목과 본문 기준 분류 규칙을 넣어 비주택 게시물을 공개 목록에서 제외한다.

  • 로컬 LLM 전환은 보류 상태다

    비용이 들지 않는 로컬 모델로 요약을 돌리는 경로는 폐기하지 않고 보류했다. 요약 품질을 필드 단위로 실측하기 전까지는 상용 API를 쓴다.

    다음 단계 — 평가 표본으로 로컬 모델과 상용 API의 수치 정확도를 대조하고, 결과에 따라 실행 위치를 다시 결정한다.

서울 청약부산 청약대구 청약인천 청약광주 청약대전 청약울산 청약세종 청약경기 청약강원 청약충북 청약충남 청약전북 청약전남 청약경북 청약경남 청약제주 청약
© 2026 청약한눈에