AI에게 복잡한 개발 작업을 어떻게 맡겨야 할까?

fever·2026년 8월 18일

개발고찰

목록 보기
10/11

AI에게 복잡한 개발 작업을 어떻게 맡겨야 할까?

AI에게 간단한 개발 작업을 맡기는 것은 어렵지 않다. 수정할 위치와 해야 할 일이 명확하다면 한두 줄의 요청만으로도 꽤 정확하게 작업한다.

OrderService의 중복 검증 로직 수정해줘.

문제는 기능 단위의 개발을 맡기기 시작하면서 발생했다.

회원 탈퇴 기능 만들어줘.

사람이 보기에는 별문제 없는 요구사항처럼 보이지만 실제로 구현하려면 결정해야 할 것이 많다. 회원 데이터를 실제로 삭제할 것인지, 상태값만 변경할 것인지, 탈퇴 일시는 저장할 것인지, 재가입은 허용할 것인지, 기존 게시글과 소셜 계정은 어떻게 처리할 것인지 같은 것들이다.

이런 내용을 알려주지 않으면 AI가 알아서 판단해야 한다. 그리고 AI가 판단해야 하는 부분이 많아질수록 내가 생각했던 결과와 멀어질 가능성도 커진다.

결국 AI에게 복잡한 개발 작업을 맡기려면 두 가지가 필요했다.

정확한 지시와 그 지시를 유지할 수 있는 기록

단순히 프롬프트를 잘 작성하는 것만으로는 부족했다.


정확한 지시만으로는 부족했다

처음에는 해결 방법이 간단하다고 생각했다. AI에게 더 자세하게 설명하면 된다.

요구사항을 자세하게 전달하기

회원 탈퇴 기능을 구현해줘.

회원 데이터는 삭제하지 않고 상태를 WITHDRAW로 변경한다.
탈퇴 일시를 저장한다.
기존 게시글은 유지한다.
소셜 계정 연결 정보는 비활성화한다.
관리자에서는 탈퇴 회원을 조회할 수 있어야 한다.

처음보다 결과는 확실히 좋아진다. AI가 임의로 판단해야 하는 부분이 줄어들기 때문이다.

하지만 실제 개발에서 요구사항은 한 번에 완성되지 않는다. 개발하다 보면 "탈퇴 사유도 저장해야 할 것 같은데?", "관리자가 계정을 복구할 수 있어야 하지 않을까?", "탈퇴 이력은 별도 테이블로 관리하는 게 낫겠다." 같은 요구사항이 계속 추가된다. 기존에 정했던 내용이 취소되거나 완전히 다른 방향으로 변경되는 경우도 있다.

최초 요구사항
    ↓
추가 요구사항
    ↓
설계 변경
    ↓
기존 요구사항 수정
    ↓
추가 구현

이런 과정이 반복되면 최초 요구사항부터 추가 요구사항, 설계 변경까지 모든 결정이 대화 안에 쌓이게 된다.

처음에는 AI가 이전 대화를 참고하면 된다고 생각했다. 하지만 작업이 길어질수록 어떤 내용이 아직 유효한지 확인하기 어려워지고, 새로운 세션에서 작업을 이어가려면 현재까지 결정한 내용을 다시 설명해야 했다.

결국 문제가 하나 보이기 시작했다.

우리가 무엇을 만들기로 했는지가 대화에만 남아 있다.


결국 문서가 필요했다

복잡한 개발일수록 요구사항을 정확하게 전달해야 한다. 그리고 그 요구사항은 지금 이 대화에서만 유효한 것이 아니라 다음 작업에서도 다시 사용할 수 있어야 한다.

가장 먼저 생각할 수 있는 방법은 기능별 Markdown 문서를 만드는 것이다.

docs/
├── login.md
├── member-withdraw.md
├── social-login.md
└── admin-member.md

member-withdraw.md에 현재까지 결정된 요구사항을 정리하고 AI에게 다음처럼 요청할 수 있다.

docs/member-withdraw.md를 기준으로 구현해줘.

대화로만 작업할 때보다 훨씬 안정적이다. 세션이 바뀌어도 같은 문서를 다시 읽히면 되고, 사람도 현재까지 결정된 내용을 다시 확인할 수 있다.

그런데 문서가 하나둘 늘어나면서 다른 문제가 생긴다. 어떤 문서는 현재 시스템을 설명하고 있고, 어떤 문서는 앞으로 구현할 기능을 설명하고 있다. 이미 개발이 끝났는데 문서에는 여전히 앞으로 개발해야 할 기능처럼 적혀 있을 수도 있다.

예를 들어 다음 두 문장은 비슷해 보이지만 의미가 전혀 다르다.

현재 로그인 실패 제한은 5회다.

로그인 실패 제한을 5회에서 10회로 변경한다.

첫 번째는 현재 시스템의 상태이고 두 번째는 앞으로 적용해야 할 변경사항이다.

단순히 Markdown 파일을 만들어 쌓아두는 것만으로는 이런 상태와 변경 과정을 관리하기 어렵다. 문서가 필요한 것은 맞았지만, 문서를 남기는 것만으로는 부족했다.

필요했던 것은 현재 시스템의 상태와 앞으로 적용할 변경사항을 구분해서 관리하는 방법이었다.


Spec을 기준으로 개발하기

이런 문제를 해결하는 접근 중 하나가 Spec-Driven Development, SDD다.

Spec-Driven Development

일반적인 AI 개발 흐름을 단순하게 표현하면 다음과 같다.

요구사항 → AI → Code

SDD에서는 중간에 Spec이 들어간다.

요구사항 → Spec → AI → Code

단순히 문서 하나가 추가된 것처럼 보이지만 중요한 차이가 있다. AI가 지금까지 나눈 대화를 기준으로 구현하는 것이 아니라 명시적으로 정의된 Spec을 기준으로 구현한다는 것이다.

"내가 지금까지 AI에게 뭐라고 했지?"를 찾아가는 대신 "현재 정의된 요구사항이 무엇이지?"를 확인할 수 있게 된다.

Spec은 개발이 끝난 뒤 작성하는 문서가 아니다

여기서 말하는 Spec은 개발이 끝난 뒤 정리하는 문서와는 조금 다르다.

일반적인 문서화가 다음과 같다면,

구현 → 문서 작성

Spec-Driven Development에서는 반대 방향으로 사용한다.

Spec 작성 → 구현 → 검증

Spec이 구현의 입력값이 되고, 구현이 끝난 뒤에는 결과가 올바른지를 판단하는 기준이 된다.

이런 방식으로 AI 개발을 해볼 방법을 찾다가 알게 된 도구가 OpenSpec이었다.


OpenSpec은 무엇일까?

OpenSpec은 AI 코딩 에이전트와 함께 Spec-Driven Development 방식으로 개발할 수 있도록 도와주는 도구다.

처음 보면 여러 Markdown 파일을 만들어주는 도구처럼 보이지만 핵심은 파일 생성 자체가 아니다. 현재 시스템의 상태와 앞으로 적용할 변경사항을 분리하고, AI가 그 정보를 기준으로 작업할 수 있도록 개발 과정을 구조화한다.

OpenSpec을 이해하기 위해서는 먼저 SpecsChanges의 차이를 알아야 한다.

Specs와 Changes

OpenSpec의 구조를 단순하게 보면 다음과 같다.

openspec/
├── specs/
└── changes/

둘의 역할은 명확하다.

Specs   = 현재 시스템은 어떻게 동작해야 하는가?
Changes = 이번 작업에서 무엇을 변경할 것인가?

예를 들어 현재 인증 시스템이 이메일 로그인을 지원하고 있다고 해보자.

openspec/
└── specs/
    └── auth/
        └── spec.md

auth/spec.md에는 현재 인증 시스템이 만족해야 하는 요구사항이 정의되어 있다.

여기에 소셜 로그인을 추가한다고 해서 기존 Spec을 바로 수정하지 않는다. 먼저 새로운 Change를 만든다.

openspec/
├── specs/
│   └── auth/
│       └── spec.md
│
└── changes/
    └── add-social-login/

현재 인증 시스템 전체를 다시 정의하는 것이 아니라 add-social-login이라는 변경 단위에서 이번 작업을 별도로 관리하는 것이다.

이 구조를 이해하면 OpenSpec이 단순한 요구사항 문서와 무엇이 다른지 조금씩 보이기 시작한다.


OpenSpec의 핵심 개념

OpenSpec을 제대로 사용하려면 Capability, Spec, Change, Delta, Artifact의 관계를 이해하는 것이 좋다. 처음에는 용어가 많아 보이지만 각각의 역할을 나누어 보면 어렵지 않다.

Capability

Capability는 시스템이 제공하는 기능 단위다.

예를 들어 인증, 회원, 주문, 결제 등을 각각 하나의 Capability로 볼 수 있다.

specs/
├── authentication/
│   └── spec.md
├── member/
│   └── spec.md
└── order/
    └── spec.md

Java 패키지나 클래스 구조를 그대로 옮기는 개념이라기보다 시스템이 어떤 기능을 제공하는지를 기준으로 나누는 것이 핵심이다.

Spec

Spec은 특정 Capability가 현재 어떻게 동작해야 하는지를 정의한다.

예를 들어 authentication Spec에는 다음과 같은 요구사항이 들어갈 수 있다.

사용자는 이메일과 비밀번호로 로그인할 수 있다.

로그인에 5회 연속 실패하면 계정이 잠긴다.

중요한 것은 Spec이 과거 작업 기록이 아니라는 점이다.

현재 시스템이 만족해야 하는 기준

이라고 생각하면 이해하기 쉽다.

Change

Change는 앞으로 시스템에 적용할 하나의 변경 단위다.

예를 들어 소셜 로그인을 추가한다면 다음과 같은 Change를 만들 수 있다.

changes/
└── add-social-login/

현재 인증 시스템 전체를 다시 설명하는 것이 아니라 소셜 로그인 추가라는 이번 작업에 필요한 내용만 별도로 관리한다.

Delta

그렇다면 기존 Spec에서 무엇이 달라지는지는 어떻게 표현할까? 이때 사용하는 개념이 Delta다.

현재 인증 Spec에 이메일 로그인만 정의되어 있고 이번 Change에서 Google 로그인을 추가한다고 해보자.

전체 인증 Spec을 다시 작성하는 대신 이번 변경으로 추가되는 부분을 Delta로 표현한다.

Current Spec
+
Delta
=
변경 후 Spec

Delta를 사용하면 현재 시스템의 기준은 그대로 유지하면서 이번 작업에서 추가되거나 수정되거나 제거되는 부분만 표현할 수 있다.

Artifact

Change 안에는 실제 개발에 필요한 여러 산출물이 존재한다. 이런 산출물을 Artifact라고 한다.

대표적으로 변경의 목적과 범위를 정의하는 Proposal, 기술적인 결정을 정리하는 Design, 실제 구현 작업을 나누는 Tasks, 기존 Spec에서 무엇이 달라지는지를 나타내는 Spec Delta 등이 있다.

결국 하나의 Change 안에서 다음 내용이 연결된다.

왜 변경하는가
    ↓
무엇을 변경하는가
    ↓
어떻게 구현하는가
    ↓
무엇을 작업하는가
    ↓
기존 시스템에서 무엇이 달라지는가

AI는 이 정보들을 기준으로 실제 구현을 진행한다.


OpenSpec은 어떻게 사용할까?

개념을 이해했다면 실제 프로젝트에서 OpenSpec이 어떻게 AI와 연결되는지도 알아야 한다.

여기서 중요한 점은 OpenSpec 자체가 코드를 구현하는 AI는 아니라는 것이다. Claude Code나 Codex 같은 AI 코딩 도구가 실제 프로젝트를 탐색하고 코드를 수정하며, OpenSpec은 그 AI가 따라갈 Spec과 Workflow를 제공한다.

OpenSpec 초기화

프로젝트에서 OpenSpec을 사용하려면 먼저 초기화한다.

openspec init

초기화를 진행하면 OpenSpec을 어떤 AI 코딩 도구와 함께 사용할 것인지 선택할 수 있다.

openspec init

↓

사용할 AI 도구 선택

↓

Claude Code
Codex
Cursor
...

여기서 선택한 도구에 맞춰 OpenSpec Workflow를 사용할 수 있도록 필요한 Command나 Skill이 설정된다.

개념적으로 보면 다음과 같다.

OpenSpec 초기화
    ↓
사용할 AI 코딩 도구 선택
    ↓
Command / Skill 설정
    ↓
AI 코딩 도구에서 OpenSpec Workflow 실행

따라서 OpenSpec은 Claude Code만을 위한 도구가 아니다. 사용하는 AI 도구에 따라 OpenSpec Workflow를 호출하는 방식이 달라질 수 있다.

OpenSpec CLI와 AI 명령은 다르다

처음 OpenSpec을 사용할 때 헷갈렸던 부분 중 하나가 명령어였다.

다음과 같은 명령은 터미널에서 실행하는 OpenSpec CLI다.

openspec init
openspec list
openspec validate

반면 Claude Code를 선택했다면 다음과 같은 OpenSpec Workflow 명령을 Claude Code 안에서 사용할 수 있다.

/opsx:explore
/opsx:propose
/opsx:apply
/opsx:archive

둘은 실행하는 위치와 역할이 다르다.

터미널
│
├── openspec init
├── openspec list
└── openspec validate


AI 코딩 도구
│
├── Explore
├── Propose
├── Apply
└── Archive

예를 들어 Claude Code에서는 /opsx:* 형태의 Slash Command를 사용할 수 있다. 반면 Codex처럼 Skill을 통해 OpenSpec Workflow를 사용하는 도구도 있기 때문에 명령어 문법 자체보다 OpenSpec이 선택한 AI 도구에 Workflow를 연결해준다는 구조를 이해하는 것이 중요하다.

이 글에서는 이후 흐름을 이해하기 쉽도록 Claude Code의 명령을 기준으로 살펴본다.


OpenSpec의 기본 Workflow

OpenSpec을 사용한다고 처음부터 복잡한 과정을 모두 거쳐야 하는 것은 아니다. 가장 기본적인 흐름은 비교적 단순하다.

Explore (선택)
    ↓
Propose
    ↓
Apply
    ↓
Archive

각 단계에서 무엇을 하는지 살펴보자.

Explore - 구현하기 전에 탐색하기

요구사항이 아직 명확하지 않다면 바로 코드를 수정하지 않고 먼저 탐색할 수 있다.

/opsx:explore

Explore에서는 기존 프로젝트 구조와 관련 기능을 살펴보고 요구사항을 구체화한다. 예를 들어 소셜 로그인을 추가하려고 한다면 현재 인증 구조가 어떻게 되어 있는지, 기존 회원과 소셜 계정을 어떻게 연결할 수 있을지 등을 AI와 함께 살펴볼 수 있다.

중요한 점은 아직 구현 단계가 아니라는 것이다. 말 그대로 문제를 탐색하고 방향을 정하는 단계다.

또한 Explore는 반드시 거쳐야 하는 단계는 아니다. 이미 요구사항과 구현 범위가 명확하다면 바로 Change를 준비할 수 있다.

Propose - 변경사항 정의하기

개발할 방향이 정해졌다면 Change를 준비한다.

/opsx:propose

Propose에서는 이번 작업을 위한 Change와 필요한 Artifact를 준비한다. 왜 이 기능이 필요한지, 무엇을 변경할 것인지, 기존 시스템의 어떤 Capability가 영향을 받는지, 구현을 위해 어떤 작업이 필요한지 등을 정리한다.

요구사항
    ↓
Propose
    ↓
Change + Artifacts
    ↓
개발자 검토

여기서 중요한 점은 바로 구현하지 않는다는 것이다.

AI가 요구사항을 잘못 이해했다면 코드가 만들어진 뒤 수정하는 것이 아니라 이 단계에서 먼저 방향을 바로잡을 수 있다.

Apply - 정의된 내용을 구현하기

Change의 내용에 문제가 없다면 실제 구현을 시작한다.

/opsx:apply

이제 AI는 마지막 프롬프트 하나만 보고 코드를 작성하는 것이 아니라 Change에 정의된 요구사항과 설계, 작업 내용을 기준으로 프로젝트를 수정한다.

Change
    ↓
Artifacts
    ↓
Apply
    ↓
Code

내가 OpenSpec을 사용하면서 가장 중요하다고 느낀 부분도 여기에 있다.

프롬프트 기반으로 작업할 때는 대화가 구현의 기준이었다면 OpenSpec에서는 현재 Change가 구현의 기준이 된다.

Archive - 완료된 변경 정리하기

구현이 완료되고 변경사항에 문제가 없다면 Change를 완료된 작업으로 정리한다.

/opsx:archive

작업이 완료되면 이번 변경사항은 더 이상 앞으로 구현해야 할 요구사항이 아니다. 현재 시스템의 일부가 된다.

OpenSpec은 완료된 Change를 정리하면서 변경된 요구사항을 현재 시스템의 Spec과 연결하고, Change 자체는 완료된 변경 기록으로 남길 수 있게 한다.

결국 기본 Workflow를 명령어가 아니라 개발 과정으로 표현하면 다음과 같다.

문제를 먼저 이해하고
    ↓
무엇을 변경할지 정의하고
    ↓
정의된 내용을 기준으로 구현하고
    ↓
완료된 변경을 정리한다

더 세밀하게 작업하고 싶다면

기본 Workflow만으로도 OpenSpec을 사용할 수 있지만, 모든 작업을 Propose → Apply → Archive로 처리해야 하는 것은 아니다.

조금 더 세밀하게 Change를 관리하고 싶다면 Expanded Workflow를 사용할 수 있다.

예를 들어 Change를 먼저 만들고,

/opsx:new

Artifact를 하나씩 생성하면서 내용을 검토하거나,

/opsx:continue

필요한 Artifact를 빠르게 준비할 수도 있다.

/opsx:ff

구현 이후에는 실제 코드가 Spec과 일치하는지 확인할 수도 있다.

/opsx:verify

따라서 OpenSpec은 하나의 고정된 개발 절차를 강제한다기보다 작업의 크기와 상황에 따라 Workflow를 선택할 수 있도록 구성되어 있다.

간단한 작업이라면 기본 흐름을 사용할 수 있다.

Propose → Apply → Archive

반대로 요구사항을 하나씩 검토하면서 진행해야 하는 작업이라면 좀 더 세밀하게 접근할 수 있다.

New → Continue → Apply → Verify → Archive

결국 중요한 것은 명령을 많이 사용하는 것이 아니라 현재 작업에 필요한 수준만큼 Spec을 만들고 검토하는 것이다.


validateverify는 무엇이 다를까?

OpenSpec을 보다 보면 비슷해 보이는 두 가지 검증 방법이 나온다.

openspec validate

그리고 Expanded Workflow에서 사용할 수 있는

/opsx:verify

이다.

둘은 확인하는 대상이 다르다.

validate는 OpenSpec의 Change나 Spec 자체가 올바르게 작성되었는지를 검사한다. 즉, 필요한 구조와 요구사항 형식이 제대로 작성되어 있는지를 확인하는 쪽에 가깝다.

반면 verify는 실제 구현 결과를 확인하는 단계다. Change에서 정의했던 요구사항과 Tasks를 기준으로 실제 코드가 의도한 대로 구현되었는지를 AI가 확인한다.

간단하게 구분하면 다음과 같다.

validate
Spec이 올바르게 작성되었는가?

verify
코드가 Spec대로 구현되었는가?

문서를 검사하는 것과 구현 결과를 검사하는 것은 서로 다른 문제이기 때문에 둘의 역할도 나뉜다.


결국 달라지는 것은 개발의 기준이다

OpenSpec을 사용한다고 AI의 코딩 능력이 갑자기 좋아지는 것은 아니다. 같은 Claude Code나 Codex를 사용하고 같은 모델이 코드를 작성한다.

달라지는 것은 AI가 무엇을 기준으로 개발하는가다.

프롬프트만으로 개발하면 작업의 기준이 대화에 존재한다.

대화 → AI → Code

작업이 길어질수록 대화도 길어지고 요구사항도 여러 곳에 흩어진다. 어떤 요구사항이 아직 유효한지, 어떤 결정이 취소됐는지를 다시 해석해야 한다.

Spec을 중심으로 개발하면 그 기준을 프로젝트 안으로 옮길 수 있다.

대화
  ↓
Change / Spec
  ↓
AI
  ↓
Code

그리고 구현이 끝나면 이번 변경은 현재 시스템의 새로운 기준이 된다.

요구사항
    ↓
Change
    ↓
구현
    ↓
현재 시스템의 Spec

AI에게 일을 잘 시키려면 정확한 지시가 필요하다. 하지만 복잡한 개발에서는 그것만으로 부족했다.

오늘 결정한 내용을 내일도 사용할 수 있어야 하고, 요구사항이 변경되면 무엇이 바뀌었는지 알 수 있어야 한다. 새로운 세션에서도 같은 기준으로 작업할 수 있어야 하고, 구현이 끝난 뒤에는 처음 정의했던 내용과 실제 결과를 비교할 수도 있어야 한다.

결국 AI에게 복잡한 개발 작업을 맡기면서 필요했던 것은 더 길고 정교한 프롬프트가 아니었다.

무엇을 만들 것인지 정확하게 정의하고, 그 정의를 기록으로 남기고, 구현이 끝날 때까지 같은 기준으로 관리하는 것.

OpenSpec은 그 과정을 AI 개발 Workflow 안으로 가져오는 방법 중 하나다.

profile
선명한 삶을 살기 위하여

0개의 댓글