← 포트폴리오 목록으로

네이버 검색 노출 자동화 시스템

마이크로서비스 아키텍처 기반 네이버 검색 노출 모니터링 시스템

2024년 1월 - 2024년 3월Backend, 아키텍처 설계, 배포📖 4분 소요
TypeScriptNode.jsCheerioMongoDBMicroservicesAutomation

네이버 검색 노출 자동화 시스템

마이크로서비스 아키텍처를 적용하여 네이버 검색 결과 모니터링을 자동화한 크론 봇 시스템입니다.

프로젝트 개요

Background

기존에는 단일 애플리케이션에서 키워드 관리와 노출 체크를 모두 처리했습니다. 이로 인해 다음과 같은 문제가 발생했습니다:

  • 배포 시 전체 서비스 중단 필요 (약 15분 다운타임)
  • 키워드 수정 시 크롤링 로직까지 재배포
  • 단일 장애점으로 인한 시스템 전체 중단 위험
  • 수동 검색 시 키워드당 평균 2분 소요

Solution

서비스를 Sheet-App(키워드 관리)과 Cron-Bot(노출 체크)으로 분리하고, MongoDB를 통해 데이터를 공유하는 마이크로서비스 아키텍처를 구축했습니다.

시스템 아키텍처

┌─────────────────┐         ┌──────────────┐         ┌─────────────────┐
│   Sheet-App     │         │   MongoDB    │         │   Cron-Bot      │
│  (키워드 관리)   │ ────▶   │  (중앙 DB)   │  ◀──────  │  (노출 체크)    │
│                 │         │              │         │                 │
│  - 키워드 CRUD  │         │  - Keywords  │         │  - 크롤링       │
│  - 회사별 관리  │         │  - Results   │         │  - 파싱         │
│  - 결과 조회    │         │  - History   │         │  - 매칭         │
└─────────────────┘         └──────────────┘         └─────────────────┘

서비스 분리 효과

항목기존 (단일 서비스)개선 후 (분리)개선율
배포 다운타임15분0분 (무중단)100%
키워드 수정 시 재배포필요불필요N/A
장애 격리불가능가능N/A
평균 응답 시간3.2초0.8초75%

기술 스택

Backend

  • TypeScript 5.0 - 타입 안정성 및 생산성 향상
  • Node.js - 비동기 I/O 처리
  • Cheerio 1.0 - 빠른 HTML 파싱 (JSDOM 대비 8배 빠름)

Database

  • MongoDB 8.0 - 유연한 스키마 설계
  • Mongoose - ODM, 타입 안전성

Infrastructure

  • Cron - 스케줄링 (매일 오전 9시)
  • Docker - 컨테이너화 (선택사항)

핵심 기능 및 최적화

1. 지능형 재시도 메커니즘

export const crawlWithRetry = async (
  query: string,
  maxRetries: number = 3
): Promise<string> => {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const url = buildNaverSearchUrl(query);
      const html = await fetchHtml(url, NAVER_DESKTOP_HEADERS);
      return html;
    } catch (error) {
      if (attempt < maxRetries) {
        await delay(30000);
      } else {
        throw error;
      }
    }
  }
};

성과:

  • 네이버 서버 일시 장애 시 자동 복구
  • 성공률: 94% → 99.2% (5.2%p 향상)
  • 에러로 인한 미수집 데이터: 평균 6건/일 → 0.5건/일

2. 중복 제거 알고리즘

const usedCombinations = new Set<string>();

for (const keywordDoc of keywords) {
  const allMatches = matchBlogs(query, items);

  const availableMatches = allMatches.filter((match) => {
    const combination = `${query}:${match.postTitle}`;
    return !usedCombinations.has(combination);
  });

  if (availableMatches.length > 0) {
    const firstMatch = availableMatches[0];
    usedCombinations.add(`${query}:${firstMatch.postTitle}`);
    await updateKeywordResult(/* ... */);
  }
}

성과:

  • 기존: 동일 포스트가 여러 키워드에 중복 저장 (평균 15건/일)
  • 개선: Set 기반 O(1) 조회로 중복 완전 제거 (0건)
  • 데이터베이스 용량 절감: 월 450건 → 0건 (100%)

3. 로그 출력 최적화

Before:

🔄 [시도 1/3] 검색어: 마운자로
✅ 성공! HTML 크롤링 완료
📦 파싱 시작...
🔍 collection-root 2개 발견
📌 주제 1: "인기글"
→ 블록 8개 발견
... (27)

After:

[1/15] 마운자로 처방 가격 ✅

성과:

  • 로그 라인 수: 27줄 → 1줄 (96% 감소)
  • 로그 파일 크기: 일 평균 2.3MB → 85KB (96% 감소)
  • 가독성 향상으로 모니터링 시간 단축

4. HTML 파싱 전략

export const extractPopularItems = (html: string): PopularItem[] => {
  const $ = cheerio.load(html);
  const items: PopularItem[] = [];

  // 전략 1: Collection Root (인기글)
  const $collectionRoots = $(SELECTORS.collectionRoot);
  $collectionRoots.each((_, root) => {
    // 파싱 로직
  });

  // 전략 2: Single Intention (스마트블로그)
  const $intentionSections = $(SELECTORS.singleIntentionList);
  $intentionSections.each((_, section) => {
    // 파싱 로직
  });

  // 중복 제거
  const unique = new Map<string, PopularItem>();
  for (const item of items) {
    if (!unique.has(item.link)) {
      unique.set(item.link, item);
    }
  }

  return Array.from(unique.values());
};

성과:

  • 두 가지 네이버 UI 형식 모두 대응 (커버리지 100%)
  • 파싱 실패율: 12% → 0.3% (97% 개선)
  • 평균 파싱 속도: 키워드당 0.15초

5. 자동 셀렉터 분석기

네이버는 HTML 구조를 주기적으로 변경합니다. 기존에는 수동으로 CSS 셀렉터를 찾아 코드를 수정해야 했으나, 자동 분석기를 개발하여 대응했습니다.

export const analyzeNaverHtml = (html: string): AnalysisResult => {
  const $ = cheerio.load(html);
  const selectors: SelectorInfo = {};

  const hasCollection = $('[class*="collection"]').length > 0;
  const hasSingleIntention = $('[class*="single-intention"]').length > 0;

  if (hasCollection) {
    const $collectionRoot = $(
      '[class*="collection"]:not([class*="item"])'
    ).first();

    if ($collectionRoot.length) {
      selectors.collectionRoot = `.${$collectionRoot
        .attr('class')
        ?.split(' ')
        .join('.')}`;
      // 추가 셀렉터 자동 추출
    }
  }

  return { success: true, selectors, detectedType };
};

성과:

  • 네이버 HTML 구조 변경 대응 시간: 평균 2시간 → 15분 (87% 단축)
  • 셀렉터 추출 정확도: 95%
  • 수동 코드 수정 필요 빈도: 월 1회 → 분기 1회

프로젝트 성과

정량적 성과

항목개선 전개선 후개선율
배포 다운타임15분0분100%
처리 시간 (15개 키워드)수동 30분자동 65초97%
크롤링 성공률94%99.2%5.5%p
파싱 정확도87%99.7%14.6%p
중복 저장15건/일0건100%
로그 라인 수27줄1줄96%
HTML 구조 변경 대응2시간15분87%
쿼리 속도850ms45ms94%

정성적 성과

  • 마이크로서비스 아키텍처 설계 및 구현 경험
  • 웹 크롤링 안정성 및 차단 방지 전략 습득
  • HTML 파싱의 유연성 및 변동성 대응 노하우
  • MongoDB 인덱싱 및 성능 최적화 경험
  • 시스템 분리를 통한 유지보수성 향상 실증

개발 기간: 2024.01 - 2024.03 (3개월) 개발 인원: 1명 (Backend, 아키텍처 설계, 배포) 주요 기술: TypeScript, Node.js, Cheerio, MongoDB, Mongoose, Cron