안녕하세요. 오늘은 커스텀 어노테이션을 활용하여 컨트롤러에서 메소드에 선언된 파라미터에서 세션 유저를 받아올 수 있도록 하는 HandlerMethodArgumentResolver 예제를 공유하고자 합니다.
우선 이전에 포스팅했었던 커스텀 어노테이션을 만드는 상세한 내용은 아래의 포스팅을 참조해주시기 바랍니다!!
2020/01/13 - [프로그래밍 언어/[Java] Study 내용 ] - [Java] Custom Annotation 커스텀 어노테이션 만들기(reflection 리플렉션)
우선 기존 코드는 다음과 같습니다.
@RequiredArgsConstructor
@Controller
public class IndexController {
private final PostsService postsService;
private final HttpSession httpSession;
@GetMapping("/")
public String index(Model model, @LoginUser SessionUser user) {
model.addAttribute("posts", postsService.findAllDesc());
SessionUser user = (SessionUser) httpSession.getAttribute("user");
if(user != null) {
model.addAttribute("userName", user.getName());
}
return "index";
}
}
- 세션 유저를 가져와서 처리해야하는 경우가 발생한다면 다분히 Session 에서 getAttribute로 꺼내와야하는 코드가 뻔하게 들어가야하는 상황이었습니다.
커스텀 어노테이션 예제
일단 로그인 유저를 지칭하는 @LoginUser 어노테이션을 만들어 보겠습니다.
@LoginUser 어노테이션
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginUser {
}
우선 거의 대부분의 커스텀 어노테이션을 특별한 속성을 두지 않는 한 굉장히 단순한 모습을 띄게 됩니다.
@Target 어노테이션의 값을 ElementType.PARAMETER로 두었기 때문에 메소드의 파라미터로 선언된 타입에서만 쓸 수 있습니다.
SessionUser 클래스
@Getter
public class SessionUser implements Serializable {
private String name;
private String email;
private String picture;
public SessionUser(User user) {
this.name = user.getName();
this.email = user.getEmail();
this.picture = user.getPicture();
}
}
- 평범한 유저 클래스의 일종입니다. 세션에 저장하기 위해 직렬화 처리를 하였습니다.
LoginUserArgumentResolver 클래스
@RequiredArgsConstructor
@Component
public class LoginUserArgumentResolver implements HandlerMethodArgumentResolver {
private final HttpSession httpSession;
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
//@LoginUser 어노테이션이 들어있으면
boolean isLoginUserAnnotation = methodParameter.getParameterAnnotation(LoginUser.class) != null;
//SessionUser 클래스 타입의 파라미터에 @LoginUser 어노테이션이 사용되었는가?
boolean isUserClass = SessionUser.class.equals(methodParameter.getParameterType());
return isLoginUserAnnotation && isUserClass;
}
@Override
public Object resolveArgument(MethodParameter methodParameter,
ModelAndViewContainer modelAndViewContainer,
NativeWebRequest nativeWebRequest,
WebDataBinderFactory webDataBinderFactory) throws Exception {
return httpSession.getAttribute("user");
}
}
- LoginUserArgumentResolver 클래스는 HandlerMethodArgumentResolver 인터페이스를 구현하는 구현체입니다. 각각의 오버라이딩 메소드인 supportsParameter와 resolveArgument를 구현해줍니다.
supportsParameter : 우선 메소드의 파라미터들이 하나씩 methodParameter 에 매핑될 것입니다. 그때 @LoginUser 어노테이션이 발견이 된다면 1단계 통과입니다. 그후 @LoginUser 어노테이션이 붙어있는 메소드 파라미터가 SessionUser 클래스 타입이라면 리턴값으로 true를 던져주게 될 것입니다.
resolveArgument : 만일 supportsParameter의 리턴값이 false 로 떨어진다면 해당 메소드는 실행되지 않습니다. true 인경우에만 실행되므로 httpSession에서 "user" 로 저장된 객체를 꺼내오게 됩니다.
WebConfig 클래스
@RequiredArgsConstructor
@Configuration
public class WebConfig implements WebMvcConfigurer {
private final LoginUserArgumentResolver loginUserArgumentResolver;
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
// HandlerMethodArgumentResolver 구현체를 여기에서 등록해줘야한다.
argumentResolvers.add(loginUserArgumentResolver);
}
}
- HandlerMethodArgumentResolver 구현체를 등록해주는 설정 클래스가 필요합니다. WebMvc 설정을 담당하는 WebMvcConfigurer 인터페이스를 구현하고 그중 argumentResolver를 등록하는 addargumentResolvers() 메소드의 argumentResolver 리스트에 우리가 구현한 LoginUserArgumentResolver 객체를 추가해주는 설정을 해줍니다.
설정 완료 후 사용하는 모습
@RequiredArgsConstructor
@Controller
public class IndexController {
private final PostsService postsService;
@GetMapping("/")
public String index(Model model, @LoginUser SessionUser user) {
model.addAttribute("posts", postsService.findAllDesc());
if(user != null) {
model.addAttribute("userName", user.getName());
}
return "index";
}
}
컨트롤러가 다음과 같이 좀더 깔끔하게 보일 수 있다. parameter 갯수가 늘어나겠지만 개인적으로 코드는 키가 큰것보다 뚱뚱한게 좋다고 생각한다. 쓸데없는 불필요한 코드에 메소드의 길이가 길어지는 것보단 간단한 설정으로 프로젝트 개발의 효율성을 올려보자!
'웹 개발 > Spring Boot' 카테고리의 다른 글
[Spring Boot] 서버사이드 렌더링 환경에서 서버 재시작없이 화면 바뀌게 하는 방법!!(devtools 라이브러리) (2) | 2020.06.22 |
---|---|
[Spring Boot] logback.xml 파일을 이용한 로그 남기기 (0) | 2020.05.29 |
[Spring Boot] REST API 게시판 서버 만들기 #6(Board 페이징 처리) (2) | 2020.02.20 |
[Spring Boot] REST API 게시판 서버 만들기 #5(User쪽 적용, 내가 TDD 활용하는 방법.) (6) | 2020.02.18 |
[Spring Boot] REST API 게시판 서버 만들기 #4(ModelMapper를 이용한 객체 매핑) (2) | 2020.02.10 |