관리자 글쓰기

이 포스팅은 공부할 겸 오픈소스를 참고하지 않고 혼자서 만들어 본 것으로 굉장히 비효율적일 것입니다. 


-------------------------------------------------------------------------------------------------------------------------------------------

0-1. UserInfo

sample 패키지 밑 spring 패키지를 생성해 UserInfo class를 만든다.

UserInfo.java

public class UserInfo {

	String uid;
	int idx;
	String unick;
	boolean error;
	
	public UserInfo(boolean error) {
		this.error=error;
	}
	public UserInfo(String id, int idx, String nickname) {
		this.uid = id;
		this.idx = idx;
		this.unick = nickname;
	}
	public String getUid() {
		return uid;
	}
	public void setUid(String uid) {
		this.uid = uid;
	}
	public int getIdx() {
		return idx;
	}
	public void setIdx(int idx) {
		this.idx = idx;
	}
	public String getUnick() {
		return unick;
	}
	public void setUnick(String unick) {
		this.unick = unick;
	}
	public boolean isError() {
		return error;
	}
	public void setError(boolean error) {
		this.error = error;
	}	
}
- 유저의 정보를 세션을 통해서 유지하기 위하여 생성한 클래스이다.


1. Controller

SampleController.java

    @RequestMapping(value="/sample/openUserLogin.do")
    public ModelAndView openUserLogin(CommandMap commandMap) throws Exception{
    	ModelAndView mv = new ModelAndView("user/userLogin");
    	
    	mv.addObject("error", commandMap.get("error"));
    	
    	return mv;
    }

    @RequestMapping(value="/sample/loginUser.do")
    public ModelAndView loginUser(CommandMap commandMap, HttpSession session) throws Exception{

    	UserInfo userInfo = sampleService.loginUser(commandMap.getMap());
    	ModelAndView mv = null;;
    	if(!userInfo.isError()) {
    		mv = new ModelAndView("redirect:/sample/openBoardList.do");
    		session.setAttribute("userInfo", userInfo);
    	}else {
    		mv = new ModelAndView("redirect:/sample/openUserLogin.do");
    		mv.addObject("error", userInfo.isError());
    	}
    	return mv;
    }
    
    @RequestMapping(value="/sample/logoutUser.do")
    public ModelAndView logoutUser(CommandMap commandMap, HttpSession session) throws Exception{
    	
    	ModelAndView mv = new ModelAndView("redirect:/sample/openBoardList.do");
    	session.invalidate();
    	
    	return mv;
    }

- 로그인 페이지와 로그인 처리, 로그아웃 처리에 대한 컨트롤러들이다. loginUser 메서드에선 위에서 생성한 UserInfo 클래스를 생성해 유저에 대한 정보를 전달하고 해당 정보의 올바른 정보인지를 판단 후 페이지로 넘어간다. 올바른 로그인 처리의 경우 UserInfo 객체를 세션의 속성으로 추가시키고 메인 페이지로 넘어간다. logoutUser 메서드는 로그인 처리를 통해 선언했던 세션에 대해 무효화한다.



2. Service(Impl)

SampleServiceImpl.java

	@Override
	public UserInfo loginUser(Map<String, Object> map) throws Exception {
		Map<String,Object> tempMap = sampleDAO.selectUserID(map);
		UserInfo userInfo = null;
		if(tempMap.get("UPW").equals(map.get("UPW").toString())) {
			userInfo = new UserInfo(tempMap.get("UID").toString(),
					Integer.parseInt(tempMap.get("IDX").toString()),
					tempMap.get("UNICK").toString());
			userInfo.setError(false);
			return userInfo;
		}else {
			userInfo = new UserInfo(true);
			return userInfo; 
		}
	}

- DAO를 통해 해당 유저의 유무를 파악 후 유저로부터 입력받은 비밀번호와 DB에 저장되어있던 컬럼의 비밀번호를 비교하여 처리하며 동일한 경우에는 UserInfo에 해당 칼럼의 정보들을 전달하고 에러의 유무를 세팅하여 UserInfo 객체를 리턴해준다. 반면 동일하지 않은 경우 에러가 있다는 boolean 값을 보내어 컨트롤러에서 처리할 수 있게 만든다.



3. DAO

SampleDAO.java

@SuppressWarnings("unchecked")
	public Map<String,Object> selectUserID(Map<String, Object> map) {
		return (Map<String,Object>)selectOne("sample.selectUserID",map);
	}

- 회원 가입 처리에서 사용하였던 selectUserID 메서드이다.


4. SQL

sample_SQL.xml

      <select id="selectUserID" parameterType="HashMap" resultType="HashMap">
      	<![CDATA[
			SELECT
				IDX,
				UID,
				UPW,
				UNICK
			FROM
				TB_USER
			WHERE
				UID=#{UID}
      	]]>
      </select>
      <insert id="insertComment" parameterType="HashMap">
      	<![CDATA[
      		INSERT INTO
      			TB_COMMENT(
      				BOARD_IDX,
					CREA_ID,
					GRADE,
					CONTENTS
				)VALUES(
					#{IDX},
					#{CREA_ID},
					'0',
					#{COM_CONTENTS}
				)					
      	]]>
      </insert>
      <insert id="insertFile" parameterType="HashMap">
      	<![CDATA[
			INSERT INTO
				TB_FILE1(
					ORIGINAL_FILE_NAME,
					STORED_FILE_NAME,
					FILE_SIZE,
					BOARD_IDX,
					CREA_ID
				)VALUES(
					#{ORIGINAL_FILE_NAME},
					#{STORED_FILE_NAME},
					#{FILE_SIZE},
					#{BOARD_IDX},
					#{CREA_ID}
				)      	
      	]]>
      </insert>
      <insert id="insertBoard" parameterType="HashMap"  useGeneratedKeys="true" keyProperty="IDX" >
      	<![CDATA[
      		INSERT INTO
	           	TB_BOARD(
	               TITLE,
	               CONTENTS,
	               HIT_CNT,
	               DEL_GB,
	               CREA_ID)
	           VALUES(
	               #{TITLE},
	               #{CONTENTS},
	               0,
	               'N',
	               #{CREA_ID})
      	]]>
      </insert>

- 위와 같이 회원 가입 부분에서 사용했던 쿼리다.

- insert 처리를 해주는 부분의 경우 기존에 작성자 아이디를 'Admin'으로 해놓았기 때문에 CREA_ID를 받아와 전달해준다.


4-1. FileUtils.java

FileUtils.java 에서 parseInsertFileInfo 메서드에 

String crea_Id = map.get("CREA_ID").toString();

- 해당 코드를 추가하여 insertFile 처리 과정 중 SQL 부분으로 CREA_ID가 전달 될 수 있게 한다.


5. JSP

userLogin.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE>
<head>
<title>로그인</title>
<%@ include file="/WEB-INF/include/include-header.jspf" %>
</head>
<body>
	<div class="user_view wdp_30" >
		<div class="con_center under_line"><h5>로그인</h5></div>
		<form id="frm" class="padding_5" onsubmit="JavaScript:fn_loginUser();"  >
			<fieldset>
				<input type="text" class="input_text" name="UID" maxlength="15" placeholder="아이디" value="" id="uid"><br/>
				<input type="password" class="input_text" name="UPW" maxlength="15" placeholder="비밀번호" value="" id="upw"><br/>
			</fieldset>
			<br/>
			<c:if test="${error}">
				아이디나 비밀번호가 일치하지 않습니다.			
			</c:if>
			<button type="submit" class="btn con_center">로그인</button>

		</form>
	</div>
</body>
	<%@ include file="/WEB-INF/include/include-body.jspf" %>
	<script type="text/javascript">
		
		function fn_loginUser(){
			var comSubmit = new ComSubmit("frm");
			comSubmit.setUrl("<c:url value='/sample/loginUser.do'/>");
			comSubmit.submit();
		}
		
	</script>
</html>

- 회원가입 페이지와 매우 유사하며 다른 특이한 사항은 없다.


boardList.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE>
<html>
<head>
	<title>게시물 리스트</title>
	<%@ include file="/WEB-INF/include/include-header.jspf" %>
</head>
<body>
	<div align="right">
		<c:choose>
			<c:when test="${empty userInfo }">
				<a href="#this" class="btn" id="login">로그인</a><a href="#this" class="btn" id="join">회원가입</a>
			</c:when>
			<c:otherwise>
				<a href="#this" class="btn" id="logout">로그아웃</a>
			</c:otherwise>
		</c:choose>
	</div>
	<table class="board_list">
		<colgroup>
			<col width="5%">
			<col width="50%">
			<col width="5%">
			<col width="15%">
			<col width="15%">
		</colgroup>
		<caption>게시판</caption>
		<thead>
			<tr>
				<th>No.</th>
				<th>제목</th>
				<th>Hit</th>
				<th>작성자</th>
				<th>작성시간</th>
			</tr>
		</thead>
		<tbody>
			<c:choose>
				<c:when test="${fn:length(list)>0}">
					<c:forEach items="${list }" var="con" >
						<tr>
							<td>${con.IDX }</td>
							<td><input type="hidden" id="IDX" value="${con.IDX }"><a href="#this" name="TITLE">${con.TITLE }</a></td>
							<td>${con.HIT_CNT }</td>
							<td>${con.CREA_ID }</td>
							<td>${con.CREA_DTM }</td>
						</tr>
					</c:forEach>
				</c:when>
				<c:otherwise>
					<tr>
						<td>게시물이 존재하지 않습니다.</td>
					</tr>
				</c:otherwise>			
			</c:choose>
		</tbody>
	</table>

	<c:if test="${not empty paginationInfo}">
		 <ui:pagination paginationInfo = "${paginationInfo}" type="text" jsFunction="fn_search"/>
	</c:if>
    <input type="hidden" id="currentPageNo" name="currentPageNo"/>
	<br><br>
	
	<c:if test="${! empty userInfo }">
		<a href="#this" class="btn" id="write">글쓰기</a>
	</c:if>
	<%@ include file="/WEB-INF/include/include-body.jspf" %>
	
	<script type="text/javascript">
	    $(document).ready(function(){
	        $("#write").on("click",function(e){
	            e.preventDefault();
	            fn_openBoardWrite();
	        })
	        $("a[name='TITLE']").on("click",function(e){
	            e.preventDefault();
	            fn_openBoardDetail($(this));
	        })
	        $("#login").on("click",function(e){
	            e.preventDefault();
	            fn_openUserLogin();
	        })
	        $("#join").on("click",function(e){
	            e.preventDefault();
	            fn_openUserJoin();
	        })
	        $("#logout").on("click",function(e){
	            e.preventDefault();
	            fn_logoutUser();
	        })
	    })
	     
	    function fn_openBoardWrite(){
	        var comSubmit = new ComSubmit();
	        comSubmit.setUrl("<c:url value='/sample/openBoardWrite.do'/>");
	        comSubmit.submit();
	    }
	    function fn_openBoardDetail(val){
	        var comSubmit = new ComSubmit();
	        comSubmit.addParam("IDX",val.parent().find("#IDX").val());
	        comSubmit.setUrl("<c:url value='/sample/openBoardDetail.do'/>");
	        comSubmit.submit();
	    }
	    function fn_search(pageNo){
            var comSubmit = new ComSubmit();
            comSubmit.setUrl("<c:url value='/sample/openBoardList.do' />");
            comSubmit.addParam("currentPageNo", pageNo);
            comSubmit.submit();
        }
	    function fn_openUserLogin(){
	        var comSubmit = new ComSubmit();
	        comSubmit.setUrl("<c:url value='/sample/openUserLogin.do'/>");
	        comSubmit.submit();
	    }
	    function fn_openUserJoin(){
	        var comSubmit = new ComSubmit();
	        comSubmit.setUrl("<c:url value='/sample/openUserJoin.do'/>");
	        comSubmit.submit();
	    }
	    function fn_logoutUser(){
	        var comSubmit = new ComSubmit();
	        comSubmit.setUrl("<c:url value='/sample/logoutUser.do'/>");
	        comSubmit.submit();
	    }
	    
	</script>
</body>
</html>

- 10~19줄, 66~68줄

로그인, 회원가입, 로그아웃에 대한 <a> 태그를 조건에 따라 적용해두었다. 로그인한 유저만 글쓰기 <a>태그를 보이도록 설정한 것이다.

- 71~128줄

로그인, 회원가입, 로그아웃의 <a> 태그에 대한 처리와 자바스크립트 함수들을 선언해두었다.



boardDetail.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE>
<html>
<head>
	<title>글 보기</title>
	<%@ include file="/WEB-INF/include/include-header.jspf" %>
</head>
<body>
	<table class="board_view">
		<colgroup>
			<col width="15%">
			<col width="35%">
			<col width="15%">
			<col width="35%">
		</colgroup>
		<caption>${map.TITLE }</caption>
		<tbody>
			<tr>
				<th>No.</th>
				<td>${map.IDX }</td>
				<th>Hit.</th>
				<td>${map.HIT_CNT }</td>
			</tr>
			<tr>
				<th>Writer.</th>
				<td>${map.CREA_ID }</td>
				<th>Date.</th>
				<td>${map.CREA_DTM }</td>
			</tr>
			<tr>
				<th>Content.</th>
				<td colspan="3">${map.CONTENTS }</td>
			</tr>
			<c:if test="${fn:length(list)>0 }">
				<tr>
					<th>첨부파일</th>
					<td colspan="3">
						<c:forEach items="${list }" var="f" >
							<p>
								<input type="hidden" id="IDX" value="${f.IDX }"><a href="#this" name="FILE">${f.ORIGINAL_FILE_NAME }</a>(${f.FILE_SIZE}byte)
							</p>
						</c:forEach>
					</td>
				</tr>			
			</c:if>
		</tbody>
	</table><br>
	<form id="frm">
		<table class="board_view">
			<colgroup>
				<col width="15%">
				<col width="85%">
			</colgroup>
			<tbody>
				<tr>
					<th>댓글</th>
					<td>
						${fn:length(comment) }
					</td>
				</tr>
				<c:if test="${fn:length(comment)>0 }">
					<c:forEach items="${comment }" var="com">
						<tr>
							<td style="background:#f7f7f7;color:#3b3a3a;" >
								${com.CREA_ID }
								<p style="font-size: 8px;" >${com.CREA_DTM }</p>
							</td>
							<td>
								<input type="hidden" value="${com.IDX }" id="com_IDX">
								<div id="com_Div"><input type="hidden" value ="${com.CONTENTS}" id="com_CON">${com.CONTENTS }</div>
								<c:if test="${com.CREA_ID==userInfo.uid }">
									<div align="right">
										<a href="#this" name="com_Del" class="btn">삭제</a>
										<a href="#this" name="com_Mod" class="btn">수정</a>
									</div> 							
								</c:if>
							</td>
						</tr>
					</c:forEach>
				</c:if>
				<c:if test="${! empty userInfo }">
					<tr>
						<td colspan="2">
							<div>
								${userInfo.uid}
								<input type="hidden" name="CREA_ID" value="${userInfo.uid }"/>
								<br/><br/>
								<textarea  rows="5" cols="130" name="COM_CONTENTS" ></textarea>
								<p align="right" ><a href="#this" id="com_write" class="btn">등록</a></p>
							</div>
						</td>
					</tr>
				</c:if>
			</tbody>
		</table>
	</form>
	
	<%@ include file="/WEB-INF/include/include-body.jspf" %>
	<c:if test="${userInfo.uid==map.CREA_ID }">
		<a href="#this" id="update" class="btn">수정하기</a>
	</c:if>
	<a href="#this" id="list" class="btn">목록</a>
	
	<script type="text/javascript">
		$(document).ready(function(){
			$("#update").on("click",function(e){
				e.preventDefault();
				fn_openBoardUpdate();
			})
			$("#list").on("click",function(e){
				e.preventDefault();
				fn_openBoardList();
			})
			$("a[name=FILE]").on("click",function(e){
				e.preventDefault();
				fn_downloadFile($(this));
			})
			$("#com_write").on("click",function(e){
				e.preventDefault();
				fn_writeComment();
			})
			$("a[name=com_Del]").on("click",function(e){
				e.preventDefault();
				fn_deleteComment($(this));
			})
			$("a[name=com_Mod]").on("click",function(e){
				e.preventDefault();
				fn_commentModify($(this));
			})
			
		})
		
		function fn_openBoardUpdate(){
			var comSubmit = new ComSubmit();
			comSubmit.addParam("IDX",${map.IDX});
			comSubmit.setUrl("<c:url value='/sample/openBoardUpdate.do'/>");
			comSubmit.submit();
		}

		function fn_openBoardList(){
			var comSubmit = new ComSubmit();
			comSubmit.setUrl("<c:url value='/sample/openBoardList.do'/>");
			comSubmit.submit();
		}

		function fn_downloadFile(obj){
			var comSubmit = new ComSubmit();
			comSubmit.addParam("IDX",obj.parent().find("#IDX").val());
			comSubmit.setUrl("<c:url value='/common/downloadFile.do'/>");
			comSubmit.submit();
            $("#commonForm").children().remove();
		}

		function fn_writeComment(){
			var comSubmit = new ComSubmit("frm");
			comSubmit.addParam("IDX",${map.IDX})
			comSubmit.setUrl("<c:url value='/sample/writeComment.do'/>");
			comSubmit.submit();
		}

		function fn_deleteComment(obj){
			var comSubmit = new ComSubmit();
			comSubmit.addParam("IDX",${map.IDX})
			comSubmit.addParam("COM_IDX",obj.parent().parent().find("#com_IDX").val());
			comSubmit.setUrl("<c:url value='/sample/deleteComment.do'/>");
			comSubmit.submit();
		}

		function fn_commentModify(obj){
			var con = obj.parent().parent().find("#com_Div").find("#com_CON").val();
			var str = "<textarea  rows='5' cols='100' name='COM_CONTENTS_UPD'>"+con+"</textarea><p align='right' ><a href=''#this' name='com_Upd' class='btn'>등록</a></p><hr/>";
			var div = obj.parent().parent().find("#com_Div");

			div.empty();
			div.append(str)
			
			$("a[name=com_Upd]").on("click",function(e){
				e.preventDefault();
				fn_updateComment($(this));
			})
		}

		function fn_updateComment(obj){
			var comSubmit = new ComSubmit("frm");
			comSubmit.addParam("IDX",${map.IDX})
			comSubmit.addParam("COM_IDX",obj.parent().parent().parent().find("#com_IDX").val());
			comSubmit.setUrl("<c:url value='/sample/updateComment.do'/>");
			comSubmit.submit();
		}
	</script>
</body>
</html>

- 72~78줄, 85~86줄, 98~100줄

댓글이나 게시글에 대해 작성자일 경우 수정이나 삭제가 가능하도록 조건을 걸었고, 85~86줄에 댓글에 대한 userInfo의 ID값을 form을 통해 다음 처리를 위하여 input의 hidden 타입에 값을 할당해주었다.



6. 테스트

- 로그인

- 로그인 화면에서 전 단계에서 회원가입 하였던 아이디로 로그인하였다.


- 로그인 과정의 로그.


- 로그인 후의 게시물 리스트 페이지. 상단 로그아웃 버튼은 로그아웃 후에는 로그인과 회원가입 버튼으로 대체됨.


- 로그인한 아이디로 게시물을 작성.



- 로그인한 아이디로 게시물을 쓴 결과물. 작성자가 test1으로 되어 있고 댓글 작성자의 아이디도 test1으로 되어있음.



- 로그인한 아이디로 댓글을 등록하였을 경우의 결과 페이지