본문으로 건너뛰기

부록: 조직 경계와 콘웨이의 법칙 - 디자인 패턴, 시스템 수준 편 ep.14

앞의 열세 개 선은 저절로 그어지지 않았다. 누군가 그었고, 대개 그 누군가는 조직도였다.


이 경계는 무엇을 청구하는가

부록입니다. 앞선 열세 편이 시스템 안의 경계를 다뤘다면, 이 편은 그 경계를 결정한 경계를 다룹니다.

Melvin Conway가 쓴 문장이 있습니다.

시스템을 설계하는 조직은, 그 조직의 소통 구조를 그대로 닮은 설계를 만들어 낸다.

이 문장은 격언이 아니라 관찰입니다. 그리고 지금까지도 유효합니다.

  • 프론트엔드팀과 백엔드팀이 나뉘어 있으면, API가 그 사이에 생깁니다.
  • 팀이 셋이면 서비스가 셋이 되는 경향이 있습니다.
  • 다른 층에 앉은 팀끼리는 경계가 두꺼워집니다.

경계를 넘는 일은 소통 비용이 들고, 소통 비용이 낮은 쪽으로 설계가 흐르기 때문입니다. 옆자리 동료의 코드는 고치기 쉽지만, 다른 팀의 코드는 회의부터 잡아야 합니다. 그러면 자연히 자기 쪽에서 해결하는 코드가 쌓입니다.

이 경계가 청구하는 것은 조율 비용입니다. 그리고 이 비용은 다른 모든 경계의 비용에 곱해집니다.

조직 경계가 답하는 질문은 하나입니다. 앞의 열세 경계는 누가 결정했는가.


이 경계에 붙어 있는 이름들

역콘웨이 기동

앞에서 인용한 “시스템을 설계하는 조직은, 그 조직의 소통 구조를 그대로 닮은 설계를 만들어 낸다”는 문장이 콘웨이의 법칙(Conway’s Law) 입니다. Melvin Conway가 논문 「How Do Committees Invent?」에서 내놓은 관찰입니다.

요지는 하나입니다. 소통 구조가 설계 구조를 결정합니다. 팀을 어떻게 나누고 누가 누구와 자주 이야기하는지가 모듈과 서비스의 경계로 그대로 옮겨집니다. 위에서 본 것처럼 팀 사이에 API가 생기고 팀 수만큼 서비스가 생기는 현상이 그 결과입니다.

  • 알고 나면 두 가지 태도가 가능합니다.

첫째, 받아들입니다. 조직 구조를 바꿀 수 없다면, 시스템 경계를 조직 경계에 맞춥니다. 억지로 어긋내면 매 작업마다 조율 회의가 생깁니다.

둘째, 뒤집습니다. 원하는 아키텍처가 있다면, 그 아키텍처에 맞게 조직을 재구성합니다. 이것을 역콘웨이 기동(Inverse Conway Maneuver) 이라고 부릅니다.

두 번째가 강력하지만 비쌉니다. 조직을 바꾸는 것은 코드를 바꾸는 것보다 훨씬 어렵습니다. 그래서 실무에서는 대개 절충합니다. 큰 경계는 조직에 맞추고, 그 안에서는 자유롭게 설계합니다.

여기서 실무적인 진단법을 하나 세울 수 있습니다.

자주 함께 바뀌는 것들이 다른 팀에 있다면, 경계가 잘못 그어진 것입니다.

ep.01 모듈 경계에서 판단한 기준과 정확히 같습니다. 층위만 다릅니다.

  • 하나의 기능을 내려면 세 팀이 각각 배포해야 한다 → 경계가 기능을 가로지릅니다.
  • 매 스프린트마다 같은 두 팀이 조율 회의를 한다 → 그 둘은 사실 한 팀이어야 할 수도 있습니다.

- 보여주는 대응 다이어그램. 상단에는 조직도가 그려져 있습니다. 좌측부터 프론트엔드팀, 백엔드팀, 데이터팀 세 개의 팀 박스가 배치되고 팀 사이에는 소통 경로가 실선으로 연결되어 있습니다. 하단에는 그 조직이 만들어 낸 시스템 아키텍처가 그려져 있습니다. 웹 애플리케이션, API 서버, 데이터 파이프라인 세 개의 시스템 박스가 같은 배치로 놓이고, 그 사이에는 API 계약이 경계로 그어져 있습니다. 상단 조직도와 하단 아키텍처 사이에는 점선 대응 화살표가 각 팀과 각 시스템을 짝지어 연결합니다. 다이어그램 우측에는 이 대응이 왜 생기는지가 설명되어 있습니다. 경계를 넘는 일은 소통 비용이 들고, 소통 비용이 낮은 쪽으로 설계가 흐른다는 내용입니다. 하단에는 진단 질문이 배치되어 있습니다. 자주 함께 바뀌는 것들이 다른 팀에 있다면 경계가 잘못 그어진 것이라는 문장입니다.

팀 토폴로지의 네 유형

Matthew Skelton과 Manuel Pais가 정리한 틀입니다. 팀을 네 종류로 나누고, 각 유형이 다른 유형과 어떻게 상호작용하는지를 정의합니다.

유형하는 일예
스트림 정렬(Stream-aligned)하나의 가치 흐름을 끝까지 담당결제팀, 검색팀
플랫폼(Platform)다른 팀이 쓸 내부 서비스 제공인프라팀, 배포 플랫폼팀
활성화(Enabling)다른 팀의 역량을 키움아키텍처 길드, DevRel
난제(Complicated Subsystem)전문 지식이 필요한 부분 담당검색 랭킹, 결제 정산

대부분의 팀은 스트림 정렬 팀이어야 합니다. 나머지 셋은 스트림 정렬 팀이 빠르게 일할 수 있도록 돕는 역할입니다.

상호작용 방식도 세 가지로 정리됩니다.

  • 협업(Collaboration): 함께 일합니다. 학습이 빠르지만 비쌉니다. 한시적으로만 씁니다.
  • 서비스 제공(X-as-a-Service): 쓰기만 합니다. 조율이 최소입니다.
  • 촉진(Facilitating): 가르치고 빠집니다. 한시적입니다.

플랫폼팀이 협업 모드에 계속 머물면 병목이 됩니다. 모든 팀이 인프라팀과 회의를 잡아야 하는 상태가 병목입니다. 플랫폼팀의 목표는 자기 없이도 다른 팀이 일할 수 있게 만드는 것입니다.

디자인 시스템 거버넌스 세 모델

디자인 시스템은 조직 경계 문제가 가장 선명하게 드러나는 자리입니다. 누가 만들고, 누가 결정하고, 누가 유지하는가.

중앙집중형(Centralized): 전담 팀이 만들고 관리합니다. 일관성이 높고 품질이 균일합니다. 대신 요청이 몰리면 병목이 되고, 프로덕트팀은 기다립니다.

연합형(Federated): 각 프로덕트팀의 대표가 모여 함께 결정합니다. 현장 요구가 잘 반영됩니다. 대신 의사결정이 느리고, 아무도 전담하지 않아 유지보수가 밀립니다.

커뮤니티형(Community-driven): 누구나 기여합니다. 속도가 빠르고 참여도가 높습니다. 대신 방향이 흩어지고 품질 편차가 큽니다.

모델일관성속도병목적합한 규모
중앙집중높음낮음시스템팀소~중
연합중간중간회의중~대
커뮤니티낮음높음리뷰대

실무에서는 혼합형이 많습니다. 코어(토큰, 기본 컴포넌트)는 중앙집중, 확장(패턴, 조합 컴포넌트)은 커뮤니티, 방향 결정은 연합입니다. 이 조합은 ep.01 모듈 경계에서 디자인 시스템을 토큰, 시스템 컴포넌트, 프로덕트 컴포넌트 세 층으로 나눈 구분과 대응합니다.

  • 토큰 → 중앙집중. 바뀌면 모두에게 영향이 갑니다.
  • 시스템 컴포넌트 → 중앙집중 + 기여 허용.
  • 프로덕트 컴포넌트 → 각 팀 소유.

거버넌스 모델은 하나로 정할 필요가 없습니다. 이 세 층마다 다르게 두는 편이 대체로 낫습니다.

디자인 시스템 거버넌스 세 모델의 비교 다이어그램. 좌측 중앙집중형은 중앙에 시스템팀 노드가 있고 여러 프로덕트팀이 그 주위에서 화살표로 요청을 보내는 방사형 구조입니다. 요청이 한 곳에 몰리는 지점이 병목으로 표시됩니다. 중앙 연합형은 각 프로덕트팀에서 나온 대표들이 원형으로 모여 있고 그 사이에 결정 회의가 배치된 구조입니다. 의사결정 경로가 길어지는 점이 표시됩니다. 우측 커뮤니티형은 여러 팀이 직접 시스템 저장소에 기여하는 다대일 구조이며, 리뷰 지점이 유일한 관문으로 표시됩니다. 각 모델 아래에는 일관성, 속도, 병목 지점, 적합한 규모가 표로 정리되어 있습니다. 하단에는 혼합형이 배치되어, 층별로 다른 모델을 적용하는 방식이 표시됩니다. 토큰은 중앙집중, 시스템 컴포넌트는 중앙집중에 기여 허용, 프로덕트 컴포넌트는 각 팀 소유입니다.

기술 부채의 소유권

부채는 대개 경계 위에 쌓입니다. 두 팀 사이의 API, 아무도 안 보는 배치 잡, 옛 버전 호환 코드가 대표적입니다.

한 팀 안에서 쌓인 부채는 그 팀에서 문제를 일으니니 결국 그 팀이 갚게 됩니다. 그러나 경계 위의 부채는 경계 위에 묘하게 떠서 경계 양쪽 모두에게 문제를 일으키는데, 아무에게도 결정권이 없습니다.

이 문제를 다루는 실무적 방법이 몇 가지 있습니다.

소유자를 명시합니다. 코드베이스의 모든 영역에 담당 팀이 있어야 합니다. CODEOWNERS 파일이 이 역할을 합니다.

# CODEOWNERS
/packages/design-system/    @org/design-system-team
/packages/checkout/         @org/checkout-team
/packages/shared/           @org/platform-team
/scripts/legacy-migration/  @org/platform-team

주인 없는 영역이 남으면 명시적으로 표시합니다. “이 코드는 소유자가 없고, 다음 분기에 정리 대상”이라고 뭐라도 적어 두는 것이 조용히 방치하는 것보다 낫습니다.

경계 위의 작업을 위해서는 양쪽 팀의 시간이 함께 잡혀야 합니다. ep.12 변경 경계의 Expand-Contract를 실행하려면, 스키마를 바꾸는 팀과 읽는 팀이 같은 스프린트에 움직여야 합니다. 한쪽만 일정을 잡으면 마이그레이션은 중간에 멈춥니다.

문서가 코드가 되는 지점

조직이 커지면 구두 합의가 유지되지 않습니다. 사람이 바뀌고, 맥락이 사라집니다.

그래서 합의를 실행 가능한 형태로 옮기는 것이 중요합니다. 문서 뿐만 아니라 검사로도 만들어야 합니다.

합의문서 형태실행 가능한 형태
모듈 간 의존 금지아키텍처 문서ESLint import/no-restricted-paths
토큰 값 직접 사용 금지디자인 가이드Stylelint 커스텀 규칙
접근성 기준체크리스트CI의 axe 검사
API 하위 호환릴리스 정책계약 테스트
번들 크기 상한성능 가이드CI의 size-limit

문서는 읽지 않으면 없는 것과 같지만, 검사는 통과하지 않으면 머지가 안 됩니다.

다만 전부를 자동화할 수는 없습니다. “왜 이렇게 결정했는가”는 여전히 글이어야 합니다. ADR(Architecture Decision Record) 이 그 자리를 채웁니다.

# ADR-012: 결제 서비스를 별도 배포 단위로 분리

## 맥락
결제팀이 주 2회 배포를 원하나, 현재 모놀리스는 격주 배포다.

## 결정
결제 도메인을 별도 서비스로 분리한다.

## 결과
- 결제팀은 독립 배포가 가능해진다
- 주문-결제 간 트랜잭션이 불가능해지므로 Saga로 전환한다
- 운영 부담이 늘어난다 (배포 파이프라인 +1, 모니터링 대상 +1)

## 대안
- 모놀리스 유지 + 배포 주기 단축: 다른 팀의 준비가 안 됨

ADR이 유용한 이유는 결정의 이유가 남기 때문입니다. 2년 뒤 “왜 이렇게 되어 있지”라는 질문에 답할 수 있습니다.

ADR의 표준 템플릿과 상태 변화는 의사결정의 기록: ADR과 RFC에서, 아키텍처 정의서 안에서 ADR이 놓이는 자리는 결정을 남기다: ADR에서 자세히 다뤘습니다.

안티패턴

조직 구조와 상충되는 아키텍처: 팀이 둘인데 서비스를 열둘로 나누면, 한 팀이 여섯 개를 관리합니다.

주인 없는 공유 코드: shared/가 커지는데 담당 팀이 없으면, 아무도 리팩터링하지 않고 아무도 지우지 못합니다.

플랫폼팀이 게이트키퍼가 되는 것: 모든 배포가 플랫폼팀 승인을 거치면, 플랫폼팀이 병목이자 원망의 대상이 됩니다. 승인이 아니라 안전한 기본값과 자동 검사로 대체합니다.

거버넌스 없는 디자인 시스템: 누구나 컴포넌트를 추가할 수 있는데 리뷰 기준이 없으면, 비슷한 컴포넌트가 세 개씩 생깁니다.


이 경계가 화면에 드러나는 자리

조직 경계는 사용자에게 제품의 이음매로 드러납니다.

팀 경계가 화면에 보일 때

한 서비스 안에서 화면마다 다른 규칙이 적용되는 경험이 있습니다.

  • 어떤 화면은 뒤로 가기가 되고 어떤 화면은 안 됩니다.
  • 에러 메시지의 어조가 화면마다 다릅니다.
  • 같은 개념을 화면마다 다른 단어로 부릅니다. “주문 내역” vs “구매 목록” vs “결제 이력”.

마지막이 특히 흔합니다. 용어가 팀 경계에서 갈라집니다. 결제팀은 “결제”, 주문팀은 “주문”, CS팀은 “구매”라고 부르면, 사용자는 세 단어가 같은 것인지 다른 것인지 알 수 없습니다.

용어 문제는 UI 컴포넌트를 통일한다고 해결되지 않습니다. 버튼 모양이 같아도 말이 다르면 다른 제품처럼 느껴집니다.

콘텐츠 디자인과 용어집(glossary)이 디자인 시스템의 일부여야 하는 이유입니다. 색과 간격만 관리하는 시스템은 절반만 하는 것입니다.

# 용어집 예
주문(order)    — 사용자가 상품을 구매하기로 한 건. 화면 노출 시 "주문"
결제(payment)  — 주문에 대한 금전 처리. 화면 노출 시 "결제"
구매(purchase) — 사용하지 않음. "주문"으로 통일

제품 경계와 시스템 경계가 어긋날 때

더 근본적인 문제도 있습니다. 사용자가 느끼는 제품의 경계와 시스템의 경계가 다를 때입니다.

사용자에게는 “쇼핑”이 하나의 경험입니다. 상품을 보고, 담고, 사고, 배송을 확인합니다. 그런데 시스템은 상품 서비스, 장바구니 서비스, 주문 서비스, 배송 서비스로 나뉘어 있습니다.

이 어긋남 자체는 문제가 아닙니다. 사용자가 내부 구조를 알 필요는 없습니다. 문제는 그 경계가 사용자에게 새어 나올 때입니다.

  • 장바구니에서 주문서로 넘어가는데 화면이 깜빡이고 로딩이 다시 시작됩니다.
  • 상품 상세에서 본 가격과 장바구니의 가격이 다릅니다.
  • 배송 조회를 누르면 갑자기 다른 디자인의 페이지로 이동합니다.

각각은 ep.11 배포 경계, ep.08 캐시 경계, ep.01 모듈 경계의 결과입니다. 그리고 그 경계들은 대개 조직 경계에서 왔습니다.

시스템 경계가 새어 나오는 지점을 찾는 가장 좋은 방법은 사용자 여정을 따라 걸어 보는 것입니다. 화면 단위 리뷰로는 보이지 않습니다. 각 화면은 그 팀 안에서 완결되어 있기 때문입니다. 경계는 화면과 화면 사이에 있습니다.

이 작업에 팀을 가로지르는 사람이 필요합니다. 그 역할을 누가 하는지가 정해져 있지 않으면, 이음매는 아무의 책임도 아닌 채로 남습니다.


마치며

열세 개의 경계를 지나 부록까지 왔습니다.

이 시리즈가 하려던 이야기는 하나입니다. 경계에는 값이 매겨져 있고, 설계는 그 값을 알고 선을 긋는 일입니다.

경계는 비용의 이름이었습니다. 모듈을 나눌 때, 서비스를 쪼갤 때, 캐시를 넣을 때, 큐를 세울 때, 우리는 매번 무언가를 얻고 무언가를 지불했습니다.

그리고 그 지불은 대개 화면에 나타났습니다. 로딩 스피너, 에러 문구, “마지막 갱신 5분 전”, “환불을 처리하고 있습니다”. 시스템 설계와 사용자 경험이 다른 일처럼 보이지만, 같은 결정의 앞면과 뒷면이었습니다.


참고 자료


시리즈 전체 목차