ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 학원 day89. 어노테이션 만들기, 사용자정보가져오기, 인터셉터
    기록 2023. 1. 9. 17:14

     

    어노테이션 

    - 편집기, 컴파일러, 프레임워크, 애플리케이션에게 부가적인 정보를 전달하기 위해 사용된다.

    - 구성요소 (Retention은 하나만 지정가능하고, Target은 복수개 지정 가능하다.)

        @Retention

                - 어노테이션이 사용되는 시점을 정의한다.

                        * Source

                               소스코드작성시 사용된다.(문법검사)

                        * Class

                               컴파일시에 사용된다.

                        * Runtime

                               프로그램 실행시에 사용된다.

                               거의 대부분의 어노테이션은 프로그램 실행시에 사용된다. 

                               개발자가 정의하는 어노테이션도 프로그램 실행시에 사용된다.

         @Target

                - 어노테이션이 적용되는 대상을 정의한다.

                        * Type : 클래스, 인터페이스

                                     예시      @Taget(ElementType.TYPE)
                                                   public @interface Controller { }

                                                  @Controller   // Tatget이 Type으로 설정된 것임
                                                   public class HomeController { 
                                                   }

     

                        * Field : 멤버변수

                        * Method : 메소드

                        * Parameter : 메소드의 매개변수

                        * Constructor : 생성자

                        * Annotation Type : 다른 어노테이션

     

    속성

          어노테이션의 속성

          프레임워크나 애플리케이션에 전달할 정보를 표현한다.

          예시

               public @interface RequestParam {

                       String       name()               default "";

                       boolean   required()           default true;

                       String       defaultValue()    default ValueConstants.DEFAULT_NONE

               }

               public String list(@RequestParam(name="page", required = false, defaultvalue = "1") int page) {}

                 -> 해석 :  요청파라미터로 페이지라는 걸 읽어와야 하는데, page는 없어도 되는데 값이 없으면 기본값을 1이라고 한다.

     

    어노테이션의 속성을 정의할 수 있다. 

    @Autowired(required = false)              // 기본값은 true, 의존성주입이 필수인지 아닌지 설정할 때 씀.

    @GetMapping (path = {"/", "/home"})   // /이거나 /home이라는 url이 요청이 오면 메소드를 실행하게 함. 하나만 적을 때는 배열표시 필요 없음.


    LoginUser 어노테이션 만들기

    LoginUser.java

    @LoginUser라는 어노테이션이 붙어있으면 세션에서 사용자정보를 찾아서 loginUserInfo라는 변수에 넣어주는 것이다.

    여러군데(새글작성, 게시글 수정, 내정보수정, 내정보조회, 비밀번호 변경, 주문하기, 탈퇴하기 등)에서 로그인된 사용자정보를 가져오는 코드가 필요할 것이다. 필요할 때마다 위의 코드를 적는 수고를 덜기 위해 @LoginUser어노테이션을 만든 것이다.

    따라서, 위의 코드를 아래처럼 적어줄 수 있다.

    @LoginUser라는 어노테이션이 붙어있으면 로그인된 사용자정보를 달라는 거고, 그걸 loginUserInfo라는 변수 안에 넣어준다.

    @LoginUser어노테이션이 안붙어있는 경우, UserRegisterForm과 같이 요청파라미터값을 전달받는 객체라고 생각한다. 그래서 요청객체에서 요청파라미터 이름이 id인 요청파라미터값을 조회하고, 이름이 name인 요청파라미터값을 조회하는데 요청파라미터값이 없기 때문에 null이 저장된다. 

    어노테이션이 활용되지 않은 경우, 요청파라미터 값이 조회되어 전달되고, 

    어노테이션이 활용된 경우, 세션에서 로그인된 사용자정보를 조회하여 전달한다.

    * HandleMethodArgumentResolver를 구현한 구현객체를 정의해서 @LoginUser 어노테이션이 지정된 매개변수에 대해서 위와 같이 수행한다.

     

    - HandlerMethodArgumentResolver 인터페이스

      * 요청핸들러 메소드의 매개변수를 분석해서 적절한 처리를 수행하는 구현클래스를 작성할 때 이 인터페이스를 구현한다.

     

    public class LoginUserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {

     

    // 이 메소드가 true를 반환하면 해당 매개변수가 이 구현클래스를 통해서 처리할 매개변수임을 나타낸다.

    // 이 메소드가 true를 반환하면 아래의 resolverArgument() 메소드가 실행된다.

    // 이 메소드가 true를 반환하면 요청핸들러 메소드의 매개변수가 @LoginUser 어노테이션이 지정된 매개변수다.

    // 요청핸들러 메소드의 매개변수에 @LoginUser어노테이션이 지정되어 있으면 true를 반환하도록 이 메소드를 재정의한다.

    @Override
    public boolean supportsParameter(MethodParameter parameter) {

         // MethodParameter는 요청핸들러 메소드의 매개변수정보를 표현하는 객체다.

         // MethodParameter를 통해서 어노테이션 정보를 알아낼 수 있다.

         return false;
    }

     

    return false가 반환되면 아래 코드가 실행되지 않고, return true가 반환되면 아래 코드가 실행된다.

     

    // 이 메소드가 반환하는 값이 매개변수에 전달된다.

    // 세션에서 "loginUser"라는 이름으로 저장된 로그인된 사용자정보를 조회해서 이 메소드를 재정의한다.
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
    NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {


    return null;
    }

    web-context.xml
    1
    2

    탈퇴하기

    redirect:delete-success 라고 적는 이유

    insert, update, delete처럼 데이터베이스의 변화를 일으키는 작업은 항상 요청이 clear되어야한다. 

    만일, 내부이동해버리면 url이 그대로 /delete로 되어 있어서 새로고침하면 다시 delete 작업이 일어난다. 

    내부이동하면 url이 그대로 남아있다는 것에 유념하자!

    UserService.java


    인터셉터

     

    - HandlerInterceptor 인터페이스

          * 요청핸들러 메소드 실행전처리/실행후처리 작업을 수행하는 구현클래스를 작성할 때 이 인터페이스를 구현한다.

     

    // LoginCheckHandlerInterceptor은 로그인 정보가 필요한 요청핸들러 메소드인 경우 로그인 정보가 존재하지 않으면 요청핸들러 메소드를 실행시키지 않도록 구현한다. (@LoginUser의 유무를 통해 로그인된 사용자정보가 필요한지 확인한다.)

    => 로그인해야지 실행되는 메소드인데 로그인하지 않았을 때는 실행되지 않도록 하기 위해 만듦

    public class LoginCheckHandlerInterceptor implements HandlerInterceptor {

     

    // 요청핸들러 메소드 실행전에 수행할 작업을 구현하는 메소드다.

    // 대부분의 사용자정의 인터셉터는 preHandle 메소드를 구현한다.

    // preHandler 메소드의 반환값이 true면 요청핸들러 메소드를 실행하고, 반환값이 false면 요청핸들러 메소드를 실행하지 않는다. (요청핸들러메소드의 실행여부를 결정할수 있다.)

       @Override
       public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
             throws Exception {
          
          return true
       }
       

    // 요청핸들러 메소드 실행후에 수행할 작업을 구현하는 메소드다.

    // 활용빈도가 낮음
       @Override
       public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
             ModelAndView modelAndView) throws Exception {
       }
       

    // 모든 요청처리를 마치고, 응답보내기 직전에 수행할 작업을 구현하는 메소드다.
    // 활용빈도가 낮음  

     @Override
       public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
             throws Exception {
          
       }
    }

    web-context.xml

    web-context.xml에 위의 코드를 추가한다. 

    <mvc:mapping path="/**/*"/>

    -> 모든 요청에 대해서 LoginCheckHandlerInterceptor가 실행되도록 한다.

    <mvc:exclude-mapping path=""/> 

    -> LoginCheckHandlerInterceptor가 실행되지 않도록 제외시키는 것을 정의할 수 있다.

    LoginCheckHandlerInterceptor.java
    
    /**
     * 요청핸들러 메소드와 매개변수에 @LoginUser 어노테이션이 있는지 조사한다. <br/>
     * 해당 어노테이션이 있으면 로그인이 필요한 작업을 요청한 것으로 판단하고, 
     * 세션객체에 로그인된 사용자정보(LoginUserInfo 객체)가 저장되어 있는지 확인한다. <br/>
     * 로그인된 사용자정보가 존재하면 true를 반환하고, 로그인된 사용자정보가 존재하지 않으면 ApplicationException 예외를 던진다.
     * @author lee_e
     *
     */
    public class LoginCheckHandlerInterceptor implements HandlerInterceptor {
    
    	@Override
    	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
    			throws Exception {
    		
    		// 요청핸들러 메소드 혹은 요청핸들러 메소드의 매개변수가 @LoginUser를 가지고 있는지 확인한다.
    		boolean isLoginRequired = hasMethodAnnotation(handler) || hasParameterAnnotation(handler);
    		
    		// isLoginRequired가 false면 로그인여부와 상관없이 true를 반환한다 - 요청핸들러 메소드가 실행됨 
    		if (!isLoginRequired) {
    			return true;
    		}
    		
    		// isLoginRequired가 true면 로그인여부를 검사한다.
    		
    		// 세션에 로그인된 사용자정보가 존재하지 않으면 예외를 발생시킨다.
    		if (SessionUtils.getAttribute("loginUser") == null) {
    			throw new ApplicationException("로그인이 필요한 서비스 입니다.");
    		}
    		// 세션에 로그인된 사용자정보가 존재하면 true를 반환한다 - 요청핸들러 메소드가 실행됨
    		return true;
    	}
    	
    	// 요청핸들러 메소드가 @LoginUser을 가지고 있으면 true를 반환한다.
    	private boolean hasMethodAnnotation(Object handler) {
    		
    		HandlerMethod handlerMethod = (HandlerMethod) handler;
    		return handlerMethod.hasMethodAnnotation(LoginUser.class);
    	}
    	
    	// 요청핸들러 메소드의 매개변수가 @LoginUser을 가지고 있으면 true를 반환한다.
    	private boolean hasParameterAnnotation(Object handler) {
    		
    		HandlerMethod handlerMethod = (HandlerMethod) handler;
    		MethodParameter[] methodParameters = handlerMethod.getMethodParameters();
    		for (MethodParameter methodParameter : methodParameters) {
    			return methodParameter.hasParameterAnnotation(LoginUser.class);
    		}
    		return false;
    	}
    	
    }

    Spring MVC로 웹 애플리케이션 구현하기

    - 요청핸들러 메소드 구현하기 (사용자가 사용하는 모든 메뉴들 하나마다 있어야 함)
       1. 컨트롤러 클래스를 정의한다.
          @Controller
          public class UserController {
          }
       2. 요청핸들러 메소드를 정의한다. (반환타입이 String, 내부이동할 jsp이름 혹은 재요청할 url)
          @Controller
          public class UserController {
             public String info() {                                        // 사용자 상세정보화면을 제공한다.
             }
             public String passwordChangeForm() {         // 비밀번호 변경화면을 제공한다.
             }
          }
       3. 요청 URL과 요청핸들러 메소드를 매핑한다. (링크클릭은 GetMapping, 입력폼이 필요한건 PostMapping)
          @Controller
          @RequestMapping("/user    // 클래스레벨의 URL 매핑, @RequestMapping 사용, 메소드 레벨의 URL매핑과 더해진다.
          public class UserController {
             @GetMapping("/info")           // 메소드 레벨의 URL 매핑, 실제 매핑은 "/user/info"다.
             public String info() {  
             }
             @GetMapping("/password")                 // 실제 매핑은 "/user/password"다.
             public String passwordChangeForm() {
             }
          }
       4. 요청핸들러 메소드의 반환값을 결정한다. (재요청URL을 응답으로 보내는 Redirect를 적는 것과 Redirect로 시작하지 않는 jsp로 내부이동하는 것으로 나뉜다. 저장, 수정, 삭제 작업 후에는 Redirect)
          @Controller
          @GetMapping("/user")
          public class UserController {
             @GetMapping("/info")
             public String info() {
                return "user/info";         "/WEB-INF/views/user/info.jsp"
             }
             @GetMapping("/password")
             public String passwordChangeForm() {
                return "user/password-form";   "/WEB-INF/views/user/password-form.jsp"
             }

              @PostMapping("/password")

               public String changePassword() {

                   return "redirect:password-success";

                   return "redirect:/user/password-success";

                   return "redirect:/password-success";     // 404 not found

                    // 재요청 URL은 상대경로, 절대경로 작성 규칙에 맞게 작성해야 한다.

                    // 재요청 URL은 언제나 다른 요청 핸들러 메소드를 요청하는 URL이어야 한다.
          }

     

    앞에 /가 있으면 절대경로.

    절대경로는 무조건 localhost 다음부터 적는 것임.


       5. 요청핸들러 메소드 실행에 필요한 값을 전달받는 매개변수를 추가한다.
          @Controller
          @GetMapping("/user")
          public class UserController {
             // 로그인된 사용자의 상세정보화면을 제공하는 요청핸들러 메소드다.
             // 로그인된 사용자 정보를 전달받아야 한다, JSP에 정보를 전달할 Model을 전달받아야 한다.
             @GetMapping("/info")
             public String info(@LoginUser LoginUserInfo loginUserInfo, Model model) {
                return "user/info";                                              "/WEB-INF/views/user/info.jsp"
             }

             // 비밀번호를 변경하는 화면을 제공하는 요청핸들러 메소드다.
             // 전달받을 정보가 없다.
             @GetMapping("/password")

             @LoginUser    // 로그인된 사용자만 접근가능하도록 함
             public String passwordChangeForm() {
                return "user/password-form";                       "/WEB-INF/views/user/password-form.jsp"
             }

             // 이전 비밀번호와 새 비밀번호를 전달받아서 로그인한 사용자의 비밀번호을 변경하는 요청핸들러 메소드다.
             // 로그인된 사용자의 비밀번호를 변경할 것이기 때문에 로그인된 사용자정보를 전달받아야한다.
             // 비밀번호 변경폼에서 입력한 이전 비밀번호와 새 비밀번호를 전달받아야 한다.
             @PostMapping("/password")
             public String changePassword(@LoginUser LoginUserInfo loginUserInfo, String oldPassword, String password) {
    // 입력폼의 name과 같은 이름으로 매개변수명을 적는다. 사용자정보는 반드시 세션에서 가져와야함!!
                return "redirect:password-success";             "localhost/user/password-success"를 재요청한다.
             }
          }
       6. 요청핸들러 메소드에 구현내용을 추가한다.
          @Controller
          @GetMapping("/user")
          public class UserController {
             
             @Autowired
             private UserService userService;

             // 로그인된 사용자의 상세정보화면을 제공하는 요청핸들러 메소드다.
             // 로그인된 사용자 정보를 전달받아야 한다, JSP에 정보를 전달할 Model을 전달받아야 한다.
             @GetMapping("/info")
             public String info(@LoginUser LoginUserInfo loginUserInfo, Model model) {
                // 업무로직 메소드 호출, 사용자 상세정보 획득            
                UserDetailDto dto = userService.getUserDetail(loginUserInfo.getId());
                // Model에 사용자 상세정보 저장   (jsp에서 user.~으로 값을 불러올 수 있다.) 
                model.addAttribute("user", dto);
                return "user/info";                       "/WEB-INF/views/user/info.jsp"
             }

     

    - 예외처리하기 (개발자 x, 공통팀에서 구현)
       1. 에러페이지를 만든다.
          WEB-INF/views/error 폴더에 에러상황에 맞는 에러페이지를 생성한다.
             WEB-INF/views/error/500.jsp  (예외발생)
             WEB-INF/views/error/404.jsp   (해당페이지 없을 때, url 잘못 입력)
             WEB-INF/views/error/db.jsp   (db오류)
             WEB-INF/views/error/app.jsp   (강제 오류 발생)

       2. 일관된 예외처리를 담당하는 @ControllerAdvice가 추가된 클래스를 정의한다. (모든 Controller가 공통적으로 필요한 것에 ControllerAdice를 붙인다. 대표적으로 예외처리)
          @ControllerAdvice
          public class ExceptionHandlerControllerAdvice {

          } 
       
       3. 예외클래스별로 예외처리 메소드를 작성한다.
          @ControllerAdvice
          public class ExceptionHandlerControllerAdvice {
             @ExceptionHandler(RuntimeException.class)    // 예외클래스 작성
             public String handleRuntimeException(RuntimeException ex) {

                ex.printStackTrace();     // 개발단계에서는 추가하는게 좋다.
                return "error/500";          // 에러페이지로 이동
             }

             @ExceptionHandler(DataAccessException.class)
             public String handleDataAccessException(DataAccessException ex) {
                return "error/db";
             }

             @ExceptionHandler(ApplicationException.class)
             public String handleApplicationException(ApplicationException ex) {
                return "error/app";
             }
          } 

    - 로그인하기
       1. 로그인폼 페이지를 제공하는 요청핸들러 메소드를 작성한다.
       @GetMapping("/login")
       public String loginform() {
          return "login-form";
       }
       2. WEB-INF/views 폴더에 로그인폼 페이지를 작성한다.
          WEB-INF/views/login-form.jsp를 작성한다.
          <form method="post" action="login">
             아이디
             <input type="text" name="id" />
             비밀번호
             <input type="password" name="password" />
             <button type="submit">로그인</button>
          </form>
       3. 로그인 처리를 담당하는 요청핸들러 메소드를 작성한다.
          @PostMapping("/login")
          public String login() {
             
             
          }
       4. 요청핸들러 메소드의 반환값을 결정한다.
          @PostMapping("/login")
          public String login() {
             
             return "redirect:home";

             return "redirect:/home";    
          }
       5. 로그인폼 화면에 제출한 요청파라미터값을 전달받는 매개변수를 추가한다.
          @PostMapping("/login")
          public String login(String id, String password) {
             
             return "redirect:home";
          }

    방법2. 스프링에게 직접 알려주는 방법


       6. 로그인처리를 수행하는 업무로직을 호출한다.
          @PostMapping("/login")
          public String login(String id, String password) {
             User user = userService.login(id, password);
             
             return "redirect:home";
          }
       6. 로그인 인증이 완료된 사용자정보를 세션에 저장시킨다.
          @PostMapping("/login")
          public String login(String id, String password) {
             User user = userService.login(id, password);

     

            // LoginUserInfo는 아이디, 이름만 저장하는 객체다. 세션에는 LoginUserInfo 객체를 속성으로 저장시킨다. 
             LoginUserInfo loginUserInfo = new LoginUserInfo(user.getId(), user.getName());

            // 세션에는 LoginUserInfo객체를 속성으로 저장시킨다.
             SessionUtils.setAttribute("loginUser", loginUserInfo);
             

             return "redirect:home";
          }

     

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    JSP에서 로그인정보 활용

    // jsp에서 로그인된 사용자정보 출력하기

    ${loginUser.name}

    // jsp에서 로그인 여부 체크하기

    <c:if test="${not empty loginUser}">

           <a href=""> 새 글쓰기 </a>

    </c:if>

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    요청 핸들러 메소드에서 로그인 정보 활용

    // 로그인여부를 검사하게 하기

    @GetMapping("/delete")

    @LoginUser // 인터셉터가 @LoginUser 어노테이션을 감지하고, 세션에 로그인정보가 존재하는지 체크한다.

    public String deleteForm() {

    }

    // 매개변수로 로그인된 사용자정보 전달받기

    @PostMapping("/delete")

    public String deleteUser(@LoginUser LoginUserInfo loginUserInfo, @RequestParam("password") String password) {

          // 매개변수에 @LoginUser LoginUserInfo loginUserInfo로 추가하면

          // 인터셉터가 @LoginUser 어노테이션을 감지하고, 세션에 로그인 정보가 존재하는지 체크한다.

          // ArgumentResolver가 세션에 저장된 로그인된 사용자정보를 매개변수로 전달한다.

    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    - 로그아웃하기
      1. 로그아웃 처리를 담당하는 요청핸들러 메소드를 작성한다.
       public String logout() {

       }
      2. 요청 URL과 요청핸들러 메소드를 매핑시킨다.
       @RequestMapping("/logout")
       public string logout() {

       }
      3. 요청핸들러 메소드의 반환값을 결정한다.
       @RequestMapping("/logout")
       public string logout() {

          return "redirect:home";
       }
       4. 로그아웃 처리를 필요한 값을 전달받는 매개변수를 추가한다.
       @RequestMapping("/logout")
       public string logout() {   // 필요한 값이 없어서 추가하지 않음

          return "redirect:home";
       }
       5. 로그아웃처리를 수행하는 코드를 요청핸들러 메소드에 추가한다.
       @RequestMapping("/logout")
       public string logout() {
          // 세션에 저장된 로그인 정보를 삭제한다.
          SessionUtils.removeAttribute("loginUser");

          return "redirect:home";
       }

     



    - ArgumentResolver로 요청핸들러 메소드의 매개변수를 분석해서 적절한 값을 매개변수로 전달받기
       0. @LoginUser  어노테이션 정의하기
          @Target({METHOD, PARAMETER})
          @Retention(RUNTIME)
          public @interface LoginUser {
             boolean required() default true;

             String role() default "";
          }

           @GetMapping("/list")

            public String getAllUsers(@LoginUser(role = " ROLE_ADMIN") LoginUserInfo loginUserInfo) {

             return 

            }
       1. 요청핸들러 메소드의 매개변수를 분석하고, 적절한 값을 제공하는 ArgumentResolver 클래스 작성하기
       public class LoginUserHandlerMethodArgumentResolver implments HandlerMethodArgumentResolver {
          
       }
       2. HandlerMethodArgumentResolver 인터페이스의 추상메소드를 구현클래스에 추가하기
       public class LoginUserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
          // 이 메소드가 true를 반환하는 매개변수가 로그인정보를 전달받는 메소드다.
          public boolean supportsParameter(MethodParameter parameter) {
          
             return false;
          }
          // 이 메소드가 반환하는 값이 로그인 정보로 매개변수에 전달된다.
          public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
             NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
             return null;
          }
       }
       3. 추상메소드를 구현하기
       public class LoginUserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
          // 이 메소드가 true를 반환하는 매개변수가 로그인정보를 전달받는 메소드다.
          public boolean supportsParameter(MethodParameter parameter) {
             // 요청핸들러 메소드가 @LoginUser를 가지고 있으면  true를 반환한다.
             return parameter.hasParameterAnnotation(LoginUser.class);
          }
          // 이 메소드가 반환하는 값이 로그인 정보로 매개변수에 전달된다.
          public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
             NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
             // 세션에 저장된 사용자정보를 반환한다.
             return SessionUtils.getAttribute("loginUser");
          }
       }


       4. web-context.xml에 사용자정의 ArgumentResolver를 추가하기 (요청핸들러 매개변수마다 실행된다.)
       <mvc:annotation-driven>

          <mvc:argument-resolvers>
             <bean class="com.sample.web.resolver.LoginUserHandlerMethodArgumentResolver" />
          </mvc:argument-resolvers>
       </mvc:annotation-driven>
       5. 로그인된 사용자 정보를 요청핸들러 메소드에서 전달받기
          @GetMapping("/user/info")
          public String info(@LoginUser LoginUserInfo loginUserInfo, Model model) {

          }
          @PostMapping("/user/password")
          public String changePassword(@LoginUser LoginUserInfo loginUserInfo, String oldPassword, String password) {

          }


    - 로그인여부 체크하기 
       1. 로그인 상태에서만 요청할 수 있는 요청에는 요청핸들러 메소드 혹은 매개변수에 @LoginUser를 추가한다.
          @GetMapping("/user/delete")
          @LoginUser
          public String deleteform() {

          }

          @GetMapping("/user/info")
          public String info(@LoginUser LoginUserInfo loginUserInfo) {

          }


       2. 요청핸들러 메소드의 실행 전처리/후처리를 담당하는 HanderInterceptor를 구현한 사용자정의 인터셉터를  클래스를 작성한다.
          public class LoginCheckHandlerInterceptor implements HandlerInterceptor {

          }


       3. HandlerInterceptor 인터페이스의 추상 메소드를 추가한다.
          public class LoginCheckHandlerInterceptor implements HandlerInterceptor {

              // preHandle() 메소드가 true를 반환하면 요청핸들러 메소드가 실행된다.
             @Override
             public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception {
                     

                     // 요청핸들러 메소드나, 메소드의 매개변수에 @LoginUser 어노테이션이 설정되어 있는지 체크한다.

                     // @LoginUser 어노테이션이 없으면 true를 반환한다.

                     // @LoginUser 어노테이션이 있으면 로그인 여부를 체크해서 로그인상태면 true를 반환한다. -> 요청핸들러 메소드가 실행된다 / 로그인상태가 아니면 예외를 발생시킨다.-> 요청핸들러 메소드가 실행되지 않는다. 

          return true;
             }
          }


       4. web-context.xml에 사용자정의 인터셉터를 등록한다.
       <mvc:interceptors>

          <!-- 사용자정의 인터셉터 등록 -->
          <mvc:interceptor>

             <!-- 모든 요청 URL에 대해서는 사용자정의 인터셉터가 실행되도록 설정한다. -->
             <mvc:mapping path="/**/*"/>

             <!-- 아래의 요청 URL에 대해서는 사용자정의 인터셉터가 실행되지 않도록 설정한다. -->
             <mvc:exclude-mapping path="/home"/>
             <mvc:exclude-mapping path="/register"/>
             <mvc:exclude-mapping path="/success"/>
             <mvc:exclude-mapping path="/login"/>

            <!-- 사용자정의 인터셉터 -->
             <bean class="com.sample.web.interceptor.LoginCheckHandlerInterceptor" />
          </mvc:interceptor>
       </mvc:interceptors>

    package com.sample.web.interceptor;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.springframework.core.MethodParameter;
    import org.springframework.web.method.HandlerMethod;
    import org.springframework.web.servlet.HandlerInterceptor;
    
    import com.sample.exception.ApplicationException;
    import com.sample.utils.SessionUtils;
    import com.sample.web.login.LoginUser;
    
    /**
     * 요청핸들러 메소드와 매개변수에 @LoginUser 어노테이션이 있는지 조사한다. <br/>
     * 해당 어노테이션이 있으면 로그인이 필요한 작업을 요청한 것으로 판단하고, 
     * 세션객체에 로그인된 사용자정보(LoginUserInfo 객체)가 저장되어 있는지 확인한다. <br/>
     * 로그인된 사용자정보가 존재하면 true를 반환하고, 로그인된 사용자정보가 존재하지 않으면 ApplicationException 예외를 던진다.
     * @author lee_e
     *
     */
    public class LoginCheckHandlerInterceptor implements HandlerInterceptor {
    
    	@Override
    	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
    			throws Exception {
    		
    		// 요청핸들러 메소드 혹은 요청핸들러 메소드의 매개변수가 @LoginUser를 가지고 있는지 확인한다.
    		boolean isLoginRequired = hasMethodAnnotation(handler) || hasParameterAnnotation(handler);
    		
    		// isLoginRequired가 false면 로그인여부와 상관없이 true를 반환한다 - 요청핸들러 메소드가 실행됨 
    		if (!isLoginRequired) {
    			return true;
    		}
    		
    		// isLoginRequired가 true면 로그인여부를 검사한다.
    		
    		// 세션에 로그인된 사용자정보가 존재하지 않으면 예외를 발생시킨다.
    		if (SessionUtils.getAttribute("loginUser") == null) {
    			throw new ApplicationException("로그인이 필요한 서비스 입니다.");
    		}
    		// 세션에 로그인된 사용자정보가 존재하면 true를 반환한다 - 요청핸들러 메소드가 실행됨
    		return true;
    	}
    	
    	// 요청핸들러 메소드가 @LoginUser을 가지고 있으면 true를 반환한다.
    	private boolean hasMethodAnnotation(Object handler) {
    		
    		HandlerMethod handlerMethod = (HandlerMethod) handler;
    		return handlerMethod.hasMethodAnnotation(LoginUser.class);
    	}
    	
    	// 요청핸들러 메소드의 매개변수가 @LoginUser을 가지고 있으면 true를 반환한다.
    	private boolean hasParameterAnnotation(Object handler) {
    		
    		HandlerMethod handlerMethod = (HandlerMethod) handler;
    		MethodParameter[] methodParameters = handlerMethod.getMethodParameters();
    		for (MethodParameter methodParameter : methodParameters) {
    			return methodParameter.hasParameterAnnotation(LoginUser.class);
    		}
    		return false;
    	}
    	
    }

    댓글

Designed by Tistory.