Skip to content

Commit

Permalink
release: v1.0.9
Browse files Browse the repository at this point in the history
## Description

+ 도서 디렉토리 구조 변경
+ 도서 리뷰 추가
+ 학습내용 추가

## Related Issues

partly resolved #54
resolved #60
  • Loading branch information
s3ich4n authored Feb 15, 2023
2 parents d9c3186 + da1cbda commit 345acc2
Show file tree
Hide file tree
Showing 40 changed files with 3,682 additions and 1,185 deletions.
90 changes: 90 additions & 0 deletions content/books/clean-code/2023-01-06---chapter01/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
---
title: "클린 코드 스터디 (1): 깨끗한 코드"
date: "2023-01-06T22:51:00.000Z"
template: "post"
draft: false
slug: "/books/clean-code/2023-01-06-pt01"
category: "devlog"
tags:
- "book_review"
- "code_quality"
description: "2023년 1월부터 시작한 클린 코드 독파 스터디 후, 매 모임 전 준비하는 게시글을 공유합니다. 이 글은 1장, 깨끗한 코드에 대해 알아봅니다."
socialImage: { "publicURL": "./media/water.jpg" }
---

# 1. 깨끗한 코드

ChatGPT로 기술 발전이 무섭게 발전하는 지금, 기계가 못하는 것을 할 수 있도록 하기 위해 이 책을 폈습니다. 우리는 기계보다 나은 코드를 짜도록 연마해야할 것입니다.

코드는 요구사항을 표현하기 위한 도구이며, 각 요구사항을 표현하기 위한 언어 또한 늘어나겠지요. 막연한 요구사항을 구체화하고, 이를 잘 풀어내는 것은 (아직은) 인간이 할 수 있는 것입니다. 그런고로, 잘 풀어내는 방법을 배워봅시다.

나쁜코드가 왜 좋지 않은지 이야기해봅시다. 이 글을 읽으시는 모든 분들은 '나쁜 코드를 어떻게 좋게 바꾸지?' 하고 고민하던 때가 있었을 것입니다.

1. 급하다고 막 짠 코드로, '이거 돈다!' 하는게 쌓이면 돌이킬 수 없습니다. 나중에 하자? **나중은 없습니다.** 실용주의 프로그래머에도 이런 말이 나옵니다: '깨진 유리창을 가만 두지 마라'.

2. 이런 나쁜 코드가 쌓이면 팀 생산성이 떨어집니다.

3. 설령 기회를 얻어, 나쁜 코드를 고칠 기회가 오더라도 모두 고치기는 쉽지 않을 것입니다.

4. 나쁜 코드를 유도하는 **나쁜 설계**를 유도하지 않도록 해야합니다. 좋은 설계에서 좋은 코드가 나올 수 있고, 좋은 코드를 유지하는 것 또한 좋은 설계의 일환입니다.

그런 의미로 코드를 잘 짜는 예술(Art)이 있다는 말에 어느정도 동의합니다. 아름다운 코드는 어떤 *감각*이라고 합니다. 아니 코드에 무슨 미학이 있는것도 아니고? 하는 생각이 들었는데, 수퍼스타들의 말을 읽어보니 납득이 됐습니다.

## 이 바닥 슈퍼스타들은...

1. Bjarne Stroustrup, (C++의 아버지)
1. 우아하고 효율적인 코드
2. 의존성을 줄이라
3. 오류를 전략적으로 처리하라
4. 성능은 최적으로. 그렇다고 원칙없이 잘 돌아가는 코드를 짜면 안됨
5. 하나의 코드는 하나의 작동을 한다
2. [Grady Booch](https://zetawiki.com/wiki/%EA%B7%B8%EB%9E%98%EB%94%94_%EB%B6%80%EC%B9%98) (Object Oriented Analysis and Design with Application](https://product.kyobobook.co.kr/detail/S000006439884)[1]의 저자)
1. 단순하고 직접적이다.
2. 잘 쓴 문장처럼 읽힌다.
3. 설계자의 의도가 바로 드러난다.
4. 명쾌한 추상화와 제어문으로 가득하다.
3. Dave A. Thomas(aka. "Big" Dave Thomas) (OTI의 창립자이자 이클립스 전략의 Godfather)
1. 안 짠 사람도 읽기 쉽고 고치기 쉽다.
2. 유닛 테스트부터 인수 테스트까지 다 있다.
3. 의미 있는 이름이 붙는다.
4. 코드를 통해 목적을 달성하는 방법은 명확한 하나만 제공된다.
5. API는 concise하다.
6. 어떤 면에서는 문학적이다. 모든 정보를 코드로 풀 수 없기 때문이다. → 사람이 읽기 쉬운 코드라는 뜻
4. Michale Feathers ([Working Effectively with Legacy Code](https://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052)[2] 의 저자)
1. 손댈 곳이 없어보이는 코드를 짜자
2. 주의깊게 보이는 코드
5. Ron Jeffries (Extreme Programming Installed, Extreme Progreaaming Adventure in C#의 저자)
1. 모든 테스트를 통과한다
2. 중복코드가 없다
3. 시스템 내 모든 설계 아이디어를 표현한다
4. 클래스, 메소드, 함수를 최소한으로 한다
6. Ward Cunningham (위키의 창시자, 익스트림 프로그래밍의 공동 창시자, OO의 정신적 지주)
1. 루틴대로 도는 코드 → 의도가 명확한 코드
2. 문제를 풀기위해 보이는 코드
7. Robert C. Martin (aka. 밥 아저씨) (이 책의 저자)
1. 앞으로의 내용은 책을 보면 알 것
1. 절대적인 것은 없으나, 상황에 맞는 기술과 기법을 익히길 바람

## 코드작성에 대한 태도

- Javadoc에는 `@author` 필드가 있습니다. 우리는 수차례 코드를 읽고 씁니다. 좋은 글을 쓰기 위한 작가로서의 책임감을 가질 필요가 있습니다.
- 보이스카우트 규칙을 기억하세요.
- _'캠프장은 처음 왔을 때보다 더 깨끗하게 하고 나갈 것.'_
- 이는 '처음 왔을 때보다 *더 나은 세상*을 만들고 떠나려 노력하라. (후략)' 라는 말에서 나왔다네요. 낭만이 있습니다...

# 논외로

- [클린 코드 같은 건 없다!](https://www.steveonstuff.com/2022/01/27/no-such-thing-as-clean-code) 하는 당찬 제목이 있던데, 읽어보면 이 말입니다.

- 업계마다 원하는게 다르고, 그에 따라 간결한 코드를 짜는 방법은 _모두에게 다르게 적용됩니다_.
- 때에 맞는 기술을 잘 선택해야 _깨끗하다_ 할 수 있습니다.

- 원작자의 마지막 멘트로 이 글을 마무리합니다.

> Hopefully I can convince you that you don’t really need clean code, you need `_____` code. It’s up to you to fill in that blank with words that describe what your project requires.
---

[1]: [이 링크](https://soniacomp.medium.com/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%EC%A0%81-%EB%B6%84%EC%84%9D%EA%B3%BC-%EB%94%94%EC%9E%90%EC%9D%B8-object-oriented-analysis-and-design-%EC%86%8C%EA%B0%9C-part-1-%EB%B2%88%EC%97%AD-67ff58fd26c9)를 참고하십시오. UML을 만든 사람 중 하나이며, 객체간의 메시지 교환, 책임, 협업과 같은 요소가 있어서 객체지향의 사실과 오해, 오브젝트에서도 언급이 되었을 것입니다.

[2]: 이 링크를 참조하십시오.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
191 changes: 191 additions & 0 deletions content/books/clean-code/2023-01-06---chapter02/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
---
title: "클린 코드 스터디 (2): 의미있는 코드"
date: "2023-01-06T22:59:00.000Z"
template: "post"
draft: false
slug: "/books/clean-code/2023-01-06-pt01"
category: "devlog"
tags:
- "book_review"
- "code_quality"
description: "2023년 1월부터 시작한 클린 코드 독파 스터디 후, 매 모임 전 준비하는 게시글을 공유합니다. 이 글은 2장, 의미있는 코드에 대해 설명합니다."
socialImage: { "publicURL": "./media/water.jpg" }
---

# 2. 의미있는 코드

의미있는 코드 이름을 붙입시다. 소프트웨어 세상 만사에 다 쓰이는게 이름이잖아요. 그러면 잘 지어봅시다.

## 의도가 분명한 코드

변수, 메소드, 에서 의미가 드러나도록 생각해봅시다. 코드 맥락에 따라, 필요한 내용이 담겨있도록 하는 이름을 유도해봅시다.

### 저의 생각 (1)

인덱스용 `i`, `j` 도 가급적이면 `idx` 처럼 써서, `foreach` 형식의 구문에서도 이해할 수 있게 만들어야할 것입니다.

### 저의 생각 (2)

파이썬에선 매개변수 사용 시, 쓰도록 하고싶을 때, 아래와 같이 사용할 수 있습니다([PEP 3102](https://peps.python.org/pep-3102/)).

사용방법은 아래와 같습니다:

```python
Python 3.7.12 | packaged by conda-forge | (default, Oct 26 2021, 06:08:21)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.34.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: def test1(
...: body: dict,
...: *,
...: testarg: bool # 쓸 거면 explicit 하게 호출하기 (PEP 3102)
...: ):
...: print(body)
...: print(testarg)
...:

In [2]: test1({"b": 1}, True)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-2-b0d6e60260e3> in <module>
----> 1 test1({"b": 1}, True)

TypeError: test1() takes 1 positional argument but 2 were given

In [3]: test1({"b": 1}, testarg=True)
{'b': 1}
True

In [4]: test1({"b": 1}, {"testarg": True})
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-4-3f3364c4dbeb> in <module>
----> 1 test1({"b": 1}, {"testarg": True})

TypeError: test1() takes 1 positional argument but 2 were given

In [5]: test1({"b": 1}, **{"testarg": True})
{'b': 1}
True

In [6]: test1({"b": 1}, **{"sdafkjnerfg": True})
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-6-b65affa75eff> in <module>
----> 1 test1({"b": 1}, **{"sdafkjnerfg": True})

TypeError: test1() got an unexpected keyword argument 'sdafkjnerfg'

```

## 그릇된 정보를 피하라

- 프로그래밍 업계 전반에서 널리 사용되는 용어, 개발중인 도메인에서 사용하고 있는 용어와 유사한 이름을 다른 뜻으로 사용하지 맙시다.
- 유사한 개념은 유사한 표기법을 사용합시다.

## 의미 있게 구분하라

아래 코드를 살펴봅시다.

```python
def swap_coord(
self,
q1: Coord,
q2: Coord,
) -> None:
""" 좌표계 Coord을 swap하는 코드다
"""
q1, q2 = q2, q1
```

뜻은 알겠습니다만, `src`, `dst` 라고 작성하는게 보다 "명확"합니다. 그래도 나은코드를 만들려면? 하는 고민이 필요한 시기라고 할 수 있겠지요.

예를들어 어떤 에러의 Traceback을 보는데, 이런식으로 나왔다고 가정해봅시다.

```python
---------------------------------------------------------------------------
Exception Traceback (most recent call last)
<ipython-input-10-9732aa3334c7> in <module>
----> 1 get_active_account()

<ipython-input-7-4703b8c01446> in get_active_account()
1 def get_active_account():
----> 2 get_active_accounts()
3

<ipython-input-8-c779c718bd0e> in get_active_accounts()
1 def get_active_accounts():
----> 2 get_active_accounts_info()
3

<ipython-input-9-154e7846498a> in get_active_accounts_info()
1 def get_active_accounts_info():
----> 2 raise Exception("!")
```

기능을 쫓아갈 때도 보다 "명확한" 이름을 써야, 추후 디버깅하고 기능을 추가할 때도 보다 쫓아가기 쉽겠지요.

## 발음하기 쉬운 이름을 쓰라

한국에서 영어는 제2외국어니까 발음은 사실 그리 문제되지 않는다고 생각합니다. 다만 도메인을 풀 때 *공통적인 단어*에 대한 논의는 필요하다고 생각합니다. 예를 들어, "구분"이라는 단어를 `gubun` 으로 공통적으로 사용할 수도 있을 것입니다. 다같이 프로그래밍을 하는것이니까요.

## 검색하기 쉬운 이름을 사용하라

수업 당 학생 수를 표기하기 위해 `7` 을 상수로 바로 쓰기보단 `MAX_CLASSES_PER_STUDENT` 같은 이름을 붙여서 쓰는편이 좋을 것입니다.

## 인코딩을 피하라

Win32 API 프로그래밍에서 흔히 쓰이던 [헝가리식 표기법](https://en.wikipedia.org/wiki/Hungarian_notation)이 특히 그랬지요. 요즘 강타입 언어들은 더 많은 타입을 지원하고, 컴파일 레벨에서도 이런 문제들을 잡을 수 있고, 클래스와 함수가 점차 작아지는 추세입니다. 파이썬이라도 `mypy` 나 적극적인 타입힌팅을 두는 식으로도 어느정도 대응은 되지요. 그리고 IDE 단에서도 타입에러를 감지합니다. 때에따라 쓸 수 있겠지만, 불필요하겠지요.

### 인터페이스와 구현 클래스?

팩토리 클래스와 구현체 클래스의 이름을 정한다면, 팩토리쪽을 추상화한 이름을 짓는편은 어떨까요? 구현체의 이름에 `Impl` 이라거나 `I` 접두사를 붙이거나 하기보단 어차피 어느 부모를 상속한건지 IDE로도 쫓아갈 수 있으니까요.

## 자신의 기억력을 자랑하지 마라

나만 아는 이름을 하면 남과 함께 일하기 어려우니 자제해야 합니다. 남과 함께 일하기 좋은 변수명을 짓는 방안으로는 어떤게 있을까요?

### 클래스 이름

동사 쓰지말고 명사/명사구로 씁시다.

### 메소드 이름

동사/동사구로 씁시다. getter나 setter, 그외 요소들은 사용하는 언어의 특징을 따릅시다.

### 특이한 이름은 지양할 것

그 당시에만 웃겨서 다시보면 노잼일거에요...

### 한 개념에 한 단어만

추상적 개념 하나에 단어 하나를 선택하고,이를 고수합시다. 일관성있게 유지해서 충분히 유추할 수 있는 코드를 짭시다.

1. 동일한 결과를 기대하는 메소드라면 단일 이름을 씁시다. 클래스마다 `fetch`, `retreive`, `get` 과 같이 **각각 다르게**쓰면 다른 코드를 쫓아가기 어렵습니다.
2. 동일 개념이라면 동일한 이름을 씁시다. 동일 코드 기반에 `controller`, `manager`, `driver` 등을 **섞어 사용하면** 다른 코드를 이해하기 어렵습니다.

## 말장난을 하지 마라

한 단어는 하나의 의미만을 가집시다. 더한다(add) 라는 개념을 확장하고 싶다면 insert나 append 같은 단어를 사용합시다. 코드를 훑어보더라도 이해하기 쉽도록 합시다.

## 해법 영역에서 가져온 이름을 사용하라

**모든** 이름을 도메인에서 따지는 말고, 기술이 주요한 개념이라면 기술이름을 응용하여 명명합시다. VISITOR 패턴을 썼다면 `AccountVisitor` 와 같은 이름을 쓰는것은 어떨까요?

## 문제 영역에서 가져온 이름을 사용하라

그게 아니라면 어지간한 이름은 도메인에서 따옵시다. 그러면 새로 오는 사람이더라도 관련 파트 사람에게 개념을 질문하여 빠른 해답을 끌어올 수 있을 것입니다.

## 의미 있는 맥락을 추가하라

클래스, 메소드, 변수를 통해 로직의 컨텍스트를 유지하다보니, 메소드 하나에 너무 많은 개념이 들어가있나요? 도메인을 풀기 위한 주요 개념이라면 이를 객체화 해봅시다.

객체끼리 메시지를 주고받을 수 있도록 하고 로직을 작게 분리하는 해결책은 어떨까요?

## 불필요한 맥락을 없애라

너무 짧은 이름보다는 긴 이름이 좋습니다만, 의미가 분명한 이름이라면 짧게 가져가서 불필요한 맥락을 빼버립시다.

## 마치면서

좋은 이름은 기억에 오래 남고, 비슷한 이름은 익숙해지기 쉽습니다. 이런 노력을 기울여서 이름에 대한 프로그래머의 생각비율을 최소화 합시다.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 345acc2

Please sign in to comment.