This page contains information related to upcoming products, features, and functionality. It is important to note that the information presented is for informational purposes only. Please do not rely on this information for purchasing or planning purposes. As with all projects, the items mentioned on this page are subject to change or delay. The development, release, and timing of any products, features, or functionality remain at the sole discretion of GitLab Inc.
Status Authors Coach DRIs Owning Stage Created
proposed @thomasrandolph @patrickbajao @igor.drozdov @jerasmus @iamphill @slashmanov @psjakubowska @ntepluhina devops create 2023-10-10

재사용 가능한 빠른 차이 (RRD)

요약

GitLab의 Diff는 각 영역이 각각의 방법을 사용하여 여러 곳에 퍼져 있습니다. 우리의 목표는 응용 프로그램 전체에 걸쳐 차이가 렌더링되는 단일하고 성능이 우수한 방법을 개발하는 것입니다. 우리의 목표는 여기서 차이 렌더링의 모든 영역을 개선하는 것입니다. 이는 백엔드에서 차이를 생성하여 프론트엔드에서 차이를 렌더링하는 것까지 모든 영역을 개선하는 것입니다.

이 문서와 관련된 모든 차이 기능은 전용 페이지에 목록화되어 있습니다.

동기

목표

  • 지각 성능 향상
  • 향상된 유지 보수 용이성
  • 모든 시나리오의 일관된 커버리지

비목표

이 노력은 다음을 포함하지 않습니다:

  • 병합 요청이나 저장소 커밋의 현재 차이 구현을 개선하는 것

목표 우선 순위

모든 목표가 중요하다고 할지라도 일관된 선택을 지원하기 위해 어떤 목표가 다른 목표보다 중요한지에 대한 지침을 제공하기 위해 노력했습니다. 따라서 우리는 다음과 같은 순서를 정의했습니다.

지각 성능향상된 유지 가능성보다 우선이고 일관된 커버리지보다 우선입니다.

예시:

  • 제안이 지각 성능을 향상시키지만 향상된 유지 가능성에 대한 대가로 이루어진 경우: ❌ 대안을 고려해야 합니다.
  • 제안이 특정 맥락에서 기능을 제거하고 커버리지에 손상을 입히지만 지각 성능이나 유지 가능성에는 영향을 미치지 않는 경우: ❌ 다시 고려해야 합니다.
  • 제안이 지각 성능을 향상시키지만 사용 중인 특정 맥락에서 기능을 제거하는 경우: ✅ 이는 유효하며 제품/UX와 논의되어야 합니다.
  • 제안이 일관된 커버리지를 보장하고 지각 성능이나 유지 가능성에는 영향을 미치지 않는 경우: ✅ 이는 유효합니다.

본질적으로 각 결정에서 모든 목표를 달성하려 노력하지만 더 높은 목표를 우선시할 것입니다.

프로세스

작업 공간 및 결과물

  • 메트릭, 예산, 개발 및 아키텍처 패턴과 같은 구현 세부 정보를 문서에 저장할 것입니다
  • 연구 결과, 감사 결과 등 대규모 자료는 RRD 프로젝트위키에 저장합니다.
  • 오디오 및 비디오 녹화물은 Code Review / RRD 재생 목록의 공개 YouTube 채널에 저장할 것입니다.
  • 초안, 회의록 및 기타 임시 문서는 공개 Google 문서에 저장할 것입니다.

제안

여기서 제안된 새로운 접근 방식은 과거에 우리가 한 것을 변경하고 다음과 같은 작업을 합니다:

  1. 차이 렌더링을 위한 가상 스크롤링 사용 중단
  2. 대부분의 렌더링 작업을 서버로 이동
  3. 클라이언트에서 서버 렌더링된 HTML을 개선합니다.
  4. 모든 페이지에서의 차이 렌더링을 위해 차이 코드베이스를 통합합니다(병합 요청, 저장소 커밋, 리비전 비교 및 기타 모든 페이지).

정의

유지 가능성

유지 가능한 프로젝트는 간단한 프로젝트입니다.

간단함은 복잡함의 반대입니다. 이는 2011년 Strange Loop에서 Rich Hickey가 설명한 간단함과 복잡함의 정의를 사용합니다.

  • 유지 가능한 코드는 간단합니다(단일 작업, 단일 개념, 다른 것과 분리된 것).
  • 유지 가능한 프로젝트는 간단한 구조(폴더는 행동 클래스들을 정의하며, 예를 들어 컴포넌트 디렉터리는 네트워크 호출을 시작하지 않기 때문에 확신할 수 있습니다)를 갖습니다.
  • 유지 가능한 응용 프로그램은 간단한 조직과 간단한 코드에서 발전합니다. 북송성에 관한 옛말은 방해 받는 책상은 방해 받는 마음을 대변합니다. 간단하게 일하기에 대해서 엄격하다면, 우리의 결과물(제품)에는 당연히 사용자가 그들의 행동에 대해 더 쉽게 추론할 수 있는 응용 프로그램을 생산할 것입니다.

완료

GitLab은 주로 MR이 병합될 준비가 되었는지를 식별하기 위해 완료의 정의를 갖추고 있습니다.

GitLab의 완료의 정의에 더해, RRD 작업은 다음 요구 사항을 준수해야 합니다:

  • 모든 메트릭을 만족하거나 초과
    • 우리의 최소 접근성 메트릭을 만족하거나 초과(이는 명시적으로 우리가 정의한 우선 순위의 일부가 아니라서, 상의가 될 수 없습니다).
  • 모든 작업은 엔지니어들을 위해 완전히 문서화되어야 함(사용자 문서는 표준 완료 정의의 요구 사항입니다).

승인 기준

성공을 측정하기 위해서는 의미 있는 지표를 설정해야 합니다. 이러한 지표는 최종 사용자에게 의미 있고 긍정적인 영향을 미쳐야 합니다.

  1. WCAG 2.2 AA를 만족하거나 초과해야 합니다.
  2. ATAG 2.0 AA를 만족하거나 초과해야 합니다.
  3. RRD 앱은 압축되었을 때 300 KiB 이하의 JavaScript를로드해야 합니다 (“across-the-wire”).
  4. RRD 앱은 압축되었을 때 150 KiB 이하의 마크업, 이미지, 스타일, 폰트 등을로드해야 합니다 (“across-the-wire”).
  5. 첫 번째 차이(Time to First Diff)(mr-diffs-mark-first-diff-file-shown)가 3초 전에 발생해야 합니다.
  6. RRD 앱은 GitLab 제품의 나머지와 완전히 격리된 상태에서 실행할 수 있어야 합니다.:
    1. “실행”이란 앱을로드하고 데이터를 표시하며 사용자 상호 작용을 허용하는 것을 의미합니다 (“읽기 전용”).
    2. 응용 프로그램의 일부가 병합 요청 또는 차이에서만 사용되는 경우에는 차이 응용 프로그램의 일부로 간주됩니다.
    3. 응용 프로그램의 일부가 제품의 나머지에서 가져와야하는 경우에는 차이로드의 일부로 간주되지 않습니다(지표 3 및 4에서 정의됨).
    4. 응용 프로그램의 일부가 제품의 나머지에서 가져와야 하는 경우, 차이 응용 프로그램의 기능을 차단해서는 안 됩니다.
    5. 응용 프로그램의 일부가 제품의 나머지에서 가져와야 합니다. 비동기적으로 로드되어야 합니다.
    6. 응용 프로그램의 일부가 5.1-5.5를 충족하는 경우_(예: 사용자가 차이에 의견을 남기려고 할 때 Markdown 편집기가 비동기적으로 로드됨)_ 그것의 포함이 예산 초과를 초래하는 경우:
      • 예산을 초과하는 것을 인정하고 통제가 불가능하다고 인정하는 문서화된 예외 목록에 추가해야 합니다.
      • 예외 목록은 정기적으로 확인되어 우리의 예산을 초과하는 지속적인 가치를 결정해야 합니다.

1: The Performance Inequality Gap, 2023

프런트엔드

이상적으로는 우리는 최초 시도에서 우리의 완료 정의와 책임 지표를 충족할 것입니다. 또한 앞으로도 이러한 범위 내에서 계속 유지해야 합니다. 이를 보장하기 위해, 다음을 고려한 응용 프로그램 아키텍처를 설계해야 합니다:

  1. 다음과 같습니다.:
    1. 확장 가능합니다.
    2. 유연합니다.
    3. 변경이 가능합니다.
  2. 전체 GitLab 제품의 중요한 미션을 수행합니다.
  3. 자신을 복잡하고 독특한 응용 프로그램으로 간주하며 다른 제품의 부차적인 효과로는 해결할 수 없는 문제점을 가지고 있다고 여깁니다.
  4. UI 변경없이 데이터 액세스/포맷 변경을 처리할 수 있어야 합니다.
  5. 데이터 액세스/포맷 변경 없이 UI 변경을 처리할 수 있어야 합니다.
  6. 훅 가능하고 검사 가능한 API를 제공하고 코드 결합을 피합니다.
  7. 다음을 분리합니다:
    • 상태 및 응용 프로그램 데이터.
    • 응용 프로그램 동작 및 UI.
    • 데이터 액세스 및 네트워크 액세스.

디자인 및 구현 세부 정보

개요

재사용 가능한 Rapid Diffs는 프런트엔드와 백엔드 모두에게 책임을 변경시킵니다.

백엔드는 다음을 수행할 것입니다:

  1. 차이 데이터 준비.
  2. 차이 라인 강조.
  3. 차이를 HTML로 렌더링하고 브라우저로 스트리밍합니다.
  4. 최종 응답에 차이 메타데이터를 포함합니다.

프런트엔드는 다음을 수행할 것입니다:

  1. 기존 및 향후 차이 HTML을 향상시킵니다.
  2. 스트리밍된 차이 HTML을 처리합니다.
  3. 사용자 상호작용을 가능케하는 동적 컨트롤로 차이 HTML을 향상시킵니다.

정적 및 동적 분리

관심 분리를 달성하기 위해 페이지의 정적 및 동적 UI를 구분해야 합니다:

  • 정적인 모든 것은 항상 서버에서 렌더링되어야 합니다.
  • 동적인 모든 것은 클라이언트에서 향상되어야 합니다.

예를 들어: 강조 표시된 차이 라인은 사용자 입력과 함께 변경되지 않으므로 서버에서 렌더링해야 합니다.

성능 최적화

페이지의 지각된 성능을 향상시키기 위해 다음 기법을 구현해야 합니다:

  1. 처음에 페이지에 렌더링된 차이의 수를 제한합니다.
  2. 나머지 차이를 렌더링하기 위해 HTML 스트리밍을 사용합니다.
    1. 차이 파일에 후크할 수 있는 Web Components를 사용합니다.
  3. 필요한 경우 redraw 오버헤드를 줄이기 위해 content-visibility을 적용합니다.
  4. 차이 토론을 비동기적으로 렌더링합니다.

페이지 및 데이터 흐름

이 다이어그램은 차이를 표시하고 사용자 상호 작용 및 사용자 제출 데이터를 수집하고 저장할 수 있는 완전한 인터랙티브 응용 프로그램에 대한 양방향 데이터 흐름을 문서화합니다. 다시 말해, 이 페이지는 차이를 표시하고 사용자가 차이에 대해 협력할 수 있는 완전한 인터랙티브 응용 프로그램에 대한 양방향 데이터 흐름을 문서화합니다.

중요한 단계
  1. Gitaly
  2. 데이터베이스
  3. Diff 저장소
  4. 캐시
  5. 백엔드
  6. 웹 API
  7. 프론트엔드*
flowchart LR Gitaly DB[데이터베이스] Cache DS[Diff 저장소] FE[프론트엔드] Display Gitaly <--> BE DB <--> BE Cache <--> BE DS <--> BE BE <--> API API <--> FE FE --> Display subgraph Rails direction LR BE[백엔드] API[웹 API] end

*: 프론트엔드는 많은 미개척된 단계를 가리킵니다. 프론트엔드가 캐시, 데이터베이스, API 추상화(네트워크 연결 등의 하위 모듈에 대한) 등이 필요할 것으로 예상됩니다. 이러한 사항들이 확장되지는 않았지만, “프론트엔드”는 이 복잡성을 대변합니다.

접근성

재사용 가능한 빠른 차이점은 웹 기반 콘텐츠의 웹 콘텐츠 접근성 지침 2.1 수준 AA 및 사용자 인터페이스의 저자 도구 접근성 지침 2.0 수준 AA와 일치하는 방식으로 표시되어야 합니다.

GitLab의 맥락에서 차이를 사용하는 접근성 있는 경험을 제공하려면 차이를 표시하고 상호 작용하는 데 모두 준수해야 합니다. 따라서 접근성 감사 및 추가 권장 내용은 변경 사항 검토에 사용되는 콘텐츠 편집기 기능도 고려할 것입니다.

ATAG 2.0 AA

차이의 특성을 고려하여 다음 지침에 주력할 것입니다:

  1. 지침 A.2.1: (저자 도구 사용자 인터페이스를 위해) 대체 콘텐츠를 저자에게 제공
  2. 지침 A.3.1: (저자 도구 사용자 인터페이스를 위해) 저자 기능에 대한 키보드 액세스 제공
  3. 지침 A.3.4: (저자 도구 사용자 인터페이스를 위해) 콘텐츠 구조를 통한 탐색 및 편집 개선
  4. 지침 A.3.6: (저자 도구 사용자 인터페이스를 위해) 기본 설정 관리

HTML 구조

차이의 HTML 구조는 보조 기술을 지원해야 합니다. 이러한 이유로 표는 제시된 데이터 간의 논리적 관계를 나타내고, 키보드를 사용하여 스크린 리더 사용자가 쉽게 탐색할 수 있으므로 우선적인 솔루션일 수 있습니다. 레이블이 지정된 열은 줄 번호와 같은 정보가 편집된 코드와 연결될 수 있도록 합니다.

가능한 구조는 다음과 같을 수 있습니다:

<table>
  <caption class="gl-sr-only">파일 index.js에 대한 변경 사항. 10줄이 변경됨: 삭제된 줄 5개, 추가된 줄 5개.</caption>
  <tr hidden>
    <th>원본 줄 번호: </th>
    <th>차이 줄 번호: </th>
    <th>줄 변경:</th>
  </tr>
  <tr>
    <td>1234</td>
    <td></td>
    <td>.tree-time-ago ,</td>
  </tr>
  […]
</table>

더 많은 구현 가이드라인을 보려면 WAI 테이블 튜토리얼을 참조하세요.

각 파일 테이블에는 다음을 읽어야 하는 변경 사항의 짧은 요약이 포함될 수 있습니다:

  • 변경된 총 줄 수,
  • 추가된 줄 수,
  • 제거된 줄 수.

테이블 콘텐츠의 요약은 <caption> 요소 내에 배치하거나, 테이블 앞에 aria-describedby로 참조되는 요소 내에 배치할 수 있습니다. 모든 접근성 이니셔티브에 대한 자세한 내용은 WAI (웹 접근성 이니셔티브)를 참조하세요.

그러나 이러한 구조가 차이 표시의 기능적 측면을 해치는 경우, 더 일반적인 요소와 ARIA 지원을 함께 사용할 수 있습니다.

시각적 표시자

각 시각적 표시자는 해당 표시자의 의미를 나타내는 스크린 리더 텍스트를 가져야 합니다. 필요한 경우, 시갤 스새도로 읽히도록 gl-sr-only 또는 gl-sr-only-focusable 클래스를 사용하세요.

보조 기술을 위한 대체표시가 필요한 시각적 표시자 중 일부는 다음과 같습니다:

  • + 또는 빨간 하이라이팅은 “추가됨”으로 읽어야 함
  • - 또는 녹색 하이라이팅은 “제거됨”으로 읽어야 함

고수준 구현

대안 솔루션

역사적 배경

재사용 가능한 빠른 차이점은 차이점 렌더링에 대한 접근 방식에서 패러다임 전환을 가져왔습니다. 이 제안된 아키텍처 이전에 차이점을 렌더링하기 위한 두 가지 다른 접근 방식이 있었습니다.

  1. 병합 요청에서는 주로 클라이언트 측 렌더링을 사용했습니다.
  2. 나머지 모든 페이지는 주로 JavaScript에서 구현된 서버 측 렌더링을 사용했습니다.

병합 요청에서는 대부분의 렌더링 작업이 클라이언트에서 수행되었습니다.

  • 백엔드는 차이점 데이터를 포함한 JSON 응답을 생성했습니다.
  • 클라이언트는 차이점을 그리고 사용자 입력에 반응하는 것을 책임져야 했습니다.

이로 인해 클라이언트 측 렌더링에 대한 가상 스크롤 솔루션이 채택되었으며, 이는 대규모 차이점 파일 목록의 그리기 속도를 크게 높였습니다.

불행하게도, 이로 인해 매우 높은 유지 보수 비용과 지속적인 버그가 발생했습니다. 또한, 사용자 경험이 나빠졌으며 페이지를 방문할 때 차이점을 바로 표시할 수 없었으며 먼저 JSON 응답을 기다려야 했습니다. 마지막으로, 이 접근 방식은 다른 페이지에서 사용된 서버 렌더링 된 차이점과 완전히 병렬로 이루어졌으며, 이로 인해 차이점을 위한 두 가지 완전히 별개의 코드베이스가 생겼습니다.

시도된 대안 솔루션 요약

지난 시도해 본 전략의 목록은 다음과 같습니다:

  • 전체 서버 측 렌더링 (채택 및 Vue 앱으로 대체): 병합 요청 변경 탭의 Vue 리팩터링 이전에 차이점이 전적으로 서버에서 렌더링되었습니다. 이로 인해 페이지가 렌더링되기까지 걸리는 시간이 길어졌습니다.
  • 프런트엔드 템플릿 (Vue) 서버 측 렌더링 (테스트됨): 결과와 영향이 설득력이 없었으며 부분적인 SSR 방향으로 가리키고 있었습니다. (PoC MR)
  • 일괄 차이점 (채택): 차이점을 비동기 페이지화된 요청으로 분할하여 크기를 늘렸습니다 (느린 시작). 전체 차이점 로딩 시간이 만족스럽지 않으며 지각된 성능은 컨텐츠가 없는 페이지가 오랜 시간 동안 유지되는 것과 관련이 있었습니다.
  • 가상 스크롤링 (채택): 전체적으로 브라우저에 부담을 주며, 완전히 네이티브 검색 기능을 사용할 수 없는 상태 변화, 스크롤링 동안 간섭 및 이상한 동작과 같은 여러 알려진 부작용이 있었습니다. (이 블루프린트에서 제안된 접근 방식과의 비교: 제안된 접근 방식과의 비교)
  • 리포지토리 커밋 상세 정보가 너무 크면 페이지화 (채택됨): 현재 임시적인 해결책으로, 리포지토리의 매우 큰 커밋 차이점은 UX에 부정적인 영향을 미치며 여러 페이지에 걸쳐 파일과 변경 사항을 숨깁니다.
  • Micro Code Review 프런트엔드 PoC (테스트됨): 이 방식은 이전에 사용된 응용 프로그램 디자인과는 매우 다르기 때문에 심각하게 탐구되지 않았습니다. 사용된 디자인의 일부 - 사용자 정의 요소 및 이벤트 의존성 -가 대안적인 접근 방식에 통합되었습니다. (Micro Code Review 프런트엔드 PoC)
  • 노드 서버를 사용하여 스트리밍 차이점 (테스트됨): 전용 노드js 서버와 스트리밍을 결합합니다. 이 블루프린트에서 제안된 SSR 접근 방식의 전조입니다. (PoC: 스트리밍 차이점 앱)

변경 제안

이러한 변경 사항(임의의 “Design A”와 같은 이름으로 표시됨)은 이 설계도의 최종 제안된 경로를 제시하지만, 공식적인 콘텐츠로 승인되지는 않았습니다.

  • 가장 높은 계층의 제목에 디자인 이름을 표시합니다. 동일한 수준의 여러 제목을 변경하는 경우 모두 동일한 이름으로 표시해야 합니다. 이렇게 하면 논리적으로 이해하기 쉬운 고수준 목차가 생성됩니다.

프론트 엔드(Design A)

고수준 구현

note
이 초안 제안은 선택되지 않을 수 있는 하나의 잠재적인 프론트 엔드 아키텍처를 제안합니다. 이는 다른 제안된 디자인과 반드시 상호 배타적인 것은 아닙니다.

(이 차트의 더 나은 시각화를 보려면 새로운 Diffs: 기술 아키텍처 디자인 참조)

flowchart TB classDef sticky fill:#d0cabf, color:black stickyMetricsA>"Metrics 3, 4 및 5는<br>전체 프론트 엔드 애플리케이션에 적용됨"] stickyMetricsA -.- fe fe Socket((WebSocket)) be subgraph fe [프론트 엔드] stickyMetricsB>"메트릭 1 및 2는 모든 UI 요소에 적용됨"] stickyInbound>"모든 데이터는 UI가 상호 작용해야 하는 방식으로 정확히 서식이 지정됨"] stickyOutbound>"모든 데이터는 백엔드가 예상하는 형식으로 정확히 서식이 지정됨"] stickyIdb>"장기적으로. 예: diffs, MRs, 이모지, 노트, 드래프트, 파일 리뷰와 같은 사용자 전용 데이터, 축소 상태 등"] stickySession>"세션 단위. 예: 선택한 탭, 스크롤 위치, 사용자 설정의 임시 변경 등"] Events([이벤트 허브]) UI[UI] uiState((로컬 상태)) Logic[애플리케이션 로직] Normalizer[데이터 정규화자] Inbound{{인바운드 계약}} Outbound{{아웃바운드 계약}} Data[데이터 액세스] idb((indexedDB)) session((sessionStorage)) Network[네트워크 액세스] end subgraph be [백엔드] stickyApi>"Diffs/Merge Request UI가 수행할 수 있는<br>정의된 작업 목록. 예: <code>mergeRequest:notes:saveDraft</code> 또는<br><code>mergeRequest:changeStatus</code> (with <br><code>status: 'draft'</code> 또는 <code>status: 'ready'</code> 등). 모델, 저장 구조 등과 같은 실행 세부 사항을 노출해서는 안 됨."] API[활동 API] unk[\"?"/] API -.- stickyApi end %% 메모지 모양 같은 스티키로 만들기 class stickyMetricsA,stickyMetricsB,stickyInbound,stickyOutbound,stickyIdb,stickySession,stickyApi sticky UI <--> uiState stickyMetricsB -.- UI Network ~~~ stickyMetricsB Logic <--> Normalizer Normalizer --> Outbound Outbound --> Data Inbound --> Normalizer Data --> Inbound Inbound -.- stickyInbound Outbound -.- stickyOutbound Data <--> idb Data <--> session idb -.- stickyIdb session -.- stickySession Events <--> UI Events <--> Logic Events <--> Data Events <--> Network Network --> Socket --> API --> unk