9:20 경 학원 도착
<9:30 1교시>
댓글 기능 이어서
gson은 List<CommentDto>를 [{},{},{}, ...]으로 반환한다.
데이터 베이스에 저장되어 있는 댓글 목록도 가지고 오는 메소드
<10:30 2교시>
<%@page import="test.user.dto.SessionDto"%>
<%@page import="test.post.dao.PostDao"%>
<%@page import="test.post.dto.PostDto"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%
//검색조건이 있는지 읽어와 본다.
String condition=request.getParameter("condition");
String keyword=request.getParameter("keyword");
String findQuery=null;
//있다면 dto 에 해당 정보를 담는다.
PostDto findDto=new PostDto();
if(condition != null){
//findDto=new PostDto();
findDto.setCondition(condition);
findDto.setKeyword(keyword);
findQuery="&condition="+condition+"&keyword="+keyword;
}
//자세히 보여줄 글의 번호를 읽어온다.
int num=Integer.parseInt(request.getParameter("num"));
findDto.setNum(num);
//DB 에서 해당 글의 정보를 얻어와서
PostDto dto=PostDao.getInstance().getData(findDto);
//세션 아이디를 읽어와서
String sessionId=session.getId();
//이미 읽었는지 여부를 얻어낸다
boolean isReaded=PostDao.getInstance().isReaded(num, sessionId);
if(!isReaded){
//글 조회수도 1 증가 시킨다
PostDao.getInstance().addViewCount(num);
//이미 읽었다고 표시한다.
PostDao.getInstance().insertReaded(num, sessionId);
}
request.setAttribute("dto", dto);
request.setAttribute("findDto", findDto);
request.setAttribute("findQuery", findQuery);
//응답한다
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/post/view.jsp</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<style>
#contents {
margin-top: 20px;
padding: 20px;
background-color: #fefefe;
border-radius: 10px;
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
border: 1px solid #ddd;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
#contents:hover {
transform: translateY(-5px);
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.3);
}
#content{
width: 100%;
height: 300px;
}
/* 댓글 프로필 이미지를 작은 원형으로 만든다. */
.profile-image{
width: 50px;
height: 50px;
border: 1px solid #cecece;
border-radius: 50%;
}
/* ul 요소의 기본 스타일 제거 */
.comments ul{
padding: 0;
margin: 0;
list-style-type: none;
}
/* .reply_icon 을 li 요소를 기준으로 배치 하기 */
.comments li{
position: relative;
}
.comments .reply-icon{
position: absolute;
top: 1rem;
left: 1rem;
color: red;
}
/* 대댓글을 들여 쓰기 위한 클래스 */
.indent{
padding-left: 50px;
}
/* 답글 아이콘은 일단 보이지 않게 */
.reply-icon{
display: none;
}
.comment-form, .re-insert-form, .update-form{
display: flex;
}
.comment-form textarea, .re-insert-form textarea, .update-form textarea{
height: 100px;
flex-grow: 1;
}
.comment-form button, .re-insert-form button, .update-form button{
flex-basis: 100px;
}
/* 대댓글폼은 일단 숨겨 놓는다 */
.re-insert-form, .update-form{
display: none;
}
/* 댓글 출력 디자인 */
.comments pre {
display: block;
padding: 9.5px;
margin: 5px 0;
font-size: 13px;
line-height: 1.42857143;
color: #333333;
word-break: break-all;
word-wrap: break-word;
background-color: #f5f5f5;
border: 1px solid #ccc;
border-radius: 4px;
}
.loader{
/* 로딩 이미지를 가운데 정렬하기 위해 */
text-align: center;
/* 일단 숨겨 놓기 */
display: none;
}
/* 회전하는 키프레임 정의 */
@keyframes rotateAni{
from{
transform: rotate(0deg);
}
to{
transform: rotate(360deg);
}
}
/* 회전하는 키프레임을 로더 이미지에 무한 반복 시키기 */
.loader svg{
animation: rotateAni 1s ease-out infinite;
}
body{
padding-bottom: 200px;
}
</style>
</head>
<body>
<div class="container">
<c:if test="${dto.prevNum ne 0}">
<a href="view.jsp?num=${dto.prevNum}${findQuery}">이전글</a>
</c:if>
<c:if test="${dto.nextNum ne 0}">
<a href="view.jsp?num=${dto.nextNum}${findQuery}">다음글</a>
</c:if>
<c:if test="${not empty findDto.condition}">
<p>
<strong>${findDto.condition }</strong> 조건
<strong>${findDto.keyword }</strong>검색어로 검색된 내용 자세히보기
</p>
</c:if>
<h3>글 상세 보기</h3>
<table class="table table-bordered">
<tr>
<th>글번호</th>
<td>${dto.num }</td>
</tr>
<tr>
<th>작성자</th>
<td>${dto.writer }</td>
</tr>
<tr>
<th>제목</th>
<td>${dto.title }</td>
</tr>
<tr>
<th>조회수</th>
<td>${dto.viewCount }</td>
</tr>
<tr>
<th>작성일</th>
<td>${dto.createdAt }</td>
</tr>
<tr>
<th>수정일</th>
<td>${dto.updatedAt }</td>
</tr>
<tr>
<td colspan="2">
<div id="contents">${dto.content }</div>
</td>
</tr>
</table>
<%-- 만일 글 작성자가 로그인된 아이디와 같다면 수정, 삭제 링크를 제공한다. --%>
<c:if test="${dto.writer eq sessionDto.userName }">
<a class="btn btn-outline-success btn-sm" href="${pageContext.request.contextPath }/post/protected/edit.jsp?num=${dto.num }">수정</a>
<a class="btn btn-outline-danger btn-sm" href="javascript:" onclick="deleteConfirm()">삭제</a>
<script>
function deleteConfirm(){
const isDelete=confirm("이 글을 삭제 하겠습니까?");
if(isDelete){
//javascript 를 이용해서 페이지 이동 시키기
location.href="${pageContext.request.contextPath }/post/protected/delete.jsp?num=${dto.num}";
}
}
</script>
</c:if>
<h4>댓글을 입력해 주세요</h4>
<!-- 원글에 댓글을 작성할 폼 -->
<form class="comment-form" action="protected/comment-insert.jsp" method="post">
<!-- 원글의 글번호가 댓글의 postNum 이 된다. -->
<input type="hidden" name="postNum" value="${dto.num}"/>
<!-- 원글의 작성자가 댓글의 대상자가 된다. -->
<input type="hidden" name="targetWriter" value="${dto.writer}"/>
<textarea name="content">${empty sessionDto ? '댓글 작성을 위해 로그인이 필요합니다' : ''}</textarea>
<button type="submit">등록</button>
</form>
<!-- 댓글 목록 -->
<div class="comments">
<ul>
</ul>
</div>
</div>
<script>
function refreshComments(){
//출력된 내용을 모두 지우고
document.querySelector(".comments ul").innerHTML="";
//댓글 1page 내용을 fetch() 를 이용해서 받아온다.
fetch("comment-list.jsp?pageNum=1&postNum=${dto.num}")
.then(res=>res.json())
.then(list=>{
console.log(list);
//댓글 목록에 있는 댓글 정보 하나 하나를 참조하면서
list.forEach(item=>{
//댓글 하나의 정보를 makeList 함수에 전달해서 댓글 정보가 출력된 li 를 얻어낸다.
const li = makeList(item);
//얻어낸 li 요소를 ul 에 추가한다.
document.querySelector(".comments ul").append(li);
});
addReplyListener(".reply-link");
});
}
refreshComments();
//로그인된 사용자의 이름
const userName="${sessionDto.userName}";
document.querySelector(".comment-form").addEventListener("submit", (e)=>{
//폼 제출 막기
e.preventDefault();
//폼에 작성된 내용을 이용해서 query 문자열을 얻어낸다.
const formData=new FormData(e.target);
const queryString = new URLSearchParams(formData).toString();
// fetch() 함수를 이용해서 댓글 정보를 페이지 전환 없이 서버에 전송한다.
fetch("protected/comment-insert.jsp",{
method:"POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body:queryString
})
.then(res=>res.json())
.then(comment=>{
//새로 추가된 댓글 정보를 이용해서 li 를 만든다.
//const li=makeList(comment);
//만든 li 를 댓글 목록의 가장 위에 출력한다.
//document.querySelector(".comments ul").insertAdjacentElement("afterbegin", li);
//댓글 1page 내용을 다시 출력해준다.
refreshComments();
});
});
//함수 호출하면서 댓글 하나의 정보를 담고 있는 object 를 전달하면 li 요소가 리턴된다.
function makeList(comment){
// li 요소를 만들어서
const li = document.createElement("li");
li.classList.add(comment.num !== comment.parentNum ? "indent" : "not");
// 프로필 이미지 처리
const profileImage = comment.profileImage
? `<img class="profile-image" src="${pageContext.request.contextPath }/upload/\${comment.profileImage}" alt="Profile Image">`
: `<svg class="profile-image" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<path d="M11 6a3 3 0 1 1-6 0 3 3 0 0 1 6 0z"/>
<path fill-rule="evenodd" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm8-7a7 7 0 0 0-5.468 11.37C3.242 11.226 4.805 10 8 10s4.757 1.225 5.468 2.37A7 7 0 0 0 8 1z"/>
</svg>`;
//li 요소안에 dl 을 출력한다.
li.innerHTML=`
<dl>
<dt>
\${profileImage}
<!-- 댓글 작성자 -->
<span>\${comment.writer}</span>
<!-- 댓글 대상자를 조건부로 출력 (대댓글에만 출력) -->
\${comment.num != comment.parentNum ? '@'+comment.targetWriter : ''}
<!-- 댓글 작성일자 -->
<small>\${comment.createdAt}</small>
<!-- 답글 링크 -->
<a data-num="\${comment.num}" class="reply-link" href="javascript:">답글</a>
<!-- 로그인된 유저가 쓴 댓글일 경우 수정, 삭제 링크를 제공한다 -->
</dt>
<dd>
<pre id="content\${comment.num}">\${comment.content}</pre>
</dd>
</dl>
<!-- 댓글의 댓글 작성할 폼 미리 출력하기 -->
<form id="reForm\${comment.num}" class="re-insert-form" method="post">
<input type="hidden" name="postNum" value="${dto.num }"/>
<input type="hidden" name="targetWriter" value="\${comment.writer }"/>
<input type="hidden" name="parentNum" value="\${comment.parentNum }"/>
<textarea name="content"></textarea>
<button type="submit">등록</button>
</form>
`;
return li;
}
function addReplyListener(selector){
//답글 링크에 대한 처리
const replyLinks=document.querySelectorAll(selector);
//반복문 돌면서 모든 링크에 이벤트 리스너 함수 등록하기
for(let i=0; i<replyLinks.length; i++){
replyLinks[i].addEventListener("click", (e)=>{
/*
if(!isLogin){
e.preventDefault();//폼 전송 막기
alert("로그인이 필요 합니다");
location.href="${pageContext.request.contextPath }/user/login-form.jsp";
}
*/
//클릭한 a 요소의 data-num 속성의 value 값을 읽어온다.
const num=e.target.getAttribute("data-num");
//보여주거나 숨길 form 의 참조값 얻어내기
const form=document.querySelector("#reForm"+num);
//눌러진 링크의 innerText 읽어오기
const currentText=e.target.innerText;
if(currentText === "답글"){
//보이게 하기
form.style.display="flex";
e.target.innerText="취소";
}else if(currentText === "취소"){
form.style.display="none";
e.target.innerText="답글";
}
});
}
}
</script>
</body>
</html>
위에가 샘이 보내준 코드
아래가 내가 따라한 코드 인데 에러남. 또 에러임. 하.ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ
<%@page import="test.user.dto.SessionDto"%>
<%@page import="test.post.dao.PostDao"%>
<%@page import="test.post.dto.PostDto"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%
//검색조건이 있는지 읽어와 본다.
String condition=request.getParameter("condition");
String keyword=request.getParameter("keyword");
String findQuery=null;
//있다면 dto 에 해당 정보를 담는다.
PostDto findDto=new PostDto();
if(condition != null){
//findDto=new PostDto();
findDto.setCondition(condition);
findDto.setKeyword(keyword);
findQuery="&condition="+condition+"&keyword="+keyword;
}
//자세히 보여줄 글의 번호를 읽어온다.
int num=Integer.parseInt(request.getParameter("num"));
findDto.setNum(num);
//DB 에서 해당 글의 정보를 얻어와서
PostDto dto=PostDao.getInstance().getData(findDto);
//세션 아이디를 읽어와서
String sessionId=session.getId();
//이미 읽었는지 여부를 얻어낸다
boolean isReaded=PostDao.getInstance().isReaded(num, sessionId);
if(!isReaded){
//글 조회수도 1 증가 시킨다
PostDao.getInstance().addViewCount(num);
//이미 읽었다고 표시한다.
PostDao.getInstance().insertReaded(num, sessionId);
}
request.setAttribute("dto", dto);
request.setAttribute("findDto", findDto);
request.setAttribute("findQuery", findQuery);
//응답한다
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/post/view.jsp</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<style>
#contents {
margin-top: 20px;
padding: 20px;
background-color: #fefefe;
border-radius: 10px;
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
border: 1px solid #ddd;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
#contents:hover {
transform: translateY(-5px);
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.3);
}
#content{
width: 100%;
height: 300px;
}
/* 댓글 프로필 이미지를 작은 원형으로 만든다. */
.profile-image{
width: 50px;
height: 50px;
border: 1px solid #cecece;
border-radius: 50%;
}
/* ul 요소의 기본 스타일 제거 */
.comments ul{
padding: 0;
margin: 0;
list-style-type: none;
}
/* .reply_icon 을 li 요소를 기준으로 배치 하기 */
.comments li{
position: relative;
}
.comments .reply-icon{
position: absolute;
top: 1rem;
left: 1rem;
color: red;
}
/* 대댓글을 들여 쓰기 위한 클래스 */
.indent{
padding-left: 50px;
}
/* 답글 아이콘은 일단 보이지 않게 */
.reply-icon{
display: none;
}
.comment-form, .re-insert-form, .update-form{
display: flex;
}
.comment-form textarea, .re-insert-form textarea, .update-form textarea{
height: 100px;
flex-grow: 1;
}
.comment-form button, .re-insert-form button, .update-form button{
flex-basis: 100px;
}
/* 대댓글폼은 일단 숨겨 놓는다 */
.re-insert-form, .update-form{
display: none;
}
/* 댓글 출력 디자인 */
.comments pre {
display: block;
padding: 9.5px;
margin: 5px 0;
font-size: 13px;
line-height: 1.42857143;
color: #333333;
word-break: break-all;
word-wrap: break-word;
background-color: #f5f5f5;
border: 1px solid #ccc;
border-radius: 4px;
}
.loader{
/* 로딩 이미지를 가운데 정렬하기 위해 */
text-align: center;
/* 일단 숨겨 놓기 */
display: none;
}
/* 회전하는 키프레임 정의 */
@keyframes rotateAni{
from{
transform: rotate(0deg);
}
to{
transform: rotate(360deg);
}
}
/* 회전하는 키프레임을 로더 이미지에 무한 반복 시키기 */
.loader svg{
animation: rotateAni 1s ease-out infinite;
}
body{
padding-bottom: 200px;
}
</style>
</head>
<body>
<div class="container">
<c:if test="${dto.prevNum ne 0}">
<a href="view.jsp?num=${dto.prevNum}${findQuery}">이전글</a>
</c:if>
<c:if test="${dto.nextNum ne 0}">
<a href="view.jsp?num=${dto.nextNum}${findQuery}">다음글</a>
</c:if>
<c:if test="${not empty findDto.condition}">
<p>
<strong>${findDto.condition }</strong> 조건
<strong>${findDto.keyword }</strong>검색어로 검색된 내용 자세히보기
</p>
</c:if>
<h3>글 상세 보기</h3>
<table class="table table-bordered">
<tr>
<th>글번호</th>
<td>${dto.num }</td>
</tr>
<tr>
<th>작성자</th>
<td>${dto.writer }</td>
</tr>
<tr>
<th>제목</th>
<td>${dto.title }</td>
</tr>
<tr>
<th>조회수</th>
<td>${dto.viewCount }</td>
</tr>
<tr>
<th>작성일</th>
<td>${dto.createdAt }</td>
</tr>
<tr>
<th>수정일</th>
<td>${dto.updatedAt }</td>
</tr>
<tr>
<td colspan="2">
<div id="contents">${dto.content }</div>
</td>
</tr>
</table>
<%-- 만일 글 작성자가 로그인된 아이디와 같다면 수정, 삭제 링크를 제공한다. --%>
<c:if test="${dto.writer eq sessionDto.userName }">
<a class="btn btn-outline-success btn-sm" href="${pageContext.request.contextPath }/post/protected/edit.jsp?num=${dto.num }">수정</a>
<a class="btn btn-outline-danger btn-sm" href="javascript:" onclick="deleteConfirm()">삭제</a>
<script>
function deleteConfirm(){
const isDelete=confirm("이 글을 삭제 하겠습니까?");
if(isDelete){
//javascript 를 이용해서 페이지 이동 시키기
location.href="${pageContext.request.contextPath }/post/protected/delete.jsp?num=${dto.num}";
}
}
</script>
</c:if>
<h4>댓글을 입력해 주세요</h4>
<!-- 원글에 댓글을 작성할 폼 -->
<form class="comment-form" action="protected/comment-insert.jsp" method="post">
<!-- 원글의 글번호가 댓글의 postnum 번호가 된다. -->
<input type="hidden" name="postNum" value="${dto.num}"/>
<!-- 원글의 작성자가 댓글의 대상자가 된다. -->
<input type="hidden" name="targetWriter" value="${dto.writer}"/>
<textarea name="content">${empty sessionDto ? '댓글 작성을 위해 로그인이 필요합니다' : ''}</textarea>
<button type="submit">등록</button>
</form>
<!-- 댓글 목록 -->
<div class="comments">
<ul>
</ul>
</div>
</div>
<script>
//댓글 목록 새로 고침
function refreshComments(){
document.querySelector(".comments ul").innerHTML="";
//댓글 1page 내용을 fetch()를 이용해서 받아온다.
fetch("comment-list.jsp?pageNum=1&postNum=${dto.num}")
.then(res=>res.json())
.then(list=>{
console.log(list);
list.forEach(item=>{
/* //댓글 하나의 정보를 makeList()함수에 전달해서 댓글 정보가 출력된 li를 얻어낸다.
const li = makeList(item);
//얻어낸 li 요소를 ul에 추가한다.
document.querySelector(".comment ul").append(li); */
//댓글 목록을 다시 불러온다
refreshComments();
});
addReplyListener(".reply-link")
});
}
function addReplyListener(selector){
//답글 링크에 대한 처리
const replyLinks=document.querySelectorAll(selector);
//반복문 돌면서 모든 링크에 이벤트 리스너 함수 등록하기
for(let i=0; i<replyLinks.length; i++){
replyLinks[i].addEventListener("click", (e)=>{
if(!isLogin){
e.preventDefault();//폼 전송 막기
alert("로그인이 필요 합니다");
location.href="${pageContext.request.contextPath }/user/login-form.jsp";
}
//클릭한 a 요소의 data-num 속성의 value 값을 읽어온다.
const num=e.target.getAttribute("data-num");
//보여주거나 숨길 form 의 참조값 얻어내기
const form=document.querySelector("#reForm"+num);
//눌러진 링크의 innerText 읽어오기
const currentText=e.target.innerText;
if(currentText === "답글"){
//보이게 하기
form.style.display="flex";
e.target.innerText="취소";
}else if(currentText === "취소"){
form.style.display="none";
e.target.innerText="답글";
}
});
}
}
//로그인된 사용자의 이름
const userName="${sessionDto.userName}";
document.querySelector(".comment-form").addEventListener("submit", (e)=>{
//폼 제출 막기
e.preventDefault();
//폼에 작성된 내용을 이용해서 query 문자열을 얻어낸다
const formData=new FormData(e.target);
const queryString = new URLSearchParams(formData).toString();
//fetch 함수를 이용해서 댓글 정보를 페이지 전환없이 서버에 전송함.
fetch("protected/comment-insert.jsp",{
method:"POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body:queryString
})
.then(res=>res.json())
.then(comment=>{
//새로 추가된 댓글 정보를 이용해서 li를 만든다
const li=makeList(comment);
//만든 li를 댓글 목록의 가장 위에 출력한다.
document.querySelector(".comments ul").insertAdjacentElement("afterbegin", li);
/* //저장된 댓글 정보가 응답됨
console.log(comment);
//li 요소를 만들어서
const li = document.createElement("li");
li.classList.add(comment.num !== comment.parentNum ? "indent" : "not");
// 프로필 이미지 처리
const profileImage = comment.profileImage
? `<img class="profile-image" src="${pageContext.request.contextPath}/upload/\${comment.profileImage}" alt="Profile Image">`
: `<svg class="profile-image" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<path d="M11 6a3 3 0 1 1-6 0 3 3 0 0 1 6 0z"/>
<path fill-rule="evenodd" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm8-7a7 7 0 0 0-5.468 11.37C3.242 11.226 4.805 10 8 10s4.757 1.225 5.468 2.37A7 7 0 0 0 8 1z"/>
</svg>`;
//li 요소 안에 dl을 출력
li.innerHTML=`
<dl>
<dt>
\${profileImage}
<!-- 댓글 작성자 -->
<span>\${comment.writer}</span>
<!-- 댓글 대상자를 조건부로 출력 (대댓글에만 출력) -->
\${comment.num != comment.parentNum ? '@'+comment.targetWriter : ''}
<!-- 댓글 작성일자 -->
<small>\${comment.createdAt}</small>
<!-- 답글 링크 -->
<a data-num="\${comment.num}" class="reply-link" href="javascript:">답글</a>
<!-- 로그인된 유저가 쓴 댓글일 경우 수정, 삭제 링크를 제공한다 -->
</dt>
<dd>
<pre id="content\${comment.num}">\${comment.content}</pre>
</dd>
</dl>
`;
document.querySelector(".comments ul").append(li); */
});
});
//함수를 호출하면서 댓글 하나의 정보를 담고 있는 object를 전달하면 li요소가 리턴되는 함수
function makeList(comment){
// li 요소를 만들어서
const li = document.createElement("li");
li.classList.add(comment.num !== comment.parentNum ? "indent" : "not");
// 프로필 이미지 처리
const profileImage = comment.profileImage
? `<img class="profile-image" src="${pageContext.request.contextPath }/upload/\${comment.profileImage}" alt="Profile Image">`
: `<svg class="profile-image" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<path d="M11 6a3 3 0 1 1-6 0 3 3 0 0 1 6 0z"/>
<path fill-rule="evenodd" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm8-7a7 7 0 0 0-5.468 11.37C3.242 11.226 4.805 10 8 10s4.757 1.225 5.468 2.37A7 7 0 0 0 8 1z"/>
</svg>`;
//li 요소안에 dl 을 출력한다.
li.innerHTML=`
<dl>
<dt>
\${profileImage}
<!-- 댓글 작성자 -->
<span>\${comment.writer}</span>
<!-- 댓글 대상자를 조건부로 출력 (대댓글에만 출력) -->
\${comment.num != comment.parentNum ? '@'+comment.targetWriter : ''}
<!-- 댓글 작성일자 -->
<small>\${comment.createdAt}</small>
<!-- 답글 링크 -->
<a data-num="\${comment.num}" class="reply-link" href="javascript:">답글</a>
<!-- 로그인된 유저가 쓴 댓글일 경우 수정, 삭제 링크를 제공한다 -->
</dt>
<dd>
<pre id="content\${comment.num}">\${comment.content}</pre>
</dd>
</dl>
<!-- 댓글의 댓글 작성할 폼 미리 출력하기 -->
<form id="reForm\${comment.num}" class="re-insert-form" method="post">
<input type="hidden" name="postNum" value="${dto.num }"/>
<input type="hidden" name="targetWriter" value="\${comment.writer }"/>
<input type="hidden" name="parentNum" value="\${comment.parentNum }"/>
<textarea name="content"></textarea>
<button type="submit">등록</button>
</form>
`;
return li;
};
</script>
</body>
</html>
오전 진도 11시에 끝나고, 프로젝트 준비하라고 시간 주심.
일단 기능 어제 완성했고, ppt 만들어서 보내야겠다.
노트북으로 ppt 작업중. 일단 기능 위주로 작성해서 달래서 그러는 중.
일단 오늘 학원에서는 버그잡자고 그래서 PPT 집에서 만들려고 하고 버그 잡는 중인데 갑자기 서버 튕기기 시작.
데이터베이스 계정 바꿔서 다시 시도.
테이블 더미 데이터가 전부 바뀜.
어제 했던 코드, 매장별 매출 조회하는데 데이터 값이 없어서 조회가 안되는 애들 문구가 제대로 뜨지 않아서 선생님 도움 받아서 해결.
dao에서 이미 list를 반환했기 때문에 list가 null인걸 확인하는건 소용이 없고, list.size()==0인지를 확인해야 함.
<%@page import="test.dao.Com1SaleDao"%>
<%@page import="test.dao.Com1Dao"%>
<%@page import="test.dto.Com1SaleDto"%>
<%@page import="java.util.List"%>
<%@page import="java.util.ArrayList" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
//현재 페이지 위치를 세션 영역에 저장 (관리자 전용 네비바에 활성 상태 표시 위함)
session.setAttribute("current_page", "view");
//로그인 상태 표시 : 세션 영역에서 접속 계정 정보 가져오기
String comname = (String)session.getAttribute("comname");
String ename = (String)session.getAttribute("ename");
Com1SaleDao saledao = Com1SaleDao.getInstance();
List<Integer> storenums = Com1Dao.getInstance().getStoreNumList();
int storenum=-1;
List<Com1SaleDto> listall= saledao.getListAll();
List<Com1SaleDto> listmonth = saledao.getListSalebyMonth();
List<Com1SaleDto> listyear = saledao.getListSalebyYear();
List<Com1SaleDto> listbystore = new ArrayList<>();
List<Com1SaleDto> listbystoremonthly = new ArrayList<>();
List<Com1SaleDto> listbystoreyearly = new ArrayList<>();
String strStoreNum=request.getParameter("storenum");
if(strStoreNum!=null&&!strStoreNum.isEmpty()){
storenum = Integer.parseInt(strStoreNum);
listbystore = saledao.getListbyStore(storenum);
listbystoremonthly = saledao.getListMonthlybyStore(storenum);
listbystoreyearly= saledao.getListYearlybyStore(storenum);
};
request.setAttribute("storenum", storenum);
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>sale/view.jsp</title>
<style>
.table-container {
padding-bottom: 100px; /* footer 높이보다 여유 있게 추가 */
}
</style>
<!-- 페이지 로딩에 필요한 자원 -->
<jsp:include page="/include/resource.jsp"></jsp:include>
</head>
<body>
<%-- 관리자 페이지 전용 네비바 --%>
<jsp:include page="/include/ceoNav.jsp"></jsp:include>
<!-- 현재 접속 상태 표시 -->
<div class="container">
<p><%=comname %>의
<%=ename %>님 접속 중
</p>
</div>
<!-- 본문 -->
<div class="contents text-center mt-3 mx-auto" style="width: 900px;">
<!-- 조회조건 -->
<div class="nav nav-tabs">
<button class="tab-button nav-item nav-link" id="allTab" onclick="switchTab('all')">전체 매출</button>
<button class="tab-button nav-item nav-link" id="yearTab" onclick="switchTab('year')">전체 연매출</button>
<button class="tab-button nav-item nav-link" id="monthTab" onclick="switchTab('month')">전체 월매출</button>
<div class="tab-button nav-item nav-link" id="storeTab" onclick="switchTab('store')">
<form action="view.jsp" method="get" id="storeForm">
<label for="storenum">지점 선택: </label>
<select name="storenum" id="storenum" onchange="switchTab('store'); document.getElementById('storeForm').submit();">
<option value="">-- 지점을 선택하세요 --</option>
<% for (Integer tmp : storenums) { %>
<option value="<%= tmp %>" <%= (tmp.equals(storenum)) ? "selected" : ""%> > <%= tmp %>호점
</option>
<% } %>
</select>
</form>
</div>
</div>
<div class="contents text-center mt-3 mx-auto" style="width: 900px;">
<div id="allContent" class="table-container tab-content p-3 bg-light rounded shadow-sm" style="display: block;">
<div class="table-responsive">
<table class="table table-hover text-center align-middle">
<thead class="table-dark">
<tr>
<th>호점</th>
<th>날짜 구분</th>
<th class="thsale">매출</th>
</tr>
</thead>
<tbody>
<% if (listall == null) { %>
<tr>
<td>매출 정보가 없습니다.</td>
</tr>
<%} else { %>
<%for (Com1SaleDto tmp : listall) { %>
<tr>
<td><%= tmp.getStoreNum() %></td>
<td><%= tmp.getSalesDate() %></td>
<td><%= tmp.getDailySales() %></td>
</tr>
<% }
}%>
</tbody>
</table>
</div>
</div>
<div id="yearContent" class="tab-content p-3 bg-light rounded shadow-sm" style="display: block;">
<div class="table-responsive">
<table class="table table-hover text-center align-middle">
<thead class="table-dark">
<tr>
<th>날짜 구분</th>
<th class="thsale">매출</th>
</tr>
</thead>
<tbody>
<% if ( listyear==null) { %>
<tr>
<td>연매출 정보가 없습니다.</td>
</tr>
<% } else { %>
<% for (Com1SaleDto tmp : listyear) { %>
<tr>
<td><%= tmp.getSyear() %></td>
<td><%= tmp.getYearlySales() %></td>
</tr>
<% }
}%>
</tbody>
</table>
</div>
</div>
<div id="monthContent" class="tab-content p-3 bg-light rounded shadow-sm" style="display: block;">
<div class="table-responsive">
<table class="table table-hover text-center align-middle">
<thead class="table-dark">
<tr>
<th>날짜 구분</th>
<th class="thsale">매출</th>
</tr>
</thead>
<tbody>
<% if (listmonth==null) { %>
<tr>
<td>월매출 정보가 없습니다.</td>
</tr>
<%}else{ %>
<%for (Com1SaleDto tmp: listmonth) { %>
<tr>
<td><%= tmp.getSmonth() %></td>
<td><%= tmp.getMonthlySales() %></td>
</tr>
<% }
}%>
</tbody>
</table>
</div>
</div>
<div id="storeContent" class="table-container tab-content p-3 bg-light rounded shadow-sm" style="display: block;">
<div class="table-responsive">
<table class="table table-hover text-center align-middle">
<thead class="table-dark">
<tr>
<th>날짜 구분</th>
<th class="thsale">매출</th>
</tr>
</thead>
<tbody>
<% if (listbystoreyearly.size() == 0) { %>
<tr>
<td colspan="2">현매장 <%=storenum %>호점의 연매출 정보가 없습니다.</td>
</tr>
<%}else{ %>
<%for (Com1SaleDto tmp: listbystoreyearly) { %>
<tr>
<td><%= tmp.getSyear() %></td>
<td><%= tmp.getYearlySales() %></td>
</tr>
<% }
}%>
</tbody>
</table>
</div>
<div class="table-responsive">
<table class="table table-hover text-center align-middle">
<thead class="table-dark">
<tr>
<th>날짜 구분</th>
<th class="thsale">매출</th>
</tr>
</thead>
<tbody>
<% if (listbystoremonthly.size()==0) { %>
<tr>
<td colspan="2">현매장 <%=storenum %>호점의 월매출 정보가 없습니다.</td>
</tr>
<%}else{ %>
<%for (Com1SaleDto tmp: listbystoremonthly) { %>
<tr>
<td><%= tmp.getSmonth() %></td>
<td><%= tmp.getMonthlySales() %></td>
</tr>
<% }
}%>
</tbody>
</table>
</div>
<div class="table-responsive">
<table class="table table-hover text-center align-middle">
<thead class="table-dark">
<tr>
<th>날짜 구분</th>
<th class="thsale">매출</th>
</tr>
</thead>
<% if (listbystore.size()==0){ %>
<tbody>
<tr>
<td colspan="2"> 현매장 <%=storenum %>호점의 매출 정보가 없습니다. </td>
</tr>
</tbody>
<%}else{ %>
<tbody>
<%for (Com1SaleDto tmp: listbystore) { %>
<tr>
<td><%= tmp.getSdate() %></td>
<td><%= tmp.getDailySales() %></td>
</tr>
<% }%>
</tbody>
<%}%>
</table>
</div>
</div>
</div>
</div>
<div class="table-container">
</div>
<div class="position-fixed bottom-0 w-100">
<jsp:include page="/include/footer.jsp" />
</div>
<script>
function switchTab(tab) {
event.preventDefault();
const tabs = ['all', 'year', 'month', 'store'];
tabs.forEach(t => {
document.getElementById(t + 'Content').style.display = 'none';
document.getElementById(t + 'Tab').classList.remove('active-tab');
});
document.getElementById(tab + 'Content').style.display = 'block';
document.getElementById(tab + 'Tab').classList.add('active-tab');
}
window.onload = function() {
const tabs = ['all', 'year', 'month', 'store'];
tabs.forEach(t => {
document.getElementById(t + 'Content').style.display = 'none';
document.getElementById(t + 'Tab').classList.remove('active-tab');
});
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.has('storenum')) {
switchTab('store');
}
};
</script>
</body>
</html>
'자바풀스택 과정 > 자바 풀 스택 : 수업내용정리' 카테고리의 다른 글
자바 풀 스택 2/12 하루 기록 053 (0) | 2025.02.12 |
---|---|
자바 풀 스택 2/11 하루 기록 052 (0) | 2025.02.11 |
자바 풀 스택 2/7 하루 기록 050 (1) | 2025.02.07 |
자바 풀 스택 2/6 하루 기록 049 (0) | 2025.02.06 |
자바 풀 스택 2/5 하루 기록 048 (1) | 2025.02.05 |