일반적으로 나쁜 코드라하면 같은 코드가 반복되는 부분입니다. 같은 코드를 계속해서 복사&붙여넣기로 반복하게 만든다면 이후에 수정이 필요할 때 모든 부분을 찾아가며 수정해야한다. 이렇게 작성될 경우 유지보수성이 떨어질 수 밖에 없으며, 혹시나 수정이 반영되지 않은 반복 코드가 있다면 문제가 발생하기때문에 이를 개선해보자!
우리가 로그인한 유저를 불러올때에 사용했던 방법은
SessionUser user = (SessionUser) httpSession.getAttribute("user");
로그인 하면서 httpSession에 user라는 키로 저장해두어서 getAttribute 메서드를 사용해서 가져와 주었는데 이 방법으로 계쏙하게되면 같은 코드가 반복되게 됩니다.
이를 어노테이션으로 변경해주어서 간단하게 가져올 수 있게 어노테이션을 만들어보자!!
package com.qkrtprjs.springbootproject.config.auth;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.PARAMETER) //파라미터로선언된 객체에만 사용하겠다
@Retention(RetentionPolicy.RUNTIME) //
public @interface LoginUser {
}
LoginUser라는 어노테이션을 만들어주고 이 관련 속성값들을 같은 위치에
LoginUserArgumentResolver 라고 만들어준다.
package com.qkrtprjs.springbootproject.config.auth;
import com.qkrtprjs.springbootproject.config.auth.dto.SessionUser;
import lombok.RequiredArgsConstructor;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import javax.servlet.http.HttpSession;
@RequiredArgsConstructor
@Component
public class LoginUserArgumentResolver implements HandlerMethodArgumentResolver {
//조건에 맞는 경우 메소드가 있다면 HandlerMethodArgumentResolver의 구현체가 지정한 값으로 해당 메소드의 파라미터로 넘길수있다.
private final HttpSession httpSession;
@Override
public boolean supportsParameter(MethodParameter parameter) { //특정파라미터를 지원하는지 판단
boolean isLoginUserAnnotation = parameter.getParameterAnnotation(LoginUser.class) != null;
boolean isUserClass = SessionUser.class.equals(parameter.getParameterType());
return isLoginUserAnnotation & isUserClass;
}
@Override
//파라미터에 전달할 객체를 생성
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
return httpSession.getAttribute("user");
}
}
이렇게 설정해주면 @LoginUser라는 어노테이션이 붙어 있고 파라미터 클래스 타입이 SessionUser.class 인 경우만 참을 반환한다.
또한 이 적용 내용을 WebMvcConfigurer에 추가해준다.
package com.qkrtprjs.springbootproject.config;
import com.qkrtprjs.springbootproject.config.auth.LoginUserArgumentResolver;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
@RequiredArgsConstructor
@Configuration
public class WebConfig implements WebMvcConfigurer {
private final LoginUserArgumentResolver loginUserArgumentResolver;
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(loginUserArgumentResolver);
}
}
HandlerMethodArgumentResolver는 항상 WebMvcConfigurer의 addArgumentResolvers()를 통해서 추가해줘야한다!!
이렇게 하면 정상적으로 @LoginUser 어노테이션을 사용해서 로그인한 SessionUser를 불러올 수 있다.
@GetMapping("/")
public String index(Model model, @LoginUser SessionUser user) {
model.addAttribute("posts", postsService.findAllDesc());
if (user != null) {
model.addAttribute("userName", user.getName());
}
return "index";
}
우리는 지금 애플리케이션을 재실행하면 로그인이 풀린다.
세션이 내장 톰캣의 메모리에 저장되기때문입니다. 기본적으로 세션을 WAS의 메모리에서 저장되고 호출된다! 그래서 재실행시에 초기화되는것이다. 그렇다면 배포를 진행할때마다 로그인이 풀려버릴것이다. 따라서 이를 해결하고 2대 이상의 서버에서 서비스를 하고 있다면 톰캣마다 세션 동기화 설정을 해주어야한다.
현재 사용하는 방법으로는 3가지가 있다.
우리는 두 번째 방식인 DB를 세션 저장소로 사용해보자, 이유는 설정이 간단하고 비용 절감을 위함
설정 방식은 간단하다 build.gradle과 application.yml을 수정해준다.
의존성
implementation 'org.springframework.session:spring-session-jdbc'
추가
application.yml
spring:
session:
store-type: jdbc
추가
후 로그인하고 h2로 접속해보면
세션을 위한 테이블이 두개가 생성된것을 확인할 수 있다.
우리는 현재 h2 DB를 사용하고있고 이 DB는 재실행할때마다 초기화가되기때문에 지금은 세션을 저장되는 것을 확인할 수 없지만 나중에 다른 DB를 사용하게된다면 세션이 풀리지 않는다.