Site Search

Vue.js를 이용한 웹게임 만들기 – cocone의 프론트엔드 업무

※이 기사는 밑의 일본어 기사를 번역한 기사입니다.

2020년 9월 10일 기사

https://blog.ensapia.engineering/2020/09/10/web-game-dev/

 

안녕하세요, 웹 개발실에서 실장 겸 프론트엔드 업무를 담당하고 있는 다카야마입니다.

어느새 9월이 되었네요. 참 빠르네요. 학생 여러분들은 여름 방학도 끝났고, 개학도 시작하셨나요? 여름 방학이라고 하면 숙제잖아요. 여름 방학이 시작되면 바로 끝내려고 했는데, 한 번도 끝내본 적이 없어요. 아무래도 저는 여름 방학 숙제는 마지막까지 쌓아두는 타입인 것 같습니다.

이번 글도 작년 즈음부터 ‘씁니다. 쓰겠습니다. 라고 말하면서 시간은 흘러 어느덧 이런 시기가 되어서야 겨우 겨우 무거운 허리를 일으켜 글을 쓰고 있는 실정입니다.

그런 이유로 이런저런 일이 있었지만, 이번에는 웹 개발실에서 하는 업무와 Vue.js를 이용한 웹 게임 제작에 대해 소개하고자 합니다.

cocone 주식회사의 제품

먼저, cocone주식회사는 1600만 명 이상(2020년 9월 기준)의 고객에게 사랑받고 있는 ‘포켓코로’를 대표로 다양한 CCP(Character Coordinating Play) 서비스를 개발, 운영하고 있습니다.

이들 제품의 대부분은 ‘아이폰, 안드로이드 앱’ 등으로 제공되고 있기 때문에 개발에 사용되는 기술(언어)은 대부분 C++, C# 등이 사용되고 있습니다.

그렇다면 ‘웹 개발실의 프론트엔드는 무엇을 하고 있을까? ‘라는 궁금증이 생기지 않으셨나요?

포켓코로

위에서 소개해드린 ‘포켓코로’는 주로 옷 입히기, 꾸미기 등을 통해 즐기는 서비스이지만, 당사는 이를 CCP(Character Coordinating Play)라는 장르를 구축하여 ‘포켓코로’ 외에도 다양한 제품들이 존재합니다.

이러한 제품에는 코디나 꾸미기를 위한 디지털 아이템이 존재하는데, 보통은 구매를 통해 얻을 수 있지만, 정기적으로 진행되는 ‘이벤트’에 참여함으로써 얻을 수도 있습니다.

포켓코로 내 이벤트

포켓코로 내에서도 다양한 이벤트가 진행되고 있습니다.

조금 오래된 이야기지만, (그래서 앞으로 서두 구절은 아닙니다만 , 3년이 넘은 이야기라 지금 생각하면 좀 식상할 수도 있겠지만, 웹 게임 개발에 도움이 되었으면 합니다). 제가 입사한 지 3년이 넘었지만, 당시에는 포켓코로 내의 이벤트는 클라이언트의 네이티브 언어(C++) 등으로 만들어져 개발, 업데이트 등 비용이 많이 드는 작업이었습니다.

좀 더 비용이 들지 않는 형태로, 정기적으로 새로운 구조나 이벤트 자체를 제공할 수 있도록 ‘웹으로 할 수 없을까?’라는 것이 입사 당시 저의 미션이었습니다.

입사 후 첫 1개월은 ‘웹 게임 개발 기반’으로서 cocone assets라는 npm private package를 여러 개(아마 10개 정도) 만들고(현재는 20개 이상 존재합니다.), 이번 뿐만 아니라 다음 웹 게임 제작을 쉽게 할 수 있도록 준비했습니다.

총 3개월 정도에 걸쳐 0부터 웹 게임 제작을 위한 개발 기반과 신규 웹 게임 이벤트를 만들었습니다.

그것이 바로 아래의 ‘농장 게임’입니다.

웹게임 동영상

위의 ‘농장 게임’의 내용 등 자세한 설명은 생략하고 간단하게 설명드리면 다음과 같습니다,

씨앗을 심는다 → 꽃이 핀다 → 수확 및 가공 → 레벨이 올라가면 새로운 꽃이 핀다.

어느 정도 성장하면 NPC 캐릭터에게 납품한다. → 보상을 받거나 미션을 달성한다.

등의 순환을 통해 최종적으로 포켓코로 내의 아이템을 획득할 수 있는 게임으로 구성되어 있습니다.

자, 그럼 위의 농장 웹 게임 영상을 보셨나요? 이거 클라이언트 네이티브 언어(C++) 등으로 만들어진 것처럼 보이지 않나요? (안 보이시는 분은 안목이 있으시네요) 놀랍게도 모두 HTML로 제작되어 있습니다.

‘아.. 흔히 볼 수 있는 캔버스네요…’

라고 생각하신 분, 유감입니다. 전혀 Canvas 요소를 사용하지 않고 순수하게 홈페이지를 만들 듯이 모두 DOM(Document Object Model)으로 구축되어 있습니다.

알기 쉽게 이미지를 통해 설명드리자면, 이건,

<div class="flower">
    <p class="pic"><img src="~" ....></p>
</div>

로 구축되어 있습니다.

이쪽도,

<div class="lv">
    <image-number :num="user.lv"></image-number>
    <div class="box exp">
        <div class="gauge">
            <p><span :style="{ width: `${ getExpWidth()}%` }"></span></p>
        </div>
        <p class="text gardenName">{{ user.rankName }}</p>
    </div>
</div>

와 같은 방식으로 구축되어 있습니다.

이것들도

<div class="text getExp">
    <p class="icon exp reward"></p>
    <p>+999</p>
</div>
<ul>
    <li v-for="flower in flowerList">
        <div class="wrap">
            <p class="image flower"><img src ..></p>
            <p class="name">{{ flower.name }}</p>
            <ul class="starList">
                <li v-for="rare in flower.rare"></li>
            </ul>
        </div>
    </li>
</ul>

같은 형태로 구성되어 있습니다.

위와 같이 화면 전체의 UI 파트 등 모두 HTML(DOM(Document Object Model))로 구축되어 있습니다.

DHTML

그래서 좀 더 알기 쉽게 표현하자면, ‘엄청나게 풍부한 표현을 한 홈페이지’라고 생각하시면 이해가 쉬울 것 같습니다.

이 기술은 특별히 새로운 기술도 아니고(이 글의 웹 게임 제작 시점은 2017년경), 예전부터 있는 기술로 ‘DHTML dynamic HTML(사어(死語))‘이라고 불리고 있습니다. 유명한 곳으로 2010년 5월 22일 구글이 팩맨 30주년 기념으로 발표한 이 화면 어디서 본거 같지 않으세요?

Google Pacman

https://macek.github.io/google_pacman/

네, 이 화면도 마찬가지로 HTML(DOM)로 제작되어 당시 큰 화제가 되었습니다(나이 들키겠다). 이른바 DHTML의 기술을 이용해 작성된 것입니다. 웹 인스펙터 등으로 확인해보면 알 수 있겠지만, 팩맨이 먹는 점조차도 div로 표현되어 있는 것을 확인할 수 있습니다.

DHTML 기술에 대해 더 깊이 알고 싶으신 분은 꽤 오래된 책이지만, O’Reilly사에서 출판된 ‘JavaScript 그래픽스 — 게임, 스마트폰, 웹에서 사용하는 최신 기법‘(일본)과 같은 책을 추천합니다.

왜 DOM으로 만드는가?

라고 여기까지 읽으신 분들 중 이런 의문을 가지신 분들도 적지 않으실거 같습니다.

구현이 힘들어 보이는데, 왜 굳이 DOM으로 만들까?’ 라고.

맞습니다. 현재 게임 개발용 자바스크립트 라이브러리도 많이 존재하는데, 왜 굳이 DOM으로 구축할까요. 이 많은 게임 개발용 자바스크립트 라이브러리의 렌더링은 Canvas로 되어 있습니다. 단적으로 말하면 Canvas면 ‘움직이지 않는다’때문입니다.

쉽게 말해서 Canvas로 렌더링을 하면 퍼포먼스가 나오지 않고, 인간이 쾌적하게 느끼는 FPS(frames per second) 값이 나오지 않기 때문입니다.

Android

위에서 ‘인간이 쾌적하게 느끼는 FPS(frames per second) 값이 나오지 않는다‘고 설명했지만, 모든 것이 다 그렇다는 것은 아닙니다.

PC라면 충분한 사양이 갖추어져 있다면 충분히 쾌적하게 돌아가고, iPhone이라면 Apple사가 OS와 단말기를 함께 판매하기 때문에 기종을 특정할 수 있고, 많은 기종을 신경 쓸 필요가 없지만, 문제는 Android입니다. Android는 Google이 OS를 제공하고, 각 제조사들이 단말기를 만들어서 판매합니다.

각 제조사가 판매하는 기종에 따라 사양이 낮거나 특유의 사양으로 인해 버그가 발생하는 등 이른바 기종 종속적인 상황이 발생합니다.

이러한 저사양 단말기에서도 최소한의 성능을 낼 수 있도록 고려한 결과입니다.

2020년 9월 현재도 국내 안드로이드 점유율은 아이폰을 앞지르고 있습니다.

따라서 고사양 단말기를 기반으로 하는 것이 아니라 저사양 단말기에 개발 기준을 둠으로써 많은 기종에 대응할 수 있게 되었습니다.

이와 같은 형태로 CSS3와 자바스크립트(Vue.js)를 조합하여 애니메이션을 생성합니다.

Vue.js의 채택

이상으로 DHTML 도입에 대한 흐름을 설명해드렸습니다.

이어서 Vue.js를 도입한 이유에 대해 말씀드리면, 제가 입사했을 때 이미 웹 게임 개발은 다른 멤버들이 진행 중이었지만, Canvas로 구현하다 보니 위와 같은 문제에 부딪혀서 여러 가지를 고려하고 몇 번 시도했지만, 출시까지 이르지 못한 상태였습니다.

다행히도 (라기보다는 그 때문에 입사를 권유받았죠ㅎㅎㅎ) 이런 웹게임 개발 경험, 지식을 가지고 있었기 때문에 바로 DHTML로 개발 전환을 하게 되었습니다.

이전 직장에서 AngularJS(1계)를 이용해 만든 경험이 있었기 때문에 그대로 Angular를 채택하고 싶었지만, Angular는 2부터 완전히 풀스펙 프레임 워크화되어 온 탓에 스마트폰 및 앱 내 웹뷰에는 너무 큰 프레임 워크가 되어있습니다.

웹 브라우저도 아니고, 스마트폰의 인 앱 웹뷰 가 되면 사양이 달라지거나 성능이 나오지 않을 수도 있고, 일반적인 웹 브라우저 구현보다 경량화를 고려하지 않으면 쾌적하게 동작하지 않습니다.

그래서 React.js도 고려했지만, 개발 기간도 길지 않고, 상태 변화가 많은 게임 개발에서는 MVVM 프레임 워크가 더 빠르고 안전하게 개발할 수 있어 구글에서 AngularJS를 사용하여 개발에 참여했던 Evan Yo 씨 가 개발한 Vue.js를 채택하였습니다.

Vue.js는,

「Angular의 정말 좋아했던 부분을 추출해서 여분의 개념 없이 정말 가벼운 것을 만들 수 있다면 어떨까?」

라는 개념으로 개발되었기 때문에 AngularJS의 좋은 점은 계승하고, 필요 없는 부분이나 좋지 않은 부분은 제거한 경량 MVVM 프레임 워크 중에서는 좋은 프레임 워크인 것 같습니다.

2017년 당시에는 국내에서도 이제 막 보급되는 분위기였지만, 지금처럼 일본어로 된 기사나 문서 등이 많지 않아 해외 기사를 많이 읽었던 기억이 있습니다.

또한, 위의 저사양 안드로이드 단말기의 웹 뷰(순정 웹 브라우저가 아닌)로 구동해본 경험도 없었기 때문에 ‘정말 괜찮을까’라는 생각으로 도입했던 기억이 있습니다.

아니나 다를까, 특정 OS 버전에서 ‘화면이 하얗게 변하는’ 현상이 발생했는데, Vue.js의 마이너 업데이트로 해결된 것으로 기억합니다.

아키텍처

자, 이제 놀랍게도 드디어 본론으로 들어가겠습니다.

지금까지의 이야기는 예고편에 불과합니다. (ㅎㅎㅎ)

그럼 지금부터 아키텍처에 대해 살펴보도록 하겠습니다.

대략적인 구성은 다음과 같습니다.

- components
- config
- model
- service
- class

・components: Vue.js에서 바인딩되는 View & ViewModel 레이어

・config: Enum과 env의 설정이 저장되는 레이어

・model: 비즈니스 로직을 담당하는 레이어

・service: RPC 통신을 담당하는 레이어

・class: OOP의 Class

라는 형태입니다.

MVVM 아키텍처라고 하면서 ViewModel 레이어는 단독으로 분리되어 있지 않은 형태를 취했습니다.

마지막까지 고민한 부분은 있었지만, 시간과의 균형도 있었으므로(조금 변명).. 과잉 구현인지 아닌지는 항상 고민되는 일이죠.

또, class라고 적혀있는데, 사실 당시부터 ‘Flux, Redux, Vuex‘ 등의 흐름의 영향도 있고, 전혀 store가 아닌데도 불구하고 ‘store‘라고 명명해버려서 나중에 멤버들에게도 혼란을 주는 결과를 초래한 것에 대해 이 자리를 빌려 사과드립니다. ㅎㅎㅎ

Model과 View 레이어를 분리했지만, ViewModel 레이어가 없는 것으로 인해 결과적으로 Model과 View 의존성이 남아있는 형태가 된 것이 아쉽지만, ViewModel 레이어 등을 만들어서 1을 100으로 만드는 것은 현 멤버에게 기대하면서(남에게 맡김).

위 그림과 같이 원래는 ViewModel 레이어를 만들어야 했지만, View 레이어(component) 안에 포함시킨 형태.

데이터 흐름

1페이지에 대해 Model은 최소 1개 이상 존재하는 형태가 됩니다.

HTML은 필요한 Component를 호출하고, 거기서 Model의 생성이 이루어집니다.

Modal에 필요한 데이터는 Class에서 생성됩니다. RPC 통신이 필요한 경우 Model에서 호출 Class를 생성합니다.

그림으로 표현하면 아래와 같습니다.

ViewModel 레이어 단독으로 존재하지 않는 만큼 일반적인 MVVM 아키텍처보다 비교적 단순하게 구성되어 있습니다.

기타 전체 아키텍처

애플리케이션 전체 아키텍처는 아래와 같습니다.

Vue.js의 assets 등은 AWS의 S3에 저장되어 있습니다. 포켓코의 iOS, Android의 어떤 WebView에서 접속이 들어오면, API Gateway를 통해 Lambda의 함수를 실행하고 있습니다. 무엇을 하고 있느냐 하면, 각 단말기의 정확한 디스플레이 크기를 가져와서 API Gateway를 통해 Lambda의 함수로 넘어갑니다.

이때 Lambda의 함수는 S3에 저장된 HTML을 가져와서 Viewport의 값을 동적으로 변경하고 있습니다.

이는 이른바 웹 스러움을 없애기 위해 고정 사이즈로 제작하여 단말기에 맞게 사이즈를 맞추기 위해 진행하는 구조입니다.

이 역시 웹 개발실에서 구축하기도니다.

추상 Class

이제 애플리케이션 및 전체 아키텍처는 어느 정도 파악이 되었을 것으로 생각되며, 이제 구현 측면에 대해 설명하겠습니다. 앞서 설명한 농장 게임을 예로 들어 다음 부분의 구현에 대해 설명하겠습니다.

이 부분은 ‘화단’인데, FlowerBed 클래스로 추상화되어 있습니다.

그 안에 ‘틀’인 Square 클래스가 존재합니다.

FlowerBed 클래스는 Square 클래스의 인스턴스를 배열로 가지고 있습니다.

FlowerBed: {
    squareList: Square[]
}

Square 클래스는 다음과 같은 속성을 가지고 있습니다.

Square {
    id: number,
    x: number,
    y: number,
    flower: Flower,
    rare: number, // 화단의 레어도
    exp: number, // 비료의 경혐치
    nextExp: number, // 다음 레어도로 가기위한 비료의 경험치
    isOpen: boolean // 해방되어 있는가
}

Model을 통해 이들 클래스의 상태는 변경되어 갑니다.

이를 Vue.js를 이용하여 HTML에 바인딩하여 표시 전환 등을 하고 있습니다.

HTML은 아래와 같은 형태입니다. (일부 생략하였습니다.)

<ul class="flowerSquareList">
    <li v-for="(square, index) in flowerBed.squareList">
        <div class="sow" v-if="square.isSowSeed">
            <p class="pic sowSeed" v-sowSeed="square"></p>
        </div>
        <div class="flower" v-if="square.flower">
            <p class="pic" v-if="square.flower.isBloom"><img :src="square.flower.images.bottom.m" width="100"></p>
        </div>
    </li>
</ul>

객체지향 프로그래밍 설명에서 흔히 볼 수 있는 ‘Animal’이나 ‘Cat’이나 ‘Dog’나 동물을 이용하여 추상 클래스를 구현하는 설명 등이 있지만, 별다른 변형 없이 그대로 추상 클래스의 취급을 기본으로 ‘캡슐화’ ‘계승’ ‘폴리모피즘’ 등, 오브젝트 지향 프로그밍에 준해 작성되어 있습니다.

애니메이션

꽃을 수확하거나, 씨앗을 심거나, 시간이 지남에 따라 꽃이 자랄 때 애니메이션이 발생합니다. 이러한 애니메이션의 표현인데 기본 CSS3와 자바스크립트로 표현하고 있습니다.

하나의 꽃이 수확되는 부분에 주목해 보겠습니다.

꽃이 뽑히고, 살짝 빠져 나간 후, 푸터에 있는 ‘가공 메뉴’로 수확되는 것을 볼 수 있습니다.

이 애니메이션은 3단계로 구성되어 있습니다.

  1. 세로로 길게 변형
  2. 조금 위로 이동
  3. 푸터의 목적 요소로 이동

방금 전의 flower 클래스의 div 요소에 대해 CSS 애니메이션 등을 적용합니다.

<div class="flower" v-if="square.flower">
    <p class="pic" v-if="square.flower.isBloom"><img :src="square.flower.images.bottom.m" width="100"></p>
</div>

‘1. 세로로 길게 변형’은 CSS의 transform scale 등으로 변형, ‘2. 조금 위로 이동’은 CSS의 transform translate로 이동.

1과 2까지가 CSS 애니메이션이 되고, 2가 끝나면 animationend 이벤트가 발동하므로 이를 트리거로 이동하고자 하는 목적 요소와 현재 꽃의 position의 x, y 값을 가져옵니다.

각 값을 구했으면 Tween을 이용하여 이동시킵니다.

let tween = Tween.fromData({
        start: {
            top: 시작 시의 y값,
            left: 시작 시의 x값
        },
        end: {
            top: 종류 시의 y값,
            left: 종류 시의 x값
        },
        option: {
            duration: 200,
            easing: 'easeInOutCubic',
            step: (val: any) => {
                elem.style.top = val.top;
                elem.style.left = val.left;
            },
            complete: () => {
                // 종류 시 처리
                ....
            }
        }
    });

이와 같은 형태로 CSS3와 자바스크립트(Vue.js)를 조합하여 애니메이션을 생성합니다.

터치 레이어

스마트폰의 조작은 기본적으로 터치 조작입니다.

터치나 슬라이드를 통해 꽃을 수확하거나 씨앗을 심을 수 있지만, 위의 애니메이션 시에는 각 DOM 요소가 움직이고 있어 터치나 슬라이드 등의 제대로 된 이벤트 수집이 불가능합니다.

따라서 ‘애니메이션을 하는 레이어‘와 ‘조작을 받는 레이어‘를 구분하여 사용하기도 합니다.

이렇게 함으로써 터치나 슬라이드에 의한 조작도 원활하게 이루어질 수 있도록 고안하였습니다.

Atomic Design Base CSS 아키텍처

이와 같이 CSS의 클래스 교체에 따라 표시가 변경되거나 애니메이션이 발생하거나 하는데, 이러한 인터랙티브한 확장성, 유연성을 갖게 할 필요가 있기 때문에 CSS 아키텍처에는 Atomic Design을 답습한 ‘APBCSS(Atomic Design Parts CSS)’의 CSS 아키텍처를 채택하고 있습니다.

APBCSS의 특징으로서 많은 CSS 아키텍처에서도 사용되고 있는 Layout과 같은 개념은 없고, 구성의 개념도 그 반대로, 세부화할 수 없는 UI 파트로부터 정의해 가는 아키텍처입니다.

Layout의 Style보다 먼저 ‘세부화할 수 없는 UI 파트‘부터 Style을 정의함으로써 보다 세밀한 상태 변화나 애니메이션 구현이 가능하도록 하였습니다.

이렇게 해서 첫 번째 ‘농장 게임’이 완성되었고, 이후에도 이를 기반으로 몇 개의 웹게임이 만들어지는데, 대략 한 달에 한 번씩 새로운 웹 게임을 제작할 수 있어 비용 절감에도 도움이 되고 있습니다.

기타 웹 서비스

라고 지금까지 Vue.js를 이용한 웹게임 제작 방법을 소개해드렸는데요, 물론 업무는 이것뿐만 아니라 일반적으로 말하는 웹 서비스 등의 개발도 진행하고 있으며, 작년 12월에는 ‘나의 개성을 상품으로 만들자‘라는 컨셉으로 전 세계 크리에이터들이 디자인한 디지털 패션 아이템을 모아 코코네가 제공하는 서비스(앱)를 통해 고객에게 전달하기 위한 노력으로 coconets를 출시했습니다.

coconets

https://coconets.design/

coconets는 마이크로 아키텍처를 채택하고 있으며, JS 프레임워크에는 Nuxt.js가 사용되어 SPA 애플리케이션으로 구축되어 있습니다.

그 외에도 업무 효율화, 자동화의 일환으로 툴 개발, 제너레이터 개발, 그리고 웹 개발실 멤버 전원이 npm의 개인 계정을 보유하고 있으며, cocone assets라는 라이브러리를 개발하여 npm 프라이빗 저장소에 등록하여 활용하고 있습니다.

죽은 기술을 소중히 여기며

웹 프론트엔드 분야는 기술과 트렌드가 빠르게 변화하는 분야이기도 합니다.

이런 상황에서 단순히 트렌드를 쫓아가는 것이 아니라, 앞으로도 변하지 않을 CS, 알고리즘 지식 등을 바탕으로 오랫동안 사용되어온 기술이나 아키텍처 등 소위 죽은 기술을 소중히 여기면서 패러다임의 전환이 일어났을 때에도 어떻게 활용할 수 있을지 고민할 수 있는 능력이 앞으로도 중요하다고 생각합니다. 최근에는 ‘Webassembly’ 등의 기술을 검증하면서 활용할 수 있는 부분 등을 검토하고 있습니다.

그래서 이번에는 Vue.js를 이용한 웹 게임을 만드는 방법을 중심으로 웹 개발실의 업무 등에 대해 작성해보았습니다.

조금이나마 cocone의 프론트엔드 업무에 관심을 가져주셨으면 좋겠습니다.

 

또, ‘더 이야기를 듣고 싶다’나 ‘꼭, 일하고 싶다’라는 기분이 되신 분은, 스스럼없이 문의해 주시면, 캐주얼 면담으로부터도 대응 가능합니다.

https://www.cocone.co.jp/recruit.html

그럼 다음 날에라도.

Category

Tag