셸 스크립트를 몰라도 자동화는 하고 싶어, ChatGPT를 활용한 git flow 관리 스크립트 자동화 진행기

Mar.21.2024 성시형

AI Programming General

들어가며

본 글은 OpenAI의 GPT-4(이하 ‘ChatGPT’)를 활용하여 git 관리 스크립트를 작성한 경험을 다루는 수기입니다.

아래 주제에 대해선 다루지 않습니다.

  • 시중에 출시된 대규모 언어 모델(Large Language Model) 간의 비교분석
  • 효과적인 프롬프트 엔지니어링
  • 기타 우리의 인생을 휘리릭 바꿔줄 AI Magic

대신 아래의 주제에 대해서 다룹니다.

  • 자동화는 하고 싶지만 IDE 밖으로 벗어나기는 싫어하던 개발자의 ChatGPT와의 협업 이야기

지금부터 시작합니다.

화제의 ChatGPT

다양한 IDE 제작으로 유명한 JetBrains사의  2023년도 개발자 에코시스템 현황에 따르면 ChatGPT를 사용해 본 개발자의 비율은 77%입니다. 이를 증명하듯, 주말에 방문한 카페 혹은 공유 오피스에선 ChatGPT를 화면 한편에 띄워놓은 개발자를 심심치 않게 찾아볼 수 있습니다.

저도 SQL, 정규표현식, 익숙지 않은 오픈소스에 대한 사용법, 새롭게 익힌 프로그래밍 언어의 문법 등에 대한 프롬프트(prompt, 대화창)로 ChatGPT의 대화 기록이 가득 차 있습니다.

2023년 필자의 질문 중 일부 발췌

하지만 ChatGPT를 회사의 업무에 즉시 적용할 만한 프롬프트를 작성하는 것이 쉽지 않았습니다.

ChatGPT의 도움을 받으려면 본인이 처한 상황을 글로 풀어내야 하기 때문입니다.

그래서 대부분의 질문은 단편적인 질문(예. ‘gradle dependencies에서 implementation와 api의 차이점이 뭔가요?’)을 주고받는 것에 그치고 있었습니다.

그러던 어느 날…

자동화를 결심한 계기

때는 2023년 9월경, 팀 내 운영 업무 중 git 백포팅(backporting) 업무가 진행되는 것을 보고 있었습니다.

💡 백포팅은 팀 내 git 관리 맥락에선 배포된 릴리즈 변경 사항을 아직 출시되지 않았으나 미리 생성된 릴리즈 브랜치에 반영하는 작업을 뜻합니다.

이 작업은 개발자가 터미널에 비슷한 형식의 git 커맨드를 반복 입력해야 하는 작업입니다. 어떤 종류의 작업인지 이해하려면 저희 팀의 git 브랜치 전략과 백포팅(backporting) 전략에 대한 사전지식이 필요합니다.

사전지식 1 – 팀의 git 브랜치 전략

저희 팀은 git flow를 변형하여 활용하고 있습니다.

  1. 여러 날짜의 배포 일자가 정해져 있고, 각 날짜에 해당하는 배포(release) 브랜치가 생성되어 있습니다.

  2. 각 기능(feature) 브랜치는 배포 브랜치로부터 생성됩니다.

  3. 기능 구현이 완료되면 생성되었던 배포 브랜치에 병합(merge)됩니다.

  4. 여러 기능이 배포 브랜치에 병합되고 나면 베타 배포 및 운영 배포를 기다리게 됩니다.

아래 이미지와 같은 구성입니다.

3개의 배포 브랜치


1월 8일, 1월 15일, 1월 21일 세 날짜에 운영 배포가 예정되어 있습니다.
배포 브랜치에 병합된 기능들은 각 날짜에 운영 배포됩니다.

운영배포일 배포 될 기능 목록
1월 8일 feat 1, feat 2
1월 15일 feat 3
1월 21일 feat 4, feat 5

위와 같은 구성은 QA(Quality Assurance, 전문 인력에 의한 기능 테스트) 일정을 순차적으로 구성할 경우 그럭저럭 잘 동작합니다.

그러나 때때로 1월 15일 배포본과 1월 8일 배포본을 동시에 QA 해야 하는 상황도 발생합니다.

각 1주일씩 QA가 진행되는 배포 브랜치


하지만 저희 팀에서 QA를 위해 제공되는 개발 환경은 하나의 브랜치만을 배포해 둘 수 있는 단일 환경입니다.
서로 다른 배포 브랜치의 기능들을 모두 테스트하려면 QA 시간에 맞추어 그때그때 배포 브랜치를 전환하여 배포해 주어야 하는 어려움이 있습니다.

사전지식 2 – 백포팅 전략 (a.k.a 기차놀이)

QA와 관련된 git 브랜치 전략의 한계를 극복하기 위해서, 저희 팀은 앞쪽 날짜 배포 브랜치에 새로운 기능이 병합된 경우 뒤쪽 날짜 브랜치에서 앞 날짜 브랜치를 즉시 리베이스(rebase)하는 백포팅 전략을 사용하고 있습니다.

도식으로 표현하면 아래와 같습니다.

  1. feat 1과 feat 2가 ‘release 0108’ 브랜치에 병합되었습니다. feat 1과 feat 2를 ‘release 0115’ 브랜치에도 반영하기 위해 ‘release 0115’ 브랜치에서 ‘release 0108’ 브랜치를 리베이스합니다.

  2. 1번이 완료되면 다음 날짜 배포 브랜치(‘release 0122’)로 이동하여 직전 날짜 배포 브랜치를 다시 리베이스합니다.

    앞 쪽 배포 날짜를 기반으로 리베이스


  3. 2번 과정을 배포 브랜치 개수만큼 반복하면 최후의 배포 브랜치엔 모든 기능이 병합됩니다. 가장 뒤 날짜의 배포 브랜치를 QA 환경에 배포합니다.

    백포팅 완료!


이렇게 이루어지는 일련의 백포팅 과정은 기차의 앞 칸에서 뒤 칸으로, 연쇄적으로 일어나는 것처럼 느껴졌기 때문에 팀 내에선 ‘기차놀이’라는 별칭으로 불렸습니다.

ChatGPT에게 부탁해 만든 기차 로고


위 과정을 개발자가 수행해야 하는 git command line 명령어로 표현하자면 다음과 같습니다.

# 릴리즈 브랜치 명의 팀내 컨벤션은 release/yyyyMMdd
# 브랜치는 release/20240108, release/20240115, release/20240122 3개가 있다고 가정

# TICKET-1004 기능 병합
git checkout feature/TICKET-1004
git merge release/20240108

# 기차놀이 시작 : 작업 수행자 터미널에서 리베이스 연쇄 시작
git checkout release/20240115

# 바로 앞 날짜의 배포 브랜치를 rebase함
# 머지 커밋을 보존하기 위해 --rebase-merges (-r) 옵션 사용
git rebase -r release/20240108

# 리베이스 방식이므로 베이스가 달라져 forced-push 필요, 최소한의 안전장치로 --force-with-lease 옵션 사용
git push --force-with-lease

# 앞선 과정이 완료되면 한칸 더 뒷 날짜로 이동하여 앞 날짜를 리베이스
git checkout release/20240122
git rebase -r release/20240115
git push --force-with-lease

백포팅은 명령어를 로컬 환경에서 직접 수행하는 방식으로 처리되었습니다. forced push로 인한 사고를 막기 위해 10명 정도의 팀원들에게 배포 브랜치에 기능 병합을 하지 말아 달라는 양해를 구하고 진행했습니다.

작업 시간은 conflict가 없는 경우 5분 정도의 시간이 소요됩니다.
그렇게 긴 시간은 아니었지만 아래와 같은 문제가 있었습니다.

  1. ‘릴리즈 브랜치에 기능 브랜치를 병합한다’는 단순한 작업에 백포팅 작업이 추가되어 집중력이 요구됨
  2. 해당 작업이 오래 수행되면 다른 개발자들의 기능 브랜치 병합에 영향을 줌
  3. 모든 작업자가 백포팅 전략에 대해 숙지해야 하고 실수없이 수행해야 함

위 문제는 git 명령어를 대신 입력해 주는 셸 스크립트 자동화를 통해 해결할 수 있어 보였습니다.

개발자가 셸 스크립트와 어색한 사이인 경우

그러나 저는 개발을 처음 배울 때부터 IDE(integrated development environment, 개발을 위한 코딩 도구)와 함께였고, 웬만하면 IDE 밖을 벗어나지 않으려고 하는 IDE 은둔형 개발자입니다.

그래서 셸 스크립트를 활용한 자동화 필요성을 느끼면 자동화하는 대신 합리화를 통해 문제를 해결해 왔습니다. ("손으로 해도 충분한데?")

그러나 ChatGPT는 낯선 분야를 탐색하고 새로운 도구를 선택하기에 적절합니다. 자동화 도구를 작성하는 것에 대해 ChatGPT의 도움을 받을 수 있다면 IDE 은둔형 개발자인 저도 자동화를 할 수 있을 것입니다.

ChatGPT와 협업하기

"이거 자동화하면 모두 편해질 것 같은데요! 제가 스크립트 짜 볼게요!" 호기롭게 팀 메신저에 메시지를 남겼습니다.
당시 ChatGPT에게 몇 번의 유효한 도움을 받았고, 자신감이 넘치는 상태였습니다.

백포팅 활동에 대해 구분 동작 단위로 정리하고 ChatGPT에 접속해 프롬프트를 작성했습니다.

안녕! 코드 관리를 git rebase -r 옵션을 통해서 하고있어. 내가 하고싶은건 이런 작업을 자동화 하는거야.

1. origin의 release/yyyy-MM-dd 형식의 브랜치들이 있음
2. 시간순서상 두번째로 빠른 release 브랜치로부터,  바로 앞 시간순서의 release 브랜치를 rebase -r 하고 origin에 forced push를 해
3. 시간순서상 마지막 브랜치까지 완료할 때까지 2번 작업을 반복해

ChatGPT는 이에 대해 셸 스크립트를 대안으로 제시해 주고 초기 스크립트를 작성해 주었습니다.

git rebase -r 옵션으로 작업을 하려면, 각각의 브랜치를 시간 순서대로 정렬하고 이를 반복적으로 rebase를 진행해야 합니다.

아래는 이러한 작업을 수행할 수 있는 셸 스크립트의 예제입니다:

#!/bin/bash

# 현재 브랜치 목록을 시간 순으로 가져옵니다.
branches=($(git for-each-ref --sort=committerdate refs/remotes/origin/release/* --format='%(refname:short)' | sed 's/origin\///'))

# 브랜치가 2개 미만이면 rebase를 할 필요가 없습니다.
if [ ${#branches[@]} -lt 2 ]; then
    echo "Rebase할 브랜치가 충분하지 않습니다."
    exit 1
fi

# 두 번째 브랜치부터 마지막 브랜치까지 진행
for ((i=1; i<${#branches[@]}; i++)); do
    base_branch=${branches[$i-1]}
    topic_branch=${branches[$i]}

    echo "Rebasing $topic_branch onto $base_branch"

    # 브랜치를 checkout 합니다.
    git checkout $topic_branch

    # rebase를 실행합니다.
    git rebase -r $base_branch

    # 문제 없이 rebase가 완료되면 origin에 force push 합니다.
    if [ $? -eq 0 ]; then
        git push origin $topic_branch -f
    else
        echo "Rebase에서 문제가 발생했습니다. 해결 후 다시 스크립트를 실행해주세요."
        exit 1
    fi
done

echo "모든 rebase가 완료되었습니다."

이 스크립트는 다음과 같은 작업을 수행합니다:

1. 시간 순으로 release/yyyy-MM-dd 형식의 브랜치를 가져옵니다.
2. 두 번째 브랜치부터 마지막 브랜치까지 이전 브랜치를 기반으로 git rebase -r을 실행합니다.
3. rebase가 성공적으로 완료되면 해당 브랜치를 origin에 강제로 push합니다.

주의사항:

이 스크립트는 potential conflicts에 대한 예외 처리를 하지 않습니다. 만약 rebase 중 충돌이 발생하면 스크립트는 중단됩니다.
해당 스크립트는 force push를 사용하기 때문에 다른 사용자와 브랜치를 공유하고 있다면 주의가 필요합니다.
항상 테스트 환경에서 스크립트를 실행해보고, 실제 환경에서 적용하기 전에 백업을 권장합니다.

작업을 달성할 수 있는 셸 스크립트뿐만 아니라 스크립트에 대한 설명과 주의 사항까지 일러줍니다.

그러나 저는 셸 스크립트에 대해 for문과 if문의 형식, echo 명령어와 같은 최소한의 지식을 가지고 있었습니다.

ChatGPT가 첨부한 내용 해석을 기반해서 제 요구사항과 비교해 보았습니다.

실제로 동작해야 하는 것과 다른 점이 몇 가지 있었습니다.

  1. 브랜치는 앞 시간 순으로 정렬해야 함
  2. 정확히 release-yyyyMMdd 형식만 취급해야 함 (백업 용도의 비슷한 형식의 브랜치들이 존재했으므로)

당시의 저는 위의 결함에도 불구하고 단번에 AI 요술지팡이로 문제를 해결했다고 생각하며 고무되어 있었습니다. AI 비서와 하이파이브 하는 상상도 잠시 했던 것 같습니다.

그렇게 저는 ChatGPT와 협업해 5분 만에 얻어낸 결과(그리고 아주 작은 결함)를 팀 메신저에 공유하며 으스대었습니다.
빠르면 10분 안에 ‘아주 약간의 결함’만 수정하여 팀 프로젝트에 적용하겠다고 알리고, ChatGPT의 프롬프트 창으로 돌아갔습니다.

그리고 세 시간이 흘렀습니다. 야근 사유는 "rebase 스크립트 자동화"로 적었던 것 같습니다.

ChatGPT와 협업하며 겪은 시행착오

10분이라고 생각한 작업시간이 세 시간까지 늘어난 것은 실제 상황에 대해서 적용할 수 있는 스크립트로 완성되기까지 여러 시행착오를 겪었기 때문입니다.

첫째, 프롬프트로는 완벽한 맥락 전달이 어렵고
둘째, AI의 할루시네이션 현상 때문이었습니다.

시행착오 1 – 프롬프팅으로는 완벽한 맥락 전달이 어려움

제가 첫 질문으로 입력한 내용은 ‘git rebase 반복작업의 자동화’였습니다. 그러나 실제로 적용하고자 했던 것은 팀 백포팅 작업의 반복 수행을 쉽게 진행하는 것입니다.
맥락에 대한 이해가 없는 상태에서 만들어진 결과물을 바로 실제 상황에 적용하긴 어렵습니다.

맥락을 알려주기 위해선 누가, 언제, 어디서, 무엇을, 어떻게, 왜 필요한지 문자로 작성해야 하는데 이 과정을 모두 전달하는 게 쉽지 않습니다.

또한 적용하면서 겪게 될 미래에 일어날 일을 마법사의 수정구처럼 모두 예측하여 미리 전달하는 것도 불가능합니다.

그러므로 단 한 번의 프롬프트로 완벽한 결과물을 얻어내기는 어렵습니다.

대신 저는 우리가 평소 생활에서 맥락을 전달하기 위해 주로 사용하는 방법인 "대화"를 통해 비교적 쉽게 맥락을 전달하고 원하는 결과를 얻어낼 수 있다는 걸 깨달았습니다.

그래서 이번 자동화 작업 또한 대화 방식으로 진행했습니다.
아래 "펼치기"는 실제로 진행된 질문 내역입니다. 생생하게 전달하고자 맞춤법 보정 없이 그대로 옮겨 적었습니다.

실제로 진행된 31번의 질문 목록 펼치기
  1. 안녕! 코드 관리를 git rebase -r 옵션을 통해서 하고있어. 내가 하고싶은건 이런 작업을 자동화 하는거야. 1. origin의 release/yyyy-MM-dd 형식의 브랜치들이 있음 2. 시간순서상 두번째로 빠른 release 브랜치로부터, 바로 앞 시간순서의 release 브랜치를 rebase -r 하고 origin에 forced push를 해 3. 시간순서상 마지막 브랜치까지 완료할 때까지 2번 작업을 반복해
  2. 처음에 브랜치 목록을 시간 순으로 정렬할 때, 커밋 시간을 기준으로 한거같아. 그게 아니라 yyyy-MM-dd가 더 빠른걸 우선되게 할 수 있을까?
  3. 고마워! 테스트 중인데, 가끔 release/yyyyMMdd 가 아니라 예컨데 다음과 같은 릴리즈 브랜치도 있어 release/20230829-fix 이런건 제외하고, 순수하게 yyyyMMdd 로 되어있는 브랜치만 정렬하고 싶어
  4. 고마워! 리베이스 작업을 실행하기전에, origin의 브랜치들로 local 브랜치들을 최신화 하고 싶어
  5. 로컬 브랜치를 최신화 하는 부분은 취소해줘. 대신 로컬 브랜치 대신 무조건 원격 브랜치를 보게 하고싶은데. (로컬에 있는 변경사항이 영향을 받지 않게 하기 위함이야)
  6. 혹시 리베이스에서 문제가 발생하면 취소할 수 있을까?
  7. 음 직전에 너가 안내해준 스크립트는 리베이스 실패가 아니라 push 실패할 때 리베이스 취소를 하는 스크립트인거 같아. 리베이스 과정에서 문제가 발생했을 때 abort 및 어떤 브랜치가 실패했는지 안내할 수 있을까?
  8. forced push를 완료하면 해당 내역도 echo해줘. 2.forced push에 실패했을 때도 안내해주고, 임시 브랜치도 지워줘
  9. 왜 Forced Push할 때만 안내메시지가 영어가 됐어? 한글로 해줘
  10. if [ $? -eq 0 ]; then 는 무슨 뜻이야? 이게 어떻게 force push 성공 여부를 판단할 수 있는거야?
  11. 그러면 왜 리베이스는 if [ $? -ne 0 ]; then 를 통해 확인해?
  12. 고마워. 생각해보니 요구사항중에 하나를 추가할게. 시간 순서상 가장 앞의 브랜치는 origin/main 브랜치를 리베이스하고 푸쉬해야돼. 이부분까지 반영해줄 수 있을까?
  13. 중복 스크립트가 좀 있는거 같아. for문 안에서 첫번째 브랜치에 대해서만 처리하는 방법은 없을까?
  14. 정말 고마워. 이걸 프로덕션에서 테스트하기엔 좀 겁나서.. 테스트해볼수 있는 좋은 방법 없을까?
  15. 너가 말한 1번 임시브랜치 생성에 대한 스크립트를 짜줄 수 있어? 2. 그리고 tmp- 접두사가 붙은 버전으로 작업 스크립트도 수정해줄 수 있어?
  16. 지금 가진 스크립트를 실행하기 위해 vim 파일로 만들고 sh 로 실행했는데, 꼭 파일로 안 만들고 실행할 수 있는 방법 없을까?
  17. “$topic_branch를 $base_branch로 rebase 진행합니다. ” 부분 있잖아, 실제로 진행해보니 아래처럼 출력되었어 “{깨진 유니코드} rebase 진행합니다.” 원인이 뭘까?
  18. 스크립트를 실행해보면 이런 오류가 있어 error: Cannot delete branch ‘tmp_rebase_branch’ checked out at ‘/Users/wedge/IdeaProjects/server’
  19. 고마워, 스크립트에 그 부분들도 추가했어. 추가로 실제로 수행한 git 명령어가 어떤건지 출력하는 것도 echo 로 추가해 줄 수있어?
  20. 테스트 과정에서 문제가 생겼어. 임시 브랜치를 만들어 동작하는 방식으로 하다보니, 실제로 forced push한 릴리즈 브랜치들이 로컬 레포지토리엔 갱신이 안 됐어. 임시 브랜치를 만드는 방식이 아니라, 로컬 환경에 있는 릴리즈 브랜치를 다 초기화 하고 진행하는 방식으로 해야할거 같아
  21. 모든 echo 앞에 ‘[기차놀이 차장]’ 문자를 붙여줄 수 있어? 또, 한글과 영어가 붙어있으니 인코딩을 깨져서 각 변수와 조사 사이에 띄어쓰기가 있으면 좋겠어
  22. 스크립트를 실행하니 테스트하던 tmp-release 도 섞여버렸어. 혹시 정확히 release/yyyy-MM-dd 형식만 필터링 하도록 해줄 수 있어?
  23. 방금 너가 알려준 스크립트 파트에서 tmp-release/yyyy-MM-dd 형식만 받으려면?
  24. git ls -remote –heads origin 을 입력하면 아래 3개가 포함되어있는데, 너가 준 스크립트에선 0건인것처럼 나와
    refs/heads/tmp-release/20230914
    refs/heads/tmp-release/20230919
    refs/heads/tmp-release/20230926
  25. 여전히 결과가 0건이야. 왜일까?
  26. 결과가 0이야. 너를 위해 순차적으로 정보를 제공할게. 일단 git ls-remote –heads origin 의 결과는 아래와 같아 (50 줄 가량의 결과 제공)
  27. 너가 알려준 git ls-remote –heads origin | grep ‘tmp-release/[0-9]\{4\}\-[0-9]\{2\}\-[0-9]\{2\}$’ 명령어를 실행하면 아무것도 나오질 않아
  28. 미안 내가 형식을 잘못 제공했어. tmp-release/yyyyMMdd 형태야
  29. 해보니까, 뭔가 마지막 브랜치가 제대로 리베이스를 못하는거 같아. refs/heads/tmp-release/20230914 refs/heads/tmp-release/20230919 refs/heads/tmp-release/20230926 이렇게 3개의 브랜치가 있었는데, 20230926 브랜치가 20230919 브랜치와 형태가 똑같은 결과로 나타났어.
  30. if ! git diff-index –quiet HEAD –; then 이 문장을 설명해줘
  31. git diff-index HEAD — 가 궁금한데, 그러면 내 로컬에 있는 변경사항과 HEAD라는 최신 커밋의 변경사항을 확인하는거지? 원래 내 스크립트의 흐름상 origin/main 과 로컬의 변경사항을 확인하는거겠네?

시행착오 2 – AI의 할루시네이션 (Hallucination)

할루시네이션(Hallucination)은 AI 언어 생성 모델이 정확하지 않거나 사실이 아닌 정보를 전달하는 것을 의미합니다.

2023년 초기 GPT 모델의 할루시네이션 현상의 대명사처럼 쓰이던 일명 ‘세종대왕 맥북 던짐 사건’은 기사로도 작성되었습니다.

한국일보 – "세종대왕 맥북 던짐 사건 알려줘" 물었더니… 챗GPT의 엉뚱 답변 ‘밈’으로 유행 중

물론 LLM(대규모 언어 모델)은 빠르게 발전하고 있어서 세종대왕의 맥북 던짐 사건에 대해 다시 물어보면 역사적 사실에 근거한 이야기만을 알려줍니다.

그러나 여전히 100% 신뢰할 순 없으며, 2024년 2월 25일 현재 기준으로 ChatGPT의 프롬프트 창 하단에 다음과 같은 경고문구도 추가되었습니다.

GPT는 실수할 수 있으며, 중요한 정보의 경우 검수를 고려해보세요.


제가 작업 중 겪은 할루시네이션은 ChatGPT에게 여러 번의 수정 요청을 통해 스크립트를 갱신하던 중 발생했습니다.

ChatGPT에게 로컬 저장소의 브랜치를 원격 저장소의 브랜치로 초기화해달라는 요청에 대응하여 나온 스크립트는 아래와 같습니다.

    # 로컬 브랜치를 원격 저장소의 상태로 초기화
    git checkout $branch
    git reset --hard origin/$branch

하지만 해당 내용은 원격 저장소와 비교해 로컬 저장소의 커밋이 최신화되어 있지 않은 상황에서는 (즉 git pull이 선행되어 있지 않다면) 원격 저장소의 상태로 초기화되지 않습니다.
원격 저장소의 브랜치와 동일한 상태를 만들려고 의도했던 것과 다른 명령어입니다.

당시의 저는 ChatGPT를 철석같이 믿고 있었기 때문에 당연하게 느껴지는 git 명령어를 실수했을 거라는 생각을 하지 않아 디버깅에 오랜 시간을 소모했습니다.

ChatGPT와의 협업 결과물

3시간에 걸친 협업의 결과로 완성한 스크립트입니다.

#!/bin/bash

echo "[기차놀이 차장] 기차놀이를 시작합니다. rebase 과정에서 conflict가 발생하여 실패하는 경우는 수동으로 기차를 몰아주세요"

# 원격 저장소의 변경사항 가져오기
git fetch origin

# 원격 저장소에서 브랜치 목록 가져오기
branches=$(git ls-remote --heads origin | grep 'refs/heads/release/[0-9]\{8\}$' | sed 's?.*refs/heads/??' | sort -t '/' -k 2)

# main 브랜치 체크아웃 및 최신화
previous_branch="main"
git checkout $previous_branch
git pull

# 변경사항 확인
if ! git diff-index --quiet HEAD --; then
    echo "[기차놀이 차장] 로컬 변경사항이 발견되었습니다. 현재 main 브랜치 이므로 작업하던 브랜치로 돌아가 변경사항을 커밋하거나 stash한 후에 다시 시도해주세요."
    exit 1
fi

for branch in $branches; do

    # 원격 브랜치 백업
    git branch -D $branch-bak
    git checkout -b $branch-bak origin/$branch

    # 로컬 브랜치를 원격 저장소의 상태로 초기화
    git branch -D $branch
    git checkout -b $branch origin/$branch

    # 리베이스
    echo "[기차놀이 차장] $branch 를 $previous_branch 로 rebase 진행합니다."
    git rebase -r origin/$previous_branch
    if [ $? -ne 0 ]; then
        echo "[기차놀이 차장] $branch 리베이스에서 문제가 발생했습니다."
        git rebase --abort
        exit 1
    fi

    # 포스푸시
    echo "[기차놀이 차장] $branch 를 origin에 포스 푸시합니다."
    git push --force-with-lease origin $branch
    if [ $? -ne 0 ]; then
        echo "[기차놀이 차장] $branch 를 origin에 푸시하는데 실패했습니다."
        exit 1
    else
        echo "[기차놀이 차장] $branch 를 성공적으로 origin에 포스 푸시했습니다."
    fi

    previous_branch=$branch
done

완성한 스크립트를 프로젝트의 root path에 추가하고, README.md에 사용법을 안내하며 작업을 마무리했습니다.

꼭 사람이 확인하여야 할 conflict 경우만 제외하고, 단순 반복 작업은 스크립트를 통해 대체 수행하게 되어 개발자들이 수월하게 테스트용 베타 환경을 제공할 수 있게 되었습니다. (해피 엔딩)

“ChatGPT, ‘Shell’ 스크립트를 완성한 행복한 개발자를 만들어 주세요.”


하지만 이번 작업물로 얻게 된 효용보다 중요한 것은, 혼자선 엄두를 낼 수 없었던 작업을 ChatGPT와의 협업으로 작업할 수 있었다는 경험이라고 생각합니다.

이런 경험을 공유해 기술적 두려움을 느끼는 저와 같은 개발자들이 더 많은 ‘용기’를 발휘하길 기대하며 글을 작성합니다.

ChatGPT와의 협업에서 느낀 교훈

이번 작업과 그동안 ChatGPT를 쓰며 얻은 교훈을 공유합니다.

첫째, ChatGPT는 요술 지팡이가 아닙니다.
둘째, ChatGPT와 대화를 하며 작업하면 수월합니다.
셋째, 거짓말을 조심해야 합니다.

ChatGPT는 요술 지팡이가 아니다

처음 ChatGPT가 등장했을 때 전 AI의 시대가 도래했고, 개발자의 시대는 저물 것으로 생각했습니다. "배달앱을 만들어 줘"라고 입력하면 수만 줄의 코드가 작성될 것이라는 상상이요.

하지만 우리의 현실은 너무도 복잡하여 몇 문장 만으로 대체될 수 없습니다.
(적어도 아직은) ChatGPT는 "도서관에 존재하는 모든 책을 다 읽은 선생님"에 머무르고 있습니다. 선생님께 모르는 것을 물어보고, 이 답변을 내 상황에 접목하는 것은 아직 인간의 몫입니다.

결국 배운 것을 제대로 써먹기 위해선 이해해야 합니다. 단순히 내가 원하는 걸 만들어주는 요술 지팡이처럼 ChatGPT를 대한다면 나만의 문제 상황에 적용하기 어렵고, 애꿎은 요술 지팡이만 탓하게 됩니다.

제 스크립트 전체 질문 목록에서 24행 ~ 28행은 스크립트 내용을 정확히 이해하지 못한 채로 단순히 적용하려는 시도에서 비롯되었습니다.

> git ls -remote --heads origin 을 입력하면 아래 3개가 포함되어있는데, 너가 준 스크립트에선 0건인것처럼 나와 
> 여전히 결과가 0건이야. 왜일까?
> 너가 알려준 git ls-remote --heads origin | grep 'tmp-release/[0-9]\{4\}\-[0-9]\{2\}\-[0-9]\{2\}$' 명령어를 실행하면 아무것도 나오질 않아

당시엔 엉뚱한 스크립트를 만들어주고 잘 고쳐주지도 못하는 ChatGPT를 탓했습니다.

그러나 만약 스크립트의 원리를 이해하고 차분하게 디버깅을 해보았으면 더욱 빠르게 결론에 도달했을 겁니다. 이 작업 이후로는 내가 모르는 것을 이해하도록 도와주는 ‘조언자’로 ChatGPT를 이용하고 있습니다.

특히나 내가 쉽게 이해하기 어려운 개념에 대해 쉬운 설명을 요구할 때 큰 효과가 있습니다.
최근에 SQL92에서 Unknown이라는 논리값을 정의한 것에 대해 자료를 찾아볼 때, 동일성의 법칙에 대해 도무지 이해되지 않아 ChatGPT를 찾았습니다.

동일성의 법칙에 대해 설명해주세요



‘동일성의 법칙’, ‘비반사적 논리’ 등 평소였으면 "그렇군…" 하고 끄덕이고 지나갔을 개념들을 이해할 수 있는 내용으로 되새겨 주니 유용했습니다.

ChatGPT는 인내심이 아주 강하고 친절한 선생님이기 때문에 모르는 것을 물어보았을 때 좋은 스승이 되어줍니다.

ChatGPT와 대화해보자

ChatGPT를 써보았는지 주위 개발자들에게 물어보면 몇 번 써보니 어떻게 질문을 해야겠는지도 모르겠고 틀린 답변을 받아 신뢰할 수 없어 사용하지 않는다는 분들이 많았습니다.

이런 어려움은 주위만 겪는 것이 아닌지 국내 IT 서점의 매대 중 한 부분은 프롬프트 프로그래밍 관련 저서들로 가득 차있습니다.

책을 집어 몇 권 읽어보면 ChatGPT의 사용법에 대해 나옵니다. 어떻게 하면 Parameter를 만들 수 있으며, 질문의 순서 배치는 어떻게 해야 하며…
이런 프롬프팅 프로그래밍의 목표 중 하나는 ChatGPT에게 ‘내 상황을 어떻게 하면 가장 효율적으로 전달할 수 있을지’입니다.

그러나 저는 ChatGPT 등장 이전에도 실생활에서 내 상황을 전달하기 위해 "대화"를 활용해 왔습니다.

우리는 하나의 기능을 개발하기 위해서 기획서와 기능요구사항 문서만으로 작업하지 않습니다. 관련 문서를 모두 읽고, 이해를 일치시키기 위해 수많은 회의를 하고, 메신저로 대화할 수 있는 채널을 만들거나 1:1 메시지를 통해 이해한 것이 맞는지 끊임없이 대화합니다.

그래서 저는 프롬프팅을 위한 별도의 학습 없이도 이미 고도로 숙련된 "대화"라는 방법을 통해 ChatGPT에게 좋은 결과를 끌어낼 수 있음을 느끼고 있습니다.

저는 ChatGPT에게 프롬프트를 작성할 때 다른 개발자에게 1:1 메시지를 보낸다는 생각으로 작성합니다.

최근 ChatGPT 선생님과의 DM 내역


대화하듯이 내가 원하는 것들을 가볍게 이야기하고 답변에서 생겨나는 질문을 다시 작성하다 보면 만족스러운 결과를 얻을 때가 많았습니다.

이렇게 ChatGPT와 친해진 이후 프롬프트 프로그래밍을 접한다면 더 좋은 효과를 누릴 수 있을 것입니다.

프롬프트 프로그래밍과 관련된 자료로는 DAIR.AI에서 제공하는 promptingguide.ai와 안될과학 YouTube 채널의 프롬프트 엔지니어링이란? 생성형 AI에게 최적의 답을 얻는 노하우 대방출! 안될과학X삼성SDS 2탄을 추천합니다.

거짓말을 조심하자

본문에서 얘기했듯이 ChatGPT를 비롯한 생성형 AI들에게는 할루시네이션이라고 불리는 거짓말이 존재합니다. 프로덕션에 적용할 코드처럼 중요한 질문을 할 때에는 반드시 테스트를 해보고 적용해야 합니다.

마무리하며

ChatGPT는 인터넷의 방대한 자료를 바탕으로 만들어졌다고 합니다. 그래서 때론 ChatGPT를 사용할 때면 그동안 인류가 쌓아온 집단지성과 대화하고 있다는 감상을 받기도 합니다.

앞서 공유의 가치를 실천하며 좋은 자료를 제공해 오신 선배 개발자들에게 감사하고, AI로 학습의 폭을 넓혀 다시 개발계에 이바지하는 선순환이 많이 일어났으면 좋겠습니다.

기본 프로필 01
성시형

우아한형제들에서 서버(백엔드) 개발자로 일하고 있습니다.