尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证전국 자연휴양림 172곳을 지도·목록으로. 예약 방식과 다음 오픈 일정, 산림청 공식 경쟁률, 블로그·카페 인기도를 함께 봅니다. (숲나들e 비공식 정리)
翻译准备中
正在加载完整 README。 以下为已保存的摘录。
전국 자연휴양림 172곳을 지도와 목록으로 한 번에 펼쳐 놓고, 언제 어떻게 예약하는지와 얼마나 경쟁이 센지를 함께 보여 주는 정적 웹앱입니다. 숲나들e는 예약은 한 곳으로 모아 놨지만 "어디를 고를지"는 판단하기 어렵습니다. 휴양림이 지역 드롭다운 뒤에 숨어 있고, 지도는 개별 휴양림 페이지에 하나씩 박혀 있고, 선착순인지 추첨인지는 안내 페이지 텍스트로만 있고, 어디가 인기 있는지는 알 길이 없습니다. 이 사이트의 핵심은 서로 다른 두 지표를 섞지 않고 나란히 놓는 것입니다. 식당으로 치면 하나는 미슐랭 스타, 하나는 지도 별점입니다. 경쟁률 칸이 비어 있는 것은 인기가 없다는 뜻이 아니라 공식 수치가 없다는 뜻입니다. 두 값이 갈리는 곳이 재미있습니다. 진부령은 2026년 성수기 경쟁률 1위(24.81:1)인데 관심 등록은 국립 46곳 중 45위(456명)입니다 — 2025년에 새로 열려 아직 알려지지 않았을 뿐, 실제로는 가장 뚫기 어려운 곳입니다. 두 값을 하나로 섞었으면 사라졌을 정보입니다. 반대로 관심은 많은데 경쟁률이 비어 있으면 공립이라 추첨 통계가 안 나오는 곳일 수 있습니다.查看完整 README ↗
전국 자연휴양림 172곳을 지도와 목록으로 한 번에 펼쳐 놓고, 언제 어떻게 예약하는지와 얼마나 경쟁이 센지를 함께 보여 주는 정적 웹앱입니다.
숲나들e는 예약은 한 곳으로 모아 놨지만 "어디를 고를지"는 판단하기 어렵습니다. 휴양림이 지역 드롭다운 뒤에 숨어 있고, 지도는 개별 휴양림 페이지에 하나씩 박혀 있고, 선착순인지 추첨인지는 안내 페이지 텍스트로만 있고, 어디가 인기 있는지는 알 길이 없습니다.
이 사이트의 핵심은 서로 다른 두 지표를 섞지 않고 나란히 놓는 것입니다. 식당으로 치면 하나는 미슐랭 스타, 하나는 지도 별점입니다.
| 공식 경쟁률 🏅 | 인기도 ★ | |
|---|---|---|
| 누가 매기나 | 산림청 국립자연휴양림관리소 | 숲나들e 이용자 (관심 등록) |
| 무엇을 재나 | 실제 추첨 신청 경쟁률 | 예약하고 싶어 하는 정도 |
| 언제 나오나 | 여름 성수기 추첨 후 연 1회 | 매주 갱신 |
| 얼마나 붙나 | 상위 몇 곳만 (2026년엔 5곳) | 자연휴양림 172곳 전부 |
경쟁률 칸이 비어 있는 것은 인기가 없다는 뜻이 아니라 공식 수치가 없다는 뜻입니다.
두 값이 갈리는 곳이 재미있습니다. 진부령은 2026년 성수기 경쟁률 **1위(24.81:1)**인데 관심 등록은 국립 46곳 중 **45위(456명)**입니다 — 2025년에 새로 열려 아직 알려지지 않았을 뿐, 실제로는 가장 뚫기 어려운 곳입니다. 두 값을 하나로 섞었으면 사라졌을 정보입니다. 반대로 관심은 많은데 경쟁률이 비어 있으면 공립이라 추첨 통계가 안 나오는 곳일 수 있습니다.
기본 화면은 자연휴양림 172곳입니다. 숲나들e 목록에는 자연휴양림이 아닌 산림휴양시설 27곳(국립등산학교·산악박물관·동서트레일·산림레포츠파크·숲체험장 등)이 함께 들어 있는데, 버리지 않고 자료에 따로 구분해 담아 두되 화면에는 올리지 않습니다.
숲나들e 추첨 이용안내에 적힌 규칙을 그대로 옮겼습니다.
| 유형 | 방식 | 곳 |
|---|---|---|
| 국립 자연휴양림 | 선착순 + 추첨 | 45 |
| 국립 (아세안휴양림·그 외 시설) | 선착순 | 13 |
| 공립 | 휴양림별 상이 (월추첨·지역주민추첨 일부) | 130 |
| 사립 | 개별 예약 | 11 |
아세안휴양림은 아세안 10개국 출신 외국인 우선예약 정책에 따라 추첨 대상에서 빠집니다. 추첨 대상 휴양림은 매번 숲나들e 공지로 확정되니 신청 전 확인하세요.
crawl_forest.py 숲나들e 기본정보 -> data/raw_forests.json (약 3분)
crawl_popularity.py 숲나들e 관심수 -> data/popularity.json (약 1분 30초)
crawl_details.py 소개 사진·요금표 -> data/details.json (약 5분 30초)
data/competition.json 산림청 발표 경쟁률 (수기 관리)
↓
build_dataset.py 대조·가공 -> forests.json, sheet.json (요금 해석은 fees.py)
의존성이 없습니다. 파이썬 표준 라이브러리만 씁니다.
python3 crawl_forest.py
python3 crawl_popularity.py
python3 crawl_details.py
python3 build_dataset.py
crawl_forest.py)| 쓰는 곳 | 얻는 것 |
|---|---|
GET /rep/cm/remmnAreaOrRcfclList.do | 전체 목록 199곳 — 시도·시군구·국공사립 |
POST /com/sub/selectMapInfo.do | 위경도·주소·전화·입퇴실시각·수용인원·개장년도·소개글 |
POST /com/selectInsttInfoList.do | 태그·관심수·대표이미지 |
robots.txt 에서 전부 허용된 경로이고, 요청 간격을 0.4초 둡니다. 좌표·주소·전화는 199곳 모두 채워집니다.
태그 검색은 자유 입력이라 전체 태그 목록을 주는 API가 없습니다. 씨앗 태그로 시작해 응답에 실려 오는 태그를 주워 다시 검색하는 식으로 넓혀 가는데, 그래도 67곳에만 붙습니다. 그래서 소개글에서 키워드를 뽑아 특징 태그를 따로 만들고(계곡·바다·편백…) 특징 셀렉트에는 그쪽만 씁니다. 숲나들e 원본 태그는 상세 화면 배지로만 남습니다.
월별 예약현황(monthRsrvtStatus.do)은 로그인을 요구해서(HTTP 401) 쓰지 않습니다.
실시간 잔여 객실은 이 사이트에 없습니다.
data/competition.json)자동 수집이 아니라 수기 파일입니다. 산림청이 여름 성수기 추첨 후 발표하는 보도자료 수치를 옮겨 적고 출처를 함께 박아 둡니다.
2026년 발표분(2026-06-18): 전체 평균 5.91:1 (객실 6.88 · 야영 2.22), 접수 137,691건 중 23,289건 당첨. 휴양림별 상위 5곳 — 진부령 24.81 · 무의도 13.57 · 대야산 12.79 · 신시도 12.18 · 용지봉 11.15. 최고 객실은 대야산 숲속의 집 601호 92.96:1.
발표가 상위 몇 곳뿐이라 나머지는 null 로 둡니다. 추정값을 만들지 않습니다.
새 발표가 나오면 이 파일만 고치면 됩니다.
crawl_details.py, fees.py)휴양림마다 숲나들e 안에 있는 작은 홈페이지의 자연휴양림안내(사진 슬라이드)와 이용요금 화면을 읽습니다. 로그인 없이 열리고 robots.txt 에서 허용된 경로입니다.
요금 표는 국립이면 형식이 하나(비수기 주중 / 성수기 및 주말)지만, 공립·사립은
휴양림이 편집기로 직접 쓴 표라 칸 구성이 제각각입니다. 그래서 수집 단계에서는 표를
칸 그대로 펴서 저장만 하고, fees.py 가 머리줄 낱말로 각 칸이 비수기·성수기 ×
주중·주말 중 어디인지 읽어 냅니다. 입장료·주차료·침구·바비큐처럼 숙박이 아닌 줄은
걸러 냅니다. 자연휴양림 172곳 중 156곳에 요금이 붙고, 나머지는 요금표가 없거나
입장료 표만 있는 곳입니다. 틀린 값을 보여 주느니 비워 둡니다.
목록에는 최소~최대(forests.json), 상세에는 객실별 요금표와 사진 여러 장
(sheet.json)을 보여 줍니다. sheet.json 은 상세를 처음 열 때만 받습니다.
요금표에는 원본 요금표 링크를 함께 걸어 실제 결제 금액은 거기서 확인하게 합니다.
crawl_popularity.py)숲나들e 이용자가 휴양림을 관심 등록(찜) 한 수입니다. 예약 사이트 안에서 찍힌 값이라 예약 의도에 가깝습니다. API 키가 필요 없습니다.
수집 방법이 조금 특이합니다. 관심수를 주는 엔드포인트는 selectInsttInfoList.do
하나뿐인데 태그 검색 전용이고, 태그가 달린 휴양림은 전체의 3분의 1뿐입니다.
그런데 이 엔드포인트는 srchTag 가 비면 무작위 8곳을 관심수와 함께 돌려줍니다.
빈 태그로 반복 호출해 모으면 자연휴양림 172곳이 전부 채워집니다.
쿠폰 수집가 문제라 뒤로 갈수록 느려집니다. 실측으로 약 177회 호출 · 94초에 186곳(자연휴양림 172/172)이 모이고, 그 뒤로는 새로 나오지 않습니다. 연속 60회 동안 새 휴양림이 안 나오면 다 모았다고 보고 멈춥니다.
끝내 안 나오는 13곳은 국립등산학교·동서트레일 같은 비휴양림 시설입니다. 애초에 관심 등록 기능이 없는 곳이라 빠져도 맞습니다.
관심수는 915,636으로 자릿수 차이가 커서 5 별점**으로 환산합니다. 목록에는 별점을, 상세에는 원본 인원을 그대로 적습니다 —
별점만 남기면 근거가 사라집니다.log10(관심 + 1)을 전체 범위로 정규화해
**0
처음에는 네이버 검색 API로 블로그·카페 게시물 수를 쓰려 했습니다. 그런데 네이버가 2026년에 검색 API를 네이버 클라우드 플랫폼의 NAVER API HUB 로 이관하면서
developers.naver.com에서 신규 등록이 막혔고(기존 키는 2027-06-30까지), 새로 받으려면 클라우드 계정과 결제수단 등록이 필요했습니다. 관심 등록 수가 키도 필요 없고 예약 의도에 더 가까운 신호라 이쪽으로 갔습니다. 네이버 방식 코드는 git 이력에 남아 있습니다.
앱키가 app.js 의 KAKAO_KEY 에 들어 있습니다.
카카오 JavaScript 키는 브라우저 소스에 노출될 수밖에 없는 값이고, 등록된 도메인에서만
동작하므로 다른 사이트로 옮겨 써도 소용이 없습니다.
키를 새로 발급하거나 다른 도메인에 올릴 때는 카카오 개발자센터에서:
https://forest-trip-map.vercel.app).app.js 의 KAKAO_KEY 에 넣습니다.지도 탭에서 키를 직접 넣으면 localStorage 에 저장되어 기본 키를 덮습니다.
?key=발급받은키 로 주소에 붙여 열어도 됩니다.
목록·검색·상세는 지도와 무관하게 동작합니다. 등록되지 않은 도메인(로컬 등)에서는 SDK가 401을 반환하고 키 입력 안내가 뜹니다.
.github/workflows/refresh.yml 이 매주 월요일 06:00 KST 에 돕니다.
필요한 비밀값이 없어 설정할 것이 없습니다.
사진·이용요금(crawl_details.py)은 1년에 한두 번 바뀌는 값이라 **매달 첫째 월요일(1~7일)**과
수동 실행 때만 받습니다. 받지 않는 주에는 직전 sheet.json 과 요금을 그대로 이어 씁니다.
diff_report.py 가 이전 자료와 대조해 종료 코드로 판정합니다.
| 코드 | 뜻 | 동작 |
|---|---|---|
| 0 | 바뀐 게 있음 | 커밋 & 푸시 |
| 10 | 바뀐 게 없음 | 커밋하지 않음 |
| 20 | 자료 급감 (이전의 90% 미만) | 워크플로 실패 |
휴양림 수는 거의 변하지 않습니다. 갑자기 줄었다면 수집이 덜 된 것이지 휴양림이 사라진 게 아니므로, 그 상태로 배포해 휴양림이 조용히 사라지는 일을 막습니다.
보고서는 커밋 메시지에 인라인으로 끼우지 않고 파일로만 주고받습니다. 휴양림 이름이 외부 사이트에서 오는 값이라 셸 명령에 직접 넣으면 주입에 취약합니다.
Vercel 에 올라가 있습니다 (https://forest-trip-map.vercel.app).
저장소가 Vercel 프로젝트에 연결되어 있어 main 에 푸시하면 자동으로 배포됩니다.
매주 갱신 워크플로의 푸시도 같은 길로 배포됩니다.
index.html, app.js, forests.json 등)은 그대로 내보냅니다.api/ 아래 파일은 서버 함수로 돕니다. 로그인·즐겨찾기가 여기 있습니다..vercelignore 로 수집 스크립트(*.py)와 원본 자료(data/), db/ 는 올리지 않습니다.예전에 쓰던 GitHub Pages(pjungjin85-sketch.github.io/forest-trip-map/)는 껐습니다.
Pages 에는 서버가 없어 로그인·즐겨찾기(/api)가 404 가 나기 때문입니다.
카카오 로그인으로 들어와 휴양림을 즐겨찾기해 두면, 어느 기기에서 로그인해도 같은 목록이 보입니다.
브라우저 ── fetch('/api/…') ──▶ Vercel 함수(api/) ──▶ Neon Postgres
└──▶ 카카오(로그인할 때만)
| 파일 | 하는 일 |
|---|---|
api/auth/login.js | state 를 쿠키에 넣고 카카오 로그인 화면으로 보냄 |
api/auth/callback.js | state 확인 → code 를 토큰으로 교환 → 회원번호·닉네임 → 사용자 저장 → 세션 쿠키 |
api/auth/logout.js | 세션을 DB 에서 지움 |
api/me.js | 로그인한 사람의 닉네임 (아니면 401) |
api/favorites.js | 내 즐겨찾기 휴양림 id 목록 |
api/favorites/[id].js | 즐겨찾기 넣기(PUT)·빼기(DELETE) |
lib/session.js | 세션 만들기·확인·삭제, 쿠키, 다른 사이트에서 온 쓰기 요청 거절 |
db/schema.sql | users · sessions · favorites 테이블 |
HttpOnly·SameSite=Lax 이고, DB 에는 토큰의 sha256 해시만 둡니다.| 이름 | 값 |
|---|---|
DATABASE_URL | Neon 연결 주소. Vercel Storage 에서 Neon 을 연결하면 자동으로 들어옵니다 |
KAKAO_REST_KEY | 카카오 앱의 REST API 키 |
KAKAO_CLIENT_SECRET | REST API 키 상세의 카카오 로그인 클라이언트 시크릿 (비즈니스 인증 것이 아님) |
카카오 개발자센터의 앱 → 플랫폼 키 → REST API 키 에 Redirect URI 두 개를 등록해 둡니다.
https://forest-trip-map.vercel.app/api/auth/callback
http://localhost:3000/api/auth/callback
npm install
vercel env pull .env.local # 환경변수를 받아 온다 (커밋되지 않음)
npm run migrate # 테이블 만들기. 여러 번 돌려도 안전
vercel dev --listen 3000 # http://localhost:3000 에서 API 까지 같이 뜬다
이 사이트는 숲나들e와 무관한 비공식 정리 페이지이며 예약 기능이 없습니다. 예약 규칙과 일정은 수집 시점 기준이고 공지로 바뀔 수 있으니, 실제 예약과 정확한 일정은 반드시 숲나들e에서 확인하세요.