회원가입 | 고객센터 |
DESIGNONEX
디자인원엑스
DX마켓
Service
PR리그
Q&AN
지식공유N
공지사항
통계
로그인 회원가입
고객센터

DX API Plugin - DXCMS REST API — React Native / 웹앱 / 외부 시스템 연동

D DX관리자
2026.07.23 03:29(수정됨) 3 0 1


목차

1. 개요 특징
2. 설치 방법
3. 인증 방식 (JWT Bearer Token)
4. 응답 표준 형식
5. 전체 엔드포인트 목록 (22)
6. 인증 API — /api/v1/auth/*
7. 게시판/게시글 API — /api/v1/boards/*
8. 댓글 API — /api/v1/posts/*
9. 회원 API — /api/v1/members/*
10. 파일 업로드 API — /api/v1/upload/*
11. 관리자 화면 사용법
12. 테스트 방법 (테스터 / cURL / Postman / React Native)
13. 다른 플러그인에서 API 확장하기
14. 오류 코드 목록
15. 보안 권장 사항
 
 

1. 개요 특징

DX API 플러그인은 DXCMS 완전한 REST API 서버로 전환합니다. React Native, Flutter, Next.js, Vue 모든 프론트엔드와 외부 시스템에서 JSON으로 DXCMS 데이터를 활용할 있습니다.
 

주요 특징

•  Laravel 스타일 RESTful API — 동일한 URL 구조, 동일한 응답 형식
•  JWT-like Bearer Token 인증 — Access Token(1시간) + Refresh Token(30) + 토큰 로테이션
•  기존 DXCMS 코드 완전 비침습 기능 그대로 동작
•  플러그인 비활성화 API 완전 종료보안 제어 가능
•  기반 확장다른 플러그인이 엔드포인트 추가 가능 (dx_api_endpoints)
•  CORS 자동 처리 — React Native, 웹앱 즉시 연동
•  Rate Limiting — 로그인 분당 10, 회원가입 분당 5 제한
•  파일 업로드이미지/파일 분리, 게시판 설정 자동 적용, 2단계 연결 방식
•  PHP 5.6+ 호환외부 라이브러리 없음

 

카테고리 엔드포인트 주요 기능
인증 (Auth) 5 로그인, 회원가입, 로그아웃, 토큰 갱신, 정보
게시판/게시글 7 목록, 상세, 작성(file_ids 포함), 수정, 삭제
댓글 3 목록, 작성, 삭제
회원 3 프로필, 정보 수정, 비밀번호 변경
파일 업로드 4 이미지 업로드, 파일 업로드, 게시글 연결, 삭제
 
 
 

2. 설치 방법

파일 구조

plugins/
  dx-api/
    manifest.php
    plugin.php              ← 훅 등록, DB 테이블 설치
    admin/
      index.php             ← 관리자 화면 (대시보드 + 테스터)
    core/
      Request.php           ← 입력 처리 (Laravel Request 유사)
      Response.php          ← JSON 응답 표준화
      Router.php            ← 라우팅 엔진 + 미들웨어
      Token.php             ← JWT 토큰 발급/검증
    endpoints/
      auth.php              ← 인증 엔드포인트
      boards.php            ← 게시판/게시글 엔드포인트
      comments.php          ← 댓글 엔드포인트
      members.php           ← 회원 엔드포인트
      upload.php            ← 파일 업로드 엔드포인트

admin/
  dx-api/
    index.php               ← 관리자 라우팅 진입점

core/api/
  v1.php                    ← DXCMS dispatchApi() 진입점
 

설치 절차

1.  plugins/dx-api/ 폴더를 DXCMS 루트의 plugins/ 폴더에 복사
2.  admin/dx-api/index.php DXCMS 루트의 admin/dx-api/ 폴더에 복사
3.  core/api/v1.php DXCMS 루트의 core/api/ 폴더에 복사
4.  관리자플러그인 → DX API → 활성화
5.  활성화 api_tokens 테이블 자동 생성
6.  관리자사이트 → DX API 메뉴 확인
 
admin/index.php 사이드바에 DX API 메뉴가 없으면: 사이트 설정 바로 아래에 adm_nav('DX API', '/admin/dx-api', ...) 추가 필요
core/api/v1.php 복사를 빠뜨리면 /api/v1/* 모든 요청이 404 반환
 
 

3. 인증 방식 (JWT Bearer Token)

DX API 세션/쿠키 대신 Bearer Token 방식을 사용합니다. 인증이 필요한 모든 요청에 Authorization 헤더를 포함해야 합니다.
 

토큰 종류

토큰 유효시간 저장 위치 용도
Access Token 1시간 (3600) 메모리 / SecureStore API 요청 인증 헤더에 사용
Refresh Token 30 SecureStore / Keychain Access Token 만료 갱신
 

인증 흐름

1. 로그인
   POST /api/v1/auth/login
   → { access_token, refresh_token, expires_in: 3600, user: {...} }

2. API 요청 시 헤더 포함
   Authorization: Bearer {access_token}

3. Access Token 만료(1시간) 후 갱신
   POST /api/v1/auth/refresh
   Body: { "refresh_token": "..." }
   → 새 access_token + 새 refresh_token 발급 (기존 토큰 파기)

4. 로그아웃
   POST /api/v1/auth/logout
   → DB의 Refresh Token 파기, 모든 기기 로그아웃
토큰 로테이션: Refresh Token 사용 즉시 파기되고 토큰 발급. 탈취 재사용 불가.
IIS 환경: Authorization 헤더가 전달되지 않으면 web.config requestFiltering 설정 필요
 
 

4. 응답 표준 형식

성공 응답

{
  "success": true,
  "message": "성공 메시지",     // 선택
  "data": { ... }              // 단건: object / 목록: array
}


페이지네이션 응답

{
  "success": true,
  "data": [ ... ],
  "meta": {
    "total": 100,              // 전체 건수
    "page": 1,                 // 현재 페이지
    "per_page": 20,            // 페이지당 건수
    "last_page": 5,            // 마지막 페이지
    "has_more": true           // 다음 페이지 존재 여부
  }
}
 

에러 응답

{
  "success": false,
  "message": "에러 메시지",
  "code": "UNAUTHORIZED",       // 머신리더블 코드
  "errors": {                  // Validation 오류 시만
    "login_id": "아이디를 입력하세요.",
    "password": "비밀번호를 입력하세요."
  }
}
 

HTTP 상태 코드

코드 의미 발생 상황
200 OK 정상 처리
201 Created 게시글/댓글 작성, 파일 업로드, 회원가입 성공
400 Bad Request 잘못된 요청, 업로드 오류
401 Unauthorized 토큰 없음/만료, 로그인 실패
403 Forbidden 권한 없음, 파일 업로드 비허용 게시판
404 Not Found 게시판/게시글/파일 없음
422 Validation Error 필수 입력값 누락, 형식 오류
429 Too Many Requests Rate Limit 초과
500 Internal Server Error 서버/DB 오류
 
 
 

5. 전체 엔드포인트 목록 (22)

메서드 엔드포인트 설명 인증
POST /api/v1/auth/login 로그인토큰 발급  
POST /api/v1/auth/register 회원가입자동 로그인  
POST /api/v1/auth/logout 로그아웃토큰 파기 🔒
POST /api/v1/auth/refresh Access Token 갱신  
GET /api/v1/auth/me 정보 조회 🔒
GET /api/v1/boards 게시판 목록  
GET /api/v1/boards/:board_key 게시판 정보  
GET /api/v1/boards/:board_key/posts 게시글 목록 (페이지네이션) 권한별
GET /api/v1/boards/:board_key/posts/:id 게시글 상세 + 첨부파일 권한별
POST /api/v1/boards/:board_key/posts 게시글 작성 (file_ids 포함) 🔒
PUT /api/v1/boards/:board_key/posts/:id 게시글 수정 🔒
DELETE /api/v1/boards/:board_key/posts/:id 게시글 삭제 (소프트) 🔒
GET /api/v1/posts/:post_id/comments 댓글 목록  
POST /api/v1/posts/:post_id/comments 댓글 작성 🔒
DELETE /api/v1/posts/:post_id/comments/:id 댓글 삭제 🔒
GET /api/v1/members/:login_id 회원 공개 프로필  
PUT /api/v1/members/me 정보 수정 🔒
PUT /api/v1/members/me/password 비밀번호 변경 🔒
POST /api/v1/upload/image 이미지 업로드 (multipart) 🔒
POST /api/v1/upload/file 파일 업로드 (multipart) 🔒
POST /api/v1/upload/attach 파일-게시글 연결 🔒
DELETE /api/v1/upload/:file_id 임시 파일 삭제 🔒
 
 
 

6. 인증 API — /api/v1/auth/*

6-1. 로그인

 POST   /api/v1/auth/login
아이디 또는 이메일로 로그인. 성공 Access Token + Refresh Token 발급.

파라미터
이름 위치 필수 설명
login_id Body 필수 아이디 또는 이메일
password Body 필수 비밀번호

Request Body (JSON)
{"login_id": "admin","password": "비밀번호"}

Response
{"success": true,"data": {
  "token_type": "Bearer",
  "access_token": "eyJ...",
  "refresh_token": "a1b2c3...",
  "expires_in": 3600,
  "user": { "id": "...", "login_id": "admin", "name": "관리자", "role": "admin", "level": "9", "point": "500" }
}}
rate:10 — IP 분당 10 초과 429 반환
 

6-2. 회원가입

 POST   /api/v1/auth/register
신규 회원가입. 성공 자동 로그인되어 토큰 즉시 반환 (HTTP 201).

파라미터
이름 위치 필수 설명
login_id Body 필수 아이디 (4 이상)
password Body 필수 비밀번호 (6 이상)
name Body 필수 이름/닉네임
email Body 선택 이메일


Request Body (JSON)
{"login_id": "newuser","password": "pass1234","name": "홍길동","email": "user@email.com"}

Response
{"success": true,"message": "회원가입이 완료되었습니다.","data": { "token_type": "Bearer", "access_token": "...", ... }}
rate:5 — IP 분당 5 초과 429 반환
site_settings join_level=0 이면 가입 불가 (403)
 

6-3. 로그아웃

 POST   /api/v1/auth/logout
현재 기기의 Refresh Token 파기. 이후 토큰 갱신 불가.
🔒 Authorization: Bearer {access_token} 헤더 필요

Response
{"success": true,"message": "로그아웃 되었습니다."}
 

6-4. Access Token 갱신

 POST   /api/v1/auth/refresh
Refresh Token으로 Access Token 발급. 기존 Refresh Token 즉시 파기되고 토큰 발급 (토큰 로테이션).

파라미터
이름 위치 필수 설명
refresh_token Body 필수 로그인 발급된 refresh_token


Request Body (JSON)
{"refresh_token": "a1b2c3..."}

Response
{"success": true,"data": {"token_type": "Bearer","access_token": "eyJ...(새)","refresh_token": "d4e5f6...(새)","expires_in": 3600}}
기존 refresh_token 즉시 폐기됨. 새로 발급된 토큰만 유효.
 

6-5. 정보 조회

 GET   /api/v1/auth/me
현재 로그인한 사용자 정보 반환. 비밀번호 민감 정보는 제외.
🔒 Authorization: Bearer {access_token} 헤더 필요

Response
{"success": true,"data": {"id": "...","login_id": "admin","name": "관리자","email": "admin@example.com","role": "admin","level": "9","point": "500","profile_img": null}}
 
 
 

7. 게시판/게시글 API — /api/v1/boards/*

7-1. 게시판 목록

 GET   /api/v1/boards
활성화된 게시판 전체 목록 반환.

Response
{"success": true,"data": [{"id":"1","board_name":"자유게시판","board_key":"free","board_type":"normal","description":"...","read_level":"0","write_level":"1"},...]}
 

7-2. 게시글 목록

 GET   /api/v1/boards/:board_key/posts
게시글 목록 반환. 페이지네이션, 검색, 카테고리 필터 지원.

파라미터
이름 위치 필수 설명
board_key URL 필수 게시판 (: free)
page Query 선택 페이지 번호 (기본: 1)
per_page Query 선택 페이지당 건수 (기본: 20, 최대: 100)
q Query 선택 검색어 (제목+내용)
category Query 선택 카테고리 필터


Response
{"success": true,"data": [{"id":"...","title":"제목","category":"공지","view_count":"150","like_count":"12","comment_count":"5","thumbnail":"...","author_name":"홍길동","author_id":"hong","excerpt":"내용 미리보기...","created_at":"2026-07-22 09:00:00"}],"meta": {"total": 100,"page": 1,"per_page": 20,"last_page": 5,"has_more": true}}
게시판 read_level 따라 비로그인 접근 제한 가능
 

7-3. 게시글 상세

 GET   /api/v1/boards/:board_key/posts/:id
게시글 상세 + 첨부파일 목록 + 여분필드 반환. 조회수 자동 증가.

파라미터
이름 위치 필수 설명
board_key URL 필수 게시판
id URL 필수 게시글 ID

Response
{"success": true,"data": {"id":"...","title":"제목","content":"<p>본문HTML</p>","files": [{"id":"1","orig_name":"사진.jpg","file_ext":"jpg","file_size":"102400","url":"https://사이트.com/data/uploads/..."}],"extra_fields": {...},"author_name":"홍길동","created_at":"..."}}
files 에디터 삽입 이미지 제외, 직접 업로드한 파일만 포함
 

7-4. 게시글 작성

 POST   /api/v1/boards/:board_key/posts
게시글 작성. 업로드된 파일을 file_ids 연결 가능.
🔒 Authorization: Bearer {access_token} 헤더 필요

파라미터
이름 위치 필수 설명
board_key URL 필수 게시판

Request Body (JSON)
{"title": "게시글 제목","content": "<p>본문 HTML</p>","category": "공지","file_ids": [1, 2, 3]}

Response
{"success": true,"message": "게시글이 등록되었습니다.","data": {"id": "1784211424708247","post_id": "1784211424708247"}}
file_ids: /api/v1/upload/* 먼저 업로드 반환된 file_id 배열 전달
썸네일 자동 생성 (게시판 thumb 설정 적용)
 

7-5. 게시글 수정

 PUT   /api/v1/boards/:board_key/posts/:id
게시글 수정. 본인 또는 관리자만 가능.
🔒 Authorization: Bearer {access_token} 헤더 필요

파라미터
이름 위치 필수 설명
board_key URL 필수 게시판
id URL 필수 게시글 ID

Request Body (JSON)
{"title": "수정된 제목","content": "<p>수정된 내용</p>","category": "일반"}

Response
{"success": true,"message": "게시글이 수정되었습니다.","data": {"id": "..."}}
변경할 필드만 포함해도 (부분 수정 지원)
 

7-6. 게시글 삭제

 DELETE   /api/v1/boards/:board_key/posts/:id
게시글 소프트 삭제 (status=0). 본인 또는 관리자만 가능.
🔒 Authorization: Bearer {access_token} 헤더 필요

파라미터
이름 위치 필수 설명
board_key URL 필수 게시판
id URL 필수 게시글 ID

Response
{"success": true,"message": "게시글이 삭제되었습니다."}
소프트 삭제: DB에서 status=0 처리. 실제 데이터는 유지됨.
 
 
 

8. 댓글 API — /api/v1/posts/*

8-1. 댓글 목록

 GET   /api/v1/posts/:post_id/comments
댓글 목록. 기본 페이지당 50.

파라미터
이름 위치 필수 설명
post_id URL 필수 게시글 ID
page Query 선택 페이지 번호
per_page Query 선택 건수 (기본: 50)

Response
{"success": true,"data": [{"id":"...","content":"댓글 내용","parent_id":"0","depth":"0","is_secret":"0","author_name":"홍길동","author_id":"hong","created_at":"..."}],"meta": {"total": 30,"page": 1,"per_page": 50}}
parent_id=0: 최상위 댓글 / parent_id>0: 대댓글
 

8-2. 댓글 작성

 POST   /api/v1/posts/:post_id/comments
댓글 작성. 대댓글 지원.
🔒 Authorization: Bearer {access_token} 헤더 필요

파라미터
이름 위치 필수 설명
post_id URL 필수 게시글 ID

Request Body (JSON)
{"content": "댓글 내용","parent_id": 0}

Response
{"success": true,"message": "댓글이 등록되었습니다.","data": {"id": "..."}}
parent_id: 0=최상위, 부모댓글ID=대댓글
댓글 작성 posts.comment_count 자동 증가
 

8-3. 댓글 삭제

 DELETE   /api/v1/posts/:post_id/comments/:id
댓글 소프트 삭제. 본인 또는 관리자만 가능.
🔒 Authorization: Bearer {access_token} 헤더 필요

파라미터
이름 위치 필수 설명
post_id URL 필수 게시글 ID
id URL 필수 댓글 ID

Response
{"success": true,"message": "댓글이 삭제되었습니다."}
삭제 posts.comment_count 자동 감소 (최소 0)
 
 
 

9. 회원 API — /api/v1/members/*

9-1. 회원 공개 프로필

 GET   /api/v1/members/:login_id
회원 공개 정보 조회. 비밀번호 민감 정보 제외.

파라미터
이름 위치 필수 설명
login_id URL 필수 회원 로그인 아이디

Response
{"success": true,"data": {"id":"...","name":"홍길동","login_id":"hong","level":"5","point":"1200","bio":"자기소개","profile_img":null,"join_date":"2026-01-01 00:00:00"}}
 

9-2. 정보 수정

 PUT   /api/v1/members/me
이름, 이메일 수정. 변경할 항목만 포함.
🔒 Authorization: Bearer {access_token} 헤더 필요

Request Body (JSON)
{"name": "새이름","email": "new@email.com"}

Response
{"success": true,"message": "회원 정보가 수정되었습니다."}
name 또는 email 하나 이상 필요
 

9-3. 비밀번호 변경

 PUT   /api/v1/members/me/password
비밀번호 변경. 변경 성공 모든 기기 토큰 파기.
🔒 Authorization: Bearer {access_token} 헤더 필요

Request Body (JSON)
{"current_password": "현재비밀번호","new_password": "새비밀번호"}

Response
{"success": true,"message": "비밀번호가 변경되었습니다. 다시 로그인하세요."}
비밀번호 변경 모든 기기에서 재로그인 필요 (전체 Refresh Token 파기)
new_password 6 이상 필수
 
 
 

10. 파일 업로드 API — /api/v1/upload/*

2단계 방식으로 동작합니다. 먼저 파일을 업로드(file_id 반환)하고, 게시글 작성 file_ids 배열로 연결합니다.
【업로드 흐름】

1단계: 파일 업로드 (post_id=0으로 임시 저장)
  POST /api/v1/upload/image  또는  POST /api/v1/upload/file
  → { file_id: 42, url: "https://..." }

2단계: 게시글 작성 시 file_ids 포함 (자동 연결)
  POST /api/v1/boards/free/posts
  Body: { "title": "...", "content": "...", "file_ids": [42, 43] }

또는 게시글 작성 후 별도 연결
  POST /api/v1/upload/attach
  Body: { "post_id": "...", "file_ids": [42, 43] }
 

10-1. 이미지 업로드

 POST   /api/v1/upload/image
이미지 파일만 허용. multipart/form-data 전송.
🔒 Authorization: Bearer {access_token} 헤더 필요

파라미터
이름 위치 필수 설명
file Form 필수 이미지 파일 (jpg, jpeg, png, gif, webp, bmp)
board_key Form 필수 게시판 (업로드 권한 썸네일 설정 적용)

Request Body (JSON)
// multipart/form-data 전송 (JSON 아님)
// React Native FormData 사용

Response
{"success": true,"message": "이미지가 업로드되었습니다.","data": {"file_id": 42,"url": "https://사이트.com/data/uploads/boards/free/20260722_abc123.jpg","save_path": "boards/free/20260722_abc123.jpg","orig_name": "product.jpg","file_size": 102400,"file_ext": "jpg","is_image": true}}
Content-Type: multipart/form-data (JSON 아님)
게시판 use_file=0 이면 403 반환
시스템 upload_max_size 초과 400 반환
이미지 getimagesize() 검증위장 파일 차단
썸네일 자동 생성 (게시판 thumb 설정 적용)
 

10-2. 파일 업로드

 POST   /api/v1/upload/file
이미지 포함 모든 파일. 위험 확장자 자동 차단.
🔒 Authorization: Bearer {access_token} 헤더 필요

파라미터
이름 위치 필수 설명
file Form 필수 업로드할 파일
board_key Form 필수 게시판

Request Body (JSON)
// multipart/form-data 전송

Response
{"success": true,"data": {"file_id": 43,"url": "https://...","orig_name": "document.pdf","file_ext": "pdf","is_image": false,...}}
차단 확장자: php, exe, sh, bat, js, jar, asp, jsp 20
시스템 upload_allowed_ext 설정 자동 적용
 

10-3. 파일-게시글 연결

 POST   /api/v1/upload/attach
업로드된 임시 파일(post_id=0) 게시글에 연결.
🔒 Authorization: Bearer {access_token} 헤더 필요

Request Body (JSON)
{"post_id": "1784211424708247","file_ids": [42, 43]}

Response
{"success": true,"message": "2개 파일이 게시글에 연결되었습니다.","data": {"post_id": "...","attached": 2,"requested": 2}}
이미 다른 게시글에 연결된 파일(post_id!=0) 연결 불가 (보안)
본인 게시글 또는 관리자만 가능
 

10-4. 임시 파일 삭제

 DELETE   /api/v1/upload/:file_id
게시글에 연결되지 않은 임시 파일(post_id=0) 삭제.
🔒 Authorization: Bearer {access_token} 헤더 필요

파라미터
이름 위치 필수 설명
file_id URL 필수 삭제할 파일 ID

Response
{"success": true,"message": "파일이 삭제되었습니다."}
post_id=0 파일만 삭제 가능. 이미 게시글에 연결된 파일 보호.
물리 파일 + 썸네일 + DB 레코드 모두 삭제
 
 
 

11. 관리자 화면 사용법

관리자사이트 → DX API 또는 /admin/dx-api 접속
 

대시보드

항목 설명
활성 토큰 현재 유효한 Refresh Token
연결된 기기 토큰이 발급된 고유 회원
엔드포인트 등록된 API 엔드포인트 (22)
인증 방식 Bearer Token 방식 표시
 

API 테스터 사용법

1.  게시판 선택 드롭다운에서 테스트할 게시판 선택
2.  게시글 ID 입력 (댓글/게시글 상세 테스트 )
3.  상단 프리셋 버튼 클릭 (로그인, 게시글 목록, 댓글 작성, 파일 업로드 )
4.  파일 업로드 프리셋 선택 파일 선택 필드 자동 표시
5.  전송 버튼 클릭
6.  로그인 성공 "access_token 자동입력" 버튼 클릭이후 요청에 자동 포함
 

토큰 관리

•  활성 토큰 목록: 회원별 발급일시, 만료일시 확인
•  개별 파기: 특정 회원의 기기에서 로그아웃 처리
•  전체 파기: 전체 사용자 재로그인 필요 (긴급 보안 조치)
 
토큰 파기 후에도 만료되지 않은 Access Token 최대 1시간 유효. 즉각 차단은 플러그인 비활성화.
 
 

12. 테스트 방법

12-1. 관리자 내장 테스터

/admin/dx-api 에서 브라우저로 바로 테스트 가능. 별도 도구 불필요.

12-2. cURL

로그인
curl -X POST https://사이트.com/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"login_id":"admin","password":"비밀번호"}'

게시글 목록
curl https://사이트.com/api/v1/boards/free/posts

인증 요청
curl https://사이트.com/api/v1/auth/me \
  -H "Authorization: Bearer eyJ..."

게시글 작성
curl -X POST https://사이트.com/api/v1/boards/free/posts \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer eyJ..." \
  -d '{"title":"테스트 글","content":"<p>내용</p>"}'

이미지 업로드 (cURL)
curl -X POST https://사이트.com/api/v1/upload/image \
  -H "Authorization: Bearer eyJ..." \
  -F "file=@/path/to/image.jpg" \
  -F "board_key=free"

12-3. Postman

1.  컬렉션 생성: DX API
2.  환경 변수 설정: BASE_URL = https://사이트.com, ACCESS_TOKEN = (비어있음)
3.  로그인 요청 Tests 탭에 자동 토큰 저장 스크립트 추가:
const res = pm.response.json();
if (res.success) {
  pm.environment.set("ACCESS_TOKEN", res.data.access_token);
  pm.environment.set("REFRESH_TOKEN", res.data.refresh_token);
}
4.  이후 모든 요청 Authorization → Bearer Token → {{ACCESS_TOKEN}}
5.  파일 업로드: Body → form-data → file 키에 파일 선택

12-4. React Native

api.js — Axios 클라이언트 설정
import axios from "axios";
import AsyncStorage from "@react-native-async-storage/async-storage";

const api = axios.create({
  baseURL: "https://사이트.com/api/v1",
  headers: { "Content-Type": "application/json" },
  timeout: 10000,
});

// 요청 인터셉터 — 토큰 자동 주입
api.interceptors.request.use(async (config) => {
  const token = await AsyncStorage.getItem("access_token");
  if (token) config.headers.Authorization = `Bearer ${token}`;
  return config;
});

// 응답 인터셉터 — 401 시 토큰 자동 갱신
api.interceptors.response.use(
  (res) => res,
  async (error) => {
    if (error.response?.status === 401) {
      const refresh = await AsyncStorage.getItem("refresh_token");
      if (refresh) {
        try {
          const res = await axios.post(
            "https://사이트.com/api/v1/auth/refresh",
            { refresh_token: refresh }
          );
          if (res.data.success) {
            const { access_token, refresh_token } = res.data.data;
            await AsyncStorage.setItem("access_token", access_token);
            await AsyncStorage.setItem("refresh_token", refresh_token);
            error.config.headers.Authorization = `Bearer ${access_token}`;
            return axios(error.config);
          }
        } catch(e) {}
      }
    }
    return Promise.reject(error);
  }
);

export default api;

로그인
async function login(loginId, password) {
  const res = await api.post("/auth/login", { login_id: loginId, password });
  const { access_token, refresh_token, user } = res.data.data;
  await AsyncStorage.setItem("access_token", access_token);
  await AsyncStorage.setItem("refresh_token", refresh_token);
  return user;
}

이미지 업로드 (React Native)
async function uploadImage(uri, boardKey) {
  const form = new FormData();
  form.append("file", {
    uri,
    type: "image/jpeg",
    name: "photo.jpg",
  });
  form.append("board_key", boardKey);

  const res = await api.post("/upload/image", form, {
    headers: { "Content-Type": "multipart/form-data" },
  });
  return res.data.data.file_id; // file_id 반환
}

// 파일 업로드 후 게시글 작성
async function createPost(boardKey, title, content, fileIds) {
  const res = await api.post(`/boards/${boardKey}/posts`, {
    title,
    content,
    file_ids: fileIds,
  });
  return res.data.data.post_id;
}
 
 
 

13. 다른 플러그인에서 API 확장하기

dx_api_endpoints 훅으로 어떤 플러그인에서도 엔드포인트를 추가할 있습니다.
 
// plugins/my-shop/plugin.php

dx_add_hook("dx_api_endpoints", function() {

  // GET /api/v1/shop/products
  DxApiRouter::get("/v1/shop/products", function($params) {
    $db = Database::getInstance();
    $pg = DxApiRequest::pagination(20);
    $limit  = (int)$pg["per_page"];
    $offset = (int)$pg["offset"];
    $items = $db->rows(
      "SELECT * FROM `{$db->table('products')}` WHERE status=1
       ORDER BY id DESC LIMIT {$limit} OFFSET {$offset}"
    );
    $total = (int)$db->value(
      "SELECT COUNT(*) FROM `{$db->table('products')}` WHERE status=1"
    );
    DxApiResponse::paginate($items, $total, $pg["page"], $pg["per_page"]);
  });

  // POST /api/v1/shop/orders (인증 필요)
  DxApiRouter::post("/v1/shop/orders", function($params) {
    $payload   = isset($GLOBALS["dx_api_user"]) ? $GLOBALS["dx_api_user"] : array();
    $productId = DxApiRequest::int("product_id");
    // ... 주문 처리
    DxApiResponse::ok(array("order_id" => $orderId), "주문 완료", 201);
  }, array("auth"));

});
 
 
 

14. 오류 코드 목록

code HTTP 설명 해결 방법
LOGIN_FAILED 401 아이디/비밀번호 불일치 자격증명 확인
UNAUTHORIZED 401 토큰 없음 또는 만료 토큰 갱신 또는 재로그인
INVALID_REFRESH_TOKEN 401 만료/사용된 Refresh Token 재로그인 필요
FORBIDDEN 403 접근 권한 없음 권한 레벨 또는 소유자 확인
NOT_FOUND 404 리소스 없음 URL/ID 확인
VALIDATION_ERROR 422 입력값 오류 errors 필드 확인
TOO_MANY_REQUESTS 429 Rate Limit 초과 잠시 재시도
UPLOAD_ERROR 400 파일 업로드 오류 파일 선택 여부 확인
FILE_TOO_LARGE 400 파일 크기 초과 upload_max_size 설정 확인
FILE_NOT_ALLOWED 400 허용되지 않는 확장자 허용 확장자 확인
INVALID_IMAGE 400 유효하지 않은 이미지 실제 이미지 파일인지 확인
UPLOAD_NOT_ALLOWED 403 게시판 파일 업로드 비허용 게시판 use_file 설정 확인
UPLOAD_FAILED 500 서버 파일 저장 실패 data/uploads/ 권한 확인
INTERNAL_SERVER_ERROR 500 서버 오류 서버 로그 확인
PLUGIN_NOT_FOUND 404 dx-api 플러그인 미설치 v1.php 복사 여부 확인
PLUGIN_DISABLED 503 dx-api 비활성화 관리자플러그인 활성화
 
 
 

15. 보안 권장 사항

필수

•  HTTPS 필수 — HTTP에서는 Bearer Token 평문 전송됨
•  Access Token 메모리에만 저장
•  Refresh Token AsyncStorage 대신 Expo SecureStore / iOS Keychain / Android Keystore 사용
•  data/uploads/ 폴더 권한 755 설정
 

권장

•  프로덕션에서 CORS origin * 대신 실제 도메인으로 제한 (Response.php 수정)
•  관리자 화면에서 주기적으로 만료 토큰 정리
•  의심스러운 활동 관리자 화면전체 토큰 파기
•  API 불필요 플러그인 비활성화로 완전 차단
 

CORS 도메인 제한 (Response.php)

// plugins/dx-api/core/Response.php send() 메서드에서
// 변경 전:
header("Access-Control-Allow-Origin: *");

// 변경 후:
$allowed = array("https://myapp.com", "https://admin.myapp.com");
$origin  = isset($_SERVER["HTTP_ORIGIN"]) ? $_SERVER["HTTP_ORIGIN"] : "";
if (in_array($origin, $allowed)) {
  header("Access-Control-Allow-Origin: " . $origin);
}
첨부파일 1개 회원전용

댓글1

D
DX관리자 2026.07.23 03:31

아직 완벽한 수준은 아니지만, 시니어 개발자님들의 의견을 듣고 더 나은 플러그인으로 발전시키기 위해 먼저 배포하게 되었습니다.

부족한 부분이나 개선이 필요한 사항이 있다면 아낌없이 말씀해 주시면 감사하겠습니다. 보내주시는 피드백 하나하나를 소중히 받아들이고, 배우는 자세로 적극 반영하도록 하겠습니다.

많은 조언과 검토를 부탁드립니다. 감사합니다.

로그인 후 댓글을 작성할 수 있습니다.
Plugin 19
41
전체 회원
1,354
전체 게시글
2,397
전체 댓글
8
오늘 방문
51,035
전체 방문
2
현재 접속
인기글 7일 이내
최신글
최신댓글
내 플레이리스트
플레이리스트가 비어있습니다
스튜디오 게시판에서
플레이리스트에 담기 버튼을
눌러보세요
목록
목록