Vue.js 3 & NodeJS/사내교육 - 점심 메뉴

[10] Frontend 개발 - 상태 관리

carrotweb 2026. 8. 4. 17:03
728x90
반응형

상태 관리

여러 하위 컴포넌트들이 동일한 데이터를 사용해야 할 경우
→ 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달해야 할 때 props를 사용합니다.

 

  • 깊은 계층 구조를 가진 컴포넌트 트리에서 비효율적입니다. → Prop Drillin

 

Prop Drilling (프랍(프롭) 드릴링)

자식 컴포넌트는 부모 컴포넌트로부터 데이터를 전달 받기 위해 <script>에 props(프랍스, property, 프라퍼티)에 속성 명과 타입을 설정합니다.

부모 컴포넌트는 <template>에서 자식 HTML 태그 안에 넘겨줄 prop을 설정합니다.

 

RootComp.vue

<template>
    <h1>Hello, Vue!</h1>
    <FooterComp userID="testid" />
</template>

<script>
import FooterComp from './FooterComp.vue'

export default {
    name : 'RootComp',
    components : {
        FooterComp
    }
}
</script>

 

FooterComp.vue

<template>
    <DeepChildComp :userID=useID />
</template>

<script>
import DeepChildComp from './DeepChildComp.vue'

export default {
    name : 'FooterComp',
    components : {
        DeepChildComp
    },
    props: ['userID']
}
</script>

Footer에는 사용하지 않지만 자식 컴포넌트인 DeepChild가 사용하기 때문에 prop를 설정해야 합니다.

 

DeepChildComp.vue

<template>
    <h2>{{ userID }}, Have a good time</h2>
</template>

<script>
export default {
    name : 'DeepChildComp',
    props: ['userID']
}
</script>

 

Provide(제공) / Inject(주입) 사용 ← Prop Drilling 해결

상위 컴포넌트는 하위 모든 컴포넌트에 대한 의존성 제공자 역할을 할 수 있습니다.

하위 모든 컴포넌트는 깊이에 관계없이 상위 체인의 컴포넌트에서 제공(provide)하는 의존성을 주입(inject)할 수 있습니다.

 

  • 하위 모든 컴포넌트에 데이터를 제공하려면 provide() 함수를 사용
  • 상위 컴포넌트에서 제공하는 데이터를 주입하려면 inject() 함수를 사용

 

하위 모든 컴포넌트에 데이터를 제공하기 위해서 provide() 함수 또는 provide 옵션을 사용합니다.

import { provide } from 'vue'

export default {
  setup() {
    provide('message', '안녕!')
  }
}

 

상위 컴포넌트에서 제공하는 데이터를 주입하려면 inject() 함수 또는 inject 옵션을 사용합니다.

import { inject } from 'vue'

export default {
  setup() {
    const message = inject('message')
    return {
      message
    }
  }
}

 

Composition API 대신 Options API로 하면 개발하면 다음과 같습니다.

export default {
  provide: {
    message: '안녕!'
  }
}
export default {
  inject: ['message'],
  data() {
    return {
      fullMessage: this.message
    }
  }
}

 

 

Provide / Inject 사용

1. src\main.js에 provide(프로바이드, 제공) 함수를 추가합니다. → App-level Provide

import { createApp } from 'vue';
import { ref } from 'vue';
import App from './App.vue';
import router from './router';

const app = createApp(App);

app.provide('appTitle', '점심 메뉴 앱');

const greetingMessage = ref('즐거운 점심 시간되세요!');
const likeCount = ref(0);

function likeUp() {
    this.likeCount++;
}

app.provide('useCommonStore', {
    greetingMessage,
    likeCount,
    likeUp
});

app.use(router).mount('#app');
  • Provide할 때 반응형 데이터와 상태 변경에 대한 함수가 같이 있는 것이 좋습니다.
  • "use"로 시작해 "Store"로 끝나는 Store의 이름을 사용하는 것이 좋습니다.

 

2. src\views\LunchMenu\LunchMenuView.vue에 provide(프로바이드, 제공) 함수를 추가합니다. → Component Provide

<template>
    <div class="lunch-menu">점심 메뉴</div>
    <div class="lunch-view">
        <router-view />
    </div>
</template>

<script>
import { provide } from 'vue';

export default {
    setup() {
        provide('lunchmenuTitle', '점심 메뉴’);
    }
}
</script>

 

3. src\views\LunchMenu\LunchMenuListView.vue에 Injext(인젝트, 주입) 함수를 추가하고 사용합니다.

<template>
    <div>{{ appTitle }} - {{ lunchmenuTitle }} 리스트</div>
    <div>
        <span>{{ greetingMessage }} </span>
        <button @click="likeUp">좋아요 - {{ likeCount }}</button>
    </div>
    :
</template>

<script>
:
import { inject } from 'vue';

export default {
    setup() {
        :
        const appTitle = inject('appTitle');
        const { greetingMessage, likeCount, likeUp } = inject('useCommonStore');
        const lunchmenuTitle = inject('lunchmenuTitle');
        :
        return {
            :
            appTitle,
            greetingMessage,
            likeCount,
            likeUp,
            lunchmenuTitle
        }
    },
    :
}
</script>

 

4. 웹 브라우저에서 확인하기 (http://127.0.0.1:8080/lunchmenu/list)

"좋아요"버튼을 누르면 카운트가 증가합니다.

 

 

반응형 객체로 상태 관리

 

상태 관리
(https://ko.vuejs.org/guide/scaling-up/state-management.html)

 

1. src에 stores 디렉터리를 생성하고 useCountStore.js 파일을 생성합니다.

import { reactive } from 'vue';

export const useCountStore = reactive({
    // 카운트
    count: 0,
    // 카운트를 증가시킵니다.
    increment() {
        this.count++;
    },
    // 카운트를 감소시킵니다.
    decrement() {
        this.count--;
    },
    // 카운트를 랜덤으로 증가시킵니다.
    incrementByRandom() {
        const random = Math.floor(Math.random() * 5);
        this.count += random;
    }
});

reactive()를 사용하여 반응형 객체를 만듭니다.

반응형 데이터와 상태 변경에 대한 함수가 같이 있는 것이 좋습니다.

 

2. src\views\LunchMenu\LunchMenuView.vue에 useCountStore을 추가하고 사용합니다.

<template>
    <div class="lunch-menu">점심 메뉴
        <button @click="countStore.incrementByRandom()">{{ countStore.count }}</button>
    </div>
    <div class="lunch-view">
        <router-view />
    </div>
</template>

<script>
import { provide } from 'vue';
import { useCountStore } from '@/stores/useCountStore.js'

export default {
    setup() {
        provide('lunchmenuTitle', '점심 메뉴');

        const countStore = useCountStore;

        return {
            countStore
        }
    }
}
</script>

:

 

3. src\views\LunchMenu\LunchMenuListView.vue에 useCountStore을 추가하고 사용합니다.

<template>
    <div>{{ appTitle }} - {{ lunchmenuTitle }} 리스트
        <button @click="countStore.incrementByRandom()">{{ countStore.count }}</button>
    </div>
    :
</template>

<script>
:
import { useCountStore } from '@/stores/useCountStore.js'

export default {
    setup() {
        :
        const countStore = useCountStore;

        return {
            :
            countStore
        }
    },
    :
}
</script>

 

4. 웹 브라우저에서 확인하기 (http://127.0.0.1:8080/lunchmenu/list)

728x90
반응형