[번역] 5가지 리액트 애니메이션 사용방법 비교 (React Animations in Depth)

DylanJu
13 min readOct 28, 2017

--

아래 내용은 https://medium.com/react-native-training/react-animations-in-depth-433e2b3f0e8e를 해석한 내용입니다. 의역과 오역이 난무하니 자세한 내용은 해당 블로그를 참조하시면 이해가 더 쉬울 것 같습니다.
개취: 억지로 번역하면 뜻이 더 모호해지는 단어들은 그 자체로 두었습니다. -:)

이 포스트에서 우리는 크로스 플랫폼을 고려한, 리액트 애니메이션을 만드는 5가지 방법을 살펴볼 것입니다.
1. React Component State에 기초한 CSS Animation
2. React Component State에 기초한 자바스크립트 스타일 애니메이션
3. React-Mition 라이브러리 by Cheng Lou (리액트에서 공식적으로 애니메이션을 담당하는 분이기도 합니다 — 역자 주)
4. Animated 라이브러리
5. Velocity-React 라이브러리

5가지 방법에 대한 repo는 여기를 클릭해주세요.

1. React Component State에 기초한 CSS Animation

애니메이션을 조정하는 가장 기본적인 방법은 CSS 클래스 프로버티 더하거나 지우는 방식입니다. 이미 CSS를 사용하고 있다면 가장 손쉬운 접근 방법입니다.

CSS를 직접 조작하는 방법의 가장 큰 장점은 퍼포먼스입니다.

단점: 이 접근 방식의 단점은 크로스 플랫폼이 아니라는 것입니다(리액트 네이티브를 포함하여). 이 것은 CSS와 DOM에 의지하기 때문에 이 방식으로는 복잡한 것을 다루기 어렵습니다.

장점: CSS 애니메이션을 고려하면, 이미 여러분이 알고 있는 문법을 다루는 것이며 기존 코드에서 opacity나 transform 등의 property 들을 변경할 필요가 없습니다. 그리고 대부분의 경우에 퍼포먼스 측면에서도 뛰어납니다. Value 를 업데이트하기 매우 간단하고 이 점은 우리에게 부드러운 transition을 가능하게 하고 우리의 컴포넌트에서 단 한번의 렌더링만 하게 합니다.

아래 예시를 보세요. 리액트 컴포넌트와 CSS 애니메이션 만으로 input 클래스를 조작할 것입니다.

첫째로, input 클래스에2개의 클래스를 만들어 붙일 것입니다.

기본적인 .input과 styling이 있습니다. 거기에 transition: width .35s linear 를 더해 약간의 애니메이션을 만들었습니다.

우리는 또한 input-focused를 만들어 width 를 150에서 240px로 늘렸습니다.

이제 리액트와 연결해보겠습니다.

  1. focused 라는 initial state로 false를 세팅했습니다. 이 것을 toggle하여 컴포넌트에 애니메이션을 실행할 것입니다.
  2. componentDidMount에 두 개의 listener를 더할 것인데, 하나는 blur 다른 하나는 focus를 위한 것입니다. 두 개 모두 다음에 강조할 focus method로 call됩니다. 명심할 점은, this.input을 참조하고 있는데 ref method를 이용해 reference를 만들고 class property로서 할당하기 위해서 입니다. 이런 동작들을 componentDidMount에서 하는데 componentWillMount에서는 돔에 접근할 수 없기 때문입니다.
  3. focus method는 state의 focused에 기반해 toggle을 실행할 것입니다.
  4. render에서 가장 중요한 것은 어떻게classNames를 할당하는지 입니다. this.state.focused가 true일때 input-focused 클래스를 더합니다. class의 array를 만들고 .join(‘ ‘)을 call해서 사용할 className을 만듭니다.

2. React Component State에 기초한 자바스크립트 스타일 애니메이션

자바스크립트를 이용한 방식은 CSS 클래스를 사용하는 방식과 매우 비슷합니다. 이 방식이 퍼포먼스에 뛰어나다는 것도 역시 비슷하지만 CSS를 통해 조작할 필요 없이 자바스크립트에서 모든 로직을 관리하는 점이 다릅니다.

장점: CSS처럼 분명한 퍼포먼스 이득이 있습니다. 또한 CSS에 의존하지 않는 좋은 접근 방법입니다.

단점: CSS 애니메이션 처럼 이 접근 방식의 단점도 크로스 플랫폼을 지원하지 않는 점입니다(리액트 네이티브 포함). 역시나 CSS와 DOM에 의존하기 때문에 복잡한 효과는 컨트롤하기 어렵습니다.

다음 예시에서 무언가를 타이핑하면 버튼이 비활성 상태에서 활성 상태로 변경될 것입니다. 그리고 버튼이 활성 상태일 때 유저 피드백을 보낼 것입니다.

  1. initial state의 disabled를 true로 세팅했습니다.
  2. onChange method는 input에 붙여질 예정인데 이것은 타이핑된 글자수를 체크하는 기능입니다. 4글자가 넘으면 활성 상태가 되고, 미만일 경우에는 비활성 상태가 됩니다.
  3. button element의 style property는 애니메이션 클래스 buttonEnabled를 더할지 말지를 this.state.disabled의 value에 따라 결정합니다.
  4. 버튼 스타일은 transition: .25s all을 갖고 있는데, backgroundColorwidth property를 모두 다루고 싶기 때문입니다.

3. React-Mition 라이브러리

React MotionCheng Lou가 만든 훌륭한 라이브러리 입니다. 그는 리액트 웹과 리액트 네이티브의 애니메이션을 2년간 담당한 분입니다. 여기를 누르시면 React Europe in 2015에서 애니메이션에 대해 그가 토론한 이야기를 볼 수 있습니다.

React Motion의 기본적인 아이디어는 “Spiring”이라는 API로 참조되는 것을 사용하는 것입니다. 그것은 견고한 애니메이션 기반의 설정인데 대부분의 경우에 잘 작동할뿐만 아니라 커스터마이징이 가능합니다. 이것은 타이밍에 의존하지 않습니다. 따라서 애니메이션을 취소하거나, 정지하거나, 되돌리거나, variable dimension으로 작동할 때 좋습니다.

React Motion의 기본적인 아이디어는 React Motion에서 제공하는 리액트 컴포넌트에 style을 설정하는 것입니다. Callback function으로 style value를 받을 수 있는데 아래 예제를 참고하세요.

<Motion style={{ x: spring(this.state.x) }}>
{
({ x }) =>
<div style={{ transform: `translateX(${x}px)` }} />
}
</Motion>

장점: React Motion 은 크로스 플랫폼을 지원하기 때문에, 리액트 웹에서 처럼 리액트 네이티브에서도 사용 가능합니다. Spring 컨셉은 저에게 처음에는 낯설었지만 나중에는 정말 천재적이라 생각했고 모든 것들을 잘 다룰 수 있었습니다. 진짜 잘 만든 API입니다.

단점: 순수 CSS와 자바스크립트에서 언급했듯이 퍼포먼스가 조금 떨어집니다. 그리고 API가 다소 새롭기 때문에 새로 배워야 하지만 익숙해지면 매우 쉽습니다.

  1. react-motion에서 Motionspring을 import
  2. initial state에서 height를 38로 세팅하고, styles의 menu height를 바꾸는 데 쓸 것입니다.
  3. animate method는 현재 height 값으로 state의 height 값을 세팅하는데 사용되는데, height가 38이면 250이 되고, 그렇지 않으면 38이 될 것입니다.
  4. Render에서 Motion component는 p 태그의 리스트를 감싸는데 씁니다. Motion의 style property를 세팅하면 this.state.height의 값이 height 값으로 할당됩니다. 이제 height는 Motion component에서 callback으로 사용할 수 있습니다. p 태그 리스트를 감싸고 있는 div의 height값으로 선언하여 callback을 쓸 수 있습니다.
  5. 버튼을 클릭하면 this.animate를 호출함으로써 height 애니메이션을 토글할 수 있습니다.

4. Animated 라이브러리

Animated 라이브러리는 리액트 네이티브에서 쓰는 같은 Animated 라이브러리를 기반으로 합니다.

Animated의 기본적인 아이디어는 개발자가 선언적으로 애니메이션을 만들고 거기서 일어나는 효과들을 객체로 컨트롤할 수 있다는 발상입니다.

장점: 크로스 플랫폼 지원. 이 라이브러리는 이미 리액트 네이티브에서 굉장히 안정적으로 사용됩니다. 한번 배워두면 웹에서도, 앱에서도 동일하게 사용할 수 있습니다. interpolate method를 통해 하나의 값을 여러개의 스타일에 interpolate할 수 있습니다(아래 예제 참고). 또한 Easing 프로퍼티를 여러 군데 쉽게 삽입하여 사용할 수 있습니다.

API를 재밌게 배울 수 있고 여러 개의 애니메이션을 유연하게 다룰 수 있게 해줍니다. 배우는 것을 도와줄 수 있는 도큐먼트가 많은데 Jason Brown이 만든 블로그비디오가 있습니다. 만약 리액트 네이티브를 위한 참고자료가 필요하면 여기 블로그를 참고하세요.

단점: 제가 트위터에서 말했듯이 리액트 웹 버전에서는 100% 안정적이지는 않습니다. 옛날 브라우저에서 auto-prefixing와 약간의 퍼포먼스 이슈가 있습니다. 리액트 네이티브에서 사용해보지 않았다면 API를 새로 배워야합니다.

  1. Animated와 Easing을 import합니다. 전체 라이브러리를 땡겨올 필요는 없고 react-domEasig API들을 직접 가져오면 됩니다.
  2. 새로운 클래스 animatedValue를 만들고 기본값을 0으로 세팅합니다. 사용할 때는 new Animated.Value(0)으로 씁니다.
  3. animate method를 만듭니다. 이 method는 모든 애니메션을 다룹다. 심지어 이후에 적용할 애니메이션 값과 interpolate method를 이용해 다른 애니메이션 값까지 사용할 수 있습니다(his method will handle all of the animation that will be taking place, though we will use this animated value later and create other animated values off of it using the interpolate method — 저도 예제만 보고 번역해서 매끄럽지 않네요. 원문을 참조해 읽어주세요. 역자 주).
    이 method 에서 this.animatedValue.setValue(0)을 호출해 animated 값을 0으로 세팅합니다. 그리고 Animated.timing을 호출해 this.animateValue의 첫번째 argument를, configuration object를 두번째 argument로 넘겨줍니다. Configuration object 에는 애니메이션의 마지막 value인 toValue, 애니메이션 재생시간인 duration, 적용하고 싶은 easing property(여기서는 Elastic을 사용)를 갖고 있습니다.
  4. render method에서 interpolate를 사용해 marginLeft라는 새로운 값을 만들었습니다. 어떤 애니메이션에서나 가져다 쓸 수 있는 값입니다. 여기서 interpolateinputRange array 와 outputRange array를 갖고 있는 configuration object를 취합니다. input과 output을 기초로 새로운 값을 만들 수 있습니다. 예제에서는 이 값들을 marginLeft에 넣어 메시지 UI의 애니메이션 값을 쓸 것입니다.
  5. 일반적인 div 대신에 Animated.div를 사용했습니다.
  6. marginLeftanimatedValue를 사용해 Animated.div를 스타일링 했습니다. marginLeftmarginLeft로 할당되고, animatedValueopacity에 직접 세팅됩니다.

5. Velocity React 라이브러리

Velocity React는 현재 존재하는 Velocity DOM 라이브러를 기반으로 합니다.

이걸 사용하고 난 뒤에, Velocity React는 Animated과 React Motion의 API를 조합해 사용하는 것 같았습니다. 전반적으로 웹에서 사용하기 좋다고 생각했고 괜찮은 라이브러리라고 생각했지만, 개인적으로 React Motion과 Animated를 더 좋아하게 될 것 같습니다.

장점: 사용하기 매우 쉽습니다. API는 매우 심플하고 직관적이며 React Motion에 비해 적용하기 쉽습니다.

단점: 배울때 약간 이해가 안되는 부분이 있는데, componentDidMount에서 애니메이션이 돌아가지 않는 부분이나 애니메이션에 runOnMount를 선언해줘야 하는 일 등이 그랬습니다. 또한 크로스 플랫폼을 지원하지 않습니다.

기본적인 API 사용방법은 다음과 같습니다.

<VelocityComponent
animation={{ opacity: this.state.showSubComponent ? 1 : 0 }}
duration={500}
>
<MySubComponent/>
</VelocityComponent>

이번 예제에서는 타이핑 할때마다 글자가 날아오는 효과를 만들어 보겠습니다.

  1. velocity-react에서 VelocityComponent를 import
  2. 우리가 애니메이션에 사용할 각각의 글자를 표현할 재사용가능한 컴포넌트를 만듭니다. 한 동작에 하나의 애니메이션을 쓰기 때문입니다.
  3. 이 컴포넌트에서 opacity는 1, marginTop은 0으로 세팅합니다. 이 값들은 child 컴포넌트의 값에 덮어씌워지는데, 이 경우에 <p>opacity: 0, marginTop: 100으로 시작합니다. 컴포넌트가 만들어질 때 opacity는 0에서 1로, marginTop은 100에서 0으로 됩니다. 또한 500ms의 지속시간을 세팅하고 runOnMount를 호출합니다. runOnMount는 컴포넌트가 마운트되거나 만들어질 때 애니메이션을 호출한다는 뜻입니다.
  4. onChange method는 render method에서 input element에 할당됩니다. input의 개별 글자들을 받아 맨 위에 선언한 VelocityLetter로 새로운 array를 만듭니다.
  5. render에서만들어진 array로 애니메이션 글자를 표현합니다.

결론

전반적으로 저는 자바스크립트 스타일의 기본 애니메이션(2번)을 쓸 것 같습니다. 화려한 애니메이션이 필요한 웹은 React Motion을 쓸 것 같구요. 또한 리액트 네이티브에서는 계속 Animated를 쓸 것 같습니다. 아마도 Animated가 웹에서도 좀 더 성숙한다면 Animated를 쓰겠죠. React Motion도 재밌긴 하지만요.

저도 1번 2번만 쓰는 편이라 번역이 쉽지는 않았습니다. 그래도 원저작자 Nader Dabit이 쉬운 영어를 써줘서 다행입니다. 아무래도 발번역이다보니 자세한 내용은 원문을 직접 보시길 추천드립니다. 더 좋은 번역이 있다면 댓글 달아주세요 :-)

--

--

DylanJu
DylanJu

Written by DylanJu

당근페이 Frontend-developer

Responses (8)