The Debugging Chronicles : "코드의 미학"

[Spring] AOP(Aspect Oriented Programming) 관점 지향 프로그램 - 2 본문

Spring

[Spring] AOP(Aspect Oriented Programming) 관점 지향 프로그램 - 2

sweetseonah1004 2024. 10. 15. 10:43

 

IoC ====> 결합도를 낮추고

AOP ====> 응집도가 높다

 

스프링은 유지보수 용이하다!!!

 

 

 


AOP 적용은 쉽다.

설정대로 하라는 대로 하면 된다.

하지만 용어에 초점을 맞추어 봐야한다.

 

핵심관심, 비즈니스, CURD를 분리해준다.

 

 

Advice란?

== 횡단 관심

== 공통 로직

 

로그를 찍는 공통 로직.

 

로그는 CRUD가 잘 수행되어서 잘 진입해는지 확인하기 위해 찍는다.

개발자 new 하지 않는다

멤버변수로 만들어 주입한다.

어노테이션 주입

 

그런데 로그를 모든 곳에 찍어야 한다면?

로그 성능을 업데이트 해야한다면?


 

객체지향프로그래밍(OOP)의 한계

; Advice를 교체할때 코드가 많이 변경되므로 개발시간, 컴파일 비용이 많이 필요해진다.

 

해결 방법

>> 스프링 컨테이너야.

너 객체 생성 및 관리 할수 있잖아?

네가해줘라!!

Advice 의 메서드(공통 로직)를 제시간에 호출해줘라~

==> 관점 지향 프로그래밍 (AOP)

 


AOP를 해보자.

 

 

1. Dependency 주입

aop를 스프링 라이브러리에 추가한다.

 

      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-aop</artifactId>
      </dependency>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-aspects</artifactId>
      </dependency>

 

2. 스키마 추가

의존성을 이해하기 위해 스키마 추가

<?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:context="http://www.springframework.org/schema/context"
   xmlns:aop="http://www.springframework.org/schema/aop"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
                  http://www.springframework.org/schema/beans/spring-beans.xsd
                  http://www.springframework.org/schema/aop
                  http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
                  http://www.springframework.org/schema/context
                  http://www.springframework.org/schema/context/spring-context-4.2.xsd">

 

3. Bean 태그 추가

스프링 컨테이너에 제때 알아서 호출되려면 메모리에 있어야한다.

메모리에서 불러오려면 Bean태그를 사용해야한다.

 

 

4.AOP 설정 파일 추가

제때 호출하기 위해 AOP 설정 파일이 필요하다

크게 두가지 부분으로 나뉘는데

1.어떤 무슨 핵심관심에 대해서 

2. 언제 어떤 공통로직을 호출해야하는지

1.어떤  핵심관심에 대해서  => Pointcut

 = = 포인트컷

==  핵심관심,CRUD,비즈니스메서드

execution(* com.koreait.app.biz.board..*Impl.*(..))

 

3부분으로 나뉜다

out 부분 - 타입의 무관하게

메서드 부분 - com.koreait.app.biz내부 안에 모든 메서드를 지금 부터 

input 부분 - 개수, 타입이 무관하다는 뜻에서 점이 두개 찍어 있다.

 

aPointcut에서

CRUD모두다 

 

bPointcut에는 R만

 

 

2. 언제 어떤 공통로직을 호출해야하는지

Aspect(Advisor, 어드바이저)

== 에스팩트

== 어드바이스 + 포인트컷

== 어드바이스(공통로직) 과 포인트컷 (핵심 관심)의 결합

>>>실무에서는 설정 그 자체를 의미하는 경우가 많음

ref= 객체명을 써준다

log advice랑 결합할꺼야

 

 

핵심 관심 후에

핵심관심 반환후에

핵심관심 에러 반환후

핵심관심 전후에

핵심관심 전에

 

 

결합 완료!!


 

Joinpoint

== 조인포인트

== 포인트컷 후보(포인트컷이 될 수 있는 대상)

== 모든 CRUD를 의미함

 

 

Weaving

== 위빙

== 핵심 관심에 공통로직을 끼워 넣는 과정

== 스프링에서는 실행 시 위빙처리가 수행됨

== 런타임 위빙 방식

(스프링에서는 실행되야지만 처리가 된다.)




pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.3.4</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>day058_1001_01</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<name>day058_1001_01</name>
	<description>Demo project for Spring Boot</description>
	<url/>
	<licenses>
		<license/>
	</licenses>
	<developers>
		<developer/>
	</developers>
	<scm>
		<connection/>
		<developerConnection/>
		<tag/>
		<url/>
	</scm>
	<properties>
		<java.version>21</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-tomcat</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		
		<dependency>
    		<groupId>mysql</groupId>
    		<artifactId>mysql-connector-java</artifactId>
    		<version>8.0.33</version>
		</dependency>
		
		<dependency>
    		<groupId>com.fasterxml.jackson.core</groupId>
    		<artifactId>jackson-databind</artifactId>
		</dependency>
		
		<dependency>
		    <groupId>org.springframework</groupId>
    		<artifactId>spring-aop</artifactId>
		</dependency>
		<dependency>
    		<groupId>org.springframework</groupId>
    		<artifactId>spring-aspects</artifactId>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

 

applicationContext.xml

<?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:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
						http://www.springframework.org/schema/beans/spring-beans.xsd
						http://www.springframework.org/schema/context
						http://www.springframework.org/schema/context/spring-context-4.2.xsd
						http://www.springframework.org/schema/aop
						http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
	
	<context:component-scan base-package="com.koreait.app.biz.board" />
	<context:component-scan base-package="com.koreait.app.biz.member" />
	
	<bean class="com.koreait.app.biz.common.LogAdvicePlus" id="la" />
	
	<aop:config>
		<aop:pointcut expression="execution(* com.koreait.app.biz..*Impl.*(..))" id="aPointcut" />
		<aop:pointcut expression="execution(* com.koreait.app.biz..*Impl.select*(..))" id="bPointcut" />
		
		<aop:aspect ref="la">
			<aop:before pointcut-ref="aPointcut" method="teemo" />
		</aop:aspect>
	</aop:config>
		 
</beans>

 

LogAdvice.java

package com.koreait.app.biz.common;

public class LogAdvice {
	public void printLog() {
		System.out.println("공통 관심 - 로그 : 비즈니스 메서드 수행 전에 호출됨");
	}
}

 

LogAdvicePlus.java

package com.koreait.app.biz.common;

public class LogAdvicePlus {
	public void teemo() {
		System.out.println("향상된 로그 ^_^");
	}
}