버튼에 onclick으로 연결된 메서드 confirmDelete() : - 삭제버튼을 누르면, "게시글을 삭제하시겠습니까?" 라는 확인 alert이 발생한다. - [확인]을 누르면 DeleteGET 컨트롤러로 연결되고, 취소를 누르면 return 되어 아무일도 일어나지 않는다.
[확인]을 눌렀을 때 DeleteGET으로 해당 게시글의 정보(post_num, page)를 전달한다.
1. 게시판 List에서 기본 페이지네이션 구현하기 [1] 2. 원래 있던 페이지 번호로 돌아가기 [2] - 게시글 상세에서 [목록]버튼 눌렀을 때 - 게시글 수정 후 [목록]버튼 눌렀을 때 - 게시글 삭제 후
게시판 List에서 페이지네이션 활성화시키기
Criteria.java -- 페이지네이션 출력 기본 설정 관리
publicclassCriteria{
privateint page; // 현재 페이지 번호privateint postsPerPage; // 한 페이지 당 게시글 수//Criteria 디폴트 생성자publicCriteria(){
this.page = 1; // 현재 페이지를 1페이지로 설정this.postsPerPage = 10; // 한 페이지 당 게시글 수 10개로 설정
}
//getter and setterpublicintgetPage(){
return page;
}
publicvoidsetPage(int page){
if(page <= 0) { // 현재 페이지 번호가 음수라면,this.page = 1; // 페이지 번호 1로 설정
}
elsethis.page = page;
}
publicintgetPostsPerPage(){
return postsPerPage;
}
publicvoidsetPostsPerPage(int postsPerPage){
if(postsPerPage <=0 || postsPerPage > 100) { // 페이지 당 게시글 수가 0개 이하거나 100개를 초과할 때this.postsPerPage = 10; // 페이지 당 게시글 수를 10개로 설정.
}
elsethis.postsPerPage = postsPerPage;
}
@Override//Criteria 정보 콘솔 출력 메서드public String toString(){
return"Criteria [page=" + page + ", postsPerPage=" + postsPerPage + "]";
}
//쿼리문에서 limit에 사용되는 인덱스를 계산하는 getterpublicintgetPageStart(){
return (this.page -1) * postsPerPage;
}
}
마지막 getter -- getPageStart()는 아래 BoardMapper.xml에서 활용되니 거기서 설명.
PageMaker.java -- 페이지네이션 출력 상세 설정 관리
import lombok.Data;
@DatapublicclassPageMaker{
privateint totalCount; // 총 게시글 수privateint startPage; // 시작 번호privateint endPage; // 끝 번호privateboolean prev; // 이전 버튼 유무privateboolean next; // 다음 버튼 유무privateint displayPageNum; // 페이지네이션 한 세트에 보여질 페이지번호 갯수private Criteria criteria; // 페이지네이션 기본 설정 정보//endPage, startPage, prev, next 값을 계산하는 메서드publicvoidcalcData(){
endPage = (int) (Math.ceil(criteria.getPage()/(double) displayPageNum)*displayPageNum);
//endPage = (int)(Math.ceil(3/(double)10) * 10)
startPage = (endPage - displayPageNum)+1;
//총 게시글 개수를 이용하여 제일 마지막 페이지 번호를 계산int theLastPage = (int)(Math.ceil(totalCount/(double)criteria.getPostsPerPage()));
if(endPage > theLastPage) {
endPage = theLastPage;
}
//현재 페이지가 1페이지면 이전(prev)버튼이 없어야 함
prev = startPage == 1 ? false : true;
//현재 페이지에 마지막 게시글이 포함되어 있으면 다음(next)버튼이 없어야 함
next = endPage * criteria.getPostsPerPage() >= totalCount ? false:true;
}
}
EX ) 현재 페이지가 13페이지이고, 페이지네이션 한 세트에 보여지는 페이지 번호 수가 10일 때 ==> startPage = 11 , endPage = 20 이어야 함.
<select id="getBoardList" resultType="com.ysy.bakingdom.vo.PostVO">select*from post where post_state ='1'orderby post_num desc<!-- 불러올 게시글 개수 설정 -->
limit #{cri.pageStart}, #{cri.postsPerPage}
</select>
limit #{cri.pageStart}, #{cri.postsPerPage} : 전체 데이터 값에서 "pageStart+1부터 postsPerPage까지" 불러옴 cri.pageStart : Critera.java 에 있는 인덱스 계산 getter의 리턴 값 => return (this.page -1) * postsPerPage;
EX) 만약 한 페이지당 게시글 수가 3개 라고 가정하면,
BoardMapper.xml -- 전체 게시글 수를 구하는 쿼리문 작성
<select id="getTotalCount" resultType="int">selectcount(*) from post where post_state ='1'</select>
<navaria-label="Page navigation example"><ulclass="pagination d-flex justify-content-center"><!-- 이전 버튼 눌렀을 때 --><c:iftest="${pm.prev}"><liclass="page-item"><aclass="page-link"href="<%=request.getContextPath()%>/noticeBoard/noticeList?page=${pm.startPage-1}"aria-label="Previous"><spanaria-hidden="true">«</span><!-- « = "<<" --></a></li></c:if><!-- 페이지 번호를 눌렀을 때 --><c:forEachbegin="${pm.startPage}"end="${pm.endPage}"var="index"><liclass="page-item <c:if test="${pm.criteria.page == index}">active</c:if>">
<aclass="page-link"href="<%=request.getContextPath()%>/noticeBoard/noticeList?page=${index}">${index}</a></li></c:forEach><!-- 다음 버튼 눌렀을 때 --><c:iftest="${pm.next}"><liclass="page-item"><aclass="page-link"href="<%=request.getContextPath()%>/noticeBoard/noticeList?page=${pm.endPage+1}"aria-label="Next"><spanaria-hidden="true">»</span><!-- » = ">>" --></a></li></c:if></ul></nav>
<c:if test="${ [pm.prev | pm.next] }"> </c:if> - [pm.prev | pm.next] 가 ture이면 [ 이전 | 다음 ] 버튼 활성화.
<c:forEach begin="${pm.startPage}" end="${pm.endPage}" var="index"> - begin(시작페이지) 부터 end(끝페이지)까지 (PageMaker.java 참고) 를 반복문으로 변수 "index"에 담고, 쭉 출력(나열)한다.
현재 페이지 번호와 index가 같은 페이지네이션 번호는 active(파란 색 표시)를 활성화 시킨다.
작업완료 결과 http://localhost:8080/noticeBoard/noticeList 페이지네이션 한 세트에 2개의 페이지가 설정되고, 한 페이지 당 게시글 5개씩 보여짐.
여기까지가 기본 페이지네이션 구현이다.
다음 포스트는..
2. 원래 있던 페이지 번호로 돌아가기 - 게시글 상세에서 [목록]버튼 눌렀을 때 - 게시글 수정 후 [목록]버튼 눌렀을 때 - 게시글 삭제 후
user데이터가 있으면 (로그인이 가능하면), 세션에 user데이터를 담아 저장한다. === 자동로그인에 체크되어 있을 때 ==
useCookie 값이 null이 아니면 (로그인 할 때 자동로그인에 체크했으면) 새로운 쿠키(autologincookie)를 생성하여 현재 세션에 저장되어 있는 세션아이디를 쿠키에 담는다.
모든 URL범위에서 쿠키를 전송할 수 있도록 경로를 설정해준다. (setpath)
저장된 쿠키의 유효기간을 설정해준다. - 30일동안 유효 : 60초 60분 24시간 * 30일 => 60*60*24*30 - currentTimeMillis()는 long 타입으로 값을 반환한다. 여기에 60*60*24*30*1000을 연산하여 값을 보내주면 int값으로 계산되어 int타입을 벗어나 오버플로우가 발생한다. 따라서 1000L로 계산하여 연산 값을 long타입으로 만들어줘야한다. (int 범위 : –2,147,483,648 ~ 2,147,483,647 , 60*60*24*30*1000 =2,592,000,000)
response.addCookie에 쿠키데이터를 실어서 응답.
아까 실려온 user데이터에서의 email과 세션데이터에 담겨져 있는 세션id, 그리고 오늘날짜로부터의 유효기간을 담아 Service에게 keepLogin을 시킨다.
UserService
1
void keepLogin(String user_email, String user_sessionId, Date user_sessionLimit);
만약 세션에 담겨진 "user" 정보가 없으면 (= 로그인되지 않은 상태 or 세션유효기간이 만료되어 자동로그인이 종료된 상태 or 자동로그인이 아닌 상태) 해당 user의 로그인 관련 쿠키 상태를 확인하기 위해, request에서 "autoLoginCookie"를 가져온다.
가져올 autoLoginCookie 정보가 없으면 (autoLoginCookie==null) 인터셉터 작동 종료.
가져올 autoLoginCookie가 있으면, 해당 Cookie 값(세션아이디)을 가지는 유저 데이터를 찾아 VO user에 저장한다. 유저 데이터가 정상적으로 존재하면 세션 정보를 user로 설정한다.
※ 개발연습생의 설정 방법이므로, 내용이 정확하지 않거나 자세하지 않을 수 있습니다! (또륵)
MySQL에 담겨있는 나의 DB들을 다른 PC나 장소에서도 똑같이 동기화 하고 공유할 수 있으면 정말 좋겠지만...
간단한 연결이나 동기화는 아마도 보안적인 부분에서 이슈가 생기기 쉬울테니까? 이렇게 하는 거겠지? 하는 연습생의 스치는 생각은 버려두고....,
아무튼 노트북과 데스크탑을 옮겨다니며 연습생활을 하고 있는데, 각 기기에 DB동기화가 안되니까 export-import를 통해 직접 파일을 넘겨주며 연결해줘야하는게 너무너무너무너무너무 불편했다.
그래서 다른 PC에서도 실시간(?)으로 DB를 사용하고 작업할 수 있는 방법에 대해 구글링을 하다보니 원격접속이라는게 있었다! 몇가지 블로그들을 찾아보니 커맨드창에서 직접 쿼리문을 이용하여 원격접속 설정을 하는 것이 정석인 것 같다.
그런데 나는 커맨드창을 좋아하지 않는다. (아니, 커맨드창이 날 좋아하지 않는다.)
그래서 보기 좋고 설정하기 편한 MySQL 워크벤치를 이용해서 원격 접속을 설정하였다!
준비물
서버PC (DB를 공유해줄 PC) | 접속PC (DB를 공유받을 PC)
서버PC의 IP주소만 제대로 알고 있으면, 인터넷 환경이 같지 않아도 된다. (나 같은 경우 서버PC는 유선 환경ㅡ즉 본체에 랜선 장착 중, 그리고 접속PC는 무선 환경ㅡ즉 Wifi로 연결중이었다!)
각 PC마다 워크벤치가 설치되어 있어야한다.
서버PC가 전원이 켜져있어야지만 접속PC로 DB를 다룰 수 있다.
1. 서버PC의 Workbench에서 root계정으로 접속 한 뒤, 새로운 계정을 생성한다.
워크벤치 좌측의 Users and Privileges - Add Account 클릭
Login Name : 새로운 계정명. 아무거나 자유롭게
Limit to Hosts Matching : 접근 가능한 IP를 제한해주는 부분. - % : 모~~든 IP에서 접근 가능하다. - 특정 IP주소 : 기입한 IP주소로만 접근이 가능하다. (예를들어 내 DB에 접근 할 수 있는건 내 노트북 뿐이다 -> 내 노트북의 IP주소를 기입)
비밀번호를 자유롭게 입력하고 우측 하단의 Apply 클릭하여 계정 생성을 완료한다.
2. 접근허용할 스키마를 선택한 후, 스키마에 대한 새계정의 권한을 설정한다.
좌측에 생성된 계정을 클릭하고, 우측에서 Schema Privileges 탭을 선택 - Add Entry 클릭
접근허용할 스키마(테이블)를 설정할 수 있다.
All Schema : 현재 root 계정에 있는 모든 DB에 접근할 수 있게 한다. - 지금 설정은 모두 프로그래밍 연습을 위한 접속이므로 일단 어느 컴에서나 모든 DB를 다 관리할 수 있게 All Schema를 선택했다. 특정 스키마만 필요한 부분이라면 Selected schema 에서 선택할 수 있다.
추가된 스키마 목록을 클릭하고 , 하단에서 새로운 계정에서 사용가능한 DDL, DML 권한을 설정한다. - 이 부분 또한 연습을 위한 목적이므로 모든 권한을 부여하도록 Select "ALL"을 클릭하여 적용해주었다.
3. 접속할 PC에서 워크벤치를 켜고, 새로운 연결을 등록해준다.
워크벤치의 메인화면에서 캡쳐에 표시된 +를 누르고 Setup New Connection창을 열어준다. - Connection Name : 연결명. 아무거나 알아보기 쉽게 입력해준다. - Hostname : 서버 PC의 IP주소를 적어준다.
커맨드에서 ipconfig 를 입력면 나오는 정보 중, IPv4 주소에 적힌 192.xxx.xxx.xxx 형식의 IP주소를 적어주면 된다.
Username : 아까 서버PC에서 만들어둔 새로운 계정이름을 입력한다.
Store in Vault ... 를 클릭하고 새로운 계정의 비밀번호를 입력하고 OK - OK !
여기서 연결이 성공했다면, 만들어둔 Connection으로 접속하면 서버PC에서 설정한대로 스키마들이 들어와있고, 관리할 수 있다.
연결에 성공한 접속 PC에서 DB를 만지면, 만진 내용이 바로 서버PC에서도 동기화 되어 있는 것을 알 수 있다!