컨트롤러에서 post api 요청시 request body에 파라미터를 담아서 보냈는데 null값이 db에 저장되는 이슈가 발생했다.
문제 코드 )
//controller
@PostMapping("/join")
public void addMember(@Valid @RequestBody MemberRequest memberRequest){
memberService.addMember(memberRequest);
}
//service
public void addMember(MemberRequest memberRequest){
Member member = memberRequestMapper.toEntity(memberRequest);
memberRepository.save(member);
}
//repository
public interface MemberRepository extends JpaRepository<Member, Long> {
}
//entity
@Entity
@Getter
@NoArgsConstructor
@DynamicInsert
@DynamicUpdate
@EntityListeners(AuditingEntityListener.class)
public class Member extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
post 요청의 경우 request body에 주로 데이터를 담아서 보내므로 @RequestBody를 사용했다. 그런데 @ModelAttribute를 사용하는 경우도 있어 혹시 어노테이션이 잘못되었는지 확인해봤다.
step1과 연계된 사항인데 클라이언트에서 데이터를 담아서 보내는 형식이 api에서 받는 형식과 일치해야하므로 @RequestBody 어노테이션을 사용한 경우 json 형식으로 데이터를 보냈는지 확인해봤다. 만약 클라이언트에서 form-data형식으로 보낸다면 @modelattribute 어노테이션을 사용하면 된다.
포스트맨을 사용하는 경우 form-data로 보내려면 body 탭의 form-data를 선택하고 key와 value 값을 입력하면 된다. json 형식으로 body에 담아서 보낼 경우는 body 탭의 raw를 선택 후 JSON을 선택하고 json형식으로 보내면 된다.
1.form-data에 담아서 보내기
- json 형식으로 보내기
service에서 mapper를 이용해서 엔티티로 변환하는 과정을 통해 memberRequest를 Member타입으로 매핑해주고 있었는데 이 부분을 breakpoint를 걸어서 확인해본 결과 memberRequest에서는 파라미터가 잘 전달되어 있었는데 매핑 후에 null로 처리되어 저장되는 것을 확인했다.
엔티티로 매핑하는 과정에서 request에 담긴 value값들이 Null로 변환되어 쿼리가 날아갔으므로 엔티티의 생성자를 확인했다.
클래스 레벨에서 @NoArgsConstructor 어노테이션을 활용해서 기본 생성자를 만들어 주고 있었는데 문제는 기본 생성자만 존재하고 Member 클래스의 필드들을 파라미터로 가지고 있는 생성자가 없었다는 것이다! 엔티티에는 @Setter 어노테이션을 사용하지 않고 있었으므로 name 필드를 가진 생성자가 없어서 request에서는 name에 test라는 올바른 value값을 넘겨도 기본 생성자를 활용해서 null만 있는 객체가 만들어졌기 때문에 발생한 문제였다.
따라서 @Builder를 활용해서 name을 매개변수로 갖는 생성자를 만들어주었고 이 생성자를 활용해서 매핑을 하도록 했다.
해결된 코드)
//entity
@Entity
@Getter
@NoArgsConstructor
@DynamicInsert
@DynamicUpdate
@EntityListeners(AuditingEntityListener.class)
public class Member extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@Builder
public Member(String name){
this.name = name;
}
}