공룡호가 사는 세상 이야기

SetTimer()

프로그래밍2007. 5. 14. 16:15

일정 시간마다 특정한 일을 수행할 때 대개 SetTimer() 함수를 사용한다. (일정 시간이 경과할 때까지 Sleep하는 건 유치할 것같다.. ㅋㅋ) 책을 좀 찾아보니 SetTimer()는 SDK에서 제공되는 API 함수가 원조격이다. 일단, MSDN을 열어봤다.

이미지를 클릭하시면 원본크기로 보실수 있습니다.

보다시피 VC++에서 제공되는 SetTimer() 함수는 총 3개가 존재한다. 그런데, 맨 위의 CWindows::SetTimer는 ATL에서 사용되기 때문에 통상으로 사용되는 SetTimer() 함수는 SDK의 API 함수와 CWnd::SetTimer 멤버함수가 되겠다. (따라서, SetTimer()는 SDK버전과 MFC버전 두개만 정리하면 된다.)

먼저, SDK의 SetTimer() 함수의 원형을 살펴본다.

UINT SetTimer(
  HWND hWnd,    // handle of window for timer messages
  UINT nIDEvent,          // timer identifier
  UINT uElapse,           // time-out value
  TIMERPROC lpTimerFunc   // address of timer procedure
);

첫번째 인자는 SetTimer()를 호출하는 윈도우의 핸들이고, 두번째는 타이머의 ID이다. 애플리케이션은 여러 개의 독립된 타이머를 이용할 수 있다. 따라서, 각각의 타이머를 구별하기 위해 ID를 부여한다. 세번째 인자는 타이머의 주기(단위는 밀리초)이며, 마지막 인자는 타이머가 동작할 때 시스템이 호출하는 콜백함수(이를 타이머 프로시져라 한다)의 함수포인터(여기서, 타이머 프로시져의 함수이름은 자기 마음대로 결정해도 된다)이다. 즉, 일정 시간이 발생하면 해당 콜백함수를 실행하게 되는데, 거기에 우리가 필요로 하는 루틴을 넣어주면 된다.

그러면, SetTimer()에 등록하는 콜백함수(타이머 프로시져)는 어떻게 정의해야 할까? (타이머 프로시져는 콜백함수이기 때문에 함수이름은 SetTimer에 등록한 함수이름과 동일해야 하며, 시스템이 정한 호출 형식대로 정의를 해두어야 한다.)

VOID CALLBACK TimerProc(
  HWND hwnd,     // handle of window for timer messages
  UINT uMsg,     // WM_TIMER message
  UINT idEvent,  // timer identifier
  DWORD dwTime   // current system time
);

호출되는 타이머 프로시져 함수는 위와 같은 형식이다. 첫번째 인자인 hWnd는 타이머를 호출한 윈도우의 핸들이며(SetTimer()함수의 첫번째 인자를 생각할 것), 두번째는 시스템이 던져주는 타이머 메시지(항상 WM_TIMER이다), 세번째는 호출한 SetTimer함수의 Id값, 네번째는 시스템 시간이다. 위의 인자들은 TimerProc가 콜백함수이기 때문에 시스템이 알아서 던져준다. 따라서, 우리는 타이머 Id를 조사(if 내지는 switch)하여 해당 타이머에 대한 기능을 구현하면 될 것이다.

그런데, MSDN을 자세히 읽어보면 SetTimer()의 인자 중에서 콜백함수 대신 NULL을 대입하는 경우가 있다. 이 경우, 시스템은 일정 시간에 이르면 콜백함수를 호출하는 것이 아니라 애플리케이션에 WM_TIMER 메시지를 전달한다. 따라서, Win32 프로그램의 경우라면 WndProc에서 WM_TIMER 발생에 따른 처리루틴을 구현하면 된다.

정리하면, SDK의 SetTimer함수는 콜백함수에 의한 처리방식과 WM_TIMER 메시지에 대한 처리방식으로 그 사용방법이 나뉘어진다. 이는 MFC에서도 마찬가지이다. 이제 MFC의 SetTimer() 함수를 살펴보자.

UINT

CWnd::SetTimer(

            UINT nIDEvent,

            UINT nElapse,

void (CALLBACK EXPORT* lpfnTimer)(HWND, UINT,

                                        UINT, DWORD) );

MFC의 SetTimer() 함수는 CWnd클래스의 멤버함수로 구현되어 있기 때문에 SDI, MDI, 다이얼로그 애플리케이션에서 모두 사용이 가능하다. SDK와의 차이점은 SDK의 첫번째 인자인 hWnd를 가지고 있지 않다는 점. 당연히 CWnd의 멤버함수이므로 hWnd를 알고 있기 때문일 거다. 그 점을 제외하면 SDK버전과 별반 다를게 없다.

다만, MFC에서는 타이머 프로시져를 사용하지 않을 경우 WM_TIMER의 처리를 위해서 OnTimer() 이벤트 핸들러를 추가해서 사용하면 될 것이고, 타이머 프로시져를 사용할 경우 타이머 프로시져는 전역함수이기 때문에 주윈도우(또는 주다이얼로그)의 멤버에 접근하기 위해서 다음과 같은 방법을 사용할 수 있다.

가령, 다이얼로그 기반으로 TestProg라는 애플리케이션을 작성하였을 경우

void CALLBACK TimerProc(HWND hWnd, UINT nIDEvent, UINT nElapse, DWORD dwTime)

{

    ...

    CWnd *pWnd = AfxGetMainWnd();

    CTestProgDlg *dlg = (CTestProgDlg *)pWnd;

    dlg->멤버함수();

}


이런 식으로 하면 된다.

'프로그래밍' 카테고리의 다른 글

Resource Leaking  (6) 2007.08.13
WebPage Redirect (MFC / API)  (0) 2007.05.16
ROBOCODE Master의 비밀!!!  (0) 2007.04.23
우연히 보게 된 코드  (2) 2007.04.19
공사  (0) 2007.04.17

Factored wall avoidance !! ㅋㅋㅋ

모두들 따라해 보자는 뜻에서 간단한 튜토리얼을...
번역 안된 영문주소 :
http://www-128.ibm.com/developerworks/library/j-fwa/

코너에 갇히거나 원하는 이동 방향에서 너무 많이 벗어나지 않으면서, 로봇과 벽 사이의 간격을 유지하는 알고리즘은 간단히 만들 수 없는 것 같다. 한 가지 간단한 솔루션으로, Factored wall avoidance가 있다.

With a few additions to the bot we built in "상대편의 움직임 추적하기"에서 구현했던 로봇에 몇 가지를 더 추가하여, 기존의 움직임 알고리즘 또는 문제가 많은 움직임 알고리즘에 Factored Wall Avoidance를 추가할 수 있다. Factored Wall Avoidance는 자신의 로봇과 벽의 근접성에 따라서 안전한 방향 설정(heading)으로 원하는 방향을 팩토링 함으로써 최상의 방향을 찾는 것이다

일반 수학적 계산에 헬퍼 메소드 추가하기

우선 자주 사용되는 수학적 알고리즘에 헬퍼 메소드를 로봇에 추가한다.

calculateBearingToXYRadians() 메소드는 java.lang.Math 메소드 atan2()를 사용하여 sourceX,sourceY에서 targetX,targetY까지 절대 위치(absolute bearing)를 계산한 다음, 이 값을 sourceHeading에 관련된 위치로 변환한다.

그리고 normalizeAbsoluteAngleRadians() 메소드와 normalizeRelativeAngleRadians() 메소드도 필요하다.

수학 헬퍼 메소드

private static final double DOUBLE_PI = (Math.PI * 2);
private static final double HALF_PI = (Math.PI / 2);

public double calculateBearingToXYRadians(double sourceX, double sourceY,
    double sourceHeading, double targetX, double targetY) {
        return normalizeRelativeAngleRadians(
           Math.atan2((targetX - sourceX), (targetY - sourceY)) -
               sourceHeading);
    }

public double normalizeAbsoluteAngleRadians(double angle) {
   if (angle < 0) {
        return (DOUBLE_PI + (angle % DOUBLE_PI));
    } else {
        return (angle % DOUBLE_PI);
    }
}

public static double normalizeRelativeAngleRadians(double angle) {
    double trimmedAngle = (angle % DOUBLE_PI);
    if (trimmedAngle > Math.PI) {
        return -(Math.PI - (trimmedAngle % Math.PI));
    } else if (trimmedAngle < -Math.PI) {
        return (Math.PI + (trimmedAngle % Math.PI));
    } else {
        return trimmedAngle;
    }
}

AdvancedRobot을 back-as-front 기능으로 확장하기

다음으로, 로봇을 반대로 이동시키기 위한 back-as-front 기능을 제공하기 위해 AdvancedRobot 클래스 기능을 몇 가지 헬퍼 메소드로 확장할 필요가 있다.

- getRelativeHeading() 메소드는 로봇의 현재 위치와 관련하여 정확한 방향을 계산한다.

- reverseDirection() 메소드는 매우 단순하다. direction 인스턴스 변수를 토글링(toggle) 하고 로봇의 방향을 바꾼다. 감속할 때에는 시간이 걸리기 때문에 로봇은 속도에 따라서, 방향을 바꾸기 전에 최대 네 개의 프레임까지 같은 방향으로 움직일 수도 있다.

- setAhead()와 setBack() 메소드는 AdvancedRobot 클래스에서 같은 이름의 메소드를 오버라이드 한다. 현재 방향에 대한 로봇의 속도를 설정하고, direction 인스턴스 변수를 필요에 따라 조정한다. 비례 연산은 로봇이 현재 움직이고 있는 방향과 관련이 있다.

- setTurnLeftRadiansOptimal()과 setTurnRightRadiansOptimal() 메소드는 (Math.PI / 2) 보다 크게 회전하여 로봇의 방향을 바꾼다. adjustHeadingForWalls 메소드를 사용할 때 이러한 메소드들을 사용해야 하는데, 나중에 설명하겠다.

로봇 헬퍼 메소드

public double getRelativeHeadingRadians() {
    double relativeHeading = getHeadingRadians();
    if (direction < 1) {
        relativeHeading =
                normalizeAbsoluteAngleRadians(relativeHeading + Math.PI);
    }
    return relativeHeading;
}

public void reverseDirection() {
    double distance = (getDistanceRemaining() * direction);
    direction *= -1;
    setAhead(distance);
}

public void setAhead(double distance) {
    double relativeDistance = (distance * direction);
    super.setAhead(relativeDistance);
    if (distance < 0) {
        direction *= -1;
    }
}

public void setBack(double distance) {
    double relativeDistance = (distance * direction);
    super.setBack(relativeDistance);
    if (distance > 0) {
        direction *= -1;
    }
}

public void setTurnLeftRadiansOptimal(double angle) {
    double turn = normalizeRelativeAngleRadians(angle);
    if (Math.abs(turn) > HALF_PI) {
        reverseDirection();
        if (turn < 0) {
            turn = (HALF_PI + (turn % HALF_PI));
        } else if (turn > 0) {
            turn = -(HALF_PI - (turn % HALF_PI));
        }
    }
    setTurnLeftRadians(turn);
}

public void setTurnRightRadiansOptimal(double angle) {
    double turn = normalizeRelativeAngleRadians(angle);
    if (Math.abs(turn) > HALF_PI) {
        reverseDirection();
        if (turn < 0) {
            turn = (HALF_PI + (turn % HALF_PI));
        } else if (turn > 0) {
            turn = -(HALF_PI - (turn % HALF_PI));
        }
    }
        setTurnRightRadians(turn);
}

Factored Wall Avoidance 추가하기

우리가 추가할 마지막 메소드는 adjustHeadingForWalls()이다.

이 메소드의 초반부는 벽과의 근접성에 기반하여 안전한 x,y 위치를 선택한다. (이것은 로봇의 현재 x 또는 y 좌표 또는 로봇이 벽에 가까이 있을 경우 중심점이 될 것이다.) 이 메소드의 후반부는 "안전한" 방향을 계산하고 로봇이 벽에 얼마나 근접해 있는가에 비례하여 원하는 방향으로 이를 팩토링 한다.

로봇이 벽으로 나아가는 정도는 WALL_AVOID_INTERVALWALL_AVOID_FACTORS 상수를 사용하여 조정될 수 있다.

벽 피하기 메소드

private static final double WALL_AVOID_INTERVAL = 10;
private static final double WALL_AVOID_FACTORS = 20;
private static final double WALL_AVOID_DISTANCE =
        (WALL_AVOID_INTERVAL * WALL_AVOID_FACTORS);

private double adjustHeadingForWalls(double heading) {
    double fieldHeight = getBattleFieldHeight();
    double fieldWidth = getBattleFieldWidth();
    double centerX = (fieldWidth / 2);
    double centerY = (fieldHeight / 2);
    double currentHeading = getRelativeHeadingRadians();
    double x = getX();
    double y = getY();
    boolean nearWall = false;
    double desiredX;
    double desiredY;

    // If we are too close to a wall, calculate a course toward
    // the center of the battlefield.
    if ((y < WALL_AVOID_DISTANCE) ||
            ((fieldHeight - y) < WALL_AVOID_DISTANCE)) {
        desiredY = centerY;
        nearWall = true;
    } else {
        desiredY = y;
    }
    if ((x < WALL_AVOID_DISTANCE) ||
            ((fieldWidth - x) < WALL_AVOID_DISTANCE)) {
        desiredX = centerX;
        nearWall = true;
    } else {
        desiredX = x;
    }

    // Determine the safe heading and factor it in with the desired
    // heading if the bot is near a wall
    if (nearWall) {
        double desiredBearing =
           calculateBearingToXYRadians(x,
                                       y,
                                       currentHeading,
                                       desiredX,
                                       desiredY);
        double distanceToWall = Math.min(
                Math.min(x, (fieldWidth - x)),
                Math.min(y, (fieldHeight - y)));
        int wallFactor =
                (int)Math.min((distanceToWall / WALL_AVOID_INTERVAL),
                              WALL_AVOID_FACTORS);
        return ((((WALL_AVOID_FACTORS - wallFactor) * desiredBearing) +
                 (wallFactor * heading)) / WALL_AVOID_FACTORS);
    } else {
        return heading;
    }
}

결론
나머지는 쉽다. 현재의 네비게이션 알고리즘을 사용하고 adjustHeadingForWalls() 메소드를 통해 그 결과를 제공함으로써 벽을 피할 수 있다.

벽 피하기 메소드
public void run() {
    while(true) {
        setTurnRightRadiansOptimal(adjustHeadingForWalls(0));
        setAhead(100);
        execute();
    }
}

끝이다.
간단하지만 효과적이지 않은가!!!!!
냐하하 -_-; 졸린다.... 내일 시험이 2개인데 ㅡㅜ

'프로그래밍' 카테고리의 다른 글

WebPage Redirect (MFC / API)  (0) 2007.05.16
SetTimer()  (0) 2007.05.14
우연히 보게 된 코드  (2) 2007.04.19
공사  (0) 2007.04.17
매시업 데이터  (0) 2007.04.15

yjae.net 님의 블로그에서 다음과 같은 코드를 보았다.

protected void myButton_Click (object sender, EventArgs e)
{
   myPanel.Visible = !myPanel.Visible;
}

나는 왜 이런 걸 생각하지 못했을까. 나 또한 내공 부족인 걸까, IQ부족인걸까, 응용력 부족인걸까... -_ㅜ

if (myPanel.Visible == true)
{
   myPanel.Visible = false;
}
else
{
   myPanel.Visible = true;
}

'프로그래밍' 카테고리의 다른 글

SetTimer()  (0) 2007.05.14
ROBOCODE Master의 비밀!!!  (0) 2007.04.23
공사  (0) 2007.04.17
매시업 데이터  (0) 2007.04.15
동적으로 생성된 컨트롤의 이벤트 처리시 넘기는 인자값으로 인한 문제  (0) 2007.04.07

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

확인

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

확인

동적으로 생성된 컨트롤의 이벤트 처리시 넘기는 인자값으로 인한 문제입니다.

 

int NumSearch = 1

LOOP()

{

                        Button btnSearchUp = new Button();  // 버튼 객체 생성

                        btnSearchUp.Width = 70;

                        btnSearchUp.Text = "UpLoad";

                        btnSearchUp.ID = "btnSearchUp" + NumSearch.ToString();

                       

                        // 이 버튼의 명령인자에 파일의 경로와 파일 이름으로 설정

                        btnSearchUp.CommandArgument = temp;

 

                        // 해당 버튼에 이벤트를 걸어준다.

                        btnSearchUp.Click += new EventHandler(btnSearchUpClick);

 

                        NumSearch++;

}

 

 

이런 식으로 하여, 동적으로 컨트롤을 생성합니다.

컨트롤 아이디는 보시는 바와 같이 "btnSearchUp" + 1 ... 2 ... 3 이런식으로 컨트롤의 아이디가 붙습니다.

그리고 버튼의 CommandArgument 속성에 temp라는 string을 넣습니다. 그러면

아래쪽에 해당 버튼의 클릭 이벤트를 만들어 준 곳에서 이 CommandArgument 속성이 전달이 됩니다.

 

        void btnSearchUpClick(object sender, EventArgs e)

        {

             ((Button)FindControl("btn")).CommandArgument    

        }

 

이벤트 메서드 입니다.

이 부분은 엄준일 님께서 알려 주셨는데요. 이렇게 이벤트를 처리하는 메소드는 1개 뿐입니다.

전달 인자를 찾아 내기 위해서 위와 같은 방법을 쓴다는데, 이 부분은 버튼 컨트롤을 FindControl() 메서드로 찾아서 해당 속성을

추출하는 것 처럼 보입니다만, 저 부분의 "btn" 부분은 컨트롤의 아이디가 아닌가요?

컨트롤의 아이디로 알고 있는데, 여기서 문제가 생깁니다.

 

예를 들어, 위에서 동적으로 10개의 버튼 컨트롤을 생성했습니다.

그런데 사용자가 어떠한 버튼을 클릭할 지 알 수 없는데,

이벤트 처리 메서드에서는 어떻게 사용자가 클릭한 버튼 컨트롤의 아이디를 찾아서 해당 속성을 가져온다는 말인가요?

 

이러한 문제는 어떻게 해결해야 하나요?

 


 

<html xmlns="http://www.w3.org/1999/xhtml" >

<head runat="server">

    <title>제목 없음</title>

</head>

<body>

    <form id="form1" runat="server">

    <div>

               <input type="hidden" id="hid" name="hid" />

    </div>

    </form>

</body>

</html>

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

        protected void Page_Load(object sender, EventArgs e)

        {

               for (int i = 0; i < 10; i++)

               {

                       Button btn = new Button();

                       btn.ID = "btn" + i;

                       btn.Click += new EventHandler(btn_Click);

                       btn.Attributes["onclick"]     = "form1.hid.value = '" + "btn"+i + "';";

                       btn.Text = i.ToString();

                       btn.CommandArgument = "인자값 : " + i.ToString();

 

                       form1.Controls.Add(btn);

                       form1.Controls.Add(new LiteralControl("<br/>"));

               }

        }

 

        void btn_Click(object sender, EventArgs e)

        {

               string btnString       = Request["hid"].ToString();

 

               Response.Write( "버튼 컨트롤 아이디 : " + btnString + "<br>");

               Button btn = (Button)FindControl( btnString );

               Response.Write( btn.CommandArgument );

        }

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

이 소스는 10 개의 버튼을 생성한 후에 버튼을 클릭하게 되면 html 히튼 컨트롤에 버튼의 아이디 값을 넣습니다.

 

그리고 포스트백 후에 버튼 이벤트가 실행되겠지요.

 

버튼 이벤트에서 버튼의 아이디 값을 가져옵니다. 그것으로 마져 CommandArgument 값을 가져오지요..

 

ASP나 JSP 식의 코딩이지요~

 

이전에도 말씀드렸다 시피, 닷넷의 생산성이 높다는 것의 하나가 바로 바인딩 가능한 서버 컨트롤이 있기 때문이지요.

 

똑같은 소소를 DataList 로 작성해 보면..

 

<%@ Import Namespace="System.Data" %>

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

 

<script runat="server">

        private void Page_Load(object sender, EventArgs e)

        {

               if( IsPostBack ) return;

              

               // 가상의 데이터

               DataTable dt = new DataTable();

               dt.Columns.Add("Title", typeof(string) );

 

               for (int i = 0; i < 10; i++)

               {

                       DataRow row = dt.NewRow();

                       row["Title"] = "btn"+i;

                       dt.Rows.Add( row );

               }

              

               dlList.DataSource      = dt;

               dlList.DataBind();

        }

 

        protected void dlList_ItemCommand(object source, DataListCommandEventArgs e)

        {

               if (e.CommandName == "upload")

               {

                       Response.Write( e.CommandArgument.ToString() + "버튼이 클릭되었습니다");

               }

        }

</script>

 

<html xmlns="http://www.w3.org/1999/xhtml" >

<head runat="server">

    <title>제목 없음</title>

</head>

<body>

    <form id="form1" runat="server">

    <div>

               <asp:DataList ID="dlList" runat="server" OnItemCommand="dlList_ItemCommand">

                       <ItemTemplate>

                              <%# DataBinder.Eval(Container.DataItem,"Title") %> <asp:Button ID="btn" runat="server" CommandName="upload" Text='Upload' CommandArgument='<%# DataBinder.Eval(Container.DataItem,"Title") %>'></asp:Button>

                       </ItemTemplate>

               </asp:DataList>

    </div>

    </form>

</body>

</html>

 

소스량은 언듯 비슷해 보이네요~

 

하지만 작성하면서 직관성이 매우 차이가 나고, 직관적이므로 디버깅도 용이해 지겠죠~

 

또한 후자처럼 작성하시게 되면 아시겠지만, 속도면에서도 굉장히 빨라집니다.

 

VS 2003 에서 작성하시는거라 위의 녹색 부분은 Oninit 메서드에서 이벤트로 따로 연결해 주셔야 합니다.

 

DataList 의 편리함을 뒤늦게라도 아시면... "아차~ 왜 진작 안썼지~" 라고 후회하실거에영^^;

'프로그래밍' 카테고리의 다른 글

공사  (0) 2007.04.17
매시업 데이터  (0) 2007.04.15
C#으로 openAPI 이용. 그리고 XML의 파싱  (0) 2007.04.04
WEB 2.0 관련자료  (0) 2007.03.27
DefiningSOA as an Architectural Style  (0) 2007.03.27

naver OpenAPI의 내PC 검색은 일반적인 검색과 리턴 값이 달랐다.
일반적인 openAPI의 리턴값은 다음과 같다. 예를들어 검색 api는 다음과 같은 XML을 리턴한다.
<?xml version="1.0" encoding="UTF-8" ?>
- <rss version="2.0">
- <channel>
  <title>Naver Open API - webkr ::'go'</title>
  <link>http://search.naver.com</link>
  <description>Naver Search Result</description>
  <lastBuildDate>Tue, 11 Apr 2006 14:36:33 +0900</lastBuildDate>
  <total>18992582</total>
  <start>1</start>
  <display>10</display>
- <item>
  <title><b>GO</b>.com</title>
  <link>http://www.go.com/</link>
  <description>... Victims, Military Personnel Sell Rations Online Government Report Finds Ready-to-Eat Meals for Sale on eBay...... trailers and the latest buzz on... Harry Potter and the Goblet of Fire , X-Men 3 , Spider-Man 3 , more... Tonight...</description>
  </item>

보면 알겠지만 <TAG>TEXT</TAG> 형식으로 구성되어 있으므로 파싱하기가 편리하다.
직접 파싱을 해도 되지만, C#에서 파서를 제공해 주는데 굳이 할 필요는 없지 않은가.
최초 XML을 접하고 파싱에 애를 먹어 각지의 도움을 얻었었다. 그리고 준일님께서 도와주셨다.

웹서비스는 XML 을 이용한 HTTP 간의 통신 방법이다.
이 기술이 현재 각광을 받는 이유는 방화벽간에 통신을 위한 포트가 아닌,
HTTP 로 어떤 플랫폼에서도 인식이 가능한 규격화된 텍스트 형식, 즉 XML 이라는 것이다.

DataSet 이란 클래스는 무수한 기능을 가진 컬렉션이다.
이부분은 ADO.NET에 대해서 더 공부하면 알겠지만, 메모리 상의 작은 데이터베이스라고 칭할만큼 무수한 기능이 많다.
DataSet 의 ReadXml 이란 메서드는 XML 형식의 데이터를 DataSet 으로 파싱할 수 있게 된다.
(단, 모든 DataSet 에 변환가능한 형식 이어야 한다.)

파싱의 예는 다음과 같다.
string QueryURL = "쿼리문"
XmlTextReader txtReader = new XmlTextReader(QueryURL);
while(txtReader.Read())
{
 if(txtReader.NodeType == XmlNodeType.Element)
 Response.Write(txtReader.Name + " : ");
 if(txtReader.NodeType == XmlNodeType.Text)
 Response.Write (txtReader.Value + "<br>");
}

쿼리문을 던져 해당 노드의 타입을 보고 Element와 Text를 구분하여 출력해 주는 예제이다.
하지만, 문제가 있다.
naver 내PC 검색의 리턴XML의 출력은 표준 형식인 RSS 2.0 형식 (http://blogs.law.harvard.edu/tech/rss)을 따른다.
표준 RSS에 내PC검색 API용으로 추가된 태그는 nns XML namespace를 가진다.

<?xml version="1.0" encoding="UTF-8" ?>
- <rss version="2.0" xmlns:nns="http://mypc.naver.com/nnsrss">
- <channel>
  <title>Naver OpenAPI - MyPC Search Result</title>
  <link>http://127.0.0.1:4311/openapi/search?key=mypc&query=%BF%E4%BF%F8</link>
  <description>Naver MyPC Search Result</description>
  <language>ko</language>
  <generator>Nano Search 2.0</generator>
  <nns:total>5</nns:total>
  <nns:start>1</nns:start>
  <nns:display>5</nns:display>
  <nns:errorcode>0</nns:errorcode>
- <item>
- <title>
<![CDATA[alias.2x03.cipher.ws_dvdrip_xvid-fov.avi  ]]>
  </title>
- <link>
<![CDATA[G:\alias.s02\alias.2x03.cipher.ws_dvdrip_xvid-fov.avi  ]]>
  </link>
- <description>
- <![CDATA[ 

와 같은 형식은 <TAG><![CDATA[TEXT]]</TAG> 형식이다. 위의 파싱 방법으로 파싱을 할 경우에는, 올바로 파싱이 되지 않는다. <![ 형식을 Element로 판단하지 못하고 무시해 버린다.
위 파싱 방법의 소스 중, Value는 Xml Element 의 내용을 그대로 가져오게 된다.
ReadElementString() 메서드를 사용하면 말끔히 해결된다.
다음 예제는 이러한 문제도 말끔히 해결된다.

XMLFile1.XML
<?xml version="1.0" encoding="utf-8" ?>
<title><![CDATA[타이틀]]></title>
Program.CS
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
namespace ConsoleTest
{
 class Program
 {
  static void Main(string[] args)
  {
   XmlTextReader reader = new XmlTextReader("../../XMLFile1.xml");
   while (reader.Read())
   {
    if (reader.NodeType == XmlNodeType.Element && reader.Name == "title")
     Console.WriteLine(reader.ReadElementString());
   }
   reader.Close();
  }
 }
}

이상. XML의 파싱은 여기까지.
XML을 적절히 파싱한다면, 이를 이용하는 방법은 헤아릴 수도 없을 정도일 것이다.
이것이 XML의 강점(?) 이며, openAPI의 매력일지도 모르겠다. *-_-*

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

확인

어디선가 가져온 글인데, 다시 아무리 찾아봐도, 어디서 가져왔는지 알 수가 없다.
핵심 토큰을 기초로 하여 구글에서 검색을 해 봐도 나오지 않는다. 혹시나 원 저작자 분이 보실일이 있으면 지적해 주십사 하고, 포스팅 한다.[너무 염치없는것인가]

나온 지 꽤 되었는데도 불구하고 (요즘 세상에는 2 ~ 3개월이면 이미 과거이지 않나^^)  IBM에서 만든 멋진 자료를 발견하였다.
원문 위치 http://www-128.ibm.com/developerworks/library/ar-soastyle/index.html#author
"DefiningSOA as an Architectural Style" 이라는 제목으로서 Bris Lublinsky라는 분이 쓴 글인데 그가운데의 그림이 SOA를 풀어가가는 데 하나의 big picture이자 plan을 제공하는 것이 아닌가 싶다.

사용자 삽입 이미지
이 분이 생각하는 Enterprise SOA Concept이다. 바로 어떠한 조직의 범주 내에서 만들어 질 수 있는 Enterprise System의 범주는 SOA의 관점으로 보면 저렇게 잡히는 구나... 라는 생각이 저절로 든다.
우선 Business model이라는 기본적인 비즈니스 틀을 만든 다음 비즈니스 프로세스와 의미론적 data model로 분리해 냈다. 우리가 SOA라 하면 기본적으로 프로세스 중심 또는 인터페이스 중심의 서비스 연결로 생각하기 쉬운데 저기 그림에서 표현된 의미론적데이터 모델이라는 것은 바로 데이터 영역 또한 어플리케이션 밑에 숨어 있는 존재가 아닌 비즈니스 프로세스와 동급으로 관리된다는것이다.
저 그림 하나만으로도 해당 Layer가 명확해 지며 정답이라는 생각이 든다.
물론 어떻게 정의하고 그려나가야 할 것은 숙제이지만, 마치 콜롬부스가 달걀을 깨어 세웠듯이 기본적으로 생각만 하고 있던 것을 저렇게 그림으로 표현한 것이 아닌가.
약간 다른 이야기지만, 우리가 프로젝트를 진행할 때 프로세스, 방법론 등에 회의를 가지고 제대로 지켜지지 않는 경우를 가지고 비난하는 경우가 다분히 있다. 그러나 그러한 방법, 원칙 자체가 존재하지 않는 이상 어떻게 한 걸음 한 걸음 전진할 수 있을까.정진하는 삶에는 계획이 필요하듯이, 기본적인 틀은 반드시 필요한 법이다.

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

확인