웹사이트 검색

Ubuntu 20.04에서 Nginx로 Vuetify 앱을 게시하는 방법


저자는 Write for DOnations 프로그램을 선택했습니다.

소개

구성 요소는 최신 프런트 엔드 개발의 핵심 기능입니다. 구성 요소는 일반적으로 다음 두 부분을 포함하는 코드 조각입니다.

  • 구성 요소의 논리: 구성 요소가 할 수 있는 것
  • 템플릿: 사용자가 웹 애플리케이션과 상호 작용하는 방식

앱을 구성 요소로 구성하면 최신의 강력한 웹 애플리케이션을 작성하는 데 도움이 됩니다. Vue.js는 다양한 종류의 구성 요소를 개발하는 데 도움이 될 수 있으며 개발자가 널리 사용합니다.

그러나 이러한 프레임워크의 문제점 중 하나는 입력 텍스트 필드와 같은 단순한 항목에 대해서도 많은 구성 요소를 생성해야 한다는 것입니다. 따라서 보다 간소화된 접근 방식은 필요에 따라 선택하고 사용할 수 있는 미리 만들어진 구성 요소가 있는 구성 요소 라이브러리를 사용하는 것입니다. 구성 요소 라이브러리를 사용하면 CSS 디자인, 색상, 크기 및 글꼴에 대해 걱정할 필요 없이 기능에 집중할 수 있습니다.

Vue.js에는 머티리얼 디자인 원칙이 있습니다. Vuetify는 고도로 구성 및 사용자 정의가 가능합니다. 필요에 맞게 구성 요소를 수정할 수 있으며 브랜드 스타일을 기반으로 일관된 구성 요소 라이브러리를 갖도록 고유한 테마를 설정할 수 있습니다.

이 튜토리얼에서는 Vuetify를 기반으로 할 일 앱을 만들고 Vue 앱을 배포하는 데 필요한 리버스 프록시로 Nginx를 사용하여 게시합니다.

참고: Vue는 프런트 엔드 프레임워크이므로 이 튜토리얼을 위해 만든 앱은 브라우저에서 실행됩니다. 그러나 인증 또는 데이터 지속성과 같은 추가 기능을 위해서는 백엔드가 필요합니다. 이 백엔드 기능을 정의하거나 개발하는 것은 이 문서의 범위를 벗어납니다.

전제 조건

이 자습서를 따르려면 다음이 필요합니다.

  • 루트가 아닌 sudo 사용자가 있는 Ubuntu 20.04 서버 1개. 시작하려면 Ubuntu 20.04용 초기 서버 설정 가이드를 따르십시오. 이 자습서에서 루트가 아닌 사용자는 sammy입니다.
  • Nginx가 설치되었습니다. Ubuntu 20.04에 Nginx를 설치하는 방법 튜토리얼의 1-3단계를 따라 수행할 수 있습니다.
  • 완전히 등록된 도메인 이름. 이 자습서에서는 전체적으로 your_domain을 사용합니다. Namecheap에서 도메인 이름을 구입하거나 Freenom에서 무료로 얻거나 선택한 도메인 등록 기관을 사용할 수 있습니다.
  • Node.js(최소 v14.0.0)가 설치되었습니다. 운영 체제에 대한 Node.js 설치 및 로컬 개발 환경 생성 방법 자습서를 따르면 됩니다.
  • Vue Vue.js로 웹사이트를 개발하는 방법에서 찾을 수 있는 Vue.js에 익숙합니다.

1단계 — Vue 애플리케이션 설정

이 단계에서는 Vue.js 애플리케이션을 구성합니다. Vue.js에는 프로젝트의 상용구를 만드는 데 사용할 수 있는 클라이언트가 있으며 이는 처음부터 시작하는 좋은 방법입니다.

다음 명령을 사용하여 Vue.js 클라이언트를 전체적으로 설치하는 것으로 시작합니다.

  1. sudo npm install -g @vue/cli

다음으로 버전을 확인합니다.

  1. vue --version

이 튜토리얼이 작성되었을 때 최신 버전은 5.0.8이었습니다.

Output
@vue/cli 5.0.8

이제 @vue/cli를 설치했으므로 이를 사용하여 vuejs 애플리케이션을 만들 수 있습니다. 이 튜토리얼에서 앱 이름은 vuetify-meets-nginx-app이지만 원하는 이름으로 변경할 수 있습니다.

앱을 만들려면 다음 명령을 실행합니다.

  1. vue create vuetify-meets-nginx-app

이 명령은 대화식이며 여러 옵션이 있습니다. 이 자습서에서는 Vue 2에 대한 Default 옵션을 선택합니다.

Output
Vue CLI v5.0.8 ? Please pick a preset: (Use arrow keys) Default ([Vue 3] babel, eslint) ❯ Default ([Vue 2] babel, eslint) Manually select features

경고: 이 글을 쓰는 시점에서 VuetifyVue.js v3를 지원하지 않습니다. Vuetify를 Vue.js v3에 추가하려고 하면 다음 오류가 표시됩니다.

Output
Error: you cannot call "get" on a collection with no paths. Instead, check the "length" property first to verify at least 1 path exists.**

자세한 내용은 Vuetify 로드맵을 참조하십시오.

애플리케이션이 생성되면 Vue가 파일과 디렉토리를 생성했음을 알 수 있습니다.

├── README.md
├── babel.config.js
├── jsconfig.json
├── node_modules
├── package-lock.json
├── package.json
├── public/
      ├── favicon.ico
      └── index.html
├── src
        ...
└── vue.config.js

간략한 개요는 다음과 같습니다.

  • babel.config.js: Babel은 Javascript 컴파일러이며 이 파일은 해당 동작을 정의합니다. 이는 최종 애플리케이션을 실행, 빌드 및 생성하는 데 필요합니다.
  • jsconfig.json: 이 파일은 앱을 컴파일하는 데에도 필요합니다. 예를 들어 이 파일은 Javascript 코드의 버전을 기본값인 ECMAScript 2009(ES5)로 설정합니다. 자세한 내용은 이 문서를 확인하세요.
  • node_modules: 설치 및 구성된 모든 라이브러리를 포함하는 디렉토리.
  • package.json: 앱의 기본 구성 파일입니다. 여기에서 앱을 실행하거나 빌드하는 데 사용할 수 있는 종속성 및 명령에 대한 정보를 볼 수 있습니다.
  • package-lock.json: 앱에서 사용하는 모든 종속성의 덤프 파일입니다. 이 파일은 npm을 사용하여 다른 노트북이나 서버에 앱을 설치하려는 경우에 특히 유용합니다.
  • public: 여기에 npm run serve 명령이 앱을 게시하는 데 필요한 기본 코드가 있습니다. @vue/cli 명령으로 생성됩니다.
  • vue.config.js: Vue 구성 파일.

src 폴더에는 다음 파일과 디렉터리가 표시됩니다.

src
├── App.vue
├── assets
│ ├── logo.png
│ └── logo.svg
├── components
│ └── HelloWorld.vue
├── main.js

간략한 개요는 다음과 같습니다.

  • App.vue: Vue.js 애플리케이션의 최상위 구성 요소입니다. 다른 모든 구성 요소는 여기에 정의된 구성 요소 안에 있습니다.
  • assets: 이미지, CSS 파일 및 글꼴과 같은 모든 자산을 여기에 배치해야 합니다.
  • components: 여기에는 생성한 모든 구성 요소가 포함됩니다. @vue/cli 명령은 HelloWorld.vue라는 것을 생성했습니다.
  • main.js: 앱 기본 파일입니다. 라이브러리나 플러그인을 구성해야 하는 경우 이 파일입니다. Vue 앱도 만들었습니다.

이제 vuetify-meets-nginx-app 디렉토리로 이동할 수 있습니다.

  1. cd vuetify-meets-nginx-app

개발 모드에서 애플리케이션을 시작하려면 다음 명령을 실행합니다.

  1. npm run serve

출력은 다음과 같습니다.

Output
INFO Starting development server... DONE Compiled successfully in 27235ms App running at: - Local: http://localhost:8080/ - Network: unavailable Note that the development build is not optimized. To create a production build, run npm run build.

개발 서버가 시작되면 localhost:8080으로 이동하여 애플리케이션을 확인합니다.

참고: 원격 서버에서 튜토리얼을 따르는 경우 포트 전달을 사용하여 브라우저에서 앱을 볼 수 있습니다. 포트 8080이 서버에서 열려 있는지 확인하십시오. 개발 서버가 계속 실행되는 동안 로컬 컴퓨터에서 다른 터미널을 열고 다음 명령을 입력하여 포트 포워딩을 시작합니다.

  1. ssh -L 8080:localhost:8080 your_non_root_user@your_server_ip

서버에 연결되면 로컬 컴퓨터의 웹 브라우저에서 http://localhost:8080으로 이동합니다. 이 자습서의 나머지 부분에서는 두 번째 터미널을 열어 둡니다.

이 단계에서는 Vue.js 애플리케이션을 만들었습니다. 다음으로 Vuetify를 프로젝트에 추가합니다.

2단계 — Vuetify를 Vue 앱에 통합하기

이 단계에서는 Vue.js 앱에 Vuetify를 추가합니다.

Vuetify와 같은 구성 요소 라이브러리가 없으면 divbutton과 같은 HTML 입력을 사용하고 웹 앱용 CSS를 디자인하고 원하는 경우 고유한 구성 요소를 만들어야 합니다. 일부 재사용 가능한 블록. 그러나 Vuetify 라이브러리를 사용하면 원하는 구성 요소를 가져와 템플릿에 추가하기만 하면 됩니다.

Vuetify는 또한 사용자 정의가 가능합니다. 예를 들어 테마를 가질 수 있습니다. 한 가지 테마는 색상 팔레트, 사용자 지정 화면 크기 및 글꼴과 같은 항목을 포함하는 CSS 라이브러리입니다. 예를 들어 기본 색상이 파란색인 경우 해당 방식으로 Vuetify를 구성할 수 있으며 CSS 클래스 기본을 사용할 때마다 Vuetify 파란색을 사용하게 됩니다. Vuetify 기능 가이드에서 테마에 대한 자세한 정보를 찾을 수 있습니다.

Vuetify 추가를 시작하려면 개발 서버가 실행 중인 터미널에 CTRL+C를 입력하여 이전 단계에서 시작한 개발 서버를 종료합니다.

다음으로 vuetify-meets-nginx-app 디렉터리에서 다음 명령을 실행합니다.

  1. vue add vuetify

이 명령은 Vue.js 클라이언트를 사용하여 Vuetify를 설치합니다.

사전 설정 옵션 목록에서 기본 구성을 선택합니다.

Output
📦 Installing vue-cli-plugin-vuetify... added 38 packages, and audited 39 packages in 2s 7 packages are looking for funding run `npm fund` for details found 0 vulnerabilities ✔ Successfully installed plugin: vue-cli-plugin-vuetify ? Choose a preset: (Use arrow keys) Configure (advanced) ❯ Default (recommended) Vite Preview (Vuetify 3 + Vite) Prototype (rapid development) Vuetify 3 Preview (Vuetify 3)

몇 분 후에 개발 서버를 다시 시작할 수 있습니다.

  1. npm run serve

localhost:8080으로 이동하면 새로운 "vuetified\ 스타일의 애플리케이션을 볼 수 있습니다.

이 시점에서 기본 앱을 만들고 스타일링을 위해 Vuetify를 추가했습니다. 다음으로 추가 기능이 포함된 할 일 앱을 만듭니다.

3단계 - To-Do Vuetify 앱 만들기

이 단계에서는 할 일 앱을 만듭니다. 할 일 앱은 몇 가지 기본 기능이 필요한 작업 목록입니다.

  • 새 작업을 추가하는 방법
  • 완료로 표시하는 방법
  • 사용자가 대기 중인 항목을 볼 수 있도록 표시하는 방법입니다.

이러한 기능을 앱에 추가하려면 애플리케이션의 최상위 구성 요소인 App.vue 파일을 수정해야 합니다. 다른 모든 구성 요소는 여기에 정의된 구성 요소 안에 있습니다.

메모:

src/App.vue로 이동하고 nano 또는 선호하는 텍스트 편집기를 사용하여 편집할 수 있도록 엽니다.

  1. cd src
  2. nano App.vue

기본 코드는 다음과 같습니다.

<template>
  <v-app>
    <v-app-bar
      app
      color="primary"
      dark
    >
      <div class="d-flex align-center">
        <v-img
          alt="Vuetify Logo"
          class="shrink mr-2"
          contain
          src="https://cdn.vuetifyjs.com/images/logos/vuetify-logo-dark.png"
          transition="scale-transition"
          width="40"
        />

        <v-img
          alt="Vuetify Name"
          class="shrink mt-1 hidden-sm-and-down"
          contain
          min-width="100"
          src="https://cdn.vuetifyjs.com/images/logos/vuetify-name-dark.png"
          width="100"
        />
      </div>

      <v-spacer></v-spacer>

      <v-btn
        href="https://github.com/vuetifyjs/vuetify/releases/latest"
        target="_blank"
        text
      >
        <span class="mr-2">Latest Release</span>
        <v-icon>mdi-open-in-new</v-icon>
      </v-btn>
    </v-app-bar>

    <v-main>
      <HelloWorld/>
    </v-main>
  </v-app>
</template>

<script>
import HelloWorld from './components/HelloWorld';

export default {
  name: 'App',

  components: {
    HelloWorld,
  },

  data: () => ({
    //
  }),
};
</script>

모든 구성 요소에는 템플릿(일반적으로 HTML 코드)과 Javascript로 작성된 기능이 포함된 스크립트의 두 부분이 있습니다.

템플릿은 최종 사용자가 브라우저에서 보게 되는 것이며 사용자가 애플리케이션과 상호 작용하는 방식을 결정합니다. 일반적으로 템플릿에서 사용할 구성 요소를 가져와야 하지만 Vuetify를 플러그인으로 설치했기 때문에 모든 구성 요소를 명시적으로 가져오지 않고도 템플릿에서 사용할 수 있습니다.

템플릿 블록에는 많은 v- HTML 태그가 있습니다. HTML에 대한 비표준이지만 이러한 태그는 Vuetify 구성 요소이며 항상 v-로 시작합니다.

템플릿에는 현재 다음이 있습니다.

  • v-app: 웹사이트 본문에 첨부되는 주요 구성 요소
  • v-app-bar: 기본 사이드바입니다.
  • v-img: 이미지를 로드하는 구성 요소입니다.
  • v-icon: 아이콘을 보여주기 위한 컴포넌트.
  • v-spacer: 다음 구성 요소를 오른쪽으로 정렬하는 구성 요소.

App.vue 파일의 script 블록과 관련하여 Vuetify 설치는 여기에 코드를 추가하지 않으므로 가지고 있는 것은 Vue에 의해 생성된 시작 코드입니다. cli 명령과 Vue 구성 요소에 필요한 최소 코드입니다.

App.vue 파일의 기본 코드를 살펴보았으므로 이제 할 일 앱을 만들 준비가 되었습니다. 첫 번째 단계는 사용하지 않을 기본 코드를 제거하는 것입니다.

App.vue 파일 정리

기본 HelloWorld 구성 요소는 할 일 앱에 필요하지 않으므로 App.vue 파일에서 제거합니다.

다른 구성 요소 또는 보기 내에서 Vue 구성 요소를 사용하려면 해당 구성 요소를 파일의 스크립트 블록으로 가져와야 합니다. App.vue 파일에서 첫 번째 줄에 HelloWorld 구성 요소 가져오기가 있습니다.

...
import HelloWorld from './components/HelloWorld';
...

이 구성요소를 사용하지 않을 것이므로 import 행을 삭제하십시오.

다음 단계는 App.vue 페이지의 구성 요소 종속성 목록에서 구성 요소를 제거하는 것입니다. 스크립트 블록에서 다음 줄을 찾습니다.

...
<script>
  ...

  components: {
    HelloWorld,
  },

  ...
</script>

components 목록에서 HelloWorld 줄을 삭제합니다.

마지막 단계는 템플릿 블록에서 제거하는 것입니다.

...
<template>
    ...
    <v-main>
      <HelloWorld/>
    </v-main>
  </v-app>
</template>
...

HelloWorld 줄을 삭제합니다.

이제 기본 HelloWorld 구성 요소가 제거되었으므로 할 일 앱 만들기를 시작할 수 있습니다.

구성 요소 데이터 필드 추가

할 일 앱을 구축하려면 앱에 대한 데이터 필드를 추가합니다. 컴포넌트의 data는 템플릿에서 사용할 수 있는 모든 데이터 모델을 반환하는 함수입니다. 이러한 모든 데이터 모델은 개체 내부의 Javascript 변수이며 구성 요소의 메서드로도 액세스할 수 있습니다.

script 블록에서 data 필드를 찾습니다.

...
<script>
  ...
  data: () => ({
    //
  }),
};
</script>

작업 목록을 저장하도록 data 함수를 수정합니다. 다음 강조 표시된 줄을 data 함수에 추가합니다.

...
<script>
    ...
    data: () => ({
        tasks: ['task 1', 'task 2', 'task 3'],
        newTask: null
    }),
};
</script>

이번 업데이트에서는 작업 이름을 저장하기 위한 newTask 변수와 작업 목록을 위한 tasks라는 두 가지 데이터 모델을 추가했습니다. 이제 두 데이터 모델 모두 템플릿과 메서드에서 사용할 수 있습니다.

참고: Vue.js가 템플릿과 구성 요소의 메서드에 대해 데이터 모델에 액세스할 수 있도록 만드는 방법에 익숙하지 않은 경우 공식 문서에서 Vue.js의 반응성 기초를 살펴보세요.

앱에 기능 추가

다음으로 기능을 추가합니다. Vue.js 구성 요소에서 기능은 메서드라는 함수 목록 안에 있습니다. data 모델 아래의 script 블록에서 강조 표시된 줄을 추가하여 세 가지 기능을 추가합니다.

...
<script>
export default {
    name: 'App',

    data: () => ({
        tasks: ['task 1', 'task 2', 'task 3'],
        newTask: null
    }),
    methods: {
        addNewTask() {
            this.tasks.push(this.newTask);
            this.clearNewTask();
        },
        clearNewTask() {
            this.newTask = '';
        },
        removeTask(i) {
            this.tasks.splice(i, 1);
        }
    }
};

세 가지 기능을 추가했습니다.

  • addNewTask: newTask 데이터 모델 내의 새 작업을 tasks 목록에 추가합니다.
  • clearNewTask: newTask에 대한 데이터 모델을 지웁니다.
  • removeTask: 배열 인덱스를 기반으로 tasks에서 작업을 제거합니다.

이제 할 일 앱에 대한 기능을 추가했습니다. 다음으로 메서드를 사용하도록 템플릿을 수정합니다.

템플릿 업데이트

할 일 앱의 마지막 부분은 템플릿입니다. 이 섹션에서는 이전 섹션에서 추가한 메서드 및 데이터 모델을 사용하도록 템플릿을 업데이트합니다.

v-app-bar에서 필요하지 않은 일부 구성 요소를 제거해야 합니다. v-btn 블록을 삭제합니다. 그러면 코드가 다음과 같이 표시됩니다.

<template>
    <v-app>
        <v-app-bar
            app
            color="primary"
            dark
        >
        <div class="d-flex align-center">
            <v-img
                alt="Vuetify Logo"
                class="shrink mr-2"
                contain
                src="https://cdn.vuetifyjs.com/images/logos/vuetify-logo-dark.png"
                transition="scale-transition"
                width="40"
            />

            <v-img
                alt="Vuetify Name"
                class="shrink mt-1 hidden-sm-and-down"
                contain
                min-width="100"
                src="https://cdn.vuetifyjs.com/images/logos/vuetify-name-dark.png"
                width="100"
            />
        </div>

        <v-spacer></v-spacer>

        </v-app-bar>
        ...
    </v-app>
</template>

다음으로 몇 가지 구성 요소를 추가하여 앱의 기본 레이아웃을 정의합니다. 첫 번째는 v-container로, 앱 콘텐츠를 중앙에 배치하고 가로로 패딩하는 기능을 제공하는 구성 요소입니다. 이 구성 요소 및 기타 컨테이너에 대한 자세한 내용은 Vuetify의 그리드 시스템 문서에서 찾을 수 있습니다.

v-container 내에서 다른 Vuetify 컨테이너인 v-card 구성 요소를 추가합니다. 패널이나 정적 이미지와 같이 화면의 콘텐츠를 구성할 때 유용합니다. v-card 구성 요소에 대한 자세한 내용은 카드에 대한 Vuetify 설명서를 참조하십시오.

현재 템플릿에서 v-main 블록을 찾아 강조 표시된 줄을 추가합니다.

...
<v-main>
    <v-container>
        <v-card elevation="0">
        </v-card>
    </v-container>
</v-main>
...

코드에서 볼 수 있듯이 v-card 구성 요소에는 elevation=0 속성이 있습니다. Elevation은 두 구성 요소 사이의 상대 z 거리를 조정하는 Vuetify 구성 요소 내부의 공통 속성입니다. 이 경우 거리가 필요하지 않으며 0은 표고를 제거하는 값입니다. 그러나 이를 가지고 놀면서 차이점을 확인하거나 표고 문서를 볼 수 있습니다.

다음으로 두 개의 v-card 기능 구성 요소인 v-card-text를 사용합니다. 여기에는 카드의 내용이 포함됩니다. 강조 표시된 줄을 v-card 구성 요소에 추가합니다.

...
<v-main>
    <v-container>
        <v-card elevation="0">
            <v-card-title></v-card-title>
            <v-card-text></v-card-text>
       </v-card>
    </v-container>
</v-main>
...

기능 구성 요소는 템플릿을 렌더링하는 구성 요소입니다. 논리가 없으며 단지 템플릿이기 때문에 렌더링 속도가 더 빠릅니다. 기능적 구성 요소에 대한 자세한 내용을 보거나 자신의 구성 요소를 만드는 방법을 배우려면 기능적 구성 요소에 대한 Vue.js 가이드를 살펴보십시오.

이제 컨테이너 구성 요소가 있으므로 새 작업의 이름을 처리할 v-text-field 구성 요소를 추가해야 합니다. 방금 추가한 v-card-title 구성 요소에 강조 표시된 줄을 삽입합니다.

...
<v-main>
    <v-container>
        <v-card elevation="0">
            <v-card-title>
              <v-text-field
                v-model="newTask"
                label="Task Name"
                prepend-icon="mdi-content-save"
                clear-icon="mdi-close-circle"
                clearable
                filled
                type="text"
                @click:prepend="addNewTask"
                @click:clear="clearNewTask"
                ></v-text-field>
            </v-card-title>
           ...
        </v-card>
    </v-container>
</v-main>
...

v-text-field는 새 작업의 이름을 지정하는 데 필요한 구성 요소입니다.

다음과 같은 속성이 있습니다.

  • v-model=\newTask\는 데이터 모델을 구성 요소에 연결합니다. 입력에 입력한 텍스트도 데이터 모델에 추가됩니다.
  • label=\Task Name\은 입력 유형의 자리 표시자에 있는 텍스트입니다.
  • prepend-icon=\mdi-content-save\는 텍스트 상자의 왼쪽 모서리에 저장 아이콘을 표시합니다.
  • clear-icon=mdi-close-circle은 지우기 버튼의 아이콘입니다.
  • clearable은 지우기 아이콘을 표시합니다.
  • filled는 대체 채워진 입력 스타일을 구성 요소에 적용합니다.
  • type=\text\는 기본 HTML 입력 필드의 입력 유형을 설정합니다. 다른 옵션으로는 이메일 또는 비밀번호가 있습니다.
  • @click: prepend=\addNewTask\는 저장 버튼의 클릭 이벤트를 addNewTask 함수에 연결합니다.
  • @click: clear=\clearNewTask\는 저장 버튼의 클릭 이벤트를 clearNewTask 함수에 연결합니다.

다음 단계는 작업 목록 모델 내의 모든 작업을 표시하는 것입니다. 이를 위해 시간 또는 순서 기반 정보를 표시하기 위한 표시 구성 요소인 v-timeline 구성 요소를 사용합니다. v-card 본문 구성 요소인 v-card-text에 추가할 것입니다. 이미 추가한 v-card-text 구성 요소 안에 강조 표시된 줄을 삽입합니다.

...
<v-main>
    <v-container>
        <v-card elevation="0">
            <v-card-title>
            ...
            </v-card-title>
            <v-card-text>
              <v-timeline
                  v-if="tasks.length > 0"
                  dense
              ></v-timeline>
            </v-card-text>
            ...
      </v-card>
   </v-container>
</v-main>
...

v-timeline은 목록의 모든 작업을 표시합니다. v-if는 데이터 모델에 작업이 하나 이상 있는 경우에만 구성 요소를 표시하는 것입니다. dense는 콘텐츠를 압축하기 위한 것입니다(기본적으로 구성 요소 CSS 스타일에서 일부 패딩과 여백을 제거함).

이제 모든 작업의 이름을 표시하려면 v-timeline 기능 구성 요소인 v-timeline-item을 사용해야 합니다. v-timeline 구성 요소 내에 다음 줄을 추가합니다.

...
<v-main>
    <v-container>
        <v-card elevation="0">
            <v-card-title>
            ...
            </v-card-title>
            <v-card-text>
              <v-timeline
                  v-if="tasks.length > 0"
                  dense
                >
                    <v-timeline-item
                        v-for="(t, index) in tasks"
                        :key="index"
                    >
                         {{ t }}
                    </v-timeline-item>
              </v-timeline>
            </v-card-text>
        </v-card>
    </v-container>
</v-main>
...

이 코드는 v-for 루프를 사용하여 작업 목록 모델의 모든 작업에 대한 v-timeline-item 구성 요소를 표시합니다. v-timeline-itemv-timeline의 기능적 구성 요소이므로 시간순 목록 스타일로 렌더링됩니다.

v-for Vue 지시문에 필수 항목이므로 인덱스를 v-for 루프에 키로 추가합니다. v-for와 함께 고유 키를 사용하는 방법에 대한 자세한 내용은 Vue 제품 설명서를 확인하세요.

{{ t }} 줄을 사용하면 v-timeline-item 구성 요소 내부에 작업 이름이 표시됩니다.

다음 단계는 목록에서 작업을 제거하는 버튼을 추가하는 것입니다. 그러나 그 전에 작업 이름과 버튼을 구성하기 위해 몇 가지 추가 그리드 시스템 구성 요소를 추가해야 합니다. v-timeline-item 구성 요소 안에 강조 표시된 줄을 추가합니다.

...
<v-main>
    <v-container>
        <v-card elevation="0">
            <v-card-title>
            ...
            </v-card-title>
            <v-card-text>
              <v-timeline
                  v-if="tasks.length > 0"
                  dense
                >
                    <v-timeline-item
                        v-for="(t, index) in tasks"
                        :key="index"
                    >
                        <v-row class="display-1 text-capitalize">
                            <v-col cols="7">
                                {{ t }}
                            </v-col>
                            <v-col
                                class="text-right"
                                cols="5"
                            >
                            </v-col>
                        </v-row>
                     </v-timeline-item>
               </v-timeline>
             </v-card-text>
         </v-card>
     </v-container>
</v-main>
...

위의 코드를 사용하여 다음을 추가했습니다.

  • 태스크의 텍스트 크기를 설정하는 두 개의 클래스가 있는 v-row 구성 요소(display-1, HTML의 H1과 유사) 및 모든 대문자로 된 문자(text-capitalize).
  • 행 내부의 v-col 구성 요소는 공간의 7/12 부분이 필요한 모든 작업의 이름을 표시합니다(cols=\7\ 속성 ).
  • 제거 버튼을 배치하는 또 다른 v-col 구성 요소입니다. 공간의 5/12 부분(cols=\5\ 속성)이 필요하고 text-right 클래스에 의해 결정된 오른쪽에 정렬된 내부의 모든 구성 요소가 있습니다.

마지막으로 v-icon 구성 요소도 포함할 때입니다.

강조 표시된 줄을 추가합니다.

...
<v-timeline
    v-if="tasks.length > 0"
    dense
>
    <v-timeline-item
        v-for="(t, index) in tasks"
        :key="index"
    >
        <v-row class="display-1 text-capitalize">
            <v-col cols="7">
                {{ t }}
            </v-col>
            <v-col
                class="text-right"
                cols="5"
            >
                <v-btn
                    icon
                    @click="removeTask(index)"
                >
                    <v-icon color="red lighten-1" large>
                        mdi-sticker-remove
                    </v-icon>
                </v-btn>
            </v-col>
        </v-row>
    ...
    </v-timeline-item>
</v-timeline>

방금 추가한 코드에서 v-btn 구성 요소의 icon 속성은 텍스트가 필요하지 않음을 지정합니다. v-icon 구성 요소에만 스타일을 지정하도록 구성 요소를 수정합니다.

@click 구성 요소 이벤트는 removeTask 메서드를 버튼의 클릭 이벤트에 바인딩합니다. 따라서 기본 버튼 클릭 이벤트가 생성될 때마다 메서드가 호출됩니다.

v-for 루프에서 제공하는 인덱스를 removeTask 메서드의 매개변수로 사용했습니다.

마지막으로 v-icon 색상은 red lighten-1이고 크기는 large이며 mdi- 스티커 제거 머티리얼 디자인의 아이콘입니다.

이제 일부 Vuetify 구성 요소로 템플릿을 업데이트하고 데이터 모델의 콘텐츠를 사용하고 표시하도록 구성했으며 앱 사용자가 페이지 메서드를 사용하여 템플릿과 상호 작용할 수 있도록 했습니다.

다음은 App.vue 파일의 최종 코드입니다.

<template>
    <v-app>
        <v-app-bar
            app
            color="primary"
            dark
        >
        <div class="d-flex align-center">
            <v-img
                alt="Vuetify Logo"
                class="shrink mr-2"
                contain
                src="https://cdn.vuetifyjs.com/images/logos/vuetify-logo-dark.png"
                transition="scale-transition"
                width="40"
            />

            <v-img
                alt="Vuetify Name"
                class="shrink mt-1 hidden-sm-and-down"
                contain
                min-width="100"
                src="https://cdn.vuetifyjs.com/images/logos/vuetify-name-dark.png"
                width="100"
            />
        </div>

        <v-spacer></v-spacer>

        </v-app-bar>

        <v-main>
            <v-container>
                <v-card elevation="0">
                    <v-card-title>
                        <v-text-field
                            v-model="newTask"
                            label="Task Name"
                            prepend-icon="mdi-content-save"
                            clear-icon="mdi-close-circle"
                            clearable
                            filled
                            type="text"
                            @click:prepend="addNewTask"
                            @click:clear="clearNewTask"
                        ></v-text-field>
                    </v-card-title>
                    <v-card-text>
                        <v-timeline
                            v-if="tasks.length > 0"
                            dense
                        >
                            <v-timeline-item
                                v-for="(t, index) in tasks"
                                :key="index"
                            >
                                <v-row class="display-1 text-capitalize">
                                    <v-col cols="7">
                                        {{ t }}
                                    </v-col>
                                    <v-col
                                        class="text-right"
                                        cols="5"
                                    >
                                        <v-btn
                                            icon
                                            @click="removeTask(index)"
                                        >
                                            <v-icon color="red lighten-1" large>
                                                mdi-sticker-remove
                                            </v-icon>
                                        </v-btn>
                                    </v-col>
                                </v-row>
                            </v-timeline-item>
                        </v-timeline>
                    </v-card-text>
                </v-card>
            </v-container>
        </v-main>
    </v-app>
</template>

<script>

export default {
    name: 'App',

    data: () => ({
        tasks: ['task 1', 'task 2', 'task 3'],
        newTask: null
    }),
    methods: {
        addNewTask() {
            this.tasks.push(this.newTask);
            this.clearNewTask();
        },
        clearNewTask() {
            this.newTask = '';
        },
        removeTask(i) {
            this.tasks.splice(i, 1);
        }
    }
};
</script>

파일을 저장하고 닫습니다.

아직 실행 중인 개발 서버가 없으면 다시 시작합니다.

  1. npm run serve

이제 localhost:8080으로 이동하여 앱이 작동하는지 확인할 수 있습니다.

이 단계에서는 할 일 앱을 만들었습니다. 기능을 추가하고 UI를 업데이트했습니다. 이제 앱을 작성했으므로 프로덕션 준비가 된 버전을 생성할 수 있습니다. 다음 단계는 프로덕션용 앱을 빌드하는 것입니다.

4단계 - 프로덕션용 앱 빌드

이전 단계에서 할 일 앱을 작성했습니다. 그러나 Nginx로 게시하기 전에 프로덕션을 위해 앱을 준비해야 합니다. 이 단계를 앱 구축이라고 합니다.

빌드 단계는 앱을 브라우저에서 읽을 수 있는 것으로 변환합니다. 브라우저에서 src 파일을 열려고 하면 아무 것도 표시되지 않습니다. 브라우저가 읽을 수 있는 HTML, JS, CSS 파일이 아니라 Vue.js 파일이기 때문입니다. Nginx로 앱을 게시하기 전에 프로덕션용 앱을 빌드해야 하며, 이 단계에서 수행할 작업입니다.

build 명령을 사용하여 이 작업을 자동으로 수행할 수 있습니다. package.json은 앱의 기본 구성 파일입니다. 여기에는 다음과 같이 build 명령과 같이 앱을 실행하거나 빌드하는 데 사용할 수 있는 종속성 및 명령에 대한 정보가 포함되어 있습니다.

{
  ...
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
  ...
}

package.json 파일 구성에 대한 자세한 내용은 package.json 가이드를 참조하세요.

빌드 프로세스를 시작하려면 CTRL+C를 사용하여 개발 서버를 중지합니다.

동일한 터미널에서 프로젝트 디렉토리로 이동합니다.

  1. cd vuetify-meets-nginx-app

빌드 명령을 실행합니다.

  1. npm run build

빌드가 완료되면 dist 디렉터리에 프로덕션용 앱 버전이 준비됩니다.

다음으로 Nginx를 리버스 프록시로 설정하여 앱을 배포하고 액세스합니다.

5단계 - Nginx를 역방향 프록시로 구성

이제 작동하는 애플리케이션이 있으므로 Nginx를 리버스 프록시로 구성하여 앱 파일을 제공하고 도메인 이름에 연결해야 합니다.

역방향 프록시는 서버에서 실행되고 외부 요청을 다른 위치로 전달하는 애플리케이션 또는 서비스입니다. 귀하의 경우 사용자가 브라우저에서 귀하의 도메인을 방문할 때마다 Nginx는 귀하의 앱에서 파일로 응답하여 이 요청을 처리합니다. 앱을 빌드할 때 HTML, JS 및 CSS 파일이 생성되기 때문에 Nginx가 서버에 있을 수 있는 다른 정적 파일이나 웹사이트로 처리하기 때문에 파일을 반환합니다.

Nginx사이트와 함께 작동합니다. 모든 사이트는 단일 파일에 구성된 다른 웹사이트입니다. 모든 구성 파일은 기본적으로 /etc/nginx/sites-available/에 있습니다. 다음 디렉터리로 이동합니다.

  1. cd /etc/nginx/sites-available/

vuetify-meets-nginx-app라는 파일을 만듭니다.

  1. sudo nano vuetify-meets-nginx-app

참고: 파일 이름은 원하는 대로 지정할 수 있지만 게시하려는 앱이나 웹사이트 이름을 지정하는 것이 관례입니다.

vuetify-meets-nginx-app 파일에서 다음 줄을 추가하고 server_name을 자신의 정보로 업데이트해야 합니다.

server {
  listen 80;
  listen [::]:80;
  server_name your_domain;
  autoindex on;
  root   /home/sammy/vuetify-meets-nginx-app/dist/;
  index  index.html;
}

이 자습서에서는 포트 80에서 수신 대기하도록 Nginx를 구성하지만 원하는 모든 포트를 사용할 수 있습니다. your_domain을 도메인 이름으로 바꿉니다. 로컬 개발 환경에서 테스트하는 경우 서버 또는 localhost의 IP 주소를 사용할 수도 있습니다.

root 줄을 사용하면 Nginx에게 모든 파일이 /home/sammy/vuetify-meets-nginx-app/dist/에 있음을 알립니다. 이전 단계에서 만든 디렉터리입니다. 마지막으로 index 줄을 사용하여 Nginx에 기본 파일이 index.html임을 알립니다.

파일을 저장하고 닫습니다.

다음으로 Nginx 구성 파일에서 권한 문제를 해결해야 합니다.

Nginx는 서버에서 실행되는 서비스입니다. 다음 명령을 사용하여 Nginx와 관련하여 실행 중인 모든 프로세스를 나열할 수 있습니다.

  1. ps -fea | grep nginx

-fea 플래그가 있는 ps 명령은 모든 현재 프로세스를 전체 형식 목록으로 나열합니다. 그런 다음 이 명령의 출력은 nginx와 일치하는 프로세스만 나열하도록 필터링됩니다.

출력은 다음과 유사합니다.

Output
root 39922 1 0 Jul14 ? 00:00:00 nginx: master process /usr/sbin/nginx -g daemon on; master_process on; www-data 39923 39922 0 Jul14 ? 00:00:01 nginx: worker process sammy 117909 117434 0 21:27 pts/0 00:00:00 grep --color=auto nginx

출력에 표시된 것처럼 nginx 서비스는 사용자 www-data로 실행 중입니다.

다음으로 다음 명령을 사용하여 /home/sammy/vuetify-meets-nginx-app/dist/의 권한을 검토합니다.

  1. ls -l /home/sammy/vuetify-meets-nginx-app/dist/

출력은 다음과 유사합니다.

Output
total 20 drwxrwxr-x 2 sammy sammy 4096 Jul 14 18:54 css -rw-rw-r-- 1 sammy sammy 4286 Jul 14 18:54 favicon.ico -rw-rw-r-- 1 sammy sammy 853 Jul 14 18:54 index.html drwxrwxr-x 2 sammy sammy 4096 Jul 14 18:54 js

모든 파일과 폴더에는 sammy 사용자가 사용할 수 있는 권한이 있습니다. 이러한 파일을 읽도록 Nginx를 구성하면 Nginx 사용자 www-data에게 실행 권한이 없기 때문에 작동하지 않습니다.

이 문제를 해결하기 위한 몇 가지 옵션이 있습니다.

  • dist 폴더에 대한 Nginx 읽기, 쓰기 및 실행 권한을 부여합니다. 그러나 전체 네트워크에서 액세스할 수 있는 서비스에 로컬 사용자 파일을 읽을 수 있는 권한을 부여하는 것은 안전하지 않습니다. 또한 Nginx는 최종 폴더로 이동해야 하기 때문에 모든 상위 폴더에 액세스할 수 있는 권한이 필요합니다. 그러면 기본적으로 /home 디렉터리가 공개됩니다. 권장되지 않습니다.
  • sudonginx를 실행합니다. 이것은 또한 안전하지 않습니다. 이제 서버의 모든 파일에 액세스할 수 있는 서비스를 갖게 되었기 때문입니다.
  • dist 콘텐츠를 Nginx만 액세스할 수 있고 다른 사람은 액세스할 수 없는 위치로 이동합니다. 이것이 가장 안전한 옵션입니다.

이 자습서에서는 세 번째 옵션을 사용합니다.

Ubuntu 및 일부 다른 Linux 기반 배포판에서 서비스 간에 파일을 교환하기 위한 공유 위치는 /var 경로입니다. Nginx가 웹사이트에 사용하는 기본 경로인 /var/www에 파일을 복사합니다.

프로젝트 디렉터리에서 다음 명령을 실행하여 파일을 /var/www 경로에 복사합니다.

  1. sudo cp -r /home/sammy/vuetify-meets-nginx-app/dist /var/www/vuetify-meets-nginx-app

모든 파일은 sammy 사용자를 위한 이전과 동일한 권한으로 복사되므로 다음과 같은 권한 그룹에 sammy 사용자를 추가해야 합니다. www-데이터. 다음 명령으로 이 작업을 수행할 수 있습니다.

  1. sudo usermod -aG www-data sammy

이 시점에서 Nginx는 안전한 방식으로 필요한 파일에 액세스할 수 있습니다. 그러나 새 버전의 앱을 생성할 때마다 프로젝트 파일을 복사해야 하므로 자동 배포, CI/CD 도구 등이 복잡해집니다. 더 나은 솔루션은 파일을 올바른 경로에 직접 생성하도록 build 명령을 수정하는 것입니다.

그렇게 하려면 편집을 위해 package.json을 열고 강조 표시된 텍스트를 추가합니다.

...
"build": "vue-cli-service build --dest /var/www/vuetify-meets-nginx-app",
...

파일을 저장하고 닫습니다.

이제 Nginx 구성을 완료할 수 있습니다. Nginx 구성 파일을 열고 새 애플리케이션 경로로 업데이트합니다.

server {
  listen 80;
  listen [::]:80;
  server_name your_domain OR your_server_IP;
  autoindex on;
  root   /var/www/vuetify-meets-nginx-app;
  index  index.html;
}

파일을 저장하고 닫습니다.

이제 사이트 파일이 준비되었으므로 활성화해야 합니다. 이를 위해 활성화된 사이트 경로로 이동합니다.

  1. cd /etc/nginx/sites-enabled/

기본 사이트를 비활성화하여 동일한 포트(포트 80)에서 두 개의 사이트가 활성화되고 수신 대기하지 않도록 하십시오.

  1. sudo rm default

마지막으로 앱의 구성 파일에 대한 simlink 파일을 만듭니다.

  1. sudo ln -s /etc/nginx/sites-available/vuetify-meets-nginx-app

Nginx는 활성화된 디렉토리에 있는 사이트 파일만 고려합니다. 구성 파일을 직접 복사할 수 있지만 잠재적으로 불일치가 있을 수 있으므로 중복 파일을 사용하지 않는 것이 좋습니다. 이것이 바로 사용 가능한 파일에 대한 파일 바로 가기인 simlink가 더 나은 접근 방식인 이유입니다.

Nginx 파일에 구문 오류가 없는지 테스트합니다.

  1. sudo nginx -t

출력은 다음과 같습니다.

Output
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful

변경 사항을 적용하려면 Nginx 서비스를 다시 시작합니다.

  1. sudo systemctl restart nginx

이제 도메인(또는 서버의 IP 주소)으로 이동하여 준비 및 게시된 작업 앱을 볼 수 있습니다.

이 단계에서는 Nginx를 역방향 프록시로 구성하여 앱을 게시했습니다.

결론

이 자습서에서는 Vue.js 애플리케이션을 만들고 Vuetify를 설치 및 구성했습니다. 그런 다음 프로덕션 준비가 된 앱의 정적 버전을 생성하고 마지막으로 이를 게시하도록 Nginx 서비스를 구성했습니다.

프로젝트 파일을 자세히 살펴보려면 Github 리포지토리를 확인하십시오.

다음 단계로 HTTPS를 통해 애플리케이션을 제공하도록 Nginx를 구성해 보세요. 시작하려면 Ubuntu 20.04에서 Let's Encrypt로 Nginx를 보호하는 방법 튜토리얼을 따르십시오.

Vuetify에 대해 자세히 알아보려면 Vuetify 양식 필드 유효성 검사 확장을 확인하십시오.