인강/ICT멘토링 레벨업패스

[ICT멘토링 레벨업패스] TypeScript

dekoms 2024. 7. 17. 01:12

컴파일 언어: 코드 전체를 한번에 해석해서, 실행할 수 있는 파일로 변환시켜주는 방식

인터프리터 언어: 코드를 한 라인씩 해석하면서, 실행하는 방식

 

TypeScript는 컴파일 과정을 통해 JavaScript로 변환되어 사용된다.

 

1. 구상

2. 설계: 머리속으로 구상한 것을 문서로 기록하는 것.

3. 구현

 

컴파일링크를 합쳐서 "빌드" 과정이라고 부름.

 

/*
    Date: 2024. 07. 17
    author: dekoms
    remark: hello world
*/

함수 위에 간단하게 설명 적어주는 느낌.

 

 

자바스크립트 엔진

: 콜스택, 메모리힙이 존재한다.

 

콜스택: 원시 타입

메모리힙: 배열, 객체, 함수포인터

 

💯💯💯 렉시컬 환경???

 

포인터 변수: 주소값을 저장하는 변수.

 

 

if문을 여러 번 작성할 때, 이미 검증된 내용을 다시 검증하지 않도록 조심하자!!!

const a = 8;

if (a > 5) {
  console.log('5보다 큽니다');
}

// 불필요한 코드
if (a == 5) {
  console.log('5입니다');
}

 

if (조건) break;

if (조건) continue;

반복문에서 해당 로직은 맨 위에서 처리하는게 일반적인듯..?

 

 

함수가 호출될 때 메모리에 쌓이고, 함수가 끝날 때 메모리에서 소멸된다.

 

전역변수, static 변수

: 데이터 영역에서 관리됨. 프로그램이 종료될 때 메모리에서 소멸됨. 한번 선언되면 소멸되지 않음.

#include <stdio.h>

void func(){
  static int value = 0;
  value++;
  
  printf("%d\n", value);
}

int main(){
  int i = 0;
  
  while(i < 5){
    func();
    i++;
  }
  
  return 0;
}

가장 처음 func()가 실행될 때 value가 초기화되고, 다음 호출부터는 값이 유지됨.

 

 

배열의 이름은 상수다. 따라서 arr2 = arr1 형식으로 복사(대입) 불가능. 요소끼리 복사해야 함.

 

 

문자열 배열

char str[12] = "Hello, World";

문자열의 끝을 인식하기 위한 null문자가 있기에 배열 크기는 12여야 함. 컴퓨터는 null문자 이하의 값은 무시하기 때문.

 

 

포인터(포인터 변수): 메모리에서 특정 주소값을 저장하는 변수. 간접 참조.

#include <stdio.h>

int main() {
  int b = 100;
  int *pB = &b;
  
  printf("b = %d\n", b);      // b = 100
  printf("&b = %p\n", &b);    // &b = 0x7ffd5055
  printf("pB = %p\n", pB);    // pB = 0x7ffd5055
  printf("*pB = %d\n", *pB);  // *pB = 100

  return 0;
}

포인터 변수를 선언할 때 사용하는 *는 단순히 포인터 변수 선언용이고, 그 이후에 사용하는 *는 포인터 변수가 가리키는 주소에 해당하는 실제 값을 나타냄. 따라서, 그냥 포인터 변수를 사용할 때는 pB만 씀. (선언할 때만 *pB)

 

배열 == 첫 번째 요소의 주소값을 가지는 포인터.

But, 일반적인 포인터 변수와 다름. arr와 같은 배열의 이름은 상수이므로 주소값을 변경할 수 없다. 포인터 상수라고 불림. 배열은 선언될 때, 이미 메모리에 고정된 위치를 가짐.

 

 

함수의 인자 전달 형태

: 실인수에서 형식인수값이 복사가 된다.

int main() {
  int a = 10;
  Temp(a);
}

void Temp(int b) {

}

a에서 b로 복사가 일어난다. 메모리 공유가 아님!!! 전체 메모리에 4byte, 4byte 총 8byte의 크기를 차지하고 있음.

=> 매개변수가 많으면 불필요한 복사가 많이 발생함.

 

int main() {
  int arr[] = {1,2,3,4,5};
  Temp(arr);
}

void Temp(int *pArr) {

}

참조에 의한 복사. 주소값을 넘겨주고, 포인터 변수로 주소값을 전달 받음.

 

 

함수 포인터: 함수의 주소값만 저장함. 직접 함수를 호출하지 않고, 함수 포인터를 호출하여 함수를 동작시킬 수 있다. 확장성을 고려하여 미리 규격을 맞춰두는 동적 바인딩에 해당됨. VSCode Extension이 함수 포인터로 동작됨. (유지보수 및 유연한 확장성) 

정적 바인딩: 컴파일 시점에 결정. Ex) int a = 10;

동적 바인딩: 런타임 시점에 결정. Ex) var a = 10;

 

 

구조체: 서로 다른 종류의 변수들을 묶어서 새로운 데이터 타입을 정의함. (사용자 정의 : 구조체, 클래스 => 추상화.)

[구조체 변수].[구조체 멤버], .을 사용하는 것을 직접 접근이라고 함.

struct student{  // 구조체 생성
  char name[10];
  int age;
  int height;
} st1;  // 구조체 변수 선언

or

student st1; // 구조체 변수 선언

구조체 변수는 각각의 메모리 공간을 가짐.

 

공용체: union 키워드 사용. 공용체 변수 중 가장 큰 데이터 타입에 해당하는 메모리 공간을 공유함.

열거형: enum(이넘) 키워드 사용. 컴파일러가 정수형 상수로 취급. 연속적인 데이터에서 주로 사용함.

 

 

코드 영역: 실행할 명령어가 순서대로 쌓임. CPU가 명령어 하나씩 처리.

스택 영역: 지역 변수 및 매개 변수

힙 영역: 동적 할당

데이터 영역: 전역 변수, static 변수

 

동적 메모리 할당: 컴파일 시점이 아닌 런타임 시점에 메모리를 할당하자!!!

int num;
fputs("학생 수를 입력하세요: ", stdout);
scanf("%d", &num);
int student[num]; // 런타임 시점에 입력받는 변수를 컴파일 시점에 대입하는 것은 논리적으로 말이 안 됨.

런타임 시점과 컴파일 시점의 차이 발생.

int num;
int *student;

printf("학생 수를 입력하세요: ");
scanf("%d", &num);

student = (int*)malloc(sizeof(int) * num);  // 런타임 시점에 입력받는 변수 활용하기

// 포인터 변수 검증 (NULL 포인터 검사)
if(student == NULL){
  printf("메모리가 할당되지 않았습니다.\n");
  return;
}

printf("할당된 메모리 크기는 %d입니다.\n", sizeof(int) * num);

free(student);

지역 변수(student)에 힙 영역의 메모리를 할당하는 방식으로 해결.

=> new 연산자를 사용한 객체 생성도 메모리 구조가 동일함.

 

메모리 누수: 지역 변수만 사라지고, 힙 영역의 메모리 공간은 살아 있는 것.

가비지 컬렉터: 단편화 현상 해결. Compaction.

 

 

✅ 객체지향언어

자바스크립트는 객체 기반 언어이다.

 

구조적 프로그래밍 vs 객체 지향 프로그래밍

 

구조적 프로그래밍

하향식 방식, 기능적인 기본 단위: 함수

 

객체 지향 프로그래밍

기능 단위: 객체. Ex) 이벤트 기반의 모든 윈도우 프로그램(메모장 등).

 

 

객체지향 철학

1. 추상화 (공통점 묶기) - 사물의 특성을 정리하여 필드와 메소드로 표현하는 과정.

: 플라톤의 이데아. 관념으로 본질이 오로지 하나로 존재함. 본질에서 파생된 여러 가지 객체들을 확인할 수 있다.

 

2. 캡슐화 (은닉, 숨김) - 추상화된 결과를 하나의 클래스에 포함시키고 스스로 보호하는 것.

: 외부에서 내부를 볼 수 없게 함. 또한, 외부로부터 데이터를 조작할 인터페이스가 필요함.

클래스 = 데이터(멤버 변수) + 메소드(멤버 함수; 인터페이스-출입가능)

클래스의 본질은 데이터 타입이다. 따라서 구조체와 비슷함.

 

모든 변수는 선언이 되면 값을 초기화해야 한다.

객체도 본질적으로 변수이므로 선언되면 초기화해야 한다. Ex) Dog a = 10; int a = 10;

객체 생성 시 초기화 전용 메소드, 생성자가 자동으로 호출된다.

 

3. 상속성

접근 지정자: 상속 관계에서 부모 클래스의 속성은 보통 protected로 설정해줘야 함. (외부에서는 private처럼 보이고 상속 관계의 자식 클래스에는 public처럼 보임)

 

4. 다형성

: 얼핏 보면 같지만 자세히 보면 다른 것.

 

- 오버로딩: 이름은 같은 함수더라도 전달인자 타입이나 개수가 다른 경우 (C언어는 함수명이 동일하면 오류 발생. But, 객체 지향 언어에서는 다른 함수로 취급함.)

 

- 오버라이딩(재정의): 상속 기반. 기존의 것을 덮어씌운다. 함수 포인터를 객체 포인터로 확장한 개념.

Dog dog = new Dog();
dog.bark();  // 멍멍(Dog)

Pudle pd = new Pudle();
pd.bark();  // 왈왈(Pudle)

dog = new Puddle();
dog.bark();  // 왈왈(Pudle)

dog = new Jindo();
dog.bark();  // 컹컹(Jindo)

Dog dog = new Puddle(); Dog dog = new Jindo(); 와 같이 객체 생성하는 부분만 수정하면 된다. 객체 포인터처럼 사용!

 

 

인터페이스

: 메소드의 목록만을 가지고 있는 명세이다. 사용자 정의 타입. 클래스를 쓸지 인터페이스를 쓸지 결정해야 함.

메소드의 목록만 선언하고 구현하지 않음.

 

인터페이스의 목적: 기능을 추가하거나 수정하는 것 보다는, 동일한 개념의 기능을 새롭게 구현하는 것. 여러가지 기능의 나열(설계).

상속의 목적: 기능 확장.

 

 

람다식

: 기존의 익명 메소드를 더욱 간결하게 만든다.

(x, y) => x + y;

 

delegate: C#에서의 함수 포인터 역할.

 

 

✅ 타입스크립트 = 자바스크립트 + 타입체크

자바스크립트에 비해 가독성 증가, 유지보수 쉬움, 좋은 퀄리티의 코드 생산.

=> 좋은 구조 및 좋은 코드.

 

객체지향프로그래밍의 특성

: 클래스, 인터페이스, 컨스트럭터, 접근 지정자 등

 

타입스크립트를 자바스크립트로 컴파일 후, 브라우저로 전송함. (컴퓨터가 이해할 수 있는 언어로 변환해줌.)

=> 타입스크립트 : 프로그래밍 언어이면서 컴파일러임.

 

ESLint(코드 품질 도구): 코드의 잠재적인 문제에 대해 경고해줌.

npm(Node.js의 앱스토어 느낌): Extension처럼 필요한 앱들을 다운로드할 수 있음.

 

tsc app.ts  // ts 파일 컴파일

node app.js // js 파일 실행

tsc --init  // tsconfig.json 파일 자동 생성
tsc -w app.ts // watch 키워드. 자동으로 코드 수정을 판별하여 재컴파일 해줌.

 

 

타입스크립트는 정적 타이핑을 지원한다. 타입 추론(Type Inference) 기능.

let myName = 'lee';
myName = 1;

오류 발생함.

 

 

데이터 타입 종류

1. 기본 데이터 타입

- number: 숫자. 정수와 실수.

- string: 문자열.

- boolean: 참, 거짓

- null: 값이 없음. (의도적임)

- undefined: 값이 할당되지 않은 변수의 기본값. (의도적이지 않음)

 

2. 객체 타입

- object: 객체.

- array: 동일한 타입의 요소를 가지는 배열.

- tuple: 각 요소가 다른 타입을 가질 수 있는 배열

 

3. 특수 타입

- any: 어떤 타입이든 할당될 수 있음.

- unknown: 타입을 미리 알 수 없는 경우 사용.

 

 

타입 명시: 변수를 선언할 때, 변수 값의 타입을 지정함으로써 변수의 데이터 타입을 지정하는 것.

let x: string = '나는 문자열이다.";
function Plus(a: number, b: number): number {
  return a + b;
}

// 함수 반환 타입이 너무 길다는 단점.
function getInfo(id: number): {
  stdId: number;
  stdName: string;
  age: number;
  gender: string;
  course: string;
  completed: boolean;
} {
  return null;
}

함수 반환 타입이 너무 길다는 단점이 있다.

interface Student {
  stdId: number;
  stdName: string;
  age: number;
  gender: string;
  course: string;
  completed: boolean;
}

function getInfo(id: number): Student {
  return {
    stdId: id,
    stdName: "lee",
    age: 20,
    gender: "female",
    course: "javascript",
    completed: true,
  };
}

interface를 활용하여 해결.

interface Student {
  stdId: number;
  stdName: string;
  age?: number;		// 선택적 property
  gender: string;
  course: string;
  completed: boolean;
}

function getInfo(id: number): Student {
  return {
    stdId: id,
    stdName: "lee",
    // age: 20,
    gender: "female",
    course: "javascript",
    completed: true,
  };
}

선택적 Property를 활용해 리턴값 조절 가능.

// 매개변수 b는 Optional로 설정.
function Plus(a: number, b?: number): number {
  return a + b;
}

함수의 매개변수를 Optional로 설정 가능.

interface Student {
  stdId: number;
  stdName?: string;
  age?: number;
  gender?: string;
  course?: string;
  completed?: boolean;
  // setName(name: string): void;
  setName: (name: string) => void;
  getName: () => string;
}

Property에서 일반함수, 화살표함수를 이용해 정의하는 방법.

 

인터페이스

: 사용자 정의 타입. 인터페이스는 js파일에서 삭제된다(렌더링하는 측면에서 필요없는 코드임 - 런타임 시점의 코드). 오로지 ts파일에서만 의미를 가짐.

 

 

열거형(enum - 이넘)

: ts파일의 enum은 js파일에서 구현된다.

// 숫자 열거형
enum GenderType {
  Male = 0,
  Female = 1,
}

// 문자열 열거형
enum GenderType {
  Male = 'male',
  Female = 'female',
}

숫자 열거형은 값이 0부터 1씩 증가함. (default)

 

 

리터럴

: 해당 값이 정확하게 일치해야 한다.

interface Student {
  gender?: "male" | "female";
}

객체 리터럴

type CardinalDirection = 'North' | 'East'|'South'|'West'
let direction: CardinalDirection;
direction = 'North' // 유효
direction = 'Northest' // 에러

타입별칭

 

리터럴 장점: 코드의 가독성이 좋아짐. 잘못된 값이 들어오는 것을 방지할 수 있음.

 

const user: { name: string; age: number } = {
  name: "john",
  age: 25,
};

속성의 타입 지정.