Files
Inho Jeong 2ff51db5d2 feat: 개별공시지가(gongsijiga-search) 스킬 추가 (#200)
* chore: version packages

* Merge dev into main (#197)

* fix(toss-securities): clarify session expiry and quote 403 handling

* Clarify toss empty-output session expiry

Portfolio and watchlist reads can exit successfully with empty payloads when the stored Toss session has expired. The empty-output path now verifies the session before JSON parsing and only promotes confirmed invalid auth doctor data into TossSessionExpiredError.

Constraint: Scope is limited to toss-securities issue #126 follow-up on PR #192

Rejected: Treat auth doctor execution failures as expired sessions | unsupported or failing doctor output is inconclusive without parsed session.valid=false

Confidence: high

Scope-risk: narrow

Directive: Keep empty-result session expiry classification tied to explicit auth doctor confirmation

Tested: npm run test --workspace toss-securities; npm run lint --workspace toss-securities; npm run ci; manual mock tossctl blank stdout invalid/inconclusive doctor checks

* Avoid false session-expiry labels for validation errors

The toss wrapper now treats bare validation_error text as an upstream command failure instead of a session-expired signal. Structured auth doctor JSON remains the source of truth for empty portfolio/watchlist invalid-session promotion, while known stored-session-invalid stderr still maps to TossSessionExpiredError.\n\nConstraint: PR #192 follow-up must stay scoped to issue #126 toss-securities behavior.\nRejected: Keep validation_error in the global regex | it mislabels auth doctor transport failures and quote 403 validation errors as session expiry.\nConfidence: high\nScope-risk: narrow\nDirective: Do not broaden the free-text session classifier without regressions for auth doctor and quote upstream validation failures.\nTested: npm run lint --workspace toss-securities; npm run test --workspace toss-securities; npm run ci; manual mock tossctl validation_error checks; architect verification CLEAR\nNot-tested: Live tossctl network/auth session against real Toss upstream

* Align court auction lookup with monthly site search (#196)

The court auction notice page posts a YYYYMM search key from its 조회 button and returns a month of rows. Keep day inputs as a compatibility filter over the monthly response and normalize the current nested detail payload shape.

Constraint: courtauction.go.kr has no public API and blocks bursty automated calls.

Rejected: querying every day independently | the upstream search surface is month-based and day calls return false empty results.

Confidence: high

Scope-risk: narrow

Directive: Preserve the site-observed YYYYMM notice search contract unless the PGJ143M01 XHR changes again.

Tested: npm --workspace packages/court-auction-notice-search test; npm run ci; live 서울중앙지방법원 2026-05 notice/detail smoke lookup.

Not-tested: PR CI after push.

Co-authored-by: OmX <omx@oh-my-codex.dev>

* Guide crawler skills toward reusable discovery (#195)

* chore: version packages

* Guide crawler skills toward reusable discovery

Constraint: User requested insane-search-style guidance for future crawling k-skills without unrelated implementation changes.
Rejected: Adding crawler code or a standalone template | too broad for a docs guidance change and risks dependency creep.
Confidence: high
Scope-risk: narrow
Directive: Keep site-specific access details inside individual skills after a site-agnostic discovery pass.
Tested: npm run ci
Not-tested: Live crawler behavior; documentation-only change.

* Clarify crawler skill discovery guidance

Constraint: Crawling k-skills need site-dependent recipes, but should derive them through a reusable discovery pass.
Rejected: Leaving guidance only in docs/adding-a-skill.md | AGENTS.md and CLAUDE.md also guide future agents.
Confidence: high
Scope-risk: narrow
Directive: Use site-agnostic discovery to find, then explicitly package, the target site's stable access path.
Tested: npm run ci
Not-tested: Live crawler behavior; documentation-only change.

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Ground corporate registration guidance in official form sources

Keep the consulting skill focused on draft/checklist support while pointing users to current IROS and law.go.kr form sources for submission-ready artifacts.

Constraint: official registry forms can change outside the repository and must be re-downloaded at use time

Rejected: committing copied official HWP/HWPX/PDF forms | they would become stale and risk misleading users

Confidence: high

Scope-risk: narrow

Directive: do not treat Markdown templates as substitutes for official registry submission forms

Tested: npm test

* Ground incorporation drafting in real HWP forms

Bundle official court incorporation forms plus public startup incorporation attachments, and make rhwp-filled HWP outputs the default drafting path for the corporate-registration skill. Replace the listed-company articles reference with a startup-suitable Ministry of Justice stock-company form and record source manifests for bundled binaries.

Constraint: user requires actual sourced HWP templates, not generated placeholder binaries.
Rejected: markdown-only drafting | it cannot produce submission-shaped Korean registry forms.
Rejected: listed-company standard articles as the default reference | it is mismatched for typical startup incorporation.
Confidence: high
Scope-risk: moderate
Directive: keep bundled HWP forms source-backed, sanitized, and edited only through copied working files.
Tested: node --test scripts/skill-docs.test.js; npm run lint; k-skill-rhwp info on bundled HWP files; kordoc conversion spot checks.
Not-tested: manual opening every HWP in Hancom Office and live registry submission.
Co-authored-by: OmX <omx@oh-my-codex.dev>

* Streamline corporate registration forms workflow

Prioritize saved HWP forms for ordinary stock-company promoter incorporations, make required court-registry receipts and director identity certificates explicit, and remove the redundant markdown articles template so the skill stays HWP-first.

Constraint: 법원등기소 기준 체크리스트 must include fee receipts, director seal/signature certificates, and resident-record documents.

Rejected: Keeping a separate markdown articles template | duplicated the stored HWP articles workflow and encouraged non-HWP drafting.

Confidence: high

Scope-risk: narrow

Directive: Keep corporate-registration-consulting focused on stored HWP form copies and explicit issued-document checklists.

Tested: node --test --test-name-pattern 'corporate-registration-consulting' scripts/skill-docs.test.js; node --check scripts/skill-docs.test.js; ./scripts/validate-skills.sh; git diff --check

Not-tested: Full npm run ci was not run because this is a skill documentation/template refactor, not release or package automation.

---------

Co-authored-by: galvaomica <galvaomica@galvaomicaui-MacBookAir.local>
Co-authored-by: OmX <omx@oh-my-codex.dev>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* chore: version packages (#198)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(realtyprice): add address parsing and sido code mapping

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(realtyprice): use string sido codes for consistency with upstream API

* feat(realtyprice): add response normalization and buildResponse

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(realtyprice): add upstream cascade fetch functions with timeout

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(realtyprice): add lookupGongsijiga orchestrator with region matching

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(realtyprice): add simple in-memory cache with TTL

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(proxy): register GET /realtyprice route with caching

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: add gongsijiga-search SKILL.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: add changeset for gongsijiga-search

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(realtyprice): align with actual realtyprice.kr API response format

- Response wraps data in model.list (not bjdList/gsiList)
- Field names are code/name (not bjd_cd/bjd_nm)
- bun2 empty → send "0000" (not empty string)
- eupmyeondong matching: try full string match first (API returns
  combined "면 리" names like "청계면 청천리")

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(gongsijiga-search): align /realtyprice route with v1 API convention

- Change route from /realtyprice to /v1/realtyprice for consistency with other proxy endpoints.
- Add realtypriceConfigured flag to /health upstreams.
- Normalize address cache key by collapsing multiple whitespaces.
- Update SKILL.md and README.md to reflect the new v1 path.

* feat(gongsijiga-search): add Sejong special-city support

- parseAddress: allow 3-token minimum for Sejong (no sigungu) and set sigungu to empty string.
- lookupGongsijiga: skip sigungu lookup for Sejong (sidoCode 29), use fixed sggCode 36110.
- Add Sejong parseAddress and lookupGongsijiga test cases.
- Update SKILL.md with Sejong address format examples.

* refactor(gongsijiga-search): split realtyprice.kr lookup into standalone package

realtyprice.kr is a fully public endpoint that needs no API key, so per
the new k-skill-proxy inclusion rule (proxy is for keyed upstreams only)
the helper now ships as `gongsijiga-search` and is invoked directly from
the user's machine.

- new workspace package packages/gongsijiga-search/ following the
  blue-ribbon-nearby/coupang-product-search convention (publishConfig,
  files, repository, keywords)
- remove /v1/realtyprice route, realtyprice.js, realtyprice.test.js, and
  the realtypriceConfigured health flag from k-skill-proxy
- document the inclusion rule in AGENTS.md and CLAUDE.md so future skills
  default to direct calls when no key is required
- advertise the new skill in README.md, docs/install.md, and add
  docs/features/gongsijiga-search.md
- drop the hardcoded toss-securities lockfile version assertion that
  pinned a workspace version (would block changesets version-packages)
  and document the anti-pattern in AGENTS.md / CLAUDE.md
- changesets: refresh the proxy refactor message and add a patch
  changeset so the new gongsijiga-search package gets published

---------

Co-authored-by: Jeffrey (Dongkyu) Kim <vkehfdl1@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: galvaomica <galvaomica@galvaomicaui-MacBookAir.local>
Co-authored-by: OmX <omx@oh-my-codex.dev>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 00:27:31 +09:00

3.8 KiB

gongsijiga-search

대한민국 국토교통부 부동산공시가격알리미(realtyprice.kr)의 공개 API를 호출해 지번 단위 개별공시지가(원/㎡)를 조회하는 Node.js 패키지입니다. 다년도 추이와 전년 대비 변동률을 정규화된 JSON으로 돌려줍니다.

Note

realtyprice.kr는 API 키가 필요 없는 완전 공개 엔드포인트이므로 이 패키지는 k-skill-proxy를 경유하지 않고 사용자 머신에서 직접 upstream을 호출합니다. (참고: 저장소의 k-skill-proxy inclusion rule — 프록시는 API 키가 필요한 upstream만 다룹니다.)

설치

배포 후:

npm install gongsijiga-search

이 저장소에서 개발할 때:

npm install

사용 예시

const { lookupGongsijiga } = require("gongsijiga-search");

async function main() {
  const result = await lookupGongsijiga("서울특별시 강남구 역삼동 736");
  console.log(result.latest); // { year, price_per_sqm, notice_date, base_date }
  console.log(result.history); // [{ year, price_per_sqm, notice_date }, ...] (descending)
  console.log(result.yoy_change_pct); // 전년 대비 % (소수점 둘째 자리 반올림)
}

main().catch((err) => {
  console.error(err.code, err.message);
  process.exitCode = 1;
});

입력 주소 형식

<시도> <시군구> <읍면동…> [산] <본번[-부번]> 형태의 한국어 지번 주소.

  • 시도: 17개 광역자치단체 풀네임/약칭 모두 지원 (예: 서울특별시 / 서울)
  • 세종특별자치시는 시군구가 없으므로 세종 <읍면동> <지번> 형식
  • 산 지번은 산 1-2 또는 산1-2
  • 본번은 4자리 이하 숫자, 부번은 - 뒤에 옴

예: 서울 강남구 역삼동 736, 전남 무안군 청계면 청천리 산 1-2, 세종 어진동 575.

응답 모양

{
  "address": "서울특별시 강남구 역삼동 736",
  "jibun": "736번지",
  "san": false,
  "latest": {
    "year": 2026,
    "price_per_sqm": 72340000,
    "notice_date": "2026-04-30",
    "base_date": "2026-01-01"
  },
  "history": [
    { "year": 2026, "price_per_sqm": 72340000, "notice_date": "2026-04-30" },
    { "year": 2025, "price_per_sqm": 68600000, "notice_date": "2025-04-30" }
  ],
  "yoy_change_pct": 5.45,
  "source_url": "https://www.realtyprice.kr/notice/gsindividual/search.htm"
}

에러 코드

error.code 의미 statusCode
ADDRESS_PARSE_FAILED 주소 파싱 실패 / 미인식 시도 / 토큰 부족 400
INVALID_BUNJI 본번이 비숫자 또는 4자리 초과 400
REGION_NOT_FOUND 시군구/읍면동 매칭 실패 (err.candidates 후보 최대 3개) 404
LAND_NOT_FOUND 해당 지번이 공시지가에 등재되지 않음 404
UPSTREAM_ERROR realtyprice.kr 비정상 HTTP 응답 502
UPSTREAM_TIMEOUT 30초 타임아웃 504

공개 API

  • lookupGongsijiga(addressRaw, fetchFn?) — 주소 → 정규화된 응답
  • parseAddress(rawAddress) — 주소 파서 (지번/산/세종 처리)
  • parseSido(text) — 시도명 → 2자리 코드
  • normalizeSearchResult(raw) — gsiList 항목 → { year, price_per_sqm, notice_date }
  • buildResponse({ address, jibun, san, history }) — 최종 응답 합성
  • fetchSigunguList, fetchEupmyeondongList, fetchGsiSearchList — 단계별 upstream 호출
  • fetchWithTimeout(url, opts, timeoutMs?, fetchFn?) — AbortController 기반 타임아웃
  • createCache() — 단순 in-memory TTL 캐시 (Map 백엔드)
  • SIDO_MAP, REALTYPRICE_BASE_URL, REFERER, makeError

Notes

  • 공시지가 ≠ 시세. 시세는 통상 공시지가의 1.5~3배.
  • 매년 1월 1일 기준, 45월 발표. 14월에는 전년도가 최신.
  • realtyprice.kr 호출에는 별도 Referer 헤더가 필요하며, 이 패키지가 자동 처리합니다.