InfoGrab DocsInfoGrab Docs

위젯

요약

프론트엔드 위젯은 페이지에 추가해 기능의 일부를 담당하는 독립형 Vue 애플리케이션 또는 Vue 컴포넌트 트리입니다. 대표적인 위젯으로 사이드바 담당자와 사이드바 기밀 설정이 있습니다. 위젯을 만들 때는 아래에 설명하는 몇 가지 원칙을 따릅니다.

프론트엔드 위젯은 페이지에 추가해 기능의 일부를 담당하는 독립형 Vue 애플리케이션 또는 Vue 컴포넌트 트리입니다.

대표적인 위젯으로 사이드바 담당자와 사이드바 기밀 설정이 있습니다.

위젯을 만들 때는 아래에 설명하는 몇 가지 원칙을 따릅니다.

Vue Apollo 필수#

모든 위젯은 동일한 스택(Vue + Apollo Client)을 사용해야 합니다. 이를 위해 (위젯을 컴포넌트로 사용하는 경우) 애플리케이션 루트에 Vue Apollo를 추가하거나 위젯에 직접 제공해야 합니다. 사이드바 위젯에는 issuable Apollo Client와 Apollo Provider를 사용합니다.

import SidebarConfidentialityWidget from '~/sidebar/components/confidential/sidebar_confidentiality_widget.vue';
import { apolloProvider } from '~/graphql_shared/issuable_client';

function mountConfidentialComponent() {
  new Vue({
    apolloProvider,
    components: {
      SidebarConfidentialityWidget,
    },
    /* ... */
  });
}

필수 주입 항목#

편집 가능한 모든 사이드바 위젯은 접힘/펼침 상태를 다루기 위해 SidebarEditableItem을 사용해야 합니다. 이 컴포넌트는 애플리케이션 루트에서 제공하는 canUpdate 속성을 필요로 합니다.

전역 상태 매핑 금지#

위젯은 최대한 재사용할 수 있도록 만드는 것이 목표입니다. 그래서 위젯이나 그 하위 컴포넌트에 외부 상태 바인딩을 추가하지 않습니다. Vuex 매핑과 중재자 스토어도 여기에 해당합니다.

위젯의 책임#

위젯은 자신이 담당하도록 설계된 엔터티(담당자, 이터레이션 등)를 가져오고 업데이트하는 일을 맡습니다. 즉, 위젯은 (Apollo 캐시에 이미 없다면) 항상 데이터를 직접 가져와야 합니다. 위젯에 초기값을 제공하더라도, 위젯은 백그라운드에서 GraphQL 쿼리를 실행해 그 결과를 Apollo 캐시에 저장해야 합니다.

장기적으로 Apollo Client 캐시를 애플리케이션 전역 상태로 사용하게 되면 사이드바 위젯에 초기 데이터를 전달할 필요가 없어집니다. 그때는 위젯이 캐시에서 데이터를 가져올 수 있습니다.

GraphQL 쿼리와 뮤테이션 사용#

위젯은 서로 다른 엔터티(에픽, 이슈, 머지 리퀘스트 등)와 함께 동작할 수 있을 만큼 유연해야 합니다. 사이드바마다 다른 GraphQL 쿼리와 뮤테이션이 필요하므로 매핑을 만듭니다.

export const assigneesQueries = {
  [TYPE_ISSUE]: {
    query: getIssueParticipants,
    mutation: updateAssigneesMutation,
  },
  [TYPE_MERGE_REQUEST]: {
    query: getMergeRequestParticipants,
    mutation: updateMergeRequestParticipantsMutation,
  },
};

쿼리 업데이트에 같은 로직을 적용하기 위해 쿼리 필드에 별칭을 붙입니다. 예를 들면 다음과 같습니다.

  • group 또는 project는 namespace가 됩니다
  • issue, epic, mergeRequest는 issuable 이 됩니다

아쉽게도 Apollo는 별칭이 붙은 필드의 typename을 undefined로 지정하므로 __typename을 명시적으로 가져와야 합니다.

query issueConfidential($fullPath: ID!, $iid: String) {
  namespace: project(fullPath: $fullPath) {
    __typename
    issuable: issue(iid: $iid) {
      __typename
      id
      confidential
    }
  }
}

다른 Vue 애플리케이션과의 통신#

위젯 상태의 변경을 (예를 들어 뮤테이션이 성공한 뒤) 상위 애플리케이션에 알려야 한다면 이벤트를 발행합니다.

updateAssignees(assigneeUsernames) {
  return this.$apollo
    .mutate({
      mutation: this.$options.assigneesQueries[this.issuableType].mutation,
      variables: {...},
    })
    .then(({ data }) => {
      const assignees = data.issueSetAssignees?.issue?.assignees?.nodes || [];
      this.$emit('assignees-updated', assignees);
    })
}

NotesApp처럼 다른 Vue 애플리케이션의 변경을 수신해야 할 때도 있습니다. 이 경우에는 클라이언트를 가져와 특정 쿼리를 수신하는 렌더리스 컴포넌트를 사용할 수 있습니다.

import { fetchPolicies } from '~/lib/graphql';
import { confidentialityQueries } from '~/sidebar/constants';
import { defaultClient as gqlClient } from '~/graphql_shared/issuable_client';

created() {
  if (this.issuableType !== IssuableType.Issue) {
    return;
  }

  gqlClient
    .watchQuery({
      query: confidentialityQueries[this.issuableType].query,
      variables: {...},
      fetchPolicy: fetchPolicies.CACHE_ONLY,
    })
    .subscribe((res) => {
      this.setConfidentiality(issuable.confidential);
    });
},
methods: {
  ...mapActions(['setConfidentiality']),
},

이러한 컴포넌트의 예시를 확인합니다.

머지 리퀘스트 위젯#

머지 리퀘스트 위젯 프레임워크 전용 문서를 참고합니다.

위젯

GitLab v19.4
원문 보기

요약

프론트엔드 위젯은 페이지에 추가해 기능의 일부를 담당하는 독립형 Vue 애플리케이션 또는 Vue 컴포넌트 트리입니다. 대표적인 위젯으로 사이드바 담당자와 사이드바 기밀 설정이 있습니다. 위젯을 만들 때는 아래에 설명하는 몇 가지 원칙을 따릅니다.

프론트엔드 위젯은 페이지에 추가해 기능의 일부를 담당하는 독립형 Vue 애플리케이션 또는 Vue 컴포넌트 트리입니다.

대표적인 위젯으로 사이드바 담당자와 사이드바 기밀 설정이 있습니다.

위젯을 만들 때는 아래에 설명하는 몇 가지 원칙을 따릅니다.

Vue Apollo 필수#

모든 위젯은 동일한 스택(Vue + Apollo Client)을 사용해야 합니다. 이를 위해 (위젯을 컴포넌트로 사용하는 경우) 애플리케이션 루트에 Vue Apollo를 추가하거나 위젯에 직접 제공해야 합니다. 사이드바 위젯에는 issuable Apollo Client와 Apollo Provider를 사용합니다.

import SidebarConfidentialityWidget from '~/sidebar/components/confidential/sidebar_confidentiality_widget.vue';
import { apolloProvider } from '~/graphql_shared/issuable_client';

function mountConfidentialComponent() {
  new Vue({
    apolloProvider,
    components: {
      SidebarConfidentialityWidget,
    },
    /* ... */
  });
}

필수 주입 항목#

편집 가능한 모든 사이드바 위젯은 접힘/펼침 상태를 다루기 위해 SidebarEditableItem을 사용해야 합니다. 이 컴포넌트는 애플리케이션 루트에서 제공하는 canUpdate 속성을 필요로 합니다.

전역 상태 매핑 금지#

위젯은 최대한 재사용할 수 있도록 만드는 것이 목표입니다. 그래서 위젯이나 그 하위 컴포넌트에 외부 상태 바인딩을 추가하지 않습니다. Vuex 매핑과 중재자 스토어도 여기에 해당합니다.

위젯의 책임#

위젯은 자신이 담당하도록 설계된 엔터티(담당자, 이터레이션 등)를 가져오고 업데이트하는 일을 맡습니다. 즉, 위젯은 (Apollo 캐시에 이미 없다면) 항상 데이터를 직접 가져와야 합니다. 위젯에 초기값을 제공하더라도, 위젯은 백그라운드에서 GraphQL 쿼리를 실행해 그 결과를 Apollo 캐시에 저장해야 합니다.

장기적으로 Apollo Client 캐시를 애플리케이션 전역 상태로 사용하게 되면 사이드바 위젯에 초기 데이터를 전달할 필요가 없어집니다. 그때는 위젯이 캐시에서 데이터를 가져올 수 있습니다.

GraphQL 쿼리와 뮤테이션 사용#

위젯은 서로 다른 엔터티(에픽, 이슈, 머지 리퀘스트 등)와 함께 동작할 수 있을 만큼 유연해야 합니다. 사이드바마다 다른 GraphQL 쿼리와 뮤테이션이 필요하므로 매핑을 만듭니다.

export const assigneesQueries = {
  [TYPE_ISSUE]: {
    query: getIssueParticipants,
    mutation: updateAssigneesMutation,
  },
  [TYPE_MERGE_REQUEST]: {
    query: getMergeRequestParticipants,
    mutation: updateMergeRequestParticipantsMutation,
  },
};

쿼리 업데이트에 같은 로직을 적용하기 위해 쿼리 필드에 별칭을 붙입니다. 예를 들면 다음과 같습니다.

  • group 또는 project는 namespace가 됩니다
  • issue, epic, mergeRequest는 issuable 이 됩니다

아쉽게도 Apollo는 별칭이 붙은 필드의 typename을 undefined로 지정하므로 __typename을 명시적으로 가져와야 합니다.

query issueConfidential($fullPath: ID!, $iid: String) {
  namespace: project(fullPath: $fullPath) {
    __typename
    issuable: issue(iid: $iid) {
      __typename
      id
      confidential
    }
  }
}

다른 Vue 애플리케이션과의 통신#

위젯 상태의 변경을 (예를 들어 뮤테이션이 성공한 뒤) 상위 애플리케이션에 알려야 한다면 이벤트를 발행합니다.

updateAssignees(assigneeUsernames) {
  return this.$apollo
    .mutate({
      mutation: this.$options.assigneesQueries[this.issuableType].mutation,
      variables: {...},
    })
    .then(({ data }) => {
      const assignees = data.issueSetAssignees?.issue?.assignees?.nodes || [];
      this.$emit('assignees-updated', assignees);
    })
}

NotesApp처럼 다른 Vue 애플리케이션의 변경을 수신해야 할 때도 있습니다. 이 경우에는 클라이언트를 가져와 특정 쿼리를 수신하는 렌더리스 컴포넌트를 사용할 수 있습니다.

import { fetchPolicies } from '~/lib/graphql';
import { confidentialityQueries } from '~/sidebar/constants';
import { defaultClient as gqlClient } from '~/graphql_shared/issuable_client';

created() {
  if (this.issuableType !== IssuableType.Issue) {
    return;
  }

  gqlClient
    .watchQuery({
      query: confidentialityQueries[this.issuableType].query,
      variables: {...},
      fetchPolicy: fetchPolicies.CACHE_ONLY,
    })
    .subscribe((res) => {
      this.setConfidentiality(issuable.confidential);
    });
},
methods: {
  ...mapActions(['setConfidentiality']),
},

이러한 컴포넌트의 예시를 확인합니다.

머지 리퀘스트 위젯#

머지 리퀘스트 위젯 프레임워크 전용 문서를 참고합니다.