From d9bc084255cac3bf5a374b697122f22a89194128 Mon Sep 17 00:00:00 2001 From: guswlwlgus <20220784@duksung.ac.kr> Date: Thu, 19 Feb 2026 10:28:12 +0900 Subject: [PATCH] =?UTF-8?q?[fix]:=20=EB=82=B1=EB=A7=90=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20api=EC=97=90=EC=84=9C=20userCategoryId=EB=A7=8C=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/words/controllers/words.controller.js | 4 ++-- src/words/dto/words.dto.js | 6 +++--- src/words/repositories/words.repository.js | 20 +++++++++++--------- src/words/routes/words.route.js | 2 -- src/words/services/words.service.js | 22 ++++++++++++++++++++-- 5 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/words/controllers/words.controller.js b/src/words/controllers/words.controller.js index b369392..3bc915e 100644 --- a/src/words/controllers/words.controller.js +++ b/src/words/controllers/words.controller.js @@ -97,7 +97,7 @@ export class WordsController { const updateDto = new UpdateWordDto({ word: req.body.word, imageUrl: req.body.imageUrl, - categoryId: req.body.categoryId + userCategoryId: req.body.categoryId }); // 유효성 검증 @@ -108,7 +108,7 @@ export class WordsController { cardId, updateDto.word, updateDto.imageUrl, - updateDto.categoryId + updateDto.userCategoryId ); return res.success({ word: updatedWord }, '낱말 카드 수정 성공'); diff --git a/src/words/dto/words.dto.js b/src/words/dto/words.dto.js index 8ccfc4f..f3ca11a 100644 --- a/src/words/dto/words.dto.js +++ b/src/words/dto/words.dto.js @@ -71,14 +71,14 @@ export class UpdateFavoriteDto { * PATCH /api/words/:cardId - Update Word Request DTO */ export class UpdateWordDto { - constructor({ word, imageUrl, categoryId }) { + constructor({ word, imageUrl, userCategoryId }) { this.word = word; this.imageUrl = imageUrl; - this.categoryId = categoryId ? String(categoryId) : null; + this.userCategoryId = userCategoryId ? String(userCategoryId) : null; } validate() { - if (!this.word && !this.imageUrl && !this.categoryId) { + if (!this.word && !this.imageUrl && !this.userCategoryId) { throw new Error('word, imageUrl, categoryId 중 하나는 필수입니다'); } } diff --git a/src/words/repositories/words.repository.js b/src/words/repositories/words.repository.js index 5f8df8e..bc7bd85 100644 --- a/src/words/repositories/words.repository.js +++ b/src/words/repositories/words.repository.js @@ -301,18 +301,20 @@ export class WordsRepository { } /** - * UserWord 업데이트 (customWord, customImageUrl, categoryId) - * @param {string} userWordId - UserWord.id - * @param {string|null} customWord - * @param {string|null} customImageUrl - * @param {string|null} categoryId - * @returns {Promise} - */ - async updateUserWord(userWordId, customWord, customImageUrl, categoryId) { + * UserWord 업데이트 (customWord, customImageUrl, userCategoryId, displayOrder) + * @param {string} userWordId - UserWord.id + * @param {string|null} customWord + * @param {string|null} customImageUrl + * @param {string|null} userCategoryId + * @param {number|undefined} displayOrder + * @returns {Promise} + */ + async updateUserWord(userWordId, customWord, customImageUrl, userCategoryId, displayOrder) { const data = {}; if (customWord !== undefined) data.customWord = customWord; if (customImageUrl !== undefined) data.customImageUrl = customImageUrl; - if (categoryId !== undefined && categoryId !== null) data.categoryId = categoryId; + if (userCategoryId !== undefined && userCategoryId !== null) data.userCategoryId = userCategoryId; + if (displayOrder !== undefined) data.displayOrder = displayOrder; return await prisma.userWord.update({ where: { id: userWordId }, diff --git a/src/words/routes/words.route.js b/src/words/routes/words.route.js index f26d20f..6ce1dea 100644 --- a/src/words/routes/words.route.js +++ b/src/words/routes/words.route.js @@ -80,8 +80,6 @@ router.get('/', optionalAuthenticate, wordsController.getWords.bind(wordsControl * summary: 개인 낱말 카드 추가 * description: 사용자가 직접 낱말 카드를 추가합니다. NLP로 품사를 자동 분석합니다. * tags: [Words] - * security: - * - bearerAuth: [] * requestBody: * required: true * content: diff --git a/src/words/services/words.service.js b/src/words/services/words.service.js index 0d9870a..acdb444 100644 --- a/src/words/services/words.service.js +++ b/src/words/services/words.service.js @@ -224,13 +224,31 @@ export class WordsService { * @param {string|undefined} categoryId - 변경할 카테고리 ID * @returns {Promise} */ - async updateWord(userId, cardId, word, imageUrl, categoryId) { + async updateWord(userId, cardId, word, imageUrl, userCategoryId) { // 무조건 UserWord만 수정: 기본 Word 복사/생성 로직 제거 const userWord = await wordsRepository.findUserWordById(cardId); if (!userWord) { throw new Error('존재하지 않는 UserWord입니다'); } - const updatedUserWord = await wordsRepository.updateUserWord(cardId, word, imageUrl, categoryId); + + let displayOrder = undefined; + // 카테고리 변경 시 displayOrder 맨 뒤로 이동 및 기존 카테고리 재정렬 + if (userCategoryId && userWord.userCategoryId !== userCategoryId) { + // 1. 이동될 카테고리 맨 뒤로 + displayOrder = await this.getNextDisplayOrder(userId, null, userCategoryId); + + // 2. 기존 카테고리 displayOrder 재정렬 + const prevCategoryId = userWord.userCategoryId; + if (prevCategoryId) { + const prevWords = await wordsRepository.findUserWords(userId, prevCategoryId, false, false); + // 이동된 단어 제외 + const filtered = prevWords.filter(w => w.id !== cardId); + const updates = filtered.map((w, idx) => ({ userWordId: w.id, displayOrder: idx })); + await wordsRepository.bulkUpdateDisplayOrders(updates); + } + } + + const updatedUserWord = await wordsRepository.updateUserWord(cardId, word, imageUrl, userCategoryId, displayOrder); return new WordCardResponseDto({ cardId: updatedUserWord.id, categoryId: updatedUserWord.userCategoryId,