웹 서비스 개발 중 글로벌 고객을 위해 다국어 자동 번역 기능을 도입하다가 DeepL API 호출 시 403 Forbidden 에러나 인증 실패를 경험해봤을 것이다. 인공지능 기반 번역 성능이 뛰어나 많은 백엔드 개발자가 DeepL API를 채택하지만, 계정 유형에 따른 엔드포인트 차이나 인증 헤더 규격을 잘못 작성하여 통신에 실패하는 경우가 많다.
이번에는 DeepL REST API의 정확한 인증 구조부터 PHP cURL을 활용한 실시간 다국어 번역 구현, 그리고 자주 발생하는 403 에러의 명확한 해결책까지 완벽하게 정리해서 소개하겠다.
DeepL API는 요청을 보낼 때 API 키를 기반으로 인증을 수행한다. 이때 많은 개발자들이 가장 흔하게 하는 실수가 두 가지 있다. 첫 번째는 API 키의 계정 플랜(Free vs Pro)에 맞지 않는 엔드포인트 URL을 호출하는 것이고, 두 번째는 HTTP 인증 헤더에 standard Bearer 방식을 붙이는 것이다.
DeepL API는 Free 플랜과 Pro 플랜의 도메인 주소가 엄격히 분리되어 있으며, 인증 헤더 역시 전용 포맷을 사용해야 정상 작동한다.
| 항목 | Free 플랜 (DeepL API Free) | Pro 플랜 (DeepL API Pro) |
|---|---|---|
| 호출 엔드포인트 URL | https://api-free.deepl.com/v2/translate | https://api.deepl.com/v2/translate |
| API 키 접두사 형태 | :fx 가 키 끝에 붙음 (예: xxx...xxx:fx) | :fx 없이 일반 문자열로 구성됨 |
| HTTP Authorization 헤더 | DeepL-Auth-Key [YOUR_API_KEY] | DeepL-Auth-Key [YOUR_API_KEY] |
| 월간 제공 글자 수 | 500,000자 무료 제공 | 종량제 (사용량만큼 과금) |
DeepL REST API v2를 PHP 백엔드에서 연동하려면 HTTP POST 요청을 보내야 한다. 요청 본문(Body)은 JSON 규격을 사용하며, 번역할 텍스트 배열(`text`)과 목적 언어 코드(`target_lang`)를 필수로 포함해야 한다.
한국어는 `KO`, 영어는 `EN` 또는 `EN-US`, `EN-GB`, 일본어는 `JA` 등의 언어 코드를 지정할 수 있다.
아래 실전 예제를 통해 403 Forbidden 에러를 유발하는 나쁜 코드와 안정적으로 동작하는 올바른 PHP cURL 처리 코드를 확인해보자.
✗ 잘못된 코드 (인증 헤더 포맷 오류 및 엔드포인트 불일치)
<?php
// 인증 실패를 유발하는 잘못된 예시
$apiKey = "12345678-abcd-efgh-1234-56789abcdef0:fx"; // Free 플랜 API 키
$url = "https://api.deepl.com/v2/translate"; // Pro 엔드포인트를 호출하여 403 Forbidden 발생
$data = [
'text' => ['안녕하세요. 시스템 점검 중입니다.'],
'target_lang' => 'EN'
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
// Bearer 토큰 방식을 사용하여 DeepL 인증 실패 발생
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Authorization: Bearer ' . $apiKey
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response; // {"message":"Forbidden"} 또는 HTTP 403 반환
?>✓ 올바른 코드 (계정별 URL 자동 분기 및 정확한 Authorization 헤더 적용)
<?php
function translateText(array $textList, string $targetLang, string $apiKey): array {
// API 키 끝에 ':fx'가 붙어있는지 확인하여 Free/Pro 엔드포인트 자동 선택
$isFreeAccount = str_ends_with(trim($apiKey), ':fx');
$endpoint = $isFreeAccount
? 'https://api-free.deepl.com/v2/translate'
: 'https://api.deepl.com/v2/translate';
$payload = [
'text' => $textList,
'target_lang' => strtoupper($targetLang)
];
$ch = curl_init($endpoint);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: DeepL-Auth-Key ' . trim($apiKey)
],
CURLOPT_TIMEOUT => 10
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);
if ($curlError) {
throw new Exception("cURL 통신 오류: " . $curlError);
}
if ($httpCode !== 200) {
throw new Exception("DeepL API 오류 (HTTP " . $httpCode . "): " . $response);
}
$result = json_decode($response, true);
return $result['translations'] ?? [];
}
// 사용 예시
try {
$myApiKey = "12345678-abcd-efgh-1234-56789abcdef0:fx";
$sourceTexts = ["안녕하세요. 반가운 개발자입니다.", "실시간 번역 모듈 테스트입니다."];
$translations = translateText($sourceTexts, 'EN', $myApiKey);
foreach ($translations as $item) {
echo "번역 결과: " . $item['text'] . "<br />";
}
} catch (Exception $e) {
echo "에러 발생: " . $e->getMessage();
}
?>
번역 결과: Hello. Nice to meet you, developer.
번역 결과: This is a real-time translation module test.
개발 현장에서 DeepL API 연동 중 발생하기 쉬운 예외 상황들을 미리 파악해두면 디버깅 시간을 크게 줄일 수 있다.
- 인증 헤더 포맷 오류 (HTTP 403): 일반적인 OAuth2 토큰처럼 `Authorization: Bearer KEY`로 전송하면 인증에 실패한다. 반드시 `Authorization: DeepL-Auth-Key KEY` 형태로 작성해야 한다.
- 엔드포인트 미스매치 (HTTP 403): 무료 API 키(`:fx`로 끝나는 키)를 `api.deepl.com`에 요청하면 권한 없음 에러가 발생한다. 키 포맷에 맞춰 `api-free.deepl.com`으로 분기 처리해야 한다.
- 텍스트 인코딩 불일치: 멀티바이트 한글이나 특수문자가 포함된 문장을 전송할 때 PHP 파일의 인코딩이 UTF-8이 아니면 JSON 파싱 이상이나 400 Bad Request가 발생할 수 있다. 항상 UTF-8 인코딩을 유지해야 한다.
DeepL API 연동은 계정 플랜에 맞는 엔드포인트 구분과 올바른 Authorization 헤더 규격만 지키면 안정적으로 고성능 번역 서비스를 구축할 수 있다. 작은 최적화와 예외 처리 습관이 모여 서비스 전체의 신뢰도를 향상시킨다는 점을 잊지 말자. 이 글의 자동 분기 cURL 모듈 코드를 참고해서 백엔드 서비스에 적용해보면, 오류 없는 완벽한 다국어 번역 시스템을 얻을 수 있을 것이다.