JPA로 게시판 구현하기 - CRUD

1

JPA

목록 보기
16/16
post-thumbnail

jpa나 spring에 관련된 글은 많이 썻는데 정작 간단한 게시판 만드는 글을 안써서 오늘은 jpa를 사용해서 간단한 게시판 crud api를 개발해보겠습니다.


우선 엔티티를 먼저 생성해주겠습니다. @NoArgsConstructor는 기본 생성자를 생성해주는 어노테이션입니다. 접근 레벨은 PROTECTED 입니다.

그리고 밑에 있는 생성자는 @Builder 패턴을 사용했습니다.

Board

@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Board {

    @Id @GeneratedValue
    @Column(name = "board_id")
    private Long id;

    @Column(length = 15, nullable = false)
    private String writer;

    @Column(length = 100, nullable = false)
    private String title;

    @Column(columnDefinition = "TEXT", nullable = false)
    private String content;


    @Builder
    public Board(String writer, String title, String content) {
        this.writer = writer;
        this.title = title;
        this.content = content;
    }

}

엔티티를 그대로 반환해주면 안되기 때문에 DTO 클래스를 만들어주도록 하겠습니다.

BoardRequestDto

@Setter
@NoArgsConstructor
@Getter
public class BoardRequestDto {

    private Long id;
    private String writer;
    private String title;
    private String content;

    @Builder
    public BoardRequestDto(String writer, String title, String content) {
        this.writer = writer;
        this.title = title;
        this.content = content;
    }

    public Board ToEntity(){
        return Board.builder()
                .writer(this.writer)
                .title(this.title)
                .content(this.content)
                .build();
    }
}

BoardResponseDto


@Data
@NoArgsConstructor
public class BoardResponseDto {

    private Long id;
    private String writer;
    private String title;
    private String content;

    @Builder
    public BoardResponseDto(String writer, String title, String content) {
        this.writer = writer;
        this.title = title;
        this.content = content;
    }
}

그 다음은 CRUD 기능을 제공해줄 Repository를 만들어주도록 하겠습니다.

BoardRepository - 인터페이스

public interface BoardRepository extends JpaRepository<Board, Long> {

}

레파지토리를 생성해주었으니 비즈니스 로직을 구현해주기 위해 서비스 계층을 만들어보겠습니다. 서비스는 추상화를 적용하기 위해 Service와 ServiceImpl 2개의 클래스로 나누는 방식을 사용하겠습니다.

BoardService

public interface BoardService {

        public void savePost(BoardRequestDto boardDto);

        public List<BoardRequestDto> getBoardList(Integer pageNum);

        public BoardRequestDto getPost(Long id);

        public void deletePost(Long id);

        public List<BoardRequestDto> searchPosts(String keyword);

        public void update(Long id, BoardRequestDto dto);
}

BoardServiceImpl

@Service
@RequiredArgsConstructor
public class BoardServiceImpl implements BoardService{

    private final BoardRepository boardRepository;

    @Transactional
    @Override
    public void savePost(BoardRequestDto boardDto){
        boardRepository.save(boardDto.ToEntity());
    }

    @Transactional
    @Override
    public List<BoardRequestDto> getBoardList(){

        List<Board> all = boardRepository.findAll();
        List<BoardRequestDto> boardDtoList = new ArrayList<>();

        for(Board board : all){
            BoardRequestDto boardDto = BoardRequestDto.builder()
                    .title(board.getTitle())
                    .content(board.getContent())
                    .writer(board.getWriter())
                    .build();

            boardDtoList.add(boardDto);
        }

        return boardDtoList;
    }

    @Transactional
    @Override
    public BoardRequestDto getPost(Long id){
        Optional<Board> boardWrapper = boardRepository.findById(id);
        Board board = boardWrapper.get();

        return BoardRequestDto.builder()
                .title(board.getTitle())
                .content(board.getContent())
                .writer(board.getWriter())
                .build();
    }

    @Transactional
    @Override
    public void deletePost(Long id){
        boardRepository.deleteById(id);
    }

    @Transactional
    @Override
    public List<BoardRequestDto> searchPosts(String keyword){
        List<Board> boards = boardRepository.findByTitleContaining(keyword);
        List<BoardRequestDto> boardList = new ArrayList<>();

        for(Board board : boards){
            BoardRequestDto build = BoardRequestDto.builder()
                    .title(board.getTitle())
                    .content(board.getContent())
                    .writer(board.getWriter())
                    .build();

            boardList.add(build);
        }

        return boardList;
    }

    @Transactional
    @Override
    public void update(Long id, BoardRequestDto dto) {
        Optional<Board> byId = boardRepository.findById(id);
        Board board = byId.get();

        board.updateBoard(dto.getWriter(), dto.getTitle(), dto.getContent());
    }

 }

getBoardList, searchPosts 같이 컬렉션 조회는 반환할 때 for 문 안에 Builder 를 사용하면 코드가 상당히 길어집니다.

따라서 stream 의 map을 사용해서 바꾸어주는 것이 더 좋습니다.

하지만 stream 을 모르는 분들이 계실 수 있으니 for문으로 처리하였습니다.


대부분의 crud 기능 구현은 마쳤으니 이를 api로 만들어보겠습니다.

@RestController
@RequiredArgsConstructor
public class BoardApiController {

    private final BoardServiceImpl boardService;
    private final BoardRepository boardRepository;

    @PostMapping("/api/post")
    public BoardResponseDto savePost(@RequestBody @Valid BoardRequestDto request) {

        boardService.savePost(request);

        return new BoardResponseDto(
                request.ToEntity().getTitle(),
                request.ToEntity().getWriter(),
                request.ToEntity().getContent()
    }

    @PutMapping("/api/post/{id}")
    public BoardResponseDto updatePost(@PathVariable("id") Long id,
                                               @RequestBody @Valid BoardRequestDto request) {

        boardService.update(id, request);
        Optional<Board> findPost = boardRepository.findById(id);
        Board board = findPost.get();

        return new BoardResponseDto(
                board.getTitle(),
                board.getWriter(),
                board.getContent());
    }

    @GetMapping("/api/board/posts")
    public List<BoardRequestDto> findPosts(){
        List<Board> findAll = boardRepository.findAll();
        List<BoardRequestDto> allPost = new ArrayList<>();

        for(Board board : findAll){
            BoardRequestDto build = BoardRequestDto.builder()
                    .content(board.getContent())
                    .writer(board.getWriter())
                    .title(board.getTitle())
                    .build();

            allPost.add(build);
        }

        return allPost;
    }

    @GetMapping("/api/board/post/{id}")
    public BoardResponseDto findPost(@PathVariable("id") Long id){
        BoardRequestDto post = boardService.getPost(id);

        return new BoardResponseDto(
                post.getWriter(),
                post.getTitle(),
                post.getContent()
        );
    }

    @DeleteMapping("/api/post/delete/{id}")
    public void delete(@PathVariable("id") Long id){
        boardService.deletePost(id);
    }

}

이렇게 간단한 게시판 기능들을 api로 만들어보았습니다.
Postman과 같은 툴로 api를 검증해보면 결과가 잘 나오는 것을 확인하실 수 있습니다.

여기에 LocalDateTime과 페이지네이션(페이징) 등등 기능을 조금만 더 추가해주면 더 좋은 게시판이 될 것 같습니다.

0개의 댓글