Skip to content

Repository files navigation

Halley

Halley

JavaSpring BootjOOQGradlePostgreSQLRedisAlpine.jsKakao MapClaudeDeepSeekCodex AuditLicense

같은 집을 함께 찾는 사람들을 위한 매물 비교·평가 도구입니다.

네이버 부동산 매물을 붙여넣으면 40여 개 필드를 파싱하고, 14개 기준으로 채점해 순위를 매깁니다. 그룹에 속한 사람들의 현금을 합산해 대출 한도와 예산을 계산하고, 함께 코멘트를 남기며 임장 동선을 짭니다.


목차

  1. 프로젝트 개요
  2. 주요 기능
  3. 채점 기준
  4. 아키텍처
  5. 매물 등록 흐름
  6. 기술 스택
  7. 외부 연동
  8. 시작하기
  9. 환경변수
  10. API
  11. 배치 작업
  12. 운영 메모
  13. 용어
  14. 문서
  15. 상태
  16. 라이선스

프로젝트 개요

집을 살 때 사람은 여러 매물을 동시에 저울질합니다. 그런데 비교할 정보가 흩어져 있습니다 — 호가는 네이버에, 실거래는 국토부에, 공시가격은 V-World에, 규제지역은 법제처 고시에, 대출 한도는 은행 창구에 있습니다.

Halley는 그것을 한 화면에 모아 같은 기준으로 견줍니다. 그리고 혼자가 아니라 그룹이 함께 봅니다 — 배우자·가족이 각자 계정으로 들어와 같은 매물 목록을 보고, 각자 임장 인상을 매기고, 현금을 합산해 예산을 계산합니다.

폐쇄형입니다. 회원가입은 프로퍼티로 열고 닫으며, 매물은 그룹 밖으로 새지 않습니다. 다른 그룹의 매물에 접근하면 403이 아니라 404를 돌려줍니다 — 403은 "있지만 못 본다"를 알려 주는 셈이라 존재 자체가 새어 나갑니다.


주요 기능

기능설명
붙여넣기 등록네이버 부동산 매물 상세 텍스트를 붙여넣으면 40여 개 필드가 자동 파싱됩니다 (PC·모바일 공통). 파싱 신뢰도를 필드별로 남겨 무엇을 못 읽었는지 보여 줍니다
자동 채점14개 기준을 우선순위 가중치로 종합 평가합니다. 산출 근거를 항목마다 문장으로 남깁니다
AI 추천도매물 제원·주변 시설·구성원 직장·쾌적함 평가·코멘트를 넣어 Claude에게 묻습니다. 사람의 판단이 바뀌면 자동으로 다시 묻습니다
그룹1인 1그룹. 초대 코드(8자리·24시간)로 합류하고, 빈 그룹은 자동 삭제됩니다. 매물·채점·코멘트가 모두 그룹 단위로 격리됩니다
대출 한도LTV·스트레스 DSR 기반 자체 계산. 규제지역·주택 보유 수·금리유형·기존 부채 종류를 반영합니다
실거래가국토부 실거래를 12개월치 조회해 같은 단지·면적대 중앙값을 보여 줍니다
공시가격·토지이용계획V-World에서 공시가격과 토지거래허가구역·정비구역을 받아 붙입니다
규제지역 자동 적재법제처 고시 PDF를 파싱해 투기과열지구·조정대상지역을 DB에 채웁니다
가격 전망실거래 추세·전세가율·장기 추세·전고점 대비·금리 국면·용적률 여유를 코드가 계산하고, 방향은 Claude가 판단합니다. 매물 카드에 화살표 하나(▲▼▶)로만 뜹니다
임장 플래너하루 방문할 매물(최대 12건)을 고르면 자가용/대중교통 기준 최적 방문 순서를 계산합니다
그룹 알림매물 등록·삭제, 코멘트, 쾌적함 평가를 그룹 Webhook으로 보냅니다
지도·로드뷰카카오맵 마커와 로드뷰 모달

채점 기준

14개 항목을 우선순위 가중치로 합산합니다. 가중치는 관리자가 드래그로 바꿉니다.

코드항목방식재료
PRICE가격자동호가 · 그룹 현금 합계 · 대출 한도
COMMUTE직주근접자동ODsay 대중교통 경로 (구성원 전원 평균)
STATION역세권자동카카오 POI 최근접 지하철역
EDUCATION교육여건혼합배정 초등학교 · 주변 학교 POI
AMENITY편의시설자동마트·병원·은행 POI
GREEN녹색환경혼합공원·하천 POI
AGE건물 연식자동사용승인연도
FLOOR자동해당층 / 총층
PARKING주차자동세대당 주차대수
HOUSEHOLDS세대수자동총세대수
MOVE_IN입주시기자동즉시 · 협의 · 날짜
COMFORT공간의 쾌적함수동구성원이 각자 1~5점. 총점에는 평균 × 20
LLM_RECOMMENDATIONAI 추천도자동Claude
COMPARATIVE_ADVANTAGE비교 우위 추천자동목록 전체를 한 번에 비교

이미 자동 채점된 항목은 수동으로 덮어쓸 수 없습니다. 화면이 칸을 추정값으로 채워 두기 때문에, 그것을 그대로 저장하면 자동 채점이 통째로 수동으로 굳고 산출 근거가 사라집니다. 다만 산출에 실패해 값이 없으면 사람이 채울 수 있습니다.


아키텍처

flowchart TB
subgraph browser["Browser — App Shell"]
UI["Mustache 한 장 + Alpine.js<br/>빌드 단계 없음<br/>폴링: 채점 판 번호 3초 · AI 추천도 2초"]
end
subgraph app["Spring Boot"]
direction TB
WEB["adapter/inbound/web<br/>컨트롤러 · DTO"]
SVC["application/service<br/>서비스 · port/out (외부·캐시만)"]
GUARD["PropertyAccessGuard<br/>그룹 격리의 유일한 길목"]
DOM["domain<br/>채점 산식 · 대출 계산 · 순수 로직"]
PERS["adapter/outbound/persistence<br/>jOOQ — 코드젠 없이 손으로 쓴 테이블 정의"]
CACHE["adapter/outbound/cache<br/>Redis(live) / InMemory(local)"]
EXT["adapter/outbound/external<br/>OpenFeign + Resilience4j<br/>FallbackFactory 필수"]
WEB --> SVC
SVC --> GUARD
SVC --> DOM
SVC --> PERS
SVC --> CACHE
SVC --> EXT
end
subgraph outside["바깥"]
direction TB
KAKAO["카카오<br/>지도 · 지오코딩 · POI · 자가용 경로"]
ODSAY["ODsay<br/>대중교통"]
MOLIT["국토교통부<br/>실거래가"]
VWORLD["V-World<br/>공시가격 · 토지이용계획 · 행정구역"]
LAW["법제처<br/>규제지역 고시"]
FSS["금융감독원<br/>대출 상품 금리"]
ECOS["한국은행 ECOS<br/>가계대출 금리 시계열"]
CLAUDE["Claude<br/>AI 추천도 · 가격 전망"]
NAVER["네이버 검색<br/>관련 기사"]
SLACK["Slack<br/>그룹별 Webhook"]
end
UI -- "REST (JSON)" --> WEB
EXT --> KAKAO & ODSAY & MOLIT & VWORLD & LAW
EXT --> FSS & ECOS & CLAUDE & NAVER & SLACK
DB[("PostgreSQL(live)<br/>H2(local)")]
PERS --> DB
Loading

포트는 캐시·세션·외부 API에만 둡니다. DB 접근은 리포지토리를 직접 씁니다 — 바꿀 계획이 없는 것에 추상화를 씌우면 읽기만 어려워집니다.


매물 등록 흐름

외부 API가 수십 번 붙는 작업이라 요청이 기다리는 부분과 배경으로 미루는 부분을 나눕니다.

flowchart TB
A["사용자가 매물 등록"] --> B["DB 저장 (커밋)"]
B --> C["응답 — 카드가 곧바로 뜬다<br/>점수 자리에 '분석 중'"]
C -.-> D
subgraph D["앞 단계 — 배경 (수 초)"]
direction LR
D1["초등학교"]
D2["토지이용계획"]
D3["채점"]
end
D --> E["채점 판 번호가 오른다<br/>목록이 스스로 갱신"]
E -.-> F
subgraph F["뒤 단계 — 배경 (수십 초)"]
direction LR
F1["실거래가"]
F2["공시가격"] --> F3["AI 추천도"]
end
F --> G["진행 막대 + 폴링으로 자동 반영"]
Loading

등록 응답은 보정을 기다리지 않습니다 (설계 I220). 한때 앞 단계를 기다렸는데, ODsay 할당량이 끝나 직주근접이 LLM으로 넘어가면 사람당 4~5초라 등록 한 번이 수십 초가 됐습니다. 카드를 먼저 보여 주고 진행 표시를 띄웁니다.

AI 추천도는 공시가격 뒤에 옵니다. 프롬프트에 공시가격(원) 줄이 들어가기 때문입니다. 나란히 돌리면 첫 판단이 '정보 없음'으로 굳고, 그 뒤로 다시 물을 계기가 없습니다.

등록 트랜잭션 안에서는 돌지 않습니다. 외부 API를 부르는 동안 DB 커넥션을 붙잡으면 동시 등록 몇 건에 풀이 마르고, 카카오 장애가 매물 등록 자체를 되돌립니다.

동시 실행 수는 세마포어로 묶습니다(ENRICHMENT_MAX_CONCURRENCY, 기본 400) — 스레드가 아니라 그 끝에 붙은 공공 API를 지키는 값입니다.


기술 스택

구분기술
언어 / 프레임워크Java 25, Spring Boot 4.1.x
빌드Gradle
영속화jOOQ 3.21 — 코드젠 없이 테이블 정의를 손으로 씁니다
DBPostgreSQL(live) / H2 인메모리(local)
캐시 · 세션Redis(live) / 인메모리(local)
화면Mustache App Shell + Alpine.js — 빌드 단계 없음
외부 호출OpenFeign + Resilience4j
지도카카오맵 JS SDK
비동기가상 스레드 (Thread.ofVirtual()) + 세마포어
배포https://halley.furaiki-lifelog.com, Let's Encrypt

외부 연동

연동용도인증없으면
카카오맵 JS지도 · 마커 · 로드뷰JS 키 (클라이언트)지도가 안 뜬다
카카오 로컬 REST지오코딩 · POIREST 키주소 검색·POI 채점 불가
카카오 Directions자가용 경로REST 키 (공유)임장 자가용 모드 불가
ODsay대중교통 경로쿼리 파라미터직주근접 미산출
국토부 실거래가참고 실거래서비스 키실거래 카드가 빈다
V-World공시가격 · 토지이용계획 · 행정구역인증키공시가격·규제 정보가 빈다
법제처규제지역 고시OC규제지역을 사람이 넣어야 한다
금감원대출 상품 금리auth기본 금리 4%로 계산
국토부 전월세 실거래전세가율서비스 키(공유)전세가율 지표가 빠진다
국토부 건축물대장현재 용적률 → 재건축 여력서비스 키(공유)용적률 여유 지표가 빠진다
한국은행 ECOS가계대출 금리 5년인증키(경로)스트레스 금리가 고정값으로 남는다
네이버 검색(뉴스)관련 기사 링크 — 점수 미반영Client ID/Secret전망 모달의 기사 목록이 빈다
ClaudeAI 추천도 · 가격 전망 판단x-api-key그 두 항목만 미산출
Slack Webhook그룹 알림URL 자체알림이 안 간다

키가 없으면 그 기능만 비고 나머지는 그대로 돕니다. 외부 연동 실패가 본 기능을 막지 않는 것이 원칙입니다. 다만 비었다는 사실은 로그와 화면에 드러냅니다 — 조용히 넘어가면 "왜 값이 없는지" 알 수 없습니다.

자세한 호출 규격·응답 구조·함정은 docs/INTERFACE_MANUAL.md에 있습니다.


시작하기

사전 요구사항

  • JDK 25
  • libheif 1.16 이상 (아이폰 HEIC 사진 디코딩)
  • Docker (PostgreSQL · Redis 로컬 실행용 — local 프로파일만 쓴다면 불필요)

macOS에서는 brew install libheif, Amazon Linux 2023에서는 sudo dnf install libheif, Debian/Ubuntu에서는 apt-get install libheif-dev로 설치합니다. Homebrew 기본 경로는 Gradle이 자동으로 찾고, 다른 위치라면 JAVA_LIBRARY_PATH로 알려 줍니다. 직접 JAR를 실행할 때는 네이티브 접근을 허용해야 합니다.

java --enable-native-access=ALL-UNNAMED -jar app.jar

macOS에서 JAR를 직접 실행한다면 -Djava.library.path=/opt/homebrew/lib도 함께 줍니다. bootRun과 테스트에는 이 경로와 네이티브 접근 옵션이 이미 설정돼 있습니다.

로컬 실행

local 프로파일은 H2 인메모리 + 인메모리 캐시라 Docker 없이 바로 뜹니다.

git clone <repo-url>cd halley
./gradlew bootRun --args='--spring.profiles.active=local'

첫 실행 시 계정이 없으면 콘솔에 임시 Admin 계정이 출력됩니다.

==========================================================
username : admin
password : SBwkpr67AKkUUEox
Please change the password after first login.
==========================================================

로그인하면 비밀번호 변경과 프로필 확인을 강제로 거칩니다.

운영 실행

DB_URL=jdbc:postgresql://... DB_USERNAME=... DB_PASSWORD=... \
REDIS_HOST=... \
./gradlew bootRun --args='--spring.profiles.active=live'

운영 DB 스키마는 자동 생성되지 않습니다 (spring.sql.init.mode: never). docs/DDL.sql로 처음 만들고, 이미 돌던 DB가 뒤처졌으면 docs/DDL-repair.sql을 쓰십시오 — 전부 IF NOT EXISTS라 현재 상태와 무관하게 안전하고 여러 번 돌려도 같은 결과입니다.

테스트

./gradlew test

환경변수

필수 (운영)

변수설명
DB_URL · DB_USERNAME · DB_PASSWORDPostgreSQL 접속 정보
REDIS_HOST · REDIS_PORTRedis 접속 정보
APP_BASE_URLSlack 알림에 붙는 링크의 앞부분. 비우면 링크를 안 답니다
APP_IMAGES_DIR올린 사진이 쌓이는 절대 경로. 아래 설명을 보십시오
IMAGE_MAX_FILE_SIZE · IMAGE_MAX_REQUEST_SIZE사진 한 장·요청의 업로드 상한. 기본 20MB · 25MB

APP_IMAGES_DIR을 반드시 절대 경로로 주십시오. 기본값 uploads는 상대 경로라 JVM을 띄운 디렉터리 기준으로 풀립니다 — jar가 놓인 자리가 아닙니다. 다른 디렉터리에서 다시 띄우면 DB 기록은 남고 파일만 사라진 것처럼 보입니다 (깨진 이미지). 서버가 /home/ec2-user/halley라면:

APP_IMAGES_DIR=/home/ec2-user/halley/uploads

실제로 어디를 쓰는지는 기동 로그에 찍힙니다: Serving uploaded images from /home/ec2-user/halley/uploads (exists=true, writable=true). 사진이 안 보이면 여기부터 보십시오.

외부 연동 키

변수발급처
KAKAO_JS_KEY카카오 개발자 — JavaScript 키
KAKAO_REST_KEY카카오 개발자 — REST 키 (로컬·Directions 공용)
ODSAY_API_KEYODsay LAB
MINISTRY_API_KEY공공데이터포털 — 국토부 실거래가
HOUSING_PRICE_API_KEYV-World (공시가격·토지이용계획·행정구역 공용)
LAW_OC법제처 국가법령정보 공동활용
FSS_API_KEY금융감독원 금융상품통합비교공시
ECOS_KEY한국은행 경제통계시스템
ANTHROPIC_API_KEYAnthropic Console
NAVER_CLIENT_ID · NAVER_CLIENT_SECRET네이버 클라우드 콘솔 — API Hub > 검색 (옛 developers.naver.com 키는 401)

Slack Webhook URL은 환경변수가 아닙니다. 그룹마다 다르므로 DB (user_group.slack_webhook_url)에 저장하고 그룹 정보 화면에서 관리합니다.

동작 조절

변수기본값설명
MEMBERSHIP_SIGN_UP_OPENtrue회원가입 화면 노출 여부
MINISTRY_LOOKBACK_MONTHS12실거래를 몇 개월 거슬러 볼지. 이 값이 그대로 호출 횟수입니다
ENRICHMENT_MAX_CONCURRENCY400보정 동시 실행 상한. 스레드가 아니라 외부 API를 지키는 값
DB_POOL_MAX5Hikari 최대 커넥션. DB 한도에 맞춰 줄이는 방향입니다
DB_POOL_MIN_IDLE1
DB_POOL_TIMEOUT_MS3000커넥션 대기 상한. 오래 매달리면 화면이 멈춘 것으로 보입니다
LOAN_STRESS_FLOOR · LOAN_STRESS_CAP0.015 · 0.030스트레스 금리 하한·상한 (고시가 바뀌면 여기만)
ECOS_STAT_CODE121Y006예금은행 대출금리
ECOS_HOUSEHOLD_ITEMBECBLA03가계대출 항목 코드
LLM_ENABLED · LLM_PROVIDER · LLM_CLAUDE_MODELtrue · claude · claude-opus-5
TRANSIT_FALLBACK_MODEL(LLM_CLAUDE_MODEL)ODsay 하루치가 끝났을 때 대신 답할 모델 (설계 I210)
SLACK_ENABLEDfalse알림 전체 스위치. 켜야 아무것도 나갑니다
SLACK_NOTIFY_PROPERTY_CREATEDfalse매물 등록 알림만 따로

카카오 개발자 콘솔에 로컬(http://localhost:8080)과 운영 도메인을 모두 등록해야 지도가 렌더됩니다.

Slack 알림 붙이기

웹훅 URL은 환경변수가 아니라 그룹마다 DB에 있습니다(user_group.slack_webhook_url). 그룹이 각자 다른 채널을 쓰기 때문입니다 — 한 곳에 몰면 우리 매물이 남의 채널에 뜹니다.

1. Slack에서 웹훅 만들기

  1. https://api.slack.com/appsCreate New App
  2. Or start your own way 아래의 Blank appContinue(위쪽 AI agent·Starter app은 템플릿입니다 — 웹훅만 쓸 것이라 필요 없습니다)
  3. 앱 이름과 워크스페이스를 고릅니다
  4. 왼쪽 메뉴 Incoming Webhooks → 스위치를 On
  5. 맨 아래 Add New Webhook to Workspace → 알림을 받을 채널 선택Allow
  6. 만들어진 URL을 복사합니다

Slack 화면은 종종 바뀝니다. 이 문서는 2026-09-02 기준입니다 — 예전에는 2번이 From scratch였습니다. 이름이 달라 보이면 "빈 앱으로 시작"에 해당하는 것을 고르면 됩니다.

생김새는 이렇습니다 (실제 값이 아니라 모양만 적습니다 — 진짜를 문서에 두면 GitHub 비밀 검사가 푸시를 막습니다):

https://hooks.slack.com/services/<팀ID>/<채널ID>/<토큰>

이 URL 자체가 인증입니다. 아는 사람은 누구나 그 채널에 글을 쓸 수 있습니다 — 공개 저장소·이슈·스크린샷에 올리지 마십시오. 새면 Slack 앱 화면에서 지우고 다시 만듭니다.

2. 앱에 넣기

헤더의 {그룹명}의 → 그룹 정보 → Slack Webhook URL 칸에 붙여넣고 저장. 바로 옆 테스트 버튼으로 실제로 닿는지 확인합니다 — 채널에 한 줄이 뜨면 된 것입니다.

3. 서버 스위치 켜기

SLACK_ENABLED=true # ← 이게 false 면 아무것도 안 나갑니다 (기본값)
SLACK_NOTIFY_PROPERTY_CREATED=true # 매물 등록 알림도 받으려면

SLACK_ENABLED의 기본값은 false입니다. 웹훅을 넣고 저장해도 이걸 안 켜면 조용합니다. 테스트 버튼은 이 스위치와 무관하게 보내므로, "테스트는 되는데 실제 알림이 안 온다"면 여기부터 보십시오.

무엇이 언제 가나

사건스위치보내는 곳
매물 등록SLACK_NOTIFY_PROPERTY_CREATEDPropertyCreatedListener
매물 삭제없음 (항상)PropertyCreatedListener
코멘트 등록없음 (항상)PropertyInsightListener
공간의 쾌적함 평가없음 (항상)PropertyInsightListener

메시지는 평문 한 줄입니다({"text": "..."}). 블록 킷을 쓰지 않습니다 — 읽는 사람이 몇 명뿐이라 꾸밈보다 한눈에 읽히는 것이 낫습니다.

:house: 새 매물이 등록되었습니다 — 상계주공7단지 714동
:speech_balloon: 월터님이 상계주공7단지 714동에 의견을 남겼습니다

안 오면 볼 것

증상원인
테스트도 안 됨URL 오타 · Slack 앱에서 웹훅을 지웠음
테스트는 되는데 알림이 없음SLACK_ENABLED=false
등록만 안 옴SLACK_NOTIFY_PROPERTY_CREATED=false
가끔 빠짐전송 실패는 notification_log에 남고 5분마다 재시도합니다(NotificationRetryJob). 관리자 → 설정 → 알림 이력에서 상태를 봅니다

알림 실패가 본 기능을 막지 않습니다. 매물 등록·코멘트는 Slack이 죽어도 그대로 됩니다.


API

REST 85개. 주요한 것만 적습니다 — 전체 명세는 docs/DESIGN.md에 있습니다.

인증 · 계정

메서드경로설명
POST/api/auth/login로그인. 남은 세션 시간을 함께 준다
POST/api/auth/logout
GET/api/auth/session세션 확인 (비밀번호 변경·프로필 확인 필요 여부 포함)
POST/api/auth/password비밀번호 변경
POST/api/users/sign-up회원가입 (MEMBERSHIP_SIGN_UP_OPEN)
PUT/api/users/me/profile프로필 (직장 좌표 · 보유 현금 · 연소득)
PUT/api/users/me/debts기존 부채 (종류별)
POST/api/users/me/withdraw탈퇴

그룹

메서드경로설명
GET/api/groups/me내 그룹
GET/api/groups/me/detail그룹 정보 화면 — 현금 합계 · 매물 수 · 구성원
PUT/api/groups/me그룹명 변경 (그룹의 누구나)
PUT/api/groups/me/webhookSlack Webhook
POST/api/groups/me/webhook/test테스트 발송
POST/api/groups/me/invites초대 코드 발급 (8자리 · 24시간)
POST/api/groups/join초대 코드로 합류

매물

메서드경로설명
GET/api/properties목록 (채점 포함, 거래유형 필터)
POST/api/properties등록 — 앞 단계 보정까지 마치고 응답
GETPUTDELETE/api/properties/{id}단건 · 수정 · 삭제
POST/api/properties/parse-preview붙여넣기 파싱 미리보기
PATCH/api/properties/{id}/status판매 상태
GET/api/properties/score-versions채점 판 번호 — 화면 폴링용

채점 · 분석

메서드경로설명
PUT/api/properties/{id}/scores수동 점수 저장
POST/api/properties/{id}/scores/recompute미산출 항목 재산출
POST/api/properties/{id}/rescore재채점
GET/api/properties/{id}/llm-recommendationAI 추천도 (진행 중 표시 포함)
GET/api/properties/{id}/forecast가격 전망 — 결과가 없어도 200 (진행 중인지 알려야 한다)
POST/api/properties/{id}/forecast/refresh전망 다시 분석 (1~2분)
GET/api/properties/{id}/news관련 기사 — 점수·프롬프트 미반영
GETPOST/api/properties/comparative-analysis비교 우위
GETPUT/api/criteria/weights가중치

매물 부가 정보

메서드경로설명
POST/api/properties/{id}/loan-estimate대출 한도 (LTV · 스트레스 DSR)
GET/api/properties/{id}/reference-transactions국토부 실거래
GETPOST/api/properties/{id}/land-use토지이용계획
GETPOSTPUTDELETE/api/properties/{id}/comments코멘트
GETPOSTDELETE/api/properties/{id}/images이미지
GETPUT/api/properties/{id}/agents중개사

임장

메서드경로설명
POST/api/itinerary/optimize최적 방문 순서
POSTGET/api/itinerary/plans · /{id}계획 저장 · 조회
POST/api/itinerary/plans/{id}/recompute다시 계산
GETPUT/api/itinerary/start-location출발지

관리자

메서드경로설명
GETPOST/api/admin/groups그룹 목록 · 생성
GETPUT/api/admin/settings시스템 설정
GETPUT/api/admin/regulations · /params규제 파라미터
GETPOSTDELETE/api/admin/regulated-areas규제지역
POST/api/admin/stress-rate/refresh스트레스 금리 재산출 (ECOS)
GET/api/admin/notifications알림 발송 이력

배치 작업

작업주기하는 일
ListingCheckJob매일매물이 판매완료됐는지 확인하고 그룹에 알린다
RegulationNoticeJob매일 04:00법제처 고시가 바뀌었으면 규제지역을 다시 적재한다
MarketRateJob매일 04:30금감원 공시 금리를 갱신한다
StressRateJob매월 1일 04:45 + 기동 시ECOS로 스트레스 금리를 다시 산출한다
NotificationRetryJob5분실패한 Slack 알림을 재시도한다

시간을 벌려 둔 이유는 기동 직후 외부 호출이 몰리지 않게 하기 위해서입니다.


운영 메모

프로파일

locallive
DBH2 인메모리PostgreSQL
캐시·세션인메모리Redis
스키마schema.sql 자동 적용수동 (docs/DDL.sql)

H2 URL에서 DB_CLOSE_DELAY=-1을 빼지 마십시오. 인메모리 DB는 마지막 커넥션이 닫히는 순간 스키마째 사라집니다 — 기동 직후엔 멀쩡하다 한참 뒤에 갑자기 모든 질의가 터집니다.

local과 live가 다른 지점

같은 컬럼이 dialect마다 다른 타입으로 옵니다 — live는 jsonb, local은 json. 타입을 좁히면 live에서만 터집니다 (JSONB cannot be cast to JSON). parse_confidence · path_summary · payload · assumptions 넷이 그렇습니다.

커넥션 풀

무료 등급 PostgreSQL은 max_connections가 20~30 언저리입니다. 느리다고 풀을 키우면 더 느려집니다 — DB가 감당할 동시 실행 수는 정해져 있고, 그보다 많은 커넥션은 DB 안에서 줄을 섭니다. 기본값을 5로 둔 이유입니다.


용어

도보 N분 — 직선거리 × 우회계수 1.3 ÷ 보행속도 67m/분. 예: 역까지 직선 500m → 500 × 1.3 ÷ 67 ≈ 9.7분. 언덕·지형은 반영하지 않으며 수동 보정할 수 있습니다.

스트레스 DSR — 미래 금리 상승을 가정해 한도를 좁히는 규제. 실효 스트레스 = 기준 스트레스 금리 × 단계 적용률 × 금리유형 가중치. 한도 산정과 실제 상환액에 다른 금리를 씁니다 — 섞으면 둘 다 틀립니다.

MCI / MCG — 주택담보대출에 붙이는 보증보험. 가입하면 소액임차보증금(방공제)만큼 한도가 늘어납니다. 5,500만원을 좌우하는 항목이라 기본으로 켜 둡니다.

임장 — 매물을 직접 보러 가는 것. 쾌적함 점수가 있으면 다녀온 것으로 봅니다.


문서

문서내용
docs/DESIGN.md전체 설계서 — 아키텍처 · ERD · 화면 정의 · API 명세 · 채점 산식 · 확정된 의사결정 이력(I1~)
docs/INTERFACE_MANUAL.md외부 API 매뉴얼 — 키 발급처 · 호출 규격 · 응답 구조 · 실측으로 드러난 함정
docs/SCHEMA.mdDB 스키마 — 관계도(mermaid) · 표별 요약 · 조심할 것
docs/DDL.sqlPostgreSQL 스키마 (초기 생성 + 마이그레이션 이력)
docs/DDL-repair.sql멱등 복구 스크립트 — 운영 DB가 뒤처졌을 때
docs/ADJUST_CACHE.md캐시·성능 검토 (실측 기반)
docs/SCORING.md추천 점수 — 항목별 가중치 · 산출 재료 · 다시 채점하는 계기
docs/PRICE_FORECAST.md가격 전망 설계 — 지표 산식 · 코드/LLM 역할 분담 · 안전장치
docs/MORTGAGE_ENGINE.md대출 계산 엔진 — LTV · 스트레스 DSR · 담보가치
docs/DDL-forecast-reset.sql전망 재시작용 정리 (429·400 시절 값 걷어내기)
docs/COMPLEX_NAME_MATCHING.md단지명 매칭 검토 — 브랜드가 바뀐 단지를 어떻게 찾을 것인가 (미구현)
AGENTS.mdAI 코딩 에이전트용 작업 지침

설계 결정은 번호로 관리합니다. 코드 주석의 (설계 I117) 같은 표기는 docs/DESIGN.md 16장의 해당 항목을 가리킵니다. 왜 그렇게 했는지가 거기 있습니다.


상태

개인 프로젝트 · 비공개 저장소 · 소수 사용자 · 상업적 이용 없음.

대출 한도·실거래가·규제지역은 자체 계산과 공공 데이터이며 실제 은행 심사 결과와 다를 수 있습니다. 투자 판단의 근거로 삼지 마십시오.

가격 전망은 특히 그렇습니다. 공개된 지표 몇 개로 낸 것이고 틀릴 수 있습니다. 지표들이 서로 다른 방향을 가리키면 많은 쪽을 따르되 그 사실을 함께 보여 줍니다 — 확신이 있어서가 아니라, 무엇을 보고 그렇게 판단했는지 드러내려는 것입니다.


라이선스

MIT © 2026 walter.hwang

코드는 MIT입니다. 다만 이 저장소가 부르는 공공·상용 API는 각자의 약관을 따릅니다 — 카카오·ODsay·국토교통부·V-World·법제처·금융감독원·한국은행·네이버·Anthropic. 포크해서 쓰실 때는 키를 각자 발급받으시고, 데이터 재배포 조건을 따로 확인하십시오.

src/main/resources/static/image/ 의 로고는 이 프로젝트의 것입니다.

About

같은 집을 함께 찾는 사람들(주로 커플)을 위한 간단 부동산 매물 비교 웹 애플리케이션. 매물에 대한 AI 분석, 항목별 평가에 따른 추천도, 대출 및 규제 정보 제공. A simple real estate listing comparison web application for people looking for the same home together (primarily couples). Provides AI analysis of listings, category-based evaluations, and information on loans and regulations.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages