Frontend

Clerk metadata로 어드민 계정 롤 부여하기

jbilee 2024. 6. 5. 23:51

Clerk 서비스를 사용하면 간편하게 인증 플로우를 구현할 수 있는 것은 물론, 유저 계정을 권한별로 나누도록 구현할 수도 있다.

 

Clerk에서는 Organization이라는 부가기능을 통해 유저를 그룹별로 관리하고 어드민 권한 등을 설정할 수 있지만, Pro 티어를 사용하지 않으면 한 Organization에 최대 5명까지만 추가할 수 있다. 하지만 metadata를 활용하면 Pro 티어 없이도 어드민 유저를 지정해 특정 페이지에 접근할 수 있도록 구현할 수 있다.

 

유저 계정에 metadata 추가

Metadata는 API를 사용하거나 Clerk 대시보드에서 수동으로 설정할 수 있다.

 

Public과 unsafe metadata는 클라이언트에서 접근할 수 있는 반면 private metadata는 서버에서만 접근할 수 있도록 되어있다. Next.js 프레임워크를 선택한 나 같은 경우 서버 함수에서만 auth 로직을 돌리도록 구현했기 때문에 private metadata에 { "role": "admin" }을 설정했다.

 

대시보드 > 유저 정보 화면

 

미들웨어 적용

아래는 미들웨어를 Clerk 미들웨어 하나만 적용한다고 했을 때 바로 사용할 수 있는 코드다. (다른 미들웨어도 함께 사용해야 할 경우 공식 문서 참고) 이 미들웨어에서는 유저 정보의 metadata를 확인해 role이 admin일 경우에만 페이지 접근을 허용한다.

import { clerkClient, clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";

const isProtectedRoute = createRouteMatcher(["/users(.*)"]);

export default clerkMiddleware(async (auth, req) => {
  if (isProtectedRoute(req)) {
    try {
      const { userId } = auth();
      const userData = await clerkClient.users.getUser(userId ?? "");
      auth().protect(() => userData.privateMetadata.role === "admin");
    } catch (e) {
      console.log(e);
    }
    auth().protect();
  }
});

export const config = {
  matcher: ["/((?!.*\\..*|_next).*)", "/", "/(api|trpc)(.*)"],
};

 

권한이 없는 유저로부터 보호할 페이지는 Clerk에서 제공하는 createRouteMatcher() 함수에 대상 path들을 배열로 전달해 지정한다. 이때 리턴되는 isProtectedRoute() 함수는 미들웨어 함수 내에서 사용해 보호된 페이지인지 아닌지를 확인한다.

 

clerkMiddleware()에 전달하는 함수는 auth() 메소드와 HTTP 요청을 인자로 받는다. 로그인한 유저가 아닐 경우 auth() 함수에서 예외가 발생하기 때문에 try-catch 블럭에서 유저 데이터를 읽고 metadata를 체크하도록 했다.

 

페이지 접근을 제한할 땐 auth().protect() 함수를 호출한다. 이 함수에는 boolean 값을 리턴하는 또다른 함수를 인자로 전달할 수 있어서, 어드민 계정인지를 체크하는 조건부를 넣어줬다. 아무것도 전달하지 않을 경우 조건없이 모든 유저들의 접근을 차단하게 된다.