TIL-2024/08/23

박상우·2024년 8월 24일
0

개발 블로그 읽기

자바스크립트 엔진과 런타임의 차이점은 무엇인가요? | bohyeon.dev

  • 내용

    ECMA Script는 JS의 핵심기능을 정의한다. JS는 웹 뿐만아니라 서버 등 다양하게 쓰인다. 그래서 분야에 관계없이 가지고 있어야 되는 기능에 대해서 규약이 필요했고, 그것을 정의한 것이 ECMA Script이다. 그래서 ECMAScript에는 웹 관련 기능, 데이터 입 출력과 관련된 기능이 없다.

    자바스트립트는 ECMAScript의 상위 집합으로 여겨진다. 현재의 JS는 ECMScript와 호스트 제공 API의 조합으로 여겨진다. 여기에는 브라우저를 위한 웹 전용 API와 Node.js를 위한 서버 전용 API가 포함된다.

    자바스크립트 엔진

    ECMAScript 엔진이라고 할 수 있다. 추가 기능 없이 ECMA-262를 구현하고 있다. 잘알려진 자바스크립트 엔진에는 V8, SpiderMonkey, JavaScriptCore가 있다.

    자바스크립트 엔진은 ECMAScript만 구현하고, 확장할 수 있게 되어있기 때문에 다양한 런타임 환경에서 사용할 수 있다.

    자바스크립트 런타임

    자바스크립트 런타임은 ECMAScript의 호스트이다. 자바스크립트 엔진을 내장하고 있고, 자바스크립트를 활용해서 추가적인 기능을 정의하는 프로그램이다.

    Chrome, Edge, Safari, Node.js와 같은 프로그램이 자바스크립트 런타임인데, 자바스크립트 엔진을 내장하고 있고, 자바스크립트를 통해 접근할 수 있는 추가 기능을 제공하기 있기 때문이다.

    런타임은 ECMAScript만을 만족하면 되기 때문에 자체적인 API를 구성할 수 있다. 그래서 각 프로그램들의 시스템 API들이 모두 다르게 구성되어 있는 이유이다.


빈 객체 {}

JS에서 빈객체를 선언할 때 객체 리터럴 방식으로 const obj = {} 와 같이 선언한다.

타입으로도 {}를 사용할 수 있는데, 이는 어떠한 속성도 가지고 있지 않음을 의미한다.

const obj :{} = {}
obj.title = 'abc' // X

Record Type & 인덱스 시그니처

Record Type

객체의 키와 값의 타입을 정의하기 위해 사용하는 타입

type Record<K extends keyof any, T> = {
	[P in K]: T;
}
type Status = Record<string, number>;

const status: Status = {
	str: 123,
	dex: 312,
	...
}

// 리터럴 타입 사용
type StatusType = 'str' | 'dex'

type Status = Record<StatusType, number>;

const status: Status = {
	atk: 123,
	def: 312,
	...
}

인덱스 시그니처

type Status = {
	[key: string]: number;
}

const status: Status = {
	atk: 123,
	def: 312,
	...
}

// mapped object type 사용
type StatusType = 'str' | 'dex'

type Status = {
	[key in StatusType]: number;
}

const status: Status = {
	atk: 123,
	def: 312,
	...
}

두 방식의 특이점 ( 상대적으로 )

  • Record Type - 표기가 간결해서 명확하다.
  • 인덱스 시그니처 - 특정 키를 추가하거나 제한할 수 있다.

참고 : https://velog.io/@ddowoo/Typescript-유틸리티-타입-Record-Type이란


any & unknown

any

JS에서 사용할 수 있는 모든 값을 오류없이 받을 수 있는 타입.

정적 타이핑을 위해 도입한 TS의 의도와 반대되는 특징을 가지고 있기 때문에 남발하면 안된다.

임시로 값을 처리하거나, 해당 데이터의 값이 모호하거나, 모를 때 임시로 사용할 수 있는 타입이다.

unknown

any 타입과 유사하게 모든 타입에 할당 가능.

any 타입 이외에 다른 타입으로 선언된 변수에 unknown 타입 값을 사용할 수 없다.

const unknownValue: unknown = 100;
const someValue: number = unknownValue // X

unknown 타입으로 선언한 변수는 값을 가져오거나 내부 속성에 접근할 수 없다. 해당 타입이 식별가능한 경우에만 사용가능하다.

그래서 모호한 값에 대해서 any로 처리하지 않고 unknown으로 대체해서 타입을 어느정도 강제할 수 있다.

이전에 이렇게 써본거 같은데?

이전에 프로젝트를 하면서 객체 타입을 적용할 때 아래와 같이 사용한 경우가 있었다.

  const { luckyId, page = 1 } = QueryString.parse(location.search, {
    ignoreQueryPrefix: true,
  }) as unknown as { luckyId: string; page?: number };

url에 있는 쿼리 스트링을 파싱하기 위해서 사용한 로직인데, 타입 단언을 두번해주는 부분에서 어색하게 느껴졌었다.

그래서 두 방식에 대한 차이를 아래처럼 이해해보았다.

  • as { luckyId: string; page?: number }
    • 타입 단언이 수용되어 컴파일 시점에서는 에러가 발생하지 않지만, 이후 런타임에서 구조가 서로 일치하지 않았을때 에러가 발생할 수 있다.
  • as unknown as { luckyId: string; page?: number }
    • unknown 타입의 특성상 해당 타입이 식별된 후에 사용가능하기 때문에 보다 안정적으로 타입을 사용할 수 있다.

맵드 타입 (Mapped Type)

다른 타입을 기반으로 타입을 선언할 때 사용하는 문법.

readonly나 ? 키워드를 추가해서 적용할 수 있고, 각 키워드 앞에 -를 붙여 기존의 키워드를 삭제할 수 도 있다.

type ReadOnlyEx = {
	readonly a: number;
	readonly b: string; 
}

type CreateMutable<Type> = {
	-readonly [Prop in keyof Type]: Type[Prop];
}

type ResultType = CreateMutable<ReadOnlyEx>; // { a: number; b: string }

type OptionalEx = {
	a?: number;
	b: string; 
}

type Concrete<Type> = {
	[Prop in keyof Type]-?: Type[Prop];
}

type ResultType = Concrete<OptionalEx>; // { a: number; b: string }

템플릿 리터럴 타입 (Template Literal Types)

type Stage =
	| "family-crate"
	| "family-enroll"
	| "challenge-create"
	| "challenge-start"
	
type StagetName = `${Stage}-stage`;
// "family-crate-stage" | "family-enroll-stage" | "challenge-create-stage" | "challenge-start-stage"
profile
나도 잘하고 싶다..!

0개의 댓글