본문으로 건너뛰기

© 2026 Molayo

Zenn헤드라인2026. 06. 09. 00:39

VibeUI에서 AI 에이전트용 API 키 인증을 설계한 이야기

요약

VibeUI에서 AI 에이전트용 API 키 인증 시스템을 설계하며 고려한 보안 실무를 다룹니다. API 키의 해시 저장, Prefix 노출, Bearer token 활용 및 권한 관리 전략을 통해 안전한 API 운영 방식을 제안합니다.

핵심 포인트

  • API 키는 SHA-256으로 해시화하여 DB에 저장해 평문 유출 방지
  • 사용자 식별을 위해 키의 Prefix만 관리 화면에 노출
  • Authorization header의 Bearer token을 활용한 인증 처리
  • 로그에 API 키가 남지 않도록 주의하며 service role 권한 제한
  • last_used_at 필드를 통한 키 사용 현황 및 교체 주기 관리

서론

VibeUI에서는 AI 에이전트가 디자인 조각(design fragments)을 검색하여 사용할 수 있는 API를 만들고 있습니다.

외부에서 API로 사용할 수 있게 하려면, 피할 수 없는 것이 API 키 인증입니다.

하지만 API 키를 "작동만 하면 된다"는 식으로 만들면 위험합니다.

API 키를 DB에 평문(plain text)으로 저장한다
프론트엔드에 강력한 권한을 부여한다
이용 로그를 남기지 않는다
...

이런 부분들을 대충 처리하면 나중에 운영이 힘들어집니다.

이 기사에서는 VibeUI에서 구현한 API 키 인증 설계를 정리합니다.

API 키는 단 한 번만 표시한다

VibeUI에서는 API 키 생성 시 랜덤한 키를 생성합니다.

형식은 보았을 때 VibeUI의 키임을 알 수 있도록 prefix를 붙이고 있습니다.

vui_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

사용자에게 표시하는 것은 생성 직후 단 한 번뿐입니다.

DB에는 API 키 그 자체가 아니라 SHA-256으로 해시(hash)화한 값을 저장합니다.

raw API key
-> SHA-256 hash
-> api_keys.hashed_key 에 저장

이를 통해 DB를 보더라도 원래의 API 키는 알 수 없습니다.

물론 API 키는 비밀번호와 같지는 않으므로, 유출되었을 때 무효화할 수 있는 설계도 필요합니다. 다만, 평문 저장을 피하는 것만으로도 사고 발생 시 피해를 줄일 수 있습니다.

prefix만 관리 화면에 노출한다

API 키를 해시화하면 사용자는 나중에 키 전체를 확인할 수 없습니다.

그래서 관리 화면에서는 키의 앞부분(prefix)만 표시합니다.

key_prefix: vui_sk_x
label: Production key
is_active: true
...

사용자는 prefix나 label을 통해 "어떤 키인지" 식별할 수 있습니다.

키 전체는 다시 표시하지 않습니다. 키를 잊어버렸다면 새로운 키를 발급하고, 오래된 키를 무효화하는 방식으로 운영합니다.

API Route에서는 Bearer token을 받는다

검색 API에서는 Authorization header로부터 Bearer token을 받습니다.

Authorization: Bearer vui_sk_xxxxxxxxxxxx

API Route 측에서는 받은 키를 그 자리에서 해시화하여, DB에 저장되어 있는 hashed_key와 대조합니다.

1. Authorization header를 읽는다
2. Bearer token을 추출한다
3. SHA-256으로 해시화한다
...

여기서 중요한 것은 받은 API 키를 로그에 남기지 않는 것입니다.

에러 발생 시 로그에 Authorization header를 그대로 출력하면, 그것만으로도 유출이 됩니다.

service role은 API Route 내로 한정한다

외부 API 인증에서는 API 키의 소유자를 확인해야 합니다.

이 처리 과정에서는 RLS(Row Level Security)를 넘어 API 키를 대조하고 싶은 상황이 있습니다. 그래서 VibeUI에서는 서버 측의 API Route 내에서 service role client를 사용하고 있습니다.

단, service role은 강력한 권한을 가지므로 사용하는 곳을 한정합니다.

사용하는 곳:
API Route
Webhook
...

API 키 인증에서는 service role을 "API 키 대조 및 이용 로그 저장을 위한 백엔드 권한"으로 취급합니다.

프론트엔드에 노출해서는 안 됩니다.

last_used_at을 업데이트한다

API 키가 유효할 경우, last_used_at을 업데이트합니다.

이는 사소해 보이지만 운영 면에서 상당히 유용합니다.

어떤 키가 사용되고 있는가
장기간 사용되지 않은 키는 무엇인가
교체 후에 구형 키가 아직 사용되고 있지는 않은가

API 키를 여러 개 발급할 수 있게 되면 사용자 스스로도 "이 키를 아직 쓰고 있었나?"라고 생각하게 됩니다.

last_used_at이 있는 것만으로도 무효화 판단을 내리기 쉬워집니다.

usage log를 남긴다

VibeUI에서는 검색 API 호출마다 usage log를 남깁니다.

기록하는 정보는 예를 들어 다음과 같습니다.

api_key_id
design_id
request_path
...

AI 에이전트용 API에서는 어떤 도구나 에이전트가 호출했는지를 나중에 확인하고 싶어 합니다.

그렇기 때문에 임의 헤더(arbitrary header)로서 다음과 같은 정보도 받을 수 있도록 했습니다.

X-Client-App: my-agent
X-AI-Agent: codex

이것은 인증에 사용하지 않습니다.

어디까지나 분석이나 이용 상황을 파악하기 위한 용도입니다.

credit제와 인증을 연결하기

API 키 인증만으로는 이용량을 제어할 수 없습니다.

VibeUI에서는 API 키를 통해 소유 사용자를 특정하고, 해당 사용자의 credit 잔액을 확인합니다.

API 키를 대조한다
-> user_id를 가져온다
-> credit_balance를 확인한다
...

여기서 API 키는 '누구의 요청인가'를 특정하는 입구가 됩니다.

credit은 해당 사용자 단위로 관리합니다.

CORS도 API의 일부로 생각하기

AI 에이전트나 외부 클라이언트에서 사용하는 API에서는 CORS 설계도 필요합니다.

VibeUI의 검색 API에서는 외부에서 호출하기 쉽도록 CORS 헤더(CORS header)를 반환하고 있습니다.

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, OPTIONS
Access-Control-Allow-Headers: Authorization, Content-Type, X-Client-App, X-AI-Agent

단, CORS는 인증이 아닙니다.

외부에서 호출할 수 있게 하더라도, 인가(Authorization)는 반드시 API 키와 DB 측의 상태를 기준으로 판단합니다.

구현하며 알게 된 점

API 키 인증은 단순히 '키가 일치하면 OK'인 것이 아니었습니다.

공개 API로 운영하려면 최소한 다음 요소들이 필요합니다.

키 생성
평문으로 저장하지 않는 설계
단 한 번만 표시하는 UI
...

특히 AI 에이전트용 API는 인간이 화면에서 몇 번 클릭하는 방식과는 다릅니다.

도구가 연속해서 호출하거나, 여러 환경에서 호출하거나, 실패 시 재시도(retry)하는 방식. 그러한 사용 패턴을 전제로 로그와 제어 기능을 처음부터 넣어두어야 한다고 느꼈습니다.

요약

VibeUI의 API 키 인증에서는 키를 그대로 저장하지 않고, 해시(hash)화하여 DB에 저장합니다.

API Route에서는 Bearer token을 받아 해시 대조, 활성화(active) 확인, credit 확인, 사용 로그(usage log) 저장까지 수행합니다.

API 키는 단 한 번만 표시한다
DB에는 해시(hash)만 저장한다
service role은 API Route 내부에 한정한다
...

AI 에이전트용 API를 만든다면, 검색 기능 그 자체만큼이나 인증과 이용 기록 설계가 중요합니다.

Discussion

AI 자동 생성 콘텐츠

본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0