티스토리 뷰

이번에 작성할 포스팅은 Vue3.0에 Fontawesome을 적용하는 방법이다.

 

사실, 아직 공식적으로 Fontawesome이 Vue 3.0을 지원하진 않는다…

그도 그럴것이 정식 릴리즈 된 지 아직 2주도 되지 않은 시점이니까.

 

Font Awesome의 공식 홈페이지에서 제시하고 있는

vue-fontawesome 깃헙 레파지토리도 방문해 보신 분이라면 알아채겠지만

Vue 3에 대한 정식 지원이 되지 않고 있다.

 

좀 더 정확히는 아래처럼

우리 지원해. 그러니까 이 방법을 쓰면 될거야, 라고 안내는 하고 있지만

vue-fontawesome의 설치 가이드

위에 버젓이 Using Vue 3.x라고 하고 npm install 명령어를 적어놨지만

실제 사용하면 아래와 같이 No matching version found for @fortawesome/vue-fontawesome@3라고 콘솔에 출력되는 걸 볼 수 있을 것이다.

동작그만, 시작부터 밑장빼기냐?

나는 처음에 fortawesome으로 시작하는 레파지토리명이 잘 못 올려놓은 건 줄 알았지만...그게 아니었다. 그걸 어떻게 아느냐면...해당 vue-fontawesome 깃헙 레파지토리의 마지막 커밋이 29일 전인데 Vue 3는 공식 릴리즈된지 10일밖에 안 됐으니까^^...

 

즉, Vue 3의 공식 릴리즈 이후로 vue-fontawesome은 변경사항이 없다는 거다.

(2020년 09월 30일 기준)

 

하지만 내가 진행하던 토이프로젝트는 이미 Vue3 를 타겟으로 하기로 마음먹었고, 진행간에 아이콘이 필요하므로 설치 및 활용을 해야했다. 그래서 그 간에 있었던 트러블 슈팅을 포스팅하려고 한다.


0. Fontawesome 사용을 위한 기본 npm 설치

우선 아래와 같은 npm 패키지는 설치하자.

npm i --save @fortawesome/fontawesome-svg-core
npm i --save @fortawesome/free-solid-svg-icons

fontawesome-svg-core는 fontawesome의 아이콘을 svg 형태로 사용 할 수 있게 도와주는 패키지free-solid-svg-icons는 fontawesome에서 제공하는 아이콘 중에서, 무료인 아이콘들 중 solid 카테고리에 있는 항목들을 설치하는 거다

 

그외에 brand, regular 카테고리의 아이콘들도 있는데 필요시 아래 명령어로 추가 설치를 하면된다.

npm i --save @fortawesome/free-brands-svg-icons
npm i --save @fortawesome/free-regular-svg-icons

참고로 유료 아이콘도 있다. 유료 아이콘부터는 'Pro'라고 불리는데 나는 무료 아이콘만으로도 충분하지만, 그 이상을 필요로 하는 사람이라면 이 링크를 통해 Font Awesome의 공식 홈페이지로 가자.


1. 일단 설치를 해야 뭐라도 해보지.

다시 본론으로 돌아와서, 당연한 이야기지만...무료든 유료든 우선 설치를 해야 활용을 할 거 아닌가?

위에서도 말했지만 vue-fontawesome에서 가이드하고 있는 아래와 같은 명령어를 사용해봤자

npm i --save @fortawesome/vue-fontawesome@3

위에서 본 No matching version found for @fortawesome/vue-fontawesome@3 로그를 다시 한번 볼 뿐이다. 자, 그러면 어떡해야 하느냐? 실제 @fortawesome/vue-fontawesome이 Vue3에 맞게 설치되는 명령어는 아래와 같다.

 npm i --save @fortawesome/vue-fontawesome@3.0.0-1

참고로 본 명령어를 통한 해결법은 Vue 3 깃헙의 Issue 페이지에 올라와있다.

역시, 대격변이후로 헤매는 건 만국 개발자 공통인 거 같다.

 

명령어를 통한 직접 설치는 위와 같이 하면 되고 package.json에 추가해서 npm install 명령어를 쓰려는 분은 아래와 같이 package.json에 작성해주시면 된다.

"@fortawesome/vue-fontawesome": "^3.0.0-1",

 

자, 여기까지 했다면 이제 Vue 3에서 활용가능한 Font Awesome의 설치를 끝냈다.


2. 실제 Vue3에서 활용하기

Vue 3는 뭔가 참...좀 그렇다.

많이 안 바뀐 거 같은데 뭐가 또 많이 바꼈다.

 

이전의 Vue에서는 글로벌 컴포넌트가 필요한 경우 아래와 같이,

Global API인 Vue.component를 통해 컴포넌트를 정의하고 사용해왔다.

 

Vue.component('font-awesome-icon', FontAwesomeIcon)

위 예시는 vue-fontawesome에 게시된 npm 패키지 이후,

엔트리파일인 main.js에 vue-fontawesome을 정의하는 부분이다.

 

당연한 말이지만...안 된다, 저거.

 

일단...Vue 3에는 Global API에 Component 가 없다.

ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

 

멀쩡히 있던...그리고 아주 잘 쓰이던 애를 없애버렸다.

Vue3의 Global API ← 이 링크를 통해 들어가보자. 

없는 걸 확인했는가? 하지만 여기엔 함정이 있다.

 

ㅂㄷㅂㄷㅂㄷㅂㄷ

Vue3 Application API ← 이 링크로 들어가보자.

봤는가? Component API가 버젓이 있ㅋ다ㅋ

 

이에 대한 상세한 설명은 Vue 3에서 나타난 Application 개념을 설명해야하는데...

본 포스팅은 어디까지나 Font Awesome을 Vue3에 적용하는 트러블 슈팅이므로 그에대한 설명은 다른 포스팅에서 하겠다.

 

자, 우선 vue-fontawesome 깃헙에 적힌 것처럼 그대로 엔트리 파일에 정의하고

실제 활용되는 코드 또한, 아래와 같이 깃헙에 있는 가이드를 그대로 따라서 실행해보겠다.

 <font-awesome-icon icon="user-secret" />

...예상한 결과지만 아래와 같이 나온다.

 

없으니까 당연히 못 찾겠지^^

자, 그럼 어떻게 해야하느냐...

 

사실 매우 간단하다. 로마에 가면 로마법을 따르듯이, Vue 3에 왔으니 Vue 3의 법을 따르면 된다.

 

Vue 3에서 Application 라는 개념이 등장을 했고, 기존에 new Vue 라는 코드를 통해 Vue 인스턴스를 생성해서 활용을 했지만...Vue 3 부터는 createApp이라는 메서드의 형태로 Vue Application 인스턴스를 생성해서 활용하게 변경되었다.

 

즉, 아래와 같이 작성해주면 된다.

import {createApp} from 'vue'
import { createRouter, createWebHashHistory } from 'vue-router'
import App from './App'
import router_main from './router/router_main'

// font-awesome과 관련된 import를 정의
import { library } from '@fortawesome/fontawesome-svg-core'
import { faUserSecret } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
library.add(faUserSecret)


let router = createRouter({
    history: createWebHashHistory(),
    routes: router_main
})
const app = createApp(App);

// 위에 createApp을 통해 생성한 Vue Application 인스턴스의 component API 활용
app.component('font-awesome-icon', FontAwesomeIcon)


app.use(router);
app.mount('#app');

위와 같이 엔트리 파일에 정의하게되면 이제 아래와 같은 예시코드를 사용하면 

 <font-awesome-icon icon="user-secret" />

아래와 같이 아이콘이 브라우저에 나오는 걸 확인할 수 있다.

 

별 거 아닌데 아이콘이 뜨니까 그렇게 반갑더라...

 

위와 같은 정의가 끝났다면, 이제 코드 어디에서든 <font-awesome-icon> 이라는 커스텀 태그를 통해서 Font Awesome의 아이콘을 활용할 수 있다.


 

3. 이전 버전 그대로 쓰고 싶을 때

또 하나의 트러블 슈팅거리가 있다.

세상엔 함정이 참 많다.

 

만약 당신이  npm 패키지에서 제공하는 vue-fontawesom 사이트의 예시를 참고해서 설치했다면,

아래와 같은 명령어를 썼을거다.

npm i @fortawesome/vue-fontawesome

보통 npm 패키지를 설치할 때 버전까지는 기입을 잘 하지 않는다. 왜냐면, 이름만 치면 설치 시점의 가장 최신 버전이 설치가 되니까 말이다. 게다가 패키지 사이트의 우상단에도 아래와 같이 아주 잘 보이게 명령어를 써두니가 말이다.

 

대부분은 그냥 저거 복붙에서 cmd에 붙이니까 말이다…

하지만 참 안타깝게도, 저 명령어로 vue-fontawesome을 설치하게 되면 2.x 버전이 설치되게 된다.

그러니까…Vue 2.x 버전을 지원하게 작성된 버전말이다.

 

위 버전을 설치하고, 2. 실제 Vue3에서 활용하기 항목에서 작성한 것처럼 엔트리 파일에 패키지들을 import 후, component API로 사용하게끔 정의하고 코드를 작성해보면 화면에 아무것도 나오지 않을 것이다. 그리고 개발자 도구를 열어보면 아래와 같은 에러로그를 확인할 수 있다.

 

개발자 도구로 열어본 실제 코드에 에러나는 위치, 그리고 콘솔창 에러로그

Cannot read property ‘icon’ of undefined 라는 에러로그가 콘솔에 출력 될 것이고,

index.es.js:434로 집혀있는 코드 위치를 찾아가보면 좌측 이미지와 같이

var iconArgs = props.icon 라고 정의 된 코드인데, props 변수에서 빨간줄이 그어져 있을 거다.

 

당연하다…

실제로 저 위치에 브레이크 포인트를 걸고 찍어보면 props 라는 객체 자체가 undefined로 나온다.

 

props = undefined

props라는 인스턴스가 이미 undefined인데 거기에 체이닝된 icon이 인식 될 리 없다.

자바스크립트는 1번의 undefined는 이해해줘도 2차 연쇄부터는 참아주지 않는 언어니까말이다.

 

뭐, 사실 이 이슈는 1번에서 설명했던 3.x 버전을 설치했다면 발생하지 않을 이슈지만…

이리 삽질 저리 삽질하는 개발자의 입장에선 충분히 겪을 수 있는 이슈다.

실제로 처음 vue-fontawesome 3.x 버전을 옳게 설치하지 못 한 내가 겪은 이슈기도 하다.

 

이렇게 에러를 만남으로써 난 또 하나의 트러블 슈팅 능력을 얻었다, 하하하!!

이 이슈를 해결하는 방법은 생각보다 심플하다.

Font Awesome은 결국 아이콘을 SVG 형태로 제공하는 역할이므로,

SVG를 직접 화면에 렌더링하는 Vue Component를 만들면 된다.

(개발자는 개발하는 사람입니다, 없으면 개발하면 됩니다. 그것이 개발자니까요)

 

사실 이 해결법도  Vue 3 Github Issue #230을 보면 중간쯤 답변으로 작성되어 있는 내용이다.

(Vue 3 Github의 #230 이슈는 이름자체가 Vue 3 Support 인 것으로 보아 Vue 3의 릴리즈 이후, 개발자들을 지원하기 위해 열려있는 이슈로 보인다. 그러니 Vue 3 최신 버전을 적용하려는 개발자라면 필수로 들락날락하자. 뭐가 안 된다 싶으면 저기부터 가보는 걸 추천한다)

 

아래는 내가 작성한 Icon 컴포넌트이며, #230 이슈에 올려진 코드에 사이즈와 관련된 props를 추가하여 수정한 코드다.

(해당 코드는 Github의  vue starter 레파지토리에도 올라와 있으며 파일에 대한 다이렉트 링크는 여기 에 남겨둘테니 관심있는 사람은 직접와서 보셔도 된다)

<template>
    <svg xmlns="http://www.w3.org/2000/svg" :class="$props.class"
         :viewBox="`0 0 ${width} ${height}`" :style="iconStyle">
        <path fill="currentColor" :d="svgPath" />
    </svg>
</template>

<script>
import { library } from '@fortawesome/fontawesome-svg-core';
import { fas } from '@fortawesome/free-solid-svg-icons';
import { defineComponent, computed } from 'vue';
import { findIconDefinition } from '@fortawesome/fontawesome-svg-core';
library.add(fas)

export default defineComponent({
    name: 'toyIcon',
    props: {
        icon: {
            type: String,
            required: true
        },
        type: {
            type: String,
            default: "fas",
            required: false
        },
        class: String,
        size: {type: [Number, String], default: ''},
        small: {type: Boolean, default: false},
        medium: {type: Boolean, default: false},
        large: {type: Boolean, default: false},
    },
    setup(props) {
        const definition = computed(() =>
            findIconDefinition({prefix: props.type, iconName: props.icon})
        );

        const width = computed(() => definition.value.icon[0]);
        const height = computed(() => definition.value.icon[1]);
        const svgPath = computed(() => definition.value.icon[4]);
        const iconStyle = computed(() =>{
            let style = {};
            if(props.small){style.width = '12px';}
            else if(props.large){style.width = '18px';}
            else if(props.size){style.width = props.size + 'px';}
            else{style.width = '14px';}
            return style;
        });
        return { width, height, svgPath, iconStyle };
    }
})
</script>

해당 코드는 내 경우에는 toyIcon.vue라는 이름으로 사용중이며 위와 같이 컴포넌트를 생성한 이후,

엔트리 파일에 동일하게 component라는 Application API를 통해 정의하거나,

import {createApp} from ‘vue’
import { createRouter, createWebHashHistory } from ‘vue-router’
import App from ‘./App’
import router_main from ‘./router/router_main’

/// 커스텀하게 정의한 toyIcon.vue를 import/
import toyIcon from ‘toyIcon.vue의 경로’

let router = createRouter({
    history: createWebHashHistory(),
    routes: router_main
})

const app = createApp(App);

/// Application component API를 통해서 전역으로 설정/
app.component(‘toy-icon’, toyIcon)
app.use(router);
app.mount(‘#app')’

혹은 필요한 vue 파일에서 직접 import 하여 사용해도 된다.

<template>
  <toy-icon icon="user-secret"/>
</template>

<script>
import toyIcon from 'toyIcon.vue의 경로'

export default {
  components: {toyIcon}
}
</script>

 

뭐, 혹시 이것마저도 귀찮으신 분은 toyIcon.vue 파일을 첨부해서 올려드리니 받아서 쓰시기 바란다.

toyIcon.vue
0.00MB

댓글
최근에 올라온 글
Total
Today
Yesterday