material-ui-logo.svg

bitbucket 환경설정을 완료하고, 프런트 쪽 코드를 이해하기 위해서 제가 처음으로 시작한 일은 디자인 기획안이 온대로 화면의 UI를 변경하는 일이었습니다. 작업하면 바로 눈에 보이는 결과물이 있어서 무척 재미있었던 것으로 기억합니다.

Global Theme?

저희 회사는 신속하고 쉬운 웹 개발을 위한 리액트 컴포넌트인 material-UI를 사용하고 있었습니다.(https://material-ui.com) 오늘은 material-UI의 많은 기능 중 하나인 theme에 대해서 말씀드리려고 합니다. 저희 코드에서는 palette와 typography 기능을 사용하고 있었는데, 이 기능들이 여러 가지 말하기 복잡한 이유로 잘 사용되지 않고 잔여물처럼 남아있었습니다. 저는 CSS 작업을 하다가 이 Theme를 부활시켜 문서화시킨 후 작업하면 훨씬 코드가 아름답고 일관성 있어질 것이라는 생각에 같이 일하는 분께 양해를 구하고 시간을 얻어 리팩토링을 시작했습니다.

Theme

우선 Theme가 어떻게 사용되는지부터 제대로 파악하고 수정할 필요가 있었습니다. 저희 코드에서 Theme는 GlobalState처럼 사용되고 있었습니다. Provider로 내부의 값을 제공해주고, useTheme를 통해서 값을 가져와 사용하는 방식이었습니다. 이렇게 말하면 '도대체 무슨소리야?' 라고 느낄 것 같아서(제 느낌또한 그렇습니다.), 직접 예시로 보여 드리려고 합니다.

import React from "react";
import { createMuiTheme } from "@material-ui/core/styles";
import { ThemeProvider } from "@material-ui/styles";

const GlobalTheme = createMuiTheme({
  palette: {
    primary: {
      light: "#444",
      main: "#333",
      dark: "#222",
      text: "#111",
    },
});

const GlobalThemeProvider = ({ children }) => (
  <ThemeProvider theme={GlobalTheme}>{children}</ThemeProvider>
);

export default GlobalThemeProvider;

우선, 이렇게 모든 페이지에서 사용할 수 있는 globalThemeProvider를 만들었습니다. (색상은 필요한 색상을 넣어서 사용해줍니다.) 이후, App.js나 index.js에 <ThemeProvider>{something}<ThemeProvider> 코드를 추가해서 원하는 색상을 모든 곳에서 골라 사용할 수 있습니다. 컴포넌트에서 사용하는 방법은 아래와 같습니다.

import React from "react";
// css-in-js
import { useTheme, makeStyles } from "@material-ui/styles";

const useStyles = makeStyles({
  root: ({ primary }) => ({
    backgroundColor: primary.light,
    widht: "50px",
    height: "50px",
  }),
});

function PrintHi() {
  const { palette } = useTheme();// theme가져오기
  const classes = useStyles({
    primary: palette.primary
  });

  return (
    <div className={classes.root}>
      {"hi"}
    </div>
  );
}

export default printHi;

makeStyles은 React의 Hooks처럼 classes객체를 만들어 사용할 수 있게 해주었습니다.
자세한 내부 코드는 파악하려 하지 않았고 '이렇게 사용하는 거구나!' 하고 이해한 뒤, 사용하는 데 있어 더 편하게 만들겠다는 계획을 세우고 리팩토링을 시작했습니다.

palette

우선 페이지에 사용하게 될 모든 색상을 정리부터 하였습니다. 총 사용하는 색상은 12가지였는데, 사용하는 용도에 맞추어 맨 처음에 저는 그냥 palette의 primary, secondary, tertiary로 아래와 같은 방식으로 정리하였습니다.

import React from "react";
import { createMuiTheme } from "@material-ui/core/styles";
import { ThemeProvider } from "@material-ui/styles";

const GlobalTheme = createMuiTheme({
  palette: {
    primary: {
      light: "#444",
      main: "#333",
      dark: "#222",
      text: "#111",
    },
    secondary: {
      light:"#555",
      main:"#666",
      dark:"#777",
      text:"#888",
    },
    tertiary: {
      light:"#999",
      main:"#101",
      dark:"#112",
      text:"#121",
    },
});

const GlobalThemeProvider = ({ children }) => (
  <ThemeProvider theme={GlobalTheme}>{children}</ThemeProvider>
);

하지만 같이 일하는 분과 의견을 나눈 결과, 저렇게 theme를 제작하면, primary, secondary, tertiary의 명확한 의미가 사라지고, 그 내부의 light, main, text 또한 걸맞게 사용할 수 없었습니다. light로 사용되었던 색상이 어떤 곳에선 text로 사용되는 경우가 빈번했기 때문이죠. 그리하여 수정된 내용은 아래와 같습니다.

import React from "react";
import { createMuiTheme } from "@material-ui/core/styles";
import { ThemeProvider } from "@material-ui/styles";

const GlobalTheme = createMuiTheme({
  palette: {
    primary: {
      light: "#444",
      main: "#333",
      dark: "#222",
      text: "#111",
    },
    grayScale: {
      100: "#1",
      200: "#2",
      300: "#3",
      400: "#4",
      500: "#5",
      600: "#6",
      700: "#7",
      800: "#8",
    },
});

const GlobalThemeProvider = ({ children }) => (
  <ThemeProvider theme={GlobalTheme}>{children}</ThemeProvider>
);

페이지의 메인이 되는 색상을 모두 primary에 넣고, 이곳저곳 자잘하게 사용되는 비슷한 부류의 색상은 모두 위처럼 변경하였습니다. 사용할 땐 grayScale[100] 같은 방식으로 사용할 수 있었습니다. 다음은 typography에 대해서 알아보겠습니다.

Typography

제가 회사에 적용한 typography 기능은 글자에 적용되는 스타일을 미리 지정하여 나중에 필요할 때 가져가서 사용할 수 있게 해주는 컴포넌트입니다. 자주 사용되거나, 특정한 의미가 있는 글자들의 스타일을 입혀서 언제든지 사용할 수 있게 하였습니다. 그 스타일에 사용되는 변수를 통해서 이 문구가 어떤 의미가 지니는 것인지도 파악되도록 제작하였습니다.

import { Typography } from "@material-ui/core";

const BaseTypography = ({ component, className, children }) => {
  return <Typography {...{ component, className }}>{children}</Typography>;
};

우선 materialUI에서 Typography를 가져와 기본적인 설정을 정했습니다. className, component를 인자로 받아 제가 만드려는 컴포넌트에서 이 기본이 되는 컴포넌트를 통하여 typography의 component(h2, h1과 같은), 추가로 지정된 className을 설정해 줄 수 있게 하였습니다. 그 이후, 위에서 했던 것처럼, makeStyles를 통해서 주고 싶은 CSS 속성을 준 후, 제가 만들고 싶은 어떤 특정한 의미가 있는 글자 컴포넌트의 className에 추가해준 후 export해서 사용할 수 있게 제작하였습니다.

import { Typography } from "@material-ui/core";
import { makeStyles } from "@material-ui/styles";

const BaseTypography = ({ component, className, children }) => {
  return <Typography {...{ component, className }}>{children}</Typography>;
};

const useStyles = makeStyles({
  Tag1: props => ({
      fontSize: "1rem",
    lineHeight: "2rem",
    color: "#999",
  })
});

export const Tag1 = props => {
  const { component = "span", className, ...others } = props;
  return (
    <BaseTypography className={`${classes.Tag1} ${className}`} {...{ component, ...others }} />
  );
};

사용할 땐 아래와 같이 사용합니다.

import React from "react";
// css-in-js
import { Tag1 } from "scripts/Components/Typography";

function PrintTag(tagText) {
  return (
    <Tag1>{tagText}</Tag1>
  );
}

export default PrintTag;

마무리

어떻게 만드는지 정말 많이 고민하고 제작하였는데, 막상 만들고 문서화 하고 나니 티가 전혀 안 나는 것 같아 많이 슬펐었습니다. 하지만 지금도 이 기능들은 사용되고 있습니다.(제가 만든 게 아까워 사용하고 있습니다.) 나중을 위해서 어떤 기능이 어떻게 사용되는 지도 WIKI에 정리하여 컨벤션처럼 '이렇게 사용하라!' 라고 작성도 하였습니다. 결국, 이 기능들을 사용해서, 불필요한 코드들을 줄일 수가 있었고, 보기에도 더 깔끔해지는 효과를 가져다주었습니다.

혹시나 왜 이렇게 한 지에 대한 의문이나, 질문이 있으시다면 댓글을 달아주시기 바랍니다. 엉성한 글 끝까지 읽어주셔서 감사합니다. 😃