Search 시작하기
Search 시작하기 (이전 프론트엔드 시스템)
이전 프론트엔드 시스템을 사용하는 Backstage 앱에서 검색을 추가·구성하는 가이드예요.
출처: 문서
본문
:::info 이 문서는 여전히 이전 프론트엔드 시스템을 사용하는 Backstage 앱을 위한 것이에요. 앱이 새 프론트엔드 시스템을 사용한다면 현재 가이드를 읽으세요. :::
Search는 Backstage의 플러그인으로 동작하므로, Search를 사용하려면 Backstage를 사용해야 해요.
아직 Backstage를 설정하지 않았다면 여기에서 시작하세요.
npx @backstage/create-app을 사용했고 packages/app/src/components/search에 검색 페이지가 이미 정의되어 있다면, 아래의 Search 사용자 지정하기로 건너뛰세요.
프론트엔드에 Search 추가하기
Backstage 루트 디렉터리에서
yarn --cwd packages/app add @backstage/plugin-search @backstage/plugin-search-react
Backstage 앱에 새 packages/app/src/components/search/SearchPage.tsx 파일을 다음 내용으로 만들어요.
import { Content, Header, Page } from '@backstage/core-components';import { Grid, List, Card, CardContent } from '@material-ui/core';import { SearchBar, SearchResult, DefaultResultListItem, SearchFilter,} from '@backstage/plugin-search-react';import { CatalogSearchResultListItem } from '@backstage/plugin-catalog';export const searchPage = ( <Page themeId="home"> <Header title="Search" /> <Content> <Grid container direction="row"> <Grid item xs={12}> <SearchBar /> </Grid> <Grid item xs={3}> <Card> <CardContent> <SearchFilter.Select name="kind" values={['Component', 'Template']} /> </CardContent> <CardContent> <SearchFilter.Checkbox name="lifecycle" values={['experimental', 'production']} /> </CardContent> </Card> </Grid> <Grid item xs={9}> <SearchResult> {({ results }) => ( <List> {results.map(result => { switch (result.type) { case 'software-catalog': return ( <CatalogSearchResultListItem key={result.document.location} result={result.document} highlight={result.highlight} /> ); default: return ( <DefaultResultListItem key={result.document.location} result={result.document} highlight={result.highlight} /> ); } })} </List> )} </SearchResult> </Grid> </Grid> </Content> </Page>);
위 Search 페이지를 packages/app/src/App.tsx 파일의 /search 라우트에 다음과 같이 바인딩해요.
import { SearchPage } from '@backstage/plugin-search';import { searchPage } from './components/search/SearchPage';const routes = ( <FlatRoutes> <Route path="/search" element={<SearchPage />}> {searchPage} </Route> </FlatRoutes>);
Search 모달 사용하기
Root.tsx에서 SidebarSearchModal 컴포넌트를 추가해요.
import { SidebarSearchModal } from '@backstage/plugin-search';export const Root = ({ children }: PropsWithChildren<{}>) => ( <SidebarPage> <Sidebar> <SidebarLogo /> <SidebarSearchModal /> <SidebarDivider />...
Root.tsx 사용에 대한 자세한 내용은 changelog를 참고하세요.
백엔드에 Search 추가하기
백엔드 앱에 다음 플러그인들을 추가해요.
Backstage 루트 디렉터리에서
yarn --cwd packages/backend add @backstage/plugin-search-backend @backstage/plugin-search-backend-module-pg @backstage/plugin-search-backend-module-catalog @backstage/plugin-search-backend-module-techdocs
그런 다음 다음 줄들을 추가해요.
packages/backend/src/index.ts
const backend = createBackend();// Other plugins...// search pluginbackend.add(import('@backstage/plugin-search-backend'));// search enginesbackend.add(import('@backstage/plugin-search-backend-module-pg'));// search collatorsbackend.add(import('@backstage/plugin-search-backend-module-catalog'));backend.add(import('@backstage/plugin-search-backend-module-techdocs'));backend.start();
위 설정으로 Search는 Lunr 인메모리 검색 엔진을 사용하지만, 데이터베이스로 Postgres를 설정했다면 Postgres를 검색 엔진으로 사용해요. 자세한 내용은 Search 엔진 문서를 참고하세요.
위 설정은 Catalog와 TechDocs 두 개의 콜레이터도 설정해 주며, 이 두 위치의 콘텐츠를 인덱싱해서 쉽게 검색할 수 있게 해줘요. 자세한 내용은 콜레이터 문서를 참고하세요.
Search 사용자 지정하기
프론트엔드
Search 플러그인 웹 라이브러리(@backstage/plugin-search-react)는 <SearchFilter.Select />와 <SearchFilter.Checkbox />를 포함해 여러 기본 필터 유형을 정적 속성으로 노출해요. 이들은 Backstage 인스턴스와 관련된 값을 제공할 수 있게 해주며, 선택하면 값이 백엔드로 전달돼요.
<CardContent> <SearchFilter.Select name="kind" values={['Component', 'Template']} /></CardContent><CardContent> <SearchFilter.Checkbox name="lifecycle" values={['production', 'experimental']} /></CardContent>
고급 필터 요구가 있다면 다음과 같이 나만의 필터 컴포넌트를 지정할 수 있어요(새 핵심 필터 기여는 환영이에요).
import { useSearch, SearchFilter } from '@backstage/plugin-search-react';const MyCustomFilter = () => { // Note: filters contain filter data from other filter components. Be sure // not to clobber other filters' data! const { filters, setFilters } = useSearch(); return (/* ... */);};// Which could be rendered like this:<SearchFilter component={MyCustomFilter} />
검색 결과가 그것을 반환하는 데 사용된 정보를 하이라이트하는 것이 좋은 관행이에요! 아래 코드는 <CatalogSearchResultListItem /> 컴포넌트를 예시로 사용해 사용자 지정 결과 항목 컴포넌트를 지정하는 방법을 보여줘요.
<SearchResult> {({ results }) => ( <List> {results.map(result => { // result.type is the index type defined by the collator. switch (result.type) { case 'software-catalog': return ( <CatalogSearchResultListItem key={result.document.location} result={result.document} highlight={result.highlight} /> ); // ... } })} </List> )}</SearchResult>
Search 프론트엔드를 더 고급으로 사용자 지정하려면 'Search API를 직접 구현하는 방법'이나 '검색 결과 하이라이팅 스타일을 사용자 지정하는 방법' 같은 how-to 가이드도 참고하세요.
백엔드
Backstage Search는 그 자체가 검색 엔진은 아니에요. 대신 Backstage 인스턴스와 선택한 검색 엔진 사이의 인터페이스를 제공해요. 현재는 인메모리 검색 엔진인 Lunr와 Elasticsearch 두 가지만 지원해요. 이들을 Backstage 인스턴스에서 구성하는 방법에 대한 자세한 내용은 Search 엔진 문서를 참고하세요.
Backstage Search는 무엇이든 검색하는 데 사용할 수 있어요! Catalog 같은 플러그인은 인덱싱할 문서를 제공하는 기본 콜레이터(예: DefaultCatalogCollator)를 제공하며, IndexBuilder에 원하는 만큼 콜레이터를 등록할 수 있어요.
const indexBuilder = new IndexBuilder({ logger: env.logger, searchEngine });const every10MinutesSchedule = env.scheduler.createScheduledTaskRunner({ frequency: { minutes: 10 }, timeout: { minutes: 15 }, initialDelay: { seconds: 3 },});const everyHourSchedule = env.scheduler.createScheduledTaskRunner({ frequency: { hours: 1 }, timeout: { minutes: 90 }, initialDelay: { seconds: 3 },});indexBuilder.addCollator({ schedule: every10MinutesSchedule, factory: DefaultCatalogCollatorFactory.fromConfig(env.config, { discovery: env.discovery, tokenManager: env.tokenManager, }),});indexBuilder.addCollator({ schedule: everyHourSchedule, factory: new MyCustomCollatorFactory(),});
Backstage Search는 예약(schedule)에 따라 인덱스를 구축하고 유지해요. 주어진 문서 유형에 대해 인덱스가 다시 구축되는 빈도를 변경할 수 있어요. 문서가 더 자주 또는 덜 자주 업데이트된다면 이렇게 하고 싶을 수 있어요. 이는 schedule 값에 전달할 예약된 SchedulerServiceTaskRunner를 구성해 처리할 수 있어요.
const every10MinutesSchedule = env.scheduler.createScheduledTaskRunner({ frequency: { minutes: 10 }, timeout: { minutes: 15 }, initialDelay: { seconds: 3 },});indexBuilder.addCollator({ schedule: every10MinutesSchedule, factory: DefaultCatalogCollatorFactory.fromConfig(env.config, { discovery: env.discovery, tokenManager: env.tokenManager, }),});
:::note
인메모리 Lunr 검색 엔진을 사용한다면, 여러 검색 백엔드 노드를 실행할 때 일관성을 보장하기 위해 다음처럼 비분산(non-distributed) SchedulerServiceTaskRunner를 구현하고 싶을 거예요(또는 검색 플러그인이 SQLite 같은 비분산 데이터베이스를 사용하도록 구성할 수도 있어요).
:::
import { SchedulerServiceTaskRunner, SchedulerServiceTaskInvocationDefinition,} from '@backstage/backend-plugin-api';const schedule: SchedulerServiceTaskRunner = { run: async (task: SchedulerServiceTaskInvocationDefinition) => { const startRefresh = async () => { while (!task.signal?.aborted) { try { await task.fn(task.signal); } catch { // ignore intentionally } await new Promise(resolve => setTimeout(resolve, 600 * 1000)); } }; startRefresh(); },};indexBuilder.addCollator({ schedule, factory: DefaultCatalogCollatorFactory.fromConfig(env.config, { discovery: env.discovery, tokenManager: env.tokenManager, }),});
Search 백엔드를 더 고급으로 사용자 지정하려면 'Software Catalog 또는 TechDocs 인덱스의 필드를 사용자 지정하는 방법' 같은 how-to 가이드도 참고하세요.