[Spring Boot] 15. 댓글 컨트롤러와 서비스 만들기 (1)
- -
15장. 댓글 컨트롤러와 서비스 만들기 (1)
15.1. 댓글 REST API의 개요
· REST 컨트롤러
- 댓글 REST API를 위한 컨트롤러
- 서비스와 협업, 클라이언트 요청을 박아 응답하며 뷰(View)가 아닌 데이터(JSON) 반환
· 서비스
- REST 컨트롤러와 리파지터리 사이에서 비즈니스 로직, 즉 처리 흐름을 담당
- 예외 상황이 발생하면 @Transactional로 변경된 데이터 롤백
· DTO
- 사용자에게 보여 줄 댓글 정보를 담은 것
- 단순히 클라이언트와 서버 간에 댓글 JSON 데이터 전송
· 엔티티
- DB 데이터를 담는 자바 객체
- 엔티티를 기반으로 테이블 생성
- 리파지터리가 DB 속 데이터를 조회하거나 전달할 때 사용
· 리파지터리
- 엔티티를 관리하는 인터페이스
- 데이터 CRUD 기능 제공
- 서비스로부터 댓글 CRUD 등의 명령을 받아 DB에 보내고 응답받음
15.2. 댓글 컨트롤러와 서비스 틀 만들기
CommentApiContoller를 생성하고 @RestController 어노테이션을 통해 해당 클래스를 REST 컨트롤러로 선언한다. 컨트롤러가 서비스와 협업할 수 있도록 @Autowired 어노테이션과 함께 commentService 객체를 주입해준다.
src > test > java > com.example.firstproject > api > CommentApiController
@RestController
public class CommentApiController {
@Autowired
private CommentService commentService;
// 1. 댓글 조회
// 2. 댓글 생성
// 3. 댓글 수정
// 4. 댓글 삭제
}
CommentService를 생성하고 @Service 어노테이션을 통해 해당 클래스를 서비스로 선언한다. 서비스와 함께 협업할 리파지터리, 즉 댓글 리파지터리(commentRepository)와 게시글 리파지터리(articleRepository) 객체를 함께 주입한다. 게시글 리파지터리까지 필요한 이유는 게시글 리파지터리가 있어야 댓글(comment)을 생성할 때 대상 게시글(article) 존재 여부를 파악할 수 있기 때문이다.
src > test > java > com.example.firstproject > service > CommentService
@Service // 서비스로 선언
public class CommentService {
@Autowired
private CommentRepository commentRepository;
@Autowired
private ArticleRepository articleRepository;
}
15.3. 댓글 조회하기
15.3.1. 요청을 받아 응답할 컨트롤러 만들기
GetMapping()으로 댓글 요청을 받는데 괄호 안에는 요청 URL인 .../api/articles/{articleId}/comments을 받는다. 메서드의 이름은 comments()로 짓고 반환형은 ResponseEntity<List<CommentDto>>로 정의한다. 반환형이 댓글 엔티티의 묶음인 List<Comment>가 아닌 이유는 DB에서 조회한 댓글 엔티티 목록은 List<Comment>이지만, 엔티티를 DTO로 변환하면 List<CommentDto>가 되기 때문이다.
앞서 만든 REST 컨트롤러는 메서드의 반환값으로 성공하는 경우와 실패하는 경우로 나눠 삼항 연산자로 작성했지만 실제 개발에서는 이 방식보다는 예외 처리 방식을 선호한다. 예외 처리(exception handling)란 예기치 못한 상황이 발생했을 때를 대비해 대처하는 코드를 미리 작성하는 것을 말한다. 여기서는 댓글 조회에 실패할 경우 스프링 부트가 예외처리를 한다고 가정해 성공하는 겨웅만 응답으로 보내자.
src > test > java > com.example.firstproject > api > CommentApiController
// 1. 댓글 조회
@GetMapping("/api/articles/{articleId}/comments") // 댓글 조회 요청 접수
public ResponseEntity<List<CommentDto>> comments(@PathVariable Long articleId) {
// 1. 서비스에 위임
List<CommentDto> dtos = commentService.comments(articleId);
// 2. 결과 응답
return ResponseEntity.status(HttpStatus.OK).body(dtos);
}
src > test > java > com.example.firstproject > dto > CommentDto
@AllArgsConstructor
@NoArgsConstructor
@Getter
@ToString
public class CommentDto {
private Long id; // 댓글의 id
private Long articleId; // 댓글의 부모 id
private String nickname; // 댓글 작성자
private String body; // 댓글 본문
}
15.3.2. 요청을 처리할 서비스 만들기
src > test > java > com.example.firstproject > service > CommentService
@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;
}
}
createCommentDto를 객체 생성 없이 호출하기 위해서 public static, 즉 정적 메서드로 createCommentDto()를 선언한다. 이러한 방식으로 객체를 만드는 메서드를 생성 메서드라고 한다. 메서드의 반환값이 댓글 DTO가 되도록 CommentDto 생성자를 호출한다.
src > test > java > com.example.firstproject > dto > CommentDto
public static CommentDto createCommentDto(Comment comment) {
return new CommentDto(
comment.getId(), // 댓글의 엔티티 id
comment.getArticle().getId(), // 댓글 엔티티가 속한 부모 게시글 id
comment.getNickname(), // 댓글 엔티티의 nickname
comment.getBody() // 댓글 엔티티의 body
);
}
15.3.3. 결과 확인하기
15.3.4. stream 문법
스트림은 자바의 컬렉션(Collection), 즉 리스트와 해시맵 등의 데이터 묶음을 요소별로 순차적으로 조작하는데 편리하다. 스트림의 주요 특징은 다음과 같다. 스트림 관련 학습은 자바 문법 중 스트림과 람다식을 찾아보자.
· 원본 데이터를 읽기만 하고 변경하지 않는다.
· 정렬된 결과를 컬렉션이나 배열에 담아 반환할 수 있다.
· 내부 반복문으로, 반복문이 코드상에 노출되지 않는다.
.stream()
.map(a -> b) // 스트림의 각 요소(a)를 꺼내 b를 수행한 결과를 맵핑
.collect(Collectors.toList()) // 스트림 데이터를 리스트 자료형으로 변환
15.3.5. stream 문법으로 개선
서비스 코드의 for() 문을 스트림(stream) 문법으로 개선해보자. 이렇게 하면 가독성을 높이고 코드의 양을 줄일 수 있다.
src > test > java > com.example.firstproject > service > CommentService (개선 전)
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;
}
return 문에서 반환한 dtos를 지우고, 댓글 엔티티 목록을 가져오게 한다. .stream()을 통해 댓글 엔티티 목록을 스트림으로 바꾼다. 스트림은 컬렉션이나 리스트에 저장된 요소들을 하나씩 참조하며 반복 처리할 때 사용한다. 스트림화 한 댓글 엔티티 목록을 DTO로 변환하는데, .map() 메서드를 이용하면 요소를 꺼내 조건에 맞게 변환이 가능하다.이후 .collect() 메서드를 통해 스트림 데이터를 리스트 자료형으로 변환한다.
src > test > java > com.example.firstproject > service > CommentService (개선 후)
public List<CommentDto> comments(Long articleId) {
return commentRepository.findByArticleId(articleId) // 댓글 엔티티 목록 조회
.stream() // 댓글 엔티티 목록을 스트림으로 변환
.map(comment -> CommentDto.createCommentDto(comment)) // 엔티티를 DTO로 맵핑
.collect(Collectors.toList()); // 스트림을 리스트로 변환
}
'BackEnd > 스프링부트3 백엔드 개발 입문' 카테고리의 다른 글
[Spring Boot] 16. 웹 페이지에서 댓글 목록 보기 (0) | 2024.01.30 |
---|---|
[Spring Boot] 15. 댓글 컨트롤러와 서비스 만들기 (2) (0) | 2024.01.29 |
[Spring Boot] 14. 댓글 엔티티와 리파지터리 만들기 (0) | 2024.01.25 |
[Spring Boot] 13. 테스트 코드 작성하기 (0) | 2024.01.24 |
[Spring Boot] 12. 서비스 계층과 트랜잭션 (0) | 2024.01.23 |
소중한 공감 감사합니다