보통 먼저 작업 관리자에서 브라우저의 CPU 사용률을 체크한다 이 때 IEXPLORER.EXE 가 100이라면

스크립트 문제인 경우이다. 그런데 점유율은 정상인데도 불구하고 문제가 발생하는 경우가 있다.

바로 브라우저의 연결 수 제한 때문에 발생한다.

이 문제가 발생하면 ie6에서는 일단 브라우저 자체가 먹통이 되어버린다. 한참을 기다리면 풀리긴 하지만

결과를 받지 못하는 현상이 발생한다.

게다가 사이트에서 사용하는 이미지가 많으면 많을수록 이런 문제는 잦아질 수 있다.

ie6, ie7, firefox2 가 연결수 2개로 위와 같은 문제가 야기될 수 있다.

대안은 첫째로 요청하는 파일들의 호스트를 분산시키는 방법이다. 라고 써있는데 이는 크게 영향이 없는 듯 하고,

둘째로는 ajax요청을 할 때 ajaxQueue 와 같은 플러그인을 사용해서 비동기 연결을 관리하는 방법이 있고,

셋째로는 클라이언트들에게 연결 수 제한을 풀도록 방법을 제시하거나, 브라우저 버전 업데이트,

최신 브라우저 사용을 요구하는 방법이 있다.

세번째 방법이 제일 효율적이면서도 단순한 방법이지만, 클라이언트가 웹 상의 레지스트리를 다운받아 적용해야하는

문제가 있고, 브라우저 업데이트도 ie는 운영체제상(?) 문제로 오래 쓰던 사람이 업데이트를 할 경우 문제가 발생할 확률이 높고,

시각적인 요소를 통해 ie6외에 다른 브라우저를 사용하도록 유도하는것이 정답이라 생각한다.

개인적으로는 ie6를 절대 버릴 수 없기에 항상 ie6를 생각하며 개발을 해 왔지만 이런 문제는 원인파악도 어렵고 참 난감하다.

ie6에서 연결수를 늘리는 레지스트리를 첨부한다.


11. 8 추가 >>

ie6 또는 올드 브라우저에서 짧은 시간에 여러 번 ajax호출을 했을 때 어느 순간 응답 시간이 30초 ~ 5분 사이로 길어지는 현상. ajax 호출 시 async를 false로 설정시 브라우저가 프리징되는 현상이 발생한다.

2개의 연결 수 제한과, 브라우저 스크립트 엔진의 조화라 생각한다. 특히 연결 수 제한을 초과한 연결에 대해 컨트롤을 할 수 없게 되어 연결이 닫히지 않고 계속 남게 되어 생기는 현상이다.

모던브라우저 사용을 권장하고, 부득이할 경우 연결 수 제한을 늘려주는 컨피그 파일을 배포하고, 호출 시 헤더 Connection 값을 "close" 로 설정하는 방법뿐이 없다.



홈페이지가 일반에 많이 보급되고 막 사용되기 시작할 무렵에는 회원가입시 패스워드를

그냥 데이터베이스 필드로 저장했다.

비밀번호를 잊은 사용자에게 바로 찾아줄 수 있는 편리함이 있었지만 해킹당할 경우

패스워드가 그대로 노출된다는 치명적인 단점이 있었다.

그래서 시간이 지나자 프로그래머들은 패스워드를 암호화(정확히는 해쉬)하여 저장하기 시작했다.

그리고 로그인시에 사용자가 입력한 값을 똑같이 해쉬화 한 후 위의 저장된 비밀번호 해쉬 값과 대조해

같으면 인증되는 방식이었다. 안전하다 생각하여 지금도 많이 쓰이고 있지만 여기에도 한 가지 단점이 있다.

바로 비밀번호가 같은 사용자는 해쉬 값도 같다는 것이다. 만약 이런 레코드가 많으면 많아질수록 해당 계정은

비밀번호를 유추해 낼 수 있다는 것이다.


그래서 나온것이 Password Salt 생각보다 어려운 개념이 아니다.

같은 비밀번호를 해쉬할 경우 같은 해쉬값이 나온다. 간단하게 비밀번호를 다르게 해주면 된다.

그렇다고 사용자가 입력한 비밀번호를 바꿀 순 없다.

대신 랜덤으로 생성한 코드를 비밀번호와 섞어서 해쉬화를 하는 것이다. 이 때 사용되는 랜덤으로 생성한 코드를 Password Salt 라 부른다.

아래는 Salt를 생성하는 VB 코드 샘플이다. MSDN 참조


Private Shared Function CreateSalt(size As Integer) As String
    Dim rng As New RNGCryptoServiceProvider
    Dim buff(size) As Byte
    rng.GetBytes(buff)

    Return Convert.ToBase64String(buff)
End Function

먼저 아래와 같이 테이블을 생성한다.

CREATE TABLE [dbo].[Membership](
	[ID] [int] IDENTITY(1,1) NOT NULL,
	[Email] [nvarchar](256) NULL,
	[Password] [nvarchar](128) NULL,
	[PasswordSalt] [nvarchar](128) NULL,
	[CreateDate] [smalldatetime] NULL,
	[LastLoginDate] [smalldatetime] NULL
)

그리고 회원가입시 Password Salt를 생성하고 사용자가 입력한 비밀번호를 아래의 함수를 통해 해쉬해서

INSERT 한다.

Private Shared Function CreatePasswordHash(pwd As String, salt As String)
    Dim saltAndPwd As String = String.Concat(pwd, salt)
    Dim hashedPwd As String = FormsAuthentication.HashPasswordForStoringInConfigFile(saltAndPwd, "sha1")
    Return hashedPwd
End Function

유저 쿼리는 아래와 같이 처리하면 된다.

SELECT * 
FROM dbo.Membership 
WHERE Email = @email AND
	[Password] = CONVERT(NVARCHAR(128), 
	HASHBYTES('SHA1', CONVERT(VARCHAR, @pwd) + (SELECT PasswordSalt FROM Membership WHERE Email = @email)),
	2)

예제는 단순히 비밀번호 뒤에 Salt를 붙여서 해쉬를 하고 있지만 조금 더 복잡한 패턴을 만들어 적용시키면 강력한 패스워드 시스템을 만들 수 있다.



UpdatePanel과 드롭다운에 몇가지 프로퍼티를 입력해 DB에서 자동으로 내용을 가져오는

커스텀 컨트롤을 같이 사용할 때 스크립트 오류가 생겨서 고생했었다.

생각해보니, 커스텀 컨트롤은 데이터가 바인딩 되는 시점을 우리가 결정할 수 없어 비동기 포스트백이

일어났을때의 결과를 예측할 수 없었다.

그래서 포스트백일 때에 기본 드롭다운을 사용하여 바인딩을 해 주었더니 문제없이 작동했다.


이 글은 Elijah Manor의 MIX11 강연 내용을 정리한 것이다.

1. False-y 값 검증
자바스크립트에는 6개의 False-y Value가 있다. (false, null, undefined, "", 0, NaN)
그래서 어떤 특정한 Object를 검사할때는 주의를 기울여야 하는데 아래가 슬라이드에 있는 예이다.
좋지 않은 예
if (someString != null && someString.length > 0) {
    //Do something here...
}if (!string.IsNullOrEmpty(someString)) {
    //Do something hrer...}

올바른 예
if (someString) {
    //Do something here...
}


Empty string 일 때 기본값 넣기 틀린 예
if (someString == null) {
    someString = "default value";
}
someString = someString ? someString : "default value";
someString = someString ?? "default value";

Empty string 일 때 기본값 넣기 올바른 예
    someString = someString || "default value";

2. 비교 연산자 사용
이건 다들 아는 내용이지만 == 연산자는 Unexpected Comprison으로 타입은 비교하지 않기 때문에 오작동의 우려가 있다.
오작동의 예
console.log(0 == '');                // true
console.log(0 == '0');               // true
console.log(false == '0');           // true
console.log(null == undefined);      // true
console.log(0 == '\t\r\n');          // true

올바른 예
console.log(0 === '');               // false
console.log(0 === '0');              // false
console.log(false === '0');          // false
console.log(null === undefined);     // false
console.log(0 === '\t\r\n');         // false

3. 배열과 오브젝트의 선언
역시 배열 또는 오브젝트 선언 땐 new 구문은 피하는게 좋다.
좋지 않은 예
var person = new Object(),
key = new Array();

올바른 예
var person = {
    firstName: "Elijah",
    lastName: "Manor",
    sayFullName: function() {
        console.log(this.firstName + " " + this.lastName);
    }},
    keys = ["123", "676", "242", "4e3"];


5. for 구문의 사용 (14:00)
이 구문을 다른 스크립트 라이브러리에서 실행할 경우 다른 결과를 보인다. 영상에서는 dojo로 돌렸을땐 그나마 정상이었지만 mootools로 실행했을때는 치명적인 결과를 나타내는 것을 볼 수 있다.
틀린 예
var myArray = [], name;
myArray[5] = "test";
console.log(myArray.length);
myArray.prototype.someVariable = "Where did this come from?";
for (name in myArray) {
    console.log(name, myArray[name];
}
(개인적으로 이게 좀 충격.)

좋은 예
for (var name in object) {
    if (object.hasOwnProperty(name)) {
        //Your code here!
    }
}
위의 방법으로 체크하지 않으면 개발자가 넣은 값 이외에 프로토타입의 내용까지 노출되는 불상사가 생길 수 있다. 예를들어
var Person = function (firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
    return this;
};
Person.prototype = {
    isMarried: false,
    hasKids: false
};
var john = new Person("John", "Smith"),
     linda = new Person("Linda", "Davis"),
     name;

john.isMarried = true;
for (name in john) {
    console.log( name + ": " + john[name]);
}
// 위는 isMarried와 hasKids 값 까지 가져온다... 예외다.
// 하지만 아래는 딱 예상했던 값만 가져온다.
for (name in linda) {
    if (linda.hasOwnProperty(name)) {
        console.log(name + ": " + linda[name]);
    }
}

6. 지역변수의 범위 (17:00)

일반적으로 C# 또는 VB에서는 블록 단위로 지역변수가 결정된다. 예를 들어 같은 function 내 일지라도IF 문 안에 선언된 변수는 지역변수로서 동작하게 된다. 자바스크립트는 Function 단위로 지역변수가 결정된다.

jQuery 소스를 보면서 왜Function 처음에 변수들을 징그럽게 선언해놓았는지 궁금했을것이다. 필자도 그랬다...

그러니 일반적으로 코딩을 한 후 지역변수들을 전부 Function의 맨 위로 올려줘 깨끗히 정리하면 된다.

try
{   
    sayHello(); //만약 맨 아래의 함수를 작성하지 않으면 오류가 발생.
} catch (e) { 
    alert(e.message);
}

// sayHello 지역 변수 함수 생성 영상에선 Function Expression 이라 한다.
var sayHello = function () {
    alert("Hello!");
};

sayHello(); //위에 변수 함수를 선언했으므로 정상적으로 실행된다.

// sayHello 함수를 선언 영상에선 Function Statement 라 한다.
function sayHello() {
    alert("Goodbye!");
}

6. 클로저의 사용(27:00)

var unorderedList = $("ul");
for(var i = 0; i < 10; i++) {
    $("<li />", {
        id: i,
        text: "Link " + i,
        click: function() {
            console.log("You've clicked " + i);
        }
    }).appendTo(unorderedList);
}
//항상 10을 클릭했다는 메시지만 나타난다.

페이지의 ul에 0부터 10까지 리스트를 만들고 클릭시 i번째를 클릭했다는 메시지를 띄우도록 설계된
코드이다.

영상에서 보신 대로 이 코드는 어떤 리스트 엘리먼트를 클릭해도 열 번째의 리스트를 클릭했다는 메시지를 로그한다.
가장 나중에 만든 요소의 함수만이 실행되는 오류가 발생한다. 클로저는 이럴 때 사용한다.

function makeAdder(x) {
    return function(y) {
        return x + y;
    };
}
var add5 = makeAdder(5);
var add10 = makeAdder(10);
console.log(add5(2)); //7
console.log(add10(2)); //12

생성된 함수를 로컬 변수에 담아두는 것이다. 그래서 나중에 호출할 수 있게 한다.
위에서는 생성된 함수를 add5, add10에 담아두었다가 호출하는 것으로 클로저를 사용하는 코드이다.
클로저를 사용해 다시 작성하면,

var unorderedList = $("ul");
for(var i = 0; i < 10; i++) {
    $("<li />", {
        id: i,
        text: "Link " + i,
        click: function(index) {
            return function() {
                console.log("You've clicked " + index);
            }
        }(i)
    }).appendTo(unorderedList);
}

그런데 코드가 조금 복잡하다. 그래서 아래와 같이 수정한다.

인라인 함수를 바깥으로 빼면서 가독성이 좋아졌다.

var unorderedList = $("ul");
for(var i = 0; i < 10; i++) {
    $("<li />", {
        id: i,
        text: "Link " + i,
        click: clickEventHandler(i)
    }).appendTo(unorderedList);
}

function clickEventHandler(index) {
    return function() {
        console.log("You've clicked " + index);
    }
}

먼저 이 글을 읽기 전에 

lancers, .NETXPERT - ASP.NET AJAX 클라이언트 스크립트에서 웹 서비스 호출 시 전역 예외 처리

이 글을 읽는다면 훨씬 이해가 빨리 될 것이다.

간단하게 설명하자면 Httpfilter를 따로 구현하여 웹 서비스 비동기 호출 시 발생하는

error status 500과 content type "application/json" 을 잡아내는 방법이다.

사실 VB.NET은 C#.NET에 비해 사용 빈도가 많이 낮다.

발표된 왠만한 기사들은 C# 에 비해 VB가 많이 느리다고 적어 놓고 있지만 꾸준한 업데이트를 통해

VB.NET도 주의를 해 가며 코딩할 경우 속도가 비슷하거나 상회할 수 있다.

현재 글쓴이는 VB.NET을 사용해 프로젝트를 진행 중에 있는데, 도중 웹 서비스에서는 따로

예외 처리를 하지 않을 경우 그냥 무시되고 지나가 버리는 맹점이 있었는데, 위의 글을 토대로

VB.NET 버전의 테스트용 프로젝트를 작성해 보았더니 매우 잘 작동하여 프로젝트를 업로드 한다.

Visual Studio 2010 프로젝트이다.



홈페이지와 페이팔간에 연동을 위한 방법은 한 가지만 있는 게 아니다.

한 대여섯 가지 되는데, 그 중에 제일 간편한 WPS 와 IPN을 사용해서

연동하는 방법을 간략히 소개하고자 한다. 참고로 이 방법은 카트를 전송해 계산 하는것이 아닌

개별적인 상품을 계산하게 하는 방법이다.

카트를 사용해야 한다면 쓰되 이 방법으로 카트의 상품들을 한데 묶어 계산하도록 하는 법과,

페이팔에서 제공하는 다른 방법을 이용하면 된다.

고객이 우리의 사이트를 돌아다니다 상품을 보고 (또는 카트를) "구입" 클릭했다.
페이팔 사이트로 이동해 billing address를 입력하고 결제를 했다.
페이팔 서버는 페이팔에 등록된 쇼핑몰 홈페이지의 리스너 주소로 IPN 메시지를 보낸다.
쇼핑몰은 받은 데이터를 다시 페이팔에 보내 정상적인지, 변조되지 않았는지 여부를 받는다.
받은 정보를 홈페이지에 있는 결재 정보와 비교해 정상이면 결재 처리

페이팔 서버로 넘기는 변수가 상당히 많고 복잡한데 사용하는 것은 몇개 안 된다.

넘기는 것 중에 가장 중요한 변수는 cmd, business, item_name, amount, invoice

순서대로 명령, 판매자 페이팔 메일주소, 상품 이름, 금액, 주문ID(?) 이다.

주문ID는 결재가 승인되었다는 메시지를 받았을 때 이 변수의 값을 토대로 결재한 레코드를 찾아

업데이트를 하기 위해 사용된다. 그러니 고유한 값이어야 한다.

만약 판매하는 상품이 배송이 필요없는 디지털 상품이면 no_shipping 을 1로 넘긴다.

이 값에 따라 주소를 안 받을 수도 있다.

다음으로 받는 변수는 위에서 쇼핑몰에서 페이팔에 두 번째 보낼때 받는 리턴 메시지로,

승인이 되었을때는 "VERIFIED" 를 리턴하며, 실패했을 때에는 "INVAILD" 를 보낸다

VERIFIED 일때 페이팔이 보내온 변수를 읽어 판매자 메일 주소(receiver_email)이

나의 페이팔 판매자 메일 주소와 일치하는지, 결재 상태(payment_status)는 "Completed" 인지,

금액(payment_amount)가 주문 레코드의 금액과 일치하는지, 트랜잭션 아이디(txn_id)가 이미 처리했던

주문인지를 체크하여 통과 하면 결재 처리를 해 주는 것이다.

이게 개념적으로는 간단한데 여러 가지 제약사양 때문에 테스트 해 보기가 힘들다.

먼저 페이팔에 등록한 IPN 리스너 주소가 등록한지 얼마 안 되어 DNS에서 못 알아보기 때문에

등록 후 기다려야 한다. (가장 큰 제약)

IPN을 보낸다고 해도 도착이 늦을 수 있다. 필자는 한 3~4분 걸려서 하는동안 성질났었다.

디버깅도 애매 하다. 시뮬레이션 툴이 있지만 좀 그렇다...... 그나마 IDE가 좋아서 쉽게 할 수 있었다.

간단하게 문서를 링크한다.

http://www.codeproject.com/KB/aspnet/paypal_c_aspnet.aspx 방법 소개와 구현

https://www.x.com/index.jspa 페이팔 디벨로퍼 사이트

http://www.sandbox.paypal.com 페이팔 샌드박스(이곳을 이용하면 가상으로 결제를 테스트해 볼 수 있다. IPN 시뮬레이터도 이곳에서 사용할 수 있다.)



실버라이트 소켓 프로그래밍을 하던 중에 간단하게 Echo Server를 만들어보기로 하고

Policy Server 와 Echo Server를 만든 후에 실행시키고 클라이언트를 실행시켰다.

그런데 PolicyServer에만 접속하고 나서 EchoServer에는 접속이 안되길래

디버그해 원인을 분석해 본 결과 다음과 같은 오류 메시지를 볼 수 있었다.

Error Code 10013 Access Denied 소켓 엑세스에 금지된 방법으로 엑세스 하려....

구글링을 통해 알아본 바로는 방화벽이 막고 있거나, 정책 파일의 문제이거나

하다고 해서 방화벽도 꺼 보고 해보았는데 안되길래 몇 가지 테스트를 거친 결과

결국 정책 서버가 클라이언트에게 정책을 제대로 전달해주지 못해 해당 포트를 엑세스 할 수 없게

되어있던 것이였다. 나는 분명 책 대로 정책 서버를 작성했는데. 이래서 책은 별로 믿을 게 못 되나보다.

MSDN에 있는 C#정책서버 예제 코드를 기반으로 VB로 작성한 후 실행하니 별 문제 없이 동작하는

것을 볼 수 있었다.


Silverlight4 를 공부하던 중에 심화학습으로 서버에 있는 ResourceDictionary파일을 동적으로 로드해

클라이언트의 요소들에 적용하는 기능을 구현해 보았다. 먼저 서버에 보안 정책 파일을 저장하고
 
ResourceDictionary 파일인 SharedResource.xaml 을 같이 두었다.

그 다음 xaml에 사각형 하나 만들고, 버튼을 누르면 서버에서 사전을 로드해 사각형에

스타일(RectangleStyle) 을 적용하는 코드를 작성한다.

전체 소스이다.

Imports System.IO
Imports System.Windows.Markup

Partial Public Class sample_downloadRes
    Inherits UserControl

    Public Sub New()
        InitializeComponent()
    End Sub

    Private Sub btnLoadRes_Click(sender As Object
        , e As System.Windows.RoutedEventArgs) Handles btnLoadRes.Click
        Dim client As New WebClient

        AddHandler client.DownloadProgressChanged, Sub(_sender As Object
            , _e As DownloadProgressChangedEventArgs)
                tbxInfo.Text = "Downloading... " & _e.ProgressPercentage.ToString()
            End Sub

        '다운로드가 끝나면 _e.Result로 스트림 형식으로 가져올 수 있습니다.
        AddHandler client.OpenReadCompleted, Sub(_sender As Object
            , _e As OpenReadCompletedEventArgs)
                tbxInfo.Text = vbNewLine & "Download Complete."
                Dim sr As StreamReader = New StreamReader(_e.Result)
                Dim myXaml = sr.ReadToEnd()
                sr.Close()
                Dim rd As ResourceDictionary = XamlReader.Load(myXaml)
                rect.Style = rd("RectangleStyle")
            End Sub

        client.OpenReadAsync(New Uri(http://localhost:8080/SharedResource.xaml")
            , UriKind.Absolute)
    End Sub
End Class


다음과 같은 데이터를 웹 서버로 넘긴다고 가정할 때
var param = new Array();
param[0] = { seq: 1, name: "test" }
param[1] = { sql: 2, name: "array" }
var data = JSON.stringify({"myData":param});
웹 서비스에서 이렇게 작성해 주면 된다.
imports System.Web.Script.Serialization

 _
Class Users
    Private _seq As Integer
    Public Property seq() As Integer
        Get
            Return _seq
        End Get
        Set(ByVal value As Integer)
            _seq = value
        End Set
    End Property
    Private _name As String
    Public Property name() As String
        Get
            Return _name
        End Get
        Set(ByVal value As String)
            _name = value
        End Set
    End Property
End Class

 _
Public Function myService(ByVal myData As List(Of Users)) As String
    For Each item In myData
        '이곳에서 사용
    End For
End Function

클래스 내에 다른 클래스를 포함하여 좀 더 복합적인 구조로도 사용이 가능하다. 아마 웹 서비스에서 json이나, 텍스트를 받아서 처리를 하는 것 보다 안전할 것이라 생각한다.

Tree view를 이용해서 도큐먼트를 만들 필요가 생겨

마스터페이지에 Treeview를 생성하고 Navigate를 하도록 만들었는데

3.5에서부턴 Treeview가 UpdatePanel을 사용할 수 있게 업데이트되었다고 하여

사용해봤지만 역시 생각했던 대로 동작하지 않았다.

자식 노드를 선택해 페이지를 리로드하면 트리뷰의 상태가 없어져버리는 것.

세션 또는 쿼리 스트링을 사용하면 가능하긴 한데 전자는 그냥 좀 꺼려지고

후자는 페이지 주소가 더러워져서 사용하기 싫었다.

여러 고민을 해본 결과... 그냥 자바스크립트로 제어하는게 제일 좋은듯 싶다.

이런 일이 여러번 있었지만 컨트롤의 구현 편함 때문에 계속 이 지경이 된다.

내 자리에 있는 메모지에 큼지막하게 적어놓았다. "ASP 서버 컨트롤은 답이 없다."