지난 글은 4월 17일 수료식으로부터 8주 뒤, 애플 파운데이션 프로그램의 멘토와 러너들에게 첫 앱 프로젝트를 소개하려고 쓴 노트였습니다. 이번 글은 그 이후의 후기입니다. 수료 후 다섯 달 동안 아카데미에서 익힌 CBL(Challenge Based Learning)을 제 환경에 맞게 적용하며 개발을 이어 왔고, 지난 글이 그중 첫 두 달을 다뤘다면 이 글은 그 뒤 석 달의 기록입니다.

다이어그램은 한눈에 볼 수 있도록 Claude와 함께 분석하고 만들었습니다. 참고: CBL을 함께 만든 Mark Nichols의 "Your Brain On CBL", challengebasedlearning.org (2023)

후기부터 말하자면 힘들지만 긍정적인 경험이었습니다. 막힐 때마다 포기하지 않을 수 있었던 건, 어떤 질문을 던져야 하는지, 그리고 어떻게 다시 순환해야 하는지 알고 있었기 때문입니다.

다만 이 석 달은 아이를 키우는 주부의 시간 안에서 흘러간 석 달이었습니다. 동료 없이 CBL의 탐구 순환(Exploratory Cycle)을 돈다는 것이 현실에서는 어떤 모습인지, 방학이라 학교에 가지 않는 아이들에게 시간을 더 쏟을 수밖에 없을 때 어떻게 해야 하는지는 제가 스스로 풀어야 할 숙제였습니다. 아이들 역시 달라진 엄마의 시간에 저마다 적응해야 했던 시기였습니다.

이 글에서는 지난 글에서 무엇을 잘못 알고 있었는지, 그리고 그 뒤로 무엇을 만들었는지 적어 보려 합니다.

시작하며

Learning in public, 그리고 달라진 풍경

개발 문화에는 "learning in public", 즉 배우는 과정을 공개하는 전통이 있습니다. 취미로 코딩을 시작하던 시절을 떠올리면, 막히면 StackOverflow를 검색하는 것이 당연했습니다. 누군가 먼저 헤맨 흔적이 공개되어 있었고, 저도 그 흔적에 기대어 배웠습니다.

지금은 AI 에이전트와 함께 코딩하는 것이 당연한 시대가 되었습니다. 개발 업계는 다른 업계에 비해 문턱이 낮은 편이고, 저처럼 비전공자로 시작한 사람도 받아 주는 곳입니다. 그런데도 최근에는 발밑이 흔들리는 느낌을 받았습니다. 바이브 코딩은 밈이 되었고, 주니어 개발자들은 쓸모없어질까 불안해하고, 시니어 개발자들은 자신이 왜 여전히 필요한지 설명하느라 바빠 보였습니다.

이런 새로운 세상에서 공개적으로 배운다는 건 무슨 의미일까요? 제가 내린 답은 AI와 함께 배우고 있다는 사실을 투명하게 드러내는 것이었습니다. 어떤 코드를 Claude가 썼는지, 어떤 결정을 제가 내렸는지, 그리고 둘 중 누가 틀렸는지까지 공개하는 것입니다. 그래서 이번 글에는 제가 틀렸던 부분도 함께 적습니다.

새로운 것에 서툴 용기

취미 코딩에서 본격적인 커리어 전환으로 마음을 정했을 때, 제 머릿속에 떠오른 문장은 이것이었습니다.

Brave enough to be bad at something new.
새로운 것에 서툴 만큼의 용기.

배우면서 겪는 어려움을 숨기고 싶어질 때마다 저는 이 문장을 봅니다. 완벽해야 시작할 수 있는 것이 아니라, 서툰 과정을 견뎌야 비로소 발전할 수 있다는 뜻입니다.

제가 되고 싶은 개발자는 "Ship early, learn to iterate"를 실천하는 사람입니다. 그러려면 먼저 이 용기가 필요합니다. 이번 글의 뒷부분이 바로 그 용기를 실천해 보려는 시도입니다.

지난 글 바로잡기

두 명의 AI 리뷰어

지난 글을 올린 뒤, 프로젝트가 어딘가 조금씩 어긋나고 있다는 느낌이 들었습니다. 무엇이 어긋났는지 말로 설명할 수는 없었습니다. CBL로 치면 다시 Investigate로 돌아가야 할 때였습니다. 그래서 9월 초에 AI 리뷰어 두 명에게 프로젝트를 점검받았습니다.

  • 첫 번째 리뷰어: 이 프로젝트를 처음 보는 Cowork 세션이었습니다. 빌드는 하지 않고 코드와 문서만 읽었습니다.
  • 두 번째 리뷰어: Claude Code였습니다. 실제로 빌드하고 앱을 실행하며 수치를 측정했습니다. "동의하지 말고, 첫 번째 리뷰가 틀렸다는 증거를 찾으라"고 지시했습니다.

두 번째 리뷰어는 대부분의 코드와 문서를 직접 작성한 당사자였습니다. "이 지적에 동의하나요?"라고 물으면 동의가 돌아올 것이 뻔했습니다. 방금 읽은 주장에 대한 AI의 동의는 확인이 아니라 메아리일 뿐입니다. 반박을 요청해야만 새로운 정보를 얻을 수 있었습니다.

CBL에서 가장 공들이는 것이 좋은 guiding question을 세우는 일인데, AI에게 질문할 때도 똑같았습니다. "맞나요?"가 아니라 "틀린 곳을 찾아 주세요"라고 물어야 답이 쓸모 있어졌습니다.

실제로 첫 번째 리뷰어는 구조적 문제 중 가장 심각하다고 꼽은 것을 틀리게 짚었습니다(아래 Git 이야기 참고). 반대로, 빌드 없이 가설로만 제시한 텍스처 메모리 문제는 측정해 보니 정확했습니다. 두 리뷰어 모두 맞기도 하고 틀리기도 했습니다. 그래서 둘을 부딪히게 한 것이 의미가 있었습니다.

그 점검과 이후의 플레이를 통해 드러난 실수 중 가장 큰 두 가지를 적어 봅니다.

실수 1. "부모가 바꾸는 퀴즈"의 문은 열려 있지 않았습니다

지난 글에서 퀴즈 로더가 Documents/riddles.json을 먼저 읽으니, 부모가 앱을 다시 빌드하지 않고도 문제를 바꿔 끼울 수 있는 "문이 열려 있다"고 판단했습니다.

실제로 확인해 보니, 그 파일이 앱에 들어갈 길 자체를 만든 적이 없었습니다. 파일 앱에서 앱 폴더를 볼 수 있게 하는 설정이 빠져 있었고, 파일을 고르는 화면도 없었습니다.

고친 것:

  • 설정을 추가해 파일 앱에서 볼 수 있게 했습니다. 하지만 파일 앱에서 .json을 열면 읽기 전용 미리보기만 나옵니다. 개발자가 아닌 보통의 부모가 편집할 수 있는 상태는 아닙니다.
  • 그래서 v1에서는 부모 편집 기능을 홍보하지 않기로 했습니다.
  • 대신 내장 문제를 제대로 채웠습니다. 4개 분야에 50문제씩, 모두 200문제입니다. 처음엔 3학년 교과 범위에 맞췄다가, 직접 폰으로 풀어 보고 종이 없이 암산할 수 있는 수준으로 다시 조정했습니다.

배운 것: 설계해 둔 것과 실제로 동작하는 것은 다릅니다. 저는 해결책을 설계만 하고, 실제 사용자인 부모의 입장에서 평가(Evaluate)해 보지 않았습니다. 이 자리에서 공개적으로 인정하고 바로잡습니다.

실수 2. 디버그 지름길이 게임을 가리고 있었습니다

개발 중에는 화면 구석을 세 번 탭하면 던전을 바로 클리어하거나 마력을 채워 주는 디버그용 지름길을 일곱 개 넣어 두었습니다. 테스트에는 편했습니다. 하지만 그 때문에 아무도 이 게임을 지름길 없이 처음부터 끝까지 플레이해 본 적이 없었습니다. 모든 플레이가 어딘가에서 지름길을 탔습니다.

고친 것:

  • 9월 11일에 지름길을 모두 걷어내고, 처음으로 지름길 없이 플레이했습니다.
  • 결과는 3000 마력 중 2610, 87%에서 멈췄습니다. 아나 공주가 던전에서 보스보다 크게 보이는 버그 때문이었습니다. 던전은 다프네의 키에 맞춰 만들었는데, 아나의 크기를 그녀 자신의 키 기준으로 계산하고 있었습니다. 원인을 찾아 고쳤습니다.
  • 또 하나를 알게 되었습니다. 1000에서 3000 마력까지 구간이 너무 깁니다. 대부분의 아이들은 포기했을 것입니다. 이야기 진행도, 별다른 보상도 없이 구구단과 영어 단어만 계속 외우는 게임을 하는 느낌이었습니다. 경제 수치 전체를 한 번에 다시 맞추는 것이 다음 작업입니다.

배운 것: 테스트를 빠르게 하려고 만든 장치가, 정작 무엇을 테스트해야 하는지를 가리고 있었습니다. 지름길을 탄 플레이로 평가한 것은 실제 게임이 아니었습니다. 지름길을 걷어낸 첫 플레이가 진짜 Evaluate였고, 거기서 경제 수치 재조정이라는 다음 사이클의 Challenge가 나왔습니다.

가장 큰 배움: 문서는 검증을 대신할 수 없습니다

지난 글에서 CLAUDE.md를 "살아 있는 아키텍처 문서"라고 소개했습니다. 실제로 효과가 있었습니다. 스프라이트 패딩 규칙, HUD 배치 규약, "이미 기각한 결정은 다시 논의하지 말 것" 같은 기록이 수십 번의 세션을 거치며 프로젝트를 지켜 주었습니다.

하지만 문서는 조용히 낡습니다. 점검해 보니 CLAUDE.md의 파일 개수가 틀려 있었고, 적어 둔 줄 번호는 코드를 고칠 때마다 어긋나 있었습니다. 가장 뼈아팠던 건 따로 있었습니다. 두 번째 리뷰어의 진단 작업이 끝까지 가지 못했습니다. 보스전까지 가려면 사람이 직접 탭해야 했기 때문입니다. 사람 없이는 이 앱을 검증할 방법이 하나도 없었습니다. 수천 줄의 문서가 테스트가 해야 할 일을 대신하고 있었던 것입니다.

테스트는 만들면서 함께. 9월 7일에 처음으로 테스트 타깃을 만들었고, 지금은 테스트가 92개입니다. 씬이나 시뮬레이터가 필요 없는 모델 레이어의 순수 로직부터 시작했습니다. 예를 들어 퀴즈 테스트는 실제로 앱에 들어가는 200문제를 읽어서, 정답이 보기 안에 있는지, 산수 문제의 답이 실제로 맞는지 계산해 봅니다.

문제는 나중에 붙이는 테스트가 어렵다는 것입니다. 로직이 씬 안에 섞여 있는 기존 플랫포머 코드에는 테스트를 붙이기가 쉽지 않았습니다. 그래서 v2에서 만들 퍼즐 미니게임은 테스트를 먼저 쓰고, 로직을 처음부터 씬과 분리해서 만들기로 했습니다.

CI는 처음부터. 이제 GitHub Actions가 main에 푸시하거나 PR을 열 때마다 빌드와 테스트를 돌립니다. 문서는 제가 기억하고 고쳐야 하지만, CI는 제가 잊어도 알려 줍니다.

시간이 조각나 있는 저에게는 이 점이 특히 중요했습니다. 아이들 일정 사이사이에 작업하다 보면, 지난번에 어디까지 확인했는지 기억하기 어렵습니다. 한정된 시간 안에서 사이클을 계속 돌리려면, 매 사이클의 검증을 제 기억에 맡기지 않아야 했습니다. 처음 CI를 설정했을 때는 시뮬레이터 이름을 고정해 두었다가 GitHub 러너 환경에서 실패했습니다. CI가 가장 먼저 잡아낸 문제가 CI 자신의 설정이었던 셈입니다.

Git은 여전히 두통입니다

솔직히 적자면, Git은 지금도 저를 자주 괴롭힙니다.

  • 두 도구, 하나의 저장소: Cowork와 Claude Code가 같은 저장소를 동시에 건드리면 HEAD.lock 충돌이 생겼습니다. 한쪽이 Git 작업을 하는 동안 다른 쪽이 잠금 파일에 막히는 것입니다. 지금은 어느 도구가 저장소를 만질지 제가 정하고, 커밋과 푸시는 제 터미널에서 직접 합니다.
  • 브랜치, PR, 머지: Claude Code는 자기 브랜치(예: claude/ending-sprint)에서 작업합니다. 그 브랜치를 PR로 올리고, CI가 통과하면 main에 머지합니다. 흐름 자체는 깔끔하지만, 어떤 작업이 어느 브랜치에 있고 무엇이 이미 머지되었는지 따라가는 게 쉽지 않았습니다. 첫 번째 리뷰어가 "브랜치에 작업이 방치되어 있다"고 잘못 보고한 것도 이 혼란에서 나왔습니다. 작업은 이미 머지되어 있었고, 제 로컬 저장소가 원격보다 뒤처져 있었을 뿐이었습니다.
  • 내 Mac에만 있던 작업: 4월 29일 첫 커밋 이후 6주 동안, 모든 작업이 제 Mac에만 있었습니다. 커밋이 100개를 넘을 때까지 백업이 하나도 없었던 셈입니다. GitHub에 저장소를 만들고 처음 푸시한 건 Phase 5를 마친 6월 10일, 지난 글을 올리기 바로 전날이었습니다. 그런데 같은 일이 한 번 더 있었습니다. 9월 10일 엔딩 스프린트를 마쳤을 때, 커밋 30개가 GitHub에는 없는 로컬 브랜치에만 있었습니다. 다음 날 푸시하고 PR을 거쳐 머지했습니다. 커밋은 저장일 뿐 백업이 아니라는 것을 두 번 배웠습니다.

이 밖에도 사운드 작업을 "v1 출시 게이트"라고 부른 것을 비롯해, 레이아웃, 마력, 저장 방식, 퀴즈 수치에 대한 지난 글의 설명 몇 가지를 바로잡습니다.

지난 글 이후

지난 글 이후 묘한 옷 공방 저장소의 커밋이 85개 늘었습니다. 그런데 날짜를 보면, 6월 12일부터 9월 2일까지는 커밋이 단 1개입니다. 나머지 84개는 9월 3일부터 14일까지, 12일 동안 몰려 있습니다. GitHub의 초록색 '잔디'에도 그 모습이 그대로 드러납니다. (6–7월에 보이는 몇 칸은 다른 프로젝트 저장소를 정리한 기록입니다.)
깃허브 캡쳐

아이들의 여름방학이던 8월은 거의 통째로 비어 있습니다. 제 환경에서 사이클을 돈다는 것은 이런 모습이었습니다.

9월의 작업은 큰 사이클 하나라기보다 짧은 사이클의 반복이었습니다. 실기기에서 플레이하는 모습을 지켜보고, 거기서 나온 피드백으로 다음 작업을 정했습니다. 이번 석 달의 Challenge를 한 문장으로 정리하면 이렇습니다.

사람이 실제 기기에서 처음부터 끝까지 플레이할 수 있는 v1 만들기.

테스터가 아이들이든 저 자신이든, 사람이 직접 끝까지 플레이할 수 있어야 했습니다. 그 결과 가장 큰 변화는 두 가지입니다. 게임이 시뮬레이터를 벗어나 실제 기기에서 돌아가고, 이제 끝이 있는 게임이 되었습니다.

테스터는 우리 집 두 딸이었습니다. 한 아이는 말로, 다른 아이는 그림으로 피드백을 주었습니다.

이야기도 순환하며 진화했습니다

순환한 것은 코드만이 아니었습니다. 이야기도 만들고, 플레이해 보고, 피드백을 받으며 계속 바뀌었습니다.

지난 글 당시의 이야기는 한 줄이었습니다. 유물을 모으면 비밀 하나가 밝혀지는 구조였습니다. 지금은 두 개의 이야기가 이어 붙어 있고, 중간에 재봉사 역할이 다프네에서 아나 공주로 넘어갑니다. 역할은 넘겨주지만, 두 사람 모두 각자 이야기의 주인공입니다. 밝혀지는 비밀도 하나에서 셋으로 늘었습니다.

플레이테스트가 이야기를 바꾼 몇 가지 예입니다.

  • 선택지를 없앴습니다. 유물을 모은 뒤 어디로 갈지 고르는 선택지가 있었는데, 한쪽을 고르면 이야기에 꼭 필요한 대사를 건너뛸 수 있었습니다.
  • 오프닝을 필수로 만들었습니다. 아이들이 가게에 왜 재봉사가 필요한지 모른 채 바로 플레이를 시작했습니다.
  • 먼저 강해진 플레이어를 위한 대사를 따로 두었습니다. 마력을 빨리 모은 플레이어에게는 "더 강해지면 다시 오라"는 대사가 바로 다음 장면과 어긋났습니다.
  • 왕의 말투를 고쳤습니다. 왕비는 해요체를 쓰는데 왕은 반말과 하오체를 섞어 쓰고 있었습니다. 아내는 격식을 차리고 남편은 편하게 말하는 이 구도는, AI가 학습한 데이터에 그런 말투가 흔하기 때문이 아닐까 짐작합니다. 하지만 제가 만드는 아이들 게임에서는 재현하고 싶지 않았습니다. 성격은 그대로 두고 말의 높낮이만 해요체로 맞췄습니다.

재미있는 건 처음부터 계획하지 않은 주제가 생겼다는 점입니다. 장면을 하나씩 만들고 고치는 사이, 자매애가 게임 전체를 꿰는 하나의 줄기가 되어 있었습니다. 게임 속에는 피를 나눈 자매가 두 쌍 있고, v2에서는 피가 섞이지 않은 캐릭터들이 서로에게 자매 같은 존재가 되어 갈 예정입니다. 따로 만든 장면들이 사이클을 거치며 이 줄기로 이어진 것입니다.

그리고 여기서도 같은 교훈을 얻었습니다. 이야기에는 유닛 테스트를 붙일 수 없습니다. 이야기가 지켜야 할 규칙을 문서로 정리하고, 대사를 한 줄씩 읽어 확인하는 것이 유일한 검증입니다. 그런데도 규칙 하나는 고친 뒤에도 세 번이나 다시 어긋났습니다. 문서만으로는 부족하다는 것을 이야기에서도 배웠습니다.

현재 가게 앞 시점 전환, 스토리북 정리, 왼손잡이 설정 등을 더한 상태이고, 다음으로는 컨트롤 패드와 경제 수치 재조정, 앱스토어 제출 준비가 남아 있습니다.

게임 화면

가게 앞 장면. 이제 손님 고양이가 화면에 등장하고, 가게 주인이 그 손님과 대화합니다.

퀴즈 화면. 이번엔 진짜로 "apple"을 묻습니다. (보상과 문제 수를 조정하기 전 빌드라 +15냥, 3문제로 보입니다.)

스포일러 방지를 위해 이야기 장면은 싣지 않았습니다.

마치며

지난 글이 "이런 걸 만들었습니다"였다면, 이번 글은 "이런 걸 잘못 알고 있었고, 이렇게 알게 되었습니다"입니다.

새로운 것에 서툴 용기는 틀린 것을 공개할 용기이기도 했습니다.

다른 사람과 주고받는 코드 리뷰만큼 쉽지 않은 일입니다. AI와 함께 혼자 개발하면 저를 지켜보는 사람이 없습니다. 그래서 배움을 공개하는 것은, 스스로 세운 목표를 지키고 제 자신에게 책임을 묻는 방법이 되었습니다.

profile
Developer in training · 前 UNHCR 🕊️ #DevJourney #LearnInPublic #KeepCalmAndCarryOnCoding

0개의 댓글