Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save hanjae-jea/086e8e6f96f59cf59074249f06d62669 to your computer and use it in GitHub Desktop.

Select an option

Save hanjae-jea/086e8e6f96f59cf59074249f06d62669 to your computer and use it in GitHub Desktop.
prd.md
# PRD: macOS Zsh 환경에서 Gum을 활용한 대화형 Git 단축 함수 구현
이 문서는 사용자가 자신만의 대화형 Git 단축 유틸리티 함수(`gss`, `gaa`, `gcm`)를 구성할 수 있도록 시스템의 세부 동작 논리와 요구사항을 정의합니다.
이 PRD의 목표는 구체적인 코드를 주입하는 대신, 개발자의 AI 코딩 어시스턴트가 논리적 구조를 바탕으로 자유롭고 다양하게 코드를 구현할 수 있도록 **알고리즘 흐름과 단계별 요구 조건**을 명확히 제공하는 것입니다.
---
## 1. 개요 및 목표
기존의 기본 Git 명령어(`git status`, `git add`, `git commit`)는 강력하지만 텍스트 기반이라 가독성이 낮고, 매 단계마다 타이핑이 번거롭다는 단점이 있습니다. 이 가이드를 통해 다음 기능을 구현합니다:
* **풍부한 비주얼 스타일**: Nerd Font 아이콘과 터미널 색상을 활용해 파일의 변경 상태를 시각화합니다.
* **대화형 스테이징 및 커밋**: 질문 창(confirm)과 다중 선택 메뉴(choose)를 통해 편리하게 파일을 스테이징하고 Conventional Commit 형식의 커밋 메시지를 생성합니다.
* **안정적인 쉘 동작**: macOS Zsh 환경에서 기존 설정과 엉키거나 충돌하지 않고 안정적으로 동작하도록 설계합니다.
---
## 2. Zsh 충돌 및 에러 방지 가이드
쉘 스크립트나 함수를 로드할 때 가장 자주 발생하는 문제는 기존 별칭(Alias)이나 명령어와의 이름 충돌입니다. 특히 대중적으로 널리 쓰이는 **Oh My Zsh의 `git` 플러그인**은 이미 아래와 같은 단축 별칭들을 선점하고 있습니다.
### 충돌 매트릭스
* **`gaa`**: Oh My Zsh에서 `alias gaa='git add --all'`로 미리 정의됨.
* **`gcm`**: Oh My Zsh에서 `alias gcm='git checkout $(git_main_branch)'`로 미리 정의됨.
이 별칭들이 활성화되어 있으면, Zsh는 사용자가 작성한 커스텀 함수보다 별칭(Alias)을 우선하여 실행합니다. 따라서 기대했던 대화형 UI 대신 기본 단축 명령어가 작동하게 됩니다.
### 설계 조건 (충돌 방지)
1. **기존 별칭 강제 해제 (Unalias)**: 작성하려는 단축 명령어(`gaa`, `gcm`)와 동일한 이름을 가진 Zsh 별칭이 있다면, 함수를 정의하기 직전에 먼저 별칭을 해제(`unalias 별칭이름 2>/dev/null`)해야 합니다.
2. **Git 저장소 유효성 검사**: 사용자가 실행하는 모든 메인 함수는 가장 먼저 현재 위치가 Git 작업 공간 내부인지 확인해야 하며, 작업 공간이 아닐 시 적절한 경고 메시지를 출력하고 종료(exit code 1 등)해야 합니다.
3. **Zsh 내장 기능 사용**: 1부터 시작하는 Zsh 배열 인덱스 체계와 문자열 변환 기능(예: 확장자 소문자 변환 시 `${(L)variable}` 사용)에 맞게 안전한 Zsh 문법으로 로직을 감싸야 합니다.
---
## 3. 구조적 설계 및 세부 로직 사양
전체 단축 유틸리티는 1개의 스타일링 헬퍼 함수와 3개의 사용자 명령어로 구성됩니다.
```mermaid
graph TD
A[gss: 스타일 상태 표시] -->|의존| B[_gum_git_style_line: 라인 스타일링 헬퍼]
C[gaa: 대화형 add] -->|호출| A
D[gcm: 대화형 commit] -->|호출| A
```
---
### A. 내부 스타일링 헬퍼 함수 (`_gum_git_style_line`)
이 함수는 `git status --porcelain`이 출력하는 로우(raw) 텍스트 데이터를 분석하여 폰트 아이콘과 전경색(Foreground)이 입혀진 터미널 한 줄 문자열을 생성하여 반환합니다.
#### 1) 입력 매개변수
* `$1`: 파일 상태 코드 (예: `M `, `A `, `??`, ` M` 등 공백을 포함한 2자리 상태 표식)
* `$2`: 변경된 파일 경로 (이름이 변경된 경우 `이전경로 -> 새경로` 포맷이 들어옴)
#### 2) 조건식 및 처리 흐름
1. **이름 변경(Rename) 처리**:
* 만약 상태 코드(`$1`)가 `"R "` (Staged Rename) 이라면, 파일 경로에서 ` -> ` 구분자 뒤쪽의 '새 파일 경로'에서 basename을 추출해 파일명으로 정의합니다.
* 그렇지 않은 보통의 상태 코드인 경우, 파일 경로 전체에서 단순 basename을 추출합니다.
2. **확장자 추출 및 대소문자 무시**:
* 정의된 파일명에서 확장자를 추출한 뒤 소문자로 일괄 변환합니다.
3. **아이콘 매핑 조건문**:
* 추출한 확장자 혹은 파일명에 알맞은 Nerd Font 아이콘을 선택합니다.
* *예시 기준*:
* 파일명이 `Dockerfile` 인 경우: `󰡨`
* 파일명이 `.gitignore` 인 경우: ``
* 확장자가 `js`, `mjs` 인 경우: `󰌞`
* 확장자가 `ts`, `tsx` 인 경우: `󰛦`
* 확장자가 `py` 인 경우: ``
* 확장자가 `md` 인 경우: ``
* 확장자가 `json` 인 경우: `󰘦`
* 확장자가 `sh`, `zsh` 인 경우: ``
* 기타 일반 파일: 기본 아이콘 ``
4. **색상 매핑 조건문**:
* Git 상태 코드 문자열(`$1`)에 매칭되는 ANSI 전경색 코드를 설정합니다.
* *추천 색상 가이드*:
* `M ` (Staged 수정), `A ` (Staged 추가): 녹색 계열 (예: ANSI `46`)
* `D ` (Staged 삭제): 적색 계열 (예: ANSI `196`)
* `R ` (Staged 이름 변경): 자주색 계열 (예: ANSI `201`)
* ` M` (Unstaged 수정): 분홍색 계열 (예: ANSI `218`)
* ` D` (Unstaged 삭제): 적색 계열 (예: ANSI `196`)
* `??` (추적되지 않는 파일): 황색 계열 (예: ANSI `220`)
* 기타 기본값: 회색 계열 (예: ANSI `240`)
5. **텍스트 렌더링 및 출력**:
* `gum style` 명령을 활용하여 상태 코드 부분과 파일 경로 부분 각각에 해당하는 색상을 입힙니다.
* `printf`를 사용하여 `[컬러 상태코드] [아이콘] [컬러 파일경로]` 규격의 포맷팅된 문자열을 출력합니다.
---
### B. 사용자 명령어: `gss` (Git Status Styled)
현재 작업 트리의 변경 내역을 수집하여 Staged 영역과 Unstaged 영역으로 구분한 뒤 테두리가 감싸진 박스 형태로 보기 좋게 인쇄합니다.
#### 1) 처리 흐름 및 조건문 설계
1. **저장소 유효성 검사**: 현재 폴더가 Git 저장소 내부인지 검사하고 아닐 시 안전하게 메시지를 남기고 에러 코드로 리턴합니다.
2. **리스트 변수 선언**: Staged 목록을 보관할 배열과 Unstaged 목록을 보관할 배열을 생성합니다.
3. **상태 한 줄 읽기 루프**:
* `git status --porcelain` 명령의 결과를 라인 단위로 읽어들이는 반복문(Loop)을 실행합니다.
* 각 라인이 비어있는 경우 생략(continue)합니다.
* 각 라인의 앞 2글자(상태 코드)와 3번째 글자 이후(파일 경로)를 추출합니다.
* **Staged 여부 조건 분기**:
* 상태 코드가 스테이징 완료 상태(첫 글자가 `M`, `A`, `D`, `R` 중 하나로 시작함)인 경우:
* `_gum_git_style_line` 헬퍼 함수를 통해 라인을 예쁘게 꾸민 뒤 **Staged 배열**에 삽입합니다.
* 그 외의 상태 코드(두 번째 글자가 수정/삭제되었거나 신규 추적되지 않는 상태)인 경우:
* 헬퍼 함수를 호출하여 꾸민 뒤 **Unstaged 배열**에 삽입합니다.
4. **최종 출력 레이아웃 조립**:
* **Staged 영역 추가 조건문**:
* 만약 Staged 배열에 요소가 들어있다면, 녹색의 굵은 글씨로 `'Staged Changes:'` 헤더 타이틀을 추가하고 그 아래에 Staged 배열 전체 내용을 담습니다.
* **Unstaged 영역 추가 조건문**:
* 만약 Unstaged 배열에 요소가 들어있다면, (Staged 변경 사항이 이미 존재했다면 빈 한 줄을 띄워 구별한 뒤) 황색의 굵은 글씨로 `'Unstaged Changes:'` 헤더 타이틀을 추가하고 그 아래에 Unstaged 배열 전체 내용을 담습니다.
5. **종료 조건 확인**:
* 조립한 전체 출력 배열의 크기가 0이면 "Nothing to commit, working tree clean." 메시지를 남기고 정상 종료합니다.
6. **박스 그리기**:
* 수집된 모든 표시용 문자열 라인들을 `gum style`에 매개변수로 전달하여, 원하는 색상의 테두리(`--border normal --border-foreground [색상번호]`)와 약간의 좌우 여백을 주어 최종 출력합니다.
---
### C. 사용자 명령어: `gaa` (Git Add All - Interactive)
`gss` 화면을 먼저 보여준 다음, 사용자가 스테이징되지 않은 모든 변경 사항을 원클릭으로 간편하게 추가(add)할 수 있는 안전한 대화형 장치를 제공합니다.
#### 1) 처리 흐름 및 조건문 설계
1. **저장소 유효성 검사**: 현재 폴더가 Git 저장소 내부인지 검사하고 아닐 시 에러 코드로 리턴합니다.
2. **변경 사항 유무 검사**: `git status --porcelain` 출력값이 완전히 비어있다면 "추가할 변경 사항이 없다"는 안내 후 즉시 종료합니다.
3. **현재 상태 표시**: `gss` 함수를 실행하여 현재 워킹 트리 내에 변경된 내역을 시각적으로 보여줍니다.
4. **확인 창(confirm) 분기**:
* 만약 스테이징되지 않은 파일이 남아있는 상태라면:
* `gum confirm "Stage all unstaged changes?"` 프롬프트를 사용자에게 제공합니다.
* 사용자가 **동의(Yes)**할 경우: `git add .` 명령을 수행하고 완료 메시지(성공 알림 체크표시 포함)를 스타일링하여 터미널에 표시합니다.
* 사용자가 **거절(No) 혹은 취소**할 경우: `"작업이 취소되었습니다."` 메시지를 출력하고 종료합니다.
---
### D. 사용자 명령어: `gcm` (Git Commit Menu)
변경 사항 시각화, 부분적/개별적 파일 스테이징 선택, 그리고 Conventional Commit 구조의 질문지 마법사를 통해 완성도 높은 커밋을 수행합니다.
#### 1) 처리 흐름 및 조건문 설계
1. **저장소 유효성 검사**: 현재 폴더가 Git 저장소 내부인지 검사하고 아닐 시 종료합니다.
2. **변경 사항 유무 검사**: 변경된 파일 내역 자체가 아예 없다면 "커밋할 변경 사항이 없다"는 메시지 출력 후 종료합니다.
3. **상태 배열 수집 및 표시**:
* `gss`에서와 동일하게 Porcelain 출력을 읽어 Staged와 Unstaged 파일 목록 배열을 채웁니다.
* `gss` 함수를 한 번 실행해 줍니다.
4. **Unstaged 파일 처리 조건 분기**:
* Unstaged 파일 배열에 항목이 존재하는 경우:
* `gum confirm "Stage all unstaged changes?"`를 호출합니다.
* **전체 스테이징 동의 시**: `git add .`을 실행합니다.
* **전체 스테이징 비동의 시**:
* `gum choose --no-limit`를 띄우며, 이때 선택 후보 리스트로 우리가 앞서 예쁘게 꾸며둔 **Unstaged 배열의 문자열들**을 전달합니다.
* 사용자가 선택을 확정하면, 반환된 스타일 문자열 목록에서 ANSI 컬러 기호나 상태 기호 등을 정제하고 **순수한 원본 파일 경로들만 파싱**해 냅니다. (힌트: 첫 번째 필드인 상태 코드와 두 번째 필드인 아이콘 문자열을 필터링하고 공백을 제거하는 파싱 로직 설계 필요)
* 걸러진 원본 파일 경로 목록을 `xargs git add` 또는 반복문을 통해 하나씩 스테이징 영역에 추가합니다.
* **프롬프트 도중 탈출(ESC 등) 시**: "작업 취소" 메시지를 띄우며 함수 전체를 취소 반환합니다.
5. **최종 스테이징 변경 사항 무결성 확인**:
* 위의 스테이징 흐름을 거친 뒤에도 Staged 상태에 변경된 내용이 최종적으로 존재하는지 확인합니다 (`git diff --staged --quiet` 등을 활용).
* 만약 스테이징된 내역이 없다면 "No changes staged for commit." 경고 후 안전하게 종료합니다.
6. **커밋 마법사 작성 프로세스**:
* **단계 1: 커밋 타입 선택**
* `gum choose`를 띄워 아래 스타일 양식의 타입 리스트를 사용자에게 제시하고 하나를 반환받습니다:
* `✨ feat: 새로운 기능 추가`
* `🐛 fix: 버그 수정`
* `📝 docs: 문서 수정`
* `🎨 style: 코드 포맷팅, 세미콜론 누락 등`
* `♻️ refactor: 코드 리팩토링`
* `⚡️ perf: 성능 개선`
* `✅ test: 테스트 코드 추가/수정`
* `🚀 chore: 빌드, 패키지 매니저 설정 등`
* 반환값에서 콜론(`:`) 이전의 접두사 영역(예: `✨ feat`)만 파싱하여 `TYPE` 변수에 저장합니다.
* **단계 2: 커밋 제목 입력**
* `gum input --placeholder "커밋 제목"` 프롬프트를 띄워 사용자가 요약 문구를 적도록 합니다.
* 사용자가 아무것도 입력하지 않고 빈 문자열을 남길 경우 "커밋 제목이 비어 있어 작업을 중단합니다." 경고와 함께 실패 상태로 리턴합니다.
* **단계 3: 커밋 상세 본문 입력**
* `gum write --placeholder "자세한 설명 (Ctrl+D 또는 Esc로 완료)"`를 띄워 긴 형태의 상세 텍스트 설명을 작성하도록 유도합니다.
* **단계 4: 최종 커밋 실행**
* 만약 본문내용(Body)이 비어있다면: `git commit -m "[TYPE]: [SUBJECT]"`를 실행합니다.
* 본문내용(Body)이 입력되었다면: `git commit -m "[TYPE]: [SUBJECT]" -m "[BODY]"`를 실행합니다.
---
## 4. 검증 및 테스트 시나리오
개발자는 자신이 활용하는 AI 코딩 어시스턴트를 사용해 완성된 Zsh 스크립트를 빌드한 후, 아래 체크리스트로 최종 작동 테스트를 진행해야 합니다.
1. **별칭 해제 우선권(Alias Priority) 작동 여부**:
* `alias gaa` 또는 `alias gcm`을 입력하여 Oh My Zsh 등의 기존 단축키가 강제로 비활성화되고 정의한 커스텀 함수가 제대로 바인딩되는지 검증합니다.
2. **저장소 경계 제어 검증**:
* Git 저장소가 아닌 곳에서 `gss` 또는 `gaa`를 실행했을 때 에러 메시지와 함께 비정상적 중단 없이 흐름이 차단되는지 확인합니다.
3. **터미널 아이콘 깨짐 현상 검사**:
* 파일 확장자 매칭 아이콘이 네모난 빈 박스나 알 수 없는 유니코드 오류 문자 없이 아름답게 로드되는지 확인합니다. (깨지는 현상 발견 시 터미널 전용 폰트 설정을 점검하십시오.)
4. **개별 선택 스테이징 복구 검사**:
* `gcm` 실행 중 전체 스테이징을 거부한 상황에서, 개별 파일들을 Space로 선택하여 활성화(`gum choose`)했을 때 실제 해당 파일들만 성공적으로 `git add`가 진행되는지 체크합니다.
5. **최종 커밋 메시지 형식 확인**:
* 완결된 커밋 이후 `git log -1`을 실행하여 `[이모지] [타입]: [제목]` 과 함께 작성한 본문 설명이 안전하게 커밋 이력에 남았는지 로그를 읽어 검증합니다.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment