"인프런의 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 강의를 듣고 작성한 글 입니다."
이전 강의 정리글에서 AOP가 적용되어 각 메소드들의 시간 측정로직이 잘 동작하는 것을 확인해 볼 수 있었다.
START : execution(String hello.hellospring.controller.HomeController.home())
END : execution(String hello.hellospring.controller.HomeController.home()) 5ms
START : execution(String hello.hellospring.controller.MemberController.list(Model))
START : execution(List hello.hellospring.service.MemberService.findMembers())
START : execution(List org.springframework.data.jpa.repository.JpaRepository.findAll())
Hibernate: select member0_.id as id1_0_, member0_.name as name2_0_ from member member0_
END : execution(List org.springframework.data.jpa.repository.JpaRepository.findAll()) 124ms
END : execution(List hello.hellospring.service.MemberService.findMembers()) 130ms
END : execution(String hello.hellospring.controller.MemberController.list(Model)) 144ms
이런식으로 시간 측정로직을 적용해주면 각 기능별로 어디서 시간이 오래 걸리고, 어디서 병목 현상이 발생하는지 점검해 볼 수 있게된다.
AOP를 잘 활용하면 ProceedingJoinPoint 객체에서 제공해주는 각종 메소드들을 통해 얻을 수 있는 것들에 한해서 원하는 대로 조작을 할 수도 있을뿐 더러,
AOP 는 각종 기능 메소드들이 호출될 때마다 인터셉트로 걸려서 기능이 수행되는데 이를 이용햇 조건을 걸어서 proceed 메소드가 동작하지 못하게 만들어 줄 수도 있다.
(중간에 인터셉트로 걸려서 실행되는 특징 때문에 메소드 시작지점에 필요한 동작들이 AOP 를 통해 수행되고 나면 proceed 메소드를 통해 반드시 본래 실행 시키려 했던 메소드들이 실행 될 수 있게끔 흐름을 진행 시켜주어야 한다.)
AOP 를 사용하면 다음과 같이 문제점들을 해결해 줄 수 있다.
1. 회원가입, 회원조회 등 핵심 관심사항과 시간을 측정하는 공통 관심사항을 분리해 줄 수 있다.
2. 시간을 측정하는 로직을 별도의 공통 로직으로 만들 수 있다.
3. 핵심 관심사항을 깔끔하게 유지할 수 있다.
4. 변경이 필요하면 AOP 에 작성된 로직만 변경해주면 된다.
5. 원하는 적용 대상을 선택할 수 있다.(@Around 어노테이션 에서 적용대상 선택가능, 보통 패키지 레벨로 많이 선택한다.)
그럼 AOP 는 구체적으로 어떻게 동작하는걸까?
AOP 의 동작 원리
AOP를 적용하기 전 스프링에서의 클래스간 의존관계는 다음과 같다.
AOP 를 적용하기 전 스프링에서의 클래스간 의존관계는 지금까지 공부해온 대로 그냥 스프링 빈 등록 이후 생성자를 통한 의존성 주입 등으로 클래스간 의존관계를 형성해주는 것이다.
즉, 클래스들 사이에 끼어들어 오는 것이 없다.
그런데 AOP 를 사용하고 @Around 어노테이션을 통해 어디에 AOP를 적용할 건지 지정해주고 나면 의존관계가 완전히 달라진다.
만약 MemberService 클래스에 AOP 가 적용되었다고 한다면 의존관계를 가지는 클래스들의 중간에서 가짜 MemberService 클래스를 만들어내는데, 이를 프록시 객체라고 한다.
이렇게 생성된 가짜 MemberService 는 각 클래스들을 스프링 빈으로 등록할 때 실제 MemberService 객체의 앞에 세워진다.
그리고 앞에 등록된 가짜 객체에 대한 수행이 끝나고 proceed 메소드가 동작하면 그때서야 실제 MemberService 객체로 넘어가서 동작을 수행하게 된다.
즉, 의존관계에 있어서 MemberService 를 주입받는 경우 의존성을 주입받게 되는 것은 실제 객체가 아니라 프록시로 만들어진 가짜 객체인 것이다.
이를 실제로 확인하는 방법이 있다.
MemberController 에서 MemberService 객체가 의존성 주입될 때 확인해 보는것이 가능하다.
사실 별건 아니고, 의존성을 주입해주는 생성자에서 출력문을 작성해주고, 여기서 MemberService 객체에 대해 getClass() 메소드를 사용해주면 된다.
- MemberController.java
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
System.out.println("memberService = " + memberService.getClass());
}
위와 같이 출력문을 작성해주고 어플리케이션을 다시 동작시켜 보면 다음과 같은 로그를 출력하는 것을 확인해 볼 수 있다.
memberService = class hello.hellospring.service.MemberService$$EnhancerBySpringCGLIB$$569c0b8e
로그를 자세히 보면 단순히 MemberService 에서 문자열이 끝나는게 아니라, 그 뒤에 Enhancer 어쩌고 저쩌고 하더니 CGLIB 가 나온다.
여기서 CGLIB 는 CG 라이브러리 라는 뜻인데, 여기서 CG 라이브러리란 MemberService 와 같은 객체들을 복제해서 코드를 조작해주는 기술이다.
요약하자면 스프링에서 AOP 가 적용되었을 때, 의존성 주입 시 스프링이 AOP 가 적용된 클래스를 확인하면 CG 라이브러리를 통해 해당 클래스의 가짜 객체(프록시 객체) 를 생성해서 실제 객체 앞에 세워두고,
이로 인해 의존성 또한 실제 객체가 아닌 프록시로 만들어진 가짜 객체가 주입되면서 AOP 에 작성해놓은 각종 로직이 각 메소드가 동작하기 전에 흐름을 인터셉트 하여 수행되게끔 만들어 줄 수 있다.
스프링에서는 이를 프록시 방식의 AOP 라고 한다.
* 참고 : 좀 더 심화했을 땐 어떤게 있나면, 아예 자바 소스코드의 컴파일 타임 때 코드의 위아래로 AOP 와 같이 특정 로직들을 집어 넣어주는 것도 가능하다고 한다.
'Spring basic' 카테고리의 다른 글
스프링 핵심원리 : 기본편 - 좋은 객체지향 설계의 5가지 원칙(SOLID) (0) | 2022.02.09 |
---|---|
스프링 핵심원리 : 기본편 - 스프링과 좋은 객체지향 프로그래밍에 대하여.... (0) | 2022.02.09 |
스프링 입문 : 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 - AOP #1 (0) | 2021.11.17 |
스프링 입문 : 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 - Spring Data JPA (0) | 2021.11.16 |
스프링 입문 : 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 - JPA (0) | 2021.11.15 |