안녕하세요, 웹 프론트엔드 개발자 Garden, 오소현입니다.
최근에 친한 개발자분들과 함께 시나브로 자바스크립트 강의 스터디를 진행하게 되었습니다 :)
저희의 스터디 레포는 아래와 같습니다!
https://github.com/The-survivor-is-strong/sinabro-js
이번 글은 1주차 강의 내용을 정리한 내용을 작성했습니다.
[chapter 1] 웹 서버와 번들러
먼저, 웹 서버가 정적 파일을 제공하는 방식과 번들러의 역할에 대해 알아보겠습니다.
웹 서버
웹 서버와 HTML, CSS, JavaScript 파일의 제공
웹 서버는 인터넷을 통해 사용자의 브라우저에 웹 페이지를 전달하는 역할을 한다. 기본적으로 웹 서버는 HTML, CSS, JavaScript 등의 정적 파일을 저장하며, 사용자가 웹사이트를 방문할 때 이 파일들을 제공한다.
[1] HTML
웹 페이지의 구조를 정의하는 HyperText Markup Language 언어, 사용자가 웹사이트 주소를 입력하면 웹 서버는 index.html
과 같은 기본 HTML 파일을 먼저 찾아 브라우저로 전송한다.
[2] CSS
Cascading Style Sheets로, 웹 페이지의 스타일을 정의한다. HTML 파일과 연결되어 색상, 글꼴, 간격 등 시각적 요소를 조정합니다. HTML 파일 로딩 후 추가적으로 요청되어 적용된다.
[3] Javascript
동적 요소를 웹 페이지에 추가하는 스크립팅 언어이다. 사용자와의 상호작용, 데이터의 동적 처리, 웹 페이지의 변경 등을 담당하며, 이 모든 사용자가 정의한 동작은 웹 페이지가 브라우저에 나타난 후에 실행된다.
웹 서버는 이러한 파일들을 처리하고 필요한 데이터와 함께 웹 페이지를 사용자에게 전송한다. 예를 들어, 사용자가 웹사이트에 접속하면 서버는 index.html
파일을 먼저 찾아 브라우저에 전달하고, 필요한 CSS와 JavaScript 파일들을 순차적으로 브라우저에 전송한다.
번들러
번들러의 개념
번들러는 웹 개발 과정에서 여러 개의 파일과 모듈을 하나의 파일로 결합하여 관리하는 도구이다. 이 도구는 웹 개발의 효율성과 성능을 극대화하며, 개발 과정을 체계적으로 관리할 수 있도록 도와준다.
번들러의 역할
[1] 성능 최적화
여러 파일을 하나로 합침으로써 서버 요청의 수를 줄이고 로딩 시간을 단축한다.
[2] 전처리
Sass, LESS (CSS 전처리기) 및 TypeScript, Babel (JavaScript 전처리기) 등을 사용하여 개발 과정을 간소화 한다.
[3] 모듈 호환성
다양한 모듈 형식을 지원하여, npm과 같은 패키지 관리자에서 제공하는 라이브러리와 쉽게 통합할 수 있다.
[4] 자동 리로드 & 핫 리플레이스
개발 중인 파일에 변경이 있을 때마다 자동으로 브라우저를 새로고침하여 변경사항을 실시간으로 반영합니다.
대표적인 번들러의 예시
webpack, parcel, rollup, vite
번들러의 실제 적용 및 사용 예
번들러는 예를 들어, React 프로젝트에서 JSX 파일과 CSS 파일을 하나의 JavaScript 파일로 번들링하는 과정을 담당한다. 개발자는 복잡한 의존성 관리와 성능 최적화를 번들러에 맡기고, 보다 중요한 애플리케이션 로직 개발에 집중할 수 있다.
Netlify 배포를 통한 번들러 사용 예시:
간단한 HTML 파일을 생성하고, 이를 Netlify를 통해 배포한다. Netlify와 같은 호스팅 플랫폼은 사용자의 파일을 받아 웹 서버에 자동으로 배포하고, 필요한 서버 설정을 대신 관리해 준다.
유저는 웹사이트에 접속하여 최종 결과물을 볼 수 있으며, 이 과정에서 로컬 개발 환경의 중요성을 강조하기도 했다. (음) 로컬에서는 보통 localhost
를 사용하여 실제 웹 환경을 모의하고, 실제 웹에서 발생할 수 있는 여러 상황을 더 잘 반영할 수 있다.
번들러를 사용하여 웹 개발의 효율성과 성능을 최대화하며, 개발 과정을 보다 체계적으로 관리할 수 있다. 또한 이렇게 웹 애플리케이션은 더 빠르고 효율적으로 사용자에게 제공될 수 있다.
이제 Tailwind CSS의 이해와 활용에 대해 알아보겠습니다.
[chapter 2] Tailwind CSS의 이해와 활용
Tailwind CSS란?
Tailwind CSS는 유틸리티 우선(Utility-First) 접근 방식을 채택한 CSS 프레임워크이다. 이 접근 방식은 재사용 가능하고 조합하기 쉬운 유틸리티 클래스를 제공하여, 빠르고 유연하게 디자인을 구현할 수 있도록 한다.
이 말은 간단하게 웹 페이지를 만들 때 필요한 스타일(예를 들어, 글자 크기, 색상, 간격 등)을 미리 만들어진 짧은 클래스 형태로 제공한다는 뜻이다.
예를 들어, 만약 글자 색을 파란색으로 하고 싶다면, Tailwind CSS에서는 이를 위한 클래스가 이미 만들어져 있는 것이다. 디자이너나 개발자는 HTML 코드에 이 클래스를 추가하기만 하면 돼서, 복잡한 CSS 코드를 작성할 필요가 없다.
이런 방식의 장점은 무엇보다 속도와 유연성이다. 디자인을 변경하고 싶을 때, 새로운 스타일을 처음부터 만드는 대신, 이미 만들어진 클래스를 조합하기만 하면 되기 때문에 매우 빠르게 작업할 수 있다. 또한, 이 클래스들은 서로 잘 어울리도록 설계되어 있어서, 새로운 디자인을 구성하는 것도 쉽다.
Tailwind CSS는 웹 페이지 디자인을 레고 블록 조립하듯이 할 수 있게 해주는 도구라고 생각하면 좋을 것 같다. 필요한 블록(클래스)을 골라서 원하는 위치에 끼우기만 하면, 원하는 모양의 디자인을 쉽고 빠르게 완성할 수 있다.
[1] 유틸리티 클래스란?
각 클래스는 특정 스타일 속성(예: 패딩, 마진, 색상 등)을 적용한다. 예를 들어, w-0
은 width를 0 픽셀로 설정한다.
[2] 응답성
Tailwind는 반응형 디자인을 쉽게 구현할 수 있게 해주는 미디어 쿼리 기반의 클래스를 제공한다.
Tailwind CSS와 CDN 의 관계
CDN(Content Delivery Network)은 전 세계 여러 지역에 데이터 센터를 두어 사용자가 가장 가까운 서버에서 웹 페이지의 콘텐츠를 빠르게 받아볼 수 있도록 하는 기술이다. 이 방식은 이미지, 비디오, 웹 페이지 등의 정적 콘텐츠를 빠르게 전달하는 데 매우 효과적이다.
그런데, Tailwind CSS와 CDN의 관계에서 몇 가지 특별한 문제가 발생한다. Tailwind CSS는 유틸리티 우선 방식을 채택한 CSS 프레임워크로, 매우 많은 수의 유틸리티 클래스를 제공한다. 이 때문에 Tailwind CSS의 전체 라이브러리 크기가 크며, 사용하지 않는 스타일까지 모두 포함되어 있어 파일 크기가 상당히 커질 수 있다.
Tailwind CSS와 CDN 사용의 한계
-
Tailwind CSS의 전체 라이브러리를 CDN을 통해 사용하게 되면, 사용하지 않는 수많은 스타일까지 모두 로드해야 한다. 이렇게 되면 웹사이트의 로딩 시간을 늘리고, 사용자의 데이터 사용량을 증가시키는 원인이 된다.
-
Tailwind CSS는 맞춤 설정이 강력한 도구이다. 개발자들은 자신의 프로젝트에 맞게 스타일을 조정하고, 필요하지 않은 스타일을 제거하여 최적화할 수 있다. 하지만 CDN을 사용하면, 이러한 맞춤 설정을 할 수 없고, 기본 설정된 스타일 그대로를 사용해야만 한다.
### Tailwind CSS에서 CDN
CDN은 정적 자원(이미지, JavaScript 파일, CSS 파일 등)을 전 세계에 분산된 서버 네트워크를 통해 제공함으로써 웹사이트의 성능을 향상시키는 역할을 한다. 사용자는 가장 가까운 서버로부터 자원을 빠르게 받아볼 수 있다. 그러나 Tailwind CSS의 경우, 라이브러리의 크기가 크고 맞춤 설정이 필요한 경우가 많기 때문에, 일반적으로 CDN보다는 빌드 과정에서 필요한 부분만 포함시켜 사용하는 것이 더 효율적이다.
따라서 Tailwind CSS와 같은 대형 프레임워크에서는 CDN을 통한 전체 라이브러리 로딩보다는 개발 과정에서 필요한 부분만 추출하여 사용하는 방식이 권장된다. 이렇게 하면 웹사이트의 성능을 최적화하고, 필요 없는 리소스 낭비를 줄일 수 있다. 이는 웹 개발에서 매우 중요한 부분으로, 특히 모바일 사용자에게 더 빠른 로딩 시간과 향상된 사용자 경험을 제공할 수 있다.
Tailwind CSS와 번들러의 통합
Tailwind CSS는 Just-in-Time(JIT) 컴파일 방식을 도입하면서, 사용자가 실시간으로 사용하는 CSS만을 생성하게 되었습니다. 이는 번들러와의 통합을 필수적으로 만들었다.
Tailwind CSS와 번들러를 함께 사용하는 것은 현대 웹 개발에서 중요한 패러다임입니다. 이 조합은 개발자가 더 빠르고 효율적으로 웹 페이지를 디자인하고 구현할 수 있게 해준다. 또한, 유틸리티 우선의 접근 방식은 스타일의 일관성을 유지하면서도 유연성을 제공하여, 대규모 프로젝트와 팀 작업에 적합하다고 한다.
Tailwind CSS와 JIT 컴파일
Tailwind CSS는 웹 페이지를 디자인할 때 사용하는 도구이다. 이건 매우 많은 스타일 옵션을 제공하지만, 모든 옵션을 한 번에 로드하면 파일이 너무 커져서 웹 페이지의 로딩 속도가 느려질 수 있다. 이 문제를 해결하기 위해 Tailwind CSS는 JIT(Just-in-Time) 컴파일 방식을 사용한다.
JIT 컴파일은 개발하는 동안 실제로 필요한 스타일만을 선택해서 만들어주는 기능이다. 예를 들어, 웹 페이지에서 특정 버튼에만 파란색 배경을 적용하려면, 그 버튼에 필요한 파란색 배경 스타일만을 생성한다. 이렇게 하면 사용하지 않는 스타일은 제외되므로 최종 CSS 파일의 크기가 줄어들고, 웹 페이지의 로딩 속도가 빨라진다.
[chapter 3] DomAPI - 1 addEventListener
addEventListener, 이제 이벤트 리스너를 추가하는 방법에 대해 알아보겠습니다.
웹 개발에서 사용자의 상호작용을 관리하는 핵심 기능 중 하나는 이벤트 리스너를 추가하는 방식이 있습니다. JavaScript의 addEventListener
메소드가 이에 해당합니다. 이에 대해 강의를 듣고 정리해보았습니다.
이벤트 리스너 추가하기
addEventListener
는 특정 이벤트가 발생했을 때 호출될 함수를 요소에 연결합니다.
element.addEventListener('event', function);
여기서 element
는 DOM에서의 요소, event
는 반응할 이벤트의 종류, 그리고 function
은 이벤트 발생 시 실행할 함수를 의미합니다.
이벤트 처리 함수
이벤트 리스너에 할당된 함수는 이벤트 객체를 인자로 받아 다양한 데이터에 접근할 수 있습니다. 예를 들어, event.target
속성을 사용하여 이벤트가 발생한 요소의 정보를 얻을 수 있습니다.
이벤트 버블링과 캡처링
DOM 이벤트는 이벤트 버블링 방식과 캡쳐링 방식으로 전파될 수 있습니다.
버블링: 이벤트가 발생한 요소에서 시작해 DOM 트리를 거슬러 올라가면서 각 요소에서 이벤트 리스너를 실행합니다.
캡처링: 이벤트가 DOM 트리의 최상위에서 시작하여 발생 요소로 내려가면서 리스너를 실행합니다.
이벤트 전파를 중지하고 싶을 때는 event.stopPropagation()
메소드를 사용합니다. 이 메소드는 이벤트가 더 이상 전파되지 않도록 중단하는 역할을 합니다.
다양한 이벤트 유형
다양한 요소에 맞는 적절한 이벤트 유형을 선택하는 것이 중요하다고 볼 수 있는데, 예를 들어 입력 필드에서는 input
이벤트를 사용하여 사용자 입력을 실시간으로 감지할 수 있고, change
이벤트는 입력 필드에서 포커스가 벗어났을 때 변경사항을 감지할때 유용하게 사용될 수 있습니다.
키보드 이벤트 처리
키보드 이벤트를 사용하면 키보드 입력에 반응하는 기능을 구현할 수 있습니다. keyup
이벤트를 사용하여 특정 키 조합을 감지하고 더 많은 인터렉션을 제공할 수 있다고 합니다.
document.addEventListener('keyup', function(event) {
if (event.key === 'Enter') {
console.log('Enter 키가 눌렸습니다!');
} });
addEventListener
는 웹 페이지에서 사용자의 상호작용을 관리하고, 다양한 이벤트에 반응하는 동적인 웹 애플리케이션을 구현할 수 있는 메소드 임을 알게 되었습니다.
[chapter 4] DomAPI - 2 innerHTML의 비효율성과 그 대안들
innerHTML, 이제 이벤트 리스너를 추가하는 방법에 대해 알아보겠습니다.
웹 개발에서 DOM을 조작하는 건 자주 일어나는뎅, 그 중에서도 innerHTML
속성을 사용하여 요소의 HTML을 쉽게 변경할 수 있지만, innerHTML은 몇가지 비효율적인 부분이 있다고 해서 정리해보게 되었습니다. 이 글에서는 innerHTML
의 작동 방식과 그 한계, 대안점을 함께 공부해보려 합니다.
innerHTML
의 작동 원리
innerHTML
속성은 문자열을 통해 HTML 요소의 내용을 바꾸는 방법 중 하나인데, 예를 들어, 다음과 같은 코드가 있다고 가정해 보겠습니다:
document.querySelector('#app').innerHTML = '<p>Updated content</p>';
이 코드는 ID가 'app'인 요소의 내부 HTML을 새로운 내용으로 교체하고 있습니다.
innerHTML
사용 시의 문제점
하지만 innerHTML
을 사용할 때 문제가 발생할 수 있는데, 아래와 같이 알아보겠습니다.
-
성능 저하:
innerHTML
을 사용하면 지정된 요소의 자식 요소 전체를 파싱하고 새로운 노드로 교체해야 하므로, 대규모 DOM에서는 성능에 부정적인 영향을 줄 수 있습니다. -
스크립트 재실행:
innerHTML
로 삽입된 HTML 안에<script>
태그가 있으면, 이 스크립트는 실행되지 않습니다. -
상태 손실: 기존 요소를 새 요소로 완전히 교체하면, 이전 요소의 모든 상태(예: 입력 필드의 값, 포커스 상태)가 손실됩니다. 예를 들어, 사용자가 입력 필드에 데이터를 입력하는 동안
innerHTML
이 실행되면 입력 중이던 데이터와 포커스가 모두 사라집니다.
효율적인 대안
이러한 문제를 해결하기 위해 다른 DOM 조작 방법들이 있습니다.
-
textContent
와innerText
: 단순 텍스트 내용만 변경해야 할 경우,textContent
또는innerText
속성을 사용하면 불필요한 HTML 파싱 과정을 거치지 않아도 됩니다. -
createElement
와appendChild
: 새 요소를 추가할 때는createElement
로 요소를 만든 후appendChild
를 사용하여 DOM에 추가하는 방법이 효율적입니다. 이건 기존의 DOM 구조를 유지하면서 새 요소만 추가하므로, 전체를 다시 로드할 필요가 없습니다. -
프레임워크 사용: React나 Vue 같은 현대적인 프론트엔드 프레임워크는 DOM을 효율적으로 조작하는 자체 메커니즘을 제공합니다. 예를 들어, React는 가상 DOM을 사용하여 변경이 필요한 부분만 실제 DOM에 반영합니다.
결론
innerHTML
은 특정 상황에서 유용할 수 있지만, 큰 규모의 어플리케이션 또는 동적인 요소가 많은 인터랙티브 웹 페이지에서는 그 한계가 명확합니다. 효율적인 DOM 조작을 위해서는 innerHTML
의 대안을 적극적으로 고려하고, 가능한 경우 현대적인 프론트엔드 프레임워크를 활용하는 것이 좋을 것 같습니다.
[chapter 5] DomAPI - 3 대량의 이벤트 처리
이제 대량의 이벤트 처리에 대해 알아보겠습니다.
웹 개발에서 대량의 DOM 요소에 이벤트 리스너를 효율적으로 관리하는 방법은 성능 최적화와 연관성이 높은데요, 이번 글에서는 여러 카드 요소에 이벤트 리스너를 추가하고, 이벤트 처리를 위한 다양한 접근법을 탐구해 보겠습니다.
동적 요소에 이벤트 리스너 추가하기
예를 들어, 사용자가 '카드 추가' 버튼을 클릭할 때마다 새로운 카드가 화면에 추가되고, 각 카드에 특정 기능을 수행하는 버튼이 포함된다고 가정해 볼게요. 이 경우, 각각의 새로운 버튼에 개별적으로 이벤트 리스너를 추가하는 것은 비효율적일 수 있다고 해요. 그래서 아래와 같은 방법으로 작성해보았습니다.
- 이벤트 위임 사용: 하나의 공통된 상위 요소에 이벤트 리스너를 추가하고, 이벤트가 발생했을 때 이벤트 타겟의 종류를 체크하여 적절한 동작을 수행하게 합니다. 이 방식은 메모리 사용을 줄이고, 동적으로 요소가 추가되거나 제거될 때 이벤트 리스너를 재할당할 필요가 없어 성능을 개선할 수 있습니다.
document.querySelector('.card-container').addEventListener('click', function(event) {
if (event.target.matches('.button-class')) {
// 버튼 클릭 시 실행할 코드
}
});
클로저를 활용한 이벤트 데이터 관리
각 이벤트 리스너 내에서 특정 데이터(예: 카드 인덱스)에 접근해야 할 경우, 클로저를 사용하여 해당 데이터를 이벤트 리스너와 연결할 수 있습니다. 이 방법은 각 버튼에 대한 정보를 보존하면서도, 그 정보가 외부에서 쉽게 변경될 수 없도록 캡슐화합니다.
function createCard(index) {
const button = document.createElement('button');
button.textContent = '카드 ' + index;
button.addEventListener('click', function() {
alert('카드 ' + index + '가 클릭됐습니다');
});
document.body.appendChild(button);
}
데이터 속성 활용
HTML5의 데이터 속성(data-*
)을 활용하여 요소에 추가 정보를 저장할 수 있습니다. 이벤트가 발생했을 때 쉽게 접근할 수 있으며, 동적으로 생성된 요소의 상태나 속성을 유지하는 데 유용하게 사용될 수 있습니다.
button.dataset.index = index;
document.querySelector('.card-container').addEventListener('click', function(event) {
if (event.target.dataset.index) {
console.log('카드 ' + event.target.dataset.index + ' 클릭됨');
}
});
대량의 요소에 이벤트 리스너를 효율적으로 관리하는 방법은 웹 애플리케이션의 성능에 큰 영향을 미칩니다. 이벤트 위임, 클로저, 데이터 속성을 적절히 활용하여 리소스를 절약하고, 유지보수가 용이하며, 확장 가능한 코드를 작성하는 게 중요해보여요!