프로그래밍 언어 및 패러다임
프로그래밍 언어
해석 방식
컴파일 : 한 언어로 이루어진 코드를 다른 언어로 바꾸는 것
- 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))
비동기적인 데이터 스트림을 이용한 프로그래밍 기법
이벤트를 하나의 값으로 생각하고 프로그래밍
모든 것을 스트림으로 간주하고 선언적으로 프로그래밍하는 것