반응형

인프런에서 백기선님의 무료 강의를 수강했다. 러닝 타임은 약 2시간 30분 정도이라서 부담스럽지 않게 들을 수 있었다.
강의명에 스프링 입문이라고 되어있지만, 그만큼 핵심적인 개념이기 때문에 별도의 무료 강의로 제공해주시는 거라고 생각된다.
Spring IoC, Bean, DI, Spring AOP, Proxy Pattern, Spring PSA 등 스프링이 따르고 있는 컨셉과 패턴들에 대해서 요약된 강의이다.

짧은 강의지만 완강 인증ㅎㅎ

예제로 배우는 스프링 입문 (개정판)
예제코드

 

강의를 들으며 내용을 정리하여 올려보겠다.
참고로 Spring Framework가 내부적으로 개발자 대신 구현해주고 있는 IoC, DI, DIP 등을 코드로 이해하기를 원한다면 아래 글을 추천한다.
Java로 알아보는 IoC, DIP, DI, IoC Container (상)

 

Java로 알아보는 IoC, DIP, DI, IoC Container (상)

객체지향설계에서 클래스는 반드시 느슨하게 결합되어야 한다. 느슨한 결합은 한 클래스의 변경이 다른 클래스들에게 영향을 미치지 않는 것을 의미한다. 그래서 어플리케이션을 지속가능하고

prohannah.tistory.com


프로젝트 설정

  • 프로젝트를 빌드하면 jar(java) 혹은 war(web) 파일이 생성되는데, 별도 설정을 하지 않을 경우 jar로 기본 생성됨

Spring IoC

Spring IoC

  • Spring IoC Container가 bean이라는 객체를 생성하여 의존성 관리. bean들간 서로 필요한 의존성을 IoC Container가 주입해줌

Spring IoC Container

  • BeanFactory가 실질적은 IoC Container고 ApplicationContext는 이를 상속받고 있어 거의 같은 일을 한다고 볼 수 있다. (+그 외 여러가지 클래스도 상속받고 있어서 수행하는 일들이 많음)
  • IoC Container를 사용하는 이유 중 하나는 간편하게 싱글톤 스콥의 객체 생성이 가능하다는 것
  • ApplicationContext(혹은 BeanFactory)라고 하는 IoC Container 코드는 실제로 볼 일이 별로 없다. 가장 핵심적인 클래스이긴 하지만 소스코드에서 직접 참고해서 사용하는 경우는 거의 없다.
MyClass myClass = applicationContext.getBean("myClass");

스프링 빈(Bean)

  • 빈을 등록하는 방법 2가지 1. Component Scanning, 2. xml 혹은 자바 설정파일을 통해 직접 빈으로 등록
  • Component Scanning (컴포넌트 스캐닝) : @Component, @Controller, @Service, @Configuration, @Repository 어노테이션이 붙은 클래스를 찾아서 해당 클래스의 인스턴스를 bean으로 등록함. 어노테이션 프로세스 중 IoC Container가 사용하는 여러가지 인터페이스들이 있는데, 그런 인터페이스들을 라이프사이클 콜백이라고 부름. 여러가지 라이프사이클 콜백 중에서 bean을 등록하는 일을 하는 에너테이션 프로세서(처리기)가 존재함. @SpringBootApplication 내에 @ComponentScan 이 어디부터 컴포넌트를 찾아보라고 알려줌. 컴포넌트 스캔 어노테이션이 붙은 위치에서 하위 패키지를 쭈욱 뒤짐
  • @Repository 는 조금 독특한 형태로 빈으로 등록되는데, 이건 Spring Data JPA에 의해 등록된다. 특정한 인터페이스를 상속하는 클래스(혹은 인터페이스)를 찾아서 내부를 구현해준 뒤 빈으로 등록해줌
  • 빈으로 직접 등록하는 방법 : xml or 자바설정파일을 통해 가능한데, 최근 추세는 자바 설정파일을 사용함(@Configuration + @Bean 어노테이션을 통해 빈 등록 가능)
  • Bean 직접 꺼내기 : @Autowired 어노테이션을 통해 IoC Container의 빈을 주입받을 수 있고, 혹은 ApplpicationContext 에서 꺼낼 수 있음

의존성 주입(Dependency Injection)

  • @Autowired 어노테이션을 통해 IoC Container로부터 Bean을 주입받을 수 있음
  • 주입 대상은 필드, setter 메서드, 생성자
  • Spring 4.3부터 어떤 클래스에 생성자가 하나고, 그 생성자에서 주입받는 레퍼런스 변수들이 모두 bean으로 등록되어있다면, 그 생성자에 @Autowired 을 붙이지 않아도 bean을 자동으로 주입하는 기능 추가됨 (그래서 spring 4.3 이상부터 생성자에 어노테이션 생략 가능)
  • IoC Container에 존재하지 않는, 즉 Bean이 아닌 레퍼런스를 주입하려고 하면 오류 발생
  • Spring에서 권장하는 방법은 생성자 주입 방법이나, 용도에 맞게 생성자 혹은 Setter 방법을 선택하길 권장함. 생성자 주입은 필수적으로 필요한 레퍼런스 주입을 강제할 수 있고, Setter 주입은 옵셔널한 레퍼런스 주입 혹은 두 Bean이 서로를 참조하는 경우 세터나 필드 인젝션을 사용할 수 있다 (하지만 상호참조 의존성이 발생되지 않도록 설계하는 것이 좋다)

Spring AOP

Spring AOP

  • @Transactional 은 Spring AOP 기반으로 만들어진 어노테이션임
  • AOP : 로깅, 성능체크 등 여러 군데 흩뿌려진 반복적인 코드를 한 곳에 모아서 응집력있게 관리할 수 있어 유지보수에 효울적이고, 좀 더 비즈니스 로직에 집중할 수 있으며, 변화 수용에 용이함
  • AOP 최초 구현자(?)는 아드리안 콜리어라는 사람
  • AOP 구현 방법은 방식에 따라 세 가지가 있음. 1. 컴파일, 2. 바이트코드 조작, 3. 프록시 패턴
  • 컴파일 : A.java → (AOP) → A.class -> 메모리 컴파일 시점에 원하는 코드가 들어간 것 처럼 컴파일 해주는 기능
  • 바이트코드 : A.java -> A.class -> (AOP) -> 메모리 클래스로더가 A.class를 읽어와서 메모리에 올릴 때 조작
  • 프록시 패턴(Spring AOP가 사용하는 방법) : 디자인 패턴 중 하나로 AOP와 같은 효과를 냄
  • 어떻게 기존 코드를 건들이지 않고, 오리진 객체를 다른 객체(코드)로 바꿀 수 있는가!
  • https://refactoring.guru/design-patterns/proxy

Proxy Pattern

  • [예제코드] 클라이언트 코드(Store)는 변경되지 않은 상태로 성능 측정이 가능해짐
  • 이게 AOP를 프록시 패턴으로 구현하는 방법이고, 이런 일들이 Spring AOP에서는 대부분 자동으로 이루어지고 있음
  • 위 코드를 예를 들면, Cash가 빈으로 등록되는 대신 Spring AOP가 Cash에 대한 프록시 객체를 생성해서 그 객체를 IoC에 빈으로 등록하는 것임. 그래서 클라이언트 코드는 본인은 인지하지 못하지만 Cash 대신 Cash의 프록시 객체를 사용하는 것
  • 예를 들어 @Transactional 어노테이션이 붙어있으면 해당 어노테이션을 사용하는 클래스의 타입으로 프록시 객체를 생성하여 빈으로 등록함

Spring AOP 실습

  • [예제코드] @LogExecuteTime 어노테이션 생성
  • 어노테이션만 생성하면 주석과 다를바가 없음. 실제로 해당 어노테이션을 읽어들여 처리하는 Aspect를 생성해야함
  • [예제코드] LogAspect 생성 중
  • Around, joinPoint, Advise 등 용어 정리
  • 아까는 프록시 패턴을 이용해 직접 구현해본거고, 이거는 Spring AOP 내부메커니즘을 통해 만든것임. 둘다 프록시 패턴을 사용했기 ㅐ문에 동작원리는 동일함
  • 이곳저곳 흩어져서 반복되는 코드가 있으면 Aspect 적용을 고려해도 좋을 것 같다.
  • 참고로 Aspect 적용 방법에 따라 타겟에다 어노테이션을 붙이지 않아도 되는데, 명시적으로 어노테이션 생성 후 사용하면 코드 파악에 더 좋은 것 같다. 왜냐면 인텔리제이 같은 툴들이 어떤 Aspect가 사용되고, 어디에 사용되었는지 보여주기 떄문임

Spring PSA

PSA(포터블 뜻 풀이)

  • 스프링 트라이앵글 : IoC, AOP, PAS
  • 우리가 서블릿 어플리케이션을 만들고 있음에도 서블릿을 사용하지 않는 이유가 바로 PSA 덕분
  • HTTP 요청을 @GetMapping, @RequestMapping 등을 통해서 받아들이는데, 이 아래에서는 서블릿 기반의 코드가 동작하는 것임. 이런 추상화 객체를 사용해서 좀 더 편하게 개발할 수 있음
  • 서비스 추상화를 사용하는 이유 : https://en.wikipedia.org/wiki/Service_abstraction

Spring Web MVC와 webFlux 추상화 계층

  • 스프링에서는 다양한 service abstraction을 제공해주고 있는데, 우리가 먼저 살펴볼 것은 Spring Web MVC 추상화 계층임
  • @Controller 는 HTTP 요청을 매핑할 수 있는 컨트롤러 역할을 수행하는 클래스가 됨. 스프링 웹 MVC를 통해 서블릿을 간편하게 개발할 수 있는 데는 뒷단에 스프링이 제공해주는 여러가지 기능들이 숨어져있기 떄문임
  • Spring Web MVC가 추상화 계층인 이유? 서블릿을 로우 레벨로 사용하지 않아도 되고 매핑도 간편하며 편의성을 제공함. 뒷단 동작을 여러 가지 기술로 대체할 수 있음(기술 확장성)
  • 서비스 추상화를 통해 우리의 코드를 거의 그대로 둔 상태로 기술 스택을 대체할 수 있음 ⇒ 예를 들어, Spring 5에 들어온 리액티브 웹 플럭스라는 새로운 스프링 모델을 사용하면 기존 웹 MVC와 거의 흡사한 방식으로 코딩하지만 뒷단 기술이 완전히 대체됨. 웹 MVC는 서블릿처럼 하나의 요청이 들어올 때 마다 하나의 스레드를 사용하는 구조지만, 플럭스는 CPU 개수만큼 최소한의 스레드를 유지하면서 앞단의 요청을 받아 가용성을 높이는 방식을 채택할 수 있음(뒷단도 스레드풀을 이용하긴 함)
  • 위에서 언급한 기술스택으로 바꾸면 네티 기반으로 실행 가능 (MVC ↔ webFlux 코드는 완벽호환 X)
  • 네티 공식문서에 코딩 가이드가 있으나, 우리는 Spring Service Abstraction을 통해 기존 MVC와 유사하게 코딩하면서(기존과 동일하게 @Controller, @REquestMapping 등의 인터페이스 사용) 톰캣 대신 네티를 사용하는 것임!
  • 추상화 계층을 통해 우리의 코드는 유지하면서 뒷단을 서블릿이나 혹은 리액티브를 선택하여 사용할수 있음
  • 코드를 거의 변경하지 않고 밑단에 있는 서버를 톰캣, 제티, 네티, 언더토우 등으로 변경 가능
  • 이것도 포터블 서비스 앱스트렉션으로 볼 수 있음

Spring의 다른 PSA들

  • 트랜잭션 : All or Nothing
  • 이것도 PSA 중 하나로 트랜잭션 매니저의 구현체를 다르게 바꿔낄 수 있음! jpa or datasource or hibernate 등
  • 스프링 캐시도 마찬가지! jCache, EhCache, ConcurrentMap 등

기타

  • 인텔리제이 클래스명 좌측에 녹색콩+C 표시 되어있으면 Bean으로 등록된 클래스라는 뜻임! (나만 몰랐음?!)
반응형