AI가 규칙을 "알잘딱" 지키는 백엔드 레포 만들기

코드베이스의 구조와 결정론적 검증 시스템으로 만드는 AI Native 개발 환경 리팩토링하기

Perry • Software Enginner

  • 엔지니어링

안녕하세요, 채널톡 소프트웨어 엔지니어 페리입니다.

채널톡 팀은 Claude Code, Cursor 등 AI 코딩 도구를 적극적으로 활용하고 있습니다. 코드 생성, 리팩토링, 테스트 작성 등 다양한 작업에서 AI의 도움을 받고 있는데요. 그 과정에서 한 가지 뚜렷한 문제를 발견했습니다.

AI가 우리 프로젝트의 아키텍처 규칙을 지키지 않는다는 것입니다. CLAUDE.md에 "이 모듈은 저 모듈을 import하면 안 됩니다"라고 적어도, AI는 종종 무시합니다. 결국 사람이 리뷰에서 잡아야 하는데, 그러면 AI를 쓰는 의미가 반감됩니다.

이 글에서는 채널톡의 Go 백엔드 서비스에서 이 문제를 어떻게 해결했는지를 다룹니다. DDD 기반 리팩토링과 커스텀 아키텍처 테스트 도입을 통해, LLM이 안정적으로 코드를 생성할 수 있는 환경을 만든 과정입니다.

DDD(Domain-Driven Design) 가 익숙하지 않은 분을 위해 간단히 설명하면, 비즈니스 도메인(예: 주문, 결제, 사용자)을 중심으로 코드를 구성하는 설계 방법론입니다. 핵심은 관련된 코드를 하나의 모듈(bounded context)로 묶고, 모듈 간 경계를 명확하게 유지하는 것입니다.

1. 프롬프트의 한계: CLAUDE.md는 왜 잘 안 되는가

1.1 우리가 직접 겪은 것

컨텍스트 파일이란? AI 코딩 도구가 프로젝트를 이해할 수 있도록 레포지토리 루트에 두는 설명서입니다. CLAUDE.md(Claude Code용), .cursorrules(Cursor용) 등이 대표적입니다. 코딩 규칙, 아키텍처 설명, 금지 사항 등을 자연어로 기술합니다.

우리 팀도 처음에는 CLAUDE.md에 기대를 걸었습니다. 의존성 규칙, 네이밍 규칙, 디렉토리 구조 등을 정성껏 적었습니다. 하지만 특히 "하지 마라" 계열의 지침 — 예를 들어 "서브도메인 간 직접 의존은 금지" — 은 무시되는 빈도가 높았습니다.

1.2 연구 결과도 같은 이야기를 한다

이건 우리만의 경험이 아니었습니다. 최근 발표된 연구 결과가 이를 뒷받침합니다.

지표

결과

SWE-bench Lite 성공률

LLM 생성 컨텍스트 추가 시 0.5% 하락

AgentBench 성공률

2% 하락

추론 비용

20~23% 증가

정성 들여 직접 작성한 경우

최대 4% 향상

문서화가 전혀 없는 레포

2.7% 긍정 효과 (유일한 양수)

컨텍스트 파일을 열심히 작성해도 성능은 거의 개선되지 않고, 오히려 비용만 올라갑니다. 의미 있는 개선을 보인 유일한 경우는 문서화가 전혀 없는 레포뿐이었습니다.

1.3 왜 그런가?

직관적으로는 "규칙을 더 많이 알려주면 더 잘 따를 것"이라고 생각합니다. 하지만 실제로는 그렇지 않습니다.

  • 자연어의 모호성: "서브도메인 간 import를 지양해주세요"라는 지침은 "절대 하지 마"인지 "가급적이면"인지 AI가 판단할 수 없습니다

  • 읽었다고 따르지는 않음: AI가 컨텍스트를 "읽었다"는 것과 "따른다"는 것은 전혀 다릅니다. 특히 긴 컨텍스트에서 후반부의 지침은 무시될 확률이 높습니다

  • 규칙 충돌: 규칙이 많아지면 서로 모순되는 경우가 생기고, AI는 어느 쪽을 따를지 임의로 결정합니다

연구의 결론은 명확합니다.

"컨텍스트 파일에 지침을 늘리는 것보다 유닛 테스트와 타입 체크를 강화하는 게 우선이다."

우리 팀에서도 **CLAUDE.md 사용을 최소화하고, 테스트 + 타입 체크 + 린터 + 아키텍처 테스트 같은 결정론적 코드 제어를 강화하는 방향**을 선택했습니다.

2. 대안: 결정론적 코드 제어

2.1 두 가지 접근법

AI에게 코딩 규칙을 전달하는 방법은 크게 두 가지입니다.

프롬프트 기반 (CLAUDE.md)

결정론적 제어 (테스트/린터)

방식

"이렇게 짜라" 자연어 지침

잘못 짜면 빌드/테스트 실패

강제력

없음 (무시 가능)

절대적 (CI에서 차단)

비용

추론 비용 20~23% 증가

0 (이미 CI에 포함)

효과

-0.5% ~ +4% (연구 수치)

위반 시 100% 감지

유지보수

코드 변경 시 문서도 갱신 필요

코드 = 규칙 (자동 동기화)

AI 학습

자연어 해석 필요

에러 메시지로 즉시 학습

2.2 우리가 선택한 것

우리가 도입한 결정론적 제어는 크게 네 가지입니다. (각각의 상세 내용은 이후 섹션에서 다룹니다.)

  1. 커스텀 아키텍처 테스트 — 프로젝트 고유 규칙을 Go AST로 강제 (6장에서 상세 설명)

  2. golangci-lint — Go 표준 규칙을 pre-commit hook에서 즉시 피드백

  3. 타입 시스템 활용 — interface + private impl 패턴으로 잘못된 의존을 컴파일 에러로 전환

  4. 3단계 enforcement pipeline — pre-commit → pre-push → CI로 가장 빠른 단계에서 잡음

"규칙을 문서로 적지 마라. 코드로 강제하라."

이것이 이 글의 핵심 주장입니다. 이제부터는 이 "강제"를 어떻게 만들었는지를 설명하겠습니다.

3. Before: 기존 구조의 문제점

결정론적 제어를 도입하려면 먼저 구조가 정리되어야 합니다. 규칙을 정의할 수 없는 구조에서는 규칙을 강제할 수도 없기 때문입니다.

리팩토링 전 우리 서비스의 구조는 다음과 같았습니다.

store/svc에서 모든 모듈로 빨간 화살표가 뻗어나가는 것이 보입니다. hook/svc는 역방향으로 store/svccommand/svc를 의존하며 순환 의존성 위험까지 안고 있었습니다.

God Object 문제

God Object란? 너무 많은 책임을 가진 객체(또는 패키지)를 말합니다. 하나의 패키지가 거의 모든 모듈을 의존하면, 그 패키지를 수정할 때 전체 시스템에 영향이 퍼집니다.

store/svc는 app, command, widget, exposure, role 등 거의 모든 모듈을 의존하는 전형적인 God Object였습니다. 200개 이상의 파일이 internal import를 통해 서로 얽혀 있었고, 하나의 변경이 어디까지 영향을 미칠지 추적이 불가능했습니다.

AI에게 이 구조가 주는 문제

이 구조가 AI에게 얼마나 불리한지 구체적으로 살펴보면:

  • "이 파일 어디 있어?" — AI가 핸들러를 찾으려면 api/http/를 뒤지고, 서비스를 찾으려면 internal/을 뒤져야 합니다. 탐색 자체에 토큰을 낭비합니다

  • "이거 수정해도 돼?" — God Object가 모든 것에 의존하므로, 하나를 바꾸면 200개 이상의 파일에 영향이 갈 수 있습니다. AI가 영향 범위를 추적할 수 없습니다

  • "어떤 패턴으로 짜야 해?" — 모듈마다 패턴이 달랐습니다. 어떤 곳은 AppQuerySvc, 어떤 곳은 QueryService, 또 어떤 곳은 query. AI가 일관된 코드를 생성하기 어렵습니다

이 상태에서 CLAUDE.md에 규칙을 적어봤자 소용없습니다. God Object가 모든 것을 import할 수 있는 구조에서 "import하지 마세요"라고 적는 건, Go 컴파일러가 허용하는 것을 자연어로 금지하겠다는 뜻입니다.

구조 자체를 바꿔서, 잘못된 의존이 자동으로 감지되게 해야 합니다.

4. 설계: Go 컨벤션 v2 — AI가 예측할 수 있는 구조

먼저 두 번의 리팩토링을 거쳐 도달한 최종 설계를 보여드리겠습니다. "왜 이렇게 됐는지"(실행 과정)는 5장에서 다룹니다.

4.1 핵심 원칙: 대칭성

우리가 세운 가장 중요한 원칙은 대칭성(symmetry)입니다. "하나를 알면 전부를 안다"를 구조로 만드는 것입니다.

모든 계층이 동일한 패턴을 따릅니다. 예외 없이.

Go

repo, svc, handler, infra, saga — 전부 이 패턴입니다. AI가 하나의 예시만 보면 나머지 모든 코드를 예측할 수 있습니다. CLAUDE.md에 "이 패턴을 따르세요"라고 적을 필요 없이, 코드가 곧 규칙이 됩니다.

4.2 도메인 구조 — 구조가 곧 규칙

Plaintext
internal/domain/{name}/
├── subdomain/                   (서브도메인 그룹)
│   ├── core/  {model/, repo/, svc/}    ← 핵심 도메인. 다른 서브도메인에 의존하지 않음
│   ├── role/  {model/, repo/, svc/}    ← 선택적 서브도메인. core에 의존 가능
│   └── ...
├── svc/                         (App Service — 서브도메인 간 조율)
│   └── public.go                (Public Service — 외부에서 접근하는 유일한 창구)
├── handler/                     (driving adapter — HTTP/gRPC 핸들러)
│   ├── http/
│   └── jsonrpc/
├── infra/                       (driven adapter — 외부 서비스 호출)
│   └── {service}/client.go
└── alias.go                     (외부에서 접근할 수 있는 유일한 진입점)

alias.go란? Go의 type alias를 활용한 패턴입니다. 도메인 외부에서는 alias.go에 정의된 타입만 import할 수 있으므로, 도메인 내부 구현을 완벽하게 캡슐화합니다. 예를 들어 type Public = svc.Public으로 정의하면, 외부에서는 app.Public만 사용하고 app/subdomain/core/svc를 직접 import할 수 없습니다.

이 구조의 핵심은 구조 자체가 규칙을 강제한다는 점입니다.

  • alias.go를 통해서만 외부 접근이 가능합니다. Saga나 다른 도메인이 서브도메인 internal을 직접 import하는 것은 사실상 불가능합니다

  • 모든 도메인이 동일한 디렉토리 레이아웃을 가집니다. AI가 하나의 도메인 구조를 보면 다른 도메인도 동일한 구조라고 예측할 수 있습니다

  • 핸들러(handler/)가 도메인 안에 위치합니다. 도메인을 통째로 추출하면 핸들러도 함께 따라갑니다

4.3 의존성 매트릭스

Saga란? 여러 도메인에 걸친 작업을 조율하는 패턴입니다. 예를 들어 "앱 설치"라는 작업이 app, extension, hook 세 도메인에 걸쳐 있다면, Saga가 각 도메인의 Public Service를 순서대로 호출합니다. 하나가 실패하면 이전 단계를 롤백(compensation)합니다.

From ↓ \ To →

Same Sub

Other Sub

App Svc

Public

Other Domain

Saga

Subdomain

App Service

Public Service

(Public만)

Saga

(Public만)

이 표의 모든 아키텍처 테스트로 강제됩니다. AI가 위반하면 테스트가 실패하고, 에러 메시지로 정확히 어디가 잘못되었는지 알 수 있습니다. (6장에서 상세 설명)

4.4 네이밍: Package Stutter 제거

Go 표준 가이드에서 강조하는 원칙 중 하나가 Package Stutter 금지입니다. 패키지명이 이미 컨텍스트를 제공하므로 exported 이름에서 패키지명을 반복하지 않습니다.

Go

이 규칙은 단순한 미학의 문제가 아닙니다. AI가 repo 패키지의 인터페이스 이름을 예측할 때, stutter가 없으면 repo.App이라는 단 하나의 정답이 존재합니다. stutter가 있으면 AppRepo, AppRepository, AppRepoInterface 등 여러 가능성이 생기고, AI의 예측 정확도가 떨어집니다.

4.5 Saga 패턴: 도메인 간 조율

여러 도메인에 걸친 작업은 Saga가 담당합니다. Saga는 각 도메인의 Public Service만 의존하며, 서브도메인 internal에는 절대 접근하지 않습니다.

Go

이 설계가 독자적인 것은 아닙니다. handler와 client를 도메인 안으로 가져오는 것은 Go 커뮤니티에서 이미 검증된 패턴입니다(부록 C 참고). 우리는 여기에 Uber FX(Go용 DI 프레임워크)를 활용한 모듈 패턴과 아키텍처 테스트를 결합했습니다.

5. 실행: 두 번의 대규모 리팩토링

실행은 두 번의 대규모 리팩토링으로 나뉘었습니다. 1차에서 구조를 잡고, 2차에서 규칙을 강제하는 순서입니다.

5.1 1차 리팩토링: God Object 해체와 Saga 패턴 도입

1차의 핵심은 Saga 패턴을 처음부터 새로 만드는 것이었습니다. 리팩토링 전에는 Saga가 없었습니다. God Object인 store/svc가 모든 모듈 간 조율을 직접 담당하고 있었습니다.

Plaintext
Before:                              After:
store/svc (God Object)               saga/ (모듈 간 조율 전담)
├── app 의존                          ├── lifecycle/     (설치/삭제)
├── command 의존                      ├── discovery/     (조회 조합)
├── widget 의존                       └── registry/      (등록/해제)
├── exposure 의존
└── role 의존                        domain/ (각 모듈 독립)
   → 전부 하나의 패키지에서 조율            ├── app/     {svc/, alias.go}
                                     ├── extension/
                                     └── hook/

주요 변경:

변경

내용

Saga 신규 생성

Write Saga(트랜잭션 조율)와 Query Saga(조회 조합) 도입

Public Service 도입

각 도메인의 외부 인터페이스를 svc/public.go로 통일

alias.go 도입

도메인 외부에서 접근할 수 있는 유일한 진입점

domain/ 구조

internal/app/ → internal/domain/app/으로 이동

팀에서 10개의 피드백을 받았고, 서브도메인 간 호출 전략, Saga compensation(실패 시 롤백) 복구, Context timeout 처리 등의 구체적인 논의를 거쳤습니다.

5.2 2차 리팩토링: Convention v2 + 아키텍처 테스트

1차에서 구조를 잡았다면, 2차의 목적은 그 구조를 코드로 강제할 수 있게 만드는 것이었습니다.

먼저, 하나의 도메인에 v2 컨벤션을 시험 적용했습니다.

Phase

변경

예시

프로젝트 구조

lib/ → pkg/, shared/internal/pkg/

Go 표준 레이아웃 적용

네이밍

AppQuerySvc → Query, AppRepoApp

Package stutter 제거

도메인 구조

subdomain/ 래퍼 도입, handler/infra를 도메인 안으로 이동

자체 완결적 모듈

시험 적용으로 검증한 뒤, 7개 도메인과 9개 Saga 전체에 확장했습니다. 단순한 도메인부터 시작해서, 서브도메인이 10개인 가장 복잡한 도메인까지 점진적으로 적용했습니다. 핸들러 35개도 의존성을 분석해서 도메인 또는 saga의 handler/로 재배치했습니다. 최종 상태: 0 errors, 0 warnings.

핵심은 리팩토링 자체가 목적이 아니었다는 것입니다. 아키텍처 테스트를 걸 수 있는 구조를 만드는 것이 진짜 목적이었습니다. 구조가 정리되지 않으면 규칙을 자동화할 수 없기 때문입니다.

5.3 Claude Code가 사람 없이 리팩토링을 수행한 이유

흥미로운 점은, 2차 리팩토링의 상당 부분을 Claude Code가 사람의 개입 없이 자율적으로 수행했다는 것입니다. 다른 피처 개발과 병행하면서, 리팩토링 작업은 Claude Code에게 맡길 수 있었습니다.

이것이 가능했던 이유가 바로 아키텍처 테스트입니다.

  1. Claude Code가 파일을 이동하고 import를 수정합니다

  2. go build가 컴파일 에러를 잡고, go test ./test/architecture/...가 구조 위반을 잡습니다

  3. Claude Code가 에러 메시지를 보고 스스로 수정합니다

  4. 모든 테스트가 통과할 때까지 이 루프를 반복합니다

아키텍처 테스트가 없었다면 이런 자율 수행은 불가능했을 것입니다. "파일을 옮겨라"는 간단하지만, "옮긴 후 의존성 방향이 맞는지"를 판단하려면 규칙이 코드로 존재해야 합니다. CLAUDE.md에 규칙을 적어두는 것만으로는, AI가 중간에 길을 잃을 수밖에 없습니다.

좋은 아키텍처 테스트가 있으면, AI는 "맞게 했는지"를 스스로 검증할 수 있습니다. 그리고 검증할 수 있는 AI는, 사람 없이도 일할 수 있습니다.

6. 핵심: 커스텀 아키텍처 테스트

이 섹션이 이 글의 하이라이트입니다. CLAUDE.md 대신 우리가 만든 것을 소개합니다.

6.1 왜 커스텀으로 만들었나

go-arch-lint, archunit-go 등 기존 도구를 검토했지만, 우리 프로젝트의 세부 규칙을 표현하기 어려웠습니다. 특히 다음과 같은 규칙들은 기성 도구로 구현이 불가능했습니다.

  • subdomain/ 래퍼 안의 서브도메인 간 격리

  • Saga가 alias.go를 통해서만 도메인에 접근하는 규칙

  • interface + private impl + constructor 3종 세트 강제

  • 파일명과 인터페이스명의 일치 검증

Go의 go/ast 패키지를 사용하면 Go 소스 코드를 직접 파싱할 수 있으므로, 우리 컨벤션에 100% 맞는 규칙을 구현할 수 있었습니다. 무엇보다 별도 도구 설치 없이 go test로 실행되므로 CI에 자연스럽게 통합됩니다.

6.2 구조

Plaintext
test/architecture/
├── arch_test.go              (4개 테스트 스위트 + 서머리 + 벤치마크)
├── analyzer/
│   ├── parser.go             (Go 파일 AST 파싱)
│   └── domain.go             (도메인/서브도메인 식별)
├── report/
│   └── violation.go          (위반 보고서 생성)
└── ruleset/
    ├── config.go             (severity 설정 — 모든 도메인 ERROR)
    ├── dependency.go         (의존성 규칙 — ~31K lines)
    ├── naming.go             (네이밍 규칙 — ~34K lines)
    ├── interface.go          (인터페이스 패턴 — ~12K lines)
    └── structure.go          (구조 규칙 — ~28K lines)

총 ~105K lines의 커스텀 분석 코드입니다. 적지 않은 양이지만, CLAUDE.md에 자연어로 규칙을 나열하는 것보다 훨씬 확실한 투자입니다.

6.3 4가지 규칙 카테고리

Dependency Rules (의존성)

가장 중요한 카테고리입니다. 도메인 간, 서브도메인 간 의존성 방향을 강제합니다.

  • 서브도메인에서 다른 서브도메인 직접 import

  • Saga에서 서브도메인/App Service import (Public만)

  • 핸들러에서 다른 도메인의 서브도메인 직접 import (alias.go 경유)

  • 계층 역방향 의존 (model ← repo ← svc 방향만 허용)

AI가 이 규칙을 위반하면 어떤 일이 일어날까요?

Plaintext
=== FAIL: TestDependencyRules
    violation: subdomain "role" imports subdomain "httpfn" directly
    file: internal/domain/app/subdomain/role/svc/token.go:15
    rule: subdomains must not import other subdomains
    fix: use Public Service or move logic to core/

AI가 이 에러 메시지를 보면 어디서 무엇이 잘못되었고, 어떻게 고쳐야 하는지 즉시 알 수 있습니다. CLAUDE.md에 같은 규칙을 자연어로 적으면 AI가 무시할 수 있지만, 테스트 실패는 무시할 수 없습니다.

Naming Rules (네이밍)

  • Package stutter 검출: repo.AppRepo repo.App

  • Impl 접미사 금지: appRepoImpl app (unexported)

  • 파일명-인터페이스 일치: install.goInstall interface

  • 파일명 레이어 접미사 금지: install_svc.go install.go

Interface Pattern Rules (인터페이스 패턴)

  • 모든 계층에 exported interface + unexported impl + constructor 강제

  • constructor는 interface를 반환해야 함

  • impl struct는 interface와 같은 파일에 위치해야 함

Structural Rules (구조)

  • 금지 패키지명: util, common, misc, helper, shared, lib

  • Import alias 규칙 강제

6.4 점진적 enforcement

처음부터 전체를 ERROR로 설정하면 기존 코드가 모두 실패합니다. 그래서 점진적으로 전환했습니다.

  1. 모든 도메인/saga를 WARNING으로 시작

  2. 도메인별로 위반 사항 수정

  3. 수정 완료된 도메인은 ERROR로 전환

  4. 최종 상태: 모든 도메인/saga가 ERROR

Go

7개 도메인 + 9개 saga가 모두 enforced 상태. 아키텍처 위반 시 테스트가 실패합니다.

6.5 CLAUDE.md vs 아키텍처 테스트: 구체적 비교

규칙

CLAUDE.md

아키텍처 테스트

"서브도메인 간 import 금지"

AI가 무시할 수 있음

CheckSubdomainIsolation() — 100% 감지

"Saga는 Public만 사용"

복잡한 설명 필요

CheckSagaDependency() — 컴파일 수준 강제

"interface + impl 패턴"

예시를 적어도 변형 생성

CheckInterfacePattern() — 정확한 패턴 매칭

"Package stutter 금지"

"repo.AppRepo 대신 repo.App"

CheckPackageStutter() — 자동 검출

"금지 패키지명"

리스트를 적어도 새 이름 만듬

CheckForbiddenPackageNames() — 즉시 차단

CLAUDE.md는 부탁입니다. 아키텍처 테스트는 강제입니다.

7. 결과: Before vs After

구조 비교

수치

항목

Before

After

모듈 결합도

높음 (200+ 파일 cross-import)

90% 감소 (Public만 의존)

변경 영향 범위

7개 패키지

1개 모듈

테스트 mock 수

과도함

70% 감소 (Public만 mock)

아키텍처 위반 감지

불가능

100% 자동 감지

Enforced 도메인

0

7/7 domain + 9/9 saga

AI에게 달라진 것

구체적인 시나리오로 Before/After를 비교해보겠습니다.

시나리오 1: "새 서브도메인 추가"

  • Before: AI가 어디에 만들지 모릅니다. CLAUDE.md를 읽어도 기존 코드와 패턴이 달라서 혼란. 결과 불확실.

  • After: AI가 기존 서브도메인(예: role/)의 구조를 보고 동일하게 생성. 아키텍처 테스트가 패턴 위반을 즉시 감지. 결과 확실.

시나리오 2: "Saga에서 도메인 internal 직접 import"

  • Before: CLAUDE.md에 "하지 마세요" → AI가 무시 → 코드 리뷰에서 발견 → 수동 수정

  • After: AI가 import → go test 실패 → 에러 메시지 "Saga must use Public Service via alias.go" → AI가 자동 수정

시나리오 3: "핸들러에서 다른 도메인 서브도메인 직접 import"

  • Before: 감지 불가. 프로덕션까지 갈 수 있음.

  • After: pre-push hook에서 차단. push 자체가 안 됨.

8. 교훈과 다음 단계

핵심 교훈

"규칙을 문서로 적지 마라. 코드로 강제하라."

우선순위

수단

효과

1

아키텍처 테스트 + 타입 체크 + 린터

위반 시 100% 감지, 추가 비용 0

2

예측 가능한 코드 구조 (DDD)

AI가 패턴을 보고 학습, 문서 불필요

3 (최후수단)

CLAUDE.md / 컨텍스트 파일

효과 미미 (-0.5%~+4%), 비용 20~23% 증가

DDD는 사람만을 위한 것이 아닙니다. DDD의 이점 — 명확한 경계, 일관된 패턴, 모듈의 자체 완결성 — 은 AI에게도 그대로 적용됩니다. 특히 "예측 가능한 구조"는 AI의 코드 생성 정확도에 직접적인 영향을 줍니다.

구조가 정리되어야 규칙을 자동화할 수 있습니다. God Object가 있는 상태에서는 아키텍처 테스트를 걸 수 없습니다. 먼저 리팩토링 → 그 다음 테스트로 강제 → 이후 AI가 안전하게 코드를 생성하는 순서가 중요합니다.

점진적 적용이 핵심입니다. 하나의 도메인에 시험 적용 → 팀 피드백 → 전체 확장. WARNING → ERROR 점진적 전환. 한 번에 모든 것을 바꾸려 하면 실패합니다.

좋은 테스트가 있으면, AI는 사람 없이도 일할 수 있습니다. 5.3에서 다뤘듯이, 아키텍처 테스트가 검증을 대신해주면 AI의 자율 작업이 가능해집니다.

다음 단계

이번 작업으로 "AI가 안정적으로 코드를 짤 수 있는 환경"의 기반을 만들었습니다. 다음으로는 아래의 태스크를 추가로 진행중에 있습니다.

  • 다른 서비스로 확장: 검증된 패턴을 팀 내 다른 Go 서비스에 적용

  • 지식 그래프 통합: 도메인 구조가 잘 정리되어 있으면 서비스 간 영향도를 자동으로 파악하는 지식 시스템 구축이 쉬워짐

  • 멀티 에이전트 시스템 연동: 도메인 단위 자체 완결적 모듈 → 에이전트 단위 작업 위임의 기반

마무리

AI 코딩 도구의 성능은 빠르게 올라가고 있지만, 그 성능을 안정적으로 발휘하게 만드는 건 결국 코드베이스의 구조결정론적 검증 시스템입니다. CLAUDE.md에 규칙을 적는 것보다, 잘못된 코드가 애초에 통과하지 못하는 환경을 만드는 것이 더 확실합니다.

여러분의 프로젝트에서도 "AI가 자꾸 규칙을 어긴다"는 문제를 겪고 있다면, 프롬프트를 고치기 전에 구조와 시스템을 먼저 점검해보는 것은 어떨까요?


채널톡에서는 AI가 안정적으로 코드를 생성할 수 있는 환경 구축, 대규모 Go 백엔드 리팩토링, 커스텀 정적 분석 도구 개발 같은 도전적인 문제를 함께 풀어갈 엔지니어를 찾고 있습니다. 이러한 문제 해결 과정에 함께하며 성장하고 싶으신 분들은 언제든지 채널톡의 문을 두드려 주세요!

부록

A. 타임라인

단계

내용

1차: 구조 수립

God Object 해체, Saga 패턴 신규 도입, Public Service + alias.go 패턴, domain/ 구조 생성, 네이밍 v1

아키텍처 테스트 도입

커스텀 Go AST 기반 분석기 개발 (~105K lines)

2차: 규칙 강제

lib/→pkg/, subdomain/ 래퍼, 핸들러 도메인 내 이동, 네이밍 v2, 7개 도메인 + 9개 saga 전체 enforcement, pre-push hook

B. 아키텍처 테스트 규칙 전체 목록

카테고리

코드량

주요 검증 항목

Dependency

~31K lines

서브도메인 격리, 계층 방향, saga import, handler import

Naming

~34K lines

Package stutter, impl 네이밍, 파일 네이밍, handler 프로토콜 embedding

Interface Pattern

~12K lines

interface + private impl 패턴, constructor 반환 타입, impl 같은 파일 위치

Structure

~28K lines

Import alias 네이밍, 금지 패키지명 (util, common, misc 등)

  • Go go/ast 기반 커스텀 분석기 (~105K lines)\

  • 위반 시 CI 실패

C. Go 커뮤니티 레퍼런스

레퍼런스

Stars

핵심 패턴

ThreeDotsLabs/wild-workouts

5.3k

Hexagonal — handler(ports/)와 client(adapters/)가 bounded context 안에 위치

go-kit/kit

26k

transport/ 서브패키지가 도메인 안에 위치, endpoint 패턴

benbjohnson/wtf

3k

root 패키지에 도메인 타입, http/에 서버+클라이언트

mehdihadeli/go-food-delivery

800

Vertical Slice + FX — 기능별 handler+command+event 한 폴더

D. 참고 자료

We Make a Future Classic Product