:: Dev/Spring

[Sp-re-ing] 08 - 게시판 구현하기 (4) - 게시물 삭제 API

jETA 2020. 5. 6. 10:00
반응형

오늘 만들 것

이번 글에서는 게시판 구현의 마지막 단계인 게시글 삭제를 구현합니다.

게시글 삭제는 두 가지 방법을 사용할 수 있습니다.

실제로 DB에서 삭제하거나 삭제 표기를 하는 방법입니다.

 

이번 글에서는 주로 사용되는 방법인 삭제 표기를 진행합니다.

 

시작

이번 글은 Lab06의 코드에 기능을 추가하는 방식으로 진행합니다.
Lab06는 이전 글에서 확인할 수 있습니다.

별도의 프로젝트에서 진행하고자 하면, Lab06과 동일하게 프로젝트를 생성한 뒤 코드를 복사해서 준비합니다.
Lab06의 코드를 그대로 사용한다면 아래 예제 코드의 패키지명에 유의합니다.

아래 예제 코드는 별도의 프로젝트를 생성하는 방식으로 진행합니다.

 

SQL Mapper 수정

다음 경로의 파일을 아래와 같이 편집합니다: /src/main/resources/mappers/UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="net.jetalab.spreinglab08.dao.BoardDAO">

    <insert id="newBoard" parameterType="net.jetalab.spreinglab08.dto.BoardDTO" useGeneratedKeys="true" keyProperty="seq">
        INSERT INTO lab08(`title`, `contents`, `author`, `password`)
        VALUES (#{title}, #{contents}, #{author}, #{password})
    </insert>

    <select id="getBoard" parameterType="net.jetalab.spreinglab08.dto.BoardDTO" resultType="net.jetalab.spreinglab08.dto.BoardDTO">
        SELECT `seq`, `title`, `contents`, `author`, `reads`
          FROM lab08
         WHERE `seq` = #{seq}
           <if test="password != null">
           AND `password` = #{password}
           </if>
           AND `deleted` = 'N'
    </select>

    <update id="editBoard" parameterType="net.jetalab.spreinglab08.dto.BoardDTO">
        UPDATE lab08
           SET `password` = `password`
           <if test="title != null">
             , `title` = #{title}
           </if>
           <if test="contents != null">
             , `contents` = #{contents}
           </if>
           <if test="author != null">
             , `author` = #{author}
           </if>
           <if test="deleted != null">
             , `deleted` = #{deleted}
           </if>
         WHERE `seq` = #{seq}
    </update>

</mapper>

<update id="editBoard" />가 수정되었습니다.

23번째 줄 ~ 35번째 줄: password는 변경되지 않기에 그대로 두고, 변경이 요구된 항목만 변경되도록 설정합니다. 이렇게 구성하면 UserDTO에 값이 존재하는 항목만 UPDATE되도록 활용할 수 있고, 의도치 않은 데이터 손실을 피할 수 있습니다./p>

 

Controller 생성

DELETE로 삭제할 글의 ID와 그 글의 암호를 받을 Controller를 생성합니다.

다음 경로에 아래 코드를 작성하여 저장합니다: /src/main/java/YOUR/DOMAIN/ARTIFACT/controller/BoardController.java

package net.jetalab.spreinglab08.controller;

import net.jetalab.spreinglab08.dao.BoardDAO;
import net.jetalab.spreinglab08.dto.BoardDTO;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@EnableAutoConfiguration
@MapperScan(basePackages = "net.jetalab.spreinglab08.dao")
public class BoardController {

    @Autowired
    private BoardDAO boardDAO;

    @RequestMapping(value = "/board", method = RequestMethod.POST)
    public ResponseEntity<BoardDTO> postBoard(BoardDTO board) throws Exception {
        if ((board.getAuthor() == null) || (board.getContents() == null) || (board.getPassword() == null) || (board.getTitle() == null)) {
            return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
        }

        boardDAO.newBoard(board);
        return new ResponseEntity<>(board, HttpStatus.OK);
    }

    @RequestMapping(value = "/board/{seq}", method = RequestMethod.GET)
    public ResponseEntity<BoardDTO> getBoard(@PathVariable("seq") final int seq) throws Exception {
        BoardDTO param = new BoardDTO();
        param.setSeq(seq);

        /* TODO: 조회수 증가 */

        BoardDTO board = boardDAO.getBoard(param);

        if (board == null) return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        else return new ResponseEntity<>(board, HttpStatus.OK);
    }

    @RequestMapping(value = "/board/{seq}", method = RequestMethod.PUT)
    public ResponseEntity<BoardDTO> putBoard(@PathVariable("seq") final int seq, BoardDTO param) throws Exception {
        if ((param.getAuthor() == null) || (param.getContents() == null) || (param.getPassword() == null) || (param.getTitle() == null)) {
            return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
        }

        param.setSeq(seq); // 조회할 게시물 번호 지정
        BoardDTO board = boardDAO.getBoard(param);
        if (board == null) return new ResponseEntity<>(HttpStatus.NOT_FOUND);

        board.setTitle(param.getTitle());
        board.setContents(param.getContents());
        board.setAuthor(param.getAuthor());
        boardDAO.editBoard(board);

        return new ResponseEntity<>(board, HttpStatus.OK);
    }

    @RequestMapping(value = "/board/{seq}", method = RequestMethod.DELETE)
    public ResponseEntity<BoardDTO> deleteBoard(@PathVariable("seq") final int seq, BoardDTO param) throws Exception {
        if (param.getPassword() == null) {
            return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
        }

        param.setSeq(seq); // 조회할 게시물 번호 지정
        BoardDTO board = boardDAO.getBoard(param);
        if (board == null) return new ResponseEntity<>(HttpStatus.NOT_FOUND);

        board.setDeleted("Y");
        boardDAO.editBoard(board);

        return new ResponseEntity<>(HttpStatus.OK);
    }

}

DELETE /board/{seq}가 추가되었습니다.

66번째 줄 ~ 68번째 줄: password가 없다면 올바르지 않은 요청으로 보고 오류를 반환합니다.

74번째 줄: 만약 조회된 게시물이 있다면 deleted 플래그를 삭제된 것으로 지정합니다.

 

테스트

먼저 삭제할 글을 호출해봅니다.

오늘은 3번이다.

 

이제 삭제할 글 번호와 비밀번호를 입력해서 삭제를 요청해봅니다.

이 때, 만약 비밀번호가 틀리면 오류를 반환합니다.

여전히 404 오류가 잘 반환됩니다.

 

비밀번호가 올바르다면 정상적으로 처리됩니다.

데이터는 없지만 200 상태값을 반환했습니다.

 

이제 게시글을 다시 호출하면 404 오류가 반환됩니다.

게시글 호출도 할 수 없고,
두 번 삭제할 수도 없습니다.

 

DB에도 삭제된 상태가 지정되어 있습니다.

3번 게시글의 deleted가 Y입니다.

 

예제 코드

본 포스트의 예제 코드는 GitHub에 공개되어 있습니다.

https://github.com/jETA-Kor/sp-re-ing/tree/master/lab08

반응형