티스토리 뷰
안녕하세요? 이런 메일을 드리게 되어서 정말 죄송하다는 말씀을 먼저 드리고 싶습니다. 저는 다른 사람에게 좋은 말을 해주는 것도, 나쁜 말을 하는 것도 익숙하지 않은 사람이기에 선한 의도의 비판이나 악의가 숨어있는 칭찬 같은 복잡한 기술을 가진 글쓰기는 어려워하는 편입니다. 그렇기에 잘 돌려서 기분나쁘지 않게 의도를 전달하는 것도 솔직히 말씀드리면 별로 자신이 없습니다. 같은 말이라도 예쁘게 전달하지 못하는 것은 제 부덕의 탓이니 그점에 먼저 사과를 드리고 시작하고 싶습니다.
몇 년 전에 클래스의 명명을 가지고 논쟁이 있었던 적이 있었습니다. 아마 제 기억이 맞다면 ID Generator를 IdGenerator로 써야 되냐 IDGenerator로 써야 되냐를 가지고 싸웠던 것 같습니다. ID는 영어 약자이고 실제로 Id라는 단어는 없으니 IDGenerator가 맞다는 입장이 있었고, 자바의 일반적인 명명 규칙을 생각해보면 단어 단위로 첫 글자만 대문자로 표기하는 것이 맞기 때문에 IdGenerator가 맞다는 입장이 있었지요. 사실 뭐여도 상관없었을 겁니다. Id든 ID든 컴파일 하면 다 같은 바이트 코드가 되니까요. 하지만 저희는 d와 D를 가지고 대립했고, 사실 마지막에는 거의 감정적인 싸움이 벌어지기 직전까지 갔었지요.
사실 제가 프로젝트의 개발자들이 가져야 하는 표준이나 규칙에 대해서 가지고 있는 생각은 자유주의, 아니 방임주의에 가깝습니다. 사람들의 눈이 시각적인 객체를 어떻게든 정렬해서 보려고 하는 것처럼, 프로젝트의 개발자들은 안 그런 것 같아도 다른 사람들이 짠 코드에 영향을 많이 받고 자신의 코드를 전체의 코드와 같은 맥락에 위치하도록 노력합니다. 세밀하게 보면 다들 제멋대로 짜는 것 같지만, 크게 보면 또 그렇게 무질서한 것도 아닙니다. 어쨌든 수요와 공급이 하나의 일치된 지점을 찾아가는 것처럼 프로젝트의 컨벤션이라는 것도 굳이 강제하지 않아도 모두가 편하게 느낄 수 있는 하나의 규칙으로 수렴할 수 있다는 것이 제 생각입니다.
개발자도 결국 인간이고, 그렇기에 양심의 자유는 매우 중요합니다. 즉, 각자가 가진 내재적인 생각과 기준은 모두 존중받을 가치가 있습니다. 우리 프로젝트의 개발자들은 각자 다른 환경에서 교육을 받았고 다른 규칙을 가진 프로젝트를 거쳐왔습니다. 그 때문에 각자가 가진 고유한 기준을 무너뜨리도록 강제하는 것은 정말 그럴 필요가 있는지 깊이 생각해볼 문제입니다. 예를 들어서 Value Object를 ProductVO 와 같은 형태로 대문자로 쓰는 것이 표준인 프로젝트가 있고, ProductVo와 같이 소문자로 쓰는 것이 프로젝트가 있을 겁니다. 저도 실제로 두 프로젝트를 모두 경험해봤으니까요. 만약 VO로 쓰는 프로젝트만 경험해온 개발자라면 이번 프로젝트에서도 당연히 VO로 쓰려고 할 것이고, Vo는 어색하다고 느낄 겁니다. Vo로 쓰는 프로젝트만 경험한 개발자라면 반대로 생각하겠지요. 여기에서 리드 개발자의 첫 번째 고민이 생겨납니다. 저 두 개발자가 같은 프로젝트에 들어왔을 때, 누구의 손을 들어줘야 할까요?
앞에서 말했던 것처럼, 저는 누구의 손도 들어줄 필요가 없다고 생각합니다. 그리고 많은 선배들은 그 생각이 위험하다고 말합니다. 프로젝트에는 거부할 수 없는 강력한 기준이 있고 모두가 그것을 따라야 혼란이 없고 불필요한 비용이 발생하지 않는다고 합니다. 그런데 정말 VO냐 Vo냐가 그렇게 중요한 문제입니까? 그건 그렇게 쉽게 단정지을 수 있는 문제가 아닙니다. 중요할 수도 있고 중요하지 않을 수도 있는데, 정말 중요한 것은 그것이 중요하거나 중요하지 않은 논리적인 기준이 있느냐는 것입니다. 저는 오히려 그 객체가 Value Object인지 Entity인지를 구분하는 것이 더 중요하다고 생각하는 주의입니다. 거기에는 논리적인 기준이 있거든요. Entity 성격을 가진 객체를 VO로 표기하는 것은 논리적 오류에 가깝기 때문에 양심의 자유로 치부할 수 있는 가벼운 문제는 아닙니다. 각자의 기준이 존중받아야 된다고 해서 운전을 해도 되는 혈중알콜농도를 개인이 정할 수는 없는 거니까요. 누구나 이해할 수 있고, 이해해야 되는 명확한 기준이 있는 문제는 오히려 논쟁의 여지도 없습니다. 잘못된 지식과 이해, 관습은 고칠 수 있는 거니까요.
프로젝트의 규칙과 기준을 정할 책임이 있는 저희는 맞춰야 되는 기준과 맞추지 않아도 되는 기준을 깊이 있게 고민해야 합니다. 그 고민이 쉬운 것은 아니기에 보통 다들 기준을 다소 강하게 정합니다. 문제는 그 기준이 깊은 성찰 끝에 나온 것이 아니기 때문에 그것을 따라야 하는 논리적 이유가 분명하지 않다는 점입니다. 저는 굳이 기준을 만들어야 한다면 IdGenerator가 맞다고 주장했고, 지금도 그 생각은 변함이 없습니다. 거기에는 어떤 언어학적이거나 심미적인 이유가 있었던 것은 아닙니다. 저희는 가끔 IdGenerator와 같은 Camel Case 규칙에 따른 명명을 ID_GENERATOR와 같은 Snake Case로 바꿔야 할 일이 생기는데, 이것을 자동으로 해주는 유틸리티들이 가끔 IDGenerator를 I_D_GENERATOR와 같이 이상하게 변환하는 일이 생기기 때문입니다. 이건 그냥 실용적인 이유입니다. 자동으로 변환할 일이 없는 거라면 그냥 IDGenerator나 ProductVO 같은 명명 규칙도 별문제 없었을 겁니다. 사실 별것 아닌 문제였고 그게 왜 감정적인 싸움으로 번졌는지는 저도 가물가물합니다. 아마 논리적인 이유를 전달함에 있어서 세련된 언동을 보이지 못한 저희의 미숙함이 가장 큰 원인이었겠지요.
사실 몇 년 전의 IdGenerator 이야기를 한 것은 그냥 그럴 때도 있었고 지금 생각하면 별 것 아니니까 제가 지금부터 할 이야기도 너무 기분 나쁘게 생각하지는 말아주십사 꺼낸 추억이었습니다. 그래서 제가 진짜 메일을 드린 이유가 뭐냐면,
죄송하지만 서버는 하나가 아닙니다. 당연히 저희는 아주 간단한 프로젝트를 수행할 때도 웹 서버, 애플리케이션 서버, 데이터베이스 서버를 구성하니까 서버는 보통 하나가 아닙니다. 다만 제가 드리고 싶은 말씀은 저기에서 애플리케이션 서버도 단일 객체로 취급할 수는 없다는 것입니다. 너무나 당연한 이야기라서 이 이야기를 꺼내기까지 많은 시간이 걸렸습니다만 지금 시점에 꼭 짚고 넘어가야 할 문제이기도 합니다.
저희는 개발을 할 때 자신의 PC에서 애플리케이션 서버를 보통 하나만 띄워놓고 개발을 합니다. 그냥 개발할 때는 그게 제일 편하기 때문입니다. 저희가 개발을 한다고 하는 행동은 보통 요구사항을 기능으로 구현하는 일이고 그 과정에서 서버가 두 개 이상이어야 될 이유는 전혀 없습니다. 오히려 서버가 다수가 되면 배포도 여러 곳에 해야 하고 설정도 여러 곳에 바꿔야 하니까 불편한 점이 많습니다.
그리고 저희가 구성해놓은 개발계나 테스트계 역시 모두 애플리케이션 서버는 하나씩만 구성되어 있습니다. 이쪽은 비용 문제가 걸려있으니 이해할 수 있는 구성입니다. 하지만 운영계는 서버가 하나만 구성되는 경우가 거의 없습니다. 보통은 하나가 죽어도 다른 하나로 서비스를 계속할 수 있도록 최소 두 개의 서버를 설치해둡니다. 물론 저희가 오랫동안 운영까지 간 프로젝트가 없었다는 사실은 저도 잘 알고 있습니다. 하지만 저희가 실험실에서 프로토타입 만드는 일을 하는 것은 아니지 않습니까? 저희는 연구원이 아니라 개발자이고, 아무리 희망이 없는 상황이더라도 항상 운영까지 이어질 것을 염두에 두고 개발해야 합니다. 그것은 저희의 직업적 소명에 가깝습니다.
그렇기 때문에 각종 컴포넌트에서 멤버 변수로 어떤 상태를 가지는 인스턴스를 선언하는 것은 매우 위험한 행동입니다. 예를 들어서 어떤 서비스 클래스가 모종의 용도로 Map 클래스를 가지고 쓰고 있다면, 그 코드는 무조건 문제를 일으킵니다. 생각해보시면 서비스 클래스는 별다른 설정이 없으면 기본적으로 싱글턴(Singleton) 클래스가 되기 때문에 저희의 인생처럼 항상 혼자만 존재하게 됩니다. 그 클래스에 선언된 멤버 변수 역시 단 하나만 존재하게 되니까 사실 별문제가 없습니다. 심지어 작성하신 코드에는 그 맵에 여러 스레드가 접근할 것을 우려하셔서 동기화 처리까지 해놓으셨더라구요. 그럼 더더욱 문제될 일은 없겠지요.
서버가 하나라면 말입니다. 하지만 서버가 두 개가 되면 문제가 완전히 달라집니다. 당연히 서로 다른 서버는 메모리 영역을 공유하지 않고, 공유하게 할 방법도 없습니다. 따라서 두 개의 서버는 서로 다른 두 개의 맵을 가지게 되고, 이것은 무조건 문제를 일으키게 됩니다. 맵은 상태를 저장하는데 사용자의 요청이 들어올 때마다 다른 상태를 참조하게 될 것이니까요. 첫 번째 요청에서 저장한 정보가 두 번째 요청에서는 없어졌다가 세 번째 요청에서 다시 나타나는 일이 생길 수 있는 겁니다.
개발자들이 작성하는 코드는 하나의 객체이기 때문에 그 흐름을 따라갈 때 평행하게 존재하는 두 개 이상의 서버에서의 상태와 맥락의 흐름을 따라가는 것은 그리 쉬운 일은 아닙니다. 그렇기 때문에 실수를 방지하기 위해서 저희는 여기에서 지켜야만 하는 또 하나의 기준을 만들 수 있습니다. 서버가 기동 되는 동안 쭉 유지되는 인스턴스 내에서는 상태를 저장하는 클래스를 멤버 변수로 두지 말고 매 요청마다 로컬 변수로 처리하라구요. 이런 것이 우리 프로젝트의 혈중알콜농도 기준이 될 수 있을 겁니다. 물론 멤버 변수를 로컬 변수로 기계적으로 변환할 수 있는 것은 아닙니다. 상태 유지가 꼭 필요한 경우 쓰라고 저희는 별도의 데이터베이스를 구성해두었으니까 그걸 쓰시면 됩니다. 데이터베이스에 계속 엑세스 하는 것이 부담되신다면 중간에 인-메모리 캐시를 구성해드릴 수도 있습니다. 필요하시다면 얼마든지요.
저는 어떤 개발자가 인터페이스 클래스에 ProductManager_IF 와 같은 식으로 명명하는 것도 용인하고 넘어갔습니다. 물론 자바에서 보통 안 쓰는 컨벤션이지만 이런 명명 규칙을 다른 사람과 맞추는 것은 그냥 매너나 도덕의 영역으로 생각할 수 있는 겁니다. 거기에 무조건 다른 사람과 같은 생각을 가지라고 강요하는 것은, 정말로 그래야 되는가 깊이 고민해봐야 되는 행동이고 저는 저런 명명이 프로젝트에 주는 위해가 개인의 기준을 존중하는 것에 비하면 크지 않다고 판단했습니다. 반면에 명백히 이상 동작을 일으킬 수 있는 잘못된 코드 작성을 막는 것은 일종의 법에 해당합니다. 그러니까 저희가 최소한의 도덕이라고 배우는 그것이요. 다른 사람이나 프로젝트 전체에 피해를 줄 수 있는 행동은 강제로라도 막아야 되는 것입니다. 물론 그런 규정이 개개인에게 불편함이나 불쾌함을 유발할 수 있으므로 저희는 그것이 정말 최소한의 기준이 되도록 신중하게 정해야 할 것입니다.
작성하신 코드를 운영계에 그대로 올리면 많은 문제가 될 수 있음을 보고하는 것은 제 역할이었습니다. 그리고 PM 님은 저에게 그 문제를 해결하라고 하셨고, 저는 문제가 되는 코드를 싹 고치려면 다시 짜야 되는 코드가 꽤 많아서 한 달 정도는 빠듯하게 잡아야 한다고 말씀드렸습니다. 그 대답이 별로 마음에 들지 않으셨나 봅니다. 결국 저는 왜 혼나야 되는지도 모른 채로 계속 혼나다가, 스티키 세션(Sticky Session) 옵션을 이용하면 코드 수정 없이 같은 사람의 요청은 같은 서버로 계속 들어가도록 구성하여 대부분의 문제를 해결할 수 있음을 실토할 수밖에 없었습니다. 그 대답에 만족하셨는지 그렇게 하라고 하셨고, 그렇게 했습니다. 문제가 한 방에 해결된 것처럼 보였고 운영계는 정상 동작하는 것처럼 보였습니다.
그렇게 하면 안 되는 겁니다. 스티키 세션은 문제의 해결책이 될 수 없습니다. 서버가 중간에 한 대 더 늘어나거나 줄어들면 어떻게 됩니까? 요청은 다른 서버로 들어갈 수 있습니다. 유지되어야 되는 상태는 무너지고 사용자들은 뭐라 설명하기도 힘든 결함을 겪게 될 것입니다. 어떤 코드에서는 AtomicLong을 카운터로 쓰셨는데, 이건 애초에 스티키 세션 옵션으로도 해결이 안 됩니다. 서버는 두 개의 서로 다른 카운터를 가지게 되니까 한 명의 사용자 입장에서는 일관성 있는 카운터를 볼 수 있을지 몰라도 데이터베이스에 들어가는 값은 엉망이 될 겁니다.
하지만 저희는 그 문제를 해결할 수 없을 겁니다. 일견 잘 돌아가고 있는 것처럼 보이는 서버는 명시적인 문제를 발생시키기 전까지는 잠재적인 문제를 해결하기 위한 공수를 투입하지 않게 해줍니다. 그거 고칠 시간에 다른 기능이나 하나 더 추가하라고 하실 거란 말이지요. 하물며 전체 코드의 30퍼센트 이상을 다시 짜야 된다고 주장해봐야 그게 어떻게 먹히겠습니까. 차라리 계속 혼나더라도 운영계에 못 올린다고 주장했어야 맞는 것이었을까요? 어쩌면 그것이 제 양심일 수도 있었겠지만 저는 그것을 지키지 못했습니다. 어쨌든 만드신 코드를 제가 인수인계받은 이상 저는 이것을 정상적으로 돌려야 될 책임이 있는 것이니까요. 저에게 주어진 시간이 많지 않아서 근시안적인 해결책을 내놨다는 사실이 저에게는 매우 우울하게 다가옵니다.
그래서 저희는 최선을 다해야 합니다. 서버가 하나일 때, 두개일 때, N개 일 때를 고민해야 합니다. 데이터베이스가 멈췄을 때, 파일 저장이 오래 걸릴 때를 고려해야 합니다. 네트워크에 단절이 일어나서 서로 다른 두 개의 계가 구성될 때를 생각해봐야 합니다. 물론 빠듯한 시간 내에서 그런 것들을 모두 고려하는 것은 불가능에 가깝지만, '내 컴퓨터에서 잘 돌아가니까 난 책임을 다했어'식의 태도는 다른 사람에게 우울증을 안겨줄 수 있으므로 어쨌든 자신의 아는 범위에서, 그리고 모를 수 있는 범위까지 알아보려고 노력은 해봐야 합니다. 서버가 하나 일때와 두 개 이상일 때가 매우 다른 것처럼, 실수도 한 번일 때와 두 번 이상일 때는 맥락이 완전히 다르니까요. 한 번은 한 번이지만 두 번째는 세 번째를 암시한다는 말이 있지 않습니까.
각자의 기준과 생각이 존중받아야 하는 것만큼이나 각자는 다른 사람의 기준과 생각을 존중해줄 필요가 있습니다. 그 과정에서 충돌이 발생하고 기분이 상하는 경우도 있습니다. 기준이라는 것이 그렇게 퍼즐 조각 처럼 딱딱 맞아떨어지는 것은 아니니까요. 언젠가는 어떻게든 서로의 영역을 침범할 수밖에 없습니다. 그것을 중재하기 위해서 모두가 지켜야 할 공통의 룰을 만드는 것은 분명 필요한 일입니다. 다만 저희는 오히려 그동안 너무 공통의 룰을 광범위하게 남발해서 그것의 가치를 절하시킨 것이 문제였을 겁니다.
본인의 확고한 생각이 있다면, 그것을 그대로 지키시면 됩니다. 다만 저희가 그 생각의 확고함을 알 수 있도록 나중에 설명해주셨으면 좋겠습니다. 애매하거나 고민이 있는 부분이 있다면 언제라도 같이 고민해봤으면 좋겠습니다. 서로의 고민을 털어놓고 올바른 방향을 찾아가는 과정은 바쁜 와중에도 꼭 필요한 일이고, 매우 생산적인 일이고 그렇기에 실제로 엄청 재미있는 일이기도 합니다. 저는 이 서로의 기준을 지켜주고 또 겹치는 부분을 조정해나가는 과정이 흔히 말하는 프로젝트에서의 '협업'과 '커뮤니케이션'의 핵심이라고 생각하고 있습니다. 그러니 제가 도울 수 있는 부분이 있다면 언제라도 편하게 말씀해주시길 바라겠습니다.
혹시 제가 드린 메일에서 불편하신 부분이 있었다면 다시 한번 사과의 말씀 드리겠습니다. 그리고 저와 다른 생각이 있으시다면 언제라도 어떤 경로로라도 저에게 알려주시면 저도 다시 한번 고민해보겠습니다. 감사합니다.
'에세이' 카테고리의 다른 글
Little Big Adventure (2) | 2018.09.07 |
---|---|
From Papercut to Sharp Edges (0) | 2018.08.26 |
2012 (0) | 2018.08.12 |
Build, Pray, Run (0) | 2018.08.06 |
스윙바이 (1) | 2018.07.28 |