URL 라우팅 — 누가 어디로 보내는가
주소창에 뭔가를 입력했을 때, 서버 안에서 벌어지는 일을 아래 시뮬레이터로 직접 눌러봅시다.
localhost:8081/test/ 뒤에 /, /login, /board 중 하나를 넣어보세요.
시뮬레이터
서버 내부에서 무슨 일이 일어났을까
URL을 바꿔가며 눌러보세요. 2번 노드의 Servlet 이름이 URL마다 달라진다는 점이 핵심입니다.
각 Servlet은 각각 독립된 Java 파일이고, 그 안에서 resp.sendRedirect("xxx.jsp") 로
브라우저에게 "이 JSP 보러 가" 라고 안내합니다.
핵심 개념: URL 하나 = Servlet 하나 = Java 파일 하나
학생이 가장 먼저 오해하는 지점: "/login 을 치면 login.jsp가 바로 열리는 거 아니에요?"
실제로는 그 사이에 Servlet(Java 파일)이 끼어서 "이 주소로 온 요청에 무슨 일을 해줄지" 처리합니다.
이 수업에서 우리는 각 URL마다 담당 Servlet 파일을 따로 만드는 패턴을 씁니다.
/login 은 LoginServlet.java 가, /board 는 BoardServlet.java 가 받습니다.
기능이 늘어나면 src/main/java 아래에 Servlet 파일이 하나씩 추가되는 식이에요.
"그럼 누가 /login 요청은 LoginServlet 으로 가야 한다고 결정하나요?"
— 그건 Tomcat (서블릿 컨테이너) 의 역할입니다. 여러분이 아래처럼
@WebServlet("/login") 어노테이션 한 줄만 붙여두면, Tomcat이 시작될 때 그 매핑 표를 읽어두고
요청이 올 때마다 정해진 Servlet을 자동으로 호출해줍니다.
Servlet은 직접 화면을 안 그린다 — sendRedirect로 JSP에 떠넘긴다
Servlet의 일과 JSP의 일은 다릅니다. Servlet은 "요청 받기, 파라미터 처리, DB 호출"까지 담당하고,
그 다음 화면을 그리는 일은 JSP에게 떠넘깁니다. 그 떠넘기는 방법이 바로
resp.sendRedirect("xxx.jsp") 예요. 의미는 단순합니다: 서버가 브라우저에게
"이 JSP를 다시 요청해" 라고 알려주는 거죠.
LoginServlet.java — /login 전용 파일
// src/main/java/com/edu/controller/LoginServlet.java package com.edu.controller; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.*; @WebServlet("/login") // ← Tomcat에게 "나는 /login 담당이에요" public class LoginServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String userId = req.getParameter("userId"); String pw = req.getParameter("password"); // TODO: DB에서 userId/pw 검증 boolean ok = checkLogin(userId, pw); if (ok) { resp.sendRedirect("main.jsp"); // ← 성공: 메인으로 } else { resp.sendRedirect("login.jsp"); // ← 실패: 로그인 화면 다시 } } }
BoardServlet.java — /board 전용 파일 (별개의 .java 파일!)
// src/main/java/com/edu/controller/BoardServlet.java package com.edu.controller; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.*; @WebServlet("/board") // ← 다른 Servlet은 다른 URL 담당 public class BoardServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // TODO: DB에서 게시글 목록을 session에 담아두기 resp.sendRedirect("board.jsp"); // ← 게시판 화면으로 보냄 } }
파일 구조가 이렇게 쌓입니다:
src/main/java/com/edu/controller/ ├── IndexServlet.java // @WebServlet("/") — 메인 진입 ├── LoginServlet.java // @WebServlet("/login") — 로그인 처리 └── BoardServlet.java // @WebServlet("/board") — 게시판 처리 src/main/webapp/ ├── main.jsp // 메인 화면 ├── login.jsp // 로그인 폼 ├── board.jsp // 게시판 화면 └── WEB-INF/ └── web.xml
WEB-INF/views 안이 아니라 webapp 루트에 그냥 있다는 점을 주목하세요.
sendRedirect 는 "브라우저가 직접 그 URL로 다시 접속" 하는 방식이라, JSP 파일이 외부에서 접근 가능한 위치에 있어야 합니다.
WEB-INF 안 파일은 외부에서 직접 접근이 막혀있어서 sendRedirect 대상이 될 수 없어요.
sendRedirect와 forward — 무엇이 다른가
Servlet에서 JSP로 넘기는 방법은 사실 두 가지가 있습니다. 학생들이 처음 헷갈리는 부분이라 비교해두면 좋아요.
| 구분 | sendRedirect | forward |
|---|---|---|
| 요청 횟수 | 2번 (Servlet 한 번 + JSP 한 번) | 1번 (서버 내부 이동) |
| 주소창 URL | JSP의 URL로 변경됨 | 변경 안 됨 |
| request 데이터 | 전달 안 됨 (날아감) | 그대로 유지 |
| JSP 위치 제약 | 외부 접근 가능 위치 | WEB-INF 안에 둘 수 있음 |
| 주 사용처 | 처리 끝난 후 다른 페이지로 이동 | 모델 데이터를 화면에 그대로 전달 |
이 수업의 학생들 코드는 sendRedirect 위주이므로, request로 데이터를 넘기고 싶을 때는
session에 담거나 URL 파라미터로 붙여서 전달합니다. 예: resp.sendRedirect("main.jsp?msg=welcome").
주의 · @WebServlet("/") 는 "default servlet"
@WebServlet("/") 는 단순히 "/" 만 처리하는 게 아니라,
다른 어떤 Servlet에도 매칭되지 않은 요청까지 모두 떠안는 fallback 역할을 합니다.
그래서 매칭 우선순위는 이렇게 작동해요:
| 요청 URL | 매칭되는 Servlet | 이유 |
|---|---|---|
/login | LoginServlet | 정확 매치 (exact match 최우선) |
/board | BoardServlet | 정확 매치 |
/ | IndexServlet | default servlet |
/unknown | IndexServlet | 매칭 안 된 나머지도 default로 감! |
/ 는 그냥 메인 페이지용이 아니라 '미아 보호소' 역할도 한다."
이걸 모르고 있으면 "왜 오타친 주소도 메인 페이지가 뜨지?" 하면서 한참 디버깅하게 됩니다.
404를 내고 싶으면 IndexServlet의 service 에서 path를 검사해서 resp.sendError(404) 를 직접 호출해야 합니다.
참고 · web.xml 로 매핑하는 예전 방식
어노테이션이 안 되는 환경에선 web.xml 에 직접 써주기도 합니다. 결과는 같아요.
<!-- src/main/webapp/WEB-INF/web.xml --> <servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>com.edu.controller.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping> <servlet> <servlet-name>BoardServlet</servlet-name> <servlet-class>com.edu.controller.BoardServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>BoardServlet</servlet-name> <url-pattern>/board</url-pattern> </servlet-mapping>
LoginServlet → BoardServlet → IndexServlet
으로 바뀌는 걸 주목하게 해주세요. 그게 바로 "각 URL마다 담당 Java 파일이 다르다" 는 증거입니다.
기능 하나 추가 ⇒ Servlet 하나 추가 ⇒ Java 파일 하나 추가. 이 패턴이 학생 프로젝트의 기본 구조입니다.
주소 규칙 요약
| 입력 URL | Controller 결정 | 열리는 JSP |
|---|---|---|
localhost:8081/test/ | index 분기 | index.jsp |
localhost:8081/test/login | login 분기 | login.jsp |
localhost:8081/test/board | board 분기 | board.jsp |
localhost:8081/test/unknown | else → 404 | 에러 페이지 |