기억보다 기록을

[Spring] OOP의 횡단관심(Crosscutting concern)과 AOP(1) 본문

Development/Spring

[Spring] OOP의 횡단관심(Crosscutting concern)과 AOP(1)

juyeong 2022. 2. 2. 23:06
반응형

 개발할 때 신경 써야 하는 요소 중 결합도와 응집도가 있다. 정처기를 준비하며 "결낮높응.. 결낮높응.." 주문처럼 되뇌며 공부했던 기억이 난다. 결합도는 낮아야 하고 응집도는 높아야 한다는 거 알겠다. 알겠는데, 그걸 코드로 어떻게 구현할까? 스프링에서는 이를 위해 어떤 기능을 제공할까? 스프링의 IoC는 결합도와 관련된 기능이고, AOP는 응집도와 관련된 기능이라는데.. 무슨 소리인지 이해해보자. 

 

 여기 비슷하게 생겼는데 연관도 있는 두 단어가 있다. AOP(Aspcet Oriented Programming)와 OOP(Object Oriented Programming)이다. 

 AOP의 핵심은 Seperation of Concerns, 관심분리다. 관심 분리를 위해선 횡단 관심이 무엇인지 알아야 한다.  횡단. 솔직히 잘 안 와닿는다. 횡단 관심은 영어로 Crosscutting concerns이다. '가로로 자르는 관심'이라고 생각했는가? 당신 말이 맞다. 다음 그림을 보자.

 

 계좌이체, 입출금, 이자계산이 핵심 비지니스 로직이라면, 로깅/보안/트랜잭션은 반복되는 코드이다. 부가적인 코드이지만 중요한 역할을 담당한다. 새로운 메소드를 구현하기 위해 우리는 기존의 코드를 가져다 쓴다. 즉, 부가적인 코드들도 반복된다. 반복되는 부가적인 코드들을 효율적으로 관리하기 위해서 횡단 분리가 필요하다.

 

 만약 우리가 Core concern과 Crosscutting concern을 완벽하게 분리할 수 있다면 우리가 구현하는 메소드에는 핵심 비지니스 로직만으로 구성되어 코드는 간결해지고 응집도는 높아질 것이다. 그러나 기존의 OOP언어로는 관심 분리에 한계가 있다. 다음 예시를 보자. 게시판(board)의 비지니스 로직이다.

 

logAdvice.java

모든 비지니스 메소드가 실행되기 전, 공통으로 처리할 로직을 printlog()로 구현

public class LogAdvice {
	public void printLog() {
		System.out.println("[공통로그] 비지니스 로직 수행 전에 동작");
	}

}

 

BoardServiceImpl.java

위의 구현한 메소드를 boardService 컴포넌트에서 사용할 수 있도록 작성

import java.util.List;

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

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

@Service("boardService")
public class BoardServiceImpl implements BoardService {
	@Autowired
	private BoardDAO boardDAO;
	private LogAdvice log;
	
	public BoardServiceImpl() {
		log = new LogAdvice(); 
	}

	public void insertBoard(BoardVO vo) {
		log.printLog();
		boardDAO.insertBoard(vo);
	}

	public void updateBoard(BoardVO vo) {
		log.printLog();
		boardDAO.updateBoard(vo);
	}

	public void deleteBoard(BoardVO vo) {
		log.printLog();
		boardDAO.deleteBoard(vo);
	}

	public BoardVO getBoard(BoardVO vo) {
		log.printLog();
		return boardDAO.getBoard(vo);
	}

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

스프링 컨테이너가 동작하며 BoardServiceImpl 객체가 생성될 때, 생성자로 초기화한 logAdvice객체도 함께 생성된다.

또한 비지니스 메소드가 수행되기 전에 printlog() 메소드를 호출한다. 공통기능을 수정할 때는 printLog()메소드를 수정하면 된다. 언뜻 보면 좋은 방법 같다.

그러나 생각해보자. logAdvice 클래스를 변경해야 하는 상황이라면? printlog()메소드의 시그니처를 변경해야만 한다면? 그때는 어쩔 수 없이 생성자를 수정하고 관련된 모든 메소드도 수정해야 한다. 결합도가 너무 높아 문제가 된다.

 

 OOP 하면 떠오르는 네 가지 특징(추상화, 캡슐화, 상속, 다형성)에서 알 수 있듯이 OOP는 모듈화가 뛰어나다. 그러나 객체를 생성하고 공통 메소드를 호출하는 코드가 비지니스 로직 안에 있다면 횡단 분리에는 한계가 있다. 이 점을 보완할 수 있는 것이 AOP이다.

 

스프링의 AOP를 사용하여 횡단을 분리하는 방법에는 2가지가 있다.

1) aop 라이브러리 추가 (의존성 주입)

pom.xml

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

		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjtools</artifactId>
			<version>${org.aspectj-version}</version>
		</dependency>

2) 네임스페이스 추가 및 aop 설정 (bean으로 등록) 

    2-1)applicationContext.xml의 namespace에서 aop를 추가

    2-2)logAdvice클래스를 스프링 설정 파일에 <bean>등록, aop 관련 설정 추가

 

 

이 때 관련 설정을 추가하며 포인트 컷 개념이 나온다. 포인트 컷이 뭐냐면..!

포인트 컷을 비롯한 aop용어들은 2편으로 이어집니다✍

 

 

 

 

 

 

 

 

 

반응형