1. 정의

Asynchronos JavaScript And XML

비동기 자바스크립트와 XML을 뜻함

html form을 이용하지 않고 자바스크립트를 통해 서버에 데이터를 요청하는 것

서버 측의 스크립트와 통신하기 위한 XMLHttpRequest 객체를 사용하는 것

서버에서 로딩된 데이터를 페이지에 보여주기 위해 새로운 html 페이지로 갈 필요도 없고, 새로고침을 할 필요도 없음

=> 부분 부분만 로딩되므로 다소 속도가 빠름(비동기성)

 

* Asynchronos : 비동기적

  클라이언트 > 서버로 요청시 요청을 보내놓고 프로그램은 계속 돌아간다는 의미

  먼저 요청한 것에 대한 callback 함수가 먼저 실행되지 않음

 

* 자바스크립트는 웹 브라우저에 대한 스크립트 언어이기 때문에 서버와의 소통 기능은 없으나 ajax는 소통이 가능함

 

* XML : Extensible Markup Language

 데이터 형식의 일종. 확장이 가능한 표시 언어

 

* Markup Language : 데이터에 태그로 항목 표시를 해줌

<person>

<name> ~ </name>

<age> ~ </age>

</person>

 

* XML > Ajax

 

독립적인 기술이라기 보다는, 여러 기술의 묶음을 지칭하는 용어에 가까움

=> html, css, DOM, JS, XML, XSLI, XPath, XMLHttpRequest 등의 기술들이 사용됨

 

마이크로소프트 사에서 처음으로 만든 프레임워크

 

* Ajax의 본래 명칭 : XHR (XML http Request). http request를 서버에 보내는 것

  xml은 JS에서 사용하기에는 다소 불편함 점이 있기 때문에 JS로 서버에 요청을 보낼 시에는 주로 JSON을 많이 사용함

 


* Ajax로 request를 여러개 만들었을 경우, 어떤게 먼저 돌아올지 알 수 없기 때문에 어떤 callback이 먼저 실행될지 알 수 없음

 

 

2. 순서

ㄱ. XHR 객체 생성 : XMLHttpRequest Object 생성

ㄴ. callback 생성

ㄷ. html 메소드, url 결정 : open a request

ㄹ. XHR 메소드로 정보 발송 : send a request

 

 

3. 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>ex02</title>
<script type="text/javascript">
    function loadDoc() {
//      ajax 사용하려면 브라우저 내장 함수로 XMLHttpRequest 객체 생성해야 함
//      (과거에는 ActiveXObject라는 생성자 사용하였음)
        var xhr = new XMLHttpRequest();
        
//      통신 후 성공, 실패에 따른 처리가 필요할 경우 서버가 응답했을 때 callback으로 호출되는 property에 함수를 할당하면 됨
//      1. XMLHttpRequest.onreadystatechange() 메소드 이용
//      2. XMLHttpRequest의 이벤트 핸들러를 이용
//      3. EventTarget.addEventListener() 메소드 이용
 
//      XMLHttpRequestEventTarget > XMLHttpRequest
//      <상속 받는 메소드>
//      - onabort() : abort 이벤트 발생시 호출
//      - onerror() : 서버 응답이 200이 아닐 때 호출
//      - onloadstart() : XHR 요청을 시작할 때 호출
//      - onloadend() : XHR 요청이 완료되면 호출됨. onload() 와 다르게 성공/실패 상관 없이 호출됨
//      - onprogres() : onloadstart()와 onloadend() 사이에 호출됨. 파라미터로 ProgressEvent를 전달 받으며 이 객체의 프로퍼티는
//                      브라우저마다 다를 수 있음
//      - onload() : XHR 요청이 성공적으로 완료되면 호출됨. XHR.status가 200일 때만 호출되는 메소드
//      - ontimeout : XHR.timeout으로 설정한 시간 내에 응답이 도착하지 않으면 호출됨.
//                    요청은 실패한 것으로 간주되며 onprogress()와 onload()는 호출되지 않음

 

//      onreadystatechange() : XMLHttpRequest의 프로퍼티로 콜백 함수를 저장함
//                             readyState의 값이 변할 때마다 호출되기 때문에 그대로 실행하면 안 됨
//                             (readyState의 값을 체크하는 조건 분기를 추가해야 원하는 결과를 얻을 수 있음)
        xhr.onreadystatechange = function() {
//          XHLHttpRequest.readyState : XMLHttpRequest의 현재 상태를 의미
//          - 0 : request not initialized
//          - 1 : server connection established
//          - 2 : request received
//          - 3 : progressing request
//          - 4 : request finished and response is ready
 
//          XHLHttpRequest.status : 응답 상태 코드를 숫자로 리턴함(200 : 정상)
            if (xhr.readyState == 4 && xhr.status == 200) {
                document.getElementById("demo").innerHTML = xhr.responseText;
            }
        };
        
//      XMLHttpRequest.open(method, url[, async])        
//      open() : Request의 유형을 지정함. send()로 메세지를 날리기 전, 어디에 어떤 방식으로 작동하는지 정하는 메소드
//               method, url은 필수지만 async는 기본값이 true로 생략할 수 있는 항목
//               async = false인 경우 send() 후 스크립트 진행을 중단하며 서버로부터 응답이 올 때까지 대기함
//      method : HTTP 메소드 타입(get, post)
//      url : 서버 경로
//      async : 비동기 여부(true, false)
        xhr.open("GET""ex03.jsp");
//      XMLHttpRequest.send([string]) : 폼 데이터로 전송할 문자열을 인자로 넣어 보냄
//      send() : 서버로 request 송신. open() 메소드에서 설정한 값에 따라 서버로 데이터를 요청함
//               string은 HTTP 메소드 타입이 POST일 경우에만 명시하며 폼 데이터로 간주됨
//               POST 방식일 때에는 setRequestHeader() 메소드로 컨텐츠 타입을 지정하지 않으면 서버에서 처리가 불가능함
//               string은 반드시 쿼리 스트링 형태로 작성되어야 함
        xhr.send();
    }
</script>
</head>
<body>
    <button onclick="loadDoc()">who r u</button>
    <div id="demo"></div>
</body>
</html>
cs

 

1
2
3
4
5
6
7
8
9
10
11
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>ex03</title>
</head>
<body>
    <h2>i am waldo</h2>
</body>
</html>
cs

 

 

 

<자바스크립트 + ajax를 이용한 예제>

'Framework > Spring' 카테고리의 다른 글

eGov :: 전자정부프레임워크란?  (0) 2017.06.21
파일 다운로드  (4) 2017.03.14
Framework :: JSON (JavaScript Object Notation)  (0) 2017.01.10
Framework :: Spring  (0) 2017.01.06
Framework :: JNDI vs JDBC(DBCP)  (0) 2017.01.02

1. 정의

JavaScript Object Notation

자바스크립트에서 데이터(객체)를 받아 할당하여 표현하는 자료 표현 방법

경량 데이터를 xml 보다 간단하게 표현하기 위해 만든 프레임워크

xml 보다 기능이 적기 때문에 파싱 속도도 상대적으로 빠르고 간단하여 클라이언트 사이드(ex. 모바일)에서 유용하게 쓰임

name/value가 쌓인 컬렉션 타입으로 이루어져 있음

Ajax로 서버와 통신시 데이터를 주고 받을 때 데이터 교환을 쉽게 하기 위해 JSON을 많이 사용함

 

* 공식 미디어 타입 : application/json

확장자 : .json

 

* JSON은 length를 알 수 없는 연관배열을 표현하기에 유리하다

=> {key : value}

 

 

2. 장점

주고 받는 데이터의 타입에 제한이 없음

텍스트로 이루어져 있어 사람과 기계 간 둘 다 읽고 쓰기가 쉬움

언어나 플랫폼에 독립적이라 서로 다른 시스템 간에 객체 교환이 쉬움

자바스크립트 문법을 채용했기 때문에 eval() 함수를 이용하여 바로 사용 가능

 

 

3. 사용처

ㄱ. 구성 정보 표시

ㄴ. 통신 프로토콜 실행

 

 

3. xml vs JSON 비교

 

 xml

 JSON

 정의

 구조화된 문서를 전송 가능하게 만든 텍스트 포맷 형식

 장점

 - 태그 구조이기 때문에 작성이 간편함

 가독성이 좋음

 - DTD 등 xml 자체의 기능 확장성

 - 최소한의 정보만 가지고 있음

 => 속도 증가

 - 객체구조 {} / 배열구조 []로 효율적인 데이터 구성 가능

 - parsing이 간편함

 - 일반적인 변수처럼도 사용 가능

 단점

 - 태그 글자라 문서 양이 많아짐

 - 배열, 반복 구조인 경우 불필요한 데이터가 많이 생김

 => parsing 속도 저하

 - 내용의 의미 파악이 힘듦(함축적이라)

 - xml보다는 빠르지만 대용량 전송에는 부적함

 

* xml : Extensible Markup Language

 

 

4. 자료 표현 종류 (대표적으로 분류)

종류

표현법

객체

{Object}

배열

[Array]

변수

name(String) : value

 

 

5. 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>json example</title>
</head>
<body>
    name : <span id="name"></span><br>
    age : <span id="age"></span><br>
    address : <span id="street"></span><br>
    phone : <span id="phone"></span><br>
</body>
<script type="text/javascript">
var employees = {
        "name" : "홍길동",
        "street" : "서울시 서초구 서초동",
        "age" : 33,
        "phone" : "010-1234-5678"};
        
        document.getElementById("name").innerHTML = employees.name;
        document.getElementById("age").innerHTML = employees.age;
        document.getElementById("street").innerHTML = employees.street;
        document.getElementById("phone").innerHTML = employees.phone;
</script>
</html>
cs

 

 

<JSON을 이용하여 연관 배열 데이터를 주고 받는 예제>

'Framework > Spring' 카테고리의 다른 글

파일 다운로드  (4) 2017.03.14
Ajax :: Ajax (Asynchronos JavaScript And XML)  (0) 2017.01.10
Framework :: Spring  (0) 2017.01.06
Framework :: JNDI vs JDBC(DBCP)  (0) 2017.01.02
JDBC :: 드라이버 종류  (0) 2017.01.01

1. 정의

자바 플랫폼을 위한 오픈 소스 애플리케이션 프레임워크

자바 개발을 편하게 할 수 있는 오픈 소스 경량급 애플리케이션 프레임워크

자바 개발을 위한 프레임워크로, 종속 객체를 생성해주고 조립해주는 도구

POJO(Plain Old Java Object)를 자바EE에 의존적이지 않게 연결해주는 역할

 

 

2. 특징

크기, 부하 측면에서 경량

IoC(제어 역행)을 통해 애플리케이션 간의 느슨한 결합을 도모함

AoC(관점지향) 프로그래밍

애플리케이션 생명주기를 관리함

1. JNDI

Java naming and directory interface

디렉토리 서비스에서 제공하는 데이터나 객체를 발견하고 참고(look)하기 위한 자바 API.

DB Pool을 미리 Naming 시켜놓는 방법. 외부에 있는 객체를 가져오기 위한 기술.

클래스에 이름을 부여하고 원격으로 접속하여 클래스 사용시 클래스의 다른 이름 역할을 함.

Tomcat과 같은 WAS를 보면 특정 폴더에 필요한 라이브러리가 있는데, 그것을 사용하기 위해 JNDI를 사용하여 가져온다.

(WAS가 부팅시 JNDI 객체를 등록함)


* JNDI를 사용하던 하지 않던, JDBC는 무조건 필요하다


* Naming Service  종류

- LDAP (Lightweight Directory Access Protocol) : 네트워크 디렉토리 서비스 표준인 x.500을 위한 간단한 형태 제공

- DNS (Domain Name System) : 인터넷 네이밍 시스템. 도메인을 IP 주소로 변환함. 대규모 분산 DB 서버 구조

- NIS (Network Information System) : SUN에서 개발된 네트워크 네이밍 서비스

- CosNaming (Common Object Service) : 코바 응용프로그램을 위한 네이밍 서비스 지원

- JNDI : SUN에서 개발된 자바 기반 객체 등록 서비스. JNDI SPI (Service Provider Interface) 제공

JNDI 서버의 실제 구현 기능은 각 Application Server의 vender가 관리함



2. JDBC (DBCP)

Java database connectivity (Database connection pool)

자바 데이터베이스 연결. 데이터베이스와 연결하기 위한 자바 인터페이스.

DB와 커넥션을 맺고 있는 객체를 관리하는 커넥션풀.


* DBCP : JDBC를 이용하여 생성한 커넥션의 효율성을 높이기 위해 커넥션 객체를 관리하는 것


ㄱ. 프로세스

DriverManager 통한 DB Connection 객체 생성

> Connection 객체에서 statement 객체 생성, 쿼리 실행

> ResultSet

> Connection close


'Framework > Spring' 카테고리의 다른 글

Framework :: JSON (JavaScript Object Notation)  (0) 2017.01.10
Framework :: Spring  (0) 2017.01.06
JDBC :: 드라이버 종류  (0) 2017.01.01
Spring :: STS(Spring tool suite) 설치  (0) 2017.01.01
MyBatis :: Paging 페이징 구현  (7) 2016.07.23

JDBC 드라이버 종류에는 4가지가 있는데, 변천사로 구분된다.


1. Type 1

JDBC - OJDBC Bridge Driver

표준 드라이버 호출들을 대응되는 ODBC 호출로 변환하고, 운영 시스템이 ODBC(Open Database Connectivity) 라이브러리를 통해 ODBC 데이터 소스로 전송한다

JDBC > ODBC > ODBC Driver 순으로 호출로 변환한다

클라이언트에 ODBC 드라이버 + JDBC 드라이버 둘 다 있어야 한다 => 비효율적, 제한적

시스템이 여러 단계를 거쳐 DB 호출을 전달할 뿐 아니라 JDBC 기능은 어떤 ODBC 드라이버라도 다룰 수 있는 것으로만 제한된다

=> 동적으로 드라이버를 다운로드 하는 환경에서는 사용이 어려움



2. Type 2

Native-API Driver : OCI Driver(Partly Java Driver)

일부가 자바로 되어 있는 JDBC 드라이버 (= 자바 드라이버를 사용하여 특정 업체의 API와 통신함)

Type1에서 ODBC API 변환 단계가 생략된 것을 제외하고는 비슷하다


API를 통해 결과를 돌려줄 때, JDBC 드라이버는 JDBC 표준을 따르는 결과를 만들어 프로그램으로 리턴

> 클라이언트에 설치된 로컬 드라이버(DB 만든 벤더가 제공하는 타 형태의 드라이버)가 필요

> oracle, DB2 등 DBMS 벤더들이 제공하는 API를 다시 호출


하게 되는데, Type1과 마찬가지로 클라이언트의 컴퓨터에 정적으로 드라이버를 설치하여 DB와 연동하는 환경에 적합하다



3. Type 3

Net-Protocol Driver (All Java Driver)

완전한 자바 기술. 코드로 이루어진 JDBC 드라이버

JDBC API 표준에 의해 만들어졌기 때문에  DBMS 종류에 관계 없이 사용이 가능하다

DBMS와 독립적인 Net-Protocol을 사용하여 미들웨어 계층의 서버와 통신하고, 이 때 미들웨어 서버는 게이트웨이 역할을 한다

4가지 타입 중 가장 융통성이 뛰어난 타입이기 때문에 동적으로 다운로드 되는 Applet 같은 환경에 더욱 적합하다

클라이언트가 정적으로 이루어졌다고 해도 Type3 사용시 유지보수가 쉬워진다

특별한 요구 사항이 필요한 경우 빼고는 Type3이 적절하다



4. Type4

Native-Protocol Driver (thin Driver)

완전한 자바 기술. 코드로 이루어진 JDBC 드라이버를 의미한다


* Type 3과의 차이점

DBMS 벤더가 표준을 기준으로 기능을 추가/삭제하여 독자적인 형태로 만든 타입이다

> 특정 DBMS에 의존적인 반면, 해당 DBMS만이 제공하는 기능을 사용 가능한 장점이 있다

> 4가지 Type의 드라이버 중 DB에 가장 효율적으로 접근하는 방법을 제공한다

> 추가로 설치해야 할 라이브러리 미들웨어가 필요 없어 배포하기에도 가장 손쉬운 방법으로 제공한다

> 클라이언트가 동적인 환경에서 특정 DBMS만이 제공하는 기능들을 사용할 경우 적합하다

STS는 이클립스 기반의 스프링 개발 플러그인이 설치되어 있는 패키지이다.


Spring tool suite : 스프링 개발 도구

vFabric tc server : 톰캣을 클러스터링해서 사용할 수 있도록 만든 서버

Spring roo : 웹 개발을 간편화시킨 기술

Apache maven : 빌드 도구


STS를 설치하면 위의 4가지도 같이 설치되는데, Spring에서 주로 사용되는 기술이다.



1. STS 사이트 접속


http://spring.io/ 로 접속한다.



2. TOOLS 클릭

해당 페이지의 가장 하단으로 내려가 TOOLS를 클릭한다.



3. download 클릭



4. 사양에 따라 .zip 파일 다운로드


1. com.paging 패키지에 들어가는 Paging.java 소스

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package com.paging;
 
public class Paging {
 
    int recordsPerPage;       // 페이지당 레코드 수
    int firstPageNo;          // 첫번째 페이지 번호
    int prevPageNo;           // 이전 페이지 번호
    int startPageNo;          // 시작 페이지 (페이징 너비 기준)
    int currentPageNo;        // 페이지 번호
    int endPageNo;            // 끝 페이지 (페이징 너비 기준)
    int nextPageNo;           // 다음 페이지 번호
    int finalPageNo;          // 마지막 페이지 번호
    int numberOfRecords;      // 전체 레코드 수
    int sizeOfPage;           // 보여지는 페이지 갯수 (1,2,3,4,5 갯수)
    
    public Paging(int currentPageNo, int recordsPerPage) {
 
        this.currentPageNo = currentPageNo;
        //기본 페이지 : 5개 보기를 default로 설정함
        this.sizeOfPage = 5;
        
        //recordsPerPage가 0이 아니면 recordsPerPage, 아니면 무조건 5(default : 5)
        this.recordsPerPage = (recordsPerPage != 0) ? recordsPerPage : 5;
        
//        System.out.println("debug > paging sizeOfPage : " + this.sizeOfPage);
//        System.out.println("debug > paging recordsPerPage : " + this.recordsPerPage);
//        System.out.println("debug > paging currentPageNo : " + this.currentPageNo);
    }
    
    public int getRecordsPerPage() {
        return recordsPerPage;
    }
 
    public void setRecordsPerPage(int recordsPerPage) {
        this.recordsPerPage = recordsPerPage;
    }
 
    public int getFirstPageNo() {
        return firstPageNo;
    }
 
    public void setFirstPageNo(int firstPageNo) {
        this.firstPageNo = firstPageNo;
    }
 
    public int getPrevPageNo() {
        return prevPageNo;
    }
 
    public void setPrevPageNo(int prevPageNo) {
        this.prevPageNo = prevPageNo;
    }
 
    public int getStartPageNo() {
        return startPageNo;
    }
 
    public void setStartPageNo(int startPageNo) {
        this.startPageNo = startPageNo;
    }
 
    public int getCurrentPageNo() {
        return currentPageNo;
    }
 
    public void setCurrentPageNo(int currentPageNo) {
        this.currentPageNo = currentPageNo;
    }
 
    public int getEndPageNo() {
        return endPageNo;
    }
 
    public void setEndPageNo(int endPageNo) {
        this.endPageNo = endPageNo;
    }
 
    public int getNextPageNo() {
        return nextPageNo;
    }
 
    public void setNextPageNo(int nextPageNo) {
        this.nextPageNo = nextPageNo;
    }
 
    public int getFinalPageNo() {
        return finalPageNo;
    }
 
    public void setFinalPageNo(int finalpageNo) {
        this.finalPageNo = finalpageNo;
    }
 
    public int getNumberOfRecords() {
        return numberOfRecords;
    }
 
    public void setNumberOfRecords(int numberOfRecords) {
        this.numberOfRecords = numberOfRecords;
    }
 
    /**
     * 페이징 생성
     */
    public void makePaging() {
        if (numberOfRecords == 0)        // 게시글 전체 수가 없는 경우
            return;
        
        if (currentPageNo == 0)
            setCurrentPageNo(1);        // 기본 값 설정
        
        if (recordsPerPage == 0)
            setRecordsPerPage(10);        // 기본 값 설정
        
                                        // 마지막 페이지
        int finalPage = (numberOfRecords + (recordsPerPage - 1)) / recordsPerPage;
        
        if (currentPageNo > finalPage)
            setCurrentPageNo(finalPage);// 기본 값 설정
        
        if (currentPageNo < 0 || currentPageNo > finalPage)
            currentPageNo = 1;            // 현재 페이지 유효성 체크
                                        // 시작 페이지 (전체)
        boolean isNowFirst = currentPageNo == 1 ? true : false;
        boolean isNowFinal = currentPageNo == finalPage ? true : false;
        
        int startPage = ((currentPageNo - 1/ sizeOfPage) * sizeOfPage + 1;
        int endPage = startPage + sizeOfPage - 1;        
        
        if (endPage > finalPage)
            endPage = finalPage;
        
        setFirstPageNo(1);                    // 첫번째 페이지 번호
        
        if (isNowFirst)
            setPrevPageNo(1);                // 이전 페이지 번호
        else                                // 이전 페이지 번호
            setPrevPageNo(((currentPageNo - 1< 1 ? 1 : (currentPageNo - 1)));
 
        setStartPageNo(startPage);            // 시작페이지
        setEndPageNo(endPage);                // 끝 페이지
        
        if (isNowFinal)
            setNextPageNo(finalPage);        // 다음 페이지 번호
        else
            setNextPageNo(((currentPageNo + 1> finalPage ? finalPage : (currentPageNo + 1)));
        
        setFinalPageNo(finalPage);            // 마지막 페이지 번호
    }
}
cs

 

Paging.java 클래스 생성자에서 기본적으로 받아와야 할 값은

currentPageNo(현재 페이지), recordsPerPage(페이지당 게시글 수) 이렇게 두 인자가 필요하다.

생성자에서 인자 두 개를 받아 controller에서 컬럼의 인덱스를 리턴하는 offset 변수를 선언하는데 쓰게 된다.

 

 

2. controller (Servlet)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package com.servlet.book;
 
import java.io.IOException;
import java.util.List;
 
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.paging.Paging;
import com.service.BookManageService;
import com.vo.BookVO;
 
@SuppressWarnings("serial")
@WebServlet("/BookCheckOutAdmin")
public class BookCheckOutAdminServlet extends HttpServlet {
       
    public BookCheckOutAdminServlet() {
        super();
    }
 
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
                                throws ServletException, IOException {
        doPost(request, response);
    }
 
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
                                throws ServletException, IOException {
 
        // Paging Calculation
        int currentPageNo = 1;        
        int recordsPerPage = 0;
        String url = null;
        
        //커넥션풀 연결, 인스턴스 생성
        BookManageService bookManageService = BookManageService.getInstance();
        
        //pages, lines 파라미터를 받아 currentPageNo, recordsPerPage 대입
        //처음 페이지 열릴 때에는 당연히 1, 0임
        if (request.getParameter("pages"!= null)
            currentPageNo = Integer.parseInt(request.getParameter("pages"));
        
        if (request.getParameter("lines"!= null)
            recordsPerPage = Integer.parseInt(request.getParameter("lines"));
        
        //Paging 객체 생성(currentPagenNo, recordsPerPage를 인자로 넣고 초기화함)
        //객체 선언한 뒤 paging 출력해보면 recordsPerPage가 5로 나온다. 
        Paging paging = new Paging(currentPageNo, recordsPerPage);
        
        //System.out.println("debug > BookCheckOutAdminServlet paging : " + paging);
        
        //해당 게시글의 인덱스를 구하는 변수(offset) 
        int offset = (paging.getCurrentPageNo() - 1* paging.getRecordsPerPage();    
        
        //System.out.println("debug > BookCheckOutAdminServlet offset : " + offset);
        
        //현재 대출 가능한 도서(book_lending_possible이 true인 목록만 가져옴)
        List<BookVO> bookList = bookManageService.selectAllLendingPossibleBook(offset,
                                            paging.getRecordsPerPage());        
       
        //System.out.println("debug > BookCheckOutAdminServlet bookList : " + bookList);
 
       //bookList 전체 갯수 구하여 numberOfRecords 메소드에 셋팅함 
        paging.setNumberOfRecords(bookManageService.getBookDao().getNoOfRecords());
 
        //System.out.println("debug > BookCheckOutAdminServlet numberOfRecords : "
        //                            paging.getNumberOfRecords);
 
       //페이징 만듦 
        paging.makePaging();    
        
        //bookList가 존재할 경우
        if (bookList != null) {
            //book_check_out_list.jsp에서 출력할 객체들을 request 단위로 설정함
            request.setAttribute("bookList", bookList);
            request.setAttribute("paging", paging);
            request.setAttribute("servletPath""BookCheckOutAdmin");
 
            //System.out.println("debug > BookCheckOutAdminServlet attribute bookList : "
            //                            + request.getAttribute("bookList"));
            //System.out.println("debug > BookCheckOutAdminServlet attribute paging : "
            //                            + request.getAttribute("paging"));
            //System.out.println("debug > BookCheckOutAdminServlet servletPath : "
            //                            + request.getAttribute("servletPath"));
 
            url = "book_check_out_list.jsp";            
        }
        //list가 존재하지 않을 경우
        else {
            //error_msg.jsp에서 출력할 객체들을 request 단위로 설정함
            request.setAttribute("msg""Error 가 발생했습니다.");   
 
            //System.out.println("debug > BookCheckOutAdminServlet attribute msg : "
            //                            + request.getAttribute("msg"));
         
            url = "error_msg.jsp";
        }        
         
        //해당 url로 모든 request, response 정보 넘김
        //http 창에서 url이 이동하는 url로 변하진 않음
        request.getRequestDispatcher(url).forward(request, response);
    }
}
 
cs

 

페이징을 할 때에는 SELECT 쿼리문에서 LIMIT 를 사용하기 때문에

LIMIT에 들어갈 컬럼의 인덱스 값과 SELECT할 컬럼의 갯수가 필요하다.

currentPage(현재 페이지), recordsPerPage로 설정해 놓고 Paging 객체 생성자에 두 값을 넣어

컬럼의 인덱스(offset)를 도출한다.

 

* 컬럼의 index 구하기 : (현재 페이지 - 1 ) * 페이지당 SELECT할 컬럼 수

 

편의에 맞게 '~개 보기'로 하여 recordsPerPage의 값이 변할 수 있다.

 

* 페이징을 할 때 꼭 알아야 할 값 (중요) *

- 현재 페이지

- 한 페이지 당 출력할 컬럼 갯수

- 전체 컬럼 수

 

전체 컬럼 수를 알아야 페이지가 몇 개가 나올 것인지를 알 수 있기 때문에 전체 컬럼 수는 꼭 필요하다.

그러나 컬럼은 편의에 따라 추가하거나 삭제될 수 있기 때문에

SELECT 쿼리문으로 해당 list 를 구한 후 COUNT를 도출해야만 한다.

 

(list를 구한 후 bookDAO.getNoOfRecords() 메소드를 이용하여 numberOfRecords를 setter한 뒤,

페이징을 만드는 메소드인 paging.makepaging() 을 실행하였음)

 

또한 반드시 setAttribute로 설정해줘야 list.jsp에서 paging 파라미터들을 사용할 수 있다.

 

 

3. Service

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.service;
 
import java.util.List;
 
import com.dao.BookDao;
import com.vo.BookVO;
 
public class BookManageService {
 
    static BookManageService    bookManageService = null;
    BookDao                     bookDao;
    BookRentInfoDao             bookRentInfoDao;
    
    public BookManageService() {
 
        bookDao = BookDao.getInstance();

    }

    
    //BookDao를 리턴하는 getBookDao 메소드
    public BookDao getBookDao() {
 
        return bookDao;
    }
    
    public static BookManageService getInstance() {
        
        if (bookManageService == null) {
            bookManageService = new BookManageService();
        }
        
        return bookManageService;
    }
 
    public List<BookVO> selectAllLendingPossibleBook(int offset, int noOfRecords) {
        
        return bookDao.selectAllLendingPossibleBook(offset, noOfRecords);
    }
}
cs

 

Controller에서 Service의 selectAllLendingPossibleBook 메소드를 호출한 뒤

Service 클래스에서 DAO 클래스를 호출한다.

 

디폴트 생성자를 통해 인스턴스를 초기화했다.

 

* Service 클래스 *

- DAO 클래스를 바로 호출하지 않고, Service가 DAO를 호출하게 함.

- 1개의 Service는 1개 이상의 DAO 클래스를 실행 가능하게 한다.

 (대형 프로젝트에서는 1개의 Service가 3개 이상의 DAO를 실행 가능하게 하기도 함)

- 메뉴 1개당 보통 Servlet 1개, DAO 1개, Service 1개, sql 1개가 존재함.

- 객체지향에 따라 역할에 맞게 분류함.

 

 

4. DAO

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.dao;
 
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
 
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.log4j.Logger;
 
import com.mybatis.MyBatisConnectionFactory;
import com.vo.BookVO;
 
public class BookDao implements BookDaoImpl {
 
    static BookDao        bookDao;
    SqlSessionFactory    sqlSessionFactory;
    final static Logger log = Logger.getLogger(BookDao.class);
    
    int             noOfRecords;
    
    public BookDao(SqlSessionFactory sqlSessionFactory) {
 
        this.sqlSessionFactory = sqlSessionFactory;
    }
 
    public int getNoOfRecords() {
 
        return noOfRecords;
    }
    
    @Override
    public List<BookVO> selectAllLendingPossibleBook(int offset, int noOfRecords) {
        
//        log.debug("selectAllLendingPossibleBook offset : " + offset + 
//                            " , noOfRecords : " + noOfRecords);
        
        List<BookVO> bookList = new ArrayList<BookVO>();
        SqlSession session = sqlSessionFactory.openSession();
        
        HashMap<String, Object> params = new HashMap<String, Object>();
 
        params.put("offset", offset);
        params.put("noOfRecords", noOfRecords);        
        
//        log.debug("selectAllLendingPossibleBook params : " + params);
        
        try {
            bookList = session.selectList("BookDAO.selectAllLendingPossibleBook", params);
            this.noOfRecords = session.selectOne("BookDAO.selectTotalRecords");
            
//            log.debug("selectAllLendingPossibleBook bookList : " + bookList);
//            log.debug("selectAllLendingPossibleBook noOfRecords : " + this.noOfRecords);
            
        } finally {
            session.close();
        }
        
        return bookList;
    }
cs

 

DAO 클래스에서 offset, noOfRecords 인자를 받아 쿼리를 구현한다.

 

받은 인자들을 쿼리문으로 보낼 때 HashMap을 쓴 이유는

MyBatis에서 인자를 2개 이상 보내기 위한 방법은 HashMap 밖에 없기 때문이다.

 

보내야 할 인자가 1개면 단순하게 변수를 보내면 끝이지만,

2개 이상인 경우 반드시 HashMap 객체를 써야 한다.

 

쿼리를 bookList 인스턴스에 담은 후

전체 bookList 갯수를 구하는 쿼리를 실행하여 noOfRecords 변수를 초기화한다.

 

(일전에 앞에서 list를 구한 뒤 count를 구해야 한다고 앞서 적은 바 있다.)

 

 

5. DAOImpl

 

1
2
3
4
5
6
7
8
9
10
package com.dao;
 
import java.util.List;
 
import com.vo.BookVO;
 
public interface BookDaoImpl {
 
    List<BookVO>        selectAllLendingPossibleBook(int offset, int noOfRecords);
}
cs

 

 

6. mapper.xml

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<mapper namespace="BookDAO">
 
    <resultMap id="resultBook" type="BookVO">
        <result property="bookCode" column="book_code" />
        <result property="bookName" column="book_name" />
        <result property="bookAuthor" column="book_author" />
        <result property="bookMade" column="book_made" />
        <result property="bookPrice" column="book_price" />
        <result property="bookLendingPossible" column="book_lending_possible" />
        <result property="bookLendingCount" column="book_lending_count" />        
    </resultMap>
 
    <select id="selectAllLendingPossibleBook" parameterType="hashmap" resultMap="resultBook">
        SELECT SQL_CALC_FOUND_ROWS
                book_code,
                book_name,
                book_author,
                book_made,
                book_price,
                book_lending_count
        FROM book
        WHERE book_lending_possible is true
        LIMIT #{offset}, #{noOfRecords}        
    </select>
 
    <select id="selectTotalRecords" resultType="int">
        SELECT FOUND_ROWS();
    </select>
 
</mapper>
cs

 

FOUND_ROWS() : total count를 구할 때 한 번의 쿼리만으로 처리 가능.

 count를 쓰면 두 번의 쿼리를 날려야 하므로 과부하를 유발한다.

 

SQL_CALC_FOUND_ROWS를 쓰고 FOUND_ROWS() 를 쓰면

앞 쿼리에 썼던 SELECT한 전체 갯수를 FOUND_ROWS()를 쓴 쿼리에서 반환한다.

 

(이전 쿼리에 LIMIT가 있는 경우, LIMIT 부분은 무시하고 SELECT된 전체 갯수를 반환한다.)

 

FOUND_ROWS()로 전체 갯수를 구하는 이유 : 페이징의 필수 요소이기 때문에

(실제로 DAO 클래스에서 쿼리 부분을 커멘트 하면 페이징이 되지 않음)

 

 

7. import 될 paging.jsp

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
 
<div class="paginate">
 
    <c:if test="${param.currentPageNo ne param.firstPageNo}">
        <a href="javascript:goPage('${servletPath}', ${param.prevPageNo}, ${param.recordsPerPage})" class="prev">이전</a>
    </c:if>
    
    <span>
        <c:forEach var="i" begin="${param.startPageNo}" end="${param.endPageNo}" step="1">
            <c:choose>
                <c:when test="${i eq param.currentPageNo}">
                    <b><font size=+1>
                            <a href="javascript:goPage('${servletPath}', ${i}, ${param.recordsPerPage})" class="choice">${i}</a>
                        </font>
                    </b>
                </c:when>
                <c:otherwise>
                    <a href="javascript:goPage('${servletPath}', ${i}, ${param.recordsPerPage})">${i}</a>
                </c:otherwise>
            </c:choose>
        </c:forEach>
    </span>
    
    <c:if test="${param.currentPageNo ne param.finalPageNo}">
        <a href="javascript:goPage('${servletPath}', ${param.nextPageNo}, ${param.recordsPerPage})" class="next">다음</a>
    </c:if>
 
</div>
cs

 

 

8. paging.jsp 에서 이동하는 javascript goPage 함수

 

1
2
3
4
5
6
function goPage(url, pages, lines) {
    
    url += '?' + "pages=" + pages + "&lines=" + lines;
    
    location.href = url;    
}
cs

 

 

9. list.jsp

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>main</title>
 
<script type="text/javascript" src="./js/script.js"></script>
 
</head>
<body>
 
        <jsp:include page="paging.jsp" flush="true">
            <jsp:param name="servletPath" value="${servletPath}" />
            <jsp:param name="recordsPerPage" value="${paging.recordsPerPage}" />
            <jsp:param name="firstPageNo" value="${paging.firstPageNo}" />
            <jsp:param name="prevPageNo" value="${paging.prevPageNo}" />
            <jsp:param name="startPageNo" value="${paging.startPageNo}" />
            <jsp:param name="currentPageNo" value="${paging.currentPageNo}" />
            <jsp:param name="endPageNo" value="${paging.endPageNo}" />
            <jsp:param name="nextPageNo" value="${paging.nextPageNo}" />
            <jsp:param name="finalPageNo" value="${paging.finalPageNo}" />
        </jsp:include>
 
</body>
</html>
cs

 

paging.jsp로 작성한 소스를 jsp 액션 태그 include로 불러오고,

Controller에서 request 단위로 setAttribute 설정한 paging 객체를 불러온다. 

 

 

 

실제로 프로젝트를 구현하면 위와 같이 페이징이 되고,

 

 

recordsPerPage 값을 몇으로 설정하냐에 따라서 SELECT 쿼리문에 들어가는 LIMIT의 값이 바뀌게 된다.

(option 값 변화에 따라 LIMIT가 바뀌는 부분은 본문에 넣지 않았다.)

 

 

콘솔 창에서 이런 오류가 발생했을 때에는

mapper 파일의 namespace 경로와 mapper 파일 경로의 select, insert 등 쿼리문의 경로가 일치하지 않아서 생기는 오류이다.

 

 

 

mapper.xml 파일에서 namespace를 트랜잭션 경로인 PersonDAO로 기입했어야 하는데

PersonVO로 기입했어서 계속 오류가 났었다. 주의하기.

'Framework > Spring' 카테고리의 다른 글

Spring :: STS(Spring tool suite) 설치  (0) 2017.01.01
MyBatis :: Paging 페이징 구현  (7) 2016.07.23
MyBatis :: selectList와 selectOne  (3) 2016.02.15
MyBatis :: ConnectionFactory  (0) 2016.01.19
MyBatis :: 동적 sql (=쿼리)  (0) 2016.01.19

간단한 게시판 프로젝트를 만들던 와중, contents.jsp에서 오류가 나서 그 오류를 살펴보았다.

 

 

24번째 라인에서 오류가 났다는 건 personVO = personDao.selectById("id", "DESC"); 부분에서 오류가 났다는 것이었다.

 

 

오류 내용 : Type mismatch : cannot conver from List<PersonVO> to PersonVO

List<PersonVO> 형을 PersonVO로 변환할 수 없다는 내용의 오류였다.

 

 

PersonDao.java 파일을 살펴봤다.

HashMap을 사용하여 List<PersonVO>에 넣었는데, 오류가 형 변환이라길래 단순히 List<PersonVO>를 PersonVO로 변환하면 오류가 해결되지 않을까 해서

 

personVO = (PersonVO)personDao.selectById("name", "DESC");

 

로 바꿔봤다.

 

 

 

코드상으로는 오류가 나지 않으나 컴파일을 시키니 또 형 변환 오류가 났다. 이게 아닌 것 같다..

 

오류를 해결할 수 있는 키워드가 '형 변환'인 것만은 확실하다.

 

PersonDao.java 파일을 유심히 보다가, 내가 출력하려는 contents.jsp 페이지의 목적이 list.jsp에서 클릭하는 사람의 정보 하나만 가져오는 것이기 때문에 personDao.selectById 메소드는 selectOne 메소드를 사용하여 List<PersonVO>가 아닌 단일 정보인 PersonVO를 출력하는 것으로 고쳐야 한다.

 

 

contents.jsp 의 수정된 부분 코드.

 

 

내가 오류의 문제를 잘 못 이해하고 있었던 것.

list.jsp에서 클릭하는 한 명의 정보만 선택되어야 하기 때문에 selectOne 메소드를 사용해야 한다.

 

오류 해결.

그러나 오류는 또 다른 오류를 생성하고.. 이름이 왜 null로 표기되는지 또 다른 오류 해결을 위하여.

 

 

* selectOne vs selectList 비교

1. selectOne : 쿼리 결과가 없으면 null을 반환

   쿼리 결과로 레코드가 하나만 나와야 한다

   DB에 레코드가 하나만 있는 경우에 사용 => 다수의 레코드가 있을 시 TooManyResultsException 에러 발생

 

2. selectList : 쿼리 결과를 List<E>로 반환

  결과가 없을 시에는 빈 List를 반환한다(null로 반환하지는 않는다)

 

 

자바 메소드로 매개변수 받는 법을 더 연습해야겠다.

데이터베이스에 접속하기 위해서는 커넥션을 생성해야 함

그런데 자주 데이터베이스에 접속하고, 접속하는 데이터베이스가 다르고, 접속하는 사람들이 달라지면 접속할 때마다 새롭게 코딩을 해야 하는 일이 발생한다.

 

이러한 불편함을 덜고 클래스의 재사용성을 높이기 위해 커넥션 공장(Connection Factory)를 만들게 됨

 

커넥션을 구현할 때마다 커넥션 생성 구문을 사용하는게 아니라, 커넥션 팩토리 클래스를 이용하여 커넥션을 대신 생성하게 하는 것

 

커넥션 팩토리는 기본적으로 2개의 메소드와 1개의 생성자를 포함한다.

 

static 멤버로 SqlSessionFactory를 주고 static 메소드인 getSqlSessionFactory()를 통해 커넥션 팩토리 객체를 얻어내도록 하고 있음

 

내부에서 SqlSessionFactory 객체를 생성하고 있기 때문에 static 변수 선언 앞에 private 붙여도 됨

 

 

 

+ Recent posts