Styled Component Syntax 이해하기

SSG.COM 모바일 UI 컴포넌트를 emotion.js 라이브러리를 사용하여 스타일링하고 있습니다. 스타일드 컴포넌트를 작성하는 몇 개월동안, 저는 Tagged Template Literals syntax에 대한 이해가 없었습니다. 이 포스팅은 저처럼 “이게 되는건 알겠는데, 어떻게 되는거지?” 궁금하신 분들을 위한 글입니다. 스타일을 작성할 때 낯선 문법에서 오는 불안감이 어느 정도 해소되기를 바랍니다.

ES6에 완전히 익숙하지 않다면, 아래 코드가 낯설게 느껴지실 겁니다.

const Title = styled.h1`
  font-size: 1.5em;
  color: ${theme.primary};
  text-align: center;
  {(props) => props.border && 'border-top: 1px solid #000;' }
`;

위 코드를 이해하려면 ES6의 Tagged Template Literals를 살펴봐야 합니다.

Tagged Template Literals

Tagged Template은 백틱으로 작성한 템플릿 리터럴을 파싱한 뒤 실행되는 “함수”입니다.

아래는 이해에 도움이 될만한 간단한 샘플 코드입니다.

/**
 * Tagged Template 샘플
 *
 * @stringArray: ${표현식} 기준으로 split된 string 배열
 * @variable: 첫번째 ${표현식}
 */
function taggedTemplate(stringArray, variable, variable2) {
  console.log(stringArray);
  // [ '첫 번째 string', '두 번째 string' ]

  console.log(variable);
  //  - 표현식1 -

  return `${stringArray[0]}${variable}${stringArray[1]}`;
}

const stringVar = " - 표현식1 - ";

const customSentence = taggedTemplate`
  첫 번째 string
  ${stringVar} 
  두 번째 string
`;

console.log(customSentence);
// 첫 번째 string - 표현식1 - 두 번째 string

taggedTemplate 이름을 가진 함수는 첫 번째 파라미터로 string array를 두번째 파라미터로 변수 하나를 받는 일반적인 함수입니다. 함수를 정의할 때는 특별할 것이 없습니다.

중요한 차이는 호출할 때 일어납니다. Tagged Template 은 아래의 전통적인 방법 대신,

taggedTemplate();

아래와 같은 포맷으로 호출합니다.

taggedTemplate``;

이렇게 호출하면 백틱 안쪽에 작성한 템플릿 리터럴이 파싱되는데, 내부의 각 ${표현식}을 구분자로 하여 string이 split됩니다.

taggedTemplate 함수의 첫 번째 파라미터로 파싱된 string array가 전달되고, 두 번째 파라미터부터 각 ${표현식}이 순서대로 전달됩니다.

전달받은 파라미터를 활용하여 원하는 어떤 방식으로든 가공하여 결과값을 리턴하면 됩니다.

Styled Components 기본 작동 방식 흉내내기

자, 이제 Tagged Template의 기본을 이해했습니다.

조금 더 복잡한 예시를 살펴보겠습니다.

const theme = {
  spacing: {
    min: "2px",
    max: "24px",
  },
  colors: {
    primary: "coral",
    secondary: "peachpuff",
  },
};

function styled(css, ...variables) {
  const computedCss = css.map((chunk, index) => `${chunk}${variables[index] || ""}`).join("");
  return computedCss;
}

const Button = styled`
  background: ${theme.colors.secondary};
  margin-bottom: ${theme.spacing.min};
  span {
    padding: 0.25em 1em;
    color: ${theme.colors.primary};
  }
`;
/* Output:
  background: peachpuff; 
  margin-bottom: 2px; 
   
  span { 
    padding: 0.25em 1em; 
    color: coral; 
  } 
*/

Tagged Templatestyled와 이전 샘플 코드와 차이는 ...variables 부분입니다.

이전 샘플 코드에서는 넘어올 ${표현식}을 각각 받았었습니다.

반면 이번 코드는 전달된 모든 ${표현식}을 스프레드 연산자를 통해 variables이라는 하나의 변수로 받고 있습니다.

이 방식은 매우 유용한데, 단순히 아래와 같이 처리가 가능하기 때문입니다.

function styled(css, ...variables) {
  const computedCss = css.map((chunk, index) => `${chunk}${variables[index] || ""}`).join("");
  return computedCss;
}

props에 접근가능한 콜백 추가하기

실제 스타일드 컴포넌트를 사용할 때는 props를 받아서 Tagged Template에 콜백을 전달할 일이 많습니다.

Tagged Template에서 어떻게 props에 접근 가능할까요?

아래는 실제 이모션이나 스타일드 컴포넌트 작동 방식과는 다르지만, 어떤식으로 props 접근하는지 이해하는데 도움이 됩니다.

function styled(css, ...variables) {
  const theme = {
    spacing: {
      min: "2px",
      max: "24px",
    },
    colors: {
      primary: "coral",
      secondary: "peachpuff",
    },
  };

  const props = {
    theme,
    primary: true,
    bigSpacing: true,
  };

  const computedCss = css.map((chunk, index) => `${chunk}${variables[index] ? variables[index](props) : ""}`).join("");
  return computedCss;
}

const Button = styled`
  background: ${({ primary, theme }) => (primary ? theme.colors.primary : theme.colors.secondary)};
  margin-bottom: ${({ bigSpacing, theme }) => (bigSpacing ? theme.spacing.max : theme.spacing.min)};
  span {
    padding: 0.25em 1em;
    color: ${({ primary, theme }) => (primary ? theme.colors.secondary : "#fff")};
  }
`;
/* Output:
  background: coral; 
  margin-bottom: 24px; 
 
  span { 
    padding: 0.25em 1em; 
    color: peachpuff; 
  } 
*/

이상 Tagged Template을 살펴봤습니다. 스타일드 컴포넌트 뿐만 아니라 어떤 템플릿이든지 가공이 필요할 때, Tagged Template이 강력한 기능을 제공할 수 있다는 것을 기억해두시면 좋을 것 같습니다.

출처