Friday, February 10, 2012
Retabbed
Login | Register
아티클
  Search
Ajax.NET을 이용한 라이프게임
Ajax.NET을 이용한 라이프게임

지난번 자바스크립트 만으로 만들어진 라이프게임을 Ajax를 이용하여 구현해 보도록 한다. Ajax란 자바스크립트, DOM, XHTML등을 이용하여 페이지를 포스트백에 의존하지 않고 갱신하는 프로그래밍 방법론을 말한다. 이러한 방법론은 이미 예전 GUI프로그래밍에서는 당연히 사용하던 기법이었으나 Web에서는 이러한 기법이 일반적인 기법이 아니었던 관계로 특별히 이러한 이름으로 부르는 것에 불과하다. 기술적인 측면에서는 이미 1980년 초에 정립된 RPC(Remote Procedure Call)과 일맥 상통하는 것이라 하겠다.

즉, 클라이언트 코드에서 서버에 있는 함수를 호출하는 기법을 말한다. 불행히도 ASP.NET 1.1까지는 이러한 기법이 명확하게 정의가 되어있지 않았기 때문에 다양한 사설(?) Ajax라이브러리들이 등장하게 되었다. ASP.NET 2.0부터는 Client Callback이라는 이름으로 공식적으로 지원되고 있다. 여기서 사용할 Ajax.net는 닷넷을 위한 Ajax라이브러리의 하나로 ASP.NET 2.0의 Client Callback보다도 훨씬 많은 편리한 기능들을 제공하고 있다.

먼저 사용법을 알아보자. 자료실의 Ajax.net라이브러리를 다운받아 압축을 풀어둔다. Ajax.net을 사용할 어플리케이션의 Web.config에 다음 라인을 추가한다.

< system.web >

        < httpHandlers >

             < add verb ="POST,GET" path ="ajaxpro/*.ashx" type ="AjaxPro.AjaxHandlerFactory, AjaxPro"/>

        </ httpHandlers >

</ system.web >

HttpHandler는 서버에 Http Request가 있을 때 특정 Request를 특정한 프로그램으로 보내주는 역할을 한다. 위에서 추가된 핸들러는 “ajaxpro*.ashx”를 형태를 포함하는 리퀘스트를 AjaxPro Handler로 보내는 역할을 한다. 차후에 Ajax구현 방법 분석을 자세히 살펴볼 기회가 있을 것이다.

다음은 프로젝트의 참조에 AjaxPro.dll추가 한다. 이것으로 기본적인 설정은 완료 되었다. 이제 프로그래밍을 보자. 클라이언트에서 서버의 함수를 호출하기 위해서는 결국 서버 쪽에서 클라이언트로 서버 함수를 부를 수 있는 자바스크립트 함수를 등록해 주어야 하는데 다음과 같이 하면 된다.

               private void Page_Load(object sender, System.EventArgs e)

               {

                       AjaxPro.Utility.RegisterTypeForAjax(typeof(WebApplication1.WebForm1));

               }

Page_Load에 함수 하나만 추가하면 끝이다. 이 함수의 인수는 클라이언트에서 호출 할 함수를 포함하고 있는 클래스 이름을 주면 된다. 이 함수는 서버의 함수이름들을 자바스크립트로 생성하여 클라이언트 공간에 제공하는 역할을 한다. 서버 쪽의 함수는 아래와 같은 형식으로 구현한다.

               [AjaxPro.AjaxMethod(HttpSessionStateRequirement.ReadWrite)]

               public string MakeBoardTable(int boardSizeX, int boardSizeY, int cellSize)

               {

                       GameBoard gb = new GameBoard(boardSizeX, boardSizeY, cellSize);

                      

                       Session["BoardData"] = gb;

 

                       return gb.GetHTMLTable(true);

               }

일반 함수와 동일하고 다른 점은 함수 앞에 Ajax관련 어트리뷰트를 붙여주는 정도이다. 일반적인 어트리뷰트는

               [AjaxPro.AjaxMethod]

만 해주면 되는데 여기서는 Session변수를 사용하고 있기 때문에 특별히 Session에 대한 정의를 해주고 있다. 이 함수를 부르는 클라이언트 자바스크립트는 다음과 같다.

function NewGameAjax() {

    GameBoard.innerHTML = WebApplication1.WebForm1.MakeBoardTable(boardSizeX, boardSizeY, cellSize).value;

}

일반적인 함수를 부르는 방법과 완전히 동일하다. 다른 점이라면 서버에서 string을 반환하는데 클라이언트에서는 오브젝트 형태로 받게 되는 점이다. 실제 값은 object.value에 들어 있다. 위에서 함수를 부르는 방식은 동기화된 방식으로 부르는 것이다. 즉 서버 함수가 값을 리턴 할 때까지 위의 함수의 실행이 완료되게 된다. 비동기 방식으로 호출할 경우에는 마지막 인수로 서버함수가 실행 값을 리턴 할 때 부를 자바스크립트 함수를 정해주면 된다.

WebApplication1.WebForm1.MakeBoardTable(boardSizeX, boardSizeY, cellSize, result_func)


형식으로 하면 된다. 클라이언트 쪽 전체 구현을 보자

function NewGameAjax() {

    GameBoard.innerHTML = WebApplication1.WebForm1.MakeBoardTable(boardSizeX, boardSizeY, cellSize).value;

}

 

function cellClick(c, x, y) {

        if(c.style.backgroundColor == 'red') {

               c.style.backgroundColor = 'aqua';

               WebApplication1.WebForm1.SetCellData(x, y, 0);

        } else {

               c.style.backgroundColor = 'red';

               WebApplication1.WebForm1.SetCellData(x, y, 1);

        }

}

 

function RunGameAjax() {

        GameBoard.innerHTML = WebApplication1.WebForm1.GetNextGeneration().value;

    if(!GameStoped)

               setTimeout(RunGameAjax, 10);

}

 

function StartGameAjax() {

    GameStoped = false;

    RunGameAjax();

}

 

function StopGameAjax() {

    GameStoped = true;

}

게임보드를 그리는 일을 서버 쪽에서 하고 있기 때문에 클라이언트에서는 하는 일이 거의 없다. 지난번 순수 자바스크립트로 구현했을 때는 셀 별로 onclik을 붙이는 방법을 사용하지 않았는데 여기서는 그 방법을 사용하고 있다. 서버 쪽 코드를 보자.

               [AjaxPro.AjaxMethod(HttpSessionStateRequirement.ReadWrite)]

               public string MakeBoardTable(int boardSizeX, int boardSizeY, int cellSize)

               {

                       GameBoard gb = new GameBoard(boardSizeX, boardSizeY, cellSize);

                      

                       Session["BoardData"] = gb;

 

                       return gb.GetHTMLTable(true);

               }

 

 

               [AjaxPro.AjaxMethod(HttpSessionStateRequirement.ReadWrite)]

               public void SetCellData(int x, int y, intvalue)

               {

                       GameBoard gb = (GameBoard)Session["BoardData"];

                       gb.SetData(x, y, value);

                       Session["BoardData"] = gb;

               }

 

               [AjaxPro.AjaxMethod(HttpSessionStateRequirement.ReadWrite)]

               public string GetNextGeneration()

               {

                       GameBoard gb = (GameBoard)Session["BoardData"];

                       GameBoard gbt = gb.Copy();

 

                       for(int y=1; y<gb.SizeY-1; y++) {

                              for(int x=1; x<gb.SizeX-1; x++) {

                                      int n = gb.GetData(x-1, y-1) + gb.GetData(x, y-1) + gb.GetData(x+1, y-1) + gb.GetData(x-1, y) + gb.GetData(x+1, y) + gb.GetData(x-1, y+1) + gb.GetData(x, y+1) + gb.GetData(x+1, y+1);

                                      if(gb.GetData(x,y) == 0) {

                                             if(n == 3) {

                                                     gbt.SetData(x, y, 1);   

                                             }

                                      } else {

                                             if(n < 2 || n > 3) {

                                                     gbt.SetData(x, y, 0);   

                                             }

                                      }

                              }

                       }

                       Session["BoardData"] = gbt;

                       return gbt.GetHTMLTable(false);      

               }

 

        }

 

        public class GameBoard

        {

               int _sizeX, _sizeY, _cellSize;

               int[,] _BoaraData;

 

               public int SizeX

               {

                       get

                       {

                              return _sizeX;

                       }

                       set

                       {

                              _sizeX = value;

                       }

               }

 

               public int SizeY

               {

                       get

                       {

                              return _sizeY;

                       }

                       set

                       {

                              _sizeY = value;

                       }

               }

 

               public GameBoard(int x, int y, int cs)

               {

                       _BoaraData = newint[x, y];

                       _sizeX = x;

                       _sizeY = y;

                       _cellSize = cs;

               }

 

               public void SetData(int x, int y, intvalue)

               {

                       _BoaraData[x, y] = value;

               }

 

               public int GetData(int x, int y)

               {

                       return _BoaraData[x, y];

               }

 

               public GameBoard Copy()

               {

                       GameBoard gb = new GameBoard(_sizeX, _sizeY, _cellSize);

                       for(int x=0; x<_sizeX; x++)

                       {

                              for(int y=0; y<_sizeY; y++)

                              {

                                      gb.SetData(x, y, _BoaraData[x, y]);

                              }

                       }

                       return gb;

               }

 

               public string GetHTMLTable(bool click)

               {

                       StringBuilder sb = new StringBuilder();

                       sb.Append("<table id='BoardTable' style='background-color:Aqua' cellspacing='0' cellpadding='0' border='1'>");

                       for(int y=0; y<_sizeY; y++)

                       {

                              sb.Append("<tr>");

                              for(int x=0; x<_sizeX; x++)

                              {

                                      string cellColor = "";

                                      if (_BoaraData[x, y] == 1)

                                             cellColor = " style='background-color:red' ";

                                      string clickStr = "";

                                      if (click)

                                             clickStr = " onclick=cellClick(this," + x.ToString() + "," + y.ToString();

 

                                      sb.Append("<td" + cellColor + " width=" + _cellSize + " height=" + _cellSize + clickStr  + ")></td>");

                              }

                              sb.Append("</tr>");

                       }

                       sb.Append("</table>");

 

                       return sb.ToString();

               }

게임보드의 데이터는 서버가 가지고 있는데 서버가 이를 보관하는 방법은 Session변수에 넣는 방법을 택하고 있다. GameBoard라는 Class를 정의하여 이 클래스 안에서 보드를 정의하고 HTML을 그려주는 역할을 하고 있다. 세대를 생성하기 위해서 Copy 메서드를 클래스 안에서 구현하고 있다. GetHTMLTable 함수에서 HTML을 생성할 때 새로운 게임을 만들때는 onclik 어트리뷰트를 셀에 넣지만 게임의 각 세대를 만들어 낼 때는 넣지 않고 있다. 그 이유는 onclick이 들어갈 경우 DOM 트리를 구성할 때 시간이 훨씬 더 걸리기 때문이다. 실제 게임이 실행된 다음은 보드를 클릭할 일이 없으므로 빼도 아무런 상관이 없다.

프로젝트 소스: http://www.code99.net/tabid/842/ItemID/22/Default.aspx
 


 


 





 


게시일자 Tuesday, December 20, 2005 (Archive on Tuesday, December 27, 2005)
게시자: 불나방  저자: 불나방
돌아가기

평점:
평가:
평가 올리기

현재평점평균:
Terms | Privacy | host
Copyright 2005 Code99.NET
 | 아티클 | 자료실 | 블로그 | Q&A | 관리자에게