화요일 오전, 슬랙에 메시지를 하나 보냈다. "결제 쪽 API 바뀐 거 같은데 확인 부탁드려요." 백엔드 팀의 담당자가 두 시간 뒤에 답했다. "어떤 API 말씀이세요?" 내가 다시 답했다. "주문 생성 API요. 응답 구조가 달라진 것 같아서." 또 한 시간 뒤에 답이 왔다. "주문 생성 API는 변경된 적 없는데요. 혹시 스테이징에서 테스트하신 건가요?" 그때서야 깨달았다. 내가 로컬에서 테스트한 건데, 환경 변수가 잘못 설정돼서 다른 서버를 가리키고 있었다. API는 바뀐 적이 없었다.
이 에피소드에서 내가 낭비한 시간: 30분. 상대방이 낭비한 시간: 1시간(메시지 확인, 코드 히스토리 확인, 답변 작성). 그리고 그 사이에 둘 다 컨텍스트 스위칭 비용을 치렀다. 총 비용을 따지면 반나절이다. 원인은 하나다. 내가 보낸 첫 메시지에 정보가 부족했다.
이건 시작에 불과했다.
2일을 날린 슬랙 메시지
더 큰 사건은 3개월 뒤에 터졌다. 프론트엔드에서 새 기능을 개발하고 있었는데, 디자이너가 슬랙에 이렇게 올렸다. "이 화면 인터랙션 좀 바꿔야 할 것 같아요." 그 메시지 아래에 피그마 링크가 하나 있었다.
나는 피그마를 열어봤다. 기존 디자인과 달라진 부분이 있었다. 모달 안의 버튼 위치가 바뀌어 있었고, 새로운 상태(로딩 중)가 추가되어 있었다. "아, 디자인이 업데이트됐구나"라고 판단하고, 그에 맞춰 코드를 수정하기 시작했다. 버튼 위치를 바꾸고, 로딩 상태를 추가하고, 관련 테스트도 업데이트했다. 하루가 걸렸다.
다음 날 디자이너한테 확인을 받으려고 했더니, 그 디자이너가 당황했다. "아, 그건 아직 확정된 게 아니에요. 검토 중인 시안이었는데... '바꿔야 할 것 같다'는 건 의견을 물어본 거였어요."
하루 치 작업을 롤백했다. 개발 시간 하루, 롤백 시간 반나절, 그리고 디자이너와의 후속 미팅 1시간. 총 2일을 날렸다.
원인을 분석하면 양쪽 다 문제가 있었다. 디자이너는 "의견 요청"인지 "작업 요청"인지를 명확히 하지 않았다. 나는 확인 없이 바로 작업을 시작했다. 근데 더 근본적인 문제는, 우리 팀에 커뮤니케이션 프로토콜이 없었다는 거다.
이 사건 이후 팀에서 간단한 규칙을 만들었다. 슬랙 메시지 앞에 태그를 붙이기로. [의견요청] [작업요청] [공유] [긴급]. 이 네 글자 태그 하나가, 수신자가 메시지를 어떻게 처리해야 하는지를 즉시 알려준다. 사소해 보이지만 효과는 컸다. 2달 뒤에 비슷한 종류의 오해가 눈에 띄게 줄었다.
제대로 된 버그 리포트의 힘
QA 팀에서 버그 리포트가 올라온다. "결제 화면에서 오류가 발생합니다."
이걸로 뭘 할 수 있을까? 아무것도 못 한다. 어떤 브라우저? 어떤 디바이스? 어떤 계정? 어떤 상품? 어떤 결제 수단? 오류 메시지가 뭐였는지? 재현 경로가 어떻게 되는지? 콘솔에 에러가 찍혔는지? 네트워크 탭에서 뭐가 보이는지?
정보가 하나도 없으니까, 나는 질문을 하기 시작한다. 한 번에 다 물어보면 좋겠지만, 보통 하나 물어보면 답이 오고, 그 답을 보고 다시 물어보게 된다. 이 핑퐁이 3-4번 반복되면 반나절이 지나간다. 그사이에 다른 작업을 하다가 돌아오다가를 반복하니까 집중력도 깨진다.
반대 경험도 있다. 같은 QA 팀에서 다른 사람이 올린 버그 리포트는 이랬다:
제목: 결제 화면 - 신용카드 결제 시 "결제 처리 중" 상태에서 무한 로딩
환경: Chrome 121 / macOS Sonoma / 프로덕션
재현 경로:
- 상품 A (상품 ID: 12345)를 장바구니에 추가
- 결제 진행
- 결제 수단: 신용카드 선택
- 카드 정보 입력 후 "결제하기" 클릭
- "결제 처리 중" 스피너가 표시되고 이후 변화 없음
예상 동작: 결제 완료 후 주문 완료 페이지로 이동 실제 동작: 스피너가 무한히 표시됨 (5분 이상 대기해도 변화 없음)
콘솔 로그:
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'orderId')(스크린샷 첨부)네트워크: POST /api/payments 응답 200이지만 response body가 비어 있음 (스크린샷 첨부)
재현율: 100% (3회 테스트)
이 리포트를 받으면 바로 원인을 찾으러 갈 수 있다. 네트워크 탭 스크린샷에서 API 응답이 비어 있다는 정보가 핵심이었다. 백엔드 쪽을 확인해보니 결제 게이트웨이와의 연동에서 응답 파싱이 누락된 부분이 있었다. 리포트부터 수정 완료까지 2시간. 첫 번째 리포트 방식이었으면 최소 하루가 걸렸을 거다.
기술 제안서(RFC)를 쓰는 법
시니어가 되면서 가장 많이 하는 일 중 하나가 기술 제안서를 쓰는 거다. 새로운 아키텍처를 제안하거나, 기술 부채를 해결하는 방안을 내놓거나, 새 도구를 도입하자고 설득하거나. 이걸 잘하느냐 못하느냐가 실제로 영향력에 큰 차이를 만든다.
처음에 기술 제안서를 쓸 때 실수했던 건, 결론부터 말하지 않은 것이다. 장황하게 배경을 설명하고, 현재 상황의 문제점을 나열하고, 대안을 비교하고, 마지막에 "따라서 X를 도입하자"로 끝나는 구조. 20분 읽어야 결론을 알 수 있는 문서. 바쁜 사람(특히 리드, CTO)은 그 20분을 참지 못한다. 처음 2분에서 핵심을 파악하지 못하면 닫아버린다.
지금은 이런 구조로 쓴다:
# 제안 요약
"X를 도입하자. 현재 Y 문제가 있고, X가 이걸 해결한다.
예상 비용은 2주, 예상 효과는 Z."
# 문제
[현재 무엇이 문제인가. 가능하면 숫자로.]
# 제안
[구체적으로 무엇을 하자는 건가.]
# 대안
[다른 선택지는 뭐가 있고, 왜 이 제안이 더 나은가.]
# 리스크
[이 제안의 단점과 리스크는 뭔가. 솔직하게.]
# 실행 계획
[구체적으로 어떤 단계로 진행하는가.]
핵심은 첫 세 줄이다. 의사결정자가 첫 세 줄만 읽고도 "이건 추진할 가치가 있겠다" 또는 "이건 지금 타이밍이 아니다"를 판단할 수 있어야 한다. 나머지는 궁금한 사람이 더 읽는 거다.
또 하나 중요한 건 리스크 섹션이다. 제안서에 리스크를 솔직하게 쓰면 오히려 신뢰도가 올라간다. "이 방법은 완벽하다"는 제안은 의심을 산다. "이 방법에는 A, B 리스크가 있지만, A는 이렇게 완화할 수 있고, B는 발생 확률이 낮다"는 제안이 훨씬 설득력 있다.
"모르겠다"의 힘
개발자 커리어에서 가장 강력한 말 중 하나가 "모르겠다"이다.
주니어 시절엔 이 말이 무능하게 들릴까 봐 무서웠다. 회의에서 "이 방법이 맞을까요?" 하고 물어보면, 확신이 없어도 "네, 괜찮을 것 같아요"라고 대답했다. 아는 척하는 게 안전하다고 생각했다.
시니어가 되면서 깨달은 건, 아는 척이 가장 위험하다는 거다.
한번은 아키텍처 미팅에서 데이터베이스 선택에 대한 논의가 있었다. 나는 프론트엔드가 주 분야라 백엔드 인프라에 깊은 지식이 없었다. 근데 미팅 분위기상 의견을 내야 할 것 같아서, 얕은 지식으로 "MongoDB가 스키마 유연성 측면에서 좋을 것 같다"고 말했다. 나중에 알고 보니, 우리 서비스의 데이터 특성상 관계형 DB가 훨씬 적합했다. 내 의견이 크게 반영된 건 아니었지만, 잘못된 방향으로 논의를 끌고 간 건 사실이었다.
그 이후로 모르는 건 "모르겠다"고 말하기 시작했다. 근데 그냥 "모르겠다"가 아니라, 이런 식이다:
"이 부분은 내 전문 영역이 아니라서 정확하게 판단하기 어렵다. 근데 내가 아는 범위에서 말하면, A 측면은 이렇게 보인다. B 측면은 해당 분야 전문가한테 확인이 필요할 것 같다."
이렇게 말하면 두 가지 좋은 일이 벌어진다. 첫째, 내가 아는 부분과 모르는 부분의 경계가 명확해져서, 논의가 더 정확한 방향으로 간다. 둘째, 팀에서 나의 의견에 대한 신뢰도가 오히려 올라간다. 내가 "이건 확실하다"고 말할 때, 진짜 확실하다는 걸 다들 알게 되니까.
Gergely Orosz(The Pragmatic Engineer 저자)가 여러 차례 강조한 내용이 있다. 시니어 엔지니어를 구별하는 건 코드 실력이 아니라 커뮤니케이션이라는 거다. 같은 수준의 기술력을 가진 두 엔지니어가 있을 때, 자기 생각을 명확하게 전달하고, 다른 사람의 의견을 정확히 이해하고, 복잡한 기술적 결정의 맥락을 문서로 남기고, "모르는 걸 안다"고 솔직하게 말하는 사람이 시니어가 된다. 코드만 잘 짜는 사람은 시니어까지는 갈 수 있지만, 그 이상은 어렵다.
비동기 커뮤니케이션의 기술
원격 근무가 늘면서 비동기 커뮤니케이션의 중요성이 커졌다. 슬랙 메시지, PR 코멘트, 문서 — 전부 비동기다. 이건 대면 대화와 근본적으로 다른 규칙이 적용된다.
대면: 말이 애매해도, 상대방 표정을 보면서 즉시 보충 설명을 할 수 있다. "아 그게 아니라..."가 가능하다.
비동기: 한 번 보낸 메시지가 전부다. 상대방이 읽는 시점에 나는 다른 작업을 하고 있다. 애매한 부분이 있으면 상대방은 혼자 해석해야 한다. 그 해석이 내 의도와 다르면 핑퐁이 시작된다.
그래서 비동기 메시지에는 과할 정도로 맥락을 넣어야 한다.
나쁜 예: "이거 수정해주세요."
좋은 예: "결제 화면(payment/checkout.tsx)의 쿠폰 적용 로직에서, 할인 금액이 원가보다 큰 경우 음수가 되는 케이스가 있습니다. 결제 금액이 음수가 되지 않도록 Math.max(0, discountedPrice) 같은 가드가 필요합니다. 관련 이슈: #234"
두 번째 메시지가 길지만, 이 한 번의 메시지로 끝난다. 첫 번째 메시지는 최소 3번의 핑퐁이 필요하다.
회의에서의 커뮤니케이션
개발자들이 회의를 싫어하는 이유 중 하나는, 대부분의 회의가 비효율적이기 때문이다. 근데 회의 자체가 나쁜 게 아니라, 커뮤니케이션 방식이 나쁜 거다.
Gergely Orosz가 "Talk First, Code Later"라는 제목의 글에서 말한 것처럼, 복잡한 기술적 결정은 코드를 짜기 전에 이야기를 먼저 해야 한다. 근데 그 이야기가 생산적이려면 구조가 필요하다.
기술 논의 회의에서 내가 쓰는 패턴은 이렇다:
시작 전: 논의할 내용을 미리 문서로 공유한다. 회의에서 처음 설명하면 시간이 낭비된다. 읽어오지 않은 사람이 있으면? "5분 드릴 테니 읽고 시작합시다"가 "30분 동안 설명하겠습니다"보다 낫다.
논의 중: "이건 어떻게 생각해요?"보다 "이 두 가지 중 어떤 게 나은 것 같아요?"가 생산적이다. 열린 질문은 논의를 확산시키고, 닫힌 질문은 논의를 수렴시킨다. 확산이 필요한 단계가 있고, 수렴이 필요한 단계가 있다.
끝나기 전: "오늘 결정된 것"과 "다음에 할 것"을 명확히 정리한다. "다들 알겠죠?"로 끝나면, 다음 회의에서 "지난번에 뭐라고 결론 났었죠?"가 나온다.
커뮤니케이션은 기술이다
"나는 코딩은 잘하는데 커뮤니케이션이 부족해"라는 말을 종종 듣는다. 이 말에는 "커뮤니케이션은 타고나는 것"이라는 전제가 깔려 있다. 근데 커뮤니케이션은 코딩과 똑같이 배울 수 있는 기술이다.
처음부터 좋은 버그 리포트를 쓰는 사람은 없다. 처음부터 명확한 기술 제안서를 쓰는 사람도 없다. 처음부터 회의를 잘 진행하는 사람도 없다. 다 연습과 피드백으로 좋아지는 거다. React의 state 관리를 배우듯이, 명확한 메시지 작성법도 배울 수 있다. TypeScript의 타입 시스템을 익히듯이, 기술 제안서의 구조도 익힐 수 있다.
차이가 있다면, 코딩 실력은 혼자서도 올릴 수 있지만, 커뮤니케이션 실력은 상대방이 있어야 올라간다는 거다. 그래서 더 의도적인 연습이 필요하다. 슬랙 메시지를 보내기 전에 "이 메시지를 받는 사람이 필요한 맥락이 다 있나?" 3초만 생각해보는 것. PR 코멘트를 달기 전에 "이 코멘트가 상대방한테 어떻게 읽힐까?" 잠깐 생각해보는 것. 이런 작은 습관이 쌓이면, 1년 뒤에는 확연히 다르다.
코드는 결국 사람이 읽는다. 그리고 코드 이전에, 사람과 사람 사이에 오가는 메시지가 있다. 그 메시지의 품질이 코드의 품질을 결정한다.
