🤧

개발 언어

Python

Python은 내가 가장 잘 다루는 언어고, Z5 프로토타입 때까지 고려했던 Node.js에 비하면 안정적인 언어다. 선택할 당시에 매력적으로 여긴 Python의 장점은 다음과 같다:
  • 간결한 코드
  • gevent의 동기식 비동기 프로그래밍
  • 풍부한 오픈소스 생태계
하지만 결과적으로 매우 나쁜 선택이었다:
  • 처음 예상과 달리 Frontend는 CPU-bound가 되었는데 Python은 연산 성능이 안 좋기로 소문난 언어다.
  • 팀이 커지자 간결한 코드는 더 이상 장점이 아니었다. 큰 팀에선 확고하게 정적언어의 특성이 더 도움된다.
  • Python 인재를 게임 업계에서 쉽게 구할 수 없었다. C++/C# 이외의 언어로는 게임 업계 채용 시장에서 불리하다.
Python이라서 의외로 좋은 점으로는 백도어서버가 있었다. 이것 덕분에 서비스 중에도 무중단 메모리 디버깅이 가능하다. 흔하지 않은 장점이다.
처음 기술을 선정할 때만 해도 gevent가 Python 3를 지원하지 않아서 Python 2.7을 선택했다. 이제 gevent가 Python 3도 지원하므로 교체해도 괜찮다. 하지만 손봐야 할 코드베이스가 너무 크기 때문에 아직 제대로 시도하지 못 했다. 2020년 1월 1일부로 Python 2.7은 더 이상 공식적으로 지원되지 않을 것이다(PEP 373). 꽤 많은 자원을 투입해서라도 빠른 시일 내에 Python 3로 교체해야 할까? 아니면 프로젝트가 이미 성숙해있다는 판단 하에 Python 2.7에 머물러도 괜찮을까? 선택하기 어려운 문제다.

이름 짓기

Python 문화에서의 이름은 C# 문화에서의 이름보다 짧고 덜 구체적이다. KeyError vs. KeyNotFoundException
Python은 동적언어고 참조 추적이 어려워서 grep으로 찾기 쉬운(일명 "grep-able") 변별력 있는 단어를 주로 쓴다. 가령 액세서에 쓸 동사로 get보단 fetch, collect, retrieve, find, search, download처럼 좀 더 맥락에 어울리는 걸 선호한다.
흥섭식 네이밍이라고 불리는 명명 패턴이 있다. 어떤 개념을 평범한 명사의 조합으로 설명하는 대신 고유한 이름을 붙이는 걸 말한다. AnimalSimulator가 아닌 Zookeeper라고 명명한 걸 예로 들 수 있을 것이다. 나는 미시적인 곳에선 일반명사를 선호하고, 거시적이고 브랜딩이 필요한 곳에는 고유명사를 선호한다. 하지만 "거시적이고 브랜딩이 필요함"을 판단하는 기준은 사람마다 달라서 누군가에겐 부적절하게 보일 수 있다. 일례로 난 AnimalSimulator 대신 Zookeeper란 이름을 쓴 건 적절한 브랜딩으로 느끼지만 MS Orleans가 채택한 Grain(=Actor), Silo(=Host)라는 이름은 그렇게 느끼지 않는다. 프로젝트 이름에는 확실히 브랜딩이 필요하지만 그보다 작은 개념에선 이견의 여지가 많을 것이다.
한 코드베이스에서 거의 같은 개념인데 미묘하게 다른 여럿이 있을 때 이름 충돌이 일어난다. 이를테면 로깅엔 여러 층위가 있어서 단 하나의 로깅 시스템만 사용할 수 없었다. 그래서 "Log" 이외에 "Record"나 "Crumb"같은 이름도 쓰게 됐다. 코딩 동의어를 선택할 땐 가능하면 오픈소스 커뮤니티의 용례를 참고했다. Log/Record/Crumb도 용례를 참고한 것이다. 하지만 이들이 다르다는 건 알기 쉬워도, 코드를 처음 봤을 때 각각이 의미하는 바를 바로 파악하긴 어렵다(Log가 이미 있었기 때문에 Crumb을 썼을 뿐 의미론적으로 한 쪽이 더 적절한 게 아니다). 따라서 좋은 명명법이라고 볼 순 없다.
흥섭식 네이밍이나 코딩 동의어나, 어쩌면 코드베이스가 거대한 모놀리스라서 생긴 문제일 것 같다. 거대한 프로젝트를 여러 프로젝트로 쪼개서 프로젝트 명을 제외하고는 모두 일반명사를 쓰는 게 이상적이지 않을까?

다시 만들기

난 경력 0~2년차 때 스타트업 준비를 병행했다. 큰 인기를 끌진 못 했지만 독특한 SNS를 하나 출시하고 운영했다. 어느날 개발팀은 프로젝트를 새로운 언어(PHP→Python)로 포팅해야 한다는 생각에 사로잡혔다. 그렇게 복잡한 프로젝트가 아니었으니 단숨에 포팅할 수 있을 것이라 생각했다. 하지만 기존의 기능을 하나 둘 옮기다 보니 우리 머리 속에선 이미 사라졌지만 조용히 제 할 일을 하고 있던 기능이 꽤 많단 걸 알게 됐다. 끝내 포팅 계획을 완결시키지 못 했고 아무 부가가치도 만들지 못한 채 시간만 낭비했다. 패착이 하나 더 있었는데 포팅할 때 스펙을 고정하지 못 한 점이다.
이 경험으로 다시 만들기라는 결정은 반드시 실패한다고 생각하게 됐다. 코드베이스엔 우리가 잊고 있는 기능이 수도 없이 많을 것이며 성과에 대한 압박이나 풀타임 기획자(게임디자이너)가 있는 프로젝트에서 스펙을 고정하는 건 불가능하다. 듀랑고 개발 중기에 Python을 선택했던 걸 후회했음에도 언어를 바꾸지 않고 끝까지 밀어붙인 건 이 때문이다.
이 점에서 마이크로 서비스 아키텍처에 대한 미련이 남는다. 모놀리스식 코드베이스에선 모든 기능이 서로 복잡하게 얽혀있어서 부분적으로 기반기술을 교체하는 게 사실상 불가능하기 때문이다. Python을 후회했을 때 언어를 바꾸진 않더라도 모듈의 경계를 뚜렷이 쪼개고 기능 간 연결을 느슨하게 만들려고 노력했다면 어땠을까?

발목 잡히기

클라우드 인프라 제공자인 AWS, Azure, GCP 중 AWS는 절대적인 1위다. 이 지위가 앞으로 꺾일 것 같진 않다. 우리는 AWS와 긴밀히 협업해왔고 AWS의 기능을 잘 활용해왔다. AWS와의 독점적인 우호적 관계 덕분에 남들보다 비용도 덜 낸다. 하지만 AWS에 발목이 잡혔다. 그래서 중국에 출시하려면 생각해야 할 것이 많다.
AWS는 cloud-native를 지지한다. AWS를 기본 빌딩블록으로 취급해서 가장 최적의 사용법을 추구하라는 것이다. 업계 3위인 GCP는 발목 잡히기(lock-in)를 경계하라고 한다. 자사 기술은 대부분 오픈소스니까 당장 GCP를 쓰더라도 나중에 다른 클라우드 인프라로 옮길 때 문제가 없다고 한다.
난 두 관점 사이에서 예전엔 전자를 지지했고 지금은 후자를 지지한다. 후자를 지지하게 된 이유는 다음과 같다:
  • 비즈니스 요구사항이 정말 빠르게 바뀐다.
  • 내가 선택한 기술이 도태되기도 한다. 당시에 탁월한 선택지였어도 발전이 멈추거나 더 좋은 기술이 새로 나타날 수 있다. 가장 늦게 나온 Kubernetes는 기존에 있던 Docker Swarm과 ECS를 압도했다.

따로 만들기

gauge, lets, tossi, profiling 등 k1server에서 사용하는 일부 기능은 별도의 오픈소스 저장소로 분리돼있다. 난 작은 기능을 라이브러리로 떼내어 오픈소스화하는 걸 좋아해왔다. 이렇게 할 때 다음과 같은 장점이 있다:
  • 경계가 좁은 만큼 품질을 높이기 쉽다. 충실한 문서화와 100% 테스트 커버리지 달성이 가능하다.
  • 집에서도 개발하고 싶은 열정을 발휘할 수 있다.
  • 그 자체로 오픈소스 포트폴리오로 쌓인다.
다만 개인기에 지나치게 의존적이 되어 인수인계에 더 큰 어려움이 생기는 것 같다.