Web/Spring

[JPA] 댓글 수정하기 기능 - @RequestBody, @ModelAttribute (2)

HYJJ 2022. 4. 22. 21:13

지난 번 글(https://begintoend.tistory.com/50)에서 이어진다.

 

댓글을 작성하는 부분을 완료했으니 댓글을 수정하는 Service 부분을 수정했으니 이제 전송하는 부분을 만질 차례다. 그냥 url을 달아버려도 상관없지만 알림창을 띄우고 싶었기 때문에 JavaScript를 활용하여 작성하였다.

 

작성한 코드는 아래와 같다.

function putComments(id, postId) {
            let putUrl = "/comments/"+id+"/put";
            var content = document.getElementsByName("content")[0].value;
            // content를 잘 가지고 오는지를 확인
            console.log(content);

            fetch(putUrl, {
                method : "PUT",
                headers: {
                    "Content-Type" : "application/json",
                },
                body : JSON.stringify({
                    id: id,
                    postId : postId,
                    content : content
                }),
            })
                .then((response) => response.json())
                .then(
                    alert("댓글을 수정했습니다.");
                );
    }

 

fetch에 url을 넣고, method에 put을 넣은 후, JSON 형태로 보낸 후 완료가 된다면 alert 창을 띄우는 식이다. jQuery의 Ajax만 써보고, fetch는 거의 안써봐서 문법을 몰랐고 공식 문서를 보면서 작성해봤다. 

https://developer.mozilla.org/ko/docs/Web/API/Fetch_API/Using_Fetch

 

Fetch 사용하기 - Web API | MDN

Fetch API는 HTTP 파이프라인을 구성하는 요청과 응답 등의 요소를 JavaScript에서 접근하고 조작할 수 있는 인터페이스를 제공합니다. Fetch API가 제공하는 전역 fetch() (en-US) 메서드로 네트워크의 리소

developer.mozilla.org

 

mode나 cache 이런 부분은 반드시 필요한 부분이 아니라 안썼고, header에서 어떤 타입으로 전송하는지를 명시해줘야 하기 때문에 header에 application-json을 붙였고 body에 필요한 부분을 써서 전송하는 방식으로 작성하였다. 

 

처음에는 @RequestParam을 통해 데이터를 가져온다고 단순하게 생각을 했는데 다시 한번  생각을 해보니 putMapping 이기에 body로 데이터가 전송된다. 그래서 @RequestParam은 주소 뒤에 ?key=value 값으로 붙는 걸 가져올 수 있기에 @RequestParam을 통해 가져오는 건 적절하지 않다. 

 

확인을 해봤을 때 컨솔에서는 데이터가 잘 넘어왔고, 전송은 잘 되었지만 컨트롤러 @PutMapping 부분에서 데이터를 받을 때 문제가 생긴 듯 하다. 데이터가 전달 되었을 때 가령 

 body : JSON.stringify({
                    id: id,
                    postId : postId,
                    content : content
                }),

여기 부분에서 "댓글 111" 을  전달해서 넣었다면 컬럼에 있는 데이터에 "댓글111" 이렇게 들어간 게 아니라 {"content" : "댓글111"} 이렇게 넣어진 것을 확인하게 되었다. 아마 데이터를 전송할 때는 문제가 안생겼지만 컨트롤러에서 데이터를 받아서 update문을 넣을 때 문제가 생긴 것 같다.

 

일단 작성했던 코드는 아래와 같다.

@PutMapping("/comments/{id}/put")
public String putComment(@PathVariable String id, @RequestBody String content) {
    consumerService.updateComment(id, comment);
    return "redirect:/community";
}

@RequestBody에 대해 잘 모른 거 같고, 생각해보니 이전에 팀원이 작성한 코드를 살펴봤을 때 이미지 업로드를 하는 기능에서@ModelAttribute를 활용한 것도 기억이 났다. 이를 토대로 둘 중에서 적합한 기능을 쓰며, 두 개 각각의 특성을 잘 이해하면 되지 않을까 하는 생각이 들었다. (@RequestBody를 통해 가져오거나 @ModelAttribute를 사용하여 Lombok의 getter setter를 통해 데이터를 get 혹은 set 하면 될거 같다는 생각이 들었다.)

 

일단 먼저 @RequestBody에 대한 내용을 보면 아래와 같았다. 

https://www.baeldung.com/spring-request-response-body 

@RequestBody와 @ResponseBody에서 구체적인 차이가 있는지 잠깐 궁금했는데 이에 대한 내용도 같이 나와있었다. 일단 나중에 읽어보고 @RequestBody란 이렇게 정의 내려 있었다. 

Simply put, the @RequestBody annotation maps the HttpRequest body to a transfer or domain object, enabling automatic deserialization of the inbound HttpRequest body onto a Java object.

@RequestBody는 HttpRequest body를 도메인 객체에 매핑해주는데 자동적으로 HttpRequest body를 자바 객체로 역직렬화(deserialization) 해준다고 한다. 역직렬화란 단어를 처음 들어봐서 검색을 해보니 body로 전송된 JSON 타입의 데이터를 자바의 객체로 바꾸는 걸 말하는 거 같았다. (아닐 수도 있음.)

 

예시에서도 살펴볼 수 있었지만 @RequestBody에 보통 데이터를 말하는 필드를 포함하는 객체를 호출하고 있음을 살펴볼 수 있었다. 여기서 예시는 postMapping이지만 어떻게 데이터를 저장하는지 살펴보기 위해서는 예제가 필요한 듯 하다. 

예시는 로그인 인듯 하며 LoginForm을 통해 전달 받는 것을 확인할 수 있었다.

한번 정리를 해본다면 json 형태 {key : value, key : value ...} 로 저장된 데이터를 전송 

-> ResponseBody로 받는데 이 때 옆에 쓴 LoginForm이 받는 데이터의 형식을 정해주는 거 같았다.

 

 

다시 이전에 썼던 코드로 돌아가서 문제점을 찾는다면 

@PutMapping("/comments/{id}/put")
public String putComment(@PathVariable String id, @RequestBody String content) {
    consumerService.updateComment(id, comment);
    return "redirect:/community";
}

@RequestBody 부분에 content를 String으로 받는 걸로 지정했기에 JSON 객체가 String으로 받아져서 {"content" : "댓글111"} 이렇게 그대로 저장된 거 같았다.

문제를 정확하게 알았으니 객체를 지정해서 다시 받아 보도록 하겠다.   

 @PutMapping("/comments/{id}/put")
public String putComment(@PathVariable String id, @RequestBody CommentForm commentForm, Model model) {
    consumerService.updateComment(commentForm.getId(), commentForm.getContent());
    model.addAttribute("ACCESS", "SUCCESS");
    return "redirect:/community/"+commentForm.getPostId();
}

 

일단 return 쪽은 오류가 났으니까 무시하고, @RequestBody를 요청할 때 양식을 따로 클래스로 만들어서 지정하였고 다음과 같았다.

package com.house.start.controller.form;


import lombok.Getter;
import lombok.Setter;

@Getter @Setter
public class CommentForm {

    private Long id;
    private String content;
    private Long postId;

}

적고나서 검색을 다시 해보니 @RequestBody에는 getter setter가 따로 필요 없다고 한다. 알아서 찾아서 넣어주기에 @ModelAttribute에서 필요했던 Lombok 설정이 필요 없다고 한다. 나중에 @ModelAttribute를 사용할 때 참고하고 일단 넣어서 잘 넘어가는지 확인해보자. 

 

 디자인은 아직 안 입혀서 다음과 같고 콘솔에서 확인했을 때 잘 넘어간 것을 확인할 수 있었다. 

일단 잘 넘어간 걸 확인 했고 이제 수정이 완료 된 후에 return 된 부분을 수정하도록 하고, Update에서 필요한 DynamicUpdate 어노테이션에 대해서는 내일 찾아보도록 하겠다.