Skip to content

사이드 프로젝트 이력서 시각화 서비스 제작기 6편

데이터의 시각화: 내 커리어를 한눈에 보는 인생 그래프

지난 5편까지 텍스트를 분석하고 연도별 성취도 점수를 계산하는 로직을 완성했습니다. 오늘은 이 숫자 데이터들을 활용해 실제 시각적인 인생 그래프 이미지를 생성해 보겠습니다. 아직은 하드코딩된 텍스트 리스트를 사용하지만, 모든 모듈을 하나로 엮어 파이프라인을 완성하는 기념비적인 단계입니다.

1. 에이전트에게 시각화 로직 요청하기

단순한 그래프를 넘어 이력서 서비스에 어울리는 깔끔한 디자인을 위해 Antigravity 에이전트에게 구체적인 스타일 가이드를 포함해 질문했습니다.

1-1. Agent Prompt

내가 utils.py에 만든 성취도 계산 함수 결과값이 아래와 같은 리스트 형태로 나와.
데이터 예시: [{'year': 2021, 'score': 90}, {'year': 2022, 'score': 90}, {'year': 2023, 'score': 100}]

이 데이터를 받아서 꺾은선 그래프(Line Chart)를 그려주는 함수를 @files:utils.py에 추가해줘.

- 라이브러리: Matplotlib 사용
- 디자인: 배경은 밝은 회색(#f8f9fa), 선은 푸른색, 각 점에 점수 라벨 표시
- 환경: 윈도우 환경에서 한글이 깨지지 않도록 '맑은 고딕' 폰트를 설정해줘.
- 출력: life_graph.png 파일로 저장해야 해.

2. 변별력을 위한 스코어링 로직 고도화

그래프의 '역동성'을 살리기 위해 Antigravity와 상의하여 utils.py의 점수 산출 방식을 더 세밀하게 조정했습니다.

  • 직급(Role) 가중치: 인턴(-15점 감점)부터 리드/아키텍트(+25점 가점)까지 직급별 격차를 둠
  • 낮은 기본 점수: 기본 점수를 35점으로 설정하여 성과가 쌓였을 때의 상승 곡선을 극대화
  • 수치 성과 보상: 25% 향상 같은 구체적 수치 발견 시 즉시 +15점 부여

3. 통합 테스트 실행 (test_graph.py)

이제 parser 단계의 Mock 데이터부터 analyzer의 AI 요약, 그리고 utils의 시각화까지 한 번에 실행하는 통합 테스트 코드를 작성했습니다.

3-1. Agent Prompt

test_graph.py는 그래프를 그려야 하는 테스트이면서 analyzer, util, parser를 모두 가져다가 사용하는 이어지는 테스트함수여야 해
예제는 mock_resume_data = [ "김철수 | 백엔드 개발자", "2021.03 - 2022.12: (주)망고소프트 인턴 및 주임", "파이썬 기반 레거시 시스템 리팩토링 및 API 성능 개선", "Redis 캐싱 도입으로 데이터 조회 속도 25% 향상", "2023.01 - 2025.02: 파인애플 테크놀로지 대리", "MSA 기반 결제 서비스 마이크로서비스 설계 및 구축", "Kafka를 활용한 실시간 결제 로그 수집 파이프라인 개발", "2025.03 - 현재: 스타트업 '오렌지랩' 백엔드 리드", "클라우드 아키텍처 최적화 및 주니어 개발자 멘토링" ]
이걸 사용하면 돼
import os
from parser import extract_text
from analyzer import ResumeAnalyzer
from utils import calculate_achievement_scores, draw_life_graph

def run_test():
    print("=== Antigravity LifeGraph 통합 테스트 시작 ===")

    # 1. Parser 단계 (예제 데이터를 사용하거나 실제 파일이 있으면 파싱)
    mock_resume_data = [
        "김철수 | 백엔드 개발자",
        "2021.03 - 2022.12: (주)망고소프트 인턴 및 주임",
        "파이썬 기반 레거시 시스템 리팩토링 및 API 성능 개선",
        "Redis 캐싱 도입으로 데이터 조회 속도 25% 향상",
        "2023.01 - 2025.02: 파인애플 테크놀로지 대리",
        "MSA 기반 결제 서비스 마이크로서비스 설계 및 구축",
        "Kafka를 활용한 실시간 결제 로그 수집 파이프라인 개발",
        "2025.03 - 현재: 스타트업 '오렌지랩' 백엔드 리드",
        "클라우드 아키텍처 최적화 및 주니어 개발자 멘토링"
    ]

    # 실제 파일 테스트를 원하는 경우 아래 주석을 풀고 경로를 지정하세요.
    # file_path = "path/to/resume.docx"
    # if os.path.exists(file_path):
    #     parsed_text = extract_text(file_path)
    # else:
    #     parsed_text = mock_resume_data

    parsed_text = mock_resume_data
    print(f"[1/4] Parser: {len(parsed_text)}개의 문장을 준비했습니다.")

    # 2. Analyzer 단계 (Hugging Face 모델을 통한 성과 요약)
    print("[2/4] Analyzer: AI 모델을 로드하고 성과를 요약합니다. (약 1분 내외 소요...)")
    try:
        analyzer = ResumeAnalyzer()
        summary_text = analyzer.extract_achievements(parsed_text)
        print("\n" + "="*50)
        print("AI 성과 요약 원문 (분석 기초 데이터):")
        print("-" * 50)
        print(summary_text)
        print("="*50 + "\n")
    except Exception as e:
        print(f"Analyzer 단계에서 오류 발생: {e}")
        return

    # 3. Utils - 점수 계산 단계
    print("\n[3/4] Utils: 연도별 성취도 점수를 계산합니다.")
    result_scores = calculate_achievement_scores(summary_text)
    print(f"계산된 데이터: {result_scores}")

    # 4. Utils - 그래프 그리기 단계
    print("[4/4] Utils: 꺾은선 그래프(life_graph.png)를 생성합니다.")
    try:
        draw_life_graph(result_scores)
        if os.path.exists('life_graph.png'):
            print("\n✅ 테스트 완료: life_graph.png 파일이 성공적으로 생성되었습니다.")
        else:
            print("\n❌ 테스트 실패: 그래프 파일이 생성되지 않았습니다.")
    except Exception as e:
        print(f"그래프 생성 중 오류 발생: {e}")

if __name__ == "__main__":
    run_test()

4. 첫 번째 실행 결과와 뜻밖의 발견

드디어 python test_graph.py를 실행하여 얻은 결과물입니다.

첫 번째 실행 결과 이미지

분석 및 회고

분명 로직상으로는 연도별로 점수가 달라져야 하는데, 결과 이미지를 보니 모든 연도가 100점 일직선으로 나왔습니다.

  • 원인 추정: AI 모델이 요약한 텍스트에 모든 연도에 걸쳐 '설계', '구축', '최적화' 같은 고득점 키워드가 중복 포함되어, 모든 연도에 최고점이 매핑된 것으로 보입니다.
  • 교훈: 정교한 알고리즘을 짰더라도, AI가 뱉어주는 '기초 데이터(Raw Data)'의 품질과 정제 방식이 결과에 얼마나 큰 영향을 미치는지 뼈저리게 느꼈습니다.

다음 단계 예고

시각화 파이프라인 자체는 완벽하게 작동하여 life_graph.png 파일을 뽑아내는 데 성공했습니다. 하지만 우리가 원한 것은 '직선'이 아니라 '인생의 굴곡'입니다. 다음 7편에서는 이 '모든 연도 100점' 현상을 해결하기 위해 데이터 파싱 로직을 보정하고, 드디어 하드코딩을 벗어나 실제 파일(.docx) 내용을 읽어오는 실전 파싱을 다뤄보겠습니다.