넥스트에디션 캠핏은 예약·리뷰·콘텐츠가 결합된 밀도 높은 아웃도어 데이터를 가진 곳이라, 캠핑장을 조건에 맞게 근거와 함께 추천하는 이 도구는 캠핏의 실제 예약·리뷰 데이터가 있어야 추천을 검증한다.
'예약/리뷰/콘텐츠/커머스 데이터가 보기 드문 밀도'가 자산. 리뷰 근거 기반 조건 추천은 그 밀도를 바로 쓰는 1주 단위.
'AI를 기능 하나가 아니라 경험 전반을 바꾸는 플랫폼'. 추천+근거 요약이 탐색 경험의 핵심.
'캠핑을 넘어 커뮤니티·커머스로 확장'. 조건 추천은 커머스 연계의 입력이 됨.
캠핑장 예약·리뷰 데이터를 받아 사용자 조건(반려견·아이동반·우천)에 맞는 곳을 리뷰 근거와 함께 추천하고 요약하는 1주 포트폴리오.
캠핏 PE. 사용자가 리뷰를 일일이 뒤지던 탐색을, 조건 추천 + 리뷰 근거 요약 도구로 옮기고 싶은 사람.
캠핑장에 리뷰·속성 데이터를 붙이면, LLM 이 (a) 사용자 조건에 맞는 캠핑장을 리뷰 근거와 함께 추천하고 (b) 왜 맞는지 리뷰를 요약해 보여준다.
캠핑장 예약·리뷰 데이터를 받아 사용자 조건에 맞는 곳을 리뷰 근거(RAG)와 함께 추천하고 요약하는 동작하는 도구.
추천한 캠핑장의 클릭/예약 전환으로 적합도를 재고, 근거 인용 정확도를 보여주는 대시보드.
조건에 맞는 장비·콘텐츠까지 연계 추천해 탐색→예약→커머스로 잇는 단계.
조건을 넣으면 맞는 캠핑장과 리뷰 근거 요약이 몇 초 안에 나오는 걸 시연한다
추천 근거로 인용한 리뷰 구절이 실제 리뷰에 존재하는 비율 100%. 없으면 환각으로 자동 차단
측정: 인용 일치 규칙, 표본: 추천 200건
일부러 만든 상충·결측 리뷰·미등록 캠핑장 30종을 넣어도 추천이 멈추지 않고 처리되는 걸 시연한다
위 수치는 더미데이터 자가채점이며, 실제 추천 적합도는 캠핏 실데이터 + 실 검수자가 있어야 측정 가능. 이 포트폴리오는 그 검증을 바로 돌릴 수 있는 상태까지 만든다.
이건 고객·실데이터 없이 혼자 만든 포트폴리오라 모든 수치는 합성 데이터 자가채점이다. 면접에서 '데모 동작 + 실검증 준비 상태'로 솔직히 설명하는 게 오히려 신뢰를 준다
정답을 내가 만들었으니 잘 맞는 게 당연. 그래서 결측·경계·미등록·표기 흔들림 케이스를 더미에 일부러 섞어 쉬운 문제만 푸는 게 아님을 보여준다
리뷰 편향: 리뷰 많은 캠핑장이 유리해질 수 있어 review_count 보정·신규 캠핑장 노출 규칙을 둔다
실제 도입 시 달라지는 것: 실 리뷰 분포·계절성이 더미와 다르므로 매칭 가중치를 실데이터로 다시 맞춰야 한다(도입 단계 과제)
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
캠핑장 속성·평점·리뷰수를 묶어 조건 매칭 추천 입력으로 쓴다
| site_id | name_raw | site_key | pet_ok | kid_friendly | rain_cover | rating | review_count | price_krw |
|---|---|---|---|---|---|---|---|---|
| S900 | OO글램핑(구명칭) | — | — | true | — | — | 0 | 45,000 |
| S901 | 무평점신규 | site_901 | true | — | false | — | 0 | 50,000 |
| S902 | 만점캠핑 | site_902 | true | true | true | 5 | 1 | 99,000 |
| S903 | 미등록캠핑 | — | false | false | false | 2 | 3 | 20,000 |
| S000 | 캠핑장0 | site_0 | false | true | true | 3.5 | 10 | 30,000 |
z.object({ site_id: z.string(), name_raw: z.string(), site_key: z.string().nullable(), pet_ok: z.boolean().nullable(), kid_friendly: z.boolean().nullable(), rain_cover: z.boolean().nullable(), rating: z.number().nullable(), review_count: z.number(), price_krw: z.number() }). 쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라: rating/pet_ok/rain_cover 가 null(정보 미수집), review_count 0(신규·무리뷰), rating 5.0+review_count 1(표본 1개 과신 함정), site_key null 미등록, name_raw 표기 흔들림(구명칭/별칭). 형식 깨뜨리기 시도 3건도 별도로.z.object({ site_id: z.string(), name_raw: z.string(), site_key: z.string().nullable(), pet_ok: z.boolean().nullable(), kid_friendly: z.boolean().nullable(), rain_cover: z.boolean().nullable(), rating: z.number().nullable(), review_count: z.number(), price_krw: z.number() })| Node.js | 리뷰·예약 ingest + 추천 API (`POST /recommend`. 조건 → 캠핑장 + 근거) |
| React | 추천 결과 + 리뷰 근거 카드 UI |
| AWS | 배포 + 리뷰 임베딩 벡터 저장 |
| LLM | 조건 매칭 설명 + 리뷰 요약 생성 |
| RAG | 리뷰 검색으로 추천 근거(실제 리뷰 구절) 인용 |
✓ pass → 그대로 멈춰도 완성품. 더 키우려면 2주차(추천 적합도 검증)로.
✓ pass → 여기서 멈춰도 완결. 더 키우려면 3주차(커머스 확장)로.
조건 추천 결과(추천 + 리뷰 근거 인용)
z.object({ site_id: z.string(), match_score: z.number(), reasons: z.array(z.object({ condition: z.string(), review_quote: z.string() })), summary: z.string() })[
{
"site_id": "S001",
"match_score": 0.82,
"reasons": [
{
"condition": "pet_ok",
"review_quote": "반려견 동반 가능했고 울타리가 잘 돼있어요"
}
],
"summary": "반려견·우천 대비 모두 충족"
},
{
"site_id": "S901",
"match_score": 0.4,
"reasons": [],
"summary": "리뷰 없음. 추천 근거 부족, 확인 필요"
},
{
"site_id": "S900",
"match_score": 0.5,
"reasons": [
{
"condition": "kid_friendly",
"review_quote": "아이들 놀기 좋아요"
}
],
"summary": "반려견 정보 결측. 일부만 확인"
}
]reasons 의 review_quote 가 실제 리뷰에 존재하는지는 자동 검증, summary 적절성은 사람/judge 검증.