The Debugging Chronicles : "코드의 미학"
[Team Project - 코마] 중중프 완료 본문
팀명 : 코마 (7인)
(Model 파트 2인 / View파트 3인 / Controller 파트 2인 )
담당: View 파트장
맡은 페이지 : 메인, 마이페이지, 글 상세 페이지
언제나 발표날은 떨리고 아쉽다.
피드백을 먼저 올리자면
1.몇몇 발표자들은 말끝을 흐리는 경우가 있어서 잘 들리지않아 집중이 어려웠습니다.
2.에러에 대한 리포트 부분은 고찰이 부족해보입니다.
3.중프때는 자기꺼 시연하고 자기꺼 발표하도록
4.똑같은거 시연하더라도 시연 각 각 하기
5.대본은 짜서 발표하기
6.실제 샘플데이터를 사용해서 발표
7.바탕 하얀색으로 해서 보여주기
8.합 맞추어서 설명하기
9.단어 선정을 잘해서 하기 나이때가 어려보이면 좋지 않음
7.답변 더 잘 준비하기
8.필살기 위주로 발표 준비하기
9. 대본 작성은 녹음을 하고
10.코드 라인 짚어주기
설계
와이어 프레임은 피크마를 이용해서 만들었다.
그리고 컨트롤러에게 전달할 파라미터 값들을 미리 정리해 두었다
실제 코드 구현
더보기
Main.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib tagdir="/WEB-INF/tags" prefix="mytag" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<!-- Fonts and icons -->
<script src="assets/js/plugin/webfont/webfont.min.js"></script>
<script src="https://kit.fontawesome.com/7f7b0ec58f.js"
crossorigin="anonymous"></script>
<!-- CSS Files -->
<link rel="stylesheet" href="assets/css/bootstrap.min.css" />
<link rel="stylesheet" href="assets/css/plugins.min.css" />
<link rel="stylesheet" href="assets/css/kaiadmin.css" />
</head>
<body>
<!-- GNB 커스텀 태그 -->
<mytag:gnb member_id="${MEMBER_ID}" ></mytag:gnb>
<!-- container start -->
<div class="container">
<div class="page-inner">
<div class="row pt-3">
<div class="col-12 p-0">
<form action="MAINPAGEACTION.do" method="GET">
<div class="row">
<div class="col-md-3 col-lg-3">
<div class="form-group">
<select name="board_list"
class="form-select form-control-lg">
<option>글 제목</option>
<option>작성자</option>
<option>아이디</option>
</select>
</div>
</div>
<div class="col-md-6 col-lg-7">
<div class="form-group">
<div class="input-icon">
<input name="board_keyword" type="text" class="form-control"
placeholder="검색어를 입력해주세요" /> <span class="input-icon-addon">
<button type="submit" class="btn">
<i class="fa fa-search"></i>
</button>
</span>
</div>
</div>
</div>
<div
class="col-md-3 col-lg-2 d-flex align-items-center justify-content-end">
<a href="INSERTBOARDPAGEACTION.do" class="d-block btn">
<button type="button"
class="btn btn-primary btn-round px-5 py-3">글 작성</button>
</a>
</div>
</div>
</form>
</div>
</div>
<c:forEach var="board" items="${BOARD}">
<c:choose>
<c:when test="${board.board_num> 0}">
<div class="row pt-5">
<div class="col-md-10">
<div class="card card-stats card-round mb-0">
<div class="card-body p-5 d-flex justify-content-between">
<h3 class="card-title">
<a href="BOARDONEPAGEACTION.do?board_num=${board.board_num}"
class="link-dark"> ${board.board_title}</a>
</h3>
<div class="info-user">
<div class="username">작성자 : ${board.board_writer_id}</div>
<div class="status">글 조회수 : ${board.board_cnt}</div>
</div>
</div>
</div>
</div>
</div>
</c:when>
<c:otherwise>
<h1>최신 글 목록이 없습니다...</h1>
</c:otherwise>
</c:choose>
</c:forEach>
<div class="row pt-5">
<div class="col-md-10 d-flex justify-content-center">
<nav aria-label="Page navigation">
<ul id="pagination" class="pagination justify-content-center">
</ul>
</nav>
</div>
</div>
</div>
<!-- container end -->
<div class="cookies d-none" id="cookies">
<div class="cookies-header py-1 px-3 text-end">
<a id="cookies-cancel" class="btn btn-toggle">x</a>
</div>
<div class="cookies-body p-3 text-center">
<h2 class="text-white">최고의 클라이밍 슈즈</h2>
<p class="text-white">모멘텀 암벽화</p>
<img src="assets/img/shoes.png" />
<h2 class="text-white">~ 50%</h2>
<button class="btn btn-info btn-round">바로 구매하기</button>
<a id="cookie-today-hide" class="btn btn-toogle text-white">오늘
하루 보지 않기</a>
</div>
</div>
</div>
<!-- Core JS Files -->
<script src="assets/js/core/jquery-3.7.1.min.js"></script>
<script src="assets/js/core/popper.min.js"></script>
<script src="assets/js/core/bootstrap.min.js"></script>
<script src="assets/js/core/jquery.cookie.js"></script>
<script>
//cookies
var cookies = document.getElementById("cookies");
var cancel = document.getElementById("cookies-cancel");
var btnTodayHide = document.getElementById("cookie-today-hide");
// 최초 쿠키배너 노출
// cookie라는 이름의 쿠키가 존재하지 않으면 레이어 노출
if (!$.cookie('cookie')) {
cookieShow();
}
// 쿠키 배너 닫기 버튼 클릭
cancel.addEventListener("click", function() {
cookieHide(0);
});
// 쿠키 배너 오늘 하루 닫기 버튼 클릭
btnTodayHide.addEventListener("click", function() {
cookieHide(1);
});
// 쿠키 배너 노출
function cookieShow() {
cookies.classList.remove("d-none");
}
// 쿠키 배너 비노출
function cookieHide(state) {
cookies.classList.add("d-none");
if (state === 1) {
// cookie 처리
// 'cookie' 이름의 쿠키가 있는지 체크해본다
if ($.cookie('cookie') == undefined) {
//'cookie'이름을 넣고 1일 후 쿠키가 삭제되도록
// path 값을 '/'를 주면 모든페이지에서 유효한 쿠키가 생성
$.cookie('cookie', 'Y', {
expires : 1,
path : '/'
});
}
}
}
// 페이지네이션 생성
// 4가지 값
// 화면에 보여질 페이지 그룹
// 화면에 보여질 첫번째 페이지 = 화면에 그려질 마지막 페이지 - (한 화면에 나타낼 페이지 - 1)
// 화면에 보여질 마지막 페이지 = 화면에 보여질 페이지 그룹 * 한 화면에 나타낼 페이지
// 총 페이지 수 = Math.ceil(전체 개수 / 한 페이지에 나타낼 데이터 수)
// 페이지네이션 생성 함수
function renderpagination(currentPage, _totalCount,param) {
// 현재 게시물의 전체 개수가 10개 이하면 pagination을 숨깁니다.
if (_totalCount <= 10) return;
let board_list = param.get('board_list');
console.log(board_list);
let board_keyword = param.get('board_keyword'); // 서버에서 전달된 님 내용임 맞겠지
console.log(board_keyword);
// 총 페이지 수 계산 (전체 게시물 수를 한 페이지에 보여줄 게시물 수로 나눈 값의 올림)
const totalPage = Math.ceil(_totalCount / 10);
// 현재 페이지 그룹 계산 (현재 페이지를 10으로 나눈 값의 올림)
const pageGroup = Math.ceil(currentPage / 10);
// 현재 페이지 그룹에서의 마지막 페이지 계산
let last = pageGroup * 10;
// 마지막 페이지가 총 페이지 수를 초과하지 않도록 조정
if (last > totalPage) last = totalPage;
// 현재 페이지 그룹에서의 첫 번째 페이지 계산
const first = last - (10 - 1) <= 0 ? 1 : last - (10 - 1);
// 다음 그룹의 첫 페이지 계산
const next = last + 1;
// 이전 그룹의 마지막 페이지 계산
const prev = first - 1;
// 페이지네이션 버튼을 담을 비어있는 DocumentFragment 객체 생성
const fragmentPage = document.createDocumentFragment();
// 이전 그룹으로 이동하는 버튼 생성 (prev가 0보다 크다면 생성)
if (prev > 0) {
const preli = document.createElement('li');
preli.id = 'prev-btn';
preli.className = 'page-item';
if(board_list != null){
preli.insertAdjacentHTML("beforeend",
"<a id='allprev' class='page-link' href='MAINPAGEACTION.do?page=" + prev + "&board_list="+board_list+"&board_keyword="+board_keyword+"' aria-label='Previous'>" +
"<span aria-hidden='true'>«</span>" +
"</a>"
);
}
else{
preli.insertAdjacentHTML("beforeend",
"<a id='allprev' class='page-link' href='MAINPAGEACTION.do?page="+prev+"' aria-label='Previous'>"
+"<span aria-hidden='true'>«</span> </a>");
}
fragmentPage.appendChild(preli);
}
// 현재 페이지 그룹의 페이지 번호 버튼 생성
for (let i = first; i <= last; i++) {
const li = document.createElement("li");
li.className = 'page-item';
if(board_list != null){
li.insertAdjacentHTML("beforeend",
"<a class='page-link m-2' href='MAINPAGEACTION.do?page=" + i + "&board_list="+board_list+"&board_keyword="+board_keyword+"' id='page-" + i + "' data-num='" + i + "'>" +
i +
"</a>"
);
}
else{
li.insertAdjacentHTML("beforeend",
"<a class='page-link m-2' href='MAINPAGEACTION.do?page=" + i + "' id='page-" + i + "' data-num='" + i + "'>" +
i +
"</a>");
}
fragmentPage.appendChild(li);
}
// 다음 그룹으로 이동하는 버튼 생성 (last가 totalPage보다 작다면 생성)
if (last < totalPage) {
const endli = document.createElement('li');
endli.id = 'next-btn';
endli.className = 'page-item';
if(board_list != null){
endli.insertAdjacentHTML("beforeend",
"<a class='page-link' href='MAINPAGEACTION.do?page=" + next + "&board_list="+board_list+"&board_keyword="+board_keyword+"' id='allnext' aria-label='Next'>" +
"<span aria-hidden='true'>»</span>" +
"</a>"
);
}
else{
endli.insertAdjacentHTML("beforeend",
"<a class='page-link' href='MAINPAGEACTION.do?page=" + next +"' id='allnext' aria-label='Next'><span aria-hidden='true'>»</span></a>");
}
fragmentPage.appendChild(endli);
}
// 생성된 페이지네이션 버튼들을 화면에 추가
document.getElementById('pagination').appendChild(fragmentPage);
};
// 페이지 버튼 클릭 이벤트 처리
$("#pagination a").click(function (e) {
// 기본 동작(페이지 이동) 방지
e.preventDefault();
// 클릭된 페이지 링크 요소를 jQuery 객체로 저장
const $item = $(this);
// 클릭된 페이지 링크의 텍스트(페이지 번호)를 가져와 selectedPage 변수에 저장
let selectedPage = $item.text();
// 각 버튼의 ID에 따라 선택된 페이지 설정
if ($item.attr("id") === "next")
selectedPage = next;
if ($item.attr("id") === "prev")
selectedPage = prev;
if ($item.attr("id") === "allprev")
selectedPage = 1;
if ($item.attr("id") === "allnext")
selectedPage = totalPage;
// 페이지네이션 재생성 및 해당 페이지 데이터 로드
renderpagination(selectedPage, _totalCount);
list.search(selectedPage); // 이 함수가 제대로 정의되어 있는지 확인
});
// DOM이 완전히 로드된 후 페이지네이션을 생성
document.addEventListener("DOMContentLoaded", function() {
const _totalCount = ${totalCount}; // 서버에서 전달된 전체 게시물 개수
const currentPage = ${currentPage}; // 서버에서 전달된 현재 페이지 번호
let query = window.location.search;
let param = new URLSearchParams(query);
renderpagination(currentPage,_totalCount,param); // 페이지네이션 생성 함수 호출
// 현재 페이지를 표시하기 위해 active 클래스 추가
$("#pagination a").removeClass("active text-white");
$("#pagination a#page-" + currentPage).addClass("active text-white");
console.log(currentPage);
});
</script>
</body>
</html>
더보기
mypage.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib tagdir="/WEB-INF/tags" prefix="mytag" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<!-- Fonts and icons -->
<script src="assets/js/plugin/webfont/webfont.min.js"></script>
<script src="https://kit.fontawesome.com/7f7b0ec58f.js"
crossorigin="anonymous"></script>
<!-- CSS Files -->
<link rel="stylesheet" href="assets/css/bootstrap.min.css" />
<link rel="stylesheet" href="assets/css/plugins.min.css" />
<link rel="stylesheet" href="assets/css/kaiadmin.css" />
</head>
<body>
<!-- GNB 커스텀 태그 -->
<mytag:gnb member_id="${MEMBER_ID}" ></mytag:gnb>
<!-- container start -->
<div class="container">
<div class="page-inner">
<div class="row justify-content-center">
<div class="col-md-5">
<div class="card card-stats card-round p-5 ">
<div class="row">
<div class="col-12 d-flex justify-content-center">
<div class="avatar avatar-xxl">
<img src="${MEMBERDATA.member_profile}"
class="avatar-img rounded-circle" alt="profile image" />
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="info-user pt-4 pb-4">
<h5>이름 : ${MEMBERDATA.member_name}</h5>
<h5>전화번호 : ${MEMBERDATA.member_phone}</h5>
<h5>이메일 : ${MEMBERDATA.member_id}</h5>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6 text-center">
<a href="CHANGEMEMBERPAGEACTION.do">
<button class="btn btn-secondary">회원정보 변경</button>
</a>
</div>
<div class="col-md-6 text-center">
<a href="DELETEMEMBERACTION.do">
<button class="btn btn-danger">회원 탈퇴</button>
</a>
</div>
</div>
</div>
</div>
</div>
<div class="row justify-content-center ">
<div class="col-md-12">
<div class="card card-stats card-round pt-3 px-5 pb-5">
<ul class="nav nav-tabs nav-line nav-color-secondary"
id="line-tab" role="tablist">
<li class="nav-item">
<a class="nav-link active"
id="line-post-tab" data-bs-toggle="pill" href="#line-post"
role="tab" aria-controls="pills-post" aria-selected="true">
내가 작성한 글 관리
</a>
</li>
<c:if test="${MEMBERDATA.member_role == 'T'}">
<li class="nav-item"><a class="nav-link" id="line-crew-tab"
data-bs-toggle="pill" href="#line-crew" role="tab"
aria-controls="pills-crew" aria-selected="false"> 신규 회원 관리 </a>
</li>
</c:if>
</ul>
<div class="tab-content mt-3 mb-3" id="line-tabContent">
<div class="tab-pane fade show active" id="line-post"
role="tabpanel" aria-labelledby="line-post-tab">
<table class="w-100">
<tbody>
<tr ><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<c:forEach var="board" items="${BOARD}">
<tr>
<td colspan=5><a href="BOARDONEPAGEACTION.do?board_num=${board.board_num}"
class="text-muted"> ${board.board_title} </a></td>
<td align="right">
<button class="btn btn-primary me-3"
onclick="location.href='BOARDUPDATAPAGEACTION.do?board_num=${board.board_num}'">수정</button>
<button class="btn btn-danger"
onclick="deleteBoard(${board.board_num})">삭제</button>
</td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
<div class="tab-pane fade" id="line-crew" role="tabpanel"
aria-labelledby="line-crew-tab">
<div class="row">
<div class="col-12 text-center">
<h3>신규 회원 목록</h3>
<c:forEach var="newmember" items="${MEMBER_LIST}">
<p>${newmember.member_name}</p>
</c:forEach>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- container end -->
</div>
<!-- Core JS Files -->
<script src="assets/js/core/jquery-3.7.1.min.js"></script>
<script src="assets/js/core/popper.min.js"></script>
<script src="assets/js/core/bootstrap.min.js"></script>
<script type="text/javascript">
function deleteBoard(boardNum){
if(confirm('정말 삭제하시겠습니까?')){
location.href='BOARDDELETEACTION.do?board_num='+boardNum;
}
}
</script>
</body>
</html>
더보기
post.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib tagdir="/WEB-INF/tags" prefix="mytag" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<!-- Fonts and icons -->
<script src="assets/js/plugin/webfont/webfont.min.js"></script>
<script src="https://kit.fontawesome.com/7f7b0ec58f.js"
crossorigin="anonymous"></script>
<!-- CSS Files -->
<link rel="stylesheet" href="assets/css/bootstrap.min.css" />
<link rel="stylesheet" href="assets/css/plugins.min.css" />
<link rel="stylesheet" href="assets/css/kaiadmin.css" />
</head>
<body>
<!-- GNB 커스텀 태그 -->
<mytag:gnb member_id="${MEMBER_ID}" ></mytag:gnb>
<!-- container start -->
<div class="container">
<div class="page-inner">
<div class="row py-3">
<div class="col-12">
<h1 class="text-center">${BOARD.board_title}</h1>
</div>
</div>
<div class="row border-bottom border-dark pb-3">
<div class="col-md-1 d-flex justify-content-end align-items-center">
<div class="avatar avatar-sm">
<img src="${member_profile}" alt="profile"
class="avatar-img rounded-circle" />
</div>
</div>
<div class="col-md-11 d-flex align-items-center">
<p class="mb-0">작성자: ${BOARD.board_writer_id}</p>
</div>
</div>
<div class="row py-5">
<div class="col-12 d-flex justify-content-center">
<div class="w-75">
<p class="text-start">${BOARD.board_content}</p>
</div>
</div>
</div>
<div class="row border-top border-dark py-3">
<form action="REPLYACTION.do" method="POST">
<input type="hidden" name="board_id" value="${BOARD.board_num}" />
<div class="row">
<div class="col-11">
<div class="form-group">
<input name="reply_content" type="text" class="form-control"
id="comment" placeholder="댓글를 입력해주세요" />
</div>
</div>
<div class="col-1 d-flex align-items-center">
<button type="submit" class="btn btn-secondary">댓글</button>
</div>
</div>
</form>
</div>
<c:forEach var="reply" items="${REPLY}">
<c:choose>
<c:when test="${not empty REPLY}">
<div class="row border-top border-bottom py-3 px-5 comment-item">
<div class="col-md-2">
<p>작성자: ${reply.reply_writer_id}</p>
</div>
<div class="col-md-9">
<form action="REPLYUPDATEACTION.do" method="POST">
<input type="hidden" name="board_id" value="${BOARD.board_num}" />
<input type="hidden" name="reply_id" value="${reply.reply_num}" />
<p class="comment-text">${reply.reply_content}</p>
<div class="edit-form d-none">
<div class="row">
<div class="col-8">
<input type="text" class="form-control comment-edit" name="reply_content"/>
</div>
<div class="col-4">
<a href="REPLYUPDATE.do">
<button type="submit" class="btn btn-primary save-edit">변경완료</button>
</a>
</div>
</div>
</div>
</form>
</div>
<div class="col-1">
<c:if test="${MEMBER_ID eq reply.reply_writer_id}">
<div class="dropdown">
<button class="btn btn-icon btn-clean me-0" type="button"
id="dropdownMenuButton" data-bs-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
<i class="fas fa-ellipsis-h"></i>
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
<a class="dropdown-item edit-comment">수정</a>
<a class="dropdown-item" href="REPLYDELETEACTION.do?replyId=${reply.reply_num}&board_num=${BOARD.board_num}">삭제</a>
</div>
</div>
</c:if>
</div>
</div>
</c:when>
</c:choose>
</c:forEach>
<!-- Core JS Files -->
<script src="assets/js/core/jquery-3.7.1.min.js"></script>
<script src="assets/js/core/popper.min.js"></script>
<script src="assets/js/core/bootstrap.min.js"></script>
<script>
// DOMContentLoaded 이벤트가 발생하면 콜백 함수를 실행
// 즉, DOM이 완전히 로드된 후에 이 코드가 실행
document.addEventListener('DOMContentLoaded', () => {
// .comment-item 클래스를 가진 모든 요소를 선택하여 comments라는 변수에 저장
const comments = document.querySelectorAll('.comment-item');
// 각 댓글(comment-item)에 대해 반복 작업을 수행합니다.
comments.forEach(comment => {
// 현재 댓글의 편집 버튼(.edit-comment)을 선택하여 editButton 변수에 저장
const editButton = comment.querySelector('.edit-comment');
// 현재 댓글의 편집 입력란(.comment-edit)을 선택하여 commentEdit 변수에 저장
const commentEdit = comment.querySelector('.comment-edit');
// 현재 댓글의 텍스트 영역(.comment-text)을 선택하여 commentText 변수에 저장
const commentText = comment.querySelector('.comment-text');
// 현재 댓글의 저장 버튼(.save-edit)을 선택하여 saveEditButton 변수에 저장
const saveEditButton = comment.querySelector('.save-edit');
// editButton 클릭 시 실행될 이벤트 리스너를 추가
editButton.addEventListener('click', (event) => {
// 기본 클릭 동작을 막습니다
event.preventDefault();
// commentText 요소에 'd-none' 클래스를 추가하여 댓글 텍스트를 숨기기
commentText.classList.add('d-none');
// 클릭된 버튼의 부모 요소(comment)를 기준으로 하위 요소들에 접근
const currentComment = event.currentTarget.closest('.comment-item');
// currentComment 내의 commentText에 'd-none' 클래스를 추가하여 댓글 텍스트를 숨기기
currentComment.querySelector('.comment-text').classList.add('d-none');
// currentComment 내의 editForm에서 'd-none' 클래스를 제거하여 편집 폼을 표시
currentComment.querySelector('.edit-form').classList.remove('d-none');
// commentEdit 입력란에 현재 댓글의 텍스트 내용을 설정
commentEdit.value = commentText.textContent.trim();
});
// saveEditButton 클릭 시 실행될 이벤트 리스너를 추가
saveEditButton.addEventListener('click', () => {
// commentEdit 입력란의 값을 commentText 요소에 설정하여 변경된 댓글을 표시
commentText.textContent = commentEdit.value;
// editForm 요소에 'd-none' 클래스를 추가하여 편집 폼을 숨기기
editForm.classList.add('d-none');
// commentText 요소에서 'd-none' 클래스를 제거하여 댓글 텍스트를 다시 표시
commentText.classList.remove('d-none');
});
});
});
</script>
</body>
</html>
'Project' 카테고리의 다른 글
[최종 프로젝트] 인텔리제이 사용과 깃 커밋 히스토리 관리 (1) | 2024.10.25 |
---|---|
중간 프로젝트 기능 발표를 위한 PPT 작업 (0) | 2024.09.25 |
쇼핑몰 프로그램을 MVC 패턴으로 구현하기 (0) | 2024.07.24 |
포켓몬 잡기 03 (0) | 2024.07.11 |
포켓몬 잡기 02 (2) | 2024.07.11 |