제2의 비엔지니어 인생관을 꿈꾸며

Posted
Filed under MSSQL
프로그래밍의 전유물 Try ~ Catch 를 MSSQL 에서도 꽤 오래전(MSSQL 2005 부터 지원)부터 사용 할 수 있게 되었다. 과연 DB 프로그래밍에서, Try ~ Catch 가 필요할까? 잠깐 살펴보자면, 어플리케이션용 프로그래밍언어와 DB용 프로그래밍언어는 목적이 좀 다르다. (구지 딴지를 걸면 할말은 없지만 본인 생각이다)

▶ DB는 CRUD (Insert,Select,Update,Delete) 에 집중된 처리를 할 수 있게해주어야한다.
▶ 그러므로, DB에 호출되어저, 넘어오는데이터는 사실 Validation 이 대부분 끝난 데이터 들이어야 한다.
(예를들면, 뭐,.. Type , Length , 유효성 등등  필드 조건 / 업무에 맞는 Spec )

하지만, 제어에 따라 DB단(DB 프로그래밍)에서 뭔가 다시한번  데이터를 체크하거나, 제어 처리해야 수월한 경우가 발생한다. 그러므로 MSSQL 에서의  Try ~ Catch 를 비롯한 기타 오류처리 변수 및 함수에 대한 지원은 반갑지 아니 할 수 없다.(반갑다.--)

● Try ~ Catch (Begin Try ~ End Try Begin Catch  ~ End Catch)
Begin Try
    // 오류 발생의 여지가 있거나, 예외가 날 가능성이 있는 코드
End Try
Begin Catch
   // 오류가 났을때 처리
End Catch

일반 어플리케이션 언어와 구문과 비슷한 문법을 가지고 있다.
물론 Exception 객체나 finally 그리고 예외 구분에 따른 catch  구문을 다중적으로 가질수 없는 축약식 느낌이긴하다.

여기서 중요한 점은 모든 오류를 Try ~ Catch가 잡아내지 못한다는 점이다.
MSSQL 의 Try ~ Catch 가 인지 할 수 있는 오류 심각도는 10 이상이고. 연결을 끊는,또는 강제 중단의 오류를 예외처리 Catch 하지 못한다. 

이번엔, Try ~ catch 를 활용하여 , 오류 처리 및 예외처리를 위해 필요한 함수에 대해 알아보자

● 오류 상태 체크 함수 
select @@Error -- 오류 코드를 출력한다.
select Error_Number()       -- 오류 코드 출력 = @@Error 과 같다.
select Error_Line()             -- 오류난 소스의 라인을 출력한다.
select Error_Message()     -- 오류 메세지를  출력한다.
select Error_Procedure()   -- 오류난 프로시져명을 출력한다.
select Error_Severity()      -- 오류의 심각도를 출력하한다.

select Error_State()           -- 오류의 상태를 출력한다.

위의 상태값을 눈으로 확인하기위해 , 아래 프로시져를 작성해 보자

[예제] 

Create proc sp_Test
as
Begin Try  
     EXECUTE SP_Nothing;   // SP_Nothing 는 없는 프로시저 이다.
End Try  
Begin Catch
    SELECT   
        @@Error as Error ,
        Error_Number()  as Number,
        Error_Line() as Line,
        Error_Message() as [Message],
        Error_Procedure() as [Procedure],
        Error_Severity() as severity,
        Error_State() as state  
End Catch


  Exec sp_Test  -- 실행해보자  
일단 오류 오류번호 와 심각도 , 상태 숫자는 우린 뭔지 모르겟지만,
그것을 제외하고도 ( getdate() 만 추가한다면,) 언제 , 어디서 , 어떤 오류가 , 몇 라인에 났는지 쉽게알 수 있다.


참고로 Error 함수들을 다 외우지 않아도 된다.(역쉬 편리한, 인텔리센스 ㅠ_)


이제 try  ~ catch 구문이 편리하게 오류 예외처리를 해주므로 너무 편리해졌지만, 안타깝게도 모든 오류를 처리해주지 않는다. 위에서 잠껀 언급한대로,  오류 심각도는 10 이상이고. 연결을 끊는,또는 강제 중단의 오류를 예외처리 Catch 하지 못한다. 예를들면 실행중 연결이 끊기거나, 실행중인 프로시져를, Kill 하거나, 하면 Catch 를 타지 못한다.

더 아이러니한건 어떤게 오류 레벨이 어떻고 어떤지, 오류 레벨로 기억하기란, 개발자 입장에서는, 다 알고 있기도 난해하다.
아래 샘플로 감을 한번 잡아보자.


● Try ~ Catch 예제

원래 예제도 되도록 복사해서  테스트 해볼수 있도록 Text 로 작성하려고 노력하지만, 이번엔 빠른 작성을  스크린샷으로 대신하고, 아래 샘플 쿼리를 첨부해 놓도록 하겠다.



----------------------------------------------------------------------------------------------------------------------------------------------
# 조건 1 : 존재하지 않는 프로시셔 -   예외처리 성공(ㅇ) 

※ 존재하지 않는 프로시져 호출시 정상적으로 예외처리를 한다. 이번에 테이블로  해보자!


----------------------------------------------------------------------------------------------------------------------------------------------
# 조건 2 : 존재하지 않는 테이블 -
  예외처리 오류(X) 
※ 존재하지 않는 테이블 쿼리시 예외처리를 못한다.ㅠ  그럼 함수를 호출해 보자 해보자


----------------------------------------------------------------------------------------------------------------------------------------------
#조건 3 : 존재하지않는 함수 - 
 예외처리 오류(X) 

※ 역시 안된다. 없는 개체의 호출은 프로시져 호츨만 성공하고, 나머지 테이블 


----------------------------------------------------------------------------------------------------------------------------------------------
# 조건 4 : 존재하지 하지만 사용할 수 없는 동의어(Synonym)
 -   예외처리 오류(X) 

※ 시놈님(동의어) 도 역시 안된다.


결국 없는 개체의 예외처리는
테이블,  함수, 동의어(시놈님) 모두 오류 , 프로시져만 성공 , 존재 하지도 않는 개체를 쿼리에작성할 일은 없지만 어쨋던 프로시져만 성공했다. 그럼 논리적인 오류를 작성 해보자.


자. 그럼 이제, 논리 오류에 대해 예외를 확인해보자.


----------------------------------------------------------------------------------------------------------------------------------------------# 조건 5 :  예제로 잘나오는 몫 0 으로 나누기 :
  예외처리 성공(ㅇ) 

----------------------------------------------------------------------------------------------------------------------------------------------
# 조건 6 :  유니크 컬럼에 중복 값 넣기:
  예외처리 성공(ㅇ) 




----------------------------------------------------------------------------------------------------------------------------------------------
# 조건 7 :  타입 다르게 넣기 :  
 예외처리 성공(ㅇ)  → idx 가 int 형인데 'b' 문자열 값을 넣어봤다.

※ 생각보다 논리적인 오류 처리는 잘 된다? 그렇타면 ,,

----------------------------------------------------------------------------------------------------------------------------------------------# 조건 8 : 우리가 흔히하는 실수,  테이블 제공 컬럼수와 다르게 데이터 입력 -   예외처리 오류(X) 

----------------------------------------------------------------------------------------------------------------------------------------------
# 조건 9 : 우리가 흔히하는 실수,  명령어 오타-   예외처리 오류(X) 
→ 눈에 띄게하기위해 values 부분을 valueInsert 로 해 보았다.
※ 역시 안된다 , 생각보다 많은걸 오류 처리를 못하는거 같이 보인다. 이외에도 쿼리 문법이 아닌 if 문 이나 case 등의
명령어 문법에 대해서도 예외처리가 안된다.

예제를 가만히 생각해 보자, 아 뭐지? 처리되는게 예외처리하는게 생각보다 얼마 없네? 라고 생각이 들 수 도있다.


자 그럼 이제 어플 개발언어로 돌아와서 생각해보자.


프로그램의 작성은 코딩 → 컴파일 → 실행 과정을 거친다. 물론 내부적으로  작성한 코딩이 맞는 문법 및 명령어 체크작업, 객체 및 변수의 존재 유무 및 초기화 문제 등등 , 기타 여러가지 문제점을 "코딩 → 컴파일" 과정에서 모두 소화 & 체크해서 개발자로 하게끔 수정을 한다.(컴파일 언어인 경우다) 

하지만 인터프리터나 , SQL 처럼 컴파일따로  실행따로 구분되어진 언어가 아닌, 실행과 동시에 컴파일과 문법 파싱작업이 동시에 이루어지는 스크립트 언어들은, 아무래도 개발에 특화된 언어에 비해 개발환경이 뛰어나지는 않타. 지극히 정상적일수도있다.

정리하자면, 만약 프로시져를 작성한다고 가정했을때, 어차피 위에서  예외처리가 안되는 상황의 쿼리 작성문들을, 프로시저에 작성하여, 생성 & 수정 한다면, 그 과정에서 최소한의 문법파싱 같은 작업이 이루어 지므로 없는 테이블을 작성하거나,  없는 함수를 쓰거나 하는 오류를 범 할 확률이 거의 없겟다. (반대로 예외처리가 되는 예제들은, 런타임시 값들에 의해 오류가 날 확률이 높다는 반증이다)

결론은, 객체 관련 오류보다 (심각도가 10 보다 높은오류) 논리적인 오류, 그러니까 값에 의해 발생되는 예외를 처리하도록 만들어지는게 사실상 맞다는 말이다.

역으로 생각해보면 , 없는 프로시져가 예외처리가 된다는게 오히려 의외의 결과 일수가 있다.
(하지만 프로시저 경우 작성시 try 안에 없는 프로시져 쿼리를 작성하면  프로시저는 만들어지지만, 아래와 같은 문구가 출력되면서 만들어진다.)

" 모듈 '생성되는 프로시저명'은(는) 누락된 개체 '호출될프로시져(없는프로시져)'에 종속되어 있습니다. 이 모듈은 그래도 만들어지지만 해당 개체가 있어야 실행될 수 있습니다.  "
2018/04/19 10:34 2018/04/19 10:34