feat: [academy] 프론트엔드 개발 백과사전 SVG 이미지 + 도움말 풍선 추가

- 섹션별 SVG 일러스트 10개를 주요 위치에 삽입 (5~10.svg)
- 도움말 풍선(?) 트리거 8개 추가 (핵심 개념 설명)
- toggleBalloon() JavaScript 함수 및 외부 클릭 닫기 구현
This commit is contained in:
김보곤
2026-02-23 10:51:01 +09:00
parent 72d699ea18
commit 2c92c49980

View File

@@ -84,6 +84,82 @@
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: #ede9fe;
color: #7c3aed;
font-size: 11px;
font-weight: 700;
cursor: pointer;
border: 1.5px solid #c4b5fd;
vertical-align: middle;
margin-left: 4px;
transition: all 0.15s ease;
position: relative;
user-select: none;
}
.help-balloon-trigger:hover {
background: #7c3aed;
color: white;
border-color: #6d28d9;
}
.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;
@@ -122,19 +198,10 @@
<p class="text-sm" style="color: #cbd5e1;">HTML, CSS, JavaScript부터 프레임워크, 성능 최적화까지 비개발자도 이해하는 프론트엔드 가이드</p>
</div>
<div class="shrink-0" style="width: 240px; padding: 1.5rem;">
<div class="overflow-hidden rounded-xl flex items-center justify-center" style="background: rgba(139,92,246,0.15); height: 160px;">
<svg style="width: 120px; height: 120px;" viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="15" y="20" width="90" height="65" rx="6" stroke="#c4b5fd" stroke-width="2.5" fill="none"/>
<rect x="25" y="30" width="35" height="8" rx="2" fill="#a78bfa" opacity="0.6"/>
<rect x="25" y="42" width="55" height="5" rx="1.5" fill="#7c3aed" opacity="0.4"/>
<rect x="25" y="50" width="45" height="5" rx="1.5" fill="#7c3aed" opacity="0.3"/>
<rect x="25" y="58" width="50" height="5" rx="1.5" fill="#7c3aed" opacity="0.2"/>
<rect x="65" y="30" width="18" height="18" rx="3" fill="#8b5cf6" opacity="0.5"/>
<line x1="15" y1="72" x2="105" y2="72" stroke="#c4b5fd" stroke-width="1.5"/>
<circle cx="60" cy="78" r="2.5" fill="#c4b5fd"/>
<rect x="40" y="85" width="40" height="4" rx="2" fill="#c4b5fd" opacity="0.5"/>
<text x="60" y="105" text-anchor="middle" fill="#c4b5fd" font-size="10" font-family="monospace">&lt;/&gt;</text>
</svg>
<div class="overflow-hidden rounded-xl">
<img src="{{ asset('images/academy/frontend-dev/1.svg') }}" alt="코드 에디터와 브라우저 프리뷰"
class="w-full rounded-xl cursor-pointer academy-img-hover"
onclick="openLightbox(this)">
</div>
</div>
</div>
@@ -229,6 +296,7 @@
<h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
<span class="w-2 h-2 bg-purple-400 rounded-full"></span>
프론트엔드 vs 백엔드
<span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon">프론트엔드는 사용자의 브라우저에서 실행되는 코드(HTML, CSS, JS)이고, 백엔드는 서버에서 실행되는 코드(PHP, Python ). 둘은 API를 통해 데이터를 주고받는다.</span></span>
</h3>
<div class="bg-purple-50 rounded-lg p-5 border border-purple-100 mb-4">
<p class="text-sm text-purple-900 leading-relaxed">
@@ -294,13 +362,20 @@
<p class="text-yellow-700">전기 배선<br>스위치를 누르면 조명이 켜진다</p>
</div>
</div>
<div class="bg-purple-50 rounded-lg p-4 border border-purple-100">
<div class="bg-purple-50 rounded-lg p-4 border border-purple-100 mb-4">
<p class="text-xs text-purple-800 leading-relaxed">
<strong>SAM 시스템의 프론트엔드:</strong>
SAM도 프론트엔드와 백엔드로 분리되어 있다. MNG(관리자 ) Blade + HTMX로, React 앱은 Next.js로 화면을 구성하고,
API 서버(Laravel) 데이터를 처리한다.
</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/frontend-dev/2.svg') }}" alt="프론트엔드 vs 백엔드 비교 다이어그램"
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>
</section>
@@ -413,6 +488,7 @@
<h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
<span class="w-2 h-2 bg-purple-400 rounded-full"></span>
시맨틱 태그
<span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon">시맨틱(Semantic) "의미를 가진"이라는 뜻이다. &lt;div&gt; 대신 &lt;header&gt;, &lt;nav&gt;, &lt;main&gt; 등을 사용하면 검색엔진과 스크린리더가 페이지 구조를 이해할 있다.</span></span>
</h3>
<p class="text-sm text-gray-700 mb-3">
시맨틱(Semantic) 태그는 "의미가 있는" 태그다. <code>&lt;div&gt;</code> 쓰면 검색엔진이나 스크린리더가 내용을 이해하기 어렵다.
@@ -443,6 +519,13 @@
브라우저는 HTML을 읽어 위와 같은 트리(나무) 구조로 변환한다. 이를 <strong>DOM(Document Object Model)</strong>이라 한다.
</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/frontend-dev/3.svg') }}" alt="HTML DOM 트리 구조 다이어그램"
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">DOM 트리 HTML 문서의 구조</p>
</div>
</div>
</section>
@@ -462,6 +545,7 @@
<h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
<span class="w-2 h-2 bg-purple-400 rounded-full"></span>
선택자와 박스모델
<span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon">선택자(Selector) "어떤 HTML 요소에 스타일을 적용할지" 지정하는 규칙이다. 박스모델은 모든 요소가 content padding border margin 4 상자로 구성된다는 개념이다.</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>
@@ -495,11 +579,20 @@
</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/frontend-dev/4.svg') }}" alt="CSS 박스모델 다이어그램"
class="rounded-lg cursor-pointer academy-img-hover"
style="max-height: 300px; width: auto;"
onclick="openLightbox(this)">
</div>
<p class="text-xs text-gray-400 mb-6 text-center">CSS 박스모델 margin, border, padding, content</p>
<!-- 3-2. Flexbox와 Grid -->
<div id="css-layout" 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-purple-400 rounded-full"></span>
Flexbox와 Grid
<span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon">Flexbox는 방향(가로 또는 세로)으로 요소를 나열할 사용하고, Grid는 가로+세로 2차원으로 배치할 사용한다. SAM에서는 Tailwind CSS의 flex/grid 클래스를 활용한다.</span></span>
</h3>
<p class="text-sm text-gray-700 mb-3">
현대 CSS 레이아웃의 기둥이다. 요소를 가로/세로로 배치하는 강력한 도구다.
@@ -581,6 +674,7 @@
<h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
<span class="w-2 h-2 bg-purple-400 rounded-full"></span>
DOM 조작과 비동기 통신
<span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon">DOM 조작은 JavaScript로 화면의 내용을 동적으로 변경하는 것이다. 비동기 통신(fetch) 페이지 새로고침 없이 서버에서 데이터를 가져오는 기술이다.</span></span>
</h3>
<p class="text-sm text-gray-700 mb-3">
JavaScript는 DOM(문서 객체) 조작하여 화면을 동적으로 변경할 있다.
@@ -602,6 +696,15 @@
<code>fetch()</code>, <code>async/await</code>, <code>Promise</code> 역할을 한다.
</p>
</div>
<!-- SVG 일러스트: JavaScript 이벤트 흐름 -->
<div class="mt-5 bg-gray-50 rounded-xl p-4 border flex justify-center academy-img-wrap">
<img src="{{ asset('images/academy/frontend-dev/5.svg') }}" alt="JavaScript 이벤트 흐름과 DOM 조작"
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">JavaScript 이벤트 처리와 DOM 조작 흐름</p>
</div>
</div>
</section>
@@ -621,6 +724,7 @@
<h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
<span class="w-2 h-2 bg-purple-400 rounded-full"></span>
프레임워크 vs 라이브러리
<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">비유: 반조립 가구 vs 공구 세트</p>
@@ -720,6 +824,15 @@
</div>
</div>
</div>
<!-- SVG 일러스트: 프레임워크 비교 -->
<div class="mt-5 bg-gray-50 rounded-xl p-4 border flex justify-center academy-img-wrap">
<img src="{{ asset('images/academy/frontend-dev/6.svg') }}" alt="프레임워크 비교: React, Vue, Angular"
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">React / Vue / Angular 비교와 SAM 아키텍처</p>
</div>
</div>
</section>
@@ -738,6 +851,7 @@
<h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
<span class="w-2 h-2 bg-purple-400 rounded-full"></span>
브레이크포인트와 모바일 퍼스트
<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>
@@ -823,6 +937,15 @@
줄이 없으면 모바일에서 데스크톱 크기로 축소되어 보인다. 모든 반응형 페이지의 필수 태그다.
</p>
</div>
<!-- SVG 일러스트: 반응형 디자인 -->
<div class="mt-5 bg-gray-50 rounded-xl p-4 border flex justify-center academy-img-wrap">
<img src="{{ asset('images/academy/frontend-dev/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>
</section>
@@ -841,6 +964,7 @@
<h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
<span class="w-2 h-2 bg-purple-400 rounded-full"></span>
렌더링 파이프라인
<span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon">렌더링 파이프라인은 브라우저가 HTML/CSS/JS 코드를 화면의 픽셀로 변환하는 일련의 처리 과정이다.</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>
@@ -917,6 +1041,15 @@
</p>
</div>
</details>
<!-- SVG 일러스트: 브라우저 렌더링 파이프라인 -->
<div class="mt-5 bg-gray-50 rounded-xl p-4 border flex justify-center academy-img-wrap">
<img src="{{ asset('images/academy/frontend-dev/8.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">HTML 코드가 화면의 픽셀로 변환되는 6단계 과정</p>
</div>
</div>
</section>
@@ -935,6 +1068,7 @@
<h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
<span class="w-2 h-2 bg-purple-400 rounded-full"></span>
REST API와 HTTP 메서드
<span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon">REST API는 URL과 HTTP 메서드(GET/POST/PUT/DELETE) 조합하여 서버 자원을 조작하는 규약이다. 웹에서 가장 널리 쓰이는 API 설계 방식이다.</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>
@@ -1047,6 +1181,15 @@
SAM React 앱은 <code>api.sam-erp.com</code> API 서버와 통신한다. MNG에서는 HTMX가 서버에 요청을 보내고 HTML 조각을 받아 화면을 업데이트한다.
</p>
</div>
<!-- SVG 일러스트: API 요청과 응답 -->
<div class="mt-5 bg-gray-50 rounded-xl p-4 border flex justify-center academy-img-wrap">
<img src="{{ asset('images/academy/frontend-dev/9.svg') }}" alt="API 요청과 응답 흐름"
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">프론트엔드와 백엔드 API 요청/응답 흐름</p>
</div>
</div>
</section>
@@ -1065,6 +1208,7 @@
<h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
<span class="w-2 h-2 bg-purple-400 rounded-full"></span>
핵심 도구와 개발 흐름
<span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon">프론트엔드 개발에는 코드 에디터(VS Code), 패키지 관리자(npm), 버전 관리(Git), 번들러(Vite) 여러 도구를 조합하여 사용한다.</span></span>
</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-3 text-xs mb-4">
<div class="bg-blue-50 rounded-lg p-4 border border-blue-200">
@@ -1133,6 +1277,15 @@
</div>
</div>
</div>
<!-- SVG 일러스트: 개발 워크플로우 -->
<div class="mt-5 bg-gray-50 rounded-xl p-4 border flex justify-center academy-img-wrap">
<img src="{{ asset('images/academy/frontend-dev/10.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>
</section>
@@ -1151,6 +1304,7 @@
<h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
<span class="w-2 h-2 bg-purple-400 rounded-full"></span>
Lighthouse와 Core Web Vitals
<span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon">Lighthouse는 Google이 제공하는 성능 측정 도구이다. Core Web Vitals는 LCP(로딩 속도), FID(반응 속도), CLS(레이아웃 안정성) 3가지 핵심 지표이다.</span></span>
</h3>
<p class="text-sm text-gray-700 mb-3">
<strong>Lighthouse</strong> Google이 제공하는 성능 측정 도구다.
@@ -1339,6 +1493,28 @@ function 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>