MyBatis+Spring+Boot+eGov 리팩토링 모음
전자정부 표준프레임워크(eGov)에서 Jakarta Commons Validation을 활용한 검증 구현과 AOP 기반 예외처리 전략, 메시지 국제화 연동 방법을 상세히 설명하는 실무 중심 가이드입니다.
MyBatis+Spring+Boot+eGov 파트 (리팩토링)
“순수스프링”은 xml이 web.xml > context-servlet.xml > context-common.xml 순으로 크게 나뉜다.
(“스프링, 스프링부트 비교” 파트 참고)
대부분 부트 사용해서 Java Config 방식을 알면 되지만, 레거시한 프로젝트는 xml 많이 사용하므로 알아두자.
메시지국제화,외부설정,캐시 - XML
(1)메시지 국제화 - XML
context-common.xml에 messageSource빈과 국제화빈 등록,
context-servlet.xml에 해당 인터셉터를 등록
message/meesage-common.properties, *_ko.properties, *_en.properties 를 생성
메시지 국제화는 “핸들러매핑에 인터셉터”를 등록하는 부분이 필요하다.
“디스패처서블릿->HandlerMapping->인터셉터 거쳐서 컨트롤러 진입”을 해야하기 때문! (Locale 위해)
- Java Config나 <mvc:interceptors>(xml)패턴으로 인터셉터를 추가하게 되면 부트는 “모든 핸들러매핑”에 인터셉터를 추가해준다!
XML 코드: (이거 적용함)
resources/spring/context-common.xml
<!-- -->
<!-- 메시지소스와 국제화 -->
<!-- set message source -->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>messages.message-common</value>
</list>
</property>
</bean>
<!-- setting Locale -->
<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"
p:paramName="lang" />
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver" />
resources/spring/context-servlet.xml
<!-- 부트처럼 "모든 핸들러매핑에 인터셉터 등록" 방안 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/*" />
<ref bean = "localeChangeInterceptor" /> <!-- 메시지 국제화 등록 -->
</mvc:interceptor>
</mvc:interceptors>
(2)외부설정, 캐시는 JPA 방식꺼 보기. 이건… 비추
eGov 라이브러리 사용하여 빈 등록해서 XML이나 Java에서 간단히 할 수 있게 지원 중이다.
=> JAVA ex: EgovPropertyServiceImpl 빈 등록하며 setProperties()로 사용할 변수(Map) 넣으면 됨.
근데, 위 방식처럼 application.properties에 변수 등록해서 사용하는게 더 좋을것 같음(할 수 있는게 많으니)
eGov 사용코드(Java Config):
외부설정 (그냥 따로 클래스 사용하는 느낌인디..)
@Configuration
public class EgovConfigProperties {
@Bean(destroyMethod="destroy")
public EgovPropertyServiceImpl propertiesService() {
Map<String, String> properties = new HashMap<>();
properties.put("pageUnit", "10");
properties.put("pageSize", "10");
//
EgovPropertyServiceImpl egovPropertyServiceImpl = new EgovPropertyServiceImpl();
egovPropertyServiceImpl.setProperties(properties);
return egovPropertyServiceImpl;
}
}
//
//test code: 다양한 get메소드 제공
private final EgovPropertyService propertiesService; //빈 가져옴
propertiesService.getInt("pageUnit"); //10
검증(Validation-Jakarta Commons) - XML
웹(JSP) 방식으로 주로 정리 -> API 방식은 “JPA + Boot 파트” 참고
Jakarta Commons Validation 방식을 설명한다. (일반적인 스프링 제공 검증 방식이 아님)
⇒ JPA 플젝은 다른 방식 (Bean Validation)
API의 경우 클라쪽 “검증”은 서버가 할 일이 아니다(JS는 프론트쪽 개발진이 해야지!)
웹의 경우 클라와 서버쪽 둘 다 “검증”해주는게 좋다.API의 “예외”의 경우 서버는 JSON으로 변경된 데이터를 “Valid(검증)”하는거라서 JSON→DTO매핑될 때 에러나 그 시점에 다양한 에러(주로 서비스로직)들은 “Exception(예외)”으로 해결! => 웹은 “검증”만으로 충분하지만 API는 “검증+예외”가 필요!
=> 근데, 막상 해보니 웹&API 둘다 “검증+예외”를 적용 했다. eGov에선 웹에 에러페이지만 연동해주는게 아니라 “예외”까지 굳이 하더라?eGov가이드에선 계속 Jakarta Valid 방식을 사용하더라.
순수스프링의 Bean Validation 방식이 내가하던 기존 방식이고, 자카트라 방식과는 조금 다름.
Bean Vaildation 방식 예시:
- DTO 클래스에 검증 어노테이션(
@NotNull
등) 적용하여 검증기에게 규칙 전달- 컨트롤러에서
@Validated
어노테이션으로 검증 활성화 (스프링프레임워크의 LocalValidatorFactoryBean이 사용됨)BindingResult
로 검증 결과 수집- 오류출력: 타임리프는 th:error로 자동으로 bindingresult에서 해당필드 검증 오류있나 체크해서 등록해둔 오류메시지-@NotNull(“이미지가 없습니다”)를 출력
JSP는 form:error로 할 수 있고,
직접 bindingresult를 가져와 사용해도 된다.
자카트라는 JSP에 validator 태그 사용시 “자동으로 JS검증 코드 생성”해줌. 즉, 클라쪽도 검증 자동 지원.
=> 원래 Bean Validation방식은 컨트롤러 쪽에서 검증(서버단)하고 클라에선 직접 JS검증 코드 만들었었음.
빈은 스프링 모듈의 DefaultBeanValidator, DefaultValidatorFactory 사용
Jakarta Commons Validation 방식 예시:
-
제공된 validator-rules.xml 사용 및 기본제공 룰 말고 커스텀 룰 추가하는법: 공문
-
validation.xml, validator.jsp, URL매핑 : 공통 규칙 정의 (서버+클라이언트)
-
validation.xml로 원하는 규칙 정의 + 원하는 메시지 설정(=5번에서 설명) message-common.properties
-
context-common.xml에 빈 등록 - DefaultBeanValidator, DefaultValidatorFactory
-
context-servlet.xml이나 컨트롤러에 validator.jsp URL등록 (태그로 JS검증 코드 자동생성 목적)
부트는 그냥 “컨트롤러”에서 해라!! 자동 설정이랑 겹쳐서 머리아프다.참고: validator.jsp -> “/validator.do” 와 매핑 (자동으로 룰에 따른 메소드가 생성되는것 같더라)
<!-- validator.jsp --> <%@ page language="java" contentType="javascript/x-javascript" %> <%@ taglib prefix="validator" uri="http://www.springmodules.org/tags/commons-validator" %> <validator:javascript dynamicJavascript="false" staticJavascript="true"/>
-
-
JSP validator 태그 적용해보기 -> 클라단 검증
적용 예시: 버튼 type을 submit이 아닌 button으로 하여 js함수(=test) 검증 후 submit하도록 하기!
<%@ taglib prefix="validator" uri="http://www.springmodules.org/tags/commons-validator" %> <!-- ...등 import --> <script type="text/javascript" src="<c:url value="/validator.do"/>"></script> <validator:javascript formName="updateItemDto" staticJavascript="false" xhtml="true" cdata="false"/> <script type="text/javascript"> function test(form){ if(!validateUpdateItemDto(form)){ //자동 생성된 검증 메소드 사용한 것 return; }else{ form.submit(); //valid 통과! } } </script> <button class="btn btn-light m-0" type="button" onclick="redirectSavedBgm(), test(this.form)" .../>
-
Spring MVC Controller 적용해보기 -> 서버단 검증
적용 예시: form taglib를 활용하자 + modelAttribute필수 + form:error필수
private final DefaultBeanValidator beanValidator; //빈 주입 @PostMapping("item/{itemId}") public String studioIdUpdate(@ModelAttribute UpdateItemDto form, Model model) throws Exception { beanValidator.validate(form, bindingResult); //@Validated 미사용은 직접 해줘야 함. if(bindingResult.hasErrors()) return 수정폼;
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> <form:form action="" method="post" modelAttribute="updateItemDto"> <input type="password" class="no-spin" id="password" name="password" value="${item.password}" ...생략 /> <form:errors id="password" name="password" path="password" />
+번외) 직접 bindingResult가져와 jsp에서 사용하고 싶은경우?
@PostMapping("item/{itemId}") public String studioIdUpdate(@ModelAttribute UpdateItemDto form, Model model) throws Exception { beanValidator.validate(form, bindingResult); //@Validated 미사용은 직접 해줘야 함. if(bindingResult.hasErrors()) { model.addAttribute("bindingResult", bindingResult); //서버단 Valid결과 알려주는것 (직접 에러 커스텀 하려고 추가했음. form:error만 사용할경우 이 코드 필요없음) ... 생략
<div class="field-error"> <%-- <c:if test="${not empty bindingResult.fieldErrors['password']}"> --%> <c:if test="${not empty bindingResult.fieldErrors}"> 비밀번호 오류 <%-- 비밀번호 오류: <c:out value="${bindingResult.fieldErrors}" /> --%> </c:if> </div>
-
validator 태그 검증한 클라 에러 메시지와 controller에 bindingresult에 검증한 서버 에러 메시지
클라단 검증의 경우 validater-rule.xml 를 보면 msg=”errors.required” 이런 코드 체크!
message.properties에서 errors.required 관련 메시지로 검증 메시지(alert)를 출력한다.서버단 검증의 경우 타임리프로 했던 th:error와 유사하게 form:error으로 가능
둘 다 bindingResult 결과로 검증결과를 얻어서 message.properties의 메시지로 폼에 출력해주고
Bean Valid 방식으로 @NotNull(“메시지”)로 했다면 해당 메시지를 출력해준다.직접 모든걸 하고 싶으면 model로 bindingResult를 넘겨서 jsp에서 가공하여 출력해도 된다.
여기선 message-common.properties를 활용했다.
연동은 validator.xml에서 설정 가능하고, validator-rule.xml의 msg에서도 설정 가능하다.#validator.xml에 연동한 메시지 updateItemDto.password=비밀번호는 필수 입력 항목입니다.~! #validator-rule.xml에 msg필드에 자동으로 연동된 메시지 errors.required={0} 은 필수 입력값입니다.~!~!
- 클라단 메시지든 서버단 메시지든 두 메시지가 전부 출력된다. 하나로 통일해서 사용하자.
예로 updateItemDto.password를 지우고 errors.required만 사용
- 클라단 메시지든 서버단 메시지든 두 메시지가 전부 출력된다. 하나로 통일해서 사용하자.
클라 검증 메시지, 서버 검증 메시지 예시:
번외인 직접 bindingresult가져와 jsp에서 커스텀한 방식 예시(코드는 앞에서 언급한것 동일):
xml 설정 코드와 message 예시:
validator-rule.xml
<form-validation>
<global>
<validator name="required"
classname="org.springmodules.validation.commons.FieldChecks"
method="validateRequired"
methodParams="java.lang.Object,
org.apache.commons.validator.ValidatorAction,
org.apache.commons.validator.Field,
org.springframework.validation.Errors"
msg="errors.required">
...
validator.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE form-validation PUBLIC
"-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.1//EN"
"http://jakarta.apache.org/commons/dtds/validator_1_1.dtd">
<form-validation>
<formset>
<form name="updateItemDto">
<!-- id 필드: 필수 값 -->
<field property="id" depends="required">
<arg0 key="updateItemDto.id" />
</field>
<!-- nickname 필드: 필수 값 -->
<field property="nickname" depends="required">
<arg0 key="updateItemDto.nickname" />
</field>
<!-- password 필드: 숫자만 허용 -->
<field property="password" depends="required,integer">
<arg0 key="updateItemDto.password" />
<arg1 key="updateItemDto.password.integer" />
</field>
<!-- title 필드: 필수 값 -->
<field property="title" depends="required">
<arg0 key="updateItemDto.title" />
</field>
<!-- content 필드: 필수 값 -->
<field property="content" depends="required">
<arg0 key="updateItemDto.content" />
</field>
<!-- pageIndex 필드 -->
<field property="pageIndex" >
</field>
</form>
</formset>
</form-validation>
context-common.xml
<!-- jakarta common validation 빈 등록 -->
<!-- Integration Apache Commons Validator by Spring Modules -->
<bean id="beanValidator" class="org.springmodules.validation.commons.DefaultBeanValidator">
<property name="validatorFactory" ref="validatorFactory"/>
</bean>
<bean id="validatorFactory" class="org.springmodules.validation.commons.DefaultValidatorFactory">
<property name="validationConfigLocations">
<list>
<!-- 공통기술 -->
<value>classpath:/validator/validator-rules.xml</value>
<value>classpath:/validator/**/*.xml</value>
</list>
</property>
</bean>
validator.jsp
<%@ page language="java" contentType="javascript/x-javascript" %>
<%@ taglib prefix="validator" uri="http://www.springmodules.org/tags/commons-validator" %>
<validator:javascript dynamicJavascript="false" staticJavascript="true"/>
controller.java
@GetMapping("/validator.do")
public String validator() {
return "jsp/validator"; // Jakarta common validation 활용 위해
}
message-common.properties
# -- validator errors -- #
updateItemDto.id=ID는 필수 입력 항목입니다.
updateItemDto.nickname=닉네임은 필수 입력 항목입니다.
updateItemDto.password=비밀번호는 필수 입력 항목입니다.~!
updateItemDto.password.integer=비밀번호는 숫자로 입력해주세요.테스트임!!
updateItemDto.title=제목은 필수 입력 항목입니다.
updateItemDto.content=내용은 필수 입력 항목입니다.
#
fail.common.msg=에러가 발생했습니다!
fail.common.sql=sql 에러가 발생했습니다! error code: {0}, error msg: {1}
info.nodata.msg=해당 데이터가 없습니다.
errors.prefix=<div class="error">
errors.suffix=</div><br/>
errors.required={0} 은 필수 입력값입니다.~!~!
errors.minlength={0} 은 {1}자 이상 입력해야 합니다.
errors.maxlength={0} 은 {1}자 이상 입력할수 없습니다.
errors.invalid={0} 은 유효하지 않은 값입니다.
errors.byte={0} 은 byte 타입이어야 합니다.
errors.short={0} 은 short 타입이어야 합니다.
errors.integer={0} 은 integer 타입이어야 합니다.
errors.long={0} 은 long 타입이어야 합니다.
errors.float={0} 은 float 타입이어야 합니다.
errors.double={0} 은 double 타입이어야 합니다.
errors.date={0} 은 날짜 유형이 아닙니다.
errors.range={0} 은 {1} 과 {2} 사이의 값이어야 합니다.
errors.creditcard={0} 은 유효하지 않은 신용카드 번호입니다.
errors.email={0} 은 유효하지 않은 이메일 주소입니다.
errors.ihidnum=유효하지 않은 주민등록번호입니다.
errors.korean={0}은 한글을 입력하셔야 합니다.
Bean 방식 vs Jakarta 방식 코드 예시(+JS직접,자동): th:error vs form:errors
th:error, form:errors는 유사하다. 오히려 Bean, Jakarta 방식의 다른점을 비교해야 한다.
“클라단 검증” 과 “메시지 설정” 및 xml설정은 위 정리글 보고,
“서버단 검증”을 중점적으로 비교
/*
차이점:
@Validated 유무로 인해 beanValidator.validate(form, bindingResult); 생략 유무
@Pattern(regexp = "^[0-9]+", message = "비밀번호는 숫자로 입력 해주세요.") 처럼 DTO에 바로 적용 메시지 유무
*/
//Bean Validation
@PostMapping("item/{itemId}")
public String studioIdUpdate(@Validated @ModelAttribute UpdateItemDto form, @RequestParam int pageIndex, BindingResult bindingResult,
@PathVariable Long itemId, RedirectAttributes redirectAttributes, Model model) throws Exception {
if(bindingResult.hasErrors()) {
log.info("error={}", bindingResult);
model.addAttribute("bindingResult", bindingResult); //서버단 Valid결과 알려주는거
return "jsp/studio_item"; //다시 폼으로 이동
// 어차피 "검증" 에 걸려서 DB 사용안하기에 PRG 패턴 상관없움
}
form.setPageIndex(pageIndex);
itemService.update(form);
redirectAttributes.addFlashAttribute("status", "updateON");
redirectAttributes.addAttribute("itemId", itemId);
return "redirect:/gallery/itemDetail/{itemId}";
//DTO
@Data
public class UpdateItemDto {
@NotNull
private Long id;
@NotNull
private String nickname;
@NotNull
@Pattern(regexp = "^[0-9]+", message = "비밀번호는 숫자로 입력 해주세요.")
private String password;
@NotNull
private String title;
@NotNull
private String content;
private int pageIndex;
}
//
//
//Jakarta Validation
private final DefaultBeanValidator beanValidator;
@PostMapping("item/{itemId}")
public String studioIdUpdate(@ModelAttribute UpdateItemDto form, @RequestParam int pageIndex, BindingResult bindingResult,
@PathVariable Long itemId, RedirectAttributes redirectAttributes, Model model) throws Exception {
beanValidator.validate(form, bindingResult); //@Validated 사용 안하면 직접 해줘야 함.
if(bindingResult.hasErrors()) {
log.info("error={}", bindingResult);
model.addAttribute("bindingResult", bindingResult); //서버단 Valid결과 알려주는거 (직접 에러 커스텀도 해가지고 추가했음. form:error만 사용할경우 이 코드 필요없음)
return this.studioCompleteId(itemId, pageIndex, model); //다시 폼으로 이동 (item/{itemId}로 이동해야해서 내무메소드로 호출하겠음. 애초에 수정폼은 따로 둬서 폼바로 호출해야 했다. 이 방식은 비추다 ㅠ.)
}
form.setPageIndex(pageIndex);
itemService.update(form);
redirectAttributes.addFlashAttribute("status", "updateON");
redirectAttributes.addAttribute("itemId", itemId);
return "redirect:/gallery/itemDetail/{itemId}";
}
//DTO
@Data
public class UpdateItemDto {
private Long id;
private String nickname;
private String password;
private String title;
private String content;
private int pageIndex;
}
<!-- form:errors 출력! th:error는 방식 유사하여 생략 -->
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<form:form action=""
method="post" modelAttribute="updateItemDto">
<input type="password" class="no-spin" id="password" name="password" value="${item.password}" ...생략 />
<form:errors id="password" name="password" path="password" />
<!-- -->
<!-- -->
<!-- 번외로 소개한 bindingResult model에 담아 가져와 직접 커스텀 방식 -->
<div class="field-error">
<%-- <c:if test="${not empty bindingResult.fieldErrors['password']}"> --%>
<c:if test="${not empty bindingResult.fieldErrors}">
비밀번호 오류
<%-- 비밀번호 오류: <c:out value="${bindingResult.fieldErrors}" /> --%>
</c:if>
</div>
예외처리와 AOP - XML
XML 방식으로 주로 정리 -> Java Config 방식은 “JPA + Boot 파트” 참고
API의 경우 클라쪽 “검증”은 서버가 할 일이 아니다(JS는 프론트쪽 개발진이 해야지!)
웹의 경우 클라와 서버쪽 둘 다 “검증”해주는게 좋다.API의 “예외”의 경우 서버는 JSON으로 변경된 데이터를 “Valid(검증)”하는거라서 JSON→DTO매핑될 때 에러나 그 시점에 다양한 에러(주로 서비스로직)들은 “Exception(예외)”으로 해결! => 웹은 “검증”만으로 충분하지만 API는 “검증+예외”가 필요!
=> 근데, 막상 해보니 웹&API 둘다 “검증+예외”를 적용 했다. eGov에선 웹에 에러페이지만 연동해주는게 아니라 “예외”까지 굳이 하더라?예외를 처리하는 방법 크게 2가지
예외를 잡아서 정상화 하는 방법 ⇒ 예로 try, catch
예외를 해결할 수 없는 문제로 인정하고 공통 처리하는 방법(사용자에게 죄송합니다. 같은 화면을 보여주는 방법) ⇒ 예로 @ExceptionHandler + @ControllerAdvice
특히, API(JSON)의 경우 대부분 2번으로 해결 됨. 직접 예외를 throw로 던져서 공통 관리해도 되니까.
예외를 처리하는 계층의 흐름 이해:
- 서비스계층의 비즈니스 로직의 예외 발생하면 정상화하거나 공통 처리위해 던지기
- 컨트롤러에서 웹이면 서비스의 Exception을 JSP 뷰로 매핑, API면 JSON으로 응답
서비스계층의 예외 처리:
-
eGov에선 서비스계층의 비즈니스 로직 예외처리를 위해 EgovAbstractServiceImpl 를 사용하며,
EgovBizException 발생 메소드(processException) 와 Exception 발생없이 후처리 로그 메소드(leaveaTrace)를 제공한다.- 다국어 지원 메시지, 확장성, 표준화 로직 때문에 사용!
-
leaveaTrace -> EgovBizException 발생 없이 로그 생성(메시지소스를 사용하는게 특징)
-
메소드 원리: traceHandler빈을 참조한 leaveaTrace빈을 xml이나 java로 등록 후 leaveaTrace빈을 주입해 사용하여 핸들링한다.
-
MessageSource 빈에 등록된 메시지를 로그(Info)로 제공
-
xml 설정 코드 예시:
context-common.xml (비즈니스단 계층용 설정파일)<!-- EgovAbstractServiceImpl가 제공하는 leaveaTrace(예외처리 관련) 사용 위해 빈 등록 필수! --> <bean id="leaveaTrace" class="org.egovframe.rte.fdl.cmmn.trace.LeaveaTrace"> <property name="traceHandlerServices"> <list> <ref bean="traceHandlerService" /> </list> </property> </bean> <bean id="traceHandlerService" class="org.egovframe.rte.fdl.cmmn.trace.manager.DefaultTraceHandleManager"> <property name="reqExpMatcher"> <ref bean="antPathMater" /> </property> <property name="patterns"> <list> <value>*</value> </list> </property> <property name="handlers"> <list> <ref bean="defaultTraceHandler" /> </list> </property> </bean> <bean id="antPathMater" class="org.springframework.util.AntPathMatcher" /> <bean id="defaultTraceHandler" class="org.egovframe.rte.fdl.cmmn.trace.handler.DefaultTraceHandler" />
-
leaveaTrace 메소드 적용 예시:
-
//EgovAbstractServiceImpl를 상속한 서비스로직이라 가정 try{ //비즈니스 로직 }catch { //fail.common.msg=에러가 발생했습니다! 라고 message.properties에 있음 leaveaTrace("fail.common.msg"); }
-
-
processException -> 예외들을 EgovBizException 예외로 바꿈 + 로그 생성(메시지소스를 사용)
-
메소드 원리: 발생한 예외를 EgovBizException 로 감싸는 방식
-
MessageSource 빈에 등록된 메시지를 로그(Info)로 제공
-
//EgovAbstractServiceImpl를 상속한 서비스로직이라 가정 try{ int i = 1/0; }catch(ArithmeticException ae){ throw processException("fail.common.msg"); }
-
-
예시 결과:
컨트롤러계층(+AOP)의 예외 처리:
웹 플젝 스프링 부트는 ErrorPage, BasicErrorController를 자동등록하여 예외 핸들링 에러페이지 자동
API는 부트든 아니든 @ExceptionHandelr + @ControllerAdvice로 예외 공통 관리 및 JSON 응답 젤 편함!
-
웹 플젝 eGov에서는 부트가 한 에러페이지 자동등록 설정을 SimpleMappingExceptionResolver빈 등록으로 직접 하는편. (리졸버뷰를 생각하자)
-
jsp에도 MessageSource 빈의 메시지가 출력이 됨.(exception.message로 서버상에서 MessageSource 빈 메시지를 기록해둔다고 예상)
-
xml 설정과 error-page(jsp) 코드 예시:
context-servlet.xml (컨트롤러단 계층용 설정파일)<!-- 직접 원하는 Exception과 에러뷰를 매핑 확장 가능 --> <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="defaultErrorView" value="jsp/cmmn/genneralException" /> <property name="exceptionMappings"> <props> <prop key="org.egovframe.rte.fdl.cmmn.exception.EgovBizException">jsp/cmmn/egovBizException</prop> <prop key="org.springframework.dao.DataAccessException">jsp/cmmn/egovBizException</prop> <prop key="org.springframework.transaction.TransactionException">jsp/cmmn/egovBizException</prop> </props> </property> </bean>
egovBizException.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" %> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>에러가 발생하였습니다.종류는 EgovBizException 입니다.</title> </head> <body> 에러가 발생하였습니다.종류는 EgovBizException 입니다. 메세지는${exception.message} 입니다. </body> </html>
genneralException.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" %> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>일반적인 에러가 발생하였습니다.</title> </head> <body> 일반적인 에러가 발생하였습니다. 메세지는${exception.message} 입니다. </body> </html>
-
-
@ExceptionHandelr 로 예외 공통 관리하던걸 “eGov의 ExceptionHandler인터페이스”를 구현하여 AOP를 직접 설정하자. (예시 부분의 콘솔을 봐라)
-
주의: “부트”(순수스프링 아님)에선 자동으로 예외처리기를 제공하다보니 아래 AOP적용한 예외처리기와 중복 사용될 수 있다. (로그상에서 예외가 1번더 출력됨을 확인)
-
xml 설정과 ExceptionHandler 구현 코드 예시:
ExceptionHandler 구현 코드 java@Slf4j public class SecretGalleryExceptionHandler implements ExceptionHandler { public void occur(Exception exception, String packageName) { log.debug(" EasyCompanyExceptionHandler run...............{}", ((EgovBizException) exception).getWrappedException()); try { if (exception instanceof EgovBizException) { Exception exx = (Exception) ((EgovBizException) exception).getWrappedException(); if (exx != null) { log.debug(" sending a alert mail is completed "); exx.printStackTrace(); } } } catch (Exception e) { //일반 예외 e.printStackTrace(); } } }
context-aspect.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd"> <!-- --> <aop:config> <aop:pointcut id="serviceMethod" expression="execution(* com.secretgallery..impl.*Impl.*(..))" /> <aop:aspect ref="exceptionTransfer"> <aop:after-throwing throwing="exception" pointcut-ref="serviceMethod" method="transfer" /> </aop:aspect> </aop:config> <!-- --> <bean id="exceptionTransfer" class="org.egovframe.rte.fdl.cmmn.aspect.ExceptionTransfer"> <property name="exceptionHandlerService"> <list> <ref bean="defaultExceptionHandleManager" /> </list> </property> </bean> <!-- --> <bean id="defaultExceptionHandleManager" class="org.egovframe.rte.fdl.cmmn.exception.manager.DefaultExceptionHandleManager"> <property name="reqExpMatcher"> <ref bean="antPathMater" /> </property> <property name="patterns"> <list> <value>**service.impl.**</value> </list> </property> <property name="handlers"> <list> <ref bean="exceptionHandler" /> </list> </property> </bean> <!-- --> <bean id="exceptionHandler" class="com.secretgallery.exception.SecretGalleryExceptionHandler" /> </beans>
-
사용예시 코드:
등록한 EasyCompanyExceptionHandler빈을 통해 공통으로 예외처리 관리 가능
//EgovBizException 관련 공통예외처리 로직 수행 (ExceptionHandler 구현로직 참고) throw processException("msg.exception.case1", new RuntimeException("실제 원인")); //일반 공통예외처리 로직 수행 (ExceptionHandler 구현로직 참고) throw new Exception(); ...
- 특히, “msg.exception.case1=에러테스트를 위한 에러를 발생시킵니다.” 으로 기록된 메시지를 JSP에 출력
-
-
전체적 흐름 이해하기:
-
서비스계층의 leaveaTrace, processException 를 적절히 활용하여 예외 처리하고 싶은부분에 사용(leaveaTrace빈이 잘 동작)
예외 정상으로? leaveaTrace
예외 EgovBizException 으로 바꾸려면? processException
예외 일반은? 그냥 원하는 예외로 throw 하거나 그냥 둬도 AOP에서 잘 처리 -
컨트롤러,AOP단에서 exceptionTransfer빈이 동작하여 예외를 가로채고 EasyCompanyExceptionHandler빈으로 공통으로 예외처리(보통 콘솔에 메시지 출력)
-
컨트롤러,AOP단에서 SimpleMappingExceptionResolver빈이 동작하여 예외일때 리졸버뷰로써 잘 동작
예외 EgovBizException은? 등록한 egovBizException.jsp 출력
예외 DataAccessException은? 등록한 egovBizException.jsp 출력
예외 TransactionException은? 등록한 egovBizException.jsp 출력
예외 일반은? 등록한 genneralException.jsp 출력
-
-
예시 결과:
홈페이지
콘솔
번외) 메소드 수행시간 측정 AOP는 Java Config로 해보겠다.
-
TimeTraceAop.java 코드 예시:
@Aspect // AOP @Component // "빈" 등록 @Slf4j public class TimeTraceAop { @Around("execution(* com.secretgallery..*(..)) && !within(com.secretgallery.security..*)") public Object execute(ProceedingJoinPoint joinPoint) throws Throwable { // 프록시 실행 long start = System.currentTimeMillis(); log.debug("START: {}", joinPoint.toString()); try { return joinPoint.proceed(); // 실제 실행 } finally { long finish = System.currentTimeMillis(); long timeMs = finish - start; log.debug("END: {} {}ms", joinPoint.toString(), timeMs); } } }
“공통컴포넌트”, “DBIO Editor”, “운영환경”, “배치”
표준프레임워크는 "실행,개발,관리,운영" 4개의 환경과 "모바일, 공통컴포넌트"로 구성
표준프레임워크 실행환경
표준프레임워크 개발환경
표준프레임워크 관리환경
표준프레임워크 운영환경
공통컴포넌트
모바일 표준프레임워크
이클립스의 기능에 로그인쪽(게시판 등?) “공통컴포넌트” 이용 + Spring Security(공통기능)도 표준프레임워크로 security, 암호화, 공문 참고!
- 프로젝트 우클릭> New > eGovFrame Common Component > 에 위치
- 자동 코드 예시를 제공
DBIO Editor 예시:
- DB실행
- DBIO 실습(자세히는 PDF)
- Mapper Configuration 파일 생성(sample_config.xml): 프로젝트 우클릭 > New > mapperConfiguration
- Mapper 파일 생성(sample_map.xml): Mapper Configuration Editor > New
- Mapper 파일 편집 -> mapper 에디터를 활용!
에디터로 간단히 설정하는데 “xml코드가 자동 생성되는 편리!!”- Result Map 작성: Mapper Editor> ResultMap 우클릭> Add resultMap
- Query 작성: Mapper Editor> Query 우클릭> Add Select Query
- 자동생성 코드 예시(sample_map.xml):
<?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 ><resultMap id="resultMap" type="java.lang.String"><result property="deptName" column="DEPT_NAME"/> </resultMap> <select id="selectDept" parameterType="java.lang.String" resultMap="resultMap"> SELECT DEPT_NAME FROM PUBLIC.DEPT WHERE DEPT_NO = #{deptNo} </select> </mapper>
- Query 테스트
이클립스의 기능에 01.개발환경_교육교재.pdf에 Jenkins(CI) 이거 pdf 참고하기
Spring Batch는 “eGov 가이드 학습하기” 게시물이랑 “프로젝트 코드” 참고
댓글남기기