아키텍처 드리프트 감지: 코드와 설계의 정합성 유지하기 - Archyl Blog

아키텍처 드리프트는 문서화된 설계와 실제 코드베이스 사이의 조용한 괴리입니다. 이 가이드는 드리프트의 원인, 감지 방법, 그리고 아키텍처 다이어그램이 허구가 되는 것을 방지하는 방법을 설명합니다.

아키텍처 드리프트 감지: 코드와 설계의 정합성 유지하기

조직 어딘가에 잘못된 아키텍처 다이어그램이 있습니다. 6개월 전에 다른 서비스와 합쳐진 마이크로서비스를 보여주고 있을 수도 있습니다. 프로덕션 장애 중에 팀이 Memcached로 전환했는데 여전히 Redis를 캐싱 레이어로 나열하고 있을 수도 있습니다. 충분한 편법과 우회가 쌓여 스파게티처럼 보이는 서비스를 깔끔한 헥사고날 아키텍처로 설명하고 있을 수도 있습니다.

이것이 아키텍처 드리프트입니다: 시스템의 문서와 실제 작동 방식 사이의 점진적이고 조용한 괴리. 버그와 달리 드리프트는 알림을 발생시키지 않습니다. 성능 저하와 달리 모니터링에 나타나지 않습니다. 누군가 오래된 문서를 기반으로 결정을 내릴 때까지 조용히 있다가 -- 그 결정이 잘못되었음이 드러납니다.

아키텍처 드리프트는 보편적입니다. 모든 팀이 경험합니다. 문제는 문서가 드리프트할지 여부가 아니라, 얼마나 빨리 감지하고 어떻게 대처할 것인가입니다.

아키텍처 드리프트란?

아키텍처 드리프트는 소프트웨어 시스템의 실제 구현이 문서화되거나 의도된 아키텍처에서 벗어날 때 발생합니다. 이 용어는 학술 소프트웨어 엔지니어링 커뮤니티에서 만들어졌지만, 실무 엔지니어라면 누구나 뼈저리게 느끼는 개념입니다.

드리프트는 아키텍처 문서의 모든 수준에서 나타납니다:

구조적 드리프트

문서화된 구조가 더 이상 코드베이스와 일치하지 않습니다:

  • 독립형 컨테이너로 문서화된 서비스가 모놀리스에 흡수됨
  • 컴포넌트 이름이 변경되었지만 다이어그램에는 여전히 이전 이름이 표시됨
  • 새 서비스가 생성되었지만 아키텍처 모델에 추가되지 않음
  • 데이터베이스가 MySQL에서 PostgreSQL로 마이그레이션되었지만 컨테이너 다이어그램에는 여전히 MySQL로 표시됨

행동적 드리프트

문서화된 행동이 더 이상 현실과 일치하지 않습니다:

  • 동기 API 호출이 비동기 메시지로 대체되었지만 관계에는 여전히 "REST/HTTP"로 표시됨
  • 데이터 흐름이 API 게이트웨이를 경유하도록 변경되었지만 다이어그램에는 직접 서비스 간 통신으로 표시됨
  • 시스템 맥락 다이어그램에 반영되지 않은 인증 단계가 추가됨

의존성 드리프트

문서화된 의존성이 더 이상 실제 통합과 일치하지 않습니다:

  • 써드파티 API가 자체 솔루션으로 대체됨
  • 새로운 외부 의존성(결제 제공자, 모니터링 서비스)이 추가되었지만 문서화되지 않음
  • 통합이 폐기되었지만 시스템 맥락 다이어그램에 여전히 표시됨

결정 드리프트

문서화된 아키텍처 결정이 더 이상 따르지 않고 있습니다:

  • ADR에 "모든 영구 저장에 PostgreSQL 사용"이라고 되어 있지만 한 팀이 MongoDB를 사용하기 시작함
  • 적합성 규칙에 "프론트엔드에서 직접 데이터베이스 접근 금지"라고 되어 있지만 누군가 클라이언트 측 Supabase 통합을 추가함
  • 배포 아키텍처에 "단일 리전"이라고 되어 있지만 서비스가 여러 리전에 배포됨

아키텍처 드리프트가 발생하는 이유

드리프트의 원인을 이해하는 것이 방지의 핵심입니다. 드리프트는 보통 악의적이거나 태만한 것이 아니라, 소프트웨어가 개발되는 방식의 자연스러운 결과입니다.

문서보다 속도 우선

금요일까지 기능을 배포해야 할 때, 아키텍처 다이어그램 업데이트는 가장 먼저 빠지는 항목입니다. 코드 변경이 결과물이고, 문서 업데이트는 부가 작업입니다. 단기적으로는 합리적인 행동이지만 장기적으로는 치명적입니다.

많은 작은 변경들

드리프트는 하나의 극적인 순간에 일어나는 경우가 거의 없습니다. 각각이 문서 업데이트를 정당화하기에는 너무 사소한 수백 가지 작은 변경을 통해 축적됩니다:

  • 파일 이름 변경
  • 유틸리티 패키지 추가
  • 라이브러리 의존성 교체
  • 함수를 별도 모듈로 추출

개별 변경 하나하나는 문서 업데이트를 촉발하기에 충분히 중요하지 않습니다. 그러나 합쳐지면 아키텍처가 변형됩니다.

팀원 교체

엔지니어가 떠나면 암묵적 지식도 함께 갑니다. 새 팀은 코드베이스를 물려받지만 왜 그렇게 구성되었는지에 대한 이해는 물려받지 못합니다. 문서가 아닌 코드에서 보이는 것을 기반으로 변경하여 드리프트를 넓힙니다.

피드백 루프의 부재

문서가 현실과 일치하는지 확인하는 사람이 없으면, 드리프트는 보이지 않습니다. 감지 메커니즘 없이는 드리프트를 발견하는 유일한 방법이 장애 발생 시, 감사 시, 또는 새 엔지니어가 다이어그램이 코드와 일치하지 않는다고 지적할 때입니다. 그때쯤이면 드리프트가 광범위할 수 있습니다.

긴급 변경

프로덕션 장애는 종종 아키텍처적 편법을 요구합니다: API 레이어를 거치지 않는 직접 데이터베이스 연결, 설정 서비스 대신 하드코딩된 설정, 영구적이 된 임시 캐시. 이러한 변경은 정상적인 검토 프로세스를 우회하며 거의 문서화되지 않습니다.

아키텍처 드리프트의 비용

드리프트는 단순히 미학적 문제가 아닙니다. 구체적이고 측정 가능한 비용이 있습니다.

잘못된 결정

아키텍트가 오래된 문서를 기반으로 결정을 내리면, 그 결정이 잘못될 수 있습니다. "이 서비스는 트래픽이 적으니 동기 의존성을 감당할 수 있다" -- 하지만 문서가 오래되어 실제로는 문서화된 부하의 10배를 처리하고 있습니다.

느린 온보딩

신규 엔지니어는 멘탈 모델을 구축하기 위해 아키텍처 문서에 의존합니다. 문서가 잘못되면 잘못된 멘탈 모델을 구축합니다. 실제 아키텍처에 맞지 않는 코드를 작성합니다. 혼란을 드러내는 질문을 하여 시니어 엔지니어의 시간을 소비합니다.

장애 대응

프로덕션 장애 중에 아키텍처 다이어그램은 팀이 영향 범위와 의존성을 이해하는 데 도움을 줘야 합니다. 다이어그램이 잘못되면, 팀은 잘못된 의존성 체인을 추적하거나 중요한 업스트림 시스템을 놓치며 귀중한 시간을 낭비합니다.

규정 준수 및 감사 실패

규제 산업에서는 규정 준수(SOC 2, ISO 27001, HIPAA)를 위해 아키텍처 문서가 종종 필요합니다. 감사관이 문서가 현실과 일치하지 않음을 발견하면, 이는 발견 사항 -- 잠재적으로 심각한 것입니다.

AI 에이전트 혼란

AI 코딩 에이전트가 더 보편화되면서, 맥락을 위해 아키텍처 문서에 점점 더 의존하게 됩니다. 오래된 C4 모델을 읽는 에이전트는 실제 아키텍처가 아닌 문서화된 아키텍처에 맞는 코드를 생성합니다. 이는 드리프트를 수정하는 것이 아니라 증폭시킵니다.

아키텍처 드리프트 감지 방법

수동 검토 (전통적 접근)

가장 간단한 접근 방식은 정기적인 수동 검토입니다: 팀을 모으고, 아키텍처 다이어그램을 살펴보며, 여전히 현실과 일치하는지 확인합니다.

이 방식이 효과적인 경우: 작은 팀, 단순한 아키텍처, 분기별 주기.

이 방식이 실패하는 경우: 대규모 시스템, 빠르게 움직이는 팀, 또는 코드를 가장 잘 아는 사람이 검토 회의에 참석할 시간이 없을 때. 수동 검토는 확인 편향도 겪습니다 -- 사람들은 기대하는 것을 보는 경향이 있습니다.

아키텍처 적합성 함수

Neal Ford와 "Building Evolutionary Architectures" 책에서 대중화된 적합성 함수는 아키텍처 속성을 검증하는 자동화된 테스트입니다:

// 예시: 핸들러 패키지에서 직접 데이터베이스 import가 없는지 확인
func TestNoDatabaseImportsInHandlers(t *testing.T) {
    packages := analyzeImports("./internal/handler/...")
    for _, pkg := range packages {
        for _, imp := range pkg.Imports {
            assert.NotContains(t, imp, "database/sql",
                "Handler %s imports database/sql directly", pkg.Name)
            assert.NotContains(t, imp, "gorm.io",
                "Handler %s imports GORM directly", pkg.Name)
        }
    }
}

적합성 함수는 특정 규칙을 시행하는 데 강력하지만, 작성하고 유지하는 데 초기 노력이 필요합니다. 전체 모델이 아닌 제약 조건을 확인합니다.

정적 분석 도구

ArchUnit (Java), Deptrac (PHP), go-arch-lint (Go)와 같은 도구는 코드 구조를 분석하고 의존성 규칙을 시행합니다:

// go-arch-lint 설정
components:
  handler:
    in: ./internal/handler/
  service:
    in: ./internal/service/
  repository:
    in: ./internal/repository/

rules:
  handler:
    can_depend_on: [service]
  service:
    can_depend_on: [repository]
  repository:
    can_depend_on: []

이러한 도구는 단일 코드베이스 내에서 레이어드 아키텍처를 시행하는 데 탁월합니다. 하지만 서비스 간 드리프트를 다루거나 아키텍처 모델이 코드와 일치하는지 검증하지는 않습니다.

자동화된 드리프트 점수 측정

이것이 Archyl이 취하는 접근 방식입니다. 특정 규칙을 확인하는 대신, 전체 아키텍처 모델을 코드베이스와 대조하여 검증합니다:

  • 문서화된 각 시스템이 저장소와 일치하는가?
  • 문서화된 각 컨테이너가 코드베이스의 디렉토리와 일치하는가?
  • 문서화된 각 코드 요소가 여전히 존재하는 파일을 참조하는가?
  • 문서화된 각 관계의 양 끝점이 여전히 유효한가?

결과는 드리프트 점수(0-100)와 정확히 무엇이 드리프트했는지 보여주는 상세 분석입니다. 특정 제약만이 아니라 전체 모델을 검증하기 때문에 가장 포괄적인 접근 방식입니다.

Archyl의 드리프트 감지에서 핵심 설계 결정:

경량. AI 토큰이 소비되지 않고, 파일 내용을 읽지 않습니다. Git 제공자 API에 대한 파일 경로 존재 확인만 수행합니다. 드리프트 점수 측정이 몇 분이 아니라 몇 초 만에 완료됩니다.

결정적. 같은 코드베이스, 같은 모델, 같은 점수. LLM 온도나 프롬프트 엔지니어링으로 인한 변동이 없습니다.

저비용. 비용 걱정 없이 모든 push에서 실행합니다. 하루 백 번의 계산도 문제없습니다.

실행 가능. 분석에서 정확히 어떤 요소가 드리프트했는지 보여주므로 무엇을 수정해야 하는지 알 수 있습니다.

아키텍처 드리프트 방지 방법

감지는 필요하지만 충분하지 않습니다. 목표는 드리프트가 처음부터 축적되는 것을 방지하는 것입니다.

문서 업데이트를 완료 정의의 일부로 만들기

코드 변경이 아키텍처를 수정하면, PR에 문서 업데이트가 포함되어야 합니다. PR 템플릿에 체크박스를 추가하세요:

## 체크리스트
- [ ] 테스트 통과
- [ ] 코드 리뷰 완료
- [ ] 아키텍처 문서 업데이트 (해당하는 경우)

이것이 모든 것을 잡아내지는 못하지만, 문서가 일급 산출물이라는 기대를 확립합니다.

CI에서 드리프트 감지 자동화

가장 효과적인 단일 방지 메커니즘은 드리프트가 임계값을 초과하면 실패하는 CI 게이트입니다:

on:
  push:
    branches: [main]

jobs:
  drift:
    runs-on: ubuntu-latest
    steps:
      - uses: archyl-com/actions/drift-score@v1
        with:
          api-key: ${{ secrets.ARCHYL_API_KEY }}
          organization-id: ${{ secrets.ARCHYL_ORG_ID }}
          project-id: 'your-project-uuid'
          threshold: '70'

드리프트 점수가 떨어져 빌드가 실패하면, 머지하기 전에 누군가가 수정해야 합니다. 문서 정확도가 테스트 통과만큼 타협 불가능해집니다.

낮은 임계값(50-60%)으로 시작하고 팀이 습관을 들이면서 점진적으로 높이세요.

Architecture-as-Code 사용

아키텍처 모델이 텍스트 기반 형식(Structurizr DSL, Archyl YAML)으로 정의되면, 코드와 함께 버전 관리할 수 있습니다. 이것은 다음을 의미합니다:

  • 아키텍처 변경이 Pull request에 나타남
  • 변경이 팀에 의해 검토됨
  • 아키텍처 진화의 이력이 Git에 기록됨

변경이 보이지 않고 검토할 수 없는 GUI 도구에서 정의된 아키텍처보다 훨씬 낫습니다.

드리프트 알림 설정

Archyl은 드리프트 이벤트에 대한 웹훅 알림을 지원합니다:

  • drift.score_computed: 모든 드리프트 계산 시 발동합니다. 가시성을 위해 Slack 채널에 게시합니다.
  • drift.score_degraded: 점수가 10점 이상 하락할 때 발동합니다. 이것이 조기 경보 시스템입니다.

팀이 모니터링하는 채널에 이 알림을 설정하세요. 인식이 행동의 첫 단계입니다.

아키텍처 리뷰 실시

월간 또는 분기별 아키텍처 리뷰는 여러 목적을 수행합니다:

  • 문서화된 아키텍처가 여전히 현실과 일치하는지 검증
  • 자동화 도구가 놓친 드리프트 식별 (예: 행동적 드리프트)
  • 드리프트한 컴포넌트를 코드에서 업데이트해야 하는지 문서에서 업데이트해야 하는지 논의
  • 재검토가 필요할 수 있는 결정에 대해 ADR 검토 및 업데이트

적합성 규칙 도입

적합성 규칙은 항상 참이어야 하는 아키텍처 제약을 정의합니다:

  • "프론트엔드 컨테이너는 데이터베이스 컨테이너에 의존해서는 안 된다"
  • "모든 공개 API는 API 게이트웨이를 통해야 한다"
  • "각 서비스는 자체 데이터베이스를 소유해야 한다 (공유 데이터베이스 금지)"

Archyl에서 적합성 규칙은 플랫폼에서 정의되고 적합성 검사 기능을 통해 시행됩니다. AI 에이전트는 MCP를 통해 이러한 규칙을 읽고 코드 생성 시 준수할 수 있습니다.

적합성 규칙은 드리프트 감지를 보완합니다. 드리프트 감지는 모델이 현실과 일치하는지 확인합니다. 적합성 검사는 현실이 규칙을 따르는지 확인합니다.

아키텍처 드리프트 vs. 아키텍처 침식

이 용어들은 관련되어 있지만 구별됩니다:

아키텍처 드리프트는 문서와 구현 사이의 괴리입니다. 코드 자체는 완벽할 수 있습니다 -- 문서만 잘못된 것입니다.

아키텍처 침식은 아키텍처 자체의 열화입니다. 코드가 아키텍처 원칙을 위반하고, 기술 부채를 축적하며, 유지보수가 어려워집니다. 침식은 코드 품질 문제입니다. 드리프트는 문서 정확성 문제입니다.

이 둘은 종종 동시에 발생합니다. 문서가 드리프트하면, 팀은 의도된 아키텍처에 대한 인식을 잃습니다. 그 인식 없이는 아키텍처를 침식시키는 변경을 합니다. 드리프트가 침식을 가능하게 합니다.

이것이 드리프트 감지가 단순한 문서 정확성을 넘어 중요한 이유입니다. 정확한 문서는 침식을 방지하는 참조 역할을 합니다. 모든 사람이 의도된 아키텍처를 볼 수 있으면, 그것을 유지할 가능성이 더 높습니다.

시간에 따른 드리프트 측정 및 추적

단일 드리프트 점수도 유용합니다. 추세는 더 강력합니다.

기준선 설정

첫 번째 드리프트 계산을 실행하여 현재 위치를 파악합니다. 점수가 낮더라도 당황하지 마세요 -- 아키텍처 문서를 적극적으로 유지하지 않은 대부분의 팀은 40-70% 사이의 점수를 볼 것입니다.

목표 설정

현실적인 개선 목표를 수립합니다:

  • 1개월차: 가장 명백한 드리프트를 수정하여 기준선에서 60%로 개선
  • 3개월차: 워크플로에 문서 업데이트를 통합하여 75% 도달
  • 6개월차: CI 게이트와 정기 검토를 통해 80% 이상 유지

추세 추적

Archyl은 모든 드리프트 계산을 전체 분석과 함께 저장합니다. 드리프트 이력 뷰는 점수의 타임라인을 보여주어 다음을 확인할 수 있습니다:

  • 시간이 지남에 따라 드리프트가 나아지고 있는가 악화되고 있는가?
  • 특정 스프린트나 릴리스가 상당한 하락을 야기했는가?
  • CI 임계값이 저하를 방지하고 있는가?

개선 축하

팀이 드리프트 점수를 개선하면 인정합니다. 아키텍처 문서화는 감사받지 못하는 작업입니다. 진행 상황을 가시적으로 만들고 인정하면 행동이 강화됩니다.

AI 지원 개발에서 드리프트 감지의 역할

AI 코딩 에이전트의 부상은 드리프트 감지를 그 어느 때보다 중요하게 만듭니다.

AI 에이전트는 맥락을 위해 아키텍처 문서에 점점 더 의존하고 있습니다. MCP와 같은 프로토콜을 통해 에이전트는 코드를 생성하기 전에 C4 모델, ADR, 적합성 규칙을 읽을 수 있습니다. 이것은 에이전트를 더 효과적으로 만듭니다 -- 추측 대신 아키텍처에 맞는 코드를 생성합니다.

하지만 이것은 문서가 정확할 때만 작동합니다. 오래된 C4 모델을 읽고 그것을 기반으로 코드를 생성하는 에이전트는 잘못된 아키텍처에 맞는 코드를 생산합니다. 에이전트가 드리프트를 방지하는 것이 아니라 증폭시킵니다.

드리프트 감지는 AI 에이전트를 정직하게 유지하는 피드백 루프를 만듭니다:

  1. 에이전트가 아키텍처를 읽음 (MCP를 통해)
  2. 에이전트가 코드를 생성함 (문서화된 아키텍처에 맞게)
  3. 코드가 머지됨 (실제 아키텍처가 변경될 수 있음)
  4. 드리프트 감지가 실행됨 (괴리를 포착)
  5. CI 게이트가 실패함 (드리프트가 임계값을 초과하면)
  6. 팀이 문서를 업데이트함 (현실을 반영)
  7. 에이전트가 업데이트된 아키텍처를 읽음 -- 루프가 닫힘

4단계 없이는 루프가 열려 있습니다. 문서는 점점 더 허구가 됩니다. 에이전트는 점점 더 환상의 아키텍처에 맞는 코드를 생성합니다. 격차가 매 커밋마다 넓어집니다.

드리프트 감지가 이 루프를 닫는 메커니즘입니다.

드리프트 감지 시작하기

아키텍처 문서가 없는 경우

AI 발견부터 시작하세요. 저장소를 Archyl에 연결하고 발견을 실행하여 생성된 C4 모델을 검토합니다. 이렇게 하면 대략 70-80% 정확한 기준 모델을 얻을 수 있습니다. 그런 다음 드리프트 감지를 설정하여 그 정확성을 유지합니다.

기존 문서가 있는 경우

아키텍처 모델을 드리프트 감지를 지원하는 도구에 가져오거나 다시 생성합니다. 첫 번째 드리프트 계산을 실행합니다. 점수가 현재 문서가 얼마나 정확한지 정확히 알려주고 -- 분석이 먼저 수정해야 할 것을 보여줍니다.

이미 드리프트를 추적하고 있는 경우

드리프트 감지를 CI에 통합합니다. 임계값을 설정합니다. 알림을 구성합니다. 추세를 추적하기 시작합니다. 드리프트를 일회성 감사가 아닌 팀 지표로 만듭니다.

어디서 시작하든

가장 중요한 것은 시작하는 것입니다. 아키텍처 드리프트는 기술 부채처럼 시간이 지남에 따라 복리로 쌓입니다. 해결을 늦출수록 따라잡는 데 더 많은 작업이 필요합니다. 하지만 기술 부채와 달리, 드리프트 감지는 몇 분 만에 설정할 수 있으며 즉각적인 가치를 제공합니다.

아키텍처 문서가 현실을 반영하고 있거나 그렇지 않거나 둘 중 하나입니다. 이제 어느 쪽인지 측정할 수 있습니다.


아키텍처 문서 유지에 대해 더 알아보기: 아키텍처 드리프트 점수: 작동 방식 | C4 Model이란? | AI 기반 아키텍처 문서화. 또는 Archyl 무료 체험으로 몇 분 만에 첫 번째 드리프트 점수를 계산하세요.