파인핏 Biz에서 분석 대상자에게 email 등으로 전송할 때, 분석 결과 데이터가 webhook을 통해 고객사 서버로 전송됩니다.
CenterAdmin이 Remo-API-SERVER에 API 사용을 신청합니다.
Remo에서 API 사용 신청을 검토하고 승인합니다.
사용 승인된 CenterAdmin이 본인이 사용할 API credential을 발행합니다.
발행한 API credential에 webHookUrl을 설정합니다.
파인핏 Biz에서 분석 대상자에게 email 등으로 전송하기를 실행했을 때, 해당 데이터가 크레덴셜에 설정한 webHookUrl로 전송됩니다.
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
// JSON 파싱을 위한 미들웨어
app.use(express.json({ limit: '10mb' }));
// Webhook 엔드포인트
app.post('/webhook', async (req, res) => {
try {
console.log('Webhook 수신:', new Date().toISOString());
// 요청 본문에서 데이터 추출
const webhookData = req.body;
// WebhookDataResponseDto 구조
const {
shapeMeasure, // 체형 측정 데이터 (선택적)
poseMeasure, // 자세 측정 데이터 (선택적)
screeningParticipant, // 검사 참가자 정보 (필수)
screeningCenter, // 검사 센터 정보 (필수)
webhookUrl, // 웹훅 URL (필수)
tag // 웹훅 태그 (필수)
} = webhookData;
// tag를 통한 이벤트 타입 확인
console.log('Webhook 태그:', tag);
// 참가자 정보 처리
if (screeningParticipant) {
console.log('참가자 정보:', {
이름: screeningParticipant.name,
전화번호: screeningParticipant.phone,
이메일: screeningParticipant.email
});
}
// 센터 정보 처리
if (screeningCenter) {
console.log('센터 정보:', {
관리자이메일: screeningCenter.email
});
}
// 체형 측정 데이터 처리
if (shapeMeasure) {
console.log('체형 측정 데이터 수신:', {
키: `${shapeMeasure.height_mm}µm`,
몸무게: `${shapeMeasure.weight_g}µg`,
체지방률: `${shapeMeasure.body_fat_percentage}%`,
체형분류: shapeMeasure.shape_class,
체형크기: shapeMeasure.shape_size
});
// 데이터베이스에 저장
await saveShapeMeasureData(shapeMeasure);
}
// 자세 측정 데이터 처리
if (poseMeasure) {
console.log('자세 측정 데이터 수신:', {
정면기울기: `${poseMeasure.far_tilt_m_}도`,
측면기울기: `${poseMeasure.sar_tilt_m_}도`,
거북목여부: poseMeasure.sar_fwd_head ? '예' : '아니오',
라운드숄더: `${poseMeasure.round_shoulder_m_}도`
});
// 좌표 데이터 파싱 (JSONString으로 전송되므로 JSON.parse() 필요)
if (poseMeasure.far_coords) {
const farCoords = JSON.parse(poseMeasure.far_coords);
console.log('정면 2D 좌표 데이터:', farCoords);
}
if (poseMeasure.far_coords3D) {
const farCoords3D = JSON.parse(poseMeasure.far_coords3D);
console.log('정면 3D 좌표 데이터:', farCoords3D);
}
if (poseMeasure.sar_coords) {
const sarCoords = JSON.parse(poseMeasure.sar_coords);
console.log('측면 2D 좌표 데이터:', sarCoords);
}
if (poseMeasure.sar_coords3D) {
const sarCoords3D = JSON.parse(poseMeasure.sar_coords3D);
console.log('측면 3D 좌표 데이터:', sarCoords3D);
}
// 데이터베이스에 저장
await savePoseMeasureData(poseMeasure);
}
// 성공 응답 (200 OK)
res.status(200).json({
success: true,
message: 'Webhook 데이터 수신 완료',
timestamp: new Date().toISOString(),
receivedData: {
hasShapeMeasure: !!shapeMeasure,
hasPoseMeasure: !!poseMeasure,
participantCount: 1,
tag: tag
}
});
} catch (error) {
console.error('Webhook 처리 오류:', error);
// 오류 응답 (500 Internal Server Error)
res.status(500).json({
success: false,
message: 'Webhook 데이터 처리 중 오류 발생',
error: error.message,
timestamp: new Date().toISOString()
});
}
});
// 데이터베이스 저장 함수 예시
async function saveShapeMeasureData(shapeMeasure) {
// MongoDB, PostgreSQL 등에 데이터 저장
console.log('체형 측정 데이터 저장 중...');
// await db.collection('shapeMeasures').insertOne(shapeMeasure);
}
async function savePoseMeasureData(poseMeasure) {
// MongoDB, PostgreSQL 등에 데이터 저장
console.log('자세 측정 데이터 저장 중...');
// await db.collection('poseMeasures').insertOne(poseMeasure);
}
// 서버 시작
app.listen(port, () => {
console.log(`Webhook 서버가 포트 ${port}에서 실행 중입니다.`);
console.log(`Webhook 엔드포인트: http://localhost:${port}/webhook`);
});
// 헬스체크 엔드포인트
app.get('/health', (req, res) => {
res.status(200).json({
status: 'OK',
timestamp: new Date().toISOString(),
uptime: process.uptime()
});
});
# package.json 생성
npm init -y
# 필요한 패키지 설치
npm install express
# 개발 의존성 설치
npm install --save-dev nodemon
# 스크립트 추가 (package.json)
{
"scripts": {
"start": "node app.js",
"dev": "nodemon app.js"
}
}
# 개발 모드로 실행 (자동 재시작)
npm run dev
# 프로덕션 모드로 실행
npm start
분석결과 webhook을 받기 위해서는 Remo로 연락 바랍니다.
webhook 연동을 위해 다음 정보가 필요합니다:
파인핏 Biz에서 분석 대상자에게 email 등으로 전송하기를 실행했을 때 해당 데이터가 전송됩니다.
전송되는 데이터는 WebhookDataResponseDto 구조를 따릅니다:
{
shapeMeasure: ShapeMeasureResponseDto, // 체형 측정 데이터 (선택적)
poseMeasure: PoseMeasureResponseDto, // 자세 측정 데이터 (선택적)
screeningParticipant: ScreeningParticipantWebhookDto, // 검사 참가자 정보 (필수)
screeningCenter: ScreeningCenterWebhookDto, // 검사 센터 정보 (필수)
webhookUrl: string, // 웹훅 URL (필수)
tag: string // 웹훅 태그 (필수) - RESOURCE:ACTION 형식
}
tag 속성은 웹훅이 전송되는 곳의 리소스와 행위를 나타내는 식별자입니다.
형식: RESOURCE:ACTION
예시:
REPORT:CREATE - 리포트 생성 시 전송되는 웹훅용도:
검사에 참가한 사용자의 기본 정보를 포함합니다.
객체 구조:
{
phone: string, // 참가자 전화번호 (필수)
name: string, // 참가자 이름 (필수)
email?: string // 참가자 이메일 (선택적, null 가능)
}
필드 설명:
검사를 수행한 센터의 관리자 정보를 포함합니다.
객체 구조:
{
email: string // 센터 관리자 이메일 (필수)
}
필드 설명:
객체 구조:
{
id: number, // 고유 ID
uuid: string, // UUID
createdAt: Date, // 생성일
updatedAt: Date, // 수정일
deletedAt: Date, // 삭제일 (선택적)
// 기본 신체 정보
height_mm: number, // 키 (µm 단위)
weight_g: number, // 몸무게 (µg 단위)
skeletal_muscle_mass_g: number, // 골격근량 (g 단위)
body_fat_mass_g: number, // 체지방량 (g 단위)
body_fat_percentage: number, // 체지방률 (%)
abdominal_fat_percentage: number, // 복부지방률 (%)
// 이미지 URL
command: string, // AI 분석 요청 명령어
state: number, // AI 분석 처리 결과 상태
front_original_image: string, // 정면 사진 원본 URL
side_original_image: string, // 측면 사진 원본 URL
frontCaptureImage: string, // 프론트 렌더링 결과 이미지 URL
// 백분율 정보
height_per: number, // 키 백분율 (0-100)
weight_per: number, // 몸무게 백분율 (0-100)
// 둘레 측정값 (mm 단위)
chest: number, // 가슴 둘레
chest_max: number, // 가슴 최대 둘레
chest_min: number, // 가슴 최소 둘레
chest_per: number, // 가슴 둘레 백분율
waist: number, // 허리 둘레
waist_max: number, // 허리 최대 둘레
waist_min: number, // 허리 최소 둘레
waist_per: number, // 허리 둘레 백분율
hip: number, // 엉덩이 둘레
hip_max: number, // 엉덩이 최대 둘레
hip_min: number, // 엉덩이 최소 둘레
hip_per: number, // 엉덩이 둘레 백분율
thigh: number, // 허벅지 둘레
thigh_max: number, // 허벅지 최대 둘레
thigh_min: number, // 허벅지 최소 둘레
thigh_per: number, // 허벅지 둘레 백분율
// 체형 분류
shape_class: number, // 체형 클래스 (0: 사각형, 1: 둥근형, 2: 삼각형, 3: 역삼각형, 4: 모래시계형)
shape_size: number, // 체형 크기 (0: 매우 마름, 1: 마름, 2: 표준, 3: 과체중, 4: 비만)
// 팔 길이 측정값 (mm 단위)
arm_length: number, // 양팔 평균 길이
arm_per: number, // 양팔 길이 백분율
left_up_arm_length: number, // 왼쪽 위팔 길이
right_up_arm_length: number, // 오른쪽 위팔 길이
left_down_arm_length: number, // 왼쪽 아래팔 길이
right_down_arm_length: number, // 오른쪽 아래팔 길이
left_arm_length: number, // 왼쪽 팔 길이
right_arm_length: number, // 오른쪽 팔 길이
// 다리 길이 측정값 (mm 단위)
leg_length: number, // 양다리 평균 길이
leg_per: number, // 양다리 길이 백분율
left_up_leg_length: number, // 왼쪽 위다리 길이
right_up_leg_length: number, // 오른쪽 위다리 길이
left_down_leg_length: number, // 왼쪽 아래다리 길이
right_down_leg_length: number, // 오른쪽 아래다리 길이
left_leg_length: number, // 왼쪽 다리 길이
right_leg_length: number, // 오른쪽 다리 길이
// 기타 길이 측정값 (mm 단위)
lower_body_length: number, // 하체 길이
trunk_length: number, // 상체 길이
head_length: number, // 머리 길이
body_length: number // 전신 길이
}
객체 구조:
{
id: number, // 고유 ID
uuid: string, // UUID
createdAt: Date, // 생성일
updatedAt: Date, // 수정일
deletedAt: Date, // 삭제일 (선택적)
// 기본 신체 정보
height_mm: number, // 키 (µm 단위)
weight_g: number, // 몸무게 (µg 단위)
skeletal_muscle_mass_g: number, // 골격근량 (g 단위)
body_fat_mass_g: number, // 체지방량 (g 단위)
body_fat_percentage: number, // 체지방률 (%)
abdominal_fat_percentage: number, // 복부지방률 (%)
// 이미지 URL
command: string, // AI 분석 요청 명령어
state: number, // AI 분석 처리 결과 상태
front_original_image: string, // 정면 사진 원본 URL
side_original_image: string, // 측면 사진 원본 URL
frontCaptureImage: string, // 프론트 렌더링 결과 이미지 URL
// 정면 분석 데이터 (far: front)
far_coords: Array<Array<number>>, // 정면 골격 2D 좌표 배열 (JSONString으로 전송되므로 JSON.parse() 필요)
far_coords3D: Array<Array<number>>, // 정면 골격 3D 좌표 배열 (JSONString으로 전송되므로 JSON.parse() 필요)
far_tilt_m_: number, // 정면 사진 좌우 기울기 각도 (밀리도 단위)
far_head_bal_m_: number, // 정면 머리 좌우 기울기 각도 (밀리도 단위)
far_shoulder_bal_m_: number, // 정면 어깨 기울기 밸런스 각도 (밀리도 단위)
far_pelvic_bal_m_: number, // 정면 골반 기울기 밸런스 각도 (밀리도 단위)
far_knee_bal_m_: number, // 정면 무릎 기울기 밸런스 각도 (밀리도 단위)
far_right_QAngle_m_: number, // 정면 오른쪽 다리 Q값 (O다리 각도, 밀리도 단위)
far_left_QAngle_m_: number, // 정면 왼쪽 다리 Q값 (O다리 각도, 밀리도 단위)
// 측면 분석 데이터 (sar: side)
sar_coords: Array<Array<number>>, // 측면 골격 2D 좌표 배열 (JSONString으로 전송되므로 JSON.parse() 필요)
sar_coords3D: Array<Array<number>>, // 측면 골격 3D 좌표 배열 (JSONString으로 전송되므로 JSON.parse() 필요)
sar_tilt_m_: number, // 측면 사진 앞뒤 기울기 각도 (밀리도 단위)
sar_head_tilt_m_: number, // 측면 머리 앞뒤 기울기 각도 (밀리도 단위)
sar_fwd_head: number, // 거북목 여부 (0: 거짓, 1: 참)
turtle_neck_m_: number, // 거북목 기울기 각도 (밀리도 단위)
round_shoulder_m_: number, // 라운드 숄더 기울기 각도 (밀리도 단위)
sar_pelvic_tilt_m_: number // 골반 전후경사 기울기 각도 (밀리도 단위, -: 앞으로, +: 뒤로)
}
webhook 연동에 대한 자세한 내용이나 기술 지원이 필요한 경우, 아래 연락처로 문의해 주세요:
Remo 기술지원팀