아키텍쳐 순수주의 졸업

아키텍처 입문

2020년에 로버트 C. 마틴의 Clean Code를 읽었습니다. 프로그래밍을 시작한 이래로 많은 시도와 삽질을 하였고, 코드와 개발을 주제로 숱하게도 번뇌에 쉽싸였는데, 그것들이 한 번에 정리되고 위로받는 기분이었습니다.

그때부터 프로그램을 바라보는 시각이 조금 바뀌었습니다. 기존에는 그냥 컴퓨터에게 일을 맡기는 명령들의 조합 정도로 보았다면, 이 무렵부터는 여러 구성 요소를 가진 하나의 시스템이자 거대한 성채처럼 보이기 시작했습니다. 먼 과거에 핵막이 있는 진핵생물이 탄생한 수준의 사건이 내면에서 일어난 것입니다.

프로그램은 고유의 구조를 가진 하나의 성채로, 구성 요소들이 잘 정돈된 위치에서 미리 정해진 방법으로 소통하며 각자의 일을 하는 시스템이자 어찌 보면 하나의 생명과도 같았습니다. 이렇게 생각하니 모든 코드가 마치 그들만의 색깔을 지니는 것 같았고 때로는 시나 문학처럼 읽히기도 했습니다.

드디어 프로그램의 복잡성이라는 난제에 실마리가 찾아오는 듯 보였습니다. 그리고 이런 인식 없이 그동안 코드를 어떻게 작성해왔는지 의문이 들기도 했습니다. 😅

아키텍처 순수주의

컴퓨터 공학에서 수십 년 동안 사람들을 힘들게 한 증상들이 있습니다. 코드가 엉켜 있어서 풀 수가 없게 되어 있다든가, 파일이 너무 길다든가, 함수 하나가 너무 많은 일을 한다든가, 아니면 테스트가 어렵다든가요. 이러한 고통에 노출된 사람들은 “소프트웨어는 왜 복잡할까”, “소프트웨어는 왜 실패할까” 같은 질문을 남겼습니다. 그리고 그러한 맥락에서 여러 디자인 패턴도 나오고 새로운 원칙들도 나왔습니다.

저는 밥 아저씨가 들고 온 클린 아키텍처 패턴에 매료되었습니다. 이 패턴은 너무 유명해서 자세한 이야기는 생략하겠습니다만, 제가 느끼기에 핵심은 이거였습니다:

  • 도메인과 주변부의 분리
  • 레이어 간 확실한 경계
  • 단방향 의존성

도메인은 천천히 변하고 기술은 빨리 변하니, 핵심이 되는 도메인 코드가 기술 변화에 영향 받지 않도록 최대한 순수하게 유지하자는 이야기입니다. 이걸 현실세계에서 실현하기 위한 방법으로 IoC도 나오고 DTO도 나오는 거죠.

이걸 접하자마자 저의 엔지니어 자아는 행복한 꿈에 취했습니다. 제가 보기에 현실세계는 지저분하고 복잡하지만 도메인은 순수하였기에, 이를 오염 없이 격리하자는 주장은 제가 추구하는 이상과 방향이 같았습니다.

이 시점에서 저는 “모든 프로그램은 이래야 해” 라는 생각을 가지게 되었습니다. 코드를 볼 때 이런 질문들을 던지기 시작했습니다:

  • “이게 가장 옳은 구조인가?”
  • “이론적으로 이게 가장 아름다운가?”
  • “코드가 마땅히 지녀야 하는 미덕을 지니고 있는가?”

그리고 이 기준에 부합하지 않는 코드를 보면 스트레스를 받고, 심하게는 (내적으로) 비방하기까지 했습니다. 지금 보면 조금 우습지만, 당시에는 이게 모든 엔지니어가 추구해야 하는 방향이라고 생각했거든요.

현실을 자각

다행히도(?) 이런 생각은 현실에 노출되면 조금씩 바뀌기 마련입니다. 자연의 섭리 같은 것일까요.

다양한 방면에서 엉망진창인 세계를 겪었습니다. 라인 수가 너무 많아서 읽을 수도 없는 것은 기본이었습니다. 린트는 커녕 인덴트도 제대로 되어 있지 않은 코드, 똑같은 이름인데 다른 일을 하는 코드, 명백히 “금기”를 어긴 코드, 이외에도 나열할 수도 없이 많습니다.

그런데 그중에는 신기하게도 아무도 문제삼지 않고 심지어 불편함도 느끼지 않는 코드도 많았습니다. 이해가 되지 않았습니다. 결국 그것들을 꾸역꾸역 고치다가 번아웃을 겪기도 했습니다.

그러다 어느 순간 무언가를 눈치챕니다. 이렇게 코드를 “예쁘게” 만든 결과가 무엇이지? 결국 제가 실질적으로 이룬 것은 개인적 성취감 말고는 아무 것도 없었습니다. 필요에 의해 한 것이 아닌, 자기만족을 위해 한 것이었으니까요.

기술적으로 아름답지 않아도 잘 돌아가고 유지되는 코드가 존재합니다. 완벽하지 않지만 그래도 팀에서 소통하고 개선하는 데에 아무런 문제도 없는 코드. 이 사실을 받아들이기가 어려웠습니다. 받아들이는 순간 “사람이 고통받지 않으려면 코드는 기술적으로 완벽해야 한다” 는 내적 당위가 무너지는 것이었기 때문입니다.

그리고 어느 순간 제 접근 방식이 옳았는지 의심하게 되는 시점이 찾아옵니다. “지저분”하지만 문제없는 코드는 애써 못 본 척 하며 넘겼는데, 제가 작성한 “우아한” 코드를 타인이 이해하기 어려워하는 모습을 보았기 때문입니다.

이상의 종말: 인지적 거부반응

예전부터 한결같이 “가독성 좋은 코드”를 지향해왔습니다. 모든 엔지니어링은 그 목표를 위해 수행되었습니다. 그리고 최소한 저에게는 읽기 편하고 기술적으로도 우아한 코드를 만들어내었습니다. 그리고 그 코드가 타인에게도 읽기 편할 것이라고 생각해왔습니다. 그러나 이 부분이 저의 가장 큰 착각이었습니다.

모든 설계는 저자의 사고방식의 투영입니다. 이 사고방식은 독자와 매끄럽게 공명할 수도, 반대로 거부반응을 일으킬 수도 있습니다. 그것은 마치 정신적 신체(mental body)에 장기를 이식하다가 생기는 면역 반응 같은 것입니다. 작성자에게는 자연스럽고 당연한 판단으로 보일지라도 독자에게는 공감할 수 없는 사고 흐름인 것입니다.

엔지니어링을 “묘기” 수준으로 적용하는 것은 작성자에게는 쾌감이지만, 그것을 읽는 독자에게는 해독해야 할 또 하나의 복잡함이 될 뿐입니다.

한때 “이런 아키텍처를 못 받아들이는 것은 노력 부족” 이라고 생각하던 시절이 있었습니다. 부끄러울 따름입니다.

엔지니어링은 팀 전체의 인지적 비용을 곱셈으로 올려버립니다. 새로운 구조를 도입한 비용은 작성자를 제외한 모든 독자에게 전가됩니다. 간단한 수정을 위해서 여러 파일들을 한참 뒤지고 코드를 한동안 들여다보아야 하는 일이 발생할 수 있습니다. 그리고 이렇게 비용(거부 반응)이 큰 아키텍처라면, 없는 편이 나을 수도 있습니다.

때로는 pathFromRoot(file)같은 wrapper 함수보다 path.join(this.rootPath, file)같은 원시적 표현이 낫습니다. 전자는 아무리 이름을 잘 지었더라도 결국에는 작성자만 아는 표현인 반면, 후자는 너무나도 전통적이고 잘 알려진 표현이기에 저 호출 자체가 마치 하나의 언어 문법처럼 아무 비용 없이 읽힐 수 있기 때문입니다.

이상 대신 공생을

분명히 “좋은” 코드라는 것은 존재합니다. 그러나 무엇이 “좋은” 코드인지는 생각해볼 여지가 있습니다.

구조적으로 흠결없고 일관되고 우아하며 변화에 강한 코드는 매우 이상적이며, 기술적 관점에서 보았을 때 우리가 바라보아야 할 목표임은 분명합니다.

그러나 코드가 존재하는 목적을 항상 상기해보아야 합니다. 코드는 일을 하기 위해 존재하고, 그런 코드를 많은 사람들이 쉽고 빠르게 관리할 수 있도록 하기 위해 엔지니어링이 존재합니다. 구조적 이상은 그 수단으로서 존재할 뿐입니다.

그러니 이제는 질문을 바꾸기로 했습니다:

  • “이 구조가 상식적이고 일반적인가?”
  • “이렇게 짜면 다른 사람이 바로 알아볼 수 있을까?
  • “이거 나중에 수정하는 데에 얼마나 걸릴까?”

아무도 안 쓰는 완벽한 구조보다, 모두가 조금은 불편하지만 오래 가는 구조가 낫습니다.

마무리

어쩌면 좋은 아키텍처란, 기술적으로 옳은 구조가 아니라 사람을 덜 힘들게 하는 구조인지도 모르겠습니다. 코드 너머에는 항상 인간이 있음을 잊지 말아야 합니다. 나의 코드가 다른 사람에게 어떻게 읽힐지에 집중해야겠습니다.

오히려 예쁘고 우아한 설계보다 중요한 것은 작성자가 왜 이런 판단을 했는지 추측할 수 있는 한 줄의 주석이 아닐까 생각합니다.

댓글