블로그 화면 구성하기 (2)

·2023년 12월 3일
0

Spring Boot

목록 보기
12/21
post-thumbnail

이전 포스팅에서 이어집니다!
🍀 블로그 화면 구성하기 (1)


💙 삭제 기능 구현

1. 삭제 기능 코드 작성

article.js

// 삭제 기능
const deleteButton = document.getElementById('delete-btn');

if(deleteButton){
    deleteButton.addEventListener('click', e => {
        let id = document.getElementById('article-id').value;
        fetch(`/api/articles/${id}`, {
            method: 'DELETE'
        })
        .then(() => {
            alert('삭제가 완료되었습니다.');
            location.replace('/articles');
        });
    });
}

article.html

(생략)
            <article>
                <input type="hidden" id="article-id" th:value="${article.id}">
(생략)
                <!-- [삭제] 버튼에 id 추가 -->
                <button type="button" class="btn btn-secondary btn-sm" id="delete-btn">삭제</button>
            </article>
        </div>
    </div>
</div>

<script src="/js/article.js"></script>
</body>
</html>

2. 실행 테스트

삭제 버튼을 클릭했을 때 기능이 제대로 작동하는지 테스트해 본다! (과정 생략)
삭제하고 목록으로 돌아왔을 때 방금 삭제한 글이 목록에서 사라져야 성공이다.


💜 수정/생성 기능 구현

1. 수정/생성 뷰 컨트롤러 작성

BlogViewController.java

(생략)
    @GetMapping("/new-article")
    // id 키를 가진 쿼리 파라미터의 값을 id 변수에 매핑 (id는 없을 수도 있음)
    public String newArticle(@RequestParam(required = false) Long id, Model model){
        if(id == null){ // id가 없으면 생성
            model.addAttribute("article", new ArticleViewResponse());
        } else{ // id가 없으면 수정
            Article article = blogService.findById(id);
            model.addAttribute("article", new ArticleViewResponse(article));
        }

        return "newArticle";
    }

2. 수정/생성 뷰 생성

newArticle.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>블로그 글</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"
</head>
<body>
    <div class="p-5 mb-5 text-center</> bg-light">
        <h1 class="mb-3">My Blog</h1>
        <h4 class="mb-3">블로그에 오신 것을 환영합니다.</h4>
    </div>

    <div class="container mt-5">
        <div class="row">
            <div class="col-lg-8">
                <article>
                    <!-- 아이디 정보 저장 -->
                    <input type="hidden" id="article-id" th:value="${article.id}">

                    <header class="mb-4">
                        <input type="text" class="form-control" placeholder="제목" id="title" th:value="${article.title}">
                    </header>
                    <section class="mb-5">
                        <textarea class="form-control h-25" rows="10" placeholder="내용" id="content" th:text="${article.content}"></textarea>
                    </section>

                    <!-- id가 있을 때는 [수정] 버튼을, 없을 때는 [등록] 버튼이 보이게 함 -->
                    <button th:if="${article.id} != null" type="button" id="modify-btn" class="btn btn-primary btn-sm">수정</button>
                    <button th:if="${article.id} == null" type="button" id="create-btn" class="btn btn-primary btn-sm">등록</button>
                </article>
            </div>
        </div>
    </div>

<script src="/js/article.js"></script>
</body>
</html>

article.js

(생략)
// 수정 기능
// 1. id가 modify-btn인 엘리먼트 조회
const modifyButton = document.getElementById('modify-btn');

if(modifyButton){
    // 클릭 이벤트가 감지되면 수정 API 요청
    modifyButton.addEventListener('click', e=>{
        let params = new URLSearchParams(location.search);
        let id = params.get('id');

        fetch(`/api/articles/${id}`, {
            method: 'PUT',
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                title: document.getElementById('title').value,
                content: document.getElementById('content').value
            })
        })

        .then(()=>{
            alert('수정이 완료되었습니다.');
            location.replace(`/articles/${id}`);
        })
    })
}

article.html

(생략)
                <button type="button" class="btn btn-primary btn-sm" id="modify-btn" th:onclick="|location.href='@{/new-article?id={articleId}(articleId=${article.id})}'|">수정</button>
(생략)

3. 실행 테스트

테스트 완료!


🤍 생성 기능 마무리

1. 생성 기능 작성

article.js

(생략)
// 생성 기능
const createButton = document.getElementById('create-btn');

if(createButton){
    createButton.addEventListener('click', e=>{
        fetch("/api/articles", {
            method: "POST",
            headers: {
                "Content-Type" : "application/json"
            },
            body: JSON.stringify({
                title: document.getElementById("title").value,
                content: document.getElementById("content").value
            }),
        })

        .then(()=>{
            alert("등록이 완료되었습니다.");
            location.replace("/articles");
        })
    })
}

articleList.html

(생략)
        <button type="button" id="create-btn" th:onclick="|location.href='@{/new-article}'|" class="btn btn-secondary btn-sm mb-3">글 등록</button>
(생략)

2. 실행 테스트

실행 테스트까지 완료한다!


💫 마치며

이렇게 타임리프를 사용해 CRUD 뷰와 기능을 구현해 보았다. 전반적인 구조나 순서는 똑같으나 뷰 구현 시 th:를 붙이는 것이 아직은 낯설지만.. 익숙해질 때까지 계속 연습해 보자.

다음 포스팅에서는 스프링 시큐리티로 로그인/로그아웃, 회원 가입을 구현해 볼 것이다. 😀

profile
풀스택 개발자 기록집 📁

0개의 댓글