소개

DIBO Extension API는 웹사이트에 다양한 확장 기능을 추가할 수 있는 REST API 서비스입니다. 본 문서는 개발자를 위한 기술 가이드입니다.

Base URL

http
https://api.di-bo.me

제공 기능

Block API
이미지와 콘텐츠를 포함한 블록 위젯 관리
Timer API
타이머 기반 이벤트 위젯 관리
Edit API
사용자 정의 입력폼 위젯 관리
Popup API
모달 팝업 위젯 관리
Floating API
플로팅 버튼 위젯 관리
Rolling Banner API
롤링 배너 슬라이드 위젯
Member API
사이트 회원가입, 로그인, 회원 관리
Board API
게시판 글, 댓글, 좋아요/신고

인증

모든 API 요청에는 인증이 필요합니다.

인증 헤더

http
access-token: {your_access_token}

# 회원(end-user) 본인 요청 시 추가
member-token: {member_token}

방법 1: DIBO 관리 페이지에서 토큰 발급

  1. 1DIBO 관리 페이지 로그인
  2. 2관리 → 사이트 선택 후 대시보드 메뉴 이동
  3. 3기본 정보 하단에 토큰 생성 버튼 클릭
  4. 4발급된 코드 복사

발급된 코드에는 다음 두 가지 변수가 포함됩니다:

html
<!-- Axios CDN -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<!-- dibo-token Info -->
<script>
    const diboToken = 'eyJhbGciOiJIUzI1NiIsInR...'
    let diboMemberToken;
</script>
<!-- member-token 유효성 확인 -->
<script>
    window._diboMemberReady = (async function checkMemberToken() {
        const memberToken = localStorage.getItem('member-token');
        if (!memberToken) return;
        try {
            await axios.get('https://api.di-bo.me/member/token', {
                headers: {
                    'access-token': diboToken,
                    'member-token': memberToken,
                }
            });
            diboMemberToken = memberToken;
        } catch (error) {
            if (error.response && error.response.status === 401) {
                localStorage.removeItem('member-token');
            }
        }
    })();
</script>
이름타입필수설명
diboTokenstring필수모든 API 호출에 access-token 헤더로 사용하는 사이트 토큰
diboMemberTokenstring선택로그인한 회원의 member-token. 회원 전용 요청 전 await window._diboMemberReady 로 준비를 기다린 뒤 사용

방법 2: config_id 확인

각 익스텐션은 고유한 config_id로 대상을 지정합니다.

(1) 익스텐션 설정에서 코드 복사 (자동)

DIBO 관리 페이지 → 나의 익스텐션 → 익스텐션 설정 → 코드 복사 클릭 시, 복사된 코드에 config_id가 자동으로 내 값으로 치환되어 들어갑니다.

(2) API로 조회

http
GET /config?site_id={site_id}

응답의 각 항목 id가 그 익스텐션의 config_id입니다.

공통 사항

요청 헤더

http
Content-Type: application/json
access-token: {your_access_token}
파일이 포함되는 요청(회원가입, 입력폼/블록 제출, 이미지 업로드)은 multipart/form-data를 사용하며, JSON 데이터는 req_body 필드에 JSON 문자열로 전달합니다.

응답 형식

모든 응답은 JSON 형식으로 반환됩니다. 목록 조회 API는 기본적으로 최신순으로 정렬되어 반환됩니다.

HTTP 상태 코드

코드설명대응 방법
200 OK성공-
204 No Content삭제 성공-
400 Bad Request잘못된 요청요청 파라미터 확인
401 Unauthorized인증 실패access-token 확인
403 Forbidden권한 없음사용자 권한 확인
404 Not Found리소스 없음ID 및 경로 확인
409 Conflict중복된 리소스기존 데이터 확인
500 Internal Server Error서버 오류관리자 문의

빠른 시작

DIBO 관리 페이지에서 익스텐션을 추가한 뒤, 아래 흐름으로 사이트에 연결합니다.

1
토큰 발급
대시보드 "토큰생성"으로 받은 코드를 페이지에 넣습니다. diboToken이 access-token 입니다.
2
config_id 확인
익스텐션 "코드 복사"에 자동으로 들어 있거나, GET /config?site_id={site_id} 로 조회합니다.
3
데이터 호출 후 렌더
익스텐션 타입에 맞는 API를 호출해 데이터를 받아 화면에 그립니다.
javascript
// 예: 블록(배너/카드) 위젯 데이터를 받아 렌더
const res = await axios.get(`https://api.di-bo.me/block?config_id=copy_config_id`, {
  headers: { 'access-token': diboToken }
});
res.data.forEach((item) => {
  // item.main_img_url, item.title, item.link 로 카드 렌더
});
회원/게시판 등 로그인 회원 기능은 member-token을 함께 보냅니다. 호출 전 await window._diboMemberReady로 검증 완료를 기다립니다. 회원/게시판 기능은 스탠다드 이상 구독에서 동작합니다.

Extension Block API

블록 형태의 콘텐츠 위젯을 관리하는 API입니다. 이미지, 텍스트, 링크를 포함한 카드 형태의 UI 컴포넌트를 생성할 수 있습니다.

필요 정보: DIBO 관리 페이지 → 나의 익스텐션 → 익스텐션 설정 → 코드 복사에서 config_id 확인

데이터 모델

typescript
interface ExtensionBlock {
  id: string;                // 블록 고유 ID
  client_id: string;         // 클라이언트 ID
  config_id: string;         // 설정 ID
  main_img_url?: string;     // 메인 이미지 URL
  sub_img_url?: string;      // 서브 이미지 URL
  title: string;             // 제목 (최대 128자)
  content?: string;          // 내용
  link?: string;             // 링크 URL
  display_order?: number;    // 표시 순서
  created_at: datetime;      // 생성일시
  updated_at: datetime;      // 수정일시
}

엔드포인트

블록 목록 조회

GET/block?config_id={config_id}

응답: ExtensionBlock[]

블록 생성

POST/block

Content-Type: multipart/form-data

이름타입필수설명
req_bodyJSON string필수config_id, title, content, link 포함 JSON 문자열
main_imagefile선택메인 이미지 파일
sub_imagefile선택서브 이미지 파일
bash
curl -X POST https://api.di-bo.me/block \
  -H "access-token: your_token" \
  -F 'req_body={"config_id":"config_789","title":"새로운 블록","content":"설명","link":"https://example.com"}' \
  -F "main_image=@/path/to/main.jpg" \
  -F "sub_image=@/path/to/sub.jpg"

블록 상세 조회

GET/block/{block_id}?config_id={config_id}

블록 수정

PUT/block/{block_id}

요청 형식은 생성과 동일 (multipart/form-data)

블록 삭제

DELETE/block/{block_id}?config_id={config_id}

Extension Timer API

타이머 기반 이벤트를 관리하는 API입니다. 카운트다운, 이벤트 시작/종료 등의 시간 기반 위젯을 구현할 수 있습니다.

데이터 모델

typescript
interface ExtensionTimer {
  id: string;
  client_id: string;
  config_id: string;
  started_at: datetime;      // 시작 시간
  start_title: string;       // 시작 제목 (최대 128자)
  ended_at?: datetime;       // 종료 시간 (선택)
  end_title?: string;        // 종료 제목 (선택)
  display_order?: number;
  created_at: datetime;
  updated_at: datetime;
}

엔드포인트

타이머 목록 조회

GET/timer?config_id={config_id}

타이머 생성

POST/timer
json
{
  "config_id": "config_789",
  "started_at": "2024-01-20T09:00:00Z",
  "start_title": "이벤트 시작",
  "ended_at": "2024-01-20T21:00:00Z",
  "end_title": "이벤트 종료"
}

타이머 상세 조회

GET/timer/{timer_id}?config_id={config_id}

타이머 수정

PUT/timer/{timer_id}

타이머 삭제

DELETE/timer/{timer_id}?config_id={config_id}

Extension Edit API

사용자 정의 입력폼 데이터를 관리하는 API입니다.

입력폼 구조 설정은 DIBO 관리 페이지에서 진행합니다. 본 API는 이미 설정된 폼에 데이터를 입력/조회/수정/삭제하는 기능을 제공합니다.

지원 입력 타입

타입설명예시
single_line한 줄 텍스트이름, 제목
multi_line여러 줄 텍스트상세 설명, 메시지
single_selection단일 선택카테고리 선택
checkbox체크박스동의 여부
list_selection목록 선택다중 옵션
phone전화번호010-1234-5678
email이메일user@example.com
date날짜2024-01-15
file파일 업로드문서, 이미지

데이터 모델

typescript
interface ExtensionEdit {
  id: string;
  client_id: string;
  config_id: string;
  set_id: string;        // 폼 설정 ID
  column_1: string;      // 컬럼1 (필수)
  column_2?: string;
  column_3?: string;
  // ... column_10까지
  created_at: datetime;
  updated_at: datetime;
}

엔드포인트

입력폼 데이터 목록 조회

GET/edit-set/{set_id}/rows?config_id={config_id}

입력폼 데이터 생성

POST/edit-set/{set_id}/rows

Content-Type: multipart/form-data

bash
curl -X POST https://api.di-bo.me/edit-set/{set_id}/rows \
  -H "access-token: your_token" \
  -F 'req_body={"config_id":"config_789"}' \
  -F "column_1=홍길동" \
  -F "column_2=hong@example.com" \
  -F "column_3=010-1234-5678" \
  -F "column_4=기술 지원" \
  -F "column_5=API 연동 관련 문의드립니다." \
  -F "column_6=@/path/to/document.pdf"

입력폼 데이터 상세 조회

GET/edit-set/{set_id}/rows/{row_id}?config_id={config_id}

입력폼 데이터 수정

PUT/edit-set/{set_id}/rows/{row_id}

입력폼 데이터 삭제

DELETE/edit-set/{set_id}/rows/{row_id}?config_id={config_id}

Extension Floating API

플로팅 버튼 데이터를 반환합니다. design.modesingle이면 단일 버튼, expandable이면 클릭 시 메뉴가 펼쳐집니다.

데이터 모델

typescript
interface ExtensionFloating {
  display: FloatingDisplay;
  design: FloatingDesign;
  items?: FloatingItem[];
}

interface FloatingDisplay {
  show_on?: 'all' | 'specific' | 'exclude';
  show_pages?: string[];
  show_delay?: number;
  hide_on_mobile?: boolean;
  hide_on_desktop?: boolean;
}

interface FloatingDesign {
  mode?: 'single' | 'expandable';
  vertical?: string;       // top | bottom
  horizontal?: string;     // left | right
  offset_x?: number;
  offset_y?: number;
  width?: number;
  height?: number;
  border_radius?: number;
  trigger_icon?: string | null;
  trigger_icon_open?: string | null;
}

interface FloatingItem {
  id: string;
  display_order?: number | null;
  label?: string | null;
  icon_url?: string | null;
  link_url?: string | null;
  link_target?: string;    // _self | _blank
}

엔드포인트

플로팅 버튼 조회

GET/floating?config_id={config_id}

응답: ExtensionFloating. mode=single이면 items[0]만, mode=expandable이면 items 전체를 메뉴로 렌더링합니다.

javascript
const res = await axios.get(`https://api.di-bo.me/floating?config_id=copy_config_id`, {
  headers: { 'access-token': diboToken }
});
const { design, items } = res.data;
const list = design.mode === 'single' ? items.slice(0, 1) : items;
// design.vertical/horizontal 위치에 list 의 각 항목(icon_url/label/link_url) 렌더

Extension Rolling Banner API

롤링 배너 데이터를 반환합니다. 롤링 동작 설정(rolling)과 슬라이드 항목(items)을 함께 반환합니다.

데이터 모델

typescript
interface ExtensionRolling {
  rolling: RollingConfig;
  items?: RollingItem[];
}

interface RollingConfig {
  auto_play?: boolean;
  interval?: number;          // 전환 간격(ms)
  pause_on_hover?: boolean;
  loop?: boolean;
  direction?: 'horizontal' | 'vertical';
  transition?: RollingTransition;
}

interface RollingTransition {
  style?: 'slide' | 'fade' | 'zoom' | 'flip' | 'none';
  duration?: number;
  easing?: string;            // CSS easing (예: ease-in-out)
}

interface RollingItem {
  id: string;
  image_url?: string | null;
  mobile_image_url?: string | null;
  video_url?: string | null;
  title?: string | null;
  description?: string | null;
  button_label?: string | null;
  link_url?: string | null;
  link_target?: string;       // _self | _blank
}

엔드포인트

롤링 배너 조회

GET/rolling-banner?config_id={config_id}

응답: ExtensionRolling

javascript
const res = await axios.get(`https://api.di-bo.me/rolling-banner?config_id=copy_config_id`, {
  headers: { 'access-token': diboToken }
});
const { rolling, items } = res.data;
// items 를 rolling.interval 간격으로 rolling.transition.style 전환하며 슬라이드 렌더

Member API

사이트 방문자(end-user)의 회원가입, 로그인, 본인 정보 관리 API입니다.

회원 기능은 스탠다드 이상 구독에서 동작합니다.

인증

이름타입필수설명
access-tokenheader필수사이트 토큰 (모든 회원 API 공통)
member-tokenheader선택로그인 응답으로 받은 회원 토큰 (본인 정보/탈퇴/비밀번호 변경 등)

데이터 모델

typescript
interface MemberToken {
  member_token: string;
  pwd_change_required?: boolean;
  email_verified?: boolean;
}

interface MemberDetailWithCustom {
  id: string;
  site_id: string;
  group_ids?: string[] | null;
  email: string;
  user_id?: string | null;
  member_type: 'general' | 'business';
  profile_img?: string | null;
  name?: string | null;
  phone_num?: string | null;
  birth_date?: string | null;
  status: string;
  email_verified?: boolean | null;
  created_at: string;
  updated_at: string;
  // 그룹 설정에 따라 business_* 또는 custom_1~5 추가될 수 있음
}

엔드포인트

회원가입

POST/member/signup

Content-Type: multipart/form-data. req_body에 email, pwd, member_type, group_ids, 약관 동의 등 JSON 문자열 전달. 파일 필드: profile_image, business_file, custom_file_1~5.

로그인

POST/member/login
json
{ "email": "member@example.com", "pwd": "Password1234!" }

응답: MemberToken

이메일 인증

POST/member/email-send
POST/member/email-verify

아이디 찾기

POST/member/find-id

비밀번호 재설정

PUT/member/update-password

member-token 유효성 확인

GET/member/token

비밀번호 변경 (로그인 회원)

PUT/member/change-password

회원 탈퇴

DELETE/member/withdraw

회원 본인 정보 조회 / 수정 / 삭제

GET/member/{member_id}?site_id={site_id}
PUT/member/{member_id}?site_id={site_id}
DELETE/member/{member_id}?site_id={site_id}
본인 정보 수정은 multipart/form-data로 변경 필드를 req_body에 담아 전달합니다.

Member Group API

회원가입 폼을 구성하기 위해 그룹 설정을 조회합니다. 스탠다드 이상 구독에서 동작합니다.

데이터 모델

typescript
interface MemberGroup {
  id: string;
  site_id: string;
  name: string;
  group_type: 'general' | 'business';
  login_type?: 'email' | 'id';
  join_process?: 'approval' | 'auto';
  pw_min_length?: number;
  pw_policy?: { upper?: boolean; number?: boolean; special?: boolean };
  profile_field?: { use?: boolean; required?: boolean };
  terms_enabled?: boolean;
  terms_content?: { service?: string; privacy?: string; marketing?: string };
  general_fields?: object | null;
  business_fields?: object | null;
  custom_fields?: CustomFieldSetting[] | null;
  created_at: string;
  updated_at: string;
}

interface CustomFieldSetting {
  type?: string;
  name?: string;
  options?: string[];
  use?: boolean;
  required?: boolean;
}

엔드포인트

회원 그룹 목록 조회

GET/member-group?site_id={site_id}

응답: MemberGroup[]

javascript
const res = await axios.get(`https://api.di-bo.me/member-group?site_id=SITE_ID`, {
  headers: { 'access-token': diboToken }
});
const group = res.data[0];
// group.login_type(email/id), group.custom_fields, group.terms_content 에 맞춰 폼 렌더

회원 그룹 단건 조회

GET/member-group/{group_id}

Board API

게시판 글, 댓글, 대댓글과 좋아요/신고, 이미지 업로드, 내 활동 조회 API입니다. 스탠다드 이상 구독에서 동작합니다.

인증

이름타입필수설명
access-tokenheader필수사이트 토큰
member-tokenheader선택회원 작성/투표/내 활동 시 필요
비회원 작성을 허용한 게시판은 authorpassword로 작성/수정/삭제가 가능합니다.

데이터 모델

typescript
interface Post {
  id: number;
  board_id: string;
  slug: string;
  category?: string | null;
  title: string;
  content: string;           // HTML
  author: string;
  views?: number;
  thumbnail?: string | null;
  tags?: string[];
  likes?: number;
  dislikes?: number;
  my_vote?: string | null;   // member-token 있을 때 본인 투표 상태
  comments_enabled?: boolean;
  is_guest_post?: boolean;
  created_at: string;
  updated_at: string;
}

interface Comment {
  id: number;
  post_id: number;
  author: string;
  content: string;
  images?: string[];
  likes?: number;
  secret?: boolean;
  replies?: Reply[];
  created_at: string;
}

엔드포인트

게시판 목록

GET/board/boards

게시글 목록

GET/board/boards/{board_id}/posts?category={}&q={}&page={}&limit={}&sort={}
이름타입필수설명
categorystring선택카테고리 필터
qstring선택검색어
pagenumber선택페이지 번호
limitnumber선택페이지당 결과 수
sortlatest | popular선택정렬 방식

게시글 작성

POST/board/boards/{board_id}/posts
javascript
// 회원 글 작성 (member-token)
await window._diboMemberReady;
await axios.post(`https://api.di-bo.me/board/boards/${boardId}/posts`,
  { title: '문의합니다', content: '<p>내용</p>', category: '일반' },
  { headers: { 'access-token': diboToken, 'member-token': diboMemberToken } }
);

// 비회원 글 작성 (비회원 허용 게시판)
await axios.post(`https://api.di-bo.me/board/boards/${boardId}/posts`,
  { title: '문의', content: '<p>내용</p>', author: '홍길동', password: '1234' },
  { headers: { 'access-token': diboToken } }
);

게시글 상세

GET/board/boards/{board_id}/posts/{post_id}

게시글 수정 / 삭제

PUT/board/posts/{post_id}
DELETE/board/posts/{post_id}?password={pwd}

게시글 투표 / 신고

POST/board/posts/{post_id}/like
POST/board/posts/{post_id}/dislike
POST/board/posts/{post_id}/report

댓글

GET/board/posts/{post_id}/comments?sort={top|new}
POST/board/posts/{post_id}/comments
PUT/board/comments/{comment_id}
DELETE/board/comments/{comment_id}
POST/board/comments/{comment_id}/like
POST/board/comments/{comment_id}/replies

대댓글

PUT/board/replies/{reply_id}
DELETE/board/replies/{reply_id}

이미지 업로드

POST/board/images

Content-Type: multipart/form-data. 응답: { urls: string[] }

내 활동 (member-token 필요)

GET/board/members/me/posts
GET/board/members/me/liked-posts
GET/board/members/me/disliked-posts
GET/board/members/me/reported-posts

Extension Catalog API

설치 가능한 위젯(익스텐션) 목록과 복사용 기본 코드, 그리고 내 사이트에 설치된 익스텐션(config) 목록을 조회합니다.

데이터 모델

typescript
interface Extension {
  id: string;
  extension_type: 'free' | 'block' | 'timer' | 'edit' | 'popup' | 'floating' | 'rolling_banner';
  status: 'approved' | 'disapproved' | 'deleted';
  title: string;
  content?: string | null;
  keyword?: string[] | null;
  thumbnail_url?: string | null;
  image_urls?: string[] | null;
  created_at: string;
  updated_at: string;
}

interface ExtensionConfig {
  id: string;             // config_id
  site_id: string;
  extension_id: string;
  status: 'approved' | 'disapproved';
  title: string;
  extension_type: string;
  thumbnail_url?: string | null;
  created_at: string;
  updated_at: string;
}

엔드포인트

위젯 목록 / 단건

GET/extension
GET/extension/{extension_id}?extension_type={extension_type}

위젯 기본 코드

GET/code?extension_id={extension_id}&extension_type={extension_type}
GET/code/{code_id}?extension_id={extension_id}&extension_type={extension_type}
코드 안의 copy_config_id는 실제 config_id로 치환해 사용합니다.

내 사이트 익스텐션 (config) 목록

GET/config?site_id={site_id}

응답: ExtensionConfig[]. 각 항목의 id가 위젯 데이터 API에서 사용하는 config_id입니다.

javascript
const res = await axios.get(`https://api.di-bo.me/config?site_id=SITE_ID`, {
  headers: { 'access-token': diboToken }
});
const popup = res.data.find((c) => c.extension_type === 'popup');
// popup.id 를 popup 위젯 API 호출 시 config_id 로 사용

에러 처리

에러 응답 형식

json
{ "detail": "에러 메시지" }

주요 에러 코드

코드설명대응 방법
400잘못된 요청요청 파라미터 확인
401인증 실패access-token 확인
403권한 없음사용자 권한 확인
404리소스 없음ID 및 경로 확인
409중복된 리소스기존 데이터 확인
500서버 오류관리자 문의

예제 코드

JavaScript (Axios)

공통 설정

javascript
// DIBO 관리 페이지에서 제공된 토큰
const diboToken = 'eyJhbGciOiJIUzI1NiIsInR...'

// 코드 복사에서 확인한 config_id
const configId = 'CFYEDHZSP5ZT'

Block API 예제

javascript
// 블록 목록 조회
async function getBlocks(configId) {
  const response = await axios.get(`https://api.di-bo.me/block?config_id=${configId}`, {
    headers: { 'access-token': diboToken }
  });
  return response.data;
}

// 블록 생성
async function createBlock(configId, title, content, link, mainImage) {
  const formData = new FormData();
  formData.append('req_body', JSON.stringify({ config_id: configId, title, content, link }));
  if (mainImage) formData.append('main_image', mainImage);

  const response = await axios.post('https://api.di-bo.me/block', formData, {
    headers: { 'access-token': diboToken }
  });
  return response.data;
}

Timer API 예제

javascript
async function createTimer(configId, startTime, startTitle, endTime, endTitle) {
  const response = await axios.post('https://api.di-bo.me/timer', {
    config_id: configId,
    started_at: startTime,
    start_title: startTitle,
    ended_at: endTime,
    end_title: endTitle
  }, {
    headers: { 'access-token': diboToken }
  });
  return response.data;
}

Edit API 예제

javascript
async function submitFormData(setId, configId, fields) {
  const formData = new FormData();
  formData.append('req_body', JSON.stringify({ config_id: configId }));

  // 텍스트 필드 (관리 페이지 설정의 컬럼 순서에 맞춰 매핑)
  Object.entries(fields).forEach(([key, value]) => {
    if (value instanceof File) formData.append(key, value);
    else if (value != null) formData.append(key, String(value));
  });

  const response = await axios.post(`https://api.di-bo.me/edit-set/${setId}/rows`, formData, {
    headers: { 'access-token': diboToken }
  });
  return response.data;
}

Python 예제

python
import requests, json

class DiboAPI:
    def __init__(self, access_token):
        self.base_url = "https://api.di-bo.me"
        self.headers = { "access-token": access_token }

    def get_blocks(self, config_id):
        res = requests.get(f"{self.base_url}/block?config_id={config_id}", headers=self.headers)
        res.raise_for_status()
        return res.json()

    def create_block(self, config_id, title, content=None, link=None, main_image_path=None):
        files = { 'req_body': (None, json.dumps({"config_id": config_id, "title": title, "content": content, "link": link})) }
        if main_image_path:
            files['main_image'] = open(main_image_path, 'rb')
        res = requests.post(f"{self.base_url}/block", headers=self.headers, files=files)
        res.raise_for_status()
        return res.json()

# 사용 예시
api = DiboAPI(access_token="eyJhbGciOiJIUzI1NiIsInR...")
blocks = api.get_blocks("CFYEDHZSP5ZT")
제한 사항
  • 파일 업로드 최대 크기: 50MB
  • 블록 제목 최대 길이: 128자
  • 타이머 제목 최대 길이: 128자
  • Edit 입력폼 최대 컬럼 수: 10개