- 서버는 Docker 없이 네이티브로 운영됨을 명시하는 핵심 안내 박스 추가 - 섹션 2 제목을 "SAM 로컬 Docker 아키텍처"로 변경 - 섹션 5-3에 서버 네이티브 구조도 추가 - 섹션 6에 로컬(Docker) vs 서버(네이티브) 명령어 비교표 추가 - 목차(TOC) 업데이트
1246 lines
84 KiB
PHP
1246 lines
84 KiB
PHP
@extends('layouts.app')
|
|
|
|
@section('title', 'Docker 환경이해')
|
|
|
|
@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 프리뷰 오버레이 */
|
|
#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);
|
|
}
|
|
</style>
|
|
@endpush
|
|
|
|
@section('content')
|
|
<div class="max-w-6xl mx-auto">
|
|
|
|
<!-- 히어로 배너 -->
|
|
<div class="rounded-2xl overflow-hidden mb-8 shadow-lg" style="background: linear-gradient(135deg, #1a2a3a 0%, #0f172a 100%);">
|
|
<div class="flex items-center" style="flex-wrap: wrap;">
|
|
<div style="flex: 1 1 300px; padding: 2rem 2.5rem;">
|
|
<div class="flex items-center gap-2 text-sm mb-2" style="color: #64b5f6;">
|
|
<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: #ffffff;">Docker 환경</span>
|
|
</div>
|
|
<h1 class="text-3xl font-bold mb-2" style="color: #ffffff;">Docker 환경이해</h1>
|
|
<p class="text-sm" style="color: #cbd5e1;">SAM 프로젝트의 로컬 Docker 환경과 서버 네이티브 환경 — 비개발자도 이해할 수 있는 가이드</p>
|
|
</div>
|
|
<div class="shrink-0" style="width: 240px; padding: 1.5rem;">
|
|
<div class="overflow-hidden rounded-xl">
|
|
<img src="{{ asset('images/academy/docker-environment/1.svg') }}" alt="Docker 컨테이너 비유 일러스트"
|
|
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-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="#docker-why" class="block text-blue-700 hover:text-blue-900 py-1 font-medium">1. Docker란?</a>
|
|
<a href="#docker-analogy" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">컨테이너 비유</a>
|
|
<a href="#docker-vs-vm" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">컨테이너 vs VM</a>
|
|
|
|
<a href="#architecture" class="block text-blue-700 hover:text-blue-900 py-1 font-medium mt-2">2. SAM 로컬 Docker 아키텍처</a>
|
|
<a href="#arch-services" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">서비스 구성</a>
|
|
<a href="#arch-flow" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">요청 흐름</a>
|
|
|
|
<a href="#services" class="block text-blue-700 hover:text-blue-900 py-1 font-medium mt-2">3. 서비스별 상세</a>
|
|
<a href="#svc-mng-api" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">MNG / API</a>
|
|
<a href="#svc-react-mysql" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">React / MySQL</a>
|
|
<a href="#svc-nginx" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">Nginx 라우팅</a>
|
|
|
|
<a href="#volumes" class="block text-blue-700 hover:text-blue-900 py-1 font-medium mt-2">4. 볼륨과 데이터</a>
|
|
<a href="#vol-bind" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">바인드 마운트</a>
|
|
<a href="#vol-docker" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">Docker 볼륨</a>
|
|
|
|
<a href="#env" class="block text-blue-700 hover:text-blue-900 py-1 font-medium mt-2">5. 환경 변수 (.env)</a>
|
|
<a href="#env-role" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">.env의 역할</a>
|
|
<a href="#env-priority" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">우선순위</a>
|
|
<a href="#env-local-server" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">로컬 vs 서버</a>
|
|
|
|
<a href="#commands" class="block text-blue-700 hover:text-blue-900 py-1 font-medium mt-2">6. 실전 명령어</a>
|
|
<a href="#cmd-compare" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">로컬 vs 서버 비교</a>
|
|
<a href="#cmd-basic" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">자주 쓰는 명령어</a>
|
|
<a href="#cmd-caution" class="block text-blue-600 hover:text-blue-800 py-0.5 pl-3">주의사항</a>
|
|
<a href="#cmd-trouble" class="block text-blue-600 hover:text-blue-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-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="#docker-why" class="block text-blue-700 hover:text-blue-900 py-1">1. Docker란? — 왜 Docker를 쓰는가</a>
|
|
<a href="#architecture" class="block text-blue-700 hover:text-blue-900 py-1">2. SAM 로컬 Docker 아키텍처</a>
|
|
<a href="#services" class="block text-blue-700 hover:text-blue-900 py-1">3. 서비스별 상세</a>
|
|
<a href="#volumes" class="block text-blue-700 hover:text-blue-900 py-1">4. 볼륨과 데이터 관리</a>
|
|
<a href="#env" class="block text-blue-700 hover:text-blue-900 py-1">5. 환경 변수 (.env) 관리</a>
|
|
<a href="#commands" class="block text-blue-700 hover:text-blue-900 py-1">6. 실전 명령어 & 트러블슈팅</a>
|
|
</nav>
|
|
</details>
|
|
</div>
|
|
|
|
{{-- ============================================================ --}}
|
|
{{-- 핵심 안내: 로컬 vs 서버 환경 차이 --}}
|
|
{{-- ============================================================ --}}
|
|
<div class="bg-indigo-50 border-2 border-indigo-300 rounded-xl p-5 mb-2">
|
|
<div class="flex items-start gap-3">
|
|
<span class="w-8 h-8 bg-indigo-500 text-white rounded-lg flex items-center justify-center text-sm font-bold shrink-0">!</span>
|
|
<div>
|
|
<h3 class="font-bold text-indigo-900 mb-2">핵심: SAM의 로컬과 서버 환경은 다르다</h3>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-3 text-xs">
|
|
<div class="bg-blue-100 rounded-lg p-3 border border-blue-300">
|
|
<p class="font-bold text-blue-900 mb-1">로컬 개발 환경 (내 PC / WSL)</p>
|
|
<p class="text-blue-800"><strong>Docker 사용</strong> — PHP, MySQL, Nginx가 모두 컨테이너 안에서 실행된다.</p>
|
|
<p class="text-blue-700 mt-1"><code class="bg-blue-200 px-1 rounded">docker exec sam-api-1 php artisan migrate</code></p>
|
|
</div>
|
|
<div class="bg-green-100 rounded-lg p-3 border border-green-300">
|
|
<p class="font-bold text-green-900 mb-1">운영 서버 (114.203.209.83)</p>
|
|
<p class="text-green-800"><strong>Docker 없음 (네이티브)</strong> — PHP, MySQL, Nginx가 서버에 직접 설치되어 있다.</p>
|
|
<p class="text-green-700 mt-1"><code class="bg-green-200 px-1 rounded">cd /home/webservice/api && php artisan migrate</code></p>
|
|
</div>
|
|
</div>
|
|
<p class="text-xs text-indigo-700 mt-2">
|
|
<strong>서버에서 Docker를 사용하지 않는 이유:</strong> 서버 스펙(2코어, 3.8GB RAM)에서 Docker는 무거워서 성능 저하가 발생한다.
|
|
따라서 서버에는 PHP, Nginx, MySQL을 직접 설치하여 운영한다.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- ============================================================ --}}
|
|
{{-- 1. Docker란? — 왜 Docker를 쓰는가 --}}
|
|
{{-- ============================================================ --}}
|
|
<section id="docker-why" class="scroll-mt-20">
|
|
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
|
|
<h2 class="text-xl font-bold text-gray-800 mb-6 flex items-center gap-3">
|
|
<span class="w-8 h-8 bg-blue-500 text-white rounded-lg flex items-center justify-center text-sm font-bold">1</span>
|
|
Docker란? — 왜 Docker를 쓰는가
|
|
</h2>
|
|
|
|
<!-- 1-1. 컨테이너 비유 -->
|
|
<div id="docker-analogy" class="scroll-mt-20 mb-8">
|
|
<h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
|
|
<span class="w-2 h-2 bg-blue-400 rounded-full"></span>
|
|
컨테이너 비유
|
|
</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>Docker</strong>는 애플리케이션을 <strong>컨테이너</strong>라는 격리된 환경에 넣어 실행하는 도구다.
|
|
어떤 컴퓨터에서든 동일한 환경으로 프로그램을 돌릴 수 있게 해준다.
|
|
</p>
|
|
</div>
|
|
<div class="bg-amber-50 rounded-lg p-4 border border-amber-100 mb-4">
|
|
<p class="font-semibold text-amber-800 mb-2">비유: 이삿짐 컨테이너</p>
|
|
<p class="text-xs text-amber-900 leading-relaxed">
|
|
이사할 때 가구를 <strong>컨테이너에 통째로 넣어서 옮기면</strong>, 어디로 가든 꺼내서 바로 사용할 수 있다.
|
|
Docker도 마찬가지다. 프로그램과 실행에 필요한 모든 것(PHP, MySQL, 설정 파일 등)을 컨테이너에 넣어두면,
|
|
<strong>내 PC든 서버든 똑같이 동작한다.</strong>
|
|
</p>
|
|
</div>
|
|
<div class="text-sm text-gray-700 space-y-3">
|
|
<p class="font-semibold text-gray-800">Docker 없이 개발할 때의 문제점:</p>
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-3 text-xs">
|
|
<div class="bg-red-50 rounded-lg p-3 border border-red-200 text-center">
|
|
<p class="font-bold text-red-800 mb-1">"내 PC에선 되는데?"</p>
|
|
<p class="text-red-700">PHP 버전이 달라서<br>서버에서 오류 발생</p>
|
|
</div>
|
|
<div class="bg-red-50 rounded-lg p-3 border border-red-200 text-center">
|
|
<p class="font-bold text-red-800 mb-1">의존성 충돌</p>
|
|
<p class="text-red-700">A 프로젝트는 PHP 7.3<br>B 프로젝트는 PHP 8.4 필요</p>
|
|
</div>
|
|
<div class="bg-red-50 rounded-lg p-3 border border-red-200 text-center">
|
|
<p class="font-bold text-red-800 mb-1">설치 지옥</p>
|
|
<p class="text-red-700">PHP, MySQL, Nginx...<br>하나하나 설치하다 하루 종일</p>
|
|
</div>
|
|
</div>
|
|
<div class="bg-green-50 rounded-lg p-4 border border-green-200 mt-3">
|
|
<p class="font-semibold text-green-800 mb-1">Docker를 쓰면:</p>
|
|
<p class="text-xs text-green-700"><code class="bg-green-100 px-1 rounded">docker compose up</code> 한 줄이면 모든 서비스가 동일한 환경으로 시작된다.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 1-2. 컨테이너 vs VM -->
|
|
<div id="docker-vs-vm" class="scroll-mt-20">
|
|
<h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
|
|
<span class="w-2 h-2 bg-blue-400 rounded-full"></span>
|
|
컨테이너 vs 가상머신(VM)
|
|
</h3>
|
|
<div class="mb-5 bg-gray-50 rounded-xl p-4 border flex justify-center academy-img-wrap">
|
|
<img src="{{ asset('images/academy/docker-environment/2.svg') }}" alt="컨테이너 vs VM 비교"
|
|
class="rounded-lg cursor-pointer academy-img-hover"
|
|
style="max-height: 280px; width: auto;"
|
|
onclick="openLightbox(this)">
|
|
</div>
|
|
<p class="text-xs text-gray-400 mb-4 text-center">컨테이너 vs 가상머신 구조 비교</p>
|
|
<div class="overflow-x-auto">
|
|
<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">가상머신 (VM)</th>
|
|
<th class="text-left py-2 px-3 font-medium text-gray-600">Docker 컨테이너</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="text-xs text-gray-700">
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-medium">비유</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-medium">OS</td>
|
|
<td class="py-2 px-3">각각 별도 OS 설치</td>
|
|
<td class="py-2 px-3">호스트 OS 커널 공유</td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-medium">크기</td>
|
|
<td class="py-2 px-3">수 GB</td>
|
|
<td class="py-2 px-3">수십~수백 MB</td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-medium">시작 시간</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-medium">자원 사용</td>
|
|
<td class="py-2 px-3">무거움 (RAM 많이 차지)</td>
|
|
<td class="py-2 px-3">가벼움 (오버헤드 적음)</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{{-- ============================================================ --}}
|
|
{{-- 2. SAM Docker 아키텍처 --}}
|
|
{{-- ============================================================ --}}
|
|
<section id="architecture" class="scroll-mt-20">
|
|
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
|
|
<h2 class="text-xl font-bold text-gray-800 mb-6 flex items-center gap-3">
|
|
<span class="w-8 h-8 bg-blue-500 text-white rounded-lg flex items-center justify-center text-sm font-bold">2</span>
|
|
SAM 로컬 Docker 아키텍처 — 개발 환경 구조
|
|
</h2>
|
|
|
|
<!-- 2-1. 서비스 구성 -->
|
|
<div id="arch-services" 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>
|
|
9개 서비스 구성
|
|
</h3>
|
|
<div class="mb-5 bg-gray-50 rounded-xl p-4 border flex justify-center academy-img-wrap">
|
|
<img src="{{ asset('images/academy/docker-environment/3.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 Docker 전체 아키텍처 — samnet 브리지 네트워크</p>
|
|
|
|
<div class="bg-blue-50 rounded-lg p-4 border border-blue-100 mb-4">
|
|
<p class="text-sm text-blue-900 leading-relaxed">
|
|
<strong>로컬 개발 환경</strong>에서 SAM은 <strong>9개 서비스</strong>가 하나의 <strong>samnet 브리지 네트워크</strong>에서 통신한다.
|
|
<code class="bg-blue-100 px-1 rounded">docker-compose.yml</code> 파일로 전체 구성을 관리한다.
|
|
</p>
|
|
<p class="text-xs text-blue-700 mt-1">
|
|
(운영 서버에서는 Docker를 사용하지 않고, PHP/Nginx/MySQL이 직접 설치되어 있다.)
|
|
</p>
|
|
</div>
|
|
|
|
<div class="overflow-x-auto">
|
|
<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 font-medium">nginx</td>
|
|
<td class="py-2 px-3"><code class="bg-gray-100 px-1 rounded">sam-nginx-1</code></td>
|
|
<td class="py-2 px-3">Nginx</td>
|
|
<td class="py-2 px-3">리버스 프록시, SSL, 도메인 라우팅</td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100 bg-blue-50">
|
|
<td class="py-2 px-3 font-medium">api</td>
|
|
<td class="py-2 px-3"><code class="bg-blue-100 px-1 rounded">sam-api-1</code></td>
|
|
<td class="py-2 px-3">PHP 8.4 + Laravel</td>
|
|
<td class="py-2 px-3">REST API, DB 마이그레이션</td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100 bg-blue-50">
|
|
<td class="py-2 px-3 font-medium">mng</td>
|
|
<td class="py-2 px-3"><code class="bg-blue-100 px-1 rounded">sam-mng-1</code></td>
|
|
<td class="py-2 px-3">PHP 8.4 + Laravel</td>
|
|
<td class="py-2 px-3">관리자 웹 (HTMX + Blade)</td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-medium">react</td>
|
|
<td class="py-2 px-3"><code class="bg-gray-100 px-1 rounded">sam-react-1</code></td>
|
|
<td class="py-2 px-3">Node.js + Next.js 15</td>
|
|
<td class="py-2 px-3">고객용 프론트엔드 (React)</td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-medium">sales</td>
|
|
<td class="py-2 px-3"><code class="bg-gray-100 px-1 rounded">sam-sales-1</code></td>
|
|
<td class="py-2 px-3">PHP + Laravel</td>
|
|
<td class="py-2 px-3">영업 관리</td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-medium">admin</td>
|
|
<td class="py-2 px-3"><code class="bg-gray-100 px-1 rounded">sam-admin-1</code></td>
|
|
<td class="py-2 px-3">PHP + Laravel</td>
|
|
<td class="py-2 px-3">시스템 관리자 페이지</td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-medium">php73</td>
|
|
<td class="py-2 px-3"><code class="bg-gray-100 px-1 rounded">sam-php73-1</code></td>
|
|
<td class="py-2 px-3">PHP 7.3</td>
|
|
<td class="py-2 px-3">레거시(5130) 호환</td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100 bg-orange-50">
|
|
<td class="py-2 px-3 font-medium">mysql</td>
|
|
<td class="py-2 px-3"><code class="bg-orange-100 px-1 rounded">sam-mysql-1</code></td>
|
|
<td class="py-2 px-3">MySQL 8.0</td>
|
|
<td class="py-2 px-3">데이터베이스 (samdb + chandj)</td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-medium">phpmyadmin</td>
|
|
<td class="py-2 px-3"><code class="bg-gray-100 px-1 rounded">sam-phpmyadmin-1</code></td>
|
|
<td class="py-2 px-3">phpMyAdmin</td>
|
|
<td class="py-2 px-3">DB 관리 웹 UI</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 2-2. 요청 흐름 -->
|
|
<div id="arch-flow" class="scroll-mt-20">
|
|
<h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
|
|
<span class="w-2 h-2 bg-blue-400 rounded-full"></span>
|
|
요청의 여정 — 브라우저에서 DB까지
|
|
</h3>
|
|
<div class="mb-5 bg-gray-50 rounded-xl p-4 border flex justify-center academy-img-wrap">
|
|
<img src="{{ asset('images/academy/docker-environment/4.svg') }}" alt="요청 흐름 5단계"
|
|
class="rounded-lg cursor-pointer academy-img-hover"
|
|
style="max-height: 280px; width: auto;"
|
|
onclick="openLightbox(this)">
|
|
</div>
|
|
<p class="text-xs text-gray-400 mb-4 text-center">요청 흐름 5단계 — 브라우저 → Nginx → PHP-FPM → Laravel → MySQL</p>
|
|
|
|
<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">
|
|
손님(브라우저)이 식당에 들어오면 → 안내 데스크(Nginx)가 좌석으로 안내 →
|
|
서빙 직원(PHP-FPM)이 주문을 받아 → 주방(Laravel)에서 요리 →
|
|
재료 창고(MySQL)에서 재료를 꺼내 요리를 완성한다.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="space-y-2">
|
|
<div class="flex items-center gap-3 text-xs">
|
|
<span class="w-6 h-6 bg-blue-500 text-white rounded-full flex items-center justify-center font-bold shrink-0">1</span>
|
|
<span class="text-gray-700"><strong>브라우저</strong>에서 <code class="bg-gray-100 px-1 rounded">mng.sam.kr</code> 접속</span>
|
|
</div>
|
|
<div class="flex items-center gap-3 text-xs">
|
|
<span class="w-6 h-6 bg-blue-500 text-white rounded-full flex items-center justify-center font-bold shrink-0">2</span>
|
|
<span class="text-gray-700"><strong>Nginx</strong>가 도메인을 보고 MNG 컨테이너로 전달</span>
|
|
</div>
|
|
<div class="flex items-center gap-3 text-xs">
|
|
<span class="w-6 h-6 bg-blue-500 text-white rounded-full flex items-center justify-center font-bold shrink-0">3</span>
|
|
<span class="text-gray-700"><strong>PHP-FPM</strong>이 PHP 코드를 실행</span>
|
|
</div>
|
|
<div class="flex items-center gap-3 text-xs">
|
|
<span class="w-6 h-6 bg-blue-500 text-white rounded-full flex items-center justify-center font-bold shrink-0">4</span>
|
|
<span class="text-gray-700"><strong>Laravel</strong>이 비즈니스 로직 처리 + DB 조회</span>
|
|
</div>
|
|
<div class="flex items-center gap-3 text-xs">
|
|
<span class="w-6 h-6 bg-blue-500 text-white rounded-full flex items-center justify-center font-bold shrink-0">5</span>
|
|
<span class="text-gray-700"><strong>MySQL</strong>에서 데이터를 가져와 HTML로 응답 반환</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{{-- ============================================================ --}}
|
|
{{-- 3. 서비스별 상세 --}}
|
|
{{-- ============================================================ --}}
|
|
<section id="services" class="scroll-mt-20">
|
|
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
|
|
<h2 class="text-xl font-bold text-gray-800 mb-6 flex items-center gap-3">
|
|
<span class="w-8 h-8 bg-blue-500 text-white rounded-lg flex items-center justify-center text-sm font-bold">3</span>
|
|
서비스별 상세 — 각 컨테이너의 역할
|
|
</h2>
|
|
|
|
<!-- 3-1. MNG / API -->
|
|
<div id="svc-mng-api" 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>
|
|
MNG / API — PHP + Laravel 컨테이너
|
|
</h3>
|
|
<div class="mb-5 bg-gray-50 rounded-xl p-4 border flex justify-center academy-img-wrap">
|
|
<img src="{{ asset('images/academy/docker-environment/5.svg') }}" alt="Supervisor 내부 프로세스 구조"
|
|
class="rounded-lg cursor-pointer academy-img-hover"
|
|
style="max-height: 280px; width: auto;"
|
|
onclick="openLightbox(this)">
|
|
</div>
|
|
<p class="text-xs text-gray-400 mb-4 text-center">컨테이너 내부 프로세스 구조 — Supervisor가 관리</p>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
|
<div class="bg-blue-50 rounded-lg p-4 border border-blue-200">
|
|
<p class="font-semibold text-blue-800 text-sm mb-2">MNG 컨테이너</p>
|
|
<ul class="text-xs text-blue-900 space-y-1">
|
|
<li>PHP 8.4 + Laravel 11</li>
|
|
<li>Supervisor가 관리하는 프로세스:</li>
|
|
<li class="pl-3">- Nginx (내부 웹서버)</li>
|
|
<li class="pl-3">- PHP-FPM (코드 실행)</li>
|
|
<li class="pl-3">- Queue Worker x2 (백그라운드 작업)</li>
|
|
</ul>
|
|
</div>
|
|
<div class="bg-blue-50 rounded-lg p-4 border border-blue-200">
|
|
<p class="font-semibold text-blue-800 text-sm mb-2">API 컨테이너</p>
|
|
<ul class="text-xs text-blue-900 space-y-1">
|
|
<li>PHP 8.4 + Laravel 11</li>
|
|
<li>Supervisor가 관리하는 프로세스:</li>
|
|
<li class="pl-3">- Nginx (내부 웹서버)</li>
|
|
<li class="pl-3">- PHP-FPM (코드 실행)</li>
|
|
<li class="pl-3">- Queue Worker (백그라운드 작업)</li>
|
|
<li class="pl-3">- Scheduler (크론 작업)</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-amber-50 rounded-lg p-4 border border-amber-100">
|
|
<p class="font-semibold text-amber-800 mb-2">비유: 식당 주방</p>
|
|
<p class="text-xs text-amber-900 leading-relaxed">
|
|
하나의 컨테이너 안에 <strong>주방장(Supervisor)</strong>이 있고, 그 아래 요리사(PHP-FPM), 서빙 직원(Nginx), 설거지 담당(Queue Worker)이 각자 일을 한다.
|
|
주방장이 누가 쓰러지면 바로 새 직원을 투입한다(프로세스 자동 재시작).
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 3-2. React / MySQL -->
|
|
<div id="svc-react-mysql" 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>
|
|
React / MySQL
|
|
</h3>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div class="bg-purple-50 rounded-lg p-4 border border-purple-200">
|
|
<p class="font-semibold text-purple-800 text-sm mb-2">React (Next.js) 컨테이너</p>
|
|
<ul class="text-xs text-purple-900 space-y-1">
|
|
<li>Node.js + Next.js 15</li>
|
|
<li>고객용 프론트엔드 앱</li>
|
|
<li>서버 사이드 렌더링(SSR)</li>
|
|
<li>포트 3000에서 실행</li>
|
|
</ul>
|
|
<div class="bg-red-50 rounded p-2 mt-2 border border-red-200">
|
|
<p class="text-xs text-red-700 font-medium">서버에서 빌드 금지! 로컬에서만 빌드</p>
|
|
</div>
|
|
</div>
|
|
<div class="bg-orange-50 rounded-lg p-4 border border-orange-200">
|
|
<p class="font-semibold text-orange-800 text-sm mb-2">MySQL 컨테이너</p>
|
|
<ul class="text-xs text-orange-900 space-y-1">
|
|
<li>MySQL 8.0</li>
|
|
<li>데이터베이스 2개 운영:</li>
|
|
<li class="pl-3">- <code class="bg-orange-100 px-1 rounded">samdb</code>: SAM 메인 DB</li>
|
|
<li class="pl-3">- <code class="bg-orange-100 px-1 rounded">chandj</code>: 레거시 DB</li>
|
|
<li>포트 3306으로 접근</li>
|
|
<li>데이터는 <code class="bg-orange-100 px-1 rounded">db_data</code> 볼륨에 영구 저장</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 3-3. Nginx 라우팅 -->
|
|
<div id="svc-nginx" class="scroll-mt-20">
|
|
<h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
|
|
<span class="w-2 h-2 bg-blue-400 rounded-full"></span>
|
|
Nginx — 도메인별 라우팅
|
|
</h3>
|
|
<div class="mb-5 bg-gray-50 rounded-xl p-4 border flex justify-center academy-img-wrap">
|
|
<img src="{{ asset('images/academy/docker-environment/6.svg') }}" alt="Nginx 도메인 라우팅 맵"
|
|
class="rounded-lg cursor-pointer academy-img-hover"
|
|
style="max-height: 280px; width: auto;"
|
|
onclick="openLightbox(this)">
|
|
</div>
|
|
<p class="text-xs text-gray-400 mb-4 text-center">Nginx 도메인 → 서비스 라우팅 맵</p>
|
|
|
|
<div class="bg-blue-50 rounded-lg p-4 border border-blue-100 mb-4">
|
|
<p class="text-sm text-blue-900 leading-relaxed">
|
|
외부 Nginx가 <strong>도메인 이름</strong>을 보고 어떤 컨테이너로 보낼지 결정한다.
|
|
모든 요청은 80/443 포트로 들어오고, Nginx가 내부 서비스로 분배한다.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="overflow-x-auto">
|
|
<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"><code class="bg-blue-100 px-1 rounded">mng.sam.kr</code></td>
|
|
<td class="py-2 px-3">mng (Laravel)</td>
|
|
<td class="py-2 px-3">:8080</td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3"><code class="bg-blue-100 px-1 rounded">api.sam.kr</code></td>
|
|
<td class="py-2 px-3">api (Laravel)</td>
|
|
<td class="py-2 px-3">:8080</td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3"><code class="bg-blue-100 px-1 rounded">app.sam.kr</code></td>
|
|
<td class="py-2 px-3">react (Next.js)</td>
|
|
<td class="py-2 px-3">:3000</td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3"><code class="bg-blue-100 px-1 rounded">admin.sam.kr</code></td>
|
|
<td class="py-2 px-3">admin (Laravel)</td>
|
|
<td class="py-2 px-3">:8080</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{{-- ============================================================ --}}
|
|
{{-- 4. 볼륨과 데이터 관리 --}}
|
|
{{-- ============================================================ --}}
|
|
<section id="volumes" class="scroll-mt-20">
|
|
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
|
|
<h2 class="text-xl font-bold text-gray-800 mb-6 flex items-center gap-3">
|
|
<span class="w-8 h-8 bg-blue-500 text-white rounded-lg flex items-center justify-center text-sm font-bold">4</span>
|
|
볼륨과 데이터 관리
|
|
</h2>
|
|
|
|
<!-- 4-1. 바인드 마운트 -->
|
|
<div id="vol-bind" class="scroll-mt-20 mb-8">
|
|
<h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
|
|
<span class="w-2 h-2 bg-blue-400 rounded-full"></span>
|
|
바인드 마운트 — 소스 코드 실시간 동기화
|
|
</h3>
|
|
<div class="mb-5 bg-gray-50 rounded-xl p-4 border flex justify-center academy-img-wrap">
|
|
<img src="{{ asset('images/academy/docker-environment/7.svg') }}" alt="볼륨 마운트 구조"
|
|
class="rounded-lg cursor-pointer academy-img-hover"
|
|
style="max-height: 280px; width: auto;"
|
|
onclick="openLightbox(this)">
|
|
</div>
|
|
<p class="text-xs text-gray-400 mb-4 text-center">바인드 마운트 vs Docker 볼륨</p>
|
|
|
|
<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>내 PC 폴더를 컨테이너와 공유</strong>하는 것이다.
|
|
내 PC에서 파일을 수정하면 컨테이너 안에서도 즉시 반영된다.
|
|
마치 클라우드 공유 폴더처럼 양쪽이 항상 같은 파일을 본다.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="bg-gray-900 rounded-lg p-4 overflow-x-auto mb-4">
|
|
<pre class="text-xs font-mono leading-relaxed"><span class="text-gray-400"># docker-compose.yml 예시</span>
|
|
<span class="text-green-400">volumes:</span>
|
|
<span class="text-yellow-300">- ../mng:/var/www/mng</span> <span class="text-gray-400"># 바인드 마운트: 소스코드</span>
|
|
<span class="text-cyan-300">- mng_vendor:/var/www/mng/vendor</span> <span class="text-gray-400"># Docker 볼륨: 의존성</span></pre>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-3 text-xs">
|
|
<div class="bg-green-50 rounded-lg p-3 border border-green-200">
|
|
<p class="font-bold text-green-800 mb-1">바인드 마운트</p>
|
|
<p class="text-green-700">호스트 경로 ↔ 컨테이너 경로 연결</p>
|
|
<p class="text-green-600 mt-1">코드 수정 → 즉시 반영</p>
|
|
</div>
|
|
<div class="bg-blue-50 rounded-lg p-3 border border-blue-200">
|
|
<p class="font-bold text-blue-800 mb-1">Docker 볼륨</p>
|
|
<p class="text-blue-700">Docker가 자체 관리하는 저장소</p>
|
|
<p class="text-blue-600 mt-1">vendor, node_modules 성능 향상</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 4-2. Docker 볼륨 -->
|
|
<div id="vol-docker" class="scroll-mt-20">
|
|
<h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
|
|
<span class="w-2 h-2 bg-blue-400 rounded-full"></span>
|
|
Docker 볼륨 — 의존성과 데이터 격리
|
|
</h3>
|
|
<div class="bg-blue-50 rounded-lg p-4 border border-blue-100 mb-4">
|
|
<p class="text-sm text-blue-900 leading-relaxed">
|
|
<code class="bg-blue-100 px-1 rounded">vendor</code>와 <code class="bg-blue-100 px-1 rounded">node_modules</code>는 Docker 볼륨으로 격리한다.
|
|
이렇게 하면 호스트(WSL)의 파일 시스템 성능 문제를 피하고, 컨테이너 내부에서 빠르게 읽을 수 있다.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="overflow-x-auto">
|
|
<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-medium"><code class="bg-gray-100 px-1 rounded">db_data</code></td>
|
|
<td class="py-2 px-3">MySQL 데이터</td>
|
|
<td class="py-2 px-3">컨테이너 삭제해도 데이터 유지</td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-medium"><code class="bg-gray-100 px-1 rounded">api_vendor</code></td>
|
|
<td class="py-2 px-3">API Composer 패키지</td>
|
|
<td class="py-2 px-3">컨테이너 내부 성능 최적화</td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-medium"><code class="bg-gray-100 px-1 rounded">mng_vendor</code></td>
|
|
<td class="py-2 px-3">MNG Composer 패키지</td>
|
|
<td class="py-2 px-3">컨테이너 내부 성능 최적화</td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-medium"><code class="bg-gray-100 px-1 rounded">*_node_modules</code></td>
|
|
<td class="py-2 px-3">Node.js 패키지</td>
|
|
<td class="py-2 px-3">호스트와 분리하여 속도 향상</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="bg-red-50 rounded-lg p-4 border border-red-200 mt-4">
|
|
<p class="font-semibold text-red-800 text-sm mb-1">주의: Docker 볼륨은 호스트에서 직접 접근 불가</p>
|
|
<p class="text-xs text-red-700">
|
|
<code class="bg-red-100 px-1 rounded">vendor</code> 폴더는 Docker 볼륨 안에 있으므로,
|
|
호스트(WSL)에서 직접 수정할 수 없다. 반드시 <code class="bg-red-100 px-1 rounded">docker exec</code>로 컨테이너에 들어가서 작업해야 한다.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{{-- ============================================================ --}}
|
|
{{-- 5. 환경 변수 (.env) 관리 --}}
|
|
{{-- ============================================================ --}}
|
|
<section id="env" class="scroll-mt-20">
|
|
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
|
|
<h2 class="text-xl font-bold text-gray-800 mb-6 flex items-center gap-3">
|
|
<span class="w-8 h-8 bg-blue-500 text-white rounded-lg flex items-center justify-center text-sm font-bold">5</span>
|
|
환경 변수 (.env) 관리
|
|
</h2>
|
|
|
|
<!-- 5-1. .env의 역할 -->
|
|
<div id="env-role" 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>
|
|
.env 파일의 역할과 구조
|
|
</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>.env</strong> 파일은 애플리케이션의 <strong>환경 설정</strong>을 저장하는 파일이다.
|
|
DB 접속 정보, API 키, 앱 이름 등 <strong>환경마다 달라지는 값</strong>을 코드 밖에서 관리한다.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="bg-amber-50 rounded-lg p-4 border border-amber-100 mb-4">
|
|
<p class="font-semibold text-amber-800 mb-2">비유: 매장별 설정 카드</p>
|
|
<p class="text-xs text-amber-900 leading-relaxed">
|
|
프랜차이즈 매장마다 <strong>전화번호, 주소, 영업시간</strong>이 다르다.
|
|
매장 설정 카드(.env)에 적어두면, 같은 매뉴얼(코드)로도 매장마다 다르게 운영할 수 있다.
|
|
코드를 바꾸지 않고도 DB 주소, 비밀번호 등을 변경 가능하다.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="bg-gray-900 rounded-lg p-4 overflow-x-auto mb-4">
|
|
<pre class="text-xs font-mono leading-relaxed"><span class="text-gray-400"># .env 파일 예시</span>
|
|
<span class="text-green-400">APP_NAME</span>=<span class="text-yellow-300">SAM</span>
|
|
<span class="text-green-400">APP_ENV</span>=<span class="text-yellow-300">local</span>
|
|
<span class="text-green-400">APP_DEBUG</span>=<span class="text-yellow-300">true</span>
|
|
|
|
<span class="text-gray-400"># 데이터베이스 설정</span>
|
|
<span class="text-green-400">DB_HOST</span>=<span class="text-yellow-300">sam-mysql-1</span>
|
|
<span class="text-green-400">DB_PORT</span>=<span class="text-yellow-300">3306</span>
|
|
<span class="text-green-400">DB_DATABASE</span>=<span class="text-yellow-300">samdb</span>
|
|
<span class="text-green-400">DB_USERNAME</span>=<span class="text-yellow-300">samuser</span>
|
|
<span class="text-green-400">DB_PASSWORD</span>=<span class="text-yellow-300">sampass</span>
|
|
|
|
<span class="text-gray-400"># 메일 설정</span>
|
|
<span class="text-green-400">MAIL_MAILER</span>=<span class="text-yellow-300">smtp</span>
|
|
<span class="text-green-400">MAIL_HOST</span>=<span class="text-yellow-300">smtp.gmail.com</span></pre>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-3 text-xs">
|
|
<div class="bg-green-50 rounded-lg p-3 border border-green-200">
|
|
<p class="font-bold text-green-800 mb-1">.env 파일에 넣는 것</p>
|
|
<ul class="text-green-700 space-y-0.5">
|
|
<li>- DB 접속 정보 (호스트, 비밀번호)</li>
|
|
<li>- API 키, 시크릿 키</li>
|
|
<li>- 앱 환경 (local / production)</li>
|
|
<li>- 메일, 캐시, 큐 설정</li>
|
|
</ul>
|
|
</div>
|
|
<div class="bg-red-50 rounded-lg p-3 border border-red-200">
|
|
<p class="font-bold text-red-800 mb-1">.env 파일 주의사항</p>
|
|
<ul class="text-red-700 space-y-0.5">
|
|
<li>- Git에 커밋하면 안 됨 (.gitignore)</li>
|
|
<li>- 비밀번호가 있으므로 외부 공유 금지</li>
|
|
<li>- 서버와 로컬 값이 다름</li>
|
|
<li>- 수정 후 캐시 클리어 필요</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 5-2. 우선순위 -->
|
|
<div id="env-priority" 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>
|
|
Docker environment vs .env 우선순위
|
|
</h3>
|
|
<div class="mb-5 bg-gray-50 rounded-xl p-4 border flex justify-center academy-img-wrap">
|
|
<img src="{{ asset('images/academy/docker-environment/8.svg') }}" alt=".env 로드 흐름"
|
|
class="rounded-lg cursor-pointer academy-img-hover"
|
|
style="max-height: 280px; width: auto;"
|
|
onclick="openLightbox(this)">
|
|
</div>
|
|
<p class="text-xs text-gray-400 mb-4 text-center">.env 로드 흐름과 우선순위</p>
|
|
|
|
<div class="bg-blue-50 rounded-lg p-4 border border-blue-100 mb-4">
|
|
<p class="text-sm text-blue-900 leading-relaxed">
|
|
Docker Compose의 <code class="bg-blue-100 px-1 rounded">environment</code> 설정은
|
|
프로젝트 <code class="bg-blue-100 px-1 rounded">.env</code> 파일보다 <strong>우선</strong>한다.
|
|
즉, Docker에서 설정한 DB_HOST가 .env의 DB_HOST를 덮어쓴다.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="space-y-2 mb-4">
|
|
<div class="flex items-center gap-3 text-xs">
|
|
<span class="w-6 h-6 bg-red-500 text-white rounded-full flex items-center justify-center font-bold shrink-0">1</span>
|
|
<span class="text-gray-700"><strong>최우선:</strong> docker-compose.yml <code class="bg-gray-100 px-1 rounded">environment</code> 섹션</span>
|
|
</div>
|
|
<div class="flex items-center gap-3 text-xs">
|
|
<span class="w-6 h-6 bg-orange-500 text-white rounded-full flex items-center justify-center font-bold shrink-0">2</span>
|
|
<span class="text-gray-700"><strong>중간:</strong> 프로젝트 <code class="bg-gray-100 px-1 rounded">.env</code> 파일</span>
|
|
</div>
|
|
<div class="flex items-center gap-3 text-xs">
|
|
<span class="w-6 h-6 bg-gray-400 text-white rounded-full flex items-center justify-center font-bold shrink-0">3</span>
|
|
<span class="text-gray-700"><strong>기본값:</strong> <code class="bg-gray-100 px-1 rounded">.env.example</code> (참조용, 실제 로드 안 됨)</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-gray-900 rounded-lg p-4 overflow-x-auto">
|
|
<pre class="text-xs font-mono leading-relaxed"><span class="text-gray-400"># docker-compose.yml에서 설정한 환경 변수 (최우선)</span>
|
|
<span class="text-green-400">environment:</span>
|
|
- <span class="text-yellow-300">DB_HOST=sam-mysql-1</span> <span class="text-gray-400"># ← 이 값이 .env의 DB_HOST를 덮어씀</span>
|
|
- <span class="text-yellow-300">DB_PORT=3306</span>
|
|
- <span class="text-yellow-300">DB_DATABASE=samdb</span>
|
|
|
|
<span class="text-gray-400"># 프로젝트 .env 파일의 DB_HOST=127.0.0.1 은 무시됨</span></pre>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 5-3. 로컬 vs 서버 -->
|
|
<div id="env-local-server" class="scroll-mt-20">
|
|
<h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
|
|
<span class="w-2 h-2 bg-blue-400 rounded-full"></span>
|
|
로컬 vs 서버 .env 차이점
|
|
</h3>
|
|
<div class="mb-5 bg-gray-50 rounded-xl p-4 border flex justify-center academy-img-wrap">
|
|
<img src="{{ asset('images/academy/docker-environment/9.svg') }}" alt="로컬 vs 서버 환경 비교"
|
|
class="rounded-lg cursor-pointer academy-img-hover"
|
|
style="max-height: 280px; width: auto;"
|
|
onclick="openLightbox(this)">
|
|
</div>
|
|
<p class="text-xs text-gray-400 mb-4 text-center">로컬(Docker) vs 서버(직접 설치) 환경 비교</p>
|
|
|
|
<div class="overflow-x-auto">
|
|
<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>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="text-xs text-gray-700">
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-medium">APP_ENV</td>
|
|
<td class="py-2 px-3"><code class="bg-blue-100 px-1 rounded">local</code></td>
|
|
<td class="py-2 px-3"><code class="bg-green-100 px-1 rounded">production</code></td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-medium">APP_DEBUG</td>
|
|
<td class="py-2 px-3"><code class="bg-blue-100 px-1 rounded">true</code></td>
|
|
<td class="py-2 px-3"><code class="bg-green-100 px-1 rounded">false</code></td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-medium">DB_HOST</td>
|
|
<td class="py-2 px-3"><code class="bg-blue-100 px-1 rounded">sam-mysql-1</code> (컨테이너명)</td>
|
|
<td class="py-2 px-3"><code class="bg-green-100 px-1 rounded">127.0.0.1</code> (로컬호스트)</td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-medium">DB_PASSWORD</td>
|
|
<td class="py-2 px-3"><code class="bg-blue-100 px-1 rounded">sampass</code> (개발용)</td>
|
|
<td class="py-2 px-3"><code class="bg-green-100 px-1 rounded">****</code> (운영용 복잡 비밀번호)</td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-medium">APP_URL</td>
|
|
<td class="py-2 px-3"><code class="bg-blue-100 px-1 rounded">https://mng.sam.kr</code></td>
|
|
<td class="py-2 px-3"><code class="bg-green-100 px-1 rounded">https://mng.sam.kr</code></td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-medium">실행 방법</td>
|
|
<td class="py-2 px-3"><code class="bg-blue-100 px-1 rounded">docker exec</code></td>
|
|
<td class="py-2 px-3"><code class="bg-green-100 px-1 rounded">직접 명령 실행</code></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="bg-amber-50 rounded-lg p-4 border border-amber-100 mt-4">
|
|
<p class="font-semibold text-amber-800 mb-1">핵심 차이: DB_HOST</p>
|
|
<p class="text-xs text-amber-900">
|
|
로컬에서는 Docker 네트워크 안에서 컨테이너 이름(<code class="bg-amber-100 px-1 rounded">sam-mysql-1</code>)으로 DB에 접근하고,
|
|
서버에서는 MySQL이 같은 서버에 직접 설치되어 있으므로 <code class="bg-amber-100 px-1 rounded">127.0.0.1</code>로 접근한다.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="bg-indigo-50 rounded-lg p-4 border border-indigo-200 mt-4">
|
|
<p class="font-semibold text-indigo-800 mb-1">서버 환경 구조 (Docker 없음)</p>
|
|
<p class="text-xs text-indigo-900 mb-2">
|
|
운영 서버에는 Docker가 설치되어 있지 않다. 서버 스펙이 제한적이라(2코어/3.8GB RAM) Docker의 오버헤드를 감당하기 어렵기 때문이다.
|
|
</p>
|
|
<div class="bg-gray-900 rounded p-3">
|
|
<pre class="text-xs font-mono leading-relaxed"><span class="text-gray-400"># 운영 서버 구조 (네이티브 설치)</span>
|
|
<span class="text-green-400">운영 서버 (114.203.209.83)</span>
|
|
<span class="text-yellow-300">├── Nginx</span> <span class="text-gray-400">← 웹서버 (직접 설치)</span>
|
|
<span class="text-yellow-300">├── PHP-FPM</span> <span class="text-gray-400">← PHP 처리 (직접 설치)</span>
|
|
<span class="text-yellow-300">├── MySQL</span> <span class="text-gray-400">← DB (직접 설치)</span>
|
|
<span class="text-cyan-300">├── /home/webservice/mng</span> <span class="text-gray-400">← MNG 앱</span>
|
|
<span class="text-cyan-300">├── /home/webservice/api</span> <span class="text-gray-400">← API 앱</span>
|
|
<span class="text-cyan-300">└── /home/webservice/react</span> <span class="text-gray-400">← React 앱</span></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{{-- ============================================================ --}}
|
|
{{-- 6. 실전 명령어 & 트러블슈팅 --}}
|
|
{{-- ============================================================ --}}
|
|
<section id="commands" class="scroll-mt-20">
|
|
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
|
|
<h2 class="text-xl font-bold text-gray-800 mb-6 flex items-center gap-3">
|
|
<span class="w-8 h-8 bg-blue-500 text-white rounded-lg flex items-center justify-center text-sm font-bold">6</span>
|
|
실전 명령어 & 트러블슈팅
|
|
</h2>
|
|
|
|
<!-- 6-0. 로컬 vs 서버 명령어 비교 -->
|
|
<div id="cmd-compare" 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>
|
|
로컬(Docker) vs 서버(네이티브) 명령어 비교
|
|
</h3>
|
|
<div class="bg-indigo-50 rounded-lg p-4 border border-indigo-100 mb-4">
|
|
<p class="text-sm text-indigo-900 leading-relaxed">
|
|
로컬에서는 <code class="bg-indigo-100 px-1 rounded">docker exec</code>를 통해 컨테이너 안에서 명령을 실행하고,
|
|
서버에서는 Docker 없이 <strong>직접 명령을 실행</strong>한다.
|
|
</p>
|
|
</div>
|
|
<div class="overflow-x-auto">
|
|
<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>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="text-xs text-gray-700">
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-medium">artisan 실행</td>
|
|
<td class="py-2 px-3"><code class="bg-blue-100 px-1 rounded">docker exec sam-api-1 php artisan ...</code></td>
|
|
<td class="py-2 px-3"><code class="bg-green-100 px-1 rounded">cd /home/webservice/api && php artisan ...</code></td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-medium">composer</td>
|
|
<td class="py-2 px-3"><code class="bg-blue-100 px-1 rounded">docker exec sam-api-1 composer install</code></td>
|
|
<td class="py-2 px-3"><code class="bg-green-100 px-1 rounded">cd /home/webservice/api && composer install</code></td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-medium">마이그레이션</td>
|
|
<td class="py-2 px-3"><code class="bg-blue-100 px-1 rounded">docker exec sam-api-1 php artisan migrate</code></td>
|
|
<td class="py-2 px-3"><code class="bg-green-100 px-1 rounded">cd /home/webservice/api && php artisan migrate</code></td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-medium">캐시 클리어</td>
|
|
<td class="py-2 px-3"><code class="bg-blue-100 px-1 rounded">docker exec sam-mng-1 php artisan config:clear</code></td>
|
|
<td class="py-2 px-3"><code class="bg-green-100 px-1 rounded">cd /home/webservice/mng && php artisan config:clear</code></td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-medium">로그 확인</td>
|
|
<td class="py-2 px-3"><code class="bg-blue-100 px-1 rounded">docker logs sam-api-1 --tail 50</code></td>
|
|
<td class="py-2 px-3"><code class="bg-green-100 px-1 rounded">tail -50 /home/webservice/api/storage/logs/laravel.log</code></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 6-1. 자주 쓰는 명령어 -->
|
|
<div id="cmd-basic" 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>
|
|
자주 쓰는 로컬 Docker 명령어
|
|
</h3>
|
|
<div class="mb-5 bg-gray-50 rounded-xl p-4 border flex justify-center academy-img-wrap">
|
|
<img src="{{ asset('images/academy/docker-environment/10.svg') }}" alt="명령어 치트시트"
|
|
class="rounded-lg cursor-pointer academy-img-hover"
|
|
style="max-height: 280px; width: auto;"
|
|
onclick="openLightbox(this)">
|
|
</div>
|
|
<p class="text-xs text-gray-400 mb-4 text-center">Docker 명령어 치트시트</p>
|
|
|
|
<div class="space-y-3">
|
|
<div class="bg-green-50 rounded-lg p-4 border border-green-200">
|
|
<p class="font-semibold text-green-800 text-sm mb-3">안전한 명령어 (자유롭게 사용)</p>
|
|
<div class="space-y-2">
|
|
<div class="bg-gray-900 rounded p-3">
|
|
<pre class="text-xs font-mono"><span class="text-gray-400"># 컨테이너 상태 확인</span>
|
|
<span class="text-green-400">docker ps</span>
|
|
|
|
<span class="text-gray-400"># 컨테이너 로그 확인</span>
|
|
<span class="text-green-400">docker logs sam-api-1 --tail 50</span>
|
|
|
|
<span class="text-gray-400"># 컨테이너 안에서 명령 실행</span>
|
|
<span class="text-green-400">docker exec sam-api-1 php artisan --version</span>
|
|
<span class="text-green-400">docker exec sam-mng-1 php artisan route:list</span>
|
|
|
|
<span class="text-gray-400"># 캐시 클리어 (설정 변경 후 필수)</span>
|
|
<span class="text-green-400">docker exec sam-api-1 php artisan config:clear</span>
|
|
<span class="text-green-400">docker exec sam-api-1 php artisan cache:clear</span>
|
|
<span class="text-green-400">docker exec sam-mng-1 php artisan config:clear</span></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-amber-50 rounded-lg p-4 border border-amber-200">
|
|
<p class="font-semibold text-amber-800 text-sm mb-3">주의가 필요한 명령어</p>
|
|
<div class="bg-gray-900 rounded p-3">
|
|
<pre class="text-xs font-mono"><span class="text-gray-400"># 모든 서비스 시작</span>
|
|
<span class="text-yellow-300">docker compose up -d</span>
|
|
|
|
<span class="text-gray-400"># 특정 서비스 재시작</span>
|
|
<span class="text-yellow-300">docker compose restart api</span>
|
|
|
|
<span class="text-gray-400"># 의존성 설치 (composer.json 변경 시)</span>
|
|
<span class="text-yellow-300">docker exec sam-api-1 composer install</span>
|
|
|
|
<span class="text-gray-400"># DB 마이그레이션 (반드시 API에서만!)</span>
|
|
<span class="text-yellow-300">docker exec sam-api-1 php artisan migrate</span></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 6-2. 주의사항 -->
|
|
<div id="cmd-caution" 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>
|
|
SAM 프로젝트 주의사항
|
|
</h3>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
|
<div class="bg-red-50 rounded-lg p-4 border-2 border-red-200">
|
|
<div class="flex items-center gap-2 mb-2">
|
|
<span class="text-red-500 font-bold text-lg">✕</span>
|
|
<p class="font-semibold text-red-800 text-sm">마이그레이션은 API에서만</p>
|
|
</div>
|
|
<p class="text-xs text-red-700">MNG에서 마이그레이션을 실행하면 안 된다. DB 스키마 관리는 API 프로젝트가 담당한다.</p>
|
|
<div class="bg-red-100 rounded p-2 mt-2">
|
|
<p class="text-xs text-red-800"><code>docker exec sam-mng-1 php artisan migrate</code> — 금지!</p>
|
|
</div>
|
|
</div>
|
|
<div class="bg-red-50 rounded-lg p-4 border-2 border-red-200">
|
|
<div class="flex items-center gap-2 mb-2">
|
|
<span class="text-red-500 font-bold text-lg">✕</span>
|
|
<p class="font-semibold text-red-800 text-sm">메뉴 시더 실행 금지</p>
|
|
</div>
|
|
<p class="text-xs text-red-700">메뉴 시더를 실행하면 부서별 권한 설정이 초기화된다. 메뉴 변경은 tinker로 개별 처리한다.</p>
|
|
<div class="bg-red-100 rounded p-2 mt-2">
|
|
<p class="text-xs text-red-800"><code>php artisan db:seed --class=MngMenuSeeder</code> — 금지!</p>
|
|
</div>
|
|
</div>
|
|
<div class="bg-red-50 rounded-lg p-4 border-2 border-red-200">
|
|
<div class="flex items-center gap-2 mb-2">
|
|
<span class="text-red-500 font-bold text-lg">✕</span>
|
|
<p class="font-semibold text-red-800 text-sm">서버에서 npm run build 금지</p>
|
|
</div>
|
|
<p class="text-xs text-red-700">서버 메모리 부족으로 빌드 실패. React 빌드는 반드시 로컬에서 수행한다.</p>
|
|
<div class="bg-red-100 rounded p-2 mt-2">
|
|
<p class="text-xs text-red-800">서버(2코어/3.8GB) → 빌드 시 OOM 위험</p>
|
|
</div>
|
|
</div>
|
|
<div class="bg-green-50 rounded-lg p-4 border-2 border-green-200">
|
|
<div class="flex items-center gap-2 mb-2">
|
|
<span class="text-green-500 font-bold text-lg">✓</span>
|
|
<p class="font-semibold text-green-800 text-sm">올바른 작업 흐름</p>
|
|
</div>
|
|
<p class="text-xs text-green-700">로컬에서 코드 수정 → git commit → git push → 서버에서 git pull → composer install → config:clear</p>
|
|
<div class="bg-green-100 rounded p-2 mt-2">
|
|
<p class="text-xs text-green-800">코드 변경은 항상 로컬에서! 서버는 배포만!</p>
|
|
<p class="text-xs text-green-700 mt-1">서버에서는 Docker 없이 직접 명령 실행 (<code class="bg-green-200 px-1 rounded">php artisan</code>, <code class="bg-green-200 px-1 rounded">composer</code>)</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 6-3. 트러블슈팅 -->
|
|
<div id="cmd-trouble" class="scroll-mt-20">
|
|
<h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
|
|
<span class="w-2 h-2 bg-blue-400 rounded-full"></span>
|
|
문제 발생 시 대처법
|
|
</h3>
|
|
<div class="space-y-4">
|
|
<div class="bg-gray-50 rounded-lg p-4 border">
|
|
<p class="font-semibold text-gray-800 text-sm mb-2">"컨테이너가 안 뜬다"</p>
|
|
<div class="bg-gray-900 rounded p-3 mb-2">
|
|
<pre class="text-xs font-mono"><span class="text-gray-400"># 1. 상태 확인</span>
|
|
<span class="text-green-400">docker ps -a</span> <span class="text-gray-400"># 종료된 컨테이너도 표시</span>
|
|
|
|
<span class="text-gray-400"># 2. 에러 로그 확인</span>
|
|
<span class="text-green-400">docker logs sam-api-1 --tail 100</span>
|
|
|
|
<span class="text-gray-400"># 3. 서비스 재시작</span>
|
|
<span class="text-yellow-300">cd /home/aweso/sam/docker && docker compose restart api</span></pre>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-gray-50 rounded-lg p-4 border">
|
|
<p class="font-semibold text-gray-800 text-sm mb-2">"설정을 변경했는데 반영이 안 된다"</p>
|
|
<div class="bg-gray-900 rounded p-3 mb-2">
|
|
<pre class="text-xs font-mono"><span class="text-gray-400"># .env 변경 후 반드시 캐시 클리어</span>
|
|
<span class="text-green-400">docker exec sam-api-1 php artisan config:clear</span>
|
|
<span class="text-green-400">docker exec sam-api-1 php artisan cache:clear</span>
|
|
<span class="text-green-400">docker exec sam-api-1 php artisan view:clear</span></pre>
|
|
</div>
|
|
<p class="text-xs text-gray-600">Laravel은 설정을 캐시하므로, .env 변경 후 캐시를 비워야 새 값이 적용된다.</p>
|
|
</div>
|
|
|
|
<div class="bg-gray-50 rounded-lg p-4 border">
|
|
<p class="font-semibold text-gray-800 text-sm mb-2">"Class not found 에러"</p>
|
|
<div class="bg-gray-900 rounded p-3 mb-2">
|
|
<pre class="text-xs font-mono"><span class="text-gray-400"># Composer 오토로드 재생성</span>
|
|
<span class="text-green-400">docker exec sam-api-1 composer dump-autoload</span>
|
|
|
|
<span class="text-gray-400"># 또는 패키지 전체 재설치</span>
|
|
<span class="text-yellow-300">docker exec sam-api-1 composer install</span></pre>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-gray-50 rounded-lg p-4 border">
|
|
<p class="font-semibold text-gray-800 text-sm mb-2">"Permission denied 에러"</p>
|
|
<div class="bg-gray-900 rounded p-3 mb-2">
|
|
<pre class="text-xs font-mono"><span class="text-gray-400"># storage/logs 권한 확인</span>
|
|
<span class="text-green-400">docker exec sam-api-1 ls -la storage/logs/</span>
|
|
|
|
<span class="text-gray-400"># 권한 수정 (컨테이너 내부)</span>
|
|
<span class="text-yellow-300">docker exec sam-api-1 chmod -R 775 storage/</span>
|
|
<span class="text-yellow-300">docker exec sam-api-1 chown -R www-data:www-data storage/</span></pre>
|
|
</div>
|
|
</div>
|
|
</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();
|
|
}
|
|
});
|
|
})();
|
|
</script>
|
|
|
|
@include('components.academy-glossary', ['domain' => 'docker-environment'])
|
|
@endsection
|