서비스에서는 비지니스 로직을 처리하는 것이 아니라 트랜잭션, 도메인 간 순서 보장의 역할만 담당한다.
1) Service 및 DTO/컨트롤러 클래스 생성
2) PostsApiController
3) PostsService
2)번과 3)번에서 @RequiredArgsConstructor 에서 생성자를 해결하며, final이 선언된 모든 필드를 인자 값으로 하는 생성자를 @RequiredArgsConstructor가 대신 생성
생성자를 안 쓰고 롬복 어노테이션을 쓰는 이유?
해당 클래스의 의존성 관계가 변경될 때마다 생성자 코드를 계속해서 수정하는 번거로움을 해결하기 위함
스프링에서 빈을 주입받는 방식
1) @Autowired
2) setter
3) 생성자
가장 권장하는 방식은 생성자로 Bean 객체를 주입받는 방식
4) PostsSaveRequestDto
Entity 클래스와 거의 유사한 형태이지만 Dto 클래스를 추가로 생성해줌
=> Entity 클래스를 Request/ Response 클래스로 사용해서는 안됨
Entity 클래스는 DB와 맞닿은 핵심 클래스로, 이 클래스를 기준으로 테이블이 생성되고, 스키마가 변경된다. 화면 변경은 사소한 기능 변경인데, 이를 위해 테이블과 연결된 Entity 클래스를 변경하는 것은 너무 큰 변경
Entity 클래스가 변경되면 여러 클래스에 영향을 끼치지만, Request와 Response용 Dto는 View를 위한 클래스라 정말 자주 변경이 필요함
View Layer와 Db Layer의 역할 분리를 철저하게 하는 것이 좋음
5) 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 {
//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);
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(responseEntity.getBody()).isGreaterThan(0L);
//then
List<Posts> all = postsRepository.findAll();
assertThat(all.get(0).getTitle()).isEqualTo(title);
assertThat(all.get(0).getContent()).isEqualTo(content);
}
6) 테스트 코드 실행 결과 및 쿼리 로그
1) PostsApiController api 만들기
2) PostsResponseDto
3) PostsUpdateRequestDto
4) Posts
5) PostsService
Update 기능에서 쿼리를 날리는 부분이 없음 => JPA의 영속성 컨텍스트 때문
영속성 컨텍스트
JPA 엔티티 매니저가 활성화된 상태로 트랜잭션 안에서 데이터베이스에서 데이터를 가져오면 이 데이터는 영속성 컨텍스트가 유지된 상태가 된다.
=> 이 상태에서 해당 데이터의 값을 변경하면 트랜잭션이 끝나는 시점에 해당 테이블에 변경분을 반영한다.
=> 즉, 엔티티 객체의 값만 변경하면 별도로 Update 쿼리를 날릴 필요가 없다. 이를 더티체킹 이라고 한다.
6) PostsApiControllerTest
@Test
public void Posts_수정된다() throws Exception {
//given
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);
//when
ResponseEntity<Long> responseEntity = restTemplate.exchange(url, HttpMethod.PUT,requestEntity, 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(expectedTitle);
assertThat(all.get(0).getContent()).isEqualTo(expectedContent);
}
7) 테스트 코드 실행 후 결과
1) application.properties 설정
spring.h2.console.enabled=true
2) Application의 Main 메소드 실행
3) h2 콘솔 설정
로그인 화면
Connect 후 화면
4) 전체 데이터 확인
5) insert
6) select