diff --git a/resources/views/academy/frontend-dev.blade.php b/resources/views/academy/frontend-dev.blade.php index fd3de1d1..4a1078a1 100644 --- a/resources/views/academy/frontend-dev.blade.php +++ b/resources/views/academy/frontend-dev.blade.php @@ -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 @@
HTML, CSS, JavaScript부터 프레임워크, 성능 최적화까지 — 비개발자도 이해하는 프론트엔드 가이드
@@ -294,13 +362,20 @@
전기 배선
스위치를 누르면 조명이 켜진다
SAM 시스템의 프론트엔드: SAM도 프론트엔드와 백엔드로 분리되어 있다. MNG(관리자 웹)는 Blade + HTMX로, React 앱은 Next.js로 화면을 구성하고, API 서버(Laravel)가 데이터를 처리한다.
프론트엔드와 백엔드 — 식당의 홀과 주방
시맨틱(Semantic) 태그는 "의미가 있는" 태그다. <div>만 쓰면 검색엔진이나 스크린리더가 내용을 이해하기 어렵다.
@@ -443,6 +519,13 @@
브라우저는 HTML을 읽어 위와 같은 트리(나무) 구조로 변환한다. 이를 DOM(Document Object Model)이라 한다.
DOM 트리 — HTML 문서의 구조
@@ -462,6 +545,7 @@비유: 인테리어 디자인
@@ -495,11 +579,20 @@CSS 박스모델 — margin, border, padding, content
+현대 CSS 레이아웃의 두 기둥이다. 요소를 가로/세로로 배치하는 강력한 도구다. @@ -581,6 +674,7 @@
JavaScript는 DOM(문서 객체)을 조작하여 화면을 동적으로 변경할 수 있다.
@@ -602,6 +696,15 @@
fetch(), async/await, Promise가 이 역할을 한다.
JavaScript 이벤트 처리와 DOM 조작 흐름
@@ -621,6 +724,7 @@비유: 반조립 가구 vs 공구 세트
@@ -720,6 +824,15 @@React / Vue / Angular 비교와 SAM 아키텍처
@@ -738,6 +851,7 @@비유: 물은 그릇 모양에 따라 변한다
@@ -823,6 +937,15 @@ — 이 한 줄이 없으면 모바일에서 데스크톱 크기로 축소되어 보인다. 모든 반응형 페이지의 필수 태그다.모바일 / 태블릿 / 데스크톱 반응형 레이아웃
@@ -841,6 +964,7 @@비유: 통역사
@@ -917,6 +1041,15 @@HTML 코드가 화면의 픽셀로 변환되는 6단계 과정
@@ -935,6 +1068,7 @@비유: 식당의 주문서
@@ -1047,6 +1181,15 @@ SAM React 앱은api.sam-erp.com의 API 서버와 통신한다. MNG에서는 HTMX가 서버에 요청을 보내고 HTML 조각을 받아 화면을 업데이트한다.
프론트엔드와 백엔드 간 API 요청/응답 흐름
@@ -1065,6 +1208,7 @@코드 작성부터 배포까지 — 개발 워크플로우
@@ -1151,6 +1304,7 @@Lighthouse는 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'); + }); + } + }); })();