Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- 구멍가게코딩단
- 네트워크 설정
- 데비안
- 자료구조와 함께 배우는 알고리즘 입문
- resttemplate
- 티스토리 쿠키 삭제
- 서버설정
- 친절한SQL튜닝
- network configuration
- 코드로배우는스프링웹프로젝트
- 처음 만나는 AI수학 with Python
- 페이징
- 처음 만나는 AI 수학 with Python
- Kernighan의 C언어 프로그래밍
- 스프링 시큐리티
- 자바편
- 리눅스
- 자료구조와함께배우는알고리즘입문
- 스프링부트핵심가이드
- 목록처리
- 선형대수
- baeldung
- 알파회계
- /etc/network/interfaces
- 이터레이터
- iterator
- d
- 코드로배우는스프링부트웹프로젝트
- ㅒ
- GIT
Archives
- Today
- Total
bright jazz music
blog12: 예외처리1 본문
자바에서 기본 제공하는 예외 말고 좀 더 명확하게 의미를 전달하는 고유의 예외 클래스를 만들어서 사용한다.
컨트롤러는 바뀐 것이 없다. PostService.java에서 수정한다.
//PostController.java
///...
@Slf4j
@RestController
@RequiredArgsConstructor
public class PostController {
private final PostService postService;
@PostMapping("/posts")
public void post(@RequestBody @Valid PostCreate request) {
postService.write(request);
// return Map.of();
}
//단건 조회
@GetMapping("/posts/{postId}")
public PostResponse get(@PathVariable Long postId){
PostResponse response = postService.get(postId);
return response;
}
//여러 글 조회(글 목록 가져오기)
// /posts
@GetMapping("/posts")
//글이 너무 많은 경우 비용이 너무 많이 든다.
//DB가 뻗을 수 있음.
//DB -> 애플리케이션 서버로 전달하는 시간, 트래픽 비용이 많이 발생할 수 있다.
//따라서 페이지 설정
// 원래는 int로 받았음
// public List<PostResponse> getList(@RequestParam int page){
// 그러나 사용의 용이성을 위해 pageable을 사용함
// public List<PostResponse> getList(@PageableDefault Pageable pageable){ //1로 넘겨도 0으로 보정해서 넣어줌.
//근데 PageableDefault의 기본 size가 10이라 yml에서 설정해도 먹히지 않는다.
//이 떄는 어노테이션을 그대로 유지하면서 size를 파라미터로 넣어주는 방법이 있다.
//public List<PostResponse> getList(@PageableDefault(size=10) Pageable pageable){
//또는 어노테이션을 빼고 application.yml에서 default-page-size를 설정하여 해결할 수 수있다.
//전에는 Pageable을 사용했으나 여러 요구사항을 수용 할 수 있는 클래스를 사용하기 위해
//postSearch 클래스 사용
public List<PostResponse> getList(@ModelAttribute PostSearch postSearch){//따로 만든 요청클래스 사용하려고 함
// public List<PostResponse> getList(Pageable pageable){
return postService.getList(postSearch);
}
@PatchMapping("/posts/{postId}")
public void edit(@PathVariable Long postId, @RequestBody @Valid PostEdit request){
postService.edit(postId, request);
}
@DeleteMapping("/posts/{postId}")
public void delete(@PathVariable Long postId){
postService.delete(postId);
}
}
서비스.
.orElseThrow() 부분을 수정해준다.
//PostService.java
package com.endofma.blog.service;
import com.endofma.blog.domain.Post;
import com.endofma.blog.domain.PostEditor;
import com.endofma.blog.exception.PostNotFound;
import com.endofma.blog.repository.PostRepository;
import com.endofma.blog.request.PostCreate;
import com.endofma.blog.request.PostEdit;
import com.endofma.blog.request.PostSearch;
import com.endofma.blog.response.PostResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Slf4j
@Service
//@RequiredArgsConstructor
public class PostService {
private final PostRepository postRepository;
public PostService(PostRepository postRepository){
this.postRepository = postRepository;
}
public void write(PostCreate postCreate) {
//PostCreate 일반 클래스 ==> Post 엔티티
Post post = Post.builder()
.title(postCreate.getTitle())
.content(postCreate.getContent())
.build();
postRepository.save(post);
}
//단건 조회
public PostResponse get(Long id) {
Post post = postRepository.findById(id)
// .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 글입니다.")); //있으면 post반환 없으면 에러 반환
// .orElseThrow(() -> new PostNotFound());
.orElseThrow(PostNotFound::new); //위와 같은 표현
//또한 IllegalArgumentException과 같은 자바 기본제공 에러들은 우리의 에러를 명확히 표현해 주지는 못함.
// 이렇게 처리해주면 굳이 test에서 전부 메시지를 확인할 필요가 없다. 이 에러가 발생했다면 글을 찾지 못했다는 뜻이기 때문이다.
//응답 클래스를 분리
return PostResponse.builder()
.id(post.getId())
.title(post.getTitle())
.content(post.getContent())
.build();
}
// public List<Post> getList() {
// return postRepository.findAll();
// }
// public List<PostResponse> getList(Pageable pageable){
public List<PostResponse> getList(PostSearch postSearch){
// return postRepository.findAll(pageable).stream() //pageable
return postRepository.getList(postSearch).stream() //QueryDsl사용
.map(PostResponse::new)
.collect(Collectors.toList());
}
//게시글 수정
@Transactional //알아서 커밋
public void edit(Long id, PostEdit postEdit){
Post post = postRepository.findById(id)
// .orElseThrow(()-> new IllegalArgumentException("존재하지 않는 글입니다."));
.orElseThrow(PostNotFound::new);
// post.setTitle(postEdit.getTitle());
// post.setContent(postEdit.getContent());
// post.change(postEdit.getTitle(), postEdit.getContent());
// postRepository.save(post); 사실상 적어주지 않아도 된다. 대신 @Transactional을 사용
PostEditor.PostEditorBuilder editorBuilder = post.toEditor();
PostEditor postEditor = editorBuilder
.title(postEdit.getTitle())
.content(postEdit.getContent())
.build();
post.edit(postEditor);
}
public void delete(Long id){
Post post = postRepository.findById(id)
// .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 글입니다."));
.orElseThrow(PostNotFound::new);
postRepository.delete(post);
}
}
자바에서 제공하는 IllegalArgumentException 말고 PostNotFound.java를 만들어서 대신 메시지를 송출한다.
//PostNotFound.java
package com.endofma.blog.exception;
//public class PostNotFound extends Exception{
public class PostNotFound extends RuntimeException{ //uncheckedException
//생성자 오버로딩을 해서 실제로 발생한 예외에게 메시지를 부여하면 된다.
private static final String MESSAGE = "존재하지 않는 글입니다.";
public PostNotFound(){
super(MESSAGE);
}
public PostNotFound(Throwable cause){
super(MESSAGE, cause);
}
}
테스트.
test7, 8, 9 참조
//PostServiceTest.java
package com.endofma.blog.service;
import com.endofma.blog.domain.Post;
import com.endofma.blog.exception.PostNotFound;
import com.endofma.blog.repository.PostRepository;
import com.endofma.blog.request.PostCreate;
import com.endofma.blog.request.PostEdit;
import com.endofma.blog.request.PostSearch;
import com.endofma.blog.response.PostResponse;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class PostServiceTest {
@Autowired
private PostService postService;
@Autowired
private PostRepository postRepository;
@BeforeEach
void clean(){
postRepository.deleteAll();
}
@Test
@DisplayName("글 작성")
void test1() {
//given
PostCreate postCreate = PostCreate.builder()
.title("제목입니다.")
.content("내용입니다.")
.build();
//when
postService.write(postCreate);
//then
Assertions.assertEquals(1L, postRepository.count());
Post post = postRepository.findAll().get(0);
assertEquals("제목입니다.", post.getTitle());
assertEquals("내용입니다.", post.getContent());
}
@Test
@DisplayName("글 1개 조회")
void test2(){
//given
Post requestPost = Post.builder()
.title("foo")
.content("bar")
.build();
postRepository.save(requestPost);
//when
PostResponse response = postService.get(requestPost.getId());
//then
Assertions.assertNotNull(response);
assertEquals(1L, postRepository.count());
assertEquals("foo", response.getTitle());
assertEquals("bar", response.getContent());
}
@Test
@DisplayName("글 1페이지 조회")
void test3(){
//given
List<Post> requestPost = IntStream.range(1, 20) //for (int =0; i<30; i++)
.mapToObj(i -> Post.builder()
.title("foo " + i)
.content("bar " + i)
.build())
.collect(Collectors.toList());
postRepository.saveAll(requestPost);
// sql -> select, limit, offset 알아야 함.
// Pageable pageableRequest = PageRequest.of(0, 5, Sort.Direction.DESC, "id");
PostSearch postSearch = PostSearch.builder()
.page(1)
.size(10)
.build();
//when
List<PostResponse> posts = postService.getList(postSearch);
//then
assertEquals(10L, posts.size());
assertEquals("foo 19", posts.get(0).getTitle());
}
@Test
@DisplayName("글 제목 수정")
void test4(){
//given
Post post = Post.builder()
.title("블로그 ")
.content("제이드빌 ")
.build();
postRepository.save(post);
PostEdit postEdit = PostEdit.builder()
.title("블로그 수정")
.content("제이드빌 ")
.build();
//when
postService.edit(post.getId(), postEdit);
//then
Post chengedPost = postRepository.findById(post.getId())
.orElseThrow(() -> new RuntimeException("글이 존재하지 않습니다. id=" + post.getId()));
Assertions.assertEquals("블로그 수정", chengedPost.getTitle());
Assertions.assertEquals("제이드빌 ", chengedPost.getContent());
}
@Test
@DisplayName("글 제목 수정")
void test5(){
//given
Post post = Post.builder()
.title("블로그 ")
.content("제이드빌 ")
.build();
postRepository.save(post);
PostEdit postEdit = PostEdit.builder()
.title("블로그 수정")
.content("부림동 ")
.build();
//when
postService.edit(post.getId(), postEdit);
//then
Post chengedPost = postRepository.findById(post.getId())
.orElseThrow(() -> new RuntimeException("글이 존재하지 않습니다. id=" + post.getId()));
Assertions.assertEquals("블로그 수정", chengedPost.getTitle());
Assertions.assertEquals("부림동 ", chengedPost.getContent());
}
@Test
@DisplayName("게시글 삭제")
void test6(){
//given
Post post = Post.builder()
.title("블로그 ")
.content("제이드빌 ")
.build();
postRepository.save(post);
//when
postService.delete(post.getId());
//then
Assertions.assertEquals(0, postRepository.count());
}
@Test
@DisplayName("글 1개 조회 - 존재하지 않는 글")
void test7(){
//given
Post post = Post.builder()
.title("블로그 ")
.content("제이드빌 ")
.build();
postRepository.save(post);
//when
// IllegalArgumentException e = Assertions.assertThrows(IllegalArgumentException.class, () -> {
// postService.get(post.getId() + 1L);
// }); 앞서 적어준 예외를 처리하지 못하는 경우 메시지를 발생시킴
// Assertions.assertEquals("존재하지 않는 글입니다.", e.getMessage());
//메시지 반환측에서 메시지를 바꿀 때마다 테스트의 메시지를 전부 바꿔줘야 하는 문제가 있음.
//또한 IllegalArgumentException과 같은 자바 기본제공 에러들은 우리의 에러를 명확히 표현해 주지는 못함.
//expected
Assertions.assertThrows(PostNotFound.class, () -> {
// Assertions.assertThrows(IllegalArgumentException.class, () -> { //오류를 보고싶으면 이걸로 테스트
postService.get(post.getId() + 1L);
});
//이렇게 처리해주면 굳이 test에서 전부 메시지를 확인할 필요가 없다. 이 에러가 발생했다면 글을 찾지 못했다는 뜻이기 때문이다.
}
@Test
@DisplayName("게시글 삭제 - 존재하지 않는 글")
void test8(){
//given
Post post = Post.builder()
.title("블로그 ")
.content("제이드빌 ")
.build();
postRepository.save(post);
//then
Assertions.assertThrows(PostNotFound.class, () -> {
postService.delete(post.getId() + 1L);
});
}
@Test
@DisplayName("게시글 수정 - 존재하지 않는 글")
void test9(){
//given
Post post = Post.builder()
.title("블로그 ")
.content("제이드빌 ")
.build();
postRepository.save(post);
PostEdit postEdit = PostEdit.builder()
.title("블로그 수정 ")
.content("부림동 ")
.build();
//expected
Assertions.assertThrows(PostNotFound.class, () -> {
postService.edit(post.getId() + 1L, postEdit);
});
}
}
위에서는 postService.get(post.getId() + 1L) 등을 사용하여 존재하지 않는 글을 조회함으로써 오류를 발생시킬 수 있었다. 이 경우 테스트가 성공한다.
만약 존재하는 글을 조회하도록 postService.get(post.getId())를 사용한다면 테스트가 실패한다. 오류가 날 것을 기대하였지만 오류가 발생하지 않았기 때문이다. 이 경우 아래와 같은 로그가 생성된다.
Expected com.endofma.blog.exception.PostNotFound to be thrown, but nothing was thrown.
org.opentest4j.AssertionFailedError: Expected com.endofma.blog.exception.PostNotFound to be thrown, but nothing was thrown.
at app//org.junit.jupiter.api.AssertThrows.assertThrows(AssertThrows.java:71)
at app//org.junit.jupiter.api.AssertThrows.assertThrows(AssertThrows.java:37)
at app//org.junit.jupiter.api.Assertions.assertThrows(Assertions.java:3082)
PostNotFound 예외가 발생할 것으로 기대하였지만 아무 것도 발생하지 않았다는 의미이다.
'Projects > blog' 카테고리의 다른 글
blog12: 예외처리 3 (0) | 2023.01.14 |
---|---|
blog12: 예외처리2 (0) | 2023.01.13 |
blog11: 게시글 삭제 (0) | 2023.01.10 |
blog10: 게시글 수정 2 (오류수정, 보충) (0) | 2023.01.09 |
blog10: 게시글 수정 1 (0) | 2023.01.09 |
Comments