과학적 일정 추정: PERT에서 Monte Carlo까지

발행: (2026년 1월 2일 오후 11:21 GMT+9)
6 min read
원문: Dev.to

I’m happy to translate the article for you, but I’ll need the full text you’d like translated. Could you please paste the content (or the portion you want translated) here? I’ll keep the source line, formatting, markdown, and any code blocks exactly as they appear.

PERT (Program Evaluation and Review Technique)

PERT는 1950년대 미국 해군에 의해 폴라리스 미사일 프로젝트를 위해 개발되었습니다. 이 비밀 프로젝트는 개발 기간을 2년 단축시켰습니다.

def pert_estimation(optimistic, realistic, pessimistic):
    """
    O: Optimistic (when everything is perfect)
    R: Realistic (normal case)
    P: Pessimistic (when everything goes wrong)
    """
    # PERT formula
    expected = (optimistic + 4 * realistic + pessimistic) / 6

    # Standard deviation (uncertainty)
    std_dev = (pessimistic - optimistic) / 6

    return {
        "expected": expected,
        "std_dev": std_dev,
        "range_68%": (expected - std_dev, expected + std_dev),
        "range_95%": (expected - 2 * std_dev, expected + 2 * std_dev)
    }

# Real example: Login API development
result = pert_estimation(
    optimistic=4,   # Best: 4 hours
    realistic=8,   # Reality: 8 hours
    pessimistic=16 # Worst: 16 hours
)

print(f"Expected: {result['expected']:.1f} hours")          # 8.7 hours
print(f"68% probability: {result['range_68%']}")           # (6.7, 10.7)
print(f"95% probability: {result['range_95%']}")           # (4.7, 12.7)

왜 4를 곱하나요?
이 계수는 정상에 가까운 분포에서 가장 가능성이 높은(최빈값) 추정치에 더 큰 가중치를 부여하며, 이는 애자일 팀에 유용합니다.

플래닝 포커

A quick, team‑wide consensus technique:

  1. 모든 사람이 카드를 준비합니다 (1, 2, 3, 5, 8, 13, 21, 34…).
  2. 카드를 동시에 공개합니다.
  3. 추정값이 크게 차이날 경우, 이유를 논의합니다.
  4. 합의된 추정값에 도달합니다.
fibonacci = [1, 2, 3, 5, 8, 13, 21, 34]

심리적 효과: 큰 숫자에 대해 더 넓은 간격을 두어 과도한 정밀도를 방지합니다.

몬테카를로 시뮬레이션

몬테카를로는 무작위 샘플링을 사용하여 프로젝트 완료 시간을 모델링합니다.

import random
import numpy as np

def monte_carlo_simulation(tasks, iterations=1000):
    """Simulate project completion time."""
    results = []

    for _ in range(iterations):
        total_time = 0
        for task in tasks:
            # Randomly select actual time for each task
            actual = random.triangular(
                task['min'],
                task['max'],
                task['likely']
            )
            total_time += actual
        results.append(total_time)

    return {
        "mean": np.mean(results),
        "p50": np.percentile(results, 50),  # Median
        "p90": np.percentile(results, 90),  # 90 % probability
        "p95": np.percentile(results, 95)   # 95 % probability
    }

# Project tasks
tasks = [
    {"name": "Design",      "min": 2, "likely": 3, "max": 5},
    {"name": "Development", "min": 5, "likely": 8, "max": 15},
    {"name": "Testing",     "min": 2, "likely": 3, "max": 6}
]

result = monte_carlo_simulation(tasks)
print(f"50% probability: complete within {result['p50']:.1f} days")
print(f"90% probability: complete within {result['p90']:.1f} days")

속도 기반 추정

과거 스프린트 속도를 활용하여 향후 작업을 예측합니다.

class VelocityEstimator:
    def __init__(self, past_sprints):
        self.velocities = past_sprints

    def estimate(self, total_points):
        avg_velocity = np.mean(self.velocities)
        std_velocity = np.std(self.velocities)

        sprints_needed = total_points / avg_velocity

        return {
            "expected_sprints": sprints_needed,
            "optimistic": total_points / (avg_velocity + std_velocity),
            "pessimistic": total_points / (avg_velocity - std_velocity)
        }

# Past 10 sprint velocities
past_velocities = [23, 28, 25, 30, 22, 27, 26, 24, 29, 26]

estimator = VelocityEstimator(past_velocities)
result = estimator.estimate(total_points=150)

print(f"Expected: {result['expected_sprints']:.1f} sprints")
print(f"Range: {result['optimistic']:.1f} ~ {result['pessimistic']:.1f}")

전문가 합의 (와이드밴드 델파이)

구조화된 다라운드 추정 프로세스:

  1. Round 1 – 익명 제출

    • Dev A: 10일
    • Dev B: 5일
    • Dev C: 15일
  2. Round 2 – 이유 공유 및 재추정

    • A: “DB 마이그레이션을 고려하면…”
    • B: “아, 그걸 놓쳤네요.”
    • C: “테스트 자동화가 포함돼 있나요?”

    새로운 추정값: 8일, 9일, 10일.

  3. Round 3 – 합의

    • 최종 추정: 9일.

권장 사항

접근 방식사용 시점
플래닝 포커빠르고 쉬운 팀 합의
PERT + 속도정확도와 실용성의 균형
몬테카를로 + 와이드밴드 델파이위험 분석이 필요한 고정밀 프로젝트

유사 과거 프로젝트를 통한 추정치 조정

similar_projects = [
    {"name": "Login System A", "estimated": 20, "actual": 35},
    {"name": "Login System B", "estimated": 15, "actual": 28},
    {"name": "Login System C", "estimated": 25, "actual": 40}
]

adjustment_factor = np.mean([p["actual"] / p["estimated"] for p in similar_projects])
# adjustment_factor ≈ 1.73

# Apply to a new raw estimate
new_estimate = raw_estimate * adjustment_factor

스프린트 추정 회고

작업예상실제차이원인
API 개발8 시간12 시간+4 시간인증 복잡도
UI 구현6 시간5 시간–1 시간템플릿 재사용
테스트4 시간8 시간+4 시간엣지 케이스

교훈: 인증 및 테스트 단계는 대략 1.5× 여유를 두어야 합니다.

마무리 생각

  • PERT를 사용하여 불확실성을 계산하십시오.
  • 단일 숫자가 아니라 범위로 추정하십시오.
  • 과거 데이터(속도, 유사 프로젝트)를 활용하십시오.
  • 전체 팀을 참여시키십시오 (Planning Poker, Wideband Delphi).
  • 추정 프로세스를 지속적으로 개선하십시오.

과학적인 추정 및 프로젝트 관리가 필요하신가요? Plexo를 확인해 보세요.

Back to Blog

관련 글

더 보기 »

Agile 소프트웨어 개발 방법: 리뷰 및 분석

Agile 소프트웨어 개발: 팀이 더 빠르게 움직이는 방법 Agile은 우리가 소프트웨어를 만드는 방식을 변화시키고 있습니다. 짧은 작업 사이클, 빠른 수정, 그리고 지속적인 피드백에 의존합니다. ...

Agile 단순성의 빈 약속

애자일 단순성의 문제 > “한 문장으로 표현한 애자일: Inspect and adapt.” > 혹은 “가치를 일찍 그리고 자주 전달한다.” 모든 컨설턴트는 엘리베이터 피...