Di는 Dependency injection으로 의존성 주입의 뜻을 갖고 있다.
Java의 여러 소프트웨어 설계 패턴 중 하나로,
객체 간의 의존성을 외부에서 주입받는 방식을 의미한다.
Di를 사용하면 객체 간의 의존성을 직접 생성하지 않고, 외부에서 주입할 수 있다.
이를 통해 객체 간의 결합도를 줄이고 유지보수성을 향상시킬 수 있다.
다음과 같은 커피 원두 클래스가 있다고 가정하자
public static class CoffeeBeans{
public void grind(){
System.out.println("기본 원두로 갈고 있습니다");
}
}
커피 머신 클래스도 존재한다
public static class CoffeeMachine{
private CoffeeBeans coffeeBeans;
public CoffeeMachine(){
this.coffeeBeans = new CoffeeBeans();
}
public void brew(){
coffeeBeans.grind();
System.out.println("기본 원두로 커피를 내립니다.");
}
}
이 경우 brew를 통해 커피를 내리려면 커피머신에서 커피원두라는 필드값을 이전에 정의했던 CoffeeBeans클래스에서 가져와야 한다.
이렇게 코드를 작성하는 스타일을 '강한 결합(Tight Coupling)'이라고 한다.
이렇게 되면 오로지 '기본 원두'만을 갈 수 있는 '원두 일체형' 커피 머신이 된다.
만약, 디카페인 원두를 사용하고 싶다면 CoffeeBeans 클래스 뿐만 아니라 CoffeeMachine 클래스도 수정해야한다.
이에 반해, '느슨한 결합(Loose Coupling)'은 원두 분리형 커피머신이 된다.
우선, CoffeeBeans를 인터페이스로 정의하고
public interface CoffeeBeans {
void grind();
}
이후, CoffeeBeans 인터페이스를 구현하는 구현체 클래스를 만든다
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) 생성자 주입 (⭐️)
: 객체가 생성될 때 생성자를 통해 필요한 의존성을 주입받는 방식
: 생성자 인수를 통해 필요한 객체를 받아와서 객체가 생성될 때 모든 의존성이 명확하게 설정된다
: 또한, 객체의 불변성을 보장해준다
@Service
public class MemberSignupService {
private final Member Repository memberRepository;
@Autowired
public MemberSignupService(final MemberRepository memberRepository){
this.memberRepository = memberRepository;
}
/*나머지 로직들*/
}
2) Setter 주입
: Setter 주입은 런타임에 의존성을 주입, 의존성이 없더라도 객체가 생성된다
1.주입 받으려는 빈의 생성자를 호출, 빈을 찾거나 빈 팩토리에 등록
2.생성자 인자에 사용하는 빈을 찾거나 생성
3. Setter의 인자로 주입
@Service
public class MemberSignupService {
private final MemberRepository memberRepository;
@Autowired
public void setMemberSignupService(final MemberRepository memberRepository){
this.memberRepository = memberRepository;
}
/* 나머지 로직들 */
}
: 선택적 의존성이 있을 때 유용하다.
: 대신 필수적 의존성이 주입되어야 할 때 이를 강제할 수 없다.
3)Field 주입
: 필드 주입 방식 역시 Setter 주입 방식과 마찬가지로
런타임에 의존성 주입, 의존성이 없더라도 객체가 생성될 수 있다.
1. 주입받으려는 빈의 생성자를 호출하여 빈을 찾거나 빈 팩토리에 등록
2. 생성자 인자에 사용하는 빈을 찾거나 생성
3. 필드에 주입
@Service
public class MemberSignupSerivce{
@Autowired
private MemberRepository memberRepository;
/* 나머지 로직들*/
}
: 코드가 깔끔하고 단순해지지만 필드에 직접 주입되어 테스트 중에 의존성을 주입하는 것이 어렵다
: 명시적으로 드러나는 의존성이 없어, 의존성 구조를 이해하기 어렵다
-> 의존성 관계가 드러나지 않아 Bean들 간의 순환 참조 문제가 발생할 수 있다
Spring MVC 패턴에서 Controller에 해당하는 부분이다
Spring Servlet은 스프링 프레임워크에서 웹 애플리케이션을 구축할 때 사용하는 핵심적인 구성 요소로, 주로 Spring MVC의 일부로서 작동한다. Spring Servlet은 클라이언트의 HTTP 요청을 처리하고 적절한 응답을 제공하기 위해 서블릿(Servlet)을 사용한다.
서블릿(Servlet)이란?
웹 애플리케이션에서 클라이언트의 요청을 처리하고, 그에 대한 응답을 생성하는 중요한 구성요소이다.
즉, 자바에서 웹 애플리케이션을 만들 때 HTTP 요청을 처리하는 역할을 한다.

Servlet Container란?
Servlet을 관리해주는 컨테이너 역할이다.
Servlet이 어떤 역할을 수행하는 매뉴얼이라면,
Servelt Container는 해당 메뉴얼을 보고 직접 핸들링한다.
서블릿 컨테이너는 클라이언트의 요청을 받아주고 응답할 수 있도록, 웹서버와 Socket으로 통신한다.
Spring의 DispatcherServlet이란?
Spring에서 가장 중요한 서블릿은 DispatcherServlet이다.
이는 FrontController 패턴을 구현한 서블릿, 모든 HTTP 요청을 받는다.
요청은 적절한 컨트롤러로 전달하고, 로직을 실행 후 응답을 생성해준다.
@Controller
public class MyController {
@GetMapping("/umc")
public String hello(Model model){
model.addAttribute("message", "UMC Spring");
return "greeting";
}
}