티스토리 뷰

이전 포스팅들에서 Spring 프레임워크가 지속적인 DI(의존성 주입)을 통해 객체간 결합도를 줄인다고 강조했었다. 

이번에는 결합도가 아닌 모듈간 공통 코드를 활용하는 정도를 나타내는 "응집도"를 높이기 위한 Spring의 AOP에 대하여 다뤄보겠다.


AOP(관점 지향 프로그래밍)을 간단하게 설명하면, 비즈니스 로직에서 사용되는 메서드들이 가지고 있는 공통의 코드를 따로 분리하여 사용하여 코드의 재사용성을 높이고자 하는 개념이다.

 

AOP를 이해하는데 가장 핵심적인 개념이 "관심 분리"이다. AOP에서는 아래 그림과 같이 메서드마다 공통적으로 작성해줘야 하는 로깅이나 예외, 트렌젝션과 같은 처리 코드들을 횡단 관심이라고 한다. 이에 반해 사용자에 요청에 따라 실제로 수행되는 핵심 비즈니스 로직을 핵심 관심이라고 한다.

 

이러한 관심 분리의 개념은 AOP를 이해하는데 가장 중요한 요소이기 때문에 반드시 이해해야 한다.


우선 Spring 에서 제공하는 AOP 를 사용하기 전에 기존 OOP에서 관심분리가 왜 힘든지 확인해보자.

 

예제는 직전 포스팅에서 계속 사용했던 예제를 사용하겠다.

 

우선 간단하게 Log를 생성하는 클래스와 메서드를 생성하자.

 

◎LogAdvice.java

package com.choonham.biz.board.common;

public class LogAdvice {

	public void printLog() {
		System.out.println("[공통 로그] 비즈니스 로직 수행 전 동작");
	}
	
	public LogAdvice() {
		// TODO Auto-generated constructor stub
	}

}

 

이제, 이 로그를 비즈니스 로직에 있는 메서드들이 수행할 때마다 찍어주도록 추가해주겠다.

 

◎BoardServiceImpl.java

package com.choonham.biz.board.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.choonham.biz.board.BoardService;
import com.choonham.biz.board.BoardVO;
import com.choonham.biz.board.common.LogAdvice;

@Service("boardService")
public class BoardServiceImpl implements BoardService{
	
	public BoardServiceImpl() {
		log = new LogAdvice();
	}
	
	@Autowired
	private BoardDAOSpring boardDAOSpring;
	private LogAdvice log;
	
	@Override
	public void insertBoard(BoardVO vo) {
		log.printLog();
		boardDAOSpring.insertBoard(vo);
	}

	@Override
	public void updateBoard(BoardVO vo) {
		log.printLog();
		boardDAOSpring.updateBoard(vo);
	}

	@Override
	public void deleteBoard(BoardVO vo) {
		log.printLog();
		boardDAOSpring.deleteBoard(vo);
	}

	@Override
	public List<BoardVO> getBoardList(BoardVO vo) {
		log.printLog();
		return boardDAOSpring.getBoardList(vo);
	}

	@Override
	public BoardVO getBoard(BoardVO vo) {
		log.printLog();
		return boardDAOSpring.getBoard(vo);
	}

}

 

위 코드만 보면 감이 올 것이다. 만약에 LogAdvice가 아닌 다른 클래스를 사용하여 로그를 찍으려고 변경하려고 하면, OOP에서는 LogAdvice를 선언부 뿐만이 아니라, 해당 메서들 또한 전부 변경해줘야 한다. 

 

즉, 완전한 관심분리가 이뤄질 수 없는 구조인 것이다.

 

이러한 단점을 Spring에서 제공하는 AOP를 활용하면 쉽게 해결할 수 있다.


이제 본격적으로 Spring 프로젝트에서 AOP를 사용하는 방법을 알아보자

 

1. 우선 JDBC와 마찬가지로 <dependency>를 이용하여 AOP 라이브러리를 설치해야한다.

 

◎pom.xml

이 부분에 아래와 같이 <dependency>를 추가한다.

 

◎pom.xml

 

2. 라이브러리 설치가 끝났으니, AOP를 bean-config 파일에서 등록, 수정할 수 있도록 nameSpace를 추가하자.

이제 applicationContext.xml 파일에 아래 코드를 추가하여, AOP를 등록, 수정하기만 하면 끝이난다. 상세한 AOP 속성에 대한 설명은 다음 포스팅에서 자세하게 다루도록 하겠다.

 

◎applicationContext.xml(추가)

	<bean id = "log" class = "com.choonham.biz.board.common.LogAdvice"></bean>
	
	<aop:config>
		<aop:pointcut expression="execution(* com.choonham.biz.board.. *Impl.*(..))" id="allPointcut"/>
		<aop:aspect ref="log">
			<aop:before  pointcut-ref="allPointcut" method = "printLog"/>
		</aop:aspect>
	</aop:config>
	

이렇게 설정만 해놓으면, 다음과 같이 아무런 Log클래스를 호출하지 않아도 자동적으로 Log가 붙는 걸 확인할 수 있다.

 

◎UserServiceImlp

package com.choonham.biz.board.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.choonham.biz.board.UserService;
import com.choonham.biz.board.UserVO;

@Service("userService")
public class UserServiceImpl implements UserService{

	@Autowired
	private UserDAO userDAO;

	@Override
	public UserVO getUserInfo(UserVO vo) {
		return userDAO.getUserInfo(vo);
	}

	public UserServiceImpl() {
		// TODO Auto-generated constructor stub
	}
}

 

◎Main

package com.choonham.biz;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

import com.choonham.biz.board.UserService;
import com.choonham.biz.board.UserVO;

public class Client {

	public static void main(String[] args) {
		AbstractApplicationContext container = new GenericXmlApplicationContext("applicationContext.xml");
		
		UserService userService = (UserService)container.getBean("userService");
		
		UserVO vo = new UserVO();
		vo.setId("123");
		vo.setPassword("1234");
		
		UserVO user = userService.getUserInfo(vo);
		
		if(user != null){
			System.out.println("ㅎㅅㅎ");
		} 
		
		container.close();

	}

}

 

Comments