6.3 블로그 글 작성 API 구현

SummerToday·2024년 2월 8일
1
post-thumbnail
post-custom-banner
  • API 구현 개요

서비스 메서드 코드 구현

// dto - AddArticleRequest.java

@NoArgsConstructor //기본 생성자 추가
@AllArgsConstructor // 모든 필드 값을 파라미터로 받는 생성자 추가
@Getter
public class AddArticleRequest { // 서비스계층에서 요청 받은 데이터를 엔티티로 생성.

    private String title;
    private String content;

    public Article toEntity() { // 생성자를 사용해 객체 생성
        return Article.builder()
                .title(title)
                .content(content)
                .build(); // 빌더 패턴을 사용해 DTO를 엔티티로 만들어주는 메소드.
    }
}
  • DTO
    데이터를 옮기기 위해 사용하는 전달자 역할을 하는 객체이다.
    별도의 비즈니스 로직을 포함하지 않는다.

  • DAO
    데이터베이스와 연결되고 데이터를 조회하고 수정하는데 사용하는 객체이기 때문에 데이터 수정과 관련된 로직이 포함된다.

  • toEntity()
    빌더 패턴을 사용해 DTO를 엔티티로 만들어주는 메서드이다. 블로그 글을 추가할 때 저장할 엔티티로 변환하는 역할을 한다.


// service - BlogService.java

@RequiredArgsConstructor // final이 붙거나 @NotNull이 붙은 필드의 생성자 추가 
@Service // 빈으로 등록
public class BlogService {
    private final BlogRepository blogRepository;

    public Article save(AddArticleRequest request){ // 블로그 글 추가 메소드
        return blogRepository.save(request.toEntity());
    }
}
  • @RequiredArgsConstructor
    생성자가 하나 이상 존재할 시 @Autowierd가 자동으로 생성되어 의존성을 주입할 수 있다.
    따라서 해당 어노테이션을 통해 생성자를 생성함과 동시에 의존성을 주입을 해준다.
    의존성을 주입 받음으로써 빈으로 등록된 객체들을 해당 클래스에서 직접 생성하지 않고 사용할 수 있게 된다.

  • @Service
    비즈니스 로직을 담당하고 있는 클래스로써, 해당 클래스를 빈으로 서블릿 컨테이너에 등록한다.

  • save()
    JpaRepository에서 지원하는 저장 메서드 save()로 AddArticleRequest 클래스에 저장된 값들을 article 데이터베이스에 저장한다.

    cf. BlogRepository는 JpaRepository를 상속 받고, JpaRepository의 부모 클래스인 CrudRepository에 save() 메서드가 선언이 돼있다. 이 메서드를 사용하여 데이터베이스에 Article 엔티티를 저장할 수 있게된다.


컨트롤러 메서드 코드 구현

@RequiredArgsConstructor
@RestController // HTTP Response Body에 객체 데이터를 JSON 형식으로 반환하는 컨트롤러
public class BlogApiController {

    private final BlogService blogService;

    @PostMapping("/api/articles") // HTTP 메서드가 POST일 때 전달 받은 URL과 동일하면 해당 메서드로 매핑
    public ResponseEntity<Article> addArticle(
            @RequestBody AddArticleRequest request){
        Article savedArticle = blogService.save(request);

        return ResponseEntity.status(HttpStatus.CREATED)
                .body(savedArticle); // 요청한 자원이 성공적으로 생성되었으면 저장된 블로그 글 정보를 응답 객체에 담아 반환.
    }
}
  • @RequiredArgsConstructor
    의존성 주입 역할.

  • @RestController
    HTTP 응답으로 객체 데이터를 JSON 형식으로 반환한다.

  • @PostMapping
    HTTP 메서드가 POST일 때 요청받은 URL과 동일하면 addArticle() 메서드에 매핑한다.

  • @RequestBody
    HTTP를 요청할 때 본문 내용을 자바 객체로 변환하여 AddArticleRequest에 매핑한다.

  • ResponseEntity.status(a).body(b);
    ResponseEntity는 스프링 프레임워크에서 사용자의 HttpRequest에 대한 응답 데이터를 포함하는 클래스이다.
    따라서 HttpStatus, HttpHeaders, HttpBody를 포함하고, RestTemplate 또는 컨트롤러에서 주로 사용된다.
    해당 코드는 HTTP 요청을 보냈을 때 응답 코드를 a로 설정하여 데이터 b를 반환한다는 의미이다.

    상태코드를 알맞게 설정하여 반환해야 클라이언트가 요청 상태를 알 수 있다.

    • 응답 코드 종류

      • 200 ok : 요청을 성공적으로 수행.

      • 201 Created : 요청이 성공적으로 수행되었고, 새로운 리소스 생성.

      • 400 Bad Requeste : 요청 값이 잘못되어 요청에 실패.

      • 403 Forbidden : 권한이 없어 요청에 실패.

      • 404 Not Found : 요청 값으로 찾은 리소스가 없어 요청에 실패.

      • 500 Internal Server Error : 서버상에 문제가 있어 요청에 실패.

API 실행 테스트

  • H2 콘솔 활성화

    // application.yml
    
    spring:
     jpa:
       show-sql: true
       properties:
         hibernate:
           format_sql: true
       defer-datasource-initialization: true
     datasource:
       url: jdbc:h2:mem:testdb
     h2:
       console:
         enabled: true
        

  • Postman 접속후 HTTP 메서드 POST, URL: http://localhost:8080/api/articles,
    [body]는 JSON으로 변경 후 다음과 같이 작성하여 서버에 요청 전송.

      {
          "title": "제목",
          "content": "내용"
       }

  • localhost:8080/h2-console에 접속 하여 JDBC URL에 application.yml의 datasource의 URL 값을 입력 후 H2 데이터베이스 접속하여 해당 테이블을 확인하여 값이 잘 들어왔는지 확인한다.


테스트 코드 작성

H2 데이터베이스에 매번 접속하여 확인하기가 번거로우니, 테스트 과정을 간소화 시켜줄 테스트 코드를 작성한다.

  • BlogApiController 클래스에 Alt+ Enter를 누르고 [Create Test]를 눌러 기본값 설정대로 Test 코드를 생성하여 다음과 같이 코드를 수정한다.

    @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();
      }
    }
    • ObjectMapper 클래스
      객체를 JSON 데이터로 변환하는 직렬화(serialization) 또는 반대로 JSON 데이터를 자바에서 사용하기 위해 자바 객체로 변환하는 역직렬화(deserialization)할 때 사용한다.
      해당 클래스는 Jackson 라이브러리에서 제공된다.

  • Given-When-Then 패턴 작성

    • Given
      블로그 글 추가에 필요한 요청 객체 생성

    • When
      블로그 글 추가 API에 요청 전송. 요청 타입은 JSON이며, given절에서 미리 만들어둔 객체를 요청 본문으로 함께 보낸다.

    • Then
      응답 코드가 201 Created인지 확인 후 Blog를 전체 조회해 크기가 1인지 확인하고, 실제 저장된 데이터와 요청 값을 비교한다.
    // BlogApiControllerTest.java
    
      @SpringBootTest  
      @AutoConfigureMockMvc 
      class BlogApiControllerTest {
    
                   ~생략~
      
      @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
          // 설정한 내용을 바탕으로 요청 전송
          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);
      }
    }
    • writrValueAsString()
      객체를 JSON으로 직렬화 한다.

    • assertThat()
      각 데이터들의 값들을 검증하는 역할을 한다.

      • assertThat(articles.size()).isEqualTo(1)
        블로그 글 크기가 1임을 검증

      • assertThat(articles.size()).isGreaterThan(2)
        블로그 글 크기가 2보다 큰 지를 검증

      • assertThat(articles.size()).isLessThan(5)
        블로그 글 크기가 5보다 작은 지를 검증

      • assertThat(articles.size()).isZero()
        블로그 글 크기가 0인 지를 검증

      • assertThat(articles.title()).isEqualTo("제목")
        블로그 글의 title이 "제목"인 지를 검증

      • assertThat(articles.title()).isNotEmpty()
        블로그 글의 title이 비어있지 않은 지를 검증

      • assertThat(articles.title()).isContains("제")
        블로그 글의 title이 "제"를 포함하는 지를 검증



해당 글은 다음 도서의 내용을 정리하고 참고한 글임을 밝힙니다.
신선영, ⌜스프링 부트 3 벡엔드 개발자 되기 - 자바 편⌟, 골든래빗(주), 2023, 384쪽
profile
IT, 개발 관련 정보들을 기록하는 장소입니다.
post-custom-banner

0개의 댓글