n8n + Gemini API로 GitLab MR 자동 리뷰봇 만들기: 503 high demand 에러를 시간대별 분기로 우회한 기록

 

최근 팀 프로젝트를 진행하면서 MR 리뷰를 조금 더 효율적으로 해보고 싶어서 n8n으로 리뷰봇을 세팅해뒀다.
이 부분은 혜원 코치님께서 공유해주신 n8n 세션 자료를 참고했다. 감사합니다...

https://github.com/ssafy-project-coach/coach-session-15-n8n/tree/master?tab=readme-ov-file

구조는 단순하다.
팀원들이 GitLab에 MR을 올리면, n8n이 MR 변경사항을 가져오고, Gemini API를 통해 코드 리뷰 코멘트를 생성한 뒤, 다시 GitLab MR 댓글로 남겨주는 방식이다.

처음에는 생각보다 잘 돌아갔다.
MR 제목, 설명, 브랜치 정보, 변경된 파일 목록, 실제 diff 내용을 Gemini에게 넘기면 꽤 자연스럽게 리뷰 코멘트를 작성해줬다.
특히 단순히 “좋습니다” 같은 형식적인 리뷰가 아니라, 실제 코드 변경사항을 보고 중요한 이슈 위주로 2~3개 정도 짚어주는 식으로 프롬프트를 작성해두니 팀 코드 리뷰 보조용으로는 충분히 쓸 만했다.

그런데 문제가 하나 있었다.
평소에는 무료 Gemini API만으로도 충분히 잘 돌아가는데, 이상하게 오후 5시부터 6시 사이에만 리뷰봇이 자주 실패했다.

그렇다.
내 Gemini에게는 퇴근 자아가 있었다.

무료 API로 계속 일을 시키다 보니, 어느 순간 본인이 열정페이를 하고 있다는 사실을 자각한 것 같다.
평소에는 멀쩡히 리뷰를 달아주다가도 오후 5시만 되면 갑자기 퇴근 준비를 시작했다.
처음에는 “아니 AI가 무슨 퇴근을 해...” 싶었는데, 실행 로그를 보니 진짜였다.

Gemini API에서 내려오는 에러 메시지는 대략 이런 식이었다.

Service unavailable - try again later
This model is currently experiencing high demand.
Spikes in demand are usually temporary.
Please try again later.

에러 코드는 503 Service Unavailable이었다.
즉, 내가 요청을 잘못 보냈다기보다는 해당 시간대에 Gemini 모델 사용량이 몰려서 일시적으로 응답을 받지 못하는 상황에 가까웠다.

그래서 바로 유료 결제를 해야 하나 고민했다.
그런데 하루 종일 안 되는 것도 아니고, 딱 5시~6시 사이에만 문제가 생겼다.
무료 API 자체는 대부분의 시간대에서 충분히 잘 돌아가고 있었기 때문에, 이 문제 하나 때문에 바로 유료로 전환하기에는 조금 애매했다.

결국 “결제부터 하기 전에, 일단 n8n 워크플로우에서 이 시간대만 다르게 처리해보자”는 방향으로 수정해보기로 했다.


기존 리뷰봇 구조

처음 만든 리뷰봇의 구조는 단순했다.

When Executed by Another Workflow
→ Get MR Changes
→ MR Preprocessing
→ Set MR Info
→ AI Agent
→ Write Total Comment

전체 흐름은 다음과 같다.

먼저 부모 워크플로우에서 MR 이벤트가 발생하면 이 리뷰봇 워크플로우가 실행된다.
이 워크플로우는 When Executed by Another Workflow 노드를 통해 입력을 받고, GitLab API를 호출해서 MR의 변경사항을 가져온다.

그다음 MR Preprocessing 코드 노드에서 리뷰할 필요가 없는 파일들을 필터링한다.
예를 들어 이미지, 영상, 압축 파일, 폰트 파일, 빌드 결과물, 바이너리 파일 등은 코드 리뷰 대상에서 제외했다.
리뷰봇이 .png, .svg, .pdf, .zip, node_modules, dist, build 같은 파일까지 검토하려고 하면 토큰도 낭비되고 리뷰 품질도 떨어지기 때문이다.

이후 Set MR Info 노드에서는 Gemini에게 넘길 MR 정보를 정리한다.
여기서는 project_id, iid, title, description, source_branch, target_branch, changes 같은 값을 하나로 모아서 뒤쪽 AI Agent가 사용하기 쉽게 정리했다.

마지막으로 AI Agent가 Gemini Chat Model을 사용해서 리뷰 내용을 생성하고, Write Total Comment 노드가 GitLab MR 댓글 API를 호출해서 리뷰 코멘트를 남긴다.


Gemini에게 넘기는 프롬프트

AI Agent에는 MR 정보를 바탕으로 리뷰 코멘트를 작성하도록 프롬프트를 넣어두었다.

구조는 대략 이런 식이다.

다음 MR을 검토하고, 코드 리뷰 코멘트를 작성해주세요.

# MR 정보:
- 제목
- 설명
- 대상 브랜치
- 소스 브랜치

# 변경된 파일:
- 변경 파일 목록

# 실제 변경사항:
- 각 파일의 diff 내용

그리고 system message에는 리뷰 스타일을 따로 지정했다.

내가 원했던 리뷰 스타일은 너무 딱딱한 보고서식 리뷰가 아니라, 같은 팀원이 남기는 자연스러운 코드 리뷰였다.
그래서 다음과 같은 조건을 넣었다.

당신은 10년차 개발자입니다.
같은 팀의 동료가 제출한 MR을 리뷰합니다.

작성 가이드라인:
- 전체 코드를 검토한 후 정말 중요한 이슈만 2~3개 선별해서 리뷰하세요.
- 마크다운 형식은 사용하지 마세요.
- 파일별 구분이나 섹션 구분 없이 일반 텍스트로 작성하세요.
- 자연스러운 대화체로 작성하세요.
- 한 문단은 너무 길지 않게 작성하세요.
- 가벼운 인사말로 시작하세요.
- 마지막에는 다른 팀원들도 토론에 참여할 수 있는 열린 질문으로 마무리하세요.
- 이 리뷰는 참고용이며 반드시 직접 검증해야 한다는 안내 문구를 포함하세요.

이렇게 설정한 이유는, 자동 리뷰봇이 너무 기계적으로 보이면 팀원들이 거부감을 느낄 수 있기 때문이다.
실제로 코드 리뷰는 “정답을 주는 것”보다 “검토할 만한 지점을 알려주는 것”에 가깝다고 생각했다.
그래서 리뷰봇도 단정적으로 말하기보다는, 중요한 부분을 짚고 팀원들이 다시 확인할 수 있도록 유도하는 형태로 만들었다.


문제 상황: 특정 시간대에만 Gemini 503 에러 발생

처음에는 무료 Gemini API를 쓰고 있었는데, 사용량 자체는 크게 문제가 없었다.
하루에 MR이 20개 정도 올라온다고 해도, Gemini 2.5 Flash 기준으로 보면 비용이 엄청 크게 나올 정도의 사용량은 아니었다.
그리고 실제로 대부분의 시간대에는 무료로도 리뷰봇이 잘 동작했다.

문제는 오후 5시~6시 사이였다.

이 시간대에 MR을 올리면 n8n 실행 로그에서 Gemini Chat Model 노드가 실패했다.
에러를 확인해보니 Service Unavailable이었고, 메시지에 high demand가 포함되어 있었다.

처음에는 이 문제를 보고 유료 결제를 고민했다.
GCP 크레딧을 쓰거나 Gemini API를 유료로 전환하면 해결될까 싶었다.
그런데 생각해보니 이건 무료 사용량 초과라기보다는 모델 서버가 특정 시간대에 바빠서 발생하는 문제에 가까웠다.

만약 rate limit 문제라면 보통 요청 횟수나 토큰 제한을 줄이거나 유료 전환으로 해결할 수 있다.
하지만 지금처럼 high demand로 인한 503은 유료 전환을 한다고 해서 무조건 해결된다고 보장하기 어렵다.
그리고 실제로 우리 팀은 하루 종일 리뷰봇을 쓰는 것이 아니라, 특정 시간대에 MR이 몰리는 패턴이 있었다.

그래서 결론은 이랬다.

유료 결제 전에,
5시~6시에만 요청을 늦게 보내거나 재시도하도록 n8n 워크플로우를 수정해보자.

해결 방향

처음에는 단순히 AI Agent 노드의 Retry On Fail만 켜면 될 줄 알았다.
하지만 직접 설정해보니 n8n Cloud 환경에서는 Wait Between Tries 값을 크게 줄 수 없었다.
내 화면에서는 최대가 5000ms, 즉 5초 정도로 보였다.

그런데 Gemini의 503 high demand 문제는 5초 뒤에 바로 해결되는 유형이 아니다.
모델이 몰리는 시간대에는 몇 분 정도 기다렸다가 다시 보내야 성공할 가능성이 높다.

그래서 최종적으로는 다음 방식으로 수정했다.

5시~6시 사이에 들어온 MR이면
→ 바로 Gemini에 보내지 않고
→ 3~10분 정도 랜덤 대기
→ 그 후 재시도 설정이 켜진 AI Agent로 리뷰 생성

그 외 시간대면
→ 기존 AI Agent로 바로 리뷰 생성

즉, 모든 MR을 늦게 처리하는 것이 아니라, 문제가 자주 발생하는 시간대만 별도로 분기했다.


수정 후 워크플로우 구조

수정 후 구조는 다음과 같다.

When Executed by Another Workflow
→ Get MR Changes
→ MR Preprocessing
→ Set MR Info
→ If
   ├─ true  → Wait → AI Agent 5시 6시 전용 → Write Total Comment
   └─ false → AI Agent → Write Total Comment

기존에는 Set MR Info 다음에 바로 AI Agent로 넘어갔다.
수정 후에는 그 사이에 If 노드를 추가했다.

이 If 노드는 현재 시간이 한국 시간 기준으로 17시 이상 18시 미만인지 확인한다.
조건이 true면 5시~6시 사이이므로 위쪽 경로로 간다.
위쪽 경로에서는 Wait 노드를 통해 3~10분 정도 랜덤하게 기다린 뒤, 재시도 설정이 켜진 전용 AI Agent를 실행한다.

조건이 false면 평상시이므로 아래쪽 경로로 간다.
아래쪽 경로는 기존 AI Agent를 그대로 사용한다.


If 노드 설정

먼저 Set MR Info 뒤에 If 노드를 추가했다.

처음에는 Set MR Info를 수정해야 하나 헷갈렸는데, 실제로는 Set MR Info 자체는 그대로 둬도 됐다.
Set MR Info는 MR 정보를 정리하는 역할만 하고, 시간대 분기는 그 다음 단계에서 처리하는 것이 더 깔끔했다.

If 노드에는 다음 expression을 넣었다.

{{ $now.setZone('Asia/Seoul').hour >= 17 && $now.setZone('Asia/Seoul').hour < 18 }}

이 조건의 의미는 간단하다.

현재 시간을 Asia/Seoul 기준으로 바꾼다.
현재 시간이 17시 이상인지 확인한다.
현재 시간이 18시 미만인지 확인한다.
둘 다 만족하면 true를 반환한다.

즉, 한국 시간 기준으로 17:00 ~ 17:59 사이이면 true가 된다.

If 노드에서는 이 expression 결과가 true와 같은지 비교하도록 설정했다.
그리고 Convert types where required 옵션도 켜두었다.
이 옵션을 켜둔 이유는 expression 결과가 boolean이고 오른쪽 값이 문자열처럼 처리될 수도 있어서, 타입 비교에서 꼬이지 않게 하기 위해서다.

최종 조건은 이렇게 보면 된다.

{{ $now.setZone('Asia/Seoul').hour >= 17 && $now.setZone('Asia/Seoul').hour < 18 }}
is equal to
true

Wait 노드 설정

5시~6시 사이에 들어온 요청은 바로 Gemini로 보내지 않고, 먼저 Wait 노드를 거치게 했다.

Wait 노드는 If 노드의 true 경로에만 연결했다.

If true
→ Wait
→ AI Agent 5시 6시 전용

Wait 설정은 다음과 같이 했다.

Resume: After Time Interval
Wait Amount: Expression
Wait Unit: Seconds

Wait Amount에는 아래 expression을 넣었다.

{{ Math.floor(Math.random() * 420) + 180 }}

이 expression은 180초부터 약 600초 사이의 값을 랜덤으로 만든다.
즉, 대략 3분~10분 정도 랜덤 대기하게 된다.

굳이 랜덤 대기로 설정한 이유는, 5시~6시에 MR이 여러 개 동시에 올라올 수 있기 때문이다.
만약 모든 요청을 똑같이 3분 뒤에 다시 보내면, 3분 뒤에 Gemini 요청이 또 한 번에 몰릴 수 있다.
그래서 각 실행마다 랜덤하게 대기 시간을 다르게 주었다.

예를 들어 어떤 MR은 3분 뒤에 실행되고, 어떤 MR은 7분 뒤에 실행되고, 어떤 MR은 9분 뒤에 실행된다.
이렇게 하면 Gemini API 호출이 한 시점에 몰리는 것을 조금이나마 줄일 수 있다.

실제로 설정 화면에서 expression 결과가 477처럼 보일 수 있다.
이건 현재 테스트 시점에 계산된 랜덤 값이다.
477초면 약 7분 57초 정도 대기한다는 뜻이다.


AI Agent를 두 개로 나눈 이유

처음에는 기존 AI Agent에 그냥 Retry 설정을 켜려고 했다.
하지만 그렇게 하면 모든 시간대의 리뷰가 같은 방식으로 재시도된다.

평소에는 리뷰봇이 잘 동작하고 있었기 때문에, 모든 시간대에 재시도와 대기를 걸 필요는 없었다.
문제가 되는 시간대는 5시~6시뿐이었다.

그래서 AI Agent를 두 개로 나누었다.

AI Agent
→ 평상시용

AI Agent 5시 6시 전용
→ 5시~6시 전용
→ Retry On Fail 설정 ON

위쪽의 AI Agent 5시 6시 전용은 If true 경로에 연결했다.
아래쪽의 기존 AI Agent는 If false 경로에 연결했다.


5시~6시 전용 AI Agent 설정

AI Agent 5시 6시 전용에는 Retry On Fail을 켰다.

설정값은 다음과 같이 했다.

Retry On Fail: ON
Max Tries: 5
Wait Between Tries: 5000ms
On Error: Stop Workflow

처음에는 Wait Between Tries를 60000ms로 주고 싶었다.
1분 간격으로 재시도하면 더 안정적일 것 같았기 때문이다.
하지만 내 n8n 화면에서는 5000ms가 최대처럼 보였다.

그래서 긴 대기는 앞의 Wait 노드가 담당하고, AI Agent의 Retry On Fail은 짧은 보조 재시도 역할로만 사용하기로 했다.

정리하면 역할은 이렇게 나뉜다.

Wait 노드
→ 5시~6시 요청을 3~10분 정도 늦게 보내는 역할

AI Agent Retry On Fail
→ 그래도 Gemini가 한 번 튕겼을 때 짧게 다시 시도하는 역할

이렇게 해두면 5시~6시에 바로 Gemini API를 호출하지 않고, 먼저 랜덤 대기를 탄 뒤 실행된다.
그리고 실행 시점에도 일시적인 실패가 나면 최대 5번까지 짧게 재시도한다.


기존 AI Agent는 그대로 유지

평상시용 AI Agent는 기존 설정을 유지했다.
Retry도 굳이 켜지 않았다.

이유는 간단하다.
평소에는 문제가 없었기 때문이다.

모든 시간대에 Retry를 켜두면 실패 상황에서 실행 시간이 불필요하게 길어질 수 있다.
또한 정상적으로 처리되어야 할 MR도 늦게 댓글이 달릴 수 있다.
그래서 문제가 있는 시간대에만 별도의 처리 경로를 만들고, 나머지 시간에는 기존처럼 빠르게 처리되도록 두었다.

아래쪽 경로는 다음처럼 동작한다.

If false
→ AI Agent
→ Write Total Comment

즉, 5시~6시가 아닌 시간에는 Wait 없이 바로 리뷰를 생성하고 GitLab MR에 댓글을 남긴다.


Google Gemini Chat Model 설정

Gemini 모델은 models/gemini-2.5-flash를 사용했다.
Temperature는 0.2로 설정했다.

코드 리뷰봇의 경우 너무 창의적인 답변보다는 안정적인 답변이 필요하다고 생각했다.
그래서 temperature를 낮게 설정했다.

리뷰봇이 너무 과하게 상상해서 없는 문제를 지적하면 오히려 팀원에게 혼란을 줄 수 있다.
그래서 가능한 한 실제 diff를 기반으로 차분하게 리뷰하도록 유도했다.

Model: models/gemini-2.5-flash
Sampling Temperature: 0.2

 

여기서 중요한 점은, 내 n8n 화면에서는 Google Gemini Chat Model 노드의 Settings에는 Retry 설정이 보이지 않았다는 것이다.
처음에는 여기서 Retry를 설정하려고 했는데, 실제로는 Notes 관련 설정만 보였다.

그래서 Retry는 Gemini Chat Model 노드가 아니라, 이 모델을 사용하는 AI Agent 노드에서 처리했다.


Redis Chat Memory 설정

리뷰봇에는 Redis Chat Memory도 연결했다.
세션 키는 MR 단위로 구분되도록 설정했다.

n8n-rev-{project_id}-{iid}

이렇게 하면 프로젝트와 MR 번호 기준으로 세션이 나뉜다.
즉, 서로 다른 MR의 대화 컨텍스트가 섞이지 않게 된다.

사실 MR 리뷰봇은 한 번 입력받고 한 번 댓글을 남기는 구조라서, Memory가 반드시 필요한 것은 아니다.
하지만 같은 MR에 대해 여러 번 실행하거나, 추후 리뷰 개선 흐름을 붙일 가능성을 생각해서 유지했다.

다만 실제 운영에서는 Memory가 불필요하다고 판단되면 제거해도 된다.
특히 리뷰봇이 항상 독립적인 MR diff만 보고 답변하면 되는 구조라면, Memory 없이 더 단순하게 운영하는 것도 가능하다.


GitLab 댓글 작성 노드

마지막으로 Write Total Comment 노드에서는 GitLab API를 호출해서 MR에 댓글을 남긴다.

요청 방식은 POST이고, URL은 다음 구조다.

/projects/{project_id}/merge_requests/{iid}/notes

본문에는 AI Agent가 생성한 리뷰 결과를 넣었다.

{
  "body": "{{ $json.output }}"
}

즉, Gemini가 생성한 리뷰 텍스트가 그대로 MR 댓글로 작성된다.

여기서 중요한 점은 GitLab API 토큰이나 Credential 정보가 노출되지 않도록 해야 한다는 것이다.
n8n에서는 Credential을 별도로 관리할 수 있기 때문에, 워크플로우 안에 직접 토큰을 적지 않았다.
이 부분은 공개 블로그에 올릴 때도 반드시 가리는 것이 좋다.


최종 구조 정리

최종적으로 완성된 구조는 다음과 같다.

When Executed by Another Workflow
→ Get MR Changes
→ MR Preprocessing
→ Set MR Info
→ If
   ├─ true
   │   → Wait
   │   → AI Agent 5시 6시 전용
   │   → Write Total Comment
   │
   └─ false
       → AI Agent
       → Write Total Comment

정리하면 역할은 이렇게 나뉜다.

노드역할

When Executed by Another Workflow 부모 워크플로우에서 실행될 때 입력 받기
Get MR Changes GitLab API로 MR 변경사항 가져오기
MR Preprocessing 리뷰할 필요 없는 파일 필터링
Set MR Info AI Agent가 사용할 MR 정보 정리
If 현재 시간이 5시~6시인지 판단
Wait 5시~6시 요청을 3~10분 랜덤 대기
AI Agent 5시 6시 전용 5시~6시 전용 리뷰 생성, Retry 설정 포함
AI Agent 평상시 리뷰 생성
Write Total Comment GitLab MR에 리뷰 댓글 작성

테스트할 때 주의한 점

이런 시간 조건 분기 워크플로우는 테스트할 때 조금 헷갈릴 수 있다.
왜냐하면 현재 시간이 5시~6시가 아니면 true 경로가 아니라 false 경로로 가기 때문이다.

그래서 테스트할 때는 임시로 If 조건을 바꿔서 true 경로가 잘 동작하는지 확인했다.
예를 들어 현재 시간이 14시라면, 테스트용으로 조건을 잠깐 이렇게 바꿀 수 있다.

{{ $now.setZone('Asia/Seoul').hour >= 14 && $now.setZone('Asia/Seoul').hour < 15 }}

이렇게 하면 현재 시간대에 true 경로를 강제로 테스트할 수 있다.
테스트가 끝나면 반드시 다시 원래 조건으로 돌려놓아야 한다.

원래 조건은 다음이다.

{{ $now.setZone('Asia/Seoul').hour >= 17 && $now.setZone('Asia/Seoul').hour < 18 }}

또 n8n에서 특정 노드를 단독 실행할 때 No input data가 뜨는 경우가 있다.
이럴 때는 앞 노드가 실행되지 않아서 입력 데이터가 없는 상태일 수 있다.
해당 노드 왼쪽에 Execute previous nodes 버튼이 보이면, 이전 노드들을 먼저 실행해서 입력 데이터를 만들어줘야 한다.

[사진 삽입: Execute previous nodes가 보이는 화면]


공개 글 작성 시 가려야 할 정보

n8n 설정 화면을 캡처해서 블로그에 올릴 때는 민감한 정보가 노출되지 않도록 주의해야 한다.

특히 아래 정보는 반드시 가리는 것이 좋다.

- n8n workflow URL
- execution URL
- Credential 이름
- API Key
- GitLab token
- GitLab 프로젝트 ID
- 실제 MR 제목과 설명
- 실제 코드 diff
- 팀 내부 도메인
- 개인 계정 정보

나도 블로그에 올릴 캡처에서는 Credential, API URL, 프로젝트 식별 정보는 가리고 올릴 예정이다.
자동화 설정 글은 공유하면 도움이 되지만, 실제 인증 정보가 노출되면 보안 문제가 생길 수 있기 때문에 이 부분은 꼭 조심해야 한다.


유료 전환 대신 워크플로우 수정부터 한 이유

처음에는 Gemini API를 유료로 전환해야 하나 고민했다.
하루에 MR이 20개 정도 올라오고 한 달 동안 사용한다고 하면, Gemini 2.5 Flash 기준으로 비용이 엄청 크지는 않을 수 있다.
하지만 이번 문제는 사용량 전체가 부족해서 생긴 문제라기보다는, 특정 시간대에만 high demand가 발생하는 문제였다.

그래서 바로 유료 결제를 하기보다는, 먼저 워크플로우 레벨에서 해결할 수 있는 방법을 적용해보기로 했다.

이번 수정의 핵심은 다음과 같다.

문제가 생기는 시간대만 분기한다.
해당 시간대에는 바로 요청하지 않고 랜덤 대기한다.
그래도 실패할 수 있으니 Retry를 켠 별도 Agent를 사용한다.
평상시에는 기존 흐름을 그대로 사용한다.

이 방식의 장점은 불필요한 비용을 늘리지 않으면서도, 실제 문제가 발생하는 시간대만 안정성을 높일 수 있다는 점이다.
특히 팀 프로젝트처럼 MR이 특정 시간대에 몰리는 경우에는 단순히 유료 결제보다 이런 요청 분산 처리가 더 현실적인 해결책일 수 있다.


작업하면서 느낀 점

이번 설정을 하면서 n8n이 단순히 API를 연결하는 도구가 아니라, 조건 분기와 예외 처리를 잘 구성하면 꽤 실용적인 자동화 도구가 될 수 있다는 걸 느꼈다.

처음 리뷰봇을 만들 때는 “MR이 올라오면 Gemini로 리뷰하고 댓글 달기” 정도만 생각했다.
그런데 실제로 운영해보니 API가 항상 안정적으로 응답하지는 않았고, 특정 시간대나 네트워크 상황에 따라 실패할 수 있었다.
결국 자동화는 한 번 연결했다고 끝나는 게 아니라, 실패했을 때 어떻게 처리할지까지 같이 설계해야 한다는 걸 알게 됐다.

특히 이번에는 유료 결제라는 선택지도 있었지만, 바로 비용을 쓰기 전에 현재 실패 원인을 먼저 확인했다.
502인지 503인지, rate limit인지 high demand인지, 특정 시간대에만 발생하는지 등을 확인해보니 단순 과금 문제로 보기 어려웠다.
그래서 시간대 분기, 랜덤 대기, 재시도 설정을 조합하는 방식으로 해결 방향을 잡았다.

아직 완벽한 해결이라고 보기는 어렵다.
5시~6시에 Gemini 서버 자체가 계속 바쁘면 Wait와 Retry를 넣어도 실패할 수 있다.
하지만 적어도 매번 수동으로 다시 실행해야 하는 상황은 줄일 수 있을 것으로 기대한다.

추후에도 계속 실패가 발생하면 다음 단계로는 fallback 모델을 추가해볼 수 있을 것 같다.
예를 들어 1차는 gemini-2.5-flash로 시도하고, 실패하면 gemini-2.5-flash-lite 같은 더 가벼운 모델로 한 번 더 시도하는 방식이다.
또는 실패했을 때 Mattermost나 GitLab에 “리뷰 생성 실패, 수동 확인 필요” 같은 알림을 남기는 것도 가능할 것 같다.

이번 글은 일단 무료 Gemini API를 최대한 활용하면서, n8n 워크플로우만으로 특정 시간대 503 문제를 완화해본 기록이다.


최종 정리

이번에 적용한 해결 방식은 다음과 같다.

문제:
오후 5시~6시에 Gemini API가 503 high demand 에러를 자주 반환함

원인 추정:
특정 시간대 Gemini 모델 사용량 증가로 인한 일시적 과부하

처음 고민한 해결책:
Gemini API 유료 전환

최종 선택:
유료 전환 전에 n8n에서 시간대 분기 + 랜덤 Wait + Retry 적용

적용 결과:
5시~6시 사이 요청만 별도 경로로 보내고,
3~10분 랜덤 대기 후 재시도용 AI Agent를 실행하도록 수정

결과적으로 워크플로우는 다음처럼 바뀌었다.

기존:
Set MR Info → AI Agent → Write Total Comment

수정:
Set MR Info
→ If
   ├─ true  → Wait → AI Agent 5시 6시 전용 → Write Total Comment
   └─ false → AI Agent → Write Total Comment

이번 작업을 통해 느낀 건, AI API를 활용한 자동화는 단순히 “모델을 연결하는 것”보다 “실패했을 때 어떻게 복구할 것인지”가 훨씬 중요하다는 점이다.
특히 팀 프로젝트에서 쓰는 자동화라면, 한 번 실패할 때마다 사람이 수동으로 다시 실행해야 하는 구조는 금방 불편해진다.
그래서 작은 자동화라도 시간대 분기, 재시도, 대기, fallback 같은 안정화 로직을 같이 넣어두는 게 좋다고 느꼈다.

일단은 이 구조로 며칠 더 사용해보고, 5시~6시 실패율이 얼마나 줄어드는지 확인해볼 예정이다.
그래도 계속 실패한다면 그때는 유료 전환이나 fallback 모델 추가까지 고려해볼 생각이다.

myoskin