관리 메뉴

새로운 시작, GuyV's lIfe sTyle.

닷넷 게시판 만들기 Part 45 - 리스트 페이징 전략 및 방법 연구 본문

ⓟrogramming/asp.net 게시판

닷넷 게시판 만들기 Part 45 - 리스트 페이징 전략 및 방법 연구

가이브 2011. 6. 14. 19:10


2011/05/13 - [ⓟrogramming/asp.net 게시판] - 닷넷 게시판 만들기 Part 31
2011/05/16 - [ⓟrogramming/asp.net 게시판] - 닷넷 게시판 만들기 Part 32
2011/05/18 - [ⓟrogramming/asp.net 게시판] - 닷넷 게시판 만들기 Part 33
2011/05/19 - [ⓟrogramming/asp.net 게시판] - 닷넷 게시판 만들기 Part 34
2011/05/23 - [ⓟrogramming/asp.net 게시판] - 닷넷 게시판 만들기 Part 35
2011/05/25 - [ⓟrogramming/asp.net 게시판] - 닷넷 게시판 만들기 Part 36
2011/05/26 - [ⓟrogramming/asp.net 게시판] - 닷넷 게시판 만들기 Part 37
2011/05/27 - [ⓟrogramming/asp.net 게시판] - 닷넷 게시판 만들기 Part 38
2011/05/30 - [ⓟrogramming/asp.net 게시판] - 닷넷 게시판 만들기 Part 39
2011/05/31 - [ⓟrogramming/asp.net 게시판] - 닷넷 게시판 만들기 Part 40
2011/06/01 - [ⓟrogramming/asp.net 게시판] - 닷넷 게시판 만들기 Part 41
2011/06/08 - [ⓟrogramming/asp.net 게시판] - 닷넷 게시판 만들기 Part 42
2011/06/09 - [ⓟrogramming/asp.net 게시판] - 닷넷 게시판 만들기 Part 43
2011/06/13 - [ⓟrogramming/asp.net 게시판] - 닷넷 게시판 만들기 Part 44


1. 닷넷 개발환경 준비, 테스트
2. 닷넷 알아보기 [7/7]
3. asp.net 컨트롤 [10/10]
4. 데이터베이스(DB) [7/7]
5. 닷넷 게시판을 만들어보기 전에.. [4/4]
6. 게시판 만들기 [15/..]


프로그래밍의 목적은 무엇일까? 컴퓨터는 "소프트웨어"와 "하드웨어"로 나누어진다. 하드웨어는 눈에 보이는 기계들이고, 소프트웨어는 눈에 보이지 않는 프로그램이다. 소프트웨어는 시스템소프트웨어와 응용소프트웨어 등으로 나누어진다. 윈도우XP/Vista/7, 리눅스, 유닉스, 안드로이드, iOS 이런 녀석들을 시스템소프트웨어라고 할 수 있는 운영체제(OS)라고 한다. 그리고 이런 운영체제가 해석하는 방식에 맞추어서 프로그램을 제작하는 것을 응용 소프트웨어 개발이라고 한다. 프로그래밍의 목적은 컴퓨터를 사용하는 '사람'이 취미/학습/업무/정보제공을 (효율적으로) 할 수 있도록 사용할 수 있는 프로그램을 만드는 것이다. 

인터넷을 하기 위해서 필요한 것이 1차적으로 "하드웨어"이다. 하드웨어가 필요한 이유는 소프트웨어를 사용하기 위해서이다. 시스템소프트웨어는 하드웨어를 소프트웨어에서 사용할 수 있게 하는 목적이다.

요즘 프로그램들은 대부분 인터넷이라는 서비스를 연동하여 만들어지는 경우가 많다. 압축프로그램 같은 경우도 자동 업데이트를 수행하기 위해서 연동하기도 하고, 무료이기에 광고를 보여주게되는데, 이 광고 역시 인터넷을 통해 여러개를 번갈아가며 가져올 수 있는 것이다. 만들어서 제공하는 사람들에게나, 다운받아서 사용하는 사람들에게나 모두에게 만족할 수 있다.

우리가 하고 있는 게시판 만들기는 '웹 프로그램'의 일부분이다. 인터넷 서비스에는 표준기술이 많은데, 그 중 HTTP라는 규약을 사용하는 HTML을 이용해서 각종 정보를 제공하는 웹사이트를 만드는 것이다.

우리가 다루는 asp.net 은 MS의 닷넷 프레임워크의 일부분이라고 할 수 있다. 닷넷 프레임워크로는 현재 게임(xbox, 스마트폰용, PC용 각종 게임들), 스마트폰 어플리케이션, 우리가 하고 있는 웹 등 모든 것을 개발할 수 있다. asp.net 에서는 여러가지 문법을 선택할 수 있는데, C# 문법으로 현재 개발중이다. 게임이든 스마트폰 어플이든 웹이든, 자료(Data)를 관리하기에 매우 유용한 DBMS를 각자 사용한다. Microsoft(MS) 에서 개발한 언어이기에 같은 MS의 SQL Server 를 사용하는 것이 좀 더 유리하지만 다른 상용 DBMS 또는 MySQL 같은 무료 DBMS를 사용해도 무관하다. 또한 DBMS는 꼭 연동되는 어플리케이션이 없이 홀로 잘 작동하는 녀석이다. 말 그대로 필요에 의해 오늘날에는 프로그램 기술의 발전 끝에 연동해서 사용할 수 있게 된 것이다.

그.래.서!


1. asp.net 과 태그<tag>는 아~무 관련없다.
2. asp.net 과 DB는 아~무 관련없다.



각각은 그저 관련있게 보일 뿐이다. asp.net 의 목적상, 웹 어플리케이션을 만들기 위해 만들어진 기술이 맞지만 정작 asp.net 은 자기가 무슨 행위를 하는지 모르고 있다. 내부적으로 그렇게 만들어놨기 때문이다. DB연동할 때마다 작성한 문자열 query 변수에 넣는 그 상수값이 asp.net 에서는 쿼리문인지 결코 알 수 없고, 알 필요가 없다. asp.net 은 '객체놀이'에 만족만 시켜주면 절대 오류를 발생시키지 않기 때문이다. 

이렇게 강조하는 이유는 다른 것 없다. 대학이든 IT교육기관이든 회사 선배, 친구, 수많은 서적에서, 스스로 공부할 수 있는 과제를 던져주지 않기 때문에 프로그램을 공부하려는 사람들은 일반적으로 시작이 매우 어렵다. "자~ 이거 만들어봐라"라는 개념이 아니라, for(;;) 를 하나 알려주면 끝이 아니라 이 녀석으로 무얼 할 수 있는지 고민할 수 있는 기회를 만들어주지 않는다는 말이다. 그저 "이렇게 쓰면 돼." 하고 만다.

프로그래밍 쪽으로 시야를 넓히는 방법은 많이 만들어서 결과를 많이 봐서 효과를 볼 수 있겠지만, 하나를 완성하더라도 어떻게 작동하게 되는지는 꼭 이해하시길 바란다. 좋은 방법은 겨우겨우 몇 주동안 밤새서 만든 소중한 프로그램을 과감히 삭제하고 처음부터 다시 시작하는 것이다. 또 다른 것을 만들어보는 생각도 좋긴 하지만, 그렇게 100개 1000개 만들 때마다 몇 주동안 밤을 샐 수는 없지 않은가? 여러 기술이 복잡하게 들어가는 녀석 하나만 신나게 파주면 되겠다.

필자가 생각하는 이른바 '삽질한다'의 정의는, 프로그램을 만들기 위해 삽질한 결과물 보다, 파낸 흙이 더욱 값지다는 것이다. 신나게 땅파서 보물 하나 찾는건 일도 아니다. 아~무 생각없이 파고파고 또 파면 되니까. 파낸 흙에서 보물의 위치를 파악하고, 쉽게 파내는 방법을 파악해내시길 바란다.



지난 시간에 이어서 게시판 리스트를 이어가도록 하자.
검색에 이어서 이번에 해볼 기능은 글이 많아질 때 나타나는, 글이 많이 작성되어서 목록이 수십개 수백개 길어지는 현상을 해결하기 위해 대부분 게시판에서는 '페이징(Paging)' 기능이 있다. 잘 상기하시기 바란다. 목적이 있기 때문에 기능이 존
재하는 것이다.






위의 그림처럼 게시판, 쇼핑몰 등에서 이러한 링크를 제공하고, 링크를 클릭하면 페이지를 자유자제로 이동할 수 있게 하는 기능이다. 꼭 저렇게 숫자로 표현하지 않아도 [이전페이지], [다음페이지] 정도로 페이지 이동을 할 수도 있다. 사실 예전글을 찾기 위해서는 사람들이 몇백 페이지 정도 되는 페이지를 클릭하며 찾는게 아니라 검색을 이용하는 경우가 많을 것이다. 결론은, 만드는 사람 마음이다. 사용하는 사람들에게 얼마나 편리할까를 고민하다 보니 일반적으로 저런 형태가 그나마 편리하다고 판단이 되었나보다. 


페이징 기능의 목적은 (1)전체 게시물에서 (2)'N'개씩 잘라 보여주는 기능이다. 먼저 개념을 파악해보자. 쉽게 생각하면 되겠다. 전체 자료에서 선택한 페이지 번호에 맞는 녀석만 쏙쏙 뽑아오면 구현이 가능하다.





이전 Part 에서 게시물을 100개 이상 넣었던 board 테이블의 내용이다. (게시판처럼 정렬은 하지 않았음을 참고하자) 현재 우리 게시판에는 이 목록들이 모두 뿌려지고 있는데, 위의 그림처럼 맨 상위의 10개를 잘라서 보여주게 되면 저렇게만 보여지게 될 것이다. 그러니까 처음부터(1) 10개까지이다. 다음 그림을 보자.






그 다음부터의 게시물, 11번째부터 10개가 이제 2페이지에 해당하는 글이 될 것이다. 다음 페이지, 다다음 페이지도 마찬가지이다. 페이징을 구현하기 위해서 이렇게 데이터를 각각의 페이지에 해당하는 자료를 가져와야 하는 것이다.

가만히 생각해보면 패턴이 하나 나오게 된다. 맨 처음에 리스트(board_list.aspx) 페이지를 열면 책을 넘기듯이 생각해도 시작은 첫(1) 페이지 이다. 현재의 페이지는 무조건 1이 되겠다. 그리고 한 페이지에는 10줄씩 나오게 하도록 하자. 그럼 1페이지는 10줄, 그러니까 최근 게시물에서 위에서 10개를 보여주면 되는 것이다. 다음 페이지인 2페이지에서 10개, 그러니까 11번째 줄부터 20번째 줄까지이다. 이 패턴에서, '시작하는 위치'를 구하는 공식을 산수로 표현해보자.


[ 페이지 게시판에서 보여줄 데이터 '시작하는 위치' 구하기 ]

시작위치 = (현재페이지번호 - 1)  X 보여줄글수) + 1

※ 보여줄 글 수 : 10개



5페이지는 "((5-1) X 10) + 1" 이므로 시작위치는 41이다. 
32페이지는 "((32-1) X 10) + 1"이므로 시작위치는 311이다.
1페이지는 공식대로 시작위치는 1이다.

이렇게 시작하는 위치에서 "한 페이지에 뿌려줄 글 수"만큼 뿌려주면 되는 것이다. 꽤 쉽지 않은가? ^^

이제 페이징의 원리(라고도 할 것 없지만)는 알겠으나, 실제로 구현은 어떻게 할 것인가?
구현하는 방법은 매우 크게 두 가지로 나누어진다.


1. [DB] 페이지 값에 따라 쿼리문으로 그 위치에 해당하는 자료를 리턴
2. [asp.net] 여러개의 자료를 받는xxxDataAdapter.Fill 메서드에서 오버로드된 메서드를 이용



쉽게말해 DB 선에서 해결하는 방법이 있고, asp.net 선에서 해결하는 방법이 있다.
1번처럼 DB단에서 해결하려면 쿼리문에 대해 많은 고민을 해야한다. 우리가 사용하는 SQL Server 의 쿼리문 기능에 대해 조사를 할 필요가 있다. 여러분들이 절망을 느끼실까봐(?) 여기서는 다루지 않을 것이고, 편하게 관련 자료를 읽어보시기 바란다. 구글에서 몇 개의 링크를 가져왔다.

http://flashcafe.org/4319
http://www.davidhayden.com/blog/dave/archive/2005/12/30/2652.aspx
http://xeraph.com/3200838

이것은 DB에서 모든 것을 해결해 주기 때문에 프로그램 코드는 변경될 것이 많이 없겠다.

2번은 우리가 Database.DLL 에서도 사용하고 있지만, SqlDataAdapter.Fill(..) 메서드를 이용해서 쉽게 구현하는 방법이 있다.



위의 그림이 게시판 리스트 라이브러리인 Board.cs 에서 List(..) 메서드가 호출하는 Database.DLL 의 여러자료를 가져오기 위한 기능을 구현하는 메서드이다. DataTable 로 리턴하기 위해 먼저 DataSet 에다가 61번째 줄처럼 쿼리문의 결과를 그대로 채워주는데, 이 SqlDataAdapter.Fill(..) 메서드는 오버로드 된 메서드가 여러개 존재한다. 같이 MSDN에서 이 메서드를 확인해보자.

SqlDataAdapter.Fill 메서드


오버로드된 메서드가 꽤 많다. 설명을 읽어보면 그 중 다음 메서드를 찾을 수 있다.




설명이 좀 어렵지만 이 메서드를 이용해서 채워주면 쿼리문의 결과를 채울 대상인 DataSet(첫번재 인수)에 두번째 인수값(int)부터 세번째 인수값(int)갯수 만큼만 지정해서 넣게 되는 것이다. 일단 저 메서드 링크를 클릭해서 상세 설명을 각자가 쭉 읽어보자.




네번째 인수 값은 사용하지 않으므로 현재 사용하는 Database.DLL 처럼 대충 넣어주면 되겠다.
예제를 보면 더 확실할 것이다.




예제 설명에 "10행부터 시작하는 15개 행으로" 라고 했다. 그런데 실제 예제에서는


adapter.Fill(dataset, 9, 15, "..");


이렇게 10행 부터라 표현하는 인수가 10이 아닌 '9'이다. 이유는 첫 행 번호가 '0'부터 시작하기 때문이다. 앞에 나온 "매개 변수"설명에도 나와있듯이 '0'부터 시작하는 시작 레코드번호라고 했다.

이렇게 예제로 수행하면, 어떤 쿼리문인지는 모르겠으나 (SELECT는 확실하겠지?) dataset 에 해당 쿼리문의 결과를 채우는데, 결과를 뽑아낸 녀석에서 '10번째'부터 '15개'만 넣어라는 말이 되는 것이다.

잠시 예제 설명 위에 나와있는  노란색 박스 참고를 보자.

"DataSet에는 maxRecords에 표시된 개수까지의 레코드만 포함됩니다. 하지만 쿼리에 의해 생성되는 전체 결과 집합은 여전히 서버로부터 반환됩니다."

자.. 이 설명은 2번 방법에서 매우 중요한 내용이다. 어떻게 보면 당연한 말인데, 쿼리문은 어쨌든 데이터베이스에서 실행하게 된다. 필자는 asp.net 과 데이터베이스는 아~무 관련 없다라고 누차 강조한다. asp.net 에서 DB에게 명령을 내리는 것은 "쿼리문"밖에 없다. (하나 더 있긴하다. 저장 프로시저라는, 여기서는 다루지 않는 다른 명령 방식이 있다)

쿼리문을 "SELECT * FROM board" 이렇게 이미 날렸는데 '10번째 행 부터 15개만 넣어라'라고 해본들 DB에서는 이를 알아먹을 방법이 없는 것이다. 이미 쿼리는 실행되었고, 그 결과는 100개면 100개, 500개면 500개 대로 이미 asp.net 으로 넘어온 상태이다. 

정리를 하자면, 1번 방식을 이용하면 쿼리문으로 작성해야 하기 때문에 조금 복잡해질 수 있다. 사실 관련자료를 링크해드렸지만 그렇게 어려운 것은 없다. 관심있게 접근하면 구현이 가능하다. 당연히 1번 방식으로 우리가 만들고 있는 게시판에도 적용할 수 있다. 이 방식의 큰 장점은 성능이 좋다. 쿼리문은 복잡해질지 모르겠지만 어쨌든 asp.net 에서 받아오는 값은 최대로 해봐야 한 페이지 당 보여줄 글 수 만큼밖에 안되기 때문이다. 쿼리선에서 그렇게 뽑아냈으니, 10개면 10개지 절대 10개 이상 리턴되지 않으므로 쿼리 결과의 전송량이 적다.

2번 방식을 이용하면 쿼리를 그대로 사용하기 때문에 1페이지, 그러니까 상위 10개만 가져오면 되는데도 불구하고 쿼리 결과를 무조건 가져온다는데에 대한 성능 저하가 발생한다. 노란 박스의 글이 이런 의미이다. 그래서 2번 방식은 게시물이 천개 단위가 넘어가는 중대형 게시판에서 사용하기에는 추천하지 않는다. 그러나 구현하기가 쉽다는 억지(?) 장점이 있다.

일반적으로 실무에서 사용하는 방식은 1번이다. 아무래도 접속자에 대비한 성능 향상이 중요하기 때문이다.
설명이 좀 복잡할지 모르겠는데, 게시판의 페이징을 구현하는 방법은 이렇듯 크게 두 가지로 나눌 수 있다.


말 나온김에 1번 방식으로 구현해보자. (그냥 넘어가려했는데 ㅜㅜ)
실제로 쿼리문을 이용해 자료를 뽑아보기 위해 DB관리툴인 SQL Server Management 를 실행하고 로그인하자. 로그인에 성공했다면 좌측 DB목록에서 aspnet 을 클릭한 후 위쪽에 "새 쿼리(N)"를 눌러 쿼리문을 작성하고 실행할 수 있게 준비하자.

지금부터는 DB 쿼리문에 대한 내용이 많이 나오니 asp.net 을 잠시 잊기 바란다. ^_^ (필자도 짧은 지식으로 설명드리기가 힘들 것 같다. 편하기 읽으시기 바란다. 언제나 제일 중요한 것은 '시야를 넓히는 것'이다.)
또한 SQL Server 2005 버전 이상에서만 사용가능한 쿼리문이다. (우리는 지금 2008 버전으로 공부중이다)






백지가 열리면 쿼리를 실행할 준비가 된 것이다. 페이징을 하기 위한 쿼리는 "서브쿼리"를 이용하는 방법이다.
다른 분의 블로그에서 자세한 정보를 먼저 읽어보시기 바란다.

11장, 하위 쿼리 사용 : 서브 쿼리 사용 : Sub Query, 두 쿼리의 결합


블로그의 글을 인용하면, 다른 테이블에 있는 정보를 가져와 현재 테이블에서 찾고자 할 때, 서브 쿼리가 필요하다. 라고 되어있다. 사실 우리는 지금 다른 테이블은 아니라 같은 테이블이다. 

구현할 쿼리의 내용은 이러하다. 먼저 모든 자료를 가져오는데, (1)임시로 "고유 행 번호"를 붙여준다. 현재 게시판 테이블에서는 게시물의 고유 번호인 board_id 컬럼이 존재한다. 그러나 중간에 글이 삭제되면 순서가 맞지 않다. 만약에 board_id 가 글이 삭제되더라도 순서대로 1,2,3,4, ..,100,101,102 처럼 중간에 빠짐없이 차례대로 번호가 매겨진다면 WHERE 절로만 써서 쉽게 특정 글의 구간만 뽑아올 수 있다. (1페이지는 board_id 값이1~10인 것만, 2페이지는 11에서 20인 것만 가져오면 되기 때문이다)

그런 후 이렇게 (2)임시로 붙은 고유 행 번호를 WHERE 을 이용해서 뽑아오는 방법이다.

일단 무조건 따라해보자.


SELECT * FROM
(
SELECT ROW_NUMBER() OVER(ORDER BY board_id DESC) AS row_num, * FROM board tmp
)

AS BOARD_NUMBERED
WHERE row_num > 0 AND row_num < 11


빨간색이 게시판 테이블 명이다. 그리고 파란색이 차례로 번호가 매겨질 컬럼이다. 녹색은 쿼리 문법상 임시로 사용할 이름을 지정하는 것이므로 무시하자. 괄호안의 진하게 된 SELECT 문이 실행된 후에 그걸 다시 가져온 후 WHERE 절을 적용시키는 형식이다.






이 결과는 앞서 말한 두가지 구현방법 중 1번에 해당되는 쿼리를 실행하는 것이다. 실제 게시판의 1페이지에 해당하는 글을 뽑은 결과다. 보면, "row_num"이라는 것이 서브쿼리로 인해 임시로 붙었는데, 이 컬럼의 번호를 이용해서 특정 구간을 뽑아오는 것이다. row_num 은 현재 상수로 넣고 있지만, asp.net 에서 변수로 자동으로 페이지에 맞게 조작될 것이다. 테스트는 무조건 상수로 하는 것이 편리하다.





위의 그림도 row_num 조건을 바꾸어 2페이지를 예제로 출력한 것이다.
이렇게 실제 DB에 테스트를 해봤고, 원하는 결과를 얻는 방법을 알았으니 우리 게시판에 적용해보도록 하자.


여기까지 자르고 다음에 계속하도록 하겠다. ^_^

반응형
Comments