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가 이전 것 바로 뒤에 시작하도록 합니다.
동작 흐름
TimelineContext가 글로벌 frame을 제공 (렌더러가 window.remotion_setFrame으로 주입한 값)
<Sequence from={60}>이 SequenceContext.Provider로 from=60을 전파
useCurrentFrame()이 globalFrame - sequenceContext.from = 로컬 프레임을 반환
중첩 Sequence: 부모의 from + 자신의 from = absoluteFrom (누적)
isInRange 체크: frame이 범위 밖이면 null 반환 → 자동으로 장면 등장/퇴장
장점
- ✓ React Context만으로 타임라인 구현: 추가 라이브러리 없이 순수 React 패턴
- ✓ isInRange null 패턴: 범위 밖 컴포넌트를 렌더링하지 않아 성능 최적화
단점
- ✗ Context 남용 주의: 중첩이 깊어지면 리렌더링 범위가 넓어질 수 있음