[백엔드 첫 걸음] API 등록 기능 실습 (스프링)

khyojun·2022년 8월 13일
0
post-thumbnail

이제부터 등록, 수정, 조회 기능들을 api로 구현을 할것인데 진짜 하나씩 차근차근 이해해보도록 하자.


📂/main/java/web/PostsApiController

@RequiredArgsConstructor
@RestController
public class PostApiController {

    private final PostsService postsService;

    @PostMapping("/api/v1/posts")
    public Long save(@RequestBody PostsSaveRequestDto requestDto){
        return postsService.save(requestDto);
    }

}

📂/main/java/service/posts/PostsService

@Service
@RequiredArgsConstructor
public class PostsService {
    private final PostRepository postRepository;



    @Transactional
    public Long save(PostsSaveRequestDto requestDto){
        return postRepository.save(requestDto.toEntity()).getId();
    }
}

@RequiredArgsConstructor: 필드에 있는 모든 final을 인자값으로 하는 생성자 생성
@Service: 컴포넌트로 빈 생성

이후 Controller <-> Service에 사용할 Dto 클래스도 생성을 한다.

📂/main/java/web/dto/PostsSaveRequestDto

@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();
    }
}

@NoArgsConstructor: 인자가 없는 생성자를 생성한다.

위 코드에서부터 내려다보게 되면 DB의 Entity클래스인 domain에 있는 Posts를 사용하지 않고 따로 PostsSaveRequestDto를 사용하여서 등록을 시켜주는 역할을 계속해서 진행을 하고 있는 것처럼 볼 수가 있다. 실제로도 Entity클래스와 정말 유사한 형태처럼 생겼다. 왜 이렇게 사용을 해야 할까?

이유를 먼저 보게 된다면은 절대로 Entity 클래스를 Request/Response 클래스로 사용해서는 안된다. 절대로!

🏸 WHY?

  • Entity 클래스는 데이터베이스와 맞닿은 핵심 클래스입니다. Entity클래스를 기준으로 하여 테이블이 생성되고, 스키마도 변경이 됩니다.
  • 그러한 곳을 화면변경때문에 계속 변경을 하기에는 너무나도 효율이 좋지 않습니다.(즉 배보다 배꼽이 더 큰 꼴)
  • Entity 클래스가 변경되면 여러 클래스에 영향을 끼치지만 Request/Response용 Dto(데이터 교환 포맷)는 View를 위한 클래스라 진짜 진짜 자주 변경이 필요하다.
  • View Layer와 DB Layer의 역할 분리를 철저하게 하는 것이 좋다. 실제로 Controller에서 결과값으로 여러 테이블을 조인해서 줘야 할 경우가 빈번하므로 Entity 클래스만으로 표현하기 어려운 경우가 많다.
    ❗ 그래서 꼭 Entity 클래스와 Controller에서 쓸 Dto는 분리해서 사용을 해야 한다.

📕 요약을 해보자면

Entity 클래스를 고치기에는 그 클래스는 너무나도 중대한 역할이기 때문에 고치게 되면 다른 곳과도 연결이 되어버리기 때문에 골치 아프게된다. 그러므로 Don't Touch! 심지어 Dto클래스는 화면과 연결된 클래스이기 때문에 변경도 많이 한다. 그니까 변경 절대 X!

그럼 이제 잘 돌아가는지 확인해보기 위하여서 Test를 돌려보자.

📂/test/java/web/PostApiControllerTest.java

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class PostApiControllerTest{

@LocalServerPort
private int port;

@Autowired
private TestRestTemplate restTemplate;

@Autowired
private PostRepository postRepository;

@After
public void tearDown() throws Exception{
	postRepository.deleteAll();
}

@Test
public void Posts_등록() throws Exception{
//given
String title="title";
String content="content";

PostsSaveRequestDto requestDto= PostsSaveRequestDto.builder().title(title).content(content).author("author").build();

String url="http://localhost:" + port +"/api/v1/posts";

//when

ResponseEntity<Long> responseEntity = restTemplate.postForEntity(url, requestDto, Long.class);

//then
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
asserThat(responseEntity.getBody()).isGreaterThan(0L);


List<Posts> all = postRepository.findAll();
assertThat(all.get(0).getTitle()).isEqualTo(title);
assertThat(all.get(0).getContent()).isEqualTo(content);


}





}
  • @Runwith(SpringRunner.class)
    • 그냥 @SpringBootTest를 부르게 되면 너무 무겁다. 왜냐? application context를 전부 로딩하기 떄문에("그냥 애플리케이션 하나를 테스트 할 때 실행")
    • 그래서 필요한 것들만 가져와 실행하기 위하여 @Runwith(Spring.class) 사용
  • TestRestTemplate
    • Spring Boot에서 컨트롤러를 테스트하기 위하여 사용, 스프링에서 제공하는 http 통신을 유용하게 쓸 수 있는 템플릿
      • MockMvc와의 차이점: 서블릿 컨테이너의 실행여부(MockMvc는 실행 X, TestRestTemplate는 실행)
      • MockMvc: 서버의 입장에서 구현한 API를 통해 비즈니스 로직에 문제가 없는지 테스트
      • TestRestTemplate: 클라이언트 입장에서 사용할 때 문제가 없는지 테스트.
  • @SpringBootTest(webEnvironment=SpirngBootTest.WebEnvironment.RANDOM_PORT)
    • SpringBootTest는 실제 애플리케이션을 전부 테스트시킴, RANDOM_PORT는 스프링 부트의 내장 서버를 랜덤 포트로 띄우기위함.
  • ResponseEntity
    • ResponseEntity란, HttpEntity 클래스를 상속받고 결과 데이터와 Http 상태 코드를 직접 제어할 수 있는 클래스이다.

정리를 하긴 하였지만 실제로 정리를 못한 부분도 많이 있다. 아직 제대로 이해가 잘 되지 않는 부분도 있지만 Http공부 꼭 병행하면서 해야겠다는 생각이 가장 많이 들게 되었다.

✨ 요약

  1. Entity클래스와 충돌시키지 않기 위하여서 Controller에서 쓸 Dto클래스를(주로 View관련하여 많이 사용) 사용한다. Entity는 웬만하면 절대 건들지 않는 방향으로...
  2. Test관련 어노테이션 및 모르는 내용들 정리

참고

  1. 이동욱 저자의 <스프링 부트와 AWS로 혼자 구현하는 웹 서비스>
  2. https://stackoverflow.com/questions/58901288/springrunner-vs-springboottest
  3. https://4whomtbts.tistory.com/128
  4. https://shlee0882.tistory.com/202
profile
코드를 씹고 뜯고 맛보고 즐기는 것을 지향하는 개발자가 되고 싶습니다

0개의 댓글