๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐ŸŒต Vue.js

[Vue.js] props์™€ emit๋ฅผ ์ด์šฉํ•ด ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์™€ ์ž์‹ ์ปดํฌ๋„ŒํŠธ ์‚ฌ์ด์— ๋ฐ์ดํ„ฐ ์ „๋‹ฌํ•˜๊ธฐ.

by ._.sori 2025. 9. 5.

 

 

์ฐธ๊ณ ๋กœ Vue.js์— ๋Œ€ํ•ด์„œ ์ž˜ ๋ชจ๋ฆ…๋‹ˆ๋‹ค.

์•„๋ฌด๊ฑฐ๋‚˜ ๋ง‰ ํ•ด๋ณด๋Š” ์ค‘์ด๊ณ  ์˜ณ์€ ์ •๋ณด์ธ์ง€ ์ €๋„ ๋ชจ๋ฆ…๋‹ˆ๋‹ค!

 

 

 

props์™€ emit๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ
๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์™€ ์ž์‹ ์ปดํฌ๋„ŒํŠธ ์‚ฌ์ด์—
๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•ด๋ณด์ž.

 

 

 

 

 

 

props

  • ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ž์‹ ์ปดํฌ๋„ŒํŠธ์—๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.
  • ๋ช…์‹œ์ ์ธ props ์„ ์–ธ์ด ํ•„์š”ํ•˜๋‹ค.
  • <script setup>์„ ์‚ฌ์šฉํ•  ๋•Œ๋Š” defineProps() ๋งคํฌ๋กœ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ props๋ฅผ ์„ ์–ธํ•œ๋‹ค.
  • <script setup>์ด ์•„๋‹Œ ์ปดํฌ๋„ŒํŠธ์˜ ๊ฒฝ์šฐ props ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ์„ ์–ธํ•œ๋‹ค.
  • ์„ ์–ธ ๋ฐ ํ…œํ”Œ๋ฆฟ ์ฐธ์กฐ ์‹œ camelCase๋กœ ์‚ฌ์šฉํ•œ๋‹ค.
  • ์ž์‹ ์ปดํฌ๋„ŒํŠธ ์ „๋‹ฌ ์‹œ kebab-case๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. (HTML ์†์„ฑ ํ‘œ๊ธฐ๋ฒ•)

 

 

defineProps์˜ ์ฝ”๋“œ ์˜ˆ์‹œ _ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•œ ์„ ์–ธ(๊ถŒ์žฅ)

<script setup>

const { msg, count } = defineProps({
  msg:   { type: String, required: true },
  count: { type: Number, default: 0 },
})

</script>

 

 

 

defineProps์˜ ์ฝ”๋“œ ์˜ˆ์‹œ _ ๋ฌธ์ž์—ด ๋ฐฐ์—ด์„ ์‚ฌ์šฉํ•œ ์„ ์–ธ

<script setup>
defineProps(['myMsg'])
</script>

 

 

 

props์˜ ์ฝ”๋“œ ์˜ˆ์‹œ _ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•œ ์„ ์–ธ(๊ถŒ์žฅ)

<script>

  props: {
    msg:   { type: String, required: true },
    count: { type: Number, default: 0 },
  }
  
</script>

 

 

 

 

 


 

 

 

 

 

emit

  • $emit()๋Š” ์ž์‹ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒํ‚ค์…” ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ์—ญํ•  ๋ฉ”์„œ๋“œ์ด๋‹ค.
  • ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ์ปค์Šคํ…€ ์ด๋ฒคํŠธ๋ฅผ ์„ ์–ธํ•œ๋‹ค.
  • ๋ฐœ์ƒ์‹œํ‚ค๋Š” ์ด๋ฒคํŠธ๋Š” ๋‘ ๊ฐ€์ง€ ํ˜•ํƒœ๋กœ ์„ ์–ธ ๊ฐ€๋Šฅํ•˜๋‹ค.
  • ๋ฌธ์ž์—ด ๋ฐฐ์—ด์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฐ„๋‹จํ•œ ํ˜•ํƒœ
  • ๊ฐ ์†์„ฑ ํ‚ค๊ฐ€ ์ด๋ฒคํŠธ ์ด๋ฆ„์ด๊ณ  ๊ฐ’์ด null ๋˜๋Š” ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ํ•จ์ˆ˜์ธ ๊ฐ์ฒด ํ˜•ํƒœ

 

 

emit ์ฝ”๋“œ ์˜ˆ์‹œ

์ž์‹ ์ปดํฌ๋„ŒํŠธ

<script>
  methods: {
    showText() {
      this.$emit('show', 'lightpink')
    }
</script>

<template>
    <button @click="showText()" class="c-btn"> ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ ์ƒ‰์ƒ ๋ฐ”๊พธ๊ธฐ </button>
</template>
  ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ
  
  methods: {
    show(color) {
      this.backColor = color;
    },

 

 

 

 


 

 

 

 

๊ฐ„๋‹จํ•˜๊ฒŒ ํ”„๋กœ์ ํŠธ ๋งŒ๋“ค๊ธฐ

  • ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์ž์‹ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋“ฑ์žฅํ•œ๋‹ค. (props ์‚ฌ์šฉ)
  • ์ž์‹ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์˜ ๋ฐฐ๊ฒฝ์ƒ‰์ด ํ•‘ํฌ์ƒ‰์œผ๋กœ ๋ฐ”๋€๋‹ค. (emit ์‚ฌ์šฉ)

 

 

ํ™”๋ฉด

 

 

 

 


 

 

 

 

App.vue ์ฝ”๋“œ

  • template ์ฝ”๋“œ
<template>
<div class="app" :style="{backgroundColor: backColor}">
  <button class="p-btn" @click="child"> ์ž์‹ ์ปดํฌ๋„ŒํŠธ ๋‚˜ํƒ€๋‚˜๊ธฐ </button>
  <ChildComponent :open="open" @show="show"/>
</div>
</template>
:style์€ v-bind:์˜ ์ถ•์•ฝํ˜•์œผ๋กœ v-bind:style์ด ์•„๋‹Œ, :style๋กœ ๋ฐ”๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. v-bind๋Š” ํ•˜๋‚˜ ์ด์ƒ์˜ ์†์„ฑ ๋˜๋Š” ์ปดํฌ๋„ŒํŠธ prop์„ ํ‘œํ˜„์‹์— ๋™์ ์œผ๋กœ ๋ฐ”์ธ๋”ฉํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ :style์€ ์†์„ฑ์„ ๋ฐ”์ธ๋”ฉํ•  ๋•Œ์ธ, ์†์„ฑ์„ ๋‚ด๋ ค๋ณด๋‚ผ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.(DOM์œผ๋กœ ๋‚ด๋ ค๋ณด๋ƒ„)

์ž์‹ ์ปดํฌ๋„ŒํŠธ๋Š” emit์œผ๋กœ ์–ด๋–ค ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰์‹œํ‚ฌ์ง€์™€ ์ปฌ๋Ÿฌ๋ช…์„ ์ „๋‹ฌํ•  ๊ฒƒ์ด๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์—์„œ show๋ผ๋Š” ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋œ๋‹ค. show๋ผ๋Š” ๋ฉ”์„œ๋“œ๋Š” ์ปฌ๋Ÿฌ๋ฅผ ๋ฐ›์•„ backColor๋ฅผ ๋ฐ”๊พผ๋‹ค.  ๊ทธ๋Ÿฌ๋ฉด :style์€ ๋ฐ˜์‘ํ˜•์ด๊ธฐ ๋•Œ๋ฌธ์—  backColor๋ฅผ backgroundColor์— ๋„ฃ์–ด ์šฐ๋ฆฌ๊ฐ€ ๋‹ค์Œ์— ์ž‘์„ฑํ•  style๋ณด๋‹ค ๋” ๋จผ์ € DOM์— ์˜ฌ๋ผ๊ฐ„๋‹ค.

๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์— ๋ฒ„ํŠผ์„ ๋งŒ๋“ค๊ณ  ๊ทธ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์ž์‹ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋‚˜ํƒ€๋‚˜๋„๋ก ํ•  ๊ฒƒ์ด๋‹ค. p-btn ๋ฒ„ํŠผ์„ ๋งŒ๋“ค๊ณ  ๊ทธ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด child๋ผ๋Š” ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋˜๋„๋ก ํ–ˆ๋‹ค. 

์ž์‹ ์ปดํฌ๋„ŒํŠธ ์ด๋ฆ„์€ ChildComponent์ด๋‹ค. open์ด๋ž€ ๋ฐ์ดํ„ฐ๋Š” script์—์„œ false๋กœ ๊ธฐ๋ณธ๊ฐ’์„ ์„ค์ •ํ•  ๊ฒƒ์ด๋‹ค. ๊ทธ๋ž˜์„œ ์ฒ˜์Œ ํ™”๋ฉด์— ๋“ค์–ด๊ฐ€๋ฉด ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋Š” ๋ณด์ด์ง€ ์•Š์„ ๊ฒƒ์ด๋‹ค. p-btn์„ ๋ˆ„๋ฅด๋ฉด child ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋˜๋„๋ก ๋งŒ๋“ค์—ˆ๋Š”๋ฐ, ์ด child ๋ฉ”์„œ๋“œ๋Š” open๊ฐ’์„ ๋ฐ˜๋Œ€๋กœ ๋ฐ”๊ฟ”์ฃผ๋„๋ก ๋งŒ๋“ค ๊ฒƒ์ด๋‹ค. ๊ทธ๋ž˜์„œ p-btn์„ ๋ˆ„๋ฅด๋ฉด false ๊ฐ’์ด๋˜ open์ด true๋กœ ๋ฐ”๋€๋‹ค.

๊ทธ๋Ÿฌ๋ฉด ์ด๋Ÿฐ open ๊ฐ’์„ ChildComponent์— ๋ณด๋‚ด์•ผํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ์ด๋ฒˆ์—” :open์„ ์‚ฌ์šฉํ–ˆ๋‹ค. ์•„๊นŒ ์ฒ˜์Œ ์„ค๋ช…ํ–ˆ์„ ๋•Œ, v-bind๋Š” ํ•˜๋‚˜ ์ด์ƒ์˜ ์†์„ฑ ๋˜๋Š” ์ปดํฌ๋„ŒํŠธ prop์„ ํ‘œํ˜„์‹์— ๋™์ ์œผ๋กœ ๋ฐ”์ธ๋”ฉํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ํ–ˆ๋‹ค. :open์€ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋กœ ๋‚ด๋ ค๋ณด๋‚ด์„œ props๋ฅผ ํ†ตํ•ด ํ•ด๋‹น ๊ฐ’์„ ๋ฐ›๊ฒŒ ํ• ๊ฑฐ๋‹ˆ๊นŒ, ์ด๋ฒˆ์—” prop์„ ํ‘œํ˜„์‹์— ๋™์ ์œผ๋กœ ๋ฐ”์ธ๋”ฉํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ–ˆ๋‹ค.

@show๋Š” v-on์„ ์‚ฌ์šฉํ•œ ๊ฒƒ์œผ๋กœ, ๊ฐ’์„ ์˜ฌ๋ ค๋ณด๋‚ด๋Š” ์—ญํ• ์„ ํ•œ๋‹ค. @v-on:eventName="ํ•ธ๋“ค๋Ÿฌ"๋กœ ํ‘œ๊ธฐํ•˜๋ฉฐ, ์ถ•์•ฝํ˜•์€ @eventName="ํ•ธ๋“ค๋Ÿฌ"๊ฐ€ ๋œ๋‹ค. ์ž์‹ ์ปดํฌ๋„ŒํŠธ์—์„œ emit์œผ๋กœ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์— ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๊ธฐ์— v-on์„ ์“ด๋‹ค.

 

 

  • script ์ฝ”๋“œ
<script>
import ChildComponent from "@/components/ChildComponent.vue";

export default {
  name: "App",
  components: {ChildComponent},
  data() {
    return {open:false, backColor:'lemonchiffon'}
  },
  methods: {
    show(color) {
      this.backColor = color;
    },
    child() {
      this.open = !this.open;
    }
  }
}
</script>
App.vue์— ChildComponent๊ฐ€ ๋ณด์—ฌ์•ผํ•˜๋‹ˆ๊นŒ, import ํ•ด์ค€๋‹ค.

data์— open ๊ฐ’์„ false๋กœ ์„ค์ •ํ•ด์ฃผ๊ณ , backColor์— ์›ํ•˜๋Š” ์ปฌ๋Ÿฌ๋ฅผ ์ž…๋ ฅํ•œ๋‹ค.

์ด์ œ ๋ฉ”์„œ๋“œ๋ฅผ ์„ค์ •ํ•  ๊ฒƒ์ด๋‹ค. ๋ฉ”์„œ๋“œ show( )๋Š” color๋ฅผ ๋ฐ›์•„์™€ backColor๋ฅผ ๋ณ€๊ฒฝํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด lemonchiffon์ด๋˜ ์ปฌ๋Ÿฌ๊ฐ€ ๋ฐ”๋€” ๊ฒƒ์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  :style์„ ํ†ตํ•ด ๋ฐฐ๊ฒฝ์ปฌ๋Ÿฌ๊ฐ€ ๋ณ€๊ฒฝ๋œ๋‹ค. 

๋ฉ”์„œ๋“œ child( )๋Š” p-btn ๋ฒ„ํŠผ์ด ํด๋ฆญ๋  ๋•Œ ๋งˆ๋‹ค ๊ฐ’์ด ๋ฐ˜์ „๋œ๋‹ค. false -> true๋กœ true -> false๋กœ ๋ณ€๊ฒฝ๋œ๋‹ค. ์ด ๋ฐ”๋€ ๊ฐ’์€ ChildComponent๋กœ ๋„˜์–ด๊ฐ„๋‹ค.

 

 

  • sytle ์ฝ”๋“œ
<style>
.app {
  position: relative;
  width: 100vw;
  height: 100vh;
}

.p-btn {
  position: absolute;
  width: 30%;
  height: 10%;
  top: 50%;
  left: 75%;
  transform: translate(-50%, -50%);
  text-align: center;
  font-size: 1.1rem;
  z-index: 2;
  cursor: pointer;
}
</style>

 

 

 


 

 

 

ChildComponent.vue ์ฝ”๋“œ

  • template ์ฝ”๋“œ
<template>
  <div class="child" :class="{open:open}">
    <button @click="changeColor()" class="c-btn"> ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ ์ƒ‰์ƒ ๋ฐ”๊พธ๊ธฐ </button>
  </div>
</template>
child ํด๋ž˜์Šค๊ฐ€ ๊ฐ€์žฅ ์ƒ์œ„์— ์žˆ๋Š” ํด๋ž˜์Šค๋กœ ChildComponent์˜ ์ „์ฒด ํ™”๋ฉด์„ ์ด์•ผ๊ธฐํ•œ๋‹ค. ์ด ์ „์ฒด ํ™”๋ฉด์€ open์˜ ๊ฐ’์ด true๊ฐ€ ๋˜๋ฉด ๋ณด์ด๋„๋ก ํ•˜๊ณ , false๋ฉด ๋ณด์ด์ง€ ์•Š๋„๋กํ•œ๋‹ค. script์—์„œ .open์„ ํ†ตํ•ด ํ™”๋ฉด์˜ ์œ„์น˜๋ฅผ ์กฐ์ •ํ–ˆ๋‹ค. false์ด๋ฉด ํ™”๋ฉด ๋ฐ”๊นฅ์— ์œ„์น˜ํ•˜๊ณ  true๋ฉด .child.open์— ์„ค์ •ํ•œ ๊ฐ’์œผ๋กœ ์ธํ•ด ํ™”๋ฉด ์•ˆ์ชฝ์— ๋ณด์ด๋„๋ก ํ–ˆ๋‹ค.

c-btn์„ ๋งŒ๋“ค๊ณ  ์ด ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด changeColor ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋˜๋„๋ก ํ–ˆ๋‹ค.

 

 

  • script ์ฝ”๋“œ
<script>
export default {
  name: 'ChildComponent',
  props: {
    open: {
      type: Boolean,
      required: true,
    },
  },
  methods: {
    changeColor() {
      this.$emit('show', 'lightpink')
    }
  },
}
</script>
๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ฐ›์€ open ๊ฐ’์„ props๋กœ ๋ฐ›์•˜๋‹ค. open์˜ ๊ฐ’์€ boolean ํ˜•ํƒœ์—ฌ์•ผํ•˜๊ณ , true๊ฐ€ ๋˜์–ด์•ผ open์ด ์‹คํ–‰๋˜๋„๋ก ํ–ˆ๋‹ค.

๋ฉ”์„œ๋“œ changeColor๋Š” $emit์œผ๋กœ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์—๊ฒŒ show๋ผ๋Š” ์ปค์Šคํ…€ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœํ–‰ํ•˜๊ณ  lightpink๊ฐ€ ์ฒซ๋ฒˆ์งธ ์ธ์ž๋กœ ์ „๋‹ฌ๋œ๋‹ค. ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์— ChildComponent @show="show"๋ฅผ ์ ์—ˆ์—ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ๋ถ€๋ชจ๊ฐ€ ์ด ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœํ–‰๋œ ๊ฑธ ์•Œ๊ณ  ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์‹คํ–‰์‹œํ‚จ๋‹ค.

 

 

  • style ์ฝ”๋“œ
<style>
.child {
  position: relative;
  width: 50vw;
  height: 100vh;
  top: 0;
  right:0;
  background-color: cornflowerblue;
  transform: translateX(-100%);
  transition: transform 0.4s cubic-bezier(.71,1.7,.58,.98);
}

// open์— true๊ฐ€ ์˜ค๋ฉด transform ๊ฐ’์ด ๋ฐ”๋€๋‹ค.
.child.open {
  transform: translateX(0%);
}

.c-btn {
  position: absolute;
  width: 60%;
  height: 10%;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  text-align: center;
  font-size: 1.1rem;
  cursor: pointer;
}

</style>

 

 

  • ์ฃผ์˜ํ•˜๊ธฐ

๋งŒ์•ฝ์— ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์™€ ์ž์‹ ์ปดํฌ๋„ŒํŠธ์˜ ๋ฒ„ํŠผ ์ด๋ฆ„์ด ๊ฐ™์„ ๊ฒฝ์šฐ ๋ฒ„ํŠผ์ด ์ž‘๋™ํ•˜์ง€ ์•Š์œผ๋‹ˆ ๊ผญ ๋ฒ„ํŠผ์˜ ์ด๋ฆ„์„ ๋‹ค๋ฅด๊ฒŒ ์„ค์ •ํ•ด์ฃผ์ž!