[강의] 김영한님의 스프링 핵심원리 - 기본편 정리

크리링·2022년 9월 14일
1
post-thumbnail

1. 객체 지향 설계와 스프링

스프링이란?

스프링은 하나가 아닌 여러 가지
스프링 프레임워크, 스프링 부트, 스프링 데이터, 스프링 시큐리티, 스프링 세션, 스프링 배치, 스프링 클라우드 …

스프링 프레임워크

  • 핵심 기술 : 스프링 DI 컨테이너, AOP, 이벤트, 기타
  • 웹 기술 : 스프링 MVC, 스프링 WebFlux
  • 데이터 접근 기술 : 트랜잭션, JDBC, ORM 지원, XML 지원
  • 기술 통합 : 캐시, 이메일, 원격 접근, 스케줄링
  • 테스트 : 스프링 기반 테스트 지원
  • 언어 : 코틀린, 그루비
    최근에는 스프링 부트를 통해서 스프링 프레임워크의 기술들을 편리하게 사용

스프링 부트

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



스프링 단어?

  • 스프링이라는 단어는 문맥에 따라 다르게 사용된다.
    * 스프링 DI 컨테이너 기술
    • 스프링 프레임워크
    • 스프링 부트, 스프링 프레임워크 등을 모두 포함한 스프링 생태계



스프링의 핵심 개념, 컨셉

  • 스프링은 자바 언어 기반의 프레임워크
  • 자바 언어의 가장 큰 특징 - 객체 지향 언어
  • 스프링은 객체 지향 언어가 가진 강력한 특징을 살려내는 프레임워크
  • 스프링은 좋은 객체 지향 애플리케이션을 개발할 수 있게 도와주는 프레임워크




좋은 객체 지향 프로그래밍?

  • 추상화

  • 캡슐화

  • 상속

  • 다형성

  • 객체지향 프로그래밍은 컴퓨터 프로그램을 명령어의 목록으로 보는 시각에서 벗어나 여러개의 독립된 단위, 즉 "객체"들의 모임으로 파악하고자 하는 것이다. 각각의 객체는 메시지를 주고받고, 데이터를 처리할 수 있다.

  • 객체 지향 프로그래밍은 프로그램을 유연하고 변경이 용이하게 만들기 때문에 대규모 소프트웨어 개발에 많이 사용된다.



유연하고 변경이 용이?

  • 레고 블럭 조립하듯이
  • 키보드, 마우스 갈아 끼우듯
  • 컴포넌트를 쉽고 유연하게 변경하면서 개발할 수 있는 방법



다형성

역할과 구현을 분리

  • 역할과 구현으로 구분하면 세상이 단순해지고, 유연해지며 변경도 편리해진다.
  • 장점
    * 클라이언트는 대상의 역할(인터페이스)만 알면 된다.
    • 클라이언트는 구현 대상의 내부 구조를 몰라도 된다.
    • 클라이언트는 구현 대상의 내부 구조가 변경되어도 영향을 받지 않는다.
    • 클라이언트는 구현 대상 자체를 변경해도 영향을 받지 않는다.



자바 언어

  • 자바 언어의 다형성을 활용
    * 역할 - 인터페이스
    • 구현 - 인터페이스를 구현한 클래스, 구현 객체
  • 객체를 설계할 때 역할과 구현을 명확히 분리
  • 객체 설계시 역할(인터페이스)을 먼저 부여하고, 그 역할을 수행하는 구현 객체 만들기



객체의 협력이라는 관계부터 생각

  • 혼자 있는 객체는 없다.
  • 클라이언트 : 요청 , 서버 : 응답
  • 수 많은 객체 클라이언트와 객체 서버는 서로 협력 관계를 가진다.



자바 언어의 다형성

  • 오버라이딩을 떠올려보자
  • 오버라이딩은 자바 기본 문법
  • 오버라이딩 된 메서드가 실행
  • 다형성으로 인터페이스를 구현한 객체를 실행 시점에 유연하게 변경할 수 있다.
  • 물론 클래스 송속 관계도 다형성, 오버라이딩 적용 가능



다형성의 본질

  • 인터페이스를 구현한 객체 인터페이스를 실행 시점에 유연하게 변경할 수 있다.
  • 다형성의 본질을 이해하려면 협력이라는 객체 사이의 관계에서 시작해야함
  • 클라이언트를 변경하지 않고, 서버의 구현 기능을 쉽게 변경할 수 있다.



역할과 구현을 분리

정리

  • 유연하고, 변경이 용이
  • 확장 가능한 설계
  • 클라이언트에 영향을 주지 않는 변경 가능
  • 인터페이스를 안정적으로 잘 설계하는 것이 중요

한계

  • 역할 자체가 변하면, 클라이언트, 서버 모두에 큰 변경이 발생한다.
  • 대본 자체가 변경된다면?
  • 인터페이스를 안정적으로 잘 설계하는 것이 중요



스프링과 객체 지향

  • 다형성이 가장 중요
  • 스프링은 다형성을 극대화해서 이용할 수 있게 도와준다.
  • 스프링에서 이야기하는 제어의 역전 (IoC), 의존관계 주입(DI)은 다형성을 활용해서 역할과 구현을 편리하게 다룰 수 있도록 지원




좋은 객체 지향 설계의 5가지 원칙 (SOLID)

  • SRP : 단일 책임 원칙(Single Responsibility Principle)
  • OCP : 개방-폐쇄 원칙(Open/Closed Principle)
  • LSP : 리스코프 치환 원칙(Liskov substitution Principle)
  • ISP : 인터페이스 분리 원칙(Interface segregation Principle)
  • DIP : 의존관계 역전 원칙(Dependency Inversion Principle)

SRP 단일 책임 원칙

  • 한 클래스는 하나의 책임만 가져야 한다.
  • 하나의 책임은 모호
    * 클 수도 작을수도
    • 문맥과 상황 따라 다름
  • 중요한 기준은 변경, 변경이 있을 때 파급 효과가 적으면 단일 책임 원칙 잘 따른 것
  • 예) UI 변경, 객체의 생성과 사용을 분리

OCP 개방-폐쇄 원칙

  • 소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다.
  • 다형성 활용
  • 인터페이스를 구현한 새로운 클래스를 하나 만들어서 새로운 기능을 구현
  • 구현 객체를 변경하려면 클라이언트 코드를 변경해야 한다.

LSP 리스코프 치환 원칙

  • 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.
  • 다형성에서 하위 클래스는 인터페이스 규약을 다 지켜야 한다는 것, 다형성을 지원하기 위한 원칙, 인터페이스를 구현한 구현체는 믿고 사용하려면, 이 원칙 필요
  • 예) 자동차 인터페이스는 엑셀은 앞으로 가라 기능, 뒤로 가게 구현하면 LSP 위반, 느리더라도 앞으로 가야함

ISP 인터페이스 분리 원칙

  • 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다
  • 자동차 인터페이스 -> 운전 인터페이스, 정비 인터페이스로 분리
  • 사용자 클라이언트 -> 운전자 클라이언트, 정비사 클라이언트로 분리
  • 분리하면 정비 인터페이스 자체가 변해도 운전자 클라이언트에 영향을 주지 않음
  • ㅂ인터페이스가 명확해지고, 대체 가능성이 높아진다.

DIP 의존 관계 역전 원칙

  • 프로그래머는 "추상화에 의존해야지, 구체화에 의존하면 안된다." 의존성 주입은 이 원칙을 따르는 방법
  • 구현 클래스에 의존하지 말고, 인터페이스에 의존하라
  • 역할에 의존하게 해야한다 객체 세상도 클라이언트가 인터페이스에 의존해야 유연하게 구현체를 변경할 수 있다. 구현체에 의존하게 되면 변경이 아주 어려워진다.

MemberService는 MemoryMemberRepository와 의존 관계가 있을 때, MemoryMemberRepository를 바꿀 때 코드 변경이 불가피 해진다.

  • MemberService는 인터페이스에 의존하지만, 구현 클래스도 동시에 의존한다.
  • MemberService 클라이언트가 구현 클래스를 직접 선택
    * MemberRepository m = new MemoryMemberRepository();
  • DIP 위반



정리

  • 객체 지향의 핵심은 다형성
  • 다형성 만으로는 쉽게 부품을 갈아 끼우듯이 개발 힘듬
  • 다형성 만으로는 구현 객체를 변경할 때 클라이언트 코드도 함께 변경된다.
  • 다형성 만으로는 OCP, DIP를 지킬 수 없다.
  • 뭔가 더 필요하다.




객체 지향 설계와 스프링

스프링은 다음 기술로 다형성 + OCP, DIP를 가능하게 지원

DI(Dependency Injection) : 의존 관계, 의존성 주입
DI 컨테이너 제공

클라이언트 코드의 변경 없이 기능 확장
쉽게 부품을 교체하듯이 개발



정리

  • 모든 설계에 역할구현을 분리하자.
  • 애플리케이션 설계도 공연을 설계 하듯이 배역만 만들어두고, 배우는 언제든지 유연하게 변경할 수 있도록 만드는 것이 좋은 객체 지향 설계다.
  • 이상적으로는 모든 설계에 인터페이스를 부여하자







IoC, DI, 그리고 컨테이너

제어의 역전 IoC(Inversion of Control)

  • 기존 프로그램은 클라이언트 구현 객체가 스스로 필요한 서버 구현 객체를 생성하고, 연결하고, 실행했다. 한마디로 구현 객체가 프로그램의 제어 흐름을 스스로 조종
  • 프로그램에 대한 제어 흐름에 대한 권한은 모두 AppConfig가 가지고 있다. 심지어 'OrderServiceImpl'도 AppConfig가 생성한다. 그리고 AppConfig는 'OrderServiceImpl'이 아닌 OrderService 인터페이스의 다른 구현 객체를 생성하고 실행할 수도 있다. 그런 사실도 모른체 OrderServiceImpl은 묵묵히 자신의 로직을 실행
  • 프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것을 제어의 역전(IoC)라고 한다.

프레임워크 vs 라이브러리

  • 프레임워크가 내가 작성한 코드를 제어하고, 대신 실행하면 프레임워크가 맞다.
  • 반면에 내가 작성한 코드가 직접 제어의 흐름을 담당하면 그것은 프레임워크가 아니라 라이브러리다.

의존관계 주입 DI(Dependency Injection)

  • OrderServiceImpl은 DiscountPolicy 인터페이스에 의존한다.
  • 의존관계는 정적인 클래스 의존관계와 실행 시점에 결정되는 동적인 객체(인스턴스) 의존 관계 둘을 분리해서 생각해야 한다.

정적인 클래스 의존관계

  • 클래스가 사용하는 import 코드만 보고 의존관계를 쉽게 판단할 수 있다. 정적인 의존관계는 애플리케이션을 실행하지 않아도 분석할 수 있다.
  • OrderServiceImpl은 MemberRepository, DiscountPolicy에 의존한다는 것을 알 수 있다. 그런데 이러한 클래스 의존관계 만으로는 실제 어떤 객체가 OrderServiceImpl에 주입될지 알 수 없다.

동적인 객체 인스턴스 의존관계

  • 애플리케이션 실행 시점에 실제 생서된 객체 인스턴스의 창조가 연결된 의존 관계다.
  • 애플리케이션 실행 시점(런타임) 에 외부에서 실제 구현 객체를 생성하고 클라이언트에 전달해서 클라이언트와 서버의 실제 의존관계가 연결되는 것을 의존관계 주입이라 한다.
  • 의존관계 주입을 사용하면 클라이언트 코드를 변경하지 않고, 클라이언트가 호출하는 대상의 타입 인스턴스를 변경할 수 있다.
  • 의존관계 주입을 사용하면 정적인 클래스 관계가 변경하지 않고, 동적인 객체 인스턴스 의존관계를 쉽게 변경할 수 있다.

IoC 컨테이너, DI 컨테이너

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




스프링 컨테이너

  • ApplicationContext 를 스프링 컨테이너라고 한다.
  • 기존에는 개발자가 AppConfig를 사용해서 직접 객체를 생성하고 DI를 했지만, 이제는 스프링 컨테이너를 통해서 사용
  • 스프링 컨테이너는 @Configuration 이 붙은 AppConfig를 설정 정보로 사용한다. 여기서 @Bean이라 적힌 메서드를 모두 호출해서 반환된 객체를 스프링 컨테이너에 등록한다. 이렇게 스프링 컨테이너에 등록된 객체를 스프링 빈이라 한다.
  • 스프링 빈은 @Bean이 붙은 메서드의 명을 스프링 빈의 이름으로 사용한다. (memberService, orderService)
  • 이전에는 개발자가 필요한 객체를 AppConfig 를 사용해서 직접 조회했지만, 이제부터는 스프링 컨테이너를 통해서 필요한 스프링 빈(객체)를 찾아야 한다. 스프링 빈은 applicationContext.getBean() 메서드를 사용해서 찾을 수 있다.
  • 기존에는 개발자가 직접 자바코드로 모든 것을 했다면 이제부터는 스프링 컨테이너에 객체를 스프링 빈으로 등록하고, 스프링 컨테이너에서 스프링 빈을 찾아서 사용하도록 변경되었다.







싱글톤 컨테이너

웹 애플리케이션과 싱글톤

  • 스프링은 태생이 기업용 온라인 서비스 기술을 지원하기 위해 탄생
  • 대부분의 스프링 애플리케이션은 웹 애플리케이션
  • 웹 애플리케이션은 보통 여러 고객이 동시에 요청한다.

  • 우리가 이전에 만들었던 스프링 없는 순수한 DI 컨테이너인 AppConfig는 요청 할 때마다 객체를 새로 생성한다.
  • 고객 트래픽이 초당 100이 나오면 초당 100개 객체가 생성되고 소멸된다. --> 메모리 낭비가 심하다
  • 해결 방안은 해당 객체가 딱 1개만 생성되고, 공유하도록 설계 ---> 싱글톤 패턴




싱글톤 패턴

  • 클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는 디자인 패턴
  • 그래서 객체 인스턴스를 2개 이상 생성하지 못하도록 막아야 한다.

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

싱글톤 패턴 문제점

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




싱글톤 컨테이너

스프링 컨테이너는 싱글톤 패턴의 문제점을 해결하면서, 객체 인스턴스를 싱글톤으로 관리한다.
지금까지 우리가 학습한 스프링 빈이 바로 싱글톤으로 관리되는 빈이다.

싱글톤 컨테이너

  • 스프링 컨테이너는 싱글톤 패턴을 적용하지 않아도, 객체 인스턴스를 싱글톤으로 관리한다.
    * 컨테이너는 객체를 하나만 생성해서 관리한다.
  • 스프링 컨테이너는 싱글톤 컨테이너 역할을 한다. 이렇게 싱글톤 객체를 생성하고 관리하는 기능을 싱글톤 레지스트리라 한다.
  • 스프링 컨테이너의 이런 기능 덕분에 싱글톤 패턴의 모든 단점을 해결하면서 객체를 싱글톤으로 유지할 수 있다.
    * 싱글톤 패턴을 위한 지저분한 코드가 들어가지 않아도 된다.
    • DIP, OCP, 테스트, private 생성자로부터 자유롭게 싱글톤을 사용할 수 있다.

  • 스프링 컨테이너 덕분에 고객의 요청이 올 때마다 객체를 생성하는 것이 아니라, 이미 만들어진 객체를 공유해서 효율적으로 재사용이 가능해진다.




싱글톤 방식의 주의점

  • 싱글톤 패턴이든, 스프링 같은 싱글톤 컨테이너를 사용하든, 하나의 같은 인스턴스를 공유하기 때문에 싱글톤 객체는 상태를 유지하게 설계하면 안된다.
  • 무상태(Stateless)로 설계해야 한다.
    * 특정 클라이언트에 의존적인 필드가 있으면 안된다.
    • 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안된다.
    • 가급적 읽기만 가능해야 한다.
    • 필드 대신에 자바에서 공유되지 않는, 지역변수, 파라미터, ThreadLocal 등을 사용해야 한다.
  • 스프링 빈의 필드에 공유 값을 설정하면 정말 큰 장애가 발생할 수 있다.

  • ThreadA가 호출하고, ThreadB가 호출한다 가정했을 때 A 입장에서는 다른 결과가 나타난다.
  • 공유 필드는 조심해야 한다.(무상태 설계)







컴포넌트 스캔

컴포넌트 스캔과 의존관계 자동 주입 시작하기

  • 지금까지 스프링 빈을 등록할 때는 자바 코드의 @Bean이나 XML의 bean 등을 통해서 설정 정보에 직접 등록할 스프링 빈을 나열했다.
  • 예제에서는 몇개가 안되었지만, 이렇게 등록해야 할 스프링 빈이 수십, 수백개가 되면 일일이 등록하기도 귀찮고, 설정 정보도 커지고, 누락하는 문제도 발생한다.
  • 그래서 스프링 설정 정보가 없어도 자동으로 스프링 빈을 등록하는 컴포넌트 스캔이라는 기능을 제공한다.
  • 또 의존 관계도 자동으로 주입하는 @Autowired라는 기능도 제공한다.

컴포넌트 스캔은 이름 그대로 @Component 애노테이션이 붙은 클래스를 스캔해서 스프링 빈으로 등록한다. @Component 를 붙여주자




탐색 위치와 기본 스캔 대상

탐색할 패키지의 시작 위치 지정

모든 자바 클래스를 다 컴포넌트 스캔하면 시간이 오래 걸린다. 그래서 꼭 필요한 위치부터 탐색하도록 시작 위치를 지정할 수 있다.

권장하는 방법 : 패키지 위치를 지정하지 않고, 설정 정보 클래스의 위치를 프로젝트 최상단에 두는 것이다.

  • com.hello
  • com.hello.service
  • com.hello.repository

com.hello -> 프로젝트 시작 루트, 여기에 AppConfig 같은 메인 설정 정보를 두고, @ComponentScan 애노테이션을 붙인다.

컴포넌트 스캔 기본 대상

  • @Component : 컴포넌트 스캔에서 사용
  • @Controller : 스프링 MVC 컨트롤러 에서 사용
  • @CService : 스프링 비즈니스 로직에서 사용
  • @Repository : 스프링 데이터 접근 계층에서 사용
  • @Configuration : 스프링 설정 정보에서 사용

참고 : 애노테이션에는 상속관계라는 것이 없다.

컴포넌트 스캔의 용도 뿐만 아니라 다음 애노테이션이 있으면 스프링은 부가 기능을 수행한다.

  • @Controller : 스프링 MVC 컨트롤러 인식
  • @CService : 특별한 처리를 하지 않는다. 대신 개발자들이 핵심 비즈니스 로직이 여기 있겠구나 인식
  • @Repository : 스프링 데이터 접근 계층으로 인식하고, 데이터 계층의 예외를 스프링 예외로 변환해준다.
  • @Configuration : 스프링 설정 정보라고 인식하고, 스프링 빈이 싱글톤을 유지하도록 추가 처리 한다.







의존관계 자동 주입

다양한 의존관계 주입 방법

  • 생성자 주입
  • 수정자 주입(setter 주입)
  • 필드 주입
  • 일반 메서드 주입

생성자 주입

이름 그대로 생성자를 통해서 의존 관계를 주입 받는 방법

  • 특징
    * 생성자 호출 시점에 딱 1번만 호출되는 것이 보장된다.
    • 불편, 필수 의존 관계에 사용

수정자 주입

setter라 불리는 필등의 값을 변경하는 수정자 메서드를 통해서 의존관계를 주입하는 방법이다.

  • 특징
    * 선택, 변경 가능성이 있는 의존관계에 사용
    • 자바빈 프로퍼티 규약의 수정자 메서드 방식을 사용하는 방법이다.

필드 주입

이름 그대로 필드에 바로 주입하는 방법

  • 특징
    * 코드가 간결해서 많은 개발자들을 유혹하지만 외부에서 변경이 불가능해서 테스트 힘들다는 단점이 있다.
    • DI 프레임워크가 없으면 아무것도 할 수 없다.
    • 사용하지 말자!

일반 메서드 주입

일반 메서드를 통해서 주입 받을 수 있다.

  • 특징
    * 한번에 여러 필드를 주입 받을 수 있다.
    • 일반적으로 잘 사용하지는 않는다.




옵션 처리

주입할 스프링 빈이 없어도 동작해야 할 때가 있다.
그런데 @Autowired 만 사용하면 required 옵션의 기본값이 true로 되어 있어서 자동 주입 대상이 없으면 오류가 발생한다.

  • 자동 주입 대상을 옵션으로 처리하는 방법
    * @Autowired(required=false) : 자동 주입할 대상이 없으면 수행자 메서드 자바가 호출 안됨.
    • org.springframework.lang.@Nullable : 자동 주입할 대상이 없으면 null이 입력
    • Optional<> : 자동 주입할 대상이 없으면 Optional.empth가 입력된다.




생성자 주입 권장

불변

  • 대부분의 의존관계 주입은 한번 일어나면 애플리케이션 종료 시점까지 의존관계를 변경할 일이 없다. 오히려 대부분의 의존관계는 애플리케이션 종료 전까지변하면 안된다.(불변해야 한다)
  • 수정자 주입을 사용하면 setXxx메서드를 public으로 열어둬야 한다.
  • 누군가 실수로 변경할 수도 있고, 변경하면 안되는 메서드를 열어두는 것은 좋은 설계 방법이 아니다.
  • 생성자 주입은 객체를 생성할 때 딱 1번만 호출되므로 이후에 호출되는 일이 없다. 따라서 불변하게 설계할 수 있다.

final 키워드
생성자 주입을 사용하면 필드에 final 키워드를 사용할 수 있다. 그래서 생성자에서 혹시라도 같이 설정되지 않는 오류를 컴파일 시점에 막아준다.

정리

  • 생성자 주입 방식을 선택하는 이유는 여러가지 있지만, 프레임워크에 의존하지 않고, 순수한 자바 언어의 특징을 잘 살리는 방법
  • 기본으로 생성자 주입을 사용하고, 필수 값이 아닌 경우에는 수정자 주입 방식을 옵션으로 부여하면 된다. 생성자 주입과 수정자 주입을 동시에 사용할 수 있다.

롬복

@RequiredArgsConstructor

  • 객체 내에 final 붙은 필드를 모아서 생성자를 자동으로 만들어준다.(코드에는 보이지 않음)







빈 생명주기 콜백

스프링 빈은 간단하게 다음과 같은 라이프 사이클을 가진다.
객체 생성 -> 의존관계 주입

스프링 빈은 객체를 생성하고, 의존관계 주입이 다 끝난 다음에야 필요한 데이터를 사용할 수 있는 준비가 완료된다. 따라서 초기화 작업은 의존관계 주입이 모두 완료되고 난 다음에 호출해야 한다. 그런데 개발자가 의존 관계 주입이 모두 완료된 시점을 어떻게 알 수 있을까?

스프링은 의존관계 주입이 완료되면 스프링 빈에게 콜백 메서드를 통해서 초기화 시점을 알려주는 다양한 기능을 제공한다. 또한 스프링은 스프링 컨테이너가 종료되기 직전에 소멸 콜백을 준다. 따라서 안전하게 진행할 수 있다.

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

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

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

  • 인터페이스
  • 설정 정보에 초기화 메서드, 종료 메서드 지정
  • @PostConstruct, @PreDestroy 애노테이션 지원







빈 스코프

스프링 빈이 스프링 컨테이너의 시작과 함꼐 생성되어서 스프링 컨테이너가 종료될 때까지 유지된다고 학습했다. 이것은 스프링 빈이 기본적으로 싱글톤 스코프로 생성되기 때문이다. 스코프는 번역 그대로 빈이 존재할 수 있는 범위를 뜻한다.

스프링은 다음과 같은 다양한 스코프를 지원한다.

  • 싱글톤 : 기본 스코프, 스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위의 스코프
  • 프로토타입 : 스프링 컨테이너는 프로토타입 빈의 생성과 의존관계 주입까지만 관여하고 더는 관리하지 않는 매우 짧은 범위의 스코프이다.
  • 웹 관련 스코프
    * request : 웹 요청이 들어오고 나갈 때까지 유지되는 스코프
    • session : 웹 세션이 생성되고 종료될 때 까지 유지되는 스코프
    • application : 웹의 서블릿 컨텍스와 같은 범위로 유지되는 스코프







후기

이전에 한번 들었던 강의였지만 그 때는 코드를 따라가는데 집중했어서 그런지 새로운 내용으로 느껴지는 부분이 많았다. 스코프나 빈 생명주기, DI에 대한 정확한 개념 또한 잘 숙지하지 못하고 스프링의 장점이 무엇인지도 생각하지 않고 나름의 프로젝트 들을 스프링으로 진행했었다. 그러면서도 스프링의 기본조차 잘 몰랐다는 것이 개탄스럽다. 아직 완벽히 숙지하지는 못했지만 주기적인 복습을 통해 기본을 올바르게 숙지해내야겠다.

0개의 댓글