본문 바로가기
👨‍💻 2. 웹개발_Back end/2-6 Spring

[Spring] 둘째 날 1장 스프링 AOP - AOP 이해하기 및 라이브러리와 네임스페이스 추가

by 달님🌙 2021. 10. 27.
반응형

 

실습 코드 참조

 

moonhy7/SpringFramework: Spring Framework 실습 코드 정리 (github.com)

 

GitHub - moonhy7/SpringFramework: Spring Framework 실습 코드 정리

Spring Framework 실습 코드 정리. Contribute to moonhy7/SpringFramework development by creating an account on GitHub.

github.com

 

 

1.1절 AOP 이해하기

 

1. 결합도와 응집도

 

- 비즈니스 컴포넌트 개발에서 중요한 두 가지 원칙 : 낮은 결합도와 높은 응집도

- 스프링의 의존성 주입을 이용하면 객체들의 결합도를 떨어뜨릴 수 있어서 의존관계를 쉽게 변경 가능

- IoC (Inversion of Control) : 낮은 결합도를 만들어주는 프로그래밍

- AoP (Aspect Oriented Programming) : 높은 응집도를 만들어주는 프로그래밍

 

2. 핵심 관심과 횡단 관심

 

1) 비즈니스 메소드 코드 특성

- 핵심 비즈니스 로직은 몇 줄 안 되고 로깅, 예외, 트랜잭션 처리 같은 부가적인 코드가 대부분임

- 비즈니스 메소드에 부가적인 코드들이 반복되어 코드 분석이나 유지보수가 어려워짐

- AOP는 이러한 부가적인 공통 코드들을 효율적으로 관리하도록 함

 

2) 관심 분리 (Separation of Concerns)

- 횡단 관심 (Crosscutting Concerns) : 메소드마다 공통으로 등장하는 로깅이나 예외, 트랜잭션 처리 같은 코드들

- 핵심 관심 (Core Concerns) : 사용자의 요청에 따라 실제로 수행되는 핵심 비즈니스 로직

- 이 두 관심을 완벽하게 분리한다면 실제 비즈니스 로직만으로 메소드를 구성할 수 있으므로 더욱 간결하고 응집도가 높아짐

 

3. 객체지향 (OOP) vs 관점지향(AOP)

 

- OOP 언어에서는 위에서 말한 공통 코드를 완벽하게 독립적인 모듈로 분리해내기가 어려움

- 스프링에서는 AOP를 이용하여 관심 분리 문제를 해결할 수 있음 

- Java, JS 등은 객체지향프로그래밍(OOP)이고 Spring은 관점지향프로그래밍(AOP)임

 

4. OOP에서 완벽한 관심 분리가 어려운 이유 ( _025_BoardWeb_Business_UnderstandAOP ) 

 

1) LogAdvice 클래스 생성

- BoardService 컴포넌트의 모든 비즈니스 메소드가 실행되기 직전에 공통으로 처리할 로직을 LogAdvice 클래스에 printLog() 메소드로 구현

package com.springbook.biz.common;

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

 

 

2) BoardServiceImpl 클래스 

- printLog() 메소드를 사용할 수 있도록 LogAdvice 멤버변수 등록

package com.springbook.biz.board.impl;

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.Log4jAdvice;
import com.springbook.biz.common.LogAdvice;

@Service("boardService")
/*
 * 업무로직 처리를 담당하는 클래스
 * ex) 계좌이체 기능 처리할 때 DB입력은 DAO 하게 되는데 
 * 	   DB입력 전 필요한 작업들(계좌암호화, 금액 컴마 추가...) 하는 로직들을 ServiceImpl클래스에서 처리해주면 됨 
 * */
public class BoardServiceImpl implements BoardService{
	@Autowired
	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.upadateBoard(vo);
	}
	
	public void deleteBoard(BoardVO vo) {
		log.printLog();
		boardDAO.deleteBoard(vo);
	}
	
	public BoardVO getBoard(BoardVO vo) {
		return boardDAO.getBoard(vo);
	}
	
	public List<BoardVO> getBoardList(BoardVO vo) {
		log.printLog();
		return boardDAO.getBoardList(vo);
	}
}

 

 

3) 실행 결과

- BoardServiceImpl 객체가 생성될 때 생성자에서 LogAdvice 객체도 함께 생성됨

- 각 비즈니스 메소드에서 비즈니스 로직을 수행하기 전에 LogAdvice 클래스의 printLog() 메소드를 호출

- 공통 기능 수정 시 printLog() 메소드만 수정하면 되므로 관리가 편해짐

- 단점 : 메소드 이름 변경시 BoardServiceImpl 클래스의 모든 메소드 호출 부분을 수정해야함

 

4) Log4jAdvice 클래스 생성

- 변경될 로그 클래스 생성

package com.springbook.biz.common;

public class Log4jAdvice {
	public void printLogging() {
		System.out.println("[공통 로그-Log4j] 비즈니스 로직 수행 전 동작");
	}
}

 

5) BoardServiceImpl 클래스 

- 이번에는 printLogging() 메소드를 사용할 수 있도록 Log4jAdvice로 수정해주어야함

- 메소드 이름도 일일이 다 수정해주어야하는 단점

package com.springbook.biz.board.impl;

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.Log4jAdvice;
import com.springbook.biz.common.LogAdvice;

@Service("boardService")
/*
 * 업무로직 처리를 담당하는 클래스
 * ex) 계좌이체 기능 처리할 때 DB입력은 DAO 하게 되는데 
 * 	   DB입력 전 필요한 작업들(계좌암호화, 금액 컴마 추가...) 하는 로직들을 ServiceImpl클래스에서 처리해주면 됨 
 * */
public class BoardServiceImpl implements BoardService{
	@Autowired
	BoardDAO boardDAO;
	private Log4jAdvice log;
	
	public BoardServiceImpl() {
		log = new Log4jAdvice();
	}
	
	public void insertBoard(BoardVO vo) {
		log.printLogging();
		boardDAO.insertBoard(vo);
	}
	
	public void updateBoard(BoardVO vo) {
		log.printLogging();
		boardDAO.upadateBoard(vo);
	}
	
	public void deleteBoard(BoardVO vo) {
		log.printLogging();
		boardDAO.deleteBoard(vo);
	}
	
	public BoardVO getBoard(BoardVO vo) {
		return boardDAO.getBoard(vo);
	}
	
	public List<BoardVO> getBoardList(BoardVO vo) {
		log.printLogging();
		return boardDAO.getBoardList(vo);
	}
}

 

6) 실행 결과

 

7) 결론

- 모듈화가 뛰어난 언어인 OOP를 사용한다해도 공통 모듈인 Advice 객체 생성코드와 공통 메소드 호출 코드가 비즈니스 메소드에 들어있으면 핵심 관심과 횡단 관심을 완벽히 분리할 수 없음

- 스프링의 AOP는 이 한계를 극복할 수 있음

 

2. AOP 시작하기

 

- 목표 : BoardServiceImpl 소스와 무관하게 LogAdvice와 Log4jAdvice 클래스 메소드 실행이 가능

 

1. 비즈니스 클래스 수정 ( _026_BoardWeb_Business_Start_AOP )

- BoardServiceImpl 클래스를 원 상태로 돌려 로그 클래스들과는 아무 상관 없도록 만들기

package com.springbook.biz.board.impl;

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.Log4jAdvice;
import com.springbook.biz.common.LogAdvice;

@Service("boardService")
/*
 * 업무로직 처리를 담당하는 클래스
 * ex) 계좌이체 기능 처리할 때 DB입력은 DAO 하게 되는데 
 * 	   DB입력 전 필요한 작업들(계좌암호화, 금액 컴마 추가...) 하는 로직들을 ServiceImpl클래스에서 처리해주면 됨 
 * */
public class BoardServiceImpl implements BoardService{
	@Autowired
	BoardDAO boardDAO;
	
	public void insertBoard(BoardVO vo) {
		boardDAO.insertBoard(vo);
	}
	
	public void updateBoard(BoardVO vo) {
		boardDAO.upadateBoard(vo);
	}
	
	public void deleteBoard(BoardVO vo) {
		boardDAO.deleteBoard(vo);
	}
	
	public BoardVO getBoard(BoardVO vo) {
		return boardDAO.getBoard(vo);
	}
	
	public List<BoardVO> getBoardList(BoardVO vo) {
		return boardDAO.getBoardList(vo);
	}
}

 

 

2. AOP 라이브러리 추가

 

1) <dependency> 설정 추가

- BoardWeb 프로젝트에 있는 pom.xml 파일 수정하여 AOP 관련 라이브러리 추가

- aspectjweaver 내려받을 수 있도록 pom.xml 파일에 <dependency> 설정 추가

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

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.8</version>
</dependency>

 

2) 라이브러리가 추가 확인

- Maven Dependency에 라이브러리가 추가되었는지 확인

 

3) 최신 버전 확인

- 최신 버전을 쓰고 싶으면 검색을 통해 최신 버전을 추가해주면 됨

 

3. 네임스페이스 추가 및 AOP 설정

 

1) 네임스페이스 추가

- AOP 설정을 추가하려면 AOP에서 제공하는 엘리먼트들을 사용해야함

 

 

2) AOP 설정

- LogAdvice 클래스를 등록 후 AOP 네임스페이스를 추가

<!-- AOP 설정 -->
<context:component-scan base-package="com.springbook.biz"></context:component-scan>

<bean id="log" class="com.springbook.biz.common.LogAdvice"></bean>

<!-- AOP 설정 -->
<aop:config>
  <aop:pointcut expression="execution(* com.springbook.biz..*Impl.*(..))" id="allPointcut"/>
  <aop:aspect ref="log">
  	<aop:before pointcut-ref="allPointcut" method="printLog"/>
  </aop:aspect>
</aop:config>

 

4. 테스트 및 결과 확인

 

1) bean 객체에 LogAdvice로 설정한 실행 결과

- BoardServiceClient 프로그램 실행

- 두 메소드가 호출될 때 LogAdvice 클래스의 printLog() 메소드가 실행되는지 확인

 

2) bean 객체에 Log4jAdvice로 설정한 실행 결과

- 로그 파일 교체시에는 스프링 설정 파일의 AOP 설정에서 <bean> 객체의 class 속성과 method만 수정하면 됨

 

3) Maven 오류 해결법

 

4) 정리

- 스프링의 AOP는 클라이언트가 핵심 관심 메소드를 호출할 때 횡단 관심 메소드를 적절하게 실행해줌

- 여기서 핵심 관심 메소드와 횡단 관심 메소드 사이에서 소스상의 결합은 발생하지않음 (우리가 원하던 바)

 

 

반응형

댓글