9. 이 강연의 목표는?
오류보고를 덜 받기 위한 방법
-> 오류를 덜 발생시키기 위한 방법
오류보고 내용을 리스팅 하기 위한 방법
-> 오류 보고는 상시 일감이다. 이를 위한
프로세스에 대한 이야기
10. 본 주제에 앞서 제가 직접 겪
거나 들었던 기억에 남는 버그
와 그 해결 과정에 대한 이야
기를 해보겠습니다.
11. 자정마다 서버가 죽던 버그
덤프가 남지 않고 크래시가 발생
유저 개개인이 꾸미는 방이 존재했는데,
이 방이 날짜제였음. 대여 날짜가 바뀌면
최대 가구 개수가 달라져, 초과된 가구를
방에서 임의로 없애는 코드가 존재.
이 코드가 잘못 작성되어, 더 작은 방으로
이동시 memcpy할 크기를 결정하는 변수
가 망가졌고, 그로 인해 해당 클래스 멤버
가 덮어씌워져 2차 감염으로 서버가 죽음.
12. 대규모 패치 후 서버가 죽어요
해당 패치에 너무나 많은 내용이 포함되
어, delta중 어디가 원인인지 찾지 못함.
닉네임 변경 아이템 사용이 인게임에서
가능하도록 판매됨.
미니룸, 메신져 등에서 즉시 반영 코드를
적용했으나, 닉네임 변경 후 유저 풀에서
변경을 빼놓아버림.
즉, 메모리값에만 반영하고 해당 자료구
조의 포인터는 주인잃은 포인터가 되어
서버 크래시 발생하는 문제였음.
13. 서버가 시도때도 없이 죽네요
특정 유저가 접속하면 발생.
당시 ExceptionFilter가 있었지만 덤프의
위치가 유효하지 않았음.
힌트는 없고, 서버는 계속 죽고…
알고보니 배열로 잡고 있던 아이템 개수
가 초과되서 클래스 멤버가 덮어씌여졌
고 그로 인한 2차 감염으로 서버가 죽었
던 것.
14. 다른 유저의 귓속말이 들려요
잘못 걸려온 전화처럼 다른 유저에게 보
낸 귓속말이 들리던 버그가 보고됨.
원래 전달되어야 했던 유저에게 전달 되
는 경우도 존재. (100% 발생이 아니란 것)
서버 오픈 후 2~3시간 이후부터 만 발생.
채팅 방 자료구조에서 강제 종료가 아닌
서버 이동시 네트웍 대응 세션 포인터가
잘못 물려있는 상태가 되던 버그였음.
15. 빈 비밀번호 방이 남아있어요
기본적으로 모든 방은 시스템이 만든 방을 제외하고
는 유저가 한 명도 없으면 삭제 되게끔 되어 있었음.
서버 로그를 확인해본 결과 해당 방은 메모리상 존재
하지 않음.
모든 클라이언트에서 발생하는 현상이 아니었음.
확인해보니 방 정보를 패킷 버퍼에 있는 값의 포인터
를 이용하게끔 코딩 되어 있었음.
문제가 개발 테스트 및 QA에서 발생하지 않았던 것은
방 정보 패킷만 유난히 커 패킷 버퍼 뒤쪽이 초기화
되는 일이 별로 없었기 떄문이었고, 빈 비밀번호 방이
아니고선 값이 조금 이상하다 해도 티가 잘 안났었기
때문.
16. 어라? 백섭?
로그인 프로세스가 안전하게 보호받지 않으
며, delta 기반 저장이 아닌 경우에는 초기화
까지 되는 경우가 발생하기도 한다.
모 게임의 경우 디비 캐싱 서버 로직 버그로
인해 파일 전체가 깨지는 바람에 눈물겨운
백섭이 발생하기도 했다.
메모리 캐싱의 경우에는 점검시 캐싱한 데
이터를 저장하지 않는 버그가 발생해, 끝까
지 버티던 유저들이 손해 또는 이득을 보게
되는 경우도 있다.
17. 그리고…
여러 회사들의 10시간 이상의 장애들은
해당 게임을 플레이 한 유저들이라면 두
고 두고 화자 될 만한 사건들이라 할 수
있다.
온라인 게임 태동기에 비해 서비스 퀄리
티가 비약적으로 올라간 만큼 유저들도
장애를 곱게 봐주지 않으니… 개발자인
우리가 조금씩 더 분발하자.
18. 이렇듯 자그마한 실수 하나에도
유저 입장에선 점검을 비롯한
불편을 겪게 되어, 게임에 대한 불신이
생기는 계기가 된다.
19. 베테랑 개발자에 여유 있는 일정을
할당한다고 해서 버그가 안 생기거나
저절로 줄어들지는 않는다.
21. 다시 한번 상기 시켜드리자면,
이 강연은 디버깅을 잘하기 위한
기반 마련에 대한 강연입니다.
22. 애초에 버그를 안만들 수 있다면 얼마나 좋겠
냐만은…
버그 제로란 불가능한 법.
이미 사고가 벌어졌다면 덤프 분석을 통한 사
후 대응도 알아둘 필요가 있다.
문제 발생 후 대응 법에 속하는 덤프가 남는 상
황과 불분명한 상황에 대해 간단히 알아보자.
23. 덤프가 남고 정상 동작하는 상황
(Stack Unwinding 후 정상 동
작)
스택 오버 플로우가 났을 때 (새로운 Thread
를 만들어서, 기존 익셉션 정보를 처리해야
함. ExceptionProcess를 위해 스택을 구성
하는 과정에서 오류가 생기기 때문)
잘못된 함수 포인터 콜
스택 메모리 덮어썼을때 (스택 깨먹었다고
도 하죠)
Divide Zero
NULL 포인터 접근
같은 메모리에 delete 두번
24. 크래시가 나는 상황 (절대적이진
않음)
스택 되감기 도중 예외 발생.
잘못된 포인터 캐스팅을 통한 가상 함수
콜
잘못된 함수 포인터 콜
변수 값 덮어 썼을때 (주로 가상함수 포인
터 테이블을 덮어썼을때 크래시가 남.)
잘못된 포인터 접근
26. 1. 오류 보고 시스템
M2 프로젝트에서 개발과정 도입되었다고 알려
져 있는 이 시스템은, 나는 5명이상의 프로그래
머가 함께 일하는 모든 개발팀에서 도입되어야
한다고 생각한다.
오류 보고 시스템을 적극 사용하는 과정 중에
코드 검수가 이루어지며, 런타임 오류가 발생한
위치와 작성자, 원인 등을 명확히 파악하기에
유용하기 때문이다.
자세한 내용은 NDC2010에서 발표되었던 M2
프로젝트 오류 보고 시스템 PPT를 참고.
27. 2-1. 개발 규약
암묵적이 아닌 개발 규약은 어느 정도 존재
할 필요가 있다.
뭐 띄어쓰기는 어떻게 하고, 네이밍은 어떻
게 하고 이런 것 보다 예외 처리는 어떻게 해
야 하는지, 로그 기록에 대한 기준, 외부 라
이브러리 도입 기준, 코드 관리 규약 (만들어
져 있는 기능들에 대한 명세 및 관리) 등에
대한 이야기를 정리해놓고 손쉽게 관리하도
록 노력하는 것이 좋다.
28. 2-2. 코드 관리
버전 관리 시스템을 쓰는 회사라면, 버전 관리 시스템
을 통한 개발 기록을 보유해야 한다.
사용하지 않는 코드를 주석 내지는, Entry Point를 막
음으로써 해결하려고 한다면, 디버깅 및 개발 시에 많
은 시간을 이 코드들로 인해 사용하게 된다.
3-tier (DB, Client, Server) 구조가 일반적이라고 봤을
때 이 티어 들간의 네이밍은 일치 시키는 것이 좋다.
한 티어가 주체가 되어 네이밍을 정하면, 그를 기반으
로 한 코드 생성기로 생성된 코드를 이용한다면, 디버
깅 및 소통에서 많은 이득을 볼 수 있다.
29. 3. 코드 리뷰
코드 리뷰를 어렵게 생각하지 마라.
개발 전에 작업 방향에 대해 이야기를 나눈 이후, 개발 테
스트 이전이나 개발 테스트 중 코드 검수를 요청하면 된다.
작업 방향에 대한 이야기가 선행되어 있기 때문에 전면적
리팩토링을 하게 될 일은 없을 터이고, 개발 테스트 도중
발견됐을 버그나, 부분적인 코드 완성도를 끌어올리고 개
발 규약에 맞는지, 유저에게 오류 발생시 어떻게 보여지고
어떻게 처리될지에 대한 고려를 중점적으로 보면 된다.
다만 지나치게 잦은 코드 리뷰와, 코드의 큰 방향성을 뒤흔
드는 코드리뷰는 옳지 않다.
이는 개발 미팅 후 개발자들 간의 조율 과정에서 끝나야 한
다.
30. 3-2. 페어 프로그래밍
생각보다 쉽지 않다.
개발자는 부족하고, 개발 기간은 촉박한
문제.
페어 프로그래밍 자체에 거부감을 느끼
는 사람도 많음.
31. 3-3. 코드 리뷰 + 테스트 코드 작
성
테스트 주도 개발을 함으로써 개발 과정에
서 만들어진 테스트 코드 위주로 설명 및 리
뷰를 진행.
기본적으로 메소드 내에서 실패가 일어날
여지가 없는지 위주로 검토를 진행하면 좋
다.
전치검사-후치보장의 원칙을 지키지 않아도
되는 경우는 메소드 내에서 절대로 실패가
없을 경우에 한정되기 때문.
32. 4. 자동화 된 테스트
유닛 테스트
작은 단위로 분리 가능한 코드 조각을 테스트.
모듈 테스트
외부 프로그램을 이용하거나, 프로그램 내부 코드를 응용해 결합
된 기능을 테스트.
자동화 된 테스트 들을 업무 프로세스에 포함시킴으로써 작성된
코드가 이미 작성된 기능을 망가뜨리진 않았는지 확인 할 수 있
다.
테스트가 자동으로 이루어지는 것이지, 테스트 코드를 작성하는
과정은 당연히 개발자의 몫.
새로 작성된 코드에 대한 테스트를 작성함으로써 해당 기능에 대
한 이해도를 높이는 등의 순 작용을 가지고 있다.
33. 5. 자동화된 측정 시스템
자동화된 테스트에 함께 붙이면 되는 경우가 많
으며, 성능 측정과 잔존물 측정이 함께 이루어
져야 한다.
성능 측정은 perfmon과 같은 외부 프로그램을
통한 측정과, 코드 수행속도 측정, 부가 데이터
측정 (로그나 DB 데이터 등...)등으로 분류된다.
측정 대상은 상황마다 다르지만, 보통 퇴근시간
에 측정 시스템과 테스트 시스템을 켜놓은 후
다음날 결과를 바탕으로 회의와 업무를 진행하
는 식으로 이루어지는 것이 좋다.
34. 6-1. 잘 남겨진 로그
버그가 발생하면 발생한 프로그램에 항상
Runtime attach를 할 수도 없는 노릇이고, 그렇
다고 해서 100% 재현이 가능한 것이 아니다.
실제로는 수집된 로그를 바탕으로 버그를 잡아
야 하는데, 로그가 잘 남고 있는 코드들은 일반
적으로 버그가 덜 발생한다.
로그가 대체로 잘 남겨지지 않는 이유에는 로그
작성이 번거롭기 때문이기도 한데, 서식지정자
를 이용했을 시 장점도 분명하지만, 그로 인해
로그 남기는 과정이 번거롭게 느껴지는 것도 사
실이다.
35. 6-2. 로그를 조금이라도 쉽게 남
기는 법
stream 을 이용하면 조금 더 쉽게 이용할
수 있으며, stream 이용 시에도 서식지정
자처럼 규격을 지정할 수 있다.
다만 이를 번거롭지 않도록 코딩의 묘를
살린다면 로그 남기는 작업이 번거롭고
피곤한 작업이 되지 않을 수 있다.
36. 6-3. 로그 기록 예제
// C++ 유사 코드로 작성됨
template <typename T1, typename T2, …>
std::string ToLog(const T1& t1, const T2&
t2) const
{
return t1.ToStr() + t2.ToStr() + …;
}
클래스별로 로그를 남기는 ToStr() 메소드
구현을 강제하며, 정보가 필요한 클래스들
을 template 인자로 넘김으로써 손쉽게 로그
기록을 돕는다.