본문 바로가기

스프링

스프링 삼각형과 설정 정보 - AOP / PSA

AOP(Aspect-Oriented Programming)

관점 지향 프로그래밍

 

관점지향은 쉽게 말해 어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어서 보고 그 관점을 기준으로 각각 모듈화하겠다는 것이다. 스프링 DI가 의존성(new)에 대한 주입이라면 스프링AOP는 로직(code)에 대한 주입이다.

 

아래의 그림을 보면 입금, 출금, 이체 모듈 모두 <로깅, 보안, 트랜잭션> 기능이 반복적으로 나타나는 것을 볼 수 있다.

이처럼 다수의 모듈에 공통적으로 나타나는 부분이 존재하는 것을 "횡단 관심사"라고 한다.

 

황단 관심사

핵심 관심사는 모듈별로 다르지만 횡단 관심사는 모듈별로 중복되어 나타나는 부분이다. 프로그래머의 입장에서 "반복/중복은 분리해서 한 곳에서 관리하라"는 말이 떠오르지만 AOP에서는 더욱 진보된 방법을 사용한다.

 

스프링 AOP는 로직주입이며 로직주입을 한다면 어디에 주입할 수 있을까?

객체지향에서 로직이 있는 곳은 당연히 메소드의 안쪽이다. 그럼 메소드에서 코드를 주입할 수 있는 곳은 몇 군데일까?

Around / Before / After / AfterReturning / AfterThrowing 5군데이다.

예시

public class Boy {
	public void runSomething() {
		System.out.println("열쇠로 문을 열고 집에 들어간다.");

		try {
			System.out.println("컴퓨터로 게임을 한다.");
		} catch (Exception ex) {
			if (ex.getMessage().equals("집에 불남")) {
				System.out.println("119 에 신고한다.");
			}
		} finally {
			System.out.println("소등하고 잔다.");
		}

		System.out.println("자물쇠를 잠그고 집을 나선다.");
	}
}

위의 Boy라는 클래스에는 현재 핵심관심사와 횡단관심사가 한 메소드 안에 같이 사용되고있다.

 

-개선-

public interface Person {
	void runSomething();
}

public class Boy implements Person {
	public void runSomething() {
		System.out.println("컴퓨터로 게임을 한다.");
	}
}

Person 인터페이스를 구현하도록 Boy.java를 작성한다.

그리고 핵심 관심사가 아닌 부분, 횡단 관심사는 작성하지 않는다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation=
       "http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
	
  <aop:aspectj-autoproxy />
  
  <bean id="myAspect" class="aop002.MyAspect" />
  <bean id="boy" class="aop002.Boy" />
</beans>

XML파일 수정하자.

@Aspect
public class MyAspect {
	@Before("execution(* runSomething())")
	public void before(JoinPoint joinPoint) {
		System.out.println("얼굴 인식 확인: 문을 개방하라");
		// System.out.println("열쇠로 문을 열고 집에 들어간다.");
	}
}

MyAspect코드를 작성하자.

  • @Aspect는 이 클래스를 이제 AOP에서 사용하겠다는 의미이다.
  • @Before는 대상 메서드 실행 전에 이 메서드를 실행하겠다는 의미다.

메소드의 joinPoint는 Boy.runSomething()을 의미한다. 이제 사용자가 얼굴을 인식하고 문을 여는 것이 아니라

스프링 프레임워크가 사용자를 인식해 자동으로 문을열어준다.

 

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathApplicationContext;

public class Start {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathxmlAppliationContext("aop002/aop002.xml");
        
        Person romeo = context.getBean("boy", Person.class);
        
        romeo.runSomething();
    }
}

이제 Start.java를 스프링 프레임워크 기반에서 구동될 수 있게 작성하자.

 

Boy의 핵심관심사만 남기기 위해 4개의 파일(인터페이스, Boy, XML, Aspect파일)의 수정을 해야했지만,

Boy는 핵심 관심사만 가지게 되었고 추가 개발과 유지보수가 굉장히 편리해졌다.

AOP를 Boy.java에 적용함으로써 단일책임원칙도 자연스레 적용할 수 있게 되었다.

 

프록시를 이용한 메서드 간접호출

<aop:aspectj-autoproxy />는 무엇인가?

  • 프록시 패턴을 이용해 횡단 관심사를 핵심 관심사에 주입하는 것

메서드를 호출하면 "프록시"가  그 요청을 받아서 진짜 객체에게 전달한다.

이때 "프록시"는 그냥 전달만 하지 않고 주고 받는 내용을 감사하거나 조작할 수 있다.

 

스프링 AOP는 프록시를 사용한다. 그런데 스프링 AOP에서 재미있는 것은 호출하는 쪽(romeo.runSomething() 메서드 호출)에서나 호출당하는 쪽(romeo 객체) 그 누구도 프록시가 존재하는지조차 모른다는 것이다. 오직 스프링프레임워크만 프록시의 존재를 안다.

 

결국, <aop:aspectj-autoproxy />는 스프링 프레임워크에게 AOP 프록시를 사용하라고 알려주는 지시자이다.

 

스프링 AOP의 핵심

  • 스프링 AOP는 인터페이스(interface) 기반이다.
  • 스프링 AOP는 프록시(proxy) 기반이다.
  • 스프링 AOP는 런타임(runtime) 기반이다.

 

PSA(Portable Service Abstraction)

일관성 있는 추상화

 

서비스 추상화의 예로 JDBC를 들 수 있다.

JDBC라고 하는 표준 스펙이 있기에 오라클을 사용하든, MySQL을 사용하든, MS-SQL을 사용하든 Connection, Statement, ResultSet을 이용해 공통된 방식으로 코드를 작성할 수 있다. 데이터베이스 종류에 관계없이 같은 방식으로 제어할 수 있는 이유는 디자인 패턴에서 설명했던 어댑터(변환기) 패턴을 활용했기 때문이다. 

다수의 기술을 공통의 인터페이스로 제어할 수 있게 한 것을 서비스 추상화라고 한다.

 

스프링 프레임워크에서는 서비스 추상화를 위해 다양한 어댑터를 제공한다.

예를 들어, OXM(Object XML Mapping : 객체와 XML 매핑) 기술만 하더라도 Castor, JAXB, XMLBeans, JiBX, XStream 등 다양한 기술이 있는데, 이 다양한 기술들이 제공하는 API는 제각각이다. 스프링은 제각각인 API를 위한 어댑터를 제공함으로써 실제로 어떤 OXM 기술을 쓰든 일관된 방식으로 코드를 짤 수 있게 지원한다.

 

또한 하나의 OXM 기술에서 다른 OXM 기술로 변경할 때 큰 변화 없이 세부 기술을 교체해서 사용할 수 있게 해준다. 이처럼 서비스 추상화를 해주면서 그것도 일관성 있는 방식을 제공한다고 해서 이를 PSA (일관성 있는 서비스 추상화)라고 한다. 스프링은 OXM뿐만 아니라 ORM, 캐시, 트랜잭션 등 다양한 기술에 대한 PSA를 제공한다.

 

참고

스프링 입문을 위한 자바 객체지향의 원리와 이해

'스프링' 카테고리의 다른 글

Spring Bean  (0) 2022.02.20
[SpringBoot] PasswordEncoder 적용하기  (0) 2022.02.18
의존성주입 DI(Dependency InJection)의 종류  (0) 2022.02.09
스프링  (0) 2022.02.09
1장 오브젝트와 의존관계  (0) 2021.12.01