[4단계 - JDBC 라이브러리 구현하기] 조조(조은별) 미션 제출합니다.#816
[4단계 - JDBC 라이브러리 구현하기] 조조(조은별) 미션 제출합니다.#816cutehumanS2 merged 26 commits intowoowacourse:eun-byeolfrom
Conversation
→ 좋습니다. 👍 적절한 근거만 있다면 🆗.
→ 맞는 방향 같아요. before 처럼 DAO 메서드에서 jdbcTemplate의 메서드로 커넥션을 넘겨주라는 의도는 아니라고 생각합니다. 😸 |
cutehumanS2
left a comment
There was a problem hiding this comment.
조조~.~ 1등으로 제출하셨네요. 😸 진짜 머찌당 😆
마지막 미션 정말 잘 구현해 주셔서 감탄하고 갑니다. ㅎ ㅎ
몇 가지 코멘트 남겨 두었으니,
역시나 조조가 보시기에 필요한 부분만 반영하면 될 것 같아요.
진짜 얼마 안 남았어요. . 파이팅이에요 !!!
남은 주말도 즐(거운) 주(말) 🖤
|
냥인😼 리뷰 반영 후 다시 요청드려요! 추가 변경 사항이 있어요
|
cutehumanS2
left a comment
There was a problem hiding this comment.
조조 ! 빠른 시간 안에 학습 테스트와 리뷰 반영까지 완료하셨네요.
역시 머찐 사람. 😎
반영해 주신 사항 모두 확인했고, 학습 테스트에 몇 가지 질문 남겼습니다. ㅎ.ㅎ
제가 전에 남겼던 요상한 질문과 ‘트랜잭션이 적용되지 않은 경우의 커넥션 처리’ 부분에 대해서도
좀 까다로웠을 텐데 정말 잘 구현해 주셨더라고요. 👍
그런데…!…!….. 제가 질문드리긴 했지만 아직 해당 부분에 대한 이해도가 낮아서 많이 헷갈리네요.
왠지 더 좋은 구조로 만들 수 있을 거 같으면서도 또 모르겠고 그런 상태입니다.. 😔
조오금만 더 공부하고 다시 한번 코드 봐도 될까요? ? 🥺
+) 요구사항은 이미 만족되었고, 코멘트도 질문이 대부분이라
다음 리뷰 요청 때 이만 머지해도 될 것 같아요.
조조의 생각은 어떠신가요~~? 답변은 디엠으로 받겠습니다 !
조금 더 코멘트를 나눠 보고 싶다면 🖤를, 이젠 놓아주고 싶다면 🤍를 남겨주세요.
| @Bean | ||
| public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { | ||
| DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator(); | ||
| autoProxyCreator.setProxyTargetClass(true); | ||
| return autoProxyCreator; | ||
| } |
There was a problem hiding this comment.
[질문]
setProxyTargetClass(true)는 왜 해줘야 하는 걸까요? ?
There was a problem hiding this comment.
true로 설정해서, 프록시 생성 전략을 CGLib로 바꿀 수 있어요.
디폴트 값인 JDK Proxy를 사용하는 생성 전략은
(1) 인터페이스를 반드시 생성해야 하고,
(2) 메서드 레벨에서만 생성 가능한 불편함 있어요.
반면 CGLib를 사용하면 인터페이스를 만들지 않고 클래스에서 프록시 생성이 가능합니다.
| } | ||
|
|
||
| @Bean | ||
| public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { |
There was a problem hiding this comment.
[질문]
DefaultAdvisorAutoProxyCreator의 역할은 뭔가요~?
스프링 빈으로 등록하면 어떤 일이 가능해지나요?
There was a problem hiding this comment.
이름처럼, 자동적으로 프록시를 생성해주는 역할입니다. 스프링 빈 객체 생성시마다 DefaultAdvisorAutoProxyCreator에게 빈을 전달합니다.

출처
동작:
- 빈으로 등록된
Advisor를 찾는다. Advisor내Pointcut에 적용되는 대상(클래스와 메서드)를 찾는다.- 대상이면 프록시를 만들고, Advisor를 연결한다.
- 프록시 생성 후, 프록시 객체를 컨테이너에 돌려준다.
- 컨테이너는 받은 프록시 객체를 빈으로 등록한다.
이점:
매번 ProxyFactoryBean 통해 프록시 만드는 번거로움을 해소할 수 있다.
There was a problem hiding this comment.
👍👍 정리해 보자면, DefaultAdvisorAutoProxyCreator는 자동 프록시 생성 빈 후처리기로, 일일이 ProxyFactoryBean 빈을 등록하지 않아도 타깃 오브젝트에 자동으로 프록시가 적용되게 할 수 있습니다. 번거로움을 덜어주는 존재죠!
|
|
||
| TransactionPointcut pointcut = new TransactionPointcut(); | ||
| TransactionAdvice advice = new TransactionAdvice(platformTransactionManager); | ||
| proxyFactoryBean.addAdvisor(new TransactionAdvisor(pointcut, advice)); |
There was a problem hiding this comment.
[질문]
proxyFactoryBean에는 addAdvice()라는 어드바이스를 등록하는 메서드가 있습니다.
포인트 컷도 어드바이스처럼 그냥 등록할 수도 있었을 텐데,
왜 굳이 Advisor라는 별개의 오브젝트로 묶어서 등록하게 되어 있을까요??
There was a problem hiding this comment.
addAdvice()사용하지 않고, advisor를 사용한 이유?
advice만 추가하면, 모든 빈을 대상으로 advice가 적용됩니다. 내부적으로 DefaultPointcutAdvisor를 통해 advisor를 생성해서 등록해주고 있었습니다.
public DefaultPointcutAdvisor(Advice advice) {
this(Pointcut.TRUE, advice);
}궁금했던 부분이었는데, 덕분에 공부가 되었어요👍
There was a problem hiding this comment.
👍👍
제가 읽은 토비의 스프링이라는 책에서는 아래와 같이 나와 있었어요.
프록시 팩토리 빈에는 여러 개의 어드바이스와 포인트컷이 추가될 수 있는데, 포인트컷과 어드바이스를 따로 등록하면 어떤 어드바이스에 대해 어떤 포인트컷을 적용할지 애매해지기 때문이다. 그래서 이 둘을 Advisor 타입의 오브젝트에 담아서 조합을 만들어 등록하는 것이다. 여러 개의 어드바이스가 등록되더라도 각각 다른 포인트컷과 조합될 수 있기 때문에 각기 다른 메소드 선정 방식을 적용할 수 있다.
위 내용만 읽고 아 확실히 애매하겠네 정도로만 넘어갔는데,
DefaultPointcutAdvisor가 모든 빈을 대상으로 어드바이스가 적용되도록 구현되어 있었군요.
조조 덕분에 저도 또 하나 배워갑니다. 😼
|
냥인~ 리뷰 반영이 많이 늦었죠😿 냥인 리뷰 받을 때마다 매번 놀라요. 그만큼 깊이 있는 학습을 하신거겠죠? 좋은 질문을 많이 주셔서, 덕분에 좋은 방향으로 개선하고 공부하고 있어요🙇♀️ 추가적으로 개선한 부분이 있어요. ✅ 커넥션이 열려 있는지 판단하는 책임을 JdbcTemplate으로 이전before: // UserDaoImpl
@Override
public void update(User user) {
String sql = "update users set password = ? where id = ?";
jdbcTemplate.updateWithActiveConn(sql, user.getPassword(), user.getId());
}userDao에서 트랜잭션을 사용하는지, 하지 않는지 알아야 했어요. 이 점이 굉장히 불편했어요. after: // UserDaoImpl
@Override
public void update(User user) {
String sql = "update users set password = ? where id = ?";
jdbcTemplate.update(sql, user.getPassword(), user.getId());
}UserDao에선 알 필요 없이,
앞서 이야기했던 이런 문제도 발생하지 않아요. // JdbcTemplate
private <T> T execute(String sql, PreparedStatementExecutor<T> executor, Object... params) {
Connection connection = TransactionSynchronizationManager.getResource(dataSource);
if (connection == null) {
return executeWithNewConn(sql, executor, params); // (1)
}
Connection aliveConn = DataSourceUtils.getConnection(dataSource);
return executeWithConn(aliveConn, sql, executor, params); // (2)
}이번 변경사항입니다!💌 |
cutehumanS2
left a comment
There was a problem hiding this comment.
조조 ~~ ⭐️ 안녕하세요. 냥인입니다.
많이 바쁘시죠. ㅜ.ㅜ
그럼에도 열심히 리뷰 반영해 주셔서 정말 감사합니다.
저 또한 조조가 제 리뷰에 남기신 답변을 볼 때마다 놀랐답니다 !
제가 기대하는 답변보다 훨씬 깊이 있는 답변을 남겨주셨을 때가 꽤 있었거든요. 👏
말씀해 주신 대로 이제 정말로 머지해도 될 것 같은데요,
혹시 몰라 approve만 합니다…ㅎㅎ…
⭐️ < 요 이모지 있는 코멘트 한 번 확인하시고, 다시 리뷰 요청 주시면 바로 머지할게요!
정말 수고 많으셨습니다.
조조의 리뷰어여서 (너어어어어무) 좋았습니당 🤍🤍
| public static void releaseActiveConn() { | ||
| DataSourceUtils.releaseActiveConnection(DataSourceConfig.getInstance()); | ||
| } |
There was a problem hiding this comment.
[⭐️]
엇 근데 이 메서드는 테스트에서만 쓰이고 있네요...?
이 메서드를 사용하지 않아도 try-with-resource에 의해 커넥션은 닫히는 것 같아서,
변경 필수는 아닌데 그래도 인지하고 가면 좋을 것 같아 언급합니다 !
There was a problem hiding this comment.
(반영 완료) 테스트에서만 사용한 TransactionExecutorUtils.releaseActiveConn(), DataSourceUtils.releaseActiveConnection() 정리했습니다!
TransactionExecutorUtilsTest는 releaseActiveConn 동작을 검증하는 테스트케이스가 있었는데, 불필요해져서 삭제했어요~
| } | ||
|
|
||
| @Bean | ||
| public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { |
There was a problem hiding this comment.
👍👍 정리해 보자면, DefaultAdvisorAutoProxyCreator는 자동 프록시 생성 빈 후처리기로, 일일이 ProxyFactoryBean 빈을 등록하지 않아도 타깃 오브젝트에 자동으로 프록시가 적용되게 할 수 있습니다. 번거로움을 덜어주는 존재죠!
| @Bean | ||
| public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { | ||
| DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator(); | ||
| autoProxyCreator.setProxyTargetClass(true); | ||
| return autoProxyCreator; | ||
| } |
|
|
||
| TransactionPointcut pointcut = new TransactionPointcut(); | ||
| TransactionAdvice advice = new TransactionAdvice(platformTransactionManager); | ||
| proxyFactoryBean.addAdvisor(new TransactionAdvisor(pointcut, advice)); |
There was a problem hiding this comment.
👍👍
제가 읽은 토비의 스프링이라는 책에서는 아래와 같이 나와 있었어요.
프록시 팩토리 빈에는 여러 개의 어드바이스와 포인트컷이 추가될 수 있는데, 포인트컷과 어드바이스를 따로 등록하면 어떤 어드바이스에 대해 어떤 포인트컷을 적용할지 애매해지기 때문이다. 그래서 이 둘을 Advisor 타입의 오브젝트에 담아서 조합을 만들어 등록하는 것이다. 여러 개의 어드바이스가 등록되더라도 각각 다른 포인트컷과 조합될 수 있기 때문에 각기 다른 메소드 선정 방식을 적용할 수 있다.
위 내용만 읽고 아 확실히 애매하겠네 정도로만 넘어갔는데,
DefaultPointcutAdvisor가 모든 빈을 대상으로 어드바이스가 적용되도록 구현되어 있었군요.
조조 덕분에 저도 또 하나 배워갑니다. 😼
|
냥인~ 항상 빠르게 리뷰해주어 정말 감사합니다❤️ 불필요한 메서드 정리하였고, 덕분에 정말 즐겁게 미션 했어요 << 진짜 진짜 즐거웠어요 |
냥인 안녕하세요~~😸
4단계 구현 완료했고, aop 학습테스트는 곧 빠르게 올리겠습니다!
주요 고민과 변경사항이에요.
1. Service 레이어에서 사용하는 DataSource를 어디서 가져올 것인가?
지난 3단계 질문이었죠! 결론적으로는
DataSourceConfig.getInstance()로 가져오도록 수정했어요.이유:
DataSourceConfig의존적이라고 생각해요. 서비스가 되는 동안 한번 정해진 dataSource 설정들은 변하지 않구요. static이라 외부 주입이 안 된다는 단점이 있지만, 외부 모듈인 JdbcTemplate 생성할 때를 제외하고는 외부 주입할 필요가 없더라구요.TxUserService생성 시 인자로AppUserService만 받고 있더라구요. 물론AppUserService에서getDataSource()를 만들어 사용했을 수도 있지만요~ㅎㅎ2. Connection 재사용 시, Dao에서 JdbcTemplate으로 Connection을 넘겨줄 필요가 있을까?
LMS에 이런 요구사항이 있었어요.
서비스와 DAO에서 Connection 객체를 가져오는 부분은 DataSourceUtils를 사용하도록 수정하자.DAO에서 Connection 객체를 가져온다는 의미인데, 결론적으로는
JdbcTemplate에서 Connection 가져오도록 수정했어요.주석을 붙인 (1),(2) 모두 JdbcTemplate에서 처리할 수 있는 로직이에요. Dao가 알 필요 없다고 생각했어요.
before:
after:
그럼 이번에도 잘 부탁드립니다~💌