Codong's Development Diary RSS 태그 관리 글쓰기 방명록
분류 전체보기 (48)
2023-07-06 00:55:40

요즘 일을 하면서 퇴근하고 유튜브나 SNS를 보면서 의미 없이 시간을 보내거나, 다른 취미 생활들을 하다 보니 시간이 많이 흘렀다. 2023년부터 네이버 블로그에 따로 달마다 일기를 적기 시작했는데, 여느 때와 같이 운동하고 씻다가 문득 나의 커리어에 대한 기록은 왜 안 하고 있지? 라는 생각이 들었다. 그와 동시에 방치되어 있던 내 블로그가 생각났다.

오래간만에 들어와 보니 마지막에 쓴 글이 벌써 1년이 훌쩍 넘었더라. 사실 이직하고 나서부터 한 번도 블로그 글을 쓰지 않았던 것 같다. 퇴근하고 피곤해서도 있지만, 회사에서 했던 것들을 적기가 블로그에 적기가 애매해서 안 썼던 것도 있다.(사실 귀찮음이 컸던 것 같다.)


📝 블로그 포스팅에 대해

개발자로서 계속해서 업무를 하거나 개인적인 프로젝트를 진행할 때 구글에 검색을 많이 하게 된다. 과거부터 나는 찾고 싶은 라이브러리의 reference를 공식 document나 공홈 같은 것들을 찾아보는 게 제일 좋은데, 대부분 영어로 되어있어 쉽게 설명되어 있는 한국어로 된 블로그를 많이 찾아봤었다.

하지만 어느 때인가 점점 블로그의 글들이 공식 문서를 번역해 둔 글이거나, 거의 그대로 옮겨둔 경우가 꽤 많았다. 심지어는 다른 블로그 글을 그대로 복붙 해두어 두는 경우도 있었다. 문득 나와 알던 개발자 중에서 이러한 이유로 블로그를 하지 않는다는 사람들이나, 심한 경우 블로그 하는 사람들을 조오금 색안경 쓰고 보는 분들의 마음이 살짝 이해가 되었다.

그렇다고 그런 글을 작성한 분들을 비하할 생각은 전혀 없다. 다른 시각으로 보자면 블로그가 결국 자기만의 공간이기에 남들 보는 것을 신경 쓰지 않고 메모장처럼 글을 쓰는 분들도 많은 것을 이해하고 있다. 나 또한 그렇게 적은 글들이 있다.

그래서 나는 돌아보게 되었다. 내가 적은 글들이 영양가가 있는 글들인가? 물론 사람이 완벽할 수도 없고, 지금도 부족한 실력인데 제대로 된 정보를 전달하지 못할 가능성이 크다. 그러다 보니 블로그 포스팅을 점차 소홀히 하게 되었던 것 같다.

사실 이것도 안 할 이유만 찾는 것 같긴 하다..

꼬리에 꼬리를 무는 내 생각은 결국 과거로 거슬러 올라가게 했다. 과거에 나는 열심히 블로그 글도 잘 쓰면서 배운 것을 잘 기록하려고 했었는데 달라진 게 무엇일까 생각이 들었다.


🔥 열정에 대해

나는 블로그를 작성할 때에 명확한 목적이 있었다. 솔직히 내 부족한 포트폴리오를 채우는 수단으로써도 많이 이용이 되었던 것 같다. 그렇다고 그런 목적으로만 쓴 건 아니다. 나의 경험이나 공부했던 것을 정리해서 글로 쓰는 게 다른 사람들에게도 도움이 되길 바라기도 하고, 설명하는 과정에서 나에게도 얻는 게 많기도 해서 포스팅한 마음도 컸다.

그 결과 나는 소규모 스타트업을 다니다가 좀 더 큰 규모의 스타트업을 다니게 되었다. 그곳에는 내가 경험하지 못한 많은 사람들과 체계가 갖추어져 있었다. 이 변화는 나에게 많은 영향을 준 것 같다.

제일 먼저 떠오르는 것은 일을 하는 프로세스였다. 기획자, BE, FE, QE 등 각자의 role이 다 나누어져 있기 때문에 각자 일을 맡아서 움직이면 된다. 이는 많은 효율을 가져온다. 솔직히 이런 시스템이 갖추어져야 사람이 많은 조직에서 원활하게 굴러갈 수 있다고 생각이 든다.

하지만 나라는 개인의 입장에서는 조금 달랐다. 이전 회사에서는 기획부터 개발, 테스트까지 혼자 다 했어야 했다(물론 프로젝트의 규모가 상대적으로 작으니 좀 빡새긴 하지만 할 수는 있는 정도였다.). 반대로 이직 후에 일을 할 때 내가 생각해야 하는 부분이 줄고 개발에만 집중할 수 있어서 좋은 점도 있지만 이것에 익숙해져 버려서 나에게 주어진 일 외에는 생각을 하지 않게 되었다는 것이 문제가 되었다.

이건 내가 아직 역량도 부족하기에 책임과 일의 범위가 작기 때문에 어쩔 수 없긴 하다. 그래도 나와 같은 입장에서 여기서 두 부류로 나뉘는 것 같았다.

첫 번째는 본인이 다른 role의 업무까지도 궁금해하면서 전체적인 그림을 파악해서 내 일처럼 처리하는 사람.
두 번째는 워라밸 지키면서 주어진 일만 처리하는 사람.

마음만은 나는 첫 번째 부류가 되고 싶었는데, 시스템 속에 스며들어 톱니바퀴처럼 두 번째 부류처럼 일을 하는 게 적응이 되어버린 것 같다. 그러다 보니 자연스럽게 수동적이게 되고, 어려운 일은 피하게 되거나 다른 동료 개발자들에 숨어 해결되는 것만 기다렸던 것 같다. 이러니 개발에 대한 열정도 점점 식게 되고 지금 이 글을 적게 되는 시점까지 오게 된 것 같다.

그렇다고 아예 개발이 싫은 건 아니다. 나도 무언가 만들고 그 결과를 보고 얻는 보람과 재미가 있다. 어쩌다 이렇게 되었을까?

같은 팀에 나보다 늦게 들어오고 아예 신입으로 시작하는 분이 있는데, 그분은 첫 번째 부류에 가까웠다. 또 실력이 느는 게 내가 느껴질 정도로 열심히 성장하고 있었다. 눈에 띄게 다른 점은 타 부서의 요청이나 우리 시스템에 문제가 생기면 먼저 달려들어 확인을 했다.

같은 환경 같은 시간을 보내고 있는 사람들인데 누구는 성장하고 누구는 도태된다. 이러한 차이는 결국 마음가짐의 문제라는 생각이 들었다. 문득 예전에 인프콘에서 영한님이 말한 내용이 떠올랐다.

출처 : https://www.youtube.com/watch?v=QHlyr8soUDM

이 영상에서 보면 영한님께서는 프로그래머가 문제를 보고 지나치거나, 알아서 해결될 거라고 넘겨버리는 것은 직무유기라는 말까지 했던 것 같다. 나는 문제 해결이 아니라, 내 일감만 처리하기 바빴던 것이 아닌가?

또 엔지니어라면 기술적으로 문제를 해결해야 한다. 즉, 기술을 잘 알아야 한다. 나는 딥다이브에도 약한 것 같다. 조금이라도 어려워지면 포기하기 십상이다. 왜 더 파고들지 못하는가. No pain, no gain. 이라는 말도 있는데 아무 대가 없이 기술력을 얻는 것도 말이 안 된다. 이 과정은 누구나 겪는 것이고, 이를 이겨내야 성장할 수 있다.


🤔 앞으로 할 것인가?

이렇게 나의 상태나 문제점은 알았다. 하지만 생각만 하고 아무것도 하지 않으면 달라지는 것은 없다. 똑같이 도태될 뿐이다. 어떻게 이 문제를 타개할 것인가?

인프콘에서 보면 영한님은 열정은 불쏘시개와 같다고 말했다. 언제 꺼질지 모른다는 것이다. 그래서 추천하는 방법은 루틴, 시스템화를 하는 것이다. 매일 개발 공부하는 시간을 가지는 것이다. 그때에는 아무 이유 없이 그냥 개발 공부를 하는 것이다. 내 의지로 안된다면 내 삶의 환경을 바꾸도록 노력해 보는 것이다.

그렇지만 아무 목적 없이 막연하게 개발 공부 한다는 것은 실패할 가능성이 굉장히 커 보인다. 나는 뭔가 할 때 재미가 생겨야 지속적으로 할 수 있다고 생각한다. 어떤 상황에서 재밌었는지 생각해 보면 누군가와 같이 하나의 목표를 가지고 할 때, 또는 내가 만든 것이 나 포함 누군가에게 도움 될 때인 것 같다.

그렇기 때문에 내가 하던 사이드 프로젝트를 계속 develop 해보는 게 어떤가 싶다. 나는 웹 개발을 하고 있기 때문에, 내가 실제 서비스를 작게라도 운영해 보는 것이 많은 도움이 될 것 같다. 비록 그 서비스를 주변 사람들 또는 나만 쓴다 하더라도, 쓸만한 것을 만들 수 있도록 해보자. 그리고 그 서비스를 구축하는 과정을 실제로 경험하고 문제를 해결할 수 있도록 해보자.


😁 마무리

갑자기 씻다가 급발진해서 두서없이 주저리 글을 썼던 것 같다. 하지만 시작했을 때 끝을 봐야 등록되는 게 블로그 글인 것 같다. (임시저장을 해뒀다가 작성 안 해서 사라진 적이 몇 번 있다.)

어찌 됐건 끝까지 하는 놈이 성공하는 것 아니겠는가. 아직 개발이 싫은 것도 아니니 힘닿는 데까지 열심히 할 수 있길 노력해 보자. 미래의 내가 이 글을 봤을 때 부끄럽지 않도록 열심히 했길...🙏

ps. 나와 비슷한 연차의 사람들은 어떤 생각을 할지도 궁금쓰 😄

'사는얘기' 카테고리의 다른 글

개발자 (약)1년차 회고  (1) 2022.01.10
2022-12-25 22:05:55

개요

도커를 이용해서 mysql db를 사용하는 django project를 만들고 싶었다.

도커만 설치하면 어디서든 쉽게 실행시킬 수 있다는 장점이 넘 달달하죠잉~🍯



본문

1. docker compose 세팅

대충 나는 아래와 같이 기본적인 것만 해뒀다.

# docker-compose.yml
services:
  db:
    image: mysql:5.7
    container_name: example
    ports:
      - "3306:3306"
    environment:
      MYSQL_DATABASE: "test"
      MYSQL_USER: "root"
      MYSQL_ROOT_PASSWORD: "test1234"
    volumes:
      - ./mysql:/var/lib/mysql:rw
      - ./docker/mysql/conf.d:/etc/mysql/mysql.conf.d:ro # mysql setting 적용을 위함.

맨 아랫줄은 mysql setting 값들을 파일로 적용시키기 위함이다. 아래 사진 참고.

출처 : https://hub.docker.com/_/mysql


2. DB 생성 확인

~ ❯ docker exec -it drf-example /bin/bash
root@937526738ea0:/# mysql -u root -ptest1234
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.38-log MySQL Community Server (GPL)

Copyright (c) 2000, 2022, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| test               |
+--------------------+
5 rows in set (0.07 sec)

mysql>

원했던 대로 test db가 잘 생성되어있는 것을 확인할 수 있다.


3. django config

DATABASES = {
    'default' : {
        "ENGINE": "django.db.backends.mysql",
        'NAME': 'test',
        'USER': 'root',
        'PASSWORD': 'test1234',
        'HOST': 'localhost',
        'PORT': '3306',
    }
}

4. django 서버 실행

~/drf_example ❯ python manage.py runserver
...
django.db.utils.OperationalError: (2002, "Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)")

뜬금없이 socket 파일을 못 찾는다 ㅇㅈㄹ 너무 화가 남.... 이거 때문에 조금 시간을 허비했다....

구글링 결과 알고 보니 django settings 파일에 db host 값을 localhost라 하면 django가 db 통신을 tcp로 하는 게 아닌 소켓으로 해버린단다...

ref. https://stackoverflow.com/questions/58029324/2002-cant-connect-to-local-mysql-server-through-socket-run-mysqld-mysqld-so

host 값을 '127.0.0.1'로 바꾸면 아주 가뿐하게 실행된다~~

2022-01-16 01:25:29

🙌 개요


웹 개발을 하면서 유저가 있다면 인증이라는 개념은 절대 빠지지 않는다. 하지만 매번 토이 프로젝트와 같이 소규모로 시작하는 경우 매번 인증을 직접 구현하기에는 너무 소요가 크다는 생각이 든다.

그러다 네이버, 카카오, 구글 등 다른 사이트 아이디를 통해 로그인이 가능한 것들을 보면서 어떻게 이런 것이 가능하지? 라는 의문을 가지게 됐다.

관련해서 검색을 하다보니 OAuth2.0 이라는 개념을 접하게 되었고, 단순히 social login을 사용하기 전에 이것을 알고 가야겠다는 생각이 들었다. 그래서 OAuth2.0에 대한 내용을 간략하게 정리해보려고 한다.

제 글만으로 부족할 수 있으니 references 내용들 꼭 읽어보시는 것을 추천드립니다 😅


OAuth2.0


🧐 OAuth2.0 이란?

OAuth2.0에 대한 rfc 문서 : https://datatracker.ietf.org/doc/html/rfc6749
정리를 잘해둔 한컴인텔리전스 블로그가 있어서 참고하여 작성하였다.

OAuth2.0은 rfc 문서의 제목에서 Authorization Framework이라 지칭된다. 핵심은 Third-Party 프로그램에게 리소스 소유자(Resource Owner)를 대신하여 리소스 서버(Resource Server)에서 제공하는 자원에 대한 접근 권한을 위임하는 방식을 제공한다.

뭐가 좋아서 쓰는 걸까?

큰 장점으로 단순성을 꼽을 수 있다. 구현자의 관점에서 볼 때 OAuth 2를 이용한다면 네이버나 카카오 등 다른 사이트 로그인을 통해 인증을 하고 접근 권한을 얻는 것이므로, 복잡한 인증 절차를 직접 구현하고 제공할 필요가 없어진다.

리소스 소유자는 뭐고 리소스 서버는 뭐지?

여기서 나오는 역할들에 대해 정리하고 넘어가자.

  1. Resource Owner : 리소스 소유자 또는 사용자. 보호된 자원에 접근할 수 있는 자격을 부여해 주는 주체. 즉 id/password를 입력하는 사용자가 된다. 인증이 완료되면 권한 획득 자격(Authorization Grant)을 Client에게 부여합니다.
  2. Client : 보호된 자원을 사용하려고 접근 요청을 하는 Application (ex. 내 fastapi server)
  3. Resource Server : 사용자의 보호된 자원을 호스팅하는 server (ex. 구글, 네이버, 카카오 등)
  4. Authorization Server : 권한 서버. 인증/인가를 수행하는 서버로 클라이언트의 접근 자격을 확인하고 Access Token을 발급하여 권한을 부여하는 역할을 수행 (ex. 구글, 네이버, 카카오 등)

지금까지의 핵심은 내 fastapi 서버는 사용자가 authorization server(ex. 네이버)에 인증하도록 하고, 인증 완료 시 권한을 얻어 Resource Server(ex. 네이버)에서 로그인한 사용자 정보를 얻어 내 서비스에 이용하면 된다.

권한을 얻었는 지 어떻게 알지?

인증에 성공하면 authorization server가 Access token / Refresh token(Optional)을 발급해준다.

이것들은 뭐냐.. 간단히 말하자면 Access token은 Resource Server(ex. 네이버)에게서 사용자의 보호된 자원을 획득할 때 사용되는 만료 기간이 있는 token이고, Refresh token은 Access token 만료 시 이를 갱신하기 위한 용도로 사용하는 token이다.

네이버와 카카오의 경우 Access Token의 만료 기간은 1~2시간 정도이다. Refresh Token의 경우 둘 다 존재하고 카카오는 기간이 몇 달 정도 되지만, 네이버는 딱히 명시되어있지 않았고, 응답에도 만료기간이 없는 듯했다..?(Refresh token 만료기간 관련 네이버 문의 링크 네이버 형...! 답 좀 달아줘라...)

자 이제 본격적으로 도식화된 그림을 통해 전체 흐름을 살펴보고 이해해보자.

🔁 OAuth2.0의 전체적인 흐름

흐름을 보기에 앞서 OAuth 프로토콜에서는 다양한 클라이언트 환경에 적합하도록 권한 부여 방식에 따라 4가지 종류로 구분해서 제공한다.

  1. Authorization Code Grant : 권한 부여 승인 코드 방식
  2. Implicit Grant : 암묵적 승인 방식
  3. Resource Owner Password Credentials Grant : 자원 소유자 자격증명 승인 방식
  4. Client Credentials Grant : 클라이언트 자격증명 승인 방식

그중에서 1번 Authorization Code Grant 방식이 자체 생성한 Authorization Code를 전달하는 방식으로 많이 쓰이고 기본이 되는 방식이기에 이것만 다루도록 하겠다. (네이버, 카카오 둘 다 이 방식을 사용함.)

이미지 출처 : https://darutk.medium.com/diagrams-and-movies-of-all-the-oauth-2-0-flows-194f3c3ade85

그림의 순서대로 내가 이해한 것을 네이버 로그인을 fastapi서버로 구현한다는 느낌으로 다음과 같이 가정하에 설명해보겠다.

그림에서 App XYZ = 내 앱 서버(fastapi)
그림에서 service ABC Authorization server = 네이버 인증 서버
그림에서 service ABC Resource server = 네이버 리소스 서버

  • 1~5 사용자가 내 앱에서 제공된 로그인 페이지에 있는 네이버 아이디 로그인 버튼을 누른 후, 네이버 로그인 페이지가 뜨면서 ID/PW로 로그인을 진행한다.
  • 6 네이버 인증 서버가 ID/PW가 유효한 것이 확인되면, 내 서버에 인증 코드(authorization code)를 보내준다.
  • 7~8 내 서버에서 받은 인증코드로 네이버 인증 서버에 Access token 발급을 요청하여 발급받는다.
  • A~D 발급받은 Access token으로 사용자 정보를 가져온다.

내 설명이 맞지 않거나 이해가 안 될 수 있으니 rfc 문서의 해당 부분을 링크로 달아둘 테니 확인해보길 바란다. 4.1. Authorization Code Grant

rfc 문서의 그림을 보고 느낀 것인데, 위의 그림에서 1번 부분의 App XYZ는 rfc 문서에서 user-agent인 것 같고, 이후 6번에 나오는 App XYZ는 rfc 문서에서는 Client이지 않나 생각이 든다.

위 그림에서는 하나의 App XYZ로 표현을 했지만 혼란을 방지하기 위해 user-agent와 client로 따로 표현됐어야 하는 것이 아닌가 싶다. 여기서 말하는 App XYZ가 결국 하나의 application을 말하는 것이므로 frontend/backend를 전부 포함한 개념이라 생각하면 맞는 말 같기도 하고 잘 모르겠다.. 나만 혼란스럽나..?


👏 마무리


핵심 정리

OAuth2.0의 핵심은 자원에 대한 접근 권한을 위임하는 방식이라는 것 같다.

즉 내 app이 있고, 사용자가 있다고 할 때, 사용자가 자신의 데이터에 대한 access 권한을 부여해주는 것 같다. 그 과정에서 사용자와 내 app 사이에 네이버나 카카오와 같은 인증 서버가 중개를 해주는 것이다.

그 결과 나는 app을 개발할 때 인증을 구현할 필요가 없으므로 비즈니스 로직에 좀 더 집중할 수 있다는 것이다.

마무리 멘트

좀 더 디테일하게 들어가자면 빼먹은 내용이 많겠지만, 우선 컨셉을 알아가는 것을 목적으로 글을 써봤다. 앞으로 남들이 다 쓰니까 쓴다는 이유 말고, 정확히 왜 쓰는 건지, 어떤 이점이 있는지 알고 쓸 수 있도록 노력하자.

references

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

2022-01-10 15:51:19

📢 개요


⚠️ 주의사항

지극히 1000% 주관적인 생각이고 멋모르는 사회초년생의 귀여운 생각이니 불편한 부분이 있으셔도 감안하고 읽어주시면 감사하겠습니다 😅

들어가는 글

작년 2월부터 취업을 해서 개발자로서 일을 시작한 지 약 1년째 그동안 나름대로 느낀 점을 적어보고 나를 되돌아보는 시간을 가지려고 한다. 추가로 내가 느낀 것 중에서 혹시나 개발자를 꿈꾸시는 분이나 취업하신 지 얼마 되지 않은 분들께 조금이나마 도움이 될까 싶어서 포스팅을 시작하였다.

나의 상황

본론에 들어가기에 앞서 나는 어떤 환경에서 근무를 했고, 어떤 일을 했는지를 기록하는 것이 먼저라고 생각이 들었다.

나는 지방에 있는 4년제 대학교 정보통신공학과를 다녔다. 4학년 2학기 때에 광주 인공지능 사관학교(이후 광인사)라는 머신러닝/딥러닝/웹개발 + 이것저것 패키지로 짜인 국비지원 교육을 수료했다. 그 이후 졸업(2월 졸업) 하자마자 서울에 이력서를 써봤다. 운이 좋게도 광인사에서 했던 프로젝트가 자연어처리 위주로 했었어서 이사님께서 좋게 봐주셔서 개발자가 5명 안팎의 스타트업에 자칭 데이터 엔지니어(사실 잡부임)로 취업했다.

회사의 인프라는 aws 기반으로 돌아가고 있었고, 이후 약 7개월가량은 자연어처리 관련 데이터 전처리나 크롤링, Flask로 간단한 API를 만드는 일을 위주로 했다. 그러다 서비스 개발을 하시던 분들이 대거 퇴사를 하면서 내가 python FastAPI로 웹 서비스 backend 개발을 맡게 되었다.

간략하게 이 정도로만 나의 상황을 정리하고 넘어가도록 하겠다. 자세한 것들은 이후 내용에서 중간중간 녹여낼 것이다.

...

📝 느낀점들


방향을 바꾸다.

나는 사실 대학교 3학년 때까지 개발에 관심이 1도 없다가, 4학년 때 머신러닝/딥러닝에 흥미를 가지고 python으로 개발을 시작했다. 그러다 광인사를 다니면서 더욱 흥미를 가지고 프로젝트를 진행하며 재미를 봤다. 하지만 항상 사람이 재밌는 것만 할 순 없지...

이미지 출처 : MBC 마리텔 캡쳐

사실 이 분야가 연구 분야라 활용하여 만드는 것을 좋아하는 나와 맞지 않기도 하고, 취준 기간 공고들을 보면서 데이터 분석 관련 직무는 석사가 default인 세상이 되어가는 것을 보고 나의 길이 아니라고 생각이 들었다. (하지만 머신러닝/딥러닝 모델을 이용한 사이드 프로젝트와 같이 취미(?)로는 하면서 놓고 싶진 않다.)

그러다 회사에서 python으로 Backend 개발을 할 기회가 생기면서 옳다구나 하고 도전을 했는데 너무 재밌어서 Backend 쪽으로 커리어를 성장시켜나가기로 결정했다.

나대지 말자(아는 척하지 말자).

개발자로 일을 하면서 다른 사람들과 협업을 하게 된다. 취준 할 때의 뿜뿜하는 자신감으로 입사한 나는 내가 아는 것이 전부인양 말을 했던 적이 있다. 그러다 피본적이 한두 번이 아니다.

가만히 있으면 반이라도 간다는 말이 있다. 확실하게 아는 것이 아니면 차라리 말을 하지 않는 것이 나을 수도 있다. 사실 나는 이것보다 더 좋은 것은 확실한 논리나 근거를 바탕으로 말을 하는 것이 중요하단 말을 하고 싶다.

나를 너무 믿지 말자.

어떤 작업을 할 때 '이렇게 하면 더 편할 거 같은데..' 또는 '그냥 이렇게만 하면 끝나는 거 아닌가?'와 같은 생각으로 side effect를 고려하지 않고 단편적으로만 생각하여 작업을 진행하면 대부분 내가 고려하지 못한 측면의 문제가 발생한다. 진짜 여기서 경력의 차이가 느껴졌다. 나는 기껏해야 한 수 앞까지 밖에 생각 못하는데, 경력이 있으신 분들은 몇 수 앞을 내다보셨다. 아는 만큼 보인다는 말이 있다. 가뜩에나 드러날 밑천도 없는데 항상 겸손하자.

그렇기에 자연스럽게 따라오는 중요한 것이 있다...

이미지 출처 : http://www.quickmeme.com/meme/3p69y3

바로 TEST 코드의 중요성이었다. 짤처럼 나는 코드를 짜고 '어~ 잘되네 푸시해야지~' 하고 문제가 일어났을 때나 코드를 본 적이 많았다. 이건 진짜 에러 금방 난다. 나를 너무 믿지 말자. TEST case를 작성해두면 오히려 장기적으로 봤을 때 좋다고 생각한다. 매번 작업한 것을 일일이 검사하는 것도 일이기 때문이다.

끊임없이 배우자

국비교육을 마치고 했던 프로젝트들을 통해 얻은 경험과 지식으로만 개발이 별거 없다는 생각을 가진 적이 있었다. 하지만 입사한 지 얼마 지나지 않아 깨달았다. 실제 사람들에게 제공되는 서비스를 만들 때는 신경 써야 할 부분이 정말 많다는 것과, 내가 모르는 것들이 어마무시하게 많다는 것을.. 마치 이 짤처럼..

1걸음 다가가면 2걸음? 어림없지 5걸음 이상 멀어지는 것이 개발 공부인 것 같다. 그래도 내가 모르는 것이 많지만 그만큼 배울 것이 많고, 알아가는 재미를 얻을 기회가 많다는 것이다(내 멘탈과 열정, 시간이 허락한다면..). 괜히 개발자는 평생 공부해야 한다는 말이 있는 것이 아닌 것 같다. 또 특정 개념을 얕게 알기만 하기보다는 왜 이런 것이 나오게 되었고, 사용하는 상황 등 좀 더 깊이 알도록 노력해보자.

이에 그치지 않고 꼭 기록을 남기자. 기록의 방법이 많지만 내가 블로그를 쓰는 이유는 내가 반드시 이해를 해야 설명을 적을 수 있기 때문이다. 난 멍청해서인지 이렇게 블로그를 쓰지 않으면 생각이라는 것을 잘 안 하게 되고 기억도 안나더라. 내 머리를 너무 믿으면 아무것도 남지 않는다.

스타트업의 장단점

나는 규모가 작은 스타트업을 다니면서 좋은 점도 있지만, 안 좋은 점도 명확하다는 것을 알게 되었다. 물론 회사마다 많이 다르겠지만, 내가 다닌 회사 기준으로 주니어의 입장인 내가 느낀 장점과 단점에 대해 적어보겠다.

장점

  1. 내가 직접 하나부터 열까지 개발하여 서비스에 적용시켜볼 수 있다.
  2. 개발하고 반영하는데 까다로운 절차가 없다.

단점

  1. 모두가 바쁘기 때문에 피드백받기가 어려워서 내가 개발을 잘하고 있는지 알 수가 없다.
  2. 업무 프로세스가 구체적으로 잡히지 않았기 때문에 비효율적인 경우가 생긴다.

모든 스타트업이 다 이렇다는 것이 아니다. 분명 작은 규모여도 체계적으로 할 수도 있고, 조직원들이 잘 성장하는 경우도 있을 것이다. 나는 어떠한 것이 좋은지 아닌지를 판단하기 위해서라면 좋은 것을 본 적이 있어야 판단이 가능하다고 생각이 든다. 즉 주니어 시절에 어떤 환경에서 시작하냐에 따라 그 둘의 성장 차이는 꽤 심할 수 있다고 생각이 든다. 그렇기에 이미 잘 갖추어진 서비스가 있는 큰 기업에서 주니어 시절을 보내는 것이 좋다고 생각한다.

그렇다고 스타트업에서 얻어갈 것이 없는 것은 아니다. 이것저것 시도해보면서 실패를 겪고 직접 코드를 짜면서 체득하는 것들이 많다. 또 front/back 상관없이 모두 다 해보고 배포 자동화 등 다양하게 해 볼 수도 있다. 결국엔 성장은 본인이 하기 나름인 것 같다.

소통의 중요성

나는 서울에 올라와서 아는 사람이 많지 않다. 하지만 커뮤니티나 다른 사람들의 블로그의 글처럼 다른 개발자들은 회사에서 어떤 일을 하고 있는지 이런 것들을 알 수 있다. 이런 정보들을 통해 내가 개발자로서 어떤 위치인지, 지금 회사에서 안주하여 도태되고 있진 않는지를 점검하게 되는 것 같다. 다시 말해 안주하고 나태해지는 것을 방지할 수 있다. 또한 주변 자극을 통해 우물 밖을 나오게 하는 원동력이 되는 것 같다. 그리고 회사에서 만나게 되는 사람들도 좋은 관계를 가지고 소통한다면 서로 많은 도움을 얻거나 줄 수 있다고 생각한다.

최근 향로님의 CTO 회고 글에서 동시대를 살아가는 개발자들의 흐름을 놓치지 말아야 한다는 말은 본 적이 있다. 정말 감명 깊게 봤기 때문에 공유하고 싶다. 열심히 연습하는 자신을 놔두고 동시대가 휙 지나가버렸다는 걸 깨달을 때의 허망함이란 말로 표현할 수 없을 것이다. 나는 과연 동시대를 살고 있을까 라는 생각이 들었다. 그렇기에 이런 동시대를 살아가기 위해선 다른 개발자들과의 소통이 중요하다는 것을 강조하고 싶다.

...

👋 마무리


이미지 출처 : https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&amp;amp;amp;blogId=hyeeun0415&amp;amp;amp;logNo=221431940439

마지막으로 하고 싶은 말은 회사에 불만을 가지기 이전에 불편함을 개선하기 위해 어떤 노력을 했는가? 라는 생각을 해보자. 나는 이런 점이 아쉽고 후회됐던 것 같다. 시킨 것만 하기 바빴고, 뭔가 이상해도 그냥 따라 했다. 그러다 나중에 그런 부분이 문제가 생겨서 다시 고생하고 반복이었다. 이러한 것들을 개선하려고 노력하지 않았다. 그렇기 때문에 내가 크게 이룬 성과가 없다는 생각이 들었다.

그 결과 "그 회사에서 어떤 것을 하셨나요?" 라는 간단해 보이는 질문에 답을 하기엔 나는 했다고 하기도 부끄러운 지식을 가지고 있지는 않는가? 생각해보자. 시켜서 했다? 그냥 구글링 해보니까 이렇게 구현되어있어서? 이렇게 답하는 것은 남 탓 밖에 더 되지 않겠는가. 항상 뭔갈 할 때에 이유(또는 근거)를 가지고 움직일 수 있도록 하자.

즉, 환경 탓만 하기보다는 내가 할 수 있는 노력은 해보자는 것이다. 불편한 것이 있으면 어떻게 하면 편하게 할 수 있을지 고민해보고, 비효율적인 것이 있으면 효율적으로 할 수 있도록 바꿔보자는 것이다.

2021년을 되돌아보면 일밖에 안 했던 것 같다. 코로나 때문이라고 하긴 애매하지만 놀러 가지도 않고 그러다 보니 자연스럽게 추억이라 할만한 것이 없다.

하지만 회사에서 6시에 퇴근을 하고 거의 매일 남아서 내가 부족했던 것, 더 알아보고 싶었던 것들을 공부하면서 나름 배움의 즐거움이랄까? 재미있었다. 알고리즘 문제를 풀면서 막혀서 화가 머리끝까지 난 상태로 집에 가는 경우도 있었지만 이러한 것들 모두 추억이라면 추억이라 할 수 있지 않을까?

앞으로 이런 열정을 잃지 않고 성장할 수 있길 바라며, 모든 개발자 분들 롱런합시다~🤞

'사는얘기' 카테고리의 다른 글

간만에 적는 회고글  (0) 2023.07.06
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