실전 회고: 한 달간의 고강도 Vibe Coding, Claude로 게임 로컬 스토리지 개발한 후기
0
2026년 4월 12일
시니어 개발자가 Claude Opus 4.6을 사용하여 Vibe Coding을 진행한 생생한 회고. Texture2D 캐시 레퍼런스 카운팅을 개발하며 겪은 허니문 기간과 삽질의 경험을 공유하고, 복잡한 아키텍처 앞에서 왜 인간 개발자의 경험이 여전히 대체 불가능한지 파헤쳐 봅니다.
지난 한 달 동안 업무에서 고강도의 Claude Vibe Coding(AI 보조 프로그래밍)을 깊이 있게 경험해 보았습니다 (개인 프로젝트에서는 항상 Gemini를 사용해왔습니다). 마침 이번 달에 회사에서 API 할당량을 넉넉하게 지원해주었고, 제가 최근 게임 내 새로운 로컬 사진첩 스토리지 기능 개발을 맡게 되었기 때문입니다. 그래서 개발 방식을 전환해 보았습니다. 제가 전체적인 요구사항과 아키텍처 설계를 맡고, Claude Opus 4.6을 메인 코더로 삼아 핵심 코드를 작성하게 했습니다.
한 달간의 실전 경험을 통해 느낀 가장 큰 점은, "요구사항이 명확할 때 AI는 훌륭한 보조 도구지만, 요구사항을 고도화하고 버그를 수정할 때는 인간 개발자의 경험이 여전히 필수적"이라는 것입니다.
요구사항이 명확할 때, AI의 코드 완성도는 매우 높다
프로젝트 초기 뼈대를 잡는 단계에서는 명확한 요구사항의 경계만 제공하면 AI가 기대에 부합하는 코드를 빠르게 출력해 냈습니다.
예를 들어, 로컬 사진첩 파일의 무결성을 처리할 때 데이터 손상 방지 기능 구현을 요구했습니다. Claude는 즉시 의도를 파악하고 SQLite의 WAL(Write-Ahead Logging) 패턴을 완벽하게 차용했습니다. 임시 파일 쓰기, 원자적 교체(Atomic Swap) 및 크래시 복구 로직을 정확하게 구현해 주었습니다. 이 단계에서는 요구사항만 명확하다면 AI가 디테일을 잘 처리해주어 반복적인 기본 코드(Boilerplate)를 직접 치는 시간을 엄청나게 아껴줍니다.
버그를 수정할 때, AI의 한계가 드러난다
하지만 프로젝트가 요구사항을 수정하거나 버그를 잡는 단계에 접어들자 Claude의 한계가 나타났습니다.
AI의 가장 큰 취약점은 전체 시스템을 보는 아키텍처 시야가 부족하다는 것입니다. 로직 충돌이 발생했을 때 AI의 첫 번째 반응은 보통 '땜질식 처방'입니다. 전체 시스템 관점에서 새로운 기능을 융합하는 대신, 맥락 없이 새로운 상태 필드(Boolean)를 추가하거나 새로운 if-else 분기를 만드는 것을 아주 좋아합니다. 이때 AI의 제안을 무비판적으로 수용하면 코드는 순식간에 유지보수가 불가능해집니다.
전형적인 삽질 사례: Texture2D 캐시 레퍼런스 카운팅
C#에서 Texture2D 캐시 레퍼런스 카운팅(Reference Counting) 기능을 개발할 때 아주 전형적인 문제를 겪었습니다.
이 기능은 주로 사진첩의 메모리를 관리하기 위한 것입니다. 전체 아키텍처는 활성(Active) 페이지와 비활성(Inactive) 페이지라는 두 개의 캐시 영역으로 설계되었습니다. 레퍼런스가 있으면 활성 영역에 두고, 레퍼런스가 0이 되면 비활성 영역으로 밀어내어 언제든 메모리에서 삭제(Eviction)될 수 있도록 준비합니다.
결과적으로 비동기 콜백(Async Callback)과 캐시 레퍼런스가 결합되는 로직에서 버그가 발생했습니다. 원인은 Claude가 '페이지를 비활성 영역으로 먼저 밀어 넣은 다음, 레퍼런스를 마킹'하는 잘못된 타이밍의 코드를 작성했기 때문입니다. 이는 치명적인 경쟁 상태(Race Condition)를 유발합니다. 만약 현재 캐시 용량이 초과된 상태라면, 첫 번째 단계에서 비활성 영역으로 밀어 넣자마자 페이지가 메모리에서 삭제되어 버리고, 뒤이어 실행될 레퍼런스 마킹은 완전히 무효화되는 것입니다.
이 버그를 수정하기 위해 Claude가 내놓은 해결책은 is_early_marked(조기 마킹됨)라는 상태 필드를 하나 더 추가하여, 라이프사이클 꼬임 현상을 변칙적으로 우회하려는 것이었습니다.
저는 이 제안을 즉각 거절했습니다. 컴포넌트의 전체 라이프사이클에 대한 이해를 바탕으로 AI에게 명확한 지시를 다시 내렸습니다. "비활성 페이지를 전체적으로 검사하고 삭제를 실행하는 단계를 지연시켜서, 반드시 비동기 작업이 레퍼런스를 마킹한 '이후'에 발생하도록 보장하라"고 말입니다. 전체적인 타이밍(시퀀스)을 아키텍처 레벨에서 조정함으로써, 지저분한 잉여 필드를 하나도 추가하지 않고 문제가 깔끔하게 해결되었습니다.
요약
이번 경험은 AI가 코딩 효율성을 크게 높여주지만, 동시에 아키텍처상의 잠재적 위험을 쉽게 덮어버릴 수 있다는 것을 증명했습니다.
만약 경험이 부족한 주니어 개발자가 위와 같은 동시성 버그를 만났다면, 십중팔구 Claude의 땜빵식 제안을 그대로 채택했을 것입니다. 그러면 마킹 필드와 분기문은 점점 더 늘어날 것이고, 결국 모듈은 완전히 통제 불능의 스파게티 코드가 되고 맙니다. Vibe Coding 방식에서는 구체적인 디테일을 직접 짤 필요는 없지만, 전체 프레임워크를 보는 시야와 쓰레기 코드를 방어하는 마지노선은 여전히 인간 개발자가 굳건히 지켜야 합니다.