Skip to content

Latest commit

 

History

History
1731 lines (1447 loc) · 41.6 KB

adminAPI.md

File metadata and controls

1731 lines (1447 loc) · 41.6 KB

🛡️ Maple Talk SNS Admin API 명세서

📋 개요

Maple Talk SNS 관리자 서버의 REST API 명세서입니다. 모든 API는 일관된 응답 형식을 따르며, 웹 전용으로 설계되었습니다.

🔄 공통 응답 형식

✅ 성공 응답

{
  "code": 200,
  "message": "성공 메시지",
  "data": {
    // 실제 응답 데이터
  }
}

❌ 에러 응답

{
  "code": 400,
  "message": "에러 메시지",
  "errors": [
    {
      "field": "필드명",
      "message": "상세 에러 메시지"
    }
  ]
}

🔐 관리자 인증 도메인 (Admin Authentication Domain)

📱 플랫폼 지원

웹 브라우저 전용

  • 토큰 저장: httpOnly 쿠키로 자동 저장
  • 토큰 전송: 쿠키로 자동 전송
  • 보안: XSS, CSRF 공격 방지
  • 사용법: 별도 설정 없이 자동 작동

⚠️ 주의: 모바일 앱에서는 Admin API를 사용할 수 없습니다.

🎯 보안 특징

  • 등록된 Admin만 로그인: 미리 등록된 카카오 계정만 허용
  • 토큰 해시 저장: RefreshToken 보안 강화
  • 역할 기반 권한: OWNER > ADMIN > CS > READONLY
  • 계정 잠금: 로그인 실패 횟수 제한 (5회)

📊 관리자 역할 등급

역할 등급 권한 설명
OWNER 4 최고 권한 (모든 기능 접근 가능)
ADMIN 3 관리자 권한 (대부분 기능 접근 가능)
CS 2 고객지원 권한 (조회/수정 권한)
READONLY 1 읽기 전용 권한 (조회만 가능)

6. 관리자용 사용자 목록 조회 (Get Users for Admin)

Endpoint: GET /admin/users

Description: 관리자가 시스템에 등록된 모든 사용자 목록을 조회합니다. 삭제되지 않은 사용자만 표시되며 페이지네이션이 지원됩니다.

Query Parameters:

필드 타입 필수 기본값 설명
page number ❌ 1 페이지 번호 (1부터 시작)
limit number ❌ 20 한 페이지당 항목 수 (최대 100)
search string ❌ - 검색어 (닉네임, 이메일에서 검색)

Request Headers:

Cookie: admin_access={accessToken}

Request Body: 없음

동작 방식:

1. Admin 토큰 검증 (adminAuthMiddleware)
2. 쿼리 파라미터 검증 및 기본값 설정
3. User 테이블에서 삭제되지 않은 사용자 조회
4. 이미지 URL에 baseURL 추가
5. 페이지네이션 정보 계산
6. 스네이크 케이스 변환 후 응답

Response (200 OK):

{
  "code": 200,
  "message": "사용자 목록을 조회했습니다.",
  "data": {
    "users": [
      {
        "id": 1,
        "email": "user@example.com",
        "nickname": "사용자닉네임",
        "profile_img": "https://sa-server-u2xi.onrender.com/uploads/profile/image.jpg",
        "bio": "자기소개 텍스트",
        "profile_visibility": "public",
        "is_deleted": 0,
        "created_at": "2024-01-01T00:00:00.000Z",
        "updated_at": "2024-01-01T00:00:00.000Z",
        "login_type": "social",
        "providers": ["kakao"]
      },
      {
        "id": 2,
        "email": "local@example.com",
        "nickname": "로컬사용자",
        "profile_img": null,
        "bio": null,
        "profile_visibility": "public",
        "is_deleted": 0,
        "created_at": "2024-01-02T00:00:00.000Z",
        "updated_at": "2024-01-02T00:00:00.000Z",
        "login_type": "local",
        "providers": []
      }
    ],
    "pagination": {
      "current_page": 1,
      "total_pages": 5,
      "total_count": 50,
      "per_page": 10,
      "has_next": true,
      "has_prev": false
    }
  }
}

페이지네이션 상세:

  • current_page: 현재 페이지 번호
  • total_pages: 전체 페이지 수
  • total_count: 전체 사용자 수
  • per_page: 한 페이지당 항목 수
  • has_next: 다음 페이지 존재 여부
  • has_prev: 이전 페이지 존재 여부

필터링:

  • is_deleted = 0 → 삭제되지 않은 사용자만 표시
  • 정렬: created_at DESC (최신 사용자 우선)

이미지 URL 처리:

  • DB에 저장된 상대 경로를 완전한 URL로 변환
  • profile_img: "/uploads/profile/image.jpg" → "https://sa-server-u2xi.onrender.com/uploads/profile/image.jpg"

Error Responses:

401 Unauthorized - 인증 실패:

{
  "code": 401,
  "message": "관리자 인증 토큰이 필요합니다.",
  "errors": []
}

500 Internal Server Error:

{
  "code": 500,
  "message": "사용자 목록 조회 중 오류가 발생했습니다.",
  "errors": []
}

7. 관리자용 문의 내역 조회 (Get Inquiries for Admin)

Endpoint: GET /admin/inquiries

Description: 관리자가 시스템에 등록된 모든 문의 내역을 조회합니다. 페이지네이션과 필터링이 지원됩니다.

Query Parameters:

필드 타입 필수 기본값 설명
page number ❌ 1 페이지 번호 (1부터 시작)
limit number ❌ 20 한 페이지당 항목 수 (최대 100)
status string ❌ - 상태 필터 (pending, in_progress, resolved, closed)
priority string ❌ - 우선순위 필터 (low, normal, high, urgent)
search string ❌ - 검색어 (제목, 내용에서 검색)

Request Headers:

Cookie: admin_access={accessToken}

Request Body: 없음

동작 방식:

1. Admin 토큰 검증 (adminAuthMiddleware)
2. 쿼리 파라미터 검증 및 필터링 적용
3. Inquiry와 User, InquiryAttachment LEFT JOIN
4. 첨부파일 URL에 baseURL 추가
5. 페이지네이션 정보 계산
6. 스네이크 케이스 변환 후 응답

Response (200 OK):

{
  "code": 200,
  "message": "문의 내역을 조회했습니다.",
  "data": {
    "inquiries": [
      {
        "id": 1,
        "user": {
          "id": 10,
          "nickname": "사용자닉네임",
          "email": "user@example.com"
        },
        "category": "계정문제",
        "title": "로그인할 수 없습니다",
        "content": "로그인이 안 돼요...",
        "status": "pending",
        "priority": "normal",
        "attachments": [
          {
            "id": 1,
            "filename": "error_screenshot.png",
            "file_url": "https://sa-server-u2xi.onrender.com/uploads/inquiry/error_screenshot.png"
          }
        ],
        "created_at": "2024-01-01T00:00:00.000Z",
        "updated_at": "2024-01-01T00:00:00.000Z"
      }
    ],
    "pagination": {
      "current_page": 1,
      "total_pages": 5,
      "total_count": 50,
      "per_page": 10,
      "has_next": true,
      "has_prev": false
    }
  }
}

필터링 예시:

# 대기 중인 문의만 조회
GET /admin/inquiries?status=pending

# 긴급 우선순위 문의 조회
GET /admin/inquiries?priority=urgent

# 처리 중인 문의, 2페이지
GET /admin/inquiries?status=in_progress&page=2&limit=10

JOIN 정보:

  • User: 문의한 사용자 정보 (INNER JOIN)
  • InquiryAttachment: 첨부파일 정보 (LEFT JOIN)

이미지 URL 처리:

  • 첨부파일의 상대 경로를 완전한 URL로 변환

정렬:

  • created_at DESC (최신 문의 우선)

Error Responses:

401 Unauthorized - 인증 실패:

{
  "code": 401,
  "message": "관리자 인증 토큰이 필요합니다.",
  "errors": []
}

500 Internal Server Error:

{
  "code": 500,
  "message": "문의 내역 조회 중 오류가 발생했습니다.",
  "errors": []
}

8. 관리자용 게시물 목록 조회 (Get Posts for Admin)

Endpoint: GET /admin/posts

Description: 관리자가 시스템에 등록된 모든 게시물 목록을 조회합니다. 페이지네이션과 필터링이 지원됩니다.

Query Parameters:

필드 타입 필수 기본값 설명
page number ❌ 1 페이지 번호 (1부터 시작)
limit number ❌ 20 한 페이지당 항목 수 (최대 100)
category string ❌ - 상위 카테고리 ID 필터
sub_category string ❌ - 서브 카테고리 ID 필터
search string ❌ - 제목 검색

Request Headers:

Cookie: admin_access={accessToken}

Request Body: 없음

동작 방식:

1. Admin 토큰 검증 (adminAuthMiddleware)
2. 쿼리 파라미터 검증 및 필터링 적용
3. Post와 User, SubCategory, Category JOIN
4. 페이지네이션 정보 계산
5. 스네이크 케이스 변환 후 응답

Response (200 OK):

{
  "code": 200,
  "message": "게시물 목록을 조회했습니다.",
  "data": {
    "posts": [
      {
        "id": 1,
        "title": "게시물 제목입니다",
        "user": {
          "id": 10,
          "nickname": "작성자닉네임",
          "email": "user@example.com"
        },
        "category": {
          "id": 1,
          "name": "카테고리명",
          "sub_category": {
            "id": 5,
            "name": "서브카테고리명"
          }
        },
        "created_at": "2024-01-01T00:00:00.000Z",
        "updated_at": "2024-01-01T00:00:00.000Z"
      }
    ],
    "pagination": {
      "current_page": 1,
      "total_pages": 5,
      "total_count": 50,
      "per_page": 10,
      "has_next": true,
      "has_prev": false
    }
  }
}

필터링 예시:

# 특정 카테고리의 게시물만 조회
GET /admin/posts?category=1

# 특정 서브카테고리의 게시물 조회
GET /admin/posts?sub_category=5

# 제목 검색
GET /admin/posts?search=게시물

# 복합 필터링
GET /admin/posts?category=1&search=질문&page=2&limit=10

JOIN 정보:

  • User: 게시물 작성자 정보 (INNER JOIN)
  • SubCategory: 서브 카테고리 정보 (INNER JOIN)
  • Category: 상위 카테고리 정보 (INNER JOIN, SubCategory를 통해)

정렬:

  • created_at DESC (최신 게시물 우선)

Error Responses:

401 Unauthorized - 인증 실패:

{
  "code": 401,
  "message": "관리자 인증 토큰이 필요합니다.",
  "errors": []
}

500 Internal Server Error:

{
  "code": 500,
  "message": "게시물 목록 조회 중 오류가 발생했습니다.",
  "errors": []
}

9. 관리자용 게시물 상세 조회 (Get Post Detail for Admin)

Endpoint: GET /admin/posts/:postId

Description: 관리자가 특정 게시물의 상세 정보를 조회합니다. 게시물 내용, 작성자 정보, 카테고리 정보를 포함합니다.

Path Parameters:

필드 타입 필수 설명
postId number ✅ 게시물 ID

Request Headers:

Cookie: admin_access={accessToken}

Request Body: 없음

동작 방식:

1. Admin 토큰 검증 (adminAuthMiddleware)
2. postId 파라미터 검증
3. Post와 User, SubCategory, Category, PostContent JOIN
4. 이미지/비디오 URL에 baseURL 추가
5. 스네이크 케이스 변환 후 응답

Response (200 OK):

{
  "code": 200,
  "message": "게시물을 조회했습니다.",
  "data": {
    "id": 1,
    "title": "게시물 제목입니다",
    "user": {
      "id": 10,
      "nickname": "작성자닉네임",
      "email": "user@example.com"
    },
    "category": {
      "id": 1,
      "name": "카테고리명",
      "sub_category": {
        "id": 5,
        "name": "서브카테고리명"
      }
    },
    "content_blocks": [
      {
        "type": "text",
        "value": "텍스트 내용입니다.",
        "sequence": 1
      },
      {
        "type": "image",
        "value": "https://sa-server-u2xi.onrender.com/uploads/post/image.jpg",
        "sequence": 2
      },
      {
        "type": "video",
        "value": "https://sa-server-u2xi.onrender.com/uploads/post/video.mp4",
        "thumbnail_url": "https://sa-server-u2xi.onrender.com/uploads/post/thumbnail.jpg",
        "sequence": 3
      }
    ],
    "created_at": "2024-01-01T00:00:00.000Z",
    "updated_at": "2024-01-01T00:00:00.000Z"
  }
}

JOIN 정보:

  • User: 게시물 작성자 정보 (INNER JOIN)
  • SubCategory: 서브 카테고리 정보 (INNER JOIN)
  • Category: 상위 카테고리 정보 (INNER JOIN, SubCategory를 통해)
  • PostContent: 게시물 내용 블록들 (별도 조회)

이미지 URL 처리:

  • DB에 저장된 상대 경로를 완전한 URL로 변환
  • content_blocks[].value: 텍스트 또는 이미지/비디오 경로
  • content_blocks[].thumbnail_url: 비디오 썸네일 경로

Error Responses:

400 Bad Request - 잘못된 postId:

{
  "code": 400,
  "message": "올바른 게시물 ID를 입력해주세요.",
  "errors": []
}

401 Unauthorized - 인증 실패:

{
  "code": 401,
  "message": "관리자 인증 토큰이 필요합니다.",
  "errors": []
}

404 Not Found - 게시물 없음:

{
  "code": 404,
  "message": "게시물을 찾을 수 없습니다.",
  "errors": []
}

500 Internal Server Error:

{
  "code": 500,
  "message": "게시물 조회 중 오류가 발생했습니다.",
  "errors": []
}

10. 관리자용 게시물 삭제 (Delete Post for Admin)

Endpoint: DELETE /admin/posts/:postId

Description: 관리자가 특정 게시물을 삭제합니다. 게시물과 관련된 모든 데이터(콘텐츠, 좋아요, 북마크, 댓글 등)를 함께 삭제합니다.

Path Parameters:

필드 타입 필수 설명
postId number ✅ 삭제할 게시물 ID

Request Headers:

Cookie: admin_access={accessToken}

Request Body: 없음

동작 방식:

1. Admin 토큰 검증 (adminAuthMiddleware)
2. postId 파라미터 검증
3. 게시물 존재 확인
4. 트랜잭션 시작
5. PostContent 삭제
6. PostLike 삭제
7. PostBookmark 삭제
8. Comment 및 CommentLike 삭제
9. Post 삭제
10. 트랜잭션 커밋

Response (200 OK):

{
  "code": 200,
  "message": "게시물이 삭제되었습니다.",
  "data": {
    "success": true
  }
}

삭제되는 데이터:

  • 게시물 (Post)
  • 게시물 내용 (PostContent)
  • 좋아요 (PostLike)
  • 북마크 (PostBookmark)
  • 댓글 (Comment)
  • 댓글 좋아요 (CommentLike)

Error Responses:

400 Bad Request - 잘못된 postId:

{
  "code": 400,
  "message": "올바른 게시물 ID를 입력해주세요.",
  "errors": []
}

401 Unauthorized - 인증 실패:

{
  "code": 401,
  "message": "관리자 인증 토큰이 필요합니다.",
  "errors": []
}

404 Not Found - 게시물 없음:

{
  "code": 404,
  "message": "게시물을 찾을 수 없습니다.",
  "errors": []
}

500 Internal Server Error:

{
  "code": 500,
  "message": "게시물 삭제 중 오류가 발생했습니다.",
  "errors": []
}

11. 관리자용 피드 목록 조회 (Get Feeds for Admin - All Feeds)

Endpoint: GET /admin/feeds

Description: 관리자가 시스템에 등록된 모든 피드 목록을 조회합니다. 기존 사용자 피드 조회와 달리 팔로워 제한 없이 모든 피드를 표시합니다. 페이지네이션과 필터링이 지원됩니다.

Query Parameters:

필드 타입 필수 기본값 설명
page number ❌ 1 페이지 번호 (1부터 시작)
limit number ❌ 20 한 페이지당 항목 수 (최대 100)
user_id string ❌ - 특정 사용자 피드 필터
search string ❌ - 내용 검색 (텍스트 블록에서 검색)

Request Headers:

Cookie: admin_access={accessToken}

Request Body: 없음

동작 방식:

1. Admin 토큰 검증 (adminAuthMiddleware)
2. 쿼리 파라미터 검증 및 필터링 적용
3. FeedPost와 User JOIN (팔로워 제한 없음)
4. 각 피드의 FeedPostContent 조회 및 URL 완성
5. 검색 필터 적용 (content_blocks 텍스트 검색)
6. 페이지네이션 정보 계산
7. 스네이크 케이스 변환 후 응답

Response (200 OK):

{
  "code": 200,
  "message": "피드 목록을 조회했습니다.",
  "data": {
    "feeds": [
      {
        "id": 1,
        "user": {
          "id": 10,
          "nickname": "작성자닉네임",
          "email": "user@example.com"
        },
        "content_blocks": [
          {
            "type": "text",
            "value": "피드 내용입니다.",
            "sequence": 1
          },
          {
            "type": "image",
            "value": "https://sa-server-u2xi.onrender.com/uploads/feed/image.jpg",
            "sequence": 2
          },
          {
            "type": "video",
            "value": "https://sa-server-u2xi.onrender.com/uploads/feed/video.mp4",
            "thumbnail_url": "https://sa-server-u2xi.onrender.com/uploads/feed/thumbnail.jpg",
            "sequence": 3
          }
        ],
        "created_at": "2024-01-01T00:00:00.000Z",
        "updated_at": "2024-01-01T00:00:00.000Z"
      }
    ],
    "pagination": {
      "current_page": 1,
      "total_pages": 5,
      "total_count": 50,
      "per_page": 10,
      "has_next": true,
      "has_prev": false
    }
  }
}

필터링 예시:

# 특정 사용자의 피드만 조회
GET /admin/feeds?user_id=10

# 내용 검색
GET /admin/feeds?search=안녕하세요

# 복합 필터링
GET /admin/feeds?user_id=10&search=사진&page=2&limit=10

JOIN 정보:

  • User: 피드 작성자 정보 (INNER JOIN)

검색 대상:

  • content_blocks의 type: "text"인 블록들의 value 내용

이미지 URL 처리:

  • DB에 저장된 상대 경로를 완전한 URL로 변환
  • content_blocks[].value: 텍스트 또는 이미지/비디오 경로
  • content_blocks[].thumbnail_url: 비디오 썸네일 경로

차이점 (vs 일반 피드 조회):

  • 팔로워 제한 없음 (모든 피드 표시)
  • 관리자 전용 필터링 옵션
  • 검색 기능 포함

정렬:

  • created_at DESC (최신 피드 우선)

Error Responses:

401 Unauthorized - 인증 실패:

{
  "code": 401,
  "message": "관리자 인증 토큰이 필요합니다.",
  "errors": []
}

500 Internal Server Error:

{
  "code": 500,
  "message": "피드 목록 조회 중 오류가 발생했습니다.",
  "errors": []
}

12. 관리자용 피드 삭제 (Delete Feed for Admin)

Endpoint: DELETE /admin/feeds/:feedId

Description: 관리자가 특정 피드를 삭제합니다. 피드와 관련된 모든 데이터(콘텐츠, 좋아요, 북마크, 댓글 등)를 함께 삭제합니다.

Path Parameters:

필드 타입 필수 설명
feedId number ✅ 삭제할 피드 ID

Request Headers:

Cookie: admin_access={accessToken}

Request Body: 없음

동작 방식:

1. Admin 토큰 검증 (adminAuthMiddleware)
2. feedId 파라미터 검증
3. 피드 존재 확인
4. 트랜잭션 시작
5. FeedPostContent 삭제
6. FeedLike 삭제
7. FeedBookmark 삭제
8. FeedComment 및 FeedCommentLike 삭제
9. FeedPost 삭제
10. 트랜잭션 커밋

Response (200 OK):

{
  "code": 200,
  "message": "피드가 삭제되었습니다.",
  "data": {
    "success": true
  }
}

삭제되는 데이터:

  • 피드 (FeedPost)
  • 피드 내용 (FeedPostContent)
  • 좋아요 (FeedLike)
  • 북마크 (FeedBookmark)
  • 댓글 (FeedComment)
  • 댓글 좋아요 (FeedCommentLike)

Error Responses:

400 Bad Request - 잘못된 feedId:

{
  "code": 400,
  "message": "올바른 피드 ID를 입력해주세요.",
  "errors": []
}

401 Unauthorized - 인증 실패:

{
  "code": 401,
  "message": "관리자 인증 토큰이 필요합니다.",
  "errors": []
}

404 Not Found - 피드 없음:

{
  "code": 404,
  "message": "피드를 찾을 수 없습니다.",
  "errors": []
}

500 Internal Server Error:

{
  "code": 500,
  "message": "피드 삭제 중 오류가 발생했습니다.",
  "errors": []
}

13. 관리자용 신고 목록 조회 (Get Reports for Admin)

Endpoint: GET /admin/reports

Description: 관리자가 시스템에 등록된 모든 신고 목록을 조회합니다. 페이지네이션과 필터링이 지원됩니다.

Query Parameters:

필드 타입 필수 기본값 설명
page number ❌ 1 페이지 번호 (1부터 시작)
limit number ❌ 20 한 페이지당 항목 수 (최대 100)
status string ❌ - 상태 필터 (pending, resolved, dismissed)
target_type string ❌ - 대상 타입 필터 (post, feed_post, comment, feed_comment, short, short_comment, user, chat_room, message, story)
category string ❌ - 카테고리 필터 (spam, abuse, nudity, fraud, copyright, harassment, hate_speech, other)

Request Headers:

Cookie: admin_access={accessToken}

Request Body: 없음

동작 방식:

1. Admin 토큰 검증 (adminAuthMiddleware)
2. 쿼리 파라미터 검증 및 기본값 설정
3. Report와 User, Admin LEFT JOIN
4. 페이지네이션 정보 계산
5. 스네이크 케이스 변환 후 응답

Response (200 OK):

{
  "code": 200,
  "message": "신고 목록을 조회했습니다.",
  "data": {
    "reports": [
      {
        "id": 1,
        "reporter_id": 10,
        "reporter_nickname": "신고자닉네임",
        "target_type": "post",
        "target_id": 100,
        "category": "spam",
        "reason": "스팸 게시물입니다",
        "status": "pending",
        "admin_id": null,
        "admin_nickname": null,
        "admin_note": null,
        "resolved_at": null,
        "target_snapshot": {
          "target_type": "post",
          "target_id": 100,
          "created_at": "2024-01-01T00:00:00.000Z"
        },
        "created_at": "2024-01-01T00:00:00.000Z",
        "updated_at": "2024-01-01T00:00:00.000Z"
      }
    ],
    "pagination": {
      "current_page": 1,
      "total_pages": 5,
      "total_count": 50,
      "per_page": 10,
      "has_next": true,
      "has_prev": false
    }
  }
}

필터링 예시:

# 대기 중인 신고만 조회
GET /admin/reports?status=pending

# 특정 대상 타입의 신고 조회
GET /admin/reports?target_type=post

# 스팸 카테고리 신고 조회
GET /admin/reports?category=spam

# 복합 필터링
GET /admin/reports?status=pending&target_type=post&category=spam&page=2&limit=10

JOIN 정보:

  • User: 신고자 정보 (INNER JOIN)
  • Admin: 처리한 관리자 정보 (LEFT JOIN)

정렬:

  • created_at DESC (최신 신고 우선)

Error Responses:

401 Unauthorized - 인증 실패:

{
  "code": 401,
  "message": "관리자 인증 토큰이 필요합니다.",
  "errors": []
}

500 Internal Server Error:

{
  "code": 500,
  "message": "신고 목록 조회 중 오류가 발생했습니다.",
  "errors": []
}

14. 관리자용 신고 통계 조회 (Get Report Stats)

Endpoint: GET /admin/reports/stats

Description: 관리자가 신고에 대한 통계 정보를 조회합니다. 상태별, 카테고리별, 대상 타입별 통계를 제공합니다.

Request Headers:

Cookie: admin_access={accessToken}

Request Body: 없음

동작 방식:

1. Admin 토큰 검증 (adminAuthMiddleware)
2. Report 테이블에서 GROUP BY 집계 쿼리 실행
3. 상태별, 카테고리별, 대상 타입별 통계 계산
4. 스네이크 케이스 변환 후 응답

Response (200 OK):

{
  "code": 200,
  "message": "신고 통계를 조회했습니다.",
  "data": {
    "total_reports": 150,
    "pending_reports": 80,
    "resolved_reports": 50,
    "dismissed_reports": 20,
    "reports_by_category": {
      "spam": 60,
      "abuse": 30,
      "nudity": 20,
      "fraud": 15,
      "copyright": 10,
      "harassment": 10,
      "hate_speech": 5,
      "other": 10
    },
    "reports_by_target_type": {
      "post": 50,
      "feed_post": 30,
      "comment": 25,
      "feed_comment": 20,
      "short": 15,
      "short_comment": 10,
      "user": 5,
      "chat_room": 3,
      "message": 1,
      "story": 1
    }
  }
}

통계 항목 설명:

  • total_reports: 총 신고 수
  • pending_reports: 대기 중인 신고 수
  • resolved_reports: 처리된 신고 수
  • dismissed_reports: 기각된 신고 수
  • reports_by_category: 카테고리별 신고 수
  • reports_by_target_type: 대상 타입별 신고 수

Error Responses:

401 Unauthorized - 인증 실패:

{
  "code": 401,
  "message": "관리자 인증 토큰이 필요합니다.",
  "errors": []
}

500 Internal Server Error:

{
  "code": 500,
  "message": "신고 통계 조회 중 오류가 발생했습니다.",
  "errors": []
}

15. 관리자용 신고 처리 (Process Report)

Endpoint: POST /admin/reports/:reportId/process

Description: 관리자가 특정 신고를 처리합니다. 신고 상태를 변경하고 관리자 메모를 추가할 수 있습니다.

Path Parameters:

필드 타입 필수 설명
reportId number ✅ 처리할 신고 ID

Request Headers:

Cookie: admin_access={accessToken}

Request Body:

{
  "status": "resolved",
  "admin_note": "사용자 차단 및 게시물 삭제 조치 완료"
}

Body Parameters:

필드 타입 필수 설명
status string ✅ 처리 상태 (resolved, dismissed)
admin_note string ❌ 관리자 메모

동작 방식:

1. Admin 토큰 검증 (adminAuthMiddleware)
2. reportId 파라미터 검증
3. 신고 존재 확인 및 상태 검증 (pending만 처리 가능)
4. 트랜잭션 시작
5. 신고 상태 업데이트 (status, admin_id, admin_note, resolved_at)
6. 트랜잭션 커밋

Response (200 OK):

{
  "code": 200,
  "message": "신고가 처리되었습니다.",
  "data": null
}

Error Responses:

400 Bad Request - 잘못된 reportId:

{
  "code": 400,
  "message": "올바른 신고 ID를 입력해주세요.",
  "errors": []
}

400 Bad Request - 잘못된 상태 값:

{
  "code": 400,
  "message": "올바른 처리 상태를 입력해주세요.",
  "errors": []
}

401 Unauthorized - 인증 실패:

{
  "code": 401,
  "message": "관리자 인증 토큰이 필요합니다.",
  "errors": []
}

404 Not Found - 신고 없음:

{
  "code": 404,
  "message": "신고를 찾을 수 없습니다.",
  "errors": []
}

404 Not Found - 이미 처리됨:

{
  "code": 404,
  "message": "이미 처리된 신고입니다.",
  "errors": []
}

500 Internal Server Error:

{
  "code": 500,
  "message": "신고 처리 중 오류가 발생했습니다.",
  "errors": []
}

🔐 Admin 인증 도메인 (Admin Authentication Domain)

🌐 OAuth 로그인 흐름

[Admin Web]
  ↓ (관리자 로그인 버튼)
GET /admin/auth/kakao/start
  ↓ (302 redirect + state 쿠키)
카카오 로그인 페이지
  ↓ (사용자 로그인)
카카오 redirect
GET /admin/auth/kakao/callback?code=AUTH_CODE&state=STATE
  ↓
[Server] CSRF 검증 → 토큰 교환 → Admin 검증 → 쿠키 설정
  ↓ (302 redirect)
프론트엔드 대시보드 (${ADMIN_WEB_URL}/dashboard)

🍪 쿠키 설정 강화

  • admin_access: HttpOnly; Secure; SameSite=Lax (15분)
  • admin_refresh: HttpOnly; Secure; SameSite=Strict; Path=/admin/auth/refresh (15일)
  • admin_oauth_state: HttpOnly; SameSite=Lax (10분, 일회용)

1. 카카오 OAuth 로그인 시작 (Start Kakao OAuth Login)

Endpoint: GET /admin/auth/kakao/start

Description: 카카오 OAuth 로그인을 시작합니다. CSRF 방지용 state를 생성하고 카카오 로그인 페이지로 리다이렉트합니다.

Request Headers: 없음

Request Body: 없음

동작 방식:

1. CSRF 방지용 state 생성 (32바이트 랜덤)
2. state를 admin_oauth_state 쿠키에 저장
3. 카카오 OAuth 인증 URL 생성 (state 포함)
4. 카카오 로그인 페이지로 302 리다이렉트

카카오 OAuth URL 예시:

GET https://kauth.kakao.com/oauth/authorize?client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}&response_type=code&state={STATE}

Response: 302 리다이렉트 (카카오 로그인 페이지)

Error Responses:

500 Internal Server Error - 환경변수 누락:

{
  "code": 500,
  "message": "카카오 로그인 시작 중 오류가 발생했습니다.",
  "errors": []
}

2. 카카오 OAuth 콜백 처리 (Handle Kakao OAuth Callback)

Endpoint: GET /admin/auth/kakao/callback

Description: 카카오 OAuth 콜백을 처리합니다. authorization code와 state를 검증하고 Admin 인증을 완료합니다.

Query Parameters:

필드 타입 필수 설명
code string ✅ 카카오 authorization code
state string ✅ CSRF 방지용 state 값
error string ❌ 카카오 에러 코드 (실패 시)

동작 방식:

1. authorization code와 state 검증
2. 저장된 state 쿠키와 비교 (CSRF 방지)
3. state 쿠키 삭제 (일회용)
4. code → 카카오 access_token 교환
5. 카카오 사용자 정보 조회
6. kakao_user_id로 Admin 테이블 검색
7. Admin이 없으면 "관리자 권한이 없습니다" 에러
8. 계정 잠금 상태 확인
9. 기존 RefreshToken 삭제
10. JWT 토큰 생성 및 해시 저장
11. 로그인 시각 및 실패 카운트 초기화
12. admin_access/admin_refresh 쿠키 설정
13. 프론트엔드 대시보드로 리다이렉트

웹 쿠키 설정:

  • admin_access: HttpOnly; Secure; SameSite=Lax (15분)
  • admin_refresh: HttpOnly; Secure; SameSite=Strict; Path=/admin/auth/refresh (15일)

Response: 302 리다이렉트

성공 시 리다이렉트: ${ADMIN_WEB_URL}/dashboard

실패 시 리다이렉트:

  • ${ADMIN_WEB_URL}/login?error=not_admin - 관리자 권한 없음
  • ${ADMIN_WEB_URL}/login?error=account_locked - 계정 잠금
  • ${ADMIN_WEB_URL}/login?error=invalid_state - CSRF 공격 감지
  • ${ADMIN_WEB_URL}/login?error=login_failed - 기타 로그인 실패

Error Handling:

  • 카카오 에러 → kakao_oauth_failed
  • Authorization code 누락 → no_authorization_code
  • State 불일치 → invalid_state

3. 토큰 재발급 (Admin Token Refresh)

Endpoint: POST /admin/auth/refresh

Description: RefreshToken을 사용하여 새로운 AccessToken을 발급합니다. 해시된 토큰을 검증합니다.

Request Headers:

Cookie: admin_refresh={refreshToken}

Request Body: 없음

동작 방식:

1. 쿠키에서 admin_refresh 추출
2. RefreshToken 검증 (admin_refresh 타입 확인)
3. 토큰 해시 생성 및 DB 비교
4. 토큰 만료/취소 상태 확인
5. Admin 존재 및 활성 상태 확인
6. 새 AccessToken 생성

Response (200 OK):

{
  "code": 200,
  "message": "토큰이 재발급되었습니다.",
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  }
}

Error Responses:

400 Bad Request - RefreshToken 누락:

{
  "code": 400,
  "message": "관리자 RefreshToken이 필요합니다.",
  "errors": []
}

401 Unauthorized - 토큰 만료:

{
  "code": 401,
  "message": "관리자 RefreshToken이 만료되었습니다.",
  "errors": [
    {
      "field": "refreshToken",
      "message": "TOKEN_EXPIRED"
    }
  ]
}

401 Unauthorized - 토큰 취소됨:

{
  "code": 401,
  "message": "유효하지 않은 관리자 RefreshToken입니다.",
  "errors": []
}

4. 로그아웃 (Admin Logout)

Endpoint: POST /admin/auth/logout

Description: 관리자 계정에서 로그아웃하고 모든 토큰을 무효화합니다.

Request Headers:

Cookie: admin_access={accessToken}
Cookie: admin_refresh={refreshToken}

Request Body: 없음

동작 방식:

1. AccessToken 검증 시도
2. RefreshToken 검증 시도 (fallback)
3. adminId 추출
4. DB에서 해당 adminId의 모든 RefreshToken 삭제
5. 쿠키 삭제 지시

Response (200 OK):

{
  "code": 200,
  "message": "관리자 로그아웃이 완료되었습니다.",
  "data": null
}

웹 쿠키 삭제:

  • admin_access 쿠키 삭제
  • admin_refresh 쿠키 삭제

특별 케이스 - 토큰 없음:

{
  "code": 200,
  "message": "로그아웃이 완료되었습니다. (클라이언트 정리만 진행됨)",
  "data": null
}

5. 관리자 정보 조회 (Get Admin Info)

Endpoint: GET /admin/auth/me

Description: 현재 로그인된 관리자의 정보를 조회합니다.

Request Headers:

Cookie: admin_access={accessToken}

Request Body: 없음

Response (200 OK):

{
  "code": 200,
  "message": "관리자 정보 조회가 완료되었습니다.",
  "data": {
    "id": 1,
    "email": "admin@kakao.com",
    "name": "관리자",
    "role": "OWNER",
    "last_login_at": "2025-12-29T12:30:00.000Z",
    "created_at": "2025-01-01T00:00:00.000Z"
  }
}

Error Responses:

401 Unauthorized - 인증 실패:

{
  "code": 401,
  "message": "관리자 인증 토큰이 필요합니다.",
  "errors": []
}

404 Not Found - 관리자 없음:

{
  "code": 404,
  "message": "관리자를 찾을 수 없습니다.",
  "errors": []
}

🔑 JWT 토큰 정보 (Admin 전용)

Admin AccessToken

  • 유효기간: 15분
  • 용도: Admin API 요청 인증
  • 포함 정보: adminId, type: 'admin_access'

Admin RefreshToken

  • 유효기간: 15일
  • 용도: AccessToken 재발급
  • 저장 방식: SHA-256 해시 저장 (보안 강화)
  • 포함 정보: adminId, type: 'admin_refresh'

📝 사용 예시

웹 브라우저에서 Admin 로그인 흐름

  1. 카카오 로그인 시작
# 브라우저에서 직접 접근
GET http://localhost:3001/admin/auth/kakao/start
# → 카카오 로그인 페이지로 리다이렉트 (state 쿠키 자동 설정)
  1. 카카오 로그인 완료
사용자가 카카오 계정으로 로그인
→ 카카오가 콜백 URL로 리다이렉트 (?code=xxx&state=yyy)
→ 서버에서 CSRF 검증 후 Admin 인증
→ admin_access/admin_refresh 쿠키 설정
→ 프론트엔드 대시보드로 리다이렉트
  1. 관리자 정보 조회
curl -X GET http://localhost:3001/admin/auth/me \
  --cookie "admin_access=token_here"
  1. 토큰 재발급
curl -X POST http://localhost:3001/admin/auth/refresh \
  --cookie "admin_refresh=token_here"
  1. 로그아웃
curl -X POST http://localhost:3001/admin/auth/logout \
  --cookie "admin_access=token_here"

🛡️ 인증 미들웨어 정보

Admin 전용 미들웨어

1. adminAuthMiddleware

  • 용도: Admin API 인증 (AccessToken 검증)
  • 토큰 위치: 쿠키의 admin_access
  • 타입 확인: admin_access 타입 검증

2. adminRefreshTokenMiddleware

  • 용도: 토큰 재발급 전용 (RefreshToken 해시 검증)
  • 토큰 위치: 쿠키의 admin_refresh
  • 해시 검증: DB에서 SHA-256 해시 비교
  • 타입 확인: admin_refresh 타입 검증

3. adminLogoutMiddleware

  • 용도: 향상된 로그아웃 (AccessToken + RefreshToken 활용)
  • 특징: 토큰 만료되어도 adminId 추출 가능
  • fallback: 둘 다 실패시 graceful degradation

4. adminRoleMiddleware (향후 확장)

// 역할 기반 권한 제어
app.get('/admin/users',
  adminAuthMiddleware,
  adminRoleMiddleware(['ADMIN', 'OWNER']),
  getUsersHandler
);

미들웨어 적용 현황

API 미들웨어 설명
/admin/auth/kakao/start 없음 인증 불필요 (OAuth 시작)
/admin/auth/kakao/callback 없음 인증 불필요 (OAuth 콜백)
/admin/auth/refresh adminRefreshTokenMiddleware RefreshToken 해시 검증
/admin/auth/logout adminLogoutMiddleware 향상된 로그아웃
/admin/auth/me adminAuthMiddleware AccessToken 검증
/admin/users adminAuthMiddleware AccessToken 검증
/admin/inquiries adminAuthMiddleware AccessToken 검증
/admin/posts adminAuthMiddleware AccessToken 검증
/admin/posts/:postId adminAuthMiddleware AccessToken 검증
/admin/feeds adminAuthMiddleware AccessToken 검증
/admin/feeds/:feedId adminAuthMiddleware AccessToken 검증

🚨 에러 코드 정리

HTTP 상태 코드 설명 사용 예시
200 OK 성공적인 요청 처리
400 Bad Request 잘못된 요청 (입력값 오류)
401 Unauthorized 인증 실패 (토큰 무효)
403 Forbidden 권한 없음 (미등록 Admin)
404 Not Found 리소스 없음 (Admin 계정 없음)
423 Locked 계정 잠금 (실패 횟수 초과)
500 Internal Server Error 서버 내부 오류

🔍 토큰 에러 상세 구분

Admin AccessToken 에러

토큰 만료:

{
  "code": 401,
  "message": "관리자 인증에 실패했습니다.",
  "errors": [
    {
      "field": "token",
      "message": "TOKEN_EXPIRED"
    }
  ]
}

잘못된 토큰 타입:

{
  "code": 401,
  "message": "유효하지 않은 관리자 토큰입니다.",
  "errors": []
}

Admin RefreshToken 에러

토큰 만료:

{
  "code": 401,
  "message": "관리자 RefreshToken이 만료되었습니다.",
  "errors": [
    {
      "field": "refreshToken",
      "message": "TOKEN_EXPIRED"
    }
  ]
}

해시 불일치 (토큰 변조):

{
  "code": 401,
  "message": "유효하지 않은 관리자 RefreshToken입니다.",
  "errors": []
}

🔐 보안 강화 특징

Admin 전용 보안

  • 해시 저장: RefreshToken을 SHA-256으로 해시하여 저장
  • 등록 필터링: 미등록 카카오 계정 접근 거부
  • 실패 제한: 로그인 실패 횟수 제한 및 계정 잠금
  • 토큰 로테이션: 로그인 시 기존 토큰 자동 무효화

데이터베이스 구조

  • Admin 테이블: 관리자 기본 정보
  • AdminRefreshTokens 테이블: 해시된 토큰 및 로테이션 추적
  • 외래 키 제약: CASCADE 삭제 지원

세션 관리

  • 단일 세션: 로그인 시 기존 RefreshToken 자동 삭제
  • 토큰 만료: AccessToken 15분, RefreshToken 15일
  • 강제 로그아웃: 보안 위협 시 수동 토큰 무효화 가능

📚 추가 정보

  • 데이터베이스: MySQL (maple_talk_sns)
  • ORM: Sequelize
  • 인증 방식: JWT (JSON Web Token) + 해시
  • 카카오 API: 사용자 정보 조회 및 검증
  • 플랫폼: 웹 브라우저 전용
  • CORS: 웹 도메인 허용 (localhost:3000 등)

🗄️ Admin 계정 등록 방법

DB 직접 등록 (권장)

INSERT INTO Admins (
  kakao_user_id,
  email,
  name,
  role,
  is_active
) VALUES (
  123456789,  -- 카카오 사용자 ID
  'admin@kakao.com',
  '관리자',
  'OWNER',
  1
);

관리자 등록 API (향후 구현)

// OWNER 권한 필요
POST /admin/register
{
  "kakao_user_id": 123456789,
  "email": "admin@kakao.com",
  "name": "관리자",
  "role": "ADMIN"
}

🚀 시작하기

1. 카카오 개발자센터 설정

  1. 카카오 개발자센터 접속
  2. 애플리케이션 추가 → 웹 플랫폼 선택
  3. 앱 키 → REST API 키 복사 (CLIENT_ID)
  4. 앱 키 → Client Secret 복사 (CLIENT_SECRET)
  5. Redirect URI 설정: http://localhost:3001/admin/auth/kakao/callback

2. 환경변수 설정

# .env 파일에 추가
KAKAO_CLIENT_ID=카카오_REST_API_키          # 필수: 카카오 콘솔 REST API 키
KAKAO_CLIENT_SECRET=카카오_Client_Secret    # 선택: Client Secret (설정된 경우만)
KAKAO_REDIRECT_URI=http://localhost:3001/admin/auth/kakao/callback  # 필수: 콜백 URL
ADMIN_WEB_URL=http://localhost:5173         # 필수: 프론트엔드 URL (React/Vite)

환경변수 설명

  • KAKAO_CLIENT_ID: 카카오 개발자센터 → 앱 설정 → 앱 키 → REST API 키 값
  • KAKAO_CLIENT_SECRET: 카카오 개발자센터 → 앱 설정 → 앱 키 → Client Secret 값 (선택사항)
  • KAKAO_REDIRECT_URI: 카카오 개발자센터에 등록된 콜백 URL과 완전히 동일해야 함
  • ADMIN_WEB_URL: React/Vite 프론트엔드 URL (로그인 성공/실패 시 리다이렉트)

3. Admin 계정 등록

INSERT INTO Admins (kakao_user_id, email, name, role, is_active)
VALUES (카카오_사용자_ID, 'admin@kakao.com', '관리자', 'OWNER', 1);

4. 서버 재시작 및 테스트

# 서버 재시작
npm run dev

# 브라우저에서 테스트
http://localhost:3001/admin/auth/kakao/start

5. 프론트엔드 연동

// React에서 로그인 버튼
const handleLogin = () => {
  window.location.href = '/admin/auth/kakao/start';
};

// 로그아웃
const handleLogout = async () => {
  await fetch('/admin/auth/logout', { method: 'POST' });
  // 쿠키 자동 삭제됨
};

성능 최적화

데이터베이스 인덱스

  • Admins.kakao_user_id (Unique)
  • Admins.is_active + Admins.kakao_user_id
  • AdminRefreshTokens.token_hash (Unique)
  • AdminRefreshTokens.admin_id + AdminRefreshTokens.revoked_at

캐싱 전략

  • Admin 기본 정보: 10분 캐싱
  • 토큰 검증 결과: 5분 캐싱
  • 역할 권한 정보: 영구 캐싱

보안 최적화

  • 토큰 해시 검증 (DB 부하 감소)
  • 로그인 실패 횟수 메모리 캐싱
  • IP 기반 접근 제한 (향후 구현)

이 문서는 Maple Talk SNS Admin API의 완전한 명세서를 제공합니다. 보안과 안정성을 최우선으로 설계되었습니다.