버전: 1.0.0
형식: OpenAPI 3.0 (Swagger)
Base URL:https://api.nobumocare.com/api/v1
최종 수정: 2026-05-22
모든 API (인증 제외)는 Authorization 헤더 필수:
Authorization: Bearer {access_token}
Access Token 만료(15분) 시 /auth/refresh로 갱신.
{
"success": true,
"data": { ... },
"meta": {
"page": 1,
"limit": 20,
"total": 100
}
}{
"success": false,
"error": {
"code": "HEALTH_RECORD_NOT_FOUND",
"message": "건강 기록을 찾을 수 없습니다.",
"details": []
}
}| 코드 | 의미 |
|---|---|
| 200 | OK - 성공 |
| 201 | Created - 리소스 생성 성공 |
| 204 | No Content - 삭제 성공 |
| 400 | Bad Request - 요청 형식 오류 |
| 401 | Unauthorized - 인증 실패 |
| 403 | Forbidden - 권한 없음 |
| 404 | Not Found - 리소스 없음 |
| 409 | Conflict - 중복 요청 |
| 422 | Unprocessable Entity - 검증 실패 |
| 429 | Too Many Requests - 요청 한도 초과 |
| 500 | Internal Server Error |
K8s liveness/readiness probe용 엔드포인트. 인증 불필요.
Liveness probe — 프로세스 생존 확인 (DB 무관)
| 항목 | 값 |
|---|---|
| 인증 | 불필요 |
| 응답 | 항상 200 |
Response 200
{
"status": "alive",
"service": "caring-api"
}Readiness probe — DB 연결 성공 시 트래픽 수신
| 항목 | 값 |
|---|---|
| 인증 | 불필요 |
| DB 연결 OK | 200 |
| DB 연결 실패 | 503 |
Response 200 (정상)
{
"status": "ready",
"service": "caring-api",
"db": "ok"
}Response 503 (DB 실패)
{
"status": "not_ready",
"service": "caring-api",
"db": "error"
}보안: 에러 상세 미노출 (db=error만 반환), connect_timeout=3s I/O 가드 적용
SMS OTP 발송 (회원가입/로그인 공용)
Request Body
{
"phone": "01012345678"
}Response 200
{
"success": true,
"data": {
"expires_in": 180,
"message": "OTP가 발송되었습니다."
}
}에러 코드
INVALID_PHONE- 잘못된 전화번호 형식OTP_RATE_LIMIT- 1분 내 재발송 불가
OTP 검증 + 토큰 발급
Request Body
{
"phone": "01012345678",
"otp": "123456",
"device_id": "device-uuid-here",
"fcm_token": "firebase-token-here"
}Response 200
{
"success": true,
"data": {
"access_token": "eyJhbGci...",
"refresh_token": "eyJhbGci...",
"token_type": "Bearer",
"expires_in": 900,
"user": {
"id": "uuid",
"name": "홍길동",
"role": "senior",
"is_new_user": false
}
}
}에러 코드
OTP_INVALID- OTP 불일치OTP_EXPIRED- OTP 만료 (3분)OTP_MAX_ATTEMPTS- 5회 초과 시도
Access Token 갱신
Request Body
{
"refresh_token": "eyJhbGci..."
}Response 200
{
"success": true,
"data": {
"access_token": "eyJhbGci...",
"expires_in": 900
}
}로그아웃 (Refresh Token 무효화)
Headers: Authorization 필수
Response 204 (No Content)
내 프로필 조회
Response 200
{
"success": true,
"data": {
"id": "uuid",
"phone": "01012345678",
"email": "user@example.com",
"name": "홍길동",
"role": "senior",
"birth_date": "1950-03-15",
"profile_image": "https://cdn.nobumocare.com/profiles/uuid.jpg",
"created_at": "2026-01-01T00:00:00Z"
}
}내 프로필 수정
Request Body (모든 필드 선택)
{
"name": "홍길동",
"email": "user@example.com",
"birth_date": "1950-03-15",
"profile_image": "base64_or_s3_url"
}Response 200
{
"success": true,
"data": {
"id": "uuid",
"name": "홍길동",
"updated_at": "2026-05-22T00:00:00Z"
}
}회원 탈퇴 (soft delete, 30일 후 완전 삭제)
Request Body
{
"reason": "서비스 이용 종료",
"password_confirm": "OTP 재인증 필요"
}Response 204
개인정보 동의 내역 조회
Response 200
{
"success": true,
"data": {
"terms_agreed": true,
"terms_agreed_at": "2026-01-01T00:00:00Z",
"privacy_agreed": true,
"privacy_agreed_at": "2026-01-01T00:00:00Z",
"health_data_agreed": true,
"health_data_agreed_at": "2026-01-01T00:00:00Z",
"location_agreed": true,
"location_agreed_at": "2026-01-01T00:00:00Z",
"marketing_agreed": false
}
}동의 항목 갱신
Request Body
{
"marketing_agreed": true
}Response 200
연결 초대 코드 생성 (보호자가 노인에게 발송)
Response 201
{
"success": true,
"data": {
"invite_code": "ABC123",
"invite_url": "https://nobumocare.com/invite/ABC123",
"expires_at": "2026-05-23T00:00:00Z"
}
}초대 수락 (노인이 보호자 초대 수락)
Request Body
{
"invite_code": "ABC123",
"relation": "자녀"
}Response 201
{
"success": true,
"data": {
"relationship_id": "uuid",
"caregiver": {
"id": "uuid",
"name": "홍자녀",
"profile_image": "..."
},
"relation": "자녀",
"status": "active"
}
}내 연결 관계 목록 조회
Query Parameters
role(optional):caregiver|senior
Response 200
{
"success": true,
"data": [
{
"id": "uuid",
"role": "caregiver",
"partner": {
"id": "uuid",
"name": "홍부모",
"birth_date": "1950-03-15",
"profile_image": "..."
},
"relation": "자녀",
"is_primary": true,
"status": "active",
"created_at": "2026-01-01T00:00:00Z"
}
]
}연결 해제
Response 204
건강 데이터 기록
Request Body
{
"type": "blood_pressure",
"value_systolic": 135,
"value_diastolic": 85,
"value_main": null,
"unit": "mmHg",
"measured_at": "2026-05-22T08:30:00Z",
"note": "아침 식사 전 측정"
}Response 201
{
"success": true,
"data": {
"id": "uuid",
"type": "blood_pressure",
"value_systolic": 135,
"value_diastolic": 85,
"unit": "mmHg",
"severity": "normal",
"measured_at": "2026-05-22T08:30:00Z",
"alert_sent": false
}
}건강 데이터 타입:
blood_pressure: value_systolic + value_diastolic (mmHg)blood_sugar: value_main (mg/dL)heart_rate: value_main (bpm)weight: value_main (kg)temperature: value_main (°C)oxygen: value_main (%)
건강 기록 목록 조회
Query Parameters
senior_id(required for caregiver): 노인 UUIDtype(optional): 데이터 타입 필터from(optional): 시작 날짜 (ISO8601)to(optional): 종료 날짜 (ISO8601)page(optional, default 1)limit(optional, default 20, max 100)
Response 200
{
"success": true,
"data": [
{
"id": "uuid",
"type": "blood_pressure",
"value_systolic": 135,
"value_diastolic": 85,
"unit": "mmHg",
"severity": "normal",
"measured_at": "2026-05-22T08:30:00Z"
}
],
"meta": {
"page": 1,
"limit": 20,
"total": 45
}
}건강 데이터 요약 (최신값 + 7일 트렌드)
Query Parameters
senior_id(required for caregiver)
Response 200
{
"success": true,
"data": {
"blood_pressure": {
"latest": {
"value_systolic": 135,
"value_diastolic": 85,
"severity": "normal",
"measured_at": "2026-05-22T08:30:00Z"
},
"trend": "stable",
"weekly_avg_systolic": 132.5,
"weekly_avg_diastolic": 83.2
},
"blood_sugar": {
"latest": null,
"trend": null
}
}
}개인화 임계값 조회
Query Parameters
senior_id(required for caregiver)
Response 200
{
"success": true,
"data": {
"blood_pressure": {
"systolic_warning_high": 140,
"systolic_danger_high": 160,
"systolic_warning_low": 90,
"systolic_danger_low": 80
},
"blood_sugar": {
"fasting_warning_high": 100,
"fasting_danger_high": 126
}
}
}임계값 수정 (보호자 또는 노인 본인)
Request Body
{
"senior_id": "uuid",
"blood_pressure": {
"systolic_warning_high": 145
}
}Response 200
복약 일정 등록
Request Body
{
"senior_id": "uuid",
"med_name": "아스피린",
"dosage": "100mg 1정",
"scheduled_at": "08:00",
"repeat_days": [1, 2, 3, 4, 5, 6, 7],
"note": "식후 복용"
}Response 201
{
"success": true,
"data": {
"id": "uuid",
"med_name": "아스피린",
"dosage": "100mg 1정",
"scheduled_at": "08:00",
"repeat_days": [1, 2, 3, 4, 5, 6, 7],
"is_active": true
}
}복약 일정 목록
Query Parameters
senior_id(required for caregiver)is_active(optional, default true)
Response 200
{
"success": true,
"data": [
{
"id": "uuid",
"med_name": "아스피린",
"dosage": "100mg 1정",
"scheduled_at": "08:00",
"repeat_days": [1, 2, 3, 4, 5, 6, 7],
"is_active": true,
"today_status": "taken"
}
]
}복약 일정 수정
복약 일정 삭제 (비활성화)
복약 완료/누락 기록
Request Body
{
"schedule_id": "uuid",
"status": "taken",
"taken_at": "2026-05-22T08:15:00Z",
"note": ""
}Response 201
{
"success": true,
"data": {
"id": "uuid",
"status": "taken",
"taken_at": "2026-05-22T08:15:00Z"
}
}복약 기록 조회
Query Parameters
senior_idfrom,to(날짜 범위)schedule_id(optional)
위치 업데이트 (노인 앱에서 주기적으로 호출)
Request Body
{
"latitude": 37.5665,
"longitude": 126.9780,
"accuracy": 10.5,
"address": "서울특별시 중구 세종대로 110"
}Response 200
{
"success": true,
"data": {
"recorded_at": "2026-05-22T10:30:00Z",
"safe_zone_status": {
"in_safe_zone": true,
"zone_name": "집",
"zone_type": "home"
}
}
}노인 현재 위치 조회 (보호자용)
Response 200
{
"success": true,
"data": {
"latitude": 37.5665,
"longitude": 126.9780,
"accuracy": 10.5,
"address": "서울특별시 중구 세종대로 110",
"recorded_at": "2026-05-22T10:30:00Z",
"safe_zone_status": {
"in_safe_zone": true,
"zone_name": "집"
}
}
}위치 이동 기록 조회
Query Parameters
from,to(날짜 범위)limit(default 100, max 500)
안전구역 등록
Request Body
{
"senior_id": "uuid",
"name": "집",
"type": "home",
"latitude": 37.5665,
"longitude": 126.9780,
"radius_m": 200
}Response 201
{
"success": true,
"data": {
"id": "uuid",
"name": "집",
"type": "home",
"latitude": 37.5665,
"longitude": 126.9780,
"radius_m": 200,
"is_active": true
}
}안전구역 목록 조회
Query Parameters
senior_id
안전구역 수정
안전구역 삭제
채팅방 목록 조회
Response 200
{
"success": true,
"data": [
{
"id": "uuid",
"type": "direct",
"name": null,
"participants": [
{
"id": "uuid",
"name": "홍부모",
"profile_image": "..."
}
],
"last_message": {
"type": "text",
"content": "잘 계세요?",
"sent_at": "2026-05-22T10:00:00Z"
},
"unread_count": 2
}
]
}채팅방 생성 (가족방)
Request Body
{
"type": "family",
"name": "홍씨 가족방",
"participant_ids": ["uuid1", "uuid2", "uuid3"]
}메시지 목록 조회 (페이지네이션, 최신순)
Query Parameters
before(optional): 특정 메시지 ID 이전 메시지 조회 (커서 기반)limit(default 50, max 100)
Response 200
{
"success": true,
"data": [
{
"id": "uuid",
"sender": {
"id": "uuid",
"name": "홍자녀",
"profile_image": "..."
},
"type": "text",
"content": "안녕하세요 어머니",
"media_url": null,
"sent_at": "2026-05-22T10:00:00Z"
},
{
"id": "uuid2",
"sender": {
"id": "uuid",
"name": "홍부모"
},
"type": "image",
"content": null,
"media_url": "https://cdn.nobumocare.com/chat/image.jpg",
"sent_at": "2026-05-22T10:05:00Z"
}
],
"meta": {
"has_more": true,
"oldest_id": "uuid"
}
}메시지 전송 (텍스트)
Request Body
{
"type": "text",
"content": "잘 계세요?"
}Response 201
{
"success": true,
"data": {
"id": "uuid",
"type": "text",
"content": "잘 계세요?",
"sent_at": "2026-05-22T10:30:00Z"
}
}미디어 업로드 URL 발급 (S3 presigned URL)
Request Body
{
"file_name": "photo.jpg",
"content_type": "image/jpeg",
"file_size": 204800
}Response 200
{
"success": true,
"data": {
"upload_url": "https://s3.amazonaws.com/...presigned...",
"media_url": "https://cdn.nobumocare.com/chat/uuid/photo.jpg",
"expires_in": 300
}
}클라이언트는 upload_url로 PUT 업로드 후, media_url을 메시지 전송 시 포함.
메시지 읽음 처리
Request Body
{
"message_id": "uuid"
}Response 200
SOS 신호 발송
Request Body
{
"latitude": 37.5665,
"longitude": 126.9780,
"message": ""
}Response 201
{
"success": true,
"data": {
"event_id": "uuid",
"status": "active",
"notified_caregivers": [
{
"id": "uuid",
"name": "홍자녀",
"notification_sent": true
}
],
"emergency_service_sent": true,
"sos_sent_at": "2026-05-22T11:00:00Z"
}
}응급 이벤트 이력 조회
Query Parameters
senior_idstatus(optional):active|acknowledged|resolvedpage,limit
Response 200
{
"success": true,
"data": [
{
"id": "uuid",
"senior": {
"id": "uuid",
"name": "홍부모"
},
"latitude": 37.5665,
"longitude": 126.9780,
"address": "서울특별시 중구 세종대로 110",
"status": "resolved",
"sos_sent_at": "2026-05-22T11:00:00Z",
"resolved_at": "2026-05-22T11:15:00Z",
"resolved_by": {
"id": "uuid",
"name": "홍자녀"
}
}
]
}응급 이벤트 확인 (보호자)
Response 200
{
"success": true,
"data": {
"event_id": "uuid",
"status": "acknowledged",
"acknowledged_by": "홍자녀",
"acknowledged_at": "2026-05-22T11:05:00Z"
}
}응급 이벤트 해제
Request Body
{
"note": "직접 확인 완료, 이상 없음"
}Response 200
알림 목록 조회
Query Parameters
is_read(optional):true|falsetype(optional): 알림 타입 필터page,limit
Response 200
{
"success": true,
"data": [
{
"id": "uuid",
"type": "health_alert",
"title": "혈압 주의",
"body": "홍부모님의 수축기 혈압이 155mmHg입니다.",
"data": {
"health_record_id": "uuid",
"senior_id": "uuid"
},
"is_read": false,
"sent_at": "2026-05-22T08:35:00Z"
}
],
"meta": {
"unread_count": 5
}
}알림 읽음 처리
Response 200
전체 알림 읽음 처리
Response 200
알림 설정 조회
Response 200
{
"success": true,
"data": {
"health_alert": true,
"medication_reminder": true,
"location_alert": true,
"chat_message": true,
"emergency": true,
"quiet_hours_enabled": false,
"quiet_hours_start": "22:00",
"quiet_hours_end": "07:00"
}
}알림 설정 수정
Request Body
{
"quiet_hours_enabled": true,
"quiet_hours_start": "23:00",
"quiet_hours_end": "07:00"
}연결 시 Authorization 헤더 또는 쿼리 파라미터로 토큰 전달:
wss://api.nobumocare.com?token={access_token}
| 이벤트 | 데이터 | 설명 |
|---|---|---|
join_room |
{ room_id } |
채팅방 입장 |
leave_room |
{ room_id } |
채팅방 퇴장 |
send_message |
{ room_id, type, content, media_url } |
메시지 전송 |
typing_start |
{ room_id } |
타이핑 시작 |
typing_stop |
{ room_id } |
타이핑 종료 |
location_update |
{ latitude, longitude, accuracy } |
위치 실시간 업데이트 |
| 이벤트 | 데이터 | 설명 |
|---|---|---|
new_message |
메시지 객체 | 새 채팅 메시지 수신 |
message_read |
{ message_id, user_id, read_at } |
읽음 확인 |
user_typing |
{ room_id, user_id, name } |
상대방 타이핑 중 |
location_updated |
{ senior_id, latitude, longitude, safe_zone } |
노인 위치 변경 |
health_alert |
{ senior_id, type, severity, value } |
건강 이상 알림 |
safe_zone_alert |
{ senior_id, zone_name, event: 'enter'/'exit' } |
안전구역 이탈/진입 |
emergency_sos |
{ event_id, senior_id, latitude, longitude } |
SOS 신호 수신 |
emergency_resolved |
{ event_id, resolved_by } |
응급 상황 해제 |
medication_reminder |
{ schedule_id, med_name, scheduled_at } |
복약 알림 |
| 코드 | HTTP | 설명 |
|---|---|---|
INVALID_PHONE |
400 | 잘못된 전화번호 |
OTP_RATE_LIMIT |
429 | OTP 재발송 제한 |
OTP_INVALID |
400 | OTP 불일치 |
OTP_EXPIRED |
400 | OTP 만료 |
OTP_MAX_ATTEMPTS |
429 | OTP 시도 횟수 초과 |
TOKEN_INVALID |
401 | 유효하지 않은 토큰 |
TOKEN_EXPIRED |
401 | 토큰 만료 |
USER_NOT_FOUND |
404 | 사용자 없음 |
USER_ALREADY_EXISTS |
409 | 이미 가입된 전화번호 |
RELATIONSHIP_NOT_FOUND |
404 | 관계 없음 |
RELATIONSHIP_ALREADY_EXISTS |
409 | 이미 연결된 관계 |
INVITE_CODE_INVALID |
400 | 초대 코드 불일치/만료 |
HEALTH_RECORD_NOT_FOUND |
404 | 건강 기록 없음 |
MEDICATION_NOT_FOUND |
404 | 복약 일정 없음 |
SAFE_ZONE_NOT_FOUND |
404 | 안전구역 없음 |
SAFE_ZONE_LIMIT_EXCEEDED |
422 | 안전구역 최대 10개 |
CHAT_ROOM_NOT_FOUND |
404 | 채팅방 없음 |
MESSAGE_NOT_FOUND |
404 | 메시지 없음 |
EMERGENCY_NOT_FOUND |
404 | 응급 이벤트 없음 |
PERMISSION_DENIED |
403 | 권한 없음 |
UPLOAD_SIZE_EXCEEDED |
422 | 파일 크기 초과 (이미지 10MB, 음성 20MB) |
INVALID_MEDIA_TYPE |
422 | 허용되지 않는 파일 형식 |
RATE_LIMIT_EXCEEDED |
429 | 요청 한도 초과 |
INTERNAL_ERROR |
500 | 서버 내부 오류 |
| 엔드포인트 | 제한 |
|---|---|
| POST /auth/send-otp | 5 req/phone/hour |
| POST /auth/verify-otp | 5 req/phone/15min |
| POST /emergency/sos | 10 req/user/hour |
| POST /location/update | 60 req/user/hour |
| 일반 API | 100 req/user/min |
실제 OpenAPI 3.0 YAML 파일: /root/.hermes/projects/senior-care/openapi.yaml
개발 서버 Swagger UI: http://localhost:3000/api-docs