본문 바로가기

Ajax & jQuery

Ajax - 댓글 기능이 있는 게시판 만들기(미)

실제폴더: /ibbs, 가상주소(uri): /comm
개발환경: struts2+spring2.0, 톰캣 v8.5, iBatis, 이클립스, JDK 7.x


- struts-ibbs.xml (



BoardAction.java, CommentList.java

1. DB설정
ibbs_sqlMap.xml (sqlMapConfig.xml 에 등록)
2. xml파일
struts-ibbs.xml (struts.xml 에 등록)
3. Action클래스 생성
BoardAction.java,  CommentAction.java
4. 게시글 관련 jsp 페이지 
list.jsp, created.jsp, article.jsp
5. 댓글관련 jsp 페이지
- article.jsp (게시글 부분과 공통)
- ajax를 통해 데이터를 반환할 때, 표 형식으로 만들어서 보내기 위한 페이지 - commentList.jsp 

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

게시글 테이블
CREATE TABLE ibbs (
   boardNum  NUMBER(9) NOT NULL
   ,name      VARCHAR2(20) NOT NULL
   ,email     VARCHAR2(50)
   ,subject   VARCHAR2(50) NOT NULL
   ,content   VARCHAR2(4000) NOT NULL
   ,ipAddr    VARCHAR2(20) NOT NULL
   ,hitCount  NUMBER(9) NOT NULL
   ,created   DATE
   ,CONSTRAINT pk_ibbs_boardNum PRIMARY KEY(boardNum)
);

 

댓글 테이블
CREATE TABLE ibbsComment (
    commentNum  NUMBER(9) NOT NULL
    ,boardNum   NUMBER(9) NOT NULL
    ,name       VARCHAR2(20) NOT NULL
    ,content    VARCHAR2(100) NOT NULL
    ,ipAddr     VARCHAR2(20) NOT NULL
    ,created    DATE
    ,CONSTRAINT pk_ibbsComment_commentNum PRIMARY KEY(commentNum)
    ,CONSTRAINT fk_ibbsComment_boardNum FOREIGN KEY(boardNum)
                REFERENCES ibbs(boardNum) ON DELETE CASCADE
);

 

CommonDAOImpl.java - DB 접근 객체 생성

//CommonDAO dao = new CommonDaoImpl() 할 필요 없이 spring이 "dao"(사용자정의) 라는 이름으로 객체생성을 해줌 => 나는 갖다 써주기만 하면됨
//이름 안써주면, 클래스명인 CommonDAOImpl로 객체생성이 되어버리므로 나중에 서로다른페이지에서 갖다쓸 때 충돌이 일어날 수 있음

@Repository("dao")
public class CommonDAOImpl implements CommonDAO{
	
@Autowired // aplicationContext.xml로 가서, 너가 알아서 이 이름 객체 찾아와서 초기화 시켜
private SqlMapClientTemplate sqlMapClientTemplate;

	// 원래는, setter도 있어야함
	//public void setSqlMapClientTemplate() {..}
	// 지금은 annotation 쓸거니깐 필요없음
    
    
  ...
  
    }

MyUtil.java - 페이징 처리

@Service("myUtil")
public class MyUtil {
...
}

게시글 작성

struts-ibbs.xml

		<action name="created" class="com.ibbs.BoardAction" method="created">
			<interceptor-ref name="myPrepareParamStack"/>
			<result name="input">/ibbs/created.jsp</result>
			<result name="success" type="redirectAction">list</result>
		</action>

BoardAction.java

package com.ibbs;

import java.net.URLDecoder;
import java.net.URLEncoder;
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 BoardAction extends ActionSupport
implements Preparable,ModelDriven<BoardDTO>{

	private static final long serialVersionUID = 1L;

	private BoardDTO dto; // 원본글

	//CommonDAO => 이제 메소드마다 만들필요 없이 spring이 만들어놓은 객체를 가져다 쓰면 됨 

	// --- spring의 유일한 강점 코딩 (객체 생성) --
	// 불러오면 객체가 Object 형태로 만들어져 있음 (내가 안 만든건 다 Object임)
	@Resource(name="dao")
	private CommonDAO dao; // 모든 메소드에서 이 dao를 사용해주면 됨
	
	@Resource(name="myUtil")
	private MyUtil myUtil;

	@Override
	public BoardDTO getModel() {
		return dto;
	}
	
	@Override
	public void prepare() throws Exception {


		dto = new BoardDTO();

	}

	public String created() throws Exception {

		HttpServletRequest request = 
				ServletActionContext.getRequest();
		

		if (dto == null || dto.getMode()==null || dto.getMode().equals("")) {
			
			dto.setMode("created");
			request.setAttribute("dto", dto);
			
			return INPUT;
		}


		int numMax = dao.getIntValue("ibbs.numMax");
		dto.setBoardNum(numMax+1);
		dto.setIpAddr(request.getRemoteAddr());
		dao.insertData("ibbs.insertData", dto);

		return SUCCESS;


	}

	.....................

}

created.jsp - mode로 "created"를 받아온 상황

1) created로 들어왔을 때 보여지는 창

				<!-- 공통 (created, updated 중 어떤 걸로 들어온건지)  -->
				<input type="hidden" name="mode" value="${mode }" />

				<c:if test="${dto.mode=='created'}">
					<input type="button" value="등록하기" class="btn2" onclick="sendIt();" />
					<input type="reset" value="다시입력" class="btn2"
						onclick="document.myForm.subject.focus();" />
					<input type="button" value="작성취소" class="btn2"
						onclick="javascript:location.href='<%=cp%>/comm/list.action';" />
				</c:if>

2) 등록하기 btn을 누르면 form 안의 input값들을 가지고 해당 url로 감

		// 실제폴더: ibbs,  가상주소(uri): comm
		if(f.mode.value=="created")
			f.action = "<%=cp%>/comm/created.action";

 

게시글 리스트 

struts-ibbs.xml

		<action name="list" class="com.ibbs.BoardAction" method="list">
		<interceptor-ref name="myPrepareParamStack"/>
			<result name="success">/ibbs/list.jsp</result>
		</action>

BoardAction.java - boardNum들에 대한 각각의 댓글 개수들도 가지고 와야 함

public String list() throws Exception {

		HttpServletRequest request = 
				ServletActionContext.getRequest();

		String cp = request.getContextPath();

		int numPerPage = 3;
		int totalPage = 0;
		int totalDataCount = 0;
		
		
		String searchKey = request.getParameter("searchKey");
		String searchValue = request.getParameter("searchValue");
		String pageNum = request.getParameter("pageNum");

		if (searchValue == null) {
			searchKey="subject";
			searchValue="";
		}
		
		if(request.getMethod().equalsIgnoreCase("GET")) 
			searchValue = URLDecoder.decode(searchValue, "UTF-8");
				
		int currentPage = 1;

		if (pageNum!=null)
			currentPage = Integer.parseInt(pageNum);
				

		Map<String, Object> hMap = new HashMap<String, Object>();
		hMap.put("searchKey", searchKey);
		hMap.put("searchValue", searchValue);

		totalDataCount = 
				dao.getIntValue("ibbs.dataCount", hMap);

		if(totalDataCount!=0)
			totalPage = myUtil.getPageCount(numPerPage, totalDataCount);

		if(currentPage>totalPage)
			currentPage = totalPage;

		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("ibbs.listData", hMap);


		// 글 하나 삭제 시, 일련번호 맞추기 위한 코드
		int listNum,n=0;
		int commentSu; // 댓글 수 
		int a = 0;
		
		Iterator<Object> it = lists.iterator();

		while(it.hasNext()) {

			BoardDTO listDTO = (BoardDTO)it.next();
			// 이걸 dto라고 변수명을 지어주면 자동으로 넘겨주는 dto와 이것이 
			// 충돌날 수도 있음. 그래서 listDTO 라고 지음

			// 1. listNum 작업
			listNum = totalDataCount-(start+n-1);
			listDTO.setListNum(listNum);
			n++;
			
			// 2. 댓글(2) 작업 : sql접근
			 a = listDTO.getBoardNum();
			 commentSu = dao.getIntValue("ibbs.dataCount2", a); // 댓글 수
			 listDTO.setCommentNum(commentSu); // 해당 boardNum의 댓글 수 넣음

		}

		String params="";
		String urlArticle = "";
		String urlList = "";

		if(!searchValue.equals("")) {

			params = "searchKey="+searchKey;
			params += "&searchValue=" + 
					URLEncoder.encode(searchValue, "UTF-8");
			//encode한 상태로 uri에 넣어야함


		}

		// ◀이전 6 7 8 9 10 다음▶ 표시 링크 
		urlList = cp + "/comm/list.action";
		urlArticle = 
				cp + "/comm/article.action?pageNum="+currentPage;
		
		// 검색해서 들어온거면, 
		if(!params.equals("")) {
			urlList += "?" + params;
			urlArticle += "&" + params;
		}
		
		//param 라는 변수는 struts2가 내부적으로 사용하는 변수여서,
		// setAttribute로 param 넘기면 안됨.

		// 여기서 만약 dto를 넘기고 싶으면 위에 getter 안만들어줬기 때문에 
		// 수동으로 해야함 request.setAttribute("dto",dto); 
		request.setAttribute("lists", lists);
		request.setAttribute("totalDataCount", totalDataCount);
		request.setAttribute("pageIndexList",
				myUtil.pageIndexList(currentPage, totalPage, urlList));
		request.setAttribute("urlArticle", urlArticle); 
		request.setAttribute("params", params);
		request.setAttribute("pageNum", currentPage);
		request.setAttribute("searchKey", searchKey);
		request.setAttribute("searchValue", searchValue);

		return SUCCESS;

	}

list.jsp 

	<div id="bbsList_list">
		<div id="title">
			<dl>
				<dt class="num">번호</dt>
				<dt class="subject">제목</dt>
				<dt class="name">작성자</dt>
				<dt class="created">작성일</dt>
				<dt class="hitCount">조회수</dt>			
			</dl>		
		</div>
		
		<div id="lists">
		<c:forEach var="dto" items="${lists }">
			<dl>
				<dd class="num">${dto.listNum}</dd>
				<dd class="subject">
					<a href="${urlArticle }&boardNum=${dto.boardNum}">
					${dto.subject } (${dto.commentNum })
					</a>
				</dd>
				<dd class="name">${dto.name }</dd>
				<dd class="created">${dto.created }</dd>
				<dd class="hitCount">${dto.hitCount }</dd>
			</dl>
		</c:forEach>			
		</div>
		
			
		<div id="footer">
			<p>
				<c:if test="${totalDataCount!=0 }">
					${pageIndexList }
				</c:if>
				<c:if test="${totalDataCount==0 }">
					등록된 게시물이 없습니다.
				
				</c:if>
			</p>
		</div>
	</div>

게시글 수정

article.jsp - 에서 [수정] 버튼을 누르면

	<div id="bbsArticle_footer">
		<div id="leftFooter">		
		<input type="button" value=" 수정 " class="btn2" onclick="sendData('updated');"/>
		<input type="button" value=" 삭제 " class="btn2" onclick="sendData('deleted');"/>		
	</div>
	function sendData(value) {
		
		var boardNum = "${dto.boardNum}"; 
		// javascript에서도 넘어오는 데이터에 대해서
		//이렇게 el로 받을 수 있다.
		var url = "<%=cp%>/comm/";
		
		if (value == "updated")
			url += "updated.action?";
		
		else if (value == "deleted")
			url += "deleted.action?";
		
		url += "boardNum=" + boardNum;
		//url += "&${params}"; // pageNum, sK, sV 넘어온거
		url +="&pageNum=${pageNum}&searchKey=${searchKey}&searchValue=${searchValue}"
		
		location.replace(url); // 이동
		
	}

struts-ibbs.xml

 		<action name="updated" method="updated" class="com.ibbs.BoardAction">
		<interceptor-ref name="myPrepareParamStack"/>
			<result name="input">/ibbs/created.jsp</result>
			<result name="success" type="chain">
			  		 list
			</result>
		</action>

--북마크

 

댓글 삭제 부분

commentList.jsp

<a href="javascript:deleteData('${dto.commentNum }','${pageNum }','${dto.boardNum }');">
삭제
</a>

article.jsp

	
	function deleteData(num,page,boardNum) {
		
		var url = "<%=cp%>/comm/deleted2.action";
		
		//jQuery로만 ajax를 만들 때 사용
		//[변수명:데이터]
		$.post(url,{num:num,pageNum:page,boardNum:boardNum},function(args) {
			// js특징중하나
			//url 에 pageNum,page를 가지고 들어가서 
			//결과를 바로 받아온다 (보내는애와 받는애가 같이 있는 구조)
			
			$("#listData").html(args);
			
		});
		
		$("#listData").show();
		
	}

struts-ibbs.xml

		<action name="deleted2" class="com.ibbs.CommentAction" method="deleted">
			<result>/ibbs/commentList.jsp</result>
		</action>

CommentAction.java

	public String deleted() throws Exception {
		
		HttpServletRequest request = 
				ServletActionContext.getRequest();
		
		// 그 다음 경로는 같은 페이지 내의 함수인 list()로 보내므로 
		//매개변수로 넘어온 boardNum, pageNum, num 전부 다 받고
		// 다시 request.setAttribute로 해주지 않아도 됨
		
		int num = Integer.parseInt(request.getParameter("num"));
						
		dao.deleteData("ibbs.deleteData2", num);
				
		return list();				
	}

댓글 작성 부분

article.jsp

<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>	
</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 rows="3" cols="84" class="boxTA" style="height: 50px;" id="content">
	</textarea>
	</td>
</tr>

<tr>
	<td width="600" colspan="4" height="1" bgcolor="#e6d4a6"></td>
</tr>

<tr>
	<td width="600" height="30" colspan="4" align="right" style="padding-right: 15px;">
	<input type="button" value="등록하기" class="btn2" id="sendButton"/>
	</td>
</tr>

</table>

[등록하기] 버튼 누르면,

	$(document).ready(function(){
		
		$("#sendButton").click(function(){
			// jQuery의 사용자가 아래서 입력한 데이터를 받아오는 방식
			// javascript는 document.getElementById 이렇게 접근했었음
			var params = "name=" + $("#name").val()
					+ "&content=" + $("#content").val()
					+ "&boardNum=${dto.boardNum}";
			
			$.ajax({
				
				type:"POST", // input값 보내는거니깐 post형태로 보내기 
				url:"<%=cp%>/comm/created2.action",   
				data:params,
				success:function(args){
					
					// 결과 데이터는 이미 표로 가공된 상태로 올 것임 (commentList.jsp로부터)
					$("#listData").html(args);
										
					//ajax부분만 바뀌기 때문에, 사용자가 댓글 [등록하기] 누르고 나면 
					//입력창에 입력값이 그대로 남아있음 =>  초기화 시켜주는 작업 필요
					$("#name").val("");
					$("#content").val("");
					$("#name").focus; // 다 지우고 name에다가 커서 갖다놓기					
				},
				
				beforeSend:showRequest, // 보내기전에 실행
				
				error:function(e) {
					alert(e.responseText); // 갔다와서 에러가 나면 알람을 띄워라 
				}
				
			});
			
		});
		
	});
	
	function showRequest() {
		
		// 사용자가 입력한 데이터 가져와라
		// 공백 없앤 상태로 변수에 다시 넣어줌 
		var name = $.trim($("#name").val());
		var content = $.trim($("#content").val());
		
		if(!name) {
			alert("\n이름을 입력하세요!");
			$("#name").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;
	}	

struts-ibbs.xml

		<action name="created2" class="com.ibbs.CommentAction" method="created">
			<interceptor-ref name="myPrepareParamStack"/>
			<result>/ibbs/commentList.jsp</result>
		</action>

CommentAction.java

	public String created() throws Exception {
		
		HttpServletRequest request = 
				ServletActionContext.getRequest();
		
		// ajax에 의해 넘어온 값: name, content, boardNum  
		// CommentDTO dto에 알아서 들어가는듯.
		
		int numMax2 = dao.getIntValue("ibbs.numMax2");
		
		
		dto.setCommentNum(numMax2+1);
		dto.setIpAddr(request.getRemoteAddr());
		
		dao.insertData("ibbs.insertData2", dto);
		
		//redirect : client한테 주소 다시 한번 호출해 (화면 전체가 새로고침 되어버림)
		//지금 쓰고 있는건 ajax이고
		//ajax를 쓰는 이유는 새로고침 안 하려는게 크니까 redirect 쓰면 안됨
		
		return list();		
	}

ibbs_sqlMap.xml

	<select id="numMax2" resultClass="int">
	select nvl(max(commentNum),0) from ibbsComment
	</select>	
	
	<insert id="insertData2" parameterClass="com.ibbs.CommentDTO">
	insert into ibbsComment (commentNum, boardNum, name, content, ipAddr, created) values
	(#commentNum#, #boardNum#, #name#, #content#, #ipAddr#, sysdate)
	</insert>