1213 개발일지

Yesol Lee·2021년 12월 13일
0

개발일지 - 2021

목록 보기
6/20

오늘 한 일

출장 목록 화면의 페이지네이션, 출장 테이블 웹에서 CRUD 완료, egovGnrService 이용해 ID 생성, ibatis를 mybatis로 변경

select

기존 전자정부프레임워크의 예제 코드를 바탕으로 게시물 상세 조회 기능을 구현했다. 처음 생각하기론 목록 조회, 상세조회, 생성 및 수정 페이지 총 3개가 필요할 줄 알았다. 그런데 막상 코드를 보니 register.jsp 파일이 하나 있고 파일 조회 및 수정 시에는 updateView.do로 접근해서 분기처리를 하고, 생성 시에는 add.do로 접근하는 것을 알게 되었다.

selectList.jsp

  1. 게시물 번호나 장소 클릭 시 개별 게시물로 이동하도록 js select()함수에 게시물 id값 전달
/* 글 수정 화면 function */
function fn_egov_select(id) {
  document.listForm.selectedId.value = id;
  document.listForm.action = "<c:url value='/updateBtView.do'/>";
  document.listForm.submit();
}
<c:forEach var="result" items="${resultList}" varStatus="status">
  <tr>
    <td align="center" class="listtd"><a href="javascript:fn_egov_select('<c:out value="${result.btId}"/>')"><c:out value="${paginationInfo.totalRecordCount+1 - ((searchVO.pageIndex-1) * searchVO.pageSize + status.count)}"/></a></td>                      
    <td align="left" class="listtd"><a href="javascript:fn_egov_select('<c:out value="${result.btId}"/>')"><c:out value="${result.location}"/></a></td>
    <td align="center" class="listtd"><c:out value="${result.authorId}"/>&nbsp;</td>
    <td align="center" class="listtd"><c:out value="${result.travelerId}"/>&nbsp;</td>
    <td align="center" class="listtd"><c:out value="${result.tripStartDate} ~ ${result.tripEndDate}"/>&nbsp;</td>
  </tr>
</c:forEach>

register.jsp

  1. registerFlag 기준 변경: <meta>태그 아래 변경 -> btVO의 btId가 빈 값이면 create, 있으면 modify
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <c:set var="registerFlag" value="${empty btVO.btId ? 'create' : 'modify'}"/>
  1. table 안 form의 content 연결해주기
<tr>
  <td class="tbtd_caption"><label for="title">제목</label></td>
  <td class="tbtd_content">
    <form:input path="title" maxlength="30" cssClass="txt"/>
    &nbsp;<form:errors path="title" />
  </td>
</tr>

Controller.java

  1. selectList.jsp -> updateView.do로 이동: updateView는 기존 게시물 id값을 받아 새로운 VO 생성 후 setId()로 값 넣고 그걸 registerBt.jsp페이지로 보내준다. 나는 처음에 select()부분을 빠뜨려서 조회 실행이 안 됐다.

err: Neither BindingResult nor plain target object for bean name available as request attribute

@RequestMapping("/updateBtView.do")
public String updateBtView(@RequestParam("selectedId") String id, @ModelAttribute("searchVO") SampleDefaultVO searchVO, Model model) throws Exception {
	BtVO btVO = new BtVO();
	btVO.setBtId(id);
	// selectBt() 메소드는 이 컨트롤러 내의 select 메소드이다
	model.addAttribute(selectBt(btVO, searchVO));
	return "registerBt";
}

public BtVO selectBt(BtVO btVO, @ModelAttribute("searchVO") SampleDefaultVO searchVO) throws Exception {
	System.out.print("글 조회 결과 = "+ btService.selectBt(btVO).toString());
	return btService.selectBt(btVO);
}

SQL.xml

글 조회는 되는데 일부 속성이 null로 뜨며 조회가 안 되는 문제가 있었다. 찾아보니 SQL 변수명을 제대로 맞추지 않아서 생기는 문제였다.

  • 변수명 정리: property 부분은 java스타일의 camelCase로, column 부분은 db에서 사용한 스네이크 스타일로 통일해주었다.
  • 내가 DB에 맨 마지막에 추가한 속성이 있었는데, 그걸 resultMap에 적지 않아서 값을 불러오지 못하는 문제가 있었다.
  • 콘솔 에러에 SQL 관련 문제라는 말이 있으면 쿼리를 한 번 더 확인해 볼 필요가 있다.
<resultMap id="btVO" class="egovframework.example.sample.service.BtVO">
  <result property="btId" column="bt_id"/>
  <result property="travelerId" column="traveler_id"/>
  <result property="title" column="title"/>
  <result property="location" column="location"/>
  ...
</resultMap>

update

register.jsp

  • register 페이지에서 게시물 조회 후 '수정' 버튼을 누르면 update가 된다.
  • '수정'버튼은 js 함수 fn_fn_egov_save()와 연결되어있다.
  • validateBtVO는 어디서 온 함수인지 모르겠으나 validate.xml 설정파일과 관련이 있어 보였다.
/* 글 등록 function */
function fn_egov_save() {
  frm = document.detailForm;
  if(!validateBtVO(frm)){
    return;
  }else{
    frm.action = "<c:url value="${registerFlag == 'create' ? '/addBt.do' : '/updateBt.do'}"/>";
    frm.submit();
  }
}

validate.xml

  • 기존 예제의 sampleVO 설정을 복사해 바로 밑에 새로 추가했다. 이게 주석처리 된 상태에서도 update는 되는데 이걸 넣어 놓으면 required로 표시한 속성값을 적지 않고 생성하려 할 때 경고문을 보여주고, 아래 에러가 안 뜨는 효과가 있다.

    WARN [org.apache.commons.validator.ValidatorResources] Form 'btVO' not found for locale 'ko_KR'

<form-validation>
    <formset>
        <form name="btVO">
        	<field property="travelerId" depends="required">
        		<arg0 key="출장자 이름" />
			</field>
			<field property="location" depends="required">
        		<arg0 key="출장 장소" />
			</field>
        </form>
    </formset>
</form-validation>

delete

register.jsp

  • update가 되고 나니 delete는 거의 바로 된 것 같다. 아무런 경고 메시지없이 게시물을 삭제해버리길래 javascript 메소드에 confirm()을 이용해 한 단계를 추가해주었다.
/* 글 삭제 function */
function fn_egov_delete() {
  if(confirm("정말 삭제하시겠습니까?")==true) {
    document.detailForm.action = "<c:url value='/deleteBt.do'/>";
    document.detailForm.submit();
  } else {
    return;
  }
}

insert

egovIdGnrService 사용: ID값 생성하기

DB 테이블에 보통 index로 사용할 고유 id값을 가지는 경우가 많은데, id를 특정 규칙에 따라 자동으로 생성해주는 egov 라이브러리가 있다. 정리가 잘 된 이 블로그를 참고했다.

1. DB에 ids 테이블 만들기

egov 기본 예제를 따라해봤다면 ids, sample 2개 테이블을 만들었을 것이다. 이 때 ids가 egovIdGnrService를 이용하기 위한 테이블이다. 나는 생성값이 1개가 필요하기 때문에 1개 값을 넣었다.

insert into ids values("BT", 40);

2.context-idgen.xml 수정: tableName에 고유값 생성할 테이블명 적기

기존에 있던 egovGnrService를 복사해서 아래에 새로운 서비스를 만들어주었다. 서비스 아래 Prefix를 설정하는 부분의 이름을 strategy에 적어서 연결해준다.

  • strategy: 고유값 생성규칙 이름
  • table: 고유값 생성에 이용할 테이블명. 여기서는 ids를 이용함
  • tableName: 고유값을 생성해서 사용할 테이블명
  • prefix: 고유값 생성 시 고정으로 들어갈 문자열
<bean name="btIdGnrService" class="egovframework.rte.fdl.idgnr.impl.EgovTableIdGnrServiceImpl" destroy-method="destroy">
  <property name="dataSource" ref="dataSource" />
  <property name="strategy" 	ref="mixPrefixBt" />
  <property name="blockSize" 	value="10"/>
  <property name="table"	   	value="IDS"/>
  <property name="tableName"	value="TBBUSINESSTRIP"/>
</bean>
<bean name="mixPrefixBt" class="egovframework.rte.fdl.idgnr.impl.strategy.EgovIdGnrStrategyImpl">
  <property name="prefix"   value="BT-" />
  <property name="cipers"   value="5" />
  <property name="fillChar" value="0" />
</bean>
  • 고유값 생성 예시: BT-00000, BT-00001 ...

3. serviceImpl.java 수정: idgen.xml파일에 bean name에 적은 이름 똑같이 name에 적어주기

  • id 생성할 EgovIdGnrService 인스턴스 선언 (idgen.xml파일의 bean name과 맞춰주기)
  • insert() 메서드 안 생성 부분 인스턴스명 변경
@Resource(name = "btIdGnrService")
private EgovIdGnrService btIdGnrService;

@Override
	public String insertBt(BtVO vo) throws Exception {
	//LOGGER.debug(vo.toString());
    
	/** ID Generation Service */
	String bt_id = btIdGnrService.getNextStringId();
	vo.setBtId(bt_id);
    
	btDAO.insertBt(vo);
	return bt_id;
}

Controller.java

  • 목록 창에서 '생성' 버튼을 누르면 add.do로 이동한다. 예제파일의 controller에는 add.do 주소에 매핑되는 메소드가 2개 있는데, requestMethod가 GET, POST로 다르다. 처음 생각엔 빈 입력창을 띄우는 것이 GET, 그걸 저장하는 것이 POST 메소드일거라고 생각했는데 이상하게 빈 값으로 계속 저장이 되는 문제가 있었는데, 로그를 찍어보니 add.do 주소 요청이 오면 무조건 POST 메소드로 가는 것을 알게 되었다.
  • 매핑 주소를 addView.do로 바꿔주어도 같은 문제가 있었는데, method를 POST로 바꿔주니 입력창으로 들어갈 수 있었다.
@RequestMapping(value = "/addBt.do", method = RequestMethod.POST)
public String addBt(@ModelAttribute("searchVO") SampleDefaultVO searchVO, BtVO btVO, BindingResult bindingResult, Model model, SessionStatus status)
			throws Exception {

	// Server-Side Validation
	beanValidator.validate(btVO, bindingResult);

	if (bindingResult.hasErrors()) {
		model.addAttribute("btVO", btVO);
		return "selectBtList";
	}

	btService.insertBt(btVO);
	status.setComplete();
	return "forward:/selectBtList.do";
}

SQL.xml

  • 입력창으로 들어온 후 값을 입력하고 생성 버튼을 누르면 등록이 되는데, 등록한 게시물을 다시 조회하려고 하면 오류가 났다. db에서 직접 넣은 게시물들은 조회가 잘 되는데, 웹사이트에서 넣은 데이터가 조회되지 않는 것이다.
  • 문제는 넣어야 할 값들을 넣지 않아서였다. 내 테이블의 일부 값은 사용자가 직접 입력하는 것이 아니라 default값, 혹은 now()를 이용해 현재 시간을 넣는 등 자동으로 넣어줘야 했는데, 그 부분을 처리하지 않은 것이다.
  • SQL.xml에서 now()가 작동할지 걱정이었는데 아무런 문제없이 잘 들어갔다.
<insert id="btDAO.insertBt">
  <![CDATA[

   INSERT INTO TBBUSINESSTRIP
    ( BT_ID
		...
      , APPROVAL_STATE
      , CREATED_AT )
   VALUES ( #btId#
		...
      , 0
      , NOW())
  ]]>
</insert>

ibatis를 mybatis로 변경

이 블로그를 참고했다.
egov 기본 예제를 사용하고 있다면, 기본으로는 ibatis를 사용하고 있지만 mapper와 같은 mybatis 파일들도 미리 만들어져 있을 것이다. 블로그에 따르면 전자정부프레임워크에는 ibatis나 mybatis dependency 설정을 따로 하지 않아도 알아서 연결되어 있다고 하니 pom.xml은 패스했다.

1. mappers/SQL.xml 작성

ibatis 방식으로 작성한 sql코드와 거의 비슷한데 약간 다른 점이 있다.

  • <sqlMap namespace> -> <mapper namespace>로 변경: impl 폴더 안에 생성한 mapper 파일 이름 넣기
<!-- ibatis -->
<sqlMap namespace="Sample">
  <typeAlias  alias="btVO" type="egovframework.example.sample.service.BtVO"/>
  
<!-- myBatis -->
<mapper namespace="egovframework.example.sample.service.impl.BtMapper">

- resultMap의 id 옆에 class -> type으로 변경
<!-- ibatis -->
<resultMap id="btVO" class="egovframework.example.sample.service.BtVO">
  
<!-- myBatis -->
<resultMap id="btVO" type="egovframework.example.sample.service.BtVO">
  • 쿼리문에 java 변수 가져오는 방식 변경: #변수명# -> #{변수명}
-- ibatis
DELETE FROM TBBUSINESSTRIP WHERE BT_ID=#btId#
  
-- myBatis
DELETE FROM TBBUSINESSTRIP WHERE BT_ID=#{btId}

sql문을 작성할 때 where 조건이나 검색 조건 등에 들어가는 변수명들도 mybatis 형식으로 빠짐없이 잘 바꾸어 주어야 한다. 나는 몇 개 빠뜨려서 sql 오류가 몇 번 났다..

2. impl/Mapper.java 작성

기존 DAO를 만들 때 사용했던 메소드 명을 그대로 사용하면 추후 serviceImpl에서 dao를 변경할 때 편리하다. 예시파일의 mapper를 복사해서 이름만 바꾸어 주었다.

3. serviceImpl.java: dao -> mapper 변경

예제 파일을 복사해서 사용한다면 이미 mapper 선언 부분이 주석으로 작성되어 있을 것이다. ibatis 부분을 주석처리하고 mybatis 부분을 주석 해제한 후 변수명을 바꾸어 준다. mapper의 이름을 기존에 사용하던 dao의 인스턴스명으로 설정하면 그 아래 코드 변경없이 바로 사용할 수 있다.

/** SampleDAO */
// TODO ibatis 사용
//	@Resource(name = "btDAO")
//	private BtDAO btDAO;

	// TODO mybatis 사용
	@Resource(name="btMapper")
	private BtMapper btDAO;
    
@Override
public void updateBt(BtVO vo) throws Exception {
	btDAO.updateBt(vo); // mapper 객체로 바뀌어있음
}
profile
문서화를 좋아하는 개발자

0개의 댓글