한 줄 요약: EARS(Easy Approach to Requirements Syntax)란, 소프트웨어 요구사항을 “WHEN [조건] THE [시스템] SHALL [동작]” 형식의 표준 패턴으로 작성해 모호함을 없애고 테스트 가능성을 높이는 요구사항 명세 방법론이다.


1. EARS란 무엇인가?

“시스템은 빨라야 한다”라는 요구사항은 테스트할 수 없습니다. 무엇이 빠른 건지 기준이 없기 때문입니다. EARS는 이런 모호한 요구사항을 구조화된 패턴으로 작성해 명확하고 검증 가능하게 만듭니다.

Alistair Mavin이 2009년 Rolls-Royce에서 요구사항 품질 문제를 해결하기 위해 제안했으며, 현재 항공, 자동차, 소프트웨어 산업 전반에서 사용됩니다.

EARS 이전 vs 이후:

  • ❌ 이전: “시스템은 사용자에게 알림을 보내야 한다”
  • ✅ 이후: “WHEN 주문이 배송 완료되면 THE 시스템은 SHALL 사용자 이메일로 배송 완료 알림을 30초 이내에 발송해야 한다”

2. 핵심 개념 이해하기

EARS 5가지 패턴

패턴 구조 사용 상황
Ubiquitous THE [시스템] SHALL [동작] 항상 적용되는 요구사항
Event-Driven WHEN [이벤트] THE [시스템] SHALL [동작] 이벤트 트리거 요구사항
State-Driven WHILE [상태] THE [시스템] SHALL [동작] 특정 상태에서만 적용
Unwanted IF [조건] THEN THE [시스템] SHALL [동작] 예외 처리 요구사항
Optional WHERE [기능 포함 시] THE [시스템] SHALL [동작] 선택적 기능 요구사항

구체적인 패턴 예시

[Ubiquitous]
THE 인증 시스템은 SHALL 모든 비밀번호를 bcrypt로 해싱하여 저장해야 한다.

[Event-Driven]
WHEN 사용자가 5회 연속 로그인에 실패하면
THE 계정 관리 시스템은 SHALL 해당 계정을 30분간 잠금 처리해야 한다.

[State-Driven]
WHILE 시스템이 유지보수 모드이면
THE API 서버는 SHALL 모든 요청에 503 상태코드와 함께 복구 예상 시간을 반환해야 한다.

[Unwanted]
IF 결제 처리 중 네트워크 오류가 발생하면
THEN THE 결제 시스템은 SHALL 트랜잭션을 롤백하고 사용자에게 오류 코드를 포함한 메시지를 표시해야 한다.

AI 에이전트 요구사항에 EARS 적용

[AI 에이전트 EARS 예시]

WHEN 사용자가 코드 리뷰를 요청하면
THE 코드 리뷰 에이전트는 SHALL 30초 이내에 보안, 성능, 가독성 세 가지 차원에서 분석 결과를 반환해야 한다.

IF LLM API 응답이 10초를 초과하면
THEN THE 에이전트 오케스트레이터는 SHALL 재시도를 최대 3회 수행하고 실패 시 대체 모델로 전환해야 한다.

WHILE 에이전트가 외부 파일 시스템에 접근 중이면
THE 가드레일 시스템은 SHALL 사전 승인된 디렉토리 외의 접근 시도를 차단하고 감사 로그에 기록해야 한다.

3. 실무 적용 예시

EARS 요구사항을 Python 테스트로 자동 변환하는 예시입니다.

import pytest
import time
from unittest.mock import patch, MagicMock

# EARS: "WHEN 사용자가 5회 연속 로그인 실패 시 SHALL 30분간 계정 잠금"
class TestAccountLockout:
    def test_account_locked_after_five_failures(self, auth_service):
        """WHEN 5회 연속 실패 THEN 계정 잠금"""
        user_id = "user123"
        for _ in range(5):
            auth_service.login(user_id, "wrong_password")

        assert auth_service.is_locked(user_id) is True

    def test_lockout_duration_is_30_minutes(self, auth_service):
        """잠금 기간이 30분인지 확인"""
        user_id = "user123"
        for _ in range(5):
            auth_service.login(user_id, "wrong_password")

        lockout_until = auth_service.get_lockout_until(user_id)
        expected = time.time() + 1800  # 30분
        assert abs(lockout_until - expected) < 5  # 5초 오차 허용

    def test_login_blocked_while_locked(self, auth_service):
        """WHILE 잠금 상태 THEN 로그인 차단"""
        user_id = "user123"
        for _ in range(5):
            auth_service.login(user_id, "wrong_password")

        result = auth_service.login(user_id, "correct_password")
        assert result.success is False
        assert "잠금" in result.message


# EARS: "IF 결제 오류 THEN SHALL 트랜잭션 롤백"
class TestPaymentRollback:
    def test_transaction_rolled_back_on_network_error(self, payment_service, db):
        """IF 네트워크 오류 THEN 롤백"""
        initial_balance = db.get_balance("user123")

        with patch("requests.post", side_effect=ConnectionError):
            with pytest.raises(PaymentError):
                payment_service.charge("user123", 10000)

        # 잔액이 변경되지 않았는지 확인 (롤백 검증)
        assert db.get_balance("user123") == initial_balance

4. EARS vs 유사 개념 비교

구분 EARS 자연어 요구사항 Use Case User Story
구조화 ✅ 고정 패턴 ❌ 자유 형식 보통 보통
테스트 가능성 ✅ 명확한 기준 ❌ 모호 보통 보통
작성 난이도 낮음 (패턴 따름) 낮음 중간 낮음
AI 협업 ✅ AI가 파싱 가능 보통 보통
진입 장벽 ✅ 낮음 없음 높음 낮음

5. 마치며

EARS는 요구사항의 “무엇을”을 명확히 정의해 개발자, 테스터, AI 에이전트 모두가 동일한 이해를 가지도록 합니다. AI에게 작업을 지시할 때도 EARS 패턴을 활용하면 더 정확하고 검증 가능한 결과를 얻을 수 있습니다.

참고 자료

함께 읽으면 좋은 용어

이 개념과 함께 알아두면 이해가 깊어지는 관련 용어들입니다.

댓글 남기기