본문으로 건너뛰기

11. 책임은 코드 어디에 저장되는가

문제 제기: 사고 뒤에 남는 것은 로그뿐이다

한 물류센터에서 자동 분류기가 오배송을 반복한다. 컨베이어 벨트 위의 상자는 카메라를 지나고, 분류 모델은 목적지를 예측하고, 제어 시스템은 해당 상자를 오른쪽 또는 왼쪽 레일로 보낸다. 어느 날 특정 지역의 주소가 인쇄된 상자들이 연달아 잘못된 레일로 들어간다. 물류팀은 배송 지연에 항의하고, 개발팀은 모델의 정확도 지표를 확인한다. 모델은 테스트셋에서 충분히 높은 점수를 받았다.

회의실의 화면에는 로그가 떠 있다. 요청이 들어왔고, 예측이 나왔고, 명령이 실행되었다. 그러나 로그는 말하지 않는다. 왜 이 임계값이 선택되었는지, 왜 사람이 확인하지 않았는지, 운영자가 경고를 보았는지, 해당 오류가 이미 다른 지역에서 반복되고 있었는지. 시스템은 수많은 사건을 기록했지만, 책임의 경로는 기록하지 않았다.

사고가 발생하면 우리는 습관처럼 한 사람을 찾는다. 모델을 만든 개발자, 버튼을 누른 운영자, 요구사항을 작성한 기획자, 시스템을 승인한 관리자 중 누군가를 지목한다. 하지만 복잡한 소프트웨어에서 결과는 한 줄의 코드에서 나오지 않는다. 기본값, 데이터 파이프라인, 배포 절차, 모니터링 화면, 조직의 일정과 보상 체계가 겹쳐 하나의 행동을 만든다. 책임은 코드 한 곳에 저장되어 있지 않다. 그럼에도 책임을 추적할 구조는 코드와 시스템 안에 설계할 수 있다.

철학적 논점: 책임은 의도와 결과 사이의 다리다

책임은 단순히 “누가 원인을 제공했는가”와 다르다. 원인은 물리적으로 거슬러 올라갈 수 있지만, 책임은 누가 무엇을 알았고, 무엇을 예측할 수 있었고, 어디에서 개입할 권한을 가졌는지를 묻는다. 이 질문은 의도와 결과 사이에 다리를 놓는다.

개발자가 버그를 의도하지 않았다는 사실은 중요하다. 그러나 의도하지 않았다는 이유만으로 책임이 사라지지는 않는다. 위험을 알고도 테스트를 생략했는지, 오류 신호를 무시했는지, 사용자가 오해할 만한 화면을 그대로 배포했는지에 따라 책임의 무게는 달라진다. 반대로 최종 버튼을 누른 운영자에게 모든 책임을 몰아주는 것도 부당할 수 있다. 운영자가 충분한 설명을 받지 못했고, 취소 권한도 없었으며, 실패를 보고할 통로마저 닫혀 있었다면 그의 행동은 이미 시스템이 만든 좁은 복도 안에서 이루어진 것이다.

이때 공학자의 과제는 책임을 추상적인 윤리 강령으로 남겨 두지 않는 것이다. 책임을 시스템의 구조로 번역해야 한다. 누가 모델을 승인했는가. 어떤 데이터와 버전을 사용했는가. 모델의 확신이 낮을 때 무엇이 일어나는가. 자동 결정에 이의를 제기할 수 있는가. 사고가 나면 로그만 남는가, 아니면 판단의 근거와 당시의 선택지도 함께 남는가.

업무를 작은 티켓으로 분해하는 과정에도 책임의 철학이 들어 있다. “추천 기능 구현”이라는 티켓은 누구도 책임지지 않는 문장이 되기 쉽다. 반면 “추천 근거를 사용자에게 표시하고, 추천을 거부해도 서비스 이용이 가능하도록 구현”이라고 쓰면 책임의 범위가 달라진다. 업무 분해는 일을 작게 만드는 기술이면서, 책임이 흩어지거나 숨지 않도록 경계를 그리는 기술이다.

공학 사례: 사고 대응을 책임의 지도처럼 설계하기

의료 예약 시스템에서 환자의 예약 우선순위를 자동으로 정한다고 해 보자. 모델은 증상, 병원 사정, 대기 시간 등을 참고해 순서를 추천한다. 어느 날 한 환자의 긴급도가 낮게 평가되어 진료가 늦어진다. 사후에 “모델이 틀렸다”고 말하는 것만으로는 아무것도 복구되지 않는다. 필요한 것은 당시 시스템이 어떤 입력을 받았고, 어떤 버전의 모델이 어떤 확률을 계산했으며, 어떤 규칙이 그 결과를 최종 순서로 바꾸었는지 재구성하는 일이다.

이를 위해서는 모델 버전과 입력 데이터의 출처를 기록해야 한다. 추천값과 확정값을 분리해야 한다. 사람이 모델의 추천을 수정했다면 수정 전후와 이유를 남길 수 있어야 한다. 확신도가 낮거나 특정 조건이 충족되면 자동 처리를 멈추고 전문가 검토로 넘기는 경로도 필요하다. 중요한 것은 로그를 많이 쌓는 것이 아니다. 나중에 누군가 “그때 왜 그렇게 결정했는가”라고 물었을 때, 시스템이 대답할 수 있도록 기록의 의미를 설계하는 것이다.

사고 대응 절차도 책임의 일부다. 장애가 발생했을 때 가장 먼저 원인을 찾으려 하면 사람들은 로그를 지우거나 불리한 사실을 숨길 수 있다. 반대로 피해 확산을 막는 조치, 사용자 고지, 데이터 보존, 독립적인 검토를 순서대로 정해 두면 조직은 비난보다 복구를 먼저 선택할 수 있다. 책임 있는 시스템은 실수하지 않는 시스템이 아니라, 실수가 생겼을 때 누가 무엇을 해야 하는지 모호하지 않은 시스템이다.

반론과 긴장: 책임을 쪼개면 누구도 책임지지 않지 않는가

책임을 여러 층으로 나누자는 말에는 위험도 있다. 개발자는 데이터 팀을 가리키고, 데이터 팀은 기획을 가리키고, 기획은 고객의 요구를 가리키며, 결국 책임은 안개처럼 흩어질 수 있다. 모든 사람이 조금씩 책임진다는 말이 아무도 책임지지 않는다는 말로 변하는 순간이다.

그래서 분산된 책임에는 최종적인 책임 주체가 필요하다. 각 팀의 기여를 명확히 기록하되, 시스템 전체를 운영하고 중단할 권한을 가진 사람이 있어야 한다. 또한 책임은 처벌만을 뜻하지 않는다. 수정할 권한, 설명할 의무, 피해를 복구할 자원, 다시는 같은 오류가 반복되지 않게 바꿀 시간까지 포함한다. 책임을 코드에 저장한다는 것은 사람을 코드로 대체한다는 뜻이 아니다. 오히려 코드와 조직 사이에서 사라지기 쉬운 판단의 흔적을 다시 사람에게 돌려주는 일이다.

마무리 질문

당신의 시스템이 내일 사고를 일으킨다면, 로그를 보고 “무슨 일이 일어났는가”는 설명할 수 있는가. 더 나아가 “누가 무엇을 알고 있었고, 어느 순간 무엇을 바꿀 수 있었는가”까지 말할 수 있는가. 책임을 특정 개인에게 떠넘기지 않으면서도 아무도 책임에서 빠져나가지 않게 만드는 구조를, 당신은 업무 티켓과 코드 리뷰와 배포 절차 안에 심어 두었는가?