Web

[핵심만 골라 배우는 Vue.js by 수코딩] 3. 디렉티브 - 화면출력, 바인딩, 랜더링, 이벤트

반응형

https://vuejs.org/guide/introduction.html

 

화면출력 디렉티브

Directive Vue에서 HTML 태그에 특별한 동작을 하도록 지시하는 속성
템플릿 에서 v- 로 시작
문자열 출력 v-html <p v-html="htmlContent"></p> html태그가 처리되어 출력
보완에는 취약
v-text <p v-text="message"></p> 텍스트만 출력, 
{{}} 와 유사 하지만 부분적 변경이 안됨
  참고 : {{}}
 = 머스타시 문법- 수염같다
= 보간문법
= Interpolation
v-text는 해당 태그텍스트 전체를 교체하지만 
일부만 변수처리가능 
v-pre <p v-pre>{{ name }}</p>

출력 : 
<p>{{ name }}</p>
이스케이프
뷰 컴파일러가 뷰 에플리케이션 코드 컴파일 안하고 문자 그대로 출력
1. 더블 중괄호 그대로 필요할 때 사용
2. 성능상 컴파일할 필요 없는 정적인 영역을 건너뛰게 하고 싶을 때 (최적화용)
v-slot <template>
  <div class="card">
    <header>
      <slot name="header">[기본 헤더]</slot>
    </header>
       <footer>
      <slot name="footer">[기본 푸터]</slot>
    </footer>
  </div>
</template>
-------------------------------------------------
<template>
  <CardBox>
    <!-- 이름 있는 슬롯 -->
    <template v-slot:header>
      <h3>📝 커스텀 헤더</h3>
    </template>
    <!-- 또 다른 이름 있는 슬롯 -->
    <template v-slot:footer>
      <small>ⓒ 2025 Vue Inc.</small>
    </template>
  </CardBox>
</template>
<script>
import CardBox from './components/CardBox.vue'
export default {
  components: { CardBox }
}
</script>
자식에 컴포넌트에 name과 함께 지정하여 부모에서 값을 넣음
조건 v-show <p v-show="isVisible">보이는 문장</p> 조건 안맞을때 : display: none
가벼움(빠름)
조건이 자주바뀔때
v-if <p v-if="isLoggedIn">환영합니다</p> 조건 안맞을 때 : <!--v-if-->
무거움(느림)
조건이 자주 변하지 않을때 
v-else-if,
v-else
<p v-if="score >= 90">A</p>
<p v-else-if="score >= 80">B</p>
<p v-else>C</p>
조건이 안맞을때 
<!--v-if-->
<!--v-else-if-->

 

바인딩 v-bind <img :src="imgUrl"  v-bind:class="black"/>
<input type="text" :value="name" @input="name = $event.target.value" />

HTML 태그 속성(attribute)에 동적으로 연결 
1. v-bind:속성명  으로 사용
2. :속성명 으로 사용
{{}} 로 값을 넣을 수 없음
<template>
  <div>
    <p>진행률: {{ progress }}%</p>
    <progress :value="progress" max="100"></progress>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const progress = ref(30)
</script>
프로그래스 출력
v-model

양방향 
데이터 바인딩 
(Two-way Binding)
<template>
  <div>
    <!-- 이름 입력 -->
    <input v-model="name" placeholder="이름을 입력하세요" />
    <p>입력한 이름: {{ name }}</p>

    <!-- 과일 선택 (드롭다운) -->
    <select v-model="selected">
      <option value="">선택하세요</option>
      <option value="apple">사과</option>
      <option value="banana">바나나</option>
    </select>
    <p>선택한 과일: {{ selected }}</p>

    <!-- 체크박스 그룹 -->
    <p>좋아하는 과일을 모두 선택하세요:</p>
    <label><input type="checkbox" value="apple" v-model="fruits" /> 사과</label>
    <label><input type="checkbox" value="banana" v-model="fruits" /> 바나나</label>
    선택한 과일들: {{ fruits }}

    <!-- 라디오 버튼 그룹 -->
    <p>한 가지 과일만 선택하세요:</p>
    <label><input type="radio" name="fruits2" value="apple" v-model="fruits2" /> 사과</label>
    <label><input type="radio" name="fruits2" value="banana" v-model="fruits2" /> 바나나</label>
    <p>선택한 과일: {{ fruits2 }}</p>

    <!-- 단일 체크박스 -->
    <label>
      <input type="checkbox" v-model="agree" />
      이용약관에 동의합니다
    </label>
    <p>동의 여부: {{ agree ? '동의함' : '동의 안 함' }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'

// 입력 상태
const name = ref('')
const selected = ref('')

// 체크박스 그룹
const fruits = ref([]) // 초기 선택 없음
// const fruits = ref(['banana'])  // ← 초기 선택 바나나로 하려면 이 줄 사용

// 라디오그룹
const fruits2 = ref('')  // 예: 초기값 'banana' 가능

// 단일 체크박스
const agree = ref(false)
</script>
양방향 데이터 바인딩 (view ↔ model)
* 얕은복사/참조 주의
input  , textarea, select 처리시 사용. 셋팅값 사용자 선택값 사용. 

내부적 코드
<input v-model="msg" />
<input :value="msg" @input="msg = $event.target.value" />

내부적으로
@change 이벤트를 사용


ref('')  Composition API 방식
IME (Input Method Editor) 에서 주의
- 한글이나 한자처럼 자판에 있는 글자보다 수가 더 많은 문자를 계산&조합하여 입력해주는 소프트웨어

<template>
  <div>
    <input v-model="str" placeholder="문자열을 입력하세요" />
    <p>글자 수: {{ str.length }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const str = ref('')
</script>
영어는 실시간으로 글자수 길이 사용가능
한글 글자수 출력 불가.(조합해서 작성하는글)
별도 input 이벤트로 function 으로 분리해서 처리할 것. 
부모-자식 양방향 바인딩시 기본수식어 안됨.
컴포넌트 내부에서 처리
하기 수식어 참조
랜더링 v-once <template>
  <div>
    <p v-once>v-once 적용: {{ msg}}</p>
    <p>일반 렌더링: {{ msg }}</p>

    <button @click="changeMsg ">
          메시지 변경
     </button>
  </div>
</template>

<script setup>
import { ref } from 'vue';

const msg  = ref('처음 메시지');

function changeMsg  () {
  msg   .value = '바뀐 메시지';
}
</script>
렌더링 최적화 :
최초 렌더링 이후 다시는 업데이트하지 마라
v-memo <template>
  <ul>
    <li
      v-for="item in items"
      :key="item.id"
      v-memo="[item.name]"
    >
      {{ item.name }}
    </li>
  </ul>

  <button @click="changeOther">다른 항목 변경</button>
</template>

<script setup>
import { ref } from 'vue'

const items = ref([
  { id: 1, name: 'Vue' },
  { id: 2, name: 'React' },
  { id: 3, name: 'Svelte' },
])

function changeOther() {
  items.value[0].name = 'Vue.js' // 첫 번째 항목만 바뀜
}
</script>
조건부로 다시 렌더링
Vue 3.2 이상
v-for 와 같이 써서 일부 값이 바뀔때 데이터 재로딩 하는 로직으로 사용
* 동일 태그에 사용할것 주의
v-for <ul>
  <li v-for="(fruit, index) in fruits" :key="i">
    {{ index }} {{ fruit }}
  </li>
</ul>
data() {
  return {
    fruits: ['사과', '바나나', '포도']
  }
}
v-for와 v-if를 같은 요소에 함께 쓰면 **우선순위는 v-for > v-if** 분리 사용 추천

 


이벤트
v-on:event  
v-on:click <template>
  <div>
    <button v-on:@click="sayHello($evnet)">인사하기</button>
    <button @click="($event) =>sayHello($evnet)">인사하기</button>
    <h1>{{num}} 번<h1>
  </div>
</template>

<script>
export default {
  data(){
    return {
      num:0,
    };
  },
  methods: {
    sayHello(e) {
      alert("안녕하세요!");
      console.log(e)
      this.num = this.num +1;
    }
  }
}
</script>

1.  정식 문법 
 v-on:click="sayHello"
2.  축약형 
 @click="sayHello"

3. 그외 이벤트
@click 클릭 이벤트 @click="함수명"
@dblclick 더블 클릭 @dblclick="함수명"
@mouseenter 마우스 진입 @mouseenter="onHover"
@keyup 키보드 키 누름 @keyup.enter="submit"
@submit 폼 제출 <form @submit.prevent="onSubmit">

4. 이벤트 객체
@click="sayHello($evnet)"
@click="($event) =>sayHello($evnet)"
성능은 위에것이 좋음, 타입안정성은 아래것이 높음

5. 반응성(Reactivity)
데이터가 바뀌면 자동으로 화면이 업데이트 되는 시스템 

cf) Vue 컴포넌트스크립트 (script) 내에서 data, methods 속성접근시this로 호출
v-on:input <textarea @input="memo = $event.target.value"></textarea> 실시간 입력 @input
v-on:change <!-- 체크박스 그룹 --><input @change="id = $event.target.value" />
  <select @change="selected = $event.target.value">
    <option value="">선택하세요</option>
    <option value="apple">사과</option>
    <option value="banana">바나나</option>
  </select>
입력중 포커스를 잃을때 실행 @change
파일 처리도 @change로 처리
<template>
  <div>
    <input type="file" @change="handleFileChange" />
    
    <div v-if="file">
      <p>파일명: {{ file.name }}</p>
      <p>파일 크기: {{ (file.size / 1024).toFixed(2) }} KB</p>
      <p>파일 타입: {{ file.type }}</p>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const file = ref(null)

function handleFileChange(event) {
  const selected = event.target.files[0]
  file.value = selected || null
}
</script>
매서드  
vs 인라인 핸들러
매서드핸들러  <button @click="handleClick">Click</button>
methods: {
  handleClick(event) {
    console.log(event);
  }
}
* event 는 데이터로 넘겨줌
인라인핸들러 js 코드 그대로 사용, 변수, 함수 호출시 this 필요없음

<input @change="id = $event.target.value" />
<button @click="sayHello('Vue')">Say Hello</button>
<button @click="isActive ? doSomething() : doSomethingElse()">Click</button>
<button @click="(event) => handleClick(event)">Use Arrow Function</button>
이벤트 수식어

modifier
디렉티브에 붙는 특수한 동작 제어자
@이벤트명.수식어1.수식어2="함수"

1. 수식어 
.capture 캡처링 단계에서 실행 <div @click.capture="capture">...</div>

2. 동시실행
<button @click.prevent.stop="handleClick">전파X & 이동X</button>

3.  prevent, stop 은 사용후 키확인
.prevent   <button @click.prevent.ctrl="handleClick">...✔</button>
.stop <button @click.stop.ctrl="handleClick">...✔</button>

참고 : v-model 의 수식어는 사용자가 만들어등록
.prevent  <a href="#" @click.prevent="go">링크</a>

기본 동작 막음 
event.preventDefault(); 동일
ex) a , form 태그
.self <div @click.self="selfOnly">...</div> 자기 자신에게만 실행 
- 자식요소도 막음(캡쳐링도 막음)

if (event.target === event.currentTarget) {
selfOnly(); // 클릭된 요소가 바로 이 div일 때만 실행
} 동일
.stop <div @click.stop="stopClick">...</div> 이벤트 전파 막음 

event.stopPropagation(); 동일
- 자식요소는 확성화
.passive <div @scroll.passive="handleScroll">...</div>
 스크롤 성능에 중요한 이벤트  wheel, touchmove
<div @wheel.passive="handleWheel" />
<div @touchmove.passive="handleTouchMove" />

handleScroll 안에서 event.preventDefault() 안 쓸게 = 스크롤 법벅임 방지

element.addEventListener('scroll', handleScroll, { passive: true })  // 스크롤 버벅임방지


cf) wheel 마우스 휠 입력
touchmove 모바일 스크롤 최적화
.once  <button @click.once="init">...</button> 한 번만 실행 - 일회용
키보드
수식어

시스템
수식어
<button @click.ctrl="ctrlClick"> 클릭 +ctrl키</button>
<button @keydown.ctrl. shift.alt ="ctrlClick">Ctrl and shift and alt  키</button>
<button 
  @keydown.ctrl="onKey"
  @keydown. shift ="onKey"
  @keydown.alt="onKey"
> Ctrl or shift or alt  </button>
<input @keydown.enter="submitForm" />
<div @keydown.left="moveLeft" @keydown.right="moveRight"></div>
<div @keydown.esc="closeModal"></div>
1. event.key 기반
2. event.code 로 키 확인가능
3. .은 and 조건
각각 선언시 or 조건. 

.ctrl Ctrl 키가 눌린 상태에서 @keydown.ctrl
.shift Shift 키가 눌린 상태에서 @keydown.shift
.alt Alt 키가 눌린 상태에서 @keydown.alt
.meta ⌘ Command (Mac), ⊞ Win (Windows)
.enter Enter 키
.tab Tab 키
.delete Delete 및 Backspace 키
.esc / .escape Escape 키
.space Spacebar (스페이스)
.up, .down, .left, .right 방향키
.home, .end, .pageup, .pagedown 탐색 키
.insert Insert 키
.exact  <button @click.ctrl.exact="handle">✔ 권장</button> *(순서주의)키보드 키가 단독으로 눌렀을때
.exact 는 키확인후 사용 
v-model

<template>
  <div>
    <h2>부모 컴포넌트</h2>
    <!-- v-model:message 사용 + 수식어처럼 `.capitalize` -->
    <MyInput v-model:message.capitalize="text" />
    <p>입력된 값: {{ text }}</p>
  </div>
</template>

<script>
import MyInput from './components/MyInput.vue'

export default {
  components: { MyInput },
  data() {
    return {
      text: ''
    }
  }
}
</script>
-------------------------------------------------------------------
<template>
  <div>
    <input :value="modelValue" @input="handleInput" />
  </div>
</template>

<script>
export default {
  props: {
    modelValue: String,
    modelModifiers: {
      type: Object,
      default: () => ({})
    }
  },
  emits: ['update:modelValue'],
  methods: {
    handleInput(event) {
      let value = event.target.value
      if (this.modelModifiers.capitalize) {
        value = value.charAt(0).toUpperCase() + value.slice(1)
      }
      this.$emit('update:modelValue', value)
    }
  }
}
</script>


v-model
부모 -자식 컴포넌트 사용 시 
이벤트 수식어와 다르게(해당 수식어는 자동으로 있는 것이 맵핑 되는게 아니라)새로 작성해야함