매일 아침, 데일리 스탠드업에서 전날 작업 내용을 정리해 보고하는 개발팀이 많습니다. GitLab과 Slack을 사용하는 팀이면 이슈, Merge request(MR), 커밋 링크 등을 Slack으로 공유할 텐데요. 이 정보를 일일이 수집하고, 정리하는 과정은 번거롭습니다. 실수로 빠뜨리는 내용이 있을 수 있고요. 꼼꼼히 준비하려면 시간도 오래 걸리죠.
이 작업을 모두 자동화하면 어떨까요? GitLab에서 전날 작업 내용을 수집하고, 데일리 스탠드업 보고서를 생성해 Slack에 공유하는 과정을 모두 자동화하는 거죠.
최근 인포그랩이 주최한 n8n Korea 밋업에서 한 참가자가 이러한 아이디어를 공유했습니다. 저는 n8n으로 이를 실제 자동화 워크플로로 구현했는데요. 바로 ‘개발팀을 위한 스탠드업 자동화’ 시스템입니다.
이 글에서는 n8n, GitLab API, OpenAI, Slack으로 구축한 스탠드업 자동화 시스템을 소개하려 합니다. 자동화의 필요성부터 예상 효과, 워크플로, 관련 기술 스택, 설계 원칙, 결과까지 자세히 설명하겠습니다.
스탠드업 자동화의 필요성
먼저, 실제 개발팀이 데일리 스탠드업에서 겪는 주요 문제를 살펴보겠습니다.
- 매일 GitLab의 커밋과 이슈를 수동으로 찾아 정리하려면 15~30분이 걸립니다.
- 팀원마다 보고 방식이 달라 정보 누락이나 중복이 자주 발생합니다.
- 팀 규모가 커질수록 이 문제는 더 빈번해지며, 스탠드업 품 질 관리가 점점 더 어려워집니다.
이 문제는 자동화로 크게 개선할 수 있습니다. 스탠드업을 자동화하면 비효율적인 반복 업무와 보고 준비 시간, 수작업에 따른 실수를 줄일 수 있습니다. 아울러 팀 내 의사소통의 정확성과 일관성을 높일 수 있습니다. 그 결과, 협업의 속도와 품질을 향상할 수 있습니다.
스탠드업 자동화 전후 비교
스탠드업을 자동화하면, 다음과 같은 효과를 기대할 수 있습니다.

스탠드업 자동화 워크플로 구조
저는 앞서 정리한 자동화 기대 효과를 기반으로, 다음과 같은 워크플로를 구현했습니다. 전체 흐름은 네 가지 주요 단계로 구성됩니다.
- 일정 기반 트리거: 매일 오전 9시에 실행
- GitLab 브랜치 및 커밋 수집: 날짜 기준으로 필요한 데이터만 추출
- 커밋 → 이슈 매핑 및 AI 요약 처리: 작업 내용 정리 및 GPT 기반 요 약
- Slack 전송 및 주간 보고 저장: 일간/주간 메시지 자동 전송
사용 기술 스택 및 구성 요소
스탠드업 자동화 시스템은 다음과 같은 주요 기술로 구성했습니다.

스탠드업 자동화 설계 원칙
이 프로젝트의 핵심 목표는 단순한 데이터 나열이 아니라, 누구나 쉽게 이해할 수 있도록 일관된 형식으로 정리된 스탠드업 메시지를 안정적으로 자동 생성하는 것이었습니다. 이를 위해 데이터 수집, 중복 제거, 요약 처리, Slack 전송까지 자동화 전 과정을 정교하게 설계했습니다. 각 워크플로 단계는 다음 설계 원칙을 기반으로 구성했습니다.
일정 설정 및 날짜 계산
- 이 워크플로는 매일 오전 9시에 자동 실행됩니다. 월요일에는 지난주 금요일의 작업을 수집합니다.
- 주말(토/일)에는 자동화가 동작하지 않도록 구성합니다.
- 이유: 개발팀은 평일 기준으로 작업하며, 주말에는 활동과 작업 보고가 거의 없습니다. 따라서 주말 실행은 제외합니다.
GitLab 브랜치 순회 및 커밋 수집
- GitLab은 커밋 데이터를 브랜치 기준으로 제공합니다. 따라서 모든 브랜치를 순회하면서 커밋 데이터를 수집하도록 합니다.
- 이때 ‘maxCommitPage’ 설정으로 커밋 데이터 요청 횟수를 제한합니다. 프로젝트에서 불필요한 브랜치 정리도 병행합니다.
- 이유: 모든 브랜치를 순회하지 않으면 default 브랜치만 조회되므로 전체 작업이 누락될 위험이 있습니다. 또 브랜치 수가 많으면 API 요청량이 급증합니다. 이에 커밋 페이징 수(maxCommitPage)를 조정해 ‘Too Many Request 오류’를 예방합니다.
중복 커밋 제거
-
중복된 커밋을 걸러내도록 Set을 활용해 커밋 ID 기준으로 ‘중복 제거’ 작업을 적용합니다. 예시는 다음과 같습니다.
if (!uniqueCommitIds.has(commitId)) {
uniqueCommitIds.add(commitId);
return true;
} -
이유: 여러 브랜치에 동일한 커밋이 존재할 수 있습니다. 따라서 정확한 스탠드업 요약을 위해 중복 커밋을 제거합니다.
커밋과 이슈의 연동 구조
- GitLab 브랜치에 이슈 ID 기반 네이밍(예: issue112-fix-bug) 규칙을 적용합니다.
- Husky를 활용해 커밋 메시지에 ‘이슈 번호 자동 삽입’을 설정합니다. 그 결과, 커밋 메시지에 ‘#123’ 형식으로 이슈 번호가 포함됩니다.
- 이유: GitLab은 커밋과 이슈를 분리해서 관리합니다. 따라서 이슈 번호 삽입으로 두 데이터를 명시적으로 연결합니다.
커밋 메시지에 이슈 번호가 자동 삽입되는 예시

OpenAI 기반 이슈 요약 처리
-
복잡한 이슈 댓글 데이터는 하나의 문자열로 정리한 후, OpenAI에 전달합니다. 예시는 다음과 같습니다.
-
아래는 댓글 데이터입니다.
[
...
{
"id": 2461102933,
"type": "DiscussionNote",
"body": "파란색으로 테마로 변경했습니다.\n\n<Image className="image" src={'https://cdn.infograb.io/insight_prod//uploads/ccf9f3c9ac18018fc4484330ae5176d9/image/blog.png'}/>{width=\"624\" height=\"608\"}",
"author": {
"id": 11918549,
"username": "fabbro1",
"name": "Fabbro",
"state": "active",
"locked": false,
"avatar_url": "https://gitlab.com/uploads/-/system/user/avatar/11918549/avatar.png",
"web_url": "https://gitlab.com/fabbro1"
},
"created_at": "2025-04-22T02:31:17.646Z",
"updated_at": "2025-04-22T02:57:13.461Z",
...
"commands_changes": {}
}
...
] -
아래는 하나의 문자열로 전처리한 내용 입니다.
"- [2025-04-22T02:30:19.539Z] Fabbro: 계산기의 색상은 회색이 좋은 것 같습니다.\n- [2025-04-22T02:30:36.074Z] Fabbro: 네. 회색 스타일로 작업했습니다.\n\n<Image className="image" src={'https://cdn.infograb.io/insight_prod//uploads/4ab0f5f56e908d9531046d9df540e8ad/image/blog.png'}/>{width="779" height="643"}\n- [2025-04-22T02:30:51.541Z] Fabbro: 보니까. 그냥 빨간색이 좋겠네요. 빨간색으로 해주세요.\n- [2025-04-22T02:31:02.141Z] Fabbro: 아니다. 파란색으로 해주세요.\n- [2025-04-22T02:31:09.610Z] Fabbro: 네. 알겠습니다...\n- [2025-04-22T02:31:17.646Z] Fabbro: 파란색으로 테마로 변경했습니다.\n\n<Image className="image" src={'https://cdn.infograb.io/insight_prod//uploads/ccf9f3c9ac18018fc4484330ae5176d9/image/blog.png'}/>{width="624" height="608"}\n- [2025-04-22T02:31:29.572Z] Fabbro: 더하기, 빼 기 기능이 안 되는데요?\n- [2025-04-22T02:31:38.695Z] Fabbro: 이번 이슈에서는 마크업만 하기로해서요.\n\n다음 이슈에서 작업하겠습니다 :)" -
이슈에서 도움 요청 사항을 추출하고, 요약을 동시에 수행하기 위해 다음 AI 프롬프트를 사용합니다.
아래의 JSON은 GitLab 이슈 데이터입니다. 다음 기준에 따라 결과를 반환하세요.
1. `description` 필드에서 `# 도움 요청`으로 시작하는 섹션이 존재한다면, 그 하위 내용을 추출하여 `help` 항목에 문자열로 저장하세요. 정확한 매칭이 없다면 빈 문자열("")을 반환하세요.
2. `description`과 `notes` 필드의 전체 내용을 기반으로, 이슈의 작업 내용, 결정사항, 주요 코멘트 흐름을 **다른 사람이 이해하기 쉬운 서술형 요약**으로 정리하여 `summary` 항목에 작성하세요. 다음을 고려해야 합니다:
- 어떤 작업이 있었는지
- 어떤 이슈가 발생했는지
- 논의 과정 중 어떤 결정이 내려졌는지
- 완료/롤백 여부, 현재 상태는 어떤지 등
- 내용이 길더라도 다른 사람들이 보았을 때 이슈의 흐름, 상황, 결과를 이해하기 쉽게 작성하기.
3. `iid`는 `issueId`, `title`은 `title`로 그대로 반환하세요.
4. 최종 출력은 다음 형식을 따릅니다 (줄바꿈 없이 반환하세요):
`{"issueId": "내용", "title": "내용", "summary": "내용", "help": "내용"}` -
이유: AI가 이슈 본문과 댓글을 정확하게 해석해 스탠드업 내용을 생성하도록 데이터를 전처리해야 합니다. 이를 위해 프롬프트는 단순 텍스트 나열이 아니라 ‘맥락 있는 스탠드업 요약’을 출력하도록 설계해야 합니다. 아울러 JSON 파싱 오류를 방지하도록 줄바꿈을 제거해 반환할 것을 요청해야 합니다.
다양한 옵션 설정
프로젝트별 환경 변수를 설정할 수 있도록 다음과 같이 구성합니다.
- groupAndProjectPath: 프로젝트 경로
- projectId: 프로젝트 ID
- projectName: 프로젝트 이름
- slackTeamChannelUrl: Slack 메시지 전송 채널
- supabaseId: Supabase 테이블 ID
전체 워크플로의 통합 설정이 가능하도록 다음과 같이 구성합니다.
- baseUrl: GitLab API URL
- maxCommitPage: 브랜치별 커밋 요청 횟수 (1페이지당 커밋 100개)
- maxCommitCount: Slack 메시지에 표시할 커밋 링크 개수
- 커밋 최대 글자 수: Slack 메시지에 표시할 커밋의 최대 글자 수
- 이유: 여러 프로젝트에 유연하게 적용하기 위해 환경 변수 기반 설정을 도입합니다. 메시지 형식도 프로젝트 성격에 따라 유동적으로 조절 가능하도록 구성합니다.
maxCommitCount(5)와 커밋 최대 글자수(60) 적용 예시
