DI Container 와 CustomClassLoader 의 관계

진성대·2025년 2월 27일
0

JAVA

목록 보기
3/4

DI 컨테이너의 기본 개념 및 역할

의존성 주입 컨테이너(Dependency Injection Container)는 객체의 생성과 연결(의존성 주입)을 관리하는 구조적 컴포넌트입니다. 애플리케이션이 필요한 객체(컴포넌트 또는 빈)를 직접 생성하는 대신, DI 컨테이너가 객체를 생성하고 서로 연결해 줍니다. 이를 통해 객체 간 결합도를 낮추고 제어의 역전(IoC)을 구현합니다. 예를 들어 객체 A가 객체 B를 필요로 할 때, A 내부에서 B를 생성하는 대신 외부 컨테이너가 B를 만들어 생성자나 Setter 등을 통해 A에 전달합니다.이렇게 하면 코드 수정 없이도 구성 변경이나 테스트용 대역 객체 주입이 가능해집니다.

DI 컨테이너는 빈(bean)들의 생명주기와 의존 관계를 관리하는 역할을 합니다. 컨테이너는 설정(예: 어노테이션이나 설정 파일)에 따라 필요한 클래스의 인스턴스를 생성하고, 각 빈이 필요로 하는 다른 빈들을 주입해줍니다. 또한 빈을 싱글톤 등 특정 스코프로 관리하고, 초기화/소멸 콜백 처리 등 부가 기능을 제공할 수 있습니다. 스프링(Spring)의 BeanFactory나 ApplicationContext가 대표적인 DI 컨테이너로, 객체 등록, 생성, 조회, 주입 등을 수행하며 트랜잭션 관리, AOP 프록시 적용 등의 부가 기능도 제공합니다.

요약하면, DI 컨테이너는 애플리케이션 구성 요소를 효과적으로 생성하고 연결하여 관리함으로써, 개발자가 객체 간 의존성을 직접 처리하지 않아도 되도록 돕는 인프라입니다.


CustomClassLoader(사용자 정의 클래스 로더)의 동작 원리

자바의 클래스 로더(ClassLoader)는 JVM에서 클래스 파일(.class)을 메모리로 읽어들여 Class 객체로 만드는 역할을 합니다. 자바 클래스 로딩은 기본적으로 계층적 구조와 위임 모델로 동작합니다. 계층적으로, 부트스트랩 로더(최상위, JVM 핵심 클래스 로딩) → 확장 클래스 로더(JDK 확장 라이브러리 로딩) → 시스템 클래스 로더(애플리케이션 클래스 로딩) 순으로 구성되며, 최종적으로 개발자가 만든 클래스들은 시스템 클래스 로더가 담당합니다. 위임 모델에서는 클래스 로더가 클래스를 로딩할 때 자기 자신보다 상위 클래스 로더에 먼저 문의하여 클래스를 찾는 방식(Parent-First)을 따릅니다. 예를 들어 시스템 클래스 로더가 클래스를 로딩할 때, 부트스트랩 → 확장 → 시스템 순으로 위임하여 검색하고, 어디에도 없으면 ClassNotFoundException을 발생시킵니다. 이로써 상위 로더에 이미 로드된 클래스가 있으면 하위에서 중복 로드하지 않아 일관성을 유지하고, 중요 클래스들이 하위 로더에 의해 임의로 재정의되는 것을 방지합니다.

사용자 정의 클래스 로더(Custom ClassLoader)는 이러한 ClassLoader를 개발자가 상속 구현하여 특수한 방식으로 클래스 로딩을 수행하는 것입니다. 예를 들어, 파일 시스템이나 네트워크, DB 등 특정 경로나 매체에서 클래스를 가져와 로딩하거나, 바이트코드를 동적으로 생성하여 로딩하는 시나리오에서 CustomClassLoader를 구현합니다. CustomClassLoader는 기본 ClassLoader의 findClass() 등을 오버라이드하여 원하는 위치에서 .class를 찾고, defineClass()를 통해 클래스 바이트코드를 JVM에 정의합니다. 기본 동작은 유지하되, 필요에 따라 부모 위임 모델을 변경할 수도 있습니다 (OSGi 등 일부 프레임워크에서는 부모 위임 대신 자식 우선 또는 혼합 모델을 사용하기도 합니다).

CustomClassLoader를 사용하면 여러 모듈로 구성된 시스템의 유연한 클래스 관리가 가능합니다. 서로 다른 클래스 로더를 모듈별로 사용하면 모듈 경계를 명확히하여 클래스 충돌을 피하고(동일 이름 클래스라도 로더가 다르면 별개로 관리) 버전이 다른 라이브러리도 동시에 로드할 수 있습니다. 또한 필요 없는 모듈의 클래스 로더를 해제(unload)하면 해당 클래스들을 메모리에서 제거해 메모리 관리에 도움이 됩니다. CustomClassLoader는 아무 위치에서나 클래스 로드가 가능하게 하므로, 애플리케이션이 실행 중에 동적으로 새로운 클래스나 자원을 추가 로드하거나, 변경된 클래스를 재로딩(핫스왑)하는 것도 가능해집니다. 이런 유연성 덕분에 플러그인 시스템, 스크립팅 엔진, 애플리케이션 서버의 모듈 관리 등에서 CustomClassLoader가 적극 활용됩니다.


DI 컨테이너에서 CustomClassLoader가 필요한 이유 및 활용 사례

일반적인 DI 컨테이너 동작에도 클래스 로더는 필수적입니다. 컨테이너는 애플리케이션의 클래스들을 로딩하고, 리플렉션을 통해 어노테이션이나 인터페이스를 검사하여 빈으로 등록하거나 의존성을 주입합니다. 이때 어떤 클래스 로더로 클래스를 불러올지가 중요합니다. 대개 컨테이너는 애플리케이션이 구동되는 현재 클래스패스에 대한 클래스 로더(예: 스프링에서는 기본적으로 Thread Context ClassLoader나 ApplicationContext에 설정된 ClassLoader)를 사용해 클래스를 로드합니다.

컴포넌트 스캔(Component Scan) 과정은 DI 컨테이너와 클래스 로더 관계를 잘 보여주는 예입니다. 스프링 프레임워크에서 컴포넌트 스캔을 수행하면 클래스패스 상의 클래스들을 훑어보면서 특정 애노테이션(@Component, @Service 등)이 붙은 클래스를 찾습니다. 이 작업은 클래스 로더를 활용한 클래스 탐색으로 볼 수 있는데, 지정한 패키지 경로를 기준으로 클래스 로더가 접근할 수 있는 .class들을 검색하고 메타데이터를 확인합니다. 발견된 클래스들은 컨테이너가 Class.forName 등을 통해 로드한 뒤 빈으로 등록하고, 필요한 경우 의존성 주입까지 수행합니다. 이러한 런타임 클래스 탐색 및 로딩 기능을 위해 클래스 로더가 활용되며, 특히 애플리케이션 규모가 클 경우 광범위한 패키지 스캔은 많은 클래스를 로딩/검사하므로 시작 시간이 오래 걸릴 수 있습니다. 이는 DI 컨테이너가 클래스 로더를 통해 애플리케이션 클래스들을 다수 로딩하고 검사하기 때문에 발생하는 현상입니다.

DI 컨테이너에서 CustomClassLoader가 특히 유용한 이유는 동적 확장과 모듈 격리 때문입니다. 예를 들어, 애플리케이션이 플러그인 시스템을 제공하여 런타임에 새로운 모듈(플러그인)을 추가/제거 할 수 있다고 가정해봅시다. 컨테이너 입장에서는 새 플러그인의 클래스들을 로딩하고 빈으로 등록해야 하는데, 이때 기존 클래스패스에는 없던 외부 JAR에서 클래스를 읽어와야 합니다. 이 경우 URLClassLoader 등의 CustomClassLoader를 사용하여 외부 경로의 클래스를 로드하고, 해당 클래스들로 구성된 빈들을 컨테이너에 추가 등록할 수 있습니다. 이렇게 하면 애플리케이션 재시작 없이도 새로운 기능 모듈을 주입할 수 있습니다. 또한 각각의 플러그인을 별도 클래스 로더로 로드하면, 플러그인 간에 동일한 클래스 이름이나 라이브러리가 있더라도 충돌하지 않고 독립적으로 동작시킬 수 있습니다. 플러그인 제거 시에는 그 클래스 로더를 해제함으로써 관련 클래스와 객체들을 메모리에서 깔끔히 정리(unload)할 수도 있습니다.

의존성 충돌 방지 및 버전 관리도 CustomClassLoader 활용 사례입니다. DI 컨테이너는 여러 서드파티 라이브러리를 빈으로 관리하는데, 경우에 따라 서로 다른 모듈이 동일한 라이브러리의 다른 버전을 필요로 할 수 있습니다. 한 JVM 내에서 동일 클래스 이름의 버전을 동시에 쓰려면 클래스 로더 격리가 필요합니다. CustomClassLoader를 통해 모듈별로 클래스풀이 분리되면 각 모듈은 자신에게 알맞은 버전의 의존성을 주입받을 수 있습니다. 이는 대형 애플리케이션이나 서버 환경(예: OSGi 컨테이너, Java EE 애플리케이션 서버)에서 버전 충돌 문제를 우회하는 핵심 기법입니다.

또 다른 활용은 클래스 변조 및 프록시 생성입니다. DI 컨테이너는 AOP를 위해 프록시 객체를 생성하거나, 리플렉션 성능 향상을 위해 바이트코드를 동적으로 만들어 클래스를 정의하는 경우가 있습니다. 예컨대 스프링 AOP는 CGLIB을 통해 프록시 클래스를 런타임에 생성하는데, 이때 생성된 클래스는 기본적으로 애플리케이션 클래스 로더에 정의됩니다. 특별한 경우가 아니라면 기본 로더를 쓰지만, 만약 클래스 로딩 시점에 바이트코드 조작(로드타임 위빙)이 필요하다면 ClassLoader를 상속한 InstrumentingClassLoader 등을 사용하여 클래스 바이트코드를 수정한 후 로드할 수 있습니다. 이러한 기법을 통해 DI 컨테이너는 트랜잭션 경계 설정이나 로깅 AOP 등을 투명하게 적용하는데, CustomClassLoader가 이러한 프록시/위빙 기능을 실현하는 수단이 됩니다.


Spring, Guice 등 주요 DI 프레임워크와 CustomClassLoader의 관계

스프링(Spring) 프레임워크는 DI 컨테이너의 대표격이며, 내부적으로 클래스 로더를 활용하는 여러 지점이 있습니다. 우선 빈 로딩과 초기화 과정에서 스프링은 특정 클래스 로더를 사용하여 애플리케이션 클래스를 로딩합니다. 일반적으로는 스레드 Context ClassLoader를 사용하지만, 필요에 따라 ClassLoader를 지정할 수도 있습니다 (예: ClassPathXmlApplicationContext 생성자에 클래스 로더를 지정). 스프링 빈 중에는 BeanClassLoaderAware 인터페이스를 구현하면 컨테이너가 해당 빈을 로딩한 클래스 로더 참조를 주입해주기도 합니다. 이는 빈이 동적으로 리소스를 로드하거나 클래스 생성이 필요할 때 올바른 클래스 로더를 사용하도록 돕습니다.

스프링에서 CustomClassLoader가 두드러지게 등장하는 사례는 모듈식 환경에서입니다. 예를 들어 OSGi 환경에서 스프링을 사용할 경우, 각 번들은 자체 클래스 로더를 갖고 있어 번들 간 클래스 접근이 제한됩니다. 이때 스프링은 특수한 클래스 로더를 사용해 이 문제를 해결합니다. OSGi에서 한 번들의 서비스를 다른 번들에서 주입받을 때, 스프링은 서비스 객체를 프록시로 감싸는데, 이 프록시 클래스 생성 시 두 번들의 클래스 로더를 모두 연결한 커스텀 로더를 사용합니다. 즉, 클라이언트 번들의 로더와 스프링 번들의 로더를 체인으로 묶어, 생성되는 프록시 클래스가 양쪽의 클래스를 모두 볼 수 있게 합니다. 이렇게 하면 프록시가 원본 서비스 인터페이스(클라이언트 번들의 클래스)와 스프링이 추가한 믹스인 인터페이스(스프링 번들의 클래스)를 모두 참조할 수 있어, 모듈 경계를 넘나드는 DI가 가능해집니다. 스프링의 OSGi 지원 모듈(Spring Dynamic Modules 등)이 내부적으로 이러한 ClassLoader 트릭을 활용하여, OSGi 번들 간의 의존성 주입을 구현합니다. (다만 OSGi 환경에서도 완벽한 투명성을 위해 패키지 Import/Export 설정 등 추가 조치가 필요한 경우도 있습니다.)

Spring 이외에도 Google Guice 역시 클래스 로더와 밀접한 연관이 있습니다. Guice는 경량 DI 프레임워크로, 런타임 코드 생성을 통한 최적화와 AOPInterceptor 구현 등을 제공합니다. Guice는 이러한 프록시/인터셉터용 클래스 생성 시 독자적인 클래스 로딩 전략을 사용합니다. 특히 OSGi와의 통합 측면에서, Guice는 Bridge ClassLoader라는 개념을 도입했습니다. Guice가 어떤 클래스에 대한 프록시를 생성할 때, 여러 클래스 로더를 관여시켜 프록시 클래스의 가시성 문제와 메모리 누수를 해결합니다. 구체적으로, Guice가 프록시 등을 생성할 때 고려하는 로더는 (1) 대상 클래스의 로더 (프록시를 만들 원본 클래스가 속한 모듈의 ClassLoader), (2) Guice 자체의 로더 (Guice 프레임워크가 속한 ClassLoader), (3) 브리지 로더입니다. 브리지 클래스로더는 대상 클래스 로더의 자식으로 만들어지며, 사용자 클래스에 대한 요청은 대상 로더에 위임하고 Guice 내부 지원 클래스에 대한 요청은 Guice 로더로 위임하는 이중 위임 구조를 갖습니다. 결국 브리지 로더가 생성한 프록시 클래스는 사용자 번들의 클래스도 볼 수 있고 Guice 프레임워크 클래스도 볼 수 있으므로, OSGi 같은 환경에서도 문제가 없습니다. Guice는 기본적으로 이 브리지 로더를 사용하지만, 시스템 속성으로 끌 수 있게도 해두었는데(guice_custom_class_loading=OFF), 이 경우 OSGi 환경에서는 일부 프록시가 동작하지 않을 수 있다고 명시합니다. 이는 모듈식 ClassLoader 경계를 넘는 DI를 위해 Guice가 커스텀 로더를 사용한 좋은 예시입니다.

한편, Jakarta CDI (예: Weld)나 기타 JEE 컨테이너의 DI 구현들도 애플리케이션 서버의 클래스 로딩 구조와 조화를 이루도록 설계되었습니다. 예를 들어, Java EE 서버들은 WAR/EAR 모듈별로 클래스 로더를 격리하고 상하위 계층을 두는데, CDI 컨테이너는 일반적으로 각 배포 단위(WAR 등)의 클래스 로더 범위에서 동작하며, 필요 시 상위 모듈의 빈 주입은 프로바이더 매커니즘이나 JNDI를 통해 이루어집니다. 과거 JBoss AS 3.x에서는 Unified ClassLoader라는 방식으로 모든 배포 모듈이 하나의 네임스페이스처럼 클래스 공유를 했지만, JBoss 4.x부터 모듈별 클래스 로더(Hierarchical ClassLoader)로 변경되면서 DI 프레임워크도 각 모듈 경계를 존중하게 되었습니다. 이처럼 스프링, Guice 등 DI 프레임워크들은 일반 환경에서는 기본 클래스 로더로 충분히 동작하지만, OSGi나 애플리케이션 서버 등 모듈식 환경에서는 CustomClassLoader 기법을 사용하여 모듈 경계를 넘나드는 의존성 주입, 여러 버전 공존, 프록시 클래스 가시성 확보 등을 실현하고 있습니다.


ClassLoader 기반 DI 프레임워크 확장과 모듈 시스템 연관성

ClassLoader를 기반으로 한 DI 확장은 주로 모듈화(modularity)된 시스템에서 중요하게 대두됩니다. 대표적인 모듈 시스템으로 OSGi 프레임워크와 Java 플랫폼 모듈 시스템(JPMS)을 들 수 있습니다. 이러한 시스템에서는 애플리케이션을 여러 모듈로 분리하고, 각 모듈마다 격리된 클래스 로더 또는 모듈 로더를 사용하여 클래스를 관리합니다. DI 컨테이너를 모듈 시스템과 통합하려면, 컨테이너가 각 모듈별 클래스 로더 환경을 이해하고 그 경계를 존중하면서 빈을 주입하거나 서비스를 공유해야 합니다.OSGi의 경우, 각 번들이 독립적인 클래스 로더 네임스페이스를 가지며 명시적으로 import/export한 패키지만 서로 접근이 가능합니다. 이런 환경에서 전통적인 DI 컨테이너를 그대로 사용하면 다른 번들의 클래스를 찾지 못하거나(NoClassDefFoundError), 프록시 생성 시 인터페이스를 못 보는 문제가 발생합니다. 앞서 언급했듯이, Spring DM(Spring Dynamic Modules)이나 Aries Blueprint 같은

OSGi용 DI 솔루션들은 번들 경계를 넘는 서비스 주입을 위해 커스텀 클래스 로더나 OSGi가 제공하는 BundleContext를 활용합니다. 스프링은 프록시 생성 시 클라이언트 번들의 로더 + 스프링 번들의 로더를 결합한 클래스 로더를 사용했고, Guice도 브리지 로더로 OSGi 번들 간 클래스를 잇는 방식을 취했습니다. 또한 Guice의 브리지 로더는 생명주기 관리에도 이점이 있었는데, 주입기(Injector)별로 생성된 브리지 로더를 폐기함으로써 생성한 프록시 클래스도 GC가 가능하도록 한 것입니다. 이는 OSGi처럼 장기간 실행되는 애플리케이션에서 메모리 누수를 방지하기 위한 전략입니다. 요컨대, 모듈식 시스템에서는 DI 컨테이너가 모듈 경계(= 클래스 로더 경계)를 인지하고, 필요한 경우 특수한 클래스 로더 전략을 취함으로써 모듈 간 의존 객체 주입이나 동적 모듈 추가/제거를 안정적으로 지원합니다.

한편, Java 9+ JPMS(자바 모듈 시스템)의 등장으로 클래스 로더보다는 모듈 단위의 접근 제한이 도입되었습니다. JPMS에서는 동일 클래스 로더 내에서도 모듈 규칙에 따라 패키지 접근이 제한되는데, 리플렉션으로부터의 보호를 위해 opens 지시자가 필요합니다. DI 프레임워크들은 JPMS 환경에서 동작하려면, 주입 대상 모듈이 자신의 패키지를 컨테이너(예: Spring 프레임워크 모듈)에게 opens로 열어주어야 합니다. 이는 클래스 로더 수준보다는 모듈 메타데이터 수준의 이슈지만, 맥락상 모듈 시스템과 DI의 관계에서 언급할 부분입니다. 클래스 로더 관점에서는, JPMS에서도 각 ModuleLayer가 클래스 로더를 관리하며, 동적 모듈 로딩 시 Layer와 연동된 클래스 로더를 통해 클래스를 로드합니다. 미래의 DI 컨테이너 확장은 JPMS Layer를 이용하여 플러그인 모듈을 동적으로 로딩하고 주입하는 방향도 고려되고 있습니다. 결국, 전통적인 ClassLoader 기반 모듈 시스템(OSGi 등)이나 최신 JPMS 모두에서 DI 컨테이너는 모듈화 환경에 적응하기 위해 클래스 로더 전략을 조율해야 합니다.


CustomClassLoader를 활용한 동적 의존성 주입 및 플러그인 시스템 구현

동적 의존성 주입이란 애플리케이션 실행 중에 새로운 구성 요소를 추가하거나 변경된 구성을 주입하는 것을 의미합니다. 이러한 유연함을 실현하는 대표적 방법이 플러그인 아키텍처입니다. Java 애플리케이션에서 플러그인 아키텍처를 구현하려면, CustomClassLoader를 통한 플러그인 로딩이 핵심 요소가 됩니다.

플러그인 시스템을 간단히 설명하면, 메인 애플리케이션과는 별도로 배포된 JAR(플러그인)을 실행 시 동적으로 불러와서, 그 안에 정의된 확장 포인트(예: 인터페이스 구현체들)를 애플리케이션에 등록해 사용하는 것입니다. 이때 각 플러그인 JAR을 격리시키면서 로드하려면, 자바에서는 일반적으로 URLClassLoader를 플러그인별로 생성하여 해당 JAR의 경로를 지정하고 로드합니다. DI 컨테이너와 결합할 경우, 플러그인 JAR에서 특정 인터페이스의 구현 빈들을 찾아 주입하거나, 플러그인 자체를 하나의 하위 DI 컨테이너로 취급하여 메인 컨테이너와 연동할 수 있습니다.

예를 들어, 메인 애플리케이션이 가지는 DI 컨테이너(A)가 있고, 새로운 플러그인 모듈을 로드하면 그 모듈 전용 DI 컨테이너(B)를 생성한다고 합시다. B의 클래스 로더는 해당 플러그인 JAR에 특화된 CustomClassLoader이고, A의 클래스 로더는 기본 애플리케이션 로더입니다. 플러그인 컨테이너 B를 만들 때 A를 부모 컨테이너로 두면, 플러그인에서도 메인 빈들을 참조 가능하게 하거나(필요 시) 설정을 상속할 수 있습니다. 이렇게 하면 플러그인 코드에서는 평소와 동일하게 의존성 주입을 활용하지만, 실제 클래스 로딩은 플러그인별 클래스 로더가 담당하여 메인과 격리됩니다. Guice 등 일부 DI 프레임워크에서는 다중 Injector 구성을 통해 이러한 부모-자식 컨테이너 관계를 구현할 수 있으며, 각 Injector가 사용하는 클래스 로더를 분리하여 지정할 수 있습니다. 스프링도 ApplicationContext를 계층적으로 구성할 수 있어, 플러그인마다 독립된 ApplicationContext(및 ClassLoader)를 가질 수 있습니다.

동적 플러그인 주입의 한 가지 실용 예로, ServiceLoader와 DI를 조합하는 방식을 들 수 있습니다. 자바 표준 ServiceLoader는 클래스패스에서 특정 인터페이스 구현체를 자동 발견해주는 메커니즘인데, CustomClassLoader를 ServiceLoader에 전달하면 플러그인 JAR 내부의 서비스 구현체들을 얻어올 수 있습니다. DI 컨테이너는 이렇게 발견된 구현체를 빈으로 등록하거나, 해당 구현체를 포함한 모듈 전용 설정을 읽어들여 의존성을 주입해줄 수 있습니다. 예컨대, 어떤 인터페이스 PluginService가 있고 플러그인 JAR들이 각자 구현체를 제공한다면, 메인 프로그램은 런타임에 각 JAR를 CustomClassLoader로 불러와 ServiceLoader로 PluginService 구현을 얻습니다. 그런 다음 Guice의 모듈을 설치하거나(Spring이라면 @Bean 등록) 해서 플러그인의 PluginService 구현을 애플리케이션에 주입된 다른 컴포넌트들과 연결할 수 있습니다.

플러그인 제거 또는 교체 역시 CustomClassLoader 덕분에 비교적 수월해집니다. 플러그인 클래스들이 별도 로더에 로드되어 있으면, 그 로더를 가비지 컬렉션 가능하도록 언로드(unload)하면 메모리에서 깨끗이 제거됩니다. 이는 플러그인 업그레이드 시 구 버전 클래스를 잔존시키지 않고 치환할 수 있게 해주며, 메모리 누수 없이 장시간 시스템을 운용하게 돕습니다. 실무적으로는 완전히 언로드하려면 해당 클래스 로더를 참조하는 객체(특히 DI 컨테이너의 빈들 등)를 모두 정리해야 하지만, 잘 관리된다면 애플리케이션 재시작 없이도 구성 요소의 유연한 추가/제거를 구현할 수 있습니다.

마지막으로, CustomClassLoader를 이용한 동적 DI는 애플리케이션 서버의 핫디플로이(Hot-deploy)와도 통하는 개념입니다. 예를 들어, 웹 애플리케이션 서버는 WAR를 재배포할 때 새로운 클래스 로더로 애플리케이션을 로드하여 기존 버전을 교체하는데, DI 컨테이너도 마찬가지로 새로운 클래스 로더 환경에서 빈들을 재구성합니다. 이러한 패턴은 모듈식 아키텍처와 유연한 확장성을 지향하는 현대 소프트웨어에서 핵심적인 역할을 하며, DI 컨테이너와 CustomClassLoader의 긴밀한 협력을 필요로 합니다.

정리하면, DI 컨테이너와 CustomClassLoader는 상호보완적인 관계에 있습니다. DI 컨테이너는 객체 생성/주입의 중심 역할을 하고, CustomClassLoader는 클래스 수준에서의 유연성(모듈 격리, 동적 로딩, 다중 버전 공존 등)을 제공합니다. 특히 대규모 애플리케이션이나 플러그인 플랫폼, 모듈형 시스템에서는 두 개념의 결합이 필수적이며, 이를 통해 유연하고 확장 가능한 의존성 관리를 실현하게 됩니다.

profile
주니어 개발자

0개의 댓글