Backend
home
🖋️

1일차

생성일
2024/07/06 16:19
태그
본 프로젝트는 “스프링부트 3 백엔드 개발자 되기” 서적을 참고하여 진행하였음

엔티티 구성

Search
테이블 구조
컬럼명
자료형
null 허용
설명
VARCHAR(255)
N
게시물의 제목
VARCHAR(255)
N
내용
// domain > Article.java package com.example.msblog.domain; import jakarta.persistence.*; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @Entity // 엔티티로 지정 @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Article { @Id // Id 필드를 기본키로 지정 @GeneratedValue(strategy = GenerationType.IDENTITY) // 기본키를 자동으로 1씩 증가시키기 @Column(name = "id", updatable = false) private Long id; @Column(name = "title", nullable = false) // 'title' 이라는 not null 컬럼과 매핑 private String title; @Column(name = "content", nullable = false) private String content; @Builder // 빌더 패턴으로 작성 public Article(String title, String content) { this.title = title; this.content = content; } }
Java
복사

리포지토리 만들기

// repository > BlogRepository package com.example.msblog.repository; import com.example.msblog.domain.Article; import org.springframework.data.jpa.repository.JpaRepository; public interface BlogRepository extends JpaRepository<Article, Long> { }
Java
복사

API 구현

DTO (Data Transfer Object)
// Dto > AddArticleRequest.java package com.example.msblog.dto; import com.example.msblog.domain.Article; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; @NoArgsConstructor // 기본 생성자 추가 @AllArgsConstructor // 모든 필드 값을 파라미터로 받는 생성자 추가 @Getter // Getter 안해주면 500 에러 발생하므로 주의! public class AddArticleRequest { private String title; private String content; public Article toEntity() { // 생성자를 사용하여 객체를 생성 return Article.builder() .title(title) .content(content) .build(); } }
Java
복사
Service
// service > BlogService.java package com.example.msblog.service; import com.example.msblog.domain.Article; import com.example.msblog.dto.AddArticleRequest; import com.example.msblog.repository.BlogRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @RequiredArgsConstructor // final이 붙거나 @NotNull이 붙은 필드의 생성자 추가 @Service // 빈으로 등록 public class BlogService { private final BlogRepository blogRepository; // 블로그 글 추가 메서드 public Article save(AddArticleRequest request) { return blogRepository.save(request.toEntity()); } }
Java
복사
Controller
// controller > BlogApiController package com.example.msblog.controller; import com.example.msblog.domain.Article; import com.example.msblog.dto.AddArticleRequest; import com.example.msblog.service.BlogService; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RequiredArgsConstructor @RestController // HTTP RESPONSE BODY에 객체 데이터를 JSON 형태로 반환 public class BlogApiController { private final BlogService blogService; // HTTP 메서드가 POST일 때 전달받은 URL과 동일하면 메서드로 매핑 @PostMapping("/api/articles") // @RequestBody로 요청 본문 값 매핑 public ResponseEntity<Article> addArticle(@RequestBody AddArticleRequest request) { Article savedArticle = blogService.save(request); // 요청한 자원이 성공적으로 생성되었으며 저장된 블로그 글 정보를 응답 객체에 담아 전송 return ResponseEntity.status(HttpStatus.CREATED) .body(savedArticle); } }
Java
복사

응답 코드 확인

Search
응답 코드
내용
요청이 성공적으로 수행되었음
요청이 성공적으로 수행되었고, 새로운 리소스가 생성되었음
요청 값이 잘못되어 요청에 실패했음
권한 없어 요청 실패
요청 값으로 찾은 리소스가 없어 요청에 실패
서버 상에 문제가 있어 요청에 실패함

최초 응답 테스트

POST 요청
{ "title": "title", "content": "content" }
JSON
복사
결과 - 요청한 POST 요청에 의해 데이터가 실제로 저장됨
{ "id": 1, "title": "title", "content": "content" }
JSON
복사

테스트 코드 작성

Given
블로그 글 추가에 필요한 요청 객체를 만든다.
When
블로그 글 추가 API에 요청을 보낸다. 요청 타입은 JSON 이며, give 절에서 미리 만들어준 객체를 요청 본문으로 함께 보낸다.
Then
응답 코드가 201 Created인지 확인한다. Blog를 전체 조회해 크기가 1인지 확인하고, 실제로 저장된 데이터와 요청 값을 비교한다.
controller > BlogApiControllerTest
package com.example.msblog.controller; import com.example.msblog.domain.Article; import com.example.msblog.dto.AddArticleRequest; import com.example.msblog.repository.BlogRepository; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; import java.util.List; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest // 테스트용 애플리케이션 컨택스트 @AutoConfigureMockMvc // MockMvc 생성 및 자동 구성 class BlogApiControllerTest { @Autowired protected MockMvc mockMvc; @Autowired protected ObjectMapper objectMapper; // 직렬화, 역직렬화를 위한 클래스 @Autowired private WebApplicationContext context; @Autowired BlogRepository blogRepository; @BeforeEach // 테스트 실행 전 실행하는 메서드 public void mockMvcSetUp() { this.mockMvc = MockMvcBuilders.webAppContextSetup(context) .build(); blogRepository.deleteAll(); } @DisplayName("addArticle: 블로그 글 추가에 성공한다.") @Test public void addArticle() throws Exception { // given final String url = "/api/articles"; final String title = "title"; final String content = "content"; final AddArticleRequest userRequest = new AddArticleRequest(title, content); // 객체 JSON으로 직렬화하기 final String requestBody = objectMapper.writeValueAsString(userRequest); // when // mockMvc 활용하여 HTTP 메서드, URL, 요청 본문, 요청 타입 등을 설정한 뒤 설정한 내용을 바탕으로 테스트 요청을 보냄 // 설정한 내용 바탕으로 요청 전송 ResultActions result = mockMvc.perform(post(url) .contentType(MediaType.APPLICATION_JSON_VALUE) .content(requestBody)); // then result.andExpect(status().isCreated()); List<Article> articles = blogRepository.findAll(); assertThat(articles.size()).isEqualTo(1); // 크기가 1인지 검증 assertThat(articles.get(0).getTitle()).isEqualTo(title); assertThat(articles.get(0).getContent()).isEqualTo(content); } }
Java
복사
테스트 코드 관련 참고내용
코드
설명
assertThat(articles.size()).isEqualTo(1);
블로그 글 크기가 1이어야 한다.
assertThat(articles.size()).isGreaterThan(2);
블로그 글 크기가 2보다 커야 한다.
assertThat(articles.size()).isLessThan(5);
블로그 글 크기가 5보다 작아야 한다.
assertThat(articles.size()).isZero();
블로그 글 크기가 0이어야 한다.
assertThat(article.title()).isEqualTo(”제목”)
블로그 글의 title값이 “제목”이어야 한다.
assertThat(article.title()).isNotEmpty();
블로그 글의 title값이 비어 있지 않아야 한다.
assertThat(article.title()).contains(”제”);
블로그 글의 title값이 “제”를 포함해야 한다.