Konva 적용하기

서두

블로그 플랫폼이 VuePress인 만큼 기술적 이점을 누리기 위해, 이번엔 자유롭게 그림을 그릴 수 있도록 Konva를 적용해 보도록 하겠다.

Konva

Getting started with vue and canvas via Konva | Konva

Konva란 Canvas 기반의 드로잉 라이브러리이다. 프론트 3대장중 React, Vue 바인딩은 공식 사이트에서 링크로 제공하고 있으며, Angular 바인딩도 Konva 깃헙에 가보면 찾을 수 있다.

본 사이트가 VuePress이니 vue-konva를 사용하도록 하겠다.

Konva 설치

먼저 가이드대로 vue-konva를 설치해준다. -D (--save-dev)를 붙인 이유는, 어차피 SSG는 빌드 시점에 모든게 사용되기 때문이다. VuePress 자체도 마찬가지.

> npm i -D konva vue-konva
1

공식 가이드와는 약간 다른 방법이지만 결과적으로 같은 방법을 아래처럼 적용해보면...



 





 


// .vuepress/enhanceApp.js

import VueKonva from 'vue-konva'

export default ({
  Vue, // the version of Vue being used in the VuePress app
  ...
}) => {
  Vue.use(VueKonva)
  ...
1
2
3
4
5
6
7
8
9
10

빌드가 성공한다!

일반 Vue 앱이 아니므로 부트스트랩 위치가 앱 메인이 아닌 enhanceApp.js가 된다. 자세한건 VuePress 매뉴얼을 참고하자.

Konva 적용

이제 샘플 도형을 그려 볼 차례이다. 마크다운 본문에 직접 konva의 태그를 넣어보자. 마크다운에 data 객체를 집어 넣을 방법이 없으므로 인라인 객체로 넣어본다.

<v-stage :config="{ width: 200, height: 200 }">
  <v-layer>
    <v-circle :config="{
      x: 100,
      y: 100,
      radius: 70,
      fill: 'red',
      stroke: 'black',
      strokeWidth: 4
    }"></v-circle>
  </v-layer>
</v-stage>
1
2
3
4
5
6
7
8
9
10
11
12

안된다! 개발자 도구를 열어보니 v-stage 컴포넌트까지는 값 설정이 되었으나 v-circle 컴포넌트는 설정이 되지 않았다. 아마도 슬롯에 대한 값 전달이 마크다운에서는 잘 되지 않는 모양이다.

이대로 포기하기는 아까우니, 컴포넌트로 만들어보자.

Konva 샘플 컴포넌트 작성

아래처럼 컴포넌트를 작성하자. 중앙 정렬과 외곽선을 위해 ref 기법으로 DOM 객체를 획득하여 수동 스타일링을 해주었다.

v-stage 태그 내부는 모두 다른 태그들로 빌드되기 때문에, Vue 스타일링으로 Konva 객체에 스타일 적용은 불가능하다. config에서 하는 방법도 없기 때문에 DOM 접근법을 사용했다.




 

























 
 
 






<!-- docs/.vuepress/components/KonvaTest.vue -->

<template lang="html">
  <v-stage :config="configKonva" ref="stage">
    <v-layer>
      <v-circle :config="configCircle"></v-circle>
    </v-layer>
  </v-stage>
</template>

<script>
export default {
  data() {
    return {
      configKonva: {
        width: 200,
        height: 200
      },
      configCircle: {
        x: 100,
        y: 100,
        radius: 70,
        fill: "red",
        stroke: "black",
        strokeWidth: 4
      }
    }
  },
  mounted () {
    const divKonvaJsContent = this.$refs.stage.$el.children[0]
    divKonvaJsContent.style.margin = '0 auto' // align center
    divKonvaJsContent.style.border = '1px solid black'
  }
}
</script>

<style scoped>
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

그리고 마크다운 본문에 해당 컴포넌트를 삽입해보자.

<KonvaTest />
1

동작한다! 아래처럼 빨간 동그라미가 뜬다면 성공이다.

Konva 응용 컴포넌트 작성

해본 김에 조금 더 해보자. Konva 가이드에 있는 인터랙티브한 예제를 하나 가져와봤다. 소스 그대로에서 너비/높이와 중앙 정렬, 외곽선 정도만 추가해줬다.

결과는 아래와 같다. 마우스로 별을 드래그 해보면 이동까지 잘 동작됨을 확인할 수 있다.

SSR 고려사항

잘 동작한다고 생각했을 때, 개발서버를 종료하고 빌드를 돌려보면 실패함을 알 수 있다.



 

> npm run docs:build
...
- ReferenceError: window is not defined
1
2
3

window 객체가 없다고 에러가 날 것이다. 이유는 konva 관련 태그를 사용하는 컴포넌트가 서버사이드 렌더링이 되면 node.js상에서 빌드가 되기 때문에 window 객체가 없기 때문이다.

이런 경우는 ClientOnly 컴포넌트로 감싸주어야 한다. 그러면 KonvaTest 컴포넌트는 서버사이드에서 빌드되지 않고 남아 있다가 화면이 로드되고나서 브라우저에서 구동을 시작하게 될 것이다.

<ClientOnly>
  <KonvaTest />
</ClientOnly>
1
2
3

ClientOnly 관련 내용은 아래 링크를 참고하자.

Browser API Access Restrictions | VuePress

마크다운 본문에 매번 이렇게 감싸주기는 불편하므로, 실제로는 ClientOnly 컴포넌트로 감싸는 래퍼 컴포넌트를 별도로 만들어 주는 것이 좋겠다.

결론

생각보다 매우 간단히 적용에 성공했다. 향후 포스팅 작성중에 도형이나 다이어그램 그릴 경우에 활용 가능하겠다.

최종 수정: 2019-10-8 13:19:11