GitLab MR 리뷰 댓글 처리하기
개요
GitLab MR의 인라인 리뷰 댓글은 디스커션 (파일+라인에 고정된 스레드)에 저장됩니다. 전체 루프(위치 정보 가져오기 → 수정 → 답변 → 해결)는 glab api를 통해 실행됩니다. 이 스킬은 검증된 명령 시퀀스를 제공합니다. 중요한 점은 일반적인 명령어(glab mr view --comments)는 댓글 텍스트만 제공하고 파일/라인 위치 정보를 제공하지 않아 각 댓글이 어디를 가리키는지 알 수 없다는 것입니다.
필수 배경 지식: 판단 부분은 superpowers:receiving-code-review를 사용하세요: 각 댓글을 기술적으로 엄격하게 평가하고 적용 전에 올바른지 확인합니다. 이 스킬은 GitLab 조작 방법만 다룹니다.
설정
MR=511 # MR iid
PROJ="<group>%2F<subgroup>%2F<project>" # URL로 인코딩된 전체 경로
API="projects/$PROJ/merge_requests/$MR"
경로를 URL 인코딩합니다 (/ → %2F). 확실하지 않으면 glab repo view에서 가져옵니다.
1. 위치 정보를 포함한 댓글 가져오기
glab mr view $MR --comments는 라인 위치 정보를 포함하지 않으므로 인라인 댓글에 사용하지 마세요. 대신 Discussions API를 호출하고 position을 파싱합니다. --paginate는 필수입니다: 엔드포인트는 페이지당 20개의 스레드를 반환하며, 페이지네이션 없이 나머지는 오류 없이 버려집니다. 이미 해결된 스레드는 건너뜁니다:
glab api --paginate --output ndjson "$API/discussions" | python3 -c '
import sys, json
for line in sys.stdin:
d = json.loads(line)
n = d["notes"][0]
if n.get("system") or n.get("resolved"): continue
p = n.get("position") or {}
print(d["id"], "|", n["author"]["username"], "|", n["body"])
if p: print(" ->", p.get("new_path"), "new_line:", p.get("new_line"), "old_line:", p.get("old_line"))
'
new_line= 현재 (변경 후) 파일의 라인 → 댓글을 해석할 때 읽는 값입니다.- 변경된 라인의 댓글에는
old_line과new_line이 모두 포함됩니다.new_path+new_line을 기준으로 합니다. position이 없음 → 일반 (인라인이 아닌) 댓글입니다.
2. 각 new_path:new_line의 코드 읽고 수정하기
댓글이 문맥상 이해되도록 해당 라인의 파일을 엽니다. 평가 후에만 수정을 적용합니다 (receiving-code-review 참조). 하나의 리뷰-수정 커밋으로 만들고 푸시합니다:
git add <files>
git commit -m "fix(<scope>): <내용>에 대한 리뷰 반영" # Conventional Commits, 본문에 티켓 ID
git fetch origin <target> && git rebase origin/<target>
git push --force-with-lease # 리베이스가 no-op인 경우에만 일반 `git push`로 충분
3. 각 스레드에 답변하고 해결하기
디스커션의 notes 엔드포인트에 게시하면 자동으로 스레드에 답변이 추가됩니다 (in_reply_to_id 불필요). 그런 다음 resolved=true로 PUT 요청을 보냅니다:
DISC="<1단계에서 가져온 디스커션 id>"
glab api -X POST "$API/discussions/$DISC/notes" -f "body=완료: <변경한 내용>."
glab api -X PUT "$API/discussions/$DISC?resolved=true"
절대 glab을 셸 함수로 감싸지 마세요: 권한 허용 규칙은 리터럴 명령어만 일치시키므로 각 스레드마다 두 호출을 모두 완전히 작성하세요.
답변 작성하기
변경된 내용과 이유만 간결하게 작성하세요. 검토자는 조사 과정이 아니라 결과물의 차이점을 원합니다.
- 적용됨: 한두 문장. 변경한 내용, 해당 라인 너머로 확장한 내용, 커밋 SHA.
완료, 블록 스타일을 모든 곳에 통일: 여섯 구성 요소 모두, 이 라인뿐만 아니라. 렌더링 출력은 변경되지 않음. f6511f29 - 동의하지 않음 또는 연기됨: 결정적인 사실을 먼저, 그다음 옵션, 마지막으로 검토자에게 결정권을 돌려줍니다. 짧은 두 문단이 최대입니다. 부수적인 보류, 세 번째 지지 논거, "논의 환영" 마무리는 생략합니다.
- 절대 자신의 수정 과정을 설명하지 말고, 검토자의 댓글을 그대로 반복하지 마세요.
해결할 스레드
실제로 종결한 스레드만 해결합니다.
- 변경 적용됨 -> 답변 + 해결.
- 질문에 답변했고 답변이 문제를 해결함 -> 답변 + 해결.
- 본인의 결정 사항이 아님 (대상 환경, 범위, 우선순위) -> 답변, 해결하지 마세요. 검토자가 여전히 중재해야 하는 스레드를 해결하면 무시하는 것으로 읽히며, 검토 목록에서 사라집니다.
빠른 참조
| 단계 | 명령어 |
|---|---|
| 인라인 댓글 + 위치 목록 | glab api --paginate --output ndjson "$API/discussions" → .notes[].position.{new_path,new_line,old_line} 파싱 |
| 스레드에 답변 | glab api -X POST "$API/discussions/<id>/notes" -f "body=..." |
| 스레드 해결 | glab api -X PUT "$API/discussions/<id>?resolved=true" |
흔한 실수
- 인라인 댓글에
glab mr view --comments사용: 파일/라인 정보 없이 텍스트만 제공하므로 위치를 찾을 수 없습니다. Discussions API를 사용하세요. glab mr note resolve를 임의로 사용:glab api -X PUT ".../discussions/<id>?resolved=true"로 해결합니다.new_line대신old_line읽기: 변경된 라인의 댓글에서 현재 코드는new_line에 있습니다.--paginate누락: 20개 스레드를 넘어가면 Discussions API가 조용히 첫 페이지만 반환합니다.- 리베이스 후
--force사용:--force-with-lease를 사용하여 검토자가 그 사이에 푸시한 커밋을 덮어쓰지 않도록 합니다. - 댓글을 맹목적으로 적용: 먼저 평가하세요 (receiving-code-review). 실제 변경한 내용을 답변에 남기고 해결합니다.
- 스레드 목록에서 디스커션 id 자르기: API는 전체 40자 id를 원하며, 8자 접두사는
404 Discussion Not Found를 반환합니다.d["id"]전체를 출력하세요. - 답변에서 과도한 설명: "답변 작성하기"를 참조하세요. 추론은 MR 설명이나 티켓에 있어야 합니다.
- 연기된 결정을 해결: 검토자가 여전히 선택해야 한다면 스레드를 열어두세요.