Spring의 IoC와 DI
IoC, DI는 객체지향의 SOLID 원칙 그리고 GoF의 디자인 패턴과 같은 설계 원칙 및 디자인 패턴이다.
이 둘을 더 자세하기 구분해 보자면 IoC는 설계 원칙에 해당하고 DI는 디자인 패턴에 해당한다.
요리로 비유해보면
설계 원칙을 요리로 비유하기
신선한 재료를 사용한다.
신 김치를 사용한다.
밥과 김치의 비율을 잘 맞춰야 한다.
볶을 때 재료의 순서가 중요하다.
디자인 패턴을 요리로 비유하기
오일을 두른 팬에 채썬 파를 볶아 파기름을 만든다.
준비한 햄을 넣고 볶다가, 간장 한스푼을 넣어 풍미를 낸다.
설탕에 버무린 김치를 넣고 함께 볶는다.
미리 식혀 둔 밥을 넣어 함께 볶는다.
참기름 한스푼을 넣어 마무리한다.
좋은 코드란 무엇일까?
논리가 간단해야 한다.
중복을 제거하고 표현을 명확하게 한다.
코드를 처음보는 사람도 쉽게 이해하고 수정할 수 있어야 한다.
.
의존성을 최소화해야 한다.
새로운 기능을 추가하더라도 크게 구조의 변경이 없어야 한다.
이렇듯 좋은 코드를 작성하기 위해서는 신경써야 할 부분이 정말 많다.
따라서 Spring은 개발자가 Java를 사용하여 쉽게 좋은 코드를 작성할 수 있도록 도와주는 역할을 해준다.
실생활에 비교해 보자면 요리도구까지 전부 들어있는 밀키트라고 할 수 있다.
여기서 IoC와 DI는 좋은 코드 작성을 위한 Spring의 핵심 기술 중 하나라고 할 수 있다.
아래는 Spring Docs에서 발췌해온 내용이다.
Spring의 핵심 기술을 소개하는Docs에서 가장 처음으로 IoC 컨테이너에 대해서 설명하고 있다.
그리고 IoC에 대해 'IoC는 DI로도 알려져 있다' 라고 소개하고 있다.
의역해보자면 'DI 패턴을 사용하여 IoC 설계 원칙을 구현하고 있다' 라고 이해하면 좋다.
Spring Docs에서 소개하고 있는 IoC Container와 Bean에 대해서는 이후 강의에서 학습한다.
의존성이란?
먼저 DI를 이해하려면 '의존성'에 대한 이해가 필요하다
예를들어 우리가 다리를 다쳐서 목발을 사용하여 걷게 된다면 우리는 걷기 위해 목발에 의존하고 있는 것이다. 즉, 우리는 목발에 의존성을 두게 되었다고 할 수 있다.
코드를 통해 의존성에 대해 이해해보자
public class Consumer {
void eat() {
Chicken chicken = new Chicken();
chicken.eat();
}
public static void main(String[] args) {
Consumer consumer = new Consumer();
consumer.eat();
}
}
class Chicken {
public void eat() {
System.out.println("치킨을 먹는다.");
}
}
코드가 실행되는데 있어서는 아무런 문제가 없지만 만약 Consumer가 치킨이 아니라 피자를 먹고 싶어 한다면? 많은 수의 코드 변경이 불가피하다.
그렇다면 어떻게 하면 결합을 약하게 할 수 있을까?
Java의 Interface를 활용하면 이를 해결할 수 있다.
public class Consumer {
void eat(Food food) {
food.eat();
}
public static void main(String[] args) {
Consumer consumer = new Consumer();
consumer.eat(new Chicken());
consumer.eat(new Pizza());
}
}
interface Food {
void eat();
}
class Chicken implements Food{
@Override
public void eat() {
System.out.println("치킨을 먹는다.");
}
}
class Pizza implements Food{
@Override
public void eat() {
System.out.println("피자를 먹는다.");
}
}
이처럼 Interface 다형성의 원리를 사용하여 구현하면 고객이 어떠한 음식을 요구하더라도 쉽게 대처할 수 있다.
이러한 관계를 약한 결합 및 약한 의존성이라고 할 수 있다.
주입이란?
우리가 주사기를 통해 백신을 우리 몸속에 주입 하듯이
코드에서의 주입도 마찬가지로 여러 방법을 통해 필요로 하는 객체를 해당 객체에 전달하는 것이다.
코드를 통해 코드에서의 주입에 대해 이해해보자
필드에 직접 주입
public class Consumer {
Food food;
void eat() {
this.food.eat();
}
public static void main(String[] args) {
Consumer consumer = new Consumer();
consumer.food = new Chicken();
consumer.eat();
consumer.food = new Pizza();
consumer.eat();
}
}
interface Food {
void eat();
}
class Chicken implements Food{
@Override
public void eat() {
System.out.println("치킨을 먹는다.");
}
}
class Pizza implements Food{
@Override
public void eat() {
System.out.println("피자를 먹는다.");
}
}
이처럼 Food를 Consumer에 포함 시키고 Food에 필요한 객체를 주입받아 사용할 수 있다.
메서드를 통한 주입
public class Consumer {
Food food;
void eat() {
this.food.eat();
}
public void setFood(Food food) {
this.food = food;
}
public static void main(String[] args) {
Consumer consumer = new Consumer();
consumer.setFood(new Chicken());
consumer.eat();
consumer.setFood(new Pizza());
consumer.eat();
}
}
interface Food {
void eat();
}
class Chicken implements Food{
@Override
public void eat() {
System.out.println("치킨을 먹는다.");
}
}
class Pizza implements Food{
@Override
public void eat() {
System.out.println("피자를 먹는다.");
}
}
이처럼 set 메서드를 사용하여 필요한 객체를 주입받아 사용할 수 있다.
생성자를 통한 주입
public class Consumer {
Food food;
public Consumer(Food food) {
this.food = food;
}
void eat() {
this.food.eat();
}
public static void main(String[] args) {
Consumer consumer = new Consumer(new Chicken());
consumer.eat();
consumer = new Consumer(new Pizza());
consumer.eat();
}
}
interface Food {
void eat();
}
class Chicken implements Food{
@Override
public void eat() {
System.out.println("치킨을 먹는다.");
}
}
class Pizza implements Food{
@Override
public void eat() {
System.out.println("피자를 먹는다.");
}
}
제어의 역전이란?
이전에는 Consumer가 직접 Food를 만들어 먹었기 때문에 새로운 Food를 만들려면 추가적인 요리준비(코드변경)이 불가피했다.
그렇기 때문에 이때는 제어의 흐름이 Consumer -> Food 였다.
이를 해결하기 위해 만들어진 Food를 Consumer에게 전달해주는 식으로 변경함으로써 Consumer는 추가적인 요리준비(코드변경) 없이 어느 Food가 되었든지 전부 먹을 수 있게 되었다.
결과적으로 제어의 흐름이 Food -> Consumer로 역전되었다.
'Spring 입문주차 > 2주차' 카테고리의 다른 글
6. JPA란 무엇일까? (0) | 2024.08.20 |
---|---|
5. IoC Container와 Bean (0) | 2024.08.19 |
4. 메모장 프로젝트의 IoC & DI (0) | 2024.08.19 |
2. 역할 분리하기 (0) | 2024.08.18 |
1. 3 Layer Architecture (0) | 2024.08.16 |