Skip to content

Latest commit

 

History

History
428 lines (264 loc) · 19.8 KB

TIL220311.md

File metadata and controls

428 lines (264 loc) · 19.8 KB

Daily to do list

Java


Spring


CS


알고리즘


오늘의 회고

220311 Spring

의존관계 자동 주입

————————— 다양한 의존관계 주입 방법

의존관계 주입은 크게 4가지 : 생성자 주입, 수정자 주입(setter), 필드 주입, 일반 메서드 주입

  • 생성자 주입 이름 그대로 생성자를 통해서 의존 관계 주입.

특징 : 생성자 호출 시점에서 딱 1번 호출되는 것이 보장! 불변, 필수 의존관계에 사용 (예) 공연을 시작하기 전에 배우들을 모두 정하고 공연을 시작하는 것!! 공연하는 중간에 배우들을 못바꾸게 하고 싶은것

불변이 필요한 것이라면 수정자를 아예 만들지 말자..

?? final 은 최종적이라는 뜻으로 초기값이 저장되면 도중에 수정을 할 수 없다. + 값이 무조건 있어야 한다는 뜻, 객체자체는 변경 불가능, 객체 내부 변수는 변경 가능

final 필드 : 초기값을 줄 수 있는 방법이 2가지 필드 선언시, 생성자 (초기화 안되어있으면 컴파일 에러) final 객체 : 객체자체는 변경 불가능하지만 객체 내부 변수는 변경 가능 + 초기화가 안되어 있으면 컴파일 에러 final 클래스 : 상속이 불가능한 클래스 필드는 Setter를 통해 변경 가능 final 메서드 : 상속받은 클래스에서 부모의 final 메서드를 재정의 할 수 없다.(시스템 코어) final 메서드 인자값 : 인자값 변경 불가능(잘사용은 안함)

final : 생성자, 초기값 지정 안하면 컴파일 에러(필수로 값을 받고 싶을 때), 메서드에 쓰면 오버라이드 불가능(시스템 코어 등 바뀌면 안되는 것들)

필수에 사용하는 이유 : 생성자가 필수로 필요한 인자를 받아서1?!

**** 중요!!! 생성자가 딱 1개만 있으면 @Autowired를 생략해도 됨

  • 수정자 주입 필드명에 set을 앞에 붙이는 것을 보통 수정자(setter)라고 함. 필드값을 그냥 수정할수는 없으니 수정자 이용

특징 : 선택적(수정자 부를 때! 의존관계를 주입하니), 변경 가능(잘 사용하진 않을듯 객체 지향 위반)

참고 : @Autowired의 기본 동작은 주입할 대상이 없으면 오류가 발생, 주입할 대상이 없어도 동작하게 하려면 @Autowired(required = false) 로 지정

참고 2: 자바빈 프로퍼티, 자바에서는 과거부터 필드의 값을 직접 변경하지 않고 set,get라는 메서드를 통해서 값을 읽거나 수정하는 규칙을 만들었다. 그것이 자바빈 프로퍼티 규약. 쓰는 이유가 좀 궁금한데..

참고 3 스프링 컨테이너의 라이프 사이클 :

  1. 스프링 컨테이너 생성
  2. 스프링 빈 등록
  3. 스프링 빈 의존관계 설정 - 준비
  4. 스프링 빈 의존관계 설정 - 완료 ** 생성자 주입의 경우 스프링 빈 등록과 함꼐 이루어짐(어쩔 수 없는 생성자..호출 때문에)
  • 필드 주입 ( 안씀.., 테스트 코드에서만 가끔 씀) 그냥 바로 필드에서 @Autowired 박는거… 의존관계를 그냥 박음 private여도 가능;; 외부에서 변경이 불가능해서 테스트하기 힘들다.

  • 일반 메서드 주입 그냥 아무 메서드에서나 해놓고 @Autowired를 붙이는 방법… 생각해보면 수정자 주입이랑 비슷, 거의 사용하지 않음..

———————————— 옵션처리

주입할 스프링 빈이 없어도 동작해야 할 때가 있다..!

@Autowird가 붙은 애노테이션 객체를 봤는데 스프링 빈이 아니면?! 이라는 이야기였음..

@Autowired(required = false) >>>> 대상이 없으면 수정자 메서드 자체가 호출x @Nullable >>> null Optional<> >>>> Optional.empty로 출력됨

참고 : @Nullable, Optional은 스프링 전반에 걸쳐서 지원된다..

———————————— 왜 생성자 주입을 선택해야 하는가?!

과거에는 수정자 주입과 필드 주입을 많이 사용했지만! 최근에는 스프링을 포함한 DI 프레임워크 대부분이 생성자 주입을 권장한다!!

  • 불변 대부분의 의존관계 주입은… 한번! 일어나면 어플 종료까지 의존관계 변경할 일이 없다.. (오히려 대부분의 의존관계는 종료 전까지 변하면 안된다. 불변해야 한다..!)

수정자 주입을 사용하면, setter를 열어놔야 한다.

즉, 생성자 주입은 객체를 생성할 떄 딱 1번만 호출되므로 이후에 호출될 일이 없다!. 불변

  • 누락 프레임워크 없이 순수한 자바 코드를 단위 테스트 하는 경우에

생상자 주입을 할 경우에는 단위 테스트를 진행하면서도 혹여나 생성자 인자를 안넣을 경우에 컴파일 에러가 발생한다. 하지만 수정자로 넣거나 할 경우 직접 객체에 들어가서 필요한 인자들을 확인해야 한다.

또 좋은거 !!! 생성자 주입을 하면 final로 객체를 만들 수 있음 그러면 생성자에서 값을 안넣으면 컴파일 에러가 뜨고 이렇게 하면 누락이 되는 경우를 방지 final 은 생성자에서 값 주입 해야만 가능 즉 생성자 주입을 해주면 가능

즉, final에다가 생성자 주입을 하면 누락을 막아준다.

컴파일 오류가 세상에서 가장 빠르고 좋은 오류이다!!

참고, 수정자 주입을 포함한 나머지 주입 방식은 모두 생성자 이후에 호출되므로, 필드에 final 키워드를 사용할 수 없다 오직 생성자 주입만 final키워드 사용 가능

  • 정리 생성자 주입 방식을 선택하는 이유는 여러가지가 있지만, 프레임워크에 의존하지 않고, 순수한 자바 언어의 특징을 잘 살리는 방법이기도 함.

기본으로 생성자 주입을 사용하고 필수 값이 아닌 경우에는 수정자 주입 방식을 옵션으로 부여하면 된다.

결론:!? 항상 생성자 주입을 선택해라!! 그리고 가끔 옵션으로 선택자 주입을 선택하고 필드 주입은 절대 쓰지마라…

———————————— 롬복과 최신 트랜드

막상 개발해보면 대부분 불변, final키워드에 생성자를 쓴다 근데 이렇게 생성자 만들고 주입 받는 값을 대입하는 코드 만들어야 하는 이것조차.. 귀찮았던 사람들이 “필드주입”처럼 편리하게 사용하는 방법을 고민하다가 만든 것

롬복 = 게터 세터 등 어노테이션도 가능하고 뭐 생성자 등등 다양하게 사용하는데 많이 씀 @RequiredArgsConstructor… 이건 final 붙은 생성자를 자동으로 만들어 줌;;;;

  • 정리 최근에는 생성자를 딱 1개 두고, @Autowired 생락을 주로ㅅ 씀 여기다가 롬복 의 @RequiredArgsConstructor 까지 쓰면.. 진짜 짧아짐

—————————————— 조회 빈이 2개 이상이면 - 문제

@Autowired 는 타입으로 조회를 한다

마치 ac.getBean(DiscountPolicy.class) 이런식으로 타입으로 조회를 함

자동으로 주입할 경우 2개 이상 즉 discount에서 fix,rate 객체를 두개 다 @Component로 하면 타입으로 조회하기 때문에 두개가 선택되게 된다..

즉, 자동 의존관계 주입으로 하면 타입으로 검색하기 때문에 두 개 이상일 경우는 자동 의존관계 주입으로 하면 안되는 것이다. NoUniqueBean

이때 하위 타입으로 지정할 수도 있지만, 하위 타입으로 지정하는 것은 DIP를 위배(객체를 선택하게 됨)하고 유연성이 떨어진다. 이름만 다르고 완전히 똑같은 타입의 스프링 빈이 2개 있을 때 해결이 안된다.

스프링 빈을 수동으로 등록해서 문제를 해결해도 되지만, 자동 주입으로 해결할 수 있는 여러가지 방법이 있다!

—————————————— @Autowired 필드 명, @Qualifier, @primary

위 문제 해결 방법

  • @Autowired 필드 명 매칭

  • @Quilifier >> @Quilifier 끼리 매칭 >> 빈 이름 매칭

  • @Primary 사용

  • @Autowired 필드 명 매칭 @Autowired는 타입 매칭을 시도, 이 때 여러 빈이 있으면 필드 이름이나 파라미터 이름으로 빈을 추가 매칭

필드 명을 빈 이름으로 변경 : @Autowired private DiscountPolicy rateDiscountPolicy

이런 식으로 필드 이름을 빈 이름으로 변경

정리 :

  1. 타입 매칭
  2. 타입 매칭 결과가 2개 이상일 때 필드 명, 파라미터 명으로 빈 이름 매칭
  • @Quilifier 사용 추가 구분자를 붙여주는 방법, 주입 시 추가적인 방법을 제공하는 것이지만 빈 이름 변경은 아님

 이런식으로 애노테이션을 붙이면 됨

만약에 못찾으면??? 애노테이션 안의 이름의 스프링 빈을 추가로 찾는다. 하지만…. 명확하게 사용하는 것이 좋다 이렇게 해서는 안된다!

그래도 안되면 예외가 발생(NoSuchBeanDefinitionException

 이렇게 빈 등록시에도 사용 가능

  • @Primary 사용

자주 사용 @Primary 는 우선순위를 정하는 방법

 이런식으로 애노테이션 하나만 붙이면 가장 최우선 순위로 생각하고 실행

정리: 여기까지 보면 @Primary, @Quilifier 중 어떤 것을 사용하면 좋을지 고민이 된다. @Quilifier의 단점은 주입 받을 때 모든 코드에 붙여줘야한다는 점이다.

 @Primary 보다 @Quilifier가 우선순위가 높다

——————————————— 애노테이션 직접 만들기

애노테이션은 상속이라는 개념이 없다. 이렇게 여러 애노테이션을 모아서 사용하는 기능을 스프링이 지원해주는 기능이다. 조합해서도 쓰고 재정의할수도 있다. 근데 뚜렷한 목적 없이 하는 것은 혼란만 더 만든다

————————————— 조회한 빈이 모두 필요할 떄, List, Map

의도적으로 해당 타입의 스프링 빈이 다 필요한 경우도 있다.

(예) 할인 서비스를 제공하는데, 클라이언트가 할인의 종류를 선택할 수 있다고 가정하면 스프링을 사용하면 소위 말하는 전략 패턴을 매우 간단하게 구현 가능

DiscountService는 Map으로 모든 DiscountPolicy를 주입 받는다.

———————————————— 자동, 수동 의 올바른 실무 운영 기준

편리한 자동 기능을 기본으로 사용하자

결론으론 스프링이 나오고 시간이 갈수록 점점 자동을 선호하는 추세. 스프링은 @Component 뿐만 아니라 @Controller, @Service, @Repository 처럼 계층에 맞추어 일반적인 애플리케이션 로직을 자동으로 스캔할 수 있도록 지원한다. 거기에 더해 최근 스프링 부트는 컴포넌트 스캔을 기본으로 사용하고, 스프링 부트의 다양한 스프링 빈들도 조건이 맞으면 자동으로 등록하도록 설계

실무에서) 개발자 입장에서 스프링 빈을 하나 등록할 때! @Component만 넣어주면 끝나는 일을 @Configuration 설정 정보에 가서 @Bean을 적고… 객체를 생성하고.. 주입할 대상을 일일이 적어주는 과정이 상당히 번거롭다.

또 관리할 빈이 많아서 설정 정보가 커지면 설정 정보 관리하는 것 자체가 부담이 된다.

그리고 아주 결정적 ******* 자동 빈 등록을 해도 OCP, DIP를 지킬 수 있다.

그렇다면 수동 빈 등록은 언제 사용?????

애플리케이션은 크게 업무 로직과 기술 지원 로직으로 나눌 수 있다. 업무 로직 빈 : 웹을 지원하는 컨트롤러, 핵심 비즈니스 로직이 있는 서비스, 데이터 계층의 로직을 처리하는 리포지토리 등… 모두 업무 로직이다. 보통 비즈니스 요구사항을 개발할 때 추가되거나 변경된다.

기술 지원 빈 : 기술적인 문제나 공통 관심사(AOP)를 처리할 때 주로 사용된다. 데이터베이스 연결, 공통 로그 처리, 하부 기술이나 공통 기술들

업무 로직은 숫자도 매우 많고, 한번 개발해야 하면 컨트롤러, 서비스 리포지토리 처럼 어느 정도 유사한 패턴이 있다. 이런 경우 자동 기능을 적극 사용하는 것이 좋다. 보통 문제가 발생해도 어떤 곳에서 문제가 발생했는지 명확하게 파악하기 쉽다.

기술 지원 로직은 업무 로직과 비교해서 그 수가 매우 적고, 보통 매플리케이션 전반에 걸쳐서 광범위하게 영향을 미친다. 그리고 업무 로직은 문제가 발생햇을 때 어디가 문제인지 명확하게 잘 들어나지만, 기술 지원 로직은 적용이 잘 되고 있는지 아닌지 조차 파악하기 어려운 경우가 많다. 그래서 이런 기술 지원 로직들은 가급적 수동 빈 등록을 사용해서 명확하게 들어내는 것이 좋다.

결론 : 애플리케이션에 광범위하게 영향을 미치는 기술 지원 객체는 수동 빈으로 등록해서 딱! 설정 정보에 바로 나타나게 하는 것이 좋다.

비즈니스 로직 중에서 다형성을 적극 활용할 때도 수동 빈 등록이 좋다!!

의존관계 자동 주입으로 주입을 받는 경우를 보면 각 빈들의 이름은 무엇인지 코드만 보고 한번에 쉽게 파악할 수 있을까?;; 힘들다 이럴 때 수동 빈으로 등록하거나 자동으로 하면 ** 특정 패키지에 같이 묶어 두는게 좋다.  즉, 이렇게 수동처럼 한 눈에 딱 보고 이해가 되어야 한다.

정답은 없다 하지만 한눈에 볼 수 있게 혹은 편하게 등 명확한 목적에 맞추면 될 것 같다!

참고로 “스프링과 스프링 부트가 자동으로 등록하는 수많은 빈들은 예외” 이런 부분들은 스프링 자체를 이해하고 스프링의 의도대로 사용하는게 중요

혹시나 스프링 부트가 아니라 내가 직접 기술 지원 객체를 스프링 빈으로 등록한다면 수동으로 등록해서 명확하게 들어내는 것이 좋다!!!

  • 정리
  1. 편리한 자동 등록 기능을 기본으로 사용하자
  2. 직접 등록하는 기술 지원 객체는 수동으로 등록
  3. 다형성을 적극 활용하는 비즈니스 로직은 수동 등록을 고민해보자!

빈 생명주기 콜백 —————————— 빈 생명주기 콜백 시작

데이터베이스 커넥션 풀이나(미리 서버와 데이터베이스를 연결.), 네트워크 소켓처럼 애플리케이션 시작 시점에 필요한 연결을 미리 해두고, 애플리케이션 종료 시점에 연결을 모두 종료하는 작업을 진행하려면,

객체의 초기화와 종료 작업이 필요하다.

스프링을 통해 이러한 초기화작업, 종료 작업은 어떻게 진행되는가?!

스프링 빈은 간단하게 다음과 같은 라이프사이클을 가진다. 객체 생성 >>> 의존관계 주입 (생성자 주입은 어쩔 수 없이 객체 생성과 동시에 이루어짐)

스프링 빈은 객체를 생성하고 의존관계 주입이 다 끝난 후에 필요한 데이터를 사용할 수 있는 준비가 완료된다. 즉, 초기화 작업은 객체 생성과 의존관계 주입까지 모드 완료된 후 호출해야 한다. 그런데 개발자가 이 의존관계 주입까지 끝난 시점을 알기는 힘들다.

그래서!!! 스프링은 의존관계 주입이 완료되면 스프링 빈에게 콜백 메서드를 통해서 초기화 시점을 알려주는 다양한 기능을 제공, 또한 스프링은 스프링 컨테이너가 종료되기 직전에 소멸 콜백을 준다.

스프링 빈의 이벤트 라이프 사이클 스프링 컨테이너 생성 >> 스프링 빈 생성 >> 의존관계 주입 >> 초기화 콜백

코드 사용 >> 소멸전 콜백 >> 스프링 종료

초기화 콜백 : 빈이 생성되고, 빈의 의존관계 주입이 완료된 후 호출 소멸전 콜백 : 빈이 소멸되기 직전에 호출

스프링은 다양한 생명 주기 콜백을 지원

참고 : 객체의 생성과 초기화를 분리하자. (단일 책임 원칙?에 맞지 않나)

  • 생성자는 필수 정보(파라미터)를 받고, 메모리를 할당해서 객체를 생성하는 책임을 가진다.
  • 초기화는 이렇게 생성된 값들을 활용해서 외부 커넥션을 연결하는 등 무거운 동작을 수행

즉, 생성자 안에서 무거운 초기화 작업을 함께 하는 것보다는 객체를 생성하는 부분과 초기화 하는 부분을 명확하게 나누는 것이 유지보수 관점에서 좋다. 물론 초기화가 단순하면 생성자가 한번에 해도 나을 수 있다.

———————————— 스프링은 크게 3가지 방법으로 빈 생명주기 콜백을 지원

  • 인터페이스
  • 설정 정보에 초기화 메서드, 종료 메서드 지정
  • 애노테이션 지원 ( @PostConstruct, @PreDestory) ————————————
  • 인터페이스  InitializingBean, DisposableBean을 인터페이스로 받으면 afterPropertiesSet, destroy 메서드를 사용할 수 있음

단점… : 이것은 스프링 전용 인터페이스라 스프링 전용 인터페이스에 의존 초기화, 소멸 메서드의 이름 변경 불가 내가 코드를 고칠 수 없는 외부 라이브러리에 적용할 수 없다.

참고 : 인터페이스 초기화는 스프링 초기에 나온 것으로 잘 사용 안함

———————————— 빈 등록 초기화, 소멸 메서드

설정 정보에 @Bean(initMethod = “init”, destroyMethod = “close”)  하면 된다.

설정 정보 사용 특징: 메서드 이름을 자유롭게 할 수 있다. 스프링 빈이 스프링 코드에 의존하지 않는다. 코드가 아니라 설정 정보를 사용하기 때문에 코드를 고칠 수 없는 외부 라이브러리에도 사용 가능 (제일 큰 장점)

종료 메서드 추론: @Bean의 destroyMethod 속성엔 특별한 기능이 있따. 라이브러리 대부분 close, shutdown이라는 이름의 종료 메서드를 사용한다. @Bean의 destroyMethod는 기본값이 (interred)추론 으로 등록되어 있다. 이 기능은 close, shutdown 이라는 이름의 메서드를 자동으로 호출 해준다.

따라서 직접 스프링 빈으로 등록하면 종료 메서드는 따로 적어주지 않아도 잘 동작한다. 추론 기능이 싫으면 destroyMethod=“” 처럼 빈 공백을 지정하면 된다…하지만 쓰진않는다

——————————————— 애노테이션으로 설정 (이걸 쓰면 된다 ㅎㅎ.) @PostConstruct, @preDestroy

최신 스프링에서 가장 권장하는 방법 애노테이션만 붙이면 된다 편리 패키지를 보면 javax 즉 스프링 종속이 아닌 자바, 스프링이 아닌 다른 컨테이너에서도 동작 컴포넌트 스캔과도 잘 어울린다.

유일한 단점: 외부 라이브러리에 적용 못함 혹시 외부 라이브러리를 초기화, 종료 해야 하면 @Bean 기능을 사용해야 한다

  • 정리 @PostConstruct, @PreDestroy를 사용하면 된다 ㅋ; 코드를 고칠 수 없는 외부 라이브러리를 초기화 종료해야하면 @Bean 의 initMethod, destroyMethod를 사용하면 된다!!!

—————————————— 빈 스코프란?

———————— 빈 스코프

지금까지 우리는 스프링 빈이 스프링 컨테이너의 시작과 함께 생성, 종료까지 유지로 학습함 이것은 스프링 빈이 기본적으로 싱글톤 스코프로 생성되기 떄문이다. 스코프란 말은 말 그대로 빈이 존재할 수 있는 범위

스프링은 다양한 스코프를 지원함 싱글톤 : 기본 스코프 시작과 종료까지

프로토타입 : 초기화까지 불러주고 클라이언트에게 반환하고 끝 (종료 메서드 호출x)

웹 관련 스코프 : request : 웹 요청이 들어오고 나갈때까지 유지 session : 웹 세션이 생성되고 종료될 때까지 유지 application : 웹의 서블릿 컨텍스와 같은 범위로 유지

 이렇게 컴포넌트 위에 자동등록 가능, 빈 위에 수동 등록도 가능