관찰 가능한 인수 기준
65분 안팎
학습 목표
정상·오류·빈 상태의 기대 결과를 구체화합니다.
개념
‘잘 된다’를 비교 가능한 약속으로 바꿉니다
인수 기준은 구현 결과를 받아들일지 판단할 약속입니다. ‘가입이 잘 된다’는 문장으로는 회원이 생성되어야 하는지, 자동 로그인해야 하는지, 어떤 오류 안내가 필요한지 알기 어렵습니다. 테스트를 실행한 다음 마음에 드는 결과를 정답으로 고르면 제품이 규칙을 어긴 경우도 통과하게 됩니다. QA는 합의한 기대 결과를 먼저 적고 실제 결과와 비교할 수 있도록 준비합니다.
요구사항은 사용자가 얻으려는 동작을 설명하고 인수 기준은 그 동작을 어떤 조건에서 어떤 결과로 확인할지 설명합니다. ‘회원은 계정을 만들 수 있다’가 요구사항이라면 ‘같은 이메일이 없을 때 유효한 입력으로 가입하면 회원이 한 명 증가한다’가 인수 기준입니다. 하나의 요구사항에 성공과 거절 등 여러 기준이 붙을 수 있습니다. 문서의 줄 수와 실제 조건의 수가 같다고 가정하지 않습니다.
Given·When·Then의 역할
Given은 행동 전에 참이어야 하는 조건입니다. When은 검증할 행동 또는 사건입니다. Then은 행동 뒤 관찰할 결과입니다. 이 문법은 특정 자동화 도구를 설치하라는 뜻이 아닙니다. 사람 사이의 전제를 드러내는 구조입니다. Given에 ‘가입 성공’이라고 쓰면 검증할 결과를 이미 전제로 둔 셈이 됩니다. ‘회원 수가 0이고 같은 이메일이 없다’처럼 아직 실행하지 않은 상태로 고칩니다.
When에는 비교할 행동을 작게 씁니다. 가입하고 로그인하고 수정하기를 한 줄에 넣으면 마지막 실패가 어느 규칙을 어겼는지 분리하기 어렵습니다. 이번에는 ‘new@example.test와 합의한 유효 입력으로 가입한다’를 하나의 사건으로 씁니다. Then에는 안내와 데이터 효과를 함께 적되 로그인 성공까지 묶지 않습니다. 가입 후 로그인 화면으로 이동하고 새 회원을 재조회할 수 있다는 것이 교육용 가입 성공 기준입니다.
{
"id": "AC-01",
"given": "같은 이메일이 없고 로그아웃 상태입니다.",
"when": "new@example.test로 가입합니다.",
"then": "가입 완료 안내 후 로그인 화면으로 이동합니다.",
"observations": [
{"layer": "ui", "expected": "가입 완료 안내를 표시합니다."},
{"layer": "state", "expected": "회원 수가 1 증가합니다."}
]
}
이 조각은 acceptance 배열의 항목입니다. requirements 전체를 대신하는 파일로 저장하지 않습니다. 인수 기준 ID는 문서 전체에서 고유하게 사용합니다. REQ-01은 요구사항, AC-01은 해당 기준의 식별자입니다. 둘을 구분하면 한 요구사항 아래 기준이 추가되어도 기존 연결을 유지할 수 있습니다. ID 숫자는 중요도나 실행 순서를 뜻하지 않습니다.
거절 입력의 성공적인 처리를 씁니다
중복 이메일로 가입할 때 거절 안내가 나오는 것은 정상적인 제품 동작일 수 있습니다. ‘오류 입력을 넣었으니 테스트 실패’라고 부르지 않습니다. 기대한 거절과 무변경이 나오면 테스트는 통과입니다. 실제 결과가 기대값과 다를 때 테스트가 실패합니다. 입력의 유효성, 제품 응답의 성공 여부, 테스트 비교의 통과 여부를 각각 다른 말로 기록하면 팀의 대화가 정확해집니다.
중복 조건의 Given에는 이미 존재하는 same@example.test 회원을 적습니다. When에는 정확히 같은 이메일을 다시 보냅니다. Then에는 사용 불가 안내와 가입 화면 유지가 필요합니다. 저장 관찰에는 회원 수 및 기존 정보 무변경을 적습니다. ‘추가되지 않는다’만 쓰면 기존 회원의 정보가 덮어써지는 결함을 놓칠 수 있으므로 유지할 대상도 명시합니다. 이메일 대소문자 처리는 아직 보류이며 이 기준과 섞지 않습니다.
빈 이메일은 빈 문자열이라는 구체적인 입력을 사용합니다. 스페이스만 있는 닉네임은 빈 문자열과 다른 원본 값입니다. 교육용 합의에서는 둘 다 필수 입력 거절 대상이지만 어느 값을 넣었는지 써야 재현할 수 있습니다. 빈 값, 누락된 필드, null을 같은 말로 묶지 않습니다. 누락과 null의 API 계약은 후속 모듈에서 정리하고 지금은 화면에서 입력할 수 있는 예시를 확정합니다.
빈 상태와 오류 뒤 상태
회원이 아직 없는 상태에서도 가입 화면은 사용할 수 있어야 합니다. 회원 수 0이라는 Given은 빈 상태를 재현하는 출발점입니다. ‘가입 기록이 없는 회원’처럼 서로 맞지 않는 표현을 쓰기보다 비회원과 회원을 나눕니다. 로그인 실패에서는 이미 가입된 계정이 있는 조건과 틀린 비밀번호를 적어야 계정 미존재 조건과 혼동하지 않습니다. 실패 후 인증된 세션이 없다는 결과는 화면 안내와 별개로 확인할 대상입니다.
프로필 기준에서는 누구의 데이터가 변하는지 씁니다. 회원 A의 본인 수정은 A의 재조회 값이 변경되어야 하고 B의 값은 유지되어야 합니다. A가 B를 수정하는 요청은 거절되어야 하며 B 정보가 응답에 노출되지 않아야 합니다. 저장 무변경과 응답 비노출은 서로 대체할 수 없습니다. 한쪽만 확인하면 권한 처리가 충분하다고 잘못 결론 내릴 수 있습니다.
단정 대신 관찰 지점을 선택합니다
‘DB가 안전하게 저장된다’는 말은 검사 위치와 비교 값을 알려 주지 않습니다. ‘처리 전 회원 수 0, 처리 후 회원 수 1, 이메일로 재조회한 닉네임 새회원’은 관찰 계획을 보여 줍니다. 아직 DB 구조를 모르므로 특정 테이블이나 SQL을 지시하지 않습니다. 지금 필요한 것은 저장 효과의 의미를 분명히 하는 일입니다. SQL 조회 방식은 데이터 검증 단계에서 구체화합니다.
‘즉시’, ‘빠르게’, ‘친절하게’ 같은 표현은 팀이 비교할 기준을 합의하기 전까지 판정하기 어렵습니다. 예를 들어 시간 요구가 필요하다면 측정 시작과 종료, 환경과 허용 시간을 확인해야 합니다. QA가 임의의 숫자를 써 넣으면 합의가 된 것처럼 보일 수 있습니다. 이번 문서에는 임의의 응답 시간이나 비밀번호 정책을 추가하지 않고 빠진 조건을 질문 목록에 남깁니다.
검사 메시지와 사람 리뷰
‘REQ-03/AC-03: then 공백’은 AC-03에 행동 뒤 결과가 없다는 메시지입니다. 스페이스를 채워도 공백 검사는 통과하지 않습니다. 의미 있는 문장을 적고 해당 observations와 모순되는지 읽습니다. ‘acceptance: 중복 ID’는 다른 요구사항에서도 같은 기준 ID를 썼다는 뜻입니다. 파일의 위치가 아니라 ID로 연결하므로 새 식별자를 부여하고 추적표의 참조도 함께 수정합니다.
형식 검사기가 문장에 글자가 있는지는 확인할 수 있어도 ‘완료’라는 단어가 충분한 결과인지는 판단하지 못합니다. 리뷰할 때는 사전 조건을 그대로 준비할 수 있는지, 행동을 한 번의 검증으로 수행할 수 있는지, 결과를 두 사람이 같은 방법으로 판정할 수 있는지 묻습니다. 정상·거절·빈 상태에서 각각 한 기준을 소리 내어 읽고 누가 무엇을 확인할지 설명합니다. 설명이 달라지면 기준을 확정하지 말고 예시로 차이를 좁힙니다.
따라하기
기준 한 건을 구조에 넣기
requirements.json의 REQ-01에 source를 교육용 합의 v1, status를 agreed로 씁니다. acceptance의 AC-01에 Given=같은 이메일 없음, When=new@example.test로 가입, Then=완료 안내 후 로그인 화면을 적습니다. observations에는 ui 안내와 state 회원 수 1 증가를 별개 항목으로 둡니다. 기존 다른 필드는 유지합니다.
빈 결과를 발견하는 예시 실행
이 코드를 acceptance-check.py에 저장하고 python3 acceptance-check.py로 실행합니다. 공백만 있는 then을 검사하는 축소 예시입니다. 앱을 검사하지 않습니다. FAIL의 ID를 보고 starter에서 어떤 then을 보충해야 하는지 찾습니다.
criteria = [{"id": "AC-01", "then": "가입 완료 안내 후 로그인 화면"}, {"id": "AC-03", "then": " "}]
for c in criteria:
print(("PASS " if c["then"].strip() else "FAIL ") + c["id"])
실행 결과
PASS AC-01 FAIL AC-03
거절과 무변경 쓰기
REQ-02/AC-02에는 same@example.test가 이미 존재하는 Given과 동일 이메일 재가입 When을 씁니다. Then은 이메일 사용 불가 안내와 가입 화면 유지입니다. state의 expected에는 회원 수와 기존 회원 정보 무변경을 씁니다. REQ-03/AC-03의 빈 then은 이메일 필수 안내로 보충하고 새 회원 미생성 관찰을 유지합니다.
세 흐름에 기준 확장하기
REQ-04부터 REQ-08까지 공백 닉네임 거절, 로그인 성공, 틀린 비밀번호 거절, 본인 수정, 타인 수정 거절을 추가합니다. AC도 중복되지 않게 부여합니다. 합의된 조건은 README의 교육용 합의 v1을 사용합니다. 정책을 모르는 길이와 대소문자는 이 단계에서 임의로 확정하지 않습니다. Given·When·Then을 동료가 그대로 수행할 수 있는지 읽습니다.
확인 문제
실습
spec/requirements.json에 요구사항 8개 이상을 작성합니다. 각 기준의 Given·When·Then과 관찰 층 및 기대 결과를 채우고 가입 성공·중복 이메일·빈 입력·타인 접근을 포함합니다. REQ와 AC의 ID는 중복 없이 부여합니다. 정상·거절·빈 상태의 기준을 하나씩 골라 다른 사람이 같은 결과로 판정할 수 있는지 설명합니다.
더 읽기
면접 질문
- 가입 폼의 테스트 조건을 정하는 과정을 설명해 주시면 됩니다.
- 요구사항에 정상 동작만 적혀 있을 때 대응 방법을 설명해 주시면 됩니다.