"WBS 없이 프로젝트 하는 건 지도 없이 등산하는 것과 같다"
PM이 이런 말을 하면 개발자들은 눈을 굴립니다. "또 문서 작업이야?"
이런 경험 있으신가요?
- "프로젝트 계획? 그냥 Jira에 태스크 몇 개 만들면 되는 거 아닌가?"
- "WBS 만드는 시간에 코딩 한 줄이라도 더 하는 게 낫지 않을까?"
- "어차피 요구사항 바뀔 텐데 뭐하러 시간 낭비해?"
많은 프로젝트가 데드라인을 놓치고, 팀원들이 번아웃되는 것을 보면서 깨달은 게 있습니다. 문제는 능력이 아니라 체계였다는 것을.
오늘은 복잡한 이론 없이, 당장 실무에 적용할 수 있는 5단계 WBS 작성법을 공유합니다.
시작하기 전에: 실패하는 WBS vs 성공하는 WBS
실패하는 WBS의 공통점
많은 팀이 WBS를 만들긴 하지만, 실제로 활용하지 못합니다. 왜일까요?
const bad_wbs = {
너무_복잡: "100개 이상 레벨로 나눔. 엑셀 파일만 100MB",
너무_단순: "'개발', '테스트', '배포' 3줄로 끝",
현실_무시: "버퍼 0%, 모든 작업이 최선의 시나리오",
담당자_없음: "모든 작업이 '팀' 담당. 결국 아무도 안 함"
};
// 결과: WBS는 있지만 아무도 안 봄
성공하는 WBS의 비밀
성공하는 WBS는 살아있는 문서입니다. 매일 열어보고, 업데이트하고, 의사결정의 기준이 됩니다.
const good_wbs = {
적정_복잡도: "3-4 레벨, 한 화면에서 전체 구조 파악",
구체적: "각 작업이 하루 이내로 완료 가능",
현실적: "과거 데이터 기반, 버퍼 포함",
명확한_책임: "모든 작업에 실명 담당자"
};
// 결과: 팀 전체가 매일 확인하는 나침반
Step 1: 프로젝트 스코프 명확히 하기 (30분)
첫 단추를 잘못 끼우면 모든 게 틀어집니다. 스코프 정의는 WBS의 첫 단추입니다.
마법의 질문 3개
프로젝트 킥오프 미팅에서 이 3가지 질문에 답하지 못하면, 실패할 확률이 80%입니다.
1. "완료"의 정의는 무엇인가?
많은 프로젝트가 "거의 다 됐는데..."라는 말과 함께 지연됩니다. 왜일까요? '완료'의 기준이 모호하기 때문입니다.
❌ 모호한 완료 정의:
"로그인 기능 구현"
✅ 명확한 완료 정의:
"로그인 기능 구현"
- 이메일/패스워드 로그인 API 완성
- JWT 토큰 발급 및 검증 구현
- 비밀번호 5회 실패 시 계정 잠금
- 단위 테스트 커버리지 80% 이상
- API 문서 작성 완료
- 스테이징 서버 배포 및 QA 통과
2. 반드시 포함되어야 하는 것은?
MVP(Minimum Viable Product)와 MLP(Minimum Lovable Product)를 구분해야 합니다.
작년에 한 스타트업이 "SNS 로그인도 당연히 포함이죠?"라고 막판에 물어봤습니다. 결과? 2주 지연.
3. 명시적으로 제외하는 것은?
"아, 그것도 당연히 포함인 줄 알았는데요?"라는 말이 프로젝트 막판에 나오면 재앙입니다.
실전 스코프 문서 템플릿
이런 템플릿을 사용하면 스코프가 명확해집니다:
프로젝트: AI 챗봇 서비스 v1.0
기간: 2024.02.01 - 2024.04.30 (3개월)
예산: 5천만원
✅ IN SCOPE (반드시 포함):
- 텍스트 기반 대화 (한국어만)
* 사용자 질문 이해
* 컨텍스트 기반 답변
* 대화 히스토리 관리
- 5개 카테고리 지식베이스
* 제품 정보 (1,000개 항목)
* FAQ (500개 항목)
* 사용 가이드
* 문제 해결
* 회사 정책
- 웹 인터페이스
* 반응형 디자인 (모바일/데스크톱)
* 실시간 채팅 UI
* 대화 내역 저장/불러오기
❌ OUT OF SCOPE (명시적 제외):
- 음성 인식/TTS (v2.0에서 구현)
- 다국어 지원 (영어, 일본어는 Phase 2)
- 네이티브 모바일 앱 (웹뷰로 대체)
- 외부 CRM 연동 (별도 프로젝트)
- 실시간 상담사 연결 (v2.0)
- 파일 업로드/다운로드
🎯 SUCCESS CRITERIA (성공 기준):
- 기술적 지표
* 응답 시간 < 2초 (95 percentile)
* 정확도 > 85% (자체 평가 기준)
* 동시 접속 100명 처리
* 99.9% 가동률 (월 43분 이하 다운타임)
- 비즈니스 지표
* 일일 활성 사용자 1,000명
* 평균 대화 길이 5턴 이상
* 사용자 만족도 4.0/5.0 이상
이렇게 명확히 정의하면, 나중에 "이것도 해주세요"라는 요청이 들어왔을 때 "스코프 문서를 보시면, 이 기능은 v2.0에 포함되어 있습니다"라고 명확히 답할 수 있습니다.
Step 2: 작업 분해하기 - MECE 원칙 (1시간)
MECE(Mutually Exclusive, Collectively Exhaustive)는 맥킨지에서 만든 원칙입니다. "중복 없이, 누락 없이"라는 뜻이죠.
계층 구조의 황금률
너무 깊으면 관리가 어렵고, 너무 얕으면 의미가 없습니다. 제 경험상 최적의 구조는 이렇습니다:
Level 1: 프로젝트 전체
"AI 챗봇 서비스 구축"
Level 2: 주요 단계 (5-7개)
"기획", "백엔드 개발", "프론트엔드 개발", "테스트", "배포"
Level 3: 구성 요소 (각 3-5개)
백엔드 개발 > "API 서버", "데이터베이스", "AI 모델 연동"
Level 4: 작업 패키지 (8-40시간)
API 서버 > "사용자 인증 API (12h)", "대화 API (16h)"
실전 예제: Todo 앱을 해부하다
간단해 보이는 Todo 앱도 제대로 분해하면 이렇게 됩니다:
📱 Todo 앱 개발 (240h = 6주 * 40h)
├── 1. 기획 및 설계 (40h) [담당: PM + 전체팀]
│ ├── 1.1 요구사항 분석 (16h)
│ │ ├── 1.1.1 사용자 인터뷰 (8h)
│ │ │ * 타겟 사용자 5명 심층 인터뷰
│ │ │ * 페인포인트 도출
│ │ │ * 우선순위 정리
│ │ └── 1.1.2 기능 명세서 작성 (8h)
│ │ * 유저 스토리 20개 작성
│ │ * 수락 기준 정의
│ │ * 기술 제약사항 문서화
│ │
│ └── 1.2 UI/UX 설계 (24h) [담당: 디자이너]
│ ├── 1.2.1 와이어프레임 (8h)
│ │ * 10개 주요 화면 스케치
│ │ * 사용자 플로우 차트
│ ├── 1.2.2 디자인 시안 (12h)
│ │ * Figma 고해상도 디자인
│ │ * 다크모드 대응
│ │ * 디자인 시스템 구축
│ └── 1.2.3 프로토타입 (4h)
│ * 클릭 가능한 프로토타입
│ * 사용성 테스트
├── 2. 백엔드 개발 (80h) [담당: 백엔드 2명]
│ ├── 2.1 API 서버 (40h)
│ │ ├── 2.1.1 환경 설정 (8h) [Bob]
│ │ │ * Node.js + Express 셋업
│ │ │ * TypeScript 설정
│ │ │ * 린터, 포매터 설정
│ │ │ * Docker 환경 구성
│ │ │
│ │ ├── 2.1.2 CRUD API (16h) [Alice]
│ │ │ * POST /todos - 할일 생성
│ │ │ * GET /todos - 목록 조회 (페이지네이션)
│ │ │ * PUT /todos/:id - 수정
│ │ │ * DELETE /todos/:id - 삭제
│ │ │ * PATCH /todos/:id/complete - 완료 처리
│ │ │
│ │ ├── 2.1.3 인증 시스템 (12h) [Bob]
│ │ │ * JWT 토큰 발급/검증
│ │ │ * 리프레시 토큰 구현
│ │ │ * OAuth 2.0 (구글, 카카오)
│ │ │
│ │ └── 2.1.4 에러 처리 (4h) [Alice]
│ │ * 글로벌 에러 핸들러
│ │ * 커스텀 에러 클래스
│ │ * 에러 로깅 (Sentry)
여기서 중요한 포인트는 각 작업이 구체적인 산출물을 가진다는 것입니다. "API 개발"이 아니라 "POST /todos 엔드포인트 구현"처럼 명확히 정의합니다.
작업 크기 검증 체크리스트
모든 Level 4 작업은 이 기준을 통과해야 합니다:
const validateTask = (task) => {
const rules = {
// 시간 규칙: 너무 작으면 관리 오버헤드, 너무 크면 추정 부정확
timeRule: task.hours >= 4 && task.hours <= 40,
// 담당자 규칙: "팀"이 아닌 실제 사람 이름
ownerRule: task.owner !== "팀" && task.owner !== "누군가",
// 산출물 규칙: 완료를 검증할 수 있는 구체적 결과물
deliverableRule: task.deliverable !== undefined,
// 측정 규칙: 진행률을 측정할 수 있는가
measurableRule: task.acceptanceCriteria.length > 0,
// 독립성 규칙: 다른 작업과 명확히 구분되는가
independentRule: !task.overlapsWith.includes(otherTasks)
};
return Object.values(rules).every(rule => rule === true);
};
// 좋은 예
const goodTask = {
name: "사용자 로그인 API",
hours: 12,
owner: "김백엔드",
deliverable: "POST /auth/login 엔드포인트",
acceptanceCriteria: [
"이메일/패스워드 검증",
"JWT 토큰 발급",
"5회 실패 시 계정 잠금",
"응답시간 < 200ms"
]
};
// 나쁜 예
const badTask = {
name: "백엔드 개발",
hours: 160, // 너무 큼
owner: "백엔드팀", // 모호함
deliverable: "API 완성", // 추상적
acceptanceCriteria: [] // 검증 불가
};
Step 3: 의존관계 파악하기 (30분)
병목 찾기의 기술
가장 큰 실수 중 하나는 모든 작업을 순차적으로 배열하는 것입니다. 병렬로 진행할 수 있는 작업을 찾아내면 일정을 크게 단축할 수 있습니다.
# 의존성 매트릭스
dependencies = {
"DB 스키마 설계": [], # 독립적
"API 서버 셋업": [], # 독립적
"프론트 셋업": [], # 독립적
"API 개발": ["DB 스키마 설계", "API 서버 셋업"],
"프론트 개발": ["프론트 셋업"], # API와 병렬 가능!
"Mock API": ["API 서버 셋업"], # 프론트팀을 위한 Mock
"통합 테스트": ["API 개발", "프론트 개발"],
"배포": ["통합 테스트"]
}
# 병렬 작업 식별
parallel_tasks = [
["DB 스키마 설계", "API 서버 셋업", "프론트 셋업"],
["API 개발", "프론트 개발 (with Mock API)"],
]
Critical Path 찾기
프로젝트에서 가장 긴 경로(Critical Path)를 찾으면, 어떤 작업이 지연되면 안 되는지 알 수 있습니다.
const findCriticalPath = (tasks) => {
// 각 작업의 최대 경로 길이 계산
const paths = {
"요구사항": 2, // 2일
"디자인": 5, // 요구사항(2) + 자체(3)
"백엔드": 10, // 요구사항(2) + 자체(8)
"프론트": 11, // 디자인(5) + 자체(6)
"테스트": 13, // max(백엔드(10), 프론트(11)) + 자체(2)
"배포": 14 // 테스트(13) + 자체(1)
};
// Critical Path: 요구사항 → 디자인 → 프론트 → 테스트 → 배포
// 이 경로상의 작업이 지연되면 전체 프로젝트가 지연됨
return {
critical_path: ["요구사항", "디자인", "프론트", "테스트", "배포"],
total_duration: 14,
buffer_needed: ["프론트", "테스트"] // 위험도 높은 작업
};
};
Step 4: 시간 추정하기 - PERT 기법 (1시간)
3점 추정법의 마법
하나의 추정값만 사용하면 거의 항상 틀립니다. 대신 3개의 값을 사용하세요:
def calculate_pert_estimate(optimistic, most_likely, pessimistic):
"""
PERT (Program Evaluation and Review Technique) 추정
NASA에서 개발한 과학적 추정 방법
"""
# PERT 공식: (O + 4M + P) / 6
estimate = (optimistic + 4 * most_likely + pessimistic) / 6
# 표준편차: 불확실성의 척도
std_deviation = (pessimistic - optimistic) / 6
# 신뢰구간
confidence_68 = (estimate - std_deviation, estimate + std_deviation)
confidence_95 = (estimate - 2*std_deviation, estimate + 2*std_deviation)
return {
"estimate": estimate,
"std_dev": std_deviation,
"68%_확률": f"{confidence_68[0]:.1f} ~ {confidence_68[1]:.1f}시간",
"95%_확률": f"{confidence_95[0]:.1f} ~ {confidence_95[1]:.1f}시간"
}
# 실전 예제: 로그인 API 개발
login_api = calculate_pert_estimate(
optimistic=4, # 모든 게 순조로우면
most_likely=8, # 보통은
pessimistic=16 # 최악의 경우
)
print(f"추정: {login_api['estimate']:.1f}시간") # 8.7시간
print(f"68% 확률로 {login_api['68%_확률']}")
print(f"95% 확률로 {login_api['95%_확률']}")
팀 생산성 계수
같은 작업도 사람마다 속도가 다릅니다. 이를 반영해야 현실적인 추정이 됩니다:
const team_productivity = {
"시니어 (5년+)": {
coefficient: 1.0,
tasks_per_day: 2.5,
bug_rate: 0.05
},
"미드레벨 (2-5년)": {
coefficient: 1.5, // 시니어 대비 1.5배 시간 필요
tasks_per_day: 1.7,
bug_rate: 0.10
},
"주니어 (0-2년)": {
coefficient: 2.5, // 시니어 대비 2.5배 시간 필요
tasks_per_day: 1.0,
bug_rate: 0.20,
need_review: true // 추가 리뷰 시간 필요
}
};
// 실제 적용
const adjustEstimate = (baseHours, developer) => {
const adjusted = baseHours * team_productivity[developer].coefficient;
const withBuffer = developer === "주니어" ? adjusted * 1.3 : adjusted;
return Math.ceil(withBuffer);
};
Step 5: 버퍼 전략과 리스크 관리 (30분)
버퍼는 낭비가 아니라 보험이다
"버퍼 30% 추가"라고 단순하게 접근하면 안 됩니다. 작업 특성에 따라 다르게 적용해야 합니다:
def calculate_smart_buffer(task):
"""작업 특성에 따른 스마트 버퍼 계산"""
base_buffer = 0.1 # 기본 10%
# 리스크 요소별 추가 버퍼
risk_factors = {
"신기술": 0.3 if task.uses_new_tech else 0,
"외부의존": 0.2 if task.has_external_dep else 0,
"복잡도": 0.15 if task.complexity > 7 else 0,
"첫시도": 0.25 if task.first_time else 0,
"주니어담당": 0.2 if task.assignee == "junior" else 0
}
total_buffer = base_buffer + sum(risk_factors.values())
# 최대 버퍼는 50%로 제한 (그 이상이면 작업 재정의 필요)
return min(total_buffer, 0.5)
# 예제
risky_task = {
"name": "결제 시스템 통합",
"base_hours": 40,
"uses_new_tech": True, # Stripe 첫 사용
"has_external_dep": True, # 외부 API
"complexity": 8,
"first_time": True,
"assignee": "junior"
}
buffer = calculate_smart_buffer(risky_task) # 45% 버퍼
final_estimate = 40 * (1 + buffer) # 58시간
프로젝트 버퍼 vs 작업 버퍼
개별 작업마다 버퍼를 넣으면 전체 일정이 비현실적으로 늘어납니다. 대신 전략적으로 배치하세요:
const buffer_strategy = {
// ❌ 나쁜 방법: 모든 작업에 30% 버퍼
bad_approach: {
task1: "8h + 2.4h buffer = 10.4h",
task2: "12h + 3.6h buffer = 15.6h",
task3: "6h + 1.8h buffer = 7.8h",
total: "33.8h (너무 보수적)"
},
// ✅ 좋은 방법: 프로젝트 버퍼 풀
good_approach: {
task1: "8h",
task2: "12h",
task3: "6h",
project_buffer: "5h (전체의 20%)",
total: "31h (현실적)",
usage: "Critical Path 작업이 지연될 때만 사용"
}
};
실전 체크리스트
WBS 작성이 끝났다면, 이 체크리스트로 검증하세요:
□ 모든 작업이 4-40시간 사이인가?
□ 각 작업에 실명 담당자가 있는가?
□ 의존관계가 명확한가?
□ Critical Path를 식별했는가?
□ 3점 추정을 사용했는가?
□ 적절한 버퍼가 있는가?
□ 리스크 대응 계획이 있는가?
□ 완료 기준이 명확한가?
□ 팀 전체가 리뷰했는가?
□ 도구에 입력했는가? (엑셀/Jira/etc)
마무리: WBS는 지도다
WBS 없는 프로젝트는 지도 없이 등산하는 것과 같습니다.
처음엔 시간 낭비처럼 보여도, 한 번 제대로 만들어두면:
- 진행 상황을 한눈에 파악
- 문제 발생 시 빠른 대응
- 팀원 간 명확한 역할 분담
- 고객과의 투명한 소통
한 스타트업이 WBS 없이 시작했다가 3개월 프로젝트가 6개월로 늘어난 걸 봤습니다. 다음 프로젝트에서 WBS를 도입했더니? 2.5개월 만에 런칭했죠.
차이는 단 하나, 체계적인 계획이었습니다.
여러분도 오늘부터 WBS로 프로젝트의 안개를 걷어내 보세요!
체계적인 WBS 관리 도구가 필요하신가요? Plexo를 확인해보세요.
#WBS #프로젝트관리 #실전가이드 #개발PM #작업분해구조