IntelliJ, VScode 활용한 게시판 만들기 5탄

Jinju Bae·2022년 2월 20일
0

여러분이 게시글 insert 를 무사히 마쳤다면 이번 편에서 다룰 update 와 delete 도 무난히 따라올 수 있을 것이다.

insert 때 했던 것처럼 xml - mapper interface - service - controller 차례로 코드를 수정해보자.

들어가기에 앞서 전체적인 원리를 설명해보자면,

xml 의 sql 문을 통해 들고 온 데이터를 mapper 가 가지고 있다가 service 로 가서 controller 로 간다.

main 은 데이터베이스에 있는 테이블을 그대로 가져와서 보여주는 역할만 할 뿐이다.

따라서 update 에서 수정된 제목과 내용을 그대로 main 에 전달하는 게 아니라 updateBoard 라는 메소드를 만들어 데이터베이스에 있는 데이터를 직접 수정할 수 있도록 해야 한다. 그러면 main 이 그 데이터를 반영하겠지.

BoardMapper.xml 수정

<update id="updateBoard" parameterType="wedatalab.bulletinboard.domain.Board">
    UPDATE tbl_board
    SET title=#{title}, content=#{content}
    WHERE boardId=#{boardId};
</update>

<delete id="deleteBoard" parameterType="Long">
    DELETE
    FROM tbl_board
    WHERE boardId=#{boardId};
</delete>

※ update 태그

  • parameterType 을 Board 클래스로 준 이유는 title, content, boardId 세 개의 데이터를 넘겨줘야 하기 때문이다. 반면 아래 delete 문에서는 boardId 하나만 넘겨주면 되기에 parameterType 을 boardId 의 타입인 Long 타입 하나로 특정해주었다.

  • #{ } 는 매개변수로 넘어온 값을 말한다. 따라서 SET 절은 매개변수로 넘어온 title 으로 기존 title 을 대체하고, 매개변수로 넘어온 content 으로 기존 content 을 대체한다는 의미이다.

  • WHERE 은 조건절에 해당한다. boardId 가 매개변수로 넘어온 boardId 와 같은 컬럼에 대해 SET 구문을 적용한다.

※ delete 태그

  • 위에서 말했다시피 boardId 하나만 넘겨주면 되기에 parameterType 을 Long 으로 특정했다. Long 은 tbl_board 의 PK (Primary Key) 인 boardId 의 타입이다.

  • tbl_board 테이블에서 boardId 가 매개변수 boardId 와 같은 컬럼을 삭제하겠다는 의미다.

BoardMapper.interface 수정

BoardMapper.interface 에 다음의 두 메소드를 추가한다.

void updateBoard(Board board);

void deleteBoard(Long boardId);
  • 메소드 이름은 위에서 작성한 BoardMapper.xml 에서의 id 값과 같아야 한다.

  • 매개변수가 parameterType 으로 설정된 값과 일치해야 한다.

BoardService.class 수정

기존 BoardService.class 에서 uploadBoard 메소드 앞에 @Transactional 어노테이션을 추가해준다.

아래 updateBoard 와 deleteBoard 메소드에 대해서도 코드를 추가한다.

@Transactional // 추가
public void uploadBoard(Board board) {
    boardMapper.uploadBoard(board);
}

@Transactional
public Object updateBoard(Board board) {

// boardMapper.updateBoard(board);
return boardMapper.updateBoard(board);
}

@Transactional
public void deleteBoard(Long boardId) {
    boardMapper.deleteBoard(boardId);
}

@Transactional 어노테이션이란?

  • 스프링에서 지원하는 선언적 트랜잭션이다.

  • 일반적으로 메소드, 클래스, 인터페이스 위에 추가하여 사용한다.

  • 적용된 범위에서는 트랜잭션 기능이 포함된 프록시 객체가 생성되어 자동으로 commit 혹은 rollback을 진행한다.

출처 https://velog.io/@kdhyo/JavaTransactional-Annotation-%EC%95%8C%EA%B3%A0-%EC%93%B0%EC%9E%90-26her30h

수정과 삭제를 할 수 있도록 하려면

게시글 페이지에 수정, 삭제 버튼을 띄워야 한다.

따라서, 게시글 페이지에 띄울 html 파일인 view.html 을 수정해보자.

view.html 수정

view.html 를 다음과 같이 수정한다.

<html xmlns xmlns:th="http://www.w3.org/1999/xhtml" : th="http://www.thymeleaf.org">
<head>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
  <meta charset="UTF-8">
  <title>Title</title>

</head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div class="container">
  <p>글번호 : [[${halo.boardId}]]</p>
  <p id="title">제목 : [[${halo.title}]]</p>
  <p>작성자 : [[${halo.name}]]</p>
  내용 :
  <div id="content">
<textarea readonly="readonly"
          class="form-control"
          th:text="${halo.content}">

</textarea>

  </div>
  <form th:action="@{update}" method="get">
    <input type="hidden" name="boardId" th:value="${halo.boardId}">
    <button type="submit">수정</button>
  </form>

  <form th:action="@{delete}" method="get">
    <input type="hidden" name="boardId" th:value="${halo.boardId}">
    <button type="submit">삭제</button>
  </form>

</div>
</body>
</html>

다음의 세 요소를 추가한다.

  • p 태그에 id

  • textarea 태그에 readonly 옵션

  • form 태그로 감싼 수정 및 삭제 버튼

수정 삭제 버튼 코드를 자세히 살펴보자.

  <form th:action="@{update}" method="get">
    <input type="hidden" name="boardId" th:value="${halo.boardId}">
    <button type="submit">수정</button>
  </form>

  <form th:action="@{delete}" method="get">
    <input type="hidden" name="boardId" th:value="${halo.boardId}">
    <button type="submit">삭제</button>
  </form>

form 태그를 통해 수정 화면 (update) 에 파라미터인 boardId 를 넘길 수 있다.

이 과정은 아래 게시물에 자세히 나와 있으니 참고하자.

https://blog.naver.com/jjb0010/222618979324

작성한 뒤 게시글을 조회하면

위와 같이 수정/삭제 버튼이 생성된 것을 확인할 수 있다.

수정 버튼을 누르면 게시글 상세보기 화면 (view) 에서 게시글 수정 화면 (update) 에 파라미터인 boardId 를 넘어가도록 했으니

이제 컨트롤러에 update 가 파라미터를 받을 수 있는 코드를 작성하고

게시글 수정 화면 (update) 에 띄울 update.html 을 만들어주자.

먼저 컨트롤러를 수정해볼 것이다.

BoardController 수정

위를 참고해 BoardController 에 다음과 같은 코드를 추가해보자.

    @GetMapping("/update")
    public String updateBoardForm(Model model, Long boardId) {
        model.addAttribute("update", service.getBoard(boardId));

        return "/boards/update";
    }

    @PostMapping("/update")
    public String updateBoard(Board board) {
        service.updateBoard(board);
        return "redirect:/board/main";
    }

    @GetMapping("/delete")
    public String deleteBoard(Long boardId) {
        service.deleteBoard(boardId);
        return "redirect:/board/main";
    }

@PostMapping("/update")

  • localhost:8080/board/update 를 호출하면 updateBoard 메소드가 동작하며, 파라미터 board 를 받아 service 의 updateBoard 를 호출한다.

  • controller -> service -> mapper -> xml를 거쳐 수정이 되고, 수정이 완료되면 http://localhost:8080/board/main의 페이지인 메인화면으로 이동한다. (redirect)

@GetMapping("/delete")

  • http://localhost:8080/board/delete를 호출하면 deleteBoard 메소드가 동작하며 파라미터 boardId 로 service를 호출해 컬럼이 DB 에서 삭제될 수 있도록 작성한다.

  • @PostMapping("/update") 과 같이 메인화면으로 redirect 한다.

update.html 생성

<!DOCTYPE html>
<html xmlns xmlns:th="http://www.w3.org/1999/xhtml" : th="http://www.thymeleaf.org">
<head>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div class="container">
    <form id="form" th:action="@{update}" method="post">
        <p>글번호 : [[${update.boardId}]]</p>
        제목 :
        <div id="title">
            <textarea
                    class="form-control" placeholder='제목 입력' name="title">
            </textarea>
        </div>
        <p>작성자 : [[${update.name}]]</p>
        내용 :
        <div id="content">
            <textarea
                    class="form-control" placeholder='내용 입력' name="content">
            </textarea>
        </div>
        <input type="hidden" name="boardId" th:value="${update.boardId}">
        <button type="submit">저장</button>
    </form>
</div>
</body>
</html>

컨트롤러에서 update 라는 모델 객체에 updateBoard 라는 메소드를 실행하여 얻은 데이터를 담았다. 그 데이터에서 boardId 와 name 을 가져와 글번호, 작성자 옆에 띄운다.

(updateBoard 메소드는 제목과 내용을 수정하는 메소드이다.

아래 BoardMapper.xml 참고)

<update id="updateBoard" parameterType="wedatalab.bulletinboard.domain.Board">
    UPDATE tbl_board
    SET title=#{title}, content=#{content}
    WHERE boardId=#{boardId};
</update>

placeholder 는 내용을 입력하기 전 textarea 칸에 띄우는 글자이다.

컨트롤러에서 updateBoard 메소드 앞에 @PostMapping("/update") 어노테이션을 했다.

사용자가 입력한 제목과 내용에 대해 updateBoard 메소드가 실행되기 위해서는 <div id="title"><div id="content"> 를 속성이 <form th:action="@{update}" method="post"></form> 태그로 감싸줘야 한다. 그래야 수정한 제목과 내용이 반영되기 때문이다.

만약, method="put" 등으로 한다면 메소드가 매칭되지 않는 문제가 발생한다.

정리하자면 updateBoard 메소드 앞에 @PutMapping("/update") 어노테이션이 되어 있다면 method="put" 으로,

@PostMapping("/update") 어노테이션이면 method="post" 로 일치시키는 것이 맞을 것이다.

다음의 게시글을 참고하면 작동 원리를 더 잘 이해할 수 있다.

https://blog.naver.com/jjb0010/222618979324

placeholder 에 '제목 입력', '내용 입력' 대신 원래 제목과 내용을 띄우기

update.html 의 제목 ~ 내용 부분을 다음과 같이 바꾸자.

    제목 :
    <div id="title">
        <textarea class="form-control" name='title'>[[${update.title}]]</textarea>
    </div>
    <p>작성자 : [[${update.name}]]</p>
    내용 :
    <div id="content">
        <textarea class="form-control" name='content'>[[${update.content}]]</textarea>
    </div>

그럼 placeholder 대신에 원래 제목과 내용을 수정할 수 있게 나온다.

조회수 반영하기

게시글을 조회할 때마다 조회수가 올라가도록 해보겠다.

BoardMapper.xml 에 다음과 같은 sql 을 작성한다.

<update id="viewCount">
    UPDATE tbl_board
    SET read = read + 1
    WHERE boardId=#{boardId};
</update>

read 라는 객체는 게시글 만들기 1탄에서 domain 폴더의 Board 클래스에 int 형식으로 선언되었다.

게시글을 조회할 때마다 read 에 1을 더한 값을 read 에 업데이트한다는 뜻이다.

같은 이름으로 메소드를 만들 건데, BoardMapper 인터페이스에 객체 메소드를 만든다. 다음의 코드를 추가하자.

void viewCount(Long boardId);

그럼 이제 실행이 될 수 있도록 컨트롤러를 건드려보자.

view 페이지를 클릭했을 때, 즉 localhost:8080/board/view?boardId= ~ 주소로 이동했을 때 viewCount 메소드가 실행되도록 해야 한다.

이를 위해 기존에 작성했던 viewBoard 메소드를 다음과 같이 수정해보자.

@GetMapping("/view")
public String viewBoard(Model model, Long boardId) {
    service.viewCount(boardId); // 추가
    model.addAttribute("halo", service.getBoard(boardId));

    return "/boards/view";
}

view 에 홈 버튼 추가

조회수가 잘 올라가는 지 확인하기 위해서는 게시글을 클릭한 다음 다시 메인 페이지로 돌아가야 한다. (단순히 뒤로가기 버튼을 누른다고 해서 조회수가 반영되지 않음)

view.html 의

태그 안에 홈 버튼을 추가해주자.

  <form th:action="@{main}" method="get">
    <button type="submit"></button>
  </form>

이로써 뷰 페이지에 생긴 홈 버튼을 누르면 main 화면으로 넘어가게 된다.

아직 새로 업데이트 된 게시글에 대해서는 조회수가 올라가지 않는 문제가 있는데, 이 문제는 6탄에서 소개하도록 하겠다.

profile
진주개발일지 (珍珠開發日誌)

1개의 댓글

comment-user-thumbnail
2022년 9월 28일

게시판 프로젝트가 너무 힘들어 도움 받고 싶습니다. 혹시 프로젝트 공유 가능하실까요?ㅜㅜ
가능하시다면 지금 바로 ssg638@naver.com으로 부탁합니다:)

답글 달기