no image
게임개발 프로세스에 테스트 자동화 환경을 구축한 이야기
들어가기에 앞서, 이 글은 테스트 자동화 환경을 구축하게 된 사고의 흐름과 소감을 다루나, 환경을 구축하기 위한 세세한 절차들을 모두 명시하지는 않았음을 밝힌다. 1년간 게임 인재원에서 진행한 게임 개발 프로젝트들은 모두 약 2주~4주의 개발기간을 갖는 소규모 프로젝트들이었다. 이런 프로젝트들은 개발 도중 기능에 문제가 생길 때마다 센스와 직관으로 디버깅을 하면서 진행을 해도 큰 탈이 없었다. 하지만 게임인재원 5학기 일정을 시작하면서 1년동안 진행될 장기 프로젝트에서도 이런 식으로 개발을 할 수 있을지 의문이 들었다. 장기 프로젝트일수록 필요한 기능들을 산더미 파불고기처럼 많이 구현하게 될 텐데, 기능 중 하나가 고장이 났을 때 산더미같은 기반 코드 중 정확히 어디에서 문제가 터진 것인지 진단하는 것이..
2024.01.11
no image
그래픽스 엔진 설계변경
이전 설계에서 말도 안되는 부분을 발견했다. 3d 애니메이션을 구현하기 위해 게임 엔진에서 애니메이션이 적용된 본들의 TM들을 본의 이름을 키값으로 하는 map에 넣어 그래픽스 엔진에 통째로 전달하면 그래픽스 엔진이 노드들의 상태를 반영해 메시를 그리는 방식으로 설계를 짰었다. 하지만 매 프레임 각각의 메시 인스턴스들에게 map을 만들어서 넘겨주고, 또 그래픽스 엔진에서는 이를 받아 문자열 해싱을 통해 본을 찾아서 적용한다? 이 설계 아래에서 그래픽스 엔진이 최적화를 할 수 있는 여지도 없을 것 같고, 해싱같은 비싼 연산을 매 업데이트마다, 매 메시마다, 매 본마다 수행한다는게 매우 탐탁지 않다. 그래서 애니메이션 인스턴스 하나에 대응되는 인터페이스를 만들고, 오프셋 시간을 매개변수로 전달해 메시에 적용..
2023.06.21
no image
그래픽스 엔진 인터페이스 구상
카메라, 메시, 메터리얼, 애니메이션. 대략 이정도만 있다면 기본적인 게임은 돌아갈 것이다. 아직 그래픽스 프로그래밍을 배우고 있는 상태기 때문에, 이 중 더더욱 구현대상을 추려 IMesh정도만 구현해봐야겠다.
2023.06.19
개발일지 - 견적 내기
게임인재원 4학기 생활 중 작성하는 개발일지. 지금부터 미니프로젝트까지 남은 시간이 6주, 미니 프로젝트 진행 시간이 4주, 총 10주의 시간동안게임 엔진, 그래픽스 엔진, 게임 에디터, 이 셋을 완성하고 클라이언트 프로그램까지 작성을 끝내야 한다. 계획을 어떻게 짜야 좋을까? 우선, 프로젝트를 시작하기 전 6주동안 끝내놔야 할 작업들을 정리하자. - 게임 엔진 게임 엔진은 Entity Component System 구조로 인재원 생활 6개월간 만들어 놓은 것이 있다. 게임 루프와 게임 오브젝트들 간의 계층구조, 씬과 Component가 구현되어 있고, 이 엔진을 활용하여 2학기 프로젝트를 성공적으로 끝마친 바 있으니 게임엔진의 안정성이 1개월짜리 소규모 프로젝트 레벨에서는 검증이 되었다고 할 수 있겠다..
2023.06.16

 들어가기에 앞서, 이 글은 테스트 자동화 환경을 구축하게 된 사고의 흐름과 소감을 다루나, 환경을 구축하기 위한 세세한 절차들을 모두 명시하지는 않았음을 밝힌다.

 

 1년간 게임 인재원에서 진행한 게임 개발 프로젝트들은 모두 약 2주~4주의 개발기간을 갖는 소규모 프로젝트들이었다. 이런 프로젝트들은 개발 도중 기능에 문제가 생길 때마다 센스와 직관으로 디버깅을 하면서 진행을 해도 큰 탈이 없었다. 하지만 게임인재원 5학기 일정을 시작하면서 1년동안 진행될 장기 프로젝트에서도 이런 식으로 개발을 할 수 있을지 의문이 들었다. 장기 프로젝트일수록 필요한 기능들을 산더미 파불고기처럼 많이 구현하게 될 텐데, 기능 중 하나가 고장이 났을 때 산더미같은 기반 코드 중 정확히 어디에서 문제가 터진 것인지 진단하는 것이 예삿일이 아닐 것 같았다.

소규모 프로젝트(좌)에서는 하나의 기능에 문제가 생겼을 때 해당 기능이 의존적인 기능들을 전수조사하는 것에 대한 부담이 적었으나, 대규모 프로젝트(우)의 경우 문제가 생긴 부분을 찾아내기 매우 힘들것이라는 예상이 들었다.

 이 문제에 대한 진지한 고민은 학기 초에 빌드머신을 세팅할 때부터 시작했다. 빌드머신용 컴퓨터에 젠킨스 서버를 설치하고 이를 깃허브 리포지터리와 연동해 마스터 브랜치가 업데이트될때마다 자동으로 빌드를 시도하게 만들었지만 이것만으로는 아쉽다는 생각이 들었다. 젠킨스는 프로젝트 빌드가 성공했을 때 해당 빌드를 성공적인 빌드로 마킹하는데, 프로젝트가 그저 컴파일가능하다는 것 하나만으로 프로젝트의 안정성이 보장될 수는 없다고 보았기 때문이다.

빌드머신에 젠킨스를 구축해 깃허브 저장소가 업데이트될때마다 자동빌드를 시킨 모습. 하지만 코드가 빌드가능하다는 것만으로 전체 프로젝트의 안정성을 보장할 수는 없겠다는 생각이 들었다.

 빌드머신의 절차를 어떻게 더 고도화시킬 수 없을까 고민을 하다가 대학을 다닐때 소프트웨어 테스팅에 대한 강의를 들은 기억이 났다. 개발중인 소프트웨어의 복잡성이 증가할수록 뭘 더 개발하는 것이 어려워질테니, 구현된 기능들을 반복적으로 검증할 수 있는 체계를 마련해야 지속가능한 개발을 꾀할 수 있다는 교수님의 말씀이 떠올랐다. 비주얼 스튜디오에서 테스트와 관련된 견본 솔루션을 찾아보니 과연 유닛 테스트들을 작성할 수 있는 솔루션이 있었고, 이를 우리 프로젝트에 적용하기로 했다.

비주얼 스튜디오에서 제공하는 테스트케이스 솔루션(좌)과 팀 프로젝트를 진행하면서 생성한 테스트케이스 목록(우)

 테스트 주도 개발 방식을 적용하면서 하나의 기능을 구현할 때마다 하나의 테스트 코드를 작성하기로 했다. 예를 들어 게임엔진에 물리엔진을 이식한 경우, 나는 중력과 물리 충돌이 잘 작동하는지 확인하기 위해 평면 위에 상자를 떨어뜨린 뒤 몇 초 후 상자의 y축 좌표를 확인하는 코드를 짰다.

가설을 세우고 그 가설에 맞는 유닛테스트를 실행한 모습, 시뮬레이션을 잠깐 실행한 후 Assert문이 실행되며, 내부 조건이 참이라면 프로그램은 정상코드로 종료한다.

 나는 이 테스트케이스들이 누적될수록 프로젝트의 안정성 또한 보장될 것이라 판단했고, 이걸 잘 이용하면 젠킨스의 빌드 검증절차의 부족한 점을 보완할 수 있을 것 같았다. 나는 빌드머신에서 빌드 직후 수록된 모든 테스트들을 돌려 테스트가 하나라도 실패하면 빌드를 실패로 띄우게 만들었다. 이를 통해 마스터 브랜치에 커밋이 들어올 때마다 해당 커밋이 기존에 잘 작동하던 동작들을 망가뜨리지 않는지 확인할 수 있었고, 마스터 브랜치의 안정적인 빌드 히스토리를 추적할 수 있었다.

빌드 후 batch 파일을 실행해 등록된 테스트들을 전부 실행하고, 테스트가 단 하나라도 실패하면 빌드 결과를 실패로 기록하도록 절차를 만들었다.

 테스트 케이스 코드 중 빌드 검증에 동원되지 않는 테스트코드들도 프로젝트에 넣을 수 있게 했다. 검증에 동원되지 않는 테스트라니, 팥없는 붕어빵이 아닌가 하는 생각이 들겠지만 사람의 눈으로 봐야 검증이 가능한 그래픽스 기능 테스트, 혹은 특정 기능을 시연하는 쇼케이스 코드와 같이 그저 아카이빙하고 싶은 실행코드들도 있을 수 있기 때문이다. 이런 테스트코드들은 프로젝트의 조각, 편린이라는 의미로 Snippet 코드라고 부르기로 했다. 빌드머신에서 실행되는 테스트용 batch 파일은 모든 테스트들을 실행하지만, 접두어로 Snippet이 붙은 테스트들은 무시하도록 만들었다.

  Debug Release
EditorUnitTests 디버그 버전 게임 + 툴
단위테스트 빌드
릴리즈 버전 게임 + 툴
단위테스트 빌드
UnitTests 디버그 버전 게임
단위 테스트 빌드
릴리즈 버전 게임
단위 테스트 빌드
GraohucsExe 그래픽스 디버깅 빌드 그래픽스 확인용 빌드
EditorExe 툴 디버깅 빌드 툴 배포 빌드
Exe 게임 디버깅 빌드 게임 출시 빌드

 

 우리 프로젝트의 빌드 설정은 위와 같이 총 10개로 나뉘었다. 빌드머신은 마스터 브랜치가 업데이트 될 때마다 이 중 GraphicsExe 빌드를 제외한 나머지 8개 빌드가 컴파일에 성공하고, EditorUnitTests, UnitTests에 해당하는 4가지 버전에서 실행되는 단위 테스트들을 모두 통과해야 해당 빌드를 안정적인 것으로 간주했다.

 

테스트케이스라고 해서 자동화 테스트의 대상이 되는 코드만 있는 것은 아니다. 사람의 눈으로 봐야 검증이 되는 테스트, 기능의 사용법을 알려주기 위한 데모용 코드, 기능의 정상 동작을 시연하는 쇼케이스 테스트들은 따로 아카이빙 되었다.

프로세스를 개선한 소감

 새로운 작업방식에 대한 팀원들의 반응은 모두 호평 일색이었다. 다들 '내가 커밋을 하면 갑자기 멀쩡한 프로젝트가 절단나진 않을까?' 하는 불안을 품고 있었는데 '테스트들이 모두 검증을 통과하면 안정적인 빌드로 간주한다.'는 원칙이 생기니까 팀원들이 마스터 브랜치에 커밋을 넣는 부담이 줄었다. 로컬 PC에서 자체적으로 테스트를 진행하고 커밋을 해도 되고, 그런 절차 없이 바로 커밋하더라도 빌드머신에 로그가 남아 어떤 커밋이 어떤 테스트를 실패하게 만들었는지 바로 추적할 수 있기 때문에 잘못된 커밋에 대한 부담이 덜했다.

 테스트용으로 한번 쓰고 버리던 코드들을 Snippet 코드로 따로 뺄 수 있는 것도 반응이 매우 좋았다. 과거의 테스트 환경을 그대로 남기고 언제든 실행할 수 있게 만든다는 것은 생각보다 큰 의미가 있었다. 스니펫 코드들은 기능의 사용법을 설명하는 메뉴얼 역할을 했고, 구현된 기능을 시연하는 보고자 역할을 했다. 이전에 마구잡이로 개발할 때는 임시로 개발해 놓은 테스트용 코드들을 다음 테스트를 위해 지울때마다 뭔가 아깝다는 생각이 들었는데, 이제는 그럴 때마다 스니펫 코드로 저장한다. 이런 코드들은 묵혀두면 언젠가는 반드시 유용하게 쓰일 일이 생기더라.

 내가 처음 빌드머신을 세팅하려고 한 동기는 지속적 통합 / 지속적 배포( Continus Integration / Continuous Delivery )가 개발 프로세스에 미치는 영향을 온몸으로 체감하고 싶었던 이유가 컸다. 빌드머신을 세팅하고 나니 이 중 CD는 몰라도 CI, 지속적 통합이 왜 중요한지는 뼈저리게 느끼게 되었다. 작업물의 지속적 통합에 대한 대책을 마련하지 않은 타 팀의 경우 '지금 내가 브랜치를 합쳐도 되느냐?', '내용을 커밋했을 때 문제가 생길까봐 겁난다.' 등 서로의 작업물들을 통합하는 것에 대해 상당한 부담감을 호소하는 경우가 잦았다. 부담감이 큰 만큼 서로의 작업이 통합되는 빈도가 잦지 않았고, 어쩌다 타인의 작업물을 합친다 한들 구현된 기능에 대한 품질검증이 이루어지지 않기 때문에 불안한 상태에서 작업을 계속해야 했다. 잘 동작하던 기능에 갑자기 문제가 발견될 경우 이 문제가 어느 커밋으로부터 비롯된 건지 특정하는 것도 힘들었다. 이는 팀원간 불신이 쌓이기 쉬운 환경이었고, 이런 환경 위에서 효율적인 협업을 기대하기는 힘들 것이었다. 지속적 통합의 가치는 지속적 통합이 부재한 경우 생기는 협업의 한계에서 명백히 드러났다.

 

아쉬웠던 점

 만약 게임 엔진에서 그래픽스 출력 기능을 끄고 테스트할 수 있었다면 좋았을 것이다. 젠킨스 서버를 돌리는 프로세스에는 그래픽스 출력 장치에 대한 접근 권한이 없어 이 문제를 우회하는 편법을 써야 했고, 또 필요 없는 기능에 컴퓨터 자원이 소모되는 것이 아쉬웠다.

 또 원래 내가 생각했던 프로세스는 마스터 브랜치에 푸시가 들어올 때마다 빌드를 시도하는 게 아니라 풀 리퀘스트가 올라올 때마다 빌드머신에서 풀 리퀘스트를 반영해 테스트를 진행하고 문제가 없으면 마스터 브랜치와 병합하는 것이었다. 이렇게 절차를 만들었다면 마스터 브랜치에는 항상 안정적인 버전만 남길 수 있었을 텐데 아쉽다.

원래 의도했던 프로세스, 만약 이대로 동작했다면 마스터 브랜치에는 항상 안정적인 버전만 남길 수 있었을 것이다.

 다음은 내가 테스트 자동화 환경을 구축하기 위해 사용한 기술들이다.

 

- Jenkins : 빌드머신 구축하는데 필요

- Ngrok : 빌드머신의 IP주소와 포트번호를 도메인 네임 서버에 등록해야 github webhook을 연동할 수 있기 때문에 필요

- psexec  : 빌드머신은 System 계정으로 구동되는데, System 계정은 그래픽 출력 장치에 대한 권한이 없음. User계정에게 원격으로 CMD 명령어를 실행시켜 테스트를 진행시키고 싶을 때 필요

- Batch 파일 작성 : 일괄 테스트를 실행하면 한 프로세스에서 테스트코드들이 순차적으로 실행됨, 한 프로세스당 한 테스트코드를 실행시키면서 일괄테스트를 시키고 싶을 때, 어떤 테스트코드들은 빌드 검증에 관여하지 않게 만들고 싶을 때, 아무튼 자기 입맛에 맞게 임의로 테스트를 진행하고 싶다면 batch 파일 작성법을 배워야 함.

'자체엔진 Yunuty > 개발일지' 카테고리의 다른 글

그래픽스 엔진 설계변경  (0) 2023.06.21
그래픽스 엔진 인터페이스 구상  (0) 2023.06.19
개발일지 - 견적 내기  (0) 2023.06.16

 이전 설계에서 말도 안되는 부분을 발견했다. 3d 애니메이션을 구현하기 위해 게임 엔진에서 애니메이션이 적용된 본들의 TM들을 본의 이름을 키값으로 하는 map에 넣어 그래픽스 엔진에 통째로 전달하면 그래픽스 엔진이 노드들의 상태를 반영해 메시를 그리는 방식으로 설계를 짰었다. 하지만 매 프레임 각각의 메시 인스턴스들에게 map을 만들어서 넘겨주고, 또 그래픽스 엔진에서는 이를 받아 문자열 해싱을 통해 본을 찾아서 적용한다? 이 설계 아래에서 그래픽스 엔진이 최적화를 할 수 있는 여지도 없을 것 같고, 해싱같은 비싼 연산을 매 업데이트마다, 매 메시마다, 매 본마다 수행한다는게 매우 탐탁지 않다.

 

 그래서 애니메이션 인스턴스 하나에 대응되는 인터페이스를 만들고, 오프셋 시간을 매개변수로 전달해 메시에 적용하는 식으로 구조를 바꿨다.

 모든 리소스에 범용 고유 식별자(uuid)를 넣어 리소스들을 관리할 생각이었지만, 일단 리플렉션, 시리얼라이제이션을 구현하기 전까지는 리소스에 대한 키 값은 파일경로로 대체해야 할 것 같다.

그래픽스 엔진 YunuDX11과 게임 엔진, Yunuty 사이를 잇는 Yunu3D 인터페이스의 클래스 다이어그램

  카메라, 메시, 메터리얼, 애니메이션. 대략 이정도만 있다면 기본적인 게임은 돌아갈 것이다. 아직 그래픽스 프로그래밍을 배우고 있는 상태기 때문에, 이 중 더더욱 구현대상을 추려 IMesh정도만 구현해봐야겠다.

게임인재원 4학기 생활 중 작성하는 개발일지.

지금부터 미니프로젝트까지 남은 시간이 6주, 미니 프로젝트 진행 시간이 4주,

총 10주의 시간동안게임 엔진, 그래픽스 엔진, 게임 에디터, 이 셋을 완성하고 클라이언트 프로그램까지 작성을 끝내야 한다. 계획을 어떻게 짜야 좋을까?

 우선, 프로젝트를 시작하기 전 6주동안 끝내놔야 할 작업들을 정리하자.

 

- 게임 엔진

 게임 엔진은 Entity Component System 구조로 인재원 생활 6개월간 만들어 놓은 것이 있다. 게임 루프와 게임 오브젝트들 간의 계층구조, 씬과 Component가 구현되어 있고, 이 엔진을 활용하여 2학기 프로젝트를 성공적으로 끝마친 바 있으니 게임엔진의 안정성이 1개월짜리 소규모 프로젝트 레벨에서는 검증이 되었다고 할 수 있겠다. 범용적으로 쓰일 수 있는 에디터가 없다는 것이 문제지만 말이다.

 

- 그래픽스 엔진

 2d 그래픽만 이용해서 게임을 만들 때에는 그래픽스 엔진이라 할 것이 전혀 없었다. 화면에 임의의 위치, 각도, 사이즈로 이미지를 찍어내기만 하는 함수만 있으면, 표현하지 못할 게 없었다.

 3d 그래픽스엔진이라고 뭐가 크게 다를까? 하는 생각이 든다. 그래픽스 엔진이 내부적으로 처리해야 할 일은 여러가지가 있겠지만, 그래픽스 엔진이 게임 엔진에 뚫어줘야 할 API는 결국 특정 월드공간에 메시를 그려달라, 라이트를 배치해 달라, 스크린 공간에  2d 이미지를 찍어달라, 이 정도가 전부일 것이다.

 ..사실, 애니메이션, 파티클 이펙트 같은 것들을 생각하면 그보다는 훨씬 더 많은 인터페이스가 필요하겠지만, 일단은 최대한 간소한 형태로 그래픽스 엔진과 게임 엔진을 연동시킨 후, 에디터 작업을 들어가야겠다.

 

- 엔진 에디터

 최소한 프로젝트 시작 전에, 나와 팀원들의 심리적 안정감을 위해서라도 엔진 에디터는 완성이 되어 있어야 한다. 2학기 자체 엔진 프로젝트를 진행할 때, 맵 에디터가 존재하긴 했지만  에디터는 클라이언트 코드를 분석할 수 있어야겠고, 분석된 클라이언트 코드를 파일로 저장하고, 또 불러올 수 있어야 하니 리플렉션과 시리얼라이제이션을 구현해야 할 것이다. 당연히 둘 다 내가 6주안에 직접 만들 수 있다는 생각은 들지 않는다. 오픈소스를 가져다 쓰는 수 밖에.

 

-결론

 게임 엔진 - 그래픽스 엔진 - 엔진 에디터 - 클라이언트 프로그램 간의 연계가 정말 최소한의 기능만 존재해 마치 뼈만 앙상한 목각인형처럼 돌아가더라도, 작동하는 전체 구조를 만든 다음 각 부위에 필요한 만큼 살을 붙이는 것이 현실적인 생각일 것 같다. 따라서 지금 당장 해야 할 일은...

 

1. world 좌표에 스테틱 매시를 그리는데에 필요한 최소한의 인터페이스를 정의하고, 엔진 클라이언트 프로그램을 그려낸다.

2. 에디터 프로그램에 IMGUI를 적용해 간단한 UI 레이아웃을 그려놓는다.