5. 수정 / 생성 뷰 컨트롤러 작성하기
- 보통 블로그 글 수정과 생성은 같은 화면에서 진행됨
- 수정 / 생성에 따라 버튼 다르고, 기존 값의 유무가 다름
→ URL의 설계와 흐름 또한 다름
❶ 이미 생성한 글을 수정할 때
/new-article?id=123 123 id를 가진
사용자 -------------------------> 뷰 컨트롤러 -------------------------> 뷰
( id가 123인 글 수정 ) 엔티티 조회 후 모델에 추가
❷ 새 글을 생성할 때
/new-article
사용자 -------------------------> 뷰 컨트롤러 -------------------------> 뷰
(생성)
• 쿼리 파라미터
: HTTP 요청에서 URL의 끝에 '?'로 시작하는 키 값으로 이루어진 문자열이며, '&'로 구분함
• 글을 생성할 때는 URL에 별도 쿼리 파라미터가 없음
하지만, 수정할 때는 URL에 ?id=123과 같이 수정할 글의 id를 쿼리 파라미터에 추가하여 요청함
→ 쿼리 파라미터가 있는 경우 컨트롤러 메서드는 수정을 해야 하므로,
엔티티를 조회해 기존 글 데이터를 모델에 넣어 화면에 보여줘야 함 ( 물론 새 글일 경우, 화면에 보여줄 필요 없음 )
→ 또한, 파라미터의 id 여부에 따라 [수정] 또는 [생성] 버튼을 보여줘야 함
1) 수정 화면을 보여주기 위한 컨트롤러 메서드를 추가하기
▪︎ BlogViewController.java
- newArticle( ) 메서드 추가
@GetMapping("/new-article")
public String newArticle(@RequestParam(required = false) Long id, Model model) {
if( id == null ) { // 쿼리문자열이 없는 경우 (/new-article) : id가 없으면 생성
// 원래 수정이라면, 조회할 글이 있으므로 그 글의 데이터를
// DTO 객체 ArticleViewResponse에 저장함
model.addAttribute("article", new ArticleViewResponse());
} else {
// * 수정할 글이 있으면, else 블럭 실행
// 쿼리스트링이 있다는 뜻 : '/new-article/?id=100'
// 1) 이 쿼리스트링의 id를 Article 테이블에서 검색
// 2) 검색한 테이블의 내용을 Article 객체 VO 객체에 저장
// 3) VO 객체의 데이터를 수정할 수 있는 DTO객체 ArticleViewResponse에 저장함
// 4) 이 DTO를 웹 브라우저에게 응답 끝
Article article = blogService.findById(id);
model.addAttribute("article", new ArticleViewResponse());
}
return "newArticle"; // template/newArticle.html
}
• 쿼리 파라미터로 넘어온 id값은 newArticle( ) 메서드의 Long 타입 id 인자에 매핑함
• id가 있으면 수정, 없으면 생성
- id가 없는 경우, 기본 생성자를 이용해 빈 ArticleViewResponse 객체를 생성
- id가 있으면, 기존 값을 가져오는 findById() 메서드 호출
2. 수정 / 생성 뷰 만들기
1) 컨트롤러 메서드에서 반환하는 newArticle.html 구현
▪︎ newArticle.html ( resource/templates )
- 수정 / 생성 뷰 만들기
<!DOCTYPE html>
<html xmlns:th="http://thymeleaf.org" >
<head>
<meta charset="UTF-8">
<title>블로그 글</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/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>
<!-- 수정글일 경우, 서버로 수정 요청 시
hidden 값으로 글 번호(id)를 요청값으로 추가 -->
<!-- 아이디 정보 저장 -->
<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>
<!-- 타임리프는 동적기능 : resources/templates/ -->
<!-- 정적기능 : resources/static/
js, css, images, 동영상
/js 경로 -> resources/static/js/
-->
<script src="/js/article.js"></script>
</body>
</html>
• 수정할 때는 id가 필요
- input 엘리멘트의 type을 'hidden'으로 설정하여 엘리멘트를 숨김
• th:value : 글의 id를 저장
• th:if : id가 있을 때 [수정] 버튼, 없을 때 [등록] 버튼이 나타나도록 함
2) 실제 수정, 생성 기능을 위한 API를 구현하기
▪︎ article.js ( static 디렉터리 )에 다음 코드 추가
// * 수정 기능
// ❶ id가 modify-btn인 엘리멘트 조회
const modifyButton = document.getElementById('modify-btn');
if(modifyButton){
// ❷ 클릭 이벤트가 감지되면 수정 API 요청
modifyButton.addEventListener('click', event => {
// ?key1=value1&key2=value2
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({
// JSON : 자바스크립트의 JSON parser
// stringify : 객체(실행코드) -> 문자열 구조로 변환
title: document.getElementById('title').value,
content: document.getElementById('content').value
})
})
.then(() => {
alert('수정이 완료되었습니다.');
location.replace(`/articles/${id}`);
});
});
}
• id가 modify-btn인 엘리멘트를 찾고, 그 엘리멘트에서 클릭 이벤트가 발생하면 id가 title, content인 엘리멘트의 값을 가져와 fetch( ) 메서드를 통해 수정 API로 /api/articles/ PUT 요청을 보냄
• 요청을 보낼 때
- headers : 요청 형식을 지정
- body : HTML에 입력한 데이터를 JSON 형식으로 바꿔서 보냄
• 요청이 완료되면, then( ) 메서드로 마무리 작업을 함
2) article.html 파일을 열어 [수정] 버튼에 id값과 클릭 이벤트를 추가
▪︎ article.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>블로그 글</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/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>
<!-- 블로그 글 id 추가 -->
<input type="hidden" id="article-id" th:value="${article.id}">
<!-- header 영역 -->
<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>
<!-- [삭제] 버튼에 id 추가 -->
<button type="button" id="delete-btn" class="btn btn-secondary btn-sm">삭제</button>
</article>
</div>
</div>
</div>
<!-- article.js 파일 추가 -->
<script src="/js/article.js"></script>
</body>
</html>
3) 실행 테스트하기
- 웹 브라우저에 'localhost/articles/1'에 접속해 [수정]버튼을 눌러보자
- 수정 뷰가 출력되는 것을 확인하고, 제목 및 내용을 수정해보자
- 수정할 제목 및 내용을 입력 후, [수정] 버튼 클릭 시 수정된 글이 출력됨
6. 생성 기능 마무리하기
1) 생성 기능 작성하기
① [ 등록 ] 버튼을 누르면, 입력 칸에 있는 데이터를 가져와 게시글 생성 API에 글 생성 관련 요청을 보내주는 코드를 추가
▪︎ article.js ( /resources/static/js 디렉터리 )
// * 등록 기능
// ❶ id가 create-btn인 엘리멘트 조회
const createButton = document.getElementById('create-btn');
if(createButton){
// ❷ 클릭 이벤트가 감지되면 생성 API 요청
createButton.addEventListener("click", (event) => {
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");
});
});
}
• id가 create-btn인 엘리멘트를 조회
→ 그 엘리멘트에서 클릭 이벤트 발생 시,
id가 title, content인 엘리멘트의 값을 fetch( ) 메서드를 통해 생성 API로 '/api/articles/' POST 요청을 보내줌
② articleList.html에 idrk 'create-btn'인 [생성] 버튼을 추가
▪︎ articleList.html
/* 생략 */
<!-- 컨테이너 영역 : 컨텐츠 -->
<div class="container">
<!-- 글 등록 버튼 추가 -->
<button type="button" id="create-btn"
th:onclick="|location.href='@{ /new-article }'|"
class="btn btn-secondary btn-sm mb-3">글 등록</button>
/* 생략 */
③ 실행 테스트하기
- 웹 브라우저에 'localhost/articles'에 접속한 뒤, [등록] 버튼을 눌러 제대로 기능이 동작하는지 확인
7장 마무리
▪︎ 템플릿 엔진인 타임리프를 사용해서 게시글 리스트 뷰, 상세 뷰, 삭제 기능, 수정 기능, 생성 기능을 추가
▪︎ 템플릿 엔진
- 데이터를 넘겨받아 HTML에 데이터를 넣어 동적인 웹 페이지를 만들어주는 도구
▪︎ @Controller
: 반환값으로 뷰를 찾아 보여주는 애너테이션
'백엔드 > Spring Boot' 카테고리의 다른 글
6. 블로그 기획하고 API 만들기 흐름 요약 (0) | 2023.08.14 |
---|---|
Maven Project를 Gradle Project로 변경하기 (0) | 2023.08.11 |
7. 블로그 화면 구성하기 (4) - 삭제 기능 추가 (0) | 2023.08.10 |
Oracle Database 연결 시 환경설정 및 확인 방법 (0) | 2023.08.09 |
7. 블로그 화면 구성하기 (3) - 블로그 글 구현하기 (0) | 2023.08.08 |