스프링 핵심 원리 - 기본편 Part.2

Bluewind·2024년 6월 6일
0

withSIN

목록 보기
2/9

해당 글은 인프런의 김영한님의 강의인 '스프링 핵심 원리 - 기본편'을 공부하며 작성한 글입니다.

Section 1에서는 자바의 다형성 만으로는 SOLID 원칙의 DIP, OCP 원칙을 지킬 수 없다는 것을 알아보았다.
이번 섹션에서는 예제를 직접 만들어서 순수 자바 코드로는 어떻게 OCP, DIP 원칙을 지킬 수 있을지를 코드로 알아본다.

예제 정리

기존에는 공연 서비스 구체 클래스에서는 new 를 통하여 객체 주입을 하였다.

public class 공연서비스구현 implements 공연서비스 {
	private final 남자주인공역할 남주 = new 이정재();
}

만약 남자 주인공 역할이 황정민으로 변경되었다면 공연 서비스 코드가 아래처럼 변경된다.

public class 공연서비스구현 implements 공연서비스 {
//	private final 남자주인공역할 남주 = new 이정재();
	private final 남자주인공역할 남주 = new 황정민();
}

위의 코드는 추상 인터페이스인 '남자주인공역할'과 구체 인터페이스인 '이정재'까지 모두 의존하는 코드였다.
이 공연의 남자 주인공 역할은 이정재이다 라고 공연이 정하고 있는 셈.

그래서 '공연 기획자'가 등장한다.

공연 서비스 구체 클래스에는 new 를 통한 객체 주입이 아닌 생성자 주입을 통하여 '남자주인공역할'을 넣어준다.

public class 공연서비스구현 implements 공연서비스 {
	private final 남자주인공역할 남주;
    private final 여자주인공역할 여주;

	public 공연서비스구현(남주, 여주) {
		this.남주 = 남주;
        this.여주 = 여주;
	}
    
    public void 오프닝(남주, 여주) {
    	...
    }
}

공연 기획자 클래스에서는 생성자를 통하여 공연서비스를 호출하면 구현 클래스 안에 남자주인공역할을 하게될 황정민 클래스를 넣어준다.

public class 공연기획자 {

	public 공연서비스 공연서비스() {
    	return new 공연서비스구현(new 황정민(), 김혜수());
    }
}

공연 서비스는 남자, 여자 주인공 역할 배우가 누가 될지 모르는 추상화에만 의존하면서 시나리오를 짤 수 있다.
그리고 공연 기획자는 생성자 주입을 통하여 배우 역할들의 캐스팅을 담당하게되며 DIP가 지켜지게된다.

공연 서비스는 공연에만 관심을 가질 수 있는 관심사의 분리가 완성되었다.

이후 메인 메서드에서는 new 확장자를 통하여 주입하는것이 아닌 공연 기획자를 통하여 공연 서비스를 가져온다.

좋은 객체 지향 설계의 원칙 적용

SRP 단일 책임 원칙

한 클래스는 하나의 책임만 가져야 한다.

  • 클라이언트 객체는 실행하는 책임만 담당.
  • 구현 객체를 생성하고 연결하는 책임은 AppConfig가 담당.

DIP 의존관계 역전 원칙

프로그래머는 “추상화에 의존해야지, 구체화에 의존하면 안된다.” 의존성 주입은 이 원칙을 따르는 방법 중 하나다.

  • 클라이언트 코드가 남자주인공역할 추상화 인터페이스에만 의존하도록 코드를 변경.
  • AppConfig(공연 기획자)가 황정민 객체 인스턴스를 클라이언트 코드 대신 생성해서 클라이언트 코드에 의존관계를 주입.

OCP 개방-폐쇄 원칙

소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다.

  • 이후에 남자 주인공 역할을 또 다른 배우로 변경하여도 클라이언트 코드인 공연 서비스 코드는 변경하지 않아도 된다.
  • 소프트웨어 요소를 새롭게 확장(Open)해도 사용 영역의 변경은 닫혀(Close)있다.

IoC, DI

제어의 역전 IoC(Inversion of Control)

  • 기존 프로그램은 클라이언트 구현 객체가 프로그램의 제어 흐름을 스스로 조종했다.
  • 하지만 AppConfig가 등장한 이후에는 구현 객체는 로직을 실행하는 역할만 담당한다.
  • 프로그램의 제어 흐름은 AppConfig가 가져간다.
  • 이렇게 프로그램의 제어 흐름을 직접 제어하는것이 아니라 외부에서 관리하는 것을 제어의 역전이라 한다.

의존관계 주입 DI(Dependency Injection)

  • 공연 서비스는 남자 주인공 역할 인터페이스에만 의존한다.
    실제로 어떤 배우가 캐스팅 될지는 모른다.
  • 애플리케이션 실행 시점(런타임)에 외부에서 실제 구현 객체를 생성하고 클라이언트에 전달해서 클라이언트와 서버의 실제 의존관계가 연결 되는 것을 의존관계 주입이라 한다.
  • 객체 인스턴스를 생성하고, 그 참조값을 전달해서 연결된다.
  • 의존관계 주입을 사용하면 클라이언트 코드를 변경하지 않고, 클라이언트가 호출하는 대상의 타입 인스턴스를 변경할 수 있다.

IoC 컨테이너, DI 컨테이너

  • AppConfig 처럼 객체를 생성하고 관리하면서 의존관계를 연결해 주는 것을 'IoC 컨테이너' 또는 'DI 컨테이너'라 한다.
  • 의존관계 주입에 초점을 맞추어 최근에는 주로 DI 컨테이너라 한다.

스프링

스프링을 활용해보자

  • AppConfig에는 구성 정보를 담고있다는 @Configuration 어노테이션을 달아준다.
  • 각 메서드에는 @Bean 을 달아주어 스프링이 관리할 수 있도록 한다.
  • 스프링에서는 @Configuration이 달린 AppConfig를 보고 구성 정보로 활용하고, 스프링 컨테이너에 @Bean이 달린 메서드들을 모두 등록한다.

기존에는 메인에서 AppConfig를 통하여 객체들을 조회했지만 이제는 스프링 컨테이너를 통해서 객체(스프링 빈)을 조회해야 한다.

그렇다면 굳이 왜 스프링 컨테이너를 왜 사용해야 할까?

싱글톤

  • 스프링은 태생이 기업용 온라인 서비스 기술을 지원하기 위해 탄생했다.
  • 온라인 서비스는 보통 여러 고객이 동시에 요청을 날린다.
  • 스프링이 없는 순수한 DI 컨테이너인 AppConfig는 요청을 할 때 마다 객체를 새로 생성한다.
  • 메모리 낭비가 심하다.

객체가 한 개만 생성되고 공유하도록 설계하면 된다 > 싱글톤 패턴

싱글톤 패턴

  • 클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는 디자인 패턴이다.
  • 싱글톤 패턴을 구현하는 방법은 여러가지가 있다

싱글톤 패턴을 적용하면 고객의 요청이 올 때 마다 객체를 생성하는 것이 아니라, 이미 만들어진 객체를 공유해서 효율
적으로 사용할 수 있다.
하지만 싱글톤 패턴은 다음과 같은 수 많은 문제점들을 가지고 있다.

  • 싱글톤 패턴을 구현하는 코드 자체가 많이 들어간다.
  • 의존관계상 클라이언트가 구체 클래스에 의존한다. DIP를 위반한다.
  • 클라이언트가 구체 클래스에 의존해서 OCP 원칙을 위반할 가능성이 높다.
  • 테스트하기 어렵다.
  • 내부 속성을 변경하거나 초기화 하기 어렵다.
  • private 생성자로 자식 클래스를 만들기 어렵다.
  • 결론적으로 유연성이 떨어진다.

하지만 스프링 컨테이너는 싱글턴 패턴을 적용하지 않아도, 객체 인스턴스를 싱글톤으로 관리한다.

싱글톤 방식의 주의점

  • 객체 인스턴스를 하나만 생성해서 공유하는 싱글톤 방식은 여러 클라이언트가 하나의 같은 객체 인스턴스를 공유하기 때문에 싱글톤 객체는 상태를 유지(stateful)하게 설계하면 안된다.
  • 무상태(stateless)로 설계해야 한다
    • 특정 클라이언트에 의존적인 필드가 있으면 안된다
    • 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안되며 가급적 읽기만 가능해야 한다.
  • 스프링 빈의 필드에 공유 값을 설정하면 정말 큰 장애가 발생할 수 있다

공유필드는 조심해야 한다! 스프링 빈은 항상 무상태(stateless)로 설계하자.

※ 싱글톤 패턴 추가 참고 : 싱글톤(Singleton) 패턴 - 꼼꼼하게 알아보자 - Inpa Dev

profile
NO EFFORT, NO RESULTS

0개의 댓글