HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
📝
학습 TIL
/
📝
43일차 배운 것 정리
📝

43일차 배운 것 정리

대주제
Vue
작성완료
작성완료
전날 정리 노트 이동
다음 정리 노트 이동
주제
vuex
날짜
May 18, 2022

목차

목차1. Vue 6화 (day39)1.플러그인 (14m, 2배속 필기최소화)1-1. 플러그인 정의1-2. 플러그인 사용1-3. fetch 예제2.믹스인 (14m + 32m, 2배속 필기최소화)2.1 믹스인 기본2.2 믹스인 심화3. Teleport (42m, 생략, 추후 듣기)4. Provide, inject (16m, 2배속 필기최소화)5. Vuex(Store) (26m + 29m + 33m, 2배속 필기최소화)5.1 Vuex 도입 전 핵심 개념 구현해보기5.2 Vuex 문서 살펴보기5.3 vuex 모듈 방식으로 사용하기 (여러개의 store)

1. Vue 6화 (day39)

1.플러그인 (14m, 2배속 필기최소화)

전역에서 사용가능한 기능(함수)들을 정의하는 기능 install() 속성을 통해 정의할 수 있다.

1-1. 플러그인 정의

  • install 속성 내부에서 app.config.globalProperties.플러그인함수이름 =(()⇒{}) 으로 정의
    • install속성은 기본적으로 두 개의 인자를 받는데, 전역 app과 options로 사용한다. (install(app, options){ ... })
 

1-2. 플러그인 사용

  • 전역 App에 등록하기
    • 정의한 플러그인을 import 하여, main.js에서 app.use() 메소드로 mount전에 등록하여 준다.
      • app.use(fetcjPlugin)
  • 특정 컴포넌트에서 사용하기
    • 정의할 때 사용한 플러그인 함수이름을 통해, 전역컴포넌트에서 해당 함수를 사용할 수 있다.
      • this.$fetch("https://jsonplaceholder.typicode.com/todos/1")
  • options 설정가능
    • app.use를 통해 전역에 등록할 때 2번째 인자를 객체형태로 하여 option을 지정해줄 수 있다.
      • pluginName : 함수의 이름 바꾸기
      코드예시
       

1-3. fetch 예제

코드보기
  • fetch.js 에서 플러그인 정의하기
  • main.js에서 등록하고 App.vue에서 사용하기

2.믹스인 (14m + 32m, 2배속 필기최소화)

재사용 가능한 기능 단위를 의미 mixin으로 공통된 기능을 만들고, 컴포넌트가 mixin을 등록하여 여러 컴포넌트에서 사용가능하도록 함

2.1 믹스인 기본

  • 믹스인은 기본적으로 옵션 단위로 가지고 있음
    • data() , methods() , created(), mounted() 등...
  • 옵션병합
    • 컴포넌트의 옵션과 mixin의 옵션이 중복되는 경우 설정이 가능 (default는 컴포넌트 옵션 우선)
      • 라이프사이클 훅 같은 경우는 덮어씌어지는 것이 아니라 병합되어, 둘 다 발생하게 된다!
      • methods() 는 컴포넌트 우선으로 병합되어짐
 
  • 전역 믹스인 (필기 생략- 문서참고)
 

2.2 믹스인 심화

설문조사 form 선택하여 제출하는 예제
  • 여러 컴포넌트에서 공통적인 옵션을 사용하고 있을 때 mixin을 통해 간략화 하기
    • input의 양방향 바인딩 연습
    • textField와 RadioSelector의 컴포넌트화
      • 공통 옵션인 props(modelValue, title, items), emits 의 mixin화
    • index.js를 통한 import 가독성 증대

3. Teleport (42m, 생략, 추후 듣기)

 

4. Provide, inject (16m, 2배속 필기최소화)

React의 contextAPI 와 비슷한 개념으로, 중첩된 구조에서 하위컴포넌트와 부모컴포넌트의 데이터 교환을 위한 옵션
주의 기본 형식으로 사용한다면, inject된 data는 반응성을 가지지 않는다.
  • computed와 함께 사용하여, 반응성을 가지도록 한다.
  • computed는 computed 객체를 리턴하므로, 사용시에는 computed.value를 통해 사용해야한다
  • 중첩이 깊은 특수한 경우의 사용
  • provide는 하나의 옵션속성으로 data를 리턴하는 방식으로 사용 (export 비슷한 개념)
  • inject는 data를 받아 명시한 후 바로 사용
  • provide-inject는 단순 장거리props의 역할만 하고, emit을 통한 자식→부모 수정이 안되기 때문에, 이를 해결하는 전역스토어인 Vuex 라이브러리를 주로 사용한다.
 

5. Vuex(Store) (26m + 29m + 33m, 2배속 필기최소화)

redux와 같이 전역에 상태를 정의하고, 관리하도록 하는 뷰의 상태관리 라이브러리

5.1 Vuex 도입 전 핵심 개념 구현해보기

필요성
  • 조손요소에서 부모요소로 data를 전달하고 싶을 때
  • 여러 곳에서 변화가 발생하는 state(data)를 관리하기 위해서
 
mutations
  • 컴포넌트의 사용처에서 store의 data를 바꿔줄 수 도 있지만, 변경에 대한 책임을 한 곳에 두기 위해 생겨난 상태변경을 위한 함수모음
 
state의 반응성 가지도록 하기
  • mutation에서 직접적으로 data를 변경하면, 반응성을 줄 수 없다.
  • vue 패키지에서 반응성을 부여하기 위해 reactive 패키지를 사용한다.
    • const state = reactive({ msg: "Hello Vue?!", count: 1 });
    •  
actions
  • 데이터의 변경 이외의 모든 작업을 진행
  • actions에서 fetch 받아온 데이터로 직접적으로 수정할 수 있지만, 데이터의 관리,추적이 어려워지기 때문에, 데이터의 변경은 무조건 mutations의 함수를 통해 변경하도록 한다.
 

5.2 Vuex 문서 살펴보기

Vuex는 Vue.js 애플리케이션에 대한 상태 관리 패턴 + 라이브러리 입니다. 중앙 집중식 저장소 역할을 통해 예측 가능한 방식으로 상태를 변경
  • 상태(state)는 앱을 작동하는 데이터이다.
  • 뷰(view)는 상태에 대한 선언적 매핍이다
  • 액션(action)은 뷰에서 사용자 입력에 대해 반응성을 갖는 상태를 바꾸는 방법이다.\
 
문제의식: 공통의 상태를 공유하는 여러 컴포넌트가 있는 경우
해결
  • actions와 mutation의 구분
  • actions는 비동기처리를 포함하여 데이터 변경 이외의 모든 작업을 처리하고, mutation은 데이터변경과 관련한 동기적 처리만을 담당한다.
    • notion image
주의
  • store에서는 context 객체에 접근할 수 있는데, context객체는 state, getters, commit, dispatch에 각가 접근 할 수 있다.
    • commit 은 mutaion을 실행하기 위한 함수 (commit(’updateMsg’, todo.title))
    • dispatch는 actions를 실행하기 위한 함수 (dispatch(”fetchTodo”))
코드
vuex에서의 state, getters, mutations, actions 사용예제
 

5.3 vuex 모듈 방식으로 사용하기 (여러개의 store)

  • 예제 코드만 첨부
    • 주의 getters를 통해 store에서 꺼내올 때 점표기법이 아닌 [] 로 접근하고, store의이름을 (/)로 표기하여 가져온다는 것
      • return this.$store.getters["message/reversedMsg"];
      count와 message store로 분리하기
      store코드
      사용 컴포넌트 (Hello.vue)
  • 중요 vuex의 ‘mapState, mapGetters, mapMutations, mapActions’ 사용하기
    • mapState(’store이름’, [가져올 state, ... , ])
    • notion image
      before
      after
 
 
// main.js app.use(fetchPlugin, { pluginName: "$myName", }); // fetch.js install(app, options) { app.config.globalProperties[options.pluginName || "$fetch"] = (()=>{ 해당 플러그인 함수 내용... }) } // App.vue this.$myName("...")
export default { install(app, options) { app.config.globalProperties.$fetch = (url, opts) => { return fetch(url, opts).then((res) => res.json()); }; }, };
// main.js import fetchPlugin from '@/plugins/fetch' const app = Vue.createApp(App); app.use(fetchPlugin) app.mount("#app"); // App.vue created() { this.$fetch("https://jsonplaceholder.typicode.com/todos/1") .then((res) => console.log(res)); },
provide() { return { msg: computed(()=>this.msg) } }
html사용 시: {{msg.value}} js의 옵션 : inject: ['msg']
// store import { createStore } from "vuex"; export default createStore({ state() { return { msg: "Hello vuex", count: 1, }; }, getters: { reversedMsg(state) { return state.msg.split("").reverse().join(""); }, }, mutations: { increaseCount(state) { state.count += 1; }, updateMsg(state, newMsg) { state.msg = newMsg; }, }, actions: { async fetchTodo({ commit }) { const todo = await fetch( "https://jsonplaceholder.typicode.com/todos/1" ).then((res) => res.json()); console.log(todo); commit("updateMsg", todo.title); }, }, });
// store.index.js import { createStore } from "vuex"; import message from "./message"; import count from "./count"; export default createStore({ modules: { message, count, }, }); // store.count.js export default { namespaced: true, state() { return { count: 1, }; }, mutations: { increaseCount(state) { state.count += 1; }, decreaseCount(state) { state.count -= 1; }, updateMsg(state, newMsg) { state.msg = newMsg; }, }, }; // store.message.js export default { namespaced: true, state() { return { message: "Hello message Module Store", }; }, getters: { reversedMsg(state) { return state.message.split("").reverse().join(""); }, }, mutations: { updateMsg(state, newMsg) { state.message = newMsg; }, }, actions: { async fetchTodo({ commit }) { const todo = await fetch( "https://jsonplaceholder.typicode.com/todos/1" ).then((res) => res.json()); console.log(todo); commit("updateMsg", todo.title); }, }, };
export default { data() { return {}; }, computed: { count() { return this.$store.state.count.count; }, message() { return this.$store.state.message.message; }, reversedMsg() { return this.$store.getters["message/reversedMsg"]; }, }, methods: { increaseCount() { this.$store.commit("count/increaseCount"); }, decreaseCount() { this.$store.commit("count/decreaseCount"); }, fetchTodo() { this.$store.dispatch("message/fetchTodo"); }, }, };
export default { computed: { count() { return this.$store.state.count.count; }, message() { return this.$store.state.message.message; }, reversedMsg() { return this.$store.getters["message/reversedMsg"]; }, }, methods: { increaseCount() { this.$store.commit("count/increaseCount"); }, decreaseCount() { this.$store.commit("count/decreaseCount"); }, fetchTodo() { this.$store.dispatch("message/fetchTodo"); }, }, };
import { mapState, mapGetters, mapMutations, mapActions } from "vuex"; export default { computed: { ...mapState("count", ["count"]), ...mapState("message", ["message"]), ...mapGetters("message", ["reversedMsg"]), }, methods: { ...mapMutations("count", ["increaseCount, decreaseCount"]), ...mapActions("message", ["fetchTodo"]), }, };