고민의 시작
이전부터 좋은 코드란 무엇인지 고민해왔다.
이런 고민은 팀원과 함께 협업하여 개발해야했던 인턴을 진행하면서 좀 더 심해졌고, 정리되지 않은 상태였다.
이번 글로 하여금 생각을 정리해보고자한다.
좋은 코드를 작성하는 방법이라기보다는, 조금 더 근본적인 이해와 내가 해야하는 노력에 대한 글이다.
좋은 코드?
좋다라는건 참 모호하다.
좋은 사람은? 좋은 인생은? ~에가 빠진 좋다 라는 가치판단은 머리를 점점 혼란스럽게 할 뿐이다.
목적을 따져보기
목적을 따져보자. 코드는 왜 쓰는가?
코드는 불편한 문제를 해결하고, 자동화하여 가치를 제공하기 위해 만들어진다.
이런 관점에서는 좋은 코드를 따질 필요는 없다. 왜냐하면 완벽한 제품을 한번 만들면, 더이상 바꿀 필요가 없기때문이다.
하지만, 현대 사회에서 불편한 것들, 수요는 빠르게 생기고 사라진다.
그래서 제품을 한번 만들고 마는 것이 아니라, 지속적으로 관리하고, 새로운 것들을 추가해야한다.
코드를 통해 개발자는 제품에서 존재하던 오류는 없애고, 이를 통해 지속적으로 가치를 제공하며 새로운 것들을 추가할 수 있어야한다.
그래서 보통 좋은 코드라 함은 유지보수하기 좋은 코드를 말한다.
물론 여기까지도 혼자 개발을 한다면, 그다지 중요하지 않다.
일기장에 글씨체를 어떻게 쓰던, 글을 어떻게 쓰던,
어쨌든 일기장은 나만 알아볼 수 있다면 아무 상관 없는 것 처럼 미래의 내가 알아볼 수 만 있다면 괜찮다.
하지만 규모가 몹시 큰 제품을 혼자서 유지보수하는 것은 물리적으로 불가능하다.
모듈이 10만갠데, 제품에서 발생한 모든 오류를 혼자서 고치고, 새로운 기능을 추가한다는건, 정말 물리적으로 불가능하다.
따라서 여러 명이 유지보수 할 수 있어야한다.
그렇다면 나말고도 다른 사람이 잘 읽을 수 있어야한다.
그렇다면 좋은 코드란, 다른 사람이 잘 읽을 수 있고, 기능을 추가하기 좋은 코드를 말한다.
이 기능을 추가하기보다는 잘 읽을 수 있어야 한다에 집중해볼 것이다.
잘 읽을 수 있다.
추상화와 개념
아래 두 가지 글 중 어떤 것이 바로 이해가 되는가?
1. 민수는 동쪽으로 100m 이동한 후 우회전하여 200m를 이동하고, 다시 좌회전 하여 20m이동하여 다수의 사람들과 함께 학습할 수 있도록 마련된 장소에 가는 중이다.
2. 민수는 학교에 가는 중이다.
아무래도 두번째가 곧바로 이해된다. 그렇다면 두번째가 옳은가?
실제 코드에서는 아마 아래처럼 코드를 짤 수 있을 것 같다.
function 학교로가기(학생){
학생.동쪽전환()
학생.이동(100)
학생.우회전()
학생.이동(200)
학생.좌회전(20)
}
const 민수 = new 학생()
학교로가기(민수)
하지만 여기서 문제는 학교가 뭔지, 민수가 무엇인지 모두가 알고 있어야한다.
이건 일상에서 자주 사용하는 개념이기에 이런 것들이 바로 와닿지는 않는다. 개념을 조금 더 극단적으로 바꿔보자.
AWS CLI SDK로 S3버킷에 빌드된 웹서버 번들파일을 업로드한다.
AWS를 자주 사용한 사람이라면, 익숙한 문장일 수 있겠지만 AWS사용을 한번도 해보지 않은 사람은 이 문장이 의미하는 것을 바로 파악할 수 없다. 이건 추상화된 문장이다.
CLI, SDK, S3버킷, 웹서버, 빌드, 번들파일이라는 단어는 일상에서 자주 사용되지 않는 단어다.
이런 단어로 추상화된 문장을 배경지식이 없는 사람이 이해하는 것은 어렵다.
즉, 추상화는 일반적으로 알 수 있는 배경지식을 통해 이뤄져야한다.
또는 함께 코드를 작성하는 사람들이 가진 배경지식을 기반으로 진행되어야한다.
같은 배경지식을 갖기위해서는 계속해서 제품과 코드에 대해 의견을 나누어야 한다.
그렇지 않으면 점차적으로 파편화된 배경지식을 갖게되어 특정 사람, 특정 집단만 이해하는 추상화가 될 수 있다.
추상화의 정도
사용자가 접하는 추상화와 개발자가 다뤄야하는 추상화의 정도는 다를 수 있다.
엘리베이터를 예시로 들어보자.
사용자
엘리베이터 사용자는 추상화된 버튼을 눌러 목적지에 도착하면 된다.(2층을 눌러 2층으로 이동)
엘리베이터 사용자는 엘리베이터가 어떻게 동작하는지 알 필요가 없다. 그냥 버튼을 누르고 기다리고, 내리면 된다.
설계자/유지보수자
엘리베이터 설계자/유지보수자는 2층 버튼을 눌러 도착하는 추상화를 제공하는 사람이다.
2층이 몇 미터인지, 어떻게 층을 나눴는지, 도르레를 얼마나 움직여야 하는지, 최대 하중이 얼마인지를 알 필요가 있다.
하지만, 엘리베이터 설계자는 도르레가 어떻게 설계되었는지는 알 필요가 없다. 물론 알면 더 튼튼한 엘리베이터를 만들 수 있을지도 모른다.
혼란
엘리베이터 사용자에게 엘리베이터 설계자만큼의 추상화를 제공하는 것은 엘리베이터 사용을 어렵게 한다.
엘리베이터 설계자에게 엘리베이터 사용자에게 제공하는 버튼만 제공하는 것은 엘리베이터 설계를 어렵게 한다.
그러나 개발자는 제품의 복잡성이 올라감에따라 코드를 작성함과 동시에 소비하기도한다.
코드를 작성하는 사람 입장에서 만들어진 함수와 라이브러리를 사용한다면, 엘리베이터 사용자의 입장이 될 것이고,
핵심 로직을 작성하거나 유지보수하게된다면 엘리베이터 설계자의 입장이될 것이다.
따라서 제품을 만드는 개발자는 이 둘을 동시에 만족시켜야 하는 것이다.
추상화의 위치
개인적인 경험
이 두 가지 관점을 오가다가 헤매게 되면 주화입마에 빠지게 되는 것 같다.
어떻게 추상화된 영역을 분리할지가 모호함에 따라 주장이 약해졌고, 어찌됐던 컨벤션을 따르긴하는데 만족스럽지는 않은? 그런 상태가 됐었던 것 같다.
로직 부분을 컴포넌트에서 작성하는 경우가 많았는데, 해당 부분이 추상화가 되지 않았다고 느꼈다.
그렇다고 로직을 모두 분리하자니 파일이 어디에 무엇이 있는지 정리되기가 어려워진다고 느꼈다.
빠른 개발을 위해서 추상화 하지 않고, 공통 컴포넌트로 분리하려고 하니 불가능한 일에 가까웠다.
결과적으로 혼란을 가져왔다.
영역을 분리하기
명확하게, 설계 부분, 사용 부분을 분리하고 지킬 필요가 있다.
모든 설계부를 모두 라이브러리로 분리하면 좋겠지만
오류가 발생했을 때 매번 고쳐서 배포하고, 버전을 올리는것은 다양한 사람들이 유지보수하는 것을 더 어렵게 만든다.
정말 공통적으로 사용되는 부분, 완전히 분리가 된 책임을 지는 기능들만 분리하여 라이브러리로 배포하면 된다. 혹은 모노레포로 관리할 수 있을 것이다.
예를 들어, React는 선언형으로 UI를 업데이트한다는 역할을 수행한다.
물론 직접 함수형 컴포넌트를 호출하는 것도 가능하지만, React는 컴포넌트가 설명만 반환하기를 원하고, 이것을 지켜야 React가 원하는 사용자의 코드를 줄인다라는 목표를 달성할 수 있게된다.
그러나 공통적이지 않은 부분은? 어떻게 처리하는 것이 좋을까?
이 부분은 개발을 하는 사람들이 약속해야 한다. 컨벤션을 정해야한다.
그리고 나처럼 새롭게 신입개발자로 회사에 합류하려는 사람은 무작정 회사의 컨벤션을 따르는 것이아니라, 컨벤션에 물음을 던지고 어디가 사용부분이고, 설계부분인지 이해할 필요가 있다.
핵심적인 부분에 대해 물음을 던지지 못하고, 제대로 이해하지 못한다면 이제까지 개발했던 컨벤션과 충돌한다고 느끼게 될 것이고 나처럼 혼란에 빠지게 될것이다.
정리
정리하면, 팀과 함께 유지보수하기 좋은 코드를 작성하기 위해서는
- 일반적인 개념으로 추상화할 필요가 있다.
- 불가능하다면, 팀 내부에서 모두가 공유하고 있는 배경지식을 기반으로 추상화 해야한다.
- 설계자와 사용자에게 적절한 추상화정도는 다르다.
- 개발자는 코드의 설계자임과 동시에 사용자가 될 수 있다.
- 어디서 설계할지, 어디서 사용할지를 컨벤션으로 정하고 지키는 것이 매우 중요하다.
좋은 코드를 작성하기위해 회사에 들어가서 내가 해야할 것은
- 팀 내부에서 공유하고 있는 배경지식, 도메인지식을 빠르게 학습하여 추상화를 이해할 것
- 팀 내부의 컨벤션의 의도를 제대로 이해하기 위해 계속해서 물음을 던지고, 올바른 의도를 이해하여 지킬 것 (무작정 지키지 않을 것)
- 추상화 정도와 위치를 항상 고민할 것
위와 같이 정리할 수 있겠다.