Codong's Development Diary RSS 태그 관리 글쓰기 방명록
독서 (3)
2022-01-12 16:57:54

개요


소프트웨어 개발 일을 시작한 지 1년이 다 되어가는 시점이다. 처음에는 기능 구현만 한다면 끝인 줄 알았는데, 협업을 하거나 하루가 다르게 변하는 요구사항에 의해 코드의 수정이 많다 보니 불편함을 느낄 수밖에 없었다. 가령, 예전에 작성한 코드를 수정해야 할 때 처음 본 것 마냥 새롭게 느껴지고, 하나를 수정하면 에러가 나서 타고 타고 들어가서 하나씩 전부 수정하는 고생을 했다. 그러다 문득 '어떻게 하면 이런 고생을 안 하도록 코드를 잘 짤 수 있을까?' 라는 생각이 들었다.

제일 먼저 떠오른게 '클린 코드'였다.

하지만 클린 코드는 '군더더기 없이 깔끔하여 읽기 쉽고, 유지/보수하기 쉬운 코드다' 라고만 막연하게 알고 있지, 구체적으로 어떤 식으로 작성하는지에 대해 알고 있지 않았다. 그래서 이 책을 통해 내가 코드를 짤 때 무지성으로 짜기보다, 앞으로는 좀 더 생각하여 클린한 코드를 짤 수 있기를 기대하며 읽었다.



본문


이 책은 총 10개의 챕터로 구성되어있다. 책의 모든 내용을 다 담기에는 양이 많을 수 있으니, 내가 기억에 남는 부분만 요약해둘 예정이다.


1️⃣ 소개. 코드 포매팅과 도구


클린코드의 의미와 중요성

  • 이 책에서는 '프로그래밍 언어의 진정한 의미는 아이디어를 다른 개발자에게 전달하는 것이다' 라고 말했다. 또한 여기에 클린 코드의 진정한 본질이 있으며, 클린 코드인지 아닌지는 다른 엔지니어가 코드를 읽고 유지 관리할 수 있는지 여부에 달려 있다고 말한다.
  • 중요한 이유는 내가 느꼈던 것과 같이 대부분은 유지보수성 향상, 기술 부채의 감소, 애자일 개발을 통한 효과적인 작업 진행, 성공적인 프로젝트 관리로 이어진다는 등등 굉장히 많다.

Docstring과 annotation

  • Docstring은 comment가 아니라 문서이다. 코드의 특정 컴포넌트에 대한 문서화이다.
  • 코드에 주석(comment)을 다는 것은 나쁜 습관이다. 첫째. 주석은 코드로 아이디어를 제대로 표현하지 못했음을 나타내는 것이다. 둘째. 오해의 소지가 있다. 주석 업데이트를 깜빡하는 경우, 코드와 주석의 내용이 다를 수 있다.

2️⃣ 파이썬스러운(pythonic) 코드


✔️ 파이썬에서의 밑줄

public, private, protected 프로퍼티를 가지는 다른 언어들과 다른게 파이썬 객체의 모든 프로퍼티와 함수는 public이다. 즉 호출자가 객체의 속성을 호출하지 못하도록 할 방법이 없다. 엄격한 강제사항은 없지만 몇 가지 규칙이 있다. 밑줄로 시작하는 속성은 해당 객체에 대해 private을 의미하며, 외부에서 호출하지 않기를 기대하는 것이다. 기대할 뿐이지 금지하는 것은 아니다.

그러나 일부 속성과 메서드를 이중 밑줄을 이용해 private으로 만들 수 있다는 오해가 있다. 밑줄 두 개를 사용하면 실제로 파이썬은 다른 이름을 만든다. 이를 이름 맹글링(name mangling)이라 한다. 이것이 하는 일은 다음과 같은 이름의 속성을 만드는 것이다.

_<class-name>__<attribute-name>

Connector 라는 클래스의 __timeout 이라는 속성을 만들었다고 가정하자. 그러면 '_Connector__timeout' 이라는 속성이 만들어지며 저 이름으로 속성에 접근이 가능해진다. 파이썬에서 이중 밑줄을 사용하는 것은 완전히 다른 경우이다. 여러 번 확장되는 클래스의 메서드를 이름 충돌 없이 오버라이드 하기 위해 만들어졌다.

결과적으로 이중 밑줄은 파이썬스러운 코드가 아니다. 속성을 private으로 정의하려는 경우 하나의 밑줄을 사용하는 파이썬스러운 관습을 지키도록 하자.

✔️ 컨테이너 객체

컨테이너는 __contains__ 메서드를 구현한 객체로 __contains__ 메서드는 일반적으로 Boolean 값을 반환한다. 이 메서드는 파이썬에서 in 키워드가 발견될 때 호출된다. 예제를 통해 이 메서드를 잘 사용하면 얻을 수 있는 효과를 살펴보자.

2차원 게임 지도에서 특정 위치에 표시를 해야 한다고 생각해보자. 보통 이런 함수를 생각할 수 있다.

def mark_coordinate(grid, coord):
    if 0 <= coord.x < grid.width and 0 <= coord.y < grid.height:
        grid[coord] = MARKED

if 문이 상당히 난해하고 의도가 무엇인지 쉽게 판단하기 어려워 보인다. 그러면 어떻게 바꿀 수 있을까? 지도에서 자체적으로 grid라 부르는 영역을 판단해주면 어떨까? 그리고 이 일을 더 작은 객체에 위임하면 어떨까? (위임을 통해 응집력도 높아진다.) 이렇게 하면 지도에게 특정 좌표가 포함되어 있는지만 물어보면 된다.

class Boundaries:
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def __contains__(self, coord):
        x, y = coord
        return 0 <= x < self.width and 0 <= y < self.height

class Grid:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.limits = Boundaries(width, height)

    def __contains__(self, coord):
        return coord in self.limits

이렇게 구성이 간단하고 위임을 통해 문제를 해결한다. 두 객체 모두 최소한의 논리를 사용했고, 매서드는 짧고 응집력이 있다. 또한 외부에서도 다음과 같이 사용 가능하다.

def mark_coordinate(grid, coord):
    if coord in grid:
        grid[coord] = MARKED

✔️ 파이썬에서 유의할 점

  1. 변경 가능한(mutable) 파라미터의 기본 값
def wrong_user_display(user_metadata: dict = {"name":"john","age":30}):
name = user_metadata.pop("name")
age = user_metadata.pop("age")
return f"{name} ({age})"

이렇게 파라미터의 기본값을 변경 가능한 값으로 준다면 처음 호출할 때만 동작한다. 그렇기 때문에 다음과 같은 문제가 일어난다.

>>> user_metadata() # 'john (30)'
>>> user_metadata({"name":"jane", "age":25}) # 'jane (25)'
>>> user_metadata() # KeyError: "name"

처음 기본 값인 딕셔너리가 만들어지고, 요소들은 pop으로 삭제되었다. 그렇기 떄문에 다시 기본값으로 호출되었을 때 빈 딕셔너리기 때문에 KeyError가 발생한 것이다.

그렇기에 초기값으로 None을 사용하고 함수 본문에서 기본 값을 할당하면 된다. 각 함수는 자체 스코프와 생명주기를 가지기 때문이다.

  1. 내장(built-in) 타입 확장

리스트, 문자열, 사전과 내장 타입을 확장하는 올바른 방법은 collection 모듈을 사용하는 것이다. 예를 들어 dict를 직접 확장하는 클래스를 만들면 예상하지 못한 결과를 얻을 수 있다. 그 이유는 Cpython에서는 클래스의 메서드를 서로 호출하지 않기 때문에 메서드 중에 하나를 오버라이드하면 나머지에는 반영이 되지 않아서 예기치 않은 결과가 발생한다. ex) __getitem__을 오버라이드하고 for 루프를 사용해 객체를 반복하려고 하면 해당 로직이 적용되지 않는 것을 알게 된다. collection.UserDict를 사용해서 문제를 해결할 수 있다.

...

✍️ 장을 마무리 하며

이 장을 읽으면서 파이썬을 그래도 조금 알고 있다고 생각했는데, 구동 원리까진 생각을 안 해봤었다. with, in 키워드가 어떻게 작동하는지, iterable 객체가 어떻게 요소들을 반환하는지 알게 되었다. 신기하면서도 어렵다고 생각이 들었다. 알면 알수록 더 모르는 것 투성이니까...😂

하지만 파이썬의 매직 메서드들을 잘 이용하면 파이썬스럽게 코드를 짤 수 있다! 이번에 배운 것들을 잘 이용할 수 있도록 노력하자!


3️⃣ 좋은 코드의 일반적인 특징


사용자에게 API를 제공할 때 코드가 정상적으로 동작하기 위해 기대하는 것과 호출자가 반환받기를 기대하는 것은 디자인의 하나가 되어야 한다. 여기서 계약(contract)이라는 개념이 생긴다. 계약에 의한 디자인(Design by Contract)이란 관계자가 기대하는 바를 암묵적으로 코드에 삽입하는 대신 양측이 동의하는 계약을 먼저 한 다음, 계약을 어겼을 경우는 명시적으로 왜 계속할 수 없는지 예외를 발생시키라는 것이다.

이 책에서 말하는 계약은 소프트웨어 컴포넌트 간의 통신 중에 반드시 지켜져야 할 몇 가지 규칙을 강제하는 것이다. 주로 사전 조건, 사후 조건을 명시하지만 때로는 불변식과 부작용을 기술한다.

이렇게 하는 이유는 오류가 발생할 때 쉽게 찾아낼 수 있고, 잘못된 가정 하에 코드의 핵심 부분이 실행되는 것을 방지하기 위해서이다. 이것은 에러를 발생시키는데서 그치는 것이 아니라 책임의 한계를 명확히 하는데 도움 된다. 사전 조건 실패 -> 클라이언트 결함 / 사후 조건 실패 -> 컴포넌트 결함.

  1. 사전 조건(Precondition) : 함수가 자체적으로 로직을 실행하기 전에 검사하도록 까다로운(demanding) 접근 방법을 일반적으로 사용한다. DRY원칙을 잊지 말자. 검증을 양쪽에서 하지 말고 함수에만 두던, 클라이언트에만 두던 오직 어느 한쪽에서만 해야 한다.
  2. 사후 조건(Postcondition) : 사전 조건이 맞는다면 사후 조건은 특정 속성이 보존되도록 보장해야 한다.

이를 적용할 때 사전 조건에 대한 검사와 사후 조건에 대한 검사 그리고 핵심 기능에 대한 구현을 구분하는 것이 좋을 것이다. 이것을 파이썬스럽게 데코레이터를 사용하는 것이 흥미로운 대안이 될 수 있다.

이런 디자인 원칙의 주된 가치는 문제가 있는 부분을 효과적으로 식별하는 데 있다.

✔️ 에러 핸들링

프로그램에서 에러를 처리하는 방법에는 여러 가지가 있지만 모든 것을 처리할 수 있는 것은 아니다. 에러 처리 방법의 일부를 살펴보자.

  1. 값 대체 (substitution) : 일반적으로 기본 값으로 바꾸어도 큰 문제가 없지만 오류가 있는 데이터를 유사한 값으로 대체하는 것은 더 위험하며 일부 오류를 숨겨버릴 수 있기에 이러한 기준을 잘 고려해야 한다.
  2. 에러 로깅
  3. 예외 처리 : 예외는 오직 한 가지 일을 하는 함수의 한 부분이어야 한다. 또한 캡슐화를 약화시키기 때문에 신중하게 사용해야 한다. 마지막으로 traceback이 노출되면 안 된다.

파이썬의 예외와 관련된 몇 가지 권장 사항에 대해 알아보자.

  • 비어있는 except 블록 지양
    • 이것의 문제는 결코 실패하지 않는다는 것이다. 심지어 실패해야만 할 때조차도. 대안으론 보다 구체적인 예외를 사용하거나 실제 오류 처리를 진행한다.
  • 원본 예외 포함
    • 오류 처리 과정에서 다른 오류를 발생시키고 메시지를 변경할 수도 있다. raise <e> form <original_exception> 구문을 사용하면 된다.
  • 어설션 사용하기
    • 절대로 일어나지 않아야 하는 상황에 사용되므로 assert 문에 사용된 표현식은 불가능한 조건을 의미한다. 즉, 잘못된 가정 하에 처리를 계속하기보다는 프로그램을 중단시키는 것이 좋을 때 사용한다. 어설션에 실패하면 반드시 프로그램을 종료시켜야 한다.

✔️ 관심사의 분리

이것은 여러 수준에서 적용되는 설계 원칙이다. 책임이 다르면 컴포넌트, 계층 또는 모듈로 분리되어야 한다. 프로그램의 각 부분은 기능의 일부분(관심사)에 대해서만 책임을 지며 나머지 부분에 대해서는 알 필요가 없다. 이런 관심사 분리의 목표는 파급 효과를 최소화하여 유지보수성을 향상시키는 것이다.

  • 응집력(cohesion)과 결합력(coupling)

이것은 훌륭한 소프트웨어 설계를 위한 중요한 개념이다.

응집력이란 객체가 작고 잘 정의된 목적을 가져야 하며 가능하면 작아야 한다는 것을 의미한다. 유닉스 명령어가 한 가지 일만 잘 수행하려는 것과 비슷한 철학을 따른다. 응집력이 높을수록 더 유용하고 재사용성이 높아지므로 더 좋은 디자인이다.

결합력이란 두 개 이상의 객체가 서로 어떻게 의존하는지를 나타낸다. 객체 또는 메서드의 두 부분이 서로 너무 의존적이라면 낮은 재사용성, 파급효과, 낮은 수준의 추상화와 같은 바람직하지 않은 결과를 가져온다.

✔️ 개발 지침 약어

이 섹션에서는 좋은 디자인 아이디어를 주는 몇 가지 원칙을 검토한다. 요점은 좋은 소프트웨어 관행을 약어를 통해 쉽게 기억하자는 것이다.

1️ DRY(Do not Repeat Yourself) / OAOO(Once and Only Once)

중복은 반드시 피하라 / 코드에 있는 지식은 단 한번, 단 한 곳에 정의되어야 한다. 그렇지 않다는 것은 잘못된 시스템의 징조이다.

2️ YAGNI(You Ain't Gonna Need it)

디자인을 할 때 내린 결정으로 특별한 제약 없이 개발을 계속할 수 있다면, 굳이 필요 없는 추가 개발을 하지 말라는 것이다.

3️ KIS (Keep It Simple)

선택한 솔루션이 문제에 적합한 최소한의 솔루션인지 자문해보자. 단순한 것이 복잡한 것보다 낫다.

4️ EAFP(Easier to Ask Forgiveness than Permission) / LBYL(Look Before You Leap)

허락보다는 용서를 구하는 것이 낫다 / 도약하기 전에 살피라. EAFP는 일단 코드를 실행하고 실제 동작하지 않을 경우에 대응한다는 뜻이다. 일반적으로는 코드를 실행하고 발생한 예외를 catch 하여 except 블록에서 바로잡는 코드를 실행하게 된다. LBYL은 그 반대이다.

예를 들어 파일을 사용하기 전에 먼저 파일을 사용할 수 있는지 확인하라는 것이다.

if os.path.exists(filename):
    with open(filename) as f:
        ...

이것은 다른 프로그래밍 언어에서는 유용할 수 있지만 파이썬스러운 방식은 아니다. 파이썬은 EAFP 방식으로 만들어졌으며, 여러분도 그렇게 할 것을 권한다.(암묵적인 것보다 명시적인 것이 좋다는 것을 기억하자.) 즉 다음과 같이 작성할 수 있다.

try:
    with open(filename) as f:
        ...
except FileNotFoundError as e:
    logger.error(e)

✔️ 컴포지션(Composition)과 상속(Inheritance)

상속은 강력한 개념이지만 위험도 있다. 가장 주된 위험은 부모 클래스를 확장하여 새로운 클래스를 만들 때마다 부모와 강력하게 결합된 새로운 클래스가 생긴다는 점이다. 그럼 어떤 때에 상속을 사용하면 좋을까?

  • 상속이 좋은 선택인 경우

새로운 하위 클래스를 만들 때 상속된 모든 메서드를 실제로 사용할 것인지 생각해보는 것이 좋다. 만약 대부분의 메서드를 필요로 하지 않고 재정의하거나 대체해야 한다면 다음과 같은 이유로 설계상의 실수라고 할 수 있다.

  1. 상위 클래스는 잘 정의된 인터페이스 대신 막연한 정의너무 많은 책임을 가졌다.
  2. 하위 클래스는 확장하려고 하는 상위 클래스의 적절한 세분화가 아니다.

즉, 상속을 잘 사용한다는 것은 부모 클래스의 기능을 그대로 물려받으면서 추가 기능을 더하려는 경우 또는 특정 기능을 수정하려는 경우이다.

➕ 책에서 말하는 상속의 좋은 예 : https://docs.python.org/3/library/http.server.html#http.server.BaseHTTPRequestHandler

인터페이스 정의는 상속의 또 다른 좋은 예로, 어떤 객체에 인터페이스 방식을 강제하고자 할 때 구현을 하지 않은 기본 추상 클래스를 만들고, 실제 이 클래스를 상속하는 하위 클래스에서 적절한 구현을 하도록 하는 것이다.

상속을 올바르게 사용하면 객체를 전문화하고 기본 객체에서 출발하여 세부적인 추상화를 할 수 있다. 하지만 코드 재사용만을 목적으로 상속을 사용하려고 할 때 매우 자주 발생하는 문제는 부모 클래스와 성격이 다른 메서드를 추가할 때이다. 이런 상황을 피하기 위해 파이썬의 전형적인 안티 패턴은 데이터 구조 자체를 객체로 만드는 경우이다. 고객에게 정책을 적용하는 기능을 가진 보험관리 시스템을 예로 살펴보자.

# 잘못된 상속의 예
class TransactionPolicy(collections.UserDict):
    def change_in_policy(self, customer_id, **new_policy_data):
        self[customer_id].update(**new_policy_data)

이와 같이 구현을 하면 두 가지의 주요 문제점이 생긴다.

  1. 새 클래스를 만드는 것은 개념적으로 확장되고 세부적인 것이라는 것을 의미한다. 즉, 계층 구조가 잘못된 것이다. TransactionPolicy라는 이름만 보고 사전 타입이라는 것을 알 수 있을까?
  2. 결합의 문제가 생긴다. TransactionPolicy는 dictionary의 모든 메서드를 포함한다. pop(), items()가 필요할까? 사전 타입을 확장함으로써 얻은 이득도 별로 없다.

이것이 구현 객체를 도메인 객체와 혼합할 때 발생하는 문제이다. TransactionPolicy는 특정 도메인 정보를 나타내는 것이므로 해결하려는 문제의 일부분에 사용되는 엔티티여야만 한다. 그럼 어떻게 해결할까? 이럴 때 컴포지션을 사용하는 것이다.

즉, TransactionPolicy이 dictionary가 되는 것이 아니라 사전을 활용하는 것이다. 사전을 private 속성에 저장하고 __getitem__()으로 사전의 프록시를 만들고 나머지 필요한 public 메서드를 추가적으로 구현하는 것이다.

➕ 컴포지션(Composition) 이란 여러 객체를 합하여 다른 하나로 만드는 것을 말한다. 어떤 객체가 다른 객체의 일부분인 경우다. 자동차가 엔진, 트랜스미션, 헤드라이트 등으로 구성되는 것 같은 경우이다.

➕ 프록시(Proxy) 는 우리말로 대리자, 대변인이라는 뜻이다. 메서드를 호출하고 반환 값을 받는 것이 실제 클래스에서 처리하는지, 대리자 클래스의 메서드가 처리하는지 전혀 모르게 처리하는 것이다. 중요한 것은 흐름 제어만 할 뿐 결과값을 조작하거나 변경시키면 안 된다.

# 컴포지션 사용 리팩토링
class TransactionPolicy:
    def __init__(self, policy_data, **extra_data):
        self._data = {**policy_data, **extra_data} # dictionary 객체 composittion

    def change_in_policy(self, customer_id, **new_policy_data):
        self[customer_id].update(**new_policy_data)

    def __getitem__(self, customer_id):
        return self._data[customer_id]

    def __len__(self):
        return len(self._data)

이 방법은 개념적으로 정확할 뿐만 아니라 확장성도 뛰어나다. 현재는 dictionary지만, 향후 다른 자료구조로 변경하려 해도 인터페이스만 유지하면 사용자는 영향을 받지 않는다. 이는 결합을 줄이고 파급 효과를 최소화하며 코드를 유지 관리하기 쉽게 만든다.

✔️ 파이썬의 다중 상속

파이썬은 다중 상속을 지원한다. 다중 상속을 통해 새로운 패턴(ex. 어댑터 패턴 등)과 믹스인(mixin)을 활용하여 강력한 애플리케이션을 만들 수 있다. 다중 상속시 파이썬은 C3 linearization 또는 MRO라는 알고리즘을 사용하여 메서드 호출 순서에 대한 문제를 해결한다. 우선은 상속받는 순서가 빠른 클래스부터 우선순위를 가진다고 생각하고 넘어가자. 다중 상속에 대해 좀 더 자세히 알고 싶으면 정리를 잘해둔 다른 블로그를 추천한다.

믹스인은 그럼 무엇인가? 나는 장고를 하면서 내부 클래스들을 살펴보다가 본 적이 있었다. 이름만 봐서는 뭔가를 섞어서 만드는 느낌이다. 믹스인은 코드를 재사용하기 위해 일반적인 행동을 캡슐화해놓은 기본 클래스이다. 보통은 다른 클래스와 함께 믹스인 클래스를 다중 상속하여 믹스인에 있는 메서드나 속성을 사용한다. 상속의 계층구조를 잘 이해하고 사용한다면 효과적으로 설계가 가능하다.

다른 것을 제쳐 놓고 단순하게 말하자면 믹스인은 데코레이터처럼 기존 클래스에 직접적으로 추가하지 않고 다중 상속으로 메서드를 추가시킬 수 있다.

✔️ 함수의 인자

함수가 제대로 작동하기 위해 너무 많은 파라미터가 필요한 경우 추상화가 부족했다던지 설계가 잘못되었을 확률이 크다. 이럴 경우 리팩터링을 할 수 있도록 하자. 그리고 파라미터로 객체를 전달하여 사용하는 것이 좋은 방법이 될 수 있다. 하지만 부작용 방지를 위해 전달받은 객체를 변경해서는 안 된다.

...

✍️ 장을 마무리하며

이번 장에서는 독립성에 대해 강조를 하는 것 같다. 각각의 기능들은 변경 시 영향을 끼쳐서는 안 된다는 것이다. 이러한 결합성을 가지지 않고 독립적이라는 것은 정말 좋은데 말이 쉽지 실제로 적용시키기 힘들었었다. 하지만 이번 장에서 배운 믹스인, 상속 등을 알게 되어 이용해 잘 추상화, 캡슐화시킬 수 있도록 해야 봐야겠다. 중요한 포인트는 "파급 효과를 최소화하여 유지보수성을 향상시키자" 인 것 같다.


4️⃣ SOLID 원칙

이 장에서는 파이썬에 적용된 클린 디자인의 원리를 계속 탐구할 것이다. SOLID 원칙을 검토하고 이를 파이썬스러운 방식으로 구현하는 방법을 설명한다. SOLID의 뜻은 다음과 같다.

  1. S : 단일 책임 원칙 (Single responsibility principle)
  2. O : 개방-폐쇄 원칙 (Open/closed principle)
  3. L : 리스코프 치환 원칙 (Liskov substitution principle)
  4. I : 인터페이스 분리 원칙 (Interface segregation principle)
  5. D : 의존관계 역전 원칙 (Dependency inversion principle)

이것들에 대해 하나씩 살펴보자.

✔️ 단일 책임 원칙(SRP)

단일 책임 원칙은 소프트웨어 컴포넌트(일반적으로 클래스)가 단 하나의 책임을 져야 한다는 원칙이다. 클래스가 유일한 책임이 있다는 것은 변화해야 할 이유는 단 하나뿐이라는 말을 의미한다.

하나의 클래스 안의 메서드들이 각각이 독립적인 동작을 한다면, 이 메서드들을 다른 클래스로 분리하여 각 클래스마다 단일 책임을 갖게 할 수 있다. 이 경우 책임이 나눠진 클래스들을 조합하여 동일한 기능을 하는 객체를 만들 수 있다.

✔️ 개방/폐쇄 원칙(OCP)

개방/폐쇄 원칙은 모듈이 개방되어 있으면서도 폐쇄되어야 한다는 원칙이다. 간단히 말해서 확장 가능하고, 새로운 요구사항이나 도메인 변화에 잘 적응하는 코드를 작성해야 한다는 뜻이다. 예를 들면 새로운 기능을 추가하다가 기존 코드를 수정했다면 그것은 기존 로직이 잘못 디자인되었다는 것을 뜻한다.

기능이 추가되었을 때 기존 로직이 변경되지 않는 것에 초점을 맞춰봤을 때, if elif ... 의 체인 형식으로는 가독성도 떨어지고 메서드가 계속 커짐을 알 수 있다. 뭔 소리인지 잘 모르겠으니 예제로 살펴보겠다. SystemMonitor라는 클래스에서 event에 따라 분류를 한다고 가정하자.

class Event:
    def __init__(self, raw_data):
        self.raw_data = raw_data

class LoginEvent(Event):
    ...

class logoutEvent(Event):
    ...

class UnknownEvent(Event):
    ...


class SystemMonitor:
    def __init__(self, event_data):
        self.event_data = event_data

    def identify_event(self):
        if(
            self.event_data["before"]["session"] == 0
            and self.event_data["after"]["session"] == 1
        ):
            return LoginEvent(self.event_data)
        elif(
            self.event_data["before"]["session"] == 1
            and self.event_data["after"]["session"] == 0
        ):
            return LogoutEvent(self.event_data)
        return UnknownEvent(self.event_data)

이와 같은 경우 다른 event class 들이 생기면 메서드를 수정해줘야 한다. (+ elif의 남발은 사용하면 가독성이 최악)
이것을 개방/폐쇄 원칙에 맞춰 코드를 변경해보도록 하겠다.

class SystemMonitor:
    ...

    def identify_event(self):
        for event_cla in Event.__subclasses__():
            try:
                if event_cls.meets_condition(self.event_data):
                    return event_cls(self.event_data)
            except KeyError:
                continue
        return UnknownEvent(self.event_data)

__subclasses__ 메서드와 해당 클래스를 분류하는 로직이 meets_condition이라는 메서드로 각 클래스별 구현하도록 함으로써(다형성) event class가 늘어나도 수용할 수 있도록 유연해졌다. 핵심은 identify_event 메서드의 수정 없이 event class를 확장시켜나갈 수 있다는 것이다.

추가로 이 원칙은 다형성의 효과적인 사용과 밀접하게 관련되어 있다. 다형성을 따르는 형태의 계약을 만들고 모델을 쉽게 확장할 수 있는 일반적인 구조로 디자인하는 것이다.

✔️ 리스코프 치환 원칙(LSP)

리스코프 치환 원칙은 설계 시 안정성을 유지하기 위해 객체 타입이 유지해야 하는 일련의 특성을 말한다. 이 원칙의 주된 생각은 어떤 클래스에서든 클라이언트는 특별한 주의를 기울이지 않고도 하위 타입을 사용할 수 있어야 한다는 것이다.

느낌아 잘 안 온다. 이외에도 위키를 살펴보면 이 원칙을 만족하는 조건들이 있다.

  • 하위형에서 메서드 인수의 반공변성
  • 하위형에서 반환형의 공변성
  • 하위형에서 메서드는 상위형 메서드에서 던져진 예외의 하위형을 제외하고 새로운 예외를 던지면 안 된다.
  • 하위형에서 선행조건은 강화될 수 없다.
  • 하위형에서 후행조건은 약화될 수 없다.
  • 하위형에서 상위형의 불변조건은 반드시 유지되어야 한다.

말이 진짜 어렵다. 나는 여기 있는 모든 것을 이해하는 것은 살짝 내려놓았다. 하지만 읽어보면서 내가 생각하는 핵심은 어떤 인터페이스에서 하나의 클래스를 쓰는데, 그 클래스가 동일한 부모의 다른 자식클래스로 대체되어도 사용자가 제공하는 파라미터를 바뀐 클래스에서도 처리할 수 있어야 하고, 사용자와 약속한 결과를 반환해야 한다는 것이다.

하위형에서 메서드 인수의 반공변성, 반환형의 공변성이 이해가 잘 안 가므로 이 부분만 조금 더 살펴보겠다. 예를 들어 이해해보자면 부모 클래스가 파라미터로 backend|frontend 만 처리할 때, 하위클래스들은 이 두 개를 처리할 수 있어야 한다. 그리고 리턴 값으로는 True, False, ValueError만 반환한다 가정하면 다른 하위 클래스에서도 저 세 개의 결과값을 벗어나선 안된다는 것이다.

이것이 지켜지지 않으면 앞서 말한 개방 폐쇄 원칙도 자연스럽게 깨지게 된다. 당연하다. 다른 클래스로 바뀌었다고 작동을 안 하니까 기존 인터페이스가 수정되어버리는 상황이 생기지 않는가.

✔️ 인터페이스 분리 원칙(ISP)

객체 지향적인 용어로 인터페이스는 객체가 노출하는 메서드의 집합이다. 다중 메서드를 가진 인터페이스를 매우 정확하고 구체적인 구분에 따라 더 적은 수의 메서드를 가진 여러 개의 메서드로 분할하는 것이 좋다. SRP와 유사하지만 주요 차이점은 ISP는 인터페이스에 대해 이야기하고 있다는 점이다. 따라서 이것은 행동의 추상화이다.

그럼 인터페이스는 얼마나 작아야 할까? 나누고, 나누면 하나만 있는 것이 최고인가? 의미를 오해해서 극단적으로 받아들이면 안 된다. 즉, 반드시 딱 하나의 메서드만 있어야 한다는 뜻은 아니다. 하나 이상의 메서드라 하더라도 적절하게 하나의 클래스에 속해 있을 수 있다. 핵심은 관련이 없는 메서드는 분리하자는 것이다.

✔️ 의존성 역전 원칙(DIP)

의존성 역전 원칙은 코드가 깨지거나 손상되는 취약점으로부터 보호해주는 흥미로운 디자인 원칙을 제시한다. 의존성을 역전시킨다는 것은 코드가 세부 사항이나 구체적인 구현에 적응하도록 하지 않고, 대신 API 같은 것에 적응하도록 하는 것이다. A, B 두 객체가 상호교류한다 가정할 때, 인터페이스를 개발하고 인터페이스에 의존적일 수 있도록 한다. 인터페이스를 준수하는 것은 B의 책임이다.

그림으로 보면 이해가 쉽다.

이것과 같이 Syslog로 데이터를 보내는 방식이 변경되면 EventStreamer를 수정해야 한다. 이러한 문제를 해결하려면 EventStreamer를 구체 클래스가 아닌 인터페이스와 대화하도록 하는 것이 좋다.

위 사진과 같이 설계하면 인터페이스의 구현은 세부 구현 사항을 가진 저수준 클래스가 담당하게 된다.

주의 깊게 살펴본 독자는 실제로 이것이 왜 필요한지 궁금해할 것이다. 파이썬은 충분히 융통성이 있으며, 동적인 타입의 언어이기 때문에 인터페이스를 사용하지 않고도 간단하게 send() 메서드를 가진 객체를 넘기면 되는데 왜 굳이 추상 기본 클래스(인터페이스)를 정의하는 것일까?

엄밀히 말하면 이것 또한 사실이다. 그러나 추상 기본 클래스를 사용하는 것이 좋은 습관이다. 상속은 is a 관계임을 기억하자. 위 예시에서 Syslog는 DataTargetClient 라고 말할 수 있다. 이와 같이 모델의 가독성이 높아지고 그 결과 코드 사용자는 코드를 읽고 이해할 수 있다.

결과적으로 추상 기본 클래스(인터페이스)를 사용하는 것이 필수는 아니다. 그러나 클린 디자인을 위해 바람직하다. 이것이 이 책이 있는 이유 중 하나로 단지 파이썬이 너무 유연하여 자주 발생하는 실수를 줄이기 위함이다.

...

✍️ 장을 마무리하며

SOLID 원칙에 대해 알아보았다. 들어만 본 상태였는데 좀 더 구체적으로 생각해보는 계기가 되었다. 쭉 보고 나서 인터페이스 설계를 할 때 객체들의 책임을 적절히 나누고 의존성을 줄이도록 해야겠다는 생각이 들었다. 말이 쉽지만 이것이 가능하려면 크게 봐야한다. 전체적인 프로세스가 어떻게 진행되는지 어떤 객체들이 필요하고 어떤 소통을 해야하는지 먼저 파악해야 한다.

예를 들어 소셜 로그인 기능을 만든다고 생각해보자. 기능에 집중하지말고 구조에 집중하자. 로그인이라는 메서드가 실행되었을 때, 로그인을 하려는 client가 되는 객체가 있을 거고, 인증을 하는 객체가 있을 것이고, 그 데이터를 저장하는 또다른 객체가 있을 것이다. 인터페이스를 잘 만들어서 의존성을 줄이도록 노력하자.

잘못된 디자인은 미래의 많은 비용을 초래한다. 소프트웨어 공학에서 만능 해결책은 없다. 다만 과거 프로젝트에서 검증된 좋은 가이드라인을 따름으로서 성공할 가능성이 높은 소프트웨어를 만들도록 도와준다. SOLID 원칙을 통해 성공적인 소프트웨어를 만들 수 있도록 해보자.



마무리


내용이 한 번에 다 적을 수 있을 정도가 아니어서 끊어서 작성하려고 한다. 우선 지금까지 보면서 개념적인 부분을 많이 봤던 것 같다. 이후 좀 더 구체적으로 파이썬으로 이런 개념적인 부분을 구현할 수 있는 방법을 소개한다고 하니 끝까지 읽어봐야겠다.

이제까지 제일 기억에 남는 것은 SOLID 원칙이다. 객체지향 프로그래밍에 대해 괸심을 가지게 되는 계기가 되었고, 어떻게하면 객체들을 사용하여 유지/보수성, 생산성을 높일 수 있는지에 대해 고민을 해보게 되었다.

계속해서 이 책을 읽으면서 파이썬으로 코드를 짤 때 유지보수를 유용하게 할 수 있도록 하고, 생산성을 높일 수 있는 꿀팁들을 얻어가자.


reference

2021-12-05 21:55:53

프로그래머의 길, 멘토에게 묻다. - 데이브 후버, 애디웨일 오시나이 지음 / 강중빈 옮김 / 출판사 : 인사이트-

🙋‍♂️ 개요


개발자로 일을 시작한 지 이제 반년이 지난 나는 계속해서 성장에 갈급하고 있다. 나는 성격이 급한 탓에 대학 졸업 후 실무를 바로 해보고 싶어서 따로 취준 기간을 거치지 않고, 스타트업에 취업했다. 그러다 보니 내가 생각했던 스타트업과 현실과의 괴리감에 맞닥뜨렸다. 빠르게 이것저것을 진행하여 가능성을 보여줘야 하는 스타트업 특성상, 체계적인 것보다는 다양한 것을 시도하고 시간에 쫓기는 것에 급급하였다. 심지어 인원도 그렇게 많지 않다 보니 경력 있는 개발자분들은 나를 돌봐줄 여력이 없는 것이다. 그렇게 갈피를 잡지 못하고 갈팡질팡 하고 있을 때 지푸라기라도 잡는 심정으로 서점을 찾았다. 그렇게 이 책을 만나게 되었다.

이번에는 책을 읽기 전에 다음 내용들에 초점을 맞추어 읽어보려 한다.

  1. 이 책의 저자는 어떤 것을 말하고 싶어 하는가?
  2. 나는 이 책에서 어떤 것을 얻고 싶은가?
  3. 이 책을 통해 얻은 것을 통해 나는 어떤 생각을 가지게 되었는가?

즉, 이 책을 통해 내가 성장할 수 있는 방법 또는 마인드 셋에 대해 나만의 답을 정립할 수 있는 시간이 되길 기대하면서 읽을 것이다. 텍스트만 본다고 해서 지식을 얻을진 몰라도, 지혜를 얻어갈 수 있는 것이 아니기에 저자의 경험을 통해 깨달음을 얻고 내 생각이 어우러져 나만의 답을 찾을 수 있도록 노력하자.

🧐 들어가는 글


이 책의 목차는 다음과 같다.

  • 1장 들어가는 글
  • 2장 잔을 비우다
  • 3장 긴 여정을 걷다
  • 4장 정확한 자기 평가
  • 5장 끊임없는 학습
  • 6장 학습 과정의 구성
  • 7장 맺는 글

나는 책을 순서대로 읽는 편이라 처음부터 순차적으로 보기 시작했다. 들어가는 글을 통해 이 책에 대해 전반적인 설명과 어떻게 읽으면 좋은 지를 설명해주고 있다. 특히 이 책이 독특했던 것은, 견습 개발자가 성장하기 위한 패턴들을 소개하며 이해하기 쉽도록 상황을 들어 설명한다. 그렇기에 각 장들은 패턴들을 소개한다. 패턴이라 하면 굉장히 어렵고 거창해 보인다. 하지만 패턴의 이름을 보면 '흰 띠를 매라', '무지를 드러내라'와 같은 직관적인 이름을 가지고 있기에 쉽게 이해가 되었다.

즉, 이 책의 저자는 본인들과 많은 전문가들의 경험과 견해들을 종합하여 성장에 도움이 되는 패턴들을 정의하여 나와 같은 견습생인 독자들이 쉽게 적용할 수 있도록 해둔 것이다. 하지만 책에서 말하기를 모든 패턴들을 적용시킨다 해서 무조건 성장하는데 도움된다고는 하지 않는다. 상황에 맞는 패턴, 또는 시너지를 낼 수 있는 패턴의 조합으로 맞춰서 적용시켜야 좋은 결과를 낼 수 있을 것이라 말한다.

이것을 어떻게 실행에 옮겨 진행할 수 있을까? 각 패턴마다 저자는 구체적인 실천 방안을 적어두었기에 참고하여 나만의 실천 방법을 생각하여 적용시킬 수도 있다. 그래서 각 장마다 기억에 남는 내용과 책에서 제시한 각 패턴의 실천 방법을 적어두고, 마지막으로 내 생각을 정리해 봤다. 앞으로 패턴 이름을 나타낼 때는 위에 흰 띠를 매라처럼 진한 글씨로 나타낼 것이다. (1장과 7장은 생략.)

이제 하나씩 살펴보자.



📖 본문


2장 잔을 비우다


젊은 철학자가 가르침을 얻기 위해 고명한 선사를 찾아가서 있어지는 미셸 그랑몽의 'Tasting a New Cup of Tea'를 각색한 이야기로 시작한다.

"그대가 이미 가득 차 있는 잔을 가지고 내게 온다면, 어찌 그대에게 마실 것을 드릴 수가 있겠소?"

이야기는 거의 들어본 적이 있을 것이다. 저자는 견습과정에 어떤 마음가짐이 필요한지 설명하기 위해 이 이야기를 소개한다. 즉, 나쁜 습관을 떨쳐내는 일, 자기 역량에 대한 자부심을 접어 두는 일 등에 많은 노력이 필요하다는 것이다.

이번 장을 다 읽고 나서 한 문장으로 축약하자면, 나의 무지를 인정하고, 배우는 것에 몰두하라는 것이다. 그리고 배울 때에는 내가 가지고 있던 지식을 다 내려놓고 푹 빠져 보자. 즉, 2장에서 말하고자 하는 것은 정확한 자기 평가를 통해 자신의 지식의 빈틈을 매우고 탄탄한 견습과정을 만들 수 있도록 노력하자는 것이다.

💪실천 방안

  1. 첫 번째 언어
    • 언어 명세 찾아 읽기, 오픈소스 통독
  2. 열정을 드러내라
    • 평소 참고 있던 아이디어를 개인적으로 찾아가서 설명하고 피드백을 받을 수 있도록 설득하라.
  3. 무지를 드러내라
    • 업무에 관해 정말 이해되지 않는 것 5개 적어서 보이는 곳에 붙이기. 업무 바뀔 시 목록 갱신하기.
  4. 무지에 맞서라
    • 위의 목록 각각에 대해 학습하도록 노력하고, 지워나가라. 그중 생기는 빈틈도 목록에 기록하라.
  5. 깊은 쪽
    • 다 준비될 때까지 기다리다가는 아무 일도 못할 수가 있다. 어려운 문제가 주어진다면 잡아라. 이 말은 합당한 준비 없이 어려운 일에 도전하라는 의미가 아니다. 오히려 실패가 눈앞에 뻔히 보인다 할지라도 해외 발령 같은 제안이 들어왔을 때 그것을 받아들이라는 말이다. 실패를 준비하고 그 실패로부터 일어설 때, 소심한 자들은 결코 볼 수 없는 문이 당신에게 열릴 것이다.

... ✍️

이 중에서 나는 깊은 쪽을 읽으면서 머리를 명하게 만드는 띵언을 보았다.

무참한 실패를 맛본 적이 한 번도 없다면, 당신은 뭔가 가치 있는 일을 시도했던 적이 한 번도 없었다고 봐야 한다.
- 크리스토퍼 호킨스-

난 이제까지 너무 편한 길만 걸어오려 했던 것이 아닌가 라는 생각이 들었다. '사수(혹은 나보다 경험 많은 사람)랑 일을 하면 잘 알려주겠지'와 같이 매번 안일한 생각으로 적극적으로 배우려 파고들지 않고 나태해왔던 내가 떠올랐다. 또 매번 말로만 개인적으로 프로젝트를 시작할 거라고 하고, 다른 핑계를 대면서 시도조차 하지 않던 내가 부끄럽게 느껴졌다.


3장 긴 여정을 걷다


이 책에서는 마스터 소프트웨어(장인)로써의 길을 '긴 여정'이라고 표현했다. 이 장은 읽으면서 되게 어렵다고 느꼈다. 아직 경력이 1년도 넘지 않고, 많은 회사를 겪어 보지 않은 상황이라 그런지 여기서 말하는 상황들이 와닿거나 공감이 많이 되지 않았다. 그래도 쭉 읽으면서 하나 느낀 점이 있는데, '긴 여정'을 잊지 말라는 것이다. 즉, 장인이 되기 위한 기나 긴 여정 중 필연적으로 여러 가지 문제 또는 상황을 마주 하게 되는데, 이럴 때 내가 생각하는 목표점을 잊지 말라는 것이다.

이렇게 추상적으로 말하면 당연한 소리처럼 보일 수 있는데, 그래서 여러 가지 예를 들어 설명해주었다. 그중에서 생각나는 것들을 나열해보겠다.

  1. 관리직으로 승진을 했을 때
  2. 자신은 프로그래밍을 하고 싶다고 느꼈지만, 직장에서 개발이 아닌 다른 종류의 학습과 업무를 필요로 할 때
  3. 자신의 직무를 넘어 다른 개발 역량들을 배울 때, 직장에서 더 이상 배우는 것이 허락되지 않을 때 (ex. 테스터가 개발 역량을 키워 개발자와 경계를 허무는 경우. 물론 업무 수행은 성공적임)
  4. 등등..

이런 문제들을 마주했을 때, 보통 이직을 해야 한다고 생각할 수 있다. 그것도 좋은 방법 중 하나일 수 있지만, 사람마다 형편이 좋지 않은 경우처럼 쉽게 이직을 할 수 없는 상황이 존재할 수 있다. 그렇기에 먼저 고용주와 최대한 타협점을 찾을 수 있도록 설득할 수 던 지, 내가 상황을 바꿔나갈 수 있는 부분이 있다면 노력해보는 것이다. 긴 여정에서 크게 벗어나지 않도록 하는 것이 핵심이다.

그렇다고 이 책에서 말하고자 하는 것이 무조건 소프트웨어 장인이 되어야 한다거나 긴 여정을 가야 한다고 주장하는 것은 아니다. 다른 일을 하다가도 새로운 관점을 가지고 다시 복귀할 수도 있지 않겠는가? 또는 다른 일에 더 관심이 생겨 프로그래밍을 안 하게 되더라도, 그 길을 가는 것이 잘못됐다고 할 수 없는 것이다. 중요한 것은 '자신이 즐길 수 있는가?'이다.

이 장에선 마지막으로 '열정을 키워라' 패턴이 기억에 남는다. 상황은 '그냥' 소프트웨어 개발자로 고용되었고, 기예를 향한 열정을 질식시키는 그런 환경에서 일하고 있다는 가정이다. 그래도 몇 가지 실행 방안을 제안한다.

  1. 일에서 뭔가 흥밋거리를 찾고, 스스로 그것을 즐길 수 있는지 분별한 다음에 자기 자신을 그 일에 쏟아부어라. 시간이 여의치 않다면 퇴근 후에 시간을 따로 좀 할애해서 부숴도 괜찮은 장난감을 만들어 보라.
  2. 마음 맞는 사람들을 찾아라. 더 배우고 싶어 하는 것을 집중적으로 다루는 지역 사용자 모임에 참가하라. 블로그를 시작하고, 흥미 있어 보이는 다른 블로그들을 구독하라. 온라인 포럼과 메일링 리스트에 참여해서 배운 것을 공유하라.

이 외에도 고전을 공부하라 패턴, 자신만의 지도를 그려라 등이 있다. 핵심은 프로그래밍에 대한 즐거움을 잊지 않는 것이다.

"열네 살 때 당신이 프로그래밍에서 느꼈던 경이로움, 그 느낌을 계속 지니도록 노력하기 바랍니다. 만약 지금 일자리가 당신의 머리를 썩게 만드는 게 아닌가 하는 걱정이 든다면, 그건 아마도 사실일 겁니다."
- OSCON 발표 중 폴 그레이엄 -


💪실천 방안

  1. 긴 여정
    • 40년 후, 당신의 경력에 대한 간단한 설명과 인생행로에 가장 큰 영향을 끼쳤던 일에 대해서 글을 써 달라는 요청을 받았다고 상상해보라. 이런 사고 실험을 가지고, 당신의 장래 이력을 선택하고 계획하는데 활용하라.
  2. 자신만의 지도를 그려라
    • 지금 가진 일자리에서 이어질 것 같은 일자리를 세 가지 나열해 보라. 또 그 세 가지에서 비롯되는 세 가지 일자리를 적어보자. 이렇게 확장된 목록이 경력에 대한 선택 폭이나 경력을 쌓기 위해 바라는 위치를 잘 보여주고 있는지 자문해보라. 선택의 폭을 좁히는 제약조건은 무엇인가? 이 조건을 완화시키는 것이 어떤 영향이 있는지 주시하면서 위 과정을 다시 시도해보라. 당신이 생각하는 것보다 가능성은 더 다양하다.
  3. 직위를 지표로 이용하라
    • 당신의 직책이 어떤 것인지 길고 상세한 버전으로 한 번 적어보라. 실제로 하는 일과 당신의 역량 수준을 정확히 반영하라. 계속 업데이트하고, 이 직책에 있는 낯선 사람을 당신이 어떤 눈으로 바라볼지를 상상해보라. 즉, 그럴듯한 직함에 속지 말라. 그런 직위와 책임이 당신의 견습과정이 끝났음을 나타내는 것은 아니다.
  4. 열정을 키워라
    • 당신의 주변 환경을 개선할 수 있는 방법을 지속적으로 생각해보라.
  5. 지속적인 동기부여
    • 필연적으로 황금 족쇄가 다가올 때 경계심을 품을 수 있도록 숙달의 경지에 이르겠다는 야망이 있어야 하는 동기가 필요하다. 동기부여가 되는 일 최소한 15가지 이상을 적어보라. 잠시 기다렸다가 다섯 가지를 추가로 더 적어보고, 다른 시선을 의식해서 쓴 동기는 몇 가지나 되는가? 가장 중요한 다섯 가지를 적어 그 목록을 힘든 시기에 볼 수 있도록 잘 보관하라.
    • 황금 족쇄 : ex. 뭔가 새로운 걸 배우고 싶지만, 내가 이미 알고 있는 것만으로도 벌이가 너무 좋다.

... ✍️

나는 그냥 흘러가는데로, 회사에서 시키는 것만 하고 있는 것 같다고 생각이 들었다. 요즘 코딩할 때에도 생각을 많이 하지 않고, 어떻게 하면 좋은 코드를 만들 수 있을지 고민하지 않고 살아왔다. 이 장을 읽으면서 나는 목표가 없었음을 깨달았다. 내가 어떤 개발자가 되고 싶고, 어떻게 성장할 것인지에 대한 구체적인 고민과 계획을 세우지 않았다는 것이다.

나만의 작은 시도로 5년 후에 나는 어떤 모습이고 싶은지 진지하게 고민하고, 어떻게 하면 될 수 있을지 생각해보자. 그리고 그것을 실천하기 위해 내가 해야할 일들을 나열하고 하나씩 해낼 수 있도록 노력하자. 물론 쉽지 않을 것이다. 우선 다이어리부터 사자..😅


4장 정확한 자기 평가


빠르게 학습하는 사람들이 당면하는 주된 위험 중 하나는, 좁은 연못 속 커다란 물고기가 되어버리는 것이다. 작은 연못이나 큰 물고기 자체에 잘못된 것은 없지만, 큰 물고기가 광대한 연못 네트워크에 속한 다른 연못의 존재를 아는 것은 아주 중요하다. 재능 있고 열심히 일하는 견습생이라면 그가 이룬 작은 성공에 자족하지 말아야 한다. 능숙하게 일하는 다른 팀이나 조직, 숙련공들과 마스터들을 찾아보고 배움으로써, 이렇게 범용 해지려는 경향성에 맞서 싸워 가야 한다.

여우의 머리가 되기보다는 사자의 꼬리가 되어라!
-Tractate Avot-

그래서인지 제일 먼저 소개된 패턴은 가장 뒤떨어진 이가 되라 이다. 주변을 당신보다 뛰어난 개발자들로 채워라라는 뜻이며, 가장 뒤떨어진 멤버가 되어 그 안에서 더 성장할 여지가 있는 팀을 찾아라는 것이다. 그렇다고 이 말이 가장 못한 사람으로 머무르는 것이 아니라 다른 사람들과 같은 수준이 될 때까지 의식적으로 개선할 방법을 찾으며 더 뛰어난 개발자들을 모방해 가야 한다는 것이다. 이런 노력이 없다면, 팀에서 짐이 될 뿐 아니라, 너무 뒤쳐저 있거나 충분히 빨리 따라잡지 못하고 있다면 쫓겨날 수도 있다는 말이다. 이 패턴은 안주하지 말고 성장에 초점을 맞추라는 것 같다. 물론 따라가는데 힘들겠지만, 그만큼 내가 성장할 수 있는 기회가 된다는 것이다.

초심자가 정규 교육 과정으로 시작하든 독학으로 시작하든 간에, 소프트웨어 장인정신의 길로 향하는 첫걸음은 자신을 견습생으로 받아줄 장인을 찾는 것이다.
-피트 맥브린-

멘토를 찾아라. 멘토를 찾을 때, 견습생은 우리 모두 긴 여정을 걷고 있으며 누구도 모든 것을 알지 못한다는 사실을 기억해야 한다. 나보다 훨씬 많이 알 거라는 이유로 자신의 멘토는 마스터여야 한다는 생각에 저항을 해야 한다. 그렇지 않으면 멘토가 가진 어쩔 수 없는 약점이나 맹점에 대한 환멸을 느낀 나머지, 아직 가르침 받을 것이 많음에도 불구하고 더 배울 것이 없다고 여길 우려가 있기 때문이다.

주의점은 알겠지만, 멘토를 어떻게 찾는가? 잘 나가는 개발자는 알려져 있기에 누군지 알아내기는 쉽다. 하지만 이런 사람들이 멘토링에 관심이 없을 수도 있고, '견습 과정'이라는 이상한 것에 대한 부탁을 하려고 접근한다면 몹시 위협적으로 보일 수 있다. 그렇지만 잠재적인 멘토에게 거절당하거나 이상하게 비친다고 해도, 그 리스크는 별 것 아닌 반면에 보상은 아주 크다는 점을 기억해 두라. 비록 풀타임은 아니더라도 점심식사라도 같이 하는 것에는 돈과 시간이 아깝지 않을 일이다. 당신이 마스터의 경지에 이르는 것을 진지하게 생각한다면, 당신을 지도해줄 멘토를 끈질기게 찾아라.

마음 맞는 사람들 패턴에서는 긴 여정은 누구에게든 혼자 걷는 길이 아니며, 특히 견습과정 동안에는 친구가 필요하다고 말한다. 어떤 관계는 비록 짧지만 큰 영향을 끼치고, 또 어떤 경우는 오래 지속되면서 당신이 열정을 키울 수 있도록 한다. 그렇기에 책에서는 커뮤니티와 같은 활동을 추천한다. 또한 커뮤니티를 깜짝 놀라게 할 질문을 던질 정도의 역량은 늘 유지하도록 하라고 한다. 커뮤니티에 할 수 있는 값진 공헌 중 하나는 줄 맞춰 행군하는 것이 마땅하다고 믿는 이들에 굴하지 않고 맞서서 조그만 지적 능력의 차이를 이용해서 예의 바른 이견을 내보는 것이다.

마지막으로 겸손은 성공적인 견습과정의 토대 중 하나다. 야망과 결합될 때, 겸손은 당신을 집중하게 해주며 올바른 방향으로 전진할 수 있게 해준다. 포부 있는 견습생이라면 누구라도 본능적으로 결승점을 향해 경주해서 가능한 빨리 숙련공이 되고자 할 것이다. 그렇지만, 당신은 긴 여정을 걷는 중이고 이 여행은 단거리 경주가 아님을 기억해야 한다. 견습과정에서 가능한 한 많은 것을 얻어내도록 시간을 들이자. 경력이 3개월이던 5년이던 간에 소프트웨어 장인정신의 관점에서는 여전히 초보자다.


💪실천 방안

  1. 가장 뒤떨어진 이가 돼라
    • 다양한 사람들에게 질문하면서 아는 모든 팀, 프로젝트, 부서, 회사를 나열하여 기술적인 수준에 따라 정렬하고 성장하기 원하는 새 멤버에게 열려 있는 팀이 있는지 찾아라.
  2. 멘토를 찾아라
    • 활발한 도구나 라이브러리 또는 커뮤니티 하나 골라 가입하여 잠복하라. 그 커뮤니티의 가치관을 이해하고 인내심 있는 교사 역할을 하는 사람을 찾아 격의 없이 조언해 줄 수 있는지 청해 보기.
  3. 마음 맞는 사람들
    • 커뮤니티를 나열하고 참석해보면서 가장 흥미로운 그룹을 정하고, 정기적인 만남을 시작하라. 없다면 모임을 만들 수 있는 좋은 기회이다.
  4. 팔꿈치를 맞대고
    • 나와 유사한 목표를 가지는 사람을 찾아서 일주일에 하루 저녁 정도를 같이 일할 수 있도록 조율해보라. 일상의 피로가 동기부여를 필연적으로 악화시킨다. 그래도 적응하여 자극이 다시 돌아올 때까지 해보고, 안되면 새로운 파트너를 찾는 것은 당신 마음이다.

... ✍️

요즘 들어 성장을 위해 내가 어떻게 길을 정해야 할지 갈피도 안 잡히고 뭐라도 시작해보자 생각해보지만 잘 되지 않는 경우가 많다. 그리고 되돌아보면 나는 단순히 혼자 개발할 때보다, 사람들과 함께 하는 것이 더 즐거웠던 것 같다. 그래서인지 특히 마음 맞는 사람들 패턴이 기억에 많이 남았다.

이 패턴에서 추진력을 유지하기 위해 전담 멘토가 없는 경우, 비슷한 길을 걷고 있는 사람들과 빈번히 교류할 필요가 있다고 했다. 나 같은 경우는 혼자서만 하다 보면 의지도 꺾이고, 익숙한 것만 하고 아무런 발전이 없었다. 그러니까 일단 커뮤니티 사이트들을 찾아서 다 가입해보고 내가 가장 흥미를 느낄 수 있는 곳에서 적극적으로 활동해보자! 가능하면 온/오프라인 모임도 참석해보고 할 수 있는 것들은 다 해보자! 그러다 멘토도 찾을 수 있고, 개발에 대한 흥미를 지속하게 될 것이라 기대된다.

마지막으로 가장 뒤떨어진 이가 돼라 패턴을 통해 내가 현재 있는 직장에서의 나의 위치를 생각해보게 되었다. 현 직장이 규모가 작기도 하고 단발성 프로젝트만 하다보니 문제를 좀 더 심도있게 고민하지 못하고 기능 구현에 급급하고 거의 혼자 개발을 했다. 뭔가 이렇게만 있다가는 성장은 커녕 물경력이 될 것 같다는 생각이 들었다. 좀 더 내가 성장할 수 있을만한 곳을 알아보고 그 조직에 들어갈 수 있도록 내 역량을 갖추자.


5장 끊임없는 학습


마스터에게서 볼 수 있는 특성 중 하나는, 특정 분야에서 힘들게 얻은 전문성을 옆으로 제쳐두고서 기꺼이 새로운 것을 배우고자 하는 태도다. 배움이란, 숙달의 경지로 향하는 긴 여정에 오른 이들에게는 영속적인 활동인 것이다.

이 장에서는 능력의 폭을 넓혀라 패턴을 통해 새로운 정보를 수집하고, 다양한 관점을 가질 수 있도록 학습할 수 있는 활동을 제시한다. 하지만 새로운 정보를 수집하고 소비하면서 거기에 사로잡혀 버릴 가능성도 있다. 소프트웨어를 만드는 현실 세계로 돌아오는 것을 잊지 말자. 이 패턴은 자신의 학습은 가속화되겠지만 개발 속도는 늦춰지기 때문에 적절히 사용할 필요가 있다.

연습, 연습, 또 연습 패턴에서는 연습의 중요성을 강조한다. 조지 레너드가 묘사했던 마스터들이 연습하기를 좋아하는 이유 중 하나는, 그들이 연습을 할 때마다 뭔가를 조금씩 달리 하기 때문이다. 요점은 기억에 의존해서 기술을 연마하지 않으며, 가장 단순한 숙련된 행위를 할 때조차도 미묘한 차이를 발견해 내는 것이다. 그러므로 스스로 무엇을 연습하는지 주의를 기울이고, 진부함으로 빠지지 않도록 끊임없이 연습에 대한 평가를 해 나가야 한다.

자연스럽게 내가 적용해보고 싶은 기술이 있거나 구현해보고 싶은 프로젝트가 있다면 부숴도 괜찮은 장난감 패턴을 이용하면 괜찮을 것이다. 이 패턴은 말 그대로 실패를 해볼 수 있도록 하여 그 실패를 통해 경험을 얻을 수 있을 것이다. 요점은 그런 장난감을 만들 때마다 당신이 뭔가 새로운 것을 배운다는 점이다.

프로그래밍을 하다 보면 다른 사람의 코드를 활용할 때가 있다. 그래서 이 책에서도 소스를 활용하라 패턴을 알려준다. 단순히 코드를 사용하기만 하는 것이 아닌 오픈 소스의 최신 소스를 받고 과거 이력부터 진행되는 것을 따라가 보자. 개발자가 어떤 의도롤 가지고 코드를 모듈화 한 지 생각해보고 내 생각과 비교해보는 것이다. 더 나은 방법을 찾게 되면 그 프로젝트에 기여할 수 있는 기회를 얻은 것이다. 즉, 이 패턴에서는 코드를 읽고 이해하는 과정을 강조한다.

패턴, 관용 어법, 우수한 사례들에 대해 배우는 가장 좋은 방법은 오픈소스 코드를 읽는 것입니다. 다른 사람들이 어떻게 하는지 보십시오. 이것은 시류를 따라잡을 수 있는 훌륭한 방법인 데다 무료입니다.
-크리스 원스트라스(Chris Wanstrath)의 Ruby Hoedown 2008 키노트 중에서-

결국 이러한 기록들은 배운 것을 기록하라 패턴과 이어진다. 하지만 배운 것을 기록만 하고 그냥 잊어버리는 덫에 빠지지 않게 노력해야 한다. 즉, 정기적으로 리뷰를 하면서 전에 내렸던 결정이 새 데이터에 기초해서 재평가될 수도 있고, 모호했던 신념이 확고해질 수도 있다. 이 패턴은 당신이 걸어간 길의 자취를 그대로 남겨두어서 장차 거기로부터 새로운 교훈을 얻는 측면을 강조한다. 자연스럽게 배운 것을 공유하라 패턴과 연관이 되는데, 이 패턴을 통해 가르쳐봄으로써 더 강력한 학습 도구가 된다. "한 사람이 가르칠 때 두 사람이 배운다."라는 옛말처럼 말이다.

견습과정에 자신이 잘하고 있는지를 알기 어렵기 때문에 어려움을 많이 겪는다. 그래서 책에서는 피드백 루프를 만들어라 패턴을 통해 피드백을 얻을 수 있는 방식을 몇 가지 알려준다. 테스트 주도 개발 및 동적 타입 검사형과 같은 코드 수준, 입사나 승진 건으로 인터뷰했던 사람들에게 연락과 같이 당신이 어떻게 해 나가고 있는지 직접 물어보는 것 등이 있다. 마지막으로 실패하는 법을 배워라 패턴을 소개한다. 이 패턴의 목표는 실패로 이끌어가는 방식, 조건, 습관, 행동 양식이 무엇인지 스스로 인식하는 데 있다.


💪실천 방안

  1. 능력의 폭을 넓혀라
    • 다음 달 내로 참석하고 싶은 컨퍼런스를 찾고 그 컨퍼런스 연사 중 한 사람이 지은 책을 골라 읽어보라. 읽은 후 질문을 담은 서신을 저자에게 보내보자.
  2. 연습, 연습, 또 연습
    • 연습문제를 찾거나 자신만의 문제를 고안하라. 난이도는 당신이 쉽게 풀 수 있는 정도보다 약간 더 어렵게 하라. 4주간 일주일에 한 번씩 이 문제를 완전히 처음부터 다시 풀고, 자신의 해법이 어떻게 발전해 가는지 관찰하며 이것이 프로그래머로서 나의 강점과 약점에 대해 무엇을 말해주는가를 확인하자.
  3. 부숴도 괜찮은 장난감
    • 좋아하는 도구들을 동원해서 높은 품질을 유지하면서도 세상에서 가장 단순한 프로덕트(ex. 위키)를 만들어보자. 시간이 지나면서 기능도 더 추가하고 기존 프로덕트와 차별화할 흥미로운 방법도 찾을 수 있을 것이다.
  4. 소스를 활용하라
    • 알고리즘이 복잡한 오픈소스 프로젝트를 하나 골라 보라(ex. subversion, git, mercurial) 소스를 둘러보면서 생소한 알고리즘이나 자료구조, 설계 사상 같은 것들을 기록해 두라. 거기서 얻은 아이디어들에 강조점을 둔 블로그 포스트를 쓰고, 일상적인 작업에 적용할 수 있는지 확인해보자.
  5. 배운 것을 기록하라
    • 노트를 한 권 집어 들고 읽은 책에 대한 당신의 생각이나 읽은 뒤에 떠오르는 아이디어를 메모하고 이런 것들에 기초가 되어 블로그 글이나 잡지 기사, 심지어는 책이 만들어질 수도 있다.

... ✍️

이 장을 통해 학습을 할 때 도움이 될만한 것들을 얻었다. 특히 소스를 활용하라 패턴이 인상 깊었다. 나도 책에 적힌 견습생들이 흔히 하는 생각과 같이 오픈 소스 프로젝트를 하는 사람들은 정말 대단한 사람들이라 코드를 이해하지 못할 것이라고 생각하고 볼 생각조차 하지 않았었다. 바로 프레임워크 수준을 까보기엔 너무 어려우니까 git-hub에 python으로 만들어진 오픈소스를 검색 후 재미있어 보이는 하나를 정해서 읽고 분석해보자. 그리고 내 생각과 비교해보자.

또 나는 컨퍼런스나 강연을 통해 그 당시에만 감동을 얻고 잊어버렸던 것 같다. 질문이 생긴다면 과감하게 연락을 해보자. 책도 마찬가지다. 항상 읽고 덮기만 했지 책을 읽은 뒤에는 저자에게 감사의 말과 질문을 담아서 연락을 취해보라는 것이 기억에 남는다. 이런 생각은 한 번도 해본 적이 없었다. 배움에 있어서 적극적일 수 있도록 행동해보도록 하자.

마지막으로 지금처럼 내가 기록을 남기는 것은 좋지만, 써놓고 매번 잊어버리는 것 같다. 주기적으로 내 글을 다시 읽어보는 시간이 필요할 것 같다. 현실적으로 자주 하진 못해도, 한 달에 한 번 정도는 다시 읽어볼 수 있도록 하자.


6장 학습 과정의 구성


우리는 정보가 차고 넘치는 시대에 살고 있다. 디지털 기기를 통해 많은 정보를 쉽게 얻을 수 있지만, 경험 많은 이들이 쓴 책에서 얻을 수 있는 방대한 지혜는 많은 정보로 대신할 수 없다. 성공적인 견습과정의 요건에는 학습에 전념하는 시간뿐 아니라 몇 권의 책도 포함되어 있어야 한다. 추천 도서를 찾고 학습 과정을 구성하는 것은 스스로 해야 한다.

하지만 요즘 읽어야 할 책 수가 책을 읽는 속도보다도 더 빠르게 늘어만 간다. 그렇기에 읽기로 한 책들을 추적해 갈 독서 목록 패턴을 통해 유지하고, 다 읽은 책은 기억해두라. 배운 것을 공유하라 패턴과 같이 사용하여 읽을 책을 관리하기 위한 것뿐 아니라 과거의 독서 습관을 성찰하는 방법이다. 그리고 목록 중 특정 책이 자주 언급되거나 참고 문헌에 나타나면 목록의 가장 위에 올려놓아라. 결국 어떤 책들은 순위에서 너무 많이 밀려서 아마 결코 읽지 않게 될 것이다. 이것은 좋은 일이다. 지식의 홍수 속에서 우선순위를 매기며 걸러내는 방법을 제시하는 것이 이 패턴의 목적이기 때문이다.

또한 고전을 강조한다. 고전을 통해 웹이나 실험을 통해 그 정보가 어떻게 발전했는지를 배울 수 있다. 책을 집었을 때 제일 먼저 궁금한 것이 이게 얼마나 시대에 뒤쳐졌는가 하는 물음이라면, 당신은 잘못된 책을 읽고 있는 것이다. 그렇다고 너무 몰두한 나머지 실용적인 지식과 정보를 등한시하면 안 된다.
이렇게 목록을 작성해도 결국 읽지 않으면 아무 의미가 없다. 대중교통을 이용하거나 한다면 남는 시간이 생긴다. 꾸준히 읽어라

좋은 프로그래밍 책을 두 달에 한 권, 즉 일주일에 대략 35페이지 정도만 읽어도, 당신은 이 분야에 대해서 확실한 감을 갖게 될 것이며 주변의 거의 모든 이들과 구별되는 수준으로 올라설 것이다.
-스티브 맥코넬. [Code Complete]-

우리는 일을 하면서 빠듯한 마감 기한과 수많은 도구가 사용되는 복잡한 소프트웨어 프로젝트의 세계에 살고 있다. 그렇기에 당신은 각종 도구들을 겨우 지금 하는 작업을 완료하는 데 필요한 정도로만 배우고 있다. 튜토리얼을 하나 움큼 고르고, 내재된 이슈를 이해하지도 않은 채 장난감 예제를 복사해서 다 써 버린다. 이러면 자기 코드를 유지보수 하면서도 계속 난관에 봉착하고 깊은 지식이 필요한 작업을 할 때 어쩔 줄 몰라할 것이다. 그렇기에 더 깊이 파고들어라 패턴을 꼭 이용해 도구나 기술 분야, 각종 기법 같은 것을 왜 그런 식으로 되어 있는지 알게 될 때까지 지식의 깊이를 더해 가야 한다.

이 패턴의 사용 방법 중 하나는 근원적인 곳으로부터 정보를 얻는 것이다. 예를 들어 REST(Representation State Transfer)에 대해 얘기하면, 이 경험을 원 개념이 정의된 Roy Fielding 박사의 학위 논문을 읽을 이유로 생각해야 한다는 것이다. 그리고 그것을 알기 쉽게 풀어서 써 놓은 책을 인용하는 그런 사람의 말을 그대로 받아들이지 말라. 어떤 개념을 정말로 이해하려면 그 개념이 최초로 언급된 당시의 전후 맥락을 재구성해 볼 필요가 있다. 그 아이디어를 누가 처음 생각해 냈는지 조사해 보고, 그들이 풀려고 했던 문제가 무엇이었는지 이해하라.

또 다른 예로 사람들은 정규 표현식에 대해 피상적인 이해만 가지고 있어서 가끔 곤란한 처지에 빠지기도 한다. DFA와 NFA의 차이점을 확실히 알지 못하더라도 몇 년, 아마도 수십 년은 아무 일 없이 지낼 수 있을 것이다. 그러다가 어느 날 당신의 위키가 동작을 멈춰 버린다. 조사해 보니 정규 표현식 엔진이 재귀적으로 구현된 경우에, 백트래킹을 필요로 하는 특정한 입력값이 들어오면 아주 오랫동안 실행되다가 결국 StackOverflowException을 내고 만다는 것을 알게 된다.

이와 같이 기술과 도구에 대한 심도 있는 이해에 초점을 맞출 때는, 뜻하지 않게 좁은 한 가지 분야에 대한 전문가의 길로 빠져 버리는 일이 없도록 주의를 기울여야 한다.


💪실천 방안

  1. 독서 목록
    • 읽는 책이 있다면 벌써 실행하고 있는 것이다. 이 다음에 어떤 책을 읽을지 정하라. 즉 추진력을 유지하는 것이다.
  2. 꾸준히 읽어라
    • 두껍지 않은 책을 한 권 늘 지니고 다녀라. 잠깐씩 비는 시간을 학습에 활용할 수 있다.
  3. 더 깊이 파고들어라
    • HTTP 1.1을 기술하고 있는 RFC 2616과, RFC707을 읽고, RPC 클라이언트와 서버 프로그램을 구현해보고, RFC707의 저자들이 논했던 절충점에 대해 제대로 이해했다는 생각이 들면 같은 개념을 현대에 와서 구현한 오픈소스 프로젝트들(ex. apache thrift) 검토하기. 그렇게 상당히 정통해진 입장에서 지난 30년간 원격 프로시저 호출과 분산 시스템 분야에서 우리 지식이 어떻게 발전되어 왔는지를 주제로 블로그에 글을 쓰라. 이제 RPC에 대한 스티브 비노스키의 글을 읽어라. 당신이 얼마나 깊이 이해하고 있는지 지금도 의혹이 드는가? 당신의 의구심과 현재 이해 수준에 대해 블로그에 글을 올려라.
  4. 익숙한 도구들
    • 익숙한 도구 목록 5개를 적고 부족하면 채우고 학습해라. 이미 5개가 있다면 대체될 도구가 있는지 확인하여 교체하라. 도구를 실험할 때에는 부숴도 괜찮은 장난감 패턴을 이용하면 된다.

... ✍️

이 장에서는 더 깊이 파고들어라 패턴에서 너무 뼈를 맞았다. 여기서 말하는 상황과 내 상황이 너무 딱 들어맞았기 때문이다. 업무를 하면서 내가 딱 해결할 수 있을 정도로만 찾아서 이해하고 예제를 복사 붙여 넣기 하기 바빴지 내재된 이슈에 대해서는 이해하지 않았다. 그리고 업무에서도 나타날 수 있는 longest-prefix matching 같은 알고리즘적인 문제는 취업을 위한 것이라고 원래 너무 어려운 거라고 단정 짓고, List, Set, HashMap만으로 해결하려거나 포기해버렸던 것 같다.

HTTP client - server 통신 방식 등 내가 아주 얕게 알고 구현했던 개념들을 다시 찾아보고 깊이 이해할 수 있도록 하고 기록해보자. 그리고 알고리즘 문제들도 내가 풀고 싶은 것만 풀기보다, 자료구조 구현부터 차근차근해보자.



📘 책을 덮으며.


책의 초반에도 순서에 상관없이 읽는 게 좋다는 말이 이해가 됐다. 그때그때 나와 상황이 맞는 패턴들을 찾아 적용하면 될 것 같다. 즉, 이 책은 계속 가지고 있다가 뭔가 막히거나 내가 부족한 부분을 찾기 위해 다시 보는 참고서 느낌으로 이용하면 좋을 것 같다.

먼저 내가 읽기 전에 초점을 맞추었던 부분을 내가 생각한 것을 정리하겠다.

1. 이 책의 저자는 어떤 것을 말하고 싶어 하는가?

단지 소프트웨어 장인이 되려는 포부를 품은 견습생들에게 조언을 해주고자 책을 쓰기 시작했다고 한다. 소프트웨어 개발은 기예(craft)라고 표현한다. 무언가를 기예라고 말할 때 의미하는 것 중 하나는, 그것이 기술에 큰 가치를 두는 분야이자 전통이라는 점이다. 기술을 습득하고, 발전시키고, 최종적으로 전수하는 것까지 포함한다.

소프트웨어 장인이라는 인식을 가지게 해 주고, 기술을 갈고닦아 발전시키고 전수까지 할 수 있는 장인으로 거듭날 수 있도록 패턴을 이용해서 성장에 도움을 주고자 하는 것 같다.

2. 나는 이 책에서 어떤 것을 얻고 싶은가?

이 책에서는 잘 성장하는 사람들은 어떤 특징 및 마인드셋을 가지는지 궁금했고, 내가 앞으로 성장하기 위해서 어떻게 계획을 세우고 실행해야 되는지 궁금했다. 그것들을 이 책에서는 그런 행동과 같은 것들을 패턴화 시켜 성장하기에 좋은 방법들을 소개했다. 그렇지만 패턴 자체는 추상적일 뿐, 구체적인 예시들을 알고 싶었다. 그것 또한 책에서 구체적인 실천방안으로 적어두어서 내 상황에 맞는 패턴을 적용하면 될 것 같다.

3. 이 책을 통해 얻은 것을 통해 나는 어떤 생각을 가지게 되었는가?

가장 먼저 떠오른 생각은 다음과 같다.

아무것도 하지 않으면 아무 일도 일어나지 않는다.

책의 내용이 아무리 좋아도 내가 깨닫고 행동하지 않으면 아무 의미가 없다. 각장마다 남겨놓은 내가 해야 할 것들을 우선순위를 두어 리스트화 시키고, 차근차근 하나씩 해나갈 수 있도록 하자.

🔥 내가 해야할 실천방안

내가 적어뒀던 해야겠다고 생각이 든 것을 종합해보겠다.

  1. 5년 후에 나는 어떤 모습이고 싶은지 진지하게 고민하고, 어떻게 하면 될 수 있을지 생각해보자. 그리고 그것을 실천하기 위해 내가 해야할 일들을 나열하고 하나씩 해낼 수 있도록 노력하자
  2. 커뮤니티 사이트들을 찾아서 다 가입해보고 내가 가장 흥미를 느낄 수 있는 곳에서 적극적으로 활동해보자! 가능하면 온/오프라인 모임도 참석해보고 할 수 있는 것들은 다 해보자!
  3. git-hub에 python으로 만들어진 오픈소스를 검색 후 재미있어 보이는 하나를 정해서 읽고 분석해보자. 그리고 내 생각과 비교해보자.
  4. 좀 더 내가 성장할 수 있을만한 곳을 알아보고 그 조직에 들어갈 수 있도록 내 역량을 갖추자.
  5. HTTP client - server 통신 방식 등 내가 아주 얕게 알고 구현했던 개념들을 다시 찾아보고 깊이 이해할 수 있도록 하고 기록해보자.
  6. 알고리즘 문제들도 내가 풀고 싶은 것만 풀기보다, 자료구조 구현부터 차근차근해보자.
  7. 한 달에 한 번 정도는 내가 쓴 글을 다시 읽어볼 수 있도록 하자.

어우 생각보다 많다.... 일단 Notion 켜서 리스트를 작성하고 우선순위를 정해 천천히 해보자!


👋 마무리


우연히 진로에 관한 유튜브 영상(https://www.youtube.com/watch?v=VYjw30wTUfE) 보다가 거기서 말하기를, 이젠 100세 시대를 넘어 더 오래 살 수 있는 시대가 올 것이라고 말한다. 그 결과 직업의 사회적 의미가 목적지(Destination)에서 여정(journey)으로 바뀌었다고 한다. 이 책에서도 소프트웨어 장인이 되는 것을 여정이라고 비유를 했는데, 같은 표현을 하는 것이 신기했다.

나는 인생은 여행이라 생각한다. 여정은 여행의 부분일 뿐이고 내 선택에 따라 달라진다. 그래서 여정이라는 비유가 참 찰떡이라고 생각이 들었다. 여행은 재미 또는 보람이 있어야 되지 않겠는가? 수많은 여정 중에 나는 소프트웨어 개발이라는 여정이 내가 뭔갈 만들 수 있다는 것에 많은 재미를 느꼈고, 이것을 활용한다면 사람들에게 많은 도움을 줄 수 있겠다는 생각이 들어 기꺼이 첫걸음을 내디뎠다.

하지만 발을 딛은 지 얼마 지나지 않았지만 가면 갈수록 생각보다 험난하다는 생각에 압도당할 때도 있었다. 그렇지만 나만 처음 겪은 것이 아님을 잊지 않아야 한다고 생각한다. 인터넷을 통해 먼저 여정을 떠난 수많은 사람들의 발자취를 쉽게 찾을 수 있고, 심지어 실제로 도움을 주는 사람을 만날 수도 있다. 이왕 시작한 거 발만 담그지 말고 몸도 푹 담가봐야 해 봤다고 할 수 있지 않겠는가.

단기간에 많은 지식을 넣는 건 불가능에 가까우니 멈추지만 말고 꾸준히 long run 할 수 있도록 하자!
말만 하지 말고 행동으로 보여주자 🏃‍♂️

'독서' 카테고리의 다른 글

Python 클린코드 (1)  (0) 2022.01.12
[독서] 죽을 때까지 코딩하며 사는 법  (0) 2021.07.12
2021-07-12 11:20:31

개요


내가 개발자로 취업을 하게 되면서 처음엔 내가 뭔갈 만들 수 있다는 것과, 배우고 싶다는 열정가지고 아주 들뜬 마음으로 취업을 했었다. 그래서 몇 달간 정말 열심히 능동적으로 일했던 것 같다. 그렇지만, 회사에 오면서 내가 꿈꿔왔던 것과는 다르다는 것을 금새 깨달았다. 심지어 나는 대기업이나 중견기업처럼 시스템이 잘 갖추어진 기업에 취직한 것도 아니고, 작은 규모의 스타트업에 취업을 했는데 말이다.

그래서 처음 가졌던 마음과는 달리 뭔가 지치고 근무시간이 끝나면 코딩보다는 그냥 쉬고 싶고 무기력해져 있었다. 그러면서 마음 한편에 불안감이 엄습했다. 내가 이렇게 도태되어 있다면, 이 시장에서 살아남지 못할 것 같다는 생각 같은 거 말이다. 그래서 나는 근무시간 이외의 개인 시간을 헛되이 보내지 않기 위해 인터넷 서점을 뒤졌다.

평소에도 책을 읽으면 자기개발서 위주로 읽었는데, 프로그래머 관련해서도 이런 책이 있지 않을까 찾다가 이 책을 알게 되었다. 이름부터가 강렬해서 피해 갈 수가 없었다. 물론 내가 '죽을 때까지 코딩할 거야!' 라는 생각을 가져본 적은 없지만, 지금 뭔가 개발에 대한 열정이 살짝 식은 것 같아서 다시 불태우기에 좋은 책일 것 같아 바로 주문했다.

이 책의 예상 독자는 다음과 같이 적혀있었다.

  • 내가 좋아하는 코딩을 얼마나 오래 할 수 있을지 걱정되는 개발자
  • 코딩 슬럼프를 겪고 있는 개발자
  • 내가 속한 조직이 나에게 맞지 않는 건 아닌지 오늘도 혼란스러운 개발자
  • 지속적으로 성장하고 싶은 방법을 함께 찾고 싶은 개발자
  • 열심히 일하는 것 같은데 성과가 나지 않는 개발팀장 및 관리자

나는 세 개나 해당되서 안 살수가 없었다....😅
내용이 궁금하니 어서 읽어보자❗️



본문


우선은 내가 읽으면서 감명 깊게 보거나 적어두고 싶은 부분을 위주로 작성했다. 그리고 총 6장으로 구성되어 있는데, 각 장을 요약 및 내가 읽고 나서의 생각을 같이 정리해두는 식으로 글을 적었다. 그럼 1장부터 출발하자.


1장 어느 개발자의 죽음


평생 코딩하기 위해서는 어떻게 하면 좋을까?

  1. 돈을 많이 모아서 부동산 임대 사업과 같이 돈이 나오는 구멍을 만들어놓고 은퇴를 빨리하고 코딩을 하는 것이다.
    • 하지만 우리나라 사회구조 상 돈을 모으는 것이 쉽지가 않다. (집값 등등)
  2. '디지털 노마드'로 살아간다.
    • 코딩만으로는 힘들 수 있다. 앱 개발이 유일하다고 보지만, 이것조차도 쉽지 않다.
  3. 회사를 창업하거나 늙어서도 써줄 회사가 있을 정도로 유명해지기.

저자가 말하는 것은 열정을 가지기 위해 개발자가 일하는 환경 자체가 바뀌어야 한다고 말하고 있다. 현재의 시스템(흔히 말하는 갈아 넣는?)이 우리의 열정을 식히고 있다는 것이다.


2장 단절의 시대


지금의 사회는 100세가 넘게 살게 될 것이라고 말한다. 그런데 우리의 정년은 아무리 오래 버틴다 해도 50대 때 퇴직을 하지 않겠는가, 그러면 남은 50년 동안 뭘 먹고 살 것인가? 암울한 미래만 생각하지 말고, 관점을 바꿔보자. 근본적인 원인을 생각하여 통찰을 얻는 것이다.

저자가 말하는 개발자의 미래는 다음과 같다.

  1. 개발자 연령 상한이 없어지게 될 것이다.
  2. 개발자의 수요가 더욱 늘어날 것이다.
  3. 개인 사업의 기회도 점차 늘어날 것이다.
  4. 소프트웨어 산업의 볼륨은 더 커질 것이다.

저자는 이러한 것들이 기대감을 보탠 예측이라 했지만, 충분히 가능성이 있다고 표현했다. 다양한 정황을 통해 '죽을 때까지 코딩하기'가 실현 가능하다고 볼 수 있으므로 미래를 계획해야 한다고 주장한다.


3장 바람을 사랑하는 법


개발자의 삶을 살 때에 바람과 같은 고난을 피할 수 없다. 그렇다면 대비를 하고 되도록이면 즐겨야지 않겠는가? 코딩에 대한 열정의 불을 끄려고 하는 주변 환경에서 피하는 방법에 대해 알려준 장인 것 같다. 회사의 구성원들이 정말 중요한 것 같다. 경험에만 의지하지 말고 끝없이 내가 알고 있던 것을 의심&학습하고, 썩은 사과(권위를 이용해 자기 뜻대로만 하려는 사람들)를 조심하자.


4장 모닥불 개발자


기억에 남는 인용어들.

한 주에 60시간 일할 계획을 짜야 한다. 40시간은 회사를 위해 쓰고 나머지 20시간은 자신을 위해 쓴다.
20시간은 읽고 연습하고 공부하고 경력에 도움되는 여러 가지를 하며 보내야 한다.
-로버트 C 마틴 <클린 코더> 55쪽-

왜 나는 샤워 도중에 최고의 아이디어가 떠오를까?
-아인슈타인-

좋은 코드를 만드는 개발자가 되기 위한 도움 되는 요소들을 한 번쯤은 들어봤을 것이다. 하지만 중요한 것은 실행에 옳기질 못한다는 것이다. 그래서 나는 습관 파트에서 코끼리의 비유( 코끼리를 끌고 가는 기수(이성), 코끼리(감성) )가 정말 와닿았다. 움직이는 방향은 코끼리가 정하지 않겠는가? 그러므로 자그마한 것부터 하나씩 성취해가는 느낌을 맛보면서 코끼리(감성)를 설득할 수 있도록 해보자.

두 번째로 기억에 남는 것은 우뇌를 사용하는 방법이었다. 실제로도 뭔가 잘 안될 때 잠깐 바람 쐬러 나가서 떠오르는 경우가 많았다. 앞으로도 우뇌를 효과적으로 사용할 수 있도록 독서도 빼먹지 말아야겠다. 마지막으로 TDD도 습관들일 수 있도록 노력해야겠다. 이 부분은 매번 강조되는 것 같다


5장 모닥불 조직


5장에서는 어떤 조직이 바람이 불어왔을 때 촛불처럼 꺼지지 않고 활활 타오를 수 있는지 살아남는지에 대해 알려주었다. 전체적인 느낌으로는 저자는 지식 산업사회에는 테일러주의 시스템을 만들고 그 시스템에 사람들을 부품처럼 끼워 넣는 조직문화를 촛불 조직이라고 표현했다. 그리고 강조한 것은 '대중의 지혜' 및 '애자일'이 었다. 제대로 된 소프트웨어를 만든다는 목표에 집중을 하자. 내가 아직은 구성원으로 직장 생활을 할 입장이므로 촛불 조직에 가서 내 열정을 식히기보다는, 모닥불 조직에 잘 스며들어 모닥불 개발자가 될 수 있도록 노력해야겠다.


6장 죽을 때까지 코딩하기


마지막 6장에선 이제까지 말해 온 것들을 정리해주었다. 나는 다음과 같이 정리해 봤다.

  1. '성장 마인드 세트'를 가지자. 어려운 것이 아니다. 오늘보다 내일 더 성장할 수 있다고 믿고 도전과 노력에 더 열린 자세를 갖자.
  2. 모닥불 기업에 함께할 수 있도록 하자. 그전에 모닥불 기업의 프로세스를 이해하고, 내가 모닥불이 될 수 있는 조건을 갖추어야 한다.
  3. 우리의 수명은 많이 늘어났고, 원래 하던 일을 더 잘할 수 있도록 하자(모닥불 개발자가 되자). 그렇게 해야 우리는 평생 코딩하며 살 수 있을 것이다.


마무리


이 책을 읽으면서 제일 중요하다 느낀 것은 내가 먼저 모닥불의 조건을 갖춰야 한다는 것이다. 먼저 열정을 가지고 도전하는 것이 필요하다. 나 스스로가 끊임없이 공부하고 성장해 나가야 한다. 그러고 나서 환경을 생각해 보는 것이다. 물론 직장에서 매번 새로운 것을 하고, 열정적이기는 쉽지 않을 것이다. 그래도 매일매일 반복적이고 단순한 것만 한다면 '이곳은 내 열정을 식히지 않는가?'라는 의문을 가지고 고려를 해봐야 한다고 생각한다.

처음에 난 회사에 들어가서 실력 있는 사람만 모여있으면 좋은 회사인 줄 알았다. 하지만 생각보다 기술보단 사람이 더 중요하다는 것을 깨달았다. 저자가 폭포수 개발 방식을 예로 들었을 때, 폭포에서 윗 물이 더러운 물이 내려오면 자연스럽게 아랫물이 더러워질 것이라 했다. 아무리 좋은 기술로 아랫물을 정화시킨다 해서 근본적인 해결을 하지 않는 이상 계속 더러운 물이 내려오지 않겠는가? 결과적으로 기술 외적인 부분이 절반 이상을 차지한다고 한 저자의 말이 과언이 아님을 느끼게 되었다.

마지막으로 제일 와닿았던 부분은 우리는 이제 100세 시대를 넘어 더 오래 살지 모른다. 정년은 길어봐야 5~60대쯤으로 정해져있고, 그 이후의 삶도 상당한 기간이 남아있다. 그렇다면 먹고살기 위해선 직업을 계속 가져야 하지 않겠는가. 그렇다면 이왕 하던 거 잘하면 좋지 않겠는가. 그리고 평생 일을 해야 하는데 내가 좋아하지 않는 일을 한다면 그것만큼 불행한 삶이 어디 있겠는가.

결과적으로 이 책을 읽으면서 내가 왜 코딩을 시작하려 했었는지, 어떤 열정을 가지고 시작했는지에 대해 다시 한번 상기하게 되었던 것 같다. 그리고 앞으로 내가 이런 초심들을 잃지 않고 계속 발전시켜 나갈 수 있을지에 대해 조언을 얻게 된 책인 것 같다.

모든 개발자분들 파이팅🔥 💪

'독서' 카테고리의 다른 글

Python 클린코드 (1)  (0) 2022.01.12
[독서] 프로그래머의 길, 멘토에게 묻다  (0) 2021.12.05