파라미터따라 동적으로 컴포넌트 보여주는 라우터 처리

vue-router 좋긴 한데 …..
– 작성할 때 형식이 너무 번거롭다.
– 게다가 sidebar 에 json 과 똑같이 맞춰줘야 한다.

sidebar 는 그나마 간략하니, vue-router 작성 부분을 많이 감소시켜서, 새로 추가할 때마다 내가 노동을 적게 했으면 좋겠다.

구상

현재 vue-router 는 아래와 같이 되어 있다.

이것을 다음처럼 만들 것이다.

두 번째 안이 훨씬 복잡해 보이지만.. 코딩 양은 준다.

작성

모든 파라미터 요청을 받는 주소 하나를 만들고

/router/index.js

export default new Router({
  routes: [
    {
      path: '/',
      redirect: '/home',
      name: 'Home',
      component: require('@/components/Structure').default,
      children: [
        {
          path: 'home',
          name: '홈페이지',
          component: require('@/views/Home').default
        },
        {
          path: '/dyna/:name',
          name: '다이나믹 컴포넌트 라우팅',
          component: require('@/components/dynamicComponent').default
        }

그 주소에 따라 렌더링되는 컴포넌트를 작성한다.

잘 보면, 컴포넌트를 미리 등록해놓기만 하면, params 에 따라 동적으로 컴포넌트를 렌더링한다.

/components/dynamicComponent

<template>
    <!-- keep-alive 는 컴포넌트 변경되어도 파괴 후 새로 만들지 않고
    이전 계속 유지한다. 즉, 뭘 해놓으면 상태가 유지된다.-->
    <!--<div>-->
    <!--<keep-alive>-->
    <div>
        <div>{{$route.params}}</div>
        <component :is="dynamicComponent"></component>
    </div>
    <!--</keep-alive>-->
    <!--</div>-->
</template>

<script>
  export default {
    name: 'dynamicComponent',
    components: {
      'basic': require('@/views/basic/Basic').default,
      'components': require('@/views/basic/Components').default,
      'instance': require('@/views/basic/Instance').default
    },
    computed: {
      dynamicComponent () {
        // 파라미터 한개로 동적 렌더링 하려면.. 위에 components 등록하고 아래처럼 호출하면 됨
        return this.$route.params.name
      }
    },
    created () {
      this.dynamicComponent = '' + this.$route.params.name
    }

  }
</script>

<style scoped>

</style>

결과

똑같이 특정 경로 접속 시 컴포넌트를 렌더링 하는 코드를 비교해보자.

비포

{
          path: 'basic',
          name: '기초',
          redirect: '/basic/basic',
          component: {
            render (c) { return c('router-view') }
          },
          children: [
            {
              path: 'basic',
              name: '기초 화면',
              component: require('@/views/basic/Basic').default
            },
            {
              path: 'instance',
              name: '뷰 인스턴스',
              component: require('@/views/basic/Instance').default
            },
            {
              path: 'components',
              name: '뷰 컴포넌트',
              component: require('@/views/basic/Components').default
            }
          ]
        },

에프터

    components: {
      'basic': require('@/views/basic/Basic').default,
      'components': require('@/views/basic/Components').default,
      'instance': require('@/views/basic/Instance').default
    },

사이드바 링크는 형식이 간결하므로, 작성해야 할 코드의 양 자체가 줄었다.

주의

지금처럼 하면, components 영역이 계속 비대해지게 된다.

어쩌면 상위 링크 별로 하나의 동적 컴포넌트를 만들고 라우팅을 분리시키는 것이 좋을 수도 있다.

현재

분리안

그럼 별도의 컴포넌트에 자기 하위 라우팅 연관 컴포넌트만 책임지면 되니 양이 줄겠지.

개선점?

음.. 아예 파라미터로 컴포넌트 위치를 받은 다음에 require 에 곧장 때려 박으면,

아예 컴포넌트 등록하는 부분도 제거할 수 있다.

이렇게 시도를 했는데

require(`@/views/${this.$route.params.name}`).default

이 경우에는 레벨 처리까지 하려면 파라미터1차 2차 분석을 해야하니 귀찮아서 안함.

참조

  • 어떻게 매개변수에 따라서 라우트 컴포넌트를 동적으로 변경하나요? 링크
  • 교환가능한 동적 컴포넌트 링크
  • 동적 라우트 매칭링크
  • vuejs 에서 동적 컴포넌트 링크
  • 동적이고 비동기적인 컴포넌트를 뷰js 로 쉽게 만들기 링크
  • 라우터에서 require 사용서 꼭 default 를 설정해야함 링크

vue 마크다운 컴포넌트로 마크다운을 처리해서 보여주기

설치

npm install -S vue-markdown

짧은 플래그 옵션 : –save 는 -S, –save-dev 는 -D

사용

플러그인이 아니라서 Vue.use() 로 등록할 수가 없다. 만약 전역 컴포넌트로 등록하고 싶으면 여기를 참조하고… 여기서는 특정 컴포넌트에서 임포트에서 사용하도록 하겠다.

컴포넌트 임포트

  import VueMarkdown from 'vue-markdown'

  export default {
    name: 'markdown',
    components: {
      VueMarkdown
    },

사용예시

<template>
    <div>
        <vue-markdown>## 마크다운 화면에 보이기</vue-markdown>
    </div>
</template>

주의할 점

다음의 구린 점 두 가지가 있다.

  • 들여쓰기나 줄바꿈 후 마크다운 렌더링 안 됨
  • 내용이 바뀌어도 화면이 자동으로 변경되지 않음

들여쓰기나 줄바꿈 후 마크다운 렌더링 안 됨

줄바꿈 한 만큼 제하고 렌더링을 하지 않는다. 그래서 js-lint 나 포매팅 적용하면 모두 들여쓰기 될 때 마크다운이 다 안 먹게 됨.

다음 코드가 있다면

<template>
    <div>
        <vue-markdown>## 마크다운 화면에 보이기
## 요건 헤딩 처리되고
            ## 요건 띄어쓰기 땜에 일반 글자로 처리가 된다.
            *요것 도 처리가 안 됨*
*헬로*   *요거는?*
        </vue-markdown>
</template>

아래처럼 보이고

포매팅을 하고 나면

<template>
    <div>
        <vue-markdown>## 마크다운 화면에 보이기
            ## 요건 헤딩 처리되고
            ## 요건 띄어쓰기 땜에 일반 글자로 처리가 된다.
            *요것 도 처리가 안 됨*
            *헬로* *요거는?*
        </vue-markdown>
</template>

아래처럼 마크다운 처리 적용된 것이 다 풀려벌인다.

참 안타까운 일.

내용이 바뀌어도 화면이 자동으로 변경되지 않음

요것도 자꾸 수동으로 새로고침을 하게 해줘야 하는데 귀찮다.

보완

souce 속성 사용

아래처럼 source 속성을 사용한 다음.

        <vue-markdown :source="source"></vue-markdown>

data 에다가 백틱( ` )을 사용하면 포매터와 lint 가 건드리지 않기 땜시 요렇게 도 할 수 있구..

  export default {
    name: 'markdown',
    components: {
      VueMarkdown
    },
    data () {
      return {
        source: `
- [ ] xxx
## hello?
`
      }

md 파일 만들어서 import 한 것을 변수에 담아서 그걸 바인딩 시키는 게 편할 거 같음.

감싸는 컴포넌트를 만들고 md 파일 불러오는 기능 추가

src 속성을 주면, 해당 속성에 적힌 파일을 읽어오고, 읽어온 결과값을 뷰-마크다운에 렌더링하는 컴포넌트를 만든다.

일반적으로 클라이언트-서버 구조에서는 사용자의 브라우저에서 서버의 로컬파일을 직접 읽을 수 없다.

  • 클라이언트에서 axios 나 fetch를 사용해서 웹서버에 해당 자원(파일)을 요청해서 그결과를 사용한다.[읽기]1

그래서 만약 외부 서버의 마크다운 파일 자원을 ajax 로 요청해서 보여주려면 여기를 참조하면 된다. axios.get()fetch 로 바꿔도 작동할 것이다.

나는 사용자의 컴퓨터에서 실행되는 일렉트론 앱에서 사용자의 로컬 파일을 ‘웹팩 로더 없이’ 읽어서 보여주고 싶다.

그럼 두가지 방볍이 있는데..

  • Asset 폴더 내부에 md 파일을 넣어서 fetchaxios.get 으로 md 파일을 읽거나
  • 일렉트론 app.getPath()로 서버의 로컬 자료에 접근하는 방법이 있다.

일렉트론 app 의 api : https://github.com/electron/electron/blob/master/docs/api/app.md

Asset 이 가장 쉬운 방법이다. http:// 등의 프로토콜로 요청할 때 cors 에 걸리지 않는다면 정상적으로 로딩이 될 것이다.

fetch 와 cors 는 여기 참조. preflight 이라니.. 토큰기반 자원요청 때 나온 개념인데..

다음에 app.getPath를 시도하면 안 먹는다. 어쨌건.. file://C://... 이런식으로 주소를 생성하고, 여기다가 요청을 하면 axiosfetch 건 크롬브라우저는 요청을 거부한다.

브라우저에서 file url 스키마 요청 거부하는 것은 지극히 당연하다. 이것이 가능하면, 만약 내가 만든 사이트에 접속만 시키면 모든 하드디스크 파일을 빼올 수 있다는 말이니까.

  • 크롬 브라우저에 한계상 특정한 파일만 가능하다.읽기

CORS 에도 불구하고 어거지로 로컬 파일 가져오려면 이 방법 중 크롬 켤 때 보안 옵션을 꺼버리는 수 밖에 없는데, 이건 핵이라 쓰기 꺼려짐. 보안을 끈다는 것도 위험하게 느껴지고.

"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --disable-web-security --user-data-dir="%LOCALAPPDATA%\Google\Chrome\User Data\development"

그럼 결국 마크다운 로더 or Asset폴더 요청방법밖에 없다….. 없다?

아니 fs 라는 모듈로 파일을 읽을 수 있는 거 같네!! 여기

src/renderer/components/MdViewer.vue

<template>
    <div>
        hkehehl
        <vue-markdown :source="mdText"></vue-markdown>
        <div>hello</div>
    </div>
</template>

<script>
  import VueMarkdown from 'vue-markdown'
  // import axios from 'axios'
  import path from 'path'
  import {remote} from 'electron'
  import fs from 'fs'

  export default {
    name: 'md-viewer',
    props: ['src'],
    components: {
      VueMarkdown
    },
    data: function () {
      return ({
        'mdText': '',
        filePath: path.join(remote.app.getAppPath(), `/src/renderer/views/${this.src}`)
      })
    },
    mounted () {
      // axios.get(this.path).then(response => {
      //   this.mdText = response.data
      // })
      // fetch(this.path, {mode: 'no-cors'}).then(response => {
      //   this.mdText = response.data
      // })
      if (this.src === undefined) {
        console.log('No file selected')
      }

      fs.readFile(this.filePath, 'utf-8', (err, data) => {
        if (err) {
          alert(`파일 읽는 중 오류 발생! : ${err.message}`)
        } else {
          this.mdText = data
        }
      })
    }
  }
</script>

<style lang="scss" scoped>

</style>

요렇게 사용하면 된다.

<template>
    <div>
        <md-viewer src="components/markdown.md"></md-viewer>
    </div>
</template>

<script>
  export default {
    name: 'markdown',
    components: {'md-viewer': require('@/components/MdViewer').default}
  }
</script>

<style scoped>

</style>

간단한 기능들

컨텐츠 바뀔 때 뷰도 바뀌게 하기

https://github.com/miaolz123/vue-markdown/issues/44


  1. https://github.com/nuxt/nuxt.js/issues/2006
    – 그래서, 서버에서 서버의 자원을 읽어서 처리한 다음 클라이언트에 보여줘야 하는데 웹팩으로 구성된 프로젝트의 경우는 웹팩이 해당 파일을 읽어서 처리할 수 있어야 하므로, 특별한 로더가 필요하다.[읽기]2 ↩︎
  2. https://forum.vuejs.org/t/reading-directory-content-in-component/40
    – 서버에서 서버의 로컬자원을 런타임에서 읽어서 처리하는, 특히 마크다운 파일에 대한 용도의 로더가 있다.vue-markdown-loader ↩︎

vue scss 로더로 scss 사용하기

필요

일반적으로 style 은 내부에 기술한다.

<template>
  <div class="example">{{ msg }}</div>
</template>

<script>
export default {
    ......
}
</script>

<style>
.example {
  color: red;
}
</style>

하지만 scss 를 사용하려면? 바로 scss 로더를 설치확장자 안써도 사용하기 처리를 해줘야한다.

설치

모듈 설치

npm install --save-dev sass-loader node-sass

scss 확장자 생략 가능하게 처리

.electron-vue/webpack.renderer.config.js

  module: {
    rules: [
      ......
      {
        test: /.scss$/,
        use: [
          'vue-style-loader',
          'css-loader',
          'sass-loader'
        ]
      },
      ......

이제 lang 속성을 이용해서 scss 를 적용할 수 있다.

보통 css 는 최상위에 설정을 하니, 최상위 루트 컴포넌트에 기술을 해주자.

<style lang="scss">

    // node_modules/bootstrap-vue/dist/bootstrap-vue.css
    @import '~bootstrap/dist/css/bootstrap.css';

  <!--// Import Main styles for this application-->
  @import './assets/scss/style'
</style>

물결 모양은 node_module 경로 대신작성하는 설탕구문이다.

뷰 부트스트랩4 플러그인 사용해보기

부트스트랩용 뷰-사이드바 플러그인 설치를 할 때 부트스트랩이 없어서 스타일이 제대로 표현되지 않았다. 이제 부트 스트랩을 설치해보자.

https://bootstrap-vue.js.org/

[toc]

설치

package.json 에 표시가 되고 배포할때도 함께 사용되게 --save.

npm i --save bootstrap-vue

불러와서(import) 사용(Vue.use)하기.

main.js

......
import BootstrapVue from 'bootstrap-vue'

......
Vue.use(BootstrapVue)


new Vue({
  ......
}).$mount('#app')

사용

css 를 임포트 해와야 함 그러기 위해서 css 로더를 설치하고 설정해야 하는데 그것은 다른 글을 참조한다.

최상위 페이지인 App.vue 컴포넌트에 임포트 하자.

<template>
  <div id="app">
    <router-view></router-view>
  </div>
</template>

<script>
  export default {
    name: 'vue-elec-prac'
  }
</script>

<style lang="scss">
  @import '~bootstrap/dist/css/bootstrap.css';
  @import '~bootstrap-vue/dist/bootstrap-vue.css';
</style>

뷰 사이드바 플러그인과 라우터 컴포넌트 사용해보기

사이드바를 처음부터 만들려면 아주 개고생을 해야하니, 남이 만들어 둔 것을 사용해보자. 여기 것이 사이드바고..

라우터는 뷰-일렉트론 플젝 세팅하면서 이미 설치가 되었다.

[toc]

설치 및 설정

설치

npm install vue2-sidebar --save

npm 옵션1
* –save-dev : 개발용으로만 의존성을 설치하고 배포 때는 포함 안 됨
* –save : 배포용 때도 같이 사용됨
* 없음 : 설치는 하지만 package.json 에 기록이 안 되기 때문에 비추천한다. 아무도, 심지어 나조차도 나중에 잊어버리면 기억을 못한다.

설정

main.js 위에 추가

// 불러온 다음에
import Vue2Sidebar from 'vue2-sidebar';
// Vue 에서 전역으로 사용
Vue.use(Vue2Sidebar);

혹은 로컬 사용

components: {
    'vue2-sidebar': Vue2Sidebar
},

App.vue 에서 해당 컴포넌트를 사용하기

<template>
  <div id="app">
    <div>
      <vue2-sidebar :links="Array"></vue2-sidebar>
    </div>
    <!--<router-view></router-view>-->
  </div>
</template>

결과

비어있는 내용의 사이드바가 출력된다.

만약 Vue.use(Vue2Sidebar);를 빼먹으면 다음처럼 프로젝트 폴더 내부 내용이 보여진다.

프로젝트 폴더가 나오는 이유는, 일렉트론이라서 그렇다. 라우터로 보여줄 것을 지정해야 함.

사용

사이드바 플러그인을 구지!! 구지구지 전역에서 사용할 필요가 없다.

SPA(단일 페이지 어플리케이션)에서 사이드바는 첫페이지에 단 한 번만 사용되고, 내부의 내용이 변경되면 되기 때문.

라우터 플러그인

라우터를 불러와 줬는데 use 키워드는 사용하지 않았다. 그런데 어떻게 라우터 플러그인을 쓸 수 있는 것일까?

main.js

import router from './router'

짜잔, router 폴터 하위 .js 파일에서 Vue.use() 를 사용해 플러그인을 등록한다..

src/renderer/router.js

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

그리고는 해당 라우터 설정을 하는데, 여기서는 첫(/) 페이지에 접속하면 LandingPage.vue를 보라고 알려준다.

export default new Router({
  routes: [
    {
      path: '/',
      name: 'landing-page',
      component: require('@/components/LandingPage').default
    },
    {
      path: '*',
      redirect: '/'
    }
  ]
})

require 에다가 default 는 왜 붙이나? : es5 에서 모듈 정의하고 임포트 하면 해당 모듈 객체를 통째로 가져와 써야했다. 각각의 함수들은 새로운 변수에 할당을 하는 귀찮은 과정이 있었음. es6 에서는 모듈 정의시 default 에 특정 함수를 할당할 수 있다. 그래서 default 라고 붙여주면, 모듈 객체 통째가 아닌, 해당 함수만 할당할 수 있음. 여기에 자세한 설명이 나와있으니 참조.

사이드바 적용

그럼 이제 랜딩 페이지에 슬라이더바 컴포넌트를 사용해 보자

라우터 기능 회복

먼저 라우터가 정상 작동하도록 하게 하고..

App.

<template>
  <div id="app">
    <router-view></router-view>
  </div>
</template>

랜딩 페이지에 사이드바 적용

랜딩 페이지에 사이드바를 적용한다.

src/renderer/components/LandingPage.vue

<template>
  <vue2-sidebar :links="Array"></vue2-sidebar>
</template>

<script>
  export default {
    name: 'landing-page'
  }
</script>

<style>
</style>

일렉트론 앱 창에서 컨트롤+R을 눌러서 변경사항을 새로고침해서 확인한다.

사이드바에 메뉴 출력

먼저 봐야 하는 것이 있다.

속성(Properties)

  • heading: {type: String} (사이드바 위의 헤딩),
  • links: {type: Array} – 사이드바에서 보여질 링크들의 배열. 각 배열 항목은 다음의 속성들을 가진 객체다: label, href, icon, class 와 links (하위 링크일 때),
  • show-header: {type: Boolean, default: true}: 우측 창에서 nav 헤더를 숨긴다.
  • header-links: {type: Array}: 링크와 비슷하지만 우측 창의 nav 헤더에서 보여진다.

슬롯-파진 홈-(Slots)

다음의 홈들은 사이드바를 사용자 정의하는데 사용되어질 수 있다.

  • sidebar-header: 당신이 사이드바 위에 놓고 싶은 어떤 종류의 헤더
  • header-links: 당신의 nave 컴포텉으로 기본 헤더 링크를 대체하고 싶다면
  • default: 태그 사이에 콘텐츠를 위치시켜라. 조차 위치시킬 수 있다!

메뉴 기획

위 속성들을 사용해서 메뉴를 꾸며보자.

먼저 헤더는 ‘뷰연습’이고 링크와 헤더링크의

<template>
  <vue2-sidebar heading="뷰연습" :links="links" :header-links="header"></vue2-sidebar>
</template>

메뉴는 뷰인스턴스, 뷰컴포넌트, 뷰 컴포넌트 통신으로 하고. 각각의 하위메뉴는 하위1, 하위2라고 이름을 붙이자.

<script>
  import Vue2Sidebar from 'vue2-sidebar'

  export default {
    name: 'landing-page',
    components: {Vue2Sidebar},
    data () {
      return {
        links: [
          {
            label: '홈',
            href: '/',
            links: [
              {
                label: '기초',
                href: '/home',
                links: [
                  {label: '뷰인스턴스', href: '/home1'},
                  {label: '뷰컴포넌트', href: '/home1'}
                ]
              }
            ]
          }
        ],
        header: [
          {label: 'Header #1', href: '#/header1'},
          {label: 'Header #2', href: '#/header2'},
          {label: 'Header #3', href: '#/header3', icon: 'fa-glass'}
        ]
      }
    }
  }
</script>

그럼 다음처럼 출력이 잘 된다.

근데 css 적용이 안 되어서 원본만큼 예쁘게 나오지 않는 것 같다.

뭔가 화면이 덜 된 거 같은 이유

css 적용이 안 된 것인가 확인했으나 적용 됨

아! 부트스트랩이랑 함께 쓰는 컴포넌트구나! 즉, 부트스트랩을 적용해야함.

부트 스트랩은 다음 글에서 다루는 것이 좋겠다.

라우터와 함께 동작시키기

라우터는 라우터에 따라 화면을 변경 시켜준다. 그렇다면 모든 화면(컴포넌트)마다 사이드바를 다 포함 시켜줘야 하나?

그건 아니다.

먼저 뼈대가 되는 컴포넌트를 만들고, 경로를 서브의 서브로 만들면 해당 페이지내에서 움직이게 된다.

먼저 뼈대가 되는 메인 페이지 구조를 만들자.

src/renderer/components/Structure.vue

<template>
    <div class="app">
        <AppHeader/>
        <div class="app-body">
            <vue2-sidebar heading="뷰연습" :links="links" :header-links="header">
                <main class="main">
                    <div class="page-title">
                        <h1>{{ name }}</h1>
                        <breadcrumb :list="list"/>
                    </div>
                    <div class="container-fluid">
                        <router-view></router-view>
                    </div>
                </main>
            </vue2-sidebar>
        </div>
    </div>
</template>

잘 보면, <vue2-sidebar> 내부에 <router-view></router-view> 를 넣은 것을 볼 수 있다. 사이드바는 그대로 있으면서 내부에 라우터 뷰만 변경되는 구조다.

그리고 아래부분에 사이드바 링크를 설정해주도록 한다.

src/renderer/components/Structure.vue

<script>
  import Vue2Sidebar from 'vue2-sidebar'

  export default {
    name: 'Structure',
    components: {'vue2-sidebar': Vue2Sidebar},
    data () {
      return {
        links: [
          {
            label: '홈',
            href: '#/'
          },
          {
            label: '기초',
            href: '#/basic',
            links: [
              {label: '뷰인스턴스', href: '#/basic/instance'},
              {label: '뷰컴포넌트', href: '#/basic/component'}
            ]
          }
        ],
        header: [
          {label: 'Header #1', href: '#/header1'},
          {label: 'Header #2', href: '#/header2'},
          {label: 'Header #3', href: '#/header3', icon: 'fa-glass'}
        ]
      }
    }
  }
</script>

주소 형태 주의
만약 주소를 /basic/link 이나 /#basic/link 형태로 입력을 한다면 가상 돔은 변경이 되지만 실제로 화면에 보여지는 돔은 변경이 안 된다. 이 경우 F5를 눌러 새로고침을 하면 전체 페이지가 새로고침 되면서 화면이 보이게 됨. 정리하면, vue 내부 컴포넌트 라우팅 주소는 #/경로/경로/경로... 이런식이 되어야 라우터 변경 후 화면까지 잘 갱신된다.

이제 라우터에서 우리가 만든 뼈대를 사용해서 라우팅을 하도록 해야 한다.

src/renderer/router/index.js

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      redirect: '/home',
      name: 'Home',
      component: require('@/components/Structure').default,
      children: [
        {
          path: 'home',
          name: '홈페이지',
          component: require('@/views/Home').default
        },
        {
          path: 'basic',
          name: '기초',
          redirect: '/basic/basic',
          component: {
            render (c) { return c('router-view') }
          },
          children: [
            {
              path: 'basic',
              name: '기초 화면',
              component: require('@/views/basic/Basic').default
            },
            {
              path: 'instance',
              name: '뷰 인스턴스',
              component: require('@/views/basic/Instance').default
            },
            {
              path: 'component',
              name: '뷰 컴포넌트',
              component: require('@/views/basic/Component').default
            }
          ]
        }
      ]
    },
    {
      path: '*',
      redirect: '/'
    }
  ]
})

render(c) 가 뭐야?
render 는 화면에 컴포넌트를 뿌리는(등록하는) 문법설탕이다

render (c) { return c('router-view') }
render(createElement) {return createElement('router-view')}
template: `<router-view></router-view>`

컴포넌트는 아래와 같이 설정을 포함한 객체로 만들어 지는데 위에서 본 것들이 아래 구문의 단축버전이다.

componentConfig = {
    render: function(createElement) {
        return createElement('div', 'test')
    }
};
Vue.component('test', componentConfig);

자, 위를 보면 라우트를 정의해 놓았다. / 경로로 접속하면 우리가 만든 뼈대인 Structure 컴포넌트를 사용해 화면을 보여준다. 그리고는 하위 경로인 /home 으로 리다이렉트를 한다. 그러면 Structure 내부의 <router-view></router-view> 에다가 Home.vue 를 보여주게 된다.

이 상태에서 실제로 사이드바에 메뉴를 누르면 해당 링크가 없다고 오류가 난다.

보여줄 페이지를 만들어야 한다.

<template>
    <div>
        <h3>홈 페이지 입니다.</h3><br/>
        <router-link to="basic">기초로 이동</router-link>
    </div>
</template>

<script>
  export default {
    name: 'Home'
  }
</script>

router-link to 는 뭐야?
원래 뷰에서 라우터에 정의된 path 로 이동시 router-link to 를 사용한다.

src/renderer/views/basic/Basic.vue

<template>
    <div>
        <h3>기초 페이지 입니다.</h3>
        <router-link to="뷰 인스턴스">인스턴스로 이동</router-link>
    </div>
</template>

<script>
  export default {
    name: 'Basic'
  }
</script>

이름으로 이동 : 라우터에서 해당 경로에 정의된 name 속성의 값으로도 이동이 가능하다.

src/renderer/views/basic/Instance.vue

<template>
    <div>
        <div>
            <h3>인스턴스 페이지 입니다.</h3><br/>
            <router-link to="/basic">기초로 이동</router-link><br/>
            <router-link to="/home">홈으로 이동</router-link><br/>
            <router-link to="/basic/component">컴포넌트로 이동</router-link><br/>
        </div>
    </div>
</template>

<script>
  export default {
    name: 'Instance'
  }
</script>

경로로 이동 : 요렇게 /경로/하위경로 식으로도 이동이 가능하다.

src/renderer/views/basic/Component.vue

<template>
    <div>컴포넌트 페이지 입니다.{{newval}}</div>
    <h3>인스턴스 페이지 입니다.</h3><br/>
    <router-link to="/basic">기초로 이동</router-link><br/>
    <router-link to="/home">홈으로 이동</router-link><br/>
    <router-link to="/basic/component">컴포넌트로 이동</router-link><br/>
</template>

<script>
  export default {
    name: 'Component',
    data () {
      return {
        newval: 'val'
      }
    },
    created () {
      alert('hello')
    }
  }
</script>

뷰 인스턴스 생성 및 플러그인 사용

뷰 인스턴스 생성

main.js 파일을 보자.

생성

new Vue() 하면 인스턴스가 생성되고 인스턴스에 전달할 변수들을 js 객체 속성과 값으로 넘겨줄 수 있다.

new Vue({
  el: '#app'
})

의존성 추가

자바스크립트쪽에서 import 로 가져온다.


// import 는 자바스크립트 import import Vue from 'vue' import axios from 'axios' // App 은 App.vue 를 뜻한다. // 확장자를 자동 처리하는 기능은 다른 곳에 설정이 되어있다. import App from './App' // router 와 store 폴더내부를 통째로 import 한다. import router from './router' import store from './store'

그것을 Vue 에서 사용한다.

// Vue 용 플러그인은 use 메소드로 사용한다.
if (!process.env.IS_WEB) Vue.use(require('vue-electron'))

// vue 내부 http 변수에 자바스크립트 라이브러리 할당
Vue.http = Vue.prototype.$http = axios
Vue.config.productionTip = false


new Vue({
  el: '#app',
  router,
  // 위에서 가져온 컴포넌트를 뷰에게 사용할 것이라고 알려준 뒤
  components: {
    App
  }
  // #app 범위에서 실제로 템플릿으로 사용될 컴포넌트를 지정한다.
  template: '<App/>',

})

또 다른 버전

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import BootstrapVue from 'bootstrap-vue'
import App from './App'
import axios from 'axios'
import router from './router'
import Multiselect from 'vue-multiselect'
import Cleave from 'vue-cleave-component';
import auth from './auth'
import validate from './validate'


// 컴포넌트 이름을 바꿔서 사용하기 위해서 component 등록을 함
Vue.component('multiselect', Multiselect);
// 나머지는 그냥 바로 (전역)컴포넌트 사용
Vue.use(BootstrapVue);
Vue.use(Cleave);

new Vue({
  el: '#app',
  router,
  template: '<App/>',
  components: {
    App
  }
})

뷰 일렉트론 진입점 기본 구조

진입점 구성

일반

보통의 뷰는 다음처럼 진입점을 구성한다.

<!DOCTYPE html>
<html>
  <head>
    <title>일반 Vue </title>
  </head>
  <body>

    <!--뷰 인스턴스가 붙을 html 진입점 표시-->
    <div id="app"></div>

  </body>

  <!--뷰 js 소스 로딩-->
  <script src="http://..../vue.js"></script>

  <!--뷰 인스턴스 생성 및 위에서 만든 html 진입점에 붙이기-->
  <script>
      new Vue({
      el: '#app',
      data: {
          message: '안뇽 vue.js!'
      }
    })
  </script>
</html>

웹팩

웹팩은 나눠서 작성하면 빌드 시에 지가 알아서 묶어주고 조립해주기 때문에 html 코드랑 <script> 등이 분리된다.

HTML

html 이 위치할 곳을 설정하는 웹팩 설정.

/config/index.js


const getProxyServer = () => ({ target: 'http://localhost:8090', changeOrigin: true }); module.exports = { build: { ...... }, dev: { env: require('./dev.env'), // 추가 설정 내용 port: 8080, autoOpenBrowser: true, assetsSubDirectory: 'static', assetsPublicPath: '', proxyTable: { '/apiReq': getProxyServer() }, // CSS Sourcemaps off by default because relative paths are "buggy" // with this option, according to the CSS-Loader README // (https://github.com/webpack/css-loader#sourcemaps) // In our experience, they generally work as expected, // just be aware of this issue when enabling this option. cssSourceMap: true } }

module.exports.dev.assetsSubDirectorystatic 이다. 즉 웹팩으로 서버를 켜고, 해당 서버에 접속하면 static 폴더로 접속한다는 말. 그래서 index.html 을 해당 폴더에 위치시킨다.

/static/index.html

<html>
  <head>
    <title>일반 Vue </title>
  </head>
  <body>

    <!--뷰 인스턴스가 붙을 html 진입점 표시-->
    <div id="app"></div>

  </body>
</html>

의존하는 라이브러리

웹팩이 js 파일을 조립할 때 의존성도 조립할 수 있게 설정을 해준다.

build/webpack.base.conf.js

module.exports = {
  entry: {
    app: ['./src/main.js'],
    vendor: [
      'babel-polyfill',
      'vue',
      'vue-router'
    ]
  },

......

위코드는 웹팩이 조립할 때 vue 라이브러리를 조립하라는 것.

즉, 다음 줄을 위에 내용으로 교체한 것이다.

  <!--뷰 js 소스 로딩-->
  <script src="http://..../vue.js"></script>

실제 자바스크립트 코드

자바스크립트 코드는 어디에 갈까? 바로 위 설정에서 아래 부분이 그것을 결정한다.

module.exports = {
  entry: {
    app: ['./src/main.js'],

실제로 .src/main.js 에 가보면 js 코드가 있다.

      new Vue({
      el: '#app',
      data: {
          message: '안뇽 vue.js!'
      }
    })

원래 html 파일 내부에서 쓰는 거라 <script> 태그가 필요했었다.

  <!--뷰 인스턴스 생성 및 위에서 만든 html 진입점에 붙이기-->
  <script>
      new Vue({
      el: '#app',
      data: {
          message: '안뇽 vue.js!'
      }
    })
  </script>

뷰-일렉트론

주의 : 특정 인이 작업해 놓은 코드를 기준으로 이야기 하고 있으므로 보편성이 결여될 수 있음.

일렉트론->뷰

일렉트론을 먼저 불러오고 일렉트론이 뷰를 불러온다.

웹팩이 index.js 를 불러오면.

.electron-vue/webpack.main.config.js

'use strict'

process.env.BABEL_ENV = 'main'

const path = require('path')
const { dependencies } = require('../package.json')
const webpack = require('webpack')

const BabiliWebpackPlugin = require('babili-webpack-plugin')

let mainConfig = {
  entry: {
    main: path.join(__dirname, '../src/main/index.js')

index.js 에서 일렉트론을 호출하며….

src/main/index.js

'use strict'

import { app, BrowserWindow } from 'electron'

/**
 * Set `__static` path to static files in production
 * https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-static-assets.html
 */
if (process.env.NODE_ENV !== 'development') {
  global.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
}

let mainWindow
const winURL = process.env.NODE_ENV === 'development'
  ? `http://localhost:9080`
  : `file://${__dirname}/index.html`

일렉트론은 자신이 사용할 js 파일을 package.json 의 main 속성에서 읽어온다.

package.json

{
  "name": "vue-elec-prac",
    ......
  "main": "./dist/electron/main.js",
  "scripts": {
    ......
  }
}

html

일렉트론이 화면에 뿌릴것은 html 웹팩 플러그인에 설정이 되어있다.

webpack.renderer.config.js

let rendererConfig = {
    ......
  plugins: [
    new ExtractTextPlugin('styles.css'),
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: path.resolve(__dirname, '../src/index.ejs'),

ejs 는 임베드디 자바스크립트 탬플릿. 아래를 보면 html 웹팩플러그인 설정을 가져다가 쓴다.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>vue-elec-prac</title>
    <% if (htmlWebpackPlugin.options.nodeModules) { %>
      <!-- Add `node_modules/` to global paths so `require` works properly in development -->
      <script>
        require('module').globalPaths.push('<%= htmlWebpackPlugin.options.nodeModules.replace(/\\/g, '\\\\') %>')
      </script>
    <% } %>
  </head>
  <body>
    <div id="app"></div>
    <!-- Set `__static` path to static files in production -->
    <script>
      if (process.env.NODE_ENV !== 'development') window.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
    </script>

    <!-- webpack builds are automatically injected -->
  </body>
</html>

자바스크립트

스크립트는 다음처럼 지정해 준다.

webpack.renderer.config.js

let rendererConfig = {
  devtool: '#cheap-module-eval-source-map',
  entry: {
    renderer: path.join(__dirname, '../src/renderer/main.js')
  },

내용은 아래와 같음. vue, axios 라이브러리 임포트하고 기타 사용할 것들도 임포트를 한다.

import Vue from 'vue'
import axios from 'axios'

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

if (!process.env.IS_WEB) Vue.use(require('vue-electron'))
Vue.http = Vue.prototype.$http = axios
Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  components: { App },
  router,
  store,
  template: '<App/>'
}).$mount('#app')

자존감 – 생명은 눈에 보이지는 않지만 느낄 수는 있다

존중

자존감은 자아존중감, 자아를 존중하는 느낌존중은 높이거나 귀히 여기는 것

내가 최선을 다했어도 남들이 보기에 결과가 나쁘면나를 귀히 여기지 않는다.

심장이 죽어라 뛰고 온 몸의 세포가 당신을 소중하다 말하는데도 존중이 없기에 느낌이 없다.

남들한테 사랑받지 못하는 나는, 나도 사랑하고 싶지 않다.나는 가장 소중한 존재가 되고자 하지만, 나를 소중히 여기지도 않는다.

사람들이 소중하게 여기는 대상이 되어 존중받기 위해 그들이 보는 내가 되려 한다.

시선

부모의 시선, 친구의 시선, 선생의 시선, 연인의 시선,시선이 존재(마음)에 부딪힘이 곧 느낌이다.

느낌이 존재할 때만 나는 내가 존재함을 만난다.내가 존재함을, 내가 소중함을, 내가 귀함을 시선을 통해 만난다.

그래서 우리는 시선을 가지려, 대상을 소유하려 한다.

외로움

내가 소중한 사람임을 느끼기 위해, 대상이 날 소중하게 바라보는 시선을 강요한다.

그리고 대상에게 기대하고 원망하고 좌절한다. 세상에 태어나 나밖에 없다는 사실을 거듭 느낄 때마다 외롭고 살고 싶지 않다.

정말로 나밖에 없다는 사실이 고통스럽다.

수행

느낌을 통해 내가 있음을 알고 싶다. 내가 누구인지 알고 싶다. 나를 만나고 싶다.

어느샌가 나는 괜찮다고 되뇌인다. 타인이 필요 없다고 여기며, 타인이 필요없는 수준의 경지에 이르렀다고 말한다.

타인을 만날 수 없다는 좌절이 타인이 필요없는 세상을 꿈꾸게 하고, 독심술사, 최면술사, 초능력자나 득도한 자와 같이 되려 한다.

내가 대상을 소유하려는 움직임에서 좌절한 까닭에 타인이 내 대단함을 보고 나를 소유하게 하려는 움직으로 향함을 그는 착각을 한다.

내 존재가 그리워 벌어지는 소유의 향연이다. 그것이 정방향이건 반방향이건, 존재의 부재는 소유의 갈망으로 나타난다.

깨어남과 잠듬

영원히 대상을 소유하려함은 대상을 통해 영원히 느끼고 싶다는 말이다.

임사체험, 어떤 극을 돌파하는 시점에서의 만남 등을 통해 우리는 한 번 나를 만난다.

만남이 기뻐 그것을 알았다(얼우다)라며 좋아한다. 그러나 오래 가지 않는다. 그것은 추억이 된다.

이젠 느낌을 소유하려 한다. 그래서 임사체험, 몸얼움, 더 강한 극을향한 수련에 빠진다. 그 찰나의 느낌을 영원히 바라기에 수행을 포기하지 않고 그저 더 외로움으로 빠진다.

그러나 잠듬을 피할 수 없다. 잠을 자지 않으면 죽는다. 내가 사람이기에 내 한계에 부딪힌다. 그리고 수행자는 한계를 밟지 않고 피하려 한다.

느낌

나는 영원히 나를 그리워하고 알고 싶다. 그래서 타인의 시선, 즉 대상을 통해 내가 있음을 그리고 내가 누구인지를 만나기 원한다.

어떤 사람은 대상을 넘어서 느낌 자체를 소유하려 한다. 그는 세상의 큰 진리를 얻은 것처럼 쉬쉬한다. 정말소유했다면 사라지지 않았어야 하는데 자꾸만 사라지니 당혹감을 느끼면서도 레벨을 올리면, 더 득도하면, 한소식을 거듭하면 정말로 소유할 것이라 믿으며, 그 또한 자신을 그리워 한다.

그것이 자존감이다. 내 존재가 있다는 사실이 그토록 그리워서, 우리는 평생을 다른 대상을 통해 갈구하고 절규한다.

그러나 우리가 자신을 의식적으로 갈구하기 전에, 나는 이미 태어나 존재했다.

둘이자 한 덩어리 나

내가 태어나 존재하는데 내가 나를 구한다.

누구의 시선도 없는 골방에서 아무도 모르는 눈물을 흘리는 중에 분명 느낌이 있다. 슬픈 느낌, 억울한 느낌, 또다른 느낌이 있다.

대상도, 시선도 그리고 느낌조차 없는 곳에서 느낌이 존재한다. 아무도 없는 곳에 무엇인가 있다.

누군가는 그것을 성령님이라 말하고, 누군가는 부처라 말하고 누군가는 귀신이라 말한다.

그게 누구건 배고프면 밥을 먹으라, 심심심하면 놀으라, 외로우면 만나라, 슬프면 울라 한다.

내가 태어나 죽을 때까지, 나보다 더 날 사랑하고, 날 살리려는 생명이 거기에 늘 있다.

존재함을 증명할 수는 없지만, 결코 부정할 수 없는 존재가 나다.

느낌이 둘이자 한 덩이리이듯, 나도 둘이자 하나다.

충만함이 내게 늘 거하고 사랑이 늘 날 떠나지 않는다. 우리는 단 한 순간도 사랑 없이 살 수 없기에, 사랑은 한번도날 떠난 적이 없었다.

판과 생명

대상을 통해 내 마음에 나타나는 느낌이, 본디 내 것이었다는 것에 동의한다면,
그리고 느낌을 통해 내가 존재함을 그리워 한다는 것에 동의한다면,

우리의 영원한 외로움이 그리움이자 사랑이고, 사랑이 생명이며, 어떤 모습으로 살아왔던지…
우리가 충분히, 넘치도록 꽉 찬 존재로 공간을 채웠는지 느낌으로 동의하게 될 것이다.

공허해도 괜찮고, 마음이 아파도, 너무 웃겨도 괜찮다.
잠들고 또다시 잠들어도 괜찮다.

그동안 우리는 판이 부서져있다고 생각해서 단단한 판을 찾고자 하고, 판을 문제로 보고 해결하려 하였다.

그리고 이제 우리는 눈으로 보이진 않지만, 단단한 판이 나를 든든디 받치지 않고서야 부서진 판을 보고, 단단한 판을 구하러 여행을 다닐 수 없단 것을 느끼게 됐다.

이제는 보이지 않아도 단단한 판을 느끼며 그저 또다른 여행을 떠나면 그만이다.

단단한 땅 위에서 느낌은 다시 날 찾아오고, 내 그리움으로 다시 사랑을 만나게 될 것이다.

뷰 일렉트론으로 퀴즈 앱 만들어보기

만들 것

기본 일렉트론-뷰의 우측 영역을 퀴즈 영역으로 변경한다.

Vue-CLi 시작

일렉트론-뷰 설치 참조 해서 일렉트론-뷰 혼합 플젝을 설치 및 설정해본다.

**꼭 파워쉘 관리자 권한으로 실행 후 작업을 진행한다!!!*

npm 설치

nodejs 다운 및 설치하면 같이 설치됨

확인

node -v
npm -v

뷰-cli 설치

npm install -g vue-cli

확인

>vue -V
2.9.6

일렉트론 설치 및 설정

옛날버전 npm 쓴다면

npm-windows-upgrade 참조해서 업글

필요한 빌드 툴 설치

npm install --global --production windows-build-tools

웹팩+기타 플러그인 설정된 뷰 생성

사용하지 않는 것도 덕지덕지 붙어있다는 점이 거슬릴 수도 있으나 지금 내게는 누군가 검증해 놓은 것들을 사용할 수 있다는 이점이 있다.

>vue init simulatedgreg/electron-vue

? Generate project in current directory? (Y/n)
... 다 엔터친다.

이제 기본 설정을 다운 받았으니 의존성을 설치할 차례

// npm 의존성들을 설치한다.
> npm install
npm WARN deprecated [email protected]: babili has been renamed to babel-minify. Please update to babel-preset-minify

> [email protected] install C:\Users\jihoon\IdeaProjects\electon-vue-fitbit-toy-project\node_modules\electron-chromedriver
> node ./download-chromedriver.js

successfully dowloaded and extracted!

> [email protected] postinstall C:\Users\jihoon\IdeaProjects\electon-vue-fitbit-toy-project\node_modules\uglifyjs-webpack-plugin
> node lib/post_install.js


> [email protected] postinstall C:\Users\jihoon\IdeaProjects\electon-vue-fitbit-toy-project\node_modules\electron
> node install.js


> [email protected] postinstall C:\Users\jihoon\IdeaProjects\electon-vue-fitbit-toy-project
> npm run lint:fix


> [email protected] lint:fix C:\Users\jihoon\IdeaProjects\electon-vue-fitbit-toy-project
> eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter --fix src test

npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN [email protected] No repository field.
npm WARN [email protected] No license field.
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: [email protected] (node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for [email protected]: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})

added 1517 packages in 67.94s

의존성 설치

파워쉘 관리자 권한 실행후 다음을 실행

npm install

일렉트론 실행할 수 있게 설정

일렉트론을 실행하려면 주 스크립트가 필요하다.

npm 에게 주 스크립트를 읽으라고 알려주기 위해 package.json 으로 이동해서 main 으로 /dist/electron/main.js 스크립트 파일을 쓴다고 해보자.

package.json 은 냉장고 메모 같은 것으로, npm 이 뭘 어떻게 해야할지 알려주는 역할을 한다.

우리는 이미 세팅이 된 것으로 세팅을 했으므로 아래처럼 경로가 적혀있다.

{
  "name": "electon-vue-fitbit-toy-project",
  "version": "0.0.0",
  "author": "JihoonYi <[email protected]>",
  "description": "An electron-vue project",
  "license": null,
  "main": "./dist/electron/main.js",
  ......
  }

일렉트론 실행

요건 관리자 권한이 필요 없으니 일반 터미널에서 해도 된다.

npm run dev

기본 페이지가 나온다.

npm run dev 를 해야 /dist/electron/main.js가 생성된다.

퀴즈 앱 만들기

간단히 구조 파악

일렉트론

dist/electron/main.js

......

var winURL = process.env.NODE_ENV === 'development' ? 'http://localhost:9080' : 'file://' + __dirname + '/index.html';

function createWindow() {
  mainWindow = new __WEBPACK_IMPORTED_MODULE_0_electron__["BrowserWindow"]({
    height: 563,
    useContentSize: true,
    width: 1000
  });

  mainWindow.loadURL(winURL);

  mainWindow.on('closed', function () {
    mainWindow = null;
  });
}
......

NODE_ENV 가 개발이면 로컬호스트로 접속하고 그렇지 않으면 index.html 파일로 접속을 한다. 로컬호스트에는 vue.js 가 돌고 있다.

vuejs 라우터

로컬호스트 접속 시 라우터가 어떤 페이지를 보여줄지 결정한다.

src/renderer/router/index.js

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'landing-page',
      component: require('@/components/LandingPage').default
    },
    {
      path: '*',
      redirect: '/'
    }
  ]
})

  1. 현재 루트(/)경로에 접속 시 components 폴더 내부의 LandingPage.App 을 불러오게 한다.
  2. 그외의 모든(*) 경로로 접속 시 루트 경로로 리다이렉트 시킨다.

LandingPage 수정

우측 영역에 퀴즈 내용을 출력하고 싶다.

<template>
    <div id="wrapper">
        <img id="logo" src="[email protected]/assets/logo.png" alt="electron-vue">
        <main>

            <!-- 좌측 영역 -->
            <div class="left-side">
            </div>

            <!-- 우측 영역 -->
            <div class="right-side">

            </div>
        </main>
    </div>
</template>

우측화면 퀴즈 내용

// quizez 배열에서 퀴즈 하나와 인덱스를 반복해서 꺼내는데, 인덱스가 맞아야 보인다.
<div v-for="(quiz, index) in quizez" v-show="index === questionindex">
    <!-- 퀴즈 분류 출력 -->
    <h1>{{ quiz.category }}</h1>
    <!-- 퀴즈 질문 출력 -->
    <h2>{{ quiz.question }}</h2>
    <!-- 반응: 보기문항을 라디오 버튼으로 표시 -->
    <ol>
        <!--퀴즈 보기문항 표시 -->
        <li v-for="answer in quiz.incorrect_answers">
            <label>
                <!-- 답변[인덱스] 에다가 선택여부가 담기도록 둘을 바인딩한다 -->
                <input type="radio" name="answer" v-model="answers[index]" :value="answer"> {{answer}}
            </label>
        </li>
    </ol>
</div>
<!-- 퀴즈 인덱스가 총 문제 개수를 넘었을 때는 이전/다음을 보여주지 않는다. -->
<div v-if="questionindex < quizez.length">
    <!--  이전 버튼은 질문인덱스가 0보다 클 때만 보여준다. -->
    <!-- 클릭하면 이전 함수가 실행되고 이전 문제가 보여진다. -->
    <button v-if="questionindex > 0" v-on:click="prev">
        이전
    </button>
    <!-- 클릭하면 이전 함수가 실행되고 다음 문제가 보여진다.-->
    <button v-on:click="next">
        다음
    </button>
</div>
<!-- 총 점수를 보여준다, 문제 풀이를 완료하면 -->
<span v-if="questionindex == quizez.length">당신의 총 점수는 {{score}} / {{quizez.length}}</span>

필요한 필드가 무엇인지 알고 있으니 자료를 구조에 담아보자.

const quizQuestions = [
    {
      'category': 'Entertainment: Film',
      'type': 'multiple',
      'difficulty': 'easy',
      'question': 'Who directed E.T.the Extra - Terrestrial (1982)?',
      'correct_answer': 'Steven Spielberg',
      'incorrect_answers':
        [
          'Steven Spielberg',
          'Stanley Kubrick',
          'James Cameron',
          'Tim Burton'
        ]
    },
    {
        ......
    }
]

자료를 뷰에서 쓸 수있게 데이터 바인딩한다.

    data: function () {
      return {
        //  질문 인덱스, 현재 질문을 보여주는데 사용된다.
        questionindex: 0,
        // 위에서 정의된 자료를 quizez 라는 데이터바인딩 변수에 할당한다.
        quizez: quizQuestions,
        // 퀴즈질문개수만큼의 원소를 가진 배열을 생성하고 빈값으로 채워 바인딩.
        answers: Array(quizQuestions.length).fill('')
      }
    },

그럼, 이제 필요한 행위를 보자

methods: {
      // 원래 있던 거, 무시할 것
      open (link) {
        this.$electron.shell.openExternal(link)
      },
      // 다음 문제로 이동(인덱스 늘리기)
      next: function () {
        this.questionindex++
      },
      // 이전 문제로 이동(인덱스 줄이기)
      prev: function () {
        this.questionindex--
      }
    },

자, 이제 데이터 바인딩된 변수가 변경될 때마다 새로 계산이 되는 계산된 속성을 보자.

computed: {
      // 퀘즈 맞힌 정답 계산
      score: function () {
        var total = 0
        for (var i = 0; i < this.answers.length; i++) {
          if (this.answers[i] === this.quizez[i].correct_answer) {
            total += 1
          }
        }
        return total
      }
    }

exe 빌드하기

npm run build

exe 파일을 실행하면, 설치한 후 우리가 만든 화면을 띄운다.

Jetbrains 올팩 할인 구매했다!!!!


젯프레인에서 갑자기 50% 할인이 들어갔다!

상황

고랜드랑 얼티밋 두개를 가졌던 상황인데 이상하게 업글할 때 고랜드는 쳐주지도 않아서 업글할까 말까 하다가 그냥 나중에 사자 하고 미뤄둔 게 3주 전인데!!!


이미 제품을 가지고 있어도, 제품 새로 구매할때인 1년차 구매가격의 50%로 할인을 해준다.

짱구굴리기

얼티밋의 경우는 2년차라서 이미 할인받은 상태라 이것을 1년차 금액의 50%로 구매해봤다 5만원 정도일텐데

차라리 그럴바에는 업글이 나을 거 같아서 진행을 해봤다.

진행 결과

3주 전 가격 122 달러 추금에서

50% 할인되어

68.xx 달러 추금으로!! 업글 겟!!

뒷말

이번달 생활비 엄청 쪼들리겠다. :sob: :joy: