티스토리 뷰


스프링 프레임워크에서 AOP를 공부하면서, 앞에 메소드 명이나, 클래스 등으로 AOP 를 설정하는 방법은 예전에 공부해두었다.

이번에는 AOP를 딱 필요한데만 마구잡이로 넣고 싶을 때! 라고 생각이 들어서 찾아보고 공부한 결과 애노테이션으로 포인트 컷을 거는 방법을 알게 되었다 ( 아마 영어만 잘해도...스프링 레퍼런스로 뚝딱해치웟을텐..데..킁 개발자는 필수 불가결인 영어 공부해야할 듯.. )

이번 시나리오는

Form 이 있는 페이지로 이동할 때, 입력 Form 이 있는 페이지는 Submit 이 존재하는데 중복 서브밋 또는 뒤로가기 후 서브밋 등의 방지를 위한 기능을 페이지 이동이 있는 컨트롤러 맵핑 메소드에 걸기 위해 커스텀 애노테이션을 포인트 컷으로 이용하는 방법이다!

먼저, 중복 서브밋 또는 뒤로가기 후 서브밋 방지 등에 대한 자료는 찾아보세요~라고 말하고 싶지만

좋은 분의 자료가 있어서 링크는 달아 드립니다. ( http://insummersnow.tistory.com/48 )


폼에 토큰을 Hidden으로 숨겨서 토큰이 있을 때에만 정상적으로 입력이 되도록 하는 방식! 그러므로 일단 웹 페이지 Form 에 Hidden 으로 Input 태그를 작성해둡니다.

<input type="hidden" name="TOKEN_KEY" id="TOKEN_KEY" value="${TOKEN_KEY}" />

이렇게 JSTL 을 이용한 값 입력을 위한 input 이 존재하면, 페이지 이동 시 세션에 TOKEN_KEY 가 있으면, 토큰이 입력될 것이고 없으면, null로 처리가 될 것 입니다.

그럼 천천히, 커스텀 애노테이션을 작성합니다.

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface InsertToken {
}

AOP에서 사용하기 위한 Custom Annotation 으로 별거 없습니다. 스프링3 레시피 책에서 본대로 작성하였습니다.

위의 애노테이션을 포인트컷으로 사용할 Around Advice를 가지고 있는 AOP 클래스를 작성합니다.

import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
import org.springframework.web.servlet.ModelAndView;

import com.example.hwb.util.TokenMngUtil;

@Aspect
@Order(1)
public class TokenAspect {
	@Around("@annotation(com.example.hwb.aop.InsertToken)")
	public Object InsertTkn(ProceedingJoinPoint joinPoint) throws Throwable {
		HttpServletRequest req = null;
		ModelAndView mav = new ModelAndView();
		for (Object obj : joinPoint.getArgs()) {
			if (obj instanceof HttpServletRequest) {
				req = (HttpServletRequest) obj;
			}
		}
		TokenMngUtil.saveToken(mav, req);
		Object result = joinPoint.proceed();
		return result;
	}

	@Around("@annotation(com.example.hwb.aop.RemoveToken)")
	public Object RemoveTkn(ProceedingJoinPoint joinPoint) throws Throwable {
		HttpServletRequest req = null;
		ModelAndView mav = new ModelAndView();
		for (Object obj : joinPoint.getArgs()) {
			if (obj instanceof HttpServletRequest) {
				req = (HttpServletRequest) obj;
			}
		}

		if (!TokenMngUtil.isTokenValid(req)) {
			String referrer = req.getHeader("referrer");
			System.out.println(referrer + "잘못된 접근입니다. 중복방지 Token error");
			mav.addObject("error", "잘못된 접근 입니다.");
			mav.setViewName("redirect:/login");
			return mav;
		}
		/* 중복방지 Token 초기화 */
		TokenMngUtil.resetToken(req);
		Object result = joinPoint.proceed();
		return result;
	}
}

위에는 InsertToken 커스텀 애노테이션 작성만 있지만, RemoveToken도 똑같이 작성하면 됩니다.

포인트 컷을 작성하지 않고, Around Advice 자리에 바로 입력했는데요 ( 귀찮아서.. ) 포인트컷을 직접 작성해서 포인트컷의 메소드 명을 입력해 주셔도 무방합니다.

@Around("@annotation(com.example.hwb.aop.RemoveToken)")

이런식으로 애노테이션의 이름을 등록하신 뒤 컨트롤러 또는 서비스 클래스의 메소드 위에 @RemoveToken 이런식으로 애노테이션을 설정하면, 해당 메소드만 AOP가 설정이 됩니다^^

에러에 대한 처리는 로거를 사용하면 좋구요, 간편하게 저만 보기 위해서 일단 println으로 처리하였네요~

그래서 토큰을 심고 제거하는 AOP를 편하게 적용하기 위해 이런식으로 만들어 보았고,

Input Form 이 있는 페이지 이동에는 토큰을 심는 AOP, Form의 데이터를 받아서 입력하는 메소드에서는 토큰을 제거하는 AOP를 애노테이션으로 걸수 있도록 프로젝트에 적용해 보았습니다.


공부하면서 조금씩 정리하면서 하는 초짜 입니다.. 많은 질타와 지적 부탁드립니다~!


댓글