[인프라를 소프트웨어처럼 2/5] 코드가 모르는 그 '환경'은 누가 만드는가

"환경은 바깥에 있다"는 말의 빈칸
1화에서 우리는 다섯 축의 약속을 다시 꺼냈습니다 — 코드는 '무엇을'만 정의하고, '어디서·언제·어떤 환경'은 바깥에서 주입한다. 그리고 거기에 조용한 빈칸이 하나 있다고 했습니다. 이 글은 그 빈칸을 들여다보는 데서 시작합니다.
'바깥'은 아무 데도 아닌 곳이 아닙니다. 코드가 환경을 모르게 만들수록, 그 환경을 실제로 존재하게 만드는 책임은 사라지는 게 아니라 어딘가로 옮겨갑니다. values 파일이 가리키는 그 클러스터는 누가 띄웠으며, Cloud Adapter가 호출하는 그 클라우드 계정은 누가 만들었을까요. Clock이 읽는 그 시간대, Gateway가 다시 쓰는 그 호스트 — 코드가 모른 척한 그 모든 '어디서'는, 결국 누군가의 책상 위에 그대로 쌓입니다.
그 빈칸의 이름이 플랫폼팀입니다. 다섯 축이 깔끔하게 밀어낸 모든 '바깥'은, 플랫폼팀에게는 손으로 만들어야 하는 '안쪽'입니다. 그리고 우리가 그 빈칸을 다루는 방식이, 이 연재 전체의 주제입니다.
같은 환경을 두 사람이 본다
개발자에게 환경은 계약입니다. "이런 DB가 있고, 이런 토픽이 있고, 이 주소로 부르면 응답한다." 헥사고날의 언어로 말하면 환경은 Port입니다 — 약속된 모양의 구멍. 그 계약 뒤가 무엇이든 코드는 알 필요가 없고, 알아서도 안 됩니다. 그게 다섯 축이 지키려 한 미덕입니다.
플랫폼팀에게 같은 환경은 전혀 다른 것으로 보입니다. 계약을 실제로 충족시키는 실체(substrate)입니다. 클러스터, 데이터베이스, 네트워크, 권한, 비밀번호처럼 계약서의 빈 구멍 뒤에 들어가 진짜로 응답을 만들어 내는 쪽이죠. 같은 '환경'이라는 단어를 두고 한쪽은 그 모양만 알면 되고, 다른 한쪽은 그 속을 전부 책임집니다.

같은 '환경'이라는 단어를 개발자는 계약(Port)으로, 플랫폼팀은 그 계약을 떠받치는 실체(substrate)로 봅니다.
여기서 똑같은 함정이 기다립니다. 계약의 반대편을 만드는 이 일이, 오랫동안 "코드처럼 보이는 설정"에 머물러 있었다는 것입니다. 다섯 축이 코드 쪽에서 넘은 그 경계를, 플랫폼팀은 실체 쪽에서 한 번 더 넘어야 했습니다.
환경도 코드여야 한다 — 그런데
인프라를 코드로 적는 것만으로는 부족합니다. terraform plan이 보여주는 건 "이 설정이 바뀐다"이지 "이 환경이 동작한다"가 아닙니다. 선언 파일이 수백, 수천 개로 늘어나도, 그 파일들이 서로 모순되지 않는다는 보장은 여전히 사람의 머릿속에만 있습니다.
그래서 backend 연재가 코드에 적용한 그 원리를, 플랫폼팀은 환경에 그대로 적용합니다. 계약은 불변으로 두고, 그 계약을 충족시키는 접점만 교체합니다. 환경의 실체를 만든다는 건 결국 배포의 values 파일, 클라우드의 Cloud Adapter, 그 교체되는 쪽을 손으로 채우는 일입니다.
그래서 플랫폼팀이 환경을 다루는 첫 번째 규율은 단순합니다. 환경을 만드는 일을 "요청"이 아니라 "선언"으로 바꾼다. 사람이 콘솔을 클릭하거나 슬랙으로 부탁하는 대신, 코드 한 조각을 선언하면 환경의 한 조각이 생긴다 — 그게 시작점입니다. 요청은 사람의 기억에 남고, 선언은 git에 남습니다. 이 차이가 생각보다 멀리 갑니다.
YAML 한 줄로 토픽을 선언한다
가장 작은 단위부터 보겠습니다. Kafka 토픽입니다. 환경 '전체'를 찍어내는 거대한 야심에서 출발하면 길을 잃습니다. flex 클라우드플랫폼팀은 선언 하나가 실체 하나로 곧장 이어지는 작은 조각, 즉 토픽부터 선언으로 접었습니다.
과거에 "토픽 하나 추가해 주세요"는 티켓이었습니다. 누군가 콘솔이나 CLI에 들어가 토픽을 만들고, 파티션 수와 retention을 정하고, 어디에 무엇을 만들었는지는 본인의 기억에만 남습니다. 단계마다 사람의 손이 닿고, 손이 닿는 곳마다 재현 불가능성이 쌓입니다. 같은 토픽을 다른 환경에 다시 만들 때 설정이 미묘하게 달라지고, 누가 무엇을 왜 만들었는지는 추적되지 않습니다.
지금은 한 줄의 선언으로 접혀 있습니다. flex-terraform의 토픽 카탈로그에 토픽 이름만 적으면 기본값으로 만들어지고, 필요할 때만 파티션 수나 보관 정책(dlq·retention)을 몇 줄 더합니다.
# modules/flex/kafka-topics/topics/topic-ai.yaml
# yaml-language-server: $schema=../schemas/kafka-topics.schema.json
topics:
command.flex.ai.agent.glossary.sync.v1: {} # 기본값이면 한 줄
command.flex.ai.chat-completion.v1:
partitions: 6
command.flex.ai.rag-resource: # 2단(base+version) 형식
v1:
partitions: 6
dlq: { enabled: true, retention_ms: 172800000 }이 선언은 apply 전에 JSON Schema로 검증됩니다. 오타 난 필드, 빠진 토픽 이름, 허용 범위를 벗어난 파티션 수 — 이런 것들은 클러스터에 닿기 전에, 리뷰 단계에서 걸립니다. 검증을 통과한 선언은 프로비저닝으로 넘어가, 선언한 그대로의 토픽이 실제로 생성됩니다. 사람이 콘솔에서 손으로 만들던 일이, 선언 하나로 같은 결과를 만들어내는 일로 바뀌는 셈입니다.

한 줄의 선언이 검증·프로비저닝을 거쳐 토픽으로 생성되고, 누가 무엇을 만들었는지는 git에 남아 리뷰·추적됩니다. 사람의 손이 닿는 지점이 사라집니다.
"토픽을 추가해 주세요"라는 티켓이, git에 남는 PR 한 줄이 됩니다. 평소 손이 가던 파티션 수와 retention 설정도 같은 한 줄짜리 선언 뒤로 들어갑니다. 개발자는 토픽 이름과 필요한 값만 적고, 그 토픽을 실제 클러스터에 만드는 일은 카탈로그가 떠안습니다.
선언이 곧 거버넌스가 된다
여기서 따라오는 두 번째 이득이 더 큽니다. 환경을 코드로 선언하면, "누가 무엇을 만들었는가"가 저절로 git에 남습니다. 토픽 하나가 추가될 때마다 그것은 리뷰되는 PR이 되고, 승인하는 사람의 이름과 승인된 시각과 변경의 이유가 함께 기록됩니다. 누가 어떤 토픽을 왜 만들었는지는 더 이상 누군가의 기억이나 콘솔 한구석이 아니라, 추적되는 코드입니다.
이건 거버넌스를 위해 따로 만든 장치가 아닙니다. 선언으로 환경을 만들었더니 부산물로 따라온 것입니다. 누가 무엇을 추가했는지 묻는 감사(audit)가 들어왔을 때, 답은 별도 문서가 아니라 git 로그에 이미 있습니다. 선언 파일 자체가 "누가 무엇을 만들었는지"의 원장(ledger)이 됩니다.

토픽 선언 PR이 병합될 때마다 git history에 누가·언제·왜가 함께 남습니다. 거버넌스는 별도 문서가 아니라 선언의 부산물입니다.
이것이 IaC와 IaS의 첫 갈림길입니다. IaC는 "환경을 코드로 적었다"에서 멈출 수 있습니다. IaS는 그 코드가 검증되고, 재현되고, 거버넌스를 만들어내기를 요구합니다. 여는 글에서 약속한 두 가지, 즉 테스트 가능할 것과 재현 가능할 것이 토픽 한 줄에서 이미 작게 지켜지고 있는 셈입니다. JSON Schema 검증이 곧 apply 전 테스트이고, 같은 선언이 dev에서든 prod에서든 같은 토픽을 만들어내는 것이 재현입니다.
그런데, 환경 '전체'를 찍어내려면
토픽 한 줄은 쉬웠습니다. 버킷 한 줄도 쉬웠습니다. 각각은 독립적인 작은 조각이었고, 선언 하나가 실체 하나로 곧장 이어졌습니다. 진짜 질문은 그다음이었습니다. 개발자가 자기만의 격리된 환경 '전체'를 — 클러스터 네임스페이스, 라우팅, 그 안에서 도는 모든 서비스의 배포까지 — 통째로 찍어내게 하려면 어떻게 해야 할까요. 조각 하나를 선언하는 것과, 서로 맞물린 수십 개의 조각을 한 번에 일으켜 세우는 것은 완전히 다른 문제였습니다.
고백하자면, 우리는 이 질문에 한동안 정반대의 답을 들고 있었습니다. 환경은 만들기 어려웠고, 지우기는 더 어려웠습니다. 그래서 한번 만든 환경은 아무도 지우지 않은 채 남았고 — 어느 날, 그렇게 방치된 환경 하나가 공유 dev 전체를 멈춰 세웠습니다. 누군가 "저 dev 잠깐 점령할게요"라고 슬랙에 적던 그 무렵의 일입니다. 그 사고가 우리를 다시 처음부터 생각하게 만들었습니다. 환경이 만들기도 지우기도 부담스러운 한, 선언이라는 규율은 거기서 멈춰 버린다는 것을.
다음 화 ▸ 환경은 브랜치에서 태어난다 — "저 dev 잠깐 점령할게요"라는 슬랙과, 방치된 환경 하나가 공유 dev를 마비시킨 그날의 사고. 그리고 우리가 환경 생성을 일곱 단계에서 브랜치 하나로 접기까지. 이 연재의 심장입니다.
🚀플렉스팀 채용페이지 바로가기
☕flex Private Talk 신청하기