필터, 인터셉터, 파일업로드

Seung jun Cha·2022년 6월 23일
0

1. 서블릿 필터

1-1 개념

  • 필터는 서블릿 스펙에서 정의되며, 서블릿 컨테이너에서 요청과 응답의 전후에 특정 작업을 수행하기 위해 사용된다.
    여기서 서블릿 컨테이너는 웹 애플리케이션의 실행과 관리를 담당하는 런타임 환경으로 대표적으로 Tomcat이 있다.
    필터는 클라이언트의 요청이 서블릿으로 전달되기 전에 호출된다.
  1. 웹과 관련된 공통 관심사를 처리할 때는 HTTP의 헤더나 URL의 정보들이 필요한데, 서블릿 필터나 스프링 인터셉터는 HttpServletRequest를 제공한다.

  2. 필터 인터페이스를 구현하고 등록하면 서블릿 컨테이너가 필터를 싱글톤 객체로 생성하고, 관리한다.

  3. Dispatcher Servlet에 요청이 전달되기 전후에 URL 패턴에 맞는 모든 요청에 대해 부가작업을 처리할 수 있는 기능을 제공한다. 즉 적절하지 않은 요청이 들어올 경우 서블릿까지 도달하지 못하게 한다.

  1. 서블릿 필터는 스프링컨테이너가 아닌 WAS내에 있는 웹 컨텍스트에서 관리한다. 웹 컨텍스트는 웹 애플리케이션에서 사용되는 서블릿, 필터, 리스너 등의 웹 컴포넌트들의 실행 환경을 제공하는 객체입니다.
  • 스프링 인터셉터가 없을 때
    HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 컨트롤러
    //로그인 사용자
    HTTP 요청 -> WAS -> 필터(적절하지 않은 요청이라 판단, 서블릿 호출X)
    //비 로그인 사용자
  1. public class LogFilter implements Filter {} : 필터 인터페이스 구현
    (1)init(): 필터를 초기화하고 서비스에 추가하기 위한 메소드로 서블릿 컨테이너가 생성될 때 호출된다.
    (2)doFilter(): 고객의 요청(URL Pattern)이 올 때마다 해당하는 필터메서드가 호출된다. 필터의 로직을 구현하면 된다.
    (3)destroy(): 필터 종료 메서드, 서블릿 컨테이너가 종료될 때 호출된다.

  2. HTTP 요청이 오면 doFilter가 호출된다.
    ServletRequest request는 HTTP 요청이 아닌 경우까지 고려해서 만든 인터페이스이다. HTTP를 사용하면 HttpServletRequest httpRequest = (HttpServletRequest) request; 와 같이 다운 케스팅 하면 된다.
    (ServletRequest는 기능이 별로 없음)

  3. chain.doFilter(request, response);
    다음 필터가 있으면 필터를 호출하고, 필터가 없으면 서블릿을 호출한다.
    만약 이 로직을 호출하지 않으면 다음 단계로 진행되지 않는다
    request, response 조작이 가능하다

filter1 - filter2 - filter3
filter1 - resquest, response - filter2 - request - filter3
request, response 객체를 직접 다룰 수 있다.
  1. WebConfig : 필터 설정
@Configuration
public class WebConfig {
 @Bean
 public FilterRegistrationBean logFilter() {
 	FilterRegistrationBean<Filter> filterRegistrationBean = new
FilterRegistrationBean<>();
 	filterRegistrationBean.setFilter(new LogFilter());
 	filterRegistrationBean.setOrder(1); // 숫자가 낮을수록 먼저 동작
 	filterRegistrationBean.addUrlPatterns("/*"); // 필터를 적용할 URL
 return filterRegistrationBean;
 	}
}

(1)스프링 부트를 사용한다면 FilterRegistrationBean 을 사용해서 등록하면 된다.

(2)@ServletComponentScan @WebFilter(filterName = "logFilter", urlPatterns = "/*") 로 필터 등록이 가능하지만 필터 순서 조절이 안된다. 따라서 FilterRegistrationBean 을 사용하자.
(3) 이미지나 데이터 압축이나 문자열 인코딩과 같이 웹 애플리케이션에 전반적으로 사용되는 기능 구현하기에 적당함

2. 스프링 인터셉터

2-1 개념

  1. 스프링 MVC에서 제공하는 기술
    웹 컨테이너에서 동작하는 필터와 달리 인터셉트는 프레임워크에서 동작한다.(호출시점이 다르다)
    인터셉터는 스프링 컨텍스트 내에서 동작하며, 컨트롤러의 앞뒤에서 요청을 가로채어 처리한다.

  2. 스프링 시큐리티를 사용하지 않는 경우에 인터셉터로 인증, 보안 기능을 직접 개발
    HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터 -> 컨트롤러
    //로그인 사용자
    HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터 (적절하지 않은 요청이라 판단, 컨트롤러 호출 X)
    // 비 로그인 사용자

  3. 인터셉터는 컨트롤러를 호출할 때마다 호출되며 dispatcher-Servlet이 각각의 컨트롤러를 호출하기 전후에 어떤 작업을 해야하는지 정할 수 있다.
    즉, 컨트롤러로 넘겨주기 위한 정보를 가공하기에 용이하다


  1. preHandle : 컨트롤러 호출 전에 호출된다. preHandle 의 응답값이 true 이면 다음으로 진행하고, false 이면 더는 진행하지 않는다.
  2. postHandle : 컨트롤러 호출 후에 호출된다. 컨트롤러에서 예외가 발생하면 postHandle 은 호출되지 않는다.
  3. afterCompletion : 뷰가 렌더링 된 이후에 호출된다. 예외가 발생해도 항상 호출되며 예외가 발생하는 경우, 예외를 파라미터로 받아서 로그로 출력할 수 있다.
  • 로그인 검증 인터셉터
@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: {}", requestURI);

        HttpSession session = request.getSession(false);
        if (session == null || session.getAttribute(SessionConst.LOGIN_MEMBER) == null) {
            log.info("미인증 사용자 요청");
            response.sendRedirect("/login?redirectURL=" + requestURI);
            // 로그인 후 요청페이지로 다시 리다이렉트
            return false;
        }
        return true;
    }
}
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginCheckInterceptor())
                .order(1)
                .addPathPatterns("/**")
                .excludePathPatterns(
                        "/", "/members/add", "/login", "/logout",
                        "/css/**", "/*.ico", "/error"
                );
    }
}
  • response.sendRedirect() : HttpServletResponse 객체를 이용해서 직접적으로 HTTP 응답을 조작해 리디렉션을 처리하는 방법입니다. 주로 필터나 인터셉터에서 사용합니다
  • return "redirect:url" : 스프링 MVC 컨트롤러 내에서 사용하여 ViewResolver가 내부적으로 클라이언트에게 리디렉션 처리해줍니다. 스프링 MVC 컨트롤러에서 주로 사용합니다

3. 파일 업로드

  • 파일을 업로드 하려면 파일은 문자가 아니라 바이너리 데이터를 전송해야 한다. 또한 파일만 전송하는 상황이 아닌 문자를 같이 전송하는 상황이 대부분이다.
    문제를 해결하기 위해 HTTP는 multipart/form-data 라는 전송 방식을 제공한다.

  • 방식을 사용하려면 Form 태그에 별도의 enctype="multipart/form-data" 를 지정해야 한다.
    Content-Disposition 이라는 항목별 헤더가 추가되어 있고 여기에 부가 정보가 있다. 파일의 경우, 파일이름과 Content-Type이 추가되고 바이너리 데이터가 전송된다.

  • 스프링은 MultipartFile 이라는 인터페이스로 멀티파트 파일을 매우 편리하게 지원한다
    @RequestParam MultipartFile file
    업로드하는 HTML Form의 name에 맞추어 @RequestParam 을 적용하면 된다. 추가로
    @ModelAttribute 에서도 MultipartFile 을 동일하게 사용할 수 있다.
    file.getOriginalFilename() : 업로드 파일 명
    file.transferTo(...) : 파일 저장

파일을 업로드 할 때 제목은 텍스트 파일은 바이너리값(스트림으로 읽어야함)
-> multipart 타입으로 boundary가 나누어져 있음 (multipartRequest가 파싱할 때 알아서 나눔)

  1. getServlteContext를 가져온다 : war파일이 out폴더에 생기는데-
    현재 서버가 실행되고 있는 위치를 가져온다(서버는 out폴더에 있는 war파일에서 실행된다)

src에 파일을 저장하는게 아니고 out폴더에 저장한다

String fullPath = 서버가 실행되고 있는 위치(서블릿컨텍스트위치) + FIle.separator + out폴더에서 저장할 폴더 이름

MultipartRequest multipartRequest
= new MultipartRequest(request, fullPath, maxSize, encType, new CustomRenamePolicy(fullPath));
여기서 request는 jsp화면에서 post한 파일이 담긴 서블릿request

파일들이 계속 새로 업로드 될 수있으니까 링크드리스트

파일정보는 request로 얻을 수 있는 정보들을 변수로 함

fileLocation필드에 ../upload/파일경로를 저장한 이유는?

string으로 DB에 저장되어있는 값을 가져와서 링크드리스트 객체로 반환... 리스트로 변환하는 파싱함수를 만들기 : [fileInfo{}, fileInfo{}, fileInfo{}] 이렇게 스트링화 된 애들을 다시 링크드리스트로?
StringTokenizer를 만들어서 '를 기준으로 잘라준다

0개의 댓글