엑셀 브랜치 서버와 ArgoCD: 기획 데이터별 테스트 서버 자동화
게임 개발에서는 코드 브랜치만큼이나 데이터 브랜치가 중요하다. 기획 엑셀 데이터가 바뀌면 Unity 클라이언트와 서버가 같은 데이터 버전을 보고 테스트해야 한다.
문제는 엑셀 브랜치가 늘어날 때마다 개발 서버 설정, 앱 버전, 주소, 배포 values 파일을 사람이 맞추면 실수할 여지가 너무 많다는 점이다. 그래서 엑셀 브랜치별 dev 서버를 Integration API와 GitOps 배포 흐름으로 자동화했다.
1. 배경
기획자는 엑셀 데이터를 브랜치로 나누어 작업한다. 개발자는 특정 브랜치의 데이터를 기준으로 Unity와 서버를 함께 테스트해야 한다.
필요한 것은 단순한 브랜치 목록 API가 아니라, 브랜치 하나를 선택했을 때 다음 리소스가 일관되게 만들어지는 흐름이었다.
- 서버에서 인식할
server_type - 해당 브랜치용
AppVersion - 서버 설정과 서버 주소 그룹
- Kubernetes 배포에 사용할 Helm values 파일
- Unity가 선택할 수 있는 브랜치 목록 API
2. 문제 정의
2.1 수동 설정의 위험
엑셀 브랜치 서버를 수동으로 만들면 아래 항목을 사람이 모두 맞춰야 한다.
| 항목 | 문제 |
|---|---|
| server_type | Kubernetes 리소스명, Ingress host, Kafka topic 등에 사용되므로 이름 규칙을 어기면 배포 실패 |
| AppVersion | Unity와 서버가 같은 데이터 버전을 보려면 DB에 정확히 조립되어야 함 |
| ServerAddress | 게임 API는 브랜치 서버로, 공용 서버는 기존 dev 주소로 연결해야 함 |
| GitOps values | 파일명이 틀리거나 삭제가 누락되면 ArgoCD 상태가 꼬임 |
수동으로도 만들 수는 있지만, 반복될수록 운영 비용과 실수 가능성이 커진다.
2.2 자동화 목표
목표는 "브랜치 이름을 입력하면 테스트 가능한 dev 서버 상태까지 만든다"였다.
단, 다음 조건을 만족해야 했다.
- 브랜치 이름은 Kubernetes/GitOps 리소스명으로 안전해야 한다
- 기존 dev 서버 타입과 충돌하면 안 된다
- 이미 만들어진 브랜치는 멱등하게 반환한다
- DB 생성 후 GitOps 파일 생성이 실패하면 보상 삭제가 필요하다
- Unity는 실제 엑셀 브랜치 목록과 DB AppVersion을 함께 받아야 한다
3. 전체 흐름
flowchart LR
A["Excel Repository<br/>branch list"] --> B["Integration API"]
B --> C["ServerType / AppVersion"]
B --> D["ServerSetting / ServerAddress"]
B --> E["GitOps values file"]
E --> F["ArgoCD ApplicationSet"]
F --> G["Helm deploy"]
B --> H["Unity branch list API"]Integration API가 중심에서 엑셀 브랜치 조회, DB 리소스 생성, GitOps values 파일 생성을 조립한다. ArgoCD는 GitOps 저장소 변경을 보고 Helm values 기반으로 브랜치 서버를 배포한다.
4. 브랜치 이름 검증
브랜치 이름은 단순 표시값이 아니라 server_type으로 사용된다.
이 값은 Kubernetes 리소스명, Ingress host, Kafka topic 같은 여러 시스템의 식별자에 들어간다.
그래서 규칙을 엄격하게 제한했다.
허용: 소문자, 숫자, 하이픈
금지: 슬래시, 언더스코어, 점, 공백, 대문자
권장: 32자 이하
금지: 앞뒤 하이픈또한 예약된 dev 서버 타입과 충돌하지 않도록 했다.
예를 들어 기존 개발 서버로 쓰는 a, b, c 계열 값은 엑셀 브랜치 서버로 만들 수 없게 막았다.
이 검증을 API 밖 문서에만 두면 언젠가 깨진다. 그래서 생성 API 자체가 운영 규칙을 강제하도록 했다.
5. 생성 플로우
엑셀 브랜치 서버 생성은 크게 다섯 단계로 구성했다.
5.1 브랜치 확인과 멱등 처리
먼저 GitOps/Excel 저장소에서 실제 브랜치 목록을 조회한다. 요청한 브랜치가 존재하지 않으면 생성하지 않는다.
이미 같은 브랜치 서버가 DB에 있으면 새로 만들지 않고 기존 정보를 반환한다. 운영툴에서 같은 버튼을 두 번 눌러도 중복 리소스가 생기지 않게 하기 위해서다.
5.2 AppVersion 조립
브랜치 서버는 테스트용 데이터 버전을 따로 가져야 한다. 그래서 일반 릴리즈 버전과 충돌하지 않는 prefix를 사용해 AppVersion을 생성했다.
형식 자체보다 중요한 것은 Unity와 서버가 같은 AppVersion을 바라보도록 DB에 명확히 남기는 것이었다.
5.3 서버 설정과 주소 생성
게임 API 주소는 브랜치 서버를 바라보게 만들고, 채팅/커뮤니티/실시간/통합 API처럼 공용으로 유지할 수 있는 서버는 기존 dev 주소를 사용했다.
즉 모든 주소를 새로 만드는 것이 아니라, 브랜치별로 달라져야 하는 부분과 공용으로 유지할 부분을 분리했다.
5.4 GitOps values 파일 생성
브랜치 서버 배포는 별도 명령으로 Kubernetes에 직접 붙지 않았다. Integration API가 GitOps 저장소에 브랜치용 values 파일을 만들고, ArgoCD가 그 변경을 감지해 배포한다.
values 파일에는 브랜치 서버를 구분할 최소 값만 둔다.
server_type: feature-branch-name나머지 이미지, 리소스, 공통 환경 변수, Ingress 규칙은 기존 dev chart와 values를 재사용한다. 이렇게 해야 브랜치 서버가 늘어나도 차이가 나는 값만 관리할 수 있다.
5.5 실패 시 보상 삭제
DB 트랜잭션과 GitOps 저장소 변경은 하나의 원자적 트랜잭션으로 묶을 수 없다. 그래서 생성 중 실패하면 가능한 범위에서 GitOps 파일을 삭제하는 보상 로직을 넣었다.
중요한 것은 "절대 실패하지 않게 만들기"가 아니라, 실패했을 때 운영자가 정리해야 할 찌꺼기를 줄이는 것이다.
6. Unity용 브랜치 목록 API
Unity는 실제 엑셀 브랜치와 서버에 등록된 AppVersion을 함께 알아야 한다.
그래서 Unity용 API는 단순히 DB에 생성된 서버 목록만 내려주지 않는다.
- GitOps/Excel 저장소에서 실제 브랜치 목록 조회
- DB의 엑셀 브랜치 서버 AppVersion 조회
- 브랜치 이름 기준으로 조립
- AppVersion이 없는 브랜치는
null로 반환
이렇게 하면 Unity는 "브랜치는 존재하지만 서버가 아직 생성되지 않은 상태"와 "서버까지 생성되어 테스트 가능한 상태"를 구분할 수 있다.
7. 테스트 포인트
이 기능에서 중요한 테스트는 API가 단순히 200을 반환하는지가 아니었다. 외부 저장소 브랜치 목록과 DB의 AppVersion을 정확히 조립하는지가 핵심이었다.
테스트에서는 fake GitOps repository를 사용해 브랜치 목록을 고정했다. 그리고 DB에 특정 브랜치의 AppVersion만 넣은 뒤, Unity API 응답이 다음처럼 나오는지 확인했다.
| 브랜치 | 기대 결과 |
|---|---|
| main | AppVersion 없음 |
| feature-a | DB에 저장된 AppVersion 포함 |
| feature-b | AppVersion 없음 |
외부 GitOps 저장소를 직접 호출하지 않아도, Integration API의 조립 로직은 안정적으로 검증할 수 있었다.
8. 배운 점
엑셀 브랜치 서버 자동화의 핵심은 Kubernetes 배포 파일을 하나 더 만드는 것이 아니었다. 운영자가 수동으로 기억하던 규칙을 API와 GitOps 흐름 안으로 넣는 것이었다.
정리하면 다음이 중요했다.
- 이름 규칙은 문서가 아니라 생성 API가 강제해야 한다
- GitOps 파일은 차이가 나는 값만 담아야 한다
- DB와 GitOps처럼 원자적으로 묶기 어려운 작업에는 보상 로직이 필요하다
- Unity용 API는 실제 브랜치 목록과 서버 생성 상태를 함께 보여줘야 한다
- 테스트는 외부 저장소를 fake로 두고 조립 결과를 검증하는 것이 효과적이다
결과적으로 기획 데이터 브랜치별 테스트 서버를 더 빠르게 만들 수 있고, Unity/서버/운영툴이 같은 기준으로 브랜치 상태를 보게 되었다.
Loading comments...