๐Ÿ” ํ”„๋กœ๊ทธ๋ž˜๋จธ์Šค FE ๋ฐ๋ธŒ์ฝ”์Šค 5๊ธฐ TIL = template | v-if | v-show | event capture & passive | v-model | component

Jun 2k (Jun2)ยท2023๋…„ 11์›” 22์ผ
1
post-thumbnail

2023.11.24 ๊ฐ•์˜

๐Ÿ’ป Intro & TMI

์ ์  Vue๋ฉฐ๋“ค์–ด๊ฐ€๊ณ  ์žˆ๋‹ค.
์žฌ๋ฐŒ๋‹ค. ์ฝ”๋”ฉ ๊ณต๋ถ€๊ฐ€! ์ด๋Ÿฌ๋ฉด ๋ฏธ์นœ๋†ˆ ๊ฐ™์ง€๋งŒ ์งœ๋ฆฟํ•ด...!
์‹ค์งˆ์ ์ธ ๋ฆฌ์ŠคํŠธ๋‚˜ ์ด๋ฒคํŠธ ๊ตฌํ˜„ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ๋ฐฐ์šฐ๋ฉด์„œ Vue์™€ ํ•œ์ธต ๋” ๊ฐ€๊นŒ์›Œ์ง€๊ณ  ์žˆ๋Š” ๊ฒƒ ๊ฐ™๋‹ค.

๊ฐ™์ด ์•ผ๊ทผํ•˜๋Š” ์ธ์›์ด๋‚˜ 2์ฐจํŒ€ ๊ทธ์™ธ ๋‹ค๋ฅธ ํ”„๋กฑ์ด๋ถ„๋“ค๊ณผ๋„ ์ ์  ์นœํ•ด์ง€๊ณ  ์žˆ๋‹ค. ๋‹ค๋“ค ๋จผ์ € ๋‹ค๊ฐ€์™€์ฃผ์‹œ๊ธฐ๋„ ํ•˜๊ณ  ๋‚ด๊ฐ€ ๋จผ์ € ๋‹ค๊ฐ€๊ฐ€๊ธฐ๋„ ํ•œ๋‹ค! ์ข‹์€ ํ˜„์ƒ์ด๋‹ค!



๐Ÿง ์˜ค๋Š˜ ์ƒˆ๋กญ๊ฒŒ ๋ฐฐ์šด ๊ฒƒ

v-if ๋””๋ ‰๋ฆฌ๋ธŒ ์กฐ๊ฑด๋ฌธ์—์„œ template ํƒœ๊ทธ ์‚ฌ์šฉ

v-if ๋””๋ ‰๋ฆฌ๋ธŒ๋ฅผ ํ†ตํ•ด HTML ์š”์†Œ์˜ ๋ Œ๋”๋ง ์—ฌ๋ถ€๋ฅผ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋‹ค.
์‹ค์ œ if๋ฌธ ์ฒ˜๋Ÿผ v-else-if, v-else๋„ ๊ฐ€๋Šฅํ•œ๋ฐ ํ•œ ๊ฐ€์ง€ ์ฃผ์˜ํ•  ์ ์ด ์žˆ๋‹ค.
์กฐ๊ฑด๋ฌธ์ด ๋ถ€์—ฌ๋œ ์š”์†Œ ์‚ฌ์ด์—๋Š” ์กฐ๊ฑด๋ฌธ์ด ๋ถ€์—ฌ๋˜์ง€ ์•Š์€ HTML ์š”์†Œ๊ฐ€ ๋ผ์–ด๋“ค๋ฉด ์•ˆ๋œ๋‹ค. (๋‚„๋‚„๋น ๋น ...)

<div id="app">
  <h1 v-if="isShow">Hello Vue!</h1>
  <h2>๋‚„๋‚„๋น ๋น  ๋ชป ํ•˜๋Š” ์• </h2> // ์ด ์š”์†Œ๋กœ ์ธํ•ด ์•„๋ž˜์˜ v-else์™€ v-else-if๋Š” ๋™์ž‘ํ•˜์ง€ ์•Š๋Š”๋‹ค.
  <h2 v-else-if="0">Application..</h2>
  <h2 v-else>Good Morning~</h2>
</div>
<script>
  const App = {
  	data() {
  		return {
  			isShow: null,
  		};
  	},
  };
  // ...
</script>

๋˜ํ•œ ์กฐ๊ฑด๋ถ€ ๋ Œ๋”๋ง ์‹œ ํ•˜๋‚˜์˜ ์กฐ๊ฑด์œผ๋กœ ์—ฌ๋Ÿฌ ์š”์†Œ๋ฅผ ๋ฌถ์Œ์œผ๋กœ ์ œ์–ดํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์กฐ๊ฑด๋ฌธ์ด ๋ถ€์—ฌ๋œ ๋ถ€๋ชจ์š”์†Œ๋กœ ๊ฐ์‹ธ์•ผ ํ•œ๋‹ค.

<div id="app">
  <h1 v-if="isShow">Hello Vue!</h1>
  // ์ด๋ ‡๊ฒŒ div๋กœ h2, p, span ํƒœ๊ทธ๋ฅผ ๋ฌถ์–ด์„œ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋‹ค.
  <div v-else-if="[]">
    <h2>Application..</h2>
    <p>1234</p>
    <span>985</span>
  </div>
  <h2 v-else>Good Morning~</h2>
</div>
<script>
  const App = {
    data() {
      return {
        isShow: null,
      };
    },
  };

  const vm = Vue.createApp(App).mount('#app');
</script>


ํ•˜์ง€๋งŒ ์ด๋ ‡๊ฒŒ ๊ทธ๋ƒฅ div ํƒœ๊ทธ์™€ ๊ฐ™์ด ๊ธฐ์กด ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด ๋ Œ๋”๋ง ์‹œ ํ•ด๋‹น ์š”์†Œ๊ฐ€ ๋‚จ์•„์žˆ๊ฒŒ ๋œ๋‹ค. ์ด๊ฒƒ์„ ์˜๋„ํ–ˆ๋‹ค๋ฉด ์ƒ๊ด€์—†์ง€๋งŒ ์ˆจ๊ธฐ๊ณ  ์‹ถ๋‹ค๋ฉด template ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

<div id="app">
  <h1 v-if="isShow">Hello Vue!</h1>
  // template ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ Œ๋”๋ง๋˜์ง€ ์•Š๋Š”๋‹ค.
  <template v-else-if="[]">
    <h2>Application..</h2>
    <p>1234</p>
    <span>985</span>
  </template>
  <h2 v-else>Good Morning~</h2>
</div>



v-if์™€ v-show ๋น„๊ต

๊ฐ™์•„๋ณด์ด์ง€๋งŒ ๋‹ค๋ฅธ v-if์™€ v-show ๋””๋ ‰ํ‹ฐ๋ธŒ๋ฅผ ์•Œ์•„๋ณด์ž.
๋‘ ๋””๋ ‰ํ‹ฐ๋ธŒ ๋ชจ๋‘ HTML ์š”์†Œ๋ฅผ ์‚ฌ์šฉ์ž UX์ ์ธ ๊ด€์ ์—์„œ ์กฐ๊ฑด๋ถ€๋กœ ๋ณด์—ฌ์ฃผ๋Š” ์—ฌ๋ถ€๋ฅผ ์ œ์–ดํ•˜๋Š” ๋””๋ ‰ํ‹ฐ๋ธŒ์ด๋‹ค.

ํ•˜์ง€๋งŒ v-if๋Š” lazy, ๊ฒŒ์„๋Ÿฌ์„œ ์กฐ๊ฑด์ด ๋งŒ์กฑํ•˜์ง€ ์•Š์œผ๋ฉด ๋ Œ๋”๋ง์กฐ์ฐจ ํ•˜์ง€ ์•Š๋Š”๋‹ค.
์ด์— ๋ฐ˜ํ•ด v-show๋Š” ์ผ๋‹จ ๋ Œ๋”๋ง์€ ํ•˜๋˜ ์กฐ๊ฑด์ด ๋งŒ์กฑํ•˜์ง€ ์•Š์œผ๋ฉด
ํ•ด๋‹น HTML ์š”์†Œ์— display: none; css ์†์„ฑ์„ ์ถ”๊ฐ€ํ•ด์„œ ์ˆจ๊ธฐ๊ฒŒ ๋งŒ๋“ ๋‹ค.

๋”ฐ๋ผ์„œ v-if๋Š” ์ดˆ๊ธฐ ๋ Œ๋”๋ง ๋น„์šฉ์ด ๋‚ฎ์ง€๋งŒ ํ† ๊ธ€๊ณผ ๊ฐ™์€ ์ „ํ™˜ ๋น„์šฉ์ด ๋†’๊ณ ,
๋ฐ˜๋Œ€๋กœ v-show๋Š” ์ดˆ๊ธฐ ๋ Œ๋”๋ง ๋น„์šฉ์„ ๋†’์ง€๋งŒ ์ „ํ™˜ ๋น„์šฉ์ด ๋‚ฎ์œผ๋ฏ€๋กœ ์žฅ๋‹จ์ ์ด ์ƒ๋ฐ˜๋œ๋‹ค.

์–ด๋–ค ๊ฑธ ๋ฌด์กฐ๊ฑด ์‚ฌ์šฉํ•ด๋ผ ์ด๋Ÿฐ ๊ฒƒ์€ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๊ธฐ๋ณธ์ ์œผ๋กœ๋Š” ์ดˆ๊ธฐ ๋ Œ๋”๋ง ๋น„์šฉ์„ ๋‚ฎ์ถ”๋Š” ๊ฒƒ์ด ์ข‹๊ธฐ์— v-if๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ•˜์ง€๋งŒ ํ† ๊ธ€๋กœ ์ธํ•œ ์š”์†Œ ๋ Œ๋”๋ง ์ „ํ™˜ ํšจ๊ณผ๊ฐ€ ๋งŽ์€ ์›นํŽ˜์ด์ง€์ผ ๊ฒฝ์šฐ์—๋Š” v-show๋ฅผ ๋„์ž…ํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค. ๋ญ๋“  ์ƒํ™ฉ์— ๋งž๊ฒŒ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

  • v-show๋Š” v-cloak๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค
    v-show ๋””๋ ‰ํ‹ฐ๋ธŒ์˜ ๋˜ ๋‹ค๋ฅธ ์น˜๋ช…์ ์ธ(?) ๋‹จ์ ์ด ์žˆ๋‹ค. ์ผ๋‹จ ๋ Œ๋”๋ง์€ ๋˜๋Š” ํŠน์„ฑ ๋•Œ๋ฌธ์— ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง ๋ฐฉ์‹์˜ ์›นํŽ˜์ด์ง€์—์„œ ์•„๋ž˜์™€ ๊ฐ™์€ ์ฝ”๋“œ์—์„œ {{ msg }}์˜ msg ๋ฐ์ดํ„ฐ๊ฐ€ ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ๋ถˆ๋Ÿฌ์™€์ง€์ง€ ์•Š์œผ๋ฉด ํ•ด๋‹น ์ด์ค‘ ์ค‘๊ด„ํ˜ธ ๊ตฌ๋ฌธ์ด ๊ทธ๋Œ€๋กœ ๋…ธ์ถœ๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.
    ๋”ฐ๋ผ์„œ ์ด๋ฅผ ๋Œ€๋น„ํ•ด v-cloak ๋””๋ ‰ํ‹ฐ๋ธŒ์™€ css๋กœ [v-cloak] ์„ ํƒ์ž๋ฅผ ํ†ตํ•ด ํ•ด๋‹น ๋””๋ ‰ํ‹ฐ๋ธŒ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์š”์†Œ์— ๋Œ€ํ•ด์„œ display: none;์„ ์ ์šฉํ•ด์ค€๋‹ค.
    ์ด๋ ‡๊ฒŒ ๋˜๋ฉด v-show ๋””๋ ‰ํ‹ฐ๋ธŒ ์ž์‹ ์š”์†Œ ๋‚ด ๋ฐ์ดํ„ฐ๊ฐ€ ๋ถˆ๋Ÿฌ์™€์ง€๊ธฐ ์ „๊นŒ์ง„ v-cloak์ด ๋ถ€์—ฌ๋˜์–ด์„œ ์ˆจ๊ฒจ์ ธ ์žˆ๋‹ค๊ฐ€ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐ”์ธ๋”ฉ๋จ๊ณผ ๋™์‹œ์— ๋‹ค์‹œ ๋ณด์—ฌ์ง€๊ฒŒ ๋œ๋‹ค.
<style>
  [v-cloak] {
    display: none;
  }
</style>
<div id="app">
  <button @click="toggle">Toggle!</button>
  <h1 v-show="isShow" v-cloak>{{ msg }}</h1>
</div>


Vue v-for ๋””๋ ‰ํ‹ฐ๋ธŒ list ์ƒ์„ฑ ์‹œ id ์ง€์ • ์ด์œ 

React์—์„œ๋„ ๋ฐ˜๋ณต๋ฌธ์„ ํ†ตํ•ด li ํƒœ๊ทธ๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ๊ฐ๊ฐ์˜ ์š”์†Œ์— ๊ณ ์œ ํ•œ id๋ฅผ ์ œ๊ณตํ•ด์•ผ ํ–ˆ๋‹ค. ์ด๋Š” ๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ ๊ฒƒ์ด ์•„๋‹Œ React๊ฐ€ ๊ฐ ์š”์†Œ๋ฅผ ๊ตฌ๋ณ„ํ•˜๊ธฐ ์œ„ํ•ด์„œ์ด๋‹ค. (๋„ˆ ์ข‹์œผ๋ผ๊ณ  id ๋ถ™์ด๋ผ๋Š”๊ฑฐ ์•„๋‹ˆ์•ผ~)

Vue์—์„œ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์˜€๋‹ค.
์•„๋ž˜ ์ฝ”๋“œ๋Š” todo ๋ฆฌ์ŠคํŠธ๋ฅผ ๋งŒ๋“œ๋Š” ์˜ˆ์‹œ์ด๋‹ค.
todos ๋ฐฐ์—ด์— ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋“ค์„ ๋ฆฌ์ŠคํŠธ ํ˜•ํƒœ๋กœ ์ถœ๋ ฅํ•˜๊ธฐ ์œ„ํ•ด
v-for ๋””๋ ‰ํ‹ฐ๋ธŒ๋กœ ๊ฐ๊ฐ์˜ todo ๋ฐ์ดํ„ฐ๋“ค์„ ์ถ”์ถœํ•˜์—ฌ ์ƒ์„ฑํ•œ๋‹ค.

<ul>
  <li
    v-for="todo in todos"
    :key="todo.id">
    {{ todo.title }}
  </li>
</ul>

์ด ๋•Œ :key๋ฅผ todo.id ๋ฐ์ดํ„ฐ๋กœ ๋ฐ”์ธ๋”ฉํ•˜์—ฌ ๊ณ ์œ ํ•œ id๋ฅผ ์ œ๊ณตํ•ด์ฃผ์–ด์•ผ Vue๊ฐ€ ๊ฐ๊ฐ์˜ todo li ์š”์†Œ๋“ค์„ ์‹๋ณ„ํ•˜๊ณ  ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด๋‹ค.

๋”ฐ๋ผ์„œ v-for ๋””๋ ‰ํ‹ฐ๋ธŒ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฐฐ์—ด์ด๋‚˜ ๊ฐ์ฒด์˜ ์š”์†Œ๋ฅผ ๋”ฐ๋กœ ์ƒ์„ฑํ•  ๋–„๋Š” :key๋ฅผ ํ†ตํ•ด id๋ฅผ ๊ผญ ๋ฐ”์ธ๋”ฉํ•ด์ฃผ๋Š” ๊ฒƒ์„ ์Šต๊ด€ํ™”ํ•ด์•ผ ํ•œ๋‹ค.



์ด๋ฒคํŠธ ์บก์ณ

๋‚œ ์ด ๋•Œ๊นŒ์ง€ addEventListener์—๋Š” ์ธ์ˆ˜๊ฐ€ ๋‘ ๊ฐœ๋ฟ์ธ ์ค„ ์•Œ์•˜๋‹ค.(๊ณต์‹๋ฌธ์„œ ๊ผผ๊ผผํžˆ ์ฝ์ž..)
์•„๋ž˜ ์ฝ”๋“œ์™€ ๊ฐ™์ด ์ด๋ฒคํŠธ๋ช…, ์ฝœ๋ฐฑํ•จ์ˆ˜๋งŒ ์‚ฌ์šฉํ•ด์™”์—ˆ๊ณ  ๊ทธ๋Ÿฐ์ค„๋งŒ ์•Œ์•˜๋‹ค.

window.addEventListener('click', () => {})


ํ•˜์ง€๋งŒ MDN ๊ณต์‹๋ฌธ์„œ๋ฅผ ์ฐพ์•„๋ณด๋‹ˆ ๋–กํ•˜๋‹ˆ ์„ธ ๋ฒˆ์งธ ์˜ต์…˜ ์ธ์ˆ˜์— Capture ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๋‚ด์šฉ์ด ์žˆ์—ˆ๋‹ค.
์ด๋ฒˆ Vue ๊ฐ•์˜์—์„œ capture ์ด๋ฒคํŠธ ์ˆ˜์‹์–ด๋ฅผ ๋ฐฐ์šฐ๋ฉด์„œ ๋‹ค์‹œ๊ธˆ ์•Œ์•˜๋‹ค.

Capture ์˜ต์…˜์€ ์ด๋ฒคํŠธ ๋ฒ„๋ธ”๋ง์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•œ JS์—์„œ์˜ stopPropagation() ๋ฉ”์„œ๋“œ๋‚˜ Vue์—์„œ์˜ stop ์ด๋ฒคํŠธ ์ˆ˜์‹์–ด์™€๋Š” ๋ฐ˜๋Œ€์˜ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

๋ง ๊ทธ๋Œ€๋กœ ์ตœ์ƒ์œ„ ์š”์†Œ์—์„œ๋ถ€ํ„ฐ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•œ ํƒ€๊ฒŸ๊นŒ์ง€ ์ด๋ฒคํŠธ๊ฐ€ ์ „๋‹ฌ๋  ๋•Œ capture ์˜ต์…˜์ด ์ง€์ •๋œ ์ด๋ฒคํŠธ์— ๋Œ€ํ•ด์„œ ์บก์ณํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค.

์•„๋ž˜ ์ฝ”๋“œ์—์„œ๋Š” child ํด๋ž˜์Šค์˜ div๋Š” stop ์ˆ˜์‹์–ด๋ฅผ ํ†ตํ•ด ์ƒ์œ„ ์š”์†Œ๋กœ์˜ ์ด๋ฒคํŠธ ๋ฒ„๋ธ”๋ง์„ ๋ง‰์•˜๊ณ 
parent ํด๋ž˜์Šค div๋Š” capture ์ˆ˜์‹์–ด๋ฅผ ํ†ตํ•ด child ํด๋ž˜์Šค div๋ฅผ ํด๋ฆญํ–ˆ์„ ๋•Œ window๊ฐ์ฒด -> parent -> child ์ˆœ์œผ๋กœ ์ „๋‹ฌ๋˜์–ด ๋‚ด๋ ค์˜ค๋Š” ์ค‘๊ฐ„์— ์ด๋ฒคํŠธ๊ฐ€ ์บก์ณ๋œ๋‹ค.

๋”ฐ๋ผ์„œ child ํด๋ž˜์Šค div๋ฅผ ํด๋ฆญํ•˜๋ฉด ์บก์ณ๋œ parent ํด๋ž˜์Šค div์˜ ํด๋ฆญ ์ด๋ฒคํŠธ๊ฐ€ ๋จผ์ € ์ถœ๋ ฅ๋˜๊ณ  ์ดํ›„์— child ํด๋ž˜์Šค div์˜ ํด๋ฆญ ์ด๋ฒคํŠธ๊ฐ€ ์ถœ๋ ฅ๋œ ํ›„์— ์ดํ›„ ์ด๋ฒคํŠธ ๋ฒ„๋ธ”๋ง์„ ์ผ์–ด๋‚˜์ง€ ์•Š๋Š”๋‹ค.

<div id="app">
  <div class="parent" @click.capture="log">
    <div class="child" @click.stop="log"></div>
  </div>
</div>

<script>
  const App = {
    methods: {
      log(event) {
        console.log(event.currentTarget);
      },
    },
  };
  // ...
</script>

์ƒ์œ„ ์š”์†Œ์—์„œ ํŠน์ • ์ด๋ฒคํŠธ๋ฅผ ์ถ”์ถœํ•ด์™€์•ผ ํ•˜๋Š” ์ƒํ™ฉ์ด ๋“œ๋ฌผ๊ฒ ์ง€๋งŒ ํ•„์š”ํ•  ๊ฒฝ์šฐ๊ฐ€ ๋ฐ˜๋“œ์‹œ ์žˆ์„ํ…Œ๋‹ˆ ์ด๋ฒˆ ๊ธฐํšŒ์— ์•Œ๊ฒŒ๋˜์–ด ์ข‹์•˜๋‹ค.



์ด๋ฒคํŠธ passive ์ˆ˜์‹์–ด

passive ๋˜ํ•œ capture์™€ ๊ฐ™์ด addEventListener์˜ ์„ธ ๋ฒˆ์งธ ์˜ต์…˜ ์ค‘ ํ•˜๋‚˜์˜€๋‹ค.

passive ์ˆ˜์‹์–ด๋ฅผ ์ด๋ฒคํŠธ์— ๋ถ€์—ฌํ•˜๋ฉด ํ™”๋ฉด์˜ ๋ Œ๋”๋ง๊ณผ ์ด๋ฒคํŠธ ๋‚ด๋ถ€ ๋กœ์ง์˜ ๋™์ž‘์„ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“ค์–ด ์ค€๋‹ค.

์•„๋ž˜๋Š” ๋ถ€๋ชจ์— ์ƒ๊ธด ์Šคํฌ๋กค์— wheel ์ด๋ฒคํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ๊ฑฐ๊ธฐ์— passive ์ˆ˜์‹์–ด๋ฅผ ์ถ”๊ฐ€ํ•œ ์ฝ”๋“œ์ด๋‹ค.
์ด๋ ‡๊ฒŒ ๋˜๋ฉด ์ด๋ฒคํŠธ์˜ ์ฝœ๋ฐฑ ํ•จ์ˆ˜ log์˜ ๋™์ž‘๊ณผ ๋ธŒ๋ผ์šฐ์ € ์ƒ์˜ ์Šคํฌ๋กค์„ ๋‚ด๋ฆฌ๋Š” ๋™์ž‘์€ ๋ณ„๊ฐœ๋กœ ์ง„ํ–‰๋œ๋‹ค. ์‹ค์ œ๋กœ log ํ•จ์ˆ˜๋กœ ์ธํ•ด i๊ฐ€ 1~100๊นŒ์ง€ ์ถœ๋ ฅ๋˜์ง€๋งŒ ๋ฒ„๋ฒ…๋Œ€์ง€ ์•Š๊ณ  ์Šคํฌ๋กค์€ ๋งค๋„๋Ÿฝ๊ฒŒ ๊ตฌํ˜„๋œ๋‹ค.

<div id="app">
  <div class="parent" @wheel.passive="log">
    <div v-for="n in 15" class="child">{{ n }}</div>
  </div>
</div>
<script>
  const App = {
    methods: {
      log() {
        for (let i = 0; i < 100; i += 1) {
          console.log(i);
        }
      },
    },
  };

  const vm = Vue.createApp(App).mount('#app');
</script>


์–‘๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ์„ ์œ„ํ•œ v-model

์ด์ค‘ ์ค‘๊ด„ํ˜ธ ๊ตฌ๋ฌธ์œผ๋กœ๋Š” data๋ฅผ HTML ์š”์†Œ์— ๋ Œ๋”๋งํ•˜์—ฌ ๋‹จ๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ๋ฐ–์— ๋˜์ง€ ์•Š๋Š”๋‹ค.

๋ธŒ๋ผ์šฐ์ €์—์„œ input, select ํƒœ๊ทธ๋‚˜ input ํƒœ๊ทธ ์•ˆ์—์„œ radio, checkbox type๋“ค์„ ํ†ตํ•ด์„œ ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅ ๋ฐ ์„ ํƒํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ์–‘๋ฐฉํ–ฅ์œผ๋กœ ์—ฐ๋™์‹œํ‚ฌ ํ•„์š”์„ฑ์ด ์žˆ๋‹ค.

์ด ๋•Œ v-bind์™€ @ ์ด๋ฒคํŠธ๋ฅผ ํ†ตํ•ด ์–‘๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ์„ ๊ตฌํ˜„ํ•  ์ˆ˜๋Š” ์žˆ์ง€๋งŒ ์ฝ”๋“œ๊ฐ€ ๊ธธ์–ด์ง„๋‹ค.
์ด๊ฒƒ์„ ๋ทฐ์—์„œ๋Š” v-model๋กœ ์••์ถ•ํ•˜์—ฌ ๊ฐ€๋Šฅํ•˜๋‹ค!!

<div id="app">
  <input v-model="msg" />
  <h1>{{ msg }}</h1>
</div>
<script>
  const App = {
    data() {
      return {
        msg: 'Hello Vue!',
      };
    },
  };
  // ...
</script>

์•„์ฃผ ๊ธฐ๋ณธ์ ์ธ v-model ํ™œ์šฉ ์ฝ”๋“œ์ด๋‹ค.
input ํƒœ๊ทธ์— ์ž…๋ ฅํ•œ msg ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”๋กœ h1ํƒœ๊ทธ์— ์ด์ค‘ ์ค‘๊ด„ํ˜ธ๋ฌธ์„ ํ†ตํ•ด ์—ฐ๋™ํ•ด์„œ ์ฆ‰๊ฐ์ ์œผ๋กœ ๋ Œ๋”๋ง์ด ๋œ๋‹ค.

v-model์€ ํƒœ๊ทธ ์ข…๋ฅ˜๋‚˜ type ์ข…๋ฅ˜์— ๋”ฐ๋ผ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ˆ˜์‹์–ด๊ฐ€ ๋งŽ์ด ์กด์žฌํ•œ๋‹ค. ๊ฐ๊ฐ์˜ ๊ฒฝ์šฐ์— ์ ์šฉํ•ด์ฃผ์–ด์•ผ ํ•˜๋Š” ์ˆ˜์‹์–ด๊ฐ€ ๋‚˜๋ฆ„ ์ •ํ•ด์ ธ ์žˆ๋Š”๋ฐ ์ด๊ฑด ๊ทธ๋•Œ ๊ทธ๋•Œ ์ฐพ์•„๋ณด๋ฉด์„œ ์ ์šฉํ•˜๋ฉด์„œ ์ตํ˜€๋„ ๋  ๋“ฏ ํ•˜๋‹ค.

์ค‘์š”ํ•œ ๊ฒƒ์€ ์–‘๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ์„ ์งง๊ฒŒ ๋”ฑ! ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ!



์ปดํฌ๋„ŒํŠธ ๊ฐ„ ๋ฐ์ดํ„ฐ ๊บผ๋‚ด๊ณ  ๋‚ด๋ณด๋‚ด๊ธฐ

์˜ค๋Š˜์€ ์ง€์—ญ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ๋Œ€๋žต์ ์œผ๋กœ ๋ฐฐ์› ๋‹ค.

๊ทธ ์ค‘ ์ปดํฌ๋„ŒํŠธ ๋‚ด์™ธ๋ถ€์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋‚ด๋ณด๋‚ด๊ณ  ๋“ค์—ฌ๋„ฃ๋Š” ๋ฐฉ๋ฒ•์ด ์„œ๋กœ ๋‹ค๋ฅธ ์ ์ด ์ค‘์š”ํ–ˆ๋‹ค.
์ปดํฌ๋„ŒํŠธ ์™ธ๋ถ€์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฐธ์กฐํ•ด์„œ ์“ฐ๊ธฐ ์œ„ํ•ด์„œ๋Š” React์—์„œ์™€ ๋™์ผํ•˜๊ฒŒ props ์†์„ฑ์„ ์ด์šฉํ–ˆ๋‹ค.

์•„๋ž˜ ์ฝ”๋“œ์—์„œ๋Š” upper-name ์ง€์—ญ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ตฌํ˜„ํ•œ ๊ฒƒ์ด๋‹ค.

<div id="app">
  <upper-name
    v-for="fruit in fruits"
    :key="fruit.id"
    :name="fruit.name"
    @to-upper="toUpper(fruit, $event)"
  ></upper-name>
</div>

<script>
  // ...
  app.component('upper-name', {
    template: `
    <div @click="capitalize">{{ name }}</div>
    `,
    props: ['name'],
    methods: {
      capitalize() {
        // this.name = this.name.toUpperCase();
        this.$emit('to-upper', this.name.toUpperCase());
      },
    },
  });
  // ...
</script>

component ์™ธ๋ถ€ ๋ฐ์ดํ„ฐ์ธ fruit.name์„ name์ด๋ž€ props๋กœ ์ „๋‹ฌ๋ฐ›๋Š”๋‹ค.
component ๋‚ด์—์„œ props ์†์„ฑ์„ ํ†ตํ•ด ๋ฐฐ์—ด ์•ˆ ์š”์†Œ์— ์ด๋ฆ„์„ ์ž์œ ๋กญ๊ฒŒ ์„ค์ •ํ•˜๊ณ 
HTML ์ƒ component์—์„œ ํ•ด๋‹น ์ด๋ฆ„์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”์ธ๋”ฉํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

๋ฐ˜๋Œ€๋กœ component ๋‚ด๋ถ€์—์„œ ๋Œ€๋ฌธ์ž๋กœ ๋ฐ”๊พผ this.name.toUpperCase() ๋ฐ์ดํ„ฐ๋ฅผ ์™ธ๋ถ€๋กœ ๋‚ด๋ณด๋‚ด๊ธฐ ์œ„ํ•ด์„œ๋Š” $emit์„ ์‚ฌ์šฉํ•ด์„œ ์ฒซ ๋ฒˆ์งธ ์ธ์ž๋กœ ์ปค์Šคํ…€์ด๋ฒคํŠธ ์ด๋ฆ„, ๋‘ ๋ฒˆ์งธ ์ธ์ž๋กœ ๋‚ด๋ณด๋‚ผ ๋ฐ์ดํ„ฐ๋ฅผ ์ง€์ •ํ•˜๋ฉด ๋œ๋‹ค.

๋™์‹œ์— HTML์ƒ component์—์„œ @๋ฅผ ํ†ตํ•ด ์ปค์Šคํ…€์ด๋ฒคํŠธ๋ฅผ ์ง€์ •ํ•œ ๋’ค ๋„˜๊ฒจ์ค„ ๋ฉ”์„œ๋“œ์˜ ์ธ์ž๋กœ $event๋กœ ๋„˜๊ฒจ์ฃผ๋ฉด ๋œ๋‹ค!

๋‹ค์†Œ ๋ณต์žกํ–ˆ์ง€๋งŒ ์ปดํฌ๋„ŒํŠธ๊ฐ„ ๋ฐ์ดํ„ฐ ์ฐธ์กฐ ๋ฐฉ์‹์€ ํ•„์ˆ˜์ ์œผ๋กœ ์•Œ๊ณ  ์žˆ์–ด์•ผ ์ดํ›„ ์ปดํฌ๋„ŒํŠธ ๊ตฌ์กฐ๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ ๊ตฌํ˜„ํ•  ๋•Œ ์ž์œ ์ž์žฌ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๊ธฐ์— ์ž˜ ์•Œ์•„๋‘ฌ์•ผ๊ฒ ๋‹ค.



๐Ÿ‘€ ๋А๋‚€์ 

๐Ÿ‘ Keep

https://www.youtube.com/watch?v=24_iVKNWy2E
์ด ์˜์ƒ ๊ฐ•์˜์—์„œ์ฒ˜๋Ÿผ ์šธํŠธ๋ผ ๋Ÿฌ๋‹์„ ํ•˜๋ ค๊ณ  ํ•ญ์ƒ ๋…ธ๋ ฅ ์ค‘์ด๋‹ค.
์ƒ๊ฐ๋ณด๋‹ค ์ด๋•Œ๊นŒ์ง€ TIL๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ๊ณ„์† ์ฃผ๋„์ ์œผ๋กœ ์ฐพ์•„๋ณด๊ณ  ํ•˜๋Š” ๊ฒƒ์ด ์ด๋ฏธ ๋‚ด๊ฐ€ ์šธํŠธ๋ผ ๋Ÿฌ๋‹์Šค๋Ÿฌ์šด ํ•™์Šต์„ ํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ์ด์—ˆ๋‹ค! ์ฝ”๋”ฉ ๊ณต๋ถ€๋Š” ๋†€์ด์ด๋‹ค!! ๊ทธ๋ ‡๊ฒŒ ์ƒ๊ฐํ•˜๊ณ  ๊ฐ€๋ณด์ž๊ณ !

๐Ÿ˜ฑ Problem

๊ทธ๋งŒํผ ๊ฑด๊ฐ• ๊ด€๋ฆฌ๊ฐ€ ์ค‘์š”ํ•˜๋‹ค. ์˜ค๋Š˜ ์•„์นจ์—๋„ ์ปจ๋””์…˜์ด ์•ˆ ์ข‹์•„์„œ ๋ง๊ฒ”์„ ๋งž์•˜๋‹ค. ์šธํŠธ๋ผ ๋Ÿฌ๋‹๋„ ๋ชธ์ด ๋˜์•ผ ํ•˜์ง€.. ์ž ์€ ์ค„์ด์ง€ ๋ง๊ณ  ๋ˆˆ ๋– ์žˆ์„ ๋•Œ ์ตœ๋Œ€ํ•œ ํšจ์œจ์„ ๋ณด์ž!

๐Ÿ˜œ Try

Vue๋ฅผ ๋ฐฐ์šฐ๋ฉด์„œ ์ž˜ ์งœ์—ฌ์ ธ ์žˆ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ๋ผ๋Š” ๋А๋‚Œ์„ ๊ณ„์† ๋ฐ›๋Š”๋‹ค. ๋Œ€์„ธ๋Š” React์ด์ง€๋งŒ ์–ด๋–ค๋ฉด์—์„œ๋Š” Vue๋กœ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ๋“ค๋„ ๋งŽ์€ ๊ฒƒ ๊ฐ™๋‹ค. Vue๋กœ ํ† ์ด ํ”„๋กœ์ ํŠธ ๊ผญ ํ•ด๋ด์•ผ๊ฒ ๋‹ค.



๐Ÿ˜… ํ•ด๋‹น ๋‚ด์šฉ์€ ๊ณต๋ถ€ํ•˜๋ฉด์„œ ์ •๋ฆฌํ•œ ๊ธ€์ž…๋‹ˆ๋‹ค. ํ‹€๋ฆฐ ๋ถ€๋ถ„์ด๋‚˜ ์˜คํ•ดํ•˜๊ณ  ์žˆ๋Š” ๋ถ€๋ถ„์ด ์žˆ๋‹ค๋ฉด ํ”ผ๋“œ๋ฐฑ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

profile
ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž ์ค€๋น„์ค‘...

0๊ฐœ์˜ ๋Œ“๊ธ€