Spring - 35. 이벤트리스너, 객체검증, 폼태그라이브러리, 커스텀태그

갓김치·2021년 1월 7일
0

JSP+Spring

목록 보기
38/43

복습

DI Container

  • 컨테이너로 객체 주입받기
  • 컨테이너 객체 생성시점: 어플리케이션의 entry point
  • Generic구조: 설정파일 위치가 동적으로 바뀔 수 있음
    • prefix형태로 어디서부터 찾을건지
  • 생성된 컨테이너로부터 필요한 빈 주입받아 쓰면됨
  • 컨테이너의 계층구조
    • 상위 컨테이너가 가진 공통빈들을 하위 컨테이너가 물려받는 구조

Web MVC

  • springDispatcherServlet : 요청의 엔트리포인트 (기존: 프론트컨트롤러)
  • 하나의 어플리케이션 안에서 관리저와 유저의 컨테이너를 분리할 수 있다 (서블릿2개등록)
    • 문제점: 관리자, 유저 모두 상품정보 조회
    • 상품에 대한 정보는 db에 저장
    • 관리자, 유저 모두 persistence에 접근해야함
    • ProdDaoImpl이 관리자, 유저 모두에 등록이되어야함
    • 이럴때 사용하는게 계층구조 컨테이너
  • 계층구조 컨테이너
    • root-context: 웹에 종속되지 않는 빈, 하위컨테이너들도 쓸 빈을 등록
      • controllr를 exclude
      • component, service, repoistory, configuration
    • servlet-context: 디스패처서블릿 생성시 등록할 수 있도록
      • 기본설정을 끔 use-default-filters="false"
      • include로 컨트롤러만 등록될 수 있도록
  • auto di핵심: component-scan
  • layer간의 의존관계
    • 컨테이너를 사용하려면 처음부터 끝까지 쓰던지 아예 안써야함 -> 중간에 컨테이너가 사용하지 않는 코드가 들어가서 언제 nullpointer가 뜰지 모름
    • new MemberServiceImpl() : 해봤자 컨테이너밖에 있기때문에 MemberServiceImpl의 어노테이션들이 작동하지않음
  • 디스패처서블릿이 handlerMapping, handlerAdapter, view resolver 순서를 정함 -> servlet-context.xml

ViewResolver

  • 커스터마이징되어있는 view의 위치 및 확장자를 등록해줘야함
  • IRVR
  • 마샬링과 직렬화를 대신해주는 뷰리졸버
  • BeanNameViewResolver: bean id와 일치하는 곳으로 제어권 넘김



정적요청처리

  • 톰캣이라는 was가 정적처리를 할 수 있었던 이유는 DefaultServlet으로 웹서버역할을 대신하고 있던것 (web.xml:98)
    • /으로 매핑되어있음(web.xml:391)
  • springDispatcherServlet도 /로 매핑되어있어서 정적요청처리를 못하게되는 상황
  • 정적요청의 위치는 다다름, 처리하는 컨트롤러를 만든다는게 쉬운일이 아님 -> 그래서 스프링에서 제공이 됨

servlet-context.xml

1번방법: 우린지금쓸수없음

  • <resources location="/resource/" mapping="/resources/**"></resources>
  • /resources/ 구조를 쓸 때 장점
    • 정적자원 모아서 관리할수 있다는 특성
    • cache-period 속성추가할 수 있음: cache-period="0"
      • cache와 관련된 설정을 자동으로 관리 -> 정적자원을 전혀 캐싱하지 않음

2. default-servlet-handler

<default-servlet-handler/>

  • 정적자원을 일괄관리 할 수 없다는 것이 단점
  • 정적자원에 대한 요청을 default-servlet으로 넘겨버림
  • 캐시기간을 servelt-context.xml에서 할수없으니 필터에서 해결함
  • web.xml에 등록한 filter의 객체를 관리하는 것은 톰캣, CharacterEncodingFilter는 spring container 밖에 있는 것임
    • 그래서 이안에서는 spring container bean을 주입받을 수 없음
    • 이것을 handler intercepter라는 기능으로 처리함



스프링의 이벤트 처리구조

  • 시작과 종료가 명확하게 결정이 되어있다면 이벤트가 발생할 것
  • 스프링에서는 이벤트처리구조를 지원해줌
  • 상위컨테이너생성 시점에 cPath를 미리 만들어야함

@EventListener

  • classes: 어떤리스너?

파일업로드

파일업로드인지 판별

  • 필터가 아닌 multipart resolver 형태로 관리
  • 필터로 관리시 컨테이너 밖에있게됨
  • commonsmultipartresolver
  • standardservletmultipartresolver : 일반 파트
  • multipartResolver: hanlder adapter가 받아서 멀티파트에 대한 처리를 함

Handler Adapter 이용한 객체 검증

  • @RequestParam에 대한 검증을 H.A가 하고있음
  • 그러면 command object도 H.A가 해야겠지?
  • @Valid는 인서트안되네
  • @Validated(InsertGroup.class)
  • <annotation-driven>을 넣었을때 Validator(우리가만든 CommonValidator랑 비슷)가 같이 들어가서 이 어노테이션만으로도 검증이 되는것
  • 검증결과는 어쩌죠?
    • Map이 사라져서 리퀘스트에도 못담고 그럼 jsp에서 ${errors.mem_id}도 이제 못씀
    • 커스텀태그를 사용하여 해결해야함

커스텀태그 form

  • <%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
  • 폼 인풋을 만들어줌, 실패하면 초기값으로 id가 들어가있다?
  • ``<form:input path="mem_id"/>
  • 근데 이거쓰면 jquery가 복잡해지고 서버사이드에서 추가설정ㅇ이 있음
  • 그래서 이거 안쓸거지만 검증결과메시지는 이걸로 받아올거야
    • <form:errors path="모델에들어간이름.프로퍼티명" element="span" cssClass="error"></form:errors>

정규식으로 바꾸기

  • <span class="error">\$\{errors\.mem_(\w+)\s*\}</span>
  • <form:errors path="mem_$1" element="span" cssClass="error" />

표기 주의 ★

public String insert(@Validated(InsertGroup.class) @ModelAttribute("member") MemberVO member,
						Errors errors, Model model){
} // MemberVO 다음 바로 Errors
  • 검증대상 커맨드오브젝트 다음에 errors 넣어줘라

커스텀 에러메시지 사용

  • 커스텀태그 fmt 사용시
  • 우선 스프링 국제화 태그부터

스프링 국제화 태그

기본사용

1. 메시지 번들만들기

--member.mem_id=아이디
SELECT LOWER(TABLE_NAME)||'.'||LOWER(COLUMN_NAME)||'='||COMMENTS
FROM USER_COL_COMMENTS
WHERE TABLE_NAME = 'MEMBER';

2. 번들 컨테이너에 로딩하기

  • view에서 사용할 거니까 메시지번들 로딩을 하위컨테이너인 servlet-context에서 함 (루트도되긴하지만 view는 웹에 종속되어있기때문에)
<!-- 	i18n, l10n 지원, IRVR가 사용하기떄문에 id도 규칙임 -->
	<beans:bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource" 
		p:basename="kr.or.ddit.msg.message"
	/>
  • 지금은 서버의 로케일을 따라가는데, 추후 수정할 예정

3. view에서 커스텀태그로 사용하기

<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>

	<thead>
		<tr>
			<th><spring:message code="member.mem_id"/></th>
			<th><spring:message code="member.mem_name"/></th>
			<th><spring:message code="member.mem_hp"/></th>
			<th><spring:message code="member.mem_mail"/></th>
			<th><spring:message code="member.mem_add1"/></th>
			<th><spring:message code="member.mem_mileage"/></th>
		</tr>
	</thead>

4. HandlerIntercepter 사용하여 적용하기

  • 로케일을 한번에 적용하자니,,
    • 컨트롤러 마다 쓴다면 중복파티
    • 디스패처서블릿에 쓰자니 중요하지도 않은 부가기능을 넣기가좀;;
    • 필터를 쓰자니 컨테이너 밖에 있는놈이고.. 어쩌지
  • handler intecepter는 handler adapter와 controller사이에서 필터의 역할을 해준다!!
  • servlet-context.xml
    intercepters = filter chain (여러개 등록가능)
  • H.I 동작순서에 맞게 LocaleChangeInterceptor가 작동
    • lang 이 있으면 그에맞는 로케일로 변경
    • lang 이 없으면 클라이언트 리퀘스트 로케일
<interceptors>
  <beans:bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"
              p:paramName="lang"
              />
</interceptors>
  • 매핑 정보 생략시 전체매핑과같음
  • 파람디폴트값은 locale
	<interceptors>
		<interceptor>
			<mapping path="/**" />
			<beans:bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"
			 	p:paramName="lang"
			 />
		</interceptor>
	</interceptors>

위와 같음
근데이렇게 돌리면터짐, 쿠키에 저장해야하니까 쿠키로케일리졸버

	<!-- Locale과 관련된 쿠키 전략 , interceptor가 쓰기때문에 id 맞춰줘야함-->
	<beans:bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"
		p:cookieName="localeCookie"
		p:cookiePath="/"
		p:cookieMaxAge="#{60*60*24*7}"
	/>

이게있어야함

객체 검증 및 메시지 관련

<!-- 객체 검증 및 메시지 관련 , handler adapter 커스텀 메시징 위함, id는 어짜피 validator용이여서 맘대로 줘도됨-->
<beans:bean id="errorMessageSource" class="org.springframework.context.support.ResourceBundleMessageSource"
            p:basename="kr.or.ddit.msg.errorMessage"
            />
<beans:bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"
            p:validationMessageSource-ref="errorMessageSource"
            />

스프링 사용 시 주의점

  • 필요한 객체는 더이상 생성하지 않고 injection 받아야한다
  • 스프링 개발자의 실력은 session, request, response 를 최대한 안쓰도록 노력, handlerapdater를 최대한 활용할 것
  • jsonView써서 마셜링하려면 꼭 Model에 담아야한다. req에 넣어봤자 모름

미션

  • 계획서 보완
  • 게시판, 거래처완료
profile
갈 길이 멀다

0개의 댓글