풀스택 개발의 실제 경험
바이브 코딩 시리즈 보기
[FRAC, 3부] 문제 설계와 AI 협업
MVP는 정규 시즌(Cycle 1)의 기본 흐름을 구현하는 방식으로 시작했다. 백엔드는 파이썬 기반 FastAPI로, 프론트엔드는 React 기반 Next.js와 TailwindCSS를 활용했다. 사용자 인증은 Google OAuth를 적용했고, 로컬 환경에서 모든 기능이 정상적으로 작동함을 확인했다.
핵심 로직은 다섯 개 힌트의 공개 시점을 문제 공개 시각과 현재 시각을 비교하여 동적으로 처리하는 것이다. 실제로 정규 시즌의 다섯 힌트는 각각 0시간, 1시간, 3시간, 6시간, 12시간이 경과했을 때 차례로 공개된다. 테스트 과정에서는 문제의 공개 시각을 임의로 변경하면서 힌트가 순차적으로 잘 해금되는지, 정답·오답 입력에 따른 분기 처리가 적절히 이뤄지는지 확인했다. 또, 일정 개수의 문장을 해금하면 소설의 한 문단을 복원하는 기능도 간단하게 구현했다.
결국 3일 만에 정규 시즌의 전반적인 흐름과 핵심 인터랙션을 모두 갖춘 MVP를 만들 수 있었다. 그러나 실제 구현체와 MVP의 차이는 컸다. MVP는 “실제로 할 수 있다”는 자신감만을 준, 최소한의 프로토타입 수준이었다.
MVP를 바탕으로 본격적인 Cycle 1의 콘텐츠 기획에 들어갔다. ChatGPT와 우선순위에 대한 토론을 거치며 가장 중요한 것이 “서사의 임팩트와 차별성”임을 깨달았다. 이에 평소 관심 있던 우주론, 존재론적 질문을 바탕으로 단막 소설을 직접 집필했다. 기술서 저술 경험은 있었지만 문학 창작은 처음이라 ChatGPT의 도움을 받아 준문학 수준으로 발전시켰다.
FRAC은 매니악한 숫자 퍼즐 유저를 대상으로 하지만, 글로벌 서비스(영어권 사용자)까지 고려해 자연스럽게 읽히는 영문 버전도 필요했다. 그래서 모든 사용 가능한 생성형 AI를 동원해 한글과 영어로 단막 소설을 완성했고, 총 92문장과 13단락으로 구분했다. 이에 따라 Cycle 1은 하루에 한 문제씩 공개되는 루틴으로 92일이 걸리며, 각 단락 복원에 13일이 추가된다. 장기적으로는 Cycle 4까지 유사 분량으로 운영할 예정이라 12개월(정규 시즌) 정도의 서비스 계획을 세웠다.
Cycle 1의 각 문제는 다섯 개 문장(힌트)이 시간차로 공개되고, 이를 모두 만족하는 8자리 수를 예측하는 구조다. 각 힌트의 역할은 실제 문제를 구현하면서 점차 명확해졌다. Cycle 1 역시 AI 활용은 자유롭게 허용했다. 문제마다 한 가지 ‘테마’가 있고, 첫 번째 힌트는 이를 추상적으로 설명한다. 첫 힌트가 너무 구체적이면 AI가 단번에 정답을 내기도 하기에, 항상 포괄적이고 추상적으로 작성했다. 두 번째부터 네 번째 힌트는 특정 자릿수 정보나 합, 조건 등으로 정답 후보를 효과적으로 줄인다. 네 번째 힌트까지 공개되면, 남은 후보는 수십~수백 건으로 설계했다. 또한 각 힌트별로 10번의 정답 입력 기회를 제한해, 무작위 대입(브루트포스)나 어뷰징을 방지했다.
Cycle 1의 문제 기획은 지난한 여정이었다. 아이디어의 90% 이상은 내가 직접 기획했고, ChatGPT의 역할은 한정적이었다. 특히 문제 출제 패턴이나 논리 구조에서 확률 기반 언어모델의 한계를 많이 느꼈다. 다만, 테마를 정하면 첫 번째 힌트(추상적 설명)는 ChatGPT가 인간을 능가할 만큼 잘 만들어주기도 해서, 인간과 AI의 역할 분담이 극대화된 순간도 있었다.
Cycle 1에서 또 한 번 AI의 한계를 확인했다. 다양한 힌트 표현을 만들다 보니, 같은 의미를 다르게 서술하는 일이 자주 발생했다. 예를 들어 “정답의 X번째 자리의 수는 Y입니다.”와 “X번째 자리는 Y입니다.”, “X번째 자리숫자는 Y입니다.” 등으로 같은 내용을 여러 방식으로 나타냈다. ChatGPT에게 동일 의미의 힌트를 통일해달라 요청했으나, 이를 제대로 분간하지 못했다. 결국 이 과정은 데이터를 직접 확인하고 수정하는 수작업으로 해결했다. 이런 과정을 거쳐 60문제를 완성했고, 나머지 32문제는 핵심 테마만 유지한 채 마무리 지었다.
Cycle 0는 5개의 8자리 수를 보고 6번째 8자리 수를 예측하는 형식이다. Cycle 1의 힌트가 문장이라면, Cycle 0에서는 각 8자리 수 자체가 힌트가 된다. 따라서 C0의 문제 설계는 규칙의 논리적 설계가 필수적이었다. 논리 설계만 탄탄하다면 이론적으로 무한대에 가까운 문제를 생성할 수 있으므로, 이 영역은 파이썬 프로그래밍으로 자동화하여 충분한 분량의 문제 은행을 만드는 것을 목표로 했다.
서비스 보안상 Cycle 0의 구체적인 규칙은 밝히기 어렵지만, 요약하자면 사용자는 5개의 8자리 수에 적용된 규칙을 추론해 6번째 수를 맞추는 것이 핵심이다. 사실상 정답을 맞추는 것보다 규칙을 습득하는 과정이 더 중요하다. 그 이유는 이렇게 파악한 규칙 조합으로 더 어려운 문제를 설계할 수 있기 때문이다. 규칙을 인지하지 못하면 표면적 패턴이 거의 보이지 않기 때문에, 현존하는 최고 수준의 추론형 AI조차도 단번에 풀기 어렵다. 결국 사용자는 규칙을 배우고, 그 논리로 다음 문제를 해결하게 된다. 이것이 FRAC에서 설계한 학습 곡선의 본질이다.
이러한 학습 곡선을 설계한 이유는 모두 생성형 AI 때문이다. 단순하고 직관적인 규칙만 넣으면 대부분의 AI가 쉽게 풀 수 있다. 그러나 새로운 규칙이 처음 등장할 때는 사람도 AI도 쉽지 않다. AI가 더 넓은 추론 범위를 갖긴 하지만, 사용자는 AI를 활용하며 규칙을 습득하는 경험을 하게 된다.
Cycle 0의 문제은행은 총 3,998건이며, 이 중 400문제가 유형별로 자동 할당된다. Stage 1(100문제, 기본 규칙), Stage 2(2개 테마, 각 120문제씩 240문제), Stage 3(매우 어려운 테마, 각 30문제씩 60문제)로 구성된다. Stage 2는 Stage 1을 모두 풀어야 공개되고, Stage 3은 Stage 2를 60개 이상 풀어야 공개된다. 최대 4개의 서로 다른 문제를 하루에 경험할 수 있다. 또한 Stage 2와 3은 하루에 풀 수 있는 문제 수를 각각 6건, 1건으로 제한해 과도한 진도를 방지했다. 할당량을 모두 풀면 다음 기준 시각까지 대기해야 한다.
FRAC의 서사와 문제 기획을 마친 뒤, 나는 본격적으로 풀스택 개발에 뛰어들었다. 15년 가까이 각종 보고서와 문서 작업을 해온 덕분에, 글을 매끄럽게 쓰는 편은 아니더라도 최소한 읽히는 수준은 갖추었다고 자부한다. 이런 경험은 생성형 AI 활용 과정에서도 도움이 됐다. 프롬프트를 통해 명확하고 구체적으로 요구사항을 전달하면, 훨씬 정확하고 의도에 가까운 결과물이 나왔다. 어찌 보면 ‘프롬프트 엔지니어링’만으로 풀스택 개발을 한다는 건 다소 무모한 시도였지만, 병렬 컴퓨팅과 데이터 분석 프로그래밍을 오래 해온 경력과, 10노드급 리눅스 클러스터 관리자 경험, 그리고 MVP 성공 경험이 자신감을 주었다.
풀스택 개발의 첫걸음은 백엔드와 데이터베이스 구현이었다. 내 개발 과정은 아래와 같다.
1. FRAC 서비스의 전체 흐름을 구체적으로 문서화
2. ChatGPT와 흐름 내 모순, 부족한 점 토론
3. ChatGPT와 최종 서비스 흐름 확정
4. ChatGPT에 개발 요구사항 상세 명세 요청
5. 상세 명세로부터 하나씩 구현
6. 각 모듈별로 변경점 및 개발 이력 요약
이 과정에서 1~3번, 즉 “기획·정리·토론”이 가장 중요했다. ChatGPT는 요구사항이 명확할수록 오류 없는 코드를 거의 100% 생성했다. 이해가 안되는 부분은 원리를 물어 이해했고, 이 경험을 통해 ChatGPT가 단순한 코드 생성 AI가 아니라 실무 개발을 보조하는 최고급 개발자급 동료라는 인상을 강하게 받았다. 오히려 순수한 프로그래밍 측면에서는 이미 ChatGPT가 ‘완성 단계’에 가까울 정도로 뛰어난 결과를 내놨다.
백엔드와 DB는 ChatGPT가 추천한 대로 Python의 FastAPI와 SQLite로 시작했다. 서비스 흐름을 문서화하는 데 많은 시간을 썼고, 가능한 모든 시나리오를 미리 설계하려 했다. MVP 개발에서 프론트엔드 설계에 따라 백엔드/DB가 크게 변할 수 있다는 점을 이미 경험했기 때문이다. 백엔드/DB는 3일 만에 완성됐고, 나름 테스트도 철저히 했다.
이후 프론트엔드로 넘어가 React 기반 Next.js와 Tailwind CSS로 개발을 시작했다. 22일간 프론트엔드를 개발하며, 처음 설계한 백엔드와 DB의 허술함을 직접 체감할 수 있었다. 인트로 페이지부터 사용자의 눈길을 끌어야 한다고 판단해, 우주의 수축과 팽창을 픽셀 입자와 함께 표현하는 등 디지털 감성을 살리는 연출을 기획했다. 타자기 스타일의 카피라이트와 연출도 ChatGPT와 함께 구현했다.
Cycle 0, Cycle 1을 구현하며 어려움이 많았다. ChatGPT도 “사용자 시나리오 자체가 복잡한 데다, 웹에 노출되는 거의 모든 데이터를 백엔드에서 받아온다는 점이 구현 난도를 높인다”고 했다. 실제로 C0/C1 프론트엔드 코드만 수천 줄(각각 2,000~2,500줄)에 달했다. 초기에는 백엔드/DB가 완벽할 거라 착각한 채 엔드포인트를 남발하다 보니, 프론트 코드가 지나치게 복잡해졌고, 결국 하나씩 테스트하며 구조를 재정비해야 했다.
나중엔 백엔드와 DB가 ‘완성’됐다는 생각을 버리고, 필요할 때마다 엔드포인트와 스키마를 유연하게 고치며 진행했다. 특히 프론트엔드 중반 이후에는, 렌더링에 꼭 필요한 데이터만 미리 설계하고 필요한 엔드포인트만 추가하는 방식으로 코드 가독성과 유지보수성을 끌어올렸다.
프론트엔드에서 가장 큰 난관은 사용자 인증이었다. 1인 비개발자가 직접 회원가입/관리 기능을 구현하면 개인정보보호 이슈와 관리 부담이 너무 커, 구글 OAuth만 도입했다. 초기에는 간단한 템플릿 코드로 정상 로그인까진 가능했으나, 약 1시간마다 자동 로그아웃되는 현상이 나타났다. ChatGPT와 토론 후, ‘갱신 토큰(refresh token)’을 구현해야 함을 알게 됐다. 하지만 OAuth 자체가 생소해 프롬프트의 구체성이 떨어졌고, 이로 인해 코드의 일관성도 깨졌다.
그래서 구글의 Gemini AI를 활용해 OAuth의 갱신 토큰 구현에 도전했다. Gemini는 공식 문서 수준으로 상세히 설명해주었고, Next.js의 백엔드에 인증 서버+SQLite를 추가하는 방식을 제안해 결국 성공했다. 이 과정에서, Next.js도 백엔드 기능을 갖고 있다는 사실을 처음 알았고, 데이터베이스를 두 개(게임용, 인증용) 운영해야 하는 복잡함에 고생했다.
ChatGPT와의 개발에서 가장 어려웠던 점은, 코드가 수천 줄에 달하다 보니 문맥이 꼬이고 정보가 잊히는 상황이 자주 발생했다는 것이다. 브라우저 메모리가 1.5GB를 넘으면 응답이 늦어지기도 했다. 그래서 항상 작업 내역을 문서화하고, 세션을 쪼개 이어나갔다. 이 ‘집요한 맥락 유지’가 FRAC을 완주할 수 있게 해준 핵심 요인이라 본다. 결국 중요한 것은 완주 의지와 집념, 그리고 FRAC에 대한 애착이었다. 구체적인 기억에 남는 사례는 두 가지다.
첫째, ‘시도 횟수(attempts)’ 시각화다. 대부분의 정보를 백엔드에서 받아오다 보니, 시도 횟수 역시 서버에서 받아 갱신하는 방식이었다. 하지만 이 방식은 새로고침이 필요해 사용자 경험이 나빠졌다. 그래서 프론트엔드에서 ‘local attempts’ 변수를 도입해 버튼 클릭 시 실시간 반영, 새로고침/이동 시 서버 정보로 동기화하는 방식으로 개선했다.
둘째, Cycle 1의 힌트 해금 시간 표시다. 남은 시간 카운트다운은 브라우저가 활성화된 경우에만 정상 동작한다. 탭을 벗어나면 시간이 멈추는 현상이 있어, 이를 완벽히 해결하기보다는 ‘브라우저가 비활성화 상태에선 시간 감소가 멈춘다’는 사실을 사용자에게 고지하고, 필요하면 직접 새로고침하도록 안내했다.
이런 우여곡절 끝에 25일 만에 로컬 환경에서 완벽하게 작동하는 FRAC을 구현했다. 그러나 실제 배포는 완전히 새로운 난이도의 도전이었다. 1인 비개발자가 끝까지 요구사항을 명확히 문서화하고, 테스트하고, 결국 AI와 협업해 풀스택을 완주한 이 경험이 정말 의미 있었음을 마지막으로 강조하고 싶다.
FRAC 게임은 fracgame.com 에서 즐겨볼 수 있습니다.
영문 논문도 여기서 확인할 수 있습니다.