<p>Q&A ( <th:block th:text="${inquiryPageResponseDTO.total}"></th:block> )</p>
<div>
<p>
답변은 <span>마이롯데>나의문의내역</span>에서 확인하실 수 있습니다.<br>
처음 확인 시, 필터가 적용된 상태로 <span>비밀글 제외한 Q&A</span>가 노출됩니다.
</p>
<button type="button">문의하기</button>
</div>
<th:block th:each="inquiry : ${inquiryPageResponseDTO.dtoList}">
<div class="qna_content">
<p th:text="${inquiry.cateV1} + ' > ' + ${inquiry.cateV2}"></p>
<p th:text="${inquiry.content}"></p>
<div class="answer" th:if="${inquiry.answer != null}">
<p th:text="${inquiry.answer}"></p>
<p>롯데쇼핑(주)본점<span>판매자</span></p>
</div>
<div class="no-answer" th:if="${inquiry.answer == null}">
<p>답변 대기 중입니다.</p>
</div>
<div>
<a href="#">답변달기</a>
<p th:text="${#temporals.format(inquiry.wdate, 'yyyy.MM.dd')}"></p>
</div>
</div>
</th:block>
<div id="qnaModal" class="modal">
<div class="modal-content">
<span class="close-button">×</span>
<h3>문의 내용</h3>
<p id="modalInquiryContent"></p>
<h3>답변</h3>
<div id="modalAnswerContent"></div>
</div>
</div>
<div class="qna_page">
<a href="#" th:if="${inquiryPageResponseDTO.prev}" th:attr="data-page=${inquiryPageResponseDTO.start - 1}"><</a>
<th:block th:each="num : ${#numbers.sequence(inquiryPageResponseDTO.start, inquiryPageResponseDTO.end)}">
<a href="#" th:attr="data-page=${num}" th:class="${num == inquiryPageResponseDTO.pg ? 'current' : 'num'}" th:text="${num}">1</a>
</th:block>
<a href="#" th:if="${inquiryPageResponseDTO.next}" th:attr="data-page=${inquiryPageResponseDTO.end + 1}">></a>
</div>
기술 설명
<p>Q&A ( <th:block th:text="${inquiryPageResponseDTO.total}"></th:block> )</p>:
inquiryPageResponseDTO.total값을 동적으로 바인딩하여 전체 문의 개수 표시.<th:block>안에서th:text를 사용해 숫자만 텍스트로 출력.
th:each="inquiry : ${inquiryPageResponseDTO.dtoList}":
dtoList(문의 리스트)를 순회하며 각 문의 블록(.qna_content) 생성.카테고리 표시:
<p th:text="${inquiry.cateV1} + ' > ' + ${inquiry.cateV2}"></p>
- 1차 카테고리(
cateV1)와 2차 카테고리(cateV2)를 “ > ” 구분자로 결합해 출력.문의 내용 출력:
<p th:text="${inquiry.content}"></p>
inquiry.content를 동적으로 바인딩하여 문의 본문 표시.답변 유무에 따른 분기 처리:
답변이 있을 때 (
th:if="${inquiry.answer != null}"):<div class="answer"> <p th:text="${inquiry.answer}"></p> <p>롯데쇼핑(주)본점<span>판매자</span></p> </div>
inquiry.answer값을 출력하고, 판매자 명칭(“롯데쇼핑(주)본점”)과 “판매자” 레이블 함께 표기.답변이 없을 때 (
th:if="${inquiry.answer == null}"):<div class="no-answer"> <p>답변 대기 중입니다.</p> </div>
- 답변이 없는 경우 “답변 대기 중입니다.” 텍스트 표시.
답변달기 링크 및 작성일자:
<div> <a href="#">답변달기</a> <p th:text="${#temporals.format(inquiry.wdate, 'yyyy.MM.dd')}"></p> </div>
- “답변달기” 링크(
href="#") 클릭 시 답변 작성 모달 또는 페이지 오픈 예상.#temporals.format(inquiry.wdate, 'yyyy.MM.dd')를 사용해inquiry.wdate(작성일시)를 “YYYY.MM.DD” 형식으로 포맷팅해 표시.모달 구조 (
#qnaModal):<div id="qnaModal" class="modal"> <div class="modal-content"> <span class="close-button">×</span> <h3>문의 내용</h3> <p id="modalInquiryContent"></p> <h3>답변</h3> <div id="modalAnswerContent"></div> </div> </div>
id="qnaModal",class="modal": 모달 전체 컨테이너..close-button: 모달 닫기 버튼(×).<h3>문의 내용</h3>및id="modalInquiryContent": 모달에서 문의 내용을 표시할 영역.<h3>답변</h3>및id="modalAnswerContent": 모달에서 답변 내용을 표시할 영역.페이지네이션 (
.qna_page):<div class="qna_page"> <a href="#" th:if="${inquiryPageResponseDTO.prev}" th:attr="data-page=${inquiryPageResponseDTO.start - 1}"><</a> <th:block th:each="num : ${#numbers.sequence(inquiryPageResponseDTO.start, inquiryPageResponseDTO.end)}"> <a href="#" th:attr="data-page=${num}" th:class="${num == inquiryPageResponseDTO.pg ? 'current' : 'num'}" th:text="${num}"> 1 </a> </th:block> <a href="#" th:if="${inquiryPageResponseDTO.next}" th:attr="data-page=${inquiryPageResponseDTO.end + 1}">></a> </div>
- 이전 페이지 링크:
inquiryPageResponseDTO.prev가true일 때만<a class="prev">태그 생성.data-page속성에start - 1값을 설정해 이전 페이지 번호 전달.- 페이지 번호 반복:
#numbers.sequence(start, end)로start부터end까지 번호 생성.- 각 번호에 대해
<a>태그를 출력하고, 현재 페이지(pg)인 경우class="current", 아니면class="num".data-page속성에 페이지 번호 설정.- 다음 페이지 링크:
inquiryPageResponseDTO.next가true일 때만<a class="next">생성.data-page속성에end + 1값을 설정해 다음 페이지 번호 전달.
const prodNoElement = document.getElementById('prodNo');
const qnaEl = document.querySelector('.qna_content');
const qnaContainer = qnaEl ? qnaEl.parentNode : null;
const qnaPagination = document.querySelector('.qna_page');
// AJAX 응답으로 Q&A 목록과 페이징 생성
function updateQnaListUI(qnaData) {
if (qnaData && Array.isArray(qnaData.dtoList)) {
// 기존 Q&A 항목 제거
const existingQnaContents = qnaContainer.querySelectorAll('.qna_content');
existingQnaContents.forEach(content => content.remove());
// Q&A 항목 렌더링
qnaData.dtoList.forEach(inquiry => {
const qnaContent = document.createElement('div');
qnaContent.classList.add('qna_content');
qnaContent.innerHTML = `
<p>${inquiry.cateV1} > ${inquiry.cateV2}</p>
<p>${inquiry.content}</p>
<div class="answer">${inquiry.answer
? `<p>${inquiry.answer}</p><p>롯데쇼핑(주)본점<span>판매자</span></p>`
: '<p>답변 대기 중입니다.</p>'
}</div>
<div>
<a href="#">답변달기</a>
<p>${new Date(inquiry.wdate).toLocaleDateString('ko-KR', {
year: 'numeric',
month: '2-digit',
day: '2-digit'
})}</p>
</div>
`;
qnaContainer.appendChild(qnaContent);
});
// 페이징 초기화
qnaPagination.innerHTML = '';
// 이전 페이지 링크
if (qnaData.prev) {
const prevLink = document.createElement('a');
prevLink.href = '#';
prevLink.textContent = '<';
prevLink.setAttribute('data-page', qnaData.start - 1);
qnaPagination.appendChild(prevLink);
}
// 페이지 번호 링크
for (let i = qnaData.start; i <= qnaData.end; i++) {
const pageLink = document.createElement('a');
pageLink.href = '#';
pageLink.textContent = i;
pageLink.setAttribute('data-page', i);
pageLink.classList.add(i === qnaData.pg ? 'current' : 'num');
qnaPagination.appendChild(pageLink);
}
// 다음 페이지 링크
if (qnaData.next) {
const nextLink = document.createElement('a');
nextLink.href = '#';
nextLink.textContent = '>';
nextLink.setAttribute('data-page', qnaData.end + 1);
qnaPagination.appendChild(nextLink);
}
qnaContainer.appendChild(qnaPagination);
}
}
// Q&A 목록을 AJAX로 요청
function fetchQnaList(page, prodNo) {
const url = `/product/qnaList?pg=${page}&prodNo=${prodNo}`;
fetch(url)
.then(response => {
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
return response.json();
})
.then(data => {
updateQnaListUI(data);
})
.catch(error => {
console.error('Q&A 목록 로드 실패:', error);
});
}
// 초기 Q&A 요청
if (prodNoElement) {
fetchQnaList(1, prodNoElement.value);
}
// 페이지 번호 클릭 시 AJAX 재호출
qnaPagination.addEventListener('click', function (event) {
if (event.target.tagName === 'A' && event.target.hasAttribute('data-page') && prodNoElement) {
event.preventDefault();
const pageNumber = parseInt(event.target.getAttribute('data-page'));
const prodNoValue = prodNoElement.value;
if (!isNaN(pageNumber) && pageNumber > 0 && prodNoValue) {
fetchQnaList(pageNumber, prodNoValue);
}
}
});
기능 설명
- fetchQnaList:
/product/qnaList?pg=…&prodNo=…로 AJAX GET 요청- 응답 JSON(
PageResponseDTO<InquiryDTO>)을updateQnaListUI에 전달- updateQnaListUI:
- 기존
.qna_content요소 제거dtoList배열을 순회하며 각 문의 항목(<div class="qna_content">) 생성
- 카테고리 1/2단계(
cateV1 > cateV2), 문의 내용(content)- 답변이 있으면
<div class="answer">안에 답변과 판매자 정보, 없으면 “답변 대기 중입니다.” 표시- 작성일(
wdate)을toLocaleDateString('ko-KR')로 포맷하여 표시- 페이징 컨테이너(
.qna_page) 생성:
prev가 true면 이전 페이지 링크(data-page = start–1)start부터end까지 페이지 번호 링크(current또는num클래스)next가 true면 다음 페이지 링크(data-page = end+1)- 이벤트 바인딩:
- Q\&A 페이지네이션 클릭 시
fetchQnaList(해당페이지, prodNo)재호출
// Q&A 항목 클릭 시 모달 열기 (이벤트 위임)
document.addEventListener('click', function (event) {
if (event.target.classList.contains('qna_content')) {
const qnaModal = document.getElementById("qnaModal");
const modalInquiryContent = document.getElementById("modalInquiryContent");
const modalAnswerContent = document.getElementById("modalAnswerContent");
const qnaContentElement = event.target;
// 문의 텍스트와 답변 텍스트 추출
const inquiryTextElement = qnaContentElement.querySelector("p:nth-child(2)");
const answerDiv = qnaContentElement.querySelector(".answer");
let answerText = answerDiv ? answerDiv.innerHTML : "<p>답변 대기 중입니다.</p>";
if (inquiryTextElement) {
modalInquiryContent.textContent = inquiryTextElement.textContent;
}
modalAnswerContent.innerHTML = answerText;
if (qnaModal) {
qnaModal.style.display = "block";
}
}
});
// 모달 닫기 (× 버튼 및 바깥 클릭)
const closeBtn = document.querySelector("#qnaModal .close-button");
if (closeBtn) {
closeBtn.onclick = function () {
const qnaModal = document.getElementById("qnaModal");
if (qnaModal) {
qnaModal.style.display = "none";
}
};
}
window.onclick = function (event) {
const qnaModal = document.getElementById("qnaModal");
if (event.target === qnaModal) {
qnaModal.style.display = "none";
}
};
기능 설명
- 이벤트 위임: 문서 전체(
document)에 클릭 리스너를 등록하여,.qna_content를 클릭하면 →
- 해당 문의 텍스트(
p:nth-child(2))와 답변(.answer)을 추출하여 모달 내부에 채움#qnaModal을display = 'block'으로 보여 주기- 모달 닫기:
- × 버튼 클릭 시 모달 숨김(
display = 'none')- 모달 영역 바깥 클릭 시 모달 숨김