저자: 마이크 건더로이(Mike Gunderloy), 역 C# and ASP.NET Masters의 홍영기
.NET에는 많은 계층이 있으며 새로 출시된 릴리즈는 모든 계층에 대한 변화를 포함하고있다. 우선 .NET코드 실행을 관리하는 CLR이 있고 그 위에는 .NET코드로 작성된 유용한 클래스의 집합인 FCL(Framework Class Library)이 있다. Visual Studio .NET과 다른 유용한 개발 환경 계층은 FCL위에 있는 단축기와 코드 발생기에 있고, 애플리케이션 소프트웨어는 스택의 가장 위에 있게 된다.
.NET 1.0에서 .NET 1.1로 업그레이드 되면서 볼 수 있는 가장 흥미로운 변화도 바로 이 FCL에 있다. 마이크로소프트는 새로운 버전 출시의 결과로
100가지가 넘은 파격적인 변화를 정리하였다. 만약 닷넷 개발자들이 쉽게 접근할 수 없는 특별한 영역에 이와 같은 변화가 있다면 어떻게 해야 할까? 업그레이드를 해야 할 때 알아두어야 할 기본적인 정보 정도를 미리 알아둔다면 그렇게 골치 아픈 일은 없을 것이다.
변화에 대한 분류(A Taxonomy of Changes)
FCL을 작업한 팀의 목표는 프레임워크 버전 간에 완벽한 호환을 제공하는 것이다. 다시 말해 1.0버전에서 작업한 애플리케이션을 1.1에서도 아무런 문제없이 작동시킬 수 있고 그 반대도 가능하도록 말이다.
.NET 1.0과 1.1의 FCL을 살펴보면 약 1100가지에 달하는 API의 변화를 찾아볼 수 있을 것이다. Member, class, 심지어는 모든 namespace가 추가되거나 삭제되었다. 이런 변화의 거의 대부분은 프레임워크에 새로운 것들이 추가됨으로써 이루어진 것이지만, 큰 변화(breaking changes)라고 까지는 볼 수 없다. 새로운 클래스를 사용한 애플리케이션이 예전의 FCL에서 실행될리 없기 때문이다. 예를 들어 1.1 FCL은 1.0의 FCL에는 포함되지 않았던
System.Data.Odbc 네임스페이스를 포함하고 있다. 이런 네임스페이스로부터 나온 클래스를 사용한 코드는 1.0 FCL을 기반으로 한 곳에서는 실행되지 않는다.
진짜 큰 변화는 더 내부에 있다. 문법적으로는 올바르지만 속성은 다른 코드가 마치 다른 버전의 FCL에서 실행되는 것처럼 보이는 것을 예로 들 수 있다. 이러한 속성의 변화는 크게 1.0의 애플리케이션이 1.1 프레임워크에서 실행되는 backward breaking changes와 1.1 애플리케이션이 1.0 프레임워크에서 실행되는 forward breaking changes로 나눌 수 있다.
Backward Breaking Changes
비교적으로 소수의 개발자들만이 새로운 버전(Visual Studio .NET 2003 및 .NET Framework 1.1)으로 옮겨갔다. 그 결과 대부분의 사람들에게 forward breaking changes보다는 backward breaking changes가 더 유용할 것 같다. 아래는 일반적인 닷넷 개발자들이 backward breaking changes에 대해 문제를 일으킬만한 것들을 정리해본 것이다.
1. 인터넷 보안 변화 마이크로소프트는 인터넷에서 다운받은 코드를 위한 올바른 보안 원칙에 대해 또다시 얼버무렸다. 1.0에서 기본 보안 정책은 절대로 인터넷 계정으로 코드를 실행시키지 않았지만 1.1에서는 이것이 약간 자유로워졌다. 인터넷 계정의 제약 내에서 코드를 실행할 수 있게 되었기 때문이다(제약이 심한 편이지만 어쨌든 허용은 된다. 예를 들어
Isolated Storage 메커니즘을 통해 디스크의 파일을 사용하는 등의 실행). 만약 1.0의 보안수준으로 돌아가고싶다면 Adjust Security Wizard를 사용해서 Internet zone을 No Trust로 바꾸면 된다.
2. DataSets 내에서 null defaults의 핸들링이 좋아짐 1.0에서는 DataSet의 기본값이 "" 인 것은 DBNull로 취급되는 일이 종종 있었다. 특히, WriteXml()을 이용해서 DataSet을 serialize하거나 ReadXml()을 이용해서 deserialize할 때 희한하게도 column default이 DBNull로 바뀌어 버렸다. 그렇지만 1.1에서는 빈 스트링은 그대로 빈 스트링으로 남는다. 이렇게 핸들링이 편리해지긴 했지만 1.0에서 올바르지 않은 속성을 포착하거나 의존하는 코드를 작성했다면 그 코드는 변경해 주어야 한다.
3. SqlDataReader의 새로운 예외 System.Data.SqlClient 네임스페이스에서는 코드를 다음과 같이 작성한다.
Dim myCommand As New SqlCommand(mySelectQuery, myConnection)
myConnection.Open()
Dim myReader As SqlDataReader
myReader = myCommand.ExecuteReader()
1.0에서 명령어가 SQL 서버에 의해 데드락의 희생자로 선택되더라도 이 코드는 성공적으로 수행된다(다른 연결로 인해 잠금 문제가 있는 경우라 하더라도…). 또한 SqlDataReader로부터 실제로 데이터 읽기를 시도하기 전까지는 SqlException을 받지 못할 것이다. 반면 1.1에서는 ExecuteReader() method가 SqlException을 보낼 수 있다. 실제로 데이터를 읽고 있을 때 이와 같은 특정 예외를 볼 수만 있는 에러 핸들링을 받을 경우, 코드를 다시 손봐야 한다.
4. HttpWebRequest.MaximumResponseHeadersLength 프로퍼티 이는 1.1. FCL의 새로운 프로퍼티이다. 예상했던 바대로 HttpWebRequest 객체와 작동하는 헤더의 최대 크기를 설정한다. 이때 허용된 길이보다 큰 응답은 예외를 발생시킨다. 기본으로 64K로 설정되지만 1.0 FCL에서는 제한이 없었다. 이러한 변화는 정말 바람직한 변화로 우리 모두가 환영하는 변화이다. 하지만 HTTP headers를 사용해 거대한 양의 정보를 그냥 보내게 될 일들이 반드시 발생하게 된다. 이런 경우에는 코드에 위 프로퍼티를 사용해 에러를 피하기 바란다.
5. 지체 없이 진행되는 다이얼로그 지금까지 1.0을 고수하려는 사람들이 얼마나 있을지는 모르겠지만 노파심에서 이 부분은 꼭 말하고 넘어가야 할 것 같다. .NET은 스레드를 UI interactive(사용자 감시로 데스크탑에서 구동되는 것)와 non-UI interactive로 나눈다. 1.0에서는 Form.ShowDialog() 혹은 CommonDialog.ShowDialog()를 non-UI interactive 스레드로부터 호출할 경우 스레드가 정지한 채로 있지만 1.1에서는 메소드가 InvalidOperationException를 줄 것이다.
6. Environment.UserInteractive 작동 위의 문제와 관련해 FCL은 Environment를 제공한다. UserInteractive 프로퍼티는 현재 스레드가 UI interactive인지 아닌지를 말해준다. 하지만 1.0에서는 스레드가 UI interactive인지 아닌지에 상관없이 모두 true를 리턴한다는 문제가 있었다. 이러한 문제가 1.1에서는 해결되었다. 윈도우 서비스나 XML 웹 서비스에서 이것이 호출될 경우 1.0에서와는 달리 1.1에서는 false를 돌려준다. 만약 임의의 스레드에서 ShowDialog()의 메소드를 사용하고 있다면 이 프로퍼티를 사용하는 것이 좋다. 왜냐하면 스레드가 UI interactive가 아니라면 예외를 발생시키지 않기 때문이다.
7. 폼 로드 수정 알아두고 있어야 할 윈도우 폼의 로드 이벤트에 사항이 두 가지가 있다. 1.0에서는 형태상 폼의 로드 이벤트에서 Close()를 호출해도 아무 일이 일어나지 않았다. 하지만 1.1에서는 예상했던 바대로 폼이 닫힌다. 이 외에도 좀 내부적인 버그에 수정이 이루어졌다. 1.0에서는 폼이 ActiveX 컨트롤을 가지고 있으면 로드 이벤트는 실행되지 않았다. 하지만 1.1에서는 된다.
8. WSDL 매핑으로의 변화 WSDL(Web Services Description Language Tool)은 웹 서비스를 호출하기 위한 프록시 클래스를 생성하기 위해 사용된다. 1.0에서 이 도구는 스키마 요소를 위한 네이티브 데이터 타입으로써 xsd:anyType 의 WSDL 데이터 타입과 함께 System.String을 사용했다. 하지만 1.1에서는 System.String이 아니라 System.Object가 사용된다. 몰래 프록시를 발생하기 위해 혹은 웹 레퍼런스를 새롭게 하기위해 도구를 사용할 생각이라면, 웹 서비스가 변화하지 않았더라도 예상했던 것에서 프록시 클래스가 변경된다는 사실을 발견할 수 있을 것이다.
Forward Breaking Changes
1.0 프레임워크에서 1.1 애플리케이션을 실행하기 어렵게 만드는 몇 가지 변경 사항은 다음과 같다.
9. SQL 실행의 향상 1.0버전의 SqlCommand object는 일괄 처리문(batched statements)에서 실행되지 않았다. 예를 들어 한번의 작동으로 아래와 같은 일련의 문장을 실행하고 싶을 것이다.
SET STATISTICS PROFILE ON
SELECT * FROM MyTable
SET STATISTICS PROFILE OFF
SqlCommand를 통해 1.0에서 위와 같은 문장을 실행하려면 항상 실패할 것이다. 1.1에서는 이를 실행하기위해 저장 프로시저를 만들 필요가 없기 때문에 이런 배치 쿼리가 성공할 것이다. 이와 같은 기능에 너무 의지할 경우 1.1 코드는 1.0에서는 실행되지 않는다는 것을 알아두어야 할 것이다.
10. TextBox.MaxLength 변화 윈 폼 텍스트박스 컨트롤의 MaxLength 프로퍼티는 키보드 또는 마우스를 통해 지정되는 문자의 최대 길이를 정의해준다. 경우에 따라서는 키보드 입력을 제한하는 것이 유용할 때도 있다. 1.1에서는 AppendText() method를 사용할 수 있고 명확한 MaxLength 보다는 SelectedText 프로퍼티로 작업할 수 있다. 그렇지만 1.0에서는 작동되지 않는다.
변화에 적응하기
"잘못된" 버전의 FCL을 실행중라면 이와 같은 급격한 변화가 여러분의 애플리케이션을 부셔버릴 수도 있다는 사실을 명심해두어야 할 것이다. 만약 버전을 찾지 못하면 1.1 애플리케이션은 1.0 FCL에서 실행되려고 할 것이고, 그 반대로 1.0 애플리케이션은 1.1 FCL에서 실행되려 할 것이다.
이러한 상황을 잘 모면할 수 있는 두 가지 전략이 있다. 일단 애플리케이션이 다른 버전의 FCL에서 돌아가지 않다면 원하는 버전을 설치한 후, 두 가지 버전이 나란히(
side-by-side execution of the .NET Framework) 실행되도록 설계해라. 두 버전은 같은 컴퓨터에 아무 충돌 없이 설치될 것이다. 이것이 잠재적인 비호환에 대비하는 가장 좋은 방법이다. 특히 테스트 방법에 확신이 없다면 말이다. 둘째로는 어느 버전의 어느 어셈블리가 여러분의 애플리케이션을 실행하는데 필요한지 명확히 하기 위해
XML binding policy 파일들을 이용할 수 있다. 이는 여러분의 애플리케이션이 다른 FCL의 동작을 자세하게 명세하도록 해주는 방법을 제공한다.
그리고 마지막으로 여기에서 언급한 교훈(버전간 호환성이 완벽하다는 말은 믿지 말 것)을 절대 무시해서는 않된다. 그리고 여러분들은 항상 이와 같은 이슈들에 촉각을 곤두세우고 있어야 한다. 최고의 개발자들은 이와 같은 이슈를 그때 그때 포착하여 이미 전략을 세워두는 사람이다. 아직까지 애플리케이션에 적합한 유니트 테스트 혹은 버그-추적 시스템을 구비해놓지 못했다면 더 큰 변화로 골치아파지기 전에 시스템을 구비해놓는 것이 좋을 것이다.