검색 결과가 없습니다

검색어와 일치하는 결과가 없습니다.

원하시는 정보를 찾는 데 도움이 되도록 다음을 시도해 보십시오.

  • 검색에 사용하신 키워드의 철자가 올바른지 확인하십시오.
  • 입력한 키워드에 동의어를 사용하십시오. 예를 들어 “소프트웨어” 대신 “애플리케이션”을 사용해 보십시오.
  • 아래에 표시된 인기 검색어 중 하나를 사용해 보십시오.
  • 새로운 검색을 시작하십시오.
인기 질문

 

Java 9 | 발췌문

Java 9 Modules 이해하기

특성 및 사용법

저자: Paul Deitel


Paul Deitel

Paul Deitel

본 문서는 Java 탄생 이래 가장 중요한 신규 소프트웨어 엔지니어링 기술인 Java 9 Platform Module System(JPMS)을 소개하기 위한 문서입니다. Project Jigsaw를 통해 탄생한 모듈화(modularity) 기능은 모든 레벨의 개발자들이 소프트웨어 시스템, 특히 대규모 시스템을 구축, 유지 관리, 발전시키는 과정에서의 생산성 향상을 지원합니다.

모듈이란?

모듈화 기능을 사용하면 패키지보다 상위 단계의 집합(aggregation)을 추가할 수 있습니다. Java 9에 추가된 신규 핵심 언어 요소는 바로 모듈(module)입니다. 모듈이란 상호 관련성이 높은, 재사용 가능한 패키지 및 리소스(이미지, XML 파일 등)들을 한데 묶어 지칭하는 명칭입니다. 모듈 디스크립터(module descriptor)에는 다음과 같은 사항들에 대한 정보가 담겨 있습니다.

  • 해당 모듈의 이름
  • 해당 모듈의 종속성(해당 모듈이 종속되어 있는 다른 모듈들)
  • 해당 모듈 내 패키지 중 다른 모듈들이 사용 가능하다고 명시적으로 허용한 것(해당 모듈 내 다른 모든 패키지는 다른 모듈들이 사용 불가능하다는 암묵적 의미가 내포되어 있음)
  • 해당 모듈이 제공하는 서비스
  • 해당 모듈이 사용하는 서비스
  • 해당 모듈이 다른 모듈들의리플렉션을 허용하는 요소

역사

Java SE 플랫폼은 1995년 출시되었습니다. 현재 Java SE는 1,000만 명에 달하는 개발자들에 의해 사물인터넷(IoT) 및 임베디드 디바이스와 같이 리소스가 제한된 디바이스를 위한 소규모 앱에서부터 대규모 비즈니스 크리티컬 및 미션 크리티컬 시스템까지 모든 규모의 프로그램 개발에 사용되고 있습니다. 방대한 양의 레거시 코드가 존재하지만, 지금까지의 Java SE 플랫폼은 주로 모놀리식 단일 솔루션이었습니다. 지난 몇 년 동안 Java를 모듈화하기 위한 다양한 노력이 있었지만 널리 사용된 것은 없었고, Java 플랫폼을 모듈화하는 데 사용할 수 있는 것 또한 없었습니다.

Java SE 플랫폼의 모듈화는 여러 해 동안 시도되어 온 난제였습니다. JSR 277: Java Module System은 2005년에 Java 7용으로 처음 제안되었습니다. 이 JSR은 나중에 JSR 376: Java Platform Module System으로 대체되었고 Java 8을 대상으로 했습니다. Java SE 플랫폼은 Java 9에서 마침내 모듈화되었지만, Java 9가 2017년 9월까지 연기된 끝에야 가능해진 성과였습니다.

목표

각 모듈은 그 종속성을 분명하게 명시해야만 합니다.

JSR 376에 수록된 Java SE 플랫폼 모듈화를 위한 주요 목표들은 다음과 같습니다.

  • 신뢰할 수 있는 구성—모듈화는 컴파일 시와 실행 시 모두 인식되는 방식으로 모듈 간의 종속성을 명시적으로 선언할 수 있는 메커니즘을 제공합니다. 시스템은 선언된 종속성을 검토하여 사용자의 앱을 지원하는 데 필요한 모든 모듈들로 구성된 하위 집합을 결정할 수 있습니다.
  • 강력한 캡슐화—모듈 내의 패키지는 모듈이 명시적으로 내보내는 경우에만 다른 모듈에서 액세스할 수 있습니다. 또한 그러한 경우에도 해당 모듈이 다른 모듈의 기능이 필요하다고 명시적으로 선언하지 않는 한 다른 모듈은 해당 모듈의 패키지를 사용할 수 없습니다. 따라서 잠재적 공격자가 액세스할 수 있는 클래스가 줄어들고 그만큼 플랫폼 보안이 향상됩니다. 모듈화는 보다 정돈되고 논리적인 애플리케이션 설계에 기여합니다.
  • 확장 가능한 Java 플랫폼—과거의 Java 플랫폼은 수많은 패키지로 구성되어 있어 개발, 유지보수, 개선에 어려움을 겪었습니다. 서브세팅은 어려운 작업이었습니다. 그러나 이제 마침내 Java 플랫폼이 95개의 모듈로 모듈화되었습니다(모듈 수는 향후 Java의 발전에 따라 달라질 수 있습니다). 각 사용자는 앱 또는 장치에 필요한 모듈로만 구성된 커스텀 런타임을 만들 수 있습니다. 예를 들어 GUI를 지원하지 않는 장치인 경우 GUI 모듈이 포함되지 않은 런타임을 만듦으로써 런타임 크기를 크게 줄일 수 있습니다.
  • 플랫폼 무결성 향상—Java 9 이전에는 개발 중인 앱의 클래스에서 사용할 목적이 아닌 많은 클래스들을 플랫폼 내에서 사용할 수 있었습니다. 그리고 이제 강력한 캡슐화 기능을 통해 그러한 내부 API들을 온전히 캡슐화하여 해당 플랫폼을 사용하는 앱으로부터 숨길 수 있게 되었습니다. 단, 사용자의 앱 코드가 내부 API에 의존하는 경우 레거시 코드를 모듈화된 Java 9로 마이그레이션하는 데에는 문제를 발생시킬 수 있습니다.
  • 성능 향상—JVM은 다양한 최적화 기술을 사용하여 애플리케이션 성능을 향상시킵니다. JSR 376은 필요한 유형이 특정 모듈에만 존재한다는 사실이 미리 알려져 있는 경우 그러한 기술들이 더 효과적이라고 명시하였습니다.

JDK 모듈 목록

Java 9의 중요한 특징은 JDK를 모듈로 나누어 다양한 구성을 지원하는 것입니다. ('JEP 200: 모듈식 JDK'를 참고하세요. 테이블 1에서 모든 Java 모듈화 관련 JEP 및 JSR 목록을 확인할 수 있습니다.) 다음과 같이 JDK의 bin 폴더에서 --list-modules 옵션과 함께 java 명령어를 사용합니다.

java --list-modules

이는 JDK의 모듈 세트를 나열하는 명령어입니다. Java Language SE Specification을 구현하는 표준 모듈(java로 시작하는 이름들), JavaFX 모듈(javafx로 시작하는 이름들), JDK 한정 모듈(jdk로 시작하는 이름들), Oracle 한정 모듈(oracle로 시작하는 이름들) 등이 나열됩니다. 각 모듈명 뒤에는 버전 문자열이 표시됩니다. @9은 해당 모듈이 Java 9에 속함을 나타냅니다.

모듈 선언

앞서 언급한 바와 같이 모듈은 모듈 디스크립터를 제공해야 합니다. 모듈 디스크립터는 각 모듈의 종속성, 특정 모듈이 다른 모듈들이 사용할 수 있도록 제공하는 패키지 등에 대한 구체적인 내용이 담긴 메타데이터입니다. 모듈 디스크립터는 module-info.java라는 이름의 파일 형식으로 정의된, 컴파일된 버전의 모듈 선언입니다. 각 모듈 선언은 다음과 같이 module 키워드로 시작하고, 그 뒤에는 고유 모듈명 및 중괄호로 묶인 모듈 본문이 자리합니다.

모듈 시스템의 핵심 목표는 강력한 캡슐화입니다.

module modulename
}

모듈 선언의 본문은 비어 있을 수도 있고, requires, exports, provides…with, uses, opens 및 기타 다양한 모듈 지시어가 포함될 수도 있습니다(각 지시어에 대한 설명은 하단 참고). 모듈 선언을 컴파일하면 모듈의 루트 폴더에 module-info.class라는 이름의 파일 형식으로 저장되는 모듈 디스크립터가 생성됩니다. 자세한 내용은 하단에서 다시 설명하겠습니다. 다음은 각 모듈 지시어에 대한 간단한 설명입니다. 그 뒤에는 실제 모듈 선언문의 예제를 보여 드리겠습니다.

이후 설명할 exports, module, open, opens, provides, requires, uses, with, 그리고 to, transitive 등의 키워드는 제한적 키워드입니다. 해당 키워드들은 모듈 선언에서만 사용되고, 코드의 다른 부분에서는 식별자로 사용될 수 있습니다.

requires. requires 모듈 지시어는 이 모듈이 다른 모듈에 종속되도록 지정합니다. 이 관계를 모듈 종속성이라고 합니다. 각 모듈은 종속성을 명시적으로 언급해야 합니다. 모듈 A가 requires 모듈 B 하다면, 모듈 A는 read 모듈 B 하고, 모듈 B는 read by 모듈 A 하는 관계입니다. 다른 모듈에 대한 종속성을 지정하려면 다음과 같이 requires를 사용합니다.

requires modulename;

또한 컴파일 시에는 특정 모듈이 필요하지만, 런타임 시에는 선택 사항임을 나타내는 requires static 지시어도 있습니다. 이는 선택적 종속성(optional dependency)으로 명명된 개념으로서 이 소개문에서는 다루지 않습니다.

requires transitive—implied readability. 다른 모듈에 대한 종속성을 지정하고, 사용자의 모듈을 읽는 다른 모든 모듈들도 지정된 종속성을 읽도록 만드는 것을 암시적 가독성(implied readability)이라고 합니다. 암시적 가독성을 확보하기 위해서는 다음과 같이 requires transitive를 사용합니다.

requires transitive modulename;

java.desktop 모듈 선언의 다음 지시어를 참고하세요.

requires transitive java.xml

이 경우 java.desktop을 읽는 모든 모듈들이 암시적으로 java.xml을 읽게 됩니다. 예를 들어, java.desktop 모듈의 메소드가 java.xml 모듈에서 특정 유형을 반환하는 경우, java.desktop을 읽는 모듈의 코드는 java.xml에 종속됩니다. java.desktop의 모듈 선언에 requires transitive 지시어가 없으면, 이러한 종속 모듈은 명시적으로(explicitly) java.xml를 읽지 않는 한 컴파일되지 않습니다.

JSR 379에 따르면 Java SE의 모든 표준 모듈에는 위에 설명된 것과 같은 암시적 가독성을 반드시 부여해야 합니다. 또한 특정 Java SE 표준 모듈은 비표준 모듈에 종속될 수 있지만, 해당하는 비표준 모듈에 암시적 가독성을 부여하는 것은 엄격히 금지됩니다. 이를 통해 Java SE 표준 모듈에만 종속되는 코드를 다수의 Java SE 구현 간에 이식할 수 있습니다.

exports and exports…to. exports 모듈 지시어는 public 유형(및 해당 유형에 중첩된 publicprotected 유형) 모듈의 여러 패키지 중 하나를 다른 모든 모듈의 코드들이 액세스할 수 있도록 지정합니다. exports…to 지시어를 사용하면 내보낸 패키지에 액세스할 수 있는 모듈의 코드를 쉼표로 구분된 목록으로 정확하게 지정할 수 있으며, 이를 적격(qualified) 내보내기라고 합니다.

uses. uses 모듈 지시어는 특정 모듈이 사용하는 서비스를 지정하여 해당 모듈을 서비스 소비자로 만듭니다. service는 인터페이스를 구현하거나 uses 지시어에 지정된 abstract 클래스를 확장하는 클래스의 객체입니다.

provides…with. provides…with 모듈 지시어는 특정 모듈을 서비스 구현 제공자로 지정합니다. 즉, 해당 모듈을 서비스 공급자로 만듭니다. 지시어의 provides 부분은 모듈의 uses 지시어에 나열된 인터페이스 또는 abstract 클래스를 지정하고, 지시어의 with 부분은 인터페이스를 implements하거나 abstract 클래스를 extends하는 서비스 제공자 클래스명을 지정합니다.

open, opens, opens…to. Java 9 이전에는 리플렉션을 사용하여 특정 패키지 내의 모든 유형 및 특정 유형에 해당하는 모든 멤버(private 멤버 포함)에 대해 학습할 수 있었고, 해당 기능의 사용은 사용자의 의지와 관계없이 강제되었습니다. 따라서 진정한 캡슐화는 이루어지지 못했습니다.

모듈 시스템의 핵심 목표는 강력한 캡슐화입니다. 기본적으로 특정 모듈의 특정 유형은 공용 유형 패키지가 아닌 한 다른 모듈에서 액세스할 수 없습니다. 원하는 패키지만 노출 가능합니다. Java 9에서는 리플렉션에도 같은 노출 방식이 적용됩니다.

특정 패키지에 대한 런타임 전용 액세스를 허용하기. 폼(form)의 opens 모듈 지시어는 다음과 같이 사용됩니다.

opens package

이는 특정 packagepublic 유형(및 중첩된 publicprotected 유형)은 런타임 시에만 다른 모듈의 코드에 액세스할 수 있음을 나타냅니다. 또한 지정된 패키지의 모든 유형(및 해당 유형들의 모든 멤버)은 리플렉션을 통해 액세스할 수 있습니다.

특정 패키지에 대한 지정된 모듈들의 런타임 전용 액세스를 허용하기. 폼의 opens…to 모듈 지시어는 다음과 같이 사용됩니다.

opens package to comma-separated-list-of-modules

이는 특정 packagepublic 유형(및 중첩된 publicprotected 유형)은 나열된 모듈들의 코드에 한해, 런타임 시에만 액세스할 수 있음을 나타냅니다. 지정된 패키지 내의 모든 유형(및 해당 유형들의 모든 멤버)은 지정된 모듈 내의 코드에 대한 리플렉션을 통해 액세스할 수 있습니다.

특정 모듈 내의 모든 패키지에 대한 런타임 전용 액세스를 허용하기. 특정 모듈 내의 모든 패키지들이 런타임 시 리플렉션을 통해 다른 모든 모듈들에 의한 액세스를 제공해야 하는 경우 다음과 같이 전체 모듈을 open할 수 있습니다.

open module modulename {
   // module directives

리플렉션 기본값

기본적으로 특정 패키지에 대한 런타임 반사 액세스 권한이 있는 모듈은 해당 패키지의 public 유형(및 중첩된 publicprotected 유형)을 볼 수 있습니다. 그러나 이전 Java 버전에서와 같이, 다른 모듈 내의 코드들은 노출된 패키지의 모든 유형 및 해당하는 유형들 내의 모든 멤버들(setAccessible을 통해 액세스 가능한 private 멤버 포함)에 액세스할 수 있습니다.

setAccessible 및 리플렉션에 대한 자세한 내용은 관련 Oracle 설명서를 참고하세요.


Paul Deitel은 Deitel & Associates의 CEO 겸 Chief Technical Officer입니다. MIT를 졸업한 이래 컴퓨팅 분야에서 35년간의 경력을 쌓아 왔습니다. Paul은 Java Champion이며 22년 이상의 Java 프로그래밍 경험을 보유하고 있습니다. Paul 및 공동 저자인 Dr. Harvey M. Deitel은 세계 최고의 프로그래밍 언어 작가입니다. Paul은 전 세계의 기업, 정부, 학계 고객을 상대로 Java, Android, iOS, C#, C++, C 및 인터넷 프로그래밍 학습 코스를 제공하고 있습니다.


참고: 본 문서는 Java Magazine 2017년 9월/10월호에서 발췌한 것입니다.

자세히 알아보기