서블릿 기술
- 자바(베이스 - JDK) + 웹 구현(추가 - *.jar : 아파치 톰캣)
서블릿은 기존 자바에 웹을 구현한 기술이다.
아파치 톰캣😺
웹 서버의 역할과 자바로 서블릿 또는 JSP를 구현할 수 있는 수많은 *.jar 파일도 제공한다.
서버 측에서 서블릿과 JSP를 동작하게 만드는 역할도 한다.
정적 페이지와 동적 페이지에 대해서는 위 글을 참고한다.
🍁정적 페이지를 방문하는 과정
1. URL 방문
2. 요청 (HTTP Request)
3. 파일 검색
4. 확장자 찾기
5. 소스 읽기
6. 응답 (HTTP Response)
7. 캐시 저장
8. 실행
웹 클라이언트가 Hello.html파일을 달라고 요청(HTTP Request)하면 웹 서버(아파치 톰캣)가 HTML 페이지를 찾는다. 이때 아파치 톰캣은 페이지를 찾고 확장자(html, css, javaScript)를 검색한다. (확장자에 따라서 서버에서 일어나는 일이 달라진다)
HTML을 찾고 나면 그 소스를 읽는다. 이때 아파치 톰캣은 그 페이지에 대한 어떠한 처리도 하지 않고(html, css, javascript에 대해 아무런 조치를 하지 않는다.) 읽은 코드를 클라이언트에 그대로 돌려준다. 이를 HTTP Response라고 한다.
소스 안에 적혀 있는 구문의 실행 주체가 웹 클라이언트(브라우저)이며, 서버(아파치 톰캣)는 이를 인식하지 못한다.
그런데 브라우저는 응답받은 페이지를 바로 읽지 않는다. 서버의 하드 디스크(HDD) 어딘가의 임시 공간(cache)에 페이지의 복사본을 저장하며, 복사된 파일을 브라우저에 로드시켜서 화면에 출력하게 된다.
인터넷을 끊더라도 브라우저가 닫히지 않고 그대로 있는 이유도 내 하드 디스크에 저장이 되어 있어서이다. 웹 브라우저에서 개발자 도구로 속성을 바꿔도 원본이 바뀌지 않는 이유도 복사본을 수정한 것이기 때문이다. 이러한 과정은 파일의 재활용을 가능하게 하므로, 회선이 빨라지고 비용이 줄여준다는 장점이 있다.
🍁동적 페이지를 방문하는 과정
기본 코드
package com.test.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Calendar;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Hi extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
resp.setCharacterEncoding("UTF-8");
PrintWriter writer = resp.getWriter();
Calendar now = Calendar.getInstance();
writer.println("<!DOCTYPE html>"
+ "<html>"
+ "<head>"
+ "<meta charset=\"UTF-8\">"
+ "<title>Insert title here</title>"
+ "</head>"
+ "<body>"
+ " <h1>Hello</h1>"
+ " <p>안녕하세요</p>"
+ " <p id=\"time\">" + String.format("%tT", now)
+ "</p>"
+ "</body>"
+ "</html>");
writer.close();
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Hello</h1>
<p>안녕하세요</p>
<p id="time"></p>
<script>
document.getElementById('time').innerText = (new Date()).toLocaleTimeString();
</script>
</body>
</html>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0">
<display-name>ServletTest</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>ex01</servlet-name>
<servlet-class>com.test.servlet.Ex01</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ex01</servlet-name>
<url-pattern>/ex01.do</url-pattern>
</servlet-mapping>
</web-app>
동적 페이지 방문 과정
1. URL (hi.do)
2. 요청 (HTTP Request)
3. 확인
4. 파일 찾기
5. 컴파일 (javac.exe)
6. 실행 (java.exe)
7. doGet() 호출
8. 동적 페이지 생성
9. 페이지 전달
10. 응답 (HTTP Response)
11. 실행 (캐시 저장)
동적 페이지를 방문하는 과정은 서블릿을 요청했을 때 발생하는 과정을 말한다.
아파치와 톰캣은 다른 프로그램이다. '아파치'는 웹 서버이고, '톰캣'은 웹 응용 프로그램 서버이다. 톰캣은 프로그램을 실행하는 환경을 제공한다고 해서 서블릿 컨테이너 혹은 응용 프로그램 서버(WAS_Web Application Server)라고 부르기도 한다.
웹 클라이언트에서 hi.do의 URL을 요청한다. hi.do는 정적 페이지가 아니기 때문에 아파치에서 처리할 수 없다. 그래서 웹 응용 프로그램 서버인 톰캣에게 일을 넘긴다. 그러면 URL을 찾아 Hello.java 컴파일을 톰캣이 하고, Hello.java 실행 파일을 실행(java.exe)한다.
위 파일에는 java, html, css, javaScript가 들어있다. 이중에 서버는 java를 담당하고, 나머지 html, css, javaScript를 실행할 수 있는 브라우저에 돌려준다.
이런 구조를 만들게 된 이유는 뭘까?
만약 자바 코드를 클라이언트에서 다운로드 받아서 java를 실행하려면 사용자가 해당 웹 페이지를 이용하려면 java를 설치해야 하기 때문이다. 그런데 서버는 한 대이기 때문에 여기서 java를 처리한다면 서버만 설치하면 되기 때문에 물리적으로 이 방법이 가장 무난하다. 그래서 브라우저가 감당할 수 있는 코드만 돌려주는 것이다.
똑같은 페이지 재요청
1. URL (hi.do)
2. 요청 (HTTP Request)
3. 확인
4. doGet() 호출
5. 동적 페이지 생성
6. 페이지 전달
7. 응답 (HTTP Response)
8. 실행 (캐시 저장)
소스가 바뀌지 않았기 때문에 4번 파일을 찾는 부분에 차이가 있다. 파일을 찾은 것을 메모리에 저장하기 때문에 파일을 찾지도 않고, 컴파일하지도 않고, 실행하지도 않는다. 그리고 doGet 메서드를 호출하는 것부터 동일하다. 서블릿은 처음 실행할 때만 느리다. 그리고 두 번째 실행하는 것부터 단계가 생략되기 때문에 빨라진다.
🍁데이터 전송 및 수신
- Send: 데이터를 입력하고 전송하는 페이지
- Receive: 데이터를 수신하는 프로그램을 가지고 있는 결과 페이지
데이터를 입력하고 전송하여 데이터를 수신하는 작업을 해보도록 하자.
쉽게 말해서 데이터를 입력하고 전송하는 서블릿과 데이터를 수신하는 서블릿을 만들어야 한다.
거의 모든 페이지는 위와 같이 입력하는 페이지와 수신하는 페이지로 구성된다.
입력 및 전송 페이지
package com.test.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Ex02_Send extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
resp.setCharacterEncoding("UTF-8");
PrintWriter writer = resp.getWriter();
writer.println("<!DOCTYPE html>"
+ "<html>"
+ "<head>"
+ "<meta charset=\"UTF-8\">"
+ "<title>Insert title here</title>"
+ "</head>"
+ "<body>"
+ " <h1>데이터 전송</h1>"
+ " "
+ " <form method=\"GET\" action='/servlet/receive.do'> <!-- action: 데이터를 수신할 프로그램 URL -->"
+ " <div>"
+ " 이름: <input type=\"text\" name=\"name\">"
+ " </div>"
+ " <div>"
+ " 나이: <input type=\"text\" name=\"age\">"
+ " </div>"
+ " <input type=\"submit\" value=\"보내기\">"
+ " </form>"
+ "</body>"
+ "</html>");
writer.close();
}
}
writer.close()로 닫아줘야 서블릿이 실제 물리적인 페이지(임시 페이지)를 생성한다.
수신 페이지
package com.test.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Ex02_Receive extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
String name = req.getParameter("name");
String age = req.getParameter("age");
System.out.println(name);
System.out.println(age);
resp.setCharacterEncoding("UTF-8");
PrintWriter writer = resp.getWriter();
writer.println("<!DOCTYPE html>"
+ "<html>"
+ "<head>"
+ "<meta charset=\"UTF-8\">"
+ "<title>Insert title here</title>"
+ "</head>"
+ "<body>"
+ " <h1>데이터 수신</h1>"
+ " "
+ " <p>데이터 처리가 완료되었습니다.</p>"
+ "</body>"
+ "</html>");
writer.close();
}
}
데이터를 수신하여 입력한 Isaac과 25를 콘솔에 출력한다.
파라미터
Send에서 이름과 나이를 입력하면 Receive로 이름과 나이로 입력한 데이터가 수신 데이터로 넘어오는데, 이때 중요한 것은 두 개의 데이터 중에 어떤 게 이름인지, 어떤 게 나이인지 구분할 수가 없다는 것이다.
여기에 name을 붙이면 name=Isaac, age=25로 데이터가 넘어가므로 서버에서 데이터를 구분할 수가 있게 된다.
String name = req.getParameter("name");
String age = req.getParameter("age");
넘긴 데이터 하나를 파라미터(Parameter)라고 한다. getParameter 메서드는 이 파라미터를 하나씩 받아올 수 있게 해 준다. 여기에 넘긴 name을 적어주면 해당 데이터를 받아올 수 있으며, 넘겨진 데이터는 무조건 문자열이다.
Query String
http://localhost:8090/servlet/receive.do?name=Isaac&age=25
get 방식으로 전송되는 '?'뒤의 데이터들을 묶어서 Query String라고 한다.
이러한 데이터는 외부로 노출되므로 민감한 정보는 get 방식으로 전달하면 안 된다.
get 방식과 post 방식에 대해서는 위 글을 참고한다.
<form method=\"POST\" action='/servlet/receive.do'>
위 소스코드에서 GET을 POST로 바꾸면 에러가 발생한다.
HTTP 상태 405번은 "HTTP 메서드인 POST는 이 URL에 의해 지원되지 않습니다."라는 메시지를 출력한다.
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
서블릿은 요청 메서드가 있다고 했다. doGet과 doPost라는 요청 메서드가 있는데, GET 방식을 요청할 때에는 doGet으로, POST 방식으로 요청할 때에는 doPost를 사용한다.서로 호환이 되지 않으며, 반드시 쌍이 맞아야 작동하기 때문에 이름을 바꿔 주어야 한다.
http://localhost:8090/servlet/receive.do
이제 에러가 발생하지 않고 POST 방식으로 데이터를 받아오게 된다.