본문 바로가기

공부/타입스크립트로 함께하는 웹 풀 사이클 개발(React, Node.js)

프론트엔드 기초: React + TypeScript(5)-함수포인터, 객체지향, 클래스, 오버로딩, 오버라이딩, 인터페이스, 람다식

함수 포인터

함수 포인터의 사용

🔸함수명 앞에 *만 붙여주면 함수 포인터가 선언된다.

자료형 (*함수 포인터 이름)(인자목록);
int (*func)(int a);

🔸함수 포인터도 포인터이므로 주소값을 저장한다.

🔸형식은 함수이지만 함수가 아닌 주소값을 저장하는 포인터 변수일뿐이다.

🔸함수의 주소값만 저장하기 때문에 함수 포인터라고 한다.

🔸함수 포인터를 만들땐 함수의 자료형과 같아야 한다.

 

함수 포인터 선언해서 사용해보기

 

함수 포인터 반복문 돌면서 switch case 값에 따라 주소값 바꿔서 printf() 찍어보기

🔥함수 포인터의 사용 이유

  • 직접 함수를 호출하면 되는데 왜 복잡하게 함수 포인터를 사용할까?
  • 메모리의 크기 및 위치가 결정되는 시점은 컴파일 타임 또는 런타임 시점이다.
  • 컴파일 타임 시점에 크기나 위치가 결정되는 것은 정적 바인딩,
  • 런타임 시점의 결정되는 크기나 위치는 동적 바인딩 되었다고 한다.
  • visual studio code에서 사용되는 extension 들은 플러그인 방식으로 동작한다.
  • 새로운 기능을 추가하게 되면 매번 다시 컴파일 해야하는 불편함이 있으나,
  • 플러그인 방식을 사용하면 그럴 필요가 없다.
  • 함수 포인터의 사용은 프로그램의 유연한 확장성을 제공한다.

구조체, 공용체, 열거형

구조체

🔸하나 이상의 서로 다른 종류(타입)의 변수들을 묶어서 새로운 데이터 타입을 정의하는 것

구조체를 사용하는 이유

🔸연관된 변수들을 하나로 묶어서 관리함으로써 데이터 관리에 유용하다.

🔸데이터 양(변수의 개수)이 많아지면 구조체가 유리하다.

예를들어 학생 정보 관리 시스템이라고 가정했을때,

  • 이름, 나이, 성별 등의 정보들을 모두 변수로 선언
  • 각각의 변수를 별도로 관리하면 연관성을 알 수 없다.
  • 학생 수가 10명 이내로 적으면 커버가 되지만, 100, 200명으로 늘어나면 변수의 관리가 힘들어진다.
  • => 구조체가 대안
  • 학생 한 명을 그룹으로 지정하여 이름, 나이, 성별 등의 정보들을 그룹으로 묶는다. => 그룹이 구조체

구조체 정의하기

🔸구조체의 기본 형태 (학생 정보)

struct student {
	char name[10];
    	int age;
    	int heigth;
}
  • struct 키워드는 구조체라는 데이터 타입을 의미
  • student는 내가 만든 구조체의 이름
  • name, age, height는 구조체 멤버

❄️클래스가 구조체의 형태이고 클래스는 곧 데이터 타입을 새롭게 정의한 것이다.

🔸구조체 student는 사용자인 내가 정의한 새로운 데이터 타입니다.

선언된 구조체 변수의 형태

student st1;
student st2;
...

구조체 멤버에 접근하기

🔸구조체 변수를 통해 구조체 멤버의 값을 참조해야 한다.

[구조체 변수명].[구조체 멤버]
ex) st1.name, st1.age, st1.heigth

🔸멤버에 접근시 .(점)을 사용하는데, 이를 직접 접근 연산자라고 한다.

공용체

🔸공용체도 사용자 정의 자료형이다.

🔸구조체와의 차이점은 메모리 공간을 공유한다는 점이다.

🔸타입스크립트에서도 타입을 공유하는 것이 있다.

union unTemp {
	char a;
    	int b;
    	double c;
}

❄️구조체와 차이점은 메모리를 공유한다는 점이다.

열거형

🔸열거형은 enumeration의 약자로 enum(이넘)이라고 읽는다.

🔸데이터들을 열거한 집합이다.

🔸구조체, 공용체와 같이 직접만든 사용자 정의 타입

🔸구조체 공용체 : 연관된 데이터

🔸열거형은 연속적인 데이터로 성격이 조금 다르다.

🔸컴파일러는 열거형 멤버들을 정수형 상수로 취급한다.

enum Week {
	sum = 0,
    	mon,
    	tue,
    	wed,
    	thu,
    	fri,
    	sat
};

🔸열거형의 멤버들은 각 요일을 나타낸다.

🔸첫 번째 멤버 sun을 0으로 설정하면 다음 멤버 mon은 1씩 증가한다.

메모리 구조

메모리 영역

🔸메모리는 크게 코드영역, 스택영역, 힙영역, 데이터영역 총 4가지로 구분

🔸코드영역

  • 실행할 명령어들이 순서대로 쌓인다.
  • CPU가 이 영역에서 명령어 들을 하나씩 가져다 처리한다.

🔸스택영역

  • 스택이란 모든 원소들의 삽입, 삭제를 한쪽 방향에서만 수행하도록하는 선형 자료구조이다.
  • 이를 후입선출방식(LIFO)이라고 한다.
  • 지역변수 및 매개변수 등은 모두 스택 메모리 사용

🔸힙영역 == 큐

  • 힙은 컴퓨터 메모리의 일부가 할당되었다가 회수되는 일들의 반복
  • 힙은 컴파일 시가 아닌 실행 시 사용자로부터 할당 메모리를 입력받음 => 동적메모리

🔸데이터영역

  • 전역변수와 static 변수가 저장되는 메모리 영역
  • 이 메모리는 프로그램 종료 시 소멸

동적으로 메모리를 할당하는 이유

🔸일반 변수 선언 - 메모리 할당은 컴파일 타임에 이루어진다.

🔸예로 학생수는 유동적이므로 그때마다 이미 선언된 변수 크기 선언을 계속 바꿔주기 어렵다.

🔸따라서 고정으로 변수 크기를 선언하는 것이 아니라 실행시 결정하게 코드를 짜자.

💢scanf()함수로 받아서 쓰는것은 문제점이 있다.
scanf는 런타임에 실행되고, 배열변수 선언은 컴파일타임에 실행된다.
따라서 런타임에 입력받은 변수를 컴파일 타임에 대입하는 형태는 시점 논리에 맞지 않음

🔸실행 중 학생수를 알아야 하는 경우, 동적 메모리 할당 기법을 통해 문제 해결이 가능하다.

동적 메모리 할당 및 해제

🔸동적 메모리 할당 함수의 원형

void* malloc(size_t size);
  • 전달인자 size는 바이트 단위로 입력한다.
  • 메모리 할당이 되면 메모리의 주소값을 리턴한다.
  • 메모리 부족 시 NULL 포인터를 리턴한다.
  • void*의 의미: 타입이 지정되지 않은 포인터
  • malloc()은 힙메모리에 저장한다.

✨C언어에서 gc를 free()함수로 사용하고 gc를 해주지 않으면

메모리누수, 메모리릭 현상이 발생한다.

객체지향에 대한 이해

왜 객체지향 철학을 이해해야 하는가?

🔸자바스크립트가 객체를 사용하는 객체 기반 언어이다.

🔸객체를 생성하는 원리를 이해하면 객체를 더욱 잘 사용할 수 있다.

🔸타입스크립트와 리액트 기반에서 필요하다.

객체지향이란?

🔸객체: 영문으로 Object, '사물'을 나타내는 추상적인 개념

🔸지향: 영문으로 Oriented, '~를 향한다'는 의미

🔸이 세상의 모든 사물을 프로그래밍화 시키겠다는 의미❗

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

✨구조적 프로그래밍 방식 - 순차적, 하향식, 폭포수 방식이라고 한다.

🔸기능적인 기본 단위는 함수이다.

🔸시퀀스가 한 쪽 방향으로 흘러가는 프로그래밍 방식

🔸건축과 비슷하다고 생각하면 되는데, 건축도 설계를 시공하면서 변경하기가 어렵듯

구조적 프로그래밍도 설계를 완벽하게 해야한다. (도중에 변경하기가 어렵다.)

🔸그래서 요구사항 변경이 있을 수 있는 요즘의 소프트웨어와 잘 맞지 않다.

 

✨객체지향 프로그래밍 방식

🔸기능 단위는 객체이다.

🔸유연하기 때문에 구조적방식과 다르게 요구사항 변경에 용의하다.

🔸대표적인 예가 이벤트 기반의 모든 윈도우 프로그램이다.

추상화

🔸추상: 대상에서 특징만을 뽑아낸 것

🔸우리 일상의 사물들은 관념적이고 추상적인 것들이 많다.

🔸플라톤의 이데아(Idea)는 추상화 개념과 일맥 상통한다.

캡슐화

🔸은닉하다, 숨긴다는 의미

🔸캡슐화하는 것은 외부에서 그 내부를 볼 수 없게 한다는 의미

🔸마냥 숨기기만 한다면 데이터는 무용지물임

🔸외부로부터 데이터를 조작할 인터페이스가 필요

클래스 = 데이터 + 메소드

클래스

🔸클래스는 사용자 정의 데이터타입이다.

🔸데이터와 메소드를 사용자인 내가 새로 정의한 테이터타입이기 때문에

클리스를 추상적인 데이터타입이라고 함

🔸클래스 본질은 데이터타입(Data Type)이라는 점이다.

🔸구조체와 비슷하다.

🔸멤버 변수와 멤버 함수로 구성된다.

클래스의 구성

🔸사물의 특성을 정리하여 필드의 메소드로 표현하는 과정이 추상화

🔸추상화된 결과를 하나의 클래스에 포함시키고 스스로 보호하는 것을 캡슐화

클래스의 선언 형식

🔸클래스 선언 시 class 키워드를 쓰고, 그 뒤에 클래스 이름을 붙인다.

🔸클래스의 요소로는 생성자, 멤버변수, 메소드 등으로 구성한다.

접근지정자

C#을 통한 클래스 선언

객체의 생성

🔸클래스는 데이터타입이라고 얘기했으니 변수선언과 같이

int a; char a; 처럼 Dog a; 로 사용하여 선언 할 수 있다.

🔸Dog a; 는 변수가 아닌 객체로 부른다.

🔸객체는 힙메모리에 저장

🔸말록은 데이터 사이즈를 사용자가 입력해줘서 사용하지만,

클래스 생성은 new로 선언했을때 자동으로 계산해서 사용할 수 있게 해준다.

Dog a = new Dog();

C#

생성자의 개념

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

🔸객체도 본질적으로 변수이므로 선언되면 초기화해야 한다.

🔸객체 생성 시 초기화 전용 메소드를 제곡하는데 바로 생성자(constructor)이다.

🔸객체 생성시 자동으로 호출되는 메서드

C#

상속성

🔸현실 세계의 상속개념

🔸이미 완성된 클래스를 다른 클래스에 상속할 수 있다.

🔸부모 클래스로부터 상속을 받을 때 C++,C#은 클래스 이름 끝에 콜론(:),

자바, js의 경우 extend를 붙인 후 부모클래스의 이름을 적는다.

C++, C#
접근지정자 클래스이름 : 부모클래스 {
	// 멤버목록
}

 

C# 에서 상속에 대해 살펴봤다.

💢강의에서 각 변수들을 private로 설정했더니 오류가나서
protected로 바꾸고 오류가 다 사라졌다.
private는 클래스 내부에서만 접근가능해서 외부에서는 절대 접근 불가능하다.
그러므로 외부에서는 접근이 불가능하고, 자식은 접근가능하게
접근지정자를 protected로 설정해주어야 한다.

🔸프로젝트 관점에서 상속개념

  • A 프로젝트 (종료됨)
  • B 프로젝트 (신규과제)
  • A 프로젝트 기능이 B 프로젝트의 기능과 유사함
  • B프로젝트는 A프로젝트를 상속받고 추가 기능만 구현하면 됨

다형성

🔸함수의 이름이 같더라도 전달인자나 타입이나 갯수에 따라 구분된다.

🔸객체지향에서는 대표적으로 오버로딩(overloading)과  오버라이딩(overriding) 기법이 있다.

오버로딩 (overloading)

🔸사전적 의미는 '과적하다', '적재하다'라는 의미를 가지고 있다.

🔸겉모습은 똑같지만 내용이 다른 경우

🔸이름이 같은 함수일지라도 전달인자 타입이나 갯수가 다른 경우

🔸스타크래프트 오버로드 유닛의 예

 

C# 오버로딩 예제

오버라이딩 (overriding)

🔸오버라이딩은 '위로 올라탄다', '엎어친다'는 의미를 갖는다.

🔸무언가에 올라타서 기존의 것을 덮어 버린다는 개념

🔸상속의 개념이 기반이 되어야 한다.

✨자바나 js같은 경우엔 클래스 오버라이딩시 부모 함수와 똑같이 선언하여 변경하면 되는데,
C#같은 경우에는 부모의 함수 접근지정자앞에 virtual,
자식의 함수 접근지정자와 타입사이에 override를 명시하여 사용하여야 오류가 없이 출력된다.

 

🔥부모 클래스에 자식 클래스 재할당해서 사용하면

오버라이딩에 의해 함수가 자식 함수로 변경되어 출력된다.

인터페이스에 관하여

인터페이스란?

🔸인터페이스란 메소드의 목록만을 가지고 있는 명세(Specification), 사용자 정의 타입이다.

🔸메소드의 목록만 선언하고 구현은 하지 않는다.

🔸인터페이스의 선언형태

🔸메소드의 목록을 선언 후 자식 클래스에서 상속을 해주고

자식 클래스에서 반드시 오버라이딩을 구현해야한다.
🔸인터페이스는 독립적으로 사용할 수 없다.

🔸인터페이스끼리도 기반인터페이스를 상속 할 수 있지만 필요하면 상속하고 없으면 생략해도된다.

// 인터페이스 선언형태
접근지정자 interface 이름 (: 기반인터페이스 // 있으면 사용 없으면 생략) {
	메서드 목록
}

// 인터페이스를 상속받는 클래스의 형태
접근지정자 class 자식클래스 이름 : 인터페이스 이름 {
	상속받은 인터페이스 메서드 오버라이딩
}

인터페이스를 사용하는 이유

🔸인터페이스는 본체가 정의되지 않는 추상메소드만 갖는다.

🔸인터페이스의 목적은 기존의 기능을 추가하거나 수정의 개념보다는

동일한 개념의 기능을 새롭게 구현하는 기능이다.

🔸공동작업 시 표준을 정하는 역할

추상 클래스를 상속하는 경우

🔸일반적으로 클래스를 상속하는 이유는 기능의 확장이 목적이다.

스마트폰을 예시로 들때,
스마트폰의 기본 기능들이 있으면 그 기능들을 추구하는 바에 맞게 확장시켜
삼성폰이나 아이폰으로 출시하는 경우를 생각할 수 있다.

인터페이스를 상속하는 경우

🔸스마트폰의 기능 목록: 통화, 문제, 와이파이, 블루투스, 멀티미디어, ...등

🔸스마트폰과 상속 관계의 개념이 아니라 여러가지 기능의 나열

🔸인터페이스에서 기능을 명세하고, 자식클래스에서 상속한다.

🔸다중상속 가능

// 인터페이스 예
interface 통화기능 {
}
interface 문자메세지기능 {
}
interface 와이파이기능 {
}
interface 멀티미디어기능 {
}
interface 블루투스기능 {
}

// 스마트폰의 기본 설계를 위한 interface 다중상속
class 스마트폰_기본 : 통화기능, 문자메세지기능, 와이파이기능, 멀티미디어기능, 블루투스기능 {
	통화기능구현
    	문자메세지구현
    	와이파이구현
    	멀티미디어구현
    	블루투스구현
}
이어지는 스마트폰의 예시로,
스마트폰의 기본 기능들의 앞 단에서 이루어지는 일들을 의미한다고 생각하면 된다.
인터페이스로 표준을 정하고 상속하여 거기서 원하는 메소드들을 오버라이딩하여
원하는 메서드로 사용하게 할 수 있는 설계를 위한 전 단계이다.
💢꼭 설계를 위한 전 단계보다도 중간 구현단계에서도 필요에 의해 새로운 기능을 명세할 때 사용한다.

 

클래스와 인터페이스 간의 UML

인터페이스 코드 작성

🔸인터페이스를 생성하고, 인터페이스로부터 상속받은 클래스를 구현

🔸스타크래프트 게임의 3종족들의 유닛 기능 - 공격과 이동 등

🔸공격과 이동은 유닛의 필수 기능

🔸유닛 중에 저글링과 드라군 2개의 유닛을 인터페이스를 구현

메모리 관리

🔸플랫폼 기반의 객체 지향 언어는 가비지 컬렉터가 메모리를 자동 관리한다.

🔸백그라운드에서 더 이상 사용되지 않는 메모리를 찾아 회수한다.

🔸new 연산자로 생성하는 객체의 데이터는 힙메모리에 생성되기 때문에 동적 할당이 된다.초기 상태:

✨ 초기상태:
[A][B][C][D][E][F][ ][ ][ ][ ]
여기서 A, B, C, D, E, F는 데이터를 나타내고, [ ]는 빈 공간

단편화 발생:

[A][ ][C][ ][E][ ][ ][B][ ][F]
일부 데이터가 삭제되고 새로운 데이터가 추가되면서 메모리/디스크 공간이 단편화됨

외부 단편화:

위 상태에서 [ ]로 표시된 빈 공간들이 외부 단편화이다.
이 작은 빈 공간들은 개별적으로는 너무 작아서 새로운 큰 데이터를 저장하기 어렵다.

컴팩트(압축) 과정:
[A][C][E][B][F][ ][ ][ ][ ][ ]
컴팩트 과정을 통해 데이터를 연속적으로 재배치하여 큰 빈 공간을 만든다.

결과:

컴팩트 후에는 연속된 큰 빈 공간이 생겨 새로운 데이터를 효율적으로 저장할 수 있게 됨
이 과정을 통해 단편화로 인한 공간 낭비를 줄이고 메모리/디스크 사용 효율을 높일 수 있다.

람다를 통한 화살표 함수의 이해

익명 메소드

🔸메소드를 미리 정의하지 않고 사용할 때 정의한다.

🔸익명 메소드를 사용하면 코드가 간결해진다.

🔸익명 메소드는 별도의 메소드를 만들지 않으므로 코딩 오버헤드를 줄일 수 있다.

🔸익명 메소드는 내용 자체가 복잡하면 안된다.

🔸익명 메소든느 람다식에서 사용된다.

람다식이란

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

🔸코드를 짧고 간결하게 표현하는 것이 목적이다.

 

🔸일반메소드 기반

 

🔸익명메소드 기반

람다식 표현

🔸델리게이트 본체를 람다식으로 표현한다.

🔸델리게이트와 인수의 개수 및 타입은 일치해야 한다.

🔸익명 메소드의 식을 조금 더 간결하게 표현할 수 있는 것이 람다식

(인수) => 표현식 or 명령문

🔸delegate 키워드는 사라지며 람다식을 의미하는 => 기호로 대체된다.

 

🔸람다식 표현