InfoGrab Docs

GitLab Duo Agent Platform

요약

이 가이드는 GitLab Duo Agent Platform으로 작업하는 방법을 설명합니다. GitLab Duo Agent Platform은 AI 기반 자동화 기능을 위한 통합 인터페이스를 제공하는 Vue.js로 빌드된 단일 페이지 애플리케이션(SPA)입니다.

이 가이드는 GitLab Duo Agent Platform으로 작업하는 방법을 설명합니다.

개요#

GitLab Duo Agent Platform은 AI 기반 자동화 기능을 위한 통합 인터페이스를 제공하는 Vue.js로 빌드된 단일 페이지 애플리케이션(SPA)입니다. 이 플랫폼은 여러 내비게이션 항목이 /automate 경로 하위에 공존할 수 있도록 하는 범위가 지정된 라우팅 시스템을 사용합니다.

이 플랫폼은 컴포넌트 매핑 시스템을 통해 컨텍스트별 기능을 제공하면서 동일한 프런트엔드 인프라를 다양한 컨텍스트(프로젝트, 그룹 등)에서 재사용할 수 있도록 하는 유연한 네임스페이스 시스템으로 설계되었습니다.

네임스페이스 아키텍처#

네임스페이스 시스템은 다음을 수행하는 중앙 매핑 메커니즘을 중심으로 구축됩니다:

  • 네임스페이스 확인 - 플랫폼이 실행 중인 컨텍스트를 결정합니다

  • Vue 컴포넌트에 매핑 - 해당 네임스페이스에 적합한 Vue 컴포넌트로 라우팅합니다

  • GraphQL 쿼리를 props로 전달 - 의존성 주입을 통해 네임스페이스별 데이터를 제공합니다

진입점#

주요 진입점은 다음 위치에 있습니다:

ee/app/assets/javascripts/pages/projects/duo_agents_platform/index.js

이 파일은 플랫폼을 가져오고 초기화합니다:

import { initDuoAgentsPlatformProjectPage } from 'ee/ai/duo_agents_platform/namespace/project';

initDuoAgentsPlatformProjectPage();

앱 구조#

Mermaid 다이어그램 (12줄)
소스 코드 보기
graph TD
    A[Entry Point] --> B[initDuoAgentsPlatformPage]
    B --> C[Extract Namespace Data]
    C --> D[Create Router with Namespace]
    D --> E[Component Mapping]
    E --> F[Namespace-Specific Component]
    F --> G[GraphQL Query with Props]
    G --> H[Rendered UI]
I[Dataset Properties] --> C
J[Namespace Constant] --> E
K[Component Mappings] --&gt; E</code></pre></details></div>

새 내비게이션 항목 추가#

GitLab Duo Agent Platform은 Vue Router 구성이 브레드크럼 내비게이션을 직접 구동하는 라우터 기반 내비게이션 시스템을 사용합니다. 핵심은 ee/app/assets/javascripts/ai/duo_agents_platform/router/index.js의 라우터 구조가 URL 구조와 브레드크럼 계층 구조 모두를 결정한다는 것입니다.

라우터 기반 내비게이션 작동 방식#

시스템은 다음 상호 연결된 컴포넌트를 통해 작동합니다:

  1. 라우터 구조: meta.text 속성이 있는 중첩 라우트가 브레드크럼 레이블을 정의합니다
  2. 브레드크럼 컴포넌트: duo_agents_platform_breadcrumbs.vue가 일치된 라우트에서 브레드크럼을 자동으로 생성합니다
  3. 라우트 주입: index.jsinjectVueAppBreadcrumbs()가 라우터를 브레드크럼 시스템에 연결합니다

라우터 분석#

ee/app/assets/javascripts/ai/duo_agents_platform/router/index.js의 현재 라우터 구조를 보면:

routes: [
  {
    component: NestedRouteApp, // Simple <router-view /> wrapper
    path: '/agent-sessions',
    meta: {
      text: s__('DuoAgentsPlatform|Sessions'), // This becomes a breadcrumb
    },
    children: [
      {
        name: AGENTS_PLATFORM_INDEX_ROUTE,
        path: '', // Matches /agent-sessions exactly
        component: AgentsPlatformIndex,
      },
      {
        name: AGENTS_PLATFORM_NEW_ROUTE,
        path: 'new', // Matches /agent-sessions/new
        component: AgentsPlatformNew,
        meta: {
          text: s__('DuoAgentsPlatform|New'), // This becomes a breadcrumb
        },
      },
      // ...
    ],
  },
];

브레드크럼 생성#

브레드크럼 컴포넌트(duo_agents_platform_breadcrumbs.vue)는 다음과 같이 작동합니다:

  1. this.$route.matched에서 일치된 모든 라우트를 가져옵니다
  2. 일치된 각 라우트에서 meta.text를 추출합니다
  3. 텍스트와 라우트 경로로 브레드크럼 항목을 생성합니다
  4. 정적 브레드크럼("Automate" 등)과 결합합니다
// duo_agents_platform_breadcrumbs.vue에서
const matchedRoutes = (this.$route?.matched || [])
  .map((route) => {
    return {
      text: route.meta?.text, // Uses meta.text for breadcrumb label
      to: { path: route.path },
    };
  })
  .filter((r) => r.text); // Only routes with meta.text become breadcrumbs

새 내비게이션 항목 추가#

새 최상위 내비게이션 항목("Your Feature" 등)을 추가하려면 라우터에 새 라우트 트리를 추가해야 합니다:

1단계: 라우트 상수 추가#

파일: ee/app/assets/javascripts/ai/duo_agents_platform/router/constants.js

// 기능에 대한 라우트 이름 상수 추가
export const AGENTS_PLATFORM_YOUR_FEATURE_INDEX = 'your_feature_index';
export const AGENTS_PLATFORM_YOUR_FEATURE_NEW = 'your_feature_new';
export const AGENTS_PLATFORM_YOUR_FEATURE_SHOW = 'your_feature_show';

2단계: 라우터에 라우트 추가#

파일: ee/app/assets/javascripts/ai/duo_agents_platform/router/index.js

import YourFeatureIndex from '../pages/your_feature/your_feature_index.vue';
import YourFeatureNew from '../pages/your_feature/your_feature_new.vue';
import YourFeatureShow from '../pages/your_feature/your_feature_show.vue';

export const createRouter = (base, namespace) => {
  return new VueRouter({
    base,
    mode: 'history',
    routes: [
      // 기존 agent-sessions 라우트
      {
        component: NestedRouteApp,
        path: '/agent-sessions',
        meta: {
          text: s__('DuoAgentsPlatform|Sessions'),
        },
        children: [
          {
            name: AGENTS_PLATFORM_INDEX_ROUTE,
            path: '',
            component: getNamespaceIndexComponent(namespace),
          },
          // ... 기존 자식들
        ],
      },

      // 새로운: 기능 라우트
      {
        component: NestedRouteApp,
        path: '/your-feature', // URL 경로가 됨
        meta: {
          text: s__('DuoAgentsPlatform|Your Feature'), // 브레드크럼이 됨
        },
        children: [
          {
            name: AGENTS_PLATFORM_YOUR_FEATURE_INDEX,
            path: '', // /your-feature와 정확히 일치
            component: YourFeatureIndex,
          },
          {
            name: AGENTS_PLATFORM_YOUR_FEATURE_NEW,
            path: 'new', // /your-feature/new와 일치
            component: YourFeatureNew,
            meta: {
              text: s__('DuoAgentsPlatform|New'), // 브레드크럼: "Your Feature > New"
            },
          },
          {
            name: AGENTS_PLATFORM_YOUR_FEATURE_SHOW,
            path: ':id(\\d+)', // /your-feature/123과 일치
            component: YourFeatureShow,
            // meta.text 없음 - 라우트 파라미터를 브레드크럼 텍스트로 사용
          },
        ],
      },

      { path: '*', redirect: '/agent-sessions' },
    ],
  });
};

3단계: 백엔드 라우트 추가(필요한 경우)#

ee/config/routes/project.rb의 기존 와일드카드 라우트가 새 경로를 처리해야 합니다:

scope :automate do
  get '/(*vueroute)' => 'duo_agents_platform#show', as: :automate, format: false
end

중요: 사이드바 메뉴 항목(4단계)을 추가하는 경우 명명된 라우트 헬퍼를 추가해야 합니다:

scope :automate do
  get '/(*vueroute)' => 'duo_agents_platform#show', as: :automate, format: false
  # 사이드바 메뉴 헬퍼를 위한 명명된 라우트
  get 'agent-sessions', to: 'duo_agents_platform#show', as: :automate_agent_sessions, format: false
  get 'your-feature', to: 'duo_agents_platform#show', as: :automate_your_features, format: false
end

참고: 라우트 이름에는 복수형을 사용합니다(예: automate_your_features). 기존 패턴과 일치시키고 Rails 경로 헬퍼가 올바르게 생성되도록 합니다.

4단계: 사이드바 메뉴 항목 추가#

파일: ee/lib/sidebars/projects/super_sidebar_menus/duo_agents_menu.rb

configure_menu_items 메서드에 메뉴 항목을 추가하고 해당 메뉴 항목 메서드를 생성합니다:

override :configure_menu_items
def configure_menu_items
  add_item(duo_agents_runs_menu_item)
  add_item(duo_agents_your_feature_menu_item)  # 새 메뉴 항목 추가
  true
end

private

def duo_agents_your_feature_menu_item
  ::Sidebars::MenuItem.new(
    title: s_('Your Feature'),
    link: project_automate_your_features_path(context.project),  # 참고: 복수형 'features'
    active_routes: { controller: :duo_agents_platform },
    item_id: :agents_your_feature
  )
end

5단계: Vue 컴포넌트 생성#

파일: ee/app/assets/javascripts/ai/duo_agents_platform/pages/your_feature/your_feature_index.vue

<script>
export default {
  name: 'YourFeatureIndex',
};
</script>

<template>
  <div>
    <h1>Your Feature</h1>
  </div>

</template>

새 네임스페이스 추가#

새 네임스페이스를 추가하려면(예: 그룹용):

1. 네임스페이스 상수 정의#

// ee/app/assets/javascripts/ai/duo_agents_platform/constants.js
export const AGENT_PLATFORM_GROUP_PAGE = 'group';

2. 네임스페이스 디렉토리 구조 생성#

ee/app/assets/javascripts/ai/duo_agents_platform/namespace/group/
├── index.js
├── group_agents_platform_index.vue
└── graphql/
    └── queries/
        └── get_group_agent_flows.query.graphql

3. 네임스페이스 초기화 구현#

// ee/app/assets/javascripts/ai/duo_agents_platform/namespace/group/index.js
import { initDuoAgentsPlatformPage } from '../../index';
import { AGENT_PLATFORM_GROUP_PAGE } from '../../constants';

export const initDuoAgentsPlatformGroupPage = () => {
  initDuoAgentsPlatformPage({
    namespace: AGENT_PLATFORM_GROUP_PAGE,
    namespaceDatasetProperties: ['groupPath', 'groupId'],
  });
};

4. 네임스페이스별 컴포넌트 생성#

<!-- ee/app/assets/javascripts/ai/duo_agents_platform/namespace/group/group_agents_platform_index.vue -->
<script>
import getGroupAgentFlows from './graphql/queries/get_group_agent_flows.query.graphql';
import DuoAgentsPlatformIndex from '../../pages/index/duo_agents_platform_index.vue';

export default {
  components: { DuoAgentsPlatformIndex },
  inject: ['groupPath'], // 그룹별 주입
  apollo: {
    workflows: {
      query: getGroupAgentFlows, // 그룹별 쿼리
      variables() {
        return {
          groupPath: this.groupPath,
          // ...
        };
      },
      // ...
    },
  },
};
</script>

<template>
  <duo-agents-platform-index
    :is-loading-workflows="isLoadingWorkflows"
    :workflows="workflows"
    :workflows-page-info="workflowsPageInfo"
    :workflow-query="$apollo.queries.workflows"
  />
</template>

5. 컴포넌트 매핑 업데이트#

// ee/app/assets/javascripts/ai/duo_agents_platform/router/utils.js
import { AGENT_PLATFORM_PROJECT_PAGE, AGENT_PLATFORM_GROUP_PAGE } from '../constants';
import ProjectAgentsPlatformIndex from '../namespace/project/project_agents_platform_index.vue';
import GroupAgentsPlatformIndex from '../namespace/group/group_agents_platform_index.vue';

export const getNamespaceIndexComponent = (namespace) => {
  const componentMappings = {
    [AGENT_PLATFORM_PROJECT_PAGE]: ProjectAgentsPlatformIndex,
    [AGENT_PLATFORM_GROUP_PAGE]: GroupAgentsPlatformIndex, // 새 매핑
  };

  return componentMappings[namespace];
};

6. 진입점 생성#

// ee/app/assets/javascripts/pages/groups/duo_agents_platform/index.js
import { initDuoAgentsPlatformGroupPage } from 'ee/ai/duo_agents_platform/namespace/group';

initDuoAgentsPlatformGroupPage();

이 아키텍처의 장점#

  • 코드 재사용: 동일한 프런트엔드 인프라가 다양한 컨텍스트에서 작동합니다

  • 관심사 분리: 각 네임스페이스가 자체 데이터 가져오기 및 비즈니스 로직을 처리합니다

  • 확장성: 기존 코드를 수정하지 않고 새 네임스페이스를 쉽게 추가할 수 있습니다

  • 타입 안전성: 네임스페이스 상수 및 필수 속성을 통한 명확한 계약

  • 유지 관리성: 격리된 네임스페이스 구현이 결합도를 줄입니다

주요 구현 세부 사항#

라우터 기반 브레드크럼#

브레드크럼 시스템은 라우터 구조에서 내비게이션을 자동으로 생성합니다:

  • meta.text가 있는 부모 라우트는 브레드크럼 세그먼트가 됩니다
  • meta.text가 있는 자식 라우트는 브레드크럼 체인을 확장합니다
  • meta.text가 없는 라우트는 라우트 파라미터(:id 등)를 브레드크럼 텍스트로 사용합니다
  • 정적 브레드크럼("Automate" 등)은 모든 라우트 앞에 추가됩니다

URL 구조#

각 최상위 내비게이션 항목에는 고유한 URL 네임스페이스가 있습니다:

  • Sessions: /automate/agent-sessions, /automate/agent-sessions/new, /automate/agent-sessions/123
  • Your Feature: /automate/your-feature, /automate/your-feature/new, /automate/your-feature/456

중첩 라우트 패턴#

플랫폼은 일관된 중첩 라우트 패턴을 사용합니다:

  1. 부모 라우트: URL 경로와 최상위 브레드크럼을 정의합니다
  2. NestedRouteApp 컴포넌트: 자식 라우트를 위한 단순한 <router-view /> 래퍼
  3. 자식 라우트: 특정 페이지와 추가 브레드크럼 세그먼트를 정의합니다

예시: 현재 에이전트 세션 구현#

기존 에이전트 세션 기능이 이 패턴을 보여줍니다:

{
  component: NestedRouteApp,           // 자식 라우트를 렌더링
  path: '/agent-sessions',             // URL: /automate/agent-sessions
  meta: {
    text: s__('DuoAgentsPlatform|Sessions'),  // 브레드크럼: "Sessions"
  },
  children: [
    {
      name: AGENTS_PLATFORM_INDEX_ROUTE,
      path: '',                        // URL: /automate/agent-sessions
      component: AgentsPlatformIndex,  // 추가 브레드크럼 없음
    },
    {
      name: AGENTS_PLATFORM_NEW_ROUTE,
      path: 'new',                     // URL: /automate/agent-sessions/new
      component: AgentsPlatformNew,
      meta: {
        text: s__('DuoAgentsPlatform|New'),  // 브레드크럼: "Sessions > New"
      },
    },
    {
      name: AGENTS_PLATFORM_SHOW_ROUTE,
      path: ':id(\\d+)',               // URL: /automate/agent-sessions/123
      component: AgentsPlatformShow,   // 브레드크럼: "Sessions > 123"
      // meta.text 없음 - :id 파라미터를 브레드크럼으로 사용
    },
  ],
}

이는 다음 브레드크럼 계층 구조를 생성합니다:

  • /automate/agent-sessions → "Automate > Sessions"
  • /automate/agent-sessions/new → "Automate > Sessions > New"
  • /automate/agent-sessions/123 → "Automate > Sessions > 123"

주요 파일 참조#

  • 진입점: ee/app/assets/javascripts/pages/projects/duo_agents_platform/index.js
  • 주요 초기화: ee/app/assets/javascripts/ai/duo_agents_platform/index.js
  • 라우터: ee/app/assets/javascripts/ai/duo_agents_platform/router/index.js
  • 컴포넌트 매핑: ee/app/assets/javascripts/ai/duo_agents_platform/router/utils.js
  • 상수: ee/app/assets/javascripts/ai/duo_agents_platform/constants.js
  • 유틸리티: ee/app/assets/javascripts/ai/duo_agents_platform/utils.js
  • 프로젝트 네임스페이스: ee/app/assets/javascripts/ai/duo_agents_platform/namespace/project/

모범 사례#

  1. 라우터 구조: 브레드크럼 계층 구조를 정의하기 위해 meta.text 속성이 있는 중첩 라우트 사용
  2. URL 네이밍: URL 경로에 kebab-case 사용(/your-feature, /yourFeature 아님)
  3. 라우트 이름: 라우트 이름에 설명적인 상수 사용(AGENTS_PLATFORM_YOUR_FEATURE_INDEX)
  4. 컴포넌트 구성: pages/[feature]/ 디렉토리에 기능 컴포넌트 배치
  5. 브레드크럼 텍스트: 모든 meta.text 값에 국제화 문자열(s__()) 사용
  6. 일관된 패턴: NestedRouteApp으로 기존 중첩 라우트 패턴 따르기
  7. 네임스페이스 분리: namespace/ 하위 전용 디렉토리에 네임스페이스별 로직 유지
  8. 컴포넌트 매핑: 컴포넌트 매핑 시스템을 사용하여 네임스페이스별 컴포넌트로 라우팅
  9. 데이터 주입: 네임스페이스별 데이터에 Vue의 provide/inject 패턴 사용

트러블슈팅#

일반적인 문제#

  1. 브레드크럼이 표시되지 않음: 부모 라우트에 meta.text 속성이 있는지 확인
  2. 라우트가 작동하지 않음: ee/config/routes/project.rb에 와일드카드 라우트가 있는지 확인
  3. 사이드바가 강조 표시되지 않음: 사이드바 메뉴 항목에 올바른 active_routes가 있는지 확인
  4. 404 오류: 라우트 경로가 기존 라우트와 충돌하지 않는지 확인

디버깅 팁#

  1. Vue DevTools: $route.matched를 검사하여 어떤 라우트가 일치하는지 확인
  2. 라우터 상태: Vue DevTools에서 라우터 구성 확인
  3. 브레드크럼 디버그: 브레드크럼 컴포넌트에 console.log(this.$route.matched) 추가
  4. URL 테스트: 브라우저에서 URL로 직접 이동하여 라우트 테스트

가짜 플로우 생성#

플랫폼을 테스트하기 위한 가짜 플로우를 생성하려면 ee/lib/tasks/gitlab/duo_workflow/duo_workflow.rake Rake 작업을 실행할 수 있습니다.

특정 사용자가 특정 프로젝트에서 만든 50개의 플로우 중 20개를 만드는 예시:

bundle exec rake "gitlab:duo_workflow:populate[50,20,user@example.com,gitlab-org/gitlab-test]

GitLab Duo Agent Platform

원문 보기
요약

이 가이드는 GitLab Duo Agent Platform으로 작업하는 방법을 설명합니다. GitLab Duo Agent Platform은 AI 기반 자동화 기능을 위한 통합 인터페이스를 제공하는 Vue.js로 빌드된 단일 페이지 애플리케이션(SPA)입니다.

이 가이드는 GitLab Duo Agent Platform으로 작업하는 방법을 설명합니다.

개요#

GitLab Duo Agent Platform은 AI 기반 자동화 기능을 위한 통합 인터페이스를 제공하는 Vue.js로 빌드된 단일 페이지 애플리케이션(SPA)입니다. 이 플랫폼은 여러 내비게이션 항목이 /automate 경로 하위에 공존할 수 있도록 하는 범위가 지정된 라우팅 시스템을 사용합니다.

이 플랫폼은 컴포넌트 매핑 시스템을 통해 컨텍스트별 기능을 제공하면서 동일한 프런트엔드 인프라를 다양한 컨텍스트(프로젝트, 그룹 등)에서 재사용할 수 있도록 하는 유연한 네임스페이스 시스템으로 설계되었습니다.

네임스페이스 아키텍처#

네임스페이스 시스템은 다음을 수행하는 중앙 매핑 메커니즘을 중심으로 구축됩니다:

  • 네임스페이스 확인 - 플랫폼이 실행 중인 컨텍스트를 결정합니다

  • Vue 컴포넌트에 매핑 - 해당 네임스페이스에 적합한 Vue 컴포넌트로 라우팅합니다

  • GraphQL 쿼리를 props로 전달 - 의존성 주입을 통해 네임스페이스별 데이터를 제공합니다

진입점#

주요 진입점은 다음 위치에 있습니다:

ee/app/assets/javascripts/pages/projects/duo_agents_platform/index.js

이 파일은 플랫폼을 가져오고 초기화합니다:

import { initDuoAgentsPlatformProjectPage } from 'ee/ai/duo_agents_platform/namespace/project';

initDuoAgentsPlatformProjectPage();

앱 구조#

Mermaid 다이어그램 (12줄)
소스 코드 보기
graph TD
    A[Entry Point] --> B[initDuoAgentsPlatformPage]
    B --> C[Extract Namespace Data]
    C --> D[Create Router with Namespace]
    D --> E[Component Mapping]
    E --> F[Namespace-Specific Component]
    F --> G[GraphQL Query with Props]
    G --> H[Rendered UI]
I[Dataset Properties] --&gt; C
J[Namespace Constant] --&gt; E
K[Component Mappings] --&gt; E</code></pre></details></div>

새 내비게이션 항목 추가#

GitLab Duo Agent Platform은 Vue Router 구성이 브레드크럼 내비게이션을 직접 구동하는 라우터 기반 내비게이션 시스템을 사용합니다. 핵심은 ee/app/assets/javascripts/ai/duo_agents_platform/router/index.js의 라우터 구조가 URL 구조와 브레드크럼 계층 구조 모두를 결정한다는 것입니다.

라우터 기반 내비게이션 작동 방식#

시스템은 다음 상호 연결된 컴포넌트를 통해 작동합니다:

  1. 라우터 구조: meta.text 속성이 있는 중첩 라우트가 브레드크럼 레이블을 정의합니다
  2. 브레드크럼 컴포넌트: duo_agents_platform_breadcrumbs.vue가 일치된 라우트에서 브레드크럼을 자동으로 생성합니다
  3. 라우트 주입: index.jsinjectVueAppBreadcrumbs()가 라우터를 브레드크럼 시스템에 연결합니다

라우터 분석#

ee/app/assets/javascripts/ai/duo_agents_platform/router/index.js의 현재 라우터 구조를 보면:

routes: [
  {
    component: NestedRouteApp, // Simple <router-view /> wrapper
    path: '/agent-sessions',
    meta: {
      text: s__('DuoAgentsPlatform|Sessions'), // This becomes a breadcrumb
    },
    children: [
      {
        name: AGENTS_PLATFORM_INDEX_ROUTE,
        path: '', // Matches /agent-sessions exactly
        component: AgentsPlatformIndex,
      },
      {
        name: AGENTS_PLATFORM_NEW_ROUTE,
        path: 'new', // Matches /agent-sessions/new
        component: AgentsPlatformNew,
        meta: {
          text: s__('DuoAgentsPlatform|New'), // This becomes a breadcrumb
        },
      },
      // ...
    ],
  },
];

브레드크럼 생성#

브레드크럼 컴포넌트(duo_agents_platform_breadcrumbs.vue)는 다음과 같이 작동합니다:

  1. this.$route.matched에서 일치된 모든 라우트를 가져옵니다
  2. 일치된 각 라우트에서 meta.text를 추출합니다
  3. 텍스트와 라우트 경로로 브레드크럼 항목을 생성합니다
  4. 정적 브레드크럼("Automate" 등)과 결합합니다
// duo_agents_platform_breadcrumbs.vue에서
const matchedRoutes = (this.$route?.matched || [])
  .map((route) => {
    return {
      text: route.meta?.text, // Uses meta.text for breadcrumb label
      to: { path: route.path },
    };
  })
  .filter((r) => r.text); // Only routes with meta.text become breadcrumbs

새 내비게이션 항목 추가#

새 최상위 내비게이션 항목("Your Feature" 등)을 추가하려면 라우터에 새 라우트 트리를 추가해야 합니다:

1단계: 라우트 상수 추가#

파일: ee/app/assets/javascripts/ai/duo_agents_platform/router/constants.js

// 기능에 대한 라우트 이름 상수 추가
export const AGENTS_PLATFORM_YOUR_FEATURE_INDEX = 'your_feature_index';
export const AGENTS_PLATFORM_YOUR_FEATURE_NEW = 'your_feature_new';
export const AGENTS_PLATFORM_YOUR_FEATURE_SHOW = 'your_feature_show';

2단계: 라우터에 라우트 추가#

파일: ee/app/assets/javascripts/ai/duo_agents_platform/router/index.js

import YourFeatureIndex from '../pages/your_feature/your_feature_index.vue';
import YourFeatureNew from '../pages/your_feature/your_feature_new.vue';
import YourFeatureShow from '../pages/your_feature/your_feature_show.vue';

export const createRouter = (base, namespace) => {
  return new VueRouter({
    base,
    mode: 'history',
    routes: [
      // 기존 agent-sessions 라우트
      {
        component: NestedRouteApp,
        path: '/agent-sessions',
        meta: {
          text: s__('DuoAgentsPlatform|Sessions'),
        },
        children: [
          {
            name: AGENTS_PLATFORM_INDEX_ROUTE,
            path: '',
            component: getNamespaceIndexComponent(namespace),
          },
          // ... 기존 자식들
        ],
      },

      // 새로운: 기능 라우트
      {
        component: NestedRouteApp,
        path: '/your-feature', // URL 경로가 됨
        meta: {
          text: s__('DuoAgentsPlatform|Your Feature'), // 브레드크럼이 됨
        },
        children: [
          {
            name: AGENTS_PLATFORM_YOUR_FEATURE_INDEX,
            path: '', // /your-feature와 정확히 일치
            component: YourFeatureIndex,
          },
          {
            name: AGENTS_PLATFORM_YOUR_FEATURE_NEW,
            path: 'new', // /your-feature/new와 일치
            component: YourFeatureNew,
            meta: {
              text: s__('DuoAgentsPlatform|New'), // 브레드크럼: "Your Feature > New"
            },
          },
          {
            name: AGENTS_PLATFORM_YOUR_FEATURE_SHOW,
            path: ':id(\\d+)', // /your-feature/123과 일치
            component: YourFeatureShow,
            // meta.text 없음 - 라우트 파라미터를 브레드크럼 텍스트로 사용
          },
        ],
      },

      { path: '*', redirect: '/agent-sessions' },
    ],
  });
};

3단계: 백엔드 라우트 추가(필요한 경우)#

ee/config/routes/project.rb의 기존 와일드카드 라우트가 새 경로를 처리해야 합니다:

scope :automate do
  get '/(*vueroute)' => 'duo_agents_platform#show', as: :automate, format: false
end

중요: 사이드바 메뉴 항목(4단계)을 추가하는 경우 명명된 라우트 헬퍼를 추가해야 합니다:

scope :automate do
  get '/(*vueroute)' => 'duo_agents_platform#show', as: :automate, format: false
  # 사이드바 메뉴 헬퍼를 위한 명명된 라우트
  get 'agent-sessions', to: 'duo_agents_platform#show', as: :automate_agent_sessions, format: false
  get 'your-feature', to: 'duo_agents_platform#show', as: :automate_your_features, format: false
end

참고: 라우트 이름에는 복수형을 사용합니다(예: automate_your_features). 기존 패턴과 일치시키고 Rails 경로 헬퍼가 올바르게 생성되도록 합니다.

4단계: 사이드바 메뉴 항목 추가#

파일: ee/lib/sidebars/projects/super_sidebar_menus/duo_agents_menu.rb

configure_menu_items 메서드에 메뉴 항목을 추가하고 해당 메뉴 항목 메서드를 생성합니다:

override :configure_menu_items
def configure_menu_items
  add_item(duo_agents_runs_menu_item)
  add_item(duo_agents_your_feature_menu_item)  # 새 메뉴 항목 추가
  true
end

private

def duo_agents_your_feature_menu_item
  ::Sidebars::MenuItem.new(
    title: s_('Your Feature'),
    link: project_automate_your_features_path(context.project),  # 참고: 복수형 'features'
    active_routes: { controller: :duo_agents_platform },
    item_id: :agents_your_feature
  )
end

5단계: Vue 컴포넌트 생성#

파일: ee/app/assets/javascripts/ai/duo_agents_platform/pages/your_feature/your_feature_index.vue

<script>
export default {
  name: 'YourFeatureIndex',
};
</script>

<template>
  <div>
    <h1>Your Feature</h1>
  </div>

</template>

새 네임스페이스 추가#

새 네임스페이스를 추가하려면(예: 그룹용):

1. 네임스페이스 상수 정의#

// ee/app/assets/javascripts/ai/duo_agents_platform/constants.js
export const AGENT_PLATFORM_GROUP_PAGE = 'group';

2. 네임스페이스 디렉토리 구조 생성#

ee/app/assets/javascripts/ai/duo_agents_platform/namespace/group/
├── index.js
├── group_agents_platform_index.vue
└── graphql/
    └── queries/
        └── get_group_agent_flows.query.graphql

3. 네임스페이스 초기화 구현#

// ee/app/assets/javascripts/ai/duo_agents_platform/namespace/group/index.js
import { initDuoAgentsPlatformPage } from '../../index';
import { AGENT_PLATFORM_GROUP_PAGE } from '../../constants';

export const initDuoAgentsPlatformGroupPage = () => {
  initDuoAgentsPlatformPage({
    namespace: AGENT_PLATFORM_GROUP_PAGE,
    namespaceDatasetProperties: ['groupPath', 'groupId'],
  });
};

4. 네임스페이스별 컴포넌트 생성#

<!-- ee/app/assets/javascripts/ai/duo_agents_platform/namespace/group/group_agents_platform_index.vue -->
<script>
import getGroupAgentFlows from './graphql/queries/get_group_agent_flows.query.graphql';
import DuoAgentsPlatformIndex from '../../pages/index/duo_agents_platform_index.vue';

export default {
  components: { DuoAgentsPlatformIndex },
  inject: ['groupPath'], // 그룹별 주입
  apollo: {
    workflows: {
      query: getGroupAgentFlows, // 그룹별 쿼리
      variables() {
        return {
          groupPath: this.groupPath,
          // ...
        };
      },
      // ...
    },
  },
};
</script>

<template>
  <duo-agents-platform-index
    :is-loading-workflows="isLoadingWorkflows"
    :workflows="workflows"
    :workflows-page-info="workflowsPageInfo"
    :workflow-query="$apollo.queries.workflows"
  />
</template>

5. 컴포넌트 매핑 업데이트#

// ee/app/assets/javascripts/ai/duo_agents_platform/router/utils.js
import { AGENT_PLATFORM_PROJECT_PAGE, AGENT_PLATFORM_GROUP_PAGE } from '../constants';
import ProjectAgentsPlatformIndex from '../namespace/project/project_agents_platform_index.vue';
import GroupAgentsPlatformIndex from '../namespace/group/group_agents_platform_index.vue';

export const getNamespaceIndexComponent = (namespace) => {
  const componentMappings = {
    [AGENT_PLATFORM_PROJECT_PAGE]: ProjectAgentsPlatformIndex,
    [AGENT_PLATFORM_GROUP_PAGE]: GroupAgentsPlatformIndex, // 새 매핑
  };

  return componentMappings[namespace];
};

6. 진입점 생성#

// ee/app/assets/javascripts/pages/groups/duo_agents_platform/index.js
import { initDuoAgentsPlatformGroupPage } from 'ee/ai/duo_agents_platform/namespace/group';

initDuoAgentsPlatformGroupPage();

이 아키텍처의 장점#

  • 코드 재사용: 동일한 프런트엔드 인프라가 다양한 컨텍스트에서 작동합니다

  • 관심사 분리: 각 네임스페이스가 자체 데이터 가져오기 및 비즈니스 로직을 처리합니다

  • 확장성: 기존 코드를 수정하지 않고 새 네임스페이스를 쉽게 추가할 수 있습니다

  • 타입 안전성: 네임스페이스 상수 및 필수 속성을 통한 명확한 계약

  • 유지 관리성: 격리된 네임스페이스 구현이 결합도를 줄입니다

주요 구현 세부 사항#

라우터 기반 브레드크럼#

브레드크럼 시스템은 라우터 구조에서 내비게이션을 자동으로 생성합니다:

  • meta.text가 있는 부모 라우트는 브레드크럼 세그먼트가 됩니다
  • meta.text가 있는 자식 라우트는 브레드크럼 체인을 확장합니다
  • meta.text가 없는 라우트는 라우트 파라미터(:id 등)를 브레드크럼 텍스트로 사용합니다
  • 정적 브레드크럼("Automate" 등)은 모든 라우트 앞에 추가됩니다

URL 구조#

각 최상위 내비게이션 항목에는 고유한 URL 네임스페이스가 있습니다:

  • Sessions: /automate/agent-sessions, /automate/agent-sessions/new, /automate/agent-sessions/123
  • Your Feature: /automate/your-feature, /automate/your-feature/new, /automate/your-feature/456

중첩 라우트 패턴#

플랫폼은 일관된 중첩 라우트 패턴을 사용합니다:

  1. 부모 라우트: URL 경로와 최상위 브레드크럼을 정의합니다
  2. NestedRouteApp 컴포넌트: 자식 라우트를 위한 단순한 <router-view /> 래퍼
  3. 자식 라우트: 특정 페이지와 추가 브레드크럼 세그먼트를 정의합니다

예시: 현재 에이전트 세션 구현#

기존 에이전트 세션 기능이 이 패턴을 보여줍니다:

{
  component: NestedRouteApp,           // 자식 라우트를 렌더링
  path: '/agent-sessions',             // URL: /automate/agent-sessions
  meta: {
    text: s__('DuoAgentsPlatform|Sessions'),  // 브레드크럼: "Sessions"
  },
  children: [
    {
      name: AGENTS_PLATFORM_INDEX_ROUTE,
      path: '',                        // URL: /automate/agent-sessions
      component: AgentsPlatformIndex,  // 추가 브레드크럼 없음
    },
    {
      name: AGENTS_PLATFORM_NEW_ROUTE,
      path: 'new',                     // URL: /automate/agent-sessions/new
      component: AgentsPlatformNew,
      meta: {
        text: s__('DuoAgentsPlatform|New'),  // 브레드크럼: "Sessions > New"
      },
    },
    {
      name: AGENTS_PLATFORM_SHOW_ROUTE,
      path: ':id(\\d+)',               // URL: /automate/agent-sessions/123
      component: AgentsPlatformShow,   // 브레드크럼: "Sessions > 123"
      // meta.text 없음 - :id 파라미터를 브레드크럼으로 사용
    },
  ],
}

이는 다음 브레드크럼 계층 구조를 생성합니다:

  • /automate/agent-sessions → "Automate > Sessions"
  • /automate/agent-sessions/new → "Automate > Sessions > New"
  • /automate/agent-sessions/123 → "Automate > Sessions > 123"

주요 파일 참조#

  • 진입점: ee/app/assets/javascripts/pages/projects/duo_agents_platform/index.js
  • 주요 초기화: ee/app/assets/javascripts/ai/duo_agents_platform/index.js
  • 라우터: ee/app/assets/javascripts/ai/duo_agents_platform/router/index.js
  • 컴포넌트 매핑: ee/app/assets/javascripts/ai/duo_agents_platform/router/utils.js
  • 상수: ee/app/assets/javascripts/ai/duo_agents_platform/constants.js
  • 유틸리티: ee/app/assets/javascripts/ai/duo_agents_platform/utils.js
  • 프로젝트 네임스페이스: ee/app/assets/javascripts/ai/duo_agents_platform/namespace/project/

모범 사례#

  1. 라우터 구조: 브레드크럼 계층 구조를 정의하기 위해 meta.text 속성이 있는 중첩 라우트 사용
  2. URL 네이밍: URL 경로에 kebab-case 사용(/your-feature, /yourFeature 아님)
  3. 라우트 이름: 라우트 이름에 설명적인 상수 사용(AGENTS_PLATFORM_YOUR_FEATURE_INDEX)
  4. 컴포넌트 구성: pages/[feature]/ 디렉토리에 기능 컴포넌트 배치
  5. 브레드크럼 텍스트: 모든 meta.text 값에 국제화 문자열(s__()) 사용
  6. 일관된 패턴: NestedRouteApp으로 기존 중첩 라우트 패턴 따르기
  7. 네임스페이스 분리: namespace/ 하위 전용 디렉토리에 네임스페이스별 로직 유지
  8. 컴포넌트 매핑: 컴포넌트 매핑 시스템을 사용하여 네임스페이스별 컴포넌트로 라우팅
  9. 데이터 주입: 네임스페이스별 데이터에 Vue의 provide/inject 패턴 사용

트러블슈팅#

일반적인 문제#

  1. 브레드크럼이 표시되지 않음: 부모 라우트에 meta.text 속성이 있는지 확인
  2. 라우트가 작동하지 않음: ee/config/routes/project.rb에 와일드카드 라우트가 있는지 확인
  3. 사이드바가 강조 표시되지 않음: 사이드바 메뉴 항목에 올바른 active_routes가 있는지 확인
  4. 404 오류: 라우트 경로가 기존 라우트와 충돌하지 않는지 확인

디버깅 팁#

  1. Vue DevTools: $route.matched를 검사하여 어떤 라우트가 일치하는지 확인
  2. 라우터 상태: Vue DevTools에서 라우터 구성 확인
  3. 브레드크럼 디버그: 브레드크럼 컴포넌트에 console.log(this.$route.matched) 추가
  4. URL 테스트: 브라우저에서 URL로 직접 이동하여 라우트 테스트

가짜 플로우 생성#

플랫폼을 테스트하기 위한 가짜 플로우를 생성하려면 ee/lib/tasks/gitlab/duo_workflow/duo_workflow.rake Rake 작업을 실행할 수 있습니다.

특정 사용자가 특정 프로젝트에서 만든 50개의 플로우 중 20개를 만드는 예시:

bundle exec rake "gitlab:duo_workflow:populate[50,20,user@example.com,gitlab-org/gitlab-test]