새소식

Spring

[WIL] 🍃 의존성 주입(DI)과 제어의 역전(IoC) 그리고 스프링 빈(Spring Bean)

  • -
반응형
 

📌 Intro

  • 항해99를 시작한지 이제 4주차가 된다. 3주차 주특기 입문에서 처음으로 Spring을 사용해봤다. Spring이 어렵다고 이야기만 들었는데 생각보다 너무 편하다. 하지만 편한만큼 내부적으로 어떻게 동작하는지 잘 이해가가지 않는 단점이 있다. 이제 Spring을 제대로 공부할 시간이 4주차 5주차 2주간의 시간이 남았는데 인프런의 Spring강의를 구매해서 들어볼까 고민중이다.
  • Spring에서 중요한 개념 3가지를 한 번 정리해볼까한다.

1. DI(의존성 주입)

의존성 주입( Dependency Injection ) : 하나의 객체가 다른 객체의 의존성을 제공하는 테크닉이다.

  • 의존성 주입의 의도는 객체의 생성과 사용의 관심을 분리하는 것이다. 이는 가독성과 코드 재사용을 높혀준다.
  • 의존성 주입은 아래와 같은 문제를 해결한다.
    • 어떻게 애플리케이션이나 클래스가 객체의 생성 방식과 독립적일 수 있는가?
    • 어떻게 객체의 생성 방식을 분리된 구성 파일에서 지정할 수 있는가?
    • 어떻게 애플리케이션이 다른 구성을 지원할 수 있는가?
  • 프로그램 디자인이 결합도를 느슨하게 되도록하고 의존관계 역전 원칙과 단일 책임 원칙을 따르도록 클라이언트(수신객체)의 생성에 대한 의존성을 클라이언트의 행위로부터 분리하는 것이다.
  • 의존성 주입의 기본 단위인 주입은 새롭거나 관습적인 메커니즘이 아니다. "매개변수 전달"과 동일하게 동작한다.

  • 서비스를 사용하려는 클라이언트(수신 객체)가 해당 서비스의 구성방법을 알 필요가 없게 하는 것.
  • 의존성 주입은 프레임워크가 아닌 디자인 패턴, IoC 방법 중 하나.
  • 의존성 주입은 컴파일타임이 아닌 런타임에 종속성을 해결하는 데 사용됨.
  • 무언가를 쉽게 교체할 수 있게 만들어준다.

장점

  • 모듈들을 쉽게 교체할 수 있는 구조 -> 테스팅이 쉽고 마이그레이션하기도 쉽다.
  • 구현할 때 추상화 레이어를 넣고 이를 기반으로 구현체를 넣어주기 때문에 어플리케이션 의존성 방향이 일관되고, 어플리케이션을 쉽게 추론 가능. 👉🏻 모듈 간 관계가 조금 더 명확해짐.

단점

  • 모듈들의 분리가 증가 -> 클래스 수 증가 -> 복잡성이 증가됨.
  • 복잡성이 증가됨에 따라 약간의 런타임 페널티가 생기기도 한다.
  • 종속성은 런타임에서 해결되기 때문에 종속성 오류는 컴파일 시간에 포착할 수 없다.

의존성 주입 사용법

Lombok없이

@Controller
public class MemberController {

    private final MemberService memberService;

    @Autowired
    public MemberController(MemberService memberService) {
        this.memberService = memberService;
    }

    @PostMapping("members/new")
    public String create(MemberForm form){
        Member member = new Member();
        member.setName(form.getName());
        memberService.join(member);
        return "redirect:/";
    }
}
  • 클라이언트(수신객체)에 사용할 memberService를 private final 키워드를 사용해 선언.
  • 생성자로 의존성 주입 후 해당 메소드 위에 @Autowired 어노테이션 추가
        public MemberController(MemberService memberService) {
            this.memberService = memberService;
        }

Lombok활용

@RestController
@RequiredArgsConstructor
public class PostController {
    private final PostRepository postRepository;
    private final PostService postService;

    @PostMapping("/posts")
    public Post createPost(@RequestBody PostRequestDto requestDto){
        Post post = new Post(requestDto);
        return postRepository.save(post);
    }
}
  • 클라이언트(수신객체)에 사용할 postRepository, postService를 private final 키워드를 사용해 선언.
  • Controller 클래스 위에 @RequiedArgsConstructor 어노테이션 추가만 해주면 됨.

2. IoC(제어의 역전)

제어의 역전( Inversion of Control ) : 프로그래머가 작성한 프로그램이 재사용 라이브러리의 흐름 제어를 받게 되는 소프트웨어 디자인 패턴을 말한다.

  • 제어의 역전은 의존성 주입의 상위 개념이다.
  • Spring Container가 필요에 따라 개발자 대신 Bean들을 관리(제어)해주는 행위
    • Spring Container는 기본적으로 싱글톤 패턴의 객체 생성을 지향한다.
    • 별도로 설정하지 않으면 모든 컨테이너 안의 빈(Bean) 객체는 딱 하나만 생성되어 계속 재사용된다. Why? 내부 데이터가 바뀌지 않는 객체들을 여러 개 생성해서 사용하는 것은 자원 낭비이기 때문이다.
    • 그래서 스프링 컨테이너를 싱글톤 관리 컨테이너라고 부르는 사람들도 있는 것 같다.
  • 일반적인 상황에서는 개발자가 직접 객체를 제어해야 했다. new 연산자를 통해 객체를 생성하고, 객체의 의존성을 맺어주고, 초기화를 해주고 등등..
  • Spring 에서는 xml파일 또는 어노테이션 방식으로 스프링 컨테이너에 Bean(객체)를 등록하기만 하면, 스프링 컨테이너에서 Bean의 생명주기(생성 -> 의존성 설정 -> 초기화 -> 소멸)를 전부 관리해준다.
  • 즉, 객체에 대한 제어권이 컨테이너로 역전되기 때문에 제어의 역전이라고 하는 것

  • 일반적으로 처음에 배우는 자바 프로그램에서는 각 객체들이 프로그램의 흐름을 결정하고 각 객체를 직접 생성하고 조작하는 작업(객체를 직접 생성하여 메소드 호출)을 했다. 즉, 모든 작업을 사용자가 제어하는 구조였다.
  • 예를 들어 A 객체에서 B 객체에 있는 메소드를 사용하고 싶으면, B 객체를 직접 A 객체 내에서 생성하고 메소드를 호출한다.
  • 하지만 IOC가 적용된 경우, 객체의 생성을 특별한 관리 위임 주체에게 맡긴다. 이 경우 사용자는 객체를 직접 생성하지 않고, 객체의 생명주기를 컨트롤하는 주체는 다른 주체가 된다.
  • 즉, 사용자의 제어권을 다른 주체에게 넘기는 것을 IOC(제어의 역전) 라고 합니다.

장점

  • 개발자는 객체 관리에 덜 신경쓸 수 있게 된다.
  • 약한 결합을 이용하여 객체 간 의존 관계를 쉽게 변경할 수 있다.
  • 결과적으로 코드의 재사용성과 유지보수성을 높인다.

3. Spring Bean(스프링 빈)

스프링 빈(Spring Bean) : Spring IoC 컨테이너가 관리하는 자바 객체를 말한다.

  • 우리가 알던 기존의 Java Programming 에서는 Class를 생성하고 new를 입력하여 원하는 객체를 직접 생성한 후에 사용했었다.
  • 하지만 Spring에서는 직접 new를 이용하여 생성한 객체가 아니라, Spring에 의하여 관리당하는 자바 객체를 사용한다. 이렇게 Spring에 의하여 생성되고 관리되는 자바 객체를 Bean이라고 한다.
  • Spring Framework 에서는 Spring Bean 을 얻기 위하여 ApplicationContext.getBean() 와 같은 메소드를 사용하여 Spring 에서 직접 자바 객체를 얻어서 사용합니다.

3-1 Spring Bean을 Spring IoC Container에 등록하는 방법

  • Spring Bean을 Spring IoC Container에 등록하는 방법은 2가지가 있다.

3-1-1 자바 어노테이션(Java Annotation)을 사용하는 방법

  • 자바 어노테이션 참고!
    • JAVA에서 어노테이션 이라는 기능이 있다. 사전상으로는 주석의 의미이지만 Java 에서는 주석 이상의 기능을 가지고 있다.
    • 어노테이션은 자바 소스 코드에 추가하여 사용할 수 있는 메타데이터의 일종이다.
    • 소스코드에 추가하면 단순 주석의 기능을 하는 것이 아니라 특별한 기능을 사용할 수 있다.
  • 어노테이션 예제
    • Java에서는 @Override, @Deprecated 와 같은 기본적인 어노테이션을 제공한다. 아래의 상속 예제에서는 @Override 를 이용하여 상속임을 명시해준다.
    public class Parent { 
        public void doSomething() { 
            System.out.println("This is Parent"); 
        } 
    } 
    
    public class Son extends Parent{ 
        @Override 
        public void doSomething() { 
            System.out.println("This is Son"); 
        } 
    }
  • 그럼 실제 Spring에서 어떤 어노테이션을 사용해 Bean 등록할까?
    • @Component 어노테이션을 사용해 Bean을 등록한다.
    • @Component 어노테이션이 등록되어 있는 경우에는 Spring이 Annotation을 확인하고 자체적으로 Bean으로 등록한다.
  • Spring @Component 예제
    • 왜 @Component가 없지? 라고 생각할 수 있다. 해당 부분을 확인하기 위해서 인텔리제이를 사용한다면 Ctrl을 누른 상태로 @Controller를 클릭해본다. 그럼 아래와 같은 코드를 확인할 수 있다.
    • @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 ""; }
    • 위 @Controller 어노테이션 코드에서 @Component가 있음을 확인할 수 있다.
    • @Component 어노테이션을 통해 Spring은 해당 Controller를 Bean으로 등록한다.
  • @Controller public class MemberController { private final MemberService memberService; @Autowired public MemberController(MemberService memberService) { this.memberService = memberService; } @GetMapping("/members/new") public String createForm(){ return "/members/createMemberForm"; } }

3-1-2. Bean Configuration File에 직접 Bean 등록하는 방법

  • @Configuration과 @Bean 어노테이션을 이용하여 Bean을 등록할 수 있다.
  • 아래의 예제와 같이 @Configuration을 이용하면 Spring 프로젝트에서의 Configuration 역할을 하는 Class를 지정할 수 있다.
  • 해당 파일 하위에 Bean으로 등록하고자 하는 Class에 @Bean 어노테이션을 사용해주면 간단하게 Bean을 등록할 수 있다.
  • @Configuration public class SpringConfig { private final DataSource dataSource; private final EntityManager em; @Autowired public SpringConfig(DataSource dataSource, EntityManager em) { this.dataSource = dataSource; this.em = em; } @Bean public MemberService memberService(){ return new MemberService(memberRepository()); } @Bean public MemberRepository memberRepository(){ return new JpaMemberRepository(em); } }

참고자료

의존성 주입

제어의 역전

스프링 빈

작성일자 : 2022년 7월 9일

반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.