본문 바로가기

반응형
죄송합니다. 며칠 동안 몸살감기와 장염이 겹쳐서 앓아누웠었습니다... 다시 성실히 포스팅하도록 하겠습니다.

이번 시간에는 업로드 기능만 구현했던 글쓰기 화면을 요구조건에 마저 꾸미도록 하겠습니다.

요구조건

  • 체크박스 비활성화 & 결재 상태에 따라 체크 표시 하기
  • 게시글 다음 번호 자동으로 노출(완)
  • 작성자 자동으로 노출(완)
  • 버튼 제어
    • 등록자 : 임시 저장, 결재 버튼
    • 결재자 : 반려, 결재 버튼만 노출
  • 히스토리 영역
    • 결재 상태 변화 시 내용 추가
    • 번호 : 각 게시글 마다 1번부터 새로 발급

다음은 위 요구조건을 바탕으로 이번 시간에 구현할 순서입니다.

업로드 시 히스토리 생성 > 체크박스 구현 > 버튼 제어 > 히스토리 영역

히스토리 영역은 지금까지 하나도 건드리지 않은 만큼 내용이 길어질 수 있기에 두 편으로 나눠서 제작하도록 하겠습니다.

 

업로드 시 히스토리 생성 > 체크박스 구현 > 버튼 제어 > 히스토리 영역

히스토리 생성을 구현하기에 앞서 히스토리 테이블의 구성을 살펴보도록 하겠습니다.

  1. 히스토리 PK
  2. 결재글 PK (FK)
  3. 글쓴이 PK (FK)
  4. 결재자 PK (FK)
  5. 결재일
  6. 결재 상태

이 중     에 있는 결재자와 결재일은 Nullable Yes인 Column입니다. 히스토리 목록에 작성일은 들어가지 않으므로 Reg_Date는 넣어주지 않았습니다.

1-1. Service 작성

데이터 가공을 하기 위해 이전에 결재글 작성할 때의 데이터 가공한 것을 참고해 보겠습니다.
먼저 넣어야 할 데이터를 비교해 보겠습니다.

Approval History

App PK
App Title
App Content
Writer
Apper
App Date
Reg Date
App Status
His PK
App PK


Writer
Apper
App Date

App Status

위 표에서 보셨듯이 History에 입력되는 것들은 모두 결재글을 작성할 때에 포함됐던 것들입니다. 그러므로 결재글 가공한 코드를 참고해서 가공해주도록 하겠습니다.

결재글 가공

위 그림에서 빨간 표시를 한 것들이 History에도 들어갈 것들입니다. 모두 겹치므로 App Service에 작성할 수 있겠지만, 두 번 가공하는 것은 불필요하다고 생각하기 때문에 App Service의 메소드에서 His Dao의 메소드로 가공데이터를 넘기도록 하겠습니다.

/*                App Service                */

// App Service Impl
    @Autowired
    private HisDao hisDao;
    
    @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);
        hisDao.createHis(appMap);
    }

 

1-2. Dao 작성

/*                His Dao                */

// His Dao
public interface HisDao {
	
	void createHis(Map<String, Object> param);

}

// His Dao Impl
@Repository
public class HisDaoImpl implements HisDao{
	
    @Autowired
    private SqlSessionTemplate sqlSession;

    @Override
    public void createHis(Map<String, Object> param) {
    	sqlSession.insert("HisMapper.createHis", param);
    }

}

 

1-3. Mapper 작성

<!-- HisMapper 생성 -->
<?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="HisMapper">
    <resultMap id="History" type="java.util.HashMap">
        <result column="HIS_SEQ" property="hisSeq" />
        <result column="APP_SEQ" property="appSeq" />
        <result column="WRITER_SEQ" property="writerSeq" />
        <result column="APPER_SEQ" property="apperSeq" />
        <result column="APP_DATE" property="appDate" />
        <result column="APP_STATUS" property="appStatus" />
    </resultMap>
	
    <insert id="createHis">
        insert into e_approval_history (
            his_seq,
            app_seq,
            writer_seq,
            <if test="apperSeq != null">
                apper_seq,
                app_date,
            </if>
            app_status
        ) values (
            (select nvl(max(his_seq), 0) + 1 from e_approv_history),
            #{appSeq},
            #{writerSeq},
            <if test="apperSeq != null">
                #{apperSeq},
                sysdate,
            </if>
            #{appStatus}
        )
    </insert>
	
</mapper>

사원과 대리가 작성해서 생긴 히스토리에는 결재자가 없으므로 결제자 정보가 있는지 체크를 해줍니다(<if></if>) . 
그리고 History의 PK와 결재일(App_Date)은 가장 큰 숫자+1(존재하지 않을 시 0+1)해준 값과 현재 시간을 넣어주도록 합니다. 

1-4. 결과물

DB 결과물

 

업로드 시 히스토리 생성 > 체크박스 구현 > 버튼 제어 > 히스토리 영역

체크박스를 구현한 것을 확인하기 위해서 필요한 조건은 해당 게시물로 들어갈 수 있어야 한다는 것입니다. 해당 기능부터 구현하도록 하겠습니다.

2-1. list.jsp

<tr class="row-link" data-href="${path }/read/${appList.appSeq}">

게시물 제목같은 것들이 아닌 그 게시물 영역 자체를 누르면 이동할 수 있게 해주겠습니다.

2-2. JQeury

$(document).on('click', '.row-link', function() {
	window.location = $(this).data("href");
});

 

2-3. appPage.jsp

<c:choose>
    <c:when test="${empty appInfo }">
        <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>
    </c:when>
    <c:otherwise>
        <div class="approval-container">
            <table>
                <thead>
                    <tr>
                        <th>결재자</th>
                        <th>과장</th>
                        <th>부장</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td><input type="checkbox" id="prop" disabled></td>
                        <td><input type="checkbox" id="ing" disabled></td>
                        <td><input type="checkbox" id="done" disabled></td>
                    </tr>
                </tbody>
            </table>
        </div>
        <div class="approval-container">
            <div class="document">
            	<h3>전자결재</h3>
            </div>
            <div class="approval-form">
                <form id="appPrcForm">
                    <label for='seqU'>번호: </label>
                    <input type="text" id="seqU" name="appSeq" value="${appInfo.appSeq }" readonly>
                    <label for="writerU">작성자 : </label>
                    <input type="text" id="writerU" name="writer" value="${appInfo.writerName }" readonly>
                    <label for="appTitleU">제목 : </label>
                    <input type="text" id="appTitleU" name="appTitle" value="${appInfo.appTitle }" readonly>
                    <label for="contentU">내용</label>
                    <textarea id="contentU" name="appContent" readonly>${appInfo.appContent }</textarea>
                    <button id="replyBtn">반려</button>
                    <button id="appBtn">결재</button>
                    <input type="hidden" id="appStatus"  name="appStatus" value="${appInfo.appStatus }">
                </form>
            </div>
        </div>
        <div class="approval-container">
            <div class="document">
            	<h3>히스토리</h3>
            </div>
            <div class="approval-form">

            </div>
        </div>
    </c:otherwise>
</c:choose>

appInfo의 존재여부를 기준으로 글쓰기 화면으로 쓸 것인지, 글읽기 화면으로 쓸 것인지 결정해주는 코드입니다.

현재 히스토리는 구역만 만들어 주도록 하겠습니다.

2-4. JQeury 작성

$(function(){

    var sttVal = $("#appStatus").val();

    if(sttVal === '결재 대기'){
    	$("#prop").attr("checked",true);
    } else if(sttVal ==='결재 중'){
        $("#prop").attr("checked",true);
        $("#ing").attr("checked",true);
    } else if(sttVal === '결재 완료'){
        $("#prop").attr("checked",true);
        $("#ing").attr("checked",true);
        $("#done").attr("checked",true);
    }

})

#appStatus의 값을 이용하여 체크박스의 체크 상태를 바꿔주도록 하겠습니다.

2-5. Controller

	@RequestMapping(value="/read/{seq}", method = RequestMethod.GET)
	public String read(@PathVariable("seq") int seq,HttpServletRequest request, 
			Model model, RedirectAttributes redirectAttributes) {
		Map<String, Object> appInfo = appSer.getAppInfo(seq);
		model.addAttribute("appInfo", appInfo);
		
		HttpSession session = request.getSession();
		String sessionId = (String) session.getAttribute("sessionId");

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

글 정보를 불러오는 getAppInfo() 메소드를 적어줍니다.

2-6. Service

// App Service
	Map<String, Object> getAppInfo(int seq);

// App Service Impl
	@Override
	public Map<String, Object> getAppInfo(int seq){
		return appDao.getAppInfo(seq);
	}

 

2-7. Dao

// App Dao
	Map<String, Object> getAppInfo(int seq);

// App Dao Impl
	@Override
	public Map<String, Object> getAppInfo(int seq){
		return sqlSession.selectOne("AppMapper.getAppInfo", seq);
	}

 

2-8. Mapper

<select id="getAppInfo" resultMap="App">
    select
        eaa.app_seq,
        eaa.app_title,
        eaa.app_content,
        eaa.reg_date,
        decode(eaa.app_status,'A','결재 대기','B','결재 중','C','결재 완료','W','임시 저장','X','반려') app_status,
        eamw.mem_name writer_name,
        eama.mem_name apper_name
    from e_approval_app eaa
    left join e_approval_member eamw on eaa.writer_seq = eamw.mem_seq
    left join e_approval_member eama on eaa.apper_seq = eama.mem_seq
    where eaa.app_seq = #{seq}
</select>

결재글 PK(app_seq)값을 이용해서 특정 게시물을 조회하는 쿼리문을 짜주겠습니다.

2-9. 결과물

오늘은 글쓰기 화면 구현에서 히스토리 입력과 체크박스를 이용한 결재상태 노출 기능을 구현했습니다. 히스토리는 전혀 건들지를 않았었기 때문에 내용이 길어졌습니다. 다음엔 나머지 기능들도 구현하는 시간을 가져보도록 하겠습니다.

감사합니다 빠잇~!

 

후기

오늘 내용은 어려운 것은 없었지만 복잡한 부분이라서 실수하기 쉬운 부분이었지만 글의 내용처럼 하나하나 체크하며 구현해서 실수 없이 잘 구현할 수 있었습니다.

다른 곳에서는 금방 다 구현했지만 히스토리를 입력하는 부분(Controller > Service)에서는 조금 고생을 했습니다. 바로 서버에서 좀 바로바로 빠리하게 작동하지 않는 것이었는데 증상이 다음과 같았습니다.
결재글 입력 > 조회 > 조회 성공시 히스토리 입력 순으로 하려고 했으나 결재글 입력이 완료 되기 전에 조회를 시도해서 조회를 실패하고 히스토리 입력이 안 되는 것이었습니다.... 딜레이를 줘봤으나 2000ms에서 됐다가 안됐다가, 3000ms에서 됐다가 안됐다가... 이런 식이어서 포기를 하고 그냥 위의 코드처럼 확인하는 코드를 빼고 순차적으로 입력했습니다.

순서대로 하면 작동 되는 코드인데 저런 식으로 이상하게 작동하니... 어떻게 해결해야할 지 아직도 속시원하게 해결하지 못한 채로 끝내게 됐네요...

 

반응형
댓글