[Spring] Servlet

이지연·2026년 1월 28일

서블릿(Servlet)은 “HTTP 요청/응답을 처리하기 위한 저수준 표준 인터페이스”이고, 스프링의 @Controller는 내부적으로 서블릿 기반 위에서 더 편하게 라우팅/바인딩/응답 생성을 할 수 있도록 추상화한 계층이다.

Servlet이 왜 필요했나

웹 서버에서 클라이언트 요청이 들어오면 결국 해야 할 일은 정해져 있다: 요청(Request)을 읽고, 응답(Response)을 만들어서 반환하는 것. 서블릿이 없었다면 개발자가 이 과정을 저수준에서 직접 조립해야 해서, 비즈니스 로직 외적인 코드(파싱, 인코딩, 응답 헤더/바디 구성 등)가 과도하게 늘어나는 문제가 생긴다.

서블릿(Servlet)은 HttpServletRequest, HttpServletResponse라는 표준 객체를 통해 HTTP 요청/응답을 다루는 공통 규격을 제공하고, 개발자는 핵심 로직에 더 집중할 수 있게 된다. 그리고 이후 등장한 Spring MVC의 Controller는 “서블릿을 기반으로” 더 많은 편의 기능을 얹어준 형태라고 보면 된다.

서블릿 동작 흐름(요청 → 매핑 → doGet/doPost)

서블릿 기반의 요청 흐름은 다음처럼 이해하면 된다.

  • 클라이언트가 특정 URL로 요청을 보낸다.
  • WAS가 URL 매핑 정보를 보고 “어떤 서블릿 클래스가 처리할지” 결정한다.
  • HTTP 메서드에 따라 doGet(), doPost() 같은 메서드가 실행된다.
  • 개발자는 HttpServletRequest로 요청 데이터를 꺼내고, HttpServletResponse로 응답을 구성한다.

여기서 핵심은, 서블릿은 메서드 단위가 아니라 클래스 단위로 URL 매핑을 잡는다는 점이다.

GET 서블릿 예제(JSON 응답까지 직접 조립)

아래 코드는 /servlet/get으로 들어오는 GET 요청을 처리한다. 예를 들어 http://localhost:8080/servlet/get?id=1로 요청하면 req.getParameter("id")로 쿼리 파라미터를 꺼내고, 응답은 JSON으로 내려준다.

포인트는 “응답을 개발자가 직접 세팅”한다는 것이다.

  • resp.setContentType("application/json")
  • resp.setCharacterEncoding("UTF-8")
  • resp.getWriter()로 바디를 직접 작성
  • ObjectMapper로 객체를 JSON 문자열로 변환
@Slf4j
@WebServlet("/servlet/get")
public class ServletReqGet extends HttpServlet {
    private final ObjectMapper objectMapper;

    @Autowired
    public ServletReqGet(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {

        String id = req.getParameter("id");
        log.info(id);

        Member member = new Member("hong1", "hong1@naver.com");

        resp.setContentType("application/json");
        resp.setCharacterEncoding("UTF-8");

        PrintWriter printWriter = resp.getWriter();
        printWriter.print(objectMapper.writeValueAsString(member));
        printWriter.flush();
    }
}

이 예제는 “컨트롤러가 해주던 일”을 서블릿에서 직접 처리하는 감각을 익히기에 좋다. Spring MVC에서는 이런 작업을 @ResponseBodyResponseEntity, 메시지 컨버터 등이 대부분 대신 처리해준다.

POST 서블릿 예제(x-www-form-urlencoded 처리)

POST 요청의 body가 application/x-www-form-urlencoded인 경우에도 서블릿에서는 req.getParameter()로 값을 꺼낼 수 있다. 예제에서는 name, email을 받고, 응답은 단순 텍스트 "OK"로 내려준다.

@WebServlet("/servlet/post")
public class ServletReqPost extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {

        String name = req.getParameter("name");
        String email = req.getParameter("email");

        System.out.println(name);
        System.out.println(email);

        resp.setContentType("text/plain");
        resp.setCharacterEncoding("UTF-8");

        PrintWriter printWriter = resp.getWriter();
        printWriter.print("OK");
        printWriter.flush();
    }
}

여기서도 마찬가지로 응답 헤더/인코딩/바디를 직접 조립한다는 점이 서블릿의 특징이다.

@WebServlet을 스프링부트에서 쓰려면(@ServletComponentScan)

서블릿 3.0 이후부터는 web.xml 대신 @WebServlet 같은 애노테이션으로도 서블릿 등록이 가능하다. 다만 스프링 부트 프로젝트에서 @WebServlet을 자동 등록하려면, 서블릿 컴포넌트를 스캔하도록 설정이 필요하다.

그래서 메인 클래스에 @ServletComponentScan을 붙여 @WebServlet 기반 구성요소를 스캔/등록한다.

@SpringBootApplication
@EnableScheduling
@ServletComponentScan
public class BasicApplication {
    public static void main(String[] args) {
       SpringApplication.run(BasicApplication.class, args);
    }
}

여기서 @EnableScheduling은 “스케줄러 기능 활성화” 용도고, 서블릿과는 별개의 관심사다. 즉, 이 프로젝트는 “서블릿 실습도 하고 + 스케줄링도 켜둔 상태”라고 이해하면 된다.

WebServlet vs Controller 차이(핵심만)

서블릿과 컨트롤러는 경쟁 관계라기보다 “기반 기술과 추상화 계층” 관계로 보는 게 정확하다.

  • @WebServlet
    • URL 매핑이 클래스 단위 중심
    • HttpServletRequest/Response를 직접 다루며, 응답도 직접 조립하는 코드가 많아짐
  • @Controller / @RestController
    • 클래스뿐 아니라 메서드 단위로도 URL 매핑 가능(@GetMapping, @PostMapping)
    • 파라미터 바인딩, JSON 직렬화/역직렬화, 예외 처리 등 스프링이 많은 부분을 자동화

정리하면, 서블릿은 Spring MVC/REST의 근간이고, 컨트롤러는 그 위에서 생산성을 높여주는 고수준 API다. 서블릿을 이해하면 “스프링이 내부에서 뭘 대신해주는지”가 더 명확해져서, 디버깅이나 성능/동작 원리 이해에도 도움이 된다.

profile
Eazy하게

0개의 댓글