[SB] 3. request 객체

최재원's avatar
Mar 19, 2025
[SB] 3. request 객체
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); } }
notion image
  • 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); }
notion image
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); }
notion image
notion image
  • queryString → 클라이언트가 보낸 구체적인 질의, SQL에서 where절에 들어갈 질의
    • ? → 로 시작한다
    • key=value 형태다
    • & → 로 개수를 구분한다

 

2. req body에 있는 정보 확인

notion image
  • 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); }
notion image
  • body 데이터는 리더 버퍼에 접근해야 얻을 수 있음
  • 버퍼에 있는 내용을 1줄씩 가져와서 합침

 
notion image
notion image
  • json데이터를 던져 주어도 서버에서 읽기만 하면 문자열로 읽는 것
  • 서버에서 저 문자열을 파싱해야만 저 데이터를 DB에 저장 가능
  • 하지만 이미 WAS에서 파싱이 되어 있음
notion image
  • 클라이언트측에서 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); }
notion image
  • req객체 안에 있는 메서드를 사용해 파싱된 값을 찾을 수 있다
 

3. jsp파일을 요청 했을 때 일어나는 일

notion image
notion image
 
 
notion image
<%@ 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.jspHomeServlet.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") 로 깃발을 붙이고 자바 코드로 만들어 실행한다
    • notion image
  • 이렇게 jsp 템플릿 파일을 사용하면 html에 자바코드를 넣어 개발 할 수 있게 되어 좀더 편하게 개발할 수 있다
  • 이렇게 편한 방법이 있는데 단점은 ‘공통 로직’을 구현할 수 없다는 것 이다
  • 각각의 jsp파일을 요청하면 각각의 servlet파일이 만들어지고 실행되기 때문
  • 그래서 다음과 같은 방식이 나타났다

4. MVC 패턴

  • M : Model
  • V : View
  • C : Controller
notion image
  • 각각의 jsp파일에 공통 로직과 DB연결을 한번에 하기 위해 만들어진 방식
  • 앞 단의 dispatcher가 경로도 찾아주고 공통 로직도 구현할 수 있다
notion image
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 파일 직접 접근 막는 법

notion image
  • 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 폴더를 이용한 라우팅 처리 방법

notion image
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. 리플렉션을 이용한 라우팅 처리 방법

notion image

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가 실행된다.
  1. Dispatcher의 init이 실행된다.
    1. "org.example.demo10.controller”경로에 있는 클래스들 중에서 @Controller 어노테이션이 있는 클래스들을 찾는다.
    2. controllers set 컬렉션에 저장한다
  1. doGet 호출
    1. 요청 경로를 ?뒤 값, parameter 로 찾은 다음 분석한다.
  1. route 호출
    1. 분석된 경로에 해당하는 어노테이션을 가지고 있는 메서드를 호출
    2. 해당 메서드가 리턴한 값을 받는다
    3. 그 값에 해당하는 파일을 찾아 응답한다
 
Share article

jjack1