IT_Note

MVVM 아키텍처

 


MVC를 대체하기 위해 만들어진 아키텍처

MVC패턴의 경우 뷰와 컨트롤러가 N:N 구조로 만들어지다 보면 전체적인 구조가 복잡하게 얽히게 되는데, 

이러한 문제를 개선하기 위해 모델과 컨트롤러를 뷰에 종속적인 구조로 구성한 뷰 기준의 아키텍처 이다.

따라서 뷰가 종료되면 뷰컨트롤러와 뷰모델은 같이 사라지게 된다.

 

MVC와 MVVM의 가장 핵심적인 차이점은 MVVM에서 뷰 모델(ViewModel)이라고 불리는 뷰의 추상화를 지원한다는 점이다. 뷰 모델은 "데이터 바인딩(data binding)"이라는 기술을 사용해서 모델의 데이터와 뷰가 그리는 화면상의 데이터의 변경을 중재한다.


 

      


출처 : https://www.sencha.com/blog/ext-js-5-mvc-mvvm-and-more/


 

용어의 구체적인 설명은 다음과 같다.


(M) Model - 어플리케이션에서 사용되는 데이터를 담당한다. "Models"라고 불리는 데이터 집합으로 필드와 데이터로 정의된다. 예를 들어 User 모델은 user-name과 password 필드를 갖는다. 모델은 데이터 패키지를 통해 데이터에 대한 추적을 유지하고 결합(associations)을 통해 다른 모델들과 연결된다. 모델은 일반적으로 그리드 또는 다른 컴포넌트에 데이터를 제공하기 위한 스토어(Store)와 결합하여 사용되며, 또한 validation, conversion 기타 등등 데이터를 취급하다보면 필요할 수 있는 모든 데이터 로직의 이상적인 장소라고 할 수 있다.

 

(V) View - 뷰는 화면에 그려지는 비주얼에 관련된 모든 컴포넌트라고 할 수 있다. 그리드, 트리, 패널 등 화면에 나타나는 모든 것을 뷰라고 생각하면 된다.

 

(C) Controller - 어플리케이션이 작동하기 위해 필요한 뷰 로직을 관리한다. 이에 따라 뷰를 랜더링 하고, 라우팅하거나 모델 인스턴스를 생성하거나 또는 다른 어플리케이션 로직 일부를 수행토록 한다.

 

(VM) ViewModel - 뷰 모델은 뷰로 제공되는 데이터를 특정하여 관리하기 위한 클래스로서 컴포넌트가 바인딩을 처리하거나 데이터 변경에 따라 업데이트 되도록 하는 역할을 담당한다. 이러한 어플리케이션 아키텍처는 코드의 구조화 및 일관성을 부여한다.



 

MVVM아키텍처는 뷰와 뷰모델의 데이터바인딩이 핵심이다. 데이터바인딩을 간단하게 설명하면, 일종의 동기화 개념이다.

View의 데이터를 ViewModel에서 추상화 하고 있다, 즉 모델부분의 로직들은 뷰모델에서 관리할 수 있다는 것이고,

이를 뷰 중심으로 개별적으로 관리가 가능하다는 장점이 있다.



MVVM는 ViewController가 제공된다. 

ViewController은 기존의 MVC Controller와 비슷하지만

전역적으로 모든 뷰를 관리하지 않고 View를 개별적으로 관리한다.




View ViewModel VIewController는 한 묶음이라고 생각하면 이해하기 편하다.

View중심이며, 연관된 View가사라지면 ViewModel과 ViewController도 뒤이어 사라진다.


기존의 MVC객체는 컨트롤러가 모든 뷰를 전역적으로 관리하다보니 관련로직이 쏠리게되어

규모가 커질경우 스파게티소스가 되기 쉬웠지만 MVVM은 이를 개별적으로 관리함으로써 

이를 해결하고, 가독성을 높이는 장점이 있고 이로써 단위테스트를 하기 수월해 진다.





 

프로토타입

JavaScript/이론2017. 10. 30. 17:58



두개의 차이 숙지 필요.



1. __proto__ : 상위에서 물려 받은 객체의 프로토타입에 대한 정보. (prototype link 라고도 함)

 

2. protytpe : 자신을 원형으로 만들어질 새로운 객체들의 속성을 담는 그릇.(대부분의 입문자들이 알고 있는 정의 prototype object)

 

prototype chain : 객체의 생성 과정에서 모태가 되는 프로토타입과의 연결고리가 이어져 상속관계를 통하여 상위 프로토타입으로 연속해서 이어지는 관계를 프로토타입체인이라 한다. 이 연결은 __proto__를 따라 올라가게 된다.

 




스택 예제 MP3플레이어. JAVA



스택사용 예제

큐로 했을경우 뭐가 문제인지 생각해보기



- 사용되는 스택은 총 2개 before 과 next  이전곡 / 다음곡을 담아둘 스택.


- 스택은 클래스배열 형태로 넣어주는데 int num은 곡번호, String info배열은 제목과 가수명


- Scanner 이용해서 조작할 수 있게함. n b s p.


- 이전곡/다음곡이 없을경우 등 상황처리


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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
class St{
    
int num;   //번호
String info []; //제목,내용
 
  public St(int num,String sub,String con) {      
      this.num=num;
      this.info = new String[]{sub,con};      
  }  
  
  @Override
    public String toString() { // 주소값을 내용으로 변환.
        return num + Arrays.toString(info);
    }
  
}
 
 
public class Mp3 {    
    Stack<St> before = new Stack<St>(); // 이전곡
    Stack<St> next = new Stack<St>(); //다음곡
    St now = null//현재곡
            
    
    boolean backchk(){//뒤로가기 로직
        boolean res = !before.empty();//비어있으면, false
        
        if(res){ //이전곡 잇을경우 현재곡을 이전곡으로 설정.
            
            if(now!=null){ //현재곡이 존재할경우 현재곡을 다음곡으로 push
                next.push(now);                
            }             
            now = before.pop(); // ★현재곡은 이전곡으로.            
        }        
        return res;
    }
    
    void backtr(){ //뒤로가기 조작
        System.out.println("← 이전곡 재생버튼");
        
        if(backchk()){    
            print();
        }
        else{
            System.out.println("※※※ 이전곡이 없습니다");
        }
        
    }
    
    boolean nextchk(){        
    boolean res = !next.empty();//다음곡이 비어잇는게 아니면
        
    if(res){// 다음곡 있을경우.
    
        
    if(now!=null){
    before.push(now);
    }
    
    now = next.pop(); //현재곡은 다음곡으로     설정
    }
    
    return res;
    }
    
    void nexttr(){ //다음곡 조작.
    System.out.println("→ 다음곡 재생버튼");
    
    if(nextchk()){//잘 됫을경우
        print();        
    }
    else
        System.out.println("※※※ 다음곡이 없습니다.");
    }
    
    }
    
    void stop(){ //스탑
        System.out.println("곡을 멈춥니다.");
        
        if(now!=null){ //현재곡이 있을경우.
            
            next.push(now); //다음곡에 현재곡 저장.
            now=null;//현재곡은 비움.
            print();
        }else{
            System.out.println("※※※ 실행중인 곡이 없습니다.");
        }
    }
    
    void play(){
        System.out.println("mp3를 시작합니다");
        
        if(now==null){//멈춰 있는 상태일 경우.
            now=next.pop();//now에 다음곡 입력해줌.
            print();
        }else{
            System.out.println("※※※ 이미 실행중입니다");
            print();
        }
        
    }
    
    void play(int num){
    //해당번호 곡 재생하기 미구현    
    }
    
    public Mp3() {//생성자, 내용입력,        
 
        next.push(new St(6"제목6""내용6"));    
        next.push(new St(5"제목5""내용5"));
        next.push(new St(4"제목4""내용4"));
        next.push(new St(3"제목3""내용3"));
        next.push(new St(2"제목2""내용2"));
        next.push(new St(1"제목1""내용1"));
    }
    
    void print(){ //현재상태 출력
        System.out.println("\n이전곡"+before);
        System.out.println("현재곡"+now);
        System.out.println("다음곡"+next+"\n");
    }
 
    public static void main(String[] args) {
        // TODO Auto-generated method stub
 
        Scanner sc = new Scanner(System.in);
        Mp3 mp3 = new Mp3();
        
        
        System.out.println("★빠밤~ mp3전원이 켜졌습니다.★ \n 조작방법 - 재생:p/멈춤:s/다음곡:n/이전곡:b 입력");
        while(true){            
        String input = sc.next();    
        
        switch (input) {        
        case "b":
            mp3.backtr();
            break;
            
        case "n":
            mp3.nexttr();
            break;
            
        case "s":
            mp3.stop();
            break;
            
        case "p":
            mp3.play();
            break;
 
        default:
            break;
        }
        
        }
        
        
    }
 
}
 
cs





큐로 했을경우 FIFO이기 때문에,


이전곡 다음곡 이동시에 꼬여버리는 현상이 나타난다.

LIFO같은경우 가장 마지막에 했던것이 POP된다.


FIFO는 가장 근접한 최근곡을 이전곡으로 설정 해야 하는데, 큐로 하게되면

그게 불가능. 걍 처음넘어간 것만 








우선 스택은 LIFO (last in first out) 


큐는 FIFO(First in first out)이다.


스택은 밑이 막힌통에 차례대로 저장을 하고 가장 위에서 부터 꺼내는 구조


큐는 뻥 뚫린 통에 저장을 하고 넣은 순서대로 값이 빠져나오는 구조.




스택의 예


C에서 함수를 호출할 때, 현재 함수에서 사용되는 값을 스택에 집어넣고 부르는 함수에 쓰일 값들에 대한 작업을 시작합니다. 작업이 끝나면, 함수를 리턴하고 스택에 넣었던 값을 꺼냅니다. 이렇게 하면 함수가 호출되기 이전의 상태로 복원 시킬 수 있습니다. 


큐의 예


키보드 입력이 있다. 키보들 값을 임력하면 임시 버퍼에 쌓이게 된다. 

프로그래밍에서는 이 입력 값을 이용하기 위해 입력된 순서대로 값을 꺼내오게 된다.



ArrayList

카테고리 없음2017. 4. 17. 02:09


list는 배열의 한계 때문에 만들어진 자료형이다.


배열을 사용하기 위해서는 크기를 정해야 하는데 크기를 알 수 없는 경우가 많다.

List는 메모리가 허용하는 한 계속해서 추가할 수 있다.


java.util.List 인터페이스

java.util.Collection인터페이스를 구현한다.



자료의 내부적인 자동 동기화 기능이 없다.

내부적으로 자료를 배열구조로 가지고 있는 객체, 데이터의 추가/삭제를 위해서는 내부적인 임시배열을 작성 후 데이터를 복사한다 (자료 추가/삭제시 단점)

레퍼런스 타입의 데이터만 저장할 수 있다

데이터 내용의 중복이 가능하다

null값 허용.


장점 : 데이터의 인덱스를 가지고 있기 때문에 검색,참조시 유리, 

단점 : 대량의 자료를 추가/삭제시 성능 저하.






1.선언



List<String> list = new ArrayList<>();


리스트에 저장할 데이터의 타입을 정한다. 위에는 String

<>꺽쇠는 제네릭이라고 하며 데이터의 타입을 정함.

 String이나 Integer , 클래스를 타입으로 지정하기도 한다.


제네릭으로 타입을 지정 안해줘도 되지만 보통 해줌 오류방지등등의 이점..

안할 경우 여러가지 add가능.


ArrayList<String> list = new ArrayList<>(); , Arraylist라고 해도 무방하다. 

차이점은 List는 인터페이스 이고 ArrayList는 구현체이다. List를 기반으로 만들어진것이 ArrayList..

이 부분은 다형성과 관련되어 있다. 일단 이정도로 넘어감.




2.추가


추가- list.add("첫번째인덱스"); 

list의 0번째 인덱스에 추가된다.


현재 list는 [첫번째인덱스]


요소값을 넣고 추가할 수도 있다. 

- list.add(1,"두번째인덱스");


현재 list는 [첫번째인덱스,두번째인덱스]



3.조회 


인덱스로 값을 조회하고 싶다면 get(index)함수 사용.


list.get(2);










자바문법 복습중...








1.charAt(인수)  - 인수번째의 문자를 읽어낸다



String a = "가나다라마바사아자차카타파하";


System.out.println(a.charAt(1)); 



일경우 "나" 출력된다. 인덱스 0부터 시작.




- for문을 이용하여 거꾸로 출력하고자 한다면??


for(int i=a.length-1;i>=0;i--){


System.out.print(a.charAt(i));


}


length값은 14로 나오지만 인덱스는 0~13이므로 -1을 해주고.


i는 0과 같거나 큰 조건, 1씩 줄어들게 해준다.



----------------------------------



2.indexOf(문자) - 해당문자가 들어있는 위치를 알려준다, 문자가 없을 경우 -1을 반환한다.



마찬가지로 인덱스 0부터 시작. 


lastIndexOf같은경우 뒤부터 센다.


System.out.println(indexOf("다"));


일경우 0,1,2 가,나,다 

즉, 3번째인 2가 출력



3.substring(인수,인수) - 시작과 끝을 지정하여 문자열을 읽어낸다


첫번째 인수는 시작지점  - 반환값에포함

두번째인수는 끝지점. - 반환값에 포함 x  


반환값은 예제를보면서 설명.


System.out.println(a.substring(2,5));


일경우 2부터 5까지 문자를 출력한다. 

가나[다]라마[바]사


끝지점은 반환값에 불포함이므로


다,라,마 까지만 출력.











bbs.BoardController.java


Model을 메소드 인자로 사용하여 모델설정 역할. req.setAttribute("이름",값)

package com.sp.bbs;


@Controller("bbs.boardController")

public class BoardController {

@Autowired

private BoardService service;

@Autowired

private MyUtil myUtil;

//@RequestMapping 어노테이션을 사용할 경우, MultiActionController와 같이 한 개의 컨트롤러에서 다수의 요청을 처리.

//@RequestMapping의 value형태의 url이 들어오면 해당 method를 호출한다.

@RequestMapping(value="/bbs/created",method=RequestMethod.GET)

public String createdForm(Model model) throws Exception {

model.addAttribute("mode","created");

return "bbs/created";

}

@RequestMapping(value="/bbs/created", method=RequestMethod.POST)

public String createdSubmit(Board dto, HttpServletRequest req)throws Exception{

dto.setIpAddr(req.getRemoteAddr());

service.insertBoard(dto);

return "redirect:/bbs/list";

}

}



DB에 데이터를 저장하기 위한 bbsMapper.xml 파일 수정

<?xml version="1.0" encoding="UTF-8"?>


<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="bbs">


<insert id="insertBoard" parameterType="com.sp.bbs.Board">

INSERT INTO bbs (num, name, pwd, subject, content, ipAddr) VALUES(

bbs_seq.NEXTVAL, #{name},#{pwd},#{subject},#{content},#{ipAddr})

</insert>


</mapper>



휴.......이제 서버 실행 후 게시글을 작성해서 db에 들어가는지 확인 해 보자.


아마 들어갈 것임 




- com/sp/bbs/Board.java ( DTO 클래스 )


public class Board {

private int listNum, num;

private String name,pwd,subject,content,ipAddr,created;

private int hitCount;

getter setter 작성.



- com/sp/bbs/BoardService.java -  인터페이스 작성

package com.sp.bbs;


import java.util.List;

import java.util.Map;


public interface BoardService {

public int insertBoard(Board dto);

    public List<Board> listBoard(Map<String, Object> map);

    public int dataCount(Map<String, Object> map);

    public Board readBoard(int num);

    public int updateHitCount(int num);

    public Board preReadBoard(Map<String,Object> map);

    public Board nextReadBoard(Map<String,Object> map);

    public int updateBoard(Board dto);

    public int deleteBoard(int num);

}


- com/sp/bbs/BoardServiceImpl.java - 인터페이스 구현. 클래스

어노테이션 빼먹지 않도록 주의한다.


@Autowired 타입에 의한 의존관계 자동설정


동일한 타입이 둘 이상인 경우

@Autowired 

@Qualifier("bbs.boardService")


import java.util.List;

import java.util.Map;


import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;


import com.sp.common.dao.CommonDAO;


@Service("bbs.boardService")

public class BoardServiceImpl implements BoardService {


 @Autowired

 private CommonDAO dao;


@Override

public int insertBoard(Board dto) {

int result=0;

try {

//최대값을 이용하여 게시물 번호를 작성하는 경우

//int maxNum = dao.getIntValue("bbs.numMax")

//dto.setNum(maxNum+1);

result=dao.insertData("bbs.insertBoard", dto);

} catch (Exception e) {

// TODO: handle exception

}

return result;

}

//기타 클래스 생략 //

}


//commondao의 insertdate를 사용.


- 사용자생성 - 오라클 DB

--1 사용자를 생성한다 이름은 board1으로.비번은 java$!

CREATE USER board1 IDENTIFIED BY "java$!";


--2 사용자에게 CONN 및 테이블스페이스를 사용할 권한 부여

GRANT CONNECT, RESOURCE TO board1;


--3 테이블스페이스 변경

ALTER USER board1 DEFAULT TABLESPACE USERS;


--4 

ALTER USER board1 TEMPORARY TABLESPACE TEMP;


- 테이블생성


게시판 테이블이름은 bbs로. 

ip주소와 비밀번호를 이용하는 비회원제 게시판이다.

여러가지 방법이 있겠지만 일단 가장 기초적으로 ㄱㄱ

CREATE TABLE bbs (

num NUMBER NOT NULL

,name VARCHAR2(30) NOT NULL 

,pwd VARCHAR2(50)

,subject VARCHAR2(255) NOT NULL  

,content VARCHAR2(4000) NOT NULL

,ipAddr VARCHAR2(50) NOT NULL

,hitCount NUMBER DEFAULT 0

,created DATE DEFAULT SYSDATE

,CONSTRAINT pk_bbs_num PRIMARY KEY(num)

);


- 시퀀스 생성


기본키 num값을 위한 시퀀스 설정

시퀀스이름은 bbs_seq로.

1씩증가하며 1부터 시작한다.

CREATE SEQUENCE bbs_seq

INCREMENT BY 1

START WITH 1

NOMAXVALUE NOCYCLE NOCACHE;

SELECT * FROM seq;


참고

NOMAXVALUE : 오름차순 최대 10의 27승 까지, 내림차순 -1 까지 생성됨.

NOCACHE : 시퀀스를 메모리에 할당하여 사용하지 않음.

NOCYCLE : 해당 시퀀스가 최대값에 도달해도 다시 생성하지 않는다.[DEFAULT]





myutil.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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
package com.sp.common;
 
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
import org.springframework.stereotype.Service;
 
@Service("myUtil")
public class MyUtil {
    //********************************************
    // 총페이지 수 구하기
    public int pageCount(int numPerPage, int dataCount) {
        int pageCount=0;
        
        if(dataCount > 0) {
            if(dataCount % numPerPage == 0)
                pageCount=dataCount/numPerPage;
            else
                pageCount=dataCount/numPerPage+1;
        }
        
        return pageCount;
    }
    
    //********************************************
    // 페이징(paging) 처리(GET 방식)
    public String paging(int current_page, int total_page, String list_url) {
        StringBuffer sb=new StringBuffer();
        
        int numPerBlock=10;
        int currentPageSetup;
        int n, page;
        
        if(current_page<1 || total_page < 1)
            return "";
        
        if(list_url.indexOf("?")!=-1)
            list_url+="&";
        else
            list_url+="?";
        
        // currentPageSetup : 표시할첫페이지-1
        currentPageSetup=(current_page/numPerBlock)*numPerBlock;
        if(current_page%numPerBlock==0)
            currentPageSetup=currentPageSetup-numPerBlock;
 
        sb.append("<style type='text/css'>");
        sb.append("#paginate {clear:both;font:12px 돋움,Dotum,굴림,Gulim,AppleGothic;padding:15px 0px 0px 0px;text-align:center;height:28px;white-space:nowrap;}");
        sb.append("#paginate a {border:1px solid #ccc;height:28px;color:#000000;text-decoration:none;padding:4px 7px 4px 7px;margin-left:3px;line-height:normal;vertical-align:middle;outline:none; select-dummy: expression(this.hideFocus=true);}");
        sb.append("#paginate a:hover, a:active {border:1px solid #ccc;color:#6771ff;vertical-align:middle; line-height:normal;}");
        sb.append("#paginate .curBox {border:1px solid #e28d8d; background: #fff; color:#cb3536; font-weight:bold;height:28px;padding:4px 7px 4px 7px;margin-left:3px;line-height:normal;vertical-align:middle;}");
        sb.append("#paginate .numBox {border:1px solid #ccc;height:28px;font-weight:bold;text-decoration:none;padding:4px 7px 4px 7px;margin-left:3px;line-height:normal;vertical-align:middle;}");
        sb.append("</style>");
        
        sb.append("<div id='paginate'>");
        // 처음페이지, 이전(10페이지 전)
        n=current_page-numPerBlock;
        if(total_page > numPerBlock && currentPageSetup > 0) {
            sb.append("<a href='"+list_url+"page=1'>처음</a>");
            sb.append("<a href='"+list_url+"page="+n+"'>이전</a>");
        }
        
        // 바로가기
        page=currentPageSetup+1;
        while(page<=total_page && page <=(currentPageSetup+numPerBlock)) {
            if(page==current_page) {
                sb.append("<span class='curBox'>"+page+"</span>");
            } else {
                sb.append("<a href='"+list_url+"page="+page+"' class='numBox'>"+page+"</a>");
            }
            page++;
        }
        
        // 다음(10페이지 후), 마지막페이지
        n=current_page+numPerBlock;
        if(n>total_page) n=total_page;
        if(total_page-currentPageSetup>numPerBlock) {
            sb.append("<a href='"+list_url+"page="+n+"'>다음</a>");
            sb.append("<a href='"+list_url+"page="+total_page+"'>끝</a>");
        }
        sb.append("</div>");
    
        return sb.toString();
    }
 
    //********************************************
    // javascript 페이지 처리(javascript listPage() 함수 호출)
    public String paging(int current_page, int total_page) {
        if(current_page < 1 || total_page < 1)
            return "";
 
        int numPerBlock = 10;   // 리스트에 나타낼 페이지 수
        int currentPageSetUp;
        int n;
        int page;
        StringBuffer sb=new StringBuffer();
        
        // 표시할 첫 페이지
        currentPageSetUp = (current_page / numPerBlock) * numPerBlock;
        if (current_page % numPerBlock == 0)
            currentPageSetUp = currentPageSetUp - numPerBlock;
 
        sb.append("<style type='text/css'>");
        sb.append("#paginate {clear:both;font:12px 돋움,Dotum,굴림,Gulim,AppleGothic;padding:15px 0px 0px 0px;text-align:center;height:28px;white-space:nowrap;}");
        sb.append("#paginate a {border:1px solid #ccc;height:28px;color:#000000;text-decoration:none;padding:4px 7px 4px 7px;margin-left:3px;line-height:normal;vertical-align:middle;outline:none; select-dummy: expression(this.hideFocus=true);}");
        sb.append("#paginate a:hover, a:active {border:1px solid #ccc;color:#6771ff;vertical-align:middle; line-height:normal;}");
        sb.append("#paginate .curBox {border:1px solid #e28d8d; background: #fff; color:#cb3536; font-weight:bold;height:28px;padding:4px 7px 4px 7px;margin-left:3px;line-height:normal;vertical-align:middle;}");
        sb.append("#paginate .numBox {border:1px solid #ccc;height:28px;font-weight:bold;text-decoration:none;padding:4px 7px 4px 7px;margin-left:3px;line-height:normal;vertical-align:middle;}");
        sb.append("</style>");
        
        sb.append("<div id='paginate'>");
        
        // 처음페이지, 이전(10페이지 전)
        n = current_page - numPerBlock;
        if ((total_page > numPerBlock) && (currentPageSetUp > 0)) {
            sb.append("<a onclick='listPage(1);'>처음</a>");
            sb.append("<a onclick='listPage("+n+");'>이전</a>");
        }
 
        // 바로가기 페이지 구현
        page = currentPageSetUp + 1;
        while((page <= total_page) && (page <= currentPageSetUp + numPerBlock)) {
           if(page == current_page) {
               sb.append("<span class='curBox'>"+page+"</span>");
           } else {
               sb.append("<a onclick='listPage("+page+");' class='numBox'>"+page+"</a>");
           }
           page++;
        }
 
        // 다음(10페이지 후), 마지막 페이지
        n = current_page + numPerBlock;
        if(n>total_page) n=total_page;
        if (total_page - currentPageSetUp > numPerBlock) {
            sb.append("<a onclick='listPage("+n+");'>다음</a>");
            sb.append("<a onclick='listPage("+total_page+");'>끝</a>");
        }
        sb.append("</div>");
 
        return sb.toString();
    }
 
    //********************************************
    // javascript 페이지 처리(javascript 지정 함수 호출, methodName:호출할 스크립트 함수명)
    public String pagingMethod(int current_page, int total_page, String methodName) {
        if(current_page < 1 || total_page < 1)
            return "";
 
        int numPerBlock = 10;   // 리스트에 나타낼 페이지 수
        int currentPageSetUp;
        int n;
        int page;
        StringBuffer sb=new StringBuffer();
        
        // 표시할 첫 페이지
        currentPageSetUp = (current_page / numPerBlock) * numPerBlock;
        if (current_page % numPerBlock == 0)
            currentPageSetUp = currentPageSetUp - numPerBlock;
 
        sb.append("<style type='text/css'>");
        sb.append("#paginate {clear:both;font:12px 돋움,Dotum,굴림,Gulim,AppleGothic;padding:15px 0px 0px 0px;text-align:center;height:28px;white-space:nowrap;}");
        sb.append("#paginate a {border:1px solid #ccc;height:28px;color:#000000;text-decoration:none;padding:4px 7px 4px 7px;margin-left:3px;line-height:normal;vertical-align:middle;outline:none; select-dummy: expression(this.hideFocus=true);}");
        sb.append("#paginate a:hover, a:active {border:1px solid #ccc;color:#6771ff;vertical-align:middle; line-height:normal;}");
        sb.append("#paginate .curBox {border:1px solid #e28d8d; background: #fff; color:#cb3536; font-weight:bold;height:28px;padding:4px 7px 4px 7px;margin-left:3px;line-height:normal;vertical-align:middle;}");
        sb.append("#paginate .numBox {border:1px solid #ccc;height:28px;font-weight:bold;text-decoration:none;padding:4px 7px 4px 7px;margin-left:3px;line-height:normal;vertical-align:middle;}");
        sb.append("</style>");
        
        sb.append("<div id='paginate'>");
        
        // 처음페이지, 이전(10페이지 전)
        n = current_page - numPerBlock;
        if ((total_page > numPerBlock) && (currentPageSetUp > 0)) {
            sb.append("<a onclick='"+methodName+"(1);'>처음</a>");
            sb.append("<a onclick='"+methodName+"("+n+");'>이전</a>");
        }
 
        // 바로가기 페이지 구현
        page = currentPageSetUp + 1;
        while((page <= total_page) && (page <= currentPageSetUp + numPerBlock)) {
           if(page == current_page) {
               sb.append("<span class='curBox'>"+page+"</span>");
           } else {
               sb.append("<a onclick='"+methodName+"("+page+");' class='numBox'>"+page+"</a>");
           }
           page++;
        }
 
        // 다음(10페이지 후), 마지막 페이지
        n = current_page + numPerBlock;
        if(n>total_page) n=total_page;
        if (total_page - currentPageSetUp > numPerBlock) {
            sb.append("<a onclick='"+methodName+"("+n+");'>다음</a>");
            sb.append("<a onclick='"+methodName+"("+total_page+");'>끝</a>");
        }
        sb.append("</div>");
 
        return sb.toString();
    }
 
    //********************************************
    // HTML 태그 제거
    public String removeHtmlTag(String str) {
        if(str==null||str.length()==0)
            return "";
 
        String regex="<(/)?([a-zA-Z]*)(\\s[a-zA-Z]*=[^>]*)?(\\s)*(/)?>";
        String result=str.replaceAll(regex, "");
        return result;
    }
 
    //********************************************
    // HTML 문서의 img 태그 src 속성값 추출 
    public List<String> getImgSrc(String html) {
        List<String> result = new ArrayList<String>();
        
        if(html==null||html.length()==0)
            return result;
 
        String regex="<img[^>]*src=[\"']?([^>\"']+)[\"']?[^>]*>";
        Pattern nonValidPattern = Pattern.compile(regex);
 
        Matcher matcher = nonValidPattern.matcher(html);
        while (matcher.find()) {
            result.add(matcher.group(1));
        }
        return result;
    }
    
    //********************************************
    // 특수문자를 HTML 문자로 변경
    public String escape(String str) {
        if(str==null||str.length()==0)
            return "";
        
        StringBuilder builder = new StringBuilder((int)(str.length() * 1.2f));
 
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            switch (c) {
            case '<':
                builder.append("&lt;");
                break;
            case '>':
                builder.append("&gt;");
                break;
            case '&':
                builder.append("&amp;");
                break;
            case '\"':
                builder.append("&quot;");
                break;
            default:
                builder.append(c);
            }
        }
        return builder.toString();
    }
 
    //********************************************
    // 특수문자를 HTML 문자로 변경 및 엔터를 <br>로 변경 
     public String htmlSymbols(String str) {
        if(str==null||str.length()==0)
            return "";
 
         str=str.replaceAll("&""&amp;");
         str=str.replaceAll("\"""&quot;");
         str=str.replaceAll(">""&gt;");
         str=str.replaceAll("<""&lt;");
         
         str=str.replaceAll(" ""&nbsp;");
         str=str.replaceAll("\n""<br>");
         
         return str;
     }
 
    //********************************************
     // 문자열의 내용중 원하는 문자열을 다른 문자열로 변환
     // String str = replaceAll(str, "\n", "<br>"); // 엔터를 <br>로 변환
     public String replaceAll(String str, String oldStr, String newStr) throws Exception {
         if(str == null)
             return "";
 
         Pattern p = Pattern.compile(oldStr);
         
         // 입력 문자열과 함께 매쳐 클래스 생성
         Matcher m = p.matcher(str);
 
         StringBuffer sb = new StringBuffer();
         // 패턴과 일치하는 문자열을 newStr 로 교체해가며 새로운 문자열을 만든다.
         while(m.find()) {
             m.appendReplacement(sb, newStr);
         }
 
         // 나머지 부분을 새로운 문자열 끝에 덫 붙인다.
         m.appendTail(sb);
 
         return sb.toString();
     }
 
    //********************************************
     // E-Mail 검사
     public boolean isValidEmail(String email) {
         if (email==nullreturn false;
         boolean b = Pattern.matches(
             "[\\w\\~\\-\\.]+@[\\w\\~\\-]+(\\.[\\w\\~\\-]+)+"
             email.trim());
         return b;
     }
 
    //********************************************
     // NULL 인 경우 ""로 
     public String checkNull(String str) {
         String strTmp;
         if (str == null)
             strTmp = "";
         else
             strTmp = str;
         return strTmp;
     }
}