Servlet/JSP - AJAX(이론, 아이디 중복체크, 댓글 조회(GSON))
1. 이론
1) AJAX 개요
: Asynchronous JavaScript AND XML의 약자
서버로부터 데이터를 가져와 전체 페이지를 새로 고치지 않고 일부만 로드해 내용물만 바꿀 수 있는 기법
참고로, 우리가 기존에 a태그를 이용해서 요청 및 form태그를 통해 요청한 방식은 동기식 요청
=> 응답 페이지가 돌아와야 그 결과를 볼 수 있었음(== 페이지 화면이 한 번 깜빡)
비동기식 요청을 보내기 위해서는 AJAX라는 기술이 필요함
* 동기식 / 비동기식 요청 차이
- 동기식 : 요청 처리 후 해당하는 응답페이지가 돌아와야만 그 다음 작업이 가능
만약, 서버에서 응답페이지를 돌려주는 시간이 지연되면 무작정 기다려야 함
전체 페이지가 reload됨(새로고침, 즉 페이지가 기본적으로 한 번 깜빡거리면서 넘어감)
- 비동기식 : 현재 페이지는 그대로 유지하면서 중간중간마다 추가적인 요청을 보내줄 수 있고
요청을 한다고 해서 다음페이지로 넘어가지는 않음(현재 페이지 그대로 유지)
요청을 보내놓고 그에 해당하는 응답이 돌아올 때까지 현재 페이지에서 다른 작업을 할 수 있음
(페이지가 깜빡거리지 않음)
예) NAVER아이디중복체크 기능, 댓글, 검색어 자동완성 등
* 비동기식의 단점
- 페이지 내 복잡도가 기하급수적으로 증가 => 에러발생 시 디버깅이 어려움
- 요청 후 돌아온 응답데이터를 가지고 현재 페이지에서 새로운 요소를 동적으로 만들어서 뿌려줘야 함
=> DOM요소를 새롭게 만들어내는 구문을 잘 익혀둬야 함
* AJAX구현방식 : JavaScript방식 / JQeury방식
=> jQuery가 코드가 간결하고 사용하기 쉽다.
* JQuery에서의 AJAX 통신
[ 표현법 ]
$.ajax({
속성명 : 속성값,
속성명 : 속성값,
속성명 : 속성값,
...
});
=> AJAX기술이 가능하게 하는 객체를 인자값으로 넘긴다고 생각하자.
* 주요속성 (url/type/data/success는 꼭 기억)
- url : 요청할 url (필수작성) => form태그의 action속성
- type/method : 요청 전송 방식 (get/post 생략 시 기본값은 get) => form태그의 method속성
- data : 요청 시 전달할 값({key:value,key:value,...}) => form태그 안에 input태그에 입력한 값
- success : AJAX통신 성공 시 실행할 함수를 정의
- error : AJAX통신 실패 시 실행할 함수를 정의
- complete : AJAX통신을 성공이든 실패든 간에 무조건 끝나면 실행할 함수를 정의
(1) jQuery 방식을 이용한 AJAX 테스트
① 버튼 클릭 시 get방식으로 서버에 데이터 전송 및 응답
입력 : <input type="text" id="input1">
<button id="btn1">전송</button>
<br>
응답 : <label id="output1">현재 응답 없음</label>
- url : 매핑값
- data : key에는 name속성
- type : form태그의 method속성과 유사
- success : 성공했을 때 => 매개변수에 변수를 넣어 결과값을 받아줌
=> label의 content영역에 출력해주기
- error : 실패 시 실행할 구문
- complete : 성공/실패와 상관없이 실행할 구문
<script>
$(function(){
$('#btn1').click(function(){
//동기식 통신 : location.href = '요청url?쿼리스트링';
//비동기식 통신
$.ajax({
url : 'jqAjax1.do',
data : {input : $('#input1').val()},
type : 'get',
success : function(result){ //컨트롤러에서 받은 것을 받아줄 매개변수 result
console.log(result);
$('#output1').text(result);
},
error : function(){
alert('ajax통신 실패');
},
complete : function(){
console.log('테스트');
}
});
});
})
</script>
<AjaxController1>
1) GET방식 -> 인코딩 X
2) 값뽑기 : request.getParameter()
String str = request.getParameter("input");
- data에 담았던 key값으로 값을 뽑음
테스트 해보면,
System.out.println("테스트");
System.out.println("요청 시 전달 값 : "+str);
System.out.println("입력된 값 : " + str +", 길이 : " + str.length());
= > 페이지는 그대로 유지된 채 실행되고 있음
3) 가공, DB에 다녀오기 : 생략
4) 응답화면지정
(1) 혹시라도 응답데이터에 한글이 있을 경우를 대비해서 항상 응답데이터에 대해서 인코딩 설정하기
response.setContentType("text/html; charset=UTF-8");
(2) 응답 : response.getWriter() => jsp와의 통로를 열어준다 . (자바코드안에 html 코드 넣을 때 사용)
response.getWriter().print("입력된 값 : " + str +", 길이 : " + str.length());
② 버튼 클릭 시 post방식으로 서버에 데이터 전송 및 응답
이름 : <input type="text" id="input2_1"><br>
나이 : <input type="number" id="input2_2"><br>
<button onclick="test2();">요청</button>
<br>
응답 : <label id="output2">현재 응답 없음</label>
<script>
function test2(){
$.ajax({
url : 'jqAjax2.do',
data : {
name : $('#input2_1').val() ,
age : $('#input2_2').val()
},
type : 'post',
success : function(result){
console.log(result);
> JSONArray로 넘겼을 경우
$('#output2').text("이름 : " + result[0] + "나이 : " + result[1]);
배열형태로 넘겼을 때 가공한 것을 눈에 보여지게 하는 것은 View단의 역할(unboxing)
> JSONObject로 넘겼을 경우
자바스크립트 객체가 가지고 있는 속성값에 접근하는 두 가지 방법
객체명['속성명'], 객체명.속성명
$('#output2').text('이름 :' + result.name + ' 나이: ' + result.age);
$('#input2_1').val(''); //input태그 내 초기화
$('#input2_2').val('');
},
error : function(){
alert('AJAX 통신 실패');
}
});
}
</script>
- 태그 내 초기화 하는 이유? 입력하고 결과값을 출력하고 나서 input태그 내 입력값을 지워줌
<AjaxController2>
1) POST => 인코딩
request.setCharacterEncoding("UTF-8");
2) 값뽑기
String name = request.getParameter("name");
int age = Integer.parseInt(request.getParameter("age"));
3) VO로 가공 > Service 요청
4) 결과에 따른 응답화면 지정
- 결과에 한글이 있을 경우를 대비해 인코딩 설정(필수***)
response.setContentType("text/html; charset=UTF-8");
- 넘기기
response.getWriter().print(name, age);
==> 불가능, Ajax는 결과를 오로지 한 개만 응답할 수 있음
방법 1) 하나의 데이터로 만들어서 보내기
- 이름 : XXX, 나이: XX
String responseData = "이름: " + name + ", 나이 : " + age;
response.setContentType("text/html; charset=UTF-8");
response.getWriter().print(responseData);
방법 2) AJAX로 실제 값을 여러 개 보내고 싶을 때 => 정석
=> JSON (JavaScript Object Notation : 자바스크립트 객체 표기법)
: AJAX통신 시 데이터 전송에 이용되는 포맷형식 중 하나
- [value, value, value] => 자바스크립트의 배열 객체 => JSONArray
- {key:value, key:value, ...} => 자바스크립트의 일반 객체 => JSONObject
* JSON처리 시 사용하는 클래스 종류
=> 자바에서 기본적으로 제공X(라이브러리 필요 .jar)
다운로드 방법 -
http://code.google.com/archive/p/json-simple/downloads 접속 후 다운로드
json-simplte-1.1.1.jar 다운로드 후 WEB-INF\lib에 넣기

[1] JSONArray => [값1, 값2, ...] 배열 형태로 값을 넘길 수 있음
JSONArray jArr = new JSONArray();
- []빈 배열이 만들어짐(자바스크립트)
- 배열의 요소 추가 => add
jArr.add(name); //['홍길동']
jArr.add(age); //['홍길동', 150
- 인코딩 : application/json
response.setContentType("application/json; charset=UTF-8");
※ text/html로 넘기게 되면 문자열로 "["홍길동", 15]"이 전달됨 => 응답할 데이터의 컨텐트 타입을 지정해야 문자열형식으로 넘어가지 않는다.
- 보내기
response.getWriter().print(jArr)
[2] JSONObject => {key1 : value1, key2 : value2} 객체 형태로 값을 넘길 수 있음
- 객체 생성
JSONObject jObj = new JSONObject();
- 객체에 값 담기 => put메소드
jObj.put("name", name); //{name : "홍길동"}
jObj.put("age", age); //{name : "홍길동", age: 15}
- JSON타입의 값이라고 content type, 인코딩지정
response.setContentType("application/json; charset=UTF-8");
- 보내기
response.getWriter().print(jObj);
③ 서버로 데이터 전송 후 조회된 객체를 응답으로 받기
회원번호 입력 : <input type="number" id="input3" name="no">
<button onclick="test3();">조회 </button>
<div id="output3"></div>
<div>----ArrayList로 받기----</div>
<table id="output4">
<thead>
<th>번호</th>
<th>이름</th>
<th>나이</th>
<th>성별</th>
</thead>
<tbody>
</tbody>
</table>
- <tbody>에 추가해 줄 예정
> VO객체 하나만 보낸 케이스
function test3(){
$.ajax({
url : 'jqAjax3.do',
data : {no : $('#input3').val()},
success : function(result){
var resultStr = '회원번호: ' + result.userNo + '<br>'
+ '회원이름:' + result.userName + '<br>'
+ '나이: ' + result.age + '<br>'
+ '성별: ' + result.gender;
$('#output3').html(resultStr);
> ArrayList로 VO를 여러 개 묶어서 보낸 케이스
=>반복문으로 문자열을 이어서 만들기 (누적)
var resultStr2 = '';
for(var i = 0; i<result.length; i++){
resultStr2 += '<tr>'
+ '<td>' + result[i].userNo + '</td>'
+ '<td>' + result[i].userName + '</td>'
+ '<td>' + result[i].age + '</td>'
+ '<td>' + result[i].gender + '</td>'
+ '</tr>';
};
$('#output4 tbody').html(resultStr2);
},
error : function(){
console.log("실패");
}
});
}
<AjaxController3>
1) GET방식
2) request 값 뽑기
int memberNo = Integer.parseInt(request.getParameter("no"));
3) DB로부터 데이터를 조회했다라는 가정하에 Member객체에 값을 담기
Member m = new Member(memberNo, "고길동", 50, "남성");
4) m을 응답화면에 넘겨주기
(1) 형식, 인코딩 지정
response.setContentType("text/html; charset=UTF-8");
(2) 넘기기
response.getWriter().print(m);
*** 내부적으로 toString() 호출 문자열 형태로 값이 넘어감
JSON : 자바객체 => 자바스크립트 객체로 변환(JSONObject)
> JSONObject객체 생성
JSONObject jObj = new JSONObject(); //{}
jObj.put("userNo", m.getUserNo()); //{userNo : 1}
jObj.put("userName", m.getUserName()); //{userNo : 1, userName : "고길동"}
jObj.put("age", m.getAge()); //{userNo : 1, userName : "고길동", age : 50}
jObj.put("gender", m.getGender()); //{userNo : 1, userName : "고길동", age : 50, gender: "남성"}
response.setContentType("application/json; charset=UTF-8");
response.getWriter().print(jObj);
- 여러개의 VO객체가 들어있는 ArrayList넘기기
ArrayList<Member> list = new ArrayList();
list.add(new Member(1, "고길동", 50, "남성"));
list.add(new Member(2, "홍길동", 15, "남성"));
list.add(new Member(3, "김길동", 20, "여성"));
> JSONArray객체 생성
JSONArray jArr = new JSONArray();
for(Member member : list) {
JSONObject jObj = new JSONObject();
jObj.put("userNo", member.getUserNo()); //{userNo : 1}
jObj.put("userName", member.getUserName()); //{userNo : 1, userName : "고길동"}
jObj.put("age", member.getAge()); //{userNo : 1, userName : "고길동", age : 50}
jObj.put("gender", member.getGender()); //{userNo : 1, userName : "고길동", age : 50, gender: "남성"}
jArr.add(jObj);
==> 객체 배열의 경우 번거로움
GSON : Google JSON 라이브러리
** GSON라이브러리로 연동해야지만 가능
1) 형식, 인코딩 지정 => application/json;
response.setContentType("application/json; charset=UTF-8");
2) 객체생성, 3) gson.toJson() 호출
[표현법] gson.toJson(응답할 객체, 응답할 스트림)
gson.toJson(m, response.getWriter());
=> response.getWriter()이라는 통로로 m이라는 객체를 응답함
내가 명시적으로 키값을 제시하지 않았기 때문에 키값은 자동으로 필드명이 된다.
new Gson().toJson(list, response.getWriter());
2. 아이디 중복체크
1) 회원가입화면
<td><button type="button" onclick="idCheck();">중복확인</button></td>
<script>
function idCheck(){
//아이디 input태그부로부터 값을 뽑아와야 함 => input요소 자체를 먼저 접근
var $userId = $('#enroll-form input[name=userId]');
//name이 userId인 요소가 menubar.jsp에도 있기 때문에
//AJAX Controller로 요청하기
$.ajax({
url : 'idCheck.me',
data : {checkId : $userId.val()}, //key는 속성값으로
success : function(result){
//console.log(result);
//result 경우의 수 : "NNNNN" / "NNNNY"
//문자열 동등비교
if(result == 'NNNNN'){
alert('이미 존재하거나 탈퇴한 회원의 아이디입니다.');
//재입력
$userId.focus();
}else{
//알림창 => window.confirm()
if(confirm('사용가능한 아이디입니다. 사용하시겠습니까?')){
//중복확인 전에 막아두었던 submit버튼을 활성화해주기
$('#enroll-form button[type=submit]').removeAttr('disabled');
//아이디 같은 이후에 변경이 불가능하도록 => readonly
$userId.attr('readonly', true);
}else{
$userId.focus();
}
}
},
error : function(){
console.log("아이디 중복체크용 비동기요청 실패");
}
});
};
</script>
<AjaxIdCheckCotroller>
1) GET
2) request로부터 값뽑기
String checkId = request.getParameter("checkId");
3) VO가공
4) Service 요청 => memberService
int count = new MemberService().idCheck(checkId);
5) 결과에 따른 응답 : 화면 깜빡 XX
response.setContentType("text/html; charset=UTF-8");
ajax는 데이터만 돌려준다 => response.getWriter().print()
- 중복값이 있을 때 "NNNNN"
- 중복값이 없을 때 "NNNNY"
if(count>0) { //아이디 중복값이 있을 때 "NNNNN"
response.getWriter().print("NNNNN");
}else { //아이디 중복값이 없을 때 "NNNNY"
response.getWriter().print("NNNNY");
}
<MemberService>
public int idCheck(String checkId) {
Connection conn = JDBCTemplate.getConnection();
int count = new MemberDao().idCheck(conn, checkId);
JDBCTemplate.close(conn);
return count;
}
<MemberDao>
public int idCheck(Connection conn, String checkId) {
//SELECT문 => ResultSet => COUNT함수
//SELECT COUNT(*) FROM MEMBER WHERE USER_ID = ?
int count = 0;
PreparedStatement pstmt = null;
ResultSet rset = null;
String sql = prop.getProperty("idCheck");
try {
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, checkId);
rset = pstmt.executeQuery();
if(rset.next()) {
count = rset.getInt("COUNT(*)");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCTemplate.close(rset);
JDBCTemplate.close(pstmt);
}
return count;
}
3. 댓글조회
1) 댓글창 화면
<div id="reply-area" align="center">
<table border="1" align="center">
<thead>
<% if(loginUser!=null){ %>
<!-- 로그인 O -->
<tr>
<th>댓글작성</th>
<td>
<textarea id="replyContent" cols="50" rows="3" style="resize:none;"></textarea>
</td>
<td><button onclick='insertReply();'>댓글등록</button></td>
</tr>
<%} else{ %>
<!-- 로그인 X -->
<tr>
<th>댓글작성</th>
<td>
<textarea readonly cols="50" rows="3" style="resize:none;">로그인 후 이용가능한 서비스 입니다. </textarea>
</td>
<td><button>댓글등록</button></td>
</tr>
<%} %>
</thead>
<tbody>
</tbody>
</table>
</div>
- 자바 스크립트 영역 : ajax
function selectReplyList(){
$.ajax({
url : 'rlist.bo',
data : {bno : <%=b.getBoardNo()%>},
success : function(list){
//console.log(result)
//댓글 개수 만큼 반복 => 누적(문자열)
var result = '';
for(var i in list){
result += '<tr>'
+ '<td>' + list[i].replyWriter + '</td>'
+ '<td>' + list[i].replyContent + '</td>'
+ '<td>' + list[i].createDate + '</td>'
+ '</tr>'
}
$('#reply-area tbody').html(result);
},
error : function(){
console.log('댓글 읽어오기 실패');
}
});
};
<AjaxReplyListController>
1) GET => 인코딩 X
2) 값뽑기
int boardNo = Integer.parseInt(request.getParameter("bno"));
3) VO가공
4) Service요청
ArrayList<Reply> list = new BoardService().selectReplyList(boardNo);
5) GSON이용 => ArrayList를 자바스크립트의 객체 배열로 변환
(1) 형식, 인코딩 지정, (2) 넘기기
response.setContentType("application/json; charset=UTF-8");
new Gson().toJson(list, response.getWriter());
=> Gson따로 키값을 지정 안하면 키값 == 필드명
<BoardService>
public ArrayList<Reply> selectReplyList(int boardNo) {
Connection conn = getConnection();
ArrayList<Reply> list = new BoardDao().selectReplyList(conn, boardNo);
close(conn);
return list;
}
<BoardDao>
public ArrayList<Reply> selectReplyList(Connection conn, int boardNo) {
ArrayList<Reply> list = new ArrayList();
PreparedStatement pstmt = null;
ResultSet rset = null;
String sql = prop.getProperty("selectReplyList");
try {
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, boardNo);
rset = pstmt.executeQuery();
while(rset.next()) {
list.add(new Reply(rset.getInt("REPLY_NO"),
rset.getString("REPLY_CONTENT"),
rset.getString("USER_ID"),
rset.getString("CREATE_DATE")));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(rset);
close(pstmt);
}
return list;
}
4. 댓글작성
- JavaScript태그 작성
function insertReply(){
$.ajax({
url : 'rinsert.bo',
data : {
bno : <%=b.getBoardNo() %>,
content : $('#replyContent').val()},
//사용자 정보는 controller에서 세션에서 빼기
type : 'post',
success : function(result){
//console.log(result);
if(result > 0){
alert('댓글작성에 성공하였습니다.'); //알럿
$('#replyContent').val(''); //빈칸
selectReplyList(); //댓글조회
}
},
error : function(){
console.log('댓글작성실패');
}
});
<AjaxReplyInsertController>
1) POST => 인코딩
request.setCharacterEncoding("UTF-8");
2) 값뽑기
int boardNo = Integer.parseInt(request.getParameter("bno"));
String replyContent = request.getParameter("content");
int userNo = ((Member)request.getSession().getAttribute("loginUser")).getUserNo();
3) VO로 가공 => Reply
Reply r = new Reply();
r.setRefBno(boardNo);
r.setReplyContent(replyContent);
r.setReplyWriter(String.valueOf(userNo)); //String으로 변환
4) Service요청
int result = new BoardService().insertReply(r);
5) Gson, Json => 넘겨야할 값이 여러개일 때 묶을 때 , 넘겨야 할 값이 result 뿐이라 그냥 넘김
response.setContentType("text/html; charset=UTF-8");
response.getWriter().print(result);