월 24,200원
5개월 할부 시다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 해결됨스프링 핵심 원리 - 고급편
포인트컷 활용 질문
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]안녕하세요.개인 토이 프로젝트에 생성과 관련된 트랜잭션이 발생할 때마다 로그를 남기는 기능을 aop를 적용 해서 만들어 보고 싶어서 새로운 어노테이션을 만들어서 구현을 했습니다. 그런데 이렇게 구현하면 aop의 장점인 본래 코드를 수정하지 않고 적용할 수 있다는 점을 놓치게 되는것 같아서 고민이 생겼습니다.새로운 어노테이션을 만들어서 적용하는 것과 관련된 메서드의 이름을 전부 통일( "ex) create~~()" ) 하는 것 중에 어떤 방법이 좀 더 좋은 방법일지 기준이 잘 안섭니다. 사진도 같이 첨부하겠습니다. 감사합니다.+) 혹시 스프링 시큐리티에 대한 강의가 계획에 있으신지 궁금합니다. 당장 계획이 없으시다면 공부하기 좋은 도서도 추천해주시면 감사하겠습니다.
- 미해결스프링 핵심 원리 - 고급편
begin_exception() 에러 로그가 출력되지 않습니다
안녕하세요, 마지막 Test 작성 중에 원하는 상황이 나오지 않아 글 올립니다.아래와 같이 begin_exception() 을 수행하는데 예외가 아니라 정상 출력이 됩니다.어떻게 해야 에러 로그가 뜨는지 알고싶습니다.
- 해결됨스프링 핵심 원리 - 고급편
로그추적기 V1적용 작은 궁금증 하나 있습니다!
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]선생님 안녕하세요 정말 별거 아닌 질문일수도있지만혹시 status를 try문이 들어가기 전에 trace.begin해주면 안되는 이유가 있을까요??강의에서는 null로 먼저 초기화를 해주셨는데 왜 이렇게 하셨는지 궁금합니다!try문으로 들어가서부터 시간을 체크하기 위함인가요??매번 좋은 강의 감사합니다!
- 미해결스프링 핵심 원리 - 고급편
자동 프록시 생성용 빈 후처리기의 반환된 프록시 빈 타입
토비의 스프링을 참고로 스프링 AOP를 공부하는 중 막히는 부분이 있어서 질문 남깁니다.현재 DefaultAdvisorAutoProxyCreator라는 자동 프록시 생성기를 스프링 빈으로 등록하고, 포인트컷과 트랜잭션 부가기능을 적용할 어드바이스를 조합한 Advisor를 통해 트랜잭션이 적용되는지 테스트를 하고 있습니다. 빈 후처리기를 이용한 프록시 자동생성 방식에서 빈 후처리기는 프록시 적용 대상에 대해 프록시를 생성하고, 생성된 프록시 오브젝트를 컨테이너에게 돌려준다고 알고 있습니다. 현재 프록시 자동생성기를 이용해 예외상황에서 트랜잭션 롤백이 되는지 테스트를 돌려보았고, 여기까지 잘 통과했습니다. 그리고 나서 컨테이너가 돌려준 서비스 빈의 타입을 확인하는 과정에서 질문이 있어 남깁니다.토비의 스프링 책에서 확인해보니 자동 프록시 생성기에 의해 프록시 오브젝트가 생성되어 돌려주기 때문에 해당 빈의 타입이 java.lang.reflect.Proxy.class이어야 한다고 했습니다. 그래서 다음과 같이 테스트를 진행했더니 오류가 발생했습니다.혹시 프록시 생성기를 통해 돌려줘야 하는 빈의 타입이 Proxy.class 타입이 맞는 것인지, 제 코드에 문제가 있어서 오류가 발생한 것인지 알고 싶습니다. 트랜잭션 롤백 테스트를 통과한 것을 보면 프록시가 잘 생성되어 어드바이저와 연결된 것 같은데, 어째서 타입이 다르다고 나오는지 알고 싶습니다.#추가스프링의 AOP 프록시를 찾다보니, 프록시를 만드는 방법이 JDK와 CGLIB 두 방식이 있는 것 같습니다. ProxyFactoryBean의 Proxy.newInstance 방식으로 만든 프록시가 아닌 경우 프록시가 CGLIB 방식으로 만들어지는 것인지 궁금합니다. 빈 후처리기가 내장된 프록시 생성기를 통해 프록시를 생성한다고 했는데, DefaultAdvisorAutoProxyCreator 후처리기는 CGLIB 방식을 사용하는 것일까요? 질문이 다소 두서없지만,, 답변 기다리겠습니다. 항상 감사합니다 :)##추추가JDK 다이내믹 프록시를 이용한 트랜잭션 테스트에 대해서 프록시 빈 타입을 확인해보니 java.lang.reflect.Proxy.class와 동일한 것을 확인했습니다!!! DefaultAdvisorAutoProxyCreator 후처리기가 반환하는 프록시는 CGLIB proxy가 맞는 걸 보아하니 스프링은 JDK 다이내믹 프록시가 아닌 프록시 생성은 CGLIB가 디폴트일까요?@Test public void advisorAutoProxyCreator() { assertThat(testUserService).isInstanceOf(java.lang.reflect.Proxy.class); }제 질문만으로 이해가 부족할 것 같아서 테스트 관련한 코드들을 같이 올립니다.@Configuration public class BeanPostProcessorConfig { @Bean public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { return new DefaultAdvisorAutoProxyCreator(); } }@Configuration public class TxAdvisorConfig { private final TransactionAdvice advice; private final UserService userService; @Autowired public TxAdvisorConfig(TransactionAdvice advice, @Qualifier("userServiceImpl") UserService userService) { this.advice = advice; this.userService = userService; } @Bean public NameMatchClassMethodPointcut pointcut() { NameMatchClassMethodPointcut pointcut = new NameMatchClassMethodPointcut(); pointcut.setMappedClassName("*ServiceImpl"); pointcut.setMappedName("upgrade*"); return pointcut; } @Bean public DefaultPointcutAdvisor advisor() { return new DefaultPointcutAdvisor(pointcut(), this.advice); } }@Component public class TransactionAdvice implements MethodInterceptor { private PlatformTransactionManager transactionManager; @Autowired public void setTransactionManager(PlatformTransactionManager transactionManager) { this.transactionManager = transactionManager; } /** * 타깃을 호출하는 기능을 가진 콜백 오브젝트를 프록시로부터 받는다. * 덕분에 어드바이스는 특정 타깃에 의존하지 않고 재사용 가능하다. */ @Override public Object invoke(MethodInvocation invocation) throws Throwable { TransactionStatus status = this.transactionManager.getTransaction(new DefaultTransactionDefinition()); try { /** * 콜백을 호출해서 타깃의 메서드를 실행한다. * 타깃 메서드 호출 전후로 필요한 부가기능을 넣을 수 있다. * 경우에 따라서 타깃이 아예 호출되지 않게 하거나 재시도를 위한 반복적인 호출도 가능하다. */ Object ret = invocation.proceed(); this.transactionManager.commit(status); return ret; } catch (RuntimeException e) { this.transactionManager.rollback(status); throw e; } } }@Service public class UserServiceImpl implements UserService { public static final int MIN_LOGCOUNT_FOR_SILVER = 50; public static final int MIN_RECOMMEND_FOR_GOLD = 30; private final UserDao userDao; private UserLevelUpgradePolicy policy; @Autowired public UserServiceImpl(UserDao userDao) { this.userDao = userDao; } @Autowired public void setPolicy(UserLevelUpgradePolicy policy) { this.policy = policy; } public void add(User user) { if (user.getLevel() == null) { user.setLevel(Level.BASIC); } userDao.add(user); } public void upgradeLevels() { List<User> users = userDao.getAll(); for (User user : users) { if (policy.canUpgradeLevel(user)) { policy.upgradeLevel(user); } } } }@Autowired @Qualifier("testUserService") private UserService testUserService; @Autowired private UserDao userDao; @Component @Primary @Qualifier("testPolicy") static class TestUserLevelPolicy extends UserLevelUpgradePolicyImpl { private String id = "madDitto"; @Autowired private TestUserLevelPolicy(UserDao userDao, EmailPolicy emailPolicy) { super(userDao, emailPolicy); } public void upgradeLevel(User user) { if (user.getId().equals(this.id)) throw new TestUserPolicyException(); super.upgradeLevel(user); } } @Component @Qualifier("testUserService") static class TestUserServiceImpl extends UserServiceImpl { private UserLevelUpgradePolicy testPolicy; @Autowired public TestUserServiceImpl(UserDao userDao, @Qualifier("testPolicy") UserLevelUpgradePolicy testPolicy) { super(userDao); this.testPolicy = testPolicy; super.setPolicy(this.testPolicy); } } @Test @DisplayName("자동 프록시 생성 테스트") public void upgradeAllOrNothingAutoProxy() { userDao.deleteAll(); for (User user : users) { userDao.add(user); } try { this.testUserService.upgradeLevels(); fail("TestUserPolicyException expected"); } catch (TestUserPolicyException e) { } checkLevelUpgraded(users.get(1), false); }
- 미해결스프링 핵심 원리 - 고급편
데코레이터 구성 시 AOP를 사용해도 되는가
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]안녕하세요. 강의 잘 듣고 있습니다.질문은 "단순 데코레이터를 만들고 싶을 때 SpringAOP를 사용해도 되는가?"입니다.질문 매뉴얼의 답이 없는 질문에 포함되는 것 같아 부가설명을 적어보겠습니다. 개인적으로 이전부터 관심사와 의존성을 분리하고 싶어 데코레이터 패턴을 한번씩 쓸 때가 있었습니다.예를 들면 다음과 같습니다.게시글이 작성되면 특정 유저들에게 알림 메세지를 전송해야 한다.게시글이 수정되면 읽기 전용 모델의 캐시를 갱신해야 한다.그럼 [컨트롤러 - 알림데코레이터 - 캐시데코레이터 - 서비스 - 리포지토리 ]가 되는 거죠.이 때 데코레이터-서비스 체인을 구성해야 하는데 2가지 방법이 있었습니다.@Configuration에서 매뉴얼하게 체인 구성한 뒤 빈 생성가장 앞단의 데코레이터에 Primary를 달고 이후 순서에 따라 생성자 파라미터 주입시 @Qualifier로 구현체 주입1번 같은 경우에는 각 데코레이터마다 의존성이 많아질 수록 작성해야 하는 코드가 많아져서 제외를 했습니다.그래서 2번 방법을 사용하고 있고 다음과 같은 문제를 대면했습니다.특정 구현체가 뒷 순서 구현체를 알아야 한다. (의존성 발생) 그것도 컴파일 에러가 나지 않는 문자열(빈 이름)의 형태로.public class PostServiceMessageDecorator implements PostService { private final PostService postService; public PostServiceMessageDecorator( @Qualifier("postServiceCacheDecorator") PostService postService ) { this.postService = postService; } }체인 순서를 구성하는 것이 다소 번거롭고, 순서가 변경되거나 추가, 제거되면 코드를 바꿔야 한다. (다시 한 번 컴파일 에러가 나지 않는 문자열의 형태로)서비스 내에서 데코레이터가 붙지 않는 메소드도 구현을 해줘야 한다.그런데 SpringAOP를 사용하면 3가지 문제를 모두 해결할 수 있는 것이 아닌가 하는 생각이 듭니다.포인트컷도 잡는 것만 잡으면 되니까 데코레이터가 굳이 안붙어도 되는 메소드를 구현할 필요도 없구요.Order로 순서도 간편하게 변경이 가능하니까요.앞서 강의에서 패턴은 의도가 중요하지 실제 구현체는 다양한 방법으로 구현될 수 있다고 하신 말씀이 머릿속에 맴도는데...Aspect를 만들고 네이밍만 EntityServiceSomethingDecorator 라고 이름만 붙이면 되는게 아닌가 하는 생각이 듭니다.그러나 이 방법을 사용하는데 약간의 거부감이 있는데 AOP가 태생적으로 횡단 관심사를 해결하기 위한 기술이라는 사실 때문입니다.저는 흩어져 있는 공통 관심사 코드를 여기 저기 작성하지 않고 한 군데에서 작성하도록 한 게 개발 의도라고 생각했거든요.하지만 예시의 경우는 흩어져 있는 관심사가 아니라 특정 로직에 부가 로직을 몇 개 붙였다 뗐다 하고 싶을 뿐입니다.그렇다면 단순 데코레이터를 만들기 위해 SpringAOP를 사용하는 것은 SpringAOP의 개발 의도와는 약간 다른 사용법이 될 수 있고,보통 특정 기능을 개발 의도와 다른 방향으로 사용하면 예상하지 못한 부작용이 발생하더라구요.이 지점에서 혹시 인사이트를 얻을 수 있을지 질문을 올려봅니다.질문을 조금 다르게 얘기하면 "특정 메서드 혹은 클래스만을 위한 Aspect를 만들어도 되는가?"가 되겠네요.만약 이 경우에 AOP사용은 지양하는 것이 좋다고 생각하신다면, 매뉴얼하게 데코레이터 클래스를 작성하는 것 외에 권장하실만한 방법이 있을까요?
- 미해결스프링 핵심 원리 - 고급편
파이널으로 등록된 빈을 빼면 에러가 날 수 있다는게 어떤 의미일까요?
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]15분 00초부터 파이널 이런걸로 등록된 빈들도 있는데 이건 프록시로 못써서 빼면 에러날 수 있다고 하셨는데요.제가 이 말이 무슨 의미인지 이해를 못했습니다.파이널 클래스인 경우 cglib으로 상속 못받아서 프록시를 못 만들 수 있다는건 이해가 되는데, 빼면 에러가 난다는게 무슨 뜻인지 모르겠어요
- 해결됨스프링 핵심 원리 - 고급편
ThreadLocal 관련하여 질문드립니다.
spring webflux에서 비동기 통신을 한다면한쓰레드에서 블록킹되지 않고 다른 프로세스가 진행되는 것으로 알고 있습니다.webflux에서 threadLocal을 쓴다면 한 thread를 점유하는 의미가 없는것인지 질문드립니다.
- 미해결스프링 핵심 원리 - 고급편
포인트컷과 어드바이스의 관계를 다음과 같이 이해하면 될까요?
프록시대리자를 통칭디자인 패턴프록시 패턴접근 제어캐싱(Caching), 지연 로딩(Lazy Loading)검증(Valid), 인증(Auth)데코레이터 패턴부가 기능로깅(Log)컨버터(Convertor), 포맷터(Formatter)스프링 프록시포인트 컷검증(Filter)만 담당어드바이스검증 이외의 모든 로직을 담당데코레이터 패턴 + 프록시 패턴(캐싱, 지연 로딩)어드바이저포인트 컷 1개 + 어드바이스 1개의 묶음위와 같이 이해하는게 맞게 이해하고 있는지 궁금합니다.
- 미해결스프링 핵심 원리 - 고급편
예시 코드가 실행하면 프록시로 등록이 안되네요ㅠㅠ
이 상태에서 계속해서 proxy로 ExamRepository랑 ExamService 가 proxy로 등록이 안돼서 로그 남기는 거랑 오류 복구 과정이 안됩니다 ㅠㅠ
- 미해결스프링 핵심 원리 - 고급편
쓰레드 로컬과 쓰레드 스택의 차이
동시성 문제를 해결하기 위해서 쓰레드 로컬이 각 쓰레드별 전용 저장공간을 만들어 데이터를 저장한다고 하셨는데 JVM의 메모리영역중에서 힙과 메소드영역은 모든 쓰레드가 공유하지만 각 쓰레드별로 스택공간이 할당된다고 알고 있습니다.그렇다면 쓰레드 로컬을 통해 데이터를 저장하면 각 쓰레드의 스택영역에 데이터가 저장되는 것인가요?만약 아니라면 쓰레드 로컬과 각 쓰레드의 스택영역에 저장되는 데이터들은 어떤 차이가 있는 것인지 궁금합니다.
- 미해결스프링 핵심 원리 - 고급편
@transaction
안녕하세요! 영한님 강의 정말 감사합니다!궁금한게있어요!그러면 Srping 에서 제공하는 트랜잭션이 default로 CGLIB 를 사용하는 것을 알겠습니다. 또한, 프록시 패턴을 통해서 inner method가 트랜잭션이 안탄다는것도 완벽 이해했습니다. ( self invocation )그럼 당연히 @Transactionalpublic void a () { // logic innerMethod()}private void innerMethod() {}와 같은 코드에서도 innerMetohd가 target 에 걸리는 바람에 트랜잭션이 끊기는 걸로 이해했는데요.맞나요!?
- 미해결스프링 핵심 원리 - 고급편
@Slf4j
@Slf4j 어노테이션 의미를 잘 모르겠습니다.현재로그추적기 부분에서 사용되는 부분에서요
- 미해결스프링 핵심 원리 - 고급편
@ControllerAdvice는 AOP 로 구현된것일까?
안녕하세요! 궁금한게 생겼습니다. AOP 라는 것은 방법론일까요? 아니면 CGLIB, JDK Proxy를 통해 기술 만들어진 것을 뜻할까요? @ControolerAdvice를 보면 관심사분리를 통한건 AOP 방법론이 맞는것같은데 구현한 방법은 Proxy가 아닙니다. 이럴 떄 어떠한 답을 내릴수있을까요? 감사합니다!
- 미해결스프링 핵심 원리 - 고급편
강의 자료 다운로드 안돼요
아이패드로 강의 시청하다가 강의자료 다운로드 누르고 다시 노트북으로 다운로드 받을려니깐 다운로드를 받을 수 있는 표시가 없어요
- 미해결스프링 핵심 원리 - 고급편
"3. 템플릿 메서드 패턴과 콜백 패턴" pdf > 38페이지
소스 수정 중에 "@RequireArgsCon..." 를 지워야 오류가 발생하지 않습니다. 어떤 원리때문에, 오류가 발생했을까요?
- 미해결스프링 핵심 원리 - 고급편
"3. 템플릿 메서드 패턴과 콜백 패턴" pdf > 38페이지 질문
- "참고로 TraceTemplate 를 처음부터 스프링 빈으로 등록하고 주입받아도 된다. 이 부분은 선택이다." 라는 문구가 있습니다. 이렇게 하려면 소스를 어떻게 수정해야 하나요?
- 미해결스프링 핵심 원리 - 고급편
TraceId, TraceStatus 에 롬복을 안 쓰는 특별한 이유가 있으신가요?
제목이 곧 내용입니다!
- 미해결스프링 핵심 원리 - 고급편
setter대신 생정자에 @Lazy 어노테이션을 주었는데, 해당 방법도 올바른 방법일까요?
순환참조 방지용으로 생성자에 @Lazy 어노테이션을 추가했습니다.강의내용에는 properties에 다음과 같이 설정하라고 하셨는데spring.main.allow-circular-references=true 위 속성 대신 @Lazy 어노테이션을 사용했을때 문제가 발생할 수 있는 가능성이 있는 케이스가 있을까요?
- 미해결스프링 핵심 원리 - 고급편
JDK 동적 프록시와 기본 생성자
안녕하세요 영한님!! (또는 서포터즈님!!) 강의를 예전에 들은 후 복습을 하다가 한 가지 의문점이 생겨서 찾아보다가 명확하지가 않아서 질문을 드립니다!!JDK 다이나믹 프록시로 동적 프록시를 생성할 때 자바 리플렉션 기반으로 프록시를 생성하는 것으로 알고 있습니다. 추가적으로 리플렉션을 사용하기 위해서는 기본 생성자가 필수로 있어야 하는 것으로 알고 있습니다.하지만 JDK 다이나믹 프록시 같은 경우는 리플렉션을 사용함에도 기본 생성자 없이 프록시를 만드는 것으로 보였습니다.제가 뭔가 놓치고 있는 지점이 있는 것 같은데, 어떤 부분을 잘못 생각하고 있는걸까요 ㅎㅎ..매번 좋은 강의 감사합니다.
- 미해결스프링 핵심 원리 - 고급편
newProxyInstance의 인자중 ClassLoader 질문
newProxyInstance(ClassLoader, Class<?>[] interfaces, InvocationHandler)newProxyInstance의 인자는 위와 같습니다.본 강의에서는 interfaces 배열에 하나의 interfaces만 들어있습니다. 그리고, interfaces배열에 딱 하나 들어있는 그 인터페이스 클래스에서 getClassLoader()로 클래스로더를 받아와서 이를 newProxyInstance를 호출할때 첫번째 인자로 사용하고 있습니다.Q1. 그렇다면 만약, interfaces에 하나가 아닌 여러개가 있다고 하면 클래스로더는 어디서 받아오나요? 임의의 인터페이스에서 골라서 받아오나요?Q2. 사실 클래스로더는 아무 클래스에서 받아와도 상관없는것이고. 관습적으로 interfaces 배열에 들어있는 임의의 인터페이스에서 받아오는것이다. 이런건가요?질문 읽어주셔서 감사합니다.