이 포스팅의 코드 및 정보들은 강의를 들으며 정리한 내용을 토대로 작성한 것이 일부 존재합니다.
@PostMapping("/sign-up")
public String signUpSubmit(@ModelAttribute SignUpForm signUpForm) { ... }
바인딩한 여러 값들, 즉 복합 객체를 받을 때는 @ModelAttribute
가 사용되어야 하지만, 이 어노테이션을 생략하고 SignUpForm signUpForm
으로 받을 수 있다.
@PostMapping("/sign-up")
public String signUpSubmit(@Valid SignUpForm signUpForm, Errors errors) { ... }
이 Errors
는 바로 앞의 signUpForm이라는 복합 객체가 바인딩 되어 올 때 에러가 발생하면, 그 에러를 받는 역할을 한다.
@PostMapping("/sign-up")
public String signUpSubmit(@Valid SignUpForm signUpForm, Errors errors) {
if(errors.hasErrors()) {
return "account/sign-up";
}
...
}
위에서 나온 if문
으로 에러를 받게 되면 다시 입력받게끔 입력 폼을 반환하는 등 처리를 할 수 있다.
@PostMapping("/sign-up")
public String signUpSubmit(@Valid SignUpForm signUpForm, Errors errors) {
if(errors.hasErrors()) { // valid를 이용한 오류 체크
return "account/sign-up";
}
signUpFormValidator.validate(signUpForm, errors);
if(errors.hasErrors()) { // 중복 체크 시 오류 체크
return "account/sign-up";
}
// TODO 회원 가입 처리
return "redirect:/";
}
위 코드는 바인딩 되어 오는 객체를 2번씩이나 검사하고 있다. if문
의 중복이 발생하며, 매번 객체의 유효성을 검증하기 위해 메서드에 많은 내용을 집어넣을 수는 없다.
이 소스를 @InitBinder
를 이용하여 줄일 수 있다.
public class AccountController {
@InitBinder("signUpForm")
public void initBinder(WebDataBinder webDataBinder) {
webDataBinder.addValidators(signUpFormValidator);
}
...
}
signUpForm
이라는 데이터를 받을 때 바인더를 설정할 수 있는 것이다. 이때 WebDataBinder
를 파라미터로 받은 후, validator를 추가할 수 있다.
바인더가 받을 객체의 이름은 signUpSubmit
메서드의 파라미터 중 SignUpForm
등 타입을 따라간다.
@InitBinder에다가 바인딩할 객체의 이름이 검증하고 있던 메서드 파라미터의 타입을 camelCase로 따라간다는 것이다.
@Profile("local") // local일 때 등록해서 사용
@Component
public class Test implements testInterface {
위의 클래스처럼 @Profile
의 옵션을 local로 줬는데, 이게 뭘까?
spring.profiles.active=local
위 properties 파일처럼 기본 스프링 profile의 값을 local로 설정하게 되면
@Profile("local")
로 설정한 클래스들의 어노테이션들이 적용되는 것이다.
즉, Test클래스에 있던 @Component
까지 적용되어 빈이 등록되는 것이다.
CSRF(Cross Site Request Forgery, 사이트 간 요청 위조)란 간단하게
타 사이트에서 내 사이트로 공격하는 사이트를 대상으로 폼 데이터를 보내는 것
예) 은행 계좌 데이터를 은행 사이트가 아닌 타 사이트에서 보낼 수 있는 것
그것을 방지하는 기술로 Spring Security에서는 CSRF 토큰을 사용함
Spring Security를 사용한 상태에서 인가된 페이지를 들어가면
<form action="/sign-up" method="post">
<input type="hidden" name="_csrf" value="ea132e1c-f321-2132-bda1-effcb2e1f123">
...
</form>
form 태그 안에 hidden 타입의 input태그
가 있는 것을 확인할 수 있다. 여기에 CSRF 토큰 값이 저장되어있다. form을 이용하여 서버에 데이터를 post하게 되면 이 CSRF 토큰도 서버에 같이 전송되는 것이다.
토큰 값을 받은 서버는 (본)서버가 만들어준 폼에서 온 데이터라는 것을 인지하고 쓴다. CSRF 토큰 없이 데이터만 온다면 권한이 없다는403
에러가 발생한다.
만약 개발자가 작성한 테스트에도 CSRF토큰 없이 테스트하게 된다면, 아무리 개발자가 만든 서버라도 그 서버는 post를 통해 온 데이터를 유효하지 않다고 판단하게 되는 것이다.
@Test
void signUpSubmit_테스트() throws Exception {
mockMvc.perform(post("/sign-up")
...
.with(csrf()))
...
.andExpect(status().isOk())
.andExpect(view().name("account/sign-up"));
}
Mockito를 통해 폼이 보여지는지를 테스트하게 된다면 with(csrf())
를 통해서 토큰 값과 함께 테스트를 수행하면 된다.
리팩토링 하기 전에 테스트 코드를 먼저 작성하면 좋은 점
일을 두 번 할 필요가 없다는 것이다.
<!-- padding과 top,bottom을 5em만큼 주겠다 -->
<div class="py-5"></div>
<!-- margin과 top을 5em만큼 주겠다 -->
<div class="mt-5"></div>
Reference