[코드로 배우는 스프링부트 웹 프로젝트] - 프로젝트 구조 만들기(5) - 등록, 조회

Jongwon·2022년 12월 29일
0

이전에 서비스 계층에서 register를 만들어두었기 때문에 컨트롤러에서 구현만 하면 등록은 완료됩니다.

GuestbookController

...생략

@GetMapping("/register")
    public void register() {
        log.info("register get...");
    }
    
    @PostMapping("/register")
    public String registerPost(GuestbookDTO dto, RedirectAttributes redirectAttributes) {
        log.info("dto..." + dto);
        
        Long gno = service.register(dto);
        
        redirectAttributes.addFlashAttribute("msg", gno);
        
        return "redirect:/guestbook/list";
    }

등록 화면은 HTML Form형태이므로 Get과 Post방식의 통신을 이용합니다. Post에서 redirectAttributes에 flashAttribute를 추가하는데, 이는 redirect시에 단 한번만 msg를 전달하기 위해 사용합니다. 뒤에서 Modal(팝업창)을 만들기 위해 사용할 예정입니다.


register.html파일을 생성하고, 아래의 코드를 작성했습니다.

register.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">

<th:block th:replace="~{/layout/basic :: setContent(~{this::content})}">
    <th:block th:fragment="content">
        <h1 class="mt-4">GuestBook Register</h1>

        <form th:action="@{/guestbook/register}" th:method="post">
            <div class="form-group">
                <label>Title</label>
                <input type="text" class="form-control" name="title" placeholder="Enter Title">
            </div>
            <div class="form-group">
                <label>Content</label>
                <textarea class="form-control" rows="5" name="content"></textarea>
            </div>
            <div class="form-group">
                <label>Writer</label>
                <input type="text" class="form-control" name="writer" placeholder="Enter Writer">
            </div>
            <button type="submit" class="btn btn-primary">Submit</button>
        </form>
    </th:block>
</th:block>

업로드중..



또한 list.html에 리스트 아래에 코드와 스크립트를 추가하였습니다.

list.html

<div class="modal" tabindex="-1" role="dialog">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">Modal title</h5>
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                       <span aria-hidden="true">&times;</span>
                 </button>
             </div>
             <div class="modal-body">
                 <p>Modal body text goes here.</p>
              </div>
              <div class="modal-footer">
                   <button type="button" class="btn btn-primary">Save changes</button>
                   <button type="button" class="btn btn-secondary close" data-dismiss="modal">Close</button>
               </div>
            </div>
        </div>
    </div>

    <script th:inline="javascript">
        var msg = [[${msg}]];

        console.log(msg);

        const $modal = $(".modal")

        if(msg) {
      		$('.modal').show();
		}
      
        $(".close").on("click", () => {
            $modal.hide()
        })

        </script>

list에 추가한 코드는 등록 후 list화면으로 되돌아왔을 때(redirect) Modal을 띄우기 위한 코드입니다. 컨트롤러에서 FlashAttribute로 redirect화면에 전달한 msg에는 gno가 담겨있습니다. 하지만, 단순히 list페이지에 접근했을 때는(register후 redirect방식이 아닌 다른 방식) msg값은 null입니다.

if(msg) {
      		$('.modal').show();
		}
      
$(".close").on("click", () => {
        $modal.hide()
})

이를 이용해 script에서 msg값이 null이 아닌 값, 즉 gno값이 담겨있으면 위의 html에서 modal이 활성화되도록 구성하였습니다.

  <div class="modal" tabindex="-1" role="dialog">
    ...

마지막으로 h1태그 내부에 등록 버튼을 만들어 등록창에 접근할 수 있도록 합니다.

<h1 class="mt-4">GuestBook List Page
     <span>
          <a th:href="@{/guestbook/register}">
               <button type="button" class="btn btn-outline-primary">REGISTER</button>
           </a>
      </span>
</h1>
<table class="table table-striped">
  ...

다음으로는 조회 화면을 만들겠습니다. 각 엔티티의 gno값을 하이퍼링크로 설정해두고, url이 /guestbook/read?gno=xxx&page=xxx형태에서 각각을 조회할 수 있도록 합니다.

...
<tbody>
    <tr th:each="dto : ${result.dtoList}">
      //gno 가리키는 th태그 내부 변경
        <th scope="row">
            <a th:href="@{/guestbook/read(gno=${dto.gno}, page=${result.page})}">
                 [[${dto.gno}]]
            </a>
        </th>
...

서비스 계층에 조회 기능을 추가하겠습니다.

GuestbookService

public interface GuestbookService {
    Long register(GuestbookDTO dto);
    PageResultDTO<GuestbookDTO, Guestbook> getList(PageRequestDTO requestDTO);
    //추가
    GuestbookDTO read(Long gno);

GuestbookServiceImpl

@Override
public GuestbookDTO read(Long gno) {
    Optional<Guestbook> result = repository.findById(gno);

    return result.isPresent() ? entityToDto(result.get()) : null;
}

컨트롤러에서도 GetMapping을 통해 dto를 Model에 담아 반환하겠습니다.

@GetMapping("/read")
public void read(long gno, @ModelAttribute("requestDTO") PageRequestDTO requestDTO, Model model) {

     log.info("gno:" + gno);
     GuestbookDTO dto = service.read(gno);

     model.addAttribute("dto", dto);
}

requestDTO에는 다시 목록 페이지로 돌아갈 수 있도록 조회에 오기 직전의 page번호와 size를 저장해두고 있습니다. gno를 통해 서비스 계층에서 DTO를 조회하고, 결과를 model에 담아 반환합니다.

requestDTO는 @ModelAttribute로 선언되었기 때문에 Model 객체안에 자동으로 주입됩니다. 따라서 뷰로 돌아가는 데이터는 {"requestDTO":requestDTO}, {"dto":dto}입니다.

다음으로는 조회 페이지인 read.html을 작성합니다.
read.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<th:block th:replace="~{/layout/basic :: setContent(~{this::content})}">

    <th:block th:fragment="content">

        <h1 class="mt-4">GuestBook Read Page</h1>

      	<div class="form-group">
            <label>Gno</label>
            <input type="text" class="form-control" name="gno" th:value="${dto.gno}" readonly>
        </div>
      
        <div class="form-group">
            <label>Title</label>
            <input type="text" class="form-control" name="title" th:value="${dto.title}" readonly>
        </div>

        <div class="form-group">
            <label>Content</label>
            <textarea class="form-control" name="content" rows="5" readonly>[[${dto.content}]]</textarea>
        </div>

        <div class="form-group">
            <label>Writer</label>
            <input type="text" class="form-control" name="writer" th:value="${dto.writer}" readonly>
        </div>

        <div class="form-group">
            <label>RegDate</label>
            <input type="text" class="form-control" name="regDate" th:value="${#temporals.format(dto.regDate, 'yyyy/MM/dd HH:mm:ss')}" readonly>
        </div>

        <div class="form-group">
            <label>ModDate</label>
            <input type="text" class="form-control" name="modDate" th:value="${#temporals.format(dto.modDate, 'yyyy/MM/dd HH:mm:ss')}" readonly>
        </div>

        <a th:href="@{/guestbook/modify(gno=${dto.gno}, page=${requestDTO.page})}">
            <button type="button" class="btn btn-primary">Modify</button>
        </a>

        <a th:href="@{/guestbook/list(page=${requestDTO.page})}">
            <button type="button" class="btn btn-info">List</button>
        </a>
    </th:block>
</th:block>
profile
Backend Engineer

0개의 댓글