-
[스프링 인 액션] Chapter 5 - 구성 속성 사용하기 :: 우리의 구성 속성 생성하기개발서적읽기/Spring in Action 제 5판 2020. 7. 31. 01:22
스프링 빈에서는 구성 속성들을 어떻게 사용할까?
스프링 빈에 구성 속성을 주입하기 위해
스프링 부트는 @ConfigurationProperties 애노테이션을 제공한다.
그리고 어떤 스프링 빈이건 이 애노테이션이 지정되면
해당 빈의 속성들이 스프링 환경의 속성으로부터 주입될 수 있다.
@ConfigurationProperties가 어떻게 동작하는지 알아보기 위해
다음의 ordersForUser 메서드를 OrderController에 추가하자.
@GetMapping
public String ordersForUser(@AuthenticationPrincipal User user, Model model) {
model.addAttribute("orders", orderRepo.findByUserOrderByPlacedAtDesc(user));
return "orderList";
}package tacos.data;
/*
* @USER JungHyun
* @DATE 2020-07-29
* @DESCRIPTION
*/
import org.springframework.data.repository.CrudRepository;
import tacos.Order;
import tacos.User;
import java.util.List;
public interface OrderRepository extends CrudRepository<Order, Long> {
List<Object> findByUserOrderByPlacedAtDesc(User user);
}OrderByPlacedAtDesc에서 OrderBy는 결과를 정렬하는 기준이 되는 속성을 나타낸다.
그리고 제일 끝의 Desc는 결과를 내림차순으로 정렬되게 한다.
orderForUser()는 사용자가 여러 번 주문을 했을 때 유용하게 사용할 수 있다.
그러나 최근 몇 개의 주문이 브라우저에 나타나는 것은 유용하지만,
수백 개의 주문을 여러 페이지에 걸쳐 봐야 한다면 피곤할 것이다.
예를 들어, 가장 최근의 20개 주문만 나타나도록 조회 주문 수를 제한하고 싶다고 해보자.
@GetMapping
public String ordersForUser(@AuthenticationPrincipal User user, Model model) {
Pageable pageable = PageRequest.of(0, 20);
model.addAttribute("orders", orderRepo.findByUserOrderByPlacedAtDesc(user, pageable));
return "orderList";
}package tacos.data;
/*
* @USER JungHyun
* @DATE 2020-07-29
* @DESCRIPTION
*/
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.CrudRepository;
import tacos.Order;
import tacos.User;
import java.util.List;
public interface OrderRepository extends CrudRepository<Order, Long> {
List<Object> findByUserOrderByPlacedAtDesc(User user, Pageable pageable);
}Pageable 객체를 인자로 받기 위해 findByUserOrderByPlacedAtDesc()의 시크니처를 변경하였다.
스프링 데이터의 Pageable 인터페이스를 사용하면
페이지 번호와 크기로 결과의 일부분을 선택할 수 있다.
한편, 페이지 크기를 하드코딩했다는 점이 아쉽다.
만일 한 페이지에 20개가 너무 많아서 향후에 10개로 줄인다면?
현재는 페이지 크기가 하드코딩되어 있으므로 애플리케이션을 다시 빌드 및 배포해야 할 것이다.
이때는 커스텀 구성 속성을 사용해서 페이지 크기를 설정할 수 있다.
우선 pageSize라는 새로운 속성을 OrderController에 추가해야 한다.
그다음에 @ConfigurationProperties 애노테이션을 OrderController에 지정하면 된다.
package tacos.web;
/*
* @USER JungHyun
* @DATE 2020-07-04
* @DESCRIPTION
*/
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.support.SessionStatus;
import tacos.Order;
import tacos.User;
import tacos.data.OrderRepository;
import javax.validation.Valid;
@Slf4j
@Controller
@RequestMapping("/orders")
@SessionAttributes("order")
@ConfigurationProperties(prefix = "taco.orders")
public class OrderController {
private int pageSize = 20;
private OrderRepository orderRepo;
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public OrderController(OrderRepository orderRepo) {
this.orderRepo = orderRepo;
}
@GetMapping("/current")
public String orderForm(@AuthenticationPrincipal User user, @ModelAttribute Order order) {
if (order.getDeliveryName() == null) {
order.setDeliveryName(user.getFullname());
}
if (order.getDeliveryStreet() == null) {
order.setDeliveryStreet(user.getStreet());
}
if (order.getDeliveryCity() == null) {
order.setDeliveryCity(user.getState());
}
if (order.getDeliveryZip() == null) {
order.setDeliveryZip(user.getZip());
}
return "orderForm";
}
@PostMapping
public String processOrder(@Valid Order order, Errors errors, SessionStatus sessionStatus, @AuthenticationPrincipal User user) {
if (errors.hasErrors()) {
return "orderForm";
}
order.setUser(user);
orderRepo.save(order);
sessionStatus.setComplete();
return "redirect:/";
}
@GetMapping
public String ordersForUser(@AuthenticationPrincipal User user, Model model) {
Pageable pageable = PageRequest.of(0, pageSize);
model.addAttribute("orders", orderRepo.findByUserOrderByPlacedAtDesc(user, pageable));
return "orderList";
}
}pageSize 구성 속성 값을 설정할 때는 taco.orders.pageSize라는 이름을 사용해야 한다.
아래는 application.yml 파일에 taco.orders.pageSize값을 설정했다.
spring:
datasource:
url: jdbc:mysql://localhost/tacocloud
username: tacouser
password: tacopassword
greeting:
welcome: You are using ${spring.application.name}
taco:
orders:
pageSize: 10또는 애플리케이션을 프로덕션에서 사용 중에 빨리 변경해야 한다면
다음과 같이 환경 변수에 설정해도 된다. 그러면 다시 빌드 및 배포하지 않아도 된다.
export TACO_ORDERS_PAGESIZE = 10
다음으로 구성 데이터를 속성 홀더에 설정하는 방법을 알아보자
속성 홀더란?
구성 속성 값을 관리하는 클래스
■구성 속성 홀더 정의하기
@ConfigurationProperties가 반드시 컨트롤러나 특정 빈에만 사용될 수 있는 것은 아니다.
@ConfigurationProperties는 구성 데이터의 홀더로 사용되는 빈에 지정되는 경우도 많다.
이렇게 하면 컨트롤러와 이외의 다른 애플리케이션 클래스 외부에 구성 관련 정보를
따로 유지할 수 있다. 또한 여러 빈에 공통적인 구성 속성을 쉽게 공유할 수 있다.
OrderController의 pageSize 속성은 별개의 홀더 클래스로 추출할 수 있다.
package tacos.web;
/*
* @USER JungHyun
* @DATE 2020-07-31
* @DESCRIPTION
*/
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "taco.orders")
@Data
public class OrderProps {
private int pageSize = 20;
}@Component가 지정되었으므로, 스프링 컴포넌트 검색에서 OrderProps를
자동으로 찾은 후 스프링 애플리케이션 컨텍스트의 빈으로 생성해 준다.
이제 OrderController는 기존의 pageSize 속성을 제거하고 OrderProps 빈을 주입받는다.
package tacos.web;
/*
* @USER JungHyun
* @DATE 2020-07-04
* @DESCRIPTION
*/
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.support.SessionStatus;
import tacos.Order;
import tacos.User;
import tacos.data.OrderRepository;
import javax.validation.Valid;
@Slf4j
@Controller
@RequestMapping("/orders")
@SessionAttributes("order")
public class OrderController {
private OrderRepository orderRepo;
private OrderProps props;
public OrderController(OrderRepository orderRepo, OrderProps props) {
this.orderRepo = orderRepo;
this.props = props;
}
@GetMapping("/current")
public String orderForm(@AuthenticationPrincipal User user, @ModelAttribute Order order) {
if (order.getDeliveryName() == null) {
order.setDeliveryName(user.getFullname());
}
if (order.getDeliveryStreet() == null) {
order.setDeliveryStreet(user.getStreet());
}
if (order.getDeliveryCity() == null) {
order.setDeliveryCity(user.getState());
}
if (order.getDeliveryZip() == null) {
order.setDeliveryZip(user.getZip());
}
return "orderForm";
}
@PostMapping
public String processOrder(@Valid Order order, Errors errors, SessionStatus sessionStatus, @AuthenticationPrincipal User user) {
if (errors.hasErrors()) {
return "orderForm";
}
order.setUser(user);
orderRepo.save(order);
sessionStatus.setComplete();
return "redirect:/";
}
@GetMapping
public String ordersForUser(@AuthenticationPrincipal User user, Model model) {
Pageable pageable = PageRequest.of(0, props.getPageSize());
model.addAttribute("orders", orderRepo.findByUserOrderByPlacedAtDesc(user, pageable));
return "orderList";
}
}이제는 OrderController가 직접 pageSize 구성 속성을 처리할 필요가 없다.
OrderController는 더 깔끔해졌고 OrderProps 속성은 다른 빈에서 재사용될 수도 있다.
OrderProps 속성을 다른 빈에서 사용하는 예를 생각해보자.
여러 다른 빈에서 pageSize 속성을 사용하는데, 이 속성의 값이 5부터 25 사이인지
검사하는 애노테이션을 적용하기로 해보자.
OrderProps에 pageSize 속성을 추출했으므로 다음과 같이 OrderProps만 변경하면 된다.
package tacos.web;
/*
* @USER JungHyun
* @DATE 2020-07-31
* @DESCRIPTION
*/
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
@Component
@ConfigurationProperties(prefix = "taco.orders")
@Data
@Validated
public class OrderProps {
@Min(value = 5, message = "must be between 5 and 25")
@Max(value = 25, message = "must be between 5 and 25")
private int pageSize = 20;
}'개발서적읽기 > Spring in Action 제 5판' 카테고리의 다른 글
[스프링 인 액션] Chapter 6 - REST 서비스 생성하기 :: REST 컨트롤러 작성하기 (0) 2020.08.11 [스프링 인 액션] Chapter 5 - 구성 속성 사용하기 :: 프로파일 사용해서 구성하기 (0) 2020.07.31 [스프링 인 액션] Chapter 5 - 구성 속성 사용하기 :: 자동-구성 세부 조정하기 (0) 2020.07.31 [스프링 인 액션] Chapter 4 - 스프링 시큐리티 :: 각 폼에 로그아웃 버튼 추가하고 사용자 정보 보여주기 (0) 2020.07.30 [스프링 인 액션] Chapter 4 - 스프링 시큐리티 :: 사용자 인지하기 (0) 2020.07.30 댓글