TL;DR
Heroku API는 개발자가 배포 자동화, 앱 관리, 애드온 구성, 인프라 확장을 프로그래밍 방식으로 처리할 수 있도록 합니다. OAuth 2.0, 토큰 기반 인증을 지원하며, 앱/다이노/빌드/파이프라인을 위한 RESTful 엔드포인트를 제공합니다. 계정당 시간당 10,000 요청으로 제한되며, 이 가이드는 인증, 주요 엔드포인트, CI/CD 통합, 프로덕션 배포 전략을 실전 예제와 함께 안내합니다.
소개
Heroku는 170개 국가에서 400만 개 이상의 애플리케이션을 지원합니다. 10개 이상의 Heroku 앱을 관리하는 팀은 수동 배포와 설정에 매주 8~12시간을 낭비할 수 있습니다. Heroku API 통합은 배포 자동화, 다이노 확장, 환경 구성 동기화에 필수적입니다.
이 가이드는 토큰 인증, 앱/다이노 관리, 빌드 파이프라인, 애드온 프로비저닝, 오류 해결 등 Heroku API를 통한 자동화 실전 구현 방법을 빠르게 따라할 수 있게 정리합니다.
💡 Apidog는 API 통합 테스트를 간소화합니다. Heroku 엔드포인트를 테스트하고, 인증 흐름을 검증하며, API 응답을 검사하고, 문제를 디버깅하세요. 자세히 보기
Heroku API란 무엇인가요?
Heroku는 RESTful 플랫폼 API를 제공합니다. 주요 기능:
- 애플리케이션 생성/구성/삭제
- 다이노 확장 및 프로세스 관리
- 빌드/릴리스/파이프라인 관리
- 애드온 프로비저닝
- 도메인, SSL 인증서 관리
- 로그 드레인, 모니터링 설정
- 팀/협업자 관리
주요 기능 요약
| 기능 | 설명 |
|---|---|
| RESTful 설계 | JSON 응답, HTTP 메서드 표준 지원 |
| 토큰 인증 | OAuth2, 베어러 토큰 인증 |
| 범위 요청 | 대용량 결과 집합에 대한 페이지네이션 |
| 속도 제한 | 계정당 시간당 10,000 요청 |
| 멱등 생성 | 안전한 쓰기 작업 재시도 지원 |
| Gzip 압축 | 대역폭 절약을 위한 응답 압축 |
API 아키텍처 개요
Heroku는 버전 지정 REST API 구조(https://api.heroku.com/)와 JSON:API 스펙을 따릅니다.
API 버전 비교
| 버전 | 상태 | 인증 | 사용 사례 |
|---|---|---|---|
| 플랫폼 API (v3) | 현재 | 베어러 토큰 | 신규 통합 |
| GitHub 통합 | 현재 | OAuth 2.0 | GitHub 연결 앱 |
| 컨테이너 레지스트리 | 현재 | Docker 인증 | 컨테이너 배포 |
시작하기: 인증 설정
1단계: Heroku 계정 생성
- Heroku 웹사이트에서 계정 생성
- 이메일 인증 및 기본 설정 완료
2단계: Heroku CLI 설치
Heroku CLI는 토큰 생성과 명령 테스트에 필수입니다.
# macOS
brew tap heroku/brew && brew install heroku
# Windows
npm install -g heroku
# Linux
curl https://cli-assets.heroku.com/install.sh | sh
3단계: API 토큰 생성
Heroku CLI로 인증 및 토큰 발급:
# Heroku 로그인
heroku login
# 단기 토큰
heroku authorizations:create --short-lived
# 장기 토큰 (CI/CD용)
heroku authorizations:create --description "CI/CD Pipeline" --expires-in "1 year"
보안 TIP: 토큰은 환경 변수(.env)에 저장하고, 코드에 직접 기록하지 마세요.
# .env 파일 예시
HEROKU_API_KEY="your_api_key_here"
HEROKU_APP_NAME="your-app-name"
4단계: 토큰 인증 이해
모든 요청에는 아래 헤더가 필요합니다.
Authorization: Bearer {api_key}
Accept: application/vnd.heroku+json; version=3
5단계: 첫 번째 API 호출
인증 테스트:
curl -n https://api.heroku.com/account \
-H "Accept: application/vnd.heroku+json; version=3" \
-H "Authorization: Bearer $HEROKU_API_KEY"
예상 결과:
{
"id": "user-id-here",
"email": "developer@example.com",
"name": "Developer Name",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2026-03-20T14:22:00Z"
}
6단계: 코드에서 인증 구현
재사용 가능한 JS API 클라이언트 예시:
const HEROKU_API_KEY = process.env.HEROKU_API_KEY;
const HEROKU_BASE_URL = 'https://api.heroku.com';
const herokuRequest = async (endpoint, options = {}) => {
const response = await fetch(`${HEROKU_BASE_URL}${endpoint}`, {
...options,
headers: {
'Authorization': `Bearer ${HEROKU_API_KEY}`,
'Accept': 'application/vnd.heroku+json; version=3',
'Content-Type': 'application/json',
...options.headers
}
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Heroku API Error: ${error.message}`);
}
return response.json();
};
// 사용 예시
const account = await herokuRequest('/account');
console.log(`다음으로 로그인됨: ${account.email}`);
애플리케이션 관리
새 앱 생성
const createApp = async (appName, region = 'us') => {
const response = await herokuRequest('/apps', {
method: 'POST',
body: JSON.stringify({
name: appName,
region: region
})
});
return response;
};
// 사용 예시
const app = await createApp('my-awesome-app-2026');
console.log(`앱 생성됨: ${app.name}`);
console.log(`Git URL: ${app.git_url}`);
console.log(`웹 URL: ${app.web_url}`);
예상 응답
{
"id": "app-uuid-here",
"name": "my-awesome-app-2026",
"region": { "name": "us" },
"created_at": "2026-03-25T10:00:00Z",
"updated_at": "2026-03-25T10:00:00Z",
"git_url": "https://git.heroku.com/my-awesome-app-2026.git",
"web_url": "https://my-awesome-app-2026.herokuapp.com",
"owner": { "email": "developer@example.com" },
"build_stack": { "name": "heroku-24" }
}
앱 목록 보기
const listApps = async (limit = 50) => {
const response = await herokuRequest(`/apps?limit=${limit}`);
return response;
};
const apps = await listApps();
apps.forEach(app => {
console.log(`${app.name} - ${app.web_url}`);
});
앱 세부 정보 조회
const getApp = async (appName) => {
const response = await herokuRequest(`/apps/${appName}`);
return response;
};
const app = await getApp('my-awesome-app-2026');
console.log(`스택: ${app.build_stack.name}`);
console.log(`지역: ${app.region.name}`);
앱 구성 업데이트
const updateApp = async (appName, updates) => {
const response = await herokuRequest(`/apps/${appName}`, {
method: 'PATCH',
body: JSON.stringify(updates)
});
return response;
};
// 예시: 앱 이름 변경
const updated = await updateApp('old-app-name', { name: 'new-app-name' });
앱 삭제
const deleteApp = async (appName) => {
await herokuRequest(`/apps/${appName}`, { method: 'DELETE' });
console.log(`앱 ${appName}이 성공적으로 삭제되었습니다.`);
};
다이노 관리
다이노 확장
const scaleDyno = async (appName, processType, quantity) => {
const response = await herokuRequest(`/apps/${appName}/formation/${processType}`, {
method: 'PATCH',
body: JSON.stringify({ quantity: quantity })
});
return response;
};
// 예시: 웹 다이노를 3개로 확장
const formation = await scaleDyno('my-app', 'web', 3);
console.log(`web 다이노를 ${formation.quantity}개로 확장했습니다.`);
다이노 형성 가져오기
const getFormation = async (appName, processType = null) => {
const endpoint = processType
? `/apps/${appName}/formation/${processType}`
: `/apps/${appName}/formation`;
const response = await herokuRequest(endpoint);
return response;
};
// 사용 예시
const formation = await getFormation('my-app');
formation.forEach(proc => {
console.log(`${proc.type}: ${proc.quantity} dynos (@ ${proc.size})`);
});
사용 가능한 다이노 크기
| 다이노 유형 | 사용 사례 | 월별 비용 |
|---|---|---|
| 에코 | 취미, 데모 | $5 |
| 기본 | 소규모 프로덕션 | $7 |
| standard-1x | 표준 워크로드 | $25 |
| standard-2x | 고성능 앱 | $50 |
| 성능 | 미션 크리티컬 앱 | $250+ |
| 프라이빗 | 엔터프라이즈 격리 | 맞춤 |
다이노 재시작
const restartDynos = async (appName, processType = null) => {
const endpoint = processType
? `/apps/${appName}/formation/${processType}`
: `/apps/${appName}/dynos`;
await herokuRequest(endpoint, { method: 'DELETE' });
console.log(`${appName}의 다이노가 재시작되었습니다.`);
};
일회성 다이노 실행
const runCommand = async (appName, command) => {
const response = await herokuRequest(`/apps/${appName}/dynos`, {
method: 'POST',
body: JSON.stringify({
command: command,
size: 'standard-1x'
})
});
return response;
};
// 예시: 데이터베이스 마이그레이션
const dyno = await runCommand('my-app', 'npm run migrate');
console.log(`다이노 시작됨: ${dyno.id}`);
구성 변수
구성 변수 가져오기
const getConfigVars = async (appName) => {
const response = await herokuRequest(`/apps/${appName}/config-vars`);
return response;
};
// 사용 예시
const config = await getConfigVars('my-app');
console.log(`DATABASE_URL: ${config.DATABASE_URL}`);
console.log(`NODE_ENV: ${config.NODE_ENV}`);
구성 변수 설정
const setConfigVars = async (appName, variables) => {
const response = await herokuRequest(`/apps/${appName}/config-vars`, {
method: 'PATCH',
body: JSON.stringify(variables)
});
return response;
};
// 사용 예시
const updated = await setConfigVars('my-app', {
NODE_ENV: 'production',
API_SECRET: 'your-secret-key',
LOG_LEVEL: 'info'
});
구성 변수 모범 사례
- 비밀 커밋 금지 - 민감 정보는 환경 변수로만 관리
- 환경별 별도 변수 - 스테이징/프로덕션 분리
- 정기적 비밀 회전 - 분기별로 API 키 갱신
-
관련 변수 접두사 -
STRIPE_SECRET_KEY,STRIPE_WEBHOOK_SECRET등
빌드 및 릴리스 관리
빌드 생성
const createBuild = async (appName, sourceBlobUrl) => {
const response = await herokuRequest(`/apps/${appName}/builds`, {
method: 'POST',
body: JSON.stringify({
source_blob: { url: sourceBlobUrl }
})
});
return response;
};
// 예시
const build = await createBuild('my-app', 'https://storage.example.com/source.tar.gz');
console.log(`빌드 시작됨: ${build.id}`);
console.log(`상태: ${build.status}`);
빌드 상태 가져오기/폴링
const getBuild = async (appName, buildId) => {
const response = await herokuRequest(`/apps/${appName}/builds/${buildId}`);
return response;
};
const checkBuildStatus = async (appName, buildId, maxAttempts = 30) => {
for (let i = 0; i < maxAttempts; i++) {
const build = await getBuild(appName, buildId);
if (build.status === 'succeeded') {
console.log('빌드 성공!');
return build;
} else if (build.status === 'failed') {
throw new Error(`빌드 실패: ${build.output}`);
}
console.log(`빌드 진행 중... 시도 ${i + 1}`);
await new Promise(resolve => setTimeout(resolve, 5000));
}
throw new Error('빌드 시간 초과');
};
릴리스 목록 보기
const listReleases = async (appName, limit = 10) => {
const response = await herokuRequest(`/apps/${appName}/releases?limit=${limit}`);
return response;
};
// 예시
const releases = await listReleases('my-app');
releases.forEach(release => {
console.log(`v${release.version} - ${release.description} - ${release.created_at}`);
});
이전 릴리스로 롤백
const rollback = async (appName, releaseId) => {
const response = await herokuRequest(`/apps/${appName}/releases`, {
method: 'POST',
body: JSON.stringify({ rollback: releaseId })
});
return response;
};
// 예시: 버전 42로 롤백
const rollbackRelease = await rollback('my-app', 42);
console.log(`v${rollbackRelease.version}으로 롤백되었습니다.`);
파이프라인 관리
파이프라인 생성
const createPipeline = async (pipelineName) => {
const response = await herokuRequest('/pipelines', {
method: 'POST',
body: JSON.stringify({ name: pipelineName })
});
return response;
};
// 예시
const pipeline = await createPipeline('my-app-pipeline');
console.log(`파이프라인 생성됨: ${pipeline.id}`);
파이프라인에 앱 추가
const addAppToPipeline = async (pipelineId, appName, stage) => {
const response = await herokuRequest('/pipeline-couplings', {
method: 'POST',
body: JSON.stringify({
pipeline: pipelineId,
app: appName,
stage: stage // 'development', 'staging', 'production'
})
});
return response;
};
// 예시
await addAppToPipeline(pipelineId, 'my-app-dev', 'development');
await addAppToPipeline(pipelineId, 'my-app-staging', 'staging');
await addAppToPipeline(pipelineId, 'my-app-prod', 'production');
다음 단계로 슬러그 승격
const promoteSlug = async (slugId, toApp) => {
await herokuRequest('/promotions', {
method: 'POST',
body: JSON.stringify({
from: toApp, // Source app
to: toApp, // Target app (next stage)
slug: slugId
})
});
console.log(`슬러그 ${slugId}를 ${toApp}으로 승격했습니다.`);
};
애드온 관리
애드온 프로비저닝
const provisionAddon = async (appName, addonPlan, config = {}) => {
const response = await herokuRequest('/addon-attachments', {
method: 'POST',
body: JSON.stringify({
app: appName,
plan: addonPlan,
config: config
})
});
return response;
};
// PostgreSQL 예시
const db = await provisionAddon('my-app', 'heroku-postgresql:mini', {});
console.log(`데이터베이스 프로비저닝됨: ${db.addon.name}`);
console.log(`DATABASE_URL: ${db.addon.config_vars.DATABASE_URL}`);
인기 애드온
| 애드온 | 요금제 | 시작 가격 | 사용 사례 |
|---|---|---|---|
| heroku-postgresql | 미니 | 월 $5 | 프로덕션 DB |
| heroku-redis | 미니 | 월 $5 | 캐싱, 세션 |
| papertrail | 초콜라드 | 월 $7 | 로그 집계 |
| sentry | 개발자 | 무료 | 오류 추적 |
| mailgun | 샌드박스 | 무료 | 이메일 전송 |
| newrelic | 라이트 | 무료 | 애플리케이션 모니터링 |
애드온 목록 조회
const listAddons = async (appName) => {
const response = await herokuRequest(`/apps/${appName}/addons`);
return response;
};
const addons = await listAddons('my-app');
addons.forEach(addon => {
console.log(`${addon.plan.name} - $${addon.pricing.plan.price} - ${addon.state}`);
});
애드온 제거
const removeAddon = async (appName, addonId) => {
await herokuRequest(`/apps/${appName}/addons/${addonId}`, { method: 'DELETE' });
console.log(`애드온 ${addonId}가 ${appName}에서 제거되었습니다.`);
};
도메인 및 SSL 관리
사용자 지정 도메인 추가
const addDomain = async (appName, domainName) => {
const response = await herokuRequest(`/apps/${appName}/domains`, {
method: 'POST',
body: JSON.stringify({ hostname: domainName })
});
return response;
};
// 예시
const domain = await addDomain('my-app', 'api.example.com');
console.log(`CNAME 대상: ${domain.cname}`);
SSL 인증서 구성
const addSslCertificate = async (appName, domainId, certificateChain, privateKey) => {
const response = await herokuRequest(`/apps/${appName}/domains/${domainId}/ssl_endpoint`, {
method: 'PATCH',
body: JSON.stringify({
ssl_cert: {
cert_chain: certificateChain,
private_key: privateKey
}
})
});
return response;
};
ACM 자동화 SSL
const enableACM = async (appName, domainName) => {
const response = await herokuRequest(`/apps/${appName}/domains/${domainName}/sni_endpoint`, {
method: 'POST',
body: JSON.stringify({ kind: 'acm' })
});
return response;
};
속도 제한 및 할당량
속도 제한 이해
- 표준 제한: 계정당 시간당 10,000 요청
- 창: 60분 롤링 윈도우
- 초과 시: HTTP 429(Too Many Requests) 응답
속도 제한 처리 예시
const makeRateLimitedRequest = async (endpoint, options = {}, maxRetries = 3) => {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await herokuRequest(endpoint, options);
// 속도 제한 헤더 확인
const remaining = response.headers.get('RateLimit-Remaining');
const resetTime = response.headers.get('RateLimit-Reset');
if (remaining < 100) {
console.warn(`남은 할당량이 적음: ${remaining}, ${resetTime}에서 재설정`);
}
return response;
} catch (error) {
if (error.message.includes('429') && attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000;
console.log(`속도 제한됨. ${delay}ms 후에 재시도합니다...`);
await new Promise(resolve => setTimeout(resolve, delay));
} else {
throw error;
}
}
}
};
속도 제한 헤더
| 헤더 | 설명 |
|---|---|
| RateLimit-Limit | 시간당 최대 요청 수 |
| RateLimit-Remaining | 창에 남은 요청 수 |
| RateLimit-Reset | 창 재설정 시간(유닉스 타임) |
일반적인 문제 해결
인증 실패(401)
- API 키/토큰 정확성 확인:
heroku authorizations - 토큰 만료 여부 확인(최대 1년)
- 환경 변수 공백 등 오타 점검
- 필요 시 토큰 재생성
앱 이름 중복
- 고유 네임(팀명/랜덤/UUID 등) 사용
- 네임스페이스 접두사 활용:
team-app-env - 예시 코드:
const generateUniqueAppName = (baseName) => {
const timestamp = Date.now().toString(36);
const random = Math.random().toString(36).substring(2, 6);
return `${baseName}-${timestamp}-${random}`;
};
다이노 형성 실패
- Procfile에 프로세스 명시 확인
- 다이노 할당량 및 앱 상태 점검
- 다이노 시간 사용량 확인:
heroku ps --app=my-app
빌드 시간 초과
- 빌드팩 최적화 및 종속성 캐싱
- 대규모 빌드는 분할
- 사전 빌드된 슬러그 사용 권장
속도 제한 초과(429)
- 요청 대기열 및 지수 백오프 구현
- 요청 일괄 처리
- 헤더 모니터링 및 선제적 제한
// 간단한 속도 제한기
class HerokuRateLimiter {
constructor(requestsPerMinute = 150) {
this.queue = [];
this.interval = 60000 / requestsPerMinute;
this.processing = false;
}
async add(requestFn) {
return new Promise((resolve, reject) => {
this.queue.push({ requestFn, resolve, reject });
this.process();
});
}
async process() {
if (this.processing || this.queue.length === 0) return;
this.processing = true;
while (this.queue.length > 0) {
const { requestFn, resolve, reject } = this.queue.shift();
try {
const result = await requestFn();
resolve(result);
} catch (error) {
reject(error);
}
if (this.queue.length > 0) {
await new Promise(r => setTimeout(r, this.interval));
}
}
this.processing = false;
}
}
프로덕션 배포 체크리스트
배포 전 확인사항:
- [ ] CI/CD에 장기 API 토큰 사용
- [ ] 토큰을 안전하게 비밀관리(Vault, AWS Secrets 등) 저장
- [ ] 속도 제한, 요청 대기열 구현
- [ ] 포괄적 오류처리 및 로깅
- [ ] 다이노 시간 사용량 모니터링
- [ ] 로그 드레인 구성
- [ ] 파이프라인 승격 자동화
- [ ] 자동 인증서 관리(ACM) 활성화
- [ ] 데이터베이스 백업 전략
모니터링 & 경고
const metrics = {
apiCalls: { total: 0, successful: 0, failed: 0, rateLimited: 0 },
dynoHours: { used: 0, quota: 1000 },
deployments: { successful: 0, failed: 0, avg_duration: 0 }
};
const failureRate = metrics.apiCalls.failed / metrics.apiCalls.total;
if (failureRate > 0.05) sendAlert('Heroku API 실패율이 5%를 초과했습니다.');
if (metrics.dynoHours.used > metrics.dynoHours.quota * 0.8) sendAlert('다이노 시간 사용량이 80%를 초과했습니다.');
실제 사용 사례
자동화된 CI/CD 파이프라인
- 과제: 수동 배포로 인한 오류 및 지연
- 해결: GitHub Actions + Heroku API 통합
- 결과: 무중단 배포, 90% 빠른 릴리스
워크플로:
- GitHub push → 워크플로 트리거
- CI에서 테스트
- Heroku API로 빌드 생성
- 환경 간 슬러그 승격
- 성공/실패 알림 전송
다중 환경 관리
- 과제: 환경 간 수동 구성 동기화
- 해결: Heroku API로 중앙 집중 구성 관리
- 결과: 일관된 설정, 주 8시간 절약
주요 자동화:
- 환경별 구성 변수 동기화
- 자동 애드온 프로비저닝
- 클라이언트 온보딩 대량 작업
트래픽 기반 자동 확장
- 과제: 판매 이벤트 중 확장 지연
- 해결: Heroku API로 트래픽 기반 자동 확장
- 결과: 10배 트래픽에도 무중단 서비스
자동화 로직:
- 메트릭 API로 응답 시간 모니터링
- 대기 시간 초과 시 다이노 확장
- 트래픽 저조 시 축소/경고
결론
Heroku API는 클라우드 앱 자동화에 필수입니다.
핵심 요점:
- 토큰 인증은 안전 관리/회전 필수
- 속도 제한(1만/h) 선제 모니터링 필요
- 파이프라인으로 강력한 CI/CD 구현
- 오류처리/로깅으로 배포 안정성 확보
- Apidog은 Heroku 통합 API 테스트를 간단하게 해줍니다.
FAQ 섹션
Heroku API는 무엇에 사용되나요?
Heroku API로 애플리케이션, 다이노, 애드온, 인프라 등을 코드로 관리할 수 있습니다. CI/CD 자동화, 다중 앱 관리, 자동 확장, 모니터링 도구 등에 활용됩니다.
Heroku API 키는 어떻게 얻나요?
Heroku CLI 설치 후 heroku login → heroku authorizations:create 실행. 반환된 토큰을 환경 변수로 저장하세요.
Heroku API는 무료인가요?
API 사용은 무료지만, 다이노/애드온 등 리소스에는 과금됩니다. 시간당 10,000 요청 제한이 있습니다.
Heroku API는 어떤 인증을 사용하나요?
베어러 토큰 인증 사용. 모든 요청에 Authorization: Bearer {api_key} 헤더 필요. 토큰은 단기(1시간)/장기(최대 1년) 선택 가능.
속도 제한은 어떻게 처리하나요?
RateLimit-Remaining 헤더를 모니터링하고, 요청 대기열 및 지수 백오프를 구현하세요. 안전을 위해 분당 150개 이하 요청 권장.
Git 없이도 배포할 수 있나요?
예. 빌드 API를 통해 S3/GCS 등 클라우드 스토리지의 소스 블롭 URL을 지정해 배포할 수 있습니다.
배포 자동화는 어떻게 하나요?
파이프라인 API로 CI/CD를 구축하세요. 빌드 생성, 슬러그 승격, GitHub/자체 CI 연동 등.
릴리스와 빌드의 차이점은?
빌드는 소스코드를 슬러그로 컴파일, 릴리스는 슬러그+구성변수 조합으로 실제 배포 버전을 만듭니다.
실패한 배포 롤백 방법은?
릴리스 API로 릴리스 목록 조회 후, /releases에 rollback: <release_id>로 POST하면 이전 버전으로 복원됩니다.
여러 Heroku 계정도 관리할 수 있나요?
예. 각 계정별 API 토큰을 사용하고, HEROKU_API_KEY 환경 변수 값만 바꾸면 됩니다.

Top comments (0)