RummiArena 바이브 로그 2026-04-02 상세 해설
원문 출처: GitHub - k82022603/RummiArena/work_logs/vibe/2026-04-02.md
작성일: 2026년 4월 2일
해설 작성: 2026년 4월 4일
장르: 1인 개발자의 AI 협업 개발 일지 (바이브 로그)
개요: 이 문서는 무엇인가
이 바이브 로그는 RummiArena라는 루미큐브(Rummikub) 게임 플랫폼을 홀로 개발하는 개발자가 2026년 4월 2일 하루 동안 겪은 경험을 고백체 산문으로 기록한 작업 일지다. 단순한 버그 수정 기록이 아니라, 1인 개발자가 AI 에이전트 팀(Claude의 Agent Teams)을 활용해 어떻게 다인 팀에 준하는 생산성을 낼 수 있는지를 살아있는 사례로 보여주는 문서다.
하루의 구조는 대략 다음과 같다. 아침에 21장의 게임 버그 스크린샷을 펼치면서 시작하고, 에이전트 팀 6명을 동원해 결함 24건을 분류하고, CI/CD 파이프라인을 10번 실패시키면서 각각의 원인을 해결하고, 결국 13개 중 8개의 스테이지를 초록색으로 만들며 끝난다. 이 과정에서 SonarQube의 JVM 메모리 튜닝, Trivy의 보안 취약점 스캔, Go의 가비지 컬렉터 최적화, Docker-in-Docker 문제, GitOps 배포 전략까지 다양한 DevSecOps 기술이 등장한다.
이 문서는 그 모든 것을 맥락과 함께 상세히 풀어낸다.
1장. “같이 일 못하겠다” — 버그와의 아침 대면
1.1 버그의 실체: INVALID_MOVE 이후 서버 랙 미복원
아침의 시작은 게임 화면이 찍힌 스크린샷 21장이었다. 각 장면은 서로 다른 증상을 보이고 있었다. 타일이 사라진 곳, 턴이 넘어가지 않는 곳, 드래그한 타일이 허공에 멈춘 곳. 이것들은 얼핏 보면 각기 다른 버그처럼 보이지만, 원문이 밝히듯 결국 하나의 근본 원인에서 비롯된 것이었다.
루미큐브는 플레이어가 자신의 타일을 테이블에 올려놓는 방식으로 진행된다. 온라인 게임에서 이 구조는 클라이언트-서버 모델로 구현된다. 클라이언트(브라우저)는 플레이어가 타일을 올려놓은 상태를 렌더링하고, 서버는 그 행위가 규칙에 맞는지 검증한다. 서버가 규칙에 어긋난 행위를 감지하면 INVALID_MOVE라는 에러를 반환한다.
문제는 이 시점에 발생했다. INVALID_MOVE 에러를 반환할 때, 서버는 에러 코드만 돌려보낼 뿐 현재 서버가 기억하는 랙(rack, 플레이어 손패) 상태를 함께 전송하지 않았다. 클라이언트는 “내가 올린 타일이 무효 처리됐으니 다시 내 손에 돌아왔겠지”라고 가정하지만, 서버는 그 타일을 어딘가 이미 처리된 상태로 기억하고 있었다. 클라이언트와 서버의 상태가 서로 다른 시점을 가리키게 된 것이다. 이 불일치가 쌓이면서 게임이 무너졌다.
개발자가 “같이 일 못하겠다”고 한 것은 사람에게 한 말이 아니었다. 이 코드, 이 비동기 상태 불일치 구조에게 한 탄식이었다. 그리고 그 탄식은 오래가지 않았다. 할 일이 너무 많았기 때문이다.
1.2 루미큐브와 온라인 게임 상태 관리의 어려움
루미큐브는 단순한 카드 게임보다 상태 관리가 복잡하다. 각 플레이어는 손패(rack)를 가지고 있고, 테이블 위에는 타일 조합(set)들이 놓여 있으며, 매 턴마다 그 상태가 바뀐다. 드래그앤드롭 인터페이스로 구현하면 중간 상태(dragging, hovering, dropping)가 수십 개에 달한다.
온라인 멀티플레이어 게임에서 이런 복잡한 상태를 서버와 클라이언트 사이에 일관되게 유지하는 것은 현대 게임 엔지니어링의 핵심 과제 중 하나다. 특히 “낙관적 UI 업데이트(Optimistic UI Update)” 패턴 — 서버 응답을 기다리지 않고 클라이언트가 먼저 상태를 업데이트하는 방식 — 을 쓸 경우, 서버가 거부했을 때 롤백 로직이 완벽하게 구현되지 않으면 정확히 이런 종류의 버그가 발생한다.
RummiArena의 경우 서버의 INVALID_MOVE 핸들러가 에러만 반환하고 현재 상태를 동기화하지 않았던 것이 이 버그의 뿌리였다. 해결책은 서버가 에러를 반환할 때 반드시 현재 서버 기준의 랙 상태를 함께 전송하고, 클라이언트가 그 상태로 강제 동기화하는 것이다.
2장. 스크린샷 21장의 고고학 — 결함 분류와 에이전트 투입
2.1 고고학적 디버깅
원문은 버그를 찾는 과정을 고고학에 비유한다. 이것은 단순한 수사적 비유가 아니라 꽤 정확한 묘사다. 고고학자가 발굴 현장에서 지층을 한 겹씩 벗겨내며 문명의 흔적을 찾듯, 개발자는 에러 증상의 표면을 한 층씩 파고들어 근본 원인을 찾는다.
표면: “타일이 안 보인다”
한 층 아래: “서버 응답에서 랙 상태가 누락됐다”
두 층 아래: “INVALID_MOVE 핸들러가 에러만 반환하고 현재 상태를 동기화하지 않는다”
이 발굴 과정이 홀로 이루어졌다면 훨씬 오래 걸렸을 것이다. 개발자는 여기에 Claude의 Agent Teams를 투입했다.
2.2 Agent Teams란 무엇인가
Agent Teams는 Anthropic의 Claude가 제공하는 다중 에이전트 협업 기능이다. 단일 AI 인스턴스가 순차적으로 작업을 처리하는 것이 아니라, 여러 에이전트 인스턴스가 각자 역할을 분담해 병렬로 작업하고 결과를 종합하는 구조다.
이 로그에서 Agent Teams는 다음과 같은 페르소나로 등장한다:
- Architect: 시스템 아키텍처 관점에서 분석하고 서버 자동 롤백 전략을 설계
- Security: 보안 취약점을 검토하고 rate limit 미적용을 지적
- QA: 품질 보증 관점에서 conservation 테스트(타일 총수 불변 조건 검증)를 요청
- DevOps: 배포 파이프라인 관점에서 ArgoCD SyncWave 전략을 제안
- Go Dev: Go 언어 코드 관점에서 핸들러 함수 명명 규칙 통일을 제안
이 5명이 하나의 코드 리뷰를 각자의 렌즈로 바라보는 것이다. 1인 개발자가 5개의 전문 관점을 동시에 확보하는 방법이다.
2.3 24건의 결함 분류
분석 전담 에이전트 6명을 투입한 결과 24건의 결함이 분류됐다. Critical 7건, Major 7건, Minor 10건. 이것은 단순히 버그 목록을 나열한 것이 아니라, 결함의 심각도와 영향 범위를 체계적으로 분류한 것이다.
소프트웨어 공학에서 결함 분류(defect classification)는 수정 우선순위를 결정하고 개발 리소스를 효율적으로 배분하는 데 필수적인 작업이다. Critical은 서비스 자체를 불능으로 만드는 것, Major는 핵심 기능을 훼손하는 것, Minor는 불편하지만 서비스는 계속 가능한 것으로 나눈다.
흥미로운 점은 이 24건이 “하나의 근본 원인이 24개의 얼굴로 나타난 것”이라는 원문의 통찰이다. 이것은 소프트웨어 공학에서 근본 원인 분석(Root Cause Analysis, RCA) 의 정수다. 증상은 다양하지만 뿌리는 하나다. 그 뿌리를 고치면 24개의 얼굴이 모두 사라진다.
3장. CI 파이프라인 10회의 집념 — 실패와 학습의 반복
3.1 CI/CD 파이프라인이란 무엇인가
CI(Continuous Integration, 지속적 통합)와 CD(Continuous Delivery/Deployment, 지속적 배포)는 현대 소프트웨어 개발의 근간을 이루는 자동화 파이프라인이다. 개발자가 코드를 변경하면, 자동으로 코드 품질 검사, 보안 취약점 스캔, 테스트 실행, 빌드, 배포가 순차적으로 진행된다. 이 파이프라인이 모두 통과되면 코드가 프로덕션 환경에 배포된다.
RummiArena의 파이프라인은 13개의 스테이지로 구성되어 있었다. lint(코드 스타일 검사) 4개, test(테스트) 2개, quality(품질 검사) 2개, build(빌드) 4개, update-gitops(배포 설정 업데이트) 1개. 이날의 목표는 이 13개를 모두 초록색(통과)으로 만드는 것이었다.
10번의 실패. 매번 다른 이유. 이것이 이날의 오전과 오후를 가득 채웠다.
3.2 첫 번째 실패: SonarQube OOM (Out Of Memory)
SonarQube는 코드 품질 분석 도구다. 코드의 버그 가능성, 코드 냄새(code smell), 보안 취약점, 기술 부채를 자동으로 분석하고 점수를 매긴다. Java로 작성된 도구이기 때문에 JVM(Java Virtual Machine) 위에서 동작하며, 상당한 메모리를 요구한다.
문제는 Kubernetes Pod에 할당된 메모리와 SonarQube가 요구하는 메모리 사이의 불일치였다.
초기 상황은 이랬다. SonarQube의 JVM은 기본적으로 Xmx=512MB를 요구했다. 그런데 CI Runner Pod의 총 메모리는 1Gi(기가이비바이트, 약 1,073MB)였다. 여기에 Go 빌드 캐시가 함께 동작하면서 메모리가 부족해졌고, JVM이 힙 공간을 할당하지 못해 OOM(Out Of Memory) 에러로 죽었다.
개발자는 SonarQube의 JVM 힙을 Xmx=256MB로 낮추는 결정을 했다. 이것이 두 번째 실패로 이어졌다.
3.3 두 번째 실패: JVM GC 충돌 (Xms ≠ Xmx)
JVM에는 두 가지 핵심 메모리 설정이 있다:
- Xms: JVM이 시작할 때 확보하는 초기 힙 메모리 크기
- Xmx: JVM이 최대로 사용할 수 있는 힙 메모리 크기
Xms=128MB, Xmx=256MB로 설정했을 때 문제가 발생했다. JVM은 128MB로 시작해서 필요하면 256MB까지 힙을 확장한다. 이 확장 과정에서 가비지 컬렉터(GC, Garbage Collector)가 반복적으로 개입한다. 힙이 작을수록 GC가 더 자주 실행되고, GC가 실행되는 동안 애플리케이션이 멈추는 “Stop-the-World” 현상이 발생한다. SonarQube 분석 중에 이것이 반복되자 파이프라인이 타임아웃으로 실패했다.
해결책은 Xms=Xmx=256MB로 고정하는 것이었다. JVM에게 “처음부터 256MB를 쓰고, 그 이상은 절대 확장하지 마라”고 못 박는 것이다. JVM은 초기화 시점에 256MB를 한 번에 확보하고, 이후로는 힙 확장 없이 그 범위 안에서 동작한다. GC의 빈도가 안정화되면서 93초 만에 SonarQube 분석이 완료됐다.
SonarQube 공식 문서에서도 이와 같이 안정적인 힙 메모리 운영을 위해 Xms와 Xmx를 동일하게 설정할 것을 권장한다. 특히 Elasticsearch 프로세스의 경우 이 설정이 성능 안정성에 결정적이다.
3.4 세 번째 이후: Trivy와 lodash CVE-2026-4800
Trivy는 Aqua Security가 개발한 오픈소스 보안 취약점 스캐너다. 컨테이너 이미지, 파일시스템, Git 저장소, Kubernetes 클러스터에 존재하는 알려진 취약점(CVE, Common Vulnerabilities and Exposures)을 스캔한다. CI 파이프라인에서 코드를 빌드한 후, 이 Trivy가 의존 라이브러리의 취약점을 자동으로 검출하면 파이프라인이 실패하도록 설정되어 있었다.
이 날 Trivy가 검출한 것은 lodash의 CVE-2026-4800으로, 심각도는 HIGH였다.
lodash는 JavaScript 생태계에서 가장 널리 쓰이는 유틸리티 함수 모음 라이브러리 중 하나다. 배열 조작, 객체 병합, 문자열 처리 등 자주 쓰이는 기능들을 편리하게 제공한다. 문제는 lodash가 너무 많은 곳에서 사용되기 때문에 직접 의존하지 않아도 간접 의존(transitive dependency)으로 들어오는 경우가 많다는 것이다. 프로젝트가 A 라이브러리를 쓰고, A가 내부적으로 lodash를 쓴다면, lodash의 취약점이 내 프로젝트에도 영향을 미친다.
개발자는 lodash를 패치하거나 취약한 버전을 교체했고, Trivy 스캔은 42초 만에 통과됐다.
중요한 배경 정보: 이 시점에서 Trivy 자체가 2026년 3월 중순에 대규모 공급망 공격(Supply Chain Attack)을 받았다는 사실을 알아야 한다. TeamPCP라는 위협 행위자 그룹이 Aqua Security의 CI/CD 파이프라인을 침해해 Trivy의 공식 GitHub Action(aquasecurity/trivy-action)의 75개 버전 태그를 악성 커밋으로 교체했다. 이 사건은 CVE-2026-33634(CVSS 9.4)로 등록됐다. 이 맥락에서 이날 Trivy가 정상적으로 동작해 lodash CVE를 검출했다는 것은, 개발자가 안전한 버전의 Trivy를 사용하고 있었음을 의미한다. 보안 도구 자체의 무결성을 검증하는 것이 DevSecOps의 또 다른 과제로 부각된 시기였다.
3.5 Runner Pod 최적화: 메모리 증설과 GOGC 튜닝
파이프라인이 반복 실패하면서 몇 가지 인프라 레벨의 최적화가 이루어졌다.
Runner Pod 메모리 증설: CI를 실행하는 Kubernetes Pod의 메모리를 1Gi에서 2Gi로 늘렸다. SonarQube와 Go 빌드가 동시에 실행될 때 메모리 경합이 발생하지 않도록 한 것이다.
GOGC=50 설정: 이것은 Go 언어의 가비지 컬렉터를 제어하는 환경변수다. Go의 GC는 기본적으로 힙이 마지막 GC 이후보다 100% 커졌을 때 실행된다(GOGC=100). GOGC=50으로 설정하면 힙이 50% 커졌을 때 GC가 실행된다. 이렇게 하면 GC가 더 자주 실행되지만 각 GC가 처리하는 메모리 양이 줄어들어, CI 환경처럼 메모리가 제한된 상황에서 메모리 사용량을 더 안정적으로 유지할 수 있다.
PVC 캐시: PVC(Persistent Volume Claim)는 Kubernetes에서 영구 스토리지를 요청하는 리소스다. CI 파이프라인이 실행될 때마다 Go 모듈이나 npm 패키지 같은 의존성을 인터넷에서 다시 다운로드하면 시간이 많이 걸린다. PVC를 캐시로 붙여두면 이전에 다운로드한 패키지를 재사용할 수 있어 빌드 속도가 크게 향상된다.
이 세 가지 조치가 결합돼 파이프라인의 안정성이 높아졌다.
3.6 최종 결과: 8/13 통과, Docker DinD라는 마지막 벽
하루 끝의 상황은 13개 스테이지 중 8개 통과였다. lint 4개, test 2개, quality 2개가 모두 초록색이었다. 남은 것은 build 4개와 update-gitops 1개였는데, 그 장벽이 바로 Docker DinD(Docker-in-Docker) 였다.
Docker DinD는 Docker 컨테이너 안에서 또 다른 Docker 컨테이너를 빌드하고 실행하는 기술이다. CI 파이프라인이 Kubernetes Pod 안에서 실행될 때, 그 Pod 안에서 Docker 이미지를 빌드해야 한다면 DinD가 필요하다. 문제는 DinD가 privileged mode(특권 모드)에서 실행되어야 한다는 것인데, 이는 컨테이너가 호스트 시스템의 거의 모든 권한을 갖게 됨을 의미한다. 보안 우려뿐 아니라, OverlayFS 위에 또 다른 OverlayFS를 쌓는 중첩 스토리지 구조에서 성능 저하와 설정 오류가 빈번히 발생한다.
이것이 다음 날로 넘겨진 과제였다.
3.7 10번의 실패가 만든 1020줄의 CI 운영 매뉴얼
하루 동안 파이프라인을 10번 실패시키면서 얻은 교훈들이 1020줄의 CI 운영 매뉴얼로 정리됐다. 이것은 단순한 트러블슈팅 기록이 아니라, 유사한 상황을 다시 만났을 때 더 빠르게 해결하기 위한 지식 베이스다. 실패의 기록이 조직 학습(organizational learning)이 되는 과정이다.
4장. Agent Teams 20회 — 1인이 11인을 운영하는 방법
4.1 20회 투입의 분류
이날 Agent Teams는 총 20회 투입됐다. 분석 6회, 수정 2회, 리뷰 5회, CI 3회, 기타 4회. 이것을 세분화하면 다음과 같다.
분석(6회): 스크린샷 21장을 분석해 결함 목록을 작성하는 작업이 주를 이뤘다. 에이전트들이 각자 맡은 스크린샷 세트를 분석하고, 관찰된 증상, 추정 원인, 영향 범위를 정리해 종합 보고서를 만들었다.
수정(2회): 분석 결과를 바탕으로 실제 코드 수정이 이루어졌다. 5개 파일을 수정하고 테스트를 작성하는 작업이었다.
리뷰(5회): 수정된 코드를 다각도에서 검토하는 작업이었다. Architect, Security, QA, DevOps, Go Dev 다섯 페르소나가 각자의 관점에서 코드를 검토했다.
CI(3회): CI 파이프라인 실패 로그를 분석하고, 원인을 추적하고, 수정 방안을 제안하는 작업이었다.
기타(4회): CI 운영 매뉴얼 작성, 배포 전략 문서화 등의 부수 작업이었다.
4.2 다중 관점 리뷰의 가치
가장 인상적인 대목은 코드 리뷰 과정이다. 5명의 에이전트가 각자 다른 전문성의 렌즈로 동일한 코드를 검토했다.
Architect는 서버 자동 롤백 전략을 제안했다. INVALID_MOVE 발생 시 서버가 자동으로 이전 상태로 롤백하고 클라이언트에 현재 상태를 전파하는 메커니즘이다. 이것은 아키텍처 레벨의 문제 해결 방식이다.
Security 에이전트는 rate limit이 미적용되어 있음을 지적했다. 루미큐브 서버에서 rate limit이 없다면, 악의적인 클라이언트가 수천 개의 INVALID_MOVE 요청을 보내 서버 부하를 유발할 수 있다. 이것은 게임 로직과는 무관해 보이지만, 보안 관점에서 필수적인 고려 사항이다.
QA 에이전트는 conservation 테스트를 요청했다. “타일 총수는 항상 일정해야 한다”는 불변 조건(invariant)을 검증하는 테스트다. 어떤 게임 액션이 발생하더라도 타일이 새로 생겨나거나 사라지면 안 된다. 이 조건이 깨지는 것이 발견되면 데이터 무결성에 심각한 문제가 있는 것이다.
DevOps 에이전트는 ArgoCD의 SyncWave를 활용한 배포 순서 지정을 제안했다. SyncWave는 ArgoCD의 GitOps 배포 도구에서 여러 리소스의 배포 순서를 제어하는 어노테이션이다. 예를 들어 데이터베이스가 준비된 후에 애플리케이션 서버가 뜨도록 순서를 지정할 수 있다.
Go Dev 에이전트는 핸들러 함수 명명 규칙 통일을 제안했다. 코드 일관성은 장기 유지보수성에 직결되는 문제다.
4.3 “1인 개발자가 11인 팀을 운영하는 방법”
원문의 이 문장이 이 로그 전체의 핵심 테제다. 오전에는 1명의 분석가로, 오후에는 5명의 리뷰어로, 필요할 때는 DevOps 전문가로, 보안 전문가로 Agent Teams를 소환한다. 각각의 투입이 독립적인 작은 프로젝트처럼 운영된다.
이것이 가능한 이유는 Agent Teams의 특성에 있다. 각 에이전트는 컨텍스트(코드, 에러 로그, 스크린샷)를 공유받은 상태에서 자신의 전문 역할에 집중한다. 에이전트들의 관점이 다르기 때문에 동일한 코드에서 서로 다른 문제를 발견한다. 이것이 “혼자서는 보이지 않는 것들이 여럿이 보면 보인다”는 원문의 통찰이다.
5장. Quality Gate를 우회하면 DevSecOps가 무의미 — 철학으로서의 DevSecOps
5.1 우회의 유혹
SonarQube가 OOM으로 죽어있을 때, 가장 쉬운 해결책은 allow_failure: true를 설정해 SonarQube 스테이지를 우회하는 것이었다. 파이프라인이 SonarQube 실패를 무시하고 다음 단계로 넘어가도록 하는 것이다. 코드 품질 검사를 건너뛰고 일단 배포하는 것.
이 유혹을 “Security 에이전트”가 하루 전에 경고했다. “Quality Gate를 우회하면 DevSecOps가 무의미합니다.”
5.2 DevSecOps란 무엇인가
DevSecOps는 Development(개발), Security(보안), Operations(운영)의 합성어다. 기존에는 개발이 끝난 후 보안 검토를 하고, 그 다음 운영 팀에 넘기는 순차적 방식이었다. DevSecOps는 이 세 가지를 처음부터 통합해서, 개발 단계에서부터 보안과 운영을 고려하는 문화와 실천을 의미한다.
CI/CD 파이프라인에서 Quality Gate는 DevSecOps의 물리적 구현이다. 코드 품질 기준(SonarQube)과 보안 취약점 기준(Trivy)을 통과하지 않으면 배포가 불가능하도록 강제하는 것이다.
5.3 도구가 아니라 태도
원문이 정확히 짚는 것이 있다. “DevSecOps는 도구가 아니라 태도다. SonarQube와 Trivy를 설치하는 것이 DevSecOps가 아니라, 그것들이 실패했을 때 우회하지 않는 것이 DevSecOps다.”
이것은 소프트웨어 공학의 오래된 진실을 다른 방식으로 표현한 것이다. 규칙과 도구는 쉽게 도입할 수 있지만, 그것이 불편하거나 느릴 때 지키는 것이 진짜 어렵다. Quality Gate를 우회하고 싶은 순간은 항상 압박이 클 때, 시간이 없을 때, 지쳐있을 때다. 그 순간에 우회하지 않는 것이 DevSecOps의 실질적인 내용이다.
이날의 결과가 이 주장을 입증한다. 우회하지 않고 버텼기 때문에, quality 2/2 PASS라는 두 개의 초록불이 실제로 의미를 갖게 됐다. “코드 품질 검사를 통과했다”는 것과 “코드 품질 검사를 건너뛰었다”는 것은 결과는 같아 보이지만, 전혀 다른 것이다.
6장. 하루의 무게 — 평범한 개발 하루의 정의
6.1 하루 동안의 총 작업량
아침의 탄식(“같이 일 못하겠다”)에서 저녁의 8/13 달성까지, 이날 이루어진 작업의 목록은 다음과 같다:
- 스크린샷 21장 분석
- 결함 24건 분류 (Critical 7, Major 7, Minor 10)
- 파일 15개 수정
- 테스트 9개 신규 작성
- CI 파이프라인 10회 실행 (모두 실패, 최종 8/13)
- CI 운영 매뉴얼 1020줄 작성
- Agent Teams 20회 투입
이것을 전통적인 방식으로, 즉 1인 개발자가 에이전트 팀 없이 처리했다면 며칠이 걸렸을 작업이다.
6.2 “삽질”도 “순항”도 아닌 “평범한 개발 하루”
원문의 가장 아름다운 문장 중 하나가 여기에 있다. “이런 하루를 뭐라고 불러야 할까. 삽질이라고 하기엔 너무 생산적이었고, 순항이라고 하기엔 너무 고통스러웠다. 아마 ‘평범한 개발 하루’라고 부르는 것이 가장 정확할 것이다.”
이것은 개발자라면 누구나 공감할 수 있는 관찰이다. 코드를 쓰는 시간보다 코드가 왜 안 되는지 찾는 시간이 더 긴 하루. 파이프라인이 실패할 때마다 로그를 읽고, 원인을 추적하고, 한 줄을 고치고, 다시 돌리는 하루. 그것이 개발의 일상이다.
6.3 “실패는 벽이 아니라 계단”
하루를 마감하는 원문의 메타포가 인상적이다. 10번의 실패를 통해 배운 교훈 — 실패는 벽이 아니라 계단이다. 한 칸씩 올라가면 결국 넘어간다. 오늘 8칸을 올라갔다. 내일 5칸이 남았다(Docker DinD를 포함한 나머지 build 4개와 update-gitops 1개).
이것은 단순한 자기 위로가 아니다. CI 파이프라인 디버깅의 실제 구조를 정확히 묘사한 것이다. 각각의 실패는 새로운 정보를 제공한다. 실패 로그를 읽으면 원인을 알 수 있고, 원인을 알면 수정할 수 있다. 10번의 실패가 10개의 교훈이 됐고, 10개의 교훈이 8개의 초록불이 됐다.
7장. 기술 용어 상세 해설
이 섹션에서는 본문에 등장한 기술 용어들을 더 상세히 설명한다.
7.1 INVALID_MOVE 핸들러와 상태 동기화
루미큐브 서버에서 INVALID_MOVE 핸들러는 클라이언트가 보내온 게임 액션이 규칙에 위배될 때 호출되는 함수다. 올바른 구현 방식은 다음과 같다:
잘못된 구현:
1
2
3
클라이언트: "이 타일을 저기 올릴게요"
서버: "INVALID_MOVE" (에러 코드만 반환)
클라이언트: "...내 타일은 어디에?" // 상태 불일치
올바른 구현:
1
2
3
클라이언트: "이 타일을 저기 올릴게요"
서버: "INVALID_MOVE, 현재 서버 기준 랙 상태는 [A, B, C]입니다" (상태 포함 반환)
클라이언트: "알겠습니다, 내 랙을 [A, B, C]로 동기화합니다" // 상태 일치
7.2 JVM 메모리 모델 심화
JVM의 메모리 구조는 크게 Heap과 Non-Heap으로 나뉜다. Xms와 Xmx는 모두 Heap 메모리를 제어한다.
SonarQube의 경우 세 개의 JVM 프로세스가 동시에 실행된다:
- Web 프로세스: HTTP 요청 처리 (기본 512MB)
- Compute Engine(CE) 프로세스: 분석 작업 수행 (기본 512MB)
- Elasticsearch 프로세스: 검색 인덱싱 (기본 512MB)
세 프로세스가 동시에 실행되면 이론적으로 최소 1.5GB의 힙이 필요하다. Pod의 메모리 한계가 1Gi라면 당연히 OOM이 발생한다. 이를 해결하려면 각 프로세스의 힙을 줄이거나(이 로그의 접근 방식) Pod의 메모리 한도를 늘려야 한다.
7.3 Go GOGC 환경변수
Go의 가비지 컬렉터는 GOGC 환경변수로 제어된다. 기본값은 100이며, 이는 힙이 마지막 GC 이후 대비 100% 증가했을 때 GC를 실행한다는 의미다.
GOGC=100(기본): 힙이 2배가 되면 GC 실행GOGC=50: 힙이 1.5배가 되면 GC 실행 (더 자주, 메모리 효율 ↑, CPU 부담 약간 ↑)GOGC=200: 힙이 3배가 되면 GC 실행 (덜 자주, 메모리 사용 ↑, CPU 부담 약간 ↓)GOGC=off: GC 비활성화 (short-lived 프로세스에서만 사용)
CI 환경처럼 메모리가 제한된 곳에서 GOGC=50은 메모리 사용량을 예측 가능하게 유지하는 데 도움이 된다.
7.4 Docker DinD의 구조와 대안
Docker DinD(Docker-in-Docker)는 외부 Docker 컨테이너 안에서 Docker 데몬을 실행하는 방식이다. CI 파이프라인이 Kubernetes Pod 안에서 실행될 때 Docker 이미지를 빌드하려면 이 구조가 필요하다.
DinD의 핵심 문제점은:
- privileged mode 필요: 컨테이너가 호스트 커널에 직접 접근할 수 있게 된다. 보안 취약점이 발생하면 호스트 시스템 전체가 노출될 수 있다.
- OverlayFS 중첩: 호스트의 OverlayFS 위에 컨테이너의 OverlayFS를 다시 쌓는 구조는 성능 저하와 설정 오류를 유발한다.
- 레이어 캐시 무효화: 내부 Docker 데몬이 외부 레이어 캐시를 공유하지 못해 매번 처음부터 빌드해야 할 수 있다.
대안 기술들:
- Kaniko: Google이 개발한 Docker 이미지 빌드 도구. 데몬 없이 컨테이너 내부에서 Dockerfile을 실행한다. privileged mode가 필요 없다.
- BuildKit: Docker의 차세대 빌드 엔진. 부분적으로 rootless 빌드를 지원한다.
- Podman: Red Hat이 주도하는 Docker 대안. 데몬 없이 동작하고 rootless 지원이 강력하다.
7.5 ArgoCD SyncWave
ArgoCD는 Kubernetes 환경에서 GitOps를 구현하는 지속적 배포 도구다. SyncWave는 여러 리소스의 배포 순서를 제어하는 어노테이션이다.
1
2
3
metadata:
annotations:
argocd.argoproj.io/sync-wave: "1" # 1번 파도에 배포
같은 wave 번호를 가진 리소스들이 함께 배포되고, 모두 준비될 때까지 기다린 후 다음 wave가 배포된다. 이를 통해 데이터베이스(wave 0) → 백엔드(wave 1) → 프론트엔드(wave 2) 같은 순서를 보장할 수 있다.
7.6 Conservation 테스트와 게임 불변 조건
Conservation(보존) 테스트는 시스템의 물리적 불변 조건을 검증하는 테스트다. 루미큐브에서 타일 보존 법칙은:
1
초기 타일 총수 = 현재 플레이어 손패 타일 수 합계 + 테이블 위 타일 수
어떤 게임 액션이 일어나도 이 등식이 성립해야 한다. 이 조건이 깨진다면 버그가 있는 것이다. 이런 종류의 테스트는 게임의 데이터 무결성을 보장하는 가장 기본적인 방어선이다.
8장. 이 로그가 담고 있는 더 큰 이야기
8.1 1인 개발과 AI 에이전트 팀의 결합
이 바이브 로그가 단순한 개발 일기 이상의 의미를 갖는 이유는, 2026년 AI 보조 개발의 현실을 날 것으로 보여주기 때문이다. Agent Teams를 20회 투입하는 것이 “과한가?”라고 잠깐 의심했다가 곧 “과하지 않았다”고 결론 내리는 대목이 인상적이다.
24건의 결함을 하루 만에 분석하고, 수정하고, 리뷰하고, CI에 올리는 것은 에이전트 팀 없이는 불가능한 작업량이다. 이것이 현재 1인 또는 소규모 개발팀이 AI 에이전트를 활용해 달성하고 있는 생산성의 현실이다.
8.2 RummiArena 프로젝트의 위치
RummiArena는 단순한 개인 프로젝트가 아니다. 이 프로젝트의 GitHub 저장소 구조(work_logs/vibe/)는 체계적인 작업 기록 방식을 보여주고, CI/CD 파이프라인의 완성도(SonarQube, Trivy, ArgoCD GitOps)는 프로덕션 수준의 DevSecOps 실천을 보여준다. 또한 LxM(Ludus Ex Machina) 프로젝트 — AI 모델들이 여러 게임을 대결하는 아레나 플랫폼 — 의 일부로서, 루미큐브가 AI 모델 평가 게임 중 하나가 될 가능성이 있다.
8.3 바이브 코딩의 실체
“바이브 코딩(Vibe Coding)”이라는 용어는 2025년부터 개발자 커뮤니티에서 AI와 함께 직관적으로, 흐름을 타며 코딩하는 방식을 일컫는 말로 쓰이기 시작했다. 이 로그의 제목이 “바이브 로그”인 이유가 여기에 있다. 그런데 흥미롭게도, 이날의 기록은 “바이브”보다 훨씬 체계적이고 구조적이다. JVM 메모리 튜닝, GC 최적화, 결함 분류, DevSecOps 철학까지. 직관적인 흐름과 엄밀한 공학 실천이 공존하는 방식이다.
이것이 2026년의 AI 보조 개발의 실제 모습일지 모른다. 흐름을 타면서도 치밀하게. 탄식하면서도 계단을 오르며.
맺음말: 내일의 5칸
이 로그는 완결된 성공담이 아니다. 8/13으로 끝났다. Docker DinD라는 벽이 남아있다. 개발자 스스로 “넘을 수 있을지 모르겠다”고 고백한다.
하지만 그 솔직함이 이 로그를 빛나게 만든다. 탄식과 집념, 실패와 학습, 에이전트와 인간, 코드와 철학이 하루 안에 촘촘히 엮여있다. 21장의 스크린샷이 24건의 결함이 되고, 10번의 실패가 8개의 초록불이 되고, 1인 개발자가 11인 팀이 되는 과정. 그것이 2026년의 평범한 개발 하루다.
내일은 5칸이 남았다.
이 해설 문서는 원문 바이브 로그(2026-04-02)를 기반으로 최신 기술 정보(Trivy 공급망 공격 CVE-2026-33634, SonarQube JVM 튜닝, Docker DinD 현황 등)를 포함해 작성되었습니다.