- Tags:
- #Reflection
- Date:
미움받을 용기
회사를 그만뒀다. 입사한 지 약 1년 만이었다. 한 회사에 들어가면 대개 3년 가까이는 다니던 나로서는 이례적인 일이었다.
나는 아직도 그곳에서의 시간을 되짚는다. 의식해서 꺼내는 기억은 아니다. 아마 나의 경우에는 직장에 관한 가장 최근의 기억이 그곳에 멈춰 있어서일 것이다. 떠난 회사가 그립거나 지나간 일에 괴롭지는 않다만, 끝내 매듭짓지 못한 일이 있었다. 과거에 오래 머무는 일은 사람을 조금씩 좀먹는다.
이 글은 가장 최근의 직장에서 겪은 일과 그 안에서 내가 기울였던 노력에 대한 기록이다. 누군가를 비난하거나 팀에 해를 끼치려는 뜻은 없다. 반대로 내게 상처를 준 행동에도 악의가 있었다고 믿지 않는다.
그렇다고 모든 일을 나의 문제로 축소하고 싶지도 않다. 내가 놓였던 환경에는 분명 결함이 있었고, 그 결함은 사람들의 판단과 관계를 오래 흔들었다. 그리고 나 역시 그 안에서 현명하게 행동하지 못했다. 나는 그 현상을 어떻게 읽었고 또 무엇을 고치려 했는지를 적으려 한다. 함께 일했던 동료에게는 변명처럼 들릴 수도 있다. 그래도 괜찮다. 이 글은 내 마음이 편해지고자 쓰는 기록이니까.
회사·제품·팀 이름은 언급하지 않았고 개인을 특정할 수 있는 정보는 최대한 덜어냈다. 우연히 이 글을 마주칠 가능성을 줄이기 위해 검색 엔진에서도 숨겨두었다. 이 이야기가 억측이나 부정적인 소문으로 번지는 일은 바라지 않는다. 끝에 이르러서 어떤 문제들은 분명 해결되고 있었고, 그 시간을 내가 버티지 못했을 뿐이다.
갈증
한때 나는 어느 스타트업의 창업 멤버로 일했다. 세 번째 직장이었고, 산업기능요원을 마친 뒤 온전히 내 뜻으로 고른 첫 회사였다. 남들이 빅테크를 원할 때 나는 스타트업에 끌렸다. 회사가 망하면 그때 떠나도 된다는 가벼운 마음도 있었다. 그러나 그곳에서 보낸 4년은 나를 깊게 바꾸었다. 과분할 만큼 많은 일을 겪었고, 많은 것을 배웠다. 오늘의 이야기는 그 회사에 관한 것이 아니므로 이쯤 해둔다.
갈증은 여전했다. 내가 무엇을 모르는지조차 모른다는 사실이 날 불안하게 했다. 회사가 계획하는 변화는 정말 옳은가. 그렇게 판단하는 나 자신을 믿어도 되는가. 자신은 있었지만 확신은 없었다. 나는 대체로 시행착오 속에서 직접 부딪치며 배워 왔다. 당시 나는 회사의 방향을 결정할 수는 없어도 리더로서 일을 벌일 수 있는 위치에는 있었는데, 모든 걸 떠먹여줄 환경 따위는 내게 필요하지 않았지만, 이번에는 실패를 경험이라 부르기에는 대가가 컸다. 스타트업의 파티는 끝났다며 모두가 생존에 집중하던 시기였다.
이번만큼은 내가 해보지 못한 일을 해내는 사람들 곁에서 배우고 싶었다. 나보다 나은 전문가가 필요했다. 그래서 한동안 소홀했던 본업으로 돌아가기로 했다. 새 환경에서는 엔지니어링으로 기여하면서 더 넓은 일을 배울 수 있으리라 생각했으니. 그때의 나는 그것을 준비된 제너럴리스트가 되기 위한 선택이라고 믿었다. 이런 상황까지 오게 만든 것에 대한 원망도 있었지만, 지금 돌이켜 보면 나는 상황을 개선할 수 없다는 무력감에 책임을 버리고 도망친 것이었을지도 모른다.
기대
그 무렵 인터뷰 과정이 인상적인 회사가 있었다. 이렇다 할 특별함은 없었지만, 다시 면접관이 되면 이를 모방하겠다는 생각이 들었던 회사. 질문은 어렵지 않았지만 과정은 촘촘했다. 꽤 공을 들였겠지. 이런 회사라면 인재 밀도도 높으리라 짐작했다. 나름 신중한 선택이었다.
회사가 풀고 있는 문제도 마음에 들었다. 더 깊이 알고 싶은 도메인이 둘 있었는데, 온보딩이 끝나기 전에 그중 하나를 맡은 스쿼드에 배치될 것이라는 말을 들었다. 레거시라는 이야기도 스쳐 들었지만 내게는 문제가 아니었다. 오히려 잘됐다고 생각했다. 내가 할 일이 많다는 뜻이기도 하니까.
절차에 따라 나는 온보딩을 마칠 때까지 어느 스쿼드에도 속하지 않은 채 기다려야 했고, 온보딩은 약 4주 동안 이어졌다. 플레이북 곳곳에는 정성 들인 흔적이 남아 있었다. 오래된 정보가 더러 섞여 있었고 신입과 주니어를 상정한 절차라 지루하다는 생각도 들었지만, 온보딩 메이트와 조정하면 될 일이었다. 오랜만의 시니어 엔지니어 채용이라고 했으니 그럴 만도 했다. 그 작은 조정 외에는 모든 일이 무난해 보였다. 앞으로의 회사 생활을 기대하고 있었다.
그때 조금만 더 일찍 사정을 알았더라면, 혹은 다른 팀으로 갔더라면 나는 지금도 그 회사에 남아 있었을까. 답할 수 없는 무의미한 질문이다.
교착
스쿼드에 합류했을 때 그곳에는 나의 매니저도 있었다. 첫인상은 좋았다. 구성원들은 밝았고 분위기도 화기애애했다. 적어도 새로 들어온 내 눈에는 아무런 문제가 없어 보였다. 낯을 가리는 나만 적응하면 될 일이었다.
나중에 알게 된 실상은 달랐다. 내가 들어갈 무렵 스쿼드는 거의 1년 동안 이렇다 할 성과를 내지 못하고 있었다. 회사의 많은 사람이 사정을 알고 있었지만 신규 입사자인 내게는 말해줄 사람도, 내가 먼저 알아차릴 방법도 없었다. 온보딩 기간에 접근할 수 있는 문서는 대부분 읽었지만, 그런 갈등까지 애써 기록으로 남길 리 없었다. 애초에 기록을 잘 남기는 팀도 아니었다.
팀이 풀려던 문제는 그때는 맞았지만 지금은 틀린 의사결정이 제품에 남긴 흔적을 걷어내는 일이었다. 스타트업 씬의 흔하디흔한 MVP 성공담과는 달리, 오래된 서비스에서는 종종 있는 일이었다. 오히려 속도만을 좇다 보면 쉽게 이런 상황에 놓이게 된다. 팀은 서로 다른 고객의 요구를 함께 품을 수 있는 새로운 경험을 만들어야 했고, 오래된 서비스에 쌓인 문제는 며칠이나 한두 번의 스프린트로 사라질 수 없었다. 무엇을 나누고 어디서부터 바꿀지 수없이 판단해야 했다.
계획은 끝내 실행되지 못했다. 처음 들었던 이유는 회사가 팀의 계획에 동의하지 않는다는 것이었다. 그도 그럴 것이, 팀은 기존 사용자 경험을 파괴하고 처음부터 다시 만들기를 원했다. 실은 팀 내에서도 의견이 분분했는데, 완성된 그림에는 모두가 동의했지만 그곳에 도달하는 방법을 두고 의견이 갈렸다.
Product Owner는 가설을 하나씩 검증하기보단 급진적인 변화를 원했다. 그동안의 데이터가 이미 가설을 증명한다는 이유였다. PO가 강한 확신이 있다면 나는 존중할 테지만, 변화의 크기를 고려하면 위험한 접근인 것은 분명했다.
반면 동료들은 징검다리가 필요하다고 판단했다. 접근은 크게 두 갈래로 나뉘었다. 기존 데이터도 호환되는 사용자 경험으로 점진적 개선, 그리고 데이터 마이그레이션. 가설이 적중하더라도 고객에게 그동안 쌓아온 데이터를 모두 버리라고 하는 것은 결코 납득하기 어려웠는데, 좋은 사용자 경험이 항상 좋은 고객 경험을 만드는 것은 아니기 때문이다. 이미 사용자가 있는 제품의 핵심 기능이었다.
회의에는 타협할 수 없는 변화와 그에 대한 반대만 남았다. 이 문제를 책임지고 이끌 사람도 없었다. Disagree and Commit. 반대를 남겨둔 채 누군가의 결정에 힘을 싣는 선택이 필요했지만, 팀에서는 화합과 만장일치가 양보할 수 없는 원칙처럼 작동했다. 모두가 동의하지 않으면 누구도 움직일 수 없었다. 모든 고객에게 배포하지 않고도 작은 실험을 시작할 방법을 찾았어야 했지만, 우리에게는 끝없는 갈등의 굴레에서 빠져나올 장치가 없었다. 어쩌면 이 조직의 모두가 그랬을지도 모른다.
나 역시 이상함은 느꼈다. 다만 이것이 거의 1년째 이어진 교착이라는 사실을 몰랐던 나는, 한 번쯤은 이 팀이 몰입해서 일하는 모습을 관찰하고 싶었다. 결코 소극적인 태도는 아니었지만, 문제를 보고도 개입의 빈도를 의식적으로 줄였다. 이미 다른 동료들보다 자주 개입한다고 느꼈기 때문이다. 충분한 기다림이 있어야 상황을 이해할 수 있고, 이해한 뒤에야 제안할 수 있다고 믿었다. 결과적으로 오판이었다.
그렇다고 손을 놓고 있던 것은 아니다. 내 커리어에서 다섯 손가락에 꼽을 레거시였기에 나는 시급한 기술 부채부터 맡겠다고 했고, 그동안 PO가 팀을 움직일 만큼 제품의 방향을 정리해 오리라 믿었다. 더욱이 그가 원하는 대로 제품 부채를 청산하려면 거기에 얽힌 기술 부채부터 풀어야 했다. 직무의 경계를 가리지 않는 편이지만, 직업인으로서 각자 전문성을 발휘해야 할 분야는 달랐다.
동료들도 마찬가지였다. PO가 자신의 문제에 묶여 있는 동안 각자 자신이 주도할 수 있는 일을 찾기 시작했다. 그렇게 팀은 당장 해결할 수 있는 문제라도 시도해 보기로 했다.
새 문제를 찾으면서 스쿼드는 비로소 움직이기 시작했다. 나로서도 기대하던 순간이었다. 이 팀이 어떻게 일하는지 이제야 온전히 이해할 수 있을 테니. 어쩌면 앞으로 나아가는 경험을 통한 자신감이 오래된 교착도 풀어낼지 모를 일이었다.
하지만 무엇을 만들지 정한 뒤에는 곧바로 구현을 서둘렀다. 왜 지금 이 문제를 풀어야 하는지, 어째서 이 아이디어를 골랐는지, 무엇을 보면 성공과 실패를 가를 수 있는지 충분히 묻지 않았다. 검증 계획도, 구현한 뒤 실제로 검증하는 과정도 희미했다. 기능은 늘었지만 다음 판단에 쓸 것은 남지 않았고, 이건 그저 Feature Creep에 불과했다. 오늘날의 AI Slop과도 닮아 있었다. Product Owner의 공백이 여실히 드러나는 순간이었다.
형식은 이미 갖추고 있었다. 스프린트라는 이름이 있었고, 그에 딸린 회의와 도구도 있었다. 정작 왜 그런 주기로 일하며 각각의 회의에서 무엇을 얻어야 하는지는 이해가 공유되지 않은 듯했다. 어느 날 한 동료가 스프린트가 무엇인지 설명하는 자리를 마련했다. 정확한 계기는 기억나지 않지만 진행 과정에서 문제가 있었던 것은 확실하다. 그리고 설명을 들은 동료들은 이제야 왜 스프린트를 하는지 알겠다는 후기를 남겼다. 미팅은 성공적이었다. 그 사실 때문에 나는 더 당황스러웠다. 그동안 이들이 반복해 온 것은 무엇이었을까.
그제야 머릿속의 퍼즐이 맞춰졌다. 흩어져 있던 현상들이 하나의 위험 신호로 보이기 시작했다. 합류한 지 이미 수개월이 지난 뒤였다. 나는 이 스쿼드가 지금 겪는 문제를 스스로 해결하고 자생할 수 없는 팀이라고 결론지었다.
생존
이곳은 내가 찾아다니던 환경이 아니었다. 이전 직장을 떠나 얻은 것이 연봉 하나만은 아니어야 했다. 그렇다고 문제가 보이자마자 도망치고 싶지도 않았다. 팀을 계몽해서라도 어떻게든 살아남게 해야 했다. 동료를 대상으로 계몽이라는 단어를 떠올린 순간부터 이미 잘못이었지만, 그때는 어쩔 수 없었다.
나는 우선 멈춰야 한다고 주장했다. 근거도 검증도 없이 기능을 더하는 일은 전진이 아니었다. 과거의 결정이 남긴 흔적을 걷어내겠다면서 옳은지 확인할 수도 없는 기능을 다시 쌓는 것은 모순이었다. 더 만들기 전에 무엇이 어긋났는지 돌아봐야 했다.
동의는 얻지 못했다. 이미 오래 성과를 내지 못한 팀이었고 구성원들은 저마다의 압박 속에 있었다. 내게는 몇 달이었지만 그들에게는 더 견디기 어려울 만큼 긴 시간이었다. 그러니 멈추자는 말은 유일하게 붙들고 있던 진척마저 포기하자는 이야기로 들렸을 것이다. 더욱이 이 회사에서는 메이커가 빠른 기능 구현 외의 일을 주장하는 것만으로도 부정적인 반응을 사기 쉬웠다. 같은 직무의 매니저조차 기술 부채를 무시해 왔으니, 그들의 사전에 멈춤이라는 선택지는 없었을 것이다.
다음으로 PO와 따로 이야기했다. 동료들이 그의 의견에 생각만큼 힘을 실어주지 않는 것 같다고 말했다. 물론 그의 의견에 전부 동의해서는 아니었지만, 내 말은 진심이었다. 아무도 결정을 내리지 못하는 가운데 적어도 한 사람은 같은 편에 서야 한다고 판단했다. 예상과 달리 그는 동료들에게 실망하지 않았다고 했다. 어쩌면 함께한 시간이 짧은 내가 섣불리 단정했는지도. 다만 그가 너무 많은 책임과 부담을 혼자 지고 있다는 점은 분명해 보였다. UX 디자이너도 나와 같은 생각이었다.
그래서 워크숍을 열었다. 내가 제안하고 주도했다. 명목은 앞으로 나아갈 길을 찾는 일이었지만, 내게는 팀을 잠시 멈춰 세우고 지나온 길을 돌아보게 하려는 시도였다. 그동안의 회고는 늘 서로를 격려하는 데서 그쳤는데, 지금 필요한 것은 무엇이 잘못됐는지를 함께 직시하는 회고였다. 멈추자고 직접 말하는 대신 며칠을 워크숍에 쓰자고 제안하면 거부감도 덜할 터였다. 우회는 효과가 있었고 제안은 받아들여졌다.
그러나 무엇 하나 내 뜻대로 흘러가는 일은 없었다. 동료들이 원한 것은 Product Discovery(아이디어 탐색)이었다. 그것도 일정에 있었지만, 아이디어를 찾는 일에 모두가 참여할 필요는 없다는 동료들의 말이 뒤따랐다. 모두의 참여와 화합, 만장일치가 원칙처럼 작동하던 팀이었지만, 그것은 어떤 개인에게서 시작된 무언의 압박이었을 뿐. 동료들은 나와 있을 때만큼은 각자 할 수 있는 일을 하고 싶다며 솔직히 말했다. 왜 하필 지금일까, 난감한 일이었다. 그들의 주장은 틀리지 않았다. 적절한 분업이야말로 효과적인 협업이고, 모든 일에 모두가 참여해 온 방식이 오히려 우직하다 못해 무식했다. 다만 이 워크숍만큼은 팀 전체가 함께해야 했는데, 모든 회의에 참여하며 같은 논의를 수없이 반복한 동료들은 자신이 더 보탤 것이 없다고 느꼈을 것이다. 그들의 고통을 이해하지 못하는 것도 아니었다.
문제를 어디서부터 짚어야 할지 모를 만큼 지쳐버렸다. 내가 너무 늦었는지, 반대로 성급했는지. 정말 상황만이 이들을 이렇게 만든 것일까? 노련한 조직이었다면 처음부터 이런 일은 없었을지도. 몇 달째 진척이 없는 상황은 내게도 큰 피로를 안겼고, 바닥난 인내심은 사고를 차단하기 시작했다. 전부 갈아엎고 싶었다. 개입을 줄였다고 해서 가만히 있었던 것은 아니다. 문제를 있는 그대로 언급하고 변화를 요구하는 것만으로 문제를 해결할 수 있는 팀이었다면 진작 해결되었을 것이다. 발품 팔며 한 사람씩 신뢰를 쌓고 변화를 유도하는 것은 내게 익숙한 선택지였지만, 그런 느린 방법으로 해결할 만큼 두고 볼 문제는 아니었다. 팀은 스스로 상황을 악화시키고 있었고, 나보다는 그들 간의 유대가 더 강했기 때문이다. 차라리 내가 리더로 합류했더라면. 리더십은 상황에 맞는 유형과 태도를 선택하는 일이니 1년째 멈춰 있던 팀의 일하는 방식을 강제로 정해주는 것쯤은 정당하다고 봐야 했다.
이제 와 워크숍을 무를 수는 없었다. 팀 전체의 회고보다 아이디어 탐색의 결과를 만드는 쪽으로 목적을 조정했다. PO의 짐을 덜어내는 것도 목표 중 하나였으니 이 일만큼은 해내야 했다. 참여자는 Product Owner와 UX 디자이너, 그리고 프로덕트 엔지니어인 나까지 셋이었다.
우리는 우선 기존 제품의 흐름을 모두 시각화했다. PO가 붙들고 있던 큰 문제를 잘게 나누고 무엇부터 검증할지 정리해 나갔다. 그가 원하는 그림은 선명했기에, 어떤 단위로 진행해야 안전하게 갈 수 있는지가 중요했다. 그래야만 동료들도 납득할 수 있었다. 혼자 감당하던 생각을 밖으로 꺼내 함께 고민하기 시작했다는 것만으로도 상황은 이전과 달라 보였지만, 시간이 갈수록 PO는 의욕을 잃어가는 듯했다. 이유를 내가 단정할 수는 없다. 워크숍은 끝까지 이어졌지만 마무리는 개운하지 않았다. 다음으로 나아갈 발판을 만든 것인지, 끝내지 못한 일을 하나 더 남긴 것인지 알기 어려웠다.
머지않아 그는 퇴사했다. 마지막 회식 자리에서 그는, 자신이 고집을 부렸다며 그간의 행동을 사과했다. 그는 떠나기 전에 자신의 잘못을 인정했다. 그것만으로도 나보다 나았다. 나는 나중에 내가 저지를 잘못에 대해 그러지 못했으니까. 나의 시도는 결실을 맺지 못한 채 멈췄다. 곧이어 조직 개편이 예고됐다. 구체적인 날짜를 정하지는 않았지만, 나는 이곳을 떠나기로 마음먹었다.
균열
이때쯤부터 나는 냉소적으로 변하기 시작했다. 스쿼드만으로도 충분히 지쳐 있었는데, 동시에 나를 집요하게 괴롭혀 온 문제가 하나 더 있었기 때문이다. 때는 다시 내가 스쿼드의 교착을 알지 못했던 때로 돌아간다.
매니저와의 갈등은 코드 리뷰에서 먼저 드러났다. 내가 그의 코드에 리뷰를 남기면 대화는 대개 서로의 논리를 반박하는 댓글로 이어졌다. 토론 자체는 문제가 아니었다. 코드 리뷰는 다른 판단을 드러내고 더 나은 결론을 찾으며 서로 배우는 자리이기도 하다. 지나치게 긴 토론으로 병목을 만든 일도 기억하기로는 세 번 남짓이었다. 겉보기에는 괜찮아 보였던 이유는 그가 답변 없이 PR을 병합하거나, 이미 구현했으니 다음에 개선하자며 내가 먼저 대화를 끝냈기 때문이다. 문제는 길이가 아니라 반복이었다.
전에 남긴 것과 같은 리뷰를 남기고, 전에 내세운 것과 같은 논리로 거절당했다. 일련의 과정은 우리 둘 사이에서 끝나지 않고 챕터 회의에서도 되풀이됐다. 나에게 의사결정은 일종의 스포츠이다. 어떤 기준으로 겨루든, 끝내 반박할 수 없다면 결과를 받아들이는 일이라고 믿는다. 그러나 그는 그런 방식으로 행동하지 않았다. 늘 같은 질문에 나는 답을 했으나, 그에게는 만족스러운 답이 아니었는지 서둘러 자리를 떠났다. 이는 사실이지만 그렇다고 내가 옳았다는 뜻은 아니다. 다만 같은 논쟁이 아무것도 바꾸지 않은 채 되돌아오는 이유를 이해할 수 없었다. 만약 그가 일부러 말을 아꼈다면 지금처럼 부딪히는 일은 없어야 했다. 현재의 코드에서 볼 수 있는 나쁜 결과는 사실 그와 팀이 기존의 방식만을 고수해서 얻은 것인데, 그는 원인을 비즈니스의 복잡성이나 다른 데에서 찾는 모습을 보였다. 그에게는 처음부터 코드가 원인이 아니었으니 어쩌면 이 대화는 성립조차 할 수 없었을 것이다. 그렇게 챕터의 갈등은 지속되었고, 이 갈등이 스쿼드의 수면 위로 올라온 것은 내가 기술 부채를 해결하겠다고 선언하면서부터였다.
우리가 맡은 제품에는 오래된 결함이 있었다. 스쿼드의 이름과도 같은 핵심 개념(이 글에서는 ‘별’이라 부르겠다)하나를 보여주기 위해 고객사의 모든 별을 가져왔다. 별 하나를 수정한 뒤에도 모든 별을 다시 불러왔다. 고객사에 따라 별은 수만 개에 달했는데, 브라우저가 멈추는 일이 반복되어 고객에게는 별을 여러 묶음으로 나누어 관리하라고 안내하고 있었다. 일부 고객은 그 해결책을 납득할 수 없었고, 이 문제를 해결하는 것이 계약 연장 조건이기도 했다.
처음부터 잘못된 설계였다고 말할 수는 없다. 과거에는 고객사별로 관리할 별의 수가 많지 않으리라 예상했다고 들었다. 제품 초기에는 완성도보다 빠른 구현이 중요했을 테니 당시에는 합리적인 결정이었다. 문제는 수요를 검증해 전제가 달라진 뒤에도 수년 동안 같은 구조를 유지했다는 데 있었다.
데이터의 형태와 쓰임은 더 심각했다. 전체 별 목록을 보여주려고 만든 API 하나로 모든 기능을 구현했다. 응답값을 용도별로 가공하는 수백 줄짜리 계산 함수가 여럿 생겼고, 함수와 타입의 이름만으로는 무엇을 위한 데이터인지 알기 어려웠다. 의도를 잃은 함수가 여러 곳에서 쓰이는 탓에 새로운 프로퍼티가 계속 붙었다. 데이터가 어디에서 와서 어디로 흐르는지 이해하기 어려워졌다. 진작 API를 용도별로 나누고 각 계산을 어느 쪽에서 맡을지 책임을 구분했어야 했다.
문제를 알리지 않았으니 동료들은 아무런 문제가 없는 줄 알았다. 훗날 조직 개편을 거치며 이 사실을 알게 된 백엔드 엔지니어들조차, 왜 프론트엔드 엔지니어가 말없이 이런 구조로 구현했는지 납득하지 못했다.
보통의 나쁜 코드는 해당 기능을 고칠 때 함께 손보면 그만이다. 문제없이 동작한다면 굳이 건드릴 이유도 없다. 하지만 별은 우리가 맡은 도메인 그 자체였다. 이 구조는 이미 존재하는 기능뿐 아니라 앞으로 만들 모든 기능에 제동을 걸었고, 작은 변경의 영향은 거의 모든 페이지로 번졌다. 제품 부채를 해결하려면 거기에 얽힌 기술 부채를 먼저 다뤄야 했다.
엔지니어로서 외면할 수 없는 문제다. 직업인이라면 자신이 지켜야 한다고 믿는 가치를 주장하되, 필요하면 이를 포기하고 팀에 가장 도움이 되는 방향으로 결정해야 한다. 그러니 반드시 지금 전부 고치자는 뜻은 아니었다. 해결을 미루더라도 문제가 있다는 사실은 모두가 알아야 했다. 그래야 팀이 우선순위를 판단하고 적절한 때에 해결할 수 있다. 돈이 되는 일만 좇으며 모르는 척하는 것은 판단이 아니라 방관이었다. 나보다 한 달쯤 늦게 들어온 주니어 엔지니어도 같은 문제를 느끼고 있었다.
그러나 매니저는 반대했다. 이유를 분명히 듣지는 못했다. 나는 그에게 시니어 엔지니어에게 무엇을 기대하는지, 직무를 떠나 내게 어떤 역할을 기대하는지 물었다. 그는 잠시 생각했지만 기능을 빠르게 만드는 것 이상의 답을 내놓지 못했다. 그 답만으로 그의 생각 전체를 단정할 수는 없다. 미리 정리해 두지 않았을 수도 있고, 이미 흔들리던 스쿼드의 사정을 신규 입사자에게 말하기 어려웠을 수도 있다. 이미 무너져가는 것을 알고 있던 팀에 굳이 신규 채용으로 나를 배치한 이유가 분명 있었을 테니까.
이번만큼은 강하게 밀어붙여 동의를 얻었다. 그래야만 했다. 나는 팀이 요구한 제품 개선을 먼저 실행했고, 그 경험을 근거로 다시 주장했다. 문제의 맥락을 모르는 다른 직무의 동료들에게는 내가 제품을 두고 코드 품질에 매달리는 사람처럼 보였겠지만, 나 역시 갈등 속에서 그 맥락을 충분히 나누지 못하는 고충이 있었다. 그렇게 일주일을 온전히 리팩터링에 쏟았던 것으로 기억한다. 이것이 그가 내 주장에 물러선 마지막 일이었다.
단절
매니저와 부딪히던 사람은 나만이 아니었다. 다른 시니어 엔지니어도 챕터 미팅과 긴 토론 스레드에서 매니저와 반복해 충돌하고 있었다. 그가 자신의 리팩터링 PR을 챕터를 대상으로 발표했던 일이 인상 깊었다. 이론으로 납득시키기는 어려우니 매 순간의 판단을 커밋에 기록하여 어떤 사고로 코드를 작성하는지 설명하는 시간을 가졌고, 그것이 실제로 어떤 결과를 가져다주는지도 볼 수 있었다. 나는 그 동료를 본받아 똑같이 발표했다.
반응은 예상보다 미지근했는데, 평소 같았다면 다시 충돌했을 매니저는 두 번의 미팅 모두, 듣기만 하다가 별다른 의견 없이 자리를 떠났다. 어떤 이유로든 그는 이 문제를 나와 같은 수준으로 보고 있지 않다는 사실은 분명해 보였다. 이전부터 품고 있던 의심이 확신에 가까워졌고, 갈등은 그대로 침묵으로 남았다.
리팩터링 하나가 끝났다고 기술적인 문제가 사라진 것은 아니었다. 우리 스쿼드가 맡은 영역이 특히 심각한 레거시였을 뿐, 제품 전체의 코드베이스도 좋은 상태와는 거리가 멀었다. 나쁜 코드가 있다는 사실 자체는 괜찮다. 과거에는 합리적이었던 판단이 부채로 남을 수도 있고, 당시의 우선순위 때문에 미뤄둔 부채도 있을 수밖에 없다. 개인의 역량 부족도 죄는 아니다. 문제는 같은 현상이 반복돼도 피드백을 거부하고, 이를 누구도 막지 못한다는 데 있었다. 어지르는 사람 따로, 치우는 사람 따로였다. 우리에게는 합의된 기준도, 이견을 판정할 경로도, 피드백을 돌려줄 장치도 없었다.
개인이 뒤따라다니며 수습하고 변화를 이끌어내는 것보다 리더가 개입하는 편이 빨랐다. 그러나 그 역할을 맡은 챕터 리드이자 매니저는 애초에 이를 문제로 여기지 않았고 동료들의 요구에 대체로 반대했다. 코드 리뷰에서도, 챕터 미팅에서도, 코드가 아니라 문제 인식을 이야기할 때도 그랬다. 매니저의 지속되는 반대 때문에 힘들다는 내 말에도 그는, 자신이 줄곧 반대해 왔다는 사실을 인지하지 못하는 듯했다. 오히려 그는 시니어는 문제 해결을 주도하고 동료들을 이끌어야 한다는 말과 함께 unlearning을 요구했고, 나는 감정을 참지 못해 처음으로 언성을 높였다. 동료들을 이끌지 못한 게 아니었다. 이 반대는 오로지 한 명의 것이었다. 매니저의 적극적인 반대가 동료들의 입을 닫게 만들었다는 걸 그는 몰랐던 걸까? 이런 방식으로는 그와 아무리 더 이야기한들 서로에게 닿지 않으리라는 생각이 들었다.
결국 그를 포기했다. 어차피 서로가 피드백을 받아들이지 못하니 그냥 무시하기로 했다. 상황은 나아지지 않았지만 적어도 내 정신 건강에는 그편이 나았다. 주기적으로 있던 원온원도 바쁘다는 이유로 미루거나, 아무 문제도 없다며 말을 줄였다. 손가락 마디만 한 원형 탈모는 덤이었다.
시간이 지나 그는 갑자기 다른 스쿼드로 떠났다. PO가 퇴사한 것과 비슷한 시기였다. 그가 떠난 뒤 나와 주니어 엔지니어는 비로소 스쿼드의 기술 부채를 어떻게 다룰지 함께 고민하기 시작했다. 모든 것을 당장 고치려는 계획은 아니었다. 지금까지 쌓인 문제를 팀이 제어할 수 있도록 어떤 순서와 전략으로 풀어갈지 정하는 일이었다.
그 시도는 결과적으로 성공했지만, 나는 스쿼드와 챕터 어디에서도 소속감이나 안정감을 얻지 못했다. 나를 제외하고 유일한 시니어 엔지니어였던 동료도 곧 회사를 떠났다.
전환
이상 신호는 회사 밖에서도 나타났다. 쉽게 해결할 수 없는 문제들로 제품 품질은 날이 갈수록 떨어진 끝에 회사는 결국 고객의 날 선 비판을 들어야 했다. 구체적인 내용은 적을 수 없지만 결코 지나칠 수 있는 수준은 아니었다. B2B 고객이 이토록 거칠게 말하는 일은 드물다는 점을 생각하면 더 그랬다.
사건의 순서는 한 방향을 가리켰다. 회사는 오랫동안 ‘기능의 빠른 구현’을 좋은 성과와 같은 말로 여겼고, 그 밖의 일을 주장하는 사람은 제품보다 다른 가치에 집착하는 사람처럼 보이기 쉬웠다. 메이커의 존재 의의는 무언가를 구현하는 데 있지만 그것이 전부일 수는 없다. 처음에는 몇 사람의 태도에서 비롯된 일이라고 생각했지만 분명 회사의 문화였고, 왜 이런 문화가 생겼는지는 누구도 정확히 알지 못했다. 도입기에 통했던 성공 방식이 제품과 조직이 달라진 뒤에도 철칙으로 남았으리라고 나는 추정했다.
조직 개편 전후로 나쁜 결과가 잇따르고 나서야 회사도 방향을 바꿨다. 슬랙에 전략 변경이 공지됐고, 앞으로 스쿼드와 메이커의 행동 양식도 달라질 것이라는 설명이 붙었다. 돌이킬 수 없을 만큼 늦지는 않았지만 분명 늦은 변화였다.
나는 전부터 이 문제를 주장해 왔다. 이제는 사라진 스쿼드의 경험에서 시작된 문제의식이었다. 우리 팀만의 문제라고 치부하기에는 설명되지 않는 현상이 다른 곳에서도 보였다. 대체로 받아들여지지 않았지만 동의하는 리더도 있었다. 문제가 방치되는 것이 여전히 답답했지만, 내가 모르는 곳에서 변화를 준비하는 데 시간이 필요했을지도 모른다. 내 판단이 여전히 옳다면 이번에는 설득을 위한 더 많은 근거가 필요했다. 영역을 넓혀 다른 직무의 동료들에게 이야기를 들었다. 흩어진 현상의 인과관계를 연결해 악순환을 찾고, 시스템 원형과 인과 루프 다이어그램으로 정리했다.
그러나 일이 이미 벌어졌고, 나는 완성하지 못한 분석 자료를 공지의 답글에 곧바로 붙였다. 예견한 결과를 막을 수는 없었지만, 그 결정이 왜 필요했는지, 이미 벌어진 일을 되풀이하지 않으려면 무엇을 바꿔야 하는지 논의할 근거는 될 수 있었다. 여러 미팅과 원온원이 뒤따랐다. 받아들여지지 않던 문제를 두고 비로소 대화가 시작됐다.
전략 변경 하나로 모든 문제가 곧바로 풀릴 것이라 기대하진 않았다. 맡은 일은 그대로였지만 조직 개편으로 스쿼드 구성원의 절반 이상이 바뀌었고, 우리는 처음부터 다시 합을 맞춰야 했다. 이는 오랜 시간이 필요한 일이었고 나는 이미 지독하게 지쳐 있었다. 그래도 이번 스쿼드는 문제가 생기면 함께 들여다보려 했다. 실행하기 전에 문제를 함께 살필 수 있다는 것만으로도 이전과는 달랐다.
재건
우선 급한 불부터 꺼야 했다. 이전 스쿼드가 표류하는 동안에도 고객의 요구는 계속 쌓였고, 밀린 요구만 2년치였다. 풀어야 할 문제와 접근은 대체로 정해져 있었다. 내가 바라던 형태의 일은 아니었지만 대충 할 이유는 없었다.
계획을 실행한 뒤에야 미처 결정하지 않은 사항이 계속 드러났다. 이해와 공유가 부족했다는 증거다. 해보기 전에 모든 것을 알 수는 없으니 어쩌면 당연한 현상이다. 다만 정도가 심했다. 이미 아무 일도 하지 못하던 팀을 겪은 내게는 과거의 모습이 겹쳐 보였다. 사실 이전 스쿼드도 다르지 않았기 때문이다. 누구도 회색지대를 챙기지 않았지만, 각자가 공유 없이 임의로 판단해 처리하면서 문제를 숨겼을 뿐이었다.
다행히 새 팀은 그 현상을 그대로 두지 않았다. 구현을 시작하기 전에 해결하려는 문제와 기대 효과, 다른 작업과의 의존성을 먼저 확인하기로 했다. 어디까지 만들면 완료한 것으로 볼지 인수 기준을 맞추고, 누가 무엇을 실행할지도 정했다. 우리에게 맞는 Definition of Ready(준비의 정의)를 만들어 나갔다.
내가 주도했고 없는 것보다는 분명 나았다. 그러나 애초에 이런 상황이 벌어진다는 사실이 만족스럽지 않았다. 스타트업에서 직무의 경계를 넘는 것은 흔하지만 R&R 자체가 희미한 것은 다른 문제다. 이 회사에서는 직무별로 맡아야 할 책임을 정당히 요구하기조차 어려웠고, DoR은 그 문제를 해결하는 임시방편에 불과했다. 주어진 자율을 감당할 역량은 부족해 보였고, 나 역시 이처럼 세부적인 규칙이 있어야 겨우 돌아가는 조직은 경험한 적이 없었다. 현명함보다 당장의 개선 효과를 좇기 시작했고, 점차 이전 스쿼드의 동료들처럼 조급해지고 있었다.
급한 작업을 마친 뒤에야 제품·기술 부채를 다룰 시간이 생겼다. 전략 변경에 따른 PO의 제안이었다. 이전에 주니어 엔지니어와 세운 계획은 현실적인 타협이었다. Layered Architecture를 설계해 레거시와 격리된 틀을 만들고, 기능을 구현하다 레거시 코드를 건드릴 때 비로소 개선하여 새 구조로 옮기는 방식이었다. 그런데 갑자기 부채만 해결할 시간이 생기다니. 팀원 모두가 동의해 이런 결정을 내린 것은 이 회사에서 처음이었다.
앞서 매니저와 다투며 별(스쿼드의 핵심 개념)의 데이터 형태와 가공 함수를 분류하고 개선해 둔 덕분에, 우리는 모든 기능을 떠맡던 하나의 API를 용도별로 나누고, 대량의 별을 처리하던 가공 함수의 알고리즘을 고쳐 병목을 걷어냈다. 문제는 막연히 데이터가 많다는 데 있지 않았다. 고작 수만 개의 데이터를 대용량이라 부를 수도 없었다. 그저 구현 방식의 오류가 규모를 견디지 못하고 있었을 뿐이었다.
모든 별을 보여주는 UI의 렌더링 시간은 정상 범위 안으로 들어왔다. 수만 개의 별 앞에서 멈추던 브라우저가 더는 멈추지 않았다. 고객에게 별을 여러 묶음으로 나누어 사용하라고 안내할 필요도 없어졌다. 그 결과 스쿼드는 별을 한 묶음으로 관리하고 싶어 하던 꽤 큰 고객사의 계약 연장 조건도 받아들일 수 있게 됐다. 나에게는 이 회사에서 기존 기능의 기술적 문제를 해결해 비즈니스에 영향을 준 첫 사례였다.
분명 기뻐해야 할 결과였다. 그러나 기쁘지 않았다. 성취보다 여기까지 걸린 시간이 아까웠다. 새로운 경험으로 모든 문제를 한 번에 해결하려던 고집, 그리고 부채를 문제로 보지 않던 무지가 합쳐 고객을 오랫동안 불편하게 만든 셈이니까. 내게는 별일도 아닌 것을 질질 끌다가 이제야 해결했다는 생각뿐이었다.
회사도, 팀도, 제품도 뒤늦게라도 올바른 방향으로 움직이기 시작했다. 그러나 나는 그 움직임을 함께 기뻐할 수 없는 사람이 되어 있었다. 더는 마음의 여유가 없었고, 나는 퇴사를 결정했다.
매듭
퇴사일은 석 달 뒤로 잡았다. 나는 원래 퇴사일을 넉넉히 정하는 편이다. 마음을 준비하기에는 충분히 길고, 생각이 달라진다면 결정을 되돌리기에도 늦지 않은 시간이다. 회사에는 두 달 뒤 알리기로 했다. 재직한 지 정확히 1년이 되는 때였다.
떠날 날짜를 정하자 오히려 마음이 차분해졌다. 회사에 대한 기대는 더 남아 있지 않았지만 동료들까지 포기한 것은 아니었다. 떠나기 전에 무엇을 남겨야 회사에 도움이 될지 생각했다.
마지막으로 붙들고 있던 일은 크게 두 가지였다. 하나는 직업인으로서 동료들이 외부 도움 없이 코드를 제어하고 관리할 수 있는 기반을 남기는 것. 다른 하나는 리더십을 발휘해 회사의 구조적 문제를 분석하고 해결의 실마리를 남기는 것이었다. 후자는 앞서 말한, 근거를 모아 회사의 악순환을 정리한 작업이다. 구조의 문제가 풀리면 팀 안에서, 그리고 팀과 이해관계자 사이에서 생기는 문제는 팀이 스스로 풀 수 있으리라 생각했다.
먼저 통합·인수 테스트 환경을 만들었다. 지금의 코드는 변경의 영향 범위가 넓고 예측하기도 어려웠다. 내가 떠난 뒤에도 동료들은 이 낡은 코드베이스 위에서 계속 제품을 만들어야 했다. 동작이 망가졌는지 바로 확인할 방법이 필요했다.
E2E 테스트는 있었지만 빠른 작업 흐름에 맞지 않아 방치돼 있었다(E2E 테스트는 전략과 프로세스가 수반되지 않으면 대체로 안착에 실패한다). 기존 단위 테스트 환경을 그대로 쓸 수 있었다면 좋았겠지만, 소스코드는 여러 페이지에 걸친 동작을 테스트할 수 있는 형태가 아니었다. 스쿼드의 업무를 마치고 남는 시간마다 하나씩 리팩터링해야 했다. 그 탓에 정작 인수 테스트는 몇 개 쓰지 못했지만, 적어도 다음 테스트를 이어갈 수 있는 기반은 남겼다.
전략 변경은 챕터에도 영향을 줬다. 기술 부채를 해결하라는 지시와 여러 과업이 내려왔다. 나는 스쿼드에 새 아키텍처를 도입한 경험이 있다는 이유로 챕터의 코드 작성 패턴과 규칙을 통일할 기준을 만드는 TF를 맡았는데, 계획에 없던 일이었고 이미 바빴지만 무리해서라도 팀에 더 도움이 되고 싶었다.
그 과정에서 매니저는 의사결정의 균형을 맞춘다는 이유로 다른 TF에 참여하려던 동료 한 명을 우리 TF로 옮겼다. 그 동료는 신중해서 반대를 자주 하는 동시에 자신이 블로커가 될까 걱정하던 사람이었는데, 당시의 나는 매니저가 나를 믿지 못해 자기 편을 붙였다고 의심했다. 의도는 알 수 없지만, 이미 그를 불신하던 내게는 불쾌한 경험이었고 사실 여부를 확인할 마음도 없었다. 하루면 해결될 일도 오로지 한 사람을 설득하기 위해 한 달, 그리고 1년이 걸렸는데 좋게 보일 리가 없었다.
의도가 무엇이었든 우리는 결과를 만들었다. 챕터가 오랫동안 합의하지 못했던 좋은 코드와 설계의 기준, 이를 적용하는 방법, 실제 해결 사례를 문서로 남겼다.
이를 계기로 내가 스쿼드에서 해 온 개선도 알려졌다. 사실 진작에 공유했지만 TF의 결과물 덕분에 다시 이목을 끌었다. 매니저는 설계 의도와 효과를 발표해 달라고 요청했다. 다른 동료들도 원했다. 예전의 나라면 기다렸을 반응이었다. 그러나 반가움보다 ‘이제 와서 왜?’라는 생각이 먼저 들었다. 지금까지 반대만 했는데. 나는 칭찬조차 곧이곧대로 받아들이지 못하는 사람이 되어 있었고, 그런 나를 혐오하며 반드시 떠나야겠다고 다시 다짐했다.
용기
그렇게 두 달이 흘렀다. 나는 예정대로 퇴사 의사를 알렸다. 그동안의 갈등과 노력을 알아준 한 리더는 부서 이동을 권하며 나를 회유했지만, 마음은 바뀌지 않았다. 그때의 내게 지난 경험은 한 것도 배운 것도 없이 잃어버린 시간처럼 느껴졌다. 무언가 만들고 싶다는 억눌린 욕구를 회사를 떠나 1인 창업으로 풀고 싶었다. 그것이 현실 도피를 위한 자기합리화였을지도 모른다. 계획이 있다고 스스로를 속여야 떠날 수 있었으니까.
마침 인사평가 기간이었다. 퇴사까지 일주일도 남지 않았을 무렵, 한 동료에게서 내가 무섭게 말한다는 피드백을 받았다. 이미 알고 있어 놀랍지는 않았다. 표현은 전보다 거칠어졌고 표정에서는 냉소를 감추지 못했다. 동료가 같은 실수를 반복하면 필요 이상으로 날을 세웠다. 회고에서는 동료들이 감당할 이유 없는 감정을 드러냈다. 바깥의 동료들은 몰랐겠지만 적어도 스쿼드 안에서의 나는 생각을 편하게 나누기 어려운 사람이 되어 있었다.
그런 행동을 하는 나와 이를 지켜보는 내가 분리되고 있었다. 처음 겪는 일이었다. 모든 일에 항상 진심이 필요하다고 생각하지는 않지만, 결코 내 방식은 아니었다. 나는 일정 초대 메일로도 장문의 편지를 쓰던 사람이었다. 아마 그때의 나는 회사와 동료에게 존중받기는커녕 핍박받고 있다고 느꼈던 것 같다.
이미 알고 있었다는 사실은 잘못을 가볍게 하지 않는다. 나는 문제를 해결해야 한다는 조급함을 이유로, 그 문제를 만들지도 않은 동료들에게 쌓인 감정을 떠넘겼다. 문제를 함께 풀 사람을 설득의 대상이나 잘못을 통제해야 할 대상으로 대했다. 더는 태도를 조절하지 못했고, 서로를 위해서라도 회사를 제 발로 떠나야 했다.
최악인 것은 피드백을 받고도 떠나기 전에 사과하지 않은 일이다. 애초에 깊은 관계를 맺지 못했지만 회복할 기회마저 놓쳤다. 퇴사가 얼마 남지 않았다는 사실 뒤에 숨어 도망치듯 회사를 떠났고, 먼저 자신의 고집을 인정하고 사과한 뒤 떠난 Product Owner가 부러웠다.
나의 매니저는 고객 가까이에서 요구사항을 빠르게 구현하는 일에 강했다. 그 점에서는 분명 나보다 나았다. 문제는 누가 더 나은 엔지니어였는지가 아니다. 회사는 그의 강점을 살리는 역할을 맡기거나, 지금의 포지션을 유지한다면 기대에 맞게 성장할 수 있도록 피드백을 제공했어야 했다. 누구도 직접 맡겨보기 전에는 그가 역할을 잘 해낼지 알 수 없다. 그러니 적절한 배치나 지원이 있었다면 우리는 좋은 동료가 되었을지도. 결과는 실패였지만, 그것을 개인의 악의로 말하고 싶지는 않다. 나는 이제 그를 원망하지 않는다. 지금까지 나의 매니저를 나쁘게만 적은 듯해 글을 덧붙인다. ([수정] 지금은 회사를 떠났다는 소식을 들었다.)
결과는 내 문제의식이 틀리지 않았음을 보여주었다. 내 주장은 고객의 반응이 되어 돌아왔고 회사는 방향을 바꿨다. 받아들여지지 않던 문제는 뒤늦게 회사의 과제가 됐다. 기술적인 문제를 해결한 결과는 제품과 고객, 계약에까지 닿았다. 인정받지 못하는 일을 하려면 용기가 필요하다. 누구도 듣고 싶어 하지 않는 말을 하고, 반대 속에서도 해야 한다고 믿는 일을 주장해야 하니까.
그러나 옳았다는 사실이 그릇된 행동을 정당화하지는 않는다. 나는 반대 속에서 주장할 용기도, 회사를 떠날 용기도 있었다. 잘못을 인정하는 법은 알았지만, 잘못을 사과할 용기는 없었다. 이 글이 그때의 사과를 대신할 수 없다는 것도 안다. 다만 내가 옳았다는 기억만 가지고 있지 않도록, 비겁하게나마 내가 잘못한 일도 함께 기록하며 글을 마친다.