
클라이언트의 요청을 받은 컨트롤러는 서비스를 통해 view를 제공하고 회원 서비스를 사용하여 회원 등록, 조회를 실시합니다.
이렇게 컨트롤러의 동작이 서비스에 의존하고, 이를 의존관계가 있다고 표현할 수 있습니다.
이러한 의존 관계를 설정하는 방법에 대해서 알아보겠습니다.
@Controller가 있으면 스프링은 스프링 컨테이너에 MemberController라는 객체를 생성, 이 컨트롤러를 컨테이너 안에서 관리하고 이것을 스프링 빈이 관리된다고 합니다.
new를 사용해 직접 객체를 생성하면 다른 컨트롤러에서 객체가 중복되어 생성될 수 있으므로 한번만 생성하여 하나의 회원 서비스 인스턴스를 각각의 컨트롤러가 공유하는것이 효율적입니다. 따라서 등록된 하나의 회원 서비스를 컨테이너로부터 받아 사용해야합니다.
package hello.hellospring.controller;
import hello.hellospring.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class MemberController {
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
}
@Autowired를 사용하면 자동으로 스프링 컨테이너에 등록된 MemberService 객체를 찾아 매개변수로 넘겨줍니다. 이렇게 객체 의존관계를 외부에서 넣어주는 것을 DI(Dependency Injection)의존성 주입이라고 합니다.
위의 코드로 수정 후 main() 매서드를 실행하면 오류가 발생합니다.
memberController는 컨테이너에 등록되었지만 memberService가 등록되지 않았기 때문.
따라서 @Controller와 같이 MemberService, MemoryMemberRepository 각각 @Service, @Repository로 스프링 컨테이너에 등록해야합니다.
@Service
public class MemberService {
private final MemberRepository memberRepository;
@Autowired
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
참고: 생성자에 @Autowired를 사용하면 객체 생성 시점에 스프링 컨테이너에서 해당 스프링 빈을 찾아서 주입한다. 생성자가 1개만 있으면 @Autowired는 생략할 수 있다.

@Controller로 스프링 컨테이너에 객체를 등록, 외부 요청을 받음
@Service로 스프링 컨테이너에 객체를 등록, 비즈니스 로직을 처리
@Repository로 스프링 컨테이너에 객체를 등록, 데이터를 저장
@Autowired로 스프링 컨테이너에 등록된 객체를 찾아 매개변수로 넘겨줌, 의존관계를 가지게 함
위에서 서비스와 리포지토리에 작성한 @Service, @Repository, @Autowired 애노테이션을 제거하고 진행합니다.
@Configuration을 클래스명 앞에 등록하면 스프링이 설정 파일임을 인식, 컨네이너에 스프링 빈을 등록할 준비를 합니다.
@Bean을 사용하여 다음의 메서드를 호출해 스프링 빈이 등록됩니다.
hello.hellospring/SpringConfig.java 생성
package hello.hellospring;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import hello.hellospring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig {
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
DI에는 필드 주입, setter 주입, 생성자 주입 이렇게 3가지 방법이 있는데, 의존관계가 실행중에 동적으로 변하는 경우는 거의 없으므로 생성자 주입을 권장한다.
실무에서는 주로 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드는 컴포넌트 스캔을 사용합니다. 그리고 정형화 되지 않거나, 상황에 따라 구현 클래스를 변경해야 하면 설정(@Configuration)을 통해 스프링 빈으로 등록합니다.
📌주의
@Autowired를 통한 DI는 hellocontroller, memberService 등과 같이 스프링이 관리하는 객체에서만 동작한다. 스프링 빈으로 등록하지 않고 내가 직접 생성한 객체에서는 동작하지 않는다.