[스프링 부트] 22. 게시판 v2 리팩토링(JPA, StreamAPI, Optional)

lhs's avatar
Nov 20, 2024
[스프링 부트] 22. 게시판 v2 리팩토링(JPA, StreamAPI, Optional)
 

1. Board

@NoArgsConstructor @AllArgsConstructor @Getter @ToString @Table(name = "board_tb") @Entity public class Board { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String title; private String content; @CreationTimestamp private Timestamp createdAt; public void update(String title, String content) { this.title = title; this.content = content; } }
  • 의미가 명확한 코드를 위해 setter 대신 update 메서드를 구현하여 글 수정 시 사용한다.
  • @CreationTimestamp 어노테이션을 사용하면 엔티티가 삽입될 때 자동으로 현재 시간이 해당 필드에 넣어준다.

2. SaveDTO

@Data public static class SaveDTO { private String title; private String content; public Board toEntity() { Board board = new Board(null, title, content, null); return board; } }
  • DTO 데이터를 JPA Repository가 처리할 수 있도록, toEntity 메서드를 구현해 JPA Entity로 매핑한다.

3. BoardRepository

@RequiredArgsConstructor @Repository public class BoardRepository { private final EntityManager entityManager; public void delete(int id) { entityManager.createQuery("delete from Board b where id=:id").setParameter("id", id).executeUpdate(); } public void save(Board board) { entityManager.persist(board); } public List<Board> findAll() { return entityManager.createQuery("select b from Board b order by b.id desc", Board.class).getResultList(); } public Optional<Board> findById(int id) { return Optional.ofNullable(entityManager.find(Board.class, id)); } }
  • createNativeQuery 대신 createQuery 메서드를 사용하여 JPQL을 활용한 간결한 쿼리문을 작성한다.
  • persist 메서드를 사용하여 Board 엔티티를 영속성 컨텍스트에 추가하고, 트랜잭션 커밋 시 DB에 저장된다.
  • find 메서드를 사용하여 특정 ID로 DB에서 엔티티를 조회한다.
  • 조회한 결과가 없을 경우 Optional을 사용하여 null 처리를 안전하게 한다.
  • update 메서드는 findById를 사용하여 JPA로 수정할 수 있으므로 삭제한다.

4. BoardService

@RequiredArgsConstructor @Service public class BoardService { private final BoardRepository boardRepository; @Transactional public void 게시글수정(int id, BoardRequest.UpdateDTO updateDTO) { Board board = boardRepository.findById(id).orElseThrow(() -> new RuntimeException("해당 id의 게시글이 없습니다 : " + id)); board.update(updateDTO.getTitle(), updateDTO.getContent()); } public BoardResponse.UpdateFormDTO 게시글수정화면보기(int id) { Board board = boardRepository.findById(id).orElseThrow(() -> new RuntimeException("해당 id의 게시글이 없습니다 : " + id)); return new BoardResponse.UpdateFormDTO(board); } @Transactional public void 게시글삭제(int id) { boardRepository.delete(id); } @Transactional public void 게시글쓰기(BoardRequest.SaveDTO saveDTO) { boardRepository.save(saveDTO.toEntity()); } public BoardResponse.DetailDTO 게시글상세보기(int id) { Board board = boardRepository.findById(id).orElseThrow(() -> new RuntimeException("해당 id의 게시글이 없습니다 : " + id)); return new BoardResponse.DetailDTO(board); } public List<BoardResponse.DTO> 게시글목록보기() { return boardRepository.findAll().stream() .map(BoardResponse.DTO::new) .toList(); } }
  • findById 메서드를 호출할 때 orElseThrow를 사용하여 조회 실패 시 예외 처리를 했다.
  • 게시글수정 메서드에서는 board 객체를 수정한 후, 더티 체크로 DB에 자동으로 갱신된다.
  • 게시글쓰기 메서드에서는 DTO를 Entity로 변환하여 저장한다.
  • 게시글목록보기 메서드에서는 조회한 정보를 Stream API를 사용해 DTO로 변환한 후, 리스트로 반환한다.
Share article

LHS's Study Space