diff --git "a/jin/[Spring] AOP \354\202\254\354\232\251\354\213\234 \354\243\274\354\235\230\354\202\254\355\225\255.md" "b/jin/[Spring] AOP \354\202\254\354\232\251\354\213\234 \354\243\274\354\235\230\354\202\254\355\225\255.md"
new file mode 100644
index 0000000..a21a192
--- /dev/null
+++ "b/jin/[Spring] AOP \354\202\254\354\232\251\354\213\234 \354\243\274\354\235\230\354\202\254\355\225\255.md"
@@ -0,0 +1,178 @@
+# 스프링 AOP 주의사항
+
+# 1. 내부 호출 문제
+
+스프링 AOP는 프록시를 기반으로 동작한다.
+스프링은 내가 적용하고 싶은 횡단 관심사를 구현한 프록시 객체를 빈으로 대신 등록해서,
+빈 메서드를 호출하면 프록시 객체의 메서드를 호출하게 도와준다.
+**문제는 한 클래스의 메서드에서 같은 클래스 안의 다른 메서드를 호출 했을 때 발생한다.**
+
+예를 들어 아래 코드에서 exteranl 메서드는 내부적으로 internal 메서드를 호출한다.
+
+```java
+@Slf4j
+@Component
+public class CallServiceV0 {
+
+ @Transactional
+ public void external() {
+ log.info("call external");
+ // 내부 메서드호출
+ this.internal();
+ }
+
+ @Transactional
+ public void internal() {
+ log.info("call internal");
+ }
+}
+```
+
+이때 외부에서 `exteranl()`를 호출한다면, 프록시가 적용되어 횡단 관심사 코드가 추가된 버전의 external이 호출되겠지만,
+**그 메서드에서 호출한 `this.internal()`는 실제 객체의 메서드로 프록시의 것이 아니다.**
+**따라서, 횡단 관심사가 적용되지 않았다!**
+
+
+
+아래와 같은 상황인 것이다. exteranl만 프록시 객체의 것이 호출되었다.
+
+![image](https://github.com/binary-ho/TIL-public/assets/71186266/09fead5e-58c9-4a9f-9ccf-d4f230e921ea)
+
+
+
+
+이러한 문제가 발생할 수 있다는 것을 인지하고, 내부 호출 문제를 만들지 않도록 주의해야 한다.
+
+
+## 1.1 내부 호출 대안
+1. 자기 자신을 주입해 호출한다. : 자기 자신을 주입 받아 가지고 있게 한다. 그 다음 주입 받은 객체를 호출하면 된다.
이때 순환 참조가 발생할 수 있으므로 `Setter`를 통해 주입 받아야 한다.
예를 들어 위 예시의 경우엔 `internal()`메서드를 주입 받은 자기 자신 객체의 `internal()`을 호출하는 것
+2. 지연 조회 : ObjectProvider<>나 아예 ApplicationContext에서 빈을 뽑아 사용한다.
ObjectProvider는 객체를 스프링 컨테이너에서 조회하는 시점을 빈 생성 시점이 아니라, 실제 객체를 사용하는 시점으로 지연할 수 있다. 호출할 때가 되서야 컨테이너에서 빈을 조회한다.
+3. **구조를 변경한다.** : 그냥 내부 호출의 대상 메서드인 `internal()`과 같은 메서드를 다른 객체로 분리해서 사용한다. (제일 괜찮은 방법이고 권해진다.)
다만 구조가 이미 합리적인데도, 이러한 목적으로 객체를 분리해야 한다는 점이 껄끄럽다. 스프링 프록시 방식 AOP의 한계점이다.
+
+
+# 2. 프록시 기술과 한계점
+
+결국 앞서 말한 것처럼 프록시 기술을 사용하면서 한계점이 있을 수 밖에 없다. 내부 호출 문제가 있다.
+
+## 2.1 타입 캐스팅 문제
+JDK Dynamic Proxy와 CGLIB를 사용해 AOP 프록시를 만드는 방법에는 장단이 있지만,
+JDK Dynamic Proxy는 인터페이스가 필수이고, CGLIB는 구체 클래스를 기반으로 만들어야 한다는 한계점이 있다.
+
+그런데, 스프링 AOP는 CGLIB를 인터페이스인 경우에도 `proxyTargetClass` 옵션을 통해 사용할 수 있게 해준다. (true인 경우 CGLIB, flase인 경우 JDK 동적 프록시)
+반대로 **JDK 동적 프록시는 구현체인 구체 클래스로 타입 캐스팅이 되지 않는다는 한계가 있다.**
+
+그러니까, 인터페이스 MemberService가 있고, 그 구현체인 MemberServiceImpl이 있다고 해보자.
+이떄 MemberServiceImpl를 기반으로 만든 프록시는 MemberServiceImpl로 변환이 불가능하다!
+
+```java
+@SpringBootTest
+public class ProxyTest {
+
+ @Test
+ public void test() {
+ /* MemberServiceImpl의 프록시를 생성한다. */
+ MemberServiceImpl memberServiceImpl = new MemberServiceImpl();
+ ProxyFactory proxyFactory = new ProxyFactory(memberServiceImpl);
+ /* JDK 동적 프록시 사용 설정 */
+ proxyFactory.setProxyTargetClass(false);
+
+ /* 이건 되는데 */
+ MemberService memberService = (MemberService) proxyFactory.getProxy();
+
+ /* 이건 안 된다. */
+ MemberServiceImpl memberServiceImpl2 = (MemberServiceImpl) proxyFactory.getProxy();
+ }
+
+}
+
+/*
+ * 결과 - ClassCastException 발생
+ * java.lang.ClassCastException: class jdk.proxy3.$Proxy105 cannot be cast to class hello.aop.order.aop.proxy.MemberServiceImpl (jdk.proxy3.$Proxy105 is in module jdk.proxy3 of loader 'app'; hello.aop.order.aop.proxy.MemberServiceImpl is in unnamed module of loader 'app')
+ * at hello.aop.proxy.ProxyTest.test(ProxyTest.java:24)
+ * */
+```
+
+MemberServiceImpl을 기반으로 만든 프록시지만, MemberServiceImpl로 변환이 불가능하다.
+MemberServiceImpl를 기반으로 만든 프록시지만, MemberSerivce의 구현체이고, MemberServiceImpl는 모르기 떄문이다! (아래 그림 참고)
+
+![image](https://github.com/binary-ho/TIL-public/assets/71186266/b431594f-80f0-478f-b8e9-8f2567dfc3c3)
+
+
+![image](https://github.com/binary-ho/TIL-public/assets/71186266/633863a7-4776-4397-a85e-d1daf10d6c3a)
+
+
+
+
+그러나, CGLIB는 부모-자식 관계이므로, 당연히 캐스팅이 가능하다.
+
+![image](https://github.com/binary-ho/TIL-public/assets/71186266/3c8d9e83-ffdc-4f6a-86b5-96007c32da63)
+
+
+그래서 이게 뭐가 중요하냐?
+
+## 2.2 CGLIB의 구체 클래스 기반 프록시 문제
+
+CGLIB는 구체 클래스를 기반으로 프록시를 만드는데 그래서 아래의 문제를 겪는다.
+1. 대상 클래스에 기본 생성자가 필수이다.
+2. 생성자가 2번 호출되는 문제가 발생할 수도 있다.
+3. final 키워드 클래스나 final 메서드를 사용할 수 없음.
+
+
+
+
+### 2.2.1. 대상 클래스에 기본 생성자가 필수이다.
+CGLIB는 대상 클래스를 상속 받기 떄문에, 자식 클래스의 생성자를 호출할 때, 부모 클래스의 생성자도 호출된다.
+CGLIB 프록시는 대상 클래스를 상속 받고, 생성자에서 대상 클래스의 기본 생성자를 호출한다.
+**따라서 대상 클래스에 파라미터가 하나도 없는 기본 생성자를 꼭 만들어야 한다.**
+
+
+
+
+### 2.2.2 생성자가 2번 호출되는 문제가 발생할 수도 있다.
+CGLIB는 구체 클래스를 상속 바고, 부모 클래스의 생성자도 호출한다.
+이때 생성자가 2번 호출되는데
+1. Target의 객체를 생성하면서 한번 (내가 직접 생성할 때 호출)
+2. 프록시 객체를생성할 때 부모 클래스의 생성자가 호출되며 두번 호출된다.
+
+![image](https://github.com/binary-ho/TIL-public/assets/71186266/7a555899-685f-4ec1-9ad8-44e4d9394bc8)
+
+
+
+생성자에서 뭔가를 변화시키는 로직이 있다면 즐거운 상황은 아니다.
+
+
+
+### 2.2.3 final 키워드 클래스나 final 메서드를 사용할 수 없음.
+
+CGLIB는 상속을 기반으로 하기 떄문에 클래스나 메서드에 `final`이 붙어있다면 꼼짝없이 프록시가 생성되지 않거나, 정상 동작하지 않는다.
+
+
+
+- 결국 JDK 동적 프록시는 대상 클래스 타입으로 주십시의 문제가 있고
+- CGLIB는 대상 클래스에 기본 생성자가 필수이며, 2번 호출된다는 문제가 있다.
+
+
+# 3. 스프링에서 위 문제들을 해결한 방식
+스프링은 AOP 프록시 생성을 편리하게 해주기 위해 많이 고민했고, 계속해서 기술을 변경해왔다.
+
+#### 1. CGLIB를 스프링 코어 내부에 함꼐 패키징 (스프링 3.2 ~ )
+#### 2. CGLIB에서 기본 생성자 없이 객체를 생성하도록 변경 (스프링 4.0 ~ )
+-> `objensis`라는 기본 생성자 없이 객체 생성을 가능하게 하는 특별한 라이브러리를 도입해서 문제를 해결했다.
+
+
+#### 3. 생성자 2번 호출 문제 해결 (스프링 4.0 ~ )
+-> `objensis`의 기능을 사용해 생성자가 1번만 호출되도록 변경
+
+
+#### 4. CGLIB 기본 사용 (스프링 부트 2.0 ~ )
+-> 스프링 부트 2.0 부터 CGLIB를 기본으로 사용해, 구체 클래스 타입으로 의존관계를 주입하는 문제를 해결 (애초에 구체 클래스로 구현하니까)
+
+
+
+## 결론
+1. 내부 호출을 조심하자
+2. CGLIB로 AOP 구현시 final 키워드만 조심하자.
+스프링은 CGLIB의 여러 단점들을 해결하는 한편, 별도 설정이 없다면 인터페이스가 있더라도 JDK 동적 프록시가 아닌 CHLIB를 사용해 구체클래스를 기반으로 프록시 생성하도록 했다.
+덕분에 final 키워드 문제 외의 문제들은 전부 해결됐다. 개발자는 아무것도 모르고 써도 손쉽게 AOP를 구현할 수 있다.
+
+