좋은 코드 작성은 매우 중요합니다. 나쁜 코드를 작성하지 않는 것은 특히 중요한데, 이는 코드가 소프트웨어의 기반이기 때문입니다. 소프트웨어가 나쁜 코드로 구성되면 결국 소프트웨어는 망가지게 됩니다. 망가진다는 것은 기능의 확장이나 수정이 불가능해진다는 의미입니다.
가끔 개발자들은 당장의 기능 구현을 위해 좋은 코드를 포기하고자 하는 유혹에 빠지기도 합니다. 그러나 이러한 결정은 대부분 좋지 않은 결과로 이어집니다. 실제로 프로그램의 복잡성이 증가할수록 코드 한 줄을 입력하거나 수정하는 데 드는 시간은 점점 늘어납니다. 시간을 절약하기 위해 좋은 코드를 포기했지만 결국에는 더 많은 시간이 소요되는 경우는 흔한 일입니다.
또한, 많은 경우 기능 구현의 급함으로 인해 시간을 아끼기 위해 좋은 코드를 포기하는 것보다는 좋은 코드를 찾아보고 고민하는 노력을 줄이게 됩니다. 그리고 나중에 "프로젝트 완성이 더 급하니까, 기능 완성이 더 급하니까 어쩔 수 없었어"라는 태도로 일을 처리하게 됩니다. 하지만 나중에 다시 고칠 생각으로 코드를 다시 살펴볼 여유가 생기는 경우라도 대부분 실제로는 그런 일이 발생하지 않습니다.
물론, 여기서 말하는 나쁜 코드는 "나중에 볼 때 나쁜 코드"를 의미하는 것은 아닙니다. 때로는 그 때는 최선이었던 방법이 시간이 지남에 따라 현재에는 적합하지 않을 수 있습니다. 이러한 이유로 레거시 코드로 취급되는 경우도 분명히 존재합니다. 그러나 그 때의 최선이었던 코드는 상대적으로 쉽게 마이그레이션하거나 개편할 수 있습니다. 그러나 애초에 나쁜 코드는 이러한 작업들을 거의 불가능하게 만듭니다.
따라서, 개발자는 반드시 좋은 코드, 깨끗한 코드를 작성하기 위해 노력해야 합니다. 또한, 개발자의 실력을 평가하는 중요한 요소 중 하나는 좋은 코드, 깨끗한 코드를 작성할 수 있는 능력입니다.
좋은 코드를 작성하기 위해서는 좋은 코드의 기준과 작성 방법에 대해 고민하고 연구해야 합니다. 개발 업계에서는 학습한 내용을 공유하는 문화로 인해 많은 개발자들이 좋은 코드를 작성하기 위한 원칙과 방법을 연구해왔습니다.
개발에서 "관심사의 분리(Seperation of Concerns)"는 좋은 코드 작성을 위한 기본 원칙 중 하나입니다. 다양한 디자인 패턴, 기법, 아키텍처 등의 개선 방법들은 모두 이 관심사의 분리를 기반으로 합니다.
"관심사"는 모듈이 수행하고자 하는 목적을 의미합니다. 모듈은 함수, 클래스 등의 단위로 이해할 수 있습니다.
"관심사의 분리"는 각 모듈이 동시에 여러 관심사를 처리하지 않고, 하나의 관심사에만 집중하도록 분리하는 것을 의미합니다.
즉, 관심사의 분리는 코드의 모듈화와 구조화를 통해 코드의 가독성, 재사용성, 유지보수성 등을 향상시키는 원칙입니다. 이를 통해 각 모듈은 명확하게 정의된 역할과 책임을 가지고 독립적으로 작동할 수 있으며, 시스템 전체의 복잡성을 낮추고 유연성을 높일 수 있습니다.
그렇다면 왜 관심사를 분리해야 할까요? 하나의 모듈에서 여러 기능을 할 수 있으면 얼핏 좋아보이는 것 같은데 왜 하나의 모듈은 하나의 관심사만 처리해야 할까요?
관심사를 분리하면 하나의 모듈은 하나의 목적만 가지게 됩니다. 하나의 목적만 가지게 된다는 말을 조금 다르게 해석해보면, 이제 이 코드가 수정될 이유는 한가지만 존재하게 된다는 의미입니다.
소프트웨어는 계속해서 변화합니다. 하드웨어(hardware)는 물리적인 실체가 있는 제품들을 의미합니다. 반면 소프트웨어(software)는 코드로 구성된 무형의 제품들을 의미합니다. 물리적인 실체가 있는 제품들에 hard라는 용어가 붙은 의미는 수정하기가 힘들기 때문입니다. 하드웨어를 수정하기 위해서는 실제로 부품을 생산하고 교체하는 과정을 거쳐야하기에 많은 공수가 듭니다. 반면, 소프트웨어는 상대적으로 수정하기 쉽습니다. 코드로 이루어져있기 때문에 소프트웨어를 수정하기 위해서는 단순히 코드의 일부만 수정하면 소프트웨어의 동작을 변경할 수 있습니다. 이말은 곧 진정한 의미의 소프트웨어는 변화에 유연하게 대응할 수 있어야한다는 의미입니다. 실제로 소프트웨어가 잘못 설계되어서 변경하기 힘든 상황이라면 이는 하드웨어와 다를 바 없어지는 것입니다.
위의 이유로 인해서 소프트웨어에서의 변화는 필연적이며, 좋은 소프트웨어 일수록 기존의 기능을 수정하는 것과, 기능을 확장하는 것을 잘 할 수 있어야 합니다. 이를 우리는 흔히 “유지보수"라고 부릅니다.
본론으로 돌아와, 관심사를 분리하는 이유는 소프트웨어의 특정 부분이 변경되는 이유를 한가지로 제한하기 위해서입니다. 만약 여러 모듈들이 여러 관심사를 동시에 다루고 있다면 특정분야에 대해서 수정을 해야 할 때 관련된 모든 모듈을 수정해야 할 것입니다.
예를 들어, 애플리케이션 내에서 인증&인가
에 대해서 모든 모듈들이 관여하고 있다면, 추후 인증&인가의 동작을 수정해야 할 경우에는 모든 모듈들을 일일이 돌아다니며 수정을 해야 할 것입니다.
하지만, 인증&인가
를 다루는 핵심 모듈을 한가지로 제한해두고 나머지는 이 모듈을 사용하는 형식으로 설계되어 있다면 추후 인증&인가
의 동작이 변경되었을 경우에는 해당 모듈만 수정하면 되기에 변화에 유연하게 대응할 수 있게 됩니다.
이처럼 관심사의 분리는 소프트웨어를 만드는 프로그래밍에서 가장 기본이 되는 원칙입니다. 기본이 되는 원칙이기에 비슷한 개념을 표현하는 여러 많은 단어들과 프로그래밍 격언들이 생겨났습니다.
React v16.8에서 Hook이 발표되고 난 후 React에서 컴포넌트를 선언하는 방법의 대세는 클래스 컴포넌트에서 함수 컴포넌트로 옮겨왔습니다. 많은 사람들이 익숙하고, 이미 기존에 많은 코드들이 작성된 방법인 클래스 컴포넌트에서 함수 컴포넌트로 옮겨온 이유중에는 함수 컴포넌트의 문법이 더 단순하고, 교착상태로 인한 버그가 발생하지 않는다는 장점도 있지만, Custom Hook의 편리함과 유용성도 큰 비중을 차지하고 있습니다.
앞서, 관심사의 분리에 대해 학습했습니다. 그렇다면 리액트가 가진 관심사는 어떤 것들이 있을까요?
리액트는 UI를 구축하기 위한 라이브러리입니다. 따라서 리액트가 가진 핵심적인 관심사는
위 두가지로 나눌 수 있습니다.
이 중 UI는 실제 코드상에서는 JSX
라는 형태로 표현됩니다. 그리고 로직은 유저의 입력에 반응하고, API
를 호출하고, 스크린의 변화에 반응하는 등 여러 동작들을 통해서 UI에 영향을 미치는 행위라고 할 수 있습니다.
리액트를 활용하는 개발자들은 UI와 로직을 분리하기 위해 여러 기법을 연구합니다. 그 중 가장 유명한 기법은 Presentational - Container 패턴입니다.
Presentational - Container 패턴은 컴포넌트를 Presentational과 Container로 크게 두 계층으로 분리합니다. Container는 로직을 다루는 부분으로 UI에는 관여하지 않고, UI를 구성하고 변화하기 위한 로직에 집중합니다. Presentational은 로직을 신경 쓰지 않고, UI가 어떻게 구성되어야 하는지에 집중하는 컴포넌트입니다.
이 패턴은 Presentational 컴포넌트를 Container로 감싸고, 필요한 정보와 로직을 props로 전달하는 방식으로 설계합니다. Presentational 컴포넌트는 UI를 그리고, Container 컴포넌트는 데이터와 로직을 관리하는 역할을 합니다. 이를 통해 관심사를 분리하고 컴포넌트의 재사용성과 유지보수성을 높일 수 있습니다.
Presentational - Container 패턴은 관심사 분리를 위한 표준 패턴으로 사용되었습니다. 하지만 Hook이 등장한 후에는 Custom Hook을 사용하여 더 효율적으로 관심사를 분리할 수 있다고 판단되어 Presentational - Container 패턴은 많이 사용되지 않게 되었습니다. Custom Hook은 로직을 추출하고 재사용할 수 있는 기법으로 인식되어, Presentational 컴포넌트와 로직을 분리하는 데 더 적합하다고 평가받고 있습니다.
커스텀 훅은 리액트가 기본적으로 제공하는 훅들(useState
, useEffect
등)을 이용하여 만든 함수입니다. 주로 UI를 변경하기 위한 로직을 담고 있습니다.
함수형 컴포넌트에서 로직은 주로 useState, useEffect 등의 Hook을 사용하여 구현됩니다. 하지만 컴포넌트 내부에 많은 로직이 포함되면 컴포넌트가 복잡해지고, 동일한 로직을 여러 컴포넌트에서 재사용하기 어렵다는 단점이 있습니다.
커스텀 훅은 이러한 문제를 해결하기 위해 등장한 기법입니다. 커스텀 훅은 기존의 훅들을 사용하여 동일한 로직을 추출하고 별도의 함수로 정의하여 여러 컴포넌트에서 재사용할 수 있게 합니다.
커스텀 훅의 조건은 다음과 같습니다:
이를 통해 커스텀 훅은 편리하게 로직을 추출하고 재사용할 수 있어 코드의 가독성과 재사용성을 높일 수 있습니다.