Recent Posts
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- kotlin
- 코루틴 컨텍스트
- 개성국밥
- JanusGateway
- PytestPluginManager
- python
- JanusWebRTCGateway
- JanusWebRTC
- 달인막창
- PersistenceContext
- k8s #kubernetes #쿠버네티스
- Value too long for column
- pytest
- Spring Batch
- table not found
- 겨울 부산
- 헥사고날아키텍처 #육각형아키텍처 #유스케이스
- 깡돼후
- 자원부족
- vfr video
- taint
- 오블완
- tolerated
- mp4fpsmod
- terminal
- VARCHAR (1)
- 코루틴 빌더
- JanusWebRTCServer
- 티스토리챌린지
- preemption #
Archives
너와 나의 스토리
[리팩터링] 오류 코드를 예외로 바꾸기 Replace Error Code with Exception 본문
반응형
배경
- 오류가 발견되면 예외를 던진다. 그러면 적절한 예외 핸들러를 찾을 때까지 콜스택을 타고 위로 전파된다.
- 예외를 사용하면 오류 코드를 일일이 검사하거나 오류를 식별해 콜스택 위로 던지는 일을 신경 쓰지 않아도 된다.
- 예외는 프로그램의 정상 동작 범주에 들지 않는 오류를 나타낼 때만 쓰여야 한다.
- 예외를 던지는 코드를 프로그램 종료 코드로 바꿔도 프로그램이 여전히 정상 동작할지를 따져보고, 정상 동작하지 않을 것 같다면 예외를 사용하지 말라는 신호이다.
- 이런 경우, 예외 대신 오류를 검출하여 프로그램을 정상 흐름으로 되돌리게끔 처리해야 한다.
절차
- 콜스택 상위에 해당 예외를 처리할 예외 핸들러를 작성한다.
- 테스트한다.
- 해당 오류 코드를 대체할 예외와 그 밖의 예외를 구분할 식별 방법을 찾는다.
- 정적 검사를 수행한다.
- catch 절을 수정하여 직접 처리할 수 있는 예외는 적절히 대처하고 그렇지 않은 예외는 다시 던진다.
- 테스트한다.
- 오류 코드를 반환하는 곳 모두에서 예외를 던지도록 수정한다. 하나씩 수정할 때마다 테스트한다.
- 모두 수정했다면 그 오류 코드를 콜스택 위로 전달하는 코드를 모두 제거한다. 하나씩 수정할 때마다 테스트한다.
예시
- 전역 테이블에서 배송지의 배송 규칙을 알아내는 코드를 생각해보자.
function localShippingRules(country) { const data = countryData.shippingRules[country]; if (data) return new ShippingRules(data); else return -23; }
- 이 코드는 country가 유효한지를 이 함수 호출 전에 다 검증했다고 가정한다.
- 이 함수에서 오류가 난다면 다음과 같이 호출한 곳에서 반환된 오류 코드를 검사하여 오류가 발견되면 위로 전파한다.
-
function calculateShippingCosts(anOrder){ const shippingRules = localShippingRules(anOrder.coutnry); if (shippingRules < 0) return shippingRules; // 오류 전파 }
- 더 윗단 함수는 오류를 낸 주문을 오류 목록에 넣는다.
-
const status = calculateShippingCosts(orderData); if (status < 0) errorList.push({ order: orderData, errorCode: status });
- 여기서 고려할 것들
- localShippingRules()는 배송 규칙들이 countryData에 제대로 반영되어 있다고 가정해도 되나?
- country 인수가 전역 데이터에 저장된 키드리과 일치하는 곳에서 가져온 것인가? 아니면 앞서 검증을 받았나?
- 이 질문들의 답이 긍정적이면(예상할 수 있는 정상 동작 범주에 든다면) 오류 코드를 예외로 바꾸는 이번 리팩터링을 적용할 준비가 된 것이다.
- [Step 1] 최상위에 예외 핸들러를 작성한다.
- calculateShippingCosts() 호출을 try 블록으로 감싼다.
-
try { const status = calculateShippingCosts(orderData); } catch (e) { // 예외 처리 로직 } if (status < 0) errorList.push({ order: orderData, errorCode: status });
- 그렇다고 이렇게 하면 status 선언이 try 블록으로 국한되기 때문에 조건문에서 검사할 수 없다.
- 그래서 status 선언과 초기화를 분리해야 한다.
-
let status; try { status = calculateShippingCosts(orderData); } catch (e) { throw e; } if (status < 0) errorList.push({ order: orderData, errorCode: status });
- 잡은 예외는 모두 다시 던져야 한다.
- 호출하는 쪽 코드의 다른 부분에서도 주문을 오류 목록에 추가할 일이 있을 수 있으니 적절한 핸들러가 이미 구비되어 있을 수 있다.
- [Step 3] 해당 오류 코드를 대체할 예외와 그 밖의 예외를 구분할 식별 방법을 찾는다.
- 이번 리팩터링으로 추가된 예외만을 처리하고자 한다면 다른 예외와 구별할 방법이 필요하다.
- 예외를 클래스 기반으로 처리하는 프로그래밍 언어가 많은데, 이런 경우라면 서브클래스를 만드는 게 가장 자연스럽다.
- 현재의 자바스크립트는 여기에 해당하지 않지만 이렇게 하는게 좋다고 한다.
-
class OrderProcessingError extends Error { constructor(errorCode) { super(`주문 처리 오류: ${errorCode}`); this.code = errorCode; } get name() { return "OrderProcessingError" }; }
- [Step 5] catch 절을 수정하여 직접 처리할 수 있는 예외는 적절히 대처하고 그렇지 않은 예외는 다시 던진다.
-
let status; try { status = calculateShippingCosts(orderData); } catch (e) { if (e instanceof OrderProcessingError) errorList.push({ order: orderData, errorCode: status }); else throw e; } if (status < 0) errorList.push({ order: orderData, errorCode: status });
-
- [Step 7] 오류 검출 코드를 수정하여 오류 코드 대신 이 예외를 던지도록 한다.
-
function localShippingRules(country) { const data = countryData.shippingRules[country]; if (data) return new ShippingRules(data); else throw new OrderProcessingError(-23); // Before: return -23 }
-
- [Step 8] 모두 수정했다면 그 오류 코드를 콜스택 위로 전달하는 코드를 모두 제거한다.
- 제거하기 전에 다음처럼 함정을 추가한 후 테스트해보면 좋을 것 같다.
-
function calculateShippingCosts(anOrder) { const shippingRules = localShippingRules(anOrder.coutnry); // Before: if (shippingRules < 0) return shippingRules; // 오류 전파 if (shippingRules < 0) throw new Error("오류 코드가 다 사라지지 않았습니다."); }
- 이 함정(new Error)에 걸리지 않는다면, 이 줄 전체를 제거해도 안전하다.
-
function calculateShippingCosts(anOrder) { const shippingRules = localShippingRules(anOrder.coutnry); // 다른 코드들 }
-
// 최상위 함수 try { calculateShippingCosts(orderData); } catch (e) { if (e instanceof OrderProcessingError) errorList.push({ order: orderData, errorCode: e.code }); else throw e; }
출처:
- [리팩터링 2판]
반응형
'개발 > Refactoring' 카테고리의 다른 글
[리팩터링] 슈퍼클래스 추출하기 Extract Superclass (0) | 2022.02.16 |
---|---|
[리팩터링] 11.8 생성자를 팩터리 함수로 바꾸기 Replace Constructor with Factory Function (0) | 2022.01.25 |
[리팩터링] 11.5 매개변수를 질의 함수로 바꾸기 Replace parameter with Query (0) | 2022.01.18 |
[리팩터링] 특이 케이스 추가하기 Introduce Special Case (0) | 2022.01.02 |
[리팩토링] Replace Inline Code with Function Call 인라인 코드를 함수 호출로 바꾸기 (0) | 2021.11.20 |
Comments