AI가 빠르게 코드를 써줄 때도 Joel Spolsky의 소프트웨어 진리는 유효합니다. 테스트, 채용, 단순성에 집중해 정확성을 지키는 방법을 알아보세요.

AI는 몇 분 만에 그럴듯한 동작 코드를 만들어냅니다. 이는 프로젝트의 속도를 바꾸지만, 소프트웨어가 성공하는 조건을 바꾸지는 않습니다. Joel Spolsky의 “소프트웨어 진리”가 말하려던 건 타이핑 속도가 아니라 판단, 피드백 루프, 그리고 스스로 만드는 복잡성 회피였습니다.
변한 것은 코드 생성의 비용입니다. 세 가지 접근법, 다섯 가지 변형, 또는 전체 리라이트를 요청하고 즉시 결과를 받을 수 있습니다. 그러나 올바른 접근을 선택하고, 그것을 점검하고, 몇 달 동안 그 결과를 감내하는 비용은 변하지 않았습니다. 작성에 절약된 시간은 종종 당신이 무엇을 의미했는지 결정하고, 엣지 케이스를 검증하며, 오늘의 빠른 승리가 내일의 유지보수 비용이 되지 않도록 하는 데로 옮겨갑니다.
정확성, 보안, 유지보수성은 증거에 의존하기 때문에 여전히 실제 시간이 듭니다. 로그인 흐름은 컴파일될 때 끝나는 것이 아니라 나쁜 입력을 신뢰성 있게 거부하고, 이상한 상태를 처리하며, 데이터가 유출되지 않을 때 끝납니다. AI는 권한 검사 누락이나 결제 업데이트의 경쟁 상태 같은 결정적 세부를 놓치면서도 확신에 차 보일 수 있습니다.
AI는 빠른 초안 생성기로 대할 때 가장 강력합니다. 보일러플레이트, 반복적인 패턴, 빠른 리팩터링, 비교할 수 있는 옵션 탐색에 빛을 발합니다. 잘 쓰면 ‘백지 화면’ 단계를 압축해줍니다.
반면에 애매한 목표를 주고 결과를 그대로 받아들일 때 가장 해를 입힙니다. 반복되는 실패 패턴은 숨겨진 가정(명시되지 않은 비즈니스 규칙), 테스트되지 않은 경로(오류 처리, 재시도, 빈 상태), 그럴듯하지만 미묘하게 틀린 자신감 있는 실수, 나중에 설명하기 어려운 ‘영리한’ 해결책입니다.
코드가 싸지면 새로 귀해지는 자원은 신뢰입니다. 이 진리들이 중요한 이유는 사용자, 팀 동료, 그리고 미래의 자신과의 신뢰를 지키기 때문입니다.
AI로 기능을 몇 분 만에 생성할 수 있을 때 테스트를 제거해야 하는 느린 부분으로 취급하고 싶은 유혹이 있습니다. Spolsky의 요지는 여전히 유효합니다: 느린 부분이 진실이 있는 곳입니다. 코드는 생성하기 쉽지만, 올바른 동작은 그렇지 않습니다.
유용한 전환은 테스트를 실행 가능한 요구사항으로 다루는 것입니다. 기대 동작을 체크 가능한 방식으로 설명할 수 없다면 생각이 끝난 것이 아닙니다. AI 보조 작업에서는 모델이 약간 틀린 무언가를 자신 있게 만들어낼 수 있기 때문에 이 점이 더 중요합니다.
먼저 깨지면 가장 큰 피해를 주는 것들을 테스트하세요. 대부분 제품에서는 핵심 흐름(가입, 결제, 저장, 내보내기), 권한(누가 볼 수/편집/삭제할 수 있는가), 데이터 무결성(중복 없음, 합계 정확, 안전한 마이그레이션)이 그 대상입니다. 그런 다음 늦은 밤 사고를 일으키는 경계들—빈 입력, 긴 텍스트, 시간대, 재시도, 결제·이메일·파일 업로드 같은 불안정한 외부 경계—를 커버하세요.
AI는 테스트 케이스 제안에 능하지만 당신이 실제로 사용자에게 약속한 것을 알 수는 없습니다. 브레인스토밍 파트너처럼 사용하세요: 누락된 엣지 케이스, 악용 시나리오, 권한 조합을 물어보세요. 그런 다음 인간이 할 일을 하세요: 적용 범위를 실제 규칙에 맞추고 구현만 ‘테스트하는’ 테스트는 제거하세요.
실패를 조치 가능하게 만드세요. 실패하는 테스트는 당신을 보물찾기로 내모는 것이 아니라 무엇이 깨졌는지 알려줘야 합니다. 테스트는 작게 유지하고, 문장처럼 이름을 붙이며, 오류 메시지는 구체적으로 만드세요.
AI의 도움으로 ‘팀 노트’ CRUD 앱을 만들었다고 가정해 보세요. 화면은 빠르게 나옵니다. 올바름의 위험은 UI가 아니라 접근 제어와 데이터 규칙입니다: 사용자는 다른 팀의 노트를 보지 못해야 하고, 편집은 더 최신 변경을 덮어쓰지 말아야 하며, 노트를 삭제해도 고아 첨부 파일이 남아서는 안 됩니다. 이런 규칙을 잠그는 테스트가 병목처럼 느껴지겠지만, 동시에 안전망이기도 합니다.
테스트가 병목일 때 그것은 명확성을 강제합니다. 그 명확성이 빠른 코드를 빠른 버그로 바뀌지 않게 지켜줍니다.
지속적인 진리는 단순한 코드가 영리한 코드보다 낫다는 것입니다. AI는 세련되고 빠른 멋진 추상화를 받아들이게 만드는 유혹을 제공합니다. 그 비용은 나중에 드러납니다: 버그가 숨어들 공간이 더 많아지고, 파일을 더 많이 살펴봐야 하며, ‘이게 도대체 무엇을 하는가?’ 하는 순간이 늘어납니다.
코드가 싸면 비용을 지불하는 것은 복잡성입니다. 작고 지루한 설계는 테스트하기 쉽고, 변경하기 쉽고, 설명하기 쉽습니다. 첫 초안이 모델에서 나온 경우, 모델이 미묘하게 틀릴 수 있기 때문에 이 점은 더 중요합니다.
실용적인 규칙은 함수, 컴포넌트, 모듈을 동료가 몇 분 안에 검토할 수 있을 정도로 작게 유지하는 것입니다. 예를 들어 React 컴포넌트에 여러 커스텀 훅, 로컬 상태 기계, 일반적인 ‘스마트 렌더러’ 레이어가 필요하다면 잠시 멈추고 실제 문제를 해결하는지, 아니면 AI가 제안했기 때문에 아키텍처를 받아들이는지 자문하세요.
몇 가지 ‘단순성 검사’가 반박하는 데 도움이 됩니다:
프롬프트가 여기서 중요합니다. “최고의 아키텍처”를 요청하면 종종 과하게 구축된 결과를 얻습니다. 파일 수를 최소화하는 단서, 중복을 3곳 이상 제거하지 않으면 새로운 추상화를 피하라는 제약 등 단순함을 유도하는 제약을 요구하세요.
구체적 예: 관리 페이지에 역할 기반 접근을 추가하라고 AI에 요청했습니다. 영리한 버전은 권한 프레임워크, 데코레이터, 설정 DSL을 도입합니다. 단순한 버전은 한 곳에서 사용자 역할을 확인하고, 라우트를 한 곳에서 게이트하고, 거부된 접근을 기록합니다. 단순한 버전이 검토하기 쉽고 테스트하기 쉽고 오해하기 어렵습니다.
채팅 기반 도구(예: Koder.ai)를 사용한다면 단순함은 스냅샷과 롤백의 가치를 더 높입니다. 작고 명백한 변경은 비교, 보관, 되돌리기가 더 쉽습니다.
코드가 쉽게 생성되면 희소한 기술은 ‘무엇이 존재해야 하는가’를 선택하고 그것이 올바른지 확실히 하는 능력입니다. ‘훌륭한 프로그래머를 채용하라’는 오래된 조언은 여전히 유효하지만, 역할이 바뀝니다. 빠르게 타이핑하는 사람을 채용하는 것이 아니라 판단하고 다듬고 제품을 옹호할 사람을 채용해야 합니다.
AI 보조 개발에서 가장 가치 있는 사람들은 보통 네 가지 특성을 공유합니다: 판단력(무엇이 중요한가), 감각(좋음이 무엇인지), 디버깅 기술(진짜 원인 찾기), 의사소통(트레이드오프를 명확히 하기). 이들은 AI가 쓴 ‘대체로 동작하는’ 기능을 신뢰할 수 있는 것으로 바꿉니다.
완벽한 솔루션을 처음부터 요구하는 대신, AI가 생성한 pull request(또는 붙여넣은 diff)를 주고 현실적인 문제 몇 가지를 포함하세요: 불명확한 네이밍, 숨겨진 엣지 케이스, 빠진 테스트, 작은 보안 실수.
후보자에게 코드가 무엇을 하려는지 평이한 언어로 설명하게 하고, 가장 위험한 부분을 찾게 하고, 수정을 제안하게 하고, 회귀를 잡을 테스트를 추가(또는 설계)하게 하세요. 강력한 신호를 원하면 다음 AI 시도를 더 낫게 만들기 위해 지침을 어떻게 바꿀지도 물어보세요.
이는 불완전한 코드, 제한된 시간, 우선순위를 정해야 하는 실제 상황에서 그들이 어떻게 생각하는지를 드러냅니다.
AI는 종종 자신감 있게 들립니다. 좋은 채용자는 밀어붙이는 데 익숙합니다. 복잡성을 추가하는 기능에 ‘아니오’라고 말할 수 있고, 보안을 약화시키는 변경에 ‘아니오’라고 말할 수 있으며, 증거 없이 출시하는 것에 ‘아니오’라고 말할 수 있습니다.
구체적 신호는 “이걸 병합하겠는가?”라는 질문에 어떻게 답하는가입니다. 강한 후보자는 분위기로 답하지 않고, 결정과 함께 필요한 변경 목록을 짧게 제시합니다.
예: ‘빠른’ 접근 제어 업데이트를 요청했더니 AI가 핸들러 전체에 검사를 뿌리는 것을 제안합니다. 강한 후보자는 그 접근을 거부하고 명확한 인증/인가 레이어 하나와 관리자/비관리자 경로에 대한 테스트를 제안합니다.
마지막으로 팀이 AI 출력을 같은 방식으로 편집하도록 공통 기준을 만드세요. 간단하게 유지하세요: 하나의 완료 정의, 일관된 리뷰 기대치, 테스트 기준.
AI가 몇 분 안에 많은 코드를 생성할 수 있을 때 사고를 건너뛰고 바로 반복하는 유혹이 있습니다. 데모에는 통할 수 있지만, 정확성·예측 가능한 동작·적은 놀라움을 원할 때는 망가집니다.
좋은 프롬프트는 보통 변장한 짧은 명세입니다. 코드를 요청하기 전에 막연한 목표를 몇 가지 수용 기준과 명시적 비목표로 바꾸세요. 이렇게 하면 AI(와 팀)가 조용히 범위를 확장하는 것을 막을 수 있습니다.
명세는 작지만 구체적으로 유지하세요. 소설을 쓰는 것이 아니라 다음을 둘러싼 경계를 정하는 것입니다:
생성 전에 ‘완료’를 정의하세요. ‘완료’는 ‘컴파일됨’이나 ‘UI가 맞아 보임’ 이상의 것이어야 합니다. 테스트 기대치, 하위 호환성, 배포 후 모니터링 항목을 포함하세요.
예: “비밀번호 재설정 추가”를 원한다면 더 명확한 명세는 이렇습니다: 사용자는 이메일로 재설정을 요청; 링크는 15분 후 만료; 이메일이 존재하든 아니든 동일한 메시지 표시; IP당 속도 제한; 토큰을 평문으로 저장하지 않고 재설정 시도를 기록. 비목표: 로그인 페이지 재설계 금지. 이제 프롬프트에 가드레일이 생기고 리뷰도 단순해집니다.
간단한 변경 로그를 가볍게 유지하세요. 결정당 한 문단이면 충분합니다. 왜 이 접근을 선택했고 대안은 왜 거부했는지 적어두면, 두 주 뒤에 “왜 이렇게 했지?”라는 질문에 답할 수 있습니다.
AI로 가장 큰 변화는 코드를 만드는 것이 쉬워졌다는 것입니다. 어려운 부분은 코드가 무엇을 해야 하는지를 결정하고 그것이 그 일을 증명하는 것입니다.
먼저 목표와 제약을 평이한 언어로 쓰세요. 절대 일어나면 안 되는 것, 느려도 되는 것, 이번 범위에 포함되지 않는 것을 포함하세요. 좋은 제약은 테스트 가능해야 합니다: “사용자가 다른 사용자의 데이터를 보지 못해야 한다” 또는 “합계는 재무 내보내기와 센트 단위로 일치해야 한다.”
코드를 요청하기 전에 간단한 설계와 트레이드오프를 요청하세요. AI가 저장할 것, 검증할 것, 로깅할 것을 판단 근거 형태로 보여주길 원합니다. 영리한 제안이 나오면 다시 단순한 버전을 요청하세요—제약을 만족하는 가장 단순한 버전으로 되돌리게 하세요.
반복 가능한 루프는 다음과 같습니다:
작은 시나리오 예: 주문 화면에 ‘환불 상태’ 추가. AI는 UI를 빠르게 만들 수 있지만 정확성은 엣지 케이스에 있습니다. 부분 환불은? 결제 사업자가 웹훅을 재시도하면? 이런 케이스를 먼저 쓰고 데이터베이스 컬럼과 검증을 한 번에 구현해 테스트로 확인하세요.
Koder.ai 같은 도구를 쓰면 플래닝 모드, 스냅샷, 롤백 기능이 이 루프에 자연스럽게 들어맞습니다: 먼저 계획하고, 조각별로 생성하고, 의미 있는 변경마다 안전 복원 지점을 캡처하세요.
코드 생성이 빠르면 코드 자체를 작업 산출물로 취급하기 쉽습니다. 작업 산출물은 동작입니다: 앱이 문제가 생겨도 올바르게 동작하는 것입니다.
AI는 자주 확신에 차 보이지만 추측하는 경우가 많습니다. 실패는 지루한 부분을 건너뛰는 데서 옵니다: 테스트 실행, 엣지 케이스 확인, 실제 입력 검증. 단순한 습관: 변경을 수용하기 전에 “어떻게 이것이 올바른지 알 수 있나?”라고 물어보세요. 답이 “그냥 맞아 보인다”라면 도박입니다.
AI는 캐싱, 재시도, 더 많은 설정, 더 많은 엔드포인트, 더 예쁜 UI를 추가하는 것을 좋아합니다. 좋은 아이디어일 수 있지만 리스크도 함께 늘립니다. 대부분의 버그는 아무도 요청하지 않은 ‘있으면 좋은’ 기능에서 나옵니다.
단호한 경계 유지: 해결하려던 문제만 먼저 해결하고 멈추세요. 가치가 있는 제안은 별도 작업으로 캡처하고 자체 테스트를 달아 추적하세요.
큰 AI 생성 커밋은 수많은 무관한 결정을 숨길 수 있습니다. 리뷰는 결국 고무도장 찍기가 됩니다. 채팅 출력은 초안으로 다루고 읽고 실행하고 되돌릴 수 있는 작은 변경으로 나누세요. 스냅샷과 롤백은 적절한 시점에 찍어야 유용합니다.
간단한 제한 몇 가지: 변경 세트당 하나의 기능, 변경 세트당 하나의 마이그레이션, 한 번에 하나의 고위험 영역(인증, 결제, 데이터 삭제), 같은 변경에서 테스트 업데이트, 그리고 어떻게 검증할지 짧게 적기.
AI는 학습 데이터에서 패턴을 재생산하거나 이해하지 못한 의존성을 제안할 수 있습니다. 라이선스가 괜찮더라도 더 큰 위험은 보안입니다: 하드코딩된 비밀, 약한 토큰 처리, 안전하지 않은 파일/쿼리 작업 등.
스니펫의 동작을 설명할 수 없다면 배포하지 마세요. 더 단순한 버전을 요청하거나 스스로 다시 작성하세요.
많은 “내 머신에선 됐음” 버그는 데이터와 스케일 문제입니다. AI는 기존 행, 큰 테이블, 다운타임을 고려하지 않고 스키마 변경을 만들 수 있습니다.
현실적인 예: 모델이 PostgreSQL 테이블에 NOT NULL 새 컬럼을 추가하고 느린 루프로 백필(backfill)합니다. 프로덕션에서는 테이블을 잠그고 앱을 망가뜨릴 수 있습니다. 백만 행, 느린 네트워크, 배포 도중 실패를 항상 고려하세요.
간단한 내부 요청 추적기를 상상해 보세요: 사람들이 요청을 제출하고, 매니저가 승인하거나 거부하고, 재무가 결제 표시를 합니다. AI 도움으로 화면과 엔드포인트를 빨리 만들 수 있지만, 당신을 느리게 하는 부분은 항상 동일합니다: 규칙들, 타이핑이 아님.
먼저 최소한으로 정확해야 할 것을 적으세요. 평이한 말로 설명할 수 없다면 테스트할 수 없습니다.
초기 버전의 촘촘한 정의는 보통 이렇습니다: 필드(title, requester, department, amount, reason, status, timestamps); 역할(requester, approver, finance, admin); 상태(draft, submitted, approved, rejected, paid). 그런 다음 중요한 전환을 명시하세요: 제출된 것을 승인하거나 거부할 수 있는 사람은 오직 approver; approved를 paid로 표시할 수 있는 사람은 오직 finance.
AI는 통제된 순서로 사용하세요:
가장 가치 있는 테스트는 “페이지가 로드되는가”가 아니라 권한 검사와 상태 전환입니다. 예: 요청자가 스스로 승인할 수 없는지, approver가 결제 표시를 할 수 없는지, 거부된 요청은 결제될 수 없는지, 제출 후 금액을 편집할 수 없는지 등을 증명하세요.
가장 오래 걸리는 일은 엣지 케이스를 명확히 하는 것입니다. approver가 거부한 뒤 마음을 바꿀 수 있나? 두 approver가 동시에 승인 버튼을 누르면? finance가 부분 결제를 해야 한다면? AI는 어떤 답을 선택하든 그에 맞는 코드를 생성할 수 있지만, 답을 선택하는 것은 당신 몫입니다. 정확성은 그런 결정을 내리고 코드를 그 규칙에 맞게 강제하는 것에서 옵니다.
AI는 많은 코드를 빠르게 만들지만 마지막 단계는 여전히 사람의 일입니다: 당신이 의도한 대로 작동하는지 증명하고, 작동하지 않을 때 안전하게 실패하도록 하는 것.
체크박스를 채우기 전에 가장 작은 ‘완료’ 정의를 정하세요. 작은 기능이면 한 가지 해피 패스, 두 가지 실패 패스, 빠른 가독성 점검이면 될 수 있습니다. 결제나 인증이면 기준을 높이세요.
AI가 관리 화면에 ‘사용자 일괄 초대’ 기능을 추가했습니다. 해피 패스는 작동하지만 실제 위험은 중복 이메일, 부분 실패, 속도 제한입니다. 안정적인 배포 결정은 중복에 대한 자동화된 테스트 하나, 부분 실패 메시지에 대한 수동 확인 하나, 롤백 계획 하나일 수 있습니다.
코드가 싸지면 위험은 당신이 무엇을 요청했는지, 무엇을 수용했는지, 무엇을 배포했는지에 달려 있습니다. AI 보조 작업에서 이 진리들을 활용하려면 ‘거의 맞는’ 변경이 스며들지 못하도록 가드레일을 추가하세요.
다음 기능에 대해 한 페이지 분량의 명세로 시작하세요. 평이하게: 대상자, 기능, 하지 않을 것, 일상어로 쓴 수용 테스트 몇 가지. 이 수용 테스트가 AI가 유혹적인 지름길을 제안할 때 기준점이 됩니다.
낮은 프로세스 오버헤드로 확장 가능한 가드레일 세트:
프롬프트는 이제 프로세스의 일부입니다. 허우대 스타일을 합의하세요: 허용되는 라이브러리, 오류 처리 방식, ‘완료’의 의미, 반드시 통과해야 하는 테스트. 재사용할 수 없는 프롬프트라면 아마 너무 모호한 것입니다.
채팅 우선 방식으로 웹, 백엔드, 모바일 앱을 빌드하는 것을 선호한다면 Koder.ai (koder.ai)는 플래닝 모드, 스냅샷, 소스 코드 내보내기 같은 기능으로 이 가드레일을 지원하는 한 예입니다. 도구는 초안을 빠르게 만들 수 있게 해주지만 사람의 규율이 정확성을 유지합니다.
AI 출력은 빠른 초안으로 취급하세요. 먼저 3–5개의 합격/불합격 수용 기준을 쓰고, 한 번에 한 조각(한 엔드포인트, 한 화면, 한 마이그레이션)만 생성한 뒤 테스트와 실패 케이스로 검증하세요.
테스트는 코드가 실제로 무엇을 하는지를 발견하는 곳이기 때문입니다. AI는 신뢰해 보이는 논리를 만들 수 있지만 권한, 재시도, 엣지 상태 같은 핵심 규칙 하나를 놓칠 수 있습니다. 테스트는 기대를 실행 가능한 형태로 바꿉니다.
다음부터 시작하세요:
가치가 큰 동작들이 먼저 잠기면 그다음에 범위를 넓히세요.
제약을 명시하고 가장 단순한 접근을 요청하세요. 필요 이상으로 추상화를 추가하지 말고, 새로운 추상화는 3곳 이상에서 중복을 제거하거나 정확성을 증명하는 데 도움이 될 때만 도입하세요.
짧은 명세를 작성하세요: 입력, 출력, 오류, 제약, 비목표를 포함합니다. 구체적 예시(요청/응답 샘플, 엣지 케이스)를 넣고, 미리 ‘완료’ 기준(필요한 테스트, 하위 호환성, 확인 방법)을 정의하세요.
작게 쪼개서 병합하세요. 리뷰가 몇 분 안에 가능해야 합니다:
이렇게 하면 실질적인 리뷰가 가능해집니다.
자신감은 증거가 아닙니다—증거를 신뢰하세요. 테스트를 실행하고, 잘못된 입력을 시도하고, 권한 경계를 검증하세요. AI의 일반적 함정도 확인하세요: 누락된 인증 검사, 안전하지 않은 쿼리 빌딩, 약한 토큰 처리, 에러를 숨기는 동작 등.
‘아무거나 업데이트’ 대신 명시적 전환 엔드포인트를 선호하세요. 예: submit, approve, reject, pay 같은 엔드포인트를 만들고 각 전환을 누가 할 수 있는지와 금지된 전환을 테스트로 강제하세요.
AI로 생성된 diff를 후보에게 주세요. 불명확한 네이밍, 빠진 테스트, 엣지 케이스, 작은 보안 실수 같은 현실적 문제들을 포함하세요. 의도를 평이한 언어로 설명하게 하고, 위험이 큰 부분을 찾아 고치고 추가할 테스트를 설계하게 하세요.
계획 먼저 → 작은 단위로 생성 → 위험한 변경 전 스냅샷 → 검증 실패 시 롤백의 루프를 따르세요. 채팅 기반 플랫폼 기능(플래닝 모드, 스냅샷, 롤백)이 있다면 이 루프와 잘 맞습니다.