스터디/back

15장 ~ 16장

ㅈㅣ니 2024. 1. 4.
반응형

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

 

앞의 14장에서 엔티티와 리파지터리를 생성했다. 이번에는 댓글 CRUD를 위한 REST API를 완성할 것이다.

 

 

API 주소 설계는 다음과 같다.

GET /articles/articleId/comments
POST /articles/articleId/comments
PATCH /comments/id
DELETE /comments/id

 

컨트롤러&서비스 틀 만들기

댓글 컨트롤러와 서비스 틀을 만들것이다.

REST 컨트롤러는 api 패키지 안에 생성.

CommentApiController.java

@RestController
public class CommentApiController {
    @Autowired
    private CommentService commentService;  // 댓글 서비스 객체 주입
    // 1. 댓글 조회
    // 2. 댓글 생성
    // 3. 댓글 수정
    // 4. 댓글 삭제

}

 

service 패키지에는 CommnetService 서비스를 생성했다

CommentService.java

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

 

댓글 조회하기

댓글 조회 REST API 주소는 다음과 같다.

/articles/articleId/comments

 

CommentApiController.java

@RestController
public class CommentApiController {
    @Autowired
    private CommentService commentService;  // 댓글 서비스 객체 주입
    // 1. 댓글 조회
    @GetMapping("/api/articles/{articleId}/comments")   // 요청 url 작성
    public ResponseEntity<List<CommentDto>> comments(@PathVariable Long articleId) {
        // 서비스에 위임
        List<CommentDto> dtos = commentService.comments(articleId);
        // 결과 응답
        return ResponseEntity.status(HttpStatus.OK).body(dtos);
    }
    // 2. 댓글 생성
    // 3. 댓글 수정
    // 4. 댓글 삭제

}

반환형이 ResponseEntity<List<CommentDto>> 인 이유는

DB에서 조회한 댓글 엔티티를 DTO로 변환하면 List<CommentDto>가 되기 때문이다.

+ 응답 코드를 같이 보내려면 ResponseEntity클래스를 활용해야함

 

[ CommentService.java ]

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

    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);        // 엔티티를 DTO 로 변환한다.
            dtos.add(dto);      // DTO 를 dtos 리스트에 삽입.
        }
        // 3. 결과 반환
        return dtos;

    }
}

 

CommentDto.java

@AllArgsConstructor
@NoArgsConstructor
@Getter
@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(),
                comment.getArticle().getId(),
                comment.getNickname(),
                comment.getBody()
        );
    }
}

4번 게시글의 댓글이 잘 반환되는 것을 확인할 수 있다.

 

1분 퀴즈)

• (  ㉠  ): 리파지터리가 DB 속 데이터를 조회하거나 전달할 때 사용하는 객체

• (  ㉡  ): 단순 데이터 전송만을 목적으로 하는 객체, 클라이언트와 서버 사이에서 사용됨

• (  ㉢  ): 컨트롤러와 리파지터리의 사이에서 비즈니스 로직, 즉 처리 흐름을 담당하는 객체

• (  ㉣  ): 클라이언트의 요청을 받고 응답하는 객체로, 뷰(view)가 아닌 데이터를 반환

 

ㄱ: 엔티티

ㄴ: DTO

ㄷ: 서비스

ㄹ: 컨트롤러

 

댓글 생성하기

댓글 생성 REST API 주소는 다음과 같다.

/articles/articleId/comments

 

CommentApiController.java

// 2. 댓글 생성
@PostMapping("/api/articles/{articleId}/comments")
public ResponseEntity<CommentDto> create(@PathVariable Long articleId, @RequestBody CommentDto dto){

    // 서비스에 위임
    CommentDto createdDto = commentService.create(articleId, dto);
    // 결과 응답
    return ResponseEntity.status(HttpStatus.OK).body(createdDto);
}

 

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분 퀴즈) 다음은 댓글 생성을 위한 REST API 요청입니다. 이와 관련한 설명으로 옳지 않은 것을 고르세요.

 

① HTTP를 통해 POST 요청을 보내고 있다.

② 댓글의 정보를 JSON 형식으로 전송하고 있다.

③ 댓글의 정보를 컨트롤러에서 @RequestBody로 받을 수 있다.

④ 해당 요청을 통해 5번 게시글에 댓글이 작성된다.

 

답 : 4번 ( url은 4번 게시글에 댓글을 작성하는 것인데, body에는 5번 게시글로 접근해서.. 에러 발생 할 것.)

 

 

댓글 수정하기

댓글 수정 REST API 주소는 다음과 같다.

/comments/id

CommentApiController.java

// 3. 댓글 수정
@PostMapping("/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);
}

 

Entity 패키지의 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();  // 내용 반영
}

다크나이트로 수정된 것을 확인할 수 있다. ( patch 지원 안되어서 post 메서드로 수정함 )

 

1분 퀴즈) ㉠, ㉡에 들어갈 용어를 쓰세요.

• HTTP 요청 중 수정을 위한 메서드에는 PUT과 (  ㉠  )(이)가 있다.

• 입력값이 잘못된 경우에 (  ㉡  ) 클래스를 사용해 예외 처리를 한다.

ㄱ: PATCH

ㄴ: IllegalArgumentException

 

 

댓글 삭제하기

 

댓글 삭제 REST API 주소는 다음과 같다.

/comments/id

 

CommentApiController.java

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

 

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);
}

 

id 1번 댓글 삭제 확인

 

1분 퀴즈) ㉠~㉢에 들어갈 용어를 쓰세요.

• (  ㉠  ) 어노테이션은 HTTP 삭제 요청을 받아 특정 컨트롤러 메서드를 수행한다.

• (  ㉡  ) 어노테이션은 HTTP 요청 주소에서 특정 값을 매개변수로 가져온다.

• (  ㉢  ) 어노테이션은 예외 발생 시 변경된 데이터를 변경 전으로 롤백한다.

 

ㄱ: @DeleteMapping

ㄴ: @PathVariable

ㄷ: @Transactional

 

16장 웹 페이지에서 댓글 목록 보기

서버를 실행시킨 후 http://localhost:8080/articles/5 페이지에 접속하면 다음 화면이 나온다.

위 화면 아래에 댓글을 출력하도록 만들어 볼 예정이다.

 

ArticleController.java

show.mustache

푸터 바로 위에 _comments.mustache 파일을 연결해주었다.

 

_comments.mustache

<div>
    <!-- 댓글 목록 보기 -->
    {{>comments/_list}}
    <!-- 새 댓글 작성하기 -->
    {{>comments/_new}}
</div>

 

_list.mustache

<div id="comments-list">
    {{#commentDtos}}        <!--댓글 목록 순회-->
        <div class="card m" id="comments-{{id}}">
            <div class="card-header">
                {{nickname}}
            </div>

            <div class="card-body">
                {{body}}
            </div>
        </div>
    {{/commentDtos}}

</div>

 

_new.mustache

 

이렇게 뷰를 만들어주면 끝이 아니라. 컨트롤러에서 뷰 페이지에서 사용할 변수를 모델에 등록해줘야한다.

 이렇게 하고 다시 서버 재실행 하면...

댓글을 확인할 수 있다.

 

1분 퀴즈 )다음 설명이 참이면 O, 거짓이면 X를 쓰세요.

㉠ 뷰 페이지에서 사용할 변수는 모델에 등록해야 사용할 수 있다. ------------- (   O )

㉡ 머스테치 문법 {{#data}}…{{/data}}에서 data 값이 없는 경우, 내부 영역은 보이지 않는다. -------------(   O   )

㉢ 머스테치 문법 {{#data}}…{{/data}}에서 data 값이 있는 경우, 내부 영역은 한 번만 출력된다. -------------(    X  )

㉣ 부트스트랩은 웹 페이지 개발을 위한 다양한 도구를 제공한다. -------------(   O   )

반응형