본문 바로가기

스프링 부트/웹MVC

글 등록, 글 목록 불러오기, 해당 글의 상세 페이지로 이동하기

반응형

 

 

 

 

 

contents/board.html

<!DOCTYPE html>
<html
  lang="en"
  xmlns:th="http://www.thymeleaf.org"
  xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"
  xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
  layout:decorate="~{common/layout}"
>
  <section layout:fragment="content">
    <h1>게시판</h1>
    <div class="container">
      <div id="post__list">
        <table class="table">
          <thead>
            <tr>
              <th>id</th>
              <th>제목</th>
              <th>작성자</th>
              <th>날짜</th>
            </tr>
          </thead>
          <tbody>
            <tr th:each="post : ${post}">
              <td th:text="${post.id}"></td>
              <td>
                <a th:href="@{'/board/' + ${post.id}}"> <span th:text="${post.title}"> </span></a>
              </td>
              <td th:text="${post.author.userName}"></td>
              <td th:text="${#temporals.format(post.createdDate, 'yyyy-MM-dd HH:mm')}"></td>
            </tr>
          </tbody>
        </table>
      </div>
      <div>
        <nav aria-label="Page navigation example">
          <ul class="pagination">
            <th:block th:each="page : ${#numbers.sequence(page.startPage, page.totalPage)}">
              <li class="page-item">
                <a class="page-link" th:href="@{'/board?page=' + ${page}}"><span th:text="${page}"></span></a>
              </li>
            </th:block>
          </ul>
        </nav>

        <!-- <th:block th:each="page : ${#numbers.sequence(page.startPage, page.totalPage)}">
          <a th:href="@{'/board?page=' + ${page}}">
            <button th:text="${page}"></button>
          </a>
        </th:block> -->
      </div>

      <!-- <span th:text="${page}"> </span> -->
    </div>

    <div align="right" class="col-md-12 left">
      <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#savePostsModal">글 등록</button>
    </div>

    <div class="modal fade" id="savePostsModal" tabindex="-1" role="dialog" aria-labelledby="savePostsLabel" aria-hidden="true">
      <div class="modal-dialog" role="document">
        <div class="modal-content">
          <div class="modal-header">
            <h5 class="modal-title" id="savePostsLabel">게시글 등록</h5>
            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
              <span aria-hidden="true">&times;</span>
            </button>
          </div>
          <div class="modal-body">
            <form th:action="@{/post/save}" method="post">
              <div class="form-group">
                <label for="title">제목</label>
                <input type="text" class="form-control" name="title" id="title" placeholder="제목을 입력하세요" />
              </div>
              <div class="form-group">
                <label for="content"> 내용 </label>
                <textarea class="form-control" name="content" id="content" placeholder="내용을 입력하세요" style="resize: none; height: 300px"></textarea>
              </div>
            </form>
            <div class="modal-footer">
              <button type="button" class="btn btn-secondary" data-dismiss="modal">취소</button>
              <button type="button" class="btn btn-primary" id="btn-write">등록</button>
            </div>
          </div>
        </div>
      </div>
    </div>
    <script src="/js/lib/jquery.min.js"></script>
    <script>
      const board = {
        init: function () {
          $("#btn-write").on("click", () => {
            this.clickWriteBtn();
          });
        },
        csrf: {
          token: $("meta[name='_csrf']").attr("content"),
          header: $("meta[name='_csrf_header']").attr("content"),
        },
        clickWriteBtn: function () {
          const data = {
            title: $("#title").val(),
            content: $("#content").val(),
          };

          const { token, header } = this.csrf;

          $.ajax({
            type: "POST",
            url: "/post/save",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: JSON.stringify(data),
            beforeSend: function (xhr) {
              xhr.setRequestHeader(header, token);
            },
          })
            .done(function (result) {
              if (result.msg === "save") {
                alert("게시글이 등록되었습니다.");
                location.reload();
              } else {
                alert("게시글 등록에 실패했습니다. 다시 작성해주세요");
                location.reload();
              }
            })
            .fail(function (error) {
              alert("error");
              console.log(error);
            });
        },
      };

      board.init();
    </script>
  </section>
</html>

 

 

 

 

 

WebRestController.java

save 메소드로 Boards 객체를 전달받고 글 등록 구현

package com.cos.web01.controller;

import java.security.Principal;
import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.cos.web01.domain.boards.Boards;
import com.cos.web01.dto.user.UserSaveRequestDto;
import com.cos.web01.service.BoardService;
import com.cos.web01.service.UserSecurityService;

import lombok.AllArgsConstructor;

@RestController
@AllArgsConstructor
public class WebRestController {

	@Autowired
	private UserSecurityService userSecurityService;

	@Autowired
	private BoardService boardService;

	@PostMapping("/users/signup")
	public ResponseEntity<Map<String, Object>> saveUsers(@RequestBody UserSaveRequestDto dto) {

		userSecurityService.accountUser(dto);

		Map<String, Object> map = new HashMap<>();

		map.put("msg", "save");
		return new ResponseEntity<>(map, HttpStatus.OK);
	}

	@GetMapping("/user/userName")
	public ResponseEntity<Map<String, Object>> getUserName(Principal principal) {
		Map<String, Object> map = new HashMap<String, Object>();

		// UserSecurityService에서 laodUserByUsername 메소드에
		// 리턴 값으로 전달한 User 객체에서 설정한 Id 값을 가져옴
		map.put("userName", principal.getName());

		return new ResponseEntity<Map<String, Object>>(map, HttpStatus.OK);
	}

	@PostMapping("/user/changeName")
	public ResponseEntity<Map<String, Object>> changeUserName(@RequestBody Map<String, Object> map,
			Principal principal) {

		String changeName = map.get("changeName").toString();
		String userId = principal.getName();

		userSecurityService.changeUserName(userId, changeName);

		Map<String, Object> responseMap = new HashMap<String, Object>();

		responseMap.put("msg", "success");

		return new ResponseEntity<Map<String, Object>>(responseMap, HttpStatus.OK);

	}

	@DeleteMapping("/user")
	public ResponseEntity<Map<String, Object>> deleteUser(Principal principal) {
		String userId = principal.getName();

		userSecurityService.deleteUser(userId);

		Map<String, Object> responseMap = new HashMap<String, Object>();

		return new ResponseEntity<Map<String, Object>>(responseMap, HttpStatus.OK);
	}

	@PostMapping("/post/save")
	public ResponseEntity<Map<String, Object>> save(@RequestBody Boards boards, Principal principal) {

		String userId = principal.getName();

		System.out.println(boards);

		boardService.savePost(boards, userId);

		Map<String, Object> responseMap = new HashMap<>();

		responseMap.put("msg", "save");

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

}

 

BoardService.java - savePost 메소드, JPA의 save 메소드로 글 등록 실행

postList 메소드에서는 JPA의 findAll 메소드로 글 목록 불러오기, Pageable 객체를 파라미터로 넣어서 페이징 처리

getPost 메소드에서는 findById 메소드로 Boards 객체 불러오기

package com.cos.web01.service;

import com.cos.web01.domain.boards.Boards;
import com.cos.web01.domain.boards.BoardsRepository;
import com.cos.web01.domain.user.UserRepository;
import com.cos.web01.domain.user.Users;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import lombok.AllArgsConstructor;

@Service
@AllArgsConstructor
public class BoardService {

	private BoardsRepository boardsRepository;
	private UserRepository userRepository;

	@Transactional
	public void savePost(Boards boards, String userId) {
		Users users = userRepository.findByUserId(userId).get();

		boards.setAuthor(users);

		boardsRepository.save(boards);
	}

	@Transactional(readOnly = true)
	public Page<Boards> postList(Pageable pageable) {
		return boardsRepository.findAll(pageable);
	}

	@Transactional(readOnly = true)
	public Boards getPost(Long id) {
		Boards boards = boardsRepository.findById(id).get();

		return boards;
	}

}

 

 

WebController.java

board 메소드에서 페이징 관련 작업, 페이징을 그냥 넘기게 되면 페이지의 첫 번째가 1이 아닌 0부터 시작하기 때문에, /boards?page=0이 첫번째 페이지가 된다

view에서는 일반적으로 페이지를 1부터 시작하기때문에, 내부적으로도 이를 맞춰줘야하고, 맞춰주는 다양한 방법들 중에서,

html에서는 그대로 [1][2][3] 이런 식으로 보이게 하고, 내부에서 처리될 때는 [0][1][2] 이런식으로 맞춰주는 방법으로,

Service로 넘어가는 boardSerivce.postList() 메소드의 파라미터에  view로부터 넘어온 페이지의 숫자 값에 -1을 해주는 식으로 하였다

나중에 10페이지 이상의 분량이 쌓일 경우에 대한 페이징 처리 작업이 필요하다

 

boardById 메소드는 글 상세 페이지를 불러오는 기능으로 만든 메소드

package com.cos.web01.controller;

import java.security.Principal;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;

import com.cos.web01.domain.boards.Boards;
import com.cos.web01.domain.user.UserRepository;
import com.cos.web01.domain.user.Users;
import com.cos.web01.service.BoardService;

import lombok.AllArgsConstructor;

@Controller
@AllArgsConstructor
public class WebController {

	private UserRepository userRepository;

	@Autowired
	private BoardService boardService;

	@GetMapping({ "", "/" })
	public String index() {
		return "contents/index";
	}

	@GetMapping("/board")
	public String board(ModelMap model,
			@PageableDefault(size = 3, sort = "id", direction = Sort.Direction.DESC) Pageable pageable) {

		// System.out.println("데이터 - pageable " + pageable);
		// System.out.println("데이터 - 받아온 페이지 넘버 : " + pageable.getPageNumber());

		// System.out.println("데이터 - pageable.withPage : " +
		// pageable.withPage(pageable.getPageNumber() - 1));

		// Page<Boards> boards = boardService.postList(pageable);

		// dto로 넘겨야하는 작업 필요
		Page<Boards> boards = boardService.postList(pageable.withPage(pageable.getPageNumber() - 1));

		int totalPage = boards.getTotalPages();
		int startPage = totalPage - 10;

		if (startPage < 1) {
			startPage = 1;
		}

		Map<String, Object> page = new HashMap<String, Object>();

		page.put("pageNumber", pageable.getPageNumber());
		page.put("startPage", startPage);
		page.put("totalPage", totalPage);

		model.addAttribute("post", boards);
		model.addAttribute("page", page);

		return "contents/board";
	}

	@GetMapping("/board/{id}")
	public String boardById(@PathVariable Long id, Model model) {
		// System.out.println("param id : " + id);

		// dto로 넘겨야하는 작업 필요
		Boards post = boardService.getPost(id);

		model.addAttribute("post", post);

		return "contents/postDetail";
	}

	@GetMapping("/login/error")
	public String error() {
		return "contents/error";
	}

	@PostMapping("/login/fail")
	public String initPost() {
		return "contents/index";
	}

	@GetMapping("/info")
	public String info(Principal principal, Model model) {
		Optional<Users> users = userRepository.findByUserId(principal.getName());
		Users user = users.get();

		model.addAttribute("userName", user.getUserName());

		return "contents/info";
	}

}

 

 

반응형

'스프링 부트 > 웹MVC' 카테고리의 다른 글

업데이트, 더티 체킹  (0) 2021.12.03
스프링 시큐리티 적용  (0) 2021.12.03
thymeleaf 적용  (0) 2021.12.01
User 생성 작업  (0) 2021.12.01
셋업  (0) 2021.12.01