[TIL] 아키텍처 특성은 어떻게 측정하고 지킬까 (6장)

2026. 08. 28.Yeji Kim
Architecture

4장에서는 아키텍처 특성이 무엇인지, 5장에서는 우리 시스템에서 중요한 아키텍처 특성을 어떻게 찾아내는지를 정리했다.

그런데 여기서 한 가지 문제가 생긴다.

text
우리 서비스는 성능이 중요합니다.
 
확장성이 좋아야 합니다.
 
유지보수하기 쉬워야 합니다.

말은 좋은데, 이걸로는 실제 시스템이 잘 만들어졌는지 판단하기 어렵다.

"성능이 좋다"는 정확히 얼마나 빨라야 한다는 걸까?

"유지보수하기 쉽다"는 어떻게 확인하지?

그리고 더 중요한 문제.

처음에는 잘 지켜졌던 아키텍처가 개발을 계속하면서 망가지고 있다면 어떻게 알아낼까?

6장은 결국 이 두 가지 질문을 다룬다.

아키텍처 특성을 어떻게 측정할 것인가?

그리고 그 특성이 시간이 지나도 유지되게 어떻게 관리할 것인가?


"성능이 좋아야 한다"는 측정할 수 없다

예를 들어 요구사항이 이렇다고 해보자.

text
우리 API는 빨라야 한다.

이건 사실 거의 아무 의미가 없다.

누군가에게 빠르다는 건 1초일 수도 있고, 다른 사람에게는 100ms일 수도 있다.

조금 더 구체적으로 만들어보자.

text
API 평균 응답 시간이 300ms 이하여야 한다.

훨씬 좋아졌다.

하지만 이것도 아직 문제가 있다.

평균이 300ms라고 해도

text
99개의 요청 → 100ms
 
1개의 요청 → 20초

라면 정말 성능이 좋은 시스템일까?

그래서 실제로는

text
전체 요청의 95%가 300ms 이내에 응답해야 한다.

처럼 정의할 수 있다.

이제야 비로소

text
측정할 수 있고
테스트할 수 있고
기준을 넘었는지 판단할 수 있다.

즉 아키텍처 특성도 가능하면 객관적인 숫자나 규칙으로 바꿔야 한다.


아키텍처 특성을 정의하기 어려운 이유

책에서는 아키텍처 특성을 측정하기 어려운 이유를 몇 가지 이야기한다.

첫 번째는 아키텍처 특성이 물리 법칙처럼 정확한 개념이 아니기 때문이다.

예를 들어

text
Agility
Maintainability
Deployability
Performance

같은 단어는 굉장히 많이 사용하지만, 사람마다 생각하는 의미가 조금씩 다르다.

특히

text
"성능이 중요합니다."

라고 했을 때 개발자와 인프라 엔지니어가 생각하는 성능이 다를 수도 있다.

text
개발자
→ API 응답 속도
 
DB 엔지니어
→ Query 처리 시간
 
인프라 엔지니어
→ Throughput / CPU / Memory
 
사용자
→ 화면이 얼마나 빨리 보이는가

같은 Performance라는 단어를 사용하면서 완전히 다른 이야기를 하고 있는 셈이다.


어떤 특성은 너무 큰 개념이다

또 하나의 문제는 하나의 아키텍처 특성이 사실 여러 특성의 묶음인 경우다.

대표적으로

text
Agility

를 생각해보자.

"변화에 빠르게 대응할 수 있는 시스템"이라고 말할 수 있지만, 실제로는

text
Modularity
Deployability
Testability
Maintainability

같은 여러 특성이 합쳐져야 만들어지는 성질이다.

즉

text
Agility를 높인다.

라고 말하면 너무 추상적이다.

차라리

text
각 서비스는 독립적으로 배포할 수 있어야 한다.
 
핵심 모듈의 테스트 커버리지는 일정 기준 이상이어야 한다.
 
모듈 간 순환 의존성이 없어야 한다.

처럼 더 작은 단위로 쪼개야 실제로 관리할 수 있다.

측정할 수 없는 특성은 관리하기도 어렵다.


1. Operational Measures

가장 직관적으로 측정하기 쉬운 건 운영 관련 특성이다.

예를 들어

text
Performance
Scalability
Availability
Reliability

같은 것들.

이런 특성은 실제 시스템에서 숫자로 표현하기 비교적 쉽다.

Performance

text
p95 응답 시간 < 300ms
 
p99 응답 시간 < 800ms

Availability

text
월간 가용성 ≥ 99.99%

Scalability

text
동시 사용자 100,000명 상황에서도
p95 응답 시간이 500ms를 넘지 않는다.

Reliability

text
메시지 처리 실패율 < 0.01%

이렇게 만들면

text
"우리 시스템 성능 괜찮은 것 같은데?"

같은 감각적인 판단이 아니라 실제 데이터로 이야기할 수 있다.


측정할 때는 평균만 보면 위험하다

성능을 측정할 때 특히 평균값만 보는 것은 문제가 될 수 있다.

예를 들어 API 응답 시간이

text
평균 200ms

라고 하자.

겉으로는 좋아 보인다.

하지만 실제 분포가

text
50% → 50ms
 
40% → 100ms
 
9% → 500ms
 
1% → 15초

라면 일부 사용자는 매우 나쁜 경험을 하게 된다.

그래서 성능에서는 평균보다

text
p50
p95
p99

같은 percentile을 자주 사용한다.

text
p95 = 전체 요청 중 95%가 이 시간 안에 처리됨

아키텍처 특성을 측정할 때 중요한 건 결국

그 특성을 가장 잘 표현하는 측정 방법을 선택하는 것

이다.


2. Structural Measures

그런데 모든 아키텍처 특성을 서버 메트릭처럼 측정할 수 있는 것은 아니다.

예를 들어

text
Maintainability
Modularity
Code Quality

는 어떻게 측정할까?

여기서는 코드의 구조 자체를 측정해야 한다.

대표적인 예가 Cyclomatic Complexity다.

함수 안에 분기문이 많아질수록 가능한 실행 경로가 많아진다.

js
function getPrice(user) {
  if (user.isPremium) {
    if (user.hasCoupon) {
      // ...
    }
  }
 
  if (user.country === "KR") {
    // ...
  }
}

조건이 많아질수록 코드가 이해하기 어려워지고, 테스트해야 할 경로도 늘어난다.

그래서 이런 식의 기준을 정할 수 있다.

text
Cyclomatic Complexity <= 10

기준을 넘어가는 코드가 생기면 리팩터링 대상으로 보는 것이다.


의존성도 측정할 수 있다

구조적으로 중요한 또 다른 요소는 모듈 간 의존성이다.

예를 들어 우리가 계층형 아키텍처를 사용한다고 해보자.

text
Presentation
    ↓
Business
    ↓
Persistence

우리가 원하는 규칙은

text
Presentation → Business 가능
 
Business → Persistence 가능
 
Persistence → Presentation 금지

일 수 있다.

그런데 개발을 하다 보면 누군가 편의를 위해 이런 코드를 만들 수 있다.

text
Persistence
    ↓
Presentation

코드는 정상적으로 컴파일되고 서비스도 잘 돌아갈 수 있다.

하지만 아키텍처 관점에서는 구조가 망가진 것이다.

이런 것도 테스트할 수 있다.

text
Persistence 패키지는
Presentation 패키지를 import할 수 없다.

즉 구조적 아키텍처 특성도 검증 가능한 규칙으로 바꿀 수 있다.


3. Process Measures

세 번째는 개발 과정에 관련된 특성이다.

대표적으로

text
Deployability
Testability
Agility

같은 것들.

예를 들어 Deployability가 중요하다고 해보자.

그냥

text
우리 시스템은 배포하기 쉬워야 한다.

라고 말하기보다

text
배포 빈도
 
배포에 걸리는 시간
 
배포 실패율
 
Rollback에 걸리는 시간

을 측정할 수 있다.

예를 들어

text
평균 배포 시간 < 10분
 
배포 실패율 < 2%
 
Rollback 가능 시간 < 5분

처럼 정의하는 것이다.

이렇게 하면 애매했던 Deployability라는 특성이 실제 팀이 관리할 수 있는 지표가 된다.


그런데 측정만 하면 끝일까?

여기서 다음 문제가 생긴다.

처음 시스템을 만들었을 때

text
p95 < 300ms

를 만족했다고 해보자.

6개월 뒤 새로운 기능이 계속 추가된다.

text
300ms
↓
350ms
↓
420ms
↓
600ms

아무도 눈치채지 못한다면?

아키텍처 특성은 처음 설계할 때 한 번 확인한다고 끝나는 것이 아니다.

시스템은 계속 변하기 때문이다.

그래서 필요한 것이 Architecture Governance다.


Architecture Governance

거버넌스라고 하면 약간 거창하게 들리지만, 핵심은 단순하다.

우리가 중요하다고 정한 아키텍처 특성이 실제 개발 과정에서도 계속 지켜지고 있는지 관리하는 것.

예를 들어 팀에서 이런 규칙을 만들었다고 하자.

text
Presentation Layer는
Persistence Layer를 직접 호출하면 안 된다.

문서에만 적어두면 어떻게 될까?

text
architecture.md
 
Presentation → Business → Persistence

몇 달 뒤 급한 기능을 만드는 개발자가

ts
import { userRepository } from "../persistence";

를 넣는다.

서비스는 정상적으로 돌아간다.

테스트도 통과한다.

그리고 조금씩

text
Presentation ───────→ Persistence
       ↓
    Business
       ↓
Persistence

구조가 망가지기 시작한다.

이런 현상을 흔히 Architecture Drift, 즉 처음 의도한 아키텍처에서 조금씩 벗어나는 현상으로 볼 수 있다.


문서보다 코드로 지키는 게 강하다

이 문제를 해결하는 가장 좋은 방법 중 하나는

text
"이렇게 개발해주세요."

라고 문서에 쓰는 것보다

text
"이 규칙을 어기면 CI가 실패한다."

로 만드는 것이다.

예를 들어

text
Presentation → Persistence 직접 참조 금지

를 자동화된 테스트로 만들 수 있다.

text
Architecture Test
 
Persistence에 대한 직접 의존성이 존재하는가?
 
YES → CI FAIL
NO  → PASS

이제 규칙이 사람의 기억에 의존하지 않는다.

이런 개념이 Fitness Function으로 연결된다.


Fitness Function

책에서 굉장히 중요한 개념 중 하나다.

Fitness Function은 간단히 말하면

아키텍처 특성이 우리가 원하는 상태를 유지하고 있는지 객관적으로 평가하는 장치

다.

예를 들어 Performance가 중요한 시스템이라면

text
p95 response time < 300ms

라는 Fitness Function을 만들 수 있다.

CI에서 성능 테스트를 실행하고

text
p95 = 270ms → PASS
 
p95 = 430ms → FAIL

하도록 만드는 것이다.


코드 구조도 Fitness Function이 될 수 있다

예를 들어 우리가 이런 규칙을 중요하게 생각한다고 해보자.

text
도메인 모듈끼리 순환 의존성이 없어야 한다.

그러면

text
A → B
B → C
C → A

같은 구조가 생기면 테스트를 실패시키면 된다.

text
Circular Dependency 발견
 
→ Fitness Function FAIL

또는

text
특정 모듈의 Cyclomatic Complexity는
20을 넘으면 안 된다.

같은 규칙도 가능하다.

즉 Fitness Function은 반드시 성능 테스트일 필요가 없다.

text
Performance Test
 
Dependency Test
 
Security Test
 
Code Metric
 
Deployment Metric
 
Infrastructure Policy

등 아키텍처 특성을 객관적으로 평가할 수 있는 모든 메커니즘이 될 수 있다.


중요한 건 "지속적으로" 확인하는 것

Fitness Function의 중요한 점은 한 번 측정하는 게 아니다.

개발 과정 안에 들어가야 한다.

text
Developer
    ↓
Commit
    ↓
CI
    ↓
Unit Test
Architecture Test
Performance Test
Security Test
    ↓
Deploy

예를 들어 PR 하나가 들어올 때마다

text
순환 의존성이 생기지 않았는가?
 
레이어 규칙을 위반하지 않았는가?
 
성능 기준을 넘지 않았는가?

를 자동으로 확인한다.

그러면 아키텍처는 더 이상

text
처음에 설계하고 끝나는 것

이 아니라

text
계속 검증되는 시스템의 속성

이 된다.


모든 것을 자동화할 수 있는 건 아니다

그렇다고 모든 아키텍처 특성을 숫자로 만들 수 있는 것은 아니다.

예를 들어

text
코드가 이해하기 쉬운가?
 
API 설계가 직관적인가?
 
도메인 경계가 적절한가?

같은 것은 완전히 자동화하기 어렵다.

이런 부분은

text
Code Review
 
Architecture Review
 
ADR
 
Design Review

등 사람의 판단이 여전히 필요하다.

그래서 거버넌스는

text
Automation
+
Human Review

를 적절하게 섞는 과정이라고 볼 수 있다.

가능한 것은 자동화하고, 판단이 필요한 것은 사람이 검토한다.


중요한 건 규칙을 만드는 것보다 피드백 루프를 만드는 것

처음에는 아키텍처 거버넌스라고 하면

text
아키텍트가 규칙을 만든다.
 
개발자는 그 규칙을 따른다.

같은 느낌을 떠올릴 수도 있다.

그런데 이 장을 읽다 보면 핵심은 통제라기보다 피드백 루프에 가까운 것 같다.

text
Architecture Decision
        ↓
측정 가능한 기준 정의
        ↓
시스템 변경
        ↓
Fitness Function
        ↓
기준을 만족하는가?
        ↓
Architecture Decision 재검토

시스템은 계속 변하고, 비즈니스도 계속 변한다.

그러니 아키텍처 특성 역시

text
한 번 결정하고 끝

이 아니라

text
측정
→ 확인
→ 변화
→ 다시 측정

하는 과정이 필요하다.


프론트엔드에서도 똑같이 생각할 수 있다

이 개념은 백엔드나 인프라에만 적용되는 이야기가 아니다.

예를 들어 큰 프론트엔드 프로젝트에서

text
Performance
Maintainability
Modularity
Accessibility

를 중요하게 본다고 해보자.

그러면 이런 Fitness Function을 만들 수 있다.

Performance

text
LCP < 2.5s
 
JS Bundle < 300KB
 
특정 Interaction < 200ms

Modularity

text
feature 모듈끼리 직접 참조 금지
 
순환 의존성 금지

Maintainability

text
Cyclomatic Complexity < N
 
특정 모듈 크기 제한

Accessibility

text
axe 검사에서 critical violation = 0

그리고 CI에서 계속 검사한다.

text
PR
 ↓
Lint
 ↓
Unit Test
 ↓
Architecture Test
 ↓
Bundle Size Check
 ↓
Accessibility Test

이렇게 생각하면 Fitness Function이라는 개념이 훨씬 익숙해진다.

사실 우리가 이미 사용하고 있는

text
ESLint
 
TypeScript
 
Bundle Size Check
 
Lighthouse CI
 
Dependency Cruiser
 
Architecture Test

같은 도구들도 관점에 따라서는 아키텍처 거버넌스를 구현하는 장치라고 볼 수 있다.


결국 측정할 수 있어야 지킬 수 있다

4장부터 흐름을 연결하면 꽤 깔끔하다.

text
4장
 
Architecture Characteristics Defined
 
"아키텍처 특성이 무엇인가?"
        ↓
 
5장
 
Identifying Architecture Characteristics
 
"우리 시스템에서 무엇이 중요한가?"
        ↓
 
6장
 
Measuring & Governing Architecture Characteristics
 
"그걸 어떻게 측정하고 계속 지킬 것인가?"

결국

text
Performance가 중요하다.

에서 끝나는 게 아니라

text
Performance가 중요하다.
 
↓
 
p95 응답 시간이 300ms 이하여야 한다.
 
↓
 
지속적으로 측정한다.
 
↓
 
기준을 넘으면 알림 또는 CI 실패.
 
↓
 
필요하면 구조를 다시 검토한다.

까지 가야 실제 아키텍처 요구사항이 된다.


결국 6장은 "아키텍처를 실행 가능한 규칙으로 만드는 법" 같다

개인적으로 6장에서 가장 인상적인 부분은 아키텍처를 문서에만 존재하는 설계에서 실제로 검증 가능한 것으로 바꾸는 것이었다.

text
"우리 시스템은 모듈성이 좋아야 합니다."
 
"성능이 중요합니다."
 
"레이어를 잘 지켜주세요."

라고 말하는 건 쉽다.

하지만 실제 개발에서는 이것을

text
무엇을 측정할 것인가?
 
어떤 값을 정상이라고 볼 것인가?
 
언제 측정할 것인가?
 
기준을 어기면 어떻게 알 것인가?
 
자동화할 수 있는가?

까지 내려가야 한다.

결국

아키텍처 특성은 선언하는 것보다 측정하고 지속적으로 검증할 때 실제 의미가 생긴다.

4장에서 무엇을 중요하게 볼지 정의하고, 5장에서 우리 시스템에서 중요한 것을 골랐다면, 6장은 그것을 코드와 개발 프로세스 안에서 실제로 살아 있게 만드는 방법에 대한 이야기인 듯.


레퍼런스

  • Mark Richards, Neal Ford, Fundamentals of Software Architecture — Chapter 6, Measuring and Governing Architecture Characteristics