본문 바로가기

WINK-(Web & App)/Spring Boot 스터디

[2024-2 SprintBoot 스터디] 윤성욱 #4주차

반응형

Spring 컨테이너와 DI(의존성 주입)

1. Spring 컨테이너란?

Spring 컨테이너는 애플리케이션에서 객체를 생성하고 관리하며, 객체들 간의 의존성을 설정하는 핵심 역할을 담당힌다.

이를 통해 개발자는 객체의 생성 및 관리를 컨테이너에 맡기고 비즈니스 로직에만 집중할 수 있다.

 

스프링 컨테이너의 주요 특징

1. Bean 관리

컨테이너는 객체(스프링 빈)를 생성하고 생명 주기를 관리한다.

2. 의존성 주입(DI)

각 객체가 필요로 하는 의존성을 주입하여 객체 간의 결합도를 낮춘다.

3. 제어의 역전(IoC)

객체의 생성 및 생명 주기 관리를 컨테이너가 책임짐으로써 객체는 자신의 로직 구현에만 집중할 수 있다.

 

스프링 컨테이너 생성 예시

// 스프링 컨테이너 생성
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);

 

위 코드에서 AnnotationConfigApplicationContextApplicationContext 인터페이스의 구현체로, 설정 클래스를 기반으로 컨테이너를 생성한다.

 

2. 스프링 컨테이너와 Bean 관리

스프링 컨테이너는 설정 클래스를 기반으로 Bean(객체)을 등록하고 관리한다.

 

Bean 등록

@Configuration@Bean을 사용하여 애플리케이션에 필요한 객체를 정의

@Configuration
public class AppConfig {
    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }  

    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }
}

 

@Configuration: 설정 파일임을 선언.

@Bean: 메서드의 반환값을 스프링 컨테이너에 Bean으로 등록.

 

Bean 조회

등록된 Bean은 ApplicationContext를 통해 조회할 수 있다.

// Bean 이름으로 조회
MemberService memberService = applicationContext.getBean("memberService", MemberService.class);

// 타입으로 조회
MemberRepository memberRepository = applicationContext.getBean(MemberRepository.class);

 

3. DI(의존성 주입)와 IoC

Spring의 의존성 주입은 객체 간의 결합도를 낮추고 유연한 설계를 가능하게 만든다.

 

기존 방식의 한계

다음은 객체를 직접 생성하고 연결하는 코드

public class OrderServiceImpl {
    private final MemberRepository memberRepository = new MemoryMemberRepository();
    private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
}

 

문제점: OrderServiceImpl은 구체클래스인 MemoryMemberRepositoryFixDiscountPolicy에 강하게 결합되어 있어 확장이 어렵다.

 

DI를 활용한 설계

의존성 주입을 사용하면 객체 생성과 의존성 설정을 외부(AppConfig)가 관리

: 생성자 주입을 통해 OrderServiceImpl은 어떤 구현체를 사용할지 몰라도 된다.

public class OrderServiceImpl implements OrderService {
    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    // 생성자 주입
    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }

    // 비즈니스 로직만 구현
    @Override
    public Order createOrder(Long memberId, String itemName, int itemPrice) {
        Member member = memberRepository.findById(memberId);
        int discountPrice = discountPolicy.discount(member, itemPrice);
        return new Order(memberId, itemName, itemPrice, discountPrice);
    }
}

 

 

DI의 장점

1. 유연성 증가: 의존하는 객체를 쉽게 교체 가능.

2. 테스트 용이성: Mock 객체를 활용한 단위 테스트 가능.

3. SRP(단일 책임 원칙) 준수: 객체는 자신의 로직에만 집중, 객체 생성과 설정은 컨테이너가 담당.

 

4. IoC(Inversion of Control)의 이해

IoC는 객체의 제어 권한을 개발자에서 컨테이너로 넘기는 것을 의미한다.

 

IoC를 활용하지 않은 경우

객체는 자신이 의존하는 객체를 직접 생성하고 제어한다.

: 객체의 생성과 의존성 설정을 모두 개발자가 직접 관리해야...

public class Main {
    public static void main(String[] args) {
        MemberRepository memberRepository = new MemoryMemberRepository();
        DiscountPolicy discountPolicy = new FixDiscountPolicy();
        OrderService orderService = new OrderServiceImpl(memberRepository, discountPolicy);
    }
}

 

IoC를 활용한 경우

Spring 컨테이너가 모든 객체 생성과 의존성 설정을 관리한다.

public class Main {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        OrderService orderService = applicationContext.getBean(OrderService.class);
    }
}

 

 

DI를 통해 객체 간 결합도를 낮추고 IoC를 통해 제어 권한을 컨테이너에 위임함으로써 유지보수성과 확장성을 극대화할 수 있다.

반응형