티스토리 뷰

 

Sims님의 글을 참고하였습니다.


프로그래밍을 하다 보면 다양한 오류가 발생하기 쉽습니다.
가장 대표적인 예로는 file을 읽으려고 했는데 해당 file이 없는 경우, 0으로 나누는 경우 등등.. 쉽게 접할 수 있는 오류들이 많이 있죠. 이러한 오류를 처리하는 것을 '예외 처리'라 볼 수 있습니다.
그럼 어떻게 예외 처리를 할 수 있는지 살펴보도록 합시다.

 

 예외처리 != 버그

 

자, 일단 오류를 분류해 봅시다. 위에서 간단하게 2가지 오류를 말씀드렸습니다.
첫 번째는 파일이 존재하지 않는 경우, 두 번째는 0으로 나누었을 때 오류입니다. 둘 다 오류지만 자세히 살펴보면 특징이 조금 다릅니다.

 

첫 번째, 파일이 존재하지 않는 경우의 오류는 프로그래머가 예상을 하고 예외 처리를 해줘야 하는 경우입니다. 즉, 파일이 존재하지 않을 경우를 대비해서 예외 처리를 해줘야 한다는 것이죠. 예측이 가능합니다. 


두 번째, 0으로 나누었을 때 오류입니다. 첫 번째처럼 예외 처리를 해 줄 수 있습니다. 하지만 '0으로 나눈다'를 어떤 식으로 예외 처리를 해줄 건가요? 예측이 힘듭니다.

 

단적인 예를 들어보죠.

 

/* 코드1 */
public void Exam() { 
	for(int i = 9; i >=0 ;i--) {
    		System.out.println(10 % i); 
    	}
}

 

(코드1) 과 같이  10을 9~0까지 나눈 나머지 값을 출력한다고 가정합시다. 그럼 예외 처리를 어떻게 해야 할까요?

 

예외 처리되는 부분에 다시 'for(int i = 9; i >0 ;i++)'를 수행하여 출력해야 합니다. 생각해보세요. 예외 처리를 하는 것이 좋을까요?

아니면 (코드1) 를 수정하는 것이 좋을까요? 당연히 (코드1)를 i>0으로 수정하는 게 무조건 좋습니다. 두 번째 케이스는 '버그'에 가깝다고 보시는 것이 맞죠.


그래서 첫 번째 케이스는 '검사 예외'(- 예외 처리를 해줘야 하는 것)라 하고 두 번째 케이스는 '비 검사 예외'(- 예외 처리보단 버그에 가까워 코드를 고치는 것)이라고 부릅니다. (물론 비검사 예외도 예외 처리를 해 줄 수 있습니다.)

 

 try~ catch문을 사용해보자

 

자 이제 본격적으로 예외 처리를 할 수 있는 try/catch 구문에 대해서 알아봅시다.
try예외를 감지하는 곳이고 catch는 try에서 감지한 예외를 처리하는 부분입니다.
예를 보시는 게 더 빠를 것 같습니다. 간단한 예를 위해서 '비검사 예외'를 사용하겠습니다.

 

/* 코드2 */
public void A() { 
	try { 
    		System.out.println( 10 / 0 ); 
        	System.out.println( "끝" ); 
        } catch(Exception e) { 
        //catch(어떤 예외?) -Exception은 모든 예외 
        	System.out.println("예외 처리"); //출력
   	} 
   }
    //최종 출력 : 예외 처리

 

(코드2)와 같은 구문이 있습니다. 10을 0으로 나누고 있어서 예외가 발생하게 되죠.
이처럼 예외가 발생할 수 있는 부분을 try{ } 안에다 감싸줍니다. 그리고, 예외가 발생하면 즉시 catch 구문으로 넘어가 예외 처리를 수행하게 됩니다. 굉장히 간단합니다. try 구문에서 예외가 발생하면 catch 문으로 가서 해당 예외 처리를 하는 거죠.

 

하지만 주의해야 할 점이 있습니다. 


(코드2)의 최종 출력은 "예외 처리"입니다. try 안에 "끝"이라는 출력이 있음에도 출력하고 있지 않습니다.
눈치채셨겠지만, 예외가 발생한 지점에서 바로 catch 부분으로 넘어가는 것이죠.

 

즉, 10/0에서 예외가 발생해 catch 구문으로 넘어가 예외 처리를 하므로 "끝"이라는 글자가 출력이 되지 않은 것이죠.
(코드2)의 catch는 Exception을 매개변수로 사용하고 있습니다. Exception은 모든 예외를 나타내는 것입니다. 하지만 각각의 예외마다 처리하는 방식이 다를 수 있으므로 예상되는 예외를 매개변수에 넣어서 각각 알맞게 처리해 주시는 게 좋습니다.
(코드3)과 같이 말이죠.

 

/* 코드3 */
public static void main(String[] args) { 
  int x,sum; 
  try { 
    	x = Integer.parseInt(args[0]); sum = x / 0; 
    }catch(ArithmeticException e) { // 첫번째 예외처리 
      	System.out.println("0으로 나누어서 예외발생"); 
    }catch(Exception e ) { // 두번째 예외처리 
     	 System.out.println("args[0]를 주지않아 예외발생"); 
    } 
}

 

(코드3)과 같이 여러 가지 예외 처리를 선언할 수도 있으니, 자신이 처리해야 하는 예외들을 찾아보시고 알맞게 처리해 주는 게 필요합니다. 하지만 주의하실 점은 catch 구문도 순서가 있기에, 맨 처음부터 exception(모든 예외)로 선언해 준다면, 두 번째, 세 번째... 선언한 catch 문은 수행할 수가 없습니다.


자, 그럼 예외 처리를 살펴보았으니, 예외를 인위적으로 발생시켜 보죠.


예외를 인위적으로 발생시키는 방법은 throw라는 키워드가 있습니다.
throw'던진다' 즉 예외를 발생시키는 것이라 생각하시면 됩니다. 이것도 예시로 살펴보죠.

 

/* 코드4 */
public void A() { 
  try { 
  	throw new Exception(); 
  } catch(Exception e) { 
  	System.out.println("A에서 예외 처리"); 
  } 
} //최종 출력 : A에서 예외 처리

 

간단합니다. new 예외명();를 하여 객체를 생성하여 "throw 객체"로 예외를 발생시킵니다. 


이렇게 발생한 예외는 알맞은 catch 문에 들어가 예외 처리를 할 수 있죠. throw는 사람이 '인위적'으로 발생시키는 예외입니다. 반드시 처리해줘야 할 예외를 명시적으로 예외를 발생시켜, 유지 보수에 도움을 주기도 합니다.

마지막으로 throws라는 키워드입니다. throws는 "메소드명() throws 예외명"  형태로 사용하고, 자신을 불러온 메소드에서 예외를 던진다 정도로 생각하시면 됩니다. 예를 봅시다.

 

/* 코드5 */
public void A() { 
try { 
  	B(); 
  } catch(Exception e) { 
  	System.out.println("A에서 예외 처리"); 
  } 
} 

public void B() throws Exception{ 
	throw new Exception(); 
}

 

A메소드에서 B메소드를 호출하고 B메소드는 예외를 throw 하고 있습니다. 하지만 B에서는 try/catch 구문이 없어서 예외를 발생시켜도 처리해줄 곳이 없습니다. 이런 경우는 대게 '호출한 곳에서 처리해주겠다'라고 생각하시면 됩니다. 즉, 발생한 예외를 호출한 곳으로 넘겨주는 것이 throws입니다.


(코드5)의 B메서드 같이 throws를 해주게 되면, 인위적으로 생성한 예외가 A메소드로 넘어가 catch 부분에서 예외 처리가 되는 것이죠. throws 키워드는 메소드에서 발생할 수 있는 예외들을 나열하여 적어줌으로써 호출한 메소드에서 적절하게 대비를 하도록 유도하는 역할도 합니다.

 

throw, throws를 잘 구분하여 기억하시고 마지막으로 finally를 살펴보겠습니다.

finlly는 try/catch 구문을 수행하고 '반드시'수행되는 구문입니다. 예를 보시죠.

 

/* 코드6 */
public void A() { 
try { 
    System.out.println("1"); 
    System.out.println( 10 / 0); // 예상치못한 예외발생 
    System.out.println("2"); 
  } catch(Exception e) { 
  	System.out.println("3"); 
  } finally { 
  	System.out.println("4"); 
  } 
} //최종 출력 : 1 3 4

 

(코드6)를 봅시다. finally 구문은 catch 다음에 구현하여 사용합니다. finally 구문은 말씀드렸듯이 반드시 수행되는 구문입니다. 최종 출력을 보시면 1 3 4 가 나옵니다. 즉, 0으로 나누는 것에 예외가 발생하고 finally 구문을 수행했다는 것이죠. 정말 간단하고 알아 두시기만 하면 됩니다.

그럼 finally 구문은 왜 사용할까요? 이유는 간단합니다.

1. 반드시 수행되야 하는 구문이 있을 경우
2. try 구문, catch 구문의 중복되는 코드를 finally 구문에 넣음으로써 코드 중복을 없앨 경우

 

예를 들어볼까요? Scanner가 대표적입니다.

 

/* 코드7 */
public void A() { 
  Scanner test = new Scanner(System.in); 
  try { 
      //~~~~~~~ 기타 구문 수행 
      test.close(); 
  } catch(Exception e) { 
  	test.close(); 
  } 
}

 

(코드7)를 보시면 test로 Scanner가 선언되어있습니다. 만약 try 구문에서 예외 없이 마친다면 test.close로 Scanner를 끝내야겠죠. 하지만 try 구문 안에 //~~~~구문에서 예외가 발생한다면 어떨까요? 그럼 try 구문에서 close를 해주지 못했으니 catch 구문에 가서 똑같은 코드인 test.close를 해 줘야 합니다. 


하지만 finally 구문을 사용하게 된다면, 예외가 발생하든 안하든 반드시 수행해야 하므로 test.close의 코드 중복을 막고 보다 보기 편하게 코딩할 수 있습니다.

자, 지금까지 try/catch 예외 처리에 대해서 알아보았습니다. 어렵지 않은 내용입니다. 어떻게 사용하는지만 알고 있으면 충분히 응용하여 사용하실 수 있다고 생각 듭니다. thow, thows는 헷갈리지 않게 다시 한번 정리해 두시는 것이 좋겠습니다.

 

 여기서 궁금증 하나!

 

Q . 그럼 throws가 선언된 메서드 내에서의 try~catch문을 어떨까요?

 

@RequestMapping(value="/")
public void tabList(HttpServletRequest req, HttpServletResponse res) throws Exception{
		JSONObject jsonObject = new JSONObject();
		PrintWriter out = res.getWriter();
		
		try {
			jsonObject.put("code", 200);
		}catch(Exception e) {
			jsonObject.put("code", 400);
		}
		
		out.println(jsonObject);
	}

 

A.

try 문 안에서 예외가 발생하면, 그 예외가 catch문으로 정의가 되어있는 경우 catch문으로 빠집니다. 이 경우 밖으로 던져져지는 않습니다. catch문으로 정의가 되어있지 않은 예외이거나 try 바깥에 있는 영역의 경우 밖으로 던져집니다.

 

throws 구문이 예외를 처리한다 라고 생각하면 안되는게 throws는 예외를 지금 처리하지 않고 밖으로 던져서 책임을 떠넘긴다는 개념입니다. 그러면 지금 작성하고 있는 이 메소드를 호출하는 곳이 있겠죠? 거기서도 try catch를 쓰던지 throws를 또 쓰던지 둘 중하나는 해야 합니다.

 

결국 타고타고 올라가면 언젠간 try catch를 쓰게 됩니다.

 

(결국 public static void main까지 타고 올라가서 거기서도 throws로 던져버렸다면 그 예외는 아예 무시됩니다.)

 

 Conference

자다 뜯어보기 - 예외처리

 

 

읽어주셔서 감사합니다.

댓글
댓글쓰기 폼
공지사항
Total
248,450
Today
825
Yesterday
1,065
링크
«   2022/10   »
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31          
글 보관함