프로그래머에게 질문하기 - 옥찬호

안녕하세요. 2022년 12월에 충남과학고등학교 #include 동아리 박정식 부장님과 이메일로 인터뷰를 한 적이 있습니다.

당시에 질문과 제가 답변했던 내용을 올립니다.

  • 인터뷰 일자 : 12월 4일
  • 인터뷰 방식 : 이메일
  • 아래의 내용은 답변 내용을 대화 형식으로 재구성한 것임을 알립니다.

Q. 안녕하세요, 충남과학고등학교 #include 동아리 27기장 박정식입니다. 평소 우리는 개발자가 되고 싶거나 정보 분야에 관심이 많았더라도, 정작 프로그래머가 어떤 사람인지는 잘 몰랐는데요. 이러한 우리를 위해서 프로그래머 옥찬호님께서 이 자리에 나와주셨습니다. 안녕하세요?

A. 안녕하세요. 모멘티에서 엔진 엔지니어로 일하고 있는 옥찬호입니다. Microsoft MVP로 활동하고 있으며, C++ Korea 및 Reinforcement Learning KR의 운영진이기도 합니다.

Q. 네, 인터뷰에 응해주셔서 진심으로 감사드립니다. 그런데 엔진 엔지니어라고 소개해주셨는데, 이 엔진 엔지니어라는 직업은 무엇인가요? 이 직업에 관해 자세히 소개해주실 수 있나요?

A. 엔진 엔지니어란 여러 제품에서 사용하는 핵심 코드가 담긴 엔진을 만드는 사람을 말합니다. 저희 회사의 경우 하나의 엔진을 앱, 웹, 백엔드 등에서 사용하고 있습니다. 여러 플랫폼에서 쓸 수 있도록 엔진을 구현한 뒤 각 플랫폼에서 손쉽게 사용할 수 있도록 API도 제공하고 있습니다. 또한 버그가 발생할 경우 대응하고, 엔진 구조와 성능을 개선하기 위해 리팩토링 작업도 꾸준히 진행하고 있습니다.

Q. 아하 그렇군요. 즉, 우리가 사용하는 프로그램의 보편적으로 사용되는 핵심 부분을 만드신다는 건데, 정말 대단하십니다. 그렇다면, 이 프로그래머라는 꿈은 어떤 이유로 선택하게 되셨는지 상당히 궁금한데, 배경을 자세히 들을 수 있을까요?

A. 프로그래머라는 진로를 선택하게 된 계기는 중학생 때 컴퓨터 게임을 하면서였습니다. 초등학교 3학년 때부터 컴퓨터 학원을 다니기 시작했는데, 그때까지만 해도 프로그래밍에 대해서는 잘 모르고 있었습니다. 그러다가 중학교에 입학한 뒤 1학년 때 담임선생님의 권유로 프로그래밍 경진대회(지금의 정보 올림피아드)를 나가게 되었는데요. 그때 처음 프로그래밍을 접하게 되었습니다. 이후 컴퓨터 게임을 하다가 오랜 시간 끝에 엔딩을 보게 되었는데 너무 감동을 받아서 저도 저런 게임을 만들어서 많은 사람에게 감동을 주고 싶다는 생각이 들어 진로를 선택하게 되었습니다.

Q. 게임을 통해서 꿈을 갖게 된 어린 소년이, 결국 직접 게임을 만드는 넥슨의 게임 프로그래머로 성장한 모습이 상당히 인상 깊네요. 저희 #include 부원중에서도 예를 들어 마인크래프트를 하다가 게임상의 모드를 직접 만들고 싶어 개발을 처음 접하고, 이게 재미있어서 프로그래머라는 꿈까지도 발전하게 된 친구가 있었는데요. 아마 비슷한 경험을 해본 학생들에게 모범 선례가 될 것 같습니다. 그런데, 이렇게 넥슨에서 근무하시다가 최근 모멘티라는 새로운 회사로 이직하셨다고 들었어요. 모멘티는 일반인 사이에서는 인지도가 높지 않은 회사인데, 이 회사로의 이직을 결심하게 되신 이유가 궁금합니다. 그런김에, 모멘티라는 회사도 같이 소개해주시면 좋을 것 같습니다.

A. 넥슨에서 게임 컨텐츠 프로그래머로 약 3년, 게임 엔진 프로그래머로 약 3년 일했었습니다. 처음 입사했을 때는 모르는 지식이 많아 하나씩 배워가면서 성장 곡선이 굉장히 가파랐는데, 시간이 지나면서 점점 완만해지고 있다는 느낌을 받았습니다. 그래서 지금처럼 편하게 직장을 다닐 것인가, 아니면 새로운 도전을 하러 떠날 것인가 사이에서 고민하다가 새로운 도전을 하기로 결정했습니다. 다양한 곳에서 제안이 왔었는데, 새로운 도전을 하기로 결심한 만큼 최대한 새로운 환경에서 일할 수 있는 곳으로 모멘티를 선택하게 되었습니다. 모멘티는 ‘손으로 터치할 수 있는 영상’ 제작 기술을 바탕으로 사용자가 미디어를 직접 만져보고 참여할 수 있는 다차원 컨텐츠 플랫폼을 제공하는 회사입니다. 기존의 영상은 단순히 보는 컨텐츠로만 사용하고 있는데, 그 한계를 뛰어넘기 위해 다양한 도전들을 하고 있으며, 이를 통해 대중에게 몰입도 높은 미디어 경험을 제공하며 디지털 세계와 물리적 세계를 하나로 연결해주는 일들을 합니다.

Q. 옥찬호님은 엔진 엔지니어로서 프로그래머로만 활동하실 뿐만 아니라 여러 정보 기술 단체에서도 활동 중이신 것으로 유명하신데요. 대표적으로 C++ Korea 설립자이신 걸로도 잘 알려져 있습니다. 이 C++ Korea를 설립하시게 된 계기나 이유는 무엇인가요?

A. 대학생 때 C++과 관련해 궁금한 게 있었는데, 인터넷에 검색해도 전부 영어 자료만 나오고 한국어로 물어볼 수 있는 곳은 거의 없었습니다. 페이스북에 찾아봤는데 한국 사람들을 위한 커뮤니티는 없다는 걸 알게되어 홧김에 친구 몇 명을 초대해 커뮤니티를 만들게 되었습니다. 처음에는 약 20명 정도의 지인들이 있는 극소수의 커뮤니티였는데, 우연찮은 기회로 마이크로소프트와 함께 세미나를 열게 되었고 이를 계기로 성장해 지금의 모습이 되었습니다.

Q. 또한 번역서도 많이 쓰신 것으로도 알려져 있는데요. 대표적으로 최근에는 ‘C++ 최적화’(한빛미디어)를 번역하셨죠. 이외에도 ‘모던 C++ 입문’, ‘러스트 핵심 노트’, ‘2D 게임 프로그래밍’, ‘유니티 Shader와 Effect 제작’, ‘게임샐러드로 코드 한 줄 없이 게임 만들기’ 등이 있습니다. 이러한 번역 활동은 어떤 이유로 하게 되셨나요? 그리고 직관적으로 생각하기에는 스스로에게도 도움이 되셨을 것 같은데, 이러한 번역 활동이 어떤 도움을 본인에게 주었다고 생각하시나요?

A. 첫 번역은 대학생 때 시작하게 되었는데 전공 지식을 활용해서 돈을 벌 수 있는 방법을 찾아보다가 사이트에서 게임 개발 관련 도서의 번역자를 모집한다는 글을 보게 되어 호기심에 지원하게 되었습니다. 이후 샘플 번역을 거쳐서 본격적으로 번역을 시작하게 되었는데, 번역을 하면서 분명히 도움이 되는 부분은 있습니다. 흔히 번역을 할 때 영어를 잘 알고 있어야 한다는 이야기를 많이 하는데, 저는 오히려 국어를 잘 알고 있어야 한다고 생각합니다. 영어 문장을 한국어로 번역한 뒤 읽어보면 자연스럽지 않고 소위 ‘번역투’라는 느낌을 많이 받습니다. 이를 위해선 초벌 번역을 한 뒤에 직접 읽어보면서 여러 번 탈고하는 작업이 필요합니다. 이 과정에서 글을 어떻게 써야 독자들에게 잘 전달할 수 있을까를 고민하게 됩니다. 이런 과정이 글을 쓰는데 많은 도움을 준다고 생각합니다.

Q. 그렇군요, 단순한 내용 이해와 같은 단기적인 지식뿐 아니라 글 쓰는 것에 대한 전반적인 경험 또한 도움이 된다는 점이 인상 깊은데요. 그렇다면, 이러한 번역 활동을 해보고 싶은 학생들도 있을 것 같은데, 이 학생들에게 해주실만한 조언이 있나요?

A. 번역을 해보고 싶다면, 하나의 문장 또는 하나의 문단부터 번역해 보세요. 번역을 하셨다면, 글을 소리내서 읽어보세요. 어색한 부분이 느껴진다면, 탈고하면서 개선하고 어느 정도 완성이 되었다고 판단이 되면 다른 친구들에게 원문과 번역한 글을 보여주며 번역이 잘 되었는지 한 번 확인해보시기 바랍니다. 확인이 끝났다면, 하나의 글을 번역해보세요. 그리고 어느 정도 익숙해지면 책도 번역할 수 있을 겁니다.

Q. 지금까지는 옥찬호님과 관련하여 궁금한 점에 관한 질문을 드렸는데요, 이번에는 저희가 공부하면서 생긴 궁금한 점을 바탕으로 질문을 드리려고 해요. 우선, 가장 많이 들어온 질문인데요, 프로그래밍 언어는 어떻게 공부하는 것이 가장 효과적일까요?

A. 프로그래밍 언어를 처음 공부한다면 기본적인 문법들은 입문서, 공식 문서 또는 강의를 참고하시되 코드를 많이 짜보라는 이야기를 꼭 드리고 싶습니다. 프로그래밍 언어는 프로그램을 만들기 위한 수단입니다. 어떤 언어의 문법을 많이 안다고 하더라도 실제로 사용해보지 않으면 곧 잊어버리게 됩니다. 코드를 짜보면서 머리로, 눈으로, 손으로 반복해서 익혀야 오랫동안 기억에 남습니다.

Q. 저희는 현재 C++ 또는 Python을 교과 과정상 이수했는데요. 이러한 프로그래밍 언어가 정보과학 공부가 아닌 실생활에서 사용되는 경우는 상당히 한정되어 있었다는 아쉬움이 있었습니다. 그렇다면, 실무에서는 주로 어떤 언어를 사용하는지 예시를 들어주실 수 있으실까요?

A. 저는 회사에서 Rust를 주로 사용하고 있습니다. 또한 엔진의 API를 만들기 위해 iOS 지원을 위한 Swift, Objective-C, 웹 지원을 위한 TypeScript, WebAssembly, 백엔드 지원을 위한 Elixir 등도 사용합니다. 넥슨에 다닐 때는 게임 클라이언트에서는 C++, 서버에서는 C#을 주로 사용했습니다. 실무에서 사용하는 언어는 분야에 따라 많이 다릅니다. AI는 Python을 주로 사용하고, 웹 프론트엔드는 JavaScript와 TypeScript, 백엔드는 Java, Python, C#, Go, Rust, Elixir 등 선택지가 많습니다. 앱 개발이라면 Android는 Kotlin, iOS라면 Swift, 플러터를 사용한다면 Dart 등이겠네요. 게임 개발에서는 C++과 C#을 많이 사용합니다.

Q. 현재 러스트라는 언어가 급부상하고 있는 것으로 알려져 있습니다. 옥찬호님도 현재 회사에서 러스트를 주로 사용하고 있으시다고 말씀하셨고요. 이러한 러스트는 현 시점에서 대학교를 들어가는 학생에게 배울 만한 언어라고 생각하시나요?

A. 저는 러스트를 첫 언어로 권유하고 싶지는 않습니다. 학습 곡선이 꽤 가파릅니다. 특히 소유권, 수명과 같은 개념은 처음 봤을 때 꽤 어렵게 다가올 겁니다. 첫 언어로는 쉽게 접근할 수 있는 파이썬이나 자바스크립트로 하시고, 한 언어에 어느 정도 익숙해졌다면 그때 러스트를 공부해보세요. 그러면 러스트 언어가 갖고 있는 철학을 좀 더 이해하시게 될 겁니다.

Q. 개발 과정에서는 프로그래밍 언어만 안다고 해서 쉽게 이루어지지 않죠. 대부분은 다른 배경지식도 함께 필요로 하는 경우가 많은데요. 특히 경사하강법이나 두 점 사이의 거리와 같은 수학적 개념에 기반을 둔 경우가 상당히 많습니다. 따라서 흔히 수학은 프로그래머의 역량 향상에 큰 도움이 된다고 알려져 있습니다. 그렇다면 수학 외에도 프로그래머의 역량 향상에 도움이 되는, 중점적으로 공부하면 좋을만한 학문이 있을까요?

A. 분야마다 차이는 있겠지만, 게임 개발의 경우에는 물리학을 알아두시면 좋습니다. 컴퓨터 그래픽스에서는 광학 개념이 많이 활용됩니다. 실제로 빛의 반사나 굴절을 적용하기도 하구요. 또한 게임 물리 엔진을 만들 때 역학 지식을 많이 사용하기 때문에 알아두시면 좋습니다. 또한 프로그래밍 관련 지식을 찾아보면 영어로 되어 있는 책이나 문서를 볼 일이 많기 때문에 영어를 꼭 공부해두시기 바랍니다.

Q. 앞서의 질문에서는 프로그래머 전반에게 열려 있는 질문이었는데요. 이번에는 옥찬호님이 게임 개발자이셨기 때문에 이와 관련한 추가적인 질문을 드리려고 합니다 게임 프로그래머는 일반적인 프로그래머보다 중점적으로 공부를 해야 하는 분야나 프로그램이 있나요?

A. 학교 과목으로는 수학과 물리학을 공부하세요. 수학은 삼각함수, 벡터, 행렬 등 다양한 지식이 활용됩니다. 물리의 경우 역학을 많이 사용하고 분야에 따라서는 광학도 사용합니다. 프로그램으로 보자면 주로 유니티 엔진과 언리얼 엔진을 많이 사용합니다. 유니티 엔진은 C#, 언리얼 엔진은 C++을 사용하니 사용하는 엔진에 따라 언어도 공부하세요. 그리고 운영체제, 컴퓨터 구조, 자료구조 및 알고리즘, 컴퓨터 그래픽스 등의 CS 이론들을 공부하세요.

Q. 하나의 게임을 만드는 데는 프로그래머뿐 아니라 여러 사람이 함께 협력합니다. 대표적으로 게임을 기획하는 사람, 게임 아트를 그리는 사람 등 다양한 인력이 투입되지요. 또한 이들과의 협력도 상당히 중요할 것이라는 생각이 듭니다. 그렇다면, 게임 프로그래머는 다른 역할을 맡은 사람들과 어떻게 소통하나요?

A. 게임 프로그래머에도 여러 분류가 있습니다. 크게 나누자면 클라이언트 프로그래머와 서버 프로그래머로 나눌 수 있겠습니다. 클라이언트 프로그래머는 UI 시스템 구현, 신규 캐릭터와 보스 레이드 작업을 하게 되며 렌더링, 물리 등 다양한 엔진 작업도 합니다. 서버 프로그래머는 데이터베이스 관리, 여러 사용자가 원활하게 게임을 할 수 있도록 분산 처리도 작업하며 게임 내 핵심 로직들을 담당합니다. 이런 작업들은 혼자서 진행할 때도 있지만 컨텐츠에 따라 디자이너, 아티스트와 함께 작업하는 경우가 많습니다. 디자이너가 컨텐츠 기획 초안을 작성해 보여주면 구현 가능 여부를 판단해 전달하며 다른 대안은 없는지 살펴봅니다. 또한 좀 더 나은 컨텐츠가 될 수 있는 부분은 없는지 논의하며 컨텐츠의 질을 높이기 위해 노력합니다. 또한 아티스트가 제작한 모델링, 모션, 애니메이션, 이펙트 등을 함께 보면서 게임 안에서 잘 보이는지, 다른 컨텐츠들과 잘 어울리는지 등을 확인합니다. 하나의 컨텐츠를 만들기 위해 몇 달 동안 함께 이야기하며 결과물을 만들어냅니다.

Q. 한 프로젝트 안에서는 다양한 프로그래머가 참여하며, 방금 말씀하신 것처럼 프로그래머 사이에서도 다양한 역할이 있습니다. 따라서 다른 프로그래머와의 협업이 많이 이루어질 것 같은데요. 이처럼 한 프로젝트를 팀 단위로 협업해서 만드는데, 그때는 어떤 방식이나 툴을 사용하시나요? 또는 어떤 방법으로 주로 협업하시나요?

A. 코드 관리의 경우 Git을 사용하는 경우도 있고, 프로젝트 파일이 많은 경우에는 Perforce를 사용하는 경우도 있습니다. 개발은 C++/C#의 경우 Visual Studio를 사용하며, 현재는 Visual Studio Code 또는 IntelliJ IDEA를 사용합니다. 또한 태스크 관리는 자체 제작 프로그램을 사용한 적도 있고 현재는 JIRA를 사용하고 있습니다. 코딩 컨벤션은 언어에서 정해진 게 있을 경우 기본 형식을 따르고 있으며, 없을 경우에는 팀 내에서 합의된 컨벤션을 지키고 있습니다.

Q. 훌륭한 프로그래머가 되기 위해, 프로그래머의 역량 향상에 도움이 되는 활동이 있을까요? 저희가 해보면 좋을 만한 활동, 혹은 당장은 하지 못하더라도 대학이나 나중에 취업한 뒤에 꼭 해봤으면 하는 활동도 좋습니다. 어떤 것이 있을까요?

A. 다양한 경험을 많이 해보시기 바랍니다. 저는 아이디어를 얻기 위해 다양한 사람들을 만나봅니다. 전공에 상관 없이 다양한 사람들을 만나서 다양한 이야기들을 들으며 다양한 경험을 간접적으로 해보고 있습니다. 또한 내가 일할 분야를 정하기 위해 다양한 분야를 경험해보시는 걸 권합니다. 해커톤이나 게임잼 등의 행사를 참가해 다양한 개발을 해보시고 새로운 친구들도 만들어보세요. 혼자 개발하기 보다는 같이 개발하면서 서로 코드 리뷰도 해주고 성장하는 밑거름으로 만드시기 바랍니다.

Q. 좋은 답변 고맙습니다 이번에는 성장 과정이 아닌 실제 업무와 관련된 부분에 대해서 질문을 드려보려고합니다. 우선 질문에 들어가기에 앞서, 쉬어가는 질문 하나 해보려고 합니다. 개발 과정에서 가장 기억에 남은 버그가 있나요?

A. 넥슨에서 일할 때 어떤 버그를 제보받아 원인을 추적했던 적이 있습니다. 재현 빈도가 낮아서 원인을 찾는데 고생을 많이 했고, 추적 경로가 너무 길어서 1주일 가까이 시간을 소비했었습니다. 그 결과 게임 출시 때부터 해결하지 못한 버그와 같은 종류임을 알게 되었고 꼭 고쳐보고 싶단 마음이 들었습니다. 그래서 포기하지 않고 추적해 결국 OS에서 발생한 버그라는 결론에 도달하게 되었습니다. 그동안 아무도 해결하지 못했던 버그를 해결했기 때문에 성취감이 매우 컸었던 기억이 있습니다.

Q. 그렇군요. 이와 같은 경험들이 쌓이면 프로그래머라는 직업에 대한 자부심이나 성취감 또한 커질 것 같은데요. 그렇지만, 요즘에 인터넷을 보면 프로그래머가 레드오션이라서 취업도 힘들고, 취업해도 업무 등이 힘들다는 말이 있습니다. 야근도 많이 하고, 업무량도 많다고 하더래요. 프로그래머라는 직업을 갖고 살아가는 것이 많이 힘들고 어렵나요?

A. 프로그래머뿐만 아니라 모든 직업이 쉽지 않습니다. 노동은 힘들고 분야를 가리지 않아요. IT 분야는 하루를 멀다하고 새로운 지식이 계속 나오기 때문에 평생 공부해야 하는 직업입니다. 개발 분야에 진입하는 것은 어렵지 않습니다. 하지만 직업으로 살아갈 것인가는 생각해보는 게 좋습니다. 어떤 일을 취미로 하는 것과 업으로 삼는 건 무게가 다르다고 생각합니다.

Q. 정보과 학생들은 대부분 본인 포트폴리오가 있다는 말을 들었습니다. 또한 은근히 완전한 오답은 아니었어요. 이러한 배경에서 포트폴리오를 제작하다 궁금증이 생겼는데요. 포트폴리오는 어떻게 제작하는 것이 가장 좋을까요? 그리고 잘 만들어진 포트폴리오는 어떤 것일까요?

A. 포트폴리오를 작성하는 방법은 여러 가지가 있습니다. 몇 가지 팁을 드리자면, 첫째로 가장 최근에 작업했던 프로젝트를 최상단에 적고 역순으로 나열하는 겁니다. 아무래도 포트폴리오를 보는 면접관들은 지원자가 가장 최근에 어떤 기술을 사용해서 무엇을 만들었는지 궁금해 할 겁니다. 따라서 가장 최근에 한 프로젝트를 위에 적는게 좋겠죠. 그리고 무조건 많이 적는 건 좋지 않습니다. 포트폴리오가 많으면 다 읽는데 너무 오랜 시간이 걸리기 때문에 다 읽지 않습니다. 따라서 자신의 역량을 가장 잘 나타낼 수 있는 걸 3~4개 정도 보여주는 게 가장 좋습니다. 마지막으로 본인이 지원하는 회사에서 사용하는 언어, 기술 등을 활용한 포트폴리오를 만들면 좋습니다. 아무래도 회사에서 사용하는 언어나 기술을 활용한 게 있다면 어필하기 좋겠죠.

Q. 프로그래머는 아무래도 화면을 오래 바라보다 보니 눈 건강이 상당히 걱정됩니다. 또한 허리와 목 건강이 위협받는 경우도 많고요. 이러한 부분에서, 어떻게 건강을 유지하는지 방법을 알려주세요.

A. 안구 건조증이 있으신 분들은 점안액을 사용해 건조해지지 않도록 해주는게 좋구요. 모니터와 눈 사이의 거리를 어느 정도 두시는 것이 좋습니다. 너무 가까우면 눈에 좋지 않습니다. 그리고 1시간에 1번 정도는 바깥의 먼 곳을 바라보면서 눈을 잠시 쉬게 해주세요. 허리나 목 건강의 경우 기본적으로는 시디즈나 허먼밀러 등 좋은 의자를 사용하세요. 그리고 너무 오래 앉지 말고 1시간에 한 번은 일어나서 움직여주세요. 그리고 거북목이 되지 않도록 자세를 신경 쓰시고 목이나 허리 통증이 있으면 가까운 정형외과를 방문해서 의사선생님과 상담을 하는 게 좋습니다.

Q. 노코드 툴은 코드를 사용하지 않고 프로그램을 만드는 툴 그리고 로우코드 툴은 코드를 거의 사용하지 않고 프로그램을 만드는 툴을 의미합니다. 이 툴들을 사용하면 프로그램을 빠르고 쉽게 제작할 수 있겠지만 그만큼 자유성은 떨어지는 편인데요. 이러한 노코드 툴이나 로우코드 툴을 실제 업무에서 사용하나요?

A. 프로그래머가 사용하는 경우는 거의 없습니다. 다만 디자이너나 아티스트가 코드를 작성하지 않고 쉽게 만들 수 있도록 툴을 만들어주는 경우는 종종 있습니다. 대표적인 예로는 언리얼 엔진의 블루프린트가 있겠네요.

Q. 이제 마지막 질문인데요, 프로그래머에게 가장 중요한 역량은 무엇이라고 생각하시나요?

A. 프로그래머에게 가장 중요한 역량은 꺾이지 않는 마음(?)입니다. 공부를 하다 보면 막히는 구간이 생깁니다. 프로그래밍도 마찬가지입니다. 어떤 코드를 구현하다가 막히는 구간이 생깁니다. 또한 어떤 버그가 발생했을 때 원인을 찾다 보면 막히는 구간이 생깁니다. 막히는 부분을 검색해서 원하는 결과가 한 번에 나오면 다행이지만, 잘 나오지 않는 경우도 있습니다. 그러면 스스로 헤쳐나가야 하는데, 이 때가 가장 중요한 순간입니다. 이대로 포기할 것인가, 아니면 해결할 방법을 계속 찾아볼 것인가? 프로그래머라면 해결할 방법을 찾기 위해 다양한 방법을 시도해봐야 합니다. 인내심을 갖고 좀 더 깊게 들어갈 수도 있겠고, 주변에 있는 다른 프로그래머들에게 어떤 상황인지 설명한 뒤 방법은 없는지 논의해볼 수도 있고 아니면 우회할 수 있는 방법은 없는지 찾아보는 것도 방법입니다.

Q. 마지막으로 정보 분야 진로를 꿈꾸는 충남과학고 학생들에게 하고 싶으신 말씀이 있나요?

A. 정보 분야 진로를 꿈꾸는 모든 분들이 부디 건강을 챙기면서 행복하게 개발을 하셨으면 좋겠습니다. 개발을 하기 위해 많은 시간을 투자해 공부를 하실 겁니다. 그리고 여러분들이 사회에 진출해서 개발을 업무로 하게 되더라도 계속 공부해야 할 겁니다. 개발자의 인생은 마라톤과 같습니다. 아무리 실력이 뛰어나더라도 건강하지 못하다면 하고 싶은 일을 할 수 없게 됩니다. 그러니 개발에 몰두하셨다면 자신에게 쉬는 시간을 충분히 주시기 바랍니다. 스트레스를 받을 땐 명상을 하며 심호흡을 하고, 잠시 컴퓨터에서 벗어나 운동이나 산책을 하고, 책을 읽거나 친구들이랑 만나서 이야기를 하고, 여행을 통해 생각을 정리하고 새로운 마음가짐을 가져보고 등이 있겠습니다. 일을 할 땐 몰입해서 일하고, 쉴 때는 확실하게 쉬는 멋진 사람이 되기를 진심으로 기원합니다.

2022년 회고 / 2023년 계획

2022년도 어느덧 하루 밖에 남지 않았다. 2022년은 어떻게 살았는지 되돌아보는 회고의 시간인데, 전체적인 만족도는 그렇게 높지 않다. 올해는 어떤 일들을 했는지, 그리고 내년에는 어떤 계획을 세울지 정리해보자.

2022년 회고

  1. 월세가 올랐다.
    • 넥슨에 입사할 때 지금 살고 있는 집에 들어오게 되었다.
    • 집 계약 후 다음 해에 3만원을 올리고, 그동안 월세가 그대로였다.
    • 집주인이 월세 10만원을 올려달라고 해서 이사를 갈까 고민했던 적이 있다.
    • 알고 봤더니 올린 월세도 현재 시세보다는 싼 가격이었고, 그동안 올리지 않은 고마움도 있었다.
    • 이사가기에 그리 좋은 시기가 아니다 보니, 우선은 계속 살고 있다.
    • 하지만 내년엔 어떻게 될 지 모르겠다. 원룸을 떠나 투룸에서 살고 싶다는 욕구가 있어서…
  2. COVID-19에 걸렸다.
    • 그동안 백신도 충실히 맞았고, 나름대로 조심하면서 지냈기에 넘어갈 수 있을 줄 알았다.
    • 하지만 회사 워크샵을 가기 직전에 양성 판정을 받아 워크샵에 가지 못하고 집에 격리하게 되었다.
    • 그래도 백신을 맞은 덕분에 증상이 그리 심하진 않았고, 1~2일 지나니 다시 일할 수 있게 되었다.
    • 다만, 후유증은 좀 오래 가더라. 1~2달 정도는 고생하면서 지냈던 거 같다. 조심해야지…
  3. 10년 동안 사용했던 모니터를 바꿨다.
    • 10년 전에 Dell UltraSharp 24인치 2대를 샀었고, 최근까지 사용했었다.
    • 회사에서는 4K 32인치 모니터를 사용하고 있는데, 해상도가 커서 너무 편해보였다.
    • 그래서 집에 있던 모니터들을 교체하기로 했다. 현재는 Dell UltraSharp 4K 27인치 2대를 사용하고 있다.
    • 작업 환경이 전반적으로 개선되어 만족하고 있다. 모니터 2대에 300만원을 써서 출혈이 좀 있었지만…
  4. 미래를 위해 적금을 들었다.
    • 넥슨에 입사하고 나서는 돈을 모은다기 보다는 돈을 쓰기에 바빴다.
    • 그 당시에 나의 마음은 ‘미래를 위해 투자한다’였기에, 그런 선택을 한 것이다.
    • 작년에 이직을 하고 나서, 올해부터는 돈을 조금 모아보면 좋겠다는 생각이 들어 적금을 들었다.
    • 현재는 주마다 50만원 적금을 하고 있으며, 추가로 26주 적금을 1~2개를 하고 있다.
    • 26주 적금은 내가 필요한 물건이 있을 때 사기 위한 용도다. 최근 하나 만기가 되어 아이패드 프로를 샀다.
    • 내년에도 26주 적금은 꾸준히 할 예정이며, 여유가 있다면 적금 하나를 추가로 들려고 한다.
  5. 알고리즘 대회에 후원을 시작했다.
    • 나는 선순환의 고리를 믿는 사람이다. 그래서 선한 영향력을 어떻게 전달할 수 있을까 고민한다.
    • 사회를 좋은 방향으로 발전시키기 위한 방법에는 여러가지가 있을 것이다.
    • 최근 알고리즘 문제를 풀고 대회에 참가하면서 좀 더 풍성한 대회로 만들고 싶겠다는 생각이 들었다.
    • 그래서 개인 또는 학교 대회를 개최하는데 후원이 필요한 경우, 조금씩 지원을 해주고 있다.
    • 한 달에 약 2개의 대회를 후원하고 있으며, 후원 금액은 적게는 몇 만원, 많게는 수십만원까지 다양하다.
    • 혹시나 이 글을 읽는 분들 중 후원이 필요한 대회가 있다면 언제라도 DM으로 문의하시면 된다.
  6. 알고리즘 대회를 열기 위한 준비를 하고 있다.
    • 여러 대회를 참가하면서 구현 문제 중심의 대회는 열리지 않는 것을 보고 열면 좋겠다고 생각했다.
    • 그래서 내년 여름에 개최하는 것을 목표로 알고리즘 대회를 준비하기 시작했다.
    • 출제진은 한 명씩 개인적으로 연락해 부탁했으며, 총 9명의 인원이 문제를 출제한다.
    • 현재는 각자 낼 문제를 정하고, 문제를 만들고 있는 단계다. 검수는 내년 2월부터 시작하지 않을까 예상한다.
    • 예산도 넉넉하게 1,000만원으로 잡아뒀다. 모두의 축제가 될 수 있도록 열심히 준비하겠다.
  7. 운동을 위해 PT를 하기 시작했다.
    • 올해 목표 중에 체중 감량이 있었는데, 전반적으로 게으른 생활을 하다 보니 감량은 커녕 체중이 늘어났다.
    • 내년에는 꼭 체중 감량을 해야겠다는 생각을 하다가, 그냥 생각난 김에 바로 실천으로 옮기자고 생각했다.
    • 집 앞에 있는 헬스장을 방문한 뒤 체험을 해봤다. 전반적으로 만족해 그 날 바로 PT 30회를 결제했다.
    • 체험 때 하체 운동을 했는데, 다음 날부터 허벅지가 전부 알이 배겨 걷기조차 힘들 정도로 아팠다.
    • 아픈 게 운동이 잘되었다는 것이니, 앞으로도 열심히 해야겠다. 아직은 아는 것이 거의 없어서 배우는 단계다.
    • 내년 회고를 쓸 때는 체중 감량을 달성한 나 지신이 되기를 기대한다.
  8. Rust + WebAssembly 작업을 했다.
    • 회사에서는 엔진 엔지니어라는 직책으로 일하고 있으며, 크로스 플랫폼 코어 엔진을 만드는 작업을 한다.
    • 새로운 기능을 엔진에 추가하거나, 엔진 관련 버그를 수정하거나, 기존 코드의 구조를 개선하는 작업을 한다.
    • 또한 iOS, Web, Backend 등 다양한 플랫폼을 지원할 수 있는 API를 제공한다.
    • 올해 몇 개월 동안 TypeScript로 구현된 기존 엔진을 WebAssembly로 교체하기 위한 작업을 진행했다.
    • 작업 과정에서 어려움이 많았다. 그 이유는 인터넷에 찾아봐도 유용한 정보가 거의 없었기 때문이다.
    • rxRust가 WebAssembly를 지원하지 않는 문제가 있어 지원할 수 있게끔 오픈 소스 기여를 했다.
    • 여러 시행 착오 끝에 지금은 몇 가지 버그 수정만 남았으며, 내년 초에는 교체할 수 있을 듯 하다.
    • 시행 착오에서 겪었던 경험들은 기술 컨퍼런스에서 발표할 기회가 있다면 이야기 보따리를 풀어보도록 하겠다.
  9. 게임 개발자 로드맵을 2022 버전으로 업데이트했다.
    • https://github.com/utilForever/game-developer-roadmap
    • 게임 개발자 로드맵은 웹 개발자 로드맵을 보고 영감을 받아 만들게 되었다.
    • 처음에 만들고 나서 몇 년 동안 방치되어 있었는데, 그 사이에 트렌드가 많이 바뀌었다.
    • 그래서 마음먹고 몇 일 동안 처음부터 새로 만들게 되었다. 클라이언트는 내가 만들고, 서버는 허린님이 도와주셨다.
    • 또한 범준이에게 프리뷰 이미지 제작을 부탁했고, 흔쾌히 수락해주었다. 덕분에 멋진 이미지를 사용할 수 있게 되었다.
    • 2023 버전도 준비하고 있으며, 내년 초에 업데이트할 예정이다. 새로운 컨텐츠가 추가될 예정이다.
  10. 사이드 프로젝트 CubbyTower의 기초 작업을 완료했다.
    • https://github.com/utilForever/CubbyTower
    • 고등학교를 졸업하고 대학교에 진학한 학생 2명과 함께 시작한 프로젝트다.
    • 처음에는 개발을 가르치기 위한 목적으로 시작하게 되었다.
    • C++로 개발했고, CMake와 SFML을 사용하며 ECS(Entity Component System)을 적용했다.
    • 개발 속도가 그리 빠르지는 않았지만, 꾸준히 진행한 덕분에 기본 기능을 모두 구현할 수 있었다.
    • 원하면 다운로드 받은 뒤 빌드하면 실행할 수 있으며, 앞으로는 다양한 컨텐츠를 추가할 예정이다.
  11. RL 논문 리뷰 스터디를 진행한 지 3년이 되었다.
    • https://github.com/utilForever/rl-paper-study
    • 2020년 COVID-19로 인해 재택 근무를 하게 되면서, 오프라인 스터디를 할 수 없게 되었다.
    • 평소 강화학습에 관심이 많았는데, 혼자 보기에는 논문이 너무 많아 같이 공부할 수 있는 모임이 있겠다는 생각을 했다.
    • 강화학습 스터디가 있는지 확인해 봤는데, 없길래 하나 만들어서 진행하게 되었다.
    • 2022년에는 7/8/9기 스터디를 운영하게 되었다. 앞으로도 한 해에 3기수 정도로 진행하지 않을까 생각한다.
    • 구성원 중 절반은 이전 스터디부터 계속 참가한 분들, 나머지 절반은 새롭게 참가하는 분들로 채워진다.
  12. RosettaStone 프로젝트를 시작한 지 5년이 되었다.
    • https://github.com/utilForever/RosettaStone
    • 하스스톤을 강화학습 해보자라는 생각을 해서 2017년부터 시작한 오픈 소스 프로젝트다.
    • 2019년에 NDC, Google DevFest, 여러 강화학습 행사 등에서 발표했었다.
    • 어느덧 2022년이 되어 시작한 지 5년이 된 프로젝트가 되었다. 롱런하고 있는 프로젝트라고 볼 수 있다.
    • 그래서 지난 5년 간의 개발 경험들을 정리하는 발표를 진행했었다.
    • https://youtu.be/Lxcd3pXPgU4
    • https://www.slideshare.net/utilforever/momenti-seminar-5-years-of-rosettastone
  13. Rust 프로그래밍 스터디를 진행하게 되었다.
    • Rust로 만드는 인터프리터 스터디 : https://github.com/utilForever/2022-Make-Interpreter-Rust
    • Rust 기초 프로그래밍 + Backend 개발 스터디 : https://github.com/utilForever/2022-Korea-Rust-Backend
    • 인터프리터의 경우 디미고 허승환 학생의 요청으로 만들게 된 스터디다.
    • Backend 개발의 경우 고려대학교 GDSC의 요청으로 만들게 된 스터디다.
    • 확실히 작년보다 Rust에 대한 관심이 증가했음을 실감하고 있다. 현재도 여러 곳에서 강의 요청이 오고 있는 중이다.
    • 내년에는 경북대학교, 고려대학교, UNIST 동아리 등에서 Rust 프로그래밍 스터디를 진행할 예정이다.
  14. 다양한 주제로 여러 행사에서 발표를 했다.
  15. 다양한 오픈 소스 프로젝트에 기여를 했다.
    • https://github.com/rxRust/rxRust/pull/187
    • https://github.com/rxRust/rxRust/pull/199
    • https://github.com/pybind/pybind11/pull/3721
    • https://github.com/effolkronium/random/pull/31
    • https://github.com/kaist-cp/cs220/pull/14
    • https://github.com/mozilla/uniffi-rs/pull/1432

2023년 목표

  • 모던 C++ 프로그래밍 강의 제작하기
  • Rust 프로그래밍 강의 제작하기
  • Rust for Rustaceans 번역 마무리 및 출간
  • 여러 프로그래밍 스터디 잘 마무리하기
    • 자바스크립트로 배우는 SICP 스터디 (사내)
    • Rust로 배우는 인터프리터 / 컴파일러 스터디 (온라인)
    • Rust 프로그래밍 + 마인크래프트 만들기 스터디 (UNIST)
    • Rust 프로그래밍 + Backend 개발 스터디 (고려대학교)
    • Rust 프로그래밍 + 시스템 프로그래밍 스터디 (경북대학교)
  • PT 열심히 하기 및 체중 감량 15kg 하기
  • C++ Korea 임의단체 출범
  • C++ 및 Rust 기반 사이드 프로젝트 5개 이상 진행하기
  • 오픈 소스 프로젝트 10군데 이상 기여하기
  • 새로운 분야 공부하기
    • 자연어 처리
    • 컴퓨터 비전
    • 웹 프로그래밍
    • 앱 프로그래밍

2021년 회고 / 2022년 계획

2020년에 시작된 COVID-19는 2021년에 잠잠해지기는 커녕 확산되어 우리들의 일상 생활을 더욱 힘들게 했다. 그러나 백신 접종이 본격적으로 시작되면서 조금씩 진정되지 않을까 기대하고 있다. 내년에는 회복되었으면 하는 바람이다. 올해도 여러가지 일이 있었는데 그 중에는 인생의 전환점을 맞게된 계기도 있다. 하나씩 이야기해보자.

1월 - 넥슨 5년 근속 감사패

2015년 9월, 넥슨에 전문연구요원으로 입사해 3년 동안 복무했다. 대체 복무를 하면서 많은 것들을 배울 수 있었고 좋은 팀원들 덕분에 짧은 기간에 비약적으로 성장할 수 있었다. 그래서 복무가 끝난 뒤에도 계속 다니게 되었고 시나브로 다닌 지도 5년이 되어 근속 감사패를 받게 되었다. 마영전 클라이언트유닛팀에서 5년 동안 근무할 수 있어서 행복했고 앞으로도 팀원들과 함께 멋진 작업들을 해내고 싶다는 생각이 들었다. (그 일이 일어나기 전까지는 말이다.)

2월 - 공기청정기 선물

1월에 부모님께서 집에 오신 적이 있는데, 12월에 샀던 공기청정기 덕분에 숨쉬기가 편해졌다고 본가에도 있으면 좋겠다고 이야기하신 적이 있다. 그리고 본가에 설치할 공기청정기 2대를 샀다. 20대까지는 부모님께 제대로 선물도 못해드리고 스스로 독립하는 데 시간을 보냈다면, 30대인 지금은 어느 정도 여유도 생겨서 조금이나마 보답하는 시간을 보내고 있다. 이렇게 효도 1스택을 추가해서 기분이 좋다. 감사합니다, 부모님.

2월 - [슬기로운 커뮤니티 생활] Microsoft MVP

2020년 11월에 소프트웨어 아카데미와 함께 촬영했던 Microsoft MVP와 Student Ambassador 소개 영상이 공개되었다. Olivia Ha님과 같이 진행했는데 영상이 잘 나와서 뿌듯했다.

영상 주소 : https://www.youtube.com/watch?v=2FR-LBuIn0s

3월 - 새로운 물리 엔진 업데이트

넥슨에서 ‘마영전 클라이언트유닛’ 팀에서만 6년 가까이 일을 했는데, 전반부는 게임 플레이 프로그래머로 신규 컨텐츠 작업, 버그 수정 등을 위주로 작업했다면 후반부는 게임 엔진 프로그래머로 64비트 클라이언트를 지원하기 위해 반드시 선행되어야 하는 작업이었던 “새로운 물리 엔진” 구현 작업을 담당했다. 처음에 아무 것도 없는 상태에서 시작할 때는 막막했지만 인터넷에 여러 정보를 찾아가며 하나씩 구현해 3~4년 만에 빛을 보게 되었다. 패치 노트에는 “물리 엔진이 PhysX로 교체되었습니다.”라고 나온 1줄이 전부이지만, 이를 위해 몇 년 동안 고생했다는 점을 알아주셨으면 좋겠다. 새로운 물리 엔진 작업을 자세히 이야기하기엔 너무 길어길 거 같아 따로 써 볼 생각이다. 개인적으로는 NDC에서 이 주제로 발표했으면 좋겠다는 생각이 있었지만, 패치 일정으로 인해 하지 못해 아쉽다.

패치 노트 : https://heroes.nexon.com/news/update/view?postno=961

3월 - 생일 기념 신라호텔 휴가

3월 초에 회사에서 바쁜 나날을 보내고 잠깐이지만 신라호텔에서 생일을 대신한 휴가를 보내고 있다. 혼자 가기는 아까워서 주변에 쉼표가 필요한 사람들끼리 가게 되었는데 오랜만에 이야기꽃도 피워보고 맛있는 음식도 먹으며 재충전의 시간을 보냈다.

3월 - KAIST Include 스터디 “AlphaGo, AlphaGo Zero를 활용한 인공지능 바둑” 시작

KAIST Include 동아리장이었던 재우의 부탁으로 어떤 스터디를 할까 고민하다가, 강화학습 기초가 아닌 그 이후의 내용을 주제로 해보고 싶어서 AlphaGo와 AlphaGo Zero를 활용한 인공지능 바둑 스터디로 결정했다. 처음에는 학생들이 잘 따라올 수 있을까 걱정했는데, 오랜 시간 동안 마지막까지 잘 완주해줘서 고마웠다.

저장소 : https://github.com/utilForever/2021-KAIST-Include-AlphaGoZero

3월 - 강화학습 논문 리뷰 스터디 4~6기 스터디 시작

2020년에 시작했던 강화학습 논문 리뷰 스터디 1~3기가 성공적으로 진행되고 2021년에 4기 스터디를 시작하게 되었다. 작년과 달라진 점이 있다면 초기에는 주로 최신 논문보다는 강화학습 핵심 알고리즘 위주로 발표가 되었다면 4기부터는 2019~2020년 논문들이 제법 많이 보이기 시작했다는 거다. 그래서 최신 트렌드를 따라가는데 많은 도움이 되었다.

저장소 : https://github.com/utilForever/rl-paper-study

5월 - 전북과학고등학교 R&E 멘토링 시작

2020년 전북과학고등학교에서 진행했던 특강을 진행한 적이 있다. 올해 3월에 학교에서 연락이 와서 받았는데 1학년이 하는 R&E 중 강화학습을 주제로 한 팀이 있다고 혹시 멘토링을 해주실 수 있겠냐는 부탁을 받았다. R&E를 해 본 적은 없지만 해보고 싶었던 주제였기에 흔쾌히 수락하게 되었다. 처음에 학생들이 제안한 주제가 있었는데 좀 더 재밌는 연구를 해보면 좋겠다는 생각이 들었다. 나는 “Unrailed”라는 게임을 강화학습으로 해보자는 제안을 했고 학생들도 좋다고 해서 현재도 진행중이다. 올해 2월이면 마무리될텐데 좋은 결과 냈으면 좋겠다.

저장소 : https://github.com/utilForever/Corailed

5월 - AIFrenz 세미나 “강화학습 환경 개발” 발표

유용균님의 제안으로 AI 프렌즈에서 “강화학습 환경 개발”이라는 주제로 발표하게 되었다. 지금까지 강화학습 환경 개발을 경험하면서 겪었던 시행 착오들과 개발할 때 고려해야 되는 것들, 그리고 OpenAI Gym과의 연결 방법을 실제 코드로 살펴 보며 알아보는 시간을 가졌다.

저장소 : https://github.com/utilForever/2021-AIFrenz-RLEnv

영상 주소 : https://www.youtube.com/watch?v=PuVLgXhEBpQ

5월 - 짧은 휴식 겸 여행 (?)

수도권에 있다 보니 지방에 있는 사람들을 만나기가 너무 힘들어 연차도 쓸 겸 4박 5일 여행을 다녀왔다. 익산 - 대구 - 울산 - 부산 일정으로 다녀왔으며 전북과학고등학교와 대구소프트웨어고등학교에서는 특강도 진행했다. 어째 여행이 아니라 출장을 다녀온 느낌이 들긴 하지만, 정말 많은 사람들을 만나면서 새로운 스터디와 프로젝트들을 계획할 수 있어서 나름 의미있는 일정이었다.

5월 - Microsoft Build 2021 라이브 세션 진행

Microsoft Build 2021에 개발자와 멘토링을 주제로 발표하게 되었다. 유일한 한국어 세션으로 큰 행사에 참여하게 되어 기쁘고 영광이었다. 비록 발표 시간은 5~10분 정도로 짧았지만 이소영님과 인터뷰를 진행하면서 많은 사람들에게 도움이 되었다고 생각하니 보람을 많이 느꼈다.

영상 주소 : https://docs.microsoft.com/en-us/events/build-may-2021/general/connection-zone/con009/

6월 - 얀센 백신 접종

당시만 해도 잔여 백신으로만 접종이 가능한 상황이었는데 다행히 얀센 백신이 들어와서 접종할 수 있게 되었다. 아스트라제네카, 화이자 백신과 달리 1회 접종만으로 끝나서 편하겠지만 그만큼 방어율이 떨어질 수도 있어서 백신을 맞고난 뒤에도 마스크 잘 쓰고 손도 잘 씻고 조심해야겠다고 생각했다. 백신 접종한 날에는 컨디션에 아무 문제가 없었는데, 잘 때가 되니 슬슬 몸살 기운이 느껴져 타이레놀 2알을 먹고 잤으며 다음 날까지 이어져 하루 푹 쉬었다. 그 뒤로는 괜찮아져서 주말까지 휴식을 취한 뒤 정상적으로 회사 업무를 했다.

6월 - 오픈 소스 프로젝트 기부

그동안 다양한 오픈 소스를 써왔는데 내가 이 분들을 위해서 할 수 있는 건 없을까 고민하다가, 유용하게 사용하는 몇몇 오픈 소스 프로젝트에 기부를 했다. 현재 1달에 약 150달러를 기부하고 있는데 크지는 않지만 내 기부를 통해 이 분들에게 도움이 되었으면 좋겠다. 오픈 소스 생태계가 좀 더 건강해지길 바란다.

7월 - Microsoft MVP 선정

2021-2022 Microsoft MVP로 선정되어 새로운 1년을 함께하게 되었다. 2015년이 첫 시작이었는데 어느덧 7년차가 되었다. 도와주신 모든 분들께 진심으로 감사드리며, 앞으로도 많은 커뮤니티 활동을 하도록 노력할 생각이다. C++과 개발 전반에 관련해 도움이 필요하시다면 주저하지 말고 말씀해주시기 바란다.

7월 - 게임이랑 무관합니다만 밋업 “ECS 기반 게임 개발” 발표

이전부터 “코딩이랑 무관합니디만” 그룹에서 발표 제의가 몇 번 왔었는데, 주제나 시간이 맞지 않아 여러번 거절한 적이 있다. 그러다가 게임 개발 관련을 주제로 발표 요청이 와서 이번에 발표하게 되었다. 어떤 주제로 발표할 지 고민하다가 최근에 사이드 프로젝트에서 사용했던 ECS 기반 게임 개발로 발표하면 좋을 거 같아 예제 코드를 만들어 발표했다.

저장소 : https://github.com/utilForever/2021-CoMu-ECS-Development

7월 - Nexon Korea 퇴사

7월 말에 넥슨코리아를 떠나게 되었다. 넥슨에 전문연으로 입사해 어느덧 5년 10개월이라는 시간이 지났는데, 게임 프로그래밍에 대해 거의 모르는 상태로 입사해 수많은 시행 착오를 거치며 많이 배웠고 성장할 수 있었다. 이 자리를 빌어 저를 도와주신 모든 분들께 감사의 인사를 드린다. 길다면 길고, 짧다면 짧은 시간이지만 게임 개발과 관련해 여러 작업을 해볼 수 있었다. 버그 수정부터 보스 레이드, 신규 캐릭터, 라이브 서비스, VS 마이그레이션, 게임 물리 엔진 개발까지 이거저거 다 해봤다. 덕분에 다양한 경험을 해보게 되었다. 작년부터 게임 물리 엔진 개발이 어느 정도 마무리되면 새로운 도전을 해볼까 생각하고 있었는데, 몇 군데서 좋은 제안이 와 검토한 끝에 모멘티로 이직하게 되었습니다. 대기업이 아닌 스타트업이고, 게임 회사도 아니라서 모든 게 달라진다고 볼 수 있었다. 하지만 새로운 도전을 하기에 최적의 시기라고 생각해 심사숙고해 결정하게 되었다.

관련 내용 : 첫번째 이직 이야기

8월 - Momenti 입사

7월 말에 퇴사하고 8월 초에 바로 모멘티에 엔진 엔지니어로 입사하게 되었다. 내가 맡은 업무는 다음과 같다.

  • Frontend, Backend 및 iOS에 사용할 Rust 기반 엔진을 설계하고 구현합니다.
    • Frontend : Rust ↔ WebAssembly ↔ Javascript/Typescript
    • Backend : Rust ↔ Elixir
    • iOS : Rust ↔ Objective-C ↔ Swift
  • Frontend, Backend, iOS 개발자분들과 함께 더 나은 엔진 아키텍처를 위해 고민하고 논의합니다.
  • 제품과 엔진에 필요한 새로운 기능을 구현합니다.
  • 여러 플랫폼에서 발생할 수 있는 다양한 문제의 원인을 분석하고 해결합니다.

사용하고 있는 기술 스택은 다음과 같다.

  • Programming Language
    • Rust
    • WebAssembly
    • Javascript/Typescript
    • Elixir
    • Objective-C/Swift
  • Framework
    • wasm-bindgen (Wasm modules and JavaScript API)
    • serde (Serialization Framework)
    • rxrust (ReactiveX API)
  • Build & CI
    • Github Actions

넥슨에서도 물리 엔진 개발을 했지만, 하는 일은 전혀 다르다. 지금은 크로스 플랫폼을 고려한 코어 엔진을 만드는 작업을 하고 있고 문제가 발생했을 때 검색해도 나오지 않는 게 많아서 해결하기도 쉽지 않다. 어찌 보면 비슷한 점도 있는 거 같다. 처음에는 혼자 작업하느라 어려운 점이 많았지만 엔진 엔지니어 한 분이 새로 입사하셔서 서로 코드 리뷰를 진행하니 진행 속도에도 탄력이 붙고 많이 성장하고 있다고 느끼는 중이다. 마지막 팀 미팅 때 Rust 엔진이 들어간 앱이 정상적으로 실행되는 모습을 보며 많이 감동했는데 앞으로도 이 기분을 계속 느끼게 될 것이다.

8월 - 오픈 소스 관련 인터뷰

OpenUP에서 인터뷰 제안을 해주셔서 오픈 소스 관련 인터뷰를 진행하게 되었다. 평소에 하고 있던 사이드 프로젝트 이야기, 그리고 평소에 갖고 있던 생각들을 이야기하게 되었는데 기자님께서 잘 정리해주셔서 좋았다. 자세한 내용은 기사 링크를 참고해주시면 좋겠다.

기사 링크 : https://www.ddaily.co.kr/news/article/?no=220499

9월 - rxRust 기여

회사에서 코어 엔진을 개발하려면 Reactive Extension이 필요한데 Rust로 구현된 프로젝트 중에 마땅한 게 없어서 그나마 진행이 많이 된 rxRust를 선택했다. 그런데 필요한 Subject나 Operator가 없어서 구현하는데 애를 먹었다. 그래서 필요한 것들을 하나씩 구현해서 기여하기 시작했다. 올해 나와 팀원분이 제출한 PR만 10개가 넘는다. 회사를 다니면서 오픈 소스에 의미있는 작업을 해서 보람을 많이 느꼈다.

저장소 : https://github.com/rxRust/rxRust

9월 - 한양대학교 HAI 스터디 “강화학습 부트캠프” 시작

HAI 동아리장이었던 성환이의 부탁으로 강화학습 부트캠프 스터디를 시작했다. 강화학습 부트캠프는 강화학습 기초 수업을 들은 학생들을 대상으로 다양한 알고리즘들을 배우는 과정으로 기획했다. DQN Extensions으로 시작해 DDPG, TRPO, PPO 등 최신 알고리즘을 다루다 보니 난도가 좀 높은 편인데, 갈수록 참여율이 떨어져 아쉽다고 느낀 스터디였다. 아직 끝나지는 않았고 2022년 1월 말까지 진행할 예정이다.

저장소 : https://github.com/utilForever/2021-HYU-HAI-RLBootCamp

9월 - 경북대학교 ICPC World Finals 2020 항공권 지원

학교 후배들이 좋은 기회를 얻어 2020 ICPC World Finals Moscow에 진출하게 되었는데, 항공권 금액이 생각보다 비싸서 고민을 하고 있길래 흔쾌히 지원해주기로 했다. 흔치 않은 기회인데 항공권 때문에 참가하지 못하면 얼마나 슬픈 일이겠는가? 선배란 무엇인가… 후배들이 더 좋은 경험과 도전을 할 수 있게끔 뒤에서 말이 아닌 행동으로 지원하는 사람이라 생각한다. (이후에 학교에서 지원 결정이 내려져 지원했던 항공권 금액은 정산되었다.)

10월 - 탈잉 “월간 코드리뷰 ver 0.1” 웨비나

신수철님의 제안으로 10월 1일, 탈잉에서 진행하는 “월간 코드리뷰 ver 0.1” 웨비나에서 “오픈소스 프로젝트 키우기 : 파종부터 추수까지! 오픈소스 재배 일기”라는 제목으로 발표하게 되었다. 그동안 진행했던 여러 오픈 소스 프로젝트들을 바탕으로 어떻게 구성하고 발전시킨 과정을 압축해 내용을 채웠다. 자세한 내용은 다음 링크를 참고하시면 좋겠다.

영상 주소 : https://taling.me/Talent/Detail/38586

10월 - 대구소프트웨어마이스터고등학교 특강 “Rainbow Is All You Need”

올해도 오프라인 강의를 한 번도 못하나 싶었는데, 선생님과 이야기가 잘 되어서 1박 2일 동안 강화학습 특강을 맡게 되었다. 어느 정도 시간이 확보되어서 강화학습 기초부터 Rainbow DQN까지 배워보는 걸 목표로 했다. 빡빡한 일정 속에서도 학생들이 잘 따라와줘서 고마웠고 마지막에 간단한 대회를 열어서 마무리했다.

저장소 : https://github.com/utilForever/2021-DGSW-RL-Rainbow

10월 - 사내 기술 세미나 발표 “A Tour of Rust”

사내 기술 세미나에 발표할 기회가 생겨 다른 팀원들에게도 Rust 언어를 소개해드리면 좋겠다는 생각이 들어 “A Tour of Rust”라는 주제로 3주에 걸쳐 발표했다. 처음 보시는 분들도 쉽게 이해할 수 있도록 비유를 들어가며 설명했는데 팀원분들이 많은 도움이 되었다고 칭찬해주셔서 기분이 좋았다.

10월 - RustFest Global 2021 Organizer 합류

우연찮은 기회로 RustFest Global 2021 Organizer로 합류하게 되었다. 홈페이지 텍스트를 한국어로 번역하고, 한국 연산자들이 원활하게 의사소통할 수 있도록 통역하는 역할을 맡았다. 이후 연사자가 충분히 모집되지 않아 행사가 취소되었지만 짧은 기간 동안 뜻깊은 작업을 함께할 수 있어서 영광이었다.

홈페이지 : https://rustfest.world/

10월 - 새 노트북 구입

마이크로소프트 서피스 북 2를 산 지 어느덧 6년이 되었다. 회사나 카페에서 작업을 하다 보면 빌드 속도가 느려서 답답함을 느낀 적이 많았다. 이번에 서피스 북 4가 나오면 교체할 생각이었으나, 다른 노트북 라인업이 발표되었는데 예상보다 성능이 좋지 않았으며 내년에 출시된다고 해서 다른 노트북을 알아보게 되었다. 이후 애플에서 발표한 M1 Pro/Max 맥북 프로가 생각보다 뛰어난 성능으로 나와서 구입하게 되었다. 최상위 라인업으로 약 600만원의 지출이 있었는데 내 시간을 아껴준다는 생각으로 망설임없이 구입했다. 앞으로 몇 년 간은 이 노트북으로 개발하게 될 것이다.

11월 - 세종과학예술영재학교 특강 “게임 프로그래머와 엔진 프로그래머”

11월 18일에 세종과학예술영재학교에서 진행하는 진로 체험의 날에 “게임 프로그래머와 엔진 프로그래머”라는 주제로 발표를 했다. 넥슨에서 일했던 경험들을 바탕으로 두 프로그래머가 어떤 일을 하는지 정리해서 발표했다. 학생들이 사전 질문을 잘 해줘서 답변하는데 오랜 시간이 걸렸다. 즐거운 시간이었다.

11월 - 부스터샷 백신 접종

6월에 얀센 백신을 접종한 이후로 부스터샷 백신 접종 안내를 받아 11월에 접종하게 되었다. 어떤 백신을 맞을지 몰랐는데 가보니 모더나였고 정량의 절반만 투여한다고 했다. 우려와는 달리 큰 부작용 없이 지나가서 다행이라 생각했다. 아마 올해 상반기에 한 번 더 맞지 않을까 싶은데, 빨리 종식되었으면 하는 바람이다.

11월 - “Rust For Rustaceans” 번역 계약

도서출판 인사이트(@insightbook)와 Jon Gjengset(@jonhoo)의 “Rust for Rustaceans” 책의 번역을 진행하게 되었다. 최근 Rust 언어의 관심이 갈수록 커지고 있고, 회사에서도 Rust로 엔진을 만들고 있는데 운좋게 번역할 기회가 생겼다. 올해 상반기를 목표로 양질의 번역서를 출간할 수 있도록 노력하겠다.

12월 - 게임 개발자 인터뷰

동빈이의 부탁으로 게임 개발자 인터뷰를 진행하게 되었다. 다양한 질문에 답변을 했고, 유튜브에 올라간 뒤 댓글을 보니 많은 분들에게 도움이 된 거 같아 잘 대답했다는 생각을 했다. 이후에 발표할 기회가 있다면 현재 회사에서 하고 있는 경험들을 토대로 이야기할 수 있으면 좋겠다.

영상 주소 : https://www.youtube.com/watch?v=Yye5SF4MLps

12월 - 선린인터넷고등학교 2021 알고리즘 컨퍼런스 “Rust로 알고리즘 문제 풀어보기”

심준님의 요청으로 선린인터넷고등학교 2021 알고리즘 컨퍼런스에서 발표를 하게 되었다. 최근 Rust로 알고리즘 문제들을 풀면서 느꼈던 경험들을 정리해 이야기하면 좋겠다고 생각했다. Rust는 어떤 언어이며 알고리즘 문제를 풀 때 어떤 점들이 불편하고 어떻게 해결하면 좋은지 말했다.

발표 슬라이드 : https://www.slideshare.net/utilforever/2021-rust

12월 - game-developer-roadmap 리뉴얼

2017년에 처음 game-developer-roadmap을 만들고 나서, 한 번도 리뉴얼을 하지 않았는데 미루고 미루다가 5년 만에 전면 개편하게 되었다. 클라이언트 로드맵의 경우 아예 처음부터 다시 만들었고, 서버 로드맵의 경우 기존 내용에 불필요한 부분을 삭제하고 새로운 내용을 추가하는 형태로 작업했다. 많은 분들에게 도움이 되었으면 좋겠다.

저장소 : https://github.com/utilForever/game-developer-roadmap

오픈소스 기여

2022년 계획

  • 기술 도서 집필하기 (올해는 꼭 마무리했으면 좋겠다…)
  • C++ Crash Course 번역 마무리 및 출간
  • Rust for Rustaceans 번역 마무리 및 출간
  • C++ Korea 임의단체 출범
  • 자연어 처리 (NLP) 공부하기
  • C++ 및 Rust 기반 사이드 프로젝트 진행
  • 프로그래밍 언어 TypeScript 및 Elixir, Julia, F# 배우기
  • Rust로 배우는 인터프리터/컴파일러 스터디 진행
  • 컴퓨터 그래픽스 스터디 진행
  • 건강 관리하기
    • 식단 조절 (선식)
    • 꾸준한 운동 (필라테스 또는 PT)
    • 체중 감량 (15kg)

회고를 쓰다 보니 작년에도 이리저리 한 일들이 많았다. 특히 물리 엔진을 출시하고 이직한 게 기억이 많이 남는다. 올해도 많은 일들을 계획하고 있는데 건강과 컨디션 잘 지키면서 목표 달성했으면 좋겠다. 2022년 새해 복 많이 받으시고, 원하는 목표 다 이루는 한 해가 되었으면 합니다! 끝까지 읽어주셔서 감사합니다.

RosettaStone 개발 일지 1 - 몇 가지 버그 수정

들어가며

평소 블로그에 연말 회고를 제외하고는 글을 잘 쓰지 않았는데, 최근에 이직 관련 글을 올리면서 개발 일지도 써보면 좋을 거 같아 시작해보려고 한다.

RosettaStone은 블리자드에서 서비스하는 게임 하스스톤의 시뮬레이터를 만드는 프로젝트이며 나아가 강화학습을 통해 프로게이머 수준으로 플레이하는 에이전트를 만드는 걸 목표로 하고 있다. 2017년 5월부터 개발을 시작했으며 4년이 지난 현재도 신규 확장팩에 등장하는 새로운 키워드 및 카드들을 구현하며 활발하게 개발중이다.

rosettastone

첫번째 개발 일지로 무엇을 쓸까 고민하다가 최근에 몇 가지 버그를 고쳤던 이야기를 해보려고 한다.

살아있는 씨앗 (1 레벨)

living_seed

살아있는 씨앗 (1 레벨)야수를 뽑습니다. 그 야수의 비용이 (1) 감소합니다. (보유한 마나가 5일때 강화됩니다.)라는 효과를 갖는 카드다. 최근에 확장팩 ‘불모의 땅’ 카드들을 구현하면서 같이 작업한 카드인데, 이 카드는 보유한 마나가 특정 값이 될 때 레벨이 증가하는 카드라 기존에 없던 방식이었다. 그래서 마나가 증가할 때 동작하는 트리거를 구현했었다.

{
    ...

    // Process mana crystal trigger
    game->triggerManager.OnManaCrystalTrigger(this);
}

그런데 밑에 있는 요그사론의 수수께끼 상자 카드를 테스트하던 도중 매우 드문 확률로 문제가 생긴다는 사실을 알게 되었다. 디버깅한 결과 요그사론의 수수께끼 상자 카드를 시전하는 도중 보유한 마나를 증가시키는 주문 카드를 시전하고 나면 이 카드가 강화되어야 하는데 강화가 되지 않아 다른 주문 카드를 시전할 때 크래시가 발생하고 있었다. 단순히 트리거만 동작시킬 게 아니라 관련된 태스크들을 처리하고 갱신해줘야 했는데 처리하는 코드를 누락해서 생긴 문제였다. 원인을 파악했으니 다음과 같이 코드를 수정해 문제를 해결했다.

{
    ...

    // Process mana crystal trigger
    game->taskQueue.StartEvent();
    game->triggerManager.OnManaCrystalTrigger(this);
    game->ProcessTasks();
    game->taskQueue.EndEvent();
    game->ProcessDestroyAndUpdateAura();
}

요그사론의 수수께끼 상자

yogg

플레이어의 관점에서 보는 요그사론의 수수께끼 상자는 하스스톤이 갖는 ‘무작위성’을 가장 잘 보여주는 카드다. 임의의 주문을 시전하는데 대상도 무작위라 사용했을 때 시전된 주문에 따라 한 순간에 판세가 뒤바뀔 수 있는 반전의 카드라고 볼 수 있다.

개발자의 관점에서 보면 어떨까? 하나씩 구현한 주문 카드들의 순서에 따라 올바르게 잘 동작하는지 테스트해 볼 수 있는 멋진 교보재와 같은 카드다. 이 카드 덕분에 버그를 발견할 수 있고, 원인을 파악해 고칠 수도 있다. 그래서 이 카드의 테스트 횟수는 적으면 100번, 많게는 10,000번 정도다.

그러다 보니 다른 카드들을 구현하다 보면 매우 드문 확률로 크래시가 발생할 때가 있다. 이전에 문제가 발생해 고쳤던 적이 있는데 확장팩 ‘불모의 땅’ 카드들을 구현하면서 문제가 재발했다. 무엇이 문제였을까? 디버깅을 해보니 원인은 2가지였다.

원인 1

하수인 카드를 드로우하는 DrawMinionTask 로직은 다음과 같다.

TaskStatus DrawMinionTask::Impl(Player* player)
{
    if (m_addToStack)
    {
        player->game->taskStack.playables.clear();
    }

    auto deckCards = player->GetDeckZone()->GetAll();
    if (deckCards.empty())
    {
        return TaskStatus::STOP;
    }

    EraseIf(deckCards, [=](Playable* playable) {
        return playable->card->GetCardType() != CardType::MINION;
    });

    ...
}

DrawMinionTask이 하는 일은 덱에 있는 하수인 카드 중 원하는 수만큼 임의로 뽑는 것이다. 하지만 코드를 보면 뭔가 문제가 있음을 알 수 있다. 지금은 덱에 있는 카드 목록을 가져와 먼저 덱이 비었는지를 확인한 뒤 하수인 카드만 남도록 필터링하고 있다. 하지만 덱에 주문 카드만 있다면, 덱이 빈 상태가 아니니 if문을 통과하게 되고 하수인 카드만 남도록 필터링하면 아무 카드도 남지 않게 된다. 이 상태에서 하수인 카드를 뽑으려고 시도하니 문제가 발생한 것이었다. 따라서 덱이 빈 상태를 확인하는 코드를 하수인 카드만 남도록 필터링한 다음 확인하도록 수정했다.

TaskStatus DrawMinionTask::Impl(Player* player)
{
    if (m_addToStack)
    {
        player->game->taskStack.playables.clear();
    }

    auto deckCards = player->GetDeckZone()->GetAll();

    EraseIf(deckCards, [=](Playable* playable) {
        return playable->card->GetCardType() != CardType::MINION;
    });

    if (deckCards.empty())
    {
        return TaskStatus::STOP;
    }

    ...
}

원인 2

지옥영혼 간수라고 하는 하수인 카드가 있다.

felsoul_jailer

이 하수인의 카드 효과는 다음과 같이 구현되어 있다.

// --------------------------------------- MINION - WARLOCK
// [CS3_003] Felsoul Jailer - COST:5 [ATK:4/HP:6]
// - Race: Demon, Set: CORE, Rarity: Epic
// --------------------------------------------------------
// Text: <b>Battlecry:</b> Your opponent discards a minion.
//       <b>Deathrattle:</b> Return it.
// --------------------------------------------------------
// GameTag:
// - BATTLECRY = 1
// - DEATHRATTLE = 1
// --------------------------------------------------------
power.ClearData();
power.AddPowerTask(
    std::make_shared<DiscardTask>(1, DiscardType::ENEMY_MINION, true));
power.AddDeathrattleTask(std::make_shared<CustomTask>(
    [](Player* player, Entity* source, [[maybe_unused]] Playable* target) {
        const int entityID =
            source->GetGameTag(GameTag::TAG_SCRIPT_DATA_ENT_1);
        Playable* playable = player->game->entityList[entityID];
        player->opponent->GetGraveyardZone()->Remove(playable);
        player->opponent->GetHandZone()->Add(playable);
    }));
cards.emplace("CS3_003", CardDef(power));

이 카드는 전투의 함성: 상대편은 하수인을 버립니다. 죽음의 메아리: 그 하수인을 상대편의 손으로 다시 돌려보냅니다.라는 효과를 갖고 있다. 문제는 죽음의 메아리 효과에 있었다. 요그사론의 수수께끼 상자 카드가 주문을 시전하던 도중 비용이 5인 무작위 하수인을 소환하는 주문으로 인해 지옥영혼 간수가 소환되었고, 이후 다른 주문으로 인해 이 하수인이 죽게 되었다고 가정해 보자. 죽음의 메아리 효과로 인해 하수인을 상대편의 손으로 다시 돌려보내야 하는데 주문으로 인해 소환된 하수인이라 전투의 함성 효과를 발동하지 않았기 때문에 돌려보낼 하수인이 없는 상태다. 때문에 ‘죽음의 메아리’ 효과를 처리하는 과정에서 문제가 생겨 크래시가 발생했던 것이다. 따라서 전투의 함성 효과를 발동했는지에 따라 죽음의 메아리 효과를 발동하도록 코드를 수정했다.

// --------------------------------------- MINION - WARLOCK
// [CS3_003] Felsoul Jailer - COST:5 [ATK:4/HP:6]
// - Race: Demon, Set: CORE, Rarity: Epic
// --------------------------------------------------------
// Text: <b>Battlecry:</b> Your opponent discards a minion.
//       <b>Deathrattle:</b> Return it.
// --------------------------------------------------------
// GameTag:
// - BATTLECRY = 1
// - DEATHRATTLE = 1
// --------------------------------------------------------
power.ClearData();
power.AddPowerTask(
    std::make_shared<DiscardTask>(1, DiscardType::ENEMY_MINION, true));
power.AddDeathrattleTask(std::make_shared<CustomTask>(
    [](Player* player, Entity* source, [[maybe_unused]] Playable* target) {
        const int entityID =
            source->GetGameTag(GameTag::TAG_SCRIPT_DATA_ENT_1);
        if (entityID > 0)
        {
            Playable* playable = player->game->entityList[entityID];
            player->opponent->GetGraveyardZone()->Remove(playable);
            player->opponent->GetHandZone()->Add(playable);
        }
    }));
cards.emplace("CS3_003", CardDef(power));

상점가 털기

bazaar_burglary

상점가 털기퀘스트: 다른 직업의 카드 4장을 내 손으로 가져와야 합니다. 보상: 고대의 검라는 효과를 갖는 카드다. 이 카드를 구현할 당시에는 문제가 없었는데 언젠가부터 가끔 크래시가 발생하는 문제가 있었다. 그래서 왜 크래시가 발생하는 것인지 원인을 찾아보기 시작했고, 디버깅을 통해 분석한 결과 문제는 전혀 다른 곳에 있었다.

상점가 털기의 카드 효과를 테스트하기 위한 코드 중에는 하수인 카드인 상점가 약탈자를 손에서 내는 부분이 있다.

bazzar_mugger

상점가 약탈자속공, 전투의 함성: 다른 직업의 무작위 하수인을 내 손으로 가져옵니다.라는 효과를 갖는 카드다. 이 카드를 통해 상점가 털기의 퀘스트를 완료할 수 있기 때문에 테스트 코드에 사용했었다. 이 카드의 효과로 인해 다양한 무작위 하수인을 손으로 가져오게 되는데 그 중에는 타락 키워드가 있는 하수인들도 있다.

fairground_fool

타락 키워드는 더 높은 비용의 카드를 낸 후에 손에서 강화되는 효과를 갖는다. 하스스톤 카드 데이터에는 타락되었을 때 강화된 카드를 알 수 있는 방법이 없기 때문에 수동으로 추가해줘야 한다. 문제는 여기에 있었다. 타락 키워드가 있는 카드 중 구현한 카드는 문제가 없는데 구현하지 않은 카드는 강화된 카드가 무엇인지 지정하지 않았기 때문에 타락 키워드를 처리하는 로직에서 문제가 발생했다.

// Process keyword 'Corrupt'
for (auto& playable : player->GetHandZone()->GetAll())
{
    if (playable->HasCorrupt() && source->GetCost() > playable->GetCost())
    {
        Card* newCard = Cards::FindCardByDbfID(
            playable->GetGameTag(GameTag::CORRUPTEDCARD));
        if (newCard != nullptr)
        {
            ChangeEntity(player, playable, newCard, true);
        }
    }
}

따라서 구현하지 않은 카드를 위해 방어 코드를 추가하는 걸로 마무리햇다.

// Process keyword 'Corrupt'
for (auto& playable : player->GetHandZone()->GetAll())
{
    if (playable->HasCorrupt() && source->GetCost() > playable->GetCost())
    {
        Card* newCard = Cards::FindCardByDbfID(
            playable->GetGameTag(GameTag::CORRUPTEDCARD));
        if (newCard != nullptr && !newCard->name.empty())
        {
            ChangeEntity(player, playable, newCard, true);
        }
    }
}

뱀 덫

snake_trap

뱀 덫비밀: 내 하수인이 공격받으면, 1/1 뱀을 3마리 소환합니다.라는 효과를 갖는 카드다. 최근 이 카드와 비슷한 효과를 갖는 오아시스 아군이라는 카드를 확장팩 ‘불모의 땅’을 작업하면서 구현했었다.

oasis_ally

오아시스 아군비밀: 내 하수인이 공격받으면, 3/6 물의 정령을 소환합니다.라는 효과를 갖는 카드다. 두 카드 모두 발동 조건이 똑같다. 내 하수인이 공격받으면 발동하는 카드다. 근데 여기서 주의할 점이 있다. 하스스톤의 카드 중에는 텍스트만으로는 알 수 없는 규칙들이 존재한다. 대표적으로 뱀 덫오아시스 아군이라는 카드가 그렇다. 두 카드의 발동 조건인 내 하수인이 공격받으면은 내 전장이 하수인으로 꽉 차있을 경우에는 발동 조건을 충족하더라도 발동하지 않는다. 이번에 오아시스 아군 카드를 구현하면서 뱀 덫 카드를 구현할 때 이 부분을 누락했던 사실을 발견했다.

// ----------------------------------------- SPELL - HUNTER
// [EX1_554] Snake Trap - COST:2
// - Faction: Neutral, Set: Expert1, Rarity: Epic
// --------------------------------------------------------
// Text: <b>Secret:</b> When one of your minions is attacked,
//       summon three 1/1 Snakes.
// --------------------------------------------------------
// GameTag:
// - SECRET = 1
// --------------------------------------------------------
power.ClearData();
power.AddTrigger(std::make_shared<Trigger>(TriggerType::ATTACK));
power.GetTrigger()->triggerSource = TriggerSource::ENEMY;
power.GetTrigger()->condition = std::make_shared<SelfCondition>(
    SelfCondition::IsProposedDefender(CardType::MINION));
power.GetTrigger()->tasks = ComplexTask::ActivateSecret(
    TaskList{ std::make_shared<SummonTask>("EX1_554t", 3) });
cards.emplace("EX1_554", CardDef(power));

그래서 트리거의 발동 조건에 내 전장이 하수인으로 꽉 차있지 않아야 한다는 조건을 추가하고 싶었다. 현재 구조는 트리거의 발동 조건을 하나만 지정할 수 있게 되어 있다. 따라서 이 부분을 고쳐서 발동 조건을 여러 개 추가할 수 있도록 수정하기 시작했다. 우선 Trigger 클래스의 변수 condition의 타입을 변경했다.

class Trigger
{
    ...
    std::vector<std::shared_ptr<SelfCondition>> conditions;
};

그리고 관련 코드를 전부 변경했다.

//! Trigger for enrage.
//! \param enchantmentID The card ID of enchantment.
static Trigger EnrageTrigger(std::string&& enchantmentID)
{
    Trigger trigger(TriggerType::PREDAMAGE);
    trigger.triggerSource = TriggerSource::SELF;
    trigger.conditions = SelfCondList{ std::make_shared<SelfCondition>(
        SelfCondition::IsUndamaged()) };
    trigger.tasks = { std::make_shared<SimpleTasks::AddEnchantmentTask>(
        std::move(enchantmentID), EntityType::SOURCE) };

    return trigger;
}

이어서 내 전장이 하수인으로 꽉 차있지 않아야 한다는 조건을 확인하기 위한 메소드를 하나 추가했다.

//! SelfCondition wrapper for checking the field of event target
//! is not full.
//! \param cardType The type of the card to check.
//! \return Generated SelfCondition for intended purpose.
static SelfCondition IsEventTargetFieldNotFull()
{
    return SelfCondition([](Playable* playable) {
        if (const auto eventData = playable->game->currentEventData.get();
            eventData)
        {
            return !eventData->eventTarget->player->GetFieldZone()->IsFull();
        }

        return false;
    });
}

위에서 추가한 메소드를 사용해 뱀 덫의 카드 로직을 수정했다.

// ----------------------------------------- SPELL - HUNTER
// [EX1_554] Snake Trap - COST:2
// - Faction: Neutral, Set: Expert1, Rarity: Epic
// --------------------------------------------------------
// Text: <b>Secret:</b> When one of your minions is attacked,
//       summon three 1/1 Snakes.
// --------------------------------------------------------
// GameTag:
// - SECRET = 1
// --------------------------------------------------------
power.ClearData();
power.AddTrigger(std::make_shared<Trigger>(TriggerType::ATTACK));
power.GetTrigger()->triggerSource = TriggerSource::ENEMY;
power.GetTrigger()->conditions =
    SelfCondList{ std::make_shared<SelfCondition>(
                        SelfCondition::IsProposedDefender(CardType::MINION)),
                    std::make_shared<SelfCondition>(
                        SelfCondition::IsEventTargetFieldNotFull()) };
power.GetTrigger()->tasks = ComplexTask::ActivateSecret(
    TaskList{ std::make_shared<SummonTask>("EX1_554t", 3) });
cards.emplace("EX1_554", CardDef(power));

마지막으로 내 전장이 하수인으로 꽉 찬 상태일 때 비밀이 발동하지 않는지 확인하는 테스트 코드를 추가했다.

// ----------------------------------------- SPELL - HUNTER
// [EX1_554] Snake Trap - COST:2
// - Faction: Neutral, Set: Expert1, Rarity: Epic
// --------------------------------------------------------
// Text: <b>Secret:</b> When one of your minions is attacked,
//       summon three 1/1 Snakes.
// --------------------------------------------------------
// GameTag:
// - SECRET = 1
// --------------------------------------------------------
TEST_CASE("[Hunter : Spell] - EX1_554 : Snake Trap")
{
    ...
    
    const auto card7 =
        Generic::DrawCard(curPlayer, Cards::FindCardByName("Wisp"));
    const auto card8 =
        Generic::DrawCard(curPlayer, Cards::FindCardByName("Wisp"));
    const auto card9 =
        Generic::DrawCard(curPlayer, Cards::FindCardByName("Wisp"));

    ...

    game.Process(curPlayer, AttackTask(card4, card5));
    CHECK_EQ(curSecret->GetCount(), 1);
    CHECK_EQ(curField.GetCount(), 4);
    CHECK_EQ(opField.GetCount(), 1);

    game.Process(curPlayer, PlayCardTask::Minion(card7));
    game.Process(curPlayer, PlayCardTask::Minion(card8));
    game.Process(curPlayer, PlayCardTask::Minion(card9));
    CHECK_EQ(curField.GetCount(), 7);

    game.Process(curPlayer, EndTurnTask());
    game.ProcessUntil(Step::MAIN_ACTION);

    game.Process(opPlayer, AttackTask(card5, curField[6]));
    CHECK_EQ(curSecret->GetCount(), 1);
    CHECK_EQ(curField.GetCount(), 6);
}

마치며

위 카드들은 여러 PR(#610, #614, #621)을 통해 수정되었다. 각 카드로 인해 발생할 수 있는 다양한 경우를 미리 테스트 코드로 작성해 뒀기에 발견할 수 있었으며 앞으로도 카드를 구현할 때마다 여러 상황을 고려해 개발하고 테스트 코드도 만들 예정이다.

한편, RosettaStone 2.0 작업을 준비하고 있다. 최근 ECS(Entity-Component System)에 관심을 갖게 되었는데 강화학습과 상당히 잘 어울린다는 생각이 들어 OOP 기반으로 되어 있는 코드를 바꿔볼 생각이다. 이에 대한 자세한 이야기는 다른 글을 통해서 다뤄보도록 하겠다. 이외에 할 일 목록으로 Logger 클래스 추가, 하스스톤: 전장 재작업, 콘솔/GUI 프로그램 재작업, 강화학습 기반 코드 재작업 등을 생각하고 있다. 아직 작업을 시작하진 않았고 여유가 있을 때 조금씩 하려고 한다. (C++로 할 지, Rust로 할 지 고민중이다. 물론 둘 다 할 수도 있다.)

첫번째 개발 일지는 여기서 마무리하려고 한다. 앞으로도 기록을 남겨야 할 작업이 있을 때마다 정리해서 공유할 수 있도록 하겠다. 여기까지 열심히 읽어주신 모든 분들께 감사드린다.

첫번째 이직 이야기

넥슨에 전문연구요원 복무를 위해 입사를 했던 게 2015년 9월이다. 대개 전문연구요원 복무가 끝나면 이직을 하기 마련이지만, 좋은 팀원분들이 많았고 업무도 마음에 들어서 거의 6년 동안 다녔다. 처음에는 게임 프로그래밍에 대해 거의 모르는 상태로 입사해 고생했지만 수많은 시행착오를 거치며 많이 배웠고 성장할 수 있었다. 이 글을 통해 넥슨에 다니는 동안 저를 도와주신 모든 분께 감사의 말씀을 전하고 싶다.

길다면 길고, 짧다면 짧은 시간이지만 여러 작업을 해볼 수 있었다. 처음에는 간단한 버그 수정부터 시작해 보스 레이드, 신규 캐릭터, 라이브 서비스, Visual Studio 마이그레이션, 게임 물리 엔진 개발까지 게임 프로그래밍에서 해볼 수 있는 다양한 경험을 해보게 되었다. 게임 플레이 프로그래머와 게임 엔진 프로그래머를 동시에 경험하는 기회는 흔치 않은데 우연히 좋은 기회를 얻어 의미 있는 결과물들을 만들었고 많은 성취감을 느낄 수 있었다.

작년에 한창 게임 물리 엔진을 개발하면서 인생의 다음 단계를 생각해보기 시작했다. 현재 소속된 팀에 계속 남아서 게임 개발 또는 게임 엔진 개발을 할 것인가, 아니면 사내 이동을 해서 다른 팀에서 일해볼 것인가, 아니면 다른 회사로 이직을 할 것인가 등 여러 선택지가 있었다.

나는 회사나 팀을 선택할 때 내가 여기에 있으면서 많이 성장할 수 있는가를 고려한다. 지난 6년 가까이 새로운 지식을 많이 익히며 많은 성장을 할 수 있었지만, 작년부터 성장 곡선이 완만해지고 있음을 조금씩 느꼈다. 그리고 게임 물리 엔진까지 경험하고 나니 내가 게임 프로그래밍에서 해볼 수 있는 작업은 한 번씩 돌아보지 않았나 생각하게 되었다. 그래서 새로운 도전을 하면서 다시 성장하기 위해 이직을 하기로 했다.

신기하게도 이직을 하겠다고 마음을 정하고 나니 다양한 곳에서 이직 제의가 왔었다. 게임뿐만 아니라 인공지능, AR/VR, 렌더링 등 다양한 분야에서 좋은 기회를 제공해주셨다. 그중 몇몇 회사와 면접을 봤고 좋은 오퍼를 받았다. 나는 그중 하나의 회사를 선택해야 했었다. 오랜 시간 동안 고민을 했는데 결정하기 쉽지 않았다. 그래서 나는 몇 가지 기준을 세우기로 했다.

1. 익숙한 곳이 아닌 새로 도전해야 하는 요소가 많은가?
2. 내가 이 회사에 갔을 때 많이 성장할 수 있는가?
3. 나의 성장을 토대로 회사도 많이 성장할 수 있는가?
4. 회사에서 만들 아이템과 기술 스택이 흥미로운가?
5. 회사에서 업무에 알맞은 보상을 해주는가?

이 기준에 따라 나는 새로 도전해야 하는 요소가 가장 많고 성장을 많이 할 수 있을 것 같은 회사인 모멘티를 선택했다. 보상 측면에서만 보자면 모멘티보다 더 좋은 오퍼를 해주는 곳도 있었다. 하지만 새로운 도전을 많이 해보고 싶었다. 대기업이 아닌 스타트업이고, 게임이 아닌 새로운 아이템이고, 사용 언어도 C++에서 Rust가 되고, 새로운 사람들이 있는 곳이었기 때문이다.

그만큼 변화의 폭이 크기 때문에 새로운 회사에서 잘 적응할 수 있을지 기대 반, 걱정 반이다. 하지만 넥슨에서 그랬던 것처럼 시행착오를 거치며 시나브로 잘 적응할 수 있을거라 생각한다. 지금도 Rust와 WebAssembly를 공부하며 새로운 지식을 하나씩 익혀나가고 있다. 현재보다는 미래를 바라보며 한 걸음씩 나아간다면 앞으로 4~5년 뒤, 나는 더 많이 성장해있지 않을까?