[Spring] REST API 컨트롤러 요청과 응답

류재성's avatar
Apr 01, 2024
[Spring] REST API 컨트롤러 요청과 응답
💡
1.요청은 JSON 으로 통일, 응답도 JSON 으로 통일 2.POST, PUT 요청은 추가된 ROW 혹은 수정된 ROW 를 응답해줘야 한다. 그래야 PK 를 알 수 있다. 인서트 업데이트된 데이터가 있어야 프론트가 알 수 있다. 3. 화면에 필요한 데이터만 전달해야 한다. 그래서 DTO 가 필요하다. → DTO 를 만드는 책임은 서비스가 갖는다. 서비스에서 DTO 를 만들면 커넥션 시간이 줄어든다.
 

전체 코드

board

Board
package shop.mtcoding.blog.board; import jakarta.persistence.*; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.hibernate.annotations.CreationTimestamp; import shop.mtcoding.blog.reply.Reply; import shop.mtcoding.blog.user.User; import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; @NoArgsConstructor // 빈생성자가 필요 @Entity @Data @Table(name = "board_tb") public class Board { @Id @GeneratedValue (strategy = GenerationType.IDENTITY) private Integer id; private String title; private String content; //@JoinColumn(name="user_id") 변수명을 직접 지정 가능 @ManyToOne(fetch = FetchType.LAZY) private User user ; // 변수명이 user. user_id 를 만들어줌 @CreationTimestamp // persistance centext 에 전달될 때 자동으로 주입됨. private Timestamp createdAt; //테이블은 생성되면 안됨. 조회된 것을 담는 용도로만 사용. //@ManyToOne 은 eager 가 기본, @OneToMany 는 lazy 가 기본 @OrderBy("id desc") // 댓글 정렬 @OneToMany(mappedBy = "board",fetch = FetchType.LAZY, cascade = CascadeType.REMOVE) // Board 는 엔티티 객체의 필드명, reply 엔티티에 Board 객체를 넣는거임 private List<Reply> replies = new ArrayList<>(); // 댓글이 없으면 null 일 때 오류남. 그래서 new 를 해서 크기를 0 으로 만들어놓는다. @Transient // 테이블 생성이 안됨. 임시로 사용함 private boolean isBoardOwner ; @Builder //엔티티에는 다 걸기 public Board(Integer id, String title, String content, User user, Timestamp createdAt) { this.id = id; this.title = title; this.content = content; this.user = user; this.createdAt = createdAt; } public void update(BoardRequest.UpdateDTO requestDTO){ this.title =requestDTO.getTitle(); this.content = requestDTO.getContent() ; } }
BoardController
package shop.mtcoding.blog.board; import jakarta.servlet.http.HttpSession; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import shop.mtcoding.blog._core.util.ApiUtil; import shop.mtcoding.blog.user.User; import java.util.List; @RequiredArgsConstructor @RestController public class BoardController { private final BoardService boardService ; private final HttpSession session; // todo : 글목록조회 api 필요 @GetMapping("/") public ResponseEntity<?> main(){ List<Board> boardList = boardService.글목록조회(); return ResponseEntity.ok(new ApiUtil(boardList)); //디스패쳐 서블릿은 오브젝트가 리턴되면, 메세지컨버터가 보드에 있는 애들을 게터로 제이슨으로 변환 // GETuser를 하는 순간 없기 때문에 select 로 통신. 근데 화면에 그림을 그릴 때는 null 이기 때문에 터짐. // 메세지 컨버터가 기다린다면 괜찮음. // 그래서 메세지 컨버터가 레이지 로딩을 하면 안되고 서비스에서 레이지 로딩해서 dto 로 받아와야 됨 } @GetMapping("/api/boards/{id}/detail") public ResponseEntity<?> detail(@PathVariable int id){ User sessionUser = (User) session.getAttribute("sessionUser"); Board board = boardService.글상세보기(id,sessionUser); return ResponseEntity.ok(new ApiUtil(board)); } @GetMapping("/api/boards/{id}") public ResponseEntity<?> findOne(@PathVariable int id){ User sessionUser = (User) session.getAttribute("sessionUser"); Board board = boardService.글조회(id,sessionUser.getId()); return ResponseEntity.ok(new ApiUtil(board)); } @PostMapping("/api/boards") public ResponseEntity<?> save(@RequestBody BoardRequest.SaveDTO requestDTO){ User sessionUser = (User) session.getAttribute("sessionUser"); Board board = boardService.글쓰기(requestDTO,sessionUser); return ResponseEntity.ok(new ApiUtil(board)) ; } @DeleteMapping("/api/boards/{id}") public ResponseEntity<?> delete(@RequestBody @PathVariable Integer id){ User sessionUser = (User) session.getAttribute("sessionUser"); boardService.글삭제(id,sessionUser.getId()); return ResponseEntity.ok(new ApiUtil(null)); } // @PutMapping("/api/boards/{id}") // public ResponseEntity<?> update(@RequestBody @PathVariable Integer id){ // User sessionUser = (User) session.getAttribute("sessionUser"); // Board board = boardService.글조회(id,sessionUser.getId()); // return ResponseEntity.ok(new ApiUtil(board)) ; // } }
BoardJPARepository
package shop.mtcoding.blog.board; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import java.util.Optional; public interface BoardJPARepository extends JpaRepository<Board,Integer> { @Query("select b from Board b join fetch b.user where b.id = :id") Optional<Board> findByIdJoinUser(@Param("id") int id); @Query("select b from Board b join fetch b.user u left join fetch b.replies r where b.id = :id") Optional<Board> findByIdJoinUserAndReplies(@Param("id") int id); }
BoardRequest
package shop.mtcoding.blog.board; import lombok.Data; import shop.mtcoding.blog.user.User; public class BoardRequest { @Data public static class SaveDTO { private String title; private String content; public Board toEntity(User user) { return Board.builder() .title(title) .content(content) .user(user) .build(); } } @Data public static class UpdateDTO{ private String title; private String content; } }
BoardService
package shop.mtcoding.blog.board; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import shop.mtcoding.blog._core.err.exception.Exception403; import shop.mtcoding.blog._core.err.exception.Exception404; import shop.mtcoding.blog.user.User; import java.util.List; @RequiredArgsConstructor @Service public class BoardService { private final BoardJPARepository boardJPARepository; @Transactional public Board 글쓰기(BoardRequest.SaveDTO requestDTO, User sessionUser) { return boardJPARepository.save(requestDTO.toEntity(sessionUser)); } @Transactional public Board 글조회(int boardId, int sessionUserId) { //조회 및 예외처리 Board board = boardJPARepository.findById(boardId) .orElseThrow(() -> new Exception404("게시글을 찾을 수 없습니다.")); //권한 처리, if (sessionUserId != board.getUser().getId()) { throw new Exception403("게시글을 수정할 권한이 없습니다"); } return board; } @Transactional public void 글삭제(int boardId, int sessionUserId) { Board board = boardJPARepository.findById(boardId).orElseThrow(() -> new Exception404("게시글을 찾을 수 없습니다.")); if (sessionUserId != board.getUser().getId()) { throw new Exception403("게시글을 삭제할 권한이 없습니다"); }// 트랜잭션은 런타임익셉션이 발동하면 롤백된다. boardJPARepository.deleteById(boardId); } public List<Board> 글목록조회() { Sort sort = Sort.by(Sort.Direction.DESC, "id"); List<Board> boardList = boardJPARepository.findAll(sort); return boardList; } public Board 글상세보기(int boardId, User sessionUser) { Board board = boardJPARepository.findByIdJoinUser(boardId) .orElseThrow(() -> new Exception404("게시글을 찾을 수 없습니다")); boolean isBoardOwner = false; if (sessionUser != null) { if (sessionUser.getId() == board.getUser().getId()) { isBoardOwner = true; } } board.setBoardOwner(isBoardOwner); board.getReplies().forEach(reply ->{ boolean isReplyOwner = false ; if(sessionUser!=null) { if (reply.getUser().getId() == sessionUser.getId()) { isReplyOwner =true; } } reply.setReplyOwner(isReplyOwner); }); return board; } }
 
 

user

User
package shop.mtcoding.blog.user; import jakarta.persistence.*; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.hibernate.annotations.CreationTimestamp; import java.sql.Timestamp; @NoArgsConstructor // 빈생성자가 필요 @Entity @Data @Table(name = "user_tb") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id ; @Column(unique = true) private String username ; private String password; private String email; @CreationTimestamp // persistance centext 에 전달될 때 자동으로 주입됨. private Timestamp createdAt; @Builder //빌더 패턴 public User(int id, String username, String password, String email, Timestamp createdAt) { this.id = id; this.username = username; this.password = password; this.email = email; this.createdAt = createdAt; } }
UserController
package shop.mtcoding.blog.user; import jakarta.persistence.NoResultException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpSession; import lombok.RequiredArgsConstructor; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.stereotype.Repository; import org.springframework.web.bind.annotation.*; import shop.mtcoding.blog._core.err.exception.Exception400; import shop.mtcoding.blog._core.err.exception.Exception401; import shop.mtcoding.blog._core.util.ApiUtil; import java.net.http.HttpRequest; @RequiredArgsConstructor @RestController public class UserController { private final UserService userService; private final HttpSession session; @GetMapping("/api/users/{id}") public ResponseEntity<?> userinfo(@PathVariable Integer id){ User user = userService.회원조회(id); return ResponseEntity.ok(new ApiUtil(user)); } @PostMapping("/join") public ResponseEntity<?> join(@RequestBody UserRequest.JoinDTO requestDTO){ User user = userService.회원가입(requestDTO); return ResponseEntity.ok(new ApiUtil(user)); // 프론트에서 자동으로 세션을 가짐 } @PostMapping("/login") public ResponseEntity<?> login(@RequestBody UserRequest.LoginDTO requestDTO){ User sessionUser =userService.로그인(requestDTO); session.setAttribute("sessionUser",sessionUser); return ResponseEntity.ok(new ApiUtil(null)); } @PutMapping("/api/users/{id}") //회원 수정은 세션 id 를 받기 때문에 주소에 id가 필요없다. 하지만 프론트 입장에선 필요하다. 또 관리자가 수정을 해야한다면 구분이 필요하다. public ResponseEntity<?> update(@RequestBody @PathVariable Integer id, UserRequest.UpdateDTO requestDTO){ User sessionUser = (User) session.getAttribute("sessionUser"); User newSessionUser = userService.회원수정(sessionUser.getId(),requestDTO); session.setAttribute("sessionUser",newSessionUser); return ResponseEntity.ok(new ApiUtil(newSessionUser)); } @GetMapping("/logout") public ResponseEntity<?> logout() { session.invalidate(); return ResponseEntity.ok(new ApiUtil(null)) ; } }
UserJPARepository
package shop.mtcoding.blog.board; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import java.util.Optional; public interface BoardJPARepository extends JpaRepository<Board,Integer> { @Query("select b from Board b join fetch b.user where b.id = :id") Optional<Board> findByIdJoinUser(@Param("id") int id); @Query("select b from Board b join fetch b.user u left join fetch b.replies r where b.id = :id") Optional<Board> findByIdJoinUserAndReplies(@Param("id") int id); }
UserRequest
package shop.mtcoding.blog.user; import lombok.Data; public class UserRequest { @Data public static class JoinDTO{ private String username; private String password; private String email; public User toEntity(){ return User.builder() .username(username) .password(password) .email(email) .build(); } } @Data public static class LoginDTO{ private String username; private String password; } @Data public static class UpdateDTO{ private String password; private String email; } }
UserService
package shop.mtcoding.blog.board; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import shop.mtcoding.blog._core.err.exception.Exception403; import shop.mtcoding.blog._core.err.exception.Exception404; import shop.mtcoding.blog.user.User; import java.util.List; @RequiredArgsConstructor @Service public class BoardService { private final BoardJPARepository boardJPARepository; @Transactional public Board 글쓰기(BoardRequest.SaveDTO requestDTO, User sessionUser) { return boardJPARepository.save(requestDTO.toEntity(sessionUser)); } @Transactional public Board 글조회(int boardId, int sessionUserId) { //조회 및 예외처리 Board board = boardJPARepository.findById(boardId) .orElseThrow(() -> new Exception404("게시글을 찾을 수 없습니다.")); //권한 처리, if (sessionUserId != board.getUser().getId()) { throw new Exception403("게시글을 수정할 권한이 없습니다"); } return board; } @Transactional public void 글삭제(int boardId, int sessionUserId) { Board board = boardJPARepository.findById(boardId).orElseThrow(() -> new Exception404("게시글을 찾을 수 없습니다.")); if (sessionUserId != board.getUser().getId()) { throw new Exception403("게시글을 삭제할 권한이 없습니다"); }// 트랜잭션은 런타임익셉션이 발동하면 롤백된다. boardJPARepository.deleteById(boardId); } public List<Board> 글목록조회() { Sort sort = Sort.by(Sort.Direction.DESC, "id"); List<Board> boardList = boardJPARepository.findAll(sort); return boardList; } public Board 글상세보기(int boardId, User sessionUser) { Board board = boardJPARepository.findByIdJoinUser(boardId) .orElseThrow(() -> new Exception404("게시글을 찾을 수 없습니다")); boolean isBoardOwner = false; if (sessionUser != null) { if (sessionUser.getId() == board.getUser().getId()) { isBoardOwner = true; } } board.setBoardOwner(isBoardOwner); board.getReplies().forEach(reply ->{ boolean isReplyOwner = false ; if(sessionUser!=null) { if (reply.getUser().getId() == sessionUser.getId()) { isReplyOwner =true; } } reply.setReplyOwner(isReplyOwner); }); return board; } }
 

Reply

Reply
package shop.mtcoding.blog.reply; import jakarta.persistence.*; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.hibernate.annotations.CreationTimestamp; import shop.mtcoding.blog.board.Board; import shop.mtcoding.blog.user.User; import java.time.LocalDateTime; @NoArgsConstructor // 빈생성자가 필요 @Entity @Data @Table(name = "reply_tb") public class Reply { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id ; private String comment; //user_id 로 테이블 만들어짐. @ManyToOne(fetch = FetchType.LAZY) private User user ; @ManyToOne(fetch = FetchType.LAZY) private Board board ; @CreationTimestamp private LocalDateTime createdAt; @Transient private boolean isReplyOwner; @Builder public Reply(int id, String comment, User user, Board board, LocalDateTime createdAt) { this.id = id; this.comment = comment; this.user = user; this.board = board; this.createdAt = createdAt; } }
ReplyController
package shop.mtcoding.blog.reply; import jakarta.servlet.http.HttpSession; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import shop.mtcoding.blog._core.util.ApiUtil; import shop.mtcoding.blog.user.User; @RequiredArgsConstructor @Controller public class ReplyController { private final ReplyService replyService ; private final HttpSession session; @PostMapping("/api/replies") public ResponseEntity<?> save(@RequestBody ReplyRequest.SaveDTO requestDTO){ User sessionUser = (User) session.getAttribute("sessionUser"); Reply reply = replyService.댓글쓰기(requestDTO,sessionUser); return ResponseEntity.ok(new ApiUtil(reply)) ; } @DeleteMapping("/api/replies/{id}") public ResponseEntity<?> delete(@RequestBody @PathVariable("id") Integer id){ User sessionUser = (User) session.getAttribute("sessionUser"); replyService.댓글삭제(id,sessionUser); return ResponseEntity.ok(new ApiUtil(null)) ; } }
ReplyJPARepository
package shop.mtcoding.blog.reply; import org.springframework.data.jpa.repository.JpaRepository; public interface ReplyJPARepository extends JpaRepository<Reply,Integer> {}
ReplyRequest
package shop.mtcoding.blog.reply; import lombok.Data; import shop.mtcoding.blog.board.Board; import shop.mtcoding.blog.user.User; public class ReplyRequest { @Data public static class SaveDTO{ private Integer boardId ; private String comment ; public Reply toEntity(User sessionUser, Board board) { return Reply.builder() .comment(comment) .board(board) .user(sessionUser) .build(); } } }
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.err.exception.Exception403; import shop.mtcoding.blog._core.err.exception.Exception404; import shop.mtcoding.blog.board.Board; import shop.mtcoding.blog.board.BoardJPARepository; import shop.mtcoding.blog.user.User; @RequiredArgsConstructor @Service public class ReplyService { private final ReplyJPARepository replyJPARepository ; private final BoardJPARepository boardJPARepository ; @Transactional public Reply 댓글쓰기(ReplyRequest.SaveDTO requestDTO, User sessionUser) { Board board = boardJPARepository.findById(requestDTO.getBoardId()) .orElseThrow(() -> new Exception404("없는 게시글에 댓글을 작성할 수 없습니다.")); Reply reply = requestDTO.toEntity(sessionUser,board); replyJPARepository.save(reply); return reply ; } @Transactional public int 댓글삭제(Integer replyId,User sessionUser){ Reply reply = replyJPARepository.findById(replyId).orElseThrow(() -> new Exception404("댓글을 찾을 수 없습니다.")); int boardId = reply.getBoard().getId(); // 댓글 삭제 전 게시글 번호 남겨놓음 if(sessionUser.getId()!=reply.getUser().getId()){ throw new Exception403("댓글을 삭제할 권한이 없습니다."); } replyJPARepository.deleteById(replyId); return boardId; } }
 

_core

config/WebMvcConfig
package shop.mtcoding.blog._core.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import shop.mtcoding.blog._core.interceptor.LoginInterceptor; @Configuration // ioc 등록 public class WebMvcConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) .addPathPatterns("/api/**") .excludePathPatterns("/api/boards/{id:\\d+}/detail"); } }
err/MyExeptionHandler
package shop.mtcoding.blog._core.err; import jakarta.servlet.http.HttpServletRequest; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestController; import shop.mtcoding.blog._core.err.exception.*; import shop.mtcoding.blog._core.util.ApiUtil; @RestControllerAdvice //runtimeException 이 터지만 해당 파일로 오류가 모인다. public class MyExeptionHandler{ @ExceptionHandler(Exception400.class) public ResponseEntity<?> ex400(RuntimeException e){ ApiUtil<?> apiUtil = new ApiUtil<>(400,e.getMessage()); // http body 내부의 구성한 객체 return new ResponseEntity<>(apiUtil, HttpStatus.BAD_REQUEST); // http header , http body } @ExceptionHandler(Exception401.class) public ResponseEntity<?> ex401(RuntimeException e){ ApiUtil<?> apiUtil = new ApiUtil<>(401,e.getMessage()); // http body 내부의 구성한 객체 return new ResponseEntity<>(apiUtil,HttpStatus.UNAUTHORIZED); } @ExceptionHandler(Exception403.class) public ResponseEntity<?> ex403(RuntimeException e){ ApiUtil<?> apiUtil = new ApiUtil<>(403,e.getMessage()); // http body 내부의 구성한 객체 return new ResponseEntity<>(apiUtil,HttpStatus.FORBIDDEN); } @ExceptionHandler(Exception404.class) public ResponseEntity<?> ex404(RuntimeException e){ ApiUtil<?> apiUtil = new ApiUtil<>(404,e.getMessage()); // http body 내부의 구성한 객체 return new ResponseEntity<>(apiUtil,HttpStatus.NOT_FOUND); } @ExceptionHandler(Exception500.class) public ResponseEntity<?> ex500(RuntimeException e){ ApiUtil<?> apiUtil = new ApiUtil<>(500,e.getMessage()); // http body 내부의 구성한 객체 return new ResponseEntity<>(apiUtil,HttpStatus.INTERNAL_SERVER_ERROR); } }
err/exception/Exception400~500
package shop.mtcoding.blog._core.err.exception; public class Exception400 extends RuntimeException{ public Exception400(String msg) { super(msg); } }
interceptor/LoginInterceptor
package shop.mtcoding.blog._core.interceptor; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import org.hibernate.query.sqm.mutation.internal.Handler; import org.springframework.web.servlet.HandlerInterceptor; import shop.mtcoding.blog._core.err.exception.Exception401; import shop.mtcoding.blog.user.User; public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandler,,,,,,,,"); HttpSession session = request.getSession(); User sessionUser = (User) session.getAttribute("sessionUser"); if(sessionUser ==null){ throw new Exception401("로그인이 필요합니다."); } return true; } }
util/ApiUtil
package shop.mtcoding.blog._core.util; import lombok.Data; @Data public class ApiUtil<T> { private Integer status ; //200,400,404,405 private String msg ; // 성공, 실패 -> 메세지 성공시에는 메세지 필요없음 private T body ; // 타입이 정해져있지 않아 new 때 타입을 정하는 T로 정함 //통신이 성공했을 때 public ApiUtil(T body) { this.status = 200; this.msg ="성공"; this.body = body; } //통신이 실패했을 때 public ApiUtil(Integer status, String msg) { this.status = status; this.msg = msg; this.body=null; // 실패 시에는 메세지와 상태코드만 필요하기 때문에 바디 데이터는 필요없다. } }
 

1. 요청 BODY 수정

💡
요청 BODY에 @RequestBody 어노테이션 붙이기 Post 와 Put 매핑에 붙인다.
 
@PutMapping("/api/users/{id}") public String update(@RequestBody UserRequest.UpdateDTO requestDTO){ User sessionUser = (User) session.getAttribute("sessionUser"); User user = userService.회원조회(sessionUser.getId(),requestDTO); session.setAttribute("sessionUser",user); return "redirect:/"; }
 

2. 응답 BODY 수정

💡
응답은 ReposnseEntity.ok(new ApiUtil()) 를 사용한다.
 
@RestController public class UserController { private final UserService userService; private final HttpSession session; @PutMapping("/api/users/{id}") //회원 수정은 세션 id 를 받기 때문에 주소에 id가 필요없다. 하지만 프론트 입장에선 필요하다. 또 관리자가 수정을 해야한다면 구분이 필요하다. public ResponseEntity<?> update(@RequestBody @PathVariable Integer id, UserRequest.UpdateDTO requestDTO){ User sessionUser = (User) session.getAttribute("sessionUser"); User newSessionUser = userService.회원조회(sessionUser.getId(),requestDTO); session.setAttribute("sessionUser",newSessionUser); return ResponseEntity.ok(new ApiUtil(newSessionUser)); } }
 
 
notion image
 
 
notion image
 
로그아웃 후 수정 요청을 하면 이렇게 뜬다.
 
 
notion image
 
로그인 완료
 
notion image
 
로그인 실패
 
 
@PostMapping("/api/boards") public ResponseEntity<?> save(@RequestBody BoardRequest.SaveDTO requestDTO){ User sessionUser = (User) session.getAttribute("sessionUser"); Board board = boardService.글쓰기(requestDTO,sessionUser); return ResponseEntity.ok(new ApiUtil(board)) ; }
 
notion image
 
💡
제이슨으로 엔티티를 참조하면 엔티티 내부에 엔티티가 호출되면 무한순환 참조가 되면서 터진다. 그래서 DTO 가 필요하다.
 
 
@GetMapping("/") public ResponseEntity<?> main(){ List<Board> boardList = boardService.글목록조회(); return ResponseEntity.ok(new ApiUtil(boardList)); //디스패쳐 서블릿은 오브젝트가 리턴되면, 메세지컨버터가 보드에 있는 애들을 getter로 제이슨으로 변환 // GETuser를 하는 순간 없기 때문에 select 로 통신. 근데 화면에 그림을 그릴 때는 null 이기 때문에 터짐. // 메세지 컨버터가 기다린다면 괜찮음. 근데 }
 
notion image
 
💡
DTO 서비스에서 안만들면 문제? 1. 커넥션의 시간이 길어진다. 2. 쓸데없는 필드를 응답하게 된다. 3. 메세지 컨버터에서 JSON 만들 때 HttpMessageConversionException 오류 터짐 해결법 - OPEN-IN-VIEW : false - 서비스 종료 직전 Lazy Loading 발동 시키기 - 서비스에서 DTO 를 만들어야 함.
Share article

{CODE-RYU};