정말 오랜만에 블로그 글을 쓰는데 첫 타이틀부터 정신병이라는 과격한 단어를 넣은 스스로가 새삼 우습기도 하다. 이 글을 읽는 사람들이 각기 다른 이유로 제목을 보고 이 글을 클릭했겠지만, 어쨌든 눈길을 끌고 이 글을 클릭하도록 유도했다는 점으로 족하다.
객체 지향과 클린 코드
최근 한 선배 개발자분과 코드에 대한 이야기를 하다가 “객체지향은 정신병이다” 라는 꽤나 과격한 표현을 들었다. 우아한테크코스 를 진행하면서 정말 ‘객체’ 를 ‘지향’했던 과거의 내가 들으면 상당히 당황스러운 말이었겠지만, 실무를 배우고 있는 현재의 나로서는 차마 대놓고 부정할 수는 없는 발언이었다.
우아한테크코스에서 학습하는 가장 첫 번째 단계는 객체지향으로, Web 이라는 개념도 없이 가장 먼저 콘솔에서 아주 작은 게임을 만드는 것 부터 시작한다. 객체지향은 어렵기도 하고, 또 우아한테크코스 내부에서는 코치진과 크루들이 모두 서로가 작성한 코드에 대해 “왜?” 라는 질문을 던져대다보니, 한 줄의 코드를 짜더라도 이에 응하는 마땅한 이유가 있어야 한다. 그러다 보니 “어떤 것이 좋은 코드인가?” 에 대한 고민을 수 없이 했고, 우아한테크코스를 수료할 때 즈음에는 명확하게 누군가에게 “이게 객체지향이다” 라고 설명할 수는 없어도 어느 정도 “이 코드는 클린하지 못하군(혹은 객체지향 적이지 못하군)” 이라는 훈수를 둘 정도의 기준이 생기게 되었다.
그런데 우아한테크코스가 워낙 유명하기도 하고, 클린 코드에 대한 중요성 자체만으로는 누구던 부정할수 없는 사실이다보니 어느 새 클린코드는 취업을 위한 필수 요소로 자리매김하게 된 것 같다. 나를 포함하여, 최근 개발 트렌드에 익숙한 개발자에게 SOLID 에 대해 묻는다면 어떤 약어인지 부터 시작해서 최소 10분은 줄줄이 읊을 수 있다.
그러나 클린코드에 대해 중요성을 너무 강조하고 매몰되다 보면 부작용이 생기기 마련인데, 첫 번째로는 클린 코드에 대한 과도한 집착이다. 잘 추상화가 되어 있는 코드를 읽다 보면 영어를 읽다가 한글을 읽듯이 머릿속으로 로직이 쏙쏙 들어와 이해하기에 아주 편하다. 그런데 한 편 추상화되지 않은 코드를 읽다 보면 무슨 말인지 잘 이해가 안되고, “코드 X같이 짜놨네” 라는 생각과 함께 창을 닫아버릴 것이다. 그런데 JDK나 스프링과 같은 코드도 읽어보면 흔히 말하는 “클린”하지는 않다. 즉, 클린하지 않다고 해서 문제가 되지는 않는다는 것이다.
믿거나 말거나 근래 들어 젊은 세대(특히 10대)에서 문해력이 떨어진다는 말이 존재하듯이, 극단적인 객체지향을 추구할수록 코드 해력(解力)
이 떨어지고, 다른 사람이 짜놓은 클린하지 못한 코드에 대한 불평만 늘어놓게 될 것이다.
한 편으로 더 큰 문제점은 구현 역량의 상대적 경시로 이어질 가능성이 높다. 나는 예전에 구현 역량이란 “내 생각을 코드라는 물리적인 결과물로 만들어내는 능력”이라고 생각했는데, 이는 일부에 불과하다. 개발 공부를 어느 정도 한 사람들은 누구나 할 것 없이 본인의 머릿속에 있는 생각을 코드로 옮기는 데에는 전혀 무리가 없다. 내가 생각하는 진정한 구현 역량이란 예외를 생각해낼 수 있는 능력이다.
실무를 마주하며
올해 초에, 운이 좋게도 국내 빅테크 기업에 인턴을 거쳐 정규직으로 취업을 하게 되면서, 처음으로 서비스를 개발하는 ‘실무’를 접하게 되었다. 인턴 시절에 처음 git clone
을 치고, intellij
로 서비스를 열었을 때 가장 놀랐던 점은 서비스 클래스 하나에서 수십 개의 다른 서비스 클래스와 레포지토리를 참조하고 있다는 점이었다. 그리고 그 서비스는 또 다시 수십개로 이루어진 메소드를 타고 나가며 저마다의 로직을 수행하고 있었다.
이 코드를 보고 가장 먼저 든 생각은 당연하게도 “이 코드들은 클린하지 못하군” 이었다. 우아한테크코스 때 만들었던 모락 프로젝트는 날을 잡고 며칠에 걸쳐 리팩토링 작업을 수행했을 만큼 객체지향과 클린코드 중요시했던 나였기에, 리팩토링에 대한 욕구가 내면에서 끊임없이 차올랐다. 하지만 도메인을 잘 모르는 상태로 손을 대는 것 만큼 멍청한 짓도 없기 때문에, 꾹 참았다가 정규직 전환 이후 조심스럽게 건의해보았다.
가장 간단하게, DTO 에 대한 클래스 이름 규칙부터 시작해서 조금씩 리팩토링하는 것이 어떤지 여쭤보자 돌아온 답변은 “그런 것은 중요한 게 아니다” 였다. 물론 회식자리였기 때문에, 아주 가볍게 질문했고 또 아주 가볍게 답변을 받았기 때문에 기분이 나쁘거나 그렇지는 않았다. 하지만 집으로 돌아오면서 왜 그런 말씀을 하셨을까 곰곰히 생각해 보았지만 결국 당시에는 답을 찾지 못했다.
시간이 흐르면서, 그나마 “조금은 익숙해졌나 ?” 라는 생각이 들 때 쯤 내 실수로 몇 번 장애가 났다. 물론 실사용자에게 대면하는 작업은 위험한 작업은 아니었고, (지금 와서 생각해보면 이 부분은 팀장님이 의도적으로 나에게 장애가 나도 큰 문제가 발생하지 않는 업무를 주신게 아닐까 싶어 감사한 생각이 든다.) 장애가 날 때마다 팀원분들의 도움으로 다행히 해결할 수 있었다.
몇 번의 풍파를 겪고나서, 최대한 장애를 내지 말아야겠다는 생각을 하면서 다양한 예외 케이스를 계속 고민하다보니, 어느새 시간에 쫓기는 나를 발견할 수 있었다. 예전에는, 구현 역량(당시에는 구현 역량이 생각을 코드로 옮기는 것으로 끝나는 것인줄 알았으니)은 당연한 것이고, 이에 더해 클린 코드와 같은 개발을 추구해야 하는 것이 돈을 받고 일하는 ‘프로’ 로서, 당연히 수행해야 하는 일이라고 생각했지만, ‘실무’라는 이름 하의 진정한 구현 역량이라는 한계에 부딪혀 클린코드를 챙기기에 어려운 나 자신을 발견한 것이다. (특히, 내가 진행하고 있는 프로젝트는 사용자의 돈을 건드리는 프로젝트이기 때문에 더더욱 조심해야 했다.)
‘실무’ 가 주는 또 다른 어려움은 바로 협업이다. 팀 내외로 다른 사람들과 소통하는 커뮤니케이션도 협업이지만, 여기서 말하는 협업은 API 연동과 같은 협업을 말한다. 대개 규모가 좀 있는 IT 기업은 프론트엔드와 백엔드를 나누고, 더 나아가 빅테크 급의 기업이 되면 MSA 는 필수가 된다. 다른 기업과 팀이 어떻게 개발을 진행하는지는 모르겠지만, 적어도 우리 팀에서는 어느 상황이 됐던 인터페이스에 대한 변경이 ‘클린 코드 관점에서’ 꽤나 문제가 된다.
로이 필딩의 “REST API를 위한 최고의 버저닝 전략은 버저닝을 안 하는 것" 이라는 말이 무색하게도, FE/BE 의 인터페이스 변경에 따라 버전이 바뀌거나 혹은 내부 로직이 뒤집어진다. 다른 서비스 개발자분에게 특정 기능을 요구했을 때 그것이 구조적으로 불가능하다는 답변에 우리 테이블에 정규화되지 않은 컬럼이 추가되는 것은 예삿일이다. “값 객체 는 id 를 가지면 안된다” ? 장애가 발생하고나서 각종 로그와 DB 를 역추적하는 짓을 몇 번 하다보면 그 따위 컨벤션은 신경도 안쓰게 된다.
혹여라도 이에 대해, 팀원들이, 혹은 이를 넘어서 개발 직군 모두가 더 노력하지 않았을 뿐이라고 비난하는 것은 유토피아를 논하는 것과 다를 바가 없다. 하지만 내가 실력이 없는 것이라고 말한다면 이는 사실이기 때문에 할 말은 없다. 그러나 우아한테크코스를 진행할 때까지만 하더라도, 개발 ‘속도’ 는 나름 빠르다고 자부했기 때문에 이렇게 시간이 부족한 상황을 겪는 것은 나에게 제법 충격적인 일이었다.
클린 코드는 무의미한 짓인가 ?
그렇다고 클린 코드가 하등 쓸모 없는 짓이냐고 묻는다면 전혀 그렇지 않다. 위에서 클린 코드에 대해 마치 안좋은 점만 이야기 한 것 같지만, 실상 말하고자 하는 논지는 “구현 역량은 생각 이상으로 훨씬 더 중요하다” 는 것이다. 프로그래머는 ‘코드’ 라는 도구를 이용해 문제를 해결한 대가로 급여를 받는 사람이고, 클린 코드는 프로그래머로서 ‘살아감’에 있어 갖추어야 할 ‘덕목’이다. 예전에 박재성(포비)님의 내 업의 본질은 무엇인가? 라는 글을 아주 인상깊게 봤었는데, 이를 인용하자면 “교육자로서 갖추어야 할 업의 본질을 놓쳐서는 안된다” 와 마찬가지로, “개발자 이전에 직장인으로서 갖추어야 할 업의 본질을 놓쳐서는 안된다”는 것이 요지인 것이다.
한 편, 즐겨 보는 유튜브 채널인 개발바닥의 “클린코딩 하는데 구현을 못하는 개발자” 에서는 클린 코드를 작성했으나 기능 구현이 부족한 지원자 A와 기능 구현은 다 했으나 코드가 클린하지 못한 지원자 B 두 명을 비교하면서 누구의 점수를 더 높게 줄 것인지에 대해 이야기한다. 향로와 호돌맨 두 분 다 내 생각과 비슷하게 B에 상대적으로 점수를 높게 주었다. 여기에 개인적인 의견을 첨언하자면, 그럼에도 불구하고 클린 코드를 추구하는 사람이 저평가될 수 없는 이유는 “클린코드를 목적한다는 그 사실보다, 더 나은 개발자로서 발전하려는 동기가 기저에 내재되어 있다” 는 믿음 때문일 것이라고 생각한다.
더 읽기 쉬운 코드를 작성함으로서 나를 포함한 다른 사람들의 개발 생산성을 증대시키고, 이를 위해 ORM 과 같은 프레임워크를 이용하면서, 왜 과거에 DB 에 박혀있던 프로시져 로직이 미들웨어인 어플리케이션으로 이동했고, 그 과정에서 무슨 이유로 ORM 이 등장하게 되었는지와 같은 역사적인 관점, 그리고 이를 해결하기 위한 다양한 테크닉의 발견과 그 기저에 있는 다양한 CS 지식에 대한 호기심을 자극한다는 관점에서 보면 클린 코드는 “구현을 놓치지 않는다는 가정 하에” 반드시 갖춰야 할 역량으로도 볼 수 있다.
나의 기준
그렇다면 도대체 어느 정도까지 클린 코드를 지향해야 할까 ? “나의 기준”이 다른 누군가에게는 과하다고 평가받을 수도 있고, 또 다른 누군가에게는 부족하다고 느껴질 수 있다. 2023년의 마무리를 앞두고 있는 내가 현재 생각하는 클린 코드에 대한 대원칙은 두 가지로, “적확(的確)함” 과 “단순함” 이다.
‘가담항설’ 이라고, 네이버 웹툰 중 즐겨보는 작품이 있다. 평소 웹툰을 즐겨보는 편이라, 누군가가 볼 만한 웹툰 있냐고 물어보면 꼭 리스트에 포함되는 작품 중 하나인데, 무료 대여권을 이용해 한 3번 쯤은 정독을 한 적이 있을 정도로 재미있는 웹툰이라고 생각한다.
‘가담항설’ 을 보던 도중 아주 인상깊은 문구를 본 적이 있는데, 내용이 너무 길어 스크린 캡쳐를 하기는 좀 그렇고 나왔던 대사만 적어보면 다음과 같다.
정기 씨. 아까 제가 꽃을 버려서 슬펐나요 ? 그건 신발이 진창에 빠졌을 때만큼 슬펐나요. 아니면 가까운 이가 아플 때만큼 슬펐나요.
어떤 슬픔은 어렴풋한 슬픔이고, 어떤 슬픔은 처절한 슬픔이죠. 소소한 슬픔도, 아련한 슬픔도, 잊혀가는 슬픔도, 문득 기억이 떠올라 때때로 가슴이 아파지는 슬픔까지, 같은 슬픔조차도 사실은 전부 달라요.
책을 읽고 풍부한 단어를 알게 된다는 건, 슬픔의 저 끝에서부터, 기쁨의 저 끝까지. 자신이 가지고 있는 수많은 감정들의 결을 하나하나 구분해내는 거에요. 정확히 그만큼의 감정을 정확히 그만큼의 단어로 집어내서 자신의 마음을 선명하게 들여다보는 거죠.
내가 얼마큼 슬픈지, 얼마큼 기쁜지. 내가 무엇에 행복하고, 무엇에 불행한지. 자신의 마음이 자신을 위한 목적을 결정하도록. 그리고 자신의 마음을 타인에게 정확히 전달하도록.
나무도, 바위도 없이 숨을 곳 하나 없는 산 복판에서 매에게 쫓기는 까투리의 마음이, 망망대해 한가운데 배에 곡식 가득 싣고, 노도 잃고, 닻도 잃고, 돛줄도 끊어지고, 돛대도 꺾어지고, 바람에 물결치고 안개는 자욱이 뒤섞이며, 사방은 어두워지고 풍랑 일 노을 뜨는데 해적을 만난 사공의 마음이, 엊그제 임을 잃은 제 마음에 비할 수 있을까요.
같은 단어를 알고 있다면, 감정의 의미를 공유할 수 있고, 같은 문장을 이해할 수 있다면, 감정의 흐름을 공유할 수 있어요. 그리고 그건 서로를 온전히 이해할 수 있게 만들죠.
갑자기 왠 ‘시’ 같은 글을 가져왔나 싶겠지만, 나는 저 글을 읽고 객체지향을 떠올렸다.(나도 어쩔 수 없는 개발자인가보다.) 적절한 단어가 자신의 감정을 정확하게 집어낼 수 있듯이, 적확한 모델링이 클린 코드를 짚어낼 수 있다. 개발자는 기획자에게 업무에 대한 개요와 스펙을 충분히, 상세하게 논의하고 이를 코드에 녹여내야 한다.
종종 우발적인 중복을 착각하여 공통 로직으로 뽑아내려고 하는 실수를 범하는데, 우발적인 중복인지 아닌지 구분이 잘 되지 않는다면 아직 머리 속에서 이러한 모델링이 명확히 이루어지지 않은 것이다. 개발 단계에서의 스펙은 계속 변화하므로 진짜 중복이라고 확신이 들 때에만 공통화 해야 한다.
두 번째 대원칙은 단순함이다. 클린 코드를 하는 목적이 무엇인가 ? 결국 “사람이 읽기 쉽게 하기 위함”이다. 간단한 로직을 “함수는 한 가지 일만 하라”는 원칙을 지키기 위해 수 없이 쪼갠 메소드보다, 하나의 함수에 몰아넣고 명백히 의미가 드러나는 이름을 붙이는 것이 훨씬 낫다고 생각한다.
설계의 미덕은 단순함과 명확함으로부터 나온다. 단순하고 명확한 설계를 가진 코드는 읽기 쉽고 이해하기도 편하다. 유연한 설계는 이와는 다른 길을 걷는다. 변경하기 쉽고 확장하기 쉬운 구조를 만들기 위해서는 단순함과 명확함의 미덕을 버리게 될 가능성이 높다.
오브젝트(조영호 저), p.305
클린 코드, 디자인 패턴, 객체지향 생활체조 원칙과 같은 교보재들은 객체지향과 클린코드를 이해함에 있어 훌륭한 가이드를 제시하지만 잘 못 이해하면 결과지향적인 방법론만 취하게 된다. 객체지향과 클린코드가 목적하는 바를 명확하게 바라보고 있다면 필요한 것만 취하고, 필요하지 않은 것은 버릴 수 있다. 마치 비유하자면 우리가 덧셈을 알고 있으면 1 + 2
가 주어지든, 수억 단위의 덧셈이 주어지든 어렵지 않은 것과 같다.
위 교보재들을 오해하여 흔히 하는 실수중 하나는, 추상화의 강점을 한 번 맛보게 되서 어떤 코드이던 추상화를 하려고 든다는 점이다. 변경 가능성이 낮은 클래스를 인터페이스로 추상화하게 되면 컴파일타임과 런타임 의존성 차이로 인해 읽는 사람이 오히려 번거로워 진다. 단순히 무리하게 웹플럭스를 도입하는 것과 같은 아키텍쳐 관점의 오버엔지니어링만 있는 것이 아니라, 코드 관점에서도 오버엔지니어링은 존재한다는 것을 잊지 말자.