[21.10.21] Spring AOP

yed·2021년 10월 21일
0

@PathVariable

ajax에서 url로 보낸 데이터를 설정한 매개변수에 저장해주는 어노테이션.

rest방식의 get방식은 url에 parameter를 넣는 데이터전송을 사용하지않음

@GetMapping("/all/{replyBno}")
public ResponseEntity<List<ReplyVO>> readReplies(@PathVariable("replyBno") int replyBno) {
	List<ReplyVO> list=replyService.read(replyBno);
	return new ResponseEntity<List<ReplyVO>>(list, HttpStatus.OK);
}

자바스크립트에서 url을 read 방식으로 바꿔준다.
var url='replies/all/'+replyBno;

headers : {
	'Content-Type':'application/json',
	'X-HTTP-Method-Override':'(POST/PUT/DELETE)'
}

post, put, delete는 아무나 url에 접근할 수 없어서 ajax에서 header로 명시해줘야 가능하다


Spring AOP

AOP(Aspect Oriented Programming)

관점지향 프로그래밍. 핵심/부가로 기능을 나눠서 관점기준으로 모듈화함.

원래는 흐름대로 A->B->C->a->b->c 수평적 모듈화를 했다면 관점으로 A,a|B,b|C,c 수직적 모듈화하는 것.

흐름을 횡단하는 형식

  • @Component : Proxy 객체에 주입(injection)하기 위해 선언한다
  • @Aspect : Aspect=Advice+Pointcut

pom.xml에 aspect 설정추가

<!-- AspectJ -->
<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjrt</artifactId>
	<version>${org.aspectj-version}</version>
</dependency>

<!-- AspectJ Weaver -->
<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjweaver</artifactId>
	<version>${org.aspectj-version}</version>
</dependency>

root-context.xml 설정추가

beans 속성에 http://www.springframework.org/schema/tx 추가하기

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:mvc="http://www.springframework.org/schema/mvc"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:tx="http://www.springframework.org/schema/tx"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">

servlet-context.xml 설정추가

  • http://www.springframework.org/schema/aop 추가하기
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
  • <aop:aspectj-autoproxy /> 추가하기
<!-- Annotation 기반의 AOP 기능 사용 -->
<aop:aspectj-autoproxy />

Aspect

  • 일반적으로 메소드에 특정기능을 적용시킨다
  • 메소드 실행전/후에 특정기능을 적용시킬 수 있음

pointcut을 지정하는 방법

  1. @Before, @After, .. 어노테이션 안에서 지정한다
  2. @PointCut 어노테이션 안에서 지정한다
    동일한 pointcut이 반복되는 것을 피할 수 있고 한번 지정하는 pointcut을 여러 Advice 메소드에서 참조한다
@Pointcut("execution(* edu.spring.ex03.HomeController.home(..))")
public void pcHome() {}
	
@Around("pcHome()")
public Object homeAdvice(ProceedingJoinPoint jp) {
	Object result=null;
	LOGGER.info("===== homeAdvice");
	try {
		LOGGER.info("===== home() 호출 전"); //@Before
		result=jp.proceed(); //HomeController.home() 실행
		LOGGER.info("===== home() 리턴 후"); //@AfterReturning
	} catch (Throwable e) {
		//@AfterThrowing
		LOGGER.info("===== 예외 발생 : "+e.getMessage());
	} finally {
		//@After
		LOGGER.info("===== finally");
	}	
	return "reply";
}
  • @Pointcut() : 간섭할 포인트컷 메소드 위치 지정
  • @Around() : 포인트컷 메소드를 적용시킴

포인트컷으로 home.jsp경로를 지정하고 home.jsp의 실행 전후에 간섭할 수가 있다

INFO : edu.spring.ex03.aspect.HomeAspect - ===== homeAdvice
INFO : edu.spring.ex03.aspect.HomeAspect - ===== home() 호출 전
INFO : edu.spring.ex03.HomeController - Welcome home! The client locale is ko_KR.
INFO : edu.spring.ex03.aspect.HomeAspect - ===== home() 리턴 후

경로뿐만 아니라 클래스와 메소드의 이름으로 접근할 수 있음

@Before("execution(* edu.spring.ex03.service.CustomerServiceImple.*Customer(..))") 
public void beforeAdvice(JoinPoint jp) {
	logger.info("===== beforeAdvice");
	logger.info("target : "+jp.getTarget()); //return 할 타겟 경로
	logger.info("signature : "+jp.getSignature());	
}

DB Transaction Manager

service 활용 예시 - 댓글수

댓글 테이블에 데이터가 추가되면 게시글 테이블의 댓글수 컬럼의 데이터가 추가된 댓글 수만큼 업데이트되어야한다

update board
set reply_count=reply_count+?
where board_no=?

변수 두개를 하나의 파라미터로 넘겨야하기 때문에 key-value의 map으로 담아보내기

public int updateReplyCount(int amount, int bno) {
	Map<String, Integer> args=new HashMap<String, Integer>();
	args.put("amount", amount);
	args.put("bno", bno);
	return sqlSession.update(NAMESPACE+".update_reply_count", args);
}

두 테이블의 동작이 연속적으로 일어나야하는데 그러려면 하나의 클래스가 두개의 테이블에 접근해야함. 이런 역할을 Service가 한다. DAO는 DB와 1:1매칭이기 때문에 못함

@Autowired
private ReplyDAO replyDAO;
@Autowired
private BoardDAO boardDAO;

@Override
public int create(ReplyVO vo) {
	LOGGER.info("create() 호출");
	replyDAO.insert(vo);
	LOGGER.info("댓글 입력 성공");
	boardDAO.updateReplyCount(1, vo.getReplyBno());
	LOGGER.info("게시판 댓글 수 수정 성공");
	return 1;
}

insert/delete는 무조건 한번에 하나의 데이터만 등록되기 때문에 댓글수 컬럼의 증감폭은 1이다

root-context.xml에서 bean으로 설정

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="ds"></property>
</bean>
  • 위에서 설정한 xmlns:tx 사용하기
<!-- 트랜잭션 관리를 수행하는 Aspect를 annotation 기반으로 사용 -->
<tx:annotation-driven/>

네트워크 문제로 데이터 이동 시 데이터가 유실되거나 DB에 반영이 제대로 되지않았을 경우 데이터가 불일치하게 된다

예를 들어 위에서 본 댓글수의 경우 댓글 추가는 되었는데 댓글수 추가가 실행되지 않았을 때 데이터가 불일치하게됨

이 경우 rollback(이전상태로 돌려주기)을 해주는 게 바로 트랜잭션.

@Transactional

  • 두개의 데이터베이스를 변경할 때 앞의 내용이 실행되었고 다음 내용에서 에러가 발생하면 실행된 앞내용을 rollback시킨다.
@Transactional //exception 발생 시 rollback
@Override
public int delete(int replyNo, int replyBno) throws Exception{
	LOGGER.info("delete() 호출");
	replyDAO.delete(replyNo);
	LOGGER.info("댓글 삭제 성공");
	boardDAO.updateReplyCount(-1, replyBno);
	LOGGER.info("게시판 댓글 수 수정 성공");
	return 1;
	}

interceptor

  • 컨트롤러에 들어오는 요청(HttpRequest)과 응답(HttpResponse)을 가로채는 역할. 사전/사후 처리가 중요함.
  • Filter와 하는일은 비슷하지만 interceptor와 호출되는 시점이 다르다. 용도가 다름

    Filter : Application에 등록됨 (프로젝트 전반적)
    Interceptor : Spring Context에 등록됨(webapp/WEB-INF/spring 내부)

Filter, Interceptor, AOP의 각 사용 용도

Filter

전체적인 Request, Response에 대한 처리가 필요할 때 사용
ex) 문자 인코딩

Interceptor

세션 및 쿠키 체크 등 HTTP 프로토콜 단위로 처리가 필요할 때 사용
ex) 로그인 세션 체크

AOP

비지니스(Service)단계에서 세밀한 조정이 필요할 때 사용
ex) 로깅, 트랜잭션, 예외처리

profile
6개월 국비과정 기록하기

0개의 댓글