ASP.NET SignalR 2.0: 자습서: SignalR 2.0을 이용한 고속 실시간 기능

등록일시: 2014-01-14 08:00,  수정일시: 2014-01-21 18:38
조회수: 7,988
이 문서는 ASP.NET SignalR 기술을 널리 알리고자 하는 개인적인 취지로 제공되는 번역문서입니다. 이 문서에 대한 모든 저작권은 마이크로소프트에 있으며 요청이 있을 경우 언제라도 게시가 중단될 수 있습니다. 번역 내용에 오역이 존재할 수 있고 주석은 번역자 개인의 의견일 뿐이며 마이크로소프트는 이에 관한 어떠한 보장도 하지 않습니다. 번역이 완료된 이후에도 대상 제품 및 기술이 개선되거나 변경됨에 따라 원문의 내용도 변경되거나 보완되었을 수 있으므로 주의하시기 바랍니다.

본문에서는 ASP.NET SignalR 2.0을 이용해서 고속 메시징(High-Frequency Messaging) 기능을 제공하는 웹 응용 프로그램을 구현해봅니다. 여기에서 말하는 고속 메시징이란 일정한 비율로 갱신 정보가 전송되는 것을 의미하는데, 가령 본문의 응용 프로그램에서는 초당 최대 10회까지 메시지가 갱신됩니다.

본 자습서에서 여러분이 작성해보게 될 응용 프로그램에서는, 먼저 사용자가 드래그 할 수 있는 도형을 브라우저에 출력합니다. 그리고, 이 도형을 사용자가 드래그하면, 연결된 모든 다른 브라우저에 출력된 도형들의 위치도 드래그 된 도형의 위치와 일치하도록 일정 주기마다 갱신됩니다.

본문에 소개된 개념들은 실시간 게임이나 기타 시뮬레이션 응용 프로그램에서도 사용되고 있는 개념입니다.

본 자습서에 관한 의견들은 언제라도 환영합니다. 본문과 직접 관련이 없는 질문들은 ASP.NET SignalR 포럼이나 StackOverflow.com에 남겨주시면 됩니다.

개요

본 자습서에서는 특정 개체의 상태를 다른 브라우저들과 실시간으로 공유하는 응용 프로그램의 작성 방법을 보여줍니다. 본문에서 작성해 볼 응용 프로그램의 이름은 MoveShape로, 이 응용 프로그램의 페이지에는 사용자가 드래그 할 수 있는 HTML DIV 요소가 하나 출력됩니다. 사용자가 이 DIV 요소를 드래그하면, 이동한 요소의 새로운 위치가 서버로 전송되고, 연결되어 있는 모든 다른 브라우저들에게 전달되어 도형의 위치가 일치하도록 갱신됩니다.

The application window

이 응용 프로그램은 Damian Edwards의 데모를 기초로 하고 있으며, 이 동영상에서 직접 데모를 시청하실 수 있습니다.

본 자습서에서는 먼저 도형이 드래그되어 이벤트가 발생할 때마다 SignalR 메시지를 전송하는 방법을 살펴봅니다. 그리고, 각각의 연결된 클라이언트들은 메시지가 수신될 때마다 자신의 도형 위치를 갱신할 것입니다.

물론, 이렇게만 구현해도 응용 프로그램이 동작이야 하겠지만, 전송되는 메시지의 횟수에 상한선이 없기 때문에 클라이언트들과 서버가 메시지로 넘쳐나서 성능이 저하될 수 있으므로, 그다지 권장되는 프로그래밍 모델은 아닙니다. 클라이언트에 출력되는 애니메이션도 매번 새로운 위치로 부드럽게 도형이 이동하지 못하고, 메서드 호출에 따라 바로 이동되는 등, 일관성 없이 뚝뚝 끊어질 것입니다. 그래서, 본 자습서의 후반부에서는 클라이언트나 서버에 의해 메시지가 전송되는 최대 속도를 조절하는 타이머 기능을 구현하는 방법과 도형을 부드럽게 이동하는 방법도 살펴봅니다. 본문에서 만들어 볼 응용 프로그램의 완성본은 코드 갤러리에서 다운로드 받을 수 있습니다.

본 자습서는 다음과 같은 절들로 구성되어 있습니다:

전제조건

본 자습서는 Visual Studio 2013을 필요로 합니다.

프로젝트를 생성하고 SignalR과 JQuery.UI NuGet 패키지 추가하기

이번 절에서는 Visual Studio 2013으로 프로젝트를 생성해보겠습니다.

다음은 Visual Studio 2013을 사용해서 ASP.NET 빈 웹 응용 프로그램을 생성한 다음, SignalR과 jQuery.UI 라이브러리를 추가하는 과정들을 보여줍니다:

  1. 먼저, Visual Studio에서 ASP.NET 웹 응용 프로그램을 생성합니다.

    Create web

  2. 새 ASP.NET 프로젝트(New ASP.NET Project) 대화 상자에서 Empty 템플릿을 선택하고 확인(OK) 버튼을 클릭합니다.

    Create empty web

  3. 그리고, 솔루션 탐색기에서 프로젝트를 마우스 오른쪽 버튼으로 클릭한 다음, 추가(Add) | 새 항목(New Item) 메뉴를 선택해서 새 항목 추가(Add New Item) 대화 상자를 띄우고, 웹(Web) 카테고리에서 SignalR 허브 클래스(v2)(SignalR Hub Class (v2))를 선택합니다. 허브 클래스의 이름을 MoveShapeHub.cs로 지정하고 추가(Add) 버튼을 클릭합니다. 그러면, 새로운 MoveShapeHub 클래스가 생성되고 SignalR을 지원하기 위한 스크립트 파일들의 모음과 어셈블리 참조들이 프로젝트에 추가됩니다.

    또는, 도구(Tools) | 라이브러리 패키지 관리자(Library Package Manager) | 패키지 관리자 콘솔(Package Manager Console) 메뉴를 선택해서 콘솔을 열고, 다음 명령을 실행해서 프로젝트에 SignalR을 추가할 수도 있습니다:

    install-Package Microsoft.AspNet.SignalR

    이렇게 콘솔을 통해서 SignalR을 추가한 경우에는 SignalR 허브 클래스를 직접 생성해줘야 합니다.

  4. 도구(Tools) | 라이브러리 패키지 관리자(Library Package Manager) | 패키지 관리자 콘솔(Package Manager Console) 메뉴를 선택해서 콘솔을 열고, 패키지 관리자 창에서 다음 명령을 실행합니다.

    install-Package jQuery.UI.Combined

    그러면, 도형을 애니메이션 시킬 때 사용하게 될 jQuery UI 라이브러리가 추가됩니다.

  5. 이제 솔루션 탐색기에서 Scripts 노트를 확장해봅니다. 그러면, 프로젝트에 jQuery와 jQueryUI, 그리고 SignalR을 위한 스크립트 라이브러리들이 추가된 것을 확인할 수 있습니다.

    Script library references

기반 응용 프로그램 작성하기

이번 절에서는 마우스 이동 이벤트가 발생할 때마다 도형의 위치를 서버로 전송하는 브라우저 응용 프로그램을 작성해봅니다. 그러면, 서버는 수신된 정보를 연결된 다른 모든 클라이언트들에게 수신한 그대로 브로드캐스트합니다. 본문의 후반부에서는 이 응용 프로그램을 보다 확장해볼 것입니다.

  1. 아직 MoveShapeHub.cs 클래스를 만들지 않았다면 솔루션 탐색기에서 마우스 오른쪽 버튼으로 프로젝트를 클릭하고 추가(Add) | 클래스...(Class...) 메뉴를 선택합니다. (역주, 이전 절을 정상적으로 따라했다면 이미 클래스가 만들어져 있을 것입니다) 클래스 이름을 MoveShapeHub로 지정한 다음, 추가(Add) 버튼을 클릭합니다.

  2. 방금 생성한 MoveShapeHub의 코드를 다음 코드로 변경합니다.

    using Microsoft.AspNet.SignalR;
    using Newtonsoft.Json;
    
    namespace MoveShapeDemo
    {
        public class MoveShapeHub : Hub
        {
            public void UpdateModel(ShapeModel clientModel)
            {
                clientModel.LastUpdatedBy = Context.ConnectionId;
                // Update the shape model within our broadcaster
                Clients.AllExcept(clientModel.LastUpdatedBy).updateShape(clientModel);
            }
        }
    
        public class ShapeModel
        {
            // We declare Left and Top as lowercase with 
            // JsonProperty to sync the client and server models
            [JsonProperty("left")]
            public double Left { get; set; }
    
            [JsonProperty("top")]
            public double Top { get; set; }
    
            // We don't want the client to get the "LastUpdatedBy" property
            [JsonIgnore]
            public string LastUpdatedBy { get; set; }
        }
    }

    MoveShapeHub 클래스는 SignalR 허브를 구현한 클래스입니다. ASP.NET SignalR 2.0: 자습서: SignalR 2.0 시작하기 문서에서 설명했던 것처럼, 허브에는 클라이언트에서 직접 호출할 메서드를 구현합니다. 이번 예제에서는 클라이언트가 도형의 새 X 좌표와 Y 좌표를 담고 있는 개체를 서버로 전송하고, 이 개체는 연결된 다른 모든 클라이언트들에게 브로드캐스트 됩니다. SignalR은 자동으로 이 개체를 JSON으로 직렬화시킵니다.

    클라이언트로 전송되는 개체(ShapeModel)에는 도형의 좌표를 담고 있는 멤버 변수들이 담겨 있습니다. 이 개체의 서버 버전에는 어떤 클라이언트의 데이터가 담겨 있는지를 나타내는 멤버 변수도 존재하는데, 이 멤버 변수는 해당 클라이언트가 자신의 데이터를 다시 받지 않도록 구분하는 용도로 사용됩니다. 이 멤버 변수는 JsonIgnore 어트리뷰트를 이용해서 직렬화에서 제외되어 클라이언트로 전송됩니다.

응용 프로그램 구동 시 허브 시작하기

  1. 계속해서 이번에는 응용 프로그램이 시작될 때 허브에 대한 맵핑을 구성해보도록 하겠습니다. SignalR 2.0에서는 OWIN 구동시 시작 클래스의 Configuration 메서드가 실행되면서 MapSignalR 메서드를 호출하도록, OWIN 시작 클래스(OWIN Startup Class)를 추가하는 방식으로 이 작업을 수행합니다. 이 클래스는 OwinStartup 어셈블리 어트리뷰트를 통해서 OWIN의 구동 프로세스에 추가됩니다.

    다시, 솔루션 탐색기에서 마우스 오른쪽 버튼으로 프로젝트를 클릭하고 추가(Add) | 새 항목(New Item) 메뉴를 선택해서 새 항목 추가(Add New Item) 대화 상자를 띄운 다음, 웹(Web) 카테고리에서 OWIN 시작 클래스(OWIN Startup Class)를 선택합니다. 클래스의 이름은 Startup으로 지정하고 추가(Add) 버튼을 클릭합니다.

  2. Startup.cs 파일의 내용을 다음 코드로 변경합니다:

    using Microsoft.Owin;
    using Owin;
    
    [assembly: OwinStartup(typeof(MoveShapeDemo.Startup))]
    namespace MoveShapeDemo
    {
        public class Startup
        {
            public void Configuration(IAppBuilder app)
            {
                // Any connection or hub wire up and configuration should go here
                app.MapSignalR();
            }
        }
    }

클라이언트 추가하기

  1. 이번에는 클라이언트를 추가해보겠습니다. 다시, 솔루션 탐색기에서 마우스 오른쪽 버튼으로 프로젝트를 클릭한 다음, 추가(Add) | HTML 페이지(HTML Page) 메뉴를 클릭합니다. HTML 페이지의 이름을 Default.html로 지정하고 확인(OK) 버튼을 클릭합니다.

  2. 솔루션 탐색기에서 방금 생성한 HTML 페이지를 마우스 오른쪽 버튼으로 클릭한 다음, 시작 페이지로 설정(Set as Start Page) 메뉴를 클릭합니다.

  3. HTML 페이지의 기본 코드를 다음 코드 조각으로 변경합니다.

    노트: 프로젝트에 실제로 추가된 스크립트 파일들의 버전과 아래 코드에 작성된 스크립트 참조의 버전이 일치하는지 확인하시기 바랍니다.

    <!DOCTYPE html>
    <html>
    <head>
        <title>SignalR MoveShape Demo</title>
        <style>
            #shape {
                width: 100px;
                height: 100px;
                background-color: #FF0000;
            }
        </style>
    </head>
    <body>
        <script src="Scripts/jquery-1.10.2.min.js"></script>
        <script src="Scripts/jquery-ui-1.10.3.min.js"></script>
        <script src="Scripts/jquery.signalR-2.0.0.js"></script>
        <script src="/signalr/hubs"></script>
        <script>
            $(function () {
                var moveShapeHub = $.connection.moveShapeHub,
                $shape = $("#shape"),
                shapeModel = {
                    left: 0,
                    top: 0
                };
                moveShapeHub.client.updateShape = function (model) {
                    shapeModel = model;
                    $shape.css({ left: model.left, top: model.top });
                };
                $.connection.hub.start().done(function () {
                    $shape.draggable({
                        drag: function () {
                            shapeModel = $shape.offset();
                            moveShapeHub.server.updateModel(shapeModel);
                        }
                    });
                });
            });
        </script>
        
        <div id="shape" />
    </body>
    </html>

    이 HTML 및 자바스크립트 코드는 붉은색 DIV로 도형을 생성하고, jQuery 라이브러리를 이용해서 도형의 드래깅 동작을 활성화시킵니다. 그리고, 도형의 drag 이벤트를 이용해서 도형의 위치를 서버로 전송합니다.

  4. F5를 눌러서 응용 프로그램을 실행합니다. 그리고, 페이지의 URL을 복사해서 두 번째 브라우저 창에 붙여 넣습니다. 브라우저 창들 중에서 한 쪽의 도형을 드래그하면 다른 브라우저 창의 도형도 따라서 움직일 것입니다.

    The application window

클라이언트 루프 추가하기

마우스 이동 이벤트가 발생할 때마다 매번 도형의 위치를 전송하면 불필요한 네트워크 트래픽이 발생하기 때문에, 클라이언트에서 발생하는 메시지들을 조절할 필요가 있습니다. 따라서 이번에는 자바스크립트의 setInterval 함수를 이용해서 새로운 위치 정보를 일정한 비율로 서버로 전송하는 루프를 구성해보겠습니다. 이 루프는 게임이나 각종 시뮬레이션 등에서 전체 기능을 진행해나가는 반복적으로 호출되는 함수인, "게임 루프"의 가장 기본적인 형태입니다.

역주: '게임 루프'라는 용어는 관련 업계에 종사하시는 분들에게는 매우 일상적인 개념일 것입니다. 그러나, 주로 SI를 비롯한 엔터프라이즈 업계에서 종사하시는 분들에게는 다소 낯선 개념이라고 생각하는데, 구글링을 해보시면 쉽게 대략적인 의미를 파악하실 수 있을 것입니다.

  1. HTML 페이지의 클라이언트 코드를 다음 코드와 같이 업데이트 합니다.

    <!DOCTYPE html>
    <html>
    <head>
    <title>SignalR MoveShape Demo</title>
    <style>
        #shape {
            width: 100px;
            height: 100px;
            background-color: #FF0000;
        }
    </style>
    </head>
    <body>
        <script src="Scripts/jquery-1.10.2.min.js"></script>
        <script src="Scripts/jquery-ui-1.10.3.min.js"></script>
        <script src="Scripts/jquery.signalR-2.0.0.js"></script>
        <script src="/signalr/hubs"></script>
        <script>
            $(function () {
                var moveShapeHub = $.connection.moveShapeHub,
                    $shape = $("#shape"),
                    // Send a maximum of 10 messages per second 
                    // (mouse movements trigger a lot of messages)
                    messageFrequency = 10,
                    // Determine how often to send messages in
                    // time to abide by the messageFrequency
                    updateRate = 1000 / messageFrequency,
                    shapeModel = {
                        left: 0,
                        top: 0
                    },
                    moved = false;
                moveShapeHub.client.updateShape = function (model) {
                    shapeModel = model;
                    $shape.css({ left: model.left, top: model.top });
                };
                $.connection.hub.start().done(function () {
                    $shape.draggable({
                        drag: function () {
                            shapeModel = $shape.offset();
                            moved = true;
                        }
                    });
                    // Start the client side server update interval
                    setInterval(updateServerModel, updateRate);
                });
                function updateServerModel() {
                    // Only update server if we have a new movement
                    if (moved) {
                        moveShapeHub.server.updateModel(shapeModel);
                        moved = false;
                    }
                }
            });
        </script>
       
        <div id="shape" />
    </body>
    </html>

    이번 코드에는 일정한 주기마다 호출되는 updateServerModel 함수가 추가되었습니다. 이 함수는 전송할 새로운 위치 데이터가 존재함을 나타내는 moved 플래그가 설정되어 있으면 서버로 위치 데이터를 전송합니다.

  2. 다시 F5를 눌러서 응용 프로그램을 실행합니다. 그리고, 페이지의 URL을 복사해서 두 번째 브라우저 창에 붙여 넣습니다. 브라우저 창들 중에서 한 쪽의 도형을 드래그하면 다른 브라우저 창의 도형도 따라서 움직입니다. 그러나, 이번에는 서버로 전송되는 메시지의 횟수가 조절되고 있기 때문에, 애니메이션이 이전 절에서 살펴봤을 때보다는 부드럽게 나타나지 않을 것입니다.

    The application window

서버 루프 추가하기

응용 프로그램의 현재 상태에서는 서버가 메시지를 받은 횟수만큼 그대로 서버에서 클라이언트로 메시지가 전송됩니다. 따라서, 클라이언트에 관해서 살펴봤던 것과 비슷한 문제가 발생하게 됩니다. 즉, 필요한 수준 이상으로 메시지가 전송될 수 있기 때문에 과도한 수준의 연결이 만들어질 수 있습니다. 이번 절에서는 클라이언트로 전송되는 메시지의 양을 조절해주는 타이머를 구현해서 서버를 개선해보겠습니다.

  1. MoveShapeHub.cs 파일의 코드를 다음 코드 조각으로 변경합니다.

    using System;
    using System.Threading;
    using Microsoft.AspNet.SignalR;
    using Newtonsoft.Json;
    
    namespace MoveShapeDemo
    {
        public class Broadcaster
        {
            private readonly static Lazy<Broadcaster> _instance =
                new Lazy<Broadcaster>(() => new Broadcaster());
            // We're going to broadcast to all clients a maximum of 25 times per second
            private readonly TimeSpan BroadcastInterval = TimeSpan.FromMilliseconds(40);
            private readonly IHubContext _hubContext;
            private Timer _broadcastLoop;
            private ShapeModel _model;
            private bool _modelUpdated;
    
            public Broadcaster()
            {
                // Save our hub context so we can easily use it 
                // to send to its connected clients
                _hubContext = GlobalHost.ConnectionManager.GetHubContext<MoveShapeHub>();
                _model = new ShapeModel();
                _modelUpdated = false;
    
                // Start the broadcast loop
                _broadcastLoop = new Timer(
                    BroadcastShape,
                    null,
                    BroadcastInterval,
                    BroadcastInterval);
            }
    
            public void BroadcastShape(object state)
            {
                // No need to send anything if our model hasn't changed
                if (_modelUpdated)
                {
                    // This is how we can access the Clients property 
                    // in a static hub method or outside of the hub entirely
                    _hubContext.Clients.AllExcept(_model.LastUpdatedBy).updateShape(_model);
                    _modelUpdated = false;
                }
            }
    
            public void UpdateShape(ShapeModel clientModel)
            {
                _model = clientModel;
                _modelUpdated = true;
            }
    
            public static Broadcaster Instance
            {
                get
                {
                    return _instance.Value;
                }
            }
        }
    	
        public class MoveShapeHub : Hub
        {
            // Is set via the constructor on each creation
            private Broadcaster _broadcaster;
    
            public MoveShapeHub() : this(Broadcaster.Instance)
            {
            }
    
            public MoveShapeHub(Broadcaster broadcaster)
            {
                _broadcaster = broadcaster;
            }
    
            public void UpdateModel(ShapeModel clientModel)
            {
                clientModel.LastUpdatedBy = Context.ConnectionId;
                // Update the shape model within our broadcaster
                _broadcaster.UpdateShape(clientModel);
            }
        }
    
        public class ShapeModel
        {
            // We declare Left and Top as lowercase with 
            // JsonProperty to sync the client and server models
            [JsonProperty("left")]
            public double Left { get; set; }
    
            [JsonProperty("top")]
            public double Top { get; set; }
    
            // We don't want the client to get the "LastUpdatedBy" property
            [JsonIgnore]
            public string LastUpdatedBy { get; set; }
        }
    }

    이번 코드에는 .NET 프레임워크의 Timer 클래스를 이용해서 외부로 전송되는 메시지들을 조절하는 Broadcaster 클래스가 추가되었습니다.

    허브 자체가 일시적이기 때문에 (필요할 때마다 다시 생성됩니다) Broadcaster 클래스는 싱글톤(Singleton)으로 생성됩니다. 그리고, 지연된 초기화(Lazy Initialization, .NET 4에서 도입되었습니다)로 클래스가 필요할 때까지 생성을 미뤄서, 타이머가 시작되기 전까지 첫 번째 허브의 인스턴스가 완벽하게 생성되는 것을 보장합니다.

    역주: 지연된 초기화(Lazy Initialization) 자체는 이미 널리 알려져 있던 개념으로 이를 가리키는 번역 용어도 게으른 초기화, 초기화 지연 등 다양합니다. 바로, Lazy<T> 클래스가 .NET 프레임워크 4에서 새로 추가된 기능입니다.

    또한, 클라이언트의 UpdateShape 함수에 대한 호출도 허브의 UpdateModel 메서드 외부로 이동했기 때문에 더 이상 메시지를 수신했다고 해서 즉시 호출되지 않습니다. 그 대신, 클라이언트에 대한 메시지는 Broadcaster 클래스에 포함된 _broadcastLoop 타이머에 의해서 초당 25번까지만 호출되도록 관리됩니다.

    마지막으로, 허브가 직접 클라이언트 메서드를 호출하는게 아니라 Broadcaster 클래스가 GlobalHost를 통해서 현재 동작중인 허브에 대한 참조(_hubContext)를 얻은 다음, 대신 호출합니다.

  2. 다시 F5를 눌러서 응용 프로그램을 실행합니다. 그리고, 이번에도 페이지의 URL을 복사해서 두 번째 브라우저 창에 붙여 넣습니다. 브라우저 창들 중에서 한 쪽의 도형을 드래그하면 다른 브라우저 창의 도형도 따라서 움직일 것입니다. 이전 절에서 살펴봤던 브라우저의 움직임과 그리 다를게 없지만, 클라이언트로 전송되는 메시지의 횟수가 조절되고 있는 상태입니다.

    The application window

클라이언트에서 부드러운 애니메이션 구현하기

이제 응용 프로그램이 거의 완성됐지만, 아직 한 가지 개선할 부분이 남아 있는데, 바로 서버의 메시지에 따라 이동하는 클라이언트 도형의 움직임이 그것입니다. 도형을 서버에서 전달된 새로운 위치로 바로 이동하는 대신, JQuery UI 라이브러리의 animate 함수를 사용해서 기존 위치에서 새 위치로 이동하는 도형의 움직임을 부드럽게 만들어 보겠습니다.

  1. 클라이언트의 updateShape 메서드를 다음 코드에 강조 처리된 부분처럼 변경합니다:

    <!DOCTYPE html>
    <html>
    <head>
        <title>SignalR MoveShape Demo</title>
        <style>
            #shape {
                width: 100px;
                height: 100px;
                background-color: #FF0000;
            }
        </style>
    </head>
    <body>
        <script src="Scripts/jquery-1.10.2.min.js"></script>
        <script src="Scripts/jquery-ui-1.10.3.min.js"></script>
        <script src="Scripts/jquery.signalR-2.0.0.js"></script>
        <script src="/signalr/hubs"></script>
        <script>
            $(function () {
                var moveShapeHub = $.connection.moveShapeHub,
                    $shape = $("#shape"),
                    // Send a maximum of 10 messages per second 
                    // (mouse movements trigger a lot of messages)
                    messageFrequency = 10,
                    // Determine how often to send messages in
                    // time to abide by the messageFrequency
                    updateRate = 1000 / messageFrequency,
                    shapeModel = {
                        left: 0,
                        top: 0
                    },
                    moved = false;
                moveShapeHub.client.updateShape = function (model) {
                     shapeModel = model;
                     // Gradually move the shape towards the new location (interpolate)
                     // The updateRate is used as the duration because by the time 
                     // we get to the next location we want to be at the "last" location
                     // We also clear the animation queue so that we start a new 
                     // animation and don't lag behind.
                     $shape.animate(shapeModel, { duration: updateRate, queue: false });
                };
                $.connection.hub.start().done(function () {
                    $shape.draggable({
                        drag: function () {
                            shapeModel = $shape.offset();
                            moved = true;
                        }
                    });
                    // Start the client side server update interval
                    setInterval(updateServerModel, updateRate);
                });
                function updateServerModel() {
                    // Only update server if we have a new movement
                    if (moved) {
                        moveShapeHub.server.updateModel(shapeModel);
                        moved = false;
                    }
                }
            });
        </script>
       
        <div id="shape" />
    </body>
    </html>

    이 코드는 도형을 기존 위치에서부터 서버에서 전달받은 새 위치까지, 이동할 경로를 따라 애니메이션 인터벌(이 코드에서는 100 밀리세컨드)마다 이동시킵니다. 새로운 애니메이션이 시작되기 전에 도형에 적용된 기존 애니메이션들은 모두 제거됩니다.

  2. 다시, F5를 눌러서 응용 프로그램을 실행합니다. 그리고, 페이지의 URL을 복사해서 두 번째 브라우저 창에 붙여 넣습니다. 브라우저 창들 중에서 한 쪽의 도형을 드래그하면 다른 브라우저 창의 도형도 따라서 움직일 것입니다. 다른 창에서 동작하는 도형의 움직임은 전달된 메시지에 직접 대응해서 실행되는 것이 아니라 시간에 따라 보간되어 이동하기 때문에 더 부드럽게 보일 것입니다.

    The application window

이후 과정 안내

본 자습서에서는 서버와 클라이언트 간에 고속으로 메시지가 전송되는 SignalR 응용 프로그램을 개발하는 방법을 살펴봤습니다. 이런 통신 패러다임은 SignalR로 개발한 ShootR 게임 같은 온라인 게임이나 기타 시뮬레이션을 개발할 때 유용합니다.

본 자습서에서 만들어볼 응용 프로그램의 완성본은 코드 갤러리에서 다운로드 받을 수 있습니다.

더 고급의 SignalR 개발 개념들을 살펴보고 싶다면, SignalR 소스 코드와 리소스들이 제공되는 다음 사이트들을 방문해보시기 바랍니다:

본 자습서의 예제 응용 프로그램이나 다른 SignalR 응용 프로그램들을 호스팅 공급자에 배포해서 인터넷 상에서 사용할 수도 있습니다. 마이크로소프트는 최대 10개까지 웹 사이트를 무료로 호스팅 할 수 있는 Windows Azure trial account를 제공해주고 있습니다. 예제 SignalR 응용 프로그램을 배포하는 과정은 Publish the SignalR Getting Started Sample as a Windows Azure Web Site 포스트를 참고하시기 바랍니다. 그리고, Visual Studio 웹 프로젝트를 Windows Azure 웹 사이트에 배포하는 방법에 대한 더 자세한 정보는 Deploying an ASP.NET Application to a Windows Azure Web Site를 참고하시기 바랍니다.

노트: 현재 Windows Azure 웹 사이트에서는 WebSocket 전송방식이 지원되지 않습니다. WebSocket 전송방식을 사용할 수 없는 경우, SignalR은 ASP.NET SignalR 2.0: SignalR 소개 문서의 전송방식들(Transports)과 그 대안(Fallbacks) 절에서 설명한 것처럼 사용 가능한 다른 전송방식들을 사용하게 됩니다. *

* 이제 Windows Azure 웹 사이트에서도 .NET 프레임워크 버전이 4.5로 설정되어 있고, 사이트 구성 페이지의 웹 소켓 항목이 활성화되어 있으면 WebSocket을 사용할 수 있습니다