본문 바로가기
국비학원/수업기록

국비 지원 개발자 과정_Day99

by 루팽 2023. 4. 20.

Filter - API(인터페이스)

업무에 대한 서블릿 - 업무마다 존재하기에 중복도 있음 → 제거하는데 Filter사용

1. 업무 전처리

2. 처리

3. 업무 후처리

 

공통된 부분을 모아서 Filter(인터페이스)

1. 전처리

2. 서블릿 호출

3. 후처리 → MdelAndView(이제 사용x), Model, ModelMap

 

스프링의 경우 Front 서블릿인 DispatcherServlet 호출

→ 각 처리단계로 이어짐(같은처리 혹은 다른 처리), 서블릿계층(스프링에선 컨트롤러와 같음)

 

context는 저장소

서블릿 컨텍스트 - 필터사용

Servlet Context에 필터거주 - 필터 통해 서블릿 경유

WebApplication Context에 인터셉터 거주

 

Servlet Context에 사용자가 요청하고 Servlet이 요청받음 → Spring에 WebApplicationContext가 받음

서블릿 컨테이너를 WAS(Tomcat)이 쥐고있다(자세힌 jsp-api.jar와 servlet-api.jar)

Spring 컨테이너(BeanFactory와 ApplicationContext)

Interceptor → Controller

1. 전처리

2. 컨트롤러 메소드

3. 후처리

여러 빈 존재 가능(빈[객체]들에 대한 객체주입을 인터셉트가 받을 수 있다) → 필터와의 차이점

 

필터와 인터셉터

1. Filter가 발전하여 Interceptor가 됨

2. 서블릿이 발전해서 컨트롤러가 됨

전처리에서는 사용자로부터 입력을 받는다

Filter는 DispatcherServlet과 역할이 비슷하다

JSP와 서블릿이 발전해서 스프링이 되었다

 

설계방법에서 분리의 기술

1. 관심사 분리

2. 변하는 것과 변하지 않는 것

3. 중복코드

 

Interceptor는 Filter와 유사한 기능

FIlter와는 달리 WebApplicationContext에 위치하고 빈 주입이 가능하다

이것은 ServletContext(저장소)와 WebApplicationContext 모두 저장소

 

<필터와 인터셉터로 서비스 수행시간 계산 - Mblog1Application.java>

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@SpringBootApplication
@ServletComponentScan
public class Mblog1Application {

	public static void main(String[] args) {
		SpringApplication.run(Mblog1Application.class, args);
	}

	@Bean
	public WebMvcConfigurer corsConfigurer() {
		return new WebMvcConfigurer() {
			@Override
			public void addCorsMappings(CorsRegistry registry) {
				// 두 포트번호 모두 서비스를 제공하지만 3000번은 동기화된 서비스 제공, 7000번은 build된 코드까지만 제공된다
				// 결과적으로 3000번은 개발 서버로, 7000번은 서비스 서버로 하용하면 될 것이다
				registry.addMapping("/**").allowedOrigins("<http://localhost:3000>", "<http://localhost:7000>");
			}
		};
	}
}

 

<필터와 인터셉터로 서비스 수행시간 계산 - CustomerFilter.java>

package com.example.demo.servlet;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

@WebFilter(urlPatterns = "/*")
public class CustomerFilter implements Filter {
	Logger logger = LogManager.getLogger(CustomerFilter.class);
	/****************************************
	 * @param : 요청객체
	 * @param : 응답객체
	 * @param : 서블릿 또는 다음 필터 호출하는데 사용
	 ****************************************/
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		logger.info("doFilter");
		// 1. 전처리
		long startTime = System.currentTimeMillis();
		// 2. 처리
		chain.doFilter(request, response);
		// 3. 후처리 - 서비스 수행 시간 계산(performance)
		long endTime = System.currentTimeMillis();
		System.out.print(((HttpServletRequest)request).getRequestURI());
		// 수행시간(종료시간 - 시작시간)
		System.out.println("수행시간: " + (endTime - startTime) + "ms");
	}
}

 

<필터와 인터셉터로 서비스 수행시간 계산 - CustomerServlet.java>

package com.example.demo.servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/custom")
public class CustomerServlet extends HttpServlet {

	@Override
	protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		// 1. 전처리
		long startTime = System.currentTimeMillis();
		// 2. 처리
		int idx1 = (int)(Math.random()*6)+1;
		int idx2 = (int)(Math.random()*6)+1;
		res.setContentType("text/html;charset=utf-8");
		PrintWriter out = res.getWriter();
		out.println("<html>"); // ln은 line skip(줄바꿈)이 아니고 여기까지 말했다는 의미
		out.println("<head><title>필터실습</title></head>");
		out.println("<body>");
		out.println("내용입니다.");
		out.println("</body>");
		out.println("</html>");
		// 3. 후처리 - 서비스 수행 시간 계산(performance)
		long endTime = System.currentTimeMillis();
		System.out.print(((HttpServletRequest)req).getRequestURI());
		// 수행시간(종료시간 - 시작시간)
		System.out.println("수행시간: " + (endTime - startTime) + "ms");
	}
}

 

<필터와 인터셉터로 서비스 수행시간 계산 - CustomerInterceptor.java>

package com.example.demo.servlet;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

// 하나의 메소드는 하나의 책임만 갖는다 - 설계시 주의사항
@Component // 추가해야 CustomerInterceptor가 ApplicationContext가 되도록 해줌
public class CustomerInterceptor implements HandlerInterceptor {
	Logger logger = LogManager.getLogger(CustomerInterceptor.class);
	
	/*
	 	필터나 인터셉트나 컨트롤러 같은 것들은 서버 프로그램이기 때문에 싱글톤이라는 것을 명심할 것!
	 	싱글톤이라서 여러 스레드가 하나의 객체를 공유한가는 것이 주의할 점이라는 의미임
	 	다른 스레드가 startTime을 다른 값으로 덮어쓰기하는 경우가 발생할 수 있음
	 	그래서 여기서는 후처리에 필요한 startTime을 요청객체에 setAttribute해서 사용함
	 */
	// 전처리를 구현하는 메소드
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		logger.info("전처리 구현 메소드");
		// 1. 전처리
		long startTime = System.currentTimeMillis();
		request.setAttribute("startTime", startTime);
		// preHandle의 세번쨰 파라미터 handler는 요청과 연결된 컨트롤러의 메소드임
		HandlerMethod method = (HandlerMethod)handler;
		logger.info("method.getMethod() => " + method.getMethod()); // URL과 연결된 메소드명
		logger.info("method.getBean() => " + method.getBean()); // 메소드가 포함된 컨트롤러 -> 컨트롤클래스 주소번지
		return HandlerInterceptor.super.preHandle(request, response, handler);
	}
	
	// 후처리를 구현하는 메소드
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		logger.info("후처리 구현 메소드");
		long startTime = (long)request.getAttribute("startTime");
		long endTime = System.currentTimeMillis();
		logger.info(((HttpServletRequest)request).getRequestURI());
		// 수행시간(종료시간 - 시작시간)
		logger.info("수행시간: " + (endTime - startTime)  + "ms");
		HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
	}
}

 

<필터와 인터셉터로 서비스 수행시간 계산 - WebMvcConfig.java>

package com.example.demo;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import com.example.demo.servlet.CustomerInterceptor;

// WebMvcConfig의 addInterceptors가 있어야 사용자 정의 인터셉터 사용가능
// 이런 설정들은 반드시 Configuration 어노테이션 붙여야함
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(new CustomerInterceptor())
				.addPathPatterns("/**"); // 와일드카드 2번 써야함 -> 인터셉터를 적용할 대상을 설정함
	}
}

 

<함수, 컴포넌트 예제 - TentPage.jsx>

import React, { useEffect, useState } from 'react'
import ScheduleHeader from '../include/ScheduleHeader'
import ScheduleFooter from '../include/ScheduleFooter'
import TentSideBar from '../tent/TentSideBar'
import TentMain from '../tent/TentMain'
import styles from './tent.module.css'
import axios from 'axios'

const TentPage = () => {
  const [tentList, setTentList] = useState([])
  const getTentList = (name, value) => {
    console.log(name + "=> " + value)
    if("first" === name) {
      axios.get("/tent.json").then(res => {
        setTentList(res.data)
        if(value) {
          const tents = tentList.filter(tent => tent.id === 1)
          setTentList([...tents])
        }
      })
    } else if("second" === name) {
      axios.get("/tent.json").then(res => {
        setTentList(res.data)
        if(value) {
          const tents = tentList.filter(tent => tent.id === 2)
          setTentList([...tents])
        }
      })
    } else if("third" === name) {
      axios.get("/tent.json").then(res => {
        setTentList(res.data)
        if(value) {
          const tents = tentList.filter(tent => tent.id === 3)
          setTentList([...tents])
        }
      })
    } else { // 아무것도 체크하지 않았을 때
      axios.get("/tent.json").then(res => {
        setTentList(res.data)
      })
    }
  }
  useEffect(() => {
    getTentList()
  }, [])
  return (
    <>
      <ScheduleHeader />
      <div styles={styles.container}>
        <TentSideBar getTentList={getTentList} />
        <TentMain tentList={tentList} />
      </div>
      <ScheduleFooter />
    </>
  )
}

export default TentPage

 

<함수, 컴포넌트 예제 - TentSideBar.jsx>

import React, { useState } from 'react'
import styles from './sidebar.module.css'
import { Form } from 'react-bootstrap'

const TentSideBar = ({getTentList}) => {
  const [first, setFirst] = useState(false)
  const [second, setSecond] = useState(false)
  const [third, setThird] = useState(false)
  const handleFirst = () => {
    setFirst(!first)
    console.log(first)
    getTentList('first', !first)
  }
  const handleSecond = () => {
    setSecond(!second)
    console.log(second)
    getTentList('second', !second)
  }
  const handleThird = () => {
    setThird(!third)
    console.log(third)
    getTentList('third', !third)
  }

  return (
    <div styles={styles.editor}>
      <Form.Check type='checkbox' label='1등급' onClick={handleFirst} />
      <Form.Check type='checkbox' label='2등급' onClick={handleSecond} />
      <Form.Check type='checkbox' label='3등급' onClick={handleThird} />
    </div>
  )
}

export default TentSideBar

 

<함수, 컴포넌트 예제 - TentMain.jsx>

import React from 'react'
import styles from './main.module.css'

const TentMain = ({tentList}) => {
  return (
    <div styles={styles.preview}>
      <ul>
        {tentList.map((tent, index) => (
          <li key={index}>{`상품명: ${tent.title}, 가격: ${tent.price}`}</li>
        ))}
      </ul>
    </div>
  )
}

export default TentMain

댓글