diff --git a/src/category/category.service.js b/src/category/category.service.js index 46ec638..8a44899 100644 --- a/src/category/category.service.js +++ b/src/category/category.service.js @@ -75,12 +75,10 @@ export const getCategoryListService = async ({ userId }) => { }); categories = await listUserCategories({ userId }); - } - // 기본 UserCategory 생성 후, 각 카테고리별 Word를 UserWord로 복사 + // 기본 UserCategory 생성 직후에만 복사 const wordsRepo = (await import("../words/repositories/words.repository.js")).default; for (const userCategory of categories.filter(c => c.isDefault)) { - // 기본 카테고리 이름으로 Category 찾기 const category = await prisma.category.findFirst({ where: { categoryName: userCategory.categoryName, @@ -88,10 +86,10 @@ export const getCategoryListService = async ({ userId }) => { } }); if (category) { - // 해당 Category의 Word를 UserWord로 복사 await wordsRepo.createSnapshotFromWords(userId, userCategory.id); } } + } return Promise.all( categories.map(async (c) => { diff --git a/src/words/controllers/words.controller.js b/src/words/controllers/words.controller.js index d7f3a6c..b369392 100644 --- a/src/words/controllers/words.controller.js +++ b/src/words/controllers/words.controller.js @@ -11,6 +11,7 @@ export class WordsController { async getWords(req, res, next) { try { const userId = req.user?.userId; + const accountType = req.user?.accountType; const queryDto = new GetWordsQueryDto({ categoryId: req.query.categoryId, @@ -19,6 +20,7 @@ export class WordsController { const result = await wordsService.getWords( userId, + accountType, queryDto.categoryId, queryDto.onlyFavorite ); diff --git a/src/words/routes/words.route.js b/src/words/routes/words.route.js index 6ad547b..765440e 100644 --- a/src/words/routes/words.route.js +++ b/src/words/routes/words.route.js @@ -16,9 +16,10 @@ const router = express.Router(); * get: * summary: 낱말 카드 조회 * description: | - * 낱말 카드 목록을 조회합니다. - * 토큰이 있으면 사용자별 낱말(즐겨찾기/개인화 포함), 없으면 기본 낱말만 반환합니다. - * categoryId가 있으면 data.category(카테고리 이름)도 함께 반환됩니다. +* 낱말 카드 목록을 조회합니다. +* 토큰이 있으면 게스트/소셜 계정 모두 사용자별 낱말(즐겨찾기/개인화 포함)을 조회합니다. +* 토큰이 없으면 기본 낱말만 반환합니다. +* categoryId가 있으면 data.category(카테고리 이름)도 함께 반환됩니다. * tags: [Words] * parameters: * - in: query diff --git a/src/words/services/words.service.js b/src/words/services/words.service.js index 9ce6e87..0d9870a 100644 --- a/src/words/services/words.service.js +++ b/src/words/services/words.service.js @@ -9,13 +9,13 @@ export class WordsService { /** * 내부용: 전체 낱말 카드 목록 생성 (categoryId 없이) */ - async _getAllWordCards(userId, onlyFavorite = false) { + async _getAllWordCards(userId, accountType, onlyFavorite = false) { const wordCards = []; let categoryNameToUserCategoryIdMap = new Map(); let categoryNameToCategoryIdMap = new Map(); let hasUserCategories = false; - if (userId) { + if (userId && accountType !== 'GUEST') { const userCategories = await wordsRepository.findAllUserCategories(userId); if (userCategories.length > 0) { hasUserCategories = true; @@ -44,17 +44,14 @@ export class WordsService { }); } - // 소셜 로그인(유저가 존재) 시에는 기본 Word를 반환하지 않고 UserWord만 반환 - // 게스트(유저 없음)일 때만 기본 Word 반환 - if (!onlyFavorite && !userId) { - const words = await wordsRepository.findWords(null, userId); + // 소셜/게스트 로그인(유저가 존재) 시에는 UserWord 기반, 비회원(토큰 없음)만 기본 Word 반환 + if (!onlyFavorite && !userId && !accountType) { + const words = await wordsRepository.findWords(); words.forEach((word, index) => { - const wordCategoryName = word.category?.categoryName; - let mappedCategoryId = categoryNameToCategoryIdMap.get(wordCategoryName) || word.categoryId; wordCards.push(new WordCardResponseDto({ cardId: word.id, - categoryId: mappedCategoryId, - categoryName: wordCategoryName, + categoryId: word.categoryId, + categoryName: word.category?.categoryName, partOfSpeech: word.partOfSpeech, word: word.word, imageUrl: word.imageUrl, @@ -101,20 +98,20 @@ export class WordsService { /** * 다음 displayOrder 계산 (기본 Word + UserWord 모두 고려) * @param {string} userId + * @param {string} accountType * @param {string} categoryId * @returns {Promise} */ - async getNextDisplayOrder(userId, categoryId) { + async getNextDisplayOrder(userId, accountType, categoryId) { // 1. UserWord 조회 (삭제된 것 제외) - const userWords = await wordsRepository.findUserWords(userId, categoryId, false, false); - - // 2. UserWord가 있으면 최대값 + 1 반환 - if (userWords.length > 0) { - const maxOrder = Math.max(...userWords.map(w => w.displayOrder)); - return maxOrder + 1; + if (accountType !== 'GUEST') { + const userWords = await wordsRepository.findUserWords(userId, categoryId, false, false); + if (userWords.length > 0) { + const maxOrder = Math.max(...userWords.map(w => w.displayOrder)); + return maxOrder + 1; + } } - - // 3. UserWord가 없으면 기본 Word 개수 반환 + // 게스트이거나 UserWord가 없으면 기본 Word 개수 반환 const words = await wordsRepository.findWords(categoryId, userId); return words.length; } @@ -124,13 +121,14 @@ export class WordsService { * 기본 낱말(Word) + 개인 낱말(UserWord) 통합 반환 * * @param {string} userId + * @param {string} accountType * @param {string|null} categoryId - Category.id 또는 UserCategory.id * @param {boolean} onlyFavorite * @returns {Promise} { category, words } */ - async getWords(userId, categoryId = null, onlyFavorite = false) { + async getWords(userId, accountType, categoryId = null, onlyFavorite = false) { // 1. 전체 낱말 목록 생성 (categoryId 없이) - const allWords = await this._getAllWordCards(userId, onlyFavorite); + const allWords = await this._getAllWordCards(userId, accountType, onlyFavorite); // 2. categoryId가 없으면 전체 반환 if (!categoryId) {