# AI 에이전트가 검증할 수 있는 인수 기준을 쓰는 법

Canonical: https://specscore.md/ko/articles/acceptance-criteria/

2026년 11월 3일 업데이트.

[인수 기준](https://specscore.md/acceptance-criteria-specification)(AC, acceptance criteria)은 변경이 완료로 치려면 참이어야 하는 조건입니다. Given/When/Then은 [Gherkin](https://cucumber.io/docs/gherkin/reference/) 안의 단계 형태입니다. 이 언어를 [Cucumber](https://cucumber.io/docs/bdd/)는 행위 주도 개발(BDD, behaviour-driven development)에 씁니다. Given은 시작 상태, When은 동작이 하나 일어나는 것, Then은 그에 이어 관찰할 수 있는 결과입니다. 제품 요구사항 문서(PRD, product requirements document)는 문제와 의도한 제품을 이름으로 적는 산문 요약입니다. [스펙 주도 개발](https://specscore.md/ko/spec-driven-development/)(SDD, spec-driven development)은 그 산문만을 정본으로 두지 않고, 구조화된 명세를 구현을 대조하는 정본으로 둡니다. [EARS](https://alistairmavin.com/ears/)(Easy Approach to Requirements Syntax)는 요구사항 문장을 제약해 절이 고정된 순서로 있게 하는 방법입니다. 지속적 통합(CI, continuous integration)은 제안된 변경마다 도는 자동 검사입니다. 이 사이트는 SpecScore의 두 검사를 [검증기 페이지](https://specscore.md/ko/validators/)에서 설명합니다. 이 페이지는 인공지능(AI) 코딩 에이전트가 검증할 수 있는 인수 기준을 쓰는 방법과 인수 기준 예시, 그리고 SpecScore 명령줄 인터페이스(CLI, command-line interface)가 실제로 검사하는 것을 보여 줍니다.

## 인수 기준이란

인수 기준은 「무엇이 참이어야 하는가」에 답합니다. 리뷰어, 테스트, 또는 에이전트가 받아들이거나 거절할 수 있는 조건을 이름 붙입니다. 호출할 함수나 고칠 파일은 이름 붙이지 않습니다. 그 선택은 구현에 속합니다.

요구사항은 규칙입니다. SpecScore [요구사항 명세](https://github.com/specscore/specscore/blob/main/spec/features/requirement/README.md)에서 요구사항은 기능 안의 `#### REQ:` 제목입니다. 의무가 하나이고, 식별자가 따로 있습니다. 인수 기준은 관련된 요구사항을 하나의 조건으로 묶습니다. 시나리오는 그 조건이 성립함을 보여 주는 구체적인 사례 하나입니다. [인수 기준 명세](https://specscore.md/acceptance-criteria-specification)는 이렇게 말합니다. 기준은 「무엇이 참이어야 하는가」에 답하고, 시나리오는 「어떻게 증명하는가」에 답합니다.

PRD는 같은 의도를 문단에 담을 수 있습니다. 문단은 사람에게 도움이 됩니다. 에이전트는 어느 문장이 통과와 실패의 시험인지 여전히 짐작해야 합니다. 기준을 그 자체로 한 문장에 쓰면 그 짐작이 사라집니다.

## 인수 기준을 검증 가능하게 만드는 것

AI 코딩 에이전트가 기준을 따를 수 있는 때는, 나중에 읽는 사람이 증거를 가리키며 통과 또는 실패라고 말할 수 있을 때뿐입니다. 다섯 가지 습관이 그것을 가능하게 합니다. 어느 것도 문장 틀이 아닙니다. Given/When/Then과 체크리스트를 포함해, 어떤 틀에도 적용할 수 있는 검사입니다.

### 관찰할 수 있음

결과는 외부의 사람이 볼 수 있는 것입니다. 페이지의 메시지, 저장되었거나 저장되지 않은 기록, 리다이렉트, 거절된 양식입니다. 「시스템은 구매자를 이해한다」는 관찰할 수 없습니다. 「페이지가 이메일 칸 옆에 Enter a valid email address를 보여 준다」는 관찰할 수 있습니다.

### 구체적

행위자, 입력, 결과를 이름 붙입니다. 「잘못된 입력은 처리된다」는 어느 입력인지, 어느 처리인지 말하지 않습니다. 「구매자가 이메일 not-an-email을 제출한다」는 말합니다. 구체적이라는 것은 운영의 값을 모두 붙여 넣는다는 뜻이 아닙니다. 읽는 사람이 시험용 데이터를 만들어 무엇이 일어나야 하는지 알 수 있다는 뜻입니다.

### 행위 하나

기준 하나에 행위 하나입니다. 「구매자는 로그인하고, 비밀번호를 재설정하고, 잠금 메시지를 볼 수 있다」는 한 문장 안의 기준 셋입니다. 첫 번째를 구현하고 세 번째를 뺀 에이전트도 그 문장이 끝났다고 말할 수 있습니다. 나누세요.

### 통과 또는 실패

검증 가능한 인수 기준에는 결과가 둘입니다. 「양식은 빠르게 느껴져야 한다」는 정도의 문제이지 결과가 아닙니다. 한계가 필요하면 한계를 쓰세요. 「체크아웃 페이지는 서버로 추가 요청 없이 오류를 보여 준다.」 실패가 어떻게 보이는지 말할 수 없으면, 그 문장은 아직 기준이 아닙니다.

### 모호한 말을 두지 않음

결과를 숨기는 말을 빼세요. 우아한, 직관적인, 견고한, 사용자에게 편한, 매끄러운, 빠른, 해야 하는, 적절한. 각각을 받아들일 증거로 바꾸세요. 「오류를 우아하게 처리한다」는 「칸 옆에 Enter a valid email address를 보여 주고, 주문을 넣지 않는다」가 됩니다.

## Given/When/Then과 체크리스트

팀이 실제로 남겨 두는 인수 기준 예시의 대부분은 두 형태로 충분합니다. Given/When/Then은 시나리오입니다. 체크리스트는 결과의 목록입니다. 상태, 동작, 결과의 순서가 중요하면 시나리오를 쓰세요. 한 동작에서 서로 독립인 결과를 나열할 때는 체크리스트를 쓰세요.

Cucumber의 [Gherkin 레퍼런스](https://cucumber.io/docs/gherkin/reference/)는 단계에 Given, When, Then, And, But이라는 키워드를 줍니다. 레퍼런스는 영어를 씁니다. Gherkin에는 그 키워드의 번역도 있습니다. 키워드 뒤의 말은 Cucumber가 파일을 실행할 때 step definition이라는 코드에 맞춰집니다. SpecScore는 그 step definition 층을 쓰지 않습니다. 아래 예는 Gherkin이며, 형태가 익숙해 보이도록 둔 것입니다. SpecScore 파일이 아닙니다.

### Given/When/Then

```gherkin
Scenario: Reject an email that is not a valid address
  Given the shopper is on the checkout form
  When the shopper submits the email "not-an-email"
  Then the page shows "Enter a valid email address" beside the email field
  And the order is not placed
```

### 체크리스트

```markdown
- Submitting the email "not-an-email" shows "Enter a valid email address" beside the email field.
- The order is not placed.
```

When은 하나로 두세요. 두 번째 동작은 두 번째 시나리오입니다. And는 같은 키워드를 잇습니다. 또 하나의 Given이거나, 또 하나의 Then입니다. And로 두 번째 행위를 슬쩍 넣지 마세요.

체크리스트는 키워드가 없는 같은 계약입니다. 각 줄은 여전히 관찰할 수 있는 결과 하나입니다. 위의 체크리스트는 시나리오의 Then을 확인 항목으로 쓴 것입니다.

## EARS 요구사항과의 관계

이 문장 패턴은 [EARS 요구사항 구문을 예와 함께 설명](https://specscore.md/ko/articles/ears-requirements/)에 정리되어 있습니다.

[Alistair Mavin의 가이드](https://alistairmavin.com/ears/)는 EARS(Easy Approach to Requirements Syntax)를, 글로 된 요구사항을 부드럽게 제약하는 장치로 정의합니다. 절은 하나의 순서로 있습니다. 가이드가 주는 일반 형태는 While <optional pre-condition>, when <optional trigger>, the <system name> shall <system response>입니다. 요구사항에는 사전 조건이 0개 이상, 트리거가 0개 또는 1개, 시스템 이름이 하나, 시스템 응답이 하나 이상 있습니다.

그 문장은 의무입니다. 시스템이 지켜야 하는 규칙입니다. 인수 기준은 그 의무가 충족된 것으로 치려면 참이어야 하는 조건입니다. Given/When/Then 시나리오는 그 조건의 구체적인 사례 하나입니다. 요구사항을 EARS 문장으로 쓰고도, 기준에는 예시 입력을 넣지 않은 채로 둘 수 있습니다.

SpecScore 기능에서 요구사항은 `#### REQ:` 제목 아래의 EARS풍 문장일 수 있습니다. 기준은 추상적인 조건으로 남습니다. 시나리오가 예시를 가집니다. EARS는 둘 중 어느 것도 대체하지 않습니다. 요구사항 문장만 제약합니다.

## 인수 기준 예시: 모호한 것과 검증 가능한 것

### 증거를 이름 붙이기

**모호함**

```text
The checkout form should handle bad input gracefully and feel fast.
```

**검증 가능**

```text
When the shopper submits the email "not-an-email", the checkout page shows "Enter a valid email address" beside that field and does not place the order.
```

모호한 줄에는 숨은 요구가 둘(오류와 속도) 있고 증거는 없습니다. 검증할 수 있는 줄에는 동작이 하나, 사람이나 에이전트가 검사할 수 있는 결과가 둘 있습니다. 예문은 영어로 둡니다. 형식의 키워드가 영어이기 때문입니다.

### 행위 하나

**모호함**

```text
Users can sign in, reset a password, and see an error if the account is locked.
```

**검증 가능**

```text
When a visitor submits a known email and the matching password, the app sets a session cookie and redirects to /dashboard.
```

로그인, 재설정, 잠금은 행위가 셋입니다. 검증할 수 있는 줄은 로그인만 남깁니다. 재설정과 잠금은 각각 자신의 입력과 결과를 가진 자신의 기준을 받습니다.

### 함수 이름이 아닌 결과

**모호함**

```text
The handler returns 400 from validateEmail().
```

**검증 가능**

```text
When the shopper submits an email that is not a valid address, the checkout page shows "Enter a valid email address" and does not place the order.
```

함수 이름과 상태 코드는 기준을 오늘의 코드에 묶습니다. 검사가 다른 함수로 옮겨도 결과는 그대로 성립합니다. 상태 코드가 약속한 행위 그 자체일 때는 남기고, 관찰할 수 있는 결과로 그렇게 말하세요.

## SpecScore는 이것을 어떻게 저장하고, CI는 무엇을 검사하는가

SpecScore는 형식을 [specscore/specscore](https://github.com/specscore/specscore) 저장소에, CLI를 [specscore/specscore-cli](https://github.com/specscore/specscore-cli)에 공개합니다. 두 문서는 인수 기준을 정확히 같은 형태로 서술하지 않습니다. 둘 다 현재의 것입니다. 이 절은 어느 쪽이 어느 쪽인지 말한 다음, `specscore spec lint`가 실제로 거부하는 것을 말합니다.

그 [인수 기준 명세](https://specscore.md/acceptance-criteria-specification), 곧 그 URL에서 제공되는 기능 README는 기준을 기능 README 안에 둡니다. 모든 기능에 `## Acceptance Criteria` 절이 있습니다. 각 기준은 `### AC: {slug}` 제목입니다. slug는 소문자이고 하이픈으로 잇습니다. 다음 줄은 `**Requirements:**`이며, 묶는 요구사항을 다음 형태로 나열합니다. `{feature-id}#req:{slug}`. 그 뒤에 산문, 곧 합성된 조건이 옵니다. 명세는 그 산문을 추상적으로 두라고 말합니다. Given/When/Then 단계, 예시 입력, 구현 세부 사항을 담아서는 안 됩니다. 기준이 아직 없으면 그 절은 「Not defined yet.」이라고 읽고, Open Questions 절은 「Acceptance criteria not yet defined for this feature.」라고 말합니다. 기준은 선택인 묶음입니다. 명세는, 요구사항이 둘 이상을 묶을 때 하나를 더하고, 요구사항 하나를 되풀이할 뿐일 때는 빼라고 말합니다.

구체적인 증명은 기능의 `_tests/` 디렉터리에 있는 시나리오 파일입니다. [시나리오 명세](https://github.com/specscore/specscore/blob/main/spec/features/scenario/README.md)는 `# Scenario:` 제목, `## Steps` 절, 대문자 `GIVEN`, `WHEN`, `THEN`, `AND`, 그리고 WHEN이 정확히 하나를 요구합니다. 선택인 `**Validates:**` 줄은 시나리오를 `{feature-path}#ac:{slug}` 또는 요구사항에 연결합니다. `## Rehearse` 절은 `rehearse` 펜스 안에 shebang으로 시작하는 실행 가능한 스크립트를 둘 수 있습니다.

CLI는 기준마다 하나인 얇은 기준 파일도 `_acs/<slug>.ac.md`에서 읽습니다. [로그인 예](https://specscore.md/example-login)와 [Rehearse 가이드](https://specscore.md/rehearse)가 이 형태를 문서화합니다. `# AC: <slug>`, 선택인 `**Status:**`, `**Verifies:**`, `**Applies-to:**`(이 셋은 모두 선택), 그리고 `## Statement` 아래의 조건입니다. `specscore rehearse acs <feature-dir>`는 그 파일들에서 요약 표를 출력합니다. `specscore rehearse acs --write`는 기능 README의 Acceptance Criteria 절을 그 표와, 절이 생성되었으며 손으로 고치지 말라는 주석으로 바꿉니다. `# AC:` 제목이 없는 파일은 그 명령의 오류입니다.

이 저장소의 CI는 [dogfood 워크플로](https://github.com/specscore/specscore/blob/main/.github/workflows/dogfood.yml)입니다. CLI v0.39.1을 설치하고, 풀 리퀘스트와 main으로의 푸시에서 `specscore spec lint`를 실행합니다. 2026년 11월 3일에 확인한 바로는, 그 릴리스와 CLI 기본 브랜치가 모두 아래 규칙을 포함합니다. 린터가 검사하는 것은 구조입니다. 영어의 잘잘못은 채점하지 않습니다.

규칙 `ac-heading-format`은 오류입니다. 기능 README(제목이 `# Feature:`인 파일)에서 인수 기준 제목처럼 보이는 줄은 정확히 `### AC: <id>`여야 합니다. 콜론 뒤는 공백 하나, id는 kebab-case입니다. `### AC: <id> (verifies ...)` 형태의 제목도 받습니다. `--fix`는 공백만 고칩니다. 대문자나 밑줄이 있는 id는 보고되고 사람에게 남습니다. 이 규칙은 `**Requirements:**` 줄을 요구하지 않고, 「Not defined yet.」자리표시도 요구하지 않으며, 산문에 Given, When, Then이 있어도 경고하지 않습니다. 그 경고를 위한 명세 자신의 시나리오는 이 CLI의 lint 규칙이 아닙니다.

원본 기능을 이름 붙인 계획에서, 린터는 그 기능의 Acceptance Criteria 절 안에 있는 `### AC:` 제목을 읽습니다. 규칙 `P-002`는 작업의 `**Verifies:**` 값이 그 제목 중 하나에 대한 `<feature-slug>#ac:<ac-slug>`가 아님을 보고합니다. 규칙 `P-001`는 어떤 작업도 다루지 않고 `## Deferred AC Coverage` 아래에도 없는 제목을 보고합니다. 이 두 규칙은 `_acs/*.ac.md`를 열지 않습니다. `rehearse acs --write`의 요약 표에는 `### AC:` 제목이 없으므로, 계획 규칙은 얇은 파일을 보지 않습니다.

같은 lint 명령은 시나리오 단계 키워드를 검사하지 않습니다. `specscore rehearse`는 시나리오 파일을 실행합니다. Rehearse 가이드는 펜스로 감싼 명령 위에 `## Given` 또는 `## When` 같은 제목을 쓰고, `**Use:**`로 재사용할 수 있는 검사를 부릅니다. Given이 하나이고 When 갈래가 여럿인 파일은 서로 다른 사례로 실행됩니다. `specscore rehearse new <feature-slug>#ac:<ac-slug>`는 기능 README를 읽고, 그 기준에서 Given/When/Then 줄을 `spec/features/<feature-slug>/_tests/<ac-slug>.md`로 꺼냅니다. 그 어느 것도 dogfood 워크플로에서는 돌지 않습니다. 워크플로가 실행하는 것은 `specscore spec lint`입니다.

### 인라인 기준, 인수 기준 명세에서

```markdown
## Acceptance Criteria

### AC: invalid-email-rejected

**Requirements:** checkout#req:reject-invalid-email

An email that is not a valid address is rejected, and the order is not placed.
```

### 얇은 기준 파일, rehearse acs가 읽는 형태

```markdown
# AC: invalid-email-rejected

**Status:** accepted
**Verifies:** checkout#req:reject-invalid-email

## Statement

An email that is not a valid address is rejected, and the order is not placed.
```

### 시나리오 파일, 시나리오 명세에서

```markdown
# Scenario: Reject an invalid email

**Validates:** [checkout#ac:invalid-email-rejected](../README.md#ac-invalid-email-rejected)

## Steps

GIVEN the shopper is on the checkout form
WHEN the shopper submits the email "not-an-email"
THEN the page shows "Enter a valid email address" beside the email field
AND the order is not placed
```

### Rehearse 시나리오, Rehearse 가이드에 있는 형태

~~~~markdown
# Rehearse: Reject an invalid email

## Given a submitted email that is not a valid address
```bash
printf 'email=not-an-email\n' > form.txt
```

## When the response is recorded
```bash
printf 'Enter a valid email address\n' > out.txt
```

## Then the message is the invalid-email text
```bash
grep -q 'Enter a valid email address' out.txt
```
~~~~

네 개의 견본은 그 형태를 보여 주기 위한 것입니다. 완전한 기능 트리가 아니며, 이 페이지는 그것들이 단독으로 `specscore spec lint`를 통과한다고 주장하지 않습니다. 기능 README는 그 명령이 깨끗해지려면 다른 필수 절도 필요합니다.

모호한 요청에서 기준을 끌어낼 질문 목록이 필요하면 [좋은 질문 하기](https://specscore.md/asking-good-questions)를 쓰세요. 로그인 둘러보기는 얇은 기준 하나, `session-hardened`를 다른 산출물 사이에 두어 보여 줍니다.

## 자주 묻는 질문

### 인수 기준이란 무엇입니까?

인수 기준(AC)은 변경이 완료로 치려면 참이어야 하는 조건입니다. SpecScore의 인수 기준 명세에서 그것은 추상적입니다. 무엇이 참이어야 하는지를 말하고, 구체적인 단계는 시나리오에 남깁니다.

### Given/When/Then이란 무엇입니까?

Given/When/Then은 Gherkin의 단계 형태입니다. Given은 시작 상태, When은 동작이 하나 일어나는 것, Then은 그에 이어 관찰할 수 있는 결과입니다. Cucumber는 Gherkin 레퍼런스에서 키워드를 문서화합니다. SpecScore의 시나리오 명세는 대문자 키워드 GIVEN, WHEN, THEN, AND를 쓰며, 시나리오 파일마다 WHEN은 정확히 하나입니다.

### AI 코딩 에이전트를 위한 인수 기준은 어떻게 씁니까?

관찰할 수 있는 행위를 하나, 행위자와 입력과 결과를 갖추고, 모호한 말 없이 쓰세요. 예시 데이터는 시나리오에 두고, 추상적인 기준에는 두지 마세요. 그러면 에이전트는 기준에 맞춰 구현하고 시나리오를 검사할 수 있습니다.

### 인수 기준은 테스트 케이스와 같습니까?

아닙니다. 기준은 조건을 말합니다. 테스트 케이스, 또는 SpecScore 시나리오는 구체적인 입력으로 한 번 실행해 조건이 성립하거나 실패함을 보여 줍니다. 여러 시나리오가 기준 하나를 받칠 수 있습니다.

### specscore spec lint는 모호한 문장을 판단합니까?

아닙니다. 이 저장소의 CI가 설치하는 CLI v0.39.1에서 ac-heading-format은 ### AC: 제목을 검사합니다. 「graceful」이나 「fast」 같은 말은 채점하지 않고, 기준에 Given/When/Then이 있어도 경고하지 않습니다. 문장은 쓰는 일입니다. [검증기 페이지](https://specscore.md/ko/validators/)는 그 결정적인 lint를 수동 AI 검토와 나눕니다.

### Given/When/Then 단계는 SpecScore에서 어디에 둡니까?

시나리오에 두고, 인수 기준 안에는 두지 않습니다. 인수 기준 명세는 기준 안의 Given/When/Then 단계를 금합니다. 시나리오 명세는 그것을 _tests/ 아래 ## Steps에 둡니다. Rehearse는 관련된 형태를 실행합니다. _tests/ 아래의 제목과 펜스로 감싼 명령이며, Rehearse 가이드에 문서화되어 있습니다. 린터는 단계 키워드를 강제하지 않습니다.

## 출처

2026년 11월 3일에 읽었습니다. 제3자 페이지는 그곳에 공개된 정의에 대해서만 인용합니다.

- [Cucumber — Gherkin 레퍼런스](https://cucumber.io/docs/gherkin/reference/)
- [Cucumber — 행위 주도 개발](https://cucumber.io/docs/bdd/)
- [Alistair Mavin — EARS](https://alistairmavin.com/ears/)
- [EARS 요구사항 구문을 예와 함께 설명](https://specscore.md/ko/articles/ears-requirements/)
- [SpecScore — 인수 기준 명세](https://specscore.md/acceptance-criteria-specification)
- [specscore/specscore — 인수 기준 기능](https://github.com/specscore/specscore/blob/main/spec/features/acceptance-criteria/README.md)
- [specscore/specscore — 시나리오 기능](https://github.com/specscore/specscore/blob/main/spec/features/scenario/README.md)
- [specscore/specscore — 요구사항 기능](https://github.com/specscore/specscore/blob/main/spec/features/requirement/README.md)
- [SpecScore — 기능 명세](https://specscore.md/feature-specification)
- [SpecScore — Rehearse](https://specscore.md/rehearse)
- [SpecScore — 로그인 예](https://specscore.md/example-login)
- [SpecScore — 좋은 질문 하기](https://specscore.md/asking-good-questions)
- [specscore/specscore-cli](https://github.com/specscore/specscore-cli)
- [specscore/specscore](https://github.com/specscore/specscore)
- [dogfood 워크플로 (CLI v0.39.1)](https://github.com/specscore/specscore/blob/main/.github/workflows/dogfood.yml)
- [specscore-cli — ac-heading-format](https://github.com/specscore/specscore-cli/blob/main/pkg/lint/ac_heading.go)
- [specscore-cli — 계획 규칙 P-001과 P-002](https://github.com/specscore/specscore-cli/blob/main/pkg/lint/plan_rules.go)

## 관련

- [AI 코딩을 위한 Markdown PRD 템플릿](https://specscore.md/ko/articles/feature-spec-template/)
- [CI에서 명세 린터로 명세를 린트하기](https://specscore.md/ko/articles/lint-specs-in-ci/)
- [EARS 요구사항 구문을 예와 함께 설명](https://specscore.md/ko/articles/ears-requirements/)
- [스펙 주도 개발](https://specscore.md/ko/spec-driven-development/)
- [스펙 주도 개발 도구](https://specscore.md/ko/spec-driven-development/tools/)
- [인수 기준 명세](https://specscore.md/acceptance-criteria-specification)
- [좋은 질문 하기](https://specscore.md/asking-good-questions)
- [로그인 기능을 예시로](https://specscore.md/example-login)
- [Rehearse](https://specscore.md/rehearse)
- [SpecScore가 검증하는 것](https://specscore.md/ko/validators/)
- [CLI](https://specscore.md/ko/cli/)
