본문 바로가기
BackEnd/Project

[MVC] Ch06. MVC 프레임워크(3)

by 개발 Blog 2024. 8. 1.

공부 내용을 정리하고 앞으로의 학습에 이해를 돕기 위해 작성합니다.

 

스프링 웹 MVC 프레임워크 - 1

1. Request (요청): 클라이언트가 서버로 HTTP 요청을 보낸다.
2. DispatcherServlet: 모든 요청을 수신하는 프론트 컨트롤러이다. 이 서블릿은 요청을 적절한 핸들러로 라우팅 한다.
3. Handler Mapping: 요청 URI에 따라 적절한 핸들러(Controller)를 선택한다. 요청된 경로와 일치하는 핸들러를 찾아 반환한다.
4. Handler Adapter: 선택된 핸들러를 호출할 수 있도록 적응시키는 역할을 한다. 다양한 유형의 핸들러를 지원하기 위해 필요하다.
5. Controller: 비즈니스 로직을 실행하고, 필요한 경우 모델 데이터를 생성하거나 수정하며, 결과로 뷰 이름을 반환한다.
6. View Resolver: 컨트롤러에서 반환된 뷰 이름을 실제 뷰(예: JSP, HTML)로 변환한다. 뷰 이름을 기반으로 클라이언트에게 응답할 화면을 결정한다.
7. View: 최종적으로 사용자에게 보여줄 화면을 렌더링 한다. 여기서 모델 데이터를 사용하여 페이지를 구성하고, 사용자에게 시각적으로 표현한다.
8. Response (응답): 생성된 뷰가 클라이언트에게 응답으로 전달된다.

애노테이션 기반 MVC 프레임워크 구현 실습

이 실습에서는 Spring Web MVC와 유사한 흐름을 가진 프레임워크를 구현할 것이다.

- DispatcherServlet

- AnnotationHandlerMapping

- HandlerAdapter

- ViewResolver

 

디스패처 서블릿 실습에 사용했던 코드를 기반으로 MVC 프레임 워크를 만든다.

 

UserListController 생성

package org.example.mvc.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

public class UserListController implements Controller{
    @Override
    public String handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        request.setAttribute("users", List.of());
        return "/user/list.jsp";
    }
}

- 사용자 목록을 보여주는 역할을 한다.

- request.setAttribute()를 사용하여 users와 그에 대응하는 객체(빈 리스트)를 request 객체에 저장한다.

 

user.jsp를 webapps -> user 밑에 생성한다.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<!DOCTYPE html>
<html lang="kr">
<head>
    <meta charset="UTF-8">
    <title>list</title>
</head>
<body>

<table>
    <thead>
    <tr>
        <th>#</th>
        <th>아이디</th>
        <th>이름</th>
        <th></th>
    </tr>
    </thead>
    <tbody>
    <c:forEach items="${users}" var="user" varStatus="status">
        <tr>
            <th scope="row">${status.count}</th>
            <td>${user.userId}</td>
            <td>${user.name}</td>
            </td>
        </tr>
    </c:forEach>
    </tbody>
</table>
</body>
</html>

- 컨트롤러에서 제공한 사용자 목록을 반복하면서 각 사용자의 목록을 테이블 형식으로 웹 페이지에 출력한다.

- JSP와 JSTL을 활용하여 동적인 웹 콘텐츠를 생성하고, 클라이언트에게 사용자 데이터를 시각적으로 제공 역할을 한다.

- ${users}는 request 객체에 저장된 users 속성을 참조하며 user 변수는 반복의 각 항목을 나타낸다.

 

RequestMappingHandlerMapping에 users 경로를 UserListController로 매핑하기 위해 코드를 추가한다.

package org.example.mvc;

import org.example.mvc.controller.Controller;
import org.example.mvc.controller.HomeController;
import org.example.mvc.controller.UserListController;

import java.util.HashMap;
import java.util.Map;

public class RequestMappingHandlerMapping {
    private Map<String, Controller> mappings = new HashMap<>();

    void init() {
        mappings.put("/", new HomeController());
        mappings.put("/users", new UserListController());
    }

    public Controller findHandler(String urlPath) {
        return mappings.get(urlPath);
    }
}

- 컨트롤러가 추가될 때마다 경로와 컨트롤러를 추가해 주면 디스패처 서블릿에서 적절한 컨트롤러를 찾아서 화면을 보여준다.

 

서버를 실행하고 해당 url을 요청하면 

아이디와 이름을 가진 화면이 출력된다.

 

하지만 지금 코드에서는 경로가 같을 때 문제가 발생한다.

 

- RequestMappingHandlerMapping이 사진에서 HandlerMapping이다.

- HandlerMapping이 Handler를 선택하는 부분을 생성할 차례다.

 

RequestMappingHandlerMapping, RequestMethod, HandlerKey 클래스를 작성하여 HTTP 요청에 따라 적절한 컨트롤러를 매핑하는 기능을 구현한다. 이 과정에서 GET 요청과 POST 요청을 구분하여 처리할 수 있게 된다.

 

RequestMethod 열거형(enum)

먼저, HTTP 메서드(GET, POST 등)를 나타내는 RequestMethod 열거형을 정의한다.

이 열거형은 요청 메서드를 구분하고, 각 요청에 맞는 컨트롤러를 매핑하는 데 사용된다.

package org.example.mvc.controller;

public enum RequestMethod {
    GET, POST, PUT, DELETE
}

- HTTP 요청 메서드의 종류를 정의하며, 각 요청이 어떤 메서드를 사용하는지를 나타낸다.

 

HandlerKey 클래스

HandlerKey 클래스는 RequestMethod와 요청 URI를 조합하여 생성된다.

이 클래스는 요청을 처리할 컨트롤러를 찾기 위한 키로 사용된다.

package org.example.mvc;

import org.example.mvc.controller.RequestMethod;

public class HandlerKey {

    private final RequestMethod requestMethod;
    private final String uriPath;

    public HandlerKey(RequestMethod requestMethod, String uriPath) {
        this.requestMethod = requestMethod;
        this.uriPath = uriPath;
    }
}

- HandlerKey: 요청 메서드와 URI 경로를 조합하여 생성되는 키이다.

 

RequestMappingHandlerMapping 클래스

이 클래스는 다양한 요청 경로와 HTTP 메서드에 대해 적절한 컨트롤러를 매핑하는 역할을 한다.

초기화 과정에서 각 요청 경로와 메서드에 대해 매핑을 설정하며, 요청이 들어올 때 해당 요청을 처리할 컨트롤러를 반환한다.

public class RequestMappingHandlerMapping {
    private Map<HandlerKey, Controller> mappings = new HashMap<>();
    
	...
    
    void init() {
        mappings.put(new HandlerKey(RequestMethod.GET, "/"), new HomeController());
        mappings.put(new HandlerKey(RequestMethod.GET, "/users"), new UserListController());
        mappings.put(new HandlerKey(RequestMethod.POST, "/users"), new UserCreateController());
    }
    
    public Controller findHandler(HandlerKey handlerKey) {
   	 return mappings.get(handlerKey);
    }
}

- mappings 필드

  •  HandlerKey와 Controller 객체를 매핑하여 저장하는 맵이다.

- init() 메서드

  • RequestMappingHandlerMapping을 초기화하면서 각 요청 경로와 HTTP 메서드에 대해 컨트롤러를 매핑한다.
  • 예를 들어, GET 요청일 때 /users 경로로 UserListController
  • POST 요청일 때 /users 경로로 UserCreateController가 호출되도록 설정한다.

- findHandler

  • HandlerKey를 기반으로 매핑된 Controller 객체를 찾아 반환한다.

 

이를 통해 같은 URI 경로라도 요청 메서드에 따라 다른 컨트롤러를 사용할 수 있으며, 애플리케이션의 구조와 흐름을 유연하게 관리할 수 있다.

 

이제 http://localhost:8080/users 경로로 HTTP 요청 테스트를 한다.

하지만 기대했던 결과가 아니다.

 

위와 같이 원하는 화면이 나타나지 않는 이유는 RequestMappingHandlerMapping에서 mappings.get(uriPath)를 호출할 때, HandlerKey 객체가 올바르게 비교되지 않기 때문이다. HandlerKey 객체는 키로 사용되기 때문에, 키 객체 간의 비교는 기본적으로 객체의 메모리 주소를 비교하게 된다. 따라서 같은 값이라도 다른 인스턴스는 다르게 인식된다. 이를 해결하려면 HandlerKey 클래스에서 equals와 hashCode 메서드를 오버라이드하여 객체의 내용 기반으로 비교할 수 있도록 해야 한다.

package org.example.mvc;

import org.example.mvc.controller.RequestMethod;

import java.util.Objects;

public class HandlerKey {

    private final RequestMethod requestMethod;
    private final String uriPath;

    public HandlerKey(RequestMethod requestMethod, String uriPath) {
        this.requestMethod = requestMethod;
        this.uriPath = uriPath;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        HandlerKey that = (HandlerKey) o;
        return requestMethod == that.requestMethod && Objects.equals(uriPath, that.uriPath);
    }

    @Override
    public int hashCode() {
        return Objects.hash(requestMethod, uriPath);
    }
}

 

DispatcherServlet도 HandlerKey 객체로 반환받도록 수정해 준다.

@WebServlet("/")
public class DispatcherServlet extends HttpServlet {

    ...

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        try {
            Controller handler = rmhm.findHandler(new HandlerKey(RequestMethod.valueOf(request.getMethod()), request.getRequestURI()));
            
            ...

        } catch (Exception e) {
            ...
        }
    }
}

 

이제 다시 테스트해보면

 

정상적으로 화면이 출력되는 것을 확인할 수 있다.

 

다음으로 사용자 관리를 위해 User, UserRepository, UserCreateController 클래스를 구현한다.

 

User 클래스

package org.example.mvc.model;

public class User {
    private final String userId;
    private final String name;

    public User(String userId, String name) {
        this.userId = userId;
        this.name = name;
    }

    public String getUserId() {
        return userId;
    }

    public String getName() {
        return name;
    }
}

- 사용자 ID와 이름을 속성으로 가지며 애플리케이션 내에서 사용자 객체를 생성하고 관리하는 역할을 한다.

 

UserRepository 클래스

package org.example.mvc.repository;

import org.example.mvc.model.User;

import java.util.HashMap;
import java.util.Map;

public class UserRepository {
    private static Map<String, User> users = new HashMap<>();

    public static void save(User user) {
        users.put(user.getUserId(), user);
    }
}

- 사용자 데이터를 저장하는 간단한 메모리 기반 저장소를 제공하며, 사용자의 데이터를 추가할 수 있다.

- 사용자 ID를 키로, User 객체를 값으로 저장하는 HashMap을 사용한다.

- save 메서드는 사용자 객체를 저장하거나 업데이트한다.

 

UserCreateController 클래스

package org.example.mvc.controller;

import org.example.mvc.model.User;
import org.example.mvc.repository.UserRepository;

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

public class UserCreateController implements Controller {
    @Override
    public String handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        UserRepository.save(new User(request.getParameter("userId"), request.getParameter("name")));
        return "redirect:/users";
    }
}

- 이 클래스는 사용자 입력 데이터를 받아 User 객체를 생성하고, 이를 UserRepository에 저장한다.

- 사용자 생성이 완료되면, 사용자 목록 페이지로 리다이렉트 하여 사용자에게 업데이트된 정보를 보여준다.

 

이번에는 사용자를 특정 화면으로 포워딩하기 위해 ForwardController 클래스와 RequestMappingHandlerMapping 클래스에 해당 매핑을 추가한다. 

 

ForwardController 

ForwardController 클래스는 요청을 특정 URI로 포워딩하는 역할을 한다.

이 클래스는 Controller 인터페이스를 구현하며, 포워딩할 URI 경로를 생성자에서 받아서 저장한다.

package org.example.mvc.controller;

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

public class ForwardController implements Controller {

    private final String forwardUriPath;

    public ForwardController(String forwardUriPath) {
        this.forwardUriPath = forwardUriPath;
    }

    @Override
    public String handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        return forwardUriPath;
    }
}

- forwardUriPath

  • 포워딩할 URI 경로를 저장하는 필드이다.

- handleRequest

  • 클라이언트의 요청을 처리하고, 지정된 forwardUriPath를 반환한다.
  • 반환된 URI는 DispatcherServlet에 의해 클라이언트가 이동할 새로운 경로로 사용된다.

RequestMappingHandlerMapping

새로운 ForwardController를 등록하여 특정 경로에 대한 요청이 들어왔을 때 해당 화면으로 포워딩할 수 있도록 설정하였다.

public class RequestMappingHandlerMapping {
    private Map<HandlerKey, Controller> mappings = new HashMap<>();

    void init() {
        mappings.put(new HandlerKey(RequestMethod.GET, "/"), new HomeController());
        mappings.put(new HandlerKey(RequestMethod.GET, "/users"), new UserListController());
        mappings.put(new HandlerKey(RequestMethod.POST, "/users"), new UserCreateController());
        
        //추가
        mappings.put(new HandlerKey(RequestMethod.GET, "/user/form"), new ForwardController("/user/form.jsp"));
    }

    public Controller findHandler(HandlerKey handlerKey) {
        return mappings.get(handlerKey);
    }
}

- mappings 필드

  • 요청 경로와 HTTP 메서드를 키로 하여 Controller 객체를 값으로 가지는 맵이다.

- init() 메서드

  • 다양한 경로와 HTTP 메서드에 대해 컨트롤러를 매핑한다.
  • 이번에 추가된 /user/form 경로에 대한 요청은 ForwardController가 처리하여 지정된 경로로 포워딩한다.

-findHandler

  • HandlerKey 객체를 기반으로 매핑된 컨트롤러를 찾아 반환한다.

다음으로 사용자로부터 데이털르 입력받아 서버로 전송하는 웹폼을 만들기 위해 form.jsp 파일을 추가한다.

 

form.jsp

form.jsp는 사용자로부터 ID와 이름을 입력받는 폼을 제공하는 JSP 페이지이다.

이 폼은 데이터를 POST 메서드를 통해 서버에 제출하고, 새로운 사용자를 생성하기 위한 데이터로 사용된다.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<!DOCTYPE html>
<html lang="kr">
<head>
    <meta charset="UTF-8">
    <title>form</title>
</head>
<body>
<form method="post" action="/users">
    <div>
        <label for="userId">사용자 아이디</label>
        <input class="form-control" id="userId" name="userId" placeholder="User ID">
    </div>
    <div>
        <label for="name">이름</label>
        <input class="form-control" id="name" name="name" placeholder="Name">
    </div>
    <button type="submit">회원가입</button>
</form>
</body>
</html>

- 사용자가 데이터를 입력하고 "회원가입" 버튼을 클릭하면, 폼 데이터는 POST 메서드를 통해 /users 경로로 전송된다.

- 이 경로는 UserCreateController가 처리하며, 새로운 사용자 데이터를 저장하고 사용자 목록 페이지로 리다이렉트 한다.

 

이제 http://localhost:8080/user/form 경로로 HTTP 요청을 보내어, form.jsp 페이지가 제대로 로드되는지 확인해 보자.

정상적으로 화면이 출력된다.

 

하지만 데이터를 입력하고 회원가입 버튼을 클릭하면 아래와 같이 에러 페이지가 나타난다. 

 

원인은 DispatcherServlet에서 반환된 viewName이 "redirect:/users"인 경우에도 포워딩이 이루어지기 때문이다.

포워딩과 리다이렉트는 다른 동작이며, 이를 구분해야 한다.

 

리다이렉트가 필요한 경우에는 서버가 클라이언트에게 새로운 URL로 요청을 보내도록 지시해야 하며, 포워딩은 서버 내부에서 요청을 다른 리소스(JSP 페이지 등)로 전달하는 것이다. 따라서, DispatcherServlet에서 viewName이 "redirect:"로 시작하는 경우 리다이렉트 하도록 처리하고, 그렇지 않은 경우에는 지정된 JSP 페이지로 포워딩하도록 로직을 분리해야 한다.

 

참고 (리다이렉션과 포워딩의 차이)

https://doublesprogramming.tistory.com/63

https://kotlinworld.com/329

 

 

현재까지 한 과정을 정리해 보면,

1. 클라이언트가 특정 URL로 요청을 보내면, DispatcherServlet이 이 요청을 받아 RequestMappingHandlerMapping을 통해 적절한 컨트롤러를 찾는다.

2. 컨트롤러는 요청을 처리하고 결과를 반환하며, 이 결과는 화면에 그대로 출력된다. 

 

HandlerAdapter와 ViewResolver를 구현해야 하는데 우선 ViewResolver부터 구현한다. 

 

View 인터페이스

package org.example.mvc.view;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

public interface View {
    void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
}

- 뷰를 렌더링 하기 위한 인터페이스이다.

- 각 뷰는 이 인터페이스를 구현하여 render 메서드를 통해 클라이언트에게 응답을 전달한다.

 

JspView 클래스

package org.example.mvc.view;

import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

public class JspView implements View{
    private final String name;

    public JspView(String name) {
        this.name = name;
    }

    @Override
    public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        model.forEach(request::setAttribute);

        //forward
        RequestDispatcher requestDispatcher = request.getRequestDispatcher(name);
        requestDispatcher.forward(request, response);

    }
}

- View 인터페이스를 구현하여 JSP 파일로의 포워딩을 처리한다.

- 모델 데이터를 JSP 페이지에 전달하고, 클라이언트에게 응답을 제공한다.

 

RedirectView 클래스

package org.example.mvc.view;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

public class RedirectView implements View{
    public static final String DEFAULT_REDIRECT_PREFIX = "redirect:";
    private final String name;

    public RedirectView(String name) {
        this.name = name;
    }

    @Override
    public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        response.sendRedirect(name.substring(DEFAULT_REDIRECT_PREFIX.length()));
    }
}

- View 인터페이스를 구현하여 클라이언트에게 지정된 URL로 리다이렉트하도록 지시한다.

- "redirect:"로 시작하는 뷰 이름을 처리한다.

 

ViewResolver 인터페이스

package org.example.mvc.view;

public interface ViewResolver {
    View resolveView(String viewName);
}

- 뷰 이름을 받아 적절한 View 객체를 반환하는 resolveView 메서드를 정의한 인터페이스이다.

 

JspViewResolver 클래스

package org.example.mvc.view;

import static org.example.mvc.view.RedirectView.DEFAULT_REDIRECT_PREFIX;

public class JspViewResolver implements ViewResolver{
    @Override
    public View resolveView(String viewName) {
        if (viewName.startsWith(DEFAULT_REDIRECT_PREFIX)){
            return new RedirectView(viewName);
        }
        return new JspView(viewName + ".jsp");
    }
}

- ViewResolver 인터페이스를 구현하여, 뷰 이름에 따라 JspView 또는 RedirectView 객체를 반환한다.

- 이를 통해 포워딩과 리다이렉트를 구분하여 처리한다.

 

다음으로 DispatcherServlet에서 viewResolver에 대한 로직을 구현한다.

@WebServlet("/")
public class DispatcherServlet extends HttpServlet {

    private static final Logger log = LoggerFactory.getLogger(DispatcherServlet.class);

    private RequestMappingHandlerMapping rmhm;

    private List<ViewResolver> viewResolvers;

    @Override
    public void init() throws ServletException {
        rmhm = new RequestMappingHandlerMapping();
        rmhm.init();

        viewResolvers = Collections.singletonList(new JspViewResolver());
    }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        log.info("[DispatcherServlet] service started");

        try {
            Controller handler = rmhm.findHandler(new HandlerKey(RequestMethod.valueOf(request.getMethod()), request.getRequestURI()));
            String viewName = handler.handleRequest(request, response);

            for (ViewResolver viewResolver : viewResolvers) {
                View view = viewResolver.resolveView(viewName);
                view.render(new HashMap<>(), request, response);
            }

        } catch (Exception e) {
            log.error("exception occurred: [{}]", e.getMessage(), e);
            throw new ServletException(e);
        }
    }
}

- ViewResolver 리스트 추가

  • viewResolvers라는 리스트를 추가하여 여러 ViewResolver를 관리한다. 이를 통해 다양한 뷰 타입을 지원할 수 있다.
  • init 메서드에서 viewResolvers 리스트를 초기화하고, 기본적으로 JspViewResolver를 추가하여 JSP 뷰를 처리하도록 설정한다.

- 뷰 해석 및 렌더링

  • service 메서드에서 컨트롤러가 반환한 viewName을 가지고 viewResolvers 리스트를 순회하며 적절한 View 객체를 찾는다.
  • ViewResolver는 resolveView 메서드를 통해 viewName에 해당하는 View 객체를 반환한다. 만약 viewName이 "redirect:"로 시작하면 RedirectView를, 그렇지 않으면 JspView를 반환한다.
  • 반환된 View 객체의 render 메서드를 호출하여, 클라이언트에게 응답을 전달한다.

이제 http://localhost:8080/user/form을 입력하고 회원가입 버튼을 눌러보면 정상적으로 리다이렉트 되는 것을 확인할 수 있다.

 

등록된 회원의 목록을 가져오기 위해 UserListController와 UserRepository에 로직을 추가한다.

public class UserListController implements Controller{
    @Override
    public String handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
    
    	//findAll로 모든 회원 조회
        request.setAttribute("users", UserRepository.findAll());
        return "/user/list";
    }
}

---------------------------------------------------------------------

public class UserRepository {
    private static Map<String, User> users = new HashMap<>();

    public static void save(User user) {
        users.put(user.getUserId(), user);
    }
	
    // 회원 목록 반환
    public static Collection<User> findAll() {
        return users.values();
    }
}

 

이제 회원 정보를 입력하고 회원가입을 눌러보면 

한글이 깨지긴 하지만 정상적으로 출력되는 것을 확인할 수 있다. 

 

웹 애플리케이션에서 한글이 깨지는 문제를 방지하기 위해 서블릿 필터를 사용하여 요청과 응답의 문자 인코딩을 설정할 수 있다. 이를 위해 CharacterEncodingFilter라는 서블릿 필터를 구현한다.

 

CharacterEncodingFilter 클래스

모든 요청과 응답에 대해 문자 인코딩을 "UTF-8"로 설정하는 필터이다.

이 필터는 한글과 같은 다국어 문자를 올바르게 처리하기 위해 사용된다.

package org.example.mvc.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter("/*")
public class CharacterEncodingFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");

        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {

    }
}

- init 메서드: 필터 초기화 시 호출되며, 현재는 특별한 초기화 작업이 필요하지 않아 빈 상태로 남겨두었다.

- doFilter 메서드: 필터의 핵심 기능을 수행한다. 이 메서드는 다음과 같은 작업을 한다:

  • request.setCharacterEncoding("UTF-8")을 호출하여 모든 요청의 인코딩을 "UTF-8"로 설정한다.
  • response.setCharacterEncoding("UTF-8")을 호출하여 모든 응답의 인코딩도 "UTF-8"로 설정한다.
  • chain.doFilter(request, response)을 호출하여 다음 필터나 서블릿으로 요청을 전달한다. 이 과정에서 필터 체인이 유지된다.

- destroy 메서드: 필터가 제거될 때 호출되며, 현재는 특별한 자원 해제가 필요하지 않다.

 

- @WebFilter("/*") 애노테이션을 사용하여 이 필터가 모든 요청에 대해 적용되도록 설정하였다.

이제 정상적으로 목록을 출력하는 것을 확인할 수 있다.

'BackEnd > Project' 카테고리의 다른 글

[MVC] Ch07. DI 프레임워크  (0) 2024.08.02
[MVC] Ch06. MVC 프레임워크(4)  (0) 2024.08.01
[MVC] Ch06. MVC 프레임워크(2)  (0) 2024.07.31
[MVC] Ch06. MVC 프레임워크(1)  (0) 2024.07.30
[MVC] Ch05. JDBC 프로그래밍  (0) 2024.07.30