반응형

 

클로저(Closure)란?

    “클로저는 함수가 외부의 변수에 접근할 수 있는 기능”이다.

     즉, 내부 함수가 외부 함수의 변수에 접근하고 기억하는 것이 클로저이다.


예제 코드로 살펴보기 🔍
function outer() {
    let count = 0; // 외부 함수의 지역 변수

    return function inner() {
        count++;
        console.log(`현재 count는 ${count}입니다.`);
    };
}

const counter = outer(); // outer는 실행되고, inner가 반환됨
counter(); // 현재 count는 1입니다.
counter(); // 현재 count는 2입니다.

 

겉으로만 봤을 때는 counter를 실행하면 count가 각각 1, 1로 출력될 것 같지만 실제로는 아니다.

outer() 함수는 실행되고 사라졌지만,그 안에서 만든 inner() 함수는 count를 기억하고 있다.

왜냐하면 count가 클로저에 포함되어서 메모리에 살아있기 때문이다.


클로저의 핵심 구성
구성 요소 설명
외부 함수 (outer) 지역 변수를 선언하는 함수
내부 함수 (inner) 외부 함수의 지역 변수에 접근하는 함수
반환 내부 함수가 외부로 반환되어 사용됨
기억 외부 함수의 지역 변수를 내부 함수가 기억 (→ 클로저)

그렇다면 클로저를 왜 쓰는걸까?

 

    1. 데이터 보호 / 은닉 (캡슐화): 외부에서 직접 접근 못하게 하고, 함수로만 조작하도록 할 수 있음.

function createCounter() {
    let count = 0;
    return {
        increment() { count++; return count; },
        decrement() { count--; return count; },
    };
}

const counter = createCounter();
counter.increment(); // 1
counter.decrement(); // 0
// count에 직접 접근은 불가능

 

    2. 상태 유지:  클로저를 사용하면 상태를 기억하게 할 수 있음.

    3. 콜백, 이벤트 핸들러:  비동기 처리에서도 클로저가 유용하게 쓰임.

 

 

[주의할 점]
1) 메모리 누수: 클로저로 인해 변수가 해제되지 않고 남아있으면 메모리 낭비가 발생할 수 있다.
2) 남용 주의: 모든 함수에 클로저를 만들 필요는 없고, 필요한 상황에서만 쓰는 게 좋다.

 

요약

 

    1) 클로저는 내부 함수가 외부 함수의 변수에 접근할 수 있게 해주는 기능

    2) 함수가 자신이 만들어질 때의 환경을 기억

    3) 상태 유지, 캡슐화, 비동기 처리 등에 매우 유용

    4) 하지만 메모리 관리에는 주의해야 함

 

반응형
반응형

 

하루에 한 문제씩은 꼭 코딩 테스트를 풀고 있는 요즘 .. 메서드의 중요성을 크게 깨닫고 있다 ㅎㅎ ..

그래서 준비해본 자바스크립트에서의 다양한 메서드 알아보기 ! 🧐

 

문자열 관련 메서드

 

1. chatAt(index): 문자열 특정 인덱스의 문자 반환

const str = "Hello";
console.log(str.charAt(1));  // "e"

 

2. split(separator): 문자열을 특정 구분자로 나누어 배열로 반환

const str = "apple,banana,orange";
str.split(",");  // ["apple", "banana", "orange"]

 

3. replace(searchValue, newValue): 문자열에서 특정 패턴을 찾아 다른 값으로 교체

const str = "Hello, world!";
str.replace("world", "everyone");  // "Hello, everyone!"

 

4. toUpperCase(): 문자열을 대문자로 변환

  + toLowerCase(): 문자열을 소문자로 변환

const str = "Hello";
str.toUpperCase();  // "HELLO"
str.toLowerCase();  // "hello"

 

5. trim(): 문자열 양옆의 공백 제거

const str = "   Hello, World!   ";
str.trim();  // "Hello, World!"

 

6. repeat(count): 문자열을 지정한 횟수만큼 반복

const str = "abc";
str.repeat(3);  // "abcabcabc"

배열 메서드

 

1. push(element): 배열의 끝에 요소 추가

const arr = [1, 2, 3];
arr.push(4);  // [1, 2, 3, 4]

 

2. pop(): 배열의 마지막 요소 제거

const arr = [1, 2, 3];
arr.pop();  // [1, 2]

 

3. unshift(element): 배열의 첫 번째에 요소 추가

const arr = [1, 2, 3];
arr.unshift(0);  // [0, 1, 2, 3]

 

4. shift(): 배열의 첫 번째 요소 제거

const arr = [1, 2, 3];
arr.shift();  // [2, 3]

 

5. join(separator): 배열의 모든 요소를 연결하여 하나의 문자열로 반환

const array = [1, 2, 3];
array.join("");  // "123"

 

6. forEach(callback): 각 요소에 대해 콜백 함수 실행

// 배열
const arr = [1, 2, 3];
arr.forEach(num => console.log(num));

 

7. map(callback): 배열의 각 요소에 함수를 적용하여 새로운 배열 반환

const arr = [1, 2, 3];
arr.map(num => num * 2);  // [2, 4, 6]

 

8. filter(callback): 배열에서 조건을 만족하는 요소만 필터링하여 새로운 배열 반환

const arr = [1, 2, 3, 4, 5];
arr.filter(num => num % 2 === 0);  // [2, 4]

 

9. reduce(callback, initialValue): 배열을 하나의 값으로 축소

const arr = [1, 2, 3, 4];
arr.reduce((acc, num) => acc + num, 0);  // 10

 

10. sort(compareFunction): 배열을 정렬

const arr = [3, 1, 2];
arr.sort((a, b) => a - b);  // [1, 2, 3]

문자열과 배열 모두 사용할 수 있는 메서드

 

1. includes(searchValue): 특정 값이 포함되어 있는지 확인

const str = "Hello, world!";
const array = [1, 2, 3];

console.log(str.includes("world"));  // true
console.log(str.includes("Hi"));     // false

console.log(array.includes(2));      // true
console.log(array.includes(5));      // false

 

2. indexOf(searchValue):  특정 값의 첫 번째 위치를 반환

  + lastIndexOf(searchValue): 특정 값의 마지막 위치를 반환

const str = "Hello, world!";
const array = [1, 2, 1, 3];

str.indexOf("o");      // 4
str.lastIndexOf("l");  // 10

array.indexOf(1);      // 0
array.indexOf(1);      // 2

 

3. slice(startIndex, endIndex): 특정 부분을 잘라내어 반환

     * endIndex는 포함되지 않음.

const str = "Hello, world!";
const array = [1, 2, 3, 4];

str.slice(0, 5);  // "Hello"
array.slice(0, 2);  // [1, 2]

 

4. concat(value1, value2, ...): 여러 문자열이나 배열 결합

const str1 = "Hello";
const str2 = "World";
str1.concat(" ", str2);  // "Hello World"

const arr1 = [1, 2];
const arr2 = [3, 4];
arr1.concat(arr2)  // [1, 2, 3, 4]

문자열 메서드 의미 배열 메서드 의미 배열 메서드 의미
chatAt() 특정 인덱스의 문자 반환 push() 마지막 요소 추가 forEach() 각 요소에 대해 콜백 함수 실행
split() 특정 구분자로 나누어 배열로 반환 pop() 마지막 요소 제거 map() 각 요소에 함수를 적용하여
새로운 배열 반환
replace() 특정 값을 다른 값으로 교체 unshift() 첫 번째 요소 추가 filter() 조건을 만족하는 요소만
필터링하여 새로운 배열 반환
toUpperCase()
toLowerCase()
대문자로 변환
문자로 변환
shift() 첫 번째 요소 제거 reduce()  배열을 하나의 값으로 축소
trim() 양옆의 공백 제거 join() 모든 요소를 연결하여 하나의 문자열로 반환 sort() 배열 정렬
repeat() 지정한 횟수만큼 반복  
문자열 + 배열 공통 메서드 의미
includes() 특정 값이 포함되어 있는지 확인
indexOf()
lastIndexOf()
특정 값의 첫 번째 위치를 반환
특정 값의 마지막 위치를 반환
slice(startIndex, endIndex) 특정 부분을 잘라내어 반환
concat(value1, value2, ...) 여러 문자열이나 배열 결합

객체 관련 메서드

 

1. Object.keys(): 객체의 key를 배열로 반환

const obj = { a: 1, b: 2, c: 3 };
const keys = Object.keys(obj);
console.log(keys);  // ["a", "b", "c"]

 

2. Object.values(): 객체의 value를 배열로 반환

const obj = { a: 1, b: 2, c: 3 };
const values = Object.values(obj);
console.log(values);  // [1, 2, 3]

 

3. Object.entries(): 객체의 key-value 쌍을 배열로 반환

const obj = { a: 1, b: 2, c: 3 };
const entries = Object.entries(obj);
console.log(entries);  // [["a", 1], ["b", 2], ["c", 3]]

 

4. Object.assign(): 객체를 다른 객체로 복사하거나 병합

const obj1 = { a: 1 };
const obj2 = { b: 2 };
const merged = Object.assign({}, obj1, obj2);
console.log(merged);  // { a: 1, b: 2 }

 

5. Object.freeze(): 객체를 동결하여 수정 불가하게 만들기

const obj = { a: 1 };
Object.freeze(obj);
obj.a = 2;  // 수정 불가
console.log(obj.a);  // 1

 

6. Object.seal(): 객체를 봉인하여 프로퍼티 삭제 불가하게 만들기 (수정은 가능)

const obj = { a: 1 };
Object.seal(obj);
obj.a = 2;  // 수정 가능
delete obj.a;  // 삭제 불가
console.log(obj.a);  // 2

 

7. Object.hasOwnProperty(searchValue): 객체에 특정 속성이 존재하는지 확인

const obj = { a: 1, b: 2 };
console.log(obj.hasOwnProperty("a"));  // true
console.log(obj.hasOwnProperty("c"));  // false

 

8. Object.fromEntries(): 배열이나 Map을 객체로 변환

const arr = [["a", 1], ["b", 2]];
const obj = Object.fromEntries(arr);
console.log(obj);  // { a: 1, b: 2 }

Map 관련 메서드

 

1. set(key, value): key-value 쌍을 Map에 추가

const map = new Map();
map.set("a", 1);
map.set("b", 2);
console.log(map);  // Map(2) { 'a' => 1, 'b' => 2 }

 

2. get(key): Map에서 특정 key 값을 가져옴

const map = new Map([["a", 1], ["b", 2]]);
console.log(map.get("a"));  // 1
console.log(map.get("c"));  // undefined

 

3. has(key): Map에 특정 key가 존재하는지 확인

const map = new Map([["a", 1], ["b", 2]]);
console.log(map.has("a"));  // true
console.log(map.has("c"));  // false

 

4. delete(key): Map에서 특정 key-value 쌍을 삭제

const map = new Map([["a", 1], ["b", 2]]);
map.delete("a");
console.log(map);  // Map(1) { 'b' => 2 }

 

5. clear(): Map의 모든 요소 삭제

const map = new Map([["a", 1], ["b", 2]]);
map.clear();
console.log(map);  // Map(0) {}

 

6. size(): Map의 요소 개수 확인

const map = new Map([["a", 1], ["b", 2]]);
console.log(map.size);  // 2

 

7. keys(): Map의 key들을 iterator 형태로 반환

const map = new Map([["a", 1], ["b", 2]]);
const keys = [...map.keys()];
console.log(keys);  // ["a", "b"]

 

8. values(): Map의 value들을 iterator 형태로 반환

const map = new Map([["a", 1], ["b", 2]]);
const values = [...map.values()];
console.log(values);  // [1, 2]

 

9. entries(): Map의 key-value 쌍들을 iterator 형태로 반환

const map = new Map([["a", 1], ["b", 2]]);
const entries = [...map.entries()];
console.log(entries);  // [["a", 1], ["b", 2]]

객체와 Map에 공통적인 메서드

 

1. forEach(): Map의 각 요소에 대해 콜백 함수 실행

// 객체
const obj = { a: 1, b: 2 };
Object.entries(obj).forEach(([key, value]) => {
  console.log(`${key}: ${value}`);
});

// Map
const map = new Map([["a", 1], ["b", 2]]);
map.forEach((value, key) => {
  console.log(`${key}: ${value}`);
});
[객체와 Map의 차이점 💡]
1) 객체는 주로 문자열을 key로 사용하고, 순서가 보장되지 않음.
    (비록 ES6 이후 객체는 키의 순서를 어느 정도 보장하지만 Map보다는 덜 신뢰할 수 있음)
2) Map임의의 자료형을 key로 사용 가능하고, 항상 순서를 보장하며, 성능이 더 뛰어날 때가 많음.

객체 메서드 의미 Map 메서드 의미
Object.keys() key를 배열로 반환 set(key, value) key-value 쌍을 Map에 추가
Object.values() value를 배열로 반환 get(key) 특정 key 값을 가져옴
Object.entries() key-value 쌍을 배열로 반환 has(key) 특정 key가 존재하는지 확인
Object.assign() 다른 객체로 복사하거나 병합 delete(key) 특정 key-value 쌍을 삭제
Object.freeze() 동결하여 수정 불가하게 만들기 clear() 모든 요소 삭제
Object.seal() 봉인하여 프로퍼티 삭제 불가하게 만들기 (수정은 가능) size() 요소 개수 확인
Object.hasOwnProperty() 특정 속성이 존재하는지 확인 keys() key들을 iterator 형태로 반환
Object.fromEntries() 배열이나 Map을 객체로 변환 values() value들을 iterator 형태로 반환
    entries()

key-value 쌍들을 iterator 형태로 반환
객체 + Map 공통적인 메서드 의미
forEach() Map의 각 요소에 대해 콜백 함수 실행

 

반응형
반응형

 

var, let, const는 크게 변수의 범위(스코프) / 중복 선언 / 호이스팅으로 구분하여 비교할 수 있다.


변수의 범위로 보는 차이점

 

1. var함수 스코프(function scope)를 가지는 변수이다. 즉, 변수를 선언한 함수 내에서만 유효하고, 함수 밖에서는 접근할 수 없다.

    함수 바깥에서 선언하면 전역 변수(global variable)로 취급된다.

     * 함수 스코프에서만 가지므로 if문이나 for문 등에서는 적용이 되지 않는다.

function sayHi () { 
  var say = 'Hi';
  console.log(say);  // Hi 출력
}
console.log(say);  // Error 발생

if(true) {
  var title = 'JavaScript';
  console.log(title);  // JavaScript 출력
}
console.log(title);  // JavaScript 출력

for(var i = 1; i < 4; i++;) {
  console.log(i);  // 1부터 3까지 출력
}
console.log(i); // 4 출력

   

2. let, const블록 스코프(block scope)를 가지므로, 변수를 선언한 {} 안에서만 유효하고, 그 밖에서는 접근할 수 없다.

function sayHi () { 
  let say = 'Hi';
  console.log(say);  // Hi 출력
}
console.log(say);  // Error 발생

if(true) {
  const title = 'JavaScript';
  console.log(title);  // JavaScript 출력
}
console.log(title);  // Error 발생

for(let i = 1; i < 4; i++;) {
  console.log(i);  // 1부터 3까지 출력
}
console.log(i); // Error 발생

중복 선언

 

1. var같은 이름으로 선언된 변수를 다시 선언할 수 있다.

    중복 선언 시에는 나중에 선언된 값으로 덮어씌워진다.

var title = 'JavaScript';
console.log(title);  // JavaScript 출력

var title = 'HTML';
console.log(title);  // HTML 출력

title = 'CSS';
console.log(title);  // CSS 출력

 

2. let같은 스코프 내에서 같은 이름의 변수는 다시 선언할 수 없다.

let title = 'JavaScript';
console.log(title);  // JavaScript 출력

let title = 'HTML';
console.log(title);  // Syntax Error

title = 'CSS';
console.log(title);  // CSS 출력

 

3. const같은 스코프 내에서 같은 이름의 변수는 다시 선언할 수 없다.

    또한, 선언 후 재할당이 불가능하고, 한번 값이 할당되면 그 값을 변경할 수 없다.

    하지만 객체나 배열 같은 참조형 데이터의 경우, 객체의 속성이나 배열의 요소를 변경할 수 있다.

const title = 'JavaScript';
console.log(title);  // JavaScript 출력

const title = 'CSS' // Syntax Error
title = 'HTML'; // Type Error

const obj = { name: 'Alice' };
obj.name = 'Bob'; // 가능

호이스팅(Hoisting)

 

[호이스팅이란? 🔍]

호이스팅은 자바스크립트의 변수 선언함수 선언코드 실행 전에 끌어올려지는 현상이다.

자바스크립트는 코드를 실행하기 전에 선언을 먼저 처리하고, 그 다음에 실제 코드 실행을 처리한다는 특징이 있다.

하지만 호이스팅이 어떻게 작동하는지에 따라 다르게 동작할 수 있다.


1. var로 선언된 변수는 선언만 호이스팅되고 초기화는 되지 않는다.

    즉, 변수가 함수의 제일 위로 끌어올려지지만 값은 할당되지 않은 상태로 올라간다.

console.log(title);  // undefined 출력

var title = 'This is title';

 

2. let, const으로 선언된 변수도 호이스팅이 일어나지만, 변수 선언 전에 접근하면 ReferenceError가 발생한다.

    초기화 전까지는 Temporal Dead Zone (TDZ)에 있기 때문에 값을 참조할 수 없다.

console.log(title);  // Reference Error
console.log(name);   // Reference Error

let title = 'This is title';
const name = 'My name is name';

요약 정리
특성 var let const
범위(scope) 함수 스코프 블록 스코프 블록 스코프
재선언 가능 불가능 불가능
재할당 가능 가능 불가능
호이스팅 선언만 호이스팅 (값은 undefined) 선언 및 초기화 전까지 접근 불가 (TDZ) 선언 및 초기화 전까지 접근 불가 (TDZ)

 

반응형
반응형

 

.js 란?

    .js는 자바스크립트의 파일 확장자이며 일반적으로 사용하는 순수 자바스크립트 코드가 들어간다.

    브라우저가 이해할 수 있는 코드이고, 리액트를 쓰지 않아도 문제 없는 파일 형식이다.

 

[예시 코드]

function sayHello(name) {
  console.log("Hello, " + name);
}

sayHello("John");

.jsx 란?

    .jsx는 JavaScript XML의 약자이며 자바스크립트 코드 안에 HTML과 비슷한 문법을 쓸 수 있게 해주는 확장자이다.

    주로 React에서 사용되며, JSX 문법을 사용하면 마치 HTML처럼 UI를 구성할 수 있다.

    하지만 브라우저는 JSX를 이해하지 못하므로 컴파일 변환이 필요하다.

      * .js에서도 JSX 문법을 사용할 수는 있음!

[개념 알고가기 💡]
1. XML(eXtensible Markup Language)이란?
    확장 가능한 마크업 언어로, HTML처럼 태그로 데이터를 표현하지만 HTML보다 훨씬 더 유연하고 데이터 중심적이다.
    원래는 데이터 저장/전송용 포맷으로 만들어졌으며, 특징은 다음과 같다.
    1) 사용자가 직접 태그를 정의 가능
    2) 구조적 데이터 표현에 적합
    3) 사람도 읽기 쉽고, 기계도 파싱하기 쉬움
    * 단순히 데이터를 표현하는 방법이며, 실제 동작은 하지않고 데이터를 설명하는 "형식"이다.

    * 따라서 JSX는 JavaScript XML이란 이름이지만, 실제로는 XML이 아니고 XML처럼 생긴 문법 자바스크립트에 끼워 넣은 것이다.

 

[예시 코드]

export default function Hello() {
  return <h1>Hello, world!</h1>;
}

 

.js 확장자에서도 JSX 문법을 사용할 수 있는데, 왜 굳이 .jsx로 사용하면서 컴파일 변환을 해줘야하는거지?

.js에서도 JSX를 사용할 수 있는 이유

 

    그 이유는 Babel이나 Webpack 같은 빌드 도구가 .js 파일 안에서도 JSX 문법을 인식하고 변환해줄 수 있게 설정되어 있기 때문이다.

    그렇기 때문에 .js 파일에서 JSX 문법을 사용한다해도, 브라우저는 애초에 JSX를 이해하지 못하기 때문에 컴파일 변환은 일어난다.


그렇다면 .jsx로 확장자를 지정해주는 이유는 뭘까?

     1) 가독성: JSX가 들어있다는 걸 파일 이름만 보고 바로 알 수 있다.

     2) 협업: 다른 개발자에게 "이 파일은 리액트 컴포넌트입니다"라는 신호를 준다.

     3) 에디터 지원: .jsx 파일은 대부분의 에디터가 JSX 문법 강조를 더 잘 지원한다.

     => 즉, 기술적으로 꼭 .jsx를 써야 하진 않지만, 명확한 의도를 전달하는 좋은 습관이 될 수 있다!


 

그렇다면 컴파일 변환은 어떻게 일어나는 걸까?

JSX의 컴파일 흐름

 

1) JSX 코드 작성

const element = <h1>Hello</h1>;

 

2) Babel이 JSX 코드를 자바스크립트로 변환

const element = React.createElement("h1", null, "Hello");

 

3) 변환된 코드는 브라우저에서 실행 가능!


무엇이 컴파일을 해주는 걸까?
[Babel (JS 변환기)]
  - JSX → JavaScript
  - ES6+ → ES5
  - TypeScript → JavaScript
[Webpack (번들러)]
  - 여러 JS 파일들을 하나로 묶고 Babel을 연동해서 변환 과정을 처리하게 함

 

그렇다면 실제 개발 흐름은 다음과 같다.

[개발 흐름 🔍]
1. 소스 코드 작성 (.jsx, .ts, 최신 문법)
2. Babel이 변환 (JSX → 일반 JS)
3. Webpack이 번들링 (여러 파일 → 하나의 main.js)
4. 브라우저는 변환된 JS만 실행

.js 와 .jsx 차이점 요약
항목 .js 파일 .jsx 파일
목적 일반 자바스크립트 코드 JSX 문법을 포함한 자바스크립트 코드
사용 환경 전통적인 웹/서버/Node 등 어디든 가능 주로 React에서 컴포넌트를 만들 때 사용
JSX 사용 가능 (하지만 명확하지 않음) 가능 + JSX가 포함된다고 명확히 표시
컴파일 필요 여부 JSX가 없으면 바로 실행 가능 반드시 Babel 등의 변환이 필요
추천 사용처 유틸 함수, API 로직, 설정 파일 등 React 컴포넌트 작성할 때

 

 

 

반응형
반응형

 

 

동기(Synchronous)란? : 일이 순서대로 끝날 때까지 기다리는 방식

 

라면으로 예시를 들어보자면, 라면을 먹기 위해 물을 끓이기 시작하고, 물이 다 끓으면 면을 넣고 끓인 뒤 라면을 먹는다.

코드로 표현하자면 다음과 같다.

function boilWater() {
  console.log('1. 물 끓이기 시작');
  for (let i = 0; i < 3; i++) {
    console.log('2. 물 끓이는 중 ...');
  }
  console.log('3. 물을 다 끓였어요!');
}

function makeRamen() {
  boilWater();
  console.log('4. 면 넣고 끓이기');
  console.log('5. 라면 완성!');
}

makeRamen();

코드의 흐름을 보면, makeRamen이 호출되면서 먼저 boilWater 함수가 실행된다.

boilWater 함수에서 1번이 출력된 후, 2번이 3번 출력되고 나서 3번이 출력된다.

이 과정이 끝날 때까지 다음 코드는 실행되지 않으며, 작업이 끝나면 함수를 빠져나와 그 이후의 작업인 4번과 5번이 순서대로 출력된다.

// 출력
1. 물 끓이기 시작
2. 물 끓이는 중 ...
2. 물 끓이는 중 ...
2. 물 끓이는 중 ...
3. 물을 다 끓였어요!
4. 면 넣고 끓이기
5. 라면 완성!

 

그렇다면 비동기에서의 라면은 어떻게 끓일까?

 

비동기란(Asynchronous)? : 일이 끝나길 기다리지 않고 다른 일을 먼저 처리하는 방식

 

일단 라면을 먹기 위해 물을 끓일 것이다.

하지만 이전과 다른 점은, 라면을 끓일동안 게임을 하다 올 것이다.

게임을 하다가 물이 끓으면 타이머가 울리고, 그때 면을 넣고 끓인 후 라면을 먹으면 된다.

코드로 표현하자면 다음과 같다.

function boilWaterAsync() {
    console.log("1. 물 끓이기 시작");
    setTimeout(() => {
        console.log("3. 물 끓었음");
        console.log("4. 면 넣고 끓이기");
        console.log("5. 라면 완성!");
    }, 3000);
}

function doSomethingElse() {
    console.log("2. 게임하기 🎮");
}

boilWaterAsync();
doSomethingElse();

먼저 boilWaterAsync가 호출되고 1번이 출력된다.

그 후 setTimeout으로 3초 뒤 실행하게끔 3, 4, 5번이 출력되게끔하고,

그 사이에 doSomethingElse가 호출되면서 2번이 출력된다.

그리고 3초 후 3, 4, 5번이 순서대로 출력된다.

1. 물 끓이기 시작
2. 게임하기
(3초 후 ...)
3. 물 끓었음
4. 면 넣고 끓이기
5. 라면 완성!

 

그럼 이제 비동기에서 중요한 개념들인 콜백, Promise, async/await은 뭘까?

 

콜백 함수란? : 콜백은 “나중에 실행할 함수”를 인자로 전달해서, 어떤 일이 끝난 뒤 실행되게 하는 방식
function cook(callback) {
    console.log("요리 시작");
    setTimeout(() => {
        console.log("요리 끝!");
        callback();
    }, 2000);
}

cook(() => {
    console.log("먹자! 🍽️");
});

위의 코드를 보면, cook이 먼저 호출되고 '요리 시작'이 출력된다.

그 후 2초 뒤에 '요리 끝'이 출력된 다음 아규먼트로 넘겨준 callback 함수가 실행되면서 '먹자!'가 출력된다.

 

콜백 함수에서의 주의점은 콜백 지옥(Callback Hell)을 조심해야하는데,

이는 함수 안에 함수, 또 함수 안에 함수와 같은 형태로 가독성이 떨어지고 디버깅이 어려워진다.

function login(id, callback) {
    setTimeout(() => {
        console.log("로그인 성공");
        callback(id);
    }, 1000);
}

function getUserInfo(id, callback) {
    setTimeout(() => {
        console.log(`사용자 정보 불러오기: ${id}`);
        callback({ id, name: "Alice" });
    }, 1000);
}

function getFriends(user, callback) {
    setTimeout(() => {
        console.log(`${user.name}의 친구 목록`);
        callback(["Bob", "Charlie"]);
    }, 1000);
}

// 콜백 지옥 시작
login("user123", (id) => {
    getUserInfo(id, (user) => {
        getFriends(user, (friends) => {
            console.log("친구 목록:", friends);
        });
    });
});

 

 

Promise란? : 작업이 끝났을 때 성공/실패 결과를 주겠다고 약속하는 객체

 

Promise는 .then()과 .catch()를 이용해서 결과를 처리할 수 있다.

function cook() {
    return new Promise((resolve) => {
        console.log("요리 시작");
        setTimeout(() => {
            console.log("요리 끝!");
            resolve("라면 완성 🍜");
        }, 2000);
    });
}

cook().then((result) => {
    console.log(result); // 라면 완성 🍜
});

cook이 호출되면서 '요리 시작'이 출력되고, 2초 후 '요리 끝'이 출력된다.

그 순간 resolve("라면 완성 🍜")가 호출되어 Promise가 성공(이행)되고, .then()이 그 값을 받아 '라면 완성 🍜'을 출력한다.

 

하지만 Promise를 사용하면서도 주의할 점은 Promise 체인이 길어지면 가독성이 떨어진다.

function login(id) {
    return new Promise((resolve) => {
        setTimeout(() => {
            console.log("로그인 성공");
            resolve(id);
        }, 1000);
    });
}

function getUserInfo(id) {
    return new Promise((resolve) => {
        setTimeout(() => {
            console.log(`사용자 정보 불러오기: ${id}`);
            resolve({ id, name: "Alice" });
        }, 1000);
    });
}

function getFriends(user) {
    return new Promise((resolve) => {
        setTimeout(() => {
            console.log(`${user.name}의 친구 목록`);
            resolve(["Bob", "Charlie"]);
        }, 1000);
    });
}

login("user123")
    .then(getUserInfo)
    .then(getFriends)
    .then((friends) => {
        console.log("친구 목록:", friends);
    });

 

async/await 이란? : Promise를 더 간단하고 동기 코드처럼 보이게 만드는 문법

 

택배로 예시를 들어보면, Promise 같은 경우는 택배가 오면 그때 열어보는 것이고 async/await은 택배가 올 때까지 기다렸다가 열어보는 것이라고 할 수 있다.

function cook() {
    return new Promise((resolve) => {
        console.log("요리 시작");
        setTimeout(() => {
            console.log("요리 끝!");
            resolve("라면 완성 🍜");
        }, 2000);
    });
}

async function eat() {
    const food = await cook(); // cook() 끝날 때까지 기다림
    console.log(food);
    console.log("먹자! 😋");
}

eat();

코드를 보면, 먼저 eat이 호출되고 cook이 실행된다.

cook이 전부 실행될 때 까지 기다린 후에 food가 출력되고 마지막으로 '먹자'가 출력된다.

food에는 cook에서 resolve가 호출되면서 Promise가 성공했기 때문에 '라면 완성'이 저장된다.

가독성도 좋기 때문에 많이 쓰이는 문법이다.


요약
콜백 함수 함수 안에 함수
Promise 성공/실패를 나중에 알려주는 약속
async / await Promise를 더 깔끔하게 쓰는 방식

 

반응형
반응형

 

이전 글에서는 String()과 toString()의 차이점에 대해 알아보았다.

String()과 toString()의 차이점 보러가기: https://y-flm.tistory.com/21
 

JS - String()과 toString()의 차이점

* 앞으로는 자잘한 궁금증을 기록해보려는 습관을 가지려고 한다! *  문득 코딩 테스트 문제를 풀면서 궁금증이 생겼다.String()과 toString(), Number()와 parseInt()의 차이점은 무엇일까?먼저 String()과 t

y-flm.tistory.com

 

Number()

Number()도 String()과 마찬가지로 전역 함수이다.

문자열, 불리언, 객체 등을 숫자로 변환하는데, 만약 문자열이 해석될 수 없는 경우에는 NaN을 반환한다.

let str = '123';
let num = Number(str) // 123

 

parseInt()

parseInt()는 문자열을 정수로 변환해준다.

문자열의 앞부분에서 숫자가 나오면 그 숫자만큼 변환하고, 그 뒤의 문자는 무시한다.

let str = "123.45";
let num = parseInt(str);  // 123

 

만약 소수점까지 변환하고 싶다면 parseFloat()을 사용하면 된다.

let str = "123.45";
let num = parseFloat(str);  // 123.45

 

추가로 parseInt()를 이용해서 진수로 변환한 값을 다시 10진수로 변환할 수 있다.

let binary = "1010";
let decimal = parseInt(binary, 2); // 10

 

 

정리

Number() parseInt() parseFloat()
전역 함수, 숫자로 변환해주지만
해석될 수 없는 문자열이라면 NaN을 반환
문자열을 정수로 변환 문자열을 실수로 변환
숫자 변환이 정확하고,
전체적인 숫자 데이터 변환이 필요할 때 사용
소수점을 제외한 정수만 필요할 때 사용 소수점을 포함한 실수가 필요할 때 사용
반응형
반응형

* 앞으로는 자잘한 궁금증을 기록해 보려는 습관을 가지려고 한다! *

 

 

문득 코딩 테스트 문제를 풀면서 궁금증이 생겼다.

String()과 toString(), Number()와 parseInt()의 차이점은 무엇일까?


먼저 String()과 toString()은 값을 문자열로 변환해 주는 역할을 한다.

String()

String()은 전역 함수로, 숫자 / 배열 / 객체 등 어떠한 값도 문자열로 변환 가능하다.

let num = 123;
let str = String(123); // "123"

 

toString()

toString()은 객체 메서드로, 숫자 / 배열 등의 값에서 사용할 수 있다고 한다.

하지만 null이나 undefined 값을 처리하게 되면 에러가 발생할 수 있다.

let num = 123;
let str = num.toString(); // "123"

 

여기까지만 봤을 때는 '그럼 오류없이 모든 값을 문자열로 변환해 주는 String()을 쓰는 게 더 낫지 않나?'라는 생각이 들었었다.

그래서 각각 사용하는 경우를 찾아보았다.

 

그냥 문자열로 변환하는 것이라면 안전하게 String()을 사용하는 걸 권장한다.

그렇다면 toString()은 언제 쓰나 생각해 봤는데 내가 까먹고 있던 부분이 있었다.

 

숫자를 2 ~ 36진수로 변환할 때 toString()을 사용한다는 걸 잊고 있었다. 

toString() 괄호 안에는 2 ~ 36까지 입력할 수 있고, 생략하면 10진수로 변환된다.

return 12.0.toString(2); // "1100" (2진수)
return 12.0.toString(8); // "14"   (8진수)

* 12.0.toString(2)의 형식으로 작성한 이유는. JS에서 숫자 뒤의. 은 부동 소수점의 일부로 해석하게 된다.

   따라서 12.toString(2)으로 사용할 경우 toString()을 부동 소수점 숫자 뒤에 오는 잘못된 메서드 호출로 인식되기 때문에 오류가 발생한다.

 

   이를 해결하기 위해서는, 

     1. 12.0.toString(2)으로 작성: .0추가, 두 번째. 은 마침표 표기법으로 해석하기 때문.

     2. (12). toString(2)으로 작성: 괄호로 감싸주면 사용 가능


정리

String() toString()
전역 함수, 어떤 값이라도 문자열 변환 가능 객체 메서드, 숫자 / 배열 등 문자열 변환 가능하지만
null이나 undefined 값을 처리하려고 하면 오류 발생
값이 명확하다면 둘 다 상관 X, 하지만 안전하게 String() 사용하는걸 권장
toString()은 진수 변환으로 사용 가능

 

 

Number()와 parseInt()의 차이점은 다음 포스트에!

반응형

+ Recent posts