타입스크립트 블록체인 첼린지 -노마드코더

ZeroJun·2022년 7월 7일
0

타입스크립트

목록 보기
4/4

타입스크립트 프로젝트 환경

npm init -y // defalut값으로 설정된 package.json생성
npm install -D typescript // devDependencies에 타입스크립트 설치
touch tsconfig.json // tsconfig.json생성
// tsconfig.json파일을 통해 vscode는 타입스크립트로 작업한다는 것을 즉시 알게되고
// 자동완성기능을 제공해준다.

위 명령어 완료 후 src/index.ts생성

tsconfig.json설정하기

{
  "include": ["src"],
  "compilerOptions": {
    "outDir": "build",
    "target": "ES3"
  }
}
  • 여기서 include는 빌드할 파일을 의미하고, 현재는 src폴더만 대상이지만 추후 폴더가 늘어나면 배열속에 넣어주면 된다.

  • compilerOptions의 outDir은 타입스크립트 파일이 컴파일 되어 자바스크립트로 만들어진 파일이 저장되는 디랙토리를 지정한다.

  • target은 컴파일 후 생성될 자바스크립트 버전을 지정한다.

그 다음 package.json을 수정한다.

수정 전

{
  "name": "typechain",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

수정 후

{
  "name": "typechain",
  "version": "1.0.0",
  "description": "",
  "scripts": {
	"build": "tsc"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

이를통해 npm run build를 하면 src의 타입스크립트 파일이 자바스크립트로 컴파일 되어 build폴더에 저장된다.

최신브라우저는 es6를 거의 지원하므로 compileOption.target:es6로 지정한다.

lib Configuration

{
  "include": ["src"],
  "compilerOptions": {
    "outDir": "build",
    "target": "ES6",
    "lib": ["ES6", "DOM"]
  }
}

lib는 합쳐진 라이브러리의 정의 파일을 특정해주는 역할을 한다.
그 정의파일의 목표로하는 런타임 환경을 알려준다. 이 말은 자바스크립트 코드가 어디에서 동작할지를 알려준다는 것이다.
위의 예시는 ES6와 브라우저(DOM)환경에서 코드를 실행시킬 것이라 선언한 것이다.
코드가 동작하는 환경에 따라 타입스크립트는 기본적으로 API의 타입을 알기에 타입에 대해 알려준다.

위처럼 DOM을 지정하면 document.querySeletor... 를 사용할 수 있다.
여기서 querySeletor의 정의부를 들어가면 lib.dom.d.ts파일로 이동한다.
lib.dom.d.ts는 node_modules/typescript/lib안에 파일이 있다.

위와 같은 구조로 되어있기 때문에 타입스크립트는 DOM에 관한 자동완성을 제공해줄 수 있는 것이다. DOM을 지우면 document에 대한 자동완성을 지원하지 않는다.

대신 이처럼 문제해결 방법을 제시해 준다.

Declaration Files

lib키 설명의 see more을 통해 브라우저 링크로 들어가면 lib를 다음과 같이 설명한다.

TypeScript includes a default set of type definitions for built-in JS APIs (like Math), as well as type definitions for things found in browser environments (like document). TypeScript also includes APIs for newer JS features matching the target you specify; for example the definition for Map is available if target is ES6 or newer.

타입스크립트는 자바스크립트가 제공하는 localstorage같은 api들의 기본타입 설명을 포함하고 있다는 것이다.

어떻게 자바스크립트 파일과 모듈을 위한 타입 정의를 작성할까?

먼저 src폴더에 myPackage.js파일을 만들어 다음과 같이 작성한다.

export function init(config) {
  return true;
}
export function exit(code) {
  return code + 1;
}

그 후 index.ts로 가서 위에서 작성한 myPackage를 누군가 GitHub와 npm에 푸시해둔 것이고, 우리가 이걸 설치했다고 가정하여 함수를 불러와본다. 즉 myPackage가 node_Module인 것처럼 생각하는 것이다.

import { init } from "myPackage";

init(); // 인자 하나를 받는 init을 이렇게 호출해도 에러를 뿜지 않는다.
// 이것은 strict모드가 아니기 때문이다.

tsconfig.json에서 strict를 true로 설정한다.

{
  "include": ["src"],
  "compilerOptions": {
    "outDir": "build",
    "target": "ES6",
    "lib": ["ES6"],
    "strict": true
  }
}

그럼 에러를 뿜게 된다

에러가 나는 이유는 타입스크립트는 lib.dom.d.ts처럼 d.ts라는 정의파일에서 우리가 사용하는 타입을 찾아내기 때문에 myPackage를 설명하는 myPackage.d.ts파일을 생성해야한다.

interface Config {
  url: string;
}
declare module "myPackage" {
  function init(config: Config): boolean;
}

.d.ts에서 myPackage와 구성요소에 대한 설명을 정의하면 위처럼 다른 파일에서 모듈과 구성요소의 정보를 확인할 수 있게된다.

node_modules에 설치된 자바스크립트 모듈은 이렇게 사용된다.

자바스크립트 프로젝트를 타입스크립트로 이전할 때 발생하는 일

원래 자바스크립트에서 import하는 것 처럼 경로를 지정하면 에러가 발생하게 된다.

tsconfig.json에서 allowJs:true를 해주면 에러가 사라진다.

// tsconfig.json

{
  "include": ["src", "src/myPackage.d.ts", "src/myPackage.js"],
  "compilerOptions": {
    "outDir": "build",
    "target": "ES6",
    "lib": ["ES6"],
    "strict": true,
    "allowJs": true
  }
}

자바스크립트 파일이지만 ts의 규칙에 종속시키고 싶다면 js파일에서 // @ts-check주석을 단 후 아래와 같이 작성해주면 된다.

// @ts-check
/**
 * Initailizes the project
 * @param {object} config 
 * @param {boolean} config.debug
 * @param {string} config.url
 * @returns boolean
 */
export function init(config) {
  return true;
}
/**
 * Exits the program
 * @param {number} code 
 * @returns
 */
export function exit(code) {
  return code + 1;
}

이것을 JSDoc라고 부른다.
여기서 param과 returns를 제대로 설정해주지 않으면 에러가 발생한다.
이렇게 코멘트만 작성하면 자바스크립트도 타입스크립트처럼 보호받을 수 있다.

실제 함수를 사용할 때 타입과 함수에 대한 코멘트를 확인할 수 있다.

간단한 블록체인 구현해보기

먼저 저장시 자동 실행을 통한 원활한 작업을 위해 nodemon과 ts-node를 설치한다

npm i -D ts-node
npm i nodemon

그 후 스크립트에 아래와 같이 작성 후 npm run dev만 입력하면 앞으론 코드를 저장할 때마다 ts파일을 자동으로 실행하여 결과를 보여준다.

"dev": "nodemon --exec ts-node src/index.ts"

블록체인은 말 그대로 여러 개의 블록이 사슬처럼 묶인 것이다. 그 블록 안에는 블록체인으로 보호하고 싶은 데이터가 들어있다. 각 블록은 다른 블록에 묶여있다. 마치 블록들이 사슬처럼 연결되어 있는 것이다. 그리고 그 연결고리는 해쉬값이다.

* 타입스크립트로 작성되지 않은 패키지를 import할 때 해결법

깃허브 DefinitelyTyped 레파지토리에 접속하여 type/...원하는패키지폴더/index.d.ts에 들어가면 타입정의가 되어있다.
그것을 다 복사붙여 넣기 하는 것이 아니라 npm i -D @types/node 이런식으로 설치해주면 된다.

import crypto from "crypto";
// 해시값 도출 패키지
// tsconfig의 "esModuleInterop": true추가

interface BlockShape {
  prevHash: string; // 이전 해쉬 값
  height: number; // 블록의 위치
  data: string; // 블록으로 보호할 데이터
}

class Block implements BlockShape {
  public hash: string;
  constructor(
    public prevHash: string,
    public height: number,
    public data: string // 세 개의 값을 토대로 새로운 hash값을 생성한다. // 해쉬 값은 입력값이 같으면 항상 일정하다. // 즉 데이터가 변하지 않으면 해쉬값도 변하지 않는다. // 이를 통해 블록체인의 데이터가 보호된다. // 해쉬값이 변하지 않으면 블록체인의 블록정보도 수정되지 않은 것이다.
  ) {
    this.hash = Block.calculateHash(prevHash, height, data);
  }
  static calculateHash(prevHash: string, height: number, data: string) {
    const toHash = `${prevHash}${height}${data}`;
    return crypto.createHash("sha256").update(toHash).digest("hex");
    // toHash를 새로운 해쉬 값으로 만들어 준 것을 리턴
  }
}

class Blockchain {
  private blocks: Block[];
  constructor() {
    this.blocks = [];
  }
  private getPrevHash() {
    if (this.blocks.length === 0) return "";
    return this.blocks[this.blocks.length - 1].hash;
  }
  public addBlock(data: string) {
    const newBlock = new Block(
      this.getPrevHash(),
      this.blocks.length + 1,
      data
    );
    this.blocks.push(newBlock);
  }
  public getBlocks() {
    return [...this.blocks];
    // 원본 블록들의 보호를 위해 복사본을 리턴한다.
  }
}

const blockchain = new Blockchain();

blockchain.addBlock("First one");
blockchain.addBlock("Second one");
blockchain.addBlock("Third one");

blockchain.getBlocks().push(new Block("xxxx", 1111, "HACKEDDD"));
// getBlocks는 복사본을 리턴하기 때문에 블록체인에 영향이 가지 않는다.

console.log(blockchain.getBlocks());

0개의 댓글