스프링 프레임워크에서 사용되는 디자인 패턴
디자인 패턴(Design Pattern)
소프트웨어 디자인 과정에서 자주 발생하는 문제들에 대한 일반적인 해결책
객체 지향 4대 원칙(캡슐화, 상속, 추상화, 다형성)과 설계 원칙(SOLID)를 기반으로 구현되어 있음
4대 원칙 ➜ 요리 도구
설계 원칙 ➜ 도구의 사용법
디자인 패턴 ➜ 레시피
디자인 패턴 분류
GoF(Gang of Four)의 디자인 패턴은 크게
생성 패턴
,구조 패턴
,행위 패턴
으로 분류됨
생성 패턴 (Creational Pattern)
생성 패턴의 목적
객체 인스턴스를 생성할 때와 관련된 문제에 대한 해결책을 다룸
객체 생성 과정을 보다 유연하게 만들어주고, 코드의 타이트한 결합을 방지함
클래스의 캡슐화를 통해 코드의 유연성과 재사용 가능성을 향상시키는 패턴
생성 패턴의 예
싱글턴 (Singleton)
: 하나의 클래스 인스턴스를 전역에서 접근 가능하게 하면서 해당 인스턴스가 한 번만 생성도록 보장하는 패턴팩토리 메서드 (Factory Method)
: 객체를 생성하기 위한 인터페이스를 정의하고, 서브 클래스에서 어떤 클래스의 인스턴스를 생성할지 결정(서브 클래스에게 객체 생성을 위임)하는 패턴추상 팩토리 (Abstract Factory)
: 관련된 객체의 패밀리를 생성하기 위한 인터페이스 제공하며, 구체적인 팩토리 클래스를 통해 객체 생성을 추상화하는 패턴빌더 (Builder)
: 복잡한 객체의 생성 과정을 단순화하고, 객체를 단계적으로 생성하여 구성하는 패턴프로토타입 (Prototype)
: 객체를 복제하여 새로운 객체를 생성하는 패턴으로, 기존 객체를 템플릿으로 사용하는 패턴
구조 패턴 (Structural Pattern)
구조 패턴의 목적
클래스와 객체를 조합하여 더 큰 구조를 만드는 패턴
서로 다른 인터페이스를 가진 객체들을 조합하여 단일 인터페이스르 제공하거나, 서로 다른 객체들을 묶어 새로운 기능을 제공하는 패턴
구조 패턴의 예
어댑터 (Adapter)
: 인터페이스 호환성을 제공하지 않는 클래스를 사용하기 위해 래퍼(Wrapper)를 제공하는 패턴브리지 (Bridge)
: 추상화와 구현을 분리하여 두 가지를 독립적으로 확장할 수 있는 패턴컴포지트 (Composite)
: 개별 객체와 복합 객체를 동일하게 다루어 트리 구조의 객체를 구성하는 패턴데코레이터 (Decorator)
: 객체에 동적으로 새로운 기능을 추가하여 객체를 확장할 수 있는 패턴퍼사드 (Facade)
: 서브 시스템을 더 쉽게 사용할 수 있도록 단순한 인터페이스를 제공하는 패턴플라이웨이트 (Flyweight)
: 공유 가능한 객체를 통해 메모리 사용을 최적화하는 패턴프록시 (Proxy)
: 다른 객체에 대한 대리자(Proxy)를 제공하여 접근 제어, 지연 로딩 등을 구현하는 패턴
행위 패턴 (Behavioral Pattern)
행위 패턴의 목적
클래스나 객체 사이의 알고리즘이나 책임 분배에 관련된 패턴
객체들의 알고리즘을 캡슐화하여 객체의 행위를 변경하고 싶은 경우
객체들의 행위를 객체 안으로 캡슐화하여 재사용성을 높이고 싶은 경우
행위 패턴의 예
옵저버 (Observer)
: 객체 간의 일대다 종속 관계를 정의하여 한 객체의 상태 변경이 일어나면 다른 객체들에게 알리는 패턴전략 (Strategy)
: 알고리즘을 객체의 행동으로 캡슐화하여, 동일한 행동을 다른 알고리즘으로 교체할 수 있게하는 패턴커맨드 (Command)
: 요청을 객체로 캡슐화하여 요청을 매개변수화 하고, 요청을 큐에 저장하거나 로깅하고 실행을 지연시키는 패턴상태 (State)
: 객체의 상태를 캡슐화하고, 상태 전환을 관리하는 패턴책임 연쇄 (Chain of Responsibility)
: 요청을 보내는 객체와 이를 처리하는 객체를 분리하여, 다양한 처리자 중 하나가 요청을 처리하는 패턴방문자 (Visitor)
: 객체 구조의 요소에 대한 연산을 정의하여, 구조의 요소가 연산을 수행하도록 하는 패턴인터프리터 (Interpreter)
: 언어나 문법에 대한 해석기를 제공하여, 주어진 언어로 표현된 문제를 해결하는 패천메멘토 (Memento)
: 객체의 내부 상태를 저장하고 복원할 수 있는 기능을 제공하는 패턴중재자 (Mediator)
: 객체 간의 상호 작용을 캡슐화하여, 객체 간의 직접적인 통신을 방지하는 패턴템플릿 메서드 (Template Method)
: 알고리즘 구조를 정의하면서 하위 클래스(서브 클래스)에서 각 단계의 구현을 제공하는 패턴반복자 (Iterator)
: 컬렉션 내의 요소들에 접근하는 방법을 표준화하여 컬렉션 내부 구조에 독립적으로 접근할 수 있는 패턴
스프링 프레임워크에서 사용되는 디자인 패턴
싱글톤 패턴 ➜ 스프링 컨테이너(스프링 빈) 팩토리 메서드 패턴 ➜ 스프링의 DI(BeanFactory, Application Context) 프록시 패턴 ➜ 스프링의 AOP(@Transactional) 템플릿 메서드 패턴 ➜ JDBCTemplate, DispatcherServlet 어댑터 패턴 ➜ HandlerAdapter 등등
◽ 싱글턴 패턴 (Singleton Pattern)
어플리케이션에서 객체를 딱 하나만 만들어 사용하기 위한 패턴
인스턴스에서 static을 통해 최초 1번만 메모리를 할당시켜서 클래스의 인스턴스를 딱 1개만 생성하고, 어디서든지 그 인스턴스에 접근할 수 있도록 함
싱글턴 패턴의 구현 방법
생성자를
private
으로 선언하여 외부에서 인스턴스를 생성하지 못하도록 하고,getInstance()
메서드를 통해 싱글턴 인스턴스를 얻을 수 있도록 함
순수 Java로 구현한 싱글톤 패턴의 문제점
싱글톤 패턴을 구현하기 위한 코드가 더 늘어남
인스턴스를 반환해주는 구현 클래스를 직접 참조해야하기 때문에 DIP를 위반함
내부 속성을 변경 및 초기화하기 어렵고, private 생성자로 자식 클래스를 생성하기 어려움
싱글톤 컨테이너(스프링 컨테이너)
스프링에서는 사용자가 싱글톤 패턴을 구현하지 않고, 싱글톤 패턴의 문제점을 해결하면서 객체의 인스턴스를 싱글톤으로 관리함
스프링 컨테이너는 싱글톤 패턴을 적용하지 않아도, 객체 인스턴스를 싱글톤으로 관리함
스프링 빈(Bean)은 싱글톤 패턴으로 관리되는 빈으로 하나의 빈 인스턴스가 컨테이너 내에서 공유되고, 여러 요청에서 동일한 빈 인스턴스를 사용함
컨테이너 당 하나의 싱글톤 인스턴스를 가지는 구조로, 한 애플리케이션 내부에 여러 개의 컨테이너를 가지는 구조라면 전역적 싱글톤이 아닌 컨테이너에서의 싱글톤 인스턴스 개념을 가짐
@Autowired
싱글톤스프링에서
@Autowired
어노테이션을 통해 컨테이너 내 Bean을 주입받아 사용할 수 있음최초 애플리케이션 구동 시, Bean을 컨테이너에 등록하여
@Autowired
구문을 찾아 해당 변수에 등록한 Bean을 주입하여 사용함
빈 스코프를 프로토타입으로 설정하면, 싱글톤이 아닌 매번 새로운 인스턴스를 생성할 수 있음 (디폴트는 싱글톤)
안티패턴
전역 상태 공유
싱글턴은 전역 상태를 공유하므로, 다른 객체들 사이에 강한 결합을 만들고, 테스트하기 어렵게 만듦
개발 확장 어려움
만약 다수의 싱글톤이 서로 강하게 결합되어 있다면, 코드의 확장이 어려워질 수 있음
OCP(Open-Closed Principle)를 위반하게 됨
스레드 세이프 문제
멀티스레드 환경에서 동시에
getInstance()
메서드를 호출하면, 여러 인스턴스가 생성될 수 있음이를 해결하기 위해
getInstance()
메서드에synchronized
키워드를 사용할 수 있지만, 성능이 저하됨
싱글턴 패턴을 안티패턴으로 간주하여 자바 코드에서 사용이 감소하고 있음
◽ 팩토리 메서드 패턴 (Factory Method Pattern)
객체를 생성하는 인터페이스는 미리 정의하되, 객체 생성은 서브 클래스(팩토리)로 위임하는 패턴
객체를 만들어주는 클래스를 별도로 만들어서 객체를 생성하는 방식
생성된 객체를 반환하는 메서드를 추상화하여 인터페이스를 통한 다형성을 제공함
팩토리 메서드 패턴 구현 방법
공통 인터페이스를 정의하고, 인터페이스를 상속한 클래스를 구현함
구현한 클래스의 객체 반환용 팩토리 클래스를 정의
팩토리 클래스를 통해 객체를 받아서 사용함
BeanFactory, Application Context
Spring의 DI(Dependency Injection)는 팩토리 메서드 패턴을 따름 스프링에서는 빈을 생성하는 빈 컨테이너가 팩토리 패턴을 사용하여 빈을 관리함
Spring의
BeanFactory
스프링의
BeanFactory
인터페이스는 객체(빈) 생성과 관리의 복잡성을 추상화하는 팩토리 메서드 패턴의 일종으로, 이를 통해 스프링 컨테이너는 빈의 생명주기를 관리함Bean Factory
: Bean 정의를 포함하고 Bean을 생성하는 Spring 컨테이너getBean()
메서드는 팩토리 메서드로 간주됨
기본적으로 스프링은 Bean Container를 Bean을 생성하는 Factory로 취급함
BeanFactory의 확장 인터페이스인
Application Context
스프링은 BeanFactory를 상속하여 추가적인 Application을 다루는
Application Context
인터페이스를 구현함XML 파일이나 자바 어노테이션과 같은 외부 설정을 기반으로 Bean Container를 시작하기 위해 Application Context를 사용함
◽ 프록시 패턴 (Proxy Pattern)
개념
한 객체(proxy)가 다른 객체(subject or service)에 대한 접근을 제어하기 위한 용도로 대리인이나 대변인에 해당하는 객체를 제공하는 패턴
프록시 객체는 실제 객체(대상 원본 객체)에 대한 참조를 가지고 있어서 클라이언트의 요청을 처리하고, 실제 객체로 작업을 위임함
대상 객체(Subject)의 메소드를 직접 실행하는 것이 아닌, 대상 객체에 접근하기 전에 프록시(Proxy) 객체의 메서드를 접근한 후 추가적인 로직을 처리한 뒤에 대상 객체에 접근함
프록시와 대상은 동일한 인터페이를 가지고 있으며 이를 통해 다른 인터페이스와 완전히 호환되도록 바꿀 수 있음
사용 시기
접근을 제어하거나 기능을 추가하고 싶은데 기존 객체를 수정할 수 없는 상황인 경우
초기화 지연, 접근 제어, 로깅, 캐싱 등 기존 객체 동작에 수정 없이 가미하고 싶은 경우
Spring에서는 두 가지 타입의 Proxy를 사용함
빈으로 등록하려느 객체가 인터페이스를 하나라도 구현하고 있으면 JDK를 이용하고, 인터페이스를 구현하고 있지 않으면 내장된 CGLib 라이브러리를 사용함
CGLib Proxy
: 클래스의 바이트 코드를 조작하여 프록시 객체를 생성해주는 라이브러리JDK Dynamic Proxy
: Interface들을 프록시할 때 사용
Spring의 AOP
스프링에서는 프록시 패턴을 사용하여 AOP를 구현함
스프링에서는 AOP에 기반해 수 많은 로킹 처리, 트랜잭션 처리를 공통 모듈화시켜서 따로 작성함
Spring의 AOP는 CGLib라는 런타임 프록시 생성 라이브러리를 사용해서 AOP를 위한 프록시 객체를 생성하여 사용함
스프링에서는 어노테이션으로 서비스가 추상회되어 많이 사용하는데, 그 중에서
@Transactional
은 DB 트랜잭션 처리를 간단하게 만들어주고 비즈니스에 집중하게 만들어줌스프링은 BookRepository 빈 객체를
EnhancerBySpringCGLIB
객체로 wrapping하고, BookRepository 객체에 대한 접근을 제어할 수 있게 함
◽ 템플릿 메서드 패턴 (Template Method Pattern)
어떤 작업을 처리하는 일부분을 서브 클래스로 캡슐화하여 전체 일을 수행하는 구조는 바꾸지 않으면서 특정 단계에서 수행하는 내용을 바꾸는 패턴
변하지 않는 기능(템플릿)은 상위 클래스에 만들어두고 자주 변경되며 확장할 기능은 하위 클래스에서 만들도록 함
상위의 메서드 실행 동작 순서는 고정하면서 세부 실행 내용은 다양화될 수 있는 경우에 사용
JdbcTemplate
JdbcTemplate은 커넥션 정리와 결과 반복, 예외 처리, 트랜잭션 처리 등과 같은 복잡하고 세부적인 사항들을 스프링 내부에서 알아서 처리하게 해주는 코드가 들어가 있음
따라서 반복되는 DB 연동 로직은 JdbcTemplate 클래스의 템플릿 메서드가 제공하고, 개발자는 달라지는 SQL 구문과 설정값만 수정하면 됨
DB에 질의할 때 4가지 단계를 거치게 되는데 JdbcTemplate의
query()
가 템플릿 메서드 패턴으로 동작함
DispatcherServlet ➜ FrameworkServlet ➜ HttpServletBean ➜ HttpServlet
상속 구조를 가지고 있는데,DispatcherServlet
의doService()
메서드가 템플릿 메서드 패턴으로 구현됨FrameworkServlet
에서doService()
에 대한 구현은 하위 클래스에게 위임하고 있고,DispatcherServlet
은doService()
를 구체화하고 있음FrameworkServlet
의processRequest()
로직을 수행하다가doService()
를 만나면DispatcherServlet
의doService()
로직을 수행함
◽ 어댑터 패턴 (Adapter Pattern)
서로 일치하지 않는 인터페이스를 갖는 클래스들을 함께 동작시키기 위해 사용하는 패턴
스프링에서는 어댑터 패턴을 사용하여 다양한 유형의 컨트롤러 및 메시지 변환 작업을 수행함
Ref
Last updated