Communitygithub.com

AlexGit92s/Remotion2.0

Remotion 마크업을 구조화하여 스튜디오에서 상호작용 가능하게 만듭니다.

Remotion2.0란 무엇인가요?

이 스킬은 Remotion 컴포넌트를 작성할 때 특정 마크업 규칙을 적용하여 Remotion Studio가 코드 구조를 인식하고 상호작용(클릭 선택, 드래그 앤 드롭, 크기 조절, 회전, CSS 스타일 편집, 키프레임 및 이징 값 편집)을 가능하게 하는 방법을 설명합니다. `Interactive.withSchema({wrapInSequence: true})`를 사용하여 사용자 정의 씬, 카드, 타이틀 등 인스턴스별로 props를 편집할 수 있는 컴포넌트를 만들고, `InteractivitySchema`를 통해 의미 있는 콘텐츠와 모양 제어를 노출합니다. 각 독립적으로 편집 가능한 항목은 고유한 JSX 노드를 가지며, 모든 CSS 스타일은 인라인으로 유지하고, 애니메이션은 `interpolate()`를 인라인으로 사용하여 작성합니다. 또한 재사용 가능한 컴포넌트를 연결된 컴포지션으로 등록하고, 시퀀스를 통한 타이밍 제어, 그리고 상호작용이 불가능한 복잡한 마크업의 경우 값이 회색으로 표시되는 점을 설명합니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/AlexGit92s/Remotion2.0/tree/HEAD/.claude/skills/remotion-interactivity

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

미리보기

스킬 README에서

Remotion2.0 preview

문서

Remotion 마크업을 특정 방식으로 작성하면 Remotion Studio가 코드 구조를 인식하고 상호작용을 가능하게 합니다:

  • 항목을 클릭하여 선택할 수 있음
  • 드래그 앤 드롭, 크기 조절 및 회전 가능
  • CSS 스타일 편집 가능
  • 키프레임 및 이징 값을 편집 가능하게 함

마크업이 Studio가 상호작용을 처리하기에는 너무 복잡하면 값이 회색으로 표시됩니다.

고유한 타임라인을 가진 상호작용 컴포넌트 선호

사용자 정의 씬, 카드, 제목 및 인스턴스별로 props를 편집할 수 있어야 하는 기타 시각적 컴포넌트에는 Interactive.withSchema({wrapInSequence: true})를 사용하세요. InteractivitySchema에서 의미 있는 콘텐츠와 모양 컨트롤을 노출하세요. 데코레이션 구현 세부 사항은 컴포넌트 내부에 유지하세요.

컴포넌트는 style을 받아 하나의 시각적 루트로 전달해야 합니다. 변형, 자르기 또는 불투명도가 모든 레이어에 영향을 미쳐야 하는 경우 공유 루트를 유지하세요. 스타일을 레이아웃이나 애니메이션 변경 없이 기존 요소로 이동할 수 있는 경우 중복된 내부 래퍼를 평탄화하세요.

import type React from 'react';
import {Interactive, type InteractivitySchema} from 'remotion';

type LowerThirdProps = {
  readonly children: string;
  readonly accentColor: string;
  readonly style?: React.CSSProperties;
};

const LowerThirdInner: React.FC<LowerThirdProps> = ({
  children,
  accentColor,
  style,
}) => {
  return (
    <Interactive.Div
      style={{
        position: 'absolute',
        left: 80,
        bottom: 80,
        display: 'flex',
        alignItems: 'center',
        gap: 20,
        backgroundColor: 'white',
        borderRadius: 16,
        padding: '20px 32px',
        color: 'black',
        fontFamily: 'Helvetica, Arial, sans-serif',
        fontSize: 48,
        fontWeight: 600,
        ...style,
      }}
    >
      <div
        style={{
          width: 8,
          alignSelf: 'stretch',
          borderRadius: 4,
          backgroundColor: accentColor,
        }}
      />
      {children}
    </Interactive.Div>
  );
};

const lowerThirdSchema = {
  children: {type: 'text-content', default: '', description: '텍스트'},
  accentColor: {
    type: 'color',
    default: '#0b84f3',
    description: '강조 색상',
  },
} as const satisfies InteractivitySchema;

export const LowerThird = Interactive.withSchema({
  Component: LowerThirdInner,
  componentName: '<LowerThird>',
  schema: lowerThirdSchema,
  wrapInSequence: true,
});

컴포넌트 내부에 주입된 style을 전달하는 것은 필수입니다. 편집 가능한 스타일은 컴포넌트 호출 지점에서 인라인으로 유지하세요.

스키마 필드 선택

스키마의 각 키는 Studio가 호출 지점에서 읽고 편집할 수 있는 prop입니다. 스키마에 없는 props는 여전히 작동하지만 Studio에서 편집할 수 없습니다. 키는 style.color와 같은 점 표기법을 사용할 수 있습니다.

유형사용 용도키프레임 가능
text-content텍스트 (예: 제목)아니요
number숫자, 선택적 min, max, step, integer예
boolean켜기/끄기 스위치예 (hold)
colorCSS 색상 문자열예
enum각각 고유한 중첩 스키마를 가진 variants 중 선택옵트인 (hold)
array숫자, 색상, 열거형 등의 리스트아니요
asset미디어 소스, 선택적 assetType아니요
font-familyCSS 폰트 패밀리아니요
font-weight폰트 두께예
translate, scale, rotation-css, rotation-degrees, transform-origin변형예
uv-coordinate요소의 정규화된 [x, y] 포인트예
svg-pathSVG 경로 데이터예
remotion-captionsCaption[] 데이터아니요
hidden컨트롤에서 제외되는 값—

모든 필드는 default가 필요하며, Studio가 레이블로 표시하는 description이 있어야 합니다. keyframable: false로 설정하면 정적 컨트롤이 표시됩니다.

콘텐츠 및 인스턴스별 모양(예: 텍스트와 강조 색상)을 노출하세요. style.translate, style.scale, style.rotate, style.transformOrigin 및 style.opacity에 대한 필드를 추가하지 마세요: wrapInSequence: true가 이미 추가합니다.

루트의 CSS 속성을 인스턴스별로 편집 가능하게 만들려면 내장 스키마 프래그먼트를 펼치세요:

const cardSchema = {
  ...Interactive.textSchema, // style.color, style.fontSize, style.fontWeight, ...
  ...Interactive.backgroundSchema, // style.backgroundColor
  ...Interactive.borderRadiusSchema, // style.borderRadius, ...
} as const satisfies InteractivitySchema;

또한 사용 가능: Interactive.borderSchema 및 자막을 허용하는 컴포넌트용 Interactive.captionsSchema. 모든 옵션은 InteractivitySchema를 참조하세요.

재사용 가능한 컴포넌트를 연결된 컴포지션으로 등록

고유한 레이어나 애니메이션이 있는 상당한 씬과 재사용 가능한 컴포넌트를 독립형 컴포지션으로 등록하세요. Studio는 이를 연결된 컴포지션이라고 합니다: 부모 비디오와 동일한 컴포넌트 구현을 공유하면서 고유한 타임라인에서 열 수 있습니다.

컴포넌트가 부모 없이도 작동하도록 필수 스타일을 정의하고 폰트를 내부에 로드하세요. 부모 독립성을 참조하세요.

부모가 렌더링하는 동일한 내보낸 컴포넌트 참조를 등록하세요. 위의 예에서는 component={LowerThird}를 사용하고 LowerThirdInner나 인라인 래퍼는 사용하지 마세요. wrapInSequence: true를 사용하면 연결에 추가 <Sequence>가 필요하지 않습니다.

import {Composition} from 'remotion';
import {LowerThird} from './LowerThird';

export const RemotionRoot = () => (
  <Composition
    id="LowerThird"
    component={LowerThird}
    width={1280}
    height={720}
    fps={30}
    durationInFrames={30}
    defaultProps={{
      children: 'Jane Doe, Product Designer',
      accentColor: '#0b84f3',
    }}
  />
);

컴포넌트 미리보기에 적합한 고유 ID, 대표적인 인라인 기본값, dimensions, fps 및 duration을 선택하세요. 등록 기본값은 부모 인스턴스의 props를 자동으로 복사하지 않습니다. 연결된 컴포지션의 등록 및 추출 세부 사항을 참조하세요.

타이밍을 컴포넌트에 직접 배치

이미 타이밍 props를 허용하는 하나의 컴포넌트 주위에 별도의 <Sequence>를 피하세요. 내장 상호작용 컴포넌트와 wrapInSequence: true로 만든 사용자 정의 컴포넌트는 name, from, durationInFrames, trimBefore, playbackRate 및 premountFor를 직접 받을 수 있습니다.

<LowerThird
  name="Lower third"
  from={30}
  durationInFrames={90}
  accentColor="#0b84f3"
>
  Jane Doe, Product Designer
</LowerThird>

중복 시퀀스를 제거할 때, 그 타이밍과 이름을 자식으로 이동하세요. 기존 자식 타이밍을 유지하고, 오프셋을 덮어쓰거나 이중 적용하지 마세요. 컴포넌트 내부의 useCurrentFrame() 호출은 로컬 프레임을 읽는 반면, 호출 지점의 스타일 표현식은 여전히 호출자의 프레임을 사용합니다. 애니메이션 스타일을 이동할 때 이 구분을 유지하세요.

여러 형제에 대한 공유 타이밍을 제공하거나, useVideoConfig()에 대한 dimensions를 재정의하거나, 타이밍을 처리하지 않는 컴포넌트를 감쌀 때 <Sequence>를 유지하세요. 연속 레이아웃에는 <Series.Sequence>를, 전환에는 <TransitionSeries.Sequence>를 유지하세요. 직접 from props는 이러한 동작을 대체하지 않습니다.

각 독립적으로 편집 가능한 항목에 고유한 JSX 노드 제공

Studio는 항목을 생성한 JSX 소스 노드를 편집합니다. 여러 런타임 항목이 동일한 JSX 노드에서 오면 하나의 소스 편집 대상만 공유합니다.

독립적으로 편집 가능해야 하는 각 컴포지션 등록, 클립, 씬, 레이어 또는 시퀀스에 대해 별도의 JSX 노드를 작성하고 편집 가능한 props를 해당 노드에 유지하세요. 이는 <Composition>, <Still>, 내장 미디어 컴포넌트, <Sequence>, <Series.Sequence>, <TransitionSeries.Sequence> 및 사용자 정의 컴포넌트에 적용됩니다.

예를 들어, 다음과 같이 편집 가능한 타임라인을 작성하세요:

<Series>
  <Series.Sequence name="Introduction" durationInFrames={90}>
    <Introduction />
  </Series.Sequence>
  <Series.Sequence name="Demo" durationInFrames={150}>
    <Demo />
  </Series.Sequence>
</Series>

.map()이나 다른 프로그램적 루프는 하나의 JSX 소스 노드에서 여러 런타임 항목을 생성합니다. 이는 시각화 막대나 입자와 같이 하나의 템플릿으로 의도적으로 제어되는 반복 출력에 적합합니다. 인스턴스가 Studio에서 독립적인 ID나 이름, props, 메타데이터, 타이밍, 순서, 삭제 또는 복제가 필요한 경우에는 적합하지 않습니다.

Interactive를 사용하여 HTML 요소를 상호작용 가능하게 만들기

모든 HTML 및 SVG 요소(<Img>는 이미 상호작용 가능)는 Interactive를 사용하여 상호작용 가능하게 만들 수 있습니다:

<Interactive.Div
  name="Greeting card"
  style={{fontSize: 80, padding: 24}}
>
  Hello
</Interactive.Div>

이렇게 하면 Studio에서 스타일과 키프레임을 설정할 수 있습니다. 합리적으로 사용하세요: 컴포넌트에 요소가 많으면 타임라인이 복잡해질 수 있습니다.

인라인 텍스트 선호

텍스트가 고정되어 있고 한 번만 사용되는 경우, 상수를 추출하는 대신 상호작용 요소 내부에 직접 작성하세요.

// 👍 고정된 복사본은 편집 가능한 상태로 유지
<Interactive.Div name="Title">
  Remotion Best Practices
</Interactive.Div>

텍스트가 동적이거나 재사용되는 경우에만 prop이나 변수를 사용하세요.

상호작용 요소에 설명적인 이름 지정

요소에 name prop을 추가하여 쉽게 식별할 수 있게 하세요. 계산된 이름은 피하고, 하드코딩하세요.

<>
  <Interactive.Div name="Hero title" style={{fontSize: 80}}>
    Launch day
  </Interactive.Div>
  <Img name="Avatar" src="https://remotion.media/image.jpeg" />
  <Video name="Background" src="https://remotion.media/video.mp4" />
  <Interactive.Div name="Title">
    Launch day
  </Interactive.Div>
</>

모든 CSS 스타일을 인라인으로 유지

가장 좋은 방법은 style에 일반 객체를 전달하는 것입니다 - 상수 참조, 객체 스프레드, 수학 연산은 피하세요.

<Interactive.Div
  style={{
    fontSize: 80,
    color: 'red',
  }}
>
  Hello World!
</Interactive.Div>
const baseStyle = useMemo(() => {
  return {
    fontSize: 12 // ❌ 인라인이 아닌 스타일은 지원되지 않음
  }
}, []);

<Interactive.Div
  style={{
    ...baseStyle, // ❌ 스프레드는 지원되지 않음
    color: RED, // ❌ 상수 참조는 지원되지 않음
    scale: frame * 10 // ❌ 수학 연산은 지원되지 않음
  }}
>
  Hello World!
</Interactive.Div>

interpolate()를 사용하여 애니메이션 적용

변경되는 속성에 인라인 interpolate() 호출로 애니메이션을 작성하세요. 출력 범위, 이징, 외삽 및 output 속성은 하드코딩된 값을 사용해야 합니다.

입력 범위는 useVideoConfig()에서 직접 비구조화한 durationInFrames, fps, width 및 height를 추가로 사용할 수 있습니다. durationInFrames와 같은 단일 식별자, 2 * fps 또는 fps * 2와 같은 숫자 곱셈, durationInFrames - 1과 같은 숫자 뺄셈이 지원됩니다.

const {fps, durationInFrames} = useVideoConfig();

// 👍 인라인 값은 표준화 및 키프레임화 가능
<Interactive.Div
  name="Product card"
  style={{
    color: 'white',
    fontSize: 80,
    scale: interpolate(frame, [0, fps], [0, 1], {
      easing: Easing.spring({damping: 200}),
      output: 'perceptual-scale',
      extrapolateLeft: 'clamp',
      extrapolateRight: 'clamp'
    }),
    rotate: interpolate(frame, [0, 1 * fps], ['0deg', '20deg'], {
      easing: Easing.spring({damping: 200}),
      extrapolateLeft: 'clamp',
      extrapolateRight: 'clamp'
    }),
    translate: interpolate(
      frame,
      [durationInFrames - 30, durationInFrames],
      ['0px 0px', '0px 120px'],
      {
        easing: Easing.spring({damping: 200}),
        output: 'perceptual-scale',
        extrapolateLeft: 'clamp',
        extrapolateRight: 'clamp'
      }
    ),
  }}
/>
const translateY = interpolate(frame, [0, 30], [0, 120]); // ❌ 수학 연산은 마크업에 직접 있어야 함

<Interactive.Div
  name="Product card"
  style={{
    translate: translateY, // ❌ 인라인 interpolate() 호출만 지원됨
    rotate: interpolate(frame, [start, start + 10], [0, Math.PI]), // ❌ 임의의 변수와 수학 연산 사용 불가, 상수 사용 불가
    scale
  }}
/>

기타 고려 사항

  • Remotion Studio가 소스 코드를 편집할 때 포맷팅이 변경될 수 있으므로 소스 코드 포맷팅이 정확해야 합니다.
  • 상호작용 가능한 요소가 너무 많으면 성능에 영향을 줄 수 있으므로 신중하게 사용하세요.
  • wrapInSequence: true를 사용하면 각 인스턴스가 고유한 타임라인을 가지므로, 타임라인 관리가 더 직관적입니다.

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

관련 스킬