컴포넌트란?

자식 요소가 로딩중이 완료될 때 까지 fallback에 제공된 컴포넌트를 보여준다.

1
2
3
<Suspense fallback={<Loading />}>
<SomeComponent />
</Suspense>

주의사항

  • 리액트는 처음 마운트 되기 전에 렌더링이 중단된다면, 어떤 상태도 보존하지 않는다. 컴포넌트가 로드되면 리액트는 중단된 트리를 처음부터 렌더링을 시도한다.
  • 컴포넌트는 컴포넌트 내부에서 startTransition이나 useDeferredValue로 인한 업데이트가 아닌 경우라면 fallback이 표시된다.
  • 컴포넌트가 다시 일시 중단되었기 때문에 이미 보여진 컨텐츠를 감출 필요가 있다면, layout effect를 비운다. 다시 컨텐츠를 보여줄 준비가 됐을 때, 리액트는 layout effect를 발생시킨다. 이는 컨텐츠가 숨겨진 동안 DOM layout을 측정하지 않도록 한다.

사용법

컨텐츠가 로딩되는 동안에 fallback을 보여준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { Suspense } from "react";
import Albums from "./Albums.js";

export default function ArtistPage({ artist }) {
return (
<>
<h1>{artist.name}</h1>
<Suspense fallback={<Loading />}>
<Albums artistId={artist.id} />
</Suspense>
</>
);
}

function Loading() {
return <h2>🌀 Loading...</h2>;
}

컨텐츠를 한번에 공개

1
2
3
4
5
6
<Suspense fallback={<Loading />}>
<Biography />
<Panel>
<Albums />
</Panel>
</Suspense>

내부에 하나의 컴포넌트라도 일시중단 되더라도 모든 하위 컴포넌트가 대기했다가 한번에 공개된다.

가장 가까운 의 fallback을 보여준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<Suspense fallback={<Loading />}>
<Details artistId={artist.id} />
</Suspense>;

function Details({ artistId }) {
return (
<>
<Biography artistId={artistId} />
<Panel>
<Albums artistId={artistId} />
</Panel>
</>
);
}

때문에 직접적인 자식이 아니더라도 Biography와 Albums와 가장 가까운 부모 경계를 공유하기 때문에 함께 표시된다.

로드 될 때, 중첩된 컨텐츠 표시한다.

1
2
3
4
5
6
7
8
<Suspense fallback={<BigSpinner />}>
<Biography />
<Suspense fallback={<AlbumsGlimmer />}>
<Panel>
<Albums />
</Panel>
</Suspense>
</Suspense>

처음에 Biography가 로딩이 완료 되지 않았을 때, BigSpinner가 fallback으로 보여지고, 이후 Biography는 로딩이 완료되었기 때문에 보여지고, 아래 Albums이 로딩이 완료되지 않았다면 AlbumsGlimmer를 보여주고 있다가 로딩이 완료되어 Albums를 보여준다.

핵심은 Biography가 Albums 로딩이 완료될 때까지 기다리지 않는 다는 것이다.

이미 공개된 컨텐츠가 숨겨지는 것을 방지하기

Code Example Link

  • 내부에 startTransition이 사용되었다면, fallback이 보여지지 않는다.
  • 때문에 fallback의 BigSpinner에 의해 이전에 보여진 컨텐츠가 숨겨지는 것을 방지할 수 있다.

댓글 공유

Context API란?

앱의 규모가 커지고 복잡해짐에 따라 부모 컴포넌트에서 중첩된 자식 컴포넌트에게 데이터나 데이터 변경함수를 전달해줄 때, 몇단계를 걸쳐서 전달해줌으로서 애플리케이션이 더욱 복잡해지는 문제가 발생하였다.
이를 해결하기 위해 context API가 등장하였고, 이로써 props를 단계별로 넘겨주지 않아도 데이터를 제공할 수 있게 되었다. 그 결과 컴포넌트 트리로 묶인 컴포넌트 간 데이터 공유가 수월해졌다.

  • 리액트에서 제공하는 내장 기능이다.

단, Context API를 사용하게 되면 컴포넌트의 재사용이 어려워지므로 꼭 필요한 경우에만 사용한다.

Redux란?

Redux는 action이라는 이벤트를 사용하여 데이터를 관리할 수 있는 상태관리 라이브러리이다.

redux

리덕스의 3원칙

  1. 단 하나의 store에서 모든 상태가 저장된다.
  2. store의 상태를 변경하는 유일한 방법은 action 객체를 dispatch하는 방법 뿐이다.
  3. action에 의한 상태 변경은 순수함수를 통해서만 가능하도록 작성해야 한다.

리덕스 특징

  • thunk, saga와 같은 미들웨어를 추가적으로 설정할 수 있다. 비동기 처리를 Util로 처리 가능
  • Redux Devtool Extension을 사용하여 디버깅이 용이하다.
  • 전역 상태 관리 이외도 로컬스토리지에 상태 저장, 버그 리포트 등의 기능을 제공한다.

Context API와 Redux의 차이

Context API Redux
Learning Curve 쉬움 어려움
알아야 할 것들 적다 많다
유지보수성 작고 간단한 애플리케이션에 적합 복잡한 애플리케이션에 적합

댓글 공유

React에서 key를 사용하는 이유

리액트에서 배열 데이터와 map 메서드를 사용하여 다수의 컴포넌트를 렌더링할 수 있다.

1
2
3
4
5
6
7
8
9
10
function NumberList({ numbers }) {
const listItems = numbers.map((number) => <li>{number}</li>);
return <ul>{listItems}</ul>;
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById("root")
);

React 프레임워크는 state, props가 업데이트된 이후의 virtual DOM과 이전의 virtual DOM을 비교하고 변경 사항만 Real DOM에 반영하여 효율적으로 UI를 렌더링한다. 이를 재조정 과정이라고 한다.

재조정 과정에서 key가 없다면 이전 DOM과 이후 DOM의 요소를 비교하기 어려워지고 O(n^3) 복잡성을 가지는 알고리즘을 사용해야 하기 때문에 성능상 문제가 생길 수 있다.

댓글 공유

JavaScript 데이터 타입

자바스크립트는 크게 2가지의 데이터 타입으로 나뉜다.

  1. 원시타입 (Primitive Data Type)

  2. 객체타입(참조타입) (Object Type, Reference Type)

이 중에서도 원시타입에는 6가지의 타입이 존재하고 그 외의 것들은 모두 객체타입이다.

Primitive Data Type (원시타입)

원시타입의 종류는 6가지가 있다.

  • number (숫자형)
  • string (문자형)
  • undefined
  • null
  • boolean (불리언형)
  • symbol

원시 타입의 값은 변경 불가능한 값이며, pass-by-value(값에 의한 전달)이다.

number

Javascript에서는 숫자형을 모두 실수로 처리한다. 추가로 아래와 같은 자료도 숫자형이다.

  • Infinity : 양의 무한대
  • -Infinity : 음의 무한대
  • NaN : 산술 연산 불가(Not A Number)

NaN은 자기 자신과 일치하지 않는 유일한 값이다. 그러므로 어떤 값이 NaN인지 판단하기 위해서는 Number.isNaN() 빌트인 함수를 사용하거나 Object.is() 메서드를 사용하여야 한다.

1
2
3
4
5
NaN === NaN; // false

Number.isNaN(NaN); // true

Object.is(NaN, NaN); // true

string

C언어와 다르게 JavaScript의 문자열은 원시타입으로 변경 불가능하다.

1
2
var str = "Hello";
str = "World";

위 코드에서 str 변수에 ‘World’라는 문자열로 데이터를 수정하는 것이 아니다.
임의의 메모리 주소를 갖는 메모리 공간에 ‘World’라는 문자열을 저장하고 변수의 식별자인 str이 새로운 메모리 주소를 가리켜 변수를 재할당하는 것이다.

boolean

참, 거짓을 나타내는 자료형이다. 암묵적 불리언 형변환이 발생하면 해당 값들을 false로 바꿔주는 falsy 값들이 있다.

falsy

  • “” (빈문자열)
  • undefined
  • null
  • 0, -0
  • NaN

undefined

변수의 선언 단계는 사실 선언단계와 초기화 단계 두 단계로 나눠서 볼 수 있는데, 초기화 단계에서 컴퓨터가 변수가 할당되기 이전에 undefined값으로 변수의 값을 초기화해준다.

때문에 이는 개발자의 의도가 담긴 것이 아니다.

null

변수의 값이 없다는 것에 개발자의 의도를 담고 싶다면 null을 사용한다. 자바스크립트는 대소문자를 구별하기 때문에 null, Null, NULL 모두 다른 값이다.

함수가 유효한 값을 반환할 수 없는 경우 명시적을 null을 반환하기도 한다.

null 값의 type을 확인하기 위해서는 typeof 연산자를 사용하는 것 대신 일치 연산자(===)를 사용한다.

1
2
3
var foo = null;
typeof null; // 'object'
foo === null; // true

symbol

심볼은 이름이 충돌한 위험이 없는 유일무이한 원시타입의 값이다. 주로 객체의 유일한 프로퍼티 키를 만들기 위해 사용한다.

1
2
3
4
5
6
var key = Symbol("key");
typeof key; // 'symbol'

var obj = {};

obj[key] = "value"; // 이름이 충돌할 위험이 없는 유일무이한 값인 심볼을 프로퍼티 키로 사용

Object Type, Reference Type (객체타입)

객체란, 데이터와 그 데이터에 관련된 동작(절차, 방법, 기능 등)을 모두 포함할 수 있는 개념적 존재이다.

다시말해 데이터를 의미하는 프로퍼티와 동작을 의미하는 메소드를 포함할 수 있는 독립적 주체이다.

원시타입을 제외한 나머지 값(배열, 함수, 정규표현식 등)이 모두 객체이다. 객체는 pass-by-reference (참조에 의한 전달)이다.

얕은 복사와 깊은 복사

객체는 참조에 의한 전달을 하기 때문에 복사를 객체 데이터를 복사하였을 때, 서로의 참조값을 공유하고 있으므로 변경 사항도 공유되는 특징이 있다.

1
2
3
4
5
6
7
8
const obj = { name: "yiju", age: 29 };
const copy_obj = obj;

copy_obj.name = "loco";

console.log(copy_obj); // {name: 'loco', age: 29}
console.log(obj); // {name: 'loco', age: 29}
console.log(copy_obj === obj); // true

때문에 객체를 복사할 때에는 참조값을 다르게 복사해줘야한다. Object.assign 메서드를 사용하거나 ES6에 등장한 Spread 문법을 사용하여 객체를 복사할 수 있다.

1
2
3
4
5
6
7
8
9
10
const obj = { name: "yiju", age: 29 };
const copy_obj = Object.assign({}, obj);
const copy_obj2 = { ...obj };

copy_obj.name = "loco";
copy_obj2.name = "mac";

console.log(obj); // {name: 'yiju', age: 29}
console.log(copy_obj); // {name: 'loco', age: 29}
console.log(copy_obj2); // {name: 'mac', age: 29}

위와 같은 방식을 얕은 복사라고 부른다. 왜냐하면 depth가 1단계 밖에 복사하지 못하기 때문이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const obj = {
name: "yiju",
age: 29,
school: { high: "donghwa", univ: "kangwon" },
};
const copy_obj = Object.assign({}, obj);

copy_obj.age = 40;
copy_obj.school.high = "Yebong";

// {name:'yiju', age:40, school: {high: 'Yebong', univ:'kangwon'}}
console.log(copy_obj);
// {name:'yiju', age:29, school: {high: 'Yebong', univ:'kangwon'}}
console.log(obj);

1단계 깊이인 age는 다른 참조값을 공유하지 않지만 2단계 깊이인 school의 객체값은 참조값을 공유하고 있다.

깊은 복사를 하기 위해서는 재귀적으로 객체 내부를 단계별로 복사를 해줘야 가능하다. 혹은 라이브러리를 사용하여 구현할 수 있다.

JSON.stringify()를 사용할 수 도 있지만 이 방법은 메서드까진 복사할 수 없기 때문에 추천되지 않는다.

댓글 공유

React Virtual DOM이란?

카테고리 React

Virtual DOM이란?

Virtual DOM은 Real DOM을 효율적으로 조작하기 위해 가상으로 DOM 구조를 흉내낸 자바스크립트 객체이다.

가상적인 표현을 메모리에 저장하고 React 같은 프레임워크를 통해 Real DOM과 동기화 하기 위해 사용된다.

Virtual DOM 사용하는 이유는?

Real DOM을 조작하여 DOM Tree의 배치가 바뀌면 Real DOM의 재렌더링이 발생하기 때문에 컴퓨터에 부담이 되고 속도가 느려지게 될 것이다.

그래서 Real DOM과 구조가 똑같은 Virtual DOM을 만들고 DOM 조작이 가해진 요소만 찾아 Real DOM에 변화를 가하는 방법을 사용하여 메모리 상에서만 동작하고 실제 렌더링을 하지 않아 연산 비용을 최소화 할 수 있다.

React에서 Virtual DOM 작동원리

  1. 우리가 UI를 조작하면 React는 Virtual DOM을 생성한다.
  2. diffing 알고리즘을 통해 이전 Virtual DOM과 변경사항을 비교한다.
  3. 이 때 변경된 부분을 파악하여 Real DOM에 반영하여 변경된 부분만 재렌더링이 발생한다.

댓글 공유

실행 컨텍스트란?

자바스크립트에서 실행 컨텍스트는 모든 코드의 동작원리를 담고 있는 핵심 개념이다.

다음 예시를 실행 컨텍스트 관점으로 설명해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var outer = () => {
const a = 3;

const inner = () => {
const b = 5;
return a + b;
};

return inner;
};

const closure = outer();

closure();

소스코드 평가와 실행

1. 전역 코드 평가

실행컨텍스트1

  1. 전역 실행 컨텍스트 생성
  2. var 키워드로 선언된 전역변수 outer는 전역 객체에 등록
  3. const 키워드로 선언된 전역변수 closure는 선언적 환경 레코드에 등록
  4. this는 전역 객체 바인딩

2. 전역 코드 실행 단계

실행컨텍스트2

  1. outer 우항의 함수를 평가한 뒤, 메모리에 저장한 뒤 전역 객체에서 outer 식별자에 할당
  2. closure 우항의 outer 함수를 전역 객체에서 찾아 호출

3. outer 함수 평가 단계

실행컨텍스트3

  1. outer 함수 실행 컨텍스트 생성
  2. outer 함수에서 선언된 변수 a, inner를 환경 레코드에 등록
  3. outer 함수의 외부 렉시컬 환경 참조는 outer 변수에 할당된 익명함수 객체의 내부슬롯 [[Environment]]가 가리키는 외부 렉시컬 환경을 가리킨다.

4. outer 함수 실행 단계

실행컨텍스트4

  1. outer 함수 환경 레코드의 변수 a에 값 3이 할당된다.
  2. 변수 inner에 익명 함수 객체의 참조값이 할당되고, 익명 함수 객체의 내부 슬롯 [[Environment]]은 정의된 outer 렉시컬 환경을 가리킨다.

5. 전역 코드 실행 단계 복귀

실행컨텍스트5

  1. 전역 렉시컬 환경의 선언적 환경 레코드에서 closure를 찾는다.
  2. inner 함수 객체가 closure라는 식별자에 할당된다.

6. closure 함수 평가 단계

실행컨텍스트6

  1. closure() 문이 실행되면서 closure의 실행 컨텍스트가 생성된다.
  2. closure를 실행하기 위해 inner 함수 객체를 평가한다.

    즉, closure() 평가단계 === inner() 평가단계

  3. const로 선언된 변수 b가 closure 환경 레코드에 등록된다.
  4. 화살표 함수의 this는 함수가 정의될 때 상위 스코프의 this로 정적으로 결정된다.

7. closure 함수 실행 단계

실행컨텍스트7

  1. closure 즉, inner 함수 내부 코드가 실행
  2. b에 값 5가 할당되고 a+b를 수행하기 위해 스코프 체인에서 a를 찾는다.
  3. closure 환경 레코드에는 a가 존재하지 않기 때문에 외부 렉시컬 환경 참조를 따라 익명함수 객체로 이동하고, 익명함수 객체의 [[Environment]]내부 슬롯이 가리키는 outer 함수 렉시컬 환경으로 이동된다. 결국 outer 환경 레코드에서 a를 찾을 수 있다.
  4. a+b 연산을 수행하고 연산값을 반환한다.

8. closure 실행 컨텍스트 소멸

실행컨텍스트8

  • closure 함수 코드가 실행을 마치고 closure의 실행 컨텍스트가 pop 되어 더 이상 참조되지 않는 객체들은 Garbage Collector에 의해 메모리가 해제된다.

9. 전역 실행 컨텍스트 소멸

실행컨텍스트9

  • 마지막으로 전역 실행컨텍스트가 pop 되어 참조될 수 있는 객체가 없기 때문에 모든 객체가 Garbage Collector에 의해 메모리가 해제된다.

댓글 공유

스코프는 무엇인가요?

카테고리 CS

스코프

스코프란, 식별자가 참조할 수 있는 범위(유효범위)를 말한다. 자바스크립트 엔진이 식별자를 찾기 위해 사용하는 규칙이다.

  • 모든 식별자는 자신이 선언된 위치에 의해 상위 스코프를 결정한다.
  • 식별자는 어떤 값을 구별하기 위해 유일 해야하므로 중복될 수 없다. 단, 스코프가 다르면 중복될 수 있다.

지역 스코프

자바스크립트는 함수 레벨 스코프를 갖는다. 그러므로 지역이란, 함수 몸체 내부를 말한다.

즉, 지역 스코프는 함수에 의해서 생성된다.

let, const가 나오면서 블록 레벨 스코프를 갖는 식별자를 선언할 수 있다. 여기서 지역 스코프는 코드 블록에 의해 생성된다.

1
2
3
4
5
6
7
var x = 1;

if (true) {
var x = 10;
}

console.log(x);

var 키워드는 함수 레벨 스코프를 갖는다. 즉, 함수 몸체 내부에서 var 키워드로 선언된 변수는 지역 스코프 범위를 참조할 수 있다. 함수 몸체 이외에서 선언된 변수는 모드 전역 스코프 범위로 본다.

스코프 체인

스코프는 함수의 중첩에 의해 계층적 구조를 갖는다. 모든 스코프는 하나의 계층적 구조로 연결된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
var x = 10; // 전역 스코프

function outer() {
var x = 100;
function inner() {
var x = -100;
console.log(x);
}
console.log(x); // 100
inner(); // -100
}
console.log(x); // 10
outer();
  1. 전역 스코프 (최상위 스코프)

  2. outer 지역 스코프

  3. inner 지역 스코프

자바스크립트 엔진은 변수를 참조하는 스코프에서 시작하여 상위 스코프 방향으로 이동하며 변수를 검색한다.

실제 자바스크립트 엔진은 코드를 실행하기 전 렉시컬 환경이라는 자료구조를 생성한다. 변수 선언이 실행되면 변수 식별자가 자료구조의 key로 등록되고 변수 할당이 일어나면 변수 식별자에 해당하는 값을 변경한다.

렉시컬 스코프

렉시컬 스코프란, 함수 정의가 평가되는 시점에 상위 스코프가 정적으로 결정된다.

함수 호출이 아닌 함수 정의(함수 선언문, 함수 표현식)가 실행되어 생성된 함수 객체는 자신이 정의된 스코프인 상위 스코프를 기억한다.

즉, 내가(함수가) 정의된 스코프가 자신의 상위 스코드이다.


전역변수와 var 키워드

변수는 생명주기가 있어 선언에 의해 생성되고 언젠가 소멸한다. 소멸하는 시점은 스코프에 따라 다르다.

  • 전역변수 : 런타임 이전에 자바스크립트 엔진에 의해 선언되고 애플리케이션이 종료되면 소멸한다.

  • 지역변수 : 함수가 호출될 때 생성되고 함수가 종료하면 소멸한다. 함수가 호출되는 순간 함수 몸체에 있는 선언들이 호이스팅 된다.

전역변수의 문제점

  1. 암묵적 결합
    어디서든 참조하고 변경할 수 있어 의도치 않게 변경될 수 있다.

  2. 긴 생명주기

메모리 자원도 오랜 기간 소비한다. 1번의 위험이 오래 지속되어 오류의 기회가 많아진다.

  1. 네임 스페이스 오염

자바스크립트는 파일을 분리해도 전역 스코프를 공유한다는 점에서 다른 파일에서 동일한 이름의 전역 변수끼리 충돌이 일어날 수 있다.

그러므로 전역변수를 꼭 써야하는 상황이 아니라면 사용하지 말자.

전역변수 사용 억제 방법

  1. 즉시실행함수
    즉시실행함수를만들어 함수 종료 시 변수도 사라져 메모리 해제도 빨리 되고 지역 스코프를 가져 충돌의 위험을 줄일 수 있다.

  2. 모듈패턴
    클래스를 모방하여 관련이 있는 변수와 함수를 모아 즉시실행함수로 감싸 하나의 모듈로 만든다.

전역변수 억제와 캡슐화까지 가능하다.

캡슐화란, 객체의 상태를 나타내는 프로퍼티와 프로퍼티를 참조하고 조작할 수 있는 메서드를 하나로 묶는 것을 말한다. 객체의 특정 프로퍼티나 메서드를 감출 목적으로 사용하기도 한다.(정보은닉)

var 키워드

var 키워드는 함수 레벨 스코프를 따른다. 함수 몸체 외부에서 var 키워드로 선언시 전역 변수로 선언되고 전역 스코프를 갖는다.

1
2
3
var x = 10;

console.log(window.x); // 10;

var 키워드는 전역에서 선언시 전역 객체의 프로퍼티로 등록된다.

1
2
3
4
5
6
7
y = 100;

function foo() {
z = 20;
}

foo();

키워드로 선언되지 않은 변수는 어디에서든지 항상 전역 변수이다.

선언되지 않은 변수는 해당 코드가 실행되어야 존재한다.

키워드 없이 선언하는 것은 옳지 않다.

러버덕하면서 알게된 내용

1
2
3
4
5
6
7
8
var x = 1;

function foo() {
y = 2;
}

Object.getOwnPropertyDescriptor(window, "x"); // {value: 100, writable: true, enumerable: true, configurable: false}
Object.getOwnPropertyDescriptor(window, "y"); // {value: 50, writable: true, enumerable: true, configurable: true}

var 키워드로 선언된 변수와 암묵적 선언으로 선언된 변수 모두 전역 객체의 프로퍼티로 등록된다.

하지만, 객체의 프로퍼티를 보면 configurable 값이 다른 것을 볼 수 있는데, 이것이 true이면 재정의(삭제, 변경)가 가능하다는 의미이다.

따라서 암묵적 선언으로 전역 객체의 프로퍼티로 등록이 되면 재정의가 가능하므로 암묵적 선언은 사용하지 않는 것이 좋다.


let, const 키워드

let, const 키워드는 블록 레벨 스코프를 따른다. 그러므로 var 키워드의 함수 레벨 스코프보다 더 한정적인 스코프를 지원하여 작은 범위의 스코프를 다룰 수 있어 유용하다.

let

var 키워드와 비교하여 let 키워드를 알아보자.

  1. 변수 중복 선언 금지

  2. 블록 레벨 스코프

let 키워드로 선언한 전역변수는 전역객체의 프로퍼티가 아니다. 전역 객체의 프로퍼티가 되는 것들은 var 키워드로 선언한 전역변수 및 전역함수, 그리고 선언하지 않는 변수에 값을 할당한 암묵적 전역이 있다. 이에 대해서는 실행 컨텍스트에 대해 배울 때 자세히 배우자.

  1. 변수 호이스팅

var 키워드로 선언한 변수는 선언단계와 초기화 단계가 동시에 진행된다. 하지만 let 키워드로 선언한 변수는 선언단계와 초기화 단계가 분리되어 진행된다.

선언단계가 자바스크립트 엔진에 의해 실행되고 초기화 단계는 변수 선언문에 도달했을 때, 실행된다.

1
2
3
4
5
6
7
let a;
console.log(a); // undefined
a = 10;
console.log(a); // 10

console.log(b); // Uncaught ReferenceError: b is not defined
let b;

언뜻보면 let 키워드는 호이스팅이 일어나지 않는 것처럼 보이지만, 선언단계는 호이스팅이 되어 최상단에서 먼저 실행되었고 초기화 단계는 선언문에 도달했을 경우 진행되기 때문에 위와 같은 현상이 발생하는 것이다.

const

let 키워드와 비교해서 알아보자

  1. 선언과 초기화

const 키워드로 선언한 변수는 개발자가 선언과 초기화를 동시에 해야한다.

1
2
3
const foo = 1; // 1

const baz; // Uncaught SyntaxError : Unexpected identifier

이전에 선언을 하면 선언단계와 초기화 단계가 진행된다 그랬었는데, 이것도 초기화가 맞지만 const를 사용하기 위해서는 개발자가 직접 초기화를 해줘야한다. 만약 초기화를 해주지 않고 나중에 값을 할당한다는 것이 재할당으로 해석되기 때문이다.

  1. 재할당 금지

const 키워드로 선언한 변수에 원시값을 할당하면 값을 변경할 수 없다. 하지만 객체를 할당한 경우 값을 변경할 수 있다. 재할당 금지라는 말이 불변을 의미하지는 않는다. 왜냐하면 식별자가 가리키는 메모리 주소 공간은 참조값이 저장되어 있고 객체를 변경하여도 참조값은 변하지 않기 때문이다.

  1. 상수

변하지 않는 값을 사용하기 위해 우리는 상수를 사용한다.

주로 상수의 이름은 대문자로 사용한다. 원시값을 할당한 경우 원시값은 변경 불가능한 값이고 재할당이 금지되므로 할당된 값을 변경할 방법은 없다.

댓글 공유

프로세스와 스레드는 무엇인가요?

프로세스란? 작업의 단위로써, 작업을 관리하는 주체는 OS이다. 프로세스는 최소 1개의 스레드(thread)를 가진다.

컴퓨터가 명령을 받게되면 명령을 실행하면서 연산코드의 흐름이 생겨난다. 이 때의 흐름을 스레드라고 한다.

즉, 스레드가 실질적으로 연산을 하는 주체가 된다.

기본적으로 프로세스 흐름(스레드)은 1개이다. 그런데 흐름이 n개 일 수 있는데, 이런 경우를 멀티 스레딩(Multi-threading)라고 한다.

멀티 스레딩은 동시성과 동기화라는 특징이 있는데 이는 아래 예시를 통해 설명할 수 있다.

만약 프로세스가 여러 개라면 멀티 태스킹(Multi-tasking)이라고 한다.

프로세스와 스레드 비유 설명

프로세스와 스레드 비유 설명

한 가구(Process)가 있다고 하자. 해당 가구는 집(Virtual Memory)이라는 공간에 살게된다. 한 가구안에 세대원(Thread) 3명이 있다고 하자.

집 안에는 세대원들이 각자 방(Thread Local Storage)을 하나씩 사용한다. 뿐만 아니라 Thread마다 Stack 자료구조로 관리되는 메모리 공간이 따로 있다.

각자 방은 본인만 사용 가능하고 거실, 화장실, 부엌은 공용공간(Heap)이다.

프로세스와 스레드 작동원리

프로세스와 스레드 작동원리

  1. Process가 Thread를 처리하기 위해서는 CPU와 메모리(연습장)가 필요하는데 이를 관리해주는 것이 바로 OS가 하는 일이다.

  2. 이 때 Process는 실제 메모리를 사용하지 않고 RAM(1차 메모리)과 HDD(2차 메모리)를 추상화한 Virtual Memory를 사용한다.

  3. Virtual Memory는 RAM과 연결된 부분이 있을 수도 있고 HDD와 연결된 부분이 있을 수도 있다. RAM이 부족해서 연결하지 못하는 것보단 HDD라도 연결해주는 것이 낫기 때문이다.

OS가 Virtual Memory를 Process에게 할당해준다. 때문에 Process 내부의 Thread는 작동범위가 Virtual Memory로 제한된다.

참고

유튜브 널널한 개발자 - Process와 Thread 차이

댓글 공유

0.1은 우리 눈에 보이는 0.1이 아니다?

컴퓨터는 데이터를 RAM이라는 곳에 임시 저장한 뒤 CPU가 꺼내와서 데이터를 연산한다.

RAM에 데이터를 저장할 때 2진수로 저장을 하는데, 정수가 아닌 소수를 저장할 때는 다음과 같은 방식으로 저장한다.

ex) 5.125 -> 101.001 -> 1.01001 * 2^2

컴퓨터가 소수 저장하는 원리

  1. 32칸의 공간을 마련한 뒤 첫칸에 부호를 나타내는 숫자를 적어둔다. (양수:0, 음수:1)
  2. 소수점 우측 부분을 mantissa 부분으로, 정수부분 8칸 공간 뒤에 23칸에 넣어준다.
  3. 지수에다가 127을 더한 뒤 2진법으로 만들고(2+127 -> 10000001) 이를 맨 앞 8칸에다가 넣어준다.

순환소수

하지만 0.1같은 소수는 32칸으로 표현할 수 없는 무한한 소수점 이하의 값을 가진다. 그러므로 컴퓨터는 32칸까지만 표시를 해주고 뒤의 값은 무시한다.

1
2
3
4
const a = 1.1;
const b = 0.1;

a + b === 1.2; // false

32칸뒤에 잘려나간 부분때문에 오차가 발생하여 위 비교식이 false가 판단된다.

정리

정확히 계산하기 위해서는 정수를 사용하자.

1m를 표시하는데, 1.2m를 표시하는 것을 변수에 그대로 담지 말고, mm로 단위를 통일하고 1200를 변수에 담자.

댓글 공유

REST API란 무엇인가요?

카테고리 CS

REST API란 무엇인가요?

자원의 이름으로 구분하여 해당 자원의 상태(정보)를 주고 받는 것을 의미한다. REST API는 HTTP 프로토콜을 의도에 맞게 디자인 하도록 유도하고 있다.

즉, REST는 HTTP를 기반으로 클라이언트가 서버의 리소스에 접근하는 방식을 규정한 아키텍처이고 REST API는 REST를 기반으로 서비스 API를 구현한 것이다.

REST API의 구성

  • 자원 : 모든 자원에는 고유한 ID가 존재하고 ID는 /store/:store_ID와 같은 HTTP URI이다.
  • 행위 : 자원에 대한 행위 (HTTP 요청 메서드)
  • 표현 : 자원에 대한 행위의 구체적 내용 (페이로드, JSON)

REST API 설계 규칙

1. URI는 리소스를 표현하는데 집중하고 마지막에 슬래시 포함하지 않는다.

1
2
3
4
5
6
7
# bad
GET /getTodos/1
GET /todos/show/1
GET /todos/1/

# good
GET /todos/1

2. 행위에 대한 정의는 HTTP 요청 메서드를 통해 정의한다.

HTTP 요청 메서드 종류 목적 페이로드
GET index/retrieve 모든/특정 리소스 취득 X
POST create 리소스 생성 O
PUT replace 리소스 전체 교체 O
PATCH modify 리소스 일부 수정 O
DELETE delete 모든/특정 리소스 삭제 X
  • 리소스에 대한 행위는 HTTP 요청 메서드를 통해 표현하며 URI에 표현하지 않는다.
1
2
3
4
5
# bad
GET /todos/delete/1

# good
DELETE /todos/1

3. 슬래시 구분자(/)는 계층 관계를 표현

1
http://example.com/stores/restaurant

4. 불가피하게 긴 URI인 경우 하이픈(-) 사용

1
http://example.com/stores/very-long-store

단 밑줄(_)은 포함하지 않는다.

5. 파일 확장자는 URI에 포함하지 않는다.

1
2
3
4
5
# bad
http://example.com/stores/restaurant/:1/logo.png

# good
GET /stores/restaurant/:1/logo HTTP/1.1 HOST: example.com Accept: image/png
  • 대신 Accept header를 사용한다.

RESTful한 REST API 설계하기

RESTful은 이해하기 쉽고 사용하기 쉬운 REST API를 만들고, 일관적인 컨벤션을 통해 API의 이해도를 높이는 것을 말한다.

REST API 설계 예시

CRUD HTTP verbs Route
resource 목록 표시 GET /resource
resource 하나 내용 표시 GET /resource/:id
resource 생성 POST /resource
resource 수정 PUT /resource/:id
resource 삭제 DELETE /resource/:id

응답상태 코드

상태코드 설명
1xx 전송 프로토콜 수준의 정보 교환
2xx 클라이언트 요청 성공
3xx 클라이언트 요청 완료하기 위해 추가 행동 필요
4xx 클라이언트의 잘못된 요청
5xx 서버쪽 오류

댓글 공유

loco9939

author.bio


author.job