본문으로 건너뛰기
강홍재/ James
← Work
Experiment2026 · Solo Builder· Started(First Commit date)

Speak Five

직무와 상황에 맞는 영어 문장 5개를 듣고, 따라 말하고, 영어를 가린 채 회상하는 학습 앱의 초기 골격 - 18시간, 커밋 8개로 선택 화면·시간대 규칙·테스트 게이트까지만 세웠고, 문장은 아직 0개다. 제품 이름도 가칭이다.

  • React
  • TypeScript
  • Vite
  • Capacitor
  • Playwright
  • Temporal
  • QA
스크린샷
  • 오늘의 학습 - 직무·상황을 고르면 그 조합의 문장을 보여주는 자리. 검수된 문장이 0개라 빈 상태를 그대로 보여준다

  • 직무 선택 - 5직군으로 묶은 16직무 드롭다운. ID는 도메인 타입 한 곳에서만 정의한다

  • 상황 선택 - 직무마다 5개, 전체 80상황. 날짜는 KST 달력 날짜로, 저장은 UTC Z로 가른다

Setup

Problem

BRD의 출발 사례는 하나다. 미국 회사에 입사한 QA 엔지니어가 업무 영어와 졸업 말하기 점수를 같이 고민하는 상황. 업무 내용을 설명할 지식은 있는데, 영어로 진행 상황·문제·기대 결과·협업 요청을 꺼내는 데 어려움을 겪는 사람이다. BRD는 이 한 사례를 전체 시장의 수요로 일반화하지 않는다고 적어뒀고, 초기 고객 후보로 "한국어를 사용하며 해외 동료와 협업하는 직장인"을 제안만 해뒀다. PRD의 한 줄은 "업무에서 꺼내 쓸 표현을 짧게 반복하는 제품"이다. 첫 사용자 스토리도 "QA 엔지니어로서 오늘 버그를 설명할 표현을 골라 연습하고 싶다"다. 사업 상태는 BRD 머리에 그대로 적혀 있다. 고객 실험과 매출이 아직 검증되지 않은 기획.

Context

2026년 10월 3일 22:58에 첫 커밋, 다음 날 16:56에 여덟 번째 커밋. 18시간 안에 들어간 작업이다. 문서는 10개(README, SSOT, BRD, PRD, ARCHITECTURE, DATA_API_SPEC, CONTENT_SPEC, QA_TEST_PLAN, ROADMAP, PROJECT_STRUCTURE)고 버전은 0.1.7 Draft다. 제품 이름 Speak Five는 가칭이고 배포용 앱 식별자는 미확정이다. 로드맵은 R0 기획·콘텐츠 준비, R1 공통 웹·웹뷰 체험판, R1 고객 실험(20명·14일), R2 계정·유료 운영, R3 확장 순이다. 월 4,900원이라는 가격도 문서에는 "검증 전 제안"으로만 적혀 있다. 지금 위치는 R1 골격이고, README는 "이 골격은 R1 완료나 출시 준비를 뜻하지 않는다"고 먼저 말한다.

Users

가설상 사용자는 한국어로 일하면서 해외 동료와 협업하는 직장인이고, 그중 첫 사례가 QA 엔지니어다. 실제 사용자는 0이다. 검수된 문장이 하나도 없어서 지금 화면은 직군·직무·상황 선택과 빈 콘텐츠 상태만 보여주고, 선택값은 메모리에만 있어 새로고침하면 사라진다.

Hypothesis

직무·상황으로 고른 문장 5개를 듣고, 따라 말하고, 영어를 가린 채 회상하는 짧은 반복이 업무 영어를 꺼내 쓰는 데 실제로 도움이 된다면, 검수된 문장 200개(4직군 × 50)만으로 20명·14일 실험에서 신호가 나온다. 실험 전까지는 어느 것도 확인된 게 없다.

Build

What I did
  • React·TypeScript·Vite 실행 환경에 린트·단위 테스트·E2E·GitHub Actions CI를 먼저 붙였다. npm run verify 하나가 린트(경고 0)·단위 테스트·빌드·E2E를 순서대로 돈다
  • 5직군·16직무·80상황 선택 화면 - 직군으로 묶은 직무 드롭다운과 공통 커스텀 Select. ID는 domain/profile/types.ts 한 곳에서만 정의하고 콘텐츠 트리는 그 ID를 참조한다
  • 시간대 규칙을 먼저 코드로 못박았다 - 저장·API·로그는 UTC Z 표기, 표시·학습일·복습일은 Asia/Seoul, day_key는 timestamp가 아니라 학습 시간대의 달력 날짜. calendar.ts는 Z가 아닌 timestamp를 RangeError로 거부한다
  • 세션 선택·복습 일정·보고서 집계를 어댑터를 모르는 순수 도메인 모듈로 떼어냈다. 의존은 한 방향이고 domain은 adapters를 참조하지 않는다
  • 콘텐츠 스키마 검증·도메인 타입·저장/오디오/생명주기/콘텐츠 어댑터 계약, Capacitor 설정과 플랫폼 생성 절차까지 - 단, 네이티브 빌드와 실기기 테스트는 하지 않았다
  • 마지막 커밋에서 Temporal 경로를 둘로 나눠 테스트하게 했다 - CI의 Node 26은 Temporal을 내장해서 폴리필 경로가 검증되지 않고 있었다
Product decisions
  • 미검수 문장과 가짜 완료 결과를 앱에 넣지 않는다 - 그래서 카탈로그는 bootstrap-empty-v1, 문장 0개다. 선택 화면이 빈 상태를 보여주는 건 버그가 아니라 결정이다
  • 웹으로 공통 기능을 만들고 iOS·Android는 웹뷰 앱으로 낸다. 빌드 타깃은 iOS 16.4 WebView에 맞췄고, native/에는 Capacitor 설정만 두고 빈 가짜 프로젝트는 만들지 않는다
  • R1에는 업무 Backend가 없다. backend/에는 UTC Z 계약만 적어두고 실행 서버는 두지 않는다
  • 제품 이름·앱 식별자·가격을 확정하지 않은 채로 둔다. 모르는 것을 확정한 것처럼 적지 않기 위해서다
  • 다음 단위는 기능이 아니라 콘텐츠다 - 4직군 × 1상황 × 5문장, 검수된 20문장과 오디오로 핵심 흐름을 한 번 잇는 것
QA considerations
  • 출시 코드와 테스트 코드가 같은가 - Playwright는 dev 서버가 아니라 production 빌드(preview, 4174 포트)에 붙는다. 설정 파일 주석이 이유를 적어뒀다. 출시되는 건 production 빌드이기 때문
  • CI 환경이 숨기는 경로가 있는가 - Node 26은 Temporal을 내장하니 폴리필 경로는 CI에서 한 번도 돌지 않고 있었다. vitest 프로젝트를 runtime과 polyfill 둘로 나눠 같은 단위 테스트 296개를 두 번(592개) 돌리고, E2E는 addInitScript로 Temporal을 지운 뒤 2026-10-03T15:00Z가 "10월 4일"로 그려지는지 본다
  • 날짜 경계에서 같은 입력에 같은 결과인가 - UTC Z 저장과 KST 달력 날짜를 분리하고, Z가 아닌 timestamp는 아예 받지 않는다. 자정 근처의 하루 어긋남을 함수 하나의 계약으로 막는 방식
  • 테스트 통과를 완료로 읽지 않는가 - QA_TEST_PLAN은 FR-001~010 추적표와 기기·오디오 매트릭스를 갖췄지만 모든 칸이 "미실행"이다. 문서 스스로 "구조 테스트 통과를 전체 수용 기준 통과로 취급하지 않는다", "테스트 더블을 실제 저장·음성 검증 완료로 취급하지 않는다"고 적어뒀다
  • 실기기 검증이 빠져 있다 - README가 "이 테스트는 실제 iOS·Android WebView 검증을 대체하지 않는다"고 명시한다. 음성·권한은 시뮬레이터로 대체하지 않는다는 게 계획서의 원칙이다

Outcome

Metrics
  • 커밋 8개, 2026-10-03 22:58 ~ 10-04 16:56 KST, 단일 저자
  • 문서 10개, 버전 0.1.7 Draft. 콘텐츠 트리 5직군·16직무·80상황, 검수 문장 0개
  • 단위 테스트 파일 9개(선언 179개, Temporal 2경로로 592개 실행), E2E 스펙 2개(25개 통과·desktop 전용 1개 건너뜀), 통합 테스트 0개. 수치는 마지막 커밋 메시지에 적힌 실행 결과
  • CI - push·PR마다 Node 26에서 npm run verify, 실패 시 trace·스크린샷 7일 보관
  • 사용자·학습·매출 지표 전부 측정 전. QA 계획의 릴리스 게이트 네 칸 모두 미실행·미제작·미준비
Result / Learning

결과는 없다. 18시간 만에 남긴 건 "무엇을 넣지 않을 것인가"가 코드로 고정된 골격이다. 문장이 검수되기 전에는 화면이 비어 있고, CI가 숨기던 폴리필 경로는 이제 매번 돈다. 다음 단위가 기능이 아니라 검수된 20문장이라는 것도 문서에 적혀 있다. 이 항목은 완성 기록이 아니라 시작점 기록이다. 고객 실험(20명·14일)이 돌고 나면 그 숫자를 여기 적는다.

Retrospective
  • 문서 10개 1만 자 가까이를 하루에 썼는데 검수된 문장은 0개다. 이 제품의 병목은 코드가 아니라 콘텐츠 검수라는 걸 로드맵도 알고 있다. 다음에 손대는 건 코드가 아니어야 한다.
  • Temporal 폴리필 경로가 CI에서 검증되지 않고 있었다는 건 마지막 커밋에서야 알았다. "CI 통과"가 어떤 환경의 통과인지는 처음부터 물었어야 했다.
Tech stack
  • React
  • TypeScript
  • Vite
  • Vitest
  • Playwright
  • Temporal (@js-temporal/polyfill)
  • Capacitor
  • GitHub Actions