🍁순수 자바스크립트로 구현
Ajax를 사용해서 서버로 데이터를 요청하고 응답받는 게 가능하다.
이를 구현하기 위해서는 Ajax 객체를 먼저 만들어야 한다.
Ajax 객체 생성
const ajax = new XMLHttpRequest(); //서버와 데이터를 송수신(전화기)
서버와 통신하는 Ajax 객체를 생성한다.
이 객체가 전화기의 역할을 하여 서버와 송수신할 수 있게 된다.
open, send 메서드
//<form method="GET" action="주소">
ajax.open('GET', '서버쪽 프로그램 주소');
ajax.open('GET', '/ajax/ex02.txt');
ajax.send();
ajax에는 open이라는 메서드가 있다. 이때 form 태그의 GET과 open 메서드의 GET은 같다.
서버와 통신을 하기 위해서 GET 방식으로 통신할지, POST 방식으로 통신할지에 대한 메서드와 서버 쪽 프로그램 주소를 넣는다.
즉, 두 번째 값은 form 태그의 action 값을 넣는다고 보면 된다. 이 action의 타깃의 ex02.txt이며, 이후에 send 메서드를 호출한다.
open 메서드는 서버를 연결하는 게 아니라 설정만 하는 것이다. 그리고 send 메서드가 실제적으로 서버와 연결을 한다.
readyState, status, responseText 프로퍼티
ajax.onreadystatechange = function() {
//readystate change
//$('#msg').html(ajax.responseText);
$('#msg').append('<div>' + ajax.readyState + '</div>');
$('#msg').append('<div>' + ajax.status + '</div>');
$('#msg').append('<div>' + ajax.responseText + '</div>');
$('#msg').append('<hr>');
};
왜 4번이 발생한 것일까? readyState값이 처음에는 1인데, 갈수록 1씩 늘어 4까지 출력된다.
onreadystatechange는 readyState 값이 변할 때마다 발생하는 이벤트이다.
https://www.tcpschool.com/ajax/ajax_server_response
프로퍼티에 대해서는 위 글을 참고한다.
readyState 프로퍼티
readyState 프로퍼티는 ajax 객체의 상태이자 현재 서버와의 통신 상태를 의미한다.
- UNSENT (숫자 0): ajax 객체가 생성됨.
- OPENED (숫자 1): open() 메서드가 성공적으로 실행됨.
- HEADERS_RECEIVED (숫자 2): 모든 요청에 대한 응답이 도착함.
- LOADING (숫자 3): 요청한 데이터를 처리 중임.
- DONE (숫자 4): 요청한 데이터의 처리가 완료되어 응답할 준비가 완료됨.
status 프로퍼티
status 프로퍼티는 상태 코드라고 해서 서버의 문서 상태를 나타낸다.
서버가 요청에 응답할 때 어떤 상태인지를 나타낸다.
- 200 (OK): 모든 상태가 정상이며, 서버에 문서가 존재함.
- 404 (Not Found): 서버에 문서가 존재하지 않음.
- 405 (Method Not Allowed): 해당 메서드가 허락되지 않음.
- 408 (Request Timeout): 서버 쪽에서 장기간 응답하지 않음.
- 500 (Internal Server Error)
200번이 아닌 다른 모든 번호는 에러이므로, 200번만이 올바른 응답이라고 보면 된다.
404번은 브라우저가 없는 주소를 요청한 경우이다.
405번은 브라우저가 GET 방식으로 불렀는데, 서버는 POST로 준비해 놓으면 POST로 받을 수 있는데, GET으로 불렀기 때문에 발생하는 상태이다. 또는 반대의 경우에도 해당한다.
408번은 무한루프 등으로 인해 응답이 끝나지 않아 만료시간이 오래되어 서버에서 잘못됐다고 생각하여 발생하는 상태이다.
500번대인 상태는 서버쪽에서 잘못된 것이다. 500번대 에러가 나면 자바 코드를 확인해야 한다.
https://developer.mozilla.org/ko/docs/Web/HTTP/Status
http 상태 코드(응답 코드)에 대해서는 위 글을 참고한다.
responseText 프로퍼티
if (ajax.readyState == 4) {
$('#msg').append('<div>responseText: ' + ajax.responseText + '</div>');
}
readyState의 데이터를 수신한 직후인 2번까지 응답받은 responseText에 데이터가 없다.
3번은 불안정한 상태이기 때문에 접근이 되는 데이터도 있고, 접근이 안 되는 데이터도 있다. 그래서 송수신이 되는 것처럼 보이지만 사실은 아니다.
해석까지 모두 마치고 난 4번이 되는 순간이 서버와 연결을 할 수 있는 상태이다.
그래서 responseText를 쓰려면 readyState가 4라는 전제 조건이 필요하다.
Ajax의 서버 처리
Ajax로 서버 처리를 하면 문서가 통째로 안에 들어 있는 문제가 발생한다.
실제로 여기에 데이터만 찍히는 게 아니라는 점을 알고 있어야 한다.
jsp는 문서를 만들고 돌려준다는 특징이 있다. 그래서 그동안 돌려받은 문서를 브라우저가 받아서 HTML과 CSS를 해석한 다음에 화면을 만들어서 보여줬었다.
그런데 이번에는 상황이 다르다! Ajax가 요청을 했는데, Ajax는 브라우저가 아니다.
Ajax는 소스로 돌려받기 때문에 해석을 할 수 없다. 그래서 모든 클라이언트 소스를 내부에 넣고 문서가 편집되는 문제가 발생하는 것이다.
if (ajax.readyState == 4) {
$('#msg').append('<div>readyState: ' + ajax.readyState + '</div>');
$('#msg').append('<div>status: ' + ajax.status + '</div>');
$('#msg').append('<xmp>responseText: ' + ajax.responseText + '</div>');
$('#msg').append('<hr>');
}
심지어 <div> 태그에 div를 xmp로 변경하면 돌려받은 페이지를 출력한다.
따라서 일반적인 브라우저의 서버 처리와 Ajax를 사용하는 서버 처리는 구분해서 작업해야 한다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
${count}
이를 해결하기 위해서는 페이지를 돌려주는 게 아니라 필요한 데이터만을 돌려주면 된다.
PrintWriter writer = resp.getWriter();
writer.print(count);
writer.close();
//req.setAttribute("count", count);
//RequestDispatcher dispatcher = req.getRequestDispatcher("/WEB-INF/views/ex02data.jsp");
//dispatcher.forward(req, resp);
또는 태그 작업을 할 일이 없기 때문에 출력할 값만을 돌려줄 수 있도록 PrintWriter를 사용한다.
위 코드는 Ex02Data.java 파일의 코드 일부이다.
PritWriter에 대해서는 위 글을 참고한다.
페이지(데이터) 요청
- 브라우저를 통해서 요청
- ajax를 통해서 요청
브라우저를 통해서 요청하면 서버 측에서는 HTML 페이지를 반환한다.
ajax를 통해서 요청하면 서버측에서는 필요한 데이터만 반환해야 한다.
전체 코드
Ex02.java
package com.test.ajax.controller;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/ex02.do")
public class Ex02 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
RequestDispatcher dispatcher = req.getRequestDispatcher("/WEB-INF/views/ex02.jsp");
dispatcher.forward(req, resp);
}
}
Ex02Data.java
package com.test.ajax.controller;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.test.ajax.repository.AjaxDAO;
@WebServlet("/ex02data.do")
public class Ex02Data extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
AjaxDAO dao = new AjaxDAO();
int count = dao.getMemoCount();
PrintWriter writer = resp.getWriter();
writer.print(count);
writer.close();
//req.setAttribute("count", count);
//RequestDispatcher dispatcher = req.getRequestDispatcher("/WEB-INF/views/ex02data.jsp");
//dispatcher.forward(req, resp);
}
}
ex02.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="http://pinnpublic.dothome.co.kr/cdn/example-min.css">
<style>
</style>
</head>
<body>
<div>
<input type="text" id="txt1" value="${count}">
<input type="button" value="버튼1" id="btn1">
</div>
<div id="msg" class="message"></div>
<div>
<input type="text" id="txt2">
<input type="button" value="버튼2" id="btn2">
</div>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="http://pinnpublic.dothome.co.kr/cdn/example-min.js"></script>
<script>
$('#btn1').click(function() {
const ajax = new XMLHttpRequest();
ajax.onreadystatechange = function() {
//readystate change
//$('#msg').html(ajax.responseText);
if (ajax.readyState == 4 && ajax.status == 200) {
$('#msg').append('<div>readyState: ' + ajax.readyState + '</div>');
$('#msg').append('<div>status: ' + ajax.status + '</div>');
$('#msg').append('<div>responseText: ' + ajax.responseText + '</div>');
$('#msg').append('<hr>');
$('#txt1').val(ajax.responseText);
} else {
if (ajax.readyState == 4) {
alert('서버와 통신이 불안정합니다.');
}
}
};
//<form method="GET" action="주소">
//ajax.open('GET', '/ajax/ex02.txt');
ajax.open('GET', '/ajax/ex02data.do');
ajax.send();
});
</script>
</body>
</html>
if (ajax.readyState == 4 && ajax.status == 200) {
$('#msg').append('<div>readyState: ' + ajax.readyState + '</div>');
$('#msg').append('<div>status: ' + ajax.status + '</div>');
$('#msg').append('<div>responseText: ' + ajax.responseText + '</div>');
$('#msg').append('<hr>');
$('#txt1').val(ajax.responseText);
} else {
if (ajax.readyState == 4) {
alert('서버와 통신이 불안정합니다.');
}
}
업무 진행을 readyState가 4일 때, status가 200일 때로 진행하지 않으면 위와 같이 에러 페이지가 들어가게 된다. 또는 else
ex02data.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
${count}
Ajax로 데이터를 가져오면 다른 작업을 하고 있을 때 서버와 통신을 하더라도 새로고침이 발생하지 않는다. 그래서 다른 파티에 전혀 영향을 미치지 않는다.