BE/스프링 부트 3

15장 댓글 컨트롤러와 서비스 만들기

이제하네 2023. 12. 26. 10:25

댓글 REST API의 개요

  • 댓글 REST 컨트롤러 , 서비스, DTO를 만들어 CURD를 위한 REST API 완성
    • REST 컨트롤러 : 댓글 REST API를 위한 컨트롤러로 서비스와 협업, 클라이언트 요총을 받아 응답하며 뷰(view)가 아닌 데이터 반환
    • 서비스: REST 컨트롤러와 리파지터리 사이에서 비즈니스 로직, 즉 처리 흐름을 담당하며 예외 상황이 발생했을 때 @Transactional로 변경된 데이터 롤백
    • DTO : 사용자에게 보여 줄 댓글 정보를 담은 것, 단순히 클라이언트와 서버 간에 댓글 JSON 데이터 전송
  • 댓글 CRUD에 필요한 REST API 주소가 필요함
    • GET : 댓글 조회
    • POST : 댓글 생성
    • PATCH : 댓글 수정 
    • DELETE : 댓글 삭제 

 

댓글 컨트롤러와 서비스 틀 만들기

  1. 프로젝트의 api 패키지에서 CommentApiController.java 파일을 생성
  2. 컨트롤러의 기본 틀이 생성되면 다음과 같이 설정
    1. 이 클래스를 REST 컨트롤러로 선언하기위해 @RestController 작성
    2. 컨트롤러가 서비스와 협업할수 있도록 commentService 객체를 주입 
  3. 프로젝트의 service 패키지에서  CommentService.java 파일을 생성
  4. 서비스의 기본 틀이 생성되면 다음과 같이 설정
    1. 이 클래스를 서비스로 선언하기위해 @Service 작성
    2. 서비스와 함께 협업할 댓글 리파지터리(commentRepository)와 게시글 리파지터리(articleRepository) 객체를 주입
      • 댓글 리파지터리 뿐만 아니라 게시글 리파지터리까지 주입하는 이유는 게시글 리파지터리가 있어야 댓글을 생성할 때 대상 게시글의 존재 여부를 파악할 수 있음
    3.  
CommentService.java 

@Service // 서비스 선언
public class CommentService {
	
	@Autowired
	private CommentRepository commentRepository; // 댓글 리파지터리 객체 주입
	
	@Autowired
	private ArticleRepository articleRepository; // 게시글 리파지터리 객체 주입
}

==========================================================
CommentApiController.java

@RestController //REST 컨트롤러 선언
public class CommentApiController {
	
	@Autowired
	private CommentService commentService; // 댓글 서비스 객체 주입
    // 1.댓글 조회
    // 2.댓글 수정
    // 3.댓글 수정
    // 4.댓글 삭제
}

 

댓글 조회하기

  • 요청을 받아 응답할 컨트롤러 만들기
    1. 컨트롤러(CommentApiController)에서 댓글 조회 요청을 받고 처리하는 메서드 생성
      1. @GetMapping()으로 댓글 조회 요청 받고 요청 URL인 /api/articles/{articleId}/comments작성
      2. 메서드의 이름은 comments()하고 매개변수는 몇번 게시글을 조회하는지 알아야 하므로 @GetMapping의 articleId를 받아오고 메서드의 반환형은 ResponseEntity<List<CommentDto>> 로 정의
        • 반환형이 ResponseEntity<List<CommentDto>> 인 이유는 DB에서 조회한 댓글 엔티티 목록은 List<Comment>이지만 엔티티를 DTO로 변환하면 List<CommentDto>가 되고 응답 코드와 함께 보내기위해 ResponseEntity 클래스를 활용하면 이 메서드의 최종 반환형은 ResponseEntity<List<CommentDto>>이 됨
      3. 메서드의 실행 결과는 아직 아무것도 없으므로 일단 null을 반환
    2. 댓글 컨트롤러는 댓글 서비스에 조회 작업을 위임하고 얻은 결과를 클라이언트에 응답
    3. 서비스에 댓글 조회를 위임하기 위해 CommentService의 comments(articleId) 메서드를 호출하고 메서드의 실행 결과로 받환받은 값은 List<CommentDto>(댓글 DTO의 묶음) 타입의 dtos라는 변수에 저장
      • comments() 메서드의 전달값으로 articleId를 넘긴 이유는 해당 게시글의 id를 알아야 해당 게시글의 댓글을 가져올수 있기 때문에 
    4. 전에 REST 컨트롤러를 만들 때 메서드의 반환값을 성공하는 경우와 실패하는 경우로 나눠 삼항 연산자로 작성했으나 이번에는 예외 처리 방식으로 코드를 작성하여 서비스에서 반환받은 dtos를  ResponseEntity에 실어 보내므로 return 문에 null을 지우고 ResponseEntity의 상태(status)에는 OK, 본문(body)에는 조회한 댓글 목록인 dtos을 실어보냄
      • 예외 처리(exception handling) : 예기치 못한 상황이 발생할 때를 대비해 대처하는 코드를 미리 작성하는 것
      • 여기서는 댓글 조회에 실패할 경우 스프링 부트가 예외 처리를 한다고 가정해 댓글 조회에 성공하는 경우만 응답으로 보냄 
    5. 프로젝트 dto 패키지에 CommentDto.java 파일을 생성
    6. CommentDto에 필요한 기능을 롬복으로 설정
    7. CommentDto는 Comment(댓글 엔티티)를 담을 그릇이기에 Comment의 구조와 같이 id(댓글의 id), articleId(댓글의 부모id), nickname(댓글 작성자), body(댓글 본문)를 필드로 선언
  • 요청을 처리할 서비스 만들기
    1. 컨트롤러가 댓글 조회 요청을 받고 그 결과를 반환한다면 서비스는 실제 댓글을 조회
    2. CommentService.java 파일안에 comments() 메서드 추가하고 동작 순서를 주석으로 작성
      1. 댓글 조회
      2. 엔티티 -> DTO 변환
      3. 결과 반환
    3. 서비스가 DB에서 데이터를 가져올 때 리파지터리에서 하기 때문에 CommnetRepository의 findByArticleId(articleId) 메서드 호출하여 articleId번 게시글의 모든 댓글을 가져오고 반환값은 List<Comment> 타입의 comments에 저장
      • 반환값이 Comment 타입이 아니라 List<Comment>타입인 이유는 한 게시글에 여러 댓글이 있을수 있어서
    4. 조회한 댓글 엔티티 목록을 DTO 목록으로 변환하기 위해 CommentDto를 저장하는 빈 ArrayList를 만들고 List<CommentDto> 타입의 dtos 변수에 저장
    5. 비어 있는 dtos에 DTO를 하나씩 담기
      1. 조회한 댓글 엔티티의 개수(comments.size())만큼 반복하도록 for() 문을 작성
      2. 조회한 댓글 엔티티 목록(comments)에서 엔티티를 하나씩 꺼내 c변수에 저장
      3. CommentDto의 createCommentDto(c) 메서드를 호출해 엔티티를 DTO로 변환하고 그결과를 dto 변수에저장
        • createCommentDto()는 댓글 엔티티를 입력받아 CommentDto 객체를 반환하는 메서드
      4. dtos 리스트에 add() 메서드를 호출해 dto를 추가 
        • for()문이 수행되면 모든 댓글 엔티티가 DTO로 변환되고, 변환된 DTO가 dtos에 저장
    6. 마지막으로 dtos를 반환하면 컨트롤러가 이를 받아서 클라이언트에 응답
    7. CommentDto.java 파일 아래에  createCommentDto() 메서드 생성 
    8. createCommentDto() 메서드 앞에 public static 작성 후 매개변수를 Comment타입의 comment로 변경
      • 정적 메서드(static method) : 클래스로 객체를 생성하지 않고 호출이 가능한 메서드 
    9. 메서드의 반환값이 댓글 DTO가 되도록 CommentDto의 생성자 호출 
      1. 댓글 DTO와 엔티티 차이가 거의 없으므로 매개변수로 받은 엔티티(comment) 의 getter() 메서드로 입력값을 가져옴 
      2. 댓글의 id , 댓글이 속한 부모 게시글(Aaticle)의 id , 댓글의 nickname, 댓글의 body를 차례로 생성자에 입력
      3.  
CommentApiController.java

// 1.댓글 조회
	@GetMapping("/api/articles/{articleId}/comments") //댓글 조회 요청 접수
	public ResponseEntity<List<CommentDto>> comments(@PathVariable Long articleId){ //메서드 생성
		// 서비스의 위임
		List<CommentDto> dtos = commentService.comments(articleId);
		// 결과 응답
		return ResponseEntity.status(HttpStatus.OK).body(dtos); //null 반환
		//return null; //null 반환
	}

=============================================================

CommentService.java

	public List<CommentDto> comments(Long articleId) {

		// 1. 댓글 조회 
        List<Comment> comments = commentRepository.findByArticleId(articleId); 
        // 2. 엔티티 -> DTO 변환
		List<CommentDto> dtos = new ArrayList<CommentDto>();

		for (int i = 0; i < comments.size(); i++) {
			Comment c = comments.get(i);
			CommentDto dto = CommentDto.createCommentDto(c);
			dtos.add(dto);
		}

		// 3. 결과 반환
		return dtos;
        
=====================================================
CommentDto.java

@AllArgsConstructor // 모든 필드를 매개변수로 갖는 생성자 자동 생성
@NoArgsConstructor // 매개변수가 아예 없는 기본 생성자 자동 생성
@Getter // 각 필드 값을 조회할 수 있는 getter 메서드 자동 생성
@ToString // 모든 필드를 출력할 수 있는 toString 메서드 자동 생성
public class CommentDto {
	
	private Long id; // 댓글의 id
	private Long articleId; // 댓글의 부모 id
	private String nickname; // 댓글 작성자
	private String body; // 댓글 본문
	

	public static CommentDto createCommentDto(Comment comment) {
	    return new CommentDto(
	            comment.getId(), // 댓글 엔티티의 id
	            comment.getArticle().getId(), // 댓글 엔티티가 속한 부모 게시글의 id
	            comment.getNickname(), // 댓글 엔티티의 nickname
	            comment.getBody() // 댓글 엔티티의 body
	    );
	}
}

서비스 코드 개선전 댓글 조회 성공

 

  • 결과 확인하기
    1. 프로젝트를 실행후 Talend API Tester 에서 메서드는 GET ,URL 은 http://localhost:8887/api/articles/4/comments을 입력하고 Send 버튼을 클릭하면 4번 게시글의 모든 댓글 조회가 가능
    2. 서비스 코드를 개선하기위해 for()문을 스트림(stream)문법으로 개선하기위해 서비스 코드에 1~2번 코드를 주석처리후 return 문에 원래 있던 dtos;를 지우고 commentRepository의 findByArticleId(articleId) 메서드를 호출
      • 스트림(stream) : 컬렉션, 배열등에 저장되어 있는 요소들을 하나씩 참조하며 반복적인 처리를 가능하게 하는 기능
      • 스트림의 특징
        1. 스트림은 원본 데이터를 변경하지 않음
        2. 스트림은 일회용이라서 한번 사용하면 닫혀서 재사용이 불가능
        3. 스트림은 작업을 내부 반복으로 처리
      • 스트림의 구조
        • 데이터소스객체집합.Stream생성().중개연산().최종연산()
    3. 가져온 댓글 엔티티 목록을 스트림으로 바꿈
    4. 스트리화한 댓글 엔티티 목록을 DTO로 변환하고 map() 메서드를 이용하여 스트림에서 요소를 꺼내 조건에 맞게 변환
      • .map(a -> b) : 이런 구조로 스트림의 각 요소(a)를 꺼내 b를 수행한 결과로 매핑
    5. 댓글 엔티티를 DTO로 변환하였으나 변환 타입이 잘못되었기 때문에 collect()메서드를 추가해 스트림 데이터를 원하는 자료형으로 변환
      • .collect(Collectors.tolist()) : 이런 구조로 스트림 데이터를 리스트 자료형으로 변환 
CommentService.java
    
   	public List<CommentDto> comments(Long articleId) {
    return commentRepository.findByArticleId(articleId) // 댓글 엔티티 목록 조회
				.stream() // 댓글 엔티티 목록을 스트림으로 변환
				.map(comment -> CommentDto.createCommentDto(comment)) // 엔티티를 DTO로 매핑
				.collect(Collectors.toList()); // 스트림을 리스트로 변환
    }

서비스 코드 개선후 댓글 조회 성공

 

댓글 생성하기

  • 요청을 받아 응답할 컨트롤러 만들기
    1. 컨트롤러로 가서 2번 주석에 댓글 생성 요청을 받고 이를 처리하는 메서드 생성
      1. @PostMapping("/api/articles/{articleId}/comments")로 댓글 생성 요청 받음
      2. 메서드의 이름은 create()하고 첫번째 매개변수에는 댓글이 달릴 부모의 게시글 id를 @PathVariable로 요청 URL의 articleId를 가져오고 두번째 매개변수는 생성할 댓글 정보를 HTTP 요청의 BODY로부터 JSON 데이터를 받아오므로 @RequestBody를 이용해 CommentDto dto를 받고 메서드의 반환형은 댓글 생성 결과와 응답 코드를 함께 보내기위해 ResponseEntity<CommentDto>로 작성
        • @RequestBody : HTTP 요청 본문에 실린 내용(JSON,XML,YAML)을 자바 객체로 변환
    2. 컨트롤러는 서비스에 댓글 생성 작업을 위임하고 결과만 받아서 클라이언트에 응답
    3. 댓글 생성을 서비스에 위임하기 위해 CommentService의 create(articleId , dto) 메서드를 호출하고 메서드의 반환값은 CommentDto 타입의 createDto 라는 변수로 저장
    4. 서비스가 댓글을 성공적으로 생성한다면 반환받은 결과를 클라이언트에 응답해야하기 때문에 return 문에 ResponseEntity의 상태(status)에는 OK, 본문(body)에는 생성한 댓글 데이터인 createdDto를 보냄
  • 요청을 처리할 서비스 만들기
    1. 서비스에 create()메서드 만들고 메서드의 동작을 주석으로 적어둠
      1. DB에서 부모 게시글을 조회해 가져오고 없을 경우 예외 발생시키키
      2. 부모 게시글의 새 댓글 엔티티 생성하기
      3. 생성된 엔티티를 DB에 저장하기
      4. DB에 저장한 엔티티를 DTO로 변환해 반환하기
    2. create() 메서드는 DB 내용을 바꾸기 때문에 실패할 경우 트랜잭션 처리를 해야하므로 메서드 위에 @Transactional을 추가해 문제가 발생했을때 롤백
    3. 생성하려는 댓글의 부모 게시글을 조회해 가져오고 없으면 예외 발생
      1. articleRepository의 findById(articleId) 메서드를 호출해 부모 게시글을 article 변수에 받아옴
      2. 만약 부모 게시글이 없다면 orElseThrow() 메서드로 예외를 발생시켜 이후 코드는 수행되지 않고 종료
        • orElseThrow() 메서드는 Optional 객체(존재할 수도 있지만 안 할 수도 있는 객체, 즉 null이 될 수도 있는 객체)에 값이 존재하면 그 값을 반환하고 값이 존재하지 않으면 전달값으로 보낸 예외를 발생시킴
        • 전달값으로 IllegalArgumentException 클래스를 사용하면 메서드가 잘못됐거나 부적합한 전달값을 보냈음을 나타냄
    4. 에러가 발생하지 않았으면 부모 게시글의 새 댓글 엔티티를 생성하는 코드 Comment의 생성메서드 createComment(dto,article)을 호출해 댓글 엔티티를 반환
    5. comment 엔티티를 잘 만들었다는가정해 생선된 엔티티를 DB에 저장하기위해 CommentRepository를 통해 comment엔티티를 DB에 넣거 그 결과를 Comment  타입의 created로 받아옴
    6. 문제가 발생하지 않으면 DB에 저장한 엔티티를 DTO로 변환해 반환하기 위해 return 문에 생성메서드 호출
    7. entity 패키지에 Comment.java 에 createComment()메서드 생성하고 엔티티를 생성할 수 없는 경우에는 예외를 발생시키고, 그렇지 않으면 엔티티를 생성한 후 반환하는 두가지 케이스로 나눠서 작성
    8. 예외 발생 코드도 2가지 나누어 작성
      1. dto에 id가 존재하는 경우(dto.getId != null) 는 IllegalArgumentException예외를 발생
        • 댓글을 생성하기 전부터 id 값이 있을 수는 없기 때문에 (id는 DB가 자동으로 생성)
      2. 게시글이 일치하지 않는 경우에도 예외를 발생
        • dto에서 가져온 부모게시글과 엔티티에서 가져온 부모게시글이 다르면 안되기 때문에
          (dto.getArticleId() != article.getId()) 이는 JSON 데이터와 URL 요청 정보가 다르다는 뜻
    9. 예외 상황이 발생하지 않았다면 엔티티를 만들어 반환하기위해 return문에서 댓글 생성자를 호출하고 전달값으로 필요한 요소인 id,nickname,body는 dto에서 가져오고 부모 게시글은 article 자체를 입력 
CommentApiController.java

	// 2.댓글 생성
	@PostMapping("/api/articles/{articleId}/comments") //댓글 생성 요청 접수
	public ResponseEntity<CommentDto> create(@PathVariable Long articleId,@RequestBody CommentDto dto){
		
		// 서비스에 위임
		CommentDto createDto = commentService.create(articleId,dto);
		// 결과 응답
		return ResponseEntity.status(HttpStatus.OK).body(createDto);
	}
    
=================================================================
CommentService.java

	@Transactional
	public CommentDto create(Long articleId, CommentDto dto) {
		
		// 1. 게시글 조회 및 예외 발생
		Article article = articleRepository.findById(articleId)  // 부모 게시글 가져오기
		        .orElseThrow(() -> new IllegalArgumentException("댓글 생성 실패! " +
		                "대상 게시글이 없습니다.")); // 없으면 에러 메시지 출력
		// 2. 댓글 엔티티 생성
		Comment comment = Comment.createComment(dto, article);
		// 3. 댓글 엔티티를 DB에 저장
		Comment created = commentRepository.save(comment);
		// 4. DTO로 변환해 저장
		return CommentDto.createCommentDto(created);
	}

=================================================================

entity/Comment.java

	public static Comment createComment(CommentDto dto, Article article) {
		
		// 예외 발생
		if (dto.getId() != null)
		    throw new IllegalArgumentException("댓글 생성 실패! 댓글의 id가 없어야 합니다.");
		if (dto.getArticleId() != article.getId())
		    throw new IllegalArgumentException("댓글 생성 실패! 게시글의 id가 잘못됐습니다.");
		
		// 엔티티 생성 및 반환
		return new Comment(
				dto.getId(),      // 댓글 아이디
				article,          // 부모 게시글
				dto.getNickname(),// 댓글 닉네임
				dto.getBody()     // 댓글 본문
				);
	}

댓글 생성 성공

 

댓글 수정하기

  • 요청을 받아 응답할 컨트롤러 만들기
    1. 컨트롤러에서 댓글 수정을 요청 받고 이를 처리할 메서드 생성
      1. @PatchMapping("/api/comments/{id}") 로 댓글 수정 요청 받음 , 여기서 id는 게시글이 아니라 댓글의 id임
      2. 메서드 이름은 update()로 작성후 매개변수로는 @PatchMapping에서 받아온 id값을 사용하고 @RequestBody를 통해 JSON 데이터를 dto로 받고 반환형 ResponseEntity<CommentDto>로 작성
    2. 댓글 컨트롤러는 댓글 서비스에 수정 작업을 위임하고 반환받은 결과를 클라이언트에 응답
    3. 댓글 수정 작업을 서비스에 위임하기 위해 CommentService의 update(id, dto) 메서드 호출하고 결과는 CommentDto 타입의 updateDto라는 변수로 받음
    4. 서비스 댓글을 성공적으로 수정했다면 반환받은 결과를 클라이언트에 응답해야하기 때문에 return 문에 null을 지우고 ResponseEntity의 상태에는 OK, 본문에는 수정한 댓글 데이터인 updateDto를 넣음
  • 요청을 처리할 서비스 만들기
    1. CommentService 파일안에 update() 메서드 추가하고 이 메서드는 DB 의 내용을 변경하므로 실패할 경우를 대비하여 데이터를 롤백 할수 있도록 @Transactional을 추가
    2. update() 메서드에 처리할 내용
      1. DB에 해당 댓글을 조회해 가져오고 없을 경우 예외 발생
        1. commentRepository.findById(id)를 호출해 해당 댓글을 Comment 타입의 target변수로 받아옴ㅁ
        2. 해당 댓글이 없으면 orElseThrow()메서드로 예외 발생
      2. 가져온 댓글 내용 수정하기
        1. 예외가 발생되지 않았으면 target.patch(dto) 메서드를 호출해 기존 댓글 엔티티에 수정 정보 추가
      3. 수정한 댓글을 DB에 갱신하기(수정 데이터로 덮어쓰기)
        1. commentRepository.save(target) 메서드를 호출해 DB 내용을 target으로 갱신하고 결과는 updated 변수에 저장
      4. DB에 반영된 데이터를 DTO로 변환해 컨트롤러로 반환하기
        1. return 문에서 CommentDto.createCommentDto(updated) 메서드를  호출하면 새로운 DTO를 생성해 반영
    3. Comment 엔티티에 patch()메서드 생성
    4. 엔티티를 수정할수 없는 경우에는 예외를 발생 시키고 문제가 없으면 내용을 수정
      1. 댓글 수정 요청시 URL에 있는 id 와 JSON 데이터의 id가 다른 경우 예외처리
      2. 예외가 발생하지 않았으면 댓글 수정
CommentApiController.java
	// 3.댓글 수정
	@PatchMapping("/api/comments/{id}") // 댓글 수정 요청 접수
	public ResponseEntity<CommentDto> update(@PathVariable Long id,@RequestBody CommentDto dto){
		//서비스에 위임
		CommentDto updateDto = commentService.update(id,dto);
		//결과 응답
		return ResponseEntity.status(HttpStatus.OK).body(updateDto);
	}
    
=========================================================================
CommentService.java
	@Transactional
	public CommentDto update(Long id, CommentDto dto) {
		// 1. 댓글 조회 및 예외 발생
		Comment target = commentRepository.findById(id) // 수정할 댓글 가져오기
				.orElseThrow(() -> new IllegalArgumentException("댓글 수정 실패 !" + "대상 댓글이 없습니다.")); //없으면 에러 메시지 출력
	    // 2. 댓글 수정
		target.patch(dto);
	    // 3. DB로 갱신
		Comment updated = commentRepository.save(target);
	    // 4. 댓글 엔티티를 DTO로 변환 및 반환
		return CommentDto.createCommentDto(updated);
	}
    
 =========================================================================
 Comment.java
 
 
	public void patch(CommentDto dto) {
		//예외 발생
		if (this.id != dto.getId())
			   throw new IllegalArgumentException("댓글 수정 실패! 잘못된 id가 입력됐습니다.");
		//객체 생성
		if (dto.getNickname() != null) // 수정할 닉네임 데이터가 있다면
		    this.nickname = dto.getNickname(); // 내용 반영
		if (dto.getBody() != null) // 수정할 본문 데이터가 있다면
		    this.body = dto.getBody(); // 내용 반영
	}

댓글 수정 성공

 

댓글 삭제하기

  • 요청을 받아 응답할 컨트롤러 만들기
    1. 컨트롤러에서 댓글 삭제를 요청 받고 이를 처리할 메서드 생성
      1. @DeleteMapping("/api/comments/{id}")로 댓글 삭제를 요청받음
      2. 메서드이름은 delete()로 작성후 매개변수는 몇번 댓글을 삭제하는지 알아야 하므로 @DeleteMapping에서 받아온 id작성 후 반환형은 ResponseEntity<CommentDto>로 작성
    2. 컨트롤러는 서비스에 삭제 작업을 위임하고 서비스가 반환한 결과를 클라이언트에 응답
    3. 댓글 삭제를 서비스에 위임하기 위해 CommentService 의 delete(id) 메서드를 호출하고 결과는 CommentDto 타입의 deleteDto라는 변수에 저장
    4. 서비스가 댓글ㄹ을 성공적으로 삭제한다면 반환 받은 결과를 클라이언트에 응답해야하므로 return 문에 ResponseEntity의 상태에는 OK, 본문에는 삭제한 댓글 데이터인 deletedDto를 실어 보냄
  • 요청을 처리할 서비스 만들기
    1. CommentService.java 에 delete() 메서드를 작성후 DB 내용을 변경하므로 실패할 경우를 대비해 @Transactional을 추가
    2. delete() 메서드에서 수행할 내용을 주석으로 적어 놓고 시작
      1. DB에서 해당 댓글을 조회해 가져오고 없을 경우 예외 발생시키기
        1. commentRepository.findById(id)를 호출해 해당 댓글을 Comment타입의 target 변수에 받아옴
        2. 만약 해당 댓글이 없다면 orElseThrow() 메서드로 예외 발생
      2. 가져온 댓글을 DB에서 삭제하기
        1. 예외가 발생하지 않았으면 commentRepository.delete(target)메서드를 호출해 DB에 해당 댓글을 삭제
      3. 삭제한 댓글을 DTO로 변환해 반환하기
        1. target 엔티티를 DTO로 변환해 반환. return문에서 CommentDto.createCommentDto(target) 메서드를 호출하면 새로운 DTO를 생성해 반환
    3.  
CommentApiController.java

	// 4.댓글 삭제
	@DeleteMapping("/api/comments/{id}") // 댓글 삭제 요청 접수
	public ResponseEntity<CommentDto> delete (@PathVariable Long id){
		//서비스에 위임
		CommentDto deleteDto = commentService.delete(id);
		//결과 응답
		return ResponseEntity.status(HttpStatus.OK).body(deleteDto);
	}

=========================================================================
CommentService.java

	@Transactional
	public CommentDto delete(Long id) {
		// 1.댓글 조회 및 예외 발생
		Comment target = commentRepository.findById(id)
				.orElseThrow(() -> new IllegalArgumentException("댓글 삭제 실패!" + "대상이 없습니다."));
		// 2.댓글 삭제
		commentRepository.delete(target);
		// 3.삭제 댓글을 DTO로 변환 및 반환
		return CommentDto.createCommentDto(target);
	}

댓글 삭제 성공