중간시험이 모두 끝나고 4, 5주차를 한꺼번에 공부하게 되었다...
앞의 1,2,3주차에서는 Spring boot를 배우기 전에 백엔드 개발을 위한 필수적인 사전 지식을 공부하였다면, 이번 주차부터는 본격적으로 Spring boot에 대해 배운다.
엔터프라이즈용 Java 애플리케이션 개발을 편하게 할 수 있게 해주는 오픈소스 경량급 애플리케이션 프레임워크
프레임워크란, 어떠한 목적을 쉽게 달성할 수 있도록 해당 목적과 관련된 코드의 뼈대를 미리 만들어 둔 것. (프레임 위에서의 작업)
API는 2개 이상의 소프트웨어 컴포넌트 사이에서 상호작용 할 수 있도록 정의된 인터페이스를 말합니다. 즉, 다른 개발자들이 사용할 수 있도록 함수나 메서드, 클래스를 정의하는 것입니다.
라이브러리와 API를 혼동하기 쉬운데, 실제 개발을 할 때는 여러 컴포넌트를 합쳐서 개발을 하게 되고, 각각의 컴포넌트들은 API를 가지고 있습니다. 이때 많은 컴포넌트들이 라이브러리의 형태로 제공되기 때문에 API와 라이브러리를 혼동할 수 있습니다. 하지만, 라이브러리는 컴포넌트 자체를 의미하고, API는 그 컴포넌트를 활용하기 위한 규약입니다.
프레임워크는 응용프로그램이나 소프트웨어 구현을 수월하게 하기 위해 제공된 소프트웨어 환경이다. 라이브러리는 우리가 선택적으로 끌어다 쓰는 것과 달리 프레임워크는 프레임워크에 의존하여 개발해야하고, 프레임워크가 정의한 규칙을 준수해야한다.
Spring IoC (제어의 역전: Inversion of Control) 컨테이너는 객체의 생성과 관리를 개발자가 아닌 Spring 프레임워크가 직접 담당하는 개념입니다. 개발자는 필요한 객체만 선언해 두고, Spring이 알아서 적절한 객체를 주입해주는 형태이죠.
👉🏻 그러므로 '제어의 역전'은 객체의 생성, 생명주기의 관리까지 모든 객체에 대한 제어권이 개발자에서 컨테이너에게로 바뀌었다는 뜻입니다.
컨테이너: 컨테이너는 보통 객체의 생명주기를 관리, 생성된 인스턴스들에게 추가적인 기능을 제공하도록 하는 것이다.
객체를 class로 정의합니다.
객체들 간의 연관성을 지정: Spring설정 파일(Config) 또는 어노테이션(@Component, @Cinfiguration, @Autowired, @Bean)을 통해 객체들이 어떻게 연결될지 (의존성 주입) 지정해줍니다.
IoC 컨테이너가 이 정보를 바탕으로 객체들을 생성하고 필요한 곳에 주입합니다.
👉🏻 POJO (Plain Old Java Object) 기반의 개발을 가능하게 한다!
: 복잡한 라이브러리나 프레임워크에 의존/종속적 X, 순수한 자바 객체
spring 컨테이너가 관리하는 자바의 객체를 의미.
ApplicationContext.getBean() 함수를 호출하면 얻어지는 것이 Sping의 빈이다.
Bean을 통해 객체를 인스턴스화 -> 객체 간의 의존 관계 관리
객체 생성할 때,
new 로 객체 직접 생성 -> 객체 생성과 의존성 관리를 개발자가 전적으로 책임져야 함.
그러므로 아래와 같이 빈으로 등록하여 관리하는 방법이 있다.
1) @Component -> @Autowired : 묵시적 Bean 정의
클래스에 Component 어노테이션 추가하여 Spring이 자동으로 해당 클래스를 스캔하고 Bean으로 등록하도록 한다. 그리고 Autowired로 다른 클래스에서 해당 Bean을 끌어온다.
2) @Configuration -> @Bean : 명시적 Bean 정의
Spring 설정 파일에 Configuration 어노테이션을 추가하고, Bean 어노테이션을 붙여 명시적으로 빈을 지정한다.
'의존성 주입'은 객체 인스턴스에 설정된 속성을 통해서만 종속성을 정의하는 프로세스
Spring 컨테이너는 빈을 생성할 때, 이러한 종속성을 주입한다.
이 프로세스는 기본적으로 클래스를 직접 생성, service locater 패턴을 사용하여 종속성의 인스턴스화 또는 위치를 스스로 제어하는 것과는 전혀 반대이다. (Inversion of Control)
DI 원칙을 사용하면,
👉🏻 코드가 더 깔끔해지고, 객체에 종속성이 제공되어 분리가 더 효과적이다.
개발을 하며 만드는 객체들은 서로 의존되어있을 수밖에 없다. 필연적으로 서로 의존성을 지닐 수 밖에 없기 때문에 객체들 간에 결합이 생긴다.
CoffeeBeans를 인터페이스로 구현
public interface CoffeeBeans {
void grind();
}
인터페이스를 구현하는 구현체 클래스를 만든다.
public static class RegularCoffeeBeans implements CoffeeBeans{
@Override
public void grind(){
System.out.println("기본 원두로 가는 중")
}
}
다른 원두를 쓰고 싶을 때는 '디카페인'이라는 구현체 클래스를 하나 정의
public static class DecafCoffeeBeans implements CoffeeBeans {
@Override
public void grind() {
System.out.println("디카페인 원두로 가는 중")
}
}
👉🏻 이처럼 각 객체끼리의 의존성이 존재할 때, 의존성을 외부에서 주입받는 것이 바람직합니다.
그리고, Spring은 이런 의존성 주입을 자동화해줌으로써, 개발자의 부담을 줄여줍니다.
1️⃣ 생성자 주입 (Constructor Injection) - 스프링 공식 문서 권장 방식
2️⃣ setter 주입 (Setter Injection) - 의존 관계가 선택적이거나 변경 가능한 경우
3️⃣ 필드 주입 (Field Injection)
생성자 주입은 생성자를 통해서 의존 관계를 주입받는 방법이다.
생성자에 @Autowired를 하면 스프링 컨테이너에 @Component로 등록된 빈에서 생성자에 필요한 빈들을 주입한다.
👉🏻 객체가 생성될 때 필요한 의존성을 모조리 설정해버린다.
이는 객체의 불변성(immunability)를 보장해준다.
@Service
public class MemberSignupService {
private final MemberRepository memberRepository;
@Autowired
public MemberSignupService(final MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
...
}
생성자 호출 시점에 1번만 호출되는 것을 보장.
객체 생성될 때 필수적인 의존성(필드값)들을 보장. (의존성 없는 객체 X)
주입받을 필드를 final로 선언 가능하다.
런타임에 의존성을 주입하기 때문에, 의존성이 없더라도 객체 생성이 가능.
선택적으로 의존성 주입이 가능
NullPointException 에러가 발생할 수 있다는 매우 치명적 단점!
Setter 주입 방식과 마찬가지로, 런타임에 의존성을 주입하기 때문에 의존성이 없더라도 객체가 생성될 수 있다.
필드에 직접 주입되기 때문에 테스트 중에 의존성을 주입하는 것이 어렵다는 단점이 있다.
의존성이 명시적으로 드러나지 않기 때문에 구조를 이해하기 어렵다.
-> Bean들 간의 순환 참조 문제가 발생할 수 있다.
[참고 자료]
https://ittrue.tistory.com/227
웹 어플리케이션에서 클라이언트의 요청을 처리, 그에 대한 응답을 생성
Servlet을 관리해주는 컨테이너 역할
Front Controller 패턴을 구현한 서블릿.
모즌 HTTP 요청을 받는 서블릿이다.
[참고 자료]
Spring MVC 동작 구조 : https://iri-kang.tistory.com/4