[Enact] 포커스를 담당하는 고차컴포넌트 (HOC)

HOC의 개념과 실제 포커싱 처리 예시

Frontend
2024년 1월 15일

Enact로 개발을 시작하기에 앞서, focusing을 담당하는 두 HOC(Higher Order Component)의 차이점을 알아보려고 합니다.
개발 초기 단계에 꽤 헷갈리는 개념이었기에 정리해두고자 합니다.

HOC ?

HOC란, Higher Order Component의 약자로서 고차 컴포넌트라고도 불립니다.
이는 쉽게 말해 React에서 컴포넌트의 재사용, 로직의 공유, 상태 추상화 등을 위해서 사용되는 기술이라고 할 수 있는데,
HOC는 컴포넌트를 인자로 받아서 새로운 컴포넌트를 반환하는 함수라고 할 수 있습니다.
이를 통해 React에서 컴포넌트 간의 코드를 재사용할 수 있게 해줍니다.

함수를 반환하므로, HOF(Higher Order Function) 즉, 고차 함수 라고도 할 수 있으며, 실제 React에서도 이와 관련한 기술을 많이 사용하고 있습니다.
완벽하게 동일한 패턴은 아니지만, React 에서 자주 쓰이는 memo 역시 이 개념을 활용한다고 할 수 있습니다.

따라서, SpotlightContainerDecoratorSpottable은 포커싱에 관련한 기능을 제공하는 고차 컴포넌트라고 할 수 있습니다.

SpotlightContainerDecorator

SpotlightContainerDecorator는 컴포넌트를 Spotlightfocusable 한 컨테이너로 만들어 주는 역할입니다.
포커스 컨테이너는 포커스를 받을 수 있는 자식 컴포넌트들을 그룹화한 것으로, 포커스 이동을 이 SpotlightContainerDecorator 컨테이너의 자식 컴포넌트 내에서만 제한할 수 있게 해줍니다.
이는 복잡한 UI에서 포커스 관리를 단순화하는데 굉장히 유용하게 사용할 수 있고, 특정 UI의 section 내에서 포커스 이동을 제한하거나 포커스를 이동하고 싶을 떄 사용할 수 있습니다.

즉, SpotlightContainerDecorator는 그 자체로 포커싱이 가능한 컴포넌트를 만드는 것이 아니고, 내부에 있는 Spottable한 컴포넌트들을 관리하는 단순한 wrapper라고 생각할 수 있습니다.

SpotlightContainerDecorator는 보통 이렇게 사용됩니다.

import { SpotlightContainerDecorator } from "@enact/spotlight/SpotlightContainerDecorator";
 
const Container = SpotlightContainerDecorator({}, "div");

여기서 SpotlightContainerDecorator는 총 2개의 인자를 받아올 수 있는데,

  • 첫 번째 인자로는 config 객체를 받아오고,
  • 두 번째 인자로는 div 혹은, ul, section 등의 시멘틱 태그들을 받아올 수 있습니다.

config 객체 내에서 자주 사용하는 옵션으로는 leaveFor 혹은 enterTo가 있는데, 각각에 대한 설명은 다음과 같습니다.

leaveFor

leaveFor는 포커스가 현재 컨테이너를 벗어날 때의 동작을 지정할 수 있습니다.
이 기능을 사용하여 특정 방향으로 포커스를 이동시킬 때, 다른 컨테이너나 컴포넌트로 포커스를 명시적으로 전환할 수 있습니다.
예를 들어 특정 방향키를 눌렀을 때 포커스가 다른 특정 컨테이너로 이동을 하거나, 이동하지 않게 만들 수 있습니다.

const Container = SpotlightContainerDecorator(
  { leaveFor: { left: "", right: "" } },
  "div"
);

이런 식으로 작성하게 되면, 키보드의 왼쪽이나 오른쪽 방향키를 눌렀을 때, 다른 컴포넌트 혹은 컨테이너로 포커싱이 이동하지 않게 만들어 줍니다.
실제로 개발을 진행하며 특정 컴포넌트 혹은 컨테이너에 포커싱이 가지 않도록 하기위해 자주 사용하게 됩니다.

enterTo

enterTo는 포커스가 해당 컨테이너로 진입할 때의 기본 동작을 지정해줍니다.

const Container = SpotlightContainerDecorator(
  { enterTo: "last-focused" /* 혹은 'default-element' */ },
  "div"
);

위 설정은, 컨테이너 내부로 포커싱이 이동하게 될 때, 컨테이너를 마지막으로 떠날 때 포커스된 요소에 다시 포커스를 부여합니다.
실제로 위 기능은 사용자가 이전 상태로 돌아가려고 할 때 일관된 사용자 경험을 제공해주기에 유용하게 쓰인다고 할 수 있습니다.

반면, default-element는 특정 요소에 항상 포커스를 부여하게 됩니다.
내가 지정한 요소가 없다면, 컨테이너의 첫 번째 요소로 포커스가 가게 됩니다.

Spottable

Spottable이란, 해당 컴포넌트를 포커스가 가능하게 만들어주는 고차 컴포넌트입니다.
이를 통해서 컴포넌트는 포커스를 받을 수 있께 되고, 사용자의 입력에 반응하여 동작을 변경하거나, 스타일을 부여할 수 있습니다.

컴포넌트를 일반 시멘틱 태그인 div로 감싸게 되면, 해당 컴포넌트에 대해 키보드 이벤트에 대한 포커싱 처리를 구현하는 로직이 따로 필요한데,
Spottable을 사용하게 되면, 해당 컴포넌트를 focusable 하게 만들어주는 것입니다.

import { Spottable } from "@enact/spotlight/Spottable";
 
const SpottableComponent = Spottable("div");

이는, 기존 div로 만든 컴포넌트를 Spottable로 감싸주어 focusable하게 만들어주어 포커싱 관련한 로직을 따로 작성할 필요가 없게 해줍니다.

쉽게 말하자면

필자는 개발을 진행하면서 개발 초기 단계에 SpottableSpotlightContainerDecorator의 차이점에 대해서 제대로 이해하지 못했습니다.
따라서 두 개의 역할과 필요성에 대해 인지하지 못했고, Enact를 사용하면서도 Enact스럽지 않은 개발 을 하고 있었습니다.

SpotlightContainerDecoratorSpottable로 만들어진 컴포넌트를 감싸는 container의 역할을 하면서, 포커싱이 진입할 때의 처리를 돕는 역할이고,
Spottable은 컴포넌트 자체를 focusable하게 만들어주는 것입니다.

Tags:
EnactHOC