천객만래 [千客萬來] (It has an interminable succession of visitors)

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


쌈꼬쪼려 소백촌닭
Posted by SB패밀리
컴포넌트 델파이 6, 7으로 업그레이드 시 dsgnintf 에러 해결하기

안냐세요..

새해 인사겸.. 해서 팁을 하나 올리게 되네요.
델파이 컴포넌트를 델파이 6, 7 버전으로 업그레이드할 때

dsgnintf와 proxies 에러가 발생하는 경우를 자주 보게 됩니다...

DsgnIntf.pas 파일을 델파이 7에서

'DesignIntf.pas', 'DesignConst.pas', 'DesignEditors.pas'로 변경되었습니다.

따라서, 보통은 컴포넌트 컴파일시에 DsgnIntf.dcu 에러가 발생하면

보통 다음과 같이 uses절에서

'DsgnInft' --> 'DesignIntf'

로 대체해주면 됩니다만..
다음과 같은 방법으로 해보는 것도 좋을 것 같습니다.

{$I DFS.INC} { Standard defines for all Delphi Free Stuff components }

uses
...
{$IFDEF DFS_COMPILER_7} DesignIntf {$ELSE} DsgnIntf {$ENDIF};


---------------------------------------------------------------------------------------------

참고로 'Dfs.inc' 파일은 Delphi Free Stuff: http://www.delphifreestuff.com/ 에서 구할 수 있습니다.
그래도, 편하게 구하도록 여기에 업로드 하도록 하겠습니다.
dfs.inc 파일로 올리니까.. html, php 파일이라며 안되니까.. 작아도 압축하겠습니다.

- 쌈꼬쪼려 소백촌닭 -
Posted by SB패밀리

프레임워크를 만들었음에도 불구하고 초기 프레임워크 ㅐ념이 나올 때의 글만 보다가 요즘 다시 보니 새로운 느낌까지 들고 너무 책을 등한시 했다는 생각이 든다.
출퇴근시간이 1시간이상 소요되는 되다가 저녁먹고 나서는 건강을 위해서 운동을 해야하고
요즘 경제서적보랴 ... 한동안 등한시 해왔던 개발이론 분야....
다시 책을 집어 들고 인터넷에서 여러 개발자분들의 노고를 감사히 읽으면서 이해를 해야겠다.

프레임워크 생산성의 향상

•프로그램에서의 생산성 향상 방안 : 코드의 재사용
•Function, Procedure 개념 지원 언어의 출현, 이후 객체지향 언어가 출현
•컴포넌트 사용 : 재사용을 높이고 검증된 코드를 사용하게됨 --> SW 품질이 높아지고 개발비용/유지비용이 낮아짐
•컴포넌트 재활용과 디자인 재활용
•객체지향 프로그램에서는..
•클래스 : 기능을 모듈화 & 추상화 지원
•추상클래스 : 객체간 프로토콜 일치 가능
•동적바인딩 : 프로시저 호출 지연 바인딩(Late-binding)
•클래스 상속 : 서브클래스에서 기능을 추가/보완
•다형성 : 객체의 기능을 교체
•랄프존슨이 말한 프레임워크
•추상클래스들의 집합과 상호 협조하는 클래스들의 인스턴스 동작방법으로 이루어진 재사용 가능한 디자인
•예제) 윈도우 애플리케이션용 프레임워크
•화면의 로직처리
•윈도우 그리는 컴포넌트
•통신 컴포넌트
•내부 정보 처리하는 도큐먼트 컴포넌트
•등등등..

Framework이란 무엇인가? 

 Ralph Johnson 이라는 사람은 추상클레스( abstract class) 들의 집합과 상호 협조하는 클래스들의 인스턴스 동작 방법으로 이루어진 재사용 가능한 디자인이라 정의.

프로젝트에 어울리는 잘 만들어진 프레임워크를 이용한 어플리케이션은 비록 거대해지더라도 프레임워크의 의도에 맞게 어플리케이션의 설계가 이루어짐. 때문에 어플리케이션에 의해 System의 안정성을 해치거나, 터무니없이 낮은 퍼포먼스를 내는 일은 거의 없을 것 임.

라이브러리와 프레임워크의 차이!
라이브러리는 개별적인 기능들을 집단형태로 묶음을 의미.
때문에 라이브러리를 이용해 문제를 해결하고자 할때는 메소드의 기능을 정확히 이해하고 사용해야 함.
프레임워크는 원하는 기능을 제공하는 모듈의 인스턴스와 이에 수반되는 여러가지 장치 들의 상호작용까지의 묶어서 정의 하게 됨.때문에 프레임워크에서 제공되는 기능을 사용하게 되면 이와 수반된 이미 검증된 작업의 플로우까지 함께 사용하게 되는 것입니다.
이렇게 함으로 해서 어플리케이션의 코드를 줄일 수 있고, 안정성을 높이며 개발 속도를  빠르게 할 수 있습니다.


라이브러리 대신 프레임워크 사용하는 이유

•라이브러리
•정의 : 해결하고자 하는 도메인 문제와 상관없이 재사용할 컴포넌트의 집합
•사용시 : 제공하는 메소드의 기능을 정확히 알고 있어야 함 (목적, 인자, 결과값)
•프레임워크
•정의 : 해결하고자 하는 도메인 문제에 특화된 인스턴스들이 함께 동작되는 컴포넌트 집합과 디자인
•사용시 : 필요한 지점에서 적절히 삽입하고 쉬운 사용, 세부적인 부분은 몰라도 됨
•프레임워크 = 반제품(Semi-Complete Application) : 적은 비용으로 App 만들기 위해 절반정도는 구현되어 있는 수준

•예제 : 신뢰성 있는 메시징, 트랜젝션 개발시
•라이브러리 : 각 라이브러리별 메소드를 알고, 개발해야 됨
•프레임워크 : WCF 쓰면 내부의 세부사항은 몰라도 기능 흐름만 파악하면 되며, 코드가 주어듬


좋은 프레임워크와 효과적인 사용법

•일반적인 프레임워크 사용방법
•1) 클래스의 서브클래스 생성
•2) 생성된 객체의 내부환경을 설정
•3) 프레임워크가 제시하는 표준 방법으로 App에 적용
•좋은 프레임워크의 요소
•효율성(Effectiveness) : 개발자에게 효율적이여야, 개발 속도를 빠르게 해야, 개발비용을 줄여야 함
•확장성(Extensibility) : 특정 도메인의 App 개발시 개발항목을 포괄하는 확장성을 가져야 함 (App이 삼각형 윈도우를 지원해야 한다는 식의 무리한 확장성은 포함 안해도 됨)
•적용규모(Coverage) : 구현사항을 어느 범위까지 적용할지?
•평이성(Simplicity)와 이해도(Understandability) : 교육이 쉬워야, 너무 복잡하거나 추상적이면 안됨
•통합성(Framework Intergration) : 다른 프레임워크나 라이브러리와 상호소통이 잘되면 좋다
•품질(Qualities) : 능률성,적용성,표준성.. 필수항목은 아닌듯
•안정성(Stability) : 개발코드에 정확한 계산과 결과물 도출


프레임워크 도입시 고려할 점

•필요이상으로 복잡해지고 코드나 객체가 커지면 안됨
•랄프존슨 : "원할하게 일할 수 있을 만큼 이 프레임워크가 쉽게 느껴지는가?"
•프레임워크 도입시 더 비효율적이 될거 같으면 때려치워라! (개발비용, 교육, 문서화 작업 등...)


프레임워크 만들기

•추상 개념을 먼저 생각하기는 어렵다.. 먼저 구현해 보자
•먼저 구현 -> 문제 해결 방법을 작은 집합으로 나눔 -> 구현체 중 컨텍스트를 인자화해 변경할 수 있도록 수정
•도메인 전문가가 만들때 : Up-Front 디자인 방식
•코딩 전문가가 만들때
•반복적인 구현을 통해 코드를 개선해 나감
•반복 개발시 Hot Spots과 Frozen Spots를 잘 구분해서 다음번 개발시 잘 구성하자
•App이 다 만들어지면 이전 프레임워크와 비교하여 재사용성 부분을 반영
•구현클래스에서 추상클래스 유추하기
•구현클래스에서 일반적인 부분들을 뽑아서 추상클래스 만들기
•1) 공통 기능의 구현클래스 준비
•2) 구현 클래스의 공통 기능을 하나의 메소드로 옮긴다.
•3) 옮겨진 구현 클래스의 멤버로 있는 메소드의 이름을 동일하게
•4) 이름을 변경한 메소드의 인자, 리턴값을 동일하게 일반화
•5) 해당 메소드를 분리하거나 병합할 필요시 리팩토링
•6) 같은 메소드명이지만 다른 구현이라면 추상클래스에서 메소드명을 반영
•7) 구현 클래스의 메소드 내용이 같다면 수퍼클래스로 구현을 옮김
•상향식 설계
•하향식 : 구현 클래스 먼저 만들고 이를 바탕으로 추상클래스 만듬
•상향식보다 쉽다.
•R = I + J = AX + AY = A(X + Y) --> 여기서 A는 공통부분

필요한 설계 원칙
 
•의존 관계 역전 원칙(The Dependency Inversion Principle)
•인터페이스 분리 원칙(The Interface Segregation Principle)
•리스코프 치환 원칙(The Liskov Substitution Principle)
•단일 책임 원칙(The Single Responsibility Principle)
•개방 폐쇄 원칙(The Open-Closed Principle)


프레임워크 개발의 9 단계

•http://st-www.cs.illinois.edu/users/droberts/evolve.html
•시간축에 따라 동시에 진행할 수도 있다.
•1) Three Example : 세가지 애플리케이션 패턴
•2) White-box Framwork : 화이트박스 패턴
•3) Component Library : 컴포넌트 라이브러리 패턴
•4) Hot Spots : 핫 스팟 패턴
•5) Pluggable Object : 플러그러블 오브젝트 패턴
•6) Fine-grained Object : 파인-그레인드 객체 패턴
•7) Black-box Framework : 블랙박스 패턴
•8) Visual Builder : 비주얼 빌더 패턴
•9) Language Tools : 언어도구 패턴

프레임워크 개발은 여러 도메인에 대하여 여러가지 문제를해결할 수 있도록 만들어지기 때문에 특정 도메인의 간단한 문제를 해결하기 위하여 기능과 구현이 복잡해지고 코드의 크기나 객체의 크기가 커질 수도 있음.

Posted by SB패밀리