← RLSC — 컴포넌트의 일생

소비 라인 · 런타임

런타임 대치

풀에서 고른 컴포넌트를 캔버스에 앉히는 구간. 생성과 대치가 같은 경로를 쓴다. 대치 3종(내용 · 스타일 · 이미지)은 전부 모양(sheet json)을 고치지만, 무엇을 근거로 고치는지는 셋이 다르다.

저장소 miricanvas-web-2 · miricanvas-iui 기준 origin/main 80caef442e 갱신 2026-09-13
§0

대치의 시작과 끝

이 절이 답하는 질문 — 사용자가 «이걸로 바꿔» 를 누른 뒤 어떤 코드가 순서대로 도나?

CmdReplaceComponent
BySignature

EventHandler.ts:71

buildMappedComponentSheetJson

:95 에서 호출 · 4단계

ComponentItemReplaceCommand

:118 → 캔버스 반영

줄 번호는 packages/engine/editor/src/text-visualization/CmdReplaceComponentBySignatureEventHandler.ts 기준이다.
§1

서버는 무엇까지 주나

이 절이 답하는 질문 — 검색 응답에 무엇이 들어 있고, 브라우저가 따로 받아 오는 것은 무엇인가?

comp-sign 시그니처(structureHash)로 후보를 검색하고 하나를 고른다. 서버가 돌려주는 것은 후보키와 파일 URL 이고, 큰 컬럼(텍스트 지도·노드 롤)은 브라우저가 그 URL 로 따로 받아 온다.

packages/miricanvas-legacy/src/business/text_visualization/_core/compSignSearch.ts:147
export async function resolveCandidates(matched: MatchedCandidate[]) { return Promise.all(matched.map(async (c) => { const [textMap, textNodeRoles] = await Promise.all([ fetchSignatureJson<TextMap>(c.textMapUrl), fetchSignatureJson<TextNodeRoles>(c.textNodeRolesUrl), ]); return { componentKey: c.componentKey, variant, textMap, textNodeRoles, ... }; })); }

이 payload 가 §2 의 입력이 된다. 서버·브라우저 경계의 전체 지도는 web-2 역할 분담, 시그니처 자체의 구성은 Component Signature 에 있다.

§2

4단계 — 코드가 부르는 순서

이 절이 답하는 질문 — 네 단계는 각각 무엇을 입력으로 받고 무엇을 내놓나? 순서를 바꿀 수 있나?
componentSheetJson 후보의 원본 1 · :108 내용 대치 applyContentMapping 2 · :110 role 폰트 주입 applyRoleFontsToTextNodes 3 · :116 스타일 대치 applyMappedStylesToNodes 4 · :129 이미지 대치 replaceComponentImages 3단계는 nodes 만 교체한다 — 페이지 컨테이너는 1단계 결과를 쓴다 (:122) images 가 없으면 여기서 끝 (:124) buildMappedComponentSheetJson.ts:91-138
네 단계 모두 결국 같은 것을 고친다 — sheet json.

위키 도식과 순서가 다르다

원본 도식은 role 폰트 → 내용 → 스타일 → 이미지 였지만, 코드는 내용(:108) → role 폰트(:110) → 스타일(:116) → 이미지(:129) 다. 폰트 주입이 contentMappedSheetJson 을 입력으로 받으므로 순서가 뒤바뀔 수 없다 — 도식 쪽이 틀렸다.

buildMappedComponentSheetJson.ts:108-122
const contentMappedSheetJson = applyContentMapping(componentSheetJson); const fontInjectedSheetJson = applyRoleFontsToTextNodes({ componentSheetJson: contentMappedSheetJson, // ← 1단계 결과를 받는다 structuredContent: structureJson, roleFonts: extractRoleFontsFromDesign(), }); const { nodes: nodesWithMappedStyles, isSuccess } = await applyMappedStylesToNodes({ componentSheetJson: fontInjectedSheetJson, // ← 2단계 결과를 받는다 structureJson, componentOriginId, }); // 스타일 대치는 nodes만 바꾸므로, content 대치된 페이지 컨테이너에 스타일된 nodes만 교체한다. const styledSheetJson = { ...contentMappedSheetJson, nodes: nodesWithMappedStyles };
§3

스타일 대치의 실체 — 테마가 두 갈래다

이 절이 답하는 질문 — «Style Meta 에 맞춘다» 는 말이 실제로 가리키는 코드는 무엇인가?

여기가 도식과 코드가 가장 크게 갈리는 곳이다. «Style Meta 에 맞춘다»는 두 개의 서로 다른 구현을 하나로 뭉뚱그린 서술이었다.

A · 내용시각화 (이 문서의 흐름)B · AI 프레젠테이션
진입TemplateStyleMapper.getMappedStylesapplyColorMapping
테마 출처런타임 추출 — getTemplateV2ByIdx 로 템플릿 페이지를 받아 buildColorProfile()오프라인 파일 — getTemplateStyleMetadata → styleMetadataFileUrl fetch
테마 타입ColorProfile = { groups }ThemeRegistry
적용 함수recolorComponent(profile, targetBgHex, sheet)같은 iui 패키지의 색 매핑
Component RLSC쓰지 않는다 — 계약 호환용으로 받기만 한다—
캐시StyleMapperMemoryCache (최대 20)fetch 결과

RLSC 를 안 쓴다는 것이 주석에 박혀 있다

packages/engine/editor/src/style_replacement/TemplateStyleMapper.ts:40-50
/** * 현재 페이지의 template 테마를 기반으로 매핑된 스타일을 반환 * @param componentDoc - 스타일을 적용할 컴포넌트 문서 * @param _componentRlsc - 상위 StyleMapper 계약 호환을 위해 받지만 recolor API에서는 사용하지 않음 */ public override async getMappedStyles( componentDoc: PageJson, _componentRlsc: StructuredContent, // ← 밑줄 = 미사용 componentOriginId?: number ): Promise<DesignData | null>

recolorComponent 의 입력

profile
ColorProfile — 현재 템플릿 페이지에서 추출
targetBgHex
현재 페이지 배경색
componentSheet
대치 대상 sheet json

모양 + 테마 + 배경색. 의미(RLSC)는 들어가지 않는다.

테마는 어디서 오나

현재 페이지의 templateReference.key 를 읽어 템플릿 idx 를 얻고, 그 템플릿 페이지들을 받아 buildColorProfile() 로 색 프로파일을 만든다. groups 가 비어 있으면 매핑 자체를 건너뛴다.

즉 목적지 색은 «지금 이 디자인이 쓰고 있는 템플릿»이 정한다 — 컴포넌트가 원래 무슨 색이었는지가 아니라.

color-mapper v2 8단계는 이 경로가 아니다

mapComponentToThemeV2 (slots → keys → Visual Tree → features → candidates → scorer → Hungarian → apply)는 실재한다 — packages/color-mapper/src/v2/index.ts 머리말에 여덟 단계가 그대로 적혀 있다. 그런데 miricanvas-web-2 어디에서도 부르지 않는다. 고정 커밋에서 저장소 전체를 훑어 호출처가 0 임을 확인했다.

현재 사용처는 iui 저장소 안의 문서와 평가 스크립트뿐이다. 런타임이 실제로 쓰는 것은 같은 패키지의 recolor.ts:3181 recolorComponent 이고, 테마는 recolor.ts:1849 buildColorProfile 이 만든다. v2 는 다음 세대이고, 지금 프로덕션은 recolor 다.

§4

그래서 RLSC 는 어디서 쓰이나

이 절이 답하는 질문 — 의미 정보가 실제로 판단에 개입하는 지점은 어디인가?
①

검색 · 랭킹

comp-sign 시그니처의 원료. 무엇을 후보로 올릴지와 그 순서를 정한다.

②

role 폰트 주입

2단계. 현 디자인의 role 별 폰트를 후보의 텍스트 노드에 입힌다. structuredContent 를 직접 받는다.

③

이미지 대치의 role 게이트

4단계. 노드에 role 이 있고 그것이 Decoration 이 아닐 때만 후보로 삼고, Background 는 따로 다룬다.

③의 근거는 replaceComponentImages.ts:116 (isCandidate(node.id) && !!node.role && normalizeRole(node.role) !== 'Decoration') 와 같은 파일 :169 (normalizeRole(node.role) === 'Background') 다.

한 줄 결론

대치 3종은 전부 모양(sheet json)을 고친다. 판단 근거는 의미(RLSC) — 단 검색·폰트·이미지 게이트에서만이고, 색 재배정에는 들어가지 않는다. 색의 목적지는 현재 템플릿에서 추출한 ColorProfile 이다.

§5

코드 지도

단계파일 · 줄저장소
진입 (핸들러)text-visualization/CmdReplaceComponentBySignatureEventHandler.ts:71web-2
4단계 오케스트레이션text-visualization/buildMappedComponentSheetJson.ts:91-138web-2
1 · 내용 대치node/item/component/buildSignatureMappedSheetJson.tsweb-2
2 · role 폰트style_replacement/_utils/ApplyRoleFontsToTextNodes.ts:60web-2
3 · 스타일 대치style_replacement/TemplateStyleMapper.ts:46web-2
3-내부 · recolorpackages/color-mapper/src/recolor.ts:3181iui
3-내부 · 테마 추출packages/color-mapper/src/recolor.ts:1849iui
3-차세대 · v2 8단계packages/color-mapper/src/v2/index.ts  미사용iui
4 · 이미지 대치node/item/component/image-replacement/replaceComponentImages.ts:141web-2
검색 후보 해석miricanvas-legacy/.../text_visualization/_core/compSignSearch.ts:147web-2
AIP 테마 경로style_replacement/colorMapping.tsweb-2
캔버스 반영node/item/component/ComponentItemReplaceCommandweb-2

web-2 경로는 별도 표기가 없으면 packages/engine/editor/src/ 아래다.