Midnight Peach 2.0 샘플 애플리케이션, 웹 애플리케이션 (2/2)

Midnight Peach 2009. 3. 25. 23:43

[주의]
이 글은 MP의 현재 버전 (3.X)에 맞지 않습니다.

단순 참고용으로만 활용하십시오

Midnight Peach 2.0 샘플 애플리케이션, 웹 애플리케이션 (1/2)에 이어서 계속 진행하겠습니다.

데이터베이스 연결 문자열

web.config 파일을 열어 데이터베이스 연결 문자열을 설정합니다.

image

여기서 연결 문자열의 이름은 아무렇게나 지정해서는 안됩니다.

MP의 아래 화면에서 지정한 이름과 동일해야 하는데, 여기서는 Memopad로 지정되어 있습니다.

image

연결 문자열을 설정하였으면 DB에 제대로 연결이 되고 생성된 비지니스 레이어에 문제가 없는지 간단히 테스트를 해보도록 합시다.

Default.aspx 파일에 아래와 같은 코드를 입력하고 웹 애플리케이션 프로젝트를 실행하여 봅니다.

image

Memo 테이블의 로우 갯수를 출력하는 코드인데, 샘플 애플리케이션과 함께 배포된 DB에는 10개의 메모가 들어 있습니다.

따라서 10이 출력되면 정상입니다.

MemoList.aspx 페이지

메모들의 목록을 표시하는 페이지를 만듭시다.

프로젝트에 새 아이템을 추가하고 이름을 MemoList라고 합니다.

이 페이지가 하는 일은 다음과 같습니다.

  • 제목으로 검색된 메모 목록을 표시한다.
  • 메모를 클릭하면 해당 메모의 상세화면이 열린다.
  • 페이징을 지원한다.

여기서 약간의 통찰력을 발휘할 필요가 있습니다.

위 요구사항을 보고 필요한 비지니스 로직을 찾아내어, 프리젠테이션 코드와 비지니스 로직 코드를 분리하여야 합니다.

지금은 요구사항이 간단하니까 별다른 고민 없이 다음과 같은 비지니스 메서드가 필요함을 알게 될 것입니다.

image

페이지에서 GetBySubject 메서드가 필요함을 알게되었으니, MemoList 페이지의 작성은 잠시 멈추고 GetBySubject 메서드를 먼저 구현해 보도록 하겠습니다.

필드에서라면 비지니스 로직을 담당하는 개발자에게 의뢰를 하는 형태가 될 수도 있겠지요.

커스텀 비지니스 로직의 구현

여기서부터는 비지니스 로직 담당자의 입장에서 생각을 해봅시다.

GetBySubject란 메서드를 작성하도록 요청을 받았는데, 이 메서드가 하는 일은 ‘제목에 특정 검색어가 포함된 메모들의 목록을 반환하되 페이징을 지원한다’ 입니다.

작성을 해봅시다.

먼저 커스텀 비지니스 로직을 구현하기 위한 클래스를 만들어야 할 텐데, 메모 엔터티에 대한 비지니스 로직이니까 MemoBiz 라는 클래스가 필요할 것입니다.

그런데 사실 이 클래스는 이미 MP가 만들어 놓았습니다.

그렇다면 GetBySubject 메서드를 구현하기 위해 MP가 만들어 놓은 MemoBiz 클래스를 수정하면 될까요?

물론 안될 이유는 없지만, MP가 생성한 코드는 수동으로 고치지 않는 것이 좋습니다.

왜냐하면 나중에 다시 코드가 생성되면 수동으로 추가한 코드가 덮어씌여지기 때문입니다.

대신에 MemoBiz 클래스 파일을 하나 더 추가하고 이를 partial 클래스로 선언하는 것이 정석입니다.

사실 MP가 생성한 모든 클래스는 partial 클래스라서 이러한 방식으로 커스터마이징을 할 수 있습니다.

Memopad.Biz 클래스의 CustomBiz 폴더에 Memo 클래스를 추가합니다.

(물론 CustomBiz 폴더가 아닌 다른 곳에 추가하여도 상관없지만, MP가 추천하는 구조는 CustomBiz 폴더 입니다.)

그럼 프로젝트의 구성은 아래와 같겠네요.

image

추가된 MemoBiz의 코드를 아래와 같이 수정합니다.

image

partial 클래스로 선언하고 네임 스페이스를 MP가 생성한 MemoBiz와 동일하게 맞춰주었습니다.

이제 GetBySubject를 구현합니다.

image

길어 보이기는 하지만 실제 코드는 한 줄입니다.

MemoBiz의 부모인 EntityBiz에 정의된

image

를 다시 호출하고 있습니다.

사실 이 포스트에서 가장 중요한 부분이 여기 입니다.

‘제목에 특정 검색어가 포함된 메모들의 목록을 반환하되 페이징을 지원하는’ 비지니스 메서드를 만드는데, 단 한 줄의 코드만이 필요합니다.

SqlConnection 이나 SqlCommand 객체를 만들 필요도 없으며, DataSet이나 SqlDataReader 같은 클래스는 아예 등장하지도 않습니다.

참고로 [DataObjectMethod(DataObjectMethodType.Select)] 라는 특성은 ObjectDataSource에서 사용되는 특성인데, 이 메서드가 ObjectDataSource의 SelectMethod로 표시되어야 한다는 의미입니다.

이 포스트의 뒤에서 좀 더 자세한 설명을 하도록 하겠습니다.

ObjectDataSource의 구성

다시 MemoList.aspx 페이지로 돌아가서 작업을 계속하도록 하겠습니다.

도구상자에서 ObjectDataSource를 끌어와 페이지에 추가합니다.

추가된 ObjectDataSource의 이름을 odsMemo로 변경한 후 데이터 소스 구성 대화상자를 엽니다.

image

첫번째 단계는 비지니스 객체를 선택하는 것인데, 그림에서 보듯이 선택할 수 있는 객체가 세 가지 밖에 없습니다.

이는 Show only data components 체크박스가 켜져 있기 때문입니다. 즉 이 체크박스가 켜져 있으면 [DataObject] 속성이 붙은 클래스만 이 곳에 표시합니다.

클래스의 수가 수 백개만 넘어도 이 옵션을 켜지 않으면 해당 클래스를 찾기가 힘이 들 것입니다.

참고로 MP가 생성한 비지니스 객체에는 기본적으로 [DataObject] 특성이 붙어 있습니다.

MemoBiz를 선택하고 다음으로 넘어 갑니다.

image

이번에는 SELECT 메서드를 선택합니다.

비주얼 스튜디오는 리플렉션을 통해 SELECT 메서드로 사용할 수 있는 MemoBiz 클래스의 메서드의 목록을 추정하여 보여줍니다.

만일 비주얼 스튜디오의 추정을 통하지 않고 명식적으로 지정하고 싶다면 메서드에 [DataObjectMethod] 특성을 붙여주면 됩니다.

또한 [DataObjectMethod]의 매개변수를 지정함에 따라, 위 그림과 같이 여러 개의 후보 메서드 중에서 기본값으로 지정할 수도 있습니다.

여기서는 위에서 만든 GetBySubject를 선택하고 다음으로 넘어갑니다.

(UPDATE, INSERT,DELETE 메서드에는 이미 기본값이 지정되어 있긴 하지만, 사용하지 않을 메서드이므로 나중에 수동으로 삭제할 것입니다.)

image

마지막으로 GetBySubject의 매개변수를 설정하야 하는데, GetBySubject의 경우에는 특별한 설정이 필요없습니다.

text 매개변수의 경우에는 사실 TextBox 컨트롤에서 값을 읽어오는 것이 정석이겠으나, 여기서는 데모의 목적으로 코드에서 직접 설정하는 방법을 택할 것입니다.

또한 startRowIndex와 maximumRows 이 두 개의 매개변수는 페이징을 위한 것으로서, 나중에 추가할 DataPager 컨트롤이 제어할 값으로 개발자가 직접 제어할 필요가 없습니다.

따라서 그냥 마침을 눌러 ObjectDataSource의 구성을 끝마칩니다.

또한 ObjectDataSource의 InsertMethod, UpdateMethod, DeleteMethod 속성은 사용되지 않으니까 삭제합니다.

ObjectDataSource에 이벤트 추가

이제 ObjectDataSource의 매개변수인 text의 값을 설정하는 코드를 작성할 차례입니다.

text의 값을 설정하기에 가장 적절한 곳은 ObjectDataSource의 OnSelecting 이벤트일 것입니다.

ObjectDataSource의 OnSelecting 이벤트 핸들러를 추가합니다.

(그 전에 txtSearch 라는 TextBox를 페이지에 추가해 두시기 바랍니다.)

image

ObjectDataSource에 페이징 추가

이번에는 ObjectDataSource에 페이징을 추가해 봅시다.

ObjectDataSource에 페이징을 추가하기 위해서는 EnablePaging 속성을 true로 설정하는 것과 더불어 아래와 같이 세 개의 속성을 추가로 지정하여야 합니다.

  • SelectCountMethod
  • StartRowIndexParameterName
  • MaximumRowsParameterName

SelectCountMethod는 전체 로우의 갯수를 반환하는 메서드의 이름입니다.

현재 이런 일을 하는 메서드는 없으니까 새로 만들어야 합니다.

GetBySubject가 정의된 MemoBiz에 GetBySubjectCount 라는 메서드를 만들어 봅시다.

먼저 GetBySubjectCount의 시그니처에 대해서 살펴보면, 반환값은 당연히 int 형이면 되지만, 매개변수의 경우에는 특정한 룰이 있습니다.

ObjectDataSource의 SelectMethod에 지정된 메서드의 매개변수를 모두 포함하여야 하고, 추가로 마지막에 두 개의 int 형을 가져야 한다는 것인데, 이 규칙을 ObjectDataSource에 대입하면 GetBySubjectCount의 시그니처는 다음과 같게 됩니다.

public int GetBySubjectCount(string text, int startRowIndex, int maximumRows)

시그니처만 알아내면 실제 구현은 무척 간단합니다.

GetBySubject 메서드가 정의된 MemoBiz 클래스에 GetBySubjectCount 메서드를 정의합니다.

image

마찬가지로 단 한 줄의 코드로 구현이 가능합니다.

GetBySubjectCount 메서드를 구현했으니 ObjectDataSource의 SelectCountMethod 속성의 값을 GetBySubjectCount 라고 설정합니다.

StartRowIndexParameterName와 MaximumRowsParameterName 속성의 값은 각각 페이징 메서드에서 가져올 로우의 인덱스와 갯수를 지정하는 매개변수의 이름을 나타내는데, 기본값은 각각 startRowIndex와 maximumRows로 지정이 되어 있습니다.

위에서 구현한 GetBySubjectCount의 매개변수의 이름이 이 기본값과 이미 동일하게 설정되어 있으므로 이 두 속성은 지정하지 않아도 괜찮습니다.

여기까지 작업된 ObjectDataSource의 코드는 다음과 같습니다.

image

ListView 추가

메모의 목록을 표시하기 위해서 ListView 컨트롤을 사용하겠습니다.

MemoList.aspx 페이지에 ListView 컨트롤을 추가하고 아래와 같이 구성합니다.

image

ListView에 대한 설명은 이 포스트의 범위를 넘어 자세한 설명을 할 수는 없지만, ListView는 ASP.NET 3.5에서 가장 강력한 데이터 바인딩 컨트롤입니다.

적어도 MSDN에 있는 ListView의 설명 만은 꼭 익혀두시길 권장합니다.

DataPager의 추가

페이징을 사용하기 위해 DataPager 컨트롤을 추가합니다.

image

페이징을 시연하기 위해 PageSize를 3으로 지정하였습니다.

MemoList.aspx의 완성

여기까지 해서 MemoList.aspx 페이지가 완성되었습니다.

프로젝트를 실행하면 아래와 같은 화면을 볼 수 있습니다.

image

링크를 눌러 보면 검색과 페이징이 동작함을 확인할 수 있습니다.

MemoDetail.aspx의 ObjectDataSource

지금부터는 Midnight Peach 2.0 샘플 애플리케이션, 웹 애플리케이션의 두 번째 페이지인 MemoDetail.aspx를 만들어보도록 하겠습니다.

프로젝트에 MemoDetail.aspx를 추가하고 ObjectDataSource를 추가합니다.

ObjectDataSource를 추가하는 이유는 메모 엔터티의 상세 정보를 보여주기 위해 DetailsView 컨트롤의 사용할 것인데, 이 DetailsView의 데이터 소스로 ObjectDataSource를 사용하기 때문입니다.

먼저 ObjectDataSource를 구성한 후 DetailsView 컨트롤을 추가하도록 하겠습니다.

ObjectDataSource 구성 대화상자나 이용하거나 직접 타이핑하거나 하여 아래와 같이 ObjectDataSource를 구성합니다.

image

이번 경우에는 ObjectDataSource에 지정된 모든 메서드가 MP에서 이미 생성해 둔 것이므로 추가로 비지니스 메서드를 만들 필요가 없습니다.

바로 DetailsView 컨트롤로 넘어 갑시다.

DetailsView 컨트롤

MemoDetail.aspx 페이지에 DetailsView 컨트롤을 추가하고 아래와 같이 설정합니다.

image

대체로 이해하기 무난한 코드인데, 두 개의 이벤트 핸들러에 관해서는 좀 더 설명을 드려야 할 것 같습니다.

먼저 OnItemUpdating 이벤트의 이벤트 핸들러를 다음과 같이 구현합니다.

image

OnItemUpdating 이벤트는 물론 Memo 객체가 변경되기 전에 발생하는데, DetailsViewUpdateEventArgs의 NewValues에는 새로 업데이트 할 값이 저장되어 있습니다.

이때 Memo 객체의 경우에는 두 가지 문제가 발생합니다.

먼저 WrittenAt 속성은 DateTime 형인데, 새로 업데이트할 값으로 전달된 값은 string 형입니다. 따라서 이를 DateTime 형으로 형변환을 하여야 합니다.

또 Body 속성은 null을 허용하지 않으므로, 만일 null이 전달된다면 빈 문자열로 변환하여야 합니다.

마찬가지로 OnItemInserting 이벤트는 Memo 객체가 추가되기 전에 발생합니다.

역시 DetailsViewUpdateEventArgs의 Values에는 추가될 Memo 객체의 값이 넘어옵니다.

여기서 추가될 Memo 객체는 Memo의 기본 생성자로 생성되는데, 그렇다면 그 멤버 필드는 기본값으로 초기화가 됩니다.

즉, Memo 객체라면 WrittenAt은 DateTime형이니까 1년 1월 1일 자정이라는 값을 가지고 string 형인 Subject 등은 null 값을 가지게 됩니다.

하지만 Memo 객체는 DB 스키마와 DBML 파일의 정의에 의해 이러한 값을 지정할 수 없습니다.

따라서 아래와 같이 새로 생성된 Memo 객체의 값을 적당한 값으로 초기화하는 코드가 필요합니다.

 

이렇게 해서 메모패드 웹 애플리케이션을 만들어보았습니다.

: