ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [3주차] CRUD(by JPA) 설계 후 Postman으로 실행하기
    SpringBoot 2023. 11. 28. 14:28

    3주차 과제

    꽤나... 오래걸림 

    개념이 제대로 잡혀있지 않음을 깨달았다 

     

     

    출처: 코딩하는 어린 콩

    이 그림이 전체적 파일 구성과 이해에 도움을 준 거 같아서 첨부 

    presentation layer(controller) / business layer(service) / persistence layer(repository)

    세가지 간의 흐름에 대한 이해가 필요하다 

     

     

     

    Board.java 

    package gdsctuk.sbbasic.sptingbootstudybasic.entity;
    
    import lombok.*;
    
    import jakarta.persistence.*;
    import org.springframework.data.annotation.CreatedDate;
    import org.springframework.data.annotation.LastModifiedDate;
    import org.springframework.data.jpa.domain.support.AuditingEntityListener;
    
    import java.time.LocalDateTime;
    
    @Getter
    @Setter
    @NoArgsConstructor
    @Entity
    @EntityListeners(AuditingEntityListener.class)
    @Table(name = "Board")
    public class Board {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(nullable = false, updatable = false)
        //@OneToMany(mappedBy = "bId", cascade = CascadeType.ALL)
        private Long boardId;
    
        @Column(length = 40, nullable = false)
        private String boardTitle;
    
        @Column(length = 10000, nullable = false)
        private String boardContent;
    
        @Column(length = 20, nullable = false)
        private String boardWriter;
    
        @CreatedDate
        private LocalDateTime boardWriteTime;
    
        @LastModifiedDate
        private LocalDateTime boardEditTime;
    
        private Boolean isDeleted; // Null default - soft delete 판별용
    
    
    
        // 값 주입을 위해 사용; builder
        @Builder
        public Board(Long boardId, String boardTitle, String boardContent, String boardWriter,
                     LocalDateTime boardWriteTime, LocalDateTime boardEditTime) {
            this.boardId = boardId;
            this.boardTitle = boardTitle;
            this.boardContent = boardContent;
            this.boardWriter = boardWriter;
            this.boardWriteTime = boardWriteTime;
            this.boardEditTime = boardEditTime;
        }
    
        public void updateBoard(String boardTitle, String boardContent) {
            this.boardId = boardId;
            this.boardTitle = boardTitle;
            this.boardContent = boardContent;
        }
    
        public void delete() {
            this.isDeleted = true;
        }
    
    }

     

     

     

     

    Response Dto는 여러 개를 만들어서 용도에 따라 사용하였다 

    BoardResponseDto.java
    BoardResponseIdDto.java / BoardResponseListDto

    BoardId가 기본키이기 때문에 해당 값을 반환해 주는 DTO / Board 목록을 조회할 때 list 형으로 반환하는 DTO로 추가

     

     

    RequestDto 

    BoardRequestDto.java / BoardUpdateDto.java

    필요로 하는 요소가 달라서 기본 RequestDto와 업데이트 시에 이용하는 dto로 분리해서 활용하였다. 

     

     

     

    BoardRepository.java(interface) - 메서드를 사용하기 위해 구성, 이때 JPA가 기본 제공하는 메서드 사용이 가능하다 

    package gdsctuk.sbbasic.sptingbootstudybasic.repository;
    
    import gdsctuk.sbbasic.sptingbootstudybasic.entity.Board;
    import org.springframework.data.jpa.repository.JpaRepository;
    
    import java.util.List;
    import java.util.Optional;
    
    //반환 형태, method 명을 미리 선언 - interface
    public interface BoardRepository extends JpaRepository<Board, Long>{
    
        Board save(Board board);
        Optional<Board> findById(Long id);
        List<Board> findAll(); // method - 반환값 List(Board)
    }

     

     

     

    BoardMapper.java - builder 패턴을 활용하여 mapping 구성

    package gdsctuk.sbbasic.sptingbootstudybasic.mapper;
    
    
    import gdsctuk.sbbasic.sptingbootstudybasic.dto.BoardRequestDto;
    import gdsctuk.sbbasic.sptingbootstudybasic.dto.BoardResponseDto;
    import gdsctuk.sbbasic.sptingbootstudybasic.dto.BoardResponseIdDto;
    import gdsctuk.sbbasic.sptingbootstudybasic.dto.BoardResponseListDto;
    import gdsctuk.sbbasic.sptingbootstudybasic.entity.Board;
    import org.springframework.stereotype.Component;
    
    import java.util.List;
    import java.util.stream.Collectors;
    
    @Component
    public class BoardMapper {
    
        public Board toEntity(BoardRequestDto request) { // entity에 알맞게 mapping해주기 위해 선언 - 보드라는 엔티티에 매핑해 주기 위해
            return Board.builder()
                    .boardTitle(request.getBoardTitle())
                    .boardContent(request.getBoardContent())
                    .boardWriter(request.getBoardWriter())      // nullable = false 값들은 모두 포함
                    .build();
        }
    
        public BoardResponseIdDto toResponseId(Board board){
            return BoardResponseIdDto.builder()
                    .id(board.getBoardId())
                    .build();
        }
    
        public BoardResponseDto toResponse(Board board) {
            return BoardResponseDto.builder()
                    .boardId(board.getBoardId())
                    .boardTitle(board.getBoardTitle())
                    .boardContent(board.getBoardContent())
                    .boardEditTime(board.getBoardEditTime())
                    .build();
        }
    
        public BoardResponseListDto toListResponse(List<Board> boardList) {
            List<BoardResponseDto> boardResponseList =
                    boardList.stream().map(this::toResponse).collect(Collectors.toList());
                                // 순차 method/클래스 내의 toResponse 사용
    
            return BoardResponseListDto.builder()
                    .boardList(boardResponseList)
                    .build();
        }
    }

     

     

    BoardService.java - 비즈니스 로직을 수행하고, 알맞은 정보로 가공하는 부분 

    package gdsctuk.sbbasic.sptingbootstudybasic.service;
    
    import gdsctuk.sbbasic.sptingbootstudybasic.dto.*;
    import gdsctuk.sbbasic.sptingbootstudybasic.entity.Board;
    import gdsctuk.sbbasic.sptingbootstudybasic.mapper.BoardMapper;
    import gdsctuk.sbbasic.sptingbootstudybasic.repository.BoardRepository;
    import lombok.RequiredArgsConstructor;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    import java.util.List;
    
    
    @Service
    @RequiredArgsConstructor
    public class BoardService {
        // request가 보드에 들어갈 수 있는 mapper 짜서 board rep save 하도록
    
        private final BoardRepository boardRepository; //private final 선언해야 17번 의존성 주입 가능
        private final BoardMapper boardMapper;
    
        // 게시글 생성
        public BoardResponseIdDto createBoard(BoardRequestDto request) {
            Board board = boardRepository.save(boardMapper.toEntity(request)); // 값 저장
            BoardResponseIdDto boardResponseIdDto = boardMapper.toResponseId(board);  //return boardMapper.toResponseId(board);
            return boardResponseIdDto;
        }
    
    
        // 게시글 List 조회
        public BoardResponseListDto findAllBoard() {
            List<Board> boards = boardRepository.findAll(); // List는 s 붙여서 처리 많음
    
            return boardMapper.toListResponse(boards);
        }
    
        // 게시글 하나 조회
        public BoardResponseDto findOneBoard(Long id) {
            Board board = boardRepository.findById(id)
                    .orElseThrow(IllegalStateException::new);
    
            return boardMapper.toResponse(board);
        }
    
        // 게시글 수정
        @Transactional
        public BoardResponseDto updateBoard(BoardUpdateDto request) {
            Board board = boardRepository.findById(request.getBoardId())
                    .orElseThrow(IllegalStateException::new);
    
            board.updateBoard(request.getBoardTitle(), request.getBoardContent());
    
            return boardMapper.toResponse(board);
        }
    
    
        // 게시글 삭제
        @Transactional
        public  BoardResponseDto deleteBoard(Long id) {
            Board board = boardRepository.findById(id)
                    .orElseThrow(IllegalStateException::new);
            board.delete();
    
    
            return boardMapper.toResponse(board);
        }
    }

    이때 detele의 경우 soft delete를 활용하였다. 

     

     

    BoardController.java - 사용자의 요청을 처리 후 지정된 View에 모델 객체를 넘겨주는 역할(코딩하는 어린 콩) 

    @RequiredArgsConstructor
    @RestController
    @RequestMapping("/boards")
    public class BoardController {
    
        private final BoardService boardService;
        private final BoardMapper boardMapper;
    
        @PostMapping(value = "/create")
        public ResponseEntity<BoardResponseIdDto> createBoard(@RequestBody BoardRequestDto request){
            return ResponseEntity.ok(boardService.createBoard(request));
    
        }
    
        @GetMapping("/list")
        public ResponseEntity<BoardResponseListDto> findALlBoard() {
            return ResponseEntity.ok(boardService.findAllBoard());
        }
    
        @GetMapping("/find/{id}")
        public ResponseEntity<BoardResponseDto> findById(@PathVariable Long id) {
            return ResponseEntity.ok(boardService.findOneBoard(id));
        }
        
        @PutMapping("/update/{id}")
        public ResponseEntity<BoardResponseDto> updateBoard(@RequestBody BoardUpdateDto request) {
            return ResponseEntity.ok(boardService.updateBoard(request));
        }
    
        @DeleteMapping("/delete/{id}")
        public ResponseEntity<BoardResponseDto> deleteBoard(@PathVariable Long id) {
            return ResponseEntity.ok(boardService.deleteBoard(id));
        }
    
    }

     

     

    Postman 테스트 

    Create 

     

     

     

    List(FindAll)

    * null 값으로 보이는 부분은 오류가 아니고 dto/mapper에서 반환을 안 해줘서 존재하지 않는 것이다

     

    FindOne

     

     

    Update

    기존 내용: boot

    아이디, 제목, 내용 값을 받아서 수정해 주는 걸로 설계하였는데 아이디 값은 일단은 참조 값으로 url에 활용해야 해서 집어넣은 거긴 한데 업데이트하는 부분에서는 제외해야 할 것 같다(무결성 문제 등) 

     

     

    Delete

    Postman에서 삭제 요청 후 MySQL에 접속해서 Board를 조회해 보니 

    id 값이 2인 행의 is_deleted 값이 1(true)로 soft delete가 이루어짐을 확인해 볼 수 있었다

     이처럼 soft delete는 실제로 행을 삭제하는 것이 아닌, is_delete라는 boolean 속성의 값을 true로 반환하여 

    delete됨을 labeling 하는 방법이라고 이해하면 될 것 같다 

    'SpringBoot' 카테고리의 다른 글

    ch2 - Spring Data JPA  (0) 2023.12.21
    [5주차] 댓글 CRUD 구현  (0) 2023.12.04
    [4주차] 지난 피드백 반영, Git에 코드 올리기  (0) 2023.11.30
    [2주차]DB 설계(by JPA)  (1) 2023.11.12
    [1주차] 개발 환경 세팅(+ DB 연동)  (0) 2023.11.06

    댓글