이번 시간에는 글쓰기를 구현하도록 하겠습니다.
리스트 페이지의 요구조건을 만족하기 전에 글쓰기가 먼저 선행 되어야 합니다. 이번 시간에는 글을 쓰는 기능을 구현하도록 하겠습니다.
리스트 페이지 > 글 쓰기 페이지
만족해야할 것
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);을 해줬는데 이 때는 왜 여전히 안 됐는지 아직도 의문입니다..
'Spring > 전자 결재 Project' 카테고리의 다른 글
[Spring] 전자결재 : 메인 리스트 화면 2 (2) | 2024.01.04 |
---|---|
[Spring] 전자결재 : 메인 리스트 화면 1 (1) | 2024.01.03 |
[Spring] 전자결재 : 로그인 (0) | 2023.12.28 |
[Spring] 전자 결재 - 데이터베이스 설계 (1) | 2023.12.27 |
[Spring] 전자 결재 - 요구 사항 정리 (0) | 2023.12.26 |