////
Search
Duplicate
👖

Chapter 09. 모듈

모듈이나 모듈 시스템이란 무엇일까?
이 질문을 했을 때 얻을 수 있는 일반적인 답변은 다음과 같다.
모듈은 독립된 코드 묶음이다.
자바에서 모듈 시스템은 패키지다.
자바 9에서 모듈 시스템은 module-info.java다.
이러한 답변들은 모두 모호하다. 답변이 모두 모듈이나 모듈 시스템이 뭔지를 설명하기보다 구체적인 사례에 빗대어 설명하기 때문이다.
우리가 궁금한 것은 모듈과 모듈 시스템이 무엇이냐다. 자바에서 모듈에 무엇이 대응하냐가 아니다.
모듈과 모듈 시스템이 뭔지 알고 있다면 다음과 같은 질문에 대답할 수 있어야 한다.
소프트웨어에서 말하는 모듈이나 모듈 시스템이란 무엇일까?
자바의 패키지는 왜 모듈 시스템이 될 수 없을까?
자바 9부터 추가된 모듈 시스템은 무엇이고 왜 추가된 것일까?
자바 9부터 모듈 시스템이 추가됐다면 자바 9 이전에는 모듈 시스템이 없었다고 봐야할까?
이번 장에서는 이런 질문들에 대해 답변을 해보며 먼저 모듈과 모듈 시스템에 관해 알아본다. 나아가 자바가 발전해 온 발전 과정에 엮어 자바 9부터 모듈 시스템이 추가된 배경에 관해서도 설명한다.

1. 모듈성

모듈이란 무엇일까? 먼저 모듈이란 용어는 추상적이라는 것을 알아야 한다. 이는 소프트웨어 공학에서만 한정적으로 사용되는 용어가 아니기 때문이다.
이런 다양한 분야에서 모듈은 일반적으로 어떤 목적을 수행하기 위해 분리된 작은 구성 단위를 의미한다.
모듈의 사용례를 보면 대부분 큰 개체의 일부를 뜻하고 있음을 알 수 있다. 그러나 이런 의미는 소프트웨어 분야에서 사용하기엔 너무 범용적이다.
소프트웨어 공학에서 말하는 모듈이란 무엇일까? 이 질문에 대해 일반적으로 들을 수 있는 답변은 다음과 같다.
1.
모듈은 프로그램의 기본 구성 요소다.
이러한 정의는 앞선 답변과 마찬가지로 지나치게 넓은 의미를 갖고 있기 때문에 여전히 모듈이 무엇인지 설명하지 못한다.
예를 들어, 객체지향에서 프로그램의 기본 구성 요소는 객체다. 앞선 정의에 따르면 우리는 객체 하나하나를 모두 모듈이라고 불러야 한다. 그러나 이는 너무 지나친 해석같으면서도 모호하다.
따라서 이러한 답변은 효용성이 없는 답변이다.
2.
특수한 목적을 가지고 만들어지는 라이브러리다.
이 정의는 너무 집약적이다. 이러한 정의를 따른다면 라이브러리를 사용하지 않는 경우, 해당 어플리케이션은 모듈이 없다고 해도 무리가 없다.
소프트웨어를 개발하면서 라이브러리를 만들진 않아도 모듈을 만든다라고 자주 표현한다. 따라서 라이브러리가 아니어도 모듈이 될 수 있다.
소프트웨어 공학에서 모듈은 프로그램의 기본 구성 요소이면서 라이브러리를 포괄하는 조금 더 큰 규모의 용어다. 즉, 라이브러리와 프로그램의 구성 요소 사이에 위치하는 개념이다.
저자가 이 이야기를 꺼낸 이유는 단지, 사람마다 모듈이라는 개념을 받아들이는 수준이 다르다는 것을 알려주기 위함이었다.
따라서 보다 명확한 기준이 필요하다고 느끼며 저자는 이에 관해 자신의 기준은 다음과 같다고 언급했다.
소프트웨어 관점에서 모듈이란 독립성과 은닉성을 만족하며 연관된 코드들의 묶음이다.
독립성은 모듈이 독립적이어야 한다는 정의며 은닉성은 모듈의 사용자는 모듈의 내부를 몰라도 된다. 즉 공개된 인터페이스를 이용해 모듈과 통신한다는 정의다.
이때 독립성과 은닉성 같은 특징들을 모듈성이라고 부른다. 즉, 연관된 코드 묶음이 모듈성을 만족할 때, 모듈이 될 수 있다.
모듈성에 관해 자세하게 설명하기 전, 모듈 시스템을 먼저 설명하는데, 이는 다음과 같다.
모듈 시스템이란 연관된 코드 묶음이 모듈성을 갖출 수 있게 도와주는 시스템적인 해결책이다. 모듈성을 지원하기 위해 모듈 시스템은 다음과 같은 기능을 필수적으로 지원해야 한다.
의존성 관리 : 모듈을 사용하기 위해 어떤 의존성이 필요한지 명시할 수 있어야 한다.
캡슐화 관리 : 모듈은 불필요한 구현을 외부로 드러내지 않아야 한다.
모듈 시스템이 이런 기능을 제공해야 하는 이유는 독립성과 은닉성에 대응해서 설명할 수 있다.
모듈이 독립성을 보장하기 위해 모듈 시스템은 모듈 차원의 의존성 관리를 할 수 있어야 한다.
모듈이 은닉성을 보장하기 위해 모듈 시스템은 모듈 차원의 캡슐화 관리를 할 수 있어야 한다.
이런 관점에서 자바의 패키지 시스템은 모듈 시스템이라고 부를 수 있을까? 정답은 아니요다. 자바의 패키지 시스템은 패키지 수준의 의존성 관리와 캡슐화 관리 기능을 지원하지 않기 때문이다.
자바에 대신 존재하는 모듈 시스템이 있는데, 이는 module-info.java라고 하는 모듈 디스크럽터다.
module myproject.main { requires org.apache.httpcomponenets.httpclient; exports com.myorg.myptoject.model; }
Java
복사
requires를 사용해 외부 의존성을 관리하고 exports를 사용해 노출할 모듈 내 인터페이스 패키지를 관리할 수 있다.
이는 자바 9에서 추가된 기능으로, 자바 9가 나오기 전까진 자바에 모듈 시스템이 존재하지 않았다.
저자는 모듈 시스템이 추구하는 가치를 조금 더 깊이 이해하고 패키지를 모듈에 가깝게 쓰라고 조언한다.
다음 주제로 넘어가기 전, 이번 장의 내용을 간략하게 요약한다.
모듈과 모듈 시스템
모듈은 모듈성을 만족한다.
모듈성에는 은닉성과 독립성이라는 특성이 있다.
모듈 시스템은 코드 묶음이 모듈성을 갖출 수 있게 도와주는 시스템이다.
모듈 시스템은 기능적으로 모듈 수준의 의존성 관리와 캡슐화 관리가 가능해야 한다.
패키지와 모듈은 다르다.
패키지 시스템이 모듈 시스템은 아니다.
패키지를 만들 때도 모듈처럼 독립성과 은닉성을 추구하는 것이 좋다.

1. 독립성

모듈이 독립적이어야 한다는 의미는 모듈이 다른 모듈이나 컴포넌트에 강하게 의존하지 않고 각 모듈을 개별적으로 수정하거나 교체할 수 있어야 한다는 뜻이다.
모듈이 독립적이어야 하는 이유는 유지보수 용이, 확장성 향상, 코드의 재사용성 향상 등이 있다.
이런 내용들을 기반으로 우리는 다음과 같은 사실을 유추할 수 있다.
모듈을 사용하기 전에 필요한 의존성을 알 수 있어야 한다.
모듈의 의존성이 모두 준비된다면 모듈을 사용하는데 아무런 문제가 없어야 한다.
어떤 시스템이 독립적이어야 한다면 대상이 외부 시스템과 완전히 격리되어야 한다는 말이 아니다. 독립적이다라는 말은 외부에 강한 의존이 생기는 것을 피하라는 의미일 뿐이다.
즉 다음과 같이 이해하는 것이 바람직하다.
최대한 내부에서 해결하라.
외부에는 강하게 의존하지 마라.
외부 시스템을 사용한다면 외부 시스템의 사용을 명시하라.
모듈의 독립성을 보장하기 위해서는 의존하는 모듈이 무엇인지 명확히 하면서도 연관된 코드만 모이도록 해야 한다. 응집도를 충분히 높이라는 말이다.

2. 은닉성

일반적으로 은닉성은 객체지향에서 캡슐화를 소개할 때 언급되는 개념으로 이 때문에 개발자들은 클래스 수준에서만 일어나는 일이라고 착각하기 쉽다.
이 개념은 모듈이나 패키지 수준까지 발전할 수 있으며 더 나아가 API 서버까지도 확장할 수 있다.
모듈은 은닉성을 추구해야 하는데 이는 클래스가 은닉성을 추구 추구하는 것처럼 모듈 수준의 캡슐화가 가능해야 한다는 말이다.
즉, 우리는 모듈을 외부에 공유하더라도 공개된 인터페이스 이외에 불필요한 정보를 숨길 수 있어야 한다는 것이다.
은닉성을 추구해야 하는 이유는 다양한데, 저자는 개인적으로 그래야 작업하기 편하기 때문이라고 생각한다. 이를 설명하기 위해 하이럼 법칙을 소개한다.
하이럼 법칙은 API 사용자가 충분히 많다면 계약을 어떻게 했는지는 크게 중요하지 않다. 시스템의 모든 관측 가능한 행동은 사용자에 의해 결정된다.
이는 API를 사용하는 사용자가 충분히 많다면 개발자의 설계 의도는 더 이상 중요하지 않다라고 의역할 수 있다.
일부 사용자들이 개발자의 설계 의도대로 API를 사용하지 않기 때문에 API에는 개발자의 초기 의도와는 다른 새로운 책임들이 할당되곤 한다. 이는 명시된 공식적인 책임은 아니나 사용자들이 워낙 많아 만들어진 암묵적인 책임이다.
하이럼은 사용자가 충분히 많아질 때, 이런 현상이 생기는 것을 경험으로 관측하여 언급한 내용이라 하이럼 법칙이라는 이름이 붙었다.
문제는 이렇게 생긴 암묵적인 책임과 부담이 API 개발자의 몫이 된다는 것이다.
몽키 패치를 예시로 들며 사용자가 어디까지 개발자의 의도를 벗어나 사용할 수 있는지를 언급해주셨다.
따라서 모듈 수준의 인터페이스 관리가 필요하다고 할 수 있다. 모듈을 사용한다는 의미가 모듈의 모든 기능을 사용할 수 있게 된다는 의미가 되어서는 안 된다. 모듈의 사용자는 모듈이 책임지는 공개된 일부 기능에만 접근할 수 있어야 한다.
그래야 내부 구현이 변경되어도 모듈의 사용자에게 끼치는 영향을 최소화할 수 있다.

2. 패키지 구조

이번에 다룰 내용은 자바 프로젝트의 패키지 구조에 관한 것으로 모듈과 이유는 직접적으로 연관된 내용은 아니지만 결이 많이 비슷하기 때문이다.
크게 두 가지 방식의 패키지 구조가 일반적인데 하나는 계층 기반 구조, 하나는 도메인 기반 구조다.

1. 계층 기반 구조

계층 기반 구조는 레이어드 아키텍처를 사용하는 프로젝트에서 자주 보이는 패키지 구성 방식으로, 최상단 패키지에 레이어드 아키텍처의 계층을 두고 해당 계층에 대응하는 컴포넌트를 아래에 넣는 방식으로 구성한다.
이는 최상단의 패키지가 곧 레이어드 아키텍처의 계층을 의미한다.
그럼 이러한 구조를 선택했을 때 얻을 수 있는 장단점에 무엇이 있을까?
가장 두드러지는 장점은 이해하기 쉽고 사용하기 쉽다는 점이다.
패키지 구조가 레이어드 아키텍처의 인지 모델을 그대로 따르고 있으므로 각 레이어에 관한 약간의 이해와 이에 대응하는 스프링 컴포넌트가 무엇인지만 알면 누구나 쉽게 개발할 수 있다.
단점 또한 너무 명확하다. 도메인이 눈에 들어오지 않는다는 점이다.
저자는 이를 도메인 관점의 응집도가 떨어져, 비즈니스 코드를 한곳에 모아볼 수 없다고 설명했다.
패키지는 논리적으로 연관된 클래스 파일들을 모아놓는 곳이나 도메인이라는 논리적 연관 관계가 아닌 다른 관계로 파일들을 모아두어 응집도가 떨어진다고 평가했다.
정리
1.
계층 기반 구조는 쉽다.
2.
계층 기반 구조는 도메인이 눈에 들어오지 않는다.
3.
계층 기반 구조는 프로젝트를 구성하는 주요 단위를 계층으로 본다.

2. 도메인 기반 구조

이는 패키지 최상단에 프로그램이 사용하는 도메인이 오도록 한다. 그리고 각 도메인에 대응하는 코드를 이 패키지의 하위 항목에 위치시킨다.
이러한 구성의 가장 큰 장점은 다음과 같다.
도메인 코드를 한곳으로 모음으로써 비즈니스 코드가 여기저기 산재되지 않게 됐다는 것이다.
더불어 프로젝트의 패키지 최상단에 도메인을 드러낸 덕분에 패키지 구조만 보고도 이 프로젝트가 어떤 도메인을 사용하고 있는지 쉽게 알 수 있다.
단점도 분명히 있다. 해당 구조를 채택하면 계층이 명확하게 보이지 않는다는 것이다.
이로 인해 대응되는 스프링 컴포넌트도 눈에 들어오지 않는다.
이를 개선하기 위해 저자는 도메인 아래에 계층 디렉토리를 두어 도메인 - 계층 기반 구조를 제안한다.
도메인 기반 구조(도메인 계층 기반 구조)는 다음과 같은 특징을 갖고 있다.
1.
도메인 기반 구조는 계층 기반 구조보다 복잡하다.
2.
도메인 기반 구조는 도메인이 눈에 들어온다.
3.
도메인 기반 구조는 프로젝트를 구성하는 주요 단위를 도메인으로 본다.

3. 정리

두 패키지 구성 방식은 프로젝트를 구성하는 주요 단위가 다르다. 계층 기반 구조는 계층을 주요 단위로 도메인 기반 구조는 도메인을 프로젝트의 주요 단위로 본다.
하지만 은탄환은 없다. 어느 하나를 선택하는 과정에서 상충관계가 생기며 내가 수행해야 하는 프로젝트의 성격에 맞게 선택할 줄 아는 것이 중요하다.
저자는 다음 두 가지 기준을 바탕으로 선택하는 것을 권장한다.
프로젝트의 규모는 어떤가?
프로젝트를 어떤 방향으로 모듈화하고 싶은가?

3. 패키지와 모듈

이번 장은 저자가 꼭 하고 싶었던 이야기를 한 번 더 강조하는 장이다.
1.
모듈과 모듈 시스템이 무엇인지 알아야 한다.
2.
모듈과 패키지의 차이점을 알아야 한다.
3.
필요하다면 패키지도 모듈성을 추구해야 한다.
둘의 차이점을 알아야 한다면서 패키지도 모듈성을 추구해야한다는 건 어폐가 안 맞아보이나 이는 모듈성이 소프트웨어 개발의 기본 원리이기 때문이다.
시스템의 복잡도를 낮추고 확장성을 높일 수 있다는 것이 검증된 안전하고 확실한 전략이다. 그러니 어떤 대상을 모듈화하겠다라는 생각보단 모듈성을 추구하는 것이 좋다.
패키지를 만들면서도 클래스를 만들면서도 API 서버를 만들면서도 모듈성을 따져보는 것이 좋다.
개발하면서 각 단계마다 의존성과 캡슐화를 한 번씩만 더 신경쓰면 된다. 나머지 작업은 그저 연관된 코드를 모을 뿐이다.