개인적으로 진행중인 프로젝트에 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에 대한 이해가 일천하다보니 이런일도 발생하는군요. 어쨌든 이만 줄이겠습니다.