들어가며
처음 써보는 Spring Security와 JWT가 너무 어렵다... 막상 정리하려니 되게 막막하므로
오늘의 TIL은 2주차 과제를 하면서 발생한 에러들에 대한 트러블 슈팅을 해보고자 한다.
Lombok으로 생성자 주입하기
의존성 주입을 해주는 방법은 크게 3가지가 존재한다.
- 생성자 주입
- 필드 주입
- 수정자(setter) 주입
이 중 가장 권장되는 방식은 생성자 주입이라고 한다.
생성자는 객체를 생성할 때 한 번만 호출되므로 불변하다는 특징을 갖게되고, 그러므로 final 키워드를 사용할 수 있게 되기 때문이다.
이런 생성자의 특징을 이용하면 Lombok의 @RequiredArgsConstructor
로 간단하게 생성자 주입을 구현할 수 있다.
@RequiredArgsConstructor
는 final 키워드나 @NonNull 어노테이션이 붙어있는 필드를 인자로 가진 생성자를 자동으로 만들어 주는 녀석이다. 생성자 주입은 해당 클래스에 주입하고자하는 객체를 인자로 받아오도록 구현한다.
이렇게만 봐도 대충 어떻게 사용할 지 감이 온다. 실제 코드를 봐보자.
Lombok 적용 전
@RestController
@RequestMapping("/api")
public class UserRestController {
private final UserService userService;
@Autowired
private UserRestController(UserService userService) {
this.userService = userService;
}
Lombok 적용 후
@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class UserRestController {
private final UserService userService;
상대적으로 코드가 깔끔해진 걸 확인할 수 있다.
하지만 그렇다고 Lombok을 마구 남용하는 건 좋지 않은 습관이다. Lombok을 이용해서 개발을 하다보면 stackoverflow 에러를 만날 때도 있고, Lombok에 대해 잘 모를 때는 이게 대체 무슨 코드인지 헷갈릴 때도 많다. (내가 그랬다)
Lombok을 사용할 때의 주의점에 대해 정리한 글을 보면 정말 많은 경우에 사용을 자제하는게 좋다고 말하고 있다.
이 중에서 '사용금지'라고 적혀있는 어노테이션 중에 하나가 @RequiredArgsConstructor
라고 하는 걸 볼 수 있는데, 개인 토이 프로젝트에서만 사용하고 실무 프로젝트에서는 가능한 보수적으로 사용하도록 하자!
트러블 슈팅
문제 - Spring Security config - deprecated
항해99 강의를 보면서 회원가입과 로그인을 위한 Spring Security를 공부하던 중 Spring Security를 위해 사용하는 WebSecurityConfigurerAdapter가 더 이상 사용되지 않는다는 의미의 deprecated 취소선이 떴다.
구글링을 해보니 Spring 공식 문서에서 대체로 사용할 방식으로 변경이 필요하다고 한다.
기존 WebSecurityConfigurerAdapter를 사용하는 방식
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authz) -> authz
.anyRequest().authenticated()
)
.httpBasic(withDefaults());
}
}
기존의 방식은 WebSecurityConfigurerAdapter가 제공하는 메서드를 오버라이드하여 사용했었다.
Spring에서 권장하는 SecurityFilterChain을 사용하는 방식
@Configuration
public class SecurityConfiguration {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authz) -> authz
.anyRequest().authenticated()
)
.httpBasic(withDefaults());
return http.build();
}
}
위처럼 SecurityFilterChain을 빈에 등록하여 사용할 수 있다.
기존 방식 대신 SecurityFilterChain을 사용하는 이유는 다음 Spring 공식 문서에서 자세히 다루고 있으므로 참고하길 바란다.
문제 - file upload - 415 unsurpported media type 에러
MultipartFile 타입으로 image 파일을 받아와서 DB에 저장하는 로직을 구현하고 나서 postman으로 테스트를 했는데 다음과 같은 에러가 나타났다.
Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'multipart/form-data;boundary=--------------------------592347047834626803344370;charset=UTF-8' not supported]
에러가 난 코드는 다음과 같다
@PostMapping("/register")
public boolean register(@RequestBody @Valid RegisterRequestDto requestDto) {
userService.register(requestDto);
// 에러처리 할 것.
return true;
}
별거 없어보이는데 해답은 @RequestBody
어노테이션에 있었다.
@RequestBody
어노테이션은 RequestBody에 담겨오는 JSON이나 XML 데이터를 파싱하여 객체로 변환시켜주는 어노테이션인데, MultipartFile의 경우에는 JSON data가 아니라 Form data로 넘어오기 때문에 @ModelAttribute
어노테이션을 사용해줘야 한다.
postman을 사용할 때의 방식도 조금 바뀐다.
Headers의 Content-Type을 mulitipart/form-data로
Body는 form-data로 선택하여 보내주면 된다.
두 어노테이션의 차이를 간단하게 정리하자면,
@RequestBody
를 사용하면 요청 본문의 JSON, XML, Text 등의 데이터가 적합한 HttpMessageConverter를 통해 파싱되어 Java 객체로 변환 된다.
@ModelAttribute
를 사용하면 HTTP 파라미터 데이터를 Java 객체에 맵핑한다. Query String 및 Form 형식이 아닌 데이터는 처리할 수 없고, 객체의 필드에 접근해 데이터를 바인딩 할 수 있는 생성자 혹은 setter 메서드가 필요하다.
문제 - JPA 컬럼 타입(varchar 등)의 크기 지정하기
기존에는 각 타입의 크기에 대해 생각할 일이 없어서 몰랐는데, 이번 기획에 따라 DB에 저장되는 컬럼의 크기를 제한하여 저장하려고 보니 JPA에서 따로 설정을 해줘야한다는 걸 알았다.
방법은 간단하다.
@Column
어노테이션의 속성에 length만 추가해주면 된다.
예시
@Column(nullable = false, length = 10)
private String name;
@Column(nullable = false, length = 20)
private String nickname;
@Column(nullable = false, unique = true, length = 50)
private String email;
삽질로그
마치며
마음같아서는 Spring Security에 대해 정리하고 싶은데 최근까지 배운 것 중 top3 안에 들만큼 정리할 부분이나 이해를 못하고 있는 부분이 많다. 내일은 과제보단 강의를 들으면서 JWT, Oauth 2.0와 함께 어떤 구조로 되어있는 지를 정리해봐야겠다.
출처 및 참고
[Spring, OOP] 생성자 주입이 좋은 이유와 스프링을 이용한 다양한 DI
@RequestBody vs @ModelAttribute
'Study > TIL' 카테고리의 다른 글
[TIL] 06/14 항해99 37일차 - 트러블 슈팅 (0) | 2022.06.15 |
---|---|
[TIL] 06/13 항해99 36일차 - Timestamp format 변경, 트러블 슈팅 (1) | 2022.06.13 |
[TIL] 06/10 항해99 33일차 - 프록시, 즉시 로딩 / 지연 로딩 (0) | 2022.06.11 |
[TIL] 06/09 항해99 32일차 - 스프링 프레임워크(Spring Framework (0) | 2022.06.09 |
[TIL] 06/08 항해99 31일차 - 유효성검증, 삽질로그 (0) | 2022.06.09 |