본문 바로가기

반응형
이번 시간에는 글쓰기를 구현하도록 하겠습니다.

 

리스트 페이지의 요구조건을 만족하기 전에 글쓰기가 먼저 선행 되어야 합니다. 이번 시간에는 글을 쓰는 기능을 구현하도록 하겠습니다.

리스트 페이지 > 글 쓰기 페이지

만족해야할 것 
1) 성함 , 직급 노출
2) 로그아웃 버튼
3) 글쓰기 버튼

1. 성함, 직급 노출 & 로그아웃 버튼

성함과 직급은 저번 시간에 노출을 시켜줬습니다. 요구조건은 성함(직급) 형태로 노출 시키라고 했으니 괄호를 추가해주도록 합니다. 또한, 로그아웃도 저번에 구현을 해줬으니 생긴 것만 조금 다듬고 넘어가도록 하겠습니다.

1-1. 컨트롤러

	// Logout
	@RequestMapping(value="/logout")
	public String logout(HttpSession session) {
		session.invalidate();
		return "redirect:/login";
	}

 

2. 글쓰기 버튼

2-1. 글쓰기 버튼 만들기

 

2-2. View 만들기

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>

<c:set var="path" value="${ pageContext.request.contextPath }" />
<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<link rel="stylesheet" href="/resources/assets/css/style.css">
	<title>전자결재</title>
</head>
<body>

	<div class="approval-container">
		<div class="document">
			<h3>전자결재 작성</h3>
		</div>
		<div class="approval-form">
			<form id="appCreate-form">
				<label for="seq">글 번호</label>
				<input type="text" id="seq" name="appSeq" value="${nextSeq }" readonly>
				<label for="writer">작성자</label>
				<input type="text" id="writer" value="${sessionName }" readonly>
				<label for="title">글 제목</label>
				<input type="text" id="title" name="appTitle">
				<label for="content">내용</label>
				<textarea id="content" name="appContent" rows="4"></textarea>
				<input type="hidden" name="id" value="${sessionId }">
				<button onclick="appCreateBtn(event)">등록</button>
				<button id="subCreate-Btn">임시 저장</button>
			</form>
		</div>
	</div>

</body>
</html>

서버로 결재글 폼을 제출하는데 appSeq, appTitle, content, id, writer 입니다. 하지만 writer는 사용자가 보기 좋게 하기 위해 쓴 것이라 서버에서는 writer를 제외한 데이터만 이용할 예정입니다.

2-3. JQuery 작성

	<script src="https://code.jquery.com/jquery-3.7.1.js" integrity="sha256-eKhayi8LEQwp4NKxN+CfCh+3qOVUtJn3QNZ0TciWLP4=" crossorigin="anonymous"></script>
	<script>
		function appCreateBtn(event){
			event.preventDefault();
			
			var titleLength = $("#title").val().length;
			var contentLength = $("#content").val().length;
			
			if(titleLength == 0 && contentLength > 0){
				alert("제목을 입력하세요.");
				$("#title").focus();
			} else if (titleLength > 0 && contentLength == 0){
				alert("내용을 입력하세요.");
				$("#content").focus();
			} else if(titleLength == 0 && contentLength == 0){
				alert("글을 작성하지 않았습니다.");
				$("#title").focus();
			} else {
				$("#appCreate-form").attr({
					'action' : '/create',
					'method' : 'post'
				}).submit();
			}
		}
	</script>

appTitle과 appContent는 not null인 요소이기 때문에 적었는지 안 적었는지 체크를 해주는 코딩을 했습니다. 

2-4. Controller 작성

	@RequestMapping(value = "/create", method = RequestMethod.GET)
	public String create(HttpServletRequest request, RedirectAttributes redirectAttributes, Model model) {
		HttpSession session = request.getSession();
		String sessionId = (String) session.getAttribute("sessionId");

		if (sessionId == null) {
			redirectAttributes.addFlashAttribute("msg", "로그인 하세요");
			return "redirect:/login";
		}

		// 다음 글 SEQ 조회
		int nextSeq = appSer.getNextSeq();
		model.addAttribute("nextSeq", nextSeq);

		return "appPage";
	}

	@RequestMapping(value = "/create", method = RequestMethod.POST)
	public String createPost(@RequestParam Map<String, Object> param) {
		appSer.createApp(param);
		return "redirect:/list";
	}

2-2를 보면 newSeq가 있을 것입니다. 이는 글을 작성할 때, 이 글이 갖게 될 번호를 보이게 해달라는 요구조건에 맞춰서 미리 조회를 해주는 메소드를 이용해 addAttribute 해주는 코드입니다.
위 Controller에 등장하는 appSer의 메소드는 getNextSql()와 createApp()이 있습니다. 둘 다 같이 진행 하도록 하겠습니다.

2-5. Service 작성

// App Service 
public interface AppService {
	
	int getNextSeq();
	
	void createApp(Map<String, Object> param);
}

// App Service Impl
@Service
public class AppServiceImpl implements AppService{
	
	@Autowired
	private AppDao appDao;
	
	@Autowired
	private MemDao memDao;
	
	@Override
	public int getNextSeq() {
		return appDao.getNextSeq();
	}
	
	@Override
	public void createApp(Map<String, Object> param) {
		int appSeq = Integer.parseInt(param.get("appSeq").toString());
		String appTitle = param.get("appTitle").toString();
		String appContent = param.get("appContent").toString();
		
		Map<String, Object> appMap = new HashMap<String, Object>();
		appMap.put("appSeq", appSeq);
		appMap.put("appTitle", appTitle);
		appMap.put("appContent", appContent);

		Map<String, Object> memInfo = memDao.getMemInfo(param);
		int writerSeq = Integer.parseInt(memInfo.get("memSeq").toString());
		String writerPst = memInfo.get("memPosition").toString();
		
		appMap.put("writerSeq", writerSeq);
		if("부장".equals(writerPst)) {
			appMap.put("apperSeq", writerSeq);
			appMap.put("appStatus", "C");
		} else if("과장".equals(writerPst)){
			appMap.put("apperSeq", writerSeq);
			appMap.put("appStatus", "B");
		} else {
			appMap.put("appStatus", "A");
		}
		
		appDao.createApp(appMap);
	}

}

지난 시간에 설계해줬던 DB를 보면 우리가 이번에 View단에서 넘겨받은 것들을 제외하고 reg_date, upt_date, writer_seq, app_status, apper_seq가 있을 것입니다. upt_date는 실제 글을 작성할 땐 사용하지 않으니 빼주고, apper_seq는 과장이나 부장이 결재글을 작성할 때 apper_seq에 같이 들어가야 하니 글쓴이의 직급을 이용해주도록 합니다. (결재대기 > A, 결재중 > B, 결재완료 > C) 

2-6. Dao 작성

// App Dao
package com.JoAriTB.EApprov.approval;

import java.util.List;
import java.util.Map;

public interface AppDao {
	
	int getNextSeq();
	
	void createApp(Map<String, Object> param);
}

// App Dao Impl
@Repository
public class AppDaoImpl implements AppDao{
	
	@Autowired
	private SqlSessionTemplate sqlSession;
	
	@Override
	public int getNextSeq() {
		return sqlSession.selectOne("AppMapper.getNextSeq");
	}
	
	@Override
	public void createApp(Map<String, Object> param) {
		sqlSession.insert("AppMapper.createApp", param);
	}
}

 

2-7. Mapper 작성

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">


<mapper namespace="AppMapper">
	<resultMap id="App" type="java.util.HashMap">
		<result column="APP_SEQ" property="appSeq" />
		<result column="APP_TITLE" property="appTitle" />
		<result column="APP_CONTENT" property="appContent" />
		<result column="APP_STATUS" property="appStatus" />
		<result column="REG_DATE" property="regDate" />
		
		<result column="UPT_DATE" property="uptDate" />
		<result column="WRITER_SEQ" property="writerSeq" />
		<result column="APPER_SEQ" property="apperSeq" />
	</resultMap>
	
	<select id="getNextSeq" resultType="int">
		select 
			nvl(max(app_seq), 0) + 1 nextSeq
		from e_approval_app
	</select>
	
	<insert id="createApp">
		insert into e_approval_app(
		    app_seq,
		    app_title,
		    app_content,
		    reg_date,
		    app_status,
		    writer_seq
		    <if test="apperSeq != null">
		    , apper_seq
		    </if>
		) values (
			#{appSeq},
			#{appTitle},
			#{appContent},
			sysdate,
			#{appStatus},
			#{writerSeq}
			<if test="apperSeq != null">
			, #{apperSeq}
			</if>
		)
	</insert>
</mapper>

nvl과 max를 통해 지금 DB에 있는 결재글의 마지막 글의 Seq를 조회해주도록 합니다. 그리고 결재글이 없을 땐 0, 마지막에 +1을 통해 그 다음 번호를 부여받을 수 있도록 해줍니다.
결재글 작성하는 순간은 sysdate를 통해서 넣어주도록 합니다. 2-5에서 reg_date를 빼준 이유입니다. 또한 if문을 통해 apper_seq가 없을 경우는 빼주도록 해줍니다.

3. 결과물

부장 ~ 사원

결과물

 

후기

이번에는 다른 것들은 어렵지 않았지만 MyBatis의 if문을 몰라서 꽤나 고생했습니다. Column이 Map변수의 키값에 없으면 자연스레 null로 들어갈 것이라고 생각했는데 그렇지 않고 부적합한 열유형 에러를 띄우니 처음엔 왜 이러는지 몰랐다가 if문을 생각해내고 해결할 수 있게 됐습니다. 
지금 생각해보면 당연히 그래줘야 하는데 무슨 정신으로 그랬는지... 이 때 드는 의문점은 appMap.put("apperSeq", null);을 해줬는데 이 때는 왜 여전히 안 됐는지 아직도 의문입니다..
반응형
댓글