코드를 잘 작성하는 방법. 읽기 쉬운 코드에 대한 이야기.

Redux 가이드의 보일러 플레이트 줄이기

https://ko.redux.js.org/usage/reducing-boilerplate/#%EC%95%A1%EC%85%98

Flux에서는 전통적으로 모든 액션 타입을 문자열 상수로 정의합니다:

> const ADD_TODO = 'ADD_TODO'
> const REMOVE_TODO = 'REMOVE_TODO'
> const LOAD_ARTICLE = 'LOAD_ARTICLE'

이게 어떤 이점이 있을까요? 작은 프로젝트에서 상수는 불필요하다는 지적이 종종 있었고 옳은 말입니다. 큰 프로젝트에서는 액션 타입을 상수로 정의하는 이점들이 있습니다:

  • 모든 액션 타입이 한 곳에 모이기 때문에 이름 짓기의 일관성을 유지하는데 도움이 됩니다.
  • 새 기능을 만들기 전에 기존의 모든 액션을 한눈에 보고 싶을 때가 있을겁니다. 여러분이 필요로 하는 액션이 팀의 다른 사람에 의해 이미 추가되었지만 여러분이 모르고 있을 수도 있으니까요.
  • 추가되고, 제거되고, 변경된 액션 타입의 목록은 풀 리퀘스트에서 팀원 모두가 새 기능의 범위와 구현을 따라가는걸 도와줄겁니다.
  • 만약 여러분이 액션 상수를 불러오다가 오타를 내면 undefined가 나올겁니다. 액션을 보냈는데 아무 일도 일어나지 않는 것보다는 훨씬 알아차리기 쉽습니다.

리덕스의 액션에 대한 예시지만 다양한 프로젝트에 적용할 수 있는 방법이다. 상수를 한군데 작성함으로써 반복되어 보일러플레이트로 판단할 수도 있다. 하지만 문자열에 의도를 표현하고, 한 군데서 관리할 수 있고, 기능 파악에 도움을 주고, 오타에 대한 대응이 있어서 좋은 방법이라고 설명한다.

대단한 구조를 가진 코드는 아니지만, 간단하고 자연스레 이런 방법을 사용하는 경우도 많겠지만, 의미를 찾고, 문서화를 했다는 점에서 잘 작성하려는 노력이 보인다.

Parse, don't validate

검증하지 말고 파싱하라.

https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/

요지는 데이터를 검증하는 것보다 파싱을 통해 변환된 타입으로 반환하는 것이 더 좋다는 이야기이다. 짧지만 임팩트가 강한 슬로건이 긴 글을 압축한다.

validateNonEmpty :: [a] -> IO ()
validateNonEmpty (_:_) = pure ()
validateNonEmpty [] = throwIO $ userError "list cannot be empty"

parseNonEmpty :: [a] -> IO (NonEmpty a)
parseNonEmpty (x:xs) = pure (x:|xs)
parseNonEmpty [] = throwIO $ userError "list cannot be empty"

validateNonEmpty ro수는 리스트가 비어있지 않은지 검증만 한다. parseNonEmpty 함수는 비어있지 않은 리스트를 NonEmpty 타입으로 변환한다. 타입 시스텐이 비어있지 않음을 보장한다. 각 함수의 반환 값을 사용할 때, 검증 방식은 재검증에 대한 여지가 있지만 파싱 방식은 그렇지 않다.

검증의 위험성은 처리 로직과 함께 사용될 때 드러난다. 검증을 통과한 입력을 처리하다가, 다른 부분의 검증에 실패하면 이미 수정된 부분을 되돌리기 어렵다. 모든 검증을 미리 처리할 수 있겠지만, 정말 미리 처리되었는지 그것을 판단하기는 어렵다. 이런식으로 검증의 더미에 내던지고 에러 케이스를 처리할 것이라 기대하는 안티 패턴을 Shotgun Parser이라 한다.