• Home
  • About
    • Young's Github Pages photo

      一日不作一日不食

    • About
    • Github
  • Posts
    • All Posts
    • All Tags
  • Projects

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>

01

// 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>");

          ...
     }
}

02

  • 서블릿에서 개발자는 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("속성명"); 

03

  • 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에 대한 코드르 만들 필요가 없음)
     }
}

04

  • 위 코드를 실행하면 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("&nbsp;");
               }
               out.println("<input type='button' value='추가'  class='btn' id='btnAdd'/>");
               out.println("</div>");
               out.println("<div id='prj_member'></div>");
          }
          
           ...
     }
}

05

관계유지

  • 비연결성인 웹에서 접속자의 정보를 저장하여 연결되어있는 것 처럼 사용하기 위한 기술
    • 비연결성이란 요청이 들어오면 응답을 해주고 연결을 끊는 것.
      • 필요한 때만 연결하는 방식으로 서버의 사양이 낮아도 많은 수의 클라이언트에게 서비스가 가능하다.
        • 모든 클라이언트 연결을 계속 유지하지 않음
      • 접속자의 상태를 알 수 없음
  • 관계유지를 위해 Session, Cookie가 제공

06

  • 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>
...

07

// 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>");
          }
     
          ...
          
     }
}

08

  • 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을 사용하기 때문에 보안문제 발생
      • 쿼리스트링을 변조가능
out.println("<a href='why_session_c?name="+name+"'>작업페이지로  이동</a>");

09

  • 때문에 세션은 이전페이지와 관계를 유지시키기 위해 필요하다.


Java EEServlet