From 29b9defadab64214b71e257b8a756e949bac096d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=84=9D=EB=B2=94?= Date: Wed, 17 Sep 2025 23:37:06 +0900 Subject: [PATCH 1/7] =?UTF-8?q?Chore:=20=EC=97=94=EB=93=9C=20=ED=8F=AC?= =?UTF-8?q?=EC=9D=B8=ED=8A=B8=20LF=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 10 ++++++++++ .gitattributes | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..ccccd2f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_style = space +indent_size = 2 + + diff --git a/.gitattributes b/.gitattributes index 56d6197..b681678 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,4 @@ -* text=auto +* text=auto eol=lf *.ts text eol=lf *.js text eol=lf *.json text eol=lf From de0e179e0cbdf0804486106888025e1da44769df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=84=9D=EB=B2=94?= Date: Wed, 17 Sep 2025 23:37:45 +0900 Subject: [PATCH 2/7] =?UTF-8?q?Feat:=20yjs,=20ws,=20swagger=20=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 210 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 8 +- 2 files changed, 214 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 62ff350..2242319 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,13 +12,19 @@ "@nestjs/common": "^11.0.1", "@nestjs/config": "^4.0.2", "@nestjs/core": "^11.0.1", + "@nestjs/mapped-types": "*", "@nestjs/platform-express": "^11.0.1", + "@nestjs/swagger": "^11.2.0", "@nestjs/typeorm": "^11.0.0", "jsonwebtoken": "^9.0.2", "mysql2": "^3.14.4", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", - "typeorm": "^0.3.26" + "swagger-ui-express": "^5.0.1", + "typeorm": "^0.3.26", + "ws": "^8.18.3", + "y-websocket": "^3.0.0", + "yjs": "^13.6.27" }, "devDependencies": { "@eslint/eslintrc": "^3.2.0", @@ -2057,6 +2063,12 @@ "node": ">=8" } }, + "node_modules/@microsoft/tsdoc": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.1.tgz", + "integrity": "sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==", + "license": "MIT" + }, "node_modules/@napi-rs/wasm-runtime": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", @@ -2400,6 +2412,26 @@ } } }, + "node_modules/@nestjs/mapped-types": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.1.0.tgz", + "integrity": "sha512-W+n+rM69XsFdwORF11UqJahn4J3xi4g/ZEOlJNL6KoW5ygWSmBB2p0S2BZ4FQeS/NDH72e6xIcu35SfJnE8bXw==", + "license": "MIT", + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "class-transformer": "^0.4.0 || ^0.5.0", + "class-validator": "^0.13.0 || ^0.14.0", + "reflect-metadata": "^0.1.12 || ^0.2.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, "node_modules/@nestjs/platform-express": { "version": "11.1.6", "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.6.tgz", @@ -2438,6 +2470,39 @@ "typescript": ">=4.8.2" } }, + "node_modules/@nestjs/swagger": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-11.2.0.tgz", + "integrity": "sha512-5wolt8GmpNcrQv34tIPUtPoV1EeFbCetm40Ij3+M0FNNnf2RJ3FyWfuQvI8SBlcJyfaounYVTKzKHreFXsUyOg==", + "license": "MIT", + "dependencies": { + "@microsoft/tsdoc": "0.15.1", + "@nestjs/mapped-types": "2.1.0", + "js-yaml": "4.1.0", + "lodash": "4.17.21", + "path-to-regexp": "8.2.0", + "swagger-ui-dist": "5.21.0" + }, + "peerDependencies": { + "@fastify/static": "^8.0.0", + "@nestjs/common": "^11.0.1", + "@nestjs/core": "^11.0.1", + "class-transformer": "*", + "class-validator": "*", + "reflect-metadata": "^0.1.12 || ^0.2.0" + }, + "peerDependenciesMeta": { + "@fastify/static": { + "optional": true + }, + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, "node_modules/@nestjs/testing": { "version": "11.1.6", "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.6.tgz", @@ -2579,6 +2644,13 @@ "url": "https://opencollective.com/pkgr" } }, + "node_modules/@scarf/scarf": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", + "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==", + "hasInstallScript": true, + "license": "Apache-2.0" + }, "node_modules/@sinclair/typebox": { "version": "0.34.41", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", @@ -3935,7 +4007,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, "license": "Python-2.0" }, "node_modules/array-timsort": { @@ -6466,6 +6537,16 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, + "node_modules/isomorphic.js": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz", + "integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==", + "license": "MIT", + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -7348,7 +7429,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -7505,6 +7585,27 @@ "node": ">= 0.8.0" } }, + "node_modules/lib0": { + "version": "0.2.114", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.114.tgz", + "integrity": "sha512-gcxmNFzA4hv8UYi8j43uPlQ7CGcyMJ2KQb5kZASw6SnAKAf10hK12i2fjrS3Cl/ugZa5Ui6WwIu1/6MIXiHttQ==", + "license": "MIT", + "dependencies": { + "isomorphic.js": "^0.2.4" + }, + "bin": { + "0ecdsa-generate-keypair": "bin/0ecdsa-generate-keypair.js", + "0gentesthtml": "bin/gentesthtml.js", + "0serve": "bin/0serve.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -9501,6 +9602,30 @@ "node": ">=8" } }, + "node_modules/swagger-ui-dist": { + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.21.0.tgz", + "integrity": "sha512-E0K3AB6HvQd8yQNSMR7eE5bk+323AUxjtCz/4ZNKiahOlPhPJxqn3UPIGs00cyY/dhrTDJ61L7C/a8u6zhGrZg==", + "license": "Apache-2.0", + "dependencies": { + "@scarf/scarf": "=1.4.0" + } + }, + "node_modules/swagger-ui-express": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.1.tgz", + "integrity": "sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==", + "license": "MIT", + "dependencies": { + "swagger-ui-dist": ">=5.0.0" + }, + "engines": { + "node": ">= v0.10.32" + }, + "peerDependencies": { + "express": ">=4.0.0 || >=5.0.0-beta" + } + }, "node_modules/symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", @@ -10906,6 +11031,27 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -10915,6 +11061,47 @@ "node": ">=0.4" } }, + "node_modules/y-protocols": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/y-protocols/-/y-protocols-1.0.6.tgz", + "integrity": "sha512-vHRF2L6iT3rwj1jub/K5tYcTT/mEYDUppgNPXwp8fmLpui9f7Yeq3OEtTLVF012j39QnV+KEQpNqoN7CWU7Y9Q==", + "license": "MIT", + "dependencies": { + "lib0": "^0.2.85" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + }, + "peerDependencies": { + "yjs": "^13.0.0" + } + }, + "node_modules/y-websocket": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/y-websocket/-/y-websocket-3.0.0.tgz", + "integrity": "sha512-mUHy7AzkOZ834T/7piqtlA8Yk6AchqKqcrCXjKW8J1w2lPtRDjz8W5/CvXz9higKAHgKRKqpI3T33YkRFLkPtg==", + "license": "MIT", + "dependencies": { + "lib0": "^0.2.102", + "y-protocols": "^1.0.5" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + }, + "peerDependencies": { + "yjs": "^13.5.6" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -10958,6 +11145,23 @@ "node": ">=12" } }, + "node_modules/yjs": { + "version": "13.6.27", + "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.27.tgz", + "integrity": "sha512-OIDwaflOaq4wC6YlPBy2L6ceKeKuF7DeTxx+jPzv1FHn9tCZ0ZwSRnUBxD05E3yed46fv/FWJbvR+Ud7x0L7zw==", + "license": "MIT", + "dependencies": { + "lib0": "^0.2.99" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/package.json b/package.json index 1c80a36..5a36b47 100644 --- a/package.json +++ b/package.json @@ -23,13 +23,19 @@ "@nestjs/common": "^11.0.1", "@nestjs/config": "^4.0.2", "@nestjs/core": "^11.0.1", + "@nestjs/mapped-types": "*", "@nestjs/platform-express": "^11.0.1", + "@nestjs/swagger": "^11.2.0", "@nestjs/typeorm": "^11.0.0", "jsonwebtoken": "^9.0.2", "mysql2": "^3.14.4", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", - "typeorm": "^0.3.26" + "swagger-ui-express": "^5.0.1", + "typeorm": "^0.3.26", + "ws": "^8.18.3", + "y-websocket": "^3.0.0", + "yjs": "^13.6.27" }, "devDependencies": { "@eslint/eslintrc": "^3.2.0", From 27dc06acb86d291eb6598b60f54ded16e4dbb141 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=84=9D=EB=B2=94?= Date: Wed, 17 Sep 2025 23:38:17 +0900 Subject: [PATCH 3/7] =?UTF-8?q?Chore:=20=ED=8C=A8=ED=82=A4=EC=A7=80=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.module.ts | 12 ++++++-- src/card/card.controller.spec.ts | 20 +++++++++++++ src/card/card.controller.ts | 7 +++++ src/card/card.module.ts | 9 ++++++ src/card/card.service.spec.ts | 18 +++++++++++ src/card/card.service.ts | 5 ++++ src/card/entities/card.entity.ts | 18 +++++++++++ .../cardset-manager.controller.spec.ts | 20 +++++++++++++ .../cardset-manager.controller.ts | 7 +++++ src/cardset-manager/cardset-manager.module.ts | 9 ++++++ .../cardset-manager.service.spec.ts | 18 +++++++++++ .../cardset-manager.service.ts | 6 ++++ .../entities/cardset-manager.entity.ts | 16 ++++++++++ src/cardset/cardset.controller.spec.ts | 20 +++++++++++++ src/cardset/cardset.controller.ts | 7 +++++ src/cardset/cardset.module.ts | 9 ++++++ src/cardset/cardset.service.spec.ts | 18 +++++++++++ src/cardset/cardset.service.ts | 6 ++++ src/cardset/entities/cardset.entity.ts | 30 +++++++++++++++++++ src/main.ts | 11 +++++++ 20 files changed, 264 insertions(+), 2 deletions(-) create mode 100644 src/card/card.controller.spec.ts create mode 100644 src/card/card.controller.ts create mode 100644 src/card/card.module.ts create mode 100644 src/card/card.service.spec.ts create mode 100644 src/card/card.service.ts create mode 100644 src/card/entities/card.entity.ts create mode 100644 src/cardset-manager/cardset-manager.controller.spec.ts create mode 100644 src/cardset-manager/cardset-manager.controller.ts create mode 100644 src/cardset-manager/cardset-manager.module.ts create mode 100644 src/cardset-manager/cardset-manager.service.spec.ts create mode 100644 src/cardset-manager/cardset-manager.service.ts create mode 100644 src/cardset-manager/entities/cardset-manager.entity.ts create mode 100644 src/cardset/cardset.controller.spec.ts create mode 100644 src/cardset/cardset.controller.ts create mode 100644 src/cardset/cardset.module.ts create mode 100644 src/cardset/cardset.service.spec.ts create mode 100644 src/cardset/cardset.service.ts create mode 100644 src/cardset/entities/cardset.entity.ts diff --git a/src/app.module.ts b/src/app.module.ts index 9abfcbb..f8373e5 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -2,6 +2,11 @@ import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { TypeOrmModule } from '@nestjs/typeorm'; import { AuthModule } from './auth/auth.module'; +import { CardsetModule } from './cardset/cardset.module'; +import { CardsetManagerModule } from './cardset-manager/cardset-manager.module'; +import { Cardset as CardSet } from './cardset/entities/cardset.entity'; +import { CardsetManager as CardSetManager } from './cardset-manager/entities/cardset-manager.entity'; +import { CardModule } from './card/card.module'; @Module({ imports: [ @@ -14,9 +19,12 @@ import { AuthModule } from './auth/auth.module'; username: process.env.DB_USERNAME, password: process.env.DB_PASSWORD, database: process.env.DB_DATABASE, - entities: [__dirname + '/**/*.entity{.ts,.js}'], - synchronize: process.env.NODE_ENnestV !== 'production', + entities: [CardSet, CardSetManager], + synchronize: false, }), + CardsetModule, + CardsetManagerModule, + CardModule, ], controllers: [], providers: [], diff --git a/src/card/card.controller.spec.ts b/src/card/card.controller.spec.ts new file mode 100644 index 0000000..bf807c6 --- /dev/null +++ b/src/card/card.controller.spec.ts @@ -0,0 +1,20 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { CardController } from './card.controller'; +import { CardService } from './card.service'; + +describe('CardController', () => { + let controller: CardController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [CardController], + providers: [CardService], + }).compile(); + + controller = module.get(CardController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/card/card.controller.ts b/src/card/card.controller.ts new file mode 100644 index 0000000..0932aa8 --- /dev/null +++ b/src/card/card.controller.ts @@ -0,0 +1,7 @@ +import { Controller } from '@nestjs/common'; +import { CardService } from './card.service'; + +@Controller('card') +export class CardController { + constructor(private readonly cardService: CardService) {} +} diff --git a/src/card/card.module.ts b/src/card/card.module.ts new file mode 100644 index 0000000..579e0cf --- /dev/null +++ b/src/card/card.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { CardService } from './card.service'; +import { CardController } from './card.controller'; + +@Module({ + controllers: [CardController], + providers: [CardService], +}) +export class CardModule {} diff --git a/src/card/card.service.spec.ts b/src/card/card.service.spec.ts new file mode 100644 index 0000000..782eb09 --- /dev/null +++ b/src/card/card.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { CardService } from './card.service'; + +describe('CardService', () => { + let service: CardService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [CardService], + }).compile(); + + service = module.get(CardService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/card/card.service.ts b/src/card/card.service.ts new file mode 100644 index 0000000..2213e19 --- /dev/null +++ b/src/card/card.service.ts @@ -0,0 +1,5 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class CardService { +} diff --git a/src/card/entities/card.entity.ts b/src/card/entities/card.entity.ts new file mode 100644 index 0000000..e45c838 --- /dev/null +++ b/src/card/entities/card.entity.ts @@ -0,0 +1,18 @@ +import { Cardset } from '../../cardset/entities/cardset.entity'; +import { Column, Entity, ManyToOne, PrimaryGeneratedColumn, JoinColumn } from 'typeorm'; + +@Entity('card') +export class Card { + @PrimaryGeneratedColumn({ type: 'int' }) + id!: number; + + @Column({ name: 'card_set_id', type: 'int', nullable: false }) + cardSetId!: number; + + @ManyToOne(() => Cardset, { createForeignKeyConstraints: false }) + @JoinColumn({ name: 'card_set_id' }) + cardset?: Cardset; + + @Column({ type: 'text', nullable: false }) + content!: string; +} \ No newline at end of file diff --git a/src/cardset-manager/cardset-manager.controller.spec.ts b/src/cardset-manager/cardset-manager.controller.spec.ts new file mode 100644 index 0000000..7b53c56 --- /dev/null +++ b/src/cardset-manager/cardset-manager.controller.spec.ts @@ -0,0 +1,20 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { CardsetManagerController } from './cardset-manager.controller'; +import { CardsetManagerService } from './cardset-manager.service'; + +describe('CardsetManagerController', () => { + let controller: CardsetManagerController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [CardsetManagerController], + providers: [CardsetManagerService], + }).compile(); + + controller = module.get(CardsetManagerController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/cardset-manager/cardset-manager.controller.ts b/src/cardset-manager/cardset-manager.controller.ts new file mode 100644 index 0000000..3d0652a --- /dev/null +++ b/src/cardset-manager/cardset-manager.controller.ts @@ -0,0 +1,7 @@ +import { Controller } from '@nestjs/common'; +import { CardsetManagerService } from './cardset-manager.service'; + +@Controller('cardset-manager') +export class CardsetManagerController { + constructor(private readonly cardsetManagerService: CardsetManagerService) {} +} diff --git a/src/cardset-manager/cardset-manager.module.ts b/src/cardset-manager/cardset-manager.module.ts new file mode 100644 index 0000000..f307ed2 --- /dev/null +++ b/src/cardset-manager/cardset-manager.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { CardsetManagerService } from './cardset-manager.service'; +import { CardsetManagerController } from './cardset-manager.controller'; + +@Module({ + controllers: [CardsetManagerController], + providers: [CardsetManagerService], +}) +export class CardsetManagerModule {} diff --git a/src/cardset-manager/cardset-manager.service.spec.ts b/src/cardset-manager/cardset-manager.service.spec.ts new file mode 100644 index 0000000..0beef93 --- /dev/null +++ b/src/cardset-manager/cardset-manager.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { CardsetManagerService } from './cardset-manager.service'; + +describe('CardsetManagerService', () => { + let service: CardsetManagerService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [CardsetManagerService], + }).compile(); + + service = module.get(CardsetManagerService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/cardset-manager/cardset-manager.service.ts b/src/cardset-manager/cardset-manager.service.ts new file mode 100644 index 0000000..ec47ce3 --- /dev/null +++ b/src/cardset-manager/cardset-manager.service.ts @@ -0,0 +1,6 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class CardsetManagerService { + +} diff --git a/src/cardset-manager/entities/cardset-manager.entity.ts b/src/cardset-manager/entities/cardset-manager.entity.ts new file mode 100644 index 0000000..823437b --- /dev/null +++ b/src/cardset-manager/entities/cardset-manager.entity.ts @@ -0,0 +1,16 @@ +import { Entity, PrimaryGeneratedColumn, Column, Index, Unique } from 'typeorm'; + +@Entity('card_set_managers') +@Unique(['userId', 'cardSetId']) +@Index('idx_card_set_manager_user', ['userId']) +@Index('idx_card_set_manager_cardset', ['cardSetId']) +export class CardsetManager { + @PrimaryGeneratedColumn({ type: 'int' }) + id!: number; + + @Column({ name: 'user_id', type: 'int', nullable: false }) + userId!: number; + + @Column({ name: 'card_set_id', type: 'int', nullable: false }) + cardSetId!: number; +} diff --git a/src/cardset/cardset.controller.spec.ts b/src/cardset/cardset.controller.spec.ts new file mode 100644 index 0000000..74b9be8 --- /dev/null +++ b/src/cardset/cardset.controller.spec.ts @@ -0,0 +1,20 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { CardsetController } from './cardset.controller'; +import { CardsetService } from './cardset.service'; + +describe('CardsetController', () => { + let controller: CardsetController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [CardsetController], + providers: [CardsetService], + }).compile(); + + controller = module.get(CardsetController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/cardset/cardset.controller.ts b/src/cardset/cardset.controller.ts new file mode 100644 index 0000000..f4fd1c4 --- /dev/null +++ b/src/cardset/cardset.controller.ts @@ -0,0 +1,7 @@ +import { Controller } from '@nestjs/common'; +import { CardsetService } from './cardset.service'; + +@Controller('cardset') +export class CardsetController { + constructor(private readonly cardsetService: CardsetService) {} +} diff --git a/src/cardset/cardset.module.ts b/src/cardset/cardset.module.ts new file mode 100644 index 0000000..2153f26 --- /dev/null +++ b/src/cardset/cardset.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { CardsetService } from './cardset.service'; +import { CardsetController } from './cardset.controller'; + +@Module({ + controllers: [CardsetController], + providers: [CardsetService], +}) +export class CardsetModule {} diff --git a/src/cardset/cardset.service.spec.ts b/src/cardset/cardset.service.spec.ts new file mode 100644 index 0000000..993a7fc --- /dev/null +++ b/src/cardset/cardset.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { CardsetService } from './cardset.service'; + +describe('CardsetService', () => { + let service: CardsetService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [CardsetService], + }).compile(); + + service = module.get(CardsetService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/cardset/cardset.service.ts b/src/cardset/cardset.service.ts new file mode 100644 index 0000000..d222c9d --- /dev/null +++ b/src/cardset/cardset.service.ts @@ -0,0 +1,6 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class CardsetService { + +} diff --git a/src/cardset/entities/cardset.entity.ts b/src/cardset/entities/cardset.entity.ts new file mode 100644 index 0000000..bc91d83 --- /dev/null +++ b/src/cardset/entities/cardset.entity.ts @@ -0,0 +1,30 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +@Entity('card_set') +export class Cardset { + @PrimaryGeneratedColumn({ type: 'int' }) + id!: number; + + @Column({ type: 'varchar', length: 50, nullable: false }) + name!: string; + + @Column({ name: 'group_id', type: 'int', nullable: false }) + groupId!: number; + + @Column({ + name: 'is_public', + type: 'boolean', + nullable: false, + default: false, + }) + publicVisible!: boolean; + + @Column({ type: 'varchar', length: 50, nullable: false }) + category!: string; + + @Column({ type: 'varchar', nullable: true }) + hashtag?: string | null; + + @Column({ name: 'image_url', type: 'varchar', nullable: false }) + imageUrl!: string; +} diff --git a/src/main.ts b/src/main.ts index f76bc8d..4b31676 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,8 +1,19 @@ import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; +import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; async function bootstrap() { const app = await NestFactory.create(AppModule); + + const config = new DocumentBuilder() + .setTitle('Flip Note API') + .setDescription('API documentation') + .setVersion('1.0.0') + .addBearerAuth() + .build(); + const document = SwaggerModule.createDocument(app, config); + SwaggerModule.setup('api-docs', app, document); + await app.listen(process.env.PORT ?? 3000); } bootstrap(); From a5301e1c93a8d748c97dfbc2c300504cf987f587 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=84=9D=EB=B2=94?= Date: Thu, 18 Sep 2025 14:36:50 +0900 Subject: [PATCH 4/7] =?UTF-8?q?Fix:=20=EC=B9=B4=EB=93=9C=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.module.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app.module.ts b/src/app.module.ts index f8373e5..0cec91c 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -7,7 +7,7 @@ import { CardsetManagerModule } from './cardset-manager/cardset-manager.module'; import { Cardset as CardSet } from './cardset/entities/cardset.entity'; import { CardsetManager as CardSetManager } from './cardset-manager/entities/cardset-manager.entity'; import { CardModule } from './card/card.module'; - +import { Card as Card } from './card/entities/card.entity'; @Module({ imports: [ AuthModule, @@ -19,7 +19,7 @@ import { CardModule } from './card/card.module'; username: process.env.DB_USERNAME, password: process.env.DB_PASSWORD, database: process.env.DB_DATABASE, - entities: [CardSet, CardSetManager], + entities: [CardSet, CardSetManager, Card], synchronize: false, }), CardsetModule, From 8a1c92131ac414aacd1d088605d3e6b92c0baf29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=84=9D=EB=B2=94?= Date: Thu, 18 Sep 2025 14:37:11 +0900 Subject: [PATCH 5/7] =?UTF-8?q?Chore:=20=EB=B2=84=EC=A0=84=20=EA=B3=A0?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5a36b47..a81190a 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "@nestjs/common": "^11.0.1", "@nestjs/config": "^4.0.2", "@nestjs/core": "^11.0.1", - "@nestjs/mapped-types": "*", + "@nestjs/mapped-types": "^2.1.0", "@nestjs/platform-express": "^11.0.1", "@nestjs/swagger": "^11.2.0", "@nestjs/typeorm": "^11.0.0", From 2684ff7df3ae5242fb1490e6f8aa5cb5f3845e64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=84=9D=EB=B2=94?= Date: Thu, 18 Sep 2025 14:37:30 +0900 Subject: [PATCH 6/7] =?UTF-8?q?Chore:=20=EC=8A=A4=EC=9B=A8=EA=B1=B0=20?= =?UTF-8?q?=EC=9A=B4=EC=98=81=ED=99=98=EA=B2=BD=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EB=B9=84=ED=99=9C=EC=84=B1=ED=99=94=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=A3=BC=EC=84=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main.ts b/src/main.ts index 4b31676..780532b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -13,6 +13,17 @@ async function bootstrap() { .build(); const document = SwaggerModule.createDocument(app, config); SwaggerModule.setup('api-docs', app, document); + // swagger 운영 환경에서 비활성화 + // if (process.env.SWAGGER_ENABLED === 'true' || process.env.NODE_ENV !== 'production') { + // const config = new DocumentBuilder() + // .setTitle('Flip Note API') + // .setDescription('API documentation') + // .setVersion('1.0.0') + // .addBearerAuth() + // .build(); + // const document = SwaggerModule.createDocument(app, config); + // SwaggerModule.setup('api-docs', app, document); + // } await app.listen(process.env.PORT ?? 3000); } From bab7f14091c498cffbb0c9b5d67e26b5d948d69f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=84=9D=EB=B2=94?= Date: Thu, 18 Sep 2025 14:38:03 +0900 Subject: [PATCH 7/7] =?UTF-8?q?Chore:=20=EC=BD=94=ED=8A=B8=20=EC=8A=A4?= =?UTF-8?q?=ED=83=80=EC=9D=BC=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/card/entities/card.entity.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/card/entities/card.entity.ts b/src/card/entities/card.entity.ts index e45c838..389bbd9 100644 --- a/src/card/entities/card.entity.ts +++ b/src/card/entities/card.entity.ts @@ -1,5 +1,11 @@ import { Cardset } from '../../cardset/entities/cardset.entity'; -import { Column, Entity, ManyToOne, PrimaryGeneratedColumn, JoinColumn } from 'typeorm'; +import { + Column, + Entity, + ManyToOne, + PrimaryGeneratedColumn, + JoinColumn, +} from 'typeorm'; @Entity('card') export class Card {