Vue 컴포넌트 간 데이터 전달 및 메서드 호출 완벽 가이드

서론 Vue 프로젝트에서 컴포넌트 간의 데이터 전달과 메서드 호출은 빈번하게 필요한 기능이다. Vuex 상태 관리를 사용하여 모든 데이터 전달 문제를 해결할 수도 있지만, 상황에 따라 적절한 방법을 선택하는 것이 중요하다. 이 글에서는 Vue에서 사용할 수 있는 다양한 컴포넌트 통신 방식을 상세히 다룬다.

컴포넌트 통신 방식의 분류

  1. 부모 → 자식 (props)
  2. 자식 → 부모 ($emit)
  3. 자식이 부모 메서드 호출 (props)
  4. 부모가 자식 데이터/메서드 참조 ($refs)
  5. 자식이 부모 데이터/메서드 참조 ($parent)
  6. 이벤트 버스를 통한 비父子 컴포넌트 통신
  7. Vuex를 통한 상태 관리

상세 구현 예제

  1. 부모 컴포넌트 → 자식 컴포넌트 데이터 전달

부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달하려면 props를 사용한다.

<template>
    <div>
        부모 컴포넌트:
        <input type="text" v-model="userName">
        <child-component :userName="userName"></child-component>
    </div>
</template>
<script>
import ChildComponent from "./ChildComponent.vue";
export default {
  components: {
    ChildComponent
  },
  data() {
    return {
      userName: ""
    };
  }
};
</script>

자식 컴포넌트:

<template>
    <div>
        자식 컴포넌트:
        <span>{{userName}}</span>
    </div>
</template>
<script>
export default {
  props: {
    userName: {
      type: String,
      required: true
    }
  }
};
</script>

1-1. Props 값이 업데이트되지 않는 문제 해결

부모 컴포넌트에서 전달하는 값이 변경되어도 자식 컴포넌트에서 반영되지 않는 문제가 발생할 수 있다. 이 경우 중간 변수를 사용하고 watch로 감시하여 해결한다.

<template>
    <input type="text" v-model="localValue" @input="handleInput" />
</template>
<script>
export default {
    data() {
        return {
            localValue: this.inputValue,
            additionalData: {}
        };
    },
    props: {
        inputValue: {
            type: String
        }
    },
    watch: {
        inputValue(newValue) {
            this.localValue = newValue;
        },
        additionalData: {
            handler(val) {
                // 처리 로직
            },
            deep: true
        }
    },
    methods: {
        handleInput() {
            this.$emit("update:inputValue", this.localValue);
        }
    }
};
</script>
  1. 자식 컴포넌트 → 부모 컴포넌트 데이터 전달 ($emit)

자식 컴포넌트에서 부모 컴포넌트로 데이터를 전달하거나 부모의 메서드를 호출하려면 $emit을 사용한다.

부모 컴포넌트:

<template>
    <div>
        부모 컴포넌트:
        <span>{{receivedData}}</span>
        <child-component @sendData="handleChildData"></child-component>
    </div>
</template>
<script>
import ChildComponent from "./ChildComponent.vue";
export default {
  components: {
    ChildComponent
  },
  data() {
    return {
      receivedData: ""
    };
  },
  methods: {
    handleChildData: function(data) {
      this.receivedData = data;
    }
  }
};
</script>

자식 컴포넌트:

<template>
    <div>
        자식 컴포넌트:
        <span>{{childData}}</span>
        <input type="button" value="데이터 전송" @click="sendToParent">
    </div>
</template>
<script>
export default {
  data() {
    return {
      childData: "자식 컴포넌트의 데이터"
    };
  },
  methods: {
    sendToParent() {
      this.$emit("sendData", this.childData);
    }
  }
};
</script>
  1. 자식 컴포넌트에서 부모 메서드 직접 호출

props를 통해 부모의 함수를 전달받아 직접 호출할 수 있다.

부모 컴포넌트:

<template>
    <editor-component :onSave="saveContent"></editor-component>
</template>
<script>
export default {
  methods: {
    saveContent: function (content) {
      alert("저장된 내용: " + content);
    }
  }
}
</script>

자식 컴포넌트:

<template>
    <button @click="handleSave">저장</button>
</template>
<script>
export default {
  props: {
    onSave: {
      type: Function,
      default: null
    }
  },
  methods: {
    handleSave: function () {
      if (this.onSave) {
        this.onSave("부모에게 전달할 데이터");
      }
    }
  }
}
</script>
  1. 부모가 자식 컴포넌트의 메서드 및 데이터 참조

$refs를 사용하면 부모 컴포넌트에서 자식 컴포넌트의 데이터와 메서드에 직접 접근할 수 있다.

<!-- 자식 컴포넌트에 ref属性 추가 -->
<child-component ref="childRef"></child-component>
<script>
export default {
  methods: {
    accessChild() {
      // 자식 컴포넌트의 데이터 접근
      console.log(this.$refs.childRef.childData);
      
      // 자식 컴포넌트의 메서드 호출
      this.$refs.childRef.childMethod();
    }
  }
};
</script>
  1. 자식이 부모 컴포넌트의 메서드 및 데이터 참조

$parent를 사용하여 부모 컴포넌트에 접근할 수 있다.多层嵌套 구조에서는 this.$parent.$parent 형태로 연속 접근이 가능하다.

<script>
export default {
  methods: {
    accessParent() {
      console.log(this.$parent.parentData);
      this.$parent.parentMethod();
    }
  }
};
</script>

참고: 이 방법은 부모 컴포넌트의 구조를 명확히 알고 있어야 하므로, 컴포넌트 중첩이 깊어질 경우 사용을 권장하지 않는다.

  1. 비父子 컴포넌트 간 통신

父子 관계가 아닌 컴포넌트 간 통신은 다음과 같은 방법으로 구현할 수 있다:

방법 1: 공통 부모 컴포넌트 활용 두 컴포넌트가 동일한 부모를 공유하는 경우, 부모 컴포넌트의 data를 통해 데이터를 중개할 수 있다.

방법 2: Event Bus (이벤트 버스) Vue 2.0에서 사용할 수 있는 Publish-Subscribe 패턴이다. 이벤트 버스를 전역으로 설정하여 사용한다.

// main.js
window.eventBus = new Vue();

컴포넌트 A (데이터 수신):

<template>
    <div>
        A 컴포넌트:
        <input type="button" value="증가" @click="increment">
        <span>{{counter}}</span>
    </div>
</template>
<script>
export default {
    data() {
        return {
            counter: 0
        };
    },
    methods: {
        increment() {
            this.counter++;
        },
        displayMessage() {
            console.log("A 컴포넌트 메서드 호출됨");
        }
    },
    mounted() {
        const vm = this;
        eventBus.$on("updateCounter", function(data) {
            console.log("수신된 데이터:", data);
            vm.counter = data;
            vm.displayMessage();
        });
    }
};
</script>

컴포넌트 B (데이터 전송):

<template>
    <div>
        B 컴포넌트:
        <span>{{currentValue}}</span>
        <input type="button" value="전송" @click="sendToComponentA">
    </div>
</template>
<script>
export default {
    data() {
        return {
            currentValue: 10
        };
    },
    methods: {
        sendToComponentA() {
            eventBus.$emit("updateCounter", this.currentValue);
        }
    }
};
</script>
  1. Vuex를 통한 상태 관리

복잡한 애플리케이션에서는 Vuex를 사용하여 중앙 집중식 상태 관리를 수행하는 것이 효과적이다.

<template>
    <div>
        <button @click="updateValue">값 변경</button>
        <p>Store 값: {{this.$store.state.sharedData}}</p>
        <p>Getter 값: {{computedData}}</p>
    </div>
</template>

<script>
export default {
    data() {
        return {};
    },
    computed: {
        computedData() {
            return this.$store.getters.getSharedData;
        }
    },
    watch: {
        computedData(newValue, oldValue) {
            console.log("변경된 값:", newValue);
        }
    },
    methods: {
        updateValue() {
            this.$store.commit("updateSharedData", this.$store.state.sharedData + 1);
        }
    }
};
</script>

총정리

Vue 컴포넌트 간 통신 방식은 다음과 같이 정리할 수 있다:

  • props: 부모 → 자식 데이터 전달
  • $emit: 자식 → 부모 데이터 전달 및 이벤트 발생
  • props (함수): 자식이 부모 메서드 호출
  • $refs: 부모가 자식 컴포넌트 직접 참조
  • $parent: 자식이 부모 컴포넌트 직접 참조
  • Event Bus: 비父子 컴포넌트 간 통신
  • Vuex: 전역 상태 관리

각 방식은 고유한 사용 시나리오가 있으므로, 프로젝트의 규모와 구조에 맞게 적절한 방법을 선택해야 한다.

태그: vue props emit component-communication eventbus

7월 3일 22:29에 게시됨