팀 경계
고친 사람 github-actions[bot]
팀 경계는 한 팀이 책임지는 범위와 다른 팀의 범위를 가릅니다. 경계 안의 코드와 데이터는 그 팀이 혼자 바꾸고 배포합니다. 경계를 넘는 일은 다른 팀과 정한 약속을 거쳐서만 합니다. 시간이 지나면 코드도 팀이 나뉜 대로 나뉩니다.
쉽고 빠른 이해
팀 경계는 「이 코드는 어느 팀 것인가」에 답하는 선입니다. 결제 팀은 결제 서비스와 결제 데이터베이스를 맡습니다. 주문 팀은 그것을 직접 건드리지 않습니다.
선이 없으면 아무 팀이나 아무 코드를 고칩니다. 작은 수정에도 모든 팀에 물어봐야 합니다. 장애가 나면 누가 나설지부터 정해야 합니다.
도는 방식은 이렇습니다.
- 업무 흐름을 따라 팀마다 맡을 범위를 정합니다
- 범위 안은 그 팀이 스스로 고치고 배포합니다
- 범위 밖과는 미리 정해 둔 호출 창구나 알림으로만 주고받습니다
대가도 있습니다. 비슷한 코드가 팀마다 생깁니다. 여러 팀에 걸친 일은 느려집니다. 선을 잘못 그으면 고치려고 팀을 다시 짜야 합니다. 조직에 팀이 하나뿐이거나 몇 사람뿐이라면 이 값이 얻는 것보다 큽니다.
상세
예로는 쇼핑몰의 주문 팀과 결제 팀을 끝까지 씁니다.
아파트를 떠올리면 쉽습니다. 각 세대는 자기 집 안을 이웃에게 묻지 않고 고칩니다. 복도나 배관처럼 여러 세대가 같이 쓰는 것은 관리 규약을 따라서만 손댑니다. 집 안과 공용 부분을 가르는 선이 있어서 이웃끼리 부딪히지 않습니다.
팀 경계가 가르는 것
팀 경계는 한 팀이 책임지는 범위를 그 밖과 가르는 선입니다. 여기서 책임진다는 말은 고칠 권한과 탈이 났을 때 나설 의무를 함께 진다는 뜻입니다. 결제 팀이 결제 서비스를 맡는다면 그 코드를 바꾸는 결정은 결제 팀이 내립니다. 장애 연락을 돌아가며 받는 온콜 당번도 결제 팀에서 섭니다.
선 안에 드는 것은 코드만이 아닙니다. 결제 팀이라면 코드와 데이터, 배포와 운영이 흔히 함께 경계 안에 듭니다.
| 무엇 | 결제 팀 경계 안의 예 | 선이 있으면 |
|---|---|---|
| 코드 | 결제 서비스의 코드 저장소 | 결제 팀이 검토하고 합친다 |
| 데이터 | 결제 데이터베이스 | 다른 팀은 직접 읽거나 쓰지 않는다 |
| 배포 | 결제 서비스를 운영 환경에 내보내는 일 | 다른 팀 일정을 기다리지 않는다 |
| 운영 | 결제 장애 연락 | 결제 팀이 받는다 |
네 줄이 한 팀에 모여 있을수록 선이 또렷합니다. 코드는 결제 팀이 맡지만 데이터베이스는 다른 팀과 같이 쓰는 경우가 있습니다. 그러면 선은 반쯤만 그어진 것입니다.
경계가 없을 때 생기는 일
선이 흐리면 모든 팀이 모든 코드를 만집니다. 처음에는 빨라 보입니다. 필요한 곳을 누구든 바로 고칠 수 있기 때문입니다.
문제는 수정이 쌓이면서 드러납니다. 결제 코드를 주문 팀도 고치고 배송 팀도 고치면, 결제 팀은 자기 코드가 왜 이렇게 바뀌었는지 모르게 됩니다. 작은 수정 하나에도 누가 이 코드에 기대고 있는지 다 물어봐야 합니다.
장애 때도 같습니다. 결제가 멈췄다고 해 봅시다. 마지막으로 고친 팀이 셋이면 누가 먼저 나설지부터 정해야 합니다. 팀 경계는 이런 물음에 미리 답을 정해 두는 장치입니다.
경계 안과 밖의 규칙
선을 그으면 일하는 규칙이 안과 밖으로 갈립니다. 아래는 주문 팀과 결제 팀 사이에 두 규칙이 어떻게 걸리는지를 따라갑니다.
경계 안에서는 팀이 혼자 정합니다. 결제 팀은 결제 데이터베이스의 테이블을 쪼개든 합치든 다른 팀에 묻지 않습니다. 안쪽 구조가 바깥에서 보이지 않기 때문입니다.
경계 밖과는 정해 둔 창구로만 주고받습니다. 대표적인 창구가 API(Application Programming Interface, 프로그램끼리 주고받는 호출 창구)입니다. 주문 팀은 결제 데이터베이스를 직접 읽지 않습니다. 대신 결제 팀이 열어 둔 결제 요청 API를 부릅니다.
다른 창구는 이벤트입니다. 이벤트는 「무슨 일이 일어났다」를 알리는 메시지입니다. 결제 팀이 「결제가 끝났다」는 이벤트를 내보내면 주문 팀이 그것을 받아 주문 상태를 바꿉니다. 결제 팀은 누가 그 알림을 받는지 몰라도 됩니다.
flowchart TD
subgraph O["주문 팀 경계"]
OS["주문 서비스"] --> OD["주문 데이터베이스"]
end
subgraph P["결제 팀 경계"]
PA["결제 요청 API"] --> PS["결제 서비스"]
PS --> PD["결제 데이터베이스"]
end
EV["결제가 끝났다는 이벤트"]
OS -->|"결제 요청 API 호출"| PA
PS -.->|"내보낸다"| EV
EV -.->|"받아 간다"| OS
두 팀 사이를 오가는 길은 결제 요청 API 호출과 결제가 끝났다는 이벤트, 둘뿐입니다. 이벤트는 결제 서비스가 내보내 두면 주문 서비스가 받아 갑니다. 주문 서비스에서 결제 데이터베이스로 가는 선은 없습니다.
창구를 거치게 하는 까닭은 안쪽을 바꿀 자유를 지키려는 것입니다. 주문 팀이 결제 테이블을 직접 읽고 있다면, 결제 팀은 테이블 하나 고칠 때마다 주문 팀과 일정을 맞춰야 합니다. 창구만 지키면 그 뒤는 결제 팀 마음대로 바꿀 수 있습니다.
창구 자체를 바꿀 때는 하위 호환을 지킵니다. 하위 호환은 이미 부르고 있는 쪽이 깨지지 않게 옛 방식도 한동안 받아 주는 것입니다. 창구가 자주 깨지면 다른 팀이 결제 팀의 일정에 묶이게 됩니다.
팀 API는 팀이 바깥에 내놓는 약속 전체를 가리키는 넓은 말입니다. 호출 창구뿐 아니라 문서, 문의를 받는 채널, 요청을 처리하는 방식까지 아우르는 말입니다. 다른 팀이 이 팀과 일할 때 기대도 되는 것을 모아 둔 목록이라고 보면 됩니다.
선을 긋는 기준
팀 사이의 선은 흔히 기술 계층이나 업무 흐름을 따라 긋습니다. 어느 쪽을 고르느냐에 따라 기능 하나를 고칠 때 움직이는 팀 수가 달라집니다. 팀 하나가 맡을 크기도 함께 정해야 합니다.
첫째 기준은 기술 계층입니다. 화면 팀, 서버 팀, 데이터베이스 팀으로 나누는 방식입니다. 이렇게 나누면 결제 화면에 칸 하나를 더하는 일에도 세 팀이 모두 움직입니다. 기능 하나가 선을 여러 번 넘기 때문입니다.
둘째 기준은 업무 흐름입니다. 주문 팀, 결제 팀, 배송 팀처럼 업무마다 팀을 둡니다. 각 팀이 그 업무의 화면부터 저장소까지 다 맡으니 결제 화면을 고치는 일이 결제 팀 안에서 끝납니다. 스트림 정렬 팀은 이렇게 한 업무 흐름을 처음부터 끝까지 맡는 팀입니다.
| 선을 긋는 기준 | 팀의 예 | 기능 하나를 고칠 때 |
|---|---|---|
| 기술 계층 | 화면 팀 · 서버 팀 · 데이터베이스 팀 | 여러 팀이 함께 움직인다 |
| 업무 흐름 | 주문 팀 · 결제 팀 · 배송 팀 | 그 업무를 맡은 팀 안에서 끝난다 |
업무를 어디서 자를지는 경계 컨텍스트를 기준으로 삼는 일이 많습니다. 경계 컨텍스트는 한 낱말이 한 가지 뜻으로만 통하는 업무 범위입니다.
쇼핑몰의 「고객」을 예로 들어 봅시다. 결제 쪽에서 고객은 결제 수단과 청구할 금액이 딸린 사람입니다. 배송 쪽에서 고객은 물건을 받을 이름과 주소입니다. 이렇게 한 낱말의 뜻이 갈리는 곳에 선을 그으면 각 팀이 자기 뜻대로 고객을 다룰 수 있습니다.
팀 하나가 맡을 크기는 인지 부하로 가늠합니다. 인지 부하는 한 사람이 머릿속에 한꺼번에 들고 있어야 하는 정보의 양입니다. 맡은 범위가 너무 넓으면 팀 안에서도 아무도 전체를 알지 못합니다. 그러면 경계 안에서도 「이건 누가 알지」를 묻게 되어 선을 그은 보람이 줄어듭니다.
시스템 경계와 맞물림
팀 경계는 시간이 지나면 코드의 구조로 옮겨 갑니다. 한 팀이 주문과 결제를 함께 맡으면 주문 코드가 결제 코드의 함수를 바로 부르게 됩니다. 같은 팀 안이라 맞춰 볼 일이 적기 때문입니다. 주문 팀과 결제 팀이 따로 있으면 서로 약속을 맞춰야 하니 두 코드 사이에 API 같은 창구가 생깁니다.
콘웨이의 법칙은 시스템이 그것을 만든 조직의 소통 구조를 닮는다는 관찰입니다. 위의 주문 코드와 결제 코드가 그 작은 예입니다.
역 콘웨이 전략은 이 관찰을 거꾸로 쓰는 방법입니다. 원하는 시스템 구조를 먼저 정합니다. 그다음 팀 경계를 거기에 맞춰 짭니다.
쇼핑몰을 마이크로서비스로 나누려는 경우가 그 예입니다. 마이크로서비스는 시스템을 따로 배포하는 작은 서비스 여럿으로 나누는 방식입니다. 서버 팀 하나가 남아 있으면 서비스를 여럿 그려도 다시 한 덩어리로 엉킵니다. 그래서 서비스마다 맡을 팀부터 세웁니다.
팀과 서비스가 꼭 하나씩 짝지어질 필요는 없습니다. 팀이 여럿인 조직에서 한 팀이 서비스 몇 개를 맡는 것은 괜찮습니다. 그 서비스들이 모두 그 팀의 경계 안에 들기 때문입니다.
조직 전체에 팀이 하나뿐이라면 사정이 다릅니다. 서비스를 여럿으로 나눠도 같은 사람들이 모든 서비스를 고치니 그 사이 선이 점점 흐려집니다. 이럴 때는 서비스를 나누기보다 한 덩어리 안을 나눠 두는 편이 팀 구조와 맞습니다.
모듈러 모놀리스가 그런 방식입니다. 배포는 한 덩어리로 합니다. 코드 안은 업무별 모듈로 나눠, 모듈끼리는 정해 둔 창구로만 부르게 합니다.
잘못 그은 경계의 신호
선이 업무와 어긋나면 코드보다 일하는 모습에서 먼저 드러납니다. 흔한 신호 넷과 각 신호가 가리키는 문제는 이렇습니다.
| 신호 | 가리키는 문제 |
|---|---|
| 기능 하나를 내려면 팀 셋이 일정을 맞춘다 | 한 업무가 여러 팀에 쪼개져 있다 |
| 다른 팀 데이터베이스의 테이블을 직접 읽는다 | 창구를 거치지 않고 선을 넘고 있다 |
| 다른 팀 작업이 끝나길 기다리는 시간이 길다 | 팀 사이에 일을 넘겨주는 단계가 많다 |
| 아무도 맡지 않은 코드가 있다 | 선이 비어 있다 |
이런 신호가 보이면 코드를 정리하기 전에 선을 다시 봅니다. 업무 하나가 두 팀에 쪼개져 있다면, 코드를 아무리 다듬어도 두 팀이 맞춰야 하는 일은 남습니다.
선이 어긋난 채로 서비스를 나누면 서비스끼리 늘 함께 배포해야 하는 상태가 됩니다. 분산 모놀리스는 이렇게 나뉘어 있지만 따로 움직이지 못하는 시스템을 가리킵니다.
쓰면 치르는 값
선을 또렷하게 그으면 얻는 만큼 치르는 값도 있습니다. 흔히 드는 값은 셋입니다.
첫째, 비슷한 코드가 팀마다 생깁니다. 주문 팀과 결제 팀이 각자 금액 계산 코드를 들고 있을 수 있습니다. 이것을 공통 코드로 모으면 두 팀이 그 코드를 함께 바꿔야 해서 선이 다시 흐려집니다.
둘째, 여러 팀에 걸친 일은 느려집니다. 경계 안의 일은 빠르지만 창구를 바꾸는 일은 두 팀이 같이 정해야 합니다. 선을 넘는 기능이 잦다면 선을 잘못 그은 것일 수 있습니다.
셋째, 선을 다시 긋는 일은 비쌉니다. 코드는 옮기면 되지만 팀은 역할과 보고 체계와 일하는 습관이 함께 바뀌어야 합니다. 그래서 처음 선을 그을 때 업무 흐름을 충분히 들여다봅니다.
관련 항목
팀 경계가 속하는 설계 분야
소프트웨어 아키텍처 · 사회기술 시스템 · 조직 설계 · 도메인 주도 설계
팀 경계를 정하는 기준과 법칙
콘웨이의 법칙 · 역 콘웨이 전략 · 경계 컨텍스트 · 인지 부하 · 하위 도메인
팀 경계로 나뉘는 팀 유형
팀 토폴로지 · 스트림 정렬 팀 · 플랫폼 팀 · 인에이블링 팀 · 복잡한 하위 시스템 팀 · 두 피자 팀
경계를 넘어 오가는 창구
API · 팀 API · 이벤트 · 인터페이스 · 하위 호환 · API 설계
팀 경계를 따라 갈리는 시스템 구조
마이크로서비스 · 모놀리스 · 모듈러 모놀리스 · 레이어드 · 서비스 지향 아키텍처
경계가 잘 그어졌나를 재는 성질
팀 경계 안에 드는 책임
코드 소유권 · 온콜 · 배포 · 데브옵스 · 데이터베이스
선을 잘못 그었을 때 드러나는 문제
분산 모놀리스 · 공유 데이터베이스 · 빅 볼 오브 머드 · 순환 의존
다른 이름: Team boundary · team boundary · Team boundaries · 팀 바운더리 · 팀 간 경계