회원가입을 하면 원하는 문장을
저장할 수 있어요!
다음
AWS 이용 중이라면 최대 700만 원 지원받으세요
자바 디자인 패턴 시리즈 두 번째 편으로 이번에는 구조 패턴(Structural Patterns)의 종류와 적용 방법을 살펴볼까 합니다. 지난 편에서 살펴본 어댑터 패턴(Adaptor Pattern) 역시 대표적인 구조 패턴이라고 할 수 있는데요. 이번 글에서는 어댑터 패턴 외에 퍼사드(Facade), 데코레이터(Decorator), 프록시(Proxy) 패턴과 같은 다양한 구조 패턴을 알아보고, 실제 프로젝트에서 어떻게 적용하는지 정리해 봤습니다.
회원가입을 하면 원하는 문장을
저장할 수 있어요!
다음
회원가입을 하면
성장에 도움이 되는 콘텐츠를
스크랩할 수 있어요!
확인
자바 디자인 패턴 시리즈 두 번째 편으로 이번에는 구조 패턴(Structural Patterns)의 종류와 적용 방법을 살펴볼까 합니다. 지난 편에서 살펴본 어댑터 패턴(Adaptor Pattern) 역시 대표적인 구조 패턴이라고 할 수 있는데요. 이번 글에서는 어댑터 패턴 외에 퍼사드(Facade), 데코레이터(Decorator), 프록시(Proxy) 패턴과 같은 다양한 구조 패턴을 알아보고, 실제 프로젝트에서 어떻게 적용하는지 정리해 봤습니다.
구조 패턴은 객체와 클래스의 관계를 재정의하여 새로운 구조로 만드는 소프트웨어 디자인 패턴을 말합니다. 예를 들어, 이전 편에서 다뤘던 어댑터 패턴(Adapter Pattern)과 같이 서로 호환되지 않는 인터페이스를 연결하거나, 서로 다른 인터페이스를 가진 클래스들이 상호 작용할 수 있도록 만드는 방식 등을 말합니다.
다양한 디자인 패턴 중 대표적인 구조 패턴으로 어댑터, 퍼사드, 데코레이터, 프록시 패턴 등이 있습니다. 이들 모두 객체와 클래스 간의 구조를 변경하여 코드 확장성을 향상시키며, 시스템의 유지 보수를 용이하게 해줍니다. 또한 코드 재사용성을 높여 개발자들이 복잡도가 높은 시스템을 효율적으로 구축할 수 있습니다.
구조 패턴 중에서 먼저 퍼사드 패턴에 대해서 살펴보도록 하죠. 퍼사드 패턴은 복잡한 서브시스템에 대해 하나의 통합된 인터페이스를 제공하여, 시스템 구조를 단순하게 만드는 디자인 패턴을 말합니다. 참고로 퍼사드(Facade)란 영어로 건물의 정면을 의미하는데요. 즉, 퍼사드 패턴은 건물 안의 복잡한 부분(=복잡한 서브시스템)을 건물 벽(=퍼사드 클래스)이 가리고 있는 모습과 유사하다고 할 수 있습니다.
아래 코드는 퍼사드 패턴을 적용한 예시 코드로서 컴퓨터 본체(computer body)라는 퍼사드 클래스를 통해 부팅(Booting) 과정에 필요한 서브시스템인 CPU, 메모리, 하드 디스크 등의 클래스를 단순화 시킨 코드입니다. 즉, 우리가 컴퓨터를 부팅 시킬 때 컴퓨터 내부에 있는 CPU, 메모리, 하드디스크 등 복잡한 과정을 단지 시작 버튼(start button) 하나로 단순화 시킨 것이라고 할 수 있죠.
위 3가지 클래스는 서브시스템(sub system) 역할을 하는 CPU, 메모리, 하드디스크를 추상화한 클래스입니다. 이 3가지 서브시스템에 대해 아래와 같이 ComputerBody라는 퍼사드 클래스를 구현했는데요. 이를 통해 클라이언트 코드에서는 ComputerBody의 startButton 메서드를 호출만으로 부팅 프로세스를 시작할 수 있습니다.
위 예시 코드에서 볼 수 있듯 사용자는 컴퓨터 내부의 CPU나 메모리, 하드디스크의 복잡한 동작에 대해서 신경 쓰지 않아도 컴퓨터를 간단하게 부팅 시킬 수 있습니다. 이처럼 퍼사드 패턴은 간단한 퍼사드 클래스로 제공함으로써, 복잡한 서브시스템에 대한 제어를 단순하게 만드는 역할을 합니다.
데코레이터 패턴은 기능을 동적으로 추가하거나 확장하는 방법을 제공하는 구조 패턴입니다. 이 패턴은 기존 객체의 기능을 변경하지 않으면서 새로운 기능을 추가할 수 있도록 하는데요. 이를 통해 유연하게 코드를 설계할 수 있으며, 자바 클래스 상속이 가진 여러 제약들을 손쉽게 해결할 수 있습니다.
아래 예시 코드는 데코레이터 패턴을 이용해 자동차 주문 시스템을 구현한 간단한 코드입니다. 이 예시 코드에서는 Car라는 추상 클래스를 상속하여 구체적인 차종인 Audi를 구현하고 있습니다.
다음으로 CarDecorator라는 클래스를 Car 클래스로 상속받아 구현했습니다. 여기서 이 CarDecorator가 데코레이터 패턴에서 사용하는 기법이라고 할 수 있는데요. SunRoof가 직접 Car 추상 클래스를 상속하는 대신, 중간에 또 다른 추상 클래스를 거치도록 해서 직접적인 상속 관계에 따른 제약을 최소화시키는 것이죠.
이처럼 Car와 CarDecorator 구조를 만들면, 위 클라이언트 코드와 같이 간단하게 Audi 차종에 SunRoof 옵션을 추가할 수 있습니다. 이처럼 데코레이터 패턴을 사용하면 기존 상속 구조를 유지한 채 새로운 기능을 동적으로 손쉽게 추가할 수 있습니다.
마지막으로 프록시 패턴은 객체의 접근을 제어하는 구조 패턴을 말합니다. 이 패턴은 클라이언트가 객체에 직접 접근하지 않고, 프록시를 통해 접근하도록 하여 객체의 생성과 소멸, 네트워크 통신 등의 복잡한 프로세스를 관리할 수 있도록 하는데요. 예를 들어, 프록시 패턴을 통해 이미지 로드를 지연시키는 등의 방법으로 리소스를 관리하는 기법이 있습니다.
아래 예시 코드는 이미지 로딩 과정에서 프록시 패턴을 이용하여 지연 로딩(Lazy Loading)을 구현한 것입니다. 이 예시에서 RealImage는 실제 이미지 클래스를 의미하고, ProxyImage는 프록시 이미지 클래스를 나타냅니다.
위와 같은 형태로 프록시 클래스를 만들면, 사용자는 아래 코드와 같이 Lazy Loading을 사용할 수 있습니다. 즉, 처음 ProxyImage 객체의 display 메소드가 호출될 때만 RealImage를 로드하며, 그 이후로는 반복적으로 이미지를 로드하지 않고 프록시 객체를 통해 기존에 로드된 이미지만 재사용하게 됩니다.
이처럼 프록시 패턴을 사용하면 불필요한 이미지 로딩을 피하고, 리소스를 효율적으로 사용할 수 있다는 장점이 있죠. 이 밖에도 프록시 패턴을 이용하여 보안에 필요한 권한 확인, 캐시 존재 여부 확인 등의 작업도 할 수 있습니다.
앞서 자바 구조 패턴의 종류와 각각의 예시 코드를 살펴봤는데요. 그러면 실제 프로젝트에서는 언제, 어떻게 각각의 디자인 패턴을 적용해야 할까요? 디자인 패턴의 적용은 현재 코드의 구조와 프로젝트의 요구사항 및 유지 보수 방향에 따라 달라지지만, 공통적으로 각 디자인 패턴의 적용을 판단하는 과정은 다음과 같습니다.
만약 프로젝트 내에 여러 서브시스템이 있고, 이를 동시에 제어해야 하는 경우 퍼사드 패턴의 적용을 고려해 볼 수 있습니다. 퍼사드 패턴을 이용하면 클라이언트 코드가 서브시스템의 세부 사항에 의존하지 않아도 되기 때문에 전체적인 코드의 결합도를 줄일 수 있는데요. 다만 퍼사드 패턴이 제공하는 인터페이스는 일반적으로 제한적이기 때문에, 이로 인해 클라이언트 코드가 시스템의 세부적인 기능에 접근하는데 제한이 있을 수 있다는 점을 알아두어야 합니다.
데코레이터 패턴은 프로젝트에서 객체에 대한 동적인 기능 추가가 요구될 때 고려해 볼 수 있습니다. 즉, 기존 클래스를 수정하지 않고 기능을 확장해야 하는 경우, 서브클래싱을 통한 확장이 비효율적이거나, 불가능한 경우에 이 패턴을 사용하게 되는데요. 다만 데코레이터 패턴을 사용하려면 새로운 인터페이스와 클래스를 추가해야 하기 때문에, 설계 복잡도가 높아질 수 있다는 점을 함께 고려해야 합니다.
마지막으로 프록시 패턴은 객체에 대한 접근을 제어해야 할 때 적용을 고려할 수 있습니다. 예를 들어, 원격 네트워크 연결, 메모리 내 로드가 무거운 객체 관리, 보안이 필요한 요소에 대한 접근을 제어하려는 경우에 프록시 패턴 적용을 고민해 보면 좋습니다.
프록시 패턴을 사용하면 실제 객체의 세부 사항을 클라이언트 코드에서 알 필요가 없기 때문에, 구조를 단순화하고 시스템의 유지 보수성을 향상시키는 역할을 합니다. 다만 무분별하게 프록시 패턴을 사용하면 전체 시스템 성능에 영향을 미칠 수 있으며, 테스트 시 실제 객체의 행동을 정확히 반영하지 않을 수 있기 때문에 프록시 패턴을 적용할 땐 주의가 필요합니다.
지금까지 전편에서 다루었던 어댑터 패턴에 이어 대표적인 구조 패턴인 퍼사드, 데코레이터, 프록시 패턴에 대해 알아보고, 실제 프로젝트에서의 적용 방법을 정리해 봤습니다. 앞서 살펴본 바와 같이 구조 패턴은 클래스 간의 구조를 관리하고, 리소스 및 유지 보수를 최적화하는 데 있어 유용한 디자인 패턴입니다. 다음 편에서는 디자인 패턴의 또 다른 한 축이며, 객체의 생성과 변경을 관리하는 생성 패턴(Creational Patterns)에 대해서 살펴보겠습니다.
요즘IT의 모든 콘텐츠는 저작권법의 보호를 받는 바, 무단 전재와 복사, 배포 등을 금합니다.