1523 lines
104 KiB
PHP
1523 lines
104 KiB
PHP
@extends('layouts.app')
|
|
|
|
@section('title', '프론트엔드 개발')
|
|
|
|
@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);
|
|
}
|
|
|
|
/* 도움말 풍선 */
|
|
.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;
|
|
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, #2e1065 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: #c4b5fd;">
|
|
<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;">프론트엔드 개발</span>
|
|
</div>
|
|
<h1 class="text-3xl font-bold mb-2" style="color: #ffffff;">프론트엔드 개발</h1>
|
|
<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">
|
|
<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>
|
|
</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-purple-50 border border-purple-200 rounded-xl p-5">
|
|
<h2 class="font-semibold text-purple-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-frontend" class="block text-purple-700 hover:text-purple-900 py-1 font-medium">1. 프론트엔드란?</a>
|
|
<a href="#frontend-vs-backend" class="block text-purple-600 hover:text-purple-800 py-0.5 pl-3">프론트 vs 백엔드</a>
|
|
<a href="#web-trio" class="block text-purple-600 hover:text-purple-800 py-0.5 pl-3">웹의 3요소</a>
|
|
|
|
<a href="#html" class="block text-purple-700 hover:text-purple-900 py-1 font-medium mt-2">2. HTML</a>
|
|
<a href="#html-tags" class="block text-purple-600 hover:text-purple-800 py-0.5 pl-3">태그와 구조</a>
|
|
<a href="#html-semantic" class="block text-purple-600 hover:text-purple-800 py-0.5 pl-3">시맨틱 태그</a>
|
|
|
|
<a href="#css" class="block text-purple-700 hover:text-purple-900 py-1 font-medium mt-2">3. CSS</a>
|
|
<a href="#css-selector" class="block text-purple-600 hover:text-purple-800 py-0.5 pl-3">선택자와 박스모델</a>
|
|
<a href="#css-layout" class="block text-purple-600 hover:text-purple-800 py-0.5 pl-3">Flexbox와 Grid</a>
|
|
|
|
<a href="#javascript" class="block text-purple-700 hover:text-purple-900 py-1 font-medium mt-2">4. JavaScript</a>
|
|
<a href="#js-basics" class="block text-purple-600 hover:text-purple-800 py-0.5 pl-3">변수와 함수</a>
|
|
<a href="#js-dom" class="block text-purple-600 hover:text-purple-800 py-0.5 pl-3">DOM 조작</a>
|
|
|
|
<a href="#frameworks" class="block text-purple-700 hover:text-purple-900 py-1 font-medium mt-2">5. 프레임워크</a>
|
|
<a href="#fw-compare" class="block text-purple-600 hover:text-purple-800 py-0.5 pl-3">React vs Vue</a>
|
|
<a href="#fw-sam" class="block text-purple-600 hover:text-purple-800 py-0.5 pl-3">SAM의 선택</a>
|
|
|
|
<a href="#responsive" class="block text-purple-700 hover:text-purple-900 py-1 font-medium mt-2">6. 반응형 디자인</a>
|
|
<a href="#rwd-breakpoint" class="block text-purple-600 hover:text-purple-800 py-0.5 pl-3">브레이크포인트</a>
|
|
|
|
<a href="#browser" class="block text-purple-700 hover:text-purple-900 py-1 font-medium mt-2">7. 브라우저 동작</a>
|
|
<a href="#browser-render" class="block text-purple-600 hover:text-purple-800 py-0.5 pl-3">렌더링 과정</a>
|
|
|
|
<a href="#api-integration" class="block text-purple-700 hover:text-purple-900 py-1 font-medium mt-2">8. API 연동</a>
|
|
<a href="#api-http" class="block text-purple-600 hover:text-purple-800 py-0.5 pl-3">HTTP 메서드</a>
|
|
|
|
<a href="#tooling" class="block text-purple-700 hover:text-purple-900 py-1 font-medium mt-2">9. 개발 도구</a>
|
|
<a href="#tool-workflow" class="block text-purple-600 hover:text-purple-800 py-0.5 pl-3">워크플로우</a>
|
|
|
|
<a href="#performance" class="block text-purple-700 hover:text-purple-900 py-1 font-medium mt-2">10. 성능 최적화</a>
|
|
<a href="#perf-vitals" class="block text-purple-600 hover:text-purple-800 py-0.5 pl-3">Core Web Vitals</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<!-- 우측 콘텐츠 -->
|
|
<div class="flex-1 min-w-0 space-y-10">
|
|
|
|
<!-- 모바일 목차 -->
|
|
<div class="lg:hidden bg-purple-50 border border-purple-200 rounded-xl p-4 mb-6">
|
|
<details>
|
|
<summary class="font-semibold text-purple-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-frontend" class="block text-purple-700 hover:text-purple-900 py-1">1. 프론트엔드란?</a>
|
|
<a href="#html" class="block text-purple-700 hover:text-purple-900 py-1">2. HTML — 웹의 뼈대</a>
|
|
<a href="#css" class="block text-purple-700 hover:text-purple-900 py-1">3. CSS — 웹의 옷</a>
|
|
<a href="#javascript" class="block text-purple-700 hover:text-purple-900 py-1">4. JavaScript — 웹의 두뇌</a>
|
|
<a href="#frameworks" class="block text-purple-700 hover:text-purple-900 py-1">5. 프레임워크와 라이브러리</a>
|
|
<a href="#responsive" class="block text-purple-700 hover:text-purple-900 py-1">6. 반응형 디자인</a>
|
|
<a href="#browser" class="block text-purple-700 hover:text-purple-900 py-1">7. 브라우저 동작 원리</a>
|
|
<a href="#api-integration" class="block text-purple-700 hover:text-purple-900 py-1">8. API 연동</a>
|
|
<a href="#tooling" class="block text-purple-700 hover:text-purple-900 py-1">9. 개발 도구와 워크플로우</a>
|
|
<a href="#performance" class="block text-purple-700 hover:text-purple-900 py-1">10. 성능 최적화</a>
|
|
</nav>
|
|
</details>
|
|
</div>
|
|
|
|
{{-- ============================================================ --}}
|
|
{{-- 1. 프론트엔드란? --}}
|
|
{{-- ============================================================ --}}
|
|
<section id="what-is-frontend" 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-purple-500 text-white rounded-lg flex items-center justify-center text-sm font-bold">1</span>
|
|
프론트엔드란?
|
|
</h2>
|
|
|
|
<!-- 1-1. 프론트엔드 vs 백엔드 -->
|
|
<div id="frontend-vs-backend" 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-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">
|
|
<strong>프론트엔드(Front-end)</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>이다. 손님이 메뉴판을 보고, 주문하고, 음식을 받는 공간이다.
|
|
백엔드는 <strong>주방</strong>이다. 손님은 보지 못하지만, 주문을 받아 요리하고 결과를 내보내는 곳이다.
|
|
홀이 예쁘고 편해야 손님이 만족하듯, 프론트엔드가 잘 만들어져야 사용자가 편하게 시스템을 쓸 수 있다.
|
|
</p>
|
|
</div>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-3 text-xs">
|
|
<div class="bg-purple-50 rounded-lg p-4 border border-purple-200 text-center">
|
|
<div class="mb-2">
|
|
<svg class="w-8 h-8 mx-auto text-purple-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" /></svg>
|
|
</div>
|
|
<p class="font-bold text-purple-800 mb-1">프론트엔드 (홀)</p>
|
|
<p class="text-purple-700">사용자가 보는 화면<br>HTML, CSS, JavaScript<br>버튼, 폼, 메뉴, 테이블</p>
|
|
</div>
|
|
<div class="bg-gray-50 rounded-lg p-4 border text-center">
|
|
<div class="mb-2">
|
|
<svg class="w-8 h-8 mx-auto text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2" /></svg>
|
|
</div>
|
|
<p class="font-bold text-gray-800 mb-1">백엔드 (주방)</p>
|
|
<p class="text-gray-600">서버에서 처리하는 로직<br>PHP, Python, Java<br>데이터 저장, 인증, 계산</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 1-2. 웹의 3요소 -->
|
|
<div id="web-trio" 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>
|
|
웹의 3요소
|
|
</h3>
|
|
<p class="text-sm text-gray-700 mb-4">
|
|
프론트엔드는 세 가지 기술로 구성된다. 이 셋은 항상 함께 작동한다.
|
|
</p>
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-3 text-xs mb-4">
|
|
<div class="bg-orange-50 rounded-lg p-4 border border-orange-200 text-center">
|
|
<div class="mb-2">
|
|
<svg class="w-8 h-8 mx-auto text-orange-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" /></svg>
|
|
</div>
|
|
<p class="font-bold text-orange-800 mb-1">HTML (구조)</p>
|
|
<p class="text-orange-700">건물의 뼈대<br>방, 거실, 주방의 위치를 정한다</p>
|
|
</div>
|
|
<div class="bg-blue-50 rounded-lg p-4 border border-blue-200 text-center">
|
|
<div class="mb-2">
|
|
<svg class="w-8 h-8 mx-auto text-blue-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01" /></svg>
|
|
</div>
|
|
<p class="font-bold text-blue-800 mb-1">CSS (디자인)</p>
|
|
<p class="text-blue-700">인테리어<br>벽지, 가구 배치, 조명을 결정한다</p>
|
|
</div>
|
|
<div class="bg-yellow-50 rounded-lg p-4 border border-yellow-200 text-center">
|
|
<div class="mb-2">
|
|
<svg class="w-8 h-8 mx-auto text-yellow-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" /></svg>
|
|
</div>
|
|
<p class="font-bold text-yellow-800 mb-1">JavaScript (동작)</p>
|
|
<p class="text-yellow-700">전기 배선<br>스위치를 누르면 조명이 켜진다</p>
|
|
</div>
|
|
</div>
|
|
<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>
|
|
|
|
{{-- ============================================================ --}}
|
|
{{-- 2. HTML — 웹의 뼈대 --}}
|
|
{{-- ============================================================ --}}
|
|
<section id="html" 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-purple-500 text-white rounded-lg flex items-center justify-center text-sm font-bold">2</span>
|
|
HTML — 웹의 뼈대
|
|
</h2>
|
|
|
|
<!-- 2-1. 태그와 구조 -->
|
|
<div id="html-tags" 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-purple-400 rounded-full"></span>
|
|
태그와 구조
|
|
</h3>
|
|
<div class="bg-amber-50 rounded-lg p-4 border border-amber-100 mb-4">
|
|
<p class="font-semibold text-amber-800 mb-2">비유: 건물의 설계도</p>
|
|
<p class="text-xs text-amber-900 leading-relaxed">
|
|
HTML은 <strong>건물의 설계도</strong>와 같다. 여기는 거실, 저기는 주방, 위층은 침실 — 이렇게 공간의 위치와 구조를 정한다.
|
|
HTML 태그가 바로 이 역할을 한다. <code><div></code>는 방, <code><p></code>는 메모지, <code><img></code>는 액자다.
|
|
</p>
|
|
</div>
|
|
<p class="text-sm text-gray-700 mb-3">
|
|
HTML(HyperText Markup Language)은 웹 페이지의 구조를 정의하는 마크업 언어다.
|
|
모든 웹 페이지는 HTML 태그로 구성된다.
|
|
</p>
|
|
<div class="code-block mb-4"><span class="code-comment"><!-- HTML 기본 구조 --></span>
|
|
<span class="code-tag"><html></span>
|
|
<span class="code-tag"><head></span>
|
|
<span class="code-tag"><title></span>페이지 제목<span class="code-tag"></title></span>
|
|
<span class="code-tag"></head></span>
|
|
<span class="code-tag"><body></span>
|
|
<span class="code-tag"><h1></span>안녕하세요<span class="code-tag"></h1></span>
|
|
<span class="code-tag"><p></span>첫 번째 문단입니다.<span class="code-tag"></p></span>
|
|
<span class="code-tag"><button></span>클릭<span class="code-tag"></button></span>
|
|
<span class="code-tag"></body></span>
|
|
<span class="code-tag"></html></span></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-mono"><div></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"><p></td>
|
|
<td class="py-2 px-3">메모지</td>
|
|
<td class="py-2 px-3">문단(paragraph)</td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-mono"><h1>~<h6></td>
|
|
<td class="py-2 px-3">간판(크기순)</td>
|
|
<td class="py-2 px-3">제목(heading), h1이 가장 큼</td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-mono"><a></td>
|
|
<td class="py-2 px-3">문 (다른 방으로)</td>
|
|
<td class="py-2 px-3">링크(anchor)</td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-mono"><img></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"><form></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"><table></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"><ul> / <li></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"><button></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"><input></td>
|
|
<td class="py-2 px-3">빈 칸</td>
|
|
<td class="py-2 px-3">텍스트 입력 필드</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 2-2. 시맨틱 태그 -->
|
|
<div id="html-semantic" 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>
|
|
시맨틱 태그
|
|
<span class="help-balloon-trigger" onclick="toggleBalloon(this)">?<span class="help-balloon">시맨틱(Semantic)은 "의미를 가진"이라는 뜻이다. <div> 대신 <header>, <nav>, <main> 등을 사용하면 검색엔진과 스크린리더가 페이지 구조를 이해할 수 있다.</span></span>
|
|
</h3>
|
|
<p class="text-sm text-gray-700 mb-3">
|
|
시맨틱(Semantic) 태그는 "의미가 있는" 태그다. <code><div></code>만 쓰면 검색엔진이나 스크린리더가 내용을 이해하기 어렵다.
|
|
시맨틱 태그를 사용하면 "여기가 메뉴 영역이고, 여기가 본문이고, 여기가 푸터"라고 알려줄 수 있다.
|
|
</p>
|
|
<div class="code-block mb-4"><span class="code-comment"><!-- 시맨틱 태그로 구성한 페이지 --></span>
|
|
<span class="code-tag"><header></span> 로고, 네비게이션 (건물 입구) <span class="code-tag"></header></span>
|
|
<span class="code-tag"><nav></span> 메뉴 목록 (안내 표지판) <span class="code-tag"></nav></span>
|
|
<span class="code-tag"><main></span> 핵심 콘텐츠 (본관) <span class="code-tag"></main></span>
|
|
<span class="code-tag"><section></span> 주제별 구역 (각 층) <span class="code-tag"></section></span>
|
|
<span class="code-tag"><article></span> 독립 콘텐츠 (개별 사무실) <span class="code-tag"></article></span>
|
|
<span class="code-tag"><footer></span> 하단 정보 (건물 로비 안내판) <span class="code-tag"></footer></span></div>
|
|
<div class="bg-gray-50 rounded-lg p-4 border text-xs text-gray-700">
|
|
<p class="font-semibold text-gray-800 mb-2">DOM 트리 구조 (문서 객체 모델)</p>
|
|
<pre class="text-xs leading-relaxed text-gray-600">
|
|
document
|
|
└── html
|
|
├── head
|
|
│ └── title
|
|
└── body
|
|
├── header
|
|
│ └── nav
|
|
├── main
|
|
│ ├── section
|
|
│ └── section
|
|
└── footer</pre>
|
|
<p class="mt-2 text-gray-500">
|
|
브라우저는 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>
|
|
|
|
{{-- ============================================================ --}}
|
|
{{-- 3. CSS — 웹의 옷 --}}
|
|
{{-- ============================================================ --}}
|
|
<section id="css" 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-purple-500 text-white rounded-lg flex items-center justify-center text-sm font-bold">3</span>
|
|
CSS — 웹의 옷
|
|
</h2>
|
|
|
|
<!-- 3-1. 선택자와 박스모델 -->
|
|
<div id="css-selector" 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-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>
|
|
<p class="text-xs text-amber-900 leading-relaxed">
|
|
CSS는 <strong>인테리어</strong>와 같다. HTML이 방의 위치를 정했다면, CSS는 벽지 색, 가구 크기, 조명 밝기를 결정한다.
|
|
"거실 벽은 흰색, 소파는 가운데 배치" 같은 지시서가 CSS인 셈이다.
|
|
</p>
|
|
</div>
|
|
<p class="text-sm text-gray-700 mb-3">
|
|
CSS(Cascading Style Sheets)는 HTML 요소의 외양을 꾸미는 스타일 언어다.
|
|
<strong>선택자</strong>로 "어떤 요소를" 지정하고, <strong>속성</strong>으로 "어떻게" 꾸밀지 정한다.
|
|
</p>
|
|
<div class="code-block mb-4"><span class="code-comment">/* CSS 선택자 기초 */</span>
|
|
<span class="code-tag">p</span> { <span class="code-attr">color</span>: <span class="code-string">blue</span>; } <span class="code-comment">/* 태그 선택자 — 모든 <p> */</span>
|
|
<span class="code-tag">.warning</span> { <span class="code-attr">color</span>: <span class="code-string">red</span>; } <span class="code-comment">/* 클래스 선택자 — class="warning" */</span>
|
|
<span class="code-tag">#header</span> { <span class="code-attr">color</span>: <span class="code-string">green</span>; } <span class="code-comment">/* ID 선택자 — id="header" */</span></div>
|
|
<p class="text-sm text-gray-700 mb-3">
|
|
<strong>박스모델</strong>: 모든 HTML 요소는 상자(box)로 취급된다. 상자 안쪽부터 바깥쪽 순서로 구성된다.
|
|
</p>
|
|
<div class="bg-gray-50 rounded-lg p-4 border text-xs text-gray-700 mb-4">
|
|
<pre class="text-center leading-loose text-gray-600">
|
|
┌──────────────── margin (바깥 여백) ────────────────┐
|
|
│ ┌──────────── border (테두리) ──────────────┐ │
|
|
│ │ ┌──────── padding (안쪽 여백) ────────┐ │ │
|
|
│ │ │ │ │ │
|
|
│ │ │ content (내용물) │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ └─────────────────────────────────────┘ │ │
|
|
│ └───────────────────────────────────────────┘ │
|
|
└────────────────────────────────────────────────────┘</pre>
|
|
</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 레이아웃의 두 기둥이다. 요소를 가로/세로로 배치하는 강력한 도구다.
|
|
</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-bold text-blue-800 mb-2 text-sm">Flexbox (1차원 배치)</p>
|
|
<p class="text-xs text-blue-700 mb-2">한 줄(가로 또는 세로)로 요소를 나란히 배치할 때 사용한다.</p>
|
|
<div class="code-block text-xs"><span class="code-attr">display</span>: <span class="code-string">flex</span>;
|
|
<span class="code-attr">justify-content</span>: <span class="code-string">space-between</span>;
|
|
<span class="code-attr">align-items</span>: <span class="code-string">center</span>;</div>
|
|
</div>
|
|
<div class="bg-green-50 rounded-lg p-4 border border-green-200">
|
|
<p class="font-bold text-green-800 mb-2 text-sm">Grid (2차원 배치)</p>
|
|
<p class="text-xs text-green-700 mb-2">행과 열을 동시에 제어하는 바둑판식 레이아웃에 사용한다.</p>
|
|
<div class="code-block text-xs"><span class="code-attr">display</span>: <span class="code-string">grid</span>;
|
|
<span class="code-attr">grid-template-columns</span>: <span class="code-string">1fr 1fr 1fr</span>;
|
|
<span class="code-attr">gap</span>: <span class="code-string">1rem</span>;</div>
|
|
</div>
|
|
</div>
|
|
<div class="bg-purple-50 rounded-lg p-4 border border-purple-100">
|
|
<p class="text-xs text-purple-800 leading-relaxed">
|
|
<strong>SAM에서는 Tailwind CSS를 사용한다.</strong>
|
|
Tailwind는 CSS를 파일에 따로 작성하지 않고, HTML 클래스명으로 직접 스타일을 적용하는 방식이다.
|
|
예: <code class="bg-purple-100 px-1 rounded">class="flex items-center gap-2 bg-white rounded-lg p-4"</code>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{{-- ============================================================ --}}
|
|
{{-- 4. JavaScript — 웹의 두뇌 --}}
|
|
{{-- ============================================================ --}}
|
|
<section id="javascript" 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-purple-500 text-white rounded-lg flex items-center justify-center text-sm font-bold">4</span>
|
|
JavaScript — 웹의 두뇌
|
|
</h2>
|
|
|
|
<!-- 4-1. 변수와 함수 -->
|
|
<div id="js-basics" 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-purple-400 rounded-full"></span>
|
|
변수와 함수
|
|
</h3>
|
|
<div class="bg-amber-50 rounded-lg p-4 border border-amber-100 mb-4">
|
|
<p class="font-semibold text-amber-800 mb-2">비유: 전기 배선</p>
|
|
<p class="text-xs text-amber-900 leading-relaxed">
|
|
JavaScript는 <strong>전기 배선</strong>과 같다. HTML이 방을 만들고 CSS가 꾸몄다면,
|
|
JavaScript는 스위치를 누르면 조명이 켜지고, 리모컨을 누르면 TV가 켜지는 <strong>동작</strong>을 담당한다.
|
|
</p>
|
|
</div>
|
|
<div class="code-block mb-4"><span class="code-comment">// 변수 — 값을 담는 상자</span>
|
|
<span class="code-keyword">let</span> <span class="code-func">userName</span> = <span class="code-string">'홍길동'</span>; <span class="code-comment">// 변경 가능</span>
|
|
<span class="code-keyword">const</span> <span class="code-func">MAX_COUNT</span> = <span class="code-number">100</span>; <span class="code-comment">// 변경 불가 (상수)</span>
|
|
|
|
<span class="code-comment">// 함수 — 재사용 가능한 명령 묶음</span>
|
|
<span class="code-keyword">function</span> <span class="code-func">greet</span>(name) {
|
|
<span class="code-keyword">return</span> <span class="code-string">'안녕하세요, '</span> + name + <span class="code-string">'님!'</span>;
|
|
}
|
|
|
|
<span class="code-func">greet</span>(<span class="code-string">'김사원'</span>); <span class="code-comment">// → "안녕하세요, 김사원님!"</span></div>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-3 text-xs mb-4">
|
|
<div class="bg-gray-50 rounded-lg p-3 border">
|
|
<p class="font-bold text-gray-800 mb-1">이벤트 처리</p>
|
|
<p class="text-gray-600">클릭, 입력, 스크롤 등 사용자 행동을 감지하여 반응한다.</p>
|
|
</div>
|
|
<div class="bg-gray-50 rounded-lg p-3 border">
|
|
<p class="font-bold text-gray-800 mb-1">JSON (데이터 형식)</p>
|
|
<p class="text-gray-600">서버와 데이터를 주고받을 때 사용하는 텍스트 형식이다.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 4-2. DOM 조작 -->
|
|
<div id="js-dom" 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>
|
|
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(문서 객체)을 조작하여 화면을 동적으로 변경할 수 있다.
|
|
또한 <code>fetch()</code>를 사용하면 페이지 새로고침 없이 서버 데이터를 가져올 수 있다.
|
|
</p>
|
|
<div class="code-block mb-4"><span class="code-comment">// 버튼 클릭 → API 호출 → 화면 업데이트 예시</span>
|
|
<span class="code-keyword">const</span> <span class="code-func">btn</span> = document.<span class="code-func">querySelector</span>(<span class="code-string">'#load-btn'</span>);
|
|
<span class="code-keyword">const</span> <span class="code-func">result</span> = document.<span class="code-func">querySelector</span>(<span class="code-string">'#result'</span>);
|
|
|
|
btn.<span class="code-func">addEventListener</span>(<span class="code-string">'click'</span>, <span class="code-keyword">async</span> () => {
|
|
<span class="code-keyword">const</span> <span class="code-func">response</span> = <span class="code-keyword">await</span> <span class="code-func">fetch</span>(<span class="code-string">'/api/orders'</span>);
|
|
<span class="code-keyword">const</span> <span class="code-func">data</span> = <span class="code-keyword">await</span> response.<span class="code-func">json</span>();
|
|
result.<span class="code-func">textContent</span> = <span class="code-string">`주문 </span>${data.length}<span class="code-string">건 로드 완료`</span>;
|
|
});</div>
|
|
<div class="bg-purple-50 rounded-lg p-4 border border-purple-100">
|
|
<p class="text-xs text-purple-800 leading-relaxed">
|
|
<strong>비동기 통신이란?</strong> 서버에 데이터를 요청하면서도 화면이 멈추지 않는 방식이다.
|
|
마치 식당에서 주문을 넣고 기다리는 동안 물을 마시거나 대화를 할 수 있는 것과 같다.
|
|
<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>
|
|
|
|
{{-- ============================================================ --}}
|
|
{{-- 5. 프레임워크와 라이브러리 --}}
|
|
{{-- ============================================================ --}}
|
|
<section id="frameworks" 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-purple-500 text-white rounded-lg flex items-center justify-center text-sm font-bold">5</span>
|
|
프레임워크와 라이브러리
|
|
</h2>
|
|
|
|
<!-- 5-1. 프레임워크 비교 -->
|
|
<div id="fw-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-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>
|
|
<p class="text-xs text-amber-900 leading-relaxed">
|
|
<strong>프레임워크</strong>는 이케아의 반조립 가구 — 설계도와 부품이 정해져 있어서 순서대로 조립하면 완성된다.
|
|
<strong>라이브러리</strong>는 공구 세트 — 필요할 때 원하는 도구만 꺼내 쓴다. 설계는 내가 한다.
|
|
</p>
|
|
</div>
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-3 text-xs mb-4">
|
|
<div class="bg-blue-50 rounded-lg p-4 border border-blue-200">
|
|
<div class="flex items-center gap-2 mb-2">
|
|
<span class="w-6 h-6 bg-blue-500 text-white rounded flex items-center justify-center text-xs font-bold">R</span>
|
|
<span class="font-bold text-blue-800">React</span>
|
|
</div>
|
|
<p class="text-blue-700 mb-2">Meta(Facebook) 개발</p>
|
|
<ul class="text-blue-600 space-y-1">
|
|
<li>• 컴포넌트 기반 설계</li>
|
|
<li>• 가상 DOM으로 빠른 렌더링</li>
|
|
<li>• 생태계가 가장 큼</li>
|
|
</ul>
|
|
</div>
|
|
<div class="bg-emerald-50 rounded-lg p-4 border border-emerald-200">
|
|
<div class="flex items-center gap-2 mb-2">
|
|
<span class="w-6 h-6 bg-emerald-500 text-white rounded flex items-center justify-center text-xs font-bold">V</span>
|
|
<span class="font-bold text-emerald-800">Vue</span>
|
|
</div>
|
|
<p class="text-emerald-700 mb-2">Evan You 개발</p>
|
|
<ul class="text-emerald-600 space-y-1">
|
|
<li>• 학습 곡선이 완만</li>
|
|
<li>• 양방향 데이터 바인딩</li>
|
|
<li>• 한국에서 인기</li>
|
|
</ul>
|
|
</div>
|
|
<div class="bg-red-50 rounded-lg p-4 border border-red-200">
|
|
<div class="flex items-center gap-2 mb-2">
|
|
<span class="w-6 h-6 bg-red-500 text-white rounded flex items-center justify-center text-xs font-bold">A</span>
|
|
<span class="font-bold text-red-800">Angular</span>
|
|
</div>
|
|
<p class="text-red-700 mb-2">Google 개발</p>
|
|
<ul class="text-red-600 space-y-1">
|
|
<li>• 풀(Full) 프레임워크</li>
|
|
<li>• TypeScript 기본</li>
|
|
<li>• 대규모 엔터프라이즈</li>
|
|
</ul>
|
|
</div>
|
|
</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-medium">Next.js</td>
|
|
<td class="py-2 px-3">React 기반 프레임워크</td>
|
|
<td class="py-2 px-3">서버사이드 렌더링(SSR), 파일 기반 라우팅</td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-medium">HTMX</td>
|
|
<td class="py-2 px-3">경량 라이브러리</td>
|
|
<td class="py-2 px-3">HTML 속성만으로 AJAX 요청 처리</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="py-2 px-3 font-medium">jQuery</td>
|
|
<td class="py-2 px-3">라이브러리 (레거시)</td>
|
|
<td class="py-2 px-3">DOM 조작 간소화, 2006년 등장 후 한 시대를 풍미</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 5-2. SAM의 선택 -->
|
|
<div id="fw-sam" 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>
|
|
SAM의 프론트엔드 구조
|
|
</h3>
|
|
<div class="bg-purple-50 rounded-lg p-5 border border-purple-100">
|
|
<p class="text-sm text-purple-900 leading-relaxed mb-3">
|
|
<strong>SAM은 이중 프론트엔드 구조</strong>를 사용한다.
|
|
</p>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-3 text-xs">
|
|
<div class="bg-white rounded-lg p-3 border">
|
|
<p class="font-bold text-purple-800 mb-1">MNG (관리자 웹)</p>
|
|
<p class="text-purple-700">Laravel Blade + HTMX + Tailwind CSS</p>
|
|
<p class="text-gray-500 mt-1">서버에서 HTML을 생성하여 전달</p>
|
|
</div>
|
|
<div class="bg-white rounded-lg p-3 border">
|
|
<p class="font-bold text-purple-800 mb-1">React 앱 (모바일/대시보드)</p>
|
|
<p class="text-purple-700">Next.js + React 18</p>
|
|
<p class="text-gray-500 mt-1">브라우저에서 JavaScript로 화면 구성</p>
|
|
</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>
|
|
|
|
{{-- ============================================================ --}}
|
|
{{-- 6. 반응형 디자인 --}}
|
|
{{-- ============================================================ --}}
|
|
<section id="responsive" 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-purple-500 text-white rounded-lg flex items-center justify-center text-sm font-bold">6</span>
|
|
반응형 디자인
|
|
</h2>
|
|
|
|
<div id="rwd-breakpoint" 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>
|
|
브레이크포인트와 모바일 퍼스트
|
|
<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>과 같다. 컵에 담으면 컵 모양, 접시에 부으면 접시 모양이 된다.
|
|
화면 크기(모바일, 태블릿, 데스크톱)에 따라 레이아웃이 자동으로 바뀌는 것이 반응형이다.
|
|
</p>
|
|
</div>
|
|
<p class="text-sm text-gray-700 mb-3">
|
|
<strong>브레이크포인트(Breakpoint)</strong>는 레이아웃이 전환되는 화면 너비 기준점이다.
|
|
</p>
|
|
<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">Tailwind 접두사</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">모바일</td>
|
|
<td class="py-2 px-3">~639px</td>
|
|
<td class="py-2 px-3 font-mono">(기본)</td>
|
|
<td class="py-2 px-3">스마트폰</td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3">태블릿</td>
|
|
<td class="py-2 px-3">640px~</td>
|
|
<td class="py-2 px-3 font-mono">sm:</td>
|
|
<td class="py-2 px-3">아이패드</td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3">중간</td>
|
|
<td class="py-2 px-3">768px~</td>
|
|
<td class="py-2 px-3 font-mono">md:</td>
|
|
<td class="py-2 px-3">태블릿 가로</td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3">데스크톱</td>
|
|
<td class="py-2 px-3">1024px~</td>
|
|
<td class="py-2 px-3 font-mono">lg:</td>
|
|
<td class="py-2 px-3">노트북</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="py-2 px-3">대형</td>
|
|
<td class="py-2 px-3">1280px~</td>
|
|
<td class="py-2 px-3 font-mono">xl:</td>
|
|
<td class="py-2 px-3">대형 모니터</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="code-block mb-4"><span class="code-comment">/* 모바일 퍼스트 — 작은 화면부터 설계 */</span>
|
|
<span class="code-tag">.card</span> {
|
|
<span class="code-attr">width</span>: <span class="code-string">100%</span>; <span class="code-comment">/* 모바일: 전체 너비 */</span>
|
|
}
|
|
<span class="code-keyword">@media</span> (<span class="code-attr">min-width</span>: <span class="code-number">768</span>px) {
|
|
<span class="code-tag">.card</span> { <span class="code-attr">width</span>: <span class="code-string">50%</span>; } <span class="code-comment">/* 태블릿: 2열 */</span>
|
|
}
|
|
<span class="code-keyword">@media</span> (<span class="code-attr">min-width</span>: <span class="code-number">1024</span>px) {
|
|
<span class="code-tag">.card</span> { <span class="code-attr">width</span>: <span class="code-string">33.3%</span>; } <span class="code-comment">/* 데스크톱: 3열 */</span>
|
|
}</div>
|
|
<div class="bg-gray-50 rounded-lg p-4 border text-xs text-gray-700 mb-4">
|
|
<p class="font-semibold text-gray-800 mb-2">디자인 흐름 (모바일 퍼스트)</p>
|
|
<div class="flex items-center gap-2 flex-wrap">
|
|
<span class="bg-purple-500 text-white rounded-full px-3 py-1 text-xs font-bold">1</span>
|
|
<span>모바일 레이아웃</span>
|
|
<span class="text-purple-400">→</span>
|
|
<span class="bg-purple-500 text-white rounded-full px-3 py-1 text-xs font-bold">2</span>
|
|
<span>태블릿 확장</span>
|
|
<span class="text-purple-400">→</span>
|
|
<span class="bg-purple-500 text-white rounded-full px-3 py-1 text-xs font-bold">3</span>
|
|
<span>데스크톱 완성</span>
|
|
</div>
|
|
</div>
|
|
<div class="bg-purple-50 rounded-lg p-4 border border-purple-100">
|
|
<p class="text-xs text-purple-800 leading-relaxed">
|
|
<strong>viewport 메타 태그:</strong>
|
|
<code class="bg-purple-100 px-1 rounded"><meta name="viewport" content="width=device-width, initial-scale=1"></code>
|
|
— 이 한 줄이 없으면 모바일에서 데스크톱 크기로 축소되어 보인다. 모든 반응형 페이지의 필수 태그다.
|
|
</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>
|
|
|
|
{{-- ============================================================ --}}
|
|
{{-- 7. 브라우저 동작 원리 --}}
|
|
{{-- ============================================================ --}}
|
|
<section id="browser" 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-purple-500 text-white rounded-lg flex items-center justify-center text-sm font-bold">7</span>
|
|
브라우저 동작 원리
|
|
</h2>
|
|
|
|
<div id="browser-render" 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>
|
|
렌더링 파이프라인
|
|
<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>
|
|
<p class="text-xs text-amber-900 leading-relaxed">
|
|
브라우저는 <strong>통역사</strong>와 같다. HTML/CSS/JavaScript라는 "외국어"를 읽고, 우리가 볼 수 있는 화면으로 번역해준다.
|
|
이 번역 과정을 <strong>렌더링(Rendering)</strong>이라 한다.
|
|
</p>
|
|
</div>
|
|
<div class="bg-gray-50 rounded-lg p-4 border text-xs text-gray-700 mb-4">
|
|
<p class="font-semibold text-gray-800 mb-3">렌더링 과정 (6단계)</p>
|
|
<div class="space-y-2">
|
|
<div class="flex items-center gap-3">
|
|
<span class="w-7 h-7 bg-purple-500 text-white rounded-full flex items-center justify-center text-xs font-bold shrink-0">1</span>
|
|
<div><strong>HTML 파싱</strong> — HTML 코드를 읽어 DOM 트리를 생성한다</div>
|
|
</div>
|
|
<div class="flex items-center gap-3">
|
|
<span class="w-7 h-7 bg-purple-500 text-white rounded-full flex items-center justify-center text-xs font-bold shrink-0">2</span>
|
|
<div><strong>CSS 파싱</strong> — CSS를 읽어 CSSOM(스타일 트리)을 생성한다</div>
|
|
</div>
|
|
<div class="flex items-center gap-3">
|
|
<span class="w-7 h-7 bg-purple-500 text-white rounded-full flex items-center justify-center text-xs font-bold shrink-0">3</span>
|
|
<div><strong>렌더 트리</strong> — DOM + CSSOM을 합쳐 실제 화면에 표시할 요소를 결정한다</div>
|
|
</div>
|
|
<div class="flex items-center gap-3">
|
|
<span class="w-7 h-7 bg-purple-500 text-white rounded-full flex items-center justify-center text-xs font-bold shrink-0">4</span>
|
|
<div><strong>레이아웃</strong> — 각 요소의 정확한 위치와 크기를 계산한다</div>
|
|
</div>
|
|
<div class="flex items-center gap-3">
|
|
<span class="w-7 h-7 bg-purple-500 text-white rounded-full flex items-center justify-center text-xs font-bold shrink-0">5</span>
|
|
<div><strong>페인트</strong> — 계산된 결과를 실제 픽셀로 그린다</div>
|
|
</div>
|
|
<div class="flex items-center gap-3">
|
|
<span class="w-7 h-7 bg-purple-500 text-white rounded-full flex items-center justify-center text-xs font-bold shrink-0">6</span>
|
|
<div><strong>합성</strong> — 여러 레이어를 합쳐 최종 화면을 완성한다</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-3 text-xs mb-4">
|
|
<div class="bg-gray-50 rounded-lg p-4 border">
|
|
<p class="font-bold text-gray-800 mb-2">개발자 도구 (F12)</p>
|
|
<p class="text-gray-600 mb-2">브라우저에서 F12 키를 누르면 개발자 도구가 열린다.</p>
|
|
<ul class="text-gray-600 space-y-1">
|
|
<li><strong>Elements</strong> — HTML 구조 확인/수정</li>
|
|
<li><strong>Console</strong> — JavaScript 실행/오류 확인</li>
|
|
<li><strong>Network</strong> — API 요청/응답 확인</li>
|
|
<li><strong>Performance</strong> — 성능 분석</li>
|
|
</ul>
|
|
</div>
|
|
<div class="bg-gray-50 rounded-lg p-4 border">
|
|
<p class="font-bold text-gray-800 mb-2">주요 브라우저</p>
|
|
<ul class="text-gray-600 space-y-1">
|
|
<li><strong>Chrome</strong> — 점유율 1위, V8 엔진</li>
|
|
<li><strong>Safari</strong> — Apple 기기 기본 브라우저</li>
|
|
<li><strong>Firefox</strong> — Mozilla 재단, 개인정보 보호</li>
|
|
<li><strong>Edge</strong> — Microsoft, Chromium 기반</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<details class="bg-gray-50 rounded-lg border">
|
|
<summary class="p-4 cursor-pointer text-sm font-semibold text-gray-700">
|
|
CORS 에러가 발생하는 이유
|
|
</summary>
|
|
<div class="px-4 pb-4 text-xs text-gray-600 leading-relaxed">
|
|
<p class="mb-2">
|
|
<strong>CORS(Cross-Origin Resource Sharing)</strong>는 브라우저의 보안 정책이다.
|
|
A 도메인의 웹페이지에서 B 도메인의 API를 호출하면, 브라우저가 "이거 허가받은 요청이야?" 하고 확인한다.
|
|
</p>
|
|
<p class="mb-2">
|
|
서버가 응답 헤더에 <code>Access-Control-Allow-Origin</code>을 포함하지 않으면 브라우저가 요청을 차단한다.
|
|
이때 콘솔에 빨간 CORS 에러가 표시된다.
|
|
</p>
|
|
<p>
|
|
<strong>해결법:</strong> 서버(백엔드) 쪽에서 CORS 헤더를 설정해야 한다. 프론트엔드만으로는 해결할 수 없다.
|
|
</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>
|
|
|
|
{{-- ============================================================ --}}
|
|
{{-- 8. API 연동 --}}
|
|
{{-- ============================================================ --}}
|
|
<section id="api-integration" 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-purple-500 text-white rounded-lg flex items-center justify-center text-sm font-bold">8</span>
|
|
API 연동
|
|
</h2>
|
|
|
|
<div id="api-http" 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>
|
|
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>
|
|
<p class="text-xs text-amber-900 leading-relaxed">
|
|
API는 <strong>주문서</strong>와 같다. 프론트엔드(홀)가 백엔드(주방)에 "이 데이터를 주세요"라고 정해진 양식으로 요청하는 것이다.
|
|
주문서에는 "무엇을(URL) 어떻게(메서드) 해달라"고 적는다.
|
|
</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">HTTP 메서드</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-mono font-bold text-blue-600">GET</td>
|
|
<td class="py-2 px-3">메뉴판 보기</td>
|
|
<td class="py-2 px-3">데이터 조회</td>
|
|
<td class="py-2 px-3 font-mono">GET /api/orders</td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-mono font-bold text-green-600">POST</td>
|
|
<td class="py-2 px-3">새 주문하기</td>
|
|
<td class="py-2 px-3">데이터 생성</td>
|
|
<td class="py-2 px-3 font-mono">POST /api/orders</td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-mono font-bold text-orange-600">PUT</td>
|
|
<td class="py-2 px-3">주문 변경</td>
|
|
<td class="py-2 px-3">데이터 수정</td>
|
|
<td class="py-2 px-3 font-mono">PUT /api/orders/1</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="py-2 px-3 font-mono font-bold text-red-600">DELETE</td>
|
|
<td class="py-2 px-3">주문 취소</td>
|
|
<td class="py-2 px-3">데이터 삭제</td>
|
|
<td class="py-2 px-3 font-mono">DELETE /api/orders/1</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-3 text-xs mb-4">
|
|
<div>
|
|
<p class="font-bold text-gray-800 mb-2">JSON 데이터 형식</p>
|
|
<div class="code-block">{
|
|
<span class="code-attr">"id"</span>: <span class="code-number">1</span>,
|
|
<span class="code-attr">"customer"</span>: <span class="code-string">"홍길동"</span>,
|
|
<span class="code-attr">"amount"</span>: <span class="code-number">150000</span>,
|
|
<span class="code-attr">"status"</span>: <span class="code-string">"진행중"</span>
|
|
}</div>
|
|
</div>
|
|
<div>
|
|
<p class="font-bold text-gray-800 mb-2">fetch()로 API 호출</p>
|
|
<div class="code-block"><span class="code-keyword">const</span> res = <span class="code-keyword">await</span> <span class="code-func">fetch</span>(
|
|
<span class="code-string">'/api/orders'</span>,
|
|
{
|
|
<span class="code-attr">method</span>: <span class="code-string">'POST'</span>,
|
|
<span class="code-attr">headers</span>: { <span class="code-string">'Content-Type'</span>: <span class="code-string">'application/json'</span> },
|
|
<span class="code-attr">body</span>: <span class="code-func">JSON.stringify</span>({ <span class="code-attr">customer</span>: <span class="code-string">'홍길동'</span> })
|
|
}
|
|
);</div>
|
|
</div>
|
|
</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-green-600">200</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-orange-600">400</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-orange-600">401</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-orange-600">404</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-red-600">500</td>
|
|
<td class="py-2 px-3">서버 오류</td>
|
|
<td class="py-2 px-3">주방에서 화재가 났다</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="bg-purple-50 rounded-lg p-4 border border-purple-100">
|
|
<p class="text-xs text-purple-800 leading-relaxed">
|
|
<strong>SAM의 API 통신:</strong>
|
|
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>
|
|
|
|
{{-- ============================================================ --}}
|
|
{{-- 9. 개발 도구와 워크플로우 --}}
|
|
{{-- ============================================================ --}}
|
|
<section id="tooling" 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-purple-500 text-white rounded-lg flex items-center justify-center text-sm font-bold">9</span>
|
|
개발 도구와 워크플로우
|
|
</h2>
|
|
|
|
<div id="tool-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-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">
|
|
<p class="font-bold text-blue-800 mb-2">VS Code</p>
|
|
<p class="text-blue-700">가장 널리 쓰이는 코드 에디터. 확장 프로그램으로 기능을 무한히 추가할 수 있다. 무료.</p>
|
|
</div>
|
|
<div class="bg-orange-50 rounded-lg p-4 border border-orange-200">
|
|
<p class="font-bold text-orange-800 mb-2">npm (Node Package Manager)</p>
|
|
<p class="text-orange-700">JavaScript 패키지(라이브러리)를 설치·관리하는 도구. <code>npm install</code>로 의존성을 설치한다.</p>
|
|
</div>
|
|
<div class="bg-gray-50 rounded-lg p-4 border">
|
|
<p class="font-bold text-gray-800 mb-2">Git</p>
|
|
<p class="text-gray-600">코드 버전 관리. 변경 이력 추적, 협업, 되돌리기가 가능하다. (→ 서버지식 참고)</p>
|
|
</div>
|
|
<div class="bg-purple-50 rounded-lg p-4 border border-purple-200">
|
|
<p class="font-bold text-purple-800 mb-2">린터 & 포매터</p>
|
|
<p class="text-purple-700">ESLint(코드 오류 검출), Prettier(코드 스타일 자동 정리). PHP에서는 Pint를 사용한다.</p>
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
|
<div class="bg-emerald-50 rounded-lg p-4 border border-emerald-200">
|
|
<p class="font-bold text-emerald-800 mb-2 text-sm">Vite (현대적 번들러)</p>
|
|
<p class="text-xs text-emerald-700 mb-2">차세대 프론트엔드 빌드 도구. 빠른 개발 서버와 효율적인 번들링을 제공한다.</p>
|
|
<ul class="text-xs text-emerald-600 space-y-1">
|
|
<li>• 즉시 시작되는 개발 서버</li>
|
|
<li>• 변경 사항 즉시 반영 (HMR)</li>
|
|
<li>• Rollup 기반 최적 빌드</li>
|
|
</ul>
|
|
</div>
|
|
<div class="bg-gray-50 rounded-lg p-4 border">
|
|
<p class="font-bold text-gray-800 mb-2 text-sm">Webpack (전통적 번들러)</p>
|
|
<p class="text-xs text-gray-600 mb-2">오랫동안 표준이었던 번들러. 복잡하지만 강력한 설정이 가능하다.</p>
|
|
<ul class="text-xs text-gray-500 space-y-1">
|
|
<li>• 풍부한 플러그인 생태계</li>
|
|
<li>• 세밀한 설정 가능</li>
|
|
<li>• 대규모 프로젝트에 적합</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<div class="bg-gray-50 rounded-lg p-4 border text-xs text-gray-700 mb-4">
|
|
<p class="font-semibold text-gray-800 mb-3">개발 워크플로우</p>
|
|
<div class="space-y-2">
|
|
<div class="flex items-center gap-3">
|
|
<span class="w-7 h-7 bg-purple-500 text-white rounded-full flex items-center justify-center text-xs font-bold shrink-0">1</span>
|
|
<div><strong>코드 작성</strong> — VS Code에서 HTML/CSS/JS 작성</div>
|
|
</div>
|
|
<div class="flex items-center gap-3">
|
|
<span class="w-7 h-7 bg-purple-500 text-white rounded-full flex items-center justify-center text-xs font-bold shrink-0">2</span>
|
|
<div><strong>린트 검사</strong> — ESLint/Prettier로 코드 품질 확인</div>
|
|
</div>
|
|
<div class="flex items-center gap-3">
|
|
<span class="w-7 h-7 bg-purple-500 text-white rounded-full flex items-center justify-center text-xs font-bold shrink-0">3</span>
|
|
<div><strong>로컬 테스트</strong> — 브라우저에서 동작 확인 (개발 서버)</div>
|
|
</div>
|
|
<div class="flex items-center gap-3">
|
|
<span class="w-7 h-7 bg-purple-500 text-white rounded-full flex items-center justify-center text-xs font-bold shrink-0">4</span>
|
|
<div><strong>Git 커밋</strong> — 변경사항 저장 (버전 관리)</div>
|
|
</div>
|
|
<div class="flex items-center gap-3">
|
|
<span class="w-7 h-7 bg-purple-500 text-white rounded-full flex items-center justify-center text-xs font-bold shrink-0">5</span>
|
|
<div><strong>빌드</strong> — 프로덕션용 최적화 코드 생성</div>
|
|
</div>
|
|
<div class="flex items-center gap-3">
|
|
<span class="w-7 h-7 bg-purple-500 text-white rounded-full flex items-center justify-center text-xs font-bold shrink-0">6</span>
|
|
<div><strong>배포</strong> — 서버에 적용하여 사용자에게 서비스</div>
|
|
</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>
|
|
|
|
{{-- ============================================================ --}}
|
|
{{-- 10. 성능 최적화 --}}
|
|
{{-- ============================================================ --}}
|
|
<section id="performance" 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-purple-500 text-white rounded-lg flex items-center justify-center text-sm font-bold">10</span>
|
|
성능 최적화
|
|
</h2>
|
|
|
|
<div id="perf-vitals" 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>
|
|
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이 제공하는 웹 성능 측정 도구다.
|
|
Chrome 개발자 도구(F12) → Lighthouse 탭에서 실행할 수 있다.
|
|
</p>
|
|
<div class="grid grid-cols-2 md:grid-cols-4 gap-3 text-xs mb-4">
|
|
<div class="bg-green-50 rounded-lg p-3 border border-green-200 text-center">
|
|
<p class="font-bold text-green-800 mb-1">Performance</p>
|
|
<p class="text-green-600">로딩 속도</p>
|
|
</div>
|
|
<div class="bg-blue-50 rounded-lg p-3 border border-blue-200 text-center">
|
|
<p class="font-bold text-blue-800 mb-1">Accessibility</p>
|
|
<p class="text-blue-600">접근성</p>
|
|
</div>
|
|
<div class="bg-orange-50 rounded-lg p-3 border border-orange-200 text-center">
|
|
<p class="font-bold text-orange-800 mb-1">Best Practices</p>
|
|
<p class="text-orange-600">모범 사례</p>
|
|
</div>
|
|
<div class="bg-purple-50 rounded-lg p-3 border border-purple-200 text-center">
|
|
<p class="font-bold text-purple-800 mb-1">SEO</p>
|
|
<p class="text-purple-600">검색 최적화</p>
|
|
</div>
|
|
</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>
|
|
<th class="text-left py-2 px-3 font-medium text-gray-600">좋은 기준</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="text-xs text-gray-700">
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-bold">LCP</td>
|
|
<td class="py-2 px-3">Largest Contentful Paint</td>
|
|
<td class="py-2 px-3">큰 간판이 보이는 시간</td>
|
|
<td class="py-2 px-3">2.5초 이내</td>
|
|
</tr>
|
|
<tr class="border-b border-gray-100">
|
|
<td class="py-2 px-3 font-bold">FID</td>
|
|
<td class="py-2 px-3">First Input Delay</td>
|
|
<td class="py-2 px-3">버튼 눌렀을 때 반응 시간</td>
|
|
<td class="py-2 px-3">100ms 이내</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="py-2 px-3 font-bold">CLS</td>
|
|
<td class="py-2 px-3">Cumulative Layout Shift</td>
|
|
<td class="py-2 px-3">화면 레이아웃이 튀는 정도</td>
|
|
<td class="py-2 px-3">0.1 이내</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</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">최적화 방법</p>
|
|
<ul class="text-green-700 space-y-1">
|
|
<li>• <strong>이미지</strong>: WebP 포맷, 적절한 크기, lazy loading</li>
|
|
<li>• <strong>코드 분할</strong>: 필요한 코드만 로드</li>
|
|
<li>• <strong>캐싱</strong>: 브라우저 캐시, CDN 활용</li>
|
|
<li>• <strong>압축</strong>: gzip/brotli로 전송 크기 축소</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">성능 저하 원인</p>
|
|
<ul class="text-red-700 space-y-1">
|
|
<li>• 대용량 이미지 무압축 로드</li>
|
|
<li>• 사용하지 않는 JavaScript 번들</li>
|
|
<li>• 렌더링 차단 CSS/JS</li>
|
|
<li>• 너무 많은 API 요청</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<details class="bg-gray-50 rounded-lg border">
|
|
<summary class="p-4 cursor-pointer text-sm font-semibold text-gray-700">
|
|
Lighthouse로 SAM 성능 측정하는 법
|
|
</summary>
|
|
<div class="px-4 pb-4 text-xs text-gray-600 leading-relaxed space-y-2">
|
|
<p>1. Chrome에서 SAM 페이지를 연다</p>
|
|
<p>2. F12를 눌러 개발자 도구를 연다</p>
|
|
<p>3. 상단 탭에서 <strong>Lighthouse</strong>를 클릭한다</p>
|
|
<p>4. "Analyze page load"를 클릭한다</p>
|
|
<p>5. 30초~1분 후 결과가 표시된다</p>
|
|
<p class="mt-2 text-gray-500">
|
|
각 항목에서 100점에 가까울수록 좋다. 90점 이상이면 녹색, 50~89점은 주황색, 49점 이하는 빨간색이다.
|
|
</p>
|
|
</div>
|
|
</details>
|
|
|
|
<!-- 핵심 정리 -->
|
|
<div class="mt-6 bg-purple-50 rounded-lg p-5 border border-purple-100">
|
|
<h4 class="font-semibold text-purple-800 mb-2 text-sm">핵심 정리</h4>
|
|
<p class="text-xs text-purple-700 leading-relaxed">
|
|
프론트엔드는 <strong>"사용자가 보고 만지는 모든 것"</strong>이다.
|
|
HTML로 뼈대를 만들고, CSS로 꾸미고, JavaScript로 동작을 넣는다.
|
|
React, Vue 같은 프레임워크로 복잡한 화면을 효율적으로 관리하고,
|
|
반응형 디자인으로 모든 기기에서 잘 보이게 만든다.
|
|
API를 통해 백엔드와 데이터를 주고받고, Lighthouse로 성능을 측정하여 최적화한다.
|
|
SAM은 MNG(Blade+HTMX)와 React(Next.js) 이중 프론트엔드 구조로, 이 모든 기술을 활용하고 있다.
|
|
</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' => 'frontend-dev'])
|
|
@endsection
|