- URL 하드코딩 → .env APP_URL 기반 동적 URL로 변경 - DB 연결 하드코딩 → .env 기반으로 변경 - MySQL strict mode DATE 오류 수정
474 lines
30 KiB
HTML
474 lines
30 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="ko" class="scroll-smooth">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Git & GitHub: 대화형 성장 분석</title>
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;500;700&display=swap" rel="stylesheet">
|
|
<!-- Chosen Palette: Calm Harmony (Background: #F8F9FA, Text: #343A40, Accents: #4C6EF5, #228BE6, #15AABF) -->
|
|
<!-- Application Structure Plan: A non-linear, thematic SPA with a sticky top navigation (Prologue, Growth, Platform, Culture). The structure is designed as a narrative journey, starting with the origins, moving to quantitative growth, then exploring the current platform, and finishing with culture. This is more engaging than a linear report summary and allows users to jump to sections of interest. Key interactions include a hover-able timeline chart for growth metrics and clickable cards to reveal detailed information about the platform's pillars, encouraging exploration without overwhelming the user. -->
|
|
<!-- Visualization & Content Choices:
|
|
- Git/GitHub Origins: Goal=Inform. Method=HTML/CSS vertical timeline and info cards. Interaction=Click to reveal text. Justification=Visually separates the two intertwined stories and presents history in a digestible, step-by-step format. Library=None (Vanilla JS/CSS).
|
|
- User/Revenue Growth: Goal=Show Change/Compare. Method=Chart.js combined line chart. Interaction=Hover for tooltips with precise data. Justification=Effectively visualizes the explosive growth trends over time in a single, powerful graphic. Library=Chart.js.
|
|
- Platform Pillars (Actions, etc.): Goal=Organize/Inform. Method=HTML/CSS interactive cards. Interaction=Click to expand/reveal modal. Justification=Breaks down the complex, integrated platform into its three core components, allowing users to drill down into the specifics of what interests them. Library=None (Vanilla JS/CSS).
|
|
- CONFIRMATION: NO SVG graphics used. NO Mermaid JS used. -->
|
|
<!-- CONFIRMATION: NO SVG graphics used. NO Mermaid JS used. -->
|
|
<style>
|
|
body {
|
|
font-family: 'Noto Sans KR', sans-serif;
|
|
background-color: #F8F9FA;
|
|
color: #343A40;
|
|
}
|
|
.chart-container {
|
|
position: relative;
|
|
width: 100%;
|
|
max-width: 900px;
|
|
margin-left: auto;
|
|
margin-right: auto;
|
|
height: 300px;
|
|
max-height: 50vh;
|
|
}
|
|
@media (min-width: 768px) {
|
|
.chart-container {
|
|
height: 450px;
|
|
}
|
|
}
|
|
.nav-link {
|
|
transition: color 0.3s, border-bottom-color 0.3s;
|
|
border-bottom: 2px solid transparent;
|
|
}
|
|
.nav-link:hover, .nav-link.active {
|
|
color: #4C6EF5;
|
|
border-bottom-color: #4C6EF5;
|
|
}
|
|
.timeline-item-content {
|
|
transition: max-height 0.5s ease-in-out, opacity 0.5s ease-in-out;
|
|
max-height: 0;
|
|
opacity: 0;
|
|
overflow: hidden;
|
|
}
|
|
.timeline-item.active .timeline-item-content {
|
|
max-height: 500px;
|
|
opacity: 1;
|
|
}
|
|
.platform-card {
|
|
transition: transform 0.3s, box-shadow 0.3s;
|
|
}
|
|
.platform-card:hover {
|
|
transform: translateY(-5px);
|
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
|
}
|
|
</style>
|
|
</head>
|
|
<body class="antialiased">
|
|
|
|
<header id="header" class="sticky top-0 z-50 bg-white/80 backdrop-blur-lg shadow-sm">
|
|
<nav class="container mx-auto px-4 py-3">
|
|
<div class="flex justify-between items-center">
|
|
<a href="#hero" class="text-xl font-bold text-gray-800">Git & GitHub Story</a>
|
|
<div class="hidden md:flex space-x-6">
|
|
<a href="#prologue" class="nav-link font-medium text-gray-600 pb-1">서막</a>
|
|
<a href="#growth" class="nav-link font-medium text-gray-600 pb-1">성장</a>
|
|
<a href="#platform" class="nav-link font-medium text-gray-600 pb-1">플랫폼</a>
|
|
<a href="#culture" class="nav-link font-medium text-gray-600 pb-1">문화</a>
|
|
</div>
|
|
<button id="mobile-menu-button" class="md:hidden text-gray-700">
|
|
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16m-7 6h7"></path></svg>
|
|
</button>
|
|
</div>
|
|
<div id="mobile-menu" class="hidden md:hidden mt-4 space-y-2">
|
|
<a href="#prologue" class="block nav-link font-medium text-gray-600 py-2">서막</a>
|
|
<a href="#growth" class="block nav-link font-medium text-gray-600 py-2">성장</a>
|
|
<a href="#platform" class="block nav-link font-medium text-gray-600 py-2">플랫폼</a>
|
|
<a href="#culture" class="block nav-link font-medium text-gray-600 py-2">문화</a>
|
|
</div>
|
|
</nav>
|
|
</header>
|
|
|
|
<main>
|
|
<section id="hero" class="relative text-center py-24 md:py-32 bg-white">
|
|
<div class="container mx-auto px-4">
|
|
<h1 class="text-4xl md:text-6xl font-extrabold text-gray-900 mb-4 leading-tight">Git과 GitHub: 세상을 바꾼 코드</h1>
|
|
<p class="text-lg md:text-xl text-gray-600 max-w-3xl mx-auto mb-8">분산 버전 관리의 탄생부터 AI 기반 개발 플랫폼으로의 진화까지, 소프트웨어 개발의 역사를 새로 쓴 두 거인의 성장 스토리를 대화형으로 탐험해 보세요.</p>
|
|
<a href="#prologue" class="bg-blue-600 text-white font-bold py-3 px-8 rounded-full hover:bg-blue-700 transition-transform duration-300 transform hover:scale-105">탐험 시작하기</a>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="prologue" class="py-20 bg-gray-50">
|
|
<div class="container mx-auto px-4">
|
|
<div class="text-center mb-16">
|
|
<h2 class="text-3xl md:text-4xl font-bold text-gray-800">서막: 두 혁신의 탄생</h2>
|
|
<p class="mt-4 text-lg text-gray-600 max-w-2xl mx-auto">필요는 발명의 어머니였습니다. 리눅스 커널의 고통이 Git을 낳았고, Git의 잠재력이 GitHub을 탄생시켰습니다.</p>
|
|
</div>
|
|
<div class="grid md:grid-cols-2 gap-12 items-start">
|
|
<div>
|
|
<h3 class="text-2xl font-bold text-center mb-8 text-gray-700">Git: 분산 버전 관리의 서막</h3>
|
|
<div class="relative pl-8 border-l-2 border-blue-200">
|
|
<div class="timeline-item mb-10 cursor-pointer" data-target="git-1">
|
|
<div class="absolute w-6 h-6 bg-white border-4 border-blue-500 rounded-full -left-3.5 mt-1.5"></div>
|
|
<h4 class="text-lg font-semibold text-blue-600">~2002: 원시적 협업의 시대</h4>
|
|
<div id="git-1" class="timeline-item-content">
|
|
<p class="mt-2 text-gray-600">리눅스 커널 개발은 패치 파일을 이메일로 주고받는 원시적인 방식으로 이뤄졌습니다. 변경 추적이 어렵고 통합 과정은 '지옥'과 같았습니다.</p>
|
|
</div>
|
|
</div>
|
|
<div class="timeline-item mb-10 cursor-pointer" data-target="git-2">
|
|
<div class="absolute w-6 h-6 bg-white border-4 border-blue-500 rounded-full -left-3.5 mt-1.5"></div>
|
|
<h4 class="text-lg font-semibold text-blue-600">2002-2005: BitKeeper 논란</h4>
|
|
<div id="git-2" class="timeline-item-content">
|
|
<p class="mt-2 text-gray-600">상용 DVCS인 BitKeeper를 도입했지만, 오픈소스 정신에 위배된다는 논란과 라이선스 회수 사태로 이어지며 새로운 시스템의 필요성을 촉발시켰습니다.</p>
|
|
</div>
|
|
</div>
|
|
<div class="timeline-item mb-10 cursor-pointer active" data-target="git-3">
|
|
<div class="absolute w-6 h-6 bg-white border-4 border-blue-500 rounded-full -left-3.5 mt-1.5"></div>
|
|
<h4 class="text-lg font-semibold text-blue-600">2005.04: Git의 탄생</h4>
|
|
<div id="git-3" class="timeline-item-content">
|
|
<p class="mt-2 text-gray-600">리누스 토르발스가 단 2주 만에 Git 초기 버전을 개발했습니다. '빠른 속도', '완벽한 분산', '강력한 브랜칭'을 핵심 철학으로 삼았습니다.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<h3 class="text-2xl font-bold text-center mb-8 text-gray-700">GitHub: 소셜 코딩의 시작</h3>
|
|
<div class="space-y-6">
|
|
<div class="bg-white p-6 rounded-lg shadow-md border-l-4 border-green-500">
|
|
<h4 class="text-xl font-bold text-gray-800">Pull Request</h4>
|
|
<p class="mt-2 text-gray-600">코드 변경 제안을 '대화형' 프로세스로 전환하여, 딱딱한 기술 절차를 살아있는 사회적 상호작용으로 만들었습니다.</p>
|
|
</div>
|
|
<div class="bg-white p-6 rounded-lg shadow-md border-l-4 border-green-500">
|
|
<h4 class="text-xl font-bold text-gray-800">프로필과 포크</h4>
|
|
<p class="mt-2 text-gray-600">개발자 개개인에게 온라인 정체성을 부여하고, 클릭 한 번으로 다른 프로젝트를 복제하여 기여할 수 있는 문화를 만들었습니다.</p>
|
|
</div>
|
|
<div class="bg-white p-6 rounded-lg shadow-md border-l-4 border-green-500">
|
|
<h4 class="text-xl font-bold text-gray-800">오픈소스 우선 모델</h4>
|
|
<p class="mt-2 text-gray-600">공개 저장소는 무료, 비공개 저장소는 유료로 제공하여 커뮤니티의 지지를 얻고 안정적인 비즈니스 기반을 마련했습니다.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="growth" class="py-20 bg-white">
|
|
<div class="container mx-auto px-4">
|
|
<div class="text-center mb-16">
|
|
<h2 class="text-3xl md:text-4xl font-bold text-gray-800">성장: 숫자와 사건들</h2>
|
|
<p class="mt-4 text-lg text-gray-600 max-w-2xl mx-auto">부트스트랩 스타트업에서 시작하여 Microsoft에 75억 달러에 인수되기까지, GitHub의 경이로운 성장 과정을 숫자로 확인해 보세요.</p>
|
|
</div>
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-8 text-center mb-16">
|
|
<div class="bg-gray-50 p-6 rounded-lg shadow">
|
|
<p class="text-5xl font-extrabold text-blue-600">1억+</p>
|
|
<p class="text-lg font-medium text-gray-700 mt-2">개발자 사용자 (2023)</p>
|
|
</div>
|
|
<div class="bg-gray-50 p-6 rounded-lg shadow">
|
|
<p class="text-5xl font-extrabold text-green-600">$20억</p>
|
|
<p class="text-lg font-medium text-gray-700 mt-2">연간 반복 매출 (2024)</p>
|
|
</div>
|
|
<div class="bg-gray-50 p-6 rounded-lg shadow">
|
|
<p class="text-5xl font-extrabold text-indigo-600">$75억</p>
|
|
<p class="text-lg font-medium text-gray-700 mt-2">Microsoft 인수 금액 (2018)</p>
|
|
</div>
|
|
</div>
|
|
<div class="bg-gray-50 p-4 sm:p-8 rounded-lg shadow-lg">
|
|
<h3 class="text-2xl font-bold text-center mb-6 text-gray-800">사용자 및 매출 성장 추이</h3>
|
|
<div class="chart-container">
|
|
<canvas id="growthChart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="platform" class="py-20 bg-gray-50">
|
|
<div class="container mx-auto px-4">
|
|
<div class="text-center mb-16">
|
|
<h2 class="text-3xl md:text-4xl font-bold text-gray-800">플랫폼: 통합된 개발자 경험</h2>
|
|
<p class="mt-4 text-lg text-gray-600 max-w-2xl mx-auto">단순 코드 저장소를 넘어, 아이디어 구상부터 배포까지 전 과정을 책임지는 통합 개발 플랫폼으로 진화했습니다. 이 '철의 삼각지대'를 만나보세요.</p>
|
|
</div>
|
|
<div class="grid md:grid-cols-3 gap-8">
|
|
<div class="platform-card bg-white p-8 rounded-lg shadow-lg text-center cursor-pointer" data-modal="actions-modal">
|
|
<div class="text-5xl mb-4 text-indigo-500">⚙️</div>
|
|
<h3 class="text-2xl font-bold mb-2">GitHub Actions</h3>
|
|
<p class="text-gray-600">빌드, 테스트, 배포 파이프라인을 코드와 함께 관리하는 CI/CD 자동화 플랫폼입니다.</p>
|
|
</div>
|
|
<div class="platform-card bg-white p-8 rounded-lg shadow-lg text-center cursor-pointer" data-modal="codespaces-modal">
|
|
<div class="text-5xl mb-4 text-green-500">💻</div>
|
|
<h3 class="text-2xl font-bold mb-2">GitHub Codespaces</h3>
|
|
<p class="text-gray-600">클릭 한 번으로 즉시 사용 가능한, 완벽하게 구성된 클라우드 개발 환경입니다.</p>
|
|
</div>
|
|
<div class="platform-card bg-white p-8 rounded-lg shadow-lg text-center cursor-pointer" data-modal="copilot-modal">
|
|
<div class="text-5xl mb-4 text-red-500">🤖</div>
|
|
<h3 class="text-2xl font-bold mb-2">GitHub Copilot</h3>
|
|
<p class="text-gray-600">코딩 방식을 바꾸는 AI 페어 프로그래머. 코드 제안부터 함수 생성까지 지원합니다.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="culture" class="py-20 bg-white">
|
|
<div class="container mx-auto px-4">
|
|
<div class="text-center mb-16">
|
|
<h2 class="text-3xl md:text-4xl font-bold text-gray-800">문화: 원격 근무와 개발자 중심주의</h2>
|
|
<p class="mt-4 text-lg text-gray-600 max-w-2xl mx-auto">GitHub의 성공은 기술뿐 아니라 독특한 기업 문화에 있습니다. 개발자를 최우선으로 생각하고 원격 근무를 선도한 그들의 이야기를 들어보세요.</p>
|
|
</div>
|
|
<div class="grid md:grid-cols-2 gap-12 items-center">
|
|
<div class="bg-gray-50 p-8 rounded-lg shadow-lg">
|
|
<h3 class="text-2xl font-bold mb-4 text-gray-800">개발자 우선(Developer-First) 철학</h3>
|
|
<p class="text-gray-600 mb-4">관리자는 지시자가 아닌 '조력자'입니다. 팀원들이 겪는 장애물을 제거하고 그들의 성장을 돕는 것이 핵심 역할입니다.</p>
|
|
<ul class="space-y-2 text-gray-700">
|
|
<li class="flex items-start"><span class="text-blue-500 mr-2">✔</span><span>직원의 자율성과 역량 극대화 추구</span></li>
|
|
<li class="flex items-start"><span class="text-blue-500 mr-2">✔</span><span>오픈소스 기반의 공유와 협업 가치 내재화</span></li>
|
|
<li class="flex items-start"><span class="text-blue-500 mr-2">✔</span><span>개발자의 만족도가 곧 제품의 혁신으로 연결</span></li>
|
|
</ul>
|
|
</div>
|
|
<div class="bg-gray-50 p-8 rounded-lg shadow-lg">
|
|
<h3 class="text-2xl font-bold mb-4 text-gray-800">원격 근무의 선구자</h3>
|
|
<p class="text-gray-600 mb-4">팬데믹 이전부터 '원격 근무 우선' 문화를 구축했습니다. 2023년에는 모든 사무실을 폐쇄하고 '완전 원격 근무' 체제로 전환하며, 위기를 문화적 강점을 활용한 전략적 기회로 바꾸었습니다.</p>
|
|
<ul class="space-y-2 text-gray-700">
|
|
<li class="flex items-start"><span class="text-green-500 mr-2">✔</span><span>전 세계 우수 인재 유치에 유리</span></li>
|
|
<li class="flex items-start"><span class="text-green-500 mr-2">✔</span><span>부동산 등 고정 비용 절감 효과</span></li>
|
|
<li class="flex items-start"><span class="text-green-500 mr-2">✔</span><span>높은 자율성에 기반한 신뢰 문화 구축</span></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
|
|
<footer class="bg-gray-800 text-white py-8">
|
|
<div class="container mx-auto px-4 text-center">
|
|
<p>Git과 GitHub의 이야기는 계속됩니다.</p>
|
|
<p class="text-sm text-gray-400 mt-2">AI 시대의 개발 플랫폼 리더로서 그들의 미래를 주목해 주세요.</p>
|
|
</div>
|
|
</footer>
|
|
|
|
<!-- Modals -->
|
|
<div id="modal-container" class="fixed inset-0 bg-black bg-opacity-50 z-50 hidden items-center justify-center p-4">
|
|
<div id="actions-modal" class="modal-content hidden bg-white rounded-lg shadow-xl p-8 max-w-2xl w-full">
|
|
<h2 class="text-2xl font-bold mb-4">GitHub Actions ⚙️</h2>
|
|
<p class="mb-4">GitHub Actions는 CI/CD(지속적 통합/지속적 배포)를 자동화하는 플랫폼입니다. 코드 저장소에서 직접 빌드, 테스트, 배포 파이프라인을 설정하고 실행할 수 있어 개발 워크플로를 혁신합니다.</p>
|
|
<ul class="list-disc list-inside space-y-2">
|
|
<li><span class="font-semibold">Workflows:</span> YAML 파일로 자동화 프로세스를 정의합니다.</li>
|
|
<li><span class="font-semibold">Events:</span> `push`, `pull_request` 등 워크플로를 시작시키는 트리거입니다.</li>
|
|
<li><span class="font-semibold">Runners:</span> 작업을 실행하는 가상 머신(Linux, Windows, macOS)입니다.</li>
|
|
<li><span class="font-semibold">Marketplace:</span> 커뮤니티가 만든 재사용 가능한 액션 라이브러리입니다.</li>
|
|
</ul>
|
|
<button class="modal-close mt-6 bg-gray-200 text-gray-800 px-4 py-2 rounded hover:bg-gray-300">닫기</button>
|
|
</div>
|
|
<div id="codespaces-modal" class="modal-content hidden bg-white rounded-lg shadow-xl p-8 max-w-2xl w-full">
|
|
<h2 class="text-2xl font-bold mb-4">GitHub Codespaces 💻</h2>
|
|
<p class="mb-4">GitHub Codespaces는 컨테이너 기술을 기반으로 하는 즉각적인 클라우드 개발 환경입니다. 로컬 머신에 복잡한 설정을 할 필요 없이, 브라우저에서 바로 코딩을 시작할 수 있습니다.</p>
|
|
<ul class="list-disc list-inside space-y-2">
|
|
<li><span class="font-semibold">환경 일관성:</span> 모든 팀원이 동일한 개발 환경을 사용해 "내 컴퓨터에서는 되는데" 문제를 해결합니다.</li>
|
|
<li><span class="font-semibold">기여 장벽 완화:</span> 신규 참여자가 환경 설정 없이 즉시 프로젝트에 기여할 수 있습니다.</li>
|
|
<li><span class="font-semibold">어디서든 개발:</span> 인터넷만 연결되어 있다면 어떤 기기에서든 개발이 가능합니다.</li>
|
|
</ul>
|
|
<button class="modal-close mt-6 bg-gray-200 text-gray-800 px-4 py-2 rounded hover:bg-gray-300">닫기</button>
|
|
</div>
|
|
<div id="copilot-modal" class="modal-content hidden bg-white rounded-lg shadow-xl p-8 max-w-2xl w-full">
|
|
<h2 class="text-2xl font-bold mb-4">GitHub Copilot 🤖</h2>
|
|
<p class="mb-4">GitHub Copilot은 OpenAI와 공동 개발한 AI 페어 프로그래머입니다. 개발자의 코딩 스타일과 문맥을 이해하여 실시간으로 코드를 제안하고 생산성을 극대화합니다.</p>
|
|
<ul class="list-disc list-inside space-y-2">
|
|
<li><span class="font-semibold">코드 자동 완성:</span> 단일 라인부터 함수 전체까지 지능적으로 코드를 제안합니다.</li>
|
|
<li><span class="font-semibold">자연어-코드 변환:</span> 주석으로 원하는 기능을 설명하면 코드로 변환해 줍니다.</li>
|
|
<li><span class="font-semibold">반복 작업 가속화:</span> 상용구(boilerplate) 코드를 신속하게 작성하여 시간을 절약합니다.</li>
|
|
</ul>
|
|
<button class="modal-close mt-6 bg-gray-200 text-gray-800 px-4 py-2 rounded hover:bg-gray-300">닫기</button>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const growthData = {
|
|
labels: ['2012', '2015', '2016', '2018', '2022', '2023', '2024'],
|
|
users: [null, null, null, 28, 90, 100, null],
|
|
revenue: [null, null, 1.4, 2, 10, null, 20],
|
|
events: [
|
|
{ year: '2012', label: 'Series A: $1억' },
|
|
{ year: '2015', label: 'Series B: $2.5억' },
|
|
{ year: '2018', label: 'Microsoft 인수: $75억' },
|
|
{ year: '2022', label: 'ARR $10억 달성' },
|
|
{ year: '2023', label: '사용자 1억명 돌파' },
|
|
{ year: '2024', label: 'ARR $20억 달성' },
|
|
]
|
|
};
|
|
|
|
const ctx = document.getElementById('growthChart').getContext('2d');
|
|
new Chart(ctx, {
|
|
type: 'line',
|
|
data: {
|
|
labels: growthData.labels,
|
|
datasets: [
|
|
{
|
|
label: '개발자 사용자 (백만 명)',
|
|
data: growthData.users,
|
|
borderColor: '#4C6EF5',
|
|
backgroundColor: 'rgba(76, 110, 245, 0.1)',
|
|
yAxisID: 'yUsers',
|
|
tension: 0.3,
|
|
fill: true,
|
|
},
|
|
{
|
|
label: '연간 반복 매출 (억 달러)',
|
|
data: growthData.revenue,
|
|
borderColor: '#228BE6',
|
|
backgroundColor: 'rgba(34, 139, 230, 0.1)',
|
|
yAxisID: 'yRevenue',
|
|
tension: 0.3,
|
|
fill: true,
|
|
}
|
|
]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
interaction: {
|
|
mode: 'index',
|
|
intersect: false,
|
|
},
|
|
scales: {
|
|
yUsers: {
|
|
type: 'linear',
|
|
display: true,
|
|
position: 'left',
|
|
title: {
|
|
display: true,
|
|
text: '사용자 (백만 명)',
|
|
color: '#4C6EF5'
|
|
}
|
|
},
|
|
yRevenue: {
|
|
type: 'linear',
|
|
display: true,
|
|
position: 'right',
|
|
title: {
|
|
display: true,
|
|
text: '매출 (억 달러)',
|
|
color: '#228BE6'
|
|
},
|
|
grid: {
|
|
drawOnChartArea: false,
|
|
},
|
|
}
|
|
},
|
|
plugins: {
|
|
tooltip: {
|
|
callbacks: {
|
|
label: function(context) {
|
|
let label = context.dataset.label || '';
|
|
if (label) {
|
|
label += ': ';
|
|
}
|
|
if (context.parsed.y !== null) {
|
|
label += context.parsed.y;
|
|
if (context.dataset.yAxisID === 'yUsers') {
|
|
label += '백만 명';
|
|
} else {
|
|
label += '억 달러';
|
|
}
|
|
}
|
|
return label;
|
|
}
|
|
}
|
|
},
|
|
annotation: {
|
|
annotations: growthData.events.map(event => ({
|
|
type: 'point',
|
|
xValue: event.year,
|
|
yValue: growthData.users[growthData.labels.indexOf(event.year)] ?? growthData.revenue[growthData.labels.indexOf(event.year)],
|
|
backgroundColor: 'rgba(255, 99, 132, 0.5)',
|
|
radius: 5,
|
|
pointStyle: 'rectRot',
|
|
callout: {
|
|
enabled: true,
|
|
content: event.label,
|
|
position: 'auto',
|
|
},
|
|
}))
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// Mobile menu toggle
|
|
const mobileMenuButton = document.getElementById('mobile-menu-button');
|
|
const mobileMenu = document.getElementById('mobile-menu');
|
|
mobileMenuButton.addEventListener('click', () => {
|
|
mobileMenu.classList.toggle('hidden');
|
|
});
|
|
|
|
// Close mobile menu on link click
|
|
document.querySelectorAll('#mobile-menu a').forEach(link => {
|
|
link.addEventListener('click', () => {
|
|
mobileMenu.classList.add('hidden');
|
|
});
|
|
});
|
|
|
|
// Active nav link on scroll
|
|
const sections = document.querySelectorAll('section');
|
|
const navLinks = document.querySelectorAll('.nav-link');
|
|
window.addEventListener('scroll', () => {
|
|
let current = '';
|
|
sections.forEach(section => {
|
|
const sectionTop = section.offsetTop;
|
|
if (pageYOffset >= sectionTop - 80) {
|
|
current = section.getAttribute('id');
|
|
}
|
|
});
|
|
|
|
navLinks.forEach(link => {
|
|
link.classList.remove('active');
|
|
if (link.getAttribute('href').includes(current)) {
|
|
link.classList.add('active');
|
|
}
|
|
});
|
|
});
|
|
|
|
// Timeline interaction
|
|
const timelineItems = document.querySelectorAll('.timeline-item');
|
|
timelineItems.forEach(item => {
|
|
item.addEventListener('click', () => {
|
|
timelineItems.forEach(i => i.classList.remove('active'));
|
|
item.classList.add('active');
|
|
});
|
|
});
|
|
|
|
// Modal interaction
|
|
const modalContainer = document.getElementById('modal-container');
|
|
const platformCards = document.querySelectorAll('.platform-card');
|
|
const closeButtons = document.querySelectorAll('.modal-close');
|
|
|
|
platformCards.forEach(card => {
|
|
card.addEventListener('click', () => {
|
|
const modalId = card.dataset.modal;
|
|
const modal = document.getElementById(modalId);
|
|
modalContainer.style.display = 'flex';
|
|
modal.classList.remove('hidden');
|
|
});
|
|
});
|
|
|
|
const closeModal = () => {
|
|
modalContainer.style.display = 'none';
|
|
document.querySelectorAll('.modal-content').forEach(modal => {
|
|
modal.classList.add('hidden');
|
|
});
|
|
};
|
|
|
|
closeButtons.forEach(button => {
|
|
button.addEventListener('click', closeModal);
|
|
});
|
|
|
|
modalContainer.addEventListener('click', (e) => {
|
|
if(e.target === modalContainer) {
|
|
closeModal();
|
|
}
|
|
});
|
|
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|