[스프링부트] 익명블로그 만들어보기v3

송송승현's avatar
Nov 29, 2024
[스프링부트] 익명블로그 만들어보기v3

예외처리를 활용한 리팩토링

변경사항

환경 설정 : Springboot-starter-aop와 spring-boot-starter-validation 추가

plugins { id 'java' id 'org.springframework.boot' version '3.3.5' id 'io.spring.dependency-management' version '1.1.6' } group = 'com.example' version = '0.0.1-SNAPSHOT' java { toolchain { languageVersion = JavaLanguageVersion.of(21) } } configurations { compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral() } dependencies { // https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop implementation group: 'org.springframework.boot', name: 'spring-boot-starter-aop', version: '3.3.5' // https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation implementation group: 'org.springframework.boot', name: 'spring-boot-starter-validation', version: '3.3.5' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-mustache' implementation 'org.springframework.boot:spring-boot-starter-web' compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' runtimeOnly 'com.h2database:h2' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } tasks.named('test') { useJUnitPlatform() }

Board 패키지

DTO - Request에서 validation의 @NotBlank를 사용

public class BoardRequest { @Data //getter,setter, toString public static class SaveDTO{ @NotBlank(message = "title을 입력해주세요") private String title; @NotBlank(message = "content를 입력해주세요.") private String content; public Board toEntity(){ Board board = new Board(null,title, content, null); return board; } } @Data //getter,setter, toString public static class updateDTO{ @NotBlank private String title; @NotBlank private String content; } } public class BoardResponse { @Data public static class updateFormDTO{ private int id; private String title; private String content; private String createdAt; public updateFormDTO(Board board) { this.id = board.getId(); this.title = board.getTitle(); this.content = board.getContent(); this.createdAt = board.getCreatedAt().toString();// TODO: 2024.11.18 형태로 변경 } } @Data public static class DetailDTO{ private int id; private String title; private String content; private String createdAt; public DetailDTO(Board board) { this.id = board.getId(); this.title = board.getTitle(); this.content = board.getContent(); this.createdAt = board.getCreatedAt().toString();// TODO: 2024.11.18 형태로 변경 } } @Data public static class DTO{ private int id; private String title; public DTO(Board board) { this.id = board.getId(); this.title = board.getTitle(); } } }

Controller - validation의 @Valid 사용

import com.example.blog._core.error.ex.Exception400; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.validation.Errors; import org.springframework.validation.FieldError; import org.springframework.web.bind.annotation.*; import java.util.List; // Controller의 책임 : 외부 클라이언트의 요청을 받고 그 요청에 대한 응답을 함 @Controller @RequiredArgsConstructor public class BoardController { private final BoardService boardService; //was scope : application, session, request, page // session // request @GetMapping("/") public String list(Model model) { // DS(request 객체를 model이라는 객체로 랩핑해서 전달.) List<BoardResponse.DTO> boardList = boardService.게시글목록보기(); model.addAttribute("models", boardList); return "list"; } @GetMapping("/save-form") public String saveForm() { return "save-form"; } @GetMapping("/board/{id}/update") public String updateForm(@PathVariable Integer id, Model model) { model.addAttribute("model",boardService.게시글수정화면보기(id)); return "update-form"; } @GetMapping("/board/{id}") public String detail(@PathVariable("id") Integer id, Model model) { model.addAttribute("model",boardService.게시글상세보기(id)); return "detail"; } @PostMapping("/board/save") public String saveV2(@Valid BoardRequest.SaveDTO saveDTO, Errors errors) { boardService.게시글쓰기(saveDTO); return "redirect:/"; } @PostMapping("/board/{id}/delete") public String delete(@PathVariable("id") int id) { boardService.게시글삭제(id); return "redirect:/"; } @PostMapping("/board/{id}/update") public String update(@PathVariable("id") int id, @Valid BoardRequest.updateDTO updateDTO, Errors errors) { boardService.게시글수정(id,updateDTO); return "redirect:/"; } }

Service

import com.example.blog._core.error.ex.Exception404; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.List; import java.util.Optional; @Service @RequiredArgsConstructor public class BoardService { private final BoartRepository boardRepository; public List<BoardResponse.DTO> 게시글목록보기(){ return boardRepository.findAll().stream() .map(BoardResponse.DTO::new) .toList(); }; public BoardResponse.updateFormDTO 게시글수정화면보기(int id) { return new BoardResponse.updateFormDTO(boardRepository.findById(id).orElseThrow(() ->new Exception404("해당 ID의 게시글이 없습니다. : " + id))); } public BoardResponse.DetailDTO 게시글상세보기(int id) { return new BoardResponse.DetailDTO(boardRepository.findById(id).orElseThrow(() ->new Exception404("해당 ID의 게시글이 없습니다. : " + id))); } @Transactional public void 게시글쓰기(BoardRequest.SaveDTO saveDTO) { boardRepository.save(saveDTO.toEntity()); } @Transactional public void 게시글삭제(int id) { boardRepository.delete(id); } @Transactional public void 게시글수정(int id, BoardRequest.updateDTO updateDTO) { Board board =boardRepository.findById(id).orElseThrow(() ->new Exception404("해당 ID의 게시글이 없습니다." + id)); board.update(updateDTO.getTitle(), updateDTO.getContent()); } }

Repository

import jakarta.persistence.EntityManager; import jakarta.persistence.Query; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; import java.util.List; import java.util.Optional; // Repository 책임 : DB 상호작용 @Repository @RequiredArgsConstructor public class BoartRepository { //JPA는 EntityManager로 DB에 접근한다.(JAVA에서 DBConnection) private final EntityManager em; public List<Board> findAll() { return em.createQuery("select b from Board b order by b.id desc",Board.class).getResultList(); } public Optional<Board> findById(int id) { return Optional.ofNullable(em.find(Board.class, id)); } public void save(Board board) { em.persist(board); } public void delete(int id){ em.createQuery("delete from Board b where b.id=:id").setParameter("id", id).executeUpdate(); } }

Util 패키지

Aop - Controller의 @Vaild를 Errors에 담아 에외처리

import com.example.blog._core.error.ex.Exception400; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; import org.springframework.validation.Errors; @Aspect @Component public class MyValidationAspect { @Before("@annotation(org.springframework.web.bind.annotation.PostMapping)") // Around는 JoinPoint가 아니라 ProceedingJoinPoint 사용 public void validationCheck(JoinPoint jp) throws Throwable { Object[] args =jp.getArgs(); for(Object arg : args) { if(arg instanceof Errors) { Errors errors = (Errors) arg; if (errors.hasErrors()) { String errMsg = errors.getFieldErrors().get(0).getField() +" : "+errors.getFieldErrors().get(0).getDefaultMessage(); throw new Exception400(errMsg); } } } } }

MyControllerAdvice

public class Exception400 extends RuntimeException{ public Exception400(String msg) { super(msg); } } public class Exception404 extends RuntimeException{ public Exception404(String message) { super(message); } } import com.example.blog._core.error.ex.Exception400; import com.example.blog._core.error.ex.Exception404; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; // 에러 처리 @ControllerAdvice public class MyControllerAdvice { @ResponseBody @ExceptionHandler(Exception400.class) public String err400(Exception400 e) { System.out.println("err400"); String body = """ <script> alert('${msg}'); history.back(); </script> """.replace("${msg}", e.getMessage()); return body; } @ResponseBody @ExceptionHandler(Exception404.class) public String err404(Exception404 e) { System.out.println("err404"); String body = """ <script> alert('${msg}'); history.back(); </script> """.replace("${msg}", e.getMessage()); return body; } }
 
Share article

송승현의 블로그