[TYPESCRIPT] Nextjs 권한 테스트 전략: Authorization 로직을 테스트로 고정 방법

 

권한 설계를 어느 정도 정리하고 나면, 운영 단계에서 또 하나의 현실적인 문제가 등장합니다. “이 권한 로직이 앞으로도 계속 맞게 동작한다는 걸 어떻게 보장할 것인가”입니다.

 

실무에서는 이런 상황을 자주 겪게 됩니다.

  • 작은 기능 수정이 기존 권한을 깨뜨린다
  • 관리자 기능을 추가했는데 일반 사용자에게도 열려 버린다
  • 권한 버그가 QA나 운영에서 뒤늦게 발견된다

 

권한 로직은 눈에 잘 보이지 않습니다. 그래서 테스트로 고정하지 않으면, “언젠가 깨질 코드”가 되기 쉽습니다.

 

이번 글에서는 권한을 테스트로 보호하는 방법을, 단순한 테스트 기법이 아니라 운영 안정성을 위한 설계 요소로 정리합니다.

  • 권한 테스트가 필요한 이유와 범위
  • Policy 단위 테스트를 어디까지 해야 하는지
  • API/Server Action 권한 테스트 기준
  • 과도하지 않으면서 효과적인 테스트 전략

 

개념/배경 설명: 권한 버그는 왜 늦게 발견되는가

권한 버그의 가장 큰 특징은 “정상 기능 테스트에서는 잘 드러나지 않는다”는 점입니다.

 

  • 정상 사용자 시나리오는 잘 동작한다
  • 엣지 케이스(다른 역할, 다른 리소스)에서만 깨진다
  • UI에서는 막혔지만 서버에서는 허용된다

 

이런 버그는 수동 테스트로 잡기 어렵고, 운영 로그로도 바로 보이지 않습니다. 그래서 권한 로직은 코드보다 테스트가 먼저 필요합니다.

 

핵심 설계 1: 권한 테스트의 최소 단위는 Policy 함수다

권한 테스트를 어디서부터 해야 할지 고민된다면, 가장 작은 단위는 명확합니다.

 

Policy 함수 하나 = 테스트 대상 하나

 


// src/server/auth/policies/canEditPost.ts
type Context = {
  userId: string;
  role: 'ADMIN' | 'USER';
};

type Post = {
  ownerId: string;
};

export function canEditPost(ctx: Context, post: Post): boolean {
  if (ctx.role === 'ADMIN') return true;
  return ctx.userId === post.ownerId;
}

 

이 함수는 권한의 핵심 규칙을 담고 있습니다. 이 규칙이 바뀌면 서비스의 보안 의미도 함께 바뀝니다. 따라서 가장 먼저 테스트로 고정해야 합니다.

 

Policy 단위 테스트 예시


// canEditPost.test.ts
import { canEditPost } from './canEditPost';

describe('canEditPost', () => {
  it('관리자는 항상 수정 가능하다', () => {
    expect(
      canEditPost({ userId: 'a', role: 'ADMIN' }, { ownerId: 'b' }),
    ).toBe(true);
  });

  it('작성자는 자신의 글을 수정할 수 있다', () => {
    expect(
      canEditPost({ userId: 'a', role: 'USER' }, { ownerId: 'a' }),
    ).toBe(true);
  });

  it('다른 사용자의 글은 수정할 수 없다', () => {
    expect(
      canEditPost({ userId: 'a', role: 'USER' }, { ownerId: 'b' }),
    ).toBe(false);
  });
});

 

실무 포인트 정리

  • 권한 규칙은 반드시 테스트로 고정한다
  • Policy 테스트는 빠르고 단순해야 한다
  • 여기서 실패하면 배포하면 안 된다

 

핵심 설계 2: API/Server Action 테스트는 “권한 연결”만 확인한다

모든 API를 세밀하게 테스트하려고 하면, 권한 테스트는 금방 과해집니다.

 

실무 기준은 다음과 같습니다.

  • 권한 로직 자체는 Policy 단위 테스트로 충분히 검증
  • API/Server Action에서는 “권한을 호출하는지”만 확인

 


// API Route 테스트 개념 예시
it('권한이 없으면 403을 반환한다', async () => {
  const res = await request(app)
    .post('/posts/1')
    .set('Authorization', userToken)
    .send({ title: 'change' });

  expect(res.status).toBe(403);
});

 

이 테스트의 목적은 비즈니스 로직이 아니라, “권한 검증이 빠지지 않았는지”를 확인하는 것입니다.

 

실무 포인트 정리

  • API 테스트에서 모든 권한 경우를 다 만들 필요는 없다
  • 대표적인 허용/차단 케이스만 검증한다
  • 권한 테스트가 기능 테스트를 잠식하지 않게 한다

 

핵심 설계 3: UI 권한 테스트는 “보안”이 아니라 “UX” 관점이다

UI에서의 권한 테스트는 보안을 보장하지 않습니다. 보안은 항상 서버 책임입니다.

 

UI 권한 테스트의 목적은 다음입니다.

  • 권한 없는 사용자에게 버튼이 노출되지 않는지
  • 권한 변경 시 화면이 깨지지 않는지

 

따라서 UI 테스트에서는 “버튼이 보이느냐/안 보이느냐” 정도면 충분합니다.

 

운영/실무에서 자주 겪는 문제

  • 권한 로직이 바뀌었는데 테스트가 없어 그대로 배포된 경우
  • Policy는 수정했지만 API 테스트는 통과해서 문제를 놓친 경우
  • 모든 권한 조합을 테스트하려다 테스트 유지비가 폭증한 경우

 

권한 테스트의 실패는 보통 “테스트를 안 했다”가 아니라 “어디까지 테스트해야 하는지 기준이 없었다”에서 발생합니다.

 

실무 권장 체크리스트

  • 모든 핵심 Policy에 단위 테스트가 존재하는가
  • 권한 규칙 변경 시 테스트가 함께 수정되는가
  • API/Server Action에서 권한 검증 누락을 막고 있는가
  • UI 테스트가 보안 테스트 역할을 대신하고 있지 않은가
  • 권한 테스트 범위가 과하지도, 부족하지도 않은가

 


 

 

권한 테스트의 목적은 “완벽한 커버리지”가 아니라 권한 규칙이 무너지는 것을 조기에 막는 것입니다.

 

Policy를 테스트로 고정하고, API에서는 연결만 확인하며, UI는 UX 관점으로 최소화하면 권한 테스트는 부담이 아니라 안전망이 됩니다.