Skip to content

Repository files navigation

BLeyeND — 백엔드 (Grad_Project_BE)

실시간 협업 기반 AI PII 마스킹 및 검수 플랫폼의 Django REST API 서버


목차

  1. 프로젝트 개요
  2. 기술 스택
  3. 디렉토리 구조
  4. DB 스키마
  5. 실행 방법
  6. 환경변수 설명
  7. 공통 응답 포맷
  8. API 전체 목록
  9. 주요 기능 흐름
  10. 팀원별 연동 가이드

1. 프로젝트 개요

BLeyeND는 PDF 문서에서 개인정보(PII)를 AI로 자동 탐지하고, 팀원들이 실시간으로 협업해 검수·마스킹한 뒤 내보내는 플랫폼입니다.

백엔드가 담당하는 것:

  • JWT 기반 회원 인증 (이메일 인증 → 가입 → 로그인)
  • 팀(Organization) 생성, 이메일 초대, 다중 팀 전환
  • PDF 업로드 → S3 저장 → PNG 변환 (Celery 비동기)
  • FastAPI AI 엔진에 탐지 요청 → PII 영역(좌표·신뢰도) DB 저장
  • PII 영역 수동 추가·수정·삭제
  • 페이지별 검수 진행률 추적
  • 검수자 배정 → 승인/반려 → 문서 상태 갱신
  • 마스킹 처리된 PDF/ZIP 내보내기 (Celery 비동기)
  • 모든 주요 동작의 감사 로그 기록

2. 기술 스택

분류라이브러리용도
프레임워크Django 4.2, DRF 3.15REST API
인증djangorestframework-simplejwt 5.3JWT 발급·검증
비동기Celery 5.3 + RedisPDF 변환, AI 탐지, 마스킹
DBPostgreSQL (psycopg2-binary)메인 DB
파일 저장boto3, MinIOPDF·이미지 S3 저장
PDF 처리pdf2image, PillowPDF→PNG 변환, 마스킹
HTTPrequestsFastAPI AI 엔진 호출
환경변수python-decouple.env 관리

3. 디렉토리 구조

Grad_Project_BE/
├── apps/
│ ├── accounts/ # 회원가입·로그인·이메일 인증
│ │ ├── models.py # User 모델
│ │ ├── tokens.py # JWT에 name, email, organization_id 클레임 추가
│ │ ├── tasks.py # 인증코드·초대 이메일 Celery 태스크
│ │ ├── management/
│ │ │ └── commands/
│ │ │ └── ensure_personal_workspaces.py # 옛 계정 개인 워크스페이스 백필
│ │ └── views/
│ │ ├── register_views.py # 이메일 인증 3단계 + 회원가입
│ │ └── auth_views.py # 로그인, 토큰 갱신, 로그아웃, 프로필
│ │
│ ├── organizations/ # 팀(조직) 관리
│ │ ├── models.py # Organization, Membership
│ │ └── views.py # 팀 생성·수정·초대·전환·멤버 관리
│ │
│ ├── documents/ # 문서 업로드·조회·관리
│ │ ├── models.py # Document, Page
│ │ ├── serializers.py # DocumentListSerializer (thumbnail·organization_name 포함)
│ │ ├── services/
│ │ │ └── document_service.py # 최근문서 cross-org 조회 포함
│ │ └── views/
│ │ ├── upload_views.py # PDF 업로드
│ │ ├── document_views.py # 문서 CRUD
│ │ └── dashboard_views.py # 대시보드·최근·즐겨찾기·휴지통
│ │
│ ├── detection/ # AI 탐지 작업 관리
│ │ ├── models.py # DetectionJob, PiiRegion (confidence 포함)
│ │ ├── tasks.py # PDF→PNG 변환 + FastAPI 탐지 요청 Celery
│ │ └── views/
│ │ ├── job_views.py # 탐지 작업 시작·조회
│ │ └── pii_views.py # PII 영역 CRUD + 신뢰도 필터
│ │
│ ├── reviews/ # 검수 워크플로우
│ │ ├── models.py # DocumentReviewer, PageReview
│ │ └── views.py # 검수자 배정·결정·페이지 진행률
│ │
│ ├── exports/ # 마스킹 내보내기
│ │ ├── models.py # ExportJob
│ │ ├── tasks.py # 마스킹(black/blur/pixel) + PDF/ZIP 빌드
│ │ └── views.py # 내보내기 요청·폴링·다운로드 URL
│ │
│ ├── audit/ # 감사 로그
│ │ ├── models.py # AuditLog (19가지 Action)
│ │ ├── services.py # log() 헬퍼 함수
│ │ └── views.py # 관리자 전용 로그 조회
│ │
│ └── notifications/ # 인앱 알림
│ ├── models.py # Notification (5가지 Type)
│ ├── services.py # 발신 헬퍼 (detection_done / review_assigned / review_decided)
│ ├── views.py # 목록·읽음·삭제 6개 엔드포인트
│ └── serializers.py
│
├── common/
│ ├── response.py # 통일된 API 응답 형식
│ ├── pagination.py # 페이지네이션
│ ├── exceptions.py # 전역 예외 핸들러
│ └── redis_client.py # Redis 연결
│
├── config/
│ ├── settings.py # 환경변수 기반 설정
│ ├── urls.py # 최상위 URL 라우팅
│ └── celery.py # Celery 설정
│
├── FE_integration/ # 유다형을 위한 연동 코드
│ ├── axios.js # axios 인스턴스 (토큰 자동 갱신 포함)
│ ├── auth.js # 인증 API 함수 모음
│ ├── documents.js # 문서·탐지·검수·내보내기 API 함수
│ └── .env.example # VITE_API_URL / VITE_WS_URL
│
├── SERVER_integration/ # 이창현을 위한 연동 가이드
│ ├── auth.middleware.ts # JWT 검증 미들웨어
│ ├── socket.ts.diff.ts # socket.ts에 미들웨어 등록하는 diff
│ └── room.ts.guide.ts # room:join 수정 가이드
│
├── .env.example
├── requirements.txt
└── setup.sh

4. DB 스키마

┌──────────────────┐ ┌──────────────────────┐
│ organization │ │ user │
│──────────────────│ │──────────────────────│
│ id │◄──┐ │ id │
│ name │ │ │ email (unique) │
│ institution_type │ │ │ name │
│ ocr_engine │ │ │ role (admin/member) │
│ network_isolated │ │ │ organization_id FK──┘
│ is_personal │ │ │ is_active │
└──────────────────┘ │ └──────────────────────┘
│ │
┌──────────────────┐ │ ┌──────────────────────┐
│ membership │ │ │ document │
│──────────────────│ │ │──────────────────────│
│ user_id FK │ │ │ id │
│ organization FK──┘ │ │ title │
│ role │ │ organization_id FK │
│ status │ │ uploaded_by_id FK │
│ invited_email │ │ original_file_key │
│ invitation_token │ │ page_count │
└──────────────────┘ │ status │
└──────────────────────┘
│
┌───────────────────────┬┘
▼ ▼
┌──────────────────────┐ ┌──────────────────────┐
│ page │ │ detection_job │
│──────────────────────│ │──────────────────────│
│ id │ │ id │
│ document_id FK │ │ document_id FK │
│ page_number │ │ ocr_engine │
│ image_key (S3) │ │ status │
│ width / height │ │ raw_result (JSON) │
└──────────────────────┘ └──────────────────────┘
│ │
└───────────┬────────────┘
▼
┌──────────────────────────┐
│ pii_region │
│──────────────────────────│
│ id │
│ detection_job_id FK │
│ page_id FK │
│ pii_type │
│ masking_type │
│ x, y, width, height │ ← 정규화 비율 0.0~1.0
│ confidence_score │ ← AI 신뢰도 (0.0~1.0)
│ confidence_grade │ ← high / medium / low
│ is_confirmed │
│ is_manually_added │
└──────────────────────────┘
┌──────────────────────┐ ┌──────────────────────┐
│ document_reviewer │ │ page_review │
│──────────────────────│ │──────────────────────│
│ document_id FK │ │ document_id FK │
│ reviewer_id FK │ │ reviewer_id FK │
│ status │ │ page_number │
│ comment │ │ reviewed_at │
└──────────────────────┘ └──────────────────────┘
┌──────────────────────┐ ┌──────────────────────┐
│ export_job │ │ audit_log │
│──────────────────────│ │──────────────────────│
│ document_id FK │ │ user_id FK │
│ requested_by_id FK │ │ document_id FK │
│ export_format │ │ action │
│ status │ │ detail (JSON) │
│ result_file_key (S3) │ │ ip_address │
└──────────────────────┘ └──────────────────────┘

S3(MinIO) 파일 경로 규칙:

{org_id}/originals/{uuid}/{파일명}.pdf ← 업로드 원본
{org_id}/{doc_id}/pages/page_001.png ← 변환된 페이지 이미지
{org_id}/{doc_id}/exports/masked_{id}.pdf ← 마스킹된 결과물

5. 실행 방법

사전 준비

  • Python 3.11 이상
  • 이창현 SERVER repo의 Docker Compose 실행 (PostgreSQL · MinIO · Redis 제공)
  • poppler 설치 (pdf2image 의존성)
# Mac
brew install poppler
# Ubuntu/Debian
sudo apt-get install -y poppler-utils

순서

① 이창현 SERVER repo Docker 컨테이너 먼저 실행

# 이창현 SERVER repo 디렉토리에서
docker-compose up -d
# 확인: PostgreSQL(:5560), MinIO(:9000), Redis(:6379)
docker ps

② MinIO 버킷 생성 (최초 1회)

http://localhost:9001 접속 (minioadmin / minioadmin)
Buckets → Create Bucket → 이름: documents

③ 가상환경 & 패키지 설치

python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt

④ 환경변수 설정

cp .env.example .env
# .env 파일을 열어 값 입력 (아래 환경변수 설명 참고)

⑤ DB 생성 & 마이그레이션

# PostgreSQL에 DB가 없으면 먼저 생성
docker exec -it <postgres컨테이너> psql -U postgres -c "CREATE DATABASE grad_project;"
python manage.py migrate

⑥ 서버 실행 (터미널 2개)

터미널 1 — Django

python manage.py runserver 8000

터미널 2 — Celery 워커

celery -A config worker -l info

⑦ 동작 확인

curl -X POST http://localhost:8000/api/v1/auth/email-check/ \
-H "Content-Type: application/json" \
-d '{"email":"test@test.com"}'# → {"success": true, "message": "사용 가능한 이메일입니다."}

⑧ (선택) 옛 계정 백필

회원가입 시 개인 워크스페이스 자동 생성 로직이 추가되기 이전에 만들어진 계정이 DB에 남아 있는 경우, 다음 명령으로 한 번에 메워 줄 수 있습니다.

# 대상 확인만 (변경 없음)
python manage.py ensure_personal_workspaces --dry-run
# 실제 백필 (is_personal=True Organization + ADMIN Membership 생성)
python manage.py ensure_personal_workspaces

신규 가입 계정은 auth_service.create_user에서 이미 자동으로 개인 워크스페이스가 만들어지므로 이 명령을 돌릴 필요가 없습니다. 이미 활성 팀(user.organization)이 다른 팀으로 설정되어 있는 사용자의 활성 팀 상태는 명령이 건드리지 않습니다.


6. 환경변수 설명

변수예시설명
SECRET_KEYdjango-insecure-...Django 시크릿 키 (랜덤 50자 이상)
DEBUGTrue개발 시 True, 배포 시 False
DB_NAMEgrad_projectPostgreSQL DB 이름
DB_USERpostgresDB 유저
DB_PASSWORDpostgres123이창현 docker-compose와 동일하게
DB_HOSTlocalhostDB 호스트
DB_PORT5560이창현이 설정한 포트 (기본 5432가 아님)
REDIS_HOSTlocalhostRedis 호스트
REDIS_PORT6379Redis 포트
JWT_SECRETshared-secret이창현 SERVER repo와 반드시 동일
EMAIL_HOSTsmtp.gmail.comSMTP 서버
EMAIL_HOST_USERyour@gmail.com발신 이메일
EMAIL_HOST_PASSWORD앱비밀번호Gmail 앱 비밀번호
MINIO_ACCESS_KEYminioadminMinIO 접근 키
MINIO_SECRET_KEYminioadminMinIO 시크릿
MINIO_BUCKETdocuments버킷 이름
MINIO_ENDPOINT_URLhttp://localhost:9000MinIO 엔드포인트
FASTAPI_URLhttp://localhost:8001박현우 AI 엔진 주소
CORS_ALLOWED_ORIGINShttp://localhost:5173유다형 프론트 주소
FRONTEND_URLhttp://localhost:5173팀 초대 이메일 링크 생성용

개발 중 이메일 없이 테스트하는 방법:
config/settings.py에서 EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" 로 변경하면 인증 코드가 터미널에 출력됩니다.


7. 공통 응답 포맷

모든 API는 common/response.py · common/pagination.py · common/exceptions.py를 통해 동일한 응답 봉투(envelope) 형식을 사용합니다. 프론트엔드는 response.data만 보면 됩니다 (FE_integration/axios.js가 이미 이 한 꺼풀을 벗겨서 반환).

성공 (R.success / R.created):

{ "success": true, "message": "ok", "data": { /* ... */ } }

페이지네이션 (StandardPagination, 목록 API 일부 — 문서 목록·감사 로그·알림 목록):

{
"success": true,
"message": "ok",
"data": [ /* ... */ ],
"pagination": {
"count": 42,
"next": "http://localhost:8000/api/v1/documents/list/?page=2",
"previous": null,
"page": 1,
"total_pages": 3
}
}

검증 오류 (ValidationErrorcustom_exception_handler):

{ "success": false, "message": "입력값을 확인해주세요.", "errors": { "email": ["..."] } }

일반 오류 (R.error / 그 외 예외):

{ "success": false, "message": "문서를 찾을 수 없습니다." }

204 No Content(R.no_content())를 반환하는 DELETE 엔드포인트는 바디가 없습니다 (예: PII 영역 삭제, 영구 삭제).


8. API 전체 목록

모든 API는 http://localhost:8000 기준이며, 로그인 이후 요청은 헤더에 Authorization: Bearer <access_token> 필요.

인증 (/api/v1/auth/)

MethodURL인증설명
POST/email-check/이메일 중복 확인
POST/send-code/이메일 인증코드 발송
POST/verify-code/인증코드 확인
POST/register/회원가입 → JWT 발급, 개인 워크스페이스 자동 생성
POST/login/로그인 → JWT 발급
POST/token/refresh/Access 토큰 갱신
POST/logout/로그아웃
GET/me/내 프로필 조회

팀(Organization) (/api/v1/organizations/)

MethodURL인증설명
POST/팀 생성 → 생성자가 admin, 활성 팀 자동 전환
GET/my/내가 속한 팀 목록
GET/<org_id>/팀 상세 조회
PATCH/<org_id>/팀 정보 수정 (admin만)
POST/<org_id>/switch/활성 팀 전환
GET/<org_id>/members/멤버 목록
DELETE/<org_id>/members/<user_id>/멤버 강퇴 (admin만)
POST/<org_id>/invite/이메일로 팀원 초대 (admin만)
GET/invitations/<token>/초대 정보 확인
POST/invitations/<token>/accept/초대 수락

공공기관 자동 설정: 팀 생성·수정 시 institution_type=public을 선택하면, 보내준 ocr_engine 값과 무관하게 서버에서 자동으로 ocr_engine=paddle(망분리 환경에서도 동작하는 로컬 OCR), network_isolated=true로 강제 설정합니다. general(일반기업)은 입력한 값을 그대로 사용합니다.

멤버 강퇴 시 활성 팀 처리: 강퇴당한 유저의 활성 팀(user.organization)이 강퇴된 그 팀이었다면, 서버가 자동으로 그 유저의 개인 워크스페이스(is_personal=True)로 활성 팀을 되돌려줍니다. 프론트는 강퇴 응답을 받은 뒤 별도 처리 없이 /organizations/my/를 다시 호출하면 됩니다.

문서 (/api/v1/documents/)

MethodURL인증설명
POST/PDF 업로드 → Celery 변환 시작
GET/list/문서 목록 (?status=, ?search=, ?favorite=true) — 활성 팀 스코프
GET/dashboard/대시보드 통계 — 활성 팀 스코프 (분류 정책은 아래 참고)
GET/recent/최근 문서 20개 — 소속된 모든 팀 통합 (updated_at 내림차순)
GET/favorites/즐겨찾기 목록 — 활성 팀 스코프
GET/<doc_id>/문서 상세 (페이지 이미지 경로 포함)
PATCH/<doc_id>/제목·즐겨찾기 수정
DELETE/<doc_id>/휴지통으로 이동
GET/<doc_id>/download/원본 PDF presigned URL 발급
GET/trash/휴지통 목록 — 활성 팀 스코프
POST/trash/<doc_id>/restore/문서 복원
DELETE/trash/<doc_id>/영구 삭제

스코프 규칙:/recent/ 만 사용자가 ACTIVE 멤버로 속한 모든 팀의 문서를 합쳐서 반환합니다. 그 외 목록(/list/, /dashboard/, /favorites/, /trash/)은 모두 현재 활성 팀(user.organization) 스코프입니다. 활성 팀 전환은 POST /organizations/<org_id>/switch/로 합니다.

문서 목록(DocumentListSerializer) 응답 스키마:

{
"id": 12,
"title": "내부 보고서.pdf",
"status": "detected",
"page_count": 8,
"is_favorite": false,
"uploaded_by_name": "김경원",
"organization_id": 3,
"organization_name": "BLeyeND 팀",
"thumbnail": "3/12/pages/page_001.png",
"created_at": "2026-05-18T08:00:00Z",
"updated_at": "2026-05-18T08:12:34Z"
}
  • thumbnail: 문서 첫 페이지의 S3 image_key. 페이지 변환이 끝나지 않았거나 페이지가 없으면 null.
  • organization_name / organization_id: /recent/ 가 여러 팀 문서를 섞어서 반환하므로, 카드 UI에서 어느 팀 문서인지 표시할 수 있도록 함께 내려준다.

대시보드(GET /dashboard/) 응답 스키마 & 분류 정책:

{ "total": 42, "pending": 17, "reviewing": 5, "approved": 20 }
응답 키한글 라벨포함되는 Document.status
pending검수 대기pending, detecting, detected
reviewing검수 중reviewing
approved검수 완료approved, rejected

detecting(탐지 중)·detected(탐지 완료)는 아직 사람이 검수를 시작하지 않은 상태이므로 검수 대기에 포함됩니다. rejected(반려)는 검수가 종료된 상태이므로 검수 완료에 묶입니다. pending + reviewing + approved == total 이 항상 성립합니다.

문서 status 흐름:

pending → detecting → detected → reviewing → approved
→ rejected

탐지 (/api/v1/detection/)

MethodURL인증설명
POST/documents/<doc_id>/start/재탐지 요청
GET/documents/<doc_id>/jobs/탐지 작업 목록
GET/jobs/<job_id>/탐지 작업 상세 + 신뢰도별 개수
GET/jobs/<job_id>/regions/PII 영역 목록 (?page=, ?confidence_grade=high|medium|low)
POST/jobs/<job_id>/regions/PII 영역 수동 추가
PATCH/jobs/<job_id>/regions/<rid>/PII 수정 (masking_type, is_confirmed)
DELETE/jobs/<job_id>/regions/<rid>/PII 삭제

탐지 작업 상세(GET /jobs/<job_id>/) 응답 예시:

{
"id": 7,
"document_id": 12,
"ocr_engine": "clova",
"status": "done",
"pii_count": 23,
"pii_by_grade": { "high": 15, "medium": 6, "low": 2 },
"error_message": "",
"started_at": "2026-05-18T08:01:00Z",
"finished_at": "2026-05-18T08:01:42Z",
"created_at": "2026-05-18T08:00:55Z"
}

PII 신뢰도 등급 기준:

confidence_score ≥ 0.8 → high (빨간색 권장)
confidence_score ≥ 0.5 → medium (노란색 권장)
confidence_score < 0.5 → low (파란색 권장)

검수 (/api/v1/reviews/)

MethodURL인증설명
GET/my/내가 검수해야 할 문서 목록
GET/documents/<doc_id>/reviewers/검수자 목록
POST/documents/<doc_id>/reviewers/검수자 배정
POST/documents/<doc_id>/decision/승인 or 반려 결정
GET/documents/<doc_id>/progress/내 페이지 검수 진행률 조회
POST/documents/<doc_id>/pages/<page_number>/complete/페이지 검수 완료 토글

페이지 검수 진행률 응답 예시:

{
"total_pages": 10,
"reviewed_pages": 4,
"progress_percent": 40.0,
"reviewed_page_numbers": [1, 2, 5, 7]
}

내보내기 (/api/v1/exports/)

MethodURL인증설명
POST/documents/<doc_id>/마스킹 내보내기 요청 (status=approved만 가능)
GET/documents/<doc_id>/해당 문서의 내보내기 작업 목록
GET/<job_id>/작업 상태 조회 (프론트 폴링용)
GET/<job_id>/download/완료된 파일 presigned URL 발급 (30분 유효)

내보내기 포맷:pdf (기본) or image (ZIP)

마스킹 타입 (PiiRegion.masking_type으로 제어):

  • black — 검은 박스 (기본값)
  • blur — 가우시안 블러
  • pixel — 모자이크

감사 로그 (/api/v1/audit/)

MethodURL인증설명
GET/✅ admin전체 감사 로그 (관리자만). 필터: ?action=, ?user_id=, ?start_date=, ?end_date=
GET/documents/<doc_id>/✅ admin문서별 감사 로그

/audit/ 필터 파라미터:

  • action: 단일 값(?action=login) 또는 콤마 구분(?action=login,logout,register)
  • user_id: 특정 사용자 활동만
  • start_date, end_date: YYYY-MM-DD 형식. 끝일 포함(23:59:59까지)
  • 모든 필터는 AND 조합. 결과는 기본 페이지네이션 적용

기록되는 Action 목록 (총 19개):

카테고리Action기록 시점
인증register회원가입 완료
login로그인 성공
logout로그아웃
문서document_uploadPDF 업로드
document_delete휴지통 이동 or 영구 삭제 (detail.action으로 구분)
document_restore휴지통 복원
탐지detection_start재탐지 요청
detection_doneAI 탐지 완료 (Celery)
detection_failedAI 탐지 실패 (Celery)
PIIpii_addPII 수동 추가
pii_updatePII 수정 (masking_type 변경 포함)
pii_deletePII 삭제
검수review_assign검수자 배정
review_approve검수 승인
review_reject검수 반려
내보내기export_request내보내기 요청
export_done내보내기 완료 (Celery)
export_failed내보내기 실패 (Celery)
export_download마스킹 파일 다운로드 URL 발급

작업 실패 로그(*_failed)는 Celery 태스크 컨텍스트에서 기록되므로 ip_addressnull이며, detail.error에 에러 메시지(앞 500자)가 포함됩니다.

알림 (/api/v1/notifications/)

MethodURL인증설명
GET/내 알림 목록 (페이지네이션). 필터: ?is_read=true/false
GET/unread-count/안 읽은 알림 개수 (헤더 뱃지용)
POST/<id>/read/개별 읽음 처리
POST/read-all/전체 읽음 처리 (이미지의 "전체 확인" 버튼)
DELETE/<id>/개별 알림 삭제
DELETE/내 알림 전체 삭제

알림 목록 응답 스키마:

{
"id": 42,
"type": "review_assign",
"message": "test01 문서 검수가 시작되었습니다.",
"document_id": 12,
"document_title": "test01",
"organization_id": 3,
"organization_name": "팀 01",
"is_read": false,
"created_at": "2026-05-24T10:00:00Z",
"read_at": null
}

알림 발생 시점 & 수신자:

type발생 시점수신자메시지
detection_doneAI 탐지 완료 (Celery)문서 업로더"{title} 문서 탐지가 완료되었습니다."
review_assign검수자 배정배정된 검수자"{title} 문서 검수가 시작되었습니다."
review_approve검수 승인문서 업로더"{title} 문서가 검수 완료되었습니다."
review_reject검수 반려문서 업로더"{title} 문서가 반려되었습니다."
team_invite팀 초대 (선택적)초대 대상자"{org_name} 팀에 초대되었습니다."

알림 생성 실패는 audit_log 와 동일하게 본 비즈니스 로직을 막지 않습니다 — 예외를 삼키고 로깅만 합니다. 발신 호출은 모두 apps/notifications/services.py 의 헬퍼 함수를 통해 이뤄집니다.


9. 주요 기능 흐름

회원가입 흐름

1. POST /auth/email-check/ → 이메일 중복 확인
2. POST /auth/send-code/ → 인증코드 이메일 발송 (Celery)
3. POST /auth/verify-code/ → 코드 확인 (Redis TTL 5분)
4. POST /auth/register/ → 회원가입
→ 개인 워크스페이스(Organization, is_personal=True) 자동 생성
→ Membership 등록 (role=admin, status=active)
→ user.organization 에 활성 팀으로 지정
→ JWT 발급 (access 2h, refresh 14d)

개인 워크스페이스가 안 보이는 옛 계정이 있다면: 이 자동 생성 로직이 추가되기 이전에 만들어진 계정은 is_personal=True 워크스페이스가 없을 수 있습니다. python manage.py ensure_personal_workspaces 로 일괄 백필하면 GET /api/v1/organizations/my/ 응답에 is_personal: true 항목이 정상적으로 포함됩니다. (자세한 내용은 §6 ⑧ 참고)

문서 처리 흐름

1. POST /documents/ → S3에 원본 PDF 저장
→ Document DB 생성 (status=pending)
→ Celery 태스크 큐 등록
[Celery Worker]
2. S3에서 PDF 읽기
3. pdf2image로 페이지별 PNG 변환 → S3 저장 → Page DB 저장 (status=detecting)
4. FastAPI /detect 호출 → PiiRegion bulk_create (status=detected)
→ 알림 생성: detection_done (업로더에게)
5. GET /documents/<id>/ → status 폴링 (pending → detecting → detected)
6. GET /detection/jobs/<id>/regions/ → PII 영역 목록 + 신뢰도 확인

팀 초대 흐름

1. POST /organizations/<org_id>/invite/ → Membership(status=invited) 생성
→ 초대 이메일 발송 (Celery)
→ 응답에 invitation_token 포함
2. GET /organizations/invitations/<token>/ → 초대 정보 확인 (프론트 초대 수락 페이지)
3. POST /organizations/invitations/<token>/accept/ → Membership(status=active)로 변경

검수 흐름

1. POST /reviews/documents/<id>/reviewers/ → 검수자 배정 (status=reviewing)
→ 알림 생성: review_assign (배정된 검수자에게)
2. GET /reviews/documents/<id>/progress/ → 진행률 확인 (팀 전체 합산)
3. POST /reviews/documents/<id>/pages/<n>/complete/ → 페이지별 완료 토글
4. POST /reviews/documents/<id>/decision/ → 승인(approved) or 반려(rejected)
→ 알림 생성: review_approve / review_reject
(업로더에게)

내보내기 흐름

1. POST /exports/documents/<id>/ → ExportJob 생성 (status=approved 필수)
→ Celery 큐 등록
→ audit_log: export_request
[Celery Worker]
2. 페이지 이미지 S3에서 다운로드
3. 최신 done detection_job 의 PiiRegion 좌표로 마스킹 적용
- black : 검정 사각형
- blur : 가우시안 블러
- pixel : 모자이크
4. PDF 병합 또는 ZIP 압축 → S3 저장 (key: {org}/{doc}/exports/masked_{job}.{ext})
성공 → audit_log: export_done
실패 → audit_log: export_failed (retry 최대 3회)
5. GET /exports/<job_id>/ → status 폴링 (pending → running → done)
6. GET /exports/<job_id>/download/ → presigned URL 발급 (30분)
→ audit_log: export_download

10. 팀원별 연동 가이드

유다형 (프론트엔드)

FE_integration/ 폴더의 파일을 프로젝트에 복사하면 됩니다.

FE_integration/axios.js → src/api/axios.js 교체
FE_integration/auth.js → src/api/auth.js 생성
FE_integration/documents.js → src/api/documents.js 생성
FE_integration/.env.example → 프로젝트 루트에 .env 생성 시 참고 (VITE_API_URL, VITE_WS_URL)

axios.js 특징:

  • baseURL이 Django(:8000)로 설정되어 있음
  • 401 응답 시 refresh 토큰으로 자동 재발급
  • refresh 실패 시 자동 로그아웃 + /login 리다이렉트
  • response.data를 바로 반환하므로 .data.data 중복 접근 불필요

documents.js에 아직 없는 것: 알림(/notifications/), 페이지 검수 진행률·토글(/reviews/.../progress/, /.../complete/), PII 영역 수동 추가(POST /detection/jobs/<id>/regions/), 감사 로그(/audit/) 호출 함수는 아직 헬퍼로 빼두지 않았습니다. §9 API 전체 목록의 경로를 참고해 axios 인스턴스로 직접 호출하거나 필요하면 말씀해주세요 — documents.js에 추가해 드리겠습니다.

WebSocket 연결 (이창현 서버):

// src/socket.jsimport{io}from'socket.io-client';constsocket=io('http://localhost:4000',{auth: {token: localStorage.getItem('accessToken'),// Django JWT 그대로 사용},autoConnect: false,});// 문서 열 때socket.connect();socket.emit('room:join',{roomId: documentId});socket.on('room:joined',({ sessionToken })=>{localStorage.setItem('sessionToken',sessionToken);});

이창현 (WebSocket 서버)

SERVER_integration/ 폴더의 가이드를 참고하세요.

파일용도
auth.middleware.tsJWT 검증 미들웨어 — src/middlewares/auth.middleware.ts로 추가
socket.ts.diff.tssrc/websocket/socket.ts에 미들웨어를 등록하는 변경 전/후 diff
room.ts.guide.tsroom:join 핸들러에서 socket.data를 사용하도록 바꾸는 가이드

핵심 변경 2가지:

  1. auth.middleware.ts 파일을 SERVER repo에 추가하고 initSocket() 안에 io.use(jwtAuthMiddleware) 한 줄 추가 (socket.ts.diff.ts 참고)

  2. room:join 핸들러에서 FE가 보내는 userId, userName을 제거하고 socket.data에서 가져오도록 변경

// 변경 전socket.on('room:join',({ roomId, userId, userName, sessionToken })=>{ ... });// 변경 후socket.on('room:join',({ roomId, sessionToken })=>{constuserId=socket.data.userId;// JWT에서 자동 주입constuserName=socket.data.userName;// JWT에서 자동 주입
...
});

.env에서 맞춰야 할 값:

JWT_SECRET=경원이와-동일한-값

박현우 (FastAPI AI 엔진)

Django → FastAPI 요청 payload:

{
"job_id": 1,
"document_id": 5,
"ocr_engine": "clova",
"pages": [
{
"page_number": 1,
"image_key": "1/5/pages/page_001.png",
"width": 1240,
"height": 1754
}
]
}

FastAPI → Django 응답 형식:

{
"regions": [
{
"page_number": 1,
"pii_type": "phone",
"original_text": "010-1234-5678",
"x": 0.15,
"y": 0.32,
"width": 0.20,
"height": 0.03,
"confidence_score": 0.95,
"confidence_grade": "high"
}
]
}

좌표 규칙: x, y, width, height 모두 정규화 비율 (0.0~1.0)

pii_type 허용값:name, phone, email, ssn, address, card, account, passport, driver, ip, other

confidence_grade:high, medium, low (없으면 score로 자동 계산)

엔드포인트:POST /detect (Django settings.pyFASTAPI_URL 기준)

About

[실시간 협업 기반 AI 개인정보 마스킹 및 검수 플랫폼] 백엔드

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages