필자는 고등학교 1학년 말에 처음으로 백엔드 포지션에서 프로젝트를 진행했다. 내 블로그에서 자주 우려먹는 교내 공식 기숙사 관리 시스템인데(지금 파업 상태다 ㅡㅡ), 같은 동아리였던 선배와 함께 Vert.x라는 서버 프레임워크가 지원하는 Vert.x-Web 툴킷으로 Java 8 런타임에서 돌아가는 웹 어플리케이션 서버를 개발했었다. 지금 생각해보면 참 많은 문제가 있었다.

배포 방식

첫 번째는 배포 방식이다. 테스트 코드가 없는 건 둘째 치더라도, 신규 기능 개발이나 버그 해결을 하고 배포하기까지 사람 손이 정말 많이 들어갔다.

  1. Maven으로 WAS를 .jar 형태로 빌드한다.
  2. WinSCP라는 FTP 클라이언트로 서버에 원격 접속한다.
  3. jar 파일을 덮어쓴다.
  4. Putty라는 SSH 클라이언트로 서버에 원격 접속한다.
  5. screen으로 돌아가고 있는 WAS 세션에 들어가서 서버를 껐다 켠다.

다운타임 문제도 있고, 자동화할 수 있는 부분이었지만 자동화할 생각을 하지 못했다.

물리 서버가 한 대

리눅스에서 물리적인 터미널을 가상 터미널 여러개로 다중화해주는 screen이라는 도구를 써서, screen 하나는 서버 돌리고, 하나는 DB서버 돌리는 식으로 운영했다. 이게 사실 비용적인 문제 때문에 어쩔 수 없긴 했지만, 물리 서버가 한 대인 게 문제임을 인지하지 못했던 것이 더 컸다. '서버가 한 대인데 그게 고장나면?'이라고 누군가 질문하면, '아 그럼 고쳐야죠'라고 대답했을 것 같다. API 서버와 DB 서버를 분리해야 하는 필요성을 느낄만한 경험도 없었고, 아직 혼자 뭔가를 알아보기 어려운 내게 누군가가 가르쳐주지도 않았으니 말이다.

테스트 코드 없음

테스트를 코드가 아니라 PostMan으로 했다. happy path(가장 이상적인 로직)에 대해서 요청 몇 번 해보고, 잘 되면 배포했다. 이런 습관 때문에 문제가 아주 많았는데, 한가지 예를 들면 사용자가 2015년 이전의(파싱된 데이터가 없는 날짜의) 급식 정보를 로드하려 할 때 서버가 500을 내려줬던 적이 있었다. 테스트 코드가 없는 서버를 운영하는 것에 대해서는 말이 더 필요할까 싶다.

브랜칭 모델, 리뷰 없는 배포

먼저, 기능 개발이나 이슈 대응에 대한 별도의 티켓팅 과정이 없었다. 같이 서버를 개발하던 선배와 메신저로 얘기하면서 작업을 분배했고, 나는 할당받은 작업들을 wunderlist라는 to-do list 앱에 적어두고 하나씩 진행했다. 작업은 master 브랜치에서 진행했고, 배포는 리뷰 없이 각자 알아서 진행했다. 아래는 2017년 11월에 적어뒀던 작업 목록이다.

까먹어서 처리하지 못한 이슈가 생길 때도 있고, 우선순위 조절도 매우 힘들었다.

로그 시스템 없음

Vert.x라는 프레임워크에는 미들웨어 개념이 있음에도 불구하고 제대로 된 로그 시스템을 구축하지 않았다. '제대로 된 로그 시스템'이라고 한 이유는, 파일 형태로 요청 로그를 남기기는 했기 때문이다. 아무튼 '로그를 남겨서 뭐하나' 싶었다. 로그 시스템이 없다는 건, 서버에 에러가 나고 있어도 인지하지 못한다는 것이다. 따라서 버그는 '보고 받는' 식으로 발견했다. 사용자가 원하기 전에 먼저 대응해두는 것이 더 좋은 흐름인데 말이다.

raw string 형태의 쿼리

코드를 보면 이해가 될 거다. ORM이 논쟁이 많은 기술이고 뭐고 다 떠나서 ORM을 적용했더라면 저 때 조금은 덜 열불나지 않았을까 싶다. 물론 저 때엔 문자열 결합으로 쿼리 문자열을 만들어내고 나서 '와 진짜 잘만들었다'라고 생각했다.

그 외에도 API 문서가 엑셀로 정리되어 있다거나, 서버가 살아있는지 체크하는 health check가 없다거나, 데이터베이스 백업 안해 뒀다가 쿼리 잘못 날려서 계정 정보 다 날아가거나 하는 것도 있었다. 지금 보면 왜 저랬나 싶은데, 당시엔 그게 당연한 건줄 알았다.

왜?

백엔드를 조금 경험해본 사람이라면 사용자를 어떻게 인증할지, 암호화는 어떻게 할지, API 문서는 어떻게 작성해야할 지 같은 걸 비교적 쉽게 의사결정할 수 있지만, 그렇지 않은 사람은 이런 게 필요하다는 것조차 깨달을 수 있는 경로가 별로 없다.

  1. 어플리케이션 레벨의 경우, 언어와 프레임워크의 선택지가 너무 다양해서 예제를 찾기 힘들다. 안드로이드는 Java나 Kotlin, iOS는 Objective-C나 Swift, 웹 프론트엔드는 React, Vue처럼 그래도 손에 꼽는 수준인데, 백엔드는 그렇지 않다. Java+Vert.x 예제는 정말 찾기 어려웠다. 요즘 갖고 노는 Python+Sanic도 그렇고.
  2. 당연하겠지만, 애초에 production level의 코드를 뜯어볼만한 기회가 별로 없다. 웹이라면 개발자 도구를 켜고, 앱은 종종 GitHub에 올라오는 코드로 얼추 알아볼 수 있을텐데 말이다.
  3. 백엔드는 코드만 봐서 되는 포지션이 아니다. 배포와 배포 패턴, 모니터링, 로깅, 스토리지, 트래픽 변동의 대응처럼 코드 외적으로 신경써야 할 부분이 많다.
  4. 어느 기술이던 경험해보지 않으면 필요성을 못 느끼는 것이 맞다. 그러나 백엔드는 그런 경험조차 해보기 어렵다. 예를 들어, 변동이 적은 데이터는 캐싱해 두면 좋지만, 굳이 캐싱해두지 않아도 데이터베이스에 쿼리하는 시간이 워낙 짧으니 필요성을 못 느낀다. 학교에서 조별 과제로 하는 프로젝트는 사용자가 매우 적거나 없으니 말이다. 로깅도 그렇고, 백업도 그렇고, 데이터베이스 서버 분리도 그렇다. 동기부여의 기회가 별로 없다.

이런 배경에서 어째저째 백엔드 엔지니어라는 이름을 단 채 일하기 시작하고 나니, 이런 걸 도와줄 수 있는 컨텐츠를 만들어보고 싶었다. 필자의 경우엔 뭔가 하나 깨우치는 걸 혼자 하려면 엄청 어려웠는데, 누가 옆에서 5분이라도 설명해 주면 꽤 쉬웠던 것 같아서, 그런 설명충 컨텐츠를 기획했다. 고등학교 곧 졸업하는데 백엔드 좋아하는 후배들한테 뭐 하나 남겨주고 싶기도 했고, 맨날 SQL이나 파이썬 얘기만 하다가 좀 색다른 컨텐츠를 진행해보고 싶기도 했다. 

배포 전에 테스트를 돌리고, 배포를 자동화하고, 로그를 남기고, 시각화하고, 로그 파이프라인 중간에 버퍼나 proxy를 끼워넣는다거나 하는 게 누군가에겐 당연한 일일 수도 있겠지만, 그렇지 않은 사람들이 많다. 그래도 배경지식이 좀 생기면 혼자 찾아볼 수 있는 능력이라도 생기니, 같이 프로젝트 하나 해본다고 생각하면 될 것 같다. 컨텐츠 제목이 좀 싸가지 없어 보일 수도 있는데, 그냥 저게 평소 필자의 말투라서 저렇게 지은거고 딱히 뭐 의미를 부여하고자 한 건 아니다.

이 컨텐츠의 룰

'백엔드가 이정도는 해줘야 함'이라는 컨텐츠는,

  1. 친구 서너명과 '게시판 기반의 커뮤니티 포털' 프로젝트를 진행해서 서비스화 시킨다고 가정한다. 이 배경을 기반으로, 백엔드의 관점에서 기술스택 의사결정부터 시작해 '백엔드가 이정도는 해줘야하지 않나'싶을 때까지 이것저것 붙여볼 것이다.
  2. 의사결정은 '다 ~로 하니까'가 아니라 상황에 맞춰 유동적으로 해볼 것이다. 트렌드를 따르거나, 추측을 기반으로 한 기술 선택은 멍청한 것 같다는 생각이다. '로그 분석에 ELK가 일반적이어서'같이 '당연히 이래야지'같은 이유로 기술을 도입하는 것은 본인에게나 조직에게나 좋지 않은 것 같다. 컨텐츠를 진행하며 계속해서 '내 결정이 옳다는 근거는 무엇인가?'라는 고민을 글로 옮길 것이다.
  3. 프로젝트에서 이런저런 외부 도구들을 쓰기 때문에, 백엔드로 프로젝트에 두세번 정도 참여해 본 사람 정도의 수준에 맞춰서 진행한다. 컨텐츠의 후반으로 갈 수록 이런 경향이 심화될 것이다. git, SQL, HTTP 등을 경험해본 적 없다면 초반 몇 챕터를 읽는 것도 어려울 수 있다. A to Z 모두 설명해보곤 싶지만, 필자의 역량이 부족하기도 하고, 그렇게 들어가기 시작하면 끝이 없기도 하니 이건 각자가 여기저기서 알아보며 공부하도록 하자. 주는대로 받아먹기만 잘하는 사람에겐 어울리지 않는 내용일 것이다.
  4. 조직 내 문서화 툴, 메신저와 같이 '프로젝트 팀 전체'에 해당하는 의사결정은 하지 않을 것이다.
  5. 의사결정의 흐름을 부드럽게 하고, 설명을 더 알차게 하기 위해 되도록 필자가 가장 잘 쓰는 기술을 선택할 것이다. 의사결정에서 '조직에게 가장 익숙하다'라는 이유를 붙이는 경우가 그런 예다. 물론 정말로 '이 기술이 조직에게 익숙한가'는 의사결정의 기준이 되어야 하는 것이 맞지만, 이 컨텐츠에 한해서는 '익숙함'이라는 기준이 의사결정의 우선순위가 조금 더 높을 뿐이라는 것이다.
  6. 모든 의사결정은 배경과 요구사항-선택지-의사결정 순서의 소분류로 나누어 정리할 것이다.

컨텐츠를 생성하는 필자의 배경과 관점 안에서 의사결정이나 코딩을 하게 될테니, 주관적인 부분이 조금 있을 것이다. 그러니 '얘는 프로젝트에서 백엔드를 이렇게 하는구나' 하며 이해해도 될 것 같다. 무작정 싹 다 설명하면서 도와주기보단 자립을 돕는 식으로 글을 쓰도록 노력해 보겠다. 모바일 기준 7~8화면 정도 분량으로 4~50편 정도에 걸쳐 진행될 예정이며 적어도 2019년 말까진 글이 활발히 올라올 것 같다.

컨텐츠를 진행하며 의사결정이 필요할 때마다 '이 방법은 이래서 구리고, 저 방법은 이래서 선택하지 않겠다'같은 이야기를 많이 하게 될텐데, 필자가 '구리다'라고 하는 걸 본인이 썼거나 쓰고 있다고 해서 부끄러워할 필요는 없다. 다 필자의 주관적인 생각이고 '더 나은 방법'은 각자가 고민해야 하는 일이니까, '더 나은 방법이 없을까?'하는 고민을 멈추지만 말자. 그리고 가끔 가다가 예의없이 대충 설명하고 넘어가는 부분이 분명 있을테니, 맘에 안 드는 부분이 있다면 어떤 경로로든 얘기해주기 바란다. 더 좋은 글을 작성하는 데엔 독자의 피드백만한 게 없다.

Python 카테고리를 새로 생성했다. 언어론을 조금 배우며 더 나은 설명이 떠오른 부분도 많았고, 입문자를 너무 고려한 탓에 어려운 개념을 일부 배제하느라 제대로 설명하지 못한 부분도 있었다. Python-New 카테고리는 파이썬에 대해 조금 더 확실하고 명쾌한 설명을 블로그에 올리기 위해 생성하게 됐다. 기존 파이썬 카테고리와 설명이 일부 겹칠 수도 있으나, 적어도 설명이 줄어들진 않을 예정이다.

Java, C# 등 대부분의 주류 프로그래밍 언어들처럼 Python도 C로 구현되었다. Python은 아래와 같은 특징을 가진다.

  • 오픈 소스
  • 인터프리터
  • 별도의 entry point가 없는 스크립트 언어
  • 동적 검사, 암시적 형변환이 없는 강타입, 동적 타이핑
  • 암시적 타입 추론
  • 타입 명시를 지원
  • 멀티 패러다임이면서, class로 객체지향을 표현. 다중 상속 허용
  • 연산자 오버로딩 존재
  • Reference counting과 가비지 컬렉션 기반의 메모리 관리
  • 공식 코드 컨벤션 가이드라인 존재(PEP8)
  • optional한 세미콜론(그러나 자바스크립트 스타일의 optional과는 조금 다름)
  • 대부분의 프로그래밍 언어들처럼, Lexical(정적) scoping
  • function level scoping
  • 의존성 관리와 가상 환경 구성에 대한 지원이 npm만큼 강력

그리고 파이썬의 높은 생산성에 도움을 주는 몇가지 syntatical sugar와 기능들이 존재한다.

  • for statement는 foreach로만 사용 가능
  • 비교 연산자 chaining 가능(a < b < c)
  • switch가 없음
  • 가독성을 위해, 논리 연산자에 기호가 아닌 키워드를 사용(&&를 and로, ||를 or로, !를 not으로)
  • 매직 메소드로 인해 생겨나는 강력한 데이터 모델
  • 매우 큰 라이브러리 생태계
  • Glue language라는 개념을 통해, 타 언어와의 바인딩이 쉬움

문화, 강력한 직관성

내가 파이썬을 좋아하는 이유는, 바로 '가장 아름다운 하나의 답이 존재한다'라는 명제와 코드가 영어 아티클처럼 읽히는 강력한 직관성 때문이다. 높은 생산성과 가독성이 나오는 것도 이 때문이다. 인간의 생각을 코드로 가장 쉽게 표현할 수 있도록, 파이썬은 '쉬운 언어'로 진화해 왔다. PEP8이라는 스타일 가이드에 의해 코드 스타일도 단 하나의 스타일로 진화되어 왔다. JavaScript 진영에서 수많은 코드 스타일이 존재하는 것과 사뭇 다르다. '정해진' 것들이 있기 때문에 선택지는 좁을지 몰라도, 가이드라인만 따르면 코드를 확실하게, 정교하게, 가독성 높게 작성할 수 있다.

또한 언어 자체가 웬만하면 가독성이 높을 수밖에 없게 설계되어 있다. 아래의 코드 스니펫을 보면 파이썬이 얼마나 직관적인지 알 수 있다.

파이썬은 쉽다?

파이썬은 쉬운 게 맞다. 진입장벽이 낮기도 하고, 애초에 '쉬운 언어'를 만들고자 하는 의도로 만들어진 언어기 때문이다. 그러나 잘 쓰기는 어렵다. 예를 들어, 숫자들이 공백으로 구분되어 있는 문자열에서 최소값을 출력하는 코드는 아래와 같이 작성할 수 있다.

이 코드는 아래처럼 개선할 수 있다.

아래와 같은 방법도 있다.

발상의 전환이나, '이 코드가 최선인가'하는 압박이 다른 언어들보다 더 많아서, 번아웃이 오는 빈도도 많다. 따라서 우리가 취해야 할 폼은, 책 하나 다 읽었다고 '다 끝났다'라는 생각을 하지 말아야 하는 것이다. 적어도 프로그래밍 언어를 '다 안다'라고 할 수는 없으니 말이다.

구현체

Python에는 여러 구현체들이 있다. 다른 언어를 하던 프로그래머 입장에서는 'Java 7, 8, 9같은 차이겠거니' 할 수 있다. 그러나 여기서 이야기하고자 하는 건 표준 배포판인 CPython이 C로 개발되는 것과 다르게, 아예 다른 프로그래밍 언어를 이용해 구현된 파이썬이다. 이게 다 파이썬이 오픈소스여서 가능한 일이다. 파이썬 배포판의 대부분도 오픈소스로 개발되고 있다.

CPython

'파이썬'이라고 하면 대부분 CPython을 뜻한다. 파이썬 재단에서 인정하는 표준 구현체이며, python.org에 들어갔을 때 보이는 예제들이나 'Download Python'도 CPython을 기준으로 한다. Python 코드를 바이트코드로 컴파일하고, 그 결과를 VM이 해석하는 방식을 사용한다.

Cython

정적 컴파일러 최적화다. Type mixin이라는 개념을 통해 C나 C++ 모듈로 컴파일한다.

IronPython

.NET FrameworkMono 기반에서 작동할 수 있도록 C#으로 개발된 파이썬이다.

Jython

JVM 기반에서 작동할 수 있도록 Java로 개발된 파이썬이다.

배포판

이것도 다른 언어를 하던 프로그래밍 입장에서는 이해하기 어려울 것이다. 파이썬은 사용하는 대상의 스펙트럼이 매우 넓기에(머신러닝, 웹 서버, 데이터 분석 등) 특정 집단을 타겟으로 하는 별도의 배포판이 존재한다. 데이터 과학을 위한 Anaconda, ActivePython, Enthought Canopy, WinPython 등이 그 예다.

설치

Java 8, 9, 10처럼, 파이썬도 버전 개념이 존재한다. Python 2Python 3가 대표적인 메이저 버전이다. 별다른 이유가 없다면, Python 3을 사용하는 것이 좋다. 애초에 Python 3가 Python 2에서 지적된 여러 단점들을 제거하기 위해 만들어졌기 때문이다. Python 3가 나온 지 얼마 안 됐던 때에는 대부분의 라이브러리가 Python 2만을 지원했었기에 Python 3를 꺼려하는 개발자가 많았지만, 요새는 그런 경우가 거의 없다.

Windows

Windows의 경우에는 공식 페이지인 python.org에서 설치할 수 있다. 별도로 설정을 건드리지 않으면 PATH도 알아서 잡아 주므로 터미널에서 python 명령을 입력했을 때 인터프리터가 정상적으로 동작하면 설치에 성공한 것이다.

$ python

Mac

Mac은 Linux 기반의 운영체제기에, 기본적으로 파이썬이 설치되어 있다. Mac을 비롯한 리눅스, 또는 리눅스 기반 운영체제들은 Python 2와 Python 3를 별도로 관리함에 주의해야 한다. 터미널을 기준으로 하면, Python 2는 python, Python 3는 python3 명령을 사용한다.

$ python
$ python3


'프로그래밍 > Python' 카테고리의 다른 글

산술 연산자  (0) 2019.01.28
숫자 자료형과 리터럴  (0) 2019.01.25
파이썬의 타입 시스템과 빌트인 타입, 변수 선언  (0) 2019.01.23
주석  (0) 2019.01.07
Hello World, 세미콜론은 optional하다?  (0) 2019.01.03

아래 명령은 설치된 모든 패키지를 upgrade합니다.

$ pip install --upgrade $(pip list | egrep -v 'Pack|----' | awk '{print $1}')

아래 명령은 pip, wheel, setuptools를 제외한 패키지를 모두 제거합니다.

$ pip uninstall $(pip list | egrep -v 'Pack|----|pip|wheel|setuptools')

'Python 계열 > Python 레거시 글' 카테고리의 다른 글

requirements.txt로 협업 상황에서의 의존성 관리하기  (0) 2018.11.07
pip  (0) 2018.11.05
__slots__  (0) 2018.11.03
Context Manager  (0) 2018.11.01
Argument Unpacking  (0) 2018.10.30

+ Recent posts