본문 바로가기
스프링 부트

김영한 스프링의 핵심 원리 정리

by illlilillil 2022. 1. 19.

스프링이란


  • 스프링 프레임워크는 자바 플랫폼을 위한 오픈 소스 애플리케이션 프레임워크로서 간단히 스프링이라고도 한다. 동적인 웹 사이트를 개발하기 위한 여러 가지 서비스를 제공하고 있다.

스프링 프레임워크


  • 핵심 기술 : 스프링 DI 컨테이너, AOP, 이벤트
  • 웹 기술: 스프링 MVC, 스프링 WebFlux
  • 데이터 접근 기술: 트랜잭션, JDBC, JPA , XML 지원
  • 기술 통합: 캐시, 이메일, 원격접근, 스케줄링
  • 테스트: 스프링 기반 테스트 지원
  • 언어: 코틀린, 그루비

스프링 부트


  • 스프링을 편리하게 사용할 수 있도록 지원, 최근에는 기본으로 사용
  • Tomcat 같은 웹 서버를 내장해서 별도의 웹 서버를 설치하지 않아도 됨
  • 손쉬운 빌드 구성을 위한 starter 종속성 제공
  • 스프링과 3rd parth(외부) 라이브러리 자동 구성
  • 메트릭, 상태 확인, 외부 구성 같은 프로덕션 준비 기능 제공

스프링의 핵심은


  • 자바 언어 기반의 프레임워크로 좋은 객체 지향 어플리케이션을 만드는 것이 목표

객체 지향의 특징

  1. 추상화
  2. 캡슐화
  3. 상속
  4. 다형성 - 역할(인터페이스)과 구현을 구분하는 것이 핵심

역할과 구현의 분리란

  1. 실세계의 역할과 구현이라는 편리한 컨셉을 다형성을 통해 객체 세상으로 가져올 수 있음
  2. 유연하고, 변경이 용이
  3. 확장 가능한 설계
  4. 클라이언트에 영향을 주지 않는 변경 가능
  5. 인터페이스를 안정적으로 잘 설계하는 것이 중요

좋은 객체 지향의 설계 SOLID

SRP: 단일 책임 원칙(single responsibility principle)


  1. 한 클래스는 하나의 책임만 가져야 한다.
  2. 변경이 있을 때 파급 효과가 적어야 SRP를 잘 지킨 것이다.

OCP: 개방-폐쇄 원칙 (Open/closed principle)


  1. 확장에는 열려 있으나 변경에는 닫혀 있어야 한다.
  2. 다형성을 활용한다.

문제는 구현 객체를 변경 할때 클라이언트 코드를 변경해야 한다. 다형성을 지켰지만 OCP 원칙의 변경에는 위배된다. - 별도의 설정자가 필요

LSP: 리스코프 치환 원칙 (Liskov substitution principle)


  1. 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다
  2. 다형성에서 하위 클래스는 인터페이스 규약을 다 지켜야 한다는 것, 다형성을 지원하기 위한 원칙, 인터페이스를 구현한 구현체는 믿고 사용하려면, 이 원칙이 필요하다

ISP: 인터페이스 분리 원칙 (Interface segregation principle)


  1. 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.
  2. 인터페이스가 명확해지고, 대체 가능성이 높아진다.

DIP: 의존관계 역전 원칙 (Dependency inversion principle)


  1. 쉽게 이야기해서 구현 클래스에 의존하지 말고, 인터페이스에 의존하라는 뜻
  2. 앞에서 이야기한 역할(Role)에 의존하게 해야 한다는 것과 같다. 객체 세상도 클라이언트가 인터페이스에 의존해야 유연하게 구현체를 변경할 수 있다! 구현체에 의존하게 되면 변경이 아주 어려워진다

문제는 MemberRepository m = new MemoryMemberRepository();의 코드가 있을 때 클라이언트가 구현 클래스를 직접 선택한다. 이 때는 인터페이스에 의존하지 않고 구현 클래스에 의존하게 된다.

스프링이 필요한 이유

스프링은 다형성과 앞서 말한 문제가 생기는 DIP, OCP를 가능하게 한다 (DI)

클라이언트 코드 변경 없이 역할에 의존해 기능을 확장한다.

관심사의 분리

하나의 공연이라고 생각했을때 배우는 본인의 역할인 배역 수행에만 집중해야한다.

디카프리오는 상대배역이 누구라도 같은 공연을 해야한다.

공연을 구성하고 배우 지정을 하는것이 공연 기획자이다.

스프링에서는 Config 파일을 통해 구현 객체를 생성, 연결하는 별도의 클래스로 관리한다.

 

ApplicationContext를 스프링 컨테이너라고 한다.

@Configuration 이라는 어노테이션을 설정 정보라고 알려주고 @Bean이 적힌 메서드를 모두 스프링 컨테이너에 등록한다.

이렇게 스프링 컨테이너를 사용하게 되면 어떤 장점이 있을까?

스프링 컨테이너의 생성 과정

스프링 컨테이너의 생성

 

스프링 빈 등록

설정 클래스 정보를 바탕으로 빈을 등록한다.

빈 이름은 메서드의 이름을 사용한다.

 

의존 관계 주입

 

스프링 컨테이너(ApplicationContext)는 설정 정보를 참고해 의존 관계를 주입한다.

 

BeanFactory 와 ApplicationContext

BeanFactory

스프링 컨테이너의 최상위 인터페이스, 빈을 관리하고 조회하는 역할

ApplicationContext

BeanFactory의 기능을 모두 상속 받아 제공

단순히 빈 관리, 조회만으로는 스프링이 제공하는 기능을 사용할 수 없다.

 

 

ApplicationContext의 부가 기능

  • MessageSource 메시지소스를 활용한 국제화 기능
  • EnvironmentCapable 로컬,개발,운영 등을 구분해서 처리
  • ApplicationEventPublisher 이벤트 발생 및 구독 모델 처리
  • ResouceLoader 리소스 조회 기능

스프링 빈 설정 메타 정보 - BeanDefinition

역할과 구현을 나눈 것이다.

우리는 각 설정 파일을 알 필요없이 BeanDefinition만 바라보면 되는 것이다.

싱글톤 컨테이너

스프링이 없는 DI 컨테이너에서 AppConfig는 요청 시마다 새 객체를 생성하게 된다.

요청이 많아질수록 객체 생성 소멸이 많아져 메모리 낭비가 심해진다.

필요한 객체는 딱 한 개만 생성하고 공유하도록 설계한 것이 싱글톤 패턴이다.

싱글톤 패턴

클래스의 인스턴스가 딱 한 개 생성되는 것을 디자인 패턴이다.

private 생성자를 사용해서 외부에서 임의로 new 키워드를 사용하지 못하게 한다.

자원을 효율적으로 사용하고 메모리 낭비도 줄일 수 있다.

그러나 싱글톤 패턴의 문제점도 존재하는데

싱글톤 패턴 구현하는 코드 자체가 많이 들어간다.

클라이언트가 구체 클래스에 의존 - DIP 위반, OCP 위반할 가능성

테스트 어렵다. 내부 속성 변경, 초기화가 어렵다. private으로 자식 클래스를 만들기 어려워 유연성이 떨어진다.

싱글톤 컨테이너

스프링은 이러한 싱글톤 패턴의 단점을 해결하고 유연하게 관리하게 해준다.

그러나 싱글톤은 여러 클라이언트가 같은 인스턴스를 공유하기 때문에 Stateless하게 설계해야 한다. 의존적인 필드가 있으면 안되고, 값을 변경할 수 없어야 한다. 읽기만 가능해야 한다.

컴포넌트 스캔과 의존관계 자동 주입

컴포넌트 스캔은 자동으로 스프링 빈을 등록해주며

의존 관계 주입도 자동으로 해주는 @Autowired 기능도 제공한다.

의존 관계 자동 주입 4가지

  • 생성자 주입 - RequiredConstructor 어노테이션으로 대체
	private final MemberRepository memberRepository;
  private final DiscountPolicy discountPolicy;
 @Autowired //생성자가 한 개만 있으면 생략해도 된다.
 public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy
discountPolicy) {
	 this.memberRepository = memberRepository;
	 this.discountPolicy = discountPolicy;
 }
  • 수정자 주입
 private MemberRepository memberRepository;
 private DiscountPolicy discountPolicy;
 @Autowired
 public void setMemberRepository(MemberRepository memberRepository) {
	 this.memberRepository = memberRepository;
 }
  • 필드 주입 - 사용 X
  • 일반 메서드 주입

빈 생명주기 콜백

스프링은 의존관계 주입이 완료되면 스프링 빈에게 콜백 메서드를 통해서 초기화 시점을 알려주는 다양한 기능을 제공한다.

스프링 빈의 이벤트 라이프사이클 스프링 컨테이너 생성 스프링 빈 생성 의존관계 주입 초기화 콜백 사용 소멸전 콜백 스프링 종료

초기화 콜백: 빈이 생성되고, 빈의 의존관계 주입이 완료된 후 호출 소멸전 콜백: 빈이 소멸되기 직전에 호출

 

스프링은 크게 3가지 방법으로 빈 생명주기 콜백을 지원한다.

인터페이스(InitializingBean, DisposableBean) 설정 정보에 초기화 메서드, 종료 메서드 지정 @PostConstruct, @PreDestory 애노테이션 지원

자주 사용하는 방법인 애노테이션만 알아보겠다.

@PostConstruct 사용하면 라이브러리 시작 후에 실행되어야 할 것들을 수행할 수 있다.

@PreDestroy는 종료 직전에 실행된다.

특징

최신 스프링에서 가장 권장하는 방법이다. 애노테이션 하나만 붙이면 되므로 매우 편리하다. 패키지를 잘 보면 javax.annotation.PostConstruct 이다. 스프링에 종속적인 기술이 아니라 JSR-250 라는 자바 표준이다. 따라서 스프링이 아닌 다른 컨테이너에서도 동작한다. 컴포넌트 스캔과 잘 어울린다. 유일한 단점은 외부 라이브러리에는 적용하지 못한다는 것이다. 외부 라이브러리를 초기화, 종료 해야 하면 @Bean의 기능을 사용하자.

 

 

 

 

김영한 핵심 원리를 수강하고 참고하였습니다.

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8

댓글