[ Spring 디자인 패턴 ]

carrotsman·2023년 1월 24일
0

백엔드

목록 보기
6/6
post-thumbnail

소프트웨어 디자인 패턴

소프트웨어 공학의 소프트웨어 디자인에서 특정 문맥에서 공통적으로 발생하는 문제에 대해 재사용 가능한 해결책이다. 소스나 기계 코드로 바로 전환될수 있는 완성된 디자인은 아니며, 다른 상황에 맞게 사용될 수 있는 문제들을 해결하는데에 쓰이는 서술이나 템플릿이다. 디자인 패턴은 프로그래머가 어플리케이션이나 시스템을 디자인할 때 공통된 문제들을 해결하는데에 쓰이는 형식화 된 가장 좋은 관행이다.

정리하자면 소프트웨어 개발에 있어 많이 해왔던 방식을 뜻한다. 그니까 새로 개발들어갈때 어줍잖은 머리 굴려서 만들지 말고 여러가지 패턴이 있으니 그 관행에 따라 만들라는 뜻이다. Spring에도 이러한 디자인 패턴이 여러가지 존재한다. 알아보자

🌱 Spring에서 가장 많이 사용되는 패턴은 크게 네 가지이다.

  • 싱글톤 패턴 (Singleton pattern)
  • 팩토리 메소드 패턴 (Factory Method pattern)
  • 프록시 패턴 (Proxy pattern)
  • 템플릿 메소드 패턴 (Template Method Pattern)

싱글톤 패턴 (Singleton pattern)

싱글턴 패턴(Singleton pattern)을 따르는 클래스는, 생성자가 여러 차례 호출되더라도 실제로 생성되는 객체는 하나이고 최초 생성 이후에 호출된 생성자는 최초의 생성자가 생성한 객체를 리턴한다.

스프링은 기본적으로 Bean을 추가할때 하나의 인스턴스를 컨테이너에 등록하고 사용하개 되어있다. 위에 이미지에서 알 수 있듯 1개의 컨테이너당 하나의 싱글톤 인스턴스를 갖는 구조다. 한마디로 어플리케이션 내부에 여러개의 컨테이너를 가지는 구조인 경우 전역적 싱글톤이 아니라 각 컨테이너에서의 싱글톤 인스턴스라는 개념을 알 수 있다.


🐥 Autowired 싱글톤

Spring에서 @Autowired 어노테이션을 통해 컨테이너 내 Bean을 주입받아 사용할 수 있다. 이 말인 즉슨 최초 어플리케이션 구동시 Bean을 컨테이너에 등록하고 @Autowired 구문을 찾아 해당 변수에 등록한 Bean을 주입하여 사용한다.

// carrots1Controller.java
@RestController
public class carrots1Controller {

    @Autowired
    private carrotsRepository repository;
}

// carrots2Controller.java
@RestController
public class carrots2Controller {
    
    @Autowired
    private carrotsRepository repository;
}

각 Controller 클래스 내부 repository는 같은 객체다. 로그로 해당 인스턴스의 주소 값을 찍어보면 같은 인스턴스임을 확인할 수 있다.


팩토리 메소드 패턴 (Factory Method pattern)

팩토리 메서드 패턴(Factory method pattern)은 객체지향 디자인 패턴이다. Factory method는 부모(상위) 클래스에 알려지지 않은 구체 클래스를 생성하는 패턴이며. 자식(하위) 클래스가 어떤 객체를 생성할지를 결정하도록 하는 패턴이기도 하다. 부모(상위) 클래스 코드에 구체 클래스 이름을 감추기 위한 방법으로도 사용한다.

뭐 가타부타 말할 것 없이 객체 싸개를 만드는 패턴으로 위 그림에서 그 형태를 확인할 수 있다. 각 환경에 맞게 Vehicle객체를 생성하려 한다 가정해보자. VehicleFactory라는 템플릿을 사용해 환경에 맞게 호출할 수 있는 BoatFactory, AirplainFactory 클래스를 만들고 createVehicle 메소드를 통해 원하는 객체를 생성한다. 뭔말알?

👍 팩토리 메소드 패턴의 장점

  • 각 객체를 인터페이스를 통해 하나로 관리 가능
  • 비슷한 객체 추가시 추가 구현 용이
  • 공통코드 무결성 보장

👊 팩토리 메소드 패턴 사용법

  1. 공통 interface 정의
  2. interface 상속 및 Class 구현
  3. 구현한 Class 객체 반환용 Factory Class 정의
  4. Factory Class를 통해 객체를 받아 사용

1. 어플리케이션 컨텍스트

Spring의 DI(Dependency Injection)는 팩토리 메소드 패턴을 따른다. ApplicationContext가 BeanFactory를 상속하기 때문인데, 한마디로 Spring DI는 빈 컨테이너를 빈싸개 Factory로 취급한다는 얘기다.

public interface BeanFactory {
    getBean(Class<T> requiredType);
    getBean(Class<T> requiredType, Object... args);
    getBean(String name);

    // ...
}

각 getBean 메소드는 빈의 유형 및 이름과 같이 메소드에 제공된 기준과 일치하는 Bean을 반환하는 Factory 메소드로 간주된다.

@Component
public class carrots {
}
@Test
public void getCarrots() {
    ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
    carrots cr = context.getBean(carrots.class);
    assertNotNull(cr);
}

머 대충 이런 말이지요~


2. 외부 설정

외부 설정을 통해서도 완전히 바꿀 수 있다는 점에서 매우 유연하다. 바라보는 설정파일만 달라져도 전혀다른 방식으로 활용가능하다는 얘기다.

@Test
public void getCarrots() {
    // ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
    ApplicationContext context = new ClassPathXmlApplicationContext("context.xml");
    carrots cr = context.getBean(carrots.class);
    assertNotNull(cr);
}

프록시 패턴 (Proxy pattern)

일반적으로 프록시는 다른 무언가와 이어지는 인터페이스의 역할을 하는 클래스이다. 프록시는 어떠한 것(이를테면 네트워크 연결, 메모리 안의 커다란 객체, 파일, 또 복제할 수 없거나 수요가 많은 리소스)과도 인터페이스의 역할을 수행할 수 있다. 하나의 개체(프록시)가 다른 개체(주체 또는 서비스)에 대한 액세스를 제어할 수 있도록 하는 패턴이다.

잘 정리된 글이 있어 참조건다. 후후 👉 [ 디자인패턴 ] 프록시패턴 (Proxy Pattern)

👍 프록시패턴 장점

  • 사이즈가 큰 객체가 로딩되기 전에도 프록시를 통해 참조를 할 수 있다.
  • 실제 객체의 public, protected 메소드를 숨기고 인터페이스를 통해 노출시킬 수 있다.
  • 로컬에 있지 않고 떨어져있는 객체를 사용할 수 있다.
  • 원래 객체에 접근에 대해 사전처리를 할 수 있다.

👎 프록시패턴 단점

  • 객체를 생성할 때 한 단계를 거치게 되므로, 빈번한 객체 생성이 필요한 경우 성능이 저하될 수 있다.
  • 프록시 내부에서 객체 생성을 위해 스레드가 생성, 동기화가 구현되어야 하는 경우 성능이 저하될 수 있다.
  • 로직이 난해해져 가독성이 떨어질 수 있다.

🧾 프록시 패턴의 종류

  • 가상 프록시
    꼭 필요로 하는 시점까지 객체의 생성을 연기하고, 해당 객체가 생성된 것 처럼 동작하도록 만들고 싶을 때 사용하는 패턴이다. 프록시 클래스에서 작은 단위의 작업을 처리하고 리소스가 많이 요구되는 작업들이 필요할 경우만 주체 클래스를 사용하도록 구현한다.

  • 원격 프록시
    원격 객체에 대한 접근을 제어 로컬 환경에 존재하며, 원격 객체에 대한 대변자 역할을 하는 객체 서로 다른 주소 공간에 있는 객체에 대해 마치 같은 주소 공간에 있는 것 처럼 동작하게 하는 패턴이다.(예: Google Docs)

  • 보호 프록시
    주체 클래스에 대한 접근을 제어하기 위한 경우에 객체에 대한 접근 권한을 제어하거나 객체마다 접근 권한을 달리하고 싶을 경우 사용하는 패턴으로 프록시 클래스에서 클라이언트가 주체 클래스에 대한 접근을 허용할지 말지 결정하도록 할 수 있다.


@Transactional

프록시를 생성하기 위해 우리는 주제와 동일한 인터페이스를 구현하고 주제에 대한 참조를 포함하는 객체를 생성한다. 메소드에 @Transactional 어노테이션을 명시해 해당 메소드의 원자성을 보장시키게 한다. 한마디로 메소드 내부에서 오류가 발생하거나 문제가 되는 경우 호출전 상태로 되돌리기 때문에 안정성이 보장된다.

@Service
public class BookManager {

    @Autowired
    private BookRepository repository;

    @Transactional
    public Book create(String author) {
        System.out.println(repository.getClass().getName());
        return repository.create(author);
    }
}

스프링은 CGLib을 통해 프록시를 만들고 bean을 래핑해 메소드가 원자적으로 실행되도록한다
BookManager#create를 호출하면 아래와 같은 출력을 확인할 수 있다.

com.baeldung.patterns.proxy.BookRepository$$EnhancerBySpringCGLIB$$3dc2b55c

BookRepository의 주소 값이 출력될거 같지만, EnhancerBySpringCGLIB의 주소 값이 출력되는 것을 확인할 수 있다. Spring은 BookRepository 객체를 EnhancerBySpringCGLIB 객체로 wrapping한다. BookRepository 객체에 대핸 접근을 제어할 수 있게된다.


💥 CGLib 프록시

Code Generator Library의 약자로, 클래스의 바이트코드를 조작하여 Proxy 객체를 생성해주는 라이브러리

일반적으로 Spring은 두 가지 유형의 프록시를 사용한다.

  • CGLib 프록시 – 클래스를 프록시할 때 사용
  • JDK 동적 프록시 – 인터페이스 프록시 시 사용

트랜잭션을 사용하여 기본 프록시를 노출하는 동안 Spring은 bean에 대한 액세스를 제어해야 하는 모든 시나리오에 대해 프록시를 사용한다.


템플릿 메소드 패턴 (Template Method Pattern)

어떤 작업을 처리하는 일부분을 서브 클래스로 캡슐화해 전체 일을 수행하는 구조는 바꾸지 않으면서 특정 단계에서 수행하는 내역을 바꾸는 패턴

AbstractClass는 템플릿 메소드를 정의하며, 하위 클래스에서 알맞게 확장할 수 있는 메소드인 훅 메소드를 제공한다. 여기서, 템플릿 메소드는 일반적인 메소드와 훅 메소드를 이용, ConcreteClass는 물려받은 훅 메소드를 재정의하여 구축하는 패턴을 말한다. 한마디로 상속에 대한 개념을 전적으로 사용하여 구축한다 생각하면 편하다.

잘 정리된 글이 있어 참조건다. 후후2 👉 템플릿 메소드 패턴(Template Method Pattern)

👍 장점

  • 중복코드를 줄일 수 있다.
  • 자식 클래스의 역할을 줄여 핵심 로직의 관리가 용이하다.
  • 좀더 코드를 객체지향적으로 구성할 수 있다.

👎 단점

  • 추상 메소드가 많아지면서 클래스 관리가 복잡해진다.
  • 클래스간의 관계와 코드가 꼬여버릴 염려가 있다.

정리하며

오늘은 Spring 디자인 패턴에 대해 알아봤다. 근데 사실상 설명을 읽어보면 전부 Spring의 특성이라고 알고있던 내용들이다. 명확히 몰랐다는게 내 경력을 의심케하는 밤이다. 물경력.. 뻙

오늘 저녁은 불고기이다. 🥕


참고 : https://blog.naver.com/PostView.naver?blogId=hj_kim97&logNo=222295968816&redirect=Dlog&widgetTypeCall=true&directAccess=false
https://www.baeldung.com/spring-framework-design-patterns
https://velog.io/@newtownboy/%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4-%ED%94%84%EB%A1%9D%EC%8B%9C%ED%8C%A8%ED%84%B4Proxy-Pattern
https://gmlwjd9405.github.io/2018/07/13/template-method-pattern.html
https://coding-factory.tistory.com/712

profile
당근먹고 강력한 개발

0개의 댓글