냥코딩쟝
Published 2022. 9. 30. 21:27
vue 기초-vue.js의 구성 -js notes-/vue.js

mixin

http://localhost:8080/test-mixin

mixin을 활용하면 전역변수나 전역메소드 같은 효과를 줄 수 있다.다음과 같은 mixin파일을 생성한다. (위치나 이름은 자유롭게 설정할 수 있다.)

/src/components/mixin/commonMixin.js

export default {
  data() {
    return {
      mixin: {
        test: 'mixinTest'
      }
    }
  },
  methods: {
    mixinTest() {
      console.log('This is mixin method')
    }
  }
}

다음과 같은 테스트 파일을 생성한다.

/src/views/TestMixin.vue

<template><div><h1>Mixin Test</h1><p>{{ mixin.test }}</p></div></template><script>
import commonMx from '@/components/mixin/commonMixin'

export default {
  mixins: [commonMx],
  data() {
    return {}
  },
  created() {
    this.mixinTest()
  }
}
</script>

import 할때 @/components/mixin/commonMixin와 같이 @를 사용하면 현재 서버의 절대 경로를 사용하게 된다.

화면 출력(mixin Test)과 콘솔 출력(This is mixin method)을 확인 할 것.

Bootstrap-vue

http://localhost:8080/test-bootstrap

부트스트랩이란 트위터에서 사용하는 각종 레이아웃, 버튼, 입력창 등의 디자인을 css와 javascript로 만들어서 사용하기 쉽도록 제공하는 라이브러리이다. (부트스트랩을 이용한 상용 디자인 프레임워크도 많이 있다.)

vue에서 bootstrap를 사용해 보자.다음과 같이 bootstrap-vue를 설치 한다.

https://bootstrap-vue.org/docs공식 문서를 참고하여 어떤 버전을 설치해야 하는지 확인 한다.

> npm install bootstrap@4.6.1 bootstrap-vue@2.22.0 --save

main.js파일에 다음과 같이 설정 한다.

/src/main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

// bootstrap
import { BootstrapVue } from 'bootstrap-vue'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
Vue.use(BootstrapVue)

Vue.config.productionTip = false

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

부트스트랩 컴포넌트: https://bootstrap-vue.org/docs/components다음과 같이 몇가지 부트스트랩 컴포넌트를 확인 해 본다.

/src/views/TestBootstrap.vue

<template><div><h1>Botstrap Test</h1><b-row><b-col><h3>Left</h3></b-col><b-col style="text-align: left"><h3>Center</h3><div><h4>button</h4><b-button>Button</b-button><b-button variant="danger">Button</b-button><b-button variant="success">Button</b-button><b-button variant="outline-primary">Button</b-button></div><div><h4>input text</h4><b-form-input placeholder="Enter your name"></b-form-input></div><div><h4>checkbox</h4><b-form-checkbox id="checkbox-1" name="checkbox-1" value="accepted" unchecked-value="not_accepted">
            I accept the terms and use
          </b-form-checkbox></div><div><h4>radio</h4><b-form-group><b-form-radio name="some-radios" value="A">Option A</b-form-radio><b-form-radio name="some-radios" value="B">Option B</b-form-radio></b-form-group></div><div><h4>select</h4><b-form-select v-model="selected" :options="options"></b-form-select></div><div><h4>textarea</h4><b-form-textarea id="textarea" placeholder="Enter something..." rows="3" max-rows="6"></b-form-textarea></div><div><h4>input group</h4><b-input-group prepend="Username" class="mt-3"><b-form-input></b-form-input><b-input-group-append><b-button variant="outline-success">Button</b-button><b-button variant="info">Button</b-button></b-input-group-append></b-input-group></div><div><h4>table</h4><b-table striped hover :items="items"></b-table></div></b-col><b-col><h3>Right</h3></b-col></b-row></div></template><script>
export default {
  data() {
    return {
      selected: null,
      options: [
        { value: null, text: 'Please select an option' },
        { value: 'a', text: 'This is First option' },
        { value: 'b', text: 'Selected Option' },
        { value: { C: '3PO' }, text: 'This is an option with object value' },
        { value: 'd', text: 'This one is disabled', disabled: true }
      ],
      items: [
        { age: 40, first_name: 'Dickerson', last_name: 'Macdonald' },
        { age: 21, first_name: 'Larsen', last_name: 'Shaw' },
        { age: 89, first_name: 'Geneva', last_name: 'Wilson' },
        { age: 38, first_name: 'Jami', last_name: 'Carney' }
      ]
    }
  }
}
</script><style scoped>
div h4 {
  margin-top: 20px;
}
</style>

component

http://localhost:8080/test-comp

header와 footer를 component로 구성해 본다.

1) Header component 만들기

/src/views/comp/Header.vue

<template><div><div style="background-color: yellow; height: 50px">This is header</div><hr /></div></template><script>
export default {}
</script>

2) Footer component 만들기

/src/views/comp/Footer.vue

<template><div><hr /><span>COPYRIGHT COMPANY ALL RIGHTS RESERVED.</span></div></template><script>
export default {}
</script>

3) component 호출하기

/src/views/comp/index.vue

<template><div><app-header /><h1>Component Test</h1>
    여기는 body 영역
    <app-footer /></div></template><script>
import Header from '@/views/comp/Header.vue'
import Footer from '@/views/comp/Footer.vue'

export default {
  components: {
    'app-header': Header,
    'app-footer': Footer
  }
}
</script>

app-header라는 이름은 변경이 가능하다.(kebab-case로 가능)

4) router 세팅은 이렇게

/src/router/index.js

{
  path: '/test-comp',
  component: () =>  import('../views/comp') // 호출되는 파일이 index.vue인 경우에는 파일명을 생략할 수 있다.
}

만일 컴포넌트에서 호출하는 파일이 index.vue로 되어있으면 해당 파일명을 생략할 수 있다.(../views/comp/index.vue ⇒ ../views/comp)

URL을 호출했을때 Header와 Footer가 한 페이지 내에 함께 존재하는지 확인 한다.

Not Found

존재하지 않는 url을 호출해도 페이지에는 아무런 에러 메세지가 없다.(예: http://localhost:8080/abc123 --> 이런 페이지는 없지만 화면은 깨끗하게 잘 나온다.)

1) Not Found 페이지 만들기

다음과 같이 Not Found에서 사용할 페이지를 만든다. (위치는 크게 상관 없지만 보통 component들이 모여있는 곳에 작성 한다)

/src/components/NotFound.vue

<template><div><h1 class="text-60">404</h1><p class="text-36 subheading mb-3">Page not found!</p><p class="mb-5 text-muted text-18">Sorry! The page you were looking for doesn't exist.</p><a class="btn btn-lg btn-primary btn-rounded" href="/">Go back to home</a></div></template><script>
export default {}
</script>

2) Router 설정

Not Found를 설정하기 위해서는 라우터의 설정이 중요하다.다음과 같은 설정은 반드시 라우터 설정의 맨 하단에 위치해야 한다.

/src/router/index.js

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
   ...
  {
    path: '/test-comp',
    component: () =>  import('../views/comp')
  },
  {
    path:  '*', // NotFound 설정을 위한 path('*')는 반드시 맨 하단에 위치 해야 한다.
    component: () => import('../components/NotFound.vue')
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

path: '*'의 뜻는 모든 path를 잡아낸다는 뜻이다. 따라서 맨 하단에 위치해야 위에서 놓치는(존재 하지 않는)URL의 모든 것을 잡아낼 수 있다.(맨 상단에 있으면 어떤 일이 발생 할까??)

다음과 같이 존재하지 않는 url을 방문해 보자http://localhost:8080/abc123

props

http://localhost:8080/test-family/props

부모 컴포넌트에서 자식 컴포넌트로 값을 전달 할 수 있다.

1) 자식1(Alice) 컴포넌트 만들기

/src/views/family/props/Child1.vue

<template><div><h3>Hi, I am Alice</h3><p>I have to {{ job }}</p><p>{{ user.name }} {{ user.who }}</p></div></template><script>
export default {
  props: {
    job: {
      type: String,
      required: false,
      default: 'Nothing'
    },
    user: {
      type: Object,
      required: false,
      default: null
    }
  }
}
</script>

props로 선언된 부분을 통해 부모 컴포넌트에서 프로퍼티를 전달할 수 있다.

2) 자식2(Bob) 컴포넌트 만들기

/src/views/family/props/Child2.vue

<template><div><h3>Greeting to you, I am Bob</h3><p>I have to {{ job }}</p></div></template><script>
export default {
  props: {
    job: {
      type: String,
      required: false,
      default: 'Nothing'
    }
  }
}
</script>

3) 부모(Parent) 컴포넌트 만들기

/src/views/family/props/Parent.vue

<template><div><h1>Props Test</h1><h2>I am Parent</h2><p>I will make my children do something.</p><div style="border: 1px solid red"><alice job="homework" :user="myAlice" /></div><div style="border: 1px solid blue"><bob job="clean my room" /></div></div></template><script>
import Alice from './Child1.vue'
import Bob from './Child2.vue'

export default {
  components: {
    Alice,
    Bob
  },
  data() {
    return {
      myAlice: {
        name: 'Alice',
        who: 'My daughter'
      }
    }
  }
}
</script>

<alice job="something" />와 같이 job프로퍼티를 통해 자식 컴포넌트에 값을 전달할 수 있다.자세한 props사용에 대해서는 https://kr.vuejs.org/v2/guide/components-props.html 문서를 참조 할 것.

변수를 전달할때는 v-bind를 이용 해서 전달 한다.(v-bind:user는 :user와 같다.)

4) Router 세팅

/src/router/index.js

{
  path: '/test-family/props',
  component: () => import('../views/family/props/Parent.vue')
},

라우터 세팅 후 페이지를 확인해 볼 수 있다.전달하는 job의 값에 따라 자식 컴포넌트의 job값이 달라짐을 확인 할 것.

emit

http://localhost:8080/test-family/emit

자식 컴포넌트에서 발생한 사건을 부모 컴포넌트로 전달할 수 있다.

1) 자식1(Alice) 컴포넌트 만들기

/src/views/family/emit/Child1.vue

<template><div><h3>Hi, I am Alice</h3><p>I want my parent to give me money.</p><b-button variant="primary" @click="updateTodo">Money</b-button></div></template><script>
export default {
  methods: {
    updateTodo: function () {
      this.$emit('updateTodo', 'give to Alice money')
    }
  }
}
</script>

this.$emit('updateTodo', 'give to Alice money') 와 같이 $emit을 통해 부모로 부터 프로퍼티로 할당 받은 updateTodo 를 호출할 수 있다.(여기에 기록된 updateTodo 메소드는 부모 메소드가 아니라 부모가 호출한 자식 컴포넌트의 속성명이다.)

2) 자식2(Bob) 컴포넌트 만들기

/src/views/family/emit/Child2.vue

<template><div><h3>Greeting to you, I am Bob</h3><p>I want my parent to give me food.</p><b-button variant="success" @click="updateTodo">Food</b-button></div></template><script>
export default {
  methods: {
    updateTodo: function () {
      this.$emit('updateTodo', 'feed Bob food')
    }
  }
}
</script>

Bob은 Alice와는 다른 요구를 한다. (feed Bob food)

3) 부모 컴포넌트 만들기

/src/views/family/emit/Parent.vue

<template><div><h1>Emit Test</h1><h2>I am Parent</h2><p>I have to do something for my children.</p><p style="font-weight: bold">I will {{ todo }}</p><div style="border: 1px solid red"><alice @updateTodo="updateTodo($event)" /></div><div style="border: 1px solid blue"><bob @updateTodo="updateTodo($event)" /></div></div></template><script>
import Alice from './Child1.vue'
import Bob from './Child2.vue'

export default {
  components: {
    Alice,
    Bob
  },
  data() {
    return {
      todo: 'do nothing'
    }
  },
  methods: {
    updateTodo: function (newTodo) {
      this.todo = newTodo
    }
  }
}
</script>

부모 컴포넌트는 자식 컴포넌트 호출 시 <alice @updateTodo="updateTodo($event)" />에서 $event를 통해 자식 컴포넌트에서 전달한 값을 받을 수 있다. (객체도 가능함)

4) Router 세팅

/src/router/index.js

  {
    path: '/test-family/emit',
    component: () => import('../views/family/emit/Parent.vue')
  },

중요 포인트 부모와 자식 컴포넌트에서 사용하는 updateTodo이름이 중복 되는데 누구를 호출하는지 구분할 수 있어야 한다.(호출 메소드와 응답 메소드를 짝지을 수 있어야 한다.)

event bus

http://localhost:8080/test-family/event-bus

자식 컴포넌트 끼리 event를 발생 시킬 수 있다. (하지만 권장하지는 않는다. Store를 사용하면 된다.)

1) EventBus 파일 생성

/src/views/family/eventBus/eventBus.js

import Vue from 'vue'

export const EventBus = new Vue()

2) 자식1(Alice) 컴포넌트 만들기

/src/views/family/eventBus/Child1.vue

<template><div><h3>Hi, I am Alice</h3><p>
      I said: Hey Bob. where's my stuff??
      <b-button size="sm" variant="primary" @click="whatIsaid">send to Bob</b-button></p><p>Bob said:</p></div></template><script>
import { EventBus } from './eventBus'

export default {
  data() {
    return {
      count: 0
    }
  },
  methods: {
    whatIsaid: function () {
      this.count += 1
      EventBus.$emit('aliceSaid', `Give me my stuff ! x ${this.count}`)
    }
  }
}
</script>

EventBus.$emit('eventName', 'message') 를 통해서 이벤트를 전파할 수 있다.eventName: 전파하는 이벤트 이름message: 전파하는 메세지(객체도 가능함)

3) 자식2(Bob) 컴포넌트 만들기

/src/views/family/eventBus/Child2.vue

<template><div><h3>Greeting to you, I am Bob</h3><p>Alice said to me: {{ aliceMessage }}</p><p>
      Bob said: I don't know. <b-button size="sm" variant="success">reply to Alice</b-button>(여기를 구현해 보도록
      하자.)
    </p></div></template><script>
import { EventBus } from './eventBus'

export default {
  data() {
    return {
      aliceMessage: ''
    }
  },
  created() {
    EventBus.$on('aliceSaid', message => {
      this.aliceMessage = message
    })
  }
}
</script>

EventBus.$on('eventName', function (message) { ... })를 통해서 메세지를 받을 수 있다.

함수 function (message) { ... }는 message => { ... }와 같다. (화살표 함수)

4) 부모 컴포넌트 만들기

/src/views/family/eventBus/Parent.vue

<template><div><h1>Event Bus Test</h1><h2>I am Parent</h2><p>Let's listen to the children's conversation.</p><div style="border: 1px solid red"><alice /></div><div style="border: 1px solid blue"><bob /></div></div></template><script>
import Alice from './Child1.vue'
import Bob from './Child2.vue'

export default {
  components: {
    Alice,
    Bob
  },
  data() {
    return {}
  }
}
</script>

부모 컴포넌트에서는 그냥 두 자식컴포넌트를 호출했을 뿐이다.

자식1(Alice)컴포넌트의 [send to Bob]버튼을 통해 메세지 전달 테스트를 해보자.

자식2(Bob)컴포넌트에서 자식1(Alice)컴포넌트로 메세지를 보내 보자. ([reply to Alice]버튼 기능 구현)

Store (Vuex)

UserList: http://localhost:8080/test-user/listUserInfo: http://localhost:8080/test-user/info

Vuex란 state를 관리할 수 있도록 도와주는 상태 관리 라이브러리이다. 상세 설명은 (여기) 참고

보통 Store라고 명칭 한다.

가상의 User정보를 store를 통해 호출해 본다.

1) User Store 파일 생성(빈 파일 생성)

/src/store/models/user.js

export default {
  state: {
    // state에 사용할 모델이나 값을 선언 한다.
  },
  getters: {
    // getters을 통해 state값을 호출 한다.
  },
  mutations: {
    // mutations는 동기적이어야 함.(비동기 사용 불가)
  },
  actions: {
    // action은 비동기적 사용이 가능하다. (action에서 mutation을 호출하는 방법을 권장함)
  }
}

2) Store의 index파일에서 User Store파일 import 하기

/src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import User from './models/user'

// Load Vuex
Vue.use(Vuex)

// Create Store
export default new Vuex.Store({
  modules: {
    User
  }
})

3) User Store 파일 코딩

위와 같이 빈 파일을 만들었으면 이제는 다음과 같이 가상의 User정보를 조회하는 Store를 만들어 보자.

/src/store/models/user.js

export default {
  state: {
    // state에 사용할 모델이나 값을 선언 한다.
    User: {
      id: 0,
      name: null,
      username: null,
      email: null
    }, // User 상세 정보용 state
    UserList: [] // User 리스트용 state
  },
  getters: {
    // getters을 통해 state값을 호출 한다.
    User: state => state.User,
    UserList: state => state.UserList
  },
  mutations: {
    // mutations는 동기적이어야 함.(비동기 사용 불가)
    setUser(state, data) {
      state.User = data
    },
    setUserList(state, data) {
      state.UserList = data
    }
  },
  actions: {
    // action은 비동기적 사용이 가능하다. (action에서 mutation을 호출하는 방법을 권장함)
    // 사용자 정보 출력
    actUserInfo(context, payload) {
      console.log('actUserInfo', payload) // payload를 통해 검색 조건을 받을 수 있다.

      const testUserInfo = {
        id: payload,
        name: 'test',
        username: 'testUser',
        email: 'test@email.com'
      } // 이 값을 RestAPI에서 받아오면 된다.
      context.commit('setUser', testUserInfo) // mutation을 통해 User값을 세팅 한다.
    },
    // 사용자 리스트 조회
    actUserList(context, payload) {
      console.log('actUserList', payload) // payload를 통해 검색 조건을 받을 수 있다.

      const testUserList = ['user1', 'user2', 'user3'] // 이 값을 RestAPI에서 받아오면 된다.
      context.commit('setUserList', testUserList) // mutation을 통해 UserList값을 세팅 한다.
    }
  }
}

참고 actions에서 정의하는 메소드의 첫번째 파라미터context는 보통 { commit }만 받아서 처리하는 예제가 많다.하지만 context에는 { commit }외에도 유용하게 사용할 수 있는 여러 항목들이 많이 있기 때문에 context자체를 사용해서 익숙해 지는 것을 권장 한다.

4) User List 파일 생성

/src/views/user/UserList.vue

<template><div><h1>Store Test</h1><div><h3>User List</h3><b-button variant="primary" @click="searchUserList">Search</b-button><div style="border: 1px solid red">
        {{ userList }}
      </div></div></div></template><script>
export default {
  data() {
    return {
      searchParams: null // 검색 조건을 넣을 수 있음
    }
  },
  computed: {
    userList() {
      return this.$store.getters.UserList
    }
  },
  methods: {
    searchUserList() {
      this.$store.dispatch('actUserList', this.searchParams) // 검색 조건을 넘겨 준다.
    }
  }
}
</script>
  1. 검색: this.$store.dispatch('actUserList', this.searchParams)를 통해 검색 실행을 시킬 수 있다.
  2. 조회: this.$store.getters.UserList를 통해 UserList정보를 가져올 수 있다.

5) User Info 파일 생성

/src/views/user/UserInfo.vue

<template><div><h1>Store Test</h1><div><h3>User Info</h3><b-container fluid><b-row><b-col style="text-align: right">User.id</b-col><b-col style="text-align: center"><b-form-input v-model="id" placeholder="User.id를 검색하세요.(1~10)" /></b-col><b-col style="text-align: left"><b-button variant="primary" @click="searchUserInfo">Get User Info</b-button></b-col></b-row></b-container><div style="border: 1px solid red">
        {{ userInfo }}
      </div></div></div></template><script>
export default {
  data() {
    return {
      id: null // 검색할 User.id
    }
  },
  computed: {
    userInfo() {
      return this.$store.getters.User
    }
  },
  methods: {
    searchUserInfo() {
      this.$store.dispatch('actUserInfo', this.id)
    }
  }
}
</script>

위와 같이 세팅된 Store의 정보는 다른 컴포넌트에서도 실시간으로 받아볼 수 있다.(this.$store.getters.User를 통해 동일한 User정보를 받을 수 있음)

'-js notes- > vue.js' 카테고리의 다른 글

vue.js기초- 백엔드와 연결  (0) 2022.09.30
vue 기초 코드  (0) 2022.09.30
profile

냥코딩쟝

@yejang

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!