Skip to content

Commit

Permalink
Merge pull request #2 from boostcampwm-2024/refactor/feed-crawler-oop
Browse files Browse the repository at this point in the history
♻️ refactor: FeedCrawler OOP 적용
  • Loading branch information
asn6878 authored Jan 7, 2025
2 parents 5c4dc05 + c499909 commit 2e9bf2c
Show file tree
Hide file tree
Showing 10 changed files with 93 additions and 33 deletions.
23 changes: 23 additions & 0 deletions feed-crawler/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions feed-crawler/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
"mysql2": "^3.11.3",
"node-cron": "^3.0.3",
"node-html-parser": "^6.1.13",
"reflect-metadata": "^0.2.2",
"tsyringe": "^4.8.0",
"winston": "^3.16.0"
},
"devDependencies": {
Expand Down
5 changes: 2 additions & 3 deletions feed-crawler/src/common/mysql-access.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import * as mysql from "mysql2/promise";
import { CONNECTION_LIMIT } from "./constant";
import { PoolConnection } from "mysql2/promise";
import { DatabaseConnection } from "../types/database-connection";
import logger from "./logger";
import * as dotenv from "dotenv";

dotenv.config({
path: process.env.NODE_ENV === "production" ? "feed-crawler/.env" : ".env",
});

class MySQLConnection {
export class MySQLConnection implements DatabaseConnection {
private pool: mysql.Pool;
private nameTag: string;
constructor() {
Expand Down Expand Up @@ -57,5 +58,3 @@ class MySQLConnection {
await this.pool.end();
}
}

export const mysqlConnection = new MySQLConnection();
13 changes: 13 additions & 0 deletions feed-crawler/src/container.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { container } from "tsyringe";
import { DatabaseConnection } from "./types/database-connection";
import { DEPENDENCY_SYMBOLS } from "./types/dependency-symbols";
import {MySQLConnection} from "./common/mysql-access";
import {RssRepository} from "./repository/rss.repository";
import {FeedRepository} from "./repository/feed.repository";

container.registerSingleton<DatabaseConnection>(DEPENDENCY_SYMBOLS.DatabaseConnection, MySQLConnection);

container.registerSingleton<RssRepository>(DEPENDENCY_SYMBOLS.RssRepository, RssRepository);
container.registerSingleton<FeedRepository>(DEPENDENCY_SYMBOLS.FeedRepository, FeedRepository);

export { container };
25 changes: 10 additions & 15 deletions feed-crawler/src/feed-crawler.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
import { feedRepository } from "./repository/feed.repository";
import { rssRepository } from "./repository/rss.repository";
import { FeedRepository } from "./repository/feed.repository";
import { RssRepository } from "./repository/rss.repository";
import logger from "./common/logger";
import { RssObj, FeedDetail, RawFeed } from "./common/types";
import { XMLParser } from "fast-xml-parser";
import { parse } from "node-html-parser";
import { unescape } from "html-escaper";
import { ONE_MINUTE } from "./common/constant";

class FeedCrawler {
private rssParser: RssParser;

constructor() {
this.rssParser = new RssParser();
}
export class FeedCrawler {
private rssParser: RssParser = new RssParser();
constructor(private readonly rssRepository: RssRepository, private readonly feedRepository: FeedRepository) {};

async start() {
await feedRepository.deleteRecentFeed();
await this.feedRepository.deleteRecentFeed();

const rssObjects = await rssRepository.selectAllRss();
const rssObjects = await this.rssRepository.selectAllRss();

if (!rssObjects || !rssObjects.length) {
logger.info("등록된 RSS가 없습니다.");
Expand All @@ -33,9 +30,9 @@ class FeedCrawler {
}
logger.info(`총 ${newFeeds.length}개의 새로운 피드가 있습니다.`);

const insertedData = await feedRepository.insertFeeds(newFeeds);
const insertedData = await this.feedRepository.insertFeeds(newFeeds);

await feedRepository.setRecentFeedList(insertedData);
await this.feedRepository.setRecentFeedList(insertedData);
}

private async findNewFeeds(
Expand Down Expand Up @@ -174,6 +171,4 @@ class RssParser {

return unescape(feedTitle);
}
}

export const feedCrawler = new FeedCrawler();
}
23 changes: 18 additions & 5 deletions feed-crawler/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,29 @@
import "reflect-metadata";
import logger from "./common/logger.js";
import { feedCrawler } from "./feed-crawler.js";
import { mysqlConnection } from "./common/mysql-access.js";
import { FeedCrawler } from "./feed-crawler.js";
import { container } from "./container";
import {RssRepository} from "./repository/rss.repository";
import {FeedRepository} from "./repository/feed.repository";
import {DEPENDENCY_SYMBOLS} from "./types/dependency-symbols";
import {DatabaseConnection} from "./types/database-connection";

async function runCrawler() {
async function main() {
logger.info("==========작업 시작==========");
const startTime = Date.now();

const rssRepository = container.resolve<RssRepository>(DEPENDENCY_SYMBOLS.RssRepository);
const feedRepository = container.resolve<FeedRepository>(DEPENDENCY_SYMBOLS.FeedRepository);
const dbConnection = container.resolve<DatabaseConnection>(DEPENDENCY_SYMBOLS.DatabaseConnection);

const feedCrawler = new FeedCrawler(rssRepository, feedRepository);
await feedCrawler.start();

const endTime = Date.now();
const executionTime = endTime - startTime;
await mysqlConnection.end();

await dbConnection.end();
logger.info(`실행 시간: ${executionTime / 1000}seconds`);
logger.info("==========작업 완료==========");
}

runCrawler();
main();
13 changes: 8 additions & 5 deletions feed-crawler/src/repository/feed.repository.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import { FeedDetail } from "../common/types";
import logger from "../common/logger";
import { mysqlConnection } from "../common/mysql-access";
import { redisConstant } from "../common/constant";
import { redisConnection } from "../common/redis-access";
import {inject, injectable} from "tsyringe";
import {DEPENDENCY_SYMBOLS} from "../types/dependency-symbols";
import {DatabaseConnection} from "../types/database-connection";

@injectable()
export class FeedRepository {
constructor(@inject(DEPENDENCY_SYMBOLS.DatabaseConnection) private readonly dbConnection: DatabaseConnection) {};

class FeedRepository {
public async insertFeeds(resultData: FeedDetail[]) {
const query = `
INSERT INTO feed (blog_id, created_at, title, path, thumbnail)
VALUES (?, ?, ?, ?, ?)
`;

const insertPromises = resultData.map(async (feed) => {
return mysqlConnection.executeQuery(query, [
return this.dbConnection.executeQuery(query, [
feed.blogId,
feed.pubDate,
feed.title,
Expand Down Expand Up @@ -102,5 +107,3 @@ class FeedRepository {
logger.info(`[Redis] 최근 게시글 캐시가 정상적으로 저장되었습니다.`);
}
}

export const feedRepository = new FeedRepository();
13 changes: 8 additions & 5 deletions feed-crawler/src/repository/rss.repository.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { mysqlConnection } from "../common/mysql-access";
import { RssObj } from "../common/types";
import { DatabaseConnection } from "../types/database-connection";
import { DEPENDENCY_SYMBOLS } from "../types/dependency-symbols";
import { inject, injectable } from "tsyringe";

@injectable()
export class RssRepository {
constructor(@inject(DEPENDENCY_SYMBOLS.DatabaseConnection) private readonly dbConnection: DatabaseConnection) {};

class RssRepository {
public async selectAllRss(): Promise<RssObj[]> {
const query = `SELECT id, rss_url as rssUrl, name as blogName, blog_platform as blogPlatform
FROM rss_accept`;
return mysqlConnection.executeQuery(query);
return this.dbConnection.executeQuery(query, []);
}
}

export const rssRepository = new RssRepository();
4 changes: 4 additions & 0 deletions feed-crawler/src/types/database-connection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface DatabaseConnection {
executeQuery<T>(query: string, params: any[]): Promise<T[]>;
end(): Promise<void>;
}
5 changes: 5 additions & 0 deletions feed-crawler/src/types/dependency-symbols.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const DEPENDENCY_SYMBOLS = {
DatabaseConnection: Symbol.for("DatabaseConnection"),
RssRepository: Symbol.for("RssRepository"),
FeedRepository: Symbol.for("FeedRepository"),
}

0 comments on commit 2e9bf2c

Please sign in to comment.