안동민 개발노트

본문 시작

프로젝트·파일 데이터 모델

프로젝트와 파일·폴더 엔티티를 정의하고 소유 관계와 계층 구조를 구성합니다.

프로젝트 및 파일 관리 API 구현

Project의 소유자와 File·Folder ORM 관계

앞 문서의 User와 Project는 owner와 projects로 연결됩니다. Project의 files와 folders는 각각 File과 Folder의 project 관계에 대응합니다. 두 자식 관계에는 Project 삭제 시 자식도 삭제하는 CASCADE가 설정되어 있고, path는 부모 관계가 아닌 문자열입니다.

소유 사용자, 프로젝트, 파일과 폴더의 세 일대다 관계 User.projects와 Project.owner, Project.files와 File.project, Project.folders와 Folder.project가 각각 일대다 관계를 이루며, id와 ownerId 또는 projectId로 대상을 식별하고 File과 Folder의 path는 문자열로 저장합니다. 1 · projects N · owner 1 · files N · project 1 · folders N · project User id 앞 문서의 외부참조 Project id ownerId File id projectId path: '/src/main.ts' Folder id projectId path: '/src'
User — 앞 문서의 외부참조
id
Project
id, ownerId
File
id, projectId, 문자열 path 예: /src/main.ts
Folder
id, projectId, 문자열 path 예: /src
  • User 1 : Project N — User.projects와 Project.owner
  • Project 1 : File N — Project.files와 File.project
  • Project 1 : Folder N — Project.folders와 Folder.project

1:N은 일대다 관계를 나타냅니다. 필드는 관계 식별에 필요한 부분만 표시했습니다.

  • Project 삭제 → 해당 File·Folder 삭제. File.project와 Folder.project에 onDelete: 'CASCADE'가 설정되어 있습니다.
  • path는 경로 문자열입니다. 이 엔티티에는 폴더의 부모 FK나 File–Folder 관계가 없습니다. 폴더 경로 삭제는 별도 서비스 로직입니다.

이제 사용자 인증이 완료된 후 접근할 수 있는 프로젝트 및 파일 관리 API를 구현합니다.

Project Entity

src/project/entities/project.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, OneToMany, CreateDateColumn, UpdateDateColumn } from 'typeorm';
import { User } from '../../auth/entities/user.entity'; // User 엔티티 임포트
import { File } from '../../file/entities/file.entity'; // File 엔티티 임포트 (미리 정의했다고 가정)
import { Folder } from '../../folder/entities/folder.entity'; // Folder 엔티티 임포트 (미리 정의했다고 가정)

@Entity()
export class Project {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column({ nullable: false })
  name: string;

  @Column({ nullable: false })
  ownerId: string; // 소유자 ID

  @ManyToOne(() => User, user => user.projects)
  owner: User; // User 엔티티와의 관계

  @OneToMany(() => File, file => file.project)
  files: File[];

  @OneToMany(() => Folder, folder => folder.project)
  folders: Folder[];

  @CreateDateColumn()
  createdAt: Date;

  @UpdateDateColumn()
  updatedAt: Date;
}

File & Folder Entities

src/file/entities/file.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, CreateDateColumn, UpdateDateColumn } from 'typeorm';
import { Project } from '../../project/entities/project.entity';

@Entity()
export class File {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column({ nullable: false })
  name: string;

  @Column({ nullable: false })
  path: string; // 예: '/src/main.ts'

  @Column({ type: 'text', nullable: true })
  content: string; // 파일 내용 (TEXT 타입)

  @Column({ nullable: true })
  type: string; // 파일 타입 (예: 'javascript', 'python')

  @Column({ nullable: false })
  projectId: string;

  @ManyToOne(() => Project, project => project.files, { onDelete: 'CASCADE' }) // 프로젝트 삭제 시 파일도 삭제
  project: Project;

  @CreateDateColumn()
  createdAt: Date;

  @UpdateDateColumn()
  updatedAt: Date;
}
src/folder/entities/folder.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, CreateDateColumn, UpdateDateColumn } from 'typeorm';
import { Project } from '../../project/entities/project.entity';

@Entity()
export class Folder {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column({ nullable: false })
  name: string;

  @Column({ nullable: false })
  path: string; // 예: '/src'

  @Column({ nullable: false })
  projectId: string;

  @ManyToOne(() => Project, project => project.folders, { onDelete: 'CASCADE' }) // 프로젝트 삭제 시 폴더도 삭제
  project: Project;

  @CreateDateColumn()
  createdAt: Date;

  @UpdateDateColumn()
  updatedAt: Date;
}