Java EE 정리 04
28 Feb 2019
Reading time ~7 minutes
Java EE 정리 04 - 같은 이름의 Param처리, 페이지 이동, 관계 유지
같은 이름의 Parameter 처리
- 배열로 처리된다.
String[] s = request.gerParameterValues("이름");
// 이름이 없으면 null을 반환
// -null이란 heap의 주소를 가지지 않는 것
// s가 null일 수 있으므로 try~catch나 if로 예외처리
// 1. try ~ catch
try {
for(int i=0; i<s.length; i++) {
// s[i], 입력한 값들
}
} catch (NullPointerException npe) { // NullPointerException객체가 생성됨
// s가 null인 경우
}
// 2.if 사용 방법, 권장
if(s != null) {
for(int i=0; i<s.length; i++) {
// s[i], 입력한 값들
}
}
- 같은 이름의 Parameter 처리 예
<!-- test_param_value.html -->
...
<!-- jQuery CDN -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$("#btnGet").click(function() {
$("#getFrm").submit();
});
$("#btnPost").click(function() {
$("#postFrm").submit();
});
});
</script>
...
<div>
<h2>중복된 이름의 Parameter 처리 : GET방식</h2>
<div>
<form action="../parameter_values" method="get" id="getFrm">
<table>
<tr>
<td><label>이름</label></td>
<td><input type="text" name="name" class="inputBox"/></td>
</tr>
<tr>
<td><label>취미</label></td>
<td>
<input type="checkbox" name="hobby" value="야구"/>야구<br/>
<input type="checkbox" name="hobby" value="댄스"/>댄스<br/>
<input type="checkbox" name="hobby" value="축구"/>축구<br/>
<input type="checkbox" name="hobby" value="흡연"/>흡연<br/>
</td>
</tr>
<tr>
<td colspan="2">
<input type="button" value="전송" class="btn" id="btnGet"/>
</td>
</tr>
</table>
</form>
</div>
</div>
<div>
<h2>중복된 이름의 Parameter 처리 : POST방식</h2>
<div>
<form action="../parameter_values" method="post" id="postFrm">
<table>
<tr>
<td><label>이름</label></td>
<td><input type="text" name="name" class="inputBox"/></td>
</tr>
<tr>
<td><label>취미</label></td>
<td>
<input type="checkbox" name="hobby" value="야구"/>야구<br/>
<input type="checkbox" name="hobby" value="댄스"/>댄스<br/>
<input type="checkbox" name="hobby" value="축구"/>축구<br/>
<input type="checkbox" name="hobby" value="흡연"/>흡연<br/>
</td>
</tr>
<tr>
<td colspan="2">
<input type="button" value="전송" class="btn" id="btnPost"/>
</td>
</tr>
</table>
</form>
</div>
</div>
// UseWebParameterValues.java
@SuppressWarnings("serial")
public class UseWebParameterValues extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
...
// HTML Form Control의 값 받기
// 이름이 유일 <input type="text" ..
String name = request.getParameter("name");
// 이름이 중복 <input type="checkbox" ..
String[] hobbies = request.getParameterValues("hobby");
out.print("\t<strong>");
out.print(name);
out.println("\t</strong>님 께서 입력하신 취미는 아래와 같습니다.");
out.print("\t<ul>");
if (hobbies != null) {
for(int i=0; i<hobbies.length; i++) {
out.print("\t\t<li>");
out.print(hobbies[i]);
out.println("</li>");
}
} else {
out.println("<li>취미 없음.</li>");
}
out.print("\t</ul>");
out.print("\t<a href='date0228/test_param_value.html'>뒤로</a>");
out.print("\t<a href='javascript:history.back()'>뒤로</a>");
...
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8"); // POST 한글처리
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
...
// HTML Form Control의 값 받기
// 이름이 유일 <input type="text" ..
String name = request.getParameter("name");
// 이름이 중복 <input type="checkbox" ..
String[] hobbies = request.getParameterValues("hobby");
out.print("\t<strong>");
out.print(name);
out.println("\t</strong>님 께서 입력하신 취미는 아래와 같습니다.");
out.print("\t<ul>");
try {
for(int i=0; i<hobbies.length; i++) {
out.print("\t\t<li>");
out.print(hobbies[i]);
out.println("</li>");
}
} catch (NullPointerException e) {
out.println("<li>취미 없음.</li>");
}
out.print("\t</ul>");
out.print("\t<a href='date0228/test_param_value.html'>뒤로</a>");
out.print("\t<a href='javascript:history.back()'>뒤로</a>");
...
}
}
- 서블릿에서 개발자는 print, println메소드를 사용하고 디자이너는 write메소드를 사용하여 업무를 구분지을 수 있다.
- 그러나 일반적으로 서블릿으로 뷰를 만들진 않는다.
페이지 이동 - Redirect
- 웹은 URL을 가지고 페이지를 이동.
- Forward 방식, Redirect라는 2가지가 제공
-
Redirect
- 응답시 페이지 이동
- GET방식의 이동만 허용
- 비정상적인 요청이 발생하면 경우 페이지를 이동할 때 사용
- 브라우저가 재요청하기 때문에 URL이 변경됨
- 이동한 페이지에서 이전 페이지의 Parameter 값을받을 수 없다.
- 다른 site의 페이지로 이동가능
response.sendRedirect("url");
- redirect 예
@SuppressWarnings("serial")
public class TestRedirect extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
boolean flag = new Random().nextBoolean(); // 랜덤 불린값 반환
if(!flag) { // 비정상적인 요청이 있을 때
response.sendRedirect("http://sist.co.kr"); // 페이지 이동
return; // 비정상적인 경우 아래 코드가 실행되지 못하도록 막음
}
...
페이지 이동 - Forward
-
데이터 처리 페이지와 화면 디자인 페이지를 구분하여 작성할 때 사용
- 데이터 처리만 하고 디자인 페이지로 forward 처리
- GET/POST 모두 가능
-
MVC Pattern에서 주로 사용
- 코드의 복잡도를 낮출 수 있다.
-
브라우저는 페이지의 이동을 모른다.
- URL이 변경되지 않는다.
- 이동한 페이지에서 이전 페이지의 Parameter 값을받을 수 있다.
- RequestDispatcher 인터페이스를 사용
- ContextPath 내에서만 이동 가능
// 1. 이동할 페이지로 값을 보내기 위해 값을 설정
request.setAttribute("속성명", 객체);
// 2. 이동할 페이지의 설정(URI)
RequestDispatcher rd = request.getRequestDispatcher("uri");
// 3. 이동
rd.forward(request, response);
// 4. 값 받기
// 이동한 페이지에서 값(객체)을 받을 수 있다, casting해서 사용
request.getAttribute("속성명");
- forward 예
// ForwardA.java, 데이터처리에 집중
@SuppressWarnings("serial")
public class ForwardA extends HttpServlet {
// 데이터처리 코드에 집중하기위해 Forward가 만들어짐
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// nameArr와 list는 처리된 데이터라 가정
String[] nameArr = { "오영근","이재현","공선의","박정미","최혜원","김건하" };
List<String> list = new ArrayList<String>();
list.add("PC방 관리 프로그램");
list.add("영화관 예매 프로그램");
list.add("개발자 구인구직 프로그램");
// 1. 처리된 데이터를 이동한 페이지로 보내기 위해 요청객체에 속성으로 설정
request.setAttribute("name", nameArr);
request.setAttribute("project", list);
// 2. 이동할 페이지를 설정
RequestDispatcher rd = request.getRequestDispatcher("forward_b");
// 3. 이동
rd.forward(request, response);
// forward가 호출되면 출력이 안된다.(View에 대한 코드르 만들 필요가 없음)
}
}
-
위 코드를 실행하면 url은 forward_a로 나와있음
- foward 시 브라우저는 페이지가 바뀐걸 모름
- 에러를 보면 forward_b가 없어서 발생한 404에러
-
문자열 연산 시 “+”를 쓰면 StringBuilder가 생성되어 속도가 느려진다.
- 웹은 속도에 민감
- 따라서 문자열에 “+”를 사용하지 않는다.
// ForwardB.java, 디자인에 집중
// FowardB는 FowardA에 의해서 foward처리로 실행되어야 제대로 된 화면을 보여준다.
@SuppressWarnings("serial")
public class ForwardB extends HttpServlet {
// 디자인에 집중한다.
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
...
// Servlet에서 JavaScript를 짜진 않는다..(너무 복잡도가 높다)
out.println("<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js\"></script>");
out.println("<script type=\"text/javascript\">");
out.println("\t$(document).ready(function() {");
out.println("\t\t$('#btnAdd').click(function() {");
// select의 값
out.println("\t\t\tvar prj_name = $('#prj').val();");
out.println("\t\t\tvar output = ''");
out.println("\t\t\toutput='프로젝트명['+prj_name+']<br/>참여인원 : ';");
// checkbox값을 가져와서
out.println("\t\t\t$.each($(\"[name='name']\"),function(idx, obj){");
out.println("\t\t\t\tif($(obj).is(':checked')) {");
out.println("\t\t\t\t\toutput+=$(obj).val()+' ';");
out.println("\t\t\t\t}");
out.println("\t\t\t});");
// div 출력
out.println("\t\t\t$('#prj_member').html(output);");
out.println("\t\t});");
out.println("\t});");
out.println("</script>");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write("<div\tid=\"wrap\">\r\n");
out.write("\t<div id=\"header\">\r\n");
out.write("\t\t<div id=\"headerTitle\">SIST Class4</div>\r\n");
out.write("\t</div>\r\n");
out.write("\t<div id=\"container\">\r\n");
// 데이터 처리 페이지에서 처리한 결과를 받아옴, Casting 필수!
String[] nameArr = (String[])request.getAttribute("name");
List<String> list = (List<String>)request.getAttribute("project");
if (nameArr != null) {
// 출력부분을 생성
out.println("<label>프로젝트 선택</label>");
out.println("<select id='prj'>");
for(String prj : list) {
out.print("\t<option value='");
out.print(prj);
out.print("'>");
out.print(prj);
out.println("</option>");
}
out.println("</select>");
}
if (list != null) {
out.println("<div>");
out.println("<h3>투입인력</h3>");
for(String name : nameArr) {
out.print("<input type='checkbox' name='name' value='");
out.print(name);
out.print("'>");
out.print(name);
out.print(" ");
}
out.println("<input type='button' value='추가' class='btn' id='btnAdd'/>");
out.println("</div>");
out.println("<div id='prj_member'></div>");
}
...
}
}
관계유지
-
비연결성인 웹에서 접속자의 정보를 저장하여 연결되어있는 것 처럼 사용하기 위한 기술
-
비연결성이란 요청이 들어오면 응답을 해주고 연결을 끊는 것.
- 필요한 때만 연결하는 방식으로 서버의 사양이 낮아도 많은 수의 클라이언트에게 서비스가 가능하다.
- 모든 클라이언트 연결을 계속 유지하지 않음
- 접속자의 상태를 알 수 없음
- 필요한 때만 연결하는 방식으로 서버의 사양이 낮아도 많은 수의 클라이언트에게 서비스가 가능하다.
-
비연결성이란 요청이 들어오면 응답을 해주고 연결을 끊는 것.
- 관계유지를 위해 Session, Cookie가 제공
-
Session
- 접속자의 정보를 서버측 메모리에 저장하는 기술
- 모든 접속자에게 적용시킬 수 있다.
- 모든 브라우저의 정보를 저장할 수 있다.
- 브라우저 별 id를 부여하고 값을 저장
- 객체가 저장되므로 모든 값을 저장할 수 있다.
- 사용하기 위한 객체 : HttpSession
-
Cookie
- 접속자의 정보를 접속자의 HDD에 File로 저장하는 기술
- Cookie를 지원하지 않는 브라우저는 Cookie를 사용할 수 없다.
- 사용자가 직접 사용설정가능
- 모든 접속자에게 적용시킬 수 없다.
- 문자열만 저장된다.
- 사용하기 위한 객체 : Cookie
- 대체기술로 HTML5 Storage(Local, Session Storage)가 존재
-
Session Hijacking
- 일반 유저의 Cookie를 탈취해서 접속자를 변조하는 해킹기술
- Session이 없을 때 예
<!-- why_session_a.html -->
...
<!-- jQuery CDN -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$(".btn").click(function() {
checkNull();
});
$("[name='id']").keydown(function(evt) {
if(evt.which == 13) {
checkNull();
}
});
});
function checkNull() {
var id = $("[name='id']").val();
var pass = $("[name='pass']").val();
if(id == "") {
$("[name='id']").focus();
return;
}
if(pass == "") {
$("[name='pass']").focus();
return;
}
$("form").submit();
}
</script>
...
<div>
<form method="post" action="../why_session_b">
<table>
<tr>
<td>
<input type="text" name="id" placeholder="아이디" autofocus="autofocus" class="inputBox"/>
</td>
<td rowspan="2">
<input type="button" value="로그인" class="btn" style="height:45px; width:80px;">
</td>
</tr>
<tr>
<td>
<input type="password" name="pass" placeholder="비밀번호" class="inputBox"/>
</td>
</tr>
</table>
</form>
</div>
...
// WhySessionB.java
@SuppressWarnings("serial")
public class WhySessionB extends HttpServlet {
private Map<String, String> loginMap;
@Override
public void init() throws ServletException {
loginMap = new HashMap<String, String>();
loginMap.put("kong","공선의");
loginMap.put("rho", "노진경");
loginMap.put("kim", "김희청");
loginMap.put("lee", "이재찬");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
...
// 사용자가 입력한 값 받기
String id = request.getParameter("id");
String pass = request.getParameter("pass");
if (loginMap.containsKey(id) && "1234".equals(pass)) {
String name = loginMap.get(id); // 지역변수 name은 다른 Servlet에서 사용불가
out.println("<div>");
out.print("<strong>");
out.println(id);
out.print("</strong>(");
out.println(name);
out.println(")님 로그인 하셨습니다.<br/>");
out.println("<a href='why_session_c'>작업페이지로 이동</a>");
out.println("</div>");
} else {
out.println("아이디나 비밀번호를 확인해주세요.");
out.println("<a href='#void' click='history:back();'>로그인</a>");
}
...
}
}
-
WhySessionC에선 WhySessionB에 존재하는 name, id, pass 들을 쓸 수 없다.
- 지역변수는 다른 서블릿에서 사용할 수 없기 때문
- name, id, pass을 인스턴스 변수로 변경하고 WhySessionB의 객체를 생성해서 쓸 순 있겠지만 그 안에 존재하는 인스턴스변수는 이전 페이지의 변수와는 다른 새로운 변수들
// WhySessionC.java
@SuppressWarnings("serial")
public class WhySessionC extends HttpServlet {
// 링크로 이동, GET방식
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
...
// WhySessionC 서블릿에서는 이전페이지인 WhySessionB 서블릿 내의 값을 사용할 수 없다.
// 이전페이지에서 사용한 이름을 이동한 페이지에서 사용할 수 없다.
String name = req.getParameter("name");
out.println(name+"님의 작업페이지입니다.");
...
}
}
-
이동할 때 쿼리스트링을 추가해주면 정보를 전달가능
- C - D - E 페이지순으로 이동할 때 C와 E만 name을 정보를 필요로 하면 D는 불필요하게 쿼리스트링으로 값을 전달해 줘야 함
- URL이 복잡해짐
- URL을 사용하기 때문에 보안문제 발생
- 쿼리스트링을 변조가능
- C - D - E 페이지순으로 이동할 때 C와 E만 name을 정보를 필요로 하면 D는 불필요하게 쿼리스트링으로 값을 전달해 줘야 함
out.println("<a href='why_session_c?name="+name+"'>작업페이지로 이동</a>");
- 때문에 세션은 이전페이지와 관계를 유지시키기 위해 필요하다.