[ISSUE] CSS Keyframe not working

young_pallete·2022년 11월 24일
0

Issues

목록 보기
6/6
post-thumbnail

🔥 배경

기존의 포트폴리오 앱을 개발 후 배포를 했는데, 알고 보니 애니메이션이 제대로 적용되지 않는 현상을 발견했어요.

왜 돌아가지... 팔짝 뛰어야 하는데... 😭
따라서 이에 대한 원인을 분석하고 해결하고자 했어요.

🚦 본론

코드 분석

가장 원인이 된 코드는 다음 두 모듈에서의 문제였어요.

코드1. pages/experiences-and-projects/index.tsx

// ... in compounded style object
  ReverseText: styled.strong<{ reversed: boolean }>`
    display: inline-block;
    font-size: inherit;
    font-weight: inherit;
    transition: all 0.5s;
    ${({ reversed }) =>
      reversed &&
      css`
        animation: element-jump 0.3s forwards;
        animation-delay: 0.25s;
        @keyframes element-jump {
          to {
            transform: rotate(180deg) scale(1.1);
            transform-origin: 50% 60%;
          }
        }
      `}
  `,

코드2. pages/about/index.tsx

  ImageContainer: styled.div`
    display: flex;
    /* flex-shrink: 0; */
    align-items: center;
    justify-content: center;
    width: 3.5rem;
    height: 3.5rem;
    padding: 0.5rem;
    overflow: hidden;
    cursor: pointer;
    background-color: rgba(256, 256, 256, 0.8);
    border-radius: 20px;

    ${({ theme }) => css`
      @media screen and (max-width: ${theme.viewPort.tabletMax}) {
        border-radius: 12px;
      }
    `}

    &:hover {
      animation: element-jump 1s infinite;
      @keyframes element-jump {
        0% {
          transform: scaleX(1) scaleY(1);
        }
        20% {
          transform: scaleX(1.2) scaleY(0.8);
        }
        40% {
          transform: scaleX(0.9) scaleY(1.1) translateY(-0.5rem);
        }
        60% {
          transform: scaleX(1.05) scaleY(0.95) translateY(0);
        }
        80% {
          transform: scaleX(0.97) scaleY(1.03);
        }
        100% {
          transform: scaleX(1) scaleY(1);
        }
      }
    }
  `,

원인 분석

해결 성공: keyframe selector name을 변경한다.

다행히도 한 번에 오류를 해결했어요.
아무래도 마음이 걸렸던 게, selector name이 같은 점이 문제이지 않을까 싶었어요.
따라서 keyframe의 id값을 서로 다르게 조정했습니다.

// pages/experiences-and-projects/index.tsx에서 keyframe id를 reverse-text로 조정
animation: reverse-text 0.3s forwards;

잘 반영이 되었음을 확인했습니다 🥰

진짜 문제: 왜 두 키프레임이 공유되었는가?

공유될 수 있는 지점 존재

일단 두 페이지의 CSS가 공유될 수 있는 지점은 제 로직을 보면 존재합니다.
저는 이전 페이지를 clone하여 페이지 전환 효과를 만들었는데요.
이때, css 모듈은 클론했던 페이지가 언마운트 되기 전까지 제거되지 않습니다.

하지만... nested 문법을 사용했지 않는가?

아이디가 설령 같더라도, 저는 scssnesting 구문을 사용했습니다.
즉, 아무리 아이디가 같더라도 이는 각자의 클래스 안에서 정의되었으므로 반영될 일이 없어야 합니다.

하지만 결국 마치 전역에 호이스팅이 된 것처럼 서로 공유되고 있다는 것이 진짜 문제였습니다.

가설 설정

따라서 일단 핵심 원인인 2가지를 '해결을 위한 진짜 문제'라고 인식했습니다.

  1. keyframe은 scss의 nesting 구문을 지원하지 않는가?
  2. keyframe의 selector는 고유해야 하는가?

따라서 이를 탐구해본 결과, 진짜 원인을 자신 있게 규명할 수 있었습니다.

탐구

1. keyframe은 nesting되지 않습니다!

MDN의 Keyframe의 진짜 정체를 알게 됐습니다.

@keyframes rules don't cascade, so animations never derive keyframes from more than one rule set.

설명에서는 키프레임은 캐스케이딩을 지원하지 않는다라는 내용이 나왔어요.
CSS는 기본적으로 캐스케이딩을 통해 스타일을 상속하게 되는데, 이 말이 의미하는 것은 결국 keyframe을 셀렉터를 기반으로 조건부로 만들지 말라는 말과 같습니다.

그렇기 때문일까요? 실제로, @emotion에서는 별도로 키프레임을 지원합니다.
아무래도 이러한 이유 때문이 아닐까 싶어서, 결국 모든 구현에는 배경이 있다는 것을 깨달았습니다. 참고

2. keyframe은 고유하지 않아도 됩니다. 그러나 그러면 마지막에 일치한 것만 반영이 됩니다!

신기하게도, 이 설명의 바로 위쪽에서 바로 2번째의 이유를 찾을 수 있었습니다.
(실제로 찾자마자 저도 놀랐습니다. 원인의 2개가 한 번에 제시된 경우는 처음이네요. 🥰)

If multiple keyframe sets exist for a given name, the last one encountered by the parser is used.

이 말은 즉, 중복적인 키프레임 셀렉터 설정은 에러를 내뱉지 않는다는 것을 내포합니다.
하지만 "그게 너의 의도가 맞을까?"라는 주의를 줘요.
왜냐하면 마지막에 파서에 들어간 것이 반영되기 때문입니다.

이러한 컴파일 과정은 결과적으로 유지보수의 추적을 어렵게 하기 때문에, 사실상 키프레임의 셀렉터 값은 고유값이라는 것이 불문율이겠네요!

정리

즉, 이 문제는 제가 CSS의 키프레임 원리를 정확히 몰라서 발생한 문제입니다.

정리해봅시다.

  1. 키프레임은 캐스케이딩을 지원하지 않습니다. 따라서 선언된 순간 호이스팅되는 것처럼 케스케이딩 구문 밖을 벗어나 다같이 공유됩니다.
  2. 키프레임의 아이디는 중복되어서는 안 됩니다. 파서가 마지막에 적용한 것만 반영되기 때문입니다.

물론 저는 단일 page를 구현할 때 이를 잘 지켰어요. 그렇기에 단순한 테스트에서는 문제가 발생하지 않았어요.

그러나, 제가 여러 페이지를 클론한 순간, 문제는 발생했습니다. 왜냐하면

  • 클론된 엘리먼트의 새로운 마운트는 결국mount 시점 이후에 진행되었으며,
  • 결과적으로 css 모듈은 다시 parser을 거쳐 현재 페이지의 키프레임을 오염시켰기 때문이에요.

🚀 마치며

기분이 좋았어요.
역시 원인을 진짜 알아야, 개발에 있어 뭔가 찝찝한 기분이 사라지거든요.

요새 CSS가 정말 어렵다고 느낍니다.
단순히 구현하는 건 어렵지 않은데, 결국 CSS의 숨겨진 문법 철칙을 이해하는 건 꽤나 많은 시간을 요구하기 때문이죠.

뭐든지 표준을 따른다는 건 어려운 것 같아요. 하지만 언젠간, 노력하다 보면 표준에 가까워지겠죠?

이 글이 미래의 저와, 누군가에겐 도움이 됐길 바라요. 이상 🙇🏻‍♂️

profile
People are scared of falling to the bottom but born from there. What they've lost is nth. 😉

0개의 댓글