본문 바로가기
WIKI 기술 지식 베이스

5. 인가가 적용된 프론트엔드 구성 요소

원문 보기 위키 갱신

이전 섹션들에서 권한 프레임워크로 플러그인의 백엔드 API 라우트를 보호하는 방법을 배웠어요. 표시할 데이터를 반환하는 대부분의 라우트(우리의 GET /todos 라우트 같은)는 백엔드가 단순히 빈 목록이나 404를 반환하므로 프론트엔드에서 추가 변경이 필요 없어요. 그러나 변경(mutative) 액션을 트리거하는 UI 요소의 경우, 사용자에게 권한이 없을 때 숨기거나 비활성화하는 것이 일반적인 관행이에요.

출처: 문서

본문

이전 섹션들에서 권한 프레임워크로 플러그인의 백엔드 API 라우트를 보호하는 방법을 배웠어요. 표시할 데이터를 반환하는 대부분의 라우트(우리의 GET /todos 라우트 같은)는 백엔드가 단순히 빈 목록이나 404를 반환하므로 프론트엔드에서 추가 변경이 필요 없어요. 그러나 변경 액션을 트리거하는 UI 요소의 경우, 사용자에게 권한이 없을 때 숨기거나 비활성화하는 것이 일반적인 관행이에요.

예를 들어 todo 목록 애플리케이션의 "Add" 버튼을 생각해 보세요. 사용자가 이 버튼을 클릭하면 프론트엔드는 백엔드의 /todos 라우트에 POST 요청을 해요. 사용자가 todo를 추가하려다 권한이 없다면, 액션을 수행하고 오류를 마주하기 전까지는 알 방법이 없어요. 이는 좋지 않은 사용자 경험이에요. add 버튼을 비활성화하면 더 나아질 수 있어요.

참고

프론트엔드 구성 요소를 인가 뒤에 두는 것은 백엔드 라우트를 인가 뒤에 두는 것을 대체할 수 없어요. 프론트엔드의 인가 검사는 해당 백엔드 인가에 더해서, 사용자 경험 개선으로 사용되어야 해요. 백엔드 라우트를 인가 뒤에 두지 않으면 대응하는 프론트엔드 구성 요소를 비활성화해도 악의적인 행위자가 여전히 라우트에 요청을 보낼 수 있어요.

usePermission 사용

필요한 패키지를 추가하는 것부터 시작해 보겠어요.

$ yarn workspace @internal/plugin-todo-list \  add @backstage/plugin-permission-react @internal/plugin-todo-list-common

plugins/todo-list/src/components/TodoListPage/TodoListPage.tsx에서 다음 변경을 만들어 보겠어요.

plugins/todo-list/src/components/TodoListPage/TodoListPage.tsx

import {  alertApiRef,  discoveryApiRef,  fetchApiRef,  useApi,} from '@backstage/core-plugin-api';import { usePermission } from '@backstage/plugin-permission-react';import { todoListCreatePermission } from '@internal/plugin-todo-list-common';function AddTodo({ onAdd }: { onAdd: (title: string) => any }) {  const title = useRef('');  const { loading: loadingPermission, allowed: canAddTodo } = usePermission({    permission: todoListCreatePermission,  });  return (    <>      <Typography variant="body1">Add todo</Typography>      <Box        component="span"        alignItems="flex-end"        display="flex"        flexDirection="row"      >        <TextField          placeholder="Write something here..."          onChange={e => (title.current = e.target.value)}        />        <Button variant="contained" onClick={handleAdd}>          Add        </Button>        {!loadingPermission && (          <Button            disabled={!canAddTodo}            variant="contained"            onClick={() => onAdd(title.current)}          >            Add          </Button>        )}      </Box>    </>  );}

여기서 usePermission 훅을 사용해 권한 정책과 통신하고, 이 사용자가 todo 목록 항목을 만들 권한이 있는지에 대한 결정을 받아요.

정말 그렇게 간단해요! 비활성화된 버튼을 테스트하기 위해 정책을 변경해 보겠어요. Getting Started 단계에서 만든 권한 정책 모듈의 CustomPolicy 클래스를 갱신하세요.

if (isPermission(request.permission, todoListCreatePermission)) {  return {    result: AuthorizeResult.ALLOW,    result: AuthorizeResult.DENY,  };}

이제 프론트엔드에서 todo 항목을 만들 수 없다는 것을 볼 수 있을 거예요!

RequirePermission 사용

비활성화 상태를 제공하는 것은 사용자에게 유용한 신호가 될 수 있지만, 요소를 숨기는 것이 선호되는 경우가 있을 수 있어요. 그런 경우 제공되는 RequirePermission 구성 요소를 사용할 수 있어요.

plugins/todo-list/src/components/TodoListPage/TodoListPage.tsx

import {  alertApiRef,  discoveryApiRef,  fetchApiRef,  useApi,} from '@backstage/core-plugin-api';import { usePermission } from '@backstage/plugin-permission-react';import { RequirePermission } from '@backstage/plugin-permission-react';import { todoListCreatePermission } from '@internal/plugin-todo-list-common';export const TodoListPage = () => {  // ..  <Grid container spacing={3} direction="column">    <Grid item>      <AddTodo onAdd={handleAdd} />    </Grid>    <RequirePermission permission={todoListCreatePermission} errorPage={<></>}>      <Grid item>        <AddTodo onAdd={handleAdd} />      </Grid>    </RequirePermission>    <Grid item>      <TodoList key={key} onEdit={setEdit} />    </Grid>  </Grid>;};function AddTodo({ onAdd }: { onAdd: (title: string) => any }) {  const title = useRef('');  const { loading: loadingPermission, allowed: canAddTodo } = usePermission({    permission: todoListCreatePermission,  });  return (    <>      <Typography variant="body1">Add todo</Typography>      <Box        component="span"        alignItems="flex-end"        display="flex"        flexDirection="row"      >        <TextField          placeholder="Write something here..."          onChange={e => (title.current = e.target.value)}        />        {!loadingPermission && (          <Button            disabled={!canAddTodo}            variant="contained"            onClick={() => onAdd(title.current)}          >            Add          </Button>        )}        <Button variant="contained" onClick={() => onAdd(title.current)}>          Add        </Button>      </Box>    </>  );}

이제 todo 목록 항목을 추가하는 구성 요소가 전혀 렌더링되지 않는 것을 볼 수 있을 거예요. 성공이에요!

또한 RequirePermission을 사용해 라우트에 대한 접근을 방지할 수도 있어요. packages/app/src/App.tsx에서 그렇게 보일 거예요.

packages/app/src/App.tsx

import { RequirePermission } from '@backstage/plugin-permission-react';import { todoListCreatePermission } from '@internal/plugin-todo-list-common';const routes = (  <FlatRoutes>    <Route path="/search" element={<SearchPage />}>      {searchPage}    </Route>    <Route path="/settings" element={<UserSettingsPage />} />    <Route path="/todo-list" element={      {/* You might want to create a "read" permission for this, we are just using this one as an example */}      <RequirePermission permission={todoListCreatePermission}>        <TodoListPage />      </RequirePermission>    }>      {/* ... */}    </Route>  </FlatRoutes>);

이제 권한이 없다면 https://localhost:3000/todo-list로 이동하려고 할 때 오류 페이지를 보게 될 거예요.

더 알아보기 (Learn more)