티스토리 뷰

book-movie

Go 100가지 실수 패턴과 솔루션

주먹불끈 2024. 2. 11. 20:14

개요

알라딘 링크: http://aladin.kr/p/LQhvg

길벗 출판사의 페이스북 책 나눔 이벤트에 당첨되어 읽게 되었지만, 그렇지 않아도 읽어보려 했던 책이었다.

이 책은 Go를 사용하고, 실무에서 사용해 보았지만 한 단계 레벨업이 필요한 이들이라면 필독해야할 책이며 현업의 어느 순간, 어떤 구현이 더 좋을지 애매했던 주제들에 대해 명쾌한 답을 준다. 꼭 읽어보시길 권한다. 시중에 좋은 고 언어 책이 점점 더 늘어나고 있다.

그러나, 이 100가지 패턴과 솔루션을 하나하나 외우는 것은 불가능하다. 그럴 때에는 두 가지 전략이 필요하다. 하나는 일이관지(一以貫之)이다. 하나로 꿰뚫어내는 능력이다. 책 속의 수 많은 패턴과 그 솔루션은 거슬러 올라가면 몇몇 개발의 핵심 이치와 닿아있다. 단순한 외우기가 아닌 그 이치에 대한 고민을 가져야 내 것으로 만들 수 있다. 두 번째는 재독, 삼독이다. 읽고 배워야 할 것이 많은데 언제 같은 책을 여러 번 읽으랴 하겠지만 고민하며 읽은 일독은 재독, 삼독을 좀 더 쉽고 빠르게, 그리고 의미를 더욱 깊이 이해하게 만들어 줄 것이다.

아래에는 책을 읽으며 재미있었던 부분, 깨달음을 준 부분, 정리해두고 싶은 부분들을 남겨본다.

정리

 

1장. Go: 시작은 쉽지만 마스터하긴 어렵다

  1. 고 언어는 기능을 마구 추가하지 않는다.

여러분이 원하는 기능을 고 언어에서 제공하지 않는다면 그 기능이 1) 고 언어에 어울리지 않거나 2) 컴파일 속도나 3) 설계의 명료함을 떨어뜨리거나 4) 기반 시스템 모델을 필요 이상으로 복잡하게 만들기 때문이다. p25 (번호는 정현석이 매김)

  1. 단순한 고 언어에 능숙해 지려면 고 언어의 다양한 측면을 철저하게 이해해야 한다.

단순하다고 해서 쉬운 것은 아니다 - Simple doesn’t mean easy p26

2장. 코드와 프로젝트 구성

  1. 왼쪽 정렬의 가독성

정상 경로는 왼쪽으로 정렬한다. 그러면 첫 번째 열만 살짝 훑어봐도 예상대로 실행되는지를 쉽게 파악할 수 있다. p38 Align the happy path to the left; you should quickly be able to scan down one columnn to see the expected execution flow

원문 링크: https://medium.com/@matryer/line-of-sight-in-code-186dd7cdea88

정현석 이해: 왼쪽 정렬하면 기대하는 코드 실행의 흐름(에러가 발생하지 않고 정상 동작하는 경로)을 빠르게 파악할 수 있다.

  1. init 함수를 알라

init 함수는 에러 관리, 테스트 가 어렵고, 데이터베이스 커넥션 풀을 글로벌 변수에 저장해야 한다.

  1. 동작을 제한하는 인터페이스 p55

10개의 기능을 할 수 있는, 즉 10개의 메서드를 가진 구조체가 있다고 하자. 원하는 기능은 오직 하나이고, 다른 기능은 안전하게 봉인하고 싶다면 메서드 하나인 인터페이스를 정의하고 해당 구조체 인스턴스를 그 인터페이스 타입으로 변환하여 쓰면 된다.

  1. 추상화는 언제 하는가?

추상화는 찾아내야지, 창조하면 안된다. ”인터페이스로 설계하지 말고 찾아내라 - Don't design with interfaces, discover them” 롭 파이크 p57

  1. 제공자 측에 인터페이스를 두지 마라 p58

인터페이스를 채용공고의 JD(job description)에 비유하고는 한다. 채용공고는 채용하여 일을 맡기려는 회사측에서 정의하지 구직자가 정의하지 않는다. 마찬가지이다. 인터페이스는 그 인터페이스를 사용하려는 코드 쪽, 사용자 측에서 정의하자.

  1. 포스텔 법칙(Postel’s Law)

자신이 하는 일에는 엄격하고, 다른 사람으로부터 받을 때는 관대하라. p62 be conservative in what you send, be liberal in what you accept

책에서는 인터페이스에 대해서 말하는데 파라미터를 받을 때는 관대하게 인터페이스로 받되, 리턴하여 내보낼때는 엄격하게 타입을 명시하라는 것이다. 여기서 이유는 생략한다.

  1. 임베디드 필드의 격상과 원치 않은 노출 p75

책 속의 뮤텍스 예시처럼 임베디드 필드의 메서드가 외부에 노출되어 버린다.

  1. 임베딩은 조합한다.

임베딩은 조합할 뿐 상속하지는 않는다. p77

Composition over inheritance 라는 격언을 기억하자. 고 언어는 여러 임베딩을 조합하는 것을 장려한다.

  1. 함수형 옵션 패턴(functional options pattern) p84

0개 이상의 옵션을 전달하는 방법. 종종 보아왔지만 이번에 이름을 알게 되었다. 얼마전 업무를 하다 회사 분이 이 패턴을 이야기해 주시는 바람에 더욱 머리속에 각인이 되었다. ChatGPT로 생성한 예제 링크를 남겨둔다.

  1. 결정하고 행동하라

잘못된 결정이란 결정하지 못하고 망설이는 것뿐이다. p88

공감이 되는, 나 자신을 돌아보게 하는 문장이다.

  1. 패키지 네이밍

패키지 이름은 그 안에 담긴 내용물이 아닌, 그 패키지가 제공하는 기능에 맞게 지어야 이해하기 쉽다는 점을 명심하자. p91

이 패키지로 무얼 할 수 있는지를 바로 이해할 수 있는 네이밍이 필요하다.

  1. 익스포트 내용의 문서화

익스포트한 요소는 모두 문서화한다. p94

IDE로 익스포트한 요소에 가져다 대면 이 요소의 목적을 볼 수 있어야 한다.

목적은 요소의 위에 적어두고, 내용은 옆에 코멘트해준다.

3장. 데이터 타입

  1. 인터페이스 nil pointer

인터페이스는 타입과 값, 두가지 정보를 가지고 있는데 둘 다 nil이어야만 인터페이스가 nil이다.

예제 링크를 남겨둔다.

  1. 맵은 커질 수는 있지만 줄어들 수는 없다. p145

이건 몰랐다. 관리하는 두 가지 방법은 1) 주기적 복제 만들기 2) 포인터를 저장하도록 하기

  1. replect.DeepEqual은 == 연산자보다 100배 느리다. p150

성능에 민감하다면 직접 비교함수를 구현하자. 활용할 수 있다면 표준 라이브러리에서 제공하는 몇몇 비교 메서드를 사용하자.

4장. 제어 구문

언급할 만한 내용 없음

5장. 스트링

  1. TrimRight/TrimLeft 와 TrimSuffix/TrimPrefix 를 구분하라. p 186

예제코드를 공유해둔다.

  1. 결합할 문자열이 5개 이상이라면 strings.Builder를 사용하라. p190

문자열의 불변 속성 때문에 발생하는 메모리 복제를 최소화 하지만, 5개 미만의 문자열 결합에 사용하면 지나치게 복잡해진다.

  1. 매우 긴 문자열의 일부만 사용할 때에는 깊은 복제를 수행하라. p194

원본의 긴 문자열 배열이 메모리 공간을 차지하는 걸 방지해야 한다.

6장. 함수와 메서드

  1. 값 리시버와 포인터 리시버 p202

기본은 값 리시버, 특수한 상황에서는 포인터 리시버, 잘 모르겠으면 포인터 리시버를 사용하자. 상세 내용은 책을 참고

  1. nil 리시버 리턴 p207

인터페이스를 리턴하는 경우에는 nil 인 경우는 명시적으로 nil을 리턴하자. 상세 내용은 책을 참고

  1. 함수의 입력은 특정 타입이 아닌 인터페이스로 받자. p211

2장에서 언급했던 포스텔의 법칙을 기억하자. 받을 때는 관대하게 인터페이스만 만족하면 다 받는 것이다.

7장. 에러 관리

  1. Expected/Unexpected Error p234
    • errors.Is: Expected error는 프로그래밍을 하면서 발생할 수 있다고 예상하는 에러이다. sql.ErrNoRows, io.EOF가 대표적이다. 이런 경우는 sentinal error로 정의한 다음 errors.Is로 검사한다.
    • errors.As: Unexpected Error는 예상하지 못한 에러를 의미한다. 예상하지 못한 에러는 에러 타입을 errors.As로 검사하여 처리한다.
  2. 때로는 의도적으로 에러를 무시한다. p239

이때는 의도적으로 무시한다는 것을 언더바(_)로 명시적으로 보여주자. 위에 주석으로 무시하는 이유를 달아두면 더욱 좋다.

8장. 동시성: 기본 개념

  1. 채널과 뮤텍스 p260

병렬 고루틴 끼리의 동기화는 순서가 없으며 뮤텍스를 사용한다.

순서를 조율해야 하는 경우에는 채널을 사용한다.

  1. 컨텍스트의 키 p281

컨텍스트 키를 처리하는 바람직한 방법은 익스포트하지 않은 커스텀 타입을 생성하는 것이다.

우연히라도 다른 곳에서 동일한 키를 사용하지 않도록 하는 것이다.

9장. 동시성: 응용

  1. 멈출 계획 없이 고루틴을 시작하지 마라. p291

고루틴을 시작하는 주체가 멈추는 것을 책임지도록 하자.

  1. 알림의 의도만을 가진 채널은 빈 구조체인 struct{}{} 를 보내자

이건 일종의 de facto이다. 특정한 값을 전달하는 것이 아닌 알림의 의도만을 가진다는, 사실상의 표준인 것이다.

  1. 채널과 버퍼 p313
  • 동기화 하려면 버퍼를 사용하지 않는다.
  • 버퍼를 사용해야 한다면 일단 크기는 1로 한다.
  • 1보다 큰 버퍼를 사용하려면 엄격한 절차를 통해 합리적 근거를 가지고 결정해야 한다. 그리고 근거를 주석으로 남긴다.
  • 버퍼가 있으면 데드락을 발견하기 어렵다.

10장. 표준 라이브러리

  1. time.After로 생성된 리소스의 해제 시점 p348

일반적으로 time.After를 사용할 때는 주의해야 한다. 생성된 리소스는 타이머가 만료되어야만 해제된다는 사실을 명심하자.

따라서, 원하는 시점에 빠르게 리소스를 해제, 리셋하려면 time.After가 감싸고 있는 time.Timer를 직접 사용하자.

  1. 타입 임베딩에 의한 이상 동작 p349

신기하고 재미난 현상이었다. 임베딩한 필드의 메서드가 promotion 되면서, 생각지도 못하게 구조체 자체가 특정 인터페이스를 만족하는 경우이다. 예를 들어, 임베딩한 필드에 String() 메서드가 구현되어 있다면 그 구조체를 fmt.Println() 해주면 해당 메서드가 실행된다.

  1. 벽시계와 단조시계 p352

이 개념 자체를 몰랐다.

  • 벽시계는 현재 시각을 결정하는데 NTP(Network Time Protocol)로 동기화 하다보면 값이 순간적으로 변경될 수 있다. 즉, 08:02:00 AM 이라고 생각하고 있는데 최신의 정확한 시간을 동기화 하는 순간 08:01:55 AM이 되어버리는 것이다. 시간이 거꾸로 간다.
  • 단조시계는 시간이 항상 한 방향으로 흐르는 것을 보장한다.

json 마살링, 언마샬링 과정에서 단조시계 부분이 삭제된다는 것을 명심하자.

  1. sql.Open은 데이터베이스 연결을 보장하지 않는다. p357

이 부분도 신선했다. 실제 연결이 아닌 주어진 인수에 대한 검사만 한다고 한다.

따라서, 간혹 보며 돌다리도 두드리는 코드라 생각했던 db.Ping() 함수를 통해 연결을 확실히 확인해야 하는 것이다.

  1. 데이터베이스 연결 풀의 네 가지 매개변수 p359
  • SetMaxOpenConns: 최대로 열린 연결의 개수. 과부하 방지.
  • SetMaxIdleConns: 최대 대기 연결 수. 리소스 사용의 최적화.
  • SetConnMaxIdelTime: 대기연결의 최대 대기 시간. 리소스의 효율적 관리.
  • SetConnMaxLifetime: 하나의 연결이 살아있을 수 있는 최대 시간. 휴대폰이나 노트북을 한 번씩 재부팅해주는 것을 생각하면 된다. 안정성 향상.

라는 네 가지 매개변수가 있다는 것을 알고 실제 상황에 맞게 적절히 조절하는 것이 필요하다. 관련 ChatGPT 문답을 참고하자.

  1. 서버의 최소한의 timeout p378

엔드포인트를 알 수 없는 클라이언트에게 공개할 때 가장 좋은 방법은 적어도 http.Server.ReadHeaderTimeout 필드를 설정하고, http.TimeoutHandler 래퍼 함수를 사용하는 것이다.

예측할 수도, 통제할 수도 없는 네트워크를 넘나드는 애플리케이션에서는 타임아웃이 매우 중요하다.

11장. 테스팅

  1. 레이스 디텍터는 신뢰할 수 없다. 거짓 양성, 거짓 음성이 가능하다. p389
  • 테스트 코드 수준을 높여야 한다.
  • data race 테스트 로직을 루프로 돌려서 거짓 음성을 보완한다.
  1. 읽기 쓰기 테스트에는 iotest 패키지를 사용하자. p412

이런 패키지가 있다는 것 자체를 몰랐다.

12장. 최적화

  1. 기계 공감 - Mechanical sympathy p436

12장의 대부분은 기계 공감과 관련이 있다. 애플리케이션은 결국 어느 지점에서는 물리적인 하드웨어에서 돌아가게 된다. 실제 기계가 돌아가는 원리와 상황을 이해하고 공감하면 더욱 좋은 프로그래밍을 할 수 있다.

  1. CPU 캐시라인의 크리티컬 스트라이드 문제, CPU의 ILP(인스트럭션 레벨 병렬)

책의 마지막 부분에 다다르니 쉽지만은 않은 내용이 나온다. 이 때에 포기하지 않고 두 번, 세 번 읽으니 눈에 들어오기 시작했다.

마무리

다시 한 번 좋은 책을 만나 기쁘다. 정리하는 시점에 벌써 까먹은 부분도 많겠지만 언제고 실무에서 사용하거나 우연히 다른 장소에서 다시 만나 기억을 되새기고 완벽히 내 것으로 만들 것이다. 혹은 개정판으로 거듭나 새로이 만날지도 모르겠다.

반응형
반응형
잡학툰 뱃지
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/04   »
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
글 보관함