1. 핵심 주장 & 논지
프리마겐은 Odin 언어로 타워 디펜스 게임을 약 80% 완성한 상태에서 이번 개발 일지를 공개한다. 핵심 메시지는 두 가지다.
첫째, **직접 만든 UI 프레임워크(스택 기반 요소 시스템)**가 예상보다 훨씬 만족스러웠다는 것. 원래 Lua와 2D 그래픽 작업을 별로 좋아하지 않았는데, Odin이라는 언어 자체에 대한 애정 때문에 선택했고 그 결정에 확신을 갖게 됐다.
둘째, 게임처럼 상태가 계속 바뀌는 소프트웨어에서 레이아웃/UI 회귀(regression)를 어떻게 안전하게 잡아낼 것인가라는 문제를 "데모 테스트(Demo Test)"라는 자체 골든 스냅샷 테스트 시스템으로 해결했다는 것. 유닛 테스트를 싫어하던 그가, 이 방식 덕분에 처음으로 테스트에 대해 "정말 만족스럽다"고 말한다.
2. 세부 내용 & 근거
UI 시스템: 스택 기반 요소 구축
- 기존에 구축해오던 UI 시스템을 스택 기반(stack-based) 요소 구축 방식으로 전환했다.
- 요소를 만들 때마다 "열기(open)" 프로세스를 시작하고, 스택 기반 호출을 여러 번 수행해 트리 구조를 만든다.
- 트리의 어떤 부분이든 다른 모든 트리를 호출할 수 있고, "열기" 메서드는 요소를 열어둔 상태로 남겨두기 때문에 if 문 안에서 요소의 위치·배치·애니메이션을 조건부로 편집할 수 있다.
- 그 결과 같은 카드(card) 요소가 렌더링되는 위치에 따라 완전히 다르게 렌더링될 수 있다. "정말 멋지고 아름다운 경험을 할 수 있어서 너무 행복하다"고 표현.
- 빌더 내부에 직접 플렉스(Flex) 요소를 구현해서 어떤 축(axis)에 있는지 지정할 수 있게 만들었고, 유니온(union) 타입을 도입해 요소의 하위 타입(subtype)을 만들어 다양한 변형을 적용하고 있다.
- 본인 평가: "내가 만든 UI 프레임워크가 이렇게 마음에 든다니 믿기지 않는다."
레이아웃 시스템의 가장 큰 문제: 예측 불가능한 파급 효과
- 게임에 아주 작은 변경을 가하는 순간, 예상치 못한 방식으로 레이아웃 시스템 전체에 영향을 미칠 수 있다는 게 가장 큰 두려움이었다.
- "개발 시작 일주일쯤 지나서 레이아웃 일부가 잘못됐다는 걸 깨닫고, 왜 이상하게 보이는지 원인을 알아내려고 헤매는" 상황을 원하지 않았다고 강조.
해법: 데모 테스트(Demo Test) 러너
- 디스플레이 플래그를 추가해 모든 데모 파일을 불러와 하나씩 재생하는 전용 데모 실행 프로그램을 만들었다.
- 실행하면 실시간으로 확인 가능: 부모→자식으로 곱셈 방식으로 전달되는 불투명도(opacity), 고정 위치 오프축(off-axis) 정렬로 움직이는 중앙 요소, 통계 테이블, 애니메이션 등이 화면에서 그대로 재생된다.
- 가장 좋은 점: 해당 디스플레이(장면)를 골드(golden) 버전 이미지와 비교해 픽셀 단위로 테스트할 수 있다는 것. 예: "골든 리트리버(Golden Retriever)"라는 이름을 검색하면 그 데모/장면들이 화면에 나온다 (골든 이미지에 대한 언어유희).
- 프로그램이 모든 데모 장면을 불러와 기록해두고, 마우스를 클릭할 때마다 저장된 장면들이 번갈아 전환되며, 프로그램에 변경을 가할 때마다 모든 장면이 항상 동일한 결과를 내는지 검증한다.
테스트 프레임(Test Frame) 개념
- 시간 테스트 프레임 / 마우스 테스트 프레임이라는 아이디어를 도입.
- 마우스 테스트 프레임 = 지속 시간(duration) + 마우스 상태(위치, 왼쪽 버튼 눌림 여부).
- 즉 "마우스를 잡고 500밀리초 동안 이 위치로 옮겨줘" 같은 명령을 시간 순서로 기록해두면, 실제 마우스 움직임이 지나간 것처럼 프로그램을 통해 재생할 수 있다 → UI 효과, 확대/축소, 움직임, 애니메이션까지 전부 재현.
- 모든 프레임이 끝날 때마다 스크린샷을 찍어 저장하도록 지시할 수 있다. 화면 대신 텍스처에 렌더링한 뒤, 필요하면 화면에 그리거나 파일로 저장 — 이후 실행마다 그 파일을 불러와 비교 테스트를 한다.
- 이 파이프라인이 완성되면 "에이전트 작업 파일 완료"라고 부를 수 있는 수준의 자동화가 된다: 모든 유닛 테스트 실행, Windows/macOS/Darwin/ARM64 등 여러 플랫폼 빌드 검증, 그리고 모든 데모가 완전히 동일하게 재생되는지까지 한 번에 확인.
유닛 테스트에 대한 태도 변화
- 원래 유닛 테스트를 정말 싫어했던 이유: 하드코딩된 값이 너무 많아서 일일이 같은지 확인해야 하고, 드래그 값이 1픽셀만 틀려도 모든 게 깨지는 "항상 똑같은 엉망진창"이었다는 것.
- 그러나 이제는 유닛 테스트로 소규모 범위에서 기본 동작을 증명하고, 마우스 움직임·애니메이션이 실제로 구현되는 통합/엔드투엔드 테스트를 데모 시스템으로 수행하는 조합에 매우 만족한다고 밝힘. "저는 정말정말 만족하고 있다"고 두 번 강조.
다음 목표: "스스로에게 해를 끼치지 않는" 개발 방식
- 지난 한 주 동안 고민한 것은 "새 기능을 추가해도 최소한 제대로 작동한다는 걸 알 수 있게" 만드는 워크플로우.
- 현재 작업 중인 기능: 레벨을 생성하고 볼 수 있게 하되, 매번 정확히 동일한 레벨이 결정론적으로(deterministic) 생성되도록 하는 것.
- 모든 디스플레이 타일이 항상 동일한 방식으로 렌더링되고, 타일 높이 차이(어떤 타일은 높고 어떤 타일은 낮음)나 색상 스펙트럼(초록~빨강)까지 동일하게 유지되는지 검증.
- 탑이나 걷는 사람 같은 데모를 추가해도 게임이 결정론적이어야 하므로 항상 같은 걸음걸이, 같은 탑, 같은 장면이 나와야 한다.
- 문제가 생기면 로그를 켜서 "이건 확실히 페이블(fable, 오류의 의미로 쓴 표현) 문제는 아니다, 뭐가 문제인지 알려달라"는 식으로 두 이미지 간 픽셀 차이를 비교해 무엇이 바뀌었는지 알아낼 수 있다 → 큰 변경을 시도했을 때 무엇이 잘못됐는지 빠르게 파악 가능.
- 워크플로우: 변경을 적용하고 모든 것을 업데이트하려면
odin run명령 한 번으로 새로운 골든 이미지를 전부 저장 → 이후 실행이 동일한 이미지를 생성하는지, 아니면 새롭게 업데이트된 이미지를 생성하는지 확인 가능.
3. 사례 & 데이터
- 개발 언어 전환: Lua(80% 완성) → Odin(C와 유사한 단순한 문법, 애정 때문에 선택).
- 코드 규모: 현재 약 13,000줄. 본인 비교: "나는 게리 탄(Gary Tan)처럼 하루 3만 줄을 뽑는 게 아니라, 하루 1,000~1,500줄 정도 생산한다"고 자기 비하 개그.
- 마무리 드립: "팔에 힘이 없고 손바닥이 떨리는 채로 엄마가 해준 스파게티를 먹고 있다"며 에미넴(Eminem) "Lose Yourself" 가사("palms are sweaty, knees weak, arms are heavy... mom's spaghetti")를 패러디해 "안녕, 내 이름은 프리마겐입니다"로 영상을 마무리.
- 테스트 대상 플랫폼: Windows, macOS, Darwin, Darwin AMD/ARM64 등 다중 빌드 타깃.
- 다음 주 목표(축소된 범위): 원래는 "카드 → 타워 배치 → 화살 발사 → 적 처치"까지 구현하고 싶었지만, 과한 욕심이라 판단해 "카드, 타워, 다음 레벨"까지로 스코프를 줄일 계획.
4. 실행 포인트 & 시사점
- 골든 스냅샷 테스트를 UI/게임 개발에 적용하기: 스크린샷 기반 픽셀 비교(골든 이미지)로 시각적 회귀를 잡는 방식은 웹 프론트엔드의 visual regression testing과 동일한 철학이다. 게임/그래픽 프로그램에서도 강력하게 작동한다.
- 입력 재생(Input Replay) 기반 테스트: 마우스 움직임·클릭을 시간 기반 프레임으로 기록해 재생하는 방식은 E2E 테스트(예: Playwright의 trace/replay)와 유사한 아이디어를 게임에 직접 구현한 사례. UI 애니메이션처럼 "시간이 흐르는" 상호작용을 테스트할 때 참고할 수 있는 패턴이다.
- 결정론(Determinism)을 최우선 설계 원칙으로: 절차적 생성(procedural generation)이라도 시드 기반으로 완전히 동일한 결과를 재현할 수 있어야 회귀 테스트가 의미를 가진다. 랜덤성이 들어가는 시스템을 설계할 때 "같은 입력 → 같은 출력"을 보장하는 구조를 먼저 만드는 게 이후 테스트 인프라 구축을 쉽게 만든다.
- 유닛 테스트와 통합 테스트의 역할 분리: 하드코딩된 값 비교에 의존하는 유닛 테스트의 한계를 인정하고, 유닛 테스트는 "작은 범위의 기본 동작 증명"에만 쓰고, 실제 사용자 흐름과 유사한 통합/E2E 테스트는 스냅샷/리플레이 방식으로 보완하는 조합이 실무에도 적용할 만한 전략이다.
- 스코프를 의도적으로 줄이는 판단력: "카드+타워+적 처치"까지 욕심내지 않고 "카드+타워+다음 레벨"로 축소한 결정은, 작은 마일스톤을 확실히 완성하는 것이 큰 진전이라는 실용적 태도를 보여준다. 개인 프로젝트/사이드 프로젝트를 진행할 때 참고할 만한 스코프 관리 방식.
핵심 요약 (20줄)
- 프리마겐이 Odin 언어로 만들고 있는 타워 디펜스 게임 개발 일지를 공개했다.
- 원래 Lua로 80% 완성했지만 Lua와 2D 그래픽 작업을 좋아하지 않아 Odin으로 전환했다.
- Odin은 C 언어와 유사한 단순한 문법을 가진 언어로, 언어 자체에 대한 애정으로 선택했다.
- UI 시스템을 스택 기반 요소 구축 방식으로 전환해 트리 구조로 요소를 조립할 수 있게 만들었다.
- 요소를 열어둔 상태에서 if 문 안에서 위치·배치·애니메이션을 조건부로 편집할 수 있는 구조다.
- 같은 카드 요소가 위치에 따라 완전히 다르게 렌더링될 수 있어 매우 만족스러운 결과를 얻었다.
- 직접 플렉스 요소를 구현하고 유니온 타입으로 요소의 하위 타입을 만들어 다양한 변형을 적용했다.
- 레이아웃 시스템의 가장 큰 문제는 작은 변경이 예상치 못한 방식으로 전체에 영향을 미치는 것이다.
- 이를 해결하기 위해 모든 데모 파일을 불러와 하나씩 재생하는 데모 테스트 러너를 만들었다.
- 데모 장면을 골든 이미지와 픽셀 단위로 비교해 회귀를 자동으로 검증할 수 있다.
- 마우스 테스트 프레임이라는 개념을 도입해 지속 시간과 마우스 상태를 기록해 재생할 수 있게 했다.
- 마우스 움직임을 시간순으로 기록해 재생하면 UI 애니메이션과 확대/축소까지 그대로 재현된다.
- 모든 프레임이 끝나면 스크린샷을 찍어 텍스처나 파일로 저장해 이후 테스트에 활용한다.
- 이 파이프라인은 유닛 테스트, 다중 플랫폼 빌드 검증, 데모 재생 검증을 한 번에 수행하는 자동화 수준이다.
- 원래 유닛 테스트를 싫어했지만 이번 방식에는 정말 만족한다고 두 번 강조했다.
- 다음 목표는 레벨을 결정론적으로 생성해 매번 완전히 동일한 결과가 나오도록 만드는 것이다.
- 타일 높이와 색상 차이까지 항상 동일하게 렌더링되는지 검증하는 것이 핵심 과제다.
- odin run 명령 한 번으로 새로운 골든 이미지를 저장하고 변경 전후 결과를 비교할 수 있다.
- 현재 코드 규모는 약 1만3천 줄이며 하루 1000~1500줄 정도씩 작업하고 있다고 밝혔다.
- 다음 주에는 스코프를 줄여 카드, 타워, 다음 레벨까지만 구현하는 것을 목표로 삼았다.
