SQLite 개발 도구를 오랫동안 바라오던 저자가 AI 코딩 에이전트와 함께 syntaqlite를 만들며 겪은 도움과 한계를 구체적으로 돌아본다.
8년 동안, 저는 SQLite로 작업하기 위한 고품질 개발 도구 모음을 원해 왔습니다. SQLite가 업계에서 얼마나 중요한지1를 생각하면, 아무도 이를 위해 정말 훌륭한 개발자 경험을 만드는 데 투자하지 않았다는 사실이 오래도록 의아했습니다2.
몇 주 전, 저녁과 주말, 휴가일까지 써 가며 3개월 동안 약 250시간의 노력3을 들인 끝에, 저는 마침내 syntaqlite를 공개했습니다 (GitHub). 오랫동안 품어 온 바람을 이룬 셈입니다. 그리고 이것이 가능했던 가장 큰 이유는 AI 코딩 에이전트4 덕분이라고 생각합니다.
물론 AI가 한 번에 프로젝트를 끝내 줬다는 주장이나, 반대로 AI는 전부 엉망이라고 단언하는 글은 이미 넘쳐납니다. 저는 아주 다른 방식으로 접근하려 합니다. 대신 AI와 함께 syntaqlite를 만들며 겪은 경험을, 어디에서 도움이 되었고 또 어디에서 해로웠는지 체계적으로 나눠 보겠습니다.
그리고 이 경험이 얼마나 일반화될 수 있는지 여러분이 스스로 판단할 수 있도록, 프로젝트의 맥락과 제 배경도 함께 설명하겠습니다. 또한 제가 어떤 주장을 할 때마다, 가능하면 제 프로젝트 저널이나 코딩 기록, 커밋 이력5으로 뒷받침하려고 합니다.
제가 Perfetto에서 하는 일 중에는 성능 트레이스를 질의하기 위한 SQLite 기반 언어인 PerfettoSQL을 관리하는 일이 있습니다. 기본적으로는 SQLite와 같지만, 트레이스 질의 경험을 더 좋게 만들기 위한 몇 가지 확장이 들어가 있습니다. Google 내부에는 약 10만 줄의 PerfettoSQL이 있고, 매우 다양한 팀이 이를 사용합니다.
어떤 언어가 실제로 사용되기 시작하면, 사용자들은 포매터, 린터, 에디터 확장 같은 것도 기대하게 됩니다. 처음에는 오픈 소스의 SQLite 도구를 일부 가져다 쓸 수 있기를 바랐지만, 살펴볼수록 실망만 커졌습니다. 제가 찾은 것들은 신뢰성이 충분하지 않았거나, 충분히 빠르지 않았거나6, PerfettoSQL에 맞게 조정할 만큼 유연하지 않았습니다. 처음부터 새로 만들 기회가 분명히 있었지만, 그 일은 늘 “우리가 지금 할 수 있는 가장 중요한 일”은 아니었습니다. 그래서 마지못해 기존 도구들로 버텨 왔지만, 늘 더 나은 것을 바랐습니다.
한편으로는, 제 여가 시간에 직접 해 보는 선택지도 있었습니다. 저는 십 대 시절에 많은 오픈 소스 프로젝트를 만들었지만7, 대학에 들어가면서 더 이상 동기가 남아 있지 않다고 느끼며 그런 활동이 사라졌습니다. 메인테이너라는 일은 단순히 “코드를 던져 놓고” 무슨 일이 일어나는지 보는 것이 아닙니다. 버그를 분류하고, 크래시를 조사하고, 문서를 쓰고, 커뮤니티를 만들고, 무엇보다 프로젝트의 방향을 가져야 합니다.
하지만 오픈 소스에 대한 갈증, 특히 다른 사람을 도우면서도 내가 원하는 것을 자유롭게 만들 수 있다는 점은 한 번도 사라진 적이 없었습니다. SQLite 개발 도구 프로젝트는 늘 제 머릿속에서 “언젠가 해 보고 싶은 것”으로 남아 있었습니다. 다만 계속 미뤄 온 또 다른 이유가 있었습니다. 이 일은 어렵기도 하고 지루하기도 한 경계에 정확히 걸쳐 있었기 때문입니다.
제가 개인 시간을 들여 이 프로젝트를 한다면, Perfetto에만 도움이 되는 것을 만들고 싶지는 않았습니다. 어떤 SQLite 사용자에게도 쓸 수 있는 것을 만들고 싶었습니다8. 그러려면 SQL을 SQLite와 정확히 똑같이 파싱해야 합니다.
언어 지향 개발 도구의 핵심은 파서입니다. 파서는 소스 코드를 “파스 트리”로 바꾸며, 이것이 다른 모든 것의 기반이 되는 중심 자료구조입니다. 파서가 정확하지 않으면 포매터와 린터도 그 부정확함을 그대로 물려받을 수밖에 없습니다. 제가 찾은 도구들 중 많은 것은 SQLite 언어를 정확히 표현하기보다 대략 흉내 내는 파서를 갖고 있었습니다.
문제는, 다른 많은 언어와 달리 SQLite에는 어떻게 파싱되어야 하는지를 설명하는 정식 명세가 없다는 점입니다. 파서를 위한 안정적인 API도 제공하지 않습니다. 심지어 구현을 보면, 매우 특이하게도 애초에 파스 트리조차 만들지 않습니다9! 제 생각에 남는 유일하게 현실적인 접근은 SQLite 소스 코드에서 관련 부분을 조심스럽게 추출하고, 제가 원하는 파서를 만들 수 있게 그것을 수정하는 것이었습니다.
이 말은 곧 SQLite 소스 코드의 깊은 내부로 들어가야 한다는 뜻입니다. 그런데 SQLite는 이해하기가 몹시 어려운 코드베이스입니다. 전체 프로젝트가 C로 쓰였고 믿기 어려울 정도로 빽빽한 스타일을 가지고 있습니다. 저는 가상 테이블 API11와 구현을 이해하는 데만 며칠을 쓴 적이 있습니다. 전체 파서 스택을 파악하는 일은 압도적으로 느껴졌습니다.
게다가 SQLite에는 언어의 전체 표면적을 포착하는 규칙이 400개 넘게 있습니다. 이 각각의 “문법 규칙”에 대해, 해당 구문이 파스 트리의 어떤 노드로 대응되는지 제가 직접 명시해야 했습니다. 이 작업은 극도로 반복적입니다. 각 규칙은 주변의 다른 규칙들과 비슷하지만, 동시에 정의상 서로 다릅니다.
그리고 규칙만의 문제가 아닙니다. 그것이 맞는지 확인하기 위한 테스트를 생각해 내고 작성해야 하고, 뭔가 잘못되면 디버깅해야 하고, 제가 틀린 부분 때문에 사람들이 올릴 수밖에 없는 버그도 분류하고 수정해야 합니다…
오랫동안, 아이디어는 바로 여기서 멈췄습니다. 사이드 프로젝트로 하기에는 너무 어렵고12, 동기를 유지하기에는 너무 지루했고, 결국 잘 안 될 수도 있는 일에 몇 달을 투자하기에는 너무 위험해 보였습니다.
저는 2025년 초부터 코딩 에이전트(Aider, Roo Code, 그리고 7월부터는 Claude Code)를 써 왔습니다. 분명 도움이 되기는 했지만, 진지한 프로젝트를 맡길 만큼 신뢰할 수 있다고 느끼지는 못했습니다. 그런데 2025년 말쯤 되자 모델 품질이 크게 한 단계 올라간 듯했습니다13. 동시에 Perfetto에서 신뢰할 수 있는 파서만 있으면 너무 쉽게 해결됐을 문제들을 계속 마주쳤습니다. 우회책을 하나 쓸 때마다 같은 생각이 뒤에서 맴돌았습니다. 어쩌면 이제는 정말로 만들 때가 된 건 아닐까.
크리스마스 동안 생각하고 돌아볼 시간이 생겼고, 저는 AI를 가장 극단적으로 시험해 보기로 했습니다. Claude Code 하나만, 그것도 Max 요금제(월 £200)로 써서 전부 바이브 코딩으로 만들 수 있을까?
1월 대부분 동안 저는 반복적으로 시도했습니다. 제 역할은 반쯤 기술적인 매니저에 가까웠고, 설계의 거의 전부와 구현의 전부를 Claude에게 위임했습니다. 기능적으로는 꽤 괜찮은 위치에 도달했습니다. SQLite 소스에서 여러 Python 스크립트로 추출해 낸 C 파서, 그 위에 얹힌 포매터, SQLite 언어와 PerfettoSQL 확장 모두에 대한 지원, 그리고 이 모든 것을 웹 플레이그라운드로 노출한 상태였습니다.
하지만 1월 말에 코드베이스를 자세히 검토했을 때, 단점은 명확했습니다. 코드베이스가 완전히 스파게티가 되어 있었던 것입니다14. Python 기반 소스 추출 파이프라인의 큰 부분을 저는 이해하지 못했고, 함수들은 뚜렷한 구조 없이 여기저기 흩어져 있었으며, 몇몇 파일은 수천 줄까지 불어나 있었습니다. 극도로 취약했습니다. 당장의 문제는 해결했지만, 제가 품은 더 큰 비전을 감당할 수는 없었고, Perfetto 도구에 통합하는 일은 더더욱 불가능했습니다. 다행이었던 점은 이 접근이 가능하다는 것을 증명했고, 500개가 넘는 테스트를 만들어 냈으며, 그중 많은 것은 재사용할 수 있겠다고 느꼈다는 것입니다.
그래서 저는 모든 것을 버리고 처음부터 다시 시작하기로 했고, 동시에 코드베이스 대부분을 Rust로 옮기기로 했습니다15. 상위 수준 구성 요소, 예를 들어 밸리데이터나 언어 서버 구현 같은 것을 만들기에는 C가 발목을 잡으리라는 점이 보였기 때문입니다. 덤으로 추출 단계와 런타임을 각각 C와 Python으로 나누는 대신, 둘 다 같은 언어로 처리할 수도 있게 됐습니다.
더 중요했던 것은 제가 프로젝트에서 맡는 역할을 완전히 바꿨다는 점입니다. 모든 결정의 주도권을 제가 직접 쥐었고16, AI를 훨씬 더 엄격한 과정 안에서 “엄청나게 강력한 자동완성”처럼 사용했습니다. 즉, 초반에 분명한 설계 방향을 정하고, 모든 변경을 꼼꼼히 검토하고, 문제를 발견하면 즉시 고치고, AI 출력을 자동으로 점검할 수 있도록 린팅, 검증, 만만치 않은 테스트17 같은 발판에 투자했습니다.
핵심 기능들은 2월에 모였고, 마지막 구간인 업스트림 테스트 검증, 에디터 확장, 패키징, 문서화 작업을 거쳐 3월 중순에 0.1 버전을 출시했습니다.
하지만 제 생각에, 이 타임라인은 이 이야기에서 가장 흥미로운 부분이 아닙니다. 제가 정말 말하고 싶은 것은 AI 없이는 일어나지 않았을 것들과, 그것을 사용하는 동안 제가 치러야 했던 대가입니다.
저는 예전에 글을 쓴 적이 있습니다. 소프트웨어 엔지니어로서 제 가장 큰 약점 중 하나는 큰 새 프로젝트를 마주했을 때 미루는 경향이라는 내용이었습니다. 당시에는 깨닫지 못했지만, 이것은 syntaqlite를 만드는 일에 더없이 정확하게 들어맞았습니다.
AI는 기술적 판단에 대한 의심, 올바른 것을 만들고 있는지에 대한 불확실성, 시작 자체에 대한 주저함을 잠시 제쳐 둘 수 있게 해 주었습니다. 아주 구체적인 문제를 제 앞에 던져 주었기 때문입니다. “SQLite의 파싱이 어떻게 작동하는지 이해해야 한다”가 아니라, “AI가 나에게 접근 방식을 제안하게 만든 뒤, 그걸 뜯어고쳐 더 나은 것을 만들어야 한다”가 된 것입니다18. 저는 머릿속에서 끝없이 설계를 굴리는 것보다, 직접 만져 볼 수 있는 구체적 프로토타입과 눈으로 볼 코드를 훨씬 잘 다룹니다. AI는 제가 상상도 못 했던 속도로 그 지점에 도달하게 해 주었습니다. 첫걸음만 떼고 나니, 그다음 걸음들은 훨씬 쉬웠습니다.
AI는 코드 그 자체를 작성하는 행위에서는 저보다 나은 것으로 드러났습니다. 단, 그 코드가 자명하다는 가정 아래에서 말입니다. 어떤 문제를 “이 동작과 매개변수를 가진 함수를 작성하라” 또는 “이 인터페이스를 만족하는 클래스를 작성하라” 수준으로 쪼갤 수 있다면, AI는 저보다 더 빨리 그것을 만들어 냈고, 더 중요한 점은 미래의 독자에게 더 직관적으로 느껴질 수 있는 스타일로 작성하곤 했습니다. 제가 건너뛸 설명도 문서화하고, 프로젝트의 나머지 부분과 일관되게 코드를 배치하며, 사용하는 언어의 이른바 “표준 방언”에 충실합니다19.
그 표준성은 양날의 검입니다. 대부분의 프로젝트 코드에서는 표준적이라는 것이 정확히 원하는 특성입니다. 예측 가능하고, 읽기 쉽고, 놀랍지 않습니다. 하지만 모든 프로젝트에는 날카로운 가장자리, 즉 비직관적인 무언가를 해내는 데서 가치가 생기는 부분이 있습니다. syntaqlite에서는 그것이 추출 파이프라인과 파서 아키텍처였습니다. 이런 곳에서는 AI의 평준화 본능이 오히려 해로웠고, 그 부분은 제가 깊이 설계해야 했으며 종종 직접 쓰는 쪽으로 돌아갈 수밖에 없었습니다.
하지만 반대편 면도 있습니다. AI가 자명한 코드에 강한 바로 그 속도 덕분에, 리팩터링에도 강합니다. AI로 산업 규모의 코드 생성을 하려면, 반드시 끊임없이 지속적으로 리팩터링해야 합니다20. 그렇지 않으면 금세 통제가 불가능해집니다. 이것이 바이브 코딩 한 달 동안 제가 얻은 가장 중심적인 교훈이었습니다. 충분히 리팩터링하지 않았고, 그 결과 코드베이스는 제가 더 이상 추론할 수 없는 것이 되었으며, 결국 전부 버려야 했습니다. 다시 쓰는 과정에서는 리팩터링이 제 작업 흐름의 핵심이 되었습니다. 큰 덩어리의 생성 코드가 들어올 때마다 한 걸음 물러나 “이거 못생겼나?”를 자문했습니다. 때로는 AI가 스스로 정리할 수 있었습니다. 다른 때에는 AI는 보지 못하지만 저는 볼 수 있는 대규모 추상화가 있었고, 저는 방향만 제시한 뒤 실행은 맡겼습니다21. 좋은 감각이 있다면, 잘못된 접근의 비용은 극적으로 낮아집니다. 빠르게 구조를 바꿀 수 있기 때문입니다22.
AI를 사용한 여러 방식 가운데, 조사와 학습은 들인 시간 대비 가장 높은 가치를 돌려주었습니다.
저는 예전에도 인터프리터와 파서를 다뤄 봤지만, Wadler-Lindig pretty printing23은 들어본 적이 없었습니다. 포매터를 만들어야 했을 때, AI는 제가 이해할 수 있는 관점에서 구체적이고 실행 가능한 설명을 해 주었고, 더 배우기 위한 논문도 알려 주었습니다. 결국 저 혼자서도 언젠가 찾아냈겠지만, AI는 하루 이틀 읽어야 했을 내용을, “그런데 왜 이게 작동하죠?”라고 계속 물어보며 실제로 납득할 때까지 이어 갈 수 있는 집중된 대화로 압축해 주었습니다.
이 점은 제가 한 번도 일해 본 적 없는 영역까지 확장되었습니다. 저는 C++와 Android 성능 분야에는 깊은 전문성이 있지만, Rust 도구 체인이나 에디터 확장 API는 거의 건드려 본 적이 없었습니다. AI와 함께라면 그것은 문제가 아니었습니다. 기초는 같고, 용어도 비슷하며, AI가 그 간극을 메워 주기 때문입니다24. VS Code 확장은 API를 배우는 데만 하루 이틀이 걸렸을 것이고, 그 뒤에야 비로소 시작할 수 있었을 겁니다. 하지만 AI를 쓰자 한 시간 안에 동작하는 확장을 얻었습니다.
또 며칠 동안 보지 않았던 프로젝트의 일부를 다시 익히는 데도 매우 유용했습니다25. 저는 얼마나 깊게 들어갈지 조절할 수 있었습니다. “이 구성 요소를 설명해 줘”라고 하면 표면 수준의 복습을, “자세한 선형식 워크스루를 해 줘”라고 하면 더 깊은 설명을, “이 저장소에서 unsafe 사용을 감사해 줘”라고 하면 문제를 찾아다니는 검토를 받을 수 있었습니다. 맥락 전환이 많아지면 맥락은 빠르게 사라집니다. AI는 필요할 때마다 그것을 다시 가져오게 해 주었습니다.
AI는 이 프로젝트를 존재하게 만든 것만이 아니라, 지금처럼 완성도 있게 출시되게 한 이유이기도 합니다. 모든 오픈 소스 프로젝트에는 중요하지만 핵심은 아닌 기능들의 긴 꼬리가 있습니다. 이론적으로는 어떻게 해야 하는지 알지만, 핵심 작업이 더 급해서 계속 우선순위가 밀리는 것들입니다. syntaqlite에서 그 목록은 길었습니다. 에디터 확장, Python 바인딩, WASM 플레이그라운드, 문서 사이트, 여러 생태계를 위한 패키징26이 그랬습니다. AI 덕분에 이런 일들의 비용이 충분히 낮아져서, 오히려 빼는 편이 잘못된 선택처럼 느껴졌습니다.
또한 UX를 생각할 정신적 여유도 생겼습니다27. 모든 시간을 구현에만 쓰는 대신, 사용자의 첫 경험이 어떤 느낌이어야 하는지를 고민할 수 있었습니다. 어떤 오류 메시지가 실제로 SQL을 고치는 데 도움이 될지, 포매터의 기본 출력은 어떤 모습이어야 할지, CLI 플래그가 직관적인지 같은 것들입니다. 사람들에게 한 번만 써 보고 끝나는 도구와 계속 쓰게 되는 도구를 가르는 것은 바로 이런 요소들이고, AI는 제가 거기에 신경 쓸 여지를 만들어 주었습니다. AI가 없었다면 훨씬 더 작은 것을 만들었을 겁니다. 아마 에디터 확장도, 문서 사이트도 없었을 것입니다. AI는 같은 프로젝트를 더 빨리 만들게 한 것이 아닙니다. 프로젝트가 무엇인지 자체를 바꿔 놓았습니다.
AI 코딩 도구를 쓰는 일과 슬롯머신을 하는 일 사이에는 불편할 만큼 닮은 점이 있습니다28. 프롬프트를 보내고 기다리면, 아주 좋은 결과가 나오거나 쓸모없는 결과가 나옵니다. 저는 밤늦게까지 “한 번만 더 프롬프트를 보내 보자”라고 생각하는 제 자신을 자주 발견했습니다. 잘 안 될 것이라는 걸 알면서도, 무슨 일이 벌어질지 보고 싶어서 계속 AI를 시도했습니다. 매몰비용 오류도 작동했습니다. 분명 맞지 않는 작업이라는 게 보이는데도, “이번에는 표현을 다르게 하면 될지도 몰라”라고 스스로 말하며 계속 붙잡고 있었습니다.
피로의 되먹임 고리는 상황을 더 악화시켰습니다29. 에너지가 있을 때는 정확하고 범위가 잘 정해진 프롬프트를 쓸 수 있었고, 실제로 생산적이었습니다. 하지만 피곤하면 프롬프트는 모호해지고, 출력 품질은 더 나빠지며, 저는 다시 시도했고, 그 과정에서 더 피곤해졌습니다. 이런 경우에는 AI가 차라리 제가 직접 구현하는 것보다 느렸을 가능성이 컸지만, 그 고리에서 빠져나오기가 너무 어려웠습니다30.
프로젝트를 하는 동안 여러 번, 저는 코드베이스에 대한 정신적 모델을 잃었습니다31. 전체 아키텍처나 각 부분이 어떻게 맞물리는지에 대한 이해를 말하는 것이 아닙니다. 매일매일의 구체적인 감각, 어디에 무엇이 있고, 어떤 함수가 어떤 함수를 부르며, 작은 결정들이 쌓여 동작하는 시스템이 되는 그 디테일한 감각을 말합니다. 그런 일이 생기면 예상 못 한 문제가 튀어나왔고, 저는 무엇이 잘못되고 있는지 전혀 이해하지 못한 채 멍해지곤 했습니다. 저는 그 느낌이 정말 싫었습니다.
더 깊은 문제는, 그렇게 감을 잃으면 의사소통 자체가 무너진다는 점이었습니다32. 머릿속에서 흐름을 놓치고 나면, 에이전트와 의미 있게 소통하는 것이 불가능해집니다. 모든 대화가 길어지고 장황해집니다. “FooClass를 X 하도록 바꿔” 대신, “Bar를 하는 그 무언가를 X 하도록 바꿔”라고 말하게 됩니다. 그러면 에이전트는 Bar가 무엇인지, 그것이 FooClass와 어떻게 대응되는지 알아내야 하고, 때로는 틀리게 이해합니다33. 이것은 코드도 이해하지 못하는 매니저가 엔지니어에게 공상적이거나 불가능한 요구를 할 때 엔지니어들이 늘 하던 불평과 정확히 같습니다. 다만 이제는 제가 그 매니저가 되어 버린 것입니다.
해결책은 의도적인 습관이었습니다. 구현이 끝나자마자 코드를 읽고, “내가 했다면 무엇을 다르게 했을까?”를 적극적으로 생각하는 습관을 들였습니다.
물론 어떤 의미에서는 위의 내용 대부분이 몇 달 전에 제가 직접 쓴 코드에도 해당합니다. 그래서 AI 코드란 첫날부터 레거시 코드라는 생각도 나오는 것이겠지요. 하지만 AI는 원래 손으로 직접 타이핑하며 쌓이는 근육 기억을 만들지 않기 때문에, 그 괴리가 훨씬 더 빨리 생기게 만듭니다.
3개월 동안 서서히 드러난 다른 문제들도 있었습니다.
AI는 제가 핵심 설계 결정을 미루게 만든다는 것을 알게 됐습니다34. 리팩터링 비용이 싸니까, 언제든 “나중에 처리하지 뭐”라고 말할 수 있었습니다. 그리고 AI는 코드를 생성한 것과 같은 산업 규모로 리팩터링도 할 수 있었기 때문에, 미루는 비용이 낮게 느껴졌습니다. 하지만 실제로는 그렇지 않았습니다. 결정 미루기는 제 사고를 서서히 침식했습니다. 그 사이 코드베이스는 계속 헷갈리는 상태로 남아 있었기 때문입니다. 바이브 코딩 한 달은 이것의 가장 극단적인 사례였습니다. 물론 저는 문제를 이해하고 있었지만, 어려운 설계 결정을 더 일찍 내리는 데 스스로 더 엄격했다면, 올바른 아키텍처에 훨씬 빨리 수렴했을 것입니다.
테스트도 비슷한 종류의 거짓된 안도감을 만들었습니다35. 500개가 넘는 테스트가 있다는 사실은 든든하게 느껴졌고, AI는 테스트를 더 만드는 것도 쉽게 해 주었습니다. 하지만 사람도 AI도 미래에 맞닥뜨릴 모든 엣지 케이스를 미리 상상할 만큼 창의적이지는 않습니다. 바이브 코딩 단계에서는 테스트 케이스를 하나 떠올리고 나서, 어떤 구성 요소의 설계가 완전히 잘못되었고 전면적으로 다시 만들어야 한다는 사실을 깨달은 일이 여러 번 있었습니다. 이것은 제가 그 코드베이스를 신뢰하지 못하게 된 중요한 이유였고, 모든 것을 폐기하고 처음부터 다시 시작하게 만든 결정적 요인이기도 했습니다.
결국 제가 배운 것은, AI 시대에도 소프트웨어의 “기본 규칙”은 여전히 유효하다는 점입니다. 명확한 아키텍처와 잘 정의된 경계 같은 근본적인 기반이 없으면, 버그가 나타날 때마다 끝없이 쫓아다니는 처지가 될 뿐입니다.
제가 계속 돌아오게 되는 생각 중 하나는, AI가 시간의 흐름을 얼마나 이해하지 못하는가였습니다36. AI는 특정 상태의 코드베이스를 보지만, 인간처럼 시간을 느끼지는 못합니다. 저는 어떤 API를 사용하는 감각이 어떤지, 그것이 수개월 혹은 수년에 걸쳐 어떻게 변했는지, 왜 어떤 결정이 내려졌다가 나중에 뒤집혔는지 말할 수 있습니다.
이런 이해 부족에서 자연스럽게 생기는 문제는, 과거에 이미 했던 실수를 또 반복해 그 교훈을 다시 배워야 하거나, 혹은 처음에는 성공적으로 피했던 새로운 함정에 빠져 장기적으로 더 느려진다는 점입니다. 제 생각에 이것은 왜 훌륭한 시니어 엔지니어 한 명을 잃는 것이 팀에 그렇게 큰 타격이 되는지와 비슷한 문제입니다. 그런 사람은 다른 어디에도 존재하지 않는 역사와 맥락을 몸에 지니고 있고, 주변 사람들에게 길잡이 역할을 해 주기 때문입니다.
이론적으로는 명세와 문서를 계속 최신 상태로 유지해 이런 맥락을 보존하려고 시도할 수 있습니다. 하지만 AI 이전에도 우리가 그렇게 하지 않았던 데는 이유가 있습니다. 암묵적인 설계 결정을 빠짐없이 문서화하는 일은 쓰는 데 엄청난 비용과 시간이 들기 때문입니다. AI가 이런 문서를 초안하는 데 도움을 줄 수는 있지만, 무엇이 중요한지 정확히 포착했는지를 자동으로 검증할 방법이 없기 때문에, 결국 사람은 결과를 직접 하나하나 감사해야 합니다. 그리고 그것 역시 시간이 많이 듭니다.
여기에 맥락 오염 문제도 있습니다. API A에 대한 설계 노트가 언제 API B에서 메아리칠지 알 수 없습니다. 일관성은 코드베이스를 작동하게 만드는 데 매우 큰 요소이고, 그러려면 지금 작업하는 것에 대한 맥락만이 아니라 비슷한 방식으로 설계된 다른 것들에 대한 맥락도 필요합니다. 무엇이 관련 있는지를 판단하는 일은, 처음부터 바로 그런 제도적 지식이 제공하던 종류의 판단력을 정확히 필요로 합니다.
위의 내용을 돌아보면, AI가 도움이 된 때와 해가 된 때의 패턴은 꽤 일관적이었습니다.
제가 이미 깊이 이해하고 있는 것을 다룰 때, AI는 탁월했습니다. 출력 결과를 즉시 검토할 수 있었고, 잘못된 점을 반영되기 전에 잡아낼 수 있었으며, 혼자였다면 절대 내지 못했을 속도로 움직일 수 있었습니다. 파서 규칙 생성이 가장 분명한 예입니다37. 저는 각 규칙이 무엇을 만들어야 하는지 정확히 알고 있었기 때문에, AI의 출력을 1~2분 안에 검토하고 빠르게 반복할 수 있었습니다.
설명은 할 수 있지만 아직 완전히 알지는 못하는 것을 다룰 때, AI는 좋았지만 더 많은 주의가 필요했습니다. 포매터를 위해 Wadler-Lindig를 익힐 때가 그랬습니다. 저는 원하는 것이 무엇인지 표현할 수 있었고, 출력이 올바른 방향으로 가고 있는지도 평가할 수 있었으며, AI의 설명을 통해 배울 수도 있었습니다. 하지만 계속 적극적으로 관여해야 했고, 단지 AI가 준 것을 그대로 받아들일 수는 없었습니다.
제가 무엇을 원하는지조차 모르는 것을 다룰 때, AI는 도움이 안 되거나 오히려 해로웠습니다. 프로젝트 아키텍처가 가장 분명한 사례였습니다. 초반 몇 주 동안 저는 AI가 이끄는 막다른 길을 따라가며, 순간적으로는 생산적으로 느껴지지만 자세히 들여다보면 무너지는 설계들을 탐색하는 데 시간을 보냈습니다. 지금 와서 생각하면, 애초에 AI를 끼우지 않고 혼자 충분히 생각했더라면 더 빨랐을지도 모르겠다는 생각이 듭니다.
하지만 전문성만으로 충분한 것은 아닙니다. 제가 어떤 문제를 깊이 이해하고 있는 경우에도, 과제가 객관적으로 검증 가능한 답을 갖고 있지 않다면 AI는 여전히 고전했습니다38. 구현에는 적어도 국소적인 수준에서는 정답이 있습니다. 코드가 컴파일되고, 테스트가 통과하고, 출력이 요구한 것과 맞는지 확인할 수 있습니다. 하지만 설계에는 그런 것이 없습니다. 객체지향에 대해서조차 우리는 수십 년이 지난 지금도 여전히 논쟁하고 있습니다.
구체적으로 말하면, syntaqlite의 공개 API를 설계할 때 이 문제가 가장 뼈아프게 드러났습니다. 3월 초의 며칠 동안 저는 API 리팩터링만 했습니다. 숙련된 엔지니어라면 본능적으로 피했을 문제들을 AI가 완전히 뒤죽박죽으로 만들어 놓았고, 저는 그것을 손으로 바로잡아야 했습니다. “이 API는 사용하기 즐거운가”, “이 API는 사용자가 가진 문제를 해결하는 데 도움이 되는가”를 위한 테스트나 객관적 지표는 없습니다. 그리고 바로 그렇기 때문에 코딩 에이전트는 그 일에서 끔찍할 만큼 못했습니다.
이 대목은 제가 한때 물리학, 특히 상대성 이론에 집착하던 시절을 떠올리게 합니다. 물리 법칙은 어떤 작은 국소 영역에서는 단순하고 뉴턴적으로 보이지만, 멀리서 보면 시공간은 국소 그림만으로는 예측할 수 없는 방식으로 휘어집니다. 코드도 같습니다. 함수나 클래스 수준에서는 대개 분명한 정답이 있고, AI는 그 지점에서 탁월합니다. 하지만 아키텍처는 그런 국소 조각들이 서로 상호작용할 때 생기는 것이며, 국소적으로만 맞는 구성 요소들을 이어 붙인다고 해서 좋은 전역적 동작이 나오지는 않습니다.
주어진 순간마다 내가 이 축들 중 어디에 있는지를 아는 것, 그것이야말로 AI와 효과적으로 일하기 위한 핵심 능력이라고 저는 생각합니다.
8년은 하나의 프로젝트를 머릿속에 담아 두기에는 긴 시간입니다. 단 3개월의 작업 끝에 이 SQLite 도구들이 실제로 존재하고 동작하는 것을 보는 일은 엄청난 성취이며, 저는 이것이 AI 없이는 가능하지 않았다는 사실을 분명히 알고 있습니다.
하지만 그 과정은 사람들이 흔히 올리는 깔끔하고 직선적인 성공담이 아니었습니다. 저는 바이브 코딩으로 한 달을 통째로 날렸습니다. 제가 실제로 이해하지 못하는 코드베이스를 관리하는 함정에 빠졌고, 그 대가로 전체 재작성을 치렀습니다.
제가 얻은 결론은 단순합니다. AI는 구현에서는 놀라운 증폭기이지만, 설계를 대신하는 존재로는 위험합니다. 구체적인 기술 질문에 대한 올바른 답을 주는 데는 탁월하지만, 역사 감각도, 미적 감각도, 사람이 실제로 그 API를 쓰며 어떤 느낌을 받을지에 대한 감각도 없습니다. 소프트웨어의 “영혼”을 그것에 맡기면, 여러분은 그저 전에 없이 빠른 속도로 벽에 부딪히게 될 뿐입니다.
제가 다른 사람들에게서 더 보고 싶은 것은, 바로 여기서 제가 해 보려 한 것과 같은 종류의 이야기입니다. 이런 도구들로 실제 소프트웨어를 만들며 겪은 솔직하고 자세한 기록 말입니다. 주말 장난감 프로젝트나 일회성 스크립트가 아니라, 사용자와의 접촉, 버그 리포트, 그리고 스스로의 변하는 생각을 견뎌 내야 하는 종류의 소프트웨어에 대한 기록입니다.