HttpServletRequest
객체(줄여서 req
객체)는 클라이언트(웹 브라우저)가 서버로 보낸 요청 정보를 담고 있는 객체WAS가 만들어 준다
1. req header에 있는 정보 확인
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Enumeration<String> headerNames = req.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
String headerValue = req.getHeader(headerName);
System.out.println(headerName + ": " + headerValue);
}
}

- req header에 있는 다양한 정보를 확인 할 수 있다
header에 key값으로 찾을 수 있다
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String userAgent = req.getHeader("User-Agent");
System.out.println(userAgent);
String host = req.getHeader("Host");
System.out.println(host);
}

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String path = req.getRequestURI();
System.out.println("path: " + path);
String method = req.getMethod();
System.out.println("method: " + method);
String queryString = req.getQueryString(); // 구체적 질의 -> sql, where절에 걸리는 놈
System.out.println("queryString: " + queryString);
String username = req.getParameter("username");
System.out.println("username: " + username);
String password = req.getParameter("password");
System.out.println("password: " + password);
}


queryString
→ 클라이언트가 보낸 구체적인 질의, SQL에서 where절에 들어갈 질의- ? → 로 시작한다
- key=value 형태다
- & → 로 개수를 구분한다
2. req body에 있는 정보 확인

- postman으로 body 데이터를 text로 작성함
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String body = "";
BufferedReader br = req.getReader();
while (true) {
String line = br.readLine();
if (line == null) break;
body += line;
}
System.out.println("body: " + body);
}

- body 데이터는 리더 버퍼에 접근해야 얻을 수 있음
- 버퍼에 있는 내용을 1줄씩 가져와서 합침


- json데이터를 던져 주어도 서버에서 읽기만 하면 문자열로 읽는 것
- 서버에서 저 문자열을 파싱해야만 저 데이터를 DB에 저장 가능
- 하지만 이미 WAS에서 파싱이 되어 있음

- 클라이언트측에서
x-www-form-urlencoded
형으로 데이터를 보내면
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println("username: " + username);
System.out.println("password: " + password);
}

- req객체 안에 있는 메서드를 사용해 파싱된 값을 찾을 수 있다
3. jsp파일을 요청 했을 때 일어나는 일


➡
➡

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<title>JSP - Hello World</title>
</head>
<body>
<h1>
Home Page
</h1>
<br/>
<%
String name = "홍길동";
%>
<%=name%>
</body>
</html>
- 이 파일을 WAS 톰켓이
home.jsp
→HomeServlet.java
로 잠깐 만들어준다
package org.example.demo7;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/home.jsp")
public class HomeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("Content-Type", "text/html");
String name = "홍길동";
String body = """
<html>
<head>
<title>JSP - Hello World</title>
</head>
<body>
<h1>
Home Page
</h1>
<br/>
${name}
</body>
</html>
""".replace("${name}", name);
PrintWriter out = resp.getWriter();
out.println(body);
}
}
home.jsp
파일을 요청하면 → 톰켓이 이 파일을 Servlet파일로 만들어@WebServlet("/home.jsp")
로 깃발을 붙이고 자바 코드로 만들어 실행한다

- 이렇게 jsp 템플릿 파일을 사용하면 html에 자바코드를 넣어 개발 할 수 있게 되어 좀더 편하게 개발할 수 있다
- 이렇게 편한 방법이 있는데 단점은 ‘공통 로직’을 구현할 수 없다는 것 이다
- 각각의 jsp파일을 요청하면 각각의 servlet파일이 만들어지고 실행되기 때문
- 그래서 다음과 같은 방식이 나타났다
4. MVC 패턴
- M : Model
- V : View
- C : Controller

- 각각의 jsp파일에 공통 로직과 DB연결을 한번에 하기 위해 만들어진 방식
- 앞 단의
dispatcher
가 경로도 찾아주고 공통 로직도 구현할 수 있다

package org.example.demo8;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
// localhost:8080/hello.do?path=a
@WebServlet("*.do")
public class FrontController extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("common logic");
String path = req.getParameter("path");
if (path.equals("a")) {
req.getRequestDispatcher("a.jsp").forward(req, resp);
} else if (path.equals("b")) {
req.getRequestDispatcher("b.jsp").forward(req, resp);
} else if (path.equals("c")) {
req.getRequestDispatcher("c.jsp").forward(req, resp);
} else {
}
}
}
- 모든 요청을 이
controller
가 받고 공통 로직과 경로 설정도 할 수 있다 /123.do?path=a
를 요청하면a.jsp
파일을 찾아 실행 결과를 준다
- 하지만 아직 이 코드에선
a.jsp
파일로 바로 가능 방법을 막을 수 없다 /a.jsp
를 요청하면 바로a.jsp
파일이 실행된다
5. 공통 로직으로 Request 객체에 데이터를 담아서 보내는 법
package org.example.demo8;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
// localhost:8080/hello.do?path=a
@WebServlet("*.do")
public class FrontController extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("common logic");
String path = req.getParameter("path");
String name = "ssar";
if (path.equals("a")) {
req.setAttribute("name", name); // hashmap으로 name=ssar, Object타입을 받는 이유 new의 제어권이 없기 때문 -> 제네릭으로 생성 불가
req.getRequestDispatcher("a.jsp").forward(req, resp);
} else if (path.equals("b")) {
req.setAttribute("name", name);
req.getRequestDispatcher("b.jsp").forward(req, resp);
} else if (path.equals("c")) {
req.setAttribute("name", name);
req.getRequestDispatcher("c.jsp").forward(req, resp);
} else {
}
}
}
req.setAttribute()
를 사용하면 req객체에 데이터를 담을 수 있다
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>A view</h1>
<%
String name = (String) request.getAttribute("name");
%>
<h3>값 : <%=name%>
</h3>
</body>
</html>
- a.jsp에서 req 객체에 접근하는 방법
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>B view</h1>
<h3>
name : ${name}
</h3>
</body>
</html>
- b.jsp에서 req객체 내부의 데이터에 접근하는 방법
6. a.jsp 파일 직접 접근 막는 법

- jsp 파일을 WEB-INF 폴더 안으로 이동
- WEB-INF 폴더는 외부에서 접근 불가
package org.example.demo9;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/") // "localhost:8080/" 를 포함하는 모든 경로, ex) "/a" or "/a/v" or "/"
public class DemoServlet extends HttpServlet {
// http://localhost:8080/?path=a
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// body -> path=값&name=값
// queryString -> /?path=값&name=값
String path = req.getParameter("path");
if (path.equals("a")) {
req.setAttribute("name", "ssar");
req.getRequestDispatcher("/WEB-INF/a.jsp").forward(req, resp);
}
}
}
- WEB_INF 폴더를 서버에선 접근 가능
- 파일에 접근 할 때 “/WEB-INF/a.jsp”로 접근
7. WEB_INF 폴더를 이용한 라우팅 처리 방법

package org.example.demo9;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
// http://localhost:8080/
@WebServlet("/") // 모든 요청이 여기로 몰린다. ex) /, /abc, /a/b/c
public class DemoServlet extends HttpServlet {
// http://localhost:8080?path=a
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// body -> path=값&name=값
// queryString -> ?path=값&name=값
String path = req.getParameter("path");
if (path.equals("a")) {
req.setAttribute("name", "ssar");
req.getRequestDispatcher(ViewResolver.viewName("a")).forward(req, resp);
} else if (path.equals("b")) {
req.setAttribute("age", 20);
req.getRequestDispatcher(ViewResolver.viewName("b")).forward(req, resp);
}
}
}
package org.example.demo9;
public class ViewResolver {
private static final String prefix = "/WEB-INF/views/";
private static final String suffix = ".jsp";
public static String viewName(String filename) {
return prefix + filename + suffix;
}
}
- 매번 경로를 작성하기 싫어서, 경로를 작성하는 클래스와 메서드를 만들고 사용
8. 리플렉션을 이용한 라우팅 처리 방법

Dispatcher
package org.example.demo10;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.example.demo10.core.ComponentScan;
import org.example.demo10.core.RequestMapping;
import org.example.demo10.core.ViewResolver;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Set;
@WebServlet("*.do")
public class DispatcherServlet extends HttpServlet {
private Set<Object> controllers;
@Override
public void init(ServletConfig config) throws ServletException {
// 1. 컴포넌트 스캔
ComponentScan componentScan = new ComponentScan(config.getServletContext());
controllers = componentScan.scanClass("org.example.demo10.controller");
// System.out.println(controllers.size());
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// localhost:8080/user.do?path=join
String path = req.getParameter("path");
// 2. 라우팅
String templatePath = route(path);
// 3. 리다이렉션
if (templatePath.contains("redirect:")) {
String redirectPath = templatePath.replace("redirect:", "");
// resp.setStatus(302);
// resp.setHeader("Location", "?path=" + redirectPath);
resp.sendRedirect("?path=" + redirectPath);
return;
}
// 4. 이동
if (templatePath == null) {
resp.setStatus(404);
resp.getWriter().println("<h1>404 Not Found</h1>");
} else {
req.getRequestDispatcher(ViewResolver.viewName(templatePath)).forward(req, resp);
}
}
private String route(String path) {
for (Object instance : controllers) {
Method[] methods = instance.getClass().getMethods();
for (Method method : methods) {
RequestMapping rm = method.getAnnotation(RequestMapping.class);
if (rm == null) continue; // 다음 for문으로 바로 넘어감
if (rm.value().equals(path)) {
try {
return (String) method.invoke(instance);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
return null;
}
}
ComponentScan
package org.example.demo10.core;
import jakarta.servlet.ServletContext;
import java.io.File;
import java.util.HashSet;
import java.util.Set;
public class ComponentScan {
private final ServletContext servletContext;
public ComponentScan(ServletContext servletContext) {
this.servletContext = servletContext;
}
// 클래스를 스캔하는 메소드
public Set<Object> scanClass(String pkg) {
Set<Object> instances = new HashSet<>();
try {
// 톰캣의 webapps 폴더 내 WEB-INF/classes 경로 가져오기
String classPath = servletContext.getRealPath("/WEB-INF/classes/");
// C:\Program Files\Apache Software Foundation\Tomcat 11.0\webapps\ROOT\WEB-INF\classes\
File slashDir = new File(classPath);
File dotToSlashDir = new File(slashDir, pkg.replace(".", File.separator));
for (File file : dotToSlashDir.listFiles()) {
// System.out.println(file.getName());
String className = pkg + "." + file.getName().replace(".class", "");
// System.out.println(className);
try {
Class cls = Class.forName(className);
if (cls.isAnnotationPresent(Controller.class)) {
// System.out.println("Controller 어노테이션");
Object instance = cls.getDeclaredConstructor().newInstance();
instances.add(instance);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return instances;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
ViewResolver
package org.example.demo10.core;
public class ViewResolver {
private static final String prefix = "/WEB-INF/views/";
private static final String suffix = ".jsp";
public static String viewName(String filename) {
return prefix + filename + suffix;
}
}
Controller 어노테이션
package org.example.demo10.core;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Controller {
}
RequestMapping 어노테이션
package org.example.demo10.core;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequestMapping {
String value();
}
UserController
package org.example.demo10.controller;
import org.example.demo10.core.Controller;
import org.example.demo10.core.RequestMapping;
@Controller
public class UserController {
@RequestMapping("join")
public String join() {
System.out.println("UserController join");
return "join";
}
@RequestMapping("login")
public String login() {
System.out.println("UserController login");
return "login";
}
}
Dispatcher가 “*.do” 로 요청을 받으면 실행 new가 실행된다.
- Dispatcher의 init이 실행된다.
- "
org.example.demo10.controller
”경로에 있는 클래스들 중에서@Controller
어노테이션이 있는 클래스들을 찾는다.controllers
를 set 컬렉션에 저장한다
- doGet 호출
- 요청 경로를 ?뒤 값, parameter 로 찾은 다음 분석한다.
- route 호출
- 분석된 경로에 해당하는 어노테이션을 가지고 있는 메서드를 호출
- 해당 메서드가 리턴한 값을 받는다
- 그 값에 해당하는 파일을 찾아 응답한다
Share article