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));
}
}


로그아웃 후 수정 요청을 하면 이렇게 뜬다.

로그인 완료

로그인 실패
@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)) ;
}

제이슨으로 엔티티를 참조하면 엔티티 내부에 엔티티가 호출되면 무한순환 참조가 되면서 터진다. 그래서 DTO 가 필요하다.
@GetMapping("/")
public ResponseEntity<?> main(){
List<Board> boardList = boardService.글목록조회();
return ResponseEntity.ok(new ApiUtil(boardList));
//디스패쳐 서블릿은 오브젝트가 리턴되면, 메세지컨버터가 보드에 있는 애들을 getter로 제이슨으로 변환
// GETuser를 하는 순간 없기 때문에 select 로 통신. 근데 화면에 그림을 그릴 때는 null 이기 때문에 터짐.
// 메세지 컨버터가 기다린다면 괜찮음. 근데
}

DTO 서비스에서 안만들면 문제?
1. 커넥션의 시간이 길어진다.
2. 쓸데없는 필드를 응답하게 된다.
3. 메세지 컨버터에서 JSON 만들 때 HttpMessageConversionException 오류 터짐
해결법
- OPEN-IN-VIEW : false
- 서비스 종료 직전 Lazy Loading 발동 시키기
- 서비스에서 DTO 를 만들어야 함.
Share article