지난 글에 이어서 관리자 페이지에서의 회원 질문 관리에 대한 기능 구현과 설명을 이어가겠습니다.
부족하더라도 이해해주세요. :)
adminMain.html
<!-- 질문관리 섹션 -->
<div class="admin-section">
<h3 class="clickable" onclick="toggleContent(this)">질문 관리</h3>
<div class="content-list" style="display: none;">
<a th:href="@{/admin-fix-qna}">고정 질문</a><br>
<a th:href="@{/admin-customer-qna}">회원 질문</a><br>
</div>
</div>
- 회원 질문 링크 클릭 시 이전 글에서 미리 만들어 둔 Controller 처리로 회원 질문 페이지로 이동.
// 회원질문 페이지
@GetMapping("/admin-customer-qna")
public String customerQuestions(HttpSession session, Model model) {
Member admin = (Member)session.getAttribute("admin");
if(admin == null) {
model.addAttribute("message", "로그인 페이지로 이동");
model.addAttribute("text", "회원관리를 위해 로그인해주세요.");
model.addAttribute("messageType", "info");
return "admin/admin_login";
}
// 회원 질문 목록을 가져오는 로직 구현
List<Qna> customerQna = qnaService.getCustomerQna();
model.addAttribute("customerQna", customerQna);
return "admin/section/customer_questions"; // 뷰 이름
}
- customer_questions.html로 렌더링.
회원 질문 페이지 (customer_question.html)
<div class="all-container">
<h1>회원 질문 관리</h1>
<div class="customer-qnas" th:if="${customerQna.isEmpty()}">
<h3>아직 회원 질문이 없습니다..</h3>
</div>
<div class="customer-qnas" th:unless="${customerQna.isEmpty()}">
<table>
<thead>
<tr>
<th>질문자 ID</th>
<th>제목</th>
<th>내용</th>
<th>질문 날짜</th>
<th>답변 여부</th>
<th></th>
</tr>
</thead>
<tbody>
<tr th:each="qna : ${customerQna}">
<td th:text="${qna.member.id}">질문자 ID</td>
<td th:text="${qna.title}">제목</td>
<td th:text="${qna.content}">내용</td>
<td th:text="${#dates.format(qna.qna_date, 'yyyy-MM-dd HH:mm')}">질문 날짜</td>
<td>
<span th:if="${qna.answer_status == 0}">대기</span>
<span th:if="${qna.answer_status == 1}">완료</span>
</td>
<td>
<button th:if="${qna.answer_status == 0}" class="answer-btn" th:data-seq="${qna.qna_seq}"
onclick="customer_qna_answer(this)">
답변 작성
</button>
<button th:if="${qna.answer_status == 1}" class="disabled-btn" disabled>
답변 완료
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
- 조건부 렌더링 : th:if="${qna.answer_status == 0}" > 해당 질문의 답변이 없는 경우에만 버튼이 표시 , th:if="${qna.answer_status == 1}" > 마찬가지로 답변이 있는 경우에만 버튼 표시.
- 답변 작성 버튼 클릭 시 JavaScript 함수 (customer_qna_answer)
// 회원질문 답변
function customer_qna_answer(button) {
const qna_seq = button.getAttribute('data-seq');
swal.fire({
title: '답변 작성',
input: 'textarea',
inputLabel: '답변을 입력하세요.',
inputPlaceholder: '답변은 여기에 입력해주세요!',
showCancelButton: true,
confirmButtonText: '제출',
cancelButtonText: '취소',
preConfirm: (answer) => {
if(!answer) {
swal.showValidationMessage('답변을 작성하지 않았어요..~!');
}
return answer;
}
}).then((result) => {
if(result.isConfirmed) {
$.ajax({
url: '/customer-qna-answer',
type: 'POST',
data: {
qna_seq: qna_seq,
answer: result.value
},
success: function(response) {
swal.fire({
title: '답변 성공!',
text: '답변을 성공적으로 제출하였습니다.',
icon: 'success',
confirmButtonText: '확인'
});
location.reload();
},
error: function(xhr) {
swal.fire('오류', '답변 제출 실패.', 'error');
}
});
}
});
}
- input: 'textarea', : 답변을 입력하는 텍스트 영역을 SweetAlert2 창으로 제공.
- if(!answer) { swal.showValidationMessage('답변을 작성하지 않았어요..~!'); } : 입력값이 없을 경우 메시지 표시.
- $.ajax({ url: '/customer-qna-answer',type: 'POST',data: { qna_seq: qna_seq, answer: result.value }, : Ajax 요청 > url을 ' '로 POST 요청, data는 답변할 질문의 고유 번호(qna_seq), 작성된 답변 (answer)를 전달.
- 성공 시 swal.fire({...}); 성공 메시지 표시, location.reload(); 페이지 새로고침.
- 실패 시 swal.fire('오류', '답변 제출 실패.', 'error'); 오류 메시지.
Controller 처리
// 회원질문 답변처리
@PostMapping("/customer-qna-answer")
@ResponseBody
public ResponseEntity<String> submitCustomerQnaAnswer(HttpSession session, Model model,
@RequestParam("qna_seq") int qna_seq, @RequestParam("answer") String answer) {
Member admin = (Member)session.getAttribute("admin");
if(admin == null) {
model.addAttribute("message", "로그인 페이지로 이동");
model.addAttribute("text", "회원관리를 위해 로그인해주세요.");
model.addAttribute("messageType", "info");
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("로그인 필요");
}
Qna qna = qnaService.findQnaBySeq(qna_seq);
if(qna != null) {
qna.setAnswer(answer);
qna.setAnswer_date(new Date());
qna.setAnswer_status(1);
qnaService.updateQna(qna);
return ResponseEntity.ok("답변이 성공적으로 제출되었습니다.");
} else {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("질문을 찾을 수 없습니다.");
}
}
- @PostMapping("/customer-qna-answer") : url 경로에 대한 POST 요청 처리, Ajax 요청에 데이터를 받는다.
- @ResponseBody : 응답 형태를 JSON 형식로 반환.
- @RequestParam("qna_seq") int qna_seq, @RequestParam("answer") String answer : HTTP 요청에 포함된 파라미터 값을 받음.
- Qna qna = qnaService.findQnaBySeq(qna_seq); : 전달받은 질문 고유 번호(qna_seq)로 질문을 찾아 Qna 객체로 저장.
- if(qna != null) { qna.setAnswer(answer); > 파라미터로 전달받은 답변을 해당 질문의 답변으로 설정. qna.setAnswer_date(new Date()); > 현재 날짜와 시간을 답변 작성 날짜로 설정. qna.setAnswer_status(1); > 답변 상태를 1(답변 환료)로 변경. qnaService.updateQna(qna); > DB에 저장된 질문 객체를 업데이트.
- 반환값 : return ResponseEntity.ok("답변이 성공적으로 제출되었습니다."); > 200상태 코드와 메시지. return ResponseEntity.status(HttpStatus.NOT_FOUND).body("질문을 찾을 수 없습니다."); > 404 상태 코드와 메시지.
++ 테스트
'SpringBoot 프로젝트' 카테고리의 다른 글
Spring Boot - 국립 도서관 Open API를 활용한 도서 검색 구현 (0) | 2024.12.10 |
---|---|
Spring Boot - 국립 도서관 Open API를 활용한 사서 추천 도서 목록 구현 (0) | 2024.12.10 |
Spring Boot - 관리자 페이지 (4) (0) | 2024.12.07 |
Spring Boot - 관리자 페이지 (3) (0) | 2024.11.30 |
Spring Boot - 관리자 페이지 (2) (0) | 2024.11.25 |