Files
cloud-sharp-docs/설계/db/개념적 설계.md
2026-03-16 16:17:17 +09:00

12 KiB

Cloud# — 데이터베이스 개념적 설계서

본 문서는 Cloud# 서비스의 핵심 엔티티에 대한 개념적 DB 설계를 정의한다. 물리적 DDL이 아닌 엔티티의 역할, 컬럼 의미, 설계 의도, 제약 조건을 중심으로 기술한다.


목차

  1. [엔티티 목록](#1. 엔티티 목록)
  2. ER 다이어그램
  3. 엔티티 상세 설계
  4. 관계 정의
  5. 제약 조건 요약
  6. 설계 결정 사항 (Decision Log)

1. 엔티티 목록

엔티티 설명
User 사용자 인증 정보 및 스토리지 한도 관리
Folder 사용자별 폴더 트리 구조 표현
FileItem 파일 메타데이터 및 저장소 객체 연결
UploadSession tus 기반 업로드 진행 상태 추적

2. ER 다이어그램

!개념적설계_erd.svg


3. 엔티티 상세 설계


3.1 User

사용자의 인증 정보와 스토리지 한도를 관리하는 엔티티.

주요 역할

  • 로그인 식별자 관리
  • 권한(role) 관리
  • 전체 사용 가능 용량 / 사용 중 용량 관리

컬럼 정의

컬럼명 타입 Null 설명
id UUID / BIGINT NOT NULL PK — 내부 식별자
email VARCHAR NOT NULL 로그인 식별자. UNIQUE
password_hash VARCHAR NOT NULL 해시된 비밀번호
display_name VARCHAR NULL 표시 이름
role ENUM NOT NULL ADMIN | USER
storage_allowed_bytes BIGINT NULL 허용 용량. NULL 이면 무제한
storage_used_bytes BIGINT NOT NULL 현재 사용 중 용량. 기본값 0
created_at TIMESTAMP NOT NULL 계정 생성 일시
updated_at TIMESTAMP NOT NULL 마지막 수정 일시
deleted_at TIMESTAMP NULL 소프트 삭제 일시

설계 포인트

  • emailUNIQUE 제약으로 중복 가입 방지
  • storage_allowed_bytesNULL 이면 무제한 처리
    • 애플리케이션 레이어에서 NULL 여부를 먼저 확인
  • storage_used_bytes집계 캐시 컬럼으로 운영
    • 즉시 조회 성능은 우수하나, 파일 업로드·삭제 시 반드시 동기화 필요
    • 정합성 보장을 위해 트랜잭션 내에서 함께 갱신

3.2 Folder

사용자별 폴더 트리를 표현하는 엔티티.

주요 역할

  • 폴더 계층 구조 표현
  • 탐색기 경로 구성
  • 파일의 논리적 위치 제공

컬럼 정의

컬럼명 타입 Null 설명
id UUID / BIGINT NOT NULL PK
owner_user_id FK → User NOT NULL 소유 사용자
parent_folder_id FK → Folder NULL 부모 폴더. NULL 이면 루트 폴더
name VARCHAR NOT NULL 폴더 이름 (부모 기준 상대명)
full_path VARCHAR NULL 전체 경로 캐시 (예: /문서/프로젝트/2025)
created_at TIMESTAMP NOT NULL 생성 일시
updated_at TIMESTAMP NOT NULL 수정 일시
deleted_at TIMESTAMP NULL 소프트 삭제 일시

설계 포인트

자기참조 트리 구조

parent_folder_id IS NULL  →  루트 폴더
parent_folder_id = X      →  X 폴더의 하위 폴더

full_path 컬럼의 위치

full_path 는 원본 진실(source of truth)이 아닌 조회 최적화용 캐시 컬럼이다.

구분 컬럼 역할
원본 진실 id, parent_folder_id, name 실제 구조의 기준
캐시 full_path 목록 조회·검색·WebDAV 경로 변환·breadcrumb 표시용
  • 폴더 이름 변경·이동 시 full_path 와 하위 폴더의 full_path함께 갱신해야 함
  • 불일치 발생 시 id / parent_folder_id 기준으로 재계산 가능

유니크 제약

같은 부모 폴더 아래에서 동일한 이름의 폴더는 허용하지 않는다.

UNIQUE (owner_user_id, parent_folder_id, name)  -- 활성 데이터 기준
  • 소프트 삭제 구현 시 deleted_at IS NULL 조건부 유니크 인덱스 권장

루트 폴더 정책

  • 사용자당 루트 폴더(parent_folder_id IS NULL) 는 1개 권장
  • 애플리케이션 레이어 또는 DB 제약으로 강제

3.3 FileItem

사용자 관점에서 보이는 파일 자체의 메타데이터를 관리하는 엔티티.

주요 역할

  • 탐색기 파일 목록 조회
  • 파일명, MIME, 크기, 미리보기 상태 관리
  • 실제 저장소(LocalFS / S3)의 객체와 연결

컬럼 정의

컬럼명 타입 Null 설명
id UUID / BIGINT NOT NULL PK
owner_user_id FK → User NOT NULL 소유 사용자
folder_id FK → Folder NOT NULL 소속 폴더
display_name VARCHAR NOT NULL 사용자에게 보이는 파일 이름
storage_key VARCHAR NOT NULL 실제 저장소 객체 키. UNIQUE
size_bytes BIGINT NOT NULL 파일 크기 (>= 0)
mime_type VARCHAR NULL MIME 타입 (예: image/jpeg)
checksum_sha256 VARCHAR NULL 무결성 검증용 해시
preview_status ENUM NOT NULL 미리보기 파이프라인 상태
metadata_json JSON / TEXT NULL 파일 종류별 가변 메타데이터
created_at TIMESTAMP NOT NULL 업로드 완료 일시
updated_at TIMESTAMP NOT NULL 수정 일시
deleted_at TIMESTAMP NULL 소프트 삭제 일시

설계 포인트

storage_keydisplay_name 분리

display_name : 사용자에게 보이는 이름  →  "보고서 최종.pdf"
storage_key  : 저장소의 실제 키        →  "user/12/ab/cd/f83e...uuid.bin"
  • 파일 이름 변경 시 display_name 만 변경하고 저장소 객체는 그대로 유지
  • storage_key 가 외부에 노출되지 않도록 주의

metadata_json 활용 예시

// 이미지
{ "width": 1920, "height": 1080 }

// 비디오
{ "duration": 3600, "codec": "h264", "bitrate": 4000 }

// 문서
{ "page_count": 42 }

preview_status 상태 흐름

PENDING → PROCESSING → DONE
                     → FAILED
         (미지원 포맷) → UNSUPPORTED

파일명 중복 정책

같은 폴더 내에서 동일한 파일 이름은 허용하지 않는다.

UNIQUE (owner_user_id, folder_id, display_name)  -- 활성 데이터 기준
  • 소프트 삭제 구현 시 deleted_at IS NULL 조건부 유니크 인덱스 권장

3.4 UploadSession

tus 기반 대용량 업로드의 진행 상태를 관리하는 엔티티.

주요 역할

  • 업로드 시작 ~ 완료까지의 진행 상태 추적
  • 실패·중단·재개 처리
  • 임시 저장 위치와 최종 FileItem 생성 연결

컬럼 정의

컬럼명 타입 Null 설명
id UUID / BIGINT NOT NULL PK
owner_user_id FK → User NOT NULL 업로드 요청 사용자
target_folder_id FK → Folder NOT NULL 완료 후 파일이 위치할 폴더
token VARCHAR NOT NULL 외부 공개용 식별자. UNIQUE
tus_upload_id VARCHAR NULL tus 서버와의 매핑 ID. UNIQUE 가능
status ENUM NOT NULL 업로드 상태 (아래 상태 흐름 참고)
expected_size BIGINT NOT NULL 전체 파일 크기 (bytes)
received_size BIGINT NOT NULL 현재까지 수신된 크기 (bytes)
original_name VARCHAR NOT NULL 업로드 원본 파일명
mime_type VARCHAR NULL MIME 타입
storage_key_temp VARCHAR NULL 임시 저장 경로
file_item_id FK → FileItem NULL 완료 후 생성된 FileItem 참조
created_at TIMESTAMP NOT NULL 업로드 세션 생성 일시
expires_at TIMESTAMP NULL 세션 만료 일시
completed_at TIMESTAMP NULL 업로드 완료 일시

설계 포인트

token 과 내부 id 분리

id     : 내부 DB 식별자  →  외부 노출 금지
token  : 클라이언트가 업로드 재개 시 사용하는 공개 식별자

status 상태 흐름

CREATED → UPLOADING → COMPLETED
                    → FAILED
        → CANCELLED
        → EXPIRED

업로드 완료 처리 흐름

업로드 완료 시 아래 세 작업을 하나의 트랜잭션으로 처리한다.

1. UploadSession.status  →  COMPLETED
2. FileItem 신규 생성
3. User.storage_used_bytes  +=  size_bytes

received_size 제약

received_size <= expected_size  -- 항상 유지

4. 관계 정의

관계 카디널리티 설명
UserFolder 1 : N 한 사용자는 여러 폴더를 소유
FolderFolder 1 : N 폴더는 하위 폴더를 가짐 (자기참조)
UserFileItem 1 : N 한 사용자는 여러 파일을 소유
FolderFileItem 1 : N 한 폴더는 여러 파일을 포함
UserUploadSession 1 : N 한 사용자는 여러 업로드 세션을 가짐
FolderUploadSession 1 : N 한 폴더는 여러 업로드 세션의 목적지가 됨
UploadSessionFileItem 1 : 0..1 완료된 세션은 하나의 FileItem을 생성

5. 제약 조건 요약

User

제약 내용
PK id
UNIQUE email
CHECK storage_used_bytes >= 0

Folder

제약 내용
PK id
FK owner_user_idUser.id NOT NULL
FK parent_folder_idFolder.id NULL 허용
UNIQUE (owner_user_id, parent_folder_id, name) — 활성 데이터 기준
정책 사용자당 루트 폴더(parent_folder_id IS NULL) 1개 권장

FileItem

제약 내용
PK id
FK owner_user_idUser.id NOT NULL
FK folder_idFolder.id NOT NULL
UNIQUE storage_key
UNIQUE (owner_user_id, folder_id, display_name) — 활성 데이터 기준
CHECK size_bytes >= 0

UploadSession

제약 내용
PK id
FK owner_user_idUser.id NOT NULL
FK target_folder_idFolder.id NOT NULL
FK file_item_idFileItem.id NULL 허용
UNIQUE token
UNIQUE tus_upload_id (NULL 제외)
CHECK received_size <= expected_size
CHECK expected_size >= 0

6. 설계 결정 사항 (Decision Log)

# 결정 사항 이유
1 storage_used_bytes 를 집계 캐시 컬럼으로 관리 매 조회 시 SUM 집계 대신 즉시 응답. 대신 업로드·삭제 트랜잭션에서 동기화 필수
2 full_path 를 캐시 컬럼으로 정의 폴더 이동·이름 변경 시 하위 경로 전체 갱신이 필요하지만, 조회·WebDAV·breadcrumb 성능 확보
3 storage_keydisplay_name 분리 파일 이름 변경이 저장소 객체 이동 없이 메타데이터 변경만으로 처리 가능
4 UploadSession 완료 처리를 단일 트랜잭션으로 FileItem 생성과 storage_used_bytes 증가의 정합성 보장
5 소프트 삭제 (deleted_at) 적용 실수 복구, 휴지통 기능 지원. 유니크 제약은 deleted_at IS NULL 조건부 인덱스로 처리
6 token (외부) / id (내부) 식별자 분리 내부 PK 노출 방지. 클라이언트는 token 으로만 업로드 세션 접근