Hayden's Archive
클린코드(Clean Code) 7, 8장 - 로버트 C. 마틴 본문
book.naver.com/bookdb/book_detail.nhn?bid=7390287
7. 오류 처리
- 오류 처리 코드로 인해 프로그램 논리를 이해하기 어려워진다면 깨끗한 코드라 부르기 어려움
- 오류 코드보다 예외를 사용하라
- 오류 플래그를 설정하거나 호출자에게 오류 코드를 반환하는 방법을 사용하면 호출자 코드가 복잡해짐
- 오류가 발생하면 예외를 던지는 편이 낫다
- Try-Catch-Finally 문부터 작성하라
- try 블록은 트랜잭션과 비슷함
- try 블록에서 무슨 일이 생기든지 catch 블록은 프로그램 상태를 일관성 있게 유지해야 함
- 먼저 강제로 예외를 일으키는 테스트 케이스를 작성한 후 테스트를 통과하게 코드를 작성하는 방법을 권장함
- 그러면 자연스럽게 try 블록의 트랜잭션 범위부터 구현하게 되므로 범위 내에서 트랜잭션 본질을 유지하기 쉬워짐
- try 블록은 트랜잭션과 비슷함
- 미확인 예외를 사용하라
- 지금은 안정적인 소프트웨어를 제작하는 요소로 확인된 예외가 반드시 필요하지는 않음
- 확인된 예외는 OCP(Open Closed Principle)를 위반함
- 확인된 예외 참고 : 예외(Exception) 처리하기_기본개념과 예외처리 클래스 [1/5]
- 하위 단계에서 코드를 변경하면 상위 단계 메서드 선언부를 전부 고쳐야 함
- 모듈과 관련된 코드가 전혀 바뀌지 않았더라도 (선언부가 바뀌었으므로) 모듈을 다시 빌드한 다음에 배포해야
함
- 대규모 시스템에서 최상위 함수가 아래 함수를 호출하고, 아래 함수는 그 아래 함수를 호출하고, 단계를 내려갈수록 호출하는 함수 수가 증가함
- 최상위 함수를 변경해서 새로운 오류를 던진다고 가정
- 확인된 오류를 던진다면 함수는 선언부에 throws 절을 추가해야 함
- 변경한 함수를 호출하는 함수 모두가 1) catch 블록에서 새로운 예외를 처리하거나 2) 선언부에 throw 절을 추가해야 함
- 결과적으로 최하위 단계에서 최상위 단계까지 연쇄적인 수정이 일어남
- thorws 경로에 위치하는 모든 함수가 최하위 함수에서 던지는 예외를 알아야 하므로 캡슐화가 깨짐
- 최상위 함수를 변경해서 새로운 오류를 던진다고 가정
- 아주 중요한 라이브러리를 작성할 경우 모든 예외를 잡아야 하지만, 일반적인 애플리케이션은 확인된 예외에서 의존성이라는 비용이 이익보다 큼
- 예외에 의미를 제공하라
- 예외를 던질 때는 전후 상황을 충분히 덧붙여서 오류가 발생한 원인과 위치를 찾기 쉽게 함
- 오류 메시지에 정보를 담아 예외와 함께 던짐(실패한 연산 이름과 실패 유형도 언급)
- 애플리케이션이 로깅 기능을 사용한다면 catch 블록에서 오류를 기록하도록 충분한 정보를 넘김
- 호출자를 고려해 예외 클래스를 정의하라
- 외부 API를 사용할 때는 감싸기 기법이 최선
- 외부 API를 감싸면 외부 라이브러리와 프로그램 사이에서 의존성이 크게 줄어듦
- 또한 감싸기 클래스에서 외부 API를 호출하는 대신 테스트 코드를 넣어주는 방법으로 프로그램을 테스트하기도 쉬워짐
- 특정 업체가 API를 설계한 방식에 발목을 잡히지 않음
- 프로그램이 사용하기 편리한 API를 정의하면 그만
- 예외 클래스에 포함된 정보로 오류를 구분해도 괜찮은 경우, 예외 클래스가 하나만 있어도 충분함
- 한 예외는 잡아내고 다른 예외는 무시해도 괜찮은 경우라면 여러 예외 클래스 사용
- 외부 API를 사용할 때는 감싸기 기법이 최선
8. 경계
- 모든 소프트웨어를 직접 개발하는 경우는 드물다
- 그렇다면 어떻게 외부 코드를 우리 코드에 깔끔하게 통합할 것인가?
- 소프트웨어 경계를 깔끔하게 처리하는 기법과 기교
- 외부 코드 사용하기
- 경계 인터페이스인 Map을 Sensors 안으로 숨기기
- Map 인터페이스가 변하더라도 나머지 프로그램에는 영향을 미치지 않음
- Sensors 클래스 안에서 객체 유형을 관리하고 변환하므로 제네릭스를 사용하든 하지 않든 더 이상 문제가 되지 않음
- Sensor 클래스는 프로그램에 필요한 인터페이스만 제공
- 코드 이해는 쉽고 오용은 어려움
- 설계 규칙과 비즈니스 규칙을 따르도록 강제
- 경계 인터페이스를 사용할 때 유의할 점
- 이를 이용하는 클래스나 클래스 계열 밖으로 노출되지 않도록 주의
- Map 인스턴스를 공개 API의 인수로 넘기거나 반환값으로 사용하지 않음
- 경계 인터페이스인 Map을 Sensors 안으로 숨기기
- 경계 살피고 익히기
- 학습 테스트
- 우리쪽 코드를 작성해 외부 코드를 호출하는 대신 먼저 간단한 테스트 케이스를 작성해 외부 코드를 익힘
- 프로그램에서 사용하려는 방식대로 외부 API 호출
- API를 사용하려년 목적에 초점
- 학습 테스트
- log4j 익히기
- 간단한 콘솔 로거를 초기화한 방법을 익힌 후 모든 지식을 독자적인 로거 클래스로 캡슐화
- 나머지 프로그램은 log4j 경계 인터페이스를 몰라도 됨
- 간단한 콘솔 로거를 초기화한 방법을 익힌 후 모든 지식을 독자적인 로거 클래스로 캡슐화
- 학습 테스트는 공짜 이상이다
- 학습 테스트는 이해도를 높여주는 정확한 실험
- 패키지 새 버전이 나온다면 학습 테스트를 돌려 차이가 있는 확인
- 새 버전이 우리 코드와 호환되지 않으면 학습 테스트가 이 사실을 곧바로 밝혀냄
- 어차피 실제 코드와 동일한 방식으로 인터페이스를 사용하는 테스트 케이스가 필요
- 이런 경계 테스트가 있다면 패키지의 새 버전으로 이전하기 쉬워짐경계 테스트가 있다면 패키지의 새 버전으로 이전하기 쉬워짐
- 아직 존재하지 않는 코드를 사용하기
- 아는 코드와 모르는 코드를 분리하는 경계
- 우리가 바라는 인터페이스를 구현하면 우리가 인터페이스를 전적으로 통제할 수 있고, 코드 가독성이 높아지며, 코드 의도도 분명해짐
- 깨끗한 경계
- 경계에 위치하는 코드는 깔끔히 분리함
- 기대치를 정의하는 테스트 케이스도 작성
- 통제 불가능한 외부 패키지에 의존하는 대신 통제가 가능한 우리 코드에 의존하는 편이 훨씬 좋음
- 외부 패키지를 호출하는 코드를 가능한 줄여 경계 관리하기
- 새로운 클래스로 경계를 감싸기
- ADAPTER 패턴을 사용해서 우리가 원하는 인터페이스를 경계가 감싸기
- ADAPTER 패턴을 사용해 우리가 원하는 인터페이스를 패키지가 제공하는 인터페이스로 변환
- 코드 가독성이 높아지며, 경계 인터페이스를 사용하는 일관성도 높아지고, 외부 패키지가 변경했을 때 변경할 코드가 줄어듦