[WIL] 항해 3주차 회고(feat. DI,Ioc,Bean)

rara_kim·2022년 12월 4일
0

항해99

목록 보기
11/18

3주차 회고

3주차는 주특기 입문 주차로 스프링 학습을 시작했다.
공부해야할 건 많은데 물리적으로 시간이 부족하다보니 핵심만 공부해야했고, 그러다 보니 개념적 부분에서 모르고 지나치는 부분이 많았었다.
그런 부족한 부분을 채우려 부단히 노력하며 힘겹지만 뿌듯한 한주를 보냈다.
사실 스프링 부트로 프로젝트를 진행해보기는 했지만 그저 인터넷 강의를 보며 따라치기만 했었기에, 이번 주부터는 생각하고 이해하면서 코드를 작성하고자 생각하고 또 생각하며 학습했다.

강의를 보며 진행한 학습 코드도 두번씩 다시 작성해봤고 개인 과제로 제출한 CRUD 코드도 정말 여러번 고치고 또 고치고...
그러면서 Request를 받아 어떻게 Response를 날리는지 과정을 이해할 수 있었다.

또한 비전공자로서 CS지식이 부족하다보니 코드를 작성하면서 동작방식이나 일련의 흐름들에 대해 이해하기 다소 어려운 편인데, 개인과제를 빨리 끝내고서 부족했던 부분들에 대해 구글링하고 여러 블로그를 찾아보며 공부하니 훨씬 더 이해가 잘 되었다.
앞으로 이어질 숙련 주차, 심화 주차에서도 과제를 빠르게 해결하고(가능하다면) 조금 더 깊이 있는 개념적인 부분을 학습하고자 한다.

항해99는 물리적으로 시간이 부족하다보니 다소 빡빡하게 스케줄이 전개되지만, 한주가 지나고 되돌아보면 한주동안 모르던 걸 많이 알게 되었구나, 열심히 살았구나 하고 뿌듯함이 느껴지고 다시 한주동안 화이팅하자! 하는 마음이 샘솟는다.
4주차 숙련주차에서도 밀도있게 집중해서 Spring을 정말 내 주특기로 만들어 나가고 싶다.

📌이번주 키워드

DI(Dipendency Injection)

Spring 프레임워크는 3가지 핵심 프로그래밍 모델을 지원하는데, 그 중 하나가 의존성 주입(Dependency Injection, DI)이다.
DI란 객체를 직접 생성하는 게 아니라 외부에서 생성한 후 주입 시켜주는 방식이다.

외부에서 두 객체 간의 관계를 결정해주는 디자인 패턴으로, 인터페이스를 사이에 둬서 클래스 레벨에서는 의존관계가 고정되지 않도록 하고 런타임시에 관계를 동적으로 주입하여 유연성을 확보하고 결합도를 낮출 수 있게 해준다.

public class Store {
	
    private Product product;

}

두 객체 간의 관계(의존성)를 맺어주는 것을 의존성 주입이라고 하며 생성자 주입, 필드 주입, 수정자 주입 등 다양한 주입 방법이 있는데, Spring 개발팀에서는 생성자 주입을 권장하고 있다.

생성자 주입(Constructor Injection)

생성자 주입(Constructor Injection)은 생성자를 통해 객체를 생성할 때 의존 관계를 주입하는 방법이다.

@Service
public class UserService {

    private UserRepository userRepository;
    private MemberService memberService;

    @Autowired
    public UserService(UserRepository userRepository, MemberService memberService) {
        this.userRepository = userRepository;
        this.memberService = memberService;
    }
}

생성자 주입은 생성자의 호출 시점에 1회 호출 되는 것이 보장된다.
그렇기 때문에 주입받은 객체가 변하지 않거나, 반드시 객체의 주입이 필요한 경우에 강제하기 위해 사용할 수 있다.

의존성 주입이 필요한 이유

public class Store {
	private Fruit fruit;
    
    public Store() {
    	this.fruit = new Fruit;
}

1️⃣두 클래스가 강하게 결합되어 있다.

위와 같은 Store 클래스는 현재 Fruit 클래스와 강하게 결합되어 있다는 문제점이 있다.
만약 Store에서 Fruit 아닌 무언가 다른 것을 판매하고자 한다면 Store클래스의 생성자에 변경이 필요하다. 즉, 유연성이 떨어진다.
이에 대한 해결책으로 상속을 떠올릴 수 있지만, 상속을 제약이 많고 확장성이 떨어지므로 피하는 것이 좋다.

2️⃣객체들 간의 관계가 아니라 클래스 간의 관계가 맺어짐

위의 Store와 Fruit은 객체들 간의 관계가 아니라 클래스들 간의 관계가 맺어져 있다는 문제가 있다.
올바른 객체지향적 설계라면 객체들 간에 관계가 맺어져야 한다.
객체들 간에 관계가 맺어졌다면 다른 객체의 구체 클래스(Fruit인지 Milk 인지 등)를 전혀 알지 못하더라도, (해당 클래스가 인터페이스를 구현했다면) 인터페이스의 타입(Product)으로 사용할 수 있다.

결국 위와 같은 문제점이 발생하는 근본적인 이유는 Store에서 불필요하게 어떤 제품을 판매할 지에 대한 관심이 분리되지 않았기 때문이다. Spring에서는 DI를 적용하여 이러한 문제를 해결하고자 한다.

IoC(Inversion of Control)

IoC(Inversion of Control)란 제어의 역전 이라는 의미로, 말 그대로 메소드나 객체의 호출작업을 개발자가 결정하는 것이 아니라, 외부에서 결정되는 것을 의미한다.

객체의 의존성을 역전시켜 객체 간의 결합도를 줄이고 유연한 코드를 작성할 수 있게 하여 가독성 및 코드 중복, 유지 보수를 편하게 할 수 있게 한다.

스프링에서는 다음과 같은 순서로 객체가 만들어지고 실행된다.

  1. 객체 생성
  2. 의존성 객체 주입(개발자가 만드는것이 아니라 제어권을 스프링에게 위임하여 스프링이 만들어놓은 객체를 주입한다.)
  3. 의존성 객체 메소드 호출

스프링이 모든 의존성 객체를 스프링이 실행될때 다 만들어주고 필요한곳에 주입시켜줌으로써 Bean들은 싱글톤 패턴의 특징을 가지며, 제어의 흐름을 사용자가 컨트롤 하는 것이 아니라 스프링에게 맡겨 작업을 처리하게 된다.

Bean

Bean(빈)이란?

스프링 빈은 스프링 컨테이너에 의해 관리되는 자바 객체(POJO)를 의미한다.

스프링 컨테이너

스프링 컨테이너는 스프링 빈의 생명 주기를 관리하며, 생성된 스프링 빈들에게 추가적인 기능을 제공하는 역할을 한다. IoC와 DI의 원리가 스프링 컨테이너에 적용된다.

개발자는 new 연산자, 인터페이스 호출, 팩토리 호출 방식으로 객체를 생성하고 소멸하지만, 스프링 컨테이너를 사용하면 해당 역할을 대신해 준다. 즉, 제어 흐름을 외부에서 관리하게 된다. 또한, 객체들 간의 의존 관계를 스프링 컨테이너가 런타임 과정에서 알아서 만들어 준다.

스프링 컨테이너 생성

  • ApplicationContext 인터페이스를 이용한다.
  • 자주 사용하는 구현체
    • AnnotationConfigApplicationContext : 어노테이션 이용방식
    • GenericXmlApplicationContext : XML 이용방식
  • 컨테이너에 두 개 이상의 설정 파일 등록방법
    • 사용하고자 하는 설정 파일들을 매개변수로 전달
public class Main{
	public static void main(String[]args) {
    
    	//컨테이너 생성
		AnnotationConfigApplicationContext context = 
           	 	new AnnotationConfigApplicationContext(AppConfig.class);
                
        //두 개 이상의 설정 파일 등록
        new AnnotationConfigApplicationContext(AppConfig.class, Appconfig2.class);

		//컨테이너 종료
		context.close();
	}
}

스프링 빈 등록

@Component Scan

컴포넌트 스캔은 @Component를 명시하여 빈을 추가하는 방법이다. 클래스 위에 @Component를 붙이면 스프링이 알아서 스프링 컨테이너에 빈을 등록한다.

@Configuration
@ComponentScan(basePackages ={"com.example.spring_framework"})
public class AppConfig{ }

컴포넌트 스캔의 대상

@Component 외에 @Controller, @Service, @Repository, @Configuration는 @Component의 상속을 받고 있으므로 모두 컴포넌트 스캔의 대상이다.

  • @Controller
    스프링 MVC 컨트롤러로 인식된다.
  • @Repository
    스프링 데이터 접근 계층으로 인식하고 해당 계층에서 발생하는 예외는 모두 DataAccessException으로 변환한다.
  • @Service
    특별한 처리는 하지 않으나, 개발자들이 핵심 비즈니스 계층을 인식하는데 도움을 준다.
  • @Configuration
    스프링 설정 정보로 인식하고 스프링 빈이 싱글톤을 유지하도록 추가 처리를 한다. (물론 스프링 빈 스코프가 싱글톤이 아니라면 추가 처리를 하지 않음.)

@Bean

  • @Configuration 클래스(설정 파일)에서 사용한다.
  • 속성값으로 빈 이름을 등록할 수 있다.
  • 별다른 설정을 하지 않으면 메소드명이 빈 이름으로 등록된다.
@Configuration
public class AppConfig {

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

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

@Bean vs @Component

  • @Bean
    • 개발자가 컨트롤이 불가능한 외부 라이브러리들을 Bean으로 등록하고 싶은 경우
      에 사용된다.
    • 메소드 또는 어노테이션 단위에 붙일 수 있다.
  • @Component
    • 개발자가 직접 컨트롤이 가능한 클래스들의 경우에 사용된다.
    • 클래스 또는 인터페이스 단위에 붙일 수 있다.

profile
느리더라도 꾸준하게

0개의 댓글