Motion에서 overflow: hidden이 무시되는 이유와 해결 방법

2025. 7. 27. 12:35Frontend

< 기대한 애니메이션 >
테이블 탭에서 편집 여부에 따라 위젯 아이콘이 전환되는 애니메이션
편집 상태가 되면 기존 아이콘이 저장 아이콘으로 자연스럽게 전환

 

문제 상황

아이콘이 전환될 때 아이콘 박스 내부에서만 전환이 일어나야 했어요.
아이콘 박스는 일종의 틀 역할을 해서, 애니메이션도 그 안에 머물러야 해요.

하지만 overflow: hidden을 적용해도 아이콘이 박스 밖으로 튀어나오는 문제가 발생했어요.

 

원인 분석

첫 번째 이유, position 속성값에 따른 애니메이션

위젯 최상위 컴포넌트에서 overflow가 적용되는 걸 발견했어요.
위젯은 absolute로 띄워져 있는 상태였어요.

그래서 imgBox와 textBox 태그에 position 속성값을 적용해 보면서
overflow가 제대로 작동하는 조건을 실험했어요.

위젯 최상위에서 overflow: hidden 적용되는 상황

- 적용한 코드

/* index.module.css */
.iconBox {
  position: relative; /* 자식 요소의 기준점 설정 O */
  overflow: hidden; 
}

.textBox {
  position: static; /* 자식 요소의 기준점 설정 X */
  overflow: hidden;
}

 

두 번째 이유, AnimatePresence 유무에 따른 차이

또한 AnimatePresence 컴포넌트를 사용할 때와 사용하지 않을 때
레이아웃 애니메이션 동작 방식이 달라지는 것도 확인해봤어요.

// switcher.tsx (AnimatePresence 적용)
<AnimatePresence mode='popLayout' initial={false}>
  {!isSaveMode ? (
    <motion.img
      src={initIconSrc}
      alt={alt}
      key={initIconSrc}
      initial={{ x: '-100%' }}
      animate={{ x: '0' }}
      exit={{ x: '-100%' }}
    />
  ) : (
    <motion.img
      src={changedIconSrc}
      alt={alt}
      key={changedIconSrc}
      initial={{ x: '100%' }}
      animate={{ x: '0' }}
      exit={{ x: '-100%' }}
    />
  )}
</AnimatePresence>
// switcher.tsx (AnimatePresence 미적용)
<>
  {!isSaveMode ? (
    <motion.img
      src={initIconSrc}
      alt={alt}
      key={initIconSrc}
      initial={{ x: '-100%' }}
      animate={{ x: '0' }}
      exit={{ x: '-100%' }}
    />
  ) : (
    <motion.img
      src={changedIconSrc}
      alt={alt}
      key={changedIconSrc}
      initial={{ x: '100%' }}
      animate={{ x: '0' }}
      exit={{ x: '-100%' }}
    />
  )}
</>

 

 

분석결과

<AnimatePresence mode="popLayout">는 내부적으로
레이아웃 전환을 위해 position: absolute를 사용하고 있었어요.

이때 부모 position 요소가 기본값(static)이면,
자식의 position 기준이 엉뚱한 곳에 잡혀서 의도치 않은 애니메이션이 되었어요.

“To ensure consistent and expected positioning during a layout animation,
ensure that the animating parent has a position other than static.”
- Motion 공식 문서

또한, 레이아웃 애니메이션은 자식 컴포넌트가 offsetParent의 영향을 받기 때문에,
부모에 transform이 적용되어 있거나 position이 적절하지 않으면
자식 위치 기준이 예기치 않게 변경될 수 있었어요.

따라서 popLayout을 쓸 때는
부모 요소에 position: relative를 명시해야 했어요.

 

해결방법

문제를 해결하기 위해 .iconBox, .textBox에 다음과 같이 position: relative를 명시적으로 추가했어요.

.iconBox, .textBox {
  position: relative; /* 위치 선언 */
  overflow: hidden;
}

이렇게 하면 두 요소가 위치 기준 콘텍스트가 되어서,
내부 absolute 요소들이 올바르게 정렬되었어요.

그 결과, 애니메이션이 iconBox와 textBox의 너비 안에서만 일어나게 됐고,
overflow: hidden이 의도한 대로 잘 작동했어요.

또 다른 방법으로 <AnimatePresence>를 제거하면,
문제를 해결할 수 있었어요.

정상적으로 동작하는 위젯 아이콘 전환

 

결론

<AnimatePresence mode="popLayout">을 사용할 때
자식 요소의 애니메이션이 부모 박스를 벗어나지 않도록 하려면,
overflow: hidden과 함께 position: relative를 반드시 설정해야 해요.

 

참고 자료
1. AnimatePresence - Motion 공식 문서
2. position - MDN 공식 문서
3. offsetParent - MDN 공식 문서