컴퓨터공학 300 주제 시리즈의 183번째 글이다. 전체 지도는 여기.

한 줄 요약

요구사항 분석은 이해관계자가 원하는 것을 찾아내고(도출), 정리하고(분석), 문서로 남기고(명세), 그것이 맞는지 확인하는(검증) 일이다. 좋은 요구사항은 테스트로 확인할 수 있는 문장이다.

왜 필요한가

“검색이 빨랐으면 좋겠어요.” 이 문장을 받은 개발자는 무엇을 만들어야 할까. 0.1초? 1초? 검색 결과 1만 건에서? 1억 건에서? 동시에 몇 명이 검색할 때?

요구사항이 모호하면 개발자는 자기 해석대로 만든다. 그 해석이 틀렸다는 사실은 사용자가 결과물을 볼 때 드러난다. 앞의 생명주기 글에서 봤듯이, 그 시점이 늦을수록 되돌리는 비용이 크다. 요구사항 분석은 “무엇을 만들지” 에 대한 오해를 코드를 짜기 전에 잡는 거의 유일한 기회다.

핵심 개념

요구사항 공학의 네 활동

활동 하는 일 도구·기법
도출(elicitation) 이해관계자에게서 필요를 끌어낸다 인터뷰, 관찰, 워크숍, 기존 시스템 분석
분석(analysis) 충돌·누락을 찾고 우선순위를 정한다 모델링, 우선순위 기법
명세(specification) 합의된 내용을 기록한다 요구사항 명세서, 사용자 스토리
검증(validation) 기록이 실제 필요와 맞는지 확인한다 리뷰, 프로토타입, 인수 기준

SWEBOK 은 이 흐름을 “소프트웨어 요구사항” 지식 영역으로 다루고, ISO/IEC/IEEE 29148 은 요구사항 공학 프로세스와 명세서 내용을 표준화한다.

기능 요구사항과 비기능 요구사항

  • 기능 요구사항: 시스템이 무엇을 하는가. “사용자는 이메일과 비밀번호로 로그인할 수 있다.”
  • 비기능 요구사항: 시스템이 얼마나 잘 하는가. 성능, 보안, 가용성, 사용성, 유지보수성 등. “로그인 API 의 95번째 백분위 응답 시간은 300ms 이하다.”

비기능 요구사항은 빠뜨리기 쉽고, 나중에 고치기 어렵다. 기능은 하나를 더 붙이면 되지만, “초당 1만 건을 처리해야 한다” 는 요구는 아키텍처 전체를 바꿀 수 있다. 품질 특성을 빠짐없이 훑을 때는 ISO/IEC 25010 품질 모델이 체크리스트로 쓸 만하다. 2011년판은 기능 적합성, 성능 효율성, 호환성, 사용성, 신뢰성, 보안성, 유지보수성, 이식성의 8가지 특성을 정의한다(2023년에 개정판이 나왔다).

좋은 요구사항의 성질

ISO/IEC/IEEE 29148 은 개별 요구사항이 갖춰야 할 특성으로 필요성, 명확성(모호하지 않음), 완전성, 단일성, 실현 가능성, 검증 가능성 등을 든다. 실무에서 가장 자주 깨지는 것은 세 가지다.

성질 나쁜 예 고친 예
명확성 화면이 적절히 빨리 떠야 한다 상품 목록 첫 화면은 4G 환경에서 2초 안에 표시된다
단일성 주문하고 결제하고 영수증을 메일로 보낸다 세 개의 요구사항으로 나눈다
검증 가능성 사용하기 쉬워야 한다 신규 사용자 5명 중 4명이 도움말 없이 3분 안에 첫 주문을 마친다

요구 수준을 나타내는 단어: RFC 2119

인터넷 표준 문서는 요구의 강도를 RFC 2119 의 키워드로 표시한다. MUST(반드시), SHOULD(특별한 이유가 없으면), MAY(선택) 등이다. RFC 8174 는 이 키워드가 대문자로 쓰였을 때만 이 특별한 뜻을 갖는다고 명확히 했다. 사내 명세서에도 이 구분을 빌려 쓰면 “해야 한다” 와 “하면 좋다” 가 섞여 생기는 다툼이 줄어든다.

사용자 스토리와 인수 기준

애자일 팀은 요구사항을 사용자 스토리로 많이 적는다.

[사용자 스토리]
장바구니를 쓰는 고객으로서,
담아 둔 상품의 가격이 바뀌면 알림을 받고 싶다.
그래야 결제 직전에 놀라지 않는다.

“누가, 무엇을, 왜” 가 한 묶음이다. “왜” 가 빠지면 개발자가 더 나은 해법을 제안할 여지가 사라진다. 스토리만으로는 검증할 수 없으므로 인수 기준(acceptance criteria)을 붙인다. 행위 주도 개발(BDD)에서 쓰는 Given-When-Then 형식이 흔하다.

시나리오: 담아 둔 상품 가격이 내려감
  Given 고객의 장바구니에 10,000원짜리 상품 A 가 있다
  When  상품 A 의 가격이 9,000원으로 바뀐다
  Then  고객이 장바구니를 열면 "가격이 1,000원 내려갔습니다" 표시가 보인다

이 문장은 그대로 테스트 케이스가 된다. 요구사항과 테스트가 같은 문장을 공유하는 것이 핵심이다.

우선순위: MoSCoW

모든 요구를 다 만들 시간은 거의 없다. MoSCoW 는 요구를 Must have, Should have, Could have, Won’t have(이번에는 안 함) 로 나눈다. “이번에 안 하는 것” 을 명시하는 칸이 있다는 점이 중요하다. 범위에서 뺀 것을 적어 두지 않으면 나중에 “그것도 되는 줄 알았다” 가 나온다.

추적성

요구사항마다 ID 를 붙이고, 그 요구사항을 구현한 코드와 확인하는 테스트를 연결해 두는 것을 추적성이라 한다. 요구가 바뀌면 영향받는 코드와 테스트를 바로 찾을 수 있고, 테스트가 없는 요구사항도 드러난다.

직접 해 보기

요구사항 문장에서 모호한 표현과 측정 기준 누락을 잡아내는 작은 린터를 만들어 본다. 실제 도구들도 기본 원리는 비슷하다.

import re

VAGUE = ["빠르게", "빨리", "적절히", "충분히", "쉽게", "편리하게", "등", "가능한 한", "사용자 친화적"]
NUMBER_WITH_UNIT = re.compile(r"\d+(\.\d+)?\s*(ms|초|분|%|건|명|MB|GB|회)")

requirements = {
    "REQ-001": "사용자는 이메일과 비밀번호로 로그인할 수 있다.",
    "REQ-002": "검색 결과는 빠르게 표시되어야 한다.",
    "REQ-003": "로그인 API 의 p95 응답 시간은 300 ms 이하여야 한다.",
    "REQ-004": "주문하고 결제하고 영수증을 메일로 보낸다.",
    "REQ-005": "관리 화면은 사용자 친화적이어야 하며 충분히 안전해야 한다.",
}

def lint(text):
    problems = []
    for w in VAGUE:
        if w in text:
            problems.append(f"모호한 표현 '{w}'")
    if ("성능" in text or "응답" in text or "빠르" in text) and not NUMBER_WITH_UNIT.search(text):
        problems.append("성능 요구인데 수치·단위가 없음")
    if text.count("하고") >= 2:
        problems.append("여러 요구가 한 문장에 섞임(단일성)")
    return problems

for rid, text in requirements.items():
    p = lint(text)
    print(f"{rid} {'OK ' if not p else 'FIX'} {text}")
    for item in p:
        print(f"        - {item}")

실행 결과:

REQ-001 OK  사용자는 이메일과 비밀번호로 로그인할 수 있다.
REQ-002 FIX 검색 결과는 빠르게 표시되어야 한다.
        - 모호한 표현 '빠르게'
        - 성능 요구인데 수치·단위가 없음
REQ-003 OK  로그인 API 의 p95 응답 시간은 300 ms 이하여야 한다.
REQ-004 FIX 주문하고 결제하고 영수증을 메일로 보낸다.
        - 여러 요구가 한 문장에 섞임(단일성)
REQ-005 FIX 관리 화면은 사용자 친화적이어야 하며 충분히 안전해야 한다.
        - 모호한 표현 '충분히'
        - 모호한 표현 '사용자 친화적'

REQ-002 는 “빠르게” 라는 단어 하나 때문에 검증할 수 없는 문장이 됐다. REQ-003 처럼 지표(p95), 수치(300), 단위(ms)가 있어야 테스트로 통과·실패를 판정할 수 있다. 물론 이런 키워드 검사는 출발점일 뿐이다. 진짜 검증은 이해관계자와 함께 문장을 읽고 “이 조건이면 받아들이시겠어요?” 라고 묻는 리뷰에서 일어난다.

현업에서는

  • 티켓 템플릿에 인수 기준 칸을 강제한다. 이슈 템플릿에 “완료 조건” 칸을 두면, 모호한 요구가 개발 단계로 넘어오기 전에 걸러진다.
  • 비기능 요구사항은 SLO 로 바뀐다. “서비스는 안정적이어야 한다” 는 운영 단계에서 “월간 가용성 99.9%, p99 지연 500ms” 같은 서비스 수준 목표(SLO)로 구체화된다. 홈랩 클러스터에서도 모니터링 알림 임계값을 정하는 순간 이 작업을 하고 있는 것이다.
  • “안 하는 것” 목록이 다툼을 줄인다. 릴리스 노트나 기획서에 “이번 범위 밖” 을 적는 팀은 막판 범위 논쟁이 적다.
  • 도메인 전문가의 말을 그대로 쓴다. 사용자가 “출고” 라고 부르는 것을 개발 문서에서 “shipment” 와 “delivery” 로 섞어 쓰면 오해가 생긴다. 이 문제는 도메인 주도 설계의 “보편 언어” 로 이어진다.

확인 문제

  1. 기능 요구사항과 비기능 요구사항을 하나씩 예로 들라.
  2. “시스템은 사용하기 쉬워야 한다” 를 검증 가능한 요구사항으로 고쳐 써라.
  3. RFC 8174 가 RFC 2119 에 덧붙인 핵심 내용은 무엇인가?
  4. 사용자 스토리에서 “그래야 ~” 부분(이유)을 빼면 무엇을 잃는가?
  5. MoSCoW 에서 W 항목을 명시하는 이유는 무엇인가?

풀이

  1. 기능: “사용자는 비밀번호를 재설정할 수 있다.” 비기능: “비밀번호 재설정 메일은 요청 후 1분 안에 발송된다.”
  2. 예: “처음 쓰는 사용자 5명 중 4명 이상이 도움말 없이 3분 안에 회원가입을 마친다.” 대상, 조건, 측정 기준이 들어가면 된다.
  3. MUST·SHOULD 같은 키워드가 대문자로 쓰였을 때만 RFC 2119 의 특별한 의미를 갖는다는 점.
  4. 요구의 목적. 목적을 모르면 개발자가 더 단순하거나 나은 대안을 제안할 수 없고, 우선순위 판단도 어려워진다.
  5. 범위에서 뺀 것을 합의된 기록으로 남겨, 나중에 “그것도 포함인 줄 알았다” 는 오해를 막기 위해서다.

더 읽을거리 (References)