포스트

Day95 java프로그래밍 기초(Spring web MVC)

1. View Resolver

1) View Resolver의 역할

  • MVC의 기본역할은 다음과 같다.
    • Controller : 사용자 요청을 처리하고, 모델 데이터를 뷰로 전달하는 역할을 한다.
    • Model : 데이터와 비즈니스 로직을 처리한다. JSP(View)에 보낼 데이터를 만든다. image
    • View : 사용자에서 출력할 데이터를 보여주는 역할을 한다. image
  • View Resolver는 Controller에서 받은 데이터를 View가 실행될수 잇게 포워딩 해준다. image

2) 기본 View Resolver

  • 기본 ViewResolver는 리턴 값으로 받은 view name으로 포워딩 할 JSP를 찾는다.
  • view name을 리턴하지 않으면 request hanlder의 url(@RequestMapping읠 value 값)을 상대경로로 사용한다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    @RequestMapping("/c01_1")
    public class Controller01_1 {
      // http://localhost:9999/eomcs-spring-webmvc/app2/c01_1/h1
      @GetMapping("h1")
      public String handler1(Model model) {
        return "jsp/c01_1.jsp";
        // => /app2/c01_1/jsp/c01_1.jsp
      }
        
      // http://localhost:9999/eomcs-spring-webmvc/app2/c01_1/h2
      @GetMapping("h2")
      public void handler2(Model model) {
        // 뷰 이름을 리턴하지 않으면,
        // request handler의 URL을 상대 경로로 사용한다.
        // 즉 다음 리턴 문과 같다.
        // return "c01_1/h2"
        // = /app2/c01_1/c01_1/h2
      }
    }
    

3) InternalResourceViewResolver

  • JSP URL의 접두어와 접미사를 미리 설정해 둘 수 있어 URL을 지정해준다.
  • 미리 지정된 접두사, 접미사를 사용하여 뷰이름으로 콤포넌트의 URL을 완성한다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    //config에서 설정을 한다.
      @Bean
      public ViewResolver viewResolver() {
        InternalResourceViewResolver vr = new InternalResourceViewResolver("/WEB-INF/jsp2/", // prefix
            ".jsp" // suffix
        );
        return vr;
        // => prefix + 페이지컨트롤러 리턴 값 + suffix
        // 예) "/WEB-INF/jsp2/" + "board/list" + ".jsp" = /WEB-INF/jsp2/board/list.jsp
      }
    
  • ViewResolver 실행 과정
    1. 페이지 컨트롤러는 클라이언트가 요청한 작업을 실행한 후 그 결과를 출력할 뷰의 이름을 리턴한다.
    2. 프론트 컨트롤러는 request handler가 리턴한 URL을 view resolver에게 전달한다.
    3. view resolver는 자신의 정책에 맞춰서 뷰 URL을 준비한다.
    4. InternalResourceViewResolver의 경우, request handler가 리턴한 URL 앞, 뒤에 접두사와 접미사를 붙여 JSP를 찾는다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    
    @Controller
    @RequestMapping("/c01_2")
    public class Controller01_2 {
      
      // http://localhost:9999/eomcs-spring-webmvc/app2/c01_2/h1
      @GetMapping("h1")
      public String handler1(Map<String, Object> map) {
        return "c01_2/h1"; // => /WEB-INF/jsp2/c01_2/h1.jsp
      }
      
      // http://localhost:9999/eomcs-spring-webmvc/app2/c01_1/h2
      @GetMapping("h2")
      public void handler2(Model model) {
        // request handler가 뷰 이름을 리턴하지 않으면
        // request handler의 URL을 상대 경로로써 뷰 이름으로 사용한다.
        // => "/WEB-INF/jsp2/c01_2/h2.jsp"
      }
        
      // http://localhost:9999/eomcs-spring-webmvc/app2/c01_2/h3
      @GetMapping("h3")
      public Map<String, Object> handler3() {
      
        HashMap<String, Object> map = new HashMap<>();
        map.put("name", "홍길동3");
        map.put("age", 40);
      
        // Map 객체에 값을 담아 리턴하면
        // 프론트 컨트롤러는 Map 객체에 보관되어 있는 값들을 ServletRequest 보관소로 옮긴다.
        // 그리고 view URL은 request handler의 URL을 사용한다.
        // => "/WEB-INF/jsp2" + "/c01_2/h3" + ".jsp" = "/WEB-INF/jsp2/c01_2/h3.jsp"
        return map;
      }
        
      // http://localhost:9999/eomcs-spring-webmvc/app2/c01_2/h4
      @GetMapping("h4")
      public ModelAndView handler4() {
      
        ModelAndView mv = new ModelAndView();
        mv.addObject("name", "홍길동3");
        mv.addObject("age", 40);
        mv.setViewName("c01_2/h4");
      
        // ModelAndView 객체에 값과 뷰 이름을 담아 리턴하면
        // 프론트 컨트롤러는 ModelAndView 객체에 보관되어 있는 값들을
        // ServletRequest 보관소로 옮기고,
        // 설정된 뷰 이름을 ViewResolver에게 넘긴다.
        //
        return mv;
      }
        
      // http://localhost:9999/eomcs-spring-webmvc/app2/c01_2/h5
      @GetMapping("h5")
      public ModelAndView handler5() {
      
        ModelAndView mv = new ModelAndView();
        mv.addObject("name", "홍길동3");
        mv.addObject("age", 40);
          
        // 뷰 이름을 지정하지 않으면 request handler의 path를 ViewResolver에게 넘긴다.
        // InternalResourceViewResolver 는 위 URL을 다음과 같이 바꾼다.
        // => /WEB-INF/jsp2/c01_2/h5.jsp
        return mv;
      }
      
    }
    

2. 세션다루기

1) HttpSession 직접 사용하기

2) @SessionAttributes, @ModelAttribute으로 다루기

3) 세션의 값을 무효화시키는 방법

3. 인터셉터

  • 프론트 컨트롤러와 페이지 컨트롤러 사이에 코드를 삽입하는 기술이다.

1) 설정하기

  • config에서 인터셉터 호출 정책을 설정한다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    
    @ComponentScan("bitcamp.app2")
    public class App2Config implements WebMvcConfigurer {
      // 이 설정을 사용하는 프론트 컨트롤러에 적용할 인터셉터 설정하기
      @Override
      public void addInterceptors(InterceptorRegistry registry) {
          
        // 1) 모든 요청에 대해 실행할 인터셉터 등록하기
        // => 인터셉터를 적용할 URL을 지정하지 않으면  현재 프론트 컨트롤러의 모든 요청에 대해 적용된다.
        registry
          .addInterceptor(new Controller04_1_Interceptor1());
      
        // 2) 특정 요청에 대해 실행할 인터셉터 등록하기
        // 패턴: /c04_1/* => c04_1/ 바로 밑의 있는 자원에 대해서만 인터셉터를 적용한다.
        registry
          .addInterceptor(new Controller04_1_Interceptor2())
          .addPathPatterns("/c04_1/*");
          
        // 3) 특정 요청에 대해 실행할 인터셉터 등록하기
        // 패턴: /c04_1/** => /c04_1/ 의 모든 하위 경로에 있는 자원에 대해서만 인터셉터를 적용한다.
        registry
         .addInterceptor(new Controller04_1_Interceptor3())
         .addPathPatterns("/c04_1/**");
          
       // 4) 특정 요청에 대해 인터셉터 적용을 제외하기
       // => 패턴: /c04_1/** (include), /c04_1/a/** (exclude)
       // 즉, /c04_1/ 의 모든 하위 경로에 있는 자원에 대해서만 인터셉터를 적용한다.
       // 단 /c04_1/a/ 의 모든 하위 경로에 있는 자원은 제외한다.
       registry
       .addInterceptor(new Controller04_1_Interceptor4())
       .addPathPatterns("/c04_1/**")
       .excludePathPatterns("/c04_1/a/**");
      }
    }
    

2) 인터셉터 구현하기

  • 인터페이스 HandlerInterceptor를 구현하여 만든다.
  • preHandle : 페이지 컨트롤러의 핸들러를 호출하기 전에 이 메서드가 먼저 호출된다.
  • postHandle : 페이지 컨트롤러의 핸들러가 리턴한 후 이 메서드가 호출된다.
  • afterCompletion : JSP를 실행한 후 이 메서드가 호출된다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    
    public class Controller04_1_Interceptor1 implements HandlerInterceptor {
      @Override
      public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
          throws Exception {
        // 페이지 컨트롤러의 핸들러를 호출하기 전에 이 메서드가 먼저 호출된다.
        System.out.println("Interceptor1.preHandle()");
      
        // 다음 인터셉터나 페이지 컨트롤러를 계속 실행하고 싶다면 true를 리턴한다.
        // 여기서 요청 처리를 완료하고 싶다면 false를 리턴한다.
        return true;
      }
      
      @Override
      public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
          ModelAndView modelAndView) throws Exception {
        // 페이지 컨트롤러의 핸들러가 리턴한 후 이 메서드가 호출된다.
        System.out.println("Interceptor1.postHandle()");
      }
      
      @Override
      public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
          Object handler, Exception ex) throws Exception {
        // JSP를 실행한 후 이 메서드가 호출된다.
        System.out.println("Interceptor1.afterCompletion()");
      }
    }
    

4. JSON 데이터 출력

5. 예외 처리하기

  • Request예외 처리 우선순위
    1. 페이지컨트롤러
    2. @ControllAdvice
    3. web.xml설정파일
    4. Servlet Container

1) 페이지 컨트롤러

  • 페이지 컨트롤러 내에 @ExceptionHandler가 있다면 이 컨트롤러의 예외는 해당 매서드로 처리된다.

    1
    2
    3
    4
    5
    6
    7
    8
    
      @ExceptionHandler
      public ModelAndView exceptionHandler(Exception ex) {
        System.out.println("Controller06_1.exceptionHandler() 호출됨!");
        ModelAndView mv = new ModelAndView();
        mv.addObject("error", ex);
        mv.setViewName("error6");
        return mv;
      }
    

    2) @ControllerAdvice

  • @ControllerAdvice을 사용하는 클래스 파일에서 에러 처리에 대해 탐색한다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    
    @ControllerAdvice
    public class GlobalControllerAdvice {
      
      // DispatcherServlet은
      // request handler가 던지는 예외에 따라
      // 그 예외를 받을 수 있는 메서드를 찾아 호출한다.
      // 단, @ExceptionHandler 가 선언된 메서드여야 한다.
      //
      @ExceptionHandler
      public ModelAndView exceptionHandler(Exception ex) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("error", ex);
        mv.setViewName("error3");
        return mv;
      }
      
      @ExceptionHandler
      public ModelAndView ioExceptionHandler(IOException ex) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("error", ex);
        mv.setViewName("error4");
        return mv;
      }
      
      @ExceptionHandler
      public ModelAndView sqlExceptionHandler(SQLException ex) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("error", ex);
        mv.setViewName("error5");
        return mv;
      }
    }
    

3) xml 설정파일

  • xml에서 error-page를 설정하여 해당 url로 이동할 수 있다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
      <!-- 
        서블릿 컨테이너에서 요청을 처리하는 중에 오류가 발생했을 때
        오류 내용을 출력하는 기본 페이지를 교체하기 
      -->
    <!-- 1)오류가 발생했을 때 직접 JSP를 실행  -->
      <error-page>
        <location>/WEB-INF/jsp2/error1.jsp</location>
      </error-page>
       
      <!-- 2)오류가 발생했을 때 페이지 컨트롤러를 경유하여 JSP 실행  -->
      <error-page>
        <location>/app2/error</location>
      </error-page>
    
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.