7.5 블로그 글 수정/생성 기능 구현 - 1

SummerToday·2024년 2월 17일
1
post-thumbnail
post-custom-banner

수정/생성 뷰 컨트롤러 구현

// controller - BlogViewController.java

@RequiredArgsConstructor
@Controller
public class BlogViewController {

      ~ 생략 ~
    
    @GetMapping("/new-article")
    public String newArticle(@RequestParam(required = false) Long id, Model model) {
        if (id == null) { // id 값이 없을 시 빈 DTO를 모델에 저장. (생성)
            model.addAttribute("article", new ArticleViewResponse());
        } else { // id 값이 있을 시 기존 id의 글 정보를 DTO에 담아 모델에 저징. (수정)
            Article article = blogService.findById(id);
            model.addAttribute("article", new ArticleViewResponse(article));
        }

        return "newArticle";
    }
}
  • Query Parameter (Query String)
    HTTP 요청에서 URL의 끝에 '?'로 시작하는 키 값으로 이루어진 문자열이며 '&'로 구분한다.
    예를들어, ?id=123일 때에는 키는 id, 값은 123이 되는 것이다.

    단, URL에 정보가 노출되므로 비밀번호와 같은 민감한 정보는 포함되면 안된다.

    • Path Variable vs Query Parameter

      • Path Variable ( ex. /users/{userId} )
        주로 리소스 식별이나 검색에 사용된다. 예를 들어, 특정 사용자의 정보를 검색하거나 특정 제품 의 세부 정보를 얻을 때 사용될 수 있다.

      • Query Parameter ( ex. /search?q=query&sort=asc )
        주로 필터링, 정렬, 페이지네이션 등과 같이 리소스의 특정 조건을 지정할 때 사용되고,
        검색 엔진의 쿼리나 필터링, 사용자가 입력한 데이터를 전달할 때 사용된다.
  • @RequestParam
    컨트롤러의 메서드 매개변수에 사용되는 어노테이션이다. 해당 어노테이션을 사용하여 HTTP 요청에서 쿼리 매개변수를 추출하여 메서드의 매개변수로 주입할 수 있게된다.

    • @RequestParam은 다음과 같은 매개변수를 가질 수 있다.

      • value: 쿼리 매개변수의 이름을 지정한다. 기본값은 메서드의 매개변수 이름과 같다.

      • required: 해당 매개변수가 필수인지 여부를 지정한다. 기본값은 true이다.

      • defaultValue: 쿼리 매개변수가 없을 경우 사용할 기본값을 지정한다.

수정/생성 뷰 만들기

// resource - templates - 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>
        <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>
  • 수정할 때는 id가 필요하므로 input 엘리먼트의 type을 hidden으로 설정해 엘리먼트를 숨긴다.

  • th:if를 이용해 id가 있을 시 수정 버튼, 없을 시 등록 버튼이 나타나게 한다.


수정 기능 구현

// resources - static - js - article.js 

  ~ 생략 ~  

// 수정 기능
const modifyButton = document.getElementById('modify-btn');

if (modifyButton) {
    modifyButton.addEventListener('click', event => {
        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}`);
            });
    });
}
  • URLSearchParams(URL)
    URL 쿼리 문자열을 다루기 위한 표준 JavaScript 객체이다. 해당 객체를 사용하면 쿼리 문자열을 파싱하고, 쿼리 매개변수의 값을 읽거나 추가, 수정, 삭제할 수 있다.

  • location.search
    현재 페이지의 URL에서 쿼리 문자열을 나타내는 프로퍼티이다. 반환값은 문자열 형태로 반환되며, 쿼리 매개변수와 그 값을 포함한다. 여러 개의 쿼리 매개변수가 있을 경우에는 &로 구분된다.

    예를 들어, URL이 http://example.com/page?id=123&name=John와 같이 구성되어 있다면, location.search는 ?id=123&name=John과 같이 쿼리 문자열을 반환한다.

  • Content-Type
    HTTP 요청이나 응답의 헤더 중 하나로, 해당 데이터의 본문 형식을 서버 또는 클라이언트에 알린다.

    • 주요 Content-Type

      • application/json
        JSON 형식의 데이터를 나타낸다.

      • application/x-www-form-urlencoded
        웹 폼 데이터를 나타낸다. 일반적으로 HTML 폼에서 사용되는 기본값이다.

      • multipart/form-data
        파일 업로드와 함께 웹 폼 데이터를 나타낸다.

      • text/plain
        일반 텍스트를 나타낸다.
  • JSON.stringify()
    JavaScript 객체나 값들을 JSON 문자열로 변환하는 메서드이다. 객체나 값의 속성들을 문자열로 변환하고, 그 결과를 JSON 형식의 문자열로 반환한다.
    주로 JavaScript 객체를 서버로 전송하기 전에 JSON 형식으로 변환하여 사용하거나, JavaScript 객체를 로컬 스토리지에 저장할 때 사용된다.

    • 예시
    // javascript 객체
    
    const obj = {
     name: "John",
     age: 30,
     city: "New York"
    };
    const jsonString = JSON.stringify(obj);
     console.log(jsonString);

    다음과 같이 변환이 된다.

    {"name":"John","age":30,"city":"New York"}
    • javaScript 객체를 JSON 문자열로 변환할 때, 다음과 같은 방식으로 변환된다.

      • 객체의 속성들은 키와 값 쌍으로 변환된다.

      • 키와 값 사이에는 쌍따옴표(")가 사용된다.

      • 각 쌍 사이에는 콤마(,)가 사용된다.

      • 객체의 시작과 끝에는 중괄호({})가 사용된다.

      • 값이 문자열일 경우에는 문자열 그대로 출력되고, 숫자, 불리언, 배열, 또 다른 객체 등의 경우 에는 해당 값들의 JSON 표현이 사용된다.

블로그 글 뷰 수정

// resources - tempaltes - article.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">
          <h1 class="fw-bolder mb-1" th:text="${article.title}"></h1>
          <div class="text-muted fst-italic mb-2" th:text="|Posted on ${#temporals.format(article.createdAt, 'yyyy-MM-dd HH:mm')}|"></div>
        </header>
        <section class="mb-5">
          <p class="fs-5 mb-4" th:text="${article.content}"></p>
        </section>
        
        <!-- 해당 부분 수정 -->
        <button type="button" id="modify-btn"
                th:onclick="|location.href='@{/new-article?id={articleId}(articleId=${article.id})}'|"
                class="btn btn-primary btn-sm">수정</button>
        <!-- 해당 부분 수정 -->     
        
        <button type="button" id="delete-btn"
                class="btn btn-secondary btn-sm">삭제</button>
      </article>
    </div>
  </div>
</div>

<script src="/js/article.js"></script>
</body>
  • 수정 버튼에 id 값과 클릭 이벤트를 추가한다.

  • @{/new-article?id={articleId}(articleId=${article.id})}
    Thymeleaf에서 제공하는 URL 표현식으로, 해당 페이지의 URL을 동적으로 생성한다. {articleId} 부분은 URL에 포함될 파라미터의 이름을 나타내며, ${article.id} 부분은 해당 파라미터의 값으로 article.id를 사용하겠다는 것을 의미한다.




해당 글은 다음 도서의 내용을 정리하고 참고한 글임을 밝힙니다.
신선영, ⌜스프링 부트 3 벡엔드 개발자 되기 - 자바 편⌟, 골든래빗(주), 2023, 384쪽
profile
IT, 개발 관련 정보들을 기록하는 장소입니다.
post-custom-banner

0개의 댓글