씸의 장점 중의 하나는 탁월한 상태관리 기능이다. 특히 stateful 컴포넌트를 복잡한 코드 없이도 깔끔하게 정의하고 관리할 수 있다는 것은 매력이다. 엄밀히 말해서 stateful 하지 않은 애플리케이션이란 존재하지 않는다. 모든 화면이 단순 조회 화면이 아닌 이상, 한 페이지/요청에 걸쳐서 유지되는 상태는 반드시 필요하다.

스프링에서도 단순 폼 등록을 처리하기 위해서 @SessionAttribute를 지정해서 폼 지원 모델을 HTTP세션에 저장해두는 방식을 사용한다. 매번 폼 submit시마다 세션에 저장해두었던 초기 모델을 꺼내서 새로운 파라미터로 바인딩 해주는 과정을 거친다. 그리고 최종 폼 등록이 끝나면, RAP 방식으로 리다이렉트를 하든 바로 뷰를 보여주든 상관없이 아무튼 세션에 저장했던 모델을 제거해준다. 구식 MVC의 SimpleFormController도 마찬가지이다.

문제는 개발자가 실수로 세션에 저장된 모델을 제거해주는 코드를 넣지 않았을 경우와 폼의 처리를 완료하지 않은 채로 브라우저를 닫아버리거나 아예 다른 페이지나 다른 사이트로 직접 이동하는 경우이다. 전자는 방치하면 심각하지만 맘 먹으면 간단한 코드리뷰로도 쉽게 찾아낼 수 있다. @MVC는 세션 클리어를 코드에서 명시적으로 해주도록 개발방식이 바뀌어서 이전의 SFC시절보다 코드는 유연하게 작성할 수 있지만, 세션관리의 실수가 발생할 위험은 커졌다.

후자는 근본적으로 쉽게 해결할 수 있는 문제는 아니다. 해결방법은 일정시간 사용자가 서버에 접근하지 않으면 세션이 타임아웃되게 해서 자동으로 지워주는 수 밖에 없다. 문제는 보통 사용자의 불편을 막기 위해 세션 시간을 길게 늘이는 경우가 많다는 것이고, 세션에 저장된 정보는 모두 세션 타임아웃 될 때까지 계속 유지되야 한다는 점이다.

폼을 시작하고 @SessionAttribute로 세션에 모델을 저장하게 만들었는데, 여기서 다른 메뉴로 이동하는 식으로 그냥 떠나는 작업을 계속 반복하다보면 사용자 세션에 불필요한 세션 정보가 쌓이고 서버의 메모리를 쓸데없이 차지하는 문제가 발생한다. 이를 일종의 메모리 누수로 보는 사람들도 있다.

씸은 이런 문제를 세션 타임아웃과 별개로 컨버세이션 별 타임아웃을 지정할 수 있도록 해서 해결한다. 각 폼이나 위저드, 또는 일련의 작업이 시작될 때마다 씸은 독립된 컨버세이션을 만든다. 그리고 상태를 저장할 수 있는 stateful 컴포넌트를 적극 사용한다. 역시 마찬가지로 컨버세이션의 경계를 따라서 정상적으로 종료하지 않고, 다른 페이지로 갑자기 이동해버리면 기존 컨버세이션의 stateful 컴포넌트는 정상적으로 제거될 기회를 잃어버린다. 그대로 방치하면 HTTP세션을 직접 이용하는 방식과 다를바 없이 사용자 HTTP세션 전체가 종료될 때까지 대기해야 할 수도 있다.

하지만 씸은 컨버세이션마다 각각 타임아웃을 지정할 수 있어서, 일반적인 세션 타임아웃보다 짧은 시간을 주면 세션 타임아웃까지 기다리지 않고도 불필요하게 방치된 컨버세이션의 상태를 담은 오브젝트를 제거할 수 있다. 상당히 매력적인 기능이다.

그런데 여기에는 사실 함정이 있다. 컨버세이션 타임아웃은 세션 타임아웃과 동작하는 방식이 다르기 때문이다. 예를 들어 컨버세이션 타임아웃을 아무리 짧게 잡아두었더라도 해당 컨버세이션이 foreground에서 동작하고 있다면, 즉 마지막으로 작업한 화면의 컨버세이션은 컨버세이션 타임아웃에 적용을 받지 않는다. 씸의 컨버세이션 타임아웃은 백그라운드 컨버세이션, 즉 새로운 컨버세이션이 시작되기 전에 존재했던 것들에만 적용이 되기 때문이다. 따라서 아무리 짧은 컨버세이션 타임아웃을 설정해 두었더라도 그 컨버세이션 작업 중에 그냥 자리를 떴다면 컨버세이션 타임아웃의 혜택을 받을 수 없다. 여전히 HTTP세션 타임아웃까지 기다려야 한다.

두번째 함정은 백그라운드 컨버세이션, 즉 이전에 진행중인 위저드나 폼에서 정상적으로 빠져나가지 않고 링크를 타고 다른 데로 이동해버려서 남게 된 컨버세이션의 상태 정보도 백그라운드에 있다고 해서 컨버세이션 타임아웃에 바로 적용이 되는 것이 아니다. 왜냐하면 씸은 요청이 새로 갈 때만 백그라운드 컨버세이션의 타임아웃을 체크해주기 때문이다. 그 말은 역시 사용자가 자리를 뜨거나, 아예 다른 사이트로 이동하거나, 브라우저 창을 닫으면 백그라운드 컨버세이션 조차 컨버세이션 타임아웃의 혜택을 받지 못한다는 얘기다.

그래서 단순히 세밀하게 제어 가능한 HTTP세션과 같은 개념으로 컨버세이션 타임아웃을 이해하면 안된다. 컨버세이션 단위의 타임아웃에는 분명 HTTP세션만큼의 강력한 타임아웃 기능은 없다. 상대적으로 많은 stateful 컴포넌트가 만들어지게될 씸에서는 사용패턴에 따라서 위험부담이 오히려 커질 수도 있다.

씸은 왜 백그라운드 쓰레드를 하나 만들어서 컨버세이션 타임아웃을 제어하는 방식을 사용하지 않았을까 하는 점은 좀 의문이다. 아직 찾아보지는 않았는데 내가 공부한 씸 책이 나온 이후의 새로운 씸 버전에서는 이를 개선했을지도 모르겠다.

어쨌든 스프링에서도 폼에서 사용되는 세션 상태관리가 좀 더 개선이 됐으면 하는 바램이다. 다행히도 스프링은 @SessionAttribute를 통해서 저장되는 폼 모델 정보를 HTTP세션으로 고정하지 않고 언제든 세션 저장 전략을 바꿀 수 있는 DI포인트를 제공해준다. 따라서 이를 보다 세련된 구현으로 바꾼다면 간단한 애노테이션을 추가해줘서 각 폼 별로 독립적인 세션 타임아웃을 가지게 해주는 것도 크게 어렵지 않을 것이다. 또는 HTTP세션과 세션 리플리케이션 보다는 아예 처음부터 고성능 DataGrid에 세션 정보를 저장하게 해서 클러스터 안에서 단순 복제를 사용하지 않더라도 세션 정보를 공유하게 만들 수도 있을 것 같다. 항상 DI를 통한 확장성을 제공해서 실제적인 문제를 해결하도록 만드는 스프링이니, 조만간 누군가 이를 구현하지 않을까 싶기도 한데. 굳이 SWF를 사용하지 않고서도 폼의 세션/상태 정보를 독립적으로 관리하도록 만들어주면 좋겠다. 시간 나면 내가 해볼까…

물론 본격적으로 stateful 스타일의 개발을 한다면 씸이나 SWF와 같은 전용 프레임워크의 도움을 받는 것이 낫겠지만.

© 2017 Toby's Epril Suffusion theme by Sayontan Sinha