Brownfield 프로젝트에서의 SDD: 장단점 및 실제로 작동하는 전략
요약
기존의 복잡한 레거시 코드(Brownfield) 환경에서 명세 주도 개발(SDD)을 적용하여 AI 에이전트의 효율을 높이는 전략을 다룹니다. SDD를 단순한 초기 설계 도구가 아닌, 지속적인 변경 사항을 관리하고 AI의 작업 범위를 명확히 하는 방법론으로 정의합니다.
핵심 포인트
- SDD는 신규 프로젝트뿐만 아니라 기존 레거시 프로젝트의 변경 관리에도 필수적임
- 명세 작성을 통해 블랙박스 상태인 기존 시스템의 동작을 명확히 문서화할 수 있음
- 명세서의 '범위 외' 섹션은 AI 에이전트의 무분별한 코드 수정을 방지함
- 명확한 명세는 AI 활용 시 발생할 수 있는 회귀 오류를 줄여줌
SDD: 장단점 및 실제로 작동하는 전략
저에게 매우 익숙한 유형의 개발자가 있습니다.
AI 튜토리얼을 켭니다. 어떤 사람이 20분 만에 앱을 처음부터 구축하는 것을 봅니다. 흥분합니다. 그리고 자신의 실제 프로젝트를 엽니다. 그리고 눈앞에 있는 것이 방금 본 것과는 전혀 상관이 없다는 사실을 깨닫습니다.
왜냐하면 그의 프로젝트는 잘 정리된 폴더가 있는 빈 저장소 (repo)가 아니기 때문입니다.
그의 프로젝트는 누가 작성했는지 아무도 모르는 헬퍼 (helpers) 함수들에 비즈니스 로직 (business logic)이 파묻혀 있는 4년 된 모놀리스 (monolith)입니다. 47개의 컬럼이 있고 그중 어느 것에도 주석이 없는 데이터베이스 테이블이 있습니다. 코드의 어느 부분에 있느냐에 따라 같은 일을 수행하는 세 가지 서로 다른 방식이 존재합니다. 3,000줄에 달하는 utils라는 이름의 폴더가 있습니다.
이것이 바로 브라운필드 (brownfield) 프로젝트입니다.
그리고 AI는 그것을 알아서 정리해주지 않습니다.
하지만 당신이 할 수 있는 일이 하나 있습니다. 그리고 그것은 생각보다 더 잘 작동합니다.
첫째, 진짜 문제
사람들이 명세 주도 개발 (Spec-Driven Development, SDD)을 발견했을 때 가장 먼저 드는 생각은 이렇습니다: "좋네요, 하지만 그건 새로운 프로젝트를 위한 것이잖아요."
논리적인 추론입니다. SDD는 시작하기 전에 명세 (spec)를 작성하는 것에 대해 이야기합니다. 브라운필드에서는 이미 몇 년 전에 시작했습니다. 배는 이미 물 위에 떠 있습니다. 도면을 그리기 위해 항구로 돌아갈 수는 없습니다.
그 논리에는 결함이 있습니다.
SDD는 프로젝트를 위한 방법론이 아닙니다. 변경을 위한 방법론입니다.
당신이 어떤 프로젝트에서 일하든, 당신은 변경을 할 것입니다. 오늘, 내일, 다음 주에 말이죠. 기능을 추가할 때마다, 버그를 수정할 때마다, 다른 사람이 만들었지만 당신이 완전히 이해하지 못하는 무언가를 건드릴 때마다 말입니다.
그 지점이 바로 SDD가 개입하는 정확한 위치입니다. 프로젝트의 시작점이 아니라, 지금부터 당신이 수행하는 모든 변경 사항에서 말입니다.
브라운필드에서 SDD가 실제로 해결하는 것
시스템이 더 이상 블랙박스 (black box)가 아니게 됩니다.
이미 존재하는 것에 대해 명세를 작성할 때, 첫 번째 단계는 그것이 _어떻게 작동해야 하는지_가 아니라, _현재 어떻게 작동하고 있는지_를 기술하는 것입니다. 어떻게 작동하는지 말입니다.
그 연습만으로도 이미 엄청난 가치를 지닙니다.
많은 팀이 아무도 정확하게 설명할 수 없는 시스템을 수년 동안 운영해 오고 있습니다. 직관은 있습니다. 역사적 기억도 있습니다. "이렇게 작동하는 것 같긴 한데 확실하지는 않아요"라는 말도 있습니다. 명세서 (Spec)는 이러한 안개를 텍스트로 변환하도록 강제합니다.
그리고 그 텍스트는 남습니다. 텍스트는 버전 관리 (Versioning)가 됩니다. 텍스트는 에이전트 (Agent)가 읽을 수 있습니다.
에이전트에게는 경계가 생깁니다.
AI를 활용한 Brownfield 프로젝트의 전형적인 문제는 이렇습니다. 에이전트에게 한 가지를 변경해 달라고 요청했는데 다섯 가지를 바꿔 놓는 것입니다. 에이전트가 무엇을 건드려도 되는지, 무엇을 건드리면 안 되는지 모르기 때문입니다.
"범위 외 (Out of scope)" 섹션이 포함된 명세서는 이 문제를 근본적으로 해결합니다.
에이전트는 추측하지 않습니다. 주어진 대로 실행합니다. 이번 변경 사항에 포함되지 않는 것이 무엇인지 명시적으로 기술된 명세서를 제공하면, 에이전트는 그것을 건드리지 않습니다. 에이전트가 똑똑해서가 아니라, 지침 (Instructions)이 있기 때문입니다.
회귀 (Regression)가 줄어듭니다.
완전히 사라지지는 않습니다. 하지만 줄어듭니다.
만약 명세서가 변경이 완료된 후에도 유지되어야 하는 동작을 기술하고 있다면, 에이전트는 자신의 작업물을 검증할 기준을 갖게 됩니다. "내가 구현한 것이 명세서에 기술된 내용을 충족하는가?" 이는 에이전트가 스스로 답할 수 있는 질문입니다.
명세서가 없다면 에이전트는 기준이 없습니다. 직관만 있을 뿐입니다. 완전히 이해하지 못하는 레거시 코드 (Legacy code)에서 에이전트의 직관은 3주 후에 나타날 버그의 끝없는 근원이 됩니다.
지식이 축적됩니다.
이 점이 가장 과소평가되는 부분입니다.
당신이 작성하는 각각의 미니 명세서 (Mini-spec)는 저장소 (Repo)에 남는 문서가 됩니다. Notion에 남는 것이 아닙니다. Confluence에 남는 것도 아닙니다. 팀에서 가장 오래 근무한 개발자의 머릿속에 남는 것도 아닙니다. 그것을 설명하는 코드와 함께 저장소에 남습니다.
충분히 잘 명세화된 변경 사항들이 쌓이면, 시스템은 기억을 갖기 시작합니다. 가장 중요한 모듈에는 명세서가 있고, 가장 자주 사용되는 흐름이 기술되어 있습니다. 누군가 계획하지 않아도 신규 인원의 온보딩 (Onboarding)이 개선됩니다.
이는 이 방법론의 부수적인 효과입니다. 하지만 가장 가치 있는 효과 중 하나입니다.
SDD가 해결하지 못하는 것
여기서는 솔직해져야 합니다.
구조가 잘못된 코드는 여전히 나쁩니다.
스펙 (Spec)이 리팩터링을 해주는 것은 아닙니다. 세 개의 서로 다른 레이어에 부작용 (Side effects)이 있는 800줄짜리 모듈이 있다면, 스펙은 변경 범위를 좁히는 데 도움을 줄 뿐입니다. 하지만 모듈 자체는 여전히 엉망인 상태로 남습니다.
SDD는 필요할 때 청소 작업을 하지 않아도 된다는 핑계가 아닙니다. SDD는 안전하게 변경하기 위한 도구입니다. 이 둘은 같은 것이 아닙니다.
테스트가 없다면, 스펙은 허공에 떠 있는 것과 같습니다.
스펙은 기대되는 동작 (Expected behavior)을 기술합니다. 하지만 그 동작이 실제로 구현되었는지 자동으로 검증할 수 있는 것이 없다면, 스펙은 단지 텍스트일 뿐입니다.
테스트와 스펙은 상호 보완적입니다. 스펙은 무엇이 참이어야 하는지를 말해주고, 테스트는 그것이 실제로 참인지 확인합니다. 스펙은 있지만 테스트가 없다면, 시스템의 절반만 가진 것입니다. 사고를 위한 용도로는 유용할지 모르나, 무언가 깨졌을 때 알려주는 절반의 기능은 없는 셈입니다.
거대한 시스템을 사후에 문서화하는 것은 엄청난 작업입니다.
여기에는 지름길이 없습니다. 5년 된 모놀리스 (Monolith) 시스템이 있고, 아무것도 건드리기 전에 모든 것에 대한 스펙을 갖추고 싶다면 몇 달이 걸릴 것입니다. 그리고 아마 영원히 끝내지 못할 수도 있습니다. 스펙을 쓰는 동안 비즈니스에서는 계속해서 새로운 기능 (Features)을 요구할 것이기 때문입니다.
해결책은 모든 것을 한꺼번에 명세화 (Specify)하려고 시도하는 것이 아닙니다. 해결책은 아예 시도하지 않는 것입니다.
실제로 작동하는 전략
시스템을 명세화하지 마세요. 변경 사항을 명세화하세요.
이것이 프로젝트가 마비되느냐, 아니면 앞으로 나아가느냐의 차이를 만듭니다.
워크플로우는 다음과 같습니다:
1. 정확히 무엇을 건드릴지 식별하세요
시스템 전체가 아닙니다. 모듈의 일부만 변경하면 된다면 모듈 전체도 아닙니다. 독립적으로 수행하는 것이 의미 있는 최소 작업 단위 (Minimum unit of work)를 찾으세요.
범위가 작을수록 명세화하기 쉬워집니다. 그리고 명세화하기 쉬울수록 실제로 수행할 가능성도 높아집니다.
2. 원하는 상태보다 현재 상태를 먼저 작성하세요
이 단계는 가장 많은 사람들이 건너뛰는 단계입니다. 그리고 브라운필드 (Brownfield) 프로젝트에서는 가장 중요한 단계입니다.
시스템이 수행하기를 원하는 내용을 작성하기 전에, 현재 시스템이 무엇을 하고 있는지 작성하세요. 정확하게 말이죠. 단순히 "폼 데이터를 검증합니다"라고 적지 마세요. 무엇을 검증하는지, 언제 검증하는지, 실패했을 때 어떤 일이 발생하는지, 그리고 검증해야 함에도 불구하고 검증하지 않는 것은 무엇인지 작성해야 합니다.
이 연습은 두 가지 효과를 가집니다. 첫째, 실제로 코드를 수정하기 전에 수정하려는 대상을 진정으로 이해하도록 강제합니다. 둘째, 에이전트 (Agent)에게 이미 작동하고 있는 기능을 망가뜨리지 않기 위해 필요한 컨텍스트 (Context)를 제공합니다.
## 현재 상태
- 폼은 클라이언트에서만 이메일 형식을 검증함 (기본 regex)
- 필드가 비어 있는 상태에서 사용자가 제출(submit)하면 눈에 보이는 변화가 없음
...
"범위 외 (out of scope)" 섹션은 선택 사항이 아닙니다. 그것이 바로 이미 작동하고 있는 작업을 보호하는 장치입니다.
3. 에이전트에게 풍부한 컨텍스트가 아닌, 정확한 컨텍스트를 제공하세요
가장 흔한 실수는 리포지토리 (Repo) 전체를 컨텍스트에 붙여넣고 에이전트가 무엇이 중요한지 이해하기를 기대하는 것입니다.
에이전트는 잘못 필터링된 전체 컨텍스트보다, 잘 선택된 적은 양의 컨텍스트로 작업할 때 더 잘 작동합니다.
변경 사항에 직접적으로 영향을 미치는 파일들만 제공하세요. 폼 컴포넌트, 서버 엔드포인트 (Endpoint), 만약 존재한다면 검증 스키마 (Validation schema). 그 외에는 아무것도 필요 없습니다.
4. 직관이 아닌 스펙 (Spec)에 따라 검증하세요
에이전트의 작업이 끝나면 질문은 "괜찮아 보이는가?"가 아니라, "스펙에 명시된 대로 작동하는가?"가 되어야 합니다.
이것이 기준을 가지고 검토하는 것과 믿음으로 검토하는 것의 차이입니다.
만약 스펙에 기술된 내용 중 구현되지 않은 부분이 있다면, 그 구체적인 차이점을 가지고 에이전트에게 돌아가세요. "이것은 잘못되었습니다"라고 말하는 것이 아니라, "스펙은 X라고 말하는데 코드는 Y를 수행합니다"라고 말해야 합니다.
5. 스펙은 리포지토리에 남겨두세요
변경이 완료되었다고 해서 스펙을 삭제하지 마세요. 그것은 임시 문서가 아닙니다. 시스템의 기억입니다.
6개월 뒤에 해당 모듈을 건드릴 사람 — 아마 당신일 가능성이 높습니다 — 은 그 파일을 열어보고, 그렇지 않았다면 코드 고고학 (Code archaeology)에 2시간을 허비했을 내용을 2분 만에 이해하게 될 것입니다.
누적된 효과
이것이 브라운필드 (Brownfield) 환경에서 이 방법론이 가진 가장 매력적인 부분입니다.
문서화를 위한 별도의 스프린트가 필요하지 않습니다. "기술 부채 (technical debt)"를 갚기 위해 모든 것을 멈출 필요도 없습니다. 앞으로 나아가기 전에 리팩터링 (refactoring)을 해야 한다고 누군가를 설득할 필요도 없습니다.
단지 지금부터 당신이 하는 모든 변경 사항이 명세 (spec)를 동반한 변경 사항이 되도록 하기만 하면 됩니다.
그게 전부입니다.
시간이 흐름에 따라, 불투명했던 시스템은 점차 명확한 영역을 갖기 시작합니다. 가장 자주 수정되는 모듈이 가장 많은 명세를 갖게 됩니다. 이는 결과적으로 가장 가치 있는 지식, 즉 3년 동안 한 번도 건드리지 않은 부분이 아니라 변화가 일어나는 부분에 대한 지식이 됩니다.
브라운필드 (Brownfield) 환경이 사라지는 것은 아닙니다. 하지만 해결 불가능한 문제로 남지는 않습니다.
시스템은 성장함에 따라 변경 사항이 발생할 때마다 스스로 문서화되는 시스템으로 변모합니다.
마지막으로 한 가지
여기서 설명하는 방법론 — 변경 전 명세 작성, 희망 상태 이전의 현재 상태 파악, 에이전트 (agent)에게 정확한 컨텍스트 제공, 명세에 따른 검증 — 은 제가 강의에서 가르치는 내용의 핵심입니다.
저는 이를 데모가 아닌 실제 프로젝트에서 Claude Code와 함께 작업하며 구축했습니다. 그리고 여기서 말하는 것과 정확히 일치하는 유형의 프로젝트, 즉 문서화가 되어 있지 않고 아무도 내렸던 결정을 기억하지 못하는 레거시 코드 (legacy code)에 이를 적용해 왔습니다.
이것이 실제 프로젝트에 어떻게 적용되는지 확인하고 싶다면, 상세히 다루는 강의가 있습니다.
Bezael Pérez — DominiCode
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기