관리 메뉴

거니의 velog

231207_SPRING 2 (12-1) 본문

대덕인재개발원_웹기반 애플리케이션

231207_SPRING 2 (12-1)

Unlimited00 2023. 12. 7. 08:24

* AOP

1. 로깅

2. 보안

3. 트랜잭션

4. 에러

* 이 4가지 중에 제일 첫 번째 로깅처리를 할 것이고, 보안은 스프링 시큐리티 이용, 트랜잭션도 AOP 기반. 이를 활성화해야 돌아감. 마지막 에러의 예외처리는 web.xml, 어노테이션, try-catch로 진행해 볼 것.

* AOP를 보면 정말 많이 나오는 예시가 처리속도. 예를 들어 취업해서 회사 들어가 첫 업무가 본인이 맡게 될 프로젝트의 처리속도를 확인해 달라고 요청함. 메소드를 만들어서 테스트 완료 후 팀장님께 간다. 흔쾌히 피드백. 우리 플랫폼에 모든 처리속도를 확인해 달라고 하면? 약 20억개 메소드. 오늘 안에 다 끝내야 하는 업무라면? 하나하나씩 다 열어서 처리 속도를 다 만들어 내야 한다.

* 모든 서비스를 열어서 모듈 마다 복사 붙여 넣기를 해야 하나? 근데, 약 2억개 메소드를 붙이다, 이를 반복적으로 작업할 것이 아니라 하나로만 만들어 서비스가 호출 될 때마다 자동으로 호출되면 안 되나? 이에서 비롯된 개념이 AOP이다.

* 즉, 하나의 작업 단위를 만들어서 이 녀석이 내가 실행하고 싶은 녀석들을 대신 실행시켜주는 작업 단위를 생성하는 것.

* 이 녀석을 가로로 나열해 보면, 각각의 서비스 단에 주 기능들이 들어 있고, 내가 만든 처리 속도를 찍기 위한 기능들이 각 서비스 단에 하나하나 들어 있을 것이다. 이를 결과적으로 이어보면 횡단으로 떨어짐. 공통적인 부가 옵션. 이 녀석을 관심사로 분리하여 횡단으로 분리. 이를 적용한 것이 AOP. 관심사의 분리라고도 한다.

* AOP는 클라이언트에서 내가 실행해야 할 서비스 로직과 같은 기능을 실행하기 이전에 프록시라고 하는 녀석을 만들 것이다. 즉, 내가 돌려야 할 서비스 로직-타겟을 프록시가 자기인양 미리 만들어 둠. 타겟을 위장하고 프록시를 만드는 것. 실제로는 서비스 로직을 호출하는 것 같지만, 위장된 프록시를 돌려서 타겟에 대한 역할을 대신해 주는 것. 

* 우리는 MVC 패턴을 적용한 객체 지향 프로그래밍 개발 시 어떤 기반으로 진행하고 있는가? 인터페이스 기반. 스프링은 결합도를 낮추기 위해 인터페이스를 선언한다. 의존성 주입을 통한 객체를 가져다 쓰는 것을 지향하는 이유.  AOP가 인터페이스 기반으로 돌아가고 있는 프록시를 만들 것. 

* 프록시가 두 가지 종류가 있다. (1) 클래스 기반 프록시 (2) 인터페이스 기반 프록시. 우리는 여기서 인터페이스 기반의 프록시를 사용할 것. 이를 제일 먼저 확인하고 시작할 것이다. 

* 프록시가 적용된 AOP 컨트롤러를 만들 때, 프록시의 포인트컷 표현식이 있다. 어떤 위치에 어떤 클래스 레벨에서 사용한다는 것. 이를 활용해서 어드바이스라는 것을 만들 것. 

* 클라이언트에서 프록시를 통해 타겟 요청 시, 어드바이스라 함은 프록시와 타겟이 클라이언트를 통해 진행되는 과정에서의 시점을 뜻한다.

* 어라운드는 클라이언트에서 타겟을 실행하기 전에, 즉 프록시 실행 전 시작. 타겟 주 기능 실행 후 클라이언트로 돌아가기 전에 한 번 더 실행 된다. 두 군데에서 실행하는 어드바이스.

* 비포어는 타겟 실행 전 실행

* 애프터는 타겟 실행 후 실행

* 애프터 리터닝은 타겟이 정상적으로 종료되었을 때 나타나는 녀석. 에러가 났을 때는 발생하지 않음.

* 애프터 쓰로잉, 타겟 실행 후 에러가 났을 때 발생.

* 각각의 시점에 따라 어드바이스들이 포인트컷에 의해 위빙의 내용들이 AOP컨트롤러를 만들고 이런 어드바이스들에 대한 시점을 포인트컷 표현식을 활용해 위빙(너가 어디 부분에 적용되어 있는지에 대한 시점을 활용)을 쓰는데, XML  파일에 화살표가 3가지 종류로 나타날 것. 이런 녀석들을 위빙이라 한다. 이것이 나타나지 않으면 AOP가 적용되지 않은 것이다.


package kr.or.ddit.aop;

import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

// @Component는 스프링 빈으로 등록하기 위한 어노테이션
// @Aspect는 어노테이션을 붙여 이 클래스가 Aspect를 나타내는 클래스라는 것을 명시
// AOPController 클래스 빈 등록 시, aOPController로 할지 aoPController로 할지가 명확하지 않을 수 있어서 
// aopController라는 이름을 명시함.
@Component("aopController")
@Aspect
public class AOPController {

	/*
	 * [ 14장 : AOP ]
	 * 
	 * 		1. AOP 설명
	 * 
	 * 			[예시 - 가장 많은 예시로 처리속도에 대한 내용]
	 * 
	 * 			306호 반장 최룡학생이 신입으로 프로젝트를 진행하고 있었습니다.
	 * 			그러던 어느날, 지웅 팀장님을 통해서 '반장님, 지금 개발중인 서비스 처리 속도좀 로그로 남겨 주세요!' 라는 부탁을 받는다.
	 * 			반장 최룡 학생은 부탁받은 요구사항을 이행하기 위해서 본인이 만들고 있는 서비스 로직에서 처리 속도를 찍어볼 메소드를 개발해
	 * 			처리 속도가 잘 찍히는 것을 확인합니다.
	 * 			기분이 좋은 최룡 학생은 팀장님께로 달려갑니다.
	 * 			그리고 지웅 팀장님께 해당 내용을 컨펌받습니다.
	 * 			지웅 팀장님은 아주 긍정적인 검토안을 최룡 학생에게 전달하면서 우리 서비스 전체에도 각 처리속도를 찍어 주세요~ 라고 다시 전달합니다.
	 * 			최룡 학생은 본인이 만들어 낸 메소드를 각 기능별 서비스 로직에 하나하나씩 약 2만개 쯤 작성하던 도중, 의문을 갖습니다.
	 * 
	 * 				"서비스 로직에서 제일 중요한 로직은 본래의 기능이 제일 중요하고 지금 내가 작성하고 있는 로직은 옵션(부가기능)
	 * 				이 추가되는 게 아닐까?"
	 * 				"그럼 이걸 하나의 묶음으로는 처리가 불가능한 걸까?"
	 * 
	 * 			하는 생각을 하게 됩니다.
	 * 			여기서, 시간을 측정하고 권한을 체크하는 등의 기능은 옵션과 같은 부가기능으로 일종의 인프라 로직이라고 하는데,
	 * 			이 인프라 로직은 애플리케이션 전 영역에서 나타날 수 있고, 중복 코드를 만들어 내 개발의 효율성을 저하시키고 유지보수가 
	 * 			힘들어 질 수 있습니다.
	 * 
	 * 			이러한 인프라 로직은 아래처럼 하나의 관심사를 가질 수 있는데, 이런 관심사들의 중복을 횡단으로 나열해 보면,
	 * 			이를 가리켜 '횡단 관심사(Cross-Cutting Concern)'이라 부른다.
	 * 
	 * 			-----------------------------------------------------------------------
	 * 				[처리 속도 측정]		[처리 속도 측정]		[처리 속도 측정]		[처리 속도 측정]
	 * 			-----------------------------------------------------------------------
	 * 				[비즈니스 로직]		[비즈니스 로직]		[비즈니스 로직]		[비즈니스 로직]
	 * 				[처리내용 로깅]		[처리내용 로깅]		[처리내용 로깅]		[처리내용 로깅]
	 * 			-----------------------------------------------------------------------
	 * 				로그인 기능			회원가입 기능		게시판 목록			게시판 등록		......
	 * 
	 * 			이러한 횡단 관심사를 통해서 프로그래밍 하는 것을 AOP라고 한다.
	 * 
	 * 			# 간단하게 보고 넘어가기(용어)
	 * 			- Aspect(애스팩트)
	 * 				: AOP의 단위가 되는 횡단 관심사
	 * 			- 횡단 관심사(Cross-Cutting Concern)
	 * 				: 핵심(core) 비즈니스 로직(메소드 실행 시작 시간 출력, 메소드 처리 후 시간 출력 등)과 다소 거리가 있지만,
	 * 					여러 모듈에서 공통적이고 반복적인 처리를 요구하는 내용(메소드 실행 시작 시간 출력 등...)
	 * 			- 횡단 관심사 분리(Separation of Cross-Cutting Concern)
	 * 				: 횡단 관심사에 해당하는 부분(메소드 실행 시작 시간 출력 등)을 분리해서 한 곳으로 모으는 것을 의미
	 * 			- @Component
	 * 				: @Aspect와 짝궁
	 * 				: Component-Scan 시 "나 여기 있어요~ 여기 봐주세요~" 라는 의미
	 * 			- JoinPoint
	 * 				: 어드바이스가 적용될 수 있는 위치
	 * 			- Advice
	 * 				: 어떤 부가기능(메소드 실행 시작 시간 출력)을 언제(메소드 실행 전, 후 등) 사용할 지 정의
	 * 				> Advice(부가기능)는 타겟을 감싸서 위장된 프록시가 실행되기 위한 시점에 따라 옵션이 나뉘어 진다.
	 * 				> 아래에서 각 시점을 알아봅시다.
	 * 					- Before : 조인 포인트 전에 실행 (삼겹살을 굽기 전에)
	 * 					- After : 조인 포인트에서 처리가 완료된 후 실행 (삼겹살을 굽고 먹은 직후 실행)
	 * 					- After Returning : 조인 포인트가 정상적으로 종료 후 실행
	 * 					- After Throwing : 조인 포인트에서 예외 발생 시 실행, 예외가 발생 안하면 실행 안함
	 * 					- Around : 조인 포인트 전후에 실행(삼겹살을 굽기 직전과 먹은 직후 실행)
	 * 
	 * 			# AOP(관점 지향 프로그래밍(Aspect Oriented Programing))
	 * 			- 관점 지향 프로그래밍(Aspect Oriented Programing)을 의미하는 약자이다.
	 * 
	 * 				1-1) 관점 지향 프로그래밍
	 * 
	 * 					소스 코드의 여기저기에 흩어져 있는 횡단 관심사를 중심으로 설계와 구현을 하는 프로그래밍 기법이다.
	 * 					간단하게 설명하자면 관점 지향 프로그래밍은 횡단 관심사의 분리를 실현하는 방법이다.
	 * 
	 * 				1-2) AOP 개발 순서
	 * 
	 * 					1) 핵심 비즈니스 로직에만 근거해서 코드를 작성한다.
	 * 					2) 주변 로직에 해당하는 관심사들을 분리해서 따로 작성한다.
	 * 					3) 핵심 비즈니스 로직 대상 객체에 어떤 관심사들을 결합할 것인지를 설정한다.
	 * 
	 * 				1-3) AOP 사용 예(로.보.트.에)
	 * 					- 로깅 (우리가 사용할 AOP)
	 * 					- 보안 (스프링 시큐리트를 활용한 보안)
	 * 					- 트랜잭션 관리 (@Transactional을 활용한 트랜잭션 처리)
	 * 					- 예외처리 (어노테이션 및 xml 파일 등을 활용한 예외처리)
	 * 
	 * 				1-4) AOP 관련 용어
	 * 					- Aspect : AOP의 단위가 되는 횡단 관심사에 해당한다.
	 * 					- 조인 포인트(JoinPoint)
	 * 						: 횡단 관심사가 실행될 지점이나 시점(메소드 실행이나 예외 발생 등)을 말한다.
	 * 						: 어디에 적용할 것이지 결정, 메소드/객체생성시/필드접근 등등
	 * 					- 어드바이스(Advice)
	 * 						: 특정 조인 포인트에서 실행되는 코드로, 횡단 관심사를 실제로 구현해서 처리하는 부분이다.
	 * 						: 어떤 부가기능을 구현할 것인지 결정(Before, AfterReturning, AfterThrowing, After, Around)
	 * 					- 포인트 컷(Pointcut)
	 * 						: 수많은 조인 포인트 중에서 실제로 어드바이스를 적용할 곳을 선별하기 위한 표현식(expression)
	 * 						: 어드바이스가 적용될 지점
	 * 					- 위빙(Weaving)
	 * 						: 애플리케이션 코드의 적절한 지점에 Aspect를 적용하는 것을 말한다.
	 * 					- 타겟(Target)
	 * 						: AOP 처리에 의해 처리 흐름에 변화가 생긴 객체를 말한다.
	 * 						: 어떤 대상에 대해서 부가 기능을 설정할 것인지 결정.
	 * 
	 * 				1-5) 스프링 지원 어드바이스 유형(부가기능)
	 * 					- Before
	 * 						: 조인 포인트 전에 실행된다.
	 * 						: 예외가 발생하는 경우만 제외하고 항상 실행된다.
	 * 					- After Returning
	 * 						: 조인 포인트가 정상적으로 종료한 후에 실행된다.
	 * 						: 예외가 발생하면 실행되지 않는다.
	 * 					- After Throwing
	 * 						: 조인 포인트에서 예외가 발생했을 때 실행된다.
	 * 						: 예외가 발생하지 않고 정상적으로 종료하면 실행되지 않는다.
	 * 					- After
	 * 						: 조인 포인트에서 처리가 완료된 후 실행된다.
	 * 						: 예외 발생이나 정상 종료 여부와 상관없이 항상 실행된다.
	 * 					- Around
	 * 						: 조인 포인트 전후에 실행된다.
	 * 
	 * 				1-6) AOP의 기능을 활용하기 위한 설정
	 * 
	 * 					- 의존 관계 등록 (pom.xml 설정)
	 * 						> aspectjrt(이미 등록되어 있음)
	 * 						> aspectjweaver (**버전 1.5.4로 등록)
	 * 
	 * 					- 스프링 AOP 설정
	 * 						> root-context.xml 설정
	 * 							: AOP를 활성화하기 위한 태그를 작성합니다.
	 * 
	 * 			# 스프링 AOP
	 * 				스프링 AOP는 동작 시점이 여러 가지가 있지만 (컴파일 시점, 클래스 로딩 시점, 런타임 시점...) 그 중,
	 * 				런타임 시점에 프록시 객체를 생성 후 기능 삽입.
	 * 				> 스프링 AOP의 프록시는 메소드의 오버라이딩(인터페이스 기반의 참조) 개념으로 동작하므로, 
	 * 				메소드 실행 시점 시 동작
	 * 
	 * 			2. 포인트 컷 표현식
	 * 
	 * 				- execution 지시자에 대해서 알아봅시다.
	 * 
	 * 					# 포인트컷(Pointcut)
	 * 					- Advice가 실행될 지점을 표현하는 표현식
	 * 
	 * 						2-1) execution 지시자의 표현 방식
	 * 							- execution 지시자를 활용해 포인트컷을 표현한 것이다.
	 * 
	 * 							- 포인트 컷 표현 요소
	 * 					예) execution(Board kr.or.ddit.service.IBoardService.BoardService*.read*(..))
	 * 
	 * 						------------------------------------------------------------------
	 * 							표현 요소				|		설명
	 * 						------------------------------------------------------------------
	 * 						execution				|	지시자
	 * 						Board					| 	반환값
	 * 						kr.or.ddit.service		|	패키지
	 * 						BoardService*			|	클래스(타입)
	 * 						read*					|	메소드
	 * 						(..)					|	인수, 파라미터
	 * 						------------------------------------------------------------------
	 * 
	 * 						2-2) 포인트컷 표현식에 사용되는 와일드카드
	 * 						------------------------------------------------------------------
	 * 							와일드 카드				|		설명
	 * 						------------------------------------------------------------------
	 * 							*					| 	임의의 패키지 1개 계층을 의미하거나 임의의 인수 1개를 의미한다.
	 * 							..					|	임의의 패키지 0개 이상 계층을 의미하거나 임의의 인수 0개 이상을 의미한다.
	 * 							+					|	클래스명 뒤에 붙여 쓰며, 해당 클래스와 해당 클래스의 서브 클래스, 혹은 구현 클래스 모두를 의미한다.
	 * 						------------------------------------------------------------------
	 * 
	 * 						2-3) 포인트컷 표현식을 적용한 모습
	 * 
	 * 							@Before("execution(* kr.or.ddir.service.IBoardService.BoardServiceImpl*.*(..))")
	 * 							public void startLog(JoinPoint jp) {
	 * 								log.info("startLog" + jp.getSignature());
	 * 							}
	 */
	
}

[pom.xml]

	<properties>
		<java-version>1.8</java-version>
		<org.springframework-version>5.3.25</org.springframework-version>
		<org.aspectj-version>1.5.4</org.aspectj-version>
		<org.slf4j-version>1.6.6</org.slf4j-version>
	</properties>
    
    ......
    
    <!-- AOP를 활용한 로깅 처리 - 라이브러리 추가, 버전 변경 해야 함 -->
    <dependency>
        <groupId>aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>${org.aspectj-version}</version>
    </dependency>

[root-context.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:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
	
	<!-- Root Context: defines shared resources visible to all other web components -->
	
	<!--  
		root-context.xml : 스프링 설정을 위한 파일
		
		스프링 설정이란?
		- view와 관련되지 않은 객체를 정의
		- service(비즈니스 기능), DAO(repository: 저장소), DB 등 비즈니스 로직과 관련된 설정
		- BasicDataSource dataSource = new BasicDataSource();
		  dataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver");
	-->
	
	<!-- dataSource : 데이터베이스와 관련된 정보를 설정한다. -->
	<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
		<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
		<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
		<property name="username" value="spring2" />
		<property name="password" value="java" />
	</bean>
	
	<!--  
		데이터베이스와 연결을 맺고 끊어질 때까지의 라이프 사이클을 관리해주는 SqlSessionTemplate 객체를 생성한다.
		1) dataSource
		2) Mapper xml 위치 지정
		3) 마이바티스 설정 위치 지정
	-->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="mapperLocations" value="classpath:/sqlmap/**/*_SQL.xml" />
		<property name="configLocation" value="/WEB-INF/mybatisAlias/mybatisAlias.xml" />
	</bean>
	
	<!--  
		데이터베이스에 쿼리를 실행시키는 객체
		이 객체를 통해서 query를 실행한다.
	-->
	<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg name="sqlSessionFactory" index="0" ref="sqlSessionFactory" />
	</bean>
	
	<!--  
		Mapper 인터페이스 설정
		개발자가 직접 DAO를 설정하지 않아도 자동으로 Mapper 인터페이스를 활용하는 객체를 생성하게 된다.
	-->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="kr.or.ddit.mapper" />
	</bean>
	
	<!--  
		MultipartFile CommonsMultipartResolver 설정 시
	-->
	<bean id="MultipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
		<!-- 하나의 요청당 파일 업로드 용량(3145728) -->
		<property name="maxUploadSize" value="3145728" />
		<!-- 메모리에 저장되는 최대 용량 -->
		<property name="maxInMemorySize" value="3145728" />
		<property name="defaultEncoding" value="UTF-8" />
	</bean>
	
	<!-- 파일 업로드 디렉토리 설정 -->
	<bean id="uploadPath" class="java.lang.String">
		<constructor-arg value="D:\A_TeachingMaterial\07_JSP_Spring\workspace\workspace_spring2\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\Spring2Project\resources\upload" />
	</bean>
	
	<!-- 스프링 AOP 활성화 -->
	<aop:aspectj-autoproxy />
		
</beans>

@Slf4j
@Controller
@RequestMapping("/crud/board")
public class CrudBoardController {

	@Inject
	private IBoardService service;
	
	@PostConstruct
	public void init() {
		log.info("aopProxy 상태(interface 기반) : {}", AopUtils.isAopProxy(service));
		log.info("aopProxy 상태(클래스 상속 기반) : {}", AopUtils.isCglibProxy(service));
	}

* 내가 지금 사용하고 있는 aop 컨트롤러가 빈으로 등록되어 있는지 확인


* 그 이유는 우리가 만든 패키지 구조와 연관되어 있다.

중첩 등록에 의해 인식 불가.

	<!-- 파일 업로드 디렉토리 설정 -->
	<bean id="uploadPath" class="java.lang.String">
		<constructor-arg value="D:\A_TeachingMaterial\07_JSP_Spring\workspace\workspace_spring2\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\Spring2Project\resources\upload" />
	</bean>
	
	<!-- 스프링 AOP 활성화 -->
	<aop:aspectj-autoproxy />
	<context:component-scan base-package="kr.or.ddit.aop" />
	<context:component-scan base-package="kr.or.ddit.service" />

	<beans:bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
		<beans:property name="definitions">
			<beans:list>
				<beans:value>/WEB-INF/spring/tiles-config.xml</beans:value>
			</beans:list>
		</beans:property>
	</beans:bean>
	<!-- Tiles 설정을 위한 Bean 등록 끝 -->
	
	<context:component-scan base-package="kr.or.ddit.controller" />


package kr.or.ddit.aop;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

// @Component는 스프링 빈으로 등록하기 위한 어노테이션
// @Aspect는 어노테이션을 붙여 이 클래스가 Aspect를 나타내는 클래스라는 것을 명시
// AOPController 클래스 빈 등록 시, aOPController로 할지 aoPController로 할지가 명확하지 않을 수 있어서 
// aopController라는 이름을 명시함.
@Slf4j
@Component("aopController")
@Aspect
public class AOPController {
	
	@Before("execution(* kr.or.ddit.service.IBoardService.*(..))")
	public void startLog(JoinPoint jp) {
		log.info("[@Before]startLog");
		// getSignature() : 어떤 클래스의 어떤 메소드가 실행되었는지를 보여 준다. (파라미터 타입은 무엇인지 보여줌)
		log.info("[@Before]startLog : " + jp.getSignature());
		// getArgs() : 전달된 파라미터 정보를 보여줌
		// 예 ) [Board [boardNo=127, title=개똥이]]
		log.info("[@Before]startLog : " + Arrays.toString(jp.getArgs()));
	}
	
}

위빙이 뜰 것이다.


- http://localhost/crud/board/register


@Slf4j
@Service
public class BoardServiceImpl implements IBoardService {

	@Inject
	private IBoardMapper mapper;
	
	@Override
	public void register(Board board) {
		log.info("BoardServiceImpl register 실행...!");
		mapper.create(board);
	}


	/*
	 * 3. Before 어드바이스
	 * 		- 조인 포인트 전에 실행된다. 예외가 발생하는 경우만 제외하고 항상 실행된다.
	 */
	@Before("execution(* kr.or.ddit.service.IBoardService.*(..))")
	public void startLog(JoinPoint jp) {
		log.info("[@Before]startLog");
		// getSignature() : 어떤 클래스의 어떤 메소드가 실행되었는지를 보여 준다. (파라미터 타입은 무엇인지 보여줌)
		log.info("[@Before]startLog : " + jp.getSignature());
		// getArgs() : 전달된 파라미터 정보를 보여줌
		// 예 ) [Board [boardNo=127, title=개똥이]]
		log.info("[@Before]startLog : " + Arrays.toString(jp.getArgs()));
	}

	/*
	 * 4. After Returning 어드바이스
	 * 		- 조인 포인트가 정상적으로 종료한 후에 실행된다. 예외가 발생하면 실행되지 않는다.
	 */
	@AfterReturning("execution(* kr.or.ddit.service.IBoardService.*(..))")
	public void logReturning(JoinPoint jp) {
		log.info("[@AfterReturning] logReturning");
		log.info("[@AfterReturning] logReturning : " + jp.getSignature());
	}


	/*
	 * 5. After Throwing
	 * 		- 조인 포인트에서 예외가 발생했을 때 실행된다. 예외가 발생하지 않고 정상적으로 종료하면 실행되지 않는다.
	 * 
	 * 			예) crud board에서 delete 쿼리를 'no = 2' 에서 'no 2 ='으로 변경해서 진행
	 */
	@AfterThrowing(pointcut = "execution(* kr.or.ddit.service.IBoardService.*(..))", throwing = "e")
	public void logException(JoinPoint jp, Exception e) {
		log.info("[@AfterThrowing] logException");
		log.info("[@AfterThrowing] logException : " + jp.getSignature());
		log.info("[@AfterThrowing] logException : " + e);
	}
package kr.or.ddit.service.impl;

import java.util.List;

import javax.inject.Inject;

import org.springframework.stereotype.Service;

import kr.or.ddit.mapper.IBoardMapper;
import kr.or.ddit.service.IBoardService;
import kr.or.ddit.vo.Board;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class BoardServiceImpl implements IBoardService {

	@Inject
	private IBoardMapper mapper;
	
	@Override
	public void register(Board board) {
		log.info("BoardServiceImpl register 실행...!");
		mapper.create(board);
	}

	@Override
	public List<Board> list() {
		return mapper.list();
	}

	@Override
	public Board read(int boardNo) {
		return mapper.read(boardNo);
	}

	@Override
	public void modify(Board board) {
		mapper.update(board);
	}

	@Override
	public void remove(int boardNo) {
		log.info("BoardServiceImpl remove 실행...!");
		mapper.delete(boardNo);
	}

	@Override
	public List<Board> search(Board board) {
		return mapper.search(board);
	}

}
	<delete id="delete" parameterType="int">
		delete from board where board_no #{boardNo} = 
	</delete>


	/*
	 * 6. After 어드바이스
	 * 		- 조인 포인트에서 처리가 완료된 후 실행된다.
	 */
	@After("execution(* kr.or.ddit.service.IBoardService.*(..))")
	public void endLog(JoinPoint jp) {
		log.info("[@After] endLog");
		log.info("[@After] endLog : " + jp.getSignature());
		log.info("[@After] endLog : " + Arrays.toString(jp.getArgs()));
	}


	/*
	 * 7. Around 어드바이스
	 * 		- 조인 포인트 전후에 실행된다.
	 * 
	 * 			- ProceedingJoinPoint
	 * 			: around 어드바이스에서 사용함
	 *
	 *			스프링 프레임워크가 컨트롤 하고 있는 비즈니스 로직 호출을 가로챈다.
	 *			책임이 around 어드바이스로 전가되고 그래서 비즈니스 메서드에 대한 정보를 around 어드바이스
	 *			메소드가 기지고 있어야 하고, 그 정보를 스프링 컨테이너가 around 어드바이스 메소드로 넘겨주면
	 *			ProceedingJoinPoint 객체로 받아서 around 어드바이스가 컨트롤 시 활용한다.
	 */
	@Around("execution(* kr.or.ddit.service.IBoardService.*(..))")
	public Object timeLog(ProceedingJoinPoint pjp) throws Throwable {
		long startTime = System.currentTimeMillis();
		log.info("[@Around] : " + Arrays.toString(pjp.getArgs()));
		
		// 메소드 실행
		Object result = pjp.proceed();
		
		long endTime = System.currentTimeMillis();
		log.info("[@Around]pjpEnd : " + Arrays.toString(pjp.getArgs()));
		
		log.info("[@Around] : " + pjp.getSignature().getName() + ", [메소드 실행 시간] : " + (endTime - startTime));
		
		return result;
	}


	/*
	 * 8. 메서드 정보 획득
	 * 		- @Before 어노테이션이 붙은 메소드는 JoinPoint라는 매개변수를 통해 실행중인 메서드의 정보를 구할 수 있습니다.
	 */
	/*
	 * 3. Before 어드바이스
	 * 		- 조인 포인트 전에 실행된다. 예외가 발생하는 경우만 제외하고 항상 실행된다.
	 */
	@Before("execution(* kr.or.ddit.service.IBoardService.*(..))")
	public void startLog(JoinPoint jp) {
		log.info("[@Before]startLog");
		// getSignature() : 어떤 클래스의 어떤 메소드가 실행되었는지를 보여 준다. (파라미터 타입은 무엇인지 보여줌)
		log.info("[@Before]startLog : " + jp.getSignature());
		// getArgs() : 전달된 파라미터 정보를 보여줌
		// 예 ) [Board [boardNo=127, title=개똥이]]
		log.info("[@Before]startLog : " + Arrays.toString(jp.getArgs()));
		
		// 8. 메서드 정보 획득 시 사용
		// 프록시가 입혀지기 전의 원본 대상 객체를 가져온다.
		Object targetObject = jp.getTarget();
		log.info("targetObject : " + targetObject);
		
		// 프록시를 가져온다.
		Object thisObject = jp.getThis();
		log.info("thisObject : " + thisObject);
		
		// 인수를 가져온다.
		Object[] args = jp.getArgs();
		log.info("args.length : " + args.length);
		for(int i = 0; i < args.length; i++) {
			log.info("args["+i+"] : " + args[i]);
		}
	}