DOCS/journey/troubleshooting/251016_neo4j_startup_data_migration.md
Claude-51124 22557e7132 docs: 오래된 트러블슈팅 아카이브 및 구조 정리
- 7-8월 초기 구축 문서 12개를 _archive/troubleshooting/2025_07-08_initial_setup/로 이동
- book/300_architecture/390_human_in_the_loop_intent_learning.md를 journey/research/intent_classification/로 이동 (개발 여정 문서)
- 빈 폴더 제거 (journey/assets/*)
2025-11-17 14:06:05 +09:00

9.5 KiB

Neo4j 스타트업 데이터 마이그레이션

날짜: 2025-10-16 작성자: Claude (51123 서버 관리자) 작업 시간: 21:35 - 23:00 KST 분류: 데이터 마이그레이션


작업 개요

스타트업 생태계 분석을 위해 다양한 데이터를 Neo4j 그래프 데이터베이스로 통합 마이그레이션

데이터 소스:

  • YC 기업: 5,490개 (yc_companies_all.json)
  • 한국 스타트업: 12,703개 (startup_data_20251016.json)
  • VC firms: 60개 (vc_firms_connor.csv)
  • 뉴스 기사: 7,479개 (Platum, Outstanding 중복 제거 후)

최종 결과:

  • 총 노드: 26,117개
  • 총 관계: 27,227개
  • DB 크기: 532MB (HDD 저장)

데이터 구조 설계

노드 타입

  1. Startup (18,193개)

    • YC 기업: 5,490개
    • 한국 스타트업: 12,703개
    • 속성: id, name, description, website, revenue, source 등
  2. VC (60개)

    • 속성: id, name, website, city, state, description
  3. Batch (45개)

    • YC 배치 정보 (W24, S24 등)
  4. Category (9개)

    • 산업 분류 (industry type)
  5. Tag (331개)

    • 기술 스택, 비즈니스 모델 등
  6. NewsArticle (7,479개)

    • 출처: Platum, Outstanding
    • 속성: url, title, source, date

관계 타입

  1. BATCH (5,490개)

    • Startup → Batch
  2. BELONGS_TO (5,490개)

    • Startup → Category
  3. TAGGED (14,714개)

    • Startup → Tag
  4. MENTIONS (1,533개)

    • NewsArticle → Startup
    • 뉴스 제목에 기업명 포함 시 생성

기술 스택

Neo4j 연결

from neo4j import GraphDatabase
NEO4J_URI = "bolt://localhost:7687"
NEO4J_USER = "neo4j"
NEO4J_PASSWORD = "robeing2025!"

제약 조건

CREATE CONSTRAINT startup_id IF NOT EXISTS FOR (s:Startup) REQUIRE s.id IS UNIQUE
CREATE CONSTRAINT vc_id IF NOT EXISTS FOR (v:VC) REQUIRE v.id IS UNIQUE
CREATE CONSTRAINT batch_name IF NOT EXISTS FOR (b:Batch) REQUIRE b.name IS UNIQUE
CREATE CONSTRAINT category_name IF NOT EXISTS FOR (c:Category) REQUIRE c.name IS UNIQUE
CREATE CONSTRAINT news_url IF NOT EXISTS FOR (n:NewsArticle) REQUIRE n.url IS UNIQUE

데이터 로딩 과정

Phase 1: YC 기업 (5,490개)

파일: /mnt/hdd/data/DATA/startup/yc_companies_all.json

로직:

  1. Startup 노드 생성 (MERGE)
  2. Batch 노드 생성 및 관계 연결
  3. Category 노드 생성 및 관계 연결
  4. Tag 노드 생성 및 관계 연결 (tags 배열)

배치 처리: 1,000개 단위로 진행 표시


Phase 2: 한국 스타트업 (12,703개)

파일: /mnt/hdd/data/DATA/startup/data/startup_data_20251016.json

초기 시도: startup_data.json (2,870개) 사용 업그레이드: startup_data_20251016.json으로 변경 (최신 데이터)

속성:

  • corpId (고유 ID)
  • corpNameKr, corpNameEn (한글/영문 이름)
  • corpIntroKr (기업 소개)
  • corpLogoImg (로고 URL)
  • finacRevenueVal (매출액)

Phase 3: VC Firms (60개)

파일: /mnt/hdd/data/DATA/startup/vc_firms_connor.csv

속성:

  • Employbl Company ID (고유 ID)
  • Company Name
  • Website
  • City, State
  • Company Description

Phase 4: 뉴스 기사 (7,479개)

파일 구조 문제 발견:

// 잘못된 가정: articles가 최상위 배열
[{"url": "...", "title": "..."}]

// 실제 구조: articles가 중첩 객체
{"articles": [{"url": "...", "title": "..."}]}

해결:

# Before
articles = list(data.values()) if isinstance(data, dict) else data

# After
articles = data.get('articles', [])

데이터 소스:

  1. Platum: /mnt/hdd/data/DATA/startup_crawl/platum/articles_*.json
  2. Outstanding: /mnt/hdd/data/DATA/startup_crawl/outstanding/articles_*.json

중복 제거: MERGE (url 기준)으로 33,225건 → 7,479건


Phase 5: 뉴스-스타트업 관계 생성

쿼리:

MATCH (s:Startup), (n:NewsArticle)
WHERE s.name IS NOT NULL
  AND n.title CONTAINS s.name
  AND size(s.name) > 2
MERGE (n)-[:MENTIONS]->(s)

오류 수정:

  • length(s.name)size(s.name) (Neo4j 함수 호환성)

결과: 1,533개 MENTIONS 관계 생성


구현 파일

1. /mnt/hdd/data/DATA/.env

NEO4J_URI=bolt://localhost:7687
NEO4J_USER=neo4j
NEO4J_PASSWORD=robeing2025!

2. /mnt/hdd/data/DATA/load_to_neo4j.py

  • 초기 버전 (YC + 한국 기업 + VC)
  • 한국 스타트업 2,870개 → 12,703개 업그레이드

3. /mnt/hdd/data/DATA/load_all_to_neo4j.py

  • 완전판 (뉴스 기사 포함)
  • 클래스 구조: FullNeo4jLoader
  • 메서드:
    • clear_database(): 초기화
    • create_constraints(): 인덱스 생성
    • load_basic_data(): YC, 한국, VC 로드
    • load_news(): 뉴스 기사 로드
    • link_news_to_startups(): 관계 생성
    • print_stats(): 통계 출력

발견된 오류 및 해결

Error 1: Neo4j 인증 실패

Neo.ClientError.Security.Unauthorized

해결: DOCS에서 비밀번호 검색

grep -r "neo4j" /home/admin/DOCS/
# robeing2025! 발견

Error 2: 뉴스 데이터 0건

원인: JSON 구조 잘못된 가정 (articles가 중첩됨)

해결: data.get('articles', []) 사용


Error 3: Neo4j length() 타입 에러

Type mismatch: expected Path but was Boolean, Float, Integer...

원인: length() 함수는 경로 길이 전용, 문자열은 size() 사용

해결: length(s.name)size(s.name)


Error 4: 스크립트 클래스 구조 깨짐

원인: Shell append로 메서드 추가 시 클래스 밖에 추가됨

해결: 완전한 새 파일 load_all_to_neo4j.py 생성


최종 통계

노드 분포

노드 타입 개수 비율
Startup 18,193 69.6%
NewsArticle 7,479 28.6%
Tag 331 1.3%
VC 60 0.2%
Batch 45 0.2%
Category 9 0.0%
합계 26,117 100%

관계 분포

관계 타입 개수 비율
TAGGED 14,714 54.0%
BATCH 5,490 20.2%
BELONGS_TO 5,490 20.2%
MENTIONS 1,533 5.6%
합계 27,227 100%

활용 예시

1. AI 스타트업 추천

MATCH (s:Startup)-[:TAGGED]->(t:Tag)
WHERE toLower(t.name) CONTAINS 'ai'
  AND s.source = 'YC'
RETURN s.name, s.website, s.description
LIMIT 10

2. 한국 Fintech 기업 (매출 순위)

MATCH (s:Startup)-[:TAGGED]->(t:Tag)
WHERE s.source = 'Korea'
  AND toLower(t.name) CONTAINS 'fintech'
  AND s.revenue > 0
RETURN s.name, s.revenue
ORDER BY s.revenue DESC
LIMIT 20

3. B2B SaaS 기업 발굴

MATCH (s:Startup)-[:TAGGED]->(t:Tag)
WHERE toLower(t.name) IN ['saas', 'b2b', 'enterprise']
RETURN s.name, s.website, s.description, collect(t.name) as tags
LIMIT 15

4. 뉴스 버즈 기업 분석

MATCH (n:NewsArticle)-[:MENTIONS]->(s:Startup)
RETURN s.name, count(n) as mention_count, collect(n.source) as sources
ORDER BY mention_count DESC
LIMIT 10

5. 유사 기업 찾기 (태그 기반)

MATCH (target:Startup {name: '리버스마운틴'})-[:TAGGED]->(t:Tag)<-[:TAGGED]-(similar:Startup)
WHERE target <> similar
RETURN similar.name, similar.description, count(t) as common_tags
ORDER BY common_tags DESC
LIMIT 10

검증 테스트

리버스마운틴 조회

MATCH (s:Startup)
WHERE s.name CONTAINS '리버스마운틴'
RETURN s.name, s.name_en, s.description, s.revenue

결과:

  • 이름: 리버스마운틴
  • 영문명: RI BUS MAUNTIN CO.,LTD.
  • 서비스: 티키타카 (AI 기반 목표/업무 관리)
  • 매출: 0.1억원

향후 계획

1. API 개발

  • Neo4j 그래프 쿼리를 REST API로 제공
  • robeing-gateway 또는 새 마이크로서비스

2. 추가 데이터 연결

  • 12,688개 마크다운 파일 구조화
  • 투자자 정보 추가 (Co-investment 분석)

3. 시계열 분석

  • 2024-11-27 → 2025-10-16 변화 추적
  • 생존율/폐업률 모니터링

4. 고급 그래프 분석

  • PageRank: 영향력 있는 기업 발굴
  • Community Detection: 산업 클러스터 분석
  • Shortest Path: 기업 간 연결 경로

참고 자료

데이터 파일 위치

  • YC: /mnt/hdd/data/DATA/startup/yc_companies_all.json
  • 한국: /mnt/hdd/data/DATA/startup/data/startup_data_20251016.json
  • VC: /mnt/hdd/data/DATA/startup/vc_firms_connor.csv
  • 뉴스: /mnt/hdd/data/DATA/startup_crawl/{platum,outstanding}/*.json

스크립트 위치

  • 초기판: /mnt/hdd/data/DATA/load_to_neo4j.py
  • 완전판: /mnt/hdd/data/DATA/load_all_to_neo4j.py
  • 환경변수: /mnt/hdd/data/DATA/.env

Neo4j 저장소

  • 물리 경로: /mnt/hdd/neo4j/data/ (HDD)
  • 심볼릭 링크: /var/lib/neo4j/data
  • 크기: 532MB

관련 문서

  • 트렌드 분석: /home/admin/DOCS/troubleshooting/251016_startup_trend_analysis.md
  • 인프라 작업: /home/admin/DOCS/troubleshooting/251016_troubleshooting_summary.md

교훈

데이터 구조 검증

실제 JSON 파일을 직접 확인해야 함. 중첩 구조 가능성 항상 고려.

Neo4j 함수 호환성

  • 문자열 길이: size() 사용
  • 경로 길이: length() 사용
  • 문서 참조 필수

배치 처리 필요성

18,193개 스타트업 로드 시 1,000개 단위로 진행 표시 → 사용자 불안감 해소

데이터 품질

  • 한국 데이터: 투자 단계 57.8% 미공개
  • 뉴스 데이터: URL 중복 제거로 33,225 → 7,479
  • 관계 생성: 이름 매칭 단순 방식 (2글자 이하 제외)

그래프 DB 장점

  • 유사 기업 찾기: JOIN 없이 패턴 매칭
  • 뉴스 버즈 분석: 관계 카운팅 간단
  • 투자 네트워크: 미래 확장 용이