logo
Search검색어를 포함하는 게시물들이 최신순으로 표시됩니다.
    Table of Contents
    [Vue] Vue CLI

    이미지 보기

    [Vue] Vue CLI

    • 22.05.09 작성

    • 읽는 데 16

    TOC

    SFC

    Component(컴포넌트)

    • 기본 HTML 엘리먼트를 확장하여 재사용 가능한 코드를 캡슐화 하는데 도움을 줌
    • CS에서는 다시 사용할 수 있는 범용성을 위해 개발된 소프트웨어 구성 요소를 의미
    • 즉, 컴포넌트는 유지보수를 쉽게 만들어 줄 뿐만 아니라, 재사용성의 측면에서도 매우 강력한 기능을 제공

    SFC(Single File Component)

    • Vue의 컴포넌트 기반 개발의 핵심 특징

    • 하나의 컴포넌트는 .vue 확장자를 가진 하나의 파일 안에서 작성되는 코드의 결과물

    • 화면의 특정 영역에 대한 HTML, CSS, JS 코드를 하나의 파일(.vue)에서 관리

    • 즉, .vue 확장자를 가진 싱글 파일 컴포넌트를 통해 개발하는 방식

    • Vue 컴포넌트 === Vue 인스턴스 === .vue 파일


    단일 파일과 컴포넌트 ⭐

    단일 파일에서의 개발

    • 코드의 양이 많아지면 변수 관리가 힘들어지고 유지보수에 많은 비용 발생

    여러 컴포넌트

    • 각 기능별로 파일 분할 개발
    • 처음 개발을 준비하는 단계에서 시간 소요 증가
    • 변수 관리가 용이 및 기능별로 유지보수 비용 감소

    컴포넌트 구조

    한 화면 안에서도 기능별로 각기 다른 컴포넌트 존재

    • 하나의 컴포넌트는 여러 개의 하위 컴포넌트를 가질 수 있음
    • Vue는 컴포넌트 기반의 개발 환경 제공

    Vue 컴포넌트는 const app = new Vue({...}) 의 app을 의미하며, 이는 Vue 인스턴스

    • 주의📢 컴포넌트 기반의 개발이 반드시 파일 단위로 구분되어야 하는 것은 아님
    • 단일 .html 파일 안에서도 여러 개의 컴포넌트를 만들어 개발 가능

    정리

    • Vue 컴포넌트 === Vue 인스턴스(new Vue({}))
    • Vue 인스턴스 .vue 파일 안에 작성된 코드의 집합
    • HTML, CSS, JS를 .vue 확장자 파일 내에서 관리하며 개발

    Vue CLI

    • Vue.js 개발을 위한 표준 도구
    • 프로젝트의 구성을 도와주는 역할
    • Vue 개발 생태계에서 표준 tool 기준을 목표로 함

    Node.js

    • JS를 브라우저가 아닌 환경에서도 구동할 수 있도록 하는 JS 런타임 환경

    • Chrome V8 엔진을 제공하여 여러 OS 환경에서 실행할 수 있는 환경 제공

    • 브라우저 밖을 벗어날 수 없었던 JS의 태생적 한계 해결

    • JS를 SSR 아키텍처에서도 사용할 수 있도록 함

    • Ryan Dahl에 의해 발표(2009)


    NPM(Node Package Manager)

    • JS를 위한 패키지 관리자
    • pip와 마찬가지로 다양한 의존성 패키지 관리
    • Node.js의 기본 패키지 관리자

    설치 과정

    vue cli 설치

    $ npm install -g @vue/cli
    $ vue --version
    

    한 번 설치했다면 또 할 필요는 없다.


    vue project 생성

    $ vue create my-first-app
    Default ([Vue 2] babel, eslint)
    
    • Vue2 버전을 사용할 것이므로 이를 선택

    프로젝트 실행

    $ cd my-first-app
    $ npm run serve
    

    Babel & Webpack

    Babel

    • JavaScript Compiler

    • JS의 ECMAScript 2015+ 코드를 이전 버전으로 번역/변환해 주는 도구

    • 과거 JS의 파편화와 표준화의 영향으로 코드의 스펙트럼이 매우 다양 → 최신 문법이 이전 브라우저 혹은 환경에서 동작하지 않는 상황 발생

    • 원시 코드(최신 버전)를 목적 코드(구 버전)로 옮기는 번역기가 등장 → 개발자는 더 이상 코드가 특정 브라우저에서 동작하지 않는 상황에 대해 크게 고민 X


    Webpack

    • Static Module Bundler
    • 모듈 간의 의존성 문제를 해결하기 위한 도구
    • 프로젝트에 필요한 모든 모듈을 매핑하고 내부적으로 종속성 그래프를 빌드

    Static Module Bundler

    모듈?

    단지 파일 하나


    배경

    • 브라우저만 조작할 수 있었던 시기의 JS는 모듈 관련 문법 없이 사용
    • JS와 App이 복잡해지고 크기가 커짐 → 전역 scope를 공유하는 형태의 기존 개발 방식의 한계점 발생
    • 라이브러리를 만들어 필요한 모듈을 언제든 호출 / 코드를 모듈 단위로 작성하는 등의 다양한 시도

    여러 모듈 시스템 ⭐

    • ESM(ECMA Script Module)
    • AMD(Asynchronous Module Definition)
    • CommonJS
    • UMD(Universal Module Definition)

    Module 의존성 문제

    • 모듈의 수가 많아지고 라이브러리 혹은 모듈 간의 의존성(연결성)이 깊어짐 → 특정한 곳에서 발생한 문제가 어떤 모듈 간의 문제인지 파악하기 어려움

    • 즉, Webpack은 이 모듈 간의 의존성 문제 해결을 위해 등장


    Bundler

    • Bundling : 모듈 의존성 문제를 해결하는 작업

    • Bundler : Bundling을 해주는 도구

    • Webpack : Bundler 중 하나

    • 여러 모듈을 하나로 묶어주고 묶인 파일은 하나(or 여러 개)로 합쳐짐

    • Bundling된 결과물은 더 이상 순서에 영향을 받지 않고 동작

    • snowpack, parcel, rollup.js 등의 다양한 모듈 번들러 존재

    • Vue CLI는 이러한 Babel, Webpack에 대한 초기 설정이 자동


    정리

    Node.js

    • JS Runtime Environment
    • JS를 브라우저 밖에서 실행할 수 있는 새로운 환경

    Babel

    • Compiler
    • ES2015+ JS 코드를 구버전의 JS로 바꿔주는 도구

    Webpack

    • Module Bundler
    • 모듈 간의 의존성 문제를 해결하기 위한 도구

    Vue 프로젝트 구조

    node_modules

    • node.js 환경의 여러 의존성 모듈

    public/index.html

    • Vue 앱의 뼈대가 되는 파일
    • 실제 제공되는 단일 html 파일

    src

    • assets : webpack에 의해 빌드된 정적파일

    • App.vue : 최상위 컴포넌트

    • components : App.vue 내부에 들어가는 하위 컴포넌트 디렉토리

    • main.js

      • webpack이 빌드를 시작할 때 가장 먼저 불러오는 entry point
      • 실제 단일 파일에서 DOM과 data를 연결했던 것과 동일한 작업이 이루어지는 공간
      • Vue 전역에서 활용할 모듈을 등록할 수 있는 파일

    babel.config.js

    • babel 관련 설정이 작성된 파일

    package.json

    • 프로젝트의 종속성 목록과 지원되는 브라우저에 대한 구성 옵션 포함

    package-lock.json

    • node_modules에 설치되는 모듈과 관련된 모든 의존성 설정 & 관리
    • 팀원 및 배포 환경에서 정확히 동일한 종속성을 설치하도록 보장
    • 사용할 패키지의 버전 고정
    • 개발 과정 간의 의존성 패키지 충돌 방지

    Pass Props & Emit Events

    컴포넌트 작성

    • Vue app은 자연스럽게 중첩된 컴포넌트 트리로 구성
    • 컴포넌트간 부모-자식 관계 구성, 이들 사이의 의사소통 필요

    props는 아래로 events는 위로

    • 부모는 자식에게 데이터 전달(pass props)
    • 자식은 자신에게 일어난 일을 부모에게 알림(emit event)
    • 부모와 자식이 명확하게 정의된 인터페이스를 통해 격리된 상태 유지 가능

    props

    • 부모(상위) 컴포넌트의 정보를 전달하기 위한 사용자 지정 특성
    • 자식(하위) 컴포넌트는 props 옵션을 사용하여 수신하는 props를 명시적으로 선언
    • 주의📢 자식 컴포넌트의 템플릿에서 상위 데이터를 직접 참조할 수 없음

    가. Static Props 작성법

    • 자식 컴포넌트에 보낼 prop 데이터 선언
    {%- raw -%}
    // App.vue
    <template>
      <div id="app">
        <app-child my-message="This is prop data"></app-child>
      </div>
    </template>
    {% endraw -%}
    

    • 부모 데이터로부터 받은 props 데이터를 명시적으로 선언
    • 이를 template에서 사용
    {%- raw -%}
    // AppChild.vue
    <template>
      <div>
        <h1>Child</h1>
        <h2>{{ myMessage }}</h2>
      </div>
    </template>
    
    <script>
    export default {
      name: "AppChild",
      props: {
        myMessage: String,
      }
    }
    {% endraw -%}
    
    • 자식 컴포넌트로 보낼 때, 즉 template에서는 kebab-case로 사용 및 전달
    • 자식 컴포넌트에서 받을 때, 즉 script에서는 camelCase로 선언(declaration)
    • 데이터 타입을 정의해준다.(String, Array)
    • 중괄호로 열어 type: String, required:true 등 여러 옵션을 둘 수 있다.

    나. Dynamic Props 작성법

    • v-bind directive를 사용
    • 부모컴포넌트의 data의 props를 동적으로 바인딩
    • 부모에서 데이터가 업데이트 될 때마다 자식 데이터로도 전달
    {%- raw -%}
    // App.vue
    <template>
      <div id="app">
        <app-child my-message="This is prop data"></app-child>
        <app-child :parent-data="parentData"></app-child>
      </div>
    </template>
    
    <script>
    export default {
      name: "App",
      components: {
        AppChild,
      },
      data: function () {
        return {
          parentData: 'This is parent Data'
        }
      }
    }
    {% endraw -%}
    
    • 값으로 넣은 parentData가 단순 문자열이 아닌, data 내의 변수(상수)임을 의미
    • 이를 binding하는 것

    data가 함수?

    • 컴포넌트의 data는 반드시 함수여야 한다.
    • 기본적으로 각 인스턴스는 모두 같은 data 객체를 공유하므로 새로운 data 객체를 반환(return)해야 함
    • 인스턴스 각각마다 처음엔 같은 값을 return해 시작하지만 각각 조작을 달리 할 수 있으므로 개별성을 위해 함수형
    {%- raw -%}
    data: function () {
      return {
        message: null,
      }
    }
    {% endraw -%}
    

    props 전달과 v-bind

    • props 전달에서 자주 실수하는 부분
    • v-bind 없이 static 구문을 사용해 숫자 전달
    • 실제 JS 숫자를 전달하려면 값이 JS 표현식으로 평가되기 위해 v-bind 사용
    {%- raw -%}
    // 1. 일반 문자열 "1" 전달
    <comp some-prop="1"></comp>
    
    // 2. 실제 숫자 1 전달
    <comp :some-prop="1"></comp>
    {% endraw -%}
    

    단방향 데이터 흐름

    • 모든 props는 하위 속성과 상위 속성 사이의 단방향 바인딩 형성
    • 부모 속성이 변경되면 자식 속성에게 전달, 반대 방향은 안 됨
    • 자식 요소가 의도치 않게 부모 요소의 상태를 변경하여 앱의 데이터 흐름을 망치는 일을 방지
    • 부모 컴포넌트가 업데이트될 때마다 자식 요소의 모든 prop들이 최신값으로 업데이트

    Emit event

    • Listening to Child Components Events

    $emit(eventName)

    • 현재 인스턴스에서 이벤트를 트리거
    • 추가 인자는 리스너의 콜백 함수로 전달

    부모자식 컴포넌트 간 데이터 전달 과정

    • template의 자식 컴포넌트 부분에 v-on을 사용하여 자식 컴포넌트가 보낸 이벤트 청취

    {%- raw -%}
    // AppChild.vue
    <template>
      ...
      <input
        type="text"
        @keyup.enter="childInputChange"
        v-model="childInputData"
      >
    </template>
    {% endraw -%}
    
    • v-model을 이용해 childInputData와 양방향 갱신
    • enter key가 눌리면 childInputChange 콜백함수 실행

    {%- raw -%}
    // AppChild.vue
    methods: {
      childInputChange: function () {
        this.$emit('child-input-change', this.childInputData)
      }
    }
    {% endraw -%}
    
    • child-input-change 이벤트를 트리거
    • childInputData를 전달

    {%- raw -%}
    // App.vue
    <template>
      ...
      <app-child
        ...
        @child-input-change="parentGetChange"
      >
    </template>
    {% endraw -%}
    
    • 부모 컴포넌트인 App.vue에서 app-child 컴포넌트에서 이벤트 청취
    • $emit을 통해 올려보냈던 'child-input-change' event에 대해 청취하면 'parentGetChange' 콜백함수 실행

    {%- raw -%}
    methods: {
      parentGetChange: function (inputData) {
        console.log(`AppChild로부터 ${inputData}를 받음`)
      }
    }
    {% endraw -%}
    
    • 인자로 전달한 childInputData를 임의의 변수명으로 함수 내에 전달해 사용

    event 이름 컨벤션

    • 컴포넌트 및 props와 달리, 이벤트는 자동 대소문자 변환 제공 X
    • HTML의 대소문자 구분을 위해 DOM 템플릿의 v-on 이벤트리스너는 항상 소문자로 변환
    • 즉, template 내에서 'v-on:myEvent'는 'v-on:myevent'로 자동변환
    • 때문에 이벤트 이름으로는 항상 kebab-case를 사용하는 것을 권장
    {%- raw -%}
    this.$emit('myEvent') // 오답
    this.$emit('my-event') // 정답
    {% endraw -%}
    
    {%- raw -%}
    <app-child @my-event="callbackFunc"></app-child>
    {% endraw -%}
    
    profile

    FE Developer 박승훈

    노력하는 자는 즐기는 자를 이길 수 없다