게임데이터에 대한 update, insert는 게임에서만 한다. 웹이나, 앱에서 update, insert를 게임내 데이터로 직접 넣지 않아야 한다(select만 해야한다). 이게 잘못 되었을 경우의 파장은 어마어마 하다.... 게임데이터가 꼬여서 플레이 할수 없게됐는데, 롤백은 할 수 없는 무시무시한 상황이 될수도 있음..
- 반대로 말하면 웹쪽의 이벤트나, 앱쪽 기능을 위한 데이터는 게임에서 insert, update 하지 않는다.
한 가지 기능은 반드시 "하나의 크리티컬한 패스"를 반드시 지나가야 한다. 예를 들어 아이템을 획득하는 루트가 여러가지 일지라도, 실제로 아이템이 "획득" 되었다는 것을 무조건 보장해줄 수 있는 하나의 패스가 필요.
- 이걸 생각하게 된 이유는 히스토리 로그를 남길 때, 같은 로그를 두 군데 이상에서 남겨야 한다면, 변경에 대응하기 쉽지 않아지기 때문이었는데, 구지 히스토리 로그만이 아니라도, 변경에 대응하는데 있어서는 좋은 룰인것 같다. 예전에 모 게임의 아이템 생성 과정이 2~3루트로 만들어져 있어서, 한군데만 고치거나 하는 일때문에 사이드 이펙트가 많아서 고생했던 기억도 있다.
기능과 데이터는 분리되어 있을 수록 좋다. - 이거 하나만 설명하는데도 많은 글이나 시간이 필요하므로 자세한 내용은 생략한다(언젠간 길게 적을 기회가 있겠지)
모든 기획 / 내용은 변경될 수 있다고 생각해야한다. 이건 기획만이 아니라, 처음에 자신이 만들 때 생각했던 "가정"도 마찬가지. 함부로 가정하고 만들면 나중에 꼭 피를 본다.
서버에서 에러는 크게 2가지로 분류할수 있다. "유저에게 보여지는 에러"와 "시스템 내부 에러", 한 가지 기능에 대해서 이 2가지만 잘 구분해 놓아도 설계에 많은 도움이 된다. - 에러와 관련된 내용도 정리해서 하고 싶은 말이 많다. 다음 기회에
- 보통 기획자는 "아이템을 강화한다. 실패하면 실패 메시지를 띄운다" 라는 초간단 기획에서 부터, "아이템을 강화한다. 1) 강화 할 수 없는 아이템일 경우 메시지 A(강화할 수 없는 아이템에 대한 정의는 따로) 2) 더이상 강화할 수 없는 장비면 메시지 B 3) 재료가 부족하면 메시지 C 를 띄운다" 같이 에러 상황을 잘 명시한 기획까지 여러개를 만날수 있는데, 출력의 레벨 결정이나 메시지 결정을 개발자가 임의로 해야 하는 상황이 많다. 또는 에러 상황의 우선순위 결정에도 개발자가 임의로 하는 경우가 일상다반사인데(앞의 예를 든다면, 더이상 강화할 수 없는 장비이면서 재료가 부족하다면 더이상 강화할 수 없다는 에러의 우선순위가 높다), 저런 부분 하나하나가 설계나 시퀀스에 영향을 줄 수 있기 때문에, 개발에 앞서 에러 상황과, 우선순위 결정만 잘 해놔도 상당한 도움이 된다.
const 를 잘 지켜야 한다. 리턴값이나 파라메터의 const보다. 함수의 const 함을 지켜야 하는 것만 잘 관리해도 상당한 양의 실수를 미리 알수 있다. 그리고 이런 const를 지켜주는 것이 코드의 깨끗함을 상당수준까지 올려준다(함수 이름과 기능이 다른 경우라던가). - 내가 가장 잘 안되는 부분이기도 함..ㅠㅠ
특정 행동을 한번의 프로세스로 끝내지 못할경우 - 다른 스레드로 넘겼다가 받아와야 하는 작업 또는 다른 프로세스, 다른 서버로 넘겼다가 결과를 받아야 하는 경우 - 그 작업이 진행중인데 같은 요청이 들어온 것에 대한 방어가 되어 있어야 한다. 매우 주의가 필요함(다르게 생각하면 멀티쓰레드 작업시 어느 부분을 멀티 쓰레드 세이프 하게(아토믹하게) 만들거냐 하는 문제와도 비슷함)
- 예전에 일하던 팀의 게임에서 클랜 가입 요청같은 것이 여기에 해당. 유저 A가 클랜 N 에 가입 요청을 보내고, 그 처리가 완료되기 전에 클랜 T에 또 가입요청을 보낼경우. 방어가 되어 있지 않다면 문제가 된다. 여기서 주의 해야 할 점은 "중복 요청의 범위"를 어디까지로 볼것인가 하는 것인데, 이 경우는 '클랜 가입 요청을 보내는 행위'에 대해 플레이어에 대해서 중복을 체크해야 한다. 즉, 한 플레이어는 하나의 클랜 가입 요청이 끝나지 않은 상태에서 다른 가입 요청을 보낼수 없어야 한다. 각 경우에 따라서 어느 곳에 "중복 체크" 안전 장치를 할 것인지에 대해서 고민이 필요하다.
구조체나 스트링이나 배열같은 크기가 좀 있는 데이터의 경우에 한해서, 한번의 함수 호출로 데이터를 모두 사용하고 필요없는 데이터가 된다면, 포인터나 참조로만 넘겨서 쓸데 없는 데이터 복사가 일어나지 않게 하는게 좋다. ... 당연한 말이니까 자세한 설명은 생략(... 하지만 이것도 내가 가장 실수 많이 하는 것들중 하나..)
어떤 기능을 구현함에 있어서, 지금 구현한 이후에 변경사항이 많을것 같고, 그 작업을 다른 사람이 할 때 혼란을 야기할 것 같다면(.... 이걸 미리 아는것도 어렵긴 하겠네.. ㅠㅠ), 다른 사람이 작업할 때에는 세부 구현에 신경쓰지 않아도 되도록(실수할만한 부분이 적도록) 구조를 고민 해 보아야 한다.
- 예를 들어, 캐릭터 정보가 변경되면 DB 업데이트를 해야 하는데, 캐릭터 정보에 새로운 것들이 추가될 때마다, 작업해야 하는 부분이 많으면 아무래도 실수할 부분이 많아진다. 그렇다면 -> 캐릭터 정보가 변경되는걸 알려주는 observer를 만들고, 변경 발생을 감지 ->데이터 전체를 업데이트 하는 하나의 함수로 업데이트 한다면, 새로운 데이터를 추가한 것만으로는 추가 작업이 발생하지 않는다(.. 설명에 좀 문제가 있긴한데 대충 이런 느낌으로만 봐주셈둥).
- 한가지 더 예를 들자면. 또 모 게임의 아이템 시스템 중, 장비 강화나, 인챈트 같은 시스템 같이 장비자체에 어떤 행위를 통해 데이터를 변경하는 시스템이 있는데, 이 장비를 장착한 채로 강화 또는 인챈트 할경우 그 데이터가 바로 바로 그 캐릭터의 스탯에 반영되어야 한다. 이런 경우 "장착한 아이템에 변경이 생겼다" 라는 이벤트를 받아서 -> 기존에 아이템 효과를 모두 제거 -> 다시 변경된 데이터의 아이템 효과를 다시 적용 하는 형태의 흐름만 잡아 두면 추가 데이터가 발생해도 이쪽을 다시 작업할 일이 없어진다(성능 이슈는 발생할수 있음).
반드시 순서를 가져야 하는 경우 "이정도 시간을 주면 되겠지" 하는 방식은 절대 금물, 반드시 순서를 보장해 줄수 있는 루트를 찾아야 한다.
- 예를들어, 캐릭터 로그 아웃시, 두 DB에 데이터를 써야 한다면, 한쪽 DB의 작업이 많다고 해서 해당 DB 쓰기 작업이 반드시 늦게 끝난다는 보장이 없기 때문에, 반드시 양쪽 모두의 "완료"를 확인하고 로그아웃 처리를 해야 한다(양쪽의 완료를 보장해주기 위한 장치가 반드시 필요하다).
어떤 컨텐츠에서 timestamp 값을 사용한다면 그 값이 1) 서버 application의 시간인지, 2) DB의 timestamp 인지 잘 구별해서 사용해야 한다. 한번에 여러개의 테이블에 시간을 기록하거나, 그 시간이 모두 같아야 한다거나, 쿼리를 여러개 날리는데, 그 값이 같아야 할 필요가 있다면, 서버 application이 시간까지 계산하여 사용하는 것이 좋고, 그런거 상관없이 쿼리 당시의 DB 시간만 있으면 된다면 DB의 시간을 그냥 사용하면 된다. 크게 중요하진 않지만 지켜 주면 좋은 내용이라고 생각
무엇을 만들더라도 항상 "실수"가 있다고 생각해야 한다. 가장 먼저 생각할 수 있는 것은 스크립트나 어떤 데이터의 오류다. 이걸 판별하는 방법은 미리 검증을 넣어서 확인하거나(자동화된 테스트 등으로?), 서버 기동시 검증 하거나 하는 등의 방법이 있다. 중요 기능이 진행될 때 이런 실수에 의해서 잘못 동작할 우려가 있다면 1) 사전에 그 실수를 커버하는 상위의 규칙을 만들거나 2) 그 실수가 발생했을때 미리 알아 챌수 있는 시스템이 갖추어져 있어야 한다(둘중 하나는 반드시 있어야 한다). 이런 부분은 보통 기획자와 상의를 통해서 해결해야 하는 경우가 많다. 예를 들어 던전 입장시 피로도를 소모하는 게임에서, 특정 던전이 피로도를 소모하지 않는 대신 경험치를 주지 않는 다면(다른 보상), 1) 피로도를 소모하지 않는 던전은 반드시 경험치를 주지 않는다는 규칙을 시스템화 하거나 2) 피로도를 소모하지 않는 던전인데, 경험치를 주도록 되어 있는 경우가 없는지 미리 검증을 해서 수정할 수 있도록 해야 한다.
.