Frontend
웹빌더 시스템 Part 1: GrapesJS와 Next.js로 만드는 웹빌더 개발기
Giri여기어때
2025년 6월 18일
원문에서 보기 ↗안녕하세요 여기어때컴퍼니 백오피스웹개발팀 기리입니다.
최근 저희는 펜션 홈페이지를 제작할 수 있도록 지원하는 웹빌더 서비스를 오픈하였습니다. 그리하여 각 제휴점마다 고유한 홈페이지를 빠르게 제작하고 효율적으로 유지보수 할 수 있게 되었습니다. 이 웹빌더를 활용하면 다양한 템플릿 중 각 펜션에 맞는 하나를 선택하고, 몇 가지 상세 내용만을 입력하여 홈페이지를 완성할 수 있습니다. 코드 편집과 시각적 편집 간 전환 기능과 직관적인 인터페이스 덕분에 개발자뿐만 아니라 비개발자도 쉽게 사용할 수 있습니다.
이번 글에서는 웹빌더를 설계하고 구현하는 과정에서 겪은 고민과 해결 방법, 그리고 GrapesJS를 사용하면서 느낀 점들을 정리해보았습니다.
GrapesJS 란?
GrapesJS는 Backbone.js 기반으로 개발된 오픈소스 웹 에디터 프레임워크입니다. 시각적인 인터페이스를 통해 간단한 조작만으로 웹 페이지를 구성할수 있는 WYSIWYG(What You See Is What You Get) 방식의 에디터입니다. 그래서 코드 작성 없이도 다양한 UI 컴포넌트를 드래그 앤 드롭 방식으로 배치하고 스타일을 적용할 수 있습니다. 또한 Block Manager, Style Manager, Trait Manager 등 다양한 기능이 내장되어 있어, 직관적으로 웹사이트를 제작할 수 있습니다.
플러그인을 통한 확장과 커스터마이징이 용이하다는 점에서 많은 분들께 좋은 평가를 받는 것 같습니다. 위와 같은 특성을 모두 고려했을 때, 웹 에디터나 뉴스레터 에디터와 같은 서비스에 활용하기에 매우 적합한 도구라고 생각합니다.
웹빌더를 위한 GrapesJS + Next.js 구조 설계
작업을 시작하기에 앞서, GrapesJS를 어떤 구조로 구성하고 어떤 방식으로 웹빌더에 통합할지에 대해 고민이 있었습니다.
특히 다음과 같은 두 가지 문제가 핵심 골칫거리였습니다.
- GrapesJS 기본 UI만으로는 커스터마이징된 기능을 구현하고 이와 마찬가지로 원하는 디자인을 적용하기 어려웠습니다. ex) 템플릿 정보 표기, Style Manager, UI 변경 등
- 웹빌더에서는 제휴점 정보, 회원 정보 등 다양한 외부 데이터를 연동하고, HTML 및 이미지 파일을 업로드 해야합니다. 그러나 이를 서버 측에서 처리할 수 있는 기능을 구현하기가 어려웠습니다.
앞서 언급한 UI 커스터마이징의 한계와 외부 데이터 연동/처리가 불가한 문제를 해결하기 위해, 저희는 GrapesJS를 Next.js로 래핑하는 방식을 선택했습니다. 특히 이미지 업로드, 제휴점 정보 조회, HTML 파일 저장 등과 같은 추가 기능 및 서버 통신 로직을 처리할 API가 필요했기 때문에, Next.js의 API Routes를 활용해 웹빌더 전용 API를 구성하게 되었습니다.
표현을 조금 바꾸어 보면, CMS(Content Management System, 여기어때 직원이 숙소에 대한 정보를 관리하는 시스템)에서 데이터를 받아오고, AWS S3에 연동(업로드 및 조회)하는 등의 웹빌더 관련 서버 작업들을 효과적으로 처리할 수 있도록 API 구조를 설계하였다고도 설명할 수 있습니다. 위와 같은 작업을 통하여 Next.js는 GrapesJS의 프론트 환경에 백엔드 로직까지 통합할 수 있는 기반이 되어주었고, 서버에서 프론트엔드에 필요한 데이터를 가공하여 전달해줄 수 있게 되었습니다.
주요 기능 및 커스터마이징
주요 기능
아래 이미지를 통해 웹빌더에서 사용하는 주요 기능들이 어떠한 동작을 하는지 설명드리겠습니다.

- Page Manager : 여러 페이지를 생성, 관리, 전환할 수 있게 해주는 기능
- Layer Manager : 에디터에 배치된 요소들의 계층 구조를 보여주고, 관리할 수 있도록 도와주는 기능
- Block Manager : 반복해서 사용할 수 있는 사용자 정의 컴포넌트 등록
- Drag & Drop : 텍스트, 이미지, 버튼 등 다양한 컴포넌트를 드래그를 통하여 배치
- Assets Manager : 이미지, 비디오 등 다양한 미디어 파일을 에디터에 등록하고 관리할 수 있게 해주는 기능
- Style Manager : CSS 속성을 시각적으로 편집 가능
- Trait Manager : 컴포넌트의 속성(ex: href, src 등)을 에디터 패널에서 쉽게 입력하거나 수정 가능
- Export Code (HTML/CSS/JSON) : 완성된 결과물을 코드 형태로 추출
실제 적용 및 커스터마이징
- 컴포넌트 등록 예시 (ex. button)
// block
editor.BlockManager.add('custom-button', {
label: 'Button',
media: '<svg ... />', // 아이콘, 이미지 등
content: {
type: 'custom-button',
},
});
// domComponent
// 게시(배포) 이후 적용 되어야 할 script (필요 시 사용)
const scripts = function ({ variant }) {
this.classList.add(variant);
};
editor.DomComponents.addType('custom-button', {
isComponent: (el) => {
if (el.tagName === 'BUTTON' && el.classList.contains('custom-button')) {
return {
type: 'custom-button',
};
}
},
model: {
defaults: {
tagName: 'button',
// 게시(배포) 이후 적용 스크립트는 아래와 같이 적용
script: scripts,
['script-props']: ['variant'],
classes: ['custom-button'],
components: [
{
type: 'text',
tagName: 'span',
content: '버튼',
},
],
variant: 'primary',
href: '',
// traits Manager 로 값 변경 커스텀
traits: [
...(href, target 등),
{
type: 'select',
label: 'Variant',
name: 'variant',
options: [
{ value: 'primary', name: 'Primary' },
{ value: 'secondary', name: 'Secondary' },
],
changeProp: true,
default: 'primary',
},
],
},
init() {
// traits의 값이 변경 될 경우 해당하는 이벤트 실행
this.listenTo(this, 'change:variant', this.handleVariantChange);
},
handleVariantChange() {
const variant = this.get('variant');
// 기존 클래스 유지하려면 addClass 활용
this.setClass([variant]);
},
...(그 외 이벤트)
},
view: {
// lifeCycle 및 추가적인 event 처리(ex. click)
onRender() {
this.model.handleVariantChange();
},
},
});
- 결과물

React ↔ GrapesJs 간 데이터 공유
저희 웹빌더의 기능 중 하나인 “템플릿 선택 기능”을 예시로 설명드리겠습니다.

위 이미지와 같이 저희 웹빌더는 여러 개의 템플릿 중에서 운영자가 원하는 템플릿을 선택하도록 되어 있습니다. 그런데 선택한 템플릿 정보를 React와 GrapesJS 양쪽에서 모두 활용해야 했지만, 두 환경이 분리되어 있어서 데이터를 공유하기가 쉽지 않았습니다. 이 부분은 템플릿 정보를 전역 객체인 window에 저장해두고, GrapesJS에서 커스텀 컴포넌트를 만들 때 그 정보를 window에서 바로 가져와 활용하는 식으로 처리하였습니다. 이는 React와 GrapesJS 간 데이터 공유의 실질적인 대안이 되었으며, 실제 컴포넌트 렌더링 과정에서도 문제 없이 적용할 수 있었습니다.
배포 및 운영
5000여 개의 펜션 홈페이지가 동시에 운영되어야 하기 때문에, 일반적인 웹 서버로 서비스를 제공할 경우 과도한 비용이 발생할 수밖에 없습니다. 또한 웹빌더로 제작된 페이지는 HTML과 같은 정적 리소스가 주를 이루므로, 이러한 웹 페이지는 S3를 통해 제공되도록 구성하여 비용을 절감했습니다. 예약과 같은 동적인 기능이 필요한 경우에는 별도의 예약 시스템으로 연결 되는 구조로 설계하여 정적 콘텐츠와 동적 서비스가 효율적으로 분리되도록 했습니다.
아래 이미지는 배포 과정을 간단하게 나타낸 다이어그램입니다.

배포 방식
GrapesJS에서는 현재 작업 중인 페이지의 HTML, CSS 소스 코드 뿐 아니라, 프로젝트 전체 설정 파일들을 손쉽게 추출할 수 있습니다. editor 인스턴스에서 제공하는 getHtml(), getCss(), getProjectData() 함수를 활용하면 웹빌더에서 운영자가 페이지 제작을 마친 뒤 “배포(게시)” 버튼을 누를 때, 해당 작업 결과물을 각각 HTML, CSS, JSON 데이터 형태로 받아올 수 있습니다. 특히 getProjectData()를 통해 스타일, 페이지 구성, 컴포넌트 구조 등 모든 프로젝트 데이터를 JSON 파일로 저장할 수 있어, 복잡한 데이터도 구조화하여 관리하기 편리합니다.
이렇게 생성된 HTML과 JSON 결과물은 Next.js의 API를 통해 고유 도메인 키(${domain})와 이에 해당하는 리소스 파일들을 AWS CLI를 통해 S3 에 업로드하도록 구성했습니다. 최종적으로는 각 제휴점의 도메인 구조에 맞춰 ${URL}/${domain} 경로에 저장되며, 정적 웹사이트 형태로 배포됩니다.
- HTML 생성 예시
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<!-- 필요시 추가 meta, favicon 등 -->
<script type="text/javascript">
// 템플릿 정보, 기타 데이터 삽입
window.template = "${template}";
</script>
<link rel="stylesheet" href=${CSS_URL}>
<style>${editor.getCss()}</style>
</head>
${editor.getHtml()}
</html>
위와 같은 정적 파일 배포 방식을 통해 별도의 서버 환경 없이도 웹사이트를 빠르게 배포할 수 있었고, 웹 서버나 별도의 인프라를 유지할 필요가 없어 서버 비용을 크게 절감할 수 있었습니다. 운영 중에는 에디터에서 수정 후 게시만하면 최신 상태가 바로 서비스에 반영되기 때문에 유지보수도 훨씬 간편해졌습니다.
운영 방식
웹 빌더에 접속하면 먼저 전달받은 domain 주소를 확인해 S3에서 해당 도메인 경로에 저장된 JSON 파일(프로젝트 데이터)를 조회합니다. GrapesJS의 storageManager 기능을 활용하여 해당 경로에 파일이 존재한다면 저장된 페이지 구성과 스타일 등 모든 데이터를 웹 빌더 에디터로 불러와 기존 사이트를 수정하거나 새 콘텐츠를 추가하여 작업 할 수 있습니다. 반대로 S3에 해당 JSON 파일이 없다면 웹 빌더에서는 신규 템플릿 생성 화면을 띄워 운영자가 원하는 템플릿으로 새로운 펜션 사이트를 처음부터 제작할 수 있도록 안내합니다. 이러한 구조 덕분에 신규 사이트 제작과 기존 사이트 수정이 모두 직관적이고 간편하게 처리됩니다. 운영자는 복잡한 개발 과정 없이도 웹빌더만으로 사이트를 관리할 수 있어, 운영 효율성과 유지보수 편의성 측면에서 큰 이점을 얻을 수 있습니다.
- storageManager 예시
storageManager: {
type: 'remote',
autosave: false, // 자동저장 비활성화
autoload: true, // 웹 빌더 접근 시 s3에 해당 도메인이 존재하는지 확인
options: {
remote: {
urlLoad: `${API_URL}?domain=${domain}&projectData=${PROJECT_DATA}`,
onLoad: (data) => data
},
},
},
위와 같은 storageManager의 remote 설정을 통하여 웹 빌더에 접속할 때마다 도메인별로 S3에서 해당 프로젝트 데이터를 자동으로 불러와 효율적으로 사이트를 관리할 수 있도록 구성하였습니다.
마치며
GrapesJS는 현재(2025년 6월 기준) 0.22.8 버전으로, 아직 1.0 릴리즈가 되지 않은 상태입니다. 그럼에도 불구하고 사용자의 니즈에 따라 자유로운 커스터마이징이 가능하다는 큰 장점이 있기 때문에 저희는 자사의 서비스에 적용하기로 결정하였습니다.
그러나 아쉬운 부분들도 있었습니다. 다른 상용 도구와는 다르게 버전 관리나 롤백 기능이 제공되지 않았고, 배포 이후 섹션이 변경되면 기존 섹션을 삭제하고 다시 구성해야해서 무척 번거로웠습니다. 정식으로 1.0 버전이 배포되지 않은 점을 감안하면 오픈소스 커뮤니티가 활발하여 실사용 예제가 많은 편 입니다. 그렇지만 어떤 기능은 문서화가 미진하여 버그나 이슈를 마주했을 때 해결하기까지 조금 난감했습니다.
따라서 GrapesJS를 서비스에 도입하려는 분들은 공식 깃허브와 문서, 커뮤니티 이슈 등을 함께 참고하시길 권장해 드립니다.
이번 글이 GrapesJS 도입을 고민하시는 분들께 조금이나마 도움이 되었기를 바랍니다.