비록 시작은 코딩일기지만, 그 끝은 창대하게
어엿한 개발자 블로그로 성장할 수 있도록.
본 게시글은
Endless Creation Spring Study
에 사용하는 자료입니다.
우리가 어떤 서비스에서 로그인 기능을 사용할때 가장 기본적이면서 중요한 것이 바로 로그인 상태를 유지 하는 것이다. 로그인 상태를 유지하는 방법에는 크게 HttpSession
을 이용하는 방법과 쿠키를 이용하는 방법이 있다. 외부 DataBase에 세션 데이터를 보관하는 방식도 존재한다.
@Controller
에서 HttpSession
을 사용하려면 다음 두 가지 방법 중 하나를 이용하면 된다.
1. Request Mapping Annotation Method에 HttpSession Param을 추가한다.
2. Request Mapping Annotation Method에 HttpServletRequest Param을 추가하고 HttpServletRequest를 이용해서 HttpSession을 구한다.
첫 번째 방법을 사용한 코드의 예는 다음과 같다.
@PostMapping
public String form(LoginCommand loginCommand. Errors errors, HttpSession session)
{
..// session을 활용한 코드
}
Request Mapping Annotaion Method에 HttpSession
이 존재할 경우 Spring MVC는 Controller의 메소드를 호출할 때 HttpSession
객체를 파라미터로 전달한다. HttpSession
을 생성하기 전이면 새로운 HttpSession
을 생성하고 그렇지 않으면 기존에 존재하는 HttpSession
을 전달한다.
두 번째 방법은 다음과 같이 HttpServletRequest
의 getSession()
메소드를 이용하는 것이다.
@PostMapping
public String form(LoginCommand loginCommand. Errors errors, HttpServletRequest req)
{
HttpSession session = req.getSession()
..// session을 활용한 코드
}
첫 번째 방법은 항상 HttpSession
을 항상 생성하지만 두 번째 방법은 필요한 시점에만 HttpSession
을 생성할 수 있다는 차이가 있다.
만약 http://localhost:8080/member/edit 라는 비밀번호 변경 주소가 있다고 하자. 우리가 로그인을 하지 않은 상태에서 해당 주소를 요청한다고 가정하면 비밀번호 변경 폼이 출력되면 안된다. 로그인을 하지 않은 상태에서 해당 주소를 요청했을때 로그인 화면으로 이동시키는 것이 더 바람직하다.
이를 위해서 HttpSession
을 활용하여 세션 객체가 존재하는지 검사하고 존재하지 않으면 로그인 경로로 redirect 하도록 코드를 작성할 수 있다.
그렇지만 실제 웹 애플리케이션에서는 비밀번호 변경 기능 외에 더 많은 기능에서 로그인 여부를 확인해야 한다. 각 기능을 구현한 Controller 마다 코드를 확인하고 Session 확인 코드를 삽입하는 것은 바람직하지 못하다.
이렇게 다수의 Controller 에 대해 동일한 기능을 적용해야 할 때 사용할 수 있는 것이 HandlerInterceptor
이다.
HandlerInterceptor
인터페이스를 사용하면 다음의 세 시점에 공통 기능을 넣을 수 있다.
1. Controller 실행 전 :: preHandle()
preHandle()
메소드는 Controller 객체를 실행하기 전에 필요한 기능을 구현할 때 사용한다. handler 파라미터는 웹 요청을 처리할 Controller 객체이다. 이 메소드를 사용하면 다음 작업이 가능하다.
preHandle()
메소드의 return type 은 boolean 이다. preHandle()
메소드가 false 를 return 하면 Controller or 다음 HandlerInterceptor를 실행하지 않는다.
2. Controller 실행 후, 아직 view를 실행하기 전 :: postHandle()
postHandle()
메소드는 Controller가 정상적으로 실행된 이후에 추가 기능을 구현할 때 사용한다. Controller가 Exception을 발생하면 postHandle()
메소드는 실행하지 않는다.
3. view를 실행한 이후 :: afterCompletion()
afterCompletion()
메소드는 view가 Client에 응답을 전송한 뒤에 실행된다. Controller 실행 과정에서 Exception이 발생하면 이 메소드의 네 번째 파라미터로 전달된다. Exception 이 발생하지 않으면 네 번째 파라미터는 null
이 된다. 따라서 Controller 실행 후에 예기치 않은 Exception을 log로 남긴다거나 실행 시간을 기록하는 등의 후처리를 하기에 적합한 메소드이다.
HandlerInterceptor
Interface의 각 메소드는 아무 기능도 구현하지 않은 Java8의 default 메소드이다. 따라서 HandlerInterceptor
Interface의 메소드를 모두 구현할 필요가 없다. 이 Interface를 상속받고 필요한 메소드만 Overriding 하면 된다.
Http 프로토콜은 기본적으로 connectionless 이며 stateless 이다.
그래서 이를 보완하기 위해 쿠키와 세션을 사용하는 것이다. 사용자의 정보를 서버 측에서 관리하는 세션과 달리 쿠키는 Client Local에서 사용자의 정보를 관리한다.
이런 쿠키 기능을 Spring MVC Controller에서 사용할 수 있다.
사용하는 방법 중 하나는 @CookieValue
Annotation 을 사용하는 것이다. @CookieValue
Annotaion은 Request Mapping Annotaion Method의 Cookie type 파라미터에 적용한다. 이를 통해 쉽게 쿠키를 Cookie 파라미터로 전달받을 수 있다. 적용된 예시 코드는 다음과 같다.
@Controller
@RequestMapping("/login")
public class LoginController
{
...
@GetMapping
public String form(LoginCommand loginCommand,
@CookieValue(value = "REMEMBER", required = false) Cookie rCookie)
{
if(rCookie != null)
{
loginCommand.setEmail(rCookie.getValue());
loginCommand.setRememberEmail(true);
}
return "login/loginForm";
}
}
@CookieValue Annotation의 Value 속성은 쿠키의 이름을 지정한다. 예시 코드에서는 REMEMBER
라는 이름의 쿠키를 Cookie type 으로 전달받는다. 지정한 이름을 가진 쿠키가 존재하지 않을 수도 있다면 required 속성값을 false로 지정한다. 기본값은 true 이다.