20년전에 드림위버 그리고 그냥 javascript, flash 등으로 웹싸이트만들어서 판매한 경험이 있습니다.. ㅎㅎ 정말 오래되었고 녹슬었죠.
최근에 앱 만들어야 하는 상황이 생겨서 다시 꺼내들게 되었습니다.
전문 프론트엔드 개발자도 아니고 그냥 빠르게 개발할 필요가 있어서 learning curve 가 React 보다 좀 더 덜한 Vue를 사용하게 되었습니다.
아래의 내용은 제가 아는 내용을 쓴 것이 아니라 Developing a Single Page App with Flask and Vue.js 페이지에서 직접 해보면서 다시 정리한 내용입니다. (내용은 거의 모두 같습니다.)

1. Setting Up

1.1 Vue CLI 설치

sudo npm install -g @vue/cli

1.2 Vue Project 생성

vue-app 이라는 이름을 프로젝트를 만들려면 vue create 명령어 사용

$ vue create vue-app

? Please pick a preset: (Use arrow keys) 
   ❯ Manually select features  (선택)
? Check the features needed for your project: 
   ❯ ◉ Router  (스페이스 눌러서 추가. 엔터키는 다음으로 그냥 넘어감)
? Choose a version of Vue.js that you want to start the project with (Use arrow keys)  
   ❯ 2.x
? Use history mode for router? (Requires proper server setup for index fallback in production) (Y/n) 
   ❯ Y
? Pick a linter / formatter config: 
   ❯ ESLint + Airbnb config 
? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection) 
   ❯ ◉ Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? 
   ❯ In package.json 
? Save this as a preset for future projects? (y/N)
   ❯ N

설치 완료후 vue-app 디렉토리로 이동.
서버를 실행합니다.

$ cd vue-app
$ tree -I node_modules
├── babel.config.js
├── package.json
├── public
│         ├── favicon.ico
│         └── index.html
├── README.md
└── src
    ├── App.vue
    ├── assets
    │         └── logo.png
    ├── components
    │         └── HelloWorld.vue
    ├── main.js
    ├── router
    │         └── index.js
    └── views
        ├── About.vue
        └── Home.vue
File Path Description
public/index.html Vue Application의 시작 포인트이며 <div id="app"></div> 에 HTML이 추가가 됩니다.
src/main.js Vue App 의 entry point 로서 new Vue(...) 으로 Vue를 초기화 합니다.
src/App.vue root component로서 다른 모든 components들이 여기에서부터 렌더링 됩니다.
src/components UI Components가 존재합니다.
src/views components 디렉토리와 동일하나, src/router/index.js 에서 주로 사용합니다.
src/router/index.js URLS 과 components를 맵핑 시킵니다.
src/assets statis assets (이미지, 폰트 등등)을 저장합니다.
$ npm run serve

http://localhost:8080/ 에 들어가면 sample vue page가 떠 있는것을 확인 할 수 있습니다.

1.4 ESLint

프로젝트 최상위에 .eslintrc.js 파일을 만들어서 다음을 넣습니다.

module.exports = {
  extends: ['plugin:vue/essential', "@vue/airbnb"],
  rules: {
    'no-console': 'off',
  }
};

extends 에는 다음의 같은 옵션들을 넣을 수 있으며, 위일수록 강제력이 강합니다.

  1. plugin:vue/essential : 모든 룰들을 강제함
  2. plugin:vue/strongly-recommended : Readibility 를 높입니다.
  3. plugin:vue/recommended : 최소화

1.3 Flask

sudo pip install --upgrade flask flask-cors

app.py 를 만들고 python app.py 로 실행합니다.
Flask는 자세한 설명은 생략하겠습니다.

다만 CORS 라이브러리가 사용되었는데, 해당 라이브러리는 cross-origin requests를 처리할 수 있도록 도와줍니다.
Cross-origin requests란 서버와 다른 domain 주소, IP Address, Port 에서 요청한 reqeusts 를 의미 합니다.
해당 예제에서는 동일한 localhost 에서 요청한 requests 더라도, port 가 서로 다르기 때문에 CORS 에러가 발생합니다.

from flask import Flask, jsonify
from flask_cors import CORS

app = Flask(__name__)
app.config.from_object(__name__)

# Enable CORS
CORS(app, resources={r'/api/*': {'origins': '*'}})


@app.route('/api/names', methods=['GET'])
def api_names():
    return jsonify({'names': ['사나', '미나', '나연', '정연']})


if __name__ == '__main__':
    app.run(port=5000)

2. Tutorial

2.1 views/Hello.vue

views/Hello.vue 에

<template>
  <div>
    <h1>처음 만드는 앱</h1>
    <p></p>
    <ul>
      <li v-for="name in names" v-bind:key="name"></li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'Hello',
  data() {
    return {
      msg: 'Hello! Anderson!',
      names: ['창민', '정아', '윤서', '윤아'],
    };
  },
};
</script>

2.2 src/router/index.js

router/index.js 에서는 VueRouter를 초기화하며, URLS 과 특정 view component를 연결시킵니다.

import Vue from 'vue';
import VueRouter from 'vue-router';

Vue.use(VueRouter);

const routes = [
  {
    path: '/hello',
    name: 'Hello',
    component: () => import('../views/Hello.vue'),
  },
];

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL, // "/" 와 동일
  routes,
});

export default router;

2.3 src/App.vue

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

http://localhost:8080/hello 에 접속하면 Hello! Anderson! 그리고 이름 리스트가 보일겁니다.

2.4 Asynchronous HTTP Call to Flask App

axios를 설치합니다.

$ npm install axios

Hello.vue 의 script 부분을 다음과 같이 수정합니다.

<script>
import axios from 'axios';

export default {
  name: 'Hello',
  data() {
    return {
      msg: 'Hello! Anderson!',
      names: ['창민', '정아', '윤서', '윤아'],
    };
  },
  methods: {
    get_names() {
      const path = 'http://localhost:5000/api/names';
      axios.get(path)
        .then((res) => {
          this.names = res.data.names;
        })
        .catch((error) => {
          console.log(error);
        });
    },
  },
  created() {
    this.get_names();
  },
};
</script>

flask 백엔드 서버가 켜져 있는 상태에서 http://localhost:8080/hello 접속하면 아래의 그림처럼 보일 것 입니다.

2.3 Bootstrap

먼저 bootstrap을 설치합니다.

npm install bootstrap

src/main.js 에 bootstrap.css 를 추가시킵니다.

import Vue from 'vue';
import App from './App.vue';
import router from './router';
import 'bootstrap/dist/css/bootstrap.css';
... 생략

src/App.vue 에 style 을 추가 합니다.

<style>
#app {
  margin-top: 50px;
  margin-left: 50px;
}
</style>

views/Hello.vue 에 Bootstrap 이 잘 되는지 버튼을 넣어 봅니다.

...생략 HTML 어딘가
<button type="button" class="btn btn-primary">버튼!</button>
...생략

3. Veutify

3.1 Setting Up Veutify

vue create my-app 을 할때 ESLint + Airbnb config 선택하면 veutify에서 문제가 생기고 있다.
ESLint + Prettier 로 진행하면 됨

$ vue create my-app
$ cd my-app
$ vue add vuetify

.eslintrc.js 파일은 다음과 같이 수정합니다.

  • extends에 “plugin:vue/recommended” 를 삭제 했습니다. -> Pycharm 과 잘 안맞습니다.
module.exports = {
    root: true,
    env: {
        node: true,
    },
    extends: ["eslint:recommended", "@vue/prettier", "plugin:vue/base"],
    parserOptions: {
        ecmaVersion: 12,
        parser: "babel-eslint",
    },
    rules: {
        "no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
        "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
    },
};

src/plugins/vuetify.js 는 다음과 같이 수정합니다.

import Vue from "vue";
import Vuetify from "vuetify/lib/framework";
import "vuetify/dist/vuetify.min.css";

Vue.use(Vuetify);

const opts = {};

export default new Vuetify(opts);