saewoo
githubemail
2023-12-31

인터페이스를 잘 정의하면 좋은 점

Top Down 방식으로 사고하는 도구

개발을 하다보면 이런 저런 코드를 만나게 됩니다. 그 중 이해하기 쉬운 코드가 있고, 어려운 코드가 있습니다. 제가 생각하기에 이해하기 쉬운 코드는 인터페이스가 잘 정의된 함수, 컴포넌트였습니다. 반면에 이해하기 어려운 코드는 어떤 동작을 하는지 구현을 봐야하는 함수 혹은 컴포넌트 였습니다. 그래서 '인터페이스는 무엇일까?' '어떤 장점이 있을까?'에 대한 저의 생각을 정리해보았습니다.

인터페이스란?

인터페이스는 무엇일까요? 인터페이스의 정의를 위키피디아에서 찾아보았습니다.

컴퓨팅에서 인터페이스는 컴퓨터 시스템의 두 개 이상의 개별 구성 요소가 정보를 교환하는 공유 경계(접점)를 의미합니다.

In computing, an interface is a shared boundary across which two or more separate components of a computer system exchange information.

https://en.wikipedia.org/wiki/Interface_(computing)

저는 인터페이스가 '접점'으로 설명하는것을 좋아합니다. 하드웨어에서 모니터의 케이블 타입만 알면 모니터의 내부 구현을 몰라도 모니터를 연결하여 사용할 수 있듯, 소프트웨어에서도 인터페이스를 알면 내부의 구현이 어떻게 되어 있든 상관없이 사용할 수 있습니다.

인터페이스의 장점 - Top-Down 방식으로 접근 가능

예시를 들어보겠습니다. 프로젝트에서 공통적으로 사용하는 Button.jsx 컴포넌트가 있다고 하겠습니다.

const Button = (props) => {...}

위의 Button 컴포넌트의 props를 보면 해당 컴포넌트가 어떤 기능을 제공하는지 하는지 알 수 없고, 구현을 봐야 동작을 알 수 있습니다. bottom-up 방식이죠. 이는 피로하고 생산적이지 못합니다. 저는 이러한 답답함이 수능에서 주제 찾기 문제와 비슷하다고 생각합니다.

수능 주제 찾기 문제 대표적인 bottom-up 방식의 수능 영어 문제

하지만 인터페이스가 잘 정의되어 있다면 어떨까요?

Top-Down 방식이 가능해집니다.

interface를 보고 외부에 공개된 동작을 유추하고 필요한 경우에만 구현을 찾아보면 됩니다. 즉 생산적입니다.

interface Props extends HTMLAttributes<HTMLButtonElement> {
  /**
   * 버튼 라벨의 왼쪽에 표시되는 콘텐츠 */
  leftSection: React.ReactNode;
  /**
   * 버튼 라벨의 오른쪽에 표시되는 콘텐츠
   */
  rightSection: React.ReactNode
}

const Button = (props: Props) => {...}

interface라고 해서 타입만을 이야기하는 것은 아닙니다. 외부에 공개된 동작을 잘 정리하는 것이 중요하다는 점을 말하고 싶습니다.

// 버튼 컴포넌트입니다.
// 컴포넌트에 전달할 수 있는 props들은 다음과 같습니다.
//   - 기본 속성
//     - 기본 속성들을 모두 전달 할 수 있습니다.
//   - 추가 가능 속성
//     - props.leftSection: 버튼 라벨의 왼쪽에 표시되는 콘텐츠
//     - props.rightSection: 버튼 라벨의 오른쪽에 표시되는 콘텐츠
const Button = (props) => {};

혹은 jsDoc을 활용해 좀 더 정돈된 문서로 작성해도 좋을것 같습니다.

/**
 * 버튼 컴포넌트입니다.
 * @param {Object} props - 컴포넌트에 전달되는 속성들
 * @param {React.ReactNode} props.leftSection - 버튼 라벨의 왼쪽에 표시되는 콘텐츠
 * @param {React.ReactNode} props.rightSection - 버튼 라벨의 오른쪽에 표시되는 콘텐츠
 */
const Button = (props) => {};

jsDoc은 문서화에는 훌륭하지만 코드를 문서에 맞게 강제화할 수 없습니다. 이런 점에서 문서와 코드를 강제로 일치시킬 수 있는 타입스크립트로 interface를 작성하는 것이 좀 더 나은 선택이라고 생각합니다.

또한 부수효과(?)로 외부에 공개된 interface만 유지된다면, 내부의 구현은 마음대로 리팩토링할 수 있는 유연함이 생깁니다. 기존에 제공하던 인터페이스만 유지된다면 라이브러리를 쉽게 교체할 수도 있겠네요.

예시 - 라이브러리의 interface를 보고 동작 유츄하기

인터페이스를 보고 Top-Down 방식으로 문제를 해결한 저의 이야기를 들려드리고자 합니다.

회사에서는 여러 프로젝트들이 있을것이고, 그 중 하나만을 담당하면 좋겠지만 실상은 여러 프로젝트를 다루는 경우가 있습니다.

또한 모든 프로젝트가 모두 동일한 스택을 사용하면 좋겠지만 현실을 그러지 못한것 같습니다.

그러다보니 익숙한 라이브러리가 있고 생소한 라이브러리가 있기 마련입니다.

저의 경우 회사 대부분의 프로젝트가 web 기반이라 web에서의 기술 스택은 익숙한데, app은 익숙하지 않은 라이브러리들이 있었습니다. (특히 입사 초반엔 사용해보지 않은 라이브러리들을 익히는것도 일인것 같습니다.)

기획의 요구사항 중 몇 단계의 스텝을 거쳐 특정 페이지로 왔든 처음 페이지로 이동시켜야하는 경우가 있었습니다.

"D 단계에서 사용자가 특정 버튼을 클릭하면 A 페이지로 이동시켜주세요."

A 👉 B 👉 C 👉 D

A 👉 B 👉 D

한 번 뒤로가기라면 익숙한 pop 메서드를 쓰면 되지만, 중간 단계의 횟수가 다르기에 어떻게 처리해야할 지 애매했습니다.

일단 navigation 역할을 담당하는 react-native-router-flux라이브러리의 Actions 객체의 인터페이스를 확인해봤습니다. (브라우저에서 history stack과 비슷한 역할을 수행합니다.)

interface ActionsStatic {
  currentScene: any;
  jump: (sceneKey: string, props?: any) => void;
  pop: (params?: { animated?: boolean }) => void;
  popAndPush: (sceneKey: string, props?: any) => void;
  popTo: (sceneKey: string, props?: any) => void;
  push: (sceneKey: string, props?: any) => void;
  refresh: (props?: any) => void;
  replace: (sceneKey: string, props?: any) => void;
  reset: (sceneKey: string, props?: any) => void;
  drawerOpen?: any;
  drawerClose?: any;
}

popTo 메서드의 인터페이스를 보니 특정 페이지의 key를 받아서 해당 페이지까지 pop을 수행해하는 역할을 할 것 같았습니다. 실제로 그러한지 문서에서도 확인해봤습니다.

지정한 키로 Scene에 도달할 때까지 검색 스택을 pop합니다.

Pops the navigation stack until the Scene with the specified key is reached.

문서에서도 예상한대로 작성되어 있었습니다. 이처럼 인터페이스가 잘 정의되어 있다면 Top-Down 방식으로 문제를 빠르게 해결 할 수 있습니다. (참고로 해당 라이브러리는 인터페이스에 설명으로 주석이 포함되어 있지 않지만, 다른 라이브러리의 경우 interface에 주석이 포함되어있어 실제 문서를 web 상에서 확인하지 않더라도 충분히 이해할 수 있습니다. 또한 IDE를 통해 해당 함수에 호버시 작성되어 있는 주석을 쉽게 확인 할 수 있습니다. 이 점은 구 버전의 라이브러리를 쓰는경우 해당 버전의 문서를 직접 찾아보는 수고를 덜어줍니다.)

요약

인터페이스란 서로 다른 구성요소의 접점이고, 인터페이스를 작성하면 Top-Down 방식으로 접근이 가능해 빠른 문제해결이 가능하다는 이야기를 했습니다. 우리가 작성하는 코드도 interface만 보고도 동작을 유추할 수 있도록 작성해보는것은 어떨까요?