Vue 강의 내용 정리

[router 설계 필요 ]

news
ask
jobs

세개의 페이지가 필요하고
이를 url에 맞게 뿌려주면 됨.

특정 상세 페이지가 따로 있음
사용자 정보 페이지 (user view)

[vue cli 2 vs vue cli 3]

  • 명령어

    • 2.x : vue init ‘프로젝트 템플릿 이름’ ‘파일 위치’
    • 3.x : vue create ‘프로젝트 이름’
  • 웹팩 설정 파일

    • 2.x : 노출 Y WEBPACK.config이 밖으로 나와 있음 WEBPACK 에다 곧바로 수정하면 되었음
    • 3.x : 노출 N WEBPACK.config이 숨겨있음 webpack 조작할 때 별도의 필요한 부분 추가해줘야함
  • 프로젝트 구성

    • 2.x : 깃헙의 템플릿 다운로드
    • 3.x : 플러그인 기반으로 기능 추가
  • ES6 이해

    • 2.x : 필요 없음
    • 3.x : 필요

[ES lint 해제하는 방법]

/*eslint-disable*/ 을 올려두면 해당 코드는 적용 x

[vue의 프로젝트 순서]

  1. vue create 로 생성하여 vue cli 3 를 실행하면 프로젝트 생성
  2. 프로젝트 생성 main.js 가 해당 application의 구조도가 됨
  3. router 를 등록하는 방법
    1) vue-router 를 설치 (npm install vue-router --save)
    2) router 폴더 (src아래에 생성) > index.js 생성
    3) index.js에 url 정보와 components정보를 입력
    4) routes 를 export 시켜주고 main.js에서 import해줌
    5) App.vue에서 <router-view>를 추가해주면 router에서 컴포넌트를 해당
    태그에 뿌려주는 것을 확인할 수 있음

[태그 새롭게 등록]

<!--ToolBar 일 때는 tool-bar 로 바꿔서 사용가능-->
<!--Pascal 케이스는 자동완성 x-->
<!--케밥 케이스는 자동완성 o 그리고 간략 확인가능-->

앵커가 박히게 됨.

[api]

api 를 불러오면 해당 정보들을 배열형태로 받을 수 있음

[this]

vue에서 this를 찍으면 기본적으로 컴포넌트 자체가 찍히게 되는듯하다. 그러나 axios에서 then 을 처리한 후 this를 찍으면 axios가 찍히게 된다.

this 는 기본은 전역 그러나 생성자 함수에서는 그 생성자 함수 비동기 호출 시 this (then 에 나오는 this) 는 undefined이다. (이럴 경우 this binding 해줘야 함) 그러나 arrow func 일 때 this는 최종 생성자 this 를 가리킴 -> 그래서 arrow function 방식을 많이 사용한다. this 바인딩 할 필요가 없으므로

[created]

components 가 생성될 때 생성되는 곳이 created() 이다. ES6 에서

created : fucntion() {

}

를 created()로 사용할 수 있음

lifecycle hook이라고 부르고 이런 것들이 beforeMount, mounted 등등이 있다.

이와 연관된 내용이

나중에 한번 볼 것

[비동기처리, callback, promise]

  • callback 종료되는 시점에 실행되는 함수 (함수로 인자를 넘길 수 있으므로 가능함)

    1) fetchData() 실행
    2) 그 안의 result 배열 생성
    3) ajax 실행 > 데이터 받아옴.
    4) console.log 에 그 결과가 찍힘
    

    이런 순으로 코딩이 되어있는데 1>4 순이 아니라

    1,2,4,3 순으로 실행 됨 먼저 console.log 를 먼저 처리하고 그 다음에 ajax의 처리가 완료됨

    이게 비동기처리의 기본 임

    그러면 console.log를 ajax안에서 실행하면 올바른 순서로 찍힘

    callback hell 에 빠질 수 있음

  • Promise 와 resolve 콜백 관계에서 Promise가 끝나고 resolve를 통해 data 값을 넘겨주게 되는 것임.

Vuex 간단한 구조도

before NewsView 에서 API를 적용해서 호출

after NewsView <- Vuex <- API

중간에 Vuex 를 사용하여 호출할 예정임. 컴포넌트의 관리의 의미가 있음

Vuex는 상태관리도구 컴포넌트 간에 데이터 넘겨줄 때 사용

  • Vuex 를 만들어서 인스턴스를 추가해주면 개발자 도구에서 Vue 탭의 두번째 셀렉션으로 Vuex 를 사용할 수 있게 된다.

  • Vuex에서의 action상태는 dispatch로 가져올 수 있다.

  • Vuex에서의 상태에 대하여. Action : 모든 비동기 호출 값을 가져온다. Mutation : 이를 State에 담기 전 상태임. 여기를 거쳐감. State : 최종 바뀔 값.

    순서는 다음과 같다. Action(API로 부터 값을 받음)

    • mutation (값을 state로 넘겨줌) : 이 과정이 왜 있는지는 강사도 모르고 있음
    • state (여기 안의 변수를 내보낼 수 있음)

    • 이 때, 상태에 따라 데이터 넘겨줄 때 써야하는 용어들이 정해져있음 여기 를 참고하고 action -> mutation 은 commit vue component에서 action에 접근하여 데이터 가져올때는 dispatch를 사용해야함

    위의 전체 상태 값에 대한 코드를 store 에 담아서 export 시켜주면

    main.js에서 store를 import 시켜줬기 때문에 모든 컴포넌트에서 store를 사용할 수 있게 된다.

    그 다음 원하는 컴포넌트에서 this.$store.dispatch(‘actions 안의 함수 이름’) 위와 같이 사용하면 위의 state에 저장한 값에 접근할 수 있게 된다.

Destructuring (구조 분해 문법)

관련링크 위 페이지에 긴 내용이 담겨있음.

var arr = [1, 2, 3, 4];
var obj = {
	a: 10,
	b: 20,
	c: 30,
};

위와 같은 배열, 객체의 선언에서

var { a, b, c } = obj;

위를 실행할 시 a, b, c 에 각각 10,20,30 이 들어가게 된다.

자주 사용하는 방법은 아래와 같다.

var josh = {
	language: 'javascript',
	position: 'front-end',
	area: 'pangyo',
	hobby: 'singing',
	age: '102',
};

var language = josh.language;
var position = josh.position;
var area = josh.area;
var hobby = josh.hobby;
var age = josh.age;

위와 같이 사용하던 것을

var { language, position, area, hobby, age } = josh;
console.log(language); // javascript
console.log(position); // front-end
console.log(area); // pangyo
console.log(hobby); // singing
console.log(age); // 102

위와 같이 사용할 수 있다는 것이다.

vuex를 사용할 때, context.context 사용시 {commit} 으로 간략하게 사용할 수 있게 된다.

vuejs map 헬퍼에 대하여…

  • AskView 부분을 보면 주석으로 this.$store.state.ask 를 어떻게하면 간략하게 사용할 것이냐에 대한 내용을 적어두었음.

그에 대한 설명을 여기에 기록함.

1 함수를 사용함.

단순하다. computed : 아래에 메서드를 하나만들어 거기서 return this.$store.state.ask 을 넣어주면 끝

2 이제부터는 vuex 맵헬퍼 함수를 사용하여 가져올 것이다.

  1. mapState 사용법 map헬퍼함수를 사용할 때는, … (spread 구문을 사용하는 듯하다) 왜 spread 구문이냐 예를 들어
...mapState([
  'count'
])

위와 같이 사용하였다면.. state 내의 모든 존재하는 value들을 검색하여 그 중에 count 를 가져오겠다는 뜻이다.

  ...mapState({
    ask : state => state.ask
  })

여기서는 위와 같이 사용하였는데 => 인자 안에 state 를 넣으면 state의 값은 state 의 모든 값을 다 가져온다. (물론 여기서는 created 될 때, FETCH_ASK 를 호출하였기 때문에 다른 값에는 아무 것도 안들어가지만 state.ask 에만 값이 들어가 있게 됨.) 결과적으로 위 코드에서 ask 라는 프로퍼티 안에 state.ask 값을 넣어준 것과 같이 동작한다.

  1. mapGetter(객체 표기법) index.js 안에
  getters:{
      fetchedAsk(state){
          return state.asks;
      }
  },

위와 같이 getters 를 만들어 fetchedAsk 를 추가한다. getters 란 기본적으로 데이터에 접근할 때 발생한다.

그 때 fetchedAsk 를 발생시켜 state.ask 를 가져갈 수 있도록 해주는 것이다.

    ...mapGetters({
        ask : 'fetchedAsk'
    })

위와 같이 선언하여 index.js 에서 ask 값을 가져온다. 혹은 배열형태로.. …mapGetters([ ‘fetchedAsk’ ]), 위와 같이 사용할 수 있다.

Dynamic Route matching (동적 라우트 매칭)

url 에 ?id=(아이디명) 이거로 다음 페이지로 이동할 수 있다.

라우팅 routes/index.js 에 보면

  {
    path:'/user/:id',
    component: UserView,
  },

위와 같이 설정하고

라우팅 링크를 설정할 때

<router-link v-bind:to="`/user/${item.user}`">{{item.user}}</router-link>

위와 같이 설정 다시말해 v-bind:to=/user/ + id 의 구조로 설정하면

해당하는 곳으로 이동했을 때

vue 탭의 개발자도구로 들어가면 data.$route.params 안에 (여기 객체 값 확인 필요) id 값이 넘어가는 것을 알 수 있다.

위에서 어디는 params 에 넘기고 어디는 query에 넘김 방법론적인 거 나중에 이 방법 저 방법 다 배울 것

  • 다이나믹 라우팅의 순서
  1. 라우터 > index.js에 라우터 정보 등록
  2. 해당 페이지 컴포넌트로 이동했을 때 params Or querys 정보를 다룰 것
  • 데이터 fetch 순서 (vuex 쪽이긴한데 오늘 알게 됨)

    1. vue component 생성
    2. vuex 를 사용하여 dispatch실행 => store의 actions 로 가게 됨
    3. 해당 하는 함수 (FETCH_ITEM) 실행
    4. axios등을 실행하여 데이터 가져옴
    5. 여기 데이터 mutation으로 옮김
    6. mutation에서 state에 셋팅한 정보들을 component에서 사용 가능
  • font awsome 페이지 참고

<link href="/your-path-to-fontawesome/css/all.css" rel="stylesheet" />
<!--load all styles -->
  • html 에 해당하는 content를 가져왔을 때 태그를 인식시켜서 출력시켜줄 경우 v-html 을 사용하면 끝

라우터 트랜지션

라우터에 의해 페이지가 바뀔 때, 좀더 스무스하게 바뀔 수 있도록 할 수 있음

리팩토링

구조를 바꿀 것임. 공통으로 사용되는 부분을 컴포넌트로 뽑아낼 것임 ex) 하나의 글(피드)를 컴포넌트로 만들 수 있음.

  • 일단, style 태그 안에 scoped 속성을 주면 해당 컴포넌트에만 영향을 끼치겠다는 소리임

컴포넌트 만들어서 사용할 때, 명명 규칙의 변화가 됨을 유의할 것. 예를들어, ListItem.vue 로 컴포넌트를 생성해서 가져왔으면 태그는 list-item으로 생성해야함. 대문자로 구분이 - 로 구분이 되고 소문자로 변경됨.

핵심은 분기를 어떻게 해줄 것이냐하는 것임. this.$route; 에 접근할 수 있음. 이 안에 있는 정보, path or name을 통해 분기를 주면 됨

그 외에는 v-if 를 사용하는 방법 설명함. 어떤 애는 있는데 어떤 애는 없으면 이를 판단하여 분기해주면 됨.

사용자 컴포넌트 데이터 흐름

  • 첫번째 방법 userView 안에 userProfile 컴포넌트를 가져왔다. 이 경우

    1. userView에서 fetch dispatch하는 부분에서 데이터 가져옴
    2. 위 데이터를 아래 컴포넌트인 userProfile 에서 사용할 수 있음
    3. this.$store.state.user 를 통해 사용하면 됨
  • 두번째 방법 userView 안에 태그에

    <user-profile :info="userInfo"></user-profile>
    

    위와 같이 :info 안에 데이터를 넘겨준다. (물론 userView 안에 computed 속성에 userInfo 값이 있어야한다.) 그러면 userProfile 안에는 props 로 info 값을 받을 수 있다.

  • 두가지 방법의 비교 직빵으로 가져오는 것과 info 로 보내주고 받는 것.

    1번은 UserView => Vuex => API => Vuex => UserProfile => UserView

    2번은 UserView => Vuex => API => Vuex => UserView => UserProfile => UserView

    조금더 Vuex의 구조에 적합하게 만들려면 표현하는 부분에 바로 땡기는 것이 간편하게 만든 것이기 때문에

    곧바로 가져가는 1번 방법이 낫고

    그러나 구조적으로 의미를 주고 싶으면 2번으로 사용하는게 낫다.

slot 의 사용

UserProfile 에서

<slot name="username">
	<!-- 상위 컴포넌트에서 정의할 영역 -->
</slot>

위와 같이 slot을 뚫어주면

UserVue 에서

<div slot="username">{{ userInfo.id }}</div>

와 같이 사용하면 된다. 그러면 해당 위치에 내가 원하는 정보를 그대로 넘겨줄 수 있다.

언제 사용하는가 ex) ItemView 에서는 info.id 값을 넘겨주는데 UserView 에서는 info.user 값을 넘겨준다고 하면 분기를 만들수도 있겠지만 위와 같이 처리하는게 직관적으로 보기가 좋은듯

router-link 에 물려서 보내줄 수 있음.

spinner

“웹 페이지의 로딩 상태를 나타내는 컴포넌트를 의미합니다.”

  1. Spinner.vue 를 생성한다. 걍구글링해서 복붙함.
  2. javascript
  props: {
    loading: {
      type: Boolean,
      required: true,
    },
  },

props 에 loading 값을 설정할 것, 3. bus 셋팅 “Spinner는 여러 컴포넌트에서 사용할 수 있기 때문에, Event Bus를 이용하는 것이 좋습니다. event bus는 단순히 이벤트를 공유할 수 있도록 하는 bus입니다.” bus.js 생성 (단순 vue 만들어서 export 시켜줌)

  1. bus를 사용하여 App.vue 의 created()와 beforeDestroy() 안에서 사용함.
  created(){
    bus.$on('start:spinner', this.startSpinner);
    bus.$on('end:spinner', this.endSpinner);
  },
  beforeDestroy(){ // 이벤트 등록하고 나면 반드시 종료시켜줘야함.
    bus.$off('start:spinner', this.startSpinner);
    bus.$off('end:spinner', this.endSpinner);
  }
  1. 엘리먼트 created() 에서 페이지 생성 될 때 해당 로딩 화면 보여지도록 수정
bus.$emit('start:spinner'); // start:spinner 발생 (이때 부터 로딩값 보여줌)
setTimeout(() => {
	// setTimeout 은 로딩을 인위적으로 보도록 만든 것임
	this.$store
		.dispatch('FETCH_JOBS') // 데이터받아와서
		.then(() => {
			bus.$emit('end:spinner'); // 정상적으로 받아오면 spinner 종료
		})
		.catch(err => {
			console.log(err);
		});
}, 1000);

하이오더 컴포넌트 (HOC)

“하이오더 컴포넌트는 컴포넌트의 로직(코드)을 재사용하기 위한 고급기술” 위의 spinner를 만든다 하면, 모든 엘리먼트 파일에 해당 동일한 코드를 추가해줘야했다.

이를 공통 컴포넌트로 만들어서(하이오더 컴포넌트) 언제든 재사용하게 만들겠다는 것이다.

  1. CreateListView.js 이게 하이오더 컴포넌트라 할 수 있음.

  2. router 쪽에서 사용된 component의 갯수가 적어짐. 그냥 CreateListView 만 쓰면 됨.

컴포넌트의 구조가 어떻게 되는가. NewsView > ListView(hoc로 생상된 컴포넌트) > ListItem

Mixin

“공통으로 사용하고 있는 로직, 기능들을 재사용하는 방법”

하이오더 컴포넌트의 단점은 NewsView > ListView(hoc로 생상된 컴포넌트) > ListItem

Level이 깊어짐. 통신에 있어서 불편함이 발생할 수 있음.

Mixin을 사용하면

  1. 코드가 깔끔해짐.
  2. spinner의 효율이 좋아짐.

데이터 호출 시점

1. 컴포넌트 라이프 사이클 훅

  • created() 인스턴스(컴포넌트)가 생성되자 마자. 호출되는 로직들. data 관찰, computed 속성, methods 접근 가능 화면에 mounted 에 접근은 안됨

‘컴포넌트 생성되자마자’ 타이밍이 핵심!

2. 라우터 네비게이션 가드.

참고

router.beforeEach(fucntion (to, from, next){
  // to: 이동할 url
  // from : 현재 url
  // next : to에서 지정한 url로 이동하기 위해 꼭 호출해야하는 함수
})

이런식으로 라우팅 되기전 시점에 호출할 수 있음.

vue-router 공식 문서 > advanced

  • 특정 “url로 접근하기 전”에 동작을 정의! 시점은 라우터 네비게이션 가드가 먼저임.

  • 라이프 사이클 훅의 문제점. 해당 내용이 update되기 전에 그 전의 내용을 먼저 보여줬다가 그다음 내용이 뜸.

    왜 그러냐면 vuex(store) > state 에서 list라는 데이터를 공유하고 거기에 덮어쓰기를 반복하기 때문임. (news, jobs, ask 따로 쓸 때는 괜찮음)

라우터 네비게이션 가드 사용법

  component: NewsView,
  beforeEnter: (to, from, next) => {
    console.log(to);
    console.log(from);
    console.log(next);
    next();
  },

beforeEnter 를 사용하면 url 이동하기 전에 동작을 줄 수 있다. 여기서 auth 같은걸로 구분해서 if문 걸어서 못가게 만들 수도 있음.

  • 라우터 네비게이션을 사용하게 되면 반드시 그 중간중간에 기다릴 수 있도록 인지시키는 도구가 필요하다.

  • spinner
  • progress bar
  • loading bar 등등이 되어야 함!

async await

함수들의 단축, 가독성 증가 함수 앞에 async 를 붙이면 그 안에 함수 호출할 때, await 를 사용할 수 있음.

async await 의 예외처리는 try - catch 로 사용

handleException(error) 를 만들어서 사용하는 경우도 있다.

외부 라이브러리 모듈화

  • 이유 Vue.js 관련 라이브러리가 없을 때 일반 라이브러리를 결함할 수 있어야함. ※ state of js 페이지가 있음

  • 종류
    1. 차트
    2. 데이트 피크
    3. 테이블
    4. 스피너
  • chart.js

    1. 차트 라이브러리 npm 설치
    2. 설치된 라이브러리를 import 로 App.vue 에서 로딩
    • import 시킬 때 import Chart from “chart.js/auto”; 위와 같이 /auto 를 붙여줘야한다. (안그러면 딴거 참조해오기 때문에 에러남. 버전에 따른 문제라고 함.)
    1. mounted() 라이프 사이클 훅에서 차트를 그림 여기 사용할 때 script DOM 에 접근해야하므로 mounted 된 후에 사용해야 함.
    2. 차트를 컴포넌트화 시킴
      1) component 등록
      2) 해당 컴포넌트에 원하는 대로 setting
      3) App.vue 에서 import 시켜서 가져옴

      reference 속성

       document.getelementById('app');
       document.querySelector('#app');
       $('#app');
      

      vue에서 제공하는 위의 기능

       <div ref="app" id="app">hello </div>
      

      script안에 this.$refs.app; (이렇게 사용하면 됨); reference를 사용하면 좋은점. DOM 으로 변환될 때 사용 안됨. 그러므로 중복 값을 만들어도 사용 가능함!

    3. 컴포넌트의 플러그인화 매번 컴포넌트 가져올 때마다 chart.js 라이브러리를 불러와야함. 라이브러리를 플러그인으로 만들 수 있음. vuejs 플러그인 공식 문서 Vue.use(라이브러리 명) - 뷰에서 제공하는 플러그인을 가져온 경우를 말함
    • Vue.prototype.$_Chart = Chart;
      에 대한 의미.
      모든 컴포넌트에서 쓰는 $_Chart 는 여기 chart.js 에서 가져오는 것이다. ($_Chart는 공식문서 권고 표기방법)
    1. 컴포넌트 통신을 이용한 컴포넌트 기능 결합 v-bind:propsdata="..." 이런식으로 해서 넘겨줄 수 있고 이벤트에 대해선

    1) 컴포넌트에서

    onclick: fucntion() {this.$emit('refresh:chart');}
    
    1. App.vue 에서 v-on:refresh:chart="refreshChart"
    2. App.vue methods: 안에서 refreshChart 정의

컴포넌트 디자인 패턴

4가지 패턴

  1. Common - 기본적인 컴포넌트 등록 방식, 컴포넌트 통신
  2. Slot - 마크업 확장이 가능한 컴포넌트
  3. Controlled - 결합력 높음
  4. Renderless - 데이터 처리 컴포넌트

1) Common (지금까지 살펴봤던 방식과 같음)

  • 컴포넌트 설정
    App
    • AppHeader
    • AppContent

v-bind:변수명 으로 전달

  • props 를 통해서 전달할 때는
props{
  title: 'String',
}

이런식으로 타입을 지정할 수 있음. (속성=기본적인 속성)

2) slots

slot은 컴포넌트와 다르게 사이에 텍스트가 올 수 있음. 보통 컴포넌트는 사이에 아무것도 올 수 없었음. 그러나 slot에는 아이템1 이런식으로 올 수 있음.

[1] props에서 사용할 때는 (차이점을 보기위해..)
  data() 안에서 ```items: ['1', '2', '3', '4'];``` 이렇게 하고
<item v-for="item in items" :propsData="item"></item>
  //    item.vue 로 가서
  item props:['propsData']  //로하고
  {{propsData}} //를 사용했음.
[2] slot에서 사용할 때는

data 필요 없음

//item.vue 안에서 <slot> 여기는 정의할 때마다 계속 정의 됨 </slot>

약간 치환된다고 보면 됨. slot => 내용으로
만약에 리스트를 찍어주다가
어떤 요청.,. 난 이 경우는 그림을 보여주고 싶어 그러면
굳어진 컴포넌트라면 불가능 함.

쇼핑몰 상품, 모달 같은 것들을 맘대로 확장가능 스타일도 넣을 수 있음. 상당히 동적인처리가 가능해짐.

[3] controlled
자 정리해보면, props 방식의 문제를 알아야 함.
상위 컴포넌트 => props로 전달 => 하위 컴포넌트에서 props 변경 => 에러발생
상위 컴포넌트 => v-model 로 연결 => 하위 컴포넌트에서 value 와 input 이벤트 => 이벤트 발생 시 toggle 메서드 생성
[4] Renderless
  • render() function 에 대한 내용
  render: fucntion(createElement){
    # return createElement('태그이름', '태그 속성', '하위 태그 내용');
    return createElement('p', 'Hello Vue');
  }

  render: h=>h(App) // 에 대한 내용 위의 코드의 원리대로
  render: fucntion(createElement){
    # return createElement('태그이름', '태그 속성', '하위 태그 내용');
    return createElement(App);
  }

  createElement : h로 (hypertext)
여기 createElement 는 Virtual DOM 을 의미하는데 vue의 창시자 EvanU 에 의해 hypertext의 약자 h로 사용
  render: h=> h(App);
  //이렇게 되는 것임.

  //하위 데이터를 상위로 보낼 수 있음.
  render() {
      this.$scopedSlots.default ({
      response: this.response,
      loading: this.loading
    })
  }
위와 같이 정의하면

상위 컴포넌트에서
<div slot-scoped="{response, loading}">
	<!-- 안에서 해당 데이터를 받아올 수 있음. --></div
>

CLI 를 통한 배포

npm run build dist 폴더 밑에 hosting 할수 있는 파일이 생성됨.

Netlify 을 이용한 배포

bae directory 만 설정해서 배포하면 해당 사이트가 잘 나옴.


그런데, url/(path) 이렇게 url 이 지정될 경우
페이지를 찾을 수 없다고 뜬다.
딱 url로만 들어가야 잘 뜬다.
왜냐면 서버에서 해당 path url을 알 길이 없기 때문이다.
이에 대해서 설정을 해줄 수 있는 파일이
public/_redirects 에서 설정해줄 수 있다.


위 파일에
Netlify settings for single-page application
/* /index.html 200

를 추가해준다.

Netlify설정 위 링크 Netlify 에 배포할 때 설정 값임.

위와 같이 추가하면 그 뒤의 route 정보를 가져가서 확인하는듯하다.

env 파일

어플리케이션에 공통으로 접근할 수 있는 변수를 만들겠다는 것임.
1) 전역 파일 .env 파일을 만든다.
2) APP_TITLE=HELLO 이렇게 만들고
3) App.vue 에서

//created() 안에
console.log(process.env.APP_TITLE); //을 넣어서 실행
//=> undefined 이 뜬다.


4) 원래는 dotenv 라는 패키지 가져와서, 뭐 어쩌구 저쩌구해서 정의해야만 사용할 수 있었음.
5) vue cli 3 이상부터 지원하는 기능이 있음. 앞에 VUE_ 를 붙이면 됨.
6) console.log(process.env.VUE_APP_TITLE); 를 사용하면 끝!