서버 개발 발 들이기 4

바람찬허파·2023년 9월 7일
0

지난 시간, API 제작과 sql을 통한 DB 접근에 자신이 생긴 나는 2가지 API를 제작해보았다.

  • PUT /user
    변경하고자 하는 유저의 이름과 id를 받고 -> 이름을 변경
  • DELTE /user
    삭제하고자 하는 유저의 id를 받고 -> 유저를 db에서 삭제

지난 시간의 연장선으로서 어렵지 않은 내용이기에, 혼자서 코드를 작성해보았지만..
이상하게도 계속해서 500 서버 내부 오류가 발생하였다 :-(

채 5줄도 되지 않는 코드에, 어떤 문제가 있는지 확인해보았지만 강사님의 코드와 다를 것이 없었다. 심지어 어제까지는 동작했던 user 생성 코드도 동일한 에러가 발생하였다.

문제

//class UserController

@PutMapping("/user")
    public void updateName(@RequestBody UpdateRequest updateRequest) {

        String sql = "select * from user where id = ?";
        boolean isUserExist = jdbcTemplate.query(sql, (rs, rowNum) -> 0, updateRequest.getId()).isEmpty();

        if (updateRequest.getName() == "" || isUserExist) {
            throw new IllegalArgumentException();
        }
        String updateSql = "update user set name = ? where id = ?";
        jdbcTemplate.update(updateSql, updateRequest.getName(), updateRequest.getId());
    }
package com.group.libraryapp.dto.user.request;

public class UpdateRequest {
    private final Long id;
    private final String name;

    public UpdateRequest(Long id, String name) {
        this.id = id;
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }
}

깃헙에 올려두었던 정상 작동 코드와 비교한 결과, 문제는 UpdateRequest 클래스의 생성자였다.

해결책

도저히 모르겠어서 chatGPT에게 물어본 결과, 답변은 다음과 같다.

그러나 Spring Boot는 객체를 생성하고 초기화하기 위해 기본 생성자 (parameterless constructor)를 필요로 합니다. 이렇게 하는 이유는 다음과 같습니다:
객체 생성: Spring Boot는 JSON 데이터를 객체로 변환하기 위해 해당 객체를 생성해야 합니다. 생성자가 없는 경우 객체를 생성하는 데 어떤 매개변수를 사용해야 하는지 알 수 없습니다.
객체 초기화: 생성된 객체를 본문에서 받은 데이터로 초기화해야 합니다. 이를 위해 객체의 필드를 설정합니다.
그러므로 Spring Boot는 @RequestBody로 받을 클래스에 기본 생성자가 필요합니다. 이렇게 하면 객체를 생성하고 필드를 초기화할 수 있습니다.

Spring Boot의 @RequestBody 어노테이션은 객체를 생성한 후 setter 메서드를 사용하여 필드 값을 설정합니다. 따라서 필드를 final로 선언하면 이러한 setter 메서드가 사용할 수 없으므로 HTTP 요청 본문의 데이터를 객체로 변환하는 데 문제가 발생할 것입니다.

Spring Boot에서

@PutMapping("/user")
    public void updateName(@RequestBody UpdateRequest updateRequest) {
    ...
    

@RequestBody는 UpdateRequest 객체를 생성 => setter 메소드로 id, name 필드를 초기화 한다. 사용자가 "iffy", 3의 입력을 주어도, 우선은 UpdateRequest라는 객체를 생성한 뒤, 그 객체의 필드를 name = "iffy"; id = 3 이라고 초기화하는 것이다.

때문에 객체를 생성하는 기본 생성자가 반드시 필요하고, 기존 내 코드에서는 객체 생성이 불가능 했던 것이다.

    public UpdateRequest(Long id, String name) {
        this.id = id;
        this.name = name;
    }

기존 코드에서 수정해야 할 부분은 다음과 같다.

  • 필드를 final로 지정하지 않는다.
  • 기본 생성자를 둔다. (여기서는 기존의 생성자를 삭제한다)

수정 코드

package com.group.libraryapp.dto.user.request;

public class UpdateRequest {
    private  Long id;
    private  String name;

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }
}

추가 사항

SpringBoot가 객체를 생성 후 초기화 하기 위해, 클래스에 setter 메소드를 작성해야 하는가?

pring Boot에서 @RequestBody를 사용하여 HTTP 요청의 본문을 객체로 변환할 때, 필드를 설정하기 위해 setter 메서드를 추가할 필요는 없습니다. Spring Boot는 setter 메서드 없이도 필드 값을 설정할 수 있습니다.
클래스의 필드를 final로 선언하지 않고, 기본 생성자를 제공하면 Spring Boot가 필드를 초기화하기 위해 리플렉션을 사용합니다. 이를 통해 객체를 생성하고 필드 값을 직접 설정할 수 있습니다.

0개의 댓글