컴퓨터공학 300 주제 시리즈의 297번째 글이다. 전체 지도는 여기.

한 줄 요약

개발자의 커뮤니케이션은 대부분 비동기 텍스트다. 커밋 메시지, PR 설명, 코드 리뷰 댓글, 이슈, 장애 공지. 좋은 메시지는 받는 사람이 되묻지 않고 행동할 수 있도록 맥락·근거·요청을 함께 담는다.

왜 필요한가

팀이 커질수록 코드보다 사람 사이의 정보 전달이 병목이 된다.

  • “이거 왜 이렇게 했어요?” 에 아무도 답하지 못한다. 커밋 메시지가 fix 뿐이다.
  • 리뷰 댓글 하나에 상처받은 동료가 다음 PR 을 올리지 않는다.
  • “로그인 안 돼요”라는 이슈에 재현 정보가 없어 다섯 번 왕복한다.
  • 장애 중 상황 공유가 없어 세 사람이 같은 조사를 한다.

기술력이 같다면 소통을 잘하는 사람과 팀이 더 빨리 간다. 이것은 성격이 아니라 익힐 수 있는 기술이다.

핵심 개념

동기와 비동기

방식 예 맞는 상황
동기 회의, 전화, 페어 프로그래밍 빠른 왕복이 필요한 논의, 갈등 해소, 장애 대응 지휘
비동기 PR, 이슈, 문서, 메신저 스레드 기록이 남아야 하는 결정, 시간대가 다른 협업, 깊은 검토

비동기 메시지는 받는 사람이 맥락 없이 읽는다. 그래서 하나의 메시지 안에 완결성이 있어야 한다. “잠깐 시간 되세요?”만 보내고 답을 기다리는 대신, 질문 자체를 처음부터 보낸다.

질문을 잘하는 법

좋은 질문은 답하는 사람의 시간을 아낀다.

목표:     무엇을 하려고 하는가
현상:     무엇이 일어나는가 (오류 메시지 원문, 로그 일부)
기대:     무엇이 일어나야 한다고 생각하는가
시도:     이미 해 본 것과 그 결과
환경:     버전, OS, 설정 (민감 정보는 가린다)
재현:     최소 재현 예제

최소 재현 예제를 만들다 보면 스스로 원인을 찾는 경우가 많다. 그 자체로 디버깅 기법이다.

커밋 메시지

커밋 메시지는 미래의 동료(와 나)에게 보내는 편지다. Git 공식 문서는 50자 이하의 짧은 요약 한 줄로 시작하고, 빈 줄을 둔 뒤 자세한 설명을 쓰는 것을 권한다. 첫 빈 줄까지가 제목으로 취급되어 여러 도구에서 쓰이기 때문이다.

fix(api): 주문 조회 시 타임존을 KST 로 고정        <- 무엇을 (50자 안팎)
                                                   <- 빈 줄
UTC 로 저장된 값을 그대로 보여 주어 자정 근처        <- 왜 (diff 가 말해 주지 않는 것)
주문의 날짜가 하루 밀려 보였다.
Refs: #123                                         <- 관련 이슈

diff 는 “무엇이” 바뀌었는지 보여 준다. 메시지는 diff 가 말하지 못하는 “왜”를 써야 한다. Conventional Commits 명세는 feat, fix 같은 타입과 ! 로 호환성 깨짐을 표시하는 형식을 정해, 변경 기록 생성과 버전 결정을 자동화할 수 있게 한다.

PR 설명

리뷰어는 내 머릿속을 모른다. PR 설명에는 다음을 쓴다.

  • 무엇을, 왜 바꾸는가(이슈 링크)
  • 어떻게 확인했는가(테스트, 스크린숏, 실행 결과)
  • 리뷰어가 특히 봐 줬으면 하는 곳, 그리고 일부러 하지 않은 것
  • 배포 시 주의점(마이그레이션, 설정 변경)

PR 은 작게 만든다. 작은 PR 은 빨리 리뷰되고, 리뷰 품질이 높고, 되돌리기 쉽다.

코드 리뷰 댓글

Google 의 공개 엔지니어링 가이드는 코드 리뷰에 대해 몇 가지를 분명히 한다. 리뷰 요청에는 길어도 1 영업일 안에 응답할 것, 코드에 대해 말하고 사람에 대해 말하지 말 것, 이유를 설명할 것, 필수가 아닌 제안은 “Nit:” 처럼 표시할 것.

나쁜 예:  이거 왜 이렇게 짰어요? 비효율적이네요.
좋은 예:  이 루프가 요청마다 DB 를 한 번씩 조회해서 N+1 이 됩니다(목록 100건이면 쿼리 101번).
          IN 절로 한 번에 가져오면 어떨까요? 예: SELECT ... WHERE id IN (...)
좋은 예:  Nit: 변수 이름 `d` 를 `deadline` 으로 바꾸면 읽기 쉬울 것 같아요. (필수 아님)

받는 쪽도 기술이 필요하다. 리뷰 댓글은 코드에 대한 의견이지 인격 평가가 아니다. 동의하지 않으면 근거를 들어 답하고, 결론이 나지 않으면 짧은 대화로 정리한 뒤 결과를 PR 에 남긴다.

장애 상황의 커뮤니케이션

장애 중에는 정보가 부족하고 사람들이 불안하다. 역할을 나눈다.

역할 하는 일
지휘자(incident commander) 우선순위 결정, 작업 배분
커뮤니케이터 정해진 주기로 상황 공지
조사·조치 담당 원인 파악과 복구

공지는 짧고 정기적으로 한다. “현재 영향, 조치 중인 것, 다음 공지 시각.” 모르는 것은 모른다고 쓴다. 추측을 사실처럼 쓰지 않는다.

장애 후 회고(postmortem)는 비난 없이(blameless) 쓴다. Google SRE 책은 회고가 사람을 탓하지 않고 시스템과 절차의 개선점을 찾는 데 집중해야, 사람들이 실수를 숨기지 않고 정직하게 보고한다고 설명한다.

직접 해 보기

커밋 메시지 검사기를 만든다. Git 의 commit-msg 훅이나 CI 에 붙이는 도구가 하는 일이다.

import re

PATTERN = re.compile(r"^(feat|fix|docs|refactor|test|chore|perf|build|ci)(\([\w-]+\))?(!)?: \S")

def lint_commit(msg):
    lines = msg.splitlines()
    subject = lines[0]
    problems = []
    if not PATTERN.match(subject):
        problems.append("제목이 '<type>(<scope>): <설명>' 형식이 아니다")
    if len(subject) > 50:
        problems.append(f"제목이 {len(subject)}자. 50자 이내 권장")
    if subject.endswith("."):
        problems.append("제목 끝에 마침표")
    if len(lines) > 1 and lines[1].strip():
        problems.append("제목과 본문 사이에 빈 줄이 없다")
    if subject.lower().split(":")[-1].strip() in ("수정", "fix", "update", "wip", "변경"):
        problems.append("무엇을 왜 바꿨는지 알 수 없는 제목")
    return problems

samples = [
    "fix: 수정",
    "fix(api): 주문 조회 시 타임존을 KST 로 고정\n\nUTC 로 저장된 값을 그대로 보여 주어 날짜가 하루 밀렸다.\nRefs: #123",
    "Updated some files and fixed the bug in the login page that users reported.",
    "feat(auth)!: 세션 만료 시간을 30분으로 단축\n본문이 붙어 있음",
]
for m in samples:
    print(repr(m.splitlines()[0]))
    for p in lint_commit(m) or ["문제 없음"]:
        print("   -", p)

실행 결과다.

'fix: 수정'
   - 무엇을 왜 바꿨는지 알 수 없는 제목
'fix(api): 주문 조회 시 타임존을 KST 로 고정'
   - 문제 없음
'Updated some files and fixed the bug in the login page that users reported.'
   - 제목이 '<type>(<scope>): <설명>' 형식이 아니다
   - 제목이 75자. 50자 이내 권장
   - 제목 끝에 마침표
'feat(auth)!: 세션 만료 시간을 30분으로 단축'
   - 제목과 본문 사이에 빈 줄이 없다

fix: 수정 은 형식은 맞지만 내용이 없다. 형식 검사는 최소한의 기준일 뿐이다. 50자 기준은 Python 의 len() 이 세는 문자 수로 적용했다. 한글은 터미널에서 두 칸을 차지하므로 팀에 따라 기준을 조정하기도 한다.

현업에서는

  • 메신저에서 장애나 결정이 오가면 결론을 이슈나 문서에 옮겨 둔다. 메신저는 검색이 약하고 맥락이 흩어진다. 혼자 운영하는 홈랩 k3s 라도 “왜 이 노드의 설정을 바꿨는지” 를 커밋 메시지나 노트에 남기면 몇 달 뒤 같은 고민을 반복하지 않는다.
  • 상태 보고는 “완료”라는 말보다 확인 근거를 함께 쓴다. “배포했다”보다 “배포했고, 헬스체크 URL 이 200 을 반환하는 것을 확인했다”가 낫다. 확인하지 못했다면 그렇다고 쓴다.
  • 나쁜 소식은 빨리 알린다. 일정이 밀릴 것 같으면 마감 직전이 아니라 알게 된 순간 공유한다. 일찍 알리면 선택지가 많다.
  • 글로 쓰기 어려운 갈등은 짧은 통화로 풀고, 결론은 다시 글로 남긴다.

확인 문제

  1. 비동기 메시지에 완결성이 필요한 이유는?
  2. 커밋 메시지 본문에 무엇을 써야 하는가? diff 와 무엇이 다른가?
  3. 다음 리뷰 댓글을 고쳐 써라: “이건 틀렸어요.”
  4. 비난 없는 회고가 조직의 신뢰성을 높이는 이유는?

풀이

  1. 받는 사람이 다른 시간·맥락에서 읽으므로 되묻기 왕복이 비싸다. 한 메시지 안에 목표·현상·요청이 있어야 바로 행동할 수 있다.
  2. 왜 바꿨는지, 어떤 문제를 풀었는지, 고려한 대안과 영향. diff 는 무엇이 바뀌었는지만 보여 준다.
  3. 예: “여기서 items 가 비어 있으면 items[0] 에서 IndexError 가 날 것 같습니다. 빈 목록일 때는 기본값을 반환하면 어떨까요?” 문제, 근거, 제안을 담는다.
  4. 사람이 처벌을 두려워하지 않아야 실수와 근본 원인을 정직하게 보고하고, 그래야 시스템과 절차를 고칠 수 있기 때문이다.

더 읽을거리 (References)