Home

3개월 차 주니어가 느끼는 나와 시니어의 차이

2019-02-06

개요

입사한 지 3개월, 나는 훌륭한 동료들과 선배들 사이에서 정말 즐겁게 일하고 있다. 하지만 동시에 스스로의 부족함을 많이 느끼고 있고, 이는 성장에 대한 좋은 동기부여가 되고 있다.

하지만 회사에 입사하고 본격적으로 개발자로서 커리어를 시작한 지금, 나는 내가 성장하기 위해 어떤 노력을 해야 할지 잘 모르는 상태다. 개발을 막 시작했을 때에는 여러 개발 관련 지식을 쌓는 데에만 집중해도 충분했다. 그러나 지금부터 몇 년간 개발 공부만 열심히 한다고 해서 우리 회사의 멋진 선배들처럼 될 수 있을 것 같지 않았다. 분명 나와 내 선배들 사이에는 지식의 양의 차이 외의, 무언가 결정적인 차이가 있었다.

이런 차이를 경험이라고 뭉뚱그려서 말할 수도 있다. 하지만 현재 내가 선배들에 비해 부족한 점이 구체적으로 어떤 것인지 파악하고 경험을 취사선택하여 쌓는 것이 훨씬 더 빠르게 성장할 수 있는 길이라고 생각했다.

그래서 이번 기회에 나와 내가 존경하는 회사 선배들의 차이를 정확히 되짚어보기로 했다. 이 글에서는 3개월이 조금 안 되는 기간 동안 느낀, 시니어는 갖췄지만 나(주니어)는 아직 갖추지 못한 개발 지식 외적인 점들에 대해서 정리해보았다.

* 본인은 서버 개발자이기 때문에 이 글은 서버 개발자의 입장에서 작성되었다.

코드가 돌아가는 환경의 아키텍처를 고려한다

서버 개발자의 관점에서 이야기하자면, 프로덕션 레벨의 많은 서비스는 이미 컨테이너를 활용한 마이크로서비스 아키텍처를 갖추고 있다. 여러 개의 내부 서비스가 존재하는 것 뿐만 아니라, 하나의 서비스 역시 여러 개의 동일한 컨테이너와 로드 밸런서로 구성되어 있다.

그리고 이러한 아키텍처는 분명히 코드가 작동하는 방식에 영향을 미칠 수 있다.

예를 들어서, 특정 DB 테이블을 알파벳으로 구성된 id column를 기준으로 샤딩한다고 하자. 아이디가 A-M인 row는 1번 샤드에, N-Z인 row는 2번 샤드에 저장하려고 한다. 이런 식으로 아키텍처를 구성하면, 코드 작성 시 신경써야 할 부분이 하나 늘어난다. 바로 id 생성 로직이다. 만약 id를 생성하는 로직에 문제가 있어서 id가 균등하게 생성되지 않고 A로 시작하는 id가 90% 이상 생성되면 DB 샤딩에 별 의미가 없어지게 된다.

최근에 겪었던 또 다른 예시를 들어보겠다. 현재 타다 백엔드에서는 AWS Kinesis를 사용하여 드라이버의 GPS 데이터를 수집하고 처리하고 있다(참고 : 타다 시스템 아키텍처). 여기에 GPS에 이상이 있는 드라이버를 탐지하는 로직을 추가하려고 했다. 나는 맨 처음에 이상이 있는 드라이버의 기준을 “현재 시간을 기준으로 수 분 동안 GPS가 올라오지 않은 드라이버”로 세웠다. 하지만 이걸 본 선배 한 분이 “Kinesis에 장애가 나서 한 동안 GPS를 하나도 못 받으면 모든 드라이버가 이상하다고 분류되겠네?”라는 말씀을 해주셔서 기준을 “마지막으로 GPS가 올라온 시간을 기준으로 수 분 동안 GPS가 올라오지 않은 드라이버”로 변경했다. 하지만 여기서 끝이 아니었다. 우리 회사는 Kinesis를 두 개의 샤드로 샤딩하여 운영하고 있었는데, 다른 선배가 이 점을 지적하면서 “Kinesis 샤드 하나만 꺼지면 드라이버의 반이 이상하다고 분류되겠네?”라고 조언해주셨다. 결국 시간 기준을 수 분에서 Kinesis가 복구될 수 있을 만큼 넉넉한 시간으로 늘렸다.

지금까지 학교에서 과제를 할 때나 토이 프로젝트를 만들 때에는 오로지 코드만이 코드 동작 방식에 영향을 미쳤다. 하지만 위 두 예시에서는 장애의 가능성이 코드가 아니라 오로지 아키텍처에서 야기됐다. 이러한 위협은 아키텍처에 대해서 잘 알고 있고, 또한 아키텍처를 고려하여 코드를 작성해보지 않는 이상 결코 미리 대처할 수 없다. 시니어는 아키텍처(혹은, 포괄적으로 코드 외적인 요소)가 코드 작동 방식에 영향을 미칠 수 있는 가능성을 고려한다.

하위호환성을 유지한다

위에서 말했던 대로, 한 서비스는 더 이상 하나의 서버로 이루어지지 않는다. 그렇기 때문에 배포와 동시에 모든 서버가 새 버전으로 업데이트 되는 마법은 일어나지 않는다. 컨테이너 오케스트레이션 툴이 새 버전의 컨테이너를 몇 개 띄우고, 기존 버전의 컨테이너를 몇 개 죽이고, 로드 밸런싱을 새 컨테이너 쪽으로 하는 과정을 몇 번이나 반복해야 비로소 모든 서버가 새 버전의 코드로 업데이트 된다. 이러한 작업은 여러 개의 서비스에 적용되어야 하고, 더욱이 모바일 클라이언트가 있는 경우 클라이언트는 며칠, 몇 주씩이나 업데이트 되지 않기도 한다. 그리고 이 모든 상황에서 서비스는 중단되지 않고 계속 돌아가야 한다.

그렇기 때문에, 동작하는 서비스를 업데이트 하여 배포하는 경우에는 언제나 하위호환성을 유지해야 한다. 조금 더 정확히 표현하자면, 배포하는 서비스가 외부로 노출하는 API가 변경되는 모든 경우 하위호환성이 유지되어야 한다. 여기에는 클라이언트에게 노출되는 API 변경은 물론, 마이크로서비스 아키텍처를 활용할 경우 백엔드 서비스끼리의 API 변경도 포함된다. 또한 놓치기 쉽지만 DB 스키마도 마찬가지다.

하위호환성을 유지하는 핵심은 기존 API의 일부를 삭제하거나 변경하지 않는 것이다. 클라이언트가 unknown interface를 무시한다고 가정한다면, API는 언제나 추가에 자유롭다. JSON 형태의 HTTP response에 아무리 field를 추가하든, DB에 column을 추가하든, API를 사용하는 측에서 이 추가된 field를 무시한다면 아무런 일도 일어나지 않는다. 하지만 삭제나 변경은 문제가 된다. 클라이언트가 반드시 필요로 하는 field를 삭제하면 API 결과를 파싱하는데 실패할 것이므로 (운 좋게 적절한 에러처리가 미리 되어 있지 않는 한) 프로그램이 그냥 터진다. DB에서 테이블의 column 하나를 삭제/변경했다고 해보자. 기존 버전의 서버에서 날리는, 해당 column을 참조하는 모든 query가 실패할 것이다.

이러한 위험성은 AWS EC2 인스턴스 하나 띄워서 서비스를 띄워본다고 절대 알 수 없다. 시니어는 이러한 하위호환성을 언제나 고려하여 서비스가 터지지 않도록 주의한다.

필요한 수준의 일반화만 한다

분명 일반화는 코드의 확장성을 높이는 아주 좋은 수단이지만, 무리하게 일반화를 할 경우 오히려 나중에 걸림돌이 될 수 있다. 일반화를 하기 위해서는 현재 공통적이고, 앞으로 공통적일 것이라 기대되는 기능을 뽑아내야 한다. 그리고 뽑아낸 기능을 기준으로 코드의 구조를 변경해야 한다. 이는 역으로 말하면 뽑아낸 기능이 공통점이 아니게 될 경우 일반화는 불필요한 작업이 된다는 말이다. 게다가 코드 구조가 변경되었기 때문에 오히려 다른 코드를 작성하는 데에 방해가 될 수도 있다.

그리고, 확장되는 서비스에서 ”앞으로 공통적일 것이라 기대되는” 기능을 뽑아내기는 힘든 일이다. 서비스의 기획이 매주 천차만별로 달라지기 때문이다. 잠깐 시범적으로 도입한 기능은 아웃풋에 따라 언제든지 기획이 수정될 수 있고, 혹은 극단적으로 롤백될 수도 있다. 이런 상황에서 확신 없이 열심히 일반화하여 구조적으로 완벽한 코드를 작성하려고 노력하는 것은 정말 무의미한 일이다.

따라서, 앞으로 어떤 식으로 기능이 확장될 지 모르는 상황에서는 일반화를 시키지 않고 아예 코드를 걷어내기 쉽도록 결합도가 매우 낮게 구현하는 것이 나을 수 있다. 그리고 비로소 기능이 어떻게 확장될 지 예측 가능하게 됐을 때 해당 코드를 걷어내고 일반화시키는 것이다.

이 “일반화를 어디까지 할 것인가”의 적정 수준을 아는 것은 정말 경험적인 부분인 것 같다. 필요한 일반화와 불필요한 일반화를 정확히 구분해내고 쓸데없는 코드를 작성하지 않는 능력이 바로 시니어 개발자의 능력 중 하나라고 생각한다.

자신이 개발하는 서비스와 관련된 지식이 풍부하다

이전까지 오로지 개발에만 흥미가 있었던 내가 3개월간 개발을 하면서 가장 뼈저리게 느꼈던 점은, 개발 외적으로 자신의 서비스와 관련된 지식이 의외로 개발 역량에 매우 큰 영향을 미친다는 것이다. 이를 흔히들 도메인 지식이라고 부르는 것 같다. 개발적인 측면에서 도메인 지식은 서비스에게 잠정적으로 위협이 될 포인트를 제거하거나 훨씬 더 고도화된 비즈니스 로직을 작성하는 데에 큰 도움을 준다. 또한 개발 외적인 측면에서도 개발자와 기획자의 소통을 원활하게 하는 원동력이 되고, 개발자의 발언에 한층 더 힘을 실어주기도 한다.

도메인 지식이 개발에 미치는 영향은, 이를테면 이런 것이다. 타다 서비스에서는 드라이버의 GPS를 받아서 실시간으로 처리하는 작업이 매우 중요하다. 그러나 일반적으로 GPS 데이터는 매우 부정확하다. 그렇기 때문에 GPS 데이터를 100% 신뢰할 수 없다. 이 때 GPS가 대체로 언제 튀는지, 튀는 패턴은 어떠한지, 튀는 비율은 얼마인지, 어떤 조건일 때 100% 신뢰할 수 있는지 등을 알고 있다면 그렇지 않은 사람보다 훨씬 더 정확하게 GPS 관련 로직을 작성할 수 있을 것이다.

또한, 현재 개발하고 있는 서비스에 대해 큰 관심을 가지는 것 역시 중요하다. 회사에서 어떤 기획이 진행되고 있는지는 결국 내가 앞으로 어떤 기능/서비스를 개발할 지와 동일하다. 그렇기 때문에 개발자는 본인이 참여하는 프로젝트의 기획을 필수적으로 꼼꼼히 이해하고 있어야 한다. 또한, 서비스끼리 밀접하게 연관되어 다른 서비스의 기획을 모르면 아예 손을 댈 수 없는 코드도 있을 수 있다. 어떤 경우에는 개발자만이 파악할 수 있는 서비스 기획에서의 맹점이 있을 수도 있다. 그렇기 때문에 개발자가 본인이 개발하는 서비스에 관심을 가지는 것은 당연한 일이며, 반드시 필요한 일이다.

이러한 도메인 지식이나 지속적인 관심은 나는 아직 갖추지 못했고, 우리 회사의 선배들은 꽤나 갖추고 있는 부분이었다. 그리고 3개월간 나는 지식의 차이에서 나오는 코드의 안정성과 퀄리티의 차이를 수도 없이 느껴왔다. 정말 신뢰받을 수 있는 개발자가 되기 위해서는 이러한 개발 외적인 노력이 필수적이다.

요약 : 더 이상 코드가 전부가 아니다

모든 이야기는 결국, 코드 너머를 보라는 말로 정리할 수 있다. 내가 개발하고 있는 서비스는 더 이상 나 혼자 개발하고 AWS EC2 인스턴스 하나에 띄우는 토이 프로젝트가 아니다. 하나의 서비스에는 수많은 복잡한 기획이 녹아들어있고, 많은 사용자가 실시간으로 24시간 내내 사용하고 있다. 서비스 구현 및 배포에는 AWS 등 수많은 외부 서비스에 의존하지만 이 모든 외부 서비스는 언제든지 장애가 날 수 있다.

학생 때와는 달리, 코드가 돌아가는 환경은 더 이상 채점용 학교 머신이 아니라 불완전한 세상이다. 코드가 돌아가는 세상이 변화한 만큼, 개발자의 관심사 역시 변화하고 확장되어야 한다.