동기식 : 여러 작업의 동시 처리가 불가능
비동기식 : 처리가 끝나면 '콜백'이라고 부르는 통보 방식으로, 하나의 작업이 끝날 때까지 기다리지 않고도 여러 작업의 동시 처리가 가능
자바의 람다식 : 자바에서 함수를 파라미터로 전달
-파라미터로 전달되는 콜백을 내부에서 호출하는 방식
자바스크립트의 함수 : 객체와 동일한 일급 객체로 파라미터가 되거나 리턴타입이 가능하다.
-함수를 이용해 콜백을 수행할 수 있다.
https://ko.javascript.info/callbacks
-Promise : 비동기 호출을 동기화된 방식으로 작성하는 문법 기능
요구사항
- 댓글 목록
- 댓글 등록 / 조회 / 수정 / 삭제
- 댓글 페이지 이동
댓글 처리가 필요한 화면을 작성하기 위한 Axios
-비동기 호출을 동기화된 방식으로 작성하는 문법 기능인 Promise를 활용해 구현한 라이브러리
-동기화된 방식으로 Ajax 호출 코드 작성
- read.html : 이벤트 처리를 위한 자바스크립트만 존재하는 HTML 파일
- reply.js : Axios를 이용한 비동기 통신를 위한 스크립트 파일
1. read.html에 Axios 라이브러리를 사용하기 위해 CDN / reply.js 추가
<div layout:fragment="content">
<div class="row mt-3">
<div class="col">
<div class="card">
<!--굵음-->
<div class="card-header">
Board Read-header
</div>
<!-- 카드 본문에 <form>태그로 게시물에 입력할 항목 추가-->
<div class="card-body">
<!-- 글번호-->
<div class="input-group mb-3">
<div class="input-group-text">Bno</div>
<input type="text" class="form-control" th:value="${dto.Bno}" readonly>
</div>
<!--제목 -->
<div class="input-group mb-3">
<div class="input-group-text">Title</div>
<input type="text" class="form-control" th:value="${dto.title}" readonly>
</div>
<!--내용 -->
<div class="input-group mb-3">
<div class="input-group-text">Content</div>
<textarea class="form-control col-sm-5" rows="5" readonly>[[${dto.content}]]</textarea>
</div>
<!--작성자-->
<div class="input-group mb-3">
<div class="input-group-text">Writer</div>
<input type="text" class="form-control" th:value="${dto.writer}" readonly>
</div>
<!--등록시간-->
<div class="input-group mb-3">
<div class="input-group-text">regDate</div>
<input type="text" class="form-control" th:value="${#temporals.format(dto.regDate, 'yyyy-MM-dd HH:mm:ss')}" readonly>
</div>
<!--수정시간-->
<div class="input-group mb-3">
<div class="input-group-text">modDate</div>
<input type="text" class="form-control" th:value="${#temporals.format(dto.modDate, 'yyyy-MM-dd HH:mm:ss')}" readonly>
</div>
<!--등록 / 초기화 버튼-->
<div class="my-4">
<div class="float-end" th:with="link = ${pageRequestDTO.getLink()}">
<a th:href="|@{/board/list}?${link}|" class="text-decoration-none">
<button type="button" class="btn btn-primary">List</button>
</a>
<a th:href="|@{/board/modify(bno=${dto.bno})}&${link}|" class="text-decoration-none">
<button type="button" class="btn btn-secondary">Modify</button>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="/js/reply.js"></script>
</div>
2. 댓글 추가 버튼 / 댓글 출력 / 댓글 페이징 처리 화면 구성
간격을 나타내는 mt, md, my
https://getbootstrap.kr/docs/5.0/utilities/spacing/
</div> <!-- end 1row-->
<!-- 2행 : 댓글 추가 버튼 -->
<div class="row mt-3">
<div class="col-md-12">
<div class="my-4 ">
<button class="btn btn-info addReplyBtn"> ADD REPLY</button>
</div>
<ul class="list-group replyList"></ul>
</div> <!-- end col-->
</div> <!-- end 2row-->
<!-- 3행 : 댓글 목록 + 댓글 페이지 출력-->
<div class="row mt-3">
<div class="col">
<ul class="pagination replyPaging">
</ul>
</div> <!-- end col-->
</div> <!-- end 2row-->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="/js/reply.js"></script>
</div> <!--end content fragment -->
<ul> : 정렬되지 않은 리스트에 replyList, replyPaging 전달해 출력
3. Axios를 이용해 replyList, replyPaging 전달
https://yamoo9.github.io/axios/guide/usage.html
-await, async : 비동기 처리를 동기화된 코드로 작성하기 위해 사용
-async : 비동기 처리를 위한 함수로, function 앞에 위치
-await : async 함수 내에서만 동작하는 키워드로, 프라미스가 처리될 때까지 기다렸다가 호출하는 메서드 [비동기 호출]
https://ko.javascript.info/async-await
(1) reply.js에 특정 게시물의 댓글 목록을 전달하는 get1() 메서드 작성
- 메서드 명 : get1()
- 파라미터 : bno
(2) read.html에서 reply.js의 get1() 함수 호출
(1) 콘솔창을 이용해 데이터 확인
async function get1(bno){
const result = await axios.get(`/replies/list/${bno}`)
//(1) 데이터를 콘솔창을 이용해 미리 확인
console.log(result)
}
<script>
//reply.js에서 get1()호출 결과를 반환해 replyList에 저장
const bno=[[${dto.bno}]]
//(1) 데이터를 콘솔창을 이용해 미리 확인
get1(bno)
</script>
(2) 화면에서 결과를 나타내기 위해 비동기 함수 get1()의 리턴 값 반환
async function get1(bno){
const result = await axios.get(`/replies/list/${bno}`)
//(1) 데이터를 콘솔창을 이용해 미리 확인
// console.log(result)
//(2) 화면에서 결과를 확인하기 위한 리턴값
return result.data
}
<script>
//reply.js에서 get1()호출 결과를 반환해 replyList에 저장
const bno=[[${dto.bno}]]
//(1) 데이터를 콘솔창을 이용해 미리 확인
// get1(bno)
//(2) 화면에서 결과를 확인하기 위해
console.log(get1(bno))
</script>
->하지만 Promise가 반환되고, 반면에 실행 결과는 log 이후에 실행?
- 비동기 함수인 get1() 호출 시점에 반환값이 존재하지 않는다.
- 반환할 것이라는 '약속'만 반환한다.
- 비동기 처리 결과를 반환해서 처리하기 위해서는 then()과 catch()를 이용해 함수를 작성 한다
참고
- async/await와 promise.then/catch
문법 제약 때문에 async함수 바깥의 최상위 레벨 코드에선 await를 사용할 수 없습니다.
그렇기 때문에 관행처럼 .then/catch를 추가해 최종 결과나 처리되지 못한 에러를 다룹니다.
// 유효한 사용자를 찾을 때까지 반복해서 username을 물어봄
async function demoGithubUser() {
let user;
while(true) {
let name = prompt("GitHub username을 입력하세요.", "iliakan");
try {
user = await loadJson(`https://api.github.com/users/${name}`);
break; // 에러가 없으므로 반복문을 빠져나옵니다.
} catch(err) {
if (err instanceof HttpError && err.response.status == 404) {
// 얼럿 창이 뜬 이후에 반복문은 계속 돕니다.
alert("일치하는 사용자가 없습니다. 다시 입력해 주세요.");
} else {
// 알 수 없는 에러는 다시 던져집니다.
throw err;
}
}
}
alert(`이름: ${user.name}.`);
return user;
}
https://ko.javascript.info/promise-error-handling
<script>
//reply.js에서 get1()호출 결과를 반환해 replyList에 저장
const bno=[[${dto.bno}]]
//(1) 데이터를 콘솔창을 이용해 미리 확인
// get1(bno)
//(2) 화면에서 결과를 확인하기 위해
console.log(get1(bno))
//(3) then/catch 이용
get1(bno).then(data =>{
console.log(data)
}).catch(e=>{
console.error(e)
})
</script>
(3) 비동기 처리 방식의 결정
- 비동기 함수의 분리
- 비동기 함수 : 비동기 통신만 처리
- 호출한 함수 : then(), catch()로 이용해 처리
- 비동기 함수에서 모두 처리
- 비동기 함수에서 나중에 처리할 내용을 별도의 함수로 빼서 파라미터로 전달
: 비동기 함수를 분리하는 방식을 선택해, Promise 반환시 호출한 함수에서 then()처리하는 방식으로 구현한다.
메서드 명 | getList | addReply | getReply | modifyReply | removeReply |
파라미터 | {bno, page, size,goLast} |
replyObj | replyObj | rno | rno |
호출 URL | get(`/replies/list/${bno}`) | post(`/replies/`) | get(`/replies/ ${rno}`) |
put(`/replies/ ${replyObj.rno}`) |
delete(`/replies/ ${rno}`) |
URL 호출 파라미터 | {params: {page, size} |
replyObj | X | replyObj | X |
호출하는 함수 | printReplies | addEventListener | addEventListener | addEventListener | addEventListener |
1. 댓글 목록을 화면에 출력
- 비동기 처리 함수 : reply.js의 getList()
- 메서드 명 : getList()
- 파라미터 : bno, page, size, goLast
- 리턴 값 : result.data
- get방식으로 파라미터로 page, size로 페이지 관련정보를 전달해 게시물의 댓글 목록을 가져와 result에 저장
- 호출하는 함수 : read.html의 printReplies()
- 메서드 명 : printReplies()
- 파라미터 : page, size, goLast
- 결과 : data => console.log(data)로 출력
- 특징 : then(), catch()를 이용해 에러 핸들링
- 비동기 처리 방식 :
- read.html에서 비동기 처리를 위해 reply.js의 비동기 함수를 호출한다.
- Promise 반환시 호출한 함수에서 then()처리를 수행한다.
- 비동기 함수를 호출하는 printReplies()에서 호출하는 함수
- replyList, replyPaging를 요소로 검색해 DOM 변수 설정
- printLIst()함수와 printPages()함수에서 해당 DOM에 해당하는 문자열 추가
https://ko.javascript.info/searching-elements-dom
(1) reply.js에 비동기 처리 함수 getList() 작성
-파라미터 여러개 : ({~, ~, ~, ~})
async function getList({bno, page, size, goLast}){
const result = await axios.get(`/replies/list/${bno}`, {params: {page,size}})
// 화면에서 결과를 확인하기 위한 리턴값
return result.data
}
(2) read.html에 비동기 처리 함수 호출하는 함수 printReplies() 작성
//reply.js에서 getList()호출 결과를 반환해 댓글 목록 출력, 페이지 목록 출력
const bno=[[${dto.bno}]]
//댓글 목록 비동기 처리 함수 호출
function printReplies(page,size, goLast){
getList({bno, page,size, goLast}).then(
//로그에 댓글 목록 출력
data=> {console.log(data)}
).catch(e=>{
console.error(e)
})
}
//무조건 호출
printReplies(1,10)
</script>
(2) replyList, replyPaging를 요소로 검색해 DOM 변수 설정
//DOM 설정
const replyList = document.querySelector('.replyList');
const replyPaging = document.querySelector('.replyPaging');
(3) 댓글 목록 출력 함수 작성
//댓글 목록 DOM 생성 함수
function printList(dtoList) {
let str='';
if(dtoList && dtoList.length>0){
for(const dto of dtoList){
//문자열로 생성
str+=`<li class="list-group-item d-flex replyItem">
<span class="col-2"> ${dto.rno}</span>
<span class="col-6" data-rno="${dto.rno}"> ${dto.replyText}</span>
<span class="col-2"> ${dto.replyer}</span>
<span class="col-2"> ${dto.regDate}</span>
</li>`
}
}
replyList.innerHTML=str
}
(4) 페이지 목록 출력 함수 작성
//댓글 페이지 목록 DOM 생성 함수
function printPages(data) {
//pagination
let pageStr = '';
//이전 페이지 존재할 경우
if (data.prev){
pageStr+=`<li class="page-item"><a class="page-link"
data-page="${data.start-1}">PREV</a></li>`
}
//현재 페이지부터 마지막 페이지까지
//: 현재 페이지는 active 처리
for(let i=data.start; i<=data.end; i++){
pageStr+=`<li class="page-item ${i==data.page?"active":""}">
<a class="page-link" data-page="${i}">${i}</a></li>`
}
//다음 페이지 존재할 경우
if(data.next){
pageStr+=`<li class="page-item"><a class="page-link"
data-page="${data.end+1}">NEXT</a></li>`
}
replyPaging.innerHTML=pageStr
}
(5) 비동기 함수 호출하는 printReplies()에서 댓글 목록 출력함수와 페이지 목록 출력 함수 호출
//댓글 목록을 반환하는 비동기 처리 함수 호출
function printReplies(page,size, goLast){
getList({bno, page,size, goLast}).then(
//로그에 댓글 목록 출력
//data=> {console.log(data)}
data=> {
printList(data.dtoList)
printPages(data)
}
).catch(e=>{
console.error(e)
})
}
//무조건 호출
printReplies(1,10,true)
(6) JSON 문자열 데이터 조작
JSON 포맷팅과 JSON 변환 시 제외
-@JsonFormat : JSOn 처리시에 포맷팅을 지정하는 어노테이션
-@JsonIgnore : JSON 변환시에 제외하는 어노테이션
-ReplyDTO에서 JSON 문자열 데이터 조작을 위해 어노테이션 지정
변경 전, ReplyDTO
package org.zerock.b01.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ReplyDTO {
private Long rno;
//특정 게시글의 번호를 지정하기 위한 멤버변수
@NotNull
private Long bno;
@NotEmpty
private String replyText;
@NotEmpty
private String replyer;
private LocalDateTime regDate, modDate;
}
변경 후, ReplyDTO
package org.zerock.b01.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ReplyDTO {
private Long rno;
//특정 게시글의 번호를 지정하기 위한 멤버변수
@NotNull
private Long bno;
@NotEmpty
private String replyText;
@NotEmpty
private String replyer;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime regDate;
@JsonIgnore
private LocalDateTime modDate;
}
(7) 댓글 등록시 마지막 페이지로 이동하기 위한 getList() 메서드 변경
-댓글이 추가되는 경우 마지막 페이지에 등록된다.
-확인을 쉽게 하기 위해 댓글 목록 데이터의 total을 이용해 마지막 페이지 호출한다.
-서버를 다시 호출하는 방식으로 호출하는 경우의 수를 최대한 줄여서 부하를 줄이는 방식을 사용하곤 한다.
-페이징 처리를 하기 때문에 최신 댓글을 볼 수 없기 때문에,
현재 게시물 댓글의 마지막 페이지를 이용해 마지막 페이지를 다시 호출한다.
-> 마지막 페이지 호출 : total값과 size값으로 마지막 페이지 계산해 Axios로 호출
- goLast가 true인 경우
- total : 총 페이지 수
- lastPage : 마지막 페이지 수
(총 페이지 수 / 페이지 단위)의 올림수 -> 1개의 댓글이라도 있으면 1페이지가 할당되기 때문에 - 리턴 값 : 마지막 페이지의 댓글을 출력하기 위해 댓글 목록을 리턴하는 getList() 함수에 마지막 페이지를 파라미터로 전달
//댓글 마지막 페이지로 이동
async function getList({bno, page, size, goLast}){
const raw = await axios.get(`/replies/list/${bno}`)
console.log(raw)
const result = await axios.get(`/replies/list/${bno}`, {params: {page, size}})
if(goLast){
const total = result.data.total
const lastPage = parseInt(Math.ceil(total/size))
return getList({bno:bno, page:lastPage, size:size})
}
//댓글 마지막 페이지로 이동
printReplies(1,10, true)
2. 댓글 등록
모달창을 이용해 처리
- 새로운 댓글의 replyText, replyer 입력
- 자바스크립트 객체로 POST 호출
동작 순서
- reply.js에서 addReply() 메서드 호출
- addReply() 메서드에서 JSON 데이터를 전송
- JSON 데이터를 이용해 댓글을 등록
- 댓글 등록 완료시 등록된 댓글이 있는 마지막 페이지로 이동
(1) reply.js에 댓글 등록 메서드 작성
-자바스크립트 객체로 파라미터를 받아서 axios.post() 이용해서 전달
https://yamoo9.github.io/axios/guide/usage.html#post-%EC%9A%94%EC%B2%AD
axios.post() 기본 문법
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
(2) reply.js의 addReply() 작성
//댓글 등록 메서드
async function addReply(replyObj){
const response = await axios.post(`/replies/`, replyObj)
return response.data
}
-> 키와 값으로 이루어진 JSON 데이터를 전송한다.
-> '{'rno':11}'와 같은 형태의 JSON 데이터
(3) read.html에 모달창 추가
https://getbootstrap.kr/docs/5.2/components/modal/
모달창 기본 구조
<div class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal title</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Modal body text goes here.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
<fragment="content"> 마지막에 댓글 등록을 위한 모달창 추가
<!-- 댓글 등록을 위한 모달창-->
<div class="modal registerModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Register Reply</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="input-group mb-3">
<span class="input-group-text">Reply Text</span>
<!-- <p>Modal body text goes here.</p>-->
<input type="text" class="form-control replyText">
</div>
<div class="input-group mb-3">
<span class="input-group-text">Replyer</span>
<!-- <p>Modal body text goes here.</p>-->
<input type="text" class="form-control replyer">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary registerBtn">Register</button>
<button type="button" class="btn btn-online-dark closeRegisterBtn">Close</button>
</div>
</div>
</div>
</div>
-> class 속성값을 이용해 <input> 태그와 버튼들을 구분
(2) read.html의 <script>부분에 사용할 DOM 객체들을 미리 변수로 처리
//변수 설정
//댓글 등록 모달창
const registerModal =new bootstrap.Modal(document.querySelector(".registerModal"))
//input 태그
const replyText = document.querySelector(".replyText")
const replyer = document.querySelector(".replyer")
// 버튼
const registerBtn = document.querySelector(".registerBtn")
const closeRegisterBtn = document.querySelector(".closeRegisterBtn")
(3) 모달창 열고 닫는 이벤트 처리
//모달창 열고 닫는 이벤트 처리
document.querySelector(".addReplyBtn").addEventListener("click", function (e){
registerModal.show()
},false)
document.querySelector(".closeRegisterBtn").addEventListener("click", function (e){
registerModal.hide()
},false)
(4) 댓글 등록하는 모달창 이벤트 처리
//댓글 등록하는 모달창 이벤트 처리
document.querySelector(".registerBtn").addEventListener("click", function (e){
const replyObj = {
bno:bno,
<!-- String은 value로 값을 가져온다-->
replyText:replyText.value,
replyer : replyer.value
}
<!-- addReply에 파라미터 전달-->
addReply(replyObj).then(result => {
alert(result.rno)
registerModal.hide()
// 등록 후에는 변수 초기화
replyText.value=''
replyer.value=''
//댓글 목록 갱신해서 마지막 페이지로 이동
printReplies(1,10,true)
}).catch(e =>{
alert("Exception...")
})
},false)
3. 댓글 조회
-특정 댓글 조회시, 수정 및 삭제를 할 수 있는 모달창 띄우기
(1) reply.js에 댓글 조회를 위한 메서드 작성
//댓글 조회를 위한 메서드
async function getReply(rno){
const response = await axios.get(`/replies/${rno}`)
return response.data
}
(2) read.html 모달창 추가
-새로운 모달창을 추가해 사용
4. 댓글 수정
(1) reply.js에 댓글 수정을 위한 메서드 작성
//댓글 수정을 위한 메서드
async function modifyReply(replyObj){
const response = await axios.put(`/replies/${replyObj.rno}`)
return response.data
}
(2) read.html 조회시 추가한 모달창을 이용해 수정 처리
-댓글 수정 후에 다시 현재 페이지 호출하도록 수정된 댓글을 확인할 수 있도록 한다.
(하지만 수정 과정에서 많은 댓글이 달릴 경우 확인하지 못할 수도 있다.)
5. 댓글 삭제
(1) reply.js에 댓글 삭제를 위한 메서드 작성
//댓글 수정을 위한 메서드
async function modifyReply(replyObj){
const response = await axios.put(`/replies/${replyObj.rno}`,replyObj)
return response.data
}
(2) read.html 조회시 추가한 모달창을 이용해 삭제 처리
6. 댓글 조회/수정/삭제 이벤트 처리
(1) 사용할 변수 초기화
//댓글 조회/수정/삭제 모달창 띄우기
//댓글 조회/수정/삭제 클래스 모달창/속성값 변수 설정
const modifyModal = new bootstrap.Modal(document.querySelector(".modifyModal"))
const replyHeader = document.querySelector(".replyHeader")
const modifyText = document.querySelector(".modifyText")
const modifyBtn = document.querySelector(".modifyBtn")
const removeBtn = document.querySelector(".removeBtn")
const closeModifyBtn = document.querySelector(".closeModifyBtn")
(2) 댓글 클릭시 모달창 띄우기
-댓글 내용을 채워서 띄운다.
//댓글 클릭시 모달창 띄우기
replyList.addEventListener("click", function (e){
e.preventDefault()
e.stopPropagation()
const target=e.target
if(!target || target.tagName!='SPAN'){
return
}
const rno = target.getAttribute("data-rno")
if(!rno){
return
}
getReply(rno).then(reply =>{
//댓글 내용을 모달창에 띄우기
console.log(reply)
//댓글을 구별하기 위한 변수
replyHeader.innerHTML=reply.rno
//String값은 value로 접근
modifyText.value=reply.replyText
//댓글 내용을 채운 모달창 띄우기
modifyModal.show()
}).catch(e=>alert('error'))
},false)
(3) 댓글 수정/닫기/삭제 버튼 이벤트 처리
-댓글 수정 후 처리하는 경우 수정하는 사이 댓글 추가시 확인할 수 없다.
-현재 페이지를 유지하도록 구현한다.
-Modify 버튼 클릭시 수정 처리, Close 버튼 클릭시 모달창 끄기 처리
//수정 버튼 이벤트 처리
modifyBtn.addEventListener("click",function (e){
//입력받은 내용을 필요한 파라미터를 채워서 객체로 전달
//: rno를 replyHeader의 주소로 지정한다.
const replyObj={
bno:bno,
rno:replyHeader.innerHTML,
replyText:modifyText.value
}
//수정한 댓글 출력하고, 댓글을 수정하던 페이지 정보를 이용해 이전 페이지로 이동
modifyReply(replyObj).then(result =>{
alert(result.rno+' 번 댓글이 수정되었습니다.')
replyText.value=''
modifyModal.hide()
printReplies(page,size)
}).catch(e=> {
console.log(e)
})
},false)
//닫기 버튼 클릭시 모달창 이벤트 처리
closeModifyBtn.addEventListener("click", function (e){
modifyModal.hide()
},false)
//삭제 버튼 클릭시 모달창 이벤트 처리
removeBtn.addEventListener("click", function (e){
//rno
removeReply(replyHeader.innerHTML).then(result=>{
alert(result.rno+' 번 댓글이 삭제되었습니다.')
//댓글 삭제 후, 변수 초기화
replyText.value=''
modifyModal.hide()
//1페이지로 이동하기 위한 변수
page=1
//댓글 목록 출력
printReplies(page, size)
})
},false)
7. 댓글 페이지 이동 이벤트 처리
-'data-page' 속성을 이용해 페이지 번호가 지정되어 있다.
-이벤트 처리를 위해 고정된 <ul>을 대상으로 이벤트 리스너 등록
https://ko.javascript.info/dom-attributes-and-properties
https://dololak.tistory.com/364
//댓글 페이지 번호 이벤트 처리
//(1) 사용할 변수 초기화
let page=1
let size=10
//(2) 고정된 ul에 이벤트 리스너 등록
document.querySelector(".replyPaging").addEventListener("click", function (e){
e.preventDefault()
e.stopPropagation()
const target = e.target
if(!target || target.tagName!='A'){
return
}
//(3) data-page 속성에 저장된 페이지 번호를 변수로 설정
const pageNum = target.getAttribute("data-page")
//(4) 페이지 번호를 파라미터에 저장하고, 댓글 목록을 출력하는 printReplies 함수를 해당 파라미터를 전달해 호출
page=pageNum
printReplies(page,size)
},false)