이번주에는 TIL을 2번이나 못쓴것 같다.
이번주에 못 쓴 내용들 지금 차곡차곡 정리하고 있다.
이거 언젠가는 한번에 싹 푼다 진짜 기다려
그리고 이번주 WIL의 주제가 원래 준비하고 있었던 TIL 이랑도 겹치는 부분이 있어서 이 부분을 오늘 주제로 잡고 진행할 예정이다.
일단 내일까지는 과제3 까지는 끝내놓고, 그리고 전체적인 개념을 잡아볼 예정이다.
플러스 알파로 노션에 있는 추가자료들 그리고 실제로 사용하는 개념들까지도 다 잡아보는 것을 해야겠다.
일단 시작하겠다.
이 부분은 전에 TIL 에서도 잘 정리가 되어있지만 한번더 정리하겠다.
일단 우리가 지금 하는 예제에서 항상 보았던 이 부분을 생각해보자
private final UserRepository userRepository;
간단하게 생각만 하면 userRepository 객체를 그냥 생성해서 불러온 것이 아닌가?
어짜피 그냥 이거 사용할려고 사용하는것이 아닌가?
생각할 수도 있다.
하지만 객체지향 개념에서는 조금은 다르다
DI는 의존성 주입으로서 영어로는 Dependency Injection 이다.
영어 자체로 이해를 해본다면, Dependency 즉 의존관계
한 객체와 다른 한 객체가 존재를 할때, 이 한 객체가 다른 객체에 연관이 존재를 한다고 가정한다면
우리는 이 객체를 "불러와야" 한다.
사실 객체지향 개념을 사용하지 않는다면, 그냥 한 객체에 있는 내용들도 동일하게 지금 가지고 있는 객체에서 써서 사용해도 된다.
하지만 그것은 객체지향적인 개념이 될 수 없다.
지금까지 작성한 것은 나의 DI의 생각이고 이를 정의적인 측면에서 이해를 해본다면 다음과 같다.
의존성 주입은 객체가 종속 객체를 자체적으로 생성하지 않고, 대신 제공되는 객체에 종속성을 주입하는 소프트웨어 디자인패턴 이다.
만약 객체의 종속성이 유연하고 쉽게 변경이 가능해야 할 경우 테스트의 목적으로 유용할 수 있다.
일단 다음의 코드를 보자
public class EmailService {
private MessageSender messageSender;
// Dependency injection through constructor
public EmailService(MessageSender messageSender) {
this.messageSender = messageSender;
}
public void sendEmail(String message, String recipient) {
messageSender.sendMessage(message, recipient);
}
}
public interface MessageSender {
void sendMessage(String message, String recipient);
}
public class SMTPSender implements MessageSender {
@Override
public void sendMessage(String message, String recipient) {
// send the email using SMTP protocol
}
}
public class GmailSender implements MessageSender {
@Override
public void sendMessage(String message, String recipient) {
// send the email using Gmail API
}
}
이 코드를 보면 interface를 통해서 종속성 주입을 설명하는 예제이다.
종속관계를 보면 다음과 같다.
만약 우리가 EmailService 클래스를 사용할때, 생성자를 통해서 종속적인 MessageSender를 제공하는 것이 중요한데 다음과 같은 예시를 보면 이해가 빠르다.
// Using SMTPSender
MessageSender smtpSender = new SMTPSender();
EmailService emailService = new EmailService(smtpSender);
// Using GmailSender
MessageSender gmailSender = new GmailSender();
EmailService emailService = new EmailService(gmailSender);
이때 가장 중요한 단어가 Emailservice가 종속적인 객체를 제공하는 곳에서 제공되는 것이 DI의 핵심 개념이다.
또한 이와같은 것으로 제공되는 이점이 종속적인 객체를 변경하거나 테스트를하는 것이 쉬워지며, 객체의 유연한 구조를 가질 수 있다.
앞에서 말했듯이 DI를 이해를 했다면, 이제 IoC는 어찌보면 당연한 이해이다. IoC는 Inversion of Control로 아까 설명마지막에 나왔던, 종속적인 객체를 제공하는 곳에서 제공이된다는 것을 이해했다면 당연하게 이해를 할 수 있다.
(사실은 못한다)
IoC는 종속성 주입의 개념을 확장한 개념이다.
IoC는 일반적으로 컨트롤의 흐름을 뒤집는 것을 말하는데, 이때 일반적으로, 어플리케이션의 흐름은 프로그래머가 직접 관리하지만, IoC를 사용하면 어플리케이션의 흐름을 컨테이너가 관리하게 된다.(컨테이너도 계속해서 나오는 단어이므로 정리하자)
즉, 프로그래머는 컨테이너가 관리하는 객체와 의존 관계만 정의하를 한다.
이때, 컨테이너가 객체의 생성, 생명주기 관리, 의존성 주입 등의 작업을 수행한다.
그렇다면 IoC가 존재를 하는 이유가 뭘까?
IoC Contatiner를 사용하게된다면 객체간의 의존 관계를 쉽게 정리, 관리 할 수 있고, 이 어플리케이션의 테스트와 유지보수와 "확장성" 이 향상이 된다.
Bean은 컨테이너가 관리하는 객체를 말합니다.
맞다 앞에서 말했던 그 컨테이너가 또 등장한다.
이때 Bean은 애플리케이션에서 사용되는 객체와 관련된 정보(속성, 클래스 타입, 생성정책, 의존 관계 등)를 정의하고 관리하는 친구라는 것이다.
말했다 시피, Bean은 IoC 컨테이너에 의해서 관리되며, IoC 컨테이너는 Bean 정의를 기반으로 객체를 생성하고 관리하게 된다.
애플리케이션의 객체 간의 의존 관계를 관리하기 위해서는 객체를 Bean으로 정의하고 컨테이너에 의해 관리되어야 합니다.
예를 들어, 애플리케이션에서 사용하는 객체 중 일부는 생성자를 통해 생성하고, 일부는 정적 팩토리 메서드를 통해 생성할 수 있다.
또한 이러한 객체를 Bean으로 정의하면 IoC 컨테이너가 이를 관리하며, 객체 간의 의존 관계를 주입할 수 있게 된다.
보통은 우리가 항상 봐왔던 @Component, @Service, @controller, @Repository 어노테이션을 붙이면 그때부터 Bean으로 등록되어서 관리가 가능해지는 것이다.