[Spring Boot] 11. Spring Boot Project (Blog v2 - jpa)_10.Exception (Custom)

김미숙's avatar
Jul 22, 2025
[Spring Boot] 11. Spring Boot Project (Blog v2 - jpa)_10.Exception (Custom)

401 → 인증오류

GlobalExceptionHandler

LoveController는 Ajax 통신이므로 javascript가 아닌 json 형식으로 return 필요
package shop.mtcoding.blog._core.error; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import shop.mtcoding.blog._core.error.ex.*; import shop.mtcoding.blog._core.util.Resp; @RestControllerAdvice // data return public class GlobalExceptionHandler { //401 -> 인증 안됨 @ExceptionHandler(Exception401.class) public String ex401(Exception401 e) { // catch 자리 String html = """ <script> alert('${msg}') location.href="/login-form"; </script> """.replace("${msg}", e.getMessage()); return html; } @ExceptionHandler(ExceptionApi401.class) public Resp<?> exApi401(ExceptionApi401 e) { // catch 자리 return Resp.fail(401, e.getMessage()); } }

Exception401

package shop.mtcoding.blog._core.error.ex; public class Exception401 extends RuntimeException { public Exception401(String message) { super(message); } }

ExceptionApi401

package shop.mtcoding.blog._core.error.ex; public class ExceptionApi401 extends RuntimeException { public ExceptionApi401(String message) { super(message); } }

UserController

package shop.mtcoding.blog.user; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import shop.mtcoding.blog._core.error.ex.Exception401; import shop.mtcoding.blog._core.util.Resp; import java.util.Map; @RequiredArgsConstructor @Controller public class UserController { private final UserService userService; private final HttpSession session; @GetMapping("/join-form") public String joinForm() { return "user/join-form"; } @PostMapping("/join") public String join(UserRequest.JoinDTO joinDTO) { userService.회원가입(joinDTO); return "redirect:/login-form"; } @GetMapping("/check-username-available/{username}") public @ResponseBody Resp<?> checkUsernameAvailable(@PathVariable("username") String username) { Map<String, Object> dto = userService.유저네임중복체크(username); return Resp.ok(dto); } @GetMapping("/login-form") public String loginForm() { return "user/login-form"; } @PostMapping("/login") public String login(UserRequest.LoginDTO loginDTO, HttpServletResponse response) { User sessionUser = userService.로그인(loginDTO); session.setAttribute("sessionUser", sessionUser); if (loginDTO.getRememberMe() == null) { Cookie cookie = new Cookie("username", null); cookie.setMaxAge(0); // 즉시 삭제 response.addCookie(cookie); } else { Cookie cookie = new Cookie("username", loginDTO.getUsername()); cookie.setMaxAge(60 * 60 * 24 * 7); // cookie 하루동안 유지 response.addCookie(cookie); } return "redirect:/"; } @GetMapping("/logout") public String logout() { session.invalidate(); return "redirect:/login-form"; } @GetMapping("/user/update-form") public String updateForm() { // 인증로직 User sessionUser = (User) session.getAttribute("sessionUser"); if (sessionUser == null) throw new Exception401("인증이 필요합니다."); // ViewResolver -> prefix = /templates/ suffix = .mustache return "user/update-form"; } @PostMapping("/user/update") public String update(UserRequest.UpdateDTO updateDTO) { // 인증로직 User sessionUser = (User) session.getAttribute("sessionUser"); if (sessionUser == null) throw new Exception401("인증이 필요합니다."); User user = userService.회원정보수정(updateDTO, sessionUser.getId()); // session 동기화 -> 동기화 안해주면 바꾸기 전 정보를 보게 된다 session.setAttribute("sessionUser", user); return "redirect:/"; } }

UserService

package shop.mtcoding.blog.user; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import shop.mtcoding.blog._core.error.ex.Exception400; import shop.mtcoding.blog._core.error.ex.Exception401; import shop.mtcoding.blog._core.error.ex.Exception404; import java.util.HashMap; import java.util.Map; // 비즈니스 로직, 트랜잭션 처리, DTO 완료 @RequiredArgsConstructor @Service public class UserService { private final UserRepository userRepository; // 401 > 인증 403 > 권한 404 > 자원 못찾음 @Transactional public void 회원가입(UserRequest.JoinDTO joinDTO) { // 동일회원 있는지 검사 try { userRepository.save(joinDTO.toEntity()); } catch (Exception e) { //Exceptiom 400 (Bad request -> 잘못된 요청입니다) throw new Exception400("동일한 username 이 존재합니다."); } // 회원가입 userRepository.save(joinDTO.toEntity()); } public User 로그인(UserRequest.LoginDTO loginDTO) { // username,password 검사 User user = userRepository.findByUsername(loginDTO.getUsername()); if (user == null) throw new Exception401("username 혹은 password 가 일치하지 않습니다"); if (!user.getPassword().equals(loginDTO.getPassword())) { throw new Exception401("username 혹은 password 가 일치하지 않습니다"); } // 로그인 return user; } public Map<String, Object> 유저네임중복체크(String username) { User user = userRepository.findByUsername(username); Map<String, Object> dto = new HashMap<>(); if (user == null) { dto.put("available", true); } else { dto.put("available", false); } return dto; } @Transactional public User 회원정보수정(UserRequest.UpdateDTO updateDTO, Integer userId) { User user = userRepository.findById(userId); //Exception404 if (user == null) throw new Exception404("회원을 찾을 수 없습니다"); user.update(updateDTO.getPassword(), updateDTO.getEmail()); // 영속화된 객체의 상태변경 return user; } // 함수 종료될 때 더티 체킹(Dirty Checking) -> 상태가 변경되면 변경된 data를 가지고 update을 한다 }

BoardController

package shop.mtcoding.blog.board; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpSession; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import shop.mtcoding.blog.user.User; @RequiredArgsConstructor @Controller public class BoardController { private final BoardService boardService; private final HttpSession session; @GetMapping("/") public String list(HttpServletRequest request) { User sessionUser = (User) session.getAttribute("sessionUser"); if (sessionUser == null) { request.setAttribute("models", boardService.글목록보기(null)); } else { request.setAttribute("models", boardService.글목록보기(sessionUser.getId())); } return "board/list"; } @PostMapping("/board/save") public String save(BoardRequest.SaveDTO saveDTO) { User sessionUser = (User) session.getAttribute("sessionUser"); boardService.글쓰기(saveDTO, sessionUser); return "redirect:/"; } @GetMapping("/board/save-form") public String saveForm() { return "board/save-form"; } @GetMapping("/board/{id}") public String detail(@PathVariable("id") Integer id, HttpServletRequest request) { User sessionUser = (User) session.getAttribute("sessionUser"); // 비로그인 시 상세보기 Integer sessionUserId = (sessionUser == null ? null : sessionUser.getId()); BoardResponse.DetailDTO detailDTO = boardService.글상세보기(id, sessionUserId); request.setAttribute("model", detailDTO); return "board/detail"; } @GetMapping("/board/{id}/update-form") public String updateForm(@PathVariable("id") Integer id, HttpServletRequest request) { User sessionUser = (User) session.getAttribute("sessionUser"); Board board = boardService.업데이트글보기(id, sessionUser.getId()); request.setAttribute("model", board); return "board/update-form"; } @PostMapping("/board/{id}/update") public String update(@PathVariable("id") Integer id, BoardRequest.UpdateDTO reqDTO) { User sessionUser = (User) session.getAttribute("sessionUser"); boardService.게시글수정(reqDTO, id, sessionUser.getId()); return "redirect:/board/" + id; } @PostMapping("/board/{id}/delete") public String delete(@PathVariable("id") Integer id) { User sessionUser = (User) session.getAttribute("sessionUser"); boardService.게시글삭제(id, sessionUser.getId()); return "redirect:/"; } }

LoveController

package shop.mtcoding.blog.love; import jakarta.servlet.http.HttpSession; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; import shop.mtcoding.blog._core.util.Resp; import shop.mtcoding.blog.user.User; @RequiredArgsConstructor @RestController // 파일이 아닌 데이터 리턴하는 컨트롤러 (Ajax) public class LoveController { private final LoveService loveService; private final HttpSession session; @PostMapping("/love") public Resp<?> saveLove(@RequestBody LoveRequest.SaveDTO reqDTO) { User sessionUser = (User) session.getAttribute("sessionUser"); if (sessionUser == null) throw new ExceptionApi401("인증이 필요합니다."); LoveResponse.SaveDTO respDTO = loveService.좋아요(reqDTO, sessionUser.getId()); return Resp.ok(respDTO); } @DeleteMapping("/love/{id}") public Resp<?> deleteLove(@PathVariable("id") Integer id) { User sessionUser = (User) session.getAttribute("sessionUser"); if (sessionUser == null) throw new ExceptionApi401("인증이 필요합니다."); LoveResponse.DeleteDTO respDTO = loveService.좋아요취소(id, sessionUser.getId()); // loveId return Resp.ok(respDTO); } }

detail

{{> layout/header}} <input type="hidden" id="boardId" value="{{model.id}}"> <div class="container p-5"> <!-- 수정삭제버튼 --> {{#model.isOwner}} <div class="d-flex justify-content-end"> <a href="/board/{{model.id}}/update-form" class="btn btn-warning me-1">수정</a> <form action="/board/{{model.id}}/delete" method="post"> <button class="btn btn-danger">삭제</button> </form> </div> {{/model.isOwner}} <div class="d-flex justify-content-end"> <b>작성자</b> : {{model.username}} </div> <!-- 게시글내용 --> <div> <h2><b>{{model.title}}</b></h2> <hr/> <div class="m-4 p-2"> {{model.content}} </div> </div> <!-- AJAX 좋아요 영역 --> <div class="my-3 d-flex align-items-center"> {{#model.isLove}} <i id="loveIcon" class="fa fa-heart" style="font-size:20px; color:red" onclick="deleteLove({{model.loveId}})"></i> {{/model.isLove}} {{^model.isLove}} <i id="loveIcon" class="fa fa-heart" style="font-size:20px; color:black" onclick="saveLove()"></i> {{/model.isLove}} <span class="ms-1"><b id="loveCount">{{model.loveCount}}</b>명이 이 글을 좋아합니다</span> </div> <!-- 댓글 --> <div class="card mt-3"> <!-- 댓글등록 --> <div class="card-body"> <form action="/reply/save" method="post"> <input type="hidden" name="boardId" value="{{model.id}}"> <textarea class="form-control" rows="2" name="content"></textarea> <div class="d-flex justify-content-end"> <button type="submit" class="btn btn-outline-primary mt-1">댓글등록</button> </div> </form> </div> <!-- 댓글목록 --> <div class="card-footer"> <b>댓글리스트</b> </div> <div class="list-group"> <!-- 댓글아이템 --> {{#model.replies}} <div class="list-group-item d-flex justify-content-between align-items-center"> <div class="d-flex"> <div class="px-1 me-1 bg-primary text-white rounded">{{username}}</div> <div>{{content}}</div> </div> {{#isOwner}} <form action="/reply/{{id}}/delete" method="post"> <button type="submit" class="btn">🗑</button> </form> {{/isOwner}} </div> {{/model.replies}} </div> </div> </div> <script> // setInterval(()=>{ // location.reload(); // },1000) let boardId = document.querySelector("#boardId").value; async function saveLove() { let requestBody = {boardId: boardId}; let response = await fetch(`/love`, { method: "POST", body: JSON.stringify(requestBody), headers: {"Content-Type": "application/json"} }); let responseBody = await response.json(); // { loveId, loveCount } console.log(responseBody); if (responseBody.status != 200) { alert(responseBody.msg); } else { // DOM 업데이트 let loveIcon = document.querySelector('#loveIcon'); let loveCount = document.querySelector('#loveCount'); loveIcon.style.color = 'red'; loveIcon.setAttribute('onclick', `deleteLove(${responseBody.body.loveId})`); loveCount.innerHTML = responseBody.body.loveCount; } } async function deleteLove(loveId) { let response = await fetch(`/love/${loveId}`, { method: "DELETE" }); let responseBody = await response.json(); // response.text(); (X) console.log(responseBody); // DOM 업데이트 let loveIcon = document.querySelector('#loveIcon'); let loveCount = document.querySelector('#loveCount'); loveIcon.style.color = 'black'; loveIcon.setAttribute('onclick', `saveLove()`); loveCount.innerHTML = responseBody.body.loveCount; } </script> {{> layout/footer}}

ReplyController

package shop.mtcoding.blog.reply; import jakarta.servlet.http.HttpSession; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import shop.mtcoding.blog._core.error.ex.Exception401; import shop.mtcoding.blog.user.User; @RequiredArgsConstructor @Controller public class ReplyController { private final ReplyService replyService; private final HttpSession session; @PostMapping("/reply/save") public String save(ReplyRequest.SaveDTO reqDTO) { // 인증로직 User sessionUser = (User) session.getAttribute("sessionUser"); if (sessionUser == null) throw new Exception401("인증이 필요합니다."); replyService.댓글쓰기(reqDTO, sessionUser); return "redirect:/board/" + reqDTO.getBoardId(); } @PostMapping("/reply/{id}/delete") public String delete(@PathVariable("id") Integer id) { // 인증로직 User sessionUser = (User) session.getAttribute("sessionUser"); if (sessionUser == null) throw new Exception401("인증이 필요합니다."); Integer boardId = replyService.댓글삭제(id, sessionUser.getId()); replyService.댓글삭제(id, sessionUser.getId()); return "redirect:/board/" + boardId; } }
1. 바로 /board/save-form으로 가면
notion image
→ alert 창이 뜬다
 
2. 바로 /user/update-form으로 가면
notion image
→ alert 창이 뜬다
 
3. 로그인 안 한 상태로 좋아요 누르면
notion image
→ alert 창이 뜬다

403 → 권한오류

GlobalExceptionHandler

package shop.mtcoding.blog._core.error; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import shop.mtcoding.blog._core.error.ex.*; import shop.mtcoding.blog._core.util.Resp; @RestControllerAdvice // data return public class GlobalExceptionHandler { //401 -> 인증 안됨 @ExceptionHandler(Exception401.class) public String ex401(Exception401 e) { // catch 자리 String html = """ <script> alert('${msg}') location.href="/login-form"; </script> """.replace("${msg}", e.getMessage()); return html; } @ExceptionHandler(ExceptionApi401.class) public Resp<?> exApi401(ExceptionApi401 e) { // catch 자리 return Resp.fail(401, e.getMessage()); } // 403 -> 권한 없음 @ExceptionHandler(Exception403.class) public String ex403(Exception403 e) { // catch 자리 String html = """ <script> alert('${msg}') </script> """.replace("${msg}", e.getMessage()); return html; } @ExceptionHandler(ExceptionApi403.class) public Resp<?> exApi403(ExceptionApi403 e) { // catch 자리 return Resp.fail(403, e.getMessage()); } }
Exception403
package shop.mtcoding.blog._core.error.ex; public class Exception403 extends RuntimeException { public Exception403(String message) { super(message); } }
ExceptionApi403
package shop.mtcoding.blog._core.error.ex; public class ExceptionApi403 extends RuntimeException { public ExceptionApi403(String message) { super(message); } }
BoardService
package shop.mtcoding.blog.board; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import shop.mtcoding.blog._core.error.ex.Exception403; import shop.mtcoding.blog._core.error.ex.Exception404; import shop.mtcoding.blog.love.Love; import shop.mtcoding.blog.love.LoveRepository; import shop.mtcoding.blog.reply.ReplyRepository; import shop.mtcoding.blog.user.User; import java.util.List; @RequiredArgsConstructor @Service public class BoardService { private final BoardRepository boardRepository; private final LoveRepository loveRepository; private final ReplyRepository replyRepository; @Transactional public void 글쓰기(BoardRequest.SaveDTO saveDTO, User sessionUser) { Board board = saveDTO.toEntity(sessionUser); boardRepository.save(board); } public List<Board> 글목록보기(Integer userId) { return boardRepository.findAll(userId); } public BoardResponse.DetailDTO 글상세보기(Integer id, Integer userId) { Board board = boardRepository.findByIdJoinUserAndReplies(id); Love love = loveRepository.findByUserIdAndBoardId(userId, id); Boolean isLove = love == null ? false : true; Integer loveId = love == null ? null : love.getId(); Long loveCount = loveRepository.findByBoardId(board.getId()); BoardResponse.DetailDTO detailDTO = new BoardResponse.DetailDTO(board, userId, isLove, loveCount.intValue(), loveId); return detailDTO; } public Board 업데이트글보기(Integer id, Integer sessionUserId) { Board boardPs = boardRepository.findById(id); if (boardPs == null) throw new Exception404("게시글을 찾을 수 없습니다."); if (!boardPs.getUser().getId().equals(sessionUserId)) throw new Exception403("권한이 없습니다."); return boardPs; } @Transactional public Board 게시글수정(BoardRequest.UpdateDTO reqDTO, Integer id, Integer sessionUserId) { Board board = boardRepository.findById(id); if (board == null) throw new Exception404("게시글을 찾을 수 없습니다"); if (!board.getUser().getId().equals(sessionUserId)) throw new Exception403("권한이 없습니다."); board.update(reqDTO.getTitle(), reqDTO.getContent(), reqDTO.getIsPublic()); return board; } @Transactional public void 게시글삭제(Integer id, Integer sessionUserId) { // 게시글 존재 확인 Board boardPs = boardRepository.findById(id); if (boardPs == null) throw new Exception404("게시글을 찾을 수 없습니다"); if (!boardPs.getUser().getId().equals(sessionUserId)) throw new Exception403("권한이 없습니다."); // 좋아요 삭제 loveRepository.deleteByBoardId(boardPs.getId()); boardRepository.deleteById(id); } }
LoveService
package shop.mtcoding.blog.love; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import shop.mtcoding.blog._core.error.ex.ExceptionApi403; import shop.mtcoding.blog._core.error.ex.ExceptionApi404; import shop.mtcoding.blog.board.BoardRepository; import shop.mtcoding.blog.user.UserRepository; @RequiredArgsConstructor @Service public class LoveService { private final LoveRepository loveRepository; private final BoardRepository boardRepository; private final UserRepository userRepository; @Transactional public LoveResponse.SaveDTO 좋아요(LoveRequest.SaveDTO reqDTO, Integer sessionUserId) { Love lovePs = loveRepository.save(reqDTO.toEntity(sessionUserId)); Long loveCount = loveRepository.findByBoardId(reqDTO.getBoardId()); return new LoveResponse.SaveDTO(lovePs.getId(), loveCount.intValue()); } @Transactional public LoveResponse.DeleteDTO 좋아요취소(Integer id, Integer sessionUserId) { Love lovePs = loveRepository.findById(id); if (lovePs == null) throw new ExceptionApi404("취소할 좋아요가 없습니다."); // ExceptionApi404 // 권한체크 (lovePs.gerUser().getId() 비교 sessionUserId) if (!lovePs.getUser().getId().equals(sessionUserId)) { throw new ExceptionApi403("권한이 없습니다."); // ExceptionApi403 } Integer boardId = lovePs.getBoard().getId(); loveRepository.deleteById(id); Long loveCount = loveRepository.findByBoardId(boardId); return new LoveResponse.DeleteDTO(loveCount.intValue()); } }
ReplyService
package shop.mtcoding.blog.reply; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import shop.mtcoding.blog._core.error.ex.Exception403; import shop.mtcoding.blog._core.error.ex.Exception404; import shop.mtcoding.blog.user.User; @RequiredArgsConstructor @Service public class ReplyService { private final ReplyRepository replyRepository; @Transactional public void 댓글쓰기(ReplyRequest.SaveDTO reqDTO, User sessionUser) { replyRepository.save(reqDTO.toEntity(sessionUser)); } @Transactional public Integer 댓글삭제(Integer id, Integer sessionUserId) { Reply replyPs = replyRepository.findById(id); if (replyPs == null) throw new Exception404("삭제할 댓글을 찾을 수 없습니다."); // 404 if (!(replyPs.getUser().getId().equals(sessionUserId))) throw new Exception403("삭제할 권한이 없습니다."); Integer boardId = replyPs.getBoard().getId(); replyRepository.deleteById(id); return boardId; } }
 

404 → 자원을 찾을 수 없음

GlobalExceptionHandler

package shop.mtcoding.blog._core.error; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import shop.mtcoding.blog._core.error.ex.*; import shop.mtcoding.blog._core.util.Resp; @RestControllerAdvice // data return public class GlobalExceptionHandler { //401 -> 인증 안됨 @ExceptionHandler(Exception401.class) public String ex401(Exception401 e) { // catch 자리 String html = """ <script> alert('${msg}') location.href="/login-form"; </script> """.replace("${msg}", e.getMessage()); return html; } @ExceptionHandler(ExceptionApi401.class) public Resp<?> exApi401(ExceptionApi401 e) { // catch 자리 return Resp.fail(401, e.getMessage()); } // 403 -> 권한 없음 @ExceptionHandler(Exception403.class) public String ex403(Exception403 e) { // catch 자리 String html = """ <script> alert('${msg}') </script> """.replace("${msg}", e.getMessage()); return html; } @ExceptionHandler(ExceptionApi403.class) public Resp<?> exApi403(ExceptionApi403 e) { // catch 자리 return Resp.fail(403, e.getMessage()); } // 404 -> 자원을 찾을 수 없음 @ExceptionHandler(Exception404.class) public String ex404(Exception403 e) { // catch 자리 String html = """ <script> alert('${msg}') </script> """.replace("${msg}", e.getMessage()); return html; } @ExceptionHandler(ExceptionApi404.class) public Resp<?> exApi404(ExceptionApi404 e) { // catch 자리 return Resp.fail(404, e.getMessage()); } }

Exception404

package shop.mtcoding.blog._core.error.ex; public class Exception404 extends RuntimeException { public Exception404(String message) { super(message); } }

ExceptionApi404

package shop.mtcoding.blog._core.error.ex; public class ExceptionApi404 extends RuntimeException { public ExceptionApi404(String message) { super(message); } }

UserService

package shop.mtcoding.blog.user; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import shop.mtcoding.blog._core.error.ex.Exception400; import shop.mtcoding.blog._core.error.ex.Exception401; import shop.mtcoding.blog._core.error.ex.Exception404; import java.util.HashMap; import java.util.Map; // 비즈니스 로직, 트랜잭션 처리, DTO 완료 @RequiredArgsConstructor @Service public class UserService { private final UserRepository userRepository; // 401 > 인증 403 > 권한 404 > 자원 못찾음 @Transactional public void 회원가입(UserRequest.JoinDTO joinDTO) { // 동일회원 있는지 검사 try { userRepository.save(joinDTO.toEntity()); } catch (Exception e) { //Exceptiom 400 (Bad request -> 잘못된 요청입니다) throw new Exception400("동일한 username 이 존재합니다."); } // 회원가입 userRepository.save(joinDTO.toEntity()); } public User 로그인(UserRequest.LoginDTO loginDTO) { // username,password 검사 User user = userRepository.findByUsername(loginDTO.getUsername()); if (user == null) throw new Exception401("username 혹은 password 가 일치하지 않습니다"); if (!user.getPassword().equals(loginDTO.getPassword())) { throw new Exception401("username 혹은 password 가 일치하지 않습니다"); } // 로그인 return user; } public Map<String, Object> 유저네임중복체크(String username) { User user = userRepository.findByUsername(username); Map<String, Object> dto = new HashMap<>(); if (user == null) { dto.put("available", true); } else { dto.put("available", false); } return dto; } @Transactional public User 회원정보수정(UserRequest.UpdateDTO updateDTO, Integer userId) { User user = userRepository.findById(userId); //Exception404 if (user == null) throw new Exception404("회원을 찾을 수 없습니다"); user.update(updateDTO.getPassword(), updateDTO.getEmail()); // 영속화된 객체의 상태변경 return user; } // 함수 종료될 때 더티 체킹(Dirty Checking) -> 상태가 변경되면 변경된 data를 가지고 update을 한다 }

BoardService

package shop.mtcoding.blog.board; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import shop.mtcoding.blog._core.error.ex.Exception403; import shop.mtcoding.blog._core.error.ex.Exception404; import shop.mtcoding.blog.love.Love; import shop.mtcoding.blog.love.LoveRepository; import shop.mtcoding.blog.reply.ReplyRepository; import shop.mtcoding.blog.user.User; import java.util.List; @RequiredArgsConstructor @Service public class BoardService { private final BoardRepository boardRepository; private final LoveRepository loveRepository; private final ReplyRepository replyRepository; @Transactional public void 글쓰기(BoardRequest.SaveDTO saveDTO, User sessionUser) { Board board = saveDTO.toEntity(sessionUser); boardRepository.save(board); } public List<Board> 글목록보기(Integer userId) { return boardRepository.findAll(userId); } public BoardResponse.DetailDTO 글상세보기(Integer id, Integer userId) { Board board = boardRepository.findByIdJoinUserAndReplies(id); Love love = loveRepository.findByUserIdAndBoardId(userId, id); Boolean isLove = love == null ? false : true; Integer loveId = love == null ? null : love.getId(); Long loveCount = loveRepository.findByBoardId(board.getId()); BoardResponse.DetailDTO detailDTO = new BoardResponse.DetailDTO(board, userId, isLove, loveCount.intValue(), loveId); return detailDTO; } public Board 업데이트글보기(Integer id, Integer sessionUserId) { Board boardPs = boardRepository.findById(id); if (boardPs == null) throw new Exception404("게시글을 찾을 수 없습니다."); if (!boardPs.getUser().getId().equals(sessionUserId)) throw new Exception403("권한이 없습니다."); return boardPs; } @Transactional public Board 게시글수정(BoardRequest.UpdateDTO reqDTO, Integer id, Integer sessionUserId) { Board board = boardRepository.findById(id); if (board == null) throw new Exception404("게시글을 찾을 수 없습니다"); if (!board.getUser().getId().equals(sessionUserId)) throw new Exception403("권한이 없습니다."); board.update(reqDTO.getTitle(), reqDTO.getContent(), reqDTO.getIsPublic()); return board; } @Transactional public void 게시글삭제(Integer id, Integer sessionUserId) { // 게시글 존재 확인 Board boardPs = boardRepository.findById(id); if (boardPs == null) throw new Exception404("게시글을 찾을 수 없습니다"); if (!boardPs.getUser().getId().equals(sessionUserId)) throw new Exception403("권한이 없습니다."); // 좋아요 삭제 loveRepository.deleteByBoardId(boardPs.getId()); boardRepository.deleteById(id); } }

LoveService

package shop.mtcoding.blog.love; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import shop.mtcoding.blog._core.error.ex.ExceptionApi403; import shop.mtcoding.blog._core.error.ex.ExceptionApi404; import shop.mtcoding.blog.board.BoardRepository; import shop.mtcoding.blog.user.UserRepository; @RequiredArgsConstructor @Service public class LoveService { private final LoveRepository loveRepository; private final BoardRepository boardRepository; private final UserRepository userRepository; @Transactional public LoveResponse.SaveDTO 좋아요(LoveRequest.SaveDTO reqDTO, Integer sessionUserId) { Love lovePs = loveRepository.save(reqDTO.toEntity(sessionUserId)); Long loveCount = loveRepository.findByBoardId(reqDTO.getBoardId()); return new LoveResponse.SaveDTO(lovePs.getId(), loveCount.intValue()); } @Transactional public LoveResponse.DeleteDTO 좋아요취소(Integer id, Integer sessionUserId) { Love lovePs = loveRepository.findById(id); if (lovePs == null) throw new ExceptionApi404("취소할 좋아요가 없습니다."); // ExceptionApi404 // 권한체크 (lovePs.gerUser().getId() 비교 sessionUserId) if (!lovePs.getUser().getId().equals(sessionUserId)) { throw new ExceptionApi403("권한이 없습니다."); // ExceptionApi403 } Integer boardId = lovePs.getBoard().getId(); loveRepository.deleteById(id); Long loveCount = loveRepository.findByBoardId(boardId); return new LoveResponse.DeleteDTO(loveCount.intValue()); } }

ReplyService

package shop.mtcoding.blog.reply; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import shop.mtcoding.blog._core.error.ex.Exception403; import shop.mtcoding.blog._core.error.ex.Exception404; import shop.mtcoding.blog.user.User; @RequiredArgsConstructor @Service public class ReplyService { private final ReplyRepository replyRepository; @Transactional public void 댓글쓰기(ReplyRequest.SaveDTO reqDTO, User sessionUser) { replyRepository.save(reqDTO.toEntity(sessionUser)); } @Transactional public Integer 댓글삭제(Integer id, Integer sessionUserId) { Reply replyPs = replyRepository.findById(id); if (replyPs == null) throw new Exception404("삭제할 댓글을 찾을 수 없습니다."); // 404 if (!(replyPs.getUser().getId().equals(sessionUserId))) throw new Exception403("삭제할 권한이 없습니다."); Integer boardId = replyPs.getBoard().getId(); replyRepository.deleteById(id); return boardId; } }

400 → Bad Request (잘못된 요청)

GlobalExceptionHandler

package shop.mtcoding.blog._core.error; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import shop.mtcoding.blog._core.error.ex.*; import shop.mtcoding.blog._core.util.Resp; @RestControllerAdvice // data return public class GlobalExceptionHandler { //401 -> 인증 안됨 @ExceptionHandler(Exception401.class) public String ex401(Exception401 e) { // catch 자리 String html = """ <script> alert('${msg}') location.href="/login-form"; </script> """.replace("${msg}", e.getMessage()); return html; } @ExceptionHandler(ExceptionApi401.class) public Resp<?> exApi401(ExceptionApi401 e) { // catch 자리 return Resp.fail(401, e.getMessage()); } // 403 -> 권한 없음 @ExceptionHandler(Exception403.class) public String ex403(Exception403 e) { // catch 자리 String html = """ <script> alert('${msg}') </script> """.replace("${msg}", e.getMessage()); return html; } @ExceptionHandler(ExceptionApi403.class) public Resp<?> exApi403(ExceptionApi403 e) { // catch 자리 return Resp.fail(403, e.getMessage()); } // 404 -> 자원을 찾을 수 없음 @ExceptionHandler(Exception404.class) public String ex404(Exception403 e) { // catch 자리 String html = """ <script> alert('${msg}') </script> """.replace("${msg}", e.getMessage()); return html; } @ExceptionHandler(ExceptionApi404.class) public Resp<?> exApi404(ExceptionApi404 e) { // catch 자리 return Resp.fail(404, e.getMessage()); } // 400 -> 잘못된 요청 @ExceptionHandler(Exception400.class) public String ex400(Exception400 e) { // catch 자리 String html = """ <script> alert('${msg}') </script> """.replace("${msg}", e.getMessage()); return html; } }

UserService

package shop.mtcoding.blog.user; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import shop.mtcoding.blog._core.error.ex.Exception400; import shop.mtcoding.blog._core.error.ex.Exception401; import shop.mtcoding.blog._core.error.ex.Exception404; import java.util.HashMap; import java.util.Map; // 비즈니스 로직, 트랜잭션 처리, DTO 완료 @RequiredArgsConstructor @Service public class UserService { private final UserRepository userRepository; // 401 > 인증 403 > 권한 404 > 자원 못찾음 @Transactional public void 회원가입(UserRequest.JoinDTO joinDTO) { // 동일회원 있는지 검사 try { userRepository.save(joinDTO.toEntity()); } catch (Exception e) { //Exceptiom 400 (Bad request -> 잘못된 요청입니다) throw new Exception400("동일한 username 이 존재합니다."); } // 회원가입 userRepository.save(joinDTO.toEntity()); } public User 로그인(UserRequest.LoginDTO loginDTO) { // username,password 검사 User user = userRepository.findByUsername(loginDTO.getUsername()); if (user == null) throw new Exception401("username 혹은 password 가 일치하지 않습니다"); if (!user.getPassword().equals(loginDTO.getPassword())) { throw new Exception401("username 혹은 password 가 일치하지 않습니다"); } // 로그인 return user; } public Map<String, Object> 유저네임중복체크(String username) { User user = userRepository.findByUsername(username); Map<String, Object> dto = new HashMap<>(); if (user == null) { dto.put("available", true); } else { dto.put("available", false); } return dto; } @Transactional public User 회원정보수정(UserRequest.UpdateDTO updateDTO, Integer userId) { User user = userRepository.findById(userId); //Exception404 if (user == null) throw new Exception404("회원을 찾을 수 없습니다"); user.update(updateDTO.getPassword(), updateDTO.getEmail()); // 영속화된 객체의 상태변경 return user; } // 함수 종료될 때 더티 체킹(Dirty Checking) -> 상태가 변경되면 변경된 data를 가지고 update을 한다 }

그 외 오류

GlobalExceptionHandler

package shop.mtcoding.blog._core.error; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import shop.mtcoding.blog._core.error.ex.*; import shop.mtcoding.blog._core.util.Resp; @RestControllerAdvice // data return public class GlobalExceptionHandler { //401 -> 인증 안됨 @ExceptionHandler(Exception401.class) public String ex401(Exception401 e) { // catch 자리 String html = """ <script> alert('${msg}') location.href="/login-form"; </script> """.replace("${msg}", e.getMessage()); return html; } @ExceptionHandler(ExceptionApi401.class) public Resp<?> exApi401(ExceptionApi401 e) { // catch 자리 return Resp.fail(401, e.getMessage()); } // 403 -> 권한 없음 @ExceptionHandler(Exception403.class) public String ex403(Exception403 e) { // catch 자리 String html = """ <script> alert('${msg}') </script> """.replace("${msg}", e.getMessage()); return html; } @ExceptionHandler(ExceptionApi403.class) public Resp<?> exApi403(ExceptionApi403 e) { // catch 자리 return Resp.fail(403, e.getMessage()); } // 404 -> 자원을 찾을 수 없음 @ExceptionHandler(Exception404.class) public String ex404(Exception403 e) { // catch 자리 String html = """ <script> alert('${msg}') </script> """.replace("${msg}", e.getMessage()); return html; } @ExceptionHandler(ExceptionApi404.class) public Resp<?> exApi404(ExceptionApi404 e) { // catch 자리 return Resp.fail(404, e.getMessage()); } // 400 -> 잘못된 요청 @ExceptionHandler(Exception400.class) public String ex400(Exception400 e) { // catch 자리 String html = """ <script> alert('${msg}') </script> """.replace("${msg}", e.getMessage()); return html; } // 그 외 오류 @ExceptionHandler(Exception.class) public String exUnKnown(Exception e) { // catch 자리 String html = """ <script> alert('${msg}'); history.back(); </script> """.replace("${msg}", "관리자에게 문의해주세요."); System.out.println("관리자님 보세요: " + e.getMessage()); return html; } }
 
 
 
Share article

parangdajavous