Files
sam-manage/resources/views/academy/git-policy.blade.php
김보곤 d5a8911fd4 refactor: [academy] 모든 페이지에서 '백과사전' 단어 제거
- 타이틀, 히어로 제목에서 중복 표현 정리
- 8개 페이지 일괄 적용
2026-02-23 13:18:08 +09:00

1287 lines
87 KiB
PHP

@extends('layouts.app')
@section('title', 'Git 정책')
@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);
}
.academy-img-wrap {
overflow: hidden;
border-radius: 0.75rem;
}
#hover-preview {
display: none;
position: fixed;
inset: 0;
z-index: 45;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(3px);
pointer-events: none;
}
#hover-preview.is-active {
display: flex;
pointer-events: none;
}
#hover-preview img {
max-height: 80vh;
max-width: 85vw;
border-radius: 0.75rem;
box-shadow: 0 25px 60px rgba(0, 0, 0, 0.5);
opacity: 0;
transform: scale(0.3);
transition: opacity 0.25s ease, transform 0.25s ease;
}
#hover-preview.is-active img {
opacity: 1;
transform: scale(1);
}
#hover-preview .hover-caption {
position: absolute;
bottom: 2rem;
left: 50%;
transform: translateX(-50%);
color: rgba(255,255,255,0.85);
font-size: 0.8rem;
background: rgba(0,0,0,0.5);
padding: 0.4rem 1rem;
border-radius: 2rem;
white-space: nowrap;
opacity: 0;
transition: opacity 0.3s ease 0.15s;
}
#hover-preview.is-active .hover-caption {
opacity: 1;
}
#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: #f0fdfa;
color: #0f766e;
font-size: 11px;
font-weight: 700;
cursor: pointer;
border: 1.5px solid #5eead4;
vertical-align: middle;
margin-left: 4px;
transition: all 0.15s ease;
position: relative;
user-select: none;
}
.help-balloon-trigger:hover {
background: #0f766e;
color: white;
border-color: #0d9488;
}
.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;
}
.help-balloon-trigger.is-active .help-balloon.balloon-right {
left: auto;
right: -10px;
transform: scale(1);
}
.help-balloon-trigger.is-active .help-balloon.balloon-right::after {
left: auto;
right: 14px;
transform: none;
}
.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; }
</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, #042f2e 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: #5eead4;">
<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: #99f6e4;">Git 정책</span>
</div>
<h1 class="text-3xl font-bold mb-2" style="color: #ffffff;">Git 정책</h1>
<p class="text-sm" style="color: #cbd5e1;">버전 관리의 기초부터 SAM 프로젝트 Git 정책까지 비개발자도 이해하는 Git 가이드</p>
</div>
<div class="shrink-0" style="width: 240px; padding: 1.5rem;">
<div class="overflow-hidden rounded-xl">
<img src="{{ asset('images/academy/git-policy/1.svg') }}" alt="버전 관리의 필요성"
class="w-full rounded-xl cursor-pointer academy-img-hover"
onclick="openLightbox(this)">
</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-teal-50 border border-teal-200 rounded-xl p-5">
<h2 class="font-semibold text-teal-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-vcs" class="block text-teal-700 hover:text-teal-900 py-1 font-medium">1. 버전 관리란?</a>
<a href="#why-vcs" class="block text-teal-600 hover:text-teal-800 py-0.5 pl-3">파일 혼돈의 시대</a>
<a href="#git-solves" class="block text-teal-600 hover:text-teal-800 py-0.5 pl-3">Git이 해결하는 </a>
<a href="#git-basics" class="block text-teal-700 hover:text-teal-900 py-1 font-medium mt-2">2. Git 기초 개념</a>
<a href="#three-stages" class="block text-teal-600 hover:text-teal-800 py-0.5 pl-3">3단계 구조</a>
<a href="#commit-hash" class="block text-teal-600 hover:text-teal-800 py-0.5 pl-3">커밋과 해시</a>
<a href="#remote-repo" class="block text-teal-700 hover:text-teal-900 py-1 font-medium mt-2">3. 원격 저장소</a>
<a href="#push-pull" class="block text-teal-600 hover:text-teal-800 py-0.5 pl-3">Push / Pull</a>
<a href="#clone-origin" class="block text-teal-600 hover:text-teal-800 py-0.5 pl-3">Clone과 Origin</a>
<a href="#branching" class="block text-teal-700 hover:text-teal-900 py-1 font-medium mt-2">4. 브랜치</a>
<a href="#branch-types" class="block text-teal-600 hover:text-teal-800 py-0.5 pl-3">브랜치 종류</a>
<a href="#branch-merge" class="block text-teal-600 hover:text-teal-800 py-0.5 pl-3">머지</a>
<a href="#commit-messages" class="block text-teal-700 hover:text-teal-900 py-1 font-medium mt-2">5. 커밋 메시지</a>
<a href="#sam-convention" class="block text-teal-600 hover:text-teal-800 py-0.5 pl-3">SAM 컨벤션</a>
<a href="#commit-types" class="block text-teal-600 hover:text-teal-800 py-0.5 pl-3">7가지 타입</a>
<a href="#collaboration" class="block text-teal-700 hover:text-teal-900 py-1 font-medium mt-2">6. 협업 워크플로우</a>
<a href="#pr-review" class="block text-teal-600 hover:text-teal-800 py-0.5 pl-3">PR과 코드 리뷰</a>
<a href="#team-workflow" class="block text-teal-600 hover:text-teal-800 py-0.5 pl-3"> 워크플로우</a>
<a href="#conflict-resolution" class="block text-teal-700 hover:text-teal-900 py-1 font-medium mt-2">7. 충돌 해결</a>
<a href="#conflict-markers" class="block text-teal-600 hover:text-teal-800 py-0.5 pl-3">충돌 마커</a>
<a href="#resolve-steps" class="block text-teal-600 hover:text-teal-800 py-0.5 pl-3">해결 3단계</a>
<a href="#sam-git-policy" class="block text-teal-700 hover:text-teal-900 py-1 font-medium mt-2">8. SAM Git 정책</a>
<a href="#multi-repo" class="block text-teal-600 hover:text-teal-800 py-0.5 pl-3">3 저장소</a>
<a href="#push-policy" class="block text-teal-600 hover:text-teal-800 py-0.5 pl-3">푸시 정책</a>
<a href="#gitignore-security" class="block text-teal-700 hover:text-teal-900 py-1 font-medium mt-2">9. .gitignore와 보안</a>
<a href="#gitignore-rules" class="block text-teal-600 hover:text-teal-800 py-0.5 pl-3">.gitignore 규칙</a>
<a href="#env-security" class="block text-teal-600 hover:text-teal-800 py-0.5 pl-3">.env 보안</a>
<a href="#cicd-deployment" class="block text-teal-700 hover:text-teal-900 py-1 font-medium mt-2">10. 배포와 CI/CD</a>
<a href="#git-deploy" class="block text-teal-600 hover:text-teal-800 py-0.5 pl-3">Git 기반 배포</a>
<a href="#rollback" class="block text-teal-600 hover:text-teal-800 py-0.5 pl-3">롤백</a>
</div>
</div>
</div>
</nav>
{{-- ============================================================ --}}
{{-- 우측 콘텐츠 --}}
{{-- ============================================================ --}}
<div class="flex-1 min-w-0 space-y-10">
{{-- 모바일 목차 --}}
<div class="lg:hidden bg-teal-50 border border-teal-200 rounded-xl p-4 mb-6">
<details>
<summary class="font-semibold text-teal-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-vcs" class="block text-teal-700 hover:text-teal-900 py-1">1. 버전 관리란?</a>
<a href="#git-basics" class="block text-teal-700 hover:text-teal-900 py-1">2. Git 기초 개념</a>
<a href="#remote-repo" class="block text-teal-700 hover:text-teal-900 py-1">3. 원격 저장소와 클론</a>
<a href="#branching" class="block text-teal-700 hover:text-teal-900 py-1">4. 브랜치</a>
<a href="#commit-messages" class="block text-teal-700 hover:text-teal-900 py-1">5. 커밋 메시지 작성법</a>
<a href="#collaboration" class="block text-teal-700 hover:text-teal-900 py-1">6. 협업 워크플로우</a>
<a href="#conflict-resolution" class="block text-teal-700 hover:text-teal-900 py-1">7. 충돌 해결</a>
<a href="#sam-git-policy" class="block text-teal-700 hover:text-teal-900 py-1">8. SAM Git 정책</a>
<a href="#gitignore-security" class="block text-teal-700 hover:text-teal-900 py-1">9. .gitignore와 보안</a>
<a href="#cicd-deployment" class="block text-teal-700 hover:text-teal-900 py-1">10. 배포와 CI/CD</a>
</nav>
</details>
</div>
{{-- ============================================================ --}}
{{-- 1. 버전 관리란? --}}
{{-- ============================================================ --}}
<section id="what-is-vcs" 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-teal-600 text-white rounded-lg flex items-center justify-center text-sm font-bold">1</span>
버전 관리란?
</h2>
<div id="why-vcs" 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-teal-400 rounded-full"></span>
파일 혼돈의 시대
<span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon">버전 관리 시스템(VCS) 없던 시절, 대부분의 팀은 파일명에 날짜나 버전 번호를 붙여 관리했다. 방식은 파일이 늘어날수록 어떤 것이 최신인지 없게 된다.</span></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">비유: 견적서_최종_진짜최종.xlsx의 악몽</p>
<p class="text-xs text-amber-900 leading-relaxed">
엑셀 파일을 수정할 때마다 <strong>"견적서_수정.xlsx"</strong>, <strong>"견적서_최종.xlsx"</strong>,
<strong>"견적서_최종_수정.xlsx"</strong>, <strong>"견적서_진짜최종_v3.xlsx"</strong>...
이렇게 파일이 불어난 경험이 있을 것이다. 어느 것이 진짜 최종인지 아무도 모른다.
<span class="glossary-term" data-tooltip="소스 코드 버전 관리 시스템">Git</span> 악몽을 완전히 해결한다.
</p>
</div>
<div class="bg-teal-50 rounded-lg p-5 border border-teal-100 mb-4">
<p class="text-sm text-teal-900 leading-relaxed">
<strong><span class="glossary-term" data-tooltip="파일의 변경 이력을 체계적으로 기록·관리하는 것">버전 관리</span></strong> 파일의 변경 이력을 체계적으로 기록하고 관리하는 것이다.
누가, 언제, 무엇을, 변경했는지를 자동으로 추적하며,
필요하면 과거의 어떤 시점으로든 되돌릴 있다.
</p>
</div>
</div>
<div id="git-solves" 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-teal-400 rounded-full"></span>
Git이 해결하는 3가지 문제
</h3>
<div class="grid grid-cols-1 md:grid-cols-3 gap-3 text-xs mb-4">
<div class="bg-teal-50 rounded-lg p-4 border border-teal-200 text-center">
<div class="mb-2">
<svg class="w-8 h-8 mx-auto text-teal-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
</div>
<p class="font-bold text-teal-800 mb-1">1. 이력 추적</p>
<p class="text-teal-700">모든 변경이 기록된다.<br>누가, 언제, 바꿨는지<br>언제든 확인 가능.</p>
</div>
<div class="bg-teal-50 rounded-lg p-4 border border-teal-200 text-center">
<div class="mb-2">
<svg class="w-8 h-8 mx-auto text-teal-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z" /></svg>
</div>
<p class="font-bold text-teal-800 mb-1">2. 동시 작업</p>
<p class="text-teal-700">여러 사람이 같은 파일을<br>동시에 수정해도<br>안전하게 합칠 있다.</p>
</div>
<div class="bg-teal-50 rounded-lg p-4 border border-teal-200 text-center">
<div class="mb-2">
<svg class="w-8 h-8 mx-auto text-teal-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" /></svg>
</div>
<p class="font-bold text-teal-800 mb-1">3. 되돌리기</p>
<p class="text-teal-700">실수해도 과거 시점으로<br>자유롭게 복원 가능.<br>파일이 깨질 걱정 없음.</p>
</div>
</div>
<div class="bg-teal-50 rounded-lg p-4 border border-teal-100 mb-4">
<p class="text-xs text-teal-800 leading-relaxed">
<strong>Git vs 클라우드 동기화:</strong>
Google Drive나 OneDrive도 파일 이력을 보여주지만, <span class="glossary-term" data-tooltip="소스 코드 버전 관리 시스템">Git</span>과는 근본적으로 다르다.
클라우드는 자동 저장이지만, Git은 <strong>의미 있는 단위로 직접 기록</strong>한다.
"견적 금액 10% 인상 반영"처럼 변경 이유까지 남길 있다.
</p>
</div>
<div class="mb-5 bg-gray-50 rounded-xl p-4 border flex justify-center academy-img-wrap">
<img src="{{ asset('images/academy/git-policy/1.svg') }}" alt="버전 관리의 필요성"
class="rounded-lg cursor-pointer academy-img-hover"
style="max-height: 320px; width: auto;"
onclick="openLightbox(this)">
</div>
<p class="text-xs text-gray-400 mb-4 text-center">버전 관리 전후 비교 파일명 혼돈 vs Git 타임라인</p>
</div>
</div>
</section>
{{-- ============================================================ --}}
{{-- 2. Git 기초 개념 --}}
{{-- ============================================================ --}}
<section id="git-basics" 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-teal-600 text-white rounded-lg flex items-center justify-center text-sm font-bold">2</span>
Git 기초 개념
</h2>
<div id="three-stages" 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-teal-400 rounded-full"></span>
Git의 3단계 구조
<span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon">Git은 파일을 3 영역으로 관리한다. Working Directory(작업 공간), Staging Area(준비 영역), Repository(저장소). 영역의 역할을 이해하면 Git의 핵심을 파악한 것이다.</span></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">
<strong>Working Directory</strong> 작업 중인 <strong>책상</strong>이다. 파일을 자유롭게 수정한다.
<strong><span class="glossary-term" data-tooltip="커밋할 파일을 미리 모아두는 중간 영역">Staging Area</span></strong> 보낼 서류를 담는 <strong>봉투</strong>. git add로 봉투에 넣는다.
<strong><span class="glossary-term" data-tooltip="Git이 파일과 변경 이력을 보관하는 공간">Repository</span></strong> 최종 보관하는 <strong>캐비닛</strong>이다. git commit으로 캐비닛에 저장한다.
</p>
</div>
<div class="code-block mb-4"><span class="code-comment"># 1. 책상에서 파일 수정 (Working Directory)</span>
<span class="code-func">파일을 수정한다</span>
<span class="code-comment"># 2. 봉투에 넣기 (Staging Area)</span>
<span class="code-keyword">git add</span> <span class="code-string">파일이름.php</span>
<span class="code-comment"># 3. 캐비닛에 보관 (Repository)</span>
<span class="code-keyword">git commit</span> <span class="code-attr">-m</span> <span class="code-string">"feat: [order] 주문 기능 추가"</span></div>
<div class="mb-5 bg-gray-50 rounded-xl p-4 border flex justify-center academy-img-wrap">
<img src="{{ asset('images/academy/git-policy/2.svg') }}" alt="Git 3단계 구조"
class="rounded-lg cursor-pointer academy-img-hover"
style="max-height: 320px; width: auto;"
onclick="openLightbox(this)">
</div>
<p class="text-xs text-gray-400 mb-4 text-center">Git 3단계 Working Directory Staging Repository</p>
</div>
<div id="commit-hash" 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-teal-400 rounded-full"></span>
커밋과 해시
</h3>
<div class="bg-teal-50 rounded-lg p-5 border border-teal-100 mb-4">
<p class="text-sm text-teal-900 leading-relaxed">
<strong><span class="glossary-term" data-tooltip="변경 사항을 저장소에 기록하는 행위">Commit</span></strong> 특정 시점의 스냅샷이다.
커밋에는 <strong><span class="glossary-term" data-tooltip="각 커밋에 부여되는 40자리 고유 식별자">커밋 해시</span></strong>라는 고유 ID가 자동 부여된다.
: <code class="text-xs bg-teal-100 px-1 rounded">a1b2c3d</code>.
해시로 어떤 커밋이든 정확히 찾아갈 있다.
</p>
</div>
<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>
</tr>
</thead>
<tbody class="text-xs text-gray-700">
<tr class="border-b border-gray-100">
<td class="py-2 px-3 font-mono font-bold text-teal-600">git status</td>
<td class="py-2 px-3">현재 상태 확인</td>
<td class="py-2 px-3">책상 정리 상태 확인</td>
</tr>
<tr class="border-b border-gray-100">
<td class="py-2 px-3 font-mono font-bold text-teal-600">git add</td>
<td class="py-2 px-3">스테이징에 추가</td>
<td class="py-2 px-3">서류를 봉투에 넣기</td>
</tr>
<tr class="border-b border-gray-100">
<td class="py-2 px-3 font-mono font-bold text-teal-600">git commit</td>
<td class="py-2 px-3">변경 기록 저장</td>
<td class="py-2 px-3">봉투를 캐비닛에 보관</td>
</tr>
<tr class="border-b border-gray-100">
<td class="py-2 px-3 font-mono font-bold text-teal-600">git log</td>
<td class="py-2 px-3">커밋 이력 조회</td>
<td class="py-2 px-3">캐비닛 보관 목록 열람</td>
</tr>
<tr>
<td class="py-2 px-3 font-mono font-bold text-teal-600">git diff</td>
<td class="py-2 px-3">변경 내용 비교</td>
<td class="py-2 px-3">수정 전후 대조</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</section>
{{-- ============================================================ --}}
{{-- 3. 원격 저장소와 클론 --}}
{{-- ============================================================ --}}
<section id="remote-repo" 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-teal-600 text-white rounded-lg flex items-center justify-center text-sm font-bold">3</span>
원격 저장소와 클론
</h2>
<div id="push-pull" 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-teal-400 rounded-full"></span>
Push와 Pull
<span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon">원격 저장소(Remote) 팀원 모두가 접근할 있는 중앙 서버에 있는 저장소다. GitHub, GitLab, Gitea 등이 원격 저장소 서비스를 제공한다.</span></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">
로컬 저장소가 <strong> 책상의 캐비닛</strong>이라면, 원격 저장소는 <strong> 공용 캐비닛</strong>이다.
<strong><span class="glossary-term" data-tooltip="로컬 커밋을 원격 저장소에 업로드하는 것">Push</span></strong> 서류를 공용 캐비닛에 올리는 것이고,
<strong><span class="glossary-term" data-tooltip="원격 저장소의 최신 변경 사항을 로컬로 가져오는 것">Pull</span></strong> 다른 사람이 올린 서류를 책상으로 가져오는 것이다.
</p>
</div>
<div class="code-block mb-4"><span class="code-comment"># 내 작업을 원격에 업로드</span>
<span class="code-keyword">git push</span> <span class="code-func">origin</span> <span class="code-string">develop</span>
<span class="code-comment"># 원격의 최신 변경을 가져오기</span>
<span class="code-keyword">git pull</span> <span class="code-func">origin</span> <span class="code-string">develop</span></div>
<div class="mb-5 bg-gray-50 rounded-xl p-4 border flex justify-center academy-img-wrap">
<img src="{{ asset('images/academy/git-policy/3.svg') }}" alt="원격 저장소"
class="rounded-lg cursor-pointer academy-img-hover"
style="max-height: 320px; width: auto;"
onclick="openLightbox(this)">
</div>
<p class="text-xs text-gray-400 mb-4 text-center">원격 저장소 로컬 중앙 서버 동기화</p>
</div>
<div id="clone-origin" 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-teal-400 rounded-full"></span>
Clone과 Origin
</h3>
<div class="bg-teal-50 rounded-lg p-5 border border-teal-100 mb-4">
<p class="text-sm text-teal-900 leading-relaxed">
<strong><span class="glossary-term" data-tooltip="원격 저장소의 전체 내용을 로컬에 복사하는 것">Clone</span></strong> 원격 저장소를 통째로 PC에 복사하는 것이다.
프로젝트에 처음 참여할 번만 실행한다.
Clone하면 원격 저장소에 자동으로 <strong><span class="glossary-term" data-tooltip="원격 저장소의 기본 이름">Origin</span></strong>이라는 이름이 붙는다.
</p>
</div>
<div class="code-block mb-4"><span class="code-comment"># 프로젝트 처음 참여 시 — 전체 복사</span>
<span class="code-keyword">git clone</span> <span class="code-string">https://gitea.example.com/sam/mng.git</span>
<span class="code-comment"># 이후에는 pull로 최신 변경만 동기화</span>
<span class="code-keyword">git pull</span></div>
<div class="bg-teal-50 rounded-lg p-4 border border-teal-100 mb-4">
<p class="text-xs text-teal-800 leading-relaxed">
<strong>SAM의 원격 저장소:</strong>
SAM은 자체 호스팅 <span class="glossary-term" data-tooltip="경량 자체 호스팅 Git 서비스">Gitea</span> 서버를 사용한다.
GitHub처럼 브라우저에서 코드를 보고, PR을 만들고, 이슈를 관리할 있다.
</p>
</div>
</div>
</div>
</section>
{{-- ============================================================ --}}
{{-- 4. 브랜치 --}}
{{-- ============================================================ --}}
<section id="branching" 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-teal-600 text-white rounded-lg flex items-center justify-center text-sm font-bold">4</span>
브랜치
</h2>
<div id="branch-types" 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-teal-400 rounded-full"></span>
브랜치란?
<span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon">브랜치(Branch) '가지'라는 뜻이다. 나무 줄기(main)에서 가지가 뻗어나가듯, 메인 코드에서 분기하여 독립적으로 작업한 다시 합친다.</span></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">
공장의 메인 생산라인(main)에서 <strong>시험 생산라인</strong> 따로 만들어 제품을 테스트하는 것이다.
테스트가 성공하면 메인 라인에 합류시키고(merge), 실패하면 시험 라인만 폐쇄하면 된다.
메인 라인은 영향 없이 계속 가동된다.
</p>
</div>
<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>
</tr>
</thead>
<tbody class="text-xs text-gray-700">
<tr class="border-b border-gray-100">
<td class="py-2 px-3 font-mono font-bold text-teal-600">main</td>
<td class="py-2 px-3">운영 서버에 배포된 안정 코드</td>
<td class="py-2 px-3">실제 서비스 중인 코드</td>
</tr>
<tr class="border-b border-gray-100">
<td class="py-2 px-3 font-mono font-bold text-teal-600"><span class="glossary-term" data-tooltip="개발 통합 브랜치">develop</span></td>
<td class="py-2 px-3">개발 통합 브랜치</td>
<td class="py-2 px-3">다음 릴리즈 준비 코드</td>
</tr>
<tr class="border-b border-gray-100">
<td class="py-2 px-3 font-mono font-bold text-blue-600"><span class="glossary-term" data-tooltip="새 기능을 개발하는 임시 브랜치">feature/*</span></td>
<td class="py-2 px-3"> 기능 개발</td>
<td class="py-2 px-3">feature/file-upload</td>
</tr>
<tr>
<td class="py-2 px-3 font-mono font-bold text-red-600"><span class="glossary-term" data-tooltip="운영 중 긴급 버그를 수정하는 브랜치">hotfix/*</span></td>
<td class="py-2 px-3">긴급 버그 수정</td>
<td class="py-2 px-3">hotfix/login-session-bug</td>
</tr>
</tbody>
</table>
</div>
<div class="mb-5 bg-gray-50 rounded-xl p-4 border flex justify-center academy-img-wrap">
<img src="{{ asset('images/academy/git-policy/4.svg') }}" alt="브랜치 전략"
class="rounded-lg cursor-pointer academy-img-hover"
style="max-height: 320px; width: auto;"
onclick="openLightbox(this)">
</div>
<p class="text-xs text-gray-400 mb-4 text-center">브랜치 전략 main / develop / feature / hotfix</p>
</div>
<div id="branch-merge" 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-teal-400 rounded-full"></span>
브랜치 만들기와 머지
</h3>
<div class="code-block mb-4"><span class="code-comment"># 새 기능 브랜치 만들기</span>
<span class="code-keyword">git checkout</span> <span class="code-attr">-b</span> <span class="code-string">feature/file-upload</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: [file] 파일 업로드 기능 추가"</span>
<span class="code-comment"># develop 브랜치로 돌아와서 머지</span>
<span class="code-keyword">git checkout</span> <span class="code-string">develop</span>
<span class="code-keyword">git merge</span> <span class="code-string">feature/file-upload</span></div>
<div class="bg-teal-50 rounded-lg p-4 border border-teal-100 mb-4">
<p class="text-xs text-teal-800 leading-relaxed">
<strong><span class="glossary-term" data-tooltip="두 브랜치의 변경 사항을 하나로 합치는 것">Merge</span></strong> 분기된 작업을 다시 하나로 합치는 것이다.
feature 브랜치에서 개발을 완료하면 develop에 merge하고,
develop이 안정화되면 main에 merge한다.
</p>
</div>
</div>
</div>
</section>
{{-- ============================================================ --}}
{{-- 5. 커밋 메시지 작성법 --}}
{{-- ============================================================ --}}
<section id="commit-messages" 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-teal-600 text-white rounded-lg flex items-center justify-center text-sm font-bold">5</span>
커밋 메시지 작성법
</h2>
<div id="sam-convention" 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-teal-400 rounded-full"></span>
SAM 커밋 메시지 컨벤션
<span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon">커밋 메시지 컨벤션이란 전체가 동일한 형식으로 커밋 메시지를 작성하는 약속이다. 통일된 형식은 이력 검색과 코드 리뷰를 빠르게 만든다.</span></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">
커밋 메시지는 <strong>공장의 작업 일지</strong>. "작업함"이라고 적으면 나중에 아무도 내용을 없다.
<strong>"셔터 A라인 모터 교체 완료 — 소음 문제 해결"</strong>처럼 구체적으로 적어야 한다.
</p>
</div>
<div class="bg-teal-50 rounded-lg p-5 border border-teal-100 mb-4">
<p class="text-sm text-teal-900 leading-relaxed mb-3">
<strong>SAM 프로젝트 커밋 메시지 형식:</strong>
</p>
<div class="code-block"><span class="code-keyword">type</span>: [<span class="code-attr">scope</span>] <span class="code-string">작업 내용</span>
<span class="code-comment">- 세부 항목 (선택)</span>
<span class="code-comment">- 세부 항목 2</span></div>
</div>
<div class="mb-5 bg-gray-50 rounded-xl p-4 border flex justify-center academy-img-wrap">
<img src="{{ asset('images/academy/git-policy/5.svg') }}" alt="커밋 메시지"
class="rounded-lg cursor-pointer academy-img-hover"
style="max-height: 320px; width: auto;"
onclick="openLightbox(this)">
</div>
<p class="text-xs text-gray-400 mb-4 text-center">좋은 커밋 메시지 vs 나쁜 커밋 메시지</p>
</div>
<div id="commit-types" 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-teal-400 rounded-full"></span>
7가지 커밋 타입
</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>
</tr>
</thead>
<tbody class="text-xs text-gray-700">
<tr class="border-b border-gray-100">
<td class="py-2 px-3 font-mono font-bold text-green-600">feat</td>
<td class="py-2 px-3">새로운 기능 추가</td>
<td class="py-2 px-3">feat: [file] 파일 업로드 기능 추가</td>
</tr>
<tr class="border-b border-gray-100">
<td class="py-2 px-3 font-mono font-bold text-red-600">fix</td>
<td class="py-2 px-3">버그 수정</td>
<td class="py-2 px-3">fix: [auth] 세션 만료 오류 수정</td>
</tr>
<tr class="border-b border-gray-100">
<td class="py-2 px-3 font-mono font-bold text-purple-600">refactor</td>
<td class="py-2 px-3">코드 구조 개선</td>
<td class="py-2 px-3">refactor: [user] 서비스 메서드 분리</td>
</tr>
<tr class="border-b border-gray-100">
<td class="py-2 px-3 font-mono font-bold text-blue-600">docs</td>
<td class="py-2 px-3">문서 변경</td>
<td class="py-2 px-3">docs: API 문서 업데이트</td>
</tr>
<tr class="border-b border-gray-100">
<td class="py-2 px-3 font-mono font-bold text-yellow-600">chore</td>
<td class="py-2 px-3">설정/빌드 변경</td>
<td class="py-2 px-3">chore: composer 패키지 업데이트</td>
</tr>
<tr class="border-b border-gray-100">
<td class="py-2 px-3 font-mono font-bold text-orange-600">style</td>
<td class="py-2 px-3">코드 스타일 수정</td>
<td class="py-2 px-3">style: <span class="glossary-term" data-tooltip="Laravel 공식 코드 포맷터">Pint</span> 포맷팅 적용</td>
</tr>
<tr>
<td class="py-2 px-3 font-mono font-bold text-cyan-600">test</td>
<td class="py-2 px-3">테스트 추가/수정</td>
<td class="py-2 px-3">test: Product API 테스트 추가</td>
</tr>
</tbody>
</table>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-3 text-xs mb-4">
<div class="bg-green-50 rounded-lg p-3 border border-green-200">
<p class="font-bold text-green-800 mb-1">fix = 깨진 것을 고친다</p>
<p class="text-green-700">버그, 오류, 잘못된 결과</p>
</div>
<div class="bg-blue-50 rounded-lg p-3 border border-blue-200">
<p class="font-bold text-blue-800 mb-1">feat = 없던 것을 만든다</p>
<p class="text-blue-700">신규 기능, 기능 확장</p>
</div>
<div class="bg-purple-50 rounded-lg p-3 border border-purple-200">
<p class="font-bold text-purple-800 mb-1">refactor = 같은 것을 좋게</p>
<p class="text-purple-700">구조 개선, 중복 제거</p>
</div>
</div>
<div class="bg-teal-50 rounded-lg p-4 border border-teal-100 mb-4">
<p class="text-xs text-teal-800 leading-relaxed">
<strong>커밋 빈도:</strong>
"논리적 단위" 커밋한다. 파일 하나 수정마다 커밋하는 것은 너무 잦고,
시간 작업을 번에 커밋하면 되돌리기 어렵다.
하나의 의미 있는 변경이 완성되면 커밋한다.
</p>
</div>
</div>
</div>
</section>
{{-- ============================================================ --}}
{{-- 6. 협업 워크플로우 --}}
{{-- ============================================================ --}}
<section id="collaboration" 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-teal-600 text-white rounded-lg flex items-center justify-center text-sm font-bold">6</span>
협업 워크플로우
</h2>
<div id="pr-review" 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-teal-400 rounded-full"></span>
Pull Request와 코드 리뷰
<span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon">Pull Request(PR) 내가 만든 변경 사항을 브랜치에 합쳐달라고 요청하는 것이다. 다른 개발자가 코드를 검토(리뷰) 승인하면 머지된다.</span></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">
<span class="glossary-term" data-tooltip="코드 변경을 본 브랜치에 합쳐달라는 요청">Pull Request</span> <strong>품질검사 신청서</strong>.
생산라인에서 만든 제품을 바로 출하하지 않고, 품질검사 부서에 "검사해주세요"라고 신청한다.
검사관(리뷰어) 확인하고 "합격" 주면 출하(merge)된다.
</p>
</div>
<div class="mb-5 bg-gray-50 rounded-xl p-4 border flex justify-center academy-img-wrap">
<img src="{{ asset('images/academy/git-policy/6.svg') }}" alt="PR 워크플로우"
class="rounded-lg cursor-pointer academy-img-hover"
style="max-height: 320px; width: auto;"
onclick="openLightbox(this)">
</div>
<p class="text-xs text-gray-400 mb-4 text-center">PR 워크플로우 브랜치 커밋 PR 리뷰 머지</p>
</div>
<div id="team-workflow" 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-teal-400 rounded-full"></span>
워크플로우
</h3>
<div class="bg-teal-50 rounded-lg p-5 border border-teal-100 mb-4">
<p class="text-sm text-teal-900 leading-relaxed mb-3">
<strong>SAM 프로젝트의 일반적인 작업 흐름:</strong>
</p>
<div class="space-y-2 text-xs text-teal-800">
<div class="flex items-start gap-2">
<span class="bg-teal-600 text-white rounded-full w-5 h-5 flex items-center justify-center text-xs shrink-0">1</span>
<span>develop에서 feature 브랜치를 만든다</span>
</div>
<div class="flex items-start gap-2">
<span class="bg-teal-600 text-white rounded-full w-5 h-5 flex items-center justify-center text-xs shrink-0">2</span>
<span>기능을 개발하고 커밋한다</span>
</div>
<div class="flex items-start gap-2">
<span class="bg-teal-600 text-white rounded-full w-5 h-5 flex items-center justify-center text-xs shrink-0">3</span>
<span>Pull Request를 생성하여 코드 리뷰를 요청한다</span>
</div>
<div class="flex items-start gap-2">
<span class="bg-teal-600 text-white rounded-full w-5 h-5 flex items-center justify-center text-xs shrink-0">4</span>
<span>리뷰 승인 develop에 머지한다</span>
</div>
<div class="flex items-start gap-2">
<span class="bg-teal-600 text-white rounded-full w-5 h-5 flex items-center justify-center text-xs shrink-0">5</span>
<span>develop이 안정되면 main에 머지하고 배포한다</span>
</div>
</div>
</div>
<div class="code-block mb-4"><span class="code-comment"># 1. 브랜치 생성</span>
<span class="code-keyword">git checkout</span> <span class="code-attr">-b</span> <span class="code-string">feature/file-storage-system</span>
<span class="code-comment"># 2. 작업 및 커밋</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: [file] 파일 저장 시스템 구현"</span>
<span class="code-comment"># 3. 원격에 푸시 후 PR 생성</span>
<span class="code-keyword">git push</span> <span class="code-func">origin</span> <span class="code-string">feature/file-storage-system</span>
<span class="code-comment"># → Gitea에서 PR 생성</span></div>
</div>
</div>
</section>
{{-- ============================================================ --}}
{{-- 7. 충돌 해결 --}}
{{-- ============================================================ --}}
<section id="conflict-resolution" 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-teal-600 text-white rounded-lg flex items-center justify-center text-sm font-bold">7</span>
충돌 해결
</h2>
<div id="conflict-markers" 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-teal-400 rounded-full"></span>
충돌이란?
<span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon">충돌(Conflict) 사람이 같은 파일의 같은 줄을 다르게 수정했을 발생한다. Git이 어떤 변경을 채택해야 할지 모르기 때문에 사람이 직접 선택해야 한다.</span></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">
A씨와 B씨가 같은 엑셀 파일의 <strong>같은 </strong> 동시에 다른 값으로 수정한 상황이다.
A씨는 "100,000"으로, B씨는 "150,000"으로 바꿨다. 누구 것을 쓸지는 사람이 판단해야 한다.
</p>
</div>
<div class="code-block mb-4"><span class="code-comment">// 충돌 마커 Git이 파일에 표시해주는 형태</span>
<span class="code-tag">&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD</span>
<span class="code-string">$price = 100000;</span> <span class="code-comment">// 내가 수정한 내용</span>
<span class="code-attr">=======</span>
<span class="code-keyword">$price = 150000;</span> <span class="code-comment">// 상대방이 수정한 내용</span>
<span class="code-tag">&gt;&gt;&gt;&gt;&gt;&gt;&gt; feature/price-update</span></div>
<div class="mb-5 bg-gray-50 rounded-xl p-4 border flex justify-center academy-img-wrap">
<img src="{{ asset('images/academy/git-policy/7.svg') }}" alt="충돌 해결"
class="rounded-lg cursor-pointer academy-img-hover"
style="max-height: 320px; width: auto;"
onclick="openLightbox(this)">
</div>
<p class="text-xs text-gray-400 mb-4 text-center">머지 충돌 브랜치가 같은 부분을 수정했을 </p>
</div>
<div id="resolve-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-teal-400 rounded-full"></span>
충돌 해결 3단계
</h3>
<div class="space-y-3 mb-4">
<div class="bg-teal-50 rounded-lg p-4 border border-teal-200">
<p class="font-bold text-teal-800 mb-1 text-sm">1단계: 충돌 파일 확인</p>
<p class="text-xs text-teal-700"><code class="bg-teal-100 px-1 rounded"><span class="glossary-term" data-tooltip="현재 작업 디렉토리의 상태를 보여주는 명령어">git status</span></code> "both modified" 표시된 파일을 찾는다.</p>
</div>
<div class="bg-teal-50 rounded-lg p-4 border border-teal-200">
<p class="font-bold text-teal-800 mb-1 text-sm">2단계: 내용 선택</p>
<p class="text-xs text-teal-700">에디터에서 <code class="bg-teal-100 px-1 rounded">&lt;&lt;&lt;&lt;&lt;&lt;&lt;</code>, <code class="bg-teal-100 px-1 rounded">=======</code>, <code class="bg-teal-100 px-1 rounded">&gt;&gt;&gt;&gt;&gt;&gt;&gt;</code> 마커를 찾고 원하는 내용만 남긴다.</p>
</div>
<div class="bg-teal-50 rounded-lg p-4 border border-teal-200">
<p class="font-bold text-teal-800 mb-1 text-sm">3단계: 커밋</p>
<p class="text-xs text-teal-700"><code class="bg-teal-100 px-1 rounded">git add</code> 해결된 파일을 추가하고 <code class="bg-teal-100 px-1 rounded">git commit</code>으로 머지를 완료한다.</p>
</div>
</div>
</div>
</div>
</section>
{{-- ============================================================ --}}
{{-- 8. SAM 프로젝트 Git 정책 --}}
{{-- ============================================================ --}}
<section id="sam-git-policy" 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-teal-600 text-white rounded-lg flex items-center justify-center text-sm font-bold">8</span>
SAM 프로젝트 Git 정책
</h2>
<div id="multi-repo" 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-teal-400 rounded-full"></span>
3 독립 저장소
<span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon">SAM은 멀티레포 전략을 사용한다. 하나의 거대한 저장소(모노레포) 대신, 역할별로 분리된 3 저장소를 운영하여 팀이 독립적으로 작업할 있다.</span></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">비유: 공장 3 부서</p>
<p class="text-xs text-amber-900 leading-relaxed">
SAM의 3 저장소는 <strong>공장의 3 독립 부서</strong> 같다.
<strong>MNG(관리부)</strong> 관리자 화면, <strong>API(생산부)</strong> 데이터 처리,
<strong>React(영업부)</strong> 고객용 화면을 담당한다.
부서는 독립적으로 작업하지만, 하나의 공장(DB) 공유한다.
</p>
</div>
<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>
</tr>
</thead>
<tbody class="text-xs text-gray-700">
<tr class="border-b border-gray-100">
<td class="py-2 px-3 font-mono font-bold text-teal-600">mng</td>
<td class="py-2 px-3">/home/aweso/sam/mng</td>
<td class="py-2 px-3">관리자 (Laravel + Blade)</td>
</tr>
<tr class="border-b border-gray-100">
<td class="py-2 px-3 font-mono font-bold text-blue-600">api</td>
<td class="py-2 px-3">/home/aweso/sam/api</td>
<td class="py-2 px-3">API 서버 (Laravel)</td>
</tr>
<tr>
<td class="py-2 px-3 font-mono font-bold text-purple-600">react</td>
<td class="py-2 px-3">/home/aweso/sam/react</td>
<td class="py-2 px-3">프론트엔드 (Next.js)</td>
</tr>
</tbody>
</table>
</div>
<div class="mb-5 bg-gray-50 rounded-xl p-4 border flex justify-center academy-img-wrap">
<img src="{{ asset('images/academy/git-policy/8.svg') }}" alt="SAM 멀티레포"
class="rounded-lg cursor-pointer academy-img-hover"
style="max-height: 320px; width: auto;"
onclick="openLightbox(this)">
</div>
<p class="text-xs text-gray-400 mb-4 text-center">SAM <span class="glossary-term" data-tooltip="하나의 프로젝트를 여러 독립 저장소로 나누어 관리하는 전략">멀티레포</span> 구조 mng / api / react</p>
<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 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">cd</span> <span class="code-string">/home/aweso/sam/api</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] 주문 API 추가"</span></div>
</div>
<div id="push-policy" 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-teal-400 rounded-full"></span>
푸시 정책과 Pre-commit
</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-3 text-xs mb-4">
<div class="bg-teal-50 rounded-lg p-4 border border-teal-200">
<p class="font-bold text-teal-800 mb-2">푸시 정책</p>
<ul class="text-teal-700 space-y-1">
<li>- main/master에서 직접 작업 금지</li>
<li>- develop 브랜치를 기본으로 사용</li>
<li>- 사용자(팀장) 수동으로 push</li>
<li>- Claude는 커밋까지만 수행</li>
</ul>
</div>
<div class="bg-teal-50 rounded-lg p-4 border border-teal-200">
<p class="font-bold text-teal-800 mb-2"><span class="glossary-term" data-tooltip="커밋 실행 직전에 자동으로 실행되는 스크립트">Pre-commit Hook</span></p>
<ul class="text-teal-700 space-y-1">
<li>- 커밋 <span class="glossary-term" data-tooltip="Laravel 공식 코드 포맷터">Pint</span> 자동 실행</li>
<li>- 코드 포맷팅 자동 정리</li>
<li>- Claude 서명 자동 제거</li>
<li>- 포맷 오류 커밋 차단</li>
</ul>
</div>
</div>
<div class="bg-teal-50 rounded-lg p-4 border border-teal-100 mb-4">
<p class="text-xs text-teal-800 leading-relaxed">
<strong>커밋 체크리스트:</strong>
<code class="bg-teal-100 px-1 rounded">./vendor/bin/pint</code> 실행
<code class="bg-teal-100 px-1 rounded"><span class="glossary-term" data-tooltip="파일의 변경 내용을 줄 단위로 비교하는 명령어">git diff</span></code> 변경 검토
불필요 파일(.env ) 제외
<code class="bg-teal-100 px-1 rounded">git add</code>
<code class="bg-teal-100 px-1 rounded">git commit</code>
</p>
</div>
</div>
</div>
</section>
{{-- ============================================================ --}}
{{-- 9. .gitignore와 보안 --}}
{{-- ============================================================ --}}
<section id="gitignore-security" 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-teal-600 text-white rounded-lg flex items-center justify-center text-sm font-bold">9</span>
.gitignore와 보안
</h2>
<div id="gitignore-rules" 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-teal-400 rounded-full"></span>
.gitignore란?
<span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon">.gitignore는 Git이 추적하지 않을 파일 목록이다. 여기에 등록된 파일은 git add 해도 스테이징되지 않는다. 비밀번호, 대용량 파일, OS 임시 파일 등을 제외할 사용한다.</span></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">
<span class="glossary-term" data-tooltip="Git이 추적하지 않을 파일 목록을 정의하는 파일">.gitignore</span> 캐비닛(저장소) <strong>넣으면 되는 물건 목록</strong>이다.
비밀문서(비밀번호), 임시 메모(로그 파일), 개인 물건(IDE 설정) 등은
공용 캐비닛에 넣지 않는다.
</p>
</div>
<div class="code-block mb-4"><span class="code-comment"># SAM .gitignore 주요 항목</span>
<span class="code-comment"># 환경 설정 (비밀번호 포함)</span>
<span class="code-keyword">.env</span>
<span class="code-keyword">.env.*</span>
<span class="code-string">!.env.example</span> <span class="code-comment"># 양식만 추적</span>
<span class="code-comment"># 외부 패키지 (용량 큼)</span>
<span class="code-keyword">/vendor/</span>
<span class="code-keyword">/node_modules/</span>
<span class="code-comment"># IDE 설정 (개인별 다름)</span>
<span class="code-keyword">.idea/</span>
<span class="code-keyword">.vscode/</span>
<span class="code-keyword">.cursor/</span>
<span class="code-comment"># 로그, 백업, 바이너리</span>
<span class="code-keyword">*.log</span>
<span class="code-keyword">*.sql</span>
<span class="code-keyword">*.zip</span></div>
<div class="mb-5 bg-gray-50 rounded-xl p-4 border flex justify-center academy-img-wrap">
<img src="{{ asset('images/academy/git-policy/9.svg') }}" alt=".gitignore 보안"
class="rounded-lg cursor-pointer academy-img-hover"
style="max-height: 320px; width: auto;"
onclick="openLightbox(this)">
</div>
<p class="text-xs text-gray-400 mb-4 text-center">.gitignore 민감 파일을 저장소에서 차단하는 방패</p>
</div>
<div id="env-security" 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-teal-400 rounded-full"></span>
.env 파일 보안
</h3>
<div class="bg-red-50 rounded-lg p-4 border border-red-200 mb-4">
<p class="font-bold text-red-800 mb-2 text-sm">절대 금지: .env 파일을 Git에 커밋</p>
<p class="text-xs text-red-700 leading-relaxed">
.env 파일에는 데이터베이스 비밀번호, API , 암호화 키가 들어 있다.
파일이 Git에 올라가면 모든 이력에 비밀번호가 남게 되어
삭제해도 이력에서 복구할 있다. 유출되면 모든 비밀번호를 교체해야 한다.
</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-3 text-xs mb-4">
<div class="bg-red-50 rounded-lg p-3 border border-red-200">
<p class="font-bold text-red-800 mb-1">Git에 올리면 되는 파일</p>
<p class="text-red-700">.env, credentials.json, *.key<br>storage/secrets/, auth.json</p>
</div>
<div class="bg-green-50 rounded-lg p-3 border border-green-200">
<p class="font-bold text-green-800 mb-1">Git에 올려야 하는 파일</p>
<p class="text-green-700">.env.example (양식만, 값은 비움)<br>.gitignore, .gitattributes</p>
</div>
</div>
</div>
</div>
</section>
{{-- ============================================================ --}}
{{-- 10. 배포와 CI/CD --}}
{{-- ============================================================ --}}
<section id="cicd-deployment" 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-teal-600 text-white rounded-lg flex items-center justify-center text-sm font-bold">10</span>
배포와 CI/CD
</h2>
<div id="git-deploy" 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-teal-400 rounded-full"></span>
Git 기반 배포
<span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon">CI/CD는 코드 변경 자동으로 빌드→테스트→배포하는 자동화 파이프라인이다. CI(Continuous Integration) 코드 통합, CD(Continuous Deployment) 자동 배포를 뜻한다.</span></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">
<span class="glossary-term" data-tooltip="지속적 통합/지속적 배포">CI/CD</span> 공장의 <strong>자동화 컨베이어 벨트</strong>.
원재료(코드) 올려놓으면 가공(빌드) 품질검사(테스트) 포장(패키징) 출하(배포)
자동으로 진행된다. 사람이 단계를 직접 실행할 필요가 없다.
</p>
</div>
<div class="bg-teal-50 rounded-lg p-5 border border-teal-100 mb-4">
<p class="text-sm text-teal-900 leading-relaxed mb-3">
<strong>SAM의 배포 흐름:</strong>
</p>
<div class="space-y-2 text-xs text-teal-800">
<div class="flex items-start gap-2">
<span class="bg-teal-600 text-white rounded-full w-5 h-5 flex items-center justify-center text-xs shrink-0">1</span>
<span>개발자가 코드 작성 git commit</span>
</div>
<div class="flex items-start gap-2">
<span class="bg-teal-600 text-white rounded-full w-5 h-5 flex items-center justify-center text-xs shrink-0">2</span>
<span>팀장이 git push로 원격 저장소에 업로드</span>
</div>
<div class="flex items-start gap-2">
<span class="bg-teal-600 text-white rounded-full w-5 h-5 flex items-center justify-center text-xs shrink-0">3</span>
<span>서버에서 git pull로 최신 코드 받기</span>
</div>
<div class="flex items-start gap-2">
<span class="bg-teal-600 text-white rounded-full w-5 h-5 flex items-center justify-center text-xs shrink-0">4</span>
<span>composer install migrate config:clear</span>
</div>
<div class="flex items-start gap-2">
<span class="bg-teal-600 text-white rounded-full w-5 h-5 flex items-center justify-center text-xs shrink-0">5</span>
<span>서비스 정상 동작 확인</span>
</div>
</div>
</div>
<div class="mb-5 bg-gray-50 rounded-xl p-4 border flex justify-center academy-img-wrap">
<img src="{{ asset('images/academy/git-policy/10.svg') }}" alt="CI/CD 파이프라인"
class="rounded-lg cursor-pointer academy-img-hover"
style="max-height: 320px; width: auto;"
onclick="openLightbox(this)">
</div>
<p class="text-xs text-gray-400 mb-4 text-center">CI/CD 파이프라인 코드에서 배포까지의 자동화 흐름</p>
</div>
<div id="rollback" 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-teal-400 rounded-full"></span>
롤백: revert vs reset
</h3>
<div class="bg-teal-50 rounded-lg p-5 border border-teal-100 mb-4">
<p class="text-sm text-teal-900 leading-relaxed">
배포 문제가 발생하면 이전 버전으로 되돌려야 한다. Git에서는 가지 방법이 있다.
</p>
</div>
<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"><span class="glossary-term" data-tooltip="특정 커밋의 변경 사항을 되돌리는 새 커밋을 만드는 것">Revert</span> (권장)</p>
<ul class="text-green-700 space-y-1">
<li>- 되돌리는 <strong> 커밋</strong> 생성</li>
<li>- 이력이 보존되어 안전</li>
<li>- 작업에 영향 없음</li>
<li>- <code class="bg-green-100 px-1 rounded">git revert &lt;해시&gt;</code></li>
</ul>
</div>
<div class="bg-red-50 rounded-lg p-4 border border-red-200">
<p class="font-bold text-red-800 mb-2"><span class="glossary-term" data-tooltip="커밋 이력 자체를 되돌리는 것">Reset</span> (주의)</p>
<ul class="text-red-700 space-y-1">
<li>- 커밋 이력 자체를 삭제</li>
<li>- 되돌린 기록이 남지 않음</li>
<li>- 작업에 혼란 가능</li>
<li>- 로컬에서만 사용 권장</li>
</ul>
</div>
</div>
<div class="code-block mb-4"><span class="code-comment"># 안전한 롤백 — revert (권장)</span>
<span class="code-keyword">git revert</span> <span class="code-string">a1b2c3d</span> <span class="code-comment"># 해당 커밋을 되돌리는 새 커밋 생성</span>
<span class="code-comment"># 위험한 롤백 — reset (주의!)</span>
<span class="code-keyword">git reset</span> <span class="code-attr">--soft</span> <span class="code-string">HEAD~1</span> <span class="code-comment"># 직전 커밋 취소 (변경은 유지)</span>
<span class="code-keyword">git reset</span> <span class="code-attr">--hard</span> <span class="code-string">HEAD~1</span> <span class="code-comment"># 직전 커밋 + 변경 모두 삭제 (위험!)</span></div>
<div class="bg-teal-50 rounded-lg p-4 border border-teal-100 mb-4">
<p class="text-xs text-teal-800 leading-relaxed">
<strong>SAM 배포 참고:</strong>
SAM에서는 React(Next.js) 빌드를 서버에서 실행하지 않는다.
서버 스펙(2코어, 3.8GB RAM)으로는 빌드 메모리 부족이 발생하므로,
반드시 로컬에서 빌드 결과물만 배포한다.
</p>
</div>
</div>
</div>
</section>
</div>
</div>
</div>
<!-- hover 프리뷰 오버레이 -->
<div id="hover-preview">
<img id="hover-preview-img" src="" alt="">
<span class="hover-caption" id="hover-preview-caption"></span>
</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() {
var preview = document.getElementById('hover-preview');
var previewImg = document.getElementById('hover-preview-img');
var previewCaption = document.getElementById('hover-preview-caption');
var hoverTimer = null;
var isPreviewActive = false;
var HOVER_DELAY = 350;
document.querySelectorAll('.academy-img-hover').forEach(function(img) {
img.addEventListener('mouseenter', function() {
var el = this;
hoverTimer = setTimeout(function() {
showPreview(el);
}, HOVER_DELAY);
});
img.addEventListener('mouseleave', function() {
clearTimeout(hoverTimer);
if (isPreviewActive) {
hidePreview();
}
});
});
function showPreview(el) {
previewImg.src = el.src;
previewImg.alt = el.alt || '';
var caption = el.alt || '';
var nextP = el.parentElement && el.parentElement.querySelector('p');
if (nextP) caption = nextP.textContent;
previewCaption.textContent = caption;
preview.classList.add('is-active');
isPreviewActive = true;
}
function hidePreview() {
preview.classList.remove('is-active');
isPreviewActive = false;
}
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';
hidePreview();
};
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') {
if (isPreviewActive) hidePreview();
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>
@include('components.academy-glossary', ['domain' => 'git-policy'])
@endsection