← 프로젝트 프로젝트 케이스 스터디

MIDWAY

개입 전에는 결과가 정해져 있고 개입 후에는 아무도 결과를 모르는 1942년 미드웨이 해전 시뮬레이터입니다

여명의 붉은 하늘 아래 기동부대 항모 아카기의 비행갑판에 함재기가 늘어선 화면입니다
역할
1인 프로젝트 — 사료 조사·시스템디자인·개발 전반 (결정론 엔진 + 3D 관전 재생기)
기간
2026년 개인 프로젝트 (진행 중)
스택
Node.js 결정론 틱 엔진 · 이벤트 저널 SSE 스트리밍 · 시드 PRNG · 무번들 three.js · 도커 + Cloudflare Tunnel

MIDWAY는 1942년 6월 4일 하루를 관전하는 웹앱입니다. 설계는 한 문장에서 출발했습니다. 당신은 미드웨이 상공을 떠도는 시간입니다. 어디를 볼지는 고를 수 있지만 결정을 바꾸기 전까지 전투는 실제 역사대로 흘러갑니다. 문제는 관전과 개입이 서로 양립하지 않는다는 것입니다. 관전이 성립하려면 결과가 이미 정해져 있어야 하고 개입이 성립하려면 결과가 열려 있어야 합니다. 해법은 둘을 시간축에서 나눈 것입니다. 개입 지점 이전의 역사 이벤트는 앵커로 고정되어 시뮬레이션을 실제 결과 쪽으로 부드럽게 보정합니다. 개입한 순간 그 아래의 앵커가 전부 해제되고 이후는 규칙만으로 진행합니다. 이 경계 전환 때문에 개입이 실제로 결과를 바꿉니다.

만드는 순서는 사료가 먼저였습니다. 엔진보다 데이터를 앞에 두었습니다. 조사는 3차에 걸쳐 적대적 검증으로 돌렸고 셋 중 둘이 반박하면 폐기했습니다. 1차는 25개 주장 중 23개를 확정하고 2개를 폐기했고 2차는 25개 중 21개를 확정했고 3차는 9개를 전부 확정했습니다. 가장 먼저 부딪힌 것은 시계였습니다. 사료마다 시계가 달라서 미드웨이 현지 시각과 미 항모의 시각과 도쿄 시각이 뒤섞여 있습니다. 마스터 클록을 미드웨이 현지로 정하고 나머지를 정규화하는 것이 데이터 설계의 첫 문제였습니다. 그 다음이 구조화 데이터와 개입 로직이고 결정론 엔진 코어는 그 뒤에 나왔습니다. 처음 만든 재생기는 2D였습니다. 함대는 격자 위의 점이고 피드는 영문 사료 문장 그대로였지만 화면 위쪽에는 지금과 같은 문장이 이미 있었습니다. 한 곳을 보면 다른 곳을 놓친다는 문장입니다. 개입 UI도 그 2D 위에서 먼저 만들었습니다. 결정 카드에 선택지를 셋 늘어놓고 그중 하나에 사실을 표시하는 형태는 그때 정해져 지금까지 같습니다. 지금은 3D 재생기가 정본이고 2D는 보조로 남았습니다. 3D 연출 상당수는 2D 렌더러 구조상 이식할 수 없어서 이식하지 않기로 하고 그 결정을 문서로 기록했습니다. 데이터 계층은 둘이 공유합니다.

판정은 전부 서버가 합니다. 1분이 1틱인 결정론 틱루프가 돌고 모든 확률 판정은 시드 PRNG 하나에서만 나오고 산출물은 append-only 이벤트 저널입니다. 클라이언트는 저널을 받아 재생과 보간만 합니다. 시뮬레이션 로직은 두지 않습니다. 이 분리에서 관전 UX의 어포던스가 나옵니다. 배속은 틱을 얼마나 빨리 방출하느냐의 문제일 뿐이라 300배속으로 감아도 물리 결과가 같고 되감기와 유닛 추적과 이 편대의 행적 전체 보기가 저널 위에서 추가 구현 없이 나옵니다.

이 프로젝트를 지탱하는 제약은 해시 하나입니다. 개입 없이 재생한 저널의 해시가 3dd83ce5f2ca94f3이어야 하고 커밋 전에 확인합니다. 이것은 테스트를 넘어선 설계 제약입니다. 카메라와 연출과 UI를 아무리 더해도 해시가 흔들리면 그것은 역사를 건드린 것입니다. 함선이 파도를 타고 오르내리고 좌우로 기울고 선수를 들었다 놓는 움직임과 항적과 선수파와 굴뚝 배연과 야간 등화관제를 더한 열네 항목이 전부 표시 전용으로 들어간 것도 이 기준 때문입니다. 연출은 진실을 바꿀 수 없다는 규율을 기계가 판정합니다. 해시는 한 번 갱신했고 사유는 사료 보강이었습니다. 카가·소류·아카기의 침몰 이벤트 세 건을 추가하니 이벤트가 38건에서 41건이 되었습니다. 해시는 사료가 바뀌었을 때만 변경합니다.

개입 수단은 결정 노드 다섯 개와 연산 여섯 개로 좁혔습니다. 도네 4호기의 색적 지연과 나구모의 무장 교체와 호넷 급강하대의 항로와 매클러스키의 색적 결정과 히류의 반격 타이밍입니다. 각 노드에는 사실로 표시한 선택지가 정확히 하나씩 있고 그 선택은 델타를 가질 수 없습니다. 노드는 분기점이라 개입하지 않으면 사실 선택이 자동으로 적용됩니다. 대안을 고르면 이벤트 취소·시각 이동·활성화·주입과 유닛 속성·월드 플래그 설정이라는 여섯 개의 동사로 델타를 표현하고 지정된 전구에 한해 앵커를 해제합니다. 시뮬레이션이 옳게 도는 것과 관전자가 그것을 이해하는 것은 다른 문제라 인과를 이해할 수 있게 하는 표시층을 따로 만들었습니다. 상류에서 분기하면 하류 노드의 브리핑 문장 자체가 개정됩니다. 도네 4호기를 정시에 띄우면 나구모의 카드에 결심 시간이 줄었다는 전제가 더해집니다.

개입 이후는 다섯 개 층이 계산합니다. 탐지와 항공운용과 강습 이동과 교전과 손상통제가 매 틱 순서대로 평가되고 그 순서가 곧 인과 사슬입니다. 창발은 인적요소가 층을 가로질러 되먹일 때 생깁니다. 항모를 잃은 쪽은 사기가 떨어져 못 맞히고 불도 못 끄고 반격도 늦습니다. 숫자는 사료에 묶어 두었습니다. 몬테카를로 하네스가 명중 확률과 소모율을 실제 명중 수에 맞춰 캘리브레이션하고 일곱 개 검증 케이스는 전부 원 사료를 답니다. 미 뇌격대 41기의 어뢰 명중 0과 미드웨이 기지 항공대 27기의 명중 0도 재현해야 할 값입니다. 여기에 시스템이 이야기를 만드는지 확인하는 검사 열여섯 건을 따로 만들었습니다. 하나는 숫자가 사료와 맞는지를 지키고 하나는 규칙이 제대로 작동하는지를 지킵니다.

데이터 스키마부터 사료를 그대로 반영했습니다. 명중 수는 현대 학술치와 1942년의 과대보고를 나눠 기록하고 판정은 학술치로 합니다. 과대보고는 전장의 안개를 연출할 때만 씁니다. 모르는 것은 세 가지로 나눴습니다. 아직 못 찾은 것과 찾아봤더니 학계에도 없는 것과 사료끼리 어긋나는 것입니다. 도네 4호기가 왜 늦었는지는 두 번째라 결정 카드에 지연의 기계적 원인은 문헌 미상이라고 그대로 적었습니다. 히류의 발진 시각은 세 번째라 대안 시각을 배열로 저장합니다. 함대 궤적도 분 단위 실측은 도판을 직접 판독해야만 가능해서 문서화된 앵커 사이를 보간하는 근사로 확정하고 정밀화는 영구히 취소했습니다. 과장된 정밀도를 피했습니다.

관전은 사용자가 아무것도 하지 않아도 되어야 하지만 실시간이면 지루하고 고배속이면 놓칩니다. 그래서 사건 밀도로 배속을 스스로 조절합니다. 빈 바다는 최대 서른 배로 감고 사건 근처에서 부드럽게 내려가고 전 사건이 두 배 이내로 감속하도록 이징을 비대칭으로 손봤습니다. 카메라도 마찬가지입니다. 어뢰 런은 발사 주체와 진행선과 표적 임팩트의 세 비트로 끊고 항모의 사망타에서는 화각을 좁히고 거리를 보정해 현기증을 만들고 태양이 낮게 걸린 시각의 침몰은 저각 백라이트 실루엣으로 연출합니다. 다만 이 연출은 전부 자동 재생일 때만 작동합니다. 사용자가 화면에 손을 대는 순간 전부 물러납니다.

마지막으로 자기 제품을 의심하는 일이 남았습니다. 각 분기를 시드 800개로 돌려 결말 분포를 재 봤습니다. 개입하지 않으면 결말은 한 가지이고 개입하면 여덟에서 열 가지로 갈라집니다. 개입은 결과를 바꾸기보다 결과의 개수를 하나에서 열로 늘리는 쪽에 가깝습니다. 여기서 불편한 것이 나왔습니다. 매클러스키가 회항하는 분기에서 앱이 상영하는 전개는 미국 승리입니다. 그런데 그 결말은 모델 전체로 보면 4.6%의 희귀한 꼬리이고 시드의 78%에서는 일본이 이깁니다. 앱이 그 분기의 전형이 아니라 드문 표본을 보여주고 있었던 것입니다. 반사실을 만들겠다고 사료를 세 번 검증해 놓고 정작 관전자에게는 오해를 부르는 표본을 상영하고 있었습니다. 개선 방향은 문서에 기록했습니다. 분기마다 대표 표본으로 시드를 고르거나 이 전개는 N% 확률의 한 시나리오라는 분포 맥락을 카드에 노출하는 것입니다. 후자가 더 이 프로젝트답습니다. 분포에서 표본 하나를 상영하고 있을 뿐이라는 사실을 UI로 인정하는 쪽이기 때문입니다.

← 프로젝트로 돌아가기