[Javascript/Typescript] TypeScript 간단 정리

???? why TS?

 

 

자바스크립트는 약한 타입 언어라고 표현할 수 있어 비교적 덜귀찮(?)고 유연하게 개발할 수 있는 환경을 제공하지만 런타임 환경에서 쉽게 에러가 발생할 수 있는 단점을 가진다.
이러한 자바스크립트에 강한 타입 시스템을 적용해 대부분의 에러를 컴파일 환경에서 코드를 입력하는 동안 체크할 수 있는 장점을 가지고 있기 때문에 요즘 많은 곳에서 타입스크립트를 사용하고 있는 것이 아닌가 싶다.
=> 에러사전 방지, 코드 가이드 및 자동완성(개발 생산성 향상)

???? 참고 자료

정말 많은 도움이 되었고 정리가 너무 잘되어있는 heropy님의 블로그입니다
이 글은 위 블로그 정리내용을 다시 정리한 포스트 입니다
예제를 통해 초보, 입문자가 이해하기에도 쉽게 되어있어 좋은 참고자료로 썼습니다.
???? heropy님 블로그 링크

작성한 내용이 컴파일러 옵션에 따라 어떻게 자바스크립트로 변환되는지 바로 확인가능한 사이트
???? TypeScript Playground

캡틴판교님의 인강 - 타입스크립트 입문 - 기초부터 실전까지
포크 떠서 필기한 깃헙 레포

캡틴판교님의 교안
???? 타입 스크립트 핸드북

⭐️ 사용 과정

.ts 확장자를 가진 파일로 작성,
작성 후 타입스크립트 컴파일러를 통해 자바스크립트 파일로 컴파일하여 사용


⭐️ 타입 선언

타입 특징
Boolean 단순한 참(true)/거짓(false) 값
Number 모든 부동 소수점 값을 사용 가능
String 작은따옴표('), 큰따옴표(") 뿐만 아니라 ES6의 템플릿 문자열도 지원
Array 순차적으로 값을 가지는 일반 배열로 string[], Array<string> 두가지 방법으로 선언
Tuple 정해진 타입의 고정된 길이(length) 배열을 표현, 각 요소의 타입이 지정되어 있는 배열 형식
Enum 열거형, 숫자 혹은 문자열 값 집합에 이름(Member)을 부여, 값의 종류가 일정한 범위로 정해져 있는 경우 유용, 기본적으로 0부터 시작하며 값은 1씩 증가, 특정 값들의 집합을 의미하는 자료형
Any 모든 타입, 일반적인 자바스크립트 변수와 동일하게 어떤 타입의 값도 할당 가능, 외부 자원을 활용해 개발할 때 불가피하게 타입을 단언할 수 없는 경우, 유용
Unknown 알 수 없는 타입, Unknown에는 어떤 타입의 값도 할당할 수 있지만, Unknown을 다른 타입에는 할당할 수 없음
Object typeof 연산자가 "object"로 반환하는 모든 타입, 여러 타입의 상위 타입이기 때문에 그다지 유용하지 않음, 반복적인 사용을 원하는 경우, interface나 type을 사용하는 것을 추천
Null Undefined 모든 타입의 하위 타입, 서로의 타입에도 할당 가능
Void 일반적으로 값을 반환하지 않는 함수에서 사용
Never 절대 발생하지 않을 값, 어떠한 타입도 적용할 수 없음
Union 2개 이상의 타입을 허용하는 경우 ❗️ Union 타입 장점 - any 보다는 명시적임 : 타입가드시 메소드 등장
Intersection 2개 이상의 타입을 조합하는 경우
Function 화살표 함수를 이용해 타입을 지정

⭐️ 타입 추론

명시적으로 타입 선언이 되어있지 않은 경우, 타입스크립트는 타입을 추론

<타입스크립트가 타입을 추론하는 경우>

  • 초기화된 변수
  • 기본값이 설정된 매개 변수
  • 반환 값이 있는 함수

이를 활용해 모든 곳에 타입을 명시할 필요는 없음


⭐️ 타입 단언

타입 추론을 통해 판단할 수 있는 타입의 범주를 넘는 경우, 더 이상 추론하지 않도록 지시
-> 프로그래머가 타입스크립트보다 타입에 대해 더 잘 이해하고 있는 상황

DOM API 조작시 많이 사용됨

(num as number).toFixed(2);

Non-null 단언 연산자

let x: number | null | undefined
x!.toFixed(2);

⭐️ 타입 가드

특정 타입으로 타입의 범위를 좁혀나가는 과정

NAME is TYPE 형태의 타입 술부(Predicate)를 반환 타입으로 명시

function isNumber(val: string | number): val is number {
  return typeof val === 'number';
}

⭐️ 인터페이스

❗️중요
여러 객체를 정의하는 일종의 규칙이며 구조

경축! 아무것도 안하여 에스천사게임즈가 새로운 모습으로 재오픈 하였습니다.
어린이용이며, 설치가 필요없는 브라우저 게임입니다.
https://s1004games.com

interface IUser {
  name: string,
  age: number,
  isAdult: boolean
}

❗️ 타입 별칭과 인터페이스의 차이점

  • 타입의 확장 가능 / 불가능 여부
  • 인터페이스는 확장이 가능한데 반해 타입 별칭은 확장이 불가능
  • 따라서, 가능한한 type 보다는 interface로 선언해서 사용하는 것을 추천

⭐️ 기타

  • readonly 키워드를 사용하면 읽기 전용 속성을 정의
  • 함수타입은 호출 시그니처 사용
interface IFunc {
  (PARAMETER: PARAM_TYPE): RETURN_TYPE
}
  • 인터페이스로 클래스를 정의하는 경우, implements 키워드를 사용
  • 인덱싱 가능 타입: 인덱싱에 사용할 인덱서(Indexer)의 이름과 타입 그리고 인덱싱 결과의 반환 값을 지정
interface INAME {
  [INDEXER_NAME: INDEXER_TYPE]: RETURN_TYPE
}
  • 인덱싱 가능 타입에서 keyof를 사용하면 속성 이름을 타입으로 사용 가능, 타입의 개별 값에도 접근가능

  • 인터페이스도 클래스처럼 extends 키워드를 활용해 상속가능

  • 같은 이름의 인터페이스를 여러 개 만들기 가능: 기존에 만들어진 인터페이스에 내용을 추가하는 경우에 유용

  • type 키워드를 사용해 새로운 타입 조합하여 별칭(이름)을 부여 가능

  • ❗️제네릭을 통해 사용 시점에 타입을 선언할 수 있는 방법을 제공: 타입을 인수로 받아서 사용한다고 이해하면 쉬움-> 함수 이름 우측에 <T>를 작성해 시작

  • 제네릭을 작성할때 extends 키워드를 사용하는 제약 조건을 추가 가능

interface MyType<T extends string | number> {
  name: string,
  value: T
}
  • ‘타입 구현’ 영역에서 사용하는 extends는 삼항 연산자 사용 가능
  • infer 키워드를 통해 타입 변수의 타입 추론 여부를 확인 가능
  • 함수 오버로드(Overloads)는 이름은 같지만 매개변수 타입과 반환 타입이 다른 여러 함수를 가질 수 있는 것으로 양한 구조의 함수를 생성하고 관리가능: 선언부와 구현부의 매개변수 개수가 같아야 함
  • 접근 제어자
    public 어디서나 자유롭게 접근 가능(생략 가능)
    protected 나와 파생된 후손 클래스 내에서 접근 가능
    private 내 클래스에서만 접근 가능
  • 추상클래스는 다른 클래스가 파생될 수 있는 기본 클래스로, 인터페이스와 유사: 파생된 후손 클래스에서 인스턴스를 생성해야함
  • ? 키워드를 사용하여 타입을 선언할 때 선택적 매개 변수(Optional Parameter)를 지정가능,
    속성(Properties)과 메소드(Methods) 타입 선언에도 사용가능
    ? 키워드 사용은 | undefined를 추가하는 것과 동일
  • 타이핑 : 타입이 정의되지 않은 코드에 타입을 입혀주는 행위

⭐️ 모듈의 타입 선언

모든 모듈에 대해 매번 직접 타입 선언을 작성하는 것은 매우 비효율적.
여러 사용자들의 기여로 만들어진 Definitely Typed을 사용가능
수 많은 모듈의 타입이 정의되어 있으며, 지속적으로 추가되고 있음

설치

npm install -D @types/모듈이름 으로 설치해 사용
npm info @types/모듈이름 으로 검색하면 원하는 모듈의 타입 선언이 존재하는지 확인가능

동작원리

타입 선언 모듈(@types/lodash)은 node_modules/@types경로에 설치되며,
이 경로의 모든 타입 선언은 Import를 통해 컴파일에 자동으로 포함


⭐️TS 유틸리티 타입

이름 설명
Partial TYPE의 모든 속성을 선택적으로 변경한 새로운 타입을 반환
Required TYPE의 모든 속성을 필수로 변경한 새로운 타입을 반환
Readonly TYPE의 모든 속성을 읽기 전용으로 변경한 새로운 타입을 반환
Record KEY를 속성으로, TYPE를 그 속성값의 타입으로 지정하는 새로운 타입을 반환
Pick TYPE에서KEY로 속성을 선택한 새로운 타입을 반환
Omit Pick과 반대로, TYPE에서KEY속성을 생략하고 나머지를 선택한 새로운 타입을 반환
Exclude 유니언 TYPE1에서 유니언 TYPE2를 제외한 새로운 타입을 반환
Extract TYPE1에서 유니언 TYPE2를 추출한 새로운 타입을 반환
NonNullable 유니언 TYPE에서 null undefined를 제외한 새로운 타입을 반환
Parameters 함수 TYPE의 매개변수 타입을 새로운 튜플 타입으로 반환
ReturnType 함수 TYPE의 반환 타입을 새로운 타입으로 반환

???? 예제 코드 - learn-typescript 필기

???? 기본 및 시작하기

function sum(a: number, b: number): number {
    return a + b;
}

// sum('10', '20'); // Error: '10'은 number에 할당될 수 없습니다.

// NOTE: 메소드 자동완성 가능
let result = sum(10, 20)
result.toLocaleString();

// NOTE: js doc 으로 타입스크립트 같이 사용가능 하긴함
// @ts-check
/**
 * @param {number} a 첫번째숫자
 * @param {number} b 두번째숫자
 */

// NOTE: 
// $ sudo npm i typescript -g => 설치
// $ tsc index.ts => 컴파일

???? todo 앱, 인터페이스 기본, 인덱싱, 확장

//NOTE: any: 모든 타입 가능 -> any로 먼저 설정해두고 하나씩 바꾸는게 좋음
let todoItems: ITodo[];

// NOTE: 인터페이스 사용 - 객체 및 함수
interface ITodo {
  id: number,
  title: string,
  done: boolean,
}

interface ITodoFunc {
  (index: number, todo?: ITodo): void;
}

let deleteTodo: ITodoFunc;
deleteTodo = function (index: number): void {
  todoItems.splice(index, 1);
}

// api
function fetchTodoItems(): ITodo[] {
  return [
    { id: 1, title: '안녕', done: false },
    { id: 2, title: '타입', done: false },
    { id: 3, title: '스크립트', done: false },
  ];
}

// crud methods
function fetchTodo(): ITodo[] {
  return  fetchTodoItems();
}

//NOTE: void: 리턴 값이 없는 친구들
function addTodo(todo: ITodo): void {
  todoItems.push(todo);
}

function completeTodo(index: number, todo: ITodo): void {
  todo.done = true;
  todoItems.splice(index, 1, todo);
}

// business logic
function logFirstTodo(): ITodo {
  return todoItems[0];
}

function showCompleted(): ITodo[] {
  return todoItems.filter((item: ITodo) => item.done);
}

// TODO: 아래 함수의 내용을 채워보세요. 아래 함수는 `addTodo()` 함수를 이용하여 2개의 새 할 일을 추가하는 함수입니다.
function addTwoTodoItems(item1: ITodo, item2: ITodo): void {
  // addTodo() 함수를 두 번 호출하여 todoItems에 새 할 일이 2개 추가되어야 합니다.
  addTodo(item1);
  addTodo(item2);
}

// NOTE: 유틸 함수
function log(): void {
  console.log(todoItems);
}

todoItems = fetchTodoItems();

const todo1 = { id: 4, title: '네번째', done: false };
const todo2 = { id: 5, title: '다섯번째', done: false };
addTwoTodoItems(todo1, todo2);

log();

// NOTE: 인터페이스 인덱싱
interface IStringArray {
  [index: number]: string;
}

let arr: IStringArray = ['a', 'b'];
arr[0] = 'c';
console.log(arr); // ['c', 'b']
// arr[0] = 10; -> 에러

interface IStringRegExDict {
  [key: string]: RegExp;
}

let obj: IStringRegExDict = {
  cssFile: /\.css$/,
  // jsFile: 123, -> 에러
}
obj['jsFile'] = /\.js$/;
Object.keys(obj).forEach(key => {
  console.log(key); // key => 자동 추론
})

// NOTE: 인터페이스 확장
interface Person {
  name: string;
}
interface Developer extends Person {
  skill: string;
}

const seoYoung: Developer = {
  name: 'um',
  skill: 'js',
}

???? 클래스

function Person(name, age) {
  this.name = name;
  this.age = age;
}
const hulk = new Person('Banner', 33);

//ES6 + 타입스크립트
class Person2 {
  name: string;
  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}
const capt = new Person2('Steve', 100);

???? 타입별칭

/** NOTE: 타입 별칭과 인터페이스의 차이점
 * 타입의 확장 가능 / 불가능 여부
 * 인터페이스는 확장이 가능한데 반해 타입 별칭은 확장이 불가능
 * 따라서, 가능한한 type 보다는 interface로 선언해서 사용하는 것을 추천
 */

// #1
// function sum(a: number, b:number) {
//   return a + b;
// }
type SumParameter = number;

function sum(a: SumParameter, b: SumParameter) {
  return a + b;
}

// #2
type Student = {
  name: string;
  age: number;
};

function getPerson(): Student {
  // ...
  return {
    name: 'seoYoung',
    age: 28
  }
}

// #3
type Hero = {
  skill: string;
}

const captain: Hero = { 
  skill: 'throwing a shield' 
}

???? union 타입

// function logMessage(value: string) {
//   console.log(value);
// }
// function logMessage(value: number) {
//   console.log(value);
// }
// function logMessage(value: any) {
//   console.log(value);
// }

// # NOTE: Union 타입 문법 - `any` 보다는 명시적임 : 타입가드시 메소드 등장
// function logMessage(value: string | number) {
//   console.log(value);
// }

function logMessage(value: string | number) {
  if (typeof value === 'string') {
    value.toLocaleUpperCase();
  }
  if (typeof value === 'number') {
    value.toLocaleString();
  }
  throw new TypeError('value must be string or number')
}

// # Intersection 타입 문법
interface Developer {
  name: string;
  skill: string;
}

interface Person {
  name: string;
  age: number;
}

// NOTE: Union 타입 => 어떤 것이 들어올지 모르므로 공통된 속성만 접근가능
function askSomeone(someone: Developer | Person) {
  someone.name; // O
  // someone.age; // X
}

askSomeone({ name: '서영', skill: 'js' });
askSomeone({ name: '서영', age: 20 });

// NOTE: Intersection 타입 => 조합된 모든 속성만 접근가능
function askSomeone2(someone: Developer & Person) {
  someone.name; // O
  someone.age; // O
  someone.skill; // O
}

askSomeone2({ name: '서영', age: 20, skill: 'js' });

???? enum 이넘

// EXAMPLE: enum 예제
enum Answer {
    Yes = 'Y',
    No = 'N',
}

function respond(message: Answer): void {
    console.log(message);
}

respond(Answer.Yes);
  //respond('Y'); - 에러

// NOTE: 런타임 시점에서의 이넘 특징: 런타임시에 실제 객체 형태로 존재
enum E {
    X, Y, Z
}

function getX(obj: { X: number }) {
    return obj.X;
}
getX(E); // 이넘 E의 X는 숫자이기 때문에 정상 동작

// NOTE: 컴파일 시점에서의 이넘 특징: 일반적으로 keyof를 사용해야 되는 상황에서는 대신 keyof typeof를 사용
enum LogLevel {
    ERROR, WARN, INFO, DEBUG
}

// 'ERROR' | 'WARN' | 'INFO' | 'DEBUG';
type LogLevelStrings = keyof typeof LogLevel;

function printImportant(key: LogLevelStrings, message: string) {
    const num = LogLevel[key];
    if (num <= LogLevel.WARN) {
        console.log('Log level key is: ', key);
        console.log('Log level value is: ', num);
        console.log('Log level message is: ', message);
    }
}
printImportant('ERROR', 'This is a message');

???? 제네릭

function getNumber(value: number) {
  return value;
}

function getArray(value: string[]) {
  return value;
}

// 제네릭 기본 문법 - 함수
function getValue<T>(value: T): T {
  return value;
}
// getValue<string>('hi').toLocaleUpperCase();
getValue('hi').toLocaleUpperCase();
// getValue<number>(100).toLocaleString();
getValue(100).toLocaleString();

// 제네릭 기본 문법 - 인터페이스
interface Dev<T> {
  name: string;
  age: T;
}
const tony: Dev<number> = { name: 'tony', age: 100 };

// 제네릭 타입 제한 - 구체적인 타입
function getNumberAndArray<T>(value: T[]): T[] {
  value.length; // O
  return value;
}

interface IHasLength {
  length: number;
}

// NOTE: 속성가진 인터페이스 확장하여 사용
function getNumberAndArray2<T extends IHasLength>(value: T): T {
  value.length;
  return value;
}

// 제네릭 타입 제한 - keyof
interface ShoppingItems {
  name: string;
  price: number;
  address: string;
  stock: number;
}
function getAllowedOptions<T extends keyof ShoppingItems>(option: T): T {
  if (option === 'name' || option === 'address') {
    console.log('option type is string');
    return option;
  }
  if (option === 'price' || option === 'stock') {
    console.log('option type is number');
    return option;
  }
}
// getAllowedOptions('nothing');
const a = getAllowedOptions('name');
a.toUpperCase(); // Name

// EXAMPLE: 제네릭 이용 예제
interface DropdownItem<T> {
  value: T;
  selected: boolean;
}

const emails: DropdownItem<string>[] = [
  { value: 'aaa@naver.com', selected: true },
  { value: 'bbb@naver.com', selected: false },
  { value: 'ccc@naver.com', selected: false }
]

const numProducts: DropdownItem<number>[] = [
  { value: 1, selected: true },
  { value: 2, selected: false },
  { value: 3, selected: false }
]
// 유니온 타입
function createDropdownItem(item: DropdownItem<string> | DropdownItem<number>) {
  //..
}
// 유니온 타입을 제네릭으로 변경
function createDropdownItem2<T>(item: DropdownItem<T>) {
  //..
}

emails.forEach((email) => createDropdownItem(email))
numProducts.forEach((prd) => createDropdownItem(prd))

emails.forEach((email) => createDropdownItem2<string>(email))
numProducts.forEach((prd) => createDropdownItem2<number>(prd))

???? 타입 호환

// 인터페이스
interface IDeveloper {
  name: string;
  skill: string;
}

interface IPerson {
  name: string;
}

//NOTE: 타입호환 - 작은타입에 큰타입을 넣는 것은 가능
var developer: IDeveloper;
var person: IPerson;
// developer = person; // X
person = developer; // O

// 함수
var _add = function(a: number) {
  // ...
}
var _sum = function(a: number, b: number) {
  // ...
}
_sum = _add; // O
// _add = _sum; // X

// 유니온 타입
var c: IDeveloper | IPerson;
var d: IPerson | string;
// c = d; // X
d = c; // O

// 제네릭
interface Empty<T> {
}
let x: Empty<number>;
let y: Empty<string>;

x = y;  // OK, because y matches structure of x

interface NotEmpty<T> {
  data: T;
}
let x1: NotEmpty<number>;
let y1: NotEmpty<string>;

// x1 = y1;  // Error, because x and y are not compatible

???? 전화번호부 예제

interface PhoneNumberDictionary {
  [phone: string]: {
    num: number;
  };
}

interface Contact {
  name: string;
  address: string;
  phones: PhoneNumberDictionary;
}

enum Place {
  Home = 'home',
  Office = 'office',
  Studio = 'studio',
}

// api
// NOTE: Promise<제네릭>
function fetchContacts(): Promise<Contact[]> {
  const contacts: Contact[] = [
    {
      name: 'Tony',
      address: 'Malibu',
      phones: {
        home: {
          num: 11122223333,
        },
        office: {
          num: 44455556666,
        },
      },
    },
    {
      name: '마동석',
      address: '서울시 강남구',
      phones: {
        home: {
          num: 213423452,
        },
        studio: {
          num: 314882045,
        },
      },
    },
  ];
  return new Promise(resolve => {
    setTimeout(() => resolve(contacts), 2000);
  });
}

// main
class AddressBook {
  contacts: Contact[] = [];

  constructor() {
    this.fetchData();
  }

  fetchData(): void {
    fetchContacts().then(response => {
      this.contacts = response;
    });
  }

  /* TODO: 아래 함수들의 파라미터 타입과 반환 타입을 지정해보세요 */
  findContactByName(name: string): Contact[] {
    return this.contacts.filter(contact => contact.name === name);
  }

  findContactByAddress(address: string): Contact[] {
    return this.contacts.filter(contact => contact.address === address);
  }

  findContactByPhone(phoneNumber: number, phoneType: Place): Contact[] {
    return this.contacts.filter(
      contact => contact.phones[phoneType].num === phoneNumber
    );
  }

  addContact(contact: Contact): void {
    this.contacts.push(contact);
  }

  displayListByName(): string[] {
    return this.contacts.map(contact => contact.name);
  }

  displayListByAddress(): string[] {
    return this.contacts.map(contact => contact.address);
  }
  /* ------------------------------------------------ */
}

const addressBook = new AddressBook();
const found = addressBook.findContactByPhone(11122223333, Place.Office)

???? 모듈화

???? type-def.ts

interface PhoneNumberDictionary {
[phone: string]: {
    num: number;
};
}

interface Contact {
    name: string;
    address: string;
    phones: PhoneNumberDictionary;
}

enum Place {
    Home = 'home',
    Office = 'office',
    Studio = 'studio',
}

export {
    Contact,
    Place
}

???? index.ts

import { Contact, Place } from './type-def'

//..

 

[출처] https://velog.io/@tjdud0123/TypeScript-%EA%B8%B0%EB%B3%B8-%EA%B0%84%EB%8B%A8-%EC%A0%95%EB%A6%AC

 

본 웹사이트는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.
번호 제목 글쓴이 날짜 조회 수
507 [JavaScript] lastIndexOf() : String에서 원하는 값의 마지막 위치(index) 찾기 졸리운_곰 2024.02.28 2
506 [JS] 문자열의 위치 찾기 & 문자열 추출하기 file 졸리운_곰 2024.02.28 2
505 [JavaScript] lastIndexOf() : String에서 원하는 값의 마지막 위치(index) 찾기 졸리운_곰 2024.02.28 3
504 [node.js 응용] Node.js에서 다른 파일의 함수를 "include" 하는 방법 졸리운_곰 2024.02.28 1
503 [node.js 응용] NodeJS 에서 mqtt 사용하기 file 졸리운_곰 2024.02.23 1
502 [Javascript] Toggle Between Grid and List View in React file 졸리운_곰 2024.02.23 1
501 [node.js 응용] Next.js 기본 개념정리 file 졸리운_곰 2024.02.23 1
500 [HTML/JavaScript] Merge neighbouring HTML table cells with same value using JS : html 테이블 셀 자동적으로 가로/세로 합치기 졸리운_곰 2024.02.18 3
499 [웹 어셈블리, WSAM] An example of emscripten with WebSocket. file 졸리운_곰 2023.12.12 2
498 [HTML] DNS 유지한 채 도메인 포워딩 : How to set up URL frame forwarding 졸리운_곰 2023.12.04 3
497 [Javascript] [Javascript] 자바스크립트에서 Class 사용하기-constructor, extends, file 졸리운_곰 2023.11.29 16
496 [Javascript] undefined와 null의 차이점이란 ? 졸리운_곰 2023.11.29 13
495 [node.js 응용] ejs 사용설명서 file 졸리운_곰 2023.11.25 7
494 [웹 어셈블리, WSAM] サーバサイドはWebAssemblyの夢を見るか? – Node.jsでwasmってみた : 서버 측은 WebAssembly의 꿈을 꾸는가? – Node.js에서 wasm 해 보았습니다. file 졸리운_곰 2023.08.27 6
493 [웹 어셈블리 WSAM, webassembly] WebAssembly on the server-side : 서버측 웹어셈블리 file 졸리운_곰 2023.08.27 7
492 [HTML} Vercel을 이용하여 정적사이트를 무료로 운영하기 file 졸리운_곰 2023.07.09 3
491 [HTML] GitHub와 Netlify를 이용한 쉽고 빠른 HTTPS 무료 호스팅 졸리운_곰 2023.07.09 8
490 [HTML] netlify로 정적 사이트 배포하기 졸리운_곰 2023.07.09 9
» [Javascript/Typescript] TypeScript 간단 정리 졸리운_곰 2023.05.21 12
488 [JavaScript] CORS란? CORS를 해결해보자 졸리운_곰 2023.05.09 24
대표 김성준 주소 : 경기 용인 분당수지 U타워 등록번호 : 142-07-27414
통신판매업 신고 : 제2012-용인수지-0185호 출판업 신고 : 수지구청 제 123호 개인정보보호최고책임자 : 김성준 sjkim70@stechstar.com
대표전화 : 010-4589-2193 [fax] 02-6280-1294 COPYRIGHT(C) stechstar.com ALL RIGHTS RESERVED