Dev/JS & Jquery

Ajax - 방명록 만들기

창문닦이 2019. 4. 3. 17:55

 

새로고침(Reload) 하지않아도 글을 작성 시 밑에 조회가 되도록 구현해보자.

개발 환경 셋팅

톰캣 7.0, 이클립스, 오라클, JDK 7.x, Struts2, Spring2.5, iBatis

구현 순서

1. [DB] 사용할 테이블 생성 - iguest

2. DTO 생성 - GuestDTO.class

3. 페이징 처리를 위한 클래스 생성 - MyUtil.class

4. [jsp 페이지 생성] 방명록 작성/조회 페이지 - guest.jsp

5. [jsp 페이지 생성] DB에 접근하여 방명록이 작성되어 div태그로 작성되는 페이지 - list.jsp

6. [iBatis] iguest_sqlMap.xml 작성

7. [iBatis] sqlMap_config.xml에 resource 등록 (iguest_sqlMap.xml)

8. Action클래스 생성

9. [struts2] struts환경설정 - struts-iguest.xml 생성 및 등록

10. 스트럿츠가 아닌 스프링에서 객체 생성시 - action-Context.xml

1. DataBase - 사용할 iguest 테이블 생성

 

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));

 

2. DTO생성 - GuestDTO.class

package com.iguest;
public class GuestDTO {
private int listNum;
private int num;
private String name;
private String email;
private String content;
private String ipAddr;
private String created;
private String pageNum;
getter/setter 작성
}

3. 페이징 처리를 위한 클래스 생성 - MyUtil.class

package com.util;
import org.springframework.stereotype.Service;
@Service("myUtil")
public class MyUtil {
//전체페이지 수 구하기
//numPerPage : 한 화면에 표시할 데이터 갯수
//dataCount : 전체 데이터 갯수
public int getPageCount(int numPerPage, int dataCount){
int pageCount = 0;
pageCount = dataCount / numPerPage;
if(dataCount % numPerPage != 0)
pageCount++;
return pageCount;
}
//페이징 처리
//currentpage : 현재 표시할 페이지
//totalPage : 전체 페이지 수
//listUrl : 링크를 설정한 URL
//자바스크립트를 이용하여 변환
public String pageIndexList(int currentPage, int totalPage){


int numPerBlock = 5;
int currentPageSetup;
int n;
int page;
String strList=””;
if(currentPage==0)
return "";
//표시할 첫 페이지
currentPageSetup = (currentPage/numPerBlock)*numPerBlock;
if(currentPage%numPerBlock==0)
currentPageSetup = currentPageSetup - numPerBlock;


//1페이지
if((totalPage>numPerBlock)&&(currentPageSetup>0)){
strList = "<a onclick='listPage(1);'>1</a>";
}
//◀: 총페이지수가 numPerBlock 이상인 경우 이전 numPerBlock(게시글5개)을 보여줌
n = currentPage - numPerBlock;
//자바스크립트에 listPage라는 function을 정의하여 사용할 것이므로 a태그에 작성
if((totalPage>numPerBlock)&&(currentPageSetup>0)){
strList = "<a onclick='listPage("+n+");'>◀</a>";
}


//바로가기 페이지
page = currentPageSetup + 1;
while((page<=totalPage)&&(page<=currentPageSetup+numPerBlock)){
if(page==currentPage){
strList += "<font color='Fuchsia'>" +page+"</font>";
}else{
strList += "<a onclick='listPage("+page+");'>"+page+"</a>";
}
page++;
}
//▶: 총페이지수가 numPerBlock 페이지 이상인 경우 다음 numPerBlock 페이지를 보여줌
n = currentPage + numPerBlock;
if(totalPage - currentPageSetup > numPerBlock){
strList += "<a onclick='listPage(" +n+");'>▶</a>";
}
//마지막페이지
//총페이지수가 numPerBlock 페이지 이상이고 현재페이지+한페이지당갯수보다 크면 출력
if((totalPage>numPerBlock)&&(currentPageSetup+numPerBlock<totalPage)){
strList += "<a onclick='listPage("+totalPage+");'>"+totalPage+"</a>";
}
return strList;
}
}

4. 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>방명록</title>
<link rel="stylesheet" href="<%=cp%>/data/css/style.css" type="text/css">
<script type="text/javascript" src="<%=cp%>/data/js/jquery-3.3.1.min.js"></script>
<script type="text/javascript">
$(function(){
listPage(1);//첫 실행시 페이지=1로 실행
});


$(function(){
$("#sendButton").click(function(){
var params = "name=" + $("#name").val() +
"&email=" + $("#email").val() +
"&content=" + $("#content").val();
$.ajax({
type:"POST",
url:"<%=cp%>/iguest/created.action",
data:params,
success:function(args){
//넘어온값이 args에 담겨있음
$("#listData").html(args);
//초기화
방명록이 작성된 뒤 DB에 데이터가 반영이 되고 입력창에 있는 데이터값은 지워져야 한다.




하지만 reload 개념이 아니어서 input 태그에 입력된 데이터를 없애주는 소스가 필요하다.
$("#name").val("");
$("#email").val("");
$("#content").val("");
$("#name").focus();
},
beforeSend:showRequest,
error:function(e){
alert(e.responseText);
}
});
});
});
function showRequest(){
//beforesend : 보내기전에 실행되는 함수
var name = $.trim($("#name").val());//Ajax에서 제공하는 trim함수
var email = $.trim($("#email").val());
var content = $.trim($("#content").val());
if(!name){
alert("\n이름을 입력하세요.");
$("#name").focus();
return false;//null이므로 진행 중단
}
if(!content){
alert("\n내용을 입력하세요.");
$("#content").focus();
return false;//null이므로 진행 중단
}
if(content.length>200){
alert("\n내용을 200자 이내로 입력하세요.");
$("#content").focus();
return false;//null이므로 진행 중단
}
return true;
}
function listPage(page){
var url = "<%=cp%>/iguest/list.action";
$.post(url,{pageNum:page},function(args){
$("#listData").html(args);
});
$("#listData").show();//display:block으로 프로퍼티를 설정한 것과 동일한 효과
}


function deleteData(num,page){
//삭제 후 페이지 이동 필요
var url = "<%=cp%>/iguest/deleted.action";
$.post(url,{num:num,pageNum:page},function(args){
$("#listData").html(args);
});
}
</script>
</head>
<body>
<br><br>
<table width="600" border="2" cellpadding="0" cellspacing="0" bordercolor="#e6d4a6" align="center">
<tr height="40">
<td style="padding-left: 20px;">방 명 록</td>
</tr>
</table>
<br><br>
<table width="600" border="0" cellpadding="0" cellspacing="0" align="center">
<tr>
<td width="600" colspan="4" height="3" bgcolor="#e6d4a6"></td>
</tr>
<tr>
<td width="60" height="30" bgcolor="#eeeeee" 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="#eeeeee" align="center">email</td>
<td width="240" height="30" style="padding-left: 10px;">
<input type="text" id="email" size="35" maxlength="20" class="boxTF">
</td>
</tr>
<tr><td width="600" colspan="4" height="1" bgcolor="#e6d4a6"></td></tr>
<tr>
<td width="60" height="30" bgcolor="#eeeeee" align="center">내용</td>
<td width="540" colspan="3" style="padding-left: 10px;">
<textarea id="content" rows="3" cols="84" class="boxTA"></textarea>
</td>
</tr>
<tr><td width="600" colspan="4" height="1" bgcolor="#e6d4a6"></td></tr>
<tr>
<td width="600" colspan="4" height="30" align="right" style="padding-right: 15px;">
<input type="button" value="등록하기" class="btn2" id="sendButton"/>
</td>
</tr>
</table>
<br/>
<span id="listData" style="display: none;"></span>
</body>
</html>

5. jsp페이지 생성 - DB에 접근하여 방명록이 리스트가 div태그로 작성되는 페이지(list.jsp)

<c:if test="${totalDataCount!=0 }">
<table width="600" border="0" cellpadding="0" cellspacing="0" align="center">
<c:forEach var="dto" items="${lists }">
<tr>
<td colspan="2" bordercolor="#999999" height="1"></td>
</tr>
<tr height="30">
<td width="300" style="padding-left: 10px;">
No ${dto.listNum }.${dto.name }(<a href="mailto:${dto.email }">${dto.email }</a>)
</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="30" style="padding-left: 10px;" colspan="2">
${dto.content }
</td>
</tr>
</c:forEach>
<tr>
<td colspan="2" bordercolor="#dbdbdb" height="2"></td>
</tr>
<tr height="30">
<td align="center" colspan="2">
${pageIndexList }
</td>
</tr>
</table>
</c:if>

6. 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">
<select id="numMax" resultClass="int">
select nvl(max(num),0) from iguest
</select>
<select id="dataCount" resultClass="int">
select count(*) from iguest
</select>
<insert id="insertData" parameterClass="com.iguest.GuestDTO">
insert into iguest (num, name, email, content, ipAddr, created)
values(#num#,#name#,#email#, #content#,#ipAddr#,sysdate)
</insert>
<select id="listData" parameterClass="map" resultClass="com.iguest.GuestDTO">
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>
<delete id="deleteData" parameterClass="int">
delete iguest where num=#num#
</delete>
</sqlMap>

7. iguest_sqlMap.xml 등록(sqlMap_config.xml)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMapConfig PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-config-2.dtd">
<sqlMapConfig>
<settings
cacheModelsEnabled="false"
useStatementNamespaces="true"/>
<sqlMap resource="com/util/sqlMap/temp_sqlMap.xml"/>
<sqlMap resource="com/util/sqlMap/board_sqlMap.xml"/>
<sqlMap resource="com/util/sqlMap/iguest_sqlMap.xml"/>
</sqlMapConfig>

8. Action클래스 생성

package com.iguest;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
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;
public class GuestAction extends ActionSupport
implements Preparable, ModelDriven<GuestDTO>{
private static final long serialVersionUID = 1L;
private GuestDTO dto;


Ajax가 데이터를 넘김으로 Getter는 불필요함
public GuestDTO getDto() {
return dto;
}
@Resource(name="dao")
private CommonDAO dao;


@Resource(name="myUtil")
private MyUtil myUtil;


@Override
public GuestDTO getModel() {
return dto;
}
@Override
public void prepare() throws Exception {
dto = new GuestDTO();
}
//방명록 입력
public String created() throws Exception {
HttpServletRequest request = ServletActionContext.getRequest();
int maxNum = dao.getIntValue("iguest.numMax");
dto.setNum(maxNum+1);//게시물번호
dto.setIpAddr(request.getRemoteAddr());
dao.insertData("iguest.insertData", dto);
return list();//DB입력 완료후 redirect를 하면 새로고침됨(페이지 reload). Ajax로 구현하기 위해서 메소드 호출로 변경
}
//방명록 리스트 조회
public String list() throws Exception {
HttpServletRequest request = ServletActionContext.getRequest();
int numPerPage = 5;
int totalPage = 0;
int totalDataCount =0;
String pageNum = request.getParameter("pageNum");//삭제에서만 넘어옴


int currentPage = 1;
if(pageNum!=null&&pageNum!=""){
currentPage = Integer.parseInt(pageNum);
}else{
pageNum = "1";
}
totalDataCount = dao.getIntValue("iguest.dataCount");
if(totalDataCount!=0){
totalPage = myUtil.getPageCount(numPerPage, totalDataCount);
}
//삭제 진행되어 현재페이지 없어질경우
if(currentPage>totalPage)
currentPage = totalPage;
Map<String,Object> hMap = new HashMap<String,Object>();
int start = (currentPage-1)*numPerPage+1;
int end = currentPage*numPerPage;
hMap.put("start",start);
hMap.put("end", end);


List<Object> lists = (List<Object>)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++;
}
//페이징처리는 자바스크립트로 진행
String pageIndexList = myUtil.pageIndexList(currentPage, totalPage);
request.setAttribute("lists", lists);
request.setAttribute("pageIndexList",pageIndexList);
request.setAttribute("totalDataCount", totalDataCount);
request.setAttribute("pageNum", pageNum);


return SUCCESS;
}
//삭제
public String deleted() throws Exception {
HttpServletRequest request = ServletActionContext.getRequest();


int num = Integer.parseInt(request.getParameter("num"));
dao.deleteData("iguest.deleteData",num);
//redirect하지 않고 list로 연동
return list();
}
}

9. struts 환경설정 - 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">
<struts>
<package name="iguest" extends="default" namespace="/iguest" >       


<action name="guest">
<result>/iguest/guest.jsp</result>
</action>


<action name="created" method="created" class="com.iguest.GuestAction">
<interceptor-ref name="myPrepareParamsStack"/>
<result>/iguest/list.jsp</result>
</action>


<action name="list" method="list" class="com.iguest.GuestAction">
<result>/iguest/list.jsp</result>
</action>


<action name="deleted" method="deleted" class="com.iguest.GuestAction">
<result>/iguest/list.jsp</result>
</action>
</package>
</struts>

9. struts환경설정 - struts.xml에 등록

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

10.스프링에서 객체 생성시 - action-Context.xml에 작성

<bean id="GuestAction" class="com.board.GuestAction" scope="prototype"/>

구현 페이지

 

전체 흐름

★ guest.action을 호출하면 guest.jsp 페이지가 호출된다 .

<action name="guest">

<result>/iguest/guest.jsp</result>

 

</action>

★ guest.jsp 에서 페이지가 로드될 때, listPage 함수를 실행하도록 작성하였다.

$(function(){

listPage(1);//첫 실행시 페이지=1로 실행

 

});

 

1. listPage 메소드는 list.action을 호출.

2. list.actiondms GuestAction클래스의 list메소드를 실행한 뒤 result로 iguest.jsp 페이지를 반환한다.

3. 작성된 방명록이 있을 경우 list.jsp에 해당태그들이 반복문이 돌아가면서 작성된다

4. 작성된 태그들은 id=listData인 <span>태그에 innerHtml에 저장된다.

5. <span>의 display 속성을 block으로 변경한다.

▶ 페이지가 새로고침 되지않아도 비동기식을 활용하여 방명록이 업데이트되는 모습을 확인할 수 있다.