시리즈SW프로세스 V 모델과 사발 바닥의 밥풀까지 먹는 엔지니어링

Category:
Difficulty:
Date: 2026-04-20
Read Time: 12 mins read
Views: 조회

V 모델: 알뜰한 자의 폭포수 모델

우선, V 모델이 흔하게 쓰이던 소비에트의 조직 특성에 대해 알아보자. 어디까지나 기술 블로그이고, 한편으로는 ‘예고르 레토프를, 드미트리 셀리바노프를, 얀카 댜길레바, 그리고 펑크족과 로큰롤러 모두를’ 구렁텅이로 밀어넣으려 한(그리고 몇몇은 정말로 밀어넣은) 소비에트라는 조직을 좋아하지 않는다. 그러나 그들의 잔혹할 정도로 알뜰한 엔지니어링은 배울 면모가 있음을 인지하고 글을 쓴다.

그들의 조직은 알뜰한 수준이 아니고, 피도 눈물도 없는 식으로 사람들을 쪼아댔다. 밥그릇의 밥풀 하나까지 소중하게 긁어 먹으라고 잔소리하는 할아버지 정도가 아니고, ‘밥풀을 남기면 감봉당한다고 하는 사장’ 수준이었다. 이런 조직은 관리는 이상적이긴 하지만, 이제 막 IT 취업 전선에 발을 들인 내가 겪고 싶지는 않은 조직이다. 그러나 어처구니없게도 그것에는 나름 배울 점이 있었다.

부란 프로젝트: 사람을 갈았더니 그 시대에 자동 착륙을?

우선 이 미친 조직은 폭포수 모델보다 강화된 V 모델의 프로세스를 따르기 위해서 ‘명세에 한 줄 추가할 때마다’ 수식 뭉치를 제출해야 하는 황당한 강도의 정형 검증을 요구했다. 게다가, 실수하는 것에 대해서 조직은 처벌하거나 연구자를 실직자로 만들어 버리는 데에 거침이 없었다. 결과적으로 엔지니어들은 모델의 모든 프로세스 하나를 위해 살인적인 문서 작업을 진행하고, 수만 번도 넘을지도 모르는 회의 압박 속에서 ‘무인 자동 착륙’이라는 어처구니없는 목표를 향해 달려야 했던 것이다.

차라리 시험 운행에서 ‘콰광!’ 하고 무인기가 폭발했다면, ‘거봐, 사람 쪼아 봐야 될 것도 안 된다니까?’로 결론이 난 사례가 되었을지도 모른다. 지금의 정형 검증에 시달리는 엔지니어들을 보아도, 차라리 그 때에 수백 명의 박사가 실업자가 되는 편이 나았을지도 모른다고까지 생각한다. 어쨌건, 이런 엄격한 프로세스는 비극인지 희극인지 그 황당한 목표를 이뤄 내는 것에 더불어, 기상 난항에 대한 자정적 복구까지 성공하였다. 물론 이게 압축되고 압축된 언어가 거봐, 하면 되잖아라던가, 자네, 일단 해 봤어?라는 정말이 되었지만, 어처구니없게도 사람 잠을 안 재운 결과물이 성공한 것은 ‘일단 해 본 것’이 아닌 ‘논리가 맞는 지시와 토의’였다는 점은 확실히 알아 두고 갈 수밖에 없는 지점이었다.

V 모델에 맞게 부란 프로젝트의 워크플로우를 매핑해 보자. 이 경우, 왼쪽과 오른쪽이 있을 것이다.

V의 왼쪽 날개: 요구사항 분석부터 아키텍처 설계까지

  • 요구사항 분석: 조종사가 하던 궤도 진입, 도킹, 지상 착륙을 전부 소프트웨어 자동화로 끝내는 자율 시스템을 구축해라.
  • 시스템 사양: 언어는 PROL2, DIPOL을 직접 설계해 이용한다. 코드 라인 수가 불어날 수 있으니 전용 언어 설계는 필수적이다.
  • 아키텍처 설계: 4중 중복 시스템을 채택하여 결함 시 스스로 복구할 수 있게 한다.

아키텍처 설계: 이거 어디서 많이 봤는데…?

이 부란 프로젝트의 아키텍처는 4대의 컴퓨터가 동일한 계산을 하고, 결과가 다르면 다수결로 결정하여 연산 오류를 방지하게 되어 있었다. 그냥 들으면 그렇구나…하지만 어떤 창작물을 본 사람 입장에선 이것이 떠오를 것이다.

이거…에반게리온 마기 시스템 아니야?

놀랍게도 그게 그거다. 정말 구조가 닮아 있다. 이 시스템은 제로 트러스트를 컴퓨터에까지 적용하여, 4대 중에 한 대는 틀릴 수 있음을 가정한다. 마치 창작물 속의 마기 3대 중 1대는 틀릴 수 있다고 가정하고 의사결정 시스템을 구축한 것과 그 논리가 유사한 것이다.

우주의 예측이 어려운 상황을 생각하면 상부의 이러한 결정은 과하게 안전하여도 무방하다고 할 수 있다. 그러나 조직의 리스크가 곧 우주의 리스크인 이 상황과 달리, 현대 SW 업체들은 많은 부분에서 차이가 있을 수밖에 없다고 생각한다.

V의 밑바닥: 구현

모듈식 설계를 하자

안전이 중요할수록, 각 모듈의 책임 범위를 명확하게 두어야 한다. 우주에 나가는 코드는 수백만, 심하면 수천만 줄의 코드가 얽혀 있다. 이런 규모를 생각한다면, 단순하게 대충 한 객체에 밀어 넣는 설계를 했다간 정말로 우주비행사가 죽을지도 모른다. 그래서 이런 SW에서는 수만 개의 모듈, 또 수백 개의 서브모듈로 코드를 모두 쪼개 관리하는 것이 필수적이다.

물론 프로젝트 부란같은 실무 코드는 내가 소스 트리를 받아올 방법이 없으니 차치하고, 실제로 모듈식 설계, 계획 아래에서의 설계를 진행한 내 프로젝트 하나를 들어서 보도록 하자.

[yjlee@DESKTOP-8OV6J9B libttak]$ tree src/
src/
├── accel
│   ├── accel_cpu.c
│   ├── accel_cuda.cu
│   ├── accel_opencl.c
│   ├── accel_rocm.cpp
│   ├── bigint_cuda.cu
│   ├── bigint_opencl.c
│   ├── bigint_rocm.cpp
│   └── ttak_accel_dispatch.c
├── async
│   ├── function.c
│   ├── future.c
│   ├── promise.c
│   ├── sched.c
│   └── task.c
├── atomic
│   └── atomic.c
├── container
│   ├── pair.c
│   ├── pool.c
│   ├── ringbuf.c
│   └── set.c
├── ht
│   ├── hash.c
│   ├── map.c
│   └── table.c
├── io
│   ├── async.c
│   ├── bits.c
│   ├── io.c
│   ├── sync.c
│   └── zerocopy.c
├── limit
│   └── limit.c
├── log
│   └── logger.c
├── mask
│   └── dynamic_mask.c
├── math
│   ├── bigcomplex.c
│   ├── bigint_accel.c
│   ├── bigint.c
│   ├── bigmul.c
│   ├── bigreal.c
│   ├── calculus.c
│   ├── factor.c
│   ├── floatundidf.c
│   ├── matrix.c
│   ├── ntt.c
│   ├── sum_divisors.c
│   └── vector.c
├── mem
│   ├── abstract.c
│   ├── arena_helper.c
│   ├── detachable.c
│   ├── epoch.c
│   ├── epoch_gc.c
│   ├── fastpath.c
│   ├── mem.c
│   ├── owner.c
│   ├── ttak_mem_pocket.c
│   └── ttak_mem_vma.c
├── mem_tree
│   └── mem_tree.c
├── net
│   ├── core
│   │   └── port.c
│   ├── endpoint.c
│   ├── lattice.c
│   ├── mols_control.c
│   ├── session.c
│   └── view.c
├── phys
│   ├── dimless
│   │   └── transport.c
│   └── mem
│       └── buddy.c
├── priority
│   ├── buf.c
│   ├── heap.c
│   ├── nice.c
│   ├── queue.c
│   ├── scheduler.c
│   └── simple.c
├── script
│   └── bigscript.c
├── security
│   ├── aes_gcm.c
│   ├── chacha20_poly1305.c
│   ├── lea.c
│   ├── security_engine.c
│   ├── seed.c
│   ├── seed_tables.h
│   ├── sha256.c
│   └── siphash.c
├── shared
│   └── shared.c
├── stats
│   ├── stats.c
│   ├── stats_ext.c
│   └── system_usage.c
├── sync
│   └── spinlock.c
├── thread
│   ├── pool.c
│   └── worker.c
├── timing
│   ├── deadline.c
│   └── timing.c
├── tree
│   ├── ast.c
│   ├── bplus.c
│   └── btree.c
└── unsafe
    ├── context.c
    └── region.c

27 directories, 89 files
[yjlee@DESKTOP-8OV6J9B libttak]$ tree include/
include/
├── pthread.h
├── stdatomic.h
└── ttak
    ├── async
    │   ├── function.h
    │   ├── future.h
    │   ├── promise.h
    │   ├── sched.h
    │   └── task.h
    ├── atomic
    │   └── atomic.h
    ├── container
    │   ├── pair.h
    │   ├── pool.h
    │   ├── ringbuf.h
    │   └── set.h
    ├── ht
    │   ├── hash.h
    │   ├── map.h
    │   ├── table.h
    │   └── wyhash.h
    ├── io
    │   ├── async.h
    │   ├── bits.h
    │   ├── io.h
    │   ├── sync.h
    │   └── zerocopy.h
    ├── limit
    │   └── limit.h
    ├── log
    │   └── logger.h
    ├── mask
    │   └── dynamic_mask.h
    ├── math
    │   ├── bigcomplex.h
    │   ├── bigint_accel.h
    │   ├── bigint.h
    │   ├── bigmul.h
    │   ├── bigreal.h
    │   ├── calculus.h
    │   ├── factor.h
    │   ├── matrix.h
    │   ├── ntt.h
    │   ├── sum_divisors.h
    │   └── vector.h
    ├── mem
    │   ├── abstract.h
    │   ├── arena_helper.h
    │   ├── detachable.h
    │   ├── epoch_gc.h
    │   ├── epoch.h
    │   ├── fastpath.h
    │   ├── mem.h
    │   └── owner.h
    ├── mem_tree
    │   └── mem_tree.h
    ├── mols_control.h
    ├── net
    │   ├── core
    │   │   └── port.h
    │   ├── endpoint.h
    │   ├── lattice.h
    │   ├── session.h
    │   └── view.h
    ├── phys
    │   ├── dimless
    │   │   └── transport.h
    │   └── mem
    │       └── buddy.h
    ├── priority
    │   ├── heap.h
    │   ├── internal
    │   │   └── queue.h
    │   ├── nice.h
    │   ├── queue.h
    │   ├── scheduler.h
    │   └── simple.h
    ├── script
    │   └── bigscript.h
    ├── security
    │   ├── lea.h
    │   ├── security_engine.h
    │   ├── seed.h
    │   ├── sha256.h
    │   └── siphash.h
    ├── shared
    │   └── shared.h
    ├── stats
    │   ├── stats_ext.h
    │   ├── stats.h
    │   └── system_usage.h
    ├── sync
    │   ├── spinlock.h
    │   └── sync.h
    ├── thread
    │   ├── pool.h
    │   ├── thread_compat.h
    │   └── worker.h
    ├── timing
    │   ├── deadline.h
    │   └── timing.h
    ├── tree
    │   ├── ast.h
    │   ├── bplus.h
    │   └── btree.h
    ├── ttak_accelerator.h
    ├── types
    │   ├── fixed.h
    │   ├── ttak_align.h
    │   └── ttak_compiler.h
    └── unsafe
        ├── context.h
        └── region.h

29 directories, 84 files
[yjlee@DESKTOP-8OV6J9B libttak]

코드는 다 합쳐서 19000 loc이나, 모듈, 그리고 파일 분리가 충분히 세밀하여 ast를 수정할 때 btree까지 영향을 받는 등의 문제를 만들지 않는다. 물론 이것은 어디까지나 소규모 프로젝트의 예시이나, 부란 프로젝트같은 것은 phys 폴더의 약한 깊이와는 비교도 안 될 정도로 서브모듈 구조 역시 세밀하게 쪼개진다.

그리고, 모든 코드는 어떤 줄이 불필요한 동작을 실행하거나, 불필요한 연산을 활용하지 않는지 알 필요가 있다. 그런 까닭에서라도 정적 분석 프로세스는 아무리 많이 가져가도 부족하다.

V의 오른쪽: 절대로 무너져서는 안 되는, 그리고 양보할 수 없는

과거 소련은 V 모델의 단위 테스트 붕괴로 인하여 N1 로켓 프로젝트를 완전히 실패하였다. 천문학적인 예산을 공중분해시키지 않기 위해서, 프로젝트 부란에서는 이 V의 오른쪽을 채찍질 수준으로 검증했다.

단위 테스트: 모든 모듈은 수학적 무결성을 증명해야 했으며, 아폴로 프로젝트와 마찬가지로 혹독한 코드 리뷰를 통과해야 했다. 통합 테스트: 실제 우주선과 똑같은 전자 장비를 갖춘 지상 시뮬레이터에서 수만 회에 이르는 가상 비행을 통과시킨다. 시스템 테스트: 정상 작동 시의 무결성 뿐만 아니라, 의도적으로 특정 장비의 비행 도중 고장 등에서의 예외 처리를 검증한다. 인수 테스트: 실제 시험 비행을 한다. 이 단계에서 부란은 무인으로 이륙하여 초속 10m가 넘는 강풍이 부는 환경에서도 무인으로 이륙하여 스스로 문제 상황을 복구하고, 1초의 오차도 없이 자동 착륙했다.

대체 N1 프로젝트와 뭐가 달랐을까?

N1 프로젝트의 경우, 엔진의 떨림 등의 조건에 대해 수식을 완전하게 세우지 못한 채로, 불완전한 구현과 실제를 반영하지 못하는 테스트 도구로 ‘문제 없는데?’라고 판단하고 통과시켜 버린 것이다. 이러한 과오를 발생시키지 않기 위해서, 부란을 개발하는 과학자들은 미세한 변수 하나까지 계산에 반영하는 것, 실제 운영 환경과 사실상 동일한 테스트를 설계하고 검증하는 것에 기한의 70% 이상을 쏟아부으며 결과적으론 완전한 자동 이착륙이라는 기염을 토했다. 앞선 포스트에서 김성모 화백의 만화 대사를 인용하며 말한 ‘제대로 이루어지지 못한 개발 모델의 함정’을 개선하기 위해서 모든 노력을 쏟아 부은 결과, 부란은 그 시대 최고 수준의 오버테크놀로지를 구현해 내면서 성공적으로 끝마쳤다,

이것은 조직이 이상적인 프로세스를 차례대로 잘 수행했을 때 정말로 IQ 수천이 모인 기술을 실현할 수 있음을 뜻한다.

결론

조직의 관리 프로세스가 ‘대충 권위 기반’으로 굴러가지 않아야 하는 까닭이 바로 여기에 있다. 어떤 일을 어떻게 할 수 있는지에 대해서 조직이 노력을 충분히 기하지 않으면, 비록 N1 로켓처럼 ‘물리적으로 폭발’하지 않더라도 언제나 문제가 도사린다.

이 사례와 함께 V 모델을 소개하면서 ‘왜 워크플로우를 제한하는지’, 그리고 ‘정상적으로 제약하는 까닭’을 알 수 있었다.

권위 기반의 엉뚱한 제약 조건이 따라 붙지 않도록 각별히 조심하면 조직은 효율적인 성과를 낼 수 있다.

이어서

이어서 소비자들에게 가장 익숙하고, 많은 분야에서 폭넓은 발전을 이룬 일본의 90년대 품질 관리(QC)와 개발론에 대해 소개하며 ‘고전 삼총사’에 대한 소개를 할 것이다. 아마도 일본의 방법론은 우리 한국인들이 직, 간접적으로 가장 많이 영향을 받고 있는 QC/QA 영역에 대한 본가 격이기 때문에, 공장식 개발에 대한 고찰 역시 담길 것으로 생각한다. 마지막으로 ‘히타치 제작소’의 개발 프로세스에 대해 이야기하며 이야기를 끝마치도록 하겠다.

Document Classification

Primary Category
Subcategory
Keywords
소프트웨어 엔지니어링 방법론 성찰
Difficulty
Permalink
https://gg582.github.io/devnote/2026-04-20-%5B%EC%8B%9C%EB%A6%AC%EC%A6%88%5D%5BSW%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%5D-V-%EB%AA%A8%EB%8D%B8%EA%B3%BC-%EC%82%AC%EB%B0%9C-%EB%B0%94%EB%8B%A5%EC%9D%98-%EB%B0%A5%ED%92%80%EA%B9%8C%EC%A7%80-%EB%A8%B9%EB%8A%94-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4%EB%A7%81/

Citation

이윤진(Lee Yunjin) (2026). [시리즈][SW프로세스] V 모델과 사발 바닥의 밥풀까지 먹는 엔지니어링. 윤진의 IT 블로그. Retrieved from https://gg582.github.io/devnote/2026-04-20-%5B%EC%8B%9C%EB%A6%AC%EC%A6%88%5D%5BSW%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%5D-V-%EB%AA%A8%EB%8D%B8%EA%B3%BC-%EC%82%AC%EB%B0%9C-%EB%B0%94%EB%8B%A5%EC%9D%98-%EB%B0%A5%ED%92%80%EA%B9%8C%EC%A7%80-%EB%A8%B9%EB%8A%94-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4%EB%A7%81/
── 하략 ──