넥스트에디션
경력근거: 예약/리뷰/콘텐츠/커머스/운영 데이터는 아웃도어 시장에서 보기 드문 밀도와 범위
넥스트에디션의 캠핏은 예약과 리뷰와 운영 데이터가 이미 밀도 높게 쌓인 서비스라서, 새로 모을 데이터가 아니라 그 데이터를 어떻게 질의하게 만드느냐가 제품의 승부처입니다. 공고가 AI 를 기능 하나로 붙이는 것이 아니라 경험 전반을 바꾸겠다고 못박았기 때문에, 검색 결과 옆에 요약 상자를 덧붙이는 대신 필터를 하나씩 켜던 탐색 자체를 문장 한 줄과 확정 관문으로 바꾸는 화면을 골랐습니다. 프런트엔드와 백엔드와 어드민을 함께 만들고 AWS 에서 운영까지 맡는 자리라서 화면과 API 와 데이터베이스와 배포를 한 사람이 끝낸 완결품으로 만들고, 우대사항의 큐와 배치 비동기 처리는 3주차의 검색 로그 집계로 닿게 둡니다. 이 포트폴리오는 자연어를 검증 가능한 질의로 바꾸고 사람의 확정을 거쳐 실행하는 웹 풀스택 기술을 보여주며 캠핑과 아웃도어 도메인 전문성은 주장하지 않습니다. 모르는 깊이는 면접에서 모른다고 말하는 편이 낫습니다.
예약과 리뷰와 운영 데이터가 이미 밀도 높게 쌓여 있는 곳이라서, 새 데이터를 모으는 제품이 아니라 그 데이터를 어떻게 질의하느냐를 바꾸는 제품으로 잡습니다. 문장에서 뽑은 조건이 곧 재고 질의가 되고 리뷰 문구가 곧 근거가 됩니다.
AI 를 기능 하나로 붙이지 않고 경험 전반을 바꾸겠다는 자리라서, 검색창 옆에 요약 상자를 붙이는 대신 필터를 켜고 끄던 탐색 자체를 문장 한 줄과 확정 관문으로 갈아 끼웁니다.
프런트엔드와 백엔드와 어드민을 함께 만들고 클라우드 운영까지 맡는 자리라서, 기능 목록을 늘어놓는 대신 검색 화면 하나를 화면과 API 와 데이터베이스와 배포까지 끝낸 완결품으로 보입니다.
캠핑장을 찾는 사용자가 조건을 문장 한 줄로 쓰면, 화면이 그 문장을 예약 검색 조건 칩으로 쪼개어 보여 주는 캠핏 검색 화면입니다. 날짜가 여러 주말로 읽히거나 금액이 1박인지 총액인지 갈리거나 검색할 필드가 없는 표현은 확인 대기 칩이 되고, 확인 대기가 하나라도 남아 있으면 검색 버튼이 잠깁니다. 사용자가 후보를 고르거나 값을 직접 고쳐 모든 칩을 확정한 뒤에야 그 조건이 실제 재고 질의로 실행되며, 결과 카드마다 어떤 조건이 문장의 어느 구절에서 나왔는지와 리뷰의 어느 문장이 근거인지가 함께 붙습니다.
주말에 갈 캠핑장을 고르려고 반려견과 사이트 종류와 날짜 필터를 하나씩 켰다 껐다 하다가 결국 리뷰 탭에서 화장실을 따로 검색하며 저녁을 보내던 캠핏 사용자가, 머릿속 조건을 한 문장으로 쓰면 그것이 어떤 검색 조건으로 읽혔는지 눈으로 보고 고친 다음 그날 예약이 열린 곳만 받고 싶은 사람입니다.
문장에서 뽑은 검색 조건을 실행 전에 칩으로 드러내고 뜻이 갈리는 것만 사용자에게 고르게 하면, 사용자는 자기가 무엇을 검색했는지 알고 결과를 믿게 되며 조건을 잘못 읽은 검색이 결과 페이지까지 흘러가지 않는다는 가정입니다.
문장을 입력하면 예약 검색 조건 칩이 나오고, 뜻이 갈리는 칩은 확인 대기로 잠기며, 사용자가 후보를 고르거나 값을 고쳐 모두 확정한 뒤에야 재고 질의가 실행되어 결과 카드와 근거 구절이 나오는 웹 화면입니다. 만드는 덩어리는 조건 확정 관문과 질의 실행 결과 둘이며 평일 5일에 혼자 끝나는 범위입니다.
확정된 조건으로 결과가 0건이면 어떤 조건을 풀면 몇 곳이 살아나는지 조건별로 세어 보여 주고, 사용자가 풀 조건을 고른 뒤에 재질의가 도는 완화 흐름입니다. 이 주차부터 화면과 API 를 AWS 에 올려 실제 주소로 운영합니다.
검색 요청을 큐에 넣고 배치로 모아, 어떤 표현이 확인 대기로 자주 갔고 사용자가 무엇을 골랐는지 집계해 보여 주는 어드민 화면입니다. 표현별 확인 대기 빈도와 사용자가 고친 값의 분포를 보고 다음에 손댈 표현을 고릅니다.
문장을 한 줄 쓰면 조건 칩이 나오고 확인 대기를 확정해야 검색이 열리며 결과 카드에 근거 구절이 붙는 흐름을 처음부터 끝까지 시연합니다.
이번 주말과 가성비와 화장실 깨끗한 곳처럼 뜻이 갈리는 표현을 심어 둔 문장에서 그 표현이 모두 확인 대기로 잠기고, 반려견 동반과 10월 17일처럼 확정할 수 있는 표현은 잠기지 않는 것을 보입니다. 다만 정답 라벨과 데이터를 작성자가 만든 합성 기준이라 성능 검증이 아니라 데모 조건 동작입니다.
조건의 근거 구간이 입력 문장의 부분 문자열이고 리뷰 인용이 그 캠핑장 리뷰의 부분 문자열인지 규칙으로 검사해, 어긋난 건은 화면에 띄우지 않고 막는 것을 보입니다.
오타와 부정문과 조건이 없는 문장과 이모지가 섞인 입력 30종을 넣어도 화면이 죽지 않고 무엇을 못 읽었는지 남기는 것을 보입니다.
2주차에서 정답 라벨을 붙인 문장으로 조건 필드 일치율과 확인 대기 재현율 두 가지를 잽니다 (측정: 라벨 대조 규칙, 표본: 문장 100건).
위 수치는 더미데이터 자가채점이며, 실제 검색 만족도와 예약 전환은 캠핏의 실제 검색 로그와 실제 재고가 있어야 측정 가능합니다. 이 포트폴리오는 그 검증을 바로 돌릴 수 있는 상태까지 만듭니다.
수치는 합성 데이터 자가채점이라 성능 증명이 아닙니다. 면접에서는 데모가 도는 상태와 실검증 준비 상태로 솔직히 설명합니다.
정답 라벨을 작성자가 만들었으니 잘 맞는 것이 당연합니다. 주말 날짜가 두 갈래로 읽히는 문장과 금액 기준이 빠진 문장과 검색할 필드가 없는 표현과 부정문과 조건이 아예 없는 문장을 더미에 일부러 섞어 두었습니다.
확인 대기를 너무 자주 띄우면 검색이 번거로워지고 너무 안 띄우면 조건을 잘못 읽은 채 결과가 나갑니다. 확인 대기 재현율과 함께 확정 가능한 조건을 괜히 물어본 비율도 같이 재고, 어느 쪽으로 치우쳤는지 숫자로 보이게 둡니다.
이 포트폴리오는 자연어를 검증 가능한 질의로 바꾸고 사람의 확정을 거쳐 실행하는 웹 풀스택 기술을 보여주며 캠핑과 아웃도어 도메인 전문성은 주장하지 않습니다. 실제 도입에서는 성수기 요금과 사이트별 재고 규칙이 더미보다 훨씬 복잡하므로 필드 목록부터 다시 맞춰야 합니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
캠핑장 속성과 날짜별 재고와 리뷰 문장을 담은 더미입니다. 확정된 조건을 실제 질의로 실행하고 검색할 필드가 없는 표현을 리뷰 문장으로 받아 내기 위한 입력입니다.
| site_id | name_raw | region_raw | region_code | site_kind | pet_ok | kid_facility | price_per_night | available_dates | review_snippets |
|---|---|---|---|---|---|---|---|---|---|
| S-101 | 가평 물소리 오토캠핑장 | 경기 가평 | R-gg-gp | 오토캠핑 | true | true | 55,000 | ["2026-10-17","2026-10-18","2026-10-24"] | ["화장실이 새로 지은 것처럼 깨끗해서 아이 데리고 가기 좋았어요","강아지 데리고 갔는데 울타리가 있어서 마음이 놓였습니다"] |
| S-102 | 홍천 자작나무 글램핑(구 자작캠핑) | 강원 홍천 | R-gw-hc | 글램핑 | false | true | 129,000 | ["2026-10-18"] | ["난방이 잘 돼서 늦가을에도 춥지 않았어요","반려견은 못 들어간다고 안내받았습니다"] |
| S-103 | 영서 들판 노지터 | 영서 | — | 노지 | true | false | — | ["2026-10-17","2026-10-18","2026-10-19"] | ["전기가 없어서 파워뱅크를 챙겨야 합니다"] |
| S-104 | 태안 바다앞 카라반 | 충남 태안 | R-cn-ta | 카라반 | — | true | 98,000 | [] | ["바다가 바로 보여서 뷰는 최고였어요","성수기라 예약이 금방 마감됩니다"] |
| S-105 | 평창 별빛 오토캠핑 | 강원 평창 | R-gw-pc | 오토캠핑 | true | — | 45,000 | ["2026-10-24","2026-10-25"] | ["밤에 별이 정말 잘 보입니다","샤워장은 조금 낡은 편이에요"] |
아래 JSON Schema에 맞는 캠핑장 행을 30건 더 만들어 주세요. {"type":"object","properties":{"site_id":{"type":"string"},"name_raw":{"type":"string"},"region_raw":{"type":"string"},"region_code":{"type":["string","null"]},"site_kind":{"type":"string","enum":["오토캠핑","글램핑","카라반","노지"]},"pet_ok":{"type":["boolean","null"]},"kid_facility":{"type":["boolean","null"]},"price_per_night":{"type":["number","null"]},"available_dates":{"type":"array","items":{"type":"string"}},"review_snippets":{"type":"array","items":{"type":"string"}}},"required":["site_id","name_raw","region_raw","site_kind","available_dates","review_snippets"]} 쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라. 첫째 region_raw가 영서나 중부처럼 행정구역이 아닌 표기라 region_code가 null인 행, 둘째 pet_ok나 kid_facility가 null이라 조건 판정이 불가능한 행, 셋째 available_dates가 빈 배열이라 검색 결과에서 빠져야 하는 행, 넷째 review_snippets가 빈 배열인 신규 캠핑장과 리뷰 문구가 조건과 반대로 적힌 행, 다섯째 price_per_night가 0이거나 null인 무료 노지와 문의 대상 행, 여섯째 name_raw에 구 명칭과 괄호 표기가 섞인 행. 형식을 깨뜨리는 시도는 3건까지 별도로 만들어 주세요.{"type":"object","properties":{"site_id":{"type":"string"},"name_raw":{"type":"string"},"region_raw":{"type":"string"},"region_code":{"type":["string","null"]},"site_kind":{"type":"string","enum":["오토캠핑","글램핑","카라반","노지"]},"pet_ok":{"type":["boolean","null"]},"kid_facility":{"type":["boolean","null"]},"price_per_night":{"type":["number","null"]},"available_dates":{"type":"array","items":{"type":"string"}},"review_snippets":{"type":"array","items":{"type":"string"}}},"required":["site_id","name_raw","region_raw","site_kind","available_dates","review_snippets"]}사용자가 검색창에 쓴 문장과 그 문장에서 나와야 할 정답 조건과 확인 대기로 잠겨야 할 항목을 붙여 둔 더미입니다. 조건 변환과 확인 대기 판정을 라벨 대조 규칙으로 채점하기 위한 입력입니다.
| utterance_id | text | gold_filters | gold_clarify | memo |
|---|---|---|---|---|
| U-001 | 10월 17일에 가평에서 강아지랑 갈 오토캠핑장 | [{"field":"available_dates","op":"contains","value":"2026-10-17"},{"field":"region_code","op":"eq","value":"R-gg-gp"},{"field":"pet_ok","op":"eq","value":true},{"field":"site_kind","op":"eq","value":"오토캠핑"}] | [] | 모두 확정 가능해 잠금이 걸리면 안 되는 문장입니다 |
| U-002 | 이번 주말에 애들이랑 갈 만한 곳 | [{"field":"kid_facility","op":"eq","value":true}] | ["available_dates"] | 이번 주말이 토요일 1박인지 일요일까지인지 갈려 날짜를 확인 대기로 … |
| U-003 | 가성비 좋은 글램핑 추천해줘 | [{"field":"site_kind","op":"eq","value":"글램핑"}] | ["price_per_night"] | 가성비에 해당하는 금액 상한이 문장에 없어 사용자가 정해야 합니다 |
| U-004 | 화장실 깨끗한 데로 알려주세요 | [] | ["review_snippets"] | 검색 가능한 필드가 없어 리뷰 문장 검색으로 넘길지 사용자가 골라야 … |
| U-005 | 강원도 6만원 이하 2박 | [{"field":"region_code","op":"prefix","value":"R-gw"}] | ["price_per_night","available_dates"] | 6만원이 1박 기준인지 2박 총액인지 갈리고 날짜도 지정되지 않았습니다 |
아래 JSON Schema에 맞는 검색 문장과 정답 라벨을 30건 더 만들어 주세요. {"type":"object","properties":{"utterance_id":{"type":"string"},"text":{"type":"string"},"gold_filters":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"op":{"type":"string"},"value":{}},"required":["field","op","value"]}},"gold_clarify":{"type":"array","items":{"type":"string"}},"memo":{"type":["string","null"]}},"required":["utterance_id","text","gold_filters","gold_clarify"]} 쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라. 첫째 이번 주말이나 다음 달 초처럼 날짜가 여러 갈래로 읽혀 available_dates가 gold_clarify에 들어가는 문장, 둘째 가성비나 저렴한처럼 금액 상한이 빠져 price_per_night가 잠기는 문장, 셋째 화장실이나 조용함처럼 검색 가능한 필드가 없어 review_snippets로 넘어가는 문장, 넷째 부정문이 섞였지만 확정 값이 있어 gold_clarify가 비어야 하는 문장, 다섯째 상호에 오타가 있어 후보 선택이 필요한 문장, 여섯째 조건이 하나도 없는 인사말. 형식을 깨뜨리는 시도는 3건까지 별도로 만들어 주세요.{"type":"object","properties":{"utterance_id":{"type":"string"},"text":{"type":"string"},"gold_filters":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"op":{"type":"string"},"value":{}},"required":["field","op","value"]}},"gold_clarify":{"type":"array","items":{"type":"string"}},"memo":{"type":["string","null"]}},"required":["utterance_id","text","gold_filters","gold_clarify"]}| Node.js | 문장을 조건 계획으로 바꾸는 변환 API 와 확정된 조건을 재고 질의로 실행하는 검색 API 를 구현합니다. |
| React | 조건 칩과 확인 대기 잠금과 근거 구절 표시까지 검색 화면 전체를 만듭니다. 확인 대기 칩이 남으면 검색 버튼이 잠기는 것이 화면의 핵심 상태입니다. |
| PostgreSQL | 캠핑장 속성과 날짜별 재고와 리뷰 문장을 담는 스키마를 설계하고, 조건 조합 질의가 느려지지 않도록 인덱스를 잡습니다. |
| AWS | 2주차부터 화면과 API 를 AWS 에 올려 실제 주소로 운영하고 3주차 배치까지 같은 환경에서 돌립니다. |
| LLM · RAG | 문장에서 조건 후보를 뽑고 뜻이 갈리는 부분을 확인 대기로 표시합니다. 검색할 필드가 없는 표현은 리뷰 문장 검색으로 후보 구절을 찾아 근거로 인용합니다. |
| 이벤트 기반 · 큐/배치 | 3주차에서 검색 요청을 큐에 넣고 배치로 모아 표현별 확인 대기 빈도를 집계합니다. |
| 백오피스/어드민 | 3주차 집계 결과를 운영자가 보는 어드민 화면으로 만들어 다음에 손댈 표현을 고르게 합니다. |
✓ pass → 그대로 멈춰도 완결입니다. 더 키우려면 2주차에서 결과 0건일 때의 조건 완화 흐름을 붙입니다.
✓ pass → 여기까지도 완결입니다. 더 키우려면 3주차에서 검색 요청을 큐로 모아 어드민 집계 화면을 만듭니다.
문장 하나에 대한 검색 질의 계획입니다. 조건마다 문장의 어느 구간에서 나왔는지와 확정인지 확인 대기인지를 담아, 화면이 어떤 칩을 잠글지와 검색 버튼을 열지 말지를 결정합니다.
{"type":"object","properties":{"utterance_id":{"type":"string"},"filters":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"op":{"type":"string"},"value":{},"source_span":{"type":"string"},"status":{"type":"string","enum":["confirmed","needs_confirm"]},"options":{"type":"array","items":{}},"clarify_reason":{"type":["string","null"]}},"required":["field","op","source_span","status"]}},"unmapped_phrases":{"type":"array","items":{"type":"object","properties":{"span":{"type":"string"},"reason":{"type":"string"}},"required":["span","reason"]}},"ready_to_run":{"type":"boolean"},"hedge":{"type":["string","null"]}},"required":["utterance_id","filters","unmapped_phrases","ready_to_run"]}[
{
"utterance_id": "U-001",
"filters": [
{
"field": "available_dates",
"op": "contains",
"value": "2026-10-17",
"source_span": "10월 17일",
"status": "confirmed",
"options": [],
"clarify_reason": null
},
{
"field": "region_code",
"op": "eq",
"value": "R-gg-gp",
"source_span": "가평",
"status": "confirmed",
"options": [],
"clarify_reason": null
},
{
"field": "pet_ok",
"op": "eq",
"value": true,
"source_span": "강아지랑",
"status": "confirmed",
"options": [],
"clarify_reason": null
},
{
"field": "site_kind",
"op": "eq",
"value": "오토캠핑",
"source_span": "오토캠핑장",
"status": "confirmed",
"options": [],
"clarify_reason": null
}
],
"unmapped_phrases": [],
"ready_to_run": true,
"hedge": null
},
{
"utterance_id": "U-002",
"filters": [
{
"field": "kid_facility",
"op": "eq",
"value": true,
"source_span": "애들이랑",
"status": "confirmed",
"options": [],
"clarify_reason": null
},
{
"field": "available_dates",
"op": "contains",
"source_span": "이번 주말",
"status": "needs_confirm",
"options": [
"2026-10-17",
"2026-10-18",
"2026-10-17 부터 2026-10-18 까지"
],
"clarify_reason": "주말이 토요일 1박인지 일요일까지인지 문장에서 정해지지 않아 사용자가 골라야 합니다"
}
],
"unmapped_phrases": [],
"ready_to_run": false,
"hedge": "날짜 후보는 오늘 기준으로 계산한 합성 기준이라 실제 서비스에서는 사용자 지역과 시간대를 함께 봐야 합니다"
},
{
"utterance_id": "U-004",
"filters": [],
"unmapped_phrases": [
{
"span": "화장실 깨끗한",
"reason": "검색 가능한 필드에 없어 리뷰 문장 검색으로 넘길지 사용자가 골라야 합니다"
}
],
"ready_to_run": false,
"hedge": null
},
{
"utterance_id": "U-012",
"filters": [],
"unmapped_phrases": [
{
"span": "안녕하세요",
"reason": "검색 조건으로 읽을 표현이 없습니다"
}
],
"ready_to_run": false,
"hedge": null
}
][자동 검증] 1. 계획 JSON 이 위 스키마를 통과하는지 확인합니다. status 가 두 값 밖이면 그 행을 버립니다. 2. 모든 filters 의 source_span 이 입력 문장의 부분 문자열인지 확인합니다. 아니면 지어낸 근거이므로 화면에 띄우지 않습니다. 3. status 가 needs_confirm 이면 value 가 비어 있고 options 가 하나 이상이며 clarify_reason 이 있어야 합니다. 확인 대기인데 값이 이미 박혀 있으면 관문이 무의미해집니다. 4. needs_confirm 이 하나라도 있거나 unmapped_phrases 가 비어 있지 않으면 ready_to_run 이 false 여야 합니다. 화면의 검색 버튼 잠금이 이 값 하나로 결정됩니다. 5. 사용자가 확정을 마친 계획을 질의로 조립해 실행한 결과가, 같은 조건을 기준 구현으로 돌린 결과와 집합으로 같은지 대조합니다. 6. 결과 카드에 붙은 리뷰 인용이 그 캠핑장 review_snippets 의 부분 문자열인지 확인합니다. [수동 검증] 1. 확인 대기로 잠긴 문장 20건을 읽고 사용자가 실제로 고를 만한 후보가 제시됐는지 확인합니다. 2. 확정으로 통과한 문장 20건을 읽고 괜히 물어보지 않았는지와 조건을 빠뜨리지 않았는지 확인합니다.