본문으로 건너뛰기

시스템의 형태는 경계가 결정한다 - 디자인 패턴, 시스템 수준 편 ep.00

시스템을 설계한다는 것은 경계선을 긋는 일이다.

앞선 시리즈가 객체 하나의 책임에 붙은 이름을 다뤘다면, 이번 시리즈는 그 위 층위를 다룹니다. 모듈과 모듈 사이, 서버와 브라우저 사이, 지금과 나중 사이에는 선이 그어집니다. 그 선마다 값이 매겨져 있고, 설계는 그 값을 알고 선을 긋는 것입니다.


경계에는 값이 매겨져 있다

시스템 설계를 이야기할 때 우리는 보통 구성 요소를 먼저 말합니다. 어떤 서비스가 있고, 어떤 데이터베이스를 쓰고, 프론트엔드는 무엇으로 만드는가. 그러나 시스템의 성격을 실제로 결정하는 것은 구성 요소가 아니라 그 사이에 그은 선입니다.

같은 코드를 두 파일에 나눠 두면 모듈 경계가 생깁니다. 같은 함수를 서버에서 실행하면 프로세스 경계가 생깁니다. 같은 값을 두 곳에 저장하면 캐시 경계가 생깁니다. 그리고 경계가 생길 때마다 무언가가 청구됩니다.

  • 지연이 청구됩니다. 네트워크 경계를 넘는 호출은 로컬 호출보다 수천 배 느립니다.
  • 복잡도가 청구됩니다. 경계를 넘는 데이터는 직렬화되고, 검증되고, 다시 조립됩니다.
  • 일관성이 청구됩니다. 두 곳에 사본이 생기면 둘 중 하나는 낡습니다.
  • 수명이 청구됩니다. 경계는 곧 약속이 되고, 약속은 바꾸기 어려워집니다.
  • 가시성이 청구됩니다. 경계 너머에서 벌어진 일은 이쪽에서 보이지 않습니다.

경계를 긋지 않으면 이 비용을 내지 않습니다. 대신 다른 비용을 내게 됩니다. 모든 것이 모든 것에 연결되어 있어 한 곳을 고치면 다른 곳이 무너집니다.

설계는 단순히 경계를 긋는 일이 아니라, 어느 비용을 낼지 고르는 일입니다.

시스템 경계의 비용 구조를 보여주는 다이어그램. 상단에 '경계를 긋지 않았을 때'와 '경계를 그었을 때'의 두 상태가 좌우로 배치되어 있습니다. 좌측 상태에서는 여섯 개의 요소가 서로 모두 연결된 그물망 형태로 그려져 있고 '결합 비용'이라는 라벨이 붙어 있습니다. 우측 상태에서는 같은 요소들이 두 덩어리로 나뉘고 그 사이에 굵은 경계선이 그어져 있으며, 경계선을 넘는 연결이 두 개로 줄어 있습니다. 경계선 위에는 청구되는 다섯 가지 비용이 세로로 나열되어 있습니다. 지연, 복잡도, 일관성, 수명, 가시성입니다. 하단에는 '설계는 단순히 경계를 긋는 일이 아니라 어느 비용을 낼지 고르는 일'이라는 결론 문장이 배치되어 있습니다.


버튼 하나가 통과하는 경계들

추상적으로 들리는 이야기를 구체적으로 만들어 봅니다.

사용자가 결제 버튼을 누릅니다. 이 한 번의 클릭이 화면을 떠나 다시 돌아오기까지, 요청은 열세 개의 경계를 지납니다. 이 모든 것을 결정해야 합니다.

버튼 컴포넌트가 어느 모듈에 속하는지가 이미 결정입니다. 그 컴포넌트가 결제 API의 응답 형태를 직접 알아야 하는지가 결정입니다. 검증을 브라우저에서 할지 서버에서 할지가 결정이고, 결제 요청이 실패했을 때 재시도할지가 결정이고, 결제와 주문 생성이 하나의 사건인지 둘인지가 결정입니다. 그 결과를 어디에 캐시할지, 이 버튼이 다른 화면과 함께 배포되는지, 실패했을 때 무슨 일이 있었는지 나중에 알 수 있는지, 모두 결정입니다.

각 결정에는 이미 이름이 있습니다. 그 이름들을 순서대로 따라가는 것이 이 시리즈의 구성입니다.

결제 버튼 클릭 한 번이 통과하는 경계들의 단면도. 좌측 상단의 사용자 클릭에서 시작해 우측 하단까지 이어지는 흐름이 계단식으로 그려져 있습니다. 흐름은 순서대로 다음 경계들을 통과합니다. 모듈 경계에서는 버튼 컴포넌트가 어느 덩어리에 속하는지, 의존 방향 경계에서는 컴포넌트가 무엇을 알아야 하는지, 계약 경계에서는 응답 데이터의 형태가 보장되는지, 실행 경계에서는 이 코드가 서버와 브라우저 중 어디에서 도는지, 네트워크 경계에서는 호출이 실패할 수 있다는 사실, 시간 경계에서는 지금 처리할지 큐에 넣을지, 상태 경계에서는 결과 값의 소유자가 누구인지, 캐시 경계에서는 사본이 몇 개인지, 일관성 경계에서는 결제와 주문이 하나의 사건인지, 신뢰 경계에서는 어디서 검증되는지, 배포 경계에서는 무엇과 함께 배포되는지, 변경 경계에서는 어느 부분이 약속인지, 관측 경계에서는 무슨 일이 있었는지 알 수 있는지를 각각 묻습니다. 각 경계 옆에는 해당하는 편 번호가 표시되어 있습니다.


이 시리즈에서 다룰 것들

경계를 네 층위로 나눴습니다. 코드가 자리에 있을 때, 코드가 움직일 때, 값이 흐를 때, 시스템이 살아 있을 때입니다.

각 편은 다음 흐름으로 진행합니다.

  1. 이 경계는 무엇이고 무엇을 청구하는가
  2. 이 경계에 이미 붙어 있는 이름들: 구조, 코드, 안티패턴
  3. 이 경계가 화면에 드러나는 자리: 디자인·UX 결정

코드 예제는 앞선 시리즈와 같이 TypeScript로 통일했습니다.

I. 구조의 경계 - 코드가 자리에 있을 때

  • ep.01 모듈 경계: 이 코드들은 왜 같은 덩어리에 있는가. 폴더 구조는 취향이 아니라 결합에 대한 선언입니다.
  • ep.02 의존 방향: 누가 누구를 알아야 하는가. 화살표의 방향 하나가 시스템의 교체 가능성을 결정합니다.
  • ep.03 계약 경계: 경계를 넘은 데이터는 무엇을 보장하는가. 타입은 컴파일 타임에 사라집니다.

II. 실행의 경계 - 코드가 움직일 때

  • ep.04 실행 경계: 이 작업은 언제, 어디서 실행되어야 하는가. 빌드 타임과 런타임, 서버와 브라우저의 사분면입니다.
  • ep.05 네트워크 경계: 호출이 실패한다는 사실을 어디까지 인정할 것인가. 로딩과 에러 화면은 이 인정의 결과물입니다.
  • ep.06 시간의 경계: 지금 처리할 것인가, 나중으로 미룰 것인가. 응답 시점과 완료 시점이 갈라지는 순간의 이야기입니다.

III. 데이터의 경계 - 값이 흐를 때

  • ep.07 상태의 경계: 이 값의 소유자는 누구인가. 상태를 어디에 두는지가 곧 화면의 성격을 결정합니다.
  • ep.08 캐시 경계: 이 값의 사본은 몇 개이고 누가 낡았는가. 캐시는 최적화가 아니라 사본을 만드는 결정입니다.
  • ep.09 일관성 경계: 어디까지가 하나의 사건인가. 트랜잭션이 닿지 않는 곳에서 벌어지는 일입니다.
  • ep.10 신뢰 경계: 이 입력은 어디서 검증되어야 하는가. 클라이언트 검증은 UX이고 서버 검증은 보안입니다.

IV. 수명의 경계 - 시스템이 살아 있을 때

  • ep.11 배포 경계: 무엇이 함께 배포되어야 하는가. 배포 단위는 코드 구조가 아니라 릴리스 속도가 결정합니다.
  • ep.12 변경 경계: 어느 부분이 약속이고 어느 부분이 구현인가. 한 번 공개한 것은 되돌리기 어렵습니다.
  • ep.13 관측 경계: 경계 너머에서 무슨 일이 있었는지 어떻게 아는가. 앞의 열두 경계가 만든 사각지대를 되짚습니다.

부록

  • ep.14 조직 경계: 앞의 열세 경계는 누가 결정했는가. 아키텍처 다이어그램은 조직도라고 할 수 있습니다.

시즌 전체 카탈로그. 네 개의 층위가 세로로 배치되어 있습니다. 첫 번째 층위 구조의 경계는 파란색으로 표시되며 ep.01 모듈 경계, ep.02 의존 방향, ep.03 계약 경계 세 편을 포함합니다. 두 번째 층위 실행의 경계는 주황색으로 표시되며 ep.04 실행 경계, ep.05 네트워크 경계, ep.06 시간의 경계 세 편을 포함합니다. 세 번째 층위 데이터의 경계는 보라색으로 표시되며 ep.07 상태의 경계, ep.08 캐시 경계, ep.09 일관성 경계, ep.10 신뢰 경계 네 편을 포함합니다. 네 번째 층위 수명의 경계는 청록색으로 표시되며 ep.11 배포 경계, ep.12 변경 경계, ep.13 관측 경계 세 편을 포함합니다. 하단에는 회색으로 부록 ep.14 조직 경계가 별도로 배치되어 있습니다. 각 카드에는 편 번호와 경계 이름, 그리고 해당 편에서 다루는 대표 패턴 이름이 함께 적혀 있습니다.


이 시리즈를 읽는 법

열네 편은 적은 분량이 아닙니다. 순서대로 읽지 않아도 되도록 만들었습니다.

  • 처음부터 읽는다면: I군부터 순서대로 읽으시면 됩니다. 뒤로 갈수록 앞의 경계를 전제하지만, 각 편에 필요한 만큼 되짚습니다.
  • 프론트엔드 작업 중이라면: ep.04 실행 경계에서 시작하세요. ep.05 네트워크, ep.07 상태, ep.08 캐시로 이어지는 네 편이 화면을 그리는 일과 가장 가깝습니다.
  • 백엔드 작업 중이라면: ep.02 의존 방향에서 시작해 ep.06 시간, ep.09 일관성, ep.13 관측으로 가는 동선이 자연스럽습니다.
  • 디자이너라면: 각 편의 3번 단락만 이어 읽으셔도 됩니다. 로딩 화면, 에러 문구, 낡은 데이터 표시, 권한에 따른 UI 분기: 모두 시스템 경계가 화면에 드러난 자리입니다.
  • 선행 편 없이 읽을 수 있는 편: ep.01, ep.04, ep.05, ep.06, ep.11은 완전히 독립적입니다. 검색으로 어느 편에 들어오셔도 그 자리에서 읽힙니다.

앞선 시리즈를 읽지 않으셔도 됩니다. 필요한 자리에 링크만 남기고, 내용을 다시 설명하지는 않습니다.


시리즈 전체 목차


참고 자료


다음 편 예고

ep.01 - 모듈 경계와 폴더 구조 전략

폴더를 나누는 일은 취향의 문제로 보입니다. 그러나 폴더 구조는 무엇이 함께 바뀌는지에 대한 선언이고, 그 선언이 틀리면 매 작업마다 비용을 냅니다. 첫 번째 경계는 가장 조용한 경계입니다.