무작정 개발.Vlog

[ajax & Struts2] 방명록 만들기

by 무작정 개발
반응형
2022.03.18(59일 차_금요일)

 

이번에는 Struts 2 프레임워크랑 ajax를 사용하여 방명록 게시판을 만들 것이다.

최종 완성 화면은 맨 하단에 첨부하였다.

 

 

DB 테이블 생성
create 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));

iguest테이블 생성
iguest테이블 생성

 


GuestDTO 클래스 생성

package 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

<?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에 등록하기

<sqlMap resource="com/util/sqlMap/iguest_sqlMap.xml"/>

 


 

struts-iguest.xml 생성 및 등록

  • struts-iguest.xml을 생성했으면 struts.xml에 등록해줘야 한다.
  • 컨트롤러 역할
  • 이번에는 ajax를 써서 redirect가 필요 없다.

 

struts-iguest.xml 

<?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에 등록하기

<include file="struts-iguest.xml"/>

 


 

GuestAction 생성하기

 

package 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 메서드(자바스크립트로 페이징 처리) 생성
package 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>&nbsp;");
			}
			// 바로가기 페이지  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>&nbsp;");
					//<font color="Fuchsia">9</font>
				} else {
					// 같지않으면
					// 7 페이지면?
					//<a href="list.jsp?pageNum=7">7</a>&nbsp;
					sb.append("<a href=\"" + listUrl + "pageNum=" + page + 
							"\">" + page + "</a>&nbsp;");
				}		
				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>&nbsp;
			sb.append("<a href=\"" + listUrl + "pageNum=" + 
					page + "\">다음▶</a>&nbsp;");
		}
		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>&nbsp;";
		}
		
		// 페이지
		page = currentPageSetup + 1;
		while((page<=totalPage) && (page<=currentPageSetup + numPerBlock)) {
			
			if(page==currentPage) {
				strList += "<font color='Fuchsia'>" + page + "</font>&nbsp;";
			} else {
				// 모든 페이지작업을 list한테 넘긴다.
				strList += "<a onclick='listPage(" + page +");'>" +
						page +"</a>&nbsp;";
			}
			page++;
		}
		// 다음 ▶
		if(totalPage-currentPageSetup>numPerBlock) {
			strList += "<a onclick='listPage(" + page + ");'>다음▶</a>&nbsp;";
		}
		return strList;
	}
	
}

 


 

JSP 페이지 생성하기 - (list.jsp , guest.jsp)

 

guest.jsp - (방명록 작성 / 조회 페이지)

<%@ 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

<%@ 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 }.&nbsp;&nbsp;${dto.name }
		(<a href="mailto:${dto.email }">${dto.email }</a>)</b>
	</td>
	<td width="300" align="right" style="padding-right: 10px;">
		${dto.created }&nbsp;
		<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>

 

실행화면

실행 화면

반응형

블로그의 정보

무작정 개발

무작정 개발

활동하기