클라우드 융합 Full-stack 웹 개발자 양성과정/Servlet, JSP

Servlet/JSP - AJAX(이론, 아이디 중복체크, 댓글 조회(GSON))

thesunset 2022. 11. 21. 17:24

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