프로젝트를 진행하면서, 개의 중성화여부를 나타내는 컬럼을 추가해달라는 요청을 받았다.
true / false값을 저장하는 is_neutered필드를 추가하기로 했고, 찾아보니 MySQL에서는 boolean타입을 별도로 지원하지 않는다고 해서 TINYINT(1)타입으로 필드를 만들어줬다. 1은 true, 0은 false를 나타낼 수 있게끔.
// Dog entity
@Column(columnDefinition = "TINYINT(1)", nullable = false)
private boolean isNeutered;
그리고 강아지를 등록할 때 사용하는 dto들에도 마찬가지로 isNeutuerd라는 필드들을 추가해줬다.
@Getter
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class SignupRequestDto {
private String email;
private String name;
private String dogName;
private String dogType;
private String dogGender;
private boolean isNeutered;
private int dogAge;
// ... 생략
근데 이윽고 잠시 후, 문제가 생겼다. 실제 DB에 쌓이는 데이터들을 보는데, dog테이블의 is_neutered컬럼이 죄다 0만 저장되고 있던 거다.
참고로 프론트(플러터 단)에서는 요로코롬 true값을 담아서 request를 보내고 있던 상황이었음
var res = await dio.post(
"http://localhost:8080/api/auth/signup?provider=$oauth2Provider",
data: {
"email": email,
"name": _userName,
"dog_name": _dogName,
"dog_type": _dogType,
"dog_gender": _dogGender,
"is_neutered": true,
"dog_age": _dogAge,
},
);
뭘까 싶어서 컨트롤러에서 SignupRequestDto의 값을 찍어봤는데, 이럴 수가!! (ㅎㅎ..내가 봐도 오글거리는 말투다)
isNeutered필드 값이 false가 찍히고 있었다!
단순히 이 현상만 보자면 프론트에서는 true를 넘겼는데 백엔드에서 받고 보니까 false다..? 중간에 해커가 탈취해서 조작했나..? 오만가지 생각이 다 들었다.
하지만 구글링을 통해 비교적 쉽게 문제를 해결할 수 있었다. 바로 dto에서 boolean이 아니라 Boolean으로 타입을 변경해주는 것을 통해서!
일단 원인부터 정리하자면 다음과 같았다
- 롬복(Lombok)은 @Getter나 @Setter를 붙이면 필드들에 대해 get뭐시기, set뭐시기같은 게터 & 세터 메소드들을 만들어준다.
- 근데 만약 필드가 boolean타입이면서 is로 시작하는 필드라면(ex: isExpired), getIsExpired나 setIsExpired같은 이름으로 메서드를 만들어주는게 아니라 isExpired, setExpired란 이름의 게터와 세터를 만들어준다
결국 내가 SignupRequestDto에 작성한 isNeutered라는 필드에 대한 게터가 getIsNeutered가 아닌 isNeutered로 작성된 것. 근데 이게 왜 문제가 되는가? 바로 컨트롤러에 있는 @RequestBody에서 문제가 된다.
@PostMapping("/signup")
public BaseResponse<JwtResponseDto> signup(@RequestParam("provider") String oauth2ProviderName,
@RequestBody SignupRequestDto signupRequestDto) {
Oauth2Provider oauth2Provider =
Oauth2Provider.getOauth2ProviderByName(oauth2ProviderName);
// ... 생략
@RequestBody가 어떻게 동작하길래 dto의 게터가 isNeutered인게 문제가 되는 걸까?
알다시피 이 놈은 client가 보낸 request의 json데이터를 Dto에 매핑해주는 역할(역직렬화)을 한다.
MappingJackson2HttpMessageConverter가 내부적으로 ObjectMapper를 활용해서 값을 dto에 바인딩해주는데, 이 때 dto에 게터들이 있어야 한다. 즉 get뭐시기라는 형식의 이름을 가진 메서드들이 있어야 하는데, 롬복선생께서 isNeutered의 게터를 getIsNeutered가 아니라 isNeutered로 만들어줘서 문제가 생기는 것. (적절한 게터를 찾게 되지 못하는 것이기 때문) 이때 boolean타입의 기본값은 false기 때문에 자동으로 false가 들어가져있던 거다.
즉 이를 해결하기 위해선 이리 볶든 저리 볶든 Dto클래스에 getIsNeutered라는 게터가 있으면 되는거다.
물론 내가 직접 게터를 dto클래스에 만들어주면 되나! 필드의 타입을 boolean이 아닌 Boolean으로 작성(즉 래퍼클래스를 쓰는 형태로 작성)하면, is라는 접두사로 시작하는 필드여도 get뭐시기 라는 형태로 만들어진다고 한다.
@Getter
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class SignupRequestDto {
private String email;
private String name;
private String dogName;
private String dogType;
private String dogGender;
private Boolean isNeutered;
private int dogAge;
// ... 생략
✅ 참고한 글들
https://sedangdang.tistory.com/305
'WEB > Spring' 카테고리의 다른 글
SQL Mapper VS ORM (MyBatis vs JPA) (1) | 2024.02.28 |
---|---|
[Spring Security] 스프링 시큐리티의 인증/인가 과정(6.1.2버전 기준, with 공식문서) (0) | 2023.08.21 |
@Value 어노테이션 멕인 값이 null로 나올 때 (0) | 2023.02.21 |
Lombok 라이브러리 살펴보기 (0) | 2023.01.25 |
스프링 DB접근기술에 대한 이야기 (0) | 2023.01.22 |