5.4.1 DBM ForSearch 예광탄

먼저 Windows Form 응용을 생성하세요. 프로젝트 명은 DBM ForSearch 예광탄으로 할게요.

DBM ForSearch 예광탄의 메인 폼의 자식 컨트롤을 배치합시다.

형태소 이름을 입력받아 포함하고 있는 웹 페이지 주소와 참조 개수를 얻어오는 것을 테스트하기 위한 컨트롤들을 배치합니다.

특정 웹 페이지 주소에 포함하고 있는 전체 형태소 개수를 얻어오는 것을 테스트할 수 있는 컨트롤들을 배치합니다.

수집한 웹 페이지 개수를 얻어오는 것을 테스트하기 위한 컨트롤들을 배치합니다.

웹 페이지 주소를 입력하면 수집한 웹 페이지 정보를 얻어오는 것을 테스트할 수 있는 컨트롤들을 배치합니다.

DBM ForSearch 예광탄 메인 폼의 화면 배치
[그림 5.8] DBM ForSearch 예광탄 메인 폼의 화면 배치
번호컨트롤 이름컨트롤 유형설명
1gbox_getinvGroupBox그룹 박스
2lb_mn_infoLable정보 표시
3tbox_mnameTextBox형태소 입력 창
4btn_get_moButton형태소 정보 얻어오기
5 lview_moListView형태소 정보 목록
6ch_addColumnHeader컬럼 헤더(웹 페이지 주소)
7ch_rcntColumnHeader컬럼 헤더(참조 개수)
8gbox_tcntGroupBox그룹 박스
9 lb_pa_infoLabel정보 표시
10tbox_page_addrTextBox웹 페이지 주소 입력 창
11lb_tcnt_infoLabel정보 표시
12lb_tcntLabel전체 형태소 개수
13btn_get_tcntButton전체 형태소 개수 얻어오기
14gbox_cp_cntGroupBox그룹 박스
15lb_cpcnt_infoLabel정보 표시
16lb_cp_cntLabel수집한 웹 페이지 개수
17btn_get_pcntButton수집한 페이지 개수 얻어오기
18gbox_getcpageGroupBox그룹 박스
19lb_cpaddr_infoLabel정보 표시
20tbox_cp_addrTextBox웹 페이지 입력 창
21btn_get_cpButton수집한 웹 페이지 정보 얻어오기
22lb_cp_infoLabel정보 표시
23lb_cp_addrLabel웹 사이트 주소
24lb_title_infoLabel정보 표시
25lb_titleLabel타이틀
26lb_depth_infoLabel정보 표시
27lb_depthLabel상대적 깊이
28lb_date_infoLabel정보 표시
29lb_dateLabel수집한 일시
30lb_content_infoLabel정보 표시
31tbox_contentTextBox컨텐츠 (읽기 전용, Multiline)

[표 5.4] DBM ForSearch 예광탄 메인 폼의 자식 컨트롤

앞에서 작성한 시퀀스 다이어그램에서는 랭커에서 수집한 웹 페이지 정보를 얻어와 달라는 요청의 반환 형식이 string으로 정하였습니다. 그런데 수집한 웹 페이지 개체를 반환하면 보다 효과적일 것입니다. 이에 다음과 같이 시퀀스 다이어그램을 수정할게요. 이처럼 개발 공정 중에 앞쪽에서 작업한 내용의 오류를 발견하거나 수정할 필요가 생기면 관련 개발자끼리 다시 미팅을 하고 이를 반영한 결과 문서를 작성하세요.

수정한 Search 시퀀스 다이어그램
[그림 5.9] 수정한 Search 시퀀스 다이어그램

 먼저 형태소 이름을 입력하였을 때 이를 포함하고 있는 웹 페이지 주소와 참조 개수를 얻어오는 기능을 작성합시다.

btn_get_mo 버튼 컨트롤의 클릭 이벤트 핸들러를 추가합니다. 그리고 이벤트 핸들러에서는 형태소를 포함하는 웹 페이지 목록을 보여주는 lview_mo 리스트 뷰의 항목을 클리어합니다.

lview_mo.Items.Clear();

그리고 tbox_mname 텍스트 상자 컨트롤에 입력한 형태소 이름을 포함하는 정보를 얻어옵니다. 이 부분을 담당하는 것이 DBM ForSearch 라이브러리가 할 일입니다. 이 부분을 담당할 정적 클래스 EHDbmForSearch 클래스를 추가하고 형태소 이름을 입력 인자로 받고 형태소를 포함한 페이지 목록을 반환하는 GetInvertedFile 정적 메서드를 추가합니다. 그리고 btn_get_mo 버튼 클릭 이벤트 핸들러에서는 이를 호출하여 사용합니다.

List<InvertedElem> list = null;
list = EHDbmForSearch.GetInvertedFile(tbox_mname.Text);

이제 얻어온 목록을 lview_mo 리스트 뷰 컨트롤의 항목에 추가합니다.

ListViewItem lvi = null;
string[] elems = null;

foreach (InvertedElem elem in list)
{
    elems = new string[2] { elem.Url, elem.RefCount.ToString() };
    lvi = new ListViewItem(elems);
    lview_mo.Items.Add(lvi);
}

EHDbmForSearch 정적 클래스의 GetInvertedFile 메서드를 구현합시다.

public static List<InvertedElem> GetInvertedFile(string mname)

먼저 조사 결과를 담을 컬렉션을 생성합니다.

List<InvertedElem> list = new List<InvertedElem>();

그리고 역 파일 테이블에서 입력 인자로 받은 형태소의 인덱스 값을 얻어옵니다. 이 부분은 GetMIndex 정적 메서드를 만들어서 사용할게요.

int mindex = GetMIndex(mname);

만약 얻어온 인덱스가 -1이면 빈 리스트를 반환합니다.

if (mindex == -1)
{
    return list;
}

그리고 얻어온 인덱스를 이용하여 SELECT 구문을 작성합니다.

string query = string.Format("SELECT * FROM MTB_{0}", mindex);

이제 작성한 쿼리문을 인자로 SqlCommand 개체를 생성합니다. DBM ForAll 라이브러리를 만들었던 것처럼 같은 방법으로 SqlCommand 개체를 생성하는 MakeSPCommand 정적 메서드를 만들어서 사용합시다.

SqlCommand scom = MakeSPCommand(query,CommandType.Text);

연결을 열고 명령을 실행합니다. 실행할 명령이 SELECT 쿼리문이므로 ExecurteReader 메서드를 호출합니다.

scom.Connection.Open();
SqlDataReader sdr = scom.ExecuteReader();

실행한 결과를 결과를 담을 list 컬렉션에 추가합니다.

InvertedElem elem = null;
while (sdr.Read())
{
    elem = new InvertedElem();
    elem.Url = sdr["Url"] as string;
    elem.RefCount = (int)sdr["Refcnt"];
    list.Add(elem);
}


작업을 완료하였으면 SqlDataReader 개체와 연결을 닫습니다.

sdr.Close();
scom.Connection.Close();

마지막으로 결과를 반환합니다.

return list;

형태소 이름을 입력 인자로 받고 역파일 테이블의 index 값을 반환하는 GetMIndex 메서드를 작성합시다.

public static int GetMIndex(string mname)

GetMIndex 이름의 저장 프로시저를 만들었으니 이를 이용합니다. GetMIndex 저장 프로시저를 실행할 수 있는 SqlCommand 개체를 생성합니다.

SqlCommand scom = MakeSPCommand("GetMIndex",
                                 CommandType.StoredProcedure);

형태소 이름의 파라미터를 생성하고 인덱스 값을 전달받을 파라미터를 생성합니다. 그리고 생성한 파라미터 개체를 SqlCommand 개체의 Parameters 컬렉션에 추가합니다. 물론 인덱스 값을 전달받을 파라미터의 방향은 Output으로 설정합니다.

SqlParameter sp_morpheme = new SqlParameter("@Morpheme", mname);
SqlParameter sp_mindex = new SqlParameter("@MIndex", SqlDbType.Int);
sp_mindex.Direction = ParameterDirection.Output;

scom.Parameters.Add(sp_morpheme);
scom.Parameters.Add(sp_mindex);

연결을 개방하고 명령을 실행합니다. 그리고 연결을 닫습니다.

scom.Connection.Open();
scom.ExecuteNonQuery();
scom.Connection.Close();

마지막으로 인덱스 값을 받기 위한 파라미터의 값을 반환합니다.

return (int)sp_mindex.Value;

명령문과 명령 타입을 인자로 받아 SqlCommand 개체를 생성하는 MakeSPCommand 정적 메서드를 작성합시다.

static SqlCommand MakeSPCommand(string cmdtext, CommandType cmdtype)

먼저 생성에 필요한 연결 문자열은 EHDbmForSearch 클래스의 멤버 상수로 정의합니다.

static string constr =  @"Data Source=[DBMS 인스턴스 명];Initial Catalog=[DB 명];Persist Security Info=True;User ID=[계정];Password=[비밀번호];Pooling=False;";

MakeSPCommand 메서드에서는 먼저 SqlConnection 개체를 생성합니다.

SqlConnection scon = new SqlConnection(constr);

그리고 입력 인자로 받은 명령문과 생성한 SqlConnection 개체를 인자로 SqlCommand 개체를 생성합니다.

SqlCommand scom = new SqlCommand(cmdtext, scon);

입력 인자로 받은 명령 타입을 설정합니다.

scom.CommandType = cmdtype;

마지막으로 생성한 SqlCommand 개체를 반환합니다.

return scom;

이제 역파일 테이블에 있는 항목 이름을 가지고 정상적으로 동작하는지 테스트를 해 보세요.

이번에는 웹 페이지 주소를 입력받아 수집한 웹 페이지에 있는 전체 형태소의 개수를 얻어오는 부분을 작성합시다.

먼저 btn_get_tcnt 버튼 컨트롤의 클릭 이벤트 핸들러를 추가합니다.

private void btn_get_tcnt_Click(object sender, EventArgs e)

최종 사용자가 tbox_page_addr 컨트롤에 입력한 문자열을 인자로 전체 형태소 개수를 요청합니다. 이 부분은 EHDbmForSearch 클래스의 정적 메서드 GetTotalCountInUrl로 작성합시다. 그리고 얻어온 개수로 lb_tcnt 컨트롤의 Text 속성을 설정합니다.

int total_count = 0;
total_count = EHDbmForSearch.GetTotalCountInUrl(tbox_page_addr.Text);
lb_tcnt.Text = total_count.ToString();

EHDbmForSearch 클래스의 정적 메서드 GetTotalCountInUrl을 작성합시다.

public static int GetTotalCountInUrl(string url)

앞에서 만든 GetMorphCountInUrl 저장 프로시저를 실행할 수 있는 SqlCommand 개체를 생성합니다.

SqlCommand scom = MakeSPCommand("GetMorphCountInUrl",
                                  CommandType.StoredProcedure);

입력 인자로 받은 웹 페이지 주소로 파라미터 개체를 생성합니다. 그리고 개수를 얻어오기 위해 Output 유형의 파라미터 개체도 생성하여 Parmeters 컬렉션 속성에 추가합니다.

SqlParameter sp_url = new SqlParameter("@Url", url);
SqlParameter sp_cnt = new SqlParameter("@Cnt", SqlDbType.Int);
sp_cnt.Direction = ParameterDirection.Output;
scom.Parameters.Add(sp_url);
scom.Parameters.Add(sp_cnt);

연결을 개방하고 명령을 수행한 후에 결과를 반환합니다.

scom.Connection.Open();
scom.ExecuteNonQuery();
scom.Connection.Close();
return (int)sp_cnt.Value;

이번에는 수집한 웹 페이지 개수를 확인하는 기능을 구현합시다.

private void btn_get_pcnt_Click(object sender, EventArgs e)

EHDbmForSearch 클래스에 정적 메서드로 GetPostedUrlCount를 만들어 사용합시다. 여기에서는 이 메서드를 호출한 결과를 lb_cp_cnt 컨트롤의 Text 속성에 지정합니다.

int total_count = 0;
total_count = EHDbmForSearch.GetPostedUrlCount();
lb_cp_cnt.Text = total_count.ToString();

EHDbmForSearch 클래스의 정적 메서드 GetPostedUrlCount를 작성합시다.

public static int GetPostedUrlCount()

먼저 GetPostedUrlCount 저장 프로시저를 실행할 수 있는 SqlCommand 개체를 생성합니다.

SqlCommand scom = MakeSPCommand("GetPostedUrlCount",
                                CommandType.StoredProcedure);

수집한 웹 페이지 개수를 얻어오기 위해 Output 유형의 파라미터 개체를 생성합니다. 그리고 SqlCommand 개체의 Paramerters 컬렉션 속성에 추가합니다.

SqlParameter sp_cnt = new SqlParameter("@Cnt", SqlDbType.Int);
sp_cnt.Direction = ParameterDirection.Output;
scom.Parameters.Add(sp_cnt);

연결을 개방하여 명령을 실행한 후에 연결을 닫고 결과를 반환합니다.

scom.Connection.Open();
scom.ExecuteNonQuery();
scom.Connection.Close();
return (int)sp_cnt.Value;

이번에는 수집한 웹 페이지 정보를 얻어오는 부분을 작성합시다.

먼저 btn_get_cp 버튼의 클릭 이벤트 핸들러를 추가합니다.

private void btn_get_cp_Click(object sender, EventArgs e)

tbox_cp_addr 컨트롤에 입력한 문자열을 입력 인자로 수집한 웹 페이지 정보를 얻어옵니다. 이 부분은 EHDbmForSearch 클래스에 정적 메서드 GetPostedUrl 메서드를 만들어 사용합시다.

PostedUrl purl = null;
purl = EHDbmForSearch.GetPostedUrl(tbox_cp_addr.Text);

얻어온 PostedUrl 개체의 속성을 매핑할 각 Label 컨트롤과 TextBox 컨트롤의 Text 속성을 설정합니다.

lb_cp_addr.Text = purl.OriginUrl;
lb_title.Text = purl.Title;
lb_depth.Text = purl.Depth.ToString();
lb_date.Text = purl.PostedTime.ToString();
tbox_content.Text = purl.Content;

EHDbmForSearch 클래스의 GetPostedUrl 메서드를 구현합시다.

public static PostedUrl GetPostedUrl(string url)

먼저 GetPostedUrl 저장 프로시저를 실행할 수 있는 SqlCommand 개체를 생성합니다.

SqlCommand scom = MakeSPCommand("GetPostedUrl",
                                 CommandType.StoredProcedure);

입력 인자로 받은 url로 파라미터 개체를 만듭니다.

SqlParameter sp_url = new SqlParameter("@Url", url);

그리고 수집한 웹 페이지의 각 정보를 얻어오기 위해 Output 유형의 파라미터 개체를 생성하고 SqlCommand 개체의 Parameters 컬렉션 속성에 추가합니다.

SqlParameter sp_ourl = new SqlParameter("@OriginUrl", SqlDbType.VarChar, 200);
sp_ourl.Direction = ParameterDirection.Output;
SqlParameter sp_depth = new SqlParameter("@Depth", SqlDbType.Int);
sp_depth.Direction = ParameterDirection.Output;
SqlParameter sp_ptime = new SqlParameter("@PostedTime", SqlDbType.DateTime);
sp_ptime.Direction = ParameterDirection.Output;
SqlParameter sp_content = new SqlParameter("@PostedContent",
                                             SqlDbType.VarChar, 8000);
sp_content.Direction = ParameterDirection.Output;
SqlParameter sp_title = new SqlParameter("@Title", SqlDbType.VarChar, 200);
sp_title.Direction = ParameterDirection.Output;
scom.Parameters.Add(sp_url);
scom.Parameters.Add(sp_ourl);
scom.Parameters.Add(sp_depth);
scom.Parameters.Add(sp_ptime);
scom.Parameters.Add(sp_content);
scom.Parameters.Add(sp_title);

연결을 개방하여 명령을 수행하고 연결을 닫습니다.

scom.Connection.Open();
scom.ExecuteNonQuery();
scom.Connection.Close();

Output 유형의 파라미터 개체의 값으로 PostedUrl 개체를 생성하여 반환합니다.

string ourl = sp_ourl.Value as string;
if (ourl == null)
{
    return new PostedUrl();
}

int depth = (int)sp_depth.Value;
DateTime ptime = (DateTime) sp_ptime.Value;
string content = sp_content.Value as string;
string title = sp_title.Value as string;
return new PostedUrl(url, ourl, title, content, depth, ptime);

이상으로 DBM ForSearch 예광탄 구현을 마쳤습니다. 마지막으로 모든 테스트를 다시 해 보세요. MainForm의 소스 파일의 코드는 다음과 같습니다. EHDbmForSearch.cs 파일의 코드는 DBM ForSearch 라이브러리 만들기와 같으므로 여기에서는 코드 내용을 생략할게요.

▷ MainForm.cs

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using WSE_Core;


namespace DBM_For_Search_예광탄
{

    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }

        private void btn_get_mo_Click(object sender, EventArgs e)
        {
            lview_mo.Items.Clear();
            List<InvertedElem> list = null;
            list = EHDbmForSearch.GetInvertedFile(tbox_mname.Text);


            ListViewItem lvi = null;
            string[] elems = null;

            foreach (InvertedElem elem in list)
            {
                elems = new string[2] { elem.Url, elem.RefCount.ToString() };
                lvi = new ListViewItem(elems);
                lview_mo.Items.Add(lvi);
            }
        }

        private void btn_get_tcnt_Click(object sender, EventArgs e)
        {
            int total_count = 0;
            total_count = EHDbmForSearch.GetTotalCountInUrl(tbox_page_addr.Text);
            lb_tcnt.Text = total_count.ToString();
        }

        private void btn_get_pcnt_Click(object sender, EventArgs e)
        {
            int total_count = 0;
            total_count = EHDbmForSearch.GetPostedUrlCount();
            lb_cp_cnt.Text = total_count.ToString();
        }

        private void btn_get_cp_Click(object sender, EventArgs e)
        {
            PostedUrl purl = null;
            purl = EHDbmForSearch.GetPostedUrl(tbox_cp_addr.Text);
            lb_cp_addr.Text = purl.OriginUrl;
            lb_title.Text = purl.Title;
            lb_depth.Text = purl.Depth.ToString();
            lb_date.Text = purl.PostedTime.ToString();
            tbox_content.Text = purl.Content;
        }
    }
}