소프트웨어 공학 100 주제 시리즈의 76번째 글이다. (카테고리: 프로젝트 관리와 추정)

한 줄 요약

WBS(작업 분해 구조)는 프로젝트가 만들어야 할 모든 산출물을 계층으로 쪼갠 목록이고, 임계 경로(critical path)는 그 작업들을 의존 관계로 이었을 때 가장 긴 경로다. 임계 경로 위의 작업이 하루 늦으면 프로젝트가 하루 늦는다. 그 밖의 작업에는 여유(float)가 있다.

왜 필요한가

“6주면 됩니다” 라는 추정이 틀리는 흔한 이유는 개별 작업을 잘못 추정해서가 아니다. 빠진 작업과 기다리는 시간 때문이다.

  • 빠진 작업: 데이터 마이그레이션, 운영 문서, 보안 검토, 스테이징 환경 준비. 아무도 “개발” 이라고 생각하지 않아서 추정에서 빠진다.
  • 기다리는 시간: 프런트엔드가 API 스펙을 기다리고, 통합 테스트가 결제 연동을 기다린다. 작업 시간의 합이 4주여도 의존 관계 때문에 7주가 걸린다.

WBS 는 첫 번째 문제를, 임계 경로 분석은 두 번째 문제를 다룬다.

핵심 개념

WBS — 산출물 중심의 가계도

NASA WBS 핸드북(NASA/SP-20250006071, 2025)은 WBS 를 이렇게 정의한다. 최종 목표를 달성하는 데 필요한 하드웨어, 소프트웨어, 서비스, 그 밖의 모든 산출물을 식별하는 산출물 중심의 가계도(product-oriented family tree) 다. 목적은 프로젝트의 작업 내용을 관리 가능한 조각으로 나누어 비용·일정·기술 내용을 계획하고 통제하는 것이다.

핸드북에서 실무에 바로 쓸 원칙을 뽑으면 다음과 같다.

원칙 핸드북의 표현
전체 범위 WBS 는 프로젝트의 전체 작업 범위를 나타내야 한다. WBS 에 없는 작업은 프로젝트의 일부가 아니다
산출물 중심 기능 조직(부서)이 아니라 산출물로 나눈다. 단, 관리·시스템 엔지니어링 같은 공통 요소도 포함한다
사전(dictionary) 각 요소의 내용을 서술하는 WBS 사전을 함께 만든다
추적성 요구사항 문서와 WBS 요소를 교차 행렬로 맞춰, 요구 없는 작업과 작업 없는 요구를 찾는다
통제 지점 WBS 요소와 조직이 만나는 지점에 통제 계정(control account)을 두고 예산·일정·실적을 관리한다

“전체 범위” 원칙을 흔히 100% 규칙이라고 부른다. 자식 요소의 합은 부모 요소와 정확히 같아야 하고, 빠진 것도 넘치는 것도 없어야 한다.

소프트웨어 프로젝트의 WBS 예시:

1.0 쇼핑몰 v2
├── 1.1 프로젝트 관리 (계획, 보고, 위험 관리)
├── 1.2 요구사항·설계 (요구 명세, API 계약, ADR)
├── 1.3 백엔드
│   ├── 1.3.1 주문 서비스
│   ├── 1.3.2 결제 연동
│   └── 1.3.3 데이터 마이그레이션        ← 자주 빠지는 것
├── 1.4 프런트엔드 (웹, 모바일 웹)
├── 1.5 품질 (테스트 자동화, 성능 테스트, 보안 점검)
├── 1.6 운영 준비 (모니터링, 런북, 롤백 절차)   ← 자주 빠지는 것
└── 1.7 출시 (스테이징 검증, 단계적 배포, 고객 공지)

WBS 는 무엇을 만들지의 분해다. “언제, 어떤 순서로” 는 다음 단계인 일정 네트워크에서 다룬다.

CPM — 가장 긴 경로가 기간을 정한다

임계 경로 방법(CPM)은 James Kelley 와 Morgan Walker 가 1959년 Critical-path planning and scheduling에서 발표했다. 같은 해 발표된 PERT(Malcolm 외, Operations Research)도 같은 네트워크 아이디어를 R&D 일정 평가에 썼다.

NASA 일정 관리 핸드북(2024 개정 2판)은 CPM 을 “일정 네트워크의 가장 긴 경로인 임계 경로를 계산해 최소 기간을 추정하는 기법” 으로 설명한다. CPM 모델에 필요한 것은 네 가지다. 작업 목록(대개 WBS 로 분류), 작업별 기간, 작업 간 의존 관계, 그리고 마일스톤 같은 논리적 종점.

계산은 두 번의 순회다.

단계 계산 의미
전진 계산 ES = 선행 작업 EF 의 최댓값, EF = ES + 기간 가장 이른 시작·종료
후진 계산 LF = 후행 작업 LS 의 최솟값, LS = LF − 기간 전체를 늦추지 않는 가장 늦은 시작·종료
여유 TF = LS − ES 프로젝트를 늦추지 않고 미룰 수 있는 시간

핸드북의 정의에 따르면 총 여유(total float) 는 프로젝트 완료를 늦추지 않고 작업을 미룰 수 있는 시간이고, 자유 여유(free float) 는 바로 뒤 작업의 가장 이른 시작을 늦추지 않고 미룰 수 있는 시간이다. 총 여유가 0 이면 그 작업의 지연은 그대로 프로젝트 지연이 된다.

예제

임계 경로 계산

작업 순서를 정하는 것은 위상 정렬이다(위상 정렬 참고). 파이썬 표준 라이브러리의 graphlib 로 충분하다.

from graphlib import TopologicalSorter

tasks = {  # 이름: (기간[일], 선행 작업들)
    "A 요구 정리":   (3, []),
    "B API 설계":    (4, ["A 요구 정리"]),
    "C DB 스키마":   (2, ["A 요구 정리"]),
    "D 백엔드 구현": (8, ["B API 설계", "C DB 스키마"]),
    "E 프런트 구현": (6, ["B API 설계"]),
    "F 결제 연동":   (5, ["C DB 스키마"]),
    "G 통합 테스트": (3, ["D 백엔드 구현", "E 프런트 구현", "F 결제 연동"]),
    "H 출시 준비":   (1, ["G 통합 테스트"]),
}
order = list(TopologicalSorter({k: v[1] for k, v in tasks.items()}).static_order())
ES, EF = {}, {}
for t in order:                                    # 전진 계산
    dur, preds = tasks[t]
    ES[t] = max((EF[p] for p in preds), default=0)
    EF[t] = ES[t] + dur
end = max(EF.values())
succ = {t: [s for s in tasks if t in tasks[s][1]] for t in tasks}
LS, LF = {}, {}
for t in reversed(order):                          # 후진 계산
    LF[t] = min((LS[s] for s in succ[t]), default=end)
    LS[t] = LF[t] - tasks[t][0]
for t in order:
    tf = LS[t] - ES[t]
    print(f"{t:<10} ES={ES[t]:>2} EF={EF[t]:>2} LS={LS[t]:>2} LF={LF[t]:>2} TF={tf}"
          + ("  <- 임계" if tf == 0 else ""))
print("프로젝트 기간:", end, "일")

실행 결과:

A 요구 정리    ES= 0 EF= 3 LS= 0 LF= 3 TF=0  <- 임계
B API 설계   ES= 3 EF= 7 LS= 3 LF= 7 TF=0  <- 임계
C DB 스키마   ES= 3 EF= 5 LS= 5 LF= 7 TF=2
E 프런트 구현   ES= 7 EF=13 LS= 9 LF=15 TF=2
D 백엔드 구현   ES= 7 EF=15 LS= 7 LF=15 TF=0  <- 임계
F 결제 연동    ES= 5 EF=10 LS=10 LF=15 TF=5
G 통합 테스트   ES=15 EF=18 LS=15 LF=18 TF=0  <- 임계
H 출시 준비    ES=18 EF=19 LS=18 LF=19 TF=0  <- 임계
프로젝트 기간: 19 일

작업 시간의 합은 32일이지만 기간은 19일이다. 임계 경로는 A → B → D → G → H 다. 이 결과에서 읽을 수 있는 것:

  • 일정을 당기려면 임계 작업을 줄여야 한다. 결제 연동(F)을 아무리 빨리 해도 기간은 줄지 않는다. 여유가 5일이기 때문이다.
  • 사람을 옮길 곳이 보인다. F 담당자가 D 를 도우면 기간이 줄 수 있다(단, SE100 #003 의 Brooks 의 법칙을 기억하자).
  • 임계 경로는 바뀐다. D 가 3일 줄면 E 가 새 임계 경로에 올라온다.

병합 편향 — 결정론적 일정이 낙관적인 이유

통합 테스트(G)는 세 갈래가 모두 끝나야 시작한다. NASA 핸드북은 이를 병합 편향(merge bias) 으로 설명한다. 각자 불확실성을 가진 병렬 경로 여러 개가 한 지점으로 모이면, 결정론적 일정은 대개 낮은 신뢰 수준이 된다는 것이다. 간단히 시뮬레이션해 보자.

import random
random.seed(7)
N = 100_000
def path():  # 각 경로: 최소 8, 최빈 10, 최대 16 일의 삼각분포
    return random.triangular(8, 16, 10)
for k in (1, 2, 3, 5):
    hit = sum(max(path() for _ in range(k)) <= 11 for _ in range(N)) / N
    print(f"병렬 경로 {k}개 → 11일 안에 모두 끝날 확률 {hit:5.1%}")
병렬 경로 1개 → 11일 안에 모두 끝날 확률 48.0%
병렬 경로 2개 → 11일 안에 모두 끝날 확률 23.0%
병렬 경로 3개 → 11일 안에 모두 끝날 확률 11.0%
병렬 경로 5개 → 11일 안에 모두 끝날 확률  2.5%

경로 하나가 11일 안에 끝날 확률이 절반쯤이어도, 다섯 갈래가 모두 제때 합류할 확률은 3% 미만이다. 그래서 병합 지점 앞에는 버퍼를 둔다. 확률적 일정 분석은 SE100 #077 에서 더 다룬다.

실무 적용

  • WBS 는 산출물 명사로 쓴다. “구현하기” 가 아니라 “주문 API”, “마이그레이션 스크립트와 검증 리포트”. 끝났는지 판단할 수 있어야 한다.
  • 작업 패키지는 한 보고 주기 안에 끝나는 크기로. 2주 스프린트라면 대개 며칠 단위다. 더 크면 진척이 “80% 완료” 에 몇 주씩 머문다.
  • 요구사항-WBS 교차 확인. NASA 핸드북의 교차 행렬처럼, 요구사항 ID 와 WBS 요소를 표로 맞춰 본다. 요구사항 추적성(SE100 #019)의 일정 버전이다.
  • 버퍼는 숨기지 말고 드러낸다. 작업마다 여유를 몰래 얹으면 학생 증후군이나 파킨슨의 법칙(“일은 주어진 시간을 채울 만큼 늘어난다”)으로 사라진다. Eliyahu Goldratt 의 Critical Chain(North River Press, 1997)은 이 여유를 모아 프로젝트 끝과 병합 지점에 공개 버퍼로 두자고 제안했다.
  • 도구보다 네트워크. 간트 차트는 결과 그림일 뿐이다. 의존 관계 없이 막대만 그린 간트 차트로는 임계 경로를 알 수 없다.

흔한 오해와 함정

  • “WBS = 할 일 목록.” WBS 는 산출물의 계층이고 순서가 없다. 순서와 의존은 일정 네트워크에서 다룬다.
  • “애자일에는 WBS 가 필요 없다.” 에픽 → 피처 → 스토리 분해가 사실상 WBS 다. 100% 규칙(빠진 것 없음)은 애자일 백로그에도 그대로 유효하다. 특히 운영 준비와 마이그레이션이 백로그에서 자주 빠진다.
  • “임계 경로는 고정이다.” 실적이 쌓이면 바뀐다. 매주 다시 계산해야 한다.
  • “여유가 있으니 나중에 해도 된다.” 여유는 경로 전체가 공유한다. 앞 작업이 여유를 다 쓰면 뒤 작업은 임계가 된다.
  • “결정론적 일정의 종료일 = 약속 날짜.” 병합 편향 때문에 대개 낙관적이다. 범위로 말하라(SE100 #071).

확인 문제

  1. NASA 핸드북의 WBS 정의에서 “산출물 중심” 이 뜻하는 바와, WBS 에 없는 작업의 지위는?
  2. 위 예제에서 F(결제 연동)의 총 여유는 며칠이며, F 를 3일 단축하면 프로젝트 기간은?
  3. 총 여유와 자유 여유의 차이를 설명하라.
  4. D(백엔드 구현)가 8일에서 5일로 줄면 새 임계 경로와 프로젝트 기간은?
  5. 병합 편향이 결정론적 일정을 낙관적으로 만드는 이유는?

풀이

  1. 부서나 활동이 아니라 만들어야 할 산출물(하드웨어, 소프트웨어, 서비스 등)을 기준으로 분해한다는 뜻이다. WBS 에 없는 작업은 프로젝트 범위에 포함되지 않는 것으로 본다.
  2. 총 여유는 5일. F 는 임계 경로가 아니므로 단축해도 기간은 19일 그대로다.
  3. 총 여유는 프로젝트 완료일을 늦추지 않고 미룰 수 있는 시간, 자유 여유는 바로 뒤 작업의 가장 이른 시작을 늦추지 않고 미룰 수 있는 시간이다.
  4. D 의 EF 가 12 가 되어 G 의 시작은 E 의 EF(13)가 정한다. 임계 경로는 A → B → E → G → H, 기간은 13 + 3 + 1 = 17일.
  5. 병렬 경로가 모두 끝나야 다음 작업이 시작하므로, 합류 시점은 각 경로 종료 시점의 최댓값이 된다. 경로마다 불확실성이 있으면 최댓값은 개별 최빈값보다 체계적으로 늦어진다.

더 읽을거리 (References)