@ConfigurationProperties

스프링으로 개발을 하다 보면 properties 나 yml 파일에 설정한 값을 불러와야할 때가 있다.

대표적으로 외부에 노출되서는 안되는 SecretKey 값 같은 값이 있다.

AWS IAM Key 같은 값은 외부에 노출 됐다가 채굴당해서 요금폭탄 나오는 경우도 많으니 특히 조심해야한다.

@ConfigurationProperties 를 통해 properties 나 yml 파일에서 값을 불러올 수 있다.

@ConfigurationProperties 로 값 바인딩 하는법 ]

  1.  setter 로 바인딩
  2.  생성자로 바인딩

기본 설정

이 기능을 쓰기 위해 아래처럼 의존성을 추가해준다.

build.gradle

annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'

 

아래의 application.yml 파일의 데이터 값을 AppConfig 로 가져와보도록 한다.

application.yml

midcon:
  jwt-key: "pjx7jVXbdaeOmw0ZO1SotIHLVApe8FZ+LmGCuMKa8T8="

AppConfig

public class AppConfig {

    private String jwtKey;
}

DunpleApplication

스프링을 실행하고 컨테이너가 시작될 때 AppConfig 를 초기화하고 빈으로 생성하도록 등록을 해주어야 한다.

이를 위해서는 @EnableConfigurationProperties를 등록하고 해당 클래스를 추가해주어야 한다.
일반적으로 SpringBoot의 메인 클래스에 @EnableConfigurationProperties 어노테이션을 다음과 같이 추가해준다.

@EnableConfigurationProperties(AppConfig.class)
@SpringBootApplication
public class DunpleApplication {

   public static void main(String[] args) {
      SpringApplication.run(DunpleApplication.class, args);
   }
}

1. setter 로 바인딩

우선 setter 로 바인딩 하는법을 알아보자.

가져오려는 값의 prefix 또한 설정할 수 있다. 여기서는 "midcon" 으로 설정하겠다.

@ConfigurationProperties는 기본적으로 자바빈 프로퍼티 방식으로 동작하기 때문에 Setter가 반드시 필요하다.

@Setter
@ConfigurationProperties(prefix = "midcon")
public class AppConfig {

    private String jwtKey;
}

 

실행해보면 값이 잘 바인딩 된걸 확인할 수 있다.


2. 생성자로 바인딩

하지만 Setter 를 열어두는건 그다지 좋은 방법이 아니다. 

스프링 컨테이너에 의해 싱글톤으로 관리되는 객체에 변경 가능성이 열려있기 때문이다. 

그러므로 해당 클래스의 변수들을 final로 선언하고 생성자로 바인딩하여 불변성을 보장하는 것이 좋다.

변경 가능성을 닫기 위해 해당 변수를 final로 선언하고 생성자를 추가한다.

그리고 생성자를 이용해 yml 파일의 값을 바인딩하도록 @ConstructorBinding 어노테이션을 추가해준다.

(참고로 이 글은 스프링부트 2.7 점대 버전 기준임. 3.0 이상부터는 @ConstructorBinding 불필요 해당 글 참고)

@RequiredArgsConstructor
@ConstructorBinding
@ConfigurationProperties(prefix = "midcon")
public class AppConfig {

    private final String jwtKey;
}

 

실행해보면 값이 잘 바인딩 된걸 확인할 수 있다.

 

 

관련글)

JWT 를 이용한 인증

스프링부트 3.0 이상의 경우

 

 

 

참고자료)

 

[SpringBoot] final 변수를 갖는 클래스에 프로퍼티(Properties) 설정 값 불러오기, 생성자 바인딩(Construct

Spring 프레임워크로 개발을 하다 보면 프로퍼티(Properties)에 저장된 특정한 설정 값들을 불러와야 하는 경우가 있다. 많은 글 들에서 프로퍼티(Properties)를 불러오는 내용들을 설명하고 있는데, 이

mangkyu.tistory.com

 

'백엔드 > Spring' 카테고리의 다른 글

[Spring] ArgumentResolver  (0) 2023.09.29
[Spring] Filter  (0) 2023.09.29
[Spring] Interceptor  (0) 2023.09.29
[Spring] DispatcherServlet  (0) 2023.08.01
[Spring] Servlet이란  (0) 2023.07.25

ArgumentResolver

애노테이션 기반의 컨트롤러는 매우 다양한 파라미터를 사용할 수 있었다.
@RequestParam, @ModelAttribute 는 HTTP 요청 파라미터 데이터를 사용하고,
@RequestBody 는 HTTP 메시지 바디의 데이터를 사용하는 등 애노테이션으로 사용할 데이터를 구분하였다.
이렇게 애노테이션에 따라 파라미터를 유연하게 처리할 수 있었던게 바로 ArgumentResolver 덕분이다.

 

동작 방식

아래는 HandlerMethodArgumentResolver 구현체이다.

public class AuthResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return false;
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        return null;
    }
}

1. supportsParameter()

ArgumentResolver 의 supportsParameter() 를 호출해서 해당 파라미터를 지원하는지 체크한다.

2. resolveArgument()

supportsParameter() 의 값이 true 이면 resolveArgument() 를 호출해서 실제 객체를 생성한다.

이렇게 생성된 객체가 컨트롤러 호출시 아래의 그림처럼 넘어간다.

 

 

 

 

관련글

ArgumentResolver 실습

'백엔드 > Spring' 카테고리의 다른 글

[Spring] @ConfigurationProperties  (0) 2023.10.08
[Spring] Filter  (0) 2023.09.29
[Spring] Interceptor  (0) 2023.09.29
[Spring] DispatcherServlet  (0) 2023.08.01
[Spring] Servlet이란  (0) 2023.07.25

필터란

사용자 인증과 같은 웹과 관련된 공통 관심 사항을 효과적으로 처리할 수 있도록 서블릿이 지원하는 기능이다.

여기서 말하는 서블릿은 DispatcherServlet  이 아닌 서블릿 기술 자체를 말한다.

필터는 스프링에 종속되지 않은 기술이므로org.springframework 패키지가 아닌 javax.servlet 패키지에 속해있다.

 

공통 관심사는 스프링의 AOP로도 해결할 수 있지만 아래와 같은 이유로 웹과 관련된 공통 관심사는

서블릿 필터 또는 스프링 인터셉터를 사용하는 것이 좋다. 

  • 웹과 관련된 공통 관심사를 처리할 때는 쿠키나 세션을 확인해야 하기 때문에 HTTP의 헤더나 URL의 정보들이 필요하다.
  • 서블릿 필터나 스프링 인터셉터는 HttpServletRequest 를 제공하기 때문에 이를 처리하기 용이하다.

 

필터 흐름

HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 컨트롤러

 

필터의 흐름은 위와 같다. 필터를 적용하면 필터가 호출 된 다음에 서블릿(DispatcherServlet )이 호출된다.

그래서 모든 고객의 요청 로그를 남기는 요구사항이 있다면 필터를 사용하면 된다.

필터는 특정 URL 패턴에 적용할 수 있다. URL 패턴을 ("/*") 이라고 하면 모든 요청에 필터가 적용된다.

 

필터 제한

로그인 사용자
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 컨트롤러       

비 로그인 사용자
HTTP 요청 -> WAS -> 필터  <<< 적절하지 않은 요청, 서블릿 호출 X    

 

필터에서 적절하지 않은 요청이라고 판단하면 서블릿을 호출하지 않고 거기에서 끝낼수도 있다. 

 

필터 체인

HTTP 요청 -> WAS -> 필터1 -> 필터2 -> 필터3 -> 서블릿 -> 컨트롤러

 

필터는 체인으로 구성되는데 중간에 필터를 자유롭게 추가할 수 있다.

예를 들어서 로그를 남기는 필터를 먼저 적용하고 그 다음에 로그인 여부를 체크하는 필터를 만들 수 있다.

 

 

 

참고자료)

 

스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 - 인프런 | 강의

웹 애플리케이션 개발에 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. MVC 2편에서는 MVC 1편의 핵심 원리와 구조 위에 실무 웹 개발에 필요한 모든 활용 기술들을 학습할 수 있

www.inflearn.com

 

'백엔드 > Spring' 카테고리의 다른 글

[Spring] @ConfigurationProperties  (0) 2023.10.08
[Spring] ArgumentResolver  (0) 2023.09.29
[Spring] Interceptor  (0) 2023.09.29
[Spring] DispatcherServlet  (0) 2023.08.01
[Spring] Servlet이란  (0) 2023.07.25

인터셉터란

사용자 인증과 같은 웹과 관련된 공통 관심 사항을 효과적으로 처리할 수 있도록 스프링 MVC 가 제공하는 기능이다.

서블릿 필터 서블릿이 제공하는 기능이라면 스프링 인터셉터 스프링 MVC 가 제공하는 기능이다.

 

공통 관심사는 스프링의 AOP로도 해결할 수 있지만 아래와 같은 이유로 웹과 관련된 공통 관심사는

서블릿 필터 또는 스프링 인터셉터를 사용하는 것이 좋다. 

  • 웹과 관련된 공통 관심사를 처리할 때는 쿠키나 세션을 확인해야 하기 때문에 HTTP의 헤더나 URL의 정보들이 필요하다.
  • 서블릿 필터나 스프링 인터셉터는 HttpServletRequest 를 제공하기 때문에 이를 처리하기 용이하다

 

스프링 인터셉터 흐름

HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터 -> 컨트롤러

 

스프링 인터셉터의 흐름은 위와 같다. 스프링을 사용하기 때문에 위 흐름에서 표시된 서블릿은 DispatcherServlet 이다.

인터셉터는 스프링 MVC 가 제공하는 기능이기 때문에 서블릿을 지난 다음 적용된다.

 

스프링 인터셉터 제한

로그인 사용자
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터 -> 컨트롤러       

비 로그인 사용자                                              
HTTP 요청 -> WAS -> 필터  -> 서블릿 -> 스프링 인터셉터 <<< 적절하지 않은 요청, 컨트롤러 호출 X    

 

인터셉터에서 적절하지 않은 요청이라고 판단하면 서블릿을 호출하지 않고 거기에서 끝낼수도 있다. 

 

인터셉터 체인

HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 인터셉터1 -> 인터셉터2 -> 인터셉터3 -> 컨트롤러

 

스프링 인터셉터는 체인으로 구성되는데 중간에 인터셉터를 자유롭게 추가할 수 있다.

예를 들어서 로그를 남기는 인터셉터를 먼저 적용하고 그 다음에 로그인 여부를 체크하는 인터셉터를 만들 수 있다.

 


필터와의 차이점

위까지만 보면 사실 필터와 크게 다른 차이를 못느낄것이다.

필터는 서블릿(DispatcherServlet ) 호출 전에 적용되기 때문에 Response 와 Request 정도만 조정할 수 있지만

인터셉터는 서블릿 호출 이후에 적용되므로 컨트롤러 호출 전, 호출 후, 요청 완료 이후 까지 세분화해서 조정할 수 있다.

 

인터셉터 호출 흐름

1. 정상 흐름

 

  1. preHandle
    - 컨트롤러 전에 호출된다.
    - preHandle 의 응답값이 true 면 다음으로 진행하고, false 면 요청을 여기서 끝낸다.
      false 일 경우 체인된 나머지 인터셉터는 물론이고 핸들러 어댑터도 호출되지 않는다.
  2. postHandle
    - 컨트롤러 호출 후에 호출된다. (정확히는 핸들러 어댑터 호출 후)
  3. afterCompletion
    - 뷰가 렌더링 된 이후에 호출된다.
    - 항상 호출되며, 예외 상황 시 예외 정보를 포함해서 호출된다.

2. 스프링 인터셉터 예외 상황

 

  1. preHandle
    - 컨트롤러 전에 호출된다.
  2. postHandle
    - 컨트롤러에서 예외가 발생하면 postHandle 은 호출되지 않는다.
  3. afterCompletion
    - 항상 호출되며, 예외 상황 시 예외 정보를 포함해서 호출된다.

위처럼 afterCompletion은 예외가 발생해도 호출된다.

예외가 발생하면 postHandle() 은 호출되지 않으므로 예외와 무관하게 공통 처리를 하려면

afterCompletion() 을 사용해야 한다.

 

 

참고자료)

 

스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 - 인프런 | 강의

웹 애플리케이션 개발에 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. MVC 2편에서는 MVC 1편의 핵심 원리와 구조 위에 실무 웹 개발에 필요한 모든 활용 기술들을 학습할 수 있

www.inflearn.com

 

'백엔드 > Spring' 카테고리의 다른 글

[Spring] ArgumentResolver  (0) 2023.09.29
[Spring] Filter  (0) 2023.09.29
[Spring] DispatcherServlet  (0) 2023.08.01
[Spring] Servlet이란  (0) 2023.07.25
[Spring] 의존성 주입 방법  (0) 2023.07.18

스프링 MVC의 핵심 - DispatcherServlet

DispatcherServlet 은 클라이언트의 요청을 가장 앞단에서 받아 처리하는 스프링의 FrontController  이다.

따라서 DispatcherServlet 를 통해 처리되는 요청은 일련의 스프링 MVC 의 처리 과정을 거친다는의미이다.

 

요청 흐름

  1. 서블릿이 호출되면 HttpServlet 이 제공하는 serivce() 를 호출
  2. 스프링 MVC는 DispatcherServlet 의 부모인 FrameworkServlet 에서 service() 를 Override 해둠
  3. FrameworkServlet.service() 를 시작으로 여러 메서드가 호출되면서 DispacherServlet.doDispatch() 를 호출

 

동작 순서

  1. 핸들러 조회: 핸들러 매핑을 통해 요청 URL에 매핑된 핸들러(컨트롤러)를 조회한다.
  2. 핸들러 어댑터 조회: 핸들러를 실행할 수 있는 핸들러 어댑터를 조회한다.
  3. 핸들러 어댑터 실행: 핸들러 어댑터를 실행한다.
  4. 핸들러 실행: 핸들러 어댑터가 실제 핸들러를 실행한다.
  5. ModelAndView 반환: 핸들러 어댑터는 핸들러가 반환하는 정보를 ModelAndView로 변환해서 반환한다.
  6. viewResolver 호출: 뷰 리졸버를 찾고 실행한다.
    JSP의 경우: InternalResourceViewResolver 가 자동 등록되고, 사용된다.
  7. View 반환: 뷰 리졸버는 뷰의 논리 이름을 물리 이름으로 바꾸고, 렌더링 역할을 담당하는 뷰 객체를 반환한다.
    JSP의 경우 InternalResourceView(JstlView) 를 반환하는데, 내부에 forward() 로직이 있다.
  8. 뷰 렌더링: 뷰를 통해서 뷰를 렌더링 한다.

'백엔드 > Spring' 카테고리의 다른 글

[Spring] Filter  (0) 2023.09.29
[Spring] Interceptor  (0) 2023.09.29
[Spring] Servlet이란  (0) 2023.07.25
[Spring] 의존성 주입 방법  (0) 2023.07.18
[Spring] 의존성 주입(Dependency Injection) 이란?  (0) 2023.03.28

서블릿

서블릿(Servlet)이란 동적 웹 페이지를 만들 때 사용되는 자바 기반의 웹 애플리케이션 프로그래밍 기술이다.

 

 

위의 그림에서 개발자가 실제로 만드는 의미있는 비즈니스 로직은 초록색 부분에 불과하다.

하지만 웹 애플리케이션 동작에는 초록색 이외의 모든 과정 또한 필요한데

이 과정들을 대신해서 처리하여 개발자가 비즈니스 로직에 집중할 수 있게 해주는 것이 서블릿이다.

즉, 서블릿은 웹 요청과 응답의 흐름을 간단한 메서드 호출만으로 체계적으로 다룰 수 있게 해주는 기술이다.

 


서블릿 컨테이너

클라이언트로부터 요청을 받을 때 서블릿을 실행시켜주는 톰캣과 같은 WAS 를 서블릿 컨테이너라고 한다.

 

서블릿 컨테이너는 서블릿 객체를 생성, 초기화, 호출, 종료하는 생명주기를 관리한다.

JSP도 서블릿으로 변환 되어서 사용되며 동시 요청을 위한 멀티 쓰레드 처리를 지원한다.

 

HTTP 요청, 응답 흐름

  1. HTTP 요청 시 WAS는 Request, Response 객체를 새로 만들어서 서블릿 객체 호출
  2. 개발자는 Request 객체에서 HTTP 요청 정보를 편리하게 꺼내서 사용
  3. 개발자는 Response 객체에 HTTP 응답 정보를 편리하게 입력
  4. WAS는 Response 객체에 담겨있는 내용으로 HTTP 응답 정보를 생성

 

서블릿 객체는 싱글톤으로 관리한다.

고객의 요청이 올 때 마다 계속 객체를 생성하는 것은 비효율적이다.

따라서 서블릿 객체는 최초 로딩 시점에 싱글톤으로 미리 만들어두고 재활용한다.

이후 서블릿 컨테이너 종료시 함께 종료된다.

  • 모든 고객 요청은 동일한 서블릿 객체 인스턴스에 접근
  • 싱글톤이므로 공유 변수 사용에 주의 !

 


DispatcherServlet 

스프링을 사용해봤다면 DispatcherServlet 을 들어봤을것이다. 그렇다면 DispatcherServlet 은 뭘까?

이 앞에서 클라이언트가 요청 시 서블릿 컨테이너가 요청을 받는다고 했다.
스프링은 이 요청들을 가장 앞단에서 받아 처리하는 FrontController 라는 개념을 두었다.

FrontController 가 바로 DispatcherServlet 이다.

따라서 DispatcherServlet 을 통해 처리되는 요청은 일련의 스프링 MVC 의 처리 과정을 거친다는의미이다.

 

DispatcherServlet 의 동작 방식은 여기를 참고하자.

'백엔드 > Spring' 카테고리의 다른 글

[Spring] Filter  (0) 2023.09.29
[Spring] Interceptor  (0) 2023.09.29
[Spring] DispatcherServlet  (0) 2023.08.01
[Spring] 의존성 주입 방법  (0) 2023.07.18
[Spring] 의존성 주입(Dependency Injection) 이란?  (0) 2023.03.28

필드 주입

필드에 바로 주입하는 방법이다.

  • 코드가 간결해 많은 개발자들을 유혹하지만 외부에서 변경이 불가능해서 테스트하기 힘들다는 치명적인 단점이 있다.
  • DI 프레임 워크가 없으면 아무것도 할 수 없다.
  • 사용하지 않는것을 권장한다.
@Component
public class OrderServiceImpl implements OrderService {
	@Autowired
	private MemberRepository memberRepository;
	@Autowired
	private DiscountPolicy discountPolicy;
}

 

수정자(setter) 주입

setter라 불리는 필드의 값을 변경하는 수정자 메서드를 통해서 의존관계를 주입하는 방법이다.

  • 선택, 변경 가능성이 있는 의존관계에 사용
@Component
public class OrderServiceImpl implements OrderService {
	private MemberRepository memberRepository;
	private DiscountPolicy discountPolicy;
    
	@Autowired
	public void setMemberRepository(MemberRepository memberRepository) {
		this.memberRepository = memberRepository;
	}
    
	@Autowired
	public void setDiscountPolicy(DiscountPolicy discountPolicy) {
		this.discountPolicy = discountPolicy;
	}
}

 

생성자 주입

생성자를 통해서 의존 관계를 주입받는 방법이다.

  • 생성자 호출 시점에 딱 1번만 호출되는 것이 보장된다.
  • 불변, 필수 의존관계에 사용한다.
@Component
public class OrderServiceImpl implements OrderService {
	private final MemberRepository memberRepository;
	private final DiscountPolicy discountPolicy;
    
	@Autowired
	public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
		this.memberRepository = memberRepository;
		this.discountPolicy = discountPolicy;
	}
}

 

결론

애플리케이션을 개발할 때 의존성을 변경하는 경우는 거의 없다.

변경 가능성이 있는 setter는 웬만해서는 열지 않는 것을 추천하므로 의존성 주입은 되도록 생성자 주입을 사용하자.

 

'백엔드 > Spring' 카테고리의 다른 글

[Spring] Filter  (0) 2023.09.29
[Spring] Interceptor  (0) 2023.09.29
[Spring] DispatcherServlet  (0) 2023.08.01
[Spring] Servlet이란  (0) 2023.07.25
[Spring] 의존성 주입(Dependency Injection) 이란?  (0) 2023.03.28

의존성 주입(DI, Dependency Injection) 이란

애플리케이션 실행 시점(런타임)에 외부에서 실제 구현 객체를 생성하고 클라이언트에 그 참조값을 전달하여

클라이언트와 서버의 실제 의존 관계가 연결 되는것이다.

여기서 클라이언트란 인터넷 브라우저를 뜻하는게 아닌 DI를 주입받는 객체를 의미한다.

 

의존성 주입의 장점

DI를 사용하면 구현체가 아닌 추상화에 의존하여 클라이언트의 코드를 변경하지 않고

클라이언트가 호출하는 대상의 타입 인스턴스를 변경할 수 있어서 유지보수에 유리하다

 

의존성 주입의 방법

필드, getter/setter, 생성자 주입이 있다

'백엔드 > Spring' 카테고리의 다른 글

[Spring] Filter  (0) 2023.09.29
[Spring] Interceptor  (0) 2023.09.29
[Spring] DispatcherServlet  (0) 2023.08.01
[Spring] Servlet이란  (0) 2023.07.25
[Spring] 의존성 주입 방법  (0) 2023.07.18

+ Recent posts