ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 10. 스프링부트와 AWS - 게시글 등록 API 만들기
    실습/AWS 2021. 5. 7. 02:34

    지난번 오늘 쓸 내용을 다루다가 내용이 산으로 가서...게시판을 마저 만들도록 하자.

     

    게시물 등록용 API를 만들기 위해선 총 3개의 클래스가 필요하다.

     

    - Request 데이터를 받을 dto

    - API 요청을 받을 Controller

    - 트랜잭션, 도메인 기능 간의 순서를 보장하는 Service

     

    간단히 설명하면 사용자가 요청하는 Controller.

     

    전달받은 내용과 데이터를 전달하는 dto.

     

    이를 처리할 수 있도록 도와주는 Service.

     

    이렇게 구성을 할 예정이다.

    클래스 만들기

    위에서 이야기했듯 만들어야 할 클래스는 총 3개다.

     

    - src/main/java/com/my/practice00/springboot/controller/PostsApiController

    - src/main/java/com/my/practice00/springboot/controller/dto/PostsSaveRequestDto

    - src/main/java/com/my/practice00/springboot/service/posts/PostsService

    PostsSaveRequestDto 생성

     

    import com.my.practice00.springboot.domain.posts.Posts;
    import lombok.Builder;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    
    @Getter
    @NoArgsConstructor
    public class PostsSaveRequestDto {
    
        private String title;
        private String content;
        private String author;
    
        @Builder
        public PostsSaveRequestDto(String title, String content, String author){
            this.title = title;
            this.content = content;
            this.author = author;
        }
    
        public Posts toEntity(){
            return Posts.builder()
                    .title(title)
                    .content(content)
                    .author(author)
                    .build();
        }
    }

     

    Dto는 DB에 자료를 쓰거나 읽어올 때 그 데이터를 전달하는 용도로 쓴다.

     

    참고로 DB를 이용하기 위해 Dto를 별도로 만들어서 이용해야 한다.

     

    즉 @Entity가 달린 클래스 해당 프로젝트에서는 Posts클래스를 직접 읽기 쓰기 용도로 쓰지 않아야 한다.

     

    @Entity는 DB와 직접 연결된 클래스로 이걸 수정하면 다른 클래스에도 영향을 줄 수 있다.

     

    그러므로 자료를 다룰 때는 테이블과 연결된 @Entity 클래스 대신 Dto클래스를 만들어야 한다.

    PostsService 생성

     

    import com.my.practice00.springboot.controller.dto.PostsSaveRequestDto;
    import com.my.practice00.springboot.domain.posts.PostsRepository;
    import lombok.RequiredArgsConstructor;
    import org.springframework.stereotype.Service;
    
    import javax.transaction.Transactional;
    
    @RequiredArgsConstructor
    @Service
    public class PostsService {
    
        private final PostsRepository postsRepository;
    
        @Transactional
        public Long save(PostsSaveRequestDto requestDto){
            return postsRepository.save(requestDto.toEntity()).getId();
        }
    
    }

     

    Service클래스는 이전 글에서 쓴대로 Controller로 전달된 요청을 처리하기 위해 사용되는 클래스다.

     

    비즈니스 로직을 직접 처리하는건 아니다.

     

    Transaction을 통해 요청을 구분하고 Domain을 불러와서 요청을 처리한다.

    PostsApiController 생성

     

    import com.my.practice00.springboot.controller.dto.PostsSaveRequestDto;
    import com.my.practice00.springboot.service.posts.PostsService;
    import lombok.RequiredArgsConstructor;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RestController;
    
    @RequiredArgsConstructor
    @RestController
    public class PostsApiController {
    
        private final PostsService postsService;
    
        @PostMapping("/api/v1/posts")
        public Long save(@RequestBody PostsSaveRequestDto requestDto){
            return postsService.save(requestDto);
        }
    
    }

     

    고객의 요청을 처리하는 가장 상위에 있는 레이어의 Controller다.

     

    고객에데 전달받은 데이터를 PostsSaveRequestDto 형식으로 전달받아서 이것을 Service로 넘긴다.

    3개 클래스의 작동 방식

     

    위에서 작성한 클래스를 기반으로 작동방식을 설명하면 위 그림과 같다

     

    글로 좀 더 풀어쓰면 아래와 같다.

     

    - 고객이 글쓰기 요청을 하면 데이터를 PostsApiController로 전달한다.

    - 이때 데이터를 @Entity 클래스를 기반으로 만든 PostsSaveRequestDto형식으로 저장한다.

    - 그리고 이 데이터를 PostsService 클래스로 전달한다.

     

    - 데이터를 전달받은 PostsService는 PostsRepository로 전달한다.

    - 이때 PostsRepository가 자동제공하는 메서드인 save()메서드를 이용한다.

     

    - PostsReposotory 클래스는 전달받은 데이터를 Database에 저장한다.

     

    이때 주의할 점은 앞서 말했듯 @Entity 클래스의 내용은 테이블과 연결되있다

     

    그러므로 데이터를 읽고 쓸 때는 위에서처럼 Dto를 별도로 만들어서 이용해야 한다.

    PostsApiControllerTest - 테스트하기

    이제 위에 작성한 클래스가 제대로 작동하는지 테스트해보도록 하자.

     

     

    "src/test/java/com/my/practice00/springboot/controller" 경로에 PostsApiControllerTest 클래스를 만든다.

     

     

    import com.my.practice00.springboot.controller.dto.PostsSaveRequestDto;
    import com.my.practice00.springboot.domain.posts.Posts;
    import com.my.practice00.springboot.domain.posts.PostsRepository;
    import org.junit.After;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.boot.test.web.client.TestRestTemplate;
    import org.springframework.boot.web.server.LocalServerPort;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.test.context.junit4.SpringRunner;
    
    import java.util.List;
    
    import static org.assertj.core.api.Java6Assertions.assertThat;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    public class PostsApiControllerTest {
    
        @LocalServerPort
        private int port;
    
        @Autowired
        private TestRestTemplate restTemplate;
    
        @Autowired
        private PostsRepository postsRepository;
    
        @After
        public void tearDown() throws Exception{
            postsRepository.deleteAll();
        }
    
        @Test
        public void Posts_등록() throws Exception{
    
            String title = "게시글 제목";
            String content = "게시글 내용";
    
            PostsSaveRequestDto requestDto =
                    PostsSaveRequestDto.builder()
                            .title(title)
                            .content(content)
                            .author("author")
                            .build();
    
            String url = "http://localhost:" + port + "/api/v1/posts";
    
            ResponseEntity<Long> responseEntity = restTemplate.postForEntity(url, requestDto, Long.class);
    
            assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
            assertThat(responseEntity.getBody()).isGreaterThan(0L);
    
            List<Posts> all = postsRepository.findAll();
    
            assertThat(all.get(0).getTitle()).isEqualTo(title);
            assertThat(all.get(0).getContent()).isEqualTo(content);
        }
    
    }

     

    테스트 내용을 간단히 설명하면 다음과 같다.

     

    title, content 내용을 넣은 requestDto를 생성한 다음 이걸 url에 넣고 그 결과값을 전달 받는다.

     

    그리고나서 결과값이 입력한 내용과 동일한지 확인한다.

     

    그 다음 테스트할 때 입력했던 내용을 전부 다 삭제한다.

     

    참고로 여기서 테스트 할 때는 이전의 HelloController와 다른 것을 알 수 있다.

     

    바로 @WebMvcTest를 사용하지 않는 것이다.

     

    왜냐면 @WebMvcTest의 경우 JPA 기능이 작동하지 않기 때문이다.

     

    JPA 기능을 테스트할 떄는 @SpringBootTest와 TestRestTemplate를 사용한다.

     

    내용을 잘 입력하고 테스트하면 아래와 같은 결과를 확인할 수 있다.

     

Designed by Tistory.