Skip to content

Conversation

@KimDwDev
Copy link
Collaborator

@KimDwDev KimDwDev commented Dec 21, 2025

🎯 이슈 번호


✅ 작업 내용

ERD를 작성하였습니다.

owl_cloud_erd7 drawio

스키마를 작성했습니다.

  • 현재 ERD에 내용 중 작성하지 않은 스키마는 Card_comments 부분 입니다.

Users

1. 스키마

CREATE TABLE `Users` (
  user_id BINARY(16) PRIMARY KEY NOT NULL,
  email VARCHAR(255) NOT NULL UNIQUE,
  nickname VARCHAR(16) NOT NULL,
  password_hash VARCHAR(255),
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

Oauth_users

1. 스키마

CREATE TABLE `Oauth_users` (
  id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
  user_id BINARY(16) NOT NULL UNIQUE,
  `provider` VARCHAR(15) NOT NULL,
  provider_id VARCHAR(255) NOT NULL,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

  CONSTRAINT fk_user_oauth_user FOREIGN KEY (`user_id`) REFERENCES `Users`(`user_id`) ON UPDATE CASCADE ON DELETE CASCADE,
  CONSTRAINT check_oauth_user_provider CHECK(`provider` IN ('kakao', 'google'))
);

User_profiles

1. 스키마

CREATE TABLE `User_profiles` (
  id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
  user_id BINARY(16) NOT NULL UNIQUE,
  profile_path VARCHAR(255) NOT NULL,
  mime_type VARCHAR(15) NOT NULL,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

  CONSTRAINT fk_user_user_profile FOREIGN KEY (`user_id`) REFERENCES `Users`(`user_id`) ON UPDATE CASCADE ON DELETE CASCADE,
  CONSTRAINT check_user_profile_mime_type CHECK(`mime_type` IN ('image/apng', 'image/avif', 'image/gif', 'image/jpeg', 'image/png', 'image/svg+xml', 'image/webp'))
);

Delete_users

1. 스키마

CREATE TABLE `Delete_users` (
  id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
  user_id BINARY(16) NOT NULL,
  email VARCHAR(255) NOT NULL,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

User_follows

1. 스키마

CREATE TABLE `User_follows`(
  id INT UNSIGNED PRIMARY KEY NOT NULL AUTO_INCREMENT,
  follower_id BINARY(16) NOT NULL,
  following_id BINARY(16) NOT NULL,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,

  CONSTRAINT check_user_follows_follower_following_id UNIQUE(`follower_id`, `following_id`),
  CONSTRAINT check_user_follows_follower CHECK(`follower_id` <> `following_id`)
);

2. INDEX

CREATE INDEX idx_user_follows_following_created_at 
  ON `User_follows` (`following_id`, `created_at`);

Cards

1. 스키마

CREATE TABLE `Cards` (
  card_id BINARY(16) PRIMARY KEY NOT NULL,
  user_id BINARY(16) NOT NULL,
  category_id INT UNSIGNED NOT NULL,
  thumbnail_path VARCHAR(2048),
  `status` ENUM('published', 'draft', 'archived') NOT NULL,
  title VARCHAR(255) NOT NULL,
  workspace_width INT UNSIGNED NOT NULL,
  workspace_height INT UNSIGNED NOT NULL,
  background_color VARCHAR(32) NOT NULL,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  deleted_at TIMESTAMP,

  CONSTRAINT check_cards_workspace_width_workspace_height CHECK(`workspace_width` > 0 AND `workspace_height` > 0),

  CONSTRAINT check_background_color
    CHECK (
      background_color REGEXP '^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$'
      OR background_color REGEXP '^rgb\\([0-9]{1,3},[0-9]{1,3},[0-9]{1,3}\\)$'
      OR background_color REGEXP '^rgba\\([0-9]{1,3},[0-9]{1,3},[0-9]{1,3},(0(\\.[0-9]+)?|1(\\.0+)?)\\)$'
      OR background_color IN ('transparent', 'currentColor')
    )
);

2. 인덱싱

CREATE INDEX idx_cards_user_status_deleted_updated
  ON `Cards`(user_id, `status`, deleted_at, updated_at);

CREATE INDEX idx_cards_category_status_deleted_updated
  ON `Cards` (category_id, `status`, deleted_at, updated_at);

Card_items

1. 스키마

CREATE TABLE `Card_items` (
  item_id BINARY(16) PRIMARY KEY NOT NULL,
  card_id BINARY(16) NOT NULL,
  `type` ENUM('text', 'image', 'video') NOT NULL,
  x INT NOT NULL,
  y INT NOT NULL,
  width INT UNSIGNED NOT NULL,
  height INT UNSIGNED,
  rotation SMALLINT NOT NULL DEFAULT 0,
  scale_x DECIMAL(6,3) NOT NULL DEFAULT 1.000,
  scale_y DECIMAL(6,3) NOT NULL DEFAULT 1.000,
  opacity DECIMAL(3,2),
  z_index INT,
  is_locked BOOLEAN,
  is_visible BOOLEAN,
  `name` VARCHAR(255),
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  deleted_at TIMESTAMP,

  CONSTRAINT fk_card_card_items_card_id FOREIGN KEY (`card_id`) REFERENCES `Cards`(`card_id`) ON UPDATE CASCADE ON DELETE CASCADE,
  CONSTRAINT check_card_items_opacity CHECK(`opacity` IS NULL OR (`opacity` >= 0 AND `opacity` <= 1) )
);

2. INDEX

CREATE INDEX idx_card_items_card_id_deleted_at_z_index
  ON `Card_items`(`card_id`, `deleted_at`, `z_index`);

Card_stats

1. 스키마

CREATE TABLE `Card_stats`(
  id INT UNSIGNED PRIMARY KEY NOT NULL AUTO_INCREMENT,
  card_id BINARY(16) NOT NULL UNIQUE,
  like_count BIGINT UNSIGNED NOT NULL DEFAULT 0,
  view_count BIGINT UNSIGNED NOT NULL DEFAULT 0,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

  CONSTRAINT fk_card_card_stat_card_id FOREIGN KEY (`card_id`) REFERENCES `Cards`(`card_id`) ON UPDATE CASCADE ON DELETE CASCADE
);

Categories

1. 스키마

CREATE TABLE `Categories` (
  id INT UNSIGNED PRIMARY KEY NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(255) NOT NULL UNIQUE,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

Card_likes

1. 스키마

CREATE TABLE `Card_likes` (
  id INT UNSIGNED PRIMARY KEY NOT NULL AUTO_INCREMENT,
  card_id BINARY(16) NOT NULL,
  user_id BINARY(16) NOT NULL,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,

  CONSTRAINT check_card_likes_card_id_user_id UNIQUE(`card_id`, `user_id`)
);

Card_item_texts

1. 스키마

CREATE TABLE `Card_item_texts` (
  item_id BINARY(16) PRIMARY KEY NOT NULL,
  `text` TEXT NOT NULL,
  font_size INT UNSIGNED NOT NULL,
  font_family VARCHAR(100) NOT NULL,
  fill VARCHAR(32) NOT NULL,
  font_style VARCHAR(100) NOT NULL,
  text_decoration VARCHAR(100) NOT NULL,
  align ENUM('left', 'center', 'right', 'justify') NOT NULL,
  wrap VARCHAR(20) NOT NULL,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

  CONSTRAINT fk_card_item_card_item_text_item_id FOREIGN KEY (`item_id`) REFERENCES `Card_items`(`item_id`) ON UPDATE CASCADE ON DELETE CASCADE,
  CONSTRAINT check_card_item_texts_font_size CHECK(`font_size` > 0),
  CONSTRAINT check_card_item_fill
    CHECK (
      fill REGEXP '^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$'
      OR fill REGEXP '^rgb\\([0-9]{1,3},[0-9]{1,3},[0-9]{1,3}\\)$'
      OR fill REGEXP '^rgba\\([0-9]{1,3},[0-9]{1,3},[0-9]{1,3},(0(\\.[0-9]+)?|1(\\.0+)?)\\)$'
      OR fill IN ('transparent', 'currentColor')
    )
);

Card_item_images

1. 스키마

CREATE TABLE `Card_item_images` (
  item_id BINARY(16) PRIMARY KEY NOT NULL,
  src VARCHAR(2048) NOT NULL,
  mime_type VARCHAR(15) NOT NULL,
  `size` INT UNSIGNED NOT NULL, 
  `status` ENUM('uploading', 'ready', 'failed') NOT NULL,
  natural_width INT UNSIGNED NOT NULL,
  natural_height INT UNSIGNED NOT NULL,
  corner_radius INT UNSIGNED,
  stroke VARCHAR(32),
  stroke_width INT UNSIGNED,
  `filter` VARCHAR(255),
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 
  
  CONSTRAINT fk_card_item_card_item_image_item_id FOREIGN KEY (`item_id`) REFERENCES `Card_items`(`item_id`) ON UPDATE CASCADE ON DELETE CASCADE,
  CONSTRAINT check_card_item_image_mime_type CHECK(`mime_type` IN ('image/apng', 'image/avif', 'image/gif', 'image/jpeg', 'image/png', 'image/svg+xml', 'image/webp')),
  CONSTRAINT check_card_item_image_natural_width_natural_height CHECK(`natural_width` > 0 AND `natural_height` > 0),
  CONSTRAINT check_card_item_image_stroke
    CHECK (
      stroke IS NULL
      OR stroke REGEXP '^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$'
      OR stroke REGEXP '^rgb\\([0-9]{1,3},[0-9]{1,3},[0-9]{1,3}\\)$'
      OR stroke REGEXP '^rgba\\([0-9]{1,3},[0-9]{1,3},[0-9]{1,3},(0(\\.[0-9]+)?|1(\\.0+)?)\\)$'
      OR stroke IN ('transparent', 'currentColor') 
    )
);

Card_item_videos

1. 스키마

CREATE TABLE `Card_item_videos`(
  item_id BINARY(16) PRIMARY KEY NOT NULL,
  src VARCHAR(2048) NOT NULL,
  mime_type VARCHAR(50) NOT NULL,
  `size` BIGINT UNSIGNED NOT NULL, 
  `status` ENUM('uploading', 'ready', 'failed') NOT NULL,
  poster VARCHAR(2048),
  natural_width INT UNSIGNED NOT NULL,
  natural_height INT UNSIGNED NOT NULL,
  duration INT,
  is_auto_play BOOLEAN NOT NULL DEFAULT FALSE,
  is_loop BOOLEAN NOT NULL DEFAULT FALSE,
  is_muted BOOLEAN NOT NULL DEFAULT FALSE,
  volume INT UNSIGNED NOT NULL DEFAULT 100,
  corner_radius INT UNSIGNED,
  stroke VARCHAR(32),
  stroke_width INT UNSIGNED,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 

  CONSTRAINT fk_card_item_card_item_video_item_id FOREIGN KEY (`item_id`) REFERENCES `Card_items`(`item_id`) ON UPDATE CASCADE ON DELETE CASCADE,
  CONSTRAINT check_card_item_video_mime_type CHECK(`mime_type` IN ('video/mp4', 'video/webm', 'video/ogg')),
  CONSTRAINT check_card_item_video_volume CHECK(`volume` BETWEEN 0 AND 100),
  CONSTRAINT check_card_item_video_duration CHECK (`duration` IS NULL OR `duration` >= 0),
  CONSTRAINT check_card_item_video_corner_radius CHECK (`corner_radius` IS NULL OR `corner_radius` >= 0),
  CONSTRAINT check_card_item_video_stroke_width CHECK (`stroke_width` IS NULL OR `stroke_width` >= 0),
  CONSTRAINT check_card_item_video_natural_width_natural_height CHECK(`natural_width` > 0 AND `natural_height` > 0),
    CONSTRAINT check_card_item_video_stroke
    CHECK (
      stroke IS NULL
      OR stroke REGEXP '^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$'
      OR stroke REGEXP '^rgb\\([0-9]{1,3},[0-9]{1,3},[0-9]{1,3}\\)$'
      OR stroke REGEXP '^rgba\\([0-9]{1,3},[0-9]{1,3},[0-9]{1,3},(0(\\.[0-9]+)?|1(\\.0+)?)\\)$'
      OR stroke IN ('transparent', 'currentColor') 
    )
);  

🤔 리뷰 요구사항

  • 리뷰 시 중점적으로 봐주었으면 하는 부분

  • 스키마와 ERD 확인해주신 후 이대로 확정해도 괜찮을지 의견 주시면 좋을 것 같아요

  • 수정할 부분이 있으시다면 적어도 오늘 이내로는 알려주시면 좋을 것 같아요...


📸 스크린샷 (선택)

@KimDwDev KimDwDev self-assigned this Dec 21, 2025
@KimDwDev KimDwDev linked an issue Dec 21, 2025 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BE] 데이터 베이스 설계

2 participants