이제 기존의 코드를 개선해보자.
코딩을 하다보면 반복적으로 생성되는 부분이 생기기 마련이다.
이를 방치하면 나중에 해당 코드 내용을 개선해야 할때 무수한 반복 작업을 수행하게 된다.
현재 작성된 로그인 기능에서 session의 값을 가져오는 부분이 개선의 대상이다.
각 컨트롤러 마다 HttpSession을 의존 주입하여 메소드 단위로 반복적으로 getAttribute을 통해서 가져오는 것을 리팩토링 해보자.
기존의 session 값을 가져오는 방식을 메소드의 인자로 받을 수 있도록 만들것이다.
그러기 위해서 @LoginUser 라는 어노테이션을 새로 생성하려 한다.
생성에 사용한 어노테이션을 간단히 살펴 보자.
이제 생성된 어노테이션을 파라메터로 지원하기 위해서 ArgumentResolver를 만들어야 한다.
HandlerMethodArgumentResolver를 상속받아 구현체로 만들어야 한다.
이 인터페이스는 지원 조건을 검사하고 참인 경우 해당하는 파라메터를 리턴하는 기능을 지원한다.
기본적으로 스프링에서는 ArgumentResolver에서 해당하는 파라메터를 지원하는지를 비교하는 과정이 수행된다.
여담으로 스프링에서 사용하는 @RequestParam, @ModelAttribute 등도 이러한 판별을 체크한 뒤 지원인 경우에 따라 각각 resolver를 반환하는 구조로 되어 있다.
조건의 기준은 두가지 이다.
1. LoingUser 클래스가 @LoginUser로 선언되있는지 확인.
-> (반환 객체가 null이 아닌인지를 확인.)
2. 파라미터 클래스 타입이 SessionUser.class 인지 확인.
HandlerMethodArgumentResolver를 추가하기 위해서는
WebMvcConfigurer 인터페이스를 상속 받아서 addArgumentResolvers 메소드를 overide 하여 추가해야 한다.
이제 기존에 HttpSession 의존성 주입하던 부분을 리팩토링하자.
우리가 생성한 어노테이션을 파라메터로 제공하고
바로 값을 꺼내는 식으로 리팩토링을 수행하자.
console에서 정상적으로 사용자명을 log로 출력하고 있는 것을 확인할 수 있었다.
이번에는 세션정보 저장을 개선해 보자.
세션 저장소 방식으로는 크게 3가지를 책에서는 언급하고 있다.
위 3가지 방법에는 각각의 특징이 있다.
톰캣은 기본적인 선택지이지만, 두대 이상의 서버를 사용할 경우 서버간 세션 동기화 설정을 요구한다.
데이터베이스는 서버의 대수와는 상관없이 데이터베이스를 이용하므로 확장에는 용의 해보이지만 사용자가 많아질 경우 DB IO에 성능이슈가 발생할 여지가 많아진다.
메모리 데이터베이스는 B2C 서비스에서 많이 사용하는 방식이다.
실제 현업에서는 Embedded Redis 같은 방식이 아닌 별도의 외부메모리 서버를 사용한다고 한다.
하지만 사용료를 지불해야하는 부분에서 AWS 환경에서는 선택이 쉽지 않을 수 있다.
책에서는 학습의 용의성과 비용적 측면을 고려하여 2번을 선택하고 있다.
우선 spring-session-jdbc 의존성을 bundle.gradle에 추가하자.
그리고 application.properties에서
spring.session.store-type=jdbc 설정을 추가해야 한다.
의존성과 properties가 추가되고 해당 라이브러리를 다운받아서 빌드하고 서버를 띄워보자.
그리고 http://localhost:8080/h2-console 에 접근하자.
SRPING_SESSION, SRPING_SESSION_ATTRIBUTES 테이블이 각각 생성 된 것을 확인할 수 있다.
데이터를 살펴보자.
아직 로그인을 하지 않아서 아무것도 없다.
로그인을 하면 데이터가 아래와 같이 생성된다.
하지만 문제가 발생했다.
화면에서 status = 500 에러가 떨어졌다.
console을 확인해보자.
Failed to convert from type [java.lang.Object] to type [byte[]] for value 'com.kdh85.book.springawsstudy.config.auth.dto.SessionUser@6bbe813b'; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [com.kdh85.book.springawsstudy.config.auth.dto.SessionUser]
SessionUser에 직렬화 문제가 발생했다.
해결 방법은 간단하다. SessionUser에 Serializable를 상속받으면 된다.
다시 서버를 시작하여 로그인하면 정상적으로 로그인이 완료된다.
이번 장은 시행착오가 제법 많았던 것 같다.
처음으로 custom 어노테이션을 생성하고 이를 파라메터로 받을 수 있도록
ArgumentResolver로 구현해봤다.
그동안 나는 스프링에 극히 일부만을 사용해본 사람이였다는 것을 다시 한번 깨달았다.
특정시점에서 간섭할 수 있어서 파라메터로 생성하기 위한 검증단계가 있다는 것, 이것을 다형성을 통해서 여러 타입으로 풀어나갈 수 있다는 점이 정말 강력해 보였다.
그리고 평소에서는 별로 신경써 본적이 없었던 직렬화 문제도 처음으로 겪게 되었다.
세션이 데이터베이스화 하는 부분도 기존에는 브라우저 세션만 활용했던 나에게는 서버의 동기화 설정만이 답이 아니라는 것을 깨닫게 해주었다.