스프링은 자바로 웹 개발을 하기 쉽게 도와주는 서비스 -> 없어도 개발이 가능.
스프링 없이 개발을 시작해서 점차적으로 스프링을 사용한 코드로 변경해주면 스프링이 어떤 부분을 쉽게 만들어주는지 이해가 가능하게 된다 -> 왜 사용하는지 알게됨
보통 서비스를 인터페이스(역할)로 두고, 클래스로 인터페이스를 구현한다. 클래스로 구현할 때, 가장 좋은 방법은 OCP와 DIP를 준수하면서 개발을 하는 것인데 추상화(인터페이스)에 의존을 해야지 구현체(구현)에도 의존하기 쉽다.
public class ExampleServiceImpl implements ExampleService {
private final ExampleRepository exampleRepository = new MemoryExampleRepository();
}
위 코드는 얼핏 보면 DIP를 잘 따른 것 처럼 보이나, new MemoryExampleRepository()는 구현체로 결국 구현체에도 의존하고 있는 코드가 된다. 이는 추후 메모리 대신 DB를 사용한다고 하면 DbExampleRepository()로 변경할 시, 소스코드를 수정해야하는 바람직하지 않은 상황이 발생한다.
현재 이 코드는 서비스 구현의 역할과 exampleRepository의 역할을 정해주는 두가지 기능이 존재하게 된 것 -> 하나의 기능만 담당하는 것이 좋음
그렇다면 아래처럼 코드를 변경하면...
public class ExampleServiceImpl implements ExampleService {
private ExampleRepository exampleRepository;
}
더이상 구현체에 의존하지 않기 때문에 OCP와 DIP에 잘 따르는 것처럼 보인다. 하지만 이를 실행해보면 NullPoint 예외가 발생하는 것을 알 수 있다. 이는 선언은 했지만 값이 주어지지 않았기 때문에 exampleRepository에는 결국 Null이 들어있기 때문이다.
이를 해결하기 위해서는 누군가가 이 구현체에 값을 넣어주어야 한다. 이런 기능(구현 객체 생성 및 연결)을 담당하는 클래스를 AppConfig로 정해주고 이를 구현해주는 방법이 있다.
ExampleServiceImpl Class
public class ExampleServiceImpl implements ExampleService {
private ExampleRepository exampleRepository;
// Constructor 작성
public ExampleServiceImpl(ExampleRepository exampleRepository) {
this.exampleRepository = exampleRepository;
}
}
AppConfig Class
public ExampleService exampleService() {
return new ExampleServiceImpl(new MemoryExampleRepository());
}
AppConfig를 통해 애플리케이션의 실제 동작에 필요한 구현 객체를 생성하고 주입할 수 있다. 클라이언트인 ExampleServiceImpl는 의존 관계를 외부에서 주입 당한 것 같다는 의미로 DI(Dependency Injection)이라 표현한다.
AppConfig를 통해 의존관계를 주입하면 DIP와 OCP를 동시에 지킬 수 있게 된다.