본문 바로가기

개발/프로그래머스 데브코스

프로그래머스 데브코스 51일차 with. TS 웹 풀스택

📚요약

지난 시간까지는 C언어를 통해 프로그래밍의 기본 원리를 배웠습니다. 이번 시간부터 타입스크립트를 배워보겠습니다.

 

📖타입스크립트

사용 이유

  • 데이터의 자료형(타입)을 알 수 있기 때문에 버그를 줄일 수 있다. (== 생산성이 높다) 타입을 알기 때문에 유지보수 또한 쉬워집니다.
  • 타입스크립트는 자바스크립트의 슈퍼셋 즉, 자바스크립트의 확장형입니다. 그렇기 때문에 자바스크립트에서 타입스크립트를 사용하는 것은 불가능하지만, 타입스크립트 내부에서 자바스크립트를 사용하는 것은 문제가 없습니다. 그렇기에 자바스크립트로 작성된 프로젝트에 타입스크립트를 일부만 적용하는 것도 가능해 호환성이 좋다는 장점이 있습니다.
  • 추가적으로 IDE에서 타입을 작성하게 되면 메서드의 추천이 쉬워지기 때문에 코드를 작성할 때 도움이 됩니다.

더 자세한 내용은 타입스크립트 공식 홈페이지에서 확인 가능합니다.

 

JavaScript With Syntax For Types.

TypeScript extends JavaScript by adding types to the language. TypeScript speeds up your development experience by catching errors and providing fixes before you even run your code.

www.typescriptlang.org

 

📄초기 환경설정

초기 환경설정이 필요한 이유는 타입스크립트가 자바스크립트에 타입체크가 추가된 언어로 컴파일을 통해 자바스크립트로 변환하는 과정이 필요하기 때문입니다.

npm i -g typescript

위 명령어를 통해 도구를 설치하고 tsc 명령어를 통해 타입스크립트로 작성된 파일을 js로 컴파일해서 이용할 수 있습니다.

🍯tip! VSCode에서 Extension 중 prettier-code formatter를 사용할 경우 원하는 대로 코드의 형태를 맞춰주기 때문에 사용하면 코드를 작성할 때 덜 신경 쓸 수 있다.
🍯tip! VSCode에서 Extension 중 ESLint를 사용하면 코드에 나타난 오류를 잡아줍니다. Lint는 소스 코드를 분석해 오류를 표시하기 위한 도구를 나타내는 단어로 ESLint는 ES( == ECMAScript)의 오류를 알려주는 도구를 의미합니다.

 

📑tsconfig.json

tsc --init 명령어를 통해 생성되는 파일입니다. 해당 파일을 통해 원하는 컴파일 설정을 할 수 있습니다.

더보기

설정이 필요한 것만 작성해서 사용하거나 주석을 해제하고 사용할 수 있습니다.

{
  "compilerOptions": {
    /* Visit https://aka.ms/tsconfig to read more about this file */

    /* Projects */
    // "incremental": true,                              /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
    // "composite": true,                                /* Enable constraints that allow a TypeScript project to be used with project references. */
    // "tsBuildInfoFile": "./.tsbuildinfo",              /* Specify the path to .tsbuildinfo incremental compilation file. */
    // "disableSourceOfProjectReferenceRedirect": true,  /* Disable preferring source files instead of declaration files when referencing composite projects. */
    // "disableSolutionSearching": true,                 /* Opt a project out of multi-project reference checking when editing. */
    // "disableReferencedProjectLoad": true,             /* Reduce the number of projects loaded automatically by TypeScript. */

    /* Language and Environment */
    "target": "es2016",                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
    // "lib": [],                                        /* Specify a set of bundled library declaration files that describe the target runtime environment. */
    // "jsx": "preserve",                                /* Specify what JSX code is generated. */
    // "experimentalDecorators": true,                   /* Enable experimental support for legacy experimental decorators. */
    // "emitDecoratorMetadata": true,                    /* Emit design-type metadata for decorated declarations in source files. */
    // "jsxFactory": "",                                 /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
    // "jsxFragmentFactory": "",                         /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
    // "jsxImportSource": "",                            /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
    // "reactNamespace": "",                             /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
    // "noLib": true,                                    /* Disable including any library files, including the default lib.d.ts. */
    // "useDefineForClassFields": true,                  /* Emit ECMAScript-standard-compliant class fields. */
    // "moduleDetection": "auto",                        /* Control what method is used to detect module-format JS files. */

    /* Modules */
    "module": "commonjs",                                /* Specify what module code is generated. */
    // "rootDir": "./",                                  /* Specify the root folder within your source files. */
    // "moduleResolution": "node10",                     /* Specify how TypeScript looks up a file from a given module specifier. */
    // "baseUrl": "./",                                  /* Specify the base directory to resolve non-relative module names. */
    // "paths": {},                                      /* Specify a set of entries that re-map imports to additional lookup locations. */
    // "rootDirs": [],                                   /* Allow multiple folders to be treated as one when resolving modules. */
    // "typeRoots": [],                                  /* Specify multiple folders that act like './node_modules/@types'. */
    // "types": [],                                      /* Specify type package names to be included without being referenced in a source file. */
    // "allowUmdGlobalAccess": true,                     /* Allow accessing UMD globals from modules. */
    // "moduleSuffixes": [],                             /* List of file name suffixes to search when resolving a module. */
    // "allowImportingTsExtensions": true,               /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
    // "resolvePackageJsonExports": true,                /* Use the package.json 'exports' field when resolving package imports. */
    // "resolvePackageJsonImports": true,                /* Use the package.json 'imports' field when resolving imports. */
    // "customConditions": [],                           /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
    // "resolveJsonModule": true,                        /* Enable importing .json files. */
    // "allowArbitraryExtensions": true,                 /* Enable importing files with any extension, provided a declaration file is present. */
    // "noResolve": true,                                /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */

    /* JavaScript Support */
    // "allowJs": true,                                  /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
    // "checkJs": true,                                  /* Enable error reporting in type-checked JavaScript files. */
    // "maxNodeModuleJsDepth": 1,                        /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */

    /* Emit */
    // "declaration": true,                              /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
    // "declarationMap": true,                           /* Create sourcemaps for d.ts files. */
    // "emitDeclarationOnly": true,                      /* Only output d.ts files and not JavaScript files. */
    // "sourceMap": true,                                /* Create source map files for emitted JavaScript files. */
    // "inlineSourceMap": true,                          /* Include sourcemap files inside the emitted JavaScript. */
    // "outFile": "./",                                  /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
    // "outDir": "./",                                   /* Specify an output folder for all emitted files. */
    // "removeComments": true,                           /* Disable emitting comments. */
    // "noEmit": true,                                   /* Disable emitting files from a compilation. */
    // "importHelpers": true,                            /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
    // "importsNotUsedAsValues": "remove",               /* Specify emit/checking behavior for imports that are only used for types. */
    // "downlevelIteration": true,                       /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
    // "sourceRoot": "",                                 /* Specify the root path for debuggers to find the reference source code. */
    // "mapRoot": "",                                    /* Specify the location where debugger should locate map files instead of generated locations. */
    // "inlineSources": true,                            /* Include source code in the sourcemaps inside the emitted JavaScript. */
    // "emitBOM": true,                                  /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
    // "newLine": "crlf",                                /* Set the newline character for emitting files. */
    // "stripInternal": true,                            /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
    // "noEmitHelpers": true,                            /* Disable generating custom helper functions like '__extends' in compiled output. */
    // "noEmitOnError": true,                            /* Disable emitting files if any type checking errors are reported. */
    // "preserveConstEnums": true,                       /* Disable erasing 'const enum' declarations in generated code. */
    // "declarationDir": "./",                           /* Specify the output directory for generated declaration files. */
    // "preserveValueImports": true,                     /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */

    /* Interop Constraints */
    // "isolatedModules": true,                          /* Ensure that each file can be safely transpiled without relying on other imports. */
    // "verbatimModuleSyntax": true,                     /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
    // "allowSyntheticDefaultImports": true,             /* Allow 'import x from y' when a module doesn't have a default export. */
    "esModuleInterop": true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
    // "preserveSymlinks": true,                         /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
    "forceConsistentCasingInFileNames": true,            /* Ensure that casing is correct in imports. */

    /* Type Checking */
    "strict": true,                                      /* Enable all strict type-checking options. */
    // "noImplicitAny": true,                            /* Enable error reporting for expressions and declarations with an implied 'any' type. */
    // "strictNullChecks": true,                         /* When type checking, take into account 'null' and 'undefined'. */
    // "strictFunctionTypes": true,                      /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
    // "strictBindCallApply": true,                      /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
    // "strictPropertyInitialization": true,             /* Check for class properties that are declared but not set in the constructor. */
    // "noImplicitThis": true,                           /* Enable error reporting when 'this' is given the type 'any'. */
    // "useUnknownInCatchVariables": true,               /* Default catch clause variables as 'unknown' instead of 'any'. */
    // "alwaysStrict": true,                             /* Ensure 'use strict' is always emitted. */
    // "noUnusedLocals": true,                           /* Enable error reporting when local variables aren't read. */
    // "noUnusedParameters": true,                       /* Raise an error when a function parameter isn't read. */
    // "exactOptionalPropertyTypes": true,               /* Interpret optional property types as written, rather than adding 'undefined'. */
    // "noImplicitReturns": true,                        /* Enable error reporting for codepaths that do not explicitly return in a function. */
    // "noFallthroughCasesInSwitch": true,               /* Enable error reporting for fallthrough cases in switch statements. */
    // "noUncheckedIndexedAccess": true,                 /* Add 'undefined' to a type when accessed using an index. */
    // "noImplicitOverride": true,                       /* Ensure overriding members in derived classes are marked with an override modifier. */
    // "noPropertyAccessFromIndexSignature": true,       /* Enforces using indexed accessors for keys declared using an indexed type. */
    // "allowUnusedLabels": true,                        /* Disable error reporting for unused labels. */
    // "allowUnreachableCode": true,                     /* Disable error reporting for unreachable code. */

    /* Completeness */
    // "skipDefaultLibCheck": true,                      /* Skip type checking .d.ts files that are included with TypeScript. */
    "skipLibCheck": true                                 /* Skip type checking all .d.ts files. */
  }
}
🍯tip! `tsc -w 파일명` -w 옵션을 사용해서 ts파일을 컴파일하면 코드의 변화를 계속해서 적용시켜 준다. nodemon과 비슷한 느낌이다.

 

📄데이터 타입

타입스크립트는 정적 타입 언어로 선언하는 경우 데이터 타입을 명시해 주거나 컴파일 시 타입 추론을 통해 타입이 정해집니다.

  • 기본 데이터 타입
    • number : 정수와 실수
    • string : 문자열
    • boolean : 참(true), 거짓(false)
    • null : 값이 없음(의도된 빈 값)
    • undefined : 값이 할당되지 않은 변수의 기본값
  • 객체 타입
    • object : 객체
    • array : 동일한 타입의 요소를 가진 배열
    • tuple : 각 요소가 다른 타입을 가질 수 있는 배열
  • 특수 타입
    • any : 어떠한 타입이든 할당될 수 있는 타입
    • unknown : 타입을 미리 알 수 없는 경우 사용하는 타입

📑타입 추론

타입 추론이란 개발자가 변수를 선언할 때 타입을 쓰지 않아도 컴파일할 때 판단해서 타입을 정해주는 것을 의미합니다.

 

📑타입 명시

타입 명시란 변수를 선언할 때 변수의 타입을 명시해 타입을 정해주는 것을 말합니다.

// 변수의 데이터 타입 명시
let data:데이터 타입 = 타입에 맞는 값;

// 함수의 데이터 타입 명시
function func(a:데이터 타입, b:데이터 타입, 있으면 좋은 변수? : 데이터 타입) : 리턴타입 {
  // ...
}
// 리턴 타입으로 void를 사용한 경우 return이 있으면 안됩니다.

// 함수의 데이터 타입 명시2
const func2(a:데이터타입, b:데이터타입) : 리턴 타입 = func;

// 함수의 데이터 타입 명시3 
const func3 : (a:데이터타입, b:데이터타입)=>리턴타입 = func;

// 객체의 데이터 타입 명시
let obj : {
  속성1 : 데이터 타입;
  속성2 : 데이터 타입;
  ...
} = {...};

 

타입스크립트는 타입 추론과 명시를 통해 정해진 변수의 타입과 다른 타입이 할당되는 경우 에러가 발생합니다.

let a = 1;
a = '문자열';

// error TS2322: Type 'string' is not assignable to type 'number'.

 

📑인터페이스

interface 키워드를 사용해 사용자 정의 타입을 만들어서 사용할 수 있습니다. 중요한 점은 타입에 들어있는 속성들은 항상 들어있어야 에러가 발생하지 않는다는 점입니다. 또한 한 번 정의한 타입은 재사용이 가능합니다.

interface 인터페이스 이름 {
  속성1 : 데이터 타입,
  속성2 : 데이터 타입,
  ...
}

// 객체의 데이터 타입 명시
let obj : {
  속성1 : 데이터 타입;
  속성2 : 데이터 타입;
  있으면 좋은 속성? : 데이터 타입;
  ...
} = {...};

// 인터페이스를 사용한 타입 명시
let obj : 인터페이스 이름  = {...};

// 필요한 속성이 없는 경우
//error TS2741: Property '' is missing in type '' but required in type ''.
🍯tip! 속성이나 함수의 매개변수의 경우 필수가 아닌 경우가 있는데 이를 위해서는 속성이나 매개변수의 이름 옆에 ?를 붙이면 해결된다.

 

생성된 인터페이스와 implements 키워드를 사용해 클래스를 구현할 수 있습니다.

interface Person {
  name: string;
  age: number;
  gender?: string;
  setName : (name:string) => void;
}

class Teacher implements Person {
  name = 'Lee';
  age = 43;
  setName(name: string): void {
    this.name = name;
  }
}

 

📑열거형(enum)

열거형의 경우 정수를 이용해 사용한다고 했는데 타입스크립트의 문법에는 정수를 사용하지 않습니다.

enum GenderType {
  Male,
  Female
}

interface Person {
  name: string;
  age: number;
  gender?: GenderType;
  setName: (name: string) => void;
}

class Teacher implements Person {
  name = "Lee";
  age = 43;
  gender = GenderType.Male;
  setName(name: string): void {
    this.name = name;
  }
}

하지만 실제 js로 컴파일된 코드를 보게 되면 정수로 사용되는 것을 알 수 있습니다.

// 타입스크립트 컴파일한 코드
var GenderType;
(function (GenderType) {
    GenderType[GenderType["Male"] = 0] = "Male";
    GenderType[GenderType["Female"] = 1] = "Female";
})(GenderType || (GenderType = {}));
var Teacher = /** @class */ (function () {
    function Teacher() {
        this.name = "Lee";
        this.age = 43;
        this.gender = GenderType.Male;
    }
    Teacher.prototype.setName = function (name) {
        this.name = name;
    };
    return Teacher;
}());

 

다음 시간에 계속...

 

출처 & 참고

이창현 강사님의 강의

타입 추론/타입 호환/ 타입 단언/ 타입 가드, inpa, 2024.06.17