login register Sysop! about ME  
qrcode
    최초 작성일 :    2002년 02월 13일
  최종 수정일 :    2002년 02월 19일
  작성자 :    Dukyoung (김덕영)
  편집자 :    Taeyo (김 태영)
  읽음수 :    582,557

강좌 목록으로 돌아가기

필자의 잡담~

전람회의 음악이 그리운 요즘입니다.... 그중에도 "10년의 약속"을 자주 듣게 되네요...
왜지? 왜 센치해질라구 이러는 거지??? (히죽)

제가 가장 좋아하는 가수는 지금은 해체한 그룹 전람회의 리드 보컬 김동률입니다.
생일 선물로 김동률 3집 CD를 선물받았는데.. 거기에는 hidden track 하나(28번 트랙)가 숨어있었습니다.
옛 전람회의 멤버였던 서동욱과 듀엣으로 부른 그곡의 제목은 '떠나보내다' 라고 하더군요.
앨범 속지에도 그 곡에 대한 언급은 없었고, 앨범 목록에도 물론 그 곡이 실려있지 않았습니다.
아마도.. CD 를 산 사람이 아니라면 그 곡의 존재를 모를 거라는 생각이 들었습니다.
그래서 그 곡이 더욱 정겹게 느껴지는 것인지도 모르겠습니다.

그럼 오늘의 강좌를 시작하겠습니다. 오늘 우리가 진행할 내용은 '페이지 나누기 ' 부분이 되겠습니다.
지난 시간에 제가 '페이지 나누기' 부분이 게시판 만들기의 '백미' 라고 말씀드렸는데요.
그 이유가 오늘 밝혀지게 될 것 같습니다..
그럼 심호흡을 한번 크게 하시고 (흐~읍~!!!) 시작하도록 하겠습니다.

'페이지 나누기라... 왜 페이지를 나누어야 하는건데?' 라는 의문을 가지는 분들이 계실지도 모르겠군요.
그런 의문을 가진 분들을 위해.. 왜 페이지 나누기가 필요한지 잠시 말씀드리겠습니다.
사실.. 테스트를 위해서 한, 두개 게시를 올리는 경우에는 페이지를 굳이 나눌 필요가 없습니다.
하지만 여러분들께서 만든 게시판의 인기가 높아지면서 게시가 열개, 백개, 천개... 수만개에 이르게 된다면
(상상만 해도 기분 좋은 이야기지요? ^^) 이야기가 슬슬 달라지기 시작합니다.

지금까지 우리가 만든 게시판의 구조라면.. 목록보기 페이지인 'list.asp' 에 모든 목록이 다~ 나오게 됩니다.
게시가 백개건 천개건.. 전혀 상관하지 않는다는 말이지요.
만약.. 현재 맨 위에는 10000번 게시가 올라와 있는데 맨 처음 쓴 게시(1번 게시)를 보고 싶으면 어떻게 할까요?
'어.. 걱정없어. 나 휠 마우스야.' 라고 생각하십니까? 정 뜻이 그러하시다면 열심히 휠을 돌리시지여. -_-a
이런 경우에는 페이지를 방문한 사람들의 편의를 위해서 페이지를 나누어 주는 것 이 좋습니다.
그 편이 보기에도 편하고, 또한 게시판의 효율성(속도)에 있어서도 훨씬 유리하기 때문입니다.

그렇다면.. 이제 본격적으로 페이지 나누기를 알아보도록 하겠습니다.
오늘의 강좌에서는 새로운 페이지를 작성하지는 않습니다. 대신 기존의 페이지를 조금 수정할 예정입니다.
그렇다면.. 우선 목록을 보여주는 'list.asp' 페이지를 열어주시기를 바랍니다.
기존의 소스는 다음과 같습니다.

<list.asp>

01
02
03
04
05
06
07
08
09
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

<%
    Option Explicit
 
    Dim objDBConn
    Dim objRs
 
    Dim strSQL
 
    strSQL = "Select intSeq"                                          ' objRs(0) - 번호
    strSQL = strSQL & ",strName"                                  ' objRs(1) - 이름
    strSQL = strSQL & ",strEmail"                                   ' objRs(2) - 메일주소
    strSQL = strSQL & ",strSubject"                                ' objRs(3) - 제목
    strSQL = strSQL & ",intCount"                                  ' objRs(4) - 조회수
    strSQL = strSQL & ",dtmReg_Date"                           ' objRs(5) - 등록일
    strSQL = strSQL & " FROM board"
    strSQL = strSQL & " ORDER BY intSeq desc"
 
    Set objDBConn = Server.CreateObject("ADODB.Connection")
    Set objRs = Server.CreateObject("ADODB.RecordSet")
 
    objDBConn.Open "test", "sa", ""
 
    objRs.Open strSQL, objDBConn
%>
<html>
<head>
<title>목록보기</title>
</head>
<body>
<div align="center">
<h2>목록보기</h2>
<table border width="600">
   <tr align="center">
     <td>번호</td>
     <td>제목</td>
     <td>작성자</td>
     <td>등록일</td>
     <td>조회</td>
  </tr>
  <% If objRs.BOF or objRs.EOF Then %>
  <tr align="center">
    <td colspan="5">등록된 게시가 없습니다</td>
  </tr>
<%
         Else
              Do Until objRs.EOF
%>
  <tr align="center">
   <td><%=objRs(0)%></td>
    <td>
       <a href="content.asp?seq=<%=objRs(0)%>"><%=objRs(3)%></a>
    </td>
    <td> 
       <a href="mailto:<%=objRs(2)%>"><%=objRs(1)%></a>
    </td> 
    <td><%=left(objRs(5), 10)%></td>
    <td><%=objRs(4)%></td>
  </tr>
<%
                  objRs.MoveNext
              Loop
        End If
 
        objRs.Close
        Set objRs = nothing
        objDBConn.Close
       Set objDBConn = nothing
%>
</table>
<a href="regist/regist.asp">글쓰기</a>
</div>
</body>
</html>

간만에 보니 반가운 소스군요..
자 그러면.. 이 소스를 수정해 보도록 하겠습니다. 수정한 부분은 빨간색으로 표시하도록 하지요.


페이지 나누기가 적용된 list.asp 소스

01
02
03
04
05
06
07
08
09
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

<%
    Option Explicit
 
    Dim objDBConn
    Dim objRs

    Dim strSQL
    Dim intNowPage, intTotalCount, intTotalPage, intBlockPage, intPageSize
    Dim intTemp, intLoop

    intNowPage = Request.QueryString("page")    
    intPageSize = 10
    intBlockPage = 10

    If Len(intNowPage) = 0 Then
        intNowPage = 1
    End If
 
    strSQL = "Select Count(*)"
    strSQL = strSQL & ",CEILING(CAST(Count(*) AS FLOAT)/" & intPageSize & ")"
    strSQL = strSQL & " from board"
 
    Set objDBConn = Server.CreateObject("ADODB.Connection")
    Set objRs = Server.CreateObject("ADODB.RecordSet")
 
    objDBConn.Open "test", "sa", ""
 
    objRs.Open strSQL, objDBConn
    intTotalCount = objRs(0)
    intTotalPage = objRs(1)
    objRs.Close


    strSQL = "Select Top " & intNowPage * intPageSize & " intSeq"    ' 번호
    strSQL = strSQL & ",strName"                                                   ' 이름
    strSQL = strSQL & ",strEmail"                                                   ' 메일주소
    strSQL = strSQL & ",strSubject"                                                ' 제목
    strSQL = strSQL & ",intCount"                                                   ' 조회수
    strSQL =strSQL & ",dtmReg_Date"                                           ' 등록일
    strSQL = strSQL & " FROM board"
    strSQL = strSQL & " ORDER BY intSeq desc"

    objRs.Open strSQL, objDBConn
%>
<html>
<head>
<title>목록보기</title>
</head>
<body>
<div align="center">
<h2>목록보기</h2>
<% If intTotalCount > 0 Then %>
<table width="600">
  <tr>
    <td>전체게시 <%=intTotalCount%> 개 &nbsp;&nbsp;&nbsp;&nbsp;
            현재페이지 : <%=intNowPage%> / <%=intTotalPage%>
    </td>
  </tr>
</table>
<%  End If  %>

<table border width="600">
  <tr align="center">
     <td>번호</td>
     <td>제목</td>
     <td>작성자</td>
     <td>등록일</td>
     <td>조회</td>
  </tr>
  <% If objRs.BOF or objRs.EOF Then %>
  <tr align="center">
    <td colspan="5">등록된 게시가 없습니다</td>
  </tr>
<%
         Else
              objRs.Move (intNowPage - 1) * intPageSize
              Do Until objRs.EOF
%>
  <tr align="center">
    <td><%=objRs(0)%></td>
    <td>
       <a href="content.asp?seq=<%=objRs(0)%>"><%=objRs(3)%></a>
    </td>
    <td> 
       <a href="mailto:<%=objRs(2)%>"><%=objRs(1)%></a>
    </td> 
    <td><%=left(objRs(5), 10)%></td>
    <td><%=objRs(4)%></td>
  </tr>
<%
                  objRs.MoveNext
              Loop
        End If
 
        objRs.Close
        Set objRs = nothing
        objDBConn.Close
        Set objDBConn = nothing
%>
</table>
<table width="600">
  <tr>
    <td align="center">
    <%
            intTemp = Int((intNowPage - 1) / intBlockPage) * intBlockPage + 1

            If intTemp = 1 Then
                Response.Write "[이전 " & intBlockPage & "개]"
            Else
                Response.Write"<a href=list.asp?page=" & intTemp - intBlockPage & ">[이전 "
                & intBlockPage & "개]</a>"
            End If

            intLoop = 1

            Do Until intLoop > intBlockPage Or intTemp > intTotalPage
                If intTemp = CInt(intNowPage) Then
                    Response.Write "<font size= 3><b>" & intTemp &"</b></font>&nbsp;"
                Else
                    Response.Write"<a href=list.asp?page=" & intTemp & ">" & intTemp &
                    "</a>&nbsp;"
                End If
                intTemp = intTemp + 1
                intLoop = intLoop + 1
            Loop

            If intTemp > intTotalPage Then
                Response.Write "[다음 " &intBlockPage&"개]"
            Else
                Response.Write"<a href=list.asp?page=" & intTemp & ">[다음 "
                & intBlockPage & "개]</a>"
            End If
    %>
    </td>
  </tr>
</table>
<a href="regist/regist.asp">글쓰기</a>
</div>
</body>
</html>

빨간색이 남발되어서 부담스러우십니까? 너무 부담가지지 않으셔도 됩니다.
그러면.. 이제부터 페이지 나누기가 적용된 list.asp 페이지를 살펴 보도록 하겠습니다.
수정된 부분을 중점적으로 설명하도록 하지요.

먼저 8번, 9번째 줄은 페이지 나누기를 위한 변수를 선언했습니다.
intNowPage 는 지금 현재 페이지 가 몇번째 페이지인지를 나타내는 변수이고요.
intTotalCount전체 게시물의 갯수를, intTotalPage 는 총 페이지 수를 의미합니다.
intBlockPage페이지를 몇개씩 구분할 것 인지 - 이전 O개, 다음 O개 할때 사용하지요 - 를 나타내는 변수고,
intPageSize 는 한 페이지에 몇개의 게시까지 보이게 할지 를 결정하는 변수가 되겠습니다.

9번째 줄에 있는 intTemp 와 intLoop 는 임시로 사용되는 정수형 변수인데요.
굳이 간략하게 설명을 드리자면, Do ~ Loop 문을 돌때마다 증가되는 변수가 되겠습니다.

이것으로 변수 선언을 마치고, 11번 줄에서는..
현재의 페이지가 몇 페이지인지를 intNowPage 변수에 받아오는 부분이 되겠습니다.
여기서 한가지 의문이 드실지도 모르겠네요. 우리는 이 페이지가 몇 페이지인지 값을 넘겨준 기억이 없으니까요.
하지만 이 부분은 잠시 후에 설명이 될 것이므로 여기서는 그냥 넘어가도록 하겠습니다.
Request.QueryString 으로 넘긴것을 보아 url 의 뒤에 값을 넘기는 방식인 get 방식을 사용했음을...
이제 여러분들께서는 웃으면서 크게 고개를 끄덕거리시리라 믿어 의심치 않겠습니다. ^^

12, 13 번 줄에서는 페이지의 크기에 해당하는 변수인 intPageSize10 으로 설정했으며,
마찬가지로 페이지를 몇개씩 구분할 것인지를 나타내는 변수인 intBlockPage10으로 설정했습니다.
이 부분은 여러분들께서 내키시는대로 바꾸셔도 괜찮습니다. (한번씩 바꾸어보시는 것도 재미있을 겁니다.)

15번~17번 줄은, 아까 11번 줄에서 실행했던 intNowPage 라는 변수에 값이 없다면 ..
(즉, 현재 페이지가 몇 페이지인지 알 수가 없다면) 현재 페이지를 1로 지정하는 부분이 되겠습니다.
다시 말해 현재 페이지가 몇페이지인지 모르는 경우에는 가장 첫번째 페이지를 보여주라는 의미가 됩니다.
그런데.. 15번 줄에서 Len() 이라는 함수가 사알짝~ 등장을 하지요?
이 함수는 괄호 안에 있는 문자열의 길이(글자의 갯수)를 반환 합니다. 예를 들면 다음과 같습니다.

strTest = "안녕하세여"
intLength = Len(strTest)

이렇게 하면 intLength 라는 변수에는 어떤 값이 들어가 있을까요? 네에.. 5가 들어있을 것입니다.
그렇다면.. 다음 문장의 경우는 어떨까요?

strTest = "안녕 하세여"
intLength = Len(strTest)

'안녕' 과 '하세여' 사이에 공백이 하나 들어갔네요. 이 경우에는 intLength 에 어떤 값이 들어갈까요..?
Len() 함수는 공백도 하나의 문자로 인식 을 하므로 정답은 6이 되겠습니다.
상당히 이해하기 쉬운 함수였지요? 쓰임새가 많은 함수이므로 자알~ 기억해 주시기를 바랍니다.

19~21번 줄에는 SQL 문이 등장을 합니다. 그런데 지금까지 보았던 Select 문과는 약간 느낌이 다르네요.
19번 줄에 등장하는 Count(*)는 해당하는 게시물의 갯수 를 나타냅니다.
만약 board 테이블에 게시가 모두 10개가 있는데 'Select Count(*) from board' 라고 하면 10이라는 숫자가
반환된다는 의미지요. (Where + 조건문이 포함된다면, 그 조건문에 해당하는 게시의 갯수가 반환되겠지요?)

20번 줄은 총 페이지의 수를 나타내는 부분이 되겠습니다. 그런데 이부분은 알아내기가 조금 까다롭습니다.
20번 줄은 "CEILING(CAST(Count(*) AS FLOAT) / " & intPageSize & ")"인데.. 이 문장은 무엇을 의미할까요?
웬지 상당히 복잡하게 보이는데요.. 한번 차근차근히 알아보도록 하겠습니다.

우선 첫째로, 앞에 나온 CEILING() 함수 를 알아보도록 하겠습니다.
CEILING 함수는 괄호 안에 들어 있는 지정한 숫자 식보다 크거나 같은 최소 정수를 반환 합니다
예를 들어 CEILING(12.34) 이라고 하면 13이라는 숫자를 반환합니다.
CEILING(12.0001) 이라면 어떨까요? 이런 경우에도 13이라는 숫자를 반환합니다.
하지만.. CEILING(12) 라고 하는 경우에는 12라는 숫자를 반환하게 되지요.

두번째로, CEILING() 함수의 괄호 안에 있는 함수인 CAST(Count(*) AS FLOAT) 에 대해서 알아보겠습니다.
우선 CAST 함수는 다음과 같은 형식을 가집니다.

CAST(내용 AS 변환하려는 형식)

이 함수는 '내용' 에 해당하는 문장을 '변환하려는 형식' 에 맞게 변환하라 는 의미를 가집니다.
이번에도 예를 들면 다음과 같습니다.

CAST(12.34 AS INT) - 이 경우 12.34라는 실수가 12라는 정수형(INT형)으로 변환됩니다.
CAST(12.99 AS INT) - 이 경우에도 12.99라는 실수가 12라는 정수형(INT형)으로 변환됩니다.
CAST(12 AS FLOAT) - 이 경우 12 라는 정수가 12.0 이라는 실수형(FLOAT형)으로 변환됩니다.

20번 줄에서 사용된 함수에 대한 설명은 다 된것 같네요. 그런데 우리는 아까 intPageSize의 값을 10으로
설정했었습니다. 기억나시죠? 그러므로 20번 줄 SQL 문의 내용은 다음과 같이 다시 쓸 수가 있겠습니다.

CEILING(CAST(Count(*) AS FLOAT) / 10)

그러면 이 내용을 해석해 보도록 하겠습니다.
우선 게시물의 총 갯수(Count(*))를 FLOAT(실수) 형으로 변환 시킨 후, 페이지의 크기인 10으로 나누어 줍니다.
그렇게 나온 결과를 CEILING 함수를 통해서 정수형으로 다시 변환을 시켜줍니다.
이런 과정을 통해서 나온 값이 바로 총 페이지의 수가 되겠습니다.

'왜 이래야 하는거지? 총 페이지 수를 구한다매? 그렇다면 변환인지 뭔지 생략하고 그냥 게시물 갯수 나누기 페이지 크기, 즉 Count(*) / 10  하면 되는거 아닌가?' 라는 생각이 드실지도 모르겠네요.
좋은 생각입니다. 하지만 그랬을 경우 자그마한 문제가 발생하게 됩니다.
만약 한 페이지에 10개의 게시가 보여지게끔 페이지를 나누었을 경우, 게시가 10개 있다면 총페이지의
숫자는 어떻게 될까요? 예. 1페이지가 됩니다. 한페이지에 정확하게 들어가지요.
하지만 게시가 하나 추가가 되어 11개의 게시가 되면 한개의 게시는 다음 페이지로 넘어가야 하고
때문에 페이지의 갯수는 한 페이지가 늘어난 2페이지가 되어야 합니다.

정리하면.. 10개까지의 게시가 있을 경우에는 1페이지가 되지만, 11개에서 19개까지의 게시가 있을
경우에는 2페이지가 되어야 한다는 것입니다. 그래서 CEILING() 이라는 함수가 필요했던 것입니다.
총게시물 수에서 페이지 크기로 나눈 값에서 나머지가 있다면(즉, 게시가 10개가 아닌 11개, 12개...
등일 때 Count(*) / 10 의 값은 1.1, 1.2 처럼 나머지가 생기게 된다는 뜻입니다)
페이지가 하나 더 생겨야 하므로 CEILING() 함수로 그것을 계산한 것이지요.

그리고, CEILING() 함수 안에서 CAST() 함수로 총 게시물의 갯수인 Count(*) 를 실수형으로 변환한 것은
Count(*) 의 값은 정수형이고, 나누는 값인 10 또한 정수형이기 때문입니다. MS-SQL 에서는 정수를 정수로 나누면 정수를 반환 합니다. (예를 들면 12/10 = 1 이고, 19/10 = 1이라는 의미입니다.)
그렇기 때문에 Count(*) 를 실수형으로 바꾸었습니다. (12.0/10 = 1.2 이고, 19.0/10 = 1.9 가 됩니다)

나름대로는 쉽게 말씀드리려고 했는데, 상당히 복잡해진 것 같네요. ^^

이렇게 작성된 19~21번 줄까지의 SQL 문을 28번 줄에서 실행하게 됩니다.
(물론 23, 24번 줄에서 Connection 개체, RecordSet 개체의 인스턴스를 생성한 것과,
26번 줄에서 Connection 개체의 인스턴스인 objDBConn 을 Open 시킨 부분은 말씀 안드려도 되겠지요?)

29번 줄에서는 게시물의 총 갯수인 Count(*) 값을 intTotalCount라는 변수에 대입하였고,
30번 줄에서는 총 페이지 수인 CEILING(CAST(Count(*) AS FLOAT) / 10) 값을 intTotalPage 에 넣었습니다.
그리고.. 31번 줄에서 objRs 를 닫은(close) 것을 놓치지 말아주세요. 이렇게 닫은 후에는 42번 줄에서 새롭게
다른 정보를 담아서 재사용할 수가 있습니다. 만약 31번 줄이 없다면 42번 줄에서 에러가 나게 됩니다.

그리고 33번 줄에서 테이블에 있는 정보를 가져오는 SQL문이 약간 수정된 것을 볼 수가 있습니다.
다른 부분은 지난번 list.asp 페이지에서 사용한 SQL 문과 같은데요. 단 한가지, Top 이라는 문장이 들어갔네요.
Top 이라는 단어에서 대충 감을 잡으실 수 있나요? 이 Top 이라는 단어는 숫자와 함께 쓰여서
'정렬된 순서대로 숫자 만큼의 게시만 가져오라 ' 는 뜻이 됩니다. 예를 들어 보겠습니다.

Select Top 5 intSeq, strName, strEmail from Board ORDER BY intSeq desc

위와 같은 구문을 Query Analyzer 에서 실행한다면 가장 최근에 등록된 게시부터 5개까지만을 가져오게 됩니다.
만약 3개밖에 없을때 위의 구문을 실행하면 3개의 게시를 모두 가져오게 되겠지요. 어렵지 않으시지요?

42번 줄에서는 다시 작성된 strSQL 문을 실행하여 objRs에 모든 정보를 저장하였습니다.
이때 만약 objRs 에 정보가 하나도 없다면, 즉 게시판에 내용이 하나도 없다면 69~71번 줄의 내용이 실행됩니다.
(이 경우 간단하게 '등록된 게시가 없습니다' 라는 내용만이 보여지게 될 것입니다.)

하지만 게시가 있다면.. 73번 줄 이하의 내용이 실행될 것입니다. 가장 먼저 등장하는 74번 줄에서는
Move 라는 RecordSet 개체의 메소드가 나오게 되는데요. 이 메소드는 말 그대로 '이동시키는' 메소드입니다.
무엇을 어디로 이동시키냐고요? 현재의 커서를 그 위치에서 Move 다음에 지정된 숫자만큼 이동 시킵니다.

이것을 그림으로 나타내면 다음과 같습니다.

<그림 1>



위의 <그림 1> 에서는 총 30개의 게시가 들어있다고 가정한 화면이 있습니다.
조금 복잡해 보이시나요? 그럼 천천히 다음 설명을 읽어주시기 바랍니다.

우선 가장 처음, 페이지가 지정되지 않은 경우라고 가정하고 이야기를 진행해 보도록 하겠습니다.
페이지가 지정되지 않았을 경우에는 15~17번째 줄에 의해 자동적으로 1 페이지가 됩니다.
그러므로 33번 줄에서, SQL 문 안에 있는 Top 다음의 숫자는 '페이지(1) * 페이지의 크기(10)' 이므로
strSQL = "Select Top 10 intSeq... (후략)" 이 될 것입니다. 따라서 이 strSQL 문에 의해서는 30개의 게시
모두를 가져 오는 것이 아닌 위에서 10개의 게시을 가져오게 됩니다. 여기까지는 이해 가시지요?

또, 74번 줄에서는 '(현재 페이지(1) - 1) * 페이지 크기(10)' 일 것이므로, objRs.Move 0 이 될 것입니다.
Move 0 이라는 것은 무엇을 의미할까요? 네에~ 그렇습니다. 이동이 없다는 의미 가 되지요.
정보를 가져온 레코드셋의 커서(Cursor)는 기본적으로 가장 첫번째 레코드(게시)에 위치합니다.
그러므로 위의 그림에서는 가장 첫번째 게시(30번 게시)에 커서가 맞추어져 있을 테지요.

그러면 정리해 볼까요? 1 페이지인 경우(또는 페이지 값이 없는 경우)에는..
커서(Cursor)는 가장 첫 게시인 30번 게시에 맞추어져 있으며, SQL 문은 게시를 상위 10개만 가져옵니다.
따라서 1 페이지에서는 가장 최근의 게시(30번 게시)부터 10개의 게시(21번 게시)까지 보여지게 되는 것이지요.

어떻습니까? 이해가 가시는지요.. 아직 무언가 부족하시다고요? 그럼 2페이지도 한번 알아보도록 하지요.
33번 줄의 SQL 문 안의 Top 다음의 숫자는 페이지(2) * 페이지 크기(10) 이므로 20이 될 것입니다.
Top 20, 즉 가장 처음 게시에서부터 20개의 게시(11번 게시까지)를 가져오라는 의미가 되는 거지요.
그 다음 74번 줄에서, 커서를 이동(Move)시킬 곳은 (2-1) * 10 이므로 10이 될 것입니다.
objRs.Move 10, 즉 위에서 10칸을 이동하므로 20번 게시가 있는 곳으로 커서를 이동시키라는 뜻이 됩니다.
따라서 2 페이지에서는 20번 게시에서부터 11번 게시까지의 10개의 내용이 보여지게 될 것입니다.

'Top 20 이니까 20개가 보여져야 하는 것이 아닌가요?' 라고 의문을 가지실 수 있을텐데요.
위에서 20개를 가져온 것은 맞습니다만, objRs.Move 10 에 의해 커서를 가장 위의 게시(30번 게시)에서
10칸 아래(20번 게시)로 이동시킨 까닭에 20번 게시부터 11번 게시까지 10개의 게시가 보여지는 것입니다.

어떻습니까.. 지금까지 페이지를 나누는 방법에 대해서 잠시 알아보았습니다.
이해가 잘 안가실 수도 있겠습니다만, 이 부분이 핵심이므로 꼭 이해하시고 넘어가기를 바랍니다.

은근슬쩍 넘어갈 뻔 했던 51번 줄부터 59번 줄까지의 내용은, 이 게시판에 등록된 게시물의 총 수
(intTotalCount)와 현재의 페이지(intNowPage), 그리고 아까 어렵게 계산했던 총 페이지 (intTotalPage)를
보여주는 부분이 되겠습니다.
그런데.. 51번 줄의 If 문은 무슨 의미로 쓰인 것일까요?
아까 19번 줄 SQL 문에서 사용된 레코드의 총 수를 구하는 Count(*) 라는 함수를 보셨는데요.
등록된 레코드가 한개도 없다면 Count(*) 함수는 0 이라는 숫자 값을 반환합니다.
즉, 51번의 If intTotalCount > 0 Then 이라는 구문의 의미는 '게시가 한개이상 있다면' 이라는 의미가 됩니다.
만약 게시가 한개도 없다면 52~58 번 줄의 내용은 보이지 않게 되지요.

그리고.. 중복되는 부분은 생략하고요. 99번째 줄로 넘어가도록 하겠습니다.
99번 줄부터 131번 줄까지는 페이지를 쉽게 이동할 수 있도록 각 페이지별로 나열하는 부분이 되겠습니다.

자.. 무언가 복잡해 보이는 부분이 또 등장을 했군요.
빨간 글자가 잔뜩 보이니까 괜시리 복잡할 것 같지 않습니까? 겁먹지 마시고 천천히 살펴보도록 하지요. ^^

우선.. 99번 줄부터 131번 줄이 적용된 결과 페이지를 먼저 보고 말씀을 드리도록 하겠습니다.
적용된 결과 페이지는 다음과 같을 것입니다. (위의 게시판 내용부분은 생략했습니다)
아까 게시가 30개 있다고 가정했으므로 페이지는 총 3 페이지가 되어 있을 것입니다.

[이전 10개] 1 2 3 [다음 10개]

앞뒤에 있는 [이전 10개], [다음 10개] 는 여러분이 13번 줄에서 intBlockPage 라는 변수에 주었던 값이 됩니다.
(만약 intBlockPage 를 5로 주었다면 [이전 5개], [다음 5개] 가 될 것입니다.)

그리고 게시물의 페이지 수가 10 보다 큰 경우(다시 말해 페이지 수가 intBlockPage 보다 큰 경우)에는
상황에 따라 [이전 10개] 나 [다음 10개] 란에 링크가 걸려있어야 할 것입니다.
예를 들어 현재의 페이지가 23페이지인 경우에는 [이전 10개] 와 [다음 10개] 란에 모두 링크가 걸려 있어야
하며, [이전 10개] 의 링크를 클릭했을 경우에는 11페이지로, [다음 10개] 의 링크를 클릭했을 경우에는
31페이지로 이동을 해야 할 것입니다.
그렇다면.. 더이상 [이전 10개] 가 필요없을 것 같은 1~10 페이지라면 어떨까요?
네에~. [이전 10개] 라는 부분은 필요가 없기 때문에 그에 따른 적절한 처리를 해주면 됩니다.
(보이지 않게 하거나, 링크가 걸리지 않게 해주면 될 것입니다. 우리는 후자의 방법을 택하도록 하겠습니다.)

자.. 그럼 본격적으로 99 ~ 131번 줄의 소스를 살펴보도록 하겠습니다.
우선 103번 줄에 intTemp = Int((intNowPage - 1) / intBlockPage) * intBlockPage + 1라는 줄이 나옵니다.
여기의 intTemp 라는 변수는, 위에서 잠시 언급한 [이전 10개] 와 [다음 10개] 의 링크를 클릭했을 경우
보여지는 첫번째 페이지 (1페이지, 11페이지, 21페이지, 31페이지...)를 계산하기 위한 임시적인 변수입니다.
(이것은 intBlockPage 의 값이 10인 경우를 말씀드리는 것이고, 만약 intBlockPage 의 값을 5로 지정했을 경우
 intTemp 의 값은 1, 6, 11, 16... 이 될 것입니다. 지켜보면 아시겠지만 이 변수는 상당히 유용하게 쓰지요.) 

105~109번 줄까지의 내용을 살펴보겠습니다. (106번 줄은 한줄로 작성하셔야 합니다.)
105~106번 줄은 만약 intTemp 의 값이 1인 경우에는 현재 페이지가 intBlockPage 보다 작기 때문에 링크가 걸리지 않은 [이전 10개]를 보여준다는 의미가 되겠고요.
105~106번 줄은 만약 intTemp 의 값이 1이 아닌 경우에는 현재 페이지가 intBlockPage 보다 크기 때문에
링크가 걸린 상태의 [이전 10개] 를 보여준다는 의미가 되겠습니다.
무슨 말인지 잘 모르시겠다고요...? 그렇다면 예를 들어보겠습니다.

지금 현재의 페이지가 9페이지라고 가정을 해보지요. 그렇다면 103번 줄에 의해 intTemp 에 입력되는 값은
Int ((9 - 1) / 10) * 10 + 1 = 1 이 됩니다. (0.8 을 Int 형으로 바꾸면 0이 되기 때문입니다.)
intTemp 값이 1이므로 105~106번 줄에 의해 [이전 10개] 부분에는 링크가 걸리지 않게 됩니다.
이것은 당연한 결과입니다. 지금 현재 9페이지라면 '이전 10개의 페이지' 는 있을 수 없기 때문입니다.

그렇다면 만약 10페이지를 넘긴 13 페이지라면 어떨까요?
이때의 intTemp 값은 Int((13 - 1) / 10) * 10 + 1 = 11 이 됩니다.
그러므로 이번에는 105~106번 줄이 아닌 107~108번 줄에의해
<a href=list.asp?page=1>[이전 10개]</a> 와 같이 링크가 걸린 모습이 보여지게 됩니다.
이것 또한 올바른 결과입니다. 13페이지라면 '이전 10개의 페이지' 가 분명히 존재하기 때문입니다.
또한 링크가 걸려진 [이전 10개] 부분을 클릭하면 페이지는 1페이지로 이동하게 될 것입니다.

또 다른 예를 하나만 더 들어보겠습니다.
현재의 페이지가 27페이지라고 가정할 때, intTemp 의 값은 21이 될 것입니다.
그러므로<a href=list.asp?page=11>[이전 10개]</a> 와 같은 링크가 걸려지게 될 것입니다.
이 부분을 클릭하게 되면 페이지는 11페이지로 이동을 하게 될테고요.

조금 장황하게 설명이 되었는데요. 어쨌든 [이전 10개] 부분은 이와 같이 작동을 하게 됩니다.
잠시 후에 다루어 볼 [다음 10개] 부분도 지금 살펴본 [이전 10개] 부분과 같은 구성이기 때문에,
지금 이 부분을 잘 이해하셨다면 그다지 어려울 부분이 없을 것 같습니다.

111번 줄부터 121번 줄까지는 직접 이동시킬 페이지를 순서대로 보여주는 부분이 되겠습니다.
우선 111번 줄에서는 intLoop 라는 변수의 값에 1을 주었고요. (이 변수는 순환문(Do~Loop 문)에서 사용합니다)
113~121번 줄에서는 'intLoop 값이 intBlockPage(여기서는 10)보다 커질 때까지' 또는 ' intTemp 값이 총
페이지보다 커질 때까지
' 순환문을 실행하게 됩니다.

114번 줄부터 115번 줄까지의 내용은 순환문을 통해 나타나는 페이지 중에서 지금 현재의 페이지와 일치하는
페이지에는 링크를 걸지 않는 대신, 현재의 페이지라는 것을 강조하기 위해서 글자 크기를 3으로 늘리고
bold 체를 적용하라는 의미가 되겠습니다.

그런데 114번 줄에서 CInt() 함수가 등장을 하는 것을 보실 수가 있습니다.
이 함수는 '괄호 안에 있는 숫자 값을 정수형으로 변환시키라' 는 의미를 가지고 있습니다.
우리가 Request.QueryString 으로 받아온 값인 intNowPage 는 숫자입니다만, asp 는 이값이 숫자임에도 불구하고 이것을 정수형이 아닌 Variant 형으로 인식합니다. 때문에 일부러 이러한 과정을 거쳐서
정수형으로 변환시켜 준 후, intTemp 값과 비교를 하는 것을 볼 수가 있지요.
 
116번 줄부터 117번 줄까지는 현재 페이지가 아닌 경우, 해당하는 페이지로 이동하는 링크를 걸어주는 내용이
되는 것이고요. 이 내용은 그다지 어렵게 느껴지지 않으리라 생각합니다.

119번 줄과 120번 줄은 순환문을 계속하기 위해서 intTemp 와 intLoop 라는 변수를 각각 1씩 증가시켜
주는 내용이 되겠습니다.

이렇게 순환문을 모두 마친 후 123~127번 줄은 [다음 10개] 에 대한 내용이 되겠습니다.
주의 깊게 살펴보시면 이 부분 역시 [이전 10개] 와 같은 형식을 가진다는 것을 눈치채실 수 있을 것입니다. 

그럼 123~124번 줄의 내용을 살펴보도록 할까요?
Do~Loop 순환문을 빠져나온 intTemp 의 값이 총 페이지 수(intTotalPage) 보다 크다면,
(즉 순환문에서 이미 마지막 페이지까지 보여지게 되었다면 119번 줄을 통해 intTemp 값은 총 페이지 수인
intTotalPage 보다도 1이 커진 값이 될 것입니다. 이 경우를 말하는 것이지요)
더 이상의 페이지는 없다는 의미가 되므로 [다음 10개] 에는 링크가 걸리지 않을 것입니다.
하지만 그렇지 않다는 것은 아직 남은 페이지가 더 있다는 것을 의미하므로(125~126번 줄)
[다음 10개] 부분에는 링크가 걸리게 되며, 이 링크는 Do~Loop 순환문을 통해 나열된 마지막 페이지보다
1이 더 큰(119번줄을 통한 intTemp) 값을 가진 페이지로 이동시켜 줄 것입니다.

이것으로 페이지 나누기에 관련된 이야기가 끝났습니다.
아마도 오늘의 강좌가 지금까지 제가 지금껏 올렸던 강좌중에서 가장 까다롭고 난해하지 않았나.. 싶은데요.
나름대로는 쉽게 설명하고자 했습니다만, 많이 부족했던 것 같습니다.
잘 이해가 가지 않으시더라도, 반복해서 살펴보시고 또 적용해 보시다 보면 이해가 가리라 생각합니다.

자.. 그럼 이것으로 '페이지 나누기' 에 대한 강좌를 모두 마치....기 전에 말이지요. (놀라셨나요? ^^)
페이지를 나누기가 끝났다면, 조금 귀찮으시더라도 더 효율적인 게시판이 되기 위해 약간의 수정 을 더
해주는 것이 좋습니다.

페이지 나누기를 적용시킨 까닭에.. 사실 지금 우리의 게시판에는 조그마한 문제가 발생했습니다.
내용을 보고 난 후, 또는 글을 수정하고 난 후에 우리가 보고있던 페이지가 아닌 첫페이지로 돌아와 버리는
문제
가 바로 그것인데요.. 알기 쉽게 말씀드리면 다음과 같습니다.

만약 3페이지에 있는 게시의 내용을 보고, '목록으로' 를 클릭해서 리스트로 돌아오면 어떻게 될까요?
우리가 원하는 것은 다시 3페이지로 돌아오기를 바랄 것입니다만, 현실은 그렇지가 못합니다.
1페이지로 이동하기 때문이지요. 왜냐하면 목록보기 페이지로 넘어올 때 3페이지로 넘어오라는 어떠한
정보도 넘겨주지 않았기 때문입니다. 페이지에 대한 정보가 없으면 현재 페이지는 15~17 줄에 의해
1페이지가 된다는 것은 오늘의 강좌에서 이미 몇차례 말씀을 드린 기억이 있습니다.

그렇다면 이 문제를 어떻게 해결하면 될까요?
가장 보편적인 방법은 Url 의 뒤에 붙여서 값을 넘기는 Get 방식으로 현재의 페이지를 전달하는 방식 입니다.
위의 수정된 list.asp 소스 중에서 80번 줄에 다음과 같은 부분이 있습니다.

<a href="content.asp?seq=<%=objRs(0)%>"><%=objRs(3)%></a>

여기서는 게시물의 번호인 seq 값만 넘겨주고 있는 것을 보게 되는데요.
이때 현재 페이지의 값인 intNowPage 를 추가해서 넘겨주면 됩니다. 다음처럼 말이지요.

<a href="content.asp?seq=<%=objRs(0)%>&page=<%=intNowPage%>"><%=objRs(3)%></a>

어떻습니까? 상당히 간단하지 않습니까?
그렇다면 이 값을 전달받는 content.asp 페이지에서도 무언가 수정이 있어야 할 것 같지 않나요?
빙고~ 입니다. content.asp 페이지에서는 방금 넘겨준 intNowPage 값을 받아서, 목록으로 돌아가려 할때
그 값을 전달해주기만 하면 되겠습니다. 어떻습니까.. 대충 감이 잡히지 않으시나요?
그러므로 content.asp 페이지에서 추가되어야 할 부분은 다음과 같습니다.

Dim intNowPage                                                                           ' 몇페이지였는지를 저장하는 변수
intNowPage = Request.QueryString("page")                                    ' 넘겨준 페이지 값을 받아서 저장
---중략----
<a href="list.asp?page=<%=intNowPage%>">목록으로</a>           ' 목록으로 이동시킬때 값을 전달

아마 이정도만 말씀드려도 여러분들께서는 적당한 위치에 추가 및 수정하실 수 있으시리라 생각합니다.
여기까지 강좌를 잘~ 따라오신 여러분들이라면 말이지요. ^^

이 부분은 비단 content.asp 페이지에만 적용되는 것은 아닙니다. 수정하기 페이지인 edit.asp -> edit_ok.asp
페이지에서도 마찬가지 방법으로 intNowPage 값을 전달해서, 수정이 완료될 때 수정이 이루어지기 전의
목록 페이지로 이동시킬 수가 있을 것입니다.
이 부분의 응용은 여러분들께서 충분히 하실 수 있으리라는 생각에.. 여러분들의 몫으로 남겨 두겠습니다.

제가 지난 강좌에서 오늘 '페이지 나누기' 부분이 게시판 만들기의 백미라는 말씀을 잠시 드렸는데요.
오늘의 강좌를 지금껏 따라오시면서 같이 해보신 분들께서는 이제 제 말에 조금 공감을 하시리라 생각합니다.
조금 힘들고 이해하기 어렵더라도, 천천히 그리고 꾸준히 이해하려 노력하시다 보면 어느덧 한걸음 더 나아간
자신의 모습을 발견하실 수 있을 것입니다.

복잡하게 진행된 오늘의 강좌를 여기서 줄이기로 하겠습니다. 고생 많이 하셨습니다.
다음 시간은 예정된 강좌의 마지막 부분인 '검색하기' 페이지를 작성해 보도록 하겠습니다.
검색하기 부분은 오늘의 페이지 나누기 보다는 훨씬 쉬울것 같다는.. 개인적인 생각을 해봅니다. ^^
그럼 다음 강좌까지 건강하시고, 감기 조심하세요~...


P.S (추신, 덧붙임..)

한가지 부탁의 말씀을 드려야 될 것 같습니다.
제 강좌가 초보용 강좌라서.. 제게 스스로 '왕초보'라고 생각하시는 분들의 많은 질문 메일이 오는데요.
질문을 하실 때, '제대로 입력을 했는데 에러가 나요. 왜 그럴까요?' 또는 '도대체 모르겠어요. 왜 안되는 걸까요?'
와 같은 추상적인 질문을 해주시는 분들이 의외로 많이 계십니다.
물론 질문 자체를 어떻게 해야 할지 몰라서 답답한 마음에 이렇게 물어오신다는 점을 충분히 이해를 합니다만,
이런 질문을 받게 되면 저 역시 질문하시는 분께서 어떤 문제로 고민을 하고 계신지 알 수가 없어서
상세한 답변을 드리기가 매우 어렵습니다. 그런 경우에는 정말 난감하지요.

그래서.. 에러가 났을 경우에 그 에러화면을 그림 파일로 저장해서 보내주시거나, 또는 에러 메시지와 작성하신
소스를 함께 보내주시면 제가 답변 드리는데 많은 도움이 될 것 같습니다.
그리고 정말 급한 질문이시라면 제게 메일을 주시는 것보다는, 이 사이트(taeyo's ASP)의 Q & A 게시판에
올리시는 것이 더 빠른 답변을 받으실 수 있으리라고 생각합니다.


태지의 화(話)

강좌중 의문사항이나 하시고픈 말씀 겸허하게 받겠습니다: maestrody@orgio.net


authored by


 
 
.NET과 Java 동영상 기반의 교육사이트

로딩 중입니다...

서버 프레임워크 지원 : NeoDEEX
based on ASP.NET 3.5
Creative Commons License
{5}
{2} 읽음   :{3} ({4})