SMALL

 

기존 설정

# application.yml

# default
spring:
    profiles:
        active: test
---
spring:
    profiles: test
        include:
        - common
# 기타 설정 .. 
---
spring:
    profiles: prod
# 기타 설정 .. 
---
spring:
    profiles: dev
# 기타 설정 ..

spring.profiles 설정을 통해 어떠한 profile인지 표시 

spring.profiles.include 설정을 통해 추가적으로 포함할 profile 표시

 

2.4 버전 이후 설정

spring.profiles 설정은 duplicated 되었다

# application.yml

# default
spring:
  profiles:
    active: test
  group:
    dev: 
    - dev-t1
    - common
---
spring:
  config:
    activate:
      on-profile: test
# 기타 설정 .. 
---
spring:
  config:
    activate:
      on-profile: prod
# 기타 설정 ..       
---
spring:
  config:
    activate:
      on-profile: dev 
# 기타 설정 ..

spring.config.activate.on-profile 설정을 통해 어떠한 profile인 경우 해당 설정들이 활성화되는지 표시 

spring.profiles.group 설정을 통해 profile들을 그룹핑 

 

활성 profile 설정 

실제로 application을 실행할 때 spring.profiles.active 설정을 주어 어떠한 profile를 활성화할 것인지 정해주어야 한다.

해당 설정이 없을 시에는 default값으로 "default" profile이 실행된다.

 

위의 예시들처럼 yaml, properties file에 설정을 해주거나 

실행 시 argument로 넘겨도 된다.

java -jar myApp.jar --spring.profiles.active=dev

 

또는, 사용하는 개발 tool의 실행 argument에 설정해주면 된다.

( 이클립스의 경우 실행 프로젝트의 오른쪽 클릭 -> Run as.. -> Run Configurations ) 

 

추가적으로, 참고할만한 링크

spring-projects github에 2.4 관련 release note와 config data migration guide 

 

https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.4-Release-Notes

 

GitHub - spring-projects/spring-boot: Spring Boot

Spring Boot. Contribute to spring-projects/spring-boot development by creating an account on GitHub.

github.com

 

 

https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-Config-Data-Migration-Guide

 

GitHub - spring-projects/spring-boot: Spring Boot

Spring Boot. Contribute to spring-projects/spring-boot development by creating an account on GitHub.

github.com

 

LIST
SMALL

설정 파일 분리 

application.yml을 사용하는 경우에는 "---"를 사용하여 설정을 분리할 수 있다.

server:
  port: 8089
spring:
  profiles:
    active:
    - test
  application:
    name: pj2
---
server:
  port: 8089
spring:
  profiles:
    active:
    - dev
  application:
    name: devPj2

 

또한, profile 단위로 설정 파일 자체를 분리할 수도 있다.

  • application-dev.yml
  • application-test.yml
  • application-{profile}.yml

각각의 파일은 spring application을 실행 시 args 등으로 넘기는 spring.profiles.active로 결정되어진다. 

java -jar myApp.jar --spring.profiles.active=dev

 

여러 개의 설정파일 

하나의 profile에서 사용되는데 여러개의 설정 파일을 로드하고 싶은 경우가 존재할 수 있다.

이러한 경우에는 어떻게 설정하는지 알아보자.

 

spring.config.location

이 설정은 spring config의 기본 설정을 재정의하여 여러 가지의 설정 파일을 로드할 수 있다.

spring config에서 default로 제공하는 값은 아래와 같다.

사용자가 spring.config.location을 정의하면 default값은 덮어써지게 된다.

 

spring.config.additional-location

이 설정은 추가적인 위치를 설정하는 것이다.

location 설정에 플러스로 더 설정하고 싶은 경우 

 

spring.config.location, spring.config.additional-location 설정은 application.yml 파일을 로드하기 전에 사용되는 설정으로 해당 파일에 설정하면 동작하지 않는다.

SystemProperties나 CommandLine Argument로 전달해야 정삭적으로 사용될 수 있다.

java -jar myApp.jar --spring.config.additional-location=optional:classpath:test.yml

 

spring.config.import

해당 설정은 spring boot 2.4 이후부터 추가된 설정으로 위에 설명한 설정들과 달리 application.yml를 통해서 추가적인 설정 파일의 위치를 설정할 수 있다. 

 

spring:
  config:
    import:
    - optional:classpath:/test.yml

 

위의 내용들에 아래 spring문서를 확인하면 더욱 자세하게 확인 할 수있다.

https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config

LIST
SMALL

HandlerMethodArgumentResolver 란

 

HandlerMethodArgumentResolver는 Controller Method에서 특정 조건에 맞는 파라미터가 있을 때 원하는 값을 바인딩해주는 인터페이스이다.

 

Spring 공식 문서에는 아래와 같이 설명되어있다.

Strategy interface for resolving method parameters into argument values in the context of a given request.

주어진 요청으로부터, method의 parameter를 argument로 주입해주는 전략 패턴의 interface

 

Spring MVC를 작성할때, Controller에 정의한 Method들에도 @PathVariable, @RequestParam을 사용하는데 이러한 것들도 모두 HandlerMethodArgumentResolver를 구현하여 작성한 기능들이다.

 

Spring Example

위의 코드는 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter의 코드로 Spring MVC에서 기본적으로 등록된 MethodArgumentResolver들이다.

 

간단하게 많이 사용하는 MethodArgumentResolver를 살펴보면 아래와 같다.

  • PathVariableMethodArgumentResolver
    • @PathVariable
  • RequestParamMethodArgumentResolver
    • @ReqeustParam

 

Custom HandlerMethodArgumentResolver 

 

@Controller
public class indexController{
    private final HttpSession httpSession;
    @GetMapping("/")
    public String index(Model model){
        User user = (User) httpSession.getAttribute("user");
        if(user != null){
            model.addAttribute("userName", user.getName());
        }
        return "index";
    }
}

위의 예시에서 HttpSession에서 부터 User를 가져오는 코드를 User가 필요한 모든 Controller의 Method에 작성하는 일은 중복 코드가 발생하고, 비효율적이다.

 

이러한 비효율성을 HandlerMehtodArguementResolver를 통해서 해결해보자.

 

HandlerMehtodArguementResolver

@RequiredArgsConstructor
@Component
public class LoginUserArgumentResolver implements HandlerMethodArgumentResolver { 
// 조건에 맞는 경우 메소드가 있다면 구현체가 지정한 값으로 해당 메소드의 파라미터를 넘길 수 있다
    private final HttpSession httpSession;

    @Override
    public boolean supportsParameter(MethodParameter parameter) { 
    // 컨트롤러 메서드의 특정 파라미터를 지원하는지 판단 
    // 파라미터 클래스 타입이 SessionUser인 경우 true 반환 
        boolean isUserClass = SessionUser.class.equals(parameter.getParameterType());

        return isUserClass;
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
            // 파라미터에 전달할 객체를 생성 

        return httpSession.getAttribute("user");
    }

}

여기서 사용된 SessionUser는 로그인시 HttpSession에 담아놓은 사용자가 정의한 User Class이다. 

 

HandlerMehtodArguementResolver 등록

 

@RequiredArgsConstructor
@Configuration
public class WebConfig implements WebMvcConfigurer { 
    private final LoginUserArgumentResolver loginUserArgumentResolver;

    @Override 
    // HandlerMethodArgumentResolver는 항상 WebMvcConfigurer의 addArgumentResulvers()를 통해 추가해야 한다 
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(loginUserArgumentResolver);
    }
}

 

Controller

 

@Controller
@RequiredArgsConstructor
public class IndexController {
    private final PostsService PostsService;
    // private final HttpSession httpSession;

    @GetMapping("/")
    public String index(Model model, SessionUser user) {
        model.addAttribute("posts", PostsService.findAllDesc());
        // SessionUser user = (SessionUser) httpSession.getAttribute("user"); 
        if (user != null) { // 세선에 저장된 값이 있을 때만 model에 userName을 등록해 준다
            model.addAttribute("userName", user.getName());
        }
        return "index";
    }
}

 

 

LIST
SMALL

DTO 데이터 검증이 필요한 이유 

보통 Web Appliation을 작성하면, Front-End에서 사용자가 입력한 정보를 검증하고, 검증이 완료된 데이터를 Back-End에게 넘겨주게 된다.

하지만, 보통의 경우가 아닌 상황에서는 Back-End에서는 입력값으로 받은 DTO를 신뢰할 수가 없게 된다.

( Web Application UI를 통해 요청된 Request가 아닌 경우 - curl, postman 등등..)

이러한 상황에 대한 대비로 Back-End에서도 Request로부터 전달받은 DTO에 대한 검증이 필요하다.

 

@Valid

Spring에서는 DTO 필드에 조건과 메세지를 작성해주면, @Valid를 이용해 데이터 검증을 할 수 있다.

 

@Valid는 JSR-303, 380 표준 스펙으로

제약 조건이 부여된 객체에 대해 Beam Validator를 이용하여 검증하도록 지시하는 Annotation이다.

Spring에서는 LocalValidatorFactoryBean을 이용해 JSR 표준의 검증 기능을 사용한다.

 

SpringBoot 2.3 이상의 버전 부터는 spring-boot-starter-web 의존성 내부에 있던 validation이 사라졌다.

spring-boot-starter-validation 의존성을 따로 추가해 주어야 한다. 

 

Dto

@Getter
@NoArgsConstructor
public class UserSaveRequestDto {

    @NotBlank(message = "Please, insert UserID!")
    private String userId;

    @NotBlank(message = "Please, insert UserName!")
    private String userName;

    @Email(message = "Please, insert valid e-mail!",
            regexp = "^(?=.{1,64}@)[A-Za-z0-9_-]+(\\.[A-Za-z0-9_-]+)*@[^-][A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$")
    private String email;

    @NotBlank(message = "Please, insert password!")
    private String password;

    @Size(min = 1, message = "User must be mapped with Group")
    @NotNull(message = "Please, insert GroupIdList!")
    private Set<String> groupIdList;
}

javax.validation.constraints에서 제공하는 valid 관련 Annotation 

https://docs.jboss.org/hibernate/beanvalidation/spec/2.0/api/

 

 

Controller

@RequiredArgsConstructor
@RestController
public class UserController {
    private final UserService userService;

    @PostMapping("/user")
    public String save(@RequestBody @Valid UserSaveRequestDto requestDto) throws Exception {
        return userService.save(requestDto);
    }
}

검증을 원하는 @RequestBody argument에 @Valid Annotation을 추가해 주면 된다.

 

Exception Handling

@ControllerAdvice
public class CommonExceptionHandler<T> {

    @ExceptionHandler(value = {MethodArgumentNotValidException.class})
    protected ResponseEntity<Response<T>> handleMethodArgNotValidException(
            MethodArgumentNotValidException exception) {

        Response<T> response = new Response<>();
        response.addErrorMsgToResponse(
                exception.getBindingResult().getAllErrors().get(0).getDefaultMessage()
        );

        return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE).body(response);
    }
}

@RequestBody로 부터 받은 파라미터에 Error가 발생한 경우에는 MethodArgumentNotValidException이 발생한다.

Spring의 Exception Handling 관련 Annotation을 이용하여 해당 Exception에 대한 처리를 추가해 주면 된다.

LIST

+ Recent posts