JS) JS 기초

100·2일 전

JS

목록 보기
1/2
post-thumbnail

Part 1. JavaScript 기본 문법

1. 변수 선언: let, const, var

JavaScript에서는 주로 let, const를 사용한다.

let age = 20;
const name = "Kim";
  • let: 값 변경 가능
  • const: 재할당 불가능
  • var: 과거 문법. 함수 스코프와 호이스팅 문제 때문에 일반적으로 사용하지 않는다.
let a = 10;
a = 20; // 가능

const b = 10;
b = 20; // Error

기본적으로 const를 사용하고, 값이 바뀌어야 할 때만 let을 사용하는 방식이 일반적이다.

2. JavaScript의 주요 자료형

JavaScript의 기본 자료형은 다음과 같다.

let num = 10;              // number
let str = "hello";         // string
let bool = true;           // boolean
let empty = null;          // null
let value;                 // undefined
let big = 123n;            // bigint
let id = Symbol("id");     // symbol

Java와 다르게 정수와 실수를 별도의 타입으로 구분하지 않고 대부분 number로 처리한다.

typeof 10;    // "number"
typeof 3.14;  // "number"

3. 동적 타입 언어

JavaScript는 동적 타입 언어이다.

변수를 선언할 때 타입을 지정하지 않고, 실행 중에 다른 타입의 값을 넣을 수도 있다.

let value = 10;

value = "hello";
value = true;

Java에서는 변수의 타입이 고정되지만, JavaScript와 Python은 변수에 저장되는 값에 따라 타입이 결정된다.

4. 문자열

문자열은 ', ", 백틱 `을 사용할 수 있다.

const name = "Kim";
const age = 20;

백틱을 사용하면 문자열 안에 변수를 쉽게 넣을 수 있다.

const message = `이름은 ${name}, 나이는 ${age}살입니다.`;

이를 Template Literal이라고 한다.

5. 배열

JavaScript 배열은 서로 다른 타입의 값을 함께 저장할 수 있다.

const arr = [1, "hello", true];

주요 사용법:

arr.push(10);
arr.pop();

arr[0];
arr.length;

실제 웹 개발에서는 배열 자체보다 map, filter, find 같은 배열 메서드를 매우 자주 사용한다.

6. 객체

JavaScript에서 객체는 key-value 형태의 데이터를 저장한다.

const user = {
    name: "Kim",
    age: 20
};

값 접근:

user.name;
user["age"];

값 변경:

user.age = 21;

Java의 객체보다 Python의 dict와 비슷하게 느껴질 수 있지만, JavaScript에서는 객체가 언어 전체에서 매우 중요한 역할을 한다.

7. 함수

일반 함수:

function add(a, b) {
    return a + b;
}

함수 표현식:

const add = function(a, b) {
    return a + b;
};

화살표 함수:

const add = (a, b) => {
    return a + b;
};

한 줄이면 더 줄일 수 있다.

const add = (a, b) => a + b;

웹 개발, 특히 React에서는 화살표 함수를 매우 자주 사용한다.

8. 조건문과 반복문

Java와 거의 비슷하다.

if (age >= 20) {
    console.log("성인");
} else {
    console.log("미성년자");
}

반복문:

for (let i = 0; i < 5; i++) {
    console.log(i);
}

배열 순회에는 다음 방식도 자주 사용한다.

for (const value of arr) {
    console.log(value);
}

9. 구조 분해 할당

배열이나 객체에서 값을 쉽게 꺼낼 수 있는 문법이다.

const user = {
    name: "Kim",
    age: 20
};

const { name, age } = user;

배열에서도 사용할 수 있다.

const arr = [10, 20];

const [a, b] = arr;

React에서 매우 많이 등장한다.

const [count, setCount] = useState(0);

10. Spread 문법 ...

배열이나 객체의 값을 펼칠 때 사용한다.

const a = [1, 2];
const b = [...a, 3, 4];

객체:

const user = {
    name: "Kim",
    age: 20
};

const newUser = {
    ...user,
    age: 21
};

React에서 상태를 변경할 때 특히 자주 사용한다.

핵심 정리

let   → 재할당 가능
const → 재할당 불가능
var   → 가급적 사용하지 않음

JavaScript는 동적 타입 언어

배열 → []
객체 → {}

함수는 값처럼 변수에 저장 가능

() => {} → 화살표 함수

const { a, b } = obj
→ 구조 분해 할당

...arr / ...obj
→ Spread 문법

Part 2. JavaScript 타입 시스템

1. null vs undefined

둘 다 “값이 없음”을 나타내지만 의미가 다르다.

let a;
console.log(a); // undefined

let b = null;
console.log(b); // null
  • undefined: 값이 아직 할당되지 않음
  • null: 개발자가 의도적으로 “값 없음”을 넣음

실무에서는 API 응답, 초기 상태값, optional 값 처리할 때 자주 만난다.

2. == vs ===

1 == "1"   // true
1 === "1"  // false

==는 비교 전에 타입을 자동 변환한다.

===는 타입과 값이 모두 같은지 비교한다.

그래서 실무에서는 거의 항상:

if (a === b) {
}

처럼 ===를 사용한다.

3. 타입 강제 변환(Type Coercion)

JavaScript는 상황에 따라 타입을 자동으로 바꾼다.

"5" + 1   // "51"
"5" - 1   // 4

+는 문자열 연결로 동작할 수 있지만, -는 숫자 연산만 가능해서 숫자로 변환된다.

이런 암묵적 변환 때문에 JS에서 예상하지 못한 결과가 생길 수 있다.

4. Truthy / Falsy

JS에서는 boolean이 아니어도 조건문에서 참/거짓으로 판단된다.

Falsy 값:

false
0
""
null
undefined
NaN

이외 대부분의 값은 Truthy다.

if ("hello") {
    console.log("실행됨");
}

특히 주의할 것:

[]   // truthy
{}   // truthy

빈 배열과 빈 객체도 true 취급된다.

5. NaN

NaN은 Not a Number라는 뜻이다.

Number("hello"); // NaN

특이하게:

NaN === NaN // false

그래서 확인할 때는:

Number.isNaN(value);

을 사용한다.

6. typeof

타입 확인:

typeof 10          // "number"
typeof "hello"     // "string"
typeof true        // "boolean"
typeof undefined   // "undefined"
typeof {}          // "object"

그런데 유명한 예외가 있다.

typeof null // "object"

이건 JS 초기 설계에서 생긴 오래된 버그성 동작이다.

핵심 정리

undefined
→ 값이 아직 없음

null
→ 의도적으로 값 없음

== 
→ 타입 변환 후 비교

===
→ 타입 + 값 비교
→ 실무에서 기본 사용

Truthy / Falsy
→ 조건문에서 값 자체가 true/false처럼 평가됨

[]와 {}는 truthy

NaN
→ 숫자로 변환할 수 없는 값

typeof null === "object"
→ JS의 오래된 특이사항

Part 3. Scope와 실행 구조

1. Scope란?

Scope는 변수에 접근할 수 있는 범위다.

const a = 10;

function test() {
    const b = 20;
    console.log(a); // 가능
    console.log(b); // 가능
}

console.log(b); // 불가능

JS에는 크게 다음 범위가 있다.

Global Scope
Function Scope
Block Scope

2. Block Scope

let, const는 {} 단위로 범위가 나뉜다.

if (true) {
    const a = 10;
}

console.log(a); // Error

반면 var는 block scope가 아니라 function scope를 따른다.

if (true) {
    var a = 10;
}

console.log(a); // 10

이것도 var를 잘 쓰지 않는 이유 중 하나다.

3. Scope Chain

현재 범위에 변수가 없으면 바깥 범위로 올라가며 찾는다.

const a = 10;

function outer() {
    const b = 20;

    function inner() {
        console.log(a);
        console.log(b);
    }

    inner();
}

inner()는 자신의 scope → outer → global 순서로 변수를 찾는다.

이 구조를 Scope Chain이라고 한다.

4. Lexical Scope

JS는 함수가 어디에서 호출됐는지가 아니라, 어디에서 선언됐는지를 기준으로 바깥 scope를 결정한다.

const x = 10;

function test() {
    console.log(x);
}

function run() {
    const x = 20;
    test();
}

run(); // 10

test()가 run() 안에서 호출됐지만, 선언된 위치가 global이므로 x = 10을 본다.

이걸 Lexical Scope라고 한다.

5. Execution Context

JS는 코드를 실행할 때 Execution Context(실행 컨텍스트)를 만든다.

쉽게 말하면:

현재 실행 중인 코드의 변수, 함수, scope 정보를 관리하는 실행 환경

함수가 호출될 때마다 새로운 실행 컨텍스트가 생성된다.

function a() {
    b();
}

function b() {
    console.log("hello");
}

a();

개념적으로:

Global Execution Context
        ↓
a() Execution Context
        ↓
b() Execution Context

가 만들어진다.

6. Call Stack

Execution Context는 Call Stack에 쌓인다.

function first() {
    second();
}

function second() {
    console.log("hello");
}

first();

실행 흐름:

Global
↓
first()
↓
second()
↓
second 종료
↓
first 종료

Stack이기 때문에 LIFO(Last In First Out) 구조다.

이 Call Stack 개념은 나중에 싱글 스레드와 Event Loop를 이해할 때 핵심이 된다.

핵심 정리

Scope
→ 변수에 접근할 수 있는 범위

let / const
→ block scope

var
→ function scope

Scope Chain
→ 변수를 현재 scope부터 바깥으로 찾아감

Lexical Scope
→ 함수가 선언된 위치 기준으로 scope 결정

Execution Context
→ 현재 실행 중인 코드의 환경 정보

Call Stack
→ 실행 컨텍스트가 쌓이는 구조
→ LIFO

Part 4. 함수와 Closure, this

1. JS에서 함수는 값이다

JavaScript에서 함수는 변수에 저장하거나, 다른 함수에 전달하거나, 반환할 수 있다.

const add = (a, b) => a + b;

즉 함수도 하나의 값처럼 다룬다.

2. Callback 함수

다른 함수에 인자로 전달되는 함수를 callback이라고 한다.

function run(callback) {
    callback();
}

run(() => console.log("hello"));

웹 개발에서 이벤트, 비동기 처리, 배열 메서드 등에 매우 자주 사용된다.

arr.map(x => x * 2);

3. 고차 함수

함수를 인자로 받거나 함수를 반환하는 함수를 고차 함수(Higher-Order Function)라고 한다.

대표적으로:

map()
filter()
reduce()

같은 배열 메서드가 있다.

4. Closure

Closure는 함수가 자신이 선언된 외부 Scope의 변수를 기억하는 특성이다.

function outer() {
    let count = 0;

    return () => {
        count++;
        return count;
    };
}

const counter = outer();

counter(); // 1
counter(); // 2

outer() 실행은 끝났지만 내부 함수가 count를 계속 기억한다.

즉:

함수 + 함수가 선언될 당시의 외부 환경
= Closure

React의 state, event handler, hook을 이해할 때 중요하다.

5. this

this는 현재 함수가 어떤 객체와 연결되어 실행되는지에 따라 값이 달라질 수 있다.

const user = {
    name: "Kim",

    hello() {
        console.log(this.name);
    }
};

user.hello(); // Kim

여기서 this는 user를 가리킨다.

6. 화살표 함수의 this

화살표 함수는 자신의 this를 새로 만들지 않는다.

바깥 Scope의 this를 그대로 사용한다.

const user = {
    name: "Kim",

    hello: () => {
        console.log(this.name);
    }
};

이 경우 this가 user를 가리킨다고 기대하면 안 된다.

그래서 객체 메서드에서는 일반 함수 문법을 사용하는 경우가 많다.

핵심 정리

JS에서 함수는 값처럼 사용 가능

Callback
→ 다른 함수에 전달되는 함수

고차 함수
→ 함수를 인자로 받거나 반환하는 함수

Closure
→ 함수가 외부 Scope의 변수를 기억하는 특성

this
→ 함수가 어떻게 호출되었는지에 따라 결정

화살표 함수
→ 자신의 this를 만들지 않음
→ 바깥 this 사용

Part 5. 객체 / Reference / Prototype / Class

1. 객체는 참조 타입

JS에서 객체와 배열은 값을 직접 복사하는 게 아니라 참조값을 다룬다.

const a = { name: "Kim" };
const b = a;

b.name = "Lee";

console.log(a.name); // Lee

a, b가 같은 객체를 가리키기 때문이다.

2. 얕은 복사

Spread를 사용하면 새 객체를 만들 수 있다.

const a = { name: "Kim", age: 20 };
const b = { ...a };

하지만 중첩 객체는 여전히 참조가 공유될 수 있다.

const a = {
    user: { name: "Kim" }
};

const b = { ...a };

b.user.name = "Lee";

console.log(a.user.name); // Lee

즉 ...은 기본적으로 얕은 복사(Shallow Copy)다.

3. 객체 비교

객체는 내용이 아니라 참조값을 비교한다.

{} === {} // false

반면:

const a = {};
const b = a;

a === b // true

같은 객체를 가리킬 때만 true다.

4. Prototype

JavaScript 객체는 다른 객체를 기반으로 기능을 상속받을 수 있다.

이 연결 구조가 Prototype이다.

예를 들어 배열에서:

const arr = [1, 2, 3];

arr.map(...)
arr.filter(...)

map, filter를 우리가 직접 만든 적이 없는데 사용할 수 있는 이유는 배열이 Array.prototype의 메서드를 상속받기 때문이다.

개념적으로:

arr
 ↓
Array.prototype
 ↓
Object.prototype

이렇게 prototype chain을 따라 메서드를 찾는다.

5. Prototype Chain

객체에서 어떤 속성이나 메서드를 찾을 때:

현재 객체에서 찾음
↓
없으면 prototype에서 찾음
↓
또 없으면 상위 prototype에서 찾음

이 구조를 Prototype Chain이라고 한다.

6. Class

JS에서도 Java처럼 class를 사용할 수 있다.

class User {
    constructor(name) {
        this.name = name;
    }

    hello() {
        console.log(`hello ${this.name}`);
    }
}

const user = new User("Kim");

하지만 JS의 class는 본질적으로 prototype 기반 구조를 더 익숙하게 표현한 문법이다.

7. 상속

class Animal {
    move() {
        console.log("move");
    }
}

class Dog extends Animal {
    bark() {
        console.log("bark");
    }
}

Java와 비슷하게 보이지만 내부적으로는 prototype chain을 사용한다.

핵심 정리

객체 / 배열
→ 참조 타입

const b = a
→ 같은 객체 참조

{ ...a }
→ 새 객체 생성
→ 하지만 얕은 복사

{} === {}
→ false
→ 객체는 참조값 비교

Prototype
→ 다른 객체의 기능을 상속받는 구조

Prototype Chain
→ 속성/메서드를 상위 prototype으로 찾아감

class
→ prototype 기반 객체 시스템을 감싼 문법

Part 6. 싱글 스레드 / 비동기 / Event Loop

1. JavaScript는 싱글 스레드

JavaScript는 기본적으로 한 번에 하나의 작업만 실행한다.

작업 A 실행
→ 작업 A 종료
→ 작업 B 실행

즉 JS 코드를 실행하는 Call Stack은 하나다.

2. 그럼 비동기는 어떻게 처리하나?

브라우저 환경에서는 오래 걸리는 작업을 브라우저가 대신 처리한다.

대표적으로:

setTimeout(...)
fetch(...)

개념적으로:

JavaScript
→ Call Stack

Browser
→ Timer
→ Network
→ DOM Event

JS 자체는 한 번에 하나만 실행하지만, 외부 작업은 브라우저가 처리할 수 있다.

3. Event Loop

비동기 작업이 끝나면 callback이 Queue에 들어간다.

Event Loop는 계속 확인한다.

Call Stack이 비었나?
↓
비었으면 Queue의 작업 실행

이 구조 덕분에 싱글 스레드에서도 비동기 처리가 가능하다.

4. 실행 순서 예시

console.log("A");

setTimeout(() => {
    console.log("B");
}, 0);

console.log("C");

결과:

A
C
B

setTimeout(..., 0)이라고 해도 바로 실행되는 것이 아니다.

callback은 Queue에서 기다리고, 현재 동기 코드가 모두 끝난 뒤 실행된다.

5. 동기 vs 비동기

동기:

const a = 1;
const b = 2;
console.log(a + b);

앞 작업이 끝나야 다음 작업이 실행된다.

비동기:

fetch("/api/users");
console.log("next");

네트워크 응답을 기다리는 동안 다음 코드가 계속 실행될 수 있다.

6. Callback 방식

초기 JS에서는 비동기 처리를 callback으로 많이 했다.

setTimeout(() => {
    console.log("done");
}, 1000);

하지만 여러 비동기 작업이 중첩되면 코드가 복잡해진다.

a(() => {
    b(() => {
        c(() => {
        });
    });
});

이런 형태를 흔히 Callback Hell이라고 한다.

7. Promise

Promise는 비동기 작업의 결과를 표현하는 객체다.

상태는 3개다.

pending
→ 진행 중

fulfilled
→ 성공

rejected
→ 실패

사용 예:

fetch("/api/users")
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(error => console.error(error));

8. async / await

Promise를 더 읽기 쉽게 사용하는 문법이다.

async function getUsers() {
    const response = await fetch("/api/users");
    const data = await response.json();

    return data;
}

await는 Promise가 처리될 때까지 해당 async 함수의 다음 실행을 잠시 멈춘다.

중요한 점은 JS 전체 스레드를 멈추는 것은 아니다.

9. Microtask Queue

비동기 Queue가 하나만 있는 것은 아니다.

대표적으로:

Microtask Queue
→ Promise
→ async/await 후속 작업

Task Queue
→ setTimeout
→ setInterval
→ 일부 이벤트

일반적으로 현재 코드가 끝난 후:

Call Stack
↓
Microtask Queue
↓
Task Queue

순서로 처리된다.

10. 실행 순서 문제

console.log("A");

setTimeout(() => console.log("B"), 0);

Promise.resolve()
    .then(() => console.log("C"));

console.log("D");

결과:

A
D
C
B

이유:

동기 코드
→ A, D

Microtask
→ C

Task
→ B

핵심 정리

JavaScript
→ 싱글 스레드
→ Call Stack 하나

비동기 작업
→ 브라우저 Web API가 처리

작업 완료
→ Queue로 이동

Event Loop
→ Call Stack이 비면 Queue 작업 실행

Promise
→ 비동기 작업의 상태와 결과 표현

async / await
→ Promise를 읽기 쉽게 사용하는 문법

실행 우선순위
→ 동기 코드
→ Microtask
→ Task Queue

Part 7. 브라우저 / DOM / Event

1. 브라우저에서 JS의 역할

브라우저는 HTML, CSS, JavaScript를 함께 처리한다.

HTML → 구조
CSS → 스타일
JavaScript → 동작

JavaScript는 브라우저가 제공하는 API를 통해 화면과 상호작용한다.

2. DOM

DOM(Document Object Model)은 HTML 문서를 JavaScript가 다룰 수 있는 객체 구조로 표현한 것이다.

<button id="btn">클릭</button>
const btn = document.querySelector("#btn");

여기서 document는 현재 HTML 문서를 나타내는 객체다.

3. DOM 조작

요소 찾기:

const title = document.querySelector(".title");

내용 변경:

title.textContent = "Hello";

스타일 변경:

title.style.fontSize = "20px";

요소 생성:

const div = document.createElement("div");
div.textContent = "new";

4. window와 document

브라우저에서 가장 상위 객체는 보통 window다.

window.alert("hello");
window.location;

document도 window 아래에 있다.

개념적으로:

window
 └─ document
     └─ HTML DOM

5. Event

사용자의 행동이나 브라우저의 상태 변화를 Event라고 한다.

대표적인 이벤트:

click
input
change
submit
keydown
load
scroll

이벤트 등록:

btn.addEventListener("click", () => {
    console.log("clicked");
});

6. Event 객체

이벤트 핸들러에는 이벤트 정보가 전달된다.

btn.addEventListener("click", (event) => {
    console.log(event.target);
});

event.target은 실제 이벤트가 발생한 요소를 의미한다.

7. Event Bubbling

이벤트는 기본적으로 자식 요소에서 부모 요소 방향으로 전파된다.

<div id="parent">
    <button id="child">Click</button>
</div>

버튼 클릭 시:

button
→ div
→ body
→ document

방향으로 이벤트가 올라간다.

이를 Event Bubbling이라고 한다.

8. Event Capturing

반대로 부모에서 자식 방향으로 내려가는 단계도 있다.

document
→ body
→ div
→ button

이를 Capturing이라고 한다.

전체 흐름은:

Capturing
↓
Target
↓
Bubbling

이다.

9. preventDefault()

브라우저의 기본 동작을 막는다.

예를 들어 form은 submit 시 기본적으로 페이지를 이동하거나 새로고침할 수 있다.

form.addEventListener("submit", (event) => {
    event.preventDefault();
});

SPA나 React에서도 자주 접한다.

10. stopPropagation()

이벤트가 부모로 전파되는 것을 막는다.

button.addEventListener("click", (event) => {
    event.stopPropagation();
});

즉:

preventDefault()
→ 브라우저 기본 동작 차단

stopPropagation()
→ 이벤트 전파 차단

핵심 정리

DOM
→ HTML을 JS에서 다룰 수 있게 객체 구조로 표현

document
→ 현재 HTML 문서

window
→ 브라우저의 최상위 객체

Event
→ 클릭, 입력, 제출 등 사용자/브라우저 동작

Event Bubbling
→ 자식 → 부모

Event Capturing
→ 부모 → 자식

preventDefault()
→ 기본 동작 차단

stopPropagation()
→ 이벤트 전파 차단

Part 8. fetch / HTTP / CORS / 인증

1. fetch

브라우저에서 HTTP 요청을 보낼 때 사용하는 기본 API다.

const response = await fetch("/api/users");
const data = await response.json();

흐름은:

브라우저
→ HTTP 요청
→ 서버
→ HTTP 응답
→ JSON 변환

2. HTTP 요청 기본 구조

주로 쓰는 메서드:

GET    → 조회
POST   → 생성
PUT    → 전체 수정
PATCH  → 일부 수정
DELETE → 삭제

예:

await fetch("/api/users", {
    method: "POST",
    headers: {
        "Content-Type": "application/json"
    },
    body: JSON.stringify({
        name: "Kim"
    })
});

3. JSON

프론트와 백엔드가 데이터를 주고받을 때 매우 자주 사용한다.

JS 객체:

const user = {
    name: "Kim",
    age: 20
};

JSON 문자열로 변환:

JSON.stringify(user);

JSON 문자열을 객체로 변환:

JSON.parse(json);

4. fetch에서 주의할 점

fetch는 404, 500 응답이 와도 Promise 자체는 보통 reject되지 않는다.

const response = await fetch("/api/users");

if (!response.ok) {
    throw new Error("요청 실패");
}

즉:

네트워크 자체 실패
→ reject

HTTP 404 / 500
→ 응답은 정상적으로 도착
→ response.ok 확인 필요

5. Same-Origin Policy

브라우저는 보안을 위해 다른 출처의 리소스 접근을 제한한다.

Origin은 보통 다음 3개로 결정된다.

protocol + host + port

예:

https://example.com:443

이 중 하나라도 다르면 다른 Origin으로 본다.

6. CORS

CORS는 서버가 브라우저에게:

"이 Origin에서 오는 요청은 허용해도 된다"

라고 알려주는 방식이다.

예를 들어:

Frontend
http://localhost:3000

Backend
http://localhost:8080

포트가 다르므로 서로 다른 Origin이다.

서버가 적절한 CORS 헤더를 주지 않으면 브라우저가 요청을 차단할 수 있다.

중요한 점:

CORS는 브라우저의 보안 정책이다.

서버끼리 통신할 때는 일반적으로 CORS 문제가 발생하지 않는다.

브라우저에 저장되는 작은 데이터다.

서버가:

Set-Cookie

헤더를 보내면 브라우저가 저장하고 이후 요청에 자동으로 포함할 수 있다.

주로:

로그인 상태
세션 ID
사용자 설정

등에 사용한다.

8. Session

Session 방식은 로그인 정보를 주로 서버에서 관리한다.

로그인
↓
서버가 Session 생성
↓
Session ID 발급
↓
브라우저 Cookie에 저장
↓
이후 요청마다 Session ID 전달

서버는 Session ID를 보고 사용자를 식별한다.

9. JWT

JWT 방식에서는 사용자 정보를 포함한 토큰을 클라이언트에 전달한다.

로그인
↓
서버가 JWT 발급
↓
클라이언트가 저장
↓
요청마다 JWT 전달

주로:

Authorization: Bearer <token>

형태로 전달한다.

10. Session vs JWT

핵심 차이는 상태를 어디서 관리하느냐다.

Session
→ 서버가 로그인 상태 관리

JWT
→ 토큰 자체에 인증 정보 포함

단, JWT라고 해서 무조건 더 좋거나 더 최신 방식인 것은 아니다.

서비스 구조와 보안 요구사항에 따라 선택한다.

핵심 정리

fetch
→ 브라우저 HTTP 요청 API

JSON.stringify()
→ JS 객체 → JSON 문자열

JSON.parse()
→ JSON 문자열 → JS 객체

response.ok
→ HTTP 요청 성공 여부 확인

Same-Origin Policy
→ 브라우저가 다른 출처 접근 제한

CORS
→ 서버가 다른 Origin의 요청을 허용하는 방식

Cookie
→ 브라우저에 저장되는 데이터

Session
→ 서버 중심 인증 상태 관리

JWT
→ 토큰 기반 인증

Part 9. Node.js / npm / package.json / Module / Vite

1. Node.js

Node.js는 브라우저 밖에서도 JavaScript를 실행할 수 있게 해주는 런타임 환경이다.

JavaScript = 언어

Browser
→ JavaScript + Browser API

Node.js
→ JavaScript + Node API

즉 Node.js가 있으면 서버, CLI, 빌드 도구 등을 JS로 만들 수 있다.

2. npm

npm은 Node.js 패키지 관리자다.

npm install axios

외부 라이브러리를 설치하고 관리할 수 있다.

대표 명령:

npm install
→ 의존성 설치

npm install 패키지명
→ 패키지 추가

npm run dev
→ package.json에 정의된 dev 명령 실행

3. package.json

프로젝트 정보와 의존성을 관리하는 파일이다.

{
  "name": "my-app",
  "scripts": {
    "dev": "vite"
  },
  "dependencies": {
    "react": "^19.0.0"
  }
}

주요 역할:

프로젝트 정보
의존성 목록
실행 명령어
빌드 설정

4. node_modules

npm으로 설치한 라이브러리들이 들어가는 폴더다.

node_modules/

보통 용량이 크기 때문에 Git에는 올리지 않는다.

대신 package.json과 lock 파일을 공유하고:

npm install

로 다시 설치한다.

5. Module

코드를 여러 파일로 나누고 필요한 것만 가져오는 방식이다.

export function add(a, b) {
    return a + b;
}

다른 파일:

import { add } from "./math.js";

이런 방식을 ES Module(ESM)이라고 한다.

6. CommonJS

Node.js에서는 과거에 CommonJS 방식도 많이 사용했다.

const express = require("express");

내보내기:

module.exports = something;

현재는 ES Module 방식도 많이 사용한다.

import express from "express";

7. Bundler

웹 프로젝트는 파일이 많아지기 때문에 여러 파일과 의존성을 정리해 브라우저가 사용할 수 있게 묶는 과정이 필요할 수 있다.

이 역할을 하는 도구가 Bundler다.

대표적으로:

Webpack
Rollup
esbuild

8. Vite

Vite는 현대 프론트엔드 개발에서 많이 사용하는 개발 서버 + 빌드 도구다.

npm create vite@latest
npm run dev

주요 역할:

개발 서버 실행
빠른 코드 변경 반영
모듈 처리
프로덕션 빌드

React 프로젝트 만들 때 자주 보게 된다.

9. 개발 환경 전체 흐름

대략 이렇게 연결된다.

JavaScript 코드
↓
npm으로 라이브러리 관리
↓
package.json에 의존성 기록
↓
Vite로 개발 서버 실행
↓
빌드
↓
브라우저에서 실행

핵심 정리

Node.js
→ 브라우저 밖에서 JS 실행

npm
→ JS 패키지 관리자

package.json
→ 프로젝트 설정 + 의존성 + 실행 명령

node_modules
→ 설치된 패키지 저장

ES Module
→ import / export

CommonJS
→ require / module.exports

Bundler
→ 여러 파일과 의존성을 웹 실행 형태로 묶음

Vite
→ 개발 서버 + 빌드 도구

Part 10. React를 이해하기 위한 JavaScript 핵심

React는 새로운 언어가 아니라 JavaScript를 기반으로 UI를 만드는 라이브러리다. 그래서 아래 JS 개념을 잘 알면 React 코드가 훨씬 쉽게 읽힌다.

1. 구조 분해 할당

React에서 매우 자주 등장한다.

const user = {
  name: "Kim",
  age: 20
};

const { name, age } = user;

배열 구조 분해:

const [a, b] = [10, 20];

React의 useState도 같은 문법이다.

const [count, setCount] = useState(0);

2. Spread와 불변성

React에서는 기존 객체를 직접 수정하기보다 새 객체를 만드는 방식을 많이 사용한다.

const user = {
  name: "Kim",
  age: 20
};

const newUser = {
  ...user,
  age: 21
};

배열도 동일하다.

const newArr = [...arr, 4];

핵심은:

기존 값 직접 변경 X
→ 새로운 객체/배열 생성

이걸 불변성(immutability)이라고 한다.

3. map

배열을 다른 배열로 변환한다.

const nums = [1, 2, 3];

const doubled = nums.map(n => n * 2);

React에서는 리스트 렌더링에 매우 자주 사용한다.

users.map(user => (
  <div>{user.name}</div>
));

4. filter

조건에 맞는 값만 남긴다.

const nums = [1, 2, 3, 4];

const even = nums.filter(n => n % 2 === 0);

삭제 처리에도 자주 사용한다.

const newUsers = users.filter(user => user.id !== id);

5. find

조건에 맞는 첫 번째 값을 찾는다.

const user = users.find(user => user.id === 3);

API 응답이나 상태에서 특정 데이터를 찾을 때 자주 사용한다.

6. 조건부 렌더링에 쓰이는 JS 문법

삼항 연산자:

const message = login ? "로그인됨" : "로그인 필요";

React:

{login ? <Main /> : <Login />}

AND 연산자도 자주 사용한다.

{login && <Profile />}

login이 truthy일 때만 <Profile />이 렌더링된다.

7. Optional Chaining

값이 없을 수도 있을 때 안전하게 접근한다.

user.profile.name

profile이 undefined면 에러가 발생한다.

그래서:

user?.profile?.name

처럼 사용할 수 있다.

API 데이터를 다룰 때 특히 자주 등장한다.

8. Nullish Coalescing

null 또는 undefined일 때 기본값을 사용한다.

const name = user.name ?? "Unknown";

||와 차이가 있다.

0 || 100   // 100
0 ?? 100   // 0

??는 null, undefined만 비어 있는 값으로 판단한다.

9. Callback과 이벤트

React 이벤트도 결국 함수를 전달하는 구조다.

function handleClick() {
  console.log("click");
}

<button onClick={handleClick}>
  Click
</button>

즉:

onClick
→ callback 함수 전달

이다.

10. Closure와 React

React의 함수 컴포넌트와 Hook에서는 closure가 중요하다.

function Counter() {
  const [count, setCount] = useState(0);

  function handleClick() {
    console.log(count);
  }

handleClick은 선언될 당시의 count 값을 참조한다.

그래서 React에서 흔히 말하는:

stale closure

문제도 JS의 closure 특성과 관련되어 있다.

11. 비동기와 React

API 호출:

async function getUsers() {
  const response = await fetch("/api/users");
  const data = await response.json();

  return data;
}

React에서는 이런 비동기 작업을 상태와 연결한다.

개념적으로:

API 요청
↓
응답
↓
state 변경
↓
컴포넌트 다시 렌더링

React를 이해하려면 Promise, async/await가 중요한 이유다.

핵심 정리

React에서 특히 중요한 JS

구조 분해 할당
→ const { name } = user

Spread
→ ...obj, ...arr

불변성
→ 기존 값 수정 대신 새 값 생성

map
→ 배열 변환 / 리스트 렌더링

filter
→ 조건에 맞는 값만 유지

find
→ 특정 값 검색

?. 
→ 안전한 속성 접근

??
→ null/undefined 기본값 처리

Callback
→ 이벤트 처리

Closure
→ Hook과 상태 이해에 중요

async/await
→ API 통신에 필수
profile
멋있는 사람이 되는 게 꿈입니다

0개의 댓글