GIGO MIND DEV BLOG

Visual Studio Live Server

웹 프로그래밍 중 쓸만한 extension 중에서는 HTML의 태그를 빠르게 만들어 낼 수 있는 Emmet extension이라던가, Node.js에서 제공하는 live server과 같은 것이 있다.

Visual Studio IDE에서는 이 Live Server 자체는 없고, 그 대신, Browser link라는 것이 존재한다.

Browser Link는 Tools > Options > Web을 살펴보다보면 Enable 할 수 있게 되는데, VS의 debug option(메뉴 아이템 중 새로고침같이 생긴모양)에서 Enable 되었는지 확인 할 수 있다.

Link가 잘 진행 되었을 시는, CSS 스타일을 바꿀 시는 auto sync가 적용되는 모양이지만, html, aspx, cshtml과 같은 페이지들을 저장할 때는 reload on save가 적용되지 않는다.

이 때, 저장 후 CTRL+ALT+Enter를 눌러 manual refresh를 IDE에서 브라우저로 signalR을 이용해 시그널을 보내는 방법도 있겠지만, 이 또한 불편하기에, 세이브 시 리로드도 같이 수행하는 extension이 존재한다.

이 Extension의 이름은 Browser Reload On Save이다.

다운로드를 끝마치면, Tools > Options > Web의 Browser Reload On Save를 Enable 해주면 된다.

ANYMATE APP (2)

ANYMATE의 핵심 기능인 Collaborative Project and Drawing을 하기엔 웹 소켓이 필요하다 했다.

하지만 ASP.NET Framework를 사용한다고 마음먹은 지금, Web Socket을 그대로 이용하는 것보단 SignalR을 사용하는게 더 낫다고 볼 수 있다. 왜냐하면 SignalR은 Web Socket을 포함한 추상화를 제공하는 ASP.NET이 추천하는 RPC 방식이기 때문이다.

분명히, 대학교에서 Socket Programming을 배울땐 Low level로 하나 하나 설정해 chat server/client 같은 것을 생성한 적이 있다.
그리고 분명 Socket Programming은 굉장히 강력하고 목적에 따라서는 일반적으로 굉장히 좋은 방법인 것은 확실하다.

하지만 웹 브라우저와 서버와의 통신에 사용되는 Web Socket을 이용할 때는, 브라우저의 종류라던가, 환경이라던가, 모바일에서 쓰이는가라던가 등등 Web Socket 자체가 비교적 최근 기술이기에 아예 호환조차 안되는 경우가 있다.

이렇기에 마치 css에는 modernizer이 있듯, RPC에는 SignalR이 있는 것이다.


SignalR은 앞서 말햇듯, Web Socket을 포함한 RPC 방식의 Abstraction이라고 보면 편하다.

SignalR은 RPC 4가지 기능들을 한 데 모아서 필요한 목적에 따라서 그 4가지 중의 한가지 방식을 ASP.NET이 결정해서 써준다고 보면 된다.

그리고 그 4가지 방식은 다음과 같다.

  • Web Sockets
  • Server Sent Events - SSE (Event Source)
  • Forever Frame
  • Long Poll (AJAX에 Poll(unblocking)을 끼얹은 형태)

Web Socket이 내가 구현하려는 웹 앱의 목적 (클라이언트와 서버간의 push, request가 매우 잦은 경우) 과 매우 잘 맞기 때문에, Long Poll 부터 시작해서 Web Sockets 까지 간략하게 설명하도록 하겠다.
Long Poll을 설명하기에 앞서는 AJAX를 설명하고 가면 매우 편하다.


HTTP 개념 복습 (대충)

기본적으로 웹 클라이언트 - 웹 서버는 HTTP 프로토콜이라는 통신을 사용한다.
주소가 http로 시작하는 이유가 그 이유인데, 이 HTTP의 특징은 stateless 하다는 것이다.
Stateless 하다는 것은 클라이언트가 서버에게 뭔가를 요청하면, 요청 할 때마다 서버는 그 클라이언트가 몇초 전에 요청을 보냈었나, 클라이언트가 무슨 요청을 보냈었나, 이런 거를 아예 기억 못하는 기억 상실증에 걸린 것 마냥 행동한다는 뜻이다.

자, 어쨋든, 이런 식으로 우리의 브라우저(클라이언트)는 서버와 기본적으로 http를 사용하기에 우리 눈에 보이는 몇가지 특징 들이 있다.
제일 눈에 잘 보이며 HTTP의 특징을 잘 보여주는 기능은 새로 고침이라는 기능이라고 할 수 있다.
너무나 자연스럽게 적응이 되어서 별로 이상한 점도 모르겠는 새로고침이라는 기능은 HTTP를 써서 서버로부터 웹 페이지를 받아오는 클라이언트의 특징 때문에 존재한다고 생각하면 편하다.

예를 들어서, 우리가 어떤 블로그를 보고 있다고 하자.
만약 그 블로그에 글을 쓰는 주인이 어떠한 새로운 글을 올렸다면? 그리고 이 블로그에서는 HTTP의 기능만 사용한다면?

기존의 HTTP를 쓴 우리의 브라우저는 블로그를 보려고 URL에 블로그 주소를 쳤을 때, 서버에게서 블로그의 웹 페이지를 받아와서 브라우저 상에 렌더링을 한 경우이기에, 블로그 주인장이 서버에 어떤 변화를 주었다고 해도, 다른 connection이 있지 않는 이상, 클라이언트의 브라우저에는 아무 일도 일어나지 않는다.
하지만 새로고침 버튼을 누르는 순간, 다시 서버에게서 페이지를 받아오기에, 변화가 적용이 된 페이지를 다시 받아 오게 된다.

당연하게도 이 방식은 매우 불편하다.
우리는 클라이언트가 서버에 변화가 생겼을 때 바로 업데이트가 되기를 원한다.
그리고 이 방식으로 하나 제안되었던게 바로 이제 다룰 AJAX이다.


AJAX : Asynchronous Javascript XML

AJAX는 이름만 풀이해보자면 비동기적 자바스크립트 그리고 XML이다.
AJAX가 비동기적이라는것은 AJAX가 실행되는 동안에 원래 클라이언트 브라우저가 멈추지 않는다는 것이다.
즉, AJAX의 기능이 실행되는 동안에 원래 클라이언트 브라우저에서 행할 수 있는 form이라던가 submit, redirect 같은 기능들이 다 실행 가능하다는 것이다.

Javascript XML이 붙은 이유는 Javascript를 통해서 XML이라는 데이터 transition에 특화된 언어를 이용해 데이터를 주고받는다는 것을 뜻한다.

즉, 기존의 HTTP에서 페이지 정보를 받고 stateless, connectionless로 서버와 클라이언트가 단절된 환경에 존재하게 만드는 시스템에 또 다른 데이터를 주고 받을 수 있는 방법을 제공해줌으로써, 기존 HTTP의 정적인 환경에서 벗어나 조금 더 동적인 환경을 제공해준다고 보면 된다.

이 AJAX를 쓰면 앞서 다뤘던 예시인 블로그에서 블로그 주인장이 포스팅을 했을 때, 블로그를 보던 클라이언트의 브라우저에서 포스팅에 대한 데이터를 받아서 새로고침 없이도 웹 페이지를 업데이트 한다던가 같은 기능을 추가할 수 있다.

하지만 “블로그 주인장이 포스팅을 했을 때” 라고 표현했음에도 불구하고, 사실 이 데이터 통신의 시작은 항상 클라이언트가 해야 한다는 특징이 있다.
다시 말해서, 서버에서 알려줘야 할 클라이언트들을 기억해 뒀다가, 새로운 변화가 생겼을 때 서버쪽에서 먼저 나눠서 알려주는 것 (push)이 아니고, 클라이언트에서 시간이 어느정도 지나거나, 어떤 버튼이 눌리는 이벤트가 생겼을시에, 서버에게 혹시 새로운거 뭐 없니? 하고 요청을 하고, 서버쪽에서 있다, 없다에 맞춰서 XML/JSON 데이터를 날려주는게 AJAX라고 보면 된다.

알아보기 쉽게, 데이터 통신은 다음과 같이 진행된다.

클라이언트:  (3초마다) "새로운 데이터 있니?"
서버: . . . timeout
클라이언트:  (3초마다) "새로운 데이터 있니?"
서버: {"answer": "no"}
클라이언트:  (3초마다) "새로운 데이터 있니?"
서버: {"answer": "no"}
클라이언트:  (3초마다) "새로운 데이터 있니?"
서버: {"result": {"id": "1", "loc": "VA"}}

보자마자 문제점이 보이겠지만, 클라이언트 쪽에서 몇초마다 물어줘야 한다는 구조상의 비효율성, 서버쪽으로 데이터를 요청할 때마다 발생하는 오버헤드, timeout이 일어날 수 있는 낮은 의존도, 새로운 데이터가 없을 때 나타나는 비효율성 등이 있겠다.

이와 같은 단점들을 좀 보완한게 Long Polling 이다.


Long Polling

Long polling은 AJAX와 매우 비슷하게 클라이언트 쪽에서 요청을 함으로써 시작한다고 보면 된다.
하지만 AJAX와는 다른점은 서버가 데이터가 없을 때는 데이터가 생길 때까지 답을 안한다는 점이 있겠다 (polling).
클라이언트는 이 맨 처음 요청을 보낸 이후는 서버가 답을 할 때 까지 요청을 보내지 않는다.

이렇게 하면 몇초마다 물어줘야하는 비효율성이 없어지고, 서버쪽에 데이터를 요청할 때의 오버헤드가 확연히 줄며, timeout이 일어날 가능성이 줄고, 새로운 데이터가 없을 때는 서버쪽에서 polling으로 기다려주기에 비효율적인 부분이 사라진다.


Forever Frame

AJAX와 Long Polling이 클라이언트 쪽에서 요청을 하고 답을 받아오는 형태였다면, 앞으로 다룰 녀석들은 서버쪽에서 푸쉬를 해서 클라이언트로 정보를 한방향으로 넘겨주는 녀석들이다.

Forever Frame은 HTML의 iframe을 이용한다.
iframe은 페이지 내에서 또 다른 서버와 connection을 만드는것, 다시 말해서 페이지 내에서 작동하는 또 다른 페이지라고 생각하면 편하다.
이런 특징때문에 실생활에서 제일 접하기 쉬운 iframe은 배너로 뜨는 광고를 보면 편하다.

배너 광고를 보면 불법 사이트 같은 경우 배너를 클릭 하지 않더라도 마치 배너광고를 클릭한 것 마냥 작동하는 경우가 있는데, 이게 자바스크립트 클릭 같은 것을 이벤트 핸들링해놔서 그런 것이 있고, 같은 브라우저의 보는 화면 내에 두 개의 페이지가 켜져있는 상태인지라, 유저가 타이핑하는 내용같은 것도 intercept할 수 있는, 보완상에서 보면 무섭게 작동할 수 있는 녀석이다.
하지만 iframe의 부모 사이트의 쿠키나 세션같은 정보를 탈취할 수는 없으니, 유저가 조심하면 그 때부터는 그렇게 위협적인 것도 없는것이 사실이다.
(허나 자바스크립트가 실행되는 특성상, 여느 웹사이트와 마찬가지로, 유저 인풋과 상관없이 나쁜 코드가 실행될 수 있다는 근본적인 문제는 존재한다. )

어쨋든, iframe은 이런 식으로 다른 웹 페이지의 내부에서 다른 서버와 connection을 만들 수 있는, 즉 다른 서버로의 URL을 가지고 있는 HTML 요소이다.

그리고 이렇기에, iframe을 통해 연결된 서버는 클라이언트에게 announce()같은 단방향 function을 실행시켜 데이터같은걸 전해줄 수 있게 되는 것이다.
그리고 당연하게도, 이 방법에 클라이언트의 요청은 필요가 없다.

그리고 이제 iframe을 통해 완벽히 데이터를 받을 수 있다는 걸 이해한 지금 시점에, forever frame의 이름을 풀이하자면, iframe이 클라이언트상에 계속 존재하며 서버가 데이터를 자기가 원할 때 보내면 언제든지 이용된다는 뜻이다.

이 Forever Frame의 단점은 바로 iframe으로써 클라이언트의 코드에 “존재”한다는 것이다.
그렇다. 존재가 바로 단점이다. 이렇게 평생 존재하는 iframe은 데이터를 보내고나서 메모리를 많이 차지하게 되는데, 이것에 대한 low-level 해법은 데이터를 보낸 후 차지되는 메모리를 free해주는 것이다.
그리고 또 당연하게도, iframe은 html의 요소이므로, iframe을 제공하지 않는 애플리케이션같은 경우는 forever frame을 이용하지 못한다.
그리고 서버가 클라이언트에게 단방향으로 보내주는 부분은 내가 만들려고 하는 채팅 서버/클라이언트 라던가 이런 부분에 쓰이기 매우 불편하다.


Server Sent Events - SSE (Event Source)

SSE 혹은 Event Source 또한 Forever Frame 처럼 서버에서 클라이언트로 단방향 통신을 만들어낸다.

이게 작동하는 원리는 서버가 클라이언트에게 보내는 Content Type을 바꾸는 것에 있는데, 원래 html같은 정보를 보낼때는 text/html, 자바스크립트 같은 경우는 text/javascript라는 content type을 명시하는데, 이 event source는 content type을 text/event-stream이라는 것으로 명시함으로써 클라이언트가 그 컨텐트 타입을 받자마자 서버-클라이언트 persistent connection을 만들고, listening을 하기 시작한다.
그러면 이 데이터 스트림은 그 단방향 컨넥션을 통해 클라이언트에게 전달되게 된다.


Web Sockets

웹 소켓은 소켓 프로그래밍에 기초한 비교적 최근의 기술이고 내가 구현하려는 채팅 서버/클라이언트, 클라이언트에서 보내는 정보를 다른 클라이언트에게 그대로 혹은 가공 후 전해주는 것에 유용한 bidirectional, full-duplex communication이다.

먼저 클라이언트는 서버에게 웹 소켓을 사용하자고 요청한다.
서버가 요청을 안전하게 받고 accept를 하게 되면, 클라이언트와 서버간에는 persistent connection이 형성되는데, 좋은 점은 이 persistenet connection은 한 서버에서 여럿 클라이언트와 함께 동시에 형성 할 수 있다는 점이다.
한 세션에 여러 클라이언트가 있을 시에 매우 좋은 조건이다.
그리고 이 connection은 서버나 클라이언트 둘 중 한명이라도 끊을 시 사라지는데, 클라이언트가 브라우저를 닫거나 해도 알아서 이 컨넥션을 끊어준다.
즉, 예전에 온라인, 오프라인 state를 만들어야 했을 시, 클라이언트의 요청이 안 온지 몇분이 지났나 체크할 필요 없이, 웹 소켓을 통해 연결을 했다면, 온라인 오프라인 state는 클라이언트가 그 컨넥션에 존재하나의 유무를 통해 쉽게 정할 수 있다는 뜻이다.

그리고 위의 과정들은 일련의 프로토콜이고, ws protocol을 찾아보면 더 자세히 알아볼 수 있다.

당연하게도, signalR을 쓰는 이유가 그러하겠지만, 비교적 최신 기술이니 아주 옛날 브라우저들은 ws protocol을 지원하지 않는 경우가 있다.


SignalR

SignalR은 위의 설명 모두를 포함해 유저가 알게 모르게 바꿔가며 상황에 맞는 방법을 사용해준다.

닷넷 프레임워크에서는 이 signalR을 쓰기 위해 두 가지 클래스를 제공해주는데, Hub과 Persistent Connection이 두개가 있다.

Hub은 조금 더 Higher level이고 쓰기 편하다. 마이크로소프트에서 추천하는 방식도 Hub을 사용하는 것이다.

그리고 우리가 쓸 signalR 오브젝트는 이 Hub을 inherit 함으로써 사용할 수 있는데, C#의 문법으로는 class MyClass : Hub 과 같은 식으로 쓰면 바로 Hub의 특성을 가진 자식 클래스를 사용할 수 있다.

SignalR을 사용하기 위해선 설치를 해야하는데, 비쥬얼 스튜디오를 킨 후에, 솔루션의 프로젝트 내부 참조(references) 부분에 SignalR을 추가해줘야 한다.
이를 위해선, view > other windows > package manager console 을 눌러서 터미널에 Install-Package Microsoft.Aspnet.Signalr 을 쳐서 설치를 진행해도 되고,
마치 nodejs의 npm처럼 닷넷에서 제공하는 Nugget Package manager을 이용해도 된다. 이를 위해서는 프로젝트의 오른쪽 클릭 후에, Nuget Package Manage를 눌러서 explore 부분에 Microsoft.aspnet.signalR을 쳐도 된다.

패키지는 닷넷 프레임워크를 쓰는지, 닷넷 코어를 쓰는지를 잘 구분하고, 될 수 있으면 마이크로소프트에서 제공하는 코드를 사용하는게 좋다.

설치가 끝나면 References 부분에 Microsoft.AspNet.SignalR … 이 추가된 것을 확인할 수 있다.

이 SignalR을 사용하기 위해선 첫번째로, 이 웹 애플리케이션이 signalR을 사용할 것이라는걸 명시해줘야한다.

이를 위해선 app의 startup에 실행되는 cs파일에 hub를 register 하는 작업이 필요한데, 이를 위해서 webform의 global.asax(옛날 방식의 startup 파일) 혹은 Startup.cs 파일(owin middleware을 쓰는 방식)을 고쳐줘야 한다.

Global.asax.cs

protected void Application_Start()
{

   RouteTable.Routes.MapHubs();
}

startup.cs

public class Startup
  {
    public void Configuration(IAppBuilder app)
    {            
        app.MapSignalR();
    }
  }

Nuget Package Manager을 통해 깔리는 패키지 중 Owin도 있으니, Startup.cs파일을 새로 만들고, 기존의 Global.asax에 존재하는 내용을 옮겨 startup.cs파일만 남기는 방식도 있다.

어쨋든, 이렇게 hub이 register 되면, signalR을 script로 추가해 줄 수가 있게 되는데, 이는 master페이지의 asp:scriptReference, 혹은 App_Start에 존재하는 BundleConfig.cs에 추가해주거나, 만약 웹폼이 아닌 mvc같은 경우 view > shared 에 존재하는 layout의 하단에 razor태그로 scripts.render을 통해 src를 추가하는 곳에 가서 스크립트를 추가해주면 된다.

나같은 경우는 웹폼을 쓰기에,

                <asp:ScriptReference Path="~/Scripts/jquery.signalR-2.4.2.min.js" />
                <asp:ScriptReference Path="~/signalr/js" />

위의 내용을 scriptManger에 추가해줬다.

이 다음은, Hub를 inherit하는 클래스를 만들어주는 것인데, 프로젝트에 오른쪽클릭 > class 추가를 해준다.

추가된 클래스에 inherit를 추가해주기 위해서 C#에서는 : parentClass를 해주면 되는데, 이 때, namespace가 다르므로, Microsoft.AspNet.SignalR.Hub를 써주거나, 아니면 using 구문에 Microsoft.AspNet.SignalR을 추가해서 바로 : Hub를 해줘도 된다.
이런 Dependency 같은 문제인 경우는 Hub에 커서를 두고 Ctrl+. operator을 써서 자동으로 해결해도 된다.
이렇게 기존의 기능은 유지한채로 읽기 편하거나 간편화 시켜서 코드를 바꿔주는 것을 Refactoring이라고 부른다.


이렇게 셋업이 끝난 후에는, 앞서 말했던 데이터를 보내고 받는 방식을 구현해 내면 된다.
이는 웹 소켓과 같은 방식으로 진행되는데, 개념상으로 알아야 할 것은 다음과 같다.

  • 클라이언트 쪽 코드는 Javascript (Jquery)를 써서 작성한다.
  • 서버 쪽 코드는 C#을 써서 작성한다.
  • 클라이언트 쪽 코드는 IIFE (Immediately Invoked Function Expression) 을 사용하여 작성해, scope의 accessibility를 관리해주는 것이 좋다.
  • 서버쪽 코드는 Hub를 inherit 하는 클래스 안에 메소드를 생성해 진행되는데, 이 때, 서버 쪽 코드는 C#이므로 메소드의 이름은 PascalCase를 따른다. 이 메소드는 그대로 클라이언트에서 이용되는데, 이 때, 클라이언트는 자바스크립트이므로 camelCase를 따른다.
  • 시작은 클라이언트 코드에서, connection object를 만드는 것으로 시작하는데, generatedProxy를 쓰거나, 아니면 그냥 만들어도 된다.
  • 그렇게 만들어진 object 는 server, client와 같은 property가 있는데, object의 server의 C#에 쓴 메소드는 자바스크립트에서 camelCase로 이용할 수 있고, 이렇게 C#의 서버사이드로 넘어간 데이터는 javascript의 object의 client property의 메소드를 만들어주고, 이걸 C# 서버쪽에서 PascalCase로 이용함으로써, 다른 클라이언트들에게 보내줄 수 있다.
  • object 의 client property를 통해 만드는 메소드는 callback function을 통해 전달받은 data를 클라이언트 브라우저 상에서 이용할 수 있게 된다.

이 개념을 이용한 예제 코드는 다음과 같이 쓸 수 있게 된다.

(.net framework webform)
.master의 스크립트를 추가해주기 위해서, script manager을 바꾸거나, 아니면 끝 부분에 content placeholder을 배치하는 방법이 있다.  
Project.Master 
<asp:ContentPlaceHolder ID="ScriptHolder" runat="server">
</asp:ContentPlaceHolder>

index.aspx
<asp:Content ID="ScriptContent" ContentPlaceHolderID="ScriptHolder" runat="server">
    <script src="./Scripts/jquery.signalR-2.4.2.min.js" type="text/javascript"></script>
    <script src="./signalr/js" type="text/javascript"></script>
    <script src="./Scripts/SignalR.js" type="text/javascript"></script>
</asp:Content>

index.cshtml (core)
layout의 @RenderSection("Scripts", required: false)을 교체해주기 위해, 다음과 같이 작성한다.  
@section scripts {
	<script src="~/Scripts/jquery.signalR-2.2.0.min.js"></script>
	<script src="~/signalr/js"></script>
	<script src="~/Scripts/SignalR.js"></script>
}

js client side code
(function () { 
	// connection object made with generated proxy
	var myHub = $.connection.myHub;
	$.connection.hub.start()
	.done(function() {
		// data goes to server side
		myHub.server.announce("Connection Done");
	})
	.fail(function() {
		alert("Error connecting to signal");
	})

	// data taken by clients
	myHub.client.announce = function (message) {
		$("#chat-room").append(message);
	};
})();

C# server side code

using Microsoft.AspNet.SignalR;

namespace PackageName
{
	public class SessionHub : Hub
	{
		public void Announce(String data) 
		{
			// data goes to clients 
			Clients.All.Announce(data);
		}
	}	
}

이렇게 클라이언트 전체에 보내는 방식도 있겠지만, 당연하게도, 특정 유저, 그룹에 보내는 방식도 존재한다.


ANYMATE APP (1)

Project TEAMUP을 끝내기도 전에 만들고 싶은 앱이 생겼다.

이름은 ANYMATE.
최근에 자주 보이는 세션을 만들고 URL으로 접속하는 방식으로 구현해서, 세션을 만든 사람은 프로젝트 리더, 그 외에 들어오는 사람들은 프로젝트 맴버로 설정을 하는 식으로 일단 협업의 환경을 만드는 것이 기본적으로 제공되야 할 수 있는 것으로, 최근에 유튜버들이 하는 Gartic phone이나 aggie.io와 비슷하다고 보면 될 것 같다.

이걸 구현하는 방법은 찾아내질 못했는데, 아마 웹 소켓을 사용할 거라고 생각한다.

이런 기능을 가진 내가 봐왔던 웹사이트들은 다 항상 비슷한 특징이 있었는데, 로그인 기능이 기본적으로 제공되어 있다는 점.. (세션 내에서 유저를 각각 다르게 구분 지을 수 있게..) 그 외에 굳이 로그인을 안해도 될 경우에는 임시 이름이랑 태그를 지정해주는 경우가 태반이였다.

즉, 클라이언트가 웹사이트 URL을 통해 들어갈 때, Get request의 parameter 부분을 가지고 세션을 구분짓고, 그 특정 세션에 임시이름 + 태그 등을 이용해서 유니크한 유저를 넣어서 (wss를 통해 서버와 연결) 그 때부터 채팅을 하든 데이터를 나누든 하는 것 같다.

서버 입장에서는 그러면 get request를 받으면 request parameter 체크하고, 유저의 임시이름 + 태그가 방에 존재하나 안하나 체크? 해야 할 것 같고.. 없으면 wss 연결, 있으면 메세지를 보내야 할 것이다.

그런데 아무래도 URL 세션이다보니까 방 자체의 보안성은 없을 듯 싶다.
전문성으로 나아간다면 방을 잠구는 기능 등도 구현해야겠지 나중에.

글이 좀 중구난방이고 두서가 없지만 일기라고 생각하며 적겠다.

프레임워크는 node.js에다가 express 써서 소켓까지 한번에 구현하고 캔버스 기능들도 만드는게 솔직히 제일 좋아보인다.

실제로 aggie.io builtwith으로 보니깐 node.js + express더라.

그런데 나는 좀 요새 C#이 끌린다. 아무래도 비교적 최근에 JSP할 때 Java 했던 것도 있고 비슷하다보니깐.. 그리고 요 몇달간 웹 개발에는 ASP.NET CORE 쪽이 JSP를 재치고 인기가 1등이다.. OTL
Blazor 기능도 솔직히 매력적이라서 다 떄려치우고 C# 올인하면 더 유능할 것 같다.

그래서 ASP 입문차 이 웹사이트는 일단 처음에 ASP.NET FRAMEWORK로 webform으로 대충 만들어보고, mvc 그 후에 core로 넘어가서 직접 다른 클라우드 서버 환경이라던가 (linux 쓰는 AWS)에서 조금 더 싼 가격에 직접 돌려볼 수 있겠다.

일단 webform 입문으로 첫 화면으로 navigation과 설명 정도는 만들어 놓은 다음에 바로 SPA로 넘어갈 듯 싶다.

그렇게 크게 보면 세 가지 기능을 구현하고 싶다.


하나는 일단 프로젝트를 만드는건데, 이 부분이 쉽게 말하면 세션을 만드는 부분이 되겠다.
웹사이트에서 버튼을 눌러서 서버에서 주는 세션 생성도 되겠지만, 반대로 URL에 그냥 랜덤한 문자열을 집어넣어서 세션을 찾아들어가거나 아니면 없을 경우 만드는 식도 괜찮은 것 같다.

그런데 이 부분에서 고민해야 할 게, 세션이 없어져도 되냐는건데.. 예를 들어서 aggie.io는 세션 이름 들어갈 자리에 단어같은거 치면 다른 사람들 세션이 남아있는걸 자주 볼 수 있다.

이 프로젝트가 사실상 애니메이터들의 전문성을 존중해야 할 텐데, 기능은 그렇다 치더라도 만들던게 날라가는 휘발성 자료이면 솔직히 아무래도 backlash가 엄청 날 것 같긴 하다.

그러면 각 유저들의 프로젝트 내역들을 평생 저장하고, 그 프로젝트의 결과물을 또 저장하고, 하면 애니메이션의 특성상 용량이 어마어마해 질 것이다.

하여튼 프로젝트를 만들면 처음 만드는 세션일 경우, 그 사람이 프로젝트 리더가 될 것이다. 그리고 그 이후 사람들은 다 프로젝트 맴버가 되면.. 이래서 휘발성일 수 밖에 없나.. 아 머리아프다.

아니면 그냥 처음 시작 때 이름 정하는거랑 끝날 때 키 프레임 옮기고 넣고 하는거는 다 같이 정할 수 있게 해도 괜찮을 것 같다.

방장 시스템도 가능하고 방을 닫는 시스템도 가능하다면 가능하다.

내가 원하는 인터페이스는 2D로 화살표로 가고 싶은 키 프레임으로 가서 in-between을 추가하는거, 그리고 그걸 드래그해서 옮기는거 정도가 되는데, 여기까지가 프로젝트를 manage 하는 부분인 것 같다.
그리고 추가하면 좋은게 맴버들이 다루고 있는 캔버스의 preview를 가끔 업데이트해줘서 들어가서 확인 할 수 있는정도?


다른 멤버의 preview를 통해 들어가면, 거기서 같이 그리거나 할 수 있으면 좋을 것 같다. 당연히 이상한 사람이 있을 수 있으므로, 이 때도 관리하는 시스템이 필요할 듯 싶다.

일단은 기능이 중요하니까 이 부분은 나중에 좀 더 생각해보자..

Html5 Canvas를 이용해서 같이 그리는 방식이 될 거 같은데, 기본적으로 웹 소캣으로 연결된 유저들이 어떤 좌표에 어떤 색깔/어떤 도구 를 사용하냐를 볼 것 같다.

쉽게 말하면 wss로 클라이언트가 붓질을 할 때, 캔버스 상의 위치와 색깔을 받아서 서버로 data로 넘기면, 서버는 다른 클라이언트들에게 broadcast를 하고, 데이터를 받는 순간 클라이언트는 캔버스의 draw + update 함수를 실행 시켜서 같은 위치에 그림을 출력하는 걸 기능하면 될 것 같다.

위의 data만 쓰면 처음 들어갈 때 클라이언트의 canvas는 무조건 빈 상태니까, 처음 들어갈 때는 png파일이나 그 전 stroke 데이터를 받아와서 모든 작업들을 미리 준비해 놔야 할 것 같다.


그렇게 그려진 그림들은 preview에서 하나하나 화살표를 통해서 볼 수 있어져야 될 거고, fps를 정한다음에 합쳐서 export 한 후, anymate 웹사이트의 post 부분에 퍼블리쉬 할 지 정하면 좋을 것 같다.
유저에게서 직접적으로 파일을 넘겨받는거는 shell hacking을 염두하고 조심하면서 해야할 것이고.. 단지 위에 캔버스로 같이 그리는 부분에 애니메이션에 좋은 기능들을 어떻게 넣을지가 관건인 듯 싶다.

이렇게 대충 브레인스토밍은 해봤지만, 개념적으로는 조금 그림이 잡혀도 직접 하려니까 막막한 감이 없지않아 있다.
일단 하나하나씩 차근차근 해본다는 생각으로 다가가봐야 할 것 같다.

Rescheduling N400

N400 interview가 이미 스케쥴 된 상태이고,
a) 다른 지역으로,
b) 다른 날짜, 다른 요일로 옮기고 싶다면,

첫번째로는 change of address를 통해, 원래 인터뷰가 일어날 곳이였던 USCIS Office가 기존의 인터뷰를 취소한 후에, 새로운 지역의 USCIS office로 알려줄 시간을 줘야 한다.

두번째로는, 새로 인터뷰가 스케쥴되었을시에, 해당 지역의 office에 연락해 다시 reschedule을 진행하는 방법이 있다.

ASP.NET FRAMEWORK WEBFORM(1)

개념 정리

.NET Framework의 새로운 웹 어플리케이션 (C# 솔루션)을 만들때, 유저는 다음의 3가지 핵심 참조 중 1개 이상을 선택 할 수 있다.

  • Web Forms
  • MVC
  • Web API

이번에 다뤄 볼 내용은 Web Form 이다.


처음 템플랫 생성 구간에서 클라우드를 사용할 것인지, docker을 사용할 것인지, 인증 변경을 할 것인지를 정할 수 있다.
인증 변경을 개별 사용자 계정으로 하게 되면, 로그인, 로그아웃, 회원가입의 기능을 기본으로 제공해 준다.


닷넷 개발에 적합한 환경인 Microsoft Visual Studio의 인터페이스를 알면 닷넷 프레임워크의 이용이 더 수월해진다.

기본적으로는 보기(V)의 솔루션탐색기를 켜줌으로써, 오른쪽 화면에 어떤 파일 시스템으로 솔루션이 이루어졌는지 확인해야한다.

C#에서는 하나의 솔루션 (자바의 패키지같은 개념)에 여러개의 프로젝트가 들어갈 수 있다.

프로젝트의 하위 폴더들로는 App_Data, App_Start, Model, Controller, View, Content, 등등 여러가지 폴더들이 있을 수 있는데 이는 처음 탬플렛을 뭐로 했냐에 따라 정해진다.
하지만 기본적으로, Empty Template을 쓴다고 가정할 경우, 이 폴더들은 지워져도 무관하다.

여기서 알아야 할 중요한 개념은 각 프로젝트의 Root는 웹사이트의 Root라는 것이며, 이 프로젝트의 하위 파일로는 웹 서버가 클라이언트에게 전해 줄 웹 페이지의 파일들이 자리잡게 될 것이다.

즉, car.com 이라는 웹 사이트가 있을 때, 프로젝트의 Root가 car.com의 Root과 같다는 뜻은 프로젝트의 하위 파일로써 index.html, about.aspx등의 파일이 오게 될 경우,
car.com/,
car.com/about.aspx
식의 요청이 가능하며, 그에 맞는 웹 페이지를 받아온다는 것이다.

이런 파일의 추가는 프로젝트에 오른쪽 클릭, 추가(D)를 이용해 할 수 있다 (혹은 CTRL+SHIFT+A 단축키).
이 때 확인할 수 있는 내용은 확장자에 대한 것으로, aspx는 webform을 위한 확장자, master은 마스터 페이지를 위한 확장자, cshtml은 mvc 5의 확장자라는 사실을 간략하게 알 수 있다.

이렇게 생성된 html과 같은 파일은 비쥬얼 스튜디오 내부에서는 VS code와는 다르게 emmet과 같은 플러그인을 제공하지 않아 직접 태그를 작성해줘야 한다는 불편함이 있다.

단축키 또한 조금 다른데 CTRL+D로 같은 구문을 선택 할 수 있는 vsc 명령어는 Shift+Alt+. 으로 쓸 수 있다.
그 외에도, 자동 indentation은 CTRL+K+D, 코멘트 처리는 CTRL+K+C, 코멘트 처리해제는 CTRL+K+U등이 있다.

웹 페이지를 작성한 후에는, 오른쪽 솔루션 브라우저에서 해당 파일에 오른쪽 클릭 후 브라우저에서 보기를 하면 된다.
IDE로써 비쥬얼 스튜디오의 좋은 점은 느리지만 이러한 컴파일 (html말고도 컴파일이 필요한 코드를) 기능을 수행한다는 점에 있다.
혹은, 프로젝트에 오른쪽 클릭 후 시작 프로젝트로 설정을 한 후, F5나 CTRL+F5(디버깅 x)를 통해 컴파일 후 실행도 가능하다.

이렇게 프로젝트의 내부는 웹 애플리케이션의 루트와 같다는 것을 확인한 순간부터, HTML, CSS, JAVASCRIPT를 통한 웹 프로그래밍이 가능해진다.
하지만 ASP의 고유의 기능을 쓰고 싶은 우리들의 입장에서는 .aspx (asp extended)파일의 쓰임새를 알 필요가 있다.

즉 아까전에 프로젝트에 돌아가서 CTRL+SHIFT+A를 통해 새 항목을 만들고 WEBFORM, 즉 .aspx의 파일을 만들어 내용을 확인해보자.


CLASSIC ASP, ASP.NET, HTML Server Controls

html의 index처럼, JSP나 ASPX과 같은 파일들은 기본적으로 Default라는 이름을 사용한다.
Default.aspx라는 파일을 만들면 Default.aspx.cs와 Default.aspx.designer.cs 라는 두가지 파일이 추가적으로 만들어진 것을 확인할 수 있다.

Default.aspx라는 파일 자체는 User Interface의 영역으로, 실제적으로 유저가 쓰는 html태그라던가의 부분을 담고 있다.
Default.aspx.cs는 Code-behind file로 불리고, UI의 코드를 담당하는 C#파일이다.
Default.aspx.designer.cs는 자동으로 생성되는 코드가 들어가는, 굳이 따지자면 건들 필요가 없는 파일이다.

이러한 솔루션의 파일 구조는 WPF와 Window Form과 동일하다.

aspx는 asp extended를 뜻하고, 실제로 html의 파일을 aspx 확장자로 바꾸기만 해도 html의 페이지가 보인다는 걸 알 수 있다.
asp 파일 자체가 원래 이러한 특성을 가지고 있었는데, classic asp와 aspx의 차이는 가독성의 차이가 크다고 볼 수 있다.
asp는 코드블럭이 실행이 되는 그 자리에만 있을 수 있는 것에 비해, aspx는 Server Controls (서버가 알아들을 수 있는 태그) 라는 것을 이용해 가독성을 늘렸다.

서버 컨트롤에는 다음과 같은 서버 컨트롤들이 존재한다.

  • HTML Server Controls - Traditional HTML tags
  • Web Server Controls - New ASP.NET tags
  • Validation Server Controls - For input validation

aspx 파일의 구조를 보면 기본적인 html에 <% %>과 같은 inline server code, 그리고 그 안에는 Page 지시문이 존재한다.
이 때, 제일 윗줄에 생성된 Page 지시문을 확인하면, CodeBehind 파일을 AutoEventWireup을 통해 같이 쓰겠다는 것을 적어놓았는데, 이러한 CodeBehind의 파일로 넘어가기 위해선 F7 키를 누르면 된다.

또한 코드를 잘 살펴보면 HTML 태그들에 runat=”server” attribute가 존재하는 것을 확인할 수 있는데, 이는 일반적으로 aspx페이지 자체가 서버에서 작동하는, 즉 기존의 static html과는 다르게 브라우저에게 넘겨지기 전에 서버 쪽에서의 preprocess 단계가 존재한다는 것을 의미한다.
HTML 자체를 코딩하는 것이 아닌, aspx라는 파일을 server 사이드에서 compile, execute를 하고 나서, HTML을 보내준다는 의미이다.
이처럼 HTML의 태그에 attribute 특성으로 runat=”server” 이 붙는 컨트롤들을 HTML Server Controls 이라 부른다. HTML Server Controls의 특징이라 할 수 있는 점은, 모든 HTML Server Controls는 <form runat="server"> 태그 안에 존재해야 한다는 것이다.
이가 뜻하는 바는, form은 서버에서 process 돼야 하며, form 안에 존재하는 컨트롤들은 서버 스크립트에 의해 access 될 수 있음을 뜻한다.

저번 포스팅에서 다뤘던 Web Pages와는 다르게, Web Form자체는 Event-driven architecture을 따르고 있다.
모든 페이지는 System.Web.UI 라는 namespace에 존재하는 Page라는 클래스의 derivatives이며, Page는 Page_Load라는 이벤트가 실행될 때, 자체적으로 가지고 있는 properties를 이용, 페이지를 render해주는 역할을 하는 것이다.
head에 들어있는 runat attribute는 이러한 html의 메타데이터를 포함하고 있는 Page의 properties들에 추가적인 내용을 넣어줌으로써, Page_Load같은 이벤트에 맞춰 이벤트 핸들러를 설정해 주는 것이다.

<script runat="server">
Sub Page_Load
link1.HRef="http://www.w3schools.com"
End Sub
</script>
<html>
<body>
<form runat="server">
<a id="link1" runat="server">Visit W3Schools!</a>
</form>
</body>
</html>

Web Server Controls, Web Form의 디자인 모드

웹 폼에 존재하는 단연코 가장 유니크한 특징은 Web Server Controls ( 컴포넌트 ) 의 존재이다.
이 특징은 윈도우 폼으로부터 확장된 것이며, 컨트롤은 비쥬얼 스튜디오 보기>도구 상자(CTRL+ALT+X)에서 찾을 수 있다.

도구 상자에서 제공하는 컨트롤들은 기존의 HTML태그 등으로 구현해야했던 코드 혹은 더 복잡한 요소를 구현해준다.
이 각각의 컨트롤들은 ID나 Name같은 attribute를 통해 access 할 수 있으며, runat attribute를 통해 서버 사이드에서 동작한다는 것을 확인할 수 있다.

<asp:control_name id="some_id" runat="server"> </asp:control_name>

<script runat="server">
Sub submit(Source As Object, e As EventArgs)
button1.Text="You clicked me!"
End Sub
</script>

<html>
<body>

<form runat="server">
<asp:Button id="button1" Text="Click me!"
runat="server" OnClick="submit"/>
</form>

</body>
</html>

위에 존재하는 웹 서버 컨트롤, 즉 , <asp:/>의 형태를 띈 컴포넌트는 버튼이며, OnClick 시에 submit이라는 Subroutine을 실행한다는 사실을 알 수 있다.  

이는 흔히 아는 XML태그의 syntax과 동일하며, jsp에서는 jsp:actions 라는 비슷하게 생긴 것이 존재하지만, jsp가 javaBean을 사용해 객체를 받아와 값을 넣고 결과값을 받아오는 것에 대부분 쓰이기 때문에 인지를 못하지만, jsp:plugin이라는 action자체는 asp와 굉장히 비슷하다고 볼 수 있다.

예시 )
<jsp:plugin type = "applet" codebase = "dirname" code = "MyApplet.class"
   width = "60" height = "80">
   <jsp:param name = "fontcolor" value = "red" />
   <jsp:param name = "background" value = "black" />
 
   <jsp:fallback>
      Unable to initialize Java Plugin
   </jsp:fallback>
 
</jsp:plugin>

control들은 id나 name을 가지고 있기에, event listener을 정해줄 수도 있다.
예를 들어서, 버튼이라는 컨트롤을 생성 했을때, 디자인 모드에서는 더블클릭을 하거나, 소스 코드에서는 기존의 html에 존재하는 OnClick = ““을 추가해줌으로써, 이벤트 리쓰너를 추가해줄 수 있다.
그리고 나서의 이벤트는 f7을 눌러 코드 비하인드로 간 뒤, sender (this 와 같은 개념) 과 event e에 맞춰서 이벤트 핸들러를 작성 해 줄 수 있다.

한가지 특이한 점은, Javascript나 Jquery로 구현한 이벤트 리쓰너, 핸들러인 경우는 새로고침이 없이 이 벤트가 진행되는 것에 비해서, 웹 폼의 이벤트 핸들러는 페이지의 새로고침 (Page_Load) 을 유도해 깜빡임이 보이게 된다.

if (!Page.IsPostBack) 
{
	//temp 는 controller의 ID이다.
	temp.Border = System.Drawing.Color.Red;
} 

위의 컨디션은 Page_Load에 넣어주게 되면, 첫 Load때 동작한다.

또한, 이렇게 stateless한 웹 페이지에서 새로고침을 유도하기에, 숨겨진 state들을 Hidden_State로써 html에 넣어야 한다는 단점이 있다.
이는 뒤에 자세하게 다뤄질 것이다.

WEBFORM은 이렇게 코딩하기가 쉽다는 장점과 대신 코드가 더러워진다는 단점이 같이 존재한다.


Validation Server Controls, User input check

Validation Server Controls는 유저의 인풋을 체크한다.
그리고, validation이 실패할 경우, 유저에게 에러 메세지를 출력해준다.
기본적으로 Page Validation은 버튼과 같은 코드가 클릭 되었을시 실행이 되는데, CausesValidation = 을 false로 바꿨을 시, 실행되지 않는다.

<asp:control_name id="some_id" runat="server"/>

<html>
<body>

<form runat="server">
<p>Enter a number from 1 to 100:
<asp:TextBox id="tbox1" runat="server" />
<br /><br />
<asp:Button Text="Submit" runat="server" />
</p>

<p>
<asp:RangeValidator
ControlToValidate="tbox1"
MinimumValue="1"
MaximumValue="100"
Type="Integer"
Text="The value must be from 1 to 100!"
runat="server" />
</p>
</form>

</body>
</html>

기존의 HTML 태그도 minlength, maxlength같은 것이 존재하나, 클라이언트쪽에서 체크하는 것이였다.
하지만 validation controls같은 경우는 서버쪽에서 체크를 하기에 다르다고 할 수 있겠다.

이는 클라이언트에서 html을 바꿔서 잘못된 정보를 넣을 수 없다는 것을 뜻하며, 개인적으로는 조금 더 보안상 안전하다고 고려된다.


Event Driven Architecture

Event Driven Architecture을 구성하는 요소들은 미리 정해진 Events, 그리고 그 이벤트를 감지하는 Event Listeners, 그리고 감지된 이벤트에 따라 특정 기능을 수행하는 Event Handlers가 있다.

이벤트의 유무는 시간축을 하나 더 제공하여, 기존의 정적이던 웹 페이지를 동적으로 바꿔준다고 볼 수 있겠다.

저번 Javascript DOM 포스팅 에서 보았듯, 시간축의 유무가 변화의 유무와 같기 때문이다.

ASP.NET의 event의 종류는 HTML DOM events를 생각하면 편하다.
HTML의 button과 같은 태그에 onclick같은 event handler이 기본적으로 쓰인다는 점, 자바스크립트의 custom event handler 함수를 넣을 수 있다는 점, onkeypress등으로 기본과 다른 handler을 쓸 수 있다는 점 등이 있다.
즉, ASP에는 control events라 하는 각 control 마다 기본적으로 많이 쓰이는 default event가 있다.
그리고, OnCommand라는 attribute를 사용할 시, 기존에 없는 이벤트 핸들러를 만들어 넣는 것도 가능하다.
특정 체크박스의 체크가 바뀌거나 텍스트가 바뀌는 것도 감지할 수 있으며, 마지막으로 기본적으로 쓰이는 defeault events외에도, 다른 attributes를 통해 event handlers를 넣을 수 있다.

이러한 control events는 AutoPostBack property를 true로 만드냐 false로 만드냐에 따라서 postback events로 만들지 non-postback events로 만들지 정할 수 있다.
postback이란 이벤트가 실행되면 바로 서버로 포스트가 되는 버튼 클릭과 같은 것이 있다.
non-postback은 바로 서버로 포스트 되지 않는 checkbox.CheckedChanged 나 TextChanged과 같은 것이 있다.

User Action에 의해 생기는 events외에도 application, session, 그리고 page가 진행되는 과정에서 생기는 events 또한 존재한다.

  • Application_Start
  • Application_End
  • Session_Start
  • Session_End
  • Data_Binding - control binds to a data source
  • Disposed - page or control is released
  • Error - unhandled exception is thrown
  • Init - page or control is initialized
  • Load - page or control is loaded
  • PreRender - page or control is rendered
  • Unload - page or control is unloaded from memory

C#이나 VB script로 만드는 실제 이벤트 핸들러의 구조는 ?

이벤트 핸들러의 구조: 
private void EventName (object sender, EventArgs e)

이벤트의 구조는 1) object raising the event, 2) event argument를 받아와 void를 return 하는 구조로 되어있다.

이 함수 안에는 어떤 태그의 텍스트를 바꾼다던가, 스타일을 바꾼다던가, 받아온 데이터를 토대로 백엔드와 상호작용 한다던가 등의 내용이 들어간다.


ASP.NET의 유저의 액션없는 대표적인 이벤트는 Page_Load 이벤트이다.

이름에서 알 수 있듯, event 자체가 유저의 액션 없이 실행되기에, object reference나 event arguments 가 없다!

<script runat="server">
Sub Page_Load
lbl1.Text="The date and time is " & now()
End Sub
</script>

<html>
<body>
<form runat="server">
<h3><asp:label id="lbl1" runat="server" /></h3>
</form>
</body>
</html>

Page에는 IsPostBack이라는 property가 존재하며, 이는 처음에 페이지가 로드될 때, 한번만 true를 반환한다.

<script runat="server">
Sub Page_Load
if Not Page.IsPostBack then
  lbl1.Text="The date and time is " & now()
end if
End Sub

Sub submit(s As Object, e As EventArgs)
lbl2.Text="Hello World!"
End Sub
</script>

<html>
<body>
<form runat="server">
<h3><asp:label id="lbl1" runat="server" /></h3>
<h3><asp:label id="lbl2" runat="server" /></h3>
<asp:button text="Submit" onclick="submit" runat="server" />
</form>
</body>
</html>

이렇게 위에서 다룬 서버 컨트롤들은 <form runat="server"> 태그 안에 등장해야한다.
그리고 이 <form runat="server"> 태그는 .aspx 페이지에서 한번만 등장해야한다!!

HTML의 form 태그랑 다르지 않기에, action attribute로 어디로 form-data를 보내줄지 정해주고, method attribute로 post request를 보낼지 get request를 보낼지 정해줄 수 있다.
만약, action attribute를 정해주지 않는다면, form-data는 자동으로 지금 페이지에 form-data를 보내고, method는 자동으로 post가 된다.
또한 .aspx 페이지에서 name과 id를 정해두지 않았을시, 이는 ASP.NET framework에 의해 자동으로 지정된다.

이렇게 정해진 name 과 id로 상호작용을 한다고 했을 때, 유저가 옳은 정보를 보낼 시에는 문제가 없으나, 잘못된 정보를 보냈다고 생각해보자.
ASP의 단점이라고 할 수 있는 각 request마다 page_load를 한다는 특징 때문에 사실상 form안에 유저가 기입한 내용들은 깜박거림 현상과 함께 사라져야 정상이다.
하지만 앞에서 설명한 것 과 같이, 이 깜박거림과 함께 사라지는 내용을 지키기 위해서, ASP.NET은 Hidden_State를 html에 넣어 보관한다.

그리고 이 Hidden_State를 이 상황에선 ViewState라고 부른다.
ViewState는 서버로 요청을 했을 시의 페이지의 상태를 나타낸다.
그리고 이 기능은 ASP.NET WebForm에서 그냥 처리해주는 것으로, 개발자가 따로 viewstate를 정해주거나 할 필요는 없다.
하지만 이는 당연하게도 성능의 저하로 이어지니, 개발자가 원한다면 그 페이지의 ViewState는 해제할 수가 있다.

해제를 원할 시, <%@ Page EnableViewState="false" %> 라는 디렉티브를 문서의 최상단에 놓으면 된다.  

Web Pages에서는 데이터를 import 할 때, .txt, .xml, .csv 포맷을 지닌 파일인 Flat files는 App_Data에 넣고 C#상의 함수인 File.ReadAllLines()를 이용해서 Array의 형태로 받았었다.

Web Form에서는 이렇게 받아온 데이터의 Collections를 몇몇 components의 요소로 쓸 수 있는 Data Binding이라는 것을 제공한다.

또한, XML 파일 같은 경우, 사용할 수 있는 Collection의 형태로 바꿔서 이용될 수 있도록 한다.

  • asp:RadioButtonList
  • asp:CheckBoxList
  • asp:DropDownList
  • asp:Listbox

이름에서로부터 알 수 있듯이, 리스트의 요소들을 보통은 하나하나 추가해줘야 하는 컴포넌트들이라는 사실을 알 수 있다.

하지만 Data Binding은 콜렉션 타입을 이용해 한번에 이를 채워줄 수 있다는 장점이 있다.


ArrayList는 하나의 데이터 타입으로 이루어진 collection object이다.
아이템을 넣기 위해서는 Add() 메소드를 쓰며, 이는 자바와 동일하다고 볼 수 있다.

기존 ArrayList와 동일하게 처음 initial size가 존재하며, Add()는 amortized constant time으로 사이즈를 일정 수준 증가시키며 아이템을 넣는, 다이나믹한 구조를 가지고 있다.
이렇게 쓸 데 없이 존재하는 빈 공간을 없애기 위해서, TrimToSize() 라는 메소드가 존재한다.
이는 맨 처음 사이즈 16으로 만들어진 ArrayList를 들어있는 엘러먼트의 양만큼의 사이즈로 줄여주는 것이다.
또한, Sort()나 Reverse()와 같은 자바에서는 Collections에 static 메소드로 존재할 법한 메소드도 따로 인스턴스 메소드로 존재한다.

VB script subroutine, ArrayList initialization
<script runat="server">
Sub Page_Load
...
dim arr = New ArrayList
arr.Add("Male")
arr.Add("Female")
arr.TrimToSize()
arr.Sort()
...
end sub
</script>

이렇게 만들어진 콜렉션은 데이터바인딩에 이용되는데, 이는 간편하게도 등의 id를 이용하고, 해당 컴포넌트의 DataSource property를 세팅해주고 DataBind() 메소드를 실행시킴으로써 데이터바인딩이 이루어지게 된다.

arr.Sort()
...
id.DataSource=arr
id.DataBind()
...
<form runat="server">
<asp:RadioButtonList id="id" runat="server">
</form>

하나 명심해야 할 점은 RadioButton의 Text가 곧 서버에게 넘어갈 Value라는 점이다.
이를 다르게 설정하고 싶을 경우엔 key-map associate을 쓰는 mapping을 쓰는 hashtable object를 써야한다.


Hashtable은 key/value의 pair로 이루어진 타입으로 이루어진 collection object이다.
아이템을 넣기 위해서는 Add() 메소드를 쓰며, 이는 자바의 put과 동일하다고 볼 수 있다.

이렇게 만들어진 콜렉션은 데이터바인딩에 이용되는데, 이는 간편하게도 등의 id를 이용하고, 해당 컴포넌트의 DataSource property를 세팅해주고 DataBind() 메소드를 실행시킴으로써 데이터바인딩이 이루어지게 된다.

<script runat="server">
sub Page_Load
if Not Page.IsPostBack then
  dim map=New Hashtable
  map.Add("N","Norway")
  map.Add("S","Sweden")
  map.Add("F","France")
  map.Add("I","Italy")
  id.DataSource=mycountries
  id.DataValueField="Key"
  id.DataTextField="Value"
  id.DataBind()
end if
end sub
</script>
...
sub routine(s as Object, e as EventArgs)
lbl1.text="result is " & id.SelectedItem.Text
end sub
...
<form runat="server">
<asp:RadioButtonList id="id" runat="server" AutoPostBack="True" onSelectedIndexChanged="routine" />
<asp:label id="lbl1" runat="server" />
</form>

하나 명심해야 할 점은 HashTable은 기존의 Hashmap과 동일하게 sort되어있지 않다는 것이다.
SortedList과 같은 Object를 쓰게 될 경우 정렬이 되어있는 리스트를 사용할 수 있다.


SortedList는 key/value의 pair로 이루어진 타입으로 이루어진 collection object이다.
SortedList는 키의 정렬을 유지하며, 이는 자바의 TreeMap과 유사하다.
아이템을 넣기 위해서는 Add() 메소드를 쓰며, 이는 자바의 put과 동일하다고 볼 수 있다.
하지만, ArrayList의 특징을 물려받았기에, 처음 initialization 시의 크기는 정해져 있고, 요소의 양만큼 사이즈를 줄이는 것 또한 TrimToSize()를 통해 가능하다.

이렇게 만들어진 콜렉션은 데이터바인딩에 이용되는데, 이는 간편하게도 등의 id를 이용하고, 해당 컴포넌트의 DataSource property를 세팅해주고 DataBind() 메소드를 실행시킴으로써 데이터바인딩이 이루어지게 된다.

그리고 이 방법은 HashTable과 완전히 동일하다.


XML 파일은 HTML과 비슷한 태그를 이용해 문서의 속성 혹은 규칙을 정의하는 파일이라 생각하면 된다.
그리고 당연하게도 이런 속성이 들어있는 오브젝트의 콜렉션 형태도 가능하다. 왜냐하면 같은 태그가 쓰이기 때문이다.
이러한 DataSet 오브젝트는 System.Data라는 namespace를 import 할 시 사용할 수 있게 된다.
aspx의 razor syntax를 이용할 시, 다음과 같이 import 할 수 있다.

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

그리고, XML파일은 DataSet의 오브젝트로 load 될 수 있다. 이 때, ReadXml()이라는 메소드가 사용된다.

이렇게 만들어진 콜렉션은 데이터바인딩에 이용되는데, 이는 간편하게도 등의 id를 이용하고, 해당 컴포넌트의 DataSource property를 세팅해주고 DataBind() 메소드를 실행시킴으로써 데이터바인딩이 이루어지게 된다.

<script runat="server">
sub Page_Load
if not Page.isPostBack then
   dim xml = New Dataset
   xml.ReadXml(MapPath("cars.xml"))
   id.DataSource=xml
   id.DataValueField="value"
   id.DataTextField="text"
   id.DataBind()   
end if
end sub
</script>

이런 식으로 asp:RadioButtonList, asp:CheckBoxList, asp:DropDownList, asp:Listbox 같은 컨트롤러에 데이터가 요소로써 이용될 수도 있겠지만, 사실 데이터 베이스나 데이터 테이블을 들고 오는 경우에는 테이블의 요소로 쓰인다던가 하는 경우도 있을 것이다.

이는 jsp에서의 doTag()와 비슷하다.
JSP에서는 데이터베이스에서 데이터를 콜렉션 형태로 받을 시에, 각각 데이터를 iterate하며 getJspContext().setAttribute() 를 통해 jsp파일에서 쓰일 수 있는 attribute를 설정해 줄 수 있고, 각 요소마다 getJspBody.invoke()를 해줌으로써 여러개의 요소들이 각각 표현될 수 있는것이다.

<temp:list query="${sent-attribute}">
<tr><td>${attribute1}</td><td>${attribute2}</td></tr>
</temp:list>

위와 같은 xml태그를 이용할 시에, *.tld라는 tag library descriptor이라는 파일에 xml 파일이 어떠한 서블릿을 이용하는지를 구성해줘야하며, 서블릿 내부에서의 invoke()를 통해 attribute1, attribute2가 날아올 때마다, 요소가 추가된다는 사실을 알 수 있다.

asp에선 xml파일을 컬렉션 형태로 받는 법은 이전 섹션에서 배웠다.
System.Data라는 namespace를 import 해준 뒤, page_load 이벤트 시에, ReadXml()을 통해 New DataSet의 인스턴스를 만드는것이다.

그럼 이렇게 컬렉션의 형태로 있는 xml 파일을 DataSet이라는 오브젝트로 얻을 수 있는데, 그렇게 얻어진 오브젝트를 Repeater이라는 asp control에 이용 하기만 하면 된다.
바로 Repeater control의 id의 DataSource로 지정하고, DataBind()를 해주면 된다.

Control이라고 했으니, 당연히 기존 asp control의 모양새를 따른다.
이 컨트롤의 특징은 HeaderTemplate, ItemTemplate, FooterTemplate으로 이루어져 있다는 점이며,
HeaderTemplate, FooterTemplate은 ItemTemplate 이전, 이후에 한번 실행된다.
ItemTemplate은 record 하나 당 repeat 된다는 특성을 가지고 있어, 테이블을 예시로 들 경우, Header에서 table 해더 태그를 셋업해주고, Item에서 tr 태그의 아이템을 넣어주고, Footer에서 태그를 닫아주는 형태로 생각 할 수 있다.
이 기본적인 형태에서 이나 을 ItemTemplate 과 FooterTemplate 사이에 넣어 줄 수 있는데, 이름에서 알 수 있듯, 한번씩 번갈아 가면서 나올 item, 그리고 매번 아이템 이후에 자동적으로 넣어줄 separator을 정해주는 템플릿이다.

<form runat="server">
...
<asp:Repeater id="id" runat="server">

<HeaderTemplate> ... </HeaderTemplate>
<ItemTemplate> 
...
<td> <%#Container.DataItem("fieldname")%></td>
...
</ItemTemplate>
<FooterTemplate> ... </FooterTemplate>

</asp:Repeater>
...
</form>

Repeater를 테이블의 형태안에 넣어준다고 할 때 <table>, <tr>, <td> 등 반복적으로 써야 하는 태그들이 있는데, 이걸 테이블용으로 따로 컨트롤로 만든 것이 있는데 이를 DataList라고 한다.

DataList의 구조는 완벽히 Repeater과 동일하다.
하지만 그저 테이블의 태그 부분만 뺀 것이다.


ASP.NET framework에는 ADO.NET이라는 데이터베이스를 이용하는데 도움이 되는 것이 있다. 이에서도 데이터베이스에서 데이터를 액세스 하는 것은 모든 데이터베이스와 동일한 과정으로 진행된다.

처음에는 데이터베이스와 연결을 해야하고, 이를 위해 SQL 데이터베이스의 드라이버를 import 해줘야 한다.

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

<script runat="server">
sub Page_Load
dim dbconn
dbconn=New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;
data source=" & server.mappath("northwind.mdb"))
dbconn.Open()
end sub
</script>

위 예제에서는 OleDb라는 드라이버를 이용, dbconn이라는 connection object를 만들어 냈다. 이 connection은 connection string을 통해 sql 쿼리를 받아들이는 하나의 인스턴스가 된 것이며, DB가 켜져있는 동안은 유효하다.

이렇게 dbconn을 open 한 뒤에는, query string을 넣어주고, execute를 진행하면 된다.

그리고 이렇게 받아온 query의 결과는 DataSet과 동등하게 이용될 수 있다.
Repeater, DataList과 같은 Control의 아이디를 이용, DataSource를 db result로 정하고, DataBind()를 진행해 후에 ItemTemplate 에서 Container.DataItem() 메소드를 통해 출력할 수 있게 되는 것이다.

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

<script runat="server">
sub Page_Load
dim dbconn,sql,dbcomm,dbread
dbconn=New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;
data source=" & server.mappath("northwind.mdb"))
dbconn.Open()
sql="SELECT * FROM customers"
dbcomm=New OleDbCommand(sql,dbconn)
dbread=dbcomm.ExecuteReader()

customers.DataSource=dbread
customers.DataBind()

dbread.Close()
dbconn.Close()
end sub
</script>

하나의 웹 프로젝트를 진행할 때, 웹 사이트의 웹 페이지들에게서 중요한 특징 중 하나는 통일성 이여야 한다.

이 통일성을 위해서, 레이 아웃을 짜고, header, footer등을 태그 디렉티브를 통해 들고 오는 등, 각각 프레임워크들은 여러가지 방식으로 통일성을 유지한다.

ASP.NET 웹 폼에서의 레이아웃은 Master Page라는 것을 통해 쉽게 구현가능하다.
이는 웹 폼으로 프로젝트를 만들 시에, 기본으로 제공되어 있는 Site.Master 파일 또는 Site.Mobile.Master이 그 역할을 해준다.

Master Page는 *.Master이라는 확장자를 가진다.
하지만 들어있는 컨텐츠의 문법 자체는 정확히 *.aspx의 그것과 동일하다.
(html에 asp control, in-line server script, 그리고 VB script를 끼얹은 형태이다.)

Master page의 독특한 특징이라고 할 수 있는 것은 첫번째로, @ Master directive를 첫 줄에 정의해야 한다는 것이다.

<%@ Master %>
외에도 추가적인 attribute를 정의할 수 있다.  

또한, 레이아웃 정해놓고 지정된 위치에 다른 컨텐츠 내용을 다른 aspx파일에서 받아오는 형태이므로, ContentPlaceHolder이라는 Placeholder tag를 사용한다.
이는 웹 서버 컨트롤과 조금 다른게, form의 내부에 있을 필요도 없고, 독자적으로 html의 한 부분을 자리하고 있는 “태그”이다.

<%@ Master %>

<html>
<body>
<h1>Standard Header From Masterpage</h1>
<asp:ContentPlaceHolder id="CPH1" runat="server">
</asp:ContentPlaceHolder>
</body>
</html>

이렇게 레이아웃이 생성 되면, Content Page는 그 레이아웃에 컨텐츠를 넣어 줄 수가 있게 된다.
Content page의 독특한 특징은 첫 줄에 @ Page directive를 정의해야 한다는 점이다.
그리고 이 @ Page directive는 MasterPageFile을 정의함으로써, 레이아웃을 이용할 수 있게 된다.

<%@ Page MasterPageFile="master1.master" %>

또한, 정해진 레이아웃의 지정된 위치에 이 파일에 정해진 컨텐츠 내용을 넣는 형태이므로, Content라는 tag를 사용한다.
이 때, Content tag에는 ContentPlaceHolderId 라는 reference attribute가 필요하다. 어느 위치에 들어가는지 정의해야 하기 때문이다.
이 태그 또한 웹 서버 컨트롤과 조금 다른게, form의 내부에 있을 필요도 없고, 독자적으로 html의 한 부분을 자리하고 있는 “태그”이다.
내부 컨텐츠는 기존의 aspx와 동일하며, Form tag 내부의 Web Server Controls 또한 이용 가능하다.

<%@ Page MasterPageFile="master1.master" %>

<asp:Content ContentPlaceHolderId="CPH1" runat="server">
  <h2>Individual Content</h2>
  <p>Paragraph 1</p>
  <p>Paragraph 2</p>
</asp:Content>