SourceEditor
이 컨트롤은 무엇인가요?
SourceEditor는 마이크로소프트의 Monaco Editor(VS Code에서 사용하는 것과 같은 코드 편집 엔진)를 감싼 syn.uicontrols 컨트롤입니다. <syn_sourceeditor> 태그를 화면에 배치하면, 컨트롤이 초기화되면서 다음과 같은 동작이 일어납니다.
- 원래 배치했던 엘리먼트는
id="<원래id>_hidden"으로 이름이 바뀌고 화면에서 숨겨집니다(display: none). 이 엘리먼트는 옵션 값을 보관하는 용도로만 남습니다. - 같은 부모 아래에
id="<원래id>"를 그대로 물려받은 새<div>가 만들어지고, 그 안에 실제 Monaco 에디터(monaco.editor.create(...))가 렌더링됩니다. - Monaco 엔진 자체(
/lib/monaco-editor/min/vs/loader.js)는 최초 1회만 비동기로 로드되며, 로드가 끝나기 전에 여러 개의<syn_sourceeditor>가 있으면 대기열(editorPendings)에 쌓였다가 로드 완료 후 순서대로 초기화됩니다.
HtmlEditor(WYSIWYG)와의 차이
| 구분 | HtmlEditor ($htmleditor) | SourceEditor ($sourceeditor) |
|---|---|---|
| 기반 엔진 | TinyMCE | Monaco Editor (VS Code 편집 엔진) |
| 편집 대상 | 서식 있는 문서(HTML 리치 텍스트) — 굵게, 표, 이미지 삽입 등 | 소스 코드/구조화된 텍스트 — JavaScript, JSON, SQL, HTML 등 |
| 결과물 | HTML 마크업 문자열 | 언어 문법에 맞는 순수 텍스트(소스 코드) 문자열 |
| 대표 기능 | 툴바(굵게/이미지/표), 이미지 업로드(fileManagerServer) | 문법 강조(syntax highlight), 줄 번호, 미니맵, 코드 접기 |
| 용도 예 | 게시판 본문, 이메일 템플릿, 공지사항 | 스크립트/쿼리 편집, 설정 JSON 편집, 로그/코드 뷰어 |
즉, "사람이 읽는 문서를 예쁘게 꾸미는 것"이 목적이면 HtmlEditor를, "코드나 JSON처럼 문법이 있는 텍스트를 정확하게 편집하는 것"이 목적이면 SourceEditor를 사용합니다.
언제 사용하나요?
- JavaScript, JSON, SQL, HTML, XML 등 특정 언어 문법의 소스 코드를 화면에서 직접 편집해야 할 때
- 줄 번호, 문법 강조, 미니맵 같은 코드 편집기 UI가 필요할 때
- 설정 값(JSON)이나 쿼리문처럼 형식이 엄격한 텍스트를 사용자가 직접 입력/수정하게 할 때
- 단순한 여러 줄 텍스트(메모 등)만 필요하다면
TextArea(CodeMirror 기반, 더 가벼움)를, 서식 있는 문서 편집이 필요하다면HtmlEditor(TinyMCE 기반)를 대신 검토하세요.
빠른 시작
- 페이지에
<syn_sourceeditor>태그를 배치하고id,syn-datafield,syn-options속성을 지정합니다.
<syn_sourceeditor id="txtEditor1"
syn-datafield="SourceCode"
syn-options="{language: 'javascript', theme: 'vs-dark', minimap: {enabled: true}}"></syn_sourceeditor>
- 페이지 하단에
syn.loader.js스크립트 한 줄만 추가하면, 로더가 필요한 CSS/JS(SourceEditor.js,SourceEditor.css, Monaco 엔진 로더)를 자동으로 주입하고 컨트롤을 초기화합니다.
<script src="/js/syn.loader.js"></script>
- 값을 읽거나 설정할 때는 페이지 스크립트에서
syn.uicontrols.$sourceeditor를 사용합니다.
// 현재 값 조회
var code = syn.uicontrols.$sourceeditor.getValue('txtEditor1');
// 값 설정
syn.uicontrols.$sourceeditor.setValue('txtEditor1', 'function hello() {\n\talert("Hello world!");\n}');
// 값 초기화
syn.uicontrols.$sourceeditor.clear('txtEditor1');
Monaco 에디터는 비동기로 로드됩니다. 컨트롤이 아직 초기화되기 전(스크립트 로드 중)에는
getValue/setValue가 대상을 찾지 못할 수 있으므로, 페이지 로드 직후 곧바로 호출하기보다는 버튼 클릭 등 사용자 이벤트 시점에 호출하는 것이 안전합니다.
예제 실행하기
이 폴더의 example 디렉토리에 초보자용 실행 예제가 있습니다. 웹 서버를 통해 /uicontrols/SourceEditor/example/*.html 경로로 접속해서 바로 확인할 수 있습니다.
basic.html: JavaScript 모드 기본 에디터를 배치하고getValue/setValue/clear버튼으로 값을 다루는 가장 단순한 예제language-mode.html:language옵션을 JSON/HTML 등으로 바꿔가며 같은 컨트롤이 언어별로 어떻게 강조되는지 비교하는 예제events.html: Monaco 자체 이벤트 API(getControl().editor.onDidChangeModelContent)로 변경 이벤트를 감지하고,getValue/setValue/clear도 함께 보여주는 예제
각 예제는 <script src="/js/syn.loader.js"></script> 한 줄만으로 동작하도록 만들어졌습니다.
더 알아보기
- 전체 옵션(Options), 메서드, 이벤트(및 알려진 제약사항) 목록은 같은 폴더의 아래 API 참조 섹션 문서를 참고하세요.
- 실제 소스 코드는
SourceEditor.js,SourceEditor.css파일에서 확인할 수 있습니다. - Monaco 에디터 자체의 옵션/API는 Monaco Editor 공식 문서를 참고하세요.
syn-options에 지정한 값은monaco.editor.create(container, setting)에 그대로 전달되므로, 이 문서에 표로 정리한 옵션 외에도 Monaco가 지원하는 모든 생성 옵션(IStandaloneEditorConstructionOptions)을 사용할 수 있습니다.
실전 예제 페이지
/uicontrols/SourceEditor/example/ 경로의 예제를 아래 iframe에서 바로 확인할 수 있습니다.
basic.html
events.html
language-mode.html
소스와 로드 파일
| 항목 | 내용 |
|---|---|
| 컨트롤 | SourceEditor |
| 전역 별칭 | syn.uicontrols.$sourceeditor |
| 버전 | v2026.2.25 |
| 소스 | uicontrols/SourceEditor/SourceEditor.js |
| 스타일 | uicontrols/SourceEditor/SourceEditor.css |
API 참조
syn.uicontrols.$sourceeditor
마크업
<syn_sourceeditor id="txtEditor1"
syn-datafield="SourceCode"
syn-options="{language: 'javascript', theme: 'vs-dark', minimap: {enabled: true}}"></syn_sourceeditor>
- 태그 이름
syn_sourceeditor는syn.loader.js가SYN_접두사를 인식해sourceeditor타입 컨트롤로 매핑하는 규칙을 따릅니다(case 'sourceeditor'→SourceEditor.js/SourceEditor.css로드). syn-options: JSON 형식의 문자열로 옵션(defaultSetting)을 덮어씁니다. 이 값은 그대로monaco.editor.create(container, setting)에 전달됩니다.- 컨트롤이 초기화되면 원래 태그는
id="<원래id>_hidden"으로 숨겨지고, 같은id를 가진 새<div>가 만들어져 그 안에 Monaco 에디터가 렌더링됩니다. 이후getValue/setValue등 API는 원래 지정했던id를 그대로 사용합니다.
Options (defaultSetting)
SourceEditor.js의 defaultSetting에 정의된 값입니다. syn-options 속성으로 필요한 값만 덮어써서 사용합니다. 여기 표에 없는 값이라도 Monaco의 IStandaloneEditorConstructionOptions에 해당하는 옵션이면 함께 전달할 수 있습니다.
| 옵션 | 기본값 | 설명 |
|---|---|---|
width | '100%' | 에디터 컨테이너 너비. |
height | '360px' | 에디터 컨테이너 높이. |
language | 'javascript' | 문법 강조에 사용할 언어 모드. Monaco가 지원하는 언어 id(json, typescript, html, css, sql, xml, markdown, python 등)를 사용할 수 있습니다. |
minimap | 객체 설정 | 코드 축소판(미니맵) 표시 여부. |
roundedSelection | false | 선택 영역 모서리를 둥글게 표시할지 여부. |
scrollBeyondLastLine | false | 마지막 줄 이후로도 스크롤이 가능하게 할지 여부. |
readOnly | false | true이면 읽기 전용 에디터가 됩니다. |
lineNumbers | 'on' | 줄 번호 표시 방식('on', 'off', 'relative', 'interval'). |
theme | 'vs-dark' | 에디터 테마. Monaco 내장 테마 'vs'(밝은 테마), 'vs-dark'(어두운 테마), 'hc-black'(고대비) 등을 사용할 수 있습니다. |
dataType | 'string' | 값의 데이터 타입 표기. |
basePath | '/lib/monaco-editor/min/vs' | Monaco 엔진 로더 경로. syn.Config.DomainBaseUrl이 현재 origin과 다르면 자동으로 해당 도메인 기준 경로로 치환됩니다. |
belongID | null | 소속 그룹/컨테이너 식별자. |
controlText | null | 컨트롤에 표시할 보조 텍스트. |
validators | null | 유효성 검증 설정. |
transactConfig | null | 데이터 송수신(트랜잭션) 관련 설정. |
triggerConfig | null | 트리거 동작 관련 설정. |
메서드
syn.uicontrols.$sourceeditor.<메서드명>(...) 형태로 호출합니다.
| 메서드 | 매개변수 | 반환값 | 설명 |
|---|---|---|---|
getValue | elID, meta | string | 에디터의 현재 텍스트 값을 반환합니다. |
setValue | elID, value, meta | 없음 | 에디터의 텍스트 값을 설정합니다. |
clear | elID, isControlLoad | 없음 | 에디터 값을 빈 문자열로 초기화합니다. |
layoutResize | elID | 없음 | 에디터 레이아웃을 다시 계산합니다(editor.layout()). 컨테이너 크기가 코드로 바뀐 직후 등에 사용합니다. 참고로 브라우저 창 크기 변경(resize)은 모든 SourceEditor 인스턴스에 대해 자동으로 처리됩니다. |
getActions | elID | `array \ | null` |
getControl | elID | `object \ | null` |
controlLoad | elID, setting | 없음 | 컨트롤 초기화 진입점입니다. syn 프레임워크가 페이지 로드 시 자동으로 호출하며, 직접 호출할 필요는 없습니다(Monaco 엔진이 아직 로드되지 않았으면 내부 대기열에 넣었다가 로드 완료 후 초기화합니다). |
setLocale | elID, translations, control, options | 없음 | 다국 어 텍스트 적용 훅입니다. 현재 구현은 빈 함수(별도 동작 없음)입니다. |
⚠️ 참고용 메서드 — 현재 구현에 알려진 제약(버그)이 있음
아래 메서드들은 SourceEditor.js(v2026.2.25) 소스에 정의는 되어 있지만, 함수 선언에 elID 매개변수가 없는 상태로 내부에서 elID 변수를 참조하고 있습니다. 즉, 호출 스코프에 전역 변수 elID가 존재하지 않는 한 실제로 호출하면 ReferenceError: elID is not defined가 발생합니다.
| 메서드 | 매개변수(선언부) | 의도된 기능 |
|---|---|---|
applyEdits | text, position | 지정 위치(또는 현재 커서 위치)에 텍스트를 삽입/치환 |
getValueInRange | position | 지정 범위의 텍스트 조회 |
getContentLine | text | 특정 문자열이 포함된 줄 번호 조회 |
setSelection | range | 지정 범위를 선택 영역으로 지정 |
revealLine | line | 지정한 줄로 스크롤 이동 |
findSelection | text | 찾기 위젯을 열어 텍스트 검색 |
대안: 이 기능들이 필요하면 getControl(elID).editor로 Monaco 인스턴스를 직접 얻어, 위 메서드들이 내부적으로 호출하던 Monaco 공식 API를 그대로 사용하세요.
var control = syn.uicontrols.$sourceeditor.getControl('txtEditor1');
if (control && control.editor) {
var editor = control.editor;
// revealLine(line) 대신
editor.revealLine(3);
// setSelection(range) 대신
editor.setSelection(range);
// getValueInRange(position) 대신
var text = editor.getModel().getValueInRange(range);
}
이벤트 (syn-events)
SourceEditor는 다른 syn.uicontrols 컨트롤과 달리 syn-events 속성을 통한 이벤트 자동 연결을 지원하지 않습니다. SourceEditor.js 내부에는 syn-events를 읽어 event.<id>_<이벤트명> 핸들러에 연결하는 로직이 없으며, 자체적으로는 브라우저 resize 이벤트에 대한 내부 처리(editor.layout() 재호출)와 Ctrl+Space 키 입력 기본 동작 방지만 등록되어 있습니다.
변경 감지, 포커스/블러 감지 등이 필요하면 getControl(elID).editor로 얻은 Monaco 인스턴스에 Monaco의 공식 이벤트 API를 직접 등록하세요.
| Monaco 이벤트 API | 설명 |
|---|---|
editor.onDidChangeModelContent(handler) | 에디터 내용이 변경될 때 발생합니다. (change에 해당) |
editor.onDidBlurEditorText(handler) | 에디터가 포커스를 잃을 때 발생합니다. (blur에 해당) |
editor.onDidFocusEditorText(handler) | 에디터가 포커스를 얻을 때 발생합니다. (focus에 해당) |
editor.onDidChangeCursorPosition(handler) | 커서 위치가 바뀔 때 발생합니다. |
var control = syn.uicontrols.$sourceeditor.getControl('txtEditor1');
if (control && control.editor) {
control.editor.onDidChangeModelContent(function (evt) {
syn.$l.eventLog('txtEditor1_change', control.editor.getValue());
});
}
컨트롤이 비동기(Monaco 엔진 로드 후)로 초기화되므로, 위 이벤트 등록 코드는
pageLoad직후가 아니라 버튼 클릭 등 사용자가 페이지를 조작한 이후(에디터 초기화가 끝난 시점) 또는getControl이null이 아님을 확인한 뒤에 실행하는 것이 안전합니다.
참고
- 실제 구현 파일:
SourceEditor.js,SourceEditor.css - 내부적으로 Monaco Editor를 사용하며, 엔진 스크립트는
basePath(/lib/monaco-editor/min/vs)에서 최초 1회만 비동기로 로드됩니다. - Monaco 엔진이 아직 로드되지 않은 상태에서 여러
<syn_sourceeditor>가 배치돼 있으면, 로드가 끝날 때까지 내부 대기열(editorPendings)에 쌓였다가 순서대로 초기화됩니다. applyEdits,getValueInRange,getContentLine,setSelection,revealLine,findSelection메서드는 현재 소스 기준elID매개변수 누락 버그가 있어 직접 호출 시 오류가 발생합니다. 위 "참고용 메서드" 절의 대안(getControl(elID).editor로 Monaco API 직접 호출)을 사용하세요.