본문 바로가기
Spring Boot

스프링 부트(Spring Boot) - 컨트롤러에서 알러트(Alert) 메시지 처리하기 [Thymeleaf, MariaDB, Gradle, MyBatis]

by 도뎡 2023. 4. 6.
반응형

  • 본 게시판 프로젝트는 단계별(step by step)로 진행되니, 이전 단계를 진행하시는 것을 권장드립니다.
  • DBMS 툴은 DBeaver를 이용하며, DB는 MariaDB를 이용합니다. (MariaDB 설치하기)
  • 화면 처리는 HTML5 기반의 자바 템플릿 엔진인 타임리프(Thymeleaf)를 사용합니다.

이전 글을 마지막으로 게시판에서 가장 기본이 되는 CRUD(등록/조회/수정/삭제) 기능의 구현이 완료되었습니다. 지금부터는 개발의 퀄리티를 향상할 수 있는 몇 가지 기능들을 프로젝트에 적용해 볼 건데요. 첫 번째는 컨트롤러에서 특정 페이지로 리다이렉트 하는 시점에 사용자에게 메시지를 보여주는 기능입니다.

 

갑작스럽지만, 잠시 동기식 처리(synchronous)와 비동기식 처리(Asynchronous)에 대해 설명을 드리겠습니다. 현재 우리가 구현하는 게시판은 클라이언트(Client)와 서버(Server)가 통신하는 구조입니다. 쉽게 말해, 클라이언트(사용자)가 어떠한 요청을 했을 때 서버(컨트롤러)에서 데이터를 처리한 후 다시 클라이언트(사용자)에게 응답을 내려주는 개념입니다.

 

동기/비동기 방식 모두 클라이언트와 서버가 통신하는 구조입니다만, 서버가 사용자의 요청을 처리한 후 응답을 내려주는 방식에 차이가 있습니다.


동기

  • 사용자가 서버로 A라는 요청을 보냈을 때 A에 대한 응답이 내려오기 전까지 무조건 기다려야 함. (다른 요청 불가)
  • 페이지가 새로고침 되거나 다른 페이지로 이동하게 되고, 그에 따라 HTML을 처음부터 다시 로딩(렌더링) 함.

비동기

  • 사용자가 서버로 A라는 요청을 보냈을 때 A에 대한 응답이 내려오기 전에 다른 요청을 보낼 수 있음. (추가 요청 가능)
  • 페이지의 새로고침이나 이동 없이, 서버로부터 필요한 결과(데이터)만 전달받아 작업을 수행함.

우리가 구현한 CRUD 기능은 동기 방식입니다. 예를 들어, 게시글 작성 페이지에서 서버로 게시글 등록을 요청했을 때 데이터 생성이 완료된 후 게시글 리스트 페이지로 이동합니다. 이번 글에서 구현하는 "컨트롤러에서 알러트 메시지 처리하기" 기능은 앞의 예와 같이 서버에서 데이터를 처리한 후 페이지를 리다이렉트 하는 시점에 사용자에게 상황에 맞는 메시지를 전달하기 위한 용도로 사용됩니다.

 

사실, 동기 처리에서만 사용될 기능이기 때문에 추후에는 사용할 일이 없겠지만, 알아두시면 언젠가 써먹을 일이 분명히 있으실 겁니다. 비동기 방식은 REST API 방식의 댓글 기능 구현 과정에서 자세히 설명드리도록 하겠습니다. 

 

1. 메시지 처리용 DTO 클래스 생성하기

package com.study.common.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.web.bind.annotation.RequestMethod;

import java.util.Map;

@Getter
@AllArgsConstructor
public class MessageDto {

    private String message;              // 사용자에게 전달할 메시지
    private String redirectUri;          // 리다이렉트 URI
    private RequestMethod method;        // HTTP 요청 메서드
    private Map<String, Object> data;    // 화면(View)으로 전달할 데이터(파라미터)

}

 

1) @AllArgsConstructor

PostService의 @RequiredArgsConstructor와 마찬가지로 롬복(Lombok) 라이브러리에서 제공해 주는 기능입니다. 해당 어노테이션이 선언된 클래스에는 전체 멤버 변수를 필요로 하는 생성자가 생성됩니다. 이러한 형태로 말이죠.

    public MessageDto(String message, String redirectUri, RequestMethod method, Map<String, Object> data) {
        this.message = message;
        this.redirectUri = redirectUri;
        this.method = method;
        this.data = data;
    }

 

2) message

사용자에게 전달할 메시지입니다.

 

3) redirectUri

리다이렉트 할 URI입니다.

 

4) method

HTTP 요청 메서드입니다. RequestMethod는 spring-web 라이브러리에 포함된 enum(상수 처리용) 클래스입니다.

 

5) data

화면(HTML)으로 전달할 파라미터입니다. 페이지별로 전달할 파라미터의 개수는 랜덤 하기 때문에 여러 데이터를 key-value 형태(구조)로 담을 수 있는 Map을 이용합니다.

 

예를 들어, 페이징 처리가 되어있다고 가정해 보겠습니다. 3페이지에 있는 25번 게시글을 수정했다면, 수정이 완료된 후 다시 3페이지를 유지해야 합니다. 이때, 이전 페이지 정보(page=3)를 data에 담아 리다이렉트 하면 3페이지를 유지할 수 있습니다.

 

 

2. 컨트롤러에 메서드 추가하기

PostController에 다음의 메서드를 추가해 주세요.

    // 사용자에게 메시지를 전달하고, 페이지를 리다이렉트 한다.
    private String showMessageAndRedirect(final MessageDto params, Model model) {
        model.addAttribute("params", params);
        return "common/messageRedirect";
    }

 

3. 화면(HTML) 추가하기

앞의 메서드가 리턴하는 HTML 파일을 추가하고, 소스 코드를 작성해 주세요.

폴더와 HTML이 추가된 디렉터리 구조

 

<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/header :: main-head"></head>
<body>
    <form th:if="${not #maps.isEmpty( params.data )}" id="redirectForm" th:action="${params.redirectUri}" th:method="${params.method}" style="display: none;">
        <input th:each="key, status : ${params.data.keySet()}" type="hidden" th:name="${key}" th:value="${params.data.get(key)}" />
    </form>
    
    <script th:inline="javascript">
    /* <![CDATA[ */
    
        window.onload = () => {
            const message = [[ ${params.message} ]];
            if (message) {
                alert(message);
            }
      
            const form = document.getElementById('redirectForm');
            if (form) {
                form.submit();
                return false;
            }
      
            const redirectUri = [[ ${params.redirectUri} ]];
            location.href = redirectUri;
        }
    
    /* ]]> */
    </script>
</body>
</html>

 

1) 폼(form) 태그

MessageDto에 Map 타입으로 선언된 data를 순환해서 각 데이터의 key-value를 hidden 타입의 input으로 추가합니다. th:if 조건을 통해 data가 비어있지 않은 경우에만 폼 엘리먼트를 그립니다.

 

2) onload( ) 함수

13~16번 라인

우선 13~16번 라인의 로직입니다. 컨트롤러에서 전달받은 message가 있는 경우, 사용자에게 alert 메시지를 보여줍니다.

 

18~25번 라인

다음은 18~25번 라인의 로직입니다. 18번 라인의 if (form) 조건을 통해 HTML 내에 폼 태그(엘리먼트)가 있는지를 우선적으로 체크합니다. 앞에서 Map 타입의 객체인 data가 비어있지 않은 경우에만 폼(form) 태그를 HTML에 그린다는 말씀을 드렸는데요.

 

쉽게 말씀드리자면, MessageDto 타입의 객체인 params의 data가 비어있지 않다면 redirectForm을 컨트롤러로 submit 하고, data가 비어있으면 redirectUri에 해당하는 주소로 이동합니다. 즉, data의 유무에 따라 폼 데이터를 submit 할지, 단순히 주소만 이동할지가 결정됩니다.

 

4. 컨트롤러(Controller)  메서드 수정하기

화면 처리는 모두 완료되었고, 컨트롤러의 게시글 생성/수정/삭제 메서드에서 showMessageAndRedirect( )를 호출해 주기만 하면 됩니다. PostController의 savePost( ), updatePost( ), deletePost( )를 다음과 같이 변경해 주세요.

    // 신규 게시글 생성
    @PostMapping("/post/save.do")
    public String savePost(final PostRequest params, Model model) {
        postService.savePost(params);
        MessageDto message = new MessageDto("게시글 생성이 완료되었습니다.", "/post/list.do", RequestMethod.GET, null);
        return showMessageAndRedirect(message, model);
    }

    
    // 기존 게시글 수정
    @PostMapping("/post/update.do")
    public String updatePost(final PostRequest params, Model model) {
        postService.updatePost(params);
        MessageDto message = new MessageDto("게시글 수정이 완료되었습니다.", "/post/list.do", RequestMethod.GET, null);
        return showMessageAndRedirect(message, model);
    }

    
    // 게시글 삭제
    @PostMapping("/post/delete.do")
    public String deletePost(@RequestParam final Long id, Model model) {
        postService.deletePost(id);
        MessageDto message = new MessageDto("게시글 삭제가 완료되었습니다.", "/post/list.do", RequestMethod.GET, null);
        return showMessageAndRedirect(message, model);
    }

 

5. 등록/수정/삭제 테스트해 보기

첫 번째로 신규 게시글 생성입니다.

(이미지 깨짐 방지를 위해 헤더, 푸터, 좌측 메뉴는 제외시켰습니다.)

신규 게시글 등록 페이지

 

 

"생성" 완료 메시지가 정상적으로 출력되고, 확인을 클릭하면 리스트 페이지로 이동합니다.

게시글 생성 완료 메시지

 

 

 

다음은 기존 게시글 수정입니다.

기존 게시글 등록(수정) 페이지

 

 

마찬가지로 "수정" 완료 메시지가 출력되고, 확인을 클릭하면 리스트 페이지로 이동합니다.

게시글 수정 완료 메시지

 

 

 

마지막으로 게시글 삭제입니다.

게시글 상세 페이지 - 삭제 버튼 클릭

 

"삭제" 처리도 문제없이 잘 작동합니다.

게시글 삭제 완료 메시지

 

마치며

어떠한 요청에 대한 프로세스가 완료되었을 때 사용자에게 상황에 알맞은 메시지를 보여주고, 원하는 페이지로 리다이렉트 하는 기능을 구현해 보았습니다.

 

다음 글에서는 MyBatis XML Mapper의 SQL 쿼리가 실행되었을 때 해당 SQL 쿼리의 로그가 콘솔에 출력되도록 로그백(Logback)을 프로젝트에 적용해 보도록 하겠습니다.

 

오늘도 방문해 주신 여러분께 진심으로 감사드립니다. 좋은 하루 보내세요 :)

 

Board.zip
0.66MB

반응형

댓글