카테고리 없음
[Project][Springboot] api 활용
321
2021. 5. 20. 21:19
- api를 이용해서 등록, 수정, 조회를 해보려고 한다. c(r)ud
책에서 Layer에 대해 설명한 내용은 아래와 같다. 내가 스프링으로 이해하고 도식화해 본 것이라 Mapper랑 VO는 갈길을 잃었다.
여기서 Business모델 처리하는 Service계층은 데이터 접근과 웹 구현 사이에서 중간다리역할을 함
실습>>
이렇게 클래스 생성
먼저 PostsApiController
package co.kr.project2.web;
import co.kr.project2.domain.posts.PostService;
import co.kr.project2.web.dto.PostSaveRequestDto;
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 PostService postService;
@PostMapping("/api/v1/posts")
public Long save(@RequestBody PostSaveRequestDto requestDto){
return postService.save(requestDto);
}
}
- RequiredArgsConstructor를 쓰면 롬복에서 final을 쓴 요소들을 자동으로 생성자를 만들어준다
PostsService
package co.kr.project2.domain.posts;
import co.kr.project2.web.dto.PostSaveRequestDto;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
@RequiredArgsConstructor
@Service
public class PostService {
private final PostsRepository postsRepository;
@Transactional
public Long save(PostSaveRequestDto requestDto){
return postsRepository.save(requestDto.toEntity()).getId();
}
}
PostsSaveRequestDto
package co.kr.project2.web.dto;
import co.kr.project2.domain.posts.Posts;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor
public class PostSaveRequestDto {
private String title;
private String content;
private String author;
@Builder
public PostSaveRequestDto(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();
}
}
- 위의 코드는 Posts(@Entity) 코드와 매우 비슷하다.
- 그러나 Entity클래스는 DB접근할 때 기준이 돼야 하므로 변경하면 안 되니까 Entity와 Dto클래스를 분리해야 한다.
테스트 PostsApiControllerTest (test - web밑에 만듦)
package co.kr.project2.web;
import co.kr.project2.domain.posts.Posts;
import co.kr.project2.domain.posts.PostsRepository;
import co.kr.project2.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_save() 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);
}
}
- 랜덤포트 사용해서 save기능이 잘 돌아가는지 테스트한다.
잘 돌아가니까 Read , Update 기능 추가해서 결과물
PostsApiController
package co.kr.project2.web;
import co.kr.project2.domain.posts.PostService;
import co.kr.project2.web.dto.PostsResponseDto;
import co.kr.project2.web.dto.PostsSaveRequestDto;
import co.kr.project2.web.dto.PostsUpdateRequestDto;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
@RequiredArgsConstructor
@RestController
public class PostsApiController {
private final PostService postService;
@PostMapping("/api/v1/posts")
public Long save(@RequestBody PostsSaveRequestDto requestDto){
return postService.save(requestDto);
}
@PutMapping("/api/v1/posts/{id}")
public Long update(@PathVariable Long id, @RequestBody PostsUpdateRequestDto requestDto){
return postService.update(id, requestDto);
}
@GetMapping("/api/v1/posts/{id}")
public PostsResponseDto findById(@PathVariable Long id){
return postService.findById(id);
}
}
PostsResponseDto
package co.kr.project2.web.dto;
import co.kr.project2.domain.posts.Posts;
import lombok.Getter;
@Getter
public class PostsResponseDto {
private Long id;
private String title;
private String content;
private String author;
public PostsResponseDto(Posts entity) {
this.id = entity.getId();
this.title = entity.getTitle();
this.content = entity.getContent();
this.author = entity.getAuthor();
}
}
entity를 사용하면 필드의 일부만 가져와서 쓰도록 할 수 있다.
PostsUpdateRequestDto
package co.kr.project2.web.dto;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor
public class PostsUpdateRequestDto {
private String title;
private String content;
@Builder
public PostsUpdateRequestDto(String title, String content) {
this.title = title;
this.content = content;
}
}
Dto가 그냥 데이터만 담아주는 것이 아니라 빌더 역할도 하고 있다.
Posts
package co.kr.project2.domain.posts;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Getter
@NoArgsConstructor
@Entity
public class Posts {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(length=500, nullable = false)
private String title;
@Column(columnDefinition = "TEXT", nullable = false)
private String content;
private String author;
@Builder
public Posts(String title, String content, String author){
this.title = title;
this.content = content;
this.author = author;
}
public void update(String title, String content){
this.title = title;
this.content=content;
}
}
PostsService
package co.kr.project2.domain.posts;
import co.kr.project2.web.dto.PostsResponseDto;
import co.kr.project2.web.dto.PostsSaveRequestDto;
import co.kr.project2.web.dto.PostsUpdateRequestDto;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
@RequiredArgsConstructor
@Service
public class PostService {
private final PostsRepository postsRepository;
@Transactional
public Long save(PostsSaveRequestDto requestDto){
return postsRepository.save(requestDto.toEntity()).getId();
}
@Transactional
public Long update(Long id, PostsUpdateRequestDto requestDto){
Posts posts = postsRepository.findById(id)
.orElseThrow(()->new IllegalArgumentException("해당 글이 없습니다 id = "+id));
posts.update(requestDto.getTitle(), requestDto.getContent());
return id;
}
public PostsResponseDto findById(Long id){
Posts entity = postsRepository.findById(id)
.orElseThrow(()->new IllegalArgumentException("해당 글이 없습니다 id = "+id));
return new PostsResponseDto(entity);
}
}
그리고 테스트 PostsApiControllerTest
package co.kr.project2.web;
import co.kr.project2.domain.posts.Posts;
import co.kr.project2.domain.posts.PostsRepository;
import co.kr.project2.web.dto.PostsSaveRequestDto;
import co.kr.project2.web.dto.PostsUpdateRequestDto;
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.HttpEntity;
import org.springframework.http.HttpMethod;
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_save() 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);
}
@Test
public void posts_update() throws Exception{
Posts savedPosts = postsRepository.save(Posts.builder()
.title("title").content("content").author("author").build());
Long updateId = savedPosts.getId();
String expectedTitle = "title2";
String expectedContent = "content2";
PostsUpdateRequestDto requestDto = PostsUpdateRequestDto.builder()
.title(expectedTitle).content(expectedContent).build();
String url = "http://localhost:"+port+"/api/v1/posts/"+updateId;
HttpEntity<PostsUpdateRequestDto> requestEntity = new HttpEntity<>(requestDto);
ResponseEntity<Long> responseEntity = restTemplate.exchange(url, HttpMethod.PUT, requestEntity, Long.class);
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(responseEntity.getBody()).isGreaterThan(0L);
List<Posts> all = postsRepository.findAll();
assertThat(all.get(0).getTitle()).isEqualTo(expectedTitle);
assertThat(all.get(0).getContent()).isEqualTo(expectedContent);
}
}