Search
moon
sun
🧪

06 · 예제와 문제 해결

아래 예제는 AEUScripts .aeu용이다. 각 코드블록을 표시된 파일명으로 저장한다. TypeScript와 XML은 빌드한 뒤 실행하고, 배포 시 예제 ID를 자신의 namespace로 바꾼다.

일반 창과 modeless dialog

counter.aeu. Common에서 생성 파일을 실행한다. 숫자만 클로저에 보관하며 AE 프로젝트를 수정하지 않는다. 본문이 반환되어도 콜백은 유지된다. 모든 창을 닫아야 세션이 끝난다.
//@ID com.example.counter-window //@NAME 'Counter window' //@TYPE script const view = ui.createWindow({ id: 'counter', title: 'Counter', width: 360, height: 240, content: ui.parse(`<column padding="10" gap="8"> <label id="count" text="Count: 0"/> <button id="increment" text="Count"/> <button id="help" text="About"/> <button id="close" text="Close counter"/> </column>`) }); let count = 0; let helpOpen = false; view.get('increment', 'button').on('click', () => { view.get('count', 'label').text = `Count: ${++count}`; }); view.get('help', 'button').on('click', () => { if (helpOpen) return; const help = ui.createDialog({ id: 'help', title: 'About', width: 360, height: 160, content: ui.parse(`<column padding="10" gap="8"> <label text="This is a modeless AEUScripts dialog."/> <button id="close" text="Close"/> </column>`) }); help.get('close', 'button').on('click', () => help.close()); help.onClose(() => { helpOpen = false; }); help.show(); helpOpen = true; }); view.get('close', 'button').on('click', () => view.close()); view.onClose(() => console.log('Counter closed.')); view.show(); return 'Callbacks remain active after Run.';
TypeScript
복사
동작 확인: Count가 증가하는지, About 창을 닫은 후 다시 열리는지, Counter와 About을 모두 닫은 뒤 세션이 정리되는지 확인한다.

텍스트 생성 도킹 패널

titles.aeu와 titles.aeui.xml을 같은 폴더에 저장한다. Build Current Script 후 생성 .aeu를 Panels에 등록하고 새 ID이면 AE를 재시작한다. 초기화는 UI만 구성하고 Create 클릭마다 comp를 다시 조회한다.

titles.aeu

//@ID com.example.title-panel //@NAME 'Title Panel' //@DESC 'Create text at a chosen position.' //@VERSION 1.0.0 //@TYPE panel const view = ui.createPanel({ id: 'titles', title: 'Title Panel', content: ui.loadXML('titles.aeui.xml') }); function updatePreview(): void { const x = view.get('x', 'number').value; const y = view.get('y', 'number').value; view.get('preview', 'label').text = `Position: ${x}, ${y}`; } view.get('x', 'number').on('input', updatePreview); view.get('y', 'number').on('input', updatePreview); view.get('create', 'button').on('click', () => { const comp = ae.project.activeComp; if (!comp) { view.get('status', 'label').text = 'Open a composition first.'; return; } const text = view.get('title', 'input').value; if (!text.trim()) { view.get('status', 'label').text = 'Enter a title first.'; return; } const position: ae.Position = [ view.get('x', 'number').value, view.get('y', 'number').value ]; const layer = comp.layers.addText(text); layer.position = position; view.get('status', 'label').text = 'Created. AE Undo reverses this click.'; }); updatePreview(); view.show();
TypeScript
복사

titles.aeui.xml

<column gap="8" padding="10"> <panel text="New text layer" orientation="column" gap="6"> <label text="Title"/> <input id="title" value="Hello AEUScripts"/> <group orientation="column" gap="4"> <label text="X position"/> <number id="x" value="320" min="-10000" max="10000" step="1" precision="1"/> <label text="Y position"/> <number id="y" value="240" min="-10000" max="10000" step="1" precision="1"/> </group> <label id="preview" text="Position: 320, 240"/> </panel> <button id="create" text="Create text"/> <label id="status" text="Ready"/> </column>
XML
복사
동작 확인: comp 없음/빈 제목 안내, number 클릭 편집·드래그, 반복 Create, 클릭 한 번당 Undo, XML 변경 후 재빌드·Restart. number 조작은 UI 미리 보기만 바꾸며 기존 레이어를 이동하지 않는다. 각 Create는 새 레이어를 생성한다.

슬라이더와 체크박스·드롭다운

controls.aeu. 빈 트랙 클릭/드래그와 최신 값의 표시, 체크박스, 드롭다운 change 이벤트를 확인하는 일반 창 예제다.
//@ID com.example.controls //@NAME 'Controls example' //@TYPE script const view = ui.createWindow({ id: 'controls', title: 'Controls', content: ui.parse(`<column gap="8" padding="10"> <checkbox id="enabled" text="Enable amount" checked="true"/> <slider id="amount" min="0" max="100" value="25"/> <select id="mode" value="a"> <option value="a" text="Mode A"/> <option value="b" text="Mode B"/> </select> <label id="status" text="Ready"/> </column>`) }); function refresh(): void { const amount = view.get('amount', 'slider').value; const mode = view.get('mode', 'select').value; view.get('status', 'label').text = `Amount ${amount}, mode ${mode}`; } view.get('amount', 'slider').on('input', refresh); view.get('amount', 'slider').on('change', () => { console.log('Committed', view.get('amount', 'slider').value); }); view.get('mode', 'select').on('change', refresh); view.get('enabled', 'checkbox').on('change', event => { view.get('amount', 'slider').enabled = event.target.checked; }); refresh(); view.show();
TypeScript
복사

증상별 점검

•
TypeScript 구문 오류 — AE에 원본을 넣었는지 확인. Build 후 .aeuscript/*.aeu 실행
•
저장했는데 이전 코드가 실행됨 — 빌드 성공 여부와 실행 경로 확인. 실패하면 이전 산출물이 유지됨
•
XML을 고쳤는데 UI가 그대로 — XML 저장 → 원본 빌드 → 패널 Restart
•
새 패널이 Window 메뉴에 없음 — ID/TYPE panel, Panels 등록, 새 ID 등록 후 AE 재시작
•
일반 Run에서 createPanel 오류 — Panels 등록 또는 Developer의 Preview UI script 경로 사용
•
패널 초기화 중 AE 접근 오류 — activeComp 조회와 AE 변경을 이벤트 콜백 안으로 이동
•
이전 Layer/Property가 무효 — 이벤트 간 AE wrapper 보관 여부 확인. 현재 API로 기존 레이어 재검색은 불가
•
Timed out / Stopped — 원인을 해결하고 Run/Restart. UI 콜백은 250 ms; native 호출은 강제 중단 불가
•
에러 후 일부 레이어가 남음 — transaction은 rollback이 아님. AE Undo 사용
•
오류 위치 매핑 거부 — 원본/XML/생성물 일치 여부 확인 후 재빌드·재실행
•
좁은 dock에서 컨트롤 잘림 — 고정 폭 합과 orientation 확인. 자동 wrap/축소 배치 없음
•
재시작 후 입력값이 초기화됨 — JS/UI 상태 자동 직렬화는 미지원. workspace 배치 복원과 별개
부동 창의 입력/IME·AE shortcut은 추가 실기 검증 범위다. AE 단축키가 반응하지 않으면 AE 기본 패널을 클릭해 포커스를 확인한다. 자동화 도구의 창 활성화 문제와 실제 사용자 입력 오류를 구분한다.

배포 전 확인

•
활성 comp 없음과 정상 comp에서 결과가 모두 이해 가능한가.
•
반복 이벤트에서 새로운 AE wrapper를 조회하는가.
•
레이어 생성·변경이 한 번의 Undo로 되돌아가는가.
•
Stop/Restart/Close 후 다시 사용할 수 있는가.
•
작은 도킹 폭과 원하는 입력·숫자 범위에서 조작 가능한가.
•
새 도킹 ID는 AE 재시작 후 정상 등록되고, 기존 ID는 업데이트에도 유지되는가.

예제 검증 범위

가이드 전체의 실행 가능한 예제 7개는 2026-10-03 공개 타입과 실제 VS Code 확장 빌드 엔진으로 타입·메타데이터·XML·코드 생성 검사를 모두 통과했다. 이 검사는 After Effects 실기 검증과 다르다. 이번 문서 작성 중 예제를 실제 AE에서 실행하지는 않았다.

근거

skills/aeuscripts-development/references/examples.md, examples/dock-ui/, examples/phase56/, docs/QUICKSTART.md, docs/SCRIPT_UI_API.md 및 공개 타입.