[ajax & Struts2] 방명록 만들기
by 무작정 개발반응형
2022.03.18(59일 차_금요일)
이번에는 Struts 2 프레임워크랑 ajax를 사용하여 방명록 게시판을 만들 것이다.
최종 완성 화면은 맨 하단에 첨부하였다.
DB 테이블 생성
sqlcreate table iguest( num NUMBER(9), name VARCHAR2(25), email VARCHAR2(50), content VARCHAR2(4000), ipAddr VARCHAR2(50), created DATE, CONSTRAINT pk_iguest_num PRIMARY KEY(num));

GuestDTO 클래스 생성
javapackage com.iguest; public class GuestDTO { private int listNum;//일련번호 private int num; private String name; private String email; private String content; private String ipAddr; // 아이피는 db에 저장 클라이언트에 보여줄필요는없다. (누가접근했는지알기위해) private String created; private String pageNum; // 부수적으로 만듬 //getter/setter 생성 }
iguest_sqlMap.xml 생성 - (DAO 역할) & 등록하기
- Struts랑 iBatis xml을 만들었으면 xml을 등록해줘야 한다.
iguest_sqlMap.xml
html<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd"> <sqlMap namespace="iguest"> <!-- numMax : 게시물 번호 --> <select id="numMax" resultClass="int"> select nvl(max(num),0) from iguest </select> <!-- insertData : 방명록 추가 --> <insert id="insertData" parameterClass="com.iguest.GuestDTO"> insert into iguest (num,name,email,content,ipAddr,created) values (#num#,#name#,#email#,#content#,#ipAddr#,sysdate) </insert> <!-- dataCount : 게시물 갯수 --> <select id="dataCount" resultClass="int"> select nvl(count(*),0) from iguest </select> <!-- listData : 게시물 리스트 출력 --> <select id="listData" resultClass="com.iguest.GuestDTO" parameterClass="map" > select * from ( select rownum rnum, data.* from ( select num,name,email,content,ipAddr,created from iguest order by num desc) data) <![CDATA[ where rnum>=#start# and rnum<=#end# ]]> </select> <!-- deleteData : 게시물 삭제 --> <delete id="deleteData" parameterClass="int"> delete iguest where num=#num# </delete> </sqlMap>
★ Action에서 namespace로 id에 접근한다. ★
sqlMapConfig.xml에 등록하기
html<sqlMap resource="com/util/sqlMap/iguest_sqlMap.xml"/>
struts-iguest.xml 생성 및 등록
- struts-iguest.xml을 생성했으면 struts.xml에 등록해줘야 한다.
- 컨트롤러 역할
- 이번에는 ajax를 써서 redirect가 필요 없다.
struts-iguest.xml
html<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <!-- 컨트롤러 --> <!-- ajax를 써서 리다이렉트가 필요 x --> <struts> <package name="iguest" extends="default" namespace="/guest"> <action name="guest"> <result>/iguest/guest.jsp</result> </action> <action name="created" class="com.iguest.GuestAction" method="created"> <result>/iguest/list.jsp</result> </action> <action name="list" class="com.iguest.GuestAction" method="list"> <result>/iguest/list.jsp</result> </action> <action name="deleted" class="com.iguest.GuestAction" method="deleted"> <result>/iguest/list.jsp</result> </action> </package> </struts>
struts.xml에 등록하기
html<include file="struts-iguest.xml"/>
GuestAction 생성하기
javapackage com.iguest; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; import com.opensymphony.xwork2.Preparable; import com.util.MyUtil; import com.util.dao.CommonDAO; import com.util.dao.CommonDAOImpl; //Struts2는 DTO가 데이터를 넘기지만 Struts2+ajax로 만들 때는 ajax가 데이터를 넘긴다. //Struts2가 데이터를 넘길 필요가 없어진다.그래서 dto의 getter가 필요없다. public class GuestAction extends ActionSupport implements Preparable,ModelDriven<GuestDTO> { private static final long serialVersionUID = 1L; private GuestDTO dto; //데이터는 ajax가 넘기기때문에 이건 필요없당 /*public GuestDTO getDto() { return dto; }*/ @Override public GuestDTO getModel() { return dto; } @Override public void prepare() throws Exception { dto = new GuestDTO(); } public String created() throws Exception { HttpServletRequest request = ServletActionContext.getRequest(); CommonDAO dao = CommonDAOImpl.getInstance(); int numMax = dao.getIntValue("iguest.numMax"); dto.setNum(numMax + 1); dto.setIpAddr(request.getRemoteAddr()); dao.insertData("iguest.insertData", dto); return list(); //반환값은 직접찾아가고자하는 list() 메서드를 써주면 된다. } public String list() throws Exception { HttpServletRequest request = ServletActionContext.getRequest();//request를 가져온것 // DB연결자 CommonDAO dao = CommonDAOImpl.getInstance(); MyUtil myUtil = new MyUtil(); //페이징 처리 int numPerPage = 3;//하나의 페이지에 표시할 데이터 갯수 int totalPage = 0; int totalDataCount = 0; String pageNum = request.getParameter("pageNum"); int currentPage = 1; //currentPage는 1페이지를 뿌린다. if(pageNum!=null && !pageNum.equals("")) { currentPage = Integer.parseInt(pageNum); } totalDataCount = dao.getIntValue("iguest.dataCount");//ModelDriven이 움직여준다. if(totalDataCount!=0) { totalPage = myUtil.getPageCount(numPerPage, totalDataCount); } if(currentPage>totalPage) { //삭제했을때 마지막페이지를 현재 페이지로 맞춰줌 currentPage = totalPage; } Map<String, Object> hMap = new HashMap<String, Object>(); //start와 end는 게시물의 시작과 끝을 뜻함 int start = (currentPage-1)*numPerPage+1; int end = currentPage*numPerPage; hMap.put("start", start);//hMap에 start와 end를 넣은것 hMap.put("end", end); //iguest의 listsData를 실행시키는데 hMap을 넘겨준다. List<Object> lists = dao.getListData("iguest.listData", hMap); //일련번호 정렬 int listNum,n = 0; Iterator<Object> it = lists.iterator(); while(it.hasNext()) { GuestDTO vo = (GuestDTO)it.next(); listNum = totalDataCount-(start+n-1); vo.setListNum(listNum); vo.setContent(vo.getContent().replaceAll("\n", "<br/>")); n++; } //JS로 만든 페이징 처리 호출 String pageIndexList = myUtil.pageIndexList(currentPage, totalPage); request.setAttribute("lists", lists); request.setAttribute("pageIndexList", pageIndexList); request.setAttribute("totalDataCount", totalDataCount); request.setAttribute("pageNum", currentPage); return SUCCESS; // select는 리다이렉트가 필요없기때문에 SUCCESS값만 반환하면 된다. // insert/delete는 리다이렉트가 필요 } public String deleted() throws Exception { HttpServletRequest request = ServletActionContext.getRequest(); CommonDAO dao = CommonDAOImpl.getInstance(); int num = Integer.parseInt(request.getParameter("num")); dao.deleteData("iguest.deleteData", num); return list();//반환값은 직접찾아가고자하는 list() 메서드를 써주면 된다. } }
MyUtil 클래스 생성 - (페이징 처리)
- 기존에 만들었던 MyUtil 클래스에 pageIndexList 메서드(자바스크립트로 페이징 처리) 생성
javapackage com.util; public class MyUtil { // currentPageSetup page numPerBlock //전체페이지 public int getPageCount(int numPerPage,int dataCount) { // 3 34 // numPerPage : 한 페이지에 몇개의 데이터 // 전체 데이터 개수와 몇개를 뿌릴건지 // 34개와 하나의 페이지에 몇개를 뿌릴건지 int pageCount = 0; // 전체 데이터 개수를 하나의 페이지에 몇개를 뿌릴건지 나눔 pageCount = dataCount / numPerPage; // 페이지 몇장이 필요한지 알아냄 if(dataCount % numPerPage != 0) { // 34 % 3 == 0으로 떨어지지않으면 페이지 하나더 필요 pageCount++; } return pageCount; // 전체 페이지 개수 } // 페이지 처리 메소드 // 현재 내가 어떤페이지를 보고있는지 , 전체페이지 몇장 , list.jsp public String pageIndexList(int currentPage, int totalPage, String listUrl) { int numPerBlock = 5; // 5개 표시 ◀이전 6 7 8 9 10 다음▶ int currentPageSetup; // ◀ 내가 이전을 누르게되면 5페이지가 나옴 (왼쪽 화살표 값) int page; // for문의 i 6 7 8 9 10 StringBuffer sb = new StringBuffer(); if(currentPage == 0 || totalPage == 0) { // 현재페이지또는 전체페이지 0이면 null 반환 return "null"; } // list.jsp?pageNum=2 이거나 // list.jsp 오면서 ?검색했을때 있으면 // list.jsp? searchKey=subject & searchValue=aa & pageNum=2 // 로 String listUrl로 매개변수로 오게됨 // ? 가 있는지 찾아본다. if(listUrl.indexOf("?") != -1) { // ?가 있으면 listUrl = listUrl + "&"; } else { // 물음표가 없으면 물음표를 붙임 listUrl = listUrl + "?"; } // ◀이전 6 7 8 9 10 다음▶ // 현재 8페이지를 5개씩으므로 numPerBlock = 5 하면 나머지 3에 5를 곱합 // 5 = 1 * 5 // 1 2 3 4 5 다음▶ // ◀이전 6 7 8 9 10 다음▶ // ◀이전 값을 구하는 공식 // ◀이전값 5 6 5 5 currentPageSetup = (currentPage/numPerBlock)*numPerBlock; // 10 5 if(currentPage % numPerBlock == 0) { // 10으로 나눴을때 0이므로 // 5 5 5 currentPageSetup = currentPageSetup - numPerBlock; // 10이여도 5가 나옴 } // 위에 공식으로 5가 들어가면 ◀이전 // ◀이전 // 12 5 뒤에 조건 5 이면 ◀이전 만들어라. // //◀이전 필요하면 한번만 만들고 필요하지않으면 안만듬 // 15 5 6 if(totalPage > numPerBlock && currentPageSetup > 0) { // 조건에 만족하면 ◀이전 만듬 //<a href="list.jsp?pageNum=2>◀이전</a>"를 만드는 코딩 sb.append("<a href=\"" + listUrl + "pageNum=" + currentPageSetup + "\">◀이전</a> "); } // 바로가기 페이지 6 7 8 9 10 // 5개찍어야함 // page = 현재페이지 // 6 5 page = currentPageSetup + 1; // 시작값 ◀이전 부터 6,7,8,9,10 // 6 12 6 5(◀이전값) 5 while(page <= totalPage && page <= (currentPageSetup + numPerBlock)) { // 만족하면 6부터 10까지 찍힘 // 현재 페이지가 내가 보고있는 페이지랑 같으면 색깔로 지정 if(page == currentPage) { sb.append("<font color=\"Fuchsia\">" + page + "</font> "); //<font color="Fuchsia">9</font> } else { // 같지않으면 // 7 페이지면? //<a href="list.jsp?pageNum=7">7</a> sb.append("<a href=\"" + listUrl + "pageNum=" + page + "\">" + page + "</a> "); } page++; // 6 7 8 9 10 찍음 } // 다음▶ // ◀이전 6 7 8 9 10 다음▶ // ◀이전 11 12 //(위에꺼) 12 5 5 다음버튼생김 //(밑에꺼) 35 10 5 다음버튼안생김 if(totalPage - currentPageSetup > numPerBlock) { //<a href="list.jsp?pageNum=11">다음▶</a> sb.append("<a href=\"" + listUrl + "pageNum=" + page + "\">다음▶</a> "); } return sb.toString(); } //------------------------------------------------------------------------------------------------------- // 자바스크립트로 페이징 처리 하기 // listUrl이 필요없다. public String pageIndexList(int currentPage, int totalPage ) { int numPerBlock = 5; int currentPageSetup; int page; String strList = ""; if(currentPage==0) { return ""; } //표시할 첫 페이지 // 이전페이지 구함 currentPageSetup = (currentPage/numPerBlock) * numPerBlock; // 5 % 5 == 0 if(currentPage % numPerBlock == 0) { currentPageSetup = currentPageSetup - numPerBlock; } // ◀ 이전 if(totalPage > numPerBlock && currentPageSetup>0) { strList = "<a onclick='listPage(" + currentPageSetup + ");'>◀이전</a> "; } // 페이지 page = currentPageSetup + 1; while((page<=totalPage) && (page<=currentPageSetup + numPerBlock)) { if(page==currentPage) { strList += "<font color='Fuchsia'>" + page + "</font> "; } else { // 모든 페이지작업을 list한테 넘긴다. strList += "<a onclick='listPage(" + page +");'>" + page +"</a> "; } page++; } // 다음 ▶ if(totalPage-currentPageSetup>numPerBlock) { strList += "<a onclick='listPage(" + page + ");'>다음▶</a> "; } return strList; } }
JSP 페이지 생성하기 - (list.jsp , guest.jsp)
guest.jsp - (방명록 작성 / 조회 페이지)
html<%@ page contentType="text/html; charset=UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <% request.setCharacterEncoding("UTF-8"); String cp = request.getContextPath(); %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Ajax 방명록</title> <link rel="stylesheet" type="text/css" href="<%=cp %>/data/css/style.css"/> <script src="https://code.jquery.com/jquery-3.6.0.js"></script> <script type="text/javascript"> $(function(){ listPage(1); /* 1페이지를 호출한다. */ }); $(document).ready(function(){ $("#sendButton").click(function() { var params = "name=" + $("#name").val() + "&email=" + $("#email").val() + "&content=" + $("#content").val(); $.ajax({ type:"POST", /* 가상의 주소를 적어줌 */ url:"<%=cp%>/guest/created.action", data:params, //애는 insert하고 list돌아왔을때 실행하는함수 success:function(args) { /* 함수성공하면 데이터는 args로 들어옴 그러면 listData에 뿌려줘야함*/ $("#listData").html(args); /* null로 바꿔줘야지 데이터가 지워진다. */ /* 아작스는 새로고침이 안되기 때문에 */ /* 코딩으로 삭제해줘야한다. */ $("#name").val(""); $("#email").val(""); $("#content").val(""); $("#name").focus(); }, beforeSend:showRequest, // false가 오면 실행안됨 true가 와야 실행된다. //갔다와서 에러가 나면 error:function(e) { alert(e.responseText); } }); }); }); function showRequest() { /* 제이쿼리 함수 양쪽의 공백을 제거 */ var name = $.trim($("#name").val()); // name의 값을 양쪽공백 제거후 name에 넣음 var email = $.trim($("#email").val()); // name의 값을 양쪽공백 제거후 name에 넣음 var content = $.trim($("#content").val()); // name의 값을 양쪽공백 제거후 name에 넣음 if(!name) { alert("\n이름을 입력하세요"); $("#name").focus(); return false; } if(!email) { alert("\n이메일을 입력하세요"); $("#email").focus(); return false; } if(!content) { alert("\n내용을 입력하세요"); $("#content").focus(); return false; } if(content.length>=200) { alert("\n내용은 200자 까지만 가능합니다."); $("#content").focus(); return false; } // 다 체크해서 통과했으면 true가 돌아가야지 계속 실행됨 return true; } function listPage(page){ // 1 이 오게됨 var url = "<%=cp%>/guest/list.action"; //리스트가 왔을때 실행될 함수(비동기 메서드) -갔다오면 args에 데이터가 담겨서온다. //POST 방식은 json- param $.post(url,{pageNum:page},function(args){ //json방식으로 넘긴 pageNum을 GuestAction에서 받아준다. $("#listData").html(args); }); $("#listData").show();//display: none;" 밑에서 이렇게 하는 이유는 //문제가 생겼을때 데이터를 안보여줄려고 none 대신 block를 쓰고 //이 줄을 주석처리해도 된다. } function deleteData(num,page) { var url = "<%=cp%>/guest/deleted.action"; //삭제하고 리스트 왔을때 실행될 함수(비동기 메서드) $.post(url,{num:num,pageNum:page},function(args) { $("#listData").html(args); }); } </script> </head> <body> <br/><br/> <table width="700" border="2" cellpadding="0" cellspacing="0" bordercolor="#00D8FF" style="margin: auto;"> <tr height="40"> <td style="padding-left: 20px;"><b>방명록</b></td> </tr> </table> <!-- 겉모습 만듬 --> <br/><br/> <table width="700" border="0" cellpadding="0" cellspacing="0" style="margin : auto;"> <tr> <!-- colsplan 4칸합침 선 --> <td width="600" colspan="4" height="3" bgcolor="#00D8FF"></td> </tr> <tr> <td width="60" height="30" bgcolor="#00D8FF" align="center">작성자</td> <td width="240" height="30" style="padding-left: 10px;"> <input type="text" id="name" size="35" maxlength="20" class="boxTF"/> </td> <td width="60" height="30" bgcolor="#00D8FF" align="center">이메일</td> <td width="240" height="30" style="padding-left: 10px;"> <input type="text" id="email" size="35" maxlength="50" class="boxTF"/> </td> </tr> <tr> <td width="600" colspan="4" height="1" bgcolor="#00D8FF"></td> </tr> <tr> <td width="60" height="30" bgcolor="#00D8FF" align="center">내용</td> <td width="540" colspan="3" style="padding-left: 10px;"> <textarea rows="3" cols="90" class="boxTA" style="height: 50px;" id="content"></textarea> </td> </tr> <tr> <td width="600" colspan="4" height="1" bgcolor="#00D8FF"></td> </tr> <tr> <td width="600" colspan="4" height="30" align="right" style="padding-right: 15px;"> <input type="button" value="등록하기" class="btn2" id="sendButton"/> <!-- 제이쿼리로 읽어내기 위해서 id --> </td> </tr> </table> <br/> <span id="listData" style="display: none;"></span> <!-- none 안보임 --> </body> </html>
list.jsp
html<%@ page contentType="text/html; charset=UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <% request.setCharacterEncoding("UTF-8"); String cp = request.getContextPath(); //여긴 html소스가 필요가없다. 왜냐하면 list.jsp에는 ajax로 데이터를 뿌려주기때문 %> <c:if test="${totalDataCount!=0 }"> <table width="700" border="0" cellpadding="0" cellspacing="0" style=" margin: auto;"> <c:forEach var="dto" items="${lists }"> <tr> <td colspan="2" bgcolor="#00D8FF" height="1"></td> </tr> <tr height="30" bgcolor="#00D8FF"> <td width="300" style="padding-left: 10px;"> <b>No ${dto.listNum }. ${dto.name } (<a href="mailto:${dto.email }">${dto.email }</a>)</b> </td> <td width="300" align="right" style="padding-right: 10px;"> ${dto.created } <a href="javascript:deleteData('${dto.num }','${pageNum }');">삭제</a> </td> </tr> <tr> <td height="50" style="padding-left: 10px" colspan="2"> ${dto.content } </td> </tr> </c:forEach> <tr> <td colspan="2" bgcolor="#00D8FF" height="2"></td> </tr> <tr height="30"> <td align="center" colspan="2">${pageIndexList }</td> </tr> </table> </c:if>
실행화면

반응형
블로그의 정보
무작정 개발
무작정 개발