@Builder의 중요성

YJS·2023년 10월 26일
0

Spring Boot 탐구

목록 보기
7/11

😖문제 상황

컨트롤러에서 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;
    
}


🤓문제 해결 과정

step1. MemberRequest의 어노테이션을 확인한다.

post 요청의 경우 request body에 주로 데이터를 담아서 보내므로 @RequestBody를 사용했다. 그런데 @ModelAttribute를 사용하는 경우도 있어 혹시 어노테이션이 잘못되었는지 확인해봤다.

step2. MemberRequest를 클라이언트에서 어떤 형식으로 보냈는지 확인한다.

step1과 연계된 사항인데 클라이언트에서 데이터를 담아서 보내는 형식이 api에서 받는 형식과 일치해야하므로 @RequestBody 어노테이션을 사용한 경우 json 형식으로 데이터를 보냈는지 확인해봤다. 만약 클라이언트에서 form-data형식으로 보낸다면 @modelattribute 어노테이션을 사용하면 된다.

포스트맨을 사용하는 경우 form-data로 보내려면 body 탭의 form-data를 선택하고 key와 value 값을 입력하면 된다. json 형식으로 body에 담아서 보낼 경우는 body 탭의 raw를 선택 후 JSON을 선택하고 json형식으로 보내면 된다.

1.form-data에 담아서 보내기

  1. json 형식으로 보내기

step3. 서비스 단에서 엔티티로 매핑 시 null 값이 들어갔는지 확인한다.

service에서 mapper를 이용해서 엔티티로 변환하는 과정을 통해 memberRequest를 Member타입으로 매핑해주고 있었는데 이 부분을 breakpoint를 걸어서 확인해본 결과 memberRequest에서는 파라미터가 잘 전달되어 있었는데 매핑 후에 null로 처리되어 저장되는 것을 확인했다.

step4. Member 클래스의 엔티티를 확인한다.

엔티티로 매핑하는 과정에서 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;
    }
}

profile
우당탕탕 개발 일기

0개의 댓글