웹 코드 편집기 만들기

게시 됨: 2022-03-10
빠른 요약 ↬ 어떤 형태로든 코드 편집기가 필요한 플랫폼을 구축하려는 개발자라면 이 기사가 적합합니다. 이 기사에서는 일부 HTML, CSS 및 JavaScript를 사용하여 실시간으로 결과를 표시하는 웹 코드 편집기를 만드는 방법을 설명합니다.

온라인 웹 코드 편집기는 코드 편집기 응용 프로그램을 사용할 기회가 없거나 컴퓨터나 휴대폰으로 웹에서 무언가를 빠르게 시도하고 싶을 때 가장 유용합니다. 코드 편집기를 빌드하는 방법에 대한 지식이 있으면 일부 기능을 표시하기 위해 코드 편집기를 통합해야 하는 다른 프로젝트에 접근하는 방법에 대한 아이디어를 얻을 수 있기 때문에 이것은 작업하기에 흥미로운 프로젝트이기도 합니다.

다음은 이 기사를 따라하기 위해 알아야 할 몇 가지 React 개념입니다.

  • 후크,
  • 구성 요소 구조,
  • 기능성 성분,
  • 소품.

CodeMirror 사용

CodeMirror라는 라이브러리를 사용하여 편집기를 빌드합니다. CodeMirror는 브라우저용 JavaScript로 구현된 다목적 텍스트 편집기입니다. 특히 코드 편집을 위한 것으로 다양한 언어 모드와 고급 편집 기능을 위한 추가 기능이 함께 제공됩니다.

풍부한 프로그래밍 API와 CSS 테마 시스템을 사용하여 CodeMirror를 애플리케이션에 맞게 사용자 정의하고 새로운 기능으로 확장할 수 있습니다. 웹에서 실행되고 실시간으로 코드 결과를 보여주는 풍부한 코드 편집기를 만드는 기능을 제공합니다.

다음 섹션에서는 새로운 React 프로젝트를 설정하고 웹 앱을 빌드하는 데 필요한 라이브러리를 설치합니다.

새로운 React 프로젝트 생성하기

새로운 React 프로젝트를 만드는 것으로 시작해 봅시다. 명령줄 인터페이스에서 프로젝트를 생성하려는 디렉토리로 이동하고 React 애플리케이션을 생성하고 이름을 code_editor 로 지정하겠습니다.

 npx create-react-app code_editor

새로운 React 애플리케이션을 만들었으면 명령줄 인터페이스에서 해당 프로젝트의 디렉토리로 이동해 보겠습니다.

 cd code_editor

여기에 설치해야 하는 두 개의 라이브러리가 있습니다: codemirrorreact-codemirror2 .

 npm install codemirror react-codemirror2

이 프로젝트에 필요한 라이브러리를 설치했으면 탭을 만들고 편집기에 표시될 세 개의 탭(HTML, CSS 및 JavaScript용) 간에 탭 전환을 활성화하겠습니다.

점프 후 더! 아래에서 계속 읽기 ↓

버튼 컴포넌트

개별 버튼을 만드는 대신 버튼을 재사용 가능한 컴포넌트로 만들어 보겠습니다. 우리 프로젝트에서 버튼에는 필요한 세 개의 탭에 따라 세 개의 인스턴스가 있습니다.

src 폴더에 components 라는 폴더를 만듭니다. 이 새 components 폴더에서 Button.jsx 라는 Button.jsx 파일을 만듭니다.

다음은 Button 구성 요소에 필요한 모든 코드입니다.

 import React from 'react' const Button = ({title, onClick}) => { return ( <div> <button style={{ maxWidth: "140px", minWidth: "80px", height: "30px", marginRight: "5px" }} onClick={onClick} > {title} </button> </div> ) } export default Button

다음은 위에서 수행한 작업에 대한 전체 설명입니다.

  • Button 이라는 기능 구성 요소를 만든 다음 내보냈습니다.
  • 컴포넌트로 들어오는 props에서 titleonClick 을 구조화했습니다. 여기서 title 은 텍스트 문자열이고 onClick 은 버튼을 클릭할 때 호출되는 함수입니다.
  • 다음으로, button 요소를 사용하여 버튼을 선언하고 style 속성을 사용하여 버튼의 스타일을 표시하기 쉽게 만들었습니다.
  • onClick 속성을 추가하고 구조화되지 않은 onClick 함수 소품을 여기에 전달했습니다.
  • 이 구성 요소에서 마지막으로 확인할 수 있는 것은 {title}button 태그의 콘텐츠로 전달하는 것입니다. 이렇게 하면 호출될 때 버튼 구성 요소의 인스턴스에 전달되는 소품에 따라 제목을 동적으로 표시할 수 있습니다.

재사용 가능한 버튼 구성 요소를 만들었으므로 이제 구성 요소를 App.js. 로 가져옵니다. App.js 로 이동하여 새로 생성된 버튼 구성 요소를 가져옵니다.

 import Button from './components/Button';

열려 있는 탭이나 편집기를 추적하려면 열려 있는 편집기의 값을 유지하기 위한 선언 상태가 필요합니다. useState React 후크를 사용하여 해당 탭의 버튼을 클릭할 때 현재 열려 있는 편집기 탭의 이름을 저장할 상태를 설정합니다.

이를 수행하는 방법은 다음과 같습니다.

 import React, { useState } from 'react'; import './App.css'; import Button from './components/Button'; function App() { const [openedEditor, setOpenedEditor] = useState('html'); return ( <div className="App"> </div> ); } export default App;

여기에서 우리는 우리의 상태를 선언했습니다. 현재 열려 있는 편집기의 이름을 사용합니다. 값 html 이 상태의 기본값으로 전달되기 때문에 HTML 편집기는 기본적으로 열린 탭이 됩니다.

탭 버튼을 클릭했을 때 상태 값을 변경하기 위해 setOpenedEditor 를 사용할 함수를 작성해 봅시다.

참고: 두 개의 탭이 동시에 열리지 않을 수 있으므로 함수를 작성할 때 이를 고려해야 합니다.

onTabClick 이라는 함수는 다음과 같습니다.

 import React, { useState } from 'react'; import './App.css'; import Button from './components/Button'; function App() { ... const onTabClick = (editorName) => { setOpenedEditor(editorName); }; return ( <div className="App"> </div> ); } export default App;

여기에서 현재 선택된 탭의 이름인 단일 함수 인수를 전달했습니다. 이 인수는 함수가 호출되는 모든 위치에 제공되며 해당 탭의 관련 이름이 전달됩니다.

필요한 세 개의 탭에 대한 세 개의 Button 인스턴스를 만들어 보겠습니다.

 <div className="App"> <p>Welcome to the editor!</p> <div className="tab-button-container"> <Button title="HTML" onClick={() => { onTabClick('html') }} /> <Button title="CSS" onClick={() => { onTabClick('css') }} /> <Button title="JavaScript" onClick={() => { onTabClick('js') }} /> </div> </div>

다음은 우리가 한 일입니다.

  • 기본적으로 애플리케이션에 대한 컨텍스트를 제공하기 위해 p 태그를 추가하는 것으로 시작했습니다.
  • div 태그를 사용하여 탭 버튼을 래핑했습니다. div 태그는 이 튜토리얼의 뒷부분에서 CSS 파일의 그리드 디스플레이에 버튼의 스타일을 지정하는 데 사용할 className 을 전달합니다.
  • 다음으로 Button 구성 요소의 세 가지 인스턴스를 선언했습니다. 생각해보면 Button 구성 요소는 titleonClick 이라는 두 개의 소품을 사용합니다. Button 구성 요소의 모든 인스턴스에서 이 두 가지 소품이 제공됩니다.
  • title 소품은 탭의 제목을 사용합니다.
  • onClick prop은 우리가 방금 생성한 onTabClick 함수를 사용합니다. 이 함수는 선택한 탭의 이름이라는 단일 인수를 사용합니다.

현재 선택한 탭을 기반으로 JavaScript 삼항 연산자를 사용하여 조건부로 탭을 표시합니다. 즉, openedEditor 상태 값이 html 로 설정되면(즉 setOpenedEditor('html') ) HTML 섹션의 탭이 현재 표시되는 탭이 됩니다. 아래에서 하면 더 잘 이해될 것입니다.

 ... return ( <div className="App"> ... <div className="editor-container"> { openedEditor === 'html' ? ( <p>The html editor is open</p> ) : openedEditor === 'css' ? ( <p>The CSS editor is open!!!!!!</p> ) : ( <p>the JavaScript editor is open</p> ) } </div> </div> ); ...

위의 코드를 일반 영어로 살펴보겠습니다. openedEditor 의 값이 html 이면 HTML 섹션을 표시합니다. 그렇지 않고, openedEditor 의 값이 css 이면 CSS 섹션을 표시합니다. 그렇지 않고 값이 html 도 아니고 css 도 아니면 값이 js openedEditor 함을 의미합니다. 따라서 JavaScript용 탭을 표시합니다.

삼항 연산자 조건의 여러 섹션에 대해 단락 태그( p )를 사용했습니다. 계속 진행하면서 편집기 구성 요소를 만들고 p 태그를 편집기 구성 요소 자체로 교체합니다.

우리는 이미 여기까지 왔습니다! 버튼을 클릭하면 나타내는 탭을 true 로 설정하는 작업이 시작되어 해당 탭이 표시됩니다. 현재 앱의 모습은 다음과 같습니다.

현재 가지고 있는 탭 토글을 보여주는 GIF.
현재 가지고 있는 탭 토글을 보여주는 GIF. (큰 미리보기)

버튼이 있는 div 컨테이너에 약간의 CSS를 추가해 보겠습니다. 버튼이 위의 이미지처럼 세로로 쌓이는 대신 그리드에 표시되기를 원합니다. App.css 파일로 이동하여 다음 코드를 추가합니다.

 .tab-button-container{ display: flex; }

세 개의 탭 버튼이 있는 div 태그의 속성으로 className="tab-button-container" 를 추가했음을 상기하십시오. 여기에서 CSS를 사용하여 디스플레이를 flex 로 설정하여 해당 컨테이너의 스타일을 지정했습니다. 결과는 다음과 같습니다.

CSS를 사용하여 디스플레이를 플렉스로 설정합니다.
(큰 미리보기)

이 지경에 이르기까지 얼마나 많은 일을 했는지 자랑스럽게 생각합니다. 다음 섹션에서는 p 태그를 대체하여 편집기를 생성합니다.

편집기 만들기

CodeMirror 편집기 내에서 작업할 라이브러리를 이미 설치했으므로 계속해서 components 폴더에 Editor.jsx 파일을 생성하겠습니다.

구성 요소 > Editor.jsx

새 파일을 만들었으면 여기에 몇 가지 초기 코드를 작성해 보겠습니다.

 import React, { useState } from 'react'; import 'codemirror/lib/codemirror.css'; import { Controlled as ControlledEditorComponent } from 'react-codemirror2'; const Editor = ({ language, value, setEditorState }) => { return ( <div className="editor-container"> </div> ) } export default Editor

우리가 한 일은 다음과 같습니다.

  • 우리는 React가 필요할 것이기 때문에 useState 후크와 함께 React를 가져왔습니다.
  • CodeMirror CSS 파일을 가져왔습니다(이는 우리가 설치한 CodeMirror 라이브러리에서 가져오므로 특별한 방법으로 설치할 필요가 없습니다).
  • react-codemirror2 에서 Controlled 를 가져와서 이름을 ControlledEditorComponent 로 변경하여 더 명확하게 했습니다. 우리는 이것을 곧 사용할 것입니다.
  • 그런 다음 Editor 기능 구성 요소를 선언했으며 현재는 return 문에 className 이 있는 빈 div 가 있는 return 문을 가지고 있습니다.

기능 구성 요소에서 language , valuesetEditorState 를 포함하여 props의 일부 값을 구조화했습니다. 이 세 가지 소품은 App.js 에서 호출될 때 편집기의 모든 인스턴스에서 제공됩니다.

ControlledEditorComponent 를 사용하여 편집기용 코드를 작성해 보겠습니다. 우리가 할 일은 다음과 같습니다.

 import React, { useState } from 'react'; import 'codemirror/lib/codemirror.css'; import 'codemirror/mode/xml/xml'; import 'codemirror/mode/javascript/javascript'; import 'codemirror/mode/css/css'; import { Controlled as ControlledEditorComponent } from 'react-codemirror2'; const Editor = ({ language, value, setEditorState }) => { return ( <div className="editor-container"> <ControlledEditorComponent onBeforeChange={handleChange} value= {value} className="code-mirror-wrapper" options={{ lineWrapping: true, lint: true, mode: language, lineNumbers: true, }} /> </div> ) } export default Editor

CodeMirror 용어를 설명하면서 여기에서 수행한 작업을 살펴보겠습니다.

CodeMirror 모드는 편집기가 사용되는 언어를 지정합니다. 이 프로젝트에 세 명의 편집기가 있으므로 세 가지 모드를 가져왔습니다.

  1. XML: 이 모드는 HTML용입니다. XML이라는 용어를 사용합니다.
  2. JavaScript: 이것은 ( codemirror/mode/javascript/javascript ) JavaScript 모드를 가져옵니다.
  3. CSS: 이것은 ( codemirror/mode/css/css ) CSS 모드를 가져옵니다.

참고: 편집기는 재사용 가능한 구성 요소로 빌드되기 때문에 편집기에 직접 모드를 넣을 수 없습니다. 그래서 우리는 우리가 구조화한 language 소품을 통해 모드를 제공합니다. 그러나 이것은 작동하기 위해 모드를 가져와야 한다는 사실을 변경하지 않습니다.

다음으로, ControlledEditorComponent 의 내용에 대해 논의해 보겠습니다.

  • onBeforeChange
    편집기에 쓰거나 편집기에서 제거할 때마다 호출됩니다. 일반적으로 변경 사항을 추적하기 위해 입력 필드에 있는 onChange 핸들러와 유사하다고 생각하십시오. 이를 사용하여 새로운 변경 사항이 있을 때마다 편집기의 값을 가져와 편집기의 상태에 저장할 수 있습니다. 계속 진행하면서 {handleChange} 함수를 작성할 것입니다.
  • value = {value}
    이것은 주어진 시간에 편집자의 내용일 뿐입니다. 이 속성에 value 라는 이름의 구조화되지 않은 소품을 전달했습니다. value props는 해당 편집기의 값을 보유하는 상태입니다. 이것은 편집기의 인스턴스에서 제공됩니다.
  • className ="code-mirror-wrapper"
    이 클래스 이름은 우리가 스스로 만든 스타일이 아닙니다. 위에서 가져온 CodeMirror의 CSS 파일에서 제공됩니다.
  • options
    이것은 에디터가 갖고자 하는 다른 기능을 취하는 객체입니다. CodeMirror에는 놀라운 옵션이 많이 있습니다. 여기에서 사용한 것을 살펴보겠습니다.
    • lineWrapping: true
      즉, 줄이 가득 차면 코드가 다음 줄로 줄 바꿈되어야 합니다.
    • lint: true
      이것은 보풀을 허용합니다.
    • mode: language
      이 모드는 위에서 논의한 바와 같이 편집기가 사용할 언어를 사용합니다. 언어는 이미 위에서 가져왔지만 편집기는 prop을 통해 편집기에 제공된 language 값을 기반으로 언어를 적용할 것입니다.
    • lineNumbers: true
      이것은 편집기가 각 줄에 대한 줄 번호를 가져야 함을 지정합니다.

다음으로 onBeforeChange 핸들러에 대한 handleChange 함수를 작성할 수 있습니다.

 const handleChange = (editor, data, value) => { setEditorState(value); }

onBeforeChange 핸들러는 editor, data, value 세 가지에 대한 액세스를 제공합니다.

setEditorState 소품에 전달하려는 value 이기 때문에 값만 필요합니다. setEditorState 소품은 App.js 에서 선언한 각 상태에 대한 설정 값을 나타내며, 각 편집기에 대한 값을 보유합니다. 계속 진행하면서 이것을 Editor 구성 요소에 소품으로 전달하는 방법을 살펴보겠습니다.

다음으로 편집기에 대해 다른 테마를 선택할 수 있는 드롭다운을 추가합니다. 그럼 CodeMirror의 테마를 살펴보겠습니다.

CodeMirror 테마

CodeMirror에는 선택할 수 있는 여러 테마가 있습니다. 사용 가능한 다양한 테마의 데모를 보려면 공식 웹사이트를 방문하십시오. 사용자가 편집기에서 선택할 수 있는 다양한 테마로 드롭다운을 만들어 보겠습니다. 이 자습서에서는 5개의 테마를 추가하지만 원하는 만큼 추가할 수 있습니다.

먼저 Editor.js 구성 요소에서 테마를 가져옵니다.

 import 'codemirror/theme/dracula.css'; import 'codemirror/theme/material.css'; import 'codemirror/theme/mdn-like.css'; import 'codemirror/theme/the-matrix.css'; import 'codemirror/theme/night.css';

다음으로 가져온 모든 테마의 배열을 만듭니다.

 const themeArray = ['dracula', 'material', 'mdn-like', 'the-matrix', 'night']

선택한 테마의 값을 유지하기 위해 useState 후크를 선언하고 기본 테마를 dracula 로 설정합니다.

 const [theme, setTheme] = useState("dracula")

드롭다운을 만들어 보겠습니다.

 ... return ( <div className="editor-container"> <div style={{marginBottom: "10px"}}> <label for="cars">Choose a theme: </label> <select name="theme" onChange={(el) => { setTheme(el.target.value) }}> { themeArray.map( theme => ( <option value={theme}>{theme}</option> )) } </select> </div> // the rest of the code comes below... </div> ) ...

위의 코드에서는 label HTML 태그를 사용하여 드롭다운에 레이블을 추가한 다음 select HTML 태그를 추가하여 드롭다운을 만들었습니다. select 요소의 option 태그는 드롭다운에서 사용 가능한 옵션을 정의합니다.

생성한 themeArray 의 테마 이름으로 드롭다운을 채워야 했기 때문에 .map 배열 메서드를 사용하여 themeArray 를 매핑하고 option 태그를 사용하여 이름을 개별적으로 표시했습니다.

잠시만요. 위의 코드에 대한 설명이 끝나지 않았습니다. 여는 select 태그에서 onChange 속성을 전달하여 드롭다운에서 새 값이 선택될 때마다 theme 상태를 추적하고 업데이트했습니다. 드롭다운에서 새 옵션을 선택할 때마다 반환된 개체에서 값을 가져옵니다. 다음으로 상태 후크에서 setTheme 를 사용하여 상태가 보유하는 값으로 새 값을 설정합니다.

이 시점에서 드롭다운을 만들고 테마의 상태를 설정하고 새 값으로 상태를 설정하는 함수를 작성했습니다. CodeMirror가 테마를 사용하도록 하기 위해 마지막으로 해야 할 일은 ControlledEditorComponentoptions 객체에 테마를 전달하는 것입니다. options 개체에서 theme 이라는 값을 추가하고 해당 값을 theme 이라는 선택된 테마에 대한 상태 값으로 설정하겠습니다.

이제 ControlledEditorComponent 의 모습은 다음과 같습니다.

 <ControlledEditorComponent onBeforeChange={handleChange} value= {value} className="code-mirror-wrapper" options={{ lineWrapping: true, lint: true, mode: language, lineNumbers: true, theme: theme, }} />

이제 편집기에서 선택할 수 있는 다양한 테마의 드롭다운을 만들었습니다.

현재 Editor.js 의 전체 코드는 다음과 같습니다.

 import React, { useState } from 'react'; import 'codemirror/lib/codemirror.css'; import 'codemirror/theme/dracula.css'; import 'codemirror/theme/material.css'; import 'codemirror/theme/mdn-like.css'; import 'codemirror/theme/the-matrix.css'; import 'codemirror/theme/night.css'; import 'codemirror/mode/xml/xml'; import 'codemirror/mode/javascript/javascript'; import 'codemirror/mode/css/css'; import { Controlled as ControlledEditorComponent } from 'react-codemirror2'; const Editor = ({ language, value, setEditorState }) => { const [theme, setTheme] = useState("dracula") const handleChange = (editor, data, value) => { setEditorState(value); } const themeArray = ['dracula', 'material', 'mdn-like', 'the-matrix', 'night'] return ( <div className="editor-container"> <div style={{marginBottom: "10px"}}> <label for="themes">Choose a theme: </label> <select name="theme" onChange={(el) => { setTheme(el.target.value) }}> { themeArray.map( theme => ( <option value={theme}>{theme}</option> )) } </select> </div> <ControlledEditorComponent onBeforeChange={handleChange} value= {value} className="code-mirror-wrapper" options={{ lineWrapping: true, lint: true, mode: language, lineNumbers: true, theme: theme, }} /> </div> ) } export default Editor

스타일을 지정해야 하는 className 은 하나만 있습니다. App.css 로 이동하여 다음 스타일을 추가합니다.

 .editor-container{ padding-top: 0.4%; }

이제 편집기가 준비 App.js 로 돌아가서 사용하겠습니다.

src > App.js

가장 먼저 해야 할 일은 여기에서 Editor.js 구성 요소를 가져오는 것입니다.

 import Editor from './components/Editor';

App.js 에서 HTML, CSS 및 JavaScript 편집기의 내용을 각각 보유할 상태를 선언해 보겠습니다.

 const [html, setHtml] = useState(''); const [css, setCss] = useState(''); const [js, setJs] = useState('');

기억하신다면 이러한 상태를 사용하여 편집자의 콘텐츠를 보유하고 제공해야 합니다.

다음으로 조건부 렌더링에서 HTML, CSS 및 JavaScript에 사용한 단락( p ) 태그를 방금 만든 편집기 구성 요소로 교체하고 편집기의 각 인스턴스에 적절한 소품도 전달합니다. 요소:

 function App() { ... return ( <div className="App"> <p>Welcome to the edior</p> // This is where the tab buttons container is... <div className="editor-container"> { htmlEditorIsOpen ? ( <Editor language="xml" value={html} setEditorState={setHtml} /> ) : cssEditorIsOpen ? ( <Editor language="css" value={css} setEditorState={setCss} /> ) : ( <Editor language="javascript" value={js} setEditorState={setJs} /> ) } </div> </div> ); } export default App;

지금까지 따라왔다면 위의 코드 블록에서 수행한 작업을 이해하게 될 것입니다.

여기 일반 영어로 되어 있습니다. p 태그(자리 표시자로 있던)를 편집기 구성 요소의 인스턴스로 교체했습니다. 그런 다음 해당 상태와 일치하도록 language , valuesetEditorState 소품을 각각 제공했습니다.

우리는 지금까지 왔습니다! 현재 우리 앱의 모습은 다음과 같습니다.

우리 앱이 지금 어떻게 생겼는지
(큰 미리보기)

Iframe 소개

인라인 프레임(iframe)을 사용하여 편집기에 입력한 코드의 결과를 표시합니다.

MDN에 따르면:

HTML 인라인 프레임 요소( <iframe> )는 중첩된 탐색 컨텍스트를 나타내며 다른 HTML 페이지를 현재 페이지에 포함합니다.

React에서 Iframe이 작동하는 방식

Iframe은 일반적으로 일반 HTML과 함께 사용됩니다. React와 함께 Iframe을 사용하는 데 많은 변경이 필요하지 않습니다. 주요 변경 사항은 속성 이름을 카멜케이스로 변환하는 것입니다. 이것의 예는 srcdoc 이 srcdoc 가 되는 srcDoc 입니다.

웹에서 Iframe의 미래

Iframe은 웹 개발에서 계속해서 매우 유용합니다. 확인하고 싶은 것은 Portal입니다. 다니엘 브레인은 다음과 같이 설명합니다.

“포털은 이 조합에 강력하고 새로운 기능 세트를 도입합니다. 이제 iframe과 같은 느낌을 주는 무언가를 구축하는 것이 가능하며 매끄럽게 애니메이션을 적용하고 모핑하고 전체 브라우저 창을 인수할 수 있습니다.”

Portals가 해결하려고 하는 것 중 하나는 URL 표시줄 문제입니다. iframe을 사용할 때 iframe에서 렌더링된 구성 요소는 주소 표시줄에 고유한 URL을 전달하지 않습니다. 따라서 사용 사례에 따라 사용자 경험에 좋지 않을 수 있습니다. Portal은 체크아웃할 가치가 있으며 그렇게 하는 것이 좋습니다. 그러나 이것이 우리 기사의 초점이 아니기 때문에 여기에서 이것에 대해 말할 것입니다.

결과를 저장할 Iframe 만들기

편집기의 결과를 저장할 iframe을 만들어 자습서를 계속 진행해 보겠습니다.

 return ( <div className="App"> // ... <div> <iframe srcDoc={srcDoc} title="output" sandbox="allow-scripts" frameBorder="1" width="100%" height="100%" /> </div> </div> );

여기에서 iframe을 만들고 div 컨테이너 태그에 보관했습니다. iframe에서 필요한 몇 가지 속성을 전달했습니다.

  • srcDoc
    React에서 iframe 속성을 작성하는 방법이기 때문에 srcDoc 속성은 camelcase로 작성되었습니다. iframe을 사용할 때 페이지에 외부 웹 페이지를 포함하거나 지정된 HTML 콘텐츠를 렌더링할 수 있습니다. 외부 페이지를 로드하고 포함하려면 대신 src 속성을 사용합니다. 우리의 경우 외부 페이지를 로드하지 않습니다. 오히려 우리는 결과를 담고 있는 새로운 내부 HTML 문서를 만들고 싶습니다. 이를 위해 srcDoc 속성이 필요합니다. 이 속성은 포함하려는 HTML 문서를 사용합니다(아직 만들지 않았지만 곧 만들 예정입니다).
  • title
    title 속성은 인라인 프레임의 내용을 설명하는 데 사용됩니다.
  • sandbox
    이 속성에는 많은 목적이 있습니다. 우리의 경우 스크립트가 allow-scripts iframe에서 실행되도록 허용하는 데 사용하고 있습니다. 우리는 JavaScript 편집기로 작업하고 있기 때문에 이것은 빨리 유용할 것입니다.
  • frameBorder
    이것은 단지 iframe의 테두리 두께를 정의합니다.
  • widthheight
    이것은 iframe의 너비와 높이를 정의합니다.

이제 이러한 용어가 더 이해가 잘 될 것입니다. 계속해서 srcDoc 용 HTML 템플릿 문서를 보유할 상태를 선언합시다. 위의 코드 블록을 자세히 살펴보면 srcDoc 속성에 값을 전달했음을 알 수 있습니다. srcDoc ={srcDoc} . useState() React 후크를 사용하여 srcDoc 상태를 선언합시다. 이렇게 하려면 App.js 파일에서 다른 상태를 정의한 위치로 이동하여 다음 상태를 추가합니다.

 const [srcDoc, setSrcDoc] = useState(` `);

상태를 생성했으므로 다음으로 해야 할 일은 코드 편집기에 입력할 때마다 상태에 결과를 표시하는 것입니다. 그러나 우리가 원하지 않는 것은 키를 누를 때마다 구성 요소를 다시 렌더링하는 것입니다. 이를 염두에 두고 진행해 보겠습니다.

결과를 표시하도록 Iframe 구성

HTML, CSS 및 JavaScript 각각에 대한 편집기가 변경될 때마다 useEffect() 가 트리거되고 iframe에서 업데이트된 결과가 렌더링되기를 원합니다. App.js 파일에서 이를 수행하기 위해 useEffect() 를 작성해 보겠습니다.

먼저 useEffect() 후크를 가져옵니다.

 import React, { useState, useEffect } from 'react';

다음과 같이 useEffect() 를 작성해 보겠습니다.

 useEffect(() => { const timeOut = setTimeout(() => { setSrcDoc( ` <html> <body>${html}</body> <style>${css}</style> <script>${js}</script> </html> ` ) }, 250); return () => clearTimeout(timeOut) }, [html, css, js])

여기에서 HTML, CSS 및 JavaScript 편집기에 대해 선언한 값 상태가 변경되거나 업데이트될 때마다 항상 실행되는 useEffect() 후크를 작성했습니다.

setTimeout() 을 사용해야 하는 이유는 무엇입니까? 글쎄, 우리가 이것을 없이 작성했다면 편집기에서 키를 한 번 누를 때마다 iframe이 업데이트되고 일반적으로 성능에 좋지 않습니다. 따라서 setTimeout() 을 사용하여 업데이트를 250밀리초 동안 지연하여 사용자가 여전히 입력 중인지 여부를 알 수 있는 충분한 시간을 제공합니다. 즉, 사용자가 키를 누를 때마다 카운트를 다시 시작하므로 iframe은 사용자가 250밀리초 동안 유휴 상태(입력하지 않음)인 경우에만 업데이트됩니다. 이것은 키를 누를 때마다 iframe을 업데이트하지 않아도 되는 멋진 방법입니다.

위에서 우리가 한 다음 작업은 srcDoc 을 새로운 변경 사항으로 업데이트하는 것입니다. 위에서 설명한 srcDoc 구성 요소는 iframe에 지정된 HTML 콘텐츠를 렌더링합니다. 우리 코드에서 HTML 템플릿을 전달했는데, 사용자가 HTML 편집기에 입력한 코드가 포함된 html 상태를 가져와 템플릿의 body 태그 사이에 배치했습니다. 또한 사용자가 CSS 편집기에 입력한 스타일이 포함된 css 상태를 가져와 style 태그 사이에 전달했습니다. 마지막으로 사용자가 JavaScript 편집기에 입력한 JavaScript 코드가 포함된 js 상태를 가져와서 script 태그 사이에 전달했습니다.

setSrcDoc 을 설정할 때 일반 따옴표( ' ' ) 대신 백틱( ` ` )을 사용했습니다. 백틱을 사용하면 위의 코드에서와 같이 해당 상태 값을 전달할 수 있기 때문입니다.

useEffect() 후크의 return 문은 메모리 누수를 방지하기 위해 완료되면 setTimeout() 을 지우는 정리 함수입니다. 문서에는 useEffect 에 대한 자세한 내용이 있습니다.

현재 우리 프로젝트의 모습은 다음과 같습니다.

현재 우리 프로젝트의 모습
(큰 미리보기)

CodeMirror 애드온

CodeMirror 애드온을 사용하면 다른 코드 편집기에서 찾을 수 있는 기능을 더 많이 사용하여 편집기를 향상시킬 수 있습니다. 여는 태그를 입력하면 닫는 태그가 자동으로 추가되는 예와 여는 괄호가 입력되면 자동으로 닫히는 다른 예를 살펴보겠습니다.

가장 먼저 할 일은 이를 위한 애드온을 App.js 파일로 가져오는 것입니다.

 import 'codemirror/addon/edit/closetag'; import 'codemirror/addon/edit/closebrackets';

ControlledEditorComponent 옵션에 전달해 보겠습니다.

 <ControlledEditorComponent ... options={{ ... autoCloseTags: true, autoCloseBrackets: true, }} />

이제 여기 우리가 가진 것이 있습니다:

우리 프로젝트의 모습
(큰 미리보기)

편집기에 이러한 애드온을 많이 추가하여 더 풍부한 기능을 제공할 수 있습니다. 우리는 여기에서 그들 모두를 통과할 수 없었습니다.

이제 이 작업을 완료했으므로 앱의 접근성과 성능을 개선하기 위해 할 수 있는 일에 대해 간략하게 논의해 보겠습니다.

솔루션의 성능 및 접근성

웹 코드 편집기를 보면 몇 가지가 확실히 개선될 수 있습니다.

기능에 중점을 두었기 때문에 디자인에 약간 소홀했을 수 있습니다. 더 나은 접근성을 위해 다음은 이 솔루션을 개선하기 위해 수행할 수 있는 몇 가지 작업입니다.

  1. 현재 열려 있는 편집기의 버튼에 active 클래스를 설정할 수 있습니다. 버튼을 강조 표시하면 사용자에게 현재 작업 중인 편집기를 명확하게 표시하여 접근성이 향상됩니다.
  2. 편집기가 여기에 있는 것보다 더 많은 화면 공간을 차지하기를 원할 수 있습니다. 시도할 수 있는 또 다른 방법은 측면 어딘가에 도킹된 버튼을 클릭하여 iframe 팝업을 만드는 것입니다. 이렇게 하면 편집기에 더 많은 화면 공간이 제공됩니다.
  3. 이러한 종류의 편집기는 모바일 장치에서 빠른 운동을 실행하려는 사람들에게 유용하므로 모바일에 완전히 적용해야 합니다(위의 모바일에 대한 두 가지 요점은 말할 것도 없음).
  4. 현재 로드한 여러 테마 중에서 편집기 구성 요소의 테마를 전환할 수 있지만 페이지의 일반 테마는 동일하게 유지됩니다. 사용자가 전체 레이아웃에 대해 어두운 테마와 밝은 테마를 전환할 수 있도록 할 수 있습니다. 이는 접근성이 좋아 사람들이 밝은 화면을 너무 오래 바라보는 눈의 피로를 덜어준다.
  5. 우리는 주로 외부 문서가 아닌 내부 HTML 문서를 iframe에 로드했기 때문에 iframe의 보안 문제를 살펴보지 않았습니다. 따라서 iframe이 우리의 사용 사례에 적합하기 때문에 이것을 너무 신중하게 고려할 필요가 없습니다.
  6. iframe에서 또 다른 고려 사항은 페이지 로드 시간입니다. iframe에 로드되는 콘텐츠는 일반적으로 제어할 수 없기 때문입니다. 우리 앱에서는 iframe 콘텐츠가 외부에 있지 않기 때문에 문제가 되지 않습니다.

성능과 접근성은 애플리케이션이 사용자에게 얼마나 유용하고 사용 가능한지를 결정하기 때문에 애플리케이션을 구축할 때 많은 고려 가치가 있습니다.

Shedrack은 React 앱의 성능을 개선하고 최적화하는 방법을 잘 설명했습니다. 확인할 가치가 있습니다!

결론

다양한 프로젝트를 통해 작업하는 것은 우리가 광범위한 주제에 대해 배우는 데 도움이 됩니다. 이제 이 기사를 살펴보았으므로 코드 편집기를 더 풍부하게 만들기 위해 더 많은 추가 기능을 실험하고, UI를 개선하고, 위에서 설명한 접근성 및 성능 문제를 수정하여 경험을 확장할 수 있습니다.

  • 이 프로젝트의 전체 코드 기반은 GitHub에서 사용할 수 있습니다.

Codesandbox의 데모는 다음과 같습니다.

링크 및 자료

  • "Google 크롬의 포털: Iframe과 비슷하지만 더 좋거나 더 나쁘다", Daniel Brain
  • "성능 최적화", React 문서
  • "사용 설명서 및 참조 안내서", CodeMirror 문서