[Ch14] 자바 모듈 시스템

자바9에서 주목할만한 새로운 기능은 바로 모듈 시스템이다. 이번 장에서는 모듈 시스템이란 무엇이며, 새로운 자바 모듈 시스템이 어디에 사용될 수 있고, 개발자는 이로부터 어떤 이익을 얻을 수 있는지 알아본다.

추론하기 쉬운 소프트웨어

지금까지 자바8의 람다, 스트림을 통해 이해하기 쉽고 유지보수하기 쉬운 코드를 구현하는 방법을 알아봤다. 하지만 이러한 부분은 저수준의 영역에 해당하며, 궁극적으로 소프트웨어 아키텍처 -> 고수준에서는 생산성을 높일 수 있는 소프트웨어 프로젝트가 필요하다. To do This We need to pay attention to separation of concerns(관심사분리) and information hiding (정보은닉)

관심사 분리

기능을 기준으로 컴퓨터 프로그램을 나누는 동작을 말한다. 사실 우리는 “패키지”를 나눠서 프로그램 기능을 적절히 분리한다. 자바9 의 모듈은 클래스가 어떤 다른 클래스를 볼 수 있는지를 컴파일 시간에 정교하게 제어할 수 있는 것이 특징이다.

관심사 분리는 다음과 같은 장점이 있다.

  • 개별 기능을 따로 작업할 수 있으므로 팀이 쉽게 협업할 수 있다.
  • 개별 부분을 재사용하기 쉽다.
  • 전체 시스템을 쉽게 유지보수할 수 있다.

정보 은닉

세부 구현을 숨기도록 장려하는 원칙이다. 소프트웨어를 개발할 때, 요구사항은 자주 바뀌는데 세부 구현을 숨김으로써 변경이 일어났을 때, 다른 영역에 영향을 미칠 가능성을 줄일 수 있다.

  • 정보 은닉 : 객체에 대한 구체적인 정보를 노출시키지 않도록 하는 기법
  • 캡슐화 : 특정 목적을 위해 데이터와 데이터를 다루는 메서드를 묶어서 추상화하는 것

자바에서는 클래스 내의 컴포넌트에 적절하게 private 키워드를 사용했는지를 기준으로 컴파일러를 이용해 캡슐화를 확인할 수 있다.

자바 소프트웨어

public, protected, private 등의 접근 제한자와 패키지 수준 접근 권한등을 이용해 정보 은닉을 제어했다. 하지만 이런 방식으로는 원치 않는 공개를 해야 하는 등, 한계가 있으므로 자바9의 모듈이 나타났다.

자바 모듈 시스템을 설계한 이유

모듈화의 한계

필자는 잘 느끼지 못했지만, 자바9 이전까지는 모듈화된 소프트웨어 프로젝트를 만드는데 한계가 있었다. 자바는 세가지 수준의 코드 그룹화를 제공한다.

  • 클래스
  • 패키지
  • JAR 클래스와 관련해 자바는 접근 제한자와 캡슐화를 지원했다. 하지만 나머지 패키지와 JAR 수준에서는 캡슐화를 거의 지원하지 못했다.

제한된 가시성 제어

우리가 흔히 쓰는 접근제한자로는 패키지 간의 visibility 를 제어할 수는 없다. 한 패키지 안의 클래스와 인터페이스를 잘 캡슐화 해놓아도, 다른 패키지에서 사용하려면 public 으로 이들을 선언해야 한다.

특히 기본 구현을 제공하는 의미로 “impl” 이라는 문자열을 가진 패키지에서 이런 문제가 두드러진다.

클래스 경로

일반적으로 클래스를 모두 컴파일한 다음 하나의 JAR 파일에 넣고 클래스 경로에 이 JAR 파일을 추가해 사용한다. 그러면 JVM이 동적으로 클래스 경로에 정의된 클래스를 필요할 때 읽는다.

클래스 패스란? 클래스패스란 말 그대로 클래스를 찾기위한 경로이다. 자바에서 클래스패스의 의미도 똑같다. 즉, JVM이 프로그램을 실행할 때, 클래스파일을 찾는 데 기준이 되는 파일 경로를 말하는 것이다. 소스 코드(.java로 끝나는 파일)를 컴파일하면 소스 코드가 “바이트 코드”(바이너리 형태의 .class 파일)로 변환된다. javac -d /export/home/username/util codingsquid.java 출처-코딩하는오징어

이러한 방법은 아래와 같은 단점이 있다.

  • 클래스 경로에는 같은 클래스를 구분하는 버전 개념이 없다. 클래스 경로에 두 가지 버전의 같은 라이브러리가 존재할 때, 어떤 일이 일어날지 예측할 수 없다.
  • 클래스 패스는 명시적인 의존성을 지원하지 않는다. 각각의 JAR 안에 있는 모든 클래스는 classes 라는 한 주머니로 합쳐진다.

이러한 단점은 소프트웨어에서만 발생하는 것이 아니다. JDK 자체도 문젝 ㅏ있다.

거대한 JDK

JDK 는 자바 프로그램을 만들고 실행하는 데 도움을 주는 도구의 집합이다. 모바일 애플리케이션이나 JDK 전부를 필요하지 않는 클라우드에서 덩치가 커진 JDK를 사용하는 것이 문제가 되었다.

따라서 자바8에서는 컴팩트 프로파일 이라는 기법을 제시했는데, 관련 분야에 따라 JDK 라이브러리가 세 가지 프로파일로 나뉘어 각각 다른 메모리 풋프린트를 제공한 것이다. (하지만 이것은 땜질식 처방이다.)

자바 모듈 : 큰 그림

이러한 이유로 자바8은 module 이라는 새로운 자바 프로그램 structure unit 을 제공한다. module-info.java 에 모듈에 관한 정보를 담고 패키지와 같은 폴더에 위치하게 한다.

자바 모듈 시스템으로 애플리케이션 개발하기

책에서는 잘 분리된 기능을 모듈별로 구현하고, 컴파일 하는 방법을 보여준다. 아래와 같은 기능을 하는 애플리케이션을 개발한다고 가정한다.

  • 파일이나 URL 에서 비용 목록을 읽는다.
  • 비용의 문자열 표현을 파싱한다.
  • 통계를 계산한다.
  • 유용한 요약 정보를 표시한다.
  • 각 태스크의 시작, 마무리 지점을 제공한다.

책에서는 다음과 같이 기능(관심사)를 분리했다.

  • 다양한 소스에서 데이터를 읽음(Reader, HttpReader, FileReader)
  • 다양한 포맷으로 구성된 데이터를 파싱 (Parser, JSONParser, ExpenseJSON-Parser)
  • 도메인 객체를 구체화 (Expense)
  • 통계를 계산하고 반환 (SummaryCalculator, SummaryStatistics)
  • 다양한 기능을 분리 조정(ExpensesApplication)

이렇게 분리된 관심사에 따라서 패키지를 그룹화하면 아래와 같다.

  • expenses.readers
  • expenses.readers.http
  • expenses.readers.file
  • exoenses.parsers
  • expenses.parsers.json
  • expenses.model
  • expenses.statistics
  • expenses.application

잘게 분해된 패키지를 어떻게 모듈로 노출할지는 나중에 결정할 수 있다.

생성된 jar 를 모듈화 애플리케이션으로 실행하는 방법은 다음과 같다.

java --module-path expenses-application.jar \
     --module expenses/com.example.expenses.application.ExpenseApplication
  • --module-path : 어떤 모듈을 로드할 수 있는지 지정한다. 이 옵션은 클래스 파일을 지정하는 --classpath 인수와는 다르다.
  • --module : 이 옵션은 실행할 메인 모듈과 클래스를 지정한다.

이 외에도 모듈 디스크립션에 넣는 구문들이 있다.

  • export : 다른 모듈에서 사용할 수 있도록 특정 패키지를 공개 형식으로 만든다.
  • requires : 의존하고 있는 모듈을 지정한다.
  • requires transitive : 다륜 모듈이 제공하는 공개 형식을 한 모듈에서 사용할 수 있다고 지정할 수 있다. transfer 할 수 있다는 뜻
  • exports to : 사용자에게 공개할 기능을 제한함으로 가시성을 좀 더 정교하게 제어할 수 있다.
  • open, opens : 모든 패키지를 다른 모듈에 반사적으로 접근을 허용할 수 있다.

Conclusion

자바9에서는 모듈 개념이 추가되었고, 이는 완벽한 캡슐화를 위한 방법이다. 일반 jar 파일은 자동 모듈이라고 불리우는데, 암묵적으로 모든 패키지를 공개한다. 개발자 입장에서는 root cause 를 파악하기 위해 패키지에 있는 내용이 공개되고 (디버깅 등을 위해) 소스를 이해하는게 더 낫겠다는 생각은 들지만, 궁극적으로는 안전한 소프트웨어를 만드려면 모듈로 패키지를 제한하는 것이 맞아보인다.

또한, 책에서 관심사를 분리하는 과정, 그리고 그에 따른 패키지화는 인상 깊다. API를 만들때, 기능별 관심사를 분리해서 정교화된 패키징을 해봐야겠다.

댓글남기기