CommentDto를 만들어주자
package com.cos.photogramstart.web.dto.comment;
import lombok.Data;
@Data
public class CommentDto {
private String content;
private int imageId;
//toEntity가 필요없다
}
-> 매우 간단하다.
toEntity()를 만들어 줄 필요도 없기 때문이다. 왜 Comment에는 toEntity가 필요없을까?
DTO를 만들었으니 Controller에서 받아주도록 하자
이 DTO는 JSON타입이므로 매개변수에 바로 받을수가 없다.
key=value 타입만 받을 수 있기 때문이다.
이를 해결하기 위해 @RequestBody 어노테이션을 붙혀주면 된다.
-> 응답도 잘 되고 있다.
'댓글쓰기 실패'가 되는 이유는 null을 return하고 있기 때문이다.
우선 로직을 완성하러 가자
'댓글쓰기 메서드'를 호출할 때, content,imageId, userId를 받아와야 한다.
package com.cos.photogramstart.web.api;
import com.cos.photogramstart.config.PrincipalDetails;
import com.cos.photogramstart.domain.comment.Comment;
import com.cos.photogramstart.service.CommentService;
import com.cos.photogramstart.web.dto.CMRespDTO;
import com.cos.photogramstart.web.dto.comment.CommentDto;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;
@RequiredArgsConstructor
@RestController
public class CommentApiController {
private final CommentService commentService;
@PostMapping("api/comment")
public ResponseEntity<?> commentSave(@RequestBody CommentDto commentDto,
@AuthenticationPrincipal PrincipalDetails principalDetails) {
Comment comment = commentService.댓글쓰기(commentDto.getContent(), commentDto.getImageId(), principalDetails.getUser().getId());
//content, imageId, userId
return null;
}
@DeleteMapping("/api/comment/{id}")
public ResponseEntity<?> commentDelete(@PathVariable int id) {
return null;
}
}
Service로 가서 똑같이 받아주자.
imageId와 userId를 받기 위해서는 Image, User 오브젝트를 받아야 하기 때문에 상당히 번거롭다.
따라서 '네이티브 쿼리'를 작성하여 데이터를 담는것이 훨씬 편리한 방법이다.
CommentRepository로 가서 comment를 INSERT할 쿼리를 작성해주자.
package com.cos.photogramstart.domain.comment;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
public interface CommentRepository extends JpaRepository<Comment, Integer> {
@Modifying
@Query(value = "INSERT INTO comment(content, imageId, userId, createDate) VALUES(:content,:imageId,:userId, new())", nativeQuery = true)
Comment mSave(String content, int imageId, int userId);
}
쿼리가 완성되었으니, Service로 가서 이 mSave메서드를 불러오면 된다.
package com.cos.photogramstart.service;
import com.cos.photogramstart.domain.Image.Image;
import com.cos.photogramstart.domain.comment.Comment;
import com.cos.photogramstart.domain.comment.CommentRepository;
import com.cos.photogramstart.domain.user.User;
import com.cos.photogramstart.domain.user.UserRepository;
import com.cos.photogramstart.handler.exception.CustomApiException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@RequiredArgsConstructor
@Service
public class CommentService {
private final CommentRepository commentRepository;
private final UserRepository userRepository;
@Transactional
public Comment 댓글쓰기(String content, int imageId, int userId) {
return commentRepository.mSave(content, imageId, userId);
}
Service까지 완료되었으니 Controlle로 가서 로직을 완성해주자.
package com.cos.photogramstart.web.api;
import com.cos.photogramstart.config.PrincipalDetails;
import com.cos.photogramstart.domain.comment.Comment;
import com.cos.photogramstart.service.CommentService;
import com.cos.photogramstart.web.dto.CMRespDTO;
import com.cos.photogramstart.web.dto.comment.CommentDto;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;
@RequiredArgsConstructor
@RestController
public class CommentApiController {
private final CommentService commentService;
@PostMapping("api/comment")
public ResponseEntity<?> commentSave(@RequestBody CommentDto commentDto,
@AuthenticationPrincipal PrincipalDetails principalDetails) {
Comment comment = commentService.댓글쓰기(commentDto.getContent(), commentDto.getImageId(), principalDetails.getUser().getId());
//content, imageId, userId
return new ResponseEntity<>(new CMRespDTO<>(1, "댓글쓰기 성공", comment), HttpStatus.CREATED);
}
@DeleteMapping("/api/comment/{id}")
public ResponseEntity<?> commentDelete(@PathVariable int id) {
return null;
}
}
'댓글쓰기 로직'이 완료되었다.
DB에 제대로 INSERT되는지 확인해보자
-> 에러 내용을 보면 Repository에서 만든 mSave쿼리의 타입을 Comment 타입으로 받을 수 없고 void,int,Integer로만 받을 수 있다고 한다.
즉, INSERT가 성공하면 1, 아니라면 0 이런식으로 return시킨다는 말이다.
그런데 이렇게 int나 Integer로 return이 되면 Service에 comment 오브젝트로 받을 수가 없다.
즉, comment를 응답해줄 수 없다는 말인데, 이는 곧 primaryKey를 알 수 없는 문제가 발생한다
comment의 PK를 모른다면 해당 댓글을 찾아서 수정하거나 삭제할 수가 없어진다.
'네이티브 쿼리'의 사용이 불가능한것을 확인하였다.
그럼 JpaRepository가 가지고 있는 save함수를 사용할 수 밖에 없다.
Service의 '댓글쓰기 메서드'로 돌아가서 Comment객체를 만들어주어야 한다.
이 comment객체에 content는 그냥 넣을 수 있는데, image와 user는 객체로써 담을수가 없다.
따라서 imageId, userId만 가지고 있는 '가짜 객체'를 만들어서 적용시켜주는 방법을 사용할 것이다.
어차피 comment테이블에는 이 id값만 들어가면 되기 때문이다.
image객체와 user객체에도 다른 값은 null이고, id값만 들어가있는것을 확인할 수 있다.
여기서 문제는 댓글을 작성할 때, username도 같이 뿌려주어야 하는데 이게 null값이다.
따라서 image처럼 id만 필요한 것이 아니기 때문에 해당 로직으로는 불완전하다.
user오브젝트의 데이터는 userRepository를 통해서 받아야 한다.
package com.cos.photogramstart.service;
import com.cos.photogramstart.domain.Image.Image;
import com.cos.photogramstart.domain.comment.Comment;
import com.cos.photogramstart.domain.comment.CommentRepository;
import com.cos.photogramstart.domain.user.User;
import com.cos.photogramstart.domain.user.UserRepository;
import com.cos.photogramstart.handler.exception.CustomApiException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@RequiredArgsConstructor
@Service
public class CommentService {
private final CommentRepository commentRepository;
private final UserRepository userRepository;
@Transactional
public Comment 댓글쓰기(String content, int imageId, int userId) {
//Tip(객체를 만들 때 id값만 담아서 INSERT할 수 있다.) - ID만 가지고 있는 '가짜 객체' 생성
//대신 return 시에 image객체와 user객체는 id값만 가지고 있는 빈 객체를 return받는다.
Image image = new Image();
image.setId(imageId);
User userEntity = userRepository.findById(userId).orElseThrow(() -> {
throw new CustomApiException("유저 아이디를 찾을 수 없습니다");
});
Comment comment = new Comment();
comment.setContent(content);
comment.setImage(image);
comment.setUser(userEntity);
return commentRepository.save(comment);
}
@Transactional
public void 댓글삭제() {
}
}
-> image는 id만, user는 가진 데이터 모두를 들고왔다.
따라서 이제 username을 View에 응답해 줄 수 있게 되었다.
'메타코딩 SNS프로젝트' 카테고리의 다른 글
스프링부트 개념정리 with JPA 1강 - 스프링의 핵심은 무엇인가요? (0) | 2022.07.12 |
---|---|
57. chapter 10 - 댓글 - View 렌더링하기 (0) | 2022.07.11 |
55. chapter 10. 댓글 - Comment API 생성하기, Ajax 작성하기 (컨트롤러, 서비스 만들기), 댓글쓰기 Ajax 함수 만들기 (0) | 2022.07.09 |
54. chapter 10. 댓글 - Comment 모델링하기 (0) | 2022.07.09 |
51. chapter 9. 기타 기능 구현 - 인기 페이지 구현 완료 (0) | 2022.07.09 |