👚🏻‍💻 읎펙티람 자바 - 예왞

Peter·2022년 3월 25음
0
post-thumbnail

10장 - 예왞


💡 예왞는 진짜 예왞 상황에만 사용하띌

“예왞륌 정상적읞 제얎 흐늄에서 사용핎서는 안 된닀.”

1. 예왞륌 잘못 사용한 예

// 예왞륌 완전히 잘못 사용한 예
try {
		int i = 0;
		while(true)
				range[i++].climb();
} catch(ArrayIndexOutOfBoundsException e) {
}
  • 위의 예시는 아죌 끔찍한 윔드임. 묎한 룚프륌 돌닀가 배엎의 끝에 도달핎 ArrayIndexOutOfBoundsException읎 발생하멎 끝을 낮는 것.
  • 읎 윔드는 잘못된 추론을 귌거로 성능을 높여볎렀 한 사례임.
    • JVM은 배엎에 접귌할 때마닀 겜계륌 넘지 않는지 검사하는데, 음반적읞 반복묞도 ë°°ì—Ž 겜계에 도달하멎 종료한닀. → 따띌서 읎 검사륌 반복묞에도 명시하멎 같은 음읎 쀑복되므로 하나륌 생략한 것
    • 읎 추론은 ì„ž 가지 멎에서 잘못된 추론임.
      1. 예왞는 예왞 상황에 ì“ž 용도로 섀계 되었윌므로 JVM 구현자 입장에서는 명확한 검사만큌 빠륎게 만듀얎알 할 동Ʞ가 앜핚. (최적화에 별로 신겜 쓰지 않았을 가능성읎 큌)
      2. 윔드륌 try-catch 랔록 안에 넣윌멎 JVM읎 적용할 수 있는 최적화가 제한됚.
      3. 배엎을 순회하는 표쀀 ꎀ용구는 앞서 걱정한 쀑복 검사륌 수행하지 않음. JVM읎 알아서 최적화핎 없애쀌.

→ 교훈 : 예왞는 (ê·ž 읎늄읎 말핎죌듯) 였직 예왞 상황에서만 썚알 한닀. 절대로 음상적읞 제얎 흐늄용윌로 쓰여선 안 된닀.

→ 잘 섀계된 API띌멎 큎띌읎얞튞가 정상적읞 제얎 흐늄에서 예왞륌 사용할 음읎 없게 í•Žì•Œ 한닀.


2. 상태 검사 메서드, 옵셔널, 특정 값 선택 지칚

  • 상태 의졎적 메서드 ex) Iterator 읞터페읎슀의 next 메서드
  • 상태 검사 메서드 ex) Iterator 읞터페읎슀의 hasNext 메서드
  1. 왞부 동Ʞ화 없읎 여러 슀레드가 동시에 접귌할 수 있거나 왞부 요읞윌로 상태가 변할 수 있닀멎 옵셔널읎나 특정 값을 사용한닀. → 상태 검사 메서드와 상태 의졎적 메서드 혞출 사읎에 객첎의 상태가 변할 수 있Ʞ 때묞
  2. 성능읎 쀑요한 상황에서 상태 검사 메서드가 상태 의졎적 메서드의 작업 음부륌 쀑복 수행한닀멎 옵셔널읎나 특정 값을 선택한닀.
  3. 닀륞 몚든 겜우엔 상태 검사 메서드 방식읎 조ꞈ 더 낫닀고 할 수 있닀. 가독성읎 삎짝 더 좋고, 잘못 사용했을 때 발견하Ʞ가 쉜닀. 상태 검사 메서드 혞출을 깜빡 잊었닀멎 상태 의졎적 메서드가 예왞륌 던젞 버귞륌 확싀히 드러낌 것임. → 반멎 특정 값은 검사하지 않고 지나쳐도 발견하Ʞ가 얎렵닀(옵셔널은 핎당하지 않는 묞제).


💡 복구할 수 있는 상황에는 검사 예왞륌, 프로귞래밍 였류에는 런타임 예왞륌 사용하띌

“복구할 수 있는 상황 → 검사 예왞 / 프로귞래밍 였류 또는 확싀하지 않을 때 → 비검사 예왞”

1. 검사 예왞

  • 음반적윌로 복구할 수 있는 조걎음 때 발생
  • 사용 지칚 : 혞출하는 쪜에서 복구하늬띌 여겚지는 상황읎띌멎 검사 예왞륌 사용하띌
  • 검사 예왞륌 던지멎 혞출자가 ê·ž 예왞륌 catch로 잡아 처늬하거나 더 바깥윌로 전파하도록 강제하게 됚.

→ 메서드 선얞에 포핚된 검사 예왞 각각은 ê·ž 메서드륌 혞출했을 때 발생할 수 있는 유력한 결곌임을 API사용자에게 알렀죌는 것. (API 사용자에게 ê·ž 상황에서 회복핎낎띌고 요구)


2. 비검사 예왞

  • 비검사 예왞는 두 가지로, 각각 런타임 예왞와 에러닀. 읎 둘은 프로귞랚에서 잡을 필요가 없거나 통상적윌로 잡지 말아알 핹.
  • 프로귞랚에서 비검사 예왞나 에러륌 던졌닀는 것은 복구가 불가능하거나 더 싀행핎뎐알 득볎닀는 싀읎 많닀는 뜻.
  • 읎런 throwable을 잡지 않은 슀레드는 적절한 였류 메시지륌 낎뱉윌며 쀑닚됚.
  • 사용 지칚
    1. 프로귞래밍 였류륌 나타낌 때는 런타임 예왞륌 사용하자
      • 런타임 예왞의 대부분은 전제조걎을 만족하지 못했을 때 발생. ex) ArrayIndexOutOfBoundsException
    2. 에러는 볎통 JVM읎 자원 부족, 불변식 깚짐 등 더읎상 수행을 계속할 수 없는 상황을 나타낌 때 사용
    3. 비검사 throwable은 몚두 RuntimeException의 하위 큎래슀여알 핹. (Error는 상속하지 말아알 할 뿐 아니띌, throw 묞윌로 직접 던지는 음도 없얎알 핹.)


💡 필요 없는 검사 예왞 사용은 플하띌

“옵셔널만윌로는 상황을 처늬하Ʞ에 충분한 정볎륌 제공할 수 없을 때만 검사 예왞륌 던지자”

1. 검사 예왞의 사용

  • ì–Žë–€ 메서드가 검사 예왞륌 던질 수 있닀고 선얞됐닀멎, 읎륌 혞출하는 윔드에서는 catch 랔록을 두얎 ê·ž 예왞륌 붙잡아 처늬하거나 더 바깥윌로 던젞 묞제륌 전파핎알 핹. → 얎느 쪜읎든 API 사용자에게 부닎을 쀌.
  • 검사 예왞가 프로귞래뚞에게 지우는 부닎은 메서드가 당 하나의 검사 예왞만 던질 때가 특히 큌.
    • 검사 예왞가 당 하나뿐읎띌멎 였직 ê·ž 예왞 때묞에 API 사용자는 try랔록을 추가핎알 하고 슀튞늌에서 직접 사용하지 못하게 됚.

→ 검사 예왞륌 안 던지는 방법읎 없는지 고믌핎볌 가치가 있닀.


2. 검사 예왞의 대첎재

  1. 적절한 결곌 타입을 닎은 옵셔널을 반환
    • 검사 예왞륌 던지는 대신 닚순히 빈 옵셔널을 반환.
    • 읎 방식의 닚점은 예왞가 발생한 읎유륌 알렀죌는 부가정볎륌 닎을 수 없닀는 것.
  2. 메서드륌 2개로 쪌개 비검사 예왞로 바꟞Ʞ
// 검사 예왞륌 던지는 메서드 - 늬팩터링 전
try {
		obj.action(args);
} catch(TheCheckedException e) {
		... // 예왞 상황에 대처한닀.
}
// 상태 검사 메서드와 비검사 예왞륌 던지는 메서드 - 늬팩터링 후
if(obj.actionPermitted(args)) {
		obj.action(args);
} else {
		... // 예왞 상황에 대처한닀.
}


💡 표쀀 예왞륌 사용하띌

“표쀀 예왞륌 재사용하멎 API가 닀륞 사람읎 익히고 사용하Ʞ 쉬워짐”

1. 대표적읞 표쀀 예왞

예왞죌요 쓰임
IllegalArgumentException허용하지 않는 값읎 읞수로 걎넀졌을 때(null은 따로 NullPointerException윌로 처늬)
IllegalStateException객첎가 메서드륌 수행하Ʞ에 적절하지 않은 상태음 때
NullPointerExceptionnull을 허용하지 않는 메서드에 null을 걎넞을 때
IndexOutOfBoundsException읞덱슀가 범위륌 넘얎섰을 때
ConcurrentModificationException허용하지 않는 동시 수정읎 발견됐을 때
UnsupportedOperationException혞출한 메서드륌 지원하지 않을 때
  • 상황에 부합한닀멎 항상 표쀀 예왞륌 재사용하자

※ Exception, RuntimeException, Throwable, Error는 직접 재사용하지 말자


2. IllegalArgumentException vs IllegalStateException

  • 위의 표로 정늬한 ‘죌요 쓰임’읎 상혞 배타적읎지 않은 탓에, 종종 재사용할 예왞륌 선택하Ʞ가 얎렀욞 때도 있음

  • ex) 칎드 덱을 표현하는 객첎가 있고, 읞수로 걎넚 수만큌의 칎드륌 뜑아 나눠죌는 메서드륌 제공한닀고 핎볎자. 읎때 덱에 ë‚šì•„ 있는 칎드 수볎닀 큰 값을 걎넀멎 ì–Žë–€ 예왞륌 던젞알 할까?

    • 읞수의 값읎 너묎 크닀고 볞닀멎 IllegalArgumentException, 덱에 낚은 칎드 수가 너묎 적닀고 볎멎 IllegalStateException 륌 선택할 것임.
  • 위의 상황에서 음반적읞 규칙은 아래와 같닀.

    • 읞수 값읎 묎엇읎었든 얎찚플 싀팚했을거띌멎 IllegalStateException , 귞렇지 않윌멎 IllegalArgumentException 을 던지자.


💡 추상화 수쀀에 맞는 예왞륌 던지자

“아래 계잵의 예왞륌 예방하거나 슀슀로 처늬할 수 없고,
ê·ž 예왞륌 상위 계잵에 귞대로 녞출하Ʞ 곀란하닀멎 예왞 번역을 사용하띌”

1. 예왞 번역

  • 예왞 번역(excepton translation) : 상위 계잵에서 저수쀀 예왞륌 잡아 자신의 추상화 수쀀에 맞는 예왞로 바꿔 던지는 것
try {
		... // 저수쀀 추상화륌 읎용한닀.
} catch(LowerLevelException e) {
		// 추상화 수쀀에 맞게 번역한닀.
		throw new HigherLevelException(...);
}

2. 예왞 연쇄

  • 예왞 연쇄(exception chaining) : 묞제의 귌볞 원읞(cause)읞 저수쀀 예왞륌 고수쀀 예왞에 싀얎 볎낎는 방식.
try {
		... // 저수쀀 추상화륌 읎용한닀.
} catch(LowerLevelException cause) {
		// 저수쀀 예왞륌 고수쀀 예왞에 싀얎 볎낞닀.
		throw new HigherLevelException(cause);
}

// 예왞 연쇄용 생성자
class HigherLevelException extends Exception {
		HigherLevelException(Throwable cause) {
				super(cause);
		}
}
  • 고수쀀 예왞의 생성자는 (예왞 연쇄용윌로 섀계된) 상위 큎래슀의 생성자에 읎 ‘원읞’을 걎넀죌얎, 최종적윌로 Throwable(Throwable) 생성자까지 걎넀지게 한닀.

3. 권장

→ 묎턱대고 예왞륌 전파하는 것볎닀알 예왞 번역읎 우수한 방법읎지만, 귞렇닀고 낚용핎서는 곀란핚.

  • 가능하닀멎 저수쀀 메서드가 반드시 성공하도록 하여 아래 계잵에서는 예왞가 발생하지 않도록 하는 것읎 최선
    • 때론 상위 계잵의 메서드의 맀개변수 값을 아래 계잵 메서드로 걎넀Ʞ 전에 믞늬 검사하는 방법윌로 읎 목적을 달성할 수 있음
  • 아래 계잵에서 예왞륌 플할 수 없닀멎, 상위 계잵에서 ê·ž 예왞륌 조용히 처늬하여 묞제륌 API 혞출자에까지 전파하지 않는 방법.
    • 읎 겜우 발생한 예왞는 java.util.logging 같은 적절한 로깅 Ʞ능을 활용하여 Ʞ록핎두멎 좋음.


💡 메서드가 던지는 몚든 예왞륌 묞서화하띌

“발생 가능한 예왞륌 묞서로 낚Ʞ지 않윌멎 닀륞 사람읎 ê·ž 큎래슀나
읞터페읎슀륌 횚곌적윌로 사용하Ʞ 얎렵거나 심지얎 불가능할 수도 있닀.”

  • 검사 예왞는 항상 따로따로 선얞하고, 각 예왞가 발생하는 상황을 자바독의 @throw 태귞륌 사용하여 정확히 묞서화하자
  • public 메서드띌멎 필요한 전제조걎을 묞서화핎알 하며, ê·ž 수닚윌로 가장 좋은 것읎 바로 비검사 예왞듀을 묞서화하는 것임.
  • 메서드가 던질 수 있는 예왞륌 각각 @throw 태귞로 묞서화하되, 비검사 예왞는 메서드 선얞의 throws 목록에 넣지 말자.
    • 검사 예왞 : 메서드 선얞의 thorws 절에 등장 O && 메서드 죌석의 @throw 태귞에 명시 O
    • 비검사 예왞 : 메서드 선얞의 thorws 절에 등장 X && 메서드 죌석의 @throw 태귞에 명시 O
    • 읎 조걎을 만족하여 작성하멎 자바독 유틞늬티가 시각적윌로 구분핎쀌.
  • 한 큎래슀에 정의된 많은 메서드가 같은 읎유로 같은 예왞륌 던진닀멎 ê·ž 예왞륌 (각각의 메서드가 아닌) 큎래슀 섀명에 추가하는 방법도 있음.
    • ex) “읎 큎래슀의 몚든 메서드는 읞수로 null읎 넘얎였멎 NullPointerException을 던진닀.”


💡 예왞의 상섞 메시지에 싀팚 ꎀ렚 정볎륌 닎윌띌

“사후 분석을 위핎 싀팚 순간의 상황을 정확히 포착핎 예왞의 상섞 메시지에 ë‹Žì•„ì•Œ 핹”

  • 예왞륌 잡지 못핎 프로귞랚읎 싀팚하멎 자바 시슀템은 ê·ž 예왞의 슀택 추적(stack trace) 정볎륌 자동윌로 출력핚.
    • 슀택 추적은 예왞 객첎의 toString 메서드륌 혞출핎 얻는 묞자엎로, 볎통은 예왞의 큎래슀 읎늄 뒀에 상섞 메시지가 붙는 형태.
  • 싀팚 순간을 포착하렀멎 발생한 예왞에 ꎀ여된 몚든 맀개변수와 필드의 값을 싀팚 메시지에 ë‹Žì•„ì•Œ 핹.
    • ex) IndexOutOfBoundsException 의 상섞 메시지는 범위의 최솟값곌 최댓값, 귞늬고 ê·ž 범위륌 벗얎났닀는 읞덱슀의 값을 ë‹Žì•„ì•Œ 핹.
    • but, 상섞 메시지에 비밀번혞나 암혞 í‚€ 같은 정볎까지 닎아서는 안 됚.
  • 예왞의 상섞 메시지와 최종 사용자에게 볎여쀄 였류 메시지륌 혌동핎서는 안 됚.
    • 최종 사용자에게는 친절한 메시지로 작성, 예왞 메섞지는 죌 소비잵읎 프로귞래뚞임을 고렀하여 작성.
  • 예왞는 싀팚와 ꎀ렚된 정볎륌 얻을 수 있는 접귌자 메서드륌 적절히 제공하는 것읎 좋음.


💡 가능한 한 싀팚 원자적윌로 만듀띌

“메서드 명섞에 Ʞ술한 예왞띌멎 섀혹 예왞가 발생하더띌도 객첎의 상태는
메서드 혞출 전곌 똑같읎 유지돌알 한닀는 것읎 Ʞ볞 규칙”

1. 싀팚 원자적읎란?

  • 싀팚 원자적(failure-atomic) : 혞출된 메서드가 싀팚하더띌도 핎당 객첎는 메서드 혞출 전 상태륌 유지핎알 핹.
  • 싀팚 원자적읎띌멎 작업 도쀑 예왞가 발생핎도 ê·ž 객첎는 여전히 정상적윌로 사용할 수 있는 상태임.
  • 싀팚 원자적윌로 만듀 수 없닀멎 싀팚 시의 객첎 상태륌 API 섀명에 명시핎알 핹.

2. 싀팚 원자적을 얻는 방법

  1. 불변 객첎로 섀계
    • 불변 객첎는 태생적윌로 싀팚 원자적임.
    • 메서드가 싀팚하멎 새로욎 객첎가 만듀얎지지는 않을 수 있윌나 êž°ì¡Ž 객첎가 불안정한 상태에 빠지는 음은 결윔 없음. → 불변 객첎의 상태는 생성 시점에 고정되얎 절대 변하지 ì•Šêž° 때묞
  2. 작업 수행에 앞서 맀개변수의 유횚성을 검사
    • 객첎의 낎부 상태륌 변겜하Ʞ 전에 잠재적 예왞의 가능성을 대부분 걞러낌 수 있는 방법
    • 비슷한 췚지로 싀팚할 가능성읎 있는 몚든 윔드륌, 객첎의 상태륌 바꟞는 윔드볎닀 앞에 배치하는 방법도 있음
  3. 객첎의 임시 복사볞에서 작업을 수행한 닀음, 작업읎 성공적윌로 완료되멎 원래 객첎와 교첎
    • 데읎터륌 임시 자료구조에 저장핎 작업하는 게 더 빠륌 때 적용하Ʞ 좋은 방식.
  4. 작업 도쀑 발생하는 싀팚륌 가로채는 복구 윔드륌 작성하여 작업 전 상태로 되돌늬Ʞ
    • 죌로 (디슀크 Ʞ반의) 낎구성(durability)을 볎장핎알 하는 자료구조에 쓰임.


💡 예왞륌 묎시하지 말띌

“ catch 랔록을 비워두지 말자”

  • 예왞는 묞제 상황에 잘 대처하Ʞ 위핎 졎재하는데, catch 랔록을 비워두멎 예왞가 졎재할 읎유가 없얎짐.
    • 비유하자멎 화재겜볎륌 묎시하는 수쀀을 넘얎 아예 꺌버렀, 닀륞 누구도 화재가 발생했음을 알지 못하게 하는 것곌 같음.
  • 예왞륌 묎시하Ʞ로 했닀멎 catch 랔록 안에 귞렇게 결정한 읎유륌 죌석윌로 ë‚šêž°ê³  예왞 변수의 읎늄도 ignored 로 바꿔놓도록 하자.
  • 예잡할 수 있는 예왞 상황읎든 프로귞래밍 였류든, 빈 catch 랔록윌로 못 볞 척 지나치멎 ê·ž 프로귞랚은 였류륌 낎재한 채 동작하게 됚.

→ 묎시하지 않고 바깥윌로 전파되게만 놔둬도 최소한 디버깅 정볎륌 낚ꞎ 채 프로귞랚읎 신속히 쀑닚되게는 할 수 있닀

profile
https://dev-peter.online/

0개의 댓Ꞁ