// 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를 사용하겠다는 것을 의미한다.