Django to ASP.NET CORE

django lobby 서버 기능 이전.

Django에서 ASP.NET CORE로 전환한 이유

Django를 포기한 이유

  • 성능 문제에서 자유롭지 못하다.
    • 측정 결과에서 많이 느렸다.
      • Web기반 게임 구동을 위해 로직 클래스를 encode-decode를 반복했는데, 이 과정이 매우 느렸다. [pickle 라이브러리 이용]
        • 게임 로직을 들고 있던 클래스 기준 pickle
          • 37ms
        • 게임 로직을 들고 있던 클래스 기준 jsonpickle
          • 42ms
        • 그렇다고 ORM기반 row 하나 get해오는 것도 빠르진 않았다.
          • 7~11ms
      • 이 문제를 해결 하기 위한 방법
        • 속도 향상
          • Pypi
            • Python 3.4는 호환 불가.
            • 심지어 Python 2.7이었다고 해도 성능 문제가 있음.
        • Django 및 python 버전 업.
          • Django 1.10, python 3.5 이용.
            • 10% 가량 속도 향상이 있었으나 미미한 정도.
        • WebSocket 으로 전환.
          • 그렇다해도 느린 Python의 단점 여전히 유효.
        • Pickle을 이용하지 않고 ORM 기반으로 전환.
          • ORM 기반도 여타 언어들 보다 느림.
        • 결론
          • Python을 사용하는 이상 어떤 선택을 하더라도 만족스러운 속도를 얻기 힘들다는 결론.
            • 다른 언어로 전환하기로 함.

Java (Netty) + Spring으로 가지 않은 이유

  • 개발팀 내에 다수 개발자가 C++과 C#쪽에 좀 더 익숙함.
    • 물론 C++ 개발자의 Java 학습 비용은 그리 크지 않은 편으로 알려져있으나, 그 학습 비용 마저 줄이고 싶었음.
  • NET CORE 환경에 대한 탐구심.
    • 기존 Mono기반의 에뮬레이팅 환경과의 차이를 체감하고 싶었음.
  • C#과 Java의 포지션이 비슷한데, 굳이 배워서까지 Java를 써야하는지에 대한 의구심.
  • Linq의 부재.

ASP.NET CORE를 선택한 이유

  • 장점
    • C# 사용 가능
      • 서버 클라 단일 언어로 통합 가능.
        • Unity3D에서의 C#이 버전이 너무 낮다는 단점은 존재.
        • SuperSocket은 아직 .NET CORE 이용 불가.
      • Linq를 통한 생산성 향상.
    • NET CORE 로 넘어오면서, cross platform 개발 및 서비스가 가능 해짐.
      • Mono위에서 구동이 아닌 dotnet 커맨드를 통한 공통된 개발 환경과 구동 환경을 지원하기 시작함.
        • 이를 위한 .net standard를 호환하는 라이브러리만 사용 가능한 상황이 됨.
          • 구축된 환경을 보면 python이나 ruby로 구축된 웹서버를 deploy하고 구동 시키는 것과 매우 유사하게 되어있음.
    • Visual Studio를 이용한 개발 가능
    • MVC 패턴을 편리하게 지원함.
      • Entity Framework를 통한 ORM.
    • 성능적 우월함.
      • 기존에도 여타 웹 프레임워크에 비해 우수했던 성능이 Core 버전으로 넘어오면서 급격히 좋아졌다.
  • 단점
    • 오픈소스권 문화가 아니라서 MS가 제공하지 않는 기능은 .NET 플랫폼 지원언어로 개발해야함.
    • .NET CORE가 아직 1.x대이며, 연관 라이브러리들도 CORE 버전은 미지원되고 있는 것들이 많음.
      • 심지어 지원되는 것들도 호환성 버그나 일부 기능이 미구현 된 경우도 많으니 테스트가 많이 필요하다.
        • 실제로, Redis나 MySQL Connector 등이 NET CORE로 미구현 된 Feature가 꽤나 많았음.

SuperSocket를 선택한 이유

  • 선정 이유
    • 빠른 응답과, 다른 티어와의 통신이 필요할 시에는 소켓 서버가 답이라는 결론.
    • 웹 소켓을 선택 할 수도 있었지만, 작업자들이 좀 더 능숙한 SuperSocket 기본 버전을 사용하기로 했음.
    • C# 소켓 라이브러리중 많이 사용됐으며, 신뢰성 있는 상태.
      • .NET CORE 버전이 있었다 손 쳐도 불안했을 듯. (너무 많은 코드가 재작성 됐을 것이므로)
  • 아쉬운 점
    • NET CORE용 라이브러리가 아직 없다는 점.
    • 멀티 스레딩 관련 최적화와 동기화는 직접 설정 해주어야 되는 점.
      • 어찌보면 장점으로 볼 수 있기도 하다.

결론

  • Linq의 편의성은 우월하다.
  • .NET CORE로 넘어옴으로써 리눅스 호환성 문제도 제거됐다
  • .NET standard용 라이브러리가 부족한 아픔은 아직 있다.
    • 심지어 주요 사용 라이브러리마저 결함이 있거나 미구현 Feature가 너무 많음.
      • 아쉬운 점이 이 것 하나만 있으므로, 올 하반기 정도가 되면 베스트한 선택중 하나로 자리잡지 않을까 생각함.
  • Visual Studio 기반 개발의 편의성.
    • Resharper까지 쓰면 더 말할 것 없는 완벽한 환경!

동적 언어와 정적 언어

나는 프로그래밍을 정적 언어인 C언어로 시작했다. 당시 지금보다 하드웨어는 비쌌다.

소프트웨어에서 성능 문제에 많은 최적화를 요구했고, 자연스레 동적 언어는 고려대상이 되기 어려웠다.

서버 비용이 비싸던 시기에 특히나 그랬다. 특히 4~5년 전만해도 C++이 아닌 다른 언어 (C#이나 Java)로 TCP 서버를 작성한다는 얘기가 나오면, 그렇게 느린 언어들로 서버 짤 수 있어요? 라는 이야기가 다수를 이뤘으니 말 다 했다.

속도와 개발자 pool이 좀 더 큰 요인이었다면, 작은 요인으로는 클라이언트 프로그래밍 언어가 C++이던 시절에, 서버로 전향하는 나같은 케이스가 많다보니 어쩔 수 없이 C++만 선택지였던 것도 있겠다.

시대의 흐름상 서버와 다대다 통신이 덜 중요한 게임들도 등장하며, 게임 업계에도 웹서버 붐이 일었다. 서버와 1대1 통신을 기준으로하면 웹서버 만큼 장점이 많은 환경도 없다.

그렇다보니 자연스레 동적 언어에 대한 니즈나 학습이 시작됐다. 나도 그 흐름에 맞춰 관심을 갖게 된 것이 예전부터 짬짬이 공부해오던 ruby를 실무에 더 밀접하게 적용하는 것이었고.

동적언어는 정적 언어보다 확실히 느리다. 인터프리터로 동작하기 때문이기도 하고, 표현력을 위해 많은 코스트를 희생하기 때문이기도 하다.

타입을 동적으로 다룰려면 C++스럽게 생각 했을 때, 최상위 Object와 같은 객체가 필요할 것이고 상황에 따른 다형성으로 동작시키게 해야 할 것이다. 가상 함수 테이블을 통한 억세스 정도의 차이가 아니겠냐고 반문 할 수 있겠지만, 그것과는 다른 문제다.

최상위 객체에 존재하는 모든 메소드를 넣고 그것에 대한 가상 함수 테이블을 만든다고 하면 가상 함수 테이블이 너무 커질 것이고, 타입 정보를 생성해두지 않고 매번 탐색한다면 그 비용은 역시나 동적 언어만큼 들 것이다.

그렇단 얘기는 컴파일이나 캐싱이 어렵다는 뜻이다. 한다손 치더라도 같은 동작이다라는 확신이 들었을 때 동작 시간 감소나 캐싱등의 일부 이득일뿐일테고.

속도상에서 메리트가 떨어지는 동적 언어가 메인 스트림 계열로 떠오를 수 있던 것은 시대의 흐름에 영향이 있다고 본다.

동적 언어가 서버 구축 언어로 떠오를 수 있는 것은 서버 비용이 싸졌으며, 확장성이 핵심이 되었기 때문이다.

C++은 국지적 최적화에는 강하지만, 확장성있게 규칙을 만들고 구현하는 것에 적합하지 않다. 또한 표현력을 확보하거나 의도를 명확히 하기 위한 공수도 많이 들며, 생각해야 될 꺼리가 많은 편인데, 이해도가 낮은 상태에서 사용하면 자칫 동적 언어보다 느린 구현을 하는 경우도 종종 발생하기 때문이다. (혹은 굳이 느리지 않더라도 복잡도를 증가 시킨다거나 하는 문제도 많다.)


지금껏 동적 언어에 대한 장점위주로 이야기했지만, 물론 정적 언어의 장점도 여럿있다. 컴파일 타임 최적화, 컴파일시 코드 검사, 정적 코드 검사에서 좀 더 많은 부분을 검사할 수 있다.

현재는 타입 유추 기능이 대다수 정적 언어에 도입된 만큼 타입에 관한 고통이 줄어든 점도 동적 언어와의 경쟁력에 도움이 될 것이다.

하지만 위에 언급한 이유들로 인해 메리트가 떨어진다. 부분적 최적화보다는 수십 수백대의 서버를 연결해 목적을 빠르고 정확하게 달성하는 것이 더 중요해진 것이다.

특히 이런 패러다임은 단일 프로세서 연산의 시대에서, 멀티 프로세서 연산, 네트워킹 기반 분할 연산으로 변화해옴과 무관하지 않다.

즉 패러다임에 따라 더 적합한 언어들이 선택되면서, 그 과정에 동적언어가 선택됐다고 봐도 된다. 특히 함수형 언어와 함수형 프로그래밍은 더더욱 메리트가 커졌고 말이다.

얘기가 돌고 돌아 현시대의 프로그래밍 패러다임 얘기까지 나왔지만, 말하고자 하는 두 타입 언어의 차이는 역시나 반비례하는 표현력과 안정성이다.

두 타입의 언어를 다룰때마다 생각보다 발상과 경험이 달라진다. 기존 코드가 만들어놓은 제약을 훨씬 더 많이 생각해야 하는 쪽은 정적언어다. 하지만 그만큼 의도를 명확히 하기도 쉽고, 정제하기 쉽다.

동적언어는 유연하지만 그만큼 안정성 확보가 어렵다.

유닛 테스트가 여타 언어들보다 동적 언어에서 더욱 유효한 이유는 동적 언어가 표현력을 얻은 대신 런타임 코스트와 빌드타임 커버리지를 잃었다. 이는 장단점이면서 동시에 특징이다.

혹자들은 언어는 수단에 불과하다지만 막상 언어를 사용할 때마다 드는 생각과 관점이 달라지는 것을 보면, 언어가 주는 특징들을 잘 알고, 상황에 맞게 사용하는게 좋지 않을까?

데이터 통계 이야기

아무래도 서버 프로그래머로 일해다 보면, 데이터에 대한 요청을 많이 받게 된다.

사실 프로그래머로써 개인적인 (그리고 꽤나 많은 서버 프로그래머 분들도 같은 생각을 가지고 있었던) 목표는 뛰어난 퍼포먼스의 서버를 만들자는 것이었다.

하지만 서버 프로그래머에게 기대하는 최우선은 최상의 안정성의 서버이지, 최상의 성능의 서버가 아닌 경우가 많다.

그럼에도 최고의 성능의 서버를 만들고 싶은 것은 일종의 로망 아닐까?

안정성과 함께 서버 프로그래머에게 기대되는! 하지만 조명 받지 못하는 그것…!

바로! 데이터 수집과, 통계, 지표에 대한 이야기를 하고자 한다.

흔히 게임 로직이나, 가용성 확보, 성능에 대한 고민,고찰은 많지만 로그 작업은 크게 중요치 않게 여겨지곤 한다.

하지만 사업적으로, 기획 적으로, 서비스 적으로…그래 인정하자. 모든 측면에서 데이터는. 로그는 중요하다.

그것도 매우.

통계는 많은 것을 알게 해준다. 물론 통계를 통한 원인이나 현상 파악에 오류가 있을 수 있으나, 통계 그 자체가 가진 오류는 아니다.

게임 접속 과정에서의 이탈 원인을 알아 내려면 게이트웨이, 패치, 로그인 어느 시점에서 이탈했는지, 패치 시간이 얼마가 소요될 때 이탈하는 지를 알아야 한다.

매칭이 오래 걸린단다. 평균 몇초가 소요되는지, 구간 별 매칭 시간, 매칭 취소율, 매칭 취소시 기다렸던 시간을 알아야 한다.

특정 퀘스트를 통한 재화가 크게 증가했다. 버그가 생겨, 무한 클리어가 되는건 아닐까? 통계는 검증과, 근거, 추정 등 다양한 역할로 쓰인다. 내가 배워온, 내가 갖게 된 지론인 “엔지니어는 가설이 아니라 증명 해야 한다”는 이론과도 일맥상통한다.

다년간 통계로 데이터를 추출, 분석 하며 깨닳은 게 있다.

첫째로, 발생한 이벤트 단위로 추적이 쉽게 구성이 되어야 한다. 즉, Timeline 구성이 가능해야 한다. 보통 컨텐츠 특수화 등 데이터가 다양하게 구성되기에 테이블이 쪼개지는 경우가 많다. join으로 처리할 수도 있지만, 단일 테이블로 관리되는게 훨씬 편리한게 사실이다. Timestamp가 초단위 이기 이기 때문에, Timestamp를 키로 못잡고 DateTime으로 잡아야하는데, 그만큼 시간 단위가 정밀해지지만, 데이터 용량이 커지기도 한다. 대안으로 두가지 방식을 제안하는데,

  1. JSON 컬럼에 상세 데이터 저장.
    • detail 이라는 컬럼을 두고 해당 컬럼에 json으로 상세 정보를 기록해둔다.
      • mysql 5.7에선 json 컬럼을 정식 지원하므로, 훨씬 더 유연하게 쿼리할 수 있는 장점이 있다.
  2. 추가 데이터는 확장 테이블에 저장.
    • 예를 들어 상점 구매로그면, 상세 상점 확장 정보는 shop_log_extend 테이블에 저장하는 식이다.

둘째로, 그룹화가 쉬워야 한다. 데이터가 행 단위로 기록되지만, 통계로는 보통 키가 되는 정보를 바탕으로, 그룹화된 정보를 요구한다. 예를 들어, 상점 통계로써 요구되는 것은 주로 상품 별 판매 횟수, 상품 별 판매 시간, 상품 별 구매자 레벨 등 상품이 키로 잡히는 것이다. 또 로그인 기록이라고 치면, 시간 별 로그인 횟수가 의미가 있고,  스테이지 클리어 정보의 경우에도 스테이지 별 클리어 횟수, 시간 별 스테이지 진행 횟수 등을 요청 받게 된다. 즉, 스테이지도 키로 잡혀야 하며, 위 세 로그에서 모두 기준이 된 시간도 키로 잡혀야 한다.

셋째로, 로그는 실시간에 근접한 데이터를 다뤄야 한다. 통계에 생명은 정확성이다. 하지만 그에 못지 않게 신속성도 중요하다. 왜냐하면, 서비스 신뢰도나, 버그로 인한 사이드 이펙트등을 빠르게 분석할 수 있으며, 해당 컨텐츠가 어떠한 반응인지도 통계로 빠르게 확보할 수 있기 때문이다.

마지막으로, 데이터 폭발에 대비 해야 한다. 유저가 많고 이벤트가 많을 수록 데이터가 폭발한다. 보통 게임 컨텐츠는 결과만 들고 있기 때문에 생각보다는 데이터가 적다. 데이터간 연관성이 적은 경우도 많아서, 행 단위 access가 이루어져 그에 따른 부하는 더더욱 작다. 하지만 플레이시 발생한 이벤트가 많을 수록, 로그 테이블은 폭발한다. 그래서 일반 RDB에 기록하긴 쉽지 않다. 로그용 DB는 RDB가 아닌 NoSQL을 사용하거나, 파일 로그로 남긴 후 background log shipping 등의 처리로 로그로 인한 부하를 줄여야 한다. 특히 인스턴스 서버 (게임 서버)는 여러개이고, 그 결과를 취합한 곳이 통계 DB가 될텐데, 여러 퍼져 있는 서버에서의 로그를 모두 취합하려면 RDB에선 쉽지 않다. 또한 검색도 쉽게 이루어져야 한다는 의미에서 NoSQL을 로그 저장소로 쓰는 방향을 추천한다.

이상 너무나 중요하지만, (특히 게임 업계에서는. 그리고 개발 과정에서는 더더욱) 비중 있게 다뤄지지 않는 통계에 대한 이야기였다.

Github Pages로 이전 후기 & 주요 글 이전 완료

Github Pages로 글을 다수 옮겼습니다.

추리고 추려 100개 정도의 글을 옮겼고, 서식이 번거로운 20개 글 정도를 더 옮기면 완벽히 마무리 될거 같네요.

옮기다보니 Markdown이 네이버 블로그나 티스토리 에디터보다 훨씬 편하더군요.

제가 위키에 글쓰는 것에 익숙해있었다는 점이 한몫 했을 겁니다만, 애초에 Markdown 서식이 그리 어렵지 않으며, WYSIWYG 에디터보다 HTML 서식등으로 꼬일 확률이 적습니다.

글마다 색다른 서식을 주게끔 꾸미지 않고 글의 내용 위주로 집중한다면, Github Pages가 좋은 선택이란 확신이 들었습니다.

특히 대부분의 글쓰기에 필요한 서식은 마련되어있어 불편함이 없어요.

표 그리기가 훨~씬 쉽다는 점도 메리트였습니다.

이전을 염두에 두고 계신 분들께 강력 추천드려요!