Spring DI(1)

2025. 9. 2. 11:20·Spring/Core

스프링의 세 가지 철학 중 하나인 IoC/DI에 대해 학습한 내용을 이어서 정리한다. 

 

Spring DI (Dependency Injection)

스프링 프레임워크의 핵심 개념 중 하나가 바로 의존성 주입(DI, Dependency Injection)이다. 이 글에서는 의존이란 무엇인지, DI가 왜 필요한지, 그리고 스프링에서 DI가 어떻게 동작하는지 정리한다.

 

빈(Bean)이 싱글톤인 이유

스프링 컨테이너가 생성한 빈(Bean) 객체는 기본적으로 싱글톤(singleton) 범위를 가진다. 그 이유는 같은 객체를 여러 번 생성하면 불필요한 메타데이터가 중복되고, 동일한 값을 가진 객체가 여러 개 생기기 때문이다. 즉, 스프링이 관리하는 객체는 별다른 이유가 없는 한 싱글톤으로 유지되어야 한다.

 

의존이란

의존성 주입에서 의존이란 한 클래스가 다른 클래스의 메서드를 실행할 때 발생한다.

예를 들어, 서비스 클래스가 DAO 객체를 사용한다면 "서비스가 DAO에 의존한다"고 말한다.

 

즉 의존성 주입(DI)이란 객체가 자신이 사용할 다른 객체(의존 객체)를 직접 생성하지 않고 외부에서 주입받는 방식을 말한다.  

 

의존 대상을 구하는 방법은 다음과 같다.

  • 직접 의존 대상 객체 생성 (new 키워드)
  • DI (Dependency Injection, 스프링 관련)
  • 서비스 로케이터(Service Locator)

스프링은 이 중 DI 방식을 제공한다.

 

DI 통한 의존 처리

DI는 의존하는 객체를 직접 생성하지 않고 외부에서 전달받는 방식이다.
즉, Composite(구성) 관계가 아니라 Dependency(의존) 관계가 된다.

DI의 핵심 목적은 변경에 대한 유연함 확보이다.
DI를 적용하면 의존 객체의 교체가 용이하고, 변경 사항이 다른 영역으로 전파되는 것을 최소화할 수 있다.

 

 

객체 조립기 (Assembler)

DI를 구현하는 한 가지 방법은 객체 조립기(Assembler)를 두는 것이다.

객체 조립기는 다음과 같은 역할을 한다.

  • 필요한 객체를 생성한다.
  • 생성한 객체에 의존 객체를 주입한다.
  • 필요한 곳에 객체를 제공한다.

예시 코드는 다음과 같다.

public class Assembler {

    private MemberDao memberDao;
    private MemberRegisterService regSvc;
    private ChangePasswordService pwdSvc;

    public Assembler() {
        this.memberDao = new MemberDao();
        this.regSvc = new MemberRegisterService(memberDao);
        this.pwdSvc = new ChangePasswordService();
        pwdSvc.setMemberDao(memberDao);
    }

    public MemberDao getMemberDao() {
        return memberDao;
    }

    public MemberRegisterService getRegSvc() {
        return regSvc;
    }

    public ChangePasswordService getPwdSvc() {
        return pwdSvc;
    }
}

객체 조립기 사용 예제

private static Assembler assembler = new Assembler();

private static void processNewCommand(String[] arg) {
    MemberRegisterService regSvc = assembler.getMemberRegisterService();
    ...
}

private static void processChangeCommand(String[] arg) {
    ChangePasswordService changePwdSvc =
            assembler.getChangePasswordService();
    ...
}
 

스프링의 DI 설정

스프링은 객체 조립기와 유사한 기능을 제공한다.
즉, 필요한 객체를 생성하고 의존을 주입하는 범용 조립기이다.

 

@Configuration과 @Bean

스프링에서 DI를 설정하려면 설정 클래스를 정의해야 한다.

@Configuration
public class AppCtx {

    @Bean
    public MemberDao memberDao() {
        return new MemberDao();
    }

    @Bean
    public MemberRegisterService memberRegSvc() {
        return new MemberRegisterService(memberDao());
    }

    @Bean
    public ChangePasswordService changePwdSvc() {
        ChangePasswordService changePasswordService = new ChangePasswordService();
        changePasswordService.setMemberDao(memberDao());
        return changePasswordService;
    }
}

스프링 컨테이너를 생성하고 빈을 가져올 수 있다.

 
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppCtx.class);

MemberRegisterService regSvc =
        ctx.getBean("memberRegSvc", MemberRegisterService.class);

 

DI 방식

스프링은 두 가지 방식의 DI를 지원한다. 여기서의 DI는 주입할 객체를 정의하는 방식이다. 

1. 생성자 방식

@Bean
public MemberListPrinter listPrinter() {
    return new MemberListPrinter(memberDao(), memberPrinter());
}

2. 세터 방식

public class MemberInfoPrinter {

    private MemberDao memberDao;
    private MemberPrinter printer;

    public void setMemberDao(MemberDao memberDao) {
        this.memberDao = memberDao;
    }

    public void setPrinter(MemberPrinter printer) {
        this.printer = printer;
    }
}
@Bean
public MemberInfoPrinter infoPrinter() {
    MemberInfoPrinter memberInfoPrinter = new MemberInfoPrinter();
    memberInfoPrinter.setMemberDao(memberDao());
    memberInfoPrinter.setPrinter(memberPrinter());
    return memberInfoPrinter;
}
  • 생성자 방식: 빈이 생성되는 시점에 의존 객체가 주입된다.
  • 세터 방식: 의존 객체를 나중에 주입할 수 있지만 NullPointerException 위험이 있다.

 

@Configuration의 @Bean과 싱글톤

설정 클래스에서 여러 번 메서드를 호출해도 스프링은 항상 같은 빈 객체를 리턴한다.  이는 스프링이 내부적으로 설정 클래스를 확장한 프록시를 사용하기 때문이다.

 

두 개 이상의 설정 파일 사용하기

규모가 커지면 설정 클래스를 나눠 관리할 수 있다.

// 설정 클래스 1
@Configuration
public class AppConf1 {
    @Bean
    public MemberDao memberDao() { return new MemberDao(); }
    @Bean
    public MemberPrinter memberPrinter() { return new MemberPrinter(); }
}

// 설정 클래스 2
@Configuration
public class AppConf2 {

    @Autowired
    private MemberDao memberDao;
    @Autowired
    private MemberPrinter memberPrinter;

    @Bean
    public MemberRegisterService memberRegSvc() {
        return new MemberRegisterService(memberDao);
    }
}
 

스프링 컨테이너는 가변 인자로 여러 설정 클래스를 받을 수 있다.

ctx = new AnnotationConfigApplicationContext(AppConf1.class, AppConf2.class);
또는 @Import를 사용할 수도 있다.
@Configuration
@Import(AppConf2.class)
public class AppConfImport {
    @Bean
    public MemberDao memberDao() { return new MemberDao(); }
}
 

getBean() 사용

스프링 컨테이너에서 빈을 가져오는 방법은 getBean()이다.

VersionPrinter versionPrinter =
        ctx.getBean("versionPrinter", VersionPrinter.class);

타입만 지정해서 빈을 가져올 수도 있다. 단, 동일 타입의 빈이 여러 개일 경우 예외가 발생한다.

 
MemberPrinter printer = ctx.getBean(MemberPrinter.class);

 

주입 대상 객체는 모두 빈이어야 할까?

그렇지 않다.

 

스프링이 관리하는 기능이 필요하지 않고, getBean()으로 가져올 필요가 없는 객체라면 굳이 빈으로 등록할 필요는 없다. 예를 들어, 도메인 객체(Reservation 등)은 빈으로 등록하지 않고 일반 객체로 사용하는 경우가 많다.

 

2편으로 이어집니다.

'Spring > Core' 카테고리의 다른 글

Spring DI(2)  (0) 2025.09.04
JdbcTemplate 삽입 직후 Key 가져오기  (1) 2025.09.03
Spring IoC Container  (2) 2025.09.01
JDBC, JDBC Template 비교  (1) 2025.08.31
스프링이란?  (0) 2025.08.30
'Spring/Core' 카테고리의 다른 글
  • Spring DI(2)
  • JdbcTemplate 삽입 직후 Key 가져오기
  • Spring IoC Container
  • JDBC, JDBC Template 비교
sooyang
sooyang
겉촉속촉을 지향하는 개발자입니다.
  • sooyang
    겉촉속촉 개발 이야기
    sooyang
  • 전체
    오늘
    어제
    • 분류 전체보기 (48)
      • Spring (9)
        • Core (8)
        • JPA (1)
        • MVC (0)
      • 데이터베이스 (9)
        • SQL 첫걸음 (8)
        • Real MySQL (0)
      • 운영체제 (1)
        • 혼자 공부하는 운영체제 (1)
      • 네트워크 (3)
        • AWS CCP (3)
      • 우아한테크코스 (8)
        • level4 (3)
        • level3 (4)
        • level0 (1)
      • 42서울 (7)
        • la piscine (2)
        • inner circle (5)
      • 기타 (3)
        • OOP (6)
        • PS (2)
  • 링크

    • Github
  • 인기 글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
sooyang
Spring DI(1)
상단으로

티스토리툴바