inblog logo
|
jay0628
    SpringBoot

    [Spring Boot] 5. Request 객체

    김주희's avatar
    김주희
    Mar 17, 2025
    [Spring Boot] 5. Request 객체
    Contents
    0. Servlet 생성1. GET2. POST3. CV패턴 (MVC패턴)25. 03.171. FrontController를 통한 공통로직 처리2. WEB-INF3. ViewResolver4. 결정체@@최종진화@@@@@
    필기
    get : 뭔가를 찾아줘
    주소에 붙이는 데이터 = db sql문의 where에 걸린다. queryString = 구체적 질의를 하기 위해서 필요
    client가 준 정보 req로 접근하면 알 수 있음
    x-www ~ : 파싱 내가 먼저 key=value형태로 해줄게 key=value형태는 req.getParameter()로 전부 pasing 가능
     
     
    jsp = 템플릿 엔진 자바코드를 섞어서 쓸 수있다 <% %> 안에 자바코드
    아파치가 이해못하는 파일 톰캣에게 home.jsp를 서블릿 파일(homeservlet.java)로 변경
    home.jsp로 요청이 오면 톰캣이 내부적으로 HomeServlet.java를 만듦(눈에는 안보이지만)
    <%=name%>에서 =는 출력하라는 의미
     
    302(redirection)는 req,resp 두 개 만듦
    dispatcherS가 공통로직 처리하고 jsp 처리
    mvc 패턴 req,resp 새로 만드는게 아니라 forward로 전달 단점)hello.do?path=a 말고도 /a.jsp가 됨-> 공통 로직 실행X -> 강제성이 없음 ->webapp이 바깥에 공개되어있어서 => 회사마다 비공개 처리 방식이 다름 =>Spring이 해결
     

    0. Servlet 생성

    1. servlet class를 만든 후 그 클래스가 HttpServlet 클래스를 상속하도록 한다.
    1. @WebServlet을 지정해준다.
      1. @WebServlet → 패키지를 스캔해서 @WebServlet을 가지고 있는 클래스를 찾아서 괄호 안의 주소를 스캔한다.
    1. HttpServlet의 메서드를 override한다 ⇒ doGet, doPost, doPut, doDelete 메서드 생성
      1. notion image

    1. GET

    1. 헤더 값 한 개씩 가져오기

    1. getHeader() : 클라이언트가 서버로 보낸 http 요청, request 객체로부터 특정 헤더 값을 가져온다.
    @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String userAgent = req.getHeader("User-Agent"); System.out.println("User-Agent: " + userAgent); String host = req.getHeader("Host"); System.out.println("Host: " + host); }
    notion image
     

    2. 헤더 값 전부 가져오기

    1. getHeaderNames() : 클라이언트가 서버로 보낸 request의 헤더들의 이름을 모두 가져온다.
    @Override 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
     
    1. QueryString (URL의 ?이후 부분)
      1. GET 요청은 ‘뭔가를 찾아줘’라는 의미
      2. queryString는 구체적 질의를 하기 위해서 필요하다. (⇒ sql로 보내지는 것)
      3. body 부분에 담겨서 오는 게 아니라 주소에 담겨서 온다.
      4. 주소에 붙이는 데이터는 db sql문의 where에 걸리는 부분과 같다.
      5. key=value&key=value 형태
      6. getQueryString() : URL에서 ? 이후의 부분인 쿼리 문자열을 반환한다.
      7. getParameter() : http 요청에서 parameter의 값을 가져온다. 즉, 인수로 받은 key의 value를 반환한다.
        1. e.g. getParameter("username") : username=ssar라는 문자열을 찾아서 ssar 반환한다.
    // localhost:8080/demo.do?username=ssar&password=1234 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //requset header에 없던 부분(general?)을 따로 만들어줌 (진짜 중요!!!) String path = req.getRequestURI(); System.out.println("path: " + path); String method = req.getHeader("method"); System.out.println("method: " + method); // ? 이하 = queryString // 구체적 요청을 할때 붙임 // body에 담기는 게 아니라 주소에 담겨서 String queryString = req.getQueryString(); // 구체적 질의 = sql(에게 보내는 것) 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
     
     

    2. POST

    1. 버퍼로 body 전부 읽는 방법

    1. req.getReader()를 통해 요청 버퍼에 접근한다.
    1. 버퍼로부터 데이터를 읽어올 때 /n까지 읽고 난 후 버퍼 소비로 인해 없어지므로 따로 저장해둬야 된다. (body = body + line)
    // insert @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String body = ""; BufferedReader br = req.getReader(); while (true) { // 버퍼 소비 (/n까지 읽고 난 후 없어짐 -> 따로 저장해둬야 됨) String line = br.readLine(); if (line == null) break; body += line; } System.out.println("body: " + body); }
     
    • raw - JSON 형태로 데이터 전송 시
    notion image
    notion image
     
    • x-www-from-urlencoded로 key와 value를 입력하는 경우
    notion image
    notion image
     
     

    2. parameter로 key, value만 읽어오는 방법

    1. x-www-from-urlencoded : raw 타입으로 데이터 전송 시 중괄호나 쌍따옴표 등 파싱이 필요한데 미리 파싱을 해서 key=value&key=value 형태로 전달해 준다. (내가 먼저 key=value형태로 해줄게~!)
    1. key=value형태는 req.getParameter()로 전부 pasing 가능하다.
    1. req.getParameter()를 통해 key가 username인 value값과 key가 password인 value값을 찾을 수 있다.
    // insert @Override 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
    notion image
     

    3. client로부터 입력받아서 서버로 데이터 전송 받기

    • 입력받은 값을 하나씩 보내는 게 아니라 전체를 보내기 위해서 <form></form> 태그 사용
    • form 태그 action 속성 : form의 데이터가 전송되는 url
    • form 태그 method 속성 : 전송 방식
    • form 태그 enctype 속성 : form 데이터를 서버로 전송할 때 인코딩 타입 지정
    • input 태그 name 속성 : 데이터의 key값을 지정
    • button 태그 type 속성 submit를 통해 form 태그 안의 안의 모든 정보 전송
     
    notion image
    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <form action="http://localhost:8080/good.do" method="post" enctype="application/x-www-form-urlencoded"> <input type="text" placeholder="Enter username" name="username"> <input type="text" placeholder="Enter username" name="password"> <button type = "submit">전송</button> </form> </body> </html>
    notion image
     
    name 속성을 지정하지 않은 경우
    notion image
    notion image
     

    전체코드

    package org.example.demo6; 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("*.do") public class DemoServlet extends HttpServlet { // select // localhost:8080/demo.do?username=ssar&password=1234 @Override 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); // } //requset header에 없던 부분(general?)을 따로 만들어줌 (진짜 중요!!!) String path = req.getRequestURI(); System.out.println("path: " + path); String method = req.getHeader("method"); System.out.println("method: " + method); // ? 이하 = queryString // 구체적 요청을 할때 붙임 // body에 담기는 게 아니라 주소에 담겨서 String queryString = req.getQueryString(); // 구체적 질의 = sql(에게 보내는 것) 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); } // insert @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // String body = ""; // // BufferedReader br = req.getReader(); // // while (true) { // // 버퍼 소비 (/n까지 읽고 난 후 없어짐 -> 따로 저장해둬야 됨) // String line = br.readLine(); // // if (line == null) break; // // body += line; // } // System.out.println("body: " + body); String username = req.getParameter("username"); String password = req.getParameter("password"); System.out.println("username: " + username); System.out.println("password: " + password); } }
     
    필기
    302(redirection)는 req,resp 두 개 만듦
    dispatcherS가 공통로직 처리하고 jsp 처리
    mvc 패턴 req,resp 새로 만드는게 아니라 forward로 전달 단점)hello.do?path=a 말고도 /a.jsp가 됨-> 공통 로직 실행X -> 강제성이 없음 ->webapp이 바깥에 공개되어있어서 => 회사마다 비공개 처리 방식이 다름 =>Spring이 해결

    3. CV패턴 (MVC패턴)

    1. 템플릿 엔진

    1. 템플릿 엔진이란?
      1. 자바 코드에 html 코드가 들어있는 것보다 html 코드에 자바 코드가 섞어서 쓰는 것이 더 편리하다
      2. html 등과 같이 정적인 템플릿에 java 코드와 같이 동적인 데이터를 삽입함으로써 동적인 웹 페이지 구현이 가능하다.
      3. 서버 사이드 템플릿 엔진
        1. 서버 측에서 html을 생성한 후 클라이언트 측으로 전송하여 클라이언트는 그대로 화면에 표시
        2. jsp, Thymeleaf 등
    1. jsp 코드
      1. <% %>내부에 java 코드 작성
      2. <%=%>에서 ‘=’는 출력하라는 의미
    <%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> <!DOCTYPE html> <html> <head> <title>Title</title> </head> <body> <h1>Home Page</h1> <hr> <% String name = "홍길동"; %> <%=name%> </body> </html>
    notion image
    notion image
     
    1. 동작 과정
      1. Apache(WS)가 이해 못하는 파일(.jsp)을 Tomcat(WAS)에게 위임한다.
      2. WAS는 request 객체와 response 객체를 만든다.
      3. 그 다음으로 WAS는 home.jsp를 눈에는 안보이지만 내부적으로 서블릿 파일(homeservlet.java)로 전환되는 것과 같이 동작한다.
    notion image

    2. CV패턴 (mvc패턴)

    jsp 파일이 여러 개 일 경우 클라이언트 측에서 각각의 jsp 파일로 접근할 경우 단일 진입점이 없어 공통 로직을 처리하지 못하고 재사용이 불가하다.
    ⇒ 따라서 공통 로직을 처리해줄 부분이 필요하다.
    1. Dispatcher Servlet
    1. CV 패턴
    1. MVC 패턴
    1. 단점
    notion image
    프로젝트 구조
    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 { } } }
    <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h1>A view</h1> </body> </html>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h1>B view</h1> </body> </html>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h1>C view</h1> </body> </html>
    notion image
    notion image
    notion image
    notion image
    notion image
    notion image
     
     

    25. 03.17

    필기
    // set = 상태에 값을 넣는다. // Attribute 클래스의 상태값 속성 key - string type / value - Object type // new 제어권 서블릿톰캣에게 있음 -> Object type (나에게 제어권있으면 제네릭/new 다운캐스팅해서 꺼낼 수 있지만) // forward = get 요청
    view를 찾아주는 해결사 만들거임 왜? 귀찬아서 뭐가? 뭐가 귀찬은지 찾아볼거임 view resolver
    서버가 대상 리소스를 찾지 못한다 -> 자바의 private 변수와 비슷하게 노출안할 수 있음 controller가 강제된다 = 단일진입점이 강제된다. ->왜? 외부에서 파일을 direct로 못찾으니까
    왜 이렇게 설계? 강제성 -> 왜 강제성? 공통 로직을 안타고 갈까봐
    WEB-INF/a.jsp 앞에 / 붙어도 webapp 안붙어도 webapp
    WEB-INF/views/a.jsp에서 a를 제외한 부분이 계속 ㅜ중복->귀찮다
     

    1. FrontController를 통한 공통로직 처리

    1. 지난 수업 복습(?

     
    controller의 req와 resp 를 a.jsp에게 전달해서 사용한다
    즉 req에 name 담아서 a.jsp가 name이 담긴 req를 전달받았음을 증명 실습
     

    2. 코드

    <%@ 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>
    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"; // 이걸 a.jsp에 전달 if (path.equals("a")) { req.setAttribute("name", name); // set = 상태에 값을 넣는다. // Attribute 클래스의 상태값 속성 key - string type / value - Object type // new 제어권 서블릿톰캣에게 있음 -> Object type (나에게 제어권있으면 제네릭/new 다운캐스팅해서 꺼낼 수 있지만) req.getRequestDispatcher("a.jsp").forward(req, resp); // forward = get 요청 } 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 { } } }
    notion image
    notion image
     
    <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h1>B view</h1> <h3>${name}</h3> </body> </html>
    notion image
     

    2. WEB-INF

    1. WEB-INF

    1. 보안 폴더
    1. 외부에 노출되지 않는다 (→ 톰캣이 접근을 막는다)
    1. “WEB-INF/a.jsp” 앞에 “/”가 붙어도(”/WEB-INF/a.jsp”) webapp 폴더를 가리키고, 안 붙어도(”WEB-INF/a.jsp”) webapp폴더를 가리킨다.
    1. jsp 파일을 WEB-INF 폴더 안에 넣음으로써 a.jsp에 대한 direct 접근이 불가능해지고 controller를 거쳐 공통 로직을 처리한 뒤 a.jsp로 접근 가능하다.
    파일구조
    notion image

    2. 코드

    <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h1>a.jsp</h1> <h3>내 이름은 : ${name}</h3> </body> </html>
    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; // 톰캣이 demo9 패키지를 component scan 해서 servlet new한다. // http://localhost:8080/ @WebServlet("/") // 모든 요청이 여기로 몰린다. ex) /, /a, /b, /abc public class DemoServlet extends HttpServlet { // http://localhost:8080?path=a(/생략) @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // body -> path=값&name=값 (post와 put만 body가 있음) // queryString -> ?path=값&name=값 // getParameter는 body와 queryString 둘 다 parsing String path = req.getParameter("path"); if (path.equals("a")) { req.setAttribute("name", "ssar"); req.getRequestDispatcher("WEB-INF/a.jsp").forward(req, resp); // Tomcat 내부에서는 찾을 수 있음 (안->안(가능)) } } }
    notion image
     
    ❗
    파일 찾는건 불가능해짐 = URL 패턴 불가 URI 패턴으로 바뀌는 것
    notion image

    3. ViewResolver

    ❗
    viewResolver 만드는 방식은 회사마다 다르다!
    아래의 코드와 같은 방식 이외에 resolve(path)가 this return해서 거기서 .forward() 하는 코드~? 등 여러 방식으로 가능 (e.g. rs.resolve(path).forward(req,resp))

    1. ViewResolver

    1. view를 찾아주는 해결사 viewResolver
    1. WEB-INF는 보안폴더로 서버가 해당 폴더 안의 파일을 찾지 못하기 때문에 자바의 private 변수와 비슷하게 노출 안 할 수 있다.
    1. 따라서 외부에서 파일을 direct로 못찾으므로 Controller 즉, 단일 진입점이 강제된다.
    1. 공통 로직을 안 타고 갈 까봐 강제성을 부여하기 위해서 이처럼 설계한다.
    1. 이 경우 WEB-INF/views/a.jsp에서 a를 제외한 부분이 계속 중복되므로 귀찮으니까 viewResolver를 만들어서 처리한다.
     
    프로젝트 구조
    notion image

    2. 코드

    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; // 톰캣이 demo9 패키지를 component scan 해서 servlet new한다. // http://localhost:8080/ @WebServlet("/") // 모든 요청이 여기로 몰린다. ex) /, /a, /b, /abc 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=값 // getParameter는 body와 queryString 둘 다 parsing 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; } }
    <%-- Created by IntelliJ IDEA. User: GGG Date: 2025-03-17 Time: 오전 10:56 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h1>b.jsp</h1> <h3>나이 : ${age}</h3> </body> </html>
     

    3. 결과

    1. a.jsp와 b.jsp로 direct하게 접근 불가능하다.
      1. notion image
        notion image
         
    1. a.jsp와 b.jsp가 DemoServlet(Controller)으로부터 req와 resp를 전달받아서 사용하기 때문에 DemoServlet에서 setAttribute를 통해 처리된, 즉 req에 담긴 공통 로직에 대한 데이터를 가져올 수 있다.
      1. notion image
        notion image
     

    4. 결정체@@최종진화@@@@@

    1.

    프로젝트 구조
    notion image
    이전과 동일한 코드 (@RequestMapping, @Controller, ViewResolver)
    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(); }
    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 { }
    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; } }
     
     
    DispatcherServlet
    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 == 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); } } }
     
    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"; } }
     
    BoardController
    package org.example.demo10.controller; import org.example.demo10.core.Controller; @Controller public class BoardController { }
     
    notion image
    notion image
    notion image
    notion image
     
     

    2.

    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:", ""); // home만 남는걸 302로 돌려줄거임 // resp.setStatus(302); // resp.setHeader("Location", "?path=" + redirectPath); resp.sendRedirect("?path=" + redirectPath); // 위의 두 코드 내장 return; // 4번으로 내려갈 필요 없으니까 종료 } // 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; } }
    package org.example.demo10.controller; import org.example.demo10.core.Controller; import org.example.demo10.core.RequestMapping; @Controller public class BoardController { @RequestMapping("home") public String home() { return "home"; } }
    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 "redirect:home"; } }
     
    notion image
    notion image
     

    3. spring으로 만들면 얼마나 쉽게요~

    notion image
     
    notion image
     
    package org.example.hello.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @Controller public class UserController { // localhost:8080/join // body : username=ssar // header : Content-Type : application/x-www-form-urlencoded // DB에 insert 해줘 // 줄게 -> 회원가입 정보를 // @RequestParam("username") 생략 가능 @PostMapping("/join") public String join(String username) { // 1. DB insert System.out.println("username : " + username); // 2. 응답 return "redirect:/home"; } // join-form page 줘 // 줘 -> 회원가입 페이지 @GetMapping("/join-form") public String joinForm() { return "join-form"; // html? mustache? 카멜 표기법 사용X } }
     
    Usercontroller + request
    package org.example.hello.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @Controller public class UserController { // localhost:8080/join // body : username=ssar // header : Content-Type : application/x-www-form-urlencoded // DB에 insert 해줘 // 줄게 -> 회원가입 정보를 // @RequestParam("username") 생략 가능 @PostMapping("/join") public String join(String username, Model model) { // 1. DB insert System.out.println("username : " + username); // 2. 응답 model.addAttribute("username", username); // home에서 못찾는다. (request 2개 만들어지니까 model = request 객체) return "redirect:/home"; } // join-form page 줘 // 줘 -> 회원가입 페이지 @GetMapping("/join-form") public String joinForm() { return "join-form"; // html? mustache? 카멜 표기법 사용X } }
     
    package org.example.hello.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @Controller public class BoardController { // localhost:8080/detail (GET) detail.mustache @GetMapping("detail") public String detail() { return "detail"; } // RequestMapping 을 쓰면 value와 method 둘다 줘야해서 귀찮다 // context path 설정 안해도 됨 -> / // 내장 톰캣 있음 // localhost:8080/home @GetMapping("/home") public String home() { return "home"; // dispatcherServlet이 home이라는 파일 찾는다. } }
     
    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>회원가입 페이지 - 폼태그, 인풋</h1> <form action="/join" method="post"> <!-- method="post"이면 body 데이터로 들어감 / get이면 주소에 --> <input type="text" name="username" placeholder="Enter username"> <button action="submit">회원가입</button> </form> </body> </html>
    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>홈 페이지</h1> </body> </html>
    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>detail page</h1> </body> </html>
    Share article

    jay0628

    RSS·Powered by Inblog