From 4b1cd2c0935711248bf212d63a8bf3617c5cdf9f Mon Sep 17 00:00:00 2001 From: lilloo04 Date: Thu, 17 Jul 2025 15:54:17 +0900 Subject: [PATCH 1/2] =?UTF-8?q?refactor:=20getBook=20=EC=9A=94=EC=B2=AD=20?= =?UTF-8?q?=EC=8B=A4=ED=8C=A8=20=EC=8B=9C=20=ED=95=B4=EB=8B=B9=20=ED=95=AD?= =?UTF-8?q?=EB=AA=A9=20=EA=B1=B4=EB=84=88=EB=9C=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/getProfileFeed.js | 71 +++++++++++----------------------- 1 file changed, 23 insertions(+), 48 deletions(-) diff --git a/src/services/getProfileFeed.js b/src/services/getProfileFeed.js index bb859d0..13f5109 100644 --- a/src/services/getProfileFeed.js +++ b/src/services/getProfileFeed.js @@ -17,9 +17,7 @@ const getBook = new GetBook({ accessToken: SSC_TOKEN }); * @returns {Promise} 응답 DTO. */ export default async function getProfileFeed(reqDto, user) { - // (1) 유저 ID 디코드 if (!user.is_admin && user.id !== reqDto.id) { - // 권한 없음 throw new FetchError('Unauthorized request.', 403); } @@ -32,12 +30,10 @@ export default async function getProfileFeed(reqDto, user) { }; } - // (2) 페이지네이션 파라미터 설정 const limit = clamp(reqDto.limit, 1, 100, 10); const page = reqDto.page ?? 0; const offset = page * limit; - // (3) 메모와 리뷰 조회 const [memoCounts, reviewCounts] = await Promise.all([ Memo.findAll({ attributes: [ @@ -45,10 +41,7 @@ export default async function getProfileFeed(reqDto, user) { [fn('COUNT', col('memo_id')), 'memo_count'], [fn('MAX', col('updated_at')), 'last_updated'], ], - where: { - writer_id: userId, - deleted_at: null, - }, + where: { writer_id: userId, deleted_at: null }, group: ['book_isbn'], raw: true, }).catch(handleSqlError), @@ -58,17 +51,13 @@ export default async function getProfileFeed(reqDto, user) { [fn('COUNT', col('review_id')), 'review_count'], [fn('MAX', col('updated_at')), 'last_updated'], ], - where: { - writer_id: userId, - deleted_at: null, - }, + where: { writer_id: userId, deleted_at: null }, group: ['book_isbn'], raw: true, }).catch(handleSqlError), ]); const contentMap = new Map(); - /* eslint-disable camelcase */ memoCounts.forEach(({ book_isbn, memo_count, last_updated }) => { contentMap.set(book_isbn, { memoCount: Number(memo_count) || 0, @@ -76,7 +65,6 @@ export default async function getProfileFeed(reqDto, user) { lastUpdated: last_updated, }); }); - reviewCounts.forEach(({ book_isbn, review_count, last_updated }) => { if (!contentMap.has(book_isbn)) { contentMap.set(book_isbn, { @@ -92,47 +80,34 @@ export default async function getProfileFeed(reqDto, user) { } } }); - /* eslint-enable camelcase */ - // (5) 페이지네이션 적용 const entries = Array.from(contentMap.entries()) - .sort((a, b) => { - // 최신 활동 시간 기준으로 정렬 - return new Date(b[1].lastUpdated) - new Date(a[1].lastUpdated); - }) + .sort((a, b) => new Date(b[1].lastUpdated) - new Date(a[1].lastUpdated)) .slice(offset, offset + limit); - // (6) 도서 정보와 우물 정보 조회 - const items = await Promise.all( - entries.map(async ([isbn, counts]) => { - // (1) 도서 정보 조회 - const bookInfo = await getBook.fetch({ isbn }); + const items = []; + for (const [isbn, counts] of entries) { + let bookInfo = null; + try { + bookInfo = await getBook.fetch({ isbn }); + } catch (err) { + // 실패 시 해당 항목은 건너뜀 + continue; + } - // (2) 우물 정보 조회 - const well = await Well.findOne({ - include: [ - { - model: WellItem, - where: { - book_isbn: isbn, - }, - }, - ], - where: { - owner_id: userId, - }, - }).catch(handleSqlError); + const well = await Well.findOne({ + include: [{ model: WellItem, where: { book_isbn: isbn } }], + where: { owner_id: userId }, + }).catch(handleSqlError); - return { - memoCount: counts.memoCount, - reviewCount: counts.reviewCount, - book: bookInfo, - wellId: well ? encodeHashId(well.well_id) : undefined, - }; - }), - ); + items.push({ + memoCount: counts.memoCount, + reviewCount: counts.reviewCount, + book: bookInfo, + wellId: well ? encodeHashId(well.well_id) : undefined, + }); + } - // (7) 데이터 반환 return { items, count: contentMap.size, From bfb77bfcee55f9a9c31252ac80b40719dbb5794f Mon Sep 17 00:00:00 2001 From: lilloo04 Date: Thu, 17 Jul 2025 16:02:44 +0900 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20lint=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/getProfileFeed.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/services/getProfileFeed.js b/src/services/getProfileFeed.js index 13f5109..87882d3 100644 --- a/src/services/getProfileFeed.js +++ b/src/services/getProfileFeed.js @@ -17,7 +17,9 @@ const getBook = new GetBook({ accessToken: SSC_TOKEN }); * @returns {Promise} 응답 DTO. */ export default async function getProfileFeed(reqDto, user) { + // (1) 유저 ID 디코드 및 권한 검사 if (!user.is_admin && user.id !== reqDto.id) { + // 권한 없음 throw new FetchError('Unauthorized request.', 403); } @@ -30,10 +32,12 @@ export default async function getProfileFeed(reqDto, user) { }; } + // (2) 페이지네이션 파라미터 설정 const limit = clamp(reqDto.limit, 1, 100, 10); const page = reqDto.page ?? 0; const offset = page * limit; + // (3) 메모와 리뷰 집계 조회 const [memoCounts, reviewCounts] = await Promise.all([ Memo.findAll({ attributes: [ @@ -57,7 +61,9 @@ export default async function getProfileFeed(reqDto, user) { }).catch(handleSqlError), ]); + // (4) ISBN 별 메모/리뷰 정보 병합 const contentMap = new Map(); + /* eslint-disable camelcase */ memoCounts.forEach(({ book_isbn, memo_count, last_updated }) => { contentMap.set(book_isbn, { memoCount: Number(memo_count) || 0, @@ -80,21 +86,26 @@ export default async function getProfileFeed(reqDto, user) { } } }); + /* eslint-enable camelcase */ + // (5) 최신 활동 기준 정렬 및 페이지네이션 적용 const entries = Array.from(contentMap.entries()) .sort((a, b) => new Date(b[1].lastUpdated) - new Date(a[1].lastUpdated)) .slice(offset, offset + limit); + // (6) 도서 정보 및 우물 정보 조회 const items = []; for (const [isbn, counts] of entries) { + // (6.1) 도서 정보 조회 (실패 시 null 반환) let bookInfo = null; try { bookInfo = await getBook.fetch({ isbn }); } catch (err) { - // 실패 시 해당 항목은 건너뜀 + // 도서 정보 조회 실패 시 해당 항목은 건너뜀 continue; } + // (6.2) 우물 정보 조회 const well = await Well.findOne({ include: [{ model: WellItem, where: { book_isbn: isbn } }], where: { owner_id: userId }, @@ -108,6 +119,7 @@ export default async function getProfileFeed(reqDto, user) { }); } + // (7) 데이터 반환 return { items, count: contentMap.size,