Folders and files Name Name Last commit message
Last commit date
parent directory
View all files
시스템 아키텍처는 일련의 소프트웨어 컴포넌트와 그 컴포넌트를 분리하는 경계에 의해 정의됨
이러한 경계는 다양한 형태로 나타남
적절한 위치에서 경계를 횡단하게 하는 비결은 소스 코드 의존성 관리에 있음
왜 소스코드 일까?
소스 코드 모듈 하나가 변경되면, 이에 의존하는 다른 소스 코드 모듈도 변경하거나, 다시 컴파일해서 새로 배포해야 할지도 모르기 때문
경계는 이러한 변경이 전파되는 것을 막는 방화벽을 구축하고 관리하는 수단으로써 존재
아키텍처 경계 중 가장 단순하며 가장 흔한 형태는 물리적으로 엄격하게 분리되지 않는 형태
이 형태에서는 함수와 데이터가 단일 프로세서에서 같은 주소 공간을 공유하며 그저 나름의 규칙에 따라 분리되어 있을 뿐
소스 수준 분리 모드
배포 관점에서 보면 이는 소위 단일체 monolith 라고 불리는 단일 실행 파일에 지나지 않음
배포관점에서 볼 때 단일체는 경계가 드러나지 않음
단일체는 컴포넌트 수준으로 분리되지 않으므로, 배포할 때 개별 컴포넌트를 배포하는 대신 커다란 하나의 파일을 배포
따라서 경계가 드러나지 않음
최종적으로 정적으로 링크된 단일 실행 파일을 만들더라도, 그 안에 포함된 다양한 컴포넌트를 개발하고 바이너리로 만드는 과정을 독립적으로 수행할 수 있게 하는 일은 대단히 가치 있는 일
이러한 아키텍처는 거의 모든 경우에 특정한 동적 다형성에 의존하여 내부 의존성을 관리함
바로 이 때문에 최근 수십 년 동안 객체 지향 개발이 아주 중요한 패러다임이 될 수 있었음
가장 단순한 형태의 경계 횡단은 저수준 클라이언트에서 고수준 서비스로 향하는 함수 호출
이 경우 런타임 의존성과 컴파일타임 의존성은 모두 같은 방향, 즉 저수준 컴포넌트에서 고수준 컴포넌트로 향함
고수준 클라이언트가 저수준 서비스를 호출해야 한다면 동적 다형성을 사용하여 제어 흐름과는 반대 방향으로 의존성을 역전시킬 수 있음
이렇게 하면 런타임 의존성은 컴파일타임 의존성과는 반대가 됨
단일체에서 컴포넌트 간 통신은 매우 빠르고 값이 쌈
통신은 전형적인 함수 호출에 지나지 않기 때문
결과적으로, 소스 수준에서 결합이 분리되면 경계를 가로지르는 통신은 상당히 빈번할 수 있음
단일체를 배포하는 일은 일반적으로 컴파일과 정적 링크 작업을 수반하므로, 대체로 이러한 시스템에서 컴포넌트는 소수 코드 형태로 전달됨
아키텍처의 경계가 물리적으로 드러날 수도 있는데, 그 중 가장 단순한 형태는 동적 링크 라이브러리
e.g.) .NET DLL, 자바 jar 파일, 루비 Gem, 유닉스 공유 라이브러리 등
컴포넌트를 이 형태로 배포하면 따로 컴파일하지 않고 곧바로 사용할 수 있음
대신 컴포넌트는 바이너리와 같이 배포 가능한 형태로 전달됨
배포 수준의 컴포넌트는 단일체와 동일함
일반적으로 모든 함수가 동일한 프로세서와 주소 공간에 위치하며, 컴포넌트를 분리하거나 컴포넌트 간 의존성을 관리하는 전략도 단일체와 동일함
배포형 컴포넌트의 경계를 가로지르는 통신은 순전히 함수 호출에 지나지 않으므로 매우 값이 쌈
동적 링크과 런타임 로딩으로 인해 최초의 함수 호출은 오래 걸릴 수 있지만, 대체로 이들 경계를 가로지르는 통신은 매우 빈번할 것
단일체와 배포형 컴포넌트는 모두 스레드를 활용할 수 있음
스레드는 아키텍처 경계도 아니며 배포 단위도 아님
스레드는 실행 계획과 순서를 체계화하는 방법에 가까움
모든 스레드가 단 하나의 컴포넌트에 포함될 수도 있고, 많은 컴포넌트에 걸쳐 분산될 수도 있음
훨씬 강한 물리적 형태를 띠는 아키텍처 경계로는 로컬 프로세스
로컬 프로세스는 주로 명령행이나 그와 유사한 시스템 호출을 통해 생성됨
대개의 경우 로컬 프로세스는 소켓이나 메일박스, 메시지 큐와 같이 운영체제에서 제공하는 통신 기능을 이용하여 서로 통신함
각 로컬 프로세스는 정적으로 링크된 단일체이거나 동적으로 링크된 여러 개의 컴포넌트로 구성될 수 있음
로컬 프로세스를 일종의 최상위 컴포넌트라고 생각하자
즉, 로컬 프로세스는 컴포넌트 간 의존성을 동적 다형성을 통해 관리하는 저수준 컴포넌트로 구성됨
로컬 프로세스 간 분리 전략은 단일체나 바이너리 컴포넌트의 경우와 동일
소스코드 의존성의 화살표는 단일체나 바이너리 컴포넌트와 동일한 방향으로 경계를 횡단함, 즉 항상 고수준 컴포넌트를 향함
저수준 프로세스가 고수준 프로세스의 플러그인이 되도록 만드는 것이 아키텍처 관점의 목표라는 사실을 기억
로컬 프로세스 경계를 지나는 통신에는 운영체제 호출, 데이터 마샬링 및 언마샬링, 프로세스 간 문맥 교환 등이 있음
이들은 제법 비싼 작업에 속함
따라서 통신이 너무 빈번하게 이뤄지지 않도록 신중하게 제한해야 함
물리적인 형태를 띠는 가장 강력한 경계는 바로 서비스
서비스는 프로세스로, 일반적으로 명령행 또는 그와 동등한 시스템 호출을 통해 구동됨
서비스 경계를 지나는 통신은 함수 호출에 비해 매우 느림
따라서 주의를 기울여서, 가능하다면 빈번하게 통신하는 일을 피해야 함
이 수준의 통신에서는 지연에 따른 문제를 고수준에서 처리할 수 있어야 함
저수준 서비스는 반드시 고수준 서비스에 플러그인되어야 함
고수준 서비스의 소스 코드에는 저수준 서비스를 특정 짓는 어떤 물리적인 정보도 절대 포함해서는 안됨
단일체를 제외한 대다수의 시스템은 한 가지 이상의 경계 전략을 사용
서비스 경계를 활용하는 시스템이라면 로컬 프로세스 경계도 일부 포함하고 있을 수 있음
실제로 서비스는 상호작용하는 일련의 로컬 프로세스 퍼사드에 불과할 때가 많음
또한 개별 서비스 또는 로컬 프로세스는 거의 언제나 소스 코드 컴포넌트로 구성된 단일체이거나, 혹은 동적으로 링크된 배포형 컴포넌트의 집합
대체로 한 시스템 안에서 통신이 빈번한 로컬 경계와 지연을 중요하게 고려해야 하는 경계가 혼합되어 있음을 의미
You can’t perform that action at this time.