기본

프로그래밍 언어 및 패러다임

초혼 2023. 2. 12. 16:59

프로그래밍 언어

해석 방식

컴파일 : 한 언어로 이루어진 코드를 다른 언어로 바꾸는 것

 

  • AOT 컴파일 : 소스 코드를 미리 기계어로 컴파일해서 실행
  • JIT 컴파일 언어 : 소스 코드를 바이트 코드로 1차 컴파일하고 그 바이트 코드를 실행하는 VM으로 구성
  • 인터프리터 언어 : 소스 코드를 한줄 한줄 읽어서 그때마다 번역해서 수행

메모리 관리 방식

  • 비관리(Unmanaged) : C, C++
    • 동적 할당된 메모리의 해제가 자동으로 이루어지지 않음
    • 메모리 직접 접근이 가능함
  • 관리(Managed) : Java, C#, Python, Go
    • 동적 할당된 메모리의 해제가 가비지 컬렉터에 의해 자동으로 이루어짐
    • 메모리 직접 접근을 차단함

정적, 동적 타입

  • 정적 타입 : C, C++, Java, C#, TypeScript
  • 동적 타입 : Python, JavaScript
  • 구분은 자료형의 고정 여부

 

프로그래밍 패러다임

언어에 대한 분류가 아닌 프로그래밍 방식에 대한 방법론이다.

→ 특정 언어를 특정 패러다임 언어로 정의하는 것은 틀린 말

→ 특정 프로그래밍 언어가 특정 패러다임의 핵심 개념을 언어 차원에서 지원하는 경우는 있다

 

여러 패러다임을 섞어서 사용할 수 있다

→ 웹 프론트의 JavaScript의 경우 여러 패러다임의 공존의 장

 

객체 지향 프로그래밍

데이터와 그 데이터를 처리할 메소드를 한데 묶어 객체를 만들고, 객체들을 조립하는 것을 목표로 한 언어

추상화

캡슐화

객체 내부에 필요한 데이터등을 묶어서 한번에 관리

상속

부모 객체를 상속받아 추가 기능을 더 붙이거나 약간의 수정을 가한 객체를 만들 수 있다.

다형성

메소드 이름은 같더라도 매개변수의 유무, 매개변수의 개수, 매개변수의 자료형, 반환하는 값의 자료형에 따라 다른 메소드가 실행될 수 있다

 

함수형 프로그래밍

순수함수 : 코드를 액션, 계산, 데이터로 분리, 특히 액션과 계산

불변성 : 카피 온 라이트(얕은 복사)와 방어적 복사(깊은 복사, structedClone())를 통해 불변성 유지

선언적 패턴 : 계층적 설계와 추상화 벽을 이용해 ‘무엇’과 ‘어떻게’를 구분

 

순수 함수

동일한 입력에 동일한 출력이 보장

함수 내부에 인자의 값을 변경하거나 프로그램의 상태를 변경하지 않음( = Side Effect가 없음)

→ 함수 바깥의 영향을 받지도 주지도 않는 함수

 

비상태(Stateless), 불변성(Immuntability)

데이터는 불변성을 유지해야한다

데이터 변경시 원본을 변경하지 않고 복사해서 복사본의 데이터를 변경해서 사용해야 한다

 

선언형 함수

명령형 프로그래밍은 ‘어떻게’에 집중 ↔  선언형 프로그래밍은 ‘무엇’에 집중

if, for, while, break ↔  map, filter, range, take ,reduce

 

1급 객체, 고차 함수

함수는 1급 객체이다

  • 변수나 데이터 구조안에 담을 수 있다.
  • 파라미터로 전달 할 수 있다.
  • 반환값(return value)으로 사용할 수 있다.
  • 할당에 사용된 이름과 관계없이 고유한 구별이 가능하다.
  • 동적으로 프로퍼티 할당이 가능하다.

함수는 고차함수 속성을 가지고 있다

  • 함수를 인자로써 전달 할 수 있어야 한다.
  • 함수의 반환 값으로 또 다른 함수를 사용 할 수 있다

 

장점

높은 수준의 추상화를 제공한다

함수 단위의 코드 재사용이 수월하다

불변성을 지향하기 때문에 프로그램의 동작을 예측하기 쉬워진다

 

단점

순수함수를 구현하기 위해서는 코드의 가독성이 좋지 않을 수 있다

함수형 프로그래밍에서는 반복이 for문이 아닌 재귀를 통해 이루어지는데 (deep copy), 재귀적 코드 스타일은 무한 루프에 빠질 수 있다

순수함수를 사용하는 것은 쉬울 수 있지만 조합하는 것은 쉽지 않다

 

기타

객체 지향 언어와 함수형 언어는 서로 배타적인 개념이 아니라 섞어 쓸 수 있다.

 

메모이제이션(Memoization)이 가능하다

→ 값이 필요할 때 계산을 수행한 후 다음에 해당 값이 필요할 때는 계산하지 않고 캐싱해 둔 값을 재사용

 

입력이 같으면 출력이 같다는 게 언어 차원에서 보장되기 때문에 병렬화가 쉽다

→ 성능상으로는 큰 이점이 있지는 않지만 안정성이 보장된다

 

지연 연산(Lazy evaluation)

→ 어떤 값이 실제로 쓰이기 전까지 그 값의 계산을 최대한 미루는 것

 

반응형 프로그래밍

데이터의 흐름과 변경 사항 전파에 중점을 둔 선언적 프로그래밍 패러다임

중요해진 계기

JQuery에서 웹 프레임워크로 전환

내가 DOM을 조작하기 쉽게 도와준다 → DOM을 알아서 렌더링 해준다

웹 프레임워크

MVVM 패턴(Model, View, View-Model)

값이 변경되었을 때 템플릿에서 선언해둔대로 알아서 렌더링을 해준다

선언적 프로그래밍

무엇을 해야 하는지 묘사

언제 어떻게 실행되는지는 내부에서 결정

Pull → Push

기존에는 필요한 데이터를 다 불러와서 렌더링함(Pull)

미리 선언되어 있는 구조에서 값이 변화될 때마다 템플릿으로 데이터를 전달(Push)

비동기 프로그래밍

비동기는 제어하기 어렵다

→ callback, promise, async&await

→ 비동기 로직을 최대한 동기 프로그래밍처럼 동작할 수 있는 방식으로 발전

웹 프론트는 여러 비동기 로직이 복잡하게 혼재되어있다

→ 기존 방식만으로는 한계가 있음

기본 구조

IoC(Inversion of Control)

모듈과 모듈이 결합할 때 참조의 주체를 바꿔서 사용하는 방식

네트워크 요청을 받는 Foo 모듈이 Bar 모듈의 값을 증가시키는 요청을 받았을 때

// Foo.js
import Bar
function onNetworkRequest() {
	// ...
	Bar.incrementCounter(value);
	// ...
}

Bar 모듈

모듈의 상태를 변화시키는 로직이 모듈 외부에 있다

→ 강결합, 자체 상태관리 불가능, 종속적

Foo 모듈

요청의 종류에 따라 처리 방식을 다 정해야 한다

// Bar.js
import Foo
const incrementCounter = (value) => { ... }
Foo.onNetworkRequest((event) => {
	// ...
	incrementCounter(event.value)
	// ...
}

Bar 모듈

모듈의 상태를 변화시키는 로직이 모듈 내부에 있다

→ 캡슐화, 느슨한 결합

Foo 모듈

누구에게 어떻게 데이터를 전달해야할지 몰라도 된다

→ 데이터가 발생한 시점만 전달하면 된다 → 책임의 분리

→ Event와 Promise와 동일 → 엇? 비동기 로직이 됬는데?

반응형 프로그래밍

DOM의 Event, Observer 패턴, Pub / Sub 패턴, Observable 객체

결합하는 모듈이 많아질수록 효율적

데이터의 흐름과 변경사항 전파에 중점을 둔 선언적 프로그래밍 패러다임

→ 데이터 흐름과 변경사항 전파 = Event

스트림(Stream)

Event를 Next, Error, Complete 3가지 타입으로 나누어서 보편적인 상황에 사용할 수 있게 한 것(Rx)

스트림을 선언적으로 작성하는 프로그래밍 패러다임

Observable.fromEvent(window, "click")
	.map(x => parseInt(Math.random() * 10))
	.filter(x => x % 2)
	.take(2)
	.forEach(x => console.log(x))

비동기적인 데이터 스트림을 이용한 프로그래밍 기법

이벤트를 하나의 값으로 생각하고 프로그래밍

모든 것을 스트림으로 간주하고 선언적으로 프로그래밍하는 것