[3주차] 실제 운영 전, 쿠폰 인증을 샌드박스로 검증하기

소개

이번 주에는 영어 VOCA 웹앱의 홈 화면을 다듬고, 실제 운영 쿠폰을 바로 적용하기 전에 쿠폰 인증 전체 흐름을 샌드박스 환경에서 먼저 검증했습니다.

현재 서비스 정책은 DAY 1~9는 무료, DAY 10~60은 쿠폰 등록 후 이용하는 방식입니다.

그런데 실제 DAY 10 콘텐츠도 아직 만들지 않은 상태에서 운영 쿠폰과 운영 DB부터 연결하는 것은 위험하다고 판단했습니다.

그래서 실제 DAY 10 대신 테스트 단어 3개만 들어간 DAY 10 TEST 를 만들고, Cloudflare Preview 환경에서 쿠폰 등록과 이용환경 제한이 제대로 동작하는지 확인했습니다.

홈 화면도 피드백(8.8. 오프 모각)을 반영해 함께 정리했습니다.

  • 영어 VOCA 타이틀 확대

  • '오늘의 학습 시간' 영역 축소

  • DAY 선택하기를 홈의 핵심 CTA로 강조

  • '연속듣기, 복습하기, 테스트하기' 바로가기 삭제

  • '학습 회독' 영역 삭제

기능을 더 넣기보다, 홈에서는 DAY 선택에 더 집중하도록 단순화했습니다.

진행 방법

이번 작업은 ChatGPT + Codex + GitHub + Cloudflare Pages + Cloudflare D1을 사용했습니다.

실제 코드 수정은 Codex에서 진행하고, ChatGPT에서는 현재 저장소와 브랜치 상태를 확인하고 작업 단계를 나눠 지시했습니다.

1. 현재 개발 상태 확인

쿠폰 기능을 바로 만들지 않고, 먼저 Codex가 실제로 어느 저장소와 브랜치에서 작업 중인지 확인했습니다.

확인 결과:

  • 로컬 저장소 & 기준 브랜치 & 쿠폰 실습 브랜치

  • 프런트엔드: Vite + React + TypeScript

  • 실제 콘텐츠: DAY 1만 구현

  • 실제 쿠폰 백엔드: 아직 없음

  • Supabase: 과거 설계 초안만 존재

  • Firebase: 현재 저장소에는 없음

기존 홈 디자인 수정이 미커밋 상태로 남아 있어, 먼저 체크포인트 커밋으로 분리한 뒤 쿠폰 샌드박스 브랜치를 만들었습니다.

2. 실제 DAY 10 대신 DAY 10 TEST 제작

실제 교재 데이터는 전혀 사용하지 않고 테스트 단어만 사용했고, 화면에는 '쿠폰 테스트용 · 운영 콘텐츠 아님' 문구를 표시했습니다.

그리고 Production에서는 보이지 않고 Preview에서만 노출되도록 조건을 걸었습니다.

3. 운영 DB 대신 Cloudflare D1 샌드박스 구성

별도의 D1 데이터베이스를 만들었습니다.

voca-coupon-sandbox

쿠폰 원문은 DB에 저장하지 않고 SHA-256 hash로 변환해 저장했습니다.

이용환경도 기기 지문을 만드는 방식이 아니라, 브라우저에서 임의의 environment token을 생성해 localStorage에 저장하도록 했습니다.

쿠폰 상태는 다음과 같이 구분했습니다.

VALID_NEW_DEVICE
VALID_EXISTING_DEVICE
INVALID_FORMAT
INVALID_COUPON
DISABLED_COUPON
DEVICE_LIMIT_REACHED
SERVER_ERROR

4. QA 전용 쿠폰 2개 분리

실제로 생성해 둔 쿠폰 중 2개를 QA 전용으로 분리했습니다.

  • QA-A: 정상 쿠폰 테스트용

  • QA-B: 비활성 쿠폰 테스트용

쿠폰 원문은 코드나 GitHub에 넣지 않고, PowerShell에서 직접 입력해 hash만 생성한 뒤 D1에 등록했습니다.

5. Cloudflare Preview 환경 분리

Preview 환경에만 다음 설정을 추가했습니다.

COUPON_SANDBOX_API = 1
VITE_COUPON_SANDBOX = 1

D1 binding도 Preview에만 연결했습니다.

COUPON_SANDBOX_DB → voca-coupon-sandbox

Production에는 위 설정을 넣지 않았습니다.

6. 가장 큰 시행착오: DAY 10 TEST가 안 보임

처음 Preview를 열었을 때 DAY 10 TEST가 아예 보이지 않았습니다.

처음에는 UI 구현이 누락된 줄 알았는데, 코드는 이미 있었습니다.

문제는 Cloudflare 프로젝트가 GitHub 자동 배포가 아니라 Direct Upload 방식이었다는 점이었습니다.

Cloudflare Dashboard의 Preview 환경변수를 넣어도, 이미 로컬에서 만들어진 Vite 정적 번들에는 자동 반영되지 않았습니다.

그래서 Preview용 빌드 때 직접 환경변수를 넣고 다시 빌드했습니다.

$env:VITE_COUPON_SANDBOX="1"
npm.cmd run build:cloudflare
Remove-Item Env:VITE_COUPON_SANDBOX

그 다음에는 Preview로 수동 배포했습니다.

이후 DAY 10 TEST가 정상적으로 나타났습니다.

7. 실제 검증

최종적으로 다음 흐름을 직접 테스트했습니다.

  • DAY 1은 쿠폰 없이 이용 가능

  • DAY 10 TEST는 쿠폰 없이 잠김

  • QA-A 정상 등록

  • QA-B 비활성 쿠폰 차단

  • 같은 쿠폰으로 두 번째 이용환경 등록 가능

  • 세 번째 새로운 이용환경은 차단

  • 잘못된 형식은 INVALID_FORMAT

  • 형식은 맞지만 없는 쿠폰은 INVALID_COUPON

특히 PC Chrome과 Edge를 각각 등록한 뒤 모바일에서 접속하니 모바일이 세 번째 환경으로 인식되어 차단됐습니다.

이 과정에서 현재 구조가 정확히는 “기기 2대”보다는 “브라우저 이용환경 최대 2개”​라는 것도 실제로 확인할 수 있었습니다.

검수 기준 URL은 branch alias로 고정했습니다.

아이폰 한국어 앱 스크린샷

전화기 화면의 voca 앱

사용한 핵심 프롬프트

이번에는 한 번에 전체 기능을 만들라고 하지 않고, 현황 확인 → 브랜치 분리 → 로컬 구현 → D1 → Preview → 실제 검증으로 나눠 진행했습니다.

실제 운영용 쿠폰을 바로 적용하지 않고,
쿠폰 기능 전체 흐름을 시험하는 샌드박스를 먼저 만든다.

실제 DAY 10 콘텐츠를 만들지 않는다.
대신 쿠폰 테스트용 DAY 10 껍데기만 만든다.

테스트용 DAY 10:
- 테스트 단어 2~3개만 사용
- 실제 교재 데이터 사용 금지
- ‘쿠폰 테스트용 · 운영 콘텐츠 아님’ 표시
- 운영 환경에서는 노출되지 않음
- Cloudflare Preview에서만 확인

검증할 흐름:
1. DAY 1은 쿠폰 없이 열린다.
2. DAY 10 TEST는 쿠폰 없이 잠긴다.
3. 정상 시험용 쿠폰을 등록하면 DAY 10 TEST가 열린다.
4. 같은 이용환경에서 다시 접속하면 추가 등록 없이 열린다.
5. 두 번째 이용환경까지 등록된다.
6. 세 번째 이용환경은 차단된다.
7. 새로고침 후에도 권한이 유지된다.
8. DAY 1 기존 기능에 영향을 주지 않는다.

보안 원칙:
- 실제 쿠폰 원본을 공개 코드에 넣지 않는다.
- 운영 DB를 사용하지 않는다.
- 쿠폰번호를 URL, 로그, 콘솔에 남기지 않는다.
- main 브랜치를 직접 수정하지 않는다.
- 정식 도메인과 Production은 승인 없이 변경하지 않는다.

결과와 배운 점

배운 점과 나만의 꿀팁을 알려주세요.

이번 주에 가장 크게 배운 것은 운영 기능을 바로 붙이지 않고, 위험한 기능만 떼어내 먼저 실험하는 방식이 훨씬 안전하다​는 점입니다.

처음에는 DAY 9까지 모두 만든 뒤 쿠폰을 개발해야 하나 고민했지만, 실제 DAY 10 콘텐츠가 없어도 DAY 10 TEST 껍데기만으로 쿠폰 흐름 전체를 먼저 검증할 수 있었습니다.

이렇게 하니 실제 교재 데이터나 운영 DB를 건드리지 않고도 다음을 확인할 수 있었습니다.

  • 쿠폰 등록

  • 정상/비활성/미등록 쿠폰 구분

  • 이용환경 제한

  • 동일 환경 복구

  • Preview / Production 분리

  • 기존 무료 콘텐츠 영향 여부

나만의 꿀팁

  • 이번 작업에서 가장 효과적이었던 방법은 AI에게 처음부터 수정시키지 않고 계속 요청하고 확인하며 진행했습니다.

  • Codex는 실제 코드 수정과 명령 실행을 맡기고, ChatGPT에서는 결과를 검토하면서 다음 작업 범위를 좁혀주는 역할을 맡기니 작업 흐름도 안정적이었습니다.

과정 중에 어떤 시행착오를 겪었나요?

Cloudflare 환경변수와 Vite 빌드 시점 환경변수의 차이였습니다.

이번 작업을 통해 Direct Upload 방식에서는 "프런트의 VITE_... 변수는 빌드할 때 넣고, 서버용 변수와 D1 binding은 Cloudflare Preview에서 관리한다"는 점을 이해하게 됐습니다.

도움이 필요한 부분이 있나요?

  • 쿠폰 인증 구조 자체는 운영 개발로 넘어갈 수 있는 수준까지 검증되었다고 하는데, 실제는 어디에서, 얼마나 다를지?

  • 영어 VOCA APP 쿠폰 백엔드 기준은 Cloudflare Pages Functions + D1을 우선하고, Supabase는 나중에 회원계정, 서버 학습기록, 기기 간 동기화, 복잡한 관리자 데이터·통계 같은 기능이 실제로 필요해질 때 다시 검토할까 하는데 방향이 맞는지?

앞으로의 계획이 있다면 들려주세요.

이번 샌드박스 검증이 성공했기 때문에 다음에는 쿠폰 시스템을 운영용으로 안정화할 예정입니다.

이후에는 다음과 같은 순서로 이어갈 계획입니다.

  • 운영용 쿠폰 DB/API 정리

  • 실제 쿠폰 import

  • DAY 2~9 무료 콘텐츠 확장

  • 학습기록 저장방식 정리

  • DAY 10~60 실제 콘텐츠 보호

  • 전체 60DAY 및 음성 확장

이번 주에는 기능을 많이 만든 것보다, “운영 전에 위험한 기능을 어떻게 안전하게 실험할 것인가”​를 제대로 경험했다는 점이 가장 큰 수확이었습니다.

도움 받은 글

지난 토요일(8월 8일) 오프라인 모각 때 스터디장님과 많은 스터디원님들의 조언이 큰 도움이 되었습니다.

밀어주고 끌어주는

온·오프라인 AI 스터디

AI로 어디까지 할 수 있는지
직접 확인하실 분만 신청하세요.