기본 개념
사용자
웹사이트
•
에디터를 통해서 제작된 사이트이며, 방문자(및 구매자)가 사이트를 방문하여 컨텐츠를 보거나 상품을 구매해요.
•
웹사이트는 쇼핑 기능이 없는 ‘홈페이지’와 쇼핑 기능을 포함하는 ‘쇼핑몰’ 모두를 포함해요.
에디터
•
웹사이트를 제작하기 위한 툴이에요.
•
사용자는 에디터를 활용하여 사이트와 페이지를 구성 및 디자인하고 발행(공개)해요.
•
웹사이트를 제작하는 과정에서 프리뷰(Preview)를 통해 발행하기 전에 미리 구성과 디자인을 확인할 수 있어요.
블록
•
블록은 하나의 행(Row)를 모두 차지하게 삽입될 수도 있고, 하나의 행 내의 열(Column)에 삽입될 수도 있어요.
•
블록 메이커
•
블록을 제작하는 툴이에요.
세팅 빌더
•
•
특정 블록을 선택하여 나오는 코드 수정 창에서 [설정 패널] 탭을 눌러서 사용할 수 있어요.
“안녕 블록 메이커!” — 기본 문법 소개
<style>
h1 {
color: red;
font-size: 24px;
}
</style>
<template>
<h1>안녕 블록 메이커!</h1>
<ul>
<li>제목 색상: red</li>
<li>제목 사이즈: 24px</li>
</ul>
<button>설명 보기</button>
</template>
<script>
const container = bm.container;
const context = bm.context;
function clicked(e) {
e.preventDefault();
if (e.target.matches('button')) {
alert(`제목 색상은 red이고 사이즈는 24px 이에요!`);
}
}
container.addEventListener('click', clicked, true);
</script>
HTML
복사
블록을 만들기 위해서는 HTML/CSS/JavaScript에 대한 기본적인 지식이 필요해요!
간단한 블록을 만들기 위해서는 HTML/CSS 정도로도 구현이 가능하지만, 동작 복잡도가 있는 블록을 만들기 위해서는 기본적인 HTML/CSS/JavaScript 지식이 필요해요.
예시 코드는 빨간색의 “안녕 블록 메이커” 텍스트를 포함하는 <h1> 태그를 그리고 <button>을 클릭 시 설명을 출력하는 블록의 예시에요.
기본 블록 문법 설명
•
하나의 블록은 하나의 코드 파일로 구성돼요.
•
하나의 블록은 크게 3개의 최상위 HTML 태그인 <style> , <template> , <script> 태그로 구성될 수 있고, 태그별로 하나만 최상위 태그로 가질 수 있어요.
◦
<style>: 구현하려는 블록의 Style(CSS)을 입력할 수 있어요.
◦
<template>: 구현하려는 블록의 기본적인 템플릿(HTML)을 입력할 수 있어요.
◦
<script>: 구현하려는 블록의 JavaScript 코드를 입력할 수 있어요.
“{{property.myHelloPhrase}} 블록 메이커!“ — 사용자가 설정할 수 있는 블록 만들기
{
// 웹사이트에 삽입된 각 블록의 고유한 ID 에요.
"id": "...",
// 세팅 빌더를 통해 정의된 블록 설정이 property 내에 포함되어 있어요.
"property": {
"myHelloPhrase": "🖐🏻",
"myColor": "blue",
"myFontSize": 36,
},
...
}
JavaScript
복사
세팅 빌더를 통해 정의된 설정들(property)과 각 설정의 값들을 포함한 컨텍스트(Context) 객체 예시
<style>
h1 {
color: {{property.myColor}};
font-size: {{property.myFontSize}}px;
}
</style>
<template>
<h1>{{property.myHelloPhrase}} 블록 메이커!</h1>
<ul>
<li>제목 색상: {{property.myColor}}</li>
<li>제목 사이즈: {{property.myFontSize}}px</li>
</ul>
<button>설명 보기</button>
</template>
<script>
const container = bm.container;
const context = bm.context;
function clicked(e) {
e.preventDefault();
if (e.target.matches('button')) {
const myColor = context.property.myColor;
const myFontSize = context.property.myFontSize;
alert(`제목 색상은 ${myColor}이고 사이즈는 ${myFontSize}px 이에요!`);
}
}
container.addEventListener('click', clicked, true);
</script>
HTML
복사
이제 사용자는 <h1> 의 색상과 글자 크기를 설정할 수 있고, “안녕”이라는 문구를 고쳐 쓸 수 있어요.
갑자기 주어진 상황!
•
블록을 제작하는 중에 블록의 사용자가 <h1> 태그 내 “색상, 글자 크기나 컨텐츠 일부를 직접 수정할 수 있도록 지원”해달라는 요청이 왔어요.
•
사용자는 아쉽게도 코드를 직접 수정하지 못하는 분이어서, 컨텐츠와 디자인을 바꾸고 싶을 때마다 항상 코드 수정을 위해 연락이 와요. 
사용자가 설정할 수 있는 블록
1.
2.
컨텍스트(Context)
•
컨텍스트(Context)는 블록의 UI를 그리거나 블록 동작에 참고 및 활용할 수 있는 데이터를 담고 있는 일종의 JSON 객체(Object)에요.
•
컨텍스트 객체 내에는 id 필드가 존재하고, 동일한 블록이라도 여러 위치에 삽입되면 각각 고유한 ID를 가져요.
•
블록을 만드는 과정에서 세팅 빌더를 통해서 정의한 세팅(Setting)과 설정 값(디폴트 값 혹은 사용자가 설정한 값)들이 컨텍스트 객체 내 property 라는 객체 내에 키와 값이 전달돼요.
•
세팅 빌더와 컨텍스트 객체 내 property 를 활용하여 블록을 제작하면, 한 번 제작한 블록에서 코드 수정 없이도 사용자가 UI나 동작을 에디터에서 설정할 수 있도록 블록을 제작할 수 있어요.
•
컨텍스트 객체는 <style> , <template> , <script> 모든 태그 내에서 접근해 활용할 수 있어요.
◦
◦
◦
<script>: bm.context를 통해서 컨텍스트 객체에 접근할 수 있어요.
•
컨텍스트 객체는 id, property 외의 다른 상황에도 사용돼요.
◦
Handlebars.js 문법이 어려워 보이는데 혹시 다 알아야 할까요?
사용자가 설정한 값을 HTML/CSS에서 단순히 읽어서 사용하시는 경우에는 모든 문법을 자세히 알 필요 없이 property 값에 접근하기 위한 {{property.*}} 문법만 기억해도 괜찮아요!
<style> 태그
<style>
h1 {
color: red;
/* 사이트 디자인에 설정된 CSS 변수 활용 */
font-family: var(--font-family-heading);
font-weight: var(--font-weight-heading);
/* Handlebars.js 문법으로 컨텍스트 내 property 값 활용 */
margin-bottom: {{property.titleSpacing}}px;
}
p {
/* 사이트 디자인에 설정된 CSS 변수 활용 */
font-family: var(--font-family-body);
font-weight: var(--font-weight-body);
}
</style>
HTML
복사
CSS 및 Handlebars.js 로 작성된 스타일 예시
var(--font-family-heading) /* 제목 폰트 */
var(--font-weight-heading) /* 제목 폰트 굵기 */
var(--font-family-body) /* 본문 폰트 */
var(--font-weight-body) /* 본문 폰트 굵기 */
CSS
복사
기본 제공되는 글로벌 CSS 변수
•
블록에 적용될 CSS 코드를 입력할 수 있어요.
•
입력된 CSS 코드는 웹사이트에 삽입된 각 블록 별로 격리되어 적용돼요.
•
일관된 블록 디자인을 위해서 일부 CSS 변수가 글로벌 변수로 제공돼요.
•
•
•
기본 HTML 디자인이 이상해요!
블록 메이커를 통해 제작되는 블록들은 다양한 기기에서 여러 테마에 적용되어도 최대한 동일한 블록 UI를 제공할 수 있도록 브라우저에서 기본 적용되는 디폴트 CSS 스타일이 대부분 초기화(Reset)되어 있어요.
CSS 셀렉터에 Handlebars.js 문법을 사용하지 말아주세요.
현재 블록 메이커 시스템 동작 방식에 의해 블록이 정상적으로 동작하지 않을 수 있어, CSS 셀렉터에서는 Handlebars.js 문법 사용을 지양주세요.
<!-- Bad -->
<style>
.{{property.myClassName}} {
color: red;
}
</style>
HTML
복사
<template> 태그
<template>
<h1>Hello Block Maker!</h1>
<!-- Handlebars.js 문법으로 컨텍스트 객체 내 property 값에 접근 -->
<p>{{property.myDescription}}</p>
</template>
HTML
복사
HTML 및 Handlebars.js 로 작성된 템플릿 예시
<!-- 블록 컨테이너(Block Container) 영역 -->
<div>
<!-- 템플릿의 HTML은 블록 컨테이너 안에 그려져요. -->
<h1>Hello Block Maker!</h1>
<p>블록 메이커에 대한 설명이에요.</p>
</div>
HTML
복사
작성된 템플릿이 웹사이트에 그려진 예시
•
블록의 기본이 되는 HTML 템플릿을 입력할 수 있어요.
•
•
•
•
블록 컨테이너
•
입력한 블록 템플릿을 감싸고 내부에 실제로 그리게 되는 HTML DOM 컨테이너에요.
•
블록 컨테이너는 하나의 블록당 1개만 존재하고, 작성한 HTML 템플릿의 부모(Parent) 컨테이너로 볼 수 있어요.
•
<script> 태그
<script>
const container = bm.container;
const context = bm.context;
container.addEventListener('click', e => {
e.preventDefault();
if (e.target.matches('button')) {
// <button> 태그를 클릭시 사용자가 에디터에 설정한 메세지를 출력
alert(context.property.myAlertMessage);
// 고객 로그아웃 처리
bm.do('logout');
}
}, true);
</script>
HTML
복사
블록에서 접근 가능한 bm 객체와 스크립트 예시
•
<script> 태그 내에 JavaScript 코드를 작성해서 블록의 동작을 제어할 수 있어요.
•
작성한 <script> 코드는 <template> 과 <style> 이 처음 그려진 뒤에 실행돼요.
•
•
<script> 태그를 사용하는 블록은 코드에 따라서 에디터 내 프리뷰(Preview) 지원을 별도로 처리해줘야할 수 있어요. 더 자세한 내용은 <script> 태그에서 프리뷰 지원하기 섹션을 참고해 주세요.
<script> 태그 사용 시 주의 사항
외부 라이브러리(jQuery 등)를 사용하는 경우 공통 코드를 설정하여 <script> 태그 내에서 활용할 수 있지만, 꼭 필요한 경우가 아니라면 사이트의 속도 저하의 원인이 될 수 있으니 순수 JavaScript(Vanilla JavaScript) 사용을 지향해요.
<script> 태그 사용 — Bad vs. Good
<!-- Bad -->
<script>
bm.container.innerHTML = '<h1>안녕하세요!</h1>';
// 블록 컨테이너에 그려진 HTML 요소에 직접 DOM 이벤트를 바인딩
const h1 = bm.container.querySelector('h1');
h1.addEventListener('click', e => {
e.preventDefault();
alert('<h1> 태그가 클릭되었어요.');
});
// 1초 뒤 <h1> 태그를 새로 생성
setTimeout(() => {
bm.container.innerHTML = '<h1>😢클릭해도 창이 안떠요...</h1>';
}, 1000);
</script>
<!-- Good -->
<script>
bm.container.innerHTML = '<h1>안녕하세요!</h1>';
// 블록 컨테이너에 DOM 이벤트를 위임하고 분기
bm.container.addEventListener('click', e => {
e.preventDefault();
if (e.target.matches('h1')) {
alert('<h1> 태그가 클릭되었어요.');
}
}, true);
// 1초 뒤 <h1> 태그를 새로 생성
setTimeout(() => {
bm.container.innerHTML = '<h1>☺️클릭하면 창이 떠요!</h1>';
}, 1000);
</script>
HTML
복사
•
블록 컨테이너 내부의 HTML 구조나 내용물이 바뀌는 상황에도 DOM 이벤트를 다시 걸어줄 필요가 없어요.
•
제작하는 블록에서 <script> 태그를 활용해 UI를 동적으로 수정하는 경우에도 상위 컨테이너인 블록 컨테이너를 활용하는 방식이 사이드 이펙트가 덜 발생해요.
•
에디터 내 프리뷰(Preview) 지원 시 자연스러운 동작에 도움이 돼요.
<!-- Bad -->
<script>
bm.onContextChange = () => {
bm.context.counter++;
// bm.onContextChange 내에서 호출
bm.apply(); // bm.apply()에 의해 onContextChange()가 발생
};
</script>
<!-- Good -->
<script>
bm.container.addEventListener('click', () => {
bm.context.counter++;
// bm.onContextChange() 외 다른 위치에서 사용
bm.apply();
});
bm.onContextChange = () => {
console.log(bm.context.counter);
};
</script>
HTML
복사
<data> 태그
<!-- <data> 태그를 활용해서 블록 밖의 데이터를 요청 -->
<data value="$page" />
<data value="$cart" />
<data value="$customer"/>
<style>
...
</style>
<template>
{{#if (eq page.name "HOME")}}
<h1>방문을 환영합니다!</h1>
{{/if}}
{{#if customer}}
<h2>안녕하세요 {{customer.name.full}}님!</h2>
{{else}}
<h2>안녕하세요 방문자님!</h2>
{{/if}}
<p>장바구니에 {{cart.count}}개의 상품을 담으셨어요.</p>
{{#if customer}}
<button>로그아웃</button>
{{/if}}
</template>
<script>
const container = bm.container;
const context = bm.context;
bm.onContextChange = () => {
// <data> 태그를 통해 가져온 데이터나 상태가 바뀌면 실행돼요.
console.log(context.page);
console.log(context.customer);
console.log(context.cart);
};
function clicked(e) {
e.preventDefault();
if (e.target.matches('button')) {
// 팁: 로그아웃 버튼을 누르면 로그아웃 처리!
bm.do('logout');
}
}
container.addEventListener('click', clicked, true);
</script>
HTML
복사
<data> 태그를 통해 고객(Customer) 및 장바구니(Cart) 정보를 활용하는 예시
•
<data> 태그는 사이트에서 자동으로 제공되는 값을 블록에서 사용할 수 있게 해줘요.
•
<data> 태그는 <style>, <template> 및 <script> 태그와 다르게 꼭 블록에 선언될 필요는 없어요.
◦
블록에서 필요한 데이터를 가져오는 경우에 <data> 태그를 선언해서 사용할 수 있어요.
•
현재 <data> 태그의 value 로 사용할 수 있는 값은 다음과 같아요:
•
특정 데이터는 상태가 바뀌면 자동으로 반영돼요!
•
페이지가 이동되면 page 값이 바뀌어요.
•
고객이 로그인하거나 로그아웃하면 customer 값이 바뀌어요.
•
장바구니에 상품을 담거나 빼면 cart 값이 바뀌어요.
•
이런 변화가 생기면 context 값도 자동으로 바뀌고, bm.onContextChange 함수가 실행돼요.
$page : 현재 페이지 정보
{
"name": "HOME", // 페이지 구분자
"path": "/" // 페이지 패스(Path)
}
JSON
복사
•
웹사이트 주요 페이지 정보를 함께 참고해주세요.
$customer : 로그인한 고객 정보
{ // 로그인 고객 정보 (로그인하지 않은 경우 `null`)
"id": "...", // 로그인 고객의 고유 ID
"name": {
"full": "김블록" // 로그인 고객의 성명
}
}
JSON
복사
$cart : 장바구니 정보
{
"count": 1, // 담은 상품 수
"total": {
"items": {
"price": {
"original": 40000, // 담은 상품 할인 전 총액
"sale": 32000 // 담은 상품 할인 후 총액
},
"discounted": 8000 // 담은 상품 할인 총액
}
}
}
JSON
복사
$legal : 쇼핑몰 법적 필수 정보
{
// 상호/대표자 성명
"businessName": "식스샵",
"representativeName": "김식스",
// 사업자 등록번호/통신판매업 신고번호
"businessRegistrationNumber": "000-11-22222",
"mailOrderSalesRegistrationNumber": "2025-서울강남-00000",
// 전화번호/전자우편 주소(이메일 주소)
"contactPhone": "02-1111-2222",
"contactEmail": "help@sixshop.com",
// 영업소 소재지 주소
"businessAddress": "서울특별시 강남구 테헤란로 000",
"businessAddressDetail": "식스타워 6층",
// 개인정보관리책임자
"privacyOfficerName": "김식스",
// 호스팅 제공자
"hostingProvider": "식스샵",
// 에스크로 제공자
"escrowProvider": { // 미사용 시 null
"logoUrl": "https://storefront.sellerhub.com/assets/toss-payments-logo-black.png",
"infoUrl": "https://consumer.tosspayments.com/escrow/detail?mertid="
},
// 이용약관/개인정보처리방침
"termsOfServiceUrl": "/terms/policy",
"privacyPolicyUrl": "/terms/privacy",
// 사업자 정보 확인 링크
"businessInfoUrl": "https://www.ftc.go.kr/bizCommPop.do?wrkr_no=0001122222"
}
JSON
복사
$social : 연락 채널 및 소셜 미디어 링크
{
"phone": "02-1111-2222",
"email": "help@sixshop.com",
"instagram": "https://www.instagram.com/sixshop_official",
"kakao": "http://pf.kakao.com/_xiGfwC",
"blog": "https://blog.naver.com/6solution",
"youtube": "https://www.youtube.com/channel/UCPsgg6_D_4GLO18M5_TluGg",
"facebook": "https://www.facebook.com/sixshop.page"
}
JSON
복사
에디터 내 프리뷰(Preview) 지원
<style>
h1 {
/* 사용자가 블록의 설정 값(property.myColor)을 에디터에서 변경하면 프리뷰에 자동 반영돼요. */
color: {{property.myColor}};
}
</style>
<template>
<!-- 사용자가 블록의 설정 값(property.myHelloPhrase)을 에디터에서 변경하면 프리뷰에 자동 반영돼요. -->
<h1>{{property.myHelloPhrase}} 블록 메이커!</h1>
<p></p>
</template>
<script>
const container = bm.container;
const context = bm.context;
const p = container.querySelector('p');
// 처음 한 번은 반영되지만, 사용자가 블록의 설정 값(property.myContents)을 에디터에서 변경해도 자동 반영되지 않아요.
p.innerHTML = context.property.myContents;
</script>
HTML
복사
•
사용자가 설정할 수 있는 블록은 사용자가 에디터에서 블록을 삽입한 뒤, 블록이 정의한 설정 값(컨텍스트(Context) 객체의 property)들을 패널을 통해서 설정하는 과정을 거치게 돼요.
•
사용자가 에디터에서 블록을 설정하게 되면, 사용자는 설정한 값들이 블록에 바로 반영되어 프리뷰에 나오기를 기대해요.
에디터 프리뷰 내 태그별 특징
•
<template> 태그와 <style> 태그에서 사용 중인 컨텍스트(Context) 객체의 property 값들은 에디터에서 자동으로 변경을 감지하고 반영하여 에디터 프리뷰에서 바로 확인할 수 있어요.
•
<script> 태그도 <template> , <style> 태그와 동일하게 제일 처음에는 <script> 태그가 동작하면서 프리뷰에 반영돼요.
•
다만 <script> 태그는 <template> , <style> 태그와는 다르게, 별도의 문법 처리를 해주지 않으면 사용자가 블록의 설정값을 변경하였을 때 자동으로 프리뷰에 반영하지 않아요.
•
...
<script>
const container = bm.container;
const context = bm.context;
const p = container.querySelector('p');
// 최초 실행시 설정 값(property.myContents)을 반영
p.innerHTML = context.property.myContents;
// 이후 사용자가 블록의 설정 값(property.myContents)을 변경한 경우 반영 처리
bm.onContextChange = () => {
const p = container.querySelector('p');
p.innerHTML = context.property.myContents;
};
</script>
HTML
복사
<script> 태그에서 프리뷰 지원하기
•
사용자가 블록이 정의한 설정 값(컨텍스트(Context) 객체의 property)들을 설정하여 property 값에 변화가 생기면, 블록 메이커는 컨텍스트(Context) 객체에 변화가 생겼다고 판단하고 bm.onContextChange에 정의한 함수를 실행해요.
•
bm.onContextChange에 콜백(Callback) 함수를 정의하고, property 값의 변화가 발생한 경우 블록의 UI와 동작에 반영하는 코드를 작성하면 프리뷰에서도 잘 반영되는 블록을 제작할 수 있어요.
세팅(Setting)
•
세팅은 블록 제작자가 정의한 블록의 설정 항목이에요.
•
블록을 사용하는 사용자는 각 세팅의 값을 에디터에서 직접 설정할 수 있어요.
•
같은 블록이라도 세팅 값에 따라 다양한 콘텐츠와 스타일, 설정을 지원하도록 활용할 수 있어요.
TEXT
<template>
<h1>{{property.myText}}</h1>
</template>
HTML
복사
"myText": "여름 세일 기획전"
JavaScript
복사
•
한 줄의 텍스트를 입력받을 수 있어요.
•
제목, 버튼 레이블, 짧은 안내 문구처럼 간단한 텍스트를 입력할 때 사용해요.
TEXTAREA
<template>
<p>{{property.myTextarea}}</p>
</template>
HTML
복사
"myTextarea": "연말까지 30% 할인"
JavaScript
복사
•
여러 줄의 텍스트를 입력할 수 있어요.
•
설명 문구, 안내 메시지, 긴 텍스트 컨텐츠처럼 줄바꿈이 필요한 텍스트 입력에 적합해요.
RICH_TEXT
<template>
{{{property.myRichText}}}
</template>
HTML
복사
"myRichText": "<p style=\"text-align: center;\">HTML을 포함한 "리치 텍스트"</p>"
JavaScript
복사
•
다양한 서식이 적용된 텍스트를 입력할 수 있어요.
•
에디터에서 직접 스타일을 지정할 수 있어 본문이나 설명처럼 풍부한 표현이 필요한 텍스트에 적합해요.
•
템플릿에서 사용 시 HTML 이스케이프를 방지하기 위해 {{{...}}} (중괄호 3개) 문법을 사용해야 해요.
RANGE
<style>
.grid {
grid-template-columns: repeat({{property.myRange}}, 1fr);
}
</style>
HTML
복사
"myRange": 6
JavaScript
복사
•
슬라이더 형태로 숫자 범위 내에서 값을 설정할 수 있어요.
•
간격, 투명도, 컬럼 개수처럼 시각적으로 수치를 조절해야 할 때 직관적이에요.
CHECKBOX
<template>
{{#if property.myCheckbox}}
<p>체크되었어요</p>
{{/if}}
</template>
HTML
복사
"myCheckbox": true
JavaScript
복사
•
체크박스 형태로 설정 및 사용 여부를 선택할 수 있어요.
•
특정 기능의 활성화 여부나 옵션 표시 여부처럼 참/거짓을 선택할 때 사용해요.
RADIO
<style>
.container {
text-align: {{property.myRadio}};
}
</style>
HTML
복사
"myRadio": "center"
JavaScript
복사
•
여러 옵션 중 하나를 선택할 수 있어요.
•
정렬 방식, 레이아웃 배치, 크기 선택처럼 미리 정해진 선택지 중 하나를 고를 때 사용해요.
SELECT
<template>
<p>선택한 언어: {{property.mySelect}}</p>
</template>
HTML
복사
"mySelect": "한국어"
JavaScript
복사
•
드롭다운 형태로 여러 옵션 중 하나를 선택할 수 있어요.
•
선택지가 많거나 공간을 절약하고 싶을 때 RADIO 대신 사용하기 좋아요.
COLOR_PICKER
<style>
.banner {
background-color: {{property.myColor}};
}
</style>
HTML
복사
"myColor": "#FF660000"
JavaScript
복사
•
색상을 선택할 수 있어요.
•
색상 선택 UI를 통해 원하는 색을 고를 수 있고, 투명도도 함께 조절할 수 있어요.
•
배경색, 텍스트 색상, 강조 포인트처럼 커스텀 색상이 필요한 곳에 사용해요.
COLOR_SCHEME
<style>
div {
background-color: var(--color-background-100);
border: 1px solid var(--color-border-100);
}
h3 {
color: var(--color-accent-100);
}
p {
color: var(--color-text-100);
}
</style>
<template>
<div>
<h3>특별 혜택</h3>
<p>첫 구매 시 10% 할인 쿠폰을 드려요</p>
</div>
</template>
HTML
복사
/* 배경 색상 (100, 80, 60, 40, 0) */
--color-background-100
/* 텍스트 색상 (100, 90, 80, ..., 10) */
--color-text-100
/* 강조 색상 (100, 90, 80, ..., 10) */
--color-accent-100
/* 테두리 색상 */
--color-border-100
CSS
복사
•
미리 구성된 색상 조합을 선택할 수 있어요.
•
배경, 테두리, 텍스트, 강조 색상을 일관된 조합으로 설정할 때 활용돼요.
•
다른 세팅 타입과 다르게 property로 접근하는 대신, 지원되는 CSS 변수로만 스타일링에 사용할 수 있어요.
•
변수 뒤의 숫자(예: 100, 80)는 투명도를 의미하며, 0에 가까울수록 투명해져요.
IMAGE_PICKER
<template>
<img src="{{property.myImage}}" alt="이미지">
</template>
HTML
복사
"myImage": "<https://example.org/image.png>"
JavaScript
복사
•
이미지를 업로드할 수 있어요.
•
업로드된 이미지의 URL이 값으로 저장되어 배너, 썸네일, 아이콘 등에 사용할 수 있어요.
VIDEO_PICKER
<template>
<video src="{{property.myVideo}}" controls></video>
</template>
HTML
복사
"myVideo": "<https://example.org/video.mp4>"
JavaScript
복사
•
비디오를 업로드할 수 있어요.
•
업로드된 비디오의 URL이 값으로 저장되어 소개 영상, 시연 콘텐츠 등에 사용할 수 있어요.
LIST
<template>
{{#each property.myList}}
<div>{{myTitle}}</div>
{{/each}}
</template>
HTML
복사
"myList": [
{ "myTitle": "첫 번째 항목", ... },
{ "myTitle": "두 번째 항목", ... }
]
JavaScript
복사
•
목록형 항목을 설정할 수 있어요.
•
각 항목은 내부에 정의된 세팅들로 구성되며, FAQ, 카드형 콘텐츠, 슬라이드처럼 동일한 구조가 반복되는 컨텐츠에 적합해요.
LINK
<template>
<a href="{{property.myLink.value}}">{{property.myLink.label}}</a>
</template>
HTML
복사
"myLink": {
"id": null, // 리소스 ID
"type": "url", // 링크 타입 (예: "url", "product", ...)
"label": "라벨", // 표시 텍스트
"value": "<https://example.org>" // 실제 URL
}
JavaScript
복사
•
내부 페이지나 외부 URL을 연결할 수 있어요.
•
상품, 카테고리, 게시글 같은 내부 리소스나 외부 링크를 선택할 수 있어 버튼, 배너 등에 활용돼요.
MENU
<template>
{{#each property.menus}}
<a href="{{link.value}}">{{name}}</a>
{{#each childMenus}}
<a href="{{link.value}}">- {{name}}</a>
{{#each childMenus}}
<a href="{{link.value}}">-- {{name}}</a>
{{/each}}
{{/each}}
{{/each}}
</template>
HTML
복사
"menus": [
{
"id": "1", // 메뉴 ID
"name": "링크", // 메뉴 이름
"link": {
"id": null,
"type": "url",
"label": "라벨",
"value": "<https://example.org>"
},
"shouldOpenInNewTab": false, // 새 탭에서 열기 여부
"childMenus": [...] // 하위 메뉴 (3단계까지 중첩 가능)
}
]
JavaScript
복사
•
링크 메뉴를 설정할 수 있어요.
•
최대 3단계까지 중첩된 구조를 지원해요.
•
네비게이션 메뉴, 푸터 메뉴, 사이트맵처럼 구조화된 링크 목록을 만들 때 적합해요.
PRODUCT
<template>
{{#each property.products.data}}
<div>{{name}} - {{price.sale}}</div>
{{/each}}
<button>더 보기</button>
</template>
<script>
bm.container.addEventListener('click', (event) => {
event.preventDefault();
if (event.target.matches('button')) {
const currentPage = bm.config('property:products.page');
bm.config('property:products.page', currentPage + 1);
bm.apply();
}
});
</script>
HTML
복사
"products": {
"data": [
{
"id": "...", // 카탈로그 ID
"product": { "id": "..." }, // 상품 ID
"option": { "id": "..." }, // 옵션 ID (옵션: 사이즈, 색상 등)
"variation": { "id": "..." }, // 옵션 선택지 ID (옵션 선택지: M, 빨강 등)
"variant": { "id": "..." }, // 품목 ID (옵션 조합: M/빨강 등)
"availability": "in-stock", // 재고 상태 (예: "in-stock", "out-of-stock", "temporarily-out-of-stock"). "in-stock"이 아니면 품절 상태
"isQuickAddable": false, // 바로 담기 가능 여부
"slug": "...", // 상품 슬러그 (상품 URL에 사용)
"name": "상품 이름", // 상품 이름
"summary": "요약 설명", // 상품 요약
"labels": [ // 상품 라벨 목록
{
"id": "...", // 라벨 ID
"name": "인기 상품" // 라벨 이름
}
],
"images": [ // 상품 이미지 목록 (현재는 항상 1개)
{ "url": "<https://example.org/image.png>" }
],
"price": { // 가격 정보
"original": 50000, // 정가
"sale": 42500 // 판매가
},
"discounts": [ // 할인 목록
{
"name": null, // 할인명 (있는 경우)
"type": "percentage", // 할인 타입 (예: "percentage", "fixed")
"discounted": 7500, // 할인 금액
"value": 15, // 할인값 (예: 15% 또는 7500원)
"discountType": "default" // 할인 분류
}
],
"shipping": { // 배송 정보
"methods": [ // 배송 방식 목록
{
"id": "1", // 배송 방식 ID
"name": "택배" // 배송 방식 이름
}
]
},
"review": { // 리뷰 정보
"publicReviewAverageRating": 4.5, // 평균 평점
"publicReviewCount": 10 // 리뷰 개수
}
}
]
}
JavaScript
복사
•
상품을 선택하고 상품 카탈로그 정보를 가져올 수 있어요.
•
대표 상품 소개, 추천 상품, 상품 목록처럼 상품 정보를 표시해야 하는 블록에 적합해요.
•
bm.config()를 통해 데이터를 가져오는 방식을 설정할 수 있어요:
◦
bm.config('property:products.page', page) : 페이지 번호 설정
◦
bm.config('property:products.limit', limit) : 가져올 상품 개수 설정
CATEGORY
<template>
{{#each property.categories.data}}
<button data-category-id="{{id}}">{{name}}</button>
{{/each}}
{{#each property.categories.products.data}}
<div>{{name}} - {{price.sale}}</div>
{{/each}}
</template>
<script>
function updateActiveButton() {
const active = bm.config('property:categories.products.category');
const activeBtn = bm.container.querySelector(`[data-category-id="${active}"]`);
if (activeBtn) activeBtn.classList.add('active');
}
updateActiveButton();
bm.onContextChange = () => updateActiveButton();
bm.container.addEventListener('click', (event) => {
if (event.target.matches('[data-category-id]')) {
bm.config('property:categories.products.category', event.target.dataset.categoryId);
bm.apply();
}
});
</script>
HTML
복사
"categories": {
"data": [ // 선택된 카테고리 목록
{
"id": "ABC123,DEF456", // "All" (전체) 필터: 콤마(,)로 연결된 선택 카테고리 ID
"name": "All"
},
{
"id": "ABC123", // 카테고리 ID
"name": "상의"
},
{
"id": "DEF456",
"name": "하의"
}
...
],
"products": { // 선택된 카테고리 내 상품
"data": [ ... ], // 상품 목록 (PRODUCT 세팅 참고)
"count": 107 // 선택된 카테고리 내 총 상품 개수
}
}
JavaScript
복사
•
카테고리를 선택하고 해당 카테고리의 상품 정보를 가져올 수 있어요.
•
선택한 카테고리 목록과 카테고리에 속한 상품들을 함께 제공해요.
•
카테고리별 상품 목록이나 카테고리 탭 구성에 적합해요.
•
bm.config()를 통해 데이터를 가져오는 방식을 설정할 수 있어요:
◦
bm.config('property:categories.products.category', categoryId) : 선택 카테고리 변경
◦
bm.config('property:categories.products.page', page) : 페이지 번호 설정
◦
bm.config('property:categories.products.limit', limit) : 가져올 상품 개수 설정
BOARD
<template>
<h1>{{property.board.name}}</h1>
{{#each property.board.posts.data}}
<div>{{title}}</div>
{{/each}}
<button>다음 페이지</button>
</template>
<script>
bm.container.addEventListener('click', (event) => {
event.preventDefault();
if (event.target.matches('button')) {
const currentPage = bm.config('property:board.posts.page');
bm.config('property:board.posts.page', currentPage + 1);
bm.apply();
}
});
</script>
HTML
복사
"board": {
"id": 1, // 게시판 ID
"name": "공지사항", // 게시판 이름
"commentStatus": "ENABLED", // 댓글 사용 여부 (예: "ENABLED", "DISABLED")
"authorDisplayRole": "NAME", // 작성자 표시 정보 (예: "NAME", "EMAIL")
"commentWriteRole": "MEMBER", // 댓글 작성 권한 (예: "ALL", "MEMBER", "ADMIN")
"postWriteRole": "ALL", // 글 작성 권한 (예: "ALL", "MEMBER", "ADMIN")
"secretPostRole": "OPTIONAL", // 비밀글 허용 여부 (예: "NOT_USED", "REQUIRED", "OPTIONAL")
"postTemplate": { // 새 글 작성 템플릿
"title": "...",
"contents": "..."
},
"posts": { // 게시판 내 게시글
"data": [ // 게시글 목록
{
"id": 1, // 게시글 ID
"title": "게시글 제목", // 게시글 제목
"contents": "<p>내용</p>", // 게시글 본문
"displayName": "김**", // 작성자 표시명 (authorDisplayRole에 따라 다름)
"isNotice": false, // 공지글 여부
"status": "PUBLIC", // 게시글 상태 (예: "PUBLIC", "PRIVATE")
"accessType": "OPEN", // 접근 타입 (예: "OPEN", "SECRET")
"authorType": "ADMIN", // 작성자 타입 (예: "ADMIN", "MEMBER", "GUEST")
"commentsCount": 3, // 댓글 개수
"attachmentsCount": 1, // 첨부파일 개수
"thumbnails": [ // 첨부파일 썸네일 목록 (있는 경우)
"<https://example.org/thumbnail.png>"
],
"createdDate": "2023-11-02T02:24:16.388791" // 게시글 작성일시
}
],
"count": 35 // 총 게시글 개수
}
}
JavaScript
복사
•
게시판을 선택하고 게시판 정보와 게시글 목록을 가져올 수 있어요.
•
게시판 설정 정보(댓글 사용 여부, 글 작성 권한 등)와 게시글 목록을 함께 제공해요.
•
게시판 목록, 공지사항, FAQ처럼 게시글을 표시하는 블록에 적합해요.
•
bm.config()를 통해 데이터를 가져오는 방식을 설정할 수 있어요:
◦
bm.config('property:board.posts.page', page) : 페이지 번호 설정
REVIEW
<template>
{{#each property.reviews.data}}
<div>
{{authorName}} - {{rating}}점
<p>{{content}}</p>
</div>
{{/each}}
<button>다음 페이지</button>
</template>
<script>
bm.container.addEventListener('click', (event) => {
event.preventDefault();
if (event.target.matches('button')) {
const currentPage = bm.config('property:reviews.page');
bm.config('property:reviews.page', currentPage + 1);
bm.apply();
}
});
</script>
HTML
복사
"reviews": {
"data": [ // 리뷰 목록
{
"id": 1, // 리뷰 ID
"channel": "WEBSITE", // 리뷰 채널 (예: "WEBSITE", "NAVERPAY")
"authorName": "김**", // 작성자 이름
"authorEmail": "kim***", // 작성자 이메일
"isBestReview": false, // 베스트 리뷰 여부
"productId": "RJXRGSXKEAZK", // 상품 ID
"productName": "멋진 티셔츠", // 상품 이름
"productImage": "<https://example.org/product.png>", // 상품 이미지 URL
"optionTypes": [ // 작성 대상 상품 옵션
{
"option": {
"id": "8MGJMD9NYRWM", // 옵션 ID
"name": "사이즈" // 옵션 이름
},
"variation": {
"id": "YVSQ62UE6TQM", // 옵션 선택지 ID
"value": "M" // 옵션 선택지 값
}
}
],
"rating": 5, // 리뷰 평점
"content": "아주 멋진 티셔츠었어요!", // 리뷰 내용
"images": [ // 리뷰 이미지/영상 목록
{
"url": "<https://example.org/review.png>", // 미디어 URL
"type": "IMAGE" // 미디어 타입 (예: "IMAGE", "VIDEO", "IFRAME"). IFRAME은 NAVERPAY 채널에서만 사용
}
],
"commentsCount": 3, // 댓글 개수
"displayDate": "2025-01-30T10:34:01.351826" // 표시 일시
}
],
"count": 1 // 총 리뷰 개수
}
JavaScript
복사
•
리뷰를 선택하고 리뷰 정보를 가져올 수 있어요.
•
평점, 내용, 작성자, 이미지 등 다양한 리뷰 정보를 제공해요.
•
고객 리뷰 목록, 베스트 리뷰, 상품 상세 리뷰처럼 리뷰를 표시하는 블록에 적합해요.
•
bm.config()를 통해 데이터를 가져오는 방식을 설정할 수 있어요:
◦
bm.config('property:reviews.page', page) : 페이지 번호 설정
Handlebars.js 문법
•
<style>, <template> 태그에서는 일반적인 HTML/CSS와 다르게 Handlebars.js 문법 일부를 지원해요.
•
Handlebars.js 문법을 활용하면 <style>, <template> 태그 내에서 {{…}} 문법으로 컨텍스트(Context) 객체의 값에 접근하고 활용해서 사용자가 설정할 수 있는 블록을 제작할 수 있어요.
•
{{…}} 문법
{{property.mySetting}}
{{property.myNested.mySetting}}
HTML
복사
{{{…}}} 문법
{{property.myHTMLContents}}
-> <h1>Hello Block Maker!</h1>
{{{property.myHTMLContents}}}
-> <h1>Hello Block Maker!</h1>
HTML
복사
•
{{#if}} {{else}} 문법 (+{{#unless}})
<style>
{{#if property.showWelcome}}
p {
color: green;
}
{{/if}}
</style>
<template>
{{#if property.showWelcome}}
<p>만나서 반가워요!</p>
{{else}}
<p>인사는 생략할게요...</p>
{{/if}}
</template>
HTML
복사
기본적인 {{#if}} 구문 활용의 예시
<template>
{{#if (eq property.light 'green')}}
<p>초록 불이니 건널 수 있어요.</p>
{{/if}}
{{#if (and (eq property.light 'green') (gte property.cars 1))}}
<p>초록 불이지만 차가 1대 이상 있어요. 조심히 건너가세요.</p>
{{/if}}
</template>
HTML
복사
헬퍼를 함께 활용하는 {{#if}} 구문 활용의 예시
•
{{#if}} {{else}} 구문으로 분기 처리를 할 수 있어요.
•
{{#if}} 가 있으면 {{/if}} 로 항상 닫혀야 해요.
•
{{else}} 구문은 필요한 경우에만 중간에 끼워서 사용할 수 있어요.
•
{{#if}} 와 비슷하지만 논리 연산이 반대인 문법으로 {{#unless}} 도 있어요.
•
기본적인 {{#if}} 구문에서는 false, undefined, null, "", 0, [] 값들은 모두 거짓 값(Falsy)으로 취급돼요.
{{#if (helper ...)}}
•
•
헬퍼 문법을 활용하면 동일성 비교(eq), 부등호 비교(gt, gte, lt, lte) 등 다양한 비교 연산자와 논리 연산자(and, or)를 함께 활용할 수 있어요.
{{#each}} 문법 (+{{else}})
{
"property": {
"items": [
{ "name": "사과" },
{ "name": "바나나" },
{ "name": "파인애플" }
]
}
}
JSON
복사
<template>
<ul>
{{#each property.items}}
<li>
{{#if @first}}
<strong>[첫 항목]</strong>
{{/if}}
{{#if @last}}
<strong>[마지막 항목]</strong>
{{/if}}
{{@index}}: {{name}}
</li>
{{else}}
<li>표시할 항목이 없어요.</li>
{{/each}}
</ul>
</template>
HTML
복사
•
{{#each}} 구문은 배열이나 객체의 항목들을 순차적으로 출력할 때 사용해요.
•
{{#each}} 가 있으면 반드시 {{/each}} 로 닫아야 해요.
•
{{else}} 구문을 중간에 추가하면, 배열이 비어 있을 때의 대체 내용 표시에 활용할 수 있어요.
•
{{#each}} 내에서 @index, @first, @last 같은 특수 키워드를 사용할 수 있어요.
◦
@index: 현재 항목의 0부터 시작하는 인덱스 값.
◦
@first: 현재 항목이 첫 번째 항목이면 true, 아니면 false.
◦
@last: 현재 항목이 마지막 항목이면 true, 아니면 false.
•
각 배열 요소의 키 값(예시의 items[].name)은 {{#each}} 구문 내에서 {{…}} 문법으로 접근할 수 있어요.
◦
예시처럼 {{name}} 과 같이 간단하게 접근이 가능해요.
◦
{{this.name}} 과 같이 this 키워드를 사용해서 조금 더 명시적으로 표현할 수도 있어요.
편의를 위한 다양한 헬퍼(Helper) 문법
기본적인 {{#if}}, {{#unless}}, {{#each}} 헬퍼 외에도 블록 메이커에서는 다양한 헬퍼 문법을 지원해요.
구분 | 헬퍼(Helper) | 사용 예시 | 설명 |
Conditional | and | {{#if (and property.flagA property.flagB)}} 모두 true예요 {{else}} 둘 중 하나는 false예요 {{/if}} | · 여러 조건이 모두 true일 때만 true를 반환해요.
· 논리 연산에서 모든 조건이 참인지 확인할 때 사용해요.
예: {{and true true}} → true, {{and true false}} → false |
Conditional | or | {{#if (or property.flagA property.flagB)}} 둘 중 하나는 true예요 {{else}} 모두 false예요 {{/if}} | · 여러 조건 중 하나라도 true이면 true를 반환해요.
· 둘 중 하나만 참이면 되는 조건을 구성할 때 사용해요.
예: {{or false true}} → true, {{or false false}} → false |
Conditional | isNil | {{#if (isNil property.value)}} 값이 없어요 {{else}} 값이 있어요 {{/if}} | · 값이 null 또는 undefined인지 확인해요.
· 아무 값도 정의되지 않은 상태인지 확인할 때 사용해요.
예: {{isNil null}} → true, {{isNil "hello"}} → false |
Conditional | isEmpty | {{#if (isEmpty property.text)}} 비어 있어요 {{else}} 내용이 있어요 {{/if}} | · 값이 비어 있는지를 확인해요. 문자열, 배열, 객체에 사용할 수 있어요.
예: {{isEmpty ""}} → true, {{isEmpty "abc"}} → false |
Conditional | eq | {{#if (eq property.value 5)}} 값이 5예요 {{else}} 5가 아니에요 {{/if}} | · 두 값이 같은지를 비교해요.
예: {{eq 5 5}} → true, {{eq 5 "5"}} → false |
Conditional | gt | {{#if (gt property.value 10)}} 10보다 커요 {{else}} 10 이하예요 {{/if}} | · 왼쪽 값이 오른쪽 값보다 큰지 비교해요.
· 숫자의 크기를 비교할 때 자주 사용해요.
예: {{gt 5 3}} → true, {{gt 2 4}} → false |
Conditional | gte | {{#if (gte property.value 10)}} 10 이상이에요 {{else}} 10 미만이에요 {{/if}} | · 왼쪽 값이 오른쪽 값보다 크거나 같은지 확인해요.
· 특정 기준 이상인지 조건을 검사할 때 사용해요.
예: {{gte 5 5}} → true, {{gte 3 4}} → false |
Conditional | lt | {{#if (lt property.value 10)}} 10보다 작아요 {{else}} 10 이상이에요 {{/if}} | · 왼쪽 값이 오른쪽 값보다 작은지 비교해요.
· 값이 기준보다 작은지 확인할 때 사용해요.
예: {{lt 2 5}} → true, {{lt 7 3}} → false |
Conditional | lte | {{#if (lte property.value 10)}} 10 이하예요 {{else}} 10 초과예요 {{/if}} | · 왼쪽 값이 오른쪽 값보다 작거나 같은지 확인해요.
· 숫자가 어떤 한도를 넘지 않았는지 확인할 때 사용해요.
예: {{lte 3 3}} → true, {{lte 5 2}} → false |
Conditional | inRange | {{#if (inRange property.value 1 10)}} 범위 안에 있어요 {{else}} 범위를 벗어났어요 {{/if}} | · 값이 두 수 사이에 있는지 확인해요.
예: {{inRange 3 1 5}} → true, {{inRange 6 1 5}} → false |
Conditional | startsWith | {{#if (startsWith property.text "Hi")}} Hi로 시작해요 {{else}} 다른 시작이에요 {{/if}} | · 문자열이 특정 문자로 시작하는지 확인해요.
· 접두사를 검사하거나 특정 구문으로 시작하는지 판단할 때 사용해요.
예: {{startsWith "hello" "he"}} → true |
Conditional | endsWith | {{#if (endsWith property.text "ing")}} ing로 끝나요 {{else}} 다른 끝이에요 {{/if}} | · 문자열이 특정 문자로 끝나는지 확인해요.
· 접미사를 검사할 때 사용해요.
예: {{endsWith "hello" "lo"}} → true |
Conditional | includes | {{#if (includes property.text "hello")}} 'hello'가 포함되어 있어요 {{else}} 포함되어 있지 않아요 {{/if}} | · 문자열이나 배열에 특정 값이 포함되어 있는지 확인해요.
· 어떤 키워드나 항목이 있는지 체크할 때 사용해요.
예: {{includes "apple" "pp"}} → true |
Number | ceil | {{ceil property.value}} | · 숫자를 올림해요.
예: {{ceil 4.2}} → 5 |
Number | floor | {{floor property.value}} | · 숫자를 내림해요.
예: {{floor 4.8}} → 4 |
Number | round | {{round property.value}} | · 숫자를 반올림해요.
예: {{round 4.6}} → 5 |
Number | add | {{add property.a property.b}} | · 두 수를 더해요.
예: {{add 2 3}} → 5 |
Number | subtract | {{subtract property.total property.used}} | · 왼쪽 값에서 오른쪽 값을 빼요.
예: {{subtract 5 2}} → 3 |
Number | multiply | {{multiply property.width property.height}} | · 두 수를 곱해요.
예: {{multiply 3 4}} → 12 |
Number | divide | {{divide property.total property.count}} | · 왼쪽 값을 오른쪽 값으로 나눠요.
예: {{divide 10 2}} → 5 |
Number | minBy | {{minBy property.people "age"}} | · 배열에서 가장 작은 값을 가진 항목을 찾아요.
예: {{minBy items "age"}} |
Number | maxBy | {{maxBy property.students "score"}} | · 배열에서 가장 큰 값을 가진 항목을 찾아요.
예: {{maxBy items "score"}} |
Number | meanBy | {{meanBy property.scores "value"}} | · 특정 속성의 평균을 구해요.
예: {{meanBy items "score"}} → 평균값 |
Number | sumBy | {{sumBy property.items "count"}} | · 특정 속성의 합계를 구해요.
예: {{sumBy items "price"}} → 합계 |
String | camelCase | {{camelCase property.text}} | · 문자열을 camelCase 형식으로 바꿔요.
예: {{camelCase "hello world"}} → helloWorld |
String | kebabCase | {{kebabCase property.title}} | · 문자열을 kebab-case 형식으로 바꿔요.
예: {{kebabCase "Hello World"}} → hello-world |
String | lowerCase | {{lowerCase property.text}} | · 문자열을 모두 소문자로 바꿔요 (공백 포함).
예: {{lowerCase "HELLO WORLD"}} → hello world |
String | snakeCase | {{snakeCase property.text}} | · 문자열을 snake_case 형식으로 바꿔요.
예: {{snakeCase "Hello World"}} → hello_world |
String | startCase | {{startCase property.text}} | · 문자열의 단어를 대문자로 시작하게 바꿔요.
예: {{startCase "hello world"}} → Hello World |
String | upperCase | {{upperCase property.text}} | · 문자열을 모두 대문자로 바꿔요 (공백 포함).
예: {{upperCase "hello world"}} → HELLO WORLD |
String | lowerFirst | {{lowerFirst property.word}} | · 문자열의 첫 글자만 소문자로 바꿔요.
예: {{lowerFirst "Hello"}} → hello |
String | upperFirst | {{upperFirst property.word}} | · 문자열의 첫 글자만 대문자로 바꿔요.
예: {{upperFirst "hello"}} → Hello |
String | toLower | {{toLower property.text}} | · 문자열을 모두 소문자로 바꿔요.
예: {{toLower "HELLO"}} → hello |
String | toUpper | {{toUpper property.text}} | · 문자열을 모두 대문자로 바꿔요.
예: {{toUpper "hello"}} → HELLO |
String | capitalize | {{capitalize property.text}} | · 첫 글자를 대문자로 바꿔요.
예: {{capitalize "hello"}} → Hello |
String | padStart | {{padStart property.number 4 "0"}} | · 문자열 앞을 채워 길이를 맞춰요.
예: {{padStart "1" 3 "0"}} → 001 |
String | padEnd | {{padEnd property.code 5 "0"}} | · 문자열 끝을 채워 길이를 맞춰요.
예: {{padEnd "a" 3 "_"}} → a__ |
String | trim | {{trim property.text}} | · 문자열 앞뒤의 공백을 제거해요.
예: {{trim " hello "}} → hello |
String | join | {{join property.items ", "}} | · 문자열 구분자를 사용해 배열을 하나의 문자열로 만들 수 있어요.
예: {{join property.items ", "}} → a, b, c |
String | split | {{split property.text ","}} | · 문자열을 구분자로 나눠 배열로 만들어요.
예: {{split property.text ","}} → ["a", "b"] |
Date | datetime | {{datetime post.createdDate}} | · ISO8601 날짜 문자열을 포맷팅해요.
· 기본 포맷 사용 시에는 포맷 문자열을 생략하거나 단축 포맷(date, datetime)를 사용할 수 있어요.
· 커스텀 포맷 사용 시에는 Unicode Technical Standard #35 포맷을 사용할 수 있어요.
예: {{datetime "2025-01-01T00:00:00+09:00"}} → 2025-01-01
예: {{datetime "2025-01-01T00:00:00+09:00" "date"}} → 2025-01-01
예: {{datetime "2025-01-01T00:00:00+09:00" "datetime"}} → 2025-01-01 00:00
예: {{datetime "2025-01-01T00:00:00+09:00" "yyyy/MM/dd HH:mm" }} → 2025/01/01 00:00 |
Array | first | {{first property.items}} | · 배열의 첫 번째 값을 가져와요.
예: {{first property.items}} → 배열의 첫 번째 항목 |
Array | last | {{last property.items}} | · 배열의 마지막 값을 가져와요.
예: {{last property.items}} → 배열의 마지막 항목 |
Array | slice | {{slice property.items 1 3}} | · 배열의 일부를 잘라내요.
예: {{slice property.items 1 3}} → 1번째부터 3번째 전까지 |
Array | size | {{size property.items}} | · 배열, 문자열, 객체의 길이나 개수를 구해요.
예: {{size "abc"}} → 3 |
Array | concat | {{concat property.items property.extra}} | · 여러 값을 배열로 이어붙여요.
예: {{concat property.items property.extra}} → 배열을 합쳐요 |
Array | orderBy | {{orderBy property.records "name" "desc"}} | · 배열을 정렬해요.
예: {{orderBy items "name" "asc"}} |
Array | filter | {{filter property.items "isActive"}} | · 조건에 맞는 항목만 걸러내요.
예: {{filter property.items "isActive"}} |
Array | reject | {{reject property.tasks "completed"}} | · 조건을 만족하지 않는 항목만 걸러내요.
예: {{reject items "isDone"}} |
Array | chunk | {{chunk property.items 2}} | · 배열을 지정한 크기로 나눠 2차원 배열로 만들어요.
예: {{chunk property.items 2}} → [[a,b],[c,d]] 형태 |
Random | random | {{random 0 10}} | · 주어진 범위에서 무작위 수를 만들어요.
예: {{random 1 5}} → 3 |
Random | shuffle | {{shuffle property.items}} | · 배열의 순서를 무작위로 섞어요.
예: {{shuffle property.items}} → 섞인 배열 |
Random | sample | {{sample property.items}} | · 배열에서 무작위 항목 하나를 골라요.
예: {{sample property.items}} → 무작위 요소 하나 |
Random | sampleSize | {{sampleSize property.items 2}} | · 배열에서 지정한 개수만큼 무작위로 골라요.
예: {{sampleSize property.items 2}} → 무작위 2개 항목 |
Other | range | {{range 1 6}} | · 숫자 범위 배열을 만들어요.
예: {{range 0 3}} → [0, 1, 2] |
Other | defaultTo | {{defaultTo property.value "없음"}} | · 값이 null 또는 undefined일 때 기본값을 설정해요.
예: {{defaultTo null "기본값"}} → 기본값 |
Type Converter | toInteger | {{toInteger property.value}} | · 값을 정수로 바꿔요.
예: {{toInteger 4.7}} → 4 |
Type Converter | toNumber | {{toNumber property.string}} | · 값을 숫자로 바꿔요.
예: {{toNumber "3.14"}} → 3.14 |
Type Converter | toString | {{toString property.value}} | · 값을 문자열로 바꿔요.
예: {{toString 123}} → 123 |
Type Converter | toJSON | {{toJSON property.data}} | · 객체나 배열을 JSON 문자열로 바꿔요.
예: {{toJSON property.data}} → {"a":1} |
Type Converter | fromJSON | {{fromJSON property.jsonText}} | · JSON 문자열을 다시 객체로 바꿔요.
예: {{fromJSON property.jsonText}} → 객체 |
bm 문법
•
<script> 태그 내에서는 블록 정보에 접근하거나 블록을 제어할 수 있는 bm 이라는 특별한 객체를 사용할 수 있어요.
•
bm 객체는 블록과 관련된 다양한 기능을 제공하며, 웹사이트에 삽입된 각 블록마다 독립적으로 동작해요.
bm.container
<script>
const container = bm.container;
container.addEventListener('click', e => {
e.preventDefault();
alert('블록 컨테이너를 클릭했어요.');
}, true);
</script>
HTML
복사
bm.context
<script>
const context = bm.context;
console.log(context);
</script>
HTML
복사
•
컨텍스트(Context) 객체에 접근해 블록의 기능이나 UI 구성에 활용할 수 있어요.
bm.onContextChange
<script>
bm.onContextChange = () => {
// 컨텍스트 변화가 생긴 경우 콘솔에 로그
console.log(bm.context);
};
</script>
HTML
복사
•
컨텍스트(Context) 객체의 변화가 생긴 경우 실행되는 콜백 함수를 정의할 수 있는 공간이에요.
◦
블록 사용자가 에디터(프리뷰)에서 블록 설정 값(property)을 변경한 경우.
◦
<data> 태그를 통해 주입받은 컨텍스트 데이터가 상태 변화에 의해 변경된 경우.
◦
bm.apply() 가 호출되어 컨텍스트 변경이 확정된 경우.
•
bm.onContextChange 는 컨텍스트 변경이 확정된 이후, <template>, <style> 태그가 재렌더링 완료된 뒤에 실행돼요.
bm.onDestroy
<script>
// 1초마다 메시지를 출력하는 타이머 설정
const interval = setInterval(() => {
console.log('⏱️ 블록이 아직 페이지에 있어요! (1초마다 동작 중)');
}, 1000);
// 블록이 제거될 때 타이머를 정리
bm.onDestroy = () => {
console.log('🧹 블록이 사라져서 타이머를 멈췄어요!');
clearInterval(interval);
};
</script>
HTML
복사
•
블록이 제거될 때(페이지 이동이나 DOM 제거 등) 실행되는 콜백 함수를 정의할 수 있어요.
•
블록이 사라진 후에도 계속 실행되는 코드가 있으면 브라우저에 성능 문제가 생길 수 있어서, 정리(Cleanup) 작업이 필요한 경우 유용해요.
◦
setInterval(...), setTimeout(...) 등으로 설정한 타이머 해제
◦
addEventListener(...), removeEventListener(...) 등으로 등록한 DOM 이벤트 리스너 정리
◦
MutationObserver, IntersectionObserver 등의 옵저버 해제
◦
외부 상태나 리소스를 참조하는 전역 변수 또는 캐시 초기화
bm.apply()
<template>
<p>카운터: {{counter}}</p>
<button>눌러보세요!</button>
<template>
<script>
const context = bm.context;
const container = bm.container;
container.addEventListener('click', e => {
e.preventDefault();
if (e.target.matches('button')) {
// 카운터 초기화
context.counter = context.counter || 0;
// 카운터 증가
context.counter++;
// 변경사항 적용하고 블록 다시 그리기
bm.apply();
}
}, true);
</script>
HTML
복사
•
컨텍스트에 변경이 생겼을 때 변경 사항을 블록에 적용하는 기능이에요.
•
bm.apply() 가 호출되면 블록이 재렌더링되고 bm.onContextChange 콜백도 실행돼요.
•
블록의 <script> 코드에서 컨텍스트에 값을 추가/변경하거나 블록 내 설정 값을 변경한 뒤, 블록을 재렌더링하는 경우 유용하게 사용할 수 있어요.
bm.config()
<template>
<h1>{{property.board.name}}</h1>
{{#each property.board.posts.data}}
<div>{{title}}</div>
{{/each}}
<button>다음 페이지</button>
</template>
<script>
const container = bm.container;
container.addEventListener('click', e => {
e.preventDefault();
if (e.target.matches('button')) {
// 현재 페이지 번호 가져오기
const currentPage = bm.config('property:board.posts.page');
// 다음 페이지로 설정
bm.config('property:board.posts.page', currentPage + 1);
// 변경사항 적용하고 블록 다시 그리기
bm.apply();
}
}, true);
</script>
HTML
복사
•
블록 내 설정 값을 가져오거나 변경할 수 있어요.
•
bm.config(key) 형식으로 설정 값을 가져오고, bm.config(key, value) 형식으로 값을 설정해요.
•
설정 값을 변경한 뒤에는 bm.apply()를 호출해야 블록에 반영돼요.
bm.do()
<script>
bm.do('snackbar:open', {
message: '안녕하세요!'
});
</script>
HTML
복사
•
블록 메이커에서 제공하는 다양한 액션을 실행할 수 있어요.
•
bm.do(action[, data]) 형식으로 사용하며, data는 액션 종류에 따라 생략될 수 있어요.
•
비동기형 액션의 경우 Promise를 반환해요.
•
bm.call()
<script>
// GET 요청 예시
bm.call({
method: 'GET',
url: 'https://example.org/cats'
}).then(response => {
bm.context.cats = response.data;
bm.apply();
});
// POST 요청 예시
bm.call({
method: 'POST',
url: 'https://example.org/dogs',
data: {
name: '블로키'
}
}).then(response => {
console.log(response.data);
});
</script>
JavaScript
복사
•
HTTP 요청을 보낼 때 사용할 수 있어요.
•
bm.call(request) 형식으로 사용하며 Promise를 반환해요.
•
기본적으로 bm.call() 은 JSON으로 데이터를 전송(Content-Type: application/json)해요.
bm.localStorage & bm.sessionStorage
<script>
bm.localStorage.setItem('myKey', { name: '블록 메이커', age: 1 });
const user = bm.localStorage.getItem('myKey'); // 객체가 그대로 반환돼요
console.log(user.name); // "블록 메이커"
console.log(user.age); // 1
bm.localStorage.removeItem('myKey');
</script>
HTML
복사
•
블록에서 활용할 수 있는 브라우저 스토리지(localStorage, sessionStorage)에요.
•
데이터를 저장하거나 읽을 때 자동으로 JSON 변환 처리를 해주기 때문에 복잡한 데이터(객체, 배열)도 쉽게 다룰 수 있어요.
bm.do() 지원 기능
go
if (addedToCart) {
bm.do('go', '/cart');
}
JavaScript
복사
장바구니에 상품을 담은 후 장바구니 페이지로 이동시키는 예시
bm.do('go', '/cart') // URL 단순 이동 시
bm.do('go', {
url: '/cart', // (필수) 이동할 URL
scroll: false // (선택) 스크롤 위치 유지 (기본값: true, false 설정 시 현재 위치 유지)
})
JavaScript
복사
•
지정한 URL로 페이지를 이동시켜요.
logout
// <data value="$customer"/> 를 통해 주입된 고객 정보
const customer = bm.context.customer;
if (customer) {
bm.do('logout');
}
JavaScript
복사
로그인된 회원(구매자)을 로그아웃 처리하는 예시
bm.do('logout')
JavaScript
복사
•
로그인된 고객을 로그아웃 시켜요.
snackbar:open
bm.do('snackbar:open', {
message: '성공적으로 저장되었습니다'
});
bm.do('snackbar:open', {
message: '장바구니에 담았어요',
button: { name: '장바구니 가기', url: '/cart' }
});
JavaScript
복사
버튼 미사용/사용 알림 디스플레이 예시
bm.do('snackbar:open', {
message: '...', // (필수) 스낵바 메시지
button: { // (선택) 버튼 설정
name: '...', // (필수) 버튼 텍스트
url: '...' // (필수) 버튼 클릭 시 이동할 URL
}
})
JavaScript
복사
•
화면에 알림(Snackbar)을 표시해요.
modal:open
bm.do('modal:open', {
message: '장바구니에 담았어요',
buttons: [
{ name: '계속 쇼핑하기' },
{ name: '장바구니 가기', url: '/cart' }
]
});
JavaScript
복사
커스텀 닫기 버튼을 포함한 모달 디스플레이 예시
bm.do('modal:open', {
message: '...', // (필수) 모달 메시지
buttons: [ // (선택) 버튼 목록
{
name: '...', // (필수) 버튼 텍스트
url: '...' // (선택) 버튼 클릭 시 이동할 URL (생략 시 닫기 버튼으로 취급)
}
]
})
JavaScript
복사
•
화면에 모달(Modal) 대화상자를 표시해요.
board:createPost
// BOARD 세팅을 통해 블록에 설정된 게시판 정보
const board = bm.context.property.board;
const postMustBePublic = board.secretPostRole === 'NOT_USED';
// <data value="$customer"/> 를 통해 주입된 고객 정보
const customer = bm.context.customer;
const params = {
boardId: board.id,
title: '제목',
contents: '내용',
accessType: postMustBePublic ? 'OPEN' : 'SECRET',
};
if (!customer) {
// 비회원인 경우 작성자 정보 추가
Object.assign(params, {
userName: '비회원',
email: 'guest@example.org',
password: '000000'
});
}
await bm.do('board:createPost', { params });
JavaScript
복사
회원/비회원을 지원하는 게시판 글 작성 예시
await bm.do('board:createPost', {
params: {
boardId: ..., // (필수) 작성 대상 게시판 ID
title: '...', // (필수) 글 제목
contents: '...', // (필수) 글 내용
accessType: '...', // (필수) 공개 여부 ('OPEN', 'SECRET')
// 비회원인 경우 필수
userName: '...', // (조건부 필수) 작성자 이름
email: '...', // (조건부 필수) 작성자 이메일
password: '...', // (조건부 필수) 글 비밀번호
// 회원만 사용 가능
files: FileList // (선택) 첨부 파일 (FileList 객체)
}
})
JavaScript
복사
•
설정된 게시판에 글을 작성해요.
•
◦
파일은 최대 5개 첨부가 가능하고 각 10MB 제한이 있어요.
◦
지원 확장자: .jpg, .jpeg, .png, .svg, .webp, .gif, .mp4, .pdf, .docx, .hwp, .csv, .xlsx, .pptx, .psd, .ai, .zip
cart:addItem
// PRODUCT 세팅을 통해 블록에 설정된 상품 카탈로그 정보
const item = bm.context.property.products.data[index];
// 바로 담기가 불가능한 경우 상세 페이지로 이동
if (!item.isQuickAddable) {
return bm.do('modal:open', {
message: '옵션 선택이 필요한 상품입니다.\n상품 상세 페이지로 이동하시겠어요?',
buttons: [
{ name: '이동하기', url: `/products/${item.slug}` },
{ name: '취소' }
]
});
}
try {
// 장바구니 API 호출
await bm.do('cart:addItem', {
product: item.product.id,
variant: item.variant.id,
shippingMethod: item.shipping.methods[0].id,
quantity: 1
});
bm.do('snackbar:open', { message: '상품을 장바구니에 담았어요' });
} catch (err) {
// 에러 발생 시 메시지 출력
bm.do('snackbar:open', { message: err.message });
}
JavaScript
복사
상품 목록에서 상품을 장바구니에 바로 담는(Quick Add) 예시
await bm.do('cart:addItem', {
product: '...', // (필수) 상품 ID
variant: '...', // (필수) 세부 품목 ID
shippingMethod: '...', // (필수) 배송 방식 ID
quantity: 1 // (필수) 수량
})
JavaScript
복사
•
장바구니에 상품을 추가해요.
웹사이트 주요 페이지
페이지명 | 페이지 구분자(name) | URL 패스(path) |
홈페이지 | HOME | / |
커스텀 페이지 | CUSTOM | /:slug |
상품 목록 (전체) | PRODUCT_LIST | /categories/all |
상품 목록 (카테고리 별) | PRODUCT_LIST | /categories/:id |
상품 상세 | PRODUCT_DETAIL | /products/:slug |
장바구니 | CART | /cart |
주문서 | CHECKOUT | /checkout |
주문 완료 | ORDER_COMPLETED | /orders/:id/completed |
게시글 목록 (게시판 별) | BOARD | /boards/:boardId |
게시글 상세 | POST | /posts/:postId |
로그인 | LOGIN | /auth/login |
회원가입 | SIGNUP | /auth/signup |
회원가입 완료 | SIGNUP_COMPLETED | /auth/signup/completed |
… |
•
웹사이트에서 제공하고 있는 주요 페이지 목록이에요.
•
명시된 페이지 외에도 더 많은 페이지가 존재해요.
•
제작한 블록에서 페이지를 이동시키거나, <data value="$page" /> 문법을 활용해서 블록을 구성할 때 함께 참고해주세요.


.png&blockId=1594b16e-2bf1-80c3-b713-e8c0dbabb5db)
.png&blockId=1594b16e-2bf1-80c3-b713-e8c0dbabb5db&width=256)