xction-dev / xction.co.kr

Xction!의 홈페이지를 만들고 있습니다
0 stars 0 forks source link

Architecture - UI #24

Open designDefined opened 10 months ago

designDefined commented 10 months ago

프론트엔드 작업자를 위한 아키텍쳐 원칙 정리 (UI)

보통 UI라고 하면 개발보다는 디자인과 퍼블리싱의 영역으로 여겨집니다. 그렇기에 개발에서 주로 다루는 아키텍쳐에 관한 논의가 UI에 적용되는 일은 별로 없습니다. 그러나 오늘날에는 다채로우면서도 체계적인 디자인이 웹 디자인에 적용되면서, 이를 아키텍쳐의 측면에서 바라보고자 하는 시도도 많이 도입되고 있습니다. 잘 구조화된 UI는 개발을 빠르게 하고, 디자인 일관성을 지킬 수 있게 하며, 디자이너와 개발자의 소통을 편리하게 합니다.

아래 제가 임의로 UI 아키텍쳐의 원칙들 몇 개를 정리해 두었습니다. 우리가 사용하는 Next13과 mui에 대한 의존성을 적당한 수준으로 유지하면서도 서비스팀에서 만들어 준 디자인 시스템에 적합하도록 고안해 보았습니다. 물론 전혀 검증되지 않은 아키텍쳐이므로, 자유로운 의견 부탁드리겠습니다.

목차

계층들

프로그래머에게 있어 아키텍쳐를 짠다는 것은 기본적으로 계층에 따라 코드를 분류하고, 그것들 간의 위계상호작용을 정립한다는 것을 의미합니다. UI 역시 마찬가지입니다. 이전에 논의한 바처럼, 우리는 화면을 페이지 - 모듈 - 컴포넌트의 세 계층으로 나누고 각각의 역할과 용법을 명시함으로써 UI를 하나의 아키텍쳐로 정리할 것입니다.

페이지를 제외하면, 계층의 구분은 조금 애매할 수 있습니다. 아래 내용을 참고하시면 자칫 헷갈릴 수 있는 각 계층의 구분을 명확히 할 수 있을 겁니다.

컴포넌트 만들기

기본 원칙

컴포넌트는 src/components 디렉토리에 위치합니다. 컴포넌트는 어떤 페이지나 모듈에서든 사용할 수 있게 확장성이 높게 제작되어야 합니다. 컴포넌트는 먼저 디자인과 기능을 확인한 후, jsx과 mui를 커스터마이징하고, 사용하기 편하게 export하는 방식으로 제작됩니다. 각 컴포넌트의 디렉토리가 너무 잘게 나누는 것을 주의하세요. 예를 들어 CapsuleButton과 SquareButton, TextButton등이 다 다른 디렉토리에 존재하게 된다면 components 디렉토리를 너무 많이 차지하게 될 것입니다. 그보다는 src/components/Button 디렉토리 내에 Capsule.tsx, Square.tsx 등의 파일이 위치하고, index.tsx에서 컴포넌트들을 묶어서 <Button.Capsule/>, <Button.Square/>와 같은 방식으로 사용하게끔 내보내는 게 이상적입니다.

예시 코드

코드에 주석을 달아놓았고, 자세한 설명은 아래 달아두겠습니다.

/**  @jsxImportSource @emotion/react */ // emotion을 위한 jsx pragma
"use client"; // 이게 필요한 이유는 최하단의 '호환성 문제'에서 설명합니다.

import typography from "@/styles/typography"; // import style foundation
import { css } from "@emotion/react"; // jsx와 mui의 커스터마이징에 사용되는 라이브러리. mui가 아니라 @emotion/react에서 import해야 합니다.
import { HTMLAttributes } from "react"; 

type HeadingProps = HTMLAttributes<HTMLHeadingElement>; // 확장성을 위해 이 컴포넌트가 대체하는 <h1/>~<h6/>의 props를 그대로 가져옵니다.

const heading = css` // heading에 공통으로 적용할 스타일 지정
  margin: 0;
`;

function H1(props: HeadingProps) {
  return (
    <h1 
      // 미리 지정한 스타일과 foundation을 혼합하여 style 결정
      css={css`
        ${heading}
        ${typography.h1}
      `}
      // 기본 props 일괄 적용
      {...props}
    >
      {props.children}
    </h1>
  );
}

/** 중략 **/ // H2 ~ H6 마저 정의

const Heading = { H1, H2, H3, H4, H5, H6 }; // 가독성을 위해 하나의 객체로 묶기

export default Heading;

디자인 확인

Xction 디자인 시스템 가이드를 보시면 Button, Cards와 같은 Component들이 정리되어 있습니다. 기본적으로 개발의 컴포넌트는 여기에 정의된 재사용 가능한 디자인 단위에 따라 제작됩니다. 그러나 예시로 다룬 Heading과 같이, 필요에 따라 따로 같은 페이지에서 Foundation이라고 정리된 디자인 항목들도 존재할 것입니다. 이는 컴포넌트가 아니라 재사용 가능한 스타일 규칙, 즉 css 코드만을 정리해둔 것입니다. 이는 src/styles 디렉토리에 따로 정리될 것입니다. Heading에서 사용한 typography가 그 예입니다.

Props 확장하기

컴포넌트는 jsx나 mui에 존재하는 특정 태그를 대체합니다. 확장성을 위해서는 대체하는 대상의 prop을 extend하여 사용하는 것이 권장됩니다. jsx태그나 특정 컴포넌트의 props 목록을 불러올 때는 다음과 같은 방법을 사용할 수 있습니다.

// 기본 jsx 태그를 대체할 경우
// HTMLAttributes라는 고차 타입을 이용합니다.
import { HTMLAttributes } from "react";
type JsxProps = HTMLAttributes<HTMLDivElement>;

// 특정 리액트 컴포넌트를 대체할 경우
// 리액트 함수 컴포넌트의 첫 번째 인자가 props 객체임을 이용합니다.
import { Button } from "@mui/material";
type ComponentProps = type AProps = Parameters<typeof Button>[0]; // Button은 mui의 컴포넌트

rest와 spread 문법을 이용하면 props를 쉽게 전달할 수 있습니다. 아래는 기본