370 likes | 541 Vues
가상의 온라인 게임으로 살펴보는 리팩토링의 솔직한 이야기. 2008.11.14 김익중 2008 (R) http://rhea.pe.kr/. Contents. 가상의 온라인 게임으로 살펴보는 리팩토링의 솔직한 이야기 1) 일러두기 2) 리팩토링 3) 가상의 온라인 게임 NEMO Online 4) NEMO Online 제작기 5) 개발 중 리팩토링 6) 실패 사례 분석 7) NEMO Online 리팩토링 8) 배포 9) 반성하기 10) 결론. 일러두기.
E N D
가상의 온라인 게임으로 살펴보는리팩토링의 솔직한 이야기 2008.11.14 김익중 2008 (R) http://rhea.pe.kr/
Contents 가상의 온라인 게임으로 살펴보는 리팩토링의 솔직한 이야기 1) 일러두기 2) 리팩토링 3) 가상의 온라인 게임 NEMO Online 4) NEMO Online 제작기 5) 개발 중 리팩토링 6) 실패 사례 분석 7) NEMO Online 리팩토링 8) 배포 9) 반성하기 10) 결론
일러두기 • 우리 나라의 온라인/네트워크 게임의 특징 • 중박(?) 이상의 힛트를 치긴 무척 힘들다 • 한번 히트를 친 게임은 쉽게 스테디셀러 게임이 된다 • 온라인 게임의 특성상 스테디 셀러가 된 이후에도 • 꾸준한 업데이트가 필요하다.
리팩토링 • 리팩토링이란? (from 위키백과) • 소프트웨어 공학에서 리팩토링(refactoring)은 주로 '결과의 변경 없이 코드의 구조를 재조정함'을 뜻한다. 주로 가독성을 높이고 유지보수를 편하게 한다. • 버그를 없애거나 새로운 기능을 추가하는 행위는 아니다. • 사용자가 보는 외부 화면은 그대로 두면서 내부 논리나 구조를 바꾸고 개선하는 유지보수 행위이다. • 왜 리팩토링에 관심을 가져야 하는가? • 원래 SI 쪽이나 오랜 시간 version up 을 해온 해외 애플리케이션들만의 영역 • 몇 년 사이 한국의 온라인 게임들에게 필수적인 상황이 되었음 • 80:20 법칙은 게임 개발에도 통한다 • 이제까지 80%는 기존 코드에 대한 추가/업데이트였으며 전혀 새로운 코드로 채우는 것은 20%에 불과하였다.
리팩토링 • 리팩토링이 필요할때는? • 코드가 유지보수하기 힘들어졌을때 • 몇년간 서비스를 지속하는 게임에서만 일어나는 과정이 아님 • 신규 게임 제작을 하더라도 기존 라이브러리는 반드시 참조/이용하게 됨 • 결국 소스 코드의 생명력은 게임 생명 주기보다 훨씬 길어지게 됨 • 리팩토링은 항상 해야만 하는 것(애자일 개발) • 개발 완료후 유지보수 단계에서만 일어나는 일이 아니다 • 잘못된 생각 : • "이 게임은 시장에서 딱 2년만 서비스하면 되는 것이거든요? 그러니까 그런거 신경쓸 필요는 없어요."
리팩토링 • 리팩토링이 자주 발생하는 프로그램은? • 특정 영역사이의 브릿지 프로그램 • 특정 클래스 사이의 브릿지 클래스 • 특정 담당자가 없는 프로그램 • 서로 다른 팀업무 사이에 놓인 프로그램 • → 한마디로 만만한 프로그램
가상의 온라인 게임 NEMO Online • 가상의 온라인 게임 "NEMO Online" • 개발 시작은 2003년 무렵 • 게임의 특징 • 풀스크린일 경우 4:3 모니터와 16:9 모니터에 알맞게 최적화 • 윈도우 모드에서는 리사이즈 가능 및 UI 구성요소 재배치 • 로비에서 대부분의 정보는 리스트 컨트롤 형태로 제작 • 게임 중에도 로비에 위치한 유저와 통신 및 100% 동일한 기능을 사용할수 있어야 한다 • 다중 세션의 허용 - 한 클라이언트에서 동시에 플레이어를 2개 이상 배치가능 • 서버 구분 없음 - 접속자가 늘게 되면 서버를 구분할려고 기획함 • CPU 대전 지원
가상의 온라인 게임 NEMO Online • 코드의 특징 • 개발자라면 누구나 '잘' 만들려고 한다. • 개발자는 소스 코드내 동일함수의 이중 코딩을 엄격히 제한 • C++의 폴리모피즘과 인터페이스의 철저한 사용 • 자주 사용하게 될 기능은 전부 Static Library로 구성 • 모든 자료구조는 STL만을 이용함(Map, List, Vector 등...) • 게임 사이트내 표준 UI, 표준 요구사항을 기존 플랫폼이 아닌 호환성 있는 독자적인 플랫폼으로 재구성 • 유니코드 • Win98에서도 작동가능 • 개발중 수시로 리팩토링을 시도함
NEMO Online 제작기 • 세션 클래스 • 로긴 이후사용자 정보를 유지하는 기본 클래스 생성 • 세션정보가 필요한 모든 구성요소는 세션 클래스에서 상속 • 실제로 엄청난 분량의 다중상속들로 구현
NEMO Online 제작기 • 전체 사용자 정보를 유지 • 클라이언트는 RAM이 많고 인터넷도 빠르므로 모든 정보를 갖고 있자. • 서버에게 상세 정보 요청을 하지 않는다. • 로비 혹은 게임의 어느 위치에 있던 접속된 모든 사용자 정보를 생성, 업데이트, 삭제 • 이로 인해 빠른 위치(Zone) 이동 가능 • 전체 사용자 정보 클래스에서 상속을 이용하여 세분화된 사용자 정보 클래스 구현 (전체정보, 게임정보, 사이트 공통 정보, 친구 정보, etc...) • 싱글턴 패턴으로 전역변수화 시킴 • 모든 정보는 리스트화 시켜 표현 • UI 리스트는 팩토리 패턴 이용
NEMO Online 제작기 • 전체 화면시 모니터 해상도 비율 • 4:3과 16:9 에서 각각 UI 구성요소가 다름 • 16:9 로비에서는 사용자 정보 리스트가 추가됨 • 화면을 구성하는 추상 인터페이스 선언 • 인터페이스를 상속 받아 해상도 비율에 따른 독립적인 UI 구성
NEMO Online 제작기 • 리사이즈 • 리사이즈시 필요한 정보(창의 너비, 높이)를 위해 각각의 가상 윈도우 개념을 도입 • 전체 윈도우 사이즈를 기반으로 WM_SIZE를 이용하여 구성 • 가상 윈도우는 GDI 정보와 D3D 를 기반으로한 모든 컨트롤 형태를 지원 • 가상 윈도우는 전체 게임 화면에서 차지하는 퍼센트(%) 비율로 위치를 구함
NEMO Online 제작기 • 로비는 하나로 통일 • 네트워크 구조는 밸런싱 서버를 거쳐 로비 서버로 접속 • 게임서버와 로비 서버로의 이동은 서버 사이드내에서 발생 • 클라이언트/서버 관게에서 접속과 해제는 오로지 로비 서버뿐. • 게임 중에도 로비에서 발생하는 모든 정보를 받음 • 친구찾기, 위치추적(따라가기) 가능 • 대게의 경우 로비와 게임은 다른 프로젝트로 구성되는 사례가 많음 • 로비와 게임의 통신방법은 소켓, FileMap 등 다양
NEMO Online 제작기 • 게임룰 • 템플릿을 이용한 Generic Algorithm으로 구현 • 게임룰은 결코 변경되지 않을 것이라 가정했음 • 외부 데이터 관리 • INI 파일 – 초기 데모용 데이터 관리방법을 그대로 사용함 • 자체 스크립트 언어
개발 중 리팩토링 • 상기의 사항들로 제작도중일차적으로 개발자를 괴롭힌 것은 빌드 시간 • STL의 빌드문제 • 매크로 - 가상 윈도우 시스템, 운영체제 구별용 전처리기 • 추상 인터페이스와 상속 • 세션클래스들의 일반 상속 • 개발도중 우선 적용했던 해결책 • CImpl 패턴사용 • Visual AssistX가 심볼을 찾지 못함 • 헤더파일의 엄격한 참조검사 • 별 도움이 안됨
개발 중 리팩토링 • Win98문제 • INI 파일의 한계가 64KB – 서비스중 데이터 로더 재작성 • 유니코드 문제 – IncrediBuild 빌드 불가능 • 대부분 전처리기(#ifdef)로 해결
실패 사례 분석 • 개발자 입장에서 결코 성공적인 아웃풋으로 만들어 내지는 못함 • 보통은 기능추가 중 프로젝트가 망가지게 되나, • NEMO Online의 사례는… • 지독히 이해하기 힘든 소스문제 • 리팩토링 도중 디자인 패턴을 위한 디자인 패턴의 사용 • 강박적인 일반화 사용과 의미없는 폴리모피즘 • 세션 윈도우와 가상 윈도우의 디버깅 난이도 • 빌드 시간
실패 사례 분석 • 세션 클래스 • 세션 클래스를 상속받은 클래스들은 결국 UI 클래스 • 결국 세션 정보와 UI가 한 인스턴스로 합쳐짐 • 데이터와 UI가 분리되지 못하는 결과를 초래 • 리스너 구조의 범람으로 전혀 상관없는 모든 인스턴스에게 정보를 전달 • 성능 및 디버깅에 악영향 • UI가 합쳐지며 전체적인 성능은 극도로 나빠짐 • 세션 클래스의 설계는 서버 개발자의 아이디어 • UI가 붙은 코드를 평소 접해보지 못했음
실패 사례 분석 • 사용자 정보 • 서버가 분리되지 않은채 전체 사용자 정보를 담고 있어 클라이언트 성능에 악영향 • 사용자 정보를 추상 인터페이스를 바탕으로 상속받아 변경시 너무 많은 수정을 요구 • 초기 커넥션시 약 3MB 분량의 사용자 정보를 소켓으로 받음 • 아무리 클라이언트 환경이 빨라졌어도 상기의 시도는 시기상조였음 • 모니터 해상도 비율 • 인터페이스 상속에 따른 구현의 이중성 • 굳히 인터페이스 상속을 통해 구현해야 했을까?
실패 사례 분석 • 리사이즈 • UI 배치 수정에 악영향 • 특히 모서리에 라운딩 처리가 된 UI 구현시, 일정 초과의 주범 • 1px 가상 윈도우들로 간격조절 • 너무 많은 UI클래스들을 생성시킴 • 로비 • 채널변경은 필수적은 기능 • 초기 기획에 채널 변동이 없었더라도 채널 변경을 고려한 설계가 있었어야 했음 • 전체 정보를 송수신하는 이유로 서버 성능 저하 초래 • 네트워크 게임의 기본을 망각한 사례 • 보이지 않는 것은 그리지 않고들리지 않는 것은 듣지 않는다
실패 사례 분석 • 일반화된 게임룰 • STL과 템플릿을 이용한 코드는 누구나 만들수 있다 • 개발자의 겉멋이 프로젝트를 망치게 하는 대표적인 사례 • 일반화 코드는 예외상향 변경시 의미가 없어진다 • 게임의 룰은 반드시 바뀐다 • 아이템은 게임룰을 바꾸는 대표적인 사례 • CPU 대전과 실제 접속자들과의 대전에서 문제 발생 • 약간의 변동이 생길때마다 두 부분의 룰을 고침
실패 사례 분석 • 엄격한 헤더파일 구분 • 네트워크 패킷 클래스에도 적용 • 네트워크 패킷 구분화로 헤더파일의 재정의하는 사례가 생김 • 서버와 다른 define을 사용하는 데이터 발생 • 외부 데이터 관리 포맷 • 외부 자료 관리 구조는 누구나 만들수 있지만 200~300개 정도의 자료를 다루게 되면 지옥이 된다 • 검증툴이 반드시 필요 • 웹브라우저처럼 “새로고침”을 할수 있는 기능이 필요
실패 사례 분석 • Static Library • 왜 라이브러리를 만드는가? • 함수 기준으로 라이브러리로 만드는 것은 아무런 의미가 없다 • 라이브러리 코드에 절대 컨텐츠 코드가 포함되어서는 안된다. • 라이브러리에 컨텐츠 코드가 포함되는 순간 그 라이브러리는 의미가 없다 • 라이브러리를 만들기는 쉬워도 재활용은 정말 어렵다 • 솔루션에서 라이브러리 프로젝트는 빨리 없애버려라
NEMO Online 리팩토링 • Re리팩토링 시작 (2007~2008) • 시대가 바뀌어도 여전히 개발자가 믿을 것은 OOP이다 • 캡슐화 • 클래스를 추상클래스로 만들고 전달값을 클래스의 파생클래스로 만들자 • 상속 • virtual 함수 • 다형성 • 각각의 virtual 함수들을 추상클래스의 이름으로 작동시키자
NEMO Online 리팩토링 • 기본 원칙 • 변수는 ADT(abstract data type)를 사용하자 • 함수호출은 폴리모피즘을 활용한 Plug-In을 이용하자 • DLL 구조로 변경 • 규정된 Plug-In으로 호출되지 않는 일반 함수는 오버로딩 금지 • ConnectToServer(☺☺☺…) • ConnectToServer(♠♠♠) • ConnectToServier_Web_DebugOn() • ConnectToServier_Local_DebugOff() • ......
NEMO Online 리팩토링 • 컨텐츠 코드와 라이브러리 코드는 각각 다른 EXE, LIB, DLL로 구현해야 한다 • UI와 자료구조는 절대로 한 클래스 속에 혼합되어서는 안된다 • "자주 사용하는 기능" 이 아닌 컨텐츠와의 분리 • 리소스를 절대 실행 파일 내에 두지 말것 • 깨어진 유리창의 시작이 된다 • 갓 졸업한 신규입사자가 와도 매주 컨텐츠를 뜯어 고칠수 있어야 한다. • 스크립트에 지나친 의존을 하지 않는다 • 기획자, 디자이너가 작업하지 않으면 의미가 없다
NEMO Online 리팩토링 • 세션윈도우 • 무분별한 상속과 다중상속 대신 포함을 고려함 • 상속에 대한 정답은 1997년에 COM이 나오며 다 나왔다. • 가상 윈도우 갯수 줄이기 • 대부분의 컨트롤들은 그림 장난으로 충분하다 • UI 요소에 지나친 개체지향은 금물 • 진행바같은 것은 StrechBlt 로 충분
NEMO Online 리팩토링 • 사용자 정보 • 리팩토링 과정에서 기획 리뉴얼로 채널을 구분하게 됨 • 사용자 정보의 수는 줄어들었으나 여전히 리팩토링의 대상임 • 게임 중에는 로비에 있는 사용자 정보를 받지 않기로 함 • 기획과 밀접한 관련이 있음 • 위치상 떨어진 사용자들의 변경 정보는 시간 간격으로 모아서 전송 • 리사이즈 • 사이즈 정보의 리스너 / 옵저버 패턴으로 구현
NEMO Online 리팩토링 • 모니터 해상도 • 4:3 과 16:9라도 UI구성을 통일시킴 • 추상 인터페이스와 상속 자체가 무의미해짐 • 추상 인터페이스 의미 자체가 사라졌지만 아직 UI 구성 추상인터페이스를 없애지 못함 • 추상 인터페이스 제거 비용이 너무 큼 • 앞으로의 연구과제 • 현재 여전히 UI를 변경시킬때는 인터페이스 작업부터 들어가야 함
NEMO Online 리팩토링 • 게임룰 • 좋은 사례란 알고리즘(구현)의 재활용이 아니라 인터페이스의 재활용이다. • CPU 대전과 네트워크 대전 • 네트워크 패킷을 재활용 • CPU 대전과 네트워크 대전을 동일한 패킷 전송으로 구현 • 함수 Call 과 send()/recv() • 천재적인 코드를 만들지 말라 • 섣부른 일반화를 적용시키지 말것 • 이 세상은 일반화 시키기에는 달라지는 것들이 너무나 많다. • 100% 일반화가 가능한 게임은 테트리스의 행렬변환 뿐이다. • 특히 UI의 일반화는 디자이너와 전쟁을 유발시킨다.
NEMO Online 리팩토링 • 네트워크 개발자를 괴롭히는 것들 • 빈번한 추가사항 • 프로모션, 로비 배너, 방생성 옵션, 아이템, 리스트 정렬.... • 게임 서버를 경유하지 않도록 수정 • 웹서버와 WinINET의 활용 • 파일 쓰기를 제외한 모든 툴(주로 DB에 툴)은 웹언어로 작성하는 것이 가장 효율적 • 아이템툴, 밸런싱 • 기획자, 디자이너의 영역으로 완전 이동 • 보안이 중요하지 않은 데이터라면 HTTP 이용 • 단 TRACE같은 것을 남겨서는 안됨
NEMO Online 리팩토링 • 더미 클라이언트 • 누가 사용하는가? • 누가 기능별로 매시간 테스트하는가? • 게임은 단위 테스트 적용이 힘든 프로젝트 • TDD툴로 활용할 수 있음 • 스크립트 지원이 되는 더미 클라이언트는 100명의 QA가 부럽지 않다. • 더미 클라이언트는 서버와 첫 Connect 할 시점부터 꾸준히 함께 만들어야 한다 • 디버그/릴리즈를 구분하지 않고 웹에서 실행이 아닌 직접 실행 시킬수 있는 방법을 만들어야 한다. • 퇴근시 실서버에 클라이언트 접속 해두기 • 소스분석이 곤란할 경우 WM_COPYDATA로 작동하는 로봇을 만듦
NEMO Online 리팩토링 • 코딩 • 주석을 사용하지 말것 • 주석이 필요하다면 이미 잘못 만든 코드이다. • 대부분의 주석은 본인에게도 의미가 없다. • 주석처리한 코드를 다시 분석하는 일은 없다. 당장 지워버려라 • 주석 대신 빌드중 메시지 출력을 활용 • 좋은 소스코드란 개발자에게 소설처럼 읽혀야 한다.
배포 • 디자인 결과에 따라 파일 구조가 달라진다 • 런처와다운로드는 리팩토링의 결과에 따라 함께 설계가 달라져야 한다. • 게임 사이트라면 다운로더는 다양한 파일구성 타입의 게임을 런칭 • 만만한 레이어? • 사실 가장 많은 리팩토링이 필요로 하는 부분은 배포과정이다 • 클라이언트와 서버가 편하기 위해 배포과정을 더럽히지 말라 • 보안 프로그램이나 백그라운드 실행기는 클라이언트가 직접 하는 것이 옳다 • 각 회사마다 전설? • 로긴, 배포, 게임실행, 삭제 과정을 전부 이해하는 개발자가 없다
Nemo Online 반성하기 • 개발자스러운 삽질 • 있는 것 다시 만들기, 보다 그럴싸 하게 만들기… • 보통 리팩토링 요구사항이 발생하는 이유는 끊임없는 기능 추가 단계에서다. • Nemo Online은 디자인 패턴을 활용한 리팩토링 자체에 의미를 두었다. • 개발중의 리팩토링은 권장하나 자체가 목적이 되어선 안된다. • 코드를 리팩토링하는 수단으로써 디자인 패턴 • 그럼 디자인 패턴을 리팩토링하는 것은? • 극단적인 패턴추구가 정답은 아니다 • C++ 코딩 재미에 빠져 목표를 잃었다
결론 • 패키지 게임이 아닌 이상 리팩토링이 끝나는 시점은 없다 • 패키지 게임이라도 라이브러리는 다시 리팩토링이 되어야 한다 • 쉽고 단순하게 만들라 • 디자인 패턴의 남용을 의심하라 • 제네릭 프로그래밍이 타당한지 의심하라 • 컨텐츠와 라이브러리는 절대 만나서는 안된다. • 라이브러리에 코드 욕심을 부려라 • 컨텐츠 코딩은 딱 Visual C++ 바이블 수준이 최선의 코드이다. • 그 한계에 대한 조율이 좋은 유지보수하기 좋은 상태로 유지시킨다
NEMO Online 리팩토링 • Q/A