[ICT멘토링 레벨업패스] TypeScript
컴파일 언어: 코드 전체를 한번에 해석해서, 실행할 수 있는 파일로 변환시켜주는 방식
인터프리터 언어: 코드를 한 라인씩 해석하면서, 실행하는 방식
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,
};
속성의 타입 지정.