SOLID 원칙이 벽과 방에 벽돌을 배치하는 방법을 알려준다면, 컴포넌트 원칙은 빌딩에 방을 배치하는 방법을 설명한다.
큰 빌딩과 마찬가지로 대규모 소프트웨어 시스템은 작은 컴포넌트들로 만들어지므로, 컴포넌트가 무엇인지, 구성하는 요소는 무엇인지 알아보고, 컴포넌트를 결합하여 시스탬을 구성하는 방법에 대해 논의한다.
컴포넌트는 시스템의 구성 요소로 배포할 수 있는 가장 작은 단위다.
- java: .jar, ruby: gem, .net: DLL
- 컴파일 언어에서는 바이너리 파일의 결합체
- 인터프리터 언어의 경우 소스 파일의 결합체
여러 컴포넌트를 서로 링크하여 실행 가능한 단일 파일로 생성할 수 있다.
- 단일 아카이브:
.war
- 플러그인:
.jar
,dll
- 실행 파일:
.exe
컴포넌트가 마지막에 어떤 형태로 배포되든, 잘 설계된 컴포넌트라면 반드시 독립적으로 배포 가능한, 따라서 독립적으로 개발 가능한 능력을 갖춰야한다.
컴포넌트의 간략한 역사
소프트웨어 개발 초창기에는 메모리에서의 프로그램 위치와 레이아웃을 프로그래머가 직접 제어하여, 프로그램 위치가 결정되면, 재배치가 불가능했다.
- 라이브러리 함수의 소스 코드를 애플리케이션 코드에 직접 포함시켜 단일 프로그램으로 컴파일했다.
- 라이브러리는 바이너리가 아니라 소스 코드 형태로 유지되었다.
- 자원이 한정적이었기 때문에 소스코드 전체를 여러번에 걸쳐서 읽어야하지만, 코드 전체를 메모리에 상주시킬 수가 없어 여러차례 읽어야했다.
- 컴파일 시간을 단축시키기위해 함수 라이브러리의 소스코드를 애플리케이션 코드로부터 분리했다.
- 개별적으로 컴파일하고, 컴파일된 바이너리를 메모리의 특정 위치에 로드했다.
- 하지만 애플리케이션은 점점 커졌고, 할당된 공간을 넘어서게 되어 두 개의 주소 세그먼트로 분리하고 함수 라이브라리 공간을 사이에 두고 오가며 동작하게 배치해야했다.
- 사용하는 메모리가 늘어날스록 이러한 단편화는 계속될 수밖에 없었기 때문에 지속가능하지 않았다.
재배치성
이러한 문제의 해결책은 재배치가 가능한 바이너리(Relocatable binary)였다.
- 지능적인 로더를 사용해서 메모리에 재배치할 수 있는 형태의 바이너리를 생성하도록 컴파일러를 수정
함수 라이브러리를 로드할 위치와 애필리케이션을 로드할 위치를 로더에게 지시할 수 있게 되었다.
이를 통해 오직 필요한 함수만을 로드할 수 있게 되었다.
컴파일러는 재배치 가능한 바이너리 안의 함수 이름을 메타데이터 형태로 생성하도록 수정되었다.
- 프로그램이 라이브러리 함수를 호출한다면 컴파일러는 라이브러리 함수 이름을 외부 참조(External reference)
- 라이브러리 함수를 정의하는 프로그램이라면 외부 정의(External definition)
이렇게 함으로써 외부 정의를 로드할 위치가 정해지만 하면 로더가 외부 참조를 외부 정의에 링크시킬 수 있게 되었고, 이렇게 링킹 로더(Linking Loader)가 탄생했다.
링커
링킹 로더의 등장으로 프로그래머는 프로그램을 개별적으로 컴파일하고 로드할 수 있는 단위로 분할할 수 있게 되었다.
하지만 프로그램도 훨씬 커지게 되며 링킹 로더의 성능으로 감당할 수 없는 수준이 되었다.
이에 따라 로드와 링크가 두 단계로 분리되었다.
- 링커라는 별도의 애플리케이션으로 링크가 완료된 재배치 코드를 만들어주어 로딩 과정이 아주 빨라졌다.
- 한번 만들어둔 실행 파일은 언제라도 빠르게 로드할 수 있게 되었다.
이후 C와 같은 고수준 언어를 사용하기 시작하며 소스 모듈은 .c
파일에서 .o
파일로 컴파일된 후, 링커로 전달되어 바르게 로드될 수 있는 형태의 실행 파일로 만들어졌다.
하지만 프로그램의 규모가 커지며 전체 모듈을 컴파일 하는데 꽤 시간이 걸렸고, 이후에 링크에서는 더 많은 시간이 소요되었다.
로드 시간은 여전히 빨랐지만, 컴파일-링크 시간이 병목 구간이었다.
하지만 자원의 성능이 비약적으로 향상되며 프로그램을 성장시키는 속도보다 링크 시간이 줄어드는 속고가 더 빨라지기 시작했고, 소규모 작업이라면 링킹 로더마저도 다시금 사용할 만하게 되었다.
이렇게 액티브 X와 공유 라이브러리 시대가 열렸고 .jar
파일도 등장하기 시작했다.
다수의 .jar
파일 또는 다수의 공유 라이브러리를 순식간에 서로 링크한 후, 링크가 끝난 프로그램을 실행할 수 있게 되었고, 이렇게 **컴포넌트 플러그인 아키텍쳐(Component Plugin Architecture)**가 탄생했다.
결론
런타임에 플러그인 형태로 결합할 수 있는 동적 링크 파일이 이 책에서 말하는 소프트웨어 컴포넌트에 해당한다.
과거에는 초인적인 노력을 들여아만 컴포넌트 플러그인 아키텍처를 적용할 수 있었지만, 이제는 기본으로 쉽게 사용할 수 있는 지점까지 다다랐다.