OOP와 프론트엔드

SangHyeon Lee·2024년 12월 21일

시작하며

이전 OOP 스터디 이후, 개인 프로젝트를 준비했다.

두 가지 난관을 해결하는 것을 중점으로 뒀는데, 이들은 다음과 같다.

  1. 복잡한 UI 공유 상태 관리로 인한 로직 분리
  1. 각종 라이브러리 및 프레임워크 등의 툴에 대한 유연성

이들의 해결 방법은 OOP로 보였기에,

이를 프론트엔드에 어떻게 적용할 수 있을지 고민해보았다.

고찰

프론트에서 OOP가 잘 언급되지 않는 이유

OOP의 제일 핵심인 객체, 책임, 소통 이야기부터 해보자.

기본적인 전제 사항으로 객체들은 각자의 책임을 갖고, 상호 소통한다.

일반적으로 소통의 형태는 다음과 같다.

// SomeClass 내부
const response = insanceOfOtherClass.doThis(ingredient);

소통의 대상이 굳이 class일 필요는 없기에, 형태도 위와 같지 않아도 되지만

프론트엔드에서는 단방향 흐름이 일반적이라는 것이 차이점이다.

컴포넌트의 트리 구조

결국 컴포넌트가 UI의 조각이기 때문에, html의 구조를 따르기 때문에

트리구조를 이룬다는 점이 OOP적용에 어려움을 만들 것이다.

OOP를 적용함에 있어 생성자 주입에 대해 생각해보자.

컴포넌트에 DI를 적용해 상태를 전달한다면 prop drilling이 발생할 것이다.

컴포넌트에 컴포넌트를 전달하는 방법이라면 가시성이 떨어지게 된다.

이벤트에 대한 동작의 복잡성

객체들의 책임을 분리하고 동작을 설계하는 관점에서 보면 차이가 두드러진다.

이전의 "숫자야구 게임"을 생각해보자.

이 간단한 서비스에도, 하나의 입력에 대한 결과를 도출하려면 협력이 필요하다.

Guard가 validation을, Referee가 Judge를, GameMaster는 게임 진행을 책임진다.

각 객체들이 동적으로 동작하며 협력에 참여하는 것이다.

반면, 일반적인 프론트엔드 서비스에서 이러한 경우는 거의 없다.

대부분은, 주어진 입력 혹은 서버로부터의 데이터와 현재의 상태를 사용한다.

하나의 이벤트를 처리하는 동안, 상태를 동적으로 재계산 하는 일은 없을 것이다.

최대한 복잡한 프론트엔드 협력 동작을 생각해본다면, 애니메이션일 것이다.

그 것도 Interactive Animation말이다.

그러나 이러한 복잡한 작업은 WebGL등을 활용할 것이므로,

컴포넌트들로 복잡한 동적 상호작용이 일어날 상황은 쉽게 만나지 못할 것이다.

공유 상태 관리

컴포넌트 단위로 작업하기 시작하면서,

문제가 되는 지점은 멀리 떨어진 컴포넌트간 상태를 공유하는 것이었다.

이쯤와서 생각이 드는 점은, 아예 관심사가 다르다는 것이다.

컴포넌트와 상태 관리를 통해 프론트엔드에서는 UI가 상태 변화에 반응하여 "각각" 동작하면 되므로, 상태 관리의 중요성이 더 오르는 것이다.



프론트에서의 협력과 상태 관리

이렇게까지 각각 동작한다면, 어쩌면 협력이 없는 것은 아닐까?

그러나 협력의 정의를 넓힌다면, 컴포넌트들의 동작에도 협력은 존재하는 것 같다.

일반적으로 생각하는 직접적인 소통을 통한 협력이 아닌,

공유 상태를 통한 간접적인 소통을 통해서.

마치 대자보에 각 객체의 책임에 따라 수행한 동작의 결과를 기록하고,

이들을 각자 알아서 사용하는 방식이랄까.

작업을 추상화하고 분업한다는 점에서 협력이라 말할 수 있을 것이다.

커스텀 훅

협력이 가능하다면 책임도 부여할 수 있고 설계도 가능할 것이다.

그렇다면, 책임과 재사용성이 적용될 인터페이스와 클래스의 관계를

프론트엔드의 어디에서 찾을 수 있을까?

나는 컴포넌트가 구조체며, 커스텀 훅이 인터페이스의 역할을 수행할 수 있다고 생각했다.

공유 상태를 메시지로 본다면, 컴포넌트에게 기능을 제공하며 메시지를 다룰 수 있는 것은 커스텀 훅일 것이다.

커스텀 훅의 기능을 지킨다면, 어떤 컴포넌트에든 연결할 수 있으며

컴포넌트가 어떤 UI를 가질지는 해당 컴포넌트에서 정할 수 있다.

커스텀 훅은 재사용성을, 컴포넌트는 자율성을 갖게 된다.

상태 관리와 커스텀 훅

프론트엔드의 문제점인 공유 상태 관리 동작을 커스텀 훅 내부에 넣어도 될까?

재사용성을 중시하며 커스텀 훅을 제작한다면, 내부에 추상적이지 않은 동작을 넣는 것은 좋지 못하다.

그러나, 컴포넌트에서 사용되는 상태가 복잡한 경우, 컴포넌트 본문에 이들을 사용한 동작들을 꺼내놓는다면 가시성을 낮출 것이다.

뿐만 아니라, 이러한 동작들이 재사용된다면?

이에 대한 고민을 함께 해준 영상이 있었다.

커스텀 훅은 재사용성 면에서도 의미가 있지만,

캡슐화, 추상화에서도 중요한 위치를 갖는다.

정리해보자면 다음과 같을 것이다.

  1. 공유 상태 및 다양한 상태를 관리하는 경우, 여러 훅들이 쓰이는 경우
    커스텀 훅을 제작한다.

    이는 추상화 수준을 맞추는 것을 중점으로 두게 될 것이다.

  2. 재사용 될 추상 레벨의 복잡한 동작을 커스텀 훅으로 추출한다.

    이는 재사용성에 중점을 둔 것이다.

이 2가지를 기준으로 커스텀 훅을 제작하면 될 것 같다.

의의

커스텀 훅에서 공유 상태 관리를 한다면 OOP의 조건들은 얼추 갖추게 되므로

설계가 가능하다는 점이 중요한 것 같다.

설계에서 중요한 책임을 갖는 대상은 논쟁적일 수 있으나,

개인적으로는 여러 컴포넌트 간의 UI로직에 참여하는 대상은 책임을 갖는 것 같다.

공유 상태를 다루는 컴포넌트는 책임을 가진다는 것을 부정할 수 없을 것이다.

컴포넌트가 독립적으로 간단하게 동작하는 경우에는, 커스텀 훅을 적용하기 애매하다.

책임을 부여하는 방법은 커스텀 훅의 사용이 될 것인데,

그렇지 않고 컴포넌트에 책임을 부여한다면 렌더링 최적화나 추상화 정도에 따라 컴포넌트를 분리할 때 책임소지가 불분명해질 것이다.

따라서, "단순히 UI를 구성하는 컴포넌트"와 "UI로직에 묶여있는 컴포넌트"를 구분하는 것이 좋아보인다.

끝내며

UI로직에 묶인 컴포넌트에는 커스텀 훅을 통해 책임을 부여하는 기준을 세웠다.

이제 다음 문제는 이러한 컴포넌트를 어떻게 구분짓고 관리할 것인가인데,

이에 대한 방안으로는 Atomic 디자인 시스템이 될 것 같다.

원자에서 시작해서 bottom-up방식으로 합성해가는 구조인데,

각 Atom > Molecule > Organism > Template > Page로 나눠져 있다.

이 때,

Molecule는 SRP(Single Responsibility Principle)를 갖기에, 재사용성이 두드러진다.

Organism은 구체적인 컨텍스트를 가지기 때문에, Molecule보다 재사용성이 떨어지게 된다.

즉, Molecule부터 커스텀 훅을 적용할 수 있을 것이고,

Molecule는 독립적인 동작을 수행하는 "UI를 구성하는 컴포넌트"가,
Organism은 공유 상태를 사용하는 "UI로직에 묶인 컴포넌트"가 될 것 같다.


프론트엔드 작업을 하며, 추상적인 면에서 이렇게 고민을 했던 경험은 처음이어서 뜻 깊었다.

열심히 고민했던 OOP의 관점을 프론트엔드에도 적용할 수 있어보여 기쁘다.

profile
회고할 가치가 있는 개발을 하자

0개의 댓글