[styled-components] attrs()와 animation 속성

박기영·2023년 1월 14일

styled-components

목록 보기
1/2

attrs()를 활용해 클래스 재생성 관련 최적화를 하던 중,
animation 속성을 처리하는 과정에서 다양한 에러를 마주했다.
어떻게 해결했는지 기록해놓고자 한다.

우선, 정상적으로 가동하던 코드는 아래와 같았다.

const ShowLogo = keyframes`
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
`;

const LogoWrapper = styled.div.attrs((props) => ({
  style: {
    position: "absolute",
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
    width: "100vw",
    height: "100vh",
    top: `${props.scrollForTop}px`,
  },
}))`
  animation: ${ShowLogo} 1000ms ease-in-out;
`;

뭔가 애니메이션 속성만 떡하니 나와있는게 보기가 싫었다.
그래서 이걸 attrs 내부로 옮겨보려고 했다.

실패 사례 1

우선은 attrs() 안으로 무작정 넣어봤다.

const ShowLogo = keyframes`
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
`;

const LogoWrapper = styled.div.attrs((props) => ({
  style: {
    position: "absolute",
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
    width: "100vw",
    height: "100vh",
    top: `${props.scrollForTop}px`,
    animation: `${ShowLogo} 1000ms ease-in-out`,
  },
}))``;

그러자, 아래와 같은 에러가 발생했다.

Uncaught Error: It seems you are interpolating a keyframe declaration (iAjNNh) into an untagged string. This was supported in styled-components v3, but is not longer supported in v4 as keyframes are now injected on-demand. Please wrap your string in the css`` helper which ensures the styles are injected correctly

실패 사례 2

이를 해결하기 위해 코드를 아래와 같이 수정했다.

const ShowLogo = keyframes`
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
`;

const FadeInAnimation = styled.div`
  animation: ${ShowLogo} 1000ms ease-in-out;
`;

const LogoWrapper = styled(FadeInAnimation).attrs((props) => ({
  position: "absolute",
  display: "flex",
  justifyContent: "center",
  alignItems: "center",
  width: "100vw",
  height: "100vh",
  top: `${props.scrollForTop}px`,
}));

이렇게 했더니 해결되는 듯 했으나,
이번에는 아래 에러가 발생했고,

Uncaught Error: Objects are not valid as a React child (found: object with keys {$$typeof, render, attrs, componentStyle, shouldForwardProp, foldedComponentIds, styledComponentId, target, withComponent, warnTooManyClasses, toString}). If you meant to render a collection of children, use an array instead.

const ShowLogo = keyframes`
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
`;

const FadeInAnimation = styled.div`
  animation: ${ShowLogo} 1000ms ease-in-out;
`;

const LogoWrapper = styled(FadeInAnimation).attrs((props) => ({
  position: "absolute",
  display: "flex",
  justifyContent: "center",
  alignItems: "center",
  width: "100vw",
  height: "100vh",
  top: `${props.scrollForTop}px`,
}))``;

이렇게 코드를 수정한 뒤 에러가 해결되었다.
attrs 사용할 때 꼭 맨 뒤에 백틱을 사용해줘야한다. 비어있더라도. 이게 저 에러의 원인이었다.

그러나...top 속성이 먹통이 되어버렸다.

애초에 animation 속성에 대한 해결이 최우선이었기 때문에, top은 개의치않고 싶었다.
어찌저찌 문제가 해결되었다고 생각하고 싶었지만,
그렇다고 다른 속성을 작동 불능으로 만들면서 해결한 것이 완전한 해결이라 할 수 없었다.
결국, 가장 위에 작성해놨던 코드로 돌아가서 사용하는 것으로 결정...

attrs()에서 동적으로 속성 부여하기

이번에는 엘리먼트의 속성에 따라 다른 스타일을 적용할 수 있도록 styled-components를 만들었다.

const ScrollKuro = styled.div.attrs((props) => ({
  style: {
    display: "block",
    position: "absolute",
    top:
      props.location === "rightKuro"
        ? `${580 + props.kuroTop / 2.85}px`
        : `${props.kuroTop + 60 - 75}px`,
    right:
      props.location === "rightKuro" ? `${10 + props.kuroTop / 50}%` : null,
    left:
      props.location === "rightKuro" ? null : `${props.kuroTop / 100 + 18}%`,
  },
}))`
  animation: ${(props) =>
    props.location === "rightKuro"
      ? css`
          ${Rotate} 2000ms cubic-bezier(0.49, 0.76, 0.64, 0.42) infinite
        `
      : css`
          ${Rotate} 4000ms cubic-bezier(0.49, 0.76, 0.64, 0.42) infinite
        `};
`;

여기서도 나는 animation 속성을 attrs()에 넣을 수 없는지에 대해 실험을 해봤다.

동적 할당 실패 사례 1

아까 사용했던 방법을 그대로 적용해봤다.

const ScrollKuro = styled.div.attrs((props) => ({
  style: {
    display: "block",
    position: "absolute",
    top:
      props.location === "rightKuro"
        ? `${580 + props.kuroTop / 2.85}px`
        : `${props.kuroTop + 60 - 75}px`,
    right:
      props.location === "rightKuro" ? `${10 + props.kuroTop / 50}%` : null,
    left:
      props.location === "rightKuro" ? null : `${props.kuroTop / 100 + 18}%`,
    animation: () =>
      props.location === "rightKuro"
        ? css`
            ${Rotate} 2000ms cubic-bezier(0.49, 0.76, 0.64, 0.42) infinite
          `
        : css`
            ${Rotate} 4000ms cubic-bezier(0.49, 0.76, 0.64, 0.42) infinite
          `,
  },
}))``;

동적 할당 실패 사례 2

또한, 아래 방법도 실패했다.

const ScrollKuro = styled.div.attrs((props) => ({
  style: {
    display: "block",
    position: "absolute",
    top:
      props.location === "rightKuro"
        ? `${580 + props.kuroTop / 2.85}px`
        : `${props.kuroTop + 60 - 75}px`,
    right:
      props.location === "rightKuro" ? `${10 + props.kuroTop / 50}%` : null,
    left:
      props.location === "rightKuro" ? null : `${props.kuroTop / 100 + 18}%`,
    animation:
      props.location === "rightKuro"
        ? css`
            ${Rotate} 2000ms cubic-bezier(0.49, 0.76, 0.64, 0.42) infinite
          `
        : css`
            ${Rotate} 4000ms cubic-bezier(0.49, 0.76, 0.64, 0.42) infinite
          `,
  },
}))``;

동적 할당 실패 사례 3

아래 방법도 실패했다.

const ScrollKuro = styled.div.attrs((props) => ({
  style: {
    display: "block",
    position: "absolute",
    top:
      props.location === "rightKuro"
        ? `${580 + props.kuroTop / 2.85}px`
        : `${props.kuroTop + 60 - 75}px`,
    right:
      props.location === "rightKuro" ? `${10 + props.kuroTop / 50}%` : null,
    left:
      props.location === "rightKuro" ? null : `${props.kuroTop / 100 + 18}%`,
    animation: css`
      ${() =>
        props.location === "rightKuro"
          ? `${Rotate} 2000ms cubic-bezier(0.49, 0.76, 0.64, 0.42) infinite`
          : `${Rotate} 4000ms cubic-bezier(0.49, 0.76, 0.64, 0.42) infinite`}
    `,
  },
}))``;

결론

attrs() 내에 모든 속성값을 넣으려고 시도하다보니, 너무나도 중요한 것을 잊고 있었다.
attrs()를 사용하려고 했는지 말이다.
attrs()는 동적으로 변경되는 값에 대한 지속적인 클래스 재생성을 막기 위한 것이었다.
이 말은 즉슨, animation 속성을 무리해서 attrs() 안에 넣을 필요가 없다는 것이다...
동적으로 할당하는 방법에서는 여전히 해답을 찾지 못했지만,
일반적인 상황에서는 어떻게 처리해야하는지 알았으니 다행이다.

참고 자료

Charlie Thomas님 블로그
stackoverflow 질문글
kbm940526님 블로그

profile
나를 믿는 사람들을, 실망시키지 않도록

0개의 댓글