[Vue.js] SVG 와 v-if, v-show 에 의한 랜더링 문제

Nekoromancer·2019년 2월 1일
0

개인적으로 진행중인 프로젝트에 Nuxt.js 를 도입해서 작업을 하고 있었는데 뜻밖의 상황에 한참을 삽질을 했습니다

문제 발생

문제가 발생한 조건은 다음과 같습니다.

  • Fontawsome 을 설치하여 아이콘으로 사용
  • computed 에 정의된 값의 변화에 따라 토글이 되도록 v-if 를 사용

코드로 살펴보면 대략 이렇습니다.

<i class="fas fa-foo" v-if="isOk"></i>
<i class="fas fa-bar" v-else></i>

// 중략
data () {
  return {
    someDataA: '',
    someDataB: '',
  };
},
computed: {
    isOk: {
      return someDataA === someDataB;
    }
}

굉장히 흔하고 간단한 vue.js 코드 였습니다. someDataA 와 someDataB 를 비교해서 참, 거짓을 반환하고 그에 따라 아이콘이 바뀌는 로직입니다. 그런데 원하는대로 동작을 하지 않았습니다.

isOk 의 초기 반환값이 true 이기 때문에 fa-foo 는 정상적으로 출력됩니다. 하지만 값이 바뀌어 false 가 되어도 fa-foo 는 사라지지 않고 fa-bar 는 나타나지 않습니다. v-if 를 v-show 로 변경해 보아도 증상은 동일했습니다.

해결

해답에 도달하기까지 긴 과정을 거쳤지만 결론부터 말씀드리면, fontawsome 과 같이 svg 이미지를 그리는 엘리먼트에는 v-if 나 v-show 같은 동적인 동작을 직접 지정하면 안됩니다.

그럼 어떻게 하는가가 문제인데, v-if 나 v-show 를 엘리먼트에 직접 지정하지만 않으면 됩니다. 다른 엘리먼트로 한번 감싼 후에 해당 엘리먼트를 토글하면 됩니다. 아래와 같은 코드에서는 정상적으로 동작합니다.

<span v-show="isOk">
    <i class="fas fa-foo"></i>
</span>
<span v-show="!isOk">
    <i class="fas fa-bar"></i>
</span>

// 중략
data () {
  return {
    someDataA: '',
    someDataB: '',
  };
},
computed: {
    isOk: {
      return someDataA === someDataB;
    }
}

여기서 주목해야 할 부분은 v-if 가 아닌 v-show 를 사용했다는 점입니다. v-if 를 사용할 경우 역시 처음과 같이 동작하지 않습니다.

왜 그럴까?

몇가지 짐작가는 원인이 있지만 사실 명확한 이유를 찾지 못했습니다. 일단 현상만을 두고 본다면 svg 이미지는 페이지가 로드될 때 랜더링이 되어야 한다는 점입니다. v-if 를 사용하게 되면 페이지 로드시에 조건에 맞지 않는 svg 엘리먼트가 랜더링 되지 않기 때문에 조건이 참이 되어도 화면에 이미지를 그려주지 않습니다.

역시 같은 이유로 다른 엘리먼트로 svg 를 감싼다음 v-if 로 토글을 하게 되면 동작하지 않는 것으로 추정됩니다. v-show 는 페이지에 엘리먼트를 렌더링하고 display: none; 의 css 속성을 주어서 엘리먼트의 출현 여부를 토글합니다.

여기서 남는 의문은 v-show 를 svg에 직접 주었을 때 왜 출현하지 않는가입니다. display: none; 속성이 인라인으로 들어가기 때문에 페이지 로드시 랜더링 되지 않는것이 아닌가 하는 추측을 해봅니다.

정확한 이유를 아시는 분이 계시면 알려주셨으면 합니다. svg에 대한 이해가 일천하다보니 이런일도 발생하는군요. 어쨌든 이만 줄이겠습니다.

profile
고양이 앓이 중인 프론트엔드 개발자

4개의 댓글

comment-user-thumbnail
2019년 2월 1일

vue나 react에서는 가상 dom을 이용하여 항상 바뀐 결과물을 최소한의 비용으로 변경하려합니다. v-show나 v-if를 사용하여 해당 태그를 대체할 경우에 태그 명이 같을 때 가상dom은 변경되었다고 생각하지 않아 새로이 업데이트를 하지 않습니다. 같은 태그명을 가진 엘리먼트를 업데이트하기 위해선 attribute 속성에 key 속성을 주면 변경할 수 있습니다.
예를들어

2개의 답글
comment-user-thumbnail
2019년 2월 1일

https://codepen.io/anon/pen/Odpajd
되는군요.
이제 증상은 더욱 미궁 속으로 (...)

답글 달기