CS/김영한 스프링 강의

    스프링 핵심 원리 - 고급편 - 섹션13. 스프링 AOP - 실무 주의사항

    실제로 사용하다가 겪을 수 있는 경우의 수들 일단 프록시 클래스를 내부 호출 했을 때 프록시 적용 안되는 문제 프록시를 하고 있는 클래스인데 이 클래스 안에서 자신의 내부 함수를 직접 호출하고 있다면 여기엔 프록시가 적용되지 않는다. 외부에서 하면 프록시의 함수를 실행하므로 당연히 된다. 참고로 이건 AspectJ 프레임워크를 쓴다면 이렇게 스프링 AOP처럼 프록시 클래스를 만드는게 아닌 저 클래스 함수 안에 코드를 직접 쓰기 때문에 이런일이 없다. 하지만 설정이 너무 복잡하고 이걸 스프링 AOP로 해결할 수 있는 방법도 많아 거의 쓰지 않는다. 그래서 클래스 내부에서도 자기 함수를 직접 불러오는게 아닌 자신도 프록시 클래스의 함수를 사용하도록 바꿔보자. 생성자로 받으면 아직 빈에 생성되지도 않은 걸 받..

    스프링 핵심 원리 - 고급편 - 섹션12. 스프링 AOP - 실전 예제

    앞에서 배웠던 거 실제로 쓰기 실패할 시 자동으로 재시작 하는 로직을 어노테이션으로 해보자. 요새 이러는게 추세라고 함. 일단 여기에 로그용 어노테이션 @Trace를 만들어서 적용해보자. 어노테이션 @Trace가 붙어있다면 이 프록시를 만들어 적용해라. 를 원하는 함수에 붙여준다. 잘 된다. 이제 @Trace말고 재시작 하는걸 해보자. 잘 된다. 이제 어느 함수가 시간이 오래걸릴 때 경고 뜨게 한다던지 하는 식으로 창의적으로 활용하면 된다. 좋은건 한번 잘 만들어두면 어느 메소드나 클래스에서든 재활용할 수 있다.

    스프링 핵심 원리 - 고급편 - 섹션11. 스프링 AOP - 포인트컷

    은근슬적 포인트컷 지시 문법들 그냥 넘어갔었는데 이번에 다룬다. execution을 가장 많이 사용하고 나머지는 자주 사용하지는 않아 execution 위주로 볼거다. 기반 및 테스트코드 작성 MemberService 클래스 인스턴스를 만들어 거기에 hello라는 함수를 가져온 모습이다. 이걸 기반으로 포인트컷 문법들을 본다. @Slf4j public class ExecutionTest { AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); Method helloMethod; @BeforeEach public void init() throws NoSuchMethodException { helloMethod = MemberServic..

    스프링 핵심 원리 - 고급편 - 섹션9,10. 스프링 AOP 개념,구현

    지금 문제가 뭐냐면 실제로 비즈니스 기능을 수행하는 핵심 기능과 로그를 남기는 부가 기능으로 나누는데, 이런 부가 기능은 대체로 여러 클래스에서 쓰일 확률이 높다. 그래서 여러 클래스에 거쳐 사용되기 때문에 횡단 관심사라고 하고, 때문에 사용하려면 각각의 클래스마다 복붙을 해야 한다. 현실적으론 복잡해지는데, 원하는 클래스마다 함수를 써서 넣어야 하고 이걸 유틸같은 함수로 정의해서 사용해도 결국은 안의 코드를 수정해야 한다. 왜냐하면 핵심 기능을 감싸는 코드여야 하기 때문 이 문제를 해결하기 위해 스프링에서 AOP를 지원한다. 이런 고민은 특정 앱을 만든 한명이 아닌 보통 만들때 고민하는 문제들이라 아얘 지원하는걸 만들었다. @Aspect인데, 지금까지는 클래스 각각 하나를 보면서 앱을 만들었다면, 횡단..

    스프링 핵심 원리 - 고급편 - 섹션8. @Aspect AOP

    스프링은 @Aspect 애노테이션으로 매우 편리하게 포인트컷과 어드바이스로 구성되어 있는 어드바이 생성 기능을 지원한다. 별 설정없이 @Aspect만 했는데도 된 이유가 위의 @Around가 포인트컷의 역할을 하고, 그 밑의 함수가 어드바이스의 역할을 하기 때문. 처음에 컴포넌트 스캔할 때 @Aspect가 붙어있으면 이걸 기반으로 어드바이저 빌더가 어드바이저를 생성해서 프록시를 만든다. 이 방식이 매우 편리해서 실무에선 대부분 @Aspect를 사용한다. 근데 사실 만든 로그 찍는 기능이 하나의 클래스에서만 관심있는게 아니라 여러 클래스에서 관심있어 지정한라 이를 횡단 관심사라고 하다. 다음 섹션부턴 이런 횡단 관심사를 전문으로 해결하는 스프링 AOP에 대해 알아볼거다.

    스프링 핵심 원리 - 고급편 - 섹션7. 빈 후처리기

    빈 후처리기는 @Bean으로 정의한 걸 스프링이 컴포넌트 스캔해서 가져가는데, 빈 저장소에 넣기 직전에 뭔가를 더 해줄 수 있도록 정의하는 것. 이 빈 후처리기에서 바꿔주면 된다. 테스트 코드를 작성해보자. public class BasicTest { @Test void basicConfig() { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BasicConfig.class); // A는 빈으로 등록된다. A a = applicationContext.getBean("beanA", A.class); a.helloA(); // B는 빈으로 등록되지 않는다. Assertions.asse..

    스프링 핵심 원리 - 고급편 - 섹션6. 스프링이 지원하는 프록시

    전 섹션에서의 문제는 인터페이스가 있을때랑 없을때랑 적용 방법이 달랐다. 이런걸 추상화해서 통일해준게 스프링. 프록시 팩토리를 만들어줬다. 예시코드를 작성해보자. 이 MethodInterceptor의 맨 상위 부모가 Advice다. 이미 invocation 안에 어떤 클래스를 타켓으로 하는지 생성할 때 저장해놓았기 때문에 그냥 proceed()만 하면 된다. 감싸지길 원하는 실제 비즈니스 클래스를 타켓으로 하고 프록시 팩토리 안에 넣어준 뒤 프록시 받아오면 된다. 프록시 팩토리로 만들어진 프록시 클래스인지와 이게 동적으로 만들었는지 정적으로 만들어졌는지 조차도 알 수 있다. 구체 클래스로도 만들어보자. 그리고 인터페이스가 있어도 구체클래스처럼 CGLIB를 만들도록 설정할 수도 있다. 참고로 스프링 부트에..

    스프링 핵심 원리 - 고급편 - 섹션5. 동적 프록시 기술

    동적 프록시를 이해하려면 일단 자바의 리플렉션 기술이 뭔지부터 알아야 한다. @Slf4j public class ReflectionTest { @Test void reflection0() { Hello target = new Hello(); // 공통 로직1 시작 log.info("start"); String result1 = target.callA(); // 호출하는 메소드가 다름 log.info("result1={}", result1); // 공통 로직1 끝 // 공통 로직2 시작 log.info("start"); String result2 = target.callB(); // 호출하는 메소드가 다름 log.info("result2={}", result2); // 공통 로직2 끝 } @Test voi..

    스프링 핵심 원리 - 고급편 - 섹션4. 프록시 패턴과 데코레이터 패턴

    일단 v1, v2, v3를 만든다. v2 수동으로 빈에 등록하려고 여기선 @Controller를 사용하지 않음 v3 얘내들은 컴포넌트 스캔에 걸리도록 정직하게 어노테이션을 걸어준다. 새로운 요구사항으로 원본 코드는 건들지 말고 추적 기능을 추가해야 한다. 근데 앞뒤로 있어야 넣을 수 있는 추적 기능을 어떻게 수정 없이 넣을 수 있는가? 프록시를 쓰면 된다. 프록시는 대신 실행해주는거라고 보면 된다. 여기선 원본 코드와 똑같은데 기능이 조금 더 추가된 프록시를 실행하는 거다. 접근 제어가 목적이면 프록시 패턴으로, 새로운 기능 추가가 목적이면 데코레이터 패턴이라고 부르기로 했다. 이해를 위한 예제 코드를 작성하자. 그냥 프록시가 뭔지를 보여주기 위한 빈 프록시다. 만약 여러번 실행했을 때, 처음에 실행할 ..

    스프링 핵심 원리 - 고급편 - 섹션3. 템플릿 메서드 패턴과 콜백 패턴

    이 섹션에서 템플릿 메서드 패턴, 전략 패턴, 탬플릿 콜백 패턴을 알아볼거다. 전 섹션에서 본 로그추적기를 도입하려고 했는데, 막상 해놓고 보니 부가적으로 반복되는 코드가 너무 장황해졌다. 핵심기능은 단 한줄인데 단지 로그 추적을 위한 부가기능으로 뭔가 앞뒤로 덕지덕지 붙어서 너무 보기 흉하다. 이건 메소드가 2개라서 그나마 괜찮아 보이지 막 클래스와 메소드가 100개씩 넘어가면 더 힘들어진다. 하지만 반복되는 부분을 메서드로 따로 빼려고 해도 핵심 기능이 try catch로 감싸져 있어 이것도 쉽지 않다. 그래서 이 문제를 해결하는 디자인 패턴이 템플릿 메서드 패턴이다. 일단 이게 뭔지 알기 위해 테스트코드를 작성해본다. 주변은 같은데 안에만 다를 때 추상화 클래스 상속을 사용하면 된다. 이게 뭔지는 ..