InfoGrab DocsInfoGrab Docs

Vue.js 스타일 가이드

요약

기본적으로 plugin:vue/recommended 설정과 함께 eslint-vue-plugin을 사용합니다. Vue 템플릿에는 .vue를 사용하세요. Vue 앱에 전달되는 데이터를 명시적으로 정의하세요. 코드베이스를 명시적이고 검색 가능하게 유지하기 위해, 이 특정 경우에는 스프레드 연산자 사용을 권장하지 않습니다.

린팅#

기본적으로 plugin:vue/recommended 설정과 함께 eslint-vue-plugin을 사용합니다. 자세한 내용은 규칙을 확인하세요.

기본 규칙#

  • Vue 템플릿에는 .vue를 사용하세요. HAML에서 %template을 사용하지 마세요.

  • Vue 앱에 전달되는 데이터를 명시적으로 정의하세요.

// bad
return new Vue({
  el: '#element',
  name: 'ComponentNameRoot',
  components: {
    componentName
  },
  provide: {
    ...someDataset
  },
  props: {
    ...anotherDataset
  },
  render: createElement => createElement('component-name'),
}));

// good
const { foobar, barfoo } = someDataset;
const { foo, bar } = anotherDataset;

return new Vue({
  el: '#element',
  name: 'ComponentNameRoot',
  components: {
    componentName
  },
  provide: {
    foobar,
    barfoo
  },
  props: {
    foo,
    bar
  },
  render: createElement => createElement('component-name'),
}));

코드베이스를 명시적이고 검색 가능하게 유지하기 위해, 이 특정 경우에는 스프레드 연산자 사용을 권장하지 않습니다. 이는 Vuex 상태 초기화와 같이 위의 방식이 유익한 모든 경우에 적용됩니다. 위의 패턴은 또한 인스턴스 생성 시 스칼라가 아닌 값을 쉽게 파싱할 수 있게 해줍니다.

return new Vue({
  el: '#element',
  name: 'ComponentNameRoot',
  components: {
    componentName
  },
  props: {
    foo,
    bar: parseBoolean(bar)
  },
  render: createElement => createElement('component-name'),
}));

템플릿 내 컴포넌트 사용#

  • 템플릿에서 컴포넌트를 사용할 때는 다른 스타일보다 컴포넌트의 케밥 케이스(kebab-cased) 이름을 선호하세요.
// bad


// good
<my-component />

컴포넌트 name 속성#

모든 Vue 컴포넌트에는 name 속성이 있어야 합니다. 파일명에서 파생된 PascalCase를 사용하세요. 예를 들어, board_app.vuename: 'BoardApp'이 됩니다.

app.vue 또는 index.vue와 같은 일반적인 파일명의 경우, 디렉터리 경로의 컨텍스트를 접두사로 붙여 고유한 이름을 만드세요. 예를 들어, admin/users/components/app.vuename: 'AdminUsersApp'이 됩니다.

EE 컴포넌트가 CE 컴포넌트와 이름을 공유할 경우, EE 컴포넌트 이름에 EE 접미사를 추가하세요. 예를 들어, app/ee/app/ 모두에 BranchSelector 컴포넌트가 있는 경우, EE 버전은 name: 'BranchSelectorEE'를 사용해야 합니다.

<style> 태그#

몇 가지 이유로 Vue 컴포넌트에서 <style> 태그를 사용하지 않습니다:

  • SCSS 변수와 믹스인 또는 Tailwind CSS@apply 지시어를 사용할 수 없습니다.

  • 이러한 스타일은 런타임에 삽입됩니다.

  • CSS를 정의하는 다른 방법이 이미 몇 가지 있습니다.

<style> 태그 대신 Tailwind CSS 유틸리티 클래스 또는 페이지별 CSS를 사용하세요.

Vue 테스트#

시간이 지나면서 Vue 컴포넌트를 효과적으로 테스트하려는 노력 속에서 여러 프로그래밍 패턴과 스타일 선호도가 생겨났습니다. 다음 가이드는 그 중 일부를 설명합니다. 이것들은 엄격한 지침이 아니라, GitLab에서 Vue 테스트를 작성하는 방법에 대한 통찰을 제공하기 위한 제안 및 모범 사례의 모음입니다.

컴포넌트 마운트#

일반적으로 Vue 컴포넌트를 테스트할 때, 컴포넌트는 모든 테스트 블록에서 "다시 마운트"되어야 합니다.

이를 위해:

  • 최상위 describe 블록 내에 변경 가능한 wrapper 변수를 생성합니다.

  • mount 또는 shallowMount를 사용하여 컴포넌트를 마운트합니다.

  • 결과 Wrapper 인스턴스를 wrapper 변수에 재할당합니다.

전역적이고 변경 가능한 wrapper를 생성하면 다음과 같은 여러 이점이 있습니다:

  • 컴포넌트/DOM 요소를 찾는 공통 함수를 정의할 수 있습니다:
import MyComponent from '~/path/to/my_component.vue';
describe('MyComponent', () => {
  let wrapper;

  // this can now be reused across tests
  const findMyComponent = wrapper.findComponent(MyComponent);
  // ...
})

비동기 자식 컴포넌트#

shallowMount비동기 자식 컴포넌트에 대한 컴포넌트 스텁을 생성하지 않습니다. 비동기 자식 컴포넌트를 올바르게 스텁하려면 stubs 옵션을 사용하세요. 비동기 자식 컴포넌트에 name 옵션이 정의되어 있는지 확인하세요. 그렇지 않으면 wrapperfindComponent 메서드가 올바르게 작동하지 않을 수 있습니다.

createComponent 팩토리#

마운트 로직 중복을 피하기 위해, 각 테스트 블록에서 재사용할 수 있는 createComponent 팩토리 함수를 정의하는 것이 유용합니다. 이것은 wrapper 변수를 mountshallowMount의 결과로 재할당해야 하는 클로저입니다:

import MyComponent from '~/path/to/my_component.vue';
import { shallowMount } from '@vue/test-utils';

describe('MyComponent', () => {
  // Initiate the "global" wrapper variable. This will be used throughout our test:
  let wrapper;

  // Define our `createComponent` factory:
  function createComponent() {
    // Mount component and reassign `wrapper`:
    wrapper = shallowMount(MyComponent);
  }

  it('mounts', () => {
    createComponent();

    expect(wrapper.exists()).toBe(true);
  });

  it('`isLoading` prop defaults to `false`', () => {
    createComponent();

    expect(wrapper.props('isLoading')).toBe(false);
  });
})

마찬가지로, beforeEach 블록에서 createComponent를 호출하여 테스트를 더욱 중복 제거할 수 있습니다:

import MyComponent from '~/path/to/my_component.vue';
import { shallowMount } from '@vue/test-utils';

describe('MyComponent', () => {
  // Initiate the "global" wrapper variable. This will be used throughout our test
  let wrapper;

  // define our `createComponent` factory
  function createComponent() {
    // mount component and reassign `wrapper`
    wrapper = shallowMount(MyComponent);
  }

  beforeEach(() => {
    createComponent();
  });

  it('mounts', () => {
    expect(wrapper.exists()).toBe(true);
  });

  it('`isLoading` prop defaults to `false`', () => {
    expect(wrapper.props('isLoading')).toBe(false);
  });
})

createComponent 모범 사례#

  • 여러 인수보다 단일(또는 제한된 수의) 객체 인수 사용을 고려하세요. props와 같은 공통 데이터에 대한 단일 파라미터를 정의하는 것은 괜찮지만, JavaScript 스타일 가이드를 참고하여 파라미터 수 제한을 지키세요:
// bad
function createComponent(props, stubs, mountFn, foo) { }

// good
function createComponent({ props, stubs, mountFn, foo } = {}) { }

// good
function createComponent(props = {}, { stubs, mountFn, foo } = {}) { }
  • 동일한 테스트 세트 내에서 mountshallowMount 모두 필요한 경우, 컴포넌트를 마운트하는 데 사용할 마운트 함수(mount 또는 shallowMount)를 받는 createComponent 팩토리의 mountFn 파라미터를 정의하는 것이 유용할 수 있습니다:
import { shallowMount } from '@vue/test-utils';

function createComponent({ mountFn = shallowMount } = {}) { }
  • wrapper.findByTestId()를 노출하려면 mountExtendedshallowMountExtended 헬퍼를 사용하세요:
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { SomeComponent } from 'components/some_component.vue';

let wrapper;

const createWrapper = () => { wrapper = shallowMountExtended(SomeComponent); };
const someButton = () => wrapper.findByTestId('someButtonTestId');
  • 컴포넌트 내부를 확장하는 data, methods 또는 다른 마운트 옵션 사용을 피하세요.
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { SomeComponent } from 'components/some_component.vue';

let wrapper;

// bad :( - This circumvents the actual user interaction and couples the test to component internals.
const createWrapper = ({ data }) => {
  wrapper = shallowMountExtended(SomeComponent, {
    data
  });
};

// good :) - Helpers like `clickShowButton` interact with the actual I/O of the component.
const createWrapper = () => {
  wrapper = shallowMountExtended(SomeComponent);
};
const clickShowButton = () => {
  wrapper.findByTestId('show').trigger('click');
}

컴포넌트 상태 설정#

  • 가능한 한 setProps를 사용하여 컴포넌트 상태를 설정하지 마세요. 대신 컴포넌트를 마운트할 때 컴포넌트의 propsData를 설정하세요:
// bad
wrapper = shallowMount(MyComponent);
wrapper.setProps({
  myProp: 'my cool prop'
});

// good
wrapper = shallowMount({ propsData: { myProp: 'my cool prop' } });

예외는 어떤 방식으로 컴포넌트 반응성을 테스트하고 싶을 때입니다. 예를 들어, 특정 감시자(watcher)가 실행된 후 컴포넌트의 출력을 테스트하고 싶을 수 있습니다. 이러한 동작을 테스트하기 위해 setProps를 사용하는 것은 괜찮습니다.

  • 컴포넌트의 내부 상태를 설정하고 컴포넌트의 실제 I/O 테스트를 우회하는 setData 사용을 피하세요. 대신 컴포넌트의 자식에서 이벤트를 트리거하거나 다른 부수 효과를 통해 강제로 상태를 변경하세요.

컴포넌트 상태 접근#

  • props 또는 속성에 접근할 때 wrapper.props().myProp 또는 wrapper.vm.myProp 대신 wrapper.props('myProp') 구문을 선호하세요:
// good
expect(wrapper.props().myProp).toBe(true);
expect(wrapper.attributes().myAttr).toBe(true);

// better
expect(wrapper.props('myProp').toBe(true);
expect(wrapper.attributes('myAttr')).toBe(true);
  • 여러 props를 어설션할 때 toEqual을 사용하여 props() 객체의 깊은 동등성을 확인하세요:
// good
expect(wrapper.props('propA')).toBe('valueA');
expect(wrapper.props('propB')).toBe('valueB');
expect(wrapper.props('propC')).toBe('valueC');

// better
expect(wrapper.props()).toEqual({
  propA: 'valueA',
  propB: 'valueB',
  propC: 'valueC',
});
// good
expect(wrapper.props()).toEqual(expect.objectContaining({
  propA: 'valueA',
  propB: 'valueB',
}));

// better
expect(wrapper.props()).toMatchObject({
  propA: 'valueA',
  propB: 'valueB',
});

props 유효성 검사 테스트#

컴포넌트 props를 확인할 때 assertProps 헬퍼를 사용하세요. props 유효성 검사 실패는 오류로 발생합니다:

import { assertProps } from 'helpers/assert_props'

// ...

expect(() => assertProps(SomeComponent, { invalidPropValue: '1', someOtherProp: 2 })).toThrow()

Vue.js 스타일 가이드

GitLab v19.1
원문 보기
요약

기본적으로 plugin:vue/recommended 설정과 함께 eslint-vue-plugin을 사용합니다. Vue 템플릿에는 .vue를 사용하세요. Vue 앱에 전달되는 데이터를 명시적으로 정의하세요. 코드베이스를 명시적이고 검색 가능하게 유지하기 위해, 이 특정 경우에는 스프레드 연산자 사용을 권장하지 않습니다.

린팅#

기본적으로 plugin:vue/recommended 설정과 함께 eslint-vue-plugin을 사용합니다. 자세한 내용은 규칙을 확인하세요.

기본 규칙#

  • Vue 템플릿에는 .vue를 사용하세요. HAML에서 %template을 사용하지 마세요.

  • Vue 앱에 전달되는 데이터를 명시적으로 정의하세요.

// bad
return new Vue({
  el: '#element',
  name: 'ComponentNameRoot',
  components: {
    componentName
  },
  provide: {
    ...someDataset
  },
  props: {
    ...anotherDataset
  },
  render: createElement => createElement('component-name'),
}));

// good
const { foobar, barfoo } = someDataset;
const { foo, bar } = anotherDataset;

return new Vue({
  el: '#element',
  name: 'ComponentNameRoot',
  components: {
    componentName
  },
  provide: {
    foobar,
    barfoo
  },
  props: {
    foo,
    bar
  },
  render: createElement => createElement('component-name'),
}));

코드베이스를 명시적이고 검색 가능하게 유지하기 위해, 이 특정 경우에는 스프레드 연산자 사용을 권장하지 않습니다. 이는 Vuex 상태 초기화와 같이 위의 방식이 유익한 모든 경우에 적용됩니다. 위의 패턴은 또한 인스턴스 생성 시 스칼라가 아닌 값을 쉽게 파싱할 수 있게 해줍니다.

return new Vue({
  el: '#element',
  name: 'ComponentNameRoot',
  components: {
    componentName
  },
  props: {
    foo,
    bar: parseBoolean(bar)
  },
  render: createElement => createElement('component-name'),
}));

템플릿 내 컴포넌트 사용#

  • 템플릿에서 컴포넌트를 사용할 때는 다른 스타일보다 컴포넌트의 케밥 케이스(kebab-cased) 이름을 선호하세요.
// bad


// good
<my-component />

컴포넌트 name 속성#

모든 Vue 컴포넌트에는 name 속성이 있어야 합니다. 파일명에서 파생된 PascalCase를 사용하세요. 예를 들어, board_app.vuename: 'BoardApp'이 됩니다.

app.vue 또는 index.vue와 같은 일반적인 파일명의 경우, 디렉터리 경로의 컨텍스트를 접두사로 붙여 고유한 이름을 만드세요. 예를 들어, admin/users/components/app.vuename: 'AdminUsersApp'이 됩니다.

EE 컴포넌트가 CE 컴포넌트와 이름을 공유할 경우, EE 컴포넌트 이름에 EE 접미사를 추가하세요. 예를 들어, app/ee/app/ 모두에 BranchSelector 컴포넌트가 있는 경우, EE 버전은 name: 'BranchSelectorEE'를 사용해야 합니다.

<style> 태그#

몇 가지 이유로 Vue 컴포넌트에서 <style> 태그를 사용하지 않습니다:

  • SCSS 변수와 믹스인 또는 Tailwind CSS@apply 지시어를 사용할 수 없습니다.

  • 이러한 스타일은 런타임에 삽입됩니다.

  • CSS를 정의하는 다른 방법이 이미 몇 가지 있습니다.

<style> 태그 대신 Tailwind CSS 유틸리티 클래스 또는 페이지별 CSS를 사용하세요.

Vue 테스트#

시간이 지나면서 Vue 컴포넌트를 효과적으로 테스트하려는 노력 속에서 여러 프로그래밍 패턴과 스타일 선호도가 생겨났습니다. 다음 가이드는 그 중 일부를 설명합니다. 이것들은 엄격한 지침이 아니라, GitLab에서 Vue 테스트를 작성하는 방법에 대한 통찰을 제공하기 위한 제안 및 모범 사례의 모음입니다.

컴포넌트 마운트#

일반적으로 Vue 컴포넌트를 테스트할 때, 컴포넌트는 모든 테스트 블록에서 "다시 마운트"되어야 합니다.

이를 위해:

  • 최상위 describe 블록 내에 변경 가능한 wrapper 변수를 생성합니다.

  • mount 또는 shallowMount를 사용하여 컴포넌트를 마운트합니다.

  • 결과 Wrapper 인스턴스를 wrapper 변수에 재할당합니다.

전역적이고 변경 가능한 wrapper를 생성하면 다음과 같은 여러 이점이 있습니다:

  • 컴포넌트/DOM 요소를 찾는 공통 함수를 정의할 수 있습니다:
import MyComponent from '~/path/to/my_component.vue';
describe('MyComponent', () => {
  let wrapper;

  // this can now be reused across tests
  const findMyComponent = wrapper.findComponent(MyComponent);
  // ...
})

비동기 자식 컴포넌트#

shallowMount비동기 자식 컴포넌트에 대한 컴포넌트 스텁을 생성하지 않습니다. 비동기 자식 컴포넌트를 올바르게 스텁하려면 stubs 옵션을 사용하세요. 비동기 자식 컴포넌트에 name 옵션이 정의되어 있는지 확인하세요. 그렇지 않으면 wrapperfindComponent 메서드가 올바르게 작동하지 않을 수 있습니다.

createComponent 팩토리#

마운트 로직 중복을 피하기 위해, 각 테스트 블록에서 재사용할 수 있는 createComponent 팩토리 함수를 정의하는 것이 유용합니다. 이것은 wrapper 변수를 mountshallowMount의 결과로 재할당해야 하는 클로저입니다:

import MyComponent from '~/path/to/my_component.vue';
import { shallowMount } from '@vue/test-utils';

describe('MyComponent', () => {
  // Initiate the "global" wrapper variable. This will be used throughout our test:
  let wrapper;

  // Define our `createComponent` factory:
  function createComponent() {
    // Mount component and reassign `wrapper`:
    wrapper = shallowMount(MyComponent);
  }

  it('mounts', () => {
    createComponent();

    expect(wrapper.exists()).toBe(true);
  });

  it('`isLoading` prop defaults to `false`', () => {
    createComponent();

    expect(wrapper.props('isLoading')).toBe(false);
  });
})

마찬가지로, beforeEach 블록에서 createComponent를 호출하여 테스트를 더욱 중복 제거할 수 있습니다:

import MyComponent from '~/path/to/my_component.vue';
import { shallowMount } from '@vue/test-utils';

describe('MyComponent', () => {
  // Initiate the "global" wrapper variable. This will be used throughout our test
  let wrapper;

  // define our `createComponent` factory
  function createComponent() {
    // mount component and reassign `wrapper`
    wrapper = shallowMount(MyComponent);
  }

  beforeEach(() => {
    createComponent();
  });

  it('mounts', () => {
    expect(wrapper.exists()).toBe(true);
  });

  it('`isLoading` prop defaults to `false`', () => {
    expect(wrapper.props('isLoading')).toBe(false);
  });
})

createComponent 모범 사례#

  • 여러 인수보다 단일(또는 제한된 수의) 객체 인수 사용을 고려하세요. props와 같은 공통 데이터에 대한 단일 파라미터를 정의하는 것은 괜찮지만, JavaScript 스타일 가이드를 참고하여 파라미터 수 제한을 지키세요:
// bad
function createComponent(props, stubs, mountFn, foo) { }

// good
function createComponent({ props, stubs, mountFn, foo } = {}) { }

// good
function createComponent(props = {}, { stubs, mountFn, foo } = {}) { }
  • 동일한 테스트 세트 내에서 mountshallowMount 모두 필요한 경우, 컴포넌트를 마운트하는 데 사용할 마운트 함수(mount 또는 shallowMount)를 받는 createComponent 팩토리의 mountFn 파라미터를 정의하는 것이 유용할 수 있습니다:
import { shallowMount } from '@vue/test-utils';

function createComponent({ mountFn = shallowMount } = {}) { }
  • wrapper.findByTestId()를 노출하려면 mountExtendedshallowMountExtended 헬퍼를 사용하세요:
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { SomeComponent } from 'components/some_component.vue';

let wrapper;

const createWrapper = () => { wrapper = shallowMountExtended(SomeComponent); };
const someButton = () => wrapper.findByTestId('someButtonTestId');
  • 컴포넌트 내부를 확장하는 data, methods 또는 다른 마운트 옵션 사용을 피하세요.
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { SomeComponent } from 'components/some_component.vue';

let wrapper;

// bad :( - This circumvents the actual user interaction and couples the test to component internals.
const createWrapper = ({ data }) => {
  wrapper = shallowMountExtended(SomeComponent, {
    data
  });
};

// good :) - Helpers like `clickShowButton` interact with the actual I/O of the component.
const createWrapper = () => {
  wrapper = shallowMountExtended(SomeComponent);
};
const clickShowButton = () => {
  wrapper.findByTestId('show').trigger('click');
}

컴포넌트 상태 설정#

  • 가능한 한 setProps를 사용하여 컴포넌트 상태를 설정하지 마세요. 대신 컴포넌트를 마운트할 때 컴포넌트의 propsData를 설정하세요:
// bad
wrapper = shallowMount(MyComponent);
wrapper.setProps({
  myProp: 'my cool prop'
});

// good
wrapper = shallowMount({ propsData: { myProp: 'my cool prop' } });

예외는 어떤 방식으로 컴포넌트 반응성을 테스트하고 싶을 때입니다. 예를 들어, 특정 감시자(watcher)가 실행된 후 컴포넌트의 출력을 테스트하고 싶을 수 있습니다. 이러한 동작을 테스트하기 위해 setProps를 사용하는 것은 괜찮습니다.

  • 컴포넌트의 내부 상태를 설정하고 컴포넌트의 실제 I/O 테스트를 우회하는 setData 사용을 피하세요. 대신 컴포넌트의 자식에서 이벤트를 트리거하거나 다른 부수 효과를 통해 강제로 상태를 변경하세요.

컴포넌트 상태 접근#

  • props 또는 속성에 접근할 때 wrapper.props().myProp 또는 wrapper.vm.myProp 대신 wrapper.props('myProp') 구문을 선호하세요:
// good
expect(wrapper.props().myProp).toBe(true);
expect(wrapper.attributes().myAttr).toBe(true);

// better
expect(wrapper.props('myProp').toBe(true);
expect(wrapper.attributes('myAttr')).toBe(true);
  • 여러 props를 어설션할 때 toEqual을 사용하여 props() 객체의 깊은 동등성을 확인하세요:
// good
expect(wrapper.props('propA')).toBe('valueA');
expect(wrapper.props('propB')).toBe('valueB');
expect(wrapper.props('propC')).toBe('valueC');

// better
expect(wrapper.props()).toEqual({
  propA: 'valueA',
  propB: 'valueB',
  propC: 'valueC',
});
// good
expect(wrapper.props()).toEqual(expect.objectContaining({
  propA: 'valueA',
  propB: 'valueB',
}));

// better
expect(wrapper.props()).toMatchObject({
  propA: 'valueA',
  propB: 'valueB',
});

props 유효성 검사 테스트#

컴포넌트 props를 확인할 때 assertProps 헬퍼를 사용하세요. props 유효성 검사 실패는 오류로 발생합니다:

import { assertProps } from 'helpers/assert_props'

// ...

expect(() => assertProps(SomeComponent, { invalidPropValue: '1', someOtherProp: 2 })).toThrow()