서버에서 클라이언트로 값을 출력하는 것은 할 수 있게 되었다. 그럼 클라이언트에서 서버로 필요한 정보를 보내려면 어떻게 해야 하나? 그 기본은 "쿼리 문자열"과 "양식(form)"이다. 이 두가지를 사용한 데이터 전달하는 방법에 대해 설명한다.


쿼리 문자열으로 값 받기

단순히 뭔가를 표시할 뿐이라는 것은 전회 설명한 JSP의 기본 태그만으로 어떻게든 할 수 있게 되었다. 이번에는 더 나아가 "클라이언트에서 서버로 뭔가를 보내면, 그것을 받아 다시 클라이언트에게 돌려 주는" 대화형 작업을 수행해 보자.

이러한 작업의 포인트는 "클라이언트에서 서버로 어떻게 필요한 정보를 보낼까"라는 점이다. 이를 알면 서버에서 필요한 처리를 하여 그에 따라 표시를 되돌려 보낼 수 있다.

여기에는 여러 방법이 있다. 먼저 가장 간단한 것으로 "쿼리 문자열을 사용"방법부터 해보자. 쿼리 문자열이라고 하는 것은, URL 주소 뒤에 붙은 파라미터의 기술 부분이다.

Web 사이트에서 http://xxx/?abc=xyz와 같이 이런식으로 URL 주소 뒤에 이런 것이 붙어 있는 것을 본 적이 있을 것이다. 그것이 쿼리 문자열이다. 이는 다음과 같은 형태로 되어 있다.

http://도메인/파일의 지정?이름1=값1&이름2=값2& ...

URL주소 뒤에 물음표(?)을 붙이고 그 이후에 "이름=값"형태로 값에 이름으로 붙여서 기술한다. 여러 값을 전달하는 경우 and(&) 기호로 연결한다. 값은 영숫자 텍스트라면 그대로도 괜찮지만, 기호나 2바이트 문자 등이 들어가면 문제를 일으키므로 일반적으로 URL 인코딩이라는 것을 사용하여 인코딩해야 한다.

그럼 이렇게 보내온 값을 JSP에 어떻게 받을 것인가? 여기에는 "request"라는 내장 객체를 이용한다. 이는 특별히 선언하지 않아도 처음부터 사용할 수 있는 상태가 되어있는 특별한 객체이다.

request은 서버에 보낸 요청(클라이언트에서의 요구)에 대한 다양한 정보를 담고 있다. 이 객체에는 'getParameter'라는 메소드가 준비되어 있다. 이것을 사용하여 쿼리 문자 값을 꺼낼 수 있다.

String 변수 = request.getParameter(값 이름);

이런 식으로 호출하면 된다. 그럼, 실제로 간단한 샘플을 만들어 서버와 클라이언트 간에 문자열을 주고 받아 보자.

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<%
String str = request.getParameter("param");
%>
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Sample jsp</title>
    <style>
    h1 {font-size:16pt; background:#AAFFAA; padding:5px; }
    </style>
    <script type="text/javascript">
    function doAction(){
        var s = document.getElementById('input').value;
        var url = 'hello4.jsp?param=' + encodeURI(s);
        window.location.href = url;
    }
    </script>
</head>
<body>
    <h1>Sample jsp page</h1>
    <p>이 페이지는 샘플입니다.</p>
    <p>페라미터:<%=str%></p>
    <input type="text" id="input">
    <button onclick="doAction();">Click</button>
    </body>
</html>

위에 나열된 샘플은 입력 필드에 텍스트를 쓰고 버튼을 누르면 메시지가 표시된다. 여기에서는 JavaScript를 사용하여 쿼리 문자열을 붙여서 페이지의 주소를 만들고 거기에 점프하고 있다.

var s = document.getElementById('input').value;
var url = 'helo.jsp?param=' + encodeURI(s);
window.location.href = url;

여기에는 예를 들어 필드에 "abc"라고 입력하면 helo.jsp?param=abc URL 주소가 서버로 전송된다. 서버 측에서는 JSP 코드으로 페라미터 값을 받고 있다.

String str = request.getParameter("param");

이것으로 변수 str에 "abc"라는 텍스트가 할당된다. 이 후에는 <p>페라미터:<%=str %></p>와 같이 하여 받은 값을 출력하고 있다.

이번에는 단지 표시만 하고 있지만, 물론 받은 값을 바탕으로 여러가지 작업을 수행하고 결과를 출력할 수도 있다.



폼(form) 전송

사용자으로부터 입력을 받으려면, 역시 기본은 "폼(form) 전송"이다. <form> 태그에 의한 양식에 정보를 담아 전송하고 그 전송에 대한 결과를 받아 처리하는 것이다. HTML의 기본이라 할 수 있는 기능이다.

폼에서 전송된 텍스트는 "getParameter"을 이용하여 얻을 수 있다. 아래와 같은 방식이다.

String 변수 = request.getParameter(이름);

이것으로 폼에 마련된 컨트롤에서 인수에 지정된 이름의 항목의 값을 얻어 올 수 있다. 주의해야 할 것은 "지정하는 것은 ID가 아닌 이름이다"라는 점이다. <input id="hoge">와 같이, ID만 지정하면 값을 얻어 올 수 없다. 반드시 name 속성을 지정해야 한다.

또한, 같은 이름(name)의 항목이 다수 있는 경우에는 그 값을 한꺼번에 얻어 올 수 있다. 이는 'getParameterValues'라는 메소드를 사용한다.

String[] 변수 = request.getParameterValues(이름);

이 getParameterValues는 인수로 지정된 name의 값을 모두 모와서 String 배열을 돌려준다. 이는 예를 들어. 여러 항목을 선택할 수 <select> 태그에서 전체 값을 얻우 올 때에 사용된다.

그럼 예제를 만들어 보도록 하자.

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<%
String inpt = request.getParameter("input");
inpt = inpt == null ? "" : inpt;
String chk = request.getParameter("check");
chk = chk == null ? "OFF" : "ON";
String rd = request.getParameter("radio");
rd = rd == null ? "" : rd;
String[] sels = request.getParameterValues("select");
String sel = "";
if (sels != null){
    for(int i = 0;i < sels.length;i++)
        sel += sels[i] + " ";
}
String str = "INPUT:" + inpt + "<br>" +
        "CHECK: " + chk + "<br>" +
        "RADIO: " + rd + "<br>" +
        "SELECT:" + sel;
%>
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Sample jsp</title>
    <style>
    h1 {font-size:16pt; background:#AAFFAA; padding:5px; }
    </style>
</head>
<body>
    <h1>Sample jsp page</h1>
    <p>이 페이지는 샘플입니다.</p>
    <p><%=str  %></p>
    <table>
    <form method="post" action="hello.jsp">
        <tr><td>입력</td><td>
        <input type="text" id="input" name="input"></td></tr>
        <tr><td></td><td>
        <input type="checkbox" id="c1" name="check" value="Une">
        <label for="c1">체크 박스</label></td></tr>
        <tr><td></td><td>
        <input type="radio" name="radio" id="r1" value="first">
        <label for="r1">라디오 버튼1</label><br>
        <input type="radio" name="radio" id="r2" value="Second">
        <label for="r2">라디오 버튼2</label></td></tr>
        <tr><td></td><td>
        <select id="select" name="select" multiple>
            <option value="Eins">첫번째</option>
            <option value="Twei">두번재</option>
            <option value="Drei">세번째</option>
        </select></td></tr>
        <tr><td></td><td>
        <input type="submit" value="전송"></td></tr>
    </form>
    </table>
    </body>
</html>

위에 예제는 양식을 자신의 주소로 보내하여 입력된 정보를 함께 표시하는 예제이다. 여기에서는 hello.jsp라는 파일명으로 만들었기 때문에, 다른 파일 이름을 사용하는 경우는 <form> 태그의 action을 수정한다.

여기에서는 전송된 양식(<select> 이외의)의 정보는 getParameter을 사용하여 얻어오고 있다.

String inpt = request.getParameter("input");
String chk = request.getParameter("check");
String rd = request.getParameter("radio");

모두 name으로 지정한 이름을 인수로 지정하고 있다는 것을 알 수 있을 것이다. 조금 까다로운 것이 <select>에서 얻어 오는 거다. 이는 이번에 여러 항목을 선택할 수 있도록 되어있다.

String[] sels = request.getParameterValues("select");
String sel = "";
if (sels != null){
    for(int i = 0;i < sels.length;i++)
        sel += sels[i] + " ";
}

먼저 getParameterValues에서 String 배열을 얻어온다. 그리고 반복하여 배열에서 한 개씩 값을 얻어오고 있다. 여기서 전혀 값이 없다면, getParameterValues는 null이 되면 for 문은 에러가 되기 때문에 null이 아닌지 확인하고 실행하는 것을 잊지 않도록 하자.



Ajax를 사용하여 JSP에 정보 전달

폼 전송은 분명히 HTML의 기본이긴 하지만, 최근에는 그다지 많이 사용하지 않게 되고 있다. 보낼 때마다 페이지를 매번 서버에서부터 다시 로드해서 다시 읽어 들여 새로 고침을 하기에 싫어하는 사람도 많다.

폼 전송 대신에 서버와의 통신으로 많이 사용되고 있는 것이 "Ajax"이다. Ajax는 JavaScript를 이용하여 서버와 비동기 통신을 하는 기능이다. 비동기(처리가 끝날 때까지 기다리지 않고 먼저 처리를 진행 방식)이기 때문에 통신하는 동안 다른 조작을 할 수 없게 되는 일도 없고, 백그라운드로 필요한 정보를 얻기 때문에 페이지를 이동하지도 않으면서 화면 표시를 업데이트 할 수 있다.

Ajax 자체는 따로 JSP가 없어도, HTML과 JavaScript만 있으면 사용할 수 있다. 즉, 보통의 HTML 파일로 가능하다. Ajax에서 액세스하는 "서버 측 프로그램"에 JSP를 사용하면 된다.

이것은 실제로 샘플을 보는 편이 빠를 거다. 아래에 간단한 예제를 보도록 하자.

index.html

<!DOCTYPE html>
<html>
<head>
   <meta http-equiv="content-type" content="text/html; charset=UTF-8">
   <title>Hello App Engine</title>
   <style>
       h1 {font-size:16pt; background:#AAFFAA; padding:5px; }
   </style>
   <script type="text/javascript">
       function doAction(){
           var req =  createRequest();
           if (req == null){
               alert("실행이 되지 않는다!");
               return;
           }
           var s = document.getElementById('input').value;
           req.open("post", "hello.jsp?param=" + encodeURI(s));
           req.setRequestHeader("User-Agent","XMLHttpRequest");
           req.onreadystatechange = function() {
               if (this.readyState == 4 && this.status == 200) {
                   var msg = document.getElementById('msg');
                   msg.innerHTML = this.responseText;
               }
           }
           req.send();
       }

       function createRequest(){
           var httplist = [
               function(){ return new XMLHttpRequest(); },
               function(){ return new ActiveXObjct("Msxml2.XMLHTTP"); },
               function(){ return new ActiveXObject("Microsoft.XMLHTTP"); }
           ];
           for(var i = 0;i < httplist.length;i++){
               try {
                   var http = httplist[i]();
                   if (http != null) return http;
               } catch(e){
                   continue;
               }
           }
           return null;
       }
   </script>
</head>
<body>
   <h1>Hello App Engine!</h1>
   <p id="msg">무언가 써서 송신해 주세요.</p>
   <table>
       <tr>
           <td>입력</td>
           <td><input type="text" id="input" name="input"></td></tr>
       <tr>
           <td></td>
           <td><button onclick="doAction();">Send</button></td>
       </tr>
   </table>
</body>
</html>

hello.jsp

<%@ page language="java" contentType="text/plain; charset=utf-8"
   pageEncoding="utf-8"%>
<%
String method = request.getMethod();
if ("GET".equals(method)){
   out.println("can't access!");
} else {
   String inpt = request.getParameter("param");
   out.println("당신, '" + inpt + "' 라고 썼습니다.");
}
%>

여기에서는 index.html과 hello.jsp을 사용하고 있다. index.html에 액세스하여 입력 양식에 무엇인가 쓰고 버튼을 누르면 Ajax 통신으로 hello.jsp에 액세스하고 결과를 표시한다.

Ajax 통신에 대해서는 JSP와는 관계 없기 때문에 설명은 생략한다(관심있는 사람은 "Ajax로 비동기 서버 통신"에 대해서 알아보고 바란다). 여기에서는 Ajax으로 억세스하는 JSP 측의 처리를 확인한다.

이번에는 hello.jsp에 POST로 액세스하여, 결과를 받을 수 있도록 하고 있다. 직접 이 페이지에 액세스 (GET으로 액세스)하는 경우에는 "can not access!"가 표시되도록 되어 있다. 이러한 GET과 POST의 사용 구분은 request 메소드로 알아 낼 수 있다.

String method = request.getMethod();
if ("GET".equals(method)){……중략……}

request 객체의 "getMethod"는 액세스 메소드을 반환한다. 반환값은 "GET" 또는 "POST" 중 하나이다. JSP의 getParameter의 메소드는 GET이든 POST이든 동일하게 호출 값을 얻을 수 있다. 이용 상으로는 JSP는 GET/POST를 의식하지 않고 동일하게 취급할 수 있도록 설계되어 있다. 하지만 때로는 "GET의 경우 하는 처리, POST의 경우에 하는 처리"와 같이 처리를 나누지 않으면 안되는 것이 있다. 이러한 경우 getMethod가 사용된다.

다른 부분은 특별히 어려운 부분은 없을 것이다. out.println으로 출력한 것이 그대로 Ajax 통신으로 클라이언트로 전송되는 것이다. 또한 이번에는 Ajax으로 텍스트 값을 받을 뿐이다. 처음에 있는 page 지시문을 다음과 같이 작성되어 있다.

<%@ page language="java" contentType="text/plain; charset=utf-8"
    pageEncoding="utf-8"%>

잘 보면 contentType="text/plain; charset=utf-8"으로 되어 있다. text/html 대신에 text/plain으로 변경되어 있다. 이렇게 "서버에 요청하여 결과를 받을 뿐"인 경우에는 그냥 텍스트를 출력할 뿐이기에 text/plain으로 해야 한다.



받은 데이터 이스케이프 처리

클라이언트와 서버 사이에서 데이터를 교환하는 처리를 배울 때, 함께 배워야 하는 것이 있다. 그것은 "데이터 이스케이프"에 대해서이다.

사용자로부터 전송된 값을 사용하는 경우, "사용자는 어떤 값을 보내올지 모른다"라는 것을 염두에 둘 필요가 있다. 특히 생각하지 않으면 안되는 것이 JavaScript이다. 입력 필드에 <script> 태그를 사용한 스크립트를 작성하여 전송되면 어떻게 될까? 그 텍스트를 그대로 화면에 표시하도록 처리가 되어 있는 경우, 페이지가 나타날 때 스크립트가 실행될 것이다(최근의 브라우저들은 이를 막아줘서 실행이 되지 않는 경우가 많다). 예를 들면, 게시판이나 댓글 게시물과 같은 시스템의 경우, 이러한 스크립트가 게시되어 표시되도록 되어 버리면, 거기에 다른 사람이 접근할 때마다 의도하지 않은 스크립트가 실행될 것이다.

일반적으로 XSS(크로스 사이트 스크립팅)이라는 사이트 공격은 이런 취약점을 노린 것이다. 아무튼, 여기 샘플에서 하고 있는 정도라면 문제가 발생 수 없겠지만, 데이터베이스 등의 데이터를 축적하고 이를 표시하는 시스템이 되면 이런 종류의 트러블는 피해갈 수 없을 것이다. 지금 단계에서 "어떻게 대처하면 좋을지"정도는 배워 보도록 하자.

이런 종류의 공격에 대한 대책의 기본은 "텍스트를 출력하기 전에 이스케이프 처리한다"라는 것이다. 예를 들어, HTML 태그에서 사용하는 <,>와 같은 기호를 &lt;&gt;으로 대체하는 것만으로 그 태그는 무력화 할 수 있다. 이와 같이 특별한 기능을 가지는 기호 종류를 이스케이프 처리하는 것으로, 그 데이터에 포함되어 있는 기능을 무력화하는 것이다.

아래에 매우 간단한 예제를 보도록 하자.

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<%
String inpt = request.getParameter("input");
inpt = inpt == null ? "" : inpt;
String chk = request.getParameter("check");
chk = chk == null ? "OFF" : "ON";
String rd = request.getParameter("radio");
rd = rd == null ? "" : rd;

String str = "INPUT:" + getEscapedString(inpt) + "<br>" +
        "CHECK: " + getEscapedString(chk) + "<br>" +
        "RADIO: " + getEscapedString(rd) + "<br>";
%>
<%!
public String getEscapedString(String s){
    String str = s;
    str = str.replace("&","&amp;");
    str = str.replace("<","&lt;");
    str = str.replace(">","&gt;");
    str = str.replace("\"","&quot;");
    return str;
}
%>
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Sample jsp</title>
    <style>
    h1 {font-size:16pt; background:#AAFFAA; padding:5px; }
    </style>
</head>
<body>
    <h1>Sample jsp page</h1>
    <p>이 페이지는 샘플입니다.</p>
    <p><%=str%></p>
    <form method="post" action="hello.jsp">
    <table>
        <tr>
            <td>입력</td>
            <td><input type="text" id="input" name="input"></td>
        </tr>
        <tr>
            <td></td>
            <td><input type="checkbox" id="c1" name="check" value="Une"><label for="c1">체크박스</label></td>
        </tr>
        <tr>
            <td></td>
            <td>
                <input type="radio" name="radio" id="r1" value="first"><label for="r1">라디오버튼1</label><br>
                <input type="radio" name="radio" id="r2" value="Second"><label for="r2">라디오버튼2</label>
            </td>
        </tr>
        <tr>
            <td></td>
            <td><input type="submit" value="송신"></td>
        </tr>
    </table>
    </form>
    </body>
</html>

여기에는 텍스트를 보낼 때에 보낸 텍스트에서 < > \ &와 같은 기호를 모두 이스케이프 처리하여 표시하고 있다. 이스케이프 처리는 getEscapedString이라는 메소드로 정의하고 있다. 텍스트를 출력할 때 쓰내는 텍스트를 getEscapedString으로 이스케이프 처리를 하여 표시하면 된다.

또한 이스케이프 처리를 할 때, 주의해야 할 것은 "텍스트 입력된 값만을 처리하면 된다라고는 생각하지 않는다"라는 점이다. 여기에서 체크 박스나 라디오 버튼의 값까지 getEscapedString으로 처리를 하고 있다. "그렇게 보내온 값은 정해져 있으니까 불필요하지?"라고 생각할 지도 모른다. 하지만 JSP는 GET이든 POST에서도 똑같이 getParameter에서 값을 얻을 수 있다. 이는 예를 들어, hello.jsp?check=hogehoge와 같이 URL을 지정하여 액세스하여 check 본래와는 다른 값을 전달될 수 있다. getMethod으로 POST만을 받아들이도록 처리를 하지 않은 경우, 그대로 그 값이 처리되어 버리는 것이다.

그래서, 프로그램 내에서 클라이언트 측에 텍스트를 출력할 때는 항상 이스케이프 처리하도록 해야 한다.



+ Recent posts