일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- SpringBean
- API
- tdo
- test case
- Gradle
- SpringDataJPA
- 해싱
- SpringSecurity
- TDD
- gitignore
- SpringFramework
- springboot
- JPA
- 프로그래머스
- DynamicProgramming
- rombok
- 알고리즘 #코딩테스트 #프로그래머스 #C++ #vector #string
- JPA autiding
- DB
- C++
- baekjoon
- 알고리즘
- Java
- index.html
- Comparable
- kotlin
- testcode
- mustache
- 코딩테스트
- spring
- Today
- Total
천천히, 한결같이
[Spring] API 생성하기(1) 본문
이동욱 님의 스프링 부트와 AWS로 혼자 구현하는 웹 서비스책을 공부하며 정리한 내용입니다. 틀린 정보가 있을 수 있으니 주의하시고 댓글로 남겨주시길 바랍니다.
API를 만들기 위한 클래스
API를 만들기 위해선 아래 3개의 클래스가 필요합니다.
- Request 데이터를 받을 DTO
- API 요청을 받을 Container
- 트랜잭션, 도메인 기능 간의 순서를 보장하는 Service
Service클래스는 오직 트랜젝션, 도메인 간 순서 보장의 역할만 합니다.
게시물 등록 기능 만들기
우선 PostsApiController
를 web
패키지에, PostsSaveRequestDto
를 web.dto
패키지에, PostsService
를 service.posts
패키지에 생성합니다. 각 클래스의 코드는 아래와 같습니다.
- PostsApiController
package com.hwanld.book.springboot.web;
import com.hwanld.book.springboot.service.posts.PostsService;
import com.hwanld.book.springboot.web.dto.PostsSaveRequestDto;
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);
}
}
- PostsService
package com.hwanld.book.springboot.service.posts;
import com.hwanld.book.springboot.domain.post.PostsRepository;
import com.hwanld.book.springboot.web.dto.PostsSaveRequestDto;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@RequiredArgsConstructor
@Service
public class PostsService {
private final PostsRepository postsRepository;
@Transactional
public Long save(PostsSaveRequestDto requestDto) {
return postsRepository.save(requestDto.toEntity()).getId();
}
}
스프링에서 Bean을 주입 받는 방식에는 총 3가지가 있습니다. @Autowired
, setter
, Constructor(생성자)
를 사용해서 Bean을 주입 받는 방식이 있습니다. (이는 Dependency Injection, 의존성 주입과 연관이 있습니다!)
위 코드에서는 @RequiredArgsConstructor
애노테이션 때문에 final이 선언된 모든 필드를 인자값으로 하는 생성자를 롬복의 @RequiredArgsConstructor가 대신 생성해 준 것 입니다.
- PostsSaveRequestDto
package com.hwanld.book.springboot.web.dto;
import com.hwanld.book.springboot.domain.post.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();
}
}
마치 Entity 클래스와 거의 유사한 것을 알 수 있는데 절때 Entity 클래스를 Request/Responce 클래스로 사용해서는 안됩니다! Entity 클래스는 데이터베이스와 맞닿은 핵심 클래스입니다. 사소한 로직을 해결하기 위해서 Entity클래스를 변경하게되면 Entity클래스를 중심으로 동작하는 수많은 서비스 클래스나 비즈니스 로직들에서 문제가 생길 수 있기 때문에 Entity클래스는 Entity클래스의 기능만 할 수 있도록 하고, 위와 같이 다른 클래스를 통해서 로직을 해결하는 것이 좋습니다.
위에 작성한 3개의 클래스가 정상적으로 작동하는지 점검하기 위해서 아래와 같은 테스트 코드를 작성합니다.
- PostsApiControllerTest
package com.hwanld.book.springboot.web;
import com.hwanld.book.springboot.domain.post.Posts;
import com.hwanld.book.springboot.domain.post.PostsRepository;
import com.hwanld.book.springboot.web.dto.PostsSaveRequestDto;
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.Assertions.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{
//given
String title = "title";
String content = "content";
PostsSaveRequestDto requestDto = PostsSaveRequestDto.builder()
.title(title)
.content(content)
.author("author")
.build();
String url = "http://localgost:" + port + "/api/v1/posts";
//when
ResponseEntity<Long> responseEntity = restTemplate.postForEntity(url, requestDto, Long.class);
//then
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);
}
}
Api Controller
를 테스트하는데 HelloControler와는 다르게 @WebMVC
를 사용하지 않았습니다. @WebMvcTest
의 경우는 JPA 기능이 작동하지 않기 때문인데, JPA 기능까지 한번에 테스트할 때는 @SpringBootTest
와 TestRestTemplate
를 사용하면 됩니다.
'Spring' 카테고리의 다른 글
[Spring] JPA Auditing으로 생성시간, 수정시간 자동화하기 (0) | 2021.12.31 |
---|---|
[Spring] API 생성하기(2) (0) | 2021.12.30 |
[Spring] 프로젝트에 Spring Data JPA 적용하고 테스트 코드 작성하기 (0) | 2021.12.27 |
[Spring] DTO와 롬복 만들고 적용하기 (0) | 2021.12.26 |
[Spring] 컨트롤러 생성하고 테스트 코드로 검증하기 (0) | 2021.12.25 |