Java에서 File IO할 때의 try-catch-finally 스타일

Posted by epicdev Archive : 2012. 3. 27. 19:54

Java에서 File IO를 할 때에 필연적으로 사용해야하는 것이 try와 catch와 finally이다.

그런데 이런 try, catch, finally 들로 코드를 도배하다보면 정말 UGLY한 코드가 나오기가 쉽다.

아래의 코드가 일반적으로 가장 널리 사용되는 스타일이다.


이 코드는 소위 말하면 정말 UGLY하다고 할 수가 있다.

가독성도 떨어지고 뭔가 불필요하게 try와 catch가 들어있는것 처럼 보인다. (실상은 그렇지 않다. 다 필요하다.)

이처럼 불필요하게 "보이는" try와 catch를 없애려고 아래의 코드처럼 할 수도 있다.


이렇게 하고나면 맨 처음 코드에서 catch가 반복되는 것을 해결 할 수 있어 보인다.

물론 이렇게 하면 해결은 되지만, Java에서의 Exception을 처리할 때의 원칙(Exception들을 catch문 하나에서 한꺼번에 처리하지 않는다)에 위배된다.

즉, out.close에서 발생하는 Exception과 new FileOutputStream에서 발생하는 Exception 모두 하나의 catch문에서 처리가 되어버린다.

이를 해결하기 위해서 또한 아래처럼 코드를 짤 수도 있다.


이렇게 코드를 짜게되면 함수가 IOException을 throw하게 된다. 또한, finally 블록에서 out의 null 체크도 없어졌다.

(new FileOutPutStream에서 Exception이 발생하면 곧바로 IOException을 throw하면서 그 다음 line을 실행하지 않으므로 finally 블록에서의 out은 무조건 null이 아니다)

하지만 나같은 경우는 Exception처리를 외부로 유보하는 것을 좋아하지 않으므로 (이런 사람들이 많을것이라 본다), 개인적으로는 비추천이다.


그래서 이제 최종적으로 내가 "알고 있는 한" 가장 BEAUTIFUL한 코드를 살펴보도록 하겠다.


이 코드를 보면 closeQuietly라는 함수를 finally 블록에서 호출하고 있다.

closeQuietly라는 함수는 Closeable의 varargs 타입을 파라미터로 받아서, 받은 closeable들을 모두 닫아버린다.

이렇게 stream들을 닫는 함수를 따로 만듦으로써 코드가 훨씬 깔끔해졌다.


물론 위의 4가지 스타일 모두 사용가능한 스타일이다.

필요한 상황마다 적재적소에 사용 할 수 있다. 네 번째 스타일의 경우에는 때로는 "닭 잡는데 소 잡는 칼을 쓰는 격"이 될 수도 있다.

이 내용에 대한 프로그래머들의 의견은 http://stackoverflow.com/questions/2699209/java-io-ugly-try-finally-block에서 확인 할 수 있다.

위의 링크를 참조하자면, 4번째 스타일이 가장 낫다는 것이 보편적인 생각인 것 같다.



  

예외는 예외적인 문제에 사용하라

Posted by epicdev Archive : 2011. 10. 3. 12:41
예외가 있다는 것은 즉 컨트롤의 이동이 즉각적이고 로컬하지 않다는 것을 말한다. 일종의 연쇄 goto 같은 것이다. 예외를 정상적인 처리 과정의 일부로 사용하는 프로그램은 고전적인 스파게티 코드의 가독성 문제와 관리성 문제를 전부 떠안게 된다. 이런 프로그램은 캡슐화 역시 깨트린다. 예외 처리를 통해 루틴과 그 호출자들 사이의 결합도가 높아져 버린다.

에러 처리기는 또 다른 대안이다

에러 처리기는 에러가 감지되었을 때 호출되는 루틴이다. 특정 부류의 에러를 처리하기 위해 어떤 루틴을 등록하게 된다. 해당하는 에러가 났을 때 그 처리기가 호출 될 것이다.

자바의 RMI 기능을 사용하는 클라이언트 서버 애플리케이션을 구현한다고 생각 해 보라. RMI가 구현 된 방식 때문에, 원격 루틴을 호출 할 때마다 RemoteException을 처리할 준비가 되어있어야 한다. 문제는 이런 예외를 처리하기 위해 코드를 추가하는 것은 지겨운 일이며, 로컬과 리모트 루틴 모두에서 작동하는 코드를 작성하기가 어렵다는 것이다. 한 가지 가능한 우회로는 원격이 아닌 클래스로 원격 객체를 wrapping 하는 것이다. 그러면 이 클래스는 에러 처리기 인터페이스를 구현하며, 원격 예외가 감지되었을 때 호출 될 루틴을 클라이언트 코드가 등록하도록 한다.
 
실용주의프로그래머
카테고리 컴퓨터/IT > 프로그래밍/언어
지은이 앤드류 헌트 (인사이트, 2007년)
상세보기

'Archive' 카테고리의 다른 글

리소스 할당과 해제의 균형과 예외  (0) 2011.10.03
리소스의 할당과 해제  (0) 2011.10.03
죽은 프로그램은 거짓말을 하지 않는다  (0) 2011.10.03
타인의 버그를 대하는 자세  (0) 2011.10.02
GUI와 쉘  (0) 2011.10.02
  

죽은 프로그램은 거짓말을 하지 않는다

Posted by epicdev Archive : 2011. 10. 3. 11:50
프로그래밍을 하다보면 '그런 일은 절대 일어날 리 없어'라는 사고에 빠지기 쉽다. 우리 중 대다수는 파일이 성공적으로 닫혔는지, 혹은 트레이스문이 우리가 예상한 대로 찍히는지 확인하지 않는 코드를 작성한 경험이 있다. 그리고 다른 모든 조건이 동일하다면, 그럴 필요가 없었을지도 모른다. 문제의 코드는 정상 조건 하에서는 실패하지 않았을 것이다. 하지만 우리는 지금 방어적으로 코딩하고 있다.

모든 에러는  정보를 준다. 여러분은 에러가 발생할 리 없다고 스스로를 설득하고선 그걸 무시하기로 할 수 있다. 반면에 실용주의 프로그래머는 만약 에러가 있다면 정말로 뭔가 나쁜 일이 생긴 것이라고 자신에게 이야기한다.

<실용주의 프로그래머 팁>
일찍 작동을 멈추게 하라

망치지 말고 멈추라

가능한 한 빨리 문제를 발견하게 되면, 좀 더 일찍 시스템을 멈출 수 있다는 이득이 있다. 게다가 프로그램을 멈추는 것이 할 수 있는 최선일 때가 많다.

자바 언어와 라이브러리는 이 철학을 포용했다. 런타임 시스템에서 뭔가 예상하지 못한 것이 발생하면 RuntimeException을 던진다. 만약 이 예외가 catch되지 않으면 프로그램의 최상위 수준까지 스며 나올 것이고, 결국 스택 트레이스를 출력하며 프로그램을 멈춰버릴 것이다.

다른 언어에서도 똑같이 할 수 있다. 예외 처리가 지원되지 않거나 라이브러리가 예외를 던지지 않으면, 여러분 자신이 직접 에러를 처리해야 한다. C에서는 매크로가 이 작업에 매우 유용할 것이다.
#define CHECK(LINE, EXPECTED) \
    { int rc = LINE; \
    if (rc != EXPECTED) \
        ut_abort(__FILE__, __LINE__, #LINE, rc, EXPECTED); }

void ut_abort(char* file, int ln, char* line, int rc, int exp) {
    fprintf(stderr, "%s line %d\n'%s': expected %d, got %d\n",
        file, ln, line, exp, rc);
    exit(1);
}

그러면 다음을 사용해서 결코 실패하면 안 되는 호출을 감쌀 수 있다.

check(stat("/tmp", &stat_buff), 0);

만약 실패한다면, stderr에 다음과 같은 메시지가 출력될 것이다.

source.c line 19
'stat("/tmp", &stat_buff)': expected 0, got -1

분명히 실행 중인 프로그램을 그냥 종료해 버리는 것은 때로 적절치 못하다. 해제되지 않은 자원이 남아 있을 수도 있고, 로그 메시지를 기록 할 필요가 있을 수도 있고, 열려있는 트랜잭션을 청소해야 하거나, 다른 프로세스들과 상호작용해야 할 필요가 있을지도 모른다. 그렇지만 기본 원칙은 똑같다. 방금 불가능한 뭔가가 발생했다는 것을 코드가 발견한다면, 프로그램은 더 이상 유효하지 않다고 할 수 있다. 이 시점 이후로 하는 일은 모두 수상쩍은 게 된다. 되도록 프로그램을 빨리 종료해야 할 일이다. 일반적으로, 죽은 프로그램이 입히는 피해는 절름발이 프로그램이 끼치는 것보다 훨씬 덜한 법이다.

 
실용주의프로그래머
카테고리 컴퓨터/IT > 프로그래밍/언어
지은이 앤드류 헌트 (인사이트, 2007년)
상세보기

'Archive' 카테고리의 다른 글

리소스의 할당과 해제  (0) 2011.10.03
예외는 예외적인 문제에 사용하라  (0) 2011.10.03
타인의 버그를 대하는 자세  (0) 2011.10.02
GUI와 쉘  (0) 2011.10.02
예광탄 코드와 프로토타이핑의 차이점  (0) 2011.10.02
  
 «이전 1  다음»