TIL | WIL

1/31 화 (타입스크립트 엔티티 모음과 분류) TIL, TIT

깊은바다거북 2023. 2. 1. 00:14

(타입스크립트 공부중)

배운 것:

  • 타입스크립트에서 keyof과 typeof과 그 사용례에 대해 더 체계적으로 정리해봤다. (어제치 TIL에 업데이트함)
  • 타입스크립트의 엔티티를 여러가지로 구분지어 보았다. 예를 들어 ‘타입 선언’ 자리에 올 수 있는 엔티티는 열거형(enum)과 타입(type), 인터페이스, 원시형뿐이다. (아마도)
  • 객체의 “계산된 프로퍼티(Computed property)” 방식을 이용해 프로퍼티 키 값을 런타임시에 동적으로 할당해줄 수 있다.

느낀 점

지난 이주, 타입스크립트를 새롭게 공부하며 도무지 감이 잡히지 않는 기분이었다. 어렵긴 어려운데 뭔가 납득도 안 되는 느낌? 이쪽 구멍을 막으면 저쪽이 뚫리고 저쪽을 막으면 다른 곳이 고장나는 항아리를 어떻게든 절묘하게 완성시키라는 것 같았다. 어느 구멍이 어느 구멍(들)을 촉발시키는지, 각 구멍을 막을 수 있는 조각들은 정확히 어떤 게 얼만큼 있는 건지도 제대로 알려주지 않고서. 이렇게 저렇게 알려주긴 하는데 퍼즐이 너무 흩어져있는 느낌이었다.

그러던 차에 오늘에 와서 드디어 실마리를 잡게 되었다. Enum에 대한 간단한 연습 문제 10개가 제공됐는데, ‘된다/안 된다’가 판정나는 샘플이 여러 개 있으니 여기에다 임의로 여러가지 테스트를 하며 항아리 구멍에 대한 이해도를 늘릴 수 있었다. 그리고 그동안 아등바등 지나오며 쌓인 예제 코드들까지 더해지니 이제야 한바탕 속시원히 정리가 된 것 같다. 특히 아래의 타입스크립트 엔티티 모음과 분류 부분은 검색해도 찾지 못한 나만의 정리본이다. 틀린 부분이 있을 수도 있지만.

아무튼 다행이다. 이제라도 좀 정리가 돼서.

(나중에 보면 좋을 사이트)


[타입스크립트] 자동 실행 환경 설정하기 with Nodemon

= TypeScript를 cold reloading하며 실행할 수 있도록 환경 설정하기

  1. npm i typescript @types/node nodemon ts-node -D
  1. npm tsc --init - 타입스크립트 초기화 및 tsconfig.json 파일 생성
  1. tsconfig.json 파일 설정
    // tsconfig.json
    {
      "compilerOptions": {
        "target": "es2016",
        "module": "commonjs",
        "lib": ["DOM"],
        "outDir": "build", 
        "rootDir": "src", // 이 폴더 안의 ts 파일들을 컴파일함
        "strict": true,
        "noImplicitAny": true,
        "esModuleInterop": true,
    		"noEmitOnError": true,
    		"moduleDetection": "force" // 작성하는 ts 파일마다 다른 파일의 동일 변수명과 별개의 변수로 인식하게 함. 
      }
    }

    ⇒ outDir로 “build” 폴더를 설정해놓긴 했지만 ts-node를 이용할 것이므로 실제로 트랜스파일된 파일이 생성되진 않을 것이다.

  1. package.json과 같은 레벨에 nodemon.json 파일 만들기

    (nodemon --init 명령이 가능한지는 모르겠다. 그냥 수동으로 만들자)

    // nodemon.json
    {
      "watch": ["src"], // 지켜볼 폴더
      "ext": ".ts,.js", // 지켜볼 확장자
      "ignore": [], // "src" 안에서 무시했으면 하는 폴더나 파일들 리스트
      "exec": "npx ts-node ./src/*.ts" // 명령어...
    	"exec": "npx ts-node ./src/index.ts"
    }

    ⇒ “ignore” : src 폴더 안에 변경 사항을 추적하지 않을 여러 폴더를 또 만들 거라면 [”src/enums”, “src/generics”]같이 추가해주면 된다.

    ⇒ “exec” : 윈도우 환경에서는 밑의 줄로 적어주도록 한다.

  1. package.json 수정
    // package.json
    "scripts": {
    	...
    	"start:dev": "npx nodemon -q"
    }

    ⇒ 플래그 -q (-quiet)로 파일이 재실행될 때마다 나오는 메세지를 감춰줌.

유의할 점:

  1. tsconfig.json의 “rootDir” 경로 = 트랜스파일할 ts 폴더/파일 위치
  1. nodemon.json의 “watch” 경로 = 변화 감지 위치
  1. nodemon.json의 “exec” 경로 = 트랜스파일할 ts 파일 위치

이 셋이 통일되도록 잘 맞춰줘야 한다.

Cold Reloading이란

: 코드를 수정할 때마다 수동으로 앱을 재실행(building + running)시키지 않고, 자동으로 되게 하는 것.

이에 관해 내가 찾은 유일한 레퍼런스는 이것이었다:

Cold reloading will automatically rebuild the application every time a code change is detected preventing the need to rebuild the application manually. (참조: https://stephendoddtech.com/blog/game-design/cold-reloading-typescript-project)

여기서는 타입스크립트로 브라우저에서 실행하는 간단한 게임을 만드는데, Cold reloading은 코드 변경이 발생할 시 앱을 알아서 재실행시키되 브라우저는 내가 새로고침을 해야 반영이 되는 것이고, Hot reloading은 브라우저까지도 자동으로 새로고침이 되는 방식이라고 소개한다.

가장 많이 본 레퍼런스는 리액트에 있는 Hot reloadingLive reloading 개념을 비교하는 글이었다.

  • Hot reloading: 코드를 수정하면 앱을 재실행하는 것 없이 변경 사항을 반영하여 볼 수 있도록 하는 것. 지금 실행중인 앱에 그 바뀐 코드만 교체해 넣으므로써 앱이 가지고 있는 상태(state)은 리셋되지 않는다.
  • Live reloading: 코드를 수정하면 앱 전체를 다시 실행하는 것. 전에 실행되며 가지고 있던 상태(state) 데이터가 리셋된다.
  • (내가 끼워넣어본) Cold reloading: Live reloading과 비슷한 개념인 것 같다. ‘앱 전체’를 다시 실행(rebuild)한다는 점에서.

에러 ‘n p m ‘은(는) 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는 배치 파일이 아닙니다. ✔️

발생 상황: 파이참 IDE에서 여러 단축키를 실험해보던 중 갑자기 터미널과 에디터에서 영어가 “apple”이 아니라 “a p p l e “같이 입력됨.

타입: 바보이슈

원인: 키보드 입력 설정이 ‘반자’가 아니라 ‘전자’로 설정되어 있어서 그렇다.

시도:

브라우저나 다른 창에서는 영문 입력이 잘 되길래 파이참에서 저 “2 spaces”설정이 space를 끼워넣는 주범인 줄 알고 그걸로 검색함.

“pycharm how to set 2 spaces”

⇒ “pycharm how to set default space size”

⇒ “intellij default spaces are 2”

그러나 indents와 tab 사이즈가 자꾸 “2 spcaes”로 된다고/안 된다고 아우성치는 검색 결과밖에 얻지 못함.

해결:

alt + “=”로 반자/전자를 바꿔주면 된다. 아니면 윈도우 시작줄에서 직접 선택해주어도 된다.

“intellij 문자 입력시 띄어쓰기” 이걸로 원인을 한 방에 찾았다. 때로는 한글 검색이 와따봉이다.

(참조: https://ttend.tistory.com/601)


[자바스크립트] 객체 생성시 변수에 담긴 값을 key로 사용하는 방법, “계산된 프로퍼티”, 동적 키 할당

: [키 변수] : 값 혹은 [키 변수]: 값 변수

: 변수명을 대괄호 []에 감싸서 넣어준다.

ES6부터 도입된 방법이라고 한다.

const sorter = 'username';
const order = 'desc';

const resultObject = {
	[sorter] : order
}

console.log(resultObject);
// => 결과:
// {'username': 'desc'}

변수에 담기지 않은 계산값도 같은 방법으로 대입해줄 수 있다:

const someArray: string[] = ["one", "two", "three"]
const resultObject = {
	[someArray[0].length] : someArray[0]
}

console.log(resultObject);
// => 결과:
// {'3': 'one'}

참고로 변수에 담긴 값을 value로 사용하고 싶을 땐 그냥 변수명을 넣어주면 된다.

(참고: https://koonsland.tistory.com/146)

계산된 프로퍼티(Computed property)와 동적 키 할당

: 객체를 만들 때 객체 리터럴 안의 프로퍼티 키가 대괄호로 둘러싸여 있는 경우

이와 함께 대괄호 표기법(Square Bracket Notation)의 특징인 “변수나 문자열뿐만 아니라 모든 표현식의 평가 결과를 프로퍼티 키로 사용할 수 있다”, 즉 “평가가 끝난 이후의 결과가 프로퍼티 키로 사용된다”는 특징을 이용하면 다음과 같이 동적으로 프로퍼티 키를 할당해주는 것도 가능하다 :

// 예시: 
let fruit = prompt("어떤 과일을 구매하시겠습니까?", "apple");

let bag = {
  [fruit]: 5, // 변수 fruit에서 프로퍼티 이름을 동적으로 받아 옵니다.
};

alert( bag.apple ); // fruit에 "apple"이 할당되었다면, 5가 출력됩니다.

(참고: https://ko.javascript.info/object)

그 밖에 ‘객체’의 특징

  • 프로퍼티 키는 문자열이나 심볼이어야 한다. 보통은 문자열이다.
  • 값은 어떤 자료형도 가능하다.
  • 문자형이나 심볼형에 속하지 않은 값은 문자열로 자동 형 변환된다.

    예를 들어 키에 숫자 0을 넣으면 문자열 “0”으로 자동 변환된다.

  • 객체의 프로퍼티 {키: 값}를 부르는 명칭:

    ⇒ 키 : 프로퍼티 이름 = 프로퍼티 키 = 프로퍼티 식별자

    ⇒ 값 : 프로퍼티 값

  • 객체의 프로퍼티 정렬 방식: 정수 프로퍼티(integer property)는 자동으로 정렬되고, 그 외의 프로퍼티는 객체에 추가한 순서 그대로 정렬된다.

그 밖에 ‘객체’와 관련된 용어들

  • Trailing(Hanging) comma: 마지막 프로퍼티 끝에 붙는 쉼표 ✔️
  • 점 표기법(Dot notation)과 대괄호 표기법(Square bracket notation) ✔️
  • 계산된 프로퍼티(Computed property) : { [fruit]: 5 }같이 프로퍼티 키가 대괄호로 둘러싸인 경우. 표현식의 평가 결과를 프로퍼티 키로 할당한다고 하여 ‘계산된 프로퍼티’라고 부른다. ✔️
  • 단축 프로퍼티(프로퍼티 값 단축 구문)(Property value shorthand) : { name: name}을 { name }이라고 쓰는 것. ✔️
  • 프로퍼티 이름에 제약사항은 없다. “__proto__” 를 제외하면 예약어도 키로 가능하다.
  • delete obj.prop : “prop” 프로퍼티 삭제 ✔️
  • "key" in obj : 프로퍼티 키 “key”의 존재 여부 확인 ✔️
  • for (let key in obj) 반복문 : 객체의 모든 키 순회 ✔️
  • 정수 프로퍼티(Integer property) : 변형 없이 정수로 왔다 갔다 할 수 있는 문자열을 키로 가지는 프로퍼티. “49”같은 것. 반면에 '+49’와 '1.2’는 정수 프로퍼티가 아니다.

(참고: https://ko.javascript.info/object)

[타입스크립트] 객체를 타입으로 지정하고 싶을 땐

🚩
요약: 객체에서 ‘key’나 ‘value’들을 뽑아서 유니온 타입을 만들고 원하는 곳에 써준다.
const obj = { red: 'apple', yellow: 'banana', green: 'cucumber' } as const;

type Keys = keyof typeof obj; // = "red" | "green" | "yellow"
type Values = typeof obj[keyof typeof obj]; // = "apple" | "cucumber" | "banana"

const value1: Values = 'apple';
const value2: Values = obj.red;
const key1: Keys = 'red';

typeof를 이용해서 객체 → ‘type’으로 만들어 사용한다.

예를 들어 함수를 ‘선언할 땐’: 타입이 필요함.

그리고 함수를 ‘호출할 땐’: 타입에 맞는 실제 인스턴스(변수)가 필요함.

이를 코드로 나타내보면:

// ODirecton이라는 객체가 있는데
const ODirection = {
	Up: 0,
	Down: 1,
	Left: 2,
	Right: 3,
} as const

//  이 객체의 속성값을 ODirection.Up같이 인자로 받고 싶음:
run(ODirection.Up); // = 0

// 함수를 '선언할 때'는 객체가 아니라 타입이 필요함:
// => 타입 추출후 함수 선언에 넣어주기
type Direction = typeof ODirection[keyof typeof ODirection]; // type Direction = 1 | 2 | 3 | 0
function fun(dir: Direction) { console.log(dir) }
// => "Down" | "Left" | "Right" | "Up"이 아니라 1 | 2 | 3 | 0로 추출해야 한다. 함수가 '받는 것'이므로.

// 함수를 '호출할 때'는 (원래 사용하려고 한 목적인) 실제 객체가 필요함:
run(ODirection.Up) // 0

참고로 Enum은 그 자체로 타입으로 넣어줄 수도 있고 호출 시 ‘값’으로도 넣어줄 수 있다.

// EDirection이라는 Enum이 있으면
enum EDirection {
	Up,
	Down,
	Left,
	Right,
}

// 이것 그대로 함수 선언에도 사용하고
function walk(dir: EDirection) { console.log(dir) };

// 이것 그대로 함수 호출에도 사용할 수 있다.
walk(EDirection.Up); // 0


[타입스크립트] 엔티티 선언 방식 모음

‘객체형’과 ‘클래스’형 선언 형식에 유의미한 차이가 있는지, 있다면 어떤 타입들이 어떤 쪽에 해당하는지를 정리하고 싶어서 모아본 용례 모음이다.

종류: 객체, enum, type, interface, 클래스, 함수

// 인터페이스
interface IDirection { // 콤마로 구분
   Up: number,
   Down?: number,
   Left?: number,
   Right: number,
}
interface LabeledValue { // 세미콜론으로도 구분
    readonly label: string;
}
interface User { // 아예 아무 구분자도 없어도 괜찮음. 
  id: number
  firstName: string
  lastName: string
  role: string
}
interface login { // 함수
  (username: string, password: string): boolean;
}
interface CraftBeer { // 클래스 형식 
  beerName: string; 
  nameBeer(beer: string): void;
}


// 클래스
class ClassExample {
	constructor(public name: string, public age: number) {}
}

// 열거형
enum EDirection { // 숫자형
   Up,
   Down,
   Left,
   Right,
}
enum ProgrammingLanguage { // 문자형
  TypeScript = "TypeScript",
  JavaScript = "JavaScript",
  Python = "Python",
  Golang = "Golang",
}

// 객체 (리터럴)
const ODirection = {
   Up: 0,
   Down: 1,
   Left: 2,
   Right: 3,
}

// type alias
type TOrder = {
    [key: string]: {
        status: Status,
        color: Color,
        availableColors: Color[],
        orderedBy: Customer

    }
};
type TProgrammingLanguages = {
  [key: string]: string
};
type ColorKey = keyof typeof Color;
type Customer = {
  firstName: string;
  lastName: string;
};

// 함수 
function create(o: object | null): void {}

[타입스크립트] 엔티티 분류 - “Type 군”과 “Value 군”, “선언 타입군”

내가 지금까지 본 코드 용례들을 기준으로 정리해 보았다.

‘Type 군’에 속하는 것

= 핵심적으로는 ‘key’ 이름만 있고 ‘value’가 없는 것. 그리고 구조 틀과 타입을 명시한다.

= keyof의 피연산자로 쓸 수 있는 종류.

예를 들어 keyof 오브젝트는 ‘type값을 기대하고 있는데 value 값이 들어와서 안됨’ 컴파일 에러를 띄운다.

‘key’와 틀이 있는 것이라면 (keyof 파트에서 다뤘듯이) :

  1. class
  1. interface
  1. type alias로 선언한 type

이 있다.

‘Value 군’에 속하는 것

= ‘key’와 함께 실제 내용물(’value’)이 있는 것. 틀만으로 구성되지 않은 것.

= typeof의 피연산자로 쓸 수 있는 종류.

  1. 객체
  1. enum
  1. function
  1. class

타입 선언 자리에 올 수 있는 것(’선언 타입군’)

  1. enum
  1. type alias로 선언한 type
  1. interface - 객체
  1. interface - 클래스
  1. interface - 함수
  1. 원시형

이만하면 지금까지 다룬 타입들을 다 커버한 것 같다.


Uploaded by N2T