1. "로그인했는데 왜 나를 모르니?" (문제의 시작)

OAuth2, JWT, 세션 인증… 인증 방식은 정말 많고, 대부분의 경우엔 그걸로 충분합니다. 저도 그랬고요.

  • 직접 만든 이메일 클라이언트나 ChatGPT의 MyGPT에서 OAuth2를 붙였을 때는 “아, 이게 진짜 사용자 경험이다” 싶었고
  • 단독 Django 서버로 끝나는 웹 앱은 세션 인증이 최고!
  • 프론트와 협업하는 구조(프론트/백 분리)에서는 JWT가 가장 깔끔했습니다.

그런데, 이 조합이 어느 순간 한 번에 무너지는 구간이 있었습니다.

바로 비동기 작업(Celery) 이었습니다. 사용자가 버튼을 누르면, 백엔드가 직접 일을 하는 게 아니라 저 멀리 있는 AI 연산 서버나 워커에게 일을 시킵니다. 이때 워커는 말합니다.

"저기... 요청은 받았는데, 이거 누구 대신해서 하는 일이죠? request.user가 없는데요?"

로봇 워커가 apikey를 들고 편지를 전달하는 이미지

2. 문제는 “백엔드 ↔ 백엔드” + “비동기 워커(Celery)”

제가 API Key를 도입하게 된 결정적 계기는 백엔드 서버끼리 통신하는데, 그 사이에 Celery worker가 개입하는 구조때문이었습니다.

사용자가 버튼을 눌렀다고 해서 요청이 곧장 AI 연산 서버로 가는 게 아니라,

  1. 사용자가 웹 요청을 보냄
  2. 백엔드가 “작업”을 큐에 넣음
  3. Celery worker가 큐를 소비하면서 어딘가의 백엔드/연산 서버로 비동기 요청을 보냄

이 상황에서 제일 아픈 지점이 뭐였냐면:

  • worker에는 request.user가 없습니다
  • 세션도 없습니다 (브라우저가 아니니까요)
  • JWT도 애매합니다 (토큰을 “누가” “어디서” “어떻게” 관리하고 전달할지부터 복잡해짐)
  • OAuth2는 더더욱 “사용자 상호작용”이 전제인 흐름이지요. 애당초 적용 불가입니다.

결국 JWT와 세션이 무력해지는 순간에 남는 질문이 하나로 압축됩니다.

“연산서버에서 이 작업을 실행하는 주체가 어떤 고객(테넌트/유저)인지 특정할 수 있도록, 워커가 어떻게 알려주게 하지?”

3. 워커 세계에는 ‘인증’보다 ‘식별’이 먼저 필요했다

웹 요청에서는 “인증=로그인”이고 “로그인=유저”가 자연스럽게 연결됩니다. 하지만 워커는 사람이 아니라 애플리케이션이 스스로 CPU를 빌려 움직이는 존재라서, 인증이라는 말보다 먼저 “식별”이 필요했습니다. 인증은 서버간의 HMAC 나 seceryKey등을 사용하면 되는 문제니까요.

  • 이 작업은 A 고객의 데이터로 실행돼야 함
  • 결과는 A 고객의 리소스에 저장돼야 함
  • 요금제/권한/쿼터는 A 고객 기준으로 차감돼야 함

이걸 JWT나 세션으로 억지로 끼워 맞추려고 하면, 토큰 발급/보관/전달/만료/재발급 설계가 커지고, 무엇보다도 "아무리 내 서버의 고객이지만 고객이 브라우저를 직접 사용하고 있는 중이 아닌데, 백엔드에서 고객 정보를 가져다가 JWT를 발급받는다?" 라는 왠지 모를 엄청난 거부감이 듭니다. 할 수는 있지만 절대 해서는 안되는 그런 것을 떠올리는 기분이 들었습니다. 너무 찝찝해서 그 아이디어는 바로 폐기 했습니다.

4. 해결: API Key는 이 구간에서 너무 단순하고 강력했다

그래서 도입한 게 API Key였고, 이 문제를 한 번에 정리해줬습니다.

  • worker가 내부 요청을 보낼 때 헤더 하나로 인증/식별을 동시에 처리
  • 서버는 그 키를 보고 어떤 유저/고객의 요청인지 즉시 매핑
  • 키를 회수/교체(롤링)하는 것도 훨씬 명확함

예를 들면 이런 느낌으로 서버는 연산서버에 비동기 요청을 보냅니다.

POST /v1/ai/jobs
Authorization: Api-Key <KEY>
Content-Type: application/json

{ "job_id": "...", "payload": {...} }

워커는 request.user가 없어도 상관없습니다. 그냥 이대로 보내면 요청을 받는 백엔드에서 아래에 설명할 방법으로 저 Api-Key를 이용해서 사용자를 특정할 수 있습니다.

5. 결정적 개선: API Key를 USER와 묶어버리니 운영이 편해졌다

제가 특히 만족했던 포인트는 여기입니다.

기존 rest_framework_api_key 같은 라이브러리는 API Key 자체는 잘 제공하지만, 제 케이스에서는 “키 ↔ 사용자(고객) 결합”이 핵심이었습니다. 그래서 AbstractAPIKey를 상속해서 CustomAPIKey로 확장하고, AUTH_USER 모델과 FK로 연결했습니다.

결과는... 대만족 이었습니다.

대략 구조는 이런 식의 개념이에요.

from django.conf import settings
from rest_framework_api_key.models import AbstractAPIKey
from django.db import models

class CustomAPIKey(AbstractAPIKey):
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="api_keys"
    )
    is_test = models.BooleanField(default=False)  # 스테이지/테스트 키 구분

이렇게 묶어두면 단순한 “인증”을 넘어 운영 기능이 쭉 열립니다.

6. 유저당 키 자동 발급이 만들어준 운영 이득들

회원가입 시 유저에게 키를 하나 자동으로 심어두는 방식으로 운영하니, 다음이 아주 편해졌습니다.

1) 유효성 관리가 쉬워짐

  • 특정 유저의 키를 조회/비활성화/삭제가 단순해집니다.
  • 유저 탈퇴 시 연결된 키 정리도 자연스럽습니다(FK cascade).

2) 키 롤링이 쉬워짐

  • “키 유출 의심” 같은 상황에서 새 키 발급 → 구 키 폐기 흐름이 명확합니다.
  • 다중 키를 허용하면 무중단 교체도 가능합니다(새 키 배포 후 구 키 종료).

3) 요금제/쿼터/권한 관리가 유저 중심으로 붙음

  • 키 단위가 아니라 유저 단위로 과금/제한을 걸 수 있어요.
  • “이 API Key는 누구의 요청인가?”를 매번 따로 추론할 필요가 없습니다.

4) 한 유저에 여러 키를 발급할 수 있음

여기서 is_test 같은 플래그가 빛을 발합니다. APIKey 인증방식이라는 개념이 OneToOne 모델 연결이 아니라 FK이기 때문에 하나의 유저에 여러개의 key를 용도별로 달아줄 수 있습니다.

  • 같은 유저가 스테이지 모드용 키프로덕션용 키를 동시에 가질 수 있음
  • 하나의 계정으로 개발/운영 흐름을 분리하기가 쉬움
  • 로그/모니터링에서도 “테스트 트래픽 vs 실제 트래픽”을 깔끔히 나눌 수 있음

7. 인증 방식은 우열이 아니라 “상황별 무기 선택”이다

정리하면, 제가 지금도 느끼는 “상황별 최적 조합”은 대략 이렇습니다.

  • OAuth2: 외부 서비스/클라이언트 연동, 사용자 동의가 중요한 플로우에 강함
  • 세션 인증: Django 단독 웹 앱에서 개발 속도와 단순함이 최고
  • JWT: 프론트/백 분리, 모바일/SPA 등 다양한 클라이언트에서 균형이 좋음
  • API Key: 백엔드-백엔드, 자동화/워커/배치처럼 “사용자 요청이 아닌 요청”에서 압도적으로 편함

특히 Celery worker가 끼는 순간, “로그인 기반 인증”으로 세계를 통일하려는 욕심이 오히려 복잡도를 키웁니다.
그때 API Key는 정말 깔끔한 탈출구였습니다.

8. 마무리

사람(브라우저/앱)은 세션/JWT/OAuth2로 다루는 게 자연스럽습니다.
하지만 워커는 사람이 아니라 프로세스고, 프로세스는 “누구의 일인지”를 식별할 수 있어야 합니다.

제가 API Key로 넘어간 이유는 거창한 보안 담론이 아니라, 그 구간에서 가장 단순하게 문제를 해결했기 때문이었습니다.
그리고 USER와 묶어서 운영 단위를 “키”가 아니라 “사용자”로 맞추자, 키 관리는 도구가 아니라 운영 레버가 되었습니다.

여러분도 API-Key를 자주 사용하시나요? 제가 위에 사용한 방식은 APIKey의 편리함의 일부라고 생각합니다만, 이러한 작은 경험을 통해 이 글을 읽으시는 분들에게 좋은 영감을 전해드렸으면 그걸로 충분하다고 생각합니다.


관련글