[봄싹] 비동기 처리 후유증 해결
어젯밤에 벌인일을 이제사 마무리했다. 덕분에 시원하게 자바 코딩좀 했더니 재미난다.
예전에 이미 스프링 3에 추가된 task 스키마를 사용해서 쓰레드풀을 사용한 executor와 scheduler를 사용할 수 있도록 빈 설정을 해뒀다.
그때 설정한 executor를 사용해서 노티 서비스와 캘린더 서비스를 비동기 호출로 처리할 계획이었고 어제밤에 비동기 호출 예외 템플릿을 만들어 비동기 호출을 하는 것 까지 했다.
그런데 비동기 처리에 익숙하지 않다보니 예상치 못한 문제가 생겼다.
- 스터디 서비스 처리가 끝나면서 하이버네이트 세션이 닫히기 때문에 study.getMeetings() 식으로 OSIV를 믿고 작업했던 부분이 전부 에러가 발생한다.
- 스프링 시큐리티의 기본 시큐리티 컨텍스트 홀더 전략이 쓰레드로컬 방식이기 때문에 SecurityContextHolder.getContext()를 가져와봤자 비어있다. getAuthentication()이 null이다. 비동기로 실행중인 쓰레드에는 아무 정보가 없기 때문이다.
- 스프링 트랜잭션 역시 마찬가지로 쓰레드로컬을 사용하기 때문에 비동기로 호출한 녀석들 중에 SessionFactory.getCurrentSession()을 호출한 녀석들은 전부 에러가 발생한다.
하나씩 해결해 나가자. 먼저 OSIV를 믿고 코딩했던 부분은 DAO 쪽을 조정해서 쉽게 해결할 수 있다. 두가지 방법이 떠오르는데 하나는 Study를 가져올때 미리 join해서 manager, memers, meetings까지 가져오는 것이고 다른 방법은 manager, memebers, meetings가 필요할 때 그때그때 DAO에 물어봐서 가져오는거다. 난 일단 후자로 해놨는데 StudyRepository.getFatchedStudy() 같은 녀석을 추가해 첫번째 방법도 만들어둬야겠다.
두번째 문제를 해결하는 방법은 간단했지만 꼼수에 가까워서 권장하고 싶지는 않다. 비동기 호출 내부에서 필요하는 시큐리티 정볼르 미리 스터디 서비스에서 읽어두고 익명 내부 클래스 형태인 비동기 호출중인 콜백에서 참조해 쓰는것이다. 즉 비동기 호출내에서는 시큐리티 정보 참조를 포기한거나 마차가지가. 미리 스터디 서비스 쪽에서 시큐리리 객체를 읽어놨으니비동기 호출쪽에서는 그때가서 쓰레드 뒤져서 찾아올 필요없이 그냥 참조만 하면 된다. 하지만 스프링 시큐리티를 찾아보니 이미 쓰레드를 많이 쓰는 스윙을 대비해 글로벌 컨텍스트 홀더 전략을 만들어 뒀다. 시큐리티 컨텍스트 홀더가 그 전략을 사용하도록 빈설정으로 더 깔끔하고 안전하게 처리할 수 있을 것 같다.
세번째 이슈는 약간 귀찮다. 스프링의 선언적 트랜잭션은 스프링 AOP를 사용해야 된다. 스프링 AOP를 사용하려면 빈으로 등록헤야 한다. 그러나 내가 만든 예외 템플릿의 콜백은 빈으로 등록될 녀석이 아니다. 그 콜백에 트랜잭션이 걸려야 하는데 아무리 @Transactional을 붙여봤자 소용없는 짓이다. 그래서 난 프로그래밍 방식 트랜잭션처리를 선택했다. 스프링 트랜잭션이 제공하는 PSA API인 PlatformTransactionManager, TrnsactionDefinitino, TransactionStatus세개만 알면 코딩으로 트랜잭션 처리하는것도 매우 간편하다. 그래서 어제 만든 ExceptionTemplate은 SimpleExceptionTemplate으로 변경하고 ExceptionTemplate 인터페이스 하나 만들었다. 비동기 호출을 지원한 예외 템플릿은 AsyncExceptionTemplate으로 만들었고 오늘 아침 트랜잭션까지 지원한느 예외 템플릿은 TransactionalAsyncExceptionTemplate으로 만들었다.
결국.. 다 해결했다. 이제 시큐리티쪽을 글로벌 컨텍스트 홀더로 변경하고 마무리 하자.