자바 세상의 본격적으로 '함수형 프로그래밍'의 시작을 알린 '자바8'이 출시 된지도 꽤 많은 시간이 지났다. 급기야 '자바10'까지 발표되었으니, Oracle이 Sun을 인수하면서 진화론적으로 정체되어 있던 자바에 꽤나 공을 들이고 있는 모양세다.
자바10의 Spec을 대충 훑어보니, 지역변수의 타입추론 기능과 G1 GC가 최적화 되는 등 소문대로 Minor한 변화가 이루어 진 것 같다. (여기서 Minor 라는 것은 '패러다임의 변화' 수준이 아니라는 말이다. 오해 ㄴㄴ) 이 시점에서 나의 눈에 들어온 것이 지금 리뷰하려고 하는 'Java9 모듈 프로그래밍' 이라는 책이었다.
나는 기술서적을 고를때 대략 3가지 정도를 고려하는데, (물론 Yes24의 별점과 리뷰를 보는건 기본) 첫번째로 책의 디자인(...), 두번째로는 저자가 누구인지, 세번째로는 역자가 '테크 트랜스 그룹 T4'가 아닌지 이다. 세번째 이유는 해외에서 극찬을 받고 있는 'Spring In Action' 이라는 책을 해당 번역팀(?)이 번역한 역서를 읽다가, 메서드를 '방법'으로, 오버라이딩을 '과적(...)'으로 번역한 부분을 읽고 책을 던져 버릴 뻔 했어서다. 그래도 내용 자체는 좋으니 완독은 했다...
아무튼간, 조금 더 최신의 자바에 대해 알고 싶었고, 위의 3가지 조건을 만족 했으며, 자바9의 가장 큰 변화라고 할 수 있는 '모듈'을 다뤘기에 해당 책을 구입하여 2회독을 한 뒤에야 이렇게 리뷰를 남기게 되었다. (특히나 번역이 너무나 매끄럽고 좋았다. 일면식도 없지만, 이 책을 번역해주신 역자께 감사드린다)
이 책은 타이틀이 모든것을 말해준다.
"모듈 프로그래밍"
소프트웨어 개발에서 '모듈'이라는 용어는 매우 광범위하게 사용된다. 이는 크게 볼때 '시스템 아키텍쳐 관점에서의 컴포넌트', 작게는 '캡슐화된 클래스'가 될 수 있을 것이다. 하지만 이 책에서 말하는 '모듈'이라는 것은 캡슐화된 클래스의 모음, 즉 '라이브러리'에 가깝지만 '더욱 더 강력한 캡슐화가 이루어진 라이브러리'라고 할 수있다. 여기서 말하는 '더욱더 강력한 캡슐화'라는 것은 글을 써 내려가면서 언급하려고 한다.
저자는 책의 도입부에서 기존 자바를 사용할 때 만날 수 있는 2가지 문제점을 실용적이고 실감나는 사례를 들며 풀어 나간다. 여기서 2가지 문제점이란 바로 '완전하지 않은 캡슐화'와 '빌드된 바이너리의 (혹시 모를) 불안정성'이다.
'완전하지 않은 캡슐화'라는 것은 무엇인가? 더 정확히 말하면 '라이브러리 외부'로 부터의 완전하지 않은 캡슐화이다. 기존의 자바에서는 '접근 지정자(또는 제어자)' 키워드를 이용해 '캡슐화'를 실천한다. 하지만 '접근 지정자'로 할 수 있는 캡슐화의 범위는 멤버 필드, 그리고 메서드뿐이다. 저자는 바로 이 점을 문제 삼는다. 여기서 문제란 라이브러리 외부로부터 오는 '타입' 그 자체에 대한 접근을 막을 수 없다는 것이다. 타입 자체를 캡슐화 하려면 패키지 단위로 해당 타입을 묶어야 하는데, 이것은 해당 타입을 필요로 하는 라이브러리 내부의 클래스 조차 접근하지 못하게 만드는 설계의 한계를 가져오고 만다.
'빌드된 바이너리의 불안정성'은 바로 'NoClassDefFoundError' 오류로 한번에 설명이 된다. 물론 이 오류를 실무에서 보는 것은 아주 드물다. 하지만 그렇다고 해서 빌드 도구인 Gradle이나 Maven이 필요한 모든 Compiled Class들이 포함된 바이너리를 빌드 해 준다는 '보장'은 없다. 개발자의 실수로 Runtime에 필요한 라이브러리에 대해 빌드 스크립트에서 의존성을 'Test' 범위로 지정해 버린다거나, 빌드 도구의 실수(버그나 오류)로 몇몇 필수적인 Compiled Class가 누락될 수도 있다.
더 나은 캡슐화
자바9는 뿌리까지 바뀌었다. 프로젝트 'Jigsaw'를 통해 자바 플랫폼 API 자체가 모듈 프로그래밍으로 재작성 되었고, 이제 자바 프로그램은 60메가 바이트에 달하는 'rt.jar' 를 통째로 포함하여 빌드할 필요없이 꼭 필요한 플랫폼 모듈만 포함하여 빌드 될 수 있다.
또한 자바 응용 프로그램 개발자는 자바 프로젝트의 전통적인 소스 디렉토리 구조(src/main/java 와 같은)가 아닌, 'src' 디렉토리 하위에 모듈 별로 'root 디렉토리'를 생성하여 모듈 코드를 작성하게 된다. 그리고 각 root 디렉토리에는 'module-info.java'라는 특수한 이름을 가진 모듈 기술자(module descriptor)파일을 작성하게 되는데, 이 파일에 'requires' 키워드를 이용하여 타 모듈에 대한 의존성 설정을 하고, 'exports' 키워드를 이용하여 현재 모듈이 어떤 패키지를 외부에 노출(export)할 것인지를 정의하게 된다.
따라서 라이브러리 외부의 프로그램은, 자신이 사용하는 라이브러리의 모듈에서 'export' 하는 패키지의 'public' 클래스만을 접근할 수 있게 된다. 결론적으로 기존 접근 지정자 사용에 '모듈 시스템'이 더해져 더욱 더 꽁꽁 감싸여진, 앞서말한 더욱 더 강력한 캡슐화가 가능해진 것이다.
신뢰성 있는 빌드
어찌 되었든 JRE가 누락된 Compiled Class에 대해서 어느 시점에 로딩을 시도하여 'NoClassDefFoundError' 오류가 발생되는 것을 목도하지 않는 이상, 그 전에 모든 필요한 Compiled Class들이 바이너리에 안정적으로 포함되어 있는가를 확인할 수 있는 방법은 사실상 없다. (빌드된 바이너리를 decompile하여 import문에 정의된 클래스들이 잘 포함되어 있는지 일일히 확인하는 정신나간 사람은 없을 것이다) 이러한 문제에 대해 '모듈 시스템'은 어떤 솔루션을 제시 할까?
자바9 '모듈 시스템'을 이용하여 작성된 바이너리를 구동할 때, 바로 '모듈 기술자'를 통해 해당 기술자에 명시된 모든 의존성을 확인한다. 여기서 중요한점은, 의존성을 확인하는 시점이 프로그램을 구동하여 자바 어플리케이션이 '초기화' 될 때 라는 것이다.
이것은 우리에게 빌드된 바이너리에 대한 'Fail-Fast'를 제공한다. 특정 기능을 수행하지 않고는 확인할 길이 막막한 'NoClassDefFoundError'와 같은 Runtime 오류를 프로그램 구동시에 바로 잡아주어, 해당 오류에 대한 대처를 빠르게 할 수 있게하고, 그것을 통해 빌드된 바이너리의 신뢰성을 더할 수 있다는 것이다. 이는 '모듈 시스템'의 매우 중요하고 강력한 장점이라고 할 수 있다.
. . .
지금 까지 이야기한 2가지 담론이 이 책을 이루는 뼈대라고 할 수 있다. 이 책에서는 이러한 뼈대를 바탕으로 독자로 하여금 간단한 '주소록' 프로그램을 모듈을 사용하여 예제로 작성하게 하고, 책을 읽어 가는 동안 점점 발전 시키며 '모듈 시스템'의 여러 기능들을 소개 하는데,
- 자신이 의존하는 모듈의 의존성을 암시적으로 포함하는 '전이적 의존성'
- 자신의 특정 패키지를 특정 모듈에만 export하는 '제한적 export'
- 여러 모듈의 기능을 모아주는 '집합자 모듈'
- 모듈간 loose coupling을 위한 간접 계층인 '서비스'
등의 기능들이 그것이다
또한 다양한 모듈 설계 패턴, 링킹을 통해 어떻게 모듈이 해석되고 jar가 아카이빙 되는지, 또 자바9 이전의 레거시 버전에서 어떻게 마이그레이션 하는지에 대한 내용이 이어지는데 설명하는 수준이 깊지만 이해하기가 어렵지 않았다. (언제가 될진 모르겠지만) 자바9로 마이그레이션 하게 된다면 유용한 참고 자료가 될 것 같다.
. . .
리뷰를 마치며
자바9의 '모듈 시스템'은 플랫폼 자체를 재작성할 정도로 너무나 큰 변화다. 따라서 실제 운영되고 있는 서비스에 적용하기에는 매우 높은 비용과 시간이 걸릴 것 같다. 사실, 서비스 레벨의 자바 프로그램에서 '모듈'이라는 것은 그 필요성이 간절하지도 않아 보인다. 어찌 되었든, 자바의 신선한 변화를 심도있게 엿볼 수 있었기에 매우 좋은 책을 읽은 것은 확실하다. (자바가 기존의 동적 언어들과 뭔가 비슷해져 가고 있는건 기분탓일까..)