서버 사이드 컴포넌트
- 정의: 서버에서 렌더링되는 컴포넌트, 초기 페이지 로드 시 서버에서 데이터를 가져와 HTML을 생성
- 장점: SEO 최적화에 유리하여, 초기 로딩 속도가 빠르다. 데이터 요청을 서버에서 처리하므로 클라이언트의 부담을 줄인다.
- 사용 예 : 사용자 인증 정보, 데이터베이스에서 가져온 데이터를 기반으로 하는 페이지
클라이언트 사이드 컴포넌트
- 정의: 클라이언트(브라우저)에서 렌더링되는 컴포넌트로, 사용자 상호작용에 따라 동적으로 업데이트됨
장점: 사용자 경험을 향상시키며, 페이지 전환, 상태 관리를 유연하게 처리할 수 있다.
- 사용 예: 버튼 클릭 시 데이터를 가져오거나, UI 요소의 동적인 변경이 필요한 경우
애플리케이션의 코드를 여러 개의 청크로 나누어 필요할 때만 로드하는 기법입니다. 이를 통해 초기 로딩 시간을 단축하고, 사용자에게 필요한 코드만 전송하여 성능을 개선할 수 있습니다.
📑 청크(Chunk)란? 애플리케이션의 코드나 리소스를 나누어 놓은 작은 단위입니다. 코드 스플리팅을 통해 큰 파일을 여러 개의 청크로 나누어, 필요한 경우에만 해당 청크를 로드하도록 하는 방식입니다.
javascript 예시
// main.js
function loadModule() {
import('./module.js')
.then(module => {
module.default();
})
.catch(err => {
console.error('모듈 로드 실패:', err);
});
}
// 버튼 클릭 시 모듈 로드
document.getElementById('loadButton').addEventListener('click', loadModule);
React 예시
// main.js
function loadModule() {
import('./module.js')
.then(module => {
module.default();
})
.catch(err => {
console.error('모듈 로드 실패:', err);
});
}
// 버튼 클릭 시 모듈 로드
document.getElementById('loadButton').addEventListener('click', loadModule);
Next.js 예시
// pages/index.js
import dynamic from 'next/dynamic';
const DynamicComponent = dynamic(() => import('../components/DynamicComponent'), {
loading: () => <p>로딩 중...</p>,
});
function HomePage() {
return (
<div>
<h1>홈페이지</h1>
<DynamicComponent />
</div>
);
}
export default HomePage;
Next.js의 Edge Runtime은 서버와 클라이언트 사이에서 코드가 실행되는 환경으로, 주로 엣지 서버에서 작동합니다.
호이스팅(Hoisting)
var : 변수 선언이 함수나 전역 스코프의 최상단으로 끌어올려집니다. 따라서 선언 전에 접근하면 undefined를 반환합니다.
let과 const : 호이스팅은 되지만, 선언 전에 접근하면 ReferenceError가 발생합니다. 이는 "일시적 사각지대(Temporal Dead Zone)" 때문입니다
재선언 및 재할당
var: 같은 스코프 내에서 재선언이 가능하고, 재할당도 가능합니다.
let: 같은 스코프 내에서 재선언이 불가능하지만, 재할당은 가능합니다.
const: 재선언과 재할당이 모두 불가능합니다. 상수로서, 선언 후 값이 변경되지 않습니다.
스코프 (Scope)
var: 함수 스코프(function scope)를 가집니다. 즉, 함수 내에서 선언된 var는 함수 전체에서 접근 가능합니다.
let과 const: 블록 스코프(block scope)를 가집니다. 즉, {}로 감싸진 블록 내에서만 접근 가능합니다.
자바스크립트의 변수와 함수 선언이 해당 스코프의 최상단으로 끌어올려지는 특성을 말합니다. 이로 인해 변수나 함수를 선언하기 전에 참조할 수 있게 됩니다.
var로 선언된 변수는 선언만 호이스팅되며, 초기화는 호이스팅되지 않는다. 따라서, 선언 전에 접근하면 undefined가 반환됨.
console.log(a); // undefined
var a = 5;
삼수 선언은 전체가 호이스팅되므로, 선언 전에 호출할 수 있다.
greet(); // "Hello!"
function greet() {
console.log("Hello!");
}
let과 const로 선언된 변수도 호이스팅되지만, 선언되기 전에는 접근할 수 없으며, 이를 시도하면 ReferenceError가 발생합니다. 이는 "일시적 사각지대(Temporal Dead Zone)" 때문입니다.
console.log(b); // ReferenceError
let b = 10;
스코프(Scope) : 변수의 유효 범위를 정의하는 개념으로, 변수가 어디에서 접근 가능한지를 결정합니다. 자바스크립트에서는 주로 두 가지 타입의 스코프가 있습니다
클로저(Closure) : 함수가 선언된 환경을 기억하는 함수입니다. 즉, 내부 함수가 외부 함수의 변수에 접근할 수 있는 기능입니다.
코드의 최상단에서 선언된 변수는 전역 스코프에 속합니다. 어디에서나 접근할 수 있습니다.
var globalVar = "나는 전역 변수";
function example() {
console.log(globalVar); // "나는 전역 변수"
}
함수 내에서 선언된 변수는 해당 함수 내에서만 접근 가능하며, 다른 함수에서는 접근할 수 없습니다.
function example() {
var localVar = "나는 지역 변수";
console.log(localVar); // "나는 지역 변수"
}
console.log(localVar); // ReferenceError
let과 const로 선언된 변수는 블록 {} 내에서만 유효합니다.
if (true) {
let blockVar = "나는 블록 변수";
console.log(blockVar); // "나는 블록 변수"
}
console.log(blockVar); // ReferenceError
클로저를 통해 외부에서 접근할 수 없는 변수를 만들 수 있습니다.
function createCounter() {
let count = 0; // 은닉된 변수
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
클로저를 사용하면 함수 호출 간에 상태를 유지할 수 있습니다.
function makeMultiplier(factor) {
return function(x) {
return x * factor;
};
}
const double = makeMultiplier(2);
console.log(double(5)); // 10
복잡해지고 규모가 커질수록, 전역 상태 관리 라이브러리(예: Redux, MobX, Context API 등)를 사용하는 것이 더 효율적이고 유지보수하기 쉬운 방법이 될 수 있습니다. 지역 상태만으로는 이러한 문제들을 효과적으로 해결하기 어려운 경우가 많습니다.
null과 undefined는 자바스크립트에서 모두 "값이 없음"을 나타내지만, 그 의미와 용도가 다릅니다.
- undefined : 변수는 선언되었지만 값이 할당되지 않은 상태를 나타냅니다. 초기화 하지 않으면 자동으로 undefined가 된다.
- null: 변수에 "의도적으로 값이 없음"을 할당할 때 사용합니다. 이는 개발자가 특정한 상태를 나타내기 위해 사용합니다.
깊은 복사: 복사본이 원본과 완전히 독립적임. 모든 중첩 객체가 새로 복사됨
얕은 복사: 복사본은 원본과 일부 속성을 공유 중첩 객체는 같은 참조를 가지므로, 한쪽에서 변경하면 다른 쪽에도 영향을 줌.
항목 | 얕은 복사 | 깊은 복사 |
---|---|---|
정의 | 객체의 1차원 속성만 복사 | 모든 속성을 재귀적으로 복사 |
참조 | 참조 데이터 타입은 원본 객체와 공유 | 완전히 독립적인 새로운 인스턴스 생성 |
예시 | const shallowCopy = { ...original }; | const deepCopy = JSON.parse(JSON.stringify(original)); |
결과 | 원본 객체의 참조 속성이 변경되면 영향을 받음 | 원본 객체와 복사된 객체가 서로 영향을 주지 않음 |
사용 예 | 단순한 객체나 배열의 복사, 성능이 중요한 경우 | 복잡한 객체나 배열을 완전히 독립적으로 복사해야 할 때 |
방법 | Object.assign(), Spread operator (... ) 사용 | JSON 변환, Lodash의 _.cloneDeep 사용 |
// 얇은 복사
const o = { x: { y: 1 } };
const c1 = { ...o }; // 얕은 복사
console.log(c1.x === o.x); // true: 같은 중첩 객체를 가리킴
// 깊은 복사
const _ = require('lodash');
const c2 = _.cloneDeep(o); // 깊은 복사
console.log(c2.x === o.x); // false: 서로 다른 중첩 객체
// 원시 값 복사
const v = 1;
const c1 = v; // 깊은 복사처럼 값 복사
console.log(c1 === v); // true
Strict mode(엄격 모드)는 자바스크립트의 기능으로, 코드의 실행 환경을 더 안전하고 예측 가능하게 만들기 위해 특정 규칙을 적용. 엄격 모드를 사용하면 잠재적인 오류를 사전에 방지하고, 코드를 더 명확하게 작성할 수 있음
선언되지 않은 변수를 사용 X, 이를 통해 변수의 오타로 인한 버그를 방지할 수 있음
"use strict";
x = 10; // ReferenceError: x is not defined
읽기 전용 속성 수정 금지
읽기 전용 속성(예: NaN, undefined, Infinity)을 수정하려고 하면 오류가 발생
삭제(Deletion) 제한
delete 연산자를 사용하여 변수를 삭제할 수 없음
"use strict";
var obj = {};
delete obj; // SyntaxError: Delete of an unqualified identifier in strict mode.
함수의 매개변수 이름이 중복될 수 없음
"use strict";
function myFunction(a, a) { // SyntaxError: Duplicate parameter name not allowed in this context
// ...
}
this의 의미 변경
전역 컨텍스트에서 this는 undefined가 됩니다. 이는 전역 객체에 대한 불필요한 접근을 방지
모듈과 클래스에서 기본적으로 활성화
ES6의 모듈과 클래스는 기본적으로 엄격 모드가 적용됩니다.
자바스크립트에서 비동기 작업을 처리할 때, 많은 콜백 함수가 중첩되면서 코드의 가독성과 유지보수성이 떨어지는 상태를 말합니다. 일반적으로 비동기 작업을 수행하기 위해 여러 단계의 콜백을 중첩할 때 발생합니다. 이로 인해 코드가 복잡해지고, 이해하기 어려워집니다.
getData(function(result1) {
processResult1(result1, function(result2) {
processResult2(result2, function(result3) {
processResult3(result3, function(finalResult) {
console.log(finalResult);
});
});
});
});
콜백 함수를 별도로 정의하여 중첩을 줄인다.
function handleResult1(result1) {
processResult1(result1, handleResult2);
}
function handleResult2(result2) {
processResult2(result2, handleResult3);
}
function handleResult3(result3) {
processResult3(result3, function(finalResult) {
console.log(finalResult);
});
}
getData(handleResult1);
비동기 작업을 처리할 때 Promise를 사용하면, .then() 메서드를 체인하여 가독성을 높임
getData()
.then(processResult1)
.then(processResult2)
.then(processResult3)
.then(finalResult => {
console.log(finalResult);
})
.catch(error => {
console.error(error);
});
async/await 문법을 사용하면, 비동기 코드를 동기 코드처럼 작성할 수 있어 가독성이 높임
async function main() {
try {
const result1 = await getData();
const result2 = await processResult1(result1);
const result3 = await processResult2(result2);
const finalResult = await processResult3(result3);
console.log(finalResult);
} catch (error) {
console.error(error);
}
}
main();
- React: 유연하고 컴포넌트 기반의 라이브러리.
- Angular: 강력한 기능을 갖춘 전체 프레임워크.
항목 | React | Angular |
---|---|---|
개발 방식 | UI 라이브러리, 컴포넌트 기반 | 전체 프레임워크, MVC 아키텍처 |
언어 | JSX (JavaScript XML) 사용 | TypeScript 사용 |
데이터 바인딩 | 단방향 데이터 바인딩 | 양방향 데이터 바인딩 |
상태 관리 | 훅(hooks) 또는 외부 라이브러리 사용 | 서비스와 RxJS로 관리 |
생태계 | 큰 커뮤니티, 다양한 라이브러리 존재 | Google 유지보수, 안정적인 업데이트 |
성능 | 가상 DOM으로 UI 업데이트 최적화 | Zone.js로 변경 감지, 성능 개선 필요 |
이벤트 버블링(Event Bubbling)과 이벤트 캡처링(Event Capturing)은 자바스크립트에서 이벤트가 DOM 트리에서 전파되는 두 가지 방식이다. 이 두 개념은 이벤트가 발생한 요소에서 상위 요소로 또는 하위 요소로 전파되는 과정을 설명
이벤트가 발생한 요소에서 시작하여, 그 요소의 상위 요소(부모 요소)로 전파되는 방식입니다. 즉, 가장 자식 요소에서 시작해 부모 요소로 올라가는 방식
// HTML
<div id="parent">
<button id="child">Click me</button>
</div>
// Javascript
document.getElementById('parent').addEventListener('click', function() {
console.log('Parent clicked');
});
document.getElementById('child').addEventListener('click', function() {
console.log('Child clicked');
});
이벤트가 상위 요소에서 시작하여, 하위 요소(자식 요소)로 전파되는 방식입니다. 즉, 부모 요소에서 시작해 자식 요소로 내려가는 방식
document.getElementById('parent').addEventListener('click', function() {
console.log('Parent clicked');
}, true); // true를 추가하여 캡처링 모드로 설정
document.getElementById('child').addEventListener('click', function() {
console.log('Child clicked');
});
상태 관리 라이브러리(Redux, MobX 등)는 애플리케이션의 상태를 효율적으로 관리하고 공유하기 위해 사용됨
Semantic Tag: HTML에서 콘텐츠의 의미를 명확히 전달하는 태그, 웹 페이지의 구조를 이해하기 쉽게 하며, SEO 및 접근성을 향상시킴
Flex: CSS Flexbox는 1차원 레이아웃을 만들기 위한 모듈이며, 요소들을 쉽게 정렬하고 배치할 수 있게 하며, 공간을 효율적으로 활용
Grid: CSS Grid Layout은 2차원 레이아웃을 구성하기 위한 모듈, 행과 열로 구성된 복잡한 레이아웃을 쉽게 만들 수 있음
Padding: 요소의 콘텐츠와 경계(테두리) 사이의 내부 공간의 여백을 줄때 사용
Margin: 요소의 경계(테두리)와 다른 요소 사이의 외부 공간에 여백을 줄때 사용
항목 | 클래스형 컴포넌트 | 함수형 컴포넌트 |
---|---|---|
정의 방식 | ES6 클래스로 정의 | 함수로 정의 |
상태 관리 | this.state 사용 | Hooks(useState ) 사용 |
생명주기 메서드 | 사용 가능 | useEffect 로 대체 |
코드 길이 | 상대적으로 길고 복잡할 수 있음 | 간결하고 직관적 |
성능 | 약간 느릴 수 있음 | 더 가벼운 성능 |
ES6 클래스를 사용하여 정의된 컴포넌트 이며, this.state를 사용하여 내부 상태를 관리, 생명주기 메서드
componentDidMount
,componentDidUpdate
,componentWillUnmount
등의 생명주기 메서드를 사용할 수 있음
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
render() {
return <div>{this.state.count}</div>;
}
}
단순한 JavaScript 함수로 정의된 컴포넌트,
React Hooks(useState, useEffect 등)
를 사용하여 상태와 생명주기를 관리할 수 있습니다.
import React, { useState } from 'react';
function MyComponent() {
const [count, setCount] = useState(0);
return <div>{count}</div>;
}