Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/market-kurly-search-skill.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"market-kurly-search": minor
---

Publish the first reusable Market Kurly product search package and skill docs for unauthenticated price lookups.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Claude Code, Codex, OpenCode, OpenClaw/ClawHub 등 각종 코딩 에이전트
| 근처 술집 조회 | 현재 위치 기준 영업 상태·메뉴·좌석·전화번호가 포함된 근처 술집 조회 | 불필요 | [근처 술집 조회 가이드](docs/features/kakao-bar-nearby.md) |
| 우편번호 검색 | 주소 키워드로 우편번호 조회 | 불필요 | [우편번호 검색 가이드](docs/features/zipcode-search.md) |
| 다이소 상품 조회 | 다이소 매장별 상품 재고 확인 | 불필요 | [다이소 상품 조회 가이드](docs/features/daiso-product-search.md) |
| 마켓컬리 상품 조회 | 마켓컬리 상품 검색, 현재 가격, 할인 여부, 품절 여부 조회 | 불필요 | [마켓컬리 상품 조회 가이드](docs/features/market-kurly-search.md) |
| 올리브영 검색 | 올리브영 매장·상품·재고 조회 | 불필요 | [올리브영 검색 가이드](docs/features/olive-young-search.md) |
| 택배 배송조회 | CJ대한통운·우체국 송장 번호로 배송 상태 조회 | 불필요 | [택배 배송조회 가이드](docs/features/delivery-tracking.md) |
| 쿠팡 상품 검색 | 쿠팡 상품 검색, 로켓배송 필터, 가격대 검색, 비교, 베스트, 골드박스 특가 조회 | 불필요 | [쿠팡 상품 검색 가이드](docs/features/coupang-product-search.md) |
Expand Down Expand Up @@ -112,6 +113,7 @@ Claude Code, Codex, OpenCode, OpenClaw/ClawHub 등 각종 코딩 에이전트
- [근처 술집 조회 가이드](docs/features/kakao-bar-nearby.md)
- [우편번호 검색](docs/features/zipcode-search.md)
- [다이소 상품 조회](docs/features/daiso-product-search.md)
- [마켓컬리 상품 조회 가이드](docs/features/market-kurly-search.md)
- [올리브영 검색 가이드](docs/features/olive-young-search.md)
- [택배 배송조회](docs/features/delivery-tracking.md)
- [쿠팡 상품 검색](docs/features/coupang-product-search.md)
Expand Down
72 changes: 72 additions & 0 deletions docs/features/market-kurly-search.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# 마켓컬리 상품 조회 가이드

## 이 기능으로 할 수 있는 일

- 마켓컬리 상품 키워드 검색
- 현재 가격 확인
- 필요하면 원가/할인가 여부 확인
- 품절 여부와 배송 타입 확인
- 상품 링크 반환

## 먼저 필요한 것

- 인터넷 연결
- `node` 18+

## 입력값

- 상품명 또는 검색어
- 예: `우유`
- 예: `딸기`
- 예: `닭가슴살`

## 공식 표면

- search list: `https://api.kurly.com/search/v4/sites/market/normal-search?keyword=<keyword>&page=1`
- search count: `https://api.kurly.com/search/v3/sites/market/normal-search/count?keyword=<keyword>&filters=&allow_replace=true`
- goods detail page: `https://www.kurly.com/goods/<productNo>`

## 기본 흐름

1. 상품명/검색어가 없으면 먼저 물어봅니다.
2. `normal-search` 로 상품 후보를 찾습니다.
3. 후보가 너무 많으면 `count` endpoint 로 검색 결과 규모를 먼저 보여 줍니다.
4. 결과에서 상품명, 현재 가격, 할인율, 품절 여부, 배송 타입, 링크를 짧고 **보수적으로** 정리합니다.
5. 필요하면 `goods/<productNo>` 페이지의 `__NEXT_DATA__` 를 읽어 상세 정보를 보조 확인합니다.
6. 가격/품절/노출 정보는 시점에 따라 달라질 수 있으므로 조회 시각 기준 참고값이라고 답합니다.

## 예시

```js
const { countProducts, getProductDetail, searchProducts } = require("market-kurly-search")

async function main() {
const count = await countProducts("우유")
const search = await searchProducts("우유")
const detail = await getProductDetail(search.items[0].productNo)

console.log({ count, firstItem: search.items[0], detail })
}

main().catch((error) => {
console.error(error)
process.exitCode = 1
})
```

## 실전 운영 팁

- 검색어가 너무 넓으면 브랜드, 용량, 맛, 카테고리를 다시 물어보는 편이 안전합니다.
- 할인 상품은 `discountedPrice` 가 현재 가격이고 `salesPrice` 가 기준 가격일 수 있습니다.
- 품절 여부가 `false` 여도 실제 결제 시점에는 달라질 수 있으니 주문 가능을 확정처럼 말하면 안 됩니다.
- 비로그인 조회로는 장바구니/주문/주소 기반 배송 가능 여부를 확정할 수 없습니다.

## 라이브 확인 메모

2026-04-09 기준 아래 공개 호출이 로그인 없이 응답했습니다.

- `GET /search/v4/sites/market/normal-search?keyword=우유&page=1` → `no`, `name`, `salesPrice`, `discountedPrice`, `discountRate`, `isSoldOut`, `deliveryTypeNames` 확인
- `GET /search/v3/sites/market/normal-search/count?keyword=우유&filters=&allow_replace=true` → `count = 468` 확인
- `GET /goods/5063110` → `__NEXT_DATA__` 에서 상품명, 가격, 품절 여부, 배송 타입 확인

즉, **2026-04-09 기준으로는 마켓컬리 상품 검색과 가격 조회를 로그인 없이 구현할 수 있음** 을 다시 검증했습니다. 다만 이 표면은 웹 내부 사용 경로이므로 이후 스키마/헤더 요구사항이 바뀌면 수정이 필요할 수 있습니다.
3 changes: 2 additions & 1 deletion docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ npx --yes skills add <owner/repo> \
--skill fine-dust-location \
--skill han-river-water-level \
--skill daiso-product-search \
--skill market-kurly-search \
--skill olive-young-search \
--skill blue-ribbon-nearby \
--skill kakao-bar-nearby \
Expand Down Expand Up @@ -249,7 +250,7 @@ npm run ci
### Node 패키지

```bash
npm install -g @ohah/hwpjs kbo-game kleague-results lck-analytics toss-securities hipass-receipt k-lotto coupang-product-search used-car-price-search cheap-gas-nearby korean-law-mcp daiso bunjang-cli
npm install -g @ohah/hwpjs kbo-game kleague-results lck-analytics toss-securities hipass-receipt k-lotto coupang-product-search used-car-price-search cheap-gas-nearby korean-law-mcp market-kurly-search daiso bunjang-cli
export NODE_PATH="$(npm root -g)"
```

Expand Down
1 change: 1 addition & 0 deletions docs/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
- 근처 술집 조회 스킬 출시
- 택배 배송조회 스킬 출시 (CJ대한통운 / 우체국)
- 다이소 상품 조회 스킬 출시
- 마켓컬리 상품 조회 스킬 출시
- 올리브영 검색 스킬 출시
- 쿠팡 상품 검색 스킬 출시 (coupang-mcp 기반)
- 번개장터 검색 스킬 출시
Expand Down
3 changes: 3 additions & 0 deletions docs/sources.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@
- 다이소몰 상품 요약 목록: https://www.daisomall.co.kr/ssn/search/GoodsMummResult
- 다이소몰 매장 픽업 재고: https://www.daisomall.co.kr/api/pd/pdh/selStrPkupStck
- 다이소몰 온라인 재고: https://www.daisomall.co.kr/api/pdo/selOnlStck
- 마켓컬리 검색 API(v4): https://api.kurly.com/search/v4/sites/market/normal-search
- 마켓컬리 검색 개수 API(v3): https://api.kurly.com/search/v3/sites/market/normal-search/count
- 마켓컬리 상품 상세 페이지 예시: https://www.kurly.com/goods/5063110
- olive-young / multi-retail upstream repo (`hmmhmmhm/daiso-mcp`): https://github.com/hmmhmmhm/daiso-mcp
- olive-young CLI package (`daiso`): https://www.npmjs.com/package/daiso
- olive-young stores API: https://mcp.aka.page/api/oliveyoung/stores
Expand Down
135 changes: 135 additions & 0 deletions market-kurly-search/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
---
name: market-kurly-search
description: 로그인 없이 접근 가능한 마켓컬리 검색/상품 상세 표면으로 상품 후보, 현재 가격, 할인 여부, 품절 여부를 조회한다.
license: MIT
metadata:
category: retail
locale: ko-KR
phase: v1
---

# Market Kurly Search

## What this skill does

마켓컬리 웹앱이 실제로 사용하는 **비로그인 검색/상품 상세 표면**을 사용해 아래 흐름을 처리한다.

- 키워드로 상품 후보를 검색한다.
- 현재 가격과 할인 여부를 확인한다.
- 품절 여부와 배송 타입을 확인한다.
- 상품 링크를 함께 반환한다.
- **주문/장바구니 같은 액션은 하지 않는다. 조회형으로만 답한다.**

## When to use

- "마켓컬리에서 우유 얼마야?"
- "컬리에서 딸기 검색해줘"
- "이 상품 품절인지 보고 링크도 줘"
- "지금 컬리 가격만 빠르게 보고 싶어"

## When not to use

- 주문/장바구니/결제까지 자동화해야 하는 경우
- 주소 기반 배송 가능 여부나 회원 전용 가격을 확정해야 하는 경우
- 로그인 세션이 필요한 개인화 추천/찜 정보를 조회해야 하는 경우

## Prerequisites

- 인터넷 연결
- `node` 18+
- 이 저장소의 `market-kurly-search` package 또는 동일 로직

## Required inputs

### 1. Ask for a product keyword if it is missing

상품명 또는 검색어가 없으면 먼저 물어본다.

- 권장 질문: `찾을 마켓컬리 상품명이나 검색어를 알려주세요. 예: 우유, 딸기, 닭가슴살`
- 너무 넓으면: `검색어가 너무 넓어요. 브랜드나 용량까지 같이 알려주시면 가격 후보를 더 정확히 추릴 수 있어요.`

### 2. Confirm which candidate they want when the query is ambiguous

검색 결과가 여러 개면 상위 2~3개만 보여주고 다시 확인받는다.

- 권장 질문: `후보가 여러 개예요. 아래 상품 중 어떤 상품 가격을 볼까요?`
- 응답에는 상품명 + 현재 가격 + 품절 여부 + 링크를 같이 붙인다.

## Official Market Kurly surfaces

- search list: `https://api.kurly.com/search/v4/sites/market/normal-search?keyword=<keyword>&page=1`
- search count: `https://api.kurly.com/search/v3/sites/market/normal-search/count?keyword=<keyword>&filters=&allow_replace=true`
- product detail page: `https://www.kurly.com/goods/<productNo>`

## Workflow

### 1. Search by keyword first

```js
const { searchProducts } = require("market-kurly-search")

const result = await searchProducts("우유")
console.log(result.items.slice(0, 3))
```

검색 결과에서는 아래 필드를 우선 본다.

- 상품명
- 현재 가격 (`discountedPrice` 우선, 없으면 `salesPrice`)
- 할인율
- 품절 여부
- 배송 타입
- 상품 링크

### 2. Use the count endpoint when the result set is broad

```js
const { countProducts } = require("market-kurly-search")

const count = await countProducts("우유")
console.log(count)
```

후보가 너무 많으면 `count` 를 먼저 보여 주고 검색어를 좁히라고 안내한다.

### 3. Use the goods page detail as a fallback or follow-up lookup

```js
const { getProductDetail } = require("market-kurly-search")

const detail = await getProductDetail(5063110)
console.log(detail)
```

`goods/<productNo>` HTML 안의 `__NEXT_DATA__` 에서 상품명, 가격, 품절 여부, 배송 타입을 추출한다.

### 4. Respond conservatively

응답은 짧고 보수적으로 정리한다.

- 상품명
- 현재 가격
- 필요하면 원가/할인가 여부
- 품절 여부 또는 판매 가능 여부
- 상품 링크
- **가격/품절/노출 정보는 시점에 따라 달라질 수 있으니 조회 시각 기준 참고값이라고 분명히 말한다.**

## Done when

- 상품 키워드를 확인했다.
- 검색 결과에서 후보와 현재 가격을 최소 1개 이상 반환했다.
- 필요하면 상품 상세 페이지로 보조 확인했다.
- 주문/장바구니 같은 범위 밖 액션은 하지 않았다.

## Failure modes

- 검색어가 너무 넓으면 후보가 과도하게 많아질 수 있다.
- 가격/품절/배송 문구는 시점에 따라 달라질 수 있다.
- 현재 확인한 표면은 **공식 개발자 Open API가 아니라 웹이 쓰는 공개 표면** 이므로 스키마가 바뀌면 깨질 수 있다.
- 회원 전용/주소 전용 정보는 비로그인 조회만으로 확정할 수 없다.

## Notes

- 조회형 스킬이다.
- 비로그인 공개 표면 우선 원칙을 유지한다.
- 주문/장바구니/로그인 요구 기능은 시도하지 않는다.
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"lint": "node --check scripts/skill-docs.test.js scripts/korean_character_count.js scripts/test_korean_character_count.js && python3 -m py_compile scripts/fine_dust.py scripts/test_fine_dust.py scripts/ktx_booking.py scripts/test_ktx_booking.py scripts/sillok_search.py scripts/test_sillok_search.py scripts/korean_spell_check.py scripts/test_korean_spell_check.py scripts/patent_search.py scripts/test_patent_search.py scripts/mfds_drug_safety.py scripts/test_mfds_drug_safety.py scripts/mfds_food_safety.py scripts/test_mfds_food_safety.py && npm run lint --workspaces --if-present && ./scripts/validate-skills.sh",
"typecheck": "tsc --noEmit",
"test": "node --test scripts/skill-docs.test.js scripts/test_korean_character_count.js && PYTHONPATH=.:scripts python3 -m unittest scripts.test_fine_dust scripts.test_ktx_booking scripts.test_sillok_search scripts.test_korean_spell_check scripts.test_patent_search scripts.test_mfds_drug_safety scripts.test_mfds_food_safety && npm run test --workspaces --if-present && ./scripts/validate-skills.sh",
"pack:dry-run": "npm pack --workspace k-lotto --dry-run && npm pack --workspace daiso-product-search --dry-run && npm pack --workspace blue-ribbon-nearby --dry-run && npm pack --workspace kakao-bar-nearby --dry-run && npm pack --workspace cheap-gas-nearby --dry-run && npm pack --workspace kleague-results --dry-run && npm pack --workspace lck-analytics --dry-run && npm pack --workspace toss-securities --dry-run && npm pack --workspace hipass-receipt --dry-run && npm pack --workspace used-car-price-search --dry-run",
"pack:dry-run": "npm pack --workspace k-lotto --dry-run && npm pack --workspace daiso-product-search --dry-run && npm pack --workspace market-kurly-search --dry-run && npm pack --workspace blue-ribbon-nearby --dry-run && npm pack --workspace kakao-bar-nearby --dry-run && npm pack --workspace cheap-gas-nearby --dry-run && npm pack --workspace kleague-results --dry-run && npm pack --workspace lck-analytics --dry-run && npm pack --workspace toss-securities --dry-run && npm pack --workspace hipass-receipt --dry-run && npm pack --workspace used-car-price-search --dry-run",
"ci": "npm run lint && npm run typecheck && npm run test && npm run pack:dry-run",
"version-packages": "changeset version",
"release:npm": "changeset publish"
Expand Down
79 changes: 79 additions & 0 deletions packages/market-kurly-search/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# market-kurly-search

마켓컬리 웹이 실제로 사용하는 **비로그인 검색/상품 상세 표면**을 사용해 상품 후보와 현재 가격을 조회하는 Node.js 패키지입니다.

## 설치

배포 후:

```bash
npm install market-kurly-search
```

이 저장소에서 개발할 때:

```bash
npm install
```

## 사용 원칙

- 로그인 없이 확인 가능한 공개 웹 표면만 사용합니다.
- 현재 가격은 `discountedPrice` 가 있으면 그 값을, 없으면 `salesPrice` 를 사용합니다.
- 가격/품절/배송 문구는 시점에 따라 달라질 수 있으므로 조회 시각 기준 참고값으로만 답해야 합니다.
- 장바구니/주문/주소 기반 배송 가능 여부 같은 회원/액션 기능은 범위 밖입니다.

## 사용 예시

```js
const { countProducts, getProductDetail, searchProducts } = require("market-kurly-search")

async function main() {
const searchResult = await searchProducts("우유")
const detailResult = await getProductDetail(searchResult.items[0].productNo)
const countResult = await countProducts("우유")

console.log(countResult)
console.log(searchResult.items[0])
console.log(detailResult)
}

main().catch((error) => {
console.error(error)
process.exitCode = 1
})
```

## 공개 API

- `searchProducts(keyword, options?)`
- `countProducts(keyword, options?)`
- `getProductDetail(productNo, options?)`

## Live smoke snapshot

2026-04-09 기준 live smoke test 에서 아래 공개 표면이 로그인 없이 응답했습니다.

```json
{
"count": {
"query": "우유",
"count": 468
},
"firstSearchItem": {
"productNo": 5063110,
"name": "[연세우유 x 마켓컬리] 전용목장우유 900mL",
"currentPrice": 2780,
"isSoldOut": false,
"goodsUrl": "https://www.kurly.com/goods/5063110"
},
"detail": {
"productNo": 5063110,
"name": "[연세우유 x 마켓컬리] 전용목장우유 900mL",
"currentPrice": 2780,
"deliveryTypeNames": [
"샛별배송(내일 아침)"
]
}
}
```
Loading
Loading