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

1. GET
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);
}
2. 헤더 값 전부 가져오기
- 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);
}
}
- QueryString (URL의 ?이후 부분)
- GET 요청은 ‘뭔가를 찾아줘’라는 의미
- queryString는 구체적 질의를 하기 위해서 필요하다. (⇒ sql로 보내지는 것)
- body 부분에 담겨서 오는 게 아니라 주소에 담겨서 온다.
- 주소에 붙이는 데이터는 db sql문의 where에 걸리는 부분과 같다.
- key=value&key=value 형태
- getQueryString() : URL에서 ? 이후의 부분인 쿼리 문자열을 반환한다.
- getParameter() : http 요청에서 parameter의 값을 가져온다. 즉, 인수로 받은 key의 value를 반환한다.
- 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);
}
2. POST
1. 버퍼로 body 전부 읽는 방법
- req.getReader()를 통해 요청 버퍼에 접근한다.
- 버퍼로부터 데이터를 읽어올 때 /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 형태로 데이터 전송 시


- x-www-from-urlencoded로 key와 value를 입력하는 경우


2. parameter로 key, value만 읽어오는 방법
- x-www-from-urlencoded : raw 타입으로 데이터 전송 시 중괄호나 쌍따옴표 등 파싱이 필요한데 미리 파싱을 해서 key=value&key=value 형태로 전달해 준다. (내가 먼저 key=value형태로 해줄게~!)
- key=value형태는 req.getParameter()로 전부 pasing 가능하다.
- 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);
}


3. client로부터 입력받아서 서버로 데이터 전송 받기
- 입력받은 값을 하나씩 보내는 게 아니라 전체를 보내기 위해서 <form></form> 태그 사용
- form 태그 action 속성 : form의 데이터가 전송되는 url
- form 태그 method 속성 : 전송 방식
- form 태그 enctype 속성 : form 데이터를 서버로 전송할 때 인코딩 타입 지정
- input 태그 name 속성 : 데이터의 key값을 지정
- button 태그 type 속성 submit를 통해 form 태그 안의 안의 모든 정보 전송

<!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>
name 속성을 지정하지 않은 경우


전체코드
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. 템플릿 엔진
- 템플릿 엔진이란?
- 자바 코드에 html 코드가 들어있는 것보다 html 코드에 자바 코드가 섞어서 쓰는 것이 더 편리하다
- html 등과 같이 정적인 템플릿에 java 코드와 같이 동적인 데이터를 삽입함으로써 동적인 웹 페이지 구현이 가능하다.
- 서버 사이드 템플릿 엔진
- 서버 측에서 html을 생성한 후 클라이언트 측으로 전송하여 클라이언트는 그대로 화면에 표시
- jsp, Thymeleaf 등
- jsp 코드
- <% %>내부에 java 코드 작성
- <%=%>에서 ‘=’는 출력하라는 의미
<%@ 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>

- 동작 과정
- Apache(WS)가 이해 못하는 파일(.jsp)을 Tomcat(WAS)에게 위임한다.
- WAS는 request 객체와 response 객체를 만든다.
- 그 다음으로 WAS는 home.jsp를 눈에는 안보이지만 내부적으로 서블릿 파일(homeservlet.java)로 전환되는 것과 같이 동작한다.

2. CV패턴 (mvc패턴)
jsp 파일이 여러 개 일 경우 클라이언트 측에서 각각의 jsp 파일로 접근할 경우 단일 진입점이 없어 공통 로직을 처리하지 못하고 재사용이 불가하다.
⇒ 따라서 공통 로직을 처리해줄 부분이 필요하다.
- Dispatcher Servlet
- CV 패턴
- MVC 패턴
- 단점

프로젝트 구조

코드
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>






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 {
}
}
}

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

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 내부에서는 찾을 수 있음 (안->안(가능))
}
}
}

파일 찾는건 불가능해짐 = URL 패턴 불가 URI 패턴으로 바뀌는 것

3. ViewResolver
viewResolver 만드는 방식은 회사마다 다르다!
아래의 코드와 같은 방식 이외에 resolve(path)가 this return해서 거기서 .forward() 하는 코드~? 등 여러 방식으로 가능 (e.g. rs.resolve(path).forward(req,resp))
1. ViewResolver
- view를 찾아주는 해결사 viewResolver
- WEB-INF는 보안폴더로 서버가 해당 폴더 안의 파일을 찾지 못하기 때문에 자바의 private 변수와 비슷하게 노출 안 할 수 있다.
- 따라서 외부에서 파일을 direct로 못찾으므로 Controller 즉, 단일 진입점이 강제된다.
- 공통 로직을 안 타고 갈 까봐 강제성을 부여하기 위해서 이처럼 설계한다.
- 이 경우 WEB-INF/views/a.jsp에서 a를 제외한 부분이 계속 중복되므로 귀찮으니까 viewResolver를 만들어서 처리한다.
프로젝트 구조

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. 결과
- a.jsp와 b.jsp로 direct하게 접근 불가능하다.


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


4. 결정체@@최종진화@@@@@
1.
프로젝트 구조

이전과 동일한 코드 (@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 {
}



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";
}
}

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


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