2026-02-23 10:31:41 +09:00
@ extends ( 'layouts.app' )
2026-02-23 13:18:08 +09:00
@ section ( 'title' , '프론트엔드 개발' )
2026-02-23 10:31:41 +09:00
@ push ( 'styles' )
< style >
/* 이미지 기본 스타일 */
. academy - img - hover {
transition : box - shadow 0.2 s ease ;
}
. academy - img - hover : hover {
box - shadow : 0 4 px 12 px rgba ( 0 , 0 , 0 , 0.15 );
}
. academy - img - wrap {
overflow : hidden ;
border - radius : 0.75 rem ;
}
/* 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 ( 3 px );
pointer - events : none ;
}
#hover-preview.is-active {
display : flex ;
pointer - events : none ;
}
#hover-preview img {
max - height : 80 vh ;
max - width : 85 vw ;
border - radius : 0.75 rem ;
box - shadow : 0 25 px 60 px rgba ( 0 , 0 , 0 , 0.5 );
opacity : 0 ;
transform : scale ( 0.3 );
transition : opacity 0.25 s ease , transform 0.25 s ease ;
}
#hover-preview.is-active img {
opacity : 1 ;
transform : scale ( 1 );
}
#hover-preview .hover-caption {
position : absolute ;
bottom : 2 rem ;
left : 50 % ;
transform : translateX ( - 50 % );
color : rgba ( 255 , 255 , 255 , 0.85 );
font - size : 0.8 rem ;
background : rgba ( 0 , 0 , 0 , 0.5 );
padding : 0.4 rem 1 rem ;
border - radius : 2 rem ;
white - space : nowrap ;
opacity : 0 ;
transition : opacity 0.3 s ease 0.15 s ;
}
#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 ( 4 px );
}
#lightbox.is-open {
display : flex ;
}
#lightbox img {
max - height : 90 vh ;
max - width : 90 vw ;
border - radius : 0.5 rem ;
box - shadow : 0 25 px 50 px rgba ( 0 , 0 , 0 , 0.5 );
}
2026-02-23 10:51:01 +09:00
/* 도움말 풍선 */
. help - balloon - trigger {
display : inline - flex ;
align - items : center ;
justify - content : center ;
width : 18 px ;
height : 18 px ;
border - radius : 50 % ;
background : #ede9fe;
color : #7c3aed;
font - size : 11 px ;
font - weight : 700 ;
cursor : pointer ;
border : 1.5 px solid #c4b5fd;
vertical - align : middle ;
margin - left : 4 px ;
transition : all 0.15 s 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 % + 10 px );
left : 50 % ;
transform : translateX ( - 50 % ) scale ( 0.95 );
background : #1e293b;
color : #f1f5f9;
font - size : 12 px ;
font - weight : 400 ;
line - height : 1.6 ;
padding : 10 px 14 px ;
border - radius : 10 px ;
max - width : 300 px ;
min - width : 200 px ;
width : max - content ;
white - space : normal ;
word - break : keep - all ;
box - shadow : 0 8 px 24 px rgba ( 0 , 0 , 0 , 0.25 );
z - index : 40 ;
opacity : 0 ;
transition : opacity 0.15 s ease , transform 0.15 s ease ;
pointer - events : none ;
}
. help - balloon :: after {
content : '' ;
position : absolute ;
top : 100 % ;
left : 50 % ;
transform : translateX ( - 50 % );
border : 7 px 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 : - 10 px ;
transform : scale ( 1 );
}
. help - balloon - trigger . is - active . help - balloon . balloon - right :: after {
left : auto ;
right : 14 px ;
transform : none ;
}
2026-02-23 10:31:41 +09:00
/* 코드 블록 스타일 */
. code - block {
background : #1e1e2e;
color : #a6e3a1;
border - radius : 0.75 rem ;
padding : 1 rem 1.25 rem ;
font - family : 'Courier New' , Courier , monospace ;
font - size : 0.75 rem ;
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 >
2026-02-23 13:18:08 +09:00
< h1 class = " text-3xl font-bold mb-2 " style = " color: #ffffff; " > 프론트엔드 개발 </ h1 >
2026-02-23 10:31:41 +09:00
< p class = " text-sm " style = " color: #cbd5e1; " > HTML , CSS , JavaScript부터 프레임워크 , 성능 최적화까지 — 비개발자도 이해하는 프론트엔드 가이드 </ p >
</ div >
< div class = " shrink-0 " style = " width: 240px; padding: 1.5rem; " >
2026-02-23 10:51:01 +09:00
< 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) " >
2026-02-23 10:31:41 +09:00
</ 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 백엔드
2026-02-23 10:51:01 +09:00
< span class = " help-balloon-trigger " onclick = " toggleBalloon(this) " > ? < span class = " help-balloon " > 프론트엔드는 사용자의 브라우저에서 실행되는 코드 ( HTML , CSS , JS ) 이고 , 백엔드는 서버에서 실행되는 코드 ( PHP , Python 등 ) 다 . 둘은 API를 통해 데이터를 주고받는다 .</ span ></ span >
2026-02-23 10:31:41 +09:00
</ 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 >
2026-02-23 10:51:01 +09:00
< div class = " bg-purple-50 rounded-lg p-4 border border-purple-100 mb-4 " >
2026-02-23 10:31:41 +09:00
< p class = " text-xs text-purple-800 leading-relaxed " >
< strong > SAM 시스템의 프론트엔드 :</ strong >
SAM도 프론트엔드와 백엔드로 분리되어 있다 . MNG ( 관리자 웹 ) 는 Blade + HTMX로 , React 앱은 Next . js로 화면을 구성하고 ,
API 서버 ( Laravel ) 가 데이터를 처리한다 .
</ p >
</ div >
2026-02-23 10:51:01 +09:00
< 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 >
2026-02-23 10:31:41 +09:00
</ 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 >& lt ; div & gt ; </ code > 는 방 , < code >& lt ; p & gt ; </ code > 는 메모지 , < code >& lt ; img & gt ; </ 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 " >& lt ; !-- HTML 기본 구조 --& gt ; </ span >
< span class = " code-tag " >& lt ; html & gt ; </ span >
< span class = " code-tag " >& lt ; head & gt ; </ span >
< span class = " code-tag " >& lt ; title & gt ; </ span > 페이지 제목 < span class = " code-tag " >& lt ; / title & gt ; </ span >
< span class = " code-tag " >& lt ; / head & gt ; </ span >
< span class = " code-tag " >& lt ; body & gt ; </ span >
< span class = " code-tag " >& lt ; h1 & gt ; </ span > 안녕하세요 < span class = " code-tag " >& lt ; / h1 & gt ; </ span >
< span class = " code-tag " >& lt ; p & gt ; </ span > 첫 번째 문단입니다 .< span class = " code-tag " >& lt ; / p & gt ; </ span >
< span class = " code-tag " >& lt ; button & gt ; </ span > 클릭 < span class = " code-tag " >& lt ; / button & gt ; </ span >
< span class = " code-tag " >& lt ; / body & gt ; </ span >
< span class = " code-tag " >& lt ; / html & gt ; </ 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 " >& lt ; div & gt ; </ 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 " >& lt ; p & gt ; </ 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 " >& lt ; h1 & gt ; ~& lt ; h6 & gt ; </ 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 " >& lt ; a & gt ; </ 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 " >& lt ; img & gt ; </ 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 " >& lt ; form & gt ; </ 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 " >& lt ; table & gt ; </ 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 " >& lt ; ul & gt ; / & lt ; li & gt ; </ 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 " >& lt ; button & gt ; </ 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 " >& lt ; input & gt ; </ 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 >
시맨틱 태그
2026-02-23 10:51:01 +09:00
< 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 >
2026-02-23 10:31:41 +09:00
</ h3 >
< p class = " text-sm text-gray-700 mb-3 " >
시맨틱 ( Semantic ) 태그는 " 의미가 있는 " 태그다 . < code >& lt ; div & gt ; </ code > 만 쓰면 검색엔진이나 스크린리더가 내용을 이해하기 어렵다 .
시맨틱 태그를 사용하면 " 여기가 메뉴 영역이고, 여기가 본문이고, 여기가 푸터 " 라고 알려줄 수 있다 .
</ p >
< div class = " code-block mb-4 " >< span class = " code-comment " >& lt ; !-- 시맨틱 태그로 구성한 페이지 --& gt ; </ span >
< span class = " code-tag " >& lt ; header & gt ; </ span > 로고 , 네비게이션 ( 건물 입구 ) < span class = " code-tag " >& lt ; / header & gt ; </ span >
< span class = " code-tag " >& lt ; nav & gt ; </ span > 메뉴 목록 ( 안내 표지판 ) < span class = " code-tag " >& lt ; / nav & gt ; </ span >
< span class = " code-tag " >& lt ; main & gt ; </ span > 핵심 콘텐츠 ( 본관 ) < span class = " code-tag " >& lt ; / main & gt ; </ span >
< span class = " code-tag " >& lt ; section & gt ; </ span > 주제별 구역 ( 각 층 ) < span class = " code-tag " >& lt ; / section & gt ; </ span >
< span class = " code-tag " >& lt ; article & gt ; </ span > 독립 콘텐츠 ( 개별 사무실 ) < span class = " code-tag " >& lt ; / article & gt ; </ span >
< span class = " code-tag " >& lt ; footer & gt ; </ span > 하단 정보 ( 건물 로비 안내판 ) < span class = " code-tag " >& lt ; / footer & gt ; </ 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 >
2026-02-23 10:51:01 +09:00
< 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 >
2026-02-23 10:31:41 +09:00
</ 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 >
선택자와 박스모델
2026-02-23 10:51:01 +09:00
< span class = " help-balloon-trigger " onclick = " toggleBalloon(this) " > ? < span class = " help-balloon " > 선택자 ( Selector ) 는 " 어떤 HTML 요소에 스타일을 적용할지 " 지정하는 규칙이다 . 박스모델은 모든 요소가 content → padding → border → margin 4 겹 상자로 구성된다는 개념이다 .</ span ></ span >
2026-02-23 10:31:41 +09:00
</ 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 " >/* 태그 선택자 — 모든 & lt ; p & gt ; */</ 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 >
2026-02-23 10:51:01 +09:00
< 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 >
2026-02-23 10:31:41 +09:00
<!-- 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
2026-02-23 10:51:01 +09:00
< span class = " help-balloon-trigger " onclick = " toggleBalloon(this) " > ? < span class = " help-balloon " > Flexbox는 한 방향 ( 가로 또는 세로 ) 으로 요소를 나열할 때 사용하고 , Grid는 가로 + 세로 2 차원으로 배치할 때 사용한다 . SAM에서는 Tailwind CSS의 flex / grid 클래스를 활용한다 .</ span ></ span >
2026-02-23 10:31:41 +09:00
</ 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 " > 1 fr 1 fr 1 fr </ span > ;
< span class = " code-attr " > gap </ span >: < span class = " code-string " > 1 rem </ 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 조작과 비동기 통신
2026-02-23 10:51:01 +09:00
< span class = " help-balloon-trigger " onclick = " toggleBalloon(this) " > ? < span class = " help-balloon " > DOM 조작은 JavaScript로 화면의 내용을 동적으로 변경하는 것이다 . 비동기 통신 ( fetch ) 은 페이지 새로고침 없이 서버에서 데이터를 가져오는 기술이다 .</ span ></ span >
2026-02-23 10:31:41 +09:00
</ 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 >
2026-02-23 10:51:01 +09:00
<!-- 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 >
2026-02-23 10:31:41 +09:00
</ 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 라이브러리
2026-02-23 10:51:01 +09:00
< span class = " help-balloon-trigger " onclick = " toggleBalloon(this) " > ? < span class = " help-balloon " > 프레임워크는 전체 구조를 제공하고 개발자가 그 안에서 코드를 작성한다 . 라이브러리는 필요한 기능만 골라 쓸 수 있는 도구 모음이다 .</ span ></ span >
2026-02-23 10:31:41 +09:00
</ 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 >
2026-02-23 10:51:01 +09:00
<!-- 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 >
2026-02-23 10:31:41 +09:00
</ 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 >
브레이크포인트와 모바일 퍼스트
2026-02-23 10:51:01 +09:00
< span class = " help-balloon-trigger " onclick = " toggleBalloon(this) " > ? < span class = " help-balloon " > 브레이크포인트는 화면 너비에 따라 레이아웃이 바뀌는 기준점이다 . 모바일 퍼스트는 작은 화면부터 디자인한 후 큰 화면으로 확장하는 접근법이다 .</ span ></ span >
2026-02-23 10:31:41 +09:00
</ 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 " >~ 639 px </ 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 " > 640 px ~</ 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 " > 768 px ~</ 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 " > 1024 px ~</ 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 " > 1280 px ~</ 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 " >& lt ; meta name = " viewport " content = " width=device-width, initial-scale=1 " & gt ; </ code >
— 이 한 줄이 없으면 모바일에서 데스크톱 크기로 축소되어 보인다 . 모든 반응형 페이지의 필수 태그다 .
</ p >
</ div >
2026-02-23 10:51:01 +09:00
<!-- 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 >
2026-02-23 10:31:41 +09:00
</ 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 >
렌더링 파이프라인
2026-02-23 10:51:01 +09:00
< span class = " help-balloon-trigger " onclick = " toggleBalloon(this) " > ? < span class = " help-balloon " > 렌더링 파이프라인은 브라우저가 HTML / CSS / JS 코드를 화면의 픽셀로 변환하는 일련의 처리 과정이다 .</ span ></ span >
2026-02-23 10:31:41 +09:00
</ 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 >
2026-02-23 10:51:01 +09:00
<!-- 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 >
2026-02-23 10:31:41 +09:00
</ 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 메서드
2026-02-23 10:51:01 +09:00
< span class = " help-balloon-trigger " onclick = " toggleBalloon(this) " > ? < span class = " help-balloon " > REST API는 URL과 HTTP 메서드 ( GET / POST / PUT / DELETE ) 를 조합하여 서버 자원을 조작하는 규약이다 . 웹에서 가장 널리 쓰이는 API 설계 방식이다 .</ span ></ span >
2026-02-23 10:31:41 +09:00
</ 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 >
2026-02-23 10:51:01 +09:00
<!-- 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 >
2026-02-23 10:31:41 +09:00
</ 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 >
핵심 도구와 개발 흐름
2026-02-23 10:51:01 +09:00
< span class = " help-balloon-trigger " onclick = " toggleBalloon(this) " > ? < span class = " help-balloon " > 프론트엔드 개발에는 코드 에디터 ( VS Code ), 패키지 관리자 ( npm ), 버전 관리 ( Git ), 번들러 ( Vite ) 등 여러 도구를 조합하여 사용한다 .</ span ></ span >
2026-02-23 10:31:41 +09:00
</ 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 >
2026-02-23 13:18:08 +09:00
< p class = " text-gray-600 " > 코드 버전 관리 . 변경 이력 추적 , 협업 , 되돌리기가 가능하다 . ( → 서버지식 참고 ) </ p >
2026-02-23 10:31:41 +09:00
</ 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 >
2026-02-23 10:51:01 +09:00
<!-- 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 >
2026-02-23 10:31:41 +09:00
</ 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
2026-02-23 10:51:01 +09:00
< span class = " help-balloon-trigger " onclick = " toggleBalloon(this) " > ? < span class = " help-balloon " > Lighthouse는 Google이 제공하는 웹 성능 측정 도구이다 . Core Web Vitals는 LCP ( 로딩 속도 ), FID ( 반응 속도 ), CLS ( 레이아웃 안정성 ) 3 가지 핵심 지표이다 .</ span ></ span >
2026-02-23 10:31:41 +09:00
</ 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 " > 100 ms 이내 </ 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 ();
}
});
2026-02-23 10:51:01 +09:00
// 도움말 풍선 토글
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' );
});
}
});
2026-02-23 10:31:41 +09:00
})();
</ script >
@ include ( 'components.academy-glossary' , [ 'domain' => 'frontend-dev' ])
@ endsection