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();
앱 구조#
소스 코드 보기
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] --> E</code></pre></details></div>
새 내비게이션 항목 추가#
GitLab Duo Agent Platform은 Vue Router 구성이 브레드크럼 내비게이션을 직접 구동하는 라우터 기반 내비게이션 시스템을 사용합니다. 핵심은 ee/app/assets/javascripts/ai/duo_agents_platform/router/index.js의 라우터 구조가 URL 구조와 브레드크럼 계층 구조 모두를 결정한다는 것입니다.
라우터 기반 내비게이션 작동 방식#
시스템은 다음 상호 연결된 컴포넌트를 통해 작동합니다:
- 라우터 구조:
meta.text 속성이 있는 중첩 라우트가 브레드크럼 레이블을 정의합니다
- 브레드크럼 컴포넌트:
duo_agents_platform_breadcrumbs.vue가 일치된 라우트에서 브레드크럼을 자동으로 생성합니다
- 라우트 주입:
index.js의 injectVueAppBreadcrumbs()가 라우터를 브레드크럼 시스템에 연결합니다
라우터 분석#
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)는 다음과 같이 작동합니다:
this.$route.matched에서 일치된 모든 라우트를 가져옵니다
- 일치된 각 라우트에서
meta.text를 추출합니다
- 텍스트와 라우트 경로로 브레드크럼 항목을 생성합니다
- 정적 브레드크럼("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
중첩 라우트 패턴#
플랫폼은 일관된 중첩 라우트 패턴을 사용합니다:
- 부모 라우트: URL 경로와 최상위 브레드크럼을 정의합니다
- NestedRouteApp 컴포넌트: 자식 라우트를 위한 단순한
<router-view /> 래퍼
- 자식 라우트: 특정 페이지와 추가 브레드크럼 세그먼트를 정의합니다
예시: 현재 에이전트 세션 구현#
기존 에이전트 세션 기능이 이 패턴을 보여줍니다:
{
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/
모범 사례#
- 라우터 구조: 브레드크럼 계층 구조를 정의하기 위해
meta.text 속성이 있는 중첩 라우트 사용
- URL 네이밍: URL 경로에 kebab-case 사용(
/your-feature, /yourFeature 아님)
- 라우트 이름: 라우트 이름에 설명적인 상수 사용(
AGENTS_PLATFORM_YOUR_FEATURE_INDEX)
- 컴포넌트 구성:
pages/[feature]/ 디렉토리에 기능 컴포넌트 배치
- 브레드크럼 텍스트: 모든
meta.text 값에 국제화 문자열(s__()) 사용
- 일관된 패턴:
NestedRouteApp으로 기존 중첩 라우트 패턴 따르기
- 네임스페이스 분리:
namespace/ 하위 전용 디렉토리에 네임스페이스별 로직 유지
- 컴포넌트 매핑: 컴포넌트 매핑 시스템을 사용하여 네임스페이스별 컴포넌트로 라우팅
- 데이터 주입: 네임스페이스별 데이터에 Vue의 provide/inject 패턴 사용
트러블슈팅#
일반적인 문제#
- 브레드크럼이 표시되지 않음: 부모 라우트에
meta.text 속성이 있는지 확인
- 라우트가 작동하지 않음:
ee/config/routes/project.rb에 와일드카드 라우트가 있는지 확인
- 사이드바가 강조 표시되지 않음: 사이드바 메뉴 항목에 올바른
active_routes가 있는지 확인
- 404 오류: 라우트 경로가 기존 라우트와 충돌하지 않는지 확인
디버깅 팁#
- Vue DevTools:
$route.matched를 검사하여 어떤 라우트가 일치하는지 확인
- 라우터 상태: Vue DevTools에서 라우터 구성 확인
- 브레드크럼 디버그: 브레드크럼 컴포넌트에
console.log(this.$route.matched) 추가
- 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]
