'.NET/ASP.NET'에 해당되는 글 34건

  1. 2018.11.08 [ASP.NET Core] 최신 버전에서 React+Redux+Javascript+TypeScript 를 사용하는 방법
  2. 2016.11.16 [ASP.NET Core] Middleware, IHttpModule과 IHttpHandler 마이그레이션
  3. 2016.11.15 [ASP.NET Core] IControllerFactory 설정 및 마이그레이션
  4. 2016.08.09 [ASP.NET] Session state has created a session id, but cannot save it because the response was already flushed
  5. 2016.02.28 ASP.NET WebForm 에서 Dependency Injection 사용하는 방법
  6. 2013.05.21 memcached, 분산 캐시를 이용하여 분산 Session 성능 향상 (2/2)
  7. 2013.05.20 memcached, 분산 캐시를 이용하여 분산 Session 성능 향상 (1/2)
  8. 2010.07.07 ASP.NET 의 WebMatrix & Razor 신 기술 소개 (1)
  9. 2010.03.12 UMC 와 함께하는 ASP.NET 해킹하기 #1
  10. 2009.10.19 ASP.NET Web Test 중 Favicon 다운로드 문제
  11. 2009.03.02 ASP.NET 서버 모델의 성능에 대한 고찰 [2] (2)
  12. 2009.03.02 ASP.NET 서버 모델의 성능에 대한 고찰 [1] (2)
  13. 2008.10.27 Microsoft Chart Controls Released
  14. 2008.08.27 외부 라이브러리에서 Javascript 인텔리센스 활성화 하기
  15. 2008.08.07 Virtual Earth Server Controls 소개
  16. 2008.07.04 Umc.Core.Web.HttpCompression 소스 코드 공개
  17. 2008.06.20 실전 HTTP Compression 2 - 리소스 압축을 통해 웹 사이트에 진정한 날개를..
  18. 2008.06.20 실전 HTTP Compression 1 - HTTP 압축의 위험과 해결 방안
  19. 2008.06.16 실전 ASP.NET Session [4] - 세션상태 마이그레이션
  20. 2008.06.09 실전 ASP.NET Session [3] - 다양한 세션 관리 방법
  21. 2008.06.07 실전 ASP.NET Session [2] - 상태관리의 종류
  22. 2008.05.25 실전 ASP.NET Session [1] - 쿠키를 이용한 상태관리와 위험성
  23. 2008.04.27 Custom Config 에 Intellisence 날개를 달자
  24. 2008.04.13 RewritePath 와 포스트백(Postback) 문제와 해결 방법
  25. 2008.04.06 ASPX 확장명 변경과 Visual Studio 에 적용하기
  26. 2007.11.10 웹사이트를 웹 응용 프로그램으로 변환하기 (2)
  27. 2007.07.29 FTP 자격인증을 저장하는 방법
  28. 2007.07.24 delegate 를 이용한 게시판 구성 (1)
  29. 2007.04.09 너 ASPX 웹폼 확장자를 바꿔봤니? 난 해봤~어!
  30. 2007.04.06 Composite Pattern 을 사용하여 MVC Pattern 구현

ASP.NET Core 2.1 또는 그 이상의 버전에서 dotnet new reactredux 템플릿에서 TypeScript 를 지원하지 않는다. 오직 Javascript 템플릿만 지원한다. 본 내용에서는 React+Javascript 를 React+TypeScript+Javascript 를 지원하는 환경으로 구성하는 방법을 알아본다.

1. 프로젝트 생성하기

다음의 명령을 입력하여 ASP.NET Core 의 React+Redux 프로젝트를 생성한다.

dotnet new reactredux -o aspnetcore-react-redux

프로젝트 생성이 성공하였다면 cd aspnetcore-react-redux 디렉토리로 이동한다.

2. package.json 파일 업데이트

ClientApp 디렉토리는 React+Redux 보일러플레이트가 설치된 경로이다. cd ClientApp 디렉토리로 이동한다.

ClientApp 디렉토리에 있는 package.json 파일에는 Spa 웹에서 사용할 npm 패키지를 설정할 수 있다. 이 파일을 열어 다음의 패키지를 추가한다.

package.json 파일

{
  "name": "aspnetcore_reactredux_typescript",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "bootstrap": "^3.3.7",
    "react": "^16.0.0",
    "react-bootstrap": "^0.31.5",
    "react-dom": "^16.0.0",
    "react-redux": "^5.0.6",
    "react-router-bootstrap": "^0.24.4",
    "react-router-dom": "^4.2.2",
    "react-router-redux": "^5.0.0-alpha.8",
+    "react-scripts": "1.0.17",
    "redux": "^3.7.2",
    "redux-thunk": "^2.2.0",
    "rimraf": "^2.6.2"
  },
+  "devDependencies": {
+    "react-app-rewired": "^1.6.2",
+    "react-scripts-ts": "^3.1.0",
+    "typescript": "^3.1.6",
+    "@types/jest": "^23.3.9",
+    "@types/node": "^10.12.2",
+    "@types/react": "^16.0.0",
+    "@types/react-dom": "^16.0.0",
+    "@types/react-router-bootstrap": "^0.24.4",
+    "@types/react-router-dom": "^4.2.2",
+    "@types/react-router-redux": "^5.0.0"
+  },
  "scripts": {
+    "start": "rimraf ./build && react-app-rewired start --scripts-version react-scripts-ts",
+    "build": "react-app-rewired build --scripts-version react-scripts-ts",
+    "test": "react-app-rewired test --env=jsdom --scripts-version react-scripts-ts",
+    "eject": "react-scripts eject"
  }
}

누락된 부분이 없는지 확인 후 다음의 명령으로 npm 패키지를 설치한다.

npm i

3. tsconfig.json 파일 생성

{
  "compilerOptions": {
    "baseUrl": ".",
    "module": "es2015",
    "moduleResolution": "node",
    "noUnusedParameters": false,
    "noUnusedLocals": true,
    "noImplicitAny": false,
    "target": "es6",
    "jsx": "react",
    "sourceMap": true,
    "skipDefaultLibCheck": true,
    "strict": true,
    "allowSyntheticDefaultImports": true,
    "experimentalDecorators": true,
    "strictNullChecks": true
  },
  "exclude": [
    "bin",
    "node_modules"
  ]
}

4. tslint.json 파일 생성

{
  "defaultSeverity": "error",
  "extends": [
    "tslint:recommended"
  ],
  "jsRules": {},
  "rules": {
    "interface-over-type-literal": false,
    "quotemark": false,
    "ordered-imports": false,
    "object-literal-sort-keys": false,
    "arrow-parens": false,
    "one-variable-per-declaration": false,
    "only-arrow-functions": false,
    "semicolon": [
      true,
      "ignore-interfaces"
    ],
    "no-console": false,
    "member-ordering": false,
    "variable-name": [
      true,
      "ban-keywords",
      "allow-leading-underscore"
    ],
    "member-access": false,
    "comment-format": false,
    "no-var-requires": false,
    "max-line-length": false,
    "jsx-alignment": false,
    "jsx-curly-spacing": [
      true,
      "never"
    ],
    "jsx-no-lambda": true,
    "jsx-no-multiline-js": true,
    "jsx-no-string-ref": true,
    "jsx-self-close": true
  },
  "rulesDirectory": []
}

5. config-overrides.js 파일 생성

/* config-overrides.js */

module.exports = function override(config, env) {
    //do stuff with the webpack config...
    return config;
}

6. 파일 확장자 변경

이제 Javascript 를 사용할지, TypeScript 를 사용할지 결정하여 파일 확장자를 다음과 같이 변경하면 된다. (단, 파일 확장자를 변경하지 않아도 무방하다)

  1. js -> jsx
  2. js -> tsx


자세한 소스 코드는 이 링크를 참고하기 바란다.

Posted by 땡초 POWERUMC

댓글을 달아 주세요

ASP.NET Core Middleware

ASP.NET Core Middleware 는 모든 요청과 응답을 처리하는 파이프라인이다. 레거시 ASP.NET 과 ASP.NET MVC 에서는 이를 IHttpModuleIHttpHandler를 통해 구현하여 처리하거나, Global.asax.cs 에서 처리 가능하다.

IHttpModule 이 주로 사용되는 경우는 권한 처리 등과 같이 다양하게 사용된다. 모든 요청은 이 IHttpModule을 파이프라인을 통과하게 되고, 응답을 제어할 수 있기 때문이다.

이 파이프라인이 오늘에 와서 ASP.NET Core Middleware 에서 그 역할을 대신하게 된다.

- 레거시에서 파이프라인

최초 레거시 ASP.NET 은 ‘ASP.NET 파이프라인’은 최초 요청부터 마지막 응답까지 컨텍스트가 흐르는 순서가 있다. 이것이 IIS 7.0 부터 IIS 서버와 통합이 되었다.

아래 그림은 IIS 7.0의 응용 프로그램 생명주기(즉 파이프라인)이다.

Application Lifecycle
https://msdn.microsoft.com/en-us/library/bb470252.aspx

- ASP.NET Core Middleware

ASP.NET Core 는 크로스플랫폼을 지향하는데, IIS 는 마이크로소프트 윈도우에서만 실행 가능한 웹/응용프로그램 서버이다. 따라서 ASP.NET Core 를 크로스플랫폼에서 호스팅하려면 IIS 서버에서 적용되었던 파이프라인 개념을 버려야 했다. 그래서 나온 개념이 Middleware 다.

Middleware 또한 IHttpModule이 수행하는 요청과 응답을 제어하는 역할을 한다.


https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware

다만, Global.asax.cs 에서 Session 생성/소멸과 같은 이벤트를 Middleware 만으로는 세세하게 제어하지 못한다. 아마도 다른 접근 포인트가 있는지 좀 찾아봐야 겠다.

IHttpMoudle과 IHttpHandler 마이그레이션

자세한 마이그레이션 과정이 MSDN 문서에 훌륭하게 설명되어 있어서 링크로 대체하고, 간단한 샘플만 남긴다.

참고로 Middleware는 UseMvc() 메서드 이전에 남겨야 한다. 그렇지 않으면, Middleware 가 동작하지 않는다.

Startup.cs
public class Startup
{

    // 생략...

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();

        app.UseApplicationInsightsRequestTelemetry();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseBrowserLink();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }

        app.UseApplicationInsightsExceptionTelemetry();
        app.UseStaticFiles();

        // 미들웨어 등록
        app.UseMiddleware<FrameworkMiddleware>();

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });

    }
}
FrameworkMiddleware.cs
public class FrameworkMiddleware
{
    private readonly RequestDelegate next;

    public FrameworkMiddleware(RequestDelegate next)
    {
        this.next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        Console.WriteLine("==========================================");
        await next(context);
        Console.WriteLine("------------------------------------------");
    }
}



자세한 내용은 아래 링크를 방문하기 바란다.
https://docs.microsoft.com/en-us/aspnet/core/migration/http-modules

Posted by 땡초 POWERUMC

댓글을 달아 주세요

IControllerFactory

IControllerFactory 는 컨트롤러 객체를 반환하거나 객체 릴리즈 시키는 팩토리 인터페이스이다. ASP.NET MVC 4 까지 지원하지 않았던 DI(Dependency Injection) 기능을 사용하기 위해 이 인터페이스를 구현하여 사용하였다.

ASP.NET Core 에서는 객체 주입(Injection) 할 때 한 가지 큰 단점이 있다.

생성자 주입(Constructor Injection) 으로만 DI(Dependency Injection) 기능을 사용할 수 있다. 이 방법은 필자가 가장 좋아하지 않는 방법인데, 그 사용성이 여간 귀찮을 수 없다.

이 아티클에서는 프로퍼티 인젝션(Property Injection) 이 가능한 Unity Application Block 을 사용하기 위해 IControllerFactory 를 마이그레이션 하는 것을 목표로 한다.

- 레거시 ASP.NET MVC 에서 IControllerFactory 설정

Controller 클래스들을 IoC(Inversion of Control) 컨테이너(Container) 에 담아 놓고, 꺼내쓸 때 DI(Dependency Injection) 을 시키는 방식이다. 이렇게 하면 사용하는 입장에서는 복잡한 객체와 인스턴스 관계를 파악할 필요 없이 꺼내 쓰면 되는 것이다. 당연 단위 테스트에서도 간결하게 사용할 수 있게 된다.

global.asax.cs

ControllerBuilder 클래스를 이용하여 IControllerFactory 인스턴스를 설정한다.

// 종속성 주입 설정
Application.Lock();
{
    Container = configureContainer();
    ControllerBuilder.Current.SetControllerFactory(new FrameworkControllerFactory(Container));
}
Application.UnLock();

FrameworkControllerFactory.cs

public class FrameworkControllerFactory : DefaultControllerFactory
{
    private readonly IFrameworkContainer container;

    public FrameworkControllerFactory(IFrameworkContainer container)
    {
        this.container = container;
    }

    #region Overrides of DefaultControllerFactory

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType == null)
        {
            requestContext.HttpContext.Response.Redirect("/", true);
        }

        var controller = container.Resolve(controllerType);
        return controller as IController;
    }

    #endregion
}

- ASP.NET Core 에서 IControllerFactory 설정

ASP.NET Core 에서는 상당수 서비스 클래스와 인스턴스들이 IoC Container 에 등록되어 여기에서 객체를 불러 사용하고 있는 형태다. IServiceCollection 인터페이스에 서비스에 ASP.NET Core 가 동작하기 위한 코어 클래스들이 등록되어 있다.

IServiceCollection 에 Singleton 객체로 services.AddSingleton<IControllerFactory, FrameworkControllerFactory>(); 하게되면 IControllerFactory 가 등록된다.

그럼 IServiceCollection 에는 IControllerFactory 가 두 개 등록이 되어있는데, 나머지 하나가 DefaultControllerFactory 클래스이다. 이 때, ASP.NET Core는 가장 마지막에 등록된 IControllerFactory 클래스를 반환한다.

먼저, Nuget 을 통해 Unity Application Block 을 설치한다.

Startup.cs

public class Startup
{
    public static IUnityContainer Container = new UnityContainer();

    public void ConfigureServices(IServiceCollection services)
    {
        // Add framework services.
        services.AddApplicationInsightsTelemetry(Configuration);
        services.AddMvc();

        // IControllerFactory 설정
        services.AddSingleton<IControllerFactory, FrameworkControllerFactory>();
    }
}

FrameworkControllerFactory.cs

public class FrameworkControllerFactory : IControllerFactory
{
    public object CreateController(ControllerContext context)
    {
        return Startup.Container.Resolve(context.ActionDescriptor.ControllerTypeInfo.AsType());
    }

    public void ReleaseController(ControllerContext context, object controller)
    {
    }
}

모든 설정이 다 되었으면, Unity Container 에서 DI(Dependency Injection) 을 수행하는 HomeController 가 정상적으로 동작하게 된다.

HomeController.cs

public class HomeController : Controller
{
    [Dependency]
    public MessageService MessageService { get; set; }

    public IActionResult Index()
    {
        MessageService.Say();

        return View();
    }
}

public class MessageService
{
    public void Say()
    {
        Console.WriteLine("MessageService Resolved. ===========================");
    }
}


Posted by 땡초 POWERUMC

댓글을 달아 주세요

오류 유형은 아래의 웹서버 로그와 같이 “세션ID 가 생성 되었지만, Response 가 Flushed 되어 저장할 수 없다”.

Session state has created a session id, but cannot save it because the response was already flushed

이 현상은 다음의 경우의 수를 모두 만족할 경우 발생하게 된다.

  • 웹서버(IIS) 가 리사이클링 되고,

  • 웹서버에게 첫 요청이 가고,

  • 코드에서 Session 속성을 사용하기 전이고,

  • 서버 코드에서 Response 를 Flush 하고,

  • 웹브라우저가 아닌, 네트워크 라이브러리를 통해 호출을 하고,

  • Global.asax.cs 의 Session_Start 이벤트가 발생할 때


분석을 해보면 (MSDN 에서 ASP.NET Application Page LifeCycle(영문)을 참고), PreRendering 단계 이후에 SavePageStateTo 를 수행하는 것을 알 수 있다. 따라서 인터넷을 통해 찾은 해결 방법이 Session 속성의 객체를 한 번 호출해 주는 것으로 이런 현상을 제거할 수 있다.

Posted by 땡초 POWERUMC

댓글을 달아 주세요

개요

ASP.NET WebForm 에서 Dependency Injection 을 사용하는 방법을 소개한다. IoC Container 를 이용하여 System.Web.UI.Page 를 상속하는 페이지에서 Injection 을 해야 하는데, 이를 위해 IHttpHandlerFactory 를 사용하는 방법을 소개한다.

여기에서는 필자가 꾸준히 만들어 온 Unity ContainerWindsor Castle 을 기반으로 하는 Umc.Core 프레임워크를 사용한다.Umc.Core 프레임워크에는 Unity Auto Registration 기능등이 모두 포함하기 때문에 프로젝트 셋업에 편리하다는 장점도 있다.

샘플 프로젝트는 필자의 github 에서 다운로드 받을 수 있다.
https://github.com/powerumc/WebForm-DependencyInjection 

프로젝트 셋업

먼저 프로젝트를 만들고, nuget 을 이용하여 umc.core 프레임워크를 설치한다.

nuget install umc.core

그리고, Umc.Core 에 구현에 놓은 IHttpHandlerFactory 를 샘플 프로젝트에 추가해 놓았다. 이를 web.config 에 추가해 주면 된다.

<system.webServer>
    <handlers>
      <add name="WebFormPageHandlerFactory" verb="*" path="*.aspx"  type="WebForm_DependencyInjection.FrameworkContainerPageHandlerFactory"/>
    </handlers>
</system.webServer>

FrameworkContainerPageHandlerFactory 클래스의 구현은 아래의 코드를 참고하면 된다. 다만, 아래 구현 코드에서 BuildManager.CreateInstanceFromVirtualPath 를 대신 사용하면 절대 안된다.

public class FrameworkContainerPageHandlerFactory : IHttpHandlerFactory
{
    public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
    {
        var handler = BuildManager.GetObjectFactory(url, false).CreateInstance();
        if (handler.GetType().ToString().StartsWith("ASP."))
        {
            var container = context.Application["container"] as IFrameworkContainer;
            return container.Resolve(handler.GetType().BaseType) as IHttpHandler;
        }

        return handler as IHttpHandler;
    }

    public void ReleaseHandler(IHttpHandler handler)
    {
    }
}

웹 응용프로그램 Bootstrap

그 다음 할 일은 어떤 컴포넌트들을 IoC 에 등록하고 이를 Composition 할 지 코드를 통해 구현한다. ASP.NET WebForm 에서는 Global.asax.cs 의 Application_Start 메서드에 구현하는 게 가장 적절하다.

그러나 이는 RELEASE 용 빌드인 경우가 그렇고, 개발 중인 경우, 즉 DEBUG 모드 빌드인 경우 Session_Start 에 구현해 놓는 것도 좋을 것 같다.

public class Global : System.Web.HttpApplication
{
  private static IFrameworkContainer container;

  protected void Application_Start(object sender, EventArgs e)
  {
      container = new FrameworkContainerForUnity();

      var catalog = new FrameworkAssemblyCatalog(Assembly.GetExecutingAssembly());
      var visitor = new FrameworkDependencyVisitor(catalog);
      var resolver = new FrameworkCompositionResolverForUnity((FrameworkContainerForUnity)container, visitor.VisitTypes());
      resolver.Compose();

      Application.Lock();
      Application["container"] = container;
      Application.UnLock();
    }
  }

FrameworkAssemblyCatalogFrameworkCatalog를 구현한 클래스로 컴포넌트를 등록하는 방법을 구현하는 클래스다.

FrameworkDependencyVisitor 는 catalog 에서 검색된 컴포넌트들을 구석 구석 방문해서 Comsoition 을 위해 객체 그래프를 그리는 클래스다.

FrameworkCompositionResolverForUnity 는 객체 그래프를 IoC Container 에 등록하는 클래스다.

이렇게 몇 줄의 코드로 Auto Registration 과정이 모두 끝난다.

서비스 구현

간단하게 Dependency Inection 을 테스트하기 위해서 IEmailService 인터페이스와 이를 구현한 코드다.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Web;
using Umc.Core;

namespace WebForm_DependencyInjection.Services
{
    public interface IEmailService
    {
        bool Send(string to, string contents);
    }

    [DependencyContract(typeof(IEmailService))]
    public class EmailService : IEmailService
    {
        public bool Send(string to, string contents)
        {
            HttpContext.Current.Response.Write("Send email.");
            return true;
        }
    }
}

페이지 구현

이제 모두 다 됐다. Dependency Injection 이 필요한 프로퍼티에 [DependencyInjection] 특성을 선언하면 Index 페이지 인스턴스가 생성될 때 컨테이너에 등록된 컴포넌트가 주입된다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Umc.Core;
using WebForm_DependencyInjection.Services;

namespace WebForm_DependencyInjection
{
    public partial class Index : System.Web.UI.Page
    {
        // Here.. Injection for IEmailService.
        [DependencyInjection]
        protected IEmailService EmailService { get; set; }

        protected void Page_Load(object sender, EventArgs e)
        {
            EmailService.Send("", "");
        }
    }
}


Posted by 땡초 POWERUMC

댓글을 달아 주세요

세션 저장소 커스터마이징

지금까지 살펴본 바 세션 정보를 memcached를 이용하여 세션 정보를 성능과 관리, 그리고 확장가능성 측면에서 만족할 만한 솔루션이다.

여기에서 좀 더 나아가 쿠키 등을 이용하여 서브 도메인(Sub Domain)의 웹 응용 프로그램에 인증을 하거나 브라우저를 닫고 새로운 브라우저로 재접속 한 경우 기존 세션을 유지할 수 있도록 기능을 개선할 수 도 있다.

과거에는 SSO(Single-Sign-On)을 구현하기 위해 쿠키로 서브 도메인을 인증하는 경우 domain 에 의해 쿠키가 공유가 가능하다는 점을 이용하여 구현하기도 했다. 물론, SSO 솔루션들이 많이 있었지만, 수천 수만명의 사용자가 관리 대상이 아니라면 굳이 비싼 SSO 솔루션을 쓸 필요는 없었다.

세션 저장 방법 커스터마이징

ASP.NET MSSQL Session Table 또는 Windows Azure SQL Session Table은 다음과 같이 정의된다. (MSDN에 정의된 테이블 참조 1)

CREATE TABLE [ASPState].dbo.ASPStateTempSessions (
    SessionId         nvarchar(88)  NOT NULL PRIMARY KEY,
    Created          datetime       NOT NULL DEFAULT GETUTCDATE(),
    Expires          datetime       NOT NULL,
    LockDate            datetime        NOT NULL,
    LockDateLocal     datetime      NOT NULL,
    LockCookie       int             NOT NULL,
    Timeout          int             NOT NULL,
    Locked           bit             NOT NULL,
    SessionItemShort    VARBINARY(7000) NULL,
    SessionItemLong  image        NULL,
    Flags             int            NOT NULL DEFAULT 0,
)   

CREATE NONCLUSTERED INDEX Index_Expires ON [ASPState].dbo.ASPStateTempSessions(Expires)  

CREATE TABLE [ASPState].dbo.ASPStateTempApplications (
    AppId             int            NOT NULL PRIMARY KEY,
    AppName          char(280)    NOT NULL,
)   

CREATE NONCLUSTERED INDEX Index_AppName ON [ASPState].dbo.ASPStateTempApplications(AppName)

이 두 테이블에서 가장 중요한 것은 다음의 두 개의 Private Key가 되는 필드이다.

  • AppId

    웹 응용 프로그램을 구분하는 고유 Id 값이다. IIS는 여러 개의 웹 응용 프로그램을 호스팅할 수 있는데, 이 웹 응용 프로그램을 구분하는 값으로 사용된다.

  • SessionId

    웹 응용 프로그램 내에 세션 키로 사용되는 값이다. 일반적으로 이 값은 해쉬된 값으로 중복되지 않는다.

따라서 여러 웹 응용 프로그램에서 같은 SessionId를 사용할 수 있다면 인증에 대한 SSO(Single-Sign-On)은 구현되는 것과 마찬가지다. 그러므로 위의 테이블 중 dbo.ASPStateTempApplications 테이블은 없어져도 된다.

만약 MS SQL을 사용하여 세션 저장소로 이용하는 경우 SQL Session Provider가 사용하는 Stored Procedure의 Where 절에 AppId를 빼기만 하면 된다.

필자는 SQL Server가 아닌 memcached를 이용하므로 위의 구성은 굳이 생략이 가능하다.

MemCached Session Store Provider 커스터마이징

ASP.NET은 클라이언트(웹 브라우저를 사용하는 사용자)를 구분하기 위해 해시된 세션 키 값을 사용한다고 했다. 이 해시된 값은 없어도 되지만, 웹 응용 프로그램 내부적으로 세션을 사용한다면 반드시 필요한 키 값이다.

클라이언트에게는 서버 내부적으로 사용하는 해시된 키 값을 쿠키에 저장하고, 서버로 요청이 오는 경우 이 쿠키 값의 해시된 세션 키 값을 이용하여 세션 데이터를 조회하게 된다. 만약, 이 해시된 세션 키 값이 없다면 새로운 세션으로 인식한다.

SSO를 구현하기 위해서 신원이 확인된 사용자마다 완전히 유일한(Unique)한 키 값이 필요하다. 예를 들어, 이 값은 사용자의 고유 번호가 될 수 있고, 사용자 아이디 또는 이메일과 같은 유일한 값이 되어야 한다. 세션 키는 매번 변할 수 있는 값이기 때문에 유일한 값이긴 하나 사용자마다 변하지 않는 유일한 값이 될 수 없다.

이를 구현하는 방법은 매우 간단하다. SessionStateStoreProviderBase 추상 클래스를 구현할 때 Id 값을 고의로 변경하면 된다.

public sealed class MemcachedSessionStateStore : SessionStateStoreProviderBase
{
   void Command(Action action)         
   { 
        var pool = SockIOPool.GetInstance(); 

        // 필자의 원격 memcached IPs     
        pool.SetServers(new string[] { "192.168.0.23:11211", "192.168.0.23:11211", "192.168.0.23:11211" }); 
        pool.InitConnections      = 3;
        pool.MinConnections       = 3;
        pool.MaxConnections       = 5;
        pool.SocketConnectTimeout = 1000;
        pool.SocketTimeout        = 3000;
        pool.MaintenanceSleep     = 30;
        pool.Failover             = true;
        pool.Nagle                = false;
        pool.Initialize();

        action();
   }

   public override void SetAndReleaseItemExclusive(HttpContext context, string id, SessionStateStoreData item, object lockId, bool newItem)
    {
        try
        {
            if (context.User.Identity.IsAuthenticated)
            {
                id = context.User.Identity.Name;
            }

            var data = new SessionData()
                {
                    Id      = id,
                    LockAge = TimeSpan.FromMinutes(10),
                    LockId  = id,
                    Exfires =  DateTime.Now.AddMinutes(10)
                }.ToBinaryBytes().ToBase64();

            Command(() => new MemcachedClient().Set(id, data));

        }
        catch (Exception e)
        {
            // 생략...
        }
        finally
        {
        }
    }

    public override SessionStateStoreData GetItem(HttpContext context, string id, out bool locked, 
                                                                                    out TimeSpan lockAge, 
                                                                                    out object lockId,
                                                                                    out SessionStateActions actionFlags)
    {
        return GetSessionStoreItem(false, context, id, out locked, out lockAge, out lockId, out actionFlags);
    }

    public override SessionStateStoreData GetItemExclusive(HttpContext context, string id, out bool locked,
                                                                                            out TimeSpan lockAge,
                                                                                            out object lockId,
                                                                                            out SessionStateActions actionFlags)
    {
        return GetSessionStoreItem(true, context, id, out locked, out lockAge, out lockId, out actionFlags);
    }


    public override void CreateUninitializedItem(HttpContext context, string id, int timeout)
    {
       if (context.User.Identity.IsAuthenticated)
        {
            id = context.User.Identity.Name;
        }

        var data = new SessionData()
        {
            Id = id,
            LockAge = TimeSpan.FromMinutes(10),
            LockId = id,
            Exfires = DateTime.Now.AddMinutes(10)
        }.ToBinaryBytes().ToBase64();

        Command(() => new MemcachedClient().Set(id, data));
    }

    public override SessionStateStoreData CreateNewStoreData(HttpContext context, int timeout)
    {
        return new SessionStateStoreData(new SessionStateItemCollection(), SessionStateUtility.GetSessionStaticObjects(context), (int)timeout);
    }

    private string Serialize(SessionStateItemCollection items)
    {
        var ms = new MemoryStream();
        var writer = new BinaryWriter(ms);

        if (items != null)
            items.Serialize(writer);

        writer.Close();

        return Convert.ToBase64String(ms.ToArray());
    }


    private SessionStateStoreData Deserialize(HttpContext context, string serializedItems, int timeout)
    {
        var ms = new MemoryStream(Convert.FromBase64String(serializedItems));
        var sessionItems = new SessionStateItemCollection();

        if (ms.Length > 0)
        {
            var reader = new BinaryReader(ms);
            sessionItems = SessionStateItemCollection.Deserialize(reader);
        }

        return new SessionStateStoreData(sessionItems, SessionStateUtility.GetSessionStaticObjects(context), timeout);
    }  

// 이하 생략...  
// 이하 생략...  
// 이하 생략...

}  

인증된 사용자와 인증되지 않은 사용자의 세션 데이터

필자는 memcached의 사용자 세션 키를 다음과 같이 정의하여 구현하였다.

  1. 인증되지 않은 사용자는 해쉬된 세션 키를 사용한다.
  2. 인증된 사용자는 인증 정보를 세션 키로 사용한다. (UserName 정보)

모든 클라이언트의 웹 브라우저 쿠키에 ASP.NET 세션 키가 ASP.NET_SessionId 쿠키 값으로 저장된다. 이 값은 ASP.NET이 생성한 해쉬된 값이므로 언제든지 변할 수 있다가도, 사용자가 웹 응용프로그램에서 인증을 하게 되면 해쉬된 세션 키를 사용하지 않고 사용자 계정으로 세션 키 값을 대체하게 된다.

이 코드가 위의 코드 중에 다음과 같이 구현한 부분이다.

if (context.User.Identity.IsAuthenticated)
{
    id = context.User.Identity.Name;
}

그럼 현재까지 구현된 부분으로 다음과 같은 시나리오로 테스트를 하고 결과를 보자.

  1. 익명으로 웹 사이트 접속
  2. 익명 사용자의 세션 키 값을 memcached 에서 조회 (해쉬된 세션 키 rlvh3y4edt1nyzrt42sdgnvu)
  3. 웹 사이트 로그인 (로그인 사용자 계정 powerumc)

  4. 인증된 사용자의 계정으로 memcached 에서 조회

그럼 분산된 웹 응용 프로그램이나 다른 도메인의 웹 응용 프로그램 간에 서로 인증을 해보자. 폼 인증을 사용한 웹 응용 프로그램이므로 다른 웹 응용 프로그램에 폼 인증을 시킬 수 있는 방법만 있으면 된다. 웹 응용 프로그램 간에 토큰 값을 넘길 수 도 있고, 또는 요즘 유행하는 다른 인증 매커니즘을 이용할 수 도 있다.

어쨌든 서로 다른 웹 응용 프로그램이 하나의 memcached 세션 서버에 연결이 가능하다면 신원이 인증된, 위에’서 테스트 한 사용자 계정 ‘powerumc’는 어느 웹 응용 프로그램에서 세션을 조회하더라도 존재하게 된다.

사용자는 웹 브라우저를 완전히 닫거나 운영체제를 완전히 재시작하였다고 하더라도 세션이 만료되는 통상적인 시간인 약 20분 이전에 로그인만 한다면 이전에 저장된 세션 데이터를 가져와 마지막의 최신 상태를 유지할 수 있게 된다.

결론

지금까지 memcached를 이용하여 Session State Store Provider를 만들고 이를 활용하는 방법을 알아보았다. 분산된 세션 저장소를 사용해야 하는 경우 memcached를 이용하면 신뢰된 성능을 보장받을 수 있고, 세션 데이터를 유지하는 방법을 변형하여 좀 더 보안을 높이거나 유연하게 상호운용을 가능하도록 할 수도 있다.

  • 분산된 세션 저장소를 확장
  • memcached를 이용하여 신뢰할 수 있는 성능 발휘
  • 세션의 보안을 강화하거나 격리시킬 수 있는 방법이 가능
  • 세션의 상호운용성을 높여 서로 다른 도메인간에 인증 가능
  • 서로 다른 도메인간에, 서로 다른 플랫폼 간에 세션 데이터 공유 가능

이 외에 여러 가지 기법들을 활용하여 더 많은 것들을 가능할 수 있다. 세션의 활용이 웹 개발 플랫폼에서 그만큼 중요하고 보안과 직결되는 요소인 만큼 이 기회에 세션에 대한 내용을 모두 정복해 보기 바란다.

  1. MSDN에 정의된 테이블 참조 http://msdn.microsoft.com/en-us/library/aa478952.aspx


Posted by 땡초 POWERUMC

댓글을 달아 주세요

필자는 일전에 이와 관련되어 상당한 분량의 포스팅을 올린 적이 있다. 총 5회의 아티클 중 마지막 회를 모두 작성하지는 못했지만, 지금 이 내용이 그 마지막 회의 내용과 어느 정도의 내용과 유사하다고 보면 된다.


그 중, 4회 아티클 ‘[실전 ASP.NET Session [4] - 세션상태 마이그레이션]5’의 내용은 본 아티클의 내용에서 매우 중요한 기초 내용이 된다. 이 글을 읽고 있는 독자 중 잘 이해가 되지 않는다면 필자가 이전에 작성한 포스팅을 반드시 읽어보기를 바란다.

그리고 위의 필자가 작성한 링크의 아티클은 5년전에 작성된 글임을 인지해 주길 바란다. 그 때는 필자가 지금보다도 더 실력이 형편 없었을 뿐더러 현재 추구하는 웹 개발의 트랜드와 상이한 면이 있을 수도 있을 것이다. .NET Framework 버전과 개발툴의 버전도 지금 사용하는 버전과는 한참 예전 버전이었다. 하지만 5년이 지난 글임에도 기초적인 내용은 모두 탄탄하게 짚고 넘어가므로 한번씩 보는 것도 나쁘지 않다.

ASP.NET이 지원하는 분산 세션 상태(Session State)

일반적으로 1대 1의 물리적인 관계는 분명 분산환경이긴 하지만 분산 환경이라고 말하지 않는다. 어떤 물리적인 환경에 배치하든, 통계학의 분산에 대한 기대값이나 편차의 해는 변하지 않기 때문이다.

분산 환경에서 더 나은 성능을 내기 위한 알고리즘과 휘발성인 메모리 안에서 데이터가 유실되지 않도록 원자성(Atomicity)을 유지할 수 있는 방법을 제공해야 한다. 또 웹 서버의 세션이라는 특징은 매우 빈번하게 엑세스 되고, 업데이트와 삭제 작업도 매우 많이 발생한다.

하지만, 안타깝게도 memcached[1]는 ‘get’ 명령의 원자성(Atomicity)를 보장하지 않는다고 한다.

A series of commands is not atomic. If you issue a 'get' against an item, operate on the data, then wish to 'set' it back into memcached, you are not guaranteed to be the only process working on that value. In parallel, you could end up overwriting a value set by something else.


하지만 괜찮다. 웹 서버의 세션은 동시다발적인 병렬 작업으로 요청이 들어올 수 없는 구조이다. 웹 서버로 사용자의 요청이 들어올 때, 사용자의 웹 브라우저에 가진 쿠키 값이 신원보증을 하는 값, 또는 해시된 세션의 키 값이다. 그러므로 한 세션에 대해 동시에 여러 클라이언트(사용자 웹 브라우저)의 요청은 있을 수 없는 일이다.

하지만 불가능한 것도 아니다. 예전에는 자바스크립트가 허용되는 게시판이나 이와 유사한 환경에서 악성 스크립트를 심어 놓으면, 그 글을 보는 누군가는 브라우저의 쿠키 값을 취득하여 해커에게 보내고, 해커는 세션이 만료되는 20분(일반적인 세션 만료 시간) 전에 취득한 세션 값으로 재 요청을 하게 되면 다시 세션이 연장되고, 세션이 로그인된 상태인 경우 해커도 로그인된 상태로 입장하는 것이 가능하다

때문에 완전히 같은 해시된 세션의 키 값으로 여러 클라이언트(사용자의 웹 브라우저)의 요청이 온다는 것은 사용자의 세션 값이 털려서, 누군가 악용하고 있다고 보는 것이 맞다.

그러므로 아주 정상적이고 일반적인 환경에서 원자성을 제공하지 않는 ‘get’ 명령은 전혀 문제가 되지 않으며, 오히려 원자성을 보장할 수 없는 문제로 더 좋은 Read 성능을 낼 수 있기 때문에, memcached 가 세션 서버로 사용하기에 redis[2] 보다 더 좋을 수 있다.

ASP.NET이 제공하는 세션 관리 요약

아무런 설정이 없다면 In-Proc, 즉 로컬 머신의 메모리를 사용하게 된다. 만약 불행히도, Win32 DLL을 참조하는 웹 응용 프로그램이라면 웹 서버의 메모리의 크기 따윈 무시하고 최대 2GB 밖에 사용할 수 없게 된다.

이를 극복하는 방법으로 윈도우 서비스로 백그라운드로 실행되는 ASP.NET Session State Service 서비스를 사용하면 좋다. 여러 웹 응용 프로그램이 하나의 Session State Service에 연결되더라도 웹 응용 프로그램마다 고유의 Identity Key(Guid)가 할당되고, 이를 Primary Key로 구분하게 된다. 이 NT Services가 다운되거나 재시작하지 않는 이상 웹 서버의 세션은 항상 유지할 수 있다.

긴급한 패치로 웹 응용 프로그램을 업데이트 해야 하는 경우, IIS가 재시작 되는 경우가 발생할 수 있는데 이 때 Worker Process에 의해 구동되는 In-Proc 세션 정보는 모두 초기화된다. 웹사이트에서 뭔가를 구매하려는 사용자가 있었다면 큰 사고로 이어질 수 있지만, Session State Service에 세션 정보가 저장이 되므로 IIS 재시작 후에도 웹 응용 프로그램은 세션 정보를 모두 안전하게 유지할 수 있다.

보통 웹 서버와 Session State Service간에 통신을 하기 위해 방화벽의 특정 포트를 개방해야 하고, 내부적으로 서로 간에 소켓 통신에 사용되는 전용 프로토콜이 존재하므로 Session State Service를 확장하기란 쉽지 않을 것이다.

이런 경우, ASP.NET에서 MS SQL Server를 이용하여 세션 정보를 저장하는 방법을 제공해 준다. 세션 정보에 추가하고 싶은 정보나 DB상에 기록되어질 특정 필드를 추가하여 사용할 수 있고, Oracle 또는 MySQL을 사용하여 세션 상태를 저장할 수도 있다.

위의 링크 중 ‘[실전 ASP.NET Session [4] - 세션상태 마이그레이션]13’에 예제로 구현된 Oracle Session Store Provider 예제가 있으니 참고하길 바란다.

memcached를 이용하는 세션 저장소

memcached를 세션 저장소로 이용하는 것은 ASP.NET Session State Services의 역할과 구현만 다를 뿐이지 매우 유사하다. memcached도 메모리를 저장소로 이용하여 빠르게 캐시하고 요청에 빠르게 응답할 수 있는 간결한 구조로 구현된 오픈소스이다.

특히 memcached를 이용할 경우 ASP.NET Session State Services로 불가능한 클러스트링이 가능하기 때문에 수평적으로 세션 서버를 확장할 수 있다. 한 대의 세션 서버만 있는 경우보다 로드벨런싱의 자유도가 더 높고, 세션 서버의 장애에도 대처가 가능하다. 또 하나의 장점이라면 저가형 장비를 병렬로 구성이 가능하기 때문에 세션 서버를 구성하기 위한 금전적이거나 물리적인 많은 제약이 사라지게 된다.

memcached와 redis, 두 가지 오픈소스를 고려하고 있었는데, 세션 서버에 memcached가 더 적합하다고 생각하여 memcached만 다룬다. memcached의 소스 코드가 더 가볍고 취향이나 요구사항에 따라 코드를 변형하기에 더 적합하지 않을까 생각한다. 그리고 자칫 복잡해질 수 있는 구조적인 아키텍처를 좀 더 간결한 memcached로 표현하는 것이 글을 보는 입장이나 쓰는 입장에서 편하다고 믿는다.

memcached는 C언어로 구현이 되어있고, GNU C 표준 라이브러리를 사용하므로 여러 운영체제에서 사용이 가능하다. (ASP.NET Session State Services는 윈도우 환경에서만 사용이 가능하다) 물론 필자는 맥킨토시, 리눅스, 윈도우에서 memcached를 컴파일, 구동이 잘 됨을 확인하였다.

간단하게 사용자의 세션 정보를 저장할 클래스를 하나 간단하게 만들었다. 외부에서는 오직 참조만 하여 사용할 수 있도록 internal 생성 메서드를 하나 가지고 있다.


다음은 ASP.NET MVC 프로젝트로 만든 간단한 예제이다.

web.config
1
2
3
4
5
6
<sessionState mode="Custom" customProvider="MemcachedSessionStateStore">
    <providers>
        <add name="MemcachedSessionStateStore"
             type="Umc.Core.Web.SessionState.Memcached.MemcachedSessionStateStore, Web.SessionState.Memcached, Version=1.0.0.0, PublicKeyToken=eed8f2bc3bfc4c7a, Culture=neutral"/>
    </providers>
</sessionState>

  

HomeController.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class HomeController : Controller   
{
   private static readonly string KEY_OF_USER_SESSION = "__USER_SESSION_KEY__";
   public UserIdentity SessionStore
    {
        get { return (Session[KEY_OF_USER_SESSION] as UserIdentity) ?? UserIdentity.Empty; }
        set { Session[KEY_OF_USER_SESSION] = value; }
    }
 
 
    public ActionResult Index()
    {
        Session[KEY_OF_USER_SESSION] = UserIdentity.New("POWERUMC"
                                                        , "Junil, Um"
                                                        , "powerumc at gmail.com");
 
 
         /*
          // 맴버쉽 사용자 인증 설정 생략...
        var ticket = new FormsAuthenticationTicket(...);
        var user = new GenericPrincipal(new FormsIdentity(ticket), ... )
         * */
 
 
        return View();
    }
}

 

위와 같이 클라이언트의 세션 키를 이용하여 memcached의 서버에 세션 키를 이용하여 세션 값을 저장한다.

telnet 'get' 명령 결과
MPOWERUMC:~ powerumc$ telnet 192.168.0.23 11211
Trying 192.168.0.23...
Connected to 192.168.0.23.
Escape character is '^]'.
get laaymm13uyu03bofhdydi4iq
VALUE laaymm13uyu03bofhdydi4iq 0 477
AAEAAAD/////AQAAAAAAAAAMAgAAAF1XZWIuU2Vzc2lvblN0YXRlLk1lbWNhY2hlZCwgVmVyc2lvbj0xLjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWVlZDhmMmJjM2JmYzRjN2EFAQAAAEpVbWMuQ29yZS5XZWIuU2Vzc2lvblN0YXRlLk1lbWNhY2hlZC5NZW1jYWNoZWRTZXNzaW9uU3RhdGVTdG9yZStTZXNzaW9uRGF0YQQAAAATPElkPmtfX0JhY2tpbmdGaWVsZBg8TG9ja0FnZT5rX19CYWNraW5nRmllbGQYPEV4ZmlyZXM+a19fQmFja2luZ0ZpZWxkFzxMb2NrSWQ+a19fQmFja2luZ0ZpZWxkAQAAAgwNAgAAAAYDAAAAGGxhYXltbTEzdXl1MDNib2ZoZHlkaTRpcQC8oGUBAAAAAPAWAzAi0IgJAwAAAAs=
END

 

 

ASP.NET의 세션이 실제로 저장이 되었는지 확인해보자. telnet을 통해 memcached 포로토콜의 명령을 입력하여 확인하면 된다.

현재 브라우저에서 연 세션 값은 쿠기에 저장이 되어 있다. 쿠키에 저장된 세션의 키 값은 ‘laaymm13uyu03bofhdydi4iq’ 이다.


telnet에 접속하여 memcached에 저장된 세션 키의 값을 조회된다.

memcached 세션 저장소로써 활용

memcached 오픈 소스 솔루션을 이용하여 메모리 캐시를 이용하여 ASP.NET 세션 상태를 저장할 수 있도록 구성해 보았다. 이제 얼마나 좋은 성능을 낼 수 있는지 측정이 필요한데 본 아티클에서는 memcached를 세션 서버로 활용할 때에 대한 성능의 문제는 다음에 기회가 되면 더 심도있게 다루어 보도록 하겠다.

그리고 ASP.NET 뿐만 아니라, JSP 또는 Servlet, 그 외에 여러 웹 개발 프레임워크에서 쉽게 사용할 수 있다. memcached는 C#, Java, Python 등 여러가지 언어로 memcached서버에 연결할 수 있는 클라이언트 라이브러리를 어렵지 않게 찾을 수 있다.

이번 아티클에서는 MemcachedSessionStoreProvider 소스 코드를 제공하지 않았다. 물론, 필자의 위 코드를 실행하기 위해 MemcachedSessionStoreProvider 소스 코드가 있어야 한다. 이 코드는 다음 아티클에서 조금씩 작성해 나갈 예정이다.

memcached 에 대한 자세한 내용은 아래 링크의 구글 코드에 들어가면 컴파일 및 실행 방법과 유지관리 방법에 대한 위키가 있다.

memcached wiki, https://code.google.com/p/memcached/wiki/NewStart



[1]: C언어로 구현된 고성능 분산 메모리 객체를 캐시하는 서버. Free & open source, high-performance, distributed memory object caching system, generic in nature, but intended for use in speeding up dynamic web applications by alleviating database load.

[2]: 키/값을 저장할 수 있는 분산 서버로 데이터의 구조체 뿐만 아니라 문자열, lists, 빠른 검색을 위한 hashes 등을 지원. Redis is an open source, BSD licensed, advanced key-value store. It is often referred to as a data structure server since keys can contain stringshasheslistssets and sorted sets.

Posted by 땡초 POWERUMC

댓글을 달아 주세요

오늘 Microsoft 의 Scottgu's Blog 를 통해 WebMatrix & Razor 기술이 소개되었습니다.    

우선 간단하게 용어를 정리해봅시다.

WebMatrix

경량화한 웹 개발 도구

Razor

ASP.NET 의 뷰(View) 엔진

즉, WebMatrix&Razor 는 빠르게 웹 개발 환경을 구성하고 Razor 의 뷰(View) 엔진을 이용하여 신속하게 웹 페이지를 개발하고자 합니다. 웹 환경/웹 개발/데이터베이스/웹 개발 도구 등 WebMatrix&Razor 에 모두 포함이 되어 있습니다.

아마도 처음 웹 개발에 접하시는 분들이 처음 갖는 고민은..? 웹 환경 구성/웹 프로토콜 및 통신의 이해/호스팅 등 복잡했던 초기 작업을 매우 효과적이고 간소화하여 신속하게 작업을 진행할 수 있는 장점이 있습니다.

   

WebMatrix 란 무엇인가요?

WebMatrix 는 약 15MB 의 용량으로 빠르게 웹 개발 환경을 갖출 수 있습니다. (단, .NET Framework 4.0 이 인스톨 되지 않았을 경우 약 50MB). 현재 WebMatrix 는 Beta 버전이며 이 링크를 클릭하시면 다운로드 받으실 수 있습니다.

이 웹 WebMatrix 에는 다음의 구성 요소가 포함이 되어 있습니다.

  • IIS Express
  • SQL Compact Edition
  • ASP.NET Extensions

그리고 Visual Studio 2010 또는 Visual Web Development 2010 Express 개발 도구를 이용하여 웹 개발 또는 커스터마이징을 하실 수 있습니다.

   

WebMatrix 는 쉽게 사용할 수 있게 설계가 되었습니다.

초기 웹 개발 환경은 웹 페이지를 만들기 위해 해야 할 일들이 많았습니다. 환경 구성/개발 환경 구성 등 말이죠.

WebMatrix 는 아래와 같이 매우 심플한 디자인으로 웹 개발의 시작을 빠르고 쉽게 수행할 수 있습니다. Site from Web Gallery 는 오픈 소스 웹 어플리케이션을 바로 설치하여 사용할 수 있고, Site From Template 으로 최적화된 환경에서 개발을 시작할 수 도 있습니다.

Site From Web Gallery 는 온라인에서 인기 있는 다양한 오픈 소스 웹 어플리케이션이 포함되어 있답니다. ASP.NET, PHP 의 오픈 소스 웹 어플리케이션이 포함되어 있으며, 설치나 배포가 매우 간단합니다.


그 중, Scott 님은 BlogEngine.NET 으로 예제를 보여주시네요. BlogEngine.NET 는 이미 예전부터 CodePlex 를 통해 오픈 소스로 공개가 되고 현재도 인기 있는 블로그 웹 어플리케이션입니다.

BlogEngine.NET 을 선택하고 Next 버튼을 클릭하면 이것을 설치하기 위한 컴포넌트를 체크하거나 다운로드 받습니다. 즉, 원클릭(One-Click) 으로 어플리케이션이 동작하기 위한 모든 환경을 스스로 구성한다는 얘기죠^^

PHP 어플리케이션의 경우 WordPress, Drupal, Joomla, Sugar CRM 등은 MySQL 이 필요한데, 개별적인 설치 없이 이런 환경도 자동으로 다운로드 받아 설치를 합니다.

간단하게 소프트웨어 사용권 동의(EULA) 를 클릭하면 바로 설치와 구성 작업을 시작합니다.

   

그리고 금새 설치와 구성이 완료가 되었지요^^

   

모든 구성이 완료되면, 아래와 같은 Overview 페이지가 나타납니다.

   

설치된 BlogEngine.NET 을 실행하기 위해서 아래의 Run 버튼을 클릭합니다. 인터넷 익스플로러, 크롬, 파이어폭스로 실행할 수 있고, Open in all browsers 로 여러 브라우저로 한꺼번에 실행할 수 있습니다.

   

자! 여태까지 클릭 클릭만 했는데, 아래와 같이 BlogEngine.NET 이 설치되고, 구성되고, 모든 구성이 완료가 됩니다.

   

초기 관리자 아이디와 비밀번호는 admin/admin 입니다. 관리자로 로그인 하셔서 블로그의 이름을 달아주시고, 블로그 저자 소개 등을 입력해 주시면, 곧바로 블로그를 운영을 준비하셔도 될 것 같습니다.^^

WebMatrix 는 오픈 소스 웹 어플리케이션을 커스터마이징 할 수 있는 약간의 개발 환경도 제공해 줍니다. 아래와 같이 소스 코드를 변경할 수 도 있고, Files 버튼으로 파일을 편집하거나 추가, 삭제를 할 수 있습니다.

   

이제 블로그를 운영하기 위해 배포와 호스팅을 해야 합니다.

WebMatrix 의 특징은 매우 경량화되었고 작업 환경이 통합된 장점을 갖습니다. 로컬 또는 원격 웹 서버로 배포할 때, FTP 또는 FTP/SSL 또는 Microsoft Web Deploy(MSDeploy) 를 이용하여 쉽게 배포가 가능합니다.

아래의 Publish 버튼을 클릭하면 배포 준비가 시작됩니다.

   

배포 세팅 화면은 아래와 같습니다. 서버의 기본 정보를 입력하고, 데이터베이스의 연결 문자열을 입력한 후에, Publish 버튼을 클릭합니다.

   

모든 준비가 완료되었고, Publish 버튼을 누르면 이제 배포를 시작할 준비가 완료 되었습니다.

   

이하.. 금일 Microsoft Korea 의 김대우 과장님께서 진행하는 WebMatrix&Razor 세미나에 참석하기 위해 이만 줄입니다. 다른 분들께서 더 알차고 재미있게 포스팅 해 주시리라^^

Posted by 땡초 POWERUMC

댓글을 달아 주세요

  1. 드란 2010.09.03 13:56 Address Modify/Delete Reply

    툴은 vs 를 사용중이니 안부러운데.. 문법강조 색은 좋아보이네요 vs는 그냥 가끔 파랑 녹색 회색 밖에 안보여서 -_-;;
    설정하자니 귀찮고... 구글 어딘가.. 저 색들을 vs에 마춰서 올려줄것도 같은데.. 구글링도 귀찮고 ㅜㅜ;;;

YouTube 로 보기








Daum 의 TV 팟으로 보기


Posted by 땡초 POWERUMC

댓글을 달아 주세요

이 포스트도 예전에 3월 달에 써놓기만 해놨다가 이제서야 Publishing 하게 되었습니다. 워낙 구석 구석 써놓고 관리를 하지 않다 보니 뭐가 어디에 있는지 모르겠네요 ㅡㅡㅋ;

   

웹 테스트 중 Favicon 다운로드로 인한 문제

   

Visual Studio 의 웹 테스트 또는 Fiddler 등을 이용하여 테스트 중에 Internet Explorer 7 에서 문제가 발생하였습니다. 크리티컬한 문제는 아니었습니다만, 테스트 중에 불필요한 Favicon 다운로드가 너무 빈번하게 발생하였습니다.

이런 문제는 실제 테스트에서 오류로 작용하지는 않지만, 성능 관련 테스트나 시나리오가 있는 테스트에서는 의도하지 않는 결과가 나올 수 있기 때문에 올바른 테스트라고 보기가 어렵습니다.

웹 테스트로 Favicon 의 빈번한 다운로드 문제가 발생하여 Fiddler 로 캡처해본 화면 입니다.

   

[그림1] 엄청난 Favicon 을 다운로드하여 testing 을 방해한다

   

문제 해결 방법

Internet Explorer 7 에서의 해결 방법
http://blogs.msdn.com/jeffdav/archive/2007/03/01/why-doesn-t-the-favicon-for-my-site-appear-in-ie7.aspx

FireFox 에서의 해결 방법
http://www.howtogeek.com/howto/internet/firefox/quick-tip-disable-favicons-in-firefox/

Posted by 땡초 POWERUMC

댓글을 달아 주세요


 
서버와 클라이언트는 어떤 과정이 반복되나요?
 
ASP.NET 의 서버 모델은 아래의 그럼처럼 반복적인 추가 작업을 하게 됩니다.

[그림6] 서버 모델 프로세스
 
HTML Form 모델은 여러 개의 Form 의 구간을 두어 단지 필요한 데이터만 서버로 전송합니다.
아래 그림처럼 말이죠.

[그림7] HTML Form 모델 프로세스
 
이러한 뷰 스테이트(ViewState) 는 HTTP 파일 업로드가 되듯이 POST 로 서버로 업로드 됩니다. 즉 이 뷰 스테이트(ViewState) 양이 커지게 되면 web.config 에서 요청 데이터 사이즈의 크기를 조절해 주어야 하는 상황까지 벌어집니다.
 
<httpRuntime maxRequestLength="업로드 사이즈" />
 
그리고 우리나라의 인터넷 인프라가 아무리 발달하였다고 하여도 그것은 전적으로 다운로드 속도입니다. 대부분의 경우는 업로드 속도는 다운로드 속도에 못 미치며, 다운로드 속도의 절반도 못 미치는 경우가 대부분입니다.
 
그렇다고 업로드 속도가 해결된다고 모든 것이 해결되는 것은 아닙니다. 서버의 자원은 한정적입니다. 서버의 직/역직렬화 과정 등은 전송된 데이터의 양 만큼의 메모리 자원을 요구하게 됩니다. 즉, 알게 모르게 웹 서버는 스트레스를 받고 있으며 추가적인 코스트(Cost) 가 발생하기도 합니다.
 
이러한 면에서 HTML Form 모델은 기존 ASP.NET 서버 모델에 비해 하나의 서버에서 수십에서 수백 명의 사용자의 접속을 더 허용할 수 있습니다.
 
 
그럼 ASP 와 다를 바가 없는데요? 차라리 ASP 가 빠르겠네요.
 
ASP 와 ASP.NET 은 내부적으로 전혀 다른 구조입니다. 그리고 ASP.NET 은 ASP 의 인터프리터 방식과 비교할 때 더 나은 성능을 낼 수 있습니다.
 
ASP.NET 은 .NET Framework 과 C#(VB.NET 등) 으로 언어적 측면과 프레임워크가 제공하는 기능으로 생산성의 향상을 기대할 수 있습니다. 그리고 ASP.NET 파이프라인(Pipeline) 의 향상된 아키텍처와 보안적 측면에서 기존 ASP 에 비해 향상된 보안 기능을 제공합니다. 그리고 컴파일 언어 측면에서 성능 향상과 한번 컴파일된 어셈블리는 어셈블리 캐시에 적재되어 인터프리터 언어인 ASP, PHP 등에 비해 월등히 빠른 성능을 기대할 수 있습니다.
 

[그림8] ASP.NET 실행 모델
 
즉, ASP.NET 이 제공하는 인프라는 기존 ASP 등과 비교할 때 보다 향상된 아키텍처와 언어적 측면, 보안적 측면, 성능적 측면 등의 이점을 누릴 수 있습니다.
 
ASP.NET 의 서버 컨트롤을 사용하는 서버 모델은 변칙적인 웹 개발 방법에 불과합니다. 즉, 단일 Form 개발 방법을 통해 서버 컨트롤과 상태 유지의 장점 등으로 개발 생산성의 관점에서 시작되었습니다. ASP.NET 서버 모델의 단일 Form 방식은 성능 자체를 최적화할 방법이 없습니다.
 
즉, ASP.NET 을 사용하는 서버 모델은 HTML Form 모델의 성능을 따라갈 수 없습니다. 서버 컨트롤을 남용하는 특정 사람들에 의해 ASP.NET 이 느리다는 편견이 확산된 것이 안타까울 뿐입니다.
 
 
그럼 서버 컨트롤을 쓰지 않는 것이 결론인가요?
 
반드시 그렇게 하지 않아도 됩니다. ASP.NET 의 서버 모델은 HTML Form 방식의 개발 방법보다 여러 가지 처리에 대한 고민을 보다 단순화 할 수 있습니다. 그렇기 때문에 ASP.NET 을 입문하는데 ASP 에 비해 오히려 난이도가 쉽다고 생각합니다. 그리고 다양한 고객 또는 사용자의 요구 사항을 충족하기 위해서는 3-party 제품을 통해 복잡하고 많은 리소스가 투입되는 작업을 서버 컨트롤 하나로 단순화 시킬 수 있습니다.
 
가끔 이런 사람들을 봅니다.
 
서버 컨트롤 사용 못하게 하는 프로젝트도 있나요? 차라리 ASP 로 개발하자고 하세요”
 
서버 컨트롤은 ASP.NET 의 서버 모델이 제공하는 단지 보너스 정도라고 생각합니다. 서버 컨트롤이 ASP.NET 의 전부이며, 서버 컨트롤을 사용하지 않는 것은 ASP.NET 개발이 아니라고 단언하면 안됩니다. 말했듯이 ASP.NET 의 서버 모델이 기본적인 웹 개발과 비교하면 변칙적인 방법에 불과합니다.
 
ASP.NET 을 서버 모델과 HTML Form 모델로 개발하였을 때, 절대 ASP.NET 서버 모델의 개발 방법은 HTML Form 모델로 개발하는 성능의 꽁무니도 못 따라옵니다. 혹시 이견이 있다면 실무 프로젝트에서 같은 사이트를 양측 모두 적용해 보셨나요? 요즘 이런 개그가 있죠. “안 해보셨으면 말을 하지 마세요!”
 
개발하는 프로젝트의 성격과 사용자는 유저에 따라 두 개발 방식의 적절한 선택이 필요하고 두 모델에 대한 이해가 필요합니다. 그리고 ASP.NET 을 바라보는 올바른 안목이 필요합니다.

Posted by 땡초 POWERUMC

댓글을 달아 주세요

  1. 천이백 2015.01.28 19:11 Address Modify/Delete Reply

    구글링하다가 우연히 들어오게 되어서 글을 남깁니다. 이 글을 보고 답변을 다실 지는 모르겠지만
    ASP.NET의 서버 모델은 변칙적인 방법에 불과하다는 생각이 지금도 유효하신 건가요?
    ASP.NET에 대한 평가를 하려면 PHP나 JSP 또는 ASP와 비교를 해야지 html 과 비교하는 것 자체가 제가 보기엔 어불성설인 것 같네요.
    http://www.wrensoft.com/zoom/benchmarks.html
    위의 벤치마크에서도 나와 있지만 asp.net은 성능상에도 다른 서버 프로그래밍과 비교해서 뒤처지지 않는 것으로 나오네요. MS의 많은 천재 개발자들이 편법을 이용해서 프로그래밍 언어를 개발할 리도 없다고 생각하고요.
    그리고 또 한가지 국내 최대 쇼핑몰 중의 하나인 지마켓이나 옥션이 모두 asp.net 기반으로 돌아가는 쇼핑몰입니다. 이런 대형 쇼핑몰에서 아무런 문제없이 사용하고 있는 asp.net이 서버 측 성능상의 문제로 트래픽 때문에 서버가 중지된 적이 있다는 소리는 아직 한번도 안들어 본 것 같습니다만....
    웹 개발을 하는 사람이 옥션이나 지마켓보다 훨씬 더 큰 대규모의 사이트를 개발하고 있다면 모를까,단지 서버측의 부하가 좀 생긴다고 해서 여러 장점을 가진 서버 기술을 사용하지 못할 이유는 전혀 없는 것 같습니다.
    그리고 데이터베이스와 연동하는 서버측 기술도 MS에서 제공하는 것이 데이터가 계속 주고 받아야 하는 서버에 상당히 부담을 주는 연동 기술도 있지만 dataset 과 같이 서버의 자원을 가져온 다음에 연결을 끊어 주는 다양한 기술들이 있는데 이런 것만 적절히 사용해도 현재의 컴퓨터 환경에서는 그리 큰 문제가 될 것이라고는 전혀 생각이 안 듭니다.

    • 땡초 POWERUMC 2015.01.29 12:23 신고 Address Modify/Delete

      내용을 모두 이해하시지 못하신 것 같아 재차 설명을 드립니다.

      네트워크 오버헤드를 설명하기 위해 ASP.NET 이 랜더링하는 HTML 의 양을 설명한 것입니다.

      그리고 ASP.NET 을 사용하는 것은 성능 문제가 아니라, ASP.NET + 서버 컨트롤을 사용한 경우 성능 문제를 야기한다는 것입니다.
      이유는 물론 글에 설명되어 있습니다.

      그리고 옥션과 지마켓이 ASP.NET 웹 개발 프레임워크를 쓰는 것이지, ASP.NET + 서버컨트롤 방식을 사용하지는 않습니다.
      ASP.NET + 웹폼 방식으로 개발하면 기대하는 성능을 얻을 수 있습니다.
      최근 유행하는 ASP.NET MVC 도 웹폼 방식이고요.



들어가기 앞서…
 
ASP.NET 을 책을 통해 입문하게 되면, 처음 접하게 되는 것이 바로 서버 컨트롤 입니다. 그리고 MSDN 에서도 서버 컨트롤을 남용하면 웹 사이트의 성능을 저하시킬 수 있다고 말합니다. 이러한 서버 컨트롤을 사용하여 개발하는 방법을 ASP.NET 의 서버 모델이라고 합니다. ASP.NET 의 서버 모델은 웹 개발에 있어서 정말 편리하고 복잡한 처리를 단순화 시킵니다.
 
우리가 ASP.NET 을 처음 입문하면 포스트백(Postback) 이라는 용어를 듣습니다. 왜 기존의 서밋(Submit) 이라는 용어를 쓰지 않고, 독자적인 포스트 백(Postback) 이라는 용어를 사용할까요? 궁금하지 않으십니까?
 
ASP.NET 개발자들은 왜 서버 컨트롤이 웹 사이트의 성능을 저하시키는지 잘 알고 있는 사람은 드문 것 같습니다. ASP.NET 의 서버 컨트롤을 사용하는 서버 모델은 ASP.NET 의 성능을 저하시킵니다. 왜일까요?

 
ASP.NET 서버 모델이란?
ASP.NET 의 form 에 runat=”server” 를 사용하는 단일 Form 개발 방법입니다. 단일 Form 개발 방법은 서버 컨트롤을 사용할 수 있게 하며, 이러한 컨트롤에게 자동적으로 상태 유지 기능을 제공합니다.
 
HTML Form 모델이란?
ASP.NET, ASP, PHP, JSP 과 같이 다중 Form 을 이용하여 개발하는 기본적인 웹 개발 방법입니다.
 
MS MVC Framework != HTML Form 모델
HTML Form 개발 방법을 MS MVC Framework 과 동일시 하는 경우가 있는데, HTML Form 과 MS MVC Framework( 또는 MVC) 는 전혀 관련이 없습니다. 서버 모델에서도 MS MVC Framework (또는 MVC) 를 적용할 수 있으며, 윈폼 등 다양하게 적용 가능 합니다.
 
 
서버 컨트롤이 왜 느리죠?
 
서버 컨트롤은 ASP.NET 의 이벤트 기반의 프로그래밍을 가능하도록 하기 위해 독자적인 이벤트를 제공하고, 별도의 랜더(Render) 프로세스를 가집니다. 즉, 서버 컨트롤이 화면에 표시되기 위해 Render 메서드를 재정의(Override) 하여, 화면에 표시되는 HTML 을 생성합니다. 단순한 Label 컨트롤 마져 HTML Span 으로 랜더링되고, 많이 사용하는 그리드 관련 컨트롤은 table 태그를 사용하여 랜더링 합니다.
단순히 HTML Form 모델이라면 바로 스트림에 쓴 후 클라이언트에게 전달할 수 있지만, 서버 컨트롤은 이 과정에서 개별 컨트롤을 랜더링을 하게 됩니다.
 

[그림1] 서버 모델의 랜더링 과정
 
이 랜더링 하는 과정이 새발의 피보다 적은 시간이지만 웹 서버는 결코 혼자 쓰는 서버가 아니라는 점을 명심하세요.
 
이것은 서버 컨트롤을 사용하는 서버 모델의 근본적인 원인이 되어 추후에도 지속적으로 문제가 발생하게 됩니다.
 
 
서버 컨트롤은 적절하게 사용하면 되지 않나요?
 
ASP.NET 서버 모델에서 사용하는 환경에서 적절하게 사용한다는 판단은 팀 개발 환경에서 무척이나 주관적입니다. 이러한 판단은 개발자 주관에 맡길 수 밖에 없으며, 반드시 판단의 기준을 지키리라는 보장을 할 수 없습니다. 더 중요한 것은 적절하게 사용하는게 중요한 것이 아닙니다.
 
서버 컨트롤은 화면에서 사용한 개수도 중요하지만, 서버 컨트롤이 갖는 데이터가 어떤 것인지도 중요합니다. 단 하나의 서버 컨트롤만 사용하더라도 많은 양의 뷰 스테이트(ViewState) 를 갖게 된다면 더 이상 서버 컨트롤의 개수는 중요하지 않습니다.
 
 
뷰 스테이트(ViewState) 의 양이 많으면 왜 느리죠?
 
ASP.NET 의 서버 컨트롤은 상태 유지를 위해 ViewState 를 생성합니다. 뷰 스테이트는 HTML 로 랜더링될 때 히든 필드(Hidden Field) 에 그 값을 저장하고 클라이언트로 보냅니다.
서버 컨트롤을 랜더링 하고 또 상태 유지를 위해 추가적인 개별 컨트롤에 대해 객체 직렬화 과정을 거치게 됩니다.
 

[그림2] 상태 유지를 위한 뷰 스테이트(ViewState)
 
이러한 직렬화 과정이 소량의 데이터일 때 빠르다고 하더라도 데이터의 종류와 양에 따라 수십에서 수백 밀리 세컨드(Milliseconds) 가 느려집니다. 그리고 다시 한번 강조하지만 웹 서버는 혼자 쓰는 서버가 아니라는 점을 생각하면 이 과정이 누적되면 결코 짧은 시간이라고 장담할 수 없습니다.
 
 
컨트롤의 EnabledViewState 를 사용하지 않으면 되지 않나요?
 
특히 그리드 관련 컨트롤은 엄청난 양의 뷰 스테이트(ViewState) 를 생성합니다. 그리고 그리드안에 하위 컨트롤이 추가적으로 들어가있다면 거의 쥐약이나 다름없습니다. 그러므로 컨트롤에 대해 EnabledViewState 를 False 로 설정함으로써 ViewState 을 생성하지 않도록 지정할 수 있습니다.
 
하지만 이것은 근본적인 해결책이 될 수 없습니다. 그리드 컨트롤이 생성하는(기타 서버 컨트롤) HTML 은 컨트롤의 Render 메서드를 통해 HTML 코드를 생성한다고 하였습니다. 만약 tr, td 태그를 한 줄에 배치함으로써 네트워크 트래픽을 최소화 할 수 있지만 서버 컨트롤을 사용하면 이러한 HTML 코드를 직접적으로 제어할 수 없게 됩니다.
 

[그림3] 서버 컨트롤의 랜더링 코드를 제어할 수 없음
 
그리고 EnabledViewState 를 수동적으로 임의로 수정하게 되면 개발의 복잡성이 증가하고 유지 보수성이 저하됩니다. EnabledViewState 속성을 False 로 설정하면 더 이상 상태 유지 기능을 사용할 수 없기 때문에, 코드 비하인드의 로직 마저 수정되어야 합니다. 이러한 문제로 지속적인 성능 개선을 위해 여러 컨트롤에 거쳐 EnabledViewState 를 수정하게 된다면 개발의 복잡성과 유지 보수성 저하와 더불어 추가적인 리소스가 소요됩니다.
 
 
그럼 생산성을 포기하나요?
 
ASP.NET 은 이러한 상태 유지를 자동으로 처리해 줍니다. 그리고 서버 컨트롤의 이벤트 등을 사용하여 보다 빠른 생산성을 보장할 수 있다고 합니다.
 
정말일까요? 단지 이러한 이유 때문에 생산성이 향상됨을 느끼시나요?
 
서버 컨트롤의 그리드를 예로 들게 되면
 
1.     추가적인 디자인 작업이 소요됩니다.
디자이너에 의해 작업된 디자인 HTML 은 다시 한번 서버 컨트롤로 변환되어야 합니다. 차라리 디자이너에게 ASP.NET 의 서버 컨트롤 사용 방법을 알려주는 것이 편할 때도 있고, 실제로 이렇게 하기도 합니다. 하지만 디자이너에게 서버 컨트롤을 강요할 의무가 없으며, 디자이너가 서버 컨트롤을 사용하게 되면 디자이너의 원하는 디자인 작업이 힘들어 집니다.
 
특히 그리드 관련 컨트롤은 디자인된 HTML 을 그리드 관련 컨트롤로 옮기기 위해 더 머리가 아픕니다. 그리드 컨트롤이 개별적으로 랜더링 하기 때문에 1px 이 오차 나거나 디자이너가 원하는 디자인이 아닌 경우가 허다합니다.
 
2.     빌드 하기 전에 오류를 알아낼 수 없습니다.
랜더링 되는 그리드의 결과물이 조건에 따라 변경되어야 한다면, 대부분 그리드의 이벤트를 연결하여 작업합니다. 그리드에 랜더링 되는 상태 데이터를 가져오기 위해 추가적인 코드가 필요합니다.
그리고 이러한 방법은 코드를 다시 빌드하기 전에는 오류의 원인을 알 수 없기 때문에 추가적인 빌드 작업이 필요합니다.
이러한 빌드 작업은 웹 프로젝트의 어셈블리가 변경이 되며 IIS 를 리스타트 하도록 하여 웹 프로젝트 어셈블리가 다시 메모리에 적재됩니다. In-proc 세션을 사용하는 경우라면 세션마저 끊겨져 버립니다.
 
ASP.NET 의 서버 컨트롤 생산성 향상은 단편적이고 제한적인 경우에만 체감적으로 느낄 수 있으며, 상태 유지 또한 HTML Form 에서 그리 복잡한 작업은 아닙니다.
 
아래의 링크에 HTML Form 모델에서의 상태 관리 방법에 대한 글을 보시기 바랍니다.
 
 
그럼 성능 개선 방법은 없나요?
 
있습니다. 웹 사이트 최적화 기법을 통해 사용자의 최종 응답 속도를 향상시킬 수 있습니다. 만약 현재 운영하는 사이트의 성능이 문제가 된다면 다양한 튜닝 포인트를 통해 사용자의 응답 속도를 향상시킬 수 있습니다. 실제 이러한 개선 방안은 많은 서비스 또는 포털이나 도메인(Domain) 에서도 즐겨 사용하는 방법입니다.
 
하지만 이러한 방법은 서버 모델을 최적화하는 관점으로서 HTML Form 모델과 다시 비교하게 되면 더 나은 성능을 보장할 수 없고 근본적인 ASP.NET 의 성능과는 무관합니다.
 

[그림4] ASP.NET 서버 모델의 최적화 한계
 
이러한 최적화 기법을 통해 기존의 성능을 향상시킬 수 있지만, ASP.NET 의 성능 문제는 여전히 해결할 수 없습니다.
 
 
응답 속도가 향상 되었는데 또 뭐가 문제죠?
 
ASP.NET 의 서버 컨트롤은 사용의 편의성과 함께 자동적으로 상태 유지할 수 있는 크나큰 장점이 있습니다. 이러한 문제는 결국 뷰 스테이트(ViewState) 문제이고, 최적화 기법을 통해 기존 성능을 향상할 수 있습니다.
 
하지만 ASP.NET 의 상태 유지는 굉장히 위험한 요소를 가지고 있습니다. 이것은 ASP.NET 의 서버 모델은 단일 Form 밖에 사용할 수 없기 때문에 나타나는 현상입니다. 즉, 포스트 백(Postback) 이 원래 상태로 돌린다는 의미로써 이런 포스트 백(Postback) 처리를 하기 위해 상상할 수 없는 만행을 ASP.NET 이 저지르고 있습니다. 

ASP.NET 의 서버 모델과 HTML Form 모델의 랜더링된 HTML 코드는 상이하게 다릅니다.
 

[그림5] 서버 모델과 HTML Form 모델의 HTML 코드 비교
 
 
자! 뭐가 문제인 것 같나요?
 
서버 모델은 포스트 백(Postback)이 발생할 때마다 form 내에 존재하는 모든 요소를 다시 서버로 전송합니다. 즉, ViewState 가 전송되고 다양한 HTML 태그의 name 속성이 설정된 모든 요소들이 서버로 전송됩니다. ( 대부분의 서버 컨트롤의 명령이 처리되는 컨트롤은 name 속성이 포함됩니다 )
 
즉, 화면의 변경 전의 상태를 ViewState 에 기록하고, 변경 내용을 처리하기 위해 name 속성이 들어간 요소를 전송합니다. 즉, 변경 전, 변경 후 처리를 위해 엄청난 양의 데이터를 서버로 POST 로 전송합니다. 그리고 이러한 ViewState 를 다시 객체로 변경하기 위해 역직렬화 과정을 거치게 됩니다.

Posted by 땡초 POWERUMC

댓글을 달아 주세요

  1. 고래상어 2016.05.27 09:51 Address Modify/Delete Reply

    우연히 보게 되었고 참으로 흥미롭게 읽었습니다.^^
    ASP.NET 개발자인데 부정적인 내용이 많아서 나름의 의견을 적어보았습니다.

    ASP.NET의 패러다임은 웹개발을 CS처럼 하는 것입니다.
    이 편리함이라는 장점을 위해 서버 및 네트웍 자원을 더 많이 사용해야 하는 단점이 있습니다.
    이는 아담의 갈비뼈로 이브를 얻은 것에 비유하고 싶습니다.

    만일 성능이나 트래픽 때문에 ASP.NET이 문제라면 그 기능을 안쓸 수도 있습니다.
    서버태그와 뷰스테이트를 배제하고 기존 ASP나 java servlet과 유사한 구조로 개발하는 것이죠.
    물론 이렇게 해도 java servlet이 성능면에서 더 좋긴 합니다만 넘어가도 될 수준일 듯 하네요.

    결론적으로 ASP.NET 구조는 실패작이 아니라고 말하고 싶습니다.
    편의성과 신속한 개발을 원한다면 ASP.NET을, 성능이 중요한 대규모 사이트 개발이라면 java를 선택하면 것 같습니다.

  2. Pluginn 2018.01.11 16:28 Address Modify/Delete Reply

    잘읽었습니다. 저는 오히려 공감합니다.

    닷넷이 첨나오고 열정적을 공부했던사람으로서,,,

    말씀대로 서버모델 HTML모델, viewstate 등 사용가능하며, 선택 또한 가능합니다..

    문제는.... MS 가 첨부터 서버모델 방식을 너무 지향했다는거죠,
    기존에 HTML 모델 방식보다 닷넷의 서버모델을 따라라...

    HTML 모델방식에 익숙한 개발자로써는, 새로울수도있지만 , 상당히 불편했쬬,,

    또 한 윗글대로 좋을께 하나도 없습니다.


    서버모델방식이, 개발 생산성에서.. 빠르고 , 쉽고 뭐든 좋타고 떠들던 MS 결국 어떻게 되었나요?

    아니다 싶으니, 그냥 급하게 빨리만들어야하는,, 유저 접근 커넥션이 제한적인,,,,
    그래도 백단 만들기에는 편하다,,
    백단을 모두 서버 컨트롤로 만들기 시작했죠,, 왜? 그냥 서버 컨트롤 넣고 더블클릭해서,,,

    코드 넣으면되니까요.. 그러다보니 디테일하게 화면을 구성하기가 복잡해짐,,
    디테일하게 지원하기위해서는 서버 컨트롤에 무한한 속성을들 버전 올리때마다 MS 는 추가해야겟죠

    아니면 서버컨트롤 + 잡기술로 접목 = 결국 코딩이 지저분해짐
    (이는 웹이 CS 프로그램 처럼 서버 단 기술로만 이루어진게 아니기때문이죠_

    비하인드에서 자바스트립을 출력하기위해 서버단 스파게트 코드? 를 만들게 되고,,,
    클라이언트 단에서는. (물론 화면만 깨지지 않으면 되긴하지만) 소스 보기하면 개판이고,,,

    서버단에서 SCRITP ,CSS 지원하려니 MS도 머리터졌겟죠
    지금 결국 각 파트별로 전문적인 프레임워크(정확히는 프레임워크가 아니죠) 장착해서 쓰는듯 하네요

    MS 는 뭐든, 쉽고 ,편하고 , 좋게 만들려고합니다..
    (나쁘게 말하면 본인들이 만든 테두리안에서 편리함을 맛보면 나갈수 없게..)

    기존에 CS 들은가능했으리라 봅니다,
    하지만 웹은 다르죠 엄청 나게 바뀌는 패러다임들을... 따라가려면 버전업을 1년에 2번은 해야할듯,,,

    업그레이드 하면,, 하위호환은 안되며,
    Jquery 를 능가하는 script 프레임워크 node.js 능가하는 서버스크립 ajax 도 잡아야하고
    flash도 다 넣어야하고,,앱 개발도 가능하고,


    MS 서술대로 쓰면 asp.net 은 실패가 맞고요,,

    .net 에.. 나름 본인이 필요한거 빼고 넣고 해서. 쓰면.. 성공이 맞구요,,

Microsoft Chart Controls 가 릴리즈 되었습니다. .NET Framework 3.5 SP1 에서 동작하며 Windows Forms 과 ASP.NET 과 AJAX 를 지원합니다.
 
[그림1] Windows Forms 에서 Chart Controls 을 사용한 화면
 
아래의 ASP.NET Samples 링크에 가보시면 Web Forms 과 Windows Forms 에서 사용하기 위한 샘플을 다운로드 받으실 수 있습니다.
 
상용 차트 컴포넌트와 비교할 대상은 아니지만, 개인적으로 사용하기에 큰 지장은 없어 보입니다.
 

Microsoft Chart Controls Downloads

 

Microsoft Chart Controls References

 

References

 
Posted by 땡초 POWERUMC

댓글을 달아 주세요

외부 라이브러리에서 Javascript 인텔리센스 활성화 하기
 
Visual Studio 에서 추가된 기능입니다. 기존에 html(aspx) 페이지에서 <script src=””> 블록을 통해 Javascript 인텔리센스 기능이 제공이 되었지만, 여전히 문제였던 것은 Javascript 파일을 작성할 때, 외부 Javascript Function 의 인텔리센스 기능이 제공이 되지 않았습니다.
 
하지만, Visual Studio 2008 을 설치하시면 외부 Javascript Function 을 인텔리센스 기능으로 사용하실 수 있습니다.
 
크게 설명 드릴것도 없이 아래의 스크린샷 처럼 <reference> 주석을 통해 외부 Javascript Function 의 인텔리센스를 사용하기 위해 Import 할 수 있습니다.
 
[그림1] Jscript1.js 에 Function 이 선언된 모습
 
[그림2] Jscript2.js 에서 Jscript1.js Function 을 인텔리센스 기능으로 사용하는 모습
 
Reference
 
PS) 위 기능은 Visual Studio 2008 에서 추가된 기능인데, Reference 의 블로거는 Visual Studio SP1 에 추가된 기능인 것처럼 설명하네요. 참고하세요.

Visual Studio 2008 SP1 Adds JavaScript Formatting
http://weblogs.asp.net/kencox/archive/2008/08/13/visual-studio-2008-sp1-adds-javascript-formatting.aspx
 
만족합니다.
하지만, 여기에서 저는 한가지 고민을 하게 되었습니다.
 
 
그럼, Web Resources 스크립트는 어떻게 하나요?
 
이 문제로 약 하루 반나절 정도 생각을 해봤습니다. -_-; 물리적으로 URL 를 통해서 Javascript 파일에 접근할 수 있을 경우 위와 같이 사용할 수 있지만, Web Resources 는 이러한 URL 경로가 없기 때문에 위의 기능을 사용할 수 없습니다. 이 문제에 대해서 구글링을 해봐도 Web Resources 를 외부 Javascript 에서 Import 할 수 있는 방법을 없는 것 같습니다.
 
그래서 결론으로 아래와 같이 사용하시면 됩니다.
 
/// <reference path="C:\Documents and Settings\...경로생략...\JScript1.js" />
 
팁이라면 팁이 되겠네요. Web Resources 의 경우 큰 고민마시고, 파일 경로를 쓰시면 될 것 같습니다.
쩝…

2008-09-03 UPDATE ----------------------------------------------------
아래 댓글 달아주신 남정현님 말씀처럼
/// <reference name="xxx" assembly="xxx"/>

이런 방법이 있었네요^^ 감사합니다.

http://blogs.msdn.com/webdevtools/archive/2007/11/06/jscript-intellisense-a-reference-for-the-reference-tag.aspx 

근데 잘 되지 않는군요;; 쩝^^;

Posted by 땡초 POWERUMC

댓글을 달아 주세요

ASP.NET 용 Virtual Earth Server Controls 가 “Windows Live Tools for Microsoft Visual Studio” 라는 이름으로 Release 되었습니다. 기존에 Virtual Earth 를 사용하기 위해 많은 상당히 많은 Javascript 작업을 노가다(?)를 하였으나, 이제는 그럴 필요가 없어 졌네요.
 
간략하게 주요 기능을 살펴보자면,,
l ToolBox
l Drag & Drop
l Server Sides Event
l Adding Shapes
 
더 갖다 붙이자면 많지만, Server Control 로 포팅되면서 Server Control 이 갖는 모든 기능을 갖추었다고 보시면 됩니다.
 
설치를 하고, 웹 프로젝트를 만들면, 제일 먼저 ToolBox 에 다양한 Server Control 들이 추가 됩니다.

[그림1] Windows Live Tools for Microsoft Visual Studio 설치 후 ToolBox
 
하지만!!아래와 같이 더 이상 진행할 수 없는 지경에 이루게 되었습니다.


[그림2] 웹폼에 Virtual Earth Controls 을 추가할 경우 오류 메시지
 
이건 뭐지? 처음에는 영문판 Visual Studio 에서만 되는줄 알았습니다. 그래서 Preview 를 때려치려고 하였지만, 방법은 있더군요.
 
시도방법이 Assembly 를 추가하여, 코드를 통해 Control 을 웹폼에 얹으려고 하였으나, 작동이 되지 않더군요^^; (뭐.. 코드가 잘못되었겠죠?? ㅡ,.ㅡ)
원래, ToolBox 에 등록된 Server Control 은 Drag & Drop 를 하게되면, 페이지 상단에 <%@ Register … %> 지시어가 자동으로 참조가 추가 되면서 컨트롤이 웹폼에 추가됩니다만,, 코드로 Control 을 추가하려고 강제로 참조를 추가해 주니, 웹폼 디자이너에서 Drag & Drop 이 지원되더군요..
 
결국 직접 참조를 추가한 후에, ToolBox 의 Server Control 을 추가하시면 된다는 말이군요 -_-;
 

[그림3] 직접 사용자가 참조를 추가해 주어야 Drag & Drop 이 가능하다.
 
아무튼, Virtual Earth 관련 Extender 와 Window Live 관련 Controls 이 존재하지만, 직접 돌려보니 Virtual Earth Server Control 을 제외하고는 별로 볼 것이 없더군요.
 
재미있는 것은 Virtual Earth 3D 를 설치하면, 3D View 로 전환도 가능합니다. 아무튼 재미있는 컨트롤이 나오게 되어 잘 구경하다가 갑니다 ^^;
 

Posted by 땡초 POWERUMC

댓글을 달아 주세요



지난번 아티클의 HttpCompression 소스 코드를 공개합니다.
 
아직 개선의 여지는 있지만, 필요하신 분들이 많으신 줄 압니다. 코드를 수정하셔서 사용하셔도 무방하나, 상업적 용도나 재배포가 불가능 하오니, 이점 숙지하시기 바랍니다.
 
실제 적용하기 위해서 Umc.Core.Configuration 어셈블리가 필요하지만 조금 수정하시면 자신의 사이트에 맞게 수정하시면 Umc.Core.Configuration 어셈블리는 필요하지 않습니다. 이 부분에 대해선 문의를 받지 않습니다.
 
현재는 매뉴얼이 제공되지 않습니다.

Posted by 땡초 POWERUMC

댓글을 달아 주세요




 
3. 문제 해결
 
이전 아티클에서 살펴보았듯이, ASP.NET 에서 보다 효과적인 HTTP Compression 을 위해 WebResource 와 ScriptResource 의 압축의 필요성을 알아보았습니다.
 
그럼 하나하나 해결의 실마리를 풀어 보도록 하겠습니다.
 
3.1 WebResource 압축
 
WebResource 는 포함 리소스로 지정된 파일과 WebResourceAttribute 을 통해 리소스의 콘텐츠를 사용할 수 있습니다.
 
 


 
첫번째, 일반적으로 압축 되지 않은 콘텐츠이기 때문에, 임의로 GZip 알고리즘을 이용하여 압축된 바이너리를 사용하여 Response Header 의 Content-Encoding 을 Gzip 으로 해 주셔도 됩니다. 하지만 불편하겠죠?
 
두번째, 요청 파일명이 WebResource.axd 인지를 검사하여, GZip 압축을 수행할 수 있습니다.
 
세번째, ASP.NET 파이프라인에서 요청에 대한 HttpHandler 타입을 검사하는 방법입니다.
WebResource 는 IHttpHandler를 구현하는 System.Web.Handlers.AssemblyResourceLoader 클래스입니다. 그래서 Context 개체의 CurrentHandler 의 개체 타입을 검사하는 방법으로 WebResource 인지 판단할 수 있습니다.
 
하지만, 문제는 여기서 그치지 않습니다. 만약 리소스가 image/jpeg 라고 가정할 때, 이러한 검사방법으로 압축하였음에도 Content-Encoding 이 text/html 로 내보내지게 됩니다. 이미지가 깨진다는 말이죠.
 
구글링을 통해 알아본 봐, 아래와 같은 코드를 사용하면 됩니다. 대략, HttpHandler 에게 현재 요청의 ASP.NET 파이프라인의 Response 개체를 참조하도록 하여, 포함 리소스의 Content-Type 을 참조되도록 한 코드인 듯 합니다.
 
// 맴버 선언
private static FieldInfo _HttpWriterFieldInfo = typeof(HttpResponse).GetField("_httpWriter", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
 
private static FieldInfo _IgnoreFurtherWritersFieldInfo = typeof(HttpWriter).GetField("_ignoringFurtherWrites", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
 
if (context.CurrentHandler is System.Web.Handlers.AssemblyResourceLoader)
{
           HttpWriter httpWriter = (HttpWriter)_HttpWriterFieldInfo.GetValue(context.Response);
       _IgnoreFurtherWritersFieldInfo.SetValue(httpWriter, false);}
 
위의 코드를 사용하여 WebResource 의 정상적인 Content-Type 내보낼 수 있게 됩니다.
 
 
3.2 ScriptResource 압축
 
ScriptResourceAttribute 은 System.Web.Extensions 어셈블리에 정의되어 있고, 이름에서 알 수 있듯이 는 AJAX.NET 이 참조하는 스크립트 리소스 입니다. ScriptResource 는 WebResource 로 정의된 포함 리소스를 ScriptResource 로 변환하여 줄 수 있습니다.
 
MSDN 예제에 다음과 같이 사용하라고 나와 있습니다.
 
[assembly:System.Web.UI.WebResource("LocalizingScriptResources.CheckAnswer.js", "application/x-javascript")]
[assembly:System.Web.UI.ScriptResource("LocalizingScriptResources.CheckAnswer.js", "LocalizingScriptResources.VerificationResources", "Answer")]
 

 
ScriptResource 는 Context.ApplicationInstance.Modules 컬렉션에서 로드된 HttpModule 을 확인할 수 있습니다.
 
ScriptResource 는 ScriptResourceHandler 를 구현한 Handler 이며, 다음과 같은 코드로 타입을 알 수 있습니다.
 
if (context.CurrentHandler is System.Web.Handlers.ScriptResourceHandler)
{
       // 처리
}
 
하지만, 위 방법은 그다지 권장하지 않습니다. AJAX.NET 을 사용하지 않는 웹 프로젝트도 타입 검사를 위해 System.Web.Extensions 어셈블리를 참조를 한다는 것은 그다지 좋은 방법이 아니기 때문에, Type.GetType() 을 이용하여 타입 검사를 하는 것이 바람직 합니다.
 
ScriptResource 를 압축하기 위해 WebResource 보다 좀 더 이슈가 많은 놈입니다.
 
해결 방법을 보기 앞서, 이러한 리소스를 호출하는 URL 은 참 독특하게 생겼습니다.
 
http://localhost:11762/ScriptResource.axd?d=OPgKDvRlavpQaVqOD0g1boX7qB-1ghFWxrumbN3vsX9V6HIuwC8ttnL5kS-GN-I8lyEsM4Cv4jlmkKjiWk5FpbGKpocj2jsQpab1931xYKi73VoRqj6NfhWnGtvW1Qk00&t=633419587306805934
 
위와 같이, QueryString 의 d 라는 키값이 가지는 알 수 없는 문자들이 덕지덕지 붙어 있습니다. 이 문자열은 암호화되어 인코딩된 문자입니다. 이 문자열은 System.Web.Page 클래스의 EncryptString 과 DecryptString 이라는 internal 로 선언된 정적 메서드를 통해 암복호화를 수행합니다. 그리고 암복호화에 사용되는 토큰키는 HttpHandler 에 PublicKeyToken 에 정의되어 있습니다.
 
위의 d 쿼리스트링을 복호화 해보면
 
ZSystem.Web.Extensions,3.5.0.0,,31bf3856ad364e35|MicrosoftAjaxWebForms.debug.js|ko
 
이러한 알아봄직한 단어들이 나옵니다. 바로 맨 앞의 Z 로 ScriptResource 가 GZip 압축 되었다는 것을 의미합니다. a u 는 압축이 되지 않았다는 것을 알려주기도 합니다.
 
그리고 t 쿼리스트링은 어셈블리의 DateTime 의 Tick 입니다. 캐시에 동작하기 위해 어셈블리의 날짜를 URI 에 포함됩니다.
 
그럼 어떻게 압축 여부를 판단하는지 알았습니다. ScriptResource 의 압축 여부를 판단하기 위해 다음과 같은 코드를 만들 수 있습니다.
 
string param = context.Request.QueryString["d"];
string decrypt      = HttpUtil.DecryptString(param);
if( decrypt.ToUpper().StartsWith("Z") )
       return;
 
코드는 쿼리스트링의 d 값을 PublicKeyToken 을 이용하여 복호화하여 Z 로 시작하면 이미 압축된 콘텐츠 이므로, 압축을 수행하지 않도록 return 하는 코드입니다.
 
만약 위의 코드로 ScriptResource 의 이미 압축었는지의 여부를 체크하지 않으면, 서버는 압축된 콘텐츠를 다시 한번 GZip 압축하기 때문에, 브라우져는 GZip 압축을 해제하였지만 콘텐츠는 깨져버립니다. ( 두 번 압축하였으니, 두 번 풀어줘야 정상적인 콘텐츠가 되겠죠? )
 
 
4. 웹 사이트에 진정한 날개를…
 
자신이 개발하거나 운영하고 있는 웹 사이트가 있고, AJAX.NET 이나 다양한 컴포넌트를 이용하였다면 반드시 웹 캡춰 툴을 이용하여 확인해 보시기 바랍니다. 그리고, 자신이 그러한 웹 사이트를 개발하거나 관리한다면 위의 방법을 이용하여 적용해 보시기 바랍니다. text/html 이나 text/javascript 와 더불어 트래픽의 부하를 가져오는 다량의 WebResource 와 ScriptResource 가 가벼워지면서 획기적으로 줄어든 트래픽을 확인할 수 있을 것입니다.
 
그리고, 직접 개발하기 벅차신 분을 위해 개발중인 Umc.Core.Web.HttpCompression 어셈블리를 함께 드리도록 하겠습니다. 전체적으로 개발이 완료되지 않았으므로 테스트 용도로만 사용시면 됩니다. (어떠한 버그도 책임지지 않는답니다^^;)
 
아래의 내용을 각각 맞는 Config Section 에 추가해서 넣으시면 동작합니다. 그럼 오늘도 좋은 하루 되세요 …
 
<configSections>
       <sectionGroup name="Umc.Core" type="Umc.Core.Configuration.UmcConfigSectionGroup, Umc.Core.Configuration">
             <section name="Umc.Core.Web" type="Umc.Core.Configuration.UmcWebConfigSection, Umc.Core.Configuration" />
             <section name="Setting" type="Umc.Core.Configuration.UmcSettingConfigSection, Umc.Core.Configuration" />
       </sectionGroup>
</configSections>
 
<system.web>
       <httpModules>
             <!-- HttpModule 매핑-->
             <!--<add name="UmcHttpModule" type="Umc.Core.Web.UmcHttpModule, Umc.Core.Web" />-->
             <add name="UmcHttoCompressionModule" type="Umc.Core.Web.HttpCompression.HttpCompressionModule, Umc.Core.Web.HttpCompression" />
       </httpModules>
</system.web>
 
<Umc.Core>
       <!-- DefaultExtension 웹폼이사용하는기본확장자입니다.-->
       <Umc.Core.Web DefaultExtension=".aspx">
             <RewritePath Enabled="True" /> <!--웹사이트의 rewritepath 적용할지여부-->
             <HttpCompressionFile>
                    <add Name="/\.(gif|jpg|jpeg|png)$/i" Type="GZip" Enabled="False"  NameType="RegexExpression"></add>
             </HttpCompressionFile>
             <HttpCompressionMime>
                    <!--디폴트속성들입니다-->
                    <add Name="text/html" Type="GZip" MinimumLength="512" Enabled="True"/>
                    <add Name="text/css"/>
                    <add Name="text/javascript"/>
                    <add Name="application/x-javascript"/>
                    <add Name="image/bmp"/>
             </HttpCompressionMime>
       </Umc.Core.Web>
</Umc.Core>
 
 
참고 URL
 
Posted by 땡초 POWERUMC

댓글을 달아 주세요



 
 
1.    서론
 
HTTP Compression 은 웹사이트의 응답 성능을 향상 시키는데 많은 일조를 합니다. 압축 정도와 데이터의 타입에 따라 압축율은 다르지만, 최소 30% 이상의 체감 속도의 향상을 가져온다고 합니다. 제가 운영하고 있는 [Umc Projects/Umc Blog] - [UmcBlog] 블로그 소스 다운로드 의 메인화면의 HTML 만해도 101,430 Bytes 지만, GZip 압축을 적용한 결과 15,450 Bytes 로 7배 정도로 작아진 트래픽이 발생합니다.
 

[그림1] Umc Blog 메인 화면을 HTTP 압축한 비교표
 
위 압축은 ShartZipLib 를 이용하여 최고 압축율로 압축한 결과 입니다.
자~ 어떻습니까? 이제 HTTP 압축을 자신의 사이트에 도입해 보려고 한다면, 우선 다음의 강좌를 참고하여 선수 지식을 익히시기 바랍니다.
 
최고의 가이드 문서인 유경상님의 HTTP 압축 (1) : 성능 향상을 위한 다른 접근 기법
서동진님의 [1000원짜리] GZip 이용한 웹 페이지의 압축 전달
 
제가 아는 포스팅은 이것 뿐인지, 아니면 제가 못 찾는 것인지 모르겠지만, 어쨌든 왜 써야 하며, 어떻게 구현하는지는 보셔야 다음 내용을 이해할 수 있습니다.
 
 
2.    문제 제기
 
위 내용 중 특히 유경상님의 문서에서 볼 수 있듯이 HTML 과 XML Web Service 에서 압축방법과 압축해제 방법을 너무나 잘 설명해 주셨습니다. (쵝오!) .NET Framework 가 버전업이 되면서 편리한 API 와 옵션들이 추가되어 깊은 이해보다 코드 한 줄로 해결할 수 있는 방법들이 너무나 다양해 졌습니다.
 
좋습니다. 어찌되어건, HTTP Compression 은 서버의 CPU 가 가져가는 부하가 경미하기 때문에 보다 훨씬 많은 이점이 있습니다. 그렇기 때문에, 웹 사이트는 특별한 상황이 아니라면 HTTP Compression 을 활용하도록 해야 합니다.
 
IIS HTTP Compression 을 사용하지 말아야 할 때
IIS 서비스 압축은 기본적으로 DLL 도 GZip 압축 하도록 되어있습니다. 스마트클라이언트 시나리오에서는 클라이언트가 GZip 압축된 어셈블리(*.dll) 등을 다운로드 하였을 때 다운로드 된 클라이언트는 GZip 압축을 복호화 하지 못합니다. 유경상님의 HTTP Compression (III) : IIS Support 을 참고 하세요.
 
자.. 문제는 여기서 부터입니다. HTTP Compression 의 구현은 쉽지만, HTML(CSS/JS) 이나 XML Web Service 에 국한된 포스팅 입니다. 특히, 클라이언트 어플케이션일 경우 의 GZip 압축의 범위가 XML Web Service 에 국한됩니다. 하지만, 불특정 다수의 웹 서비스 사이트를 생각한다면 HTML Compression 만으로 부족한 것을 알 수 있습니다.
 

[그림2] HTML 뿐만 아니라 다양한 데이터를 클라이언트는 내려받습니다 (클릭하면 확대 됩니다)
 
우리가 웹 사이트를 접속하였을 때, 보여지는 것은 HTML/CSS/JS 만 있는 것이 아닙니다. 수십 개의 콘텐츠 중 고작, 10여개의 텍스트 용량을 줄였다고 체감속도가 향상 되었다는 것은 억지에 불과합니다.(그나마 안하는 것보다는 나은듯^^;) 왜냐하면, 웹 사이트를 접속할 때 XML, BMP 등과 같은 압축되지 않은 다양한 콘텐츠도 다운로드 되기 때문 입니다.
 
물론, HTTP Compression 을 하느냐 안하느냐도 중요합니다. 하지만 압축이 전부는 아닙니다. 자신이 개발하거나 관리하는 사이트가 이런 문제로 고민을 한다면 다양한 웹 사이트 최적화 기법을 통해서 빠른 응답을 기대할 수 있습니다.
 
웹 사이트 최적화 튜닝 방법
 
ASP.NET 개발 환경을 무척 편리해 졌습니다. .NET Framework 2.0 부터 추가된 WebResource 와 ScriptResource 를 이용하여 컴포넌트 배포시에 별도의 스크립트 파일이 없이도 동작하도록 어셈블리의 리소스에 포함시킬 수 있게 되었습니다. 무척 배포가 쉬워졌지요.
 
하지만, ASP.NET 웹사이트로 구축된 기업의 인트라넷의 경우 다양한 컴포넌트를 사용하여 개발하게 될 때, 수십여 개의 WebResource 와 ScriptResource 를 뱉어내게 됩니다. 이 리소스들은 압축되었다는 보장을 할 수 없고, 실제로 압축되지 않은 리소스들이 더 많습니다. 이런 웹 사이트의 성능 저하 주 원인이 바로 리소스입니다.
 
문제는 여기에서 생깁니다. 어떻게 이 리소스들을 압축하시겠습니까?
AJAX.NET 의 ScriptManager 컨트롤은 3개의 ScriptResource 를 포함합니다. ScriptResource 는 아무런 작업을 하지 않아도 gzip 압축이 되는데, 특정 컴포넌트의 ScriptResource 는 gzip 압축을 하지 않습니다.
 

[그림3] 아무 작업 없이도 ScriptResource 는 압축이 되기도 합니다.
 
특히 ScriptResource 의 경우는 아무런 생각 없이 압축을 수행하다가는 이미 압축된 리소스가 추가적으로 GZip 압축이 되어, 브라우져는 중복 압축된 콘텐츠를 제대로 복호화 하지 못합니다. 결국 사이트는 바보가 됩니다.
 
Next… (다음 아티클)
Posted by 땡초 POWERUMC

댓글을 달아 주세요



[.NET/ASP.NET] - 실전 ASP.NET Session [1] - 쿠키를 이용한 상태관리와 위험성
[.NET/ASP.NET] - 실전 ASP.NET Session [2] - 상태관리의 종류
[.NET/ASP.NET] - 실전 ASP.NET Session [3] - 다양한 세션 관리 방법
[.NET/ASP.NET] - 실전 ASP.NET Session [4] - 세션상태 마이그레이션

 

 

제목에는 세션상태 마이그레이션 이라고 했지만, 보다 구체적으로 언급하면 세션 공급자를 구현하는 단원입니다. 이전에 보았던 아래의 그림과 같이 ASP.NET 이 제공하는 몇 가지의 세션상태 저장소가 있지만, 상황에 따라서 사용할 수 없는 경우도 생길 수 가 있습니다. .NET Framework 3가지의 세션 공급자를 제공해 주지만 어떠한 경우는 이 3가지 모두 쓸 수 없기 때문입니다.

 

[그림1] ASP.NET 이 제공하는 기본적인 세션 공급자

 

만약, ASP.NET 프로젝트가 MS-SQL 데이터베이스 기반으로 구축이 된다면 SQL Server 를 이용한 세션 관리가 가능하지만, Oracle 데이터베이스 기반일 경우 .NET Framework Oracle Session Provider 를 기본으로 제공하지 않기 때문에 세션관리에 이슈가 있다면 프로젝트 착수 단계부터 개발 플랫폼의 선택의 기회에 제외될 수 있기도 합니다. 가령, Oracle / JSP ASP 베이스의 시스템을 닷넷으로 전환하고자 한다면, 세션 서버 구축 때문에 비싼 비용을 지불하여 MS-SQL 을 구입할 수 도 없는 노릇이지요. 또한, 세션에 추가 정보나 필드 등이 추가 되어야 할 경우 기존 상태 저장소는 사용할 수 없게 되기 때문에, 새로운 방안을 모색해야 할 것입니다.

 

ASP.NET 은 이러한 다양한 상태 공급자를 구현할 수 있도록 SessionStateStoreProviderBase 인 추상 클래스를 제공하고 있습니다. 이 클래스는 System.Web.SessionState 네임스페이스에 존재합니다. 우선 이 클래스와 상속 구현을 위해 아래의 MSDN 문서를 참고하시기 바랍니다.

 

세션 상태 저장소 공급자 구현

 

그리고 Access 데이터베이스를 이용한 세션 상태 공급자의 소스를 구현한 MSDN 소스를 참고하시기 바랍니다.

 

법: 샘플 세션 상태 저장소 공급자

 

소스코드가 복잡하지 않기 때문에, 적절한 다른 종류의 데이터베이스로의 전환은 쉽게 하실 수 있으리라 생각합니다.

 

우리가 직접 세션 공급자(Session Provider) 를 구현한다는 것은 많은 의미를 내포할 수 있습니다. 그 중, 가장 의미 있는 것은 이 Custom Session Provider 를 구현함으로써 개발자나 구축하는 프로젝트는 아무런 코드의 수정 없이 데이터베이스 플랫폼의 전환이 가능하다는 것입니다. 단지, web.config 에서 Session Provider 만 지정해주면 됩니다.

 

 

위의 첨부파일은 MSDN 예제의 Access Database Session 을 저장하는 예제인데, MSSQL Database 로 변경한 소스입니다.

web.config 와 테이블의 내용을 보면 다음과 같습니다.

 

[그림2] Custom Session Provider web.config 에 설정

 

[그림3] DB 테이블에 Custom Sessino Provider 가 세션을 저장한 결과

 

위의 web.config connectionString 에 적절한 id password 를 넣어주는 센스만 보여주신다면, 무리 없이 바로 샘플은 동작할 겁니다. ( Database Table 만드는 스크립트는 코드의 주석에 포함되어 있습니다)

 

 

처음 저의 의도와 달리 포스팅을 점점 날로 먹으려고 하는 것 같네요. 원래 이번 포스팅부터 많은 내용을 익혀서 전달해 드리려고 했는데 요즘 Umc.Core 에 올인하고 있기 때문에 포스팅을 성의 있게 쓰기에 시간이 턱없이 부족하네요. 이렇게 올인해도 집에 와서 코딩하면 20 라인 만들기도 정말 벅차답니다. 만들면서 잘못된 설계 때문에 몇 번이나 엎었는지 ^^; “세션 분산처리도 포스팅 최종회에 함께 포함하도록 합니다.

 

다음 포스팅에 실제 SSO 시스템을 적용한 예제도 준비하려고 했지만, 그렇게 하지 못할 것 같아요. 하지만, Custom Session Provider SSO 시스템에 대한 큰 그림과 세부적인 구현방법은 최대한 자세히 기술할 예정이니, 관심 있는 분은 좋은 팁 배워간다 생각하시고 보시면 될겁니다.


Posted by 땡초 POWERUMC

댓글을 달아 주세요



[.NET/ASP.NET] - 실전 ASP.NET Session [1] - 쿠키를 이용한 상태관리와 위험성
[.NET/ASP.NET] - 실전 ASP.NET Session [2] - 상태관리의 종류
[.NET/ASP.NET] - 실전 ASP.NET Session [3] - 다양한 세션 관리 방법
[.NET/ASP.NET] - 실전 ASP.NET Session [4] - 세션상태 마이그레이션

 

 

ASP.NET 은 다양한 세션 관리 방법을 제공하여 줍니다. 서버의 자원은 제한적이기 때문에 In-of-process 방식이 아닌, Out-of-process 의 세션 관리 방법이 필요하다고 이전 시간에 말한바 있습니다. 이런 이유 이외에도, Worker Process 에 의해 세션이 관리된다면 프로세서가 어떤 오류로 인해 종료가 되면 세션도 함께 증발해 버리는 경우도 생깁니다. 이유가 가져다 대면 많지만, 어떤 세션 관리 방법이 있는지 알아보고, 자신의 사이트나 회사의 사이트에 어떻게 적용할 것인지 한번 생각해 봐도 좋을 내용이 될 것입니다.

 

1.    ASP.NET Session State Service

 

ASP.NET Session State Service 라는 서비스가 별도로 존재합니다. 이 서비스는 관리도구->서비스를 통해 프로세서를 시작/종료 할 수 있고, 디폴트로 시작 안함입니다. 이 프로세서는 ASP.NET Worker Process 와는 별도의 프로세서이기 때문에, IIS 가 중단이 되거나 오동작으로 인해 세션이 증발하는 경우는 없습니다.

 

 [그림1] 서비스의 ASP.NET Session State Service

 

위의 [그림1] 에서 시작버튼을 누르게 되면 ASP.NET Session State Service 를 시작할 수 있습니다.

 

우선 여기서, 주의할 부분이 있습니다. ASP.NET Session State Service 를 사용하기 위해서는 processModel 섹션의 webGarden 속성은 반드시 false 이여야 합니다. 갑자기 튀어나온, webGarden 속성은 또 뭐죠?? 이 속성은 다중 프로세서 CPU 에만 적용되는 항목인데 다중 프로세서에서 Worker Process 를 사용할 코어(Core)의 선호도를 지정할 수 있습니다. 만약, ASP.NET Session State Service processModel webGarden 을 함께 설정한다면, 가령 A 라는 세션을 다른 Worker Process 에서 처리를 하는 교착현상(?)이 발생할 수 있습니다.

 

processModel webGarden 참고

<processModel> 구성 문서에 틀린 webGarden 설명과 필드가 포함되어 있다

 

 

webGarden 의 속성을 임의로 바꾸고자 한다면, web.config 에서는 바꿀 수 없습니다. 왜냐하면, machine.config

 

<section name="processModel" type="System.Web.Configuration.ProcessModelSection, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" allowDefinition="MachineOnly" allowLocation="false"/>

 

allowDefinition="MachineOnly" 로 설정되어 있습니다. 이 값을 allowDefinition="MachineToApplication" 으로 변경하면, 웹 응용 프로그램의 web.config 에서 processModel 섹션을 재정의 할 수 있습니다.

 

다시 원점으로 돌아와서, 세선 관리를 ASP.NET Session State Service 로 지정하는 방법은 굉장히 간단합니다.

다음과 같이 web.config 를 변경하면, 세션 상태 관리를 ASP.NET Session State Service 로 지정할 수 있습니다.

 

<system.web>

       <sessionState mode="StateServer"

                      stateConnectionString="tcpip=localhost:42424">

</sessionState>

 

tcpip address IP 도 가능하며, 도메인으로도 가능합니다. 그리고 뒤의 42424 는 디폴트로 지정된 ASP.NET Session State Service Port 입니다. 만약, 로컬 서버가 아닌 원격 서버의 ASP.NET Session State Service 를 사용하기 위해서는 방화벽의 42424 포트를 허용해 주시면 됩니다.

 

ASP.NET Session State Service 가 디폴트로 사용하는 42424 포트를 변경해 줄 필요도 생길 수 있습니다. 포트를 변경해 주는 UI 는 제공되지 않지만, 레지스트리를 변경하여 포트 번호를 바꿀 수 있습니다.

 

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\aspnet_state\Parameters

 

Port= a5b8 (42424)

 

Port 의 값을 16진수로 대응되는 값을 변경하시고, 반드시 ASP.NET Session State Service 재시작 하시면 변경된 포트를 통해 Service 를 이용할 수 있습니다.

 

간혹, ASP.NET Session State Service 를 시작하였음에도 동작하지 않을 경우, 위의 레지스트리 위치의 값을 확인해 보셔야 합니다.

 

AllowRemoteConnection=1

 

 

값이 1 이 아닌경우, 1로 변경하면 정상적으로 ASP.NET Session State Service 를 이용할 수 있습니다.

 

 

2.    SQL Server

 

SQL Server 를 이용하여 데이터베이스를 통해 세션 상태를 유지합니다. 물리적인 디스크를 통해 I/O 가 일어나므로 가장 느린 상태 관리 방법이지만, 세션의 휘발성의 염려가 없는 가장 안정된 서비스를 제공하는 방법 이기도 합니다.

 

우선, 세션으로 사용할 데이터베이스와 그 외 부수적인(테이블/저장 프로시져) 등을 생성해야 하는데, Visual Studio Command Prompt 를 통해 간단하게 생성할 수 있습니다.

 

 

C:\> aspnet_regsql.exe -S <MachineName> -E -ssadd -sstype p

 

 

[그림2] aspnet_regsql.exe 가 생성한 데이터베이스

 

위 명령을 수행하면 [그림2] 와 같이 ASPState 라는 데이터베이스가 생성이 됩니다. 만약 위의 명령줄에서

 

–sstype p

 

를 제거하면 tempdb 에 테이블이 생성 되어 집니다.

 

ASP.NET 1.1 에서 SQL Server 데이터베이스 생성하는 방법

HOWTO: 영구적으로 SQL Server 세션 상태를 관리할 있도록 ASP.NET 구성

 

데이터베이스가 정상적으로 만들어 졌다면, 다음과 같이 web.config 를 변경하여 상태관리 방법을 변경할 수 있습니다.

 

<system.web>

       <sessionState mode="SQLServer"

                      sqlConnectionString="server=localhost; uid=*******; pwd=********* ">

       </sessionState>

 

이전 포스팅에서 언급한 바, Out-of-process 를 통해 세션 상태를 관리하게 되면 Session 개체에 반드시 Serializable(직렬화할 수 있는) 개체만 Session 에 저장할 수 있습니다.

 

3.    구성 섹션을 암호화

 

본 섹션은 Out-of-process 와 관련이 없는 부분이지만, 경우에 따라 민감할 수 있습니다. 왜냐하면, web.config 를 통해 Server Farm 의 접속 정보가 잠재적으로 노출되어 있기 때문입니다. 서버의 .config 가 기본적으로 System.Web.HttpForbiddenHandler 로 지정되어 있어, 외부의 사용자는 web.config 의 내용을 알 수 없지만, 외부 개발자나 시스템 담당자 외에 공개가 되어서는 안되는 경우 구성 섹션을 암호화 하는 방법이 있습니다.

 

이 부분에 대해서는 아래의 MSDN 링크를 통해 보시면 됩니다.

Web.config 암호화

연습: RSA 컨테이너 만들기 내보내기

 

 

너무 포스팅을 쉬었는지 감을 조금 잃어버렸네요~ ^^; 다음 포스팅은 세션상태를 마이그레이션 하는 방법에 대해 보도록 할 예정입니다. MSSQL 로 제한된 세션 상태를 Oracle 에서 이용하도록 변경이 가능하며, 모든 로직을 감추고 분산처리 하는 방법, 이것을 활용한 SSO 시스템의 전반적인 설계까지 알아보도록 할 예정입니다.



Posted by 땡초 POWERUMC

댓글을 달아 주세요



[.NET/ASP.NET] - 실전 ASP.NET Session [1] - 쿠키를 이용한 상태관리와 위험성
[.NET/ASP.NET] - 실전 ASP.NET Session [2] - 상태관리의 종류
[.NET/ASP.NET] - 실전 ASP.NET Session [3] - 다양한 세션 관리 방법
[.NET/ASP.NET] - 실전 ASP.NET Session [4] - 세션상태 마이그레이션

 

 

이 단원은 ASP.NET 의 어느 책을 보아도 나오는 반드시 나오는 챕터이죠. 그만큼 기본적이고 중요한 부분입니다. 왜냐하면 웹이라는 것은 기본적으로 아무런 상태를 저장할 수 없기 때문입니다. 하지만, ASP.NET 을 이용하면 다양한 방법을 통해 상태를 쉽게 할 수 있고, 쉽게 간과할 수 있는 부분을 다시 한번 되새길 수 있도록 구성해 보았습니다.

 

Application 개체

 

이 프로퍼티는 HttpApplicationState 의 인스턴스 입니다. 대부분의 ASP.NET 의 상태관리 개체는 키(Key) 와 값(Value) 이 쌍을 이루는 컬렉션 형태입니다. 따라서 다음과 같이 사용할 수 있습니다.

 

HttpApplicationState app = HttpContext.Current.Application;

app["Key"] = "Value";

 

Response.Write( app["Key"].ToString() );

 

하지만, Page 클래스에 Application 라는 프로퍼티로 정의가 되어 있기 때문에, 다음과 같이 더 간결하게 사용할 수 있습니다.

 

Application["Key"] = "Value";

 

Application 개체는 웹 응용 프로그램의 전역적인 값을 유지할 수 있습니다. 웹 응용 프로그램의 설정이나 세션에 관계없이 전역적인 변수를 저장하는데 유용합니다. 또한, 프로세서가 실행되고 있는 서버의 메모리에 값을 저장하고 있기 때문에 빠르게 데이터를 엑세스 할 수 있습니다. 웹 응용 프로그램은 Worker Processer 의 가상 디렉토리로써 AppDomain 이라는 응용 프로그램 도메인에 속하게 됩니다. 다시 말하자면, HttpRuntime / HttpApplicationState / Page 개체는 AppDomain 에 격리되고, 그렇기 때문에, Application 개체는 응용 프로그램 간에 값을 공유할 수 없습니다.

 

Application 개체는 프로세서가 실행되는 서버의 메모리에 값을 저장하기 때문에, Application 개체에 저장할 데이터는 작으면 작을 수록 좋습니다.

 

[그림1] IIS ASP.NET 리소스 처리 아키텍쳐

( 위 그림과 더불어 참고 하세요 http://www.windowsdevcenter.com/pub/a/windows/2004/03/02/inside_iis.html )

 

ASP.NET 은 요청에 대해 스레드 풀에 요청을 할당하여 그 스레드로 페이지를 실행시킵니다. 페이지의 수행 주기가 끝나게 되면 해당 스레드는 스레드 풀에 반환되게 됩니다. ASP.NET 에 대한 요청은 이러한 다중 스레드 환경에서 Application 개체의 Input 이 발생하게 되면 동시성에 의해 원치 않는 데이터가 저장될 수 있습니다. 그렇기 때문에, Application 개체의 초기화는 Global.asax Application Start 이벤트를 통해 개체를 초기화 하도록 해야 합니다.

 

만약, Application Start/End 외에 다른 곳에서 Application 에 데이터를 Input 하기 위해 잠금/잠금 해제의 코드를 작성해 주시면 됩니다.

 

Application.Lock();

Application["Count"] = (int)Application["Count"] + 1;

Application.UnLock();

 

위와 같이 다중 스레드에서 Application 개체를 동기화 하기 위해 Lock() UnLock() 메서드를 통해 잠금을 수행할 수 있습니다.

 

Application 사용시

1.      데이터의 크기가 작을 때 경우 이용

2.      메모리에서 I/O 가 발생하기 때문에 잦은 입출력에 용이

3.      잦은 I/O 가 발생하는 페이지에 사용시 반드시 동기화 코드를 작성

 

 

ViewState 개체

 

ViewState 는 현재 페이지의 상태를 유지하기 위해 사용됩니다. 기본적으로 ViewState 히든필드(Hidden Field) 형태로 HTML 로 내려보내지며, 포스트 백이 발생하게 되면 Hidden Field 를 전송하여 페이지 상태를 유지할 수 있습니다.

 

[그림2] 소스 보기를 통해 본 ViewState

 

ViewState 는 계층적인 형태로 표현이 되는데, 자세한 정보는 태요 사이트ViewState 데이터를 분석하자를 참고 하시기 바랍니다.

 

ViewState 는 여느 상태 관리 컬렉션과 같이 간단하게 사용할 수 있습니다.

 

ViewState["CategoryID"] = "1234";

Response.Write(ViewState["CategoryID"].ToString());

 

특별한 기교 없이 쉽게 구현이 가능하지요.

 

ViewState 는 위의 [그림2] HTML 소스를 보는 것과 같이 상태 정보를 HTML 에서 정보를 가지고 있기 때문에 서버의 리소스가 필요하지 않습니다. 서버의 리소스가 필요하지 않기 때문에, 실제로 가장 많이 사용하는 상태 관리 기법이면서, ASP.NET 플랫폼의 서버 컨트롤이나 Form Runat=”Server” ( , Form 의 모든 요소를 서버에서 처리 ) 하기 때문에, 많은 부분의 상태 관리가 자동화 되어 있습니다.

 

이것이 강점이자 단점이 되는 것이 ASP.NET ViewState 입니다. Form 의 상태를 서버에서 처리를 하고, 서버 컨트롤이라는 편리한 ASP.NET 컨트롤을 이용하고, 이 컨트롤들은 스스로 상태 관리 능력을 가지고 있습니다. Control SaveViewState LoadViewState 메서드를 override 함으로써 컨트롤 스스로 상태 관리 능력을 부여할 수 있기 때문입니다. 하지만 GridView DataList 와 같은 많은 기능을 가지고 있는 컨트롤들은 바인딩 되는 DataSource 에 따라 ViewState 는 눈덩이 처럼 불어나게 됩니다. 아무 생각 없이 서버 컨트롤만을 사용하다 보면 ViewState 의 용량만 10 Kb, 20 Kb 가 훌쩍 넘어 버립니다. 우리나라와 같이 네트워크 인프라가 원활한 환경이라면 콧방귀를 뀌고 우습게 여길 수 도 있지만, 모바일 장치나 인터넷 속도가 느린 외국에서 서비스를 해야 할 경우 상황은 달라지게 됩니다.

 

ASP.NET ViewState Serializable 형태여야 합니다. ViewState HTML 코드로 랜더링 하기 위해서 이러한 직렬화 과정을 거치게 되고, 이것을 Base64 인코딩을 통해 [그림2] 와 같이 문자열로 변환하게 됩니다. 포스트백을 통해 Base64 디코딩과 역직렬화 과정을 거쳐 개체로 반환되게 됩니다. 수십/수백명의 동시 접속자가 예상되는 서비스에서는 ViewState 는 곧 CPU 부하를 증가시키고, 서버를 늘릴 수 밖에 없는 비용적인 측면을 생각하면 개인이나 기업에게는 잠재적인 부담일 수 밖에 없습니다.

 

해결책으로 가장 좋은 방법은 ViewState 를 사용하지 않는 것입니다. 대부분의 ViewState 는 서버 컨트롤에서 발생하기 때문에, 서버 컨트롤의 사용량을 줄이는 것이 좋은 방법이 될 수 있습니다. 더 나아가, Form Runat=”Server” 속성을 날려버리고, 여러개의 form 으로 나누어 서버로 전송되는 트래픽의 양을 줄여 ViewState 의 연산을 없애 버리는 것도 좋은 방법입니다.

 

다음 코드는 ViewState 를 사용하지 않는 최적화된 HTML 입니다.

 

<body>

    <form id="form1">

    <div>

             <input id="txtName" name="txtName" type="text" value="<%= txtName %>" />&nbsp;

             <input id="btnSend" name="btnSend" type="submit" value="Send" />

    </div>

    </form>

</body>

 

Form 태그의 Runat=”Server” 속성을 제거해 버렸습니다. 그렇기 때문에, Send 버튼을 클릭하게 되면 txtName 의 텍스트박스는 상태 관리가 되지 않습니다. 포스트백(Submit) 이 발생하면 txtName 의 텍스트박스는 입력된 값이 모두 사라지게 됩니다. Form Runat=”Server” 를 제거하면 기본적인 ViewState 조차 생기지 않습니다.

 

그럼 여기에 상태 관리를 위한 코드를 삽입해 보면..

 

public partial class _Default : PageBase

{

      

       protected string txtName;

 

       protected void Page_Load(object sender, EventArgs e)

       {

             try

             {

                    this.txtName = Request.Params["txtName"];

             }

             catch (Exception ex)

             {

                    Response.Write(ex);

             }

       }

}

 

위 코드를 삽입하게 되면, Send 버튼 클릭 후에도 txtName 의 텍스트박스는 이전에 입력했던 값을 유지하게 됩니다. 서버컨트롤과 자동화된 ASP.NET 의 상태 관리 없이 개발자가 직접 코드를 작성하여 상태 관리를 하는 방법입니다.

 

짧은 제 경험상, 기업의 내부 인트라넷의 경우 다양한 요구사항과 업무 처리를 위해 서버 컨트롤과 더불어 다양한 기능의 상용 컴포넌트와 컨트롤을 사용하기도 합니다. 하지만, 웹 서비스와 같이 불특정 다수에게 노출이 되는 중~대규모 사이트라면 서버컨트롤을 자재하여 사용하기도 하지만 아예 위의 방법처럼 코드를 통해서 상태 관리를 하기도 합니다. 생산성 측면에서 본다면, 당연히 개발 속도는 자동화된 ASP.NET ViewState 를 사용하는 것보다 느릴 수 있지만, 서버로 전송되는 트래픽의 양이 줄고, 서버의 CPU 연산이 줄게 되어 최종 사용자(End User) 측면에서는 빠른 응답 속도를 기대할 수 있습니다.

 

하지만, ViewState 를 포기한다는 것은 굉장히 어려운 문제일 수 있습니다. Form Runat=”Server”를 사용하지 않고, 수동적으로 상태 관리를 하게 되면, 많은 편리한 ASP.NET 의 기능을 사용할 수 없기 때문이죠. 그 대표적인 예가, ASP.NET AJAX ScriptManager 컨트롤 또한 사용할 수 없게 되고, 내부적으로 SaveViewState LoadViewState 를 사용하는 모든 서버 컨트롤(Label 외 몇 가지 컨트롤은 제외…)은 사용할 수 없게 됩니다. 때문에, 적절한 대안을 찾아야 하는데 바로 Control 이란 부모 클래스에는 EnableViewState 속성이 대안이 될 수 있습니다.

 

EnableViewState 는 컨트롤이 ViewState 를 사용할 것인지의 여부를 결정합니다. DataGrid 를 예로 들자면, 기본적인 EnableViewState 속성이 True 이기 때문에, 최초 한번의 DataBind 을 하고, 포스트백이 발생하여도 상태는 유지되지만, DataGrid 의 속성을 EnableViewState=False 로 변경하면, DataSource 의 데이터를 더 이상 ViewState 로 관리하지 않게 됩니다. 모든 컨트롤의 EnableViewState 속성을 변경해야 한다면, Page EnableViewState 도 있습니다. Page EnableViewState 는 더 이상 그 Form 은 모든 ViewState 를 관리 하지 않겠다는 것을 의미하며, 지저분한 ViewState 를 더 이상 생성하지 않습니다. (, 포스트백인지 아닌지의 여부를 감시하는 일부의 Hidden Field 를 생성할 수 있습니다.)

 

위의 내용과 더불어 EnableViewState MAC 이라는 것이 있습니다. 높은 수준의 데이터 무결성이나 데이터의 훼손이 큰 경우 EnableViewStateMac 속성을 True 로 지정해야 합니다. (기본값은 False) 입니다. 지난 아티클의 RewritePath 포스트백(Postback) 문제와 해결 방법을 통해 포스트백의 URL 을 지정해 줄 수 있는데, 이러한 비 정상적인 방법(?) 을 이용하게 되면, 서버 측에서는 ViewState 가 훼손되었거나, 데이터가 변경된 것으로 인지할 수 있습니다. 또한, 가끔씩 UpdatePanel 을 이용해 프로그램을 하다보면 개발자의 잘못된 알고리즘으로 인하여 ViewState 가 손실되거나 변경되는 경우가 있습니다. 이러한 경우들에 대해 EnableViewStateMac=False 를 통해 데이터의 무결성 검사 등의 ViewState 의 상태를 검사하지 않을 수 있게 됩니다.

 

하지만, 결정적으로 ViewState 는 클라이언트에 내려지고, 다시 이 ViewState 는 서버로 전송되는 형태이기 때문에, ViewState 는 언제든지 조작의 가능성이 있습니다. 간단한 툴을 이용해서 ViewState 가 어떤 정보를 가지고 있는지 쉽게 알 수 있기 때문에, ViewState 의 데이터를 항상 신뢰하면 안됩니다. ASP.NET 2.0 부터 이러한 ViewState 를 암호화 할 수 있는 기능이 제공됩니다. 간단히 @Page 지시자에 ViewStateEncryptionMode="Always" 를 넣음으로써 페이지의 ViewState 를 암호화 할 수 있습니다. 모든 웹 응용 프로그램의 페이지에 이러한 속성을 주기 위해서 web.config system.web 섹션에

 

<system.web>

       <pages viewStateEncryptionMode="Always">

 

로 지정해 주기만 하면 됩니다.

 

이것의 기본 속성은 Auto 입니다. Auto 는 아무런 ViewState 암호화가 수행되지 않습니다. , 개발자의 코드를 통해 페이지의 암호화를 지정해 줄 수 있는데,

 

Page.RegisterRequiresViewStateEncryption();

 

의 명시적인 호출로 Auto 상태에서 ViewState 암호화를 지정해 줄 수 있습니다. , 암호화를 수행하게 되면 그만큼의 CPU 의 부하도 늘게 된다는 것 또한 잊어서는 안됩니다.

 

ViewSate 사용시

1.      프로젝트가 어떤 용도의 서비스인지 잘 판단하여, 상태관리를 자동화 할 것인지, 수동화 할 것인지 결정

2.      EnableViewState MAC / ViewStateEncryptionMode 유용할 있지만, 남용하게 되면 CPU 파워만 증가시킨다

 

 

Sessoin 개체

 

이제야 Session 개체가 나왔습니다. 3회차부터 이 Session 개체를 더 자세히 볼 예정이기 때문에, 여기에서의 Session 은 가볍게 보도록 하겠습니다.

 

여느 상태관리 개체와 마찬가지로 사용법은 매우 간단합니다.

 

Session.Add("Key1","Value1");

                          

Session["Key2"] = "Value2";

 

 [그림3] 브라우져 요청 별 쿠키 생성

 

기본적으로 HTTP 프로토콜을 이용한 요청은 독립적으로 요청을 처리합니다. 요청에 대해 상태를 유지할 수 없기 때문에 어떤 사용자에 의한 요청인지 알 수 있는 방법이 없습니다. 그렇기 때문에, Session 은 쿠키와 브라우져에 의존적입니다. 이전 포스팅의 실전 ASP.NET Session [1] - 쿠키를 이용한 상태관리와 위험성에서도 설명했듯이, 브라우져가 사이트에 요청을 할 때 생성이 되고, 브라우져를 닫게 되면 쿠키도 삭제된다고 하였습니다. , 만료시간이 지정된 경우에 텍스트 형태로 사용자의 하드 디스크에 저장이 됩니다.

 

[그림4] 브라우져별 요청에 대한 쿠키 생성

 

브라우져는 의해 처음 사이트에 요청을 하게 되면 쿠키를 생성하게 됩니다. 때문에 브라우져 별로 처음으로 사이트를 요청하게 되면 쿠키를 생성하는데 그것이 바로 ASP.NET 이 상태관리를 위한 Session ID 를 쿠키로 생성하게 됩니다. 이러한 행위는 사용자별로 고유한 세션 저장소에 정보를 저장하기 위해서이며, 사이트에 접속한 여러 사용자를 구별한 수 있는 키(Key) 가 되기도 합니다. 좀 더 정확하게 말하면, 이러한 Session ID AppDomain 별로 웹 응용프로그램의 Application ID 가 세션의 키값으로 사용되어 집니다. 그렇게 때문에, AppDomain 간의 Session 의 값은 공유할 수 없게 됩니다.

 

ASP.NET Session 은 기본적으로 쿠키를 통해 Session ID 를 인식합니다. 하지만, 특정 사용자의 브라우져 상태는 쿠키를 허용하지 않을 수 도 있습니다. 또한 사용자 임의로 쿠키를 허용하지 않도록 할 수 도 있습니다. ASP.NET 은 쿠키 기반의 세션 유지가 아닌 쿼리 문자열을 통해 Session ID 를 유지할 수 도 있습니다.

 

system.web 섹셕의 sessionState 요소를 통해 아래와 같이 cookieless true 로 설정하면, 더 이상 쿠키 기반으로 Session ID 를 유지하지 않고, 쿼리 문자열을 통해 Session ID 를 식별하게 됩니다.

 

<system.web>

       <sessionState cookieless="true"></sessionState>

 

[그림5] 쿼리 문자열을 통한 Session ID 유지

 

ASP.NET 은 보다 자동화된 Session ID 를 유지하도록 해 줍니다. 가령, 사용자의 브라우져나 쿠키를 허용하지 않거나, 임의로 쿠키를 허용하지 않도록 설정한 경우 cookieless AutoDetect 로 설정하게 되면, ASP.NET 쿠키를 통해 Session ID 를 유지할 것인지, 쿼리 문자열로 Session ID 를 유지할 것인지를 스스로 결정하게 됩니다. 기본적으로 cookieless 속성은 디폴트로 UseCookies 로 설정되어 있기 때문에,

 

<sessionState cookieless="AutoDetect"></sessionState>

 

과 같이 개발자가 직접 수정해 주셔야 합니다.

 

기본적은 Session 설정은 In-of-process 를 통해 처리되게 됩니다. In-of-process ASP.NET Worker Process 를 통해 처리가 되며, Worker Process 가 구동되는 서버의 메모리에 저장이 됩니다. Application 개체와 마찬가지로 Session 개체는 In-of-Process 로 처리가 될 경우 웹 응용프로그램의 빌드나 web.config 의 설정이 바뀔 경우 개체의 데이터는 모두 잃어버릴 수 있습니다. 명시적으로 sessionState mode 속성을 지정하지 않으면 디폴트로 InProc 입니다

 

Session 을 잃게 된다는 것은 웹 서비스에게 치명적일 수 있습니다. 한가지 예를 들어보면, 쇼핑몰 사이트가 있습니다. 상품을 결재를 하기 위해서 대부분 여러 단계를 거쳐 인증을 하고, 결재가 진행됩니다. 하지만, 사이트 관리자는 서버의 치명적인 버그를 발견하여 수정하고 웹 어플케이션을 업데이트 한다면, 결재중인 사용자의 Session 은 끊겨버리게 됩니다. 물론, 이러한 결재 오류에 대한 정책이 잘 정립 되어 운영 된다면 문제가 없겠지만, 데이터베이스를 조작하지 못하는 서버 관리자만을 채용하는 소형 쇼핑몰일 경우 이러한 문제는 쉬쉬 하며 넘어갈 일만은 아닐 것입니다.

 

[그림6] 다양한 Session State 유지 계획

 

하지만, 여기서도 문제라면 서버의 메모리는 제한적입니다. 때문에 In-of-process 가 아닌, Out-of-process 로 전환할 필요가 생기게 됩니다. 또한 대형 사이트일 경우 Web Farm 으로 부터 여러 개의 Server Farm 을 구성하여 분산처리 할 경우 보다 나은 성능과 관리의 이점이 있습니다. , 한가지 Out-of-process Serializable 개체만을 Session 에 저장할 수 있다는 제한이 있습니다.(당연하겠지만)

 

다음 아티클에서 Session State 를 유지하는 다양한 방법을 알아 보고, 방법에 대한 보다 나은 이슈를 제시할 예정입니다.

 

용어설명 참고

위키디피아 Web Farm(Server Farm)

Web Garden – MSDN ASP.NET 작업자 프로세스

 

Session 사용시

1.      소규모 사이트일 경우 In-of-process 를 권장

2.      Session 은 어떻게 사용 하느냐도 중요하지만 어떻게 운영하는가에 대한 기술적인 계획이 필요

 


Posted by 땡초 POWERUMC

댓글을 달아 주세요

[.NET/ASP.NET] - 실전 ASP.NET Session [1] - 쿠키를 이용한 상태관리와 위험성
[.NET/ASP.NET] - 실전 ASP.NET Session [2] - 상태관리의 종류
[.NET/ASP.NET] - 실전 ASP.NET Session [3] - 다양한 세션 관리 방법
[.NET/ASP.NET] - 실전 ASP.NET Session [4] - 세션상태 마이그레이션


프로그램의 코드를 짜면서 쉽게 간과할 수 있는 상태관리 오류 등을 범하기도 합니다. 때론, 적절한 상태 저장소를 잘못 선택하여 잘못된 코드와 결과를 보는 경우도 있습니다. 이번 아티클은 그런 오류를 범하지 않고, 적절한 상태관리를 할 수 있도록 방법을 제시해 줄 예정입니다. 또한, 3회 포스팅 부터는 그저 별 것 아닌 Session 이라는 상태 저장소에 대해 좀 더 깊이 알아보고, Session 을 확장하는 방법과 시스템의 전반적인 설계 방법까지 알아볼 예정입니다.

 

1.    쿠키를 이용한 상태관리와 위험성

 

ASP.NET 플랫폼은 다양한 상태관리 방법을 제공해 줍니다. 그렇기 때문에, 개발자는 적절한 상태관리 저장소를 선택하기만 하면 됩니다. 그럼, 상태 관리의 종류와 이것을 선택하기 위한 조건, 적절한 사용 방법도 함께 살펴보겠습니다.

 

1.1.    쿠키 (Cookie) 를 이용한 상태관리

 

우선 쿠키의 생성 주기를 보면, 쿠키는 브라우져가 사이트에 요청을 할 때 생성이 됩니다. 그리고 브라우져를 닫으면 쿠키는 삭제됩니다. 이러한 쿠키에 대한 정보를 브라우져가 관리하게 되지만, 웹 서버에서 쿠키에 대한 만료시간 정보를 보낼 경우, 브라우져는 쿠키 정보를 하드 디스크에 저장하게 됩니다. 그리고 만료된 쿠키는 쿠키를 작성한 웹사이트를 방문할 때 브라우져가 삭제하게 됩니다.

 

[그림1] 자바스크립트로 네이버의 쿠키 정보 확인

 

쿠키는 브라우져마다 다를 수 있지만 최대 4096 Bytes(4KB) 의 텍스트 정보를 저장할 수 있고, 최대 300개의 쿠키를 보존하며, 사이트마다 20개의 쿠키를 허용 합니다. 용량과 개수의 제한이 있기 때문에, 경량의 정보를 저장할 때 유용합니다. 하지만, 쿠키의 만료시간을 지정하게 되면 텍스트로 사용자의 하드디스크에 정보를 저장하기 때문에 메모장을 이용하여 임의로 쿠키 정보를 변경할 수 있습니다. 심지어, 자바스크립트를 이용하여 쿠키를 조작할 수도 있기 때문에, 중요한 정보를 쿠키에 저장하는 것은 매우 부적합 합니다.

 

쇼핑몰의 최근 본 상품과 같이 서버에서 정보를 관리할 필요가 없고, 정보의 보안이 필요 없고, 가벼운 경우 선택하면 됩니다.

 

쿠키는 다음의 경우에 사용되면 됩니다.

1.      1회성 정보일 경우

2.      작은 양의 정보를 저장할 경우

3.      중요하지 않은 정보일 경우 (개인정보나 노출되면 안되는 정보는 절대로 쿠키를 사용하지 마십시오)

 

 

1.2.    쿠키의 위험성

 

위의 사용 조건이 충족 되었더라고 하더라도, 안심하면 안됩니다. 쿠키의 정보가 조작이 되었을 경우, 쿠키 정보를 절대로 신뢰하면 안되기 때문입니다.

 

다음과 같이 쿠키의 정보가 변경되었을 경우를 생각해 봅시다.

 

protected void Page_Load(object sender, EventArgs e)

{

   Response.Cookies["USERID"].Value = "<script>alert('a');</script>";

   Response.Write("USERID" + Request.Cookies["USERID"].Value );

}

 

위의 샘플은 특별한 오류가 없습니다. 하지만, 의도하지 않은 자바스크립트 코드가 실행됩니다.

 

그럼 좀 더 나쁜 상황을 만들어 보면,

 

Response.Cookies["USERID"].Value = "<script>location.href='http://피싱사이트.com';</script>";

 

Response.Cookies["USERID"].Value = "<script><iframe src='http://a.com/악성코드페이지.htm' /></script>";

 

물론, 위의 샘플은 프로그램적으로 잘못된 정보를 입력하였지만, PC 방과 같은 불특정 다수가 사용하는 곳의 PC 일 경우, 쿠키를 조작하여 피싱 사이트로 유도하여 사용자 아이디/비밀번호를 빼내거나, 중요한 개인 정보를 빼낼 수 있습니다. 또한, 악성코드가 포함된 페이지를 포함시킬 수 도 있습니다.

 

이럴 경우,

 

Response.Write( Server.HtmlEncode(Request.Cookies["USERID"].Value) );

 

같이 HTML Encoding 통해 쿠키를 스크립트로 조작하는 악의적인 조작을 피할 있습니다.

 

최근 옥션의 해킹 사건 이후, 2차 또는 3차 해킹 피해 까지 일어나고 있습니다. 이러한 최악의 시나리오가 내가 만든 코드에서 발생하였다면 얼마나 개발자로써 치욕적일까요? 빠듯한 일정이라도, 이러한 잔지식(?)을 오늘 알게 되었다면, 꼭 적용하시기 바랍니다.

 

[그림2] 양날의 검과 같은 쿠키 [ 사진출저는 여기 ]

 

위의 몇 가지 예시에서 본 것과 같이 쿠키는 매우 유용하면서도 위험할 수 있는 양날의 칼과 같습니다. 바로 쿠키의 값을 클라이언트 PC 에서 유지되고 있기 때문에, 의도되지 않은 다양한 조작이 이루어 질 수 있는 것입니다.

 

더 자세한 내용은 MSDN 내용을 참고하시면 사용방법이 잘 나와있기 때문에 MSDN 을 참고하시면 될 것 같습니다.

 

위키피디아 http://en.wikipedia.org/wiki/Http_cookie

MSDN - http://msdn.microsoft.com/ko-kr/library/ms178194(VS.80).aspx

 

Posted by 땡초 POWERUMC

댓글을 달아 주세요


[.NET/ASP.NET] - Custom Config 에 Intellisence 날개를 달자
[.NET/Visual Studio] - Custom Extension 에 Intellisence 날개를 달자


Custom Config
에 Intellisence 날개를 달자
 
Configuration 란? 우리가 웹폼이든 윈폼이든 프로젝트를 생성하면, web.config / app.config 와 같은 Configuration 파일이 생깁니다. 이런 .config 확장자가 붙는 파일을 "구성 파일" 이라고 부릅니다. 어플케이션이 구동될때 이 구성파일에 어플케이션이 동작하기 위한 환경 변수를 사용하게 되고, 환경 변수의 값이 변하게 되더라도 재컴파일 없이 환경 변수의 값을 변경할 수 있는 장점이 있습니다. 때문에, .config 파일을 사용한다는 것은 이미 잘 알고 있을 것입니다.
 
.NET Framework 은 이 구성파일을 확장할 수 있는 방법 또한 제공해 주고 있습니다.
이 부분에 대해서 자세히 잘 설명해 놓은 아래의 사이트를 참고하시면 좋을 것 같습니다.
 
MSDN,
방실님의 Configuration을 Custom으로 사용해보자
TAEYO.NET 의 .NET 에서 나만의 Configuration 만들기
즈믄님의 .Net Framework v2.0을 이용한 사용자 정의 Config File Manager
 
 
Custom Configuration 장단점
 
아래는 Custom Configuration 을 실제 적용한 web.config 입니다.
 
 
l 장점
1. 알아보기 쉽다
2. 관리가 용이하다


XML 요소를 확장시켜 요소의 이름들을 정의하고 요소의 이름으로 기능을 유추할 수 있기 때문에 알아보기가 쉽습니다.. 그렇기 때문에 깔끔하게 유지보수도 가능합니다.

l 단점
1. 늘어나는 요소(Element)와 속성(Attribute) 만큼 코드를 만들어야 한다.
2.
Custom Configuration 은 Intellisence 가 지원되지 않아 요소와 속성 규칙을 모두 외워야 한다.

코드를 만들어 놓으면 재활용성에서 유리한 점이 있긴 하지만 Intellisence(자동완성기능) 이 지원되지 않습니다. 은근히 만든 소스코드를 보면서 요소나 속성을 적기도 합니다. 개발자가 Custom Configuration 을 만들어 쓴다는 것 부터가 굉장히 노가다성 작업이 될거라 생각합니다.

 
 
그렇다면, 이 .config 의 XML Schemas 는 어디에~?
 
바로 .config 와 같은 구성파일은 XML 로 되어있습니다. 그럼, 이 XML 의 스키마를 정의하는 Schema 가 어디엔가 존재할 수 도 있다라는 것입니다.
 
그렇습니다, .config 에 대한 XML Schema는 다음과 같은 경로에서 찾아 볼 수 있답니다.
%ProgramFiles%\Microsoft Visual Studio 9.0\Xml\Schemas\DotNetConfig.xsd
 


위 폴더를 좀 더 살펴보시면, XHTML, SOAP, Code Snippet, Sitemap 과 관련된 대부분의 XML Schemas 가 있는 것도 알 수 있습니다.
 
 
XML Schemas 확장하기
 
이 포스팅에서는 XML Schema 에 대한 내용은 자재하도록 하고, 아래의 MSDN 에 보시면 Schema 의 각 요소별로 설명이 잘 되어 있으니, 참고하시면 좋을 것 같습니다.
http://msdn2.microsoft.com/en-us/library/ms256142.aspx

아래의 그림은 Umc.Core 프레임웍에 대한 .config XML Schema 입니다.
 
클릭하면 확대됩니다
 
요소별로 XML Schema 를 정의해 주고, 저장하시면 바로 .config 에 적용됩니다.
 
 
현대 개발자들에겐 자동완성기능은 필수가 아닌 필수가 되어 버렸죠. 시간을 투자하여 만들어 놓은 Custom Configuration 클래스들을 사용하려고 더 이상 소스코드를 뒤적거리지 않아도 됩니다. 한번 XML Schema 를 작성해 놓으면 정말 순식간에 .config 를 작성할 수 있기 때문입니다.
하지만 이 Schema 의 배포에 대한 문제도 신경써야 하지만, 프레임웍 설치시에 자동으로 XML Schema 를 변경하도록 하면 큰 문제는 되지 않을 것 같네요.
 
 
여기까지 포스팅을 마치도록 하고, 요즘 Umc 는 방통대 중간고사가 끝나서 너무너무 행복하답니다. ^^ 하지만 이어지는 출석수업의 압박이 부담스럽네요. 바람은 차갑고 옆구리는 시리고… 환절기 감기 조심하시고 멋진 일요일 되세요.
Posted by 땡초 POWERUMC

댓글을 달아 주세요

RewritePath 와 포스트백(Postback)의 문제
 
RewirtePath 를 사용하여, 웹의 요청을 가로채서 경로를 재작성하여 사용할 수 있습니다. 언뜻 보기에, 굉장히 놀라운 기능인 것은 분명하지만, 이것은 ASP.NET 에서 사용하기에 결정적인 문제가 있습니다. 그 원인은 다음과 같습니다.
 
다음과 같이 HttpModule 에서 웹 요청을 가로채어 RewritePath 로 경로 재작성한 샘플입니다. (확장자만 제거해 보았습니다)
 
 
HTML 코드는
 
 
과 같이 <form runat=”server”> 로 되어 있습니다. 여기에서 이 form 이 서버 태그로써 HTML 로 랜더링 될 때 강제적으로 다음과 같이 form 태그가 랜더됩니다.
 
 
그렇기 때문에, Button이나 LinkButton 등의 서버컨트롤을 사용하게 되면 포스트백이 발생되고, form 태그의 action 속성의 URL 로 포스트백이 발생합니다. 아래와 같이 Button 컨트롤에 마우스를 가져다 대면 RewritePath 로 재작성된 경우(물리적인 실제 웹페이지) 를 가리키게 됩니다.
 
 
위의 Button 컨트롤, 또는 LinkButton 등 포스트백이 발생하는 모든 컨트롤에 대해서 포스트백 후 원래의 웹페이지 주소로 아래와 같이 변하게 됩니다.
 
 
이것이 ASP.NET 플랫폼에서 의도된 것인지, 어떤 사람들은 버그라고 말을 합니다. 어느 것이 진실인지 저는 잘 모르겠지만, 분명 개발자를 고민에 빠트릴 만하다는 것입니다.
 
 
RewritePath 의 포스트백 후의 URL 변경 문제 해결하기
 
문제를 해결하기 위해 대표적인 두가지 방법이 있습니다.
 
1.      HtmlPage 를 상속한 컨트롤 만들기
모든 페이지의 <Form runat=”server”> 태그를 상속된 HtmlPage 컨트롤로 변경해 주어야 합니다. 때문에 매우 번거롭죠. 폼을 만들 때 마다, 이것부터 수정해 주셔야 할테니까요.

2.      Page 의 Render 메서드를 override 하기
1번 보다는 코드가 복잡하지만, 가장 현실적인 대안입니다. 기존 코드의 수정을 하지 않고, 전역적으로 웹 어플케이션에서 적용할 수 있습니다.
 
위의 링크를 따라가면 비록 영문이긴 하지만, 좋은 대안이 될 수 있을 것입니다.
 
2번의 원문 소스가 제대로 작동하지 않는 부분이 있어서, 수정해 보았습니다.
 
우선 HttpModule 을 구현하는 것 까지 모두 동일 하게 구현하시면 되고, HtmlTextWrtier 클래스만 보여드리도록 하겠습니다.
 
internal class FormActionFixTextWriter : HtmlTextWriter
{
       private string action;
       private bool inForm;
 
       public FormActionFixTextWriter(HtmlTextWriter writer, string action)
             : base(writer)
       {
             this.action = action;
       }
 
       public override void WriteBeginTag(string tagName)
       {
             inForm = String.Compare(tagName,"form",true) == 0;
 
             base.WriteBeginTag(tagName);
       }
 
       public override void WriteAttribute(string name, string value, bool fEncode)
       {
             if (inForm && String.Compare(name, "action") == 0)
             {
                    value = this.action;
             }
 
             base.WriteAttribute(name, value, fEncode);
       }
}
 
실행 결과를 보면.
 
 
위와 같이 요청된 URL 그대로 포스트백이 발생할 수 있도록 form 태그의 action 속성의 URL 이 수정된 것을 알 수 있습니다.
 
이번 주말에는 강원도에서 나보다 나이 어린 사촌 결혼식 갔다가 부러워 죽는 줄 알았습니다. 그리고 갑자기 친구 어머니의 교통 사고 소식에 문병 다니고, 금방 쓰러져 자야겠습니다. 피곤한 주말이었습니다. ^^;

참고
http://msdn2.microsoft.com/en-us/library/ms972974.aspx
http://weblogs.asp.net/jezell/archive/2004/03/15/90045.aspx

Posted by 땡초 POWERUMC

댓글을 달아 주세요

ASPX 확장자 바꾸기
 
ASP.NET 을 하다 보면, 죄다 사이트의 URL 확장자가 .ASPX 인 것을 알 수 있습니다. 그래서 이 .ASPX 확장명을 변경할 수 있도록 다음의 포스팅을 통해서 확장명을 바꾸는 방법도 알아본 적이 있습니다.

 
[.NET/ASP.NET] - 너 ASPX 웹폼 확장자를 바꿔봤니? 난 해봤~어!


제목부터 정말 유치 찬란하네요 ^^ 위의 포스팅의 핵심은 IIS 의 ISAPI 와 .NET Framework 의 HttpHandler, 그리고 Build Provider 입니다.
 
그런데, 이렇게 확장명을 바꾸게 되면 문제가 생기게 됩니다.
 
 
바로, 위와 같이 .umcx 확장명의 웹 페이지는 태그의 컬러가 모두 시커멓게 변하고, 자동완성 기능도 사용할 수 없다는 겁니다.
 
개발자는 바로 비명을 지르겠죠? “도대체 시커먼 편집기에서 어떻게 개발을 하라는 겁니까!!
 
 

Visual Studio 가 제공하는 확장자 추가 기능
 
이 기능은 이미 Visual Studio 2005 부터 제공이 되고 있었습니다. ( 2003 버전은 확인해 보지 못해서, 설치되신 분 알려주세요.. )
 
아래와 같이, Visual Studio 의 도구->옵션->텍스트 편집기->파일 확장명 에서 추가할 수 있습니다.
 
 
위와 같이 원하는 확장명을 입력하고, 다시 .umcx 페이지를 열게 되면, 아래와 같이 일반적인 웹페이지를 작성하는 것과 같이 이쁜 컬러와 자동완성 기능을 사용할 수 있습니다.
 
 
하지만, 잘 보시면 1 Line 에 파란 밑줄이 그어져 있는데, 저 오류는 Build Provider 가 등록되지 않았기 때문입니다. 해결방법은 이미 위에 링크한 포스팅 본문에 잘 나와있기 때문에 Build Provider 를 web.config 에 등록해 주시면 된답니다.
 
위와 같이 파일 확장명을 추가해 주게 되면, 다음과 같이 레지스트리에 변화가 생깁니다.
HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\9.0\Default Editors
( Visual Studio 2005 버젼은 8.0 노드에 있습니다 )
 
 
확장자 별로 노드가 생기게 되고, 편집기 타입에 따라 Custom 의 값이 설정됩니다. 이 Custom 값을 정리해 보면,
 
리소스 편집기
{177559E0-D141-11D0-92DF-00A0C9138C45}
바이너리 편집기
{25834150-CD7E-11D0-92DF-00A0C9138C45}
사용자 정의 컨트롤 편집기
{F390D63C-F7D8-4E74-AA04-57C1ECF53D4D}
사용자 정의 컨트롤 편집기(인코딩 사용)
{25B02EE5-DB4E-48BD-A9BB-4FD280EB058B}
소스 코드(텍스트) 편집기(인코딩)
{C7747503-0E24-4FBE-BE4B-94180C3947D7}
스크립트 편집기
{A52A054C-5228-4819-B568-E5B8040801B5}
스크립트 편집기(인코딩 사용)
{29947C11-E110-4596-85B8-42A21EF46F6B}
웹서비스 편집기
{FB9167C9-D3B8-4EDA-8083-9AF6B6F6DA62}
웹서비스 편집기(인코딩 사용)
{80323CE9-9455-4F20-8F50-DC4916F5BF9E}
C#
{8B382828-6202-11D1-8870-0000F87579D2}
C++
{8B382828-6202-11D1-8870-0000F87579D2}
Crystal Reports
{FF14D5B3-1718-4071-9306-3B9B80BBB36A}
HTML 편집기
{C76D83F8-A489-11D0-8195-00A0C91BBEE3}
HTML 편집기(인코딩 사용)
{8281C572-2171-45AA-A642-7D8BC1662F1C}
HTTP 처리기 편집기
{DAB1E6DB-4119-49A3-ACE3-0FE8F3E0669F}
HTTP 처리기 편집기(인코딩 사용)
{5FA86C67-6204-4EED-81DE-E9A98BD4E207}
Web Form 편집기
{57312C73-6202-49E9-B1E1-40EA1A6DC1F6}
Web Form 편집기(인코딩 사용)
{D704C95A-F138-4C97-8B90-AE8287758DD1}
Windows Scripting Host파일편집기
{D411C4AC-FCFD-49BF-8D63-B94E075023F2}
Windows Scripting Host파일편집기(인코딩 사용)
{CBA754F9-4BE6-437B-9FFD-4336DDAB8621}
XML 편집기
{FA3CD31E-987B-443A-9B81-186104E8DAC1}
XML 편집기(인코딩 사용)
{412B8852-4F21-413B-9B47-0C9751D3EBFB}
 
만약, 웹 개발 프로젝트에서 확장명을 변경해서 개발해야 할 경우, 개발자들에게 다음과 같은 .REG 파일을 배포하면 되겠네요.
 
UMCX.REG
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\9.0\Default Editors\umcx]
"Custom"="{57312C73-6202-49E9-B1E1-40EA1A6DC1F6}"
"Type"=dword:1
 
 
닷넷 웹 프로젝트 = .ASPX ???
 
 
“왜 닷넷 마스터페이지 확장자는 .master 이죠? 못바꾸나요?”
“왜 닷넷 XML 웹서비스 확장자는 .asmx 이죠? 못바꾸나요?”
“왜 닷넷 WCF 의 확장자는 .svc 이죠? 못바꾸나요?”
등등… 신입사원이 묻는다면, 몇 가지 알 수 없는 용어들과 섞어서 “할 수 있단다” 라고 말해 주세요.
 
여기까지 마치도록 하고, 이번 주말은 너무 빨리 지나갔습니다. ㅡ,.ㅡ;; 새로운 한 주를 위해 푹 쉬세요..
Posted by 땡초 POWERUMC

댓글을 달아 주세요

UmcBlog 는 이전에 웹사이트 프로젝트(Website Project)로 만들어졌다. 하지만, 대대적인 공사(?)를 통해 웹 어플케이션 프로젝트(Web Application Project)로 변경하면서 지난번에 언급했던 몇가지 문제점이 해결되었다.
 
두가지 문제점은 다음과 같았다.
 
l        웹 페이지의 인스턴스를 생성할 수 없기 때문에 재사용성이 떨어진다 (가능하지만 번거롭다 )
l        두 개 프로젝트가 웹사이트에 참조될 경우 형상 관리(Source Safer) 에서 바인딩이 깨진다.
 
이 두 가지 문제는 실제 프로젝트를 진행하게 된다면 굉장히 심각한 문제가 될 수 있다. VS2005 로 CS 기반의 프로젝트를 대부분 진행했던 터라 실제 프로젝트에서 어떠한 문제가 있는지는 체험해 보지 못했지만, 팀 프로젝트에 있어서 웹 사이트 프로젝트는 여간 성가신 존재가 아닐 수 없는 것은 분명한 것 같다.
 
 
 
Visual Studio 2005 Team Suite Service Pack 1 설치
 
우선 웹 어플케이션 프로젝트를 만들기 위해서는 Visual Studio 2005 Service Pack 1 이 필요하다. 혹시 아직까지 Visual Studio 2005 Service Pack 1 을 설치하지 않으셨다면 아래의 링크를 통해 다운로드 받아 설치 하시면 됩니다.
 
혹시 비스타에 개발환경이 설치되어 있다면, 두번째 윈도우 비스타용 업데이트로 함께 설치해 주어야 합니다.
 
Visual Studio 2005 Team Suite Service Pack 1
http://www.microsoft.com/downloads/details.aspx?displaylang=ko&FamilyID=bb4a75ab-e2d4-4c96-b39d-37baf6b5b1dc
 
윈도우 비스타용 Visual Studio 2005 Service Pack 1 Update
http://www.microsoft.com/downloads/details.aspx?familyid=90E2942D-3AD1-4873-A2EE-4ACC0AACE5B6&displaylang=ko
 
 
 
ASP.NET 웹 응용 프로그램으로의 전환
 
이제 VS2005 서비스팩이 설치 되었다면, 새 프로젝트의 Web 항목에 웹 응용 프로그램 프로젝트가 생성된 것을 확인할 수 있다. 그럼 웹사이트 프로젝트에서 마우스 오른쪽 버튼을 눌러 속성 내용을 보면, 다음과 같이 “웹 응용 프로젝트로 변환” 메뉴가 보인다.
 
[
그림1] 웹 응용 프로그램으로 변환
 
그럼 이제 웹 응용 프로그램으로 전환할 준비 단계는 모두 준비되었다. 이제부터 중요한 내용이니 정신 바짝 차리고 내용의 순서를 잘 보기 바란다.
 
1.        기존의 웹 사이트 프로젝트와 별도로 웹 응용 프로그램 프로젝트를 만든다.


[그림2] 기존의 웹사이트 프로젝트와 새로 생성한 웹 응용 프로그램 프로젝트

localhost:9091 프로젝트는 변환하게 될 웹사이트 프로젝트이다
Convert 프로젝트는 방금 생성한 텅 빈 웹 응용 프로그램 프로젝트이다.


2.        웹 사이트 프로젝트의 모든 내용을 웹 응용 프로그램 프로젝트로 복사한다.

새로 만들게 되는 웹 응용 프로그램 프로젝트는 VS2005 에서 새로 등장한 웹 사이트 프로젝트와 매우 다른 성격의 프로젝트이기 때문에, 다음과 같은 작업을 해야한다.


[그림3] 웹 응용 프로그램 프로젝트로 웹사이트 프로젝트 내용을 복사한다.

그럼 다음과 같이 웹 사이트 프로젝트의 모든 내용을 웹 응용 프로그램 프로젝트로 복사가 진행된다.


[그림4] 웹 응용 프로그램 프로젝트로 복사중

복사가 진행되면서 모든 폴더의 Depth 가 확장이 된다. 포함된 파일이 많은 경우 다소 시간이 걸릴 수도 있다.


3.        Designer.CS 파일을 생성 && namespace 체계 구축


[그림5] 그림과 같은 단계를 거쳐 Designer.CS 파일을 생성한다.

이후, Rss.aspx.Designer.CS 파일의 내용을 보게 되면, 전혀 namespace 체계가 정돈되지 않은 코드가 되어버린다. 코딩에는 별 문제가 없지만, 우리가 웹 응용 프로그램으로 변환하는 장점을 버리게 되는것이다.


[그림6] namespace 체계가 정돈 되지 않는 코드

여기에서는 웹사이트 최상위 namespace 는 UmcBlog 로 정할 것이다. 그럼 우선 CS 코드에 namespace 블록을 적용해 보도록 하자.


[그림7] Rss.aspx.CS 코드에 namespace 블록 적용

그리고, aspx 페이지의 Page 지시자에 Inherits 속성에도 namespace 가 적용된 상속 클래스명을 변경해 주자.


[그림8] Rss.aspx 의 Page 지시자에 상속 클래스명 변경

그리고 다시 해당 파일을 웹 응용 프로그램으로 변환을 통해 Designer.CS 파일을 생성해 보자.


[그림9] namespace 체계가 적용된 Designer.CS 파일


 
웹 어플케이션의 속성에서 “웹 응용 프로그램으로 변환”을 하게 되더라도 무리는 없다. 하지만 위에서 언급하였듯이, 웹 어플케이션으로 변환하는 의미와 장점을 버리게 되는 것이다.
 
 
Designer.CS 생성 팁(Tip)
 
웹 응용 프로그램으로 변환”을 통해 Designer.CS 파일이 생성되고, 이 디자이너 파일에 적용된 네임스페이스는 어떤 기준에 의해 생기는 것일까?
 
답은 aspx 의 Page 지시자의 Inherits 속성이다.
 

[그림10] Page 지시자의 Inhertis 속성에 의해 Designer.CS 의 namespace 체계가 결정된다
 
때문에, 작업중에 실수를 하게 된다면 Designer.CS 파일을 삭제하고 aspx 의 Page 지시자의Inhertis 속성을 수정한 후 다시 Designer.CS 파일을 생성하면 된다.
 
 
이제 웹 어플케이션 프로젝트로 변환에 완료되었다. 앞서 말한, VSS 를 통한 형상관리와 페이지의 인스턴스를 생성할 수 있게 되어 기존의 VS2003 과 같은 웹 어플케이션 코딩이 가능해졌다.
 
아마도 웹 사이트 프로젝트를 웹 어플케이션 프로젝트로 변환하는 방법을 몰라서 도전하지 못했던 독자라면 이 방법대로 한다면 꼭 성공할 수 있을 것입니다.
Posted by 땡초 POWERUMC

댓글을 달아 주세요

  1. 셀오스 2012.08.24 14:19 Address Modify/Delete Reply

    굿

  2. 셀오스 2012.08.24 14:19 Address Modify/Delete Reply

    굿



http://umc.pe.kr/article_79.aspx
아티클을 통해 FTP 로 파일을 업로드/다운로드 방법을 설명한 바 있다. FTP 의 인증 과정을 거쳐 특정 폴더의 파일에 접근 할 수 있다.
 
CredentialCache
그렇다면 우리는 CredentialCache 라는 클래스를 눈여겨 볼 수 있다. 이 클래스를 한마디로 표현하자면 “여러 자격 증명을 저장하기 위한 저공소”이다. CredentialCache 클래스의 GetCredential 메서드를 이용하여 URI 와 인증형식등을 지정하면, 가장 유사한 자격증명을 제공받을 수 있다.
 
CredentialCache cCache = new CredentialCache();
cCache.Add(new Uri("ftp://umc.pe.kr/www/a.txt"), "Basic", new NetworkCredential("ID","PASSWORD") );
CredentialCache 클래스를 사용하여 자격 인증을 캐시에 저장한다.
 
위의 코드는 특정 URI 의 Basic(기본) 인증 방식으로 자격 인증이 생성되었다.
Basic을 “Digest” 라고 바꾸어주면 다이제스트 인증을 할 수 있다.
 
기본 및 다이제스트 인증 방식은 전송되는 데이터가 암호화가 되지 않으며, 기본 인증방식은 사용자의 아이디 및 패스워드는 보안되지 않은 상태로 전송된다.
 
그렇다면 웹(ASP.NET) 에서도 CredentialCache 클래스를 이용하여 자격 인증의 상태를 보존할 수 있을까?
언듯, 클래스의 이름이 말해주듯 Cache 라는 말에 혹해버린다면 곧바로 난관에 부딪혀 버릴것이다. 물론 static 한정자로 정적 맴버를 만든다면 맴버가 초기화 되거나, 가비지컬렉터에 의해 수집이 되지 않는한 상태를 보존할 수 있겠으나, 여기에서도 문제가 따른다.
즉, 웹 어플케이션 차원에서 공용되는 맴버이므로, 사용자별 자격 인증을 저장하기엔 매우 부적합 하다.
 
Sample
 
 
그럼 어떻게 구현했는지 샘플을 보도록 하자.
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>제목 없음</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
                  FTP 주소 : <asp:TextBox ID="txtFtp" runat="server" MaxLength="100" Width="521px"></asp:TextBox>
                  <br />
                  <br />
                  User ID :
                  <asp:TextBox ID="txtID" runat="server"></asp:TextBox><br />
                  Password :
                  <asp:TextBox ID="txtPassword" runat="server" TextMode="Password"></asp:TextBox>
                  <br />
                  <br />
                  <asp:Button ID="btnConnection" runat="server" Text="Connection" OnClick="btnConnection_Click" />
                  &nbsp;&nbsp;
                  <asp:Button ID="btnCacheClear" runat="server" Text="Cache Clear" OnClick="btnCacheClear_Click" />
                  <p></p>
                  <asp:Label ID="lblCache" runat="server"></asp:Label>
                  <p></p>
                  <asp:Label ID="lblData" runat="server"></asp:Label>
         </div>
    </form>
</body>
</html>
 
Default.aspx
 
Html 부분은 특별히 설명할 것이 없으므로, CS 코드를 보자
 
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Net;
using System.IO;
using System.Drawing;
 
public partial class _Default : System.Web.UI.Page
{
         ///<summary>
         /// Cache 및 Session 에 저장될 자격인증 키값
         ///</summary>
         private static readonly string PARAM_CREDENTIALS_CACHE        = "C";
         private Uri thisUri        = null;
 
    protected void Page_Load(object sender, EventArgs e)
    {   
                  if( IsPostBack ) return;
 
                  txtFtp.Text                = "FTP://umc.pe.kr:5830/www/r.txt";
    }
         protected void btnConnection_Click(object sender, EventArgs e)
         {
                  WebClient client           = new WebClient();
                  thisUri                                     = new Uri( txtFtp.Text );
 
                  try
                  {
                           lblCache.Text = lblData.Text       = string.Empty;
                           lblCache.ForeColor                                   = Color.Red;
 
                           // Cache 및 Session 에 저장된 자격인증이 있는지 검사한다.
                           if (Cache[PARAM_CREDENTIALS_CACHE] == null)
                           {
                                   NetworkCredential ct                        = new NetworkCredential(txtID.Text, txtPassword.Text);
                                   client.Credentials                                   = ct;
                                   Cache[PARAM_CREDENTIALS_CACHE]     = ct;
 
                                   lblCache.Text                                        = "Create Credential";
                           }
                           else
                           {
                                   client.Credentials                                   = (NetworkCredential)Cache[PARAM_CREDENTIALS_CACHE];
                                   lblCache.Text                                        = "Cache Credential";
                                   lblCache.ForeColor                                   = Color.Blue;
                           }
 
                           byte[] bData = client.DownloadData(txtFtp.Text);
                           string sData = System.Text.Encoding.Default.GetString(bData);
 
                           lblData.Text = sData;
                  }
                  catch (WebException)
                  {
                           btnCacheClear_Click(this, EventArgs.Empty);
                           lblCache.Text              = "경로가 틀리거나, 인증되지 않았습니다";
                  }
         }
         protected void btnCacheClear_Click(object sender, EventArgs e)
         {
                  // Cache 또는 Session 에 저장된 자격인증을 제거한다.
                  Cache.Remove( PARAM_CREDENTIALS_CACHE );
 
                  lblCache.Text                      = "Clear Cache";
                  lblCache.ForeColor                 = Color.Black;
         }
}
 
Default.aspx.cs
 
우선 위 예제는 Cache 를 이용하여 자격 인증을 저장해 보았다.
하지만 Cache 또한 Session 과 달리 사용자별로 값을 저장하지 않기 때문에, 두개의 브라우져를 띄웠을 경우, 분명 다른 세션이기 때문에, 첫번째 브라우져에서 FTP 를 인증하게 되면, 두번째 브라우져는 인증도 하지 않음에도 인증되어 버린다.
 
만약, 사용자별로 자격 인증 상태를 저장하기 위해서는 Session 을 사용하면 된다.
소스의 Cache 를 Session 으로 바꾸어 주기만 하면 된다.
 
NetworkCredential 클래스를 ViewState 에 저장할 수 없다. 왜냐하면 ViewState는 Serializable 된 개체를 담을 수 있기 때문이다.
 
아마도 잘 응용 한다면, Active-X 를 사용하지 않는 FTP 관리 웹 어플케이션도 도전해 볼만 하다고 생각해본다.
Posted by 땡초 POWERUMC

댓글을 달아 주세요



Delegate
로 게시판을 만든다?
 
처음 우리가 웹을 접하면서 많은 부분을 할애하며 배우는 것이 게시판이다. “뭐 게시판 쯤이야” 라고 할지라도, 처음 계층형 게시판 만들기를 연습하면서 가장 머리가 뽀개질 것 같던 시간이었다. ^^;
 
게시판 만들기를 수없이 연습해 보았다면, 이젠 게시판을 어떤 구조로 만들 것인가가 중요할 것이다. 하지만, 여기에선 게시판을 통채로 만들진 않을 것이다.
이런 방법도 있다는 것도 알아두면 좋을 듯 하다.
 
위 구조는 대략 다음과 같은 다이어그램을 나타낸다.
 
 
 
소스를 보면 알겠지만, 데이터를 읽은 모든 과정은 델리게이트에게 위임하였다.
 
위와 같이 구성함으로써, DataAccess 부분으로 집중하게 될 데이터를 가져오게 될 부분을 각각 페이지로 분산시켰고, 이로써 해당 페이지에서 필요한 데이터만을 가져옴으로써, List, View, Write 페이지 및 다른 기타 페이지에서 융통성 있게 코드를 작성할 수 있다.
그러므로, DataAccess 의 코드는 그만큼 간결해 질 수 있다.
 
 
소스 살펴보기
 
///<summary>
///리스트에 출력될 데이터 모델
///</summary>
public class ListModel
{
         public ListModel()
         {
         }
 
         private string title;
         private string content;
 
         ///<summary>
         ///타이틀
         ///</summary>
         public string Title
         {
                  get { return title; }
                  set { title = value; }
         }
 
         ///<summary>
         ///내용
         ///</summary>
         public string Content
         {
                  get { return content; }
                  set { content = value; }
         }
}
[클래스 1] ListModel.cs
 
위 소스는 일반적으로 List 페이지에 뿌려질 부분의 공통적인 부분을 추출한 Entity Class 이다. 필요한 만큼 추가하면 된다.
 
 
///<summary>
/// DataAccess 클래스
///</summary>
public abstract class DataAccess
{
         public static readonly string ConnectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
 
         public delegate void FormatListRow( IDataRecord row, List<ListModel> model );
 
         public static List<ListModel> GetData( string table, FormatListRow listRow )
         {
                  SqlCommand cmd                     = null;
                  SqlDataReader reader       = null;
 
                  try
                  {
                           cmd               = new SqlCommand(
                                                              string.Format("SELECT * FROM {0}", table),
                                                              new SqlConnection(ConnectionString));
                           cmd.Connection.Open();
                           reader   = cmd.ExecuteReader(CommandBehavior.CloseConnection);
 
                           List<ListModel> model = new List<ListModel>();
 
                           while (reader.Read())
                           {
                                   listRow(reader, model);
                           }
 
                           return model;
                  }
                  catch (Exception ex)
                  {
                           throw ex;
                  }
                  finally
                  {
                           reader.Close();
                  }
         }
}
[클래스 2] DataAccess.cs
 
위 코드에서
 
public delegate void FormatListRow( IDataRecord row, List<ListModel> model );
 
이 델리게이트가 바로 데이터를 읽는 부분을 처리해줄 대리자이다.
IDataRow row 는 DataReader 의 각 ROW 를 가져오기 위한 인자이고, List<ListModel> 은 리스트목록을 저장하게 될 제네릭 컬렉션이다.
 
public static List<ListModel> GetData( string table, FormatListRow listRow )
{
while (reader.Read())
{
                  listRow(reader, model);
}
}
 
GetData 메서드는 테이블명을 인자로 받아 단순한 SELECT 쿼리를 수행한다.
 
이 코드에서 보듯이 인자값으로 델리게이트도 받을 수 있다. 이 델리게이트엔 어떤 메서드가 위임 되어 있을 것이다.
Delegate 에 메서드를 위임 하였다면 우리는 delegate 의 참조만으로 언제 어디서든 위임된 메서드를 실행 할 수 있게 되는 것이다.
 
 
///<summary>
/// ListUserControl 의 필수 구현 메서드/프로퍼티
///</summary>
public abstract class ListUserControl : UserControl
{
         public abstract void RowFormatter(IDataRecord row, List<ListModel> model);
 
         public virtual DataAccess.FormatListRow ListFormatter
         {
                  get { return new DataAccess.FormatListRow(RowFormatter); }
         }                              
}
[클래스 3] ListUserControl.cs
 
위 클래스는 샘플의 작성을 위해 임의로 구성해 본 것이다. 반듯이 위 코드처럼 구현할 필요는 없다.
우리는 Abstract 로 선언된 RowFormatter 메서드만 구현해 주면 된다.
RowFormatter 는 델리게이트에 위임할 메서드이므로 델리게이트로 선언한 인지와 반환값은 반듯이 일치해야 한다.
 
ListFormatter 프로퍼티는 델리게이트에 메서드를 위임하는 프로퍼티이다.
 
return new DataAccess.FormatListRow(RowFormatter);
 
과 같이 단순히 델리게이트에 메서드를 위임하는 하나의 과정일 뿐이다.
 
 
페이지 구성하기
 
Default.aspx 페이지는 두개의 LinkButton 에 따라 서로다른 UserControl 을 보여준다.
이 부분은 간단하므로 생략하도록 한다.
 
그럼 두개의 UserControl 소스를 연속으로 보도록 하자.
 
public partial class Stores : ListUserControl
{
         protected void Page_Load(object sender, EventArgs e)
         {
                  dgList.DataSource = DataAccess.GetData( "Stores", ListFormatter );
                  dgList.DataBind();
         }
 
         public override void RowFormatter(IDataRecord row, List<ListModel> model)
         {
                  ListModel rowModel         = new ListModel();
                  rowModel.Title             = row["Stor_Name"].ToString();
                  rowModel.Content = row["Stor_Address"].ToString();
 
                  model.Add( rowModel );
         }
}
 
[클래스 4] Stores.ascx.cs
 
public partial class Titles : ListUserControl
{
         protected void Page_Load(object sender, EventArgs e)
         {
                  dgList.DataSource = DataAccess.GetData( "Titles", ListFormatter );
                  dgList.DataBind();
         }
 
         public override void RowFormatter( IDataRecord row, List<ListModel> model )
         {
                  ListModel rowModel         = new ListModel();
                  rowModel.Title             = row["Title"].ToString();
                  rowModel.Content = row["Notes"].ToString();
 
                  model.Add( rowModel );
         }
}
[클래스 5] Titles.ascx.cs
 
위 코드는 얼핏 비슷해 보인다.
아마 이쯤에서 “아하” 하는 분들도 계실 것이다.
 
dgList.DataSource = DataAccess.GetData( "Titles", ListFormatter );
 
ListFormatter 프로퍼티는 생성된 객체의 RowFormatter 를 리턴하는 프로퍼티인 것을 기억할 것이다.
RowFormatter 는 각각의 페이지에서 필요한 데이터베이스 컬럼만을 추출하는 메서드이다.
페이지 내에서 필요한 데이터는 페이지에서 코드를 작성하면 된다.
 
각 페이지에서 필요한 데이터는 DataAccess 를 거치지 않고, 페이지 안에 정의함으로써 대리자에게 메서드의 실행을 위임하였다.
DataAccess 내에서 필요한 메서드를 각각의 페이지로 분산 시켰고, 결국 DataAccess 클래스는 한층 간결해 졌다. 페이지 코드 또한, 이리저리 클래스를 살펴볼 필요없이, 자기 페이지안에 작성함으로써 얻는 잇점이 더 클 것이다.
 
 
마치며…
 
실제로 실무 2개의 프로젝트에서 위와 같은 방법으로
[.NET/ASP.NET] - Composite Pattern 을 사용하여 MVC Pattern 구현
에서 기술한 패턴을 토대로 구현해 본 결과, 굉장히 효과적으로 코드를 작성 할 수 있었고, 관리 또한 굉장히 수월했었다.
그냥 이런 방법으로 구성 할 수 도 있다는 것 정도만 알아둬도 좋을 것 같다.
 
그럼 이만, 오늘도, 내일도, 다함께.. 고고씽~~
Posted by 땡초 POWERUMC

댓글을 달아 주세요

  1. 지송 2009.07.01 11:38 Address Modify/Delete Reply

    이런 방법도 있군요.. 잘보고 갑니다. ^^

기본적으로 웹폼을 생성하면 확장자는 ASPX 가 됩니다.
이와 달리 JSP 나 ASP 로 개발된 웹페이지는 여러확장자를 쓸 수 있지요.
가령 .html, .do, 등등.. 하지만 닷넷의 웹폼에선 확장자를 바꾸면 페이지가 동작하지 않습니다.
악의적인 목적으로 게시판이나 자료실 등 ASP 등으로 작성된 페이지를 업로드 하여, 다운로드 경로로 접근하여 페이지를 실행시킬 경우, 보안에 상당히 취약한 부분도 있었습니다.
하지만 언제나 .ASPX 확장자는 이제 식상할 때가 되지 않았습니까!
그럼 어디한번 바꾸어 봅시다.
 
우선 원하는 확장자를 IIS 에서 확장자 매핑 시키고 web.config 의 httpHandlers 에 등록해 보겠습니다.
여기에선 .umcx 라는 확장자를 사용하기로 합니다.
 
 
그다음은 web.config 의 httpHandlers 섹션에 다음과 같이 작성합니다.
<httpHandlers>
         <add path="*.umcx" verb="*" type="System.Web.UI.PageHandlerFactory" validate="true" />
</httpHandlers>
 
여기에서 위의 Type 에 등록된 클래스를 조금 짚고 넘어 가겠습니다.
PageHandlerFactory 는 IHttpHandlerFactory 를 구현한 클래스 입니다.
이 인터페이스는 IHttpHandler 를 리턴하는 GetHandler 메서드를 구현하기만 하면 됩니다.
public IHttpHandler GetHandler(HttpContext context,
        string requestType, String url, String pathTranslated)
 
이 인터페이스를 구현한 클래스를 web.config 의 httpHandlers 에 등록하게 되면 좀더 유연하게 가령, 요청이 get , post 방식에 따라 서로 다른 HttpHandler 를 반환 할 수 있습니다.
IIS 에서 확장자 매핑하면서 get,post 등 요청 방식을 지정할 수 있게 되어있습니다.
하지만 위 인터페이스를 활용하여 요청방식에 따른 각기 기능을 하나의 클래스에 넣을 수 도 있구요, 페이지 요청 방식에 따라 서로 상이한 페이지를 보여 줄 수 도 있습니다.
다음 기회 언젠가 좀 더 자세히 파 보도록 하겠습니다.
 
다시 원점으로 돌아와서,
하지만 이것만으로 끝이 아니랍니다.
우라가 작성한 페이지를 컴파일하고 컴파일 하는 동안 코드를 생성하는데 사용되는 빌드 공급자를 정의해 주어야 합니다.
 
이것또한 최상위 web.config 에 다음과 같이 정의되어 있지요
<buildProviders>
        <add extension=".aspx" type="System.Web.Compilation.PageBuildProvider" />
        <add extension=".ascx" type="System.Web.Compilation.UserControlBuildProvider" />
        <add extension=".master" type="System.Web.Compilation.MasterPageBuildProvider" />
        <add extension=".asmx" type="System.Web.Compilation.WebServiceBuildProvider" />
        <add extension=".ashx" type="System.Web.Compilation.WebHandlerBuildProvider" />
        <add extension=".soap" type="System.Web.Compilation.WebServiceBuildProvider" />
        <add extension=".resx" type="System.Web.Compilation.ResXBuildProvider" />
        <add extension=".resources" type="System.Web.Compilation.ResourcesBuildProvider" />
        <add extension=".wsdl" type="System.Web.Compilation.WsdlBuildProvider" />
        <add extension=".xsd" type="System.Web.Compilation.XsdBuildProvider" />
        <add extension=".js" type="System.Web.Compilation.ForceCopyBuildProvider" />
        <add extension=".lic" type="System.Web.Compilation.IgnoreFileBuildProvider" />
        <add extension=".licx" type="System.Web.Compilation.IgnoreFileBuildProvider" />
        <add extension=".exclude" type="System.Web.Compilation.IgnoreFileBuildProvider" />
        <add extension=".refresh" type="System.Web.Compilation.IgnoreFileBuildProvider" />
</buildProviders>
자주 보던 확장자들이 많이 있지요?
 
<compilation>
</compilation>
Web.config 의 위 섹션이 응용 프로그램의 컴파일을 담당하는 섹션입니다.
우리가 웹사이트를 작성하고 이후 수정된 사항을 복사만 하더라도 컴파일 되어 자동으로 적용되어 지는 미리 컴파일 기능 또한 위 섹션에서 담당하게 되지요~
 
이제 감이 오셨나요?
그럼 우리가 만든 페이지가 컴파일 되도록 하기 위해선 다음과 같이 섹션을 추가해 줍니다
<compilation debug="false">
         <buildProviders>
                  <add extension=".umcx" type="System.Web.Compilation.PageBuildProvider" />
         </buildProviders>
</compilation>
 
이제 모든 설정이 끝났습니다.
 
// 2009-06-20 아래의 주소는 더이상 접속할 수 없습니다.
자신이 만든 웹페이지의 확장자를 .umcx 로 바꾸어 보십시오.
위 예제 페이지는 제 블로그의 접속 차단 IP 를 위해 작성한 페이지 입니다.
 
실행결과
 
 
이번 한 주도 행운만이 가득한 한 주가 되세요~ ^^//

Posted by 땡초 POWERUMC

댓글을 달아 주세요

요즘은 시간이 많아서 그런지 자주 강좌를 올리네요 ㅋㅋ



 

이번에는 웹사이트의 핵심인 Core(코어) 모듈을 구성해 보는 시간을 가져봅니다.





잘 구성된 모듈은 안정된 시스템 그리고 코딩의 작업 속도와 바로 직결됩니다. 





모듈의 구성 방법은 개발자마다, 그리고 시스템 마다 각이 각색이겠지만, 






오늘 구성해 볼 모듈은 대부분의 웹사이트에서 적용할 수 있는 코어 구성 첫 번째




시간을 갖도록 합니다.
 




본 강의는 객체지향적인 문법을 공부하거나 소화한 분을 대상으로 합니다.
 





우리가 구성할 코어는 XML 로 구성된 사이트 맵이 필요합니다.



 
<?xmlversion="1.0"encoding="utf-8" ?>
<AdminID="ADMIN1" Path="WebAdmin">
    <Title>블로그 관리자</Title>
    <ModuleGroupID="MG1" Path="01Blog">
        <Title>기본 설정 관리</Title>
        <ModuleID="UBI1"ViewControl="/WebAdmin/01Blog/BlogBaseInfo.ascx">
            <Title>블로그 기본 정보</Title>
            <InitParams>
                <ParamName="umc">Umc</Param>
                <ParamName="umc1">Umc1