오픈서치로 시작한 임시변통 로그 집계 환경

오픈서치에 적재된 로그는 여러 가지 실험을 해볼 수는 있었지만 실제 사용 가능하지는 않아 다른 방법을 고안해야 했습니다.

오픈서치로 시작한 임시변통 로그 집계 환경

게임을 서비스 할 때 로그는 고객들의 말과 행동의 차이를 인식해 앞으로 서비스 해 나갈 방향을 설정하는데 엄청난 도움을 주는 수단입니다. 서비스를 지속하기 위해 직접 이야기하는 고객들의 의견을 적극적으로 들어야 하지만 동시에 로그를 통해 실제 게임에 일어나고 있는 일을 확인해 이들 사이의 차이가 생기는 이유를 어느 정도 설명할 수 있어야 합니다. 그렇지 않으면 어느 한 쪽에만 의존하다가 잘못된 의사결정을 내려 고객 입장에서는 누가 봐도 명백히 이상한 정책을 밀어붙이다가 서비스를 망가뜨릴 수도 있습니다. 어느 게임이라도 서비스 중이라면 로그를 꽤 잘 남기고 있을 테고 개발 중이라도 로그를 붙여 개발 도중에 수행하는 여러 테스트로부터 직접 보이지 않는 사람들의 행동을 밝혀내고 있습니다.

주로 큰 회사에서는 로그 수집, 분석, 의사결정지원 등을 수행하는 별도 부서가 있고 이들이 생산하는 정보는 기밀에 해당했기 때문에 개발팀에 소속된 스탭들이 쉽게 보기는 어려웠습니다. 개발팀에서는 그저 전담 부서에서 제공한 인터페이스를 통해 로그를 보내고 로그가 정상적으로 남는지 확인하며 로그에 남길 상황과 값들을 정의하는 정도가 로그에 대해 할 수 있는 일의 거의 전부였습니다. 사실 로그에 기반한 보고서가 기밀인 이유는 사용자 수, 매출 같은 숫자들을 유추할 수 있기 때문인데 이를 머리로는 이해하지만 이것 때문에 개발팀이 로그 전체에 접근할 수 없는 상황을 이해하기는 쉽지 않습니다. 처음에는 순진하게 미래에 개발팀에서도 로그에 접근할 수 있을 거라고 예상하고 고객들의 행동을 따라갈 수 있도록 준비했지만 막상 서비스를 시작하자 우리들은 로그에 접근할 권한이 전혀 없었고 전담 부서에 아주 구체적으로 보고 싶은 데이터 모양을 전달해야만 상당한 시일이 흐른 다음에야 정제된 데이터를 볼 수 있을 뿐이었습니다.

개인적으로 로그 데이터 분석은 시도와 실패를 계속해서 경험해 가는 과정을 통해 올바른 방법을 탐색할 수 있다고 생각합니다. 대략 답해야 하는 질문을 알고는 있지만 애초에 이 질문이 올바른 질문인지, 이 질문에 답하면 의사결정에 도움을 받을 수 있을지는 데이터를 직접 봐야 알 수 있습니다. 그런데 외부 부서에 원하는 데이터 모양을 정확히 요청하면 정확히 그 모양으로만 데이터를 받을 수 있는 환경에서는 시도와 실패 사이 간격이 너무 길어 그 업무에만 집중할 수 없었습니다. 또 외부 부서에 협조 요청을 하는 형태로 일을 진행하다 보면 서로의 우선순위가 달라 너무 늦은 시점에 회신을 받기도 하고 또 여러 가지 시도에 위축되기도 해서 결국은 로그로부터 도움을 받으려고 하던 생각을 접게 만듭니다. 다른 팀들도 비슷한 상황이었는지 로그를 보는 대신 커뮤니티에 올라오는 고객들의 목소리에 집중했는데 이 목소리는 의미가 없지는 않지만 로그와 함께 교차 검증하지 않은 채로 의사결정에 사용하면 위험합니다.

한편 작은 팀이나 작은 회사에서는 로그를 별도로 관리할 조직이 없고 또 로그 탐색을 전담할 사람을 설정하기도 어려워 이 업무 전체가 기획팀에 할당될 때가 많았습니다. 많았다기보다는 항상 그랬습니다. 한번은 인게임에서 일어나는 온갖 이벤트를 텍스트 형식으로 남겼는데 명절 연휴 동안 서버를 열어 사전 신청한 고객들을 대상으로 비공개 테스트를 한 적이 있는데 테스트 기간 동안 게임 서버의 남은 스토리지가 로그 텍스트 파일로 가득 차 서비스가 중단된 적도 있습니다. 임시로 매일 사용자가 가장 적은 시간대에 로그를 다른 기계로 보내고 게임 서버로부터 삭제했는데 한 기계에 모인 텍스트 파일은 거대한 크기였고 별 생각 없이 이 데이터를 엑셀로 불렀다가 엑셀은 이런데 쓰라고 만든 도구가 아니라는 사실을 아주 처절하게 배웁니다.

마지막 아르바이트의 유산을 통해 SQL을 그럭저럭 다룰 수 있었는데 이를 알게 된 기술 책임자님이 Log Parser라는 도구가 존재한다는 사실을 알려주셨고 그 때부터 팀에서 게임서버로부터 나온 로그로부터 통계를 만들어 보고하는 일을 추가로 담당하게 됩니다. 로그 파서는 거대한 텍스트파일을 대상으로 단순한 모양의 SQL 쿼리를 사용하게 해 주는 프로그램인데 데이터가 너무 커 애초에 엑셀로는 열 수도 없는 상황이라면 이 프로그램을 사용해 기초적인 통계를 낼 수 있었습니다. 엄밀히 말하면 이 도구만으로 기초 통계를 내는 건 썩 좋은 아이디어가 아닙니다. 애초에 이 도구는 로그가 너무 거대하기 때문에 엑셀 같은 더 편안한 프로그램을 사용할 수 없는 상황에서 로그를 전 처리 해 그나마 엑셀 같은 도구에서 불러올 수 있는 크기로 만드는 역할을 하기에 더 적합한 도구였습니다. 하지만 그 시대의 엑셀은 데이터를 최대 6만 5천 라인 까지만 처리할 수 있었고 조금 긴 검색과 조금 긴 통계 연산을 시도하면 컴퓨터 전체를 몇 분에 걸쳐 멈추게 만들었기 때문에 단순히 엑셀로 넘어가기 위한 전 처리 과정에만 사용할 수 없었습니다.

고민 끝에 로그를 통해 답해야 하는 질문에 대해 로그파서를 통해 실행할 쿼리를 미리 만들어 놓고 윈도우 배치파일을 통해 통계 쿼리 여러 개를 한번에 쭉 실행한 다음 결과를 텍스트 파일에 넣도록 하고 아침에 출근해 이 결과를 엑셀에 붙여 넣고 높은 분들이 보기에 적당한 포멧으로 바꿔 보고하는 체계를 만들었습니다. 전체를 보면 각 게임 서버는 텍스트 포멧으로 서버의 로컬 스토리지에 텍스트 파일을 쌓고 매일 새벽에 이 파일들이 모두 한 기계의 스토리지로 옮겨집니다. 그 다음에는 배치파일이 실행되어 새로 나타난 텍스트 파일을 대상으로 기초 통계 쿼리 여러 개를 알린 결과를 텍스트 파일에 저장하고 이 파일을 메일로 담당자인 제게 발송합니다. 아침에 출근해 메일에 첨부된 텍스트 파일을 엑셀에 붙여 넣고 보고서를 만들어 메일로 발송하는 식으로 한동안 동작했습니다.

나중에는 이 체계에 기반해 로그를 조회하는 php 웹사이트를 만들었는데 제대로 된 운영툴을 회사로부터 지원 받기 전까지 기획에서 로그 조회 웹사이트를 관리하는 일도 해야 했습니다. 사실 이 경험은 그렇게까지 나쁘지는 않았는데 자주 요청하는 보고서는 이미 새벽에 집계해 놨기 때문에 뒤에서 로그파서가 돌아가는 것 치고는 빨랐습니다. 물론 최신 로그를 포함해서 조회하는데는 상당한 인내심이 필요하기는 했지만요. 또 큰 회사에서는 잘 볼 수 없는 로그를 직접 만져보고 일 하다가 더 이상 집중력을 유지할 수 없을 때 로그파서를 열고 여러 가지 집계를 시도해 보고 또 데이터를 샘플링 해서 엑셀에서 처리할 수 있는 수준으로 스케일을 줄인 다음 단순 연관분석을 해 서로 연관이 있는 값들을 탐색하며 새로운 질문을 만들고 또 스스로 질문에 답하는 과정을 경험할 수 있는 기회였습니다.

이번에는 프로젝트의 목표 중 하나가 이미 만들어진 것을 다시 만들지 않는 것입니다. 이전에는 규모가 큰 게임 서비스를 만들 때 새로 나온 기술이 있더라도 직접 검증하지 않은 하는 이를 신뢰하는 대신 회사에서 이전에 사용해 온 검증된 오래된 기술과 오래된 방법을 주로 사용합니다. 이는 새로운 서비스를 개발할 때 기술적 위험을 줄여주기는 하지만 현대에 더 생산성이 높은 방법을 시도하기 어렵게 만들고 항상 오래된 방법에 근거한 생산성에 머무르는 결과로 돌아오지만 이는 규모를 통해 극복합니다. 엑셀을 위한 기형적 데이터 정의에서 소개한 20년 넘게 사용해 온 엑셀 데이터 모양을 어떻게든 유지하기 위해 큰 개발 비용을 들이거나 엑셀 모양이 아닌 데이터를 정상적인 상태가 아니라고 판단하는 사람들이 나타나는 원인이 되기도 합니다. 이에 비해 이번에는 남들이 만들어 놓은 것은 최대한 가져다 쓰고 생산성을 높일 수 있다면 일단 빨리 시도해보고 실패 여부를 빨리 판정하는 관점으로 접근하고 있습니다. 그래서 이전에 한 번도 본 적 없는 환경에 로그를 남기기 시작했는데 이게 바로 오픈서치입니다.

기술 부서에서 오픈서치에 로그를 남기기 시작한 다음에야 오픈서치라는 서비스에 대해 알게 됐는데 기술적 배경이 거의 없는 게임디자인 관점에서 대강 살펴보고 현대에 NoSQL이라고 부르는 스키마 없이 키 밸류 기반으로 데이터를 쌓는 도구라고 인식했습니다. 이전에 몇몇 사례에서 SQL 기반으로 데이터를 불러오고 집계해 보기는 했지만 이 때 까지만 해도 NoSQL은 이름만 알고 있을 뿐이었고 이에 기반한 데이터베이스에 보내는 쿼리 모양을 본 적도 없었습니다. 하지만 이미 잘 모르는 환경으로 구현되고 있었고 새로운 일 하는 방식에 적응하기를 해야 할 참이었습니다. 그런데 환경을 살펴보니 새로운 쿼리 쓰는 방식과 기술적 한계가 거의 같은 환경에서 전통적인 SQL을 사용할 수 있는 쿼리 워크벤치라는 기능이 있다는 것을 알게 됐고 새로운 일 하는 방식을 배우기를 재빨리 그만 두고 이 기능을 활용하기 시작합니다.

개발 환경에서 쿼리 워크벤치를 통해 로그를 조회하고 집계하는 일은 꽤 단순했는데 NoSQL 환경에 키 밸류 기반으로 데이터를 쌓고 또 스키마가 없다 하더라도 결국 값에 이름이 있고 키 밸류 구조에 기반해 기록된 데이터는 결국 SQL 쓰는 사람 입장에서는 그냥 키 하나하나가 일차원으로 펼쳐진 커다란 이차원 테이블을 조회하는 것과 별로 다르지 않았습니다. 그래서 그냥 큰 테이블을 조회하는 느낌으로 쿼리를 보냈고 의도대로 잘 작동했으며 속도도 빨랐습니다. 하지만 장점만 있지는 않았는데 일단 쿼리 워크벤치는 베이직 쿼리밖에 지원하지 않았습니다. 아주 기초적인 집계 함수만 사용할 수 있었고 조회 쿼리를 중첩해서 사용할 수도 없었고요. 중첩이 안 된다는 말은 어떤 조인도 직접 할 수 없다는 말입니다. 그래서 이전에 로그파서가 그랬던 것처럼 쿼리 워크벤치는 엑셀에서 데이터를 조인하기 위한 기초 데이터를 뽑는 전 처리 용도로만 사용하고 실제 조인은 엑셀에서 수행했는데 그 사이 엑셀은 100만 라인 이상의 데이터를 처리할 수 있게 됐지만 하드웨어의 발전에도 불구하고 속도가 별로 개선되지 않아 여전히 큰 집계를 요청하면 엑셀 전체가 얼어붙은 채로 몇 십 초 동안 응답하지 않는 상황을 겪었습니다.

편리한 집계 환경을 구축하기 전까지는 그래도 쿼리 워크벤치와 엑셀에 기반한 워크플로우를 구축하려 했지만 이런 시도를 해서는 안됨을 알게 해 준 사건이 있었습니다. 첫 비공개 테스트 후 이벤트 집행을 위해 인게임 로그를 집계했는데 값이 뭔가 이상했습니다. 가장 많이 죽인 사람이나 가장 오래 플레이 한 사람을 구하려 했는데 쿼리 워크벤치가 표시한 상위 목록과 엑셀에서 수동으로 구한 목록이 서로 달랐고 쿼리 워크벤치가 돌려준 값에는 명백히 있어야 할 사용자가 나타나지 않는 등 이상한 결과를 돌려주고 있었습니다. 알고 보니 쿼리 워크벤치는 베이직 쿼리만 지원할 뿐 아니라 일반 조회 쿼리는 최대 1만 라인 까지만 돌려주고 또 집계 쿼리는 결과를 최대 2백 라인 까지만 돌려주는 제한 때문이었습니다.

특히 집계 쿼리가 결과를 2백 라인 까지만 돌려주는 제한은 집계 쿼리를 여러 개 사용할 때 완전히 이상하게 동작했는데 게임 하다 특정 레벨에서 죽은 기록을 종합해 가장 많이 죽은 사람 순서대로 20명을 뽑는다면 이미 ‘죽은 기록’을 뽑을 때 1만 라인 제한에 걸린 이상한 결과를 기반으로 ‘특정 레벨’에 일치하는 기록을 2백개만 뽑은 다음 이들을 정렬해 상위 20명을 뽑아 뭔가 결과를 돌려주기는 하지만 완전히 이상한 결과를 돌려주고 있었습니다. 게다가 온프레미스 환경이 아니어서 이 제한을 수정하기도 난감한 상황이었습니다. 쿼리 워크벤치에서는 데이터베이스에 커서를 지원하지도 않아 1만 라인 다음 데이터를 뽑을 수도 없어 결국 1만 라인을 초과하는 로그를 뽑는 별도 구현을 했습니다. 하지만 이 구현의 결과는 1만 라인이 넘는 데이터를 csv 포멧으로 받아 집계해야 하는 상황을 만들었고 앞에서 여러 번 설명한 대로 엑셀을 사용하면 이론적으로는 작업할 수 있는 상황이지만 실제로는 집계 각각이 너무 느려 엑셀을 포함하는 파이프라인을 구축하기는 어려웠습니다.

결국 거대한 csv 파일을 다루는 데는 EmEditor라는 제품을 사용하기로 합니다. 이 에디터는 굉장히 오래 전부터 존재했던 것 같습니다. 거의 처음 로그파서의 존재를 알게 되던 시대에도 있었던 것 같은데 다른 기능은 잘 모르겠지만 적어도 아주 거대한 텍스트 파일을 순식간에 검색하고 순식간에 정렬할 수 있는 것은 확실합니다. 제작사의 주장에 따르면 최대 16TB짜리 파일도 처리할 수 있다고 합니다. 우리가 처리해야 하는 csv 파일은 아직 1GB 이하인 상황이어서 무리 없이 다룰 수 있었습니다. 하지만 이 에디터로는 본격적인 집계를 하기 보다는 다른 파이프라인에 사용하기 위해 커다란 csv 파일을 여러 조각으로 나누거나 원하는 칼럼만 뽑아낸 새 파일을 만들거나 꽤 강력한 검색 결과에 일치하는 라인만 뽑아 새 파일을 만드는 등 전 처리에 활용하기에는 적당했습니다. 이 도구를 사용해 그나마 엑셀로 다룰 수 있는 수준으로 크기를 줄인 데이터는 엑셀을 사용해 좀 더 익숙한 방법으로 처리해 로그 기반으로 의사결정 하는데 사용할 수 있었습니다.

하지만 엑셀은 근본적으로 이런 작업을 하기에 너무 느렸고 다른 방법을 찾아야 한다고 생각했습니다. 아직 샘플링을 하기에는 너무 이른 단계로 위험하다고 생각했습니다. 여전히 이런 집계 보고서를 만들 때 가장 유용하고 익숙한 방법은 SQL로 오픈서치에서 SQL을 지원하는 척만 하고 제대로 지원하지 않는다면 오히려 본격적으로 SQL을 지원하는 환경에 로그를 넣으면 되지 않을까 하는 생각을 하기에 이릅니다. 그렇다고 오직 이 용도만을 위해 클라우드에 돈을 내고 리소스를 사용하는 건 아직은 적절하지 않다고 생각했습니다. 이미 오픈서치에 로그를 적재하기 위해 리소스 비용을 지불하고 있었고 이는 우리들의 런웨이를 시시각각 줄여 가고 있었습니다. 고민 끝에 그냥 로컬 윈도우에 MySQL 데이터베이스를 설치하고 여기에 거대한 csv 파일을 임포트 한 다음 그냥 로컬에서 조회하면 되지 않을까 하는 생각을 합니다.

결과는 꽤 괜찮았습니다. 어떤 서비스에 사용하는 환경이 아니기 때문에 키 밸류 모양으로 기록한 로그를 2차원 테이블 모양으로 뽑아 그걸 그냥 임포트 해서 일어나는 성능 저하는 신경 쓸 일이 아니었습니다. 또 프라이머리 키 설정, 중복 금지 설정, 인덱스 생성, 풀텍스트 생성을 꽤 방만하게 설정하고 나니 꽤 만족스러운 속도로 조회되기 시작합니다. 이전에는 이너 쿼리를 사용할 수 없어 전처리에 시간을 쓰던데 비해 이제 그냥 이너 쿼리, 조인을 사용할 수 있게 됐고 집계 명령어들도 정상적으로 동작해 그냥 마음 놓고 익숙한 방법으로 결과를 조회할 수 있게 됩니다. 사실 이 쿼리들은 언리얼 에디터 두 개를 띄우고도 꽤 일할 만한 환경을 제공하는 기계에서 조회 한 번에 몇 초 정도 걸리며 꽤 느리게 동작했지만 이전에 엑셀에서 몇 분 단위로 걸릴 뿐 아니라 모든 엑셀을 얼어붙게 만들어 아무 작업도 할 수 없던 상황에서 이전 조회를 수행하는 몇 초 사이에 그냥 데이터베이스 콘솔을 여러 개 열고 다른 조회를 수행할 수도 있었습니다.

결국 원시적인 환경에서 현대적인 환경을 돌고 돌아 다시 익숙한 환경을 찾으며 좀 귀찮고 구린 절차가 완성됐지만 여러 가지 질문에 올바른 답변을 할 수 있는 근거가 되는 통계를 꽤 빨리 뽑을 수 있게 됐다는 점에서 개인적으로 나쁘지 않다고 생각하고 있습니다. 물론 근미래의 실제 서비스 단계에 도달하기 전에 이 워크플로우를 반드시 개선해야 하겠지만 한동안은 이 체계로 버틸 수 있을 것 같고 또 오래 전처럼 다른 일 하다가 집중이 안 될 때 잠깐 쉬며 로컬에 데이터베이스 서버를 실행해 머릿속에 떠오르는 가벼운 의문들을 해결하며 미래에 사용할 새로운 힌트를 얻을 수 있을 겁니다.

결론. 오픈서치에 로그를 적재하는데 까지는 괜찮았지만 이를 쿼리 워크벤치를 통해 조회하기에는 적절하지 않았습니다. 특히 베이직 쿼리만 지원하고 조회에는 최대 1만 라인, 집계에는 최대 2백 라인 까지만 돌려주는 제한은 결과를 신뢰할 수 없게 만듭니다. 과거에 비해 엑셀은 훨씬 강력해졌지만 그보다 더 빨리 데이터 크기가 커졌고 집계 요구사항도 복잡해져 엑셀은 여전히 이 워크플로우에 도입하기 까다롭습니다. 로컬에 MySQL 서버를 설치하고 로그를 임포트 해 조회했더니 오픈서치의 쿼리 워크벤치에 있던 모든 제약을 피할 수 있어 결과를 신뢰할 수 있을 뿐 아니라 엑셀이나 오픈서치에 비해서도 압도적으로 빠르게 조회됩니다. 장기적으로 이 워크플로우를 유지해서는 안되지만 당분간은 괜찮을 것 같습니다.