# 사용자 지정 디렉티브

# 시작

Vue는 코어에 포함된 기본 디렉티브 (v-modelv-show와 같은) 외에도 커스텀 디렉티브를 등록할 수 있습니다. Vue에서 코드 재사용 및 추상화의 기본 형식은 컴포넌트입니다. 그러나 일반 엘리먼트에 하위 수준의 DOM 액세스가 필요한 경우가 있을 수 있으며 이 경우 커스텀 디렉티브가 여전히 유용할 수 있습니다. 다음은 input 엘리먼트와 focusing에 대한 예제입니다.

See the Pen Custom directives: basic example by Vue (@Vue) on CodePen.

페이지가 로드되면 해당 엘리먼트는 포커스를 얻습니다. (참고: autofocus는 모바일 사파리에서 작동하지 않습니다.) 사실, 이 페이지를 방문한 이후, 다른것을 클릭하지 않았다면, 이 input 엘리먼트에 포커스가 되어 있어야 합니다. 또한, Rerun 버튼을 클릭하면 input 엘리먼트에 포커스가 됩니다.
이제 이 작업을 수행하는 디렉티브를 작성하겠습니다.

지금 이를 수행하는 지시문을 작성해 보겠습니다:

const app = Vue.createApp({})
// 전역 사용자 정의 directive v-focus 등록
app.directive('focus', {
  // 바인딩 된 엘리먼트가 DOM에 마운트 될때
  mounted(el) {
    // 엘리먼트에 포커스를 줍니다.
    el.focus()
  }
})
1
2
3
4
5
6
7
8
9

디렉티브를 로컬에서 사용하려면 컴포넌트는 directives 속성을 사용하면 됩니다.

directives: {
  focus: {
    // directive 정의
    mounted(el) {
      el.focus()
    }
  }
}
1
2
3
4
5
6
7
8

그런 다은 템플릿에서, 다음과 같이 모든 요소에서 새로운 v-focus 속성을 사용할 수 있습니다.

<input v-focus>
1

# 훅 함수

디렉티브 정의 객체는 여러가지 훅 함수를 제공합니다.(모두 선택사항입니다.)

  • beforeMount: 디렉티브가 처음 엘리먼트에 바인딩 되고 부모 컴포넌트가 마운트되기전에 호출합니다. 이곳에서 한번 실행되는 초기 세팅을 할 수 있습니다.

  • mounted: 바인딩된 요소의 부모 컴포넌트가 마운트될때 호출됩니다.

  • beforeUpdate: 포함된 컴포넌트의 VNode가 갱신되기 전에 호출이 됩니다.

TIP

렌더링 함수(render fucntions)을 설명 할 때 나중에 VNode에 대해 자세히 설명합니다.

  • updated: 포함된 컴포넌트의 VNode 그리고 컴포넌트의 자식들의 VNodes들이 업데이트 되고 난뒤에 호출됩니다.

  • beforeUnmount: 바인딩된 요소의 부모 컴포넌트가 언마운티드되기 전에 호출됩니다.

  • unmounted: 요소로부터 디렉티브가 언바인드 될때 그리고 부모 컴포넌트가 언마운트될때, 한번 호출됩니다.

Custom Directive API에서 이러한 훅들(i.e. el, binding, vnode, 그리고 prevVnode)에 전달된 인자들을 확인할 수 있습니다.

# 동적 디렉티브 전달인자들

디렉티브 전달인자들은 동적일 수 있습니다. 예를 들면, v-mydirective:[argument]="value"에서 argument 컴포넌트 인스턴스에 있는 데이터 프로퍼티 기반으로 업데이트 될 수 있습니다. 따라서 커스텀 디렉티브는 애플리케이션 전체에 걸쳐 유연하게 사용할 수 있습니다.

페이지에서 고정된 포지셔닝(fixed positioning)을 통해 엘리먼트를 고정시키는 커스텀 디렉티브를 만든다고 가정해봅시다. 다음과 같이 수직 위치 픽셀값을 업데이트하는 커스텀 디렉티브를 만들 수 있습니다.

<div id="dynamic-arguments-example" class="demo">
  <p>Scroll down the page</p>
  <p v-pin="200">Stick me 200px from the top of the page</p>
</div>
1
2
3
4
const app = Vue.createApp({})

app.directive('pin', {
  mounted(el, binding) {
    el.style.position = 'fixed'
    // bindig.value는 디렉티브에게 전달한 값입니다.. 이 경우에는 200입니다.
    el.style.top = binding.value + 'px'
  }
})

app.mount('#dynamic-arguments-example')
1
2
3
4
5
6
7
8
9
10
11

페이지의 상단으로부터 200px 떨어진 곳에 엘리먼트가 고정됩니다. 만약 우리가 상단이 아닌 좌측으로부터 떨어진 곳에 엘리먼트를 고정시키고 싶으면 어떻게 될까요? 동적 전달인자는 컴포넌트 인스터스 당 편리하게 업데이트 될 수 있습니다.

<div id="dynamicexample">
  <h3>Scroll down inside this section ↓</h3>
  <p v-pin:>I am pinned onto the page at 200px to the left.</p>
</div>
1
2
3
4
const app = Vue.createApp({
  data() {
    return {
      direction: 'right'
    }
  }
})

app.directive('pin', {
  mounted(el, binding) {
    el.style.position = 'fixed'
    // binding.arg는 디렉티브에 전달되는 인자입니다.
    const s = binding.arg || 'top'
    el.style[s] = binding.value + 'px'
  }
})

app.mount('#dynamic-arguments-example')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

결과:

See the Pen Custom directives: dynamic arguments by Vue (@Vue) on CodePen.

이제 커스텀 디렉티브는 몇 가지 다른 사용 사례를 지원할 수 있을 정도로 유연합니다. 조금 더 동적으로 만들려면, 바운드 값을 수정하는 것을 허락할 수 있습니다. 추가로 pinPadding 프로퍼티를 만들고 <input type="range">에 바인딩을 합니다.




 


<div id="dynamicexample">
  <h2>Scroll down the page</h2>
  <input type="range" min="0" max="500" v-model="pinPadding">
  <p v-pin:[direction]="pinPadding">Stick me {{ pinPadding + 'px' }} from the {{ direction }} of the page</p>
</div>
1
2
3
4
5




 




const app = Vue.createApp({
  data() {
    return {
      direction: 'right',
      pinPadding: 200
    }
  }
})
1
2
3
4
5
6
7
8

이제 컴포넌트 업데이트시 고정 할 거리를 재 계산하는 디렉티브 로직을 확장해봅시다.







 
 
 
 


app.directive('pin', {
  mounted(el, binding) {
    el.style.position = 'fixed'
    const s = binding.arg || 'top'
    el.style[s] = binding.value + 'px'
  },
  updated(el, binding) {
    const s = binding.arg || 'top'
    el.style[s] = binding.value + 'px'
  }
})
1
2
3
4
5
6
7
8
9
10
11

결과:

See the Pen Custom directives: dynamic arguments + dynamic binding by Vue (@Vue) on CodePen.

# 함수 약어

위에 예제에서 mountedupdated 두개의 훅에서 같은 동작을 하며, 다른 훅은 필요하지 않을때도 있습니다. 이럴때는 단순히 콜백을 디렉티브에 전달하여 해결 할 수 있습니다.

app.directive('pin', (el, binding) => {
  el.style.position = 'fixed'
  const s = binding.arg || 'top'
  el.style[s] = binding.value + 'px'
})
1
2
3
4
5

# 객체 리터럴

디렉티브에 여러 값이 필요한 경우, JavaScript 객체 리터럴을 전달할 수도 있습니다. 디렉티브는 유효한 JavaScript 표현식을 사용할 수 있습니다.

<div v-demo="{ color: 'white', text: 'hello!' }"></div>
1
app.directive('demo', (el, binding) => {
  console.log(binding.value.color) // => "white"
  console.log(binding.value.text) // => "hello!"
})
1
2
3
4

# 컴포넌트 사용

컴포넌트에서 사용되는 경우, 커스텀 디렉티브는 non-prop 속성과 유사하게 항상 컴포넌트의 루트 노드에 적용됩니다.

<my-component v-demo="test"></my-component>
1
app.component('my-component', {
  template: `
    <div> // v-demo 디렉티브가 여기에 적용됩니다
      <span>My component content</span>
    </div>
  `
})
1
2
3
4
5
6
7

속성과 달리, 디렉티브는 v-bind="$attrs"를 사용하여 다른 엘리먼트에 전달할 수 없습니다.

fragments 지원을 통해, 컴포넌트는 잠재적으로 둘 이상의 루트 노드를 가질 수 있습니다. 다중 루트 컴포넌트에 적용되면 디렉티브가 무시되고 경고가 발생합니다.

Deployed on Netlify.
Last updated: 2021-01-13, 05:23:36 UTC