검색결과 리스트
글
스프링 핵심 원리 - 고급편 7
인프런 강의 66일차.
- 스프링 핵심 원리 - 고급편 (김영한 강사님)
- 반드시 한번은 정복해야할 쉽지 않은 내용들
- 크게 3가지 고급 개념을 학습
1. 스프링 핵심 디자인 패턴
> 템플릿 메소드 패턴
> 전략 패턴
> 템플릿 콜백 패턴
> 프록시 패턴
> 데코레이터 패턴
2. 동시성 문제와 쓰레드 로컬
> 웹 애플리케이션
> 멀티쓰레드
> 동시성 문제
3. 스프링 AOP
> 개념, 용어정리
> 프록시 - JDK 동적 프록시, CGLIB
> 동작 원리
> 실전 예제
> 실무 주의 사항
- 기타
> 스프링 컨테이너의 확장 포인트 - 빈 후처리기
> 스프링 애플리케이션을 개발하는 다양한 실무 팁
- 타입 컨버터, 파일 업로드, 활용, 쿠키, 세션, 필터, 인터셉터, 예외 처리, 타임리프, 메시지, 국제화, 검증 등등
4. 프록시 패턴과 데코레이터 패턴
4.1. 프로젝트 생성
- https://start.spring.io
- 프로젝트 선택 Project : Gradle Project
- Language : Java
- Spring Boot : 2.7.0
- Group : hello
- Artifact : proxy
- Name : proxy
- Package name : hello.proxy
- Packaging : Jar
- Java : 11
- Dependencies : Spring Web, Lombok
* gradle.build 실행 후 아래 오류 발생 시 gradle-wrapper.properties에서 gradle 버전을 6.9 이하로 변경해서 다운로드하자. (gradle-6.9-all.zip)
> Unable to find method 'org.gradle.api.artifacts.result.ComponentSelectionReason.getDescription()Ljava/lang/String;'. Possible causes for this unexpected error include: Gradle's dependency cache may be corrupt (this sometimes occurs after a network connection timeout.) Re-download dependencies and sync project (requires network)
4.2. 예제 프로젝트 만들기 V1
- 예제는 크게 3가지 상황으로 만든다.
> v1 - 인터페이스와 구현 클래스 - 스프링 빈으로 수동 등록
> v2 - 인터페이스 없는 구체 클래스 - 스프링 빈으로 수동 등록
> v3 - 컴포넌트 스캔으로 스프링 빈 자동 등록
- 실무에서는 스프링 빈으로 등록할 클래스는 인터페이스가 있는 경우도 있고 없는 경우도 있다.
- 그리고 스프링 빈을 수동으로 직접 등록하는 경우도 있고, 컴포넌트 스캔으로 자동으로 등록하는 경우도 있다.
- 이런 다양한 케이스에 프록시를 어떻게 적용하는지 알아보기 위해 다양한 예제를 준비해보자.
- v1 : 인터페이스와 구현 클래스 - 스프링 빈으로 수동 등록
package hello.proxy.app.v1;
public interface OrderRepositoryV1 {
void save(String itemId);
}
package hello.proxy.app.v1;
public class OrderRepositoryV1Impl implements OrderRepositoryV1 {
@Override
public void save(String itemId) {
//저장 로직
if( itemId.equals("ex")){
throw new IllegalStateException("예외 발생!");
}
sleep(1000);
}
private void sleep(int millis) {
try{
Thread.sleep(millis);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
- hello.proxy.app.v1.OrderRepositoryV1.java
- hello.proxy.app.v1.OrderRepositoryV1Impl.java
package hello.proxy.app.v1;
public interface OrderServiceV1 {
void orderItem(String itemId);
}
package hello.proxy.app.v1;
public class OrderServiceV1Impl implements OrderServiceV1 {
private final OrderRepositoryV1 orderRepository;
public OrderServiceV1Impl(OrderRepositoryV1 orderRepository) {
this.orderRepository = orderRepository;
}
@Override
public void orderItem(String itemId) {
}
}
- hello.proxy.app.v1.OrderServiceV1.java
- hello.proxy.app.v1.OrderServiceV1Impl.java
package hello.proxy.app.v1;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@RequestMapping //스프링은 @RequestMapping 혹은 @Controller가 있어야 스프링 컨트롤러로 인식
@ResponseBody
public interface OrderControllerV1 {
@GetMapping("/v1/request")
String request(@RequestParam("itemId") String itemId);
@GetMapping("/v1/no-log")
String noLog();
}
- hello.proxy.app.v1.OrderControllerV1.java
- @RequestMapping : 스프링MVC는 타입에 @Controller 또는 @RequestMapping 애노테이션이 있어야 스프링 컨트롤러로 인식한다. 그리고 스프링 컨트롤러로 인식해야, HTTP URL이 매핑되고 동작한다. 이 애노테이션은 인터페이스에 사용해도 된다.
- @ResponseBody : HTTP 메시지 컨버터를 사용해서 응답한다. 이 애노테이션은 인터페이스에 사용해도 된다.
- @RequestParam("itemId") String itemId : 인터페이스에는 @RequestParam("itemId") 의 값을 생략하면 itemId 단어를 컴파일 이후 자바 버전에 따라 인식하지 못할 수 있다.
- 인터페이스에서는 꼭 넣어주자. 클래스에는 생략해도 대부분 잘 지원된다.
- 코드를 보면 request() , noLog() 두 가지 메서드가 있다.
- request() 는 LogTrace 를 적용할 대상이고, noLog() 는 단순히 LogTrace 를 적용하지 않을 대상이다.
package hello.proxy.app.v1;
public class OrderControllerV1Impl implements OrderControllerV1 {
private final OrderServiceV1 orderService;
public OrderControllerV1Impl(OrderServiceV1 orderService) {
this.orderService = orderService;
}
@Override
public String request(String itemId) {
orderService.orderItem(itemId);
return "ok";
}
@Override
public String noLog() {
return "ok";
}
}
- hello.proxy.app.v1.OrderControllerV1Impl.java
- 컨트롤러 구현체이다. OrderControllerV1 인터페이스에 스프링MVC 관련 애노테이션이 정의되어 있다.
package hello.proxy.config;
import hello.proxy.app.v1.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppV1Config {
@Bean
public OrderControllerV1 orderControllerV1(){
return new OrderControllerV1Impl(orderServiceV1());
}
@Bean
public OrderServiceV1 orderServiceV1() {
return new OrderServiceV1Impl(orderRepositoryV1());
}
@Bean
public OrderRepositoryV1 orderRepositoryV1() {
return new OrderRepositoryV1Impl();
}
}
- hello.proxy.config.AppV1Config.java
- 스프링 Bean 수동 등록 config
package hello.proxy;
import hello.proxy.config.AppV1Config;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;
@Import(AppV1Config.class)
@SpringBootApplication(scanBasePackages = "hello.proxy.app") //주의 (설정 패키지 하위에서 컴포넌트 스캔 후 Bean을 자동등록한다)
public class ProxyApplication {
public static void main(String[] args) {
SpringApplication.run(ProxyApplication.class, args);
}
}
- hello.proxy.ProxyApplication.java
- @Import(AppV1Config.class) : 클래스를 스프링 빈으로 등록한다.
- 여기서는 AppV1Config.class 를 스프링 빈으로 등록한다.
- 일반적으로 @Configuration 같은 설정 파일을 등록할 때 사용하지만, 스프링 빈을 등록할 때도 사용할 수 있다.
- @SpringBootApplication(scanBasePackages = "hello.proxy.app") : @ComponentScan 의 기능과 같다.
- 컴포넌트 스캔을 시작할 위치를 지정한다. 이 값을 설정하면 해당 패키지와 그 하위 패키지를 컴포넌트 스캔한다.
- 이 값을 사용하지 않으면 ProxyApplication 이 있는 패키지와 그 하위 패키지를 스캔한다.
* 주의
> @Configuration 은 내부에 @Component 애노테이션을 포함하고 있어서 컴포넌트 스캔의 대상이 된다.
> 따라서 컴포넌트 스캔에 의해 hello.proxy.config 위치의 설정 파일들이 스프링 빈으로 자동 등록 되지 않도록 컴포넌스 스캔의 시작 위치를 scanBasePackages=hello.proxy.app 로 설정해야 수동 등록이 가능하다.
4.2. 예제 프로젝트 만들기 V2
- v2 : 인터페이스 없는 구현 클래스를 스프링 빈으로 수동 등록
package hello.proxy.app.v2;
public class OrderRepositoryV2 {
public void save(String itemId) {
//저장 로직
if( itemId.equals("ex")){
throw new IllegalStateException("예외 발생!");
}
sleep(1000);
}
private void sleep(int millis) {
try{
Thread.sleep(millis);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
- hello.proxy.app.v2.OrderRepositoryV2.java
package hello.proxy.app.v2;
public class OrderServiceV2 {
private final OrderRepositoryV2 orderRepository;
public OrderServiceV2(OrderRepositoryV2 orderRepository) {
this.orderRepository = orderRepository;
}
public void orderItem(String itemId) {
}
}
- hello.proxy.app.v2.OrderServiceV2.java
package hello.proxy.app.v2;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Slf4j
@RequestMapping
@ResponseBody
public class OrderControllerV2 {
private final OrderServiceV2 orderService;
public OrderControllerV2(OrderServiceV2 orderService) {
this.orderService = orderService;
}
@GetMapping("/v2/request")
public String request(String itemId) {
orderService.orderItem(itemId);
return "ok";
}
@GetMapping("/v2/no-log")
public String noLog() {
return "ok";
}
}
- hello.proxy.app.v2.OrderControllerV2.java
- @Controller 를 사용하지 않고, @RequestMapping 애노테이션을 사용했다. 그 이유는 @Controller 를 사용하면 자동 컴포넌트 스캔의 대상이 되기 때문이다. 여기서는 컴포넌트 스캔을 통한 자동 빈 등록이 아니라 수동 빈 등록을 하는 것이 목표다. 따라서 컴포넌트 스캔과 관계 없는 @RequestMapping 를 타입에 사용했다.
package hello.proxy.config;
import hello.proxy.app.v2.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppV2Config {
@Bean
public OrderControllerV2 orderControllerV2(){
return new OrderControllerV2(orderServiceV2());
}
@Bean
public OrderServiceV2 orderServiceV2() {
return new OrderServiceV2(orderRepositoryV2());
}
@Bean
public OrderRepositoryV2 orderRepositoryV2() {
return new OrderRepositoryV2();
}
}
- hello.proxy.config.AppV2Config.java
- App별로 Config를 가져다 사용하도록 변경
- 기존: @Import(AppV1Config.class)
- 변경: @Import({AppV1Config.class, AppV2Config.class})
- @Import 안에 배열로 등록하고 싶은 설정파일을 다양하게 추가할 수 있다.
'Spring 정리' 카테고리의 다른 글
스프링 핵심 원리 - 고급편 9 (0) | 2023.05.19 |
---|---|
스프링 핵심 원리 - 고급편 8 (0) | 2023.05.02 |
스프링 핵심 원리 - 고급편 6 (0) | 2023.01.25 |
스프링 핵심 원리 - 고급편 5 (0) | 2023.01.25 |
스프링 핵심 원리 - 고급편 4 (0) | 2023.01.16 |