02 ES6와 타입스크립트 기초

김혜진·2023년 1월 31일
0

리액트 퀵스타트

목록 보기
2/5

개요

ES6

ECMA 스크립트(ECMAScript)의 여섯 번째 버전이라는 뜻
ECMA 스크립트 : ECMA-262 기술 규격에 정의 된 표준화 된 스크립트 프로그래밍 언어
자바스크립트 같은 언어를 표준화하기 위해 만들어짐
구현체로는 액션스크립트, J스크립트, 자바스크립트 등이 있음
ES6는 이전 버전의 문법을 포함하면서 Class, Arrow Function등 새로운 문법을 추가로 지원

개발한 애플리케이션이 브라우저 특성과 버전에 관계없이 실행되도록 하려면 ES6코드를 ES5와 같은 이전 버전의 코드로 변환하여 배포할 수 있어야 함
이러한 변환 기능을 제공하는 것을 트랜스파일러라고 부른다.
ex) 바벨(Babel), 타입스크립트 트랜스파일러(tsc)

타입스크립트

ES6 + 정적 데이터 형식, 자바스크립트 언어의 확장 버전

장점

  • 정적 데이터 형식을 사용할 수 있다.
    잘못된 값을 할당하는 등 사소한 에러코드를 줄이고 동적 데이터 형식을 사용하는 자바스크립트 언어보다 디버깅이 쉬움

  • 통합 개발 환경과 쉽게 통합된다.
    VSCode에서 쉽게 통합되어 코드 자동완성 등의 기능을 제공하므로 개발 생산성이 높고, 디버깅 도움을 받을 수도 있음

  • 기존 언어와 문법적으로 유사하다.
    자바나 C# 같은 기존 언어와 문법적으로 유사한 부분이 많음

  • 자바스크립트와 동일한 패키지 관리 환경을 사용한다.
    자바스크립트 언어 환경에서 사용하던 npm, yarn과같은 패키지 관리자를 동일하게 사용할 수 있음



ES6

let const

호이스팅

실행 컨텍스트가 만들어진 후에 var 키워드가 부여된 변수를 미리 생성하는 것
따라서 var 키워드로 변수를 중복 선언해도 에러가 발생하지 않는다.
JS 코드를 파싱하고 내부에 var 키워드가 지정된 코드를 찾아서 메모리를 미리 할당 (변수를 미리 생성)

특징

  • 함수 단위로 호이스팅
    어떤 함수가 호출되면 함수 실행을 위한 실행 컨텍스트를 만들고, 함수 내부의 코드 중에서 var가 부여된 것들을 찾아 함수 실행 컨텍스트 내부에 변수를 미리 생성
    var로 생성된 변수들은 함수 단위로 유효범위(scope)를 제공
    즉, var는 블록 수준의 유효범위를 제공하지 않음
  • 호이스팅 단계에서 이미 변수가 생성되어 있다면 변수를 생성하지 않고 건너뜀
    변수를 같은 이름으로 생성하여 에러가 발생할 것 같지만 그렇지 않음.
    개발자들에게 혼란을 줄 수 있음
    이러한 문제를 해결하기 위해 let 키워드 지원
    let은 블록 수준의 유효 범위를 지원함

let

변수의 중복 선언을 하지 않고 호이스팅도 하지 않음

const

선언될 때 한번 값이 할당되면 더 이상 값을 변경할 수 없는 상수

const p1 = { name: "john", age: 20 }
p1.age = 22;

console.log(p1);

p1 객체는 상수이다.
2행에서 age 속성을 변경했지만 에러는 나지 않는다.
p1은 객체의 메모리 주소값을 가지고 있는데 내부의 속성을 변경한다고 해서 객체의 메모리 주소가 바뀌는 것은 아니기 때문이다.

const p1 = { name: "john", age: 20 }
p1 = { name: "susan", age: 20 };

하지만 이와 같이 p1 객체에 새로운 객체가 할당되는 것은 허용하지 않는다.
메모리 주소가 바뀌기 때문이다.

const로 객체를 생성하면 객체 내부의 속성을 변경할 수는 있지만, 새로운 객체를 생성하여 할당하는 것은 허용하지 않음


기본 파라미터와 가변 파라미터

  • 기본 파라미터 : 파라미터 값을 전달하지 않을 경우 주어진 기본 값이 할당됨
function addContact(name, mobile, home="없음", email="없음") {
  ...
}
addContact("홍길동", "010-222-3331")
  • 가변 파라미터 : 여러 개의 파라미터 값을 배열로 받을 수 있음
function foodReport(name, age, ...favoriteFoods) {
  ...
}
  foodReport("이몽룡", 20 "짜장면", "냉면", "불고기");

구조 분해 할당

ES6에서는 배열이나 객체의 값들을 추출하여 한 번에 여러 변수에 할당할 수 있는 구조 분해 할당이라는 기능을 제공한다.

let arr = [10, 20, 30, 40];
let [a1, a2, a3] = arr;

10, 20, 30을 순서대로 a1, a2, a3 변수에 각각 할당함

let p1 = { name: "홍길동", age: 20, gender: "M" };
let { name: n, age: a, gender } = p1;

gender처럼 p1 객체의 속성과 할당하려는 변수의 이름이 동일할 때는 변수명을 생략할 수 있다.

function addContact1({name, phone, email="이메일 없음", age=0}) {
 console.log(name, phone, email, age);
}
addContact1({ name: "이몽룡", phone: "010-3434-8989" })

기본 파라미터 형태로 기본 값을 부여하고, 함수를 호출할 때 전달한 객체를 구조분해 할당으로 받아낸다.


화살표 함수

기존 함수 표현식에 비해 간결하며 함수를 정의하는 영역의 this를 그대로 전달받을 수 있다.
코드의 구현부에 리턴문만 존재하는 경우 중괄호를 사용하지 않고 리턴값을 화살표 기호 뒤에 작성하는 형태를 사용하여 간결하게 작성할 수 있다.

❗ 주의할 점
화살표 함수와 전통적인 함수는 서로 다른 this를 바인딩할 수 있다.

바인딩 기본 규칙

기본적으로 메서드나 함수가 호출될 때마다 현재 호출 중인 메서드를 보유한 객체가 this로 연결된다.
만일 현재 호출 중인 메서드를 보유한 객체가 없다면 전역 객체(global object, 브라우저 환경에서는 window 객체)가 연결된다.

자바스크립트 언어에서는 함수나 메서드를 호출할 때 직접 연결할 this를 지정할 수 있는 기능을 제공한다.

  • bind() : 지정한 객체를 this로 미리 연결한 (binding) 새로운 함수를 리턴함
  • apply(), call() : 지정한 객체를 this로 연결한 후 함수를 직접 호출함

화살표 함수는 함수가 정의되는 유효 범위(scope)의 this를 자신의 유효 범위의 this로 연결한다.
따라서 bind()나 apply()같은 함수를 사용하지 않아도 된다.


객체 리터럴

객체의 속성을 작성할 때 속성과 변수명이 동일하다면 변수명을 생략할 수 있다.

// var obj = { name: name, age: age, email: email };
var obj = { name, age, email };

새로운 메서드 표기법

let p1 = {
  ...
  order : function() {
    ...
  },
  discount(rate) {
    ...
  }
}

function 키워드를 사용하지 않고 바로 {} 구현부가 따라온다.
이 함수는 화살표 함수가 아님!
함수가 중첩됐을 때 바깥쪽 함수의 this는 이 함수의 this로 전달되지 않는다.


템플릿 리터럴

역따옴표(backquote: `)로 묶인 문자열에서 템플릿 대입문(${})을 이용해 동적으로 문자열을 끼워 넣을 수 있는 방법을 제공
개행 문자를 포함하여 여러 줄로 작성할 수 있다.

...(생략)
let r1 = `${name} 님에게 ${d1.toDateString()}에 연락했다.`;

모듈

전통적인 자바스크립트에서는 모듈이라는 개념이 잘 쓰이지 않고 ES6부터는 공식적으로 모듈 기능을 제공
모듈이란 독립성을 가진 재사용 가능한 코드 블록
코드블록 안에서 import, export 구문을 이용하여 모듈을 가져오거나 내보낼 수 있다.

export deault 된 것은 임포트할 때 중괄호 없이 참조할 수 있다.
단, default로 익스포트와 임포트할 수 있는 요소는 단 하나라는 점에 주의


프로미스

이전까지는 비동기 처리를 수행할 때 비동기 처리가 완료되면 콜백 함수가 호출되도록 작성하는 것이 일반적인 형태였다. ex) jQuery AJAX
하지만 이 방법은 비동기로 처리할 작업이 순차적으로 반복되면 콜백 함수들이 중첩되어 예외처리가 힘들어지고 복잡도가 증가하는 문제점이 있다.
ES6에서는 프로미스 객체를 지원해 비동기 처리를 좀 더 깔끔하게 수행할 수 있다. (axios 등의 라이브러리 사용)

new Promise()로 객체를 생성할 때 전달하는 함수를 '시작 함수'라고 부르고, Promise 객체가 생성됨과 동시에 함수를 실행한다.
시작 함수에는 비동기로 처리할 작업을 정의하고 작업이 완료되면 처리 결과에 따라 작업이 성공이라면 인자로 전달받는 resolve 함수를 호출하여 then()에 등록한 함수가 실행된다.

프로미스를 이용하면 손쉽게 비동기적인 작업을 순차적으로 실행할 수 있다.
then()을 체인처럼 엮어서 프로미스 체인(promise chain)을 생성하면 된다.

프로미스 체인에서의 예외처리는 catch()를 이용하면 된다.
then() 내부에서 에러가 발생하면 그 이후에 등록된 catch() 중 가장 가까운 위치의 catch()에 등록된 함수가 호출됨


전개 연산자

...
연산자를 함수의 인자로 사용하면 가변 파라미터(rest parameter)라고 부른다.
가변 파라미터는 개별 값을 나열하여 함수의 인자로 전달하면 함수의 내부에서 배열로 사용할 수 있도록 한다.

동일한 ... 기호를 사용하지만 전개 연산자(spread operator)는 가변 파라미터와 사용 방법이 다르다.
배열이나 객체를 ... 연산자와 함께 객체 리터럴 또는 배열 리터럴에서 사용하면 객체 또는 배열의 값을 분해된 값으로 전달함

얕은 복사(shallow copy)

let obj1 = { name: "박문수", age: 29 };
let obj2 = obj1;

객체의 메모리 주소만 복사하므로 obj1과 ob2는 동일한 객체를 참조

깊은 복사(deep copy)

let ob3 = { ...obj1 };
let ob4 = { ...obj1, email:"abc@abc.com" }; // 새로운 속성 추가

obj1 객체의 내부 속성값을 복사하여 새로운 개체 ob3을 만든다.
따라서 객체 내부의 속성의 값은 같지만 서로 다른 객체


타입스크립트

타입스크립트는 정적 타입(type: 데이터 형식)을 지정할 수 있는 것이 가장 큰 장점

지정할 수 있는 타입의 종류

  • number : 숫자
  • string : 문자열
  • boolean : true/false
  • any : 모든 타입 포함. 이 타입이라면 형식 검사를 수행하지 않음
  • array : 배열
    ex) let a : number[] = [1,2,3]
  • null : '객체가 없음'을 나타내는 값으로, null 타입으로도 사용됨
  • undefined : '할당한 값이 없음'을 나타내는 값이자 타입
  • void : 결괏값을 리턴하지 않는 함수의 타입을 지정할 때 설정
    ex) function test() : void { }
    const test2 = () : void => { }
  • union : 여러 타입의 값을 허용할 때 사용
  • 사용자 정의 타입 : 복잡한 타입을 지정할 때 사용

제네릭

제네릭이란 타입을 마치 함수의 파라미터처럼 사용하는 것
제네릭(generic)을 사용하면 일관된 타입의 값을 처리할 수 있다.

function arrayConcat2<T>(items1: T[], items2: T[]) : T[] {
  return items1.concat(items2);
}

let arr2 = arrayConcat2<number>([10,20,30], [40,50])
arr2.push("hello"); // arr2는 number[] 형식이므로 명시적 에러 발생

제네릭 타입을 number로 지정하여 호출했기 때문에 전달되는 인자와 리턴값 모두 number[] 형식
따라서 number가 아닌 다른 형식의 값을 추가하는 것을 허용하지 않는다.


타입 별칭

타입 별칭(type alias)은 기존 타입에 대한 별칭을 부여하는 기능
단순한 타입보다는 복잡하게 정의한 사용자 정의 타입을 재사용할 때 자주 사용한다.

// string 타입에 대한 별칭 부여
type MyType = string;
let a: MyType = "Hello";

// 복잡한 타입에 대한 별칭 부여
type MyType2 = { name:string; age:number; }
let b: MyType2 = { name: "홍길동", age: 20 };

// 선택적 속성과 읽기 전용 속성
type MyType3 = {
  name: string;
  age?: number;
  readonly email:string;
}
let c: MyType3 = { name: "홍길동", email: "abc@abc.com" }
// 읽기 전용이므로 에러 발생
// c.email = "abc@abc.com";

// 튜플 타입
type TupleType = [string, number];
let d: TupleType = ["hello", 100];

특히 선택적 속성의 타입을 정의할 때 물음표(?) 기호를 이용해 표현
튜플 타입을 이용하면 배열 내부의 값, 타입의 순서, 항목의 개수까지 지정할 수 있음


유니온 타입

유니온 타입(union type)은 OR의 개념을 지원하는 타입
예를 들어 문자열 타입과 숫자 타입의 값만을 허용하는 변수를 선언할 때
파이프라인 기호(|)를 이용해 정의

type yourType = string | number;

let a1 : YourType = 100;
let a2 : YourType = "hello";

no와 name 속성은 필수이고 email 또는 tel 속성 중 한 속성을 반드시 포함해야 하는 객체를 위한 타입 정의할 때 유용하게 사용

type PersonType1 = { no:number; name:string; email:string };
type PersonType2 = { no:number; name:string; tel:string };
type PersonTypeUnion = PersonType1 | PersonType2 ;

let p1 : PersonTypeUnion = { no:1001, name:"홍길동", email:"abc@abc.com" };
let p2 : PersonTypeUnion = { no:1001, name:"홍길동", tel:"010-1111-1111" };

유니온 타입 대신 email과 tel을 ? 기호를 이용해 선택적 속성으로 정의할 수도 있지만, no와 name 속성만 잇는 경우를 허용해버린다.
즉, 'email 또는 tel 속성 중 하나를 반드시 포함해야 하는 경우'를 처리하지 못 한다.
유니온 타입은 이런 문제점 해결 가능


인터섹션 타입

인터섹션(intersection type)은 AND 개념을 지원하는 타입

type PersonTypeA = {
  no:number;
  name:string;
  email:string;
};

type PersonTypeB = {
  no:number;
  name:string;
  tel:string;
}

type PersonTypeInter = PersonTypeA & PersonTypeB;

const p4 : PersonTypeInter = {
  no: 1001,
  name: "홍길동",
  email: "abc@abc.com",
  tel: "010-1111-1111"
};

PersonTypeA, PersonTypeB 두 타입을 인터섹션 타입으로 정의하면 두 타입을 모두 만족시켜야 하는 타입이 됨
따라서 인터섹션 타입으로 선언한 p4 객체는 no, name, tel, email 속성을 모두 가짐


열거형

열거형(enum)은 정해진 값을 가지는 집합을 표현, 이것을 타입으로 사용할 수 있도록 함

// 숫자 열거형
enum Media {
  Newspaper, // 0
  Broadcasting, // 1
  SNS, // 2
  Magazine, // 3
  Youtube, // 4
}

let media1 : Media = Media.Youtube;
console.log(media1) // 4


// 문자 열거형
enum Media2 {
  Newspaper = "신문",
  Broadcasting = "방송", 
  SNS = "SNS",
  Magazine = "잡지",
  Youtube = "유튜브", 
}

let media : Media2 = Media2.Youtube;
console.log(media2); // "유튜브"

기본적으로 열거형을 생성하면 숫자값에 이름을 부여함
별도의 지정이 없다면 0부터 시작하는 값이 부여되지만 직접 시작 값을 부여할 수도 있음
모든 레이블에 값을 부여할 수도 있으며, 하나의 레이블에만 숫자값을 부여하면 그 이후의 값은 1씩 증가하는 값을 가짐


인터페이스

인터페이스(interface)는 객체, 함수, 클래스의 구조를 표현하는 약속
인터페이스를이용해 객체와 함수가 지정된 형태를 갖도록 규정하고 통제할 수 있음

interface IEmp {
  no: number;
  name: string;
  salary: number;
}

let emp1 : IEmp = { no: 1001, name: "홍길동", salary: 10000 };

인터페이스를 같은 이름으로 중복 정의하면 해당 인터페이스에 선언된 멤버들은 모두 병합되어 하나의 인터페이스에 쓰인 것과 같음

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

interface IPerson {
  name: string;
  tel: string;
}

// 인터페이스 병합
let p5: IPerson = { name: "홍길동", tel: "010-1111-2222", age: 20 };

인터페이스는 타입(type)과 유사해보이지만 확장하는 방법이 다르다.
일반적인 타입은 인터섹션(&)을 이용해 확장하고, 인터페이스는 상속(extends)을 이용해 확장한다.


타입 추론

타입스크립트는 명시적인 타입이 지정되어 있지 않으면 코드를 해석하여 타입을 추론
타입 추론 기능에 의해 let x = 100; 이 실행될 때 타입이 추론되어 x는 number 타입으로 선언됨
따라서 그 이후에 x에 string 타입의 "hello"를 할당하려고 하면 에러가 발생

let으로 선언한 변수와 const로 선언한 상수는 조금 다른 방법으로 타입을 추론함

let a = "hello";
let arr = [""]

const b = "hello"
let c = "world" as const;

let 키워드로 선언한 변수 a의 타입은 string으로 추론됨
하지만 const로 선언한 b 상수는 리터럴 값으로 타입을 추론
let으로 선언한 변수라도 as const로 타이븡ㄹ 단정하면 const 상수와 동일하게 리터럴 값으로 타입을 추론


참고 사이트

실행 컨텍스트
제네릭

profile
알고 쓰자!

0개의 댓글