FireDrago

[bobzip] 로그인 확인 공통관심사 (인터셉터) 본문

카테고리 없음

[bobzip] 로그인 확인 공통관심사 (인터셉터)

화이용 2024. 6. 12. 11:33

문제상황

1. 로그인이 필요한 기능의 경우, 컨트롤러 호출될때마다 로그인 확인 로직이 필요하다.

@GetMapping("/delete")
public String add(HttpSession session) {
    Member member = (Member) session.getAttribute(LoginConst.LOGIN);
    if (member == null) {
        return "redirect:/members/login";
    }
    memberService.delete(member);
    session.removeAttribute(LoginConst.LOGIN);
    return "redirect:/";
}
@GetMapping("/add")
public String recipeForm(@ModelAttribute("recipeForm") RecipeAddForm recipeAddForm,
                         @SessionAttribute(name = LoginConst.LOGIN, required = false) Member loginMember){
    // 로그인 검증
    if (loginMember == null) {return "redirect:/members/login";}
    return "/recipe/recipeForm";
}

@PostMapping("/add")
public String addRecipe(@Validated @ModelAttribute RecipeAddForm recipeAddForm,
                        BindingResult bindingResult,
                        @SessionAttribute(name = LoginConst.LOGIN, required = false) Member loginMember) 
                        throws IOException {
    // 로그인 검증
    if (loginMember == null) {return "redirect:/members/login";}
    // 입력 정보 오류 있다면 입력폼으로
    if (bindingResult.hasErrors()) {
        return "/recipe/recipeForm";
    }
    // 재료 등록 후 반환
    List<Ingredient> ingredients = ingredientService.addIngredients(recipeAddForm.getIngredientNames());
    // 반환받은 재료와 로그인 멤버, 입력정보를 바탕으로 레시피 등록
    recipeService.addRecipe(recipeAddForm, loginMember, ingredients);
    return "redirect:/";
}

 

회원탈퇴 기능과 , 레시피 등록 폼 요청, 레시피 등록 요청 모두 로그인 검증 로직이 포함되어 있다.

이러한 기능을 '공통 관심사' 라고 한다. 로그추적, 로그인 검증 처럼 여러 기능에서 공통적으로 필요한 기능을 말한다.

 

문제해결

스프링 MVC의 인터셉터를 통해 로그인 검증 공통관심사를 처리하자!

 

서블릿은 공통관심사를 필터를 통해 처리하고,

스프링은 인터셉터를 통해 처리한다는 것을 공부한 것이 떠올라 적용해보았다.

https://flowerdragon95.tistory.com/137

 

[Spring] 서블릿 필터와 스프링 인터셉터

여러 메서드에서 로그인한 사용자만 접근할 수 있도록 하려면 어떻게 할까? 이렇게 여러 메서드에서 공통적으로 사용되는 기능을 '공통의 관심사'라고 한다. 공통의 관심사를 처리하기 위한 방

flowerdragon95.tistory.com

 

 

먼저 패키지 구조부터 살펴보자 도메인 패키지 구조에서 

인터셉터는 여러 비지니스 로직에서 사용되므로

global.interceptor 패키지에 배치했다.

WebConfig 설정파일은 인터셉터, 포맷터, 컨버터등

여러 클래스들을 등록하는 역할을 하므로  프로젝트의 최상단에 배치했다.

 

 

 

 

 

 

 

 

인터셉터는 HandlerInterceptor 를 구현하여 만들어지는데, 세가지 메서드 중에서 

로그인 확인은 핸들러가 호출되기 전에 실행되어야 하므로, preHandler 메서드를 구현한다.

 

@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, 
    			HttpServletResponse response, Object handler) 
                            throws Exception {
        String requestURI = request.getRequestURI();
        log.info("인증 체크 인터셉터 실행 {}", requestURI);
        HttpSession session = request.getSession(false);

        if (session == null || session.getAttribute(LoginConst.LOGIN) == null) {
            response.sendRedirect("/members/login?redirectURL=" + requestURI);
            return false;
        }
        return true;
    }
}
 
 

request.getSession(false) : request를 통해 세션을 가져온다. 세션이 없는경우 null값을 받기위해
                                               create = false 옵션을 사용했다. true의 경우 새로운 세션을 생성한다.

 

세션이 없거나, 로그인 정보가 세션에 없다면, 로그인 폼 요청으로 리다이렉트한다.

requestURI를 쿼리스트링으로 전달하여, 로그인완료된 경우, 요청한 URI가 자동으로 호출된다.

 

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginCheckInterceptor())
                .order(1)
                .addPathPatterns("/recipe/add", "/members/delete");
    }
}
 
 

인터셉터를 등록하기 위한 WebConfig 클래스이다. @Configuration 에너테이션을 붙여준다.

WebMvcConfigurer 는 SpringMvc 에 필요한 설정값을 콜백메서드를 사용하여 등록한다.

 

registry.addInterceptor(new LoginCheckInterceptor())              인터셉터를 등록한다.
                .order(1)                                                                      실행순서를 지정한다. 첫번째로 등록했다.
                .addPathPatterns("/recipe/add", "/members/delete")  인터셉터 적용될 URI를 설정한다.

                .excludePathPatterns();                                               인터셉터 적용하지 않을 URI를 설정한다.

콜백 메서드 : 특정 이벤트가 실행될때 호출되는 메서드

 

인터셉터가 등록되고 난 이후 레시피 등록, 회원탈퇴 등의 로직에서는 로그인 확인 로직이 제거된다.

인터셉터에서 로그인 확인을 미리 다 처리해주기 때문이다.

이제 새로운 기능을 구현하더라도, addPathPattern에 호출 URI만 등록해주면 된다. 

 

로그인 확인이라는 공통관심사를 인터셉터와 설정파일에서 관리하게 되므로

단일책임 원칙(SRP)이 더 잘 구현되는 코드가 완성됐다.