본문 바로가기
IT-개발,DB

[개발] 닷넷 어플리케이션 개발 팁 18가지

by SB리치퍼슨 2010. 12. 2.

Written by 안재우(Jaewoo Ahn), 닷넷엑스퍼트(.netXpert)

 

예전 Voice of .NETXPERT 2003 행사 때, 저희 회사의 김유철 책임이 발표했던 내용인 '아무도 가르쳐 주지 않는 .NET 애플리케이션 개발 Tips 18가지'를 정리해서 올립니다.

 먼저 첫번째로 Visual Studio .NET 관련 팁으로 시작합니다.

 Tip 1. 참조 추가 대화상자에 나의 어셈블리나 컴포넌트를 보이게 할 수 있는 방법은?

 다음과 같이 나타나게 하는 것을 의미합니다.

 

사실 처음에는 GAC(Global Assembly Cache)에 올라가면 나오지 않을까 생각했었는데, 막상 그렇지 않다는 것을 알게 되었습니다.

어쨌든 참조 대화상자에 표시하는 방법은 크게 2가지 방법이 있습니다.

 첫번째 방법은 폴더를 이용하는 방법으로서, 다음 폴더에 위치하면 됩니다.

C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\PublicAssemblies

 두번째 방법은 레지스트리를 이용하는 방법으로서, HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\7.0\AssemblyFolders의 PublicAssemblies 하위키를 이용하면 됩니다.

 Tip 2. 디버깅 빌드 속도 개선?

기본적으로 VS.NET은 빌드 수행 시 솔루션 내의 모든 프로젝트에 대해 빌드를 수행합니다. 그런데, 프로젝트의 갯수가 많아지다보면 디버깅을 위해 빌드를 수행하는데 걸리는 시간이 점점 많이 걸리게 됩니다.

이를 개선하기 위해서는 다음과 같이 옵션을 지정하여, 빌드 속도를 개선할 수 있습니다.

 

Tip 3. 인텔리센스 동적 도움말

내가 직접 만든 컴포넌트에 다음과 같이 인텔리센스 도움말이 나타나게 하려면?

 

일단 기본적으로 다음과 같은 XML 주석을 달면 된다는 것은 대부분 알고 있을 것입니다.

 

그런데 이 컴포넌트를 담고 있는 프로젝트와 이 컴포넌트를 사용하는 프로젝트가 동일 솔루션 내에 있는 경우에는 자동적으로 인텔리센스 도움말이 표시되지만, 문제는 이 컴포넌트의 DLL만 배포해서, DLL 참조를 수행할 경우입니다.

이러한 경우, 다음과 같이 프로젝트 속성에서 구성속성/빌드/XML 문서 파일에서 XML 파일의 이름을 지정해서 빌드를 수행할 때 XML 파일을 생성하게 합니다.

이제 DLL과 XML을 같이 배포하고, 이 DLL을 다른 프로젝트에서 참조하면 자동적으로 해당 XML 파일이 로드되어 인텔리센스 도움말을 지원하게 됩니다.

 

나머지 팁은 나중에 이어서 올리도록 하겠습니다.

 두번째로 .NET Framework 관련 팁 2가지입니다.

 

Tip 4. StringBuilder의 잘못된 사용

문자열(System.String)의 경우, 소위 말하는 Immutable 패턴을 따르고 있기 때문에 독특한 특성을 가지는데, 간략하게 설명하면 다음과 같습니다. 우선 다음 코드를 봅시다.

 string s = "Hello";
s = s + ", ";
s = s + "World !";

이렇게 했을 때, 실제 메모리 상에서는 다음과 같이 문자열에 해당하는 메모리 공간이 3번 점유됩니다.

 

Memory #0 : [Hello]
Memory #1 : [Hello, ]
Memory #2 : [Hello, World!]

즉 동일한 메모리 주소(#0)에 추가되는 것이 아니라, 문자열 연산을 수행한 결과의 문자열이 새로운 메모리 주소에 할당된다는 것입니다.

이에 비해 StringBuilder라는 클래스를 우리가 일반적으로 생각하는 방법처럼 동일 메모리 주소(#0)에 문자열을 추가함으로써 재할당의 오버헤드, 불필요한 메모리 공간의 낭비를 줄입니다. StringBuilder를 사용할 경우, 위 코드는 다음과 같이 됩니다.

StringBuilder sb = new StringBuilder();
sb.Append("Hello");
sb.Append(", ");
sb.Append("World!");
 

이 때문에 상당수의 .NET 프로그래밍 팁에서는 문자열 연산을 여러번 수행하는 경우, StringBuilder를 사용하면 성능이 좋아진다고 설명해놓은 경우가 많습니다.

문제는 이를 맹신하고 남용하는 사람들이 많다는 것입니다. 실제로 다른 .NET 팁을 보고 나서 무조건 StringBuilder를 사용해서 코드를 작성하는 프로그래머를 본 적도 있습니다. 과연 그게 맞을까요? 위에서 본 두 가지 경우와 아래의 경우 중 성능이 가장 좋은 것은 어느 것일까요?

string s = "Hello" + ", " + "World!";

가장 바람직한 것은 바로 위, 즉 맨 마지막의 경우입니다. 바로 위처럼 작성한 경우, C# 컴파일러는 MSIL을 생성할 때 위 코드를 다음과 같이 최적화합니다.

string s = "Hello, World!";

그러므로 문자열 상수 연산이나 보통 5개 이하의 문자열 연산에는 StringBuilder를 사용하지 말고 + 연산자를 사용하는 것이 바람직합니다. StringBuilder를 써야 할 경우는 다수의 문자열 연산을 수행하거나, 반복문(for, while 루프 등) 내에서 문자열 연산을 수행할 경우입니다.

성능 뿐만 아니라 코드 가독성 측면에서도 + 연산을 사용하는 것이 StringBuilder.Append()를 사용하는 것보다 훨씬 나으므로, 코드 생성기나 HTML 렌더링과 같은 특수한 작업을 수행하지 않는 일반적인 경우라면 + 연산을 사용하는 것을 권장하고 싶습니다.

 

Tip 5. 콘솔 애플리케이션 출력 캡쳐

요즘은 대부분 웹이나 윈도우 애플리케이션으로 작성하긴 하지만, 간혹 가다 콘솔(커맨드라인) 애플리케이션을 작성해야 할 경우도 있습니다. 이럴 경우, 콘솔 애플리케이션의 출력 내용을 캡쳐하는 방법은 없을까요?

System.Diagnostics.ProcessStartInfo 클래스를 사용하면 이를 간단하게 수행할 수 있습니다. 다음 코드를 보죠.

Process cmd = new Process();
ProcessStartInfo procInfo = new procInfo();
procInfo.RedirectStandardOutput = true;  // 표준 출력을 Redirect
procInfo.UseShellExecute = false;             // 표준 입/출력 Redirect 시에는 false로 설정
procInfo.CreateNoWindow = true;            // 윈도우를 생성하지 않음
cmd.StartInfo = procInfo;
cmd.Start();
cmd.StandardOutput.Read();                    // StreamReader를 통해 출력을 얻음
 
다음 번엔 ADO.NET 관련 팁을 올리도록 하겠습니다.
 

세번째로 ADO.NET 관련 팁 2가지입니다.

 

Tip 6. Strict Typed Parameter 사용하기

데이터액세스 작업을 수행하다보면 Parameterized Query나 SP를 사용하는 경우, SqlParameter와 같은 Parameter Class를 사용하여 매개변수를 전송해야 하는 경우가 많습니다.

SqlParameter를 만드는 방법에는 여러가지가 있지만, 일반적으로 다음 생성자를 사용하는 경우가 많습니다.

SqlParameter(string parameterName, object value);

매개변수명/값 쌍으로만 전달하면 되므로, 다음과 같이 작성하기만 하면 되어서 전반적으로 매우 편리하기 때문입니다.

 SqlParameter param = new SqlParameter("@ProductID", "50");  // Case1
SqlParameter param = new SqlParameter("@ProductID", 50);    // Case2

둘 중 어느 것을 사용하더라도 상관은 없습니다. 그런데, 이 두개만 하더라도 실제 SQL 프로파일러로 찍어보면 결과가 서로 다른 것을 알 수 있습니다.

exec ...@ProductID', N'@ProductID nvarchar(4000)', @ProductID = N'50'  // Case1
exec ...@ProdcutID', N'@ProductID int', @ProductID = 50                         // Case2

이러한 결과가 나타나는 이유는 ADO.NET이 매개변수의 값으로부터 매개변수의 Type을 추론해내기 때문입니다. 명시적으로 지정을 하든, 지정을 하지 않든 Type은 어느 경우나 반드시 필요합니다. 위의 예에서는 Case2가 좀 낫긴 하지만, 만약 ProductID가 int가 아닌 다른 숫자형이었다면?

 결론적으로 가장 바람직한 것은 매개변수의 Type을 정확하게 지정해주는 것이 좋다는 것입니다. 그러므로 매개변수명/값 쌍 형태보다는 다음과 같이 매개변수명/Type 쌍으로 생성한 후, 값을 따로 지정해주는 것이 보다 바람직합니다. Type 외에도 Size, Precision 등이 있는 경우, 이를 지정해주면 더욱 더 좋습니다.

SqlParameter param = new SqlParameter("@ProductID", SqlDbType.Int);
param.Value = 50;

프로파일링을 해보면 알 수 있지만 보다 많은 사항을 명시적으로 지정해줄 수록 서버 측 리소스를 절감해서 쿼리 속도 및 성능이 개선되는 것을 알 수 있습니다. 정리하자면 개발자의 편의성과 성능은 반비례한다는 것이 되겠죠. ^^

 

Tip 7. ADO Recordset으로부터 DataSet 만들기

기존 시스템이 ASP로 되어 있었고, ADO Recordset을 반환하는 비즈니스 로직 컴포넌트를 호출해서 데이터를 가져오는 3-Tier 구조로 되어 있었다고 가정합시다.

그러던 어느날 프리젠테이션 부분을 ASP.NET으로 교체한다고 회사에서 결정을 내렸습니다. 윗 사람은 이미 비즈니스 로직은 다 작성되어 있어서 프리젠테이션 부분만 작성하면 되니깐 금방 하겠다고 하면서 프로젝트 일정을 말도 안되게 짧게 잡아 놓았습니다. 안된다고 말을 할려니, 이전에 3-Tier 구조의 장점을 역설했던 일이 거짓말이 되어버리게 생겼습니다. 그렇다고 기존 Recordset을 그대로 사용하려니 ASP.NET에서의 데이터바인딩과 같은 장점을 전혀 사용할 수가 없습니다. 어쩔수 없이 밤을 새서라도 기존 비즈니스 로직 컴포넌트를 .NET으로 재작성하려고 생각을 했는데, 엎칱데 덮친 격으로 기존 컴포넌트에 해당하는 소스가 현재 나한테 없습니다. 이러한 경우, 도대체 어떻게 해야 할까요?

이러한 경우, 기존의 Legacy 로직을 재사용하는 것으로 방향을 잡는 것이 바람직합니다. 처음부터 재작성을 하기에는 시간과 비용이 모자라기 때문입니다.

이때 유용한 것이 바로 OleDbDataAdapter의 Fill 메서드입니다. 다른 DataAdapter들과는 다르게, OleDbDataAdapter의 Fill 메서드에는 다음과 같은 오버로딩이 존재합니다.

public int Fill(DataSet dataSet, object ADODBRecordSet, string srcTable);

이 메서드는 ADODBRecordSet의 내용을 DataSet 내에 srcTable이라는 이름의 DataTable로 채워 넣는 역할을 수행합니다. 이렇게 하여 RecordSet의 내용을 DataSet으로 옮길 수 있게 됩니다.

이렇게 할 때 장점은 다음과 같습니다.

첫째, 기존 Legacy 로직을 수정/변경 없이 그대로 재사용이 가능합니다.

둘째, 기존 Recordset이 Connection 기반이더라도 이를 통해 Disconnected 모델로 전환이 가능합니다.

셋째, 기존 Recordset의 커서 타입에 관계없이 자유롭게 Disconnected 모델에서 사용이 가능합니다.

넷째, 기존 Legacy 로직을 XML 웹 서비스나 .NET 리모팅으로 쉽게 재사용할 수 있게 만들 수 있습니다.

 

단, 이렇게 할 경우, 재사용을 통해 시간과 비용을 절감할 수는 있겠지만, 성능적인 측면에서는 오히려 저하될 수도 있다는 것에 주의해야 합니다.

 

다음 번에는 ASP.NET 관련 팁일듯 합니다. ^^

 네번째로 ASP.NET 관련 팁 8가지 중 먼저 4가지를 올립니다.

 

Tip 8. 네트워크 드라이브에 있는 ASPX 실행하기

 

웹 서버가 여러 대 있는 경우, 웹 애플리케이션이 업데이트 되었을 때 이것을 각 웹 서버에 일일이 배포하는 일은 상당히 귀찮은 작업입니다. 별도의 배포 시스템(예 : AppCenter)을 사용하여 Rolling 업그레이드를 하는 것이 정석적입니다만, 어떤 회사에서는 배포 대상 파일을 한 곳(SAN과 같은 통합 스토리지, 파일 서버의 공유 폴더 등)에만 두고 이를 네트워크 드라이브로 공유해서 사용하는 경우도 있습니다. 이 경우, 공유된 경로에만 업데이트를 해주면 모든 웹 서버에 반영되므로 최소한 배포에 있어서는 훨씬 간편해 집니다.

 

 

우선 네트워크 경로로 IIS 홈 디렉터리를 설정하는 것은 쉽습니다.

 

문제는 ASP의 경우에는 별다른 문제가 없는데, ASP.NET의 경우, 위 그림과 같은 Parser 오류가 발생하게 됩니다. 무엇이 문제일까요?

바로 .NET Framework의 런타임 보안 정책과 관련된 문제입니다. ASP.NET이 ASPX 및 코드 비하인드 DLL을 처리하려면 FullTrust 권한이 필요합니다. 그런데, .NET Framework는 네트워크 드라이브, 즉 로컬 인트라넷에 대해서는 기본적으로 FullTrust를 하지 않기 때문에 이러한 문제가 발생하게 되는 것입니다.

해결책은 위의 글 중에 답이 있습니다. 런타임 보안 정책에서 로컬 인트라넷의 권한을 FullTrust로 변경해주면 됩니다.

 

Tip 9. 애플리케이션 간에 aspx, ascx 공유하기

 

예를 들어, 다음과 같이 동일 웹 애플리케이션 내에 있지 않는(즉, 다른 웹 애플리케이션에 속한) 웹 사용자 정의 컨트롤(*.ascx)을 페이지에서 사용하려고 하면, 다음과 같은 파서 오류가 발생되게 됩니다.

 

이 문제를 해결하려면, 다음과 같은 단계로 작업을 수행합니다.

1. 공유할 웹 애플리케이션을 동일한 물리적 위치로 지정합니다. 아래는 app1과 app2가 appshared를 공유할 경우입니다. (오타 수정 : 표에서 App1/control이 아니라 App1/appshared가 되어야 합니다)

 

2. 공유할 웹 애플리케이션(appshared)의 응용 프로그램 속성을 제거합니다.

3. 공유할 웹 애플리케이션(appshared)의 web.config, Global.asax를 제거합니다.

4. 이제 각 애플리케이션(App1, App2)의 페이지에서는 사용할 ASCX를 다음과 같이 지정합니다.

<%@ Register … Src="appshared/shareusercontrol.ascx" %>

 

Tip 10. 코드 비하인드의 긴급 수정

 

다음과 같은 시나리오를 가정해 보겠습니다.

ASP.NET 애플리케이션을 개발해서 개발 PC(또는 개발 서버)에 있던 내용을 실 서버에 배포했는데, 서버 관리자가 갑자기 긴급하게 수정해야 할 사항이 발견했습니다. 그런데 공교롭게도 이 내용은 매우 사소한 것이긴 한데, 코드 비하인드 클래스에 정의되어 있는 내용이라서 소스 파일을 수정해서 DLL을 재컴파일하여 올리지 않는 한 문제를 수정할 수 없는 상황이 되었습니다.

그런데, 이미 개발자는 퇴근을 했고, 관리자는 비주얼 스튜디오와 같은 개발 도구를 설치하지 않은 상황입니다. 비주얼 스튜디오가 있다고 하더라도 관리자는 사용법을 잘 알지 못합니다.

이러한 경우에 긴급하게 조치를 할 수 있는 방법이 없을까요?

 

우선 이 방법은 서버에 배포를 할 때, ASPX와 코드 비하인드 소스 파일(.aspx.cs 또는 .aspx.vb)이 같이 배포되어 있는 상황에서만 가능합니다. 또는 수정하려는 ASPX에 해당하는 코드 비하인드 소스 파일을 가지고 있어야 합니다.

이런 조건이 성립되어 있다고 가정하고, 긴급 조치 절차는 다음과 같습니다.

1. 수정할 .aspx의 코드 비하인드 소스 파일(.aspx.cs)를 편집기 등에서 열고, 문제점을 수정합니다.

2. 위의 코드 비하인드 클래스 이름을 임시로 변경합니다. 예를 들어 ProductList라면 ProductList_1 등으로 변경합니다.

3. 수정할 .aspx에서 <% @Page .. %> 내에서 CodeBehind를 Src로 바꾸고, Inherits 내의 클래스 이름을 1에서 변경한 이름으로 바꿉니다.

    <% @Page CodeBehind="productlist.aspx.cs" Inherits="Northwind.ProductList" %>

    <% @Page Src="productlist.aspx.cs" Inherits="Northwind.ProductList_1" %>

 

일단 이렇게 하면, 긴급 수정을 할 수 있게 됩니다. 그러나, 어디까지나 이 방법은 긴급 상황에서 사용할 수 있는 임시 방편일 뿐입니다. 정상적인 수정/업데이트의 경우에는 절대로 이 방법을 사용하지 않아야 합니다.

긴급 수정을 한 이후에는 개발자에게 해당 사항을 통지해주고, 문제점을 수정해서 컴파일된 버전으로 새로 업데이트를 하도록 합니다. 최소한 컴파일된 DLL을 업데이트해야 하고, 임시로 수정한 ASPX를 다시 원래로 돌려놓아야 한다는 것에 유의하십시오.

 

Tip 11. Postback 간 스크롤 위치 고정

 

웹 폼 페이지를 스크롤해서 내린 상태에서, 포스트 백이 일어나면 스크롤 위치가 다시 맨 상단으로 돌아가버리는 단점이 있습니다.

 

이 문제를 해결하기 위한 방법은 크게 3가지가 있습니다.

첫째, SmartNavigation을 사용하는 방법입니다. 다음과 같이 page directive에 SmartNavigation을 true로 지정해 줍니다.

<%@ Page ... SmartNavigation="true" %>

이 방법은 가장 간편하긴 하지만, 페이지 내에서 클라이언트 스크립트를 사용하는 경우 Smart Navigation의 스크립트와 충돌하여 문제가 발생할 수 있습니다.

 둘째, 다음과 같이 URL 뒤에 컨트롤ID를 앵커로 지정합니다.

http://localhost/.../....aspx#DataGrid1:_ctl72:_ctl0

위 코드는 DataGrid1의 72번째 줄을 기준으로 스크롤을 하게 됩니다.

 셋째, 클라이언트 스크립트의 scrollIntoView를 사용합니다.

<scirpt> document.all('DataGrid1').rows[72].scrollIntoView(true) </script>

 해결은 가능은 하지만, 보다시피 깔끔한 방법은 없네요. ^^ 

다음번엔 나머지 ASP.NET 팁 4가지를 올리겠습니다. 

네번째로 ASP.NET 관련 팁 8가지 중 나머지 4가지를 올립니다.

 

Tip 12. 폼 인증으로 모든 파일 보호하기

 

ASP.NET을 어느 정도 다뤄보신 분은 잘 아시겠지만, ASP.NET은 내부적으로 인증 메커니즘을 제공합니다. 특별한 코딩 작업 없이 web.config에 인증(Authentication)과 권한 부여(Authorization)만 지정하면 페이지 액세스 권한을 설정할 수 있습니다. 다음 예를 봅시다.

 

<authentication mode="Forms">
  <forms name="protectall" loginUrl="login.aspx" ... />
</authentication>

 

<authorization>
  <deny users="?" />
</authorization>

이렇게 web.config에 설정하면,

- 기본적으로 쿠키 기반의 폼 인증을 사용하여 사용자를 인증하며,

- 로그인 되지 않은 익명 사용자( = ?)에 대해서는 접근을 거부(deny)하며,

- login.aspx라는 페이지로 로그인하라고 리디렉션한다는 의미입니다.

 

그런데, 문제는 폼 인증의 경우, .aspx를 요청할 때만 동작한다는 것입니다. 만약 .gif와 같은 이미지 파일을 요청하면 어떻게 될까요?

해보면 알겠지만, 로그인을 하지 않았음에도 불구하고 .gif는 보이게 됩니다. 인증 자체를 Windows와 같은 다른 인증으로 바꾸면 .gif도 방어할 수 있지만, 폼 인증에서는 기본적으로 되지 않습니다.

폼 인증을 쓰면서 .gif에 대해서도 인증 및 권한부여 메커니즘을 동작시킬 수는 없을까요?

 

방법은 다음과 같이 간단하게 할 수 있습니다.

우선, IIS 관리자를 열고, 웹 애플리케이션(또는 사이트)의 홈 디렉터리 탭을 선택합니다. 하단의 응용 프로그램 설정에서 우측에 있는 구성 버튼을 누릅니다.

그러면, 아래 그림과 같이 응용 프로그램 구성 페이지가 나타납니다. 여기서 하단의 추가 버튼을 누르고 .gif에 대한 응용 프로그램 매핑을 추가하면 됩니다. 추가 시에는 .aspx와 동일한 실행 파일 경로(C:\WINDOWS\Microsoft.NET\Framework\[버전번호]\aspnet_isapi.dll)를 지정해주면 됩니다.

 

Tip 13. 스크롤 바를 가진 DataGrid 만들기

 

DataGrid를 페이지에서 표시하려고 하는데, DataGrid의 항목이 한 페이지에 표시하기에는 너무 많다고 가정합시다. 물론 페이징을 사용해서 처리하는 것도 방법이긴 하겠지만, 다음과 같이 스크롤을 하는 것이 보기 좋을 수도 있습니다..

 

일단 이걸 처리하는 방법은 간단합니다. <DIV> 내에 DataGrid를 집어넣고, <DIV>의 스타일에 overflow:auto를 지정해주면 됩니다. 물론 auto가 아닌 다른 값을 지정해줘도 됩니다.

그런데 이 방법의 문제는 DataGrid의 머리글(Header)까지 같이 스크롤되어 버린다는 문제점이 있습니다. 우리가 원하는 것은 머리글은 그대로 있고, DataGrid의 항목만 스크롤이 되는 다음과 같은 형태가 되겠죠.

 

사실 이렇게 만드는 방법은 민망할 정도의 꽁수에 불과합니다. 어떻게 하면 되느냐 하면..

DataGrid에서 머리글을 표시하지 않도록 지정하고, 머리글은 Table 태그를 사용해서 직접 그리는 것입니다. 그릴 때는 DataGrid 안의 테이블과 크기를 잘 조정해야 하고, DataGrid를 포함하고 있는 <DIV> 바로 위에 지정해야 한다는 것에 주의하십시오.

 

<!-- 머리글 내용 -->

<Table>...</Table>

<DIV style="overflow:auto">

   <asp:DataGrid ...>

       ...

   </asp:DataGrid>

</DIV>

 

Tip 14. 서버에서 클라이언트 스크립트 처리

서버 사이드 코드에서 클라이언트 스크립트를 처리하는데는 대략 다음과 같은 시나리오가 있습니다.

- 클라이언트측 스크립트 라이브러리를 제공하는 시나리오
- 페이지 상단이나 하단에 스크립트를 생성하는 시나리오
- (페이지에 여러 개의 컨트롤 인스턴스가 존재하더라도) 페이지에 스크립트 블록이 단 한번만 나타나도록 확인하는 시나리오
- 페이지에 폼이 포함된 경우, 폼의 클라이언트측 제출 이벤트와 이벤트 처리기를 연결하도록 컨트롤을 설정하는 시나리오
- 컨트롤에 의해 렌더링된 클라이언트측 요소를 클라이언트 상에서 선언된 배열 변수에 추가하는 시나리오
- 숨겨진 필드를 통한 값 전달

- 서버 컨트롤이 렌더링된 후 컨트롤의 클라이언트 이벤트와 이벤트 핸들러 연결 

 

Page 클래스는 다양한 Register* 계열의 메서드를 통해 이러한 시나리오를 가능하게 해줍니다. 관련된 내용은 MSDN의 다음 URL을 참조하기 바랍니다.

http://msdn.microsoft.com/library/KOR/cpguide/html/cpconClient-sideFunctionalityInServerControl.asp

 

위의 붉은색 부분에 대응되도록 작성한 코드는 다음과 같습니다.

 

 private void Page_Load(object sender, System.EventArgs e)
 {
      String scriptString = "\n";
      scriptString += "<script language=JavaScript>\n";
      scriptString += "<" + "!--\n";
      scriptString += "    function showIds() {\n";
      scriptString += "        for(var index=0;index < ids.length;index++)\n";
      scriptString += "        document.write(ids[index] + '<br>');\n";
      scriptString += "    }\n";
      scriptString += "//-->\n";
      scriptString += "<" + "/" + "script>\n";
   
      RegisterStartupScript("arrayScript", scriptString);
     
      string[] ids = {"111","112","the third one","114"};
      RegisterArrayDeclaration("ids","'" + String.Join("','",ids) + "'");

      RegisterHiddenField("myHidden", ".netXpert");
       
      btnArray.Attributes.Add("onClick", "showIds()");
      btnHidden.Attributes.Add("onClick", "alert(document.all.myHidden.value); return false;");
 }

 

이 코드를 사용해서 렌더링된 HTML은 다음과 같습니다.

 

Tip 15. 웹 팜(Web Farm) 환경에서 상태 관리 문제

 

L4나 NLB를 사용하여 여러 대의 웹 서버를 묶어서 동작시키는 웹 팜(Web Farm) 환경에서 ASP.NET을 사용하는 경우, 상태 관리 문제가 이슈가 됩니다. 대표적인 것이 ViewState와 Session, 폼 인증 쿠키 등이 있겠습니다.

 예를 들어 ViewState와 관련하여 PostBack 시에 다음과 같은 에러 메시지가 발생할 수 있습니다.

 HttpException (0x80004005): The View State is invalid for this page and might be corrupted.]
   System.Web.UI.Page.LoadPageStateFromPersistenceMedium() +150
   System.Web.UI.Page.LoadPageViewState() +16
   System.Web.UI.Page.ProcessRequestMain() +421
….

 

아마 한글 메시지로는 '이 페이지의 뷰 상태가 유효하지 않거나 훼손되었습니다.'라는 걸로 나오던 걸로 기억합니다.

 이 문제가 발생하는 원인을 살펴보겠습니다.

웹 팜 환경에서는 최초 페이지 접근은 A 서버로 하고, PostBack은 B 서버로 날라갈 수도 있습니다.

PostBack 동안 ViewState가 페이지에서 저장/로드될 때는 특정한 키를 사용하여 Encrypt/Decrypt 되는 과정을 거치게 됩니다. 그런데, 이때 사용하는 키 값이 A 서버와 B 서버가 서로 다르게 설정되어 있을 것이기 때문에 위와 같은 에러가 발생하게 됩니다.

폼 인증 사용 시 쿠키와 관련한 문제도 이것과 동일합니다.

 

이 문제를 해결할 수 있는 방법은 크게 2가지입니다.

우선 첫번째는 L4 장비에서 세션이 유지되도록(즉, 한 사용자는 최초 접근한 서버로 계속 접근하도록) 설정하는 방법이 있습니다. 이 방법을 사용하면 상태 관리 문제(세션변수, ViewState, 폼인증 쿠키)를 모두 잊어버려도 된다는 장점이 있는 대신에, 로드 밸런싱의 효율성이 떨어지게 됩니다.

그래서 권장되는 두번째 방법은 바로 모든 웹 서버의 키 값을 동일하게 설정해주는 것입니다. 키를 생성하고, machine.config를 수정하는 부분에 대해서는 다음 KB를 참고하십시오.

 http://support.microsoft.com/default.aspx?scid=312906

 그러나, 세션변수(Session)의 경우에는 두번째 방법으로 해결되지 않습니다. 세션 변수는 페이지에 저장되는 것이 아니라 웹 서버의 메모리에 저장되는 것이기 때문입니다. L4 설정을 하지 않고 세션 변수를 공유하는 방법은 없을까요? 바로 ASP.NET의 StateServer나 SqlServer 모드 등을 통해 세션을 별도로 관리하는 방법이 있습니다. 여기에 관해서는 다음 URL을 참조하십시오.

 http://msdn.microsoft.com/library/KOR/cpguide/html/cpconSessionState.asp

 웹 팜에서 StateServer나 SqlServer 모드를 사용할 때는 주의해야 할 사항이 하나 있습니다. 다음 URL을 반드시 참고하시기 바랍니다.

 http://support.microsoft.com/default.aspx?scid=325056

 마지막으로 Session 변수와 ViewState에 대해 참고할 만한 좋은 문서가 있습니다. ASP.NET Program Manager인 Susan Warren이 작성한 다음 문서를 보기 바랍니다.

 http://msdn.microsoft.com/library/en-us/dnaspnet/html/asp11222001.asp

 이 팁 시리즈도 이제 다음 번이 마지막이 될 것 같네요.

출처: http://blog.naver.com/saltynut


쌈꼬쪼려 소백촌닭
반응형

댓글