GitHub Copilot 앱의 스택 세션과 스택형 풀 리퀘스트는 오래된 코드를 한 번에 크게 고치는 대신, 서로 의존하는 작업을 검토 가능한 변경으로 나누는 방식이다. 핵심은 AI가 많은 코드를 생성하는 데 있지 않다. 첫 작업의 맥락과 변경을 바탕으로 후속 세션을 만들고, 각 결과를 순서가 드러나는 별도 풀 리퀘스트로 연결하는 데 있다.

GitHub Blog에 소개된 사례는 2014년 말 무렵 시작한 개인용 대시보드 애플리케이션의 현대화 과정이다. 이 앱은 캘린더와 스마트홈 기기, 작업 관리 기능을 묶어 사용해 왔지만 React 15, Less, 오래된 react-bootstrap 같은 의존성이 남아 있었다. 아주 큰 애플리케이션은 아니었지만 스타일, 접근성, 반응형 레이아웃과 라이브러리 교체가 얽혀 있어 한 번에 정리하기에는 부담스러운 상태였다.

스택 세션과 스택형 풀 리퀘스트

스택은 같은 저장소의 여러 풀 리퀘스트를 의존 순서대로 연결한 구조다. 가장 아래에 있는 첫 풀 리퀘스트는 작업 브랜치의 변경을 dev나 main 같은 최종 대상 브랜치로 보낸다. 그 위의 후속 풀 리퀘스트는 두 번째 작업 브랜치에서 첫 번째 작업 브랜치로 변경을 보내도록 구성한다. 이렇게 하면 두 번째 변경은 아직 병합되지 않은 첫 번째 변경을 전제로 작성하고 검토할 수 있다.

  1. 최종 대상이 될 dev 또는 main과 현재 배포 상태를 확인한다.
  2. 첫 번째 작업 브랜치에서 스타일이나 레이아웃처럼 범위가 분명한 변경을 만든다.
  3. 첫 번째 작업 브랜치에서 dev·main 등 최종 대상 브랜치로 첫 풀 리퀘스트를 만든다.
  4. 첫 번째 작업 브랜치를 기반으로 두 번째 세션과 작업 브랜치를 시작한다.
  5. 두 번째 작업 브랜치에서 첫 번째 작업 브랜치로 후속 풀 리퀘스트를 만든다.
  6. 기반이 되는 첫 풀 리퀘스트부터 검토하고 병합한 뒤, 후속 풀 리퀘스트의 대상과 실제 차이를 다시 확인한다.

독립적인 풀 리퀘스트들이 모두 공통 대상 브랜치를 향하는 방식과 달리, 스택형 풀 리퀘스트는 앞 단계에서 도입한 코드와 결정을 다음 단계가 바로 사용할 수 있게 한다. 기반 작업의 병합을 기다리지 않고 후속 작업을 시작할 수 있지만, 각 풀 리퀘스트의 소스 브랜치와 대상 브랜치, 병합 순서를 분명히 공유해야 한다.

한 번의 지시로 현대화는 끝나지 않았다

작성자는 먼저 Plan 모드에서 프런트엔드 현대화 계획을 요청했다. Less를 제거하고 Tailwind 또는 일반 CSS를 검토하며, 접근성과 반응형 레이아웃을 개선한 뒤 React 기능과 의존성을 점진적으로 정리하려는 계획이었다. 링크의 hover·focus 표현, 입력 상자의 모양, 넓은 화면에서 컨테이너와 입력 요소가 지나치게 늘어나지 않도록 하는 조건도 포함했다.

계획에는 Claude Opus 4.8과 GPT-5.5의 Rubber Duck 검토가 사용됐고 여러 차례 결정을 조정했지만, 첫 실행은 원하는 결과로 이어지지 않았다. 문제는 계획의 상세함보다 작업 기준이었다. 새 세션은 main에서 분기했지만, 작성자가 실제로 사용하던 배포는 과거에 일부 현대화를 진행한 dev 브랜치를 기반으로 하고 있었다. dev에만 있던 기능 때문에 새 변경과 호환 문제가 생긴 것이다.

작성자는 dev의 변경을 main 기반 작업에 끌어오는 대신 새 변경을 dev 쪽에 적용하는 방향으로 전환했다. Copilot 앱은 새 세션을 만들고 기존에 시도한 풀 리퀘스트를 닫은 뒤, 앞서 결정한 스타일 방향을 dev 기반 변경으로 옮겼다. 이 사례가 보여주는 우선순위는 명확하다. 세션을 시작하기 전에 기본 브랜치 이름만 볼 것이 아니라 실제 배포에 쓰이는 브랜치와 필요한 기능이 남아 있는 위치를 확인해야 한다.

테스트에서 발견한 문제를 새 스택으로 분리하다

스타일 변경을 테스트하던 중 콘솔에서 findDOMNodecomponentWillReceiveProps 관련 오래된 경고가 발견됐다. 해당 참조는 작성자의 코드보다 react-bootstrap에 남아 있었다. 작성자는 라이브러리를 업그레이드할지 완전히 제거할지를 Plan 모드에서 다시 검토했고, 결과적으로 전체 교체를 권하는 계획을 받았다.

중요한 선택은 이 작업을 기존 풀 리퀘스트에 바로 넣지 않은 것이다. 스타일 현대화에 react-bootstrap 제거까지 합치면 범위가 크게 늘어난다. 작성자는 먼저 dev를 대상으로 기존 작업의 풀 리퀘스트를 만들고, 그 작업에서 분기한 새 세션에서 react-bootstrap 제거를 진행했다. 후속 세션은 이전 맥락을 이어받아 계획을 만들고 승인을 거쳐 실행됐으며, 결과도 앞선 작업을 대상으로 하는 별도 풀 리퀘스트가 됐다.

따라서 이 사례에서 스택은 단순히 브랜치를 여러 개 만든다는 뜻이 아니다. 첫 번째 풀 리퀘스트는 스타일 현대화라는 목적을 유지하고, 테스트 중 발견한 라이브러리 교체는 두 번째 풀 리퀘스트로 격리한다. 두 번째 작업이 첫 번째 변경에 의존하므로 두 풀 리퀘스트는 순서가 있는 체인을 형성한다.

왜 변경을 나누는가

AI 코딩 도구를 사용하면 직접 작성할 코드가 줄어 관련 문제를 한꺼번에 해결하고 싶어질 수 있다. 원문 작성자도 모든 요구를 담은 1만 줄 규모의 풀 리퀘스트를 만들고 싶은 유혹을 언급한다. 그러나 생성 속도가 빨라져도 사람이 변경을 이해하고 테스트해야 한다는 사실은 달라지지 않는다.

  • 목적이 선명해진다. 스타일 현대화와 라이브러리 제거를 서로 다른 이유와 완료 조건으로 설명할 수 있다.
  • 테스트 초점을 나눌 수 있다. 첫 단계에서는 스타일과 반응형 동작을 보고, 다음 단계에서는 교체된 컴포넌트와 경고 제거 여부를 살필 수 있다.
  • 범위 팽창을 억제한다. 테스트 중 발견한 모든 문제를 현재 변경에 넣지 않고 후속 작업 후보로 분리할 수 있다.
  • 후속 작업을 먼저 시작할 수 있다. 첫 변경이 최종 대상 브랜치에 병합되기 전에도 그 변경을 기반으로 다음 작업을 진행할 수 있다.
  • 맥락을 이어 간다. 앞 세션의 결정과 변경을 후속 세션이 활용할 수 있다.

반면 기반 풀 리퀘스트의 설계가 크게 바뀌면 위에 쌓인 변경도 영향을 받는다. 스택을 사용할 때는 각 풀 리퀘스트가 바로 아래의 어느 브랜치를 대상으로 하는지, 어떤 순서로 병합할지를 설명해야 한다. 원문은 모든 프로젝트에 적용할 고정된 스택 깊이나 운영 규칙을 제시하지 않으므로, 단계 수는 실제 의존성과 검토 가능성을 기준으로 정해야 한다.

레거시 코드에 적용할 때의 순서

1. 실제 기준 브랜치를 확인한다

이 사례의 첫 실패는 main과 실제 배포에 쓰이던 dev가 달랐기 때문에 발생했다. 현재 사용하는 기능이 어느 브랜치에 있는지, 새 작업이 어디에서 분기해야 하는지를 먼저 확인한다. 이 확인이 빠지면 계획이 타당해도 잘못된 코드 상태를 현대화할 수 있다.

2. 첫 변경의 범위를 좁힌다

“전체 프런트엔드 현대화”를 한 번에 실행하기보다 CSS 전환, 링크 상태 표현, 입력 요소와 컨테이너 레이아웃처럼 함께 검토할 수 있는 범위를 먼저 고른다. 원문 사례에서도 초기 초점은 스타일과 접근성, 반응형 개선이었다.

3. 첫 풀 리퀘스트의 방향을 명시한다

첫 작업 브랜치에서 dev나 main 같은 최종 대상 브랜치로 풀 리퀘스트를 만든다. 첫 작업 브랜치 자체를 대상으로 삼는 것이 아니다. 원문 사례에서는 정상적으로 작동한 스타일 변경이 dev를 대상으로 하는 첫 풀 리퀘스트가 됐다.

4. 독립적으로 검토할 문제는 후속 세션으로 보낸다

테스트에서 발견한 문제가 현재 변경의 목적을 크게 넓힌다면 새 세션 후보로 분리한다. react-bootstrap 제거처럼 앞 변경을 필요로 하는 작업은 첫 작업 브랜치에서 새 브랜치를 만들고, 두 번째 작업 브랜치에서 첫 번째 작업 브랜치로 풀 리퀘스트를 보내 스택을 구성할 수 있다.

5. 기반 변경부터 처리한다

스택은 아래쪽 풀 리퀘스트가 위쪽 변경의 기반이다. 따라서 먼저 최종 대상 브랜치를 향하는 첫 풀 리퀘스트를 검토하고, 이어지는 변경이 어떤 기반 위에 있는지 확인해야 한다. 병합 과정에서는 각 풀 리퀘스트 화면에 표시되는 대상 브랜치와 변경 범위를 다시 살피는 것이 좋다.

이 사례에서 얻을 수 있는 판단 기준

스택형 풀 리퀘스트 자체는 AI가 있어야만 가능한 개발 방식이 아니다. Git 브랜치와 풀 리퀘스트를 이용해 의존 변경을 수동으로 연결할 수도 있다. GitHub Copilot 앱 사례의 특징은 세션의 맥락과 계획, 브랜치 작업, 풀 리퀘스트 분리를 이어지는 흐름으로 처리했다는 점이다.

특히 오랫동안 방치된 코드에서는 첫 계획보다 저장소의 실제 상태 확인이 중요하다. 과거의 미완료 현대화가 다른 브랜치에 남아 있을 수 있고, 테스트를 시작한 뒤에야 오래된 의존성의 문제가 드러날 수도 있다. 이때 기존 풀 리퀘스트의 목적을 계속 넓히기보다, 현재 작업을 보존하면서 발견한 문제를 후속 세션과 풀 리퀘스트로 분리하는 것이 스택 방식의 실질적인 효용이다.

다만 원문은 계정별 제공 범위, 가격, 조직 정책, 저장소 권한이나 데이터 처리 조건을 구체적으로 설명하지 않는다. 실제 저장소에 적용할 때는 해당 계정에서 기능을 사용할 수 있는지와 저장소 설정에 맞는지를 GitHub의 최신 제품 화면과 공식 문서에서 별도로 확인해야 한다.

출처와 검증

이 글은 Cassidy Williams가 2026년 7월 30일 GitHub Blog에 공개한 개인 프로젝트 현대화 사례를 바탕으로 재구성했다. 사례에 나온 브랜치 관계, 세션 분리와 풀 리퀘스트 흐름은 원문을 기준으로 확인했으며, 실제 제공 범위와 제품 동작은 적용 시점의 GitHub 공식 안내에서 다시 확인해야 한다. GitHub Blog 원문: Stacked sessions and pull requests in the GitHub Copilot app