TIL

230321 [Javascript] (댓글 수정 기능)

하차모 2023. 3. 21. 17:48

1. 댓글 수정 기능

 

1-1. 댓글 목록 조회

<div>
    <div th:each="reply : ${reply}">
        <div>[[${reply.replyWriter}]]</div>
        <div>[[${reply.regDate}]]</div>
        <div>[[${reply.replyContent}]]</div>
        <div th:if="${session.loginInfo != null and session.loginInfo.memId == reply.replyWriter}">
            <input type="button" value="수정"> <!-- 구현할 기능 -->
            <input type="button" value="삭제" th:onclick="|location.href='@{/reply/deleteReply(replyNum=${reply.replyNum},boardNum=${board.boardNum})}'|">
        </div>
    </div>
</div>

controller에서 model 객체를 사용해 넘긴 댓글 목록을 받아 for-each문으로 댓글 목록 조회 기능을 구현했다.

각 댓글 하단에 수정, 삭제 버튼을 추가했고, 해당 버튼은 session에 로그인 정보가 저장되어 있고 로그인 아이디 정보가 댓글 작성자가 같을 때만 보여지도록 했다.

 

수정 버튼을 눌렀을 때 Javascript를 사용해 댓글 내용 div를 수정이 가능한 input 태그로 변경하려고 한다.

 

1-2. 댓글 수정 구현

<th:block layout:fragment="content_js">
	<script type="text/javascript" src="/js/detail.js"></script>
</th:block>

자바스크립트 파일을 생성해 <script> 태그로 경로를 지정해줬다.

<input type="button" value="수정" th:onclick="setInput()">

버튼을 클릭하면 js 파일의 setInput 함수가 실행될 것이다.

 

1-2-1. 댓글 태그 내용 삭제

<input type="button" value="수정" th:onclick="setInput(this)">

댓글 내용이 담긴 태그를 찾아가기 위해서는 현재 태그를 나타내는 this를 함수의 매개변수로 지정해야 한다. id나 class로 태그를 지정하면 for문으로 인해 중복될 수 있기 때문에 현재 태그에서부터 찾아가는 방법을 써야 한다.

//댓글 수정 버튼 클릭 시 실행
function setInput(selectedTag) {
    // 댓글 내용 태그 찾아가기
    // 수정 버튼 태그의 부모 태그의 바로 이전 형제 태그
    const contentDiv = selectedTag.parentElement.previousElementSibling;

    // 댓글 원 내용 저장
    const content = contentDiv.textContent;

    // 선택한 태그 안의 내용 비우기
    contentDiv.innerHTML = '';
}

this로 보낸 태그를 selectedTag로 받아 댓글 내용이 담긴 태그를 찾아간다. 댓글의 원래 내용을 input 태그에 담아줘야 하기 때문에 변수에 따로 저장한 후 내용을 비워준다.

 

1-2-2. 선택한 태그 안에 input 태그 넣기

//댓글 수정 버튼 클릭 시 실행
function setInput(selectedTag, replyNum, boardNum) {
// 댓글 내용 태그 찾아가기
    const contentDiv = selectedTag.parentElement.previousElementSibling;

    // 댓글 원 내용 저장
    const content = contentDiv.textContent;

    // 선택한 태그 안의 내용 비우기
    contentDiv.innerHTML = '';

    // 선택한 태그 안에 input 태그 넣기
    const str = `<input type="text" value="${content}">`;

    // 선택한 태그의 첫 번째 자식 태그로 삽입
    contentDiv.insertAdjacentHTML('afterbegin', str);

    //수정 버튼의 글자 변경
    selectedTag.value = '확인';
}

value 값에 저장했던 내용을 담은 input 태그를 만들어 str 변수에 저장한다. 이때 백틱(``)으로 감싸 ${}안의 문자열이 이어지도록 한다. insertAdjacentHTML('추가할 위치', '추가할 태그')는 지정한 위치에 태그를 추가하는 기능이다. 선택한 태그의 첫 번째 자식 태그로 삽입한 후, 버튼의 글자를 '확인'으로 변경한다.

 

이때, 버튼의 글자가 '수정'일 경우와 '확인'일 경우 버튼의 기능이 달라야 하기 때문에 if문으로 구분하여 준다.

 

1-2-3. 댓글 수정 쿼리

<!-- 댓글 수정 -->
<update id="updateReply">
    UPDATE SPRING_REPLY
    SET
        REPLY_CONTENT = #{replyContent}
    WHERE REPLY_NUM = #{replyNum}
</update>

'확인' 버튼을 누르면 필요한 값을 가지고 controller로 가야 한다. 쿼리를 보면 필요한 값이 여러 개이기 때문에  input 태그를 form 태그로 감싸고 button 타입이 아닌 submit 타입으로 변경해준다.

 

1-2-4. form 태그 추가 및 submit 타입 변경

//댓글 수정 버튼 클릭 시 실행
function setInput(selectedTag, replyNum, boardNum) {
	if(selectedTag.value == '수정') {
		// 댓글 내용 태그 찾아가기
		const contentDiv = selectedTag.parentElement.previousElementSibling;
		
		// 댓글 원 내용 저장
		const content = contentDiv.textContent;
		
		// 선택한 태그 안의 내용 비우기
		contentDiv.innerHTML = '';
		
		// 선택한 태그 안에 input 태그 넣기
		let str = ``;
		str += '<form id="updateReplyForm" action="/reply/updateReply" method="post">';
		str += `<input type="hidden" value="${boardNum}" name="boardNum">`;
		str += `<input type="hidden" value="${replyNum}" name="replyNum">`;
		str += `<input type="text" value="${content}" name="replyContent">`;
		str += '</form>';
		
		// 선택한 태그의 첫 번째 자식 태그로 삽입
		contentDiv.insertAdjacentHTML('afterbegin', str);
		
		//수정 버튼의 글자 변경
		selectedTag.value = '확인';
	}
	else {
		// form 태그 submit
		document.querySelector('#updateReplyForm').submit();
	}
}

 

수정 완료 후, 다시 해당 글 상세 페이지로 오기 위해서 필요한 boardNum을 hidden 속성의 input 태그로 보내준다.

선택한 태그의 value 값이 '수정'이 아닐 때는 form 태그를 submit 하는 기능을 하게 한다.

 

 

+) ReplyService

//댓글 수정
void updateReply(ReplyVO replyVO);

+) ReplyServiceImpl

// 댓글 수정
@Override
public void updateReply(ReplyVO replyVO) {
    sqlSession.update("replyMapper.updateReply", replyVO);	
}

+) ReplyController

// 댓글 수정
@PostMapping("/updateReply")
public String updateReply(ReplyVO replyVO) {
    replyService.updateReply(replyVO);
    return "redirect:/board/detail?boardNum=" + replyVO.getBoardNum();
}

post method의 form 태그에서 보냈기 때문에 PostMapping으로 받아 준다.