SimpleIsBest.NET

유경상의 닷넷 블로그

ADO.NET 2.0 오류 메시지 향상

by 블로그쥔장 | 작성일자: 2005-11-11 오후 2:28:00
이 글은 오래된 전에 작성된 글입니다. 따라서 최신 버전의 기술에 알맞지 않거나 오류를 유발할 수 있습니다. 저자는 이 글에 대한 질문을 받지 않을 것입니다. 하지만 이 글이 리뉴얼 되면 이 글에 대한 질문을 하거나 토론을 할 수도 있습니다.
닷넷 프레임워크 2.0이 Visual Studio 2005의 출시와 더불어 정식 출시되었습니다. 닷넷 프레임워크 2.0에는 기존 버전에 비해 많은 향상이 있었으며 그 중 많은 부분은 시간을 투자하여 살펴볼 의미를 갖음에도 불구하고 Visual Studio 2005와 SQL Server 2005의 그늘에 가려 관심 밖에 있는 것이 사실입니다. 뭐... 마이크로소프트 입장에서 보면 돈 받고 파는 제품에 보다 많은 마케팅 노력을 들이는 것이 당연하다고 보이지만, 우리 개발자들은 닷넷 프레임워크 2.0에 추가되거나 향상된 기능에 대해 보다 관심을 갖어 줘야 한다고 봅니다.

닷넷 프레임워크 2.0에서 향상된 새로운 기능들에 내용은 MSDN의 What's New in .NET Framework 2.0을 살펴 보십시요. 오늘은 ADO.NET에서 향상된 오류 메시지에 대한 내용을 다루고자 합니다.

ADO.NET 2.0 Error Message Enhancement

어플리케이션을 개발하다가 혹은 운영하다가 종종, 아니 항상 어떤 문제에 부딛히기 마련이다. DB에 접속이 안 된다든가, 분산 트랜잭션 오류가 난다든가 등등... 이럴 때 우리는 원인을 찾기 위해 노력하는데, 범인(원인)을 찾는 최초의 단서는 오류 발생시에 나타나는 다양한 메시지와 현상을 시발점(발음에 주의하자. 씨발쫌 으로 발음하면 곤란타... -_-;)으로 해야 함은 당연하다. 개발자 커뮤니티 같은 곳에서 질문을 올릴 때 막연히 "안되요", "오류나요", "예외가 발생합니다" 등등의 말은 아~조 성의 없는 질문으로써, 범인을 잡는데 필요한 기본적인 씨발쫌을 주지 않는 것으로 볼 수 있겠다. 이런 질문은 십중 팔구 제대로 대답받기 어렵다. (갠적으로 필자는 이런 질문을 보면, 바로 '뒤로' 버튼을 눌러버린다)

뭐... 요약하자면, 오류가 발생했을 때, 오류 메시지 혹은 예외 메시지는 문제 해결에 있어서 가장 기초적이고 중요한 단서가 되므로 매우 중요하다고 할 수 있으며, 이것으로 부터 원인을 찾아나가야 한다는 것이다. 그런 면에서 닷넷 프레임워크의 예외(exception)들은 다양한 정보를 제시한다. 많은 경우, 예외 메시지로부터 원인을 찾고 그것을 해결해 낼 수 있다.

Unknown Error Message

오류메시지가 이렇게 중요하고, 웬만한 오류는 메시지로부터 원인과 조치 방법을 알아 낼 수 있음에도 불구하고, 항상 오류 메시지가 오류에 대한 구체적인 정보를 제공하는 것이 아니다. 대표적으로 짜증나고 애매모호한 예외들은 NullReferenceException, COMException 등이 있는데, NullReferenceException은 디버그 빌드를 사용하여 호출 스택(callstack)에 오류가 발생한 라인이 표시되지 않으면 찾는데 시간 깨나 잡아먹는 녀석으로 유명하고, COMException은 말 그대로 COM 관련 오류라는 너무 애매하고 추상적인 오류다(COM에 대해 잘 모르는 사람에겐 재앙 같은 오류이기도 하다). 다행이 COMException은 0x8?????? 어쩌구 하는 오류코드를 보여주므로, 이 오류코드를 KB나 구글에서 검색하면 개략적인 원인 파악이 가능하다.

이것 외에도 ADO.NET 관련한 난감한 오류는 유명한 "일반 네트워크 오류"이다. 영어로는 General Network Error 라는 메시지를 뿌리는 이 오류는 SqlException 예외 클래스를 통해 발생한다. 뭐... 오류 내용은 네트워크 상에 오류로 DB 접속, 혹은 명령 수행에 지장이 생겼으니 니 꼴린대로 잘 해석하란 얘기다. 서버에 네트워크 케이블이 빠져 버렸다든가, 중간 라우터가 디져버렸다든가 등등의 다양한 이유일 수도 있다. 말 그대로 일반적인 네트워크 상의 문제인 것이다. 이런 메시지는 백날 잘 복사해서 질문에 올려봤다 말짱 허당이다. 메시지 자체가 구체적인 오류 원인을 알려주지 않기 때문이다.

예를 들어 보자. 다음은 연결 풀링(Connection Pooling)을 사용하지 않는 클라이언트들이 한꺼번에 서버에 접속하고자 할 때 발생하는 ADO.NET의 오류 메시지이다. 쓰바 어딜 봐도 접속 폭주가 원인이라는 힌트는 없으며 달랑 "네트워크 메뉴얼 찾아봐"란 소리나 하고 있을 뿐이다. 이런 오류 메시지는 심각한 정신적 스트레스와 더불어 반복적인 밤샘으로 체력적인 저하를 유발하는 메시지가 되겠다.

처리되지 않은 예외: System.Data.SqlClient.SqlException: 일반 네트워크 오류가 발생했습니다. 네트워크 설명서를 참조하십시오.
at System.Data.SqlClient.SqlInternalConnection.OpenAndLogin()
at System.Data.SqlClient.SqlInternalConnection..ctor(SqlConnection connection
, SqlConnectionString connectionOptions)
at System.Data.SqlClient.SqlConnection.Open()
at DBAccessTestApp.DoDBAccess() in d:\Temp\Code\ConnPool\DBAccess.cs:line 37

도통 원인 파악이 힘든 1.1 버전의 SqlException 메시지

ADO.NET Specific Error Message

ADO.NET 2.0에서는 보다 구체적으로 오류 메시지를 제시하도록 항샹되었다. SqlException의 오류 메시지는 보다 상세하게 바뀌었고, 특히 오류 메시지 뒤에는 어떤 접속 제공자(SQL Server에 접속할 때는 TCP/IP 혹은 Named Pipe 등의 방법을 사용할 수 있음을 기억하자)를 사용하였고 이 제공자가 반환한 오류 코드도 보여준다.

앞서 닷넷 프레임워크 1.1(한글판)에서 보였던 "일반 네트워크 오류" 메시지는 2.0(영문판)에서는 다음과 같이 나타난다.

Unhandled Exception: System.Data.SqlClient.SqlException: A connection was successfully established with the server, but then an error occurred during the login process. (provider: TCP Provider, error: 0 - 현재 연결은 원격 호스트에 의해 강제로 끊겼습니다.)
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)
at System.Data.SqlClient.TdsParserStateObject.ThrowExceptionAndWarning()
...... 이하 생략 .....

원인을 친절하게 설명해 주는 2.0 버전의 SqlException 메시지

일단 달랑 "일반 네트워크 오류(General Network Error)"란 메시지는 보이지 않는다. 발생한 오류 메시지 의하면 서버에 성공적으로 접속(네트워크 레벨에서) 하였으나 로그온 프로세스에 의해 연결이 강제로 끊겼다는 내용이다. 훨씬 더 구체적인 내용으로 오류를 설명하고 있다. 일단 접속은 하였으나(네트워크 상으로 문제가 없어 보인다. 그치?), 서버가 연결을 강제로 끊었으므로 서버 과부하가 아닐까라는 추측이 가능하게 해주는 메시지이다(꿈보다 해몽이 좋다고 태클 걸면 대략 할말 없다... -_-). 게다가 오류 메시지 마지막에 TCP Provider가 표시되어 TCP/IP를 통해 DB에 연결하고 있음을 보여주고, 또한 TCP Provider가 겪은 저 수준의 오류 메시지도 보여준다. 이 메시지는 SOCKET에서 발생하는 오류 메시지이다.

비슷한 예로서, DB 서버의 이름이 DNS에 등록되지 않은 경우나 연결 문자열에 서버 이름의 철자가 잘못된 상황에서 1.1 버전에서 표시하는 메시지는 다음과 같다. 단순히 SQL Server를 찾을 수 없다도 아니고, 찾을 수 없거나 액세스가 거부된 상황이다.

처리되지 않은 예외: System.Data.SqlClient.SqlException: SQL Server가 없거나 액세스가 거부되었습니다.
at System.Data.SqlClient.SqlInternalConnection.OpenAndLogin()
at System.Data.SqlClient.SqlInternalConnection..ctor(SqlConnection connection
, SqlConnectionString connectionOptions)
at System.Data.SqlClient.SqlConnection.Open()
at DBAccessTestApp.DoDBAccess() in d:\Temp\Code\ConnPool\DBAccess.cs:line 37

Host Name Resolution 오류 상황에서 애매한 1.1의 메시지

반면 동일한 상황에서 2.0의 메시지는 명확하게 그 이유를 알려준다. 주어진 서버 이름으로 IP를 알아낼 수 없다는 메시지를 명쾌하게 알려준다.

Unhandled Exception: System.Data.SqlClient.SqlException: An error has occurred while establishing a connection to the server. When connecting to SQL Server 2005, this failure may be caused by the fact that under the default settings SQL Server does not allow remote connections. (provider: TCP Provider, error: 0 - 알려진 호스트가 없습니다.)
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)
...... 이하 생략 ......

Host Name Resolution 문제임을 정확하게 찝어주는 2.0의 오류 메시지

비슷하게 SQL Server가 1433 디폴트 포트가 아닌 다른 포트를 리스닝(listening)하는 상황도 고려해 볼 수 있다. 이때도 2.0의 메시지는 원인을 명확하게 찾아 오류 메시지를 나타내 준다. 클라이언트와 서버의 포트가 다른 경우, 대개의 경우 서버의 TCP/IP 드라이버는 연결을 거부하게 되는데, 이 때 SqlException 메시지의 내용은 정확하게 "대상 컴퓨터에서 연결을 거부했으므로 연결하지 못했습니다" 라는 소켓 오류 메시지를 뿌려준다.

Summary

닷넷 프레임워크에 향상된 여러 기능 중 눈에 확 들어오지 않고 많이 언급되지 않은 기능이지만 ADO.NET 2.0의 향상된 오류 메시지는 데이터베이스 관련 예외 발생시 원인 파악및 해결에 보다 많은 정보를 제공함으로써 보다 손쉽게 오류를 해결할 수 있게 되었다. 한가지 아쉬운 점은 모든 ADO.NET 데이터 프로바이더가 향상된 오류 메시지를 제공하는 것이 아니라는 점이다. 하위에 별도의 API 스택을 갖는 OleDb, Odbc, Oracle은 하위 API 스택이 제공하는 오류메시지를 그대로 따르게 된다. 즉, 1.1이나 2.0이나 차이가 없다는 얘기가 되겠다. SQL Server와 저수준의 프로토콜 레벨에서 상호작용하는 SQL Server 데이터 프로바이더의 경우에만 상세한 오류 메시지가 제공된다는 점을 상기하면서 글을 마칠까 한다.