/spring-boot-study

spring-boot-study

Primary LanguageJava

spring-boot-study

spring-boot-study

git test 입니다.

Solid

  1. SRP - 단일 책임 원칙
  2. OCP - 개방 폐쇄 원칙
    • 변경에는 닫겨있고 확장에는 열려있다.
    • interface를 통해 역할이 같은 객체로 변경할때는 다른 코드의 변경이 필요없다.
    • 다형성을 사용했지만 ocp원칙을 지킬 수 없기 때문에 di 라는 원칙이 생긴다.
  3. LSP - 리스코프치환원칙
    • 상속된(구현된) 자식은 부모를 완전히 대체해야한다.
    • 인터페이스를 구현한 구현체는 인터페이스를 완전히 대체한다.
    • 컴파일에 단순한게 아니고 기능적인 구현을 보장해야함.
    • ex) 자동차 인터페이스의 엑셀은 앞으로 가는 기능인데 뒤로가게 구현하면 위반
  4. ISP - 인터페이스 분리 원칙
    • 특정 클라이언틀르 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.
    • 자동차 인터페이스 -> 운전, 정비 인터페이스
      • 사용자 클라이언트를 운전사 클라이언트, 정비사 클라이언트로 분리할 수 있음.
    • 분리하면 다른 인터페이스가 변해도 영향을 주지 않는다.
    • 인터페이스가 명확해지고, 대체 가능성이 높아진다.
  5. DIP - 의존 관계 역전 원칙
    • 추상화에 의존해야 한다. 구체화는 의존하지 않는다.
    • 구현 클래스에 의존하지말고 인터페이스에 의존해라
    • 결국 역할에 의존해라. 객체지향에서는 역할이 중요하지 그 역할을 누가하는 지는 중요하지 않다.

Spring

  • DI를 통해 OCP, DIP를 가능하게 지원
  • 클라이언트 코드의 변경 없이 기능 확장
  • 모든 설계에 역할구현을 분리하자
  • 이상적으로는 모든 설계에 인터페이스를 부여해야함
    • 구현기술을 바꾸더라도 변화하기가 쉬움
  • 실무적으로 고민
    • 추상화 하려면 비용이 들어감
    • 뭔가 문제가 생겼을떄도 인터페이스 -> 구현 클래스를 찾아서 들어가야함

IOC DI Container

  • IOC 제어의 역전
    • 기본적으로 클라이언트의 구현 객체가 스스로 생성하는 제어 하는 구조이다.
    • 하지만, AppConfig를 통해 생성하는 흐름을 다른 객체가 하도록 역전한다.
    • 클라이언트 조차도 AppConfig가 생성한다.
    • 프레임워크 vs 라이브러리
      • 내가 작성한 코드를 다른 곳에서 실행하면 프레임워크 ( 제어의 흐름이 내가 작성한 코드가 아니다.)
      • 라이브러니는 내가 작성하고 내가 사용한다. ( 제어의 흐름이 내가 작성한 코드이다.)
  • DI 의존관계 주입
    • 클라이언트는 인터페이스에 의존하고, 실제 어떤 구현 객체가 사용될지는 모른다.
    • 정적인 클래스 의존관계와 런타임에 결정되는 동적인 객체를 분리해야함
    • 정적인 클래스 의존관계
      • 코드상에 나오는 import등을 통해 바로 분석가능한 의존관계 MemberService - Member
    • 동적인 클래스 의존관계
      • 코드 상에서 어떤 의존관계가 생성 될지 알 수 없다.
      • 런타임 시에 외부에서 실제 구현 객체를 클라이언트에 주입하는 것이 의존관계 주입이라고 한다.
      • 의존관계 중비을 사용하면 정적인 클래스 의존관계를 변경하지 않고, 동적인 객체 인스턴스 의존관계를 변경한다.
      • OrderSerivceImpl - DiscountPolicy (FixDiscountPolicy, RateDiscountPolicy)
  • IOC 컨테이너, DI 컨테이너
    • AppConfig처럼 객체를 생성하고 관리하면서 의존관계를 연결해 주는 것
    • 의존관계 주입에 초점을 맞추어 최근에는 DI 컨테이너라고 한다.

ApplicationContext

  • new AnnotationConfigApplicationContext(AppConfig.class) 를 호출하면 appconfig @bean이라고 등록된 클래스 정보를 통해 빈을 등록한다
  • 무조건 빈은 항상 다른 이름으로 부여해야한다
  • 객체를 생성하고 빈들끼리의 의존관계를 설정한다.
  • 단순히 자바 코드를 호출하는게 아니고 싱글톤 레지스트리를 통해 조금 다르게 동작한다.

빈 조회 기본

  • getBean()

빈팩토리와 어플리케이션 컨텍스트

  • 빈팩토리 -> 어플리케이션 컨텍스트
  • 빈팩토리
    • 스프링 컨테이너 최상위 인터페이스
    • 스프링 빈을 관리하고 조회한다.
    • getBean을 제공
  • 어플리케이션 컨텍스트
    • 빈 조회 기능 이외에 수 많은 부가기능이 필요하다.
    • 메시지소스를 활용한 국제화 기능 ( 한국에서 들어오면 한국어로)
    • 환경변수 (local, dev, prod) 구분해서 처리
    • 애플리케이션 이벤트 - 이벤트를 발생하고 구독하는 모델을 편리하게 지원
    • 편리한 리소스 조회 - 파일 클래스패스 외부 등에서 리소스를 편리하게 조회

BeanDefinition

  • 설정 형식을 지원하는 추상화

  • xml을 읽어서 BeanDefinition을 만들기

  • 자바코드를 읽어서 BeanDefinition을 만들기

  • 빈 설정 메타정보이다.

    • @Bean 로 각각 메타정보를 생성한다.
  • 스프링 컨테이너는 메타정보를 바탕으로 빈을 생성한다.

  • 자바코드 기반은 팩토리 메서드를 통해 빈을 등록함.

Singleton

웹애플리케이션과 싱글톤

  • 스프링은 엔터프라이즈로 쓰기 위해 만들어짐
  • 웹어플리케이션은 동시 요청이 많음.
  • 해당 객체가 한개만 생성되고 공유 되는것 -> 싱글톤

싱글톤 패턴

  • 클래스의 인스턴스가 한개만 생성되게 보장하는 패턴
  • 객체 인스턴스를 2개 이상 생성하지 못하도록 막아야 한다.
    • 생성자를 private로 사용한다.
    • 외부에서 new로 생성되는 것을 막는다.
  • 여러가지 방법이 많음.
  • 문제점
    • 구현 코드가 많이 들어감
    • dip 위반 구체 클래스를 의존함
    • ocp 위반 가능성이 높음
    • 테스트가 어려움
    • 내부 속성 변경하거나 초기화 하기 어려움
    • 자식 클래스를 만들기 어렵다
    • 결론적으로 유연성이 떨어진다.

싱글톤 컨테이너

  • 스프링 컨테이너는 싱글톤패턴의 문제점을 해결하면서 객체를 싱글톤으로 관리한다.
  • 싱글톤 객체를 생성하고 관리하는 기능을 싱글톤 레지스트리라고 한다.
  • 스프링의 빈은 기본적으로 싱글톤이지만 새로운 객체를 생성하는 반환하는 기능도 제공한다.

싱글톤 방식의 주의점

  • 싱글톤 방식은 여러 클라이언트가 하나의 같은 객체 인스턴스를 공유하기 떄문에 무상태로 설계해아한다.
    • 특정 클라이언트에 의존적인 필드가 있으면 안됨
    • 값을 변경할 수 있는 필드가 있으면 안된다.
    • 가급적 readonly
  • 공유필드를 진짜 조심해야합니다.

@Configuration - 바이트 코드 조작

  • 스프링은 빈이 싱글톤이 되도록 보장해야한다.
  • AppConfig 를 상속해서 바이트코드조작을 통해 만들어진다.
  • @Configuration을 사용하지 않고 @Bean만 적용하면
    • 싱글톤이 보장되지 않는다.
    • 자바 코드를 그대로 실행한다.
    • 스프링이 관리하지 않는 클래스가 생성된다.

컴포넌트 스캔

  • 스프링 빈을 나열하지 않고 수십 수백개가 되면 누락하는 문제가 생김
  • 컴포넌트 스캔을 사용하면 @Configuration이 붙은 클래스도 등록됨
  • TestConfig등도 등록됨
  • 실무에서 보통은 제외하지 않지만, 테스트 하기 위해서 excludeFilter를 등록함
  • @Component 어노테이션이 붙은 클래스들을 스캔한다.
  • 빈의 이름은 클래스명을 사용하되 앞글자만 소문자로 사용한다.
  • @Autowired의 기본 조회 전략은 타입으로 조회한다.

탐색 위치와 기본 스캔 대상

  • basePackages옵션 을 사용해 탐색 시작 위치를 등록할 수있다.
  • basePackageClasses = {AutoAppConfig.class}로 클래스 위치 부터 등록한다
  • 지정하지 않으면 @ComponentScan이 붙은 설정 정보 클래스의 패키지가 시작 위치가 된다.
  • 관례상 실무에서는 설정 정보 클래스의 위치를 프로젝트 최상단에 둔다
  • @SpringBootApplication 어노테이션을 통해 이 안에 @ComponentScan이 들어 있기 때문에 여기서부터 검색한다.
  • 스캔 기본 대상
    • @Component
    • @Controller - 스프링 mvc 컨트롤러로 인식
    • @Service - 다른 추가기능은 없고 개발자들이 보고 비즈니스 로직의 위치를 파악한다.
    • @Repository - 스프링 데이터 접근계층으로 인식, 데이터 계층의 예외를 스프링 추상화된 예외로 변환해준다.
    • @Configuration
    • 자바는 어노테이션 상속관계가 없고 스프링에서 지원하는 기능이다.

필터

  • 굳이 필터 쓰지 말자
  • 스프링부트가 기존적으로 @ComponentScan을 사용한다.

중복등록과 충돌

  • 자동 빈 등록 vs 자동 빈 등록
    • 컴포넌트 스캔에 의해 자동으로 스프링 빈이 등록되는데, 그 이름이 같을 경우 예외발생
  • 수동 빈 등록 vs 자동 빈 등록
    • 수동 빈 등록이 우선권을 가짐 -> 수동 빈이 자동빈을 오버라이딩 해버림
    • 잡기 어려운 버그가 많이 발생한다
    • 최근 스프링부트는 예외 발생한다!!!!!!!!!!!!!

의존관계 자동 주입

  • 의존관계 주입의 4가지 방법
    • 생성자 주입
      • 딱 1번만 주입되는 것이 보장된다.
      • 불변, 필수 의존 관계에서 사용한다.
      • 개발할때 불변은 엄청 중요하다. 변화하는 걸 다 따라가기에는 너무 복잡하다. -> 복잡하면 오류가 난다
      • final 키워드를 통해 생성자는 무조건 값을 처리할 수 있다.
      • 생성자가 딱 하나만 있다면 @Autowired를 생략 가능하다.
    • 수정자 주입
      • 필드의 값을 수정할때 관례상 setter로 사용한다.
      • @Autowired + setter를 통해 주입이 가능하다.
      • 선택, 변경 가능한 의존 관계에서 사용한다.
    • 필드 주입
      • 코드는 간결함
      • 외부에서 주입� 불가능함 -> 테스트 만들기가 너무 힘듬
      • di 프레임워크가 없으면 사용 불가능
      • 테스트 코드에서는 사용가능하다
      • @Configuration -> 사용 할 수 있다.(수동 등록 시)
    • 일반 메서드 주입
      • 한번에 여러 필드 주입 가능
      • 쓰지 않는다.

@Autowired 의 옵션

  • @Autowired(require=false) : 자동 주입할 대상이 없으면 수정자 메서드 자체 호출 안됨
  • @Nullable : null로 넣어줌
  • Optional<>
        //Member는 스프링 빈이아님.
        @Autowired(required = false)
        public void setNoBean1(Member noBean1) {
            System.out.println("noBean1 = " + noBean1);
        }
        @Autowired
        public void setNoBean2(@Nullable Member noBean2) {
            System.out.println("noBean2 = " + noBean2);
        }
        @Autowired
        public void setNoBean3(Optional<Member> noBean3) {
            System.out.println("noBean3 = " + noBean3);
        }

생성자 주입을 사용하자

  • 불변
    • 제약이 있기 때문에 어디서 수정하는지 생각할 필요가 없다.
    • 대부분 의존관계는 애플리케이션 종료까지 변하면 안된다.
    • 누군가 수정 할 일이 없다.
  • 누락
    • 프레임워크 없이 순수한 자바코드를 단위 테스트 하는 경우에 누락할 일이 없다.
    • 강제하는 게 없으면 찾아봐야하는 추가 비용이 발생한다.
  • final 키워드를 강제로 할 수 있다.
    • 초기화 할때 생성자에서 컴파일러가 체크 해준다.
    • 컴파일 오류가 제일 좋다..
  • 생성자 주입을 제외하면 final키워드를 사용할 수 없다.
  • 생성자가 딱 하나만 있다면 @Autowired가 생략 가능하다.
  • 생성자는 빈생성과 의존관계 주입에서 일어난다

수정자 주입

  • 자바 빈 규약에서 정해진 setXxx 메소드를 사용한다.
  • 메소드 위에 @Autowired 적용
  • 빈이 생성되고 의존관계 주입단계에서 주입을 해준다.
  • 선택, 변경 가능성이 있는 의존관계에서 사용한다.
  • 주입할 대상이 선택가능하면 @Autowired(required = false)

필드 주입

  • 필드 위에 @Autowired를 넣어서 사용한다.
  • DI 컨테이너 없이 사용 불가능
  • 외부에서 수정할 방법이 없음

Lombok

  • @RequiredArgsConstructor -> final이 붙은 필드의 생성자를 만들어줌

조회 빈이 2개 이상일 때

  • @Autowired는 타입으로 조회한다.
  • 해결 방법
    • @Autowired 필드명 매치 : 타입 매칭 -> 두개 이상이면 -> 필드명, 생성자 파라미터명으로 매칭을 시도함
    • @Quailifier : 추가 구분자를 붙여주는 방법 - 빈 이름을 변경하는 것이 아님
      • 빈으로 등록될 클래스에 @Quailifier("name1")
      • di 받는 곳 파라미터 앞에 (, @Quailifier("name1") name1 )로 동작가능
      • 못찾으면 name1이라는 이름으로 한번더 찾아본다.
      • @Qualifier를 찾을때만 쓰자!!!!!!!!!!!!!
    • @Primary -> 우선순위를 지정해버린다. 여러개가 매칭되면 @Primary 등록된 빈을 가져온다.
  • @Primary 와 @Quailifier가 중복 날때는 @Primary가 우선순위가 높다.

조회한 빈이 모두 필요할 때

  • 클라이언트가 할인의 종류를 선택할 수 있을떄
  • 전략 패턴 구현

자동 등록 수동등록 올바른 실무 운영 기준

  • 자동 등록을 사용하자 - @Service, @Component, @Repository, @Controller

수동 빈을 사용할때?

  • 비즈니스 로직과 기술 지원로직(aop)로 나눌 수 있다
  • 비즈니스 로직 : 업무와 관련된 로직
    • 코드가 많음
    • 기본적인 패턴이 있음 controller / service / repository
    • 자동기능 사용하자
    • but 다형성을 적극 활용할때는 수동으로 등록해주는 것이 좋다.
  • 기술지원 로직 : 데이터베이스 연결 , 공통 로그 처리
    • 수가 적음 대신 광범위하게 영향을 미침
    • 기술 지원 로직은 파악하기가 힘듬
    • 수동 빈등록을 통해 밖으로 보이는게 좋음

빈 생명주기 콜백

  • 스프링 빈의 간단한 라이프사이클
    • 객체생성 -> 의존관계 주입( 예외 생성자 주입)
  • 초기화 : 객체가 생성되고 필요한 데이터를 사용할 수 있는 준비가 완료되어야함
    • 의존관계 주입이 완료되어야함
    • 스프링이 의존관계 주입이 완료되면 스프링 빈에게 콜백 메서드를 통해서 초기화 시점을 알려주는 기능을 제공
    • 스프링 컨테이너가 종료되기 직전에 소멸 콜백을 준다.
  • 스프링 빈의 이벤트 라이프사이클
    • 스프링컨테이너 생성 -> 스프링 빈 생성(생성자주입) -> 의존관계 주입(세터, 필드) -> 초기화 콜백(초기화 완료 콜백) -> 사용 -> 소멸전 콜백 -> 스프링 종료
  • 객체의 생성과 초기화는 분리해야한다
    • 생성자는 필수 정보를 받고 메모리를 할당함 -> 값을 세팅하는 정도의 레벨만 하는게 좋다.
    • 초기화는 값들을 활용해서 외부 커넥션 연결 등 무거운 동작을 수행한다.

생명주기 콜백 방법

  • 인터페이스 InitializingBean, DisposableBean
    • public class NetworkClient implements InitializingBean, DisposableBean
    • 인터페이스의 단점
      • 스프링에 의존적임 -> 모든 코드가 스프링에 의존해야함
      • 초기화 소멸 메서드 이름을 변경할 수 없음
      • 내가 코드를 고칠 수 없는 외부 라이브러리에 적용할 수 없다.
      • 요새는 거의 사용하지 않는 방법이다.
  • 메서드 호출방법
    • @Bean(initMethod = "init", destroyMethod = "close")
    • 메서드 이름을 자유롭게 줄수있음
    • 스프링에 의존성이 없음
    • 외부 라이브러리에도 적용할 수 있음
    • 종료 메서드 추론 (inferred)
      • destroyMethod에 값을 주지않으면
      • close, shutdown의 메소드를 자동으로 추론해서 호출해줌
      • 쓰기 싫을경우 destroyMethod = ""로 적어주면된다.
  • 어노테이션
    • 지금은 이거 쓰면 된다
    • @PostConstruct / @PreDestroy
    • 메서드에다가 어노테이션에만 달아준다.
    • 자바 표준이다.
    • ComponentScan에 좋다아
    • 외부 라이브러리를 사용하지 못하기 때문에 외부라이브러리에 필요하다면 @Bean의 initMehtod, destroyBean을 쓰자

빈스코프

- 위의 빈 사이클은 기본적으로 싱글톤 스코프 이기 때문이다.
- 프로토타입
    - 프로토타입은 빈의 생성과 의존관계 주입까지만 관여하고 더는 관리하지 않는다.
- 웹 관련 스코프
    - request : 웹 요청이 들어오고 나갈때까지의 스코프
    - session : 웹 세션이 생성되고 종료될 때 까지 유지되는 스코프
    - application : 웹의 서블릿 컨텍스트와 같은 범위로 유지되는 스코프

프로토타입 스코프

- 싱글톤과 다르게 컨테이너에서 요청할때 항상 새로운 것을 반환한다.
- 빈을 생성하고 의존관계 주입 초기화까지만 처리한다!
- 프로토타입 빈을 관리할 책임은 프로토타입 빈을 받을 클라이언트에 있다.
- @PreDestroy 동작 안함
- 종료 메서드는 직접 호출해줘야 한다.

프로토타입 - 싱글톤빈과 함께 사용할때의 문제점

- 싱글톤 빈에서 프로토타입 빈을 사용하면 처음 주입받을때만 싱글톤빈에서 들어와서 싱글톤빈의 필드에 있기 때문에 계속 같은 프로토타입빈이 된다.
- 프로토타입의 의도는 사용할때마다 새로 생성되어야 한다고 생각함.

싱글톤 빈과 프로토타입빈을 사용할때 Provider로 문제 해결

- 의존관계 검색을 통해 해결
- Prototype을 위해 만들어진게 아니고 의존관계검색을 조금 더 쉽게 해주는 방법
- ObjectFactory, ObjectProvider
    - getObject() 동작은 같음
    - provider가 편의기능이 조금 더 추가됨
    - 스프링에 의존적
- JSR-330 Provider
    - 자바 표준 사용방법
    - gradle 에 라이브러리 추가해야함
    - get() 메서드 사용
    - 심플함
    - 별도의 라이브러리가 필요하다.
    - 스프링이 아니여도 사용할 수 있다.

프로토타입빈의 사용

- 매번 사용할 때 마다 의존관계 주입이 완료된 새로운 객체가 필요하면 사용하면 된다.
- 실무에서 크게 사용할 일이 없다.
- provider는 DL이 필요한 경우 언제든지 사용 할 수 있다.
- 실무에서 쓰게된다면 ObjectProvider를 사용하는 게 좋다아..

웹스코프

- 웹 환경에서만 동작한다.
- 웹 스코프는 프로토타입과 다르게 스프링이 해당 스코프의 종료시점까지 관리한다 -> 종료메서드가 호출된다.
- request : 웹 요청이 들어오고 나갈때까지의 스코프 http 요청 `하나하나` 별도의 인스턴스 생성
- session : 웹 세션이 생성되고 종료될 때 까지 유지되는 스코프
- application : 웹의 서블릿 컨텍스트와 같은 범위로 유지되는 스코프
- websocket : socket과 동일한 생명주기

프록시

- proxyMode를 사용
- MyLogger를 상속한 바이트 조작 클래스를 받음
- 의존 관계 주입이 가짜 프록시로 주입됨
- 프록시 객체를 사용하게 되어서 싱글톤 빈을 사용하듯이 편리하게 request scope를 사용할 수 있음
- 중요한 것은 진짜 객체 조회를 꼭 필요한 시점까지 지연한다는 점