TIL 2022-12-24

Bruce Han·2022년 12월 24일
0

TIL

목록 보기
2/9
post-thumbnail

이 포스팅의 코드 및 정보들은 강의를 들으며 정리한 내용을 토대로 작성한 것이 일부 존재합니다.

1. Spring에서의 Model 바인딩

@PostMapping("/sign-up")
public String signUpSubmit(@ModelAttribute SignUpForm signUpForm) { ... }

바인딩한 여러 값들, 즉 복합 객체를 받을 때는 @ModelAttribute가 사용되어야 하지만, 이 어노테이션을 생략하고 SignUpForm signUpForm으로 받을 수 있다.

2. Spring에서 복합 객체를 검증할 때 errors 사용하기

@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문으로 에러를 받게 되면 다시 입력받게끔 입력 폼을 반환하는 등 처리를 할 수 있다.

3. @InitBinder로 객체를 바인딩하여 검증하기

@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로 따라간다는 것이다.

4. spring.profiles.active & @Profile

@Profile("local") // local일 때 등록해서 사용
@Component
public class Test implements testInterface {

위의 클래스처럼 @Profile의 옵션을 local로 줬는데, 이게 뭘까?

spring.profiles.active=local

위 properties 파일처럼 기본 스프링 profile의 값을 local로 설정하게 되면
@Profile("local")로 설정한 클래스들의 어노테이션들이 적용되는 것이다.
즉, Test클래스에 있던 @Component까지 적용되어 빈이 등록되는 것이다.

5. CSRF 깔짝 상식(feat.스프링 시큐리티)

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())를 통해서 토큰 값과 함께 테스트를 수행하면 된다.

6. 리팩토링하기 전 테스트 코드 작성에 관하여

리팩토링 하기 전에 테스트 코드를 먼저 작성하면 좋은 점

  • 그래야 코드를 변경한 이후에 불안하지 않다.
  • 변경한 코드가 무언가를 깨트리지 않았다는 것을 확인할 수 있다.

일을 두 번 할 필요가 없다는 것이다.

6-1. 테스트 체크리스트

  • 폼에 이상한 값이 들어간 경우에 다시 폼이 보여지는가?
  • 폼에 있는 값이 정상적인 경우
    • 가입한 회원 데이터가 존재하는가?
    • 이메일이 보내지는가?

6-2. 리팩토링 체크리스트

  • 메서드가 너무 길지 않은가? 메서드 몸통이 너무 크지 않은가?
  • 코드를 읽기 쉬운가?
    • 내가 작성한 코드를 내가 읽기 어렵다면 남들에게는 훨씬 더 어렵다.
  • 코드가 적절한 위치에 있는가?
    • 객체들 사이의 의존 관계
    • 책임이 너무 많지는 않은지

7. Bootstrap class 토막 팁

  • p : padding, 패딩
  • y : top, bottom
  • x : left, right
  • m : margin, 마진
  • t : top
<!-- padding과 top,bottom을 5em만큼 주겠다 -->
<div class="py-5"></div>
<!-- margin과 top을 5em만큼 주겠다 -->
<div class="mt-5"></div>
  • lead : 문단을 강조하는 클래스
    • 글자크기와 줄간격이 커진다

Reference

전체 Reference

profile
만 가지 발차기를 한 번씩 연습하는 사람은 두렵지 않다. 내가 두려워 하는 사람은 한 가지 발차기를 만 번씩 연습하는 사람이다. - Bruce Lee

0개의 댓글