매우 간단한 요구사항이라고 생각했는데 생각보다 그렇지 않았어서 정리를 해 둔다. 기본기 부족이었다. 😅
로그인 직후 메인페이지로 이동됐을 때 안내 팝업을 표시한다.
JS에서 referer 헤더 값이 로그인 URL일 때 로직을 실행하는 아이디어였다.
로컬에서 잘 동작하는 것을 확인하고 5분 컷이라고 좋아하면서 테스트서버에 적용해 보았는데 동작하지 않았다... 왜인지 테스트서버에는 referer 헤더가 없었다.
Referrer-Policy에 따라 referer 값을 제공하거나 그렇지 않을 수 있다고 한다('referer'은 'referrer'의 오타이지만 공식 헤더 명칭이다). 브라우저는 기본적으로 같은 origin 끼리만 문서나 스크립트를 로드할 수 있다는 Same-Origin Policy를 사용하며, 다른 Origin과의 상호작용이 있을 때에는 CORS(Cross-Origin Resource Sharing)이라는 제한적 정책을 사용한다.
Origin이 동일하다는 것은 scheme, hostname, port가 동일하다는 의미다.
따라서 기능을 referer에 의존하는 것은 좋지 않다.
로그인 직후 여부 판단을 컨트롤러에서 HttpSession 객체의 isNew() 메소드를 이용하자는 아이디어였다. isNew() 메소드는 해당 세션 객체가 새로 만들어진 세션의 경우 true를, JSESSIONID로 조회한 세션인 경우 false를 반환한다.
그런데 로그인 직후에도 세션의 isNew() 값이 false 였다. 로그아웃 후에도 세션이 사라지지 않았기 때문이다.
로그아웃 로직에 세션 삭제를 추가하더라도 문제는 해결되지 않는다. 무상태 프로토콜인 HTTP의 특성 상 사용자가 로그아웃을 하지 않고 브라우저를 닫았다면 서버는 사용자가 로그아웃을 한 건지 알 수가 없기 때문이다. 따라서 로그아웃 여부보다는 세션 타임아웃 설정에 따라 세션을 삭제하는 것이 일반적이다.
HttpSession 객체의 getCreationTime()을 사용하려고도 해 보았는데, JSESSIONID를 통해 세션에 접근할 때마다 타임아웃이 초기화되면서 세션의 creationTime 값 또한 갱신되므로(갱신 직후에도 isNew()는 false였다. JSESSIONID가 동일하기 때문인 듯), 이 값을 통해 로그인 시각을 알 수는 없었다.
CreationTime에 대해 자세히 써 있는 Java 또는 Spring의 공식 문서를 찾을 수는 없었는데, 이 블로그에 따르면 Spring 위의 세션은 Tomcat 등의 서블릿 컨테이너에 의해 추상화되기 때문인 것 같다.
로그인 컨트롤러에서 setAttribute를 통해 로그인 시각을 저장한다. 그리고 메인 페이지 컨트롤러에서 세션의 시간을 받아서 현재 시간과의 차이가 1초 이내일 경우 Model에 플래그 true를 담아 보낸다.
JSP의 JS에서 이 값을 체크하여 플래그가 true일 경우 로직을 실행한다. (새로고침 시에 팝업을 막고 싶다면 FlashAttribute를 활용하면 된다.) 플래그가 아니라 로그인 시각을 받아서 JS에서 처리해도 되겠지만 응답 속도 때문에 초를 설정하기가 애매해서 그냥 컨트롤러에서 처리했다.
SessionAttribute로도 동작은 하지만 Cookie를 사용하는 것이 다음의 이유로 더 좋을 것 같다.
단, 쿠키 사용을 차단한 사용자에게는 팝업을 띄울 수 없다.