diff --git a/_includes/sidebar/article-menu.html b/_includes/sidebar/article-menu.html
index 43c2fb3..95de160 100644
--- a/_includes/sidebar/article-menu.html
+++ b/_includes/sidebar/article-menu.html
@@ -15,7 +15,7 @@
function generateContent() {
var menu = document.querySelector(".post-menu");
var menuContent = menu.querySelector(".post-menu-content");
- var headings = document.querySelector(".post-content").querySelectorAll("h2, h3, h4, h5, h6");
+ var headings = document.querySelector(".post-content").querySelectorAll("h1, h2, h3, h4, h5, h6");
// Hide menu when no headings
if (headings.length === 0) {
@@ -53,16 +53,16 @@
var beginIndex = index;
var endIndex = index + 1;
while (beginIndex >= 0
- && !items[beginIndex].classList.contains('h-h2')) {
+ && !items[beginIndex].classList.contains('h-h1')) {
beginIndex -= 1;
}
while (endIndex < items.length
- && !items[endIndex].classList.contains('h-h2')) {
+ && !items[endIndex].classList.contains('h-h1')) {
endIndex += 1;
}
for (var i = 0; i < beginIndex; i++) {
item = items[i]
- if (!item.classList.contains('h-h2')) {
+ if (!item.classList.contains('h-h1')) {
item.style.display = 'none';
}
}
@@ -74,7 +74,7 @@
}
for (var i = endIndex; i < items.length; i++) {
item = items[i]
- if (!item.classList.contains('h-h2')) {
+ if (!item.classList.contains('h-h1')) {
item.style.display = 'none';
}
}
diff --git a/_posts/2022-03-17-TWL-02-1-OOP.md b/_posts/2022-03-17-TWL-02-1-OOP.md
new file mode 100644
index 0000000..17a9958
--- /dev/null
+++ b/_posts/2022-03-17-TWL-02-1-OOP.md
@@ -0,0 +1,620 @@
+---
+layout: post title: TWL-02 첫번째. 객체지향 프로그래밍 subtitle: 객체지향 프로그래밍에 대해 알아봅니다. categories: [TWL, CS]
+tags: [TWL, CS, OOP]
+---
+
+# 객체지향 프로그래밍 (OOP)
+
+> Object Oriented Programming
+
+요즘은 처음 코딩을 배울 때 어떤 프로그래밍 언어를 배우는지 모르겠지만 저는 첫 프로그래밍 언어로 `Java`를 배웠습니다. 기억 속에서 커피를 마시면서 만들어서 `Java`라는 이름을 가졌다는 이야기와 VM을
+사용하는 **객체지향적 프로그래밍 언어**라는 이야기를 처음 들으며 언어를 공부했고 이후에는 `C`, `C++`, `Python`의 언어들을 배우게 되었습니다.
+(그 학생은 5년 뒤 Java를 전혀 쓰지 않고 `Rust`, `Go`를 사용하게 됩니다...)
+
+최근에는 Java를 사용하지는 않지만 객체지향적으로 개발하라는 말은 여전히 듣습니다. 그러면 어떤 장점이 있어서 객체지향적으로 개발하라는 걸까요?
+
+## 객체지향적으로 개발하라?
+
+먼저 `객체지향적으로 개발하라`는 의미를 알아야 합니다. 구글링을 해보면 주로 객체 지향적으로 개발한다는 것은 `실제 세상처럼 객체들의 집합으로 생각하고 프로그래밍한다`라는 말이 자주 보입니다. 그런데 사실 **실제
+세상의 객체처럼 프로그래밍 한다면 객체지향적인 개발이 아닙니다.**
+
+우리가 사는 세상에서는 주체 A가 행동을 하고 다른 객체들은 주체 A의 행동에 영향을 받아서 상태가 변경됩니다. 하지만 객체지향적으로 개발한다면 주체 A가 행동할 때, 다른 객체 B에게 행동을 시킵니다. 그러면 객체
+B는 자신의 행동을 통해 자신의 상태를 변경합니다. 그 후의 결과를 주체 A가 받아서 행동을 마무리하게 됩니다.
+
+개발을 하다보면 동작이 얼마 없을 때는 하나의 주체 A가 모든 상태를 변경해도 크게 문제가 없습니다. 하지만 점점 규모가 커져 주체 A, 객체 B, C, D, ... 로 늘어나게 되면 A의 동작 하나에 여러 객체들의
+상태가 변경된다면 실행 결과에 대한 분석이 어려워집니다.
+
+
+
+
+
+꼬여있는 코드를 분석하는 것이 새로 구현하는 것보다 어렵습니다. {: style="text-align: center; color: gray; margin-top:.5em;"}
+
+만약 A가 B, C, D, ... 의 상태를 변경하는 작업들이 있다면, A의 동작은 객체의 수에 비례해 늘어나고 B, C, D, ... 의 상태를 변경하는 작업들이 복잡하게 얽혀있을 것입니다. 그런데 코드가 처음부터
+복잡하게 얽혀있지는 않았을 겁니다. 그렇다면 처음부터 엉키지 않도록 조심히 구현하면 어떨까요?
+
+먼저 생각할 수 있는 방법은 `각 객체의 상태는 객체 스스로 다루도록 만드는 것`입니다.
+
+객체지향적으로 개발하라는 말의 의미는 `상태를 변경하는 작업을 객체라는 최대한 작은 범위로 줄여` 코드가 얽히지 않도록 개발하라는 의미입니다.
+
+### 절차적 프로그래밍
+
+> Procedural Programming (프로시저 프로그래밍)
+
+객체지향적으로 개발하는 것은 `잘 프로그래밍 하기 위한` 방법 중 하나입니다. 당연하게도 `잘 프로그래밍 하기 위한` 여러 방법들이 제안되었고, 그 중 하나가 절차적
+프로그래밍(`procedural programming`)입니다.
+> `procedural programming`은 사실 프로시저(함수) 프로그래밍에 가깝지만 (직역한) 번역명으로 더 잘 알려진 절차적 프로그래밍이라고 적겠습니다
+> `oriented`가 없는데 절차 "지향"적 언어라고는 못 적겠네요;;
+
+cpu는 특정 연산을 수행하라는 명령어(`instruction`)를 받아 처리하는 구조입니다. 이런 instruction을 기반으로 만들어졌다보니 프로그래밍 언어 또한 명령형 프로그래밍인 경우가 많습니다. 절차적
+프로그래밍은 이런 배경에서 나온 프로그래밍 방법입니다.
+
+절차적 프로그래밍은 명령형 프로그래밍 언어에서 `연산을 그대로 나열하는 것이 아닌` 무슨 작업을 할지 함수로 만들어
+`단계별로 호출하도록 개발하는 방법`입니다. 이런 모습이 어떠한 절차대로 진행되는 것처럼 보이기도 합니다.
+
+사실 객체 지향적으로 개발하면서도 내부적으로는 함수를 호출하도록 개발하기 때문에 완전히 다른 개발 방법이라고 하기에는 문제가 있습니다. 하지만 절차적 프로그래밍에서는 객체지향 프로그래밍과 다르게 상태를 어떻게 다룰
+것인가에 대한 내용은 다루지 않습니다.
+
+```c
+void move(int* x, int* y, int move_x, int move_y) {
+ *x = *x + move_x;
+ *y = *y + move_y;
+}
+
+int main() {
+ int my_x = 0;
+ int my_y = 0;
+ // 내 위치를 (2, 4) 만큼 이동한다.
+ move(&my_x, &my_y, 2, 4);
+ return 0;
+}
+```
+
+c언어에서는 함수를 정의하고 데이터를 인자로 입력해 원하는 순서대로 호출합니다. {: style="text-align: center; color: gray; margin-top:.5em;"}
+
+절차적 프로그래밍 언어로 가장 유명하고 현재도 많이 사용되는 c 언어로 예를 들어보면 어떠한 동작을 하는 함수를 구현하고, 함수에 입력으로 값이나 포인터를 넘겨 연산을 순서대로 실행하도록 개발합니다.
+
+#### 추상화
+
+> 모든 프로그래밍 방법론은 추상화에서 시작된다.
+
+절차적 프로그래밍에서 추상화가 없는 것은 아닙니다. 추상화라는 것은 구체적인 작업을 핵심적인 기능만으로 표현해내는 것입니다. 예를 들어 우리는 하드디스크에 어떻게 저장되어있는지 모르더라도 파일을 통해 접근하고
+CPU가 어떻게 돌아가는지 모르더라도 프로세스가 돌아가고 있습니다. 실제로 CPU와 하드디스크에서 어떻게 동작하는지 알면 좋겠지만, 모르더라도 추상화된 동작만 안다면(심지어 모르더라도 사용은 가능합니다)
+사용하는데 문제가 없습니다.
+
+위의 코드에서는 `x, y를 조작하는 구체적인 작업`이 아닌 `move라는 핵심적인 기능(함수명)`만으로 사용할 수 있도록 추상화 한것입니다. 이후에는 어떻게 동작하는지는 함수명과 설명에 대한 신뢰를
+가지고 `move` 함수를 호출해 사용할 수 있습니다.
+> 물론 실제 구현이 설명과 다르다면 문제가 발생합니다.
+> 이때부터 개발자의 직업은 작명가로 바뀝니다.
+
+함수의 내부적인 구현을 우리가 알면 좋겠지만, 모르더라도 추상화된 동작만 알고 있다면 우리가 호출해서 사용하는데 문제가 없습니다. 내부적인 구현을 정확히 이해하지 않고도 사용할 수 있다는 점이 구체적인 동작을 그대로
+사용하지 않고 추상화하는 이유 중 하나입니다.
+> 내부 구현이 대부분의 개발자가 이해하는 내용과 다르다면 버그입니다.
+
+```c
+void move(int* x, int* y, int move_x, int move_y) {
+ *x = *x + move_x;
+ *y = *y + move_y;
+}
+
+void jump(int* x, int* y, int move_x, int move_y) {
+ *x = move_x;
+ *y = move_y;
+}
+
+int main() {
+ int my_x = 0;
+ int my_y = 0;
+ // move 대신 jump 하도록 수정
+ // move(&my_x, &my_y, 2, 4);
+ jump(&my_x, &my_y, 2, 4);
+ return 0;
+}
+```
+
+추상화된 동작은 대체하기도 쉽다. {: style="text-align: center; color: gray; margin-top:.5em;"}
+
+연산을 추상화하게 되면 `전체적인 로직에 대해서 쉽게 이해`할 수 있습니다.
+`main` 안에서 `my_x`와 `my_y`를 단순히 더하거나 빼는 연산보다는
+`move`라는 함수 안에 로직을 넣고 `main`에서는 `move` 함수를 호출하는 것이 어떤 작업을 하는 건지 쉽게 이해할 수 있습니다.
+> 디스크의 실제 동작을 보는 것보다 추상화된 파일 read/write가 더욱 이해하기 쉽습니다.
+
+뿐만 아니라 추상화된 작업은 `다른 작업으로 쉽게 변경할 수 있습니다`. 현재는 코드를 `move`하는 동작으로 사용하고 있지만 만약 `jump`하는 동작으로 수정되어야 한다면 호출하는 함수를 변경하는 것으로 쉽게
+다른 동작으로 변경할 수 있습니다.
+
+비즈니스 동작이 달라지거나 최적화가 필요하거나 혹은 업데이트를 하는 등 여러 이유로 `코드는 항상 변경될 가능성을 가지고 있습니다`. 그런 점에서 추상화를 통해 이전 코드를 간단하게 대체할 수 있다는 것은 굉장한
+장점입니다.
+
+절차적 언어에서는 함수 단위의 추상화를 제공하고 있습니다. 만약 어셈블리 언어로 개발했다면 함수를 통해 정해진 연산을 호출하는 작업은 생각하지 못했을 것입니다. 하지만 c언어에서는 구체적인 연산을 매번 사용하지 않고
+함수로 호출할 수 있도록 추상화를 제공하고 있습니다.
+> 절차적 프로그래밍은 함수로 연산을 묶어 재사용하는 정도의 수준으로 함수형 프로그래밍과는 다릅니다
+
+### 객체지향
+
+> 객체로 모든 것을 생각해 프로그래밍 하는 것
+
+그렇다면 이제 객체지향에서의 추상화에 대해 쉽게 이해할 수 있을 겁니다. 절차적 프로그래밍에서는 동작을 함수로 추상화했다면 객체지향 프로그래밍에서는 동작에 상태를 더해 객체로 추상화합니다. c언어에서 절차에 대한
+추상화 기능으로 함수를 제공해 절차적 언어라고 말하는 것처럼, 객체지향 언어는 객체를 추상화할 수 있도록 기능을 제공하는 언어를 말합니다.
+> c언어에서 객체지향처럼 흉내낼 수는 있지만 객체지향 언어라고 하기에는 기능이 부족합니다.
+
+앞서 설명했던 예시를 객체지향적으로 수정해보겠습니다.
+
+```java
+interface Mover {
+ void move(int moveX, int moveY);
+}
+
+class Human implements Mover {
+ private int x;
+ private int y;
+
+ public Human(int x, int y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ @Override
+ public void move(int moveX, int moveY) {
+ this.x += moveX;
+ this.y += moveY;
+ }
+}
+
+class Jumper implements Mover {
+ private int x;
+ private int y;
+
+ public Jumper(int x, int y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ @Override
+ public void move(int moveX, int moveY) {
+ this.x = moveX;
+ this.y = moveY;
+ }
+}
+
+public class Main {
+ public static void main(String[] args) {
+ Mover mover = new Human(0, 0);
+ Mover jumper = new Jumper(0, 0);
+ mover.move(2, 4);
+ jumper.move(2, 4);
+ }
+}
+```
+
+main에서는 추상화된 객체의 동작만이 실행된다. {: style="text-align: center; color: gray; margin-top:.5em;"}
+
+객체지향 프로그래밍에서는 동작만 추상화하는 것이 아니라 상태도 함께 객체로 추상화합니다. 상태를 가지고 있는 클래스에서 동작을 메소드로 구현해 사용할 때는 인터페이스의 함수를 호출해 상태가 변경되도록 합니다. (
+상태와 동작의 추상화)
+또한 추상화된 함수에서 다룰 값도 함께 객체에서만 접근할 수 있도록 만들어 외부 동작에 의해 추상화된 로직이 깨지지 않도록 만들 수 있습니다.(캡슐화)
+> c의 struct가 박스에 있는 데이터를 우리가 직접 꺼내는 구조라면 클래스는 박스에 손이 달려(?) 박스가 직접 데이터를 다루는 구조입니다.
+
+객체에 대해 추상화가 되었기 때문에 절차적 프로그래밍에서 함수를 다른 기능의 함수로 대체했듯이 다른 작업을 하는 객체로도 대체할 수 있습니다.
+
+### 객체지향의 특징
+
+기본적으로 꼽히는 객체지향의 특징은 다음과 같습니다.
+
+1. 추상화
+ * 객체를 추상화해 복잡한 로직이 아닌 객체를 통해 핵심적인 기능만을 볼 수 있도록 합니다.
+2. 캡슐화
+ * 데이터를 객체 내부에 저장해 접근제어자(`private`, `public`)를 통해 외부에서 조작하지 못하도록합니다.
+ * 추상화된 로직을 사용할 때 내부 데이터를 외부에서 접근한다면 정해진 로직이 깨질수 있습니다.
+3. 상속
+ * 상위 클래스의 필드와 연산을 하위클래스에서 가질 수 있습니다.
+ * 최근에는 상속의 문제점들로 인해 상속을 지원하지 않는 언어들이 보이고 있습니다. (`rust`, `go`)
+ * 대신 내부 필드로 확장할 객체를 가지는 `composition`으로 구현합니다.
+4. 다형성
+ * 같은 이름의 메소드를 다양한 타입이나 다양한 구현(`overriding`)으로
+ * `overloading`, `overriding`을 이용해 다형성이 발현됩니다.
+ * `overriding`하면서 `Dependency Injection(DI)`을 가능하게 하는 기본 성질입니다.
+ * 테스트 코드의 mocking 에도 사용되는 성질입니다.
+
+#### 객체지향의 추상화
+
+실생활에서 우리는 여러 전자 기기를 사용하지만 내부적으로 어떻게 동작하는지 모릅니다. 어떻게 돌아갈지 짐작만 하고 있을 뿐이죠. 대부분 사용설명서를 읽고 원하는 기능을 찾아 기기를 사용합니다. 추상화하는 것도
+동일합니다.
+
+
+
+
+
+사용설명서를 보시나요? {: style="text-align: center; color: gray; margin-top:.5em;"}
+
+우리는 구현할 때와 사용할 때를 구분해서 생각해야합니다. 절차적 프로그래밍의 함수나 객제지향 프로그래밍의 객체를 구현할 때는 전자기기를 만드는 것과 마찬가지로 상태가 어떻게 관리되고 어떻게 동작하는 지 알고 있어야
+합니다. 하지만 구현된 함수나 객체를 사용할 때는 전자기기를 사용하는 것처럼 굳이 내부에 대해 모르더라도 사용할 수 있어야 합니다.
+> 사용 설명서를 읽지도 않고 어림짐작으로 사용하던 모습은 api 설명을 안 보고 함수명만 보고 사용하는 모습과 비슷합니다
+
+앞서 말했지만 추상화는 객체지향에서만의 특징은 아닙니다. 절차적 프로그래밍에서도 추상화가 들어있습니다. 다만 추상화하는 정도나 대상이 다를 뿐입니다.
+
+절차적 프로그래밍에서는 `어떤 상태를 받아 연산을 하는 함수` 로 추상화하고, 객체지향 프로그래밍에서는 `상태도 연산과 함께 추상화` 해서 객체에서 관리합니다. 상태를 함께 추상화한다는 것은 추상화된 인터페이스를
+사용할 때 `상태가 어떤 방식으로 관리되는지 모르더라도 사용할 수 있음`을 의미합니다.
+
+예를 들어 절차적으로만 생각하면 함수를 정의해 연산을 추상화하고, 함수를 호출해서 사용함으로써 내부 연산을 모르더라도 사용할 수 있지만 입력으로 전달하는 `struct` 값들은 따로 관리하고 함수의 인자로 넘겨주어야
+합니다.
+
+하지만 객체지향적으로 생각하면 상태에 대한 관리도 객체에서 함으로써 객체를 사용할 때 더이상 상태값이 어떻게 관리되는지 모르더라도 사용하는 데 문제가 없습니다. 상태 값을 어떻게 관리할 지는 객체를 구현할 때의
+문제입니다.
+> `상태를 객체 내에서 잘 관리`하는 것이 객체지향 프로그래밍의 핵심입니다.
+
+예를 들어 처음 파이썬 또는 자바스크립트를 공부할 때, `dictionary`가 내부적으로 어떻게 구현되어있는지 모르더라도
+`key`로 값을 저장하고 가져온다는 사실만 알고 있으면 `dictionary`를 사용할 수 있습니다. 추상화하면 (성능은 잠시 미뤄두고...)
+객체의 구현에 대해 모르더라도 `사용하는데는 문제가 없습니다`. 그 후 `dictionary`의 구현을 Hash를 이용할 것인지 Tree를 이용할 것인지는 `구현의 문제`입니다.
+
+#### 캡슐화, 정보은닉
+
+캡슐화는 객체의 상태나 연산을 해당 객체에서만 접근할 수 있게해서 외부 연산에 의해 오류가 발생하지 않도록 합니다. Java나 C++에서 제공하는 `private`, `protected`, `public`
+접근제어자를 통해 객체, 상속받은 객체, 외부에서 접근하는 것을 제어하며, golang은 대문자, 소문자를 이용하고, Rust에서는 기본적으로 private하며 `pub` 접근 제어자만을 제공합니다.
+> 파이썬은 `_`를 앞에 붙여 접근제어자처럼 사용하지만 실제로 접근은 가능하기에 접근제어자가 없어 완벽한 객체지향언어가 아니라는 말이 나옵니다.
+
+연산이 복잡해지고 많아지면 메소드가 계속 늘어나는데 이때 객체 내부에서만 재활용할 메소드들은 `private`으로 객체 내부에서만 접근할 수 있도록 두어 외부 인터페이스와 내부에서 사용할 메소드를 구분하는데 주로
+사용합니다.
+
+#### 상속
+
+최근 언어들에서는 클래스 상속 기능을 제공하지 않는 언어들이 많습니다. 상속은 상위 클래스에서 구현한 메소드나 필드를 하위 클래스에서도 물려받는 것을 의미합니다. 상위 클래스에서 구현한 기능을 하위 클래스에서
+구현하지 않더라도 사용할 수 있다보니 구현하는 측면에서 재사용성이 늘어나는 장점이 있습니다.
+> 상속을 이용하면 확실히 구현할 코드가 줄어듭니다.
+
+하지만 상속으로 구현하게 되면 하위 클래스에서 메소드를 오버라이딩, 오버로딩하거나 여러 클래스를 다중 상속받으면서 하위 클래스에서 상위 클래스의 기능을 깨뜨리는 경우도 발생합니다. 자세한 내용은 이후 객체지향
+원칙에서 설명하겠지만 상속의 문제점들로 인해 최근에는 상위 클래스를 직접 상속받는 것이 아닌 필드로 받아 사용하는 `composition` 방식으로 구현하는 경우가 많습니다.
+
+하지만 go(`interface`)와 rust(`trait`)에서도 인터페이스를 `implements`하는 기능들은 제공합니다. 인터페이스는 실제 기능이 구현된 것이 아니기 때문에 상속에서 생기는 문제에서 비교적
+자유롭기 때문으로 보입니다.
+
+#### 다형성
+
+객체의 메소드 이름은 같지만 다양한 타입이나 구현을 가질 수 있는 성질입니다. 기본적으로는 메소드 이름이 같지만 다른 타입의 인자와 리턴값을 가지는 `overloading`과 하위 클래스에서 메소드를
+재작성하는 `overriding`으로 이루어집니다.
+
+최근 언어에서는 `overloading`은 오히려 코드를 읽는데 헷갈릴 수 있다는 이유로 지원하지 않는 언어도 있지만 `overriding`은 다릅니다. 최소한 `interface`의 메소드를 `overriding`
+할 수 있도록 기능을 제공하고 있습니다. 각 클래스에서는 구현한 클래스 타입을 필드로 가지는 것이 아니라 `interface`를 타입으로 필드를 가집니다. 그 후에 사용할 때 구현 클래스를
+대입해 `interface`로 호출하지만 대입한 구현 클래스를 실행시키는 효과를 얻을 수 있습니다.
+
+이를 통해 다른 기능을 사용할 때 다른 구현 클래스를 대입해 사용해 내부 구현을 변경하지 않더라도 다른 기능을 실행할 수 있고, 메소드의 인자로 `interface`를 받는다면 메소드의 구현을 변경하지 않더라도
+외부에서 원하는대로 메소드가 동작하도록 구현 클래스를 넘기는
+`Dependency Injection(DI)`을 할 수도 있습니다.
+
+### 객체지향의 원칙 (SOLID)
+
+> 어떻게 개발해야 좋은 구조로 개발하는 것인가?
+
+그럼 어떻게 개발해야 객체지향적으로 잘 개발하는 걸까요? 먼저 어떻게 개발하는 것이 잘 개발하는 것인지 생각해봅시다. 사람마다 다르게 생각할 수 있지만 제 생각에는 코드의 가독성이 좋으면서 유지보수가 쉽고 새로운
+기능을 확장하기 쉬운 구조로 개발하는 것입니다. 이렇게 개발할 때 지침으로 삼을 수 있는 것이 바로 SOLID 원칙입니다.
+
+* S: SRP: Single Responsibility (단일 책임의 원칙)
+ * 하나의 객체는 하나의 책임(기능, 역할)만을 가져야합니다.
+* O: OCP: Open-Closed (개방 폐쇄의 원칙)
+ * 기능 확장에는 열려있고, 수정(구현 코드)에는 닫혀있어야합니다.
+* L: LSP: Liskov Substitution (리스코프 치환의 원칙)
+ * 부모 클래스의 인스턴스 위치에 자식 클래스를 두더라도 문제가 없어야 합니다.
+ * 여기서 상속을 잘못 구현하면 문제가 발생합니다.
+* I: ISP: Interface Segregation (인터페이스 분리의 원칙)
+ * 클래스 내에서 사용하지 않을 인터페이스(메소드)는 구현하지 않아야 합니다.
+ * 각 인터페이스를 최소화하여 분리합니다.
+* D: DIP: Dependency Inversion (의존 역전의 원칙)
+ * 상위 모듈은 하위 모듈에 의존하지 않고 추상화가 세부 구현(인터페이스에 없는 메소드나 특정 구현 클래스)에 의존하지 않아야 합니다.
+ * 추상화하는 메소드가 primitive 타입이나 인터페이스를 받아 구현하는것은 괜찮지만 특정 구현 클래스를 받는 것은 지양해야 합니다.
+
+#### SRP: Single Responsibility (단일 책임의 원칙)
+
+> 하나의 객체는 하나의 책임(기능, 역할)만을 가져야합니다.
+> 객체만이 아니라 모듈이나 함수를 구현할 때에도 마찬가지입니다.
+> 한 객체가 하나의 메소드만 가져야만 한다는 것은 아닙니다.
+
+단일 책임의 원칙은 하나의 객체 또는 함수에서 하나의 역할만을 하도록 구현해야 한다는 원칙입니다. 클래스를 통해 개발할 때 주로 발생하는 문제는 하나의 객체가 너무 커진다는 것입니다. 주로 기능을 추가하다보면 필요한
+기능을 하나 둘씩 (편의를 위해) 기존에 있는 객체에 메소드를 추가하게 되고 점점 객체는 특정 하나의 역할만을 하는 것이 아닌 모든 것에 다재다능한 슈퍼 객체가 되어버립니다.
+> 이때 메소드를 늘리는 것이 아닌 객체를 분리할 생각을 해야합니다.
+
+```java
+class SuperDataViewer {
+ // 비슷한 작업의 메소드만 늘어가면서 객체가 비대해짐
+ // 주로 이미 구현한 클래스의 필드 값이나 private 메소드를 재활용하기 위해
+ // 편의상 메소드를 늘리는 경우가 많습니다.
+ public void viewHtml() {
+ ...
+ }
+
+ public void viewMarkDown() {
+ ...
+ }
+
+ public void viewCSV() {
+ ...
+ }
+
+ // Viewer에서는 보여주는 작업만 하고 update는 Viewer를 사용하는 클래스에서 구현하는 것이 옳음
+ public void updateView() {
+ ...
+ }
+}
+
+interface DataViewer {
+ void view();
+}
+
+class HTMLViewer implements DataViewer {
+ @Override
+ public void view() {
+ ...
+ }
+}
+```
+
+이것은 사실 객체지향적으로 프로그래밍한 코드라고 보기 어렵습니다. 객체 안에 절차적 프로그래밍의 함수를 때려넣은 것 뿐이죠. 주로 주의할 부분은 하나의 객체에서 비슷한 기능을 하는 작업을 메소드만 늘려서 구현하거나
+하나의 클래스의 역할을 너무 크게 생각해 너무 많은 기능을 넣은 경우입니다.
+
+위의 예에서는 각각 다른 데이터를 보여주는 상황으로 `HTMLViewer`, `MardownViewer`, `CSVViewer`와 같은 방식으로 각각 다른 클래스에서 `view`함수를 구현하는 방식으로 구현하는 것이
+더 좋아보입니다.
+
+그리고 `updateView`와 같은 `view`를 다루는 상위 작업은 `DataViewer`를 사용하는 클래스에서 구현해두면
+`DataViewer`는 `view`라는 작업에만 집중할 수 있어 역할이 더 명확해집니다.
+`update`기능 구현이 `DataViewer`의 클래스에 있는게 구현이 조금 더 편하다는 이유로 분리되지 않는다면 점점 거대해진 모든 역할을 하는 클래스가 추가될 수 있습니다.
+
+#### OCP: Open-Closed (개방 폐쇄의 원칙)
+
+> 기능 확장에는 열려있고, 수정(구현 코드)에는 닫혀있어야합니다.
+> 기능 추가는 쉽게 할 수 있지만 기존 코드는 건드리지 않아야 합니다.
+
+개방 폐쇄의 원칙은 기능을 추가하더라도 기존 코드를 건드리지 않아야한다는 원칙입니다. 새로운 기능을 추가한다는 이유로 기존 코드를 건드리게 되면 멀쩡히 동작하던 기능이 갑자기 동작하지 않을 수도 있습니다.
+
+```java
+class RealTimeDataViewer {
+ private final DataViewer dataViewer;
+
+ RealTimeDataViewer(DataViewer dataViewer) {
+ this.dataViewer = dataViewer;
+ }
+
+ void updateView() {
+ ...
+ this.dataViewer.view();
+ }
+}
+
+interface DataViewer {
+ void view();
+}
+
+class HTMLViewer implements DataViewer {
+ @Override
+ public void view() {
+ ...
+ }
+}
+
+class MarkDownViewer implements DataViewer {
+ @Override
+ public void view() {
+ ...
+ }
+}
+```
+
+만약 MarkDown을 보여주는 viewer를 제공하기 위해 기존 구현을 건드린다면 객체가 너무 비대해져 코드를 읽기 어렵거나 기존 코드를 건드리면서 예상치 못한 버그를 발생시킬 수 있습니다. 애초부터 객체를 새로
+만들어 기능을 구현하고 새로운 기능이 필요할 때에 생성한 객체를 사용하면 됩니다.
+
+새로 클래스를 구현해서 사용하면 당연히 기존 코드의 수정에 닫혀있겠지만 그것만으로는 확장에 문제가 발생합니다. 개방 폐쇄의 원칙은 단순히 클래스만 따로 만들라는 것이 아닙니다. 클래스를 따로 만들지만 같은
+인터페이스를 `implements`함으로써 사용하는 곳에서는 다른 구현 클래스만 넘겨주어 구현 클래스에 따른 기능을 사용할 수 있게 하라는 것입니다.
+
+Java에서 `List` 인터페이스가 있지만 원하는 상황에 따라 다른 성능을 위해 `List list = new ArrayList<>();`처럼 각기 다른 `ArrayList`
+, `LinkedList`를 사용하는 상황과 비슷합니다. 원하는 `List` 형태가 있다면 직접 구현해서 기능을 확장할 수 있지만 기존에 있는 다른 형태의 `List` 구현에 전혀 영향을 주지 않죠.
+
+> 인터페이스 설계부터 잘 고려해서 작성해야 개방 폐쇄의 원칙을 지킬 수 있게 됩니다.
+
+#### LSP: Liskov Substitution (리스코프 치환의 원칙)
+
+> 부모 클래스의 인스턴스 위치에 자식 클래스를 두더라도 문제가 없어야 합니다.
+
+보통 클래스를 구현할 때 구현한 클래스는 `어떤 조건을 만족해야한다`는 규칙을 두는 경우가 많습니다. 예를 들어 Queue는 `먼저 들어온(push) 값이 먼저 내보내져야(pop) 하는 규칙(FIFO)`을 가집니다.
+그에 반해 Stack은 `최근에 들어온(push) 값이 먼저 내보내져야(pop)하는 규칙(LIFO)`을 가집니다.
+
+여기서 Queue와 Stack은 사용할 때 `push`와 `pop`을 사용하는 공통점을 가지고 있고, 구현도 Queue와 동일하게 `push`하고 `pop`만 역순으로 꺼내면 되기 때문에 Queue를 부모 클래스로
+두고 `pop`만 override해서 Stack을 구현하고 싶은 유혹을 받습니다.
+
+하지만 리스코프 치환 원칙은 이런 방식으로 구현하면 안된다고 말합니다.
+
+```java
+Queue queue = new Stack();
+queue.push(1);
+queue.push(2);
+
+queue.pop(); // 구현이 stack이기 때문에 2가 반환됨
+```
+
+만약 리스코프 치환 원칙을 위반한다면 위와 같은 상황이 발생합니다.
+
+만약 상위 클래스를 상속받은 자식 클래스를 구현한다면 `자식 클래스에서는 상위 클래스의 규칙을 반드시 지켜야합니다`. 규칙을 지키지 않더라도 구현해 실행시킬 수는 있기 때문에 리스코프 치환 원칙을 자주 어기곤
+합니다.
+
+하지만 리스코프 치환 원칙을 지키지 않는다면 프로그래밍한 코드가 예상치 못한 결과를 내보낼 수 있습니다. 사실 우리들은 단순히 굴러가는 코드를 짜는 것이 아니라 좋은 코드를 작성하는 것이 목표이기 때문에 이런 원칙을
+생각해서 구현해야합니다.
+
+위의 Queue의 예시를 생각해보면 사실 Queue와 Stack은 기반으로 두는 규칙이 다르기 때문에(`FIFO와 LIFO`)
+둘은 서로 상속관계를 가지면 안됩니다. Java에서도 Queue는 인터페이스로 두고 있지만 Stack은 Queue가 아닌 Vector를 상속받는 구조로 되어있습니다.
+
+#### ISP: Interface Segregation (인터페이스 분리의 원칙)
+
+> 클래스 내에서 사용하지 않을 인터페이스(메소드)는 구현하지 않아야 합니다.
+> 인터페이스를 만들 때부터 최소한의 메소드만을 갖도록 합니다.
+
+```java
+interface IO {
+ int write(byte[] b);
+ byte[] read();
+}
+
+class FileReader implements IO {
+ @Override
+ public int write(byte[] b) {
+ return 0;
+ }
+
+ @Override
+ public byte[] read() {
+ ...
+ return b;
+ }
+}
+```
+
+인터페이스 분리 법칙은 가능한 한 인터페이스를 분리해 설계하라는 원칙입니다. 위의 예시를 보면 `IO` 인터페이스에서 read와 write를 모두 가지고 있습니다. read와 write는 비슷한 역할(IO)을 하기
+때문에 주로 묶어서 생각하지만 구현에서는 read와 write의 기능은 매우 다릅니다. 그렇기 때문에 주로 reader나 writer를 따로 구현합니다.
+
+하지만 위처럼 IO를 묶어 생각하게 되면 Reader에서 write 기능을 구현하지 않고 read만을 구현하거나 Writer에서 read 기능을 구현하지 않고 write만을 구현하는 경우가 생깁니다.
+
+하지만 인터페이스를 사용하는 목적을 생각해보면 위와 같은 구현은 좋지 않습니다.
+> 인터페이스의 메소드를 호출할 때 우리는 입력된 구현 클래스의 메소드가 구현되어있지 않을 것을 생각하지는 않습니다.
+
+인터페이스에서의 메소드는 모든 구현 클래스에서 제대로 구현해야합니다. 만약 인터페이스의 구현을 클래스에서 구현하지 않는 경우가 있다면 인터페이스가 너무 큰 기능을 가지고 있는 것이 아닌지 생각해봐야 합니다.
+
+```java
+IO io = new FileReader();
+
+class Logger {
+ void output(writer IO) {
+ writer.write(output); // FileReader에서는 제대로 동작하지 않음
+ }
+}
+```
+
+위와 같은 IO 인터페이스를 가지고 Logger에서 구현하고 있다고 가정해봅시다. 로거에서 로깅을 위한 output을 여러 IO를 통해 로깅 내용을 작성하도록 구현할 수 있습니다. 하지만 `FileReader`는
+IO를 구현했지만 내부 구현으로 write를 제대로 구현하지 않았기 때문에 IO를 인자로 전송하지만 제대로 IO의 기능을 하지 않는 것을 볼 수 있습니다.
+
+이것이 인터페이스가 분리되지 않을 때 생기는 문제입니다.
+
+인터페이스는 가능한 기능의 원자단위로 나누어 설계하고 클래스는 모든 기능을 구현해야 합니다. 구현에서는 반드시 인터페이스에서 설계된 기능대로 구현하는 것을 가정해야 추상화된 기능이 망가지지 않습니다.
+
+#### DIP: Dependency Inversion (의존 역전의 원칙)
+
+> 상위 모듈은 하위 모듈에 의존하지 않고 추상화가 세부 구현(구현 클래스의 메소드나 필드)에 의존하지 않아야 합니다.
+
+객체지향적으로 개발할 때 반드시 기억해두어야 할 원칙입니다. 우리가 함수나 클래스를 사용하는 이유는 코드를 재활용하기 위해서입니다. 그런데 만약 함수에서 인자로 받는 값이 정해진 클래스의 필드나 함수에만 의존한다면
+어떻게 될까요?
+
+```java
+class AudioCaller {
+ public void call() {
+ ...
+ }
+}
+
+class VideoCaller {
+ public void call() {
+ ...
+ }
+}
+
+class Phone {
+ public void videoCall(VideoCaller caller) {
+ caller.call();
+ }
+
+ public void audioCall(AudioCaller caller) {
+ caller.call();
+ }
+}
+
+...
+Phone phone = new Phone();
+phone.videoCall(new VideoCaller()); // 전화할 때 영상통화
+phone.videoCall(new AudioCaller()); // 타입 에러
+phone.audioCall(new AudioCaller()); // 전화할 때 음성통화
+phone.audioCall(new VideoCaller()); // 타입 에러
+```
+
+음성 통화와 영상통화 기능을 모두 제공하는 `Phone` 클래스를 구현한다고 가정해봅시다. 음성통화와 영상통화는 내부적인 구현이 달라지기 때문에 각각 클래스로 구현하고 `Phone` 클래스에서 불러와서 호출하도록
+구현할 수 있습니다.
+
+복잡한 기능을 분리하기 위해 클래스를 분리했지만 위의 예시에서는 분리한 클래스의 기능을 위해 `Phone` 클래스에 각각 함수를 호출하는 함수가 늘어나는 문제가 발생합니다. `videoCall`
+과 `audioCall` 함수에서 프로그래밍 언어적으로는 전혀 다른 클래스를 인자로 받기 때문에 함수를 다르게 두는 것 외에는 방법이 없습니다.
+
+그런데 정말 이 방법이 옳은 방법일까요? 현재는 음성통화, 영상통화만 있지만 최근 많이 사용하는 ZOOM이나 google meet과 같은 그룹회의, 카카오톡과 같은 채팅을 모두 같은 기능으로 묶을 수 있다고
+가정해봅시다. 그렇다면 새로운 기능을 구현하는 클래스가 나올 때마다
+`Phone` 클래스에서 새로운 함수가 나와야 한다는 말입니다. 이 방법은 Open-Closed 원칙에 위배되는 것으로 보입니다.
+
+이럴 때 필요한 것이 인터페이스입니다.
+
+```java
+interface Calller {
+ void call();
+}
+
+// 각각 달라지는 구현을 구현 클래스에 구현
+class AudioCaller implements Calller {
+ ...
+}
+
+// 각각 달라지는 구현을 구현 클래스에 구현
+class VideoCaller implements Calller {
+ ...
+}
+
+class Phone {
+ ...
+ // call 함수 하나로 모든 작업을 만족
+ public void call(Caller caller) {
+ caller.call();
+ }
+}
+
+...
+
+Phone phone = new Phone();
+phone.call(new VideoCaller()); // 전화할 때 영상통화
+phone.call(new AudioCaller()); // 전화할 때 음성통화
+```
+
+앞서 말한 문제를 해결하기 위해서는 함수 인자나 클래스의 필드로 객체를 받을 때 인터페이스를 받아야 합니다. 프로그래밍 언어에서는 각 클래스들은 전혀 다른 타입입니다. 하지만 그 클래스들을 같은 `interface`
+로 묶을 수 있다면 같은 타입으로 취급할 수가 있어집니다.
+
+클래스와 인터페이스를 활용하면 타입을 하나로 묶어주도록 구현할 수 있습니다. 하지만 반대로 생각해보면 인터페이스로 타입을 넘겨두고 각 클래스에서 구현을 한다면 하나의 인자에서 여러 구현을 받을 수 있는 것입니다.
+심지어 공통으로 사용할 인터페이스를 함수 인자나 클래스의 필드로 받는다면 각 클래스의 세부 구현을 사용하는 메소드에서는 전혀 알지 않아도 됩니다.
+
+### 객체지향 프로그래밍의 적용
+
+사실 객체지향 패러다임을 적용하는 것이 처음에는 어려울 수도 있습니다. 개발할 때 우리가 해결하려는 문제를 전체 구조에서부터 내려다 볼 수 있어야하기 때문입니다. 알고리즘 풀듯이 코드를 구현하는 바텀업 방식으로는
+어떻게 인터페이스를 묶어야 좋을 지는 크게 고려하지 않기 때문입니다.
+
+하지만 객체지향 프로그래밍의 핵심은 단순히 `객체로 구현한다`가 아닌 `객체를 잘 활용한다`이기 때문에 이런 원리들을 잘 지켜 좋은 코드를 작성했을 때 비로소 객체지향적인 프로그래밍을 했다고 할 수 있습니다.
+
+> 단순히 클래스를 사용한다고 객체지향적이지는 않습니다.
+> 인터페이스를 잘 분리하고 활용하는게 객체지향의 핵심으로 보입니다.
+
+### 객체지향 프로그래밍의 문제는?
+
+너무 객체지향적으로 개발하는 것이 최고라는 글이 되는 것 같아 잠시 밸런스를 맞추겠습니다.
+
+객체지향 프로그래밍은 기본적으로 객체 내부에 `상태`를 두어 `어떻게 상태를 잘 변경할 것인지`를 다루는 방법입니다. 하지만 상태를 변경할 때 여러 문제가 발생합니다.
+
+첫째로는 단일 코어가 아닌 멀티코어 상황에서 개발을 하다보니 하나의 메모리를 동시에 여러 스레드에서 접근하는 경우가 발생합니다. 물론 이런 문제를 해결하기 위한 `atomic`연산과 `mutex` 등의 방법들이
+있지만 종종 실수가 발생하기도 합니다. 예를 들어 `mutex`로 값을 원자성 있게 변경했지만 반환한 값에 reference가 있어 메모리에 동시 접근이 되는 경우가 발생하기도 합니다.
+
+여기서 한가지 더 문제가 되는 것은 `atomic`연산과 `mutex`를 사용하면 병렬처리하는 효율이 떨어질 수 있다는 것입니다. 여러 스레드에서 계산하지만 결국 하나의 메모리를 접근할 때 병목이 발생하기 때문에
+특정 메모리를 접근할 때는 단일 스레드의 효율만 나오기도 합니다.
+
+두번째 문제로는 `변경될 수 있는 상태`를 가진 객체에서 발생한 버그는 원인을 분석하기 어렵다는 것입니다. 개발 과정에서는 초기 상태의 객체를 테스트하는데 여러 동작을 거친 객체에서는 메모리가 남아있거나 이상 동작을
+하는 경우가 종종 발생합니다. 하지만 이 과정에서 에러를 뱉은 코드는 정작 에러를 발생시킨 원인이 아닌 경우가 많습니다.
+
+물론 객체를 작게 만들고 역할을 분리해두어 구현했다면 그나마 낫습니다. 하지만 비즈니스 로직이 추가되거나 변경되어 객체가 점점 커지면서 문제가 발생합니다. 사실 이 부분은 객체지향 프로그래밍의
+문제라기보다는 `변경될 수 있는 상태(mutable state)`의 문제입니다. 객체지향 프로그래밍 방법은 이 문제를 없애기보다는 완화시키는 프로그래밍 방법이기 때문에 여전히 문제가 남아있습니다. 만약 이 문제를
+해결하려면 시스템 내에서 `변경될 수 있는 상태`를 저장하는 구조가 아예 없어야 합니다.
+
+## 마치며
+
+물론 `외부에서만 입력을 받아 실행하는` (값도 외부에서 저장되어있는) 프로그램도 있지만 그렇지 못한 경우도 존재합니다.
+하지만 상태를 저장하고 있어야하는 상황에서는 객체지향 프로그래밍은 굉장히 좋은 패러다임이 됩니다.
+상태를 변경하는 포인트를 객체 내부로 최소화시켜 비교적 분석하기 편해진다는 사실을 알아야합니다.
+
+다음 장에서는 상태와 가변 데이터를 지양하는 함수형 프로그래밍에 대해 정리해보겠습니다.
+
+## Reference
+
+* [http://www.incodom.kr/객체지향](http://www.incodom.kr/%EA%B0%9D%EC%B2%B4_%EC%A7%80%ED%96%A5)
+* [https://velog.io/@phs880623/객치-지향-프로그래밍](https://velog.io/@phs880623/%EA%B0%9D%EC%B9%98-%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D)
+* [https://coding-factory.tistory.com/328](https://coding-factory.tistory.com/328)
+* [https://www.digitalocean.com/community/conceptual_articles/s-o-l-i-d-the-first-five-principles-of-object-oriented-design](https://www.digitalocean.com/community/conceptual_articles/s-o-l-i-d-the-first-five-principles-of-object-oriented-design)
diff --git a/_posts/2022-03-17-TWL-02-2-FP.md b/_posts/2022-03-17-TWL-02-2-FP.md
new file mode 100644
index 0000000..d16f959
--- /dev/null
+++ b/_posts/2022-03-17-TWL-02-2-FP.md
@@ -0,0 +1,243 @@
+---
+layout: post
+title: TWL-02 두번째. 함수지향 프로그래밍
+subtitle: 함수지향 프로그래밍에 대해 알아봅니다.
+categories: [TWL, CS]
+tags: [TWL, CS, FP]
+---
+
+프로그래밍을 하다보면 간혹 내가 예상한 값과 전혀 다른 결과가 나오곤 합니다. 왜 그런걸까요?
+당연하게도 컴퓨터는 거짓말을 하지 않으니 제가 코드를 잘못 짠 탓일겁니다.
+
+개발 도중에 발생하는 문제는 실행시킨 후 얼마 안된 상태를 저장하고 있기 때문에 해결하기가 쉽습니다.
+개발 도중에 발생한 문제는 프로그램을 재실행했을 때 금방 같은 문제가 재현되어 원인을 파악하기 쉽기 때문입니다.
+
+
+
+
+
+원인을 제거하지 않으면 결국 뚫립니다.
+{: style="text-align: center; color: gray; margin-top:.5em;"}
+
+하지만 한참을 돌던 프로그램이 갑자기 문제가 발생하면 원인 파악이 어렵습니다.
+문제의 원인이 되는 코드를 찾지 못해 일단 프로그램에서 에러가 발생하지 않도록 땜빵 코드만 작성하기도 합니다.
+하지만 땜빵한 코드는 결국 다른 곳에서 다시 에러를 발생시키기 때문에 임기응변에 그치는 경우가 많습니다.
+> 2,3일동안 켜져있던 브라우저가 갑자기 종료되면 사용하던 사람도 무엇이 원인인지 알기 어렵습니다.
+
+어디가 문제인지 확인하기 위해 로그를 작성해 두었더라도 저장된 상태가 왜 이렇게 변경되었는지는 모든 로그를 보지 않는 한 찾기 어렵습니다.
+하지만 저장되어있는 값에 따라 함수는 인자의 조건에 따라 문제가 없어보이던 코드도 문제가 발생할 수 있는 코드가 됩니다.
+심지어 단순한 나누기 연산도 나누는 값에 따라 프로그램 전체에 panic을 줄 수도 있습니다. (`divide by zero`)
+
+그런 점에서 상태 값은 문제가 될 수 있습니다. 정확히는 변경 가능한 상태 값이 문제가 됩니다.
+함수에 같은 변수 값을 인자로 입력하더라도 상태 값에 따라 결과값이 매번 변경될 수도 있습니다.
+
+어떤 방법에 문제가 있을 때 해결하기 위한 방법은 크게 두가지가 있습니다.
+하나는 문제가 발생하더라도 컨트롤할 수 있도록 관리하는 방법이고,
+다른 하나는 문제의 원인을 제거하는 방법입니다.
+
+잘 컨트롤할 수 있도록 하는 방법이 객체지향 프로그래밍, 문제의 원인을 제거하는 방법이 함수형 프로그래밍입니다.
+
+# 함수형 프로그래밍?
+> 절차적 프로그래밍의 함수와는 함수의 `급`이 다르다.
+> 함수형 프로그래밍은 순수함수와 불변값을 사용해 소프트웨어를 만드는 기법입니다.
+
+객체지향 프로그래밍은 그 이름대로 객체를 중심으로 생각해 개발합니다. 함수형 프로그래밍도 이름에서 알 수 있듯이 함수를 중심으로 생각해 개발하는 방법입니다.
+객체지향 프로그래밍이 단순히 객체를 쓰기만 하면 객체지향인 것이 아닌 것처럼, 함수형 프로그래밍도 단순히 함수를 사용한다고 함수형 프로그래밍은 아닙니다.
+절차적 프로그래밍에서 사용하는 함수와는 `급`이 다릅니다.
+
+절차적 프로그래밍에서도 함수를 정의하고 호출할 수 있지만 함수를 인자로 넘기거나 함수 내에서 함수를 생성하지 못합니다.
+사실 함수를 인자로 넘기거나 함수 내에서 함수를 생성하는 것은 함수형 프로그래밍에서 특별한 것이 아닙니다.
+객체지향 프로그래밍에서 필드값과 함수를 객체 내에서만 접근할 수 있도록 하고 인터페이스를 통해 메소드를 구현하는 것이 당연하듯이
+함수형 프로그래밍에서도 당연하게 함수를 인자로 넘기고 함수 내에서 함수를 생성할 수 있습니다.
+
+이 차이가 객체지향 프로그래밍과 함수형 프로그래밍의 차이를 만들어냅니다.
+객체지향 프로그래밍에서는 함수의 인자나 객체의 필드로 `동작을 가지고 있는 객체`를 넘겨 함수 내부에서 동작을 호출하도록 만들었다면
+함수형 프로그래밍에서는 함수의 인자로 `동작` 자체를 넘겨 조합해 실행합니다.
+
+## 선언형 언어, 명령형 언어
+
+먼저 짚고 넘어가야할 것이 함수형 프로그래밍은 객체지향 프로그래밍과 절차적 프로그래밍과는 구조가 다릅니다.
+
+
+
+
+
+선언형 언어로 대표적인 함수형 프로그래밍과 명령형 언어로 대표적인 객체지향 프로그래밍
+{: style="text-align: center; color: gray; margin-top:.5em;"}
+
+언어를 두가지로 나눠본다면 명령형 언어와 선언형 언어로 나눌 수 있습니다.
+먼저 명령형 언어는 우리가 일반적으로 사용하는 프로그래밍 언어처럼 어떻게 동작해야하는지를 작성하는 언어입니다.
+반대로 선언형 언어는 무엇을 표현하는 것인지를 작성하는 언어입니다.
+
+명령형 언어만이 프로그래밍 방법은 아니지만 우리가 주로 사용하는 프로그래밍 언어들은 대부분 명령형 언어를 사용중입니다.
+어셈블리 같은 기계 코드에서도 연산들이 명령형으로 작성되어있기 때문에 그것을 기반으로 만들어지는 프로그래밍 언어들에서 명령형
+언어로 작성되는 것이 당연해보입니다.
+
+
+
+
+
+HTML은 프로그래밍 언어는 아닙니다...만! 선언형 언어입니다.
+{: style="text-align: center; color: gray; margin-top:.5em;"}
+
+그에 반해 선언형 언어로 주로 알려진 언어들은 `HTML`과 `SQL`입니다. `어떤 작업을 해야한다` 보다는 `무엇을 보여준다`에 가깝습니다.
+HTML는 헤더, 각 컴포넌트들을 통해 무엇을 화면에 보여줄 지를 표현하는 언어이고, SQL도 DB에서 읽어오는 작업 때문에
+`어떻게 동작해야한다.`로 보이기도 하지만 `테이블 중에서 무엇을 보여준다`에 가깝습니다.
+
+그렇다면 함수형 프로그래밍 언어는 `무엇을 보여준다`를 통해 어떻게 개발하는 걸까요?
+
+### 함수형 언어?
+
+기존 언어들에서는 `함수가 무슨 동작을 하는지` 정의하고 호출할 때 정의된 동작들을 실행했습니다.
+함수형 프로그래밍에서는 동작을 정의하기보다는 `함수가 무엇인지` 정의하고 호출할 때 인자들을 정의된 값에 대입한다고 생각하면 편합니다.
+
+```python
+def fibo(n):
+ res = 1
+ while n > 1:
+ res *= n
+ n -= 1
+ return res
+```
+```haskell
+fibo n
+ | n < 2 = 1
+ | otherwise = fibo(n-2) + fibo(n-1)
+```
+
+파이썬과 하스켈에서의 피보나치
+{: style="text-align: center; color: gray; margin-top:.5em;"}
+
+예를 들어 위의 파이썬 코드로 피보나치를 구현하면 입력 인자 n 을 받아 반복문을 돌면서 `res`값에 n을 곱하고 n을 1 감소시키는 동작을 반복하도록
+구현할 수 있습니다. (물론 파이썬에서도 재귀로 작성할 수 있지만 함수형과의 차이를 더 크게 보여주기 위해)
+하스켈에서의 코드는 n의 값에 따라 `동작할 명령어의 순서`를 정의하기보다는 n 값에 따라 함수의 결과에 `대입될 값`을 정의합니다.
+n이 2보다 작을 때는 1을 대입하고 그 외에는 `fibo(n-2) + fibo(n-1)`을 대입하게 됩니다.
+
+명령형 프로그래밍에서는 함수를 `실행해야할 동작들`로 보고 호출을 `실행한 결과를 반환하는 연산`으로 보지만
+함수형 프로그래밍에서는 함수를 `인자에 따른 값`으로 보고 호출을 `인자가 입력되었을 때에 표현되는 값`으로 봅니다.
+한가지 더 중요한 것은 함수에 인자를 넣을 때에서야 **lazy**하게 값이 **평가**된다는 것입니다.
+
+```python
+def add5(a):
+ return add(5, a)
+
+def add(a, b):
+ return a + b
+
+>>> add5(3)
+8
+```
+```haskell
+Prelude> add a b = a + b
+Prelude> add5 = add 5
+Prelude> add5 3
+8
+```
+
+여기서 얻을 수 있는 장점은 함수에서 함수를 만들어내기 편하다는 점입니다. 함수를 lazy하게 평가되는 값으로 보기 때문에
+함수에서 함수를 만들어내기 쉽습니다. lazy하게 평가되는 다른 값을 만들어내는 것이기 때문입니다.
+
+위의 코드는 비슷하게 보이지만 동작은 꽤 차이가 있습니다. 두 코드 모두 `add5`를 호출해 계산하고 있는데,
+파이썬 코드에서는 `add5`함수가 호출될 때, `add` 함수에 5와 `a`값을 입력해 호출하도록 동작합니다.
+하지만 하스켈에서는 `add5`에 `add` 함수에 5를 넣어 새로운 함수를 만듭니다.
+그 후에 새로운 함수 `add5`에 3을 대입해서 계산을 합니다.
+
+물론 하스켈과 똑같이 동작하도록 만들수도 있습니다.
+
+```python
+def add(a):
+ def _add(b):
+ return a + b
+ return _add
+
+>>> add5 = add(5)
+>>> add5(3)
+8
+
+# 하지만 add를 사용할 때는 아래처럼 사용해야 합니다.
+>>> add(5)(3)
+```
+
+파이썬으로 하스켈과 비슷하게 동작하도록 작성한 코드를 보면 더 쉽게 이해할 수 있습니다.
+위의 `add`함수를 보면 실제로 연산이 실행되는것은 `a`와 `b` 모두 입력되었을 때입니다.
+
+이것이 바로 lazy한 연산입니다. 실제로 값이 모두 주어졌을 때 계산하는 것입니다.
+
+기존 프로그래밍 언어의 시각으로 생각해보면 `add`에 `a` 인자를 넘기면 `a`라는 상태가 **불변으로 저장**되고,
+이후에 `b`를 입력받아 + 연산을 한다고 이해할 수 있습니다.
+
+## 함수형 프로그래밍의 특징
+
+함수형 프로그래밍의 특징으로는 주로 아래 특징들이 꼽힙니다.
+
+* 순수 함수
+ * 함수의 `결과가 파라미터에만 결정`되고 어떠한 `side effect도 일으키지 않는` 함수
+ * 함수를 실행할 때 `외부의 값을 접근하거나 수정`하지 않습니다. (이렇게 개발해야 좋다 라는 의미입니다)
+ * side effect가 있는 함수는 비순수 함수라고 합니다.
+* 참조 투명성
+ * 인자가 같은 함수 호출을 함수의 결과로 대체할 수 있는 성질
+ * 순수 함수로 구현한다면 함수 결과로 대체하더라도 문제가 없습니다.
+* 불변 데이터
+ * 인자로 입력한 데이터의 값을 변경시키지 않음
+ * 함수를 실행할 때 `파라미터의 값을 변경하지 않음`
+* 일급 함수
+ * 함수를 파라미터로 사용하거나 반환값으로 사용하는 변수처럼 생각할 수 있는 특징
+* 지연 평가
+ * 계산 결과가 필요할 때까지 연산을 늦추는 방법
+
+명령형 프로그래밍 언어를 주로 사용하던 우리들에게 `상태를 변경하지 않고` 어떻게 구현할 지 감이 잘 오지 않습니다.
+
+하지만 흔하게 사용하는 선언형 언어인 HTML을 생각해보면 `상태를 변경하지 않는 것`이 오히려 당연합니다.
+HTML 코드에서 각 element가 내부 element 값을 변경한다는 것은 상상하기 어렵습니다.
+또한 `element 구조가 같다면 항상 같은 모습의 화면`을 보여줄 것입니다.
+만약 element 구조가 같은데 외부적인 요소에 의해서 HTML 구조가 달라진다면 화면을 구성하기 더 어려울 것입니다.
+내부 값을 변경하는 대신 `여러 element를 조합해서 전체적인 구조`를 만들어냅니다.
+
+함수형 프로그래밍에서도 마찬가지입니다. HTML처럼 `인자로 들어온 상태를 변경하지 않는 것`이 당연합니다.
+함수에 인자로 들어온 `값을 변경하지 않고`, `함수의 인자가 같다면 같은 결과`를 내보내야 합니다.
+그리고 인자나 변수의 값을 변경하는 동작 대신, `인자를 입력했을 때 여러 함수를 조합하여 원하는 값`을 만들어내는 것입니다.
+
+### 순수 함수 (side effect가 없는 함수)와 참조 투명성
+> 순수함수 = 결과값이 인자에 의해서만 결정 + side effect가 없음
+
+순수 함수는 동일한 입력값을 받으면 같은 결과를 내보내고, 함수를 실행했을 때 값이 달라지는 `side effect`가 없는 함수입니다.
+`side effect`란 값을 변경하는 행위입니다. 실제로 변수 값이 변경되지 않더라도 print를 찍어보거나 로그를 찍는 행위, 파일에 저장하는 행위는
+외부의 값을 변경하기 때문에 `side effect`입니다. 또한 변수의 값을 assign 하는 행위, `set` 함수를 통해 reference의 값을 변경하는
+행위 또한 `side effect`라고 할 수 있습니다.
+
+객체지향 프로그래밍에서는 이런 `side effect`를 객체의 메소드에서만 수행하도록 만들어 `side effect`는 발생하더라도 어디서 문제가 발생한 것인지
+비교적 쉽게 만들어줍니다. 하지만 함수형 프로그래밍에서는 애초부터 `side effect`를 허용하지 않는 순수함수를 사용하기를 권장합니다.
+
+순수함수는 입력을 넣었을때 결과가 달라지도록 하는 어떠한 `side effect`도 발생시키지 않기 때문에 다음에 같은 인자를 넘긴다면
+같은 결과를 내보냅니다. 참조 투명성은 이런 순수함수에서 함수를 다시 실행시키지 않고 결과값을 그대로 대입해도 같은 결과가 됨을 나타내는 성질입니다.
+함수형 언어에서는 이 성질을 이용해 같은 결과를 여러번 이용해야하는 재귀 함수를 최적화하기도 합니다.
+
+### 불변 데이터
+
+불변 데이터는 말 그대로 변하지 않는 값입니다. 예를 들어 자바에서 `final`과 같은 키워드를 볼 수 있습니다.
+값이 이후에 더이상 변경되지 않도록 보장하는 키워드입니다.
+
+사실 객체지향 프로그래밍의 `private`에 해당하는 작업이 불변 데이터입니다. `private` 변수는 객체 내부에서만
+값을 변경할 수 있도록 하지만 함수형 프로그래밍은 값을 아예 변경하지 못하도록 권장합니다.
+
+### 일급함수
+
+일급함수는 함수
+
+## 함수형 프로그래밍의 기법들
+
+함수형 프로그래밍의 개념만 보아서는 크게 핵심을 이해하기는 어렵습니다.
+
+
+### 고차 함수
+
+### 커링
+
+### Partial Application
+
+### 클로저
+
+
+## Reference
+
+* [https://wiki.haskell.org/Thunk](https://wiki.haskell.org/Thunk)
diff --git a/_posts/2022-03-17-TWL-02-3-OOP-FP.md b/_posts/2022-03-17-TWL-02-3-OOP-FP.md
new file mode 100644
index 0000000..0b1e41a
--- /dev/null
+++ b/_posts/2022-03-17-TWL-02-3-OOP-FP.md
@@ -0,0 +1,35 @@
+---
+layout: post
+title: TWL-02 세번째. 어떻게 프로그래밍해야할까
+subtitle: 객체지향, 함수형 프로그래밍을 어떻게 적용할지 이야기합니다.
+categories: [TWL, CS]
+tags: [TWL, CS, OOP, FP]
+---
+
+사실 객체지향 프로그래밍과 함수형 프로그래밍 모두 좋은 코드를 작성하기 위한 방법론입니다.
+우리의 목적은 객체지향적으로 작성하거나 함수형으로 작성하는것이 아니라 좋은 코드를 작성하는 것이기 때문에
+각 상황에 맞게 더 좋은 방법을 선택하면 됩니다.
+
+# 어떻게 개발해야할까?
+
+객체지향
+
+
+## 동일한 문제를 어떻게 해결하는지
+
+
+##
+
+# 상태를 가지지 않고 개발할 수 있을까?
+
+
+
+
+
+## Reference
+
+* [http://www.incodom.kr/객체지향](http://www.incodom.kr/%EA%B0%9D%EC%B2%B4_%EC%A7%80%ED%96%A5)
+* [https://velog.io/@phs880623/객치-지향-프로그래밍](https://velog.io/@phs880623/%EA%B0%9D%EC%B9%98-%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D)
+* [https://coding-factory.tistory.com/328](https://coding-factory.tistory.com/328)
+* [https://www.digitalocean.com/community/conceptual_articles/s-o-l-i-d-the-first-five-principles-of-object-oriented-design](https://www.digitalocean.com/community/conceptual_articles/s-o-l-i-d-the-first-five-principles-of-object-oriented-design)
+* [https://wiki.haskell.org/Thunk](https://wiki.haskell.org/Thunk)
diff --git a/_posts/2022-03-17-TWL-02-OOP-FP.md b/_posts/2022-03-17-TWL-02-OOP-FP.md
deleted file mode 100644
index e20c6fa..0000000
--- a/_posts/2022-03-17-TWL-02-OOP-FP.md
+++ /dev/null
@@ -1,474 +0,0 @@
----
-layout: post
-title: TWL-02 객체지향과 함수지향
-subtitle: 객체지향, 함수지향에 대해 알아봅니다.
-categories: [TWL, CS]
-tags: [TWL, CS, OOP, FP]
----
-
-# 객체지향 프로그래밍 (OOP)
-> Object Oriented Programming
-
-요즘은 어떤 프로그래밍 언어를 처음 시작하는지 모르겠지만 저는 첫 프로그래밍 언어로 `Java`를 배웠습니다. 커피를 마시면서 만들어서 `Java`라는 이름을 가졌다는 이야기와 VM을 사용하는 **객체지향적
-프로그래밍언어**라는 이야기를 처음 들으며 언어를 시작했고 이후에는 `Python`, `C`, `C++`의 언어들을 배우게 되었습니다.
-(그 학생은 5년 뒤 Java를 쓰지 않고 `Rust`, `Go`를 사용하게 됩니다...)
-
-최근에는 Java를 사용하지는 않지만 객체지향적으로 개발하라는 말은 계속해서 듣고 있습니다. 결국 어떤 언어를 사용하는지보다는 어떻게 개발하는지가 더 중요한 것 같습니다.
-그러면 어떤 장점이 있어서 객체지향적으로 개발하라는 걸까요?
-
-## 객체지향적으로 개발하라?
-
-먼저 객체지향적으로 개발한다는 의미를 알아야 합니다. 객체 지향적으로 개발한다는 것은 실제 세상처럼 객체들의 집합으로 생각하고 프로그래밍한다는 것입니다.
-간단한 예를 들자면 강아지, 책상과 같은 객체에서의 값(나이, 책상서랍 등)과 동작(짖다, 서랍에 넣다 등)을 객체에서 갖도록 해서 좀 더 코드를 이해하기 쉽게 만드는 것입니다.
-보통 우리가 다른 사람에게 무언가 설명할 때 현실에 있는 다른 무엇가를 비유해서 설명하는 것처럼 코드도 객체에 비유해서 쉽게 이해할 수 있도록 하는 것이죠.
-
-개발하다보면 동작이 얼마 없을 때는 문제 없지만 동작이 점점 커지면서 각 동작을 작은 단위로 분리해야하는데,
-이때 어떤 객체에서 무슨 작업을 할 지 이름만으로 쉽게 알 수 있어 이런 방법은 합리적으로 보입니다.
-물론 객체로 감싸는 과정에서 어느정도 오버헤드가 발생하기는 하지만 성능이 너무 중요한 상황이 아니라면
-충분히 감당 가능한 정도입니다. 어느정도 메모리, cpu 낭비를 하더라도 더 읽기 쉽고 생산성 있는 코드가 더 중요한 것입니다.
-(물론 낭비하는 자원이 감당 가능한 레벨이어서 가능한 겁니다)
-
-### 절차적 언어
-> Procedural Programming (프로시저 프로그래밍)
-
-객체 지향적으로 개발하라고 말할 때 주로 비교되는 것은 절차적 프로그래밍(`procedural programming`)이었지만
-최근에는 함수형 프로그래밍(`functional programming`)이 주로 비교되는 것 같습니다.
-> `procedural programming`은 사실 프로시저(함수) 프로그래밍에 가깝지만 (직역한) 번역명으로 더 잘 알려진 절차적 프로그래밍이라고 적겠습니다
->
-> `oriented`가 없는데 절차 "지향"적 언어라고는 못 적겠습니다.
-
-먼저 절차적 프로그래밍부터 알아보면 **"코드를 순서대로 작성하고 실행하는 언어"** 입니다.
-사실 객체지향적으로 개발하면서도 내부적으로는 어떠한 절차를 거쳐서 개발하기 때문에 완전히 다른 개발 방법이라고 하기에는 문제가 있습니다.
-객체지향적 프로그래밍 방법과 절차적 프로그래밍의 가장 큰 차이점은 데이터와 함수가 어떻게 묶이는지 입니다.
-
-```c
-void move(int* x, int* y, int move_x, int move_y) {
- *x = *x + move_x;
- *y = *y + move_y;
-}
-
-int main() {
- int my_x = 0;
- int my_y = 0;
- // 내 위치를 (2, 4) 만큼 이동한다.
- move(&my_x, &my_y, 2, 4);
- return 0;
-}
-```
-
-절차적 언어인 c 언어에서는 함수가 정해져있고 데이터를 입력으로 보냅니다.
-{: style="text-align: center; color: gray; margin-top:.5em;"}
-
-절차적 언어로 가장 유명하고 현재도 많이 사용되는 c 언어로 예를 들어보면 어떠한 동작을 하는 함수를 구현하고,
-함수에 입력으로 값이나 포인터를 넘겨 연산을 순서대로 실행하도록 개발합니다.
-
-#### 추상화
-> 모든 프로그래밍 방법론은 추상화에서 시작된다.
-
-절차적 언어인 c에서는 추상화가 없다고 할 수는 없습니다.
-추상화라는 것은 구체적으로 작업을 표현하는 것이 아닌 핵심적인 개념만을 표현하는 것입니다.
-위의 코드에서는 `x, y를 조작하는 구체적인 작업`이 아닌 `move라는 핵심적인 동작(함수명)`만으로 사용할 수 있도록 추상화 한것입니다.
-이후에는 어떻게 동작하는지는 함수명과 설명에 대한 신뢰를 가지고 `move` 함수를 호출해 사용할 수 있습니다.
-> 물론 실제 구현이 설명과 다르다면 문제가 발생합니다.
-
-내부적인 구현을 정확히 이해하지 않고도 사용할 수 있다는 점이 구체적인 동작을 그대로 사용하지 않고 추상화하는 이유 중 하나입니다.
-
-```c
-void move(int* x, int* y, int move_x, int move_y) {
- *x = *x + move_x;
- *y = *y + move_y;
-}
-
-void jump(int* x, int* y, int move_x, int move_y) {
- *x = move_x;
- *y = move_y;
-}
-
-int main() {
- int my_x = 0;
- int my_y = 0;
- // move 대신 jump 하도록 수정
- // move(&my_x, &my_y, 2, 4);
- jump(&my_x, &my_y, 2, 4);
- return 0;
-}
-```
-
-추상화된 동작은 대체하기도 쉽다.
-{: style="text-align: center; color: gray; margin-top:.5em;"}
-
-연산을 추상화하게 되면 사용할 때 `전체적인 로직에 대해서 쉽게 이해`할 수 있습니다.
-`main` 안에서 `my_x`와 `my_y`를 단순히 더하거나 빼는 연산보다는
-`move`라는 함수 안에 로직을 넣고 `main`에서는 `move` 함수를 호출하는 것이 어떤 작업을 하는 건지 쉽게 이해할 수 있습니다.
-
-뿐만 아니라 추상화된 작업은 `다른 작업으로 쉽게 변경할 수 있습니다`.
-현재는 코드를 `move`하는 동작으로 사용하고 있지만 만약 `jump`하는 동작으로 수정되어야 한다면 호출하는 함수를 변경하는 것으로
-쉽게 다른 동작으로 변경할 수 있습니다.
-
-비즈니스 동작이 달라지거나 최적화가 필요하거나 혹은 업데이트를 하는 등 여러 이유로 코드는 변경될 가능성을 가지고 있습니다. 그런 점에서
-추상화를 통해 이전 코드를 간단하게 대체할 수 있다는 것은 굉장한 장점입니다.
-
-절차적 언어에서는 함수 단위의 추상화를 제공하고 있습니다. 만약 어셈블리 언어로
-개발했다면 함수를 통해 정해진 연산을 호출하는 작업은 생각하지 못했을 것입니다. 하지만 c언어에서는 구체적인 연산을 매번 사용하지 않고
-함수로 호출할 수 있도록 추상화를 제공하고 있습니다.
-> 절차적 프로그래밍은 함수로 묶는 정도의 수준으로 함수형 프로그래밍과는 다릅니다
-
-어떠한 **"절차"** 에 대한 추상화만을 제공하는 것입니다.
-
-### 객체지향
-> 객체로 모든 것을 생각해 프로그래밍 하는 것
-
-그렇다면 이제 객체지향에서의 추상화에 대해 조금 더 쉽게 이해할 수 있을 것 같습니다. 절차적 프로그래밍에서는 절차를 함수로 추상화했다면 객체지향 프로그래밍에서는
-객체를 추상화합니다. c언어에서 절차에 대한 추상화 기능으로 함수 기능을 제공해 절차적 언어라고 말하는 것처럼,
-객체지향 언어는 객체를 추상화할 수 있도록 기능을 제공하는 언어를 말합니다.
-> c언어에서 객체지향처럼 흉내낼 수는 있지만 객체지향 언어라고 하기에는 기능이 부족합니다.
-
-앞서 설명했던 예시를 객체지향적으로 수정해보겠습니다.
-
-```java
-interface Mover {
- void move(int moveX, int moveY);
-}
-
-class Human implements Mover {
- private int x;
- private int y;
-
- public Human(int x, int y) {
- this.x = x;
- this.y = y;
- }
-
- @Override
- public void move(int moveX, int moveY) {
- this.x += moveX;
- this.y += moveY;
- }
-}
-
-class Jumper implements Mover {
- private int x;
- private int y;
-
- public Jumper(int x, int y) {
- this.x = x;
- this.y = y;
- }
-
- @Override
- public void move(int moveX, int moveY) {
- this.x = moveX;
- this.y = moveY;
- }
-}
-
-public class Main {
- public static void main(String[] args) {
- Mover mover = new Human(0, 0);
- Mover jumper = new Jumper(0, 0);
- mover.move(2, 4);
- jumper.move(2, 4);
- }
-}
-```
-
-main에서는 추상화된 객체의 동작만이 실행된다.
-{: style="text-align: center; color: gray; margin-top:.5em;"}
-
-객체지향 프로그래밍에서는 함수만 추상화하는 것이 아니라 사용되는 값도 함께 객체로 추상화합니다.
-동작은 클래스에서 구현하지만 사용할 때는 인터페이스의 추상화된 함수를 호출합니다. (객체와 동작의 추상화)
-또한 추상화된 함수에서 다룰 값도 함께 객체에서만 접근할 수 있도록 만들어 외부 동작에 의해
-추상화된 로직이 깨지지 않도록 만들 수 있습니다.(캡슐화)
-
-객체에 대해 추상화가 되었기 때문에 이전에 함수를 다른 기능의 함수로 대체했듯이
-다른 작업을 하는 객체로도 대체할 수 있습니다.
-
-### 객체지향의 특징
-
-기본적으로 꼽히는 객체지향의 특징은 다음과 같습니다.
-
-1. 추상화
- * 객체를 추상화해 복잡한 로직이 아닌 객체, 함수를 통해 핵심적인 기능만을 볼 수 있도록 합니다.
-2. 캡슐화
- * 데이터를 객체 내부에 저장해 접근제어자(`private`, `public`)를 통해 외부에서 조작하지 못하도록합니다.
- * 추상화된 로직을 사용할 때 내부 데이터를 외부에서 접근한다면 정해진 로직이 깨질수 있습니다.
-3. 상속
- * 상위 클래스의 필드와 연산을 하위클래스에서 가질 수 있습니다.
- * 최근에는 상속의 문제점들로 인해 상속을 지원하지 않는 언어들이 보이고 있습니다. (`rust`, `go`)
- * 대신 내부 필드로 확장할 객체를 가지는 `composition`으로 구현합니다.
-4. 다형성
- * 같은 이름의 함수를 다양한 타입이나 다양한 구현(`overriding`)으로
- * `overloading`, `overriding`을 이용해 다형성이 발현됩니다.
- * `overriding`하면서 `Dependency Injection(DI)`을 가능하게 하는 기본 성질입니다.
- * 테스트 코드의 mocking 에도 사용되는 성질입니다.
-
-#### 객체지향의 추상화
-
-실생활에서 우리는 여러 전자 기기를 사용하지만 내부적으로 어떻게 동작하는지 모릅니다. 어떻게 돌아갈지 짐작만 하고 있을 뿐이죠.
-대부분 사용설명서를 읽고 원하는 기능을 찾아 기기를 사용합니다. 추상화하는 것도 동일합니다. 우리는 구현할 때와 사용할 때를 구분해서
-생각해야합니다. 절차적 프로그래밍의 함수나 객제지향 프로그래밍의 객체를 구현할 때는 전자기기를 만드는 것과 마찬가지로
-상태가 어떻게 관리되고 어떻게 동작하는 지 알고 있어야 합니다. 하지만 구현된 함수나 객체를 사용할 때는 전자기기를 사용하는 것처럼
-굳이 내부에 대해 모르더라도 사용할 수 있어야 합니다.
-> 사용 설명서를 읽지도 않고 어림짐작으로 사용하던 모습은 api 설명을 안 보고 함수명만 보고 사용하는 모습과 비슷합니다
-
-앞서 말했지만 추상화는 객체지향에서만의 특징은 아닙니다. 절차적 프로그래밍에서도 추상화가 들어있습니다.
-다만 추상화하는 정도나 대상이 다를 뿐입니다.
-
-절차적 프로그래밍에서 코드를 연산 레벨에서 추상화 했다면, 객체지향 프로그래밍에서는 코드를 객체 레벨에서 추상화하게 됩니다.
-절차적 프로그래밍에서는 `어떤 상태를 받아 연산을 하는 함수` 로 추상화하고, 객체지향 프로그래밍에서는 `상태도 연산과 함께 추상화` 해서
-객체에서 관리합니다. 상태를 함께 추상화한다는 것은
-추상화된 인터페이스를 사용할 때 `상태가 어떤 방식으로 관리되는지 모르더라도 사용할 수 있음`을 의미합니다.
-
-예를 들어 절차적 프로그래밍에서 함수를 정의해 연산을 추상화하고, 함수를 호출해서 사용함으로써 내부 연산을 모르더라도 사용할 수 있지만
-입력으로 전달하는 `struct` 값들은 따로 관리하고 있어야 합니다.
-
-하지만 객체지향 프로그래밍을 하면 상태에 대한 관리도 객체에서 함으로써 객체를 사용할 때 더이상 상태값이 어떻게 관리되는지
-모르더라도 사용하는 데 문제가 없습니다. 상태값을 어떻게 관리할 지는 객체를 구현할 때의 문제입니다.
-
-예를 들어 처음 파이썬 또는 자바스크립트를 공부할 때, `dictionary`가 내부적으로 어떻게 구현되어있는지 모르더라도
-`key`로 값을 저장하고 가져온다는 사실만 알고 있으면 `dictionary`를 사용할 수 있습니다. 추상화하면 (성능은 잠시 미뤄두고...)
-객체의 구현에 대해 모르더라도 `사용하는데는 문제가 없습니다`. 그 후 `dictionary`의 구현을 Hash를 이용할 것인지 Tree를
-이용할 것인지는 `구현의 문제`입니다.
-
-#### 캡슐화, 정보은닉
-
-캡슐화는 객체의 상태나 연산을 해당 객체에서만 접근할 수 있게해서 외부 연산에 의해 오류가 발생하지 않도록 합니다.
-Java나 C++에서 제공하는 `private`, `protected`, `public` 접근제어자를 통해 객체, 상속받은 객체, 외부에서
-접근하는 것을 제어하며, golang은 대문자, 소문자를 이용하고, Rust에서는 `pub` 접근 제어자를 이용합니다.
-> 파이썬은 `_`를 앞에 붙여 접근제어자처럼 사용하지만 실제로 접근은 가능하기에 접근제어자가 없어 완벽한 객체지향언어가 아니라는 말이 나옵니다.
-
-연산이 복잡해지게 되면 함수를 계속 생성하게 되는데 이때 객체 내부에서만 재활용할 연산들은 `private`으로 객체 내부에서만
-접근할 수 있도록 두어 외부 인터페이스와 내부에서 사용할 함수를 구분하는데 주로 사용합니다.
-
-#### 상속
-
-최근 언어들에서는 클래스 상속 기능을 제공하지 않는 언어들이 많습니다. 상속은 상위 클래스에서 구현한 함수나 필드를 하위 클래스에서도
-물려받는 것을 의미합니다. 상위 클래스에서 구현한 기능을 따로 구현하지 않더라도 하위 클래스에서 재사용할 수 있다보니
-구현하는 측면에서 재사용성이 늘어나는 장점이 있습니다.
-
-하지만 상속으로 구현하게 되면 하위 클래스에서 함수를 오버라이딩하거나 오버로딩할 수 있고, 혹은 여러 클래스를 다중 상속받으면서
-하위 클래스에서 상위 클래스의 기능을 깨뜨리는 경우도 발생합니다. 자세한 내용은 이후 객체지향 원칙에서 설명하겠지만
-상속의 문제점들로 인해 최근에는 상위 클래스를 직접 상속받는 것이 아닌 필드로 받아 사용하는 `composition` 방식으로 주로 구현하고 있습니다.
-
-하지만 문제를 발생시키지 않는 인터페이스를 `implements`하는 기능들은 go(`interface`)와 rust(`trait`)에서 제공합니다.
-아무래도 인터페이스의 구현은 객체지향 프로그래밍에서 가장 중요한 역할을 하기 때문에 최근에 나온 언어들에서도 제공되는 것이 아닌가 싶습니다.
-
-#### 다형성
-
-함수 이름은 같지만 다양한 타입이나 구현을 가질 수 있는 성질입니다. 기본적으로는 함수이름이 같지만 다른 타입의 파라미터와 리턴값을 가지는
-`overloading`과 하위 클래스에서 함수를 재작성하는 `overriding`으로 이루어집니다.
-
-최근 언어에서는 `overloading`은 오히려 코드를 읽는데 헷갈릴 수 있다는 이유로 지원하지 않는 언어도 있지만 `overriding`은 다릅니다.
-최소한 `interface`의 함수를 `overriding`할 수 있도록 기능을 제공하고 있습니다.
-각 클래스에서는 구현한 클래스 타입을 필드로 가지는 것이 아니라 `interface`를 타입으로 필드를 가집니다.
-그 후에 사용할 때 구현 클래스를 대입해 `interface`로 호출하지만 대입한 구현 클래스를 실행시키는 효과를 얻을 수 있습니다.
-
-이를 통해 다른 기능을 사용할 때 다른 구현 클래스를 대입해 사용해 내부 구현을 변경하지 않더라도 다른 기능을 실행할 수 있고,
-함수의 인자로 `interface`를 받는다면 함수의 구현을 변경하지 않더라도 외부에서 원하는대로 함수가 동작하도록 구현 클래스를 넘기는
-`Dependency Injection(DI)`을 할 수도 있습니다.:
-
-### 객체지향의 원칙
-> 어떻게 개발해야 잘 개발하는 것인가?
-
-그럼 객체지향적으로 개발할 때 무슨 원리를 기반으로 개발하고 있을까요? 바로 SOLID입니다.
-
-* S: SRP: Single Responsibility (단일 책임의 원칙)
- * 하나의 객체는 하나의 책임(기능, 역할)만을 가져야합니다.
-* O: OCP: Open-Closed (개방 폐쇄의 원칙)
- * 기능 확장에는 열려있고, 수정(구현 코드)에는 닫혀있어야합니다.
-* L: LSP: Liskov Substitution (리스코프 치환의 원칙)
- * 부모 클래스의 인스턴스 위치에 자식 클래스를 두더라도 문제가 없어야 합니다.
- * 여기서 상속이 문제를 발생시킵니다.
-* I: ISP: Interface Segregation (인터페이스 분리의 원칙)
- * 클래스 내에서 사용하지 않을 인터페이스(함수)는 구현하지 않아야 합니다.
- * 각 인터페이스를 최소화하여 분리합니다.
-* D: DIP: Dependency Inversion (의존 역전의 원칙)
- * 상위 모듈은 하위 모듈에 의존하지 않고 추상화가 세부 구현(인터페이스에 없는 함수나 특정 구현 클래스)에 의존하지 않아야 합니다.
- * 추상화하는 함수가 primitive 타입이나 인터페이스를 받아 구현하는것은 괜찮지만 특정 구현 클래스를 받는 것은 지양해야 합니다.
-
-#### SRP: Single Responsibility (단일 책임의 원칙)
-> 하나의 객체는 하나의 책임(기능, 역할)만을 가져야합니다.
-> 객체만이 아니라 모듈이나 함수를 구현할 때에도 마찬가지입니다.
-> 한 객체가 하나의 함수만 가져야만 한다는 것은 아닙니다.
-
-단일 책임의 원칙은 하나의 객체 또는 함수에서 하나의 역할만을 하도록 구현해야 한다는 원칙입니다.
-클래스를 통해 개발할 때 주로 발생하는 문제는 하나의 객체가 너무 커진다는 것입니다.
-주로 기능을 추가하다보면 필요한 기능을 하나 둘씩 편의를 위해 기존에 있는 객체에 함수로 추가하게 되고
-점점 객체는 특정 하나의 역할만을 하는 것이 아닌 모든 것에 다재다능한 슈퍼 객체가 되어버립니다.
-
-```java
-class SuperDataViewer {
- // 비슷한 작업의 함수만 늘어가면서 객체가 비대해짐
- public void viewHtml() {
- ...
- }
-
- public void viewMarkDown() {
- ...
- }
-
- public void viewCSV() {
- ...
- }
-
- // Viewer에서는 보여주는 작업만 하고 update는 Viewer를 사용하는 클래스에서 구현하는 것이 옳음
- public void updateView() {
- ...
- }
-}
-
-interface DataViewer {
- void view();
-}
-
-class HTMLViewer implements DataViewer {
- @Override
- public void view() {
- ...
- }
-}
-```
-
-이것은 사실 객체지향적으로 프로그래밍한 코드라고 볼 수 없습니다. 객체 안에 절차적 프로그래밍의 함수를 때려넣은 것 뿐이죠.
-주로 주의할 부분은 하나의 객체에서 비슷한 기능을 하는 작업을 함수만 늘려서 구현하거나 하나의 클래스의 역할을 너무 크게 생각해
-너무 많은 기능을 넣은 경우입니다.
-
-위의 예에서는 각각 다른 데이터를 보여주는 상황으로 `HTMLViewer`, `MardownViewer`, `CSVViewer`와 같은 방식으로
-각각 다른 클래스에서 `view`함수를 구현하는 방식으로 구현하는 것이 더 좋아보입니다.
-그리고 `updateView`와 같은 `view`를 다루는 상위 작업은 `DataViewer`를 사용하는 클래스에서 구현해두면
-`DataViewer`는 `view`라는 작업에만 집중할 수 있어 역할이 더 명확해집니다.
-`update`기능 구현이 `DataViewer`의 클래스에 있는게 구현이 조금 더 편하다는 이유로 분리되지 않는다면
-점점 거대해진 모든 역할을 하는 클래스가 추가될 수 있습니다.
-
-#### OCP: Open-Closed (개방 폐쇄의 원칙)
-> 기능 확장에는 열려있고, 수정(구현 코드)에는 닫혀있어야합니다.
-> 기능 추가는 쉽게 할 수 있지만 기존 코드는 건드리지 않아야 합니다.
-
-개방 폐쇄의 원칙은 기능을 추가하더라도 기존 코드를 건드리지 않아야한다는 원칙입니다.
-새로운 기능을 추가한다는 이유로 기존 코드를 건드리게 되면 멀쩡히 동작하던 기능이 갑자기 동작하지 않을 수도 있습니다.
-앞서 보여드렸던 코드가 이 문제를 해결하는 방법이 될 수 있습니다.
-
-```java
-class RealTimeDataViewer {
- private final DataViewer dataViewer;
-
- RealTimeDataViewer(DataViewer dataViewer) {
- this.dataViewer = dataViewer;
- }
-
- void updateView() {
- ...
- this.dataViewer.view();
- }
-}
-
-interface DataViewer {
- void view();
-}
-
-class HTMLViewer implements DataViewer {
- @Override
- public void view() {
- ...
- }
-}
-
-class MarkDownViewer implements DataViewer {
- @Override
- public void view() {
- ...
- }
-}
-```
-
-만약 MarkDown을 보여주는 viewer를 제공하기 위해 기존 구현을 건드린다면 객체가 너무 비대해져 코드를 읽기 어렵거나
-기존 코드를 건드리면서 예상치 못한 버그를 발생시킬 수 있습니다.
-애초부터 다른 객체에 새로운 기능을 구현하고 해당 기능을 사용하면 됩니다.
-
-다른 클래스에 구현해서 사용하면 당연히 확장에도 열려있고 수정에도 닫혀있겠지만 그것만으로는 사용하는 클래스의 코드를 변경하는 문제가 발생합니다.
-개방 폐쇄의 원칙은 단순히 클래스만 따로 만들라는 것이 아닙니다. 클래스를 따로 만들지만 같은 인터페이스를 `implements`함으로써
-사용하는 곳에서는 다른 구현체만 넘겨주어 수정된 구현을 사용할 수 있게 하라는 것입니다.
-
-Java에서 `List` 인터페이스가 있지만 원하는 상황에 따라 다른 성능을 위해 `List list = new ArrayList<>();`처럼
-각기 다른 `ArrayList`, `LinkedList`를 사용하는 상황과 비슷합니다. 원하는 `List` 형태가 있다면 직접 구현해서
-기능을 확장할 수 있지만 기존에 있는 다른 형태의 `List`에 전혀 영향을 주지 않죠.
-> 인터페이스 설계부터 잘 고려해서 작성해야 개방 폐쇄의 원칙을 지킬 수 있게 됩니다.
-
-#### LSP: Liskov Substitution (리스코프 치환의 원칙)
-> 부모 클래스의 인스턴스 위치에 자식 클래스를 두더라도 문제가 없어야 합니다.
-
-보통 클래스를 구현할 때 구현한 클래스는 `어떤 조건을 만족해야한다`는 가정을 두는 경우가 많습니다.
-예를 들어
-
-#### ISP: Interface Segregation (인터페이스 분리의 원칙)
-> 클래스 내에서 사용하지 않을 인터페이스(함수)는 구현하지 않아야 합니다.
-> 인터페이스를 만들 때부터 최소한의 함수만을 갖도록 합니다.
-
-```java
-interface IO {
- int write(byte[] b);
- byte[] read();
-}
-
-class FileReader implements IO {
- @Override
- public int write(byte[] b) {
- return 0;
- }
-
- @Override
- public byte[] read() {
- ...
- return b;
- }
-}
-```
-
-
-
-#### DIP: Dependency Inversion (의존 역전의 원칙)
-> 상위 모듈은 하위 모듈에 의존하지 않고 추상화가 세부 구현(인터페이스에 없는 함수나 특정 구현 클래스)에 의존하지 않아야 합니다.
-
-
-### 객체지향의 문제는?
-
-객체지향 프로그래밍을 보면 알 수 있듯이 객체에 `상태`를 두어
-
-# 함수형 프로그래밍?
-> 절차적 프로그래밍의 함수와는 함수의 `급`이 다르다.
-
-함수형 1급 객체
-
-## 선언형 프로그래밍, 명령형 프로그래밍
-
-
-
-### 함수형 언어?
-
-
-## 함수형 프로그래밍의 특징
-
-### 순수 함수 (side effect가 없는 함수)
-
-### 불변 데이터
-
-### 일급함수
-
-### 참조 투명성
-
-
-* 하스켈과 다른 언어에서의 http server 예시를 들어볼까?
-*
-
-명령형 프로그래밍
-
-선언형 프로그래밍
-
-
-## Reference
-
-* [http://www.incodom.kr/%EA%B0%9D%EC%B2%B4_%EC%A7%80%ED%96%A5](http://www.incodom.kr/%EA%B0%9D%EC%B2%B4_%EC%A7%80%ED%96%A5)
-* [https://velog.io/@phs880623/%EA%B0%9D%EC%B9%98-%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D](https://velog.io/@phs880623/%EA%B0%9D%EC%B9%98-%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D)
-* [https://coding-factory.tistory.com/328](https://coding-factory.tistory.com/328)
-* [https://www.digitalocean.com/community/conceptual_articles/s-o-l-i-d-the-first-five-principles-of-object-oriented-design](https://www.digitalocean.com/community/conceptual_articles/s-o-l-i-d-the-first-five-principles-of-object-oriented-design)
-*
diff --git a/_sass/misc/article-menu.scss b/_sass/misc/article-menu.scss
index bc168f5..75c0d1e 100644
--- a/_sass/misc/article-menu.scss
+++ b/_sass/misc/article-menu.scss
@@ -5,7 +5,7 @@
.post-menu {
padding-left: 20px;
min-width: 200px;
- max-width: 230px;
+ max-width: 250px;
.post-menu-title {
font-size: $base-font-size * 1.5;
@@ -20,11 +20,16 @@
$indent: $base-font-size / 4;
$active-bgcolor: #ecebec;
- @for $i from 2 to 7 {
+ @for $i from 1 to 7 {
.h-h#{$i} {
- padding-inline-start: $indent + ($i - 2) * $base-font-size * 1.3;
- font-size: $base-font-size * 1.1;
- line-height: 1.4;
+ padding-inline-start: $indent + ($i - 1) * $base-font-size * 1.3;
+ @if $i < 4 {
+ font-weight: bold;
+ font-size: $base-font-size * (1.25 - $i * 0.08);
+ } @else {
+ font-size: $base-font-size * (1.1 - $i * 0.03);
+ }
+ line-height: 1.5em;
}
}
diff --git a/_sass/yat/_layout.scss b/_sass/yat/_layout.scss
index 9791a3a..13c3c3b 100644
--- a/_sass/yat/_layout.scss
+++ b/_sass/yat/_layout.scss
@@ -445,7 +445,7 @@ html {
display: block;
}
- h2, h3, h4, h5, h6 {
+ h1, h2, h3, h4, h5, h6 {
margin: 60px 0 19px;
}
diff --git a/assets/2022-03-17-TWL-02-1-OOP/01-tangled-thread.png b/assets/2022-03-17-TWL-02-1-OOP/01-tangled-thread.png
new file mode 100644
index 0000000..dd3591a
Binary files /dev/null and b/assets/2022-03-17-TWL-02-1-OOP/01-tangled-thread.png differ
diff --git a/assets/2022-03-17-TWL-02-1-OOP/02-apple-use.png b/assets/2022-03-17-TWL-02-1-OOP/02-apple-use.png
new file mode 100644
index 0000000..6ddb5c7
Binary files /dev/null and b/assets/2022-03-17-TWL-02-1-OOP/02-apple-use.png differ
diff --git a/assets/2022-03-17-TWL-02-2-FP/01-duggubi.gif b/assets/2022-03-17-TWL-02-2-FP/01-duggubi.gif
new file mode 100644
index 0000000..698b4d6
Binary files /dev/null and b/assets/2022-03-17-TWL-02-2-FP/01-duggubi.gif differ
diff --git a/assets/2022-03-17-TWL-02-2-FP/02-oop-fp.png b/assets/2022-03-17-TWL-02-2-FP/02-oop-fp.png
new file mode 100644
index 0000000..94afa16
Binary files /dev/null and b/assets/2022-03-17-TWL-02-2-FP/02-oop-fp.png differ
diff --git a/assets/2022-03-17-TWL-02-2-FP/03-html.jpeg b/assets/2022-03-17-TWL-02-2-FP/03-html.jpeg
new file mode 100644
index 0000000..37e9c56
Binary files /dev/null and b/assets/2022-03-17-TWL-02-2-FP/03-html.jpeg differ