서블릿은 “클래스”만 만들어서 끝나는 구조가 아니다. 클라이언트(브라우저)가 요청할 URL이 있어야 하고, 서버는 그 URL에 해당하는 서블릿 클래스를 찾아 실행할 수 있어야 한다.
예를 들어 아래처럼 HelloServlet을 만들었다고 하자.
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 같은 주소로 요청을 보내야 한다. 그러려면 “이 URL은 이 서블릿이 처리한다” 라는 매핑이 필요하다.
URL 매핑: @WebServlet("/hello")
서블릿 매핑을 하는 대표적인 방법이 어노테이션 기반 매핑이다.
@WebServlet("/hello")
public class HelloServlet extends HttpServlet { ... }
이렇게 하면 일반적으로 아래처럼 요청한다.
- http://localhost:8080/<컨텍스트경로>/hello
그런데 /hello로 요청하면 404가 뜬다
@WebServlet("/hello")를 붙였는데도 404(Not Found) 가 뜨는 상황이 생긴다.
이때 원인은 “매핑이 틀려서”가 아니라, 톰캣이 서블릿 클래스를 아예 발견하지 못했기 때문인 경우가 많다.
핵심은 이거다.
- @WebServlet("/hello")는 “자바 소스 파일”을 스캔하는 기능이 아니다.
- 톰캣은 배포된 웹앱의 클래스 출력 위치(클래스 로딩 경로) 에 존재하는 .class 파일을 기준으로 서블릿을 로딩하고 스캔한다.
- 즉, 웹앱 내부의 WEB-INF/classes(또는 라이브러리 WEB-INF/lib)에 서블릿 클래스가 있어야 톰캣이 그 클래스를 로딩하고 @WebServlet을 인식할 수 있다.
왜 톰캣이 HelloServlet을 못 찾는가?
VS Code에서 자바 프로젝트를 구성하면, 기본 설정상 컴파일 결과 .class가 웹앱 배포 구조와는 별개 위치로 떨어질 수 있다.
즉,
- src/HelloServlet.java는 존재하지만
- 컴파일된 HelloServlet.class가 webapp/WEB-INF/classes에 배치되지 않는다
이러면 톰캣 입장에서는 웹앱 안에 해당 클래스가 없으니,
- /hello 매핑 자체가 등록되지 않고
- 결과적으로 /hello는 존재하지 않는 경로가 되어 404가 뜬다
정리하면:
404의 진짜 이유는 @WebServlet("/hello")가 동작하지 않아서가 아니라,
톰캣이 스캔할 위치에 .class가 배치되지 않아 서블릿 자체를 로딩 못해서이다.
해결: 컴파일 출력 경로를 webapp/WEB-INF/classes로 지정한다
해결 방향은 명확하다.
- 컴파일 산출물(.class)을 톰캣이 인식하는 위치로 넣어야 한다
- 표준적인 위치가 webapp/WEB-INF/classes이다
그래서 VS Code 설정에서 자바 컴파일 출력 경로를 다음처럼 지정한다.
{
"java.project.referencedLibraries": ["lib/**/*.jar"],
"java.project.sourcePaths": ["src"],
"java.project.outputPath": "webapp/WEB-INF/classes"
}
이 설정을 적용하면,
- src의 소스가 컴파일되고
- 결과 .class가 webapp/WEB-INF/classes로 떨어지면서
- 톰캣이 웹앱을 로딩할 때 해당 클래스를 발견하고
- @WebServlet("/hello")가 정상 스캔되어
- /hello 요청이 404가 아니라 실제 서블릿으로 연결된다
설정 항목 의미 정리
java.project.referencedLibraries
외부 JAR 라이브러리 경로를 의미한다.
여기서는 lib/ 아래의 모든 .jar 파일을 클래스패스에 포함한다.
- "lib/**/*.jar" → lib 폴더 하위의 jar 전부 포함
java.project.sourcePaths
소스 코드(.java)가 있는 폴더를 지정한다.
- "src" → 자바 소스는 src에 있다고 알려준다
java.project.outputPath
컴파일된 클래스(.class)가 저장될 폴더를 지정한다.
- "webapp/WEB-INF/classes" → 컴파일 결과를 톰캣이 스캔하는 위치로 출력한다
즉, 이 설정 전체를 한 문장으로 요약하면 다음이다.
“소스는 src에서 읽고, 외부 JAR은 lib에서 쓰고, 컴파일 결과는 webapp/WEB-INF/classes에 넣어라” 이다.

이렇게 나오는 것을 볼 수 있
그 이유는
- /hello가 404 뜸
- 이유: @WebServlet("/hello")는 웹앱 내부 WEB-INF/classes에 있는 클래스만 스캔됨
그런데 클래스는 src에서 컴파일되어 프로젝트 내부 어디에도 배치되지 않음 - 그래서 톰캣이 HelloServlet을 찾지 못해 404가 뜬 것
- 이유: @WebServlet("/hello")는 웹앱 내부 WEB-INF/classes에 있는 클래스만 스캔됨
- 해결 방향
- 출력 경로를 webapp/WEB-INF/classes로 지정하면
톰캣이 클래스를 스캔해서 @WebServlet이 동작함 - 이를 위해 VS Code 설정에 아래를 넣어야 함:
- 출력 경로를 webapp/WEB-INF/classes로 지정하면
{
"java.project.referencedLibraries": ["lib/**/*.jar"],
"java.project.sourcePaths": ["src"],
"java.project.outputPath": "webapp/WEB-INF/classes"
}
- java.project.referencedLibraries
- 외부 JAR 라이브러리 경로
- 여기서는 lib/ 아래 모든 .jar를 클래스패스에 포함
- java.project.sourcePaths
- 소스 코드가 있는 폴더
- src 폴더 안에 자바 파일이 있다고 알려주는 설정
- java.project.outputPath
- 컴파일된 .class 파일이 저장될 폴더
- 여기서는 webapp/WEB-INF/classes에 저장하라고 지정
즉, 이 설정은
“소스는 src에서 읽고, 외부 JAR은 lib에서 쓰고, 결과 클래스는 webapp/WEB-INF/classes에 넣어라”
출처 : 《자바 웹 프로그래밍 Next Step》, 박재성, 로드북
'Book > 자바 웹 프로그래밍 Next Step' 카테고리의 다른 글
| 6장_ : ListUSerServlet 구현 (0) | 2026.02.06 |
|---|---|
| 5장_ : 서블릿 컨테이너가 하는 일: 생명주기와 인스턴스 생성 (0) | 2026.02.05 |
| 5장_: 임베디드 톰캣으로 웹 서버 띄우기 (0) | 2026.02.05 |
| 3장_ 로깅: System.out.println() 대신 로깅을 써야 하는 이유 (0) | 2026.02.02 |
| 3장_ 요구사항 4: 302 status code 적용 (0) | 2026.02.01 |
