본문으로 바로가기

목차

    0. 환경

    1. Controller 생성

    • 도메인, 리포지토리, 서비스는 개발을 했고 다음 차례로 뷰랑 연결을 위해 컨트롤러가 필요하게 됩니다.
    • 컨트롤러에서는 서비스의 기능이 필요하게 되는데 이 관계를 의존관계라고 합니다.
    • 회원 컨트롤러가 회원서비스와 회원 리포지토리를 사용할 수 있게 의존관계를 준비합니다.
    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;
        }
    
    }

    1.1. @Controller

    • 컨트롤러를 스프링 빈으로 등록하기 위해 사용합니다.

    1.2. @Autowired

    • 생성자에 @Autowired 가 있으면 스프링이 연관된 객체를 스프링 컨테이너에서 찾아서 넣어줍니다.
    • 객체 의존관계를 외부에서 넣어주는 것을 DI (Dependency Injection), 의존성 주입이라 합니다.
    • 이전 테스트에서는 개발자가 직접(new) 주입했고, 여기서는 @Autowired에 의해 스프링이 주입해줍니다.
    • @Autowired를 통한 DI는 helloConroller , memberService 등과 같이 스프링이 관리하는 객체에서만 동작합니다.
    • 스프링 빈으로 등록하지 않고 내가 직접 생성한 객체에서는 동작하지 않습니다.
    • 생성자가 1개만 있으면 생략 가능합니다.

    1.3. 소스 설명

    • MemberService 기능을 사용하기 위해(의존 관계) 생성자를 이용해 연결(@Autowired)합니다.
    • 스프링 컨테이너가 생성될 때 @Controller로 등록된 MemberController 객체를 생성해 스프링 빈으로 컨테이너에 등록합니다.
    • MemberController 객체를 생성하면서 생성자를 호출하게 되는데, 생성자에 @Autowired를 등록해두면 컨테이너에 스프링 빈으로 등록된 MemberService 객체를 연결시켜줍니다.
    • DI (의존 관계 주입)를 스프링이 대신해줍니다.
    • 의존 관계의 객체를 new로 직접 생성할 수도 있지만 스프링 컨테이너에 스프링 빈으로 등록해두면 여러 이점이 많으므로 스프링 컨테이너에 등록해서 받아서 쓰도록 변경해야 합니다.
    • 스프링 빈으로 등록하게 되면 인스턴스를 여러 개 생성할 필요도 없게 되고 공용으로 사용하면 됩니다.

    1.4. 실행

    • MemberController를 작성 후 스프링 부트를 실행하게 되면 다음과 같은 에러가 출력됩니다.
    • MemberService가 스프링 빈으로 등록되어 있지 않아서 나오는 에러입니다. 서비스와 리포지토리를 스프링 빈으로 등록해줍니다.
    Consider defining a bean of type 'hello.hellospring.service.MemberService' in your configuration.

    1.5. 스프링 빈을 등록하는 2가지 방법

    1. 컴포넌트 스캔과 자동 의존관계 설정 
    2. 자바 코드로 직접 스프링 빈 등록하기

    2. 스프링 빈 등록하기 (컴포넌트 스캔과 자동 의존관계 설정)

    컴포넌트 스캔과 자동 의존관계 설정

    2.1. 스프링 빈 등록 방식 (컴포넌트 스캔과 자동 의존관계 설정)

    1. 스프링 실행 파일로 스프링을 실행합니다.
    2. 스프링 컨테이너가 생성됩니다.
    3. @Component 어노테이션이 붙은 클래스를 스캔 후 싱글톤으로 객체를 생성해 스프링 컨테이너에 스프링 빈으로 자동 등록합니다. (컴포넌트 스캔)
      • 싱글톤 패턴: 오직 하나의 인스턴스만 생성합니다. 
      • 싱글톤으로 등록하기 때문에 같은 스프링 빈이면 모두 같은 인스턴스입니다.
      • 기본으로 싱글톤으로 등록하게 됩니다. 설정으로 싱글톤이 아니게 설정할 수 있지만, 특별한 경우를 제외하면 대부분 싱글톤을 사용합니다.
    4. @Autowired를 사용한 생성자의 객체에 맞게 스프링 컨테이에서 스프링 빈을 찾아서 주입합니다. (자동 의존관계 설정)
      • 생성자가 1개만 있으면 생략 가능합니다.
      • @Autowired를 통한 DI는 helloConroller , memberService 등과 같이 스프링이 관리하는 객체에서만 동작합니다.
      • 스프링 빈으로 등록하지 않고 내가 직접 생성한 객체에서는 동작하지 않습니다.
    5. 등록된 스프링 빈은 스프링이 관리해 줍니다. (이에 따라 다양한 이점이 생기게 됩니다.)

    2.2. 컴포넌트 스캔

    • @Component
      • @Controller
      • @Service
      • @Repository
    • @Controller, @Service, @Repository는 @Component 어노테이션이 아닌데 어떻게 스캔이 되나요?
      • @Controller, @Service, @Repository 어노테이션 소스 안에 @Component 어노테이션이 등록되어 있습니다.
      • @Target({ElementType.TYPE})
        @Retention(RetentionPolicy.RUNTIME)
        @Documented
        @Component
        public @interface Controller {
        
        	/**
        	 * The value may indicate a suggestion for a logical component name,
        	 * to be turned into a Spring bean in case of an autodetected component.
        	 * @return the suggested component name, if any (or empty String otherwise)
        	 */
        	@AliasFor(annotation = Component.class)
        	String value() default "";
        
        }
        
        @Target({ElementType.TYPE})
        @Retention(RetentionPolicy.RUNTIME)
        @Documented
        @Component
        public @interface Service {
        
        	/**
        	 * The value may indicate a suggestion for a logical component name,
        	 * to be turned into a Spring bean in case of an autodetected component.
        	 * @return the suggested component name, if any (or empty String otherwise)
        	 */
        	@AliasFor(annotation = Component.class)
        	String value() default "";
        
        }
        
        @Target({ElementType.TYPE})
        @Retention(RetentionPolicy.RUNTIME)
        @Documented
        @Component
        public @interface Repository {
        
        	/**
        	 * The value may indicate a suggestion for a logical component name,
        	 * to be turned into a Spring bean in case of an autodetected component.
        	 * @return the suggested component name, if any (or empty String otherwise)
        	 */
        	@AliasFor(annotation = Component.class)
        	String value() default "";
        
        }
    • @SpringBootApplication를 가지고 있는 스프링 부트 실행 파일에는 @ComponentScan이 있는 것을 확인할 수 있습니다. 따라서 스프링 부트를 실행하면서 컴포넌트를 스캔하게 됩니다.
      • @Target(ElementType.TYPE)
        @Retention(RetentionPolicy.RUNTIME)
        @Documented
        @Inherited
        @SpringBootConfiguration
        @EnableAutoConfiguration
        @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
        public @interface SpringBootApplication {
        
        	( ... ) //생략
        }​
         

    2.3. 스프링 빈 등록 범위

    • @SpringBootApplication과 public static void main(String[] args)를 가지고 있는 스프링 부트 실행파일과 같은 패키지 그리고 하위 패키지에 있는 컴포넌트만 기본적으로 스캔해서 등록하게 됩니다.
    • 초록 박스(스프링 부트 실행파일), 파란 박스(등록 가능한 범위), 빨간 박스(등록 불가능한 범위)

    2.4. Why @Controller, @Service, @Repository

    컴포넌트를 가지고 있는 어노테이션명이 다음과 같은 이유는 "컨트롤러로 외부의 요청을 받고 서비스에서 비즈니스 로직을 만들고 레포지토리에서 데이터를 저장"하는 정형화된 패턴이기 때문입니다.

    3. 회원 서비스 스프링 빈 등록

    @Service
    public class MemberService {
    
        private final MemberRepository memberRepository;
    
        @Autowired
        public MemberService(MemberRepository memberRepository) {
            this.memberRepository = memberRepository;
        }
        
        ( ... ) //생략
    }

    4. 회원 리포지토리 스프링 빈 등록

    @Repository
    public class MemoryMemberRepository implements MemberRepository {
    
        ( ... ) 생략
    }