2023.03.19 Servlet
Servlet
29. Cookie를 이용해 상태값 유지하기
session, application은 데이터의 특성에 따라서 저장하면된다
그런데 만약 사물함에 무언가를 두고 다닐수도잇지만 이것을 가지고 다니는 사람도 있다.
클라이언트가 가지고 있다가 가지고 올 수 있는데 이 값을 '쿠키'라고 한다.
그렇다면 이렇게 왓다갓다해도 값이 저장이되는가? 된다.
그래서 값을 저장하는 방식이 세가지인데 서버쪽에 저장하는 application과 session, 클라이언트쪽에 저장하는 cookie가 있는 것이다.
이 3가지 저장소를 사용하면 각각의 특징, 장단점이 있다. 이것들을 알고있어야 어디에 둘것인지를 정할 수 있다.
클라이언트가 서버에 데이터를 요청할때 값을 가져갈 수 있는데 크게 3가지 값이 있다.
브라우저가 알아서 담아주는 Header정보와 사용자가 보내는 사용자 데이터 정보가 있다.
사용자 데이터는 getParameter("x"), 헤더정보는 getHeader("remote-host")를 통해 가져온다.
그리고 여기에 쿠키도 심어진다. 이 정보는 getCookies();로 쿠키를 꺼내사용할 수 잇다.
다시 보낼때는 addCookie()로 보내주고 클라이언트가 받아서 저장한다.
클라이언트에게 쿠키를 만들겠다면 키와 값으로 addCookie()로 보내게 된다.
Cookie cookie = new Cookie("c" String.valueOf(result));
response.addCookie(cookie);
만약 서버가 쿠키가 잇었지하고 쿠키를 보내오게 된다.
서버에서는 쿠키를 배열로 받게 된다.
Cookie[] cookies = requeset.getCooikes();
String _c = "";
for문을 돌려서 내가 원하는 그 쿠키값에 해당되는 밸류값을 찾아야한다.
if(cookies != null){
for(Cookie cookie : cookies)
if("c".equals(cookie.getName())
_c = cookie.getValue();
사용자에게 값을 위임햇다가 불러올때 가져오게 할 수 있다.
쿠키는 반드시 url에 사용할 수 있는 문자형으로 보내야한다.
브라우저 - 설정 -사이트설정에서 쿠키를 허용해줘야한다. 보통은 허용되어잇다.
모든 사이트 데이터 및 권한 보기하면 사이트들이 보냇던 쿠키들을 볼수도잇다.
쿠키는 이렇게 찾아야하는 불편함이 있긴 하지만 사용자 브라우저에 값을 저장할 수 있다.
그런데 방법이 3가지나 있는데 어떻게 써야하나? 유지하는 방법을 보고 적절하게 사용할 수 있어야한다는 것이다.
@WebServlet("/calc2")
public class Calc2 extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html; charset=UTF-8");
Cookie[] cookies = request.getCookies();
String v_ = request.getParameter("v");
String op = request.getParameter("operator");
int v = 0;
if (!v_.equals("")) {
v = Integer.parseInt(v_);
}
if (op.equals("=")) {
int x = 0;
String operator = "";
for (Cookie c : cookies) {
if (c.getName().equals("value")) {
x = Integer.parseInt(c.getValue());
break;
}
}
for (Cookie c : cookies) {
if (c.getName().equals("op")) {
operator = c.getValue();
break;
}
}
int result = 0;
int y = v;
if (operator.equals("+")) {
result = x + y;
} else if (operator.equals("-")) {
result = x - y;
}
response.getWriter().printf("result is: %d", result);
} else {
Cookie valueCookie = new Cookie("value", String.valueOf(v));
Cookie opCookie = new Cookie("op", op);
response.addCookie(valueCookie);
response.addCookie(opCookie);
}
}
}30. Cookie의 path 옵션
쿠키를 사용할때 2가지를 꼭생각해야한다.
1.서블릿을 여러개 만들건데 서블릿마다 그 값을 저장할게 있어서 쿠키를 저장하려고한다.
클라이언트가 쿠키를 보내는데 서블릿마다 쿠키가 달라야한다. 그래서 쿠키를 보낼때 url을 설정할수 있다.
비효율적으로 여러가지 값이나 이름이 충돌하는 것이 없도록 할 수 있다.
valueCookie.setPath("/");
/한개: 모든 url, /notice: 특정 url에서 요청할 경우 /notice/: 특정디렉토리 하위까지 포함.
개발자도구에서 response header를 보면 설정된 쿠키를 볼 수잇다.
requeset header를 보면 쿠키를 보내는 것을 볼 수잇다.
이렇게 경로에따라 쿠키를 보낼 수 있다.
Cookie valueCookie = new Cookie("value", String.valueOf(v));
Cookie opCookie = new Cookie("op", op);
valueCookie.setPath("/calc2");
opCookie.setPath("/calc2");
response.addCookie(valueCookie);
response.addCookie(opCookie);31. Cookie의 maxAge 옵션
쿠키는 설정하지 않으면 브라우저가 닫힐때 쿠키도 없어진다.
그러나 쿠키의 장점은 브라우저가 닫혀도 기간내에는 유지하는 것이다.
보통 쿠키는 IN-MEMORY에 있다가 기간이있다면 IN-FILE(내부파일) 다른 쿠키파일에 저장되어 존재하게 된다.
쿠키이름.setMaxAge(초);
60초를 기준으로 60분이면 60*60 여기에 24시간이면 24*60*60의 연산식으로 보기 쉽게 나타내는 것이 좋다.
여기서 설정하는 것은 만료날짜이다. 쿠키를 보내면 설정한 시간만큼 쿠키가 살아있게 되는 것이다.
이 만료날짜라는 것은 이것에 따라 쿠키를 사용할 수 있다 없다가 정해지기 때문에 중요하다
32. Application/Session/Cookie 정리
32.1 application
사용범위 : 전역범위에서 사용하는 저장공간
생명주기 : WAS가 시작해서 종료할 때 까지
저장위치 : WAS 서버의 메모리
서버쪽에다 두고 전역적으로 사용할 수 있을 것같다면 여기에 저장
32.2 Session
사용범위 : 세션 범위에서 사용하는 저장공간
생명주기 : 세션이 시작해서 종료할 때 까지
저장위치 : WAS 서버의 메모리
32.3 Cookie
사용범위 : Web Browser별 지정한 path 범주 공간
생명주기 : Browser에 전달한 시간부터 만료시간까지
저장위치 : Web Browser의 메모리 또는 파일
서버에 부담을 주지않는 데이터 구조이다. Path라는 특정 url에 대해서만 사용할 수 있어서 서버와 관계없이 클라이언트가 데이터를 가질 수잇다.
1.내가 만약 데이터를 저장하려고한다. 그런데 이 값을 오래 저장하고 싶다.
-> 쿠키에 저장하는게 좋다. 이런걸 세션으로 하면 서버자원이 너무 낭비된다.
2.notice와 관련된 데이터를 가지고 있다 이 데이터를 다른 서블릿에서는 사용할일이 없다.
-> 특정범위, 특정 url에서만 사용하면 쿠키에서 저장해두고 쓰는게 좋다.
33. 서버에서 페이지 전환해주기(redirection)
굉장히 간단하지만 굉장히 많이 사용한다.
calc2에서 post되서 보내면 servlet calc2에서 아무것도 돌려주지않아 백지가 된다.
다시계산을 할때 뒤로 돌아가서 다시 요청을 했다.
그러나 서버측에서 백지를 주지않고 원래 페이지를 다시 보여줄 수 있다.
이것을 redirection이라고 한다.
response.sendRedirect("calc2.html");
사용자는 html을 요청한적이 없는데 이것을 다시 요청한것처럼 보인다.
34. 동적인 페이지(서버 페이지)의 필요성
보통의 계산기 프로그램을 보면 계산기 눌럿던게 누적이 되면서 더해진다.
사용자가 입력한 내용을 서버에서 페이지를 만들때 끼워넣어야한다.
그런데 html은 정적인 페이지라 저장된 값을 어떻게 보여줄것인가? 그래서 동적인 페이지로 변환해줘야한다.
쿠키를 읽어서 출력해주려면 서블릿으로 동적으로 만들어주면된다.
순수한 html이 아닌 서블릿으로 만든 출력한 문서를 만들어서 동적으로 보내주면 된다.
서블릿이 동적으로 그냥 calc.html을 불러오는게 아니라 동적으로 만들고 쿠키를 심어서 보내줘야한다.
원래 있는것이 아니라 서버에서 만들어서 주는 것이라 서버 문서, 동적인 문서라고 한다.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style>
input {
width: 50px;
height: 50px;
}
.output {
hegiht: 50px;
background: #e9e9e9;
font-size: 24px;
font-weight: bold;
text-align: right;
padding: 0px 5px;
}
</style>
<form action="calc3" method="post">
<table>
<tr>
<td class="output" colspan="4">0</td>
</tr>
<tr>
<td><input type="submit" name="operator" value="CE" /></td>
<td><input type="submit" name="operator" value="C" /></td>
<td><input type="submit" name="operator" value="BS" /></td>
<td><input type="submit" name="operator" value="÷" /></td>
</tr>
<tr>
<td><input type="submit" name="value" value="7" /></td>
<td><input type="submit" name="value" value="8" /></td>
<td><input type="submit" name="value" value="9" /></td>
<td><input type="submit" name="operator" value="X" /></td>
</tr>
<tr>
<td><input type="submit" name="value" value="4" /></td>
<td><input type="submit" name="value" value="5" /></td>
<td><input type="submit" name="value" value="6" /></td>
<td><input type="submit" name="operator" value="-" /></td>
</tr>
<tr>
<td><input type="submit" name="value" value="1" /></td>
<td><input type="submit" name="value" value="2" /></td>
<td><input type="submit" name="value" value="3" /></td>
<td><input type="submit" name="value" value="+" /></td>
</tr>
<tr>
<td></td>
<td><input type="submit" name="value" value="0" /></td>
<td><input type="submit" name="dot" value="." /></td>
<td><input type="submit" name="operator" value="=" /></td>
</tr>
</table>
</head>
<body>
</body>35. 처음이자 마지막으로 동적인 페이지 서블릿으로 직접 만들기
새로운 service()만들건데 doGet()과 doPost()가 나뉘어져잇다.
html은 정적이라 만들어 놓은것을 요청한다.
서블릿에 html페이지를 복사한다. 자바에선 이것들을 구분못해서 오류가 발생한다.
out.write("");이런식으로 모든 html코드에 출력코드를 달아준다.
안에 이스케이프문자 \를 하나 하나 입력해줘야된다. 컨트롤f하면 찾아서 변경가능..
매우 매우 매우 귀찮다!!
이후 JSP가 이런것들을 대신 해주는 역할을 한다.
보기에는 똑같아 보이는데 값을 심을 수 있게 된다.
out.printf("<td class="output" colspan = "4">%s</td>", exp);
이렇게 값을 꽃을 수 잇으니 동적으로 문서를 만들어야하는 필요성이 나오게 된다.
@WebServlet("/calcpage")
public class Calcpage extends HttpServlet{
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html; charset=UTF-8");
PrintWriter out = response.getWriter();
out.write("<!DOCTYPE html>");
out.write("<html>");
out.write("<head>");
out.write(" <meta charset = \"UTF-8\">");
out.write(" <title>Insert title here</title>");
out.write("<style>");
out.write("input{");
out.write("width:50px;");
out.write("height:50px;");
out.write("}");
out.write(".output{");
out.write(" hegiht:50px;");
out.write(" background: #e9e9e9;");
out.write(" font-size:24px;");
out.write(" font-weight: bold;");
out.write(" text-align: right;");
out.write(" padding:0px 5px;");
out.write("}");
out.write(" </style>");
out.write(" <form action = \"calc3\" method=\"post\">");
out.write(" <table>");
out.write(" <tr>");
out.printf(" <td class=\"output\" colspan = \"4\">%s</td>", exp);
out.write(" </tr>");
out.write(" <tr>");
out.write(" <td><input type = \"submit\" name=\"operator\" value=\"CE\"/></td>");
out.write(" <td><input type = \"submit\" name=\"operator\" value=\"C\"/></td>");
out.write(" <td><input type = \"submit\" name=\"operator\" value=\"BS\"/></td>");
out.write(" <td><input type = \"submit\" name=\"operator\" value=\"/\"/></td>");
out.write(" </tr>");
out.write(" <tr>");
out.write(" <td><input type = \"submit\" name=\"value\" value=\"7\"/></td>");
out.write(" <td><input type = \"submit\" name=\"value\" value=\"8\"/></td>");
out.write(" <td><input type = \"submit\" name=\"value\" value=\"9\"/></td>");
out.write(" <td><input type = \"submit\" name=\"operator\" value=\"*\"/></td>");
out.write(" </tr>");
out.write(" <tr>");
out.write(" <td><input type = \"submit\" name=\"value\" value=\"4\"/></td>");
out.write(" <td><input type = \"submit\" name=\"value\" value=\"5\"/></td>");
out.write(" <td><input type = \"submit\" name=\"value\" value=\"6\"/></td>");
out.write(" <td><input type = \"submit\" name=\"operator\" value=\"-\"/></td>");
out.write(" </tr>");
out.write(" <tr>");
out.write(" <td><input type = \"submit\" name=\"value\" value=\"1\"/></td>");
out.write(" <td><input type = \"submit\" name=\"value\" value=\"2\"/></td>");
out.write(" <td><input type = \"submit\" name=\"value\" value=\"3\"/></td>");
out.write(" <td><input type = \"submit\" name=\"value\" value=\"+\"/></td>");
out.write(" </tr>");
out.write(" <tr>");
out.write(" <td></td>");
out.write(" <td><input type = \"submit\" name=\"value\" value=\"0\"/></td>");
out.write(" <td><input type = \"submit\" name=\"dot\" value=\".\"/></td>");
out.write(" <td><input type = \"submit\" name=\"operator\" value=\"=\"/></td>");
out.write(" </tr>");
out.write("</table>");
out.write("</head>");
out.write("<body>");
out.write("</body>");
out.write("</html>");
}
}36. 계산기 서블릿 완성하기
post된 데이터를 어떻게 처리할 것인가를 정해야한다.
쿠키에 저장되는게 연산식이니 그 연산식 자체를 읽어서 구현하는게 편하다.
눌러서 post시키는 것은 calc3.java에서 처리한다.
calcpage는 값을 꽃아넣어주기 위해 쿠키를 읽기위해 반복문으로 읽어준다.
@WebServlet("/calcpage")
public class Calcpage extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String exp = "0";
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie c : cookies) {
if (c.getName().equals("exp")) {
exp = c.getValue();
break;
}
}
}calc3에선 사용자가 입력하는 값을 가지고 익스프레션을 만들어서 쿠키에 저장하고 리디렉션하면된다.
읽어온게 있으면 더해주고 없으면 빈문자열이 초기값이 될 것이다.
값 세개중 null이 아닌것을 그대로 누적해주면된다.
"="일때는 계산해주자.
쿠키에서 읽어와서 사용자가 입력한 내용을 덧붙이는 작업을 하고 쿠키로 저장한다.
날먹부분 자바에서 자바스크립트롤 사용할수있게 해주는 엔진가져와서할 수 있는데
Cannot invoke "javax.script.ScriptEngine.eval(String)" because "engine" is null
최신 jdk에선 지원안해서 오류난다. 그냥 된다고 생각하자
@WebServlet("/calc3")
public class Calc3 extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html; charset=UTF-8");
String value = request.getParameter("value");
String operator = request.getParameter("operator");
String dot = request.getParameter("dot");
String exp = "";
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie c : cookies) {
if (c.getName().equals("exp")) {
exp = c.getValue();
break;
}
}
}
if (operator != null && operator.equals("=")) {
ScriptEngine engine = new ScriptEngineManager().getEngineByName(exp);
try {
exp = String.valueOf(engine.eval(exp));
} catch (ScriptException e) {
e.printStackTrace();
}
} else {
exp += (value == null) ? "" : value;
exp += (operator == null) ? "" : operator;
exp += (dot == null) ? "" : dot;
}
Cookie expCookie = new Cookie("exp", exp);
response.addCookie(expCookie);
response.sendRedirect("calcpage");
}
}37. 쿠키 삭제하기
쿠키를 설정가서 삭제하는게 너무 귀찮았다.
쿠키를 삭제해야 초기화가 된다. c눌럿을때 쿠키삭제하기를 구현해보자.
else if (operator != null && operator.equals("C")) {
exp = "";
}매우 간단하다 그냥 공백을 제공하면된다.
expCookie.setMaxAge(0);을해줘야 바로 없어진다.
그러나 항상 초기화하면 문제가 잇으니 조건문에 넣어주자.
if (operator != null && operator.equals("C")) {
expCookie.setMaxAge(0);
}38. GET과 POST에 특화된 서비스 함수
지금까지 서블릿을 구현할때 모두 service함수로 처리했는데 이것들의 특화된것을 해보고자한다.
지금까지는 get요청을해도 service()가 post요청을해도 serviece()가 실행이 됬엇다.
여기서 이것들을 나눠서 구분지을 필요가 있다.
두가지방식이 있다 서비스함수에서 구분짓거나 특화된 메소드를 사용하면된다.
38.1 service()에서 구분짓기.
if(req.getMethod().equals("GET")) {
} else if (req.getMethod().equals("POST")) {
}
요청이 왔다는 것을 구분하기 위해서 반드시 대문자로 작성해주자.
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if (request.getMethod().equals("GET")) {
System.out.println("GET 요청이 왔습니다.");
} else if (request.getMethod().equals("POST")) {
System.out.println("POST 요청이 왔습니다.");
}
// super.service(request, response);
}이렇게 하는 방법도 있지만 또다른 방법도 있다.
38.2 get요청과 post요청에 특화된 메소드 사용하기.
부모가 가진 함수는 doGet혹은 doPost함수를 요청한다.
super.service(request, response); 오버라이딩된게아닌 부모의 메소드를 사용하면된다.
부모가 가진 service()는 doGet()과 doPost()메소드를 가지고 잇다.
사용자가 요청시 get요청인지 post요청인지 구분해서 각각을 호출한다.
만약 얘들이 오버라이딩이 안되있으면 405 오류가 난다.
이렇게 특화된 녀석을 만들거나 조건처리를 해야한다.
doGet, doPost를 하기전에 공통으로 할것이 있으면 service()을 오버라이딩해서 사용하고
거쳐서 밑에 doGet, doPost이 처리되도록하면된다.
service()을 오버라이딩안되있으면 알아서 doGet, doPost이 처리된다.
서비스를 거쳐서 호출되고 get이 왓다면 doGet이 실행되는 것이다.
공통으로 처리하는것이 없다면 service함수를 없애면된다.
어차피 서비스함수를 통해서 호출됨을 알았으니까 그렇다.
오버라이딩을 안하면 부모의 서비스함수가 요청되고 그안에 두겟 두포스트 메소드가 잇으니 내가 작성한 두겟 두포스트가 가져오게 되는 것이다.
@WebServlet("/calculator")
public class Calculator extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if (request.getMethod().equals("GET")) {
System.out.println("GET 요청이 왔습니다.");
} else if (request.getMethod().equals("POST")) {
System.out.println("POST 요청이 왔습니다.");
}
super.service(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("doGet 메소드가 요청되었습니다.");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("doPost 메소드가 요청되었습니다.");
}
}39. 계산기 프로그램 하나의 서블릿으로 합치기
쿠키를 calcpage calc3에서만 사용하고 싶은데 그래서 url을 지정하면 한 곳에 하나만 저장할 수 있어서
어쩔수 없지 전역에 저장하고 있엇어야했다.
그런데 이 get요청과 post요청만하는 두 페이지를 합쳐놓으면 한페이지에서만의 쿠키가 될 수 있게 되는 것이다.
calcpage는 get요청만 되고 calc3는 post만한다. service()만 사용해서 합칠 수가 잇다.
doget에는 calcpage내용을 넣고 dopost에는 calc3를 넣으면된다.
out.write("<form method="post">");
자기 페이지 요청하는것이니 url을 지정할 필요가 없다.
expCookie.setPath("/calculator");
쿠키설정을 자기만 사용하게 할 수 잇다. 다른 url에 쿠키가 전달안되게 할 수 있다.
response.sendRedirect("calculator"); 리디릭트되는것을 본인으로 해주기
훨씬더 바람직하다.