본문으로 건너뛰기
홈
기술
기술 전체
프로그래밍68
컴퓨터 과학63
AI48
웹 개발36
인프라33
데이터31
소프트웨어 공학18
소개
← 목록으로웹 개발 › 백엔드 › Spring

스프링 목적과 DI 정의

목차

스프링의목적

  • 자바개발의 간소화

(1) POJO를 이용한 가볍고(lightweight) 비 침투적(non-invasive) 개발

(2) DI와 인터페이스 지향(interface orientation)을 통한 느슨한 결합도(loose coupling)

(3) 애스펙트와 공통 규약을 통한 선언적(declarative) 프로그래밍

(4) 애스팩트와 템플릿(template)을 통한 반복적인 코드 제거

  • 스프링은 API를 이용하여 애플리케이션 코드의 분산을 가능한 한 막는다.

  • 스프링에 특화된 인터페이스 구현이나 스프링 자체에 의존성이 높은 클래스 확장을 거의 요구하지 않는다.(최악의 경우 Annotation이 붙음)

DI(Dependency Injection) : 의존성 주입

  • 구성요소간 의존관계가 소스코드 내부가 아닌 외부의 설정파일 등을 통해 정의하게 되는 디자인 패턴으로 DI를 이용하면 객체는 시스템에서 각 객체르 조율하는 제 3자에 의해 생성 시점에 종속객체(dependency)가 부여된다.

(객체 간의 의존관계를 객체-객체 가 아닌 외부에서 객체를 생성하고 전달해 줌으로 써 객체간의 의존성 제거 및 결합도를 낮추는 것)

이점

  • 의존 관계 설정이 컴파일시가 아닌 실행시 이루어져 모듈간의 결합도를 낮출 수 있다.

  • 코드의 재사용률을 높여서 작성된 모듈을 여러 곳에서 소스코드 수정없이 사용

결합도

  • 결합도가 높으면 테스트와 재활용이 어렵고 이해하기 어렵고, 오류가 발생하면 다른 오류가 발생하는 경향이 있다.

  • 결합도가 낮으면 테스트와 재활용성 이 용이하고, 이해하기 쉬우며 오류가 발생해도 수정이 간결하다.

두 객체의 결합을 실제 코드에서 보기

주문 서비스가 생성자 안에서 직접 JDBC 저장소를 만들면 서비스의 업무 규칙과 DB 연결 생성이 한곳에 묶인다. DB 주소가 바뀌거나 테스트에서 저장소를 교체할 때 서비스를 수정해야 한다. DI는 저장소 객체의 생성 책임을 서비스 밖으로 옮긴다.

interface OrderStore {
    void save(Order order);
}

final class OrderService {
    private final OrderStore store;

    OrderService(OrderStore store) {
        this.store = store;
    }

    void place(Order order) {
        if (order == null) throw new IllegalArgumentException("order required");
        store.save(order);
    }
}

이 코드는 Spring 없이도 동작한다. 실행 코드에서 new OrderService(new JdbcOrderStore(...))처럼 조립하면 된다. Spring 컨테이너를 쓰면 빈 설정을 읽어 같은 조립을 수행한다. 의존성을 외부에서 받는 것이 DI이고, 생성·연결의 제어를 컨테이너에 맡기는 구성이 IoC의 한 사례다. 인터페이스를 만들었다는 사실만으로 결합이 낮아지는 것은 아니다. 구현 선택과 연결 정보가 여전히 서비스 내부에 있으면 교체 비용이 줄지 않는다.

flowchart LR
  A["빈 설정"] --> B["OrderStore 구현 생성"]
  B --> C["OrderService 생성자"]
  C --> D["place() 업무 규칙"]

그림의 설정과 업무 규칙은 서로 다른 책임이다. DB 저장소가 실패하면 서비스는 저장 실패를 호출자에게 전달하거나 업무적으로 의미 있는 예외로 변환해야 한다. 모든 예외를 삼키면 주문이 저장되지 않았는데 성공으로 보일 수 있다. 실제 주문과 재고 변경이 함께 일어나야 한다면 DI만으로 원자성이 생기는 것이 아니므로 트랜잭션 경계도 별도로 정한다.

언제 설정이 오히려 복잡해질까?

모든 클래스를 인터페이스로 나누고 무조건 빈으로 등록하면 단순한 값 객체까지 컨테이너에 종속된다. 교체 필요가 없는 계산 객체는 일반 생성자로 만들어도 된다. 반대로 DB 연결, 외부 HTTP 클라이언트, 로깅 정책처럼 환경별 설정과 수명 관리가 필요한 객체는 컨테이너가 관리하면 조립 위치가 분명해진다. 빈 스코프가 싱글턴이면 여러 요청이 한 인스턴스를 공유하므로 요청별 상태를 필드에 보관하지 않는다.

테스트에서는 OrderStore의 가짜 구현을 전달해 저장 요청과 검증 로직을 확인한다. Spring 통합 테스트에서는 실제 빈 검색과 트랜잭션 설정을 확인한다. 전자는 업무 규칙, 후자는 조립 설정을 검증하므로 목적이 다르다. 빈을 찾지 못하면 구현 등록, 패키지 스캔, 동일 타입의 후보 수를 순서대로 확인한다.

참고: Spring IoC 컨테이너, 생성자 주입.

같은 카테고리의 글