본문 바로가기
웹개발/전자정부 eGovFrame

[전자정부] eGovFrame 게시판 (3) : Controller, Service, Dao, VO MVC 패턴. 데이터 저장하기

by 졸린이 2024. 7. 24.
반응형

저번에 프로젝트에 Oracle DB를 연동했다.

https://hellodoor.tistory.com/235 관련 내용은 왼쪽 링크에서 확인할 수 있다.

이번에는 사용자에게 데이터 입력을 받고 DB에 저장하는 과정을 Controller, Service, Dao에서 대충 봐본다.

 

뭐 디자인 패턴, MVC 패턴 이러쿵저러쿵하는데 자세히 알 필요는 없다고 생각하지만 또 모르는 것보단 좋을 수 있기 때문에 간략하게 살펴보자면 

 

디자인 패턴이란 프로그램 개발에서 자주 나타나는 과제를 해결하기 위한 방법 중 하나로, 과거의 소프트웨어 개발 과정에서 발견된 설계의 노하우를 축적하여 이름을 붙여, 이후에 재이용하기 좋은 형태로 특정의 규약을 묶어서 정리한 것이다.

라고  위키백과에 설명되어 있다.

 

쉽게 생각해보면 프로그램 개발, 유지보수를 용이하게 하기 위해 요런 처리는 이쪽에서 하고 저런 처리는 저쪽에서 하고 요 데이터는 요런 형태에 담고 뭐 이런 규칙을 만들어 논 것이라고 봐도 된다.

 

그중 MVC 패턴이라고 해서  Model, View, Controller 세 가지로 구성된 디자인 패턴이 스프링 등 다양하게 쓰이는 것으로 알고 있다.

 

Model은 데이터라고 보면 된다. 예로 사용자 이름, 번호. 이름은 문자 번호는 숫자 이런 변수이며 컨트롤러에게 명령받은 대로 DB에 데이터를 처리(select, insert, update, delete)한다.

 

View는 사용자 인터페이스. 그냥 사용자가 보는 화면이라고 보면된다. 사용자는 화면을 보면서 이름과 전화번호 입력하는 칸에 입력을 하고 저장 버튼을 누른다.

 

Controller는 Model과 View에 중간에서 명령을 전달하는 역할을 하는데 View에서 받은 데이터는 Model로 보내 저장을 할 수 있으며 Model에서 보내준 데이터는 View 즉 화면에 띄워 사용자가 볼 수 있게 한다.

 

아무튼 대~충 여기까지 정라히고 전자정부 프레임워크에서 샘플로 생성된 코드로 확인해 보자. 이 포스팅은 전자정부 프레임워크 프로젝트 생성 시에 제공해 주는 샘플 코드를 그대로 따라간다. 먼저 저번 그 상태에서 톰캣을 실행해서 localhost:8080으로 들어간다.

mvc 패턴에서 view에 해당한다. 여기서 등록 버튼을 클릭한다.

Project Explorer에서 확인해보면

webapp 밑에 WEB-INF>jsp>egovframework>sample>egovSampleList.jsp가 있다.

<div id="sysbtn">
 <ul>
  <li>
   <span class="btn_blue_l">
    <a href="javascript:fn_egov_addView();"><spring:message code="button.create" /></a>
    <img src="<c:url value='/images/egovframework/example/btn_bg_r.gif'/>" style="margin-left:6px;" alt=""/>
   </span>
  </li>
 </ul>
</div>

등록으로 되어있는 부분이 jsp에서 저 부분인데 등록이란 글자를 <spring:message code="button.create" /> 로 표기했다. 이건 '등록' 이렇게 직접적으로 텍스트를 넣어도 되지만 예를 들어 화면이 여러 개인 경우 '등록'을 '생성' 이렇게 수정하고 싶을 때 일일이 다 수정하면 번거로우므로 자주 사용하는 텍스트들은 따로 저장해 놓고 수정할 일이 생기면 정의해 놓은 파일만 수정하도록 하기 위함이다.

등록을 생성으로 바꾸고자 한다면 button.create=등록을 -> button.create=생성 이렇게 하면 된다.

뭐 이것에 대해선 나중에 다시 설명하는 걸로 하고 여기까지는 대충 넘어간다.

 

아무튼 중요한 건 저 등록 버튼을 클릭하면 fn_egov_addView() 함수가 실행된다.

/* 글 등록 화면 function */
        function fn_egov_addView() {
           	document.listForm.action = "<c:url value='/addSample.do'/>";
           	document.listForm.submit();
        }

코드를 보면 listForm의 action을 /addSample.do로 설정하고 submit() 한다. mvc패턴에서 controller를 호출한다.

EgovSampleController.java 

@RequestMapping(value = "/addSample.do", method = RequestMethod.POST)
	public String addSample(@ModelAttribute("searchVO") SampleDefaultVO searchVO, SampleVO sampleVO, BindingResult bindingResult, Model model, SessionStatus status)
			throws Exception {

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

		if (bindingResult.hasErrors()) {
			model.addAttribute("sampleVO", sampleVO);
			return "sample/egovSampleRegister";
		}

		sampleService.insertSample(sampleVO);
		status.setComplete();
		return "forward:/egovSampleList.do";
	}

컨트롤러는 클래스 이름 위에 @Controller 어노테이션이 있다. mvc에서 conntroller에 해당된다.

jsp에서 호출한 /addSample.do를 여기 @RequestMapping value 값으로 매칭시켜서 받는다.

 

코드를 보면

beanValidator.validate(sampleVO, bindingResult);

if(bindingResult.hasErrors() {} 

이렇게 되어있는데 validation은 입력이 알맞게 들어왔는지 유효성을 검증하는 것이다.

 

간략히 설명하자면 jsp에서 form commandName이 sampleVO인데 beanValidator로 egovSampleList.jsp에서 넘겨받은 sampleVO 형식이 맞는지 체크하는 것 같다.

 

여기서 VO는 데이터 객체의 구조를 의미한다. 

 

validator.xml의 sampleVO form-validation이 기술되어 있는데 name, description, regUser이 매칭되어야 한다. egovSampleList.jsp의 listForm에는 해당되지 않아 if문 안의 걸리게 되어 return "sample/egovSampleRegister"; 이 실행되어 egovSampleRegister.jsp를 호출하게 된다.

(사실 코드만 보고 대충 파악하는 거라 정확하진 않은데 얼추 맞겠지...)

 

유효성검증이 개발할 때 중요하긴 하지만 여기 샘플코드에서의 의미는 이 컨트롤러의 /addSample.do 호출을 sampleList 화면에서 호출했는지 sampleRegister화면에서 호출했는지 확인해서 분기하기 위함일 뿐이다. 그러므로 대충 그렇게만 이해하고 넘어가자.

 

즉 /addSample.do를 호출을 egovSampleList.jsp에서 호출했기에 egovSampleRegister.jsp를 불러오는 것이다. (뒤에 나오지만 egovSampleRegister.jsp에서 호출했다면 저 if문의 걸리지 않고 다음 명령어를 실행한다.)

<form:form commandName="sampleVO" id="detailForm" name="detailForm">
    ...
    		<tr>
    			<td class="tbtd_caption"><label for="name"><spring:message code="title.sample.name" /></label></td>
    			<td class="tbtd_content">
    				<form:input path="name" maxlength="30" cssClass="txt"/>
    				&nbsp;<form:errors path="name" />
    			</td>
    		</tr>
    		<tr>
    			<td class="tbtd_caption"><label for="useYn"><spring:message code="title.sample.useYn" /></label></td>
    			<td class="tbtd_content">
    				<form:select path="useYn" cssClass="use">
    					<form:option value="Y" label="Yes" />
    					<form:option value="N" label="No" />
    				</form:select>
    			</td>
    		</tr>
    		<tr>
    			<td class="tbtd_caption"><label for="description"><spring:message code="title.sample.description" /></label></td>
    			<td class="tbtd_content">
    				<form:textarea path="description" rows="5" cols="58" />&nbsp;<form:errors path="description" />
                </td>
    		</tr>
    		<tr>
    			<td class="tbtd_caption"><label for="regUser"><spring:message code="title.sample.regUser" /></label></td>
    		</tr>
    ...
</form:form>

egovSampleRegister.jsp의 detailForm commandName="sampleVO"의 구성을 보면 name(카테고리명), description(설명), reguser(등록자)가 있어 validation의 검증이 충족되어 에러로 잡히지 않는다. (if문에 걸리지 않고 다음 명령어 실행)

 

저 카테고리명, 설명, 등록자에 내용을 적고 등록을 누르면 fn_egov_save() 함수를 호출하는데

/* 글 등록 function */
        function fn_egov_save() {
        	frm = document.detailForm;
        	if(!validateSampleVO(frm)){
                return;
            }else{
            	frm.action = "<c:url value="${registerFlag == 'create' ? '/addSample.do' : '/updateSample.do'}"/>";
                frm.submit();
            }
        }

여기서도 validation체크가 있다. form이 SamleVO가 맞는지 체크하고 아니면 return 시키는데 그냥 전자정부 샘플 코드를 보는데도 뭔가 쓸데없이 귀찮게 되어있다. 일단 무시하고 넘어가면 된다.

 

어쨌든 registerFlag가 create면 /addSample.do를 호출하고 아니면 /updateSample.do를 호출하는데 해당 jsp 상단에 보면 

<c:set var="registerFlag" value="${empty sampleVO.id ? 'create' : 'modify'}"/>

이렇게 되어있는데 sampleVO.id가 비어있으면 처음 접근한 걸로 create를 할당해 주게 된다. 

즉 controller에서 sampleVO에 id를 넣어주는 로직이 없었으므로 create가 된다. 그리고 form에 사용자가 적은 내용을 담아서 /addSample.do를 호출한다. 그러면 아까 컨트롤러 코드로 돌아가서

@RequestMapping(value = "/addSample.do", method = RequestMethod.POST)
	public String addSample(@ModelAttribute("searchVO") SampleDefaultVO searchVO, SampleVO sampleVO, BindingResult bindingResult, Model model, SessionStatus status)
			throws Exception {

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

		if (bindingResult.hasErrors()) {
			model.addAttribute("sampleVO", sampleVO);
			return "sample/egovSampleRegister";
		}

		sampleService.insertSample(sampleVO);
		status.setComplete();
		return "forward:/egovSampleList.do";
	}

이젠 if문의 걸리지 않고 sampleService.insertSample(sampleVO);를 호출하게 된다.

EgovSampleService.java interface의 insertSample()를 호출하는데 EgovSampleServiceImpl에서 함수를 재정의 한다.

EgovSampleServiceImpl.java

@Override
	public String insertSample(SampleVO vo) throws Exception {
		LOGGER.debug(vo.toString());

		/** ID Generation Service */
		String id = egovIdGnrService.getNextStringId();
		vo.setId(id);
		LOGGER.debug(vo.toString());

		sampleDAO.insertSample(vo);
		return id;
	}

 

interface를 상속받았기 때문에 @Override로 재정의 해줘야 한다. id를 세팅하고 id와 view에서 넘겨받은 데이터를 dao의 sampleDAO.insertSample(vo)를 호출하여 넘겨준다.

 

SampleDAO.java

public String insertSample(SampleVO vo) throws Exception {
		return (String) insert("sampleDAO.insertSample", vo);
	}

SampleDAO, SampleVO가 mvc에서 model의 해당된다.

dao에서는 EgovAbstractDAO를 상속받아 insert함수를 사용한다.

인자를 두 개 넘기는데 앞부분의 sampleDAO.insertSample은 xml의 쿼리문이고 입력받은 데이터는 vo로 넘긴다. 

 

EgovSample_Sample_SQL.xml

<resultMap id="sample" class="egovframework.example.sample.service.SampleVO">
		<result property="id" column="id"/>
		<result property="name" column="name"/>
		<result property="description" column="description"/>
		<result property="useYn" column="use_yn"/>
		<result property="regUser" column="reg_user"/>
	</resultMap>

	<insert id="sampleDAO.insertSample">
		<![CDATA[
			INSERT INTO SAMPLE 
				( ID
				  , NAME
				  , DESCRIPTION
				  , USE_YN
				  , REG_USER )
			VALUES ( #id#
				  , #name#
				  , #description#
				  , #useYn#
				  , #regUser# )
		]]>
	</insert>

xml의 sampleDAO.insertSample이 실행되어 드디어 쿼리가 돌면서 SAMPLE 테이블의 입력한 데이터가 저장된다. view -> controller -> model 순으로 돌며 데이터가 입력받고 가공되어 처리된 것이다.

다시 컨트롤러로 돌아가서

return forward:/egovSampleList.do

이 명령어가 실행되면서 다시 sampleList 화면으로 돌아간다.

아까 넣은 내용이 들어간 것을 확인할 수 있다.

 

글을 쓰다 보니 뭘 작성한 건지 모르겠다. 기준점을 처음 본 사람도 보고 따라 하며 이해할 수 있게 하려고 했는데 정작 내가 이해를 덜 한 채로 쓰게 되고 설명도 중구난방에다가 뭘 중점적으로 설명하려고 한 건지 알 수가 없다. 하지만 작성한 게 아까워서 일단 등록...

 

spring:message, validation 내용이 많이 부실했던 것 같다. 사실 스프링 구조 자체를 명확히 알지 못해서 좀 제대로 공부하고 와야겠다.

반응형

댓글