diff --git a/The_Missing_README/Chapter04.md b/The_Missing_README/Chapter04.md new file mode 100644 index 0000000..79d182b --- /dev/null +++ b/The_Missing_README/Chapter04.md @@ -0,0 +1,157 @@ +## 4장 운영 환경을 고려한 코드 작성 + +> 개발 환경과 프로덕션 환경은 엄연히 다르다 + +프로덕션 소프트웨어는 지속적으로 동작해야 한다. 따라서 운영 가능한 코드를 작성해야 예상하지 못한 상황에 대처할 수 있다. 운영 가능한 코드란 보호 장치, 분석 장치, 제어 장치가 내장된 코드를 말한다. 안전하고 회복성 있는 코딩 기법을 이용해 방어적으로 프로그래밍해서 시스템을 보호해야 한다. + +### 장애에 대비하기 위한 방어적 프로그래밍 방안 + +방어적 코드는 코드를 운영하는 모두를 위한 배려다. 방어적 코드는 장애가 발생하는 빈도가 낮으며, 장애가 발생하더라도 대부분 복구가 가능하다. 코드를 안전하고 회복성 있게 즉, **안정한 코드(safe code)**는 컴파일 타임의 유효성 검사를 통해 런타임 장애를 최소화한다. + +불변 변수, 접근 제어자로 범위 제한, 정적 타입 검사를 통한 버그 최소화 + +#### null값 사용은 피하자 + +대부분의 언어는 값이 할당되지 않은 변수의 기본값으로 null을 할당하여 빈번하게 NullPointerException이 발생한다. 변수에 대한 널 체크나 널 객체 패턴을 통해 예외를 방지하자. + +- [과거 널 객체 패턴에 관한 정리글](https://fkdl0048.github.io/patterns/Patterns_NullObject/) + +#### 불변 변수를 사용하자 + +*C#에선 레코드라는 불변을 보장할 수 있는 타입이 있다.* + +#### 타입 힌트와 정적 타입 검사를 활용하자 + +변수를 선언할 때는 가장 구체적인 타입을 사용한다. + +#### 입력값을 검사하자 + +코드로 전달되는 입력값은 절대로 신뢰해선 안 된다. 사전 조건 및 사후 조건을 이용해서 메소드에 전달되는 입력 변수의 유효성을 검사해야 한다. + +#### 예외를 활용하자 + +매직값을 통한 리턴으로 예외를 표한하는 것은 금물이다. 최신 개발 언어는 예외나 표준 예외 처리 패턴을 제공한다. 특정한 리턴값은 메소드 시그니처에 명확하게 드러나지 않는다. 따라서 개발자는 리턴값이 에러를 의미한다는 점을 모를 수 있다. + +따라서 예외를 통해 명확하게 나타내자. + +#### 예외는 구체적으로 사용하자 + +예외를 구체적으로 정의하면 코드를 더 쉽게 사용할 수 있다. 예외를 직접 정의할 때는 너무 포괄적인 의미가 담기지 않게 하자.(추상적인 예외보다 직접적이고 명확한 예외) + +#### 예외는 일찍 던지고 최대한 나중에 처리하자 + +'일찍 던지고 늦게 잡는' 원칙을 따르자. + +`일찍 던진다`는 말은 개발자가 관련 코드를 신속하게 찾을 수 있도록 에러가 발생한 지점으로부터 최대한 가까운 지점에서 예외를 던진다는 뜻이다. (실제 에러 위치를 파악하기 위해서) `예외를 늦게 잡는다`라는 말은 예외를 처리할 적절한 위치에 도착할 때까지 계속 호출 스택을 통해 전파시킨다는 뜻이다. + +정리하면 호출하는 코드가 예외를 던지면 예외를 완전히 처리하거나 혹은 상위 스택으로 전파하자. + +#### 재시도는 현명하게 + +에러를 적절하게 처리하는 방법 중 하나는 단순히 다시 시도하는 것이다. 여러 차례 재시도하려는 시도는 주로 원격 시스템을 호출할 경우에 나타난다. + +예외에서 재시도는 현명해보일 수 있어도 재시도의 시기와 빈도를 판단하려면 노하우가 필요하다. 가장 손쉬운 방법은 단순히 예외를 잡아서 그 즉시 작업을 다시 시도하는 것이다. 하지만 이런 방법은 재시도를 무한정 반복할 수 있기 때문에 위험하다. + +따라서 **백오프(backoff)**라는 전략을 사용하는 것이 좋다. 백오프는 비선형으로 대기 시간을 늘려 시간의 상한까지 재시도에 대한 빈도를 제어한다. + +모든 클라이언트가 이런 알고리즘을 사용한다면 무수히 많은 요청으로 인해 천둥떼 현상을 겪을 수 있다. (지터를 사용해서 해결) + +#### 시스템에 멱등성을 부여하자 + +재시도를 처리하는 가장 좋은 방법은 멱등성이 있는 시스템을 구현하는 것이다. 멱등성이란 동일한 작업을 여러 번 실행해도 같은 결과가 출력됨을 의미한다. (어떤 값을 해시셋에 추가하는 것은 멱등 작업이다.) + +#### 리소스를 해제하자 + +장애가 발생하면 모든 리소스를 해제해야 한다. 더 이상 필요하지 않은 메모리, 데이터 구조, 네트워크 소켓, 파일 핸들 등을 모두 해제하자. + +C#은 Dispose 패턴을 사용해서 리소스를 해제한다. C++은 직접 메모리를 해제해야 한다. + +### 문제 원인을 찾기 위한 로깅 방안 + +로깅은 간단하게 로그를 찍어 확인하는 편리한 방법으로 어떤 로그를 기록할지 제어하는 것이 중요하다. + +#### 로그 레벨을 사용하자 + +로깅 프레임워크는 운영자가 중요도에 따라 메시지를 필터링할 수 있도록 **로그 레벨**을 지원한다. 로그 레벨을 활용하면 매우 세세한 디버깅 로그부터 정상적인 운영 상화에서 주기적으로 기록되는 로그까지, 주어진 상황에 맞춰서 로그의 양을 조정할 수 있다. + +#### 로그는 원자적으로 작성하자 + +만일 데이터화 결합했을 때만 정보가 유용하다면 한 메시지에 모든 정보를 원자적으로 저장하자. + +#### 로그는 신속하게 기록하자 + +로그를 너무 많이 기록하면 성능에 영향을 미친다. 로그는 디스크나 콘솔, 원격 시스템 등 어딘가에 반드시 기록돼야 하며, 기록되기 전에 올바른 형태의 한 문자열로 결합해야 한다. + +*유니티에서도 잘 활용되는 듯 하다.* + +### 애플리케이션 동작 측정을 위한 지표 활용 방안 + +*Unity는 따로 프로파일링을* + +지표는 숫자로 표현한 로그와도 같아서 애플리케이션 동작을 측정한다. + +#### 표준 지표 라이브러리를 사용하자 + +이미 구현된 라이브러리를 사용하라. + +#### 모든 것을 측정하자 + +측정은 비용이 적게 드는 작업이므로 가급적 많은 지표를 수집해야 한다. 다음과 같은 데이터 구조, 작업, 동작은 모두 측정하자. + +- 리소스 풀 +- 캐시 +- 데이터 구조 +- CPU 집약적 작업 +- IO 집약적 작업 +- 데이터 크기 +- 예외와 에러 +- 원격 요청 및 응답 + +### 오늘날 분산 환경에서 더욱 중요해진 추적 + +RPC 클라이언트는 추적 라이브러리를 이용해서 요청마다 호출 추적 ID를 붙인다. + +### 설정으로 런타임 동작을 손쉽게 조정하려면 + +설정정보등을 Json, TAML처럼 일반 텍스트이면서 읽을 수 있는 형식, 환경변수, 명령줄 플래그 등등으로 설정값을 관리한다. + +#### 지나치게 창의적인 설정은 금물이다 + +이는 그냥 개발에서도 적용되는 모호하거나 오용될 수 있는 설정값을 사용하지 말라는 것이다. + +#### 모든 설정을 로그에 기록하고 검증하자 + +애플리케이션이 어떤 설정값을 사용하는지 알 수 있도록 시작 시점에 즉시 설정값을 로그에 기록하자. + +#### 기본값을 제공하자 + +대부분의 사용자가 시스템을 곧바로 사용할 수 있도록 적절한 기본값을 제공하자. + +#### 관련된 설정을 그룹화하자 + +애플리케이션 설정은 특히 중첩된 설정을 지원하지 않는 키-값 형식인 경우 금새 크기가 커져서 관리가 불가능해진다. + +#### 설정도 코드처럼 테스트하자 + +코드로서의 설정은 설정도 코드처럼 취급해야 한다는 철학이다. 설정을 안전하게 변경하려면 설정도 버전 제어로 관리하고 리뷰하고 테스트하고 배포한다. + +#### 설정 파일은 깔끔하게 유지하자 + +설정 파일이 깔끔하면 다른 사람이 이해하고 변경하기가 쉽다. 사용하지 않는 설정은 지우고 표준 형식과 공백을 사용해야 하며, 잘 모르면서 다른 파일에서 설정을 복사해 붙여넣는 일은 금하자. + +#### 배포된 설정은 변경하지 말자 + +특정 머신의 설정을 수작업으로 수정하는 일은 금하자. + +### 개발자의 필수 체크리스트 + +|이것만은 지키자|이것만은 피하자| +|:---|:---| +|런타임 에러보다는 컴파일 타임에 에러를 검출하자|예외를 이용해 애플리케이션의 로직을 결정하면 안된다| +|가능하면 불변 변수를 사용하자|리턴 코드로 예외를 처리해서는 안 된다| +|입력과 출력을 검증하자|처리할 수 없는 예외는 잡지 말자| +|10대 애플리케이션 취약점을 숙지하자|로그를 여러 줄로 나누지 말자| +|버그 확인도구는 물론 타입 또는 타입 힌트도 사용하자|보안 정보나 민감한 데이터를 로그에 기록하지 말자| +|코드에 지표를 추가하자|비밀번호나 보안 정보를 설정 파일에 기록하지 말자| +|애플리케이션 설정을 추가해두자|커스텀 설정 형식은 채택하지 말자| +|모든 설정을 검증하고 로그에 기록해하자|불필요한 동작 설정은 사용하지 말자|