World Vibe Web
The distributed app store for vibe-coded projects. Browse, discover, and install open-source apps from across GitHub.
翻譯準備中
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證正在載入完整 README。 以下為已儲存的摘錄。
Next.js(App Router) + Google Sheets(상품 DB) + Cloudinary(이미지) 조합입니다. 전부 무료 플랜으로 운영 가능합니다. 1단계 — 구글 시트 두 개 준비 아래 열 이름들을 1행(헤더)에 넣어주세요 — CSV는 열 이름으로 읽으므로 실제 열 순서는 상관없습니다 (option9, price9처럼 새 열을 맨 뒤에 추가해도 됩니다). 옵션은 최대 40개(option1~option40, price1~price40)까지 쓸 수 있고, 안 쓰는 옵션 칸은 비워두면 됩니다:檢視完整 README ↗
Next.js(App Router) + Google Sheets(상품 DB) + Cloudinary(이미지) 조합입니다. 전부 무료 플랜으로 운영 가능합니다.
dandan-shop/
├─ app/
│ ├─ page.jsx 메인: 날짜탭 + 상품 목록
│ ├─ layout.jsx
│ ├─ globals.css
│ ├─ product/[id]/page.jsx 상품 상세페이지
│ └─ reservations/page.jsx 예약 검색 페이지
├─ lib/
│ └─ sheet.js 구글시트 CSV 불러와서 파싱
├─ package.json
└─ .env.local.example
아래 열 이름들을 1행(헤더)에 넣어주세요 — CSV는 열 이름으로 읽으므로 실제 열 순서는 상관없습니다
(option9, price9처럼 새 열을 맨 뒤에 추가해도 됩니다). 옵션은 최대 40개(option1option40,
price1price40)까지 쓸 수 있고, 안 쓰는 옵션 칸은 비워두면 됩니다:
| id | date | category | name | status | image | description | shiping fee | option1 | price1 | option2 | price2 | ... |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| p001 | 2026-08-19 | 수산물 | 완도활전복 1kg | 주문가능 | https://res.cloudinary.com/.../abalone.jpg | 완도산 자연산 전복입니다.당일 수급 당일 발송 | 3000 | 5~6미 | 33400 | 6~7미 | 31600 | ... |
id : 상품마다 고유값 (p001, p002... 그냥 순서대로 적으면 됨)category : 수산물 / 농산물 / 축산물 / 김치 / 젓갈 / 과일 / 가공식품 / 선물세트 중 하나status : 주문가능 / 품절 / 시즌종료 중 하나description : 줄바꿈하고 싶은 곳에 <br> 넣으면 상세페이지에서 줄바꿈으로 나옵니다shiping fee : 이 상품 주문 1건당 붙는 배송비(원). 무료배송이면 0. (열 이름 철자 그대로 써야 합니다)option1~40 / price1~40 : 옵션명과 그 옵션의 가격. 옵션이 하나뿐이어도 option1/price1에만 채우면 됩니다.
목록 화면·상세페이지에는 옵션 중 최저가가 "○○원부터"로 표시되고, 주문 화면에서 고객이 옵션을 선택합니다.detail_image : (선택) 상세페이지의 상품 설명 아래에 통째로 보여줄 긴 상세 이미지 URL. 목록/상세 상단에
뜨는 대표 사진(image)과는 별개이며, 비워두면 아무것도 표시되지 않습니다.젓갈류 합배송비 안내:
category가 "젓갈"인 상품은 옵션별 낱개 배송비 대신, 장바구니에 담긴 젓갈류 전체 무게로 배송비를 한 번만 계산합니다(5kg 이하 3,000원 / 5kg 초과 5,000원 —lib/shipping.js에서 조정 가능). 이 계산이 되려면 젓갈 상품의option1~40옵션명에 "250g", "500g", "1kg"처럼 숫자+단위(g/kg)를 반드시 포함해야 합니다(예:250g,실속형 500g,선물세트 1kg모두 인식됩니다). 옵션명에 무게 표기가 없으면 무게 0으로 처리되어 합배송 계산에서 제외되니 주의하세요. 이때 젓갈 상품의shiping fee열 값은 무시되므로 비워두거나 아무 값이나 넣어도 무방합니다.
| nickname | item | status | date |
|---|---|---|---|
| 딸기맘 | 활전복 2미 | 결제완료 | 2026-08-18 |
각 시트에서:
/d/와 /edit 사이 ID, gid=는 시트 탭 URL 끝의 숫자):
https://docs.google.com/spreadsheets/d/<시트ID>/export?format=csv&gid=<gid>
이 URL을 .env.local에 넣습니다 (아래 3단계).
주의: "파일 → 공유 → 웹에 게시" 방식(
/pub?output=csv형태의 URL)은 쓰지 마세요. 로컬/curl에서는 잘 되는 것처럼 보이지만, Vercel 같은 클라우드 서버 IP에서 요청하면 구글이 종종 400 에러로 차단합니다. 위의 "링크 공유 +/export?format=csv" 방식만 서버(Vercel)에서도 안정적으로 동작합니다. 이 방식도 API 키/인증 설정은 필요 없습니다.
image 열에 붙여넣기기존에 카페24/쿠팡에 올려둔 상품 사진 URL이 있다면 그걸 그대로 써도 됩니다.
cd dandan-shop
npm install
cp .env.local.example .env.local
.env.local을 열어서 2단계에서 복사한 CSV 게시 URL 두 개를 넣습니다:
PRODUCTS_CSV_URL=https://docs.google.com/spreadsheets/d/e/.../pub?output=csv
RESERVATIONS_CSV_URL=https://docs.google.com/spreadsheets/d/e/.../pub?output=csv
npm run dev
http://localhost:3000 에서 확인.
.env.local과 똑같이 두 개 추가몇 분 뒤 단단상회.vercel.app 형태(또는 랜덤 이름)로 바로 접속 가능. 나중에 실제 도메인 연결도 Vercel 설정에서 무료로 가능합니다.
카드결제 PG 없이 계좌이체 + 수동 확인 방식입니다. 발송인(주문자)과 수취인(받는 분) 정보를 따로 받아서, 선물 발송 같은 경우도 처리할 수 있습니다.
예약기록 시트 헤더를 이렇게 만드세요 (총 20열, 순서 정확히):
아이디 | 주문일시 | 발송인 | 발송인 주소 | 발송인 전화번호 | 수취인 | 수취인 전화번호 | 수취인 주소 |
카테고리 | 구매품목 | 수량 | 구매가격 | 배송비 | 합계금액 | 결제상태 | 발주 상태 | 택배사 | 송장번호 | 비고 | 수정이력
아이디 / 주문일시 / 결제상태(입금대기 → 입금확인) / 발주 상태(입금확인중 → 배송준비중 → 배송중 → 배송완료)는
아래 Apps Script가 주문이 들어올 때마다 자동으로 채웁니다. 직접 입력할 필요 없어요.발송인은 실제 입금하실 분과 동일한 이름으로 받습니다 (입금자명 대조용).택배사 / 송장번호는 실제 발송할 때 직접 입력하는 칸입니다 (자동 입력 안 됨).
입력하면 고객이 "내 주문 확인"에서 바로 조회할 수 있습니다.비고는 체크아웃 화면에서 고객이 남긴 요청사항이 자동으로 채워집니다(선택 입력이라 비어있을 수 있음).수정이력은 고객이 "내 주문 확인"에서 주문을 직접 수정할 때마다 자동으로 한 줄씩 쌓입니다
(자세한 내용은 아래 7번 참고). 직접 입력할 필요 없어요.예약기록 시트에서 확장 프로그램 → Apps Script 열기 → 기본 코드 지우고
scripts/AppsScript.gs 내용을 그대로 붙여넣기 → 저장
오른쪽 위 배포 → 새 배포 → 유형: 웹 앱 선택
.env.local에 추가:
ORDER_WEBHOOK_URL=(3번에서 복사한 URL)
NEXT_PUBLIC_BANK_NAME=00은행
NEXT_PUBLIC_BANK_ACCOUNT=000-0000-0000
NEXT_PUBLIC_BANK_HOLDER=단단상회
배송비는 여기서 설정하지 않습니다 — 1단계 상품마스터 시트의 shiping fee 열에서 상품마다 따로 설정하세요.
고객은 상품 상세페이지에서 "장바구니에 담기"(여러 상품 동시 주문 가능) 또는 "바로 주문하기"(담고 바로 결제)로
담은 뒤, /checkout에서 아래 정보를 한 번만 입력합니다:
접수되면 예약기록 시트에 담았던 상품 개수만큼 줄이 자동으로 추가됩니다 — 모두 같은
주문번호(ORD-yyMMdd-발송인전화번호뒷4자리-일련번호, 예: ORD-260907-8287-0001. 일련번호는
상품 여러 개면 그만큼 다음 번호가 듬성듬성 늘어남)를 공유하고,
발송인/수취인/주문일시/결제상태도 동일하게 채워지되, 카테고리·구매품목·수량·구매가격·배송비·합계금액은
그 상품 한 줄 기준 값입니다. 결제상태 "입금대기", 발주 상태 **"입금확인중"**으로 시작합니다.
진행 상황에 따라 시트에서 직접 값을 바꿔주세요:
입금확인중 → 배송준비중 → 배송중 → 배송완료취소중 → 취소완료Apps Script를 수정해서 배포할 때마다(코드 변경 시) "새 배포"가 아니라 기존 배포 옆의 수정(연필 아이콘) → 새 버전으로 업데이트해야 URL이 안 바뀝니다.
예약기록 시트가 "웹에 게시(CSV)"로 공개되어 있으므로(2단계), 개인정보 보호를 위해 검색 결과 화면에는 항상 최소한의 정보(주문번호·품목·상태)만 보여주도록 되어 있습니다. 시트 원본에는 주소·전화번호가 그대로 남아있으니, 게시 URL이 외부에 유출되지 않도록 주의하세요.
주문(체크아웃)할 때만 로그인이 필요합니다 — 상품 목록/상세는 누구나 볼 수 있어요. 전화번호 + 비밀번호로 간단히 가입하고, 로그인은 90일간 유지됩니다(쿠키 기반).
새 구글 시트를 하나 더 만들고 "회원"이라고 이름 붙이세요. 헤더는 이렇게(총 7열, 순서 정확히):
전화번호 | 비밀번호해시 | 가입일시 | 연간주문횟수 | 연간주문금액 | 당월주문량 | 당월주문금액
비밀번호해시와 가입일시는 자동으로 채워집니다. 절대 평문 비밀번호가 여기 들어가지 않습니다
(Next.js 서버가 회원가입 시 미리 해시(scrypt)해서 보냅니다).연간주문횟수/연간주문금액/당월주문량/당월주문금액(D~G열)은 가입 시 0으로 시작하고,
이후 그 전화번호로 주문이 들어올 때마다 예약기록 시트 쪽 Apps Script가 자동으로 다시 계산해서
채워줍니다. 직접 입력/수정할 필요 없어요.상품마스터/예약기록 시트 때와 똑같이 진행하세요:
/export?format=csv&gid=... URL)으로 MEMBERS_CSV_URL에확장 프로그램 → Apps Script 열고 scripts/MemberSignupAppsScript.gs 내용 붙여넣기 → 저장배포 → 새 배포 → 웹 앱, 액세스 권한 모든 사용자 → 나온 URL을 MEMBER_SIGNUP_WEBHOOK_URL에회원 통계 자동 갱신 연결 — 예약기록 시트의 Apps Script가 이 "회원" 시트를 찾을 수 있게 딱 한 번만 연결해주세요:
/d/와 /edit 사이의 긴 문자열(시트 ID)을 복사MEMBER_SHEET_ID, 값에 방금 복사한 시트 ID 입력 → 저장.env.local에 추가:
MEMBERS_CSV_URL=(1번 CSV 게시 URL)
MEMBER_SIGNUP_WEBHOOK_URL=(2번 웹 앱 URL)
SESSION_SECRET=(아무 랜덤한 긴 문자열 — 32자 이상 추천, 노출되면 안 됨)
SESSION_SECRET은 터미널에서 이렇게 만들 수 있어요:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
동작 방식:
/signup에서 전화번호+비밀번호(4자 이상)로 가입 → 자동 로그인/login에서 로그인 → 90일간 로그인 유지 (쿠키 만료 전까진 재로그인 불필요)/checkout 접근 시 자동으로 /login으로 이동, 로그인 후 다시 체크아웃으로 복귀Vercel에 배포할 때 Environment Variables에
MEMBERS_CSV_URL,MEMBER_SIGNUP_WEBHOOK_URL,SESSION_SECRET세 개도 반드시 추가해주세요.SESSION_SECRET은 로컬과 다른 값을 새로 만들어도 됩니다.
방문수·상품별 클릭수·구매수·판매율·전환율·시간대별 분포를 일/주/월/분기/반기/연 단위로
보여주는 관리자 페이지입니다. Vercel Storage에서 Neon Postgres를 연결하면 자동 생성되는
DATABASE_URL을 사용합니다.
vercel env pull)에서도 값을 받아올 수 있습니다.node scripts/init-db.mjsADMIN_PASSWORD 환경변수 설정 후 /admin/login에서 로그인app/PageviewTracker.jsx → /api/track/pageview),
구매 이벤트는 주문 접수 성공 시 app/api/order/route.js에서 함께 기록됩니다.메인 페이지는 60초마다 자동으로 최신 시트 내용을 다시 불러옵니다 (revalidate: 60).
시트에 상품 추가 → 최대 1분 안에 사이트에 반영됩니다. 더 즉시 반영이 필요하면
Google Apps Script로 Vercel의 On-Demand Revalidation을 호출하는 방법도 있는데,
필요하시면 다음 단계로 추가해드릴게요.
반품중 → 반품완료교환중 → 교환완료고객은 "내 주문 확인"에서 본인 전화번호로 로그인한 상태에서만 자기 주문을 볼 수 있고, 검색창으로 그 안에서 주문번호(아이디) 또는 발송인/수취인 성함을 좁혀서 찾을 수 있습니다 (주문번호가 같으면 상품별 줄이 전부 한 주문으로 묶여서 보입니다).
주문 상세보기/수정 — "내 주문 확인"에서 주문을 클릭하면 주문서 그대로 상세 내용이 펼쳐집니다.
결제상태가 **"입금대기"**일 때만 "수정하기" 버튼이 뜨고, 고객이 직접 고칠 수 있는 항목은
수취인 성함/전화번호/주소, 비고뿐입니다 (발송인 정보·상품·수량·가격은 수정 불가 — 금액이 바뀌면
안 되니까요). 수정하면 예약기록 시트의 수정이력 열에 "언제 무엇을 바꿨는지"가 자동으로 한 줄씩
쌓이고, 고객 화면의 상세보기에도 그 이력이 그대로 보입니다. 입금확인 이후에는 수정 버튼이
사라지므로, 그 이후 변경은 고객에게 직접 연락받아 시트에서 수동으로 처리해주세요.