n8n DeepL Credentials 오류 해결기
n8n DeepL Credentials 오류 해결기
moseoh
typescript opensource

n8n DeepL Credentials 오류 해결기

moseoh · 2026년 01월 21일

갑자기 멈춘 내 n8n 메일 번역기, 기여 한 번으로 되살리기

평화로운 아침, 당연히 와야 할 메일이 오지 않았습니다.

저는 n8n을 셀프 호스팅해서 매일 아침 받은 메일들을 자동으로 번역해 요약본을 받아보고 있습니다. 그런데 어느 날 아침, 늘 오던 번역 메일이 도착하지 않았더라고요. 확인해보니 잘 돌아가던 워크플로우의 DeepL 노드가 에러를 뱉고 있었습니다.

처음엔 단순히 API 키 만료 문제인 줄 알았는데, 알고 보니 DeepL의 정책 변경이 원인이었습니다.

문제 발견: “어제까진 잘 됐는데 왜?”

n8n 로그를 확인해보니 Forbidden - perhaps check your credentials?라는 메시지가 가득했습니다. 자격 증명을 다시 입력해도 해결되지 않았죠.

Image

원인을 찾기 위해 DeepL API 문서를 뒤져보니, **2025년 11월부터 기존의 쿼리 파라미터 방식(auth_key)을 중단(Deprecated)**하고, 2026년 1월부터는 반드시 HTTP 헤더를 통한 인증만 허용하도록 정책이 바뀌었더라고요.

Image

출처

혹시 이미 패치된 버전이 있지 않을까 싶어, 사용 중이던 latest 이미지 대신 n8n 레포를 직접 클론해 빌드 후 다시 시도해봤습니다. 결과는 동일했어요.

Image

코드 분석: 범인은 어디에 있나

n8n 레포지토리를 클론받아 DeepL 관련 파일을 뒤져봤습니다. packages/nodes-base/credentials/ 디렉토리에서 DeepLApi.credentials.ts를 발견했는데요, 역시나 예전 방식인 qs(Query String)를 사용하고 있었습니다.

// ❌ 기존 코드: 쿼리 파라미터 방식
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
qs: {
auth_key: '={{$credentials.apiKey}}',
},
},
};

지난번 OpenTelemetry 노드를 기여했을 때도 비슷했는데요, “설마 이런 대중적인 툴에서 인증 방식 변화를 놓쳤을까?” 싶었습니다. 하지만 실제로 제 n8n은 멈춰 있었고, 누군가는 고쳐야 할 문제였죠.

수정은 단순했습니다

수정 자체는 간단했습니다. qs를 제거하고 DeepL이 요구하는 Authorization 헤더를 추가하는 것이었죠. 하지만 여기서 한 번 더 디테일이 필요했습니다.

// ✅ 수정 코드: 헤더 기반 인증 적용
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
// n8n 표현식 엔진을 사용해 접두사와 키를 결합
Authorization: '={{ "DeepL-Auth-Key " + $credentials.apiKey }}',
},
qs: {
auth_key: '={{$credentials.apiKey}}',
},
},
};

처음에는 접두사 뒤의 공백 하나 때문에 한참 헤맸습니다.

n8n의 표현식 엔진이 헤더 값을 처리할 때, 결과 문자열 앞에 자동으로 공백을 trim하지 않더라고요. 그래서 제가 "DeepL-Auth-Key " 처럼 공백을 두 개 넣었을 때는 Invalid Authorization header 에러가 떴고, 반대로 공백을 빼면 DeepL-Auth-Keyabc123... 처럼 키와 접두사가 붙어버렸습니다.

결국 접두사와 키 사이에 정확히 공백 1개가 들어가도록 표현식을 다듬어야 했습니다.

Image

AI 리뷰어의 한 마디: 테스트를 추가하세요

PR을 올리자마자 예상치 못한 복병을 만났습니다. cubic 이라는 봇이 나타나 **“테스트 코드가 없으니 규칙 위반”**이라며 엄격하게 경고를 주더군요.

재미있는 점은, 기존 DeepL 자격 증명 파일에는 테스트 코드가 아예 존재하지 않았다는 겁니다. 하지만 n8n의 새로운 정책은 “기존에 없었더라도 고쳤다면 테스트를 만들어라”였죠. 결국 다른 OpenAiApi.credentials.test.ts 코드를 참고해서 테스트 코드를 작성했습니다.

// 새로 추가한 테스트 코드의 일부
it('should have correct header-based authentication format', () => {
const auth = deepLApi.authenticate as any;
expect(auth.properties.headers.Authorization).toBe('={{ "DeepL-Auth-Key " + $credentials.apiKey }}');
});

머지까지 4개월, 오픈소스 기여의 현실

PR을 올린 직후만 해도 “금방 머지되겠지” 싶었습니다. 수정 범위도 작고, DeepL 공식 정책 변경에 대응하는 거라 근거도 명확했으니까요. 그런데 실제 머지까지는 약 4개월이 걸렸습니다. 그 사이에 꽤 흥미로운 일들이 있었어요.

2026년 1월 — PR 제출

별도의 이슈를 만들지 않고 곧바로 PR로 작성해서 올렸습니다. 며칠 만에 다른 사용자들이 👍, 🚀 이모지로 반응해주더라고요. 같은 문제를 겪고 있던 사람들이 꽤 많았던 모양입니다. “곧 머지되겠구나” 싶었습니다.

2026년 2월 초 — 관련 이슈 등록

제 PR과는 별개로, 같은 문제를 보고하는 이슈가 새로 올라왔습니다. 댓글로 “제가 이미 PR을 올려뒀습니다”라고 남겼지만, 이때까지도 PR과 이슈를 정식으로 링크하지는 않았습니다.

(지금 생각해보면 이게 첫 번째 실수였어요.)

2026년 2월 중순 — 중복 PR 등장 🥲

며칠 뒤, 동일한 수정을 하는 또 다른 PR이 올라왔습니다. 코드 변경 내용도 거의 똑같았어요. 솔직히 좀 당황스러웠지만, 공개된 오픈소스에서 누구나 같은 문제를 발견하고 고칠 수 있으니 어쩔 수 없는 일이라고 생각했습니다.

이때 뒤늦게나마 제 PR 설명에 Fixes #25304를 추가해서 이슈와 연결했습니다.

2026년 5월 초 — 누군가의 핑(Ping)

그렇게 시간이 흐르고, 어느 날 제 PR에 모르는 분이 등장해 메인테이너에게 직접 핑을 찍어줬습니다.

@Matsuuu sorry for the ping — could you please help route this PR to the right team/reviewer?

그리고 며칠 뒤, 제 PR이 머지되었습니다. 솔직히 말하면, 이 핑이 없었다면 훨씬 더 묻혔을 거란 생각이 들었어요. 얼굴도 모르는 분이 굳이 시간을 내서 도와준 덕에 빛을 본 셈입니다.

이번 기여를 통해 배운 것들

1. 셀프 호스팅은 기여의 시작점

자신이 직접 사용하는 도구가 고장 났을 때만큼 기여 동기가 확실한 때는 없는 것 같습니다. 덕분에 멈췄던 제 번역 서비스도 다시 활기를 찾았습니다.

2. 오픈소스의 진화하는 규칙

오픈소스 프로젝트들도 점점 관리의 자동화가 고도화되고 있다는 걸 느꼈습니다. AI 봇이 실시간으로 코드 컨벤션과 테스트 누락을 체크해주니, 메인테이너의 손을 빌리기 전에 더 완벽한 코드를 준비하게 되더라고요.

3. 단순한 수정도 가치는 충분하다

인증 방식 하나 바꾸는 작은 수정이었지만, 전 세계의 수많은 n8n 사용자가 DeepL을 다시 사용할 수 있게 해준다는 점에서는 의미 있는 작업이었습니다.

4. 머지는 코드만으로 되지 않는다

좋은 PR을 올리는 것과 그 PR이 머지되는 것은 다른 일이라는 걸 배웠습니다. 이슈 연결, 적절한 라벨링, 그리고 때로는 누군가의 한마디까지 — 머지에는 코드 외적인 요소들이 생각보다 많이 작동하더라고요.

셀프 호스팅으로 도구를 쓰다 보면 언젠가는 반드시 고장이 납니다. 그때가 바로 오픈소스에 기여할 수 있는 가장 좋은 타이밍이더라고요. 비슷한 에러를 만난 분이 있다면, 이슈를 열기 전에 한 번 코드를 들여다보시길 추천드립니다.


PR 링크: fix(deepl): Update credentials to use header-based authentication #24614