[등록 API 만들기]
API를 만들기 위한 클래스
- Request 데이터를 받을 Dto
- API 요청을 받을 Controller
- 트랜잭션, 도메인 기능간의 순서를 보장하는 Service(비지니스 로직 처리 x)
Spring 웹 계층
1. Web Layer
- 컨트롤러와 JSP등의 뷰 템플릿 영역
- 필터, 인터셉터등 외부 요청과 응답에 대한 전반적인 영역
2. Service Layer
- @Service에 사용되는 서비스 영역
- Controller와 Dao의 중간 영역에서 사용
- @Transactonal이 사용되어야 하는 영역
3. Repository Layer
- Database와 같이 데이터 저장소에 접근하는 영역
- Dao영역
4. Dtos
- 계층간에 데이터 교환을 위한 객체
- 뷰에서 사용될 객체나 Repository Layer에서 결과로 넘겨준 객체
5. Domain Model
- 비지니스 처리를 담당하는 곳
- 도메인이라 불리는 개발 대상을 모든사람이 동일한 관점에서 이해할 수 있고 공유할 수 있도록 단순화 시킨 것
- @Entity가 사용된 영역 역시 도메인 모델
src/main/java/com/intellij/project/springboot/web/PostsApiController
@RequiredArgsConstructor
@RestController
public class PostsApiController {
private final PostsService postsService;
@PostMapping("/api/v1/posts")
public Long save(@RequestBody PostsSaveRequestDto requestDto){
return postsService.save(requestDto);
}
}
src/main/java/com/intellij/project/springboot/service/PostsService
@RequiredArgsConstructor
@Service
public class PostsService {
private final PostsRepository postsRepository;
@Transactional
public Long save(PostsSaveRequestDto requestDto) {
return postsRepository.save(requestDto.toEntity()).getId();
}
}
- @Autowired로 Bean을 주입받지 않고 생성자로 주입받는다.
- @RequiredArgsConstructor
- final이 선언된 모든 필드를 인자값으로 하는 생성자를 생성해줌
- 해당 클래스의 의존성 관계가 변경될 때마다 생성자 코드를 수정 안해줘도 됨
src/main/java/com/intellij/project/springboot/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();
}
}
- Entity와 매우 유사한 클래스지만 따로 생성
- Entity 클래스는 DB와 맞닿아 있는 핵심 클래스 이기 때문에 Request/Response 클래서 사용하면 안됨
src/test/java/com/intellij/project/springboot/web/PostsApiControllerTest
@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 = "title";
String content = "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);
}
}
- insert 테스트 클래스