⏱️

Remotion 코드 분석 — Sequence와 타임라인 시간 관리의 구현

SequenceContext로 로컬 타임을 구현하는 내부 메커니즘과 useCurrentFrame()의 동작 원리

GitHub: remotion-dev/remotion/packages/core

Remotion의 가장 영리한 설계 중 하나는 로컬 타임 개념입니다. <Sequence from={60}>안의 컴포넌트는 글로벌 프레임 60을 자신의 프레임 0으로 인식합니다. 이 마법의 구현을 추적합니다.


useCurrentFrame() 구현

// core/src/use-current-frame.ts
export const useCurrentFrame = (): number => {
  const frame = useContext(TimelineContext).frame;  // 글로벌 프레임
  const sequenceContext = useContext(SequenceContext);  // Sequence 오프셋

  if (!sequenceContext) {
    return frame;  // Sequence 바깥이면 글로벌 프레임 그대로
  }

  // 로컬 프레임 = 글로벌 프레임 - Sequence 시작점
  return frame - sequenceContext.from;
};

핵심: SequenceContext.from을 빼는 것만으로 로컬 타임이 완성됩니다.

컴포넌트 구현

// core/src/Sequence.tsx (간략화)
export const Sequence: React.FC<SequenceProps> = ({
  from,
  durationInFrames,
  children,
  name,
  layout = 'absolute-fill',
}) => {
  const parentSequence = useContext(SequenceContext);
  const frame = useContext(TimelineContext).frame;

  // 중첩 Sequence 처리: 부모의 from을 누적
  const absoluteFrom = parentSequence
    ? parentSequence.from + from
    : from;

  // 현재 프레임이 Sequence 범위 밖이면 렌더링하지 않음
  const isInRange = frame >= absoluteFrom &&
    frame < absoluteFrom + durationInFrames;

  if (!isInRange) return null;  // ← 범위 밖 = 안 보임

  return (
    <SequenceContext.Provider value={{ from: absoluteFrom }}>
      <div style={layout === 'absolute-fill' ?
        { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 } :
        undefined
      }>
        {children}
      </div>
    </SequenceContext.Provider>
  );
};

설계 포인트:
1. isInRange 체크 → 범위 밖 프레임에서는 null 반환 (자동 마운트/언마운트)
2. absoluteFrom 계산 → 중첩 Sequence의 from이 누적됨
3. SequenceContext.Provider → 자식들이 로컬 타임을 사용하도록 Context 전파

구현 — Sequence의 편의 래퍼

// core/src/Series.tsx (간략화)
export const Series: React.FC = ({ children }) => {
  let currentOffset = 0;

  return <>
    {React.Children.map(children, (child) => {
      const from = currentOffset;
      currentOffset += child.props.durationInFrames;

      return (
        <Sequence from={from} durationInFrames={child.props.durationInFrames}>
          {child.props.children}
        </Sequence>
      );
    })}
  </>;
};

Series는 Sequence의 from을 자동 계산하는 것에 불과합니다. currentOffset을 누적하여 각 Series.Sequence가 이전 것 바로 뒤에 시작하도록 합니다.

동작 흐름

1

TimelineContext가 글로벌 frame을 제공 (렌더러가 window.remotion_setFrame으로 주입한 값)

2

<Sequence from={60}>이 SequenceContext.Provider로 from=60을 전파

3

useCurrentFrame()이 globalFrame - sequenceContext.from = 로컬 프레임을 반환

4

중첩 Sequence: 부모의 from + 자신의 from = absoluteFrom (누적)

5

isInRange 체크: frame이 범위 밖이면 null 반환 → 자동으로 장면 등장/퇴장

장점

  • React Context만으로 타임라인 구현: 추가 라이브러리 없이 순수 React 패턴
  • isInRange null 패턴: 범위 밖 컴포넌트를 렌더링하지 않아 성능 최적화

단점

  • Context 남용 주의: 중첩이 깊어지면 리렌더링 범위가 넓어질 수 있음

사용 사례

Sequence 기반 커스텀 전환 효과 구현 시 타임 계산 이해 중첩 Sequence에서 프레임 계산이 맞지 않을 때 디버깅