---
term: 'API 키 보안'
seoTitle: 'API 키 보안: 저장, 회전, 유출, 클라이언트 측 키'
headline: 'API 키 보안이란?'
slug: api-key-security
category: auth-security
shortDefinition: 'API 키는 API를 호출하는 앱을 식별하는 고유 문자열이며, API 키 보안은 그 범위를 제한하고 키를 보호하는 규율입니다.'
relatedTerms:
  - json-web-token-jwt
  - api-rate-limiting-throttling
  - cors-cross-origin-resource-sharing
  - data-encryption-at-rest-transit
contrastsWith:
  - json-web-token-jwt
aboutTerms:
  - 'Publishable vs. Secret Keys'
  - 'Key Rotation'
  - 'Secrets Management'
faq:
  - question: 'API 키란 무엇입니까?'
    answer: 'API 제공업체가 등록된 애플리케이션에 발급하는 고유 문자열로, 모든 요청에 함께 보냅니다(헤더로 보내는 것이 이상적입니다). 서버는 이를 통해 호출자를 식별하고, 권한을 적용하고, 사용량을 계측하고, 속도 제한을 강제합니다. 주목할 점은 이를 정의하는 표준이 없다는 것입니다. API 키는 프로토콜이 아니라 관례입니다.'
  - question: 'API 키는 비밀번호입니까?'
    answer: '시크릿 키는 기능적으로 비밀번호급입니다. 베어러 자격 증명이므로 이를 가진 사람은 누구든 키 소유자로 신뢰받습니다. 저장 규율도 같고, 유출되었을 때의 결과도 같습니다. 차이점은 키가 사람이 아니라 애플리케이션을 식별한다는 것, 그리고 상당수가 회전하지 않는 한 만료되지 않는다는 것입니다.'
  - question: 'API 키와 토큰의 차이는 무엇입니까?'
    answer: '키는 앱을 식별하고, 토큰은 사용자를 인증합니다. 키는 정적이고 관리자가 생성하며 앱 단위로 범위가 정해집니다. OAuth 액세스 토큰은 로그인 시 발급되고 수명이 짧으며 갱신할 수 있고 특정 사용자의 권한을 담습니다. 서버 간 식별에는 키가 맞고, 사용자에 따라 달라지는 모든 것은 토큰의 몫입니다.'
  - question: 'API 키는 어디에 저장해야 합니까?'
    answer: '소스 코드에는 절대 저장하지 않습니다. 버전 관리에서 제외한 파일에서 읽는 환경 변수가 최소 기준이지만, 환경 변수는 로그, 프로세스 덤프, 컨테이너 정의를 통해 유출된다는 단서가 붙습니다. 팀 표준은 시크릿 관리 도구입니다. 저장 시 암호화되고, 접근이 통제되며, 감사 기록이 남고, 회전할 수 있습니다.'
  - question: '프론트엔드나 모바일 코드에 API 키를 넣어도 됩니까?'
    answer: '그 용도로 설계된 퍼블리셔블 키만 가능합니다. JavaScript 번들이나 앱 바이너리에 들어간 것은 모두 공개된 것입니다. 추출은 일상적인 일이고 난독화는 이를 늦출 뿐입니다. 시크릿 키는 서버 측에 두어야 하며, 클라이언트가 시크릿 키가 필요한 서비스를 써야 할 때는 자체 백엔드를 거쳐 호출해야 합니다.'
  - question: 'API 키가 유출되면 어떻게 해야 합니까?'
    answer: '즉시 키를 폐기하고, 대체 키를 배포하고, 코드와 git 기록에서 키를 제거하고, 사용 로그에서 악용 여부를 감사하고, 함께 저장되어 있던 모든 것을 회전합니다. 빠르게 움직여야 합니다. 봇은 공개 저장소에 커밋된 키를 몇 분 안에 시험합니다. 또한 폐기는 앞으로의 사용을 막을 뿐, 이미 빼돌려진 데이터를 되돌리지는 못한다는 점을 기억해야 합니다.'
  - question: 'API 키는 얼마나 자주 회전해야 합니까?'
    answer: '위험도에 따라 다릅니다. 범위가 넓거나 외부에 노출된 키는 30–90일마다, 위험이 낮은 내부 키는 그보다 길게 회전하고, 노출이 의심되거나 담당자가 퇴사하면 즉시 회전합니다. 무중단 회전은 배포가 따라잡는 동안 이전 키와 새 키가 모두 유효한 중첩 기간을 둡니다.'
  - question: 'API 키는 어떻게 유출됩니까?'
    answer: '악명 높은 순서대로 꼽으면 git 저장소에 커밋되는 경우, 클라이언트 번들과 모바일 바이너리에 담겨 배포되는 경우, 서버 로그와 브라우저 방문 기록에 남는 URL에 들어가는 경우, 애플리케이션 로그와 CI 로그에 출력되는 경우, 채팅과 티켓에 붙여 넣어지는 경우입니다. 모든 경로가 예방 가능하다는 사실이 이 목록을 더 씁쓸하게 만듭니다.'
codeLanguages: [javascript, dart, swift, kotlin]
externalAuthorities:
  - name: 'OWASP Secrets Management Cheat Sheet'
    url: 'https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html'
  - name: 'OWASP API Security Top 10 (2023)'
    url: 'https://owasp.org/API-Security/editions/2023/en/0x11-t10/'
  - name: 'OWASP Key Management Cheat Sheet'
    url: 'https://cheatsheetseries.owasp.org/cheatsheets/Key_Management_Cheat_Sheet.html'
  - name: 'RFC 6750 — OAuth 2.0 Bearer Token Usage'
    url: 'https://datatracker.ietf.org/doc/html/rfc6750'
  - name: 'API key — Wikipedia'
    url: 'https://en.wikipedia.org/wiki/API_key'
cta:
  title: '배포를 전제로 설계된 키'
  text: 'Back4app의 클라이언트 키는 설계상 공개되어도 됩니다. 데이터는 키의 비밀성이 아니라 서버 측에서 강제되는 CLP와 ACL로 보호되며, 마스터 키는 시크릿이 있어야 할 곳, 즉 서버에 남습니다.'
  linkText: '무료로 시작하기'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-10-30'
translationKey: api-key-security
---

**API 키는 API를 호출하는 앱을 식별하는 고유 문자열이며, API 키 보안은 그 범위를 제한하고 키를 보호하는 규율입니다.** 대부분의 정의가 흐릿하게 넘어가는 부분이므로 정확성부터 짚겠습니다. 키는 애플리케이션을 *식별*하고, *약한 인증*만 제공하며(베어러 자격 증명이므로 가진 사람이 곧 주인입니다), *거친 인가*(권한 부여)만 담습니다. 그 범위는 생성 시 붙인 것이 전부입니다. 사용자는 [토큰](/glossary/ko/json-web-token-jwt/)으로 인증하고 앱은 키로 식별합니다. 그리고 API 키를 정의하는 RFC는 하나도 없습니다. API 키는 관례이며, 바로 그 때문에 API 키의 보안은 표준이 보장해 주는 것이 아니라 사용자의 설정에 달려 있습니다.

## 핵심 요약

| 질문 | 답변 |
| --- | --- |
| 키가 하는 일 | 앱 식별 · 사용량 계측 · [속도 제한](/glossary/ko/api-rate-limiting-throttling/)의 기준 |
| 서로 다른 두 종류 | 퍼블리셔블 키(배포용으로 설계) vs. 시크릿 키(비밀번호급) |
| 저장 사다리 | 하드코딩: 절대 금지 → 환경 변수: 최소 기준 → 시크릿 관리 도구: 표준 |
| 철칙 | 클라이언트 번들에 든 것은 모두 공개됨. 추출을 전제로 계획 |
| 유출 대응 | 폐기 → 교체 → git 기록 삭제 → 감사. 며칠이 아니라 몇 분 안에 |

## 요청, 그리고 두 종류의 키

```text
GET /v1/search?q=espresso HTTP/1.1
Host: api.example.com
X-Api-Key: pk_live_7f2c…      ← 반드시 헤더에 넣을 것. URL은 로그,
                                 방문 기록, 리퍼러에 남음

이름은 같아도 전혀 다른 두 종류:
퍼블리셔블 키     웹/모바일 번들에 담겨 배포됨 · 앱을 식별하고
                  사용량을 계측 · 반드시 추출된다는 전제로 설계됨
시크릿 키         서버 측 전용 · 비밀번호급 베어러 자격 증명 ·
                  키를 가진 사람이 곧 키 소유자로 취급됨
```

실무에서의 퍼블리셔블 모델입니다. 보안이 다른 곳에 있기 때문에 앱과 함께 배포되는 키입니다:

**JavaScript:**

```javascript
// JavaScript / Node.js — Back4app JS SDK
// Publishable keys: safe to ship BECAUSE authorization lives server-side
Parse.initialize(APP_ID, JS_KEY); // both ship in your bundle — by design
Parse.serverURL = 'https://parseapi.back4app.com';
// What protects data isn't key secrecy — it's CLPs + ACLs checked per request

// The one key that never ships: the Master Key bypasses every ACL and CLP.
// Server-only (Cloud Code / trusted backend), read from env or secret manager.
```

**Flutter:**

```dart
// Flutter / Dart — Back4app Flutter SDK
// Publishable keys: safe to ship BECAUSE authorization lives server-side
await Parse().initialize(
  appId, // ships in the app — by design
  'https://parseapi.back4app.com',
  clientKey: clientKey, // publishable, extractable, NOT a secret
);
// What protects data isn't key secrecy — it's CLPs + ACLs checked per request
// The Master Key bypasses every ACL and CLP: server-only, never in the app.
```

**Swift:**

```swift
// iOS / Swift — Back4app Swift SDK
// Publishable keys: safe to ship BECAUSE authorization lives server-side
ParseSwift.initialize(
    applicationId: appId,  // ships in the IPA — by design
    clientKey: clientKey,  // publishable, extractable, NOT a secret
    serverURL: URL(string: "https://parseapi.back4app.com")!
)
// What protects data isn't key secrecy — it's CLPs + ACLs checked per request
// The Master Key bypasses every ACL and CLP: server-only, never in the app.
```

**Kotlin:**

```kotlin
// Android / Kotlin — Back4app Android SDK
// Publishable keys: safe to ship BECAUSE authorization lives server-side
Parse.initialize(
    Parse.Configuration.Builder(context)
        .applicationId(APP_ID) // ships in the APK — by design
        .clientKey(CLIENT_KEY) // publishable, extractable, NOT a secret
        .server("https://parseapi.back4app.com")
        .build()
)
// What protects data isn't key secrecy — it's CLPs + ACLs checked per request
// The Master Key bypasses every ACL and CLP: server-only, never in the app.
```

## API 키 vs. 토큰 vs. JWT

| | API 키 | OAuth 액세스 토큰 | JWT |
| --- | --- | --- | --- |
| 식별 대상 | 애플리케이션 | 사용자(및 권한 부여) | 클레임이 말하는 무엇이든 |
| 발급 | 관리자가 한 번 | 로그인할 때마다, 플로를 통해 | 발급 방식이 아니라 *형식*임 |
| 수명 | 회전할 때까지(대개 무기한) | 수 분에서 수 시간 | `exp`가 정한 만큼 |
| 범위 | 생성 시 고정 | 권한 부여별 범위 | 클레임으로 정의 |
| 표준 | 없음. 관례일 뿐 | [OAuth 2.0](/glossary/ko/oauth-2-social-login/) | RFC 7519 |
| 적합한 용도 | 서버 간 식별, 사용량 계측 | 사용자가 위임한 API 접근 | 서명된 클레임 전달 |

이 비교는 외워 둘 만한 한 문장으로 요약됩니다. **키는 앱을 식별하고, 토큰은 사용자를 인증합니다.** 사용자의 신원이 중요한 곳에 키를 쓰면 인증을 엉성하게 다시 만드는 셈이고, 익명 앱 계측에 사용자별 토큰을 쓰면 목적 없는 장치만 늘어납니다.

## 키를 두는 곳: 저장 사다리

**하드코딩: 절대 금지.** 소스 코드는 복사되고, 포크되고, 커밋됩니다. git은 영원히 기억하며, 시크릿 스캐닝 봇은 공개 커밋에서 몇 분 안에 키를 찾아냅니다. **환경 변수: 최소 기준.** 단, [OWASP 치트 시트](https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html)가 직설적으로 지적하는 단서가 붙습니다. 환경 변수는 오류 로그, 프로세스 덤프, 컨테이너 정의를 통해 유출됩니다. 시크릿이 git에 들어가는 것은 막아 주지만, 유출 자체를 막아 주지는 못합니다. **시크릿 관리 도구: 팀 표준.** 시크릿 관리 도구(secrets manager)는 저장 시 암호화되고, 서비스별로 접근이 통제되며, 읽을 때마다 감사 기록이 남고, 중앙에서 회전할 수 있습니다(오픈소스 선택지로는 Vault, SOPS, Infisical이 있습니다). 여기에 유출을 견딜 수 있게 만드는 위생 수칙을 더합니다. 암호학적으로 무작위하게 생성한 키, 스캐너가 알아볼 수 있도록 *접두사*를 붙인 키(`sk_live_…` 형태), 제공업체 측에서 비밀번호처럼 해시해 저장하는 키, 앱별·환경별로 하나씩 둔 키, 그리고 키를 유출하는 커밋이 반영되기 전에 실패하도록 CI에 연결한 시크릿 스캐닝(gitleaks, trufflehog)입니다.

## 클라이언트 측 문제, 솔직하게

모든 설명 글이 "시크릿 키를 클라이언트 코드에 넣지 말라"고 말하지만, 그 뒷부분까지 말하는 글은 거의 없습니다. **번들은 공개되어 있습니다.** 웹 JavaScript는 정의상 누구나 읽을 수 있고, 모바일 바이너리는 일상적으로 해체되어 문자열이 추출됩니다. 난독화는 필요한 노력을 몇 분에서 몇 시간으로 늘릴 뿐이며, 그나마도 누군가 한 번 풀어내면 끝입니다. 여기서 두 가지 결론이 나옵니다. 첫째, 클라이언트에 들어가도 되는 키는 *퍼블리셔블* 키뿐입니다. 보호가 아니라 식별을 위해 설계되었고, 실제 인가는 요청마다 서버 측에서 강제됩니다. 둘째, 클라이언트가 시크릿 키가 필요한 서드파티 서비스를 써야 한다면 시크릿은 자체 백엔드 뒤에 남겨야 합니다. 바로 프록시 패턴입니다:

```mermaid
flowchart LR
  accTitle: 시크릿 키를 서버 측에 두는 프록시 패턴
  accDescr: 클라이언트 앱은 퍼블리셔블 키만 가지고 자체 백엔드를 호출합니다. 시크릿 관리 도구에 시크릿 키를 보관한 백엔드가 서드파티 API를 호출하고 결과를 돌려주므로, 시크릿은 클라이언트로 배포되지 않습니다.
  C["클라이언트 앱<br/>퍼블리셔블 키만 보유"] -->|"자체 API"| B["자체 백엔드<br/>시크릿 관리 도구의<br/>시크릿 키"]
  B -->|"X-Api-Key: sk_live_…"| T["서드파티 API"]
  T --> B --> C
  X["공격자가 번들을 해체"] -.->|"훔칠 만한 것을<br/>찾지 못함"| C
```

## 키가 유출되었을 때: 런북

시간이 중요합니다. 봇은 공개 저장소를 감시하다가 커밋된 키를 **1–5분 안에** 악용합니다. 순서는 다음과 같습니다. **1 · 폐기**: 조사보다, 스탠드업보다 먼저 제공업체에서 키를 폐기합니다. **2 · 교체**: 새 키를 발급하고 코드가 아니라 설정을 통해 배포합니다. **3 · 삭제**: 소스 코드 *그리고 git 기록*에서 제거합니다. 삭제한 줄도 모든 클론에 계속 살아 있습니다. **4 · 감사**: 유출 기간의 제공업체 로그를 확인해 무엇이 읽히고, 생성되고, 지출되었는지 봅니다. **5 · 범위 확대**: 키와 같은 곳에 있던 모든 것(같은 .env, 같은 저장소)은 노출된 것으로 간주하고 함께 회전합니다. 그리고 실제 대응과 형식적인 절차를 가르는 단서가 있습니다. **폐기는 앞으로의 사용을 막을 뿐, 빼돌려진 데이터를 되돌리지는 못합니다.** 유출 기간에 빠져나간 것은 회전으로 끝날 일이 아니라 보안 사고입니다.

## 무중단 키 회전

회전은 탐지되지 않은 유출의 가치에 상한을 둡니다. 유효 기간이 60일 남은 도난 키는 영원히 유효한 키와 전혀 다른 자산입니다. 위험도에 따른 주기는 범위가 넓거나 외부와 공유된 키는 30–90일, 범위가 좁은 내부 키는 최대 1년이며, 노출이 의심되거나 키를 다루던 사람이 떠나면 *즉시*입니다. 무중단으로 하는 방법은 **이중 키 중첩**입니다. 이전 키가 유효한 동안 새 키를 발급하고, 여유 있게 배포를 옮긴 뒤, 이전 키를 폐기합니다. 리프레시 토큰 시스템이 공식화한 것과 같은 기법입니다. 규정 준수 체계는 점점 더 이런 정기 일정을 요구하지만, 보안상의 근거는 처음부터 그런 요구 없이도 충분했습니다.

## 일반적인 사용 사례

- **서버 간 연동** – 키가 가장 잘 맞는 본래 용도입니다. 한 서비스가 다른 서비스에 자신을 식별시킵니다.
- **사용량 계측과 과금** – 제공업체가 세고, 제한하고, 청구하는 단위로서의 키입니다.
- **퍼블리셔블 클라이언트 식별** – 노출을 전제로 만든 키를 앱 번들에 담고, 인가는 다른 곳에서 처리합니다.
- **환경 분리** – 테스트 키와 라이브 키를 나눠 스테이징의 사고가 프로덕션 데이터에 닿지 않게 합니다.
- **악용 억제** – 키별 [속도 제한](/glossary/ko/api-rate-limiting-throttling/)과 폐기가 영향 범위를 통제하는 수단이 됩니다.

## 어떤 자격 증명을 선택해야 할까? 결정 매트릭스

| 상황 | 선택할 것 |
| --- | --- |
| 백엔드가 서드파티 API를 호출 | 시크릿 키, 시크릿 관리 도구에 보관 |
| 웹/모바일에서 앱을 식별 | 퍼블리셔블 키 + 서버 측 인가 |
| 로그인한 사용자를 대신해 작업 | 키가 아니라 OAuth 토큰 |
| 서비스 간 서명된 클레임 | [JWT](/glossary/ko/json-web-token-jwt/) |
| 클라이언트가 시크릿 키가 필요한 서비스를 사용 | 프록시 역할의 자체 백엔드. 시크릿은 배포되지 않음 |
| 사용자 인증과 비슷한 방식이 필요한 머신 간 통신 | OAuth 클라이언트 자격 증명 플로 |

## 한계와 트레이드오프

- **키는 소유를 증명하지 못합니다.** 베어러 문자열은 호출자와 암호학적으로 결합되지 않습니다. 높은 보증 수준의 서비스 인증을 위해 상호 TLS와 서명된 요청이 존재하는 데에는 이유가 있습니다.
- **정적 자격 증명은 시간이 지날수록 위험해집니다.** 만료가 없으므로 모든 유출은 발견될 때까지 끝없이 열려 있습니다. 회전은 토큰이 저절로 얻는 수명 주기를 수동으로 대신하는 방법입니다.
- **범위가 거칠면 권한을 과도하게 내줍니다.** 넓은 권한을 가진 키 하나는 만능 열쇠입니다. 세분화된 범위 지정은 관리 비용이 들지만, 그 대가는 줄어든 영향 범위로 돌아옵니다.
- **키는 식별할 뿐 인증하지 않습니다.** 앱 수준 식별 위에 사용자 수준 신뢰를 쌓는 것은 감사인이 가장 먼저 찾는 [취약한 인증](https://owasp.org/API-Security/editions/2023/en/0x11-t10/)(broken authentication) 패턴입니다.
- **키 목록은 관리하지 않으면 실제와 어긋납니다.** 오래된 연동에서 쓰던 미사용 키는 삭제될 때까지 유효합니다. 좀비 엔드포인트의 사촌 격인 좀비 자격 증명입니다.

## Back4app에서의 API 키

Back4app은 관리형 데이터베이스, 자동 생성되는 REST 및 GraphQL API, 인증, 파일 스토리지, Cloud Code 서버리스 함수를 결합한 오픈소스 Backend as a Service(BaaS) 플랫폼입니다. Back4app의 키 모델은 앞의 클라이언트 측 섹션을 구체화한 것입니다. Application ID와 클라이언트 키는 **설계상 앱 안에 담겨 배포됩니다**. Back4app 문서도 클라이언트 키가 보안 메커니즘이 아니라고 명시합니다. 인가가 결코 이 키에 의존하지 않기 때문입니다. 모든 요청은 서버 측에서 [클래스 수준 권한](/glossary/ko/class-level-permissions-clp/)과 [ACL](/glossary/ko/access-control-lists-acl/)에 따라 검사되므로, 추출된 키로 공격자가 얻는 것은 익명 사용자가 얻는 것과 정확히 같습니다. 진짜 시크릿은 모든 ACL과 CLP를 우회하는 **마스터 키** 하나뿐입니다. 마스터 키는 서버 측(Cloud Code, 신뢰할 수 있는 백엔드, 환경 변수 또는 시크릿 관리 도구)에만 두며, 번들에는 절대 넣지 않습니다. 코드 탭은 이 분리를 실제로 보여 줍니다. 런북은 마스터 키에만 적용되며, 바로 그것이 핵심입니다. 지켜야 할 시크릿이 하나라면 보안 태세이고, 마흔 개라면 스프레드시트입니다.
