서블릿 요청 흐름 : Request가 채워지는 시점과 service() 호출 위치

2026. 2. 5. 16:23·Backend/Servlet

 

오늘 헷갈린 지점은

  • 내 HelloServlet#doGet() 안에서는 resp만 쓰고 req는 안 썼다.
  • 그런데도 GET 요청이 잘 처리된다.
  • 그럼 req는 대체 어디서 쓰인 건가
  • “톰캣이 만든다/전달한다” 같은 추상 얘기 말고, 코드 레벨로 어디서 쓰였는지가 궁금하다.

결론부터 박는다.

req는 드에서 안 써도, 이미 Tomcat/Servlet API 내부에서 최소 1번은 무조건 쓰인다.
그 지점이 바로 HttpServlet#service(req, resp) 안의 req.getMethod()다.
그래서 GET이면 doGet()으로 들어올 수 있고, 그 안에서 네가 req를 안 읽어도 “Hello World”만 쓰면 되니 잘 돌아간다.

 

 

 

“req 없이도” 동작하는 이유

public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServerException, IOException {
        PrintWriter out = resp.getWriter();
        out.print("Hello World!");
    }
}
 

이 코드가 하는 일은 단 하나다.

  • 응답 본문에 "Hello World!"를 쓴다.

여기엔 요청 URL, 쿼리, 헤더, 파라미터가 전혀 필요 없다.
그러니까 req.getParameter() 같은 걸 “꺼낼 필요”가 없다.

하지만 “코드에서 필요 없었다”와 “Tomcat 내부에서 아예 안 쓰였다”는 다르다.

 

 

req가 정확히 어디서 쓰였나: 핵심 한 줄

req가 “직접 안 꺼내도” 쓰이는 대표 지점은 여기다.

  • javax.servlet.http.HttpServlet#service(HttpServletRequest req, HttpServletResponse resp)
    • 여기서 req.getMethod() 호출
    • "GET"이면 doGet(req, resp) 호출
    • "POST"면 doPost(req, resp) 호출

즉, doGet()까지 들어온 순간,

Tomcat/Servlet 내부가 이미 req.getMethod()를 읽어서 doGet()으로 분기한 결과다.

그래서 “req 안 읽었는데?”라고 느끼는 건 맞고,
그렇다고 req가 안 쓰인 건 아니다. 내부에서 이미 최소 1번 쓰였다.

 

 

“그럼 request는 언제 만들어지고 값은 언제 채워지나?”

(1) Tomcat이 소켓에서 HTTP 요청을 받는다

  • TCP 소켓에서 바이트를 읽는다
  • 요청 라인/헤더/바디를 파싱한다 (GET /hello HTTP/1.1 같은 거)

(2) 그 파싱 결과를 “요청 객체”에 채운다

  • 메서드, URI, 헤더, 파라미터 등이 요청 객체에 저장된다
  • 이 객체가 서블릿 입장에서 보이는 HttpServletRequest(= req)다
    (실제로는 내부 구현체가 있고, 서블릿에 전달될 땐 파사드 형태로 보이는 경우가 많다.)

(3) 그 다음에 서블릿 호출로 넘어간다

  • “어떤 URL이 어떤 서블릿이냐” 매핑을 찾는다
  • 필터 체인을 거친다
  • 최종적으로 servlet.service(req, resp)가 호출된다

즉 결론:

req는 service()보다 먼저 만들어지고, 먼저 값이 채워진다.
service()는 “이미 채워진 req”를 받아서 처리 흐름을 분기한다.

 

 

 

req는 톰캣이 쓰고 resp는 사용자가 쓰는 건가?

계속 꼬였던 이유는, 이게 “둘 중 하나”가 아니라 둘 다이기 때문이다.

정확히 말하면:

  • req는 Tomcat 내부에서도 쓰이고, 사용자 코드에서도 쓰인다
  • resp도 Tomcat 내부에서도 쓰이고, 사용자 코드에서도 쓰인다

다만 보통 체감이 이렇게 갈린다.

  • 사용자 코드는 응답을 만들어야 하니까 resp는 거의 항상 쓴다
  • 반면 요청값이 필요 없는 단순 응답이면 req는 안 쓸 수도 있다

그래서 나는 resp만 쓰는 서블릿을 만들었고, 그게 정상인 거다.

 

 

“내가 req를 안 썼는데도” req가 내부에서 쓰인 포인트들

1) 반드시 쓰이는 포인트: req.getMethod() (분기)

  • HttpServlet#service() 안에서 실행됨
  • "GET"인지 "POST"인지 판단해야 doGet/doPost를 부를 수 있으니 필수다

2) URL 매핑 단계에서도 사실상 쓰인다 (경로 정보)

  • Tomcat은 “어떤 서블릿이 이 요청을 처리할지” 결정해야 한다
  • 이때 요청의 URI/경로 정보를 보고 매핑한다
  • 즉 req에 들어있는 경로 정보는 내부에서 “매핑 판단”에 이미 활용된다

단, 내가 직접 req.getRequestURI()를 호출하지 않았다는 건
“그 값을 네 코드가 꺼내지 않았다”는 의미일 뿐, 내부에서 참조될 수 있다.

 

 

왜 doGet()만 오버라이드하면 되나

Tomcat 내부에서 service()가 분기한다는 걸 이해하면 답이 바로 나온다.

  • 나는 doGet()을 오버라이드했다
  • 내부 service()가 GET 요청을 보고 doGet()을 호출한다
  • 그래서 내 코드가 실행된다

반대로,

  • GET 요청인데 doPost()만 오버라이드하면
  • service()는 doGet()을 호출하려고 하는데
  • 내가 doGet()을 안 바꿨으면 기본 구현이 실행되고 보통 405 같은 결과로 이어질 수 있다

 

 

정리

req는 “요청 정보를 담는 객체”이고, 그 정보는 네가 안 꺼내도 Tomcat/Servlet 내부가 최소한 메서드 분기(req.getMethod()) 같은 용도로 먼저 꺼내 쓴다.

그 후 doGet()에 들어오면, 그때부터는 네가 필요할 때만 req에서 값을 더 꺼내 쓰면 된다.
resp도 마찬가지로 내부/사용자 모두 쓰지만, 사용자는 보통 응답을 만들어야 해서 resp를 항상 만지는 편이다.

 

 

 

이해 확인용: req를 진짜 써보기

req가 “요청값을 꺼내는 통로”라는 것을 직접 이해

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    String uri = req.getRequestURI();
    String method = req.getMethod();

    resp.setContentType("text/plain; charset=UTF-8");
    PrintWriter out = resp.getWriter();
    out.println("method=" + method);
    out.println("uri=" + uri);
}

여기서 req.getMethod()는

  • 내부 service()에서도 이미 썼던 값이고,
  • 내가 여기서도 다시 꺼내 쓰는 것뿐이다.

'Backend > Servlet' 카테고리의 다른 글

forward와 JSP 실행 흐름  (0) 2026.02.06
멀티스레드 환경에서 Servlet 사용  (0) 2026.02.06
Servlet Container와 Servlet의 관계  (0) 2026.02.05
RequestMapping static Map은 괜찮은 이유  (0) 2026.02.05
서블릿에서의 static  (0) 2026.02.05
'Backend/Servlet' 카테고리의 다른 글
  • 멀티스레드 환경에서 Servlet 사용
  • Servlet Container와 Servlet의 관계
  • RequestMapping static Map은 괜찮은 이유
  • 서블릿에서의 static
sqaxe1
sqaxe1
woojoo-devlog 님의 블로그 입니다.
  • sqaxe1
    Woojoo's Devlog
    sqaxe1
  • 전체
    오늘
    어제
    • 분류 전체보기 (148)
      • Backend (9)
        • Servlet (7)
        • Spring (2)
      • Frontend (1)
      • CS (0)
      • Book (33)
        • 자바 웹 프로그래밍 Next Step (30)
        • 테스트 주도 개발: 고품질 쾌속개발을 위한 TDD.. (1)
        • 성공과 실패를 결정하는 1%의 네트워크 원리 (2)
      • Engineering (0)
        • Testing (0)
      • Infra (6)
        • AWS (6)
      • Java (4)
      • Network (1)
      • 김영한 (28)
        • 자바 입문 (8)
        • 실전 자바 - 기본편 (6)
        • 실전 자바 - 중급편 (10)
        • 실전 자바 - 고급편 (4)
      • Web (39)
        • Web Basics (39)
      • Project (24)
        • NeoSquare (0)
        • Memo Evolution (24)
      • 정보처리기사 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    aws
    개발서적
    java
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
sqaxe1
서블릿 요청 흐름 : Request가 채워지는 시점과 service() 호출 위치
상단으로

티스토리툴바