01 — 소개
Mobius 소개
Mobius란 무엇인가
Mobius는 사물인터넷 기기가 보내온 데이터를 받아 저장하고, 애플리케이션이 그 데이터를 가져다 쓸 수 있도록 공개하는 서버 소프트웨어입니다.
이런 서버를 각자 알아서 만들면 주소를 정하는 규칙도, 데이터를 담는 형식도, 권한을 다루는 방식도 제품마다 제각각이 됩니다. oneM2M은 바로 그 부분을 국제 표준으로 정리한 규격이며, Mobius는 이 표준에서 최상위 서버 역할을 맡는 IN-CSE(Infrastructure Node — Common Services Entity)를 구현한 플랫폼입니다. 표준을 그대로 따르기 때문에 Mobius에 맞춰 개발한 기기와 애플리케이션은 다른 oneM2M 플랫폼에 연결해도 수정 없이 동작합니다.
Mobius는 Node.js로 개발되었으며 소스 코드가 공개되어 있습니다. 데이터 저장소로는 MySQL과 SQLite를 지원하고, 둘 중 무엇을 사용할지는 설정 항목 하나로 결정합니다.
기기는 데이터를 올리고 애플리케이션은 그 데이터를 가져갑니다. Mobius는 두 축 사이에서 저장과 인증, 알림을 담당합니다.
어디에 놓이는가
Mobius는 기기와 애플리케이션 사이에 자리 잡는 미들웨어입니다. 기기가 측정값을 올리면 Mobius가 이를 저장하고, 애플리케이션은 표준 REST API를 통해 저장된 값을 조회합니다. 반대 방향도 마찬가지여서, 애플리케이션이 제어 값을 기록하면 해당 리소스를 구독하고 있던 기기가 그 변화를 알림으로 전달받습니다.
리소스 트리가 곧 주소가 된다
oneM2M의 데이터는 파일 탐색기의 폴더처럼 트리 구조로 쌓이며, 트리를 따라 내려간 경로가 그대로 URL이 됩니다. 그래서 리소스를 하나 만들 때마다 그것을 읽고 쓰기 위한 API를 따로 설계할 필요가 없습니다. 리소스가 만들어지는 순간 접근할 주소도 함께 생기기 때문입니다.
구조에 제약도 거의 없습니다. 컨테이너 아래에 컨테이너를 다시 만들 수 있고 깊이도 제한하지
않습니다. 모든 리소스에는 표준이 정한 속성이 함께 붙는데, 만들어진 시각(ct)과
이름(rn), 만료 시각(et), 적용된 접근 정책(acpi) 등이
여기에 해당합니다.
다루는 리소스 타입
Mobius가 만들 수 있는 리소스 타입은 다음과 같습니다. 표의 ty 번호는 요청의
Content-Type에 함께 적어, 어떤 리소스를 만들 것인지 서버에 알려 주는 값입니다.
| ty | 이름 | 하는 일 |
|---|---|---|
| 1 | accessControlPolicy | 누가 무엇을 할 수 있는지 정하는 접근 정책 |
| 2 | AE | 기기나 애플리케이션 하나. 트리로 들어오는 입구가 된다 |
| 3 | container | 데이터를 담는 그릇. 보관 한도를 지정할 수 있다 |
| 4 | contentInstance | 실제 데이터 한 건. 만든 뒤에는 수정하지 않는다 |
| 5 | CSEBase | 트리의 뿌리. Mobius 자신을 가리킨다 |
| 9 | group | 여러 리소스를 묶어 한 번에 다룬다 |
| 10 | locationPolicy | 위치 정보를 수집하는 방식을 정한다 |
| 13 | mgmtObj | 기기 관리 객체. 펌웨어, 배터리, 기기 정보 등 |
| 14 | node | 물리적인 노드 |
| 16 | remoteCSE | 연결되어 있는 다른 CSE |
| 23 | subscription | 변화를 지켜보다가 알림을 보낸다 |
| 24 | semanticDescriptor | 의미 정보를 서술한다 |
| 27 | multimediaSession | 멀티미디어 세션 |
| 28 | flexContainer | 속성을 자유롭게 정의하는 컨테이너 |
| 91–98 | hd_* | 홈 도메인 moduleClass. 조명, 색상, 문열림, 온도, 배터리 등 |
SQLite 백엔드가 지원하는 타입은 accessControlPolicy, AE,
container, contentInstance, CSEBase,
subscription 여섯 가지입니다. 목록에 없는 타입을 만들려고 하면 저장을 시도하기
전에 501로 거절하므로, 리소스 트리가 절반만 만들어진 채 남는 일은 생기지
않습니다. 모든 타입이 필요한 환경이라면 MySQL로 실행하시기 바랍니다.
02 — 구조
내부 구조
Mobius의 모듈은 요청 하나가 처리되는 경로를 기준으로 나뉘어 있습니다. 이 경로를 알아 두면 어떤 기능을 손볼 때 어느 파일을 열어야 하는지 자연스럽게 정해집니다.
요청이 처리되는 경로
HTTP 요청이 들어오면 아래 순서를 따라 처리됩니다. 각 단계는 자신이 맡은 일만 끝내고 다음 단계로 넘깁니다.
app.js에서 검증을 통과한 뒤 resource.js로 넘어갑니다. 응답은 언제나 responder.js 한 곳에서만 나갑니다.
이 흐름은 두 가지 규칙 위에서 유지됩니다. 첫째, 응답을 보내고 데이터베이스 커넥션을
반납하는 일은 요청마다 반드시 한 번만 일어납니다. 둘째, 실제로 응답을
내보내는 코드는 responder.js 한 곳에만 있습니다. 응답을 두 번
보내거나 아예 보내지 못하면 커넥션이 반납되지 못한 채 묶이기 때문에, 이 두 규칙을 코드
구조 자체로 고정해 두었습니다.
모듈이 나뉜 기준
실제로 자주 들여다보게 되는 파일들을 정리했습니다.
| 파일 | 맡은 일 |
|---|---|
mobius.js | 진입점입니다. 설정을 읽고, 포트를 확인하고, 부팅 기록을 남긴 뒤 서버를 실행하는 순서만 잡습니다 |
app.js | HTTP 서버와 라우팅을 맡습니다. 워커를 만들고 들어오는 요청을 받습니다 |
mobius/resource.js | 리소스 조작의 핵심입니다. 타입별 속성 검사와 생성·갱신 동작이 여기에 있습니다 |
mobius/sql_action.js | 모든 SQL을 만드는 곳입니다. 질의 빌더로 조립하고 값은 바인딩으로 넘깁니다 |
mobius/db/ | 데이터베이스 파사드와 백엔드 어댑터, 스키마 파일이 모여 있습니다 |
mobius/security.js | 접근 정책을 검사해, 요청자가 그 리소스를 다룰 수 있는지 판단합니다 |
mobius/sgn.js · sgn_man.js | 구독을 찾아 알림 본문을 만들고 HTTP·CoAP·MQTT로 내보냅니다 |
mobius/responder.js · shape.js | 응답이 나가는 유일한 출구와, 응답 본문을 만드는 곳입니다 |
mobius/settle.js | 요청 하나의 정산기입니다. 응답 전송과 커넥션 반납이 한 번만 일어나도록 보장합니다 |
mobius/conf_load.js · conf_schema.js | 설정을 읽어 들이는 곳과, 어떤 설정 항목이 있는지에 대한 단일 기준입니다 |
tools/ | 첫 설치 마법사와 설정 명령, 스키마 마이그레이션 러너가 들어 있습니다 |
admin/ | 관리 콘솔입니다. 별도 프로세스로 실행됩니다 |
마스터와 워커
Mobius는 CPU 코어 수만큼 워커 프로세스를 띄워 요청을 나누어 처리합니다. 마스터 프로세스는 요청 처리에 참여하지 않고, 주기적으로 도는 두 가지 작업만 담당합니다.
- 보관 한도 정리: 컨테이너에 지정한 개수나 용량 한도를 넘어선 오래된 데이터를 삭제합니다. 삭제하기 직전에 실제 데이터 개수를 다시 세기 때문에, 저장된 카운터 값이 실제보다 부풀려져 있더라도 한도 안에 있는 데이터까지 지우는 일은 없습니다.
- 카운터 정합: 컨테이너가 기록하고 있는 개수와 용량 값을 실제 데이터와 다시 맞춥니다.
워커가 예기치 않게 종료되면 마스터가 곧바로 새 워커를 띄웁니다. 다만 포트가 이미 사용 중이거나 설정 파일이 없는 경우, 또는 비밀 값의 봉인이 맞지 않는 경우처럼 설정 문제로 종료된 것이라면 다시 띄우지 않고 마스터도 같은 코드로 함께 종료합니다. 사람이 고쳐야 할 문제를 재시작으로 덮어 버리지 않기 위한 동작입니다.
데이터베이스 계층
SQL을 만드는 곳과 실행하는 곳이 분리되어 있습니다. sql_action.js가 질의를
완성하면 mobius/db/의 파사드가 현재 선택된 백엔드의 어댑터에 전달해 실행합니다.
덕분에 코어 코드는 자신이 MySQL 위에서 도는지 SQLite 위에서 도는지 알 필요가 없습니다.
이런 구조라서 백엔드를 바꾸는 데는 설정 한 줄이면 충분하고, 새로운 백엔드를 추가할 때도
mobius/db/<이름>.js 파일 하나를 만들어 두는 것으로 끝납니다. 또한 질의에
들어가는 값은 예외 없이 바인딩으로 전달되므로, 탐색 질의의 파라미터를 통한 SQL 주입은
구조적으로 발생할 수 없습니다.
디렉터리 구조
mobius.js # 진입점. 이 파일을 실행한다 app.js # HTTP 서버 · 워커 · 라우팅 package.json # 의존 모듈과 npm 명령 conf.json # 설정. 처음 실행할 때 만들어진다 mobius/ # 코어 resource.js # 리소스 조작의 핵심 sql_action.js # SQL 조립 security.js # 접근 정책 sgn.js sgn_man.js # 구독 알림 responder.js # 응답 출구 conf_load.js … # 설정 로딩 ae.js cnt.js … # 리소스 타입별 처리 db/ index.js # 백엔드 파사드 mysql.js # MySQL 어댑터 sqlite.js # SQLite 어댑터 mobiusdb.sql # MySQL 스키마. 설치할 때 이 파일을 넣는다 mobiusdb_sqlite.sql tools/ # 설치 마법사 · 설정 명령 admin/ # 관리 콘솔 (별도 프로세스) log/ # 액세스 로그와 부팅 기록
03 — 설치
설치
준비물
| 구성 요소 | 필요 여부 | 설명 |
|---|---|---|
| Node.js | 필수 | Mobius를 실행하는 런타임입니다. LTS 버전을 권장합니다 |
| MySQL | 선택 | 운영 환경의 저장소입니다. 모든 리소스 타입을 다루려면 필요합니다 |
| SQLite | 자동 | 모듈을 설치할 때 함께 들어오므로 따로 준비할 것이 없습니다 |
| MQTT 브로커 | 선택 | 알림을 mqtt:// 주소로 보낼 때만 필요합니다. Mosquitto 등 |
SQLite로 시작하시길 권합니다. 별도의 데이터베이스 서버를 설치하지 않아도 되고, 처음 실행할 때 저장소가 자동으로 만들어집니다. Node.js 하나만 준비되어 있으면 됩니다.
설치 순서
Node.js 설치
nodejs.org에서 LTS 버전을 내려받아 설치합니다. 설치를 마쳤다면 터미널에서 아래 두 명령으로 버전이 정상적으로 출력되는지 확인합니다.
node -v npm -v
데이터베이스 준비
SQLite를 사용할 때
따로 준비할 것이 없습니다. 다음 단계에서 npm install을 실행하면 SQLite 모듈이
함께 설치되고, 서버를 처음 띄울 때 저장소 파일과 테이블이 자동으로 만들어집니다.
MySQL을 사용할 때
MySQL 서버를 설치한 다음 데이터베이스를 하나 만들고 스키마 파일을 넣어 주면 됩니다. 이 한 번의 작업으로 설치가 끝납니다. 스키마 파일 안에 테이블과 인덱스는 물론 마이그레이션 이력까지 들어 있어서, 그 뒤에 따로 적용해야 할 작업이 남지 않기 때문입니다.
# 1. 데이터베이스를 만든다 mysql -u root -p -e "CREATE DATABASE mobiusdb DEFAULT CHARACTER SET utf8mb3;" # 2. 스키마를 넣는다 (Mobius 소스 폴더에서 실행) mysql -u root -p mobiusdb < mobius/db/mobiusdb.sql
MySQL Workbench 같은 도구를 사용한다면 mobiusdb 스키마를 만든 뒤
Data Import → Import from Self-Contained File 메뉴에서
mobius/db/mobiusdb.sql을 선택해도 결과는 같습니다.
기본값은 mobiusdb입니다. 다른 이름을 쓰고 싶다면 설치를 마친 뒤
npm run conf -- set dbName <이름> 명령으로 변경하면 됩니다.
MQTT 브로커 (선택)
구독 알림을 mqtt:// 주소로 보낼 계획이라면 브로커가 필요합니다.
Mosquitto를 설치한 뒤 서비스를 실행해 두시면
됩니다. 알림을 HTTP 주소로만 받을 예정이라면 이 단계는 건너뛰어도 무방합니다.
소스와 모듈 설치
-
소스 내려받기
터미널git clone https://github.com/IoTKETI/Mobius.git cd Mobius
Git이 설치되어 있지 않다면 GitHub 페이지에서 ZIP 파일로 내려받아 압축을 풀어도 됩니다.
-
의존 모듈 설치
터미널npm install명령이 끝나고
node_modules폴더가 만들어지면 정상입니다. Node.js는 별도의 빌드 과정이 없으므로 설치는 여기서 끝납니다.
04 — 구동
구동
처음 실행하기
설정 파일 conf.json이 없으면 Mobius가 스스로 설정 마법사를 실행합니다. 설치를
위한 별도의 명령을 외워 둘 필요가 없습니다.
node mobius.js
일곱 가지 항목을 차례로 물어본 다음, 입력한 값을 conf.json에 저장하고 곧바로
서버를 실행합니다.
| 묻는 항목 | 기본값 | 설명 |
|---|---|---|
| 데이터베이스 | mysql | mysql 또는 sqlite 중에서 고릅니다 |
| DB 비밀번호 | — | MySQL을 골랐을 때만 묻습니다. 입력하는 동안 화면에 표시되지 않습니다 |
| CSE 이름 | Mobius | 트리의 뿌리 이름입니다. 모든 주소가 /이름으로 시작합니다 |
| CSE-ID | /Mobius2 | 이 CSE를 가리키는 식별자입니다 |
| SP-ID | //keti.re.kr | 서비스 제공자를 가리키는 식별자입니다 |
| 수퍼유저 Origin | Sponde | 이 값으로 보낸 요청은 접근 검사를 모두 통과합니다 |
| HTTP 포트 | 7579 | 서버가 열어 둘 포트입니다 |
이 값을 요청 헤더에 넣으면 모든 접근 제어를 그대로 통과합니다. 외부에 공개되는 서버라면 기본값을 그대로 두지 말고 추측하기 어려운 값으로 바꾼 뒤, 값을 아는 사람의 범위를 최소한으로 관리하시기 바랍니다.
CSE 이름과 포트를 기본값인 Mobius와 7579로 두었다면, 이제 리소스
트리의 최상위 주소는 http://localhost:7579/Mobius가 됩니다.
다시 실행할 때
node mobius.js # conf.json 의 설정을 그대로 사용 npm start # 위와 같은 명령 node mobius.js sqlite # 이번 실행만 SQLite 로 node mobius.js mysql # 이번 실행만 MySQL 로
pm2나 systemd처럼 터미널이 없는 환경에서 처음 실행하면 마법사를 띄울 수 없기 때문에,
설정 파일을 만들지 않고 그대로 종료합니다. 터미널에서 한 번 실행해
conf.json을 만들어 둔 다음 서비스에 등록하시기 바랍니다.
정상 동작 확인
최상위 리소스를 한 번 조회해 봅니다. 아래와 같은 응답이 돌아오면 서버가 정상적으로 동작하고 있는 것입니다.
curl -i http://localhost:7579/Mobius \ -H "Accept: application/json" \ -H "X-M2M-RI: check1" \ -H "X-M2M-Origin: Sponde"
HTTP/1.1 200 OK
X-M2M-RSC: 2000
Content-Type: application/json
{"m2m:cb": {"rn": "Mobius", "ty": 5, "csi": "/Mobius2", …}}
여기서 X-M2M-RSC: 2000이 oneM2M이 정한 성공 코드입니다. HTTP 상태 코드와는
별개로 붙는 값이므로, 응답을 확인할 때는 두 가지를 함께 보는 것이 좋습니다.
설정 확인과 변경
설정은 웹 화면이 아니라 명령으로 다룹니다. conf.json 안에는 데이터베이스
비밀번호와 수퍼유저 값처럼 민감한 정보가 들어 있어서, 서버에 직접 접속할 수 있는 사람만
다루도록 의도한 것입니다.
npm run conf # 전체 목록과 적용 상태를 본다 npm run conf -- csebaseport # 한 항목만 자세히 본다 npm run conf -- set cseBase Vita # 값을 바꾼다 npm run conf -- unset maxBodyBytes # 기본값으로 되돌린다 npm run conf -- edit # 주요 항목을 차례로 다시 묻는다 npm run conf -- --all # 고급 항목까지 함께 본다 npm run status # 실행 중인지, 재시작이 필요한지 확인한다
설정 파일은 서버를 시작할 때 한 번만 읽습니다. 따라서 값을 바꿨다면 재시작해야 실제로
반영되며, 어떤 항목이 재시작을 기다리고 있는지는 npm run conf 목록에서 확인할
수 있습니다.
CSE 이름이나 포트처럼 잘못 바꾸면 기존 기기가 접속하지 못하게 되는 항목에는 관문 표시가 붙습니다. 이런 항목을 바꾸려고 하면 먼저 경고 문구를 보여 주고, 항목 이름을 직접 입력해야 다음으로 넘어갑니다.
비밀 값 봉인
데이터베이스 비밀번호와 수퍼유저 값은 반드시 전용 명령으로만 변경합니다. 편집기로
conf.json을 열어 이 두 값을 직접 고치면 다음 실행이 거부됩니다. 같은 위치에
있는 conf.seal.json이 두 값의 지문을 보관하고 있다가, 내용이 어긋나면 시작
단계에서 걸러 내기 때문입니다.
npm run setup -- --dbpass # DB 비밀번호를 다시 입력한다 npm run setup -- --superuser # 수퍼유저 Origin 을 다시 입력한다
두 명령 모두 입력 프롬프트에서 Enter만 누르면 기존 값은 그대로 두고 봉인만 다시 만듭니다. 이전 버전에서 올라와 아직 봉인이 없는 상태라면, 이 방법으로 한 번 만들어 두시면 됩니다.
관리 콘솔
관리 콘솔은 Mobius와 같은 저장소에 들어 있지만 별도의 프로세스로 동작하는 운영자용 화면입니다. 만료된 리소스나 부모가 사라진 리소스를 찾아 정리하고, 접근 정책을 검토하며, 구독 현황과 통계를 확인할 수 있습니다.
# 비밀번호를 먼저 정한다. 정하지 않으면 콘솔이 실행되지 않는다 npm run conf -- set adminPassword '사용할-비밀번호' # 콘솔을 실행한다 node admin/server.js # 또는 Mobius 가 함께 띄우게 한다 (마스터의 자식 프로세스로 실행된다) npm run conf -- set adminAutoStart on
다만 설정 변경과 프로세스 제어 기능은 콘솔에 넣지 않았습니다. 이 두 가지는 앞서 설명한 명령으로만 다룹니다.
실행되지 않을 때
Mobius는 실행에 실패한 이유를 종료 코드로 구분해 알려 줍니다. 프로세스는 살아 있는데 요청은 받지 못하는 어중간한 상태로 남지 않도록 하기 위해서입니다.
| 코드 | 의미 | 해결 방법 |
|---|---|---|
| 12 | 포트가 이미 사용 중입니다 | 해당 포트를 쓰는 프로세스를 종료하거나 csebaseport를 바꿉니다 |
| 13 | conf.json이 없습니다 | 터미널에서 node mobius.js를 한 번 실행해 만듭니다 |
| 14 | 비밀 값의 봉인이 맞지 않습니다 | npm run setup -- --superuser로 봉인을 다시 만듭니다 |
| 1 | 데이터베이스에 연결하지 못했습니다 | DB가 실행 중인지, 비밀번호와 데이터베이스 이름이 맞는지 확인합니다 |
요청별 처리 시간은 화면에 출력되지 않고 log/access-*.log의 마지막 항목에
기록됩니다. 현재 어떤 설정으로 실행 중인지는 log/mobius-boot.jsonl에 남으며,
npm run status가 이 기록을 읽어 정리해 보여 줍니다.
05 — 활용
활용
아래 예제는 위에서부터 순서대로 실행하면 그대로 동작합니다. 서버가
localhost:7579에서 Mobius라는 이름으로 실행 중이라고 가정했습니다.
요청의 기본 형태
Mobius에 보내는 모든 요청에는 최소한 두 개의 헤더가 필요합니다.
| 헤더 | 역할 |
|---|---|
X-M2M-RI | 요청 식별자입니다. 값은 자유롭게 정하되 요청마다 다르게 넣으며, 응답에 그대로 돌아옵니다 |
X-M2M-Origin | 요청자 식별자입니다. 접근 제어는 이 값을 기준으로 판단합니다 |
Content-Type | 리소스를 만들 때만 필요합니다. application/json; ty=<번호> 형태로 무엇을 만들지 알려 줍니다 |
Accept | application/json을 넣습니다. Mobius는 JSON만 주고받습니다 |
AE 등록
기기나 애플리케이션은 무엇보다 먼저 AE로 등록해야 합니다. 리소스 트리 안에
자신의 자리를 만드는 과정이라고 보시면 됩니다. X-M2M-Origin 헤더에 정확히
S라고 적어 보내면 Mobius가 식별자를 직접 만들어 응답에 담아 줍니다.
curl -X POST http://localhost:7579/Mobius \ -H "Accept: application/json" \ -H "X-M2M-RI: r-ae-1" \ -H "X-M2M-Origin: S" \ -H "Content-Type: application/json; ty=2" \ -d '{"m2m:ae": {"rn": "myAE", "api": "A.company.myapp", "rr": true}}'
{"m2m:ae": {
"rn": "myAE",
"ty": 2,
"aei": "S2026090812345601a3", <-- 이 값을 보관해 두십시오
"ri": "2-2026090812345601a3",
…
}}
응답으로 받은 aei가 이 AE의 신분증 역할을 합니다. 앞으로 이 AE 이름으로 보내는
요청에는 X-M2M-Origin에 이 값을 넣으면 됩니다. 사용하고 싶은 식별자가 따로
있다면 S 대신 그 값을 보내도 되며, 그대로 aei가 됩니다.
rn: 리소스 이름이며, 그대로 주소의 한 단계가 됩니다api: 애플리케이션 식별자로, 표준에서 반드시 요구하는 값입니다rr: 이 AE가 요청을 받을 수 있는지 여부를 나타냅니다
컨테이너 만들기
컨테이너는 데이터를 담아 두는 그릇에 해당하며, 앞서 만든 AE 아래에 만듭니다.
curl -X POST http://localhost:7579/Mobius/myAE \ -H "Accept: application/json" \ -H "X-M2M-RI: r-cnt-1" \ -H "X-M2M-Origin: S2026090812345601a3" \ -H "Content-Type: application/json; ty=3" \ -d '{"m2m:cnt": {"rn": "temperature", "mni": 1000}}'
mni는 이 컨테이너가 최대 몇 건까지 보관할지를 정하는 값입니다. 이 개수를 넘으면
마스터의 정리 작업이 오래된 것부터 지웁니다. 개수 대신 용량으로 제한하고 싶다면
mbs에 바이트 수를 지정하면 됩니다. 두 값을 모두 지정하지 않으면 데이터가 사실상
제한 없이 쌓이므로 주의가 필요합니다.
데이터 저장과 조회
실제 측정값은 contentInstance로 저장합니다. 한 번 만들면 수정하지 않는 기록이기 때문에, 센서 값이 올라올 때마다 새로 하나씩 쌓아 나가는 것이 정상적인 사용법입니다.
curl -X POST http://localhost:7579/Mobius/myAE/temperature \ -H "Accept: application/json" \ -H "X-M2M-RI: r-cin-1" \ -H "X-M2M-Origin: S2026090812345601a3" \ -H "Content-Type: application/json; ty=4" \ -d '{"m2m:cin": {"con": "23.5"}}'
실제 내용이 담기는 자리는 con입니다. 이 값은 문자열이므로, JSON을 저장하고
싶다면 문자열 형태로 변환해서 넣으면 됩니다.
최신 값 하나 읽기
curl http://localhost:7579/Mobius/myAE/temperature/la \ -H "Accept: application/json" \ -H "X-M2M-RI: r-la-1" \ -H "X-M2M-Origin: S2026090812345601a3"
la는 가장 최근에 저장된 값을, ol은 가장 오래된 값을 가리킵니다.
대시보드에서 현재 값을 보여 줄 때 가장 자주 사용하는 주소입니다.
컨테이너 자체 조회하기
curl http://localhost:7579/Mobius/myAE/temperature \ -H "Accept: application/json" \ -H "X-M2M-RI: r-cnt-2" \ -H "X-M2M-Origin: S2026090812345601a3"
응답에 담긴 cni는 현재 저장된 데이터 개수를, cbs는 그 데이터가
차지하는 전체 용량을 바이트 단위로 나타냅니다.
리소스 탐색
어떤 리소스가 있는지 모를 때는 트리 전체를 훑어볼 수 있습니다. 질의에 fu=1을
붙이면 조건에 맞는 리소스들의 주소 목록이 돌아옵니다.
curl "http://localhost:7579/Mobius?fu=1&ty=3" \ -H "Accept: application/json" \ -H "X-M2M-RI: r-dis-1" \ -H "X-M2M-Origin: Sponde"
{"m2m:uril": ["Mobius/myAE/temperature", "Mobius/myAE/humidity"]}
| 조건 | 의미 | 예 |
|---|---|---|
ty | 리소스 타입으로 거릅니다 | ty=3 · ty=3&ty=4 |
rn | 이름이 정확히 일치하는 것만 찾습니다 | rn=temperature |
lbl | 지정한 라벨이 붙어 있는 것만 찾습니다 | lbl=outdoor |
cra · crb | 생성 시각이 그 이후인 것, 이전인 것 | cra=20260101T000000 |
lvl | 트리를 몇 단계까지 훑을지 정합니다 | lvl=2 |
lim · ofst | 가져올 개수와 건너뛸 개수입니다 | lim=100&ofst=100 |
응답에 X-M2M-CTS: 1이 붙어 있으면 아직 보여 주지 않은 결과가 남아 있다는
뜻입니다. 이때 X-M2M-CTO에 담겨 오는 값을 다음 요청의 ofst로
넘기면 되고, 이 과정을 반복해 끝까지 받아올 수 있습니다.
구독과 알림
컨테이너에 새 값이 들어올 때마다 알림을 받고 싶다면 subscription을 붙이면
됩니다. 알림을 받을 주소는 nu 항목에 적어 줍니다.
curl -X POST http://localhost:7579/Mobius/myAE/temperature \ -H "Accept: application/json" \ -H "X-M2M-RI: r-sub-1" \ -H "X-M2M-Origin: S2026090812345601a3" \ -H "Content-Type: application/json; ty=23" \ -d '{"m2m:sub": { "rn": "watch", "nu": ["http://192.168.0.10:9000/noti"], "nct": 2 }}'
이제 이 컨테이너에 값이 들어올 때마다 지정한 주소로 POST 요청이 나갑니다.
nu는 배열이라 여러 곳에 동시에 보낼 수 있으며, 주소의 형태에 따라 전송 방식이
달라집니다.
| 주소 형태 | 전송 방식 |
|---|---|
http://… | HTTP POST로 보냅니다. 받는 쪽의 응답까지 읽어 성공·거절·실패를 로그에 남깁니다 |
mqtt://… | MQTT로 발행합니다. 브로커 설정이 되어 있어야 합니다 |
coap://… | CoAP 요청으로 보냅니다 |
Mobius는 알림을 한 번 보낸 뒤 재시도하지 않으며, 전송에 실패했다고 해서 구독을 삭제하지도
않습니다. 받는 쪽이 잠시 꺼져 있었다면 그동안의 알림은 그대로 사라집니다. 절대 놓치면 안 되는
값이라면 애플리케이션에서 주기적으로 la를 조회해 보완하시기 바랍니다.
접근 제어
Mobius는 기본 상태에서 요청자를 따로 가리지 않습니다. 특정 리소스를 누가 다룰 수 있는지 정하려면 accessControlPolicy를 만들어 해당 리소스에 붙여야 합니다.
curl -X POST http://localhost:7579/Mobius \ -H "Accept: application/json" \ -H "X-M2M-RI: r-acp-1" \ -H "X-M2M-Origin: Sponde" \ -H "Content-Type: application/json; ty=1" \ -d '{"m2m:acp": { "rn": "readonly", "pv": {"acr": [{"acor": ["S2026090812345601a3"], "acop": 51}]}, "pvs": {"acr": [{"acor": ["Sponde"], "acop": 63}]} }}'
acop은 허용할 동작들을 비트 값으로 더해서 표현합니다.
| 값 | 동작 | 값 | 동작 |
|---|---|---|---|
| 1 | CREATE | 8 | NOTIFY |
| 2 | RETRIEVE | 16 | DELETE |
| 4 | UPDATE | 32 | DISCOVERY |
예를 들어 51은 생성과 조회, 삭제, 탐색을 더한 값(1+2+16+32)이고
63은 모든 동작을 허용한다는 뜻입니다. pv는 이 정책이 붙은 리소스를
다룰 권한을, pvs는 정책 자체를 수정할 권한을 정합니다.
이렇게 만든 정책을 앞서 만들어 둔 컨테이너에 붙입니다.
curl -X PUT http://localhost:7579/Mobius/myAE/temperature \ -H "Accept: application/json" \ -H "X-M2M-RI: r-acp-2" \ -H "X-M2M-Origin: Sponde" \ -H "Content-Type: application/json" \ -d '{"m2m:cnt": {"acpi": ["/Mobius/readonly"]}}'
이제 이 컨테이너는 정책이 허용한 요청자만 다룰 수 있습니다. 정책을 실제로 붙이기 전에 어떤 요청이 막히는지 미리 확인하고 싶다면, 관리 콘솔의 접근 정책 화면에서 모의 실행해 볼 수 있습니다.
응답 코드 읽는 법
응답에는 HTTP 상태 코드와 oneM2M 결과 코드(X-M2M-RSC)가 함께 담겨 옵니다. 자주
마주치게 되는 값들을 정리했습니다.
| RSC | HTTP | 의미 |
|---|---|---|
| 2000 | 200 | 조회·갱신·삭제가 성공했습니다 |
| 2001 | 201 | 리소스를 만들었습니다 |
| 2002 | 200 | 리소스를 지웠습니다 |
| 2004 | 200 | 리소스를 갱신했습니다 |
| 4000 | 400 | 요청이 잘못되었습니다. 본문과 헤더를 확인하십시오 |
| 4004 | 404 | 해당 리소스가 존재하지 않습니다 |
| 4103 | 403 | 접근 정책이 요청을 거절했습니다 |
| 4105 | 409 | 같은 이름의 리소스가 이미 있습니다 |
| 5000 | 500 | 서버 내부에서 문제가 발생했습니다. 로그를 확인하십시오 |
| 5001 | 501 | 현재 백엔드가 그 타입을 지원하지 않습니다 |
요청이 실패했을 때는 응답 본문의 m2m:dbg 항목에 원인이 한 줄로 담겨 옵니다.
무엇이 잘못되었는지 확인할 때는 이 줄부터 살펴보시면 됩니다.
다음 단계
Postman을 사용한다면 지금까지의 요청들을 컬렉션으로 정리해 두면 편리합니다. 환경 변수에 서버 주소와 CSE 이름을 등록해 두면 매번 주소를 입력하지 않아도 됩니다.
소스 코드와 최신 변경 내역은 GitHub 저장소에서 확인할 수 있습니다. 표준 자체가 궁금하시다면 oneM2M에서 제공하는 규격 문서를 참고하시기 바랍니다.