CI환경에서 WebGL 렌더링 테스트 자동화 성공 삽질 경험기

Jongmoon Yoon
NAVER FE Platform
Published in
10 min readDec 14, 2018

egjs-view360 은 360도 콘텐트를 보여주기 위한 라이브러리 입니다. 2016년도 12월 27일에 네이버 블로그와 포스트에 적용된 이후로 한동안 비공개로 운영되어 왔습니다. 그러나 더 많은 사람들이 360 콘텐트 뷰어에 대한 축적된 경험과 노하우를 활용할 수 있도록 2017년 10월 14일 GitHub 에 오픈소스로서 공개하였습니다.

2018년 11월 현재 egjs view360 홈페이지

외부에 공개한 만큼 코드 품질에 대한 개발자로서의 책임감과 부담감은 클 수 밖에 없었습니다. 더욱이 초기의 30~40% 수준의 낮은 코드 커버리지는 개발자로서의 자존심을 상하게 하는 요소였습니다. 이렇게 코드 커버리지 비율이 낮은 이유에는 렌더링 테스트의 부재가 원인이었습니다.

그러나 렌더링 테스트는 생산성의 비효율로 이어졌고, 결국 2017년 12월 우리는 제품의 개발의 생산성과 품질을 향상과 더불어 개발자의 자존심도 회복하기 위해 렌더링 테스트 자동화를 도입하기로 결정했습니다. 그 이후 오픈소스로 프로젝트를 외부 공개 한지 1년이 지난 2018년 12월 현재 94% 라는 코드 커버리지를 달성하게 되었습니다

이 글은 WebGL 렌더링 테스트 자동화 프로젝트를 수행하면서 겪은 문제와 해결책을 소개합니다. 비슷한 고민을 하고 계신 다른 분들께도 작은 도움이 되시기를 바랍니다.

렌더링 테스트를 도입하지 못한 이유

테스트를 도입할 당시 WebGL 렌더링 테스트와 관련한 레퍼런스를 쉽게 찾을 수 없었습니다. WebGL 라이브러리로서 가장 유명한 라이브러리인 three.js 에서조차 WebGL 렌더링 테스트가 비어있는 것을 쉽게 찾을 수 있습니다.

Three.js 의 빈 테스트 코드

당시 이런 상황에서 서비스 적용 일정 등으로 인해 익숙하지 않은 것에 대한 시행착오를 겪을 여유가 없었습니다.

WebGL렌더링 테스트 추가하기

그러나 더 이상 렌더링 테스트를 미룰 수 없게 되었습니다. 그래서 이미지 간의 비교를 하자라는 기본 아이디어로 테스트를 도입하기 시작합니다.

도입할 당시의 테스트 환경을 참고삼아 언급하면 다음과 같습니다.

단위 테스트 코드를 Karma 와 Mocha 프레임워크 기반으로 Chrome 브라우저로 테스트하는 자동화 환경이 구축되었지만 이미지 비교를 통한 렌더링 테스트까지 가능한 환경은 아니었습니다.

그림1. egjs view360 의 테스트 환경 구성

먼저 이미지 비교하는 라이브러리를 검토하였고 Resemble.js라는 오픈소스를 적용하기로 하였습니다.

Resemble.js 의 로고

이 Resemble.js는 두 개의 이미지를 인자로 받고 두 이미지간의 픽셀 차이값을 반환하는 compare 함수를 제공합니다.

compare 함수에 image1과 image2를 인자로 지정하면 콜백 함수를 통해 이미지의 차이를 백분율로 전달 받을 수 있습니다. 이미지는 파일의 경로, 이미지 객체, 이미지 파일 객체, Node Buffer, Blob 형태를 모두 지원합니다.

  • image1: 테스트 할 화면 전환 후 Canvas API 를 통해 추출된 ‘이미지’
  • image2: 기대되는 렌더링 ‘이미지’
const compare = require("resemblejs").compare;compare(image1, image2, (err, data) => {
console.log(data.misMatchPercentage);
});

Resemble.js 를 이용하여 로컬 컴퓨터의 Chrome 브라우저에서 테스트를 성공적으로 실행시킬 수 있었습니다.

다음은 내부적으로 Resemble을 사용하여 작성한 Unit 테스트 코드의 한 예 입니다. 45, 45, 65 라는 숫자로 뷰어에서 가리킬 방향과 화각을 지정하여 뷰어에서 렌더링한 결과를 미리 준비해 놓은 이미지와 동일한지 확인하는 코드입니다.

렌더링 테스트 코드 (2개 예제)
렌더링 테스트의 기대 결과를 담은 2개의 이미지

그리고 여기까지는 Resemble.js 라는 오픈소스의 도움을 얻어 큰 어려움 없이 진행할 수 있었습니다.

그러나 Travis CI(Continuous Integration)를 연동하고 싶었습니다. CI 의 연동을 통해 좀 더 철저한 코드 검증이 가능해지는 측면도 있었고 대외적으로 CI 의 높은 코드 커버리지를 공개 함으로써 오픈소스 사용자에게 코드의 신뢰성을 줄 수 있다는 측면도 있었습니다.

Travis CI 를 연동하는 작업은 그렇게 쉽게 이루어지지 않았습니다. 그래서 본론은 이제부터 입니다. 사실 여기부터가 삽질의 시작 이었기 때문입니다.

하드웨어 가속을 지원하지 않는 CI 환경에서의 대응

아쉽게도 테스트 케이스를 수행할 Travis CI 환경은 하드웨어 가속을 지원하지 않는 가상의 컴퓨터 환경입니다. 문제는 WebGL 은 GPU 에서 동작하기 때문에 WebGL 을 아예 지원할 수 없었습니다.

이 문제를 해결하기 위해 정말 많은 시도를 했습니다. Chrome 옵션, OS, Browser 등을 변경해 보기도 했고 의존성 모듈을 여러가지 방식으로 설치해보기도 했습니다.

https://github.com/naver/egjs-view360/pull/95 은 우리가 얼마나 많은 시도와 실패들을 했는지 여실히 보여줍니다.

결국 Stack Overflow 의 글을 통해 가상 컴퓨터 환경에서는 그래픽 카드의 하드웨어 가속의 도움을 받을 수 없고 하드웨어 가속을 가상으로 지원하는 라이브러리를(osmesa)를 설치해야 한다는 것을 알게 되었습니다.

그러나 이 설명도 CI 환경이 아닌 Linux 콘솔 환경에서 실행하는 것을 대상으로 설명 되었기 때문에 CI 환경으로 반영이 필요합니다.

결과적으로 다음과 같이 TRAVIS 설정 파일(.travis.xml) 을 수정해야 합니다.

// .travis.xml
before_install:
- sudo apt-get update
- sudo apt-get install libosmesa6
- apt-cache policy libosmesa6
- sudo ln -s /usr/lib/x86_64-linux-gnu/libOSMesa.so.6 /opt/google/chrome/libosmesa.so

그 단계는 다음과 같습니다.

  1. TRAVIS CI 의 before install 단계에 osmesa 라이브러리를 설치
  2. Chrome 브라우저에서 하드웨어 가속 라이브러리를 참조할 수 있도록 링크를 연결

Chrome Headless 환경에서 렌더링 테스트를 위한 초기화 옵션 설정

분명 WebGL 이 사용 가능한 상황이 되었음에도 불구하고 로컬 환경과 다르게 CI 환경에서만 이미지 테스트가 계속 실패합니다.

원인은 WebGL 콘텍스트를 얻을 때 지정하는 옵션과 상관이 있었습니다. CI 환경에서는 WebGL 콘텍스트를 얻을 때 다음과 같이 두 가지 옵션 값을 설정해야합니다.

var gl = canvas.getContext( "webgl", {
preserveDrawingBuffer: true,
premultipliedAlpha: false
);

앞에서 언급한 옵션에 대한 좀 더 자세한 내용은 다음과 같습니다.

preserveDrawingBuffer

먼저 드로잉 버퍼에 있는 콘텐츠를 합성(composite)한 후 해당 버퍼를 유지할지 여부를 지정하는 플래그입니다. 테스트 시에는 preserveDrawingBuffer 를다음과 같이 true 로 설정해 두어야 합니다.

preserveDrawingBuffer: true

이 속성의 기본값은 false 입니다. 그러나 CI 환경의 경우 Canvas 에 그려진 이미지를 toBlob 과 같은 JavaScript API 를 통해 불러오려고 해도 이미 버퍼를 삭제했기 때문에 그 정보를 얻을 수 없다는 점을 알게 되었습니다. 따라서 true 로 설정하여 테스트 시 사용할 이미지 버퍼를 삭제하지 않습니다.

참고: https://bugs.chromium.org/p/chromium/issues/detail?id=603538

premultipliedAlpha

이미지 합성(composite) 시 버퍼에 있는 색상 값에 알파값(투명도)이 반영되었는지 WebGL 에게 알려주는 플래그입니다. 테스트 시에는 다음과 같이 false 로 설정해야 합니다.

premultipliedAlpha: false

이 옵션은 Chrome 68 버전으로 업데이트 되기 전까지는 지정할 필요가 없던 옵션입니다. Chrome 68로 업데이트한 이후 CI 환경에서 렌더링 테스트가 모두 실패합니다.

아직도 명확한 원인을 파악하지는 못했습니다. 다만 검색 중 GPU가 없는 Chrome Headless 환경에서 이미지 렌더링이 검게 된다는 이슈를 발견했는데 혹시 관련이 있지 않을까 의심했고 그 추정이 잘 맞아떨어졌습니다.

참고: https://stackoverflow.com/questions/48011613/rendering-webgl-image-in-headless-chrome-without-a-gpu/48127936#48127936

맺는 말

WebGL 렌더링 테스트를 도입함으로써 30~40% 불과했던 코드 커버리지를 94% 까지 끌어올릴 수 있었습니다.

coveralls 에서 표기한 view360 의 코드 커버리지
Github 메인 화면에 노출된 코드 커버리지

이렇게 높아진 코드 커버리지를 통해 새로 추가된 기능이 기존 동작을 해칠지 아닐지 노심초사 하지 않고 명확하게 판단할 수 있으므로 자신감 있게 코드 수정을 할 수 있습니다. 이는 코드를 유지 보수 하는 개발자 뿐만 아니라 오픈소스 참여자/기여자(Contributor) 에게도 바람직한 상황입니다. 또한 현재 네이버 동영상 서비스, 부동산 서비스, 스마트 에디터 등 실 서비스에서 서비스 되고 있는 만큼 더 안정적인 서비스를 위해 필수적인 과정이었습니다.

다만 Travis CI 환경 에서 테스트하기 위해 위한 다음 과정들은 그리 순탄하지 않았습니다. 다른 사람들의 경험과 조언들이 우리가 처한 상황에 완전히 들어맞는 경우가 없었기 때문입니다. 그래서 여러가지들을 조합해보고 시도하는 과정을 거쳐야만 했습니다.

이 내용은 WebGL 을 테스트하면서 겪을 수 있는 경험을 하나 더 제시함으로서 WebGL 을 테스트하면서 어려움을 겪으실 분들에게 조금이나마 힘이 되길 기대하는 마음으로 작성하게 되었습니다. 부디 도움이 되었기를 희망합니다.

--

--