Node.js · 2026-02-24

GraphQL 인증과 권한 관리(roles, directives)

GraphQL 서비스에서 인증과 역할 기반 권한 관리를 구현하는 방법을 Node.js와 Apollo 예제로 정리한 설명

작성일 : 2026-02-24 ㆍ 작성자 : 관리자
post
목차

개요

GraphQL API는 유연하지만 인증과 권한 관리는 신중한 설계가 필요하다. 이 글에서는 기본 인증 흐름과 역할 기반 권한 검사 방식을 설명한다. 또한 Node.js 환경에서 토큰을 이용한 인증과 Apollo 서버에서의 권한 검사 예제를 중심으로 다룬다.

왜 인증과 권한 관리가 필요한가

인증과 권한의 차이

인증(authentication)은 사용자가 누구인지 확인하는 절차다. 권한(authorization)은 인증된 사용자가 어떤 자원에 접근할 수 있는지를 판단한다. 두 개념은 서로 보완적이다. 인증만 있으면 누군지 알 수 있지만, 권한이 없으면 민감한 데이터에 접근할 수 없다.

GraphQL 특유의 문제

GraphQL은 단일 엔드포인트로 다양한 데이터를 노출한다. 따라서 세부 필드별 접근 제어가 중요하다. REST보다 더 세밀한 권한 검사가 필요한 경우가 많다.

인증 흐름(토큰 기반)

일반적인 흐름

  • 사용자가 로그인하고 서버는 JWT 같은 토큰을 발급한다.
  • 클라이언트는 요청 시 Authorization 헤더에 토큰을 담아 보낸다.
  • 서버는 토큰을 검증하고 컨텍스트에 사용자 정보를 넣는다.
  • 리졸버는 컨텍스트의 사용자 정보를 보고 접근을 허용 또는 차단한다.

Node.js + Apollo 예제

간단한 컨텍스트 설정과 리졸버 전 권한 검사 흐름 예제.

// server.js
const { ApolloServer, gql } = require('apollo-server');
const jwt = require('jsonwebtoken');

const typeDefs = gql`
  type Query { me: User }
  type User { id: ID, email: String, role: String }
`;

const resolvers = {
  Query: {
    me: (parent, args, context) => {
      if (!context.user) throw new Error('Unauthorized');
      return context.user;
    }
  }
};

const server = new ApolloServer({
  typeDefs,
  resolvers,
  context: ({ req }) => {
    const auth = req.headers.authorization || '';
    const token = auth.replace('Bearer ', '');
    if (!token) return {};
    try {
      const payload = jwt.verify(token, process.env.JWT_SECRET);
      return { user: { id: payload.sub, email: payload.email, role: payload.role } };
    } catch (e) {
      return {};
    }
  }
});

server.listen();

권한 관리: roles 기반

단순한 역할 검사

가장 쉬운 방법은 리졸버 안에서 역할을 검사하는 것이다. 작은 프로젝트에 적합하다. 하지만 많은 리졸버에 동일 검사를 반복하면 코드 중복이 생긴다.

// resolvers에서 역할 검사
function requireRole(role) {
  return (resolver) => (parent, args, context, info) => {
    if (!context.user) throw new Error('Unauthorized');
    if (context.user.role !== role) throw new Error('Forbidden');
    return resolver(parent, args, context, info);
  };
}

// 사용 예
Query: {
  adminData: requireRole('admin')((_, __, ___) => {
    return { secret: 'only for admin' };
  })
}

디렉티브를 이용한 권한 검사

스키마 레벨에서 권한을 선언하면 가독성이 좋아진다. @auth 같은 커스텀 디렉티브를 선언해 사용한다. 아래 예제는 개념을 보여주기 위한 간단한 구현이다.

// schema.graphql
directive @hasRole(role: String!) on FIELD_DEFINITION

type Query {
  adminData: String @hasRole(role: "admin")
}

// directive 구현 (간단한 래퍼)
const { SchemaDirectiveVisitor } = require('apollo-server');
const { defaultFieldResolver } = require('graphql');

class HasRoleDirective extends SchemaDirectiveVisitor {
  visitFieldDefinition(field) {
    const { resolve = defaultFieldResolver } = field;
    const { role } = this.args;
    field.resolve = async function(parent, args, context, info) {
      if (!context.user) throw new Error('Unauthorized');
      if (context.user.role !== role) throw new Error('Forbidden');
      return resolve.call(this, parent, args, context, info);
    };
  }
}

실무 팁과 고려사항

  • 토큰 만료와 갱신 로직을 설계한다. 짧은 만료와 리프레시 토큰 조합이 안전하다.
  • 필드 단위로 민감한 데이터가 있다면 디렉티브나 미들웨어로 세밀하게 제어한다.
  • 로그와 감사(audit)를 남겨 누가 언제 어떤 요청을 했는지 추적 가능하게 한다.
  • 권한 데이터는 사용자 정보와 분리하여 관리하면 권한 변경을 빠르게 반영할 수 있다.

결론

GraphQL 인증과 권한 관리는 설계가 중요하다. 컨텍스트 기반 인증 흐름을 만들고, 역할 검사는 리졸버 래핑이나 디렉티브로 구현한다. Node.js 환경에서 JWT로 인증하고, Apollo의 디렉티브나 래퍼를 통해 접근 제어를 중앙화하면 유지보수가 쉬워진다. 예제를 통해 구조를 이해한 뒤 프로젝트 요구에 맞춰 확장하는 방식이 현실적이다.

GraphQL 인증 Node.js GraphQL 권한 관리 예제 Apollo 권한 검사 Node JWT 인증 GraphQL directives roles 기반 권한 Node.js GraphQL 보안 Authorization