@extends('layouts.app')

@section('title', 'PM2 프로세스 관리')

@push('styles')
<style>
    .academy-img-hover {
        transition: box-shadow 0.2s ease;
    }
    .academy-img-hover:hover {
        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    }

    .help-balloon-trigger {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 18px;
        height: 18px;
        border-radius: 50%;
        background: #eff6ff;
        color: #1d4ed8;
        font-size: 11px;
        font-weight: 700;
        cursor: pointer;
        border: 1.5px solid #93c5fd;
        vertical-align: middle;
        margin-left: 4px;
        transition: all 0.15s ease;
        position: relative;
        user-select: none;
    }
    .help-balloon-trigger:hover {
        background: #1d4ed8;
        color: white;
        border-color: #3b82f6;
    }
    .help-balloon {
        display: none;
        position: absolute;
        bottom: calc(100% + 10px);
        left: 50%;
        transform: translateX(-50%) scale(0.95);
        background: #1e293b;
        color: #f1f5f9;
        font-size: 12px;
        font-weight: 400;
        line-height: 1.6;
        padding: 10px 14px;
        border-radius: 10px;
        max-width: 300px;
        min-width: 200px;
        width: max-content;
        white-space: normal;
        word-break: keep-all;
        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
        z-index: 40;
        opacity: 0;
        transition: opacity 0.15s ease, transform 0.15s ease;
        pointer-events: none;
    }
    .help-balloon::after {
        content: '';
        position: absolute;
        top: 100%;
        left: 50%;
        transform: translateX(-50%);
        border: 7px solid transparent;
        border-top-color: #1e293b;
    }
    .help-balloon-trigger.is-active .help-balloon {
        display: block;
        opacity: 1;
        transform: translateX(-50%) scale(1);
        pointer-events: auto;
    }

    .code-block {
        background: #1e1e2e;
        color: #a6e3a1;
        border-radius: 0.75rem;
        padding: 1rem 1.25rem;
        font-family: 'Courier New', Courier, monospace;
        font-size: 0.75rem;
        line-height: 1.7;
        overflow-x: auto;
        white-space: pre;
    }
    .code-block .code-comment { color: #6c7086; }
    .code-block .code-keyword { color: #cba6f7; }
    .code-block .code-string { color: #a6e3a1; }
    .code-block .code-flag { color: #89b4fa; }
    .code-block .code-path { color: #f9e2af; }
    .code-block .code-output { color: #9399b2; }
    .code-block .code-danger { color: #f38ba8; }
    .code-block .code-prompt { color: #89dceb; }

    .cmd-card {
        transition: all 0.15s ease;
    }
    .cmd-card:hover {
        box-shadow: 0 4px 12px rgba(0,0,0,0.08);
    }
</style>
@endpush

@section('content')
<div class="max-w-6xl mx-auto">

    {{-- ============================================================ --}}
    {{-- 히어로 배너 --}}
    {{-- ============================================================ --}}
    <div class="rounded-2xl overflow-hidden mb-8 shadow-lg" style="background: linear-gradient(135deg, #1a2a3a 0%, #0f172a 100%);">
        <div class="flex items-center" style="flex-wrap: wrap;">
            <div style="flex: 1 1 300px; padding: 2rem 2.5rem;">
                <div class="flex items-center gap-2 text-sm mb-2" style="color: #93c5fd;">
                    <span>아카데미</span>
                    <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" /></svg>
                    <span style="color: #bfdbfe;">PM2 프로세스 관리</span>
                </div>
                <h1 class="text-3xl font-bold mb-2" style="color: #ffffff;">PM2 프로세스 관리</h1>
                <p class="text-sm" style="color: #cbd5e1;">Node.js 앱을 24시간 안정적으로 유지하는 프로세스 매니저 — 비개발자를 위한 실전 가이드</p>
            </div>
            <div class="shrink-0" style="width: 240px; padding: 1.5rem;">
                <div class="text-center" style="font-size: 100px; line-height: 1;">
                    <span style="filter: drop-shadow(0 4px 8px rgba(0,0,0,0.3));">&#9881;&#65039;</span>
                </div>
            </div>
        </div>
    </div>

    <div class="flex gap-8">
        {{-- ============================================================ --}}
        {{-- 좌측 고정 목차 (TOC) --}}
        {{-- ============================================================ --}}
        <nav class="hidden lg:block shrink-0" style="width: 220px;">
            <div class="sticky top-24">
                <div class="bg-blue-50 border border-blue-200 rounded-xl p-5">
                    <h2 class="font-semibold text-blue-800 mb-3 flex items-center gap-2 text-sm">
                        <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 10h16M4 14h16M4 18h16" /></svg>
                        목차
                    </h2>
                    <div class="space-y-0.5 text-xs">
                        <a href="#what-is-pm2" class="block text-blue-700 hover:text-blue-900 py-1 font-medium">1. PM2란?</a>
                        <a href="#pm2-analogy" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">비유로 이해하기</a>
                        <a href="#pm2-3-problems" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">3가지 해결 포인트</a>
                        <a href="#pm2-sam-role" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">SAM에서의 역할</a>

                        <a href="#sam-pm2-structure" class="block text-blue-700 hover:text-blue-900 py-1 font-medium mt-2">2. SAM에서 PM2 사용 구조</a>
                        <a href="#structure-diagram" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">구조 다이어그램</a>
                        <a href="#structure-note" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">사용 환경</a>

                        <a href="#pm2-commands" class="block text-blue-700 hover:text-blue-900 py-1 font-medium mt-2">3. 자주 쓰는 PM2 명령어</a>
                        <a href="#cmd-list" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">pm2 list</a>
                        <a href="#cmd-start-restart" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">start / restart</a>
                        <a href="#cmd-stop-delete" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">stop / delete</a>
                        <a href="#cmd-logs-monit" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">logs / monit</a>
                        <a href="#cmd-save-startup" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">save / startup</a>

                        <a href="#sam-deploy" class="block text-blue-700 hover:text-blue-900 py-1 font-medium mt-2">4. SAM 배포와 PM2</a>
                        <a href="#deploy-flow" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">배포 흐름</a>
                        <a href="#deploy-why-local" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">로컬 빌드 이유</a>

                        <a href="#troubleshooting" class="block text-blue-700 hover:text-blue-900 py-1 font-medium mt-2">5. 트러블슈팅</a>
                        <a href="#trouble-not-found" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">앱을 못 찾을 때</a>
                        <a href="#trouble-errored" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">errored 상태</a>
                        <a href="#trouble-reboot" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">재부팅 후 복구</a>

                        <a href="#comparison" class="block text-blue-700 hover:text-blue-900 py-1 font-medium mt-2">6. PM2 vs 다른 방법 비교</a>
                    </div>
                </div>
            </div>
        </nav>

        {{-- ============================================================ --}}
        {{-- 우측 콘텐츠 --}}
        {{-- ============================================================ --}}
        <div class="flex-1 min-w-0 space-y-10">

            {{-- 모바일 목차 --}}
            <div class="lg:hidden bg-blue-50 border border-blue-200 rounded-xl p-4 mb-6">
                <details>
                    <summary class="font-semibold text-blue-800 text-sm cursor-pointer flex items-center gap-2">
                        <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 10h16M4 14h16M4 18h16" /></svg>
                        목차 보기
                    </summary>
                    <nav class="mt-3 space-y-1 text-sm">
                        <a href="#what-is-pm2" class="block text-blue-700 hover:text-blue-900 py-1">1. PM2란? — 왜 PM2를 쓰는가</a>
                        <a href="#sam-pm2-structure" class="block text-blue-700 hover:text-blue-900 py-1">2. SAM에서 PM2 사용 구조</a>
                        <a href="#pm2-commands" class="block text-blue-700 hover:text-blue-900 py-1">3. 자주 쓰는 PM2 명령어</a>
                        <a href="#sam-deploy" class="block text-blue-700 hover:text-blue-900 py-1">4. SAM 배포와 PM2</a>
                        <a href="#troubleshooting" class="block text-blue-700 hover:text-blue-900 py-1">5. 트러블슈팅</a>
                        <a href="#comparison" class="block text-blue-700 hover:text-blue-900 py-1">6. PM2 vs 다른 방법 비교</a>
                    </nav>
                </details>
            </div>

            {{-- ============================================================ --}}
            {{-- 1. PM2란? --}}
            {{-- ============================================================ --}}
            <section id="what-is-pm2" class="scroll-mt-20">
                <div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
                    <h2 class="text-xl font-bold text-gray-800 mb-6 flex items-center gap-3">
                        <span class="w-8 h-8 bg-blue-500 text-white rounded-lg flex items-center justify-center text-sm font-bold">1</span>
                        PM2란? — 왜 PM2를 쓰는가
                    </h2>

                    <div id="pm2-analogy" class="scroll-mt-20 mb-8">
                        <h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
                            <span class="w-2 h-2 bg-blue-400 rounded-full"></span>
                            PM2 = Node.js 프로세스 매니저
                            <span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon"><strong>프로세스(Process)</strong>란 현재 실행 중인 프로그램을 뜻한다. 크롬 브라우저를 열면 크롬 프로세스가 생기는 것처럼, Node.js 앱을 실행하면 Node.js 프로세스가 생긴다.</span></span>
                        </h3>
                        <div class="bg-blue-50 rounded-lg p-4 border border-blue-100 mb-4">
                            <p class="font-semibold text-blue-800 mb-2">비유: "프로그램이 꺼지면 자동으로 다시 켜주는 비서"</p>
                            <p class="text-xs text-blue-900 leading-relaxed">
                                회사에서 중요한 기계가 돌아가고 있다고 상상해보자. 이 기계가 갑자기 멈추면 누군가 와서 다시 켜줘야 한다.
                                PM2는 이 <strong>"누군가"</strong>의 역할을 자동으로 해주는 프로그램이다.
                                기계(Node.js 앱)가 멈추면 자동으로 다시 켜주고, 기계의 상태를 계속 감시해준다.
                            </p>
                        </div>

                        <div class="bg-amber-50 rounded-lg p-4 border border-amber-100 mb-4">
                            <p class="font-semibold text-amber-800 mb-2">PM2 없이 실행하면 어떤 문제가?</p>
                            <p class="text-xs text-amber-900 leading-relaxed mb-2">
                                PM2 없이 <code class="bg-amber-100 px-1.5 py-0.5 rounded text-xs">npm start</code>로 직접 실행하면 다음 문제가 발생한다:
                            </p>
                            <ul class="text-xs text-amber-800 space-y-1">
                                <li>1. <strong>터미널을 닫으면 앱이 꺼진다</strong> — SSH 접속을 끊으면 실행 중이던 앱도 함께 종료된다.</li>
                                <li>2. <strong>에러가 나면 앱이 중단된다</strong> — 예상치 못한 오류가 발생하면 앱이 멈추고, 누군가 수동으로 다시 켜야 한다.</li>
                                <li>3. <strong>상태 확인이 어렵다</strong> — 앱이 잘 돌고 있는지, CPU/메모리를 얼마나 쓰는지 알기 어렵다.</li>
                            </ul>
                        </div>
                    </div>

                    <div id="pm2-3-problems" class="scroll-mt-20 mb-8">
                        <h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
                            <span class="w-2 h-2 bg-blue-400 rounded-full"></span>
                            PM2가 해결하는 3가지 문제
                        </h3>
                        <div class="grid gap-3" style="grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));">
                            <div class="bg-green-50 rounded-lg p-4 border border-green-200">
                                <div class="flex items-center gap-2 mb-2">
                                    <span class="w-6 h-6 bg-green-500 text-white rounded-full flex items-center justify-center text-xs font-bold">1</span>
                                    <p class="font-bold text-green-800 text-sm">자동 재시작</p>
                                </div>
                                <p class="text-xs text-green-700">앱이 에러로 멈추면(crash) PM2가 <strong>자동으로 다시 시작</strong>해준다. 새벽 3시에 에러가 나도 자동 복구된다.</p>
                            </div>
                            <div class="bg-green-50 rounded-lg p-4 border border-green-200">
                                <div class="flex items-center gap-2 mb-2">
                                    <span class="w-6 h-6 bg-green-500 text-white rounded-full flex items-center justify-center text-xs font-bold">2</span>
                                    <p class="font-bold text-green-800 text-sm">백그라운드 실행</p>
                                    <span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon"><strong>백그라운드(Background)</strong>란 화면에 보이지 않지만 뒤에서 계속 실행되는 것을 뜻한다. 스마트폰에서 음악 앱을 끄지 않고 다른 앱을 사용해도 음악이 계속 나오는 것과 같다.</span></span>
                                </div>
                                <p class="text-xs text-green-700">터미널(SSH)을 닫아도 앱이 <strong>꺼지지 않고 계속 실행</strong>된다. 서버에 접속하지 않아도 24시간 유지된다.</p>
                            </div>
                            <div class="bg-green-50 rounded-lg p-4 border border-green-200">
                                <div class="flex items-center gap-2 mb-2">
                                    <span class="w-6 h-6 bg-green-500 text-white rounded-full flex items-center justify-center text-xs font-bold">3</span>
                                    <p class="font-bold text-green-800 text-sm">모니터링</p>
                                </div>
                                <p class="text-xs text-green-700">앱의 <strong>상태, CPU 사용량, 메모리 사용량, 로그</strong>를 한눈에 확인할 수 있다. 문제가 생기면 빠르게 원인을 파악할 수 있다.</p>
                            </div>
                        </div>
                    </div>

                    <div id="pm2-sam-role" class="scroll-mt-20">
                        <h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
                            <span class="w-2 h-2 bg-blue-400 rounded-full"></span>
                            SAM에서의 역할
                        </h3>
                        <div class="bg-indigo-50 rounded-lg p-4 border border-indigo-100">
                            <p class="text-xs text-indigo-900 leading-relaxed">
                                SAM 프로젝트에서 PM2는 <strong>SAM React (Next.js) 프론트엔드</strong>를 운영 서버에서 24시간 유지하는 역할을 한다.
                                사용자가 SAM 웹사이트에 접속하면, PM2가 관리하는 Next.js 앱이 화면을 보여주는 것이다.
                                PM2가 없으면 서버 관리자가 매번 수동으로 앱을 켜고 감시해야 하지만, PM2 덕분에 이 과정이 <strong>완전히 자동화</strong>된다.
                            </p>
                        </div>
                    </div>
                </div>
            </section>

            {{-- ============================================================ --}}
            {{-- 2. SAM에서 PM2 사용 구조 --}}
            {{-- ============================================================ --}}
            <section id="sam-pm2-structure" class="scroll-mt-20">
                <div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
                    <h2 class="text-xl font-bold text-gray-800 mb-6 flex items-center gap-3">
                        <span class="w-8 h-8 bg-blue-500 text-white rounded-lg flex items-center justify-center text-sm font-bold">2</span>
                        SAM에서 PM2 사용 구조
                    </h2>

                    <div id="structure-diagram" class="scroll-mt-20 mb-8">
                        <h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
                            <span class="w-2 h-2 bg-blue-400 rounded-full"></span>
                            구조 다이어그램
                        </h3>
                        <p class="text-xs text-gray-600 mb-3">
                            운영 서버(114.203.209.83)에서 사용자의 요청이 처리되는 과정이다.
                            PM2는 Node.js(Next.js) 앱을 관리하며, Nginx가 사용자의 요청을 PM2가 관리하는 앱으로 전달한다.
                        </p>
                        <div class="code-block mb-4"><span class="code-comment">  사용자 브라우저 (크롬, 사파리 등)</span>
<span class="code-comment">          │</span>
<span class="code-comment">          ▼</span>
<span class="code-keyword">  ┌─────────────────────────────────────┐</span>
<span class="code-keyword">  │</span>  <span class="code-string">Nginx</span> (웹서버, 포트 <span class="code-flag">80</span>/<span class="code-flag">443</span>)         <span class="code-keyword">│</span>
<span class="code-keyword">  │</span>  - 사용자 요청을 받아서 전달하는 역할  <span class="code-keyword">│</span>
<span class="code-keyword">  └────────────────┬────────────────────┘</span>
<span class="code-comment">                   │ 프록시 (요청 전달)</span>
<span class="code-comment">                   ▼</span>
<span class="code-keyword">  ┌─────────────────────────────────────┐</span>
<span class="code-keyword">  │</span>  <span class="code-danger">PM2</span> → <span class="code-string">Node.js</span> (Next.js, 포트 <span class="code-flag">3000</span>)  <span class="code-keyword">│</span>
<span class="code-keyword">  │</span>  - SAM 프론트엔드 화면 생성           <span class="code-keyword">│</span>
<span class="code-keyword">  │</span>  - PM2가 이 앱을 감시/관리            <span class="code-keyword">│</span>
<span class="code-keyword">  └────────────────┬────────────────────┘</span>
<span class="code-comment">                   │ API 호출</span>
<span class="code-comment">                   ▼</span>
<span class="code-keyword">  ┌─────────────────────────────────────┐</span>
<span class="code-keyword">  │</span>  <span class="code-string">API 서버</span> (Laravel, PHP)              <span class="code-keyword">│</span>
<span class="code-keyword">  │</span>  - 데이터 처리 & DB 접근              <span class="code-keyword">│</span>
<span class="code-keyword">  └─────────────────────────────────────┘</span></div>

                        <div class="bg-blue-50 rounded-lg p-3 border border-blue-100 text-xs text-blue-800">
                            <strong>요약:</strong> 사용자가 SAM에 접속하면 Nginx가 요청을 받고,
                            PM2가 관리하는 Next.js 앱이 화면을 생성하고, 필요한 데이터는 API 서버에서 가져온다.
                        </div>
                    </div>

                    <div id="structure-note" class="scroll-mt-20">
                        <h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
                            <span class="w-2 h-2 bg-blue-400 rounded-full"></span>
                            PM2 사용 환경
                        </h3>
                        <div class="grid gap-3" style="grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));">
                            <div class="bg-green-50 rounded-lg p-4 border border-green-200">
                                <p class="font-bold text-green-800 mb-2">운영 서버 (114.203.209.83)</p>
                                <p class="text-xs text-green-700">PM2를 <strong>사용한다</strong>. 서버에 Node.js와 PM2가 직접 설치되어 있으며, <code class="bg-green-100 px-1 rounded">sam-react</code>라는 이름으로 Next.js 앱을 관리한다.</p>
                            </div>
                            <div class="bg-gray-50 rounded-lg p-4 border border-gray-200">
                                <p class="font-bold text-gray-800 mb-2">로컬 Docker 환경 (내 PC)</p>
                                <p class="text-xs text-gray-600">PM2를 <strong>사용하지 않는다</strong>. 로컬에서는 Docker 컨테이너가 React 앱을 실행하므로 PM2가 필요 없다. <code class="bg-gray-100 px-1 rounded">npm run dev</code>로 개발 서버를 실행한다.</p>
                            </div>
                        </div>
                    </div>
                </div>
            </section>

            {{-- ============================================================ --}}
            {{-- 3. 자주 쓰는 PM2 명령어 --}}
            {{-- ============================================================ --}}
            <section id="pm2-commands" class="scroll-mt-20">
                <div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
                    <h2 class="text-xl font-bold text-gray-800 mb-6 flex items-center gap-3">
                        <span class="w-8 h-8 bg-blue-500 text-white rounded-lg flex items-center justify-center text-sm font-bold">3</span>
                        자주 쓰는 PM2 명령어
                    </h2>

                    <div class="bg-amber-50 rounded-lg p-3 border border-amber-100 text-xs text-amber-800 mb-6">
                        <strong>참고:</strong> 아래 명령어들은 모두 <strong>운영 서버</strong>에 SSH로 접속한 후 실행한다. 로컬(내 PC)에서는 사용하지 않는다.
                    </div>

                    {{-- pm2 list --}}
                    <div id="cmd-list" class="scroll-mt-20 mb-6">
                        <h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
                            <span class="w-2 h-2 bg-blue-400 rounded-full"></span>
                            pm2 list — 실행 중인 앱 목록 확인
                        </h3>
                        <div class="border border-gray-200 rounded-lg p-4 cmd-card mb-3">
                            <p class="text-xs text-gray-600 mb-3">현재 PM2가 관리하고 있는 모든 앱의 상태를 표 형태로 보여준다. 앱 이름, 상태(online/errored/stopped), CPU 사용량, 메모리 사용량을 한눈에 확인할 수 있다.</p>
                            <div class="code-block"><span class="code-prompt">$</span> <span class="code-keyword">pm2</span> <span class="code-string">list</span>
<span class="code-output">┌────┬──────────────┬─────────────┬─────────┬──────────┬───────────┐</span>
<span class="code-output">│ id │ name         │ mode        │ status  │ cpu      │ memory    │</span>
<span class="code-output">├────┼──────────────┼─────────────┼─────────┼──────────┼───────────┤</span>
<span class="code-output">│ 0  │ sam-react    │ fork        │ online  │ 0.1%     │ 120.5 MB  │</span>
<span class="code-output">└────┴──────────────┴─────────────┴─────────┴──────────┴───────────┘</span></div>
                        </div>
                        <div class="bg-blue-50 rounded-lg p-3 border border-blue-100 text-xs text-blue-800">
                            <strong>상태 의미:</strong>
                            <code class="bg-blue-100 px-1 rounded">online</code> = 정상 실행 중,
                            <code class="bg-red-100 px-1 rounded text-red-700">errored</code> = 에러 발생,
                            <code class="bg-gray-100 px-1 rounded text-gray-700">stopped</code> = 수동으로 중지됨
                        </div>
                    </div>

                    {{-- pm2 start / restart --}}
                    <div id="cmd-start-restart" class="scroll-mt-20 mb-6">
                        <h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
                            <span class="w-2 h-2 bg-blue-400 rounded-full"></span>
                            pm2 start / pm2 restart — 앱 시작 & 재시작
                        </h3>
                        <div class="border border-gray-200 rounded-lg p-4 cmd-card mb-3">
                            <p class="text-xs text-gray-600 mb-3"><strong>start</strong>는 새로운 앱을 PM2에 등록하고 실행한다. <strong>restart</strong>는 이미 등록된 앱을 껐다가 다시 켠다.</p>
                            <div class="code-block"><span class="code-comment"># 새 앱 시작 (PM2에 처음 등록할 때)</span>
<span class="code-prompt">$</span> <span class="code-keyword">pm2</span> <span class="code-string">start</span> <span class="code-path">npm</span> <span class="code-flag">--name</span> <span class="code-string">sam-react</span> <span class="code-flag">--</span> <span class="code-string">start</span>
<span class="code-comment"># 해석: npm start 명령을 "sam-react"라는 이름으로 PM2에 등록하여 실행</span>

<span class="code-comment"># 이미 등록된 앱 재시작 (배포 후 주로 사용)</span>
<span class="code-prompt">$</span> <span class="code-keyword">pm2</span> <span class="code-string">restart</span> <span class="code-path">sam-react</span>
<span class="code-comment"># 해석: sam-react 앱을 껐다가 다시 켠다 (코드 변경 반영)</span></div>
                        </div>
                        <div class="bg-green-50 rounded-lg p-3 border border-green-100 text-xs text-green-800">
                            <strong>실무 팁:</strong> 배포 시에는 보통 <code class="bg-green-100 px-1 rounded">pm2 restart sam-react</code>만 사용한다. <code>pm2 start</code>는 최초 1회만 실행하면 된다.
                        </div>
                    </div>

                    {{-- pm2 stop / delete --}}
                    <div id="cmd-stop-delete" class="scroll-mt-20 mb-6">
                        <h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
                            <span class="w-2 h-2 bg-blue-400 rounded-full"></span>
                            pm2 stop / pm2 delete — 앱 중지 & 삭제
                        </h3>
                        <div class="border border-gray-200 rounded-lg p-4 cmd-card mb-3">
                            <p class="text-xs text-gray-600 mb-3"><strong>stop</strong>은 앱 실행을 중지하지만 PM2 목록에는 남아있다. <strong>delete</strong>는 PM2 목록에서 완전히 제거한다.</p>
                            <div class="code-block"><span class="code-comment"># 앱 중지 (목록에는 남아있음, 나중에 pm2 start로 다시 실행 가능)</span>
<span class="code-prompt">$</span> <span class="code-keyword">pm2</span> <span class="code-string">stop</span> <span class="code-path">sam-react</span>

<span class="code-comment"># 앱 삭제 (PM2 목록에서 완전히 제거)</span>
<span class="code-prompt">$</span> <span class="code-keyword">pm2</span> <span class="code-string">delete</span> <span class="code-path">sam-react</span>
<span class="code-comment"># 삭제 후 다시 사용하려면 pm2 start로 새로 등록해야 한다</span></div>
                        </div>
                        <div class="bg-red-50 rounded-lg p-3 border border-red-200 text-xs text-red-800">
                            <strong>주의:</strong> <code class="bg-red-100 px-1 rounded">pm2 stop</code>을 실행하면 <strong>SAM 프론트엔드가 접속 불가</strong>해진다. 유지보수 목적 외에는 실행하지 않는다.
                        </div>
                    </div>

                    {{-- pm2 logs / monit --}}
                    <div id="cmd-logs-monit" class="scroll-mt-20 mb-6">
                        <h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
                            <span class="w-2 h-2 bg-blue-400 rounded-full"></span>
                            pm2 logs / pm2 monit — 로그 & 모니터링
                        </h3>
                        <div class="border border-gray-200 rounded-lg p-4 cmd-card mb-3">
                            <p class="text-xs text-gray-600 mb-3"><strong>logs</strong>는 앱의 실시간 출력(로그)을 보여준다. 에러 원인 파악에 필수적이다. <strong>monit</strong>은 CPU, 메모리, 로그를 한 화면에서 실시간으로 볼 수 있는 대시보드이다.</p>
                            <div class="code-block"><span class="code-comment"># 실시간 로그 보기 (Ctrl+C로 종료)</span>
<span class="code-prompt">$</span> <span class="code-keyword">pm2</span> <span class="code-string">logs</span> <span class="code-path">sam-react</span>
<span class="code-output">0|sam-react | ▲ Next.js 14.x</span>
<span class="code-output">0|sam-react | - Local:  http://localhost:3000</span>
<span class="code-output">0|sam-react | ✓ Ready in 2.3s</span>

<span class="code-comment"># 최근 100줄만 보기</span>
<span class="code-prompt">$</span> <span class="code-keyword">pm2</span> <span class="code-string">logs</span> <span class="code-path">sam-react</span> <span class="code-flag">--lines 100</span>

<span class="code-comment"># 실시간 모니터링 대시보드 (Ctrl+C로 종료)</span>
<span class="code-prompt">$</span> <span class="code-keyword">pm2</span> <span class="code-string">monit</span>
<span class="code-comment"># CPU, 메모리, 로그를 한 화면에서 실시간 확인</span></div>
                        </div>
                        <div class="bg-green-50 rounded-lg p-3 border border-green-100 text-xs text-green-800">
                            <strong>실무 팁:</strong> 앱에 문제가 생기면 가장 먼저 <code class="bg-green-100 px-1 rounded">pm2 logs sam-react</code>를 실행하여 에러 메시지를 확인한다.
                        </div>
                    </div>

                    {{-- pm2 save / startup --}}
                    <div id="cmd-save-startup" class="scroll-mt-20">
                        <h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
                            <span class="w-2 h-2 bg-blue-400 rounded-full"></span>
                            pm2 save / pm2 startup — 서버 재부팅 대비
                            <span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon"><strong>pm2 save</strong>는 현재 PM2가 관리하는 앱 목록을 파일로 저장한다. <strong>pm2 startup</strong>은 서버가 부팅될 때 PM2를 자동으로 시작하도록 시스템에 등록한다. 이 두 명령을 함께 사용하면 서버 재부팅 후에도 앱이 자동으로 살아난다.</span></span>
                        </h3>
                        <div class="border border-gray-200 rounded-lg p-4 cmd-card mb-3">
                            <p class="text-xs text-gray-600 mb-3">서버가 재부팅되면 PM2도 함께 꺼진다. 재부팅 후 앱이 자동으로 살아나게 하려면 아래 두 명령을 실행해야 한다.</p>
                            <div class="code-block"><span class="code-comment"># 현재 프로세스 목록 저장 (재부팅 후 자동 복구용)</span>
<span class="code-prompt">$</span> <span class="code-keyword">pm2</span> <span class="code-string">save</span>
<span class="code-output">[PM2] Saving current process list...</span>
<span class="code-output">[PM2] Successfully saved in /home/pro/.pm2/dump.pm2</span>

<span class="code-comment"># 부팅 시 PM2 자동 시작 설정 (최초 1회만 실행)</span>
<span class="code-prompt">$</span> <span class="code-keyword">pm2</span> <span class="code-string">startup</span>
<span class="code-comment"># 시스템 서비스로 등록 — 서버 켜질 때 PM2가 자동 실행된다</span></div>
                        </div>
                        <div class="bg-amber-50 rounded-lg p-3 border border-amber-100 text-xs text-amber-800">
                            <strong>중요:</strong> PM2에 새 앱을 추가하거나 삭제한 후에는 반드시 <code class="bg-amber-100 px-1 rounded">pm2 save</code>를 실행해야 한다. 그래야 서버 재부팅 후에도 변경된 목록이 유지된다.
                        </div>
                    </div>

                    {{-- 명령어 요약 표 --}}
                    <div class="mt-6 bg-gray-50 rounded-lg p-4 border border-gray-200">
                        <p class="font-bold text-gray-800 mb-3 text-sm">PM2 명령어 요약표</p>
                        <div class="overflow-x-auto">
                            <table class="w-full text-xs border-collapse">
                                <thead>
                                    <tr class="bg-gray-100">
                                        <th class="border border-gray-200 px-3 py-2 text-left">명령어</th>
                                        <th class="border border-gray-200 px-3 py-2 text-left">설명</th>
                                        <th class="border border-gray-200 px-3 py-2 text-left">사용 예시</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    <tr>
                                        <td class="border border-gray-200 px-3 py-2 font-mono font-bold">pm2 list</td>
                                        <td class="border border-gray-200 px-3 py-2">실행 중인 앱 목록 확인</td>
                                        <td class="border border-gray-200 px-3 py-2">앱 이름, 상태, CPU/메모리 표시</td>
                                    </tr>
                                    <tr class="bg-gray-50">
                                        <td class="border border-gray-200 px-3 py-2 font-mono font-bold">pm2 start</td>
                                        <td class="border border-gray-200 px-3 py-2">앱 시작</td>
                                        <td class="border border-gray-200 px-3 py-2 font-mono text-xs">pm2 start npm --name sam-react -- start</td>
                                    </tr>
                                    <tr>
                                        <td class="border border-gray-200 px-3 py-2 font-mono font-bold">pm2 restart</td>
                                        <td class="border border-gray-200 px-3 py-2">앱 재시작</td>
                                        <td class="border border-gray-200 px-3 py-2 font-mono text-xs">pm2 restart sam-react</td>
                                    </tr>
                                    <tr class="bg-gray-50">
                                        <td class="border border-gray-200 px-3 py-2 font-mono font-bold">pm2 stop</td>
                                        <td class="border border-gray-200 px-3 py-2">앱 중지</td>
                                        <td class="border border-gray-200 px-3 py-2 font-mono text-xs">pm2 stop sam-react</td>
                                    </tr>
                                    <tr>
                                        <td class="border border-gray-200 px-3 py-2 font-mono font-bold">pm2 delete</td>
                                        <td class="border border-gray-200 px-3 py-2">앱 삭제 (목록에서 제거)</td>
                                        <td class="border border-gray-200 px-3 py-2 font-mono text-xs">pm2 delete sam-react</td>
                                    </tr>
                                    <tr class="bg-gray-50">
                                        <td class="border border-gray-200 px-3 py-2 font-mono font-bold">pm2 logs</td>
                                        <td class="border border-gray-200 px-3 py-2">실시간 로그 보기</td>
                                        <td class="border border-gray-200 px-3 py-2 font-mono text-xs">pm2 logs sam-react</td>
                                    </tr>
                                    <tr>
                                        <td class="border border-gray-200 px-3 py-2 font-mono font-bold">pm2 monit</td>
                                        <td class="border border-gray-200 px-3 py-2">실시간 모니터링 대시보드</td>
                                        <td class="border border-gray-200 px-3 py-2">CPU, 메모리, 로그 실시간 확인</td>
                                    </tr>
                                    <tr class="bg-gray-50">
                                        <td class="border border-gray-200 px-3 py-2 font-mono font-bold">pm2 save</td>
                                        <td class="border border-gray-200 px-3 py-2">현재 프로세스 목록 저장</td>
                                        <td class="border border-gray-200 px-3 py-2">서버 재부팅 후 자동 복구용</td>
                                    </tr>
                                    <tr>
                                        <td class="border border-gray-200 px-3 py-2 font-mono font-bold">pm2 startup</td>
                                        <td class="border border-gray-200 px-3 py-2">부팅 시 PM2 자동 시작 설정</td>
                                        <td class="border border-gray-200 px-3 py-2">시스템 서비스로 등록</td>
                                    </tr>
                                </tbody>
                            </table>
                        </div>
                    </div>
                </div>
            </section>

            {{-- ============================================================ --}}
            {{-- 4. SAM 배포와 PM2 --}}
            {{-- ============================================================ --}}
            <section id="sam-deploy" class="scroll-mt-20">
                <div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
                    <h2 class="text-xl font-bold text-gray-800 mb-6 flex items-center gap-3">
                        <span class="w-8 h-8 bg-blue-500 text-white rounded-lg flex items-center justify-center text-sm font-bold">4</span>
                        SAM 배포와 PM2
                    </h2>

                    <div id="deploy-flow" class="scroll-mt-20 mb-8">
                        <h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
                            <span class="w-2 h-2 bg-blue-400 rounded-full"></span>
                            배포 흐름
                        </h3>
                        <p class="text-xs text-gray-600 mb-3">
                            SAM React 코드를 수정한 후, 사용자에게 반영하기까지의 전체 흐름이다.
                            <code class="bg-gray-100 px-1.5 py-0.5 rounded text-xs">deploy.sh</code> 스크립트가 이 과정을 자동으로 처리한다.
                        </p>
                        <div class="code-block mb-4"><span class="code-comment">  배포 흐름 (deploy.sh가 자동으로 처리)</span>

<span class="code-keyword">  ① 로컬에서 코드 수정 & git commit</span>
<span class="code-comment">     │</span>
<span class="code-comment">     ▼</span>
<span class="code-keyword">  ② ./deploy.sh 실행</span>
<span class="code-comment">     │</span>
<span class="code-comment">     ├──▶</span> <span class="code-string">③ git pull</span> <span class="code-comment">(최신 코드 받기)</span>
<span class="code-comment">     │</span>
<span class="code-comment">     ├──▶</span> <span class="code-string">④ npm run build</span> <span class="code-comment">(로컬에서 빌드)</span>
<span class="code-comment">     │</span>
<span class="code-comment">     ├──▶</span> <span class="code-string">⑤ .next 폴더 압축 → 서버 업로드</span>
<span class="code-comment">     │</span>
<span class="code-comment">     ├──▶</span> <span class="code-string">⑥ 서버에서 압축 해제</span>
<span class="code-comment">     │</span>
<span class="code-comment">     └──▶</span> <span class="code-danger">⑦ pm2 restart sam-react</span> <span class="code-comment">(앱 재시작!)</span>

<span class="code-comment">  → 사용자가 새 화면을 볼 수 있게 됨</span></div>

                        <div class="bg-blue-50 rounded-lg p-3 border border-blue-100 text-xs text-blue-800 mb-4">
                            <strong>핵심:</strong> deploy.sh 스크립트가 마지막 단계에서 자동으로 <code class="bg-blue-100 px-1 rounded">pm2 restart sam-react</code>를 호출한다. 따라서 배포 시 수동으로 PM2 명령을 실행할 필요가 없다.
                        </div>
                    </div>

                    <div id="deploy-why-local" class="scroll-mt-20">
                        <h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
                            <span class="w-2 h-2 bg-blue-400 rounded-full"></span>
                            서버에서 빌드하지 않는 이유
                        </h3>
                        <div class="bg-red-50 rounded-lg p-4 border border-red-200 mb-4">
                            <p class="font-semibold text-red-800 mb-2">서버 빌드 금지!</p>
                            <p class="text-xs text-red-700 leading-relaxed">
                                SAM 운영 서버의 스펙은 <strong>2코어 CPU, 3.8GB RAM</strong>이다.
                                Next.js 빌드(<code class="bg-red-100 px-1 rounded">npm run build</code>)는 메모리를 많이 사용하는 작업으로,
                                이 스펙에서 실행하면 <strong>20분 이상 소요</strong>되거나 <strong>메모리 부족으로 실패</strong>한다.
                                그래서 빌드는 로컬(내 PC)에서 하고, 빌드된 결과물(.next 폴더)만 서버에 올리는 방식을 사용한다.
                            </p>
                        </div>
                        <div class="grid gap-3" style="grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));">
                            <div class="bg-green-50 rounded-lg p-4 border border-green-200">
                                <p class="font-bold text-green-800 mb-2">올바른 방법 (로컬 빌드)</p>
                                <div class="code-block" style="font-size: 11px;"><span class="code-comment"># 로컬(내 PC)에서</span>
<span class="code-prompt">$</span> <span class="code-keyword">npm run build</span>
<span class="code-comment"># 빌드 결과물을 서버로 전송</span>
<span class="code-comment"># PM2 재시작</span></div>
                            </div>
                            <div class="bg-red-50 rounded-lg p-4 border border-red-200">
                                <p class="font-bold text-red-800 mb-2">잘못된 방법 (서버 빌드)</p>
                                <div class="code-block" style="font-size: 11px;"><span class="code-comment"># 서버에서 직접 빌드 — 금지!</span>
<span class="code-danger">$ ssh 서버</span>
<span class="code-danger">$ npm run build</span>
<span class="code-comment"># → 메모리 부족, 서버 먹통 위험!</span></div>
                            </div>
                        </div>
                    </div>
                </div>
            </section>

            {{-- ============================================================ --}}
            {{-- 5. 트러블슈팅 --}}
            {{-- ============================================================ --}}
            <section id="troubleshooting" class="scroll-mt-20">
                <div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
                    <h2 class="text-xl font-bold text-gray-800 mb-6 flex items-center gap-3">
                        <span class="w-8 h-8 bg-blue-500 text-white rounded-lg flex items-center justify-center text-sm font-bold">5</span>
                        트러블슈팅 — "이럴 땐 어떻게?"
                    </h2>

                    {{-- 문제 1 --}}
                    <div id="trouble-not-found" class="scroll-mt-20 mb-6">
                        <div class="border border-red-200 rounded-lg p-4 cmd-card">
                            <div class="flex items-center gap-2 mb-2">
                                <span class="w-6 h-6 bg-red-500 text-white rounded-full flex items-center justify-center text-xs font-bold">1</span>
                                <p class="font-bold text-gray-800 text-sm">Process or Namespace sam-react not found</p>
                            </div>
                            <div class="code-block mb-3"><span class="code-prompt">$</span> <span class="code-keyword">pm2</span> <span class="code-string">restart</span> <span class="code-path">sam-react</span>
<span class="code-danger">[PM2][ERROR] Process or Namespace sam-react not found</span></div>
                            <div class="bg-amber-50 rounded-lg p-3 border border-amber-100 text-xs mb-3">
                                <p class="font-semibold text-amber-800 mb-1">원인:</p>
                                <p class="text-amber-700">PM2에 <code class="bg-amber-100 px-1 rounded">sam-react</code>라는 앱이 등록되어 있지 않다. PM2가 재설치되었거나, <code class="bg-amber-100 px-1 rounded">pm2 delete</code>로 삭제된 경우 발생한다.</p>
                            </div>
                            <div class="bg-green-50 rounded-lg p-3 border border-green-200 text-xs">
                                <p class="font-semibold text-green-800 mb-1">해결:</p>
                                <div class="code-block" style="font-size: 11px;"><span class="code-comment"># Next.js 프로젝트 폴더로 이동</span>
<span class="code-prompt">$</span> <span class="code-keyword">cd</span> <span class="code-path">/home/webservice/react</span>

<span class="code-comment"># PM2에 앱을 새로 등록하고 시작</span>
<span class="code-prompt">$</span> <span class="code-keyword">pm2</span> <span class="code-string">start</span> <span class="code-path">npm</span> <span class="code-flag">--name</span> <span class="code-string">sam-react</span> <span class="code-flag">--</span> <span class="code-string">start</span>

<span class="code-comment"># 목록 저장 (재부팅 대비)</span>
<span class="code-prompt">$</span> <span class="code-keyword">pm2</span> <span class="code-string">save</span></div>
                            </div>
                        </div>
                    </div>

                    {{-- 문제 2 --}}
                    <div id="trouble-errored" class="scroll-mt-20 mb-6">
                        <div class="border border-red-200 rounded-lg p-4 cmd-card">
                            <div class="flex items-center gap-2 mb-2">
                                <span class="w-6 h-6 bg-red-500 text-white rounded-full flex items-center justify-center text-xs font-bold">2</span>
                                <p class="font-bold text-gray-800 text-sm">PM2 앱 상태가 errored</p>
                            </div>
                            <div class="code-block mb-3"><span class="code-prompt">$</span> <span class="code-keyword">pm2</span> <span class="code-string">list</span>
<span class="code-output">│ 0  │ sam-react    │ fork   │</span> <span class="code-danger">errored</span> <span class="code-output">│ 0%   │ 0 B       │</span></div>
                            <div class="bg-amber-50 rounded-lg p-3 border border-amber-100 text-xs mb-3">
                                <p class="font-semibold text-amber-800 mb-1">원인:</p>
                                <p class="text-amber-700">Node.js 앱(Next.js)에 에러가 발생하여 실행이 중단된 상태이다. 코드 오류, 의존성 문제, 환경 변수 누락 등이 원인일 수 있다.</p>
                            </div>
                            <div class="bg-green-50 rounded-lg p-3 border border-green-200 text-xs">
                                <p class="font-semibold text-green-800 mb-1">해결:</p>
                                <div class="code-block" style="font-size: 11px;"><span class="code-comment"># 1. 먼저 에러 로그를 확인한다</span>
<span class="code-prompt">$</span> <span class="code-keyword">pm2</span> <span class="code-string">logs</span> <span class="code-path">sam-react</span> <span class="code-flag">--lines 50</span>

<span class="code-comment"># 2. 에러 원인을 파악한 후 수정</span>
<span class="code-comment"># 3. 앱 재시작</span>
<span class="code-prompt">$</span> <span class="code-keyword">pm2</span> <span class="code-string">restart</span> <span class="code-path">sam-react</span>

<span class="code-comment"># 4. 상태 확인 (online이면 정상)</span>
<span class="code-prompt">$</span> <span class="code-keyword">pm2</span> <span class="code-string">list</span></div>
                            </div>
                        </div>
                    </div>

                    {{-- 문제 3 --}}
                    <div id="trouble-reboot" class="scroll-mt-20 mb-6">
                        <div class="border border-red-200 rounded-lg p-4 cmd-card">
                            <div class="flex items-center gap-2 mb-2">
                                <span class="w-6 h-6 bg-red-500 text-white rounded-full flex items-center justify-center text-xs font-bold">3</span>
                                <p class="font-bold text-gray-800 text-sm">서버 재부팅 후 앱이 안 뜸</p>
                            </div>
                            <div class="code-block mb-3"><span class="code-prompt">$</span> <span class="code-keyword">pm2</span> <span class="code-string">list</span>
<span class="code-output">┌────┬──────┬──────┬──────┬─────┬────────┐</span>
<span class="code-output">│ id │ name │ mode │ ... │ ... │ ...    │</span>
<span class="code-output">└────┴──────┴──────┴──────┴─────┴────────┘</span>
<span class="code-comment">(목록이 비어있음)</span></div>
                            <div class="bg-amber-50 rounded-lg p-3 border border-amber-100 text-xs mb-3">
                                <p class="font-semibold text-amber-800 mb-1">원인:</p>
                                <p class="text-amber-700">이전에 <code class="bg-amber-100 px-1 rounded">pm2 save</code>를 실행하지 않아서, PM2가 재부팅 후 어떤 앱을 실행해야 하는지 모르는 상태이다.</p>
                            </div>
                            <div class="bg-green-50 rounded-lg p-3 border border-green-200 text-xs">
                                <p class="font-semibold text-green-800 mb-1">해결:</p>
                                <div class="code-block" style="font-size: 11px;"><span class="code-comment"># 1. Next.js 프로젝트 폴더로 이동</span>
<span class="code-prompt">$</span> <span class="code-keyword">cd</span> <span class="code-path">/home/webservice/react</span>

<span class="code-comment"># 2. 앱 다시 시작</span>
<span class="code-prompt">$</span> <span class="code-keyword">pm2</span> <span class="code-string">start</span> <span class="code-path">npm</span> <span class="code-flag">--name</span> <span class="code-string">sam-react</span> <span class="code-flag">--</span> <span class="code-string">start</span>

<span class="code-comment"># 3. 반드시 저장! (다음 재부팅에 대비)</span>
<span class="code-prompt">$</span> <span class="code-keyword">pm2</span> <span class="code-string">save</span>

<span class="code-comment"># 4. (선택) 부팅 시 PM2 자동 시작 확인</span>
<span class="code-prompt">$</span> <span class="code-keyword">pm2</span> <span class="code-string">startup</span></div>
                            </div>
                        </div>
                    </div>

                    {{-- 문제 4 --}}
                    <div id="trouble-port" class="scroll-mt-20">
                        <div class="border border-red-200 rounded-lg p-4 cmd-card">
                            <div class="flex items-center gap-2 mb-2">
                                <span class="w-6 h-6 bg-red-500 text-white rounded-full flex items-center justify-center text-xs font-bold">4</span>
                                <p class="font-bold text-gray-800 text-sm">포트 3000 이미 사용 중</p>
                                <span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon"><strong>포트(Port)</strong>는 네트워크에서 프로그램을 식별하는 번호이다. 아파트 호수처럼, 한 서버 안에서 여러 프로그램이 각자 다른 포트 번호를 사용한다. Next.js는 기본적으로 포트 3000을 사용한다.</span></span>
                            </div>
                            <div class="code-block mb-3"><span class="code-prompt">$</span> <span class="code-keyword">pm2</span> <span class="code-string">start</span> <span class="code-path">npm</span> <span class="code-flag">--name</span> <span class="code-string">sam-react</span> <span class="code-flag">--</span> <span class="code-string">start</span>
<span class="code-danger">Error: listen EADDRINUSE: address already in use :::3000</span></div>
                            <div class="bg-amber-50 rounded-lg p-3 border border-amber-100 text-xs mb-3">
                                <p class="font-semibold text-amber-800 mb-1">원인:</p>
                                <p class="text-amber-700">이전에 실행했던 Node.js 프로세스가 아직 포트 3000을 점유하고 있다. PM2에서 제대로 종료되지 않은 잔여 프로세스가 남아있는 경우 발생한다.</p>
                            </div>
                            <div class="bg-green-50 rounded-lg p-3 border border-green-200 text-xs">
                                <p class="font-semibold text-green-800 mb-1">해결:</p>
                                <div class="code-block" style="font-size: 11px;"><span class="code-comment"># 1. 기존 앱을 PM2에서 완전히 제거</span>
<span class="code-prompt">$</span> <span class="code-keyword">pm2</span> <span class="code-string">delete</span> <span class="code-path">sam-react</span>

<span class="code-comment"># 2. 혹시 남아있는 Node.js 프로세스 확인</span>
<span class="code-prompt">$</span> <span class="code-keyword">ss</span> <span class="code-flag">-tulnp</span> | <span class="code-keyword">grep</span> <span class="code-string">:3000</span>

<span class="code-comment"># 3. 다시 시작</span>
<span class="code-prompt">$</span> <span class="code-keyword">cd</span> <span class="code-path">/home/webservice/react</span>
<span class="code-prompt">$</span> <span class="code-keyword">pm2</span> <span class="code-string">start</span> <span class="code-path">npm</span> <span class="code-flag">--name</span> <span class="code-string">sam-react</span> <span class="code-flag">--</span> <span class="code-string">start</span>

<span class="code-comment"># 4. 목록 저장</span>
<span class="code-prompt">$</span> <span class="code-keyword">pm2</span> <span class="code-string">save</span></div>
                            </div>
                        </div>
                    </div>
                </div>
            </section>

            {{-- ============================================================ --}}
            {{-- 6. PM2 vs 다른 방법 비교 --}}
            {{-- ============================================================ --}}
            <section id="comparison" class="scroll-mt-20">
                <div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
                    <h2 class="text-xl font-bold text-gray-800 mb-6 flex items-center gap-3">
                        <span class="w-8 h-8 bg-blue-500 text-white rounded-lg flex items-center justify-center text-sm font-bold">6</span>
                        PM2 vs 다른 방법 비교
                    </h2>

                    <p class="text-xs text-gray-600 mb-4">
                        Node.js 앱을 실행하는 여러 방법을 비교한 표이다. PM2가 모든 면에서 우수하기 때문에 SAM에서 PM2를 선택했다.
                    </p>

                    <div class="overflow-x-auto mb-6">
                        <table class="w-full text-xs border-collapse">
                            <thead>
                                <tr class="bg-gray-100">
                                    <th class="border border-gray-200 px-3 py-2 text-left">실행 방법</th>
                                    <th class="border border-gray-200 px-3 py-2 text-center">백그라운드 실행</th>
                                    <th class="border border-gray-200 px-3 py-2 text-center">자동 재시작</th>
                                    <th class="border border-gray-200 px-3 py-2 text-center">모니터링</th>
                                    <th class="border border-gray-200 px-3 py-2 text-center">로그 관리</th>
                                </tr>
                            </thead>
                            <tbody>
                                <tr>
                                    <td class="border border-gray-200 px-3 py-2 font-mono font-bold">npm start</td>
                                    <td class="border border-gray-200 px-3 py-2 text-center text-red-500 font-bold">X</td>
                                    <td class="border border-gray-200 px-3 py-2 text-center text-red-500 font-bold">X</td>
                                    <td class="border border-gray-200 px-3 py-2 text-center text-red-500 font-bold">X</td>
                                    <td class="border border-gray-200 px-3 py-2 text-center text-red-500 font-bold">X</td>
                                </tr>
                                <tr class="bg-gray-50">
                                    <td class="border border-gray-200 px-3 py-2 font-mono font-bold">nohup npm start &
                                        <span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon"><strong>nohup</strong>은 터미널을 닫아도 프로세스가 종료되지 않게 하는 명령어이다. <strong>&</strong>는 백그라운드 실행을 의미한다.</span></span>
                                    </td>
                                    <td class="border border-gray-200 px-3 py-2 text-center text-green-500 font-bold">O</td>
                                    <td class="border border-gray-200 px-3 py-2 text-center text-red-500 font-bold">X</td>
                                    <td class="border border-gray-200 px-3 py-2 text-center text-red-500 font-bold">X</td>
                                    <td class="border border-gray-200 px-3 py-2 text-center text-red-500 font-bold">X</td>
                                </tr>
                                <tr>
                                    <td class="border border-gray-200 px-3 py-2 font-mono font-bold">screen / tmux
                                        <span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon"><strong>screen</strong>과 <strong>tmux</strong>는 터미널 세션을 유지해주는 도구이다. 터미널을 닫아도 세션이 유지되지만, 자동 재시작이나 모니터링 기능은 없다.</span></span>
                                    </td>
                                    <td class="border border-gray-200 px-3 py-2 text-center text-green-500 font-bold">O</td>
                                    <td class="border border-gray-200 px-3 py-2 text-center text-red-500 font-bold">X</td>
                                    <td class="border border-gray-200 px-3 py-2 text-center text-red-500 font-bold">X</td>
                                    <td class="border border-gray-200 px-3 py-2 text-center text-red-500 font-bold">X</td>
                                </tr>
                                <tr class="bg-blue-50">
                                    <td class="border border-blue-200 px-3 py-2 font-mono font-bold text-blue-800">PM2</td>
                                    <td class="border border-blue-200 px-3 py-2 text-center text-green-600 font-bold">O</td>
                                    <td class="border border-blue-200 px-3 py-2 text-center text-green-600 font-bold">O</td>
                                    <td class="border border-blue-200 px-3 py-2 text-center text-green-600 font-bold">O</td>
                                    <td class="border border-blue-200 px-3 py-2 text-center text-green-600 font-bold">O</td>
                                </tr>
                            </tbody>
                        </table>
                    </div>

                    <div class="bg-indigo-50 rounded-lg p-4 border border-indigo-100">
                        <p class="font-semibold text-indigo-800 mb-2">결론</p>
                        <p class="text-xs text-indigo-900 leading-relaxed">
                            <code class="bg-indigo-100 px-1 rounded">npm start</code>는 개발 중에만 사용하는 방법이다.
                            실제 서비스를 운영할 때는 백그라운드 실행, 자동 재시작, 모니터링, 로그 관리를 모두 지원하는
                            <strong>PM2가 최적의 선택</strong>이다.
                            SAM에서도 이러한 이유로 PM2를 사용하여 Next.js 프론트엔드를 안정적으로 운영하고 있다.
                        </p>
                    </div>
                </div>
            </section>

        </div>
    </div>

</div>

{{-- Hover Preview --}}
<div id="hover-preview">
    <img id="hover-preview-img" src="" alt="">
    <div class="hover-caption" id="hover-preview-caption"></div>
</div>

{{-- Lightbox --}}
<div id="lightbox" onclick="closeLightbox()">
    <img id="lightbox-img" src="" alt="">
</div>

@include('components.academy-glossary', ['domain' => 'pm2-guide'])
@endsection

@push('scripts')
<script>
(function() {
    // 도움말 풍선 토글
    window.toggleBalloon = function(el) {
        var wasActive = el.classList.contains('is-active');
        document.querySelectorAll('.help-balloon-trigger.is-active').forEach(function(t) {
            t.classList.remove('is-active');
        });
        if (!wasActive) {
            el.classList.add('is-active');
        }
    };

    // 풍선 외부 클릭 시 닫기
    document.addEventListener('click', function(e) {
        if (!e.target.closest('.help-balloon-trigger')) {
            document.querySelectorAll('.help-balloon-trigger.is-active').forEach(function(t) {
                t.classList.remove('is-active');
            });
        }
    });

    // Lightbox
    window.openLightbox = function(el) {
        var lb = document.getElementById('lightbox');
        var img = document.getElementById('lightbox-img');
        img.src = el.src;
        img.alt = el.alt;
        lb.classList.add('is-open');
        document.body.style.overflow = 'hidden';
    };

    window.closeLightbox = function() {
        var lb = document.getElementById('lightbox');
        lb.classList.remove('is-open');
        document.body.style.overflow = '';
    };

    document.addEventListener('keydown', function(e) {
        if (e.key === 'Escape') {
            closeLightbox();
        }
    });
})();
</script>
@endpush
