실시간 뷰 컴포넌트 구축 및 배포
GitLab은 사용자 입력을 받아들이고 상태 변경을 사용자에게 다시 표시하는 개별 뷰 컴포넌트를 통해 대화형 사용자 경험을 제공합니다. 예를 들어 Merge Request 페이지에서 사용자는 승인, 의견 남기기, CI/CD 파이프라인과 상호 작용 등을 할 수 있습니다.
그러나 GitLab은 종종 상태 업데이트를 즉시 반영하지 못합니다. 이는 페이지의 일부가 사용자가 페이지를 다시로드한 후에만 업데이트되는 낡은 데이터를 표시한다는 것을 의미합니다.
이를 해결하기 위해 GitLab은 뷰 컴포넌트가 웹소켓을 통해 실시간으로 상태 업데이트를 수신할 수 있게 해주는 기술과 프로그래밍 API를 도입했습니다.
다음 문서는 GitLab Ruby on Rails 서버에서 실시간으로 업데이트를 받는 뷰 컴포넌트를 구축하고 배포하는 방법에 대해 안내합니다.
#f_real-time
내부 Slack 채널에서 도움을 요청하세요.실시간 뷰 컴포넌트 구축
전제 조건:
다음을 읽으세요:
GitLab에서 실시간 뷰 컴포넌트를 구축하려면 다음을 수행해야 합니다:
- GitLab 프론트엔드에서 Apollo 구독을 통합하여 Vue 컴포넌트를 통합합니다.
- GitLab Ruby on Rails 백엔드에서 GraphQL 구독을 추가하고 트리거합니다.
Vue 컴포넌트를 Apollo 구독과 통합
가상의 IssueView
Vue 컴포넌트를 고려해보겠습니다. 이 컴포넌트는 GitLab Issue
데이터를 관찰하고 렌더링하는 것으로 가정합니다. 여기서는 단순함을 위해 문제의 제목과 설명만 렌더링하는 것으로 가정합니다:
import issueQuery from '~/issues/queries/issue_view.query.graqhql';
export default {
props: {
issueId: {
type: Number,
required: false,
default: null,
},
},
apollo: {
// Apollo 쿼리 객체의 이름. `data`에 의해 바운드된 필드 이름과 일치해야 합니다.
issue: {
// 초기 가져오기에 사용되는 쿼리.
query: issueQuery,
// 초기 가져오기 쿼리에 사용되는 인수를 바운드합니다.
variables() {
return {
iid: this.issueId,
};
},
// 응답 데이터를 뷰 프로퍼티로 매핑합니다.
update(data) {
return data.project?.issue || {};
},
},
},
// 반응형 Vue 컴포넌트 데이터. Apollo는 이러한 데이터를 쿼리가 반환하거나 구독이 발생할 때 업데이트합니다.
data() {
return {
issue: {}, // 뷰가 로드되는 동안 초기 상태를 반환하는 것이 좋은 실천입니다.
};
},
};
// <template> 코드는 이 토론과 관련이 없으므로 생략됩니다.
쿼리는 다음과 같아야 합니다:
-
app/assets/javascripts/issues/queries/issue_view.query.graqhql
에 정의되어야 합니다. -
다음 GraphQL 작업을 포함해야 합니다:
query gitlabIssue($iid: String!) { # 여기서 경로는 예시만을 위해 직접 지정합니다. 실제로는 이렇게 하지 않아야 합니다. project(fullPath: "gitlab-org/gitlab") { issue(iid: $iid) { title description } } }
지금까지 이 뷰 컴포넌트는 데이터로 자신을 채우기 위해 초기 가져오기 쿼리만 정의합니다. 이는 뷰에 의해 시작되는 일반적인 GraphQL query
작업으로, HTTP POST 요청으로 전송됩니다. 서버에서의 이후 업데이트는 이 뷰를 낡게 만듭니다. 따라서 서버에서 업데이트를 받으려면 다음과 같아야 합니다:
- GraphQL 구독 정의를 추가합니다.
- Apollo 구독 후크를 정의합니다.
GraphQL 구독 정의 추가
구독은 GraphQL 쿼리를 정의하지만, 이는 GraphQL subscription
작업 내부에 포장됩니다. 이 쿼리는 백엔드에서 초기화되고, 그 결과는 WebSocket을 통해 뷰 컴포넌트로 푸시됩니다.
초기 가져오기 쿼리와 유사하게 다음을 수행해야 합니다:
-
app/assets/javascripts/issues/queries/issue_updated.subscription.graqhql
에 구독 파일을 정의해야 합니다. -
파일에 다음과 같은 GraphQL 작업을 포함해야 합니다:
subscription issueUpdatedSubscription($iid: String!) { issueUpdated($issueId: IssueID!) { issue(issueId: $issueId) { title description } }
새로운 구독을 추가할 때 다음과 같은 이름 지침을 사용하세요:
- 구독의 작업 이름을
Subscription
으로 끝내거나 GitLab EE에만 적용되는 경우SubscriptionEE
로 끝내세요. 예:issueUpdatedSubscription
, 또는issueUpdatedSubscriptionEE
. - 구독 이벤트 이름에 “발생했음” 동사를 사용하세요. 예:
issueUpdated
.
구독 정의는 일반적인 쿼리와 유사해 보이지만, 이해하는 데 중요한 몇 가지 주요 차이점이 있습니다:
-
쿼리
:- 프론트엔드에서 시작됩니다.
- URL에 사용되는 것과 같이 내부 ID(
iid
, 숫자)를 사용합니다. 이 내부 ID는 일반적으로 URL에서 엔티티가 참조되는 방식입니다. 이 예에서는project
의 내부 ID라는 것을 기억해야 합니다.
-
구독
:- 미래의 업데이트를 수신하기 위해 백엔드에서 프론트엔드로의 요청입니다.
- 다음과 같은 것으로 구성됩니다:
- 구독 자체를 나타내는 작업 이름(
issueUpdatedSubscription
이 예의 경우) - 중첩된 이벤트 쿼리(
issueUpdated
이 예의 경우) 이 이벤트 쿼리:- 동일한 이름의 GraphQL 트리거 실행 시 실행됩니다.
- GraphQL에서 리소스를 식별하는 선호되는 방법인 숫자가 아닌 전역 ID 문자열을 사용합니다. 자세한 내용은 GraphQL 전역 ID를 참조하세요.
- 구독 자체를 나타내는 작업 이름(
Apollo 구독 후크를 정의합니다
구독을 정의한 후, Apollo의 subscribeToMore
속성을 사용하여 뷰 컴포넌트에 추가합니다:
import issueQuery from '~/issues/queries/issue_view.query.graqhql';
import issueUpdatedSubscription from '~/issues/queries/issue_updated.subscription.graqhql';
export default {
// 이전과 동일합니다.
// ...
apollo: {
issue: {
// 이전과 동일합니다.
// ...
// 이 Apollo 후크는 실시간 푸시를 가능하게 합니다.
subscribeToMore: {
// 미래의 업데이트를 반환하는 구독 작업입니다.
document: issueUpdatedSubscription,
// 구독 작업에 사용되는 인수를 바운드합니다.
variables() {
return {
iid: this.issueId,
};
},
// feature-flags를 사용할 때 유용한 구독 비활성화 여부를 반환합니다.
skip() {
return this.shouldSkipRealTimeUpdates;
},
},
},
},
// 이전과 동일합니다.
// ...
computed: {
shouldSkipRealTimeUpdates() {
return false; // 여기서 피처 플래그를 확인할 수 있습니다.
},
},
};
이제 Apollo를 통해 WebSocket 연결을 통해 뷰 컴포넌트가 업데이트를 수신하도록 활성화할 수 있습니다. 다음으로, 백엔드에서 어떻게 이벤트가 트리거될지에 대해 알아봅니다.
GraphQL 구독 트리거
WebSocket에서 업데이트를 수신할 수 있는 뷰 컴포넌트를 작성하는 것은 이야기의 반만입니다. GitLab Rails 애플리케이션에서는 다음 단계를 수행해야 합니다.
-
GraphQL::Schema::Subscription
클래스를 구현합니다. 이 클래스는 다음을 수행합니다:-
graphql-ruby
에서 프론트엔드에서 보낸subscription
작업을 해결하는 데 사용됩니다. - 구독이 가져야 하는 인수 및 호출자에게 반환되는 페이로드를 정의합니다.
- 호출자가이 구독을 생성할 권한이 있는지 확인하려면 필요한 비즈니스 로직을 실행합니다.
-
-
Types::SubscriptionType
클래스에 새로운field
를 추가합니다. 이 필드는 Vue 컴포넌트를 통합할 때 사용되는 이벤트 이름을GraphQL::Schema::Subscription
클래스에 매핑합니다. - 이벤트 이름과 일치하는 메서드를
GraphqlTriggers
에 추가하여 해당 GraphQL 트리거를 실행합니다. - 서비스 또는 Active Record 모델 클래스를 사용하여 새 트리거를 도메인 로직의 일부로 실행합니다.
구독 구현
이미 구현 된 GraphQL::Schema::Subscription
으로 이벤트를 구독하는 경우에는이 단계가 선택 사항입니다.
그렇지 않으면 app/graphql/subscriptions/
에 새로운 구독을 구현합니다. Issue
가 업데이트되어
issueUpdated
이벤트가 발생하는 예를 들어, 구독 구현은 다음과 같습니다:
module Subscriptions
class IssueUpdated < BaseSubscription
include Gitlab::Graphql::Laziness
payload_type Types::IssueType
argument :issue_id, Types::GlobalIDType[Issue],
required: true,
description: '이슈의 ID.'
def authorized?(issue_id:)
issue = force(GitlabSchema.find_by_gid(issue_id))
unauthorized! unless issue && Ability.allowed?(current_user, :read_issue, issue)
true
end
end
end
새 클래스를 만들 때:
- 모든 구독 유형이
Subscriptions::BaseSubscription
에서 상속되도록합니다. -
payload_type
을 사용하여 구독된 쿼리가 액세스 할 수있는 데이터를 나타냅니다. 또는 노출 하려는 개별field
를 정의합니다. - 각 클라이언트가 구독하거나 이벤트가 발생할 때 호출되는 사용자 정의
subscribe
및update
훅을 정의 할 수도 있습니다. 이러한 메서드를 사용하는 방법에 대한 자세한 내용은 공식 문서를 참조하십시오. -
authorized?
를 구현하여 필요한 권한 확인을 수행합니다. 이러한 확인은subscribe
또는update
를 호출 할 때마다 실행됩니다.
공식 문서에서 GraphQL 구독 클래스에 대해 자세히 알아보세요.
구독 연결
새 구독 클래스를 구현하지 않은 경우이 단계를 건너 뜁니다.
새 구독 클래스를 구현 한 후에는 실행 전에 Types::SubscriptionType
에 해당 클래스를 field
에 매핑해야합니다.
Types::SubscriptionType
클래스를 열고 새 필드를 추가합니다.
module Types
class SubscriptionType < ::Types::BaseObject
graphql_name 'Subscription'
# 기존 필드
# ...
field :issue_updated,
subscription: Subscriptions::IssueUpdated, null: true,
description: '이슈가 업데이트될 때 트리거됩니다.'
end
end
EE::Types::SubscriptionType
을 업데이트하십시오.이슈가 업데이트 됨
에 일치하는 issue_updated
argument가
프론트 엔드에서 camel-case로 보내는 subscription
요청에 사용된 이름과 일치해야합니다. 그렇지 않으면 graphql-ruby
는
어떤 가입자에게 알릴지 모릅니다. 이 이벤트로 인해 트리거될 수 있습니다.
새 트리거 추가
기존 트리거를 재사용할 수 있는 경우이 단계를 건너 뜁니다.
우리는 GitlabSchema.subscriptions.trigger
주변에 퍼사드를 사용하여 이벤트를 트리거하는 것을 간단하게 만듭니다.
GraphqlTriggers
에 새 트리거를 추가합니다.
module GraphqlTriggers
# 기존 트리거
# ...
def self.issue_updated(issue)
GitlabSchema.subscriptions.trigger(:issue_updated, { issue_id: issue.to_gid }, issue)
end
end
EE::GraphqlTriggers
을 업데이트하십시오.- 첫 번째 인수 인
:issue_updated
는 이전 단계에서 사용된field
이름과 일치해야합니다. - 해당 인수 해시는이 이벤트를 발행해야하는 주제를 식별하는 데 사용됩니다. GraphQL은이 해시를 사용하여 이벤트를 게시 할 주제를 식별합니다.
최종 단계는이 트리거 함수를 호출하는 것입니다.
트리거 실행
이 단계의 실행은 빌드 중인 기능에 따라 다릅니다. 예를 들어
이슈의 필드가 변경되는 경우 Issues::UpdateService
를 확장하여 GraphqlTriggers.issue_updated
를 호출 할 수 있습니다.
실시간 뷰 컴포넌트가 이제 작동합니다. 이슈의 업데이트는 GitLab UI로 즉시 전파되어야합니다.
실시간 뷰 컴포넌트 배포
WebSocket은 GitLab에서 상대적으로 새로운 기술이며 대규모로 지원하려면 일부 도전 과제가 있습니다. 그래서 새로운 기능은 아래 지침에 따라 롤아웃해야합니다.
실시간 컴포넌트 배포
WebSockets을 통한 업데이트는 적절한 백엔드 코드가 준비되어 있지 않은 경우에는 시뮬레이션하기 어렵기 때문에 프론트 엔드 및 백엔드에서 동시에 작업 할 수 있습니다.
그러나 변경 사항을 별도의 머지 요청으로 보내고 먼저 백엔드 변경 사항을 배포하는 것이 더 안전합니다. 이를 통해 프론트 엔드가 이벤트를 구독하기 시작할 때 백엔드가 이미 서비스 준비가되어 있음을 보장할 수 있습니다.
기존 WebSocket 연결 재사용
기존 연결을 재사용하는 기능은 최소한의 위험을 수반합니다. 피처 플래그 롤 아웃은 자체 호스팅 고객에 더 많은 제어를 제공하기 위해 권장됩니다 그러나 GitLab.com에 대한 새로운 연결을 추정하거나 백분율로 롤 아웃하거나 할 필요는 없습니다.
새로운 WebSocket 연결 소개
GitLab 애플리케이션 일부에 WebSocket 연결을 도입하는 변경 사항은 유지하는 노드 및 Redis 및 주 데이터베이스의 하위 서비스에 대한 확장 가능성 리스크를 초래합니다.
최대 연결 추정
GitLab.com에 완전히 활성화 된 첫 번째 실시간 기능은 실시간 담당자 였습니다. 페이지의 최대 처리량과 최대 동시 WebSocket 연결에 대한 비교를 통해 초당 1 요청 당 약 4200 개의 WebSocket 연결이 추가된다는 것을 대략적으로 추정할 수 있습니다.
새 기능이 어떤 영향을 미칠지 이해하기 위해 최대 처리량 (RPS)을 요청하고 그것이 유래 한 페이지 (들) (n)`이 연결 중인 것과 비교하여 다음 공식을 적용하십시오.
(n * 4200) / 최대_활성_연결
현재 활성 연결은 이 Grafana 차트에서 확인할 수 있습니다.
이 계산은 대략적이며 새로운 기능이 배포 될 때마다 재검토되어야합니다. 기존 용량의 일정 비율로 지원해야하는 용량에 대한 대략적인 추정치를 제공합니다.
Graduated roll-out
새로운 용량은 변경 사항을 지원하기 위해 프로비저닝되어야 할 수 있으며, 이는 현재 포화 상태 및 필요한 새 연결의 비율에 따라 다릅니다. 대부분의 경우 쿠버네티스를 사용하면 비교적 쉽지만, 하향 스트림 서비스에는 여전히 위험이 존재합니다.
이를 완화하기 위해, 새로운 WebSocket 연결을 설정하는 코드가 피처 플래그로 설정되고 기본적으로 off
로 설정되도록합니다. 피처 플래그의 주의 깊고 백분율 기반의 롤아웃은 효과가 WebSocket 대시보드에서 관찰될 수 있도록합니다(https://dashboards.gitlab.net/d/websockets-main/websockets-overview?orgId=1).
- 피처 플래그 롤아웃 이슈를 생성하세요.
- 어떤 일이 일어날 것으로 기대하고 있나요 섹션에 예상 새 연결을 추가합니다.
- 백분율 기반 롤아웃 계획을 추정하기 위해 Plan 및 Scalability 팀 구성원을 추가하세요.
Backward compatibility
피처 플래그 롤아웃 기간 동안 또는 이후에 실시간 기능은 역방향 호환되어야하며, 최소한 우아하게 감소되어야합니다. 모든 고객이 Action Cable을 활성화하지 않았으며, Action Cable을 기본적으로 활성화하기 전에 추가 작업이 필요합니다.
실시간을 요구함은 중단 변경을 나타냅니다, 이에 대한 다음 기회는 15.0 버전입니다.
GitLab.com의 실시간 인프라
GitLab.com에서 WebSocket 연결은 별도의 인프라에서 제공되며, 일반적인 웹 플리트와 쿠버네티스로 배포된 것과 완전히 분리되어 있습니다. 이는 요청을 처리하는 노드에 대한 위험을 제한하지만 공유 서비스에는 적용되지 않습니다. WebSockets 쿠버네티스 배포에 대한 자세한 내용은 이 epic을 참조하십시오.
GitLab 실시간 스택 심층 분석
서버에서 시작된 푸시는 네트워크를 통해 전파되어 사용자 상호 작용 없이 클라이언트에서 보기 업데이트를 트리거해야 하므로, 전체 스택을 포함한 프론트엔드 및 백엔드를 살펴봄으로써 실시간 기능을 이해할 수 있습니다.
realtime_changes
라고 불립니다. 그것들은 조건부 GET 요청을 사용하며, 본 안내서에서 다루는 실시간 동작과는 관련이 없습니다.클라이언트에 푸시된 실시간 업데이트는 GitLab Rails 애플리케이션에서 기인합니다. 우리는 다음 기술을 사용하여 이러한 업데이트를 초기화하고 처리합니다:
GitLab Rails 백엔드에서:
- 구독 상태를 처리하기 위한 Redis PubSub.
- WebSocket 연결 및 데이터 전송을 처리하는 Action Cable.
- GraphQL 구독 및 트리거를 구현하기 위한
graphql-ruby
.
GitLab 프론트엔드에서:
- GraphQL 요청, 라우팅 및 캐싱을 처리하기 위한 Apollo Client.
- 실시간으로 업데이트되는 view 컴포넌트를 정의하고 렌더링하기 위한 Vue.js.
아래 다이어그램은 데이터가 이러한 계층간에 어떻게 전파되는지 설명합니다.
다음 섹션에서는 이 스택의 각 요소를 자세히 설명합니다.
Action Cable and WebSockets
Action Cable은 Ruby on Rails에 WebSocket 지원을 추가하는 라이브러리입니다. WebSockets는 기존의 HTTP 기반 서버 및 응용 프로그램을 단일 TCP 연결을 통해 양방향 통신을 향상시키기 위한 HTTP 친화적인 솔루션으로 개발되었습니다. 클라이언트는 먼저 일반 HTTP 요청을 서버에 보내어 연결을 WebSocket으로 업그레이드하도록 요청하고, 성공하면 동일한 TCP 연결을 사용하여 클라이언트와 서버 모두가 양방향으로 데이터를 송수신할 수 있습니다.
WebSocket 프로토콜은 전송된 데이터를 어떻게 인코딩하거나 구조화해야 하는지 지정하지 않기 때문에, Action Cable과 같은 라이브러리가 이러한 문제를 처리합니다. Action Cable은 다음과 같은 작업을 처리합니다:
- HTTP에서 WebSocket 프로토콜로의 초기 연결 업그레이드를 처리합니다. 이후
ws://
스키마를 사용한 요청은 Action Cable 서버에서 처리되며 Action Pack에서 처리되지 않습니다. - WebSocket을 통해 전송된 데이터의 인코딩 방법을 정의합니다. Action Cable은 이를 JSON으로 지정합니다. 이로써 응용 프로그램은 데이터를 Ruby Hash로 제공하고 Action Cable은 그것을 JSON으로(으로부터) 직렬화 및 역질렬화합니다.
- 클라이언트 연결 또는 연결 해제 및 클라이언트 인증을 처리하기 위한 콜백 훅을 제공합니다.
-
ActionCable::Channel
을 제공하여 게시/구독 및 원격 프로시저 호출을 구현하는 개발자 추상화를 제공합니다.
Action Cable은 서로 다른 구현을 지원하여 어떤 클라이언트가 어떤 ActionCable::Channel
에 가입했는지 추적하는데 사용합니다. 우리는 Redis 어댑터를 사용하여, 이는 분산 메시지 버스로서 Redis PubSub 채널을 사용합니다. 다른 클라이언트가 다른 Puma 인스턴스에서 동일한 Action Cable 채널에 연결할 수 있기 때문에 공유 스토리지가 필요합니다.
Channel
객체는 WebSocket 연결을 통해 전송되는 데이터의 여러 종류를 분류하고 처리하기 위한 프로그래밍 추상화입니다. Action Cable에서는 이러한 배포는 브로드캐스팅이라고 하며 클라이언트와 브로드캐스팅 간의 연관은 구독으로 불립니다. 특히, 각 Action Cable Channel
에는 여러 브로드캐스팅(PubSub 채널) 및 각 Action Cable Channel
에 대한 여러 구독이 있을 수 있습니다.Action Cable을 이용하면 Channel
API를 통해 다양한 종류의 동작을 표현할 수 있으며, 모든 Channel
의 업데이트는 동일한 WebSocket 연결을 통해 사용될 수 있으므로 각 GitLab 페이지에 푸시 기반 업데이트가 필요한 모든 페이지에는 단일 WebSocket 연결만 필요합니다.
GitLab 페이지의 실시간 업데이트를 구현하기 위해 우리는 개별 Channel
구현을 작성하는 대신, 모든 GitLab 페이지에 푸시 기반 업데이트가 필요한 페이지에 구독하는 GraphQLChannel
을 제공합니다.
GraphQL 구독: 백엔드
GitLab은 클라이언트가 GraphQL 쿼리를 사용하여 구조화된 데이터를 서버에서 요청할 수 있도록 지원합니다.
GraphQL 지원은 GitLab GraphQL 개요를 확인하여 왜 우리가 GraphQL을 채택했는지에 대해 알아보세요.
GitLab 백엔드의 GraphQL 지원은 graphql-ruby
젬에 의해 제공됩니다.
일반적으로 GraphQL 쿼리는 클라이언트에서 시작되는 HTTP POST 요청으로 표준 요청-응답 주기를 따릅니다.
실시간 기능을 위해 대신 GraphQL 구독을 사용하는데, 이는 발행/구독 패턴의 구현입니다.
이 접근 방식에서 먼저 클라이언트는 GraphqlChannel
로 구독 요청을 보내는데, 이때 다음과 같은 정보가 필요합니다:
- 구독
필드
의 이름(이벤트 이름). - 이 이벤트가 트리거될 때 실행할 GraphQL 쿼리.
이 정보는 서버에 의해 사용되어 이 이벤트 스트림을 나타내는 주제
를 생성합니다. 주제는 구독 인수와 이벤트 이름에서 파생된 고유한 이름으로,
이벤트가 트리거될 경우 통지를 받아야 하는 모든 구독자를 식별하는 데 사용됩니다. 동일한 주제에 여러 클라이언트가 구독할 수 있습니다.
예를 들어, issuableAssigneesUpdated:issuableId:<hashed_id>
는 지정된 ID를 가진 이슈의 담당자가 변경될 때 업데이트를 받기를 원하는 경우 구독하는 주제로 사용될 수 있습니다.
백엔드는 일반적으로 “에픽에 이슈 추가” 또는 “사용자가 이슈에 할당됨”과 같은 도메인 이벤트에 응답하여 구독을 트리거합니다.
GitLab에서는 서비스 객체 또는 ActiveRecord 모델 객체로 이것이 수행될 수 있습니다.
트리거는 해당 이벤트 이름과 인수를 사용하여 GitlabSchema.subscriptions.trigger
를 호출하여 실행되며,
이를 통해 graphql-ruby
에서 주제를 파생시킵니다. 그런 다음 해당 주제의 모든 구독자를 찾아 각 구독자의 쿼리를 실행하고 결과를 모든 주제 구독자에게 다시 푸시합니다.
GraphQL 구독을 위한 기본 전송으로서 Action Cable을 사용하기 때문에 주제는 Action Cable 방송으로 구현됩니다. 위에서 언급한대로 각 구독자에 대해 두 개의 PubSub 채널이 사용됩니다:
- 각 주제 당 하나의
graphql-event:<namespace>:<topic>
채널입니다. 이 채널은 어떤 클라이언트가 어떤 이벤트에 구독되어 있는지를 추적하는 데 사용되며 모든 잠재적인 클라이언트 사이에서 공유됩니다.namespace
의 사용은 선택사항이며 비어 있을 수 있습니다. - 각 클라이언트 당 하나의
graphql-subscription:<subscription-id>
채널입니다. 이 채널은 쿼리 결과를 해당 클라이언트에 다시 전송하기 위해 사용되며 따라서 다른 클라이언트 간에 공유될 수 없습니다.
다음 섹션에서는 GitLab 프론트엔드가 GraphQL 구독을 사용하여 실시간 업데이트를 구현하는 방법에 대해 설명합니다.
GraphQL 구독: 프론트엔드
GitLab 프론트엔드는 루비가 아닌 JavaScript를 실행하므로 클라이언트에서 서버로 GraphQL 쿼리, 뮤테이션 및 구독을 보내기 위해 다른 GraphQL 구현이 필요합니다. 우리는 이를 위해 Apollo를 사용합니다.
Apollo는 JavaScript에서 GraphQL의 포괄적인 구현이며 apollo-server
와 apollo-client
로 분리되어 있으며 추가적인 유틸리티 모듈도 제공합니다. 우리는 루비 백엔드를 실행하기 때문에 apollo-server
대신 apollo-client
를 사용합니다.
이것으로 다음을 단순화합니다:
- 네트워킹, 연결 관리 및 요청 라우팅.
- 클라이언트 측 상태 관리 및 응답 캐싱.
- 뷰 컴포넌트와의 GraphQL 통합을 위해 브릿지 모듈 사용.
Apollo는 다음과 같이 정의할 수 있는 함수와 훅을 제공합니다:
- 뷰가 쿼리, 뮤테이션 또는 구독을 보내는 방법.
- 응답을 다루어야 하는지 여부.
- 응답 데이터가 캐시되는 방법.
진입점은 ApolloClient
로서, 이는:
- 단일 페이지의 모든 뷰 컴포넌트 사이에서 공유됩니다.
- 서버와 통신하기 위해 모든 뷰 컴포넌트가 내부적으로 사용합니다.
다양한 유형의 요청을 어떻게 라우팅할지 결정하기 위해, Apollo는 ApolloLink
추상화를 사용합니다. 구체적으로,
실시간 서버 구독을 다른 GraphQL 요청과 분리하기 위해 ActionCableLink
를 사용합니다. 이는:
- Action Cable에 대한 WebSocket 연결을 설정합니다.
- 서버 푸시를 클라이언트의
Observable
이벤트 스트림에 매핑하여 뷰가 자신을 업데이트하기 위해 이를 구독할 수 있도록 합니다.
더 자세한 정보는 Apollo 및 Vue.js에 대해 알아보려면 GitLab GraphQL 개발 가이드를 참조하세요.