코딩자율학습 스프링부트3 자바 백엔드 개발 입문 3주차 학습 후기

이번 3주차 에서는 REST API 컨트롤러에 대해서 좀더 깊이 있게 학습했고, 서비스 계층 분리, 트랜잭션 적용, 그리고 테스트 코드 작성 까지 하면서, 교제에서의 진행되는 CRUD 프로젝트에 대해서 이전 주의 코드들을 변경하면서, 개발 역량을 향상시키는 시간이였습니다.
11일차: HTTP와 REST 컨트롤러
HTTP의 기본 원리와 REST API의 동작 방식에 대해서 학습하였습니다. @RestController, @GetMapping, @PostMapping 등의 스프링 어노테이션을 활용하여 게시글(Article)의 조회, 생성, 수정, 삭제 기능을 담당하는 ArticleController 를 구현하며 REST API 설계 및 구현 방법을 익혔습니다.
@PathVariable과 @RequestBody를 통해 클라이언트와 데이터를 효율적으로 교환하고, ResponseEntity로 HTTP 상태 코드를 명확하게 제어하는 방식을 실습하여 견고한 API를 구축하는 방법을 익혔습니다.
// ArticleApiController.java
@Slf4j
@RestController
public class ArticleApiController {
@Autowired
private ArticleRepository articleRepository;
@GetMapping("/api/articles") // 모든 게시글 조회
public List<Article> index() { /* ... */ return articleRepository.findAll(); }
@PostMapping("/api/articles") // 게시글 생성
public Article create(@RequestBody ArticleForm dto) { /* ... */ return articleRepository.save(dto.toEntity()); }
}
12일차: 서비스 계층과 트랜잭션
애플리케이션의 구조를 더욱 체계적으로 만들기 위해 서비스 계층을 사용하는데, 기존 컨트롤러에 있던 비즈니스 로직을 ArticleService 클래스로 분리함으로써 컨트롤러는 요청 처리, 서비스는 비즈니스 로직, 리파지터리는 데이터 접근이라는 각 계층의 명확한 역할을 분업화합니다. 이는 코드의 가독성과 유지보수성을 향상시킵니다.
// ArticleApiController.java
@Slf4j
@RestController
public class ArticleApiController {
@Autowired
private ArticleService articleService; // 서비스 객체 주입
@GetMapping("/api/articles")
public List<Article> index() {
return articleService.index();
}
}
// ArticleService.java
@Slf4j
@Service
public class ArticleService {
@Autowired
private ArticleRepository articleRepository;
@Transactional
public List<Article> createArticles(List<ArticleForm> dtos) {
dtos.stream().map(ArticleForm::toEntity).forEach(articleRepository::save);
articleRepository.findById(-1L).orElseThrow(() -> new IllegalArgumentException("결제 실패!"));
return /* ... */;
}
}
ArticleService는 비즈니스 로직을 담당하고, 특히 @Transactional 어노트테이션을 사용하여 여러 DB작업이 실행되고 오류시 자동 롤백되는 트랜잭션의 개념을 학습하여 데이터 무결성 확보의 중요성을 학습하였습니다.
13일차: 테스트 코드 작성하기
코드가 의도한 대로 정확히 작동하는지 확인하는 것은 필수 과정입니다. 이를 위한 테스트 코드 작성 방법을 학습하였습니다. SpringBootTest 어노테이션을 사용하여 스프링 부트 환경에서 테스트를 실행하고, @Test 어노테이션으로 개별 테스트 메서드를 정의하는 방법을 배웠습니다. 이를 통해 ArticleService의 각 메서드에 대한 단위 테스트를 작성하며 코드의 안정성을 높이는 방법을 학습하였습니다.
// ArticleServiceTest.java
@SpringBootTest
class ArticleServiceTest {
@Autowired
ArticleService articleService;
@Test // 모든 게시글 조회 테스트
void index() {
// 예상 데이터
// 실제 데이터
// 비교 및 검증
}
@Test
void show_성공_존재하는_id_입력() { /* ... */ }
@Transactional // 테스트 후 롤백
@Test
void create_성공_title과_content만_있는_dto_입력() { /* ... */ }
@Test
void create_실패_id가_포함된_dto_입력() { /* ... */ }
}
14장 댓글 엔티티와 리파지터리 만들기
게시판 기능의 확장인 댓글 기능 구현을 하며, 댓글 데이터를 정의하는 Comment 엔티티와 이를 위한 데이터베이스 스키마 및 초기 데이터를 정의하고, CommentRepository를 통해 데이터 접근 계층을 구현 하였습니다.
// Comment.java
@Entity @Getter @ToString @AllArgsConstructor @NoArgsConstructor
public class Comment {
@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@ManyToOne @JoinColumn(name="article_id")
private Article article;
@Column private String nickname;
@Column private String body;
}
// CommentRepository.java
public interface CommentRepository extends JpaRepository<Comment, Long> {
@Query(value = "SELECT * FROM comment WHERE article_id = :articleId", nativeQuery = true)
List<Comment> findByArticleId(Long articleId); // 특정 게시글의 모든 댓글 조회
List<Comment> findByNickname(String nickname); // 특정 닉네임의 모든 댓글 조회
}
// CommentRepositoryTest.java
@DataJpaTest
class CommentRepositoryTest {
@Autowired
CommentRepository commentRepository;
@Test @DisplayName("특정 게시글의 모든 댓글 조회")
void findByArticleId() { /* ... */ }
}
Comment 엔티티에서는 Article과의 MayToOne 관계를 설정하여 외래키를 명시했으며, CommentRepository에서는 Spring Data JPA의 기본 기능을 활용하는 동시에 네이티브 쿼리 및 쿼리 메서드를 통해 복잡한 조회 조건도 처리할 수 있음을 확인했습니다. 이를 CommentRepositoryTest를 통해 검증하였습니다.
15장 댓글 컨트롤러와 서비스 만들기
댓글 데이터 접근 기반 위에 실제 사용자 요청을 처리하고 비즈니스 로직을 수행하는 계층을 구현하였습니다. 이를 위해 댓글 API를 위한 DTO, 컨트롤러, 서비스 클래스를 설계하였습니다.
// CommentDto.java
@AllArgsConstructor
@NoArgsConstructor
@Getter
@ToString
public class CommentDto {
private Long id;
private Long articleId;
private String nickname;
private String body;
public static CommentDto createCommentDto(Comment comment) {
/* ... */ return new CommentDto(/* ... */); }
}
// CommentApiController.java
@RestController
public class CommentApiController {
@Autowired
private CommentService commentService; // 댓글 리파지터리 객체 주입
}
// CommentService.java
@Service
public class CommentService {
@Autowired
private CommentRepository commentRepository; // 댓글 리파지터리 객체 주입
@Autowired
private ArticleRepository articleRepository; // 게시글 리파지터리 객체 주입
public List<CommentDto> comments(Long articleId) { // 댓글 엔티티 목록 조회
return commentRepository.findByArticleId(articleId)
.stream()
.map(CommentDto::createCommentDto) // 엔티티를 DTO로 매핑
.collect(Collectors.toList());
}
}
CommentDto를 통해 엔티티와 클라이언트 간의 데이터 전달을 효율화하고, CommentApiController가 CommentService로 비즈니스 로직을 위임하며 CommentService가 리파지터리를 활용하는 계층형 구조를 완성했습니다. 특히 Stream API 를 활용한 DTO 변환은 코드의 간결성을 높이는 좋은 예시가 되었습니다.
3주차 마무리
이번주에는 스프링 부트 학습을 통해 HTTP 및 REST API의 동작 원리를 심층적으로 다루고 ArticleApiController 를 구현하여 CRUD 기능을 완성했습니다.
ArticleService를 도입하여 서비스 계층을 분리하고, @Transactional 어노데이션으로 데이터 트랜잭션 관리를 수행했습니다. ArticleServiceTest를 통해 서비스 로직에 대한 테스트 코드를 작성하여 테스트의 중요성을 배웠으며, 게시판 댓글 기능 구현을 위해 Comment 엔티티와 CommentRepository를 설계및 구현하고, CommentRepositoryTest로 이를 검증하였습니다.