ZUKU API — Social
콘텐츠 소셜(좋아요·북마크·댓글), 알림, DM, 내 프로필 목록, 관리자 회원 API입니다. backend/rs/src/router.rs 실제 경로만 기술합니다.
Base URL
| 환경 | Base URL |
|---|---|
| 프로덕션 | https://zuzunza.com/api/v1 |
| 로컬 | http://localhost:3001/api/v1 |
목차
- 인증 요구사항
- 엔드포인트 표
- Contents social
- Comments
- Notifications
- DM
- Users me lists
- Admin users
- curl · JS/TS 예제
1. 인증 요구사항
| 구분 | Auth |
|---|---|
| 콘텐츠 댓·북마크 | Bearer 필수 |
댓글 목록 GET |
없음(공개). 콘텐츠 없으면 404 |
| 댓글 작성·수정·삭제 | Bearer 필수 |
| 알림 목록 / unread-count | Bearer 선택 (없으면 빈/0에 가깝게 동작) |
알림 read-all / {id}/read |
Bearer 필수 |
| DM 전 경로 | Bearer 필수 + 대화 참여자만 |
GET /users/me/{kind} |
Bearer 필수 |
| Admin users | Bearer + role=admin (비관리자 403 FORBIDDEN) |
세션 형태: Authorization: Bearer <access_token>.
이 문서의 소셜/DM/알림 쓰기 경로는 X-API-Key를 받지 않습니다(콘텐츠 *생성* 등 다른 경로와 다름).
공통 봉투: { success, data, meta } / { success, error, meta }.
댓글·알림 단건 읽음 등과 달리 댓글 삭제·일부 삭제는 204일 수 있습니다.
2. 엔드포인트 표
| Method | Path | Auth | 설명 |
|---|---|---|---|
POST |
/contents/{id}/like |
Bearer | 좋아요 토글 |
POST |
/contents/{id}/bookmark |
Bearer | 북마크 토글 |
POST |
/creators/{handle}/follow |
Bearer | 크리에이터 팔로우 토글 |
GET |
/contents/{id}/comments |
공개 | 댓글 목록 (page/per_page) |
POST |
/contents/{id}/comments |
Bearer | 댓글 작성 → 201 |
PATCH |
/comments/{id} |
Bearer | 내 댓글 수정 |
DELETE |
/comments/{id} |
Bearer | 소프트 삭제 → 204 |
GET |
/notifications |
선택 | 알림 목록 |
GET |
/notifications/unread-count |
선택 | 미읽음 개수만 |
POST |
/notifications/read-all |
Bearer | 모두 읽음 |
POST |
/notifications/{id}/read |
Bearer | 단건 읽음 |
GET |
/dm/conversations |
Bearer | 대화 목록 (before 커서) |
POST |
/dm/conversations |
Bearer | 대화 시작/재사용 → 201 |
GET |
/dm/conversations/{id}/messages |
Bearer+참여자 | 메시지 목록 |
POST |
/dm/conversations/{id}/messages |
Bearer+참여자 | 전송 → 201 |
POST |
/dm/conversations/{id}/read |
Bearer+참여자 | 대화 읽음 |
GET |
/users/me/{kind} |
Bearer | 내 작품·좋아요·북마크·글 |
GET |
/admin/users |
Admin | 회원 검색 목록 |
GET |
/admin/users/{id} |
Admin | 회원 상세 |
PATCH |
/admin/users/{id} |
Admin | role / 정지 |
3. Contents social
POST /contents/{id}/like
토글. 응답:
{ "is_liked": true, "like_count": 42 }
콘텐츠 없음 → 404 CONTENT_NOT_FOUND.
POST /contents/{id}/bookmark
토글. 응답:
{ "is_bookmarked": true, "bookmark_count": 7 }
미인증 → 401. 콘텐츠 없음 → 404 CONTENT_NOT_FOUND.
POST /creators/{handle}/follow
크리에이터 팔로우/언팔로우 토글. Bearer 필수.
성공 200 data:
{ "is_following": true, "follower_count": 1521 }
| HTTP | code | 상황 |
|---|---|---|
| 401 | UNAUTHORIZED |
세션 없음 |
| 404 | CREATOR_NOT_FOUND |
handle 없음·정지 |
| 409 | CANNOT_FOLLOW_SELF |
자기 자신 팔로우 |
공개 프로필 GET /creators/{handle} 응답의 is_following · follower_count · following_count도 동일 테이블을 사용합니다.
홈 피드 sort=following은 팔로우한 작성자의 작품만 반환합니다.
4. Comments
GET /contents/{id}/comments
| 쿼리 | 기본 |
|---|---|
page |
1 |
per_page |
20 |
응답: data.comments, data.pagination.
POST /contents/{id}/comments
{ "body": "좋은 작품이에요", "parent_id": null }
body최대 1000자parent_id선택(대댓글)- 201:
data.comment
PATCH /comments/{id}
{ "body": "수정한 댓글" }
작성자만. 남의 댓글·없음 → 404 COMMENT_NOT_FOUND(존재 누출 방지).
DELETE /comments/{id}
소프트 삭제(답글 자리 보존). 성공 204.
5. Notifications
GET /notifications
page / per_page. 응답:
{
"notifications": [ ],
"unread_count": 3,
"pagination": { }
}
알림 항목: id, type(like \| comment \| follow \| mention \| system), actor?, message, link?, is_read, created_at.
GET /notifications/unread-count
배지 폴링용. data.unread_count만. (WebSocket/SSE 없음 — 단발 HTTP)
POST /notifications/read-all
data.updated = 갱신된 행 수.
POST /notifications/{id}/read
본인 알림만. 성공: { "id": "…", "is_read": true }.
없으면 404 NOTIFICATION_NOT_FOUND.
6. DM
1:1만 지원. 그룹·E2E·실시간 읽음 동기화는 스코프 밖입니다.
참여자가 아니면 대화 존재 여부도 숨김 → 동일 404 CONVERSATION_NOT_FOUND.
GET /dm/conversations
limit(기본 20), before(대화 id 커서).
응답: conversations, next_cursor(마지막 대화 id).
POST /dm/conversations
{ "user_id": "usr_42" }
또는
{ "handle": "peer_handle" }
user_id우선, 없으면handle- 자기 자신 / 상대 없음 → 422
VALIDATION_ERROR - 이미 있으면 기존 대화 요약 반환(목록과 같은 모양) → 201
data.conversation
GET /dm/conversations/{id}/messages
limit(기본 50), before.
next_cursor는 첫 메시지 id(페이지 방향에 맞춤).
POST /dm/conversations/{id}/messages
{ "body": "안녕하세요" }
본문 최대 2000자. 201 data.message.
POST /dm/conversations/{id}/read
data.marked_count.
7. Users me lists
GET /users/me/{kind} — 항상 현재 로그인 사용자 기준(서버가 필터).
kind |
의미 | 페이지네이션 |
|---|---|---|
contents 또는 authored |
내가 올린 작품 | page / per_page → feeds + pagination |
likes 또는 liked |
내가 좋아요한 작품 | 동일 |
bookmarks 또는 bookmarked |
내가 북마크한 작품 | 동일 |
posts |
내 커뮤니티 글 | limit(기본 20) + before → posts + next_cursor |
그 외 kind → 404 ROUTE_NOT_FOUND.
8. Admin users
세 경로 모두 require_admin_user: 미인증 401, 비관리자 403 FORBIDDEN.
GET /admin/users
쿼리: q(검색), page, per_page → users + pagination.
GET /admin/users/{id}
id는 공개 형식(usr_…) 파싱. 활동 카운트 포함 상세.
PATCH /admin/users/{id}
{
"role": "user",
"is_suspended": true,
"reason": "운영 정책 위반"
}
role/is_suspended중 최소 하나 필요role:user\|admin만- 자기 자신의 admin 해제·자기 정지 → 409
SELF_ACTION_FORBIDDEN
9. curl · JS/TS 예제
콘텐츠 좋아요 · 북마크 · 댓글
curl -sS -X POST "http://localhost:3001/api/v1/contents/$CID/like" \
-H "Authorization: Bearer $ACCESS"
curl -sS -X POST "http://localhost:3001/api/v1/contents/$CID/bookmark" \
-H "Authorization: Bearer $ACCESS"
curl -sS "http://localhost:3001/api/v1/contents/$CID/comments?page=1&per_page=20"
curl -sS -X POST "http://localhost:3001/api/v1/contents/$CID/comments" \
-H "Authorization: Bearer $ACCESS" \
-H "Content-Type: application/json" \
-d '{"body":"좋아요!"}'
const base = "http://localhost:3001/api/v1";
const headers = {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
};
await fetch(`${base}/contents/${contentId}/like`, {
method: "POST",
headers: { Authorization: headers.Authorization },
});
const commentsRes = await fetch(
`${base}/contents/${contentId}/comments?page=1&per_page=20`,
);
const commentsJson = await commentsRes.json();
await fetch(`${base}/contents/${contentId}/comments`, {
method: "POST",
headers,
body: JSON.stringify({ body: "좋아요!" }),
});
알림
curl -sS "http://localhost:3001/api/v1/notifications/unread-count" \
-H "Authorization: Bearer $ACCESS"
curl -sS -X POST "http://localhost:3001/api/v1/notifications/read-all" \
-H "Authorization: Bearer $ACCESS"
const unread = await fetch(`${base}/notifications/unread-count`, {
headers: { Authorization: `Bearer ${accessToken}` },
}).then((r) => r.json());
await fetch(`${base}/notifications/read-all`, {
method: "POST",
headers: { Authorization: `Bearer ${accessToken}` },
});
DM
curl -sS -X POST "http://localhost:3001/api/v1/dm/conversations" \
-H "Authorization: Bearer $ACCESS" \
-H "Content-Type: application/json" \
-d '{"handle":"friend"}'
curl -sS "http://localhost:3001/api/v1/dm/conversations/$CONV/messages?limit=50" \
-H "Authorization: Bearer $ACCESS"
curl -sS -X POST "http://localhost:3001/api/v1/dm/conversations/$CONV/messages" \
-H "Authorization: Bearer $ACCESS" \
-H "Content-Type: application/json" \
-d '{"body":"안녕"}'
curl -sS -X POST "http://localhost:3001/api/v1/dm/conversations/$CONV/read" \
-H "Authorization: Bearer $ACCESS"
const convRes = await fetch(`${base}/dm/conversations`, {
method: "POST",
headers,
body: JSON.stringify({ handle: "friend" }),
});
const { conversation } = (await convRes.json()).data;
await fetch(`${base}/dm/conversations/${conversation.id}/messages`, {
method: "POST",
headers,
body: JSON.stringify({ body: "안녕" }),
});
await fetch(`${base}/dm/conversations/${conversation.id}/read`, {
method: "POST",
headers: { Authorization: headers.Authorization },
});
내 목록
curl -sS "http://localhost:3001/api/v1/users/me/likes?page=1&per_page=20" \
-H "Authorization: Bearer $ACCESS"
curl -sS "http://localhost:3001/api/v1/users/me/posts?limit=20" \
-H "Authorization: Bearer $ACCESS"
const likes = await fetch(`${base}/users/me/likes?page=1&per_page=20`, {
headers: { Authorization: `Bearer ${accessToken}` },
}).then((r) => r.json());
const myPosts = await fetch(`${base}/users/me/posts?limit=20`, {
headers: { Authorization: `Bearer ${accessToken}` },
}).then((r) => r.json());
오류 코드 (이 문서 범위)
| code | HTTP | 상황 |
|---|---|---|
UNAUTHORIZED |
401 | Bearer 필요 |
FORBIDDEN |
403 | 비관리자 admin API |
CONTENT_NOT_FOUND |
404 | 콘텐츠 |
COMMENT_NOT_FOUND |
404 | 댓글 |
NOTIFICATION_NOT_FOUND |
404 | 알림 |
CONVERSATION_NOT_FOUND |
404 | DM(비참여자 포함) |
VALIDATION_ERROR |
422 | 본문·상대 지정 등 |
SELF_ACTION_FORBIDDEN |
409 | 관리자 자기 강등/정지 |
ROUTE_NOT_FOUND |
404 | 잘못된 /users/me/{kind} 등 |
*ZUKU API · Social · router.rs 기준*