Files
sam-manage/resources/views/system/git-deploy-flow.blade.php
김보곤 daf4b20fe8 fix: [system] MNG 개발서버 도메인 수정
- mng.dev.codebridge-x.com → admin.codebridge-x.com
- 도메인 스왑(48ef98e) 반영
2026-02-25 09:26:58 +09:00

856 lines
56 KiB
PHP

@extends('layouts.app')
@section('title', '운영서버 Git 동작원리')
@push('styles')
<style>
.guide-img-hover {
transition: box-shadow 0.2s ease;
}
.guide-img-hover:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
#lightbox {
display: none;
position: fixed;
inset: 0;
z-index: 50;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.9);
backdrop-filter: blur(4px);
}
#lightbox.is-open {
display: flex;
}
#lightbox img {
max-height: 90vh;
max-width: 90vw;
border-radius: 0.5rem;
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.5);
}
.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: #2563eb;
}
.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.6;
overflow-x: auto;
white-space: pre;
}
.code-block .code-comment { color: #6c7086; }
.code-block .code-tag { color: #89b4fa; }
.code-block .code-attr { color: #f9e2af; }
.code-block .code-string { color: #a6e3a1; }
.code-block .code-keyword { color: #cba6f7; }
.code-block .code-func { color: #89dceb; }
.code-block .code-number { color: #fab387; }
.flow-arrow {
display: flex;
align-items: center;
justify-content: center;
color: #3b82f6;
font-size: 1.5rem;
padding: 0.25rem 0;
}
.flow-step {
border-radius: 0.75rem;
padding: 1rem 1.25rem;
text-align: center;
font-size: 0.8rem;
}
</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, #0c1445 0%, #1e293b 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: #60a5fa;">
<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: #93c5fd;">운영서버 Git 동작원리</span>
</div>
<h1 class="text-3xl font-bold mb-2" style="color: #ffffff;">운영서버 Git 동작원리</h1>
<p class="text-sm" style="color: #cbd5e1;">git push 코드가 서버에 반영되기까지 배포 자동화 파이프라인 가이드</p>
</div>
<div class="shrink-0" style="width: 240px; padding: 1.5rem;">
<div class="overflow-hidden rounded-xl flex items-center justify-center" style="height: 140px; background: rgba(59, 130, 246, 0.1);">
<svg style="width: 100px; height: 100px;" fill="none" stroke="#60a5fa" viewBox="0 0 24 24" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M5.25 14.25h13.5m-13.5 0a3 3 0 01-3-3m3 3a3 3 0 100 6h13.5a3 3 0 100-6m-16.5-3a3 3 0 013-3h13.5a3 3 0 013 3m-19.5 0a4.5 4.5 0 01.9-2.7L5.737 5.1a3.375 3.375 0 012.7-1.35h7.126c1.062 0 2.062.5 2.7 1.35l2.587 3.45a4.5 4.5 0 01.9 2.7m0 0a3 3 0 01-3 3m0 3h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008zm-3 6h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008z" />
</svg>
</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="#overview" class="block text-blue-700 hover:text-blue-900 py-1 font-medium">1. 핵심 요약</a>
<a href="#unchanged" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">변경되지 않은 </a>
<a href="#what-changed" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">변경된 </a>
<a href="#repositories" class="block text-blue-700 hover:text-blue-900 py-1 font-medium mt-2">2. Git 원격 저장소</a>
<a href="#remote-urls" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">3 저장소 URL</a>
<a href="#gitea-server" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">Gitea 서버</a>
<a href="#before-after" class="block text-blue-700 hover:text-blue-900 py-1 font-medium mt-2">3. Before vs After</a>
<a href="#before" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">이전 (수동 배포)</a>
<a href="#after" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">현재 (Jenkins CI/CD)</a>
<a href="#pipeline" class="block text-blue-700 hover:text-blue-900 py-1 font-medium mt-2">4. 배포 파이프라인</a>
<a href="#webhook-flow" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">Webhook 흐름</a>
<a href="#jenkins-steps" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">Jenkins 단계</a>
<a href="#branch-strategy" class="block text-blue-700 hover:text-blue-900 py-1 font-medium mt-2">5. 브랜치 전략</a>
<a href="#develop-branch" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">develop 브랜치</a>
<a href="#main-branch" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">main/master 브랜치</a>
<a href="#domain-mapping" class="block text-blue-700 hover:text-blue-900 py-1 font-medium mt-2">6. 도메인 매핑</a>
<a href="#faq" class="block text-blue-700 hover:text-blue-900 py-1 font-medium mt-2">7. FAQ</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="#overview" class="block text-blue-700 hover:text-blue-900 py-1">1. 핵심 요약</a>
<a href="#repositories" class="block text-blue-700 hover:text-blue-900 py-1">2. Git 원격 저장소</a>
<a href="#before-after" class="block text-blue-700 hover:text-blue-900 py-1">3. Before vs After</a>
<a href="#pipeline" class="block text-blue-700 hover:text-blue-900 py-1">4. 배포 파이프라인</a>
<a href="#branch-strategy" class="block text-blue-700 hover:text-blue-900 py-1">5. 브랜치 전략</a>
<a href="#domain-mapping" class="block text-blue-700 hover:text-blue-900 py-1">6. 도메인 매핑</a>
<a href="#faq" class="block text-blue-700 hover:text-blue-900 py-1">7. FAQ</a>
</nav>
</details>
</div>
{{-- ============================================================ --}}
{{-- 1. 핵심 요약 --}}
{{-- ============================================================ --}}
<section id="overview" 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-600 text-white rounded-lg flex items-center justify-center text-sm font-bold">1</span>
핵심 요약
</h2>
<div class="bg-blue-50 rounded-lg p-5 border border-blue-100 mb-6">
<p class="text-sm text-blue-900 leading-relaxed">
<strong> 요약:</strong> git push 하는 방법은 <strong>전혀 바뀌지 않았다</strong>.
바뀐 것은 push <strong>이후</strong> 서버에 코드가 반영되는 과정이 <strong>수동 자동</strong>으로 변경된 것이다.
</p>
</div>
<div id="unchanged" 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-green-400 rounded-full"></span>
변경되지 않은
</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-3 text-xs mb-4">
<div class="bg-green-50 rounded-lg p-4 border border-green-200">
<p class="font-bold text-green-800 mb-2">Git 원격 저장소 (origin)</p>
<ul class="text-green-700 space-y-1">
<li>- Gitea 서버 주소 동일</li>
<li>- <code class="bg-green-100 px-1 rounded">git remote -v</code> 결과 동일</li>
<li>- 저장소 URL 변경 없음</li>
</ul>
</div>
<div class="bg-green-50 rounded-lg p-4 border border-green-200">
<p class="font-bold text-green-800 mb-2">개발자 작업 흐름</p>
<ul class="text-green-700 space-y-1">
<li>- <code class="bg-green-100 px-1 rounded">git add commit push</code> 동일</li>
<li>- 브랜치 이름 동일 (develop)</li>
<li>- 커밋 메시지 규칙 동일</li>
</ul>
</div>
</div>
</div>
<div id="what-changed" class="scroll-mt-20 mb-4">
<h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
<span class="w-2 h-2 bg-amber-400 rounded-full"></span>
변경된
</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-3 text-xs mb-4">
<div class="bg-amber-50 rounded-lg p-4 border border-amber-200">
<p class="font-bold text-amber-800 mb-2">이전 (수동)</p>
<ul class="text-amber-700 space-y-1">
<li>- git push 서버에 SSH 접속</li>
<li>- 수동으로 <code class="bg-amber-100 px-1 rounded">git pull</code></li>
<li>- 수동으로 <code class="bg-amber-100 px-1 rounded">composer install</code></li>
<li>- 수동으로 <code class="bg-amber-100 px-1 rounded">php artisan migrate</code></li>
</ul>
</div>
<div class="bg-blue-50 rounded-lg p-4 border border-blue-200">
<p class="font-bold text-blue-800 mb-2">현재 (Jenkins CI/CD)</p>
<ul class="text-blue-700 space-y-1">
<li>- git push 하면 <strong>자동 배포</strong></li>
<li>- Gitea Webhook Jenkins 트리거</li>
<li>- Jenkins가 pull/install/migrate 자동 수행</li>
<li>- 결과를 Gitea PR에 자동 보고</li>
</ul>
</div>
</div>
</div>
</div>
</section>
{{-- ============================================================ --}}
{{-- 2. Git 원격 저장소 --}}
{{-- ============================================================ --}}
<section id="repositories" 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-600 text-white rounded-lg flex items-center justify-center text-sm font-bold">2</span>
Git 원격 저장소
</h2>
<div id="remote-urls" 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>
3 저장소 URL
<span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon">SAM 프로젝트는 MNG(관리자), API(백엔드), React(프론트엔드) 3 독립 저장소로 구성되어 있다. 저장소는 별도의 Git 히스토리를 가진다.</span></span>
</h3>
<div class="overflow-x-auto mb-4">
<table class="w-full text-sm border-collapse">
<thead>
<tr class="border-b-2 border-gray-200 bg-gray-50">
<th class="text-left py-2 px-3 font-medium text-gray-600">프로젝트</th>
<th class="text-left py-2 px-3 font-medium text-gray-600">로컬 경로</th>
<th class="text-left py-2 px-3 font-medium text-gray-600">Origin URL</th>
<th class="text-left py-2 px-3 font-medium text-gray-600">현재 브랜치</th>
</tr>
</thead>
<tbody class="text-xs text-gray-700">
<tr class="border-b border-gray-100">
<td class="py-2 px-3 font-bold text-blue-600">MNG</td>
<td class="py-2 px-3 font-mono">/home/aweso/sam/mng</td>
<td class="py-2 px-3 font-mono text-gray-500">http://114.203.209.83:3000/SamProject/sam-manage.git</td>
<td class="py-2 px-3"><span class="bg-green-100 text-green-700 px-2 py-0.5 rounded-full font-mono">develop</span></td>
</tr>
<tr class="border-b border-gray-100">
<td class="py-2 px-3 font-bold text-purple-600">API</td>
<td class="py-2 px-3 font-mono">/home/aweso/sam/api</td>
<td class="py-2 px-3 font-mono text-gray-500">http://114.203.209.83:3000/SamProject/sam-api.git</td>
<td class="py-2 px-3"><span class="bg-green-100 text-green-700 px-2 py-0.5 rounded-full font-mono">develop</span></td>
</tr>
<tr>
<td class="py-2 px-3 font-bold text-emerald-600">React</td>
<td class="py-2 px-3 font-mono">/home/aweso/sam/react</td>
<td class="py-2 px-3 font-mono text-gray-500">http://114.203.209.83:3000/SamProject/sam-react-prod.git</td>
<td class="py-2 px-3"><span class="bg-green-100 text-green-700 px-2 py-0.5 rounded-full font-mono">develop</span></td>
</tr>
</tbody>
</table>
</div>
<div class="code-block mb-4"><span class="code-comment"># 원격 저장소 확인 명령어 (각 프로젝트 폴더에서 실행)</span>
<span class="code-keyword">cd</span> <span class="code-string">/home/aweso/sam/mng</span>
<span class="code-keyword">git remote</span> <span class="code-attr">-v</span>
<span class="code-comment"># origin http://114.203.209.83:3000/SamProject/sam-manage.git (fetch)</span>
<span class="code-comment"># origin http://114.203.209.83:3000/SamProject/sam-manage.git (push)</span></div>
</div>
<div id="gitea-server" class="scroll-mt-20 mb-4">
<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>
Gitea 서버
<span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon">Gitea는 자체 호스팅 Git 서비스로, GitHub와 유사한 기능(코드 저장, PR, 이슈 관리 ) 제공한다. SAM은 개발 서버(114.203.209.83)에서 Gitea를 운영한다.</span></span>
</h3>
<div class="bg-blue-50 rounded-lg p-5 border border-blue-100 mb-4">
<p class="text-sm text-blue-900 leading-relaxed">
<strong>Gitea UI:</strong> <code class="text-xs bg-blue-100 px-1 rounded">http://114.203.209.83:3000</code><br>
브라우저에서 접속하면 저장소 목록, 커밋 이력, PR(Pull Request) 관리 등을 있다.
<strong>origin URL의 IP와 포트가 동일</strong>하다 , 같은 서버다.
</p>
</div>
<div class="bg-gray-50 rounded-lg p-4 border border-gray-200 mb-4">
<p class="text-xs text-gray-700 leading-relaxed">
<strong>중요:</strong> Gitea 서버 = 개발 서버 (114.203.209.83)이다.
같은 서버에서 <strong>Gitea (포트 3000)</strong> <strong>Jenkins (포트 8080)</strong> 모두 운영한다.
git push를 하면 Gitea가 수신하고, Gitea가 Jenkins에 Webhook으로 알린다.
</p>
</div>
</div>
</div>
</section>
{{-- ============================================================ --}}
{{-- 3. Before vs After --}}
{{-- ============================================================ --}}
<section id="before-after" 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-600 text-white rounded-lg flex items-center justify-center text-sm font-bold">3</span>
Before vs After
</h2>
<div id="before" 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-amber-400 rounded-full"></span>
이전: 수동 배포
</h3>
<div class="bg-amber-50 rounded-lg p-4 border border-amber-100 mb-4">
<p class="font-semibold text-amber-800 mb-2">모든 단계를 사람이 직접 수행</p>
<p class="text-xs text-amber-900 leading-relaxed">
개발자가 git push를 , 팀장이 서버에 SSH로 접속하여
<code class="bg-amber-100 px-1 rounded">git pull</code>
<code class="bg-amber-100 px-1 rounded">composer install</code>
<code class="bg-amber-100 px-1 rounded">php artisan migrate</code>
<code class="bg-amber-100 px-1 rounded">config:clear</code> 순서대로 실행했다.
</p>
</div>
<div class="code-block mb-4"><span class="code-comment"># [이전] 수동 배포 절차</span>
<span class="code-comment"># 1. 개발자가 로컬에서 push</span>
<span class="code-keyword">git push</span> <span class="code-func">origin</span> <span class="code-string">develop</span>
<span class="code-comment"># 2. 팀장이 서버에 SSH 접속</span>
<span class="code-keyword">ssh</span> <span class="code-func">pro@114.203.209.83</span>
<span class="code-comment"># 3. 서버에서 수동 실행</span>
<span class="code-keyword">cd</span> <span class="code-string">/home/webservice/api</span>
<span class="code-keyword">git pull</span>
<span class="code-keyword">composer install</span>
<span class="code-keyword">php artisan</span> <span class="code-func">migrate</span>
<span class="code-keyword">php artisan</span> <span class="code-func">config:clear</span></div>
<div class="bg-red-50 rounded-lg p-4 border border-red-200 mb-4">
<p class="text-xs text-red-800 leading-relaxed">
<strong>문제점:</strong>
사람이 실수로 단계를 빼먹거나, 잘못된 서버에서 실행하거나,
팀장 부재 배포가 지연되는 문제가 있었다.
</p>
</div>
</div>
<div id="after" class="scroll-mt-20 mb-4">
<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>
현재: Jenkins CI/CD 자동 배포
</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">push 자동으로 모든 단계 수행</p>
<p class="text-xs text-blue-900 leading-relaxed">
개발자가 git push만 하면, Gitea가 Jenkins에 알리고,
Jenkins가 <strong>자동으로</strong> pull install migrate cache clear를 수행한다.
사람이 서버에 접속할 필요가 없다.
</p>
</div>
<div class="code-block mb-4"><span class="code-comment"># [현재] 개발자가 할 일은 이것뿐!</span>
<span class="code-keyword">git add</span> <span class="code-string">.</span>
<span class="code-keyword">git commit</span> <span class="code-attr">-m</span> <span class="code-string">"feat: [order] 주문 기능 추가"</span>
<span class="code-keyword">git push</span> <span class="code-func">origin</span> <span class="code-string">develop</span>
<span class="code-comment"># 이후는 Jenkins가 자동으로 처리:</span>
<span class="code-comment"># ① Gitea Webhook → Jenkins 트리거</span>
<span class="code-comment"># ② git pull (개발 서버)</span>
<span class="code-comment"># ③ composer install</span>
<span class="code-comment"># ④ php artisan migrate</span>
<span class="code-comment"># ⑤ php artisan config:clear && cache:clear</span>
<span class="code-comment"># ⑥ 결과 보고 (Gitea PR 코멘트)</span></div>
<div class="bg-green-50 rounded-lg p-4 border border-green-200 mb-4">
<p class="text-xs text-green-800 leading-relaxed">
<strong>장점:</strong>
실수 방지, 배포 속도 향상, 팀장 부재에도 배포 가능,
빌드 실패 자동으로 알림을 받을 있다.
</p>
</div>
</div>
</div>
</section>
{{-- ============================================================ --}}
{{-- 4. 배포 파이프라인 --}}
{{-- ============================================================ --}}
<section id="pipeline" 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-600 text-white rounded-lg flex items-center justify-center text-sm font-bold">4</span>
배포 파이프라인
</h2>
<div id="webhook-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>
Webhook 전체 흐름
<span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon">Webhook은 이벤트 발생 자동으로 HTTP 요청을 보내는 메커니즘이다. Gitea에서 push 이벤트가 발생하면 Jenkins의 특정 URL로 알림을 보낸다.</span></span>
</h3>
{{-- 파이프라인 시각화 --}}
<div class="mb-6">
<div class="grid grid-cols-1 gap-2">
<div class="flow-step bg-slate-100 border border-slate-300">
<div class="flex items-center gap-3 justify-center">
<svg class="w-5 h-5 text-slate-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" /></svg>
<span class="font-bold text-slate-700">로컬 PC (WSL)</span>
<span class="text-slate-500"> git push origin develop</span>
</div>
</div>
<div class="flow-arrow">
<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="M19 14l-7 7m0 0l-7-7m7 7V3" /></svg>
</div>
<div class="flow-step bg-orange-50 border border-orange-300">
<div class="flex items-center gap-3 justify-center">
<svg class="w-5 h-5 text-orange-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2" /></svg>
<span class="font-bold text-orange-700">Gitea (114.203.209.83:3000)</span>
<span class="text-orange-500"> push 수신 Webhook 발동</span>
</div>
</div>
<div class="flow-arrow">
<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="M19 14l-7 7m0 0l-7-7m7 7V3" /></svg>
</div>
<div class="flow-step bg-blue-50 border border-blue-300">
<div class="flex items-center gap-3 justify-center">
<svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.066 2.573c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.573 1.066c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.066-2.573c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" /><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /></svg>
<span class="font-bold text-blue-700">Jenkins (114.203.209.83:8080)</span>
<span class="text-blue-500"> 빌드 파이프라인 실행</span>
</div>
</div>
<div class="flow-arrow">
<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="M19 14l-7 7m0 0l-7-7m7 7V3" /></svg>
</div>
<div class="flow-step bg-green-50 border border-green-300">
<div class="flex items-center gap-3 justify-center">
<svg class="w-5 h-5 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" /></svg>
<span class="font-bold text-green-700">서버에 코드 반영 완료</span>
<span class="text-green-500"> git pull + 의존성 + 마이그레이션 + 캐시</span>
</div>
</div>
</div>
</div>
</div>
<div id="jenkins-steps" class="scroll-mt-20 mb-4">
<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>
Jenkins가 수행하는 단계
</h3>
<div class="space-y-2 text-xs mb-4">
<div class="flex items-start gap-3 bg-gray-50 rounded-lg p-3 border">
<span class="bg-blue-600 text-white rounded-full w-6 h-6 flex items-center justify-center text-xs shrink-0 mt-0.5">1</span>
<div>
<p class="font-bold text-gray-800">Checkout</p>
<p class="text-gray-600">Gitea에서 최신 코드를 가져온다 (git pull)</p>
</div>
</div>
<div class="flex items-start gap-3 bg-gray-50 rounded-lg p-3 border">
<span class="bg-blue-600 text-white rounded-full w-6 h-6 flex items-center justify-center text-xs shrink-0 mt-0.5">2</span>
<div>
<p class="font-bold text-gray-800">Install</p>
<p class="text-gray-600"><code class="bg-gray-200 px-1 rounded">composer install</code> PHP 의존성 동기화</p>
</div>
</div>
<div class="flex items-start gap-3 bg-gray-50 rounded-lg p-3 border">
<span class="bg-blue-600 text-white rounded-full w-6 h-6 flex items-center justify-center text-xs shrink-0 mt-0.5">3</span>
<div>
<p class="font-bold text-gray-800">Migrate</p>
<p class="text-gray-600"><code class="bg-gray-200 px-1 rounded">php artisan migrate</code> DB 스키마 변경 적용 (API만)</p>
</div>
</div>
<div class="flex items-start gap-3 bg-gray-50 rounded-lg p-3 border">
<span class="bg-blue-600 text-white rounded-full w-6 h-6 flex items-center justify-center text-xs shrink-0 mt-0.5">4</span>
<div>
<p class="font-bold text-gray-800">Cache Clear</p>
<p class="text-gray-600"><code class="bg-gray-200 px-1 rounded">php artisan config:clear && cache:clear</code> 캐시된 설정 갱신</p>
</div>
</div>
<div class="flex items-start gap-3 bg-gray-50 rounded-lg p-3 border">
<span class="bg-blue-600 text-white rounded-full w-6 h-6 flex items-center justify-center text-xs shrink-0 mt-0.5">5</span>
<div>
<p class="font-bold text-gray-800">Queue 재시작</p>
<p class="text-gray-600"><code class="bg-gray-200 px-1 rounded">supervisorctl restart</code> 워커 재시작 (변경사항 반영)</p>
</div>
</div>
<div class="flex items-start gap-3 bg-green-50 rounded-lg p-3 border border-green-200">
<span class="bg-green-600 text-white rounded-full w-6 h-6 flex items-center justify-center text-xs shrink-0 mt-0.5">6</span>
<div>
<p class="font-bold text-green-800">보고</p>
<p class="text-green-700">빌드 결과를 Gitea에 보고 (성공/실패 표시)</p>
</div>
</div>
</div>
<div class="bg-amber-50 rounded-lg p-4 border border-amber-200 mb-4">
<p class="text-xs text-amber-800 leading-relaxed">
<strong>React (Next.js) 배포는 다르다:</strong>
React는 빌드가 필요하므로 Jenkins에서 <code class="bg-amber-100 px-1 rounded">npm install npm run build</code>
결과물을 <code class="bg-amber-100 px-1 rounded">tar.gz</code> 묶어 서버에 전송 압축 해제 Node.js 재시작 순서로 배포한다.
서버에서 직접 빌드하지 않는다 (메모리 부족 방지).
</p>
</div>
</div>
</div>
</section>
{{-- ============================================================ --}}
{{-- 5. 브랜치 전략 --}}
{{-- ============================================================ --}}
<section id="branch-strategy" 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-600 text-white rounded-lg flex items-center justify-center text-sm font-bold">5</span>
브랜치 전략
</h2>
<div id="develop-branch" 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-green-400 rounded-full"></span>
develop 브랜치 개발 서버
<span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon">develop 브랜치는 일상적인 개발 작업이 이루어지는 브랜치다. push하면 개발 서버(dev.codebridge-x.com) 자동으로 반영된다.</span></span>
</h3>
<div class="bg-green-50 rounded-lg p-5 border border-green-100 mb-4">
<p class="text-sm text-green-900 leading-relaxed">
<strong>일상 개발 작업</strong> 모두 <code class="bg-green-100 px-1 rounded font-mono">develop</code> 브랜치에서 진행한다.
push Jenkins가 <strong>자동으로 개발 서버에 배포</strong>한다.
별도의 승인이 필요 없다.
</p>
</div>
<div class="code-block mb-4"><span class="code-comment"># 일상 개발 — develop 브랜치 (자동 배포)</span>
<span class="code-keyword">git checkout</span> <span class="code-string">develop</span>
<span class="code-comment"># ... 작업 ...</span>
<span class="code-keyword">git add</span> <span class="code-string">.</span>
<span class="code-keyword">git commit</span> <span class="code-attr">-m</span> <span class="code-string">"feat: [order] 주문 기능 추가"</span>
<span class="code-keyword">git push</span> <span class="code-func">origin</span> <span class="code-string">develop</span>
<span class="code-comment"># → 자동으로 dev.codebridge-x.com에 반영됨</span></div>
</div>
<div id="main-branch" 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-red-400 rounded-full"></span>
main/master 브랜치 운영 서버
<span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon">main/master 브랜치는 실제 서비스에 배포되는 안정 코드다. PR(Pull Request) 리뷰를 거쳐 팀장이 승인한 후에만 머지된다.</span></span>
</h3>
<div class="bg-red-50 rounded-lg p-5 border border-red-100 mb-4">
<p class="text-sm text-red-900 leading-relaxed">
<strong>운영 배포</strong> <code class="bg-red-100 px-1 rounded font-mono">main</code> (또는 <code class="bg-red-100 px-1 rounded font-mono">master</code>) 브랜치에 PR을 머지할 실행된다.
반드시 <strong>팀장 승인</strong> 필요하다.
</p>
</div>
<div class="code-block mb-4"><span class="code-comment"># 운영 배포 — PR 머지 방식 (팀장 승인 필수)</span>
<span class="code-comment"># 1. Gitea에서 develop → main PR 생성</span>
<span class="code-comment"># 2. 팀장이 코드 리뷰 후 승인</span>
<span class="code-comment"># 3. PR 머지</span>
<span class="code-comment"># 4. Jenkins가 자동으로 운영 서버에 배포</span>
<span class="code-comment"># 운영 서버 배포 시 추가 명령:</span>
<span class="code-comment"># composer install --no-dev --optimize-autoloader</span>
<span class="code-comment"># php artisan migrate --force</span>
<span class="code-comment"># php artisan config:cache && route:cache && view:cache</span></div>
</div>
{{-- 브랜치 흐름도 --}}
<div class="mb-4">
<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="overflow-x-auto mb-4">
<table class="w-full text-sm border-collapse">
<thead>
<tr class="border-b-2 border-gray-200 bg-gray-50">
<th class="text-left py-2 px-3 font-medium text-gray-600">브랜치</th>
<th class="text-left py-2 px-3 font-medium text-gray-600">배포 대상</th>
<th class="text-left py-2 px-3 font-medium text-gray-600">트리거</th>
<th class="text-left py-2 px-3 font-medium text-gray-600">승인</th>
</tr>
</thead>
<tbody class="text-xs text-gray-700">
<tr class="border-b border-gray-100">
<td class="py-2 px-3"><span class="font-mono font-bold text-blue-600">feature/*</span></td>
<td class="py-2 px-3 text-gray-400">배포 </td>
<td class="py-2 px-3 text-gray-400"></td>
<td class="py-2 px-3 text-gray-400"></td>
</tr>
<tr class="border-b border-gray-100">
<td class="py-2 px-3"><span class="font-mono font-bold text-green-600">develop</span></td>
<td class="py-2 px-3">개발 서버 (<code class="bg-gray-100 px-1 rounded">dev.codebridge-x.com</code>)</td>
<td class="py-2 px-3">Push 자동</td>
<td class="py-2 px-3"><span class="text-green-600 font-bold">불필요</span></td>
</tr>
<tr>
<td class="py-2 px-3"><span class="font-mono font-bold text-red-600">main/master</span></td>
<td class="py-2 px-3">운영 서버 (<code class="bg-gray-100 px-1 rounded">codebridge-x.com</code>)</td>
<td class="py-2 px-3">PR 머지 </td>
<td class="py-2 px-3"><span class="text-red-600 font-bold">팀장 승인 필수</span></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</section>
{{-- ============================================================ --}}
{{-- 6. 도메인 매핑 --}}
{{-- ============================================================ --}}
<section id="domain-mapping" 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-600 text-white rounded-lg flex items-center justify-center text-sm font-bold">6</span>
도메인 매핑
</h2>
<div class="overflow-x-auto mb-4">
<table class="w-full text-sm border-collapse">
<thead>
<tr class="border-b-2 border-gray-200 bg-gray-50">
<th class="text-left py-2 px-3 font-medium text-gray-600">서비스</th>
<th class="text-left py-2 px-3 font-medium text-gray-600">로컬 (Docker)</th>
<th class="text-left py-2 px-3 font-medium text-gray-600">개발 서버</th>
<th class="text-left py-2 px-3 font-medium text-gray-600">운영 서버</th>
</tr>
</thead>
<tbody class="text-xs text-gray-700">
<tr class="border-b border-gray-100">
<td class="py-2 px-3 font-bold text-emerald-600">React (사용자)</td>
<td class="py-2 px-3 font-mono">dev.sam.kr</td>
<td class="py-2 px-3 font-mono">dev.codebridge-x.com</td>
<td class="py-2 px-3 font-mono">codebridge-x.com</td>
</tr>
<tr class="border-b border-gray-100">
<td class="py-2 px-3 font-bold text-purple-600">API</td>
<td class="py-2 px-3 font-mono">api.sam.kr</td>
<td class="py-2 px-3 font-mono">api.dev.codebridge-x.com</td>
<td class="py-2 px-3 font-mono">api.codebridge-x.com</td>
</tr>
<tr class="border-b border-gray-100">
<td class="py-2 px-3 font-bold text-blue-600">MNG (관리자)</td>
<td class="py-2 px-3 font-mono">mng.sam.kr</td>
<td class="py-2 px-3 font-mono">admin.codebridge-x.com</td>
<td class="py-2 px-3 font-mono">mng.codebridge-x.com</td>
</tr>
<tr class="border-b border-gray-100">
<td class="py-2 px-3 font-bold text-orange-600">Sales</td>
<td class="py-2 px-3 font-mono">sales.sam.kr</td>
<td class="py-2 px-3 font-mono">sales.dev.codebridge-x.com</td>
<td class="py-2 px-3 font-mono">sales.codebridge-x.com</td>
</tr>
<tr>
<td class="py-2 px-3 font-bold text-gray-600">5130 (레거시)</td>
<td class="py-2 px-3 font-mono">5130.sam.kr</td>
<td class="py-2 px-3 text-gray-400"></td>
<td class="py-2 px-3 text-gray-400"></td>
</tr>
</tbody>
</table>
</div>
<div class="bg-blue-50 rounded-lg p-4 border border-blue-100 mb-4">
<p class="text-xs text-blue-800 leading-relaxed">
<strong>로컬 도메인:</strong> <code class="bg-blue-100 px-1 rounded">*.sam.kr</code> 로컬 개발 전용이며, <code class="bg-blue-100 px-1 rounded">/etc/hosts</code> 등록되어 있다.
실제 인터넷에서 접근할 없다.
</p>
</div>
</div>
</section>
{{-- ============================================================ --}}
{{-- 7. FAQ --}}
{{-- ============================================================ --}}
<section id="faq" 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-600 text-white rounded-lg flex items-center justify-center text-sm font-bold">7</span>
FAQ
</h2>
<div class="space-y-4">
<div class="bg-gray-50 rounded-lg p-4 border">
<p class="font-bold text-gray-800 text-sm mb-2">Q. git origin URL이 바뀌었나요?</p>
<p class="text-xs text-gray-700 leading-relaxed">
<strong>아니요.</strong> origin URL은 전혀 변경되지 않았습니다.
<code class="bg-gray-200 px-1 rounded">git remote -v</code> 확인하면 동일한 Gitea 주소가 나옵니다.
</p>
</div>
<div class="bg-gray-50 rounded-lg p-4 border">
<p class="font-bold text-gray-800 text-sm mb-2">Q. push 서버 반영이 되면?</p>
<p class="text-xs text-gray-700 leading-relaxed">
Jenkins 빌드가 실패했을 있습니다. <code class="bg-gray-200 px-1 rounded">http://114.203.209.83:8080</code>에서 빌드 결과를 확인하세요.
실패 원인은 대부분 코드 오류(syntax error)이거나 마이그레이션 충돌입니다.
</p>
</div>
<div class="bg-gray-50 rounded-lg p-4 border">
<p class="font-bold text-gray-800 text-sm mb-2">Q. Jenkins 장애 수동 배포는?</p>
<p class="text-xs text-gray-700 leading-relaxed">
<strong>비상 절차</strong>로만 사용합니다. 팀장이 서버에 SSH로 접속하여 수동으로
<code class="bg-gray-200 px-1 rounded">git pull composer install migrate config:clear</code> 실행합니다.
</p>
</div>
<div class="bg-gray-50 rounded-lg p-4 border">
<p class="font-bold text-gray-800 text-sm mb-2">Q. 운영 서버에 바로 push할 있나요?</p>
<p class="text-xs text-gray-700 leading-relaxed">
<strong>아니요.</strong> 운영 서버 배포는 반드시 <code class="bg-gray-200 px-1 rounded">develop main/master</code> PR을 통해서만 가능합니다.
팀장이 코드를 리뷰하고 승인한 후에만 머지됩니다.
</p>
</div>
<div class="bg-gray-50 rounded-lg p-4 border">
<p class="font-bold text-gray-800 text-sm mb-2">Q. feature 브랜치는 어떻게 배포하나요?</p>
<p class="text-xs text-gray-700 leading-relaxed">
feature 브랜치는 자동 배포 대상이 아닙니다.
<code class="bg-gray-200 px-1 rounded">feature/*</code> <code class="bg-gray-200 px-1 rounded">develop</code>으로 머지한
develop을 push하면 개발 서버에 자동 배포됩니다.
</p>
</div>
</div>
</div>
</section>
</div>
</div>
</div>
<!-- 클릭 라이트박스 -->
<div id="lightbox" onclick="closeLightbox()">
<button onclick="closeLightbox()" style="position:absolute; top:1rem; right:1rem; background:none; border:none; cursor:pointer; color:rgba(255,255,255,0.8); padding:0.5rem;">
<svg style="width:2rem; height:2rem;" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /></svg>
</button>
<img id="lightbox-img" onclick="event.stopPropagation()">
</div>
<script>
(function() {
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();
}
});
// 도움말 풍선 토글
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');
});
}
});
})();
</script>
@endsection