Architecture as Code: 시스템 설계를 프로그래밍 방식으로 정의하기
소프트웨어 엔지니어링에는 몇 년마다 반복되는 패턴이 있습니다. 수동적이고 시각적이었던 관행이 코드화되고, 버전 관리되고, 자동화되면 -- 모든 것이 좋아집니다.
인프라에서 그랬습니다. 클라우드 콘솔을 클릭하는 것에서 Terraform 파일을 작성하는 것으로 바뀌었습니다. 설정에서 그랬습니다. 서버에서 설정 파일을 편집하는 것에서 Kubernetes 매니페스트로 원하는 상태를 선언하는 것으로 바뀌었습니다. 데이터베이스 스키마에서 그랬습니다. 수동으로 SQL 스크립트를 실행하는 것에서 마이그레이션 파일을 작성하는 것으로 바뀌었습니다.
이제 아키텍처 문서에서 그 일이 일어나고 있습니다. Architecture as code는 시스템 설계를 프로그래밍 방식으로 정의하는 관행입니다 -- 버전 관리되고, 리뷰되고, 테스트되고, 애플리케이션 코드와 같은 파이프라인을 통해 배포될 수 있는 구조화된 텍스트 파일로.
이 가이드는 architecture as code에 대해 알아야 할 모든 것을 다룹니다: 정의, 중요성, 시각적 접근 방식과의 비교, 실제 구현 방법.
Architecture as Code란?
Architecture as code (AaC)는 소프트웨어 아키텍처를 기계가 읽을 수 있고 사람이 작성할 수 있는 텍스트 파일로 정의하는 관행입니다. 시각적 도구에서 박스와 화살표를 그리는 대신, YAML, JSON 또는 전용 DSL과 같은 구조화된 형식으로 시스템, 컨테이너, 컴포넌트 및 그 관계를 설명합니다.
YAML로 정의된 아키텍처의 간단한 예시:
version: "1.0"
project:
name: "Payment Platform"
description: "조직의 모든 결제 처리를 담당"
systems:
- name: Payment Platform
type: software_system
description: "핵심 결제 처리 시스템"
containers:
- name: Payment API
type: api
description: "결제 작업용 REST API"
technologies: [Go, gRPC, OpenAPI]
- name: Payment Processor
type: service
description: "결제 트랜잭션 처리"
technologies: [Go]
- name: Transaction Database
type: database
description: "트랜잭션 기록 저장"
technologies: [PostgreSQL]
- name: Payment Queue
type: queue
description: "비동기 결제 처리 큐"
technologies: [Kafka]
- name: Stripe
type: external_system
description: "써드파티 결제 게이트웨이"
relationships:
- from: Payment API
to: Payment Processor
label: "결제 요청 전달"
type: uses
- from: Payment Processor
to: Transaction Database
label: "트랜잭션 영속화"
type: writes_to
- from: Payment Processor
to: Payment Queue
label: "결제 이벤트 발행"
type: publishes_to
- from: Payment Processor
to: Stripe
label: "카드 결제 처리"
type: uses
이 파일이 아키텍처의 완전한 소스 오브 트루스입니다. Archyl과 같은 도구가 이를 읽고, C4 모델을 구축하고, 인터랙티브 다이어그램을 렌더링하며, 모든 것을 동기화 상태로 유지합니다. 파일은 설명하는 코드 바로 옆의 Git 저장소에 있습니다.
시각 전용 접근 방식의 한계
Architecture as code 이전에는 팀이 일반적으로 시각적 도구(Lucidchart, draw.io, Miro, Figma)를 사용하여 아키텍처를 문서화했습니다. 이러한 도구는 브레인스토밍과 초기 설계 세션에 탁월하지만, 장기 문서로서는 근본적인 한계가 있습니다:
버전 관리 불가
시각적 다이어그램은 의미 있게 diff할 수 없는 바이너리 또는 독점 파일로 저장됩니다. 누군가 다이어그램을 변경하면 변경되었다는 것은 알 수 있지만, 무엇이 변경되었는지는 알 수 없습니다. draw.io 파일에 대한 git diff에 해당하는 것이 없습니다. Pull request에서 코드 변경을 리뷰하듯 다이어그램 변경을 리뷰할 수 없습니다.
Architecture as code에서는 모든 변경이 텍스트 diff입니다. 새 서비스를 추가하는 것은 몇 줄의 YAML입니다. 컴포넌트 이름을 바꾸는 것은 한 줄 변경입니다. 리뷰어는 무엇이 변경되었고, 왜 변경되었는지(커밋 메시지에서), 승인하거나 수정을 요청할 수 있습니다.
자동화 불가
시각적 다이어그램은 고립되어 존재합니다. 작업을 트리거하거나, 규칙을 검증하거나, CI/CD 파이프라인과 통합할 수 없습니다. 다이어그램에 10개 서비스가 있다고 하지만 Kubernetes 클러스터에서 12개가 실행되어도, 아무것도 불일치를 감지하지 못합니다.
Architecture as code는 자동화를 가능하게 합니다. 아키텍처 정의를 실제 인프라와 대조하는 유효성 검사 규칙을 작성할 수 있습니다. 아키텍처 파일에서 문서를 생성할 수 있습니다. 아키텍처 파일이 현실에서 벗어나면 알림을 트리거할 수 있습니다.
대규모 협업 불가
두 사람이 같은 시각적 다이어그램을 동시에 편집하면, 충돌은 보통 한 사람의 변경이 다른 사람의 것을 덮어쓰는 것으로 해결됩니다. 시각적 파일에 대한 머지 전략이 없습니다.
Architecture as code에서는 표준 Git 머지 워크플로가 적용됩니다. 두 팀이 아키텍처 파일의 다른 부분을 수정하면 Git이 깔끔하게 머지합니다. 충돌이 발생하면 코드 충돌과 같은 방식으로 해결됩니다 -- 논의와 의도적인 해결을 통해.
일관성 보장 불가
시각적 다이어그램에는 무엇이든 포함할 수 있습니다. 박스가 불일치하게 레이블링될 수 있습니다. 화살표가 같은 다이어그램의 다른 부분에서 다른 것을 의미할 수 있습니다. 스키마도, 유효성 검사도, 네이밍 규칙의 강제도 없습니다.
Architecture as code 파일에는 스키마가 있습니다. 도구가 모든 변경에서 파일을 검증합니다. 존재하지 않는 컨테이너를 참조하면 유효성 검사가 잡아냅니다. 유효하지 않은 관계 유형을 사용하면 변경이 머지되기 전에 플래그됩니다.
종속과 이식성
시각적 다이어그램은 종종 만든 도구에 종속됩니다. Lucidchart에서 draw.io로 이동하면 모든 다이어그램을 수동으로 다시 만들어야 합니다. 한 architecture-as-code 도구에서 다른 것으로 이동하는 것은 형식 변환입니다 -- 자동화되고 반복 가능합니다.
Architecture as Code의 이점
단일 소스 오브 트루스
아키텍처가 단일 파일(또는 파일 세트)로 정의되면, 정확히 한 곳만 보면 됩니다. 어떤 다이어그램이 최신인지, 어떤 Confluence 페이지에 최신 버전이 있는지, 지난달에 누군가 이메일로 보낸 PDF가 여전히 정확한지에 대한 의문이 없습니다.
아키텍처 변경에 대한 코드 리뷰
이것이 아마 가장 변혁적인 이점입니다. 아키텍처 변경이 Pull request를 통과하면, 코드 변경과 같은 수준의 검토를 받습니다. 시니어 아키텍트가 서비스 분할이 일어나기 전에 검토할 수 있습니다. 팀이 새 의존성의 영향을 도입되기 전에 논의할 수 있습니다.
+ - name: Notification Service
+ type: service
+ description: "이메일, SMS, 푸시 알림 처리"
+ technologies: [Python, Celery, Redis]
+
+ - from: Order Service
+ to: Notification Service
+ label: "주문 알림 트리거"
+ type: uses
이 diff는 명확한 이야기를 전달합니다: 누군가 Notification Service를 추가하고 Order Service에 연결하고 있습니다. 리뷰어는 질문하거나, 대안 기술을 제안하거나, 다른 서비스 경계를 제안할 수 있습니다 -- 모두 애플리케이션 코드 한 줄 작성되기 전에.
Git 히스토리가 곧 아키텍처 히스토리
아키텍처 파일에 대한 모든 커밋은 아키텍처가 어떻게 발전했는지에 대한 영구적인 기록을 만듭니다. 다음과 같은 질문에 답할 수 있습니다:
- Search Service는 언제 추가되었나?
- MySQL에서 PostgreSQL로의 마이그레이션을 누가 승인했나?
- 6개월 전 아키텍처는 어떤 모습이었나?
- 서비스 수가 시간에 따라 어떻게 증가했나?
이 히스토리는 시스템의 진화를 이해하고 새 팀원을 온보딩하는 데 매우 귀중합니다.
CI/CD 통합
Architecture as code는 지속적 통합 및 지속적 배포 파이프라인에 자연스럽게 통합됩니다. 모든 Pull request에서 다음을 수행할 수 있습니다:
- 아키텍처 파일을 스키마에 대해 유효성 검사
- 적합성 규칙 확인 (예: 모든 서비스에 문서화된 소유자가 있어야 함)
- 업데이트된 다이어그램 생성
- 문서화된 아키텍처와 실행 중인 시스템 간의 드리프트 감지
- 아키텍처를 문서 플랫폼에 게시
이것은 아키텍처 문서를 부패하는 정적 문서가 아닌 살아 있는 산출물로 만듭니다.
리팩토링과 자동화
아키텍처 정의가 구조화된 데이터이기 때문에, 이를 조작하는 스크립트를 작성할 수 있습니다. 모든 관계에서 서비스 이름을 변경해야 하나요? YAML 파일에서 간단한 찾기-바꾸기. PostgreSQL을 사용하는 모든 서비스의 보고서를 생성해야 하나요? YAML을 파싱하고 기술로 필터링. 네이밍 규칙을 강제해야 하나요? 린터를 작성하세요.
Architecture as Code 형식과 DSL
아키텍처를 코드로 정의하기 위한 여러 형식과 DSL이 존재합니다. 가장 일반적인 접근 방식의 개요입니다.
Structurizr DSL
C4 모델의 창시자인 Simon Brown이 만든 Structurizr DSL은 가장 초기의 architecture-as-code 형식 중 하나입니다. 커스텀 DSL 구문을 사용합니다:
workspace {
model {
user = person "User"
softwareSystem = softwareSystem "My Software System" {
webapp = container "Web Application" "Delivers content" "Java"
database = container "Database" "Stores data" "PostgreSQL"
}
user -> webapp "Uses"
webapp -> database "Reads from and writes to"
}
views {
systemContext softwareSystem {
include *
autolayout lr
}
}
}
Structurizr는 C4 모델을 위한 architecture as code 개념을 개척했습니다. 그러나 커스텀 DSL 구문은 학습 곡선이 있고, 렌더링에 Structurizr 전용 도구가 필요합니다.
YAML 기반 접근 방식
YAML은 DevOps에서 선언적 설정의 사실상 표준이 되었습니다(Kubernetes, Docker Compose, GitHub Actions, Terraform HCL은 제외). 아키텍처 정의에 YAML을 사용하면 익숙함의 장점이 있습니다 -- 대부분의 개발자가 이미 YAML을 읽고 쓸 수 있습니다.
Archyl의 archyl.yaml 형식이 이 접근 방식을 취합니다:
version: "1.0"
systems:
- name: E-Commerce Platform
type: software_system
containers:
- name: Web Frontend
type: webapp
technologies: [React, TypeScript, Next.js]
- name: API Service
type: api
technologies: [Go, gRPC]
components:
- name: Auth Handler
type: handler
technologies: [JWT, OAuth2]
- name: Product Handler
type: handler
technologies: [REST]
- name: Product Database
type: database
technologies: [PostgreSQL]
relationships:
- from: Web Frontend
to: API Service
label: "API 호출"
- from: API Service
to: Product Database
label: "상품 데이터 읽기/쓰기"
중첩 구조가 C4 계층 구조를 직접 반영합니다: 시스템은 컨테이너를 포함하고, 컨테이너는 컴포넌트를 포함합니다. 관계는 인간이 읽을 수 있는 이름을 사용하며 모호한 경우 점 표기법으로 구분합니다. 형식은 grep 가능하고, diff 가능하며, 특수 도구 없이 읽을 수 있습니다.
JSON 및 기타 형식
일부 도구는 JSON, TOML 또는 기타 구조화된 형식을 사용합니다. 구체적인 형식보다 원칙이 더 중요합니다: 아키텍처 정의는 텍스트 기반이고, 버전 관리 가능하며, 기계 파싱 가능해야 합니다.
Architecture as Code 구현: 실용적 워크플로
팀에서 architecture as code를 도입하기 위한 단계별 워크플로입니다.
1단계: 기존 것에서 시작
첫날부터 전체 아키텍처를 문서화하려 하지 마세요. Container 다이어그램부터 시작하세요 -- 서비스 랜드스케이프. 모든 배포 가능한 서비스, 기술 스택, 서비스 간 핵심 관계를 나열합니다.
Archyl을 사용하는 경우, UI에서 시각적으로 모델을 만든 다음 archyl.yaml로 내보내거나, YAML 파일을 처음부터 작성할 수 있습니다. 어느 경로든 같은 결과에 도달합니다.
2단계: 저장소에 커밋
아키텍처 파일을 주요 저장소의 루트에(또는 코드베이스가 여러 저장소로 분할된 경우 전용 아키텍처 저장소에) 배치합니다. 위치보다 원칙이 중요합니다: 파일은 Git에 있어야 하고 코드 리뷰를 거쳐야 합니다.
my-platform/
archyl.yaml # 아키텍처 정의
src/
docker-compose.yml
.github/
workflows/
architecture.yml # 아키텍처용 CI 파이프라인
3단계: CI/CD 동기화 설정
메인 브랜치에 대한 모든 머지에서 아키텍처 파일을 Archyl과 동기화하도록 CI/CD 파이프라인을 구성합니다. 이렇게 하면 Archyl의 시각적 다이어그램과 인터랙티브 문서가 항상 최신 커밋된 아키텍처를 반영합니다.
GitHub Actions 워크플로 예시:
name: Sync Architecture
on:
push:
branches: [main]
paths: [archyl.yaml]
jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Sync to Archyl
run: |
curl -X POST https://api.archyl.com/v1/sync \
-H "Authorization: Bearer ${{ secrets.ARCHYL_TOKEN }}" \
-H "Content-Type: application/yaml" \
--data-binary @archyl.yaml
4단계: Pull Request를 통해 아키텍처 변경
이 시점부터 아키텍처 변경은 코드 변경과 같은 워크플로를 따릅니다:
- 브랜치 생성
archyl.yaml파일 수정- Pull request 열기
- 팀의 리뷰 받기
- 메인에 머지
- CI/CD가 변경을 Archyl에 동기화
이것은 아키텍처 변경에 코드 변경과 같은 가시성, 책임, 추적성을 부여합니다.
5단계: 적합성 규칙 추가
Architecture-as-code 관행이 성숙해지면, 아키텍처 정의를 자동으로 검증하는 적합성 규칙을 추가합니다. 예시:
- 모든 컨테이너에 최소 하나의 기술이 지정되어야 함
- 모든 외부 시스템에 설명이 있어야 함
- 고아 컨테이너 금지 (모든 컨테이너에 최소 하나의 관계가 있어야 함)
- 네이밍 규칙 준수 (예: 서비스가 "Service"로 끝나야 함)
Archyl의 적합성 규칙 엔진이 이러한 규칙을 자동으로 평가하고 CI 파이프라인 또는 Archyl 대시보드에서 위반 사항을 보고할 수 있습니다.
6단계: 시간에 따라 정의 발전시키기
시스템과 컨테이너부터 시작합니다. 특정 서비스가 내부 문서화가 필요할 만큼 복잡해지면 컴포넌트를 추가합니다. 중요한 아키텍처 결정을 내릴 때 ADR을 추가합니다. 서비스 경계를 공식화할 때 API 계약을 추가합니다.
아키텍처 파일이 시스템과 함께 유기적으로 성장합니다. 모든 세부사항을 사전에 준비할 필요가 없습니다.
Architecture as Code vs. Infrastructure as Code
Architecture as code와 Infrastructure as Code (IaC)는 상호 보완적이지만 별개의 관행입니다.
Infrastructure as Code (Terraform, Pulumi, CloudFormation)는 무엇을 배포하고 어떻게 구성할지 정의합니다. 운영적입니다: 서버를 프로비저닝하고, 네트워크를 구성하고, 클라우드 리소스를 관리합니다.
Architecture as code는 시스템이 어떤 모습이며 부분들이 어떻게 관련되는지 정의합니다. 기술적입니다: 개념적 구조, 기술 선택, 서비스 경계를 문서화합니다.
이상적인 설정은 둘을 결합합니다:
- Terraform 파일이 인프라를 정의
archyl.yaml이 아키텍처를 정의- 적합성 규칙이 둘의 정합성을 확인
Terraform이 새 서비스를 추가했지만 아키텍처 파일에 언급되지 않으면, 드리프트 감지가 불일치를 잡아냅니다.
AI 어시스턴트와 함께하는 Architecture as Code
Architecture as code의 가장 매력적인 장점 중 하나는 AI 어시스턴트가 이를 읽고 추론할 수 있다는 것입니다. 아키텍처가 구조화된 텍스트로 정의되면, Claude Code나 Cursor와 같은 도구가 다음을 할 수 있습니다:
- YAML 파일을 쿼리하여 아키텍처에 대한 질문에 답하기
- 현재 상태를 기반으로 아키텍처 변경 제안
- 문서화된 아키텍처를 존중하는 코드 생성 (예: 올바른 데이터베이스를 올바른 서비스에 사용)
- 코드와 아키텍처 정의 간의 불일치 감지
Archyl은 MCP 서버로 이를 더 발전시킵니다. AI 어시스턴트가 단순히 아키텍처 파일을 읽는 것이 아니라, 라이브 아키텍처 모델을 쿼리하고, 관계를 탐색하고, 수정까지 제안할 수 있습니다. 아키텍처가 정적 문서가 아닌 프로그래밍 가능하고 쿼리 가능한 데이터 소스가 됩니다.
흔한 실수
형식을 과도하게 설계하기
YAML이나 기존 형식이 작동할 때 커스텀 DSL을 설계하지 마세요. 목표는 도입이며, 형식이 익숙할 때 도입이 더 쉽습니다. 대부분의 개발자가 이미 Docker Compose, Kubernetes, CI/CD 설정에서 YAML을 알고 있습니다.
모든 것을 포착하려 하기
Architecture as code는 시스템의 구조적 측면을 포착해야 합니다: 무엇이 존재하며, 어떻게 연결되고, 어떤 기술이 사용되는지. 아키텍처 파일에 운영 세부사항(스케일링 정책 등), 런타임 설정(환경 변수 등), 행동 명세(API 응답 형식 등)를 포함하려 하지 마세요.
워크플로를 강제하지 않기
Architecture as code는 변경이 정의된 워크플로를 통과할 때만 작동합니다. 사람들이 아키텍처 파일을 우회하고 시각적 도구에서 직접 변경하면 파일이 오래됩니다. 어떤 방향이 권위적인지에 대한 명확한 규칙을 수립하세요.
시각적 출력 무시
Architecture as code는 시각적 다이어그램의 대체가 아닙니다 -- 이를 생산하는 더 나은 방법입니다. 텍스트 파일이 소스 오브 트루스이지만, 렌더링된 다이어그램이 사람들이 실제로 일상적으로 보는 것입니다. 시각적 출력이 접근 가능하고, 최신이며, 탐색하기 쉬운지 확인하세요.
Archyl로 시작하기
Archyl은 처음부터 architecture as code를 지원하도록 설계되었습니다. 플랫폼이 제공하는 것:
- 시스템, 컨테이너, 컴포넌트, 관계, 기술을 포함한 전체 C4 모델을 다루는 YAML 기반 DSL
- UI에서 시각적으로 모델링하고 YAML로 내보내거나 YAML을 작성하고 UI에 동기화하는 양방향 동기화
- 모든 커밋에서 자동 동기화를 위한 CI/CD 통합
- 아키텍처 정의를 표준에 대해 검증하는 적합성 규칙
- 아키텍처를 AI 어시스턴트가 쿼리할 수 있게 하는 MCP 서버
- 코드 리뷰, 코멘트, 팀 소유권이 있는 협업 기능
처음부터 시작하든 시각적 다이어그램에서 마이그레이션하든, Archyl은 모든 규모의 팀에게 architecture as code를 실용적으로 만듭니다.
Architecture as code 시작하기로 인프라와 애플리케이션 코드에 이미 가져온 것과 같은 엄격함을 아키텍처 문서에도 가져오세요.