Mybatis/실전 프로젝트 예제

게시판 구현 (페이징, 검색 포함)

DEVLIB 2025. 4. 17. 10:13
728x90

1. 프로젝트 구조 개요

src/
├── controller        ← BoardController.java
├── domain            ← Board.java, BoardSearchCondition.java
├── mapper            ← BoardMapper.java, BoardMapper.xml
└── service           ← BoardService.java

2. Board 도메인 클래스

public class Board {
    private Long id;
    private String title;
    private String content;
    private String writer;
    private LocalDateTime createdAt;
}

3. 검색 + 페이징 조건 DTO

public class BoardSearchCondition {
    private String keyword;
    private String writer;
    private int page = 1;
    private int size = 10;

    public int getOffset() {
        return (page - 1) * size;
    }
}

4. Mapper 인터페이스

@Mapper
public interface BoardMapper {
    List<Board> search(BoardSearchCondition condition);
    int count(BoardSearchCondition condition);

    void insert(Board board);
    Board findById(Long id);
    void update(Board board);
    void delete(Long id);
}

5. Mapper XML (BoardMapper.xml)

<mapper namespace="com.example.board.mapper.BoardMapper">

  <select id="search" resultType="Board" parameterType="BoardSearchCondition">
    SELECT * FROM board
    <where>
      <if test="keyword != null and keyword != ''">
        AND title LIKE CONCAT('%', #{keyword}, '%')
      </if>
      <if test="writer != null and writer != ''">
        AND writer = #{writer}
      </if>
    </where>
    ORDER BY created_at DESC
    LIMIT #{size} OFFSET #{offset}
  </select>

  <select id="count" resultType="int" parameterType="BoardSearchCondition">
    SELECT COUNT(*) FROM board
    <where>
      <if test="keyword != null and keyword != ''">
        AND title LIKE CONCAT('%', #{keyword}, '%')
      </if>
      <if test="writer != null and writer != ''">
        AND writer = #{writer}
      </if>
    </where>
  </select>

  <insert id="insert" parameterType="Board">
    INSERT INTO board (title, content, writer, created_at)
    VALUES (#{title}, #{content}, #{writer}, NOW())
  </insert>

  <select id="findById" resultType="Board" parameterType="long">
    SELECT * FROM board WHERE id = #{id}
  </select>

  <update id="update" parameterType="Board">
    UPDATE board
    SET title = #{title}, content = #{content}
    WHERE id = #{id}
  </update>

  <delete id="delete" parameterType="long">
    DELETE FROM board WHERE id = #{id}
  </delete>

</mapper>

6. 서비스 클래스

@Service
@RequiredArgsConstructor
public class BoardService {

    private final BoardMapper boardMapper;

    public List<Board> search(BoardSearchCondition condition) {
        return boardMapper.search(condition);
    }

    public int count(BoardSearchCondition condition) {
        return boardMapper.count(condition);
    }

    public void create(Board board) {
        boardMapper.insert(board);
    }

    public Board get(Long id) {
        return boardMapper.findById(id);
    }

    public void update(Board board) {
        boardMapper.update(board);
    }

    public void delete(Long id) {
        boardMapper.delete(id);
    }
}

7. REST Controller

@RestController
@RequestMapping("/api/boards")
@RequiredArgsConstructor
public class BoardController {

    private final BoardService boardService;

    @GetMapping
    public Map<String, Object> list(BoardSearchCondition condition) {
        List<Board> list = boardService.search(condition);
        int total = boardService.count(condition);
        return Map.of(
            "total", total,
            "list", list
        );
    }

    @PostMapping
    public void create(@RequestBody Board board) {
        boardService.create(board);
    }

    @GetMapping("/{id}")
    public Board detail(@PathVariable Long id) {
        return boardService.get(id);
    }

    @PutMapping("/{id}")
    public void update(@PathVariable Long id, @RequestBody Board board) {
        board.setId(id);
        boardService.update(board);
    }

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

8. 테이블 구조 (MySQL 기준)

CREATE TABLE board (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  title VARCHAR(200),
  content TEXT,
  writer VARCHAR(100),
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

전체 기능 요약

기능 URL HTTP 설명
게시글 목록 조회 (검색+페이징) /api/boards?keyword=공지&page=1&size=10 GET 조건부 목록
게시글 상세 조회 /api/boards/{id} GET 개별 게시글
게시글 작성 /api/boards POST 새 게시글 등록
게시글 수정 /api/boards/{id} PUT 제목/내용 수정
게시글 삭제 /api/boards/{id} DELETE 삭제 처리

확장 아이디어

  • 댓글 기능 추가 (1:N 매핑)
  • 파일 업로드 연동 (S3, 로컬)
  • 정렬 조건 추가 (작성일, 조회수 등)
  • 페이지네이션 응답 포맷 개선 (totalPages, hasNext 등)
LIST