From 0f79e6e3aea26d73f740e4c9a54b386b66e6d2df Mon Sep 17 00:00:00 2001 From: caoyu-dev Date: Sun, 4 Sep 2022 20:09:12 +0900 Subject: [PATCH 01/11] =?UTF-8?q?[1=EC=A3=BC=EC=B0=A8]=201=EC=9E=A5=20?= =?UTF-8?q?=EC=98=A4=EB=B8=8C=EC=A0=9D=ED=8A=B8=EC=99=80=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EA=B4=80=20-=20=EC=A1=B0=EC=9C=A0=EB=AF=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0\354\241\264 \352\264\200\352\263\204.md" | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 "ch1/1\354\236\245_\354\230\244\353\270\214\354\240\235\355\212\270\354\231\200 \354\235\230\354\241\264 \352\264\200\352\263\204.md" diff --git "a/ch1/1\354\236\245_\354\230\244\353\270\214\354\240\235\355\212\270\354\231\200 \354\235\230\354\241\264 \352\264\200\352\263\204.md" "b/ch1/1\354\236\245_\354\230\244\353\270\214\354\240\235\355\212\270\354\231\200 \354\235\230\354\241\264 \352\264\200\352\263\204.md" new file mode 100644 index 0000000..b137fd3 --- /dev/null +++ "b/ch1/1\354\236\245_\354\230\244\353\270\214\354\240\235\355\212\270\354\231\200 \354\235\230\354\241\264 \352\264\200\352\263\204.md" @@ -0,0 +1,50 @@ +## 1장 오브젝트와 의존 관계 +스프링을 사용하는 개발자들이 스프링을 통해 얻게 되는 두 가지 중요한 가치 +- 단순함(Simplicity): 스프링은 자바의 잃어버린 본질인 객체지향 언어라는 특징을 다시 살릴 수 있도록 도와주는 도구, POJO 프로그래밍 +- 유연성(Flexibility): 많은 프레임워크와 편리하게 접목하여 사용할 수 있음 + +스프링의 철학 ? +```항상 프레임워크 기반의 접근 방법을 사용하라``` + +# 1.1 초난감 DAO +사용자 정보를 JDBC API 를 통해 DB 에 저장하고 조회할 수 있는 간단한 DAO 를 만든다. +```` +DAO(Data Access Object): DB 를 사용해 데이터를 조회하거나 조작하는 기능을 전담하도록 만든 오브젝트를 말한다. +```` + +해당 코드의 문제점이 있다고 한다. 물론 기능은 정상적으로 동작한다. 근데 왜 이 코드에 문제가 많다고 하는 걸까 ? 잘 동작하는 코드를 굳이 수정하고 개선해야 하는 이유가 뭘까 ? 그렇게 DAO 코드를 개선했을 때의 장점은 무엇일까 ? 그런 장점들이 당장에, 또는 미래에 주는 유익은 무엇일까 ? 또 객체지향 설계의 원칙과는 무슨 상관이 있을까 ? 이 DAO 를 개선하는 경우와 그대로 사용하는 경우, 스프링을 사용하는 개발에서 무슨 차이가 있을까 ? +-> 스프링을 공부한다는 것은 바로 이런 문제 제기와 의문에 대한 답을 찾아나가는 과정이다. + +# 1.2 DAO 의 분리 +변화가 한 번에 한 가지 관심에 집중되어 일어난다면, 우리가 준비해야 할 일은 한 가지 관심이 한 군데에 집중되게 하는 것이다. +즉, 관심이 같은 것끼리는 모으고, 관심이 다른 것은 따로 떨어져 있게 하는 것이다. +``` +관심사의 분리(Separation of Concerns) +관심이 같은 것끼리는 하나의 객체 안으로 또는 친한 객체로 모이게 하고, 관심이 다른 것은 가능한 한 따로 떨어져서 서로 영향을 주지 않도록 분리하는 것이다. +``` + +DB 커넥션을 가져오는 코드가 여기저기에 계속 중복되어 나타난다. 이렇게 하나의 관심사가 방만하게 중복되어 있고, 여기저기 흩어져 있어서 다른 관심의 대상과 얽혀 있으면, 변경이 일어날 때 엄청난 고통을 일으키는 원인이 된다. 지저분하게 꼬여 있는 스파게티 코드가 된다는 뜻이다. +이 경우 커넥션을 가져오는 중복된 코드를 분리하는 것으로 해결할 수 있다. +``` +메소드 추출(Extract Method) +공통의 기능을 담당하는 메소드로 중복된 코드를 뽑아내는 것을 리팩토링에서는 메소드 추출 기법이라고 부른다. +``` + +위와 같이 메소드 추출을 진행하면 어느 정도의 문제 해결이 된다. 하지만 사용하는 DB 를 다르게 사용하는 경우는 어떻게 해야 하는가 ? +`getConnection()` 메소드를 abstract method 로 수정하고, 그 구현부를 상속받아 구현하는 식으로 리팩토링을 진행한다. +``` +템플릿 메소드 패턴(Template Method Pattern) +수퍼클래스에 기본적인 로직의 흐름을 만들고, 그 기능의 일부를 추상 메소드나 오버라이딩이 가능한 protected 메소드 등으로 만든 뒤 서브클래스에서 이런 메소드를 필요에 맞게 구현해서 사용하도록 하는 방법 +``` + +이때 DAO 의 `getConnection()` 메소드는 어떤 Connection 클래스를 생성할지 경정하는 방법이라고 볼 수 있다. 이를 팩토리 메소드 패턴이라 한다. +``` +팩토리 메소드 패턴(Factory Method Pattern) +템플릿 메소드 패턴과 원리는 같으나, 서브클래스에서 구체적인 오브젝트 생성 방법에 대해 결정하게 하는 방법 +``` + +위의 방법들로 관심사항이 다른 코드를 분리해내고, 서로 독립적으로 변경 또는 확장할 수 있도록 만드는 것은 간단하면서도 매우 효과적인 방법이다. 그러나 여전히 다음과 같은 문제가 남아있다. +1. DAO 로직을 어떻게 만들 것인가 ? (상속은 문제가 발생한다) +2. DB 커넥션을 생성하는 코드를 다른 DAO 클래스에 적용할 수 없다. + + From c2e5a7b8b1c8a2cfc04707e90031d711447496f2 Mon Sep 17 00:00:00 2001 From: caoyu-dev Date: Sun, 4 Sep 2022 20:31:18 +0900 Subject: [PATCH 02/11] =?UTF-8?q?1.3=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0\354\241\264 \352\264\200\352\263\204.md" | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git "a/ch1/1\354\236\245_\354\230\244\353\270\214\354\240\235\355\212\270\354\231\200 \354\235\230\354\241\264 \352\264\200\352\263\204.md" "b/ch1/1\354\236\245_\354\230\244\353\270\214\354\240\235\355\212\270\354\231\200 \354\235\230\354\241\264 \352\264\200\352\263\204.md" index b137fd3..41ff0b7 100644 --- "a/ch1/1\354\236\245_\354\230\244\353\270\214\354\240\235\355\212\270\354\231\200 \354\235\230\354\241\264 \352\264\200\352\263\204.md" +++ "b/ch1/1\354\236\245_\354\230\244\353\270\214\354\240\235\355\212\270\354\231\200 \354\235\230\354\241\264 \352\264\200\352\263\204.md" @@ -1,4 +1,4 @@ -## 1장 오브젝트와 의존 관계 +# 1장 오브젝트와 의존 관계 스프링을 사용하는 개발자들이 스프링을 통해 얻게 되는 두 가지 중요한 가치 - 단순함(Simplicity): 스프링은 자바의 잃어버린 본질인 객체지향 언어라는 특징을 다시 살릴 수 있도록 도와주는 도구, POJO 프로그래밍 - 유연성(Flexibility): 많은 프레임워크와 편리하게 접목하여 사용할 수 있음 @@ -6,7 +6,7 @@ 스프링의 철학 ? ```항상 프레임워크 기반의 접근 방법을 사용하라``` -# 1.1 초난감 DAO +## 1.1 초난감 DAO 사용자 정보를 JDBC API 를 통해 DB 에 저장하고 조회할 수 있는 간단한 DAO 를 만든다. ```` DAO(Data Access Object): DB 를 사용해 데이터를 조회하거나 조작하는 기능을 전담하도록 만든 오브젝트를 말한다. @@ -15,12 +15,13 @@ DAO(Data Access Object): DB 를 사용해 데이터를 조회하거나 조작하 해당 코드의 문제점이 있다고 한다. 물론 기능은 정상적으로 동작한다. 근데 왜 이 코드에 문제가 많다고 하는 걸까 ? 잘 동작하는 코드를 굳이 수정하고 개선해야 하는 이유가 뭘까 ? 그렇게 DAO 코드를 개선했을 때의 장점은 무엇일까 ? 그런 장점들이 당장에, 또는 미래에 주는 유익은 무엇일까 ? 또 객체지향 설계의 원칙과는 무슨 상관이 있을까 ? 이 DAO 를 개선하는 경우와 그대로 사용하는 경우, 스프링을 사용하는 개발에서 무슨 차이가 있을까 ? -> 스프링을 공부한다는 것은 바로 이런 문제 제기와 의문에 대한 답을 찾아나가는 과정이다. -# 1.2 DAO 의 분리 +## 1.2 DAO 의 분리 변화가 한 번에 한 가지 관심에 집중되어 일어난다면, 우리가 준비해야 할 일은 한 가지 관심이 한 군데에 집중되게 하는 것이다. 즉, 관심이 같은 것끼리는 모으고, 관심이 다른 것은 따로 떨어져 있게 하는 것이다. ``` 관심사의 분리(Separation of Concerns) -관심이 같은 것끼리는 하나의 객체 안으로 또는 친한 객체로 모이게 하고, 관심이 다른 것은 가능한 한 따로 떨어져서 서로 영향을 주지 않도록 분리하는 것이다. +관심이 같은 것끼리는 하나의 객체 안으로 또는 친한 객체로 모이게 하고, +관심이 다른 것은 가능한 한 따로 떨어져서 서로 영향을 주지 않도록 분리하는 것이다. ``` DB 커넥션을 가져오는 코드가 여기저기에 계속 중복되어 나타난다. 이렇게 하나의 관심사가 방만하게 중복되어 있고, 여기저기 흩어져 있어서 다른 관심의 대상과 얽혀 있으면, 변경이 일어날 때 엄청난 고통을 일으키는 원인이 된다. 지저분하게 꼬여 있는 스파게티 코드가 된다는 뜻이다. @@ -34,7 +35,8 @@ DB 커넥션을 가져오는 코드가 여기저기에 계속 중복되어 나 `getConnection()` 메소드를 abstract method 로 수정하고, 그 구현부를 상속받아 구현하는 식으로 리팩토링을 진행한다. ``` 템플릿 메소드 패턴(Template Method Pattern) -수퍼클래스에 기본적인 로직의 흐름을 만들고, 그 기능의 일부를 추상 메소드나 오버라이딩이 가능한 protected 메소드 등으로 만든 뒤 서브클래스에서 이런 메소드를 필요에 맞게 구현해서 사용하도록 하는 방법 +수퍼클래스에 기본적인 로직의 흐름을 만들고, 그 기능의 일부를 추상 메소드나 오버라이딩이 가능한 protected 메소드 등으로 만든 뒤 +서브클래스에서 이런 메소드를 필요에 맞게 구현해서 사용하도록 하는 방법 ``` 이때 DAO 의 `getConnection()` 메소드는 어떤 Connection 클래스를 생성할지 경정하는 방법이라고 볼 수 있다. 이를 팩토리 메소드 패턴이라 한다. @@ -47,4 +49,15 @@ DB 커넥션을 가져오는 코드가 여기저기에 계속 중복되어 나 1. DAO 로직을 어떻게 만들 것인가 ? (상속은 문제가 발생한다) 2. DB 커넥션을 생성하는 코드를 다른 DAO 클래스에 적용할 수 없다. +## 1.3 DAO 의 확장 +현재 UserDao 는 관심사가 너무 많다. +- `UserDao` 는 사실 어떤 DB 를 사용하는지 관심 없다. +- DB 에서 데이터만 가져오면 된다. +- 따라서 DB 를 연결하는 부분을 아예 다른 클래스로 분리해보자. + +그렇게 클래스가 구현되어 의존관계에 있게 되었고, 더 유연한 관계를 위해 도입된 것이 인터페이스이다. + +### 1.3.2 인터페이스의 도입 +인터페이스는 어떤 일을 하겠다는 기능만 정의해놓은 것이다. 따라서 인터페이스에는 어떻게 하겠다는 구현 방법은 나타나 있지 않다. 그것은 인터페이스를 구현한 클래스들이 알아서 결정할 일이다. UserDao 가 인터페이스를 사용하게 한다면 인터페이스의 메소드를 통해 알 수 있는 기능에만 관심을 가지면 되지, 그 기능을 어떻게 구현했는지에는 관심을 둘 필요가 없다. + From 20ada55499c915547c63bf3b4efe3170ea2d3b8c Mon Sep 17 00:00:00 2001 From: caoyu-dev Date: Sun, 4 Sep 2022 20:47:48 +0900 Subject: [PATCH 03/11] =?UTF-8?q?1.3=20=EC=B6=94=EA=B0=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0\354\241\264 \352\264\200\352\263\204.md" | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git "a/ch1/1\354\236\245_\354\230\244\353\270\214\354\240\235\355\212\270\354\231\200 \354\235\230\354\241\264 \352\264\200\352\263\204.md" "b/ch1/1\354\236\245_\354\230\244\353\270\214\354\240\235\355\212\270\354\231\200 \354\235\230\354\241\264 \352\264\200\352\263\204.md" index 41ff0b7..1473df3 100644 --- "a/ch1/1\354\236\245_\354\230\244\353\270\214\354\240\235\355\212\270\354\231\200 \354\235\230\354\241\264 \352\264\200\352\263\204.md" +++ "b/ch1/1\354\236\245_\354\230\244\353\270\214\354\240\235\355\212\270\354\231\200 \354\235\230\354\241\264 \352\264\200\352\263\204.md" @@ -60,4 +60,33 @@ DB 커넥션을 가져오는 코드가 여기저기에 계속 중복되어 나 ### 1.3.2 인터페이스의 도입 인터페이스는 어떤 일을 하겠다는 기능만 정의해놓은 것이다. 따라서 인터페이스에는 어떻게 하겠다는 구현 방법은 나타나 있지 않다. 그것은 인터페이스를 구현한 클래스들이 알아서 결정할 일이다. UserDao 가 인터페이스를 사용하게 한다면 인터페이스의 메소드를 통해 알 수 있는 기능에만 관심을 가지면 되지, 그 기능을 어떻게 구현했는지에는 관심을 둘 필요가 없다. +### 1.3.3 원칙과 패턴 +``` +개방 폐쇄 원칙(OCP, Open-Closed Principle) +클래스나 모듈은 확장에는 열려 있어야 하고 변경에는 닫혀 있어야 한다 +- UserDao 의 경우 인터페이스를 구현한 어떤 구현체든 생성자에 주입할 수 있으니 확장에 열려 있음 +- UserDao 의 경우 자신의 핵심 기능을 구현한 코드는 그런 변화에 영향을 받지 않고 유지할 수 있으므로 변경에 닫혀 있음 +``` + +``` +객체지향 설계 원칙(SOLID) +1. SRP(The Single Responsibility Principle): 단일 책임 원칙 +2. OCP(The Open-Closed Principle): 개방 폐쇄 원칙 +3. LSP(The Liskov Substitution Principle): 리스코프 치환 원칙 +4. ISP(The Interface Segregation Principle): 인터페이스 분리 원칙 +5. DIP(The Dependency Inversion Principle): 의존관계 역전 원칙 +``` +-> 공부, 정리 다시 필요,, + +1. 높은 응집도 +- 어떤 한 관심사를 한 클래스에 모아놓은 것이다. +- 변경이 일어날 때 모듈의 많은 부분이 함께 바뀐다면 응집도가 높다고 말할 수 있다. +2. 낮은 결합도 +- 느슨하게 연결된 형태를 유지하는 것이 바람직하다. +-- 클래스와 클래스를 연결할 떄, 인터페이스를 활용하는 것이 느슨한 결합에 유리하다. + +**전략 패턴** +위에서 작성했던 구조가 전략 패턴이라 한다. OCP 에도 적합하다. + + From 89007ccf82c7a6d36a105e04e60e30679f7ebedc Mon Sep 17 00:00:00 2001 From: caoyu-dev Date: Sun, 4 Sep 2022 23:11:24 +0900 Subject: [PATCH 04/11] =?UTF-8?q?1.4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0\354\241\264 \352\264\200\352\263\204.md" | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git "a/ch1/1\354\236\245_\354\230\244\353\270\214\354\240\235\355\212\270\354\231\200 \354\235\230\354\241\264 \352\264\200\352\263\204.md" "b/ch1/1\354\236\245_\354\230\244\353\270\214\354\240\235\355\212\270\354\231\200 \354\235\230\354\241\264 \352\264\200\352\263\204.md" index 1473df3..4f37c2d 100644 --- "a/ch1/1\354\236\245_\354\230\244\353\270\214\354\240\235\355\212\270\354\231\200 \354\235\230\354\241\264 \352\264\200\352\263\204.md" +++ "b/ch1/1\354\236\245_\354\230\244\353\270\214\354\240\235\355\212\270\354\231\200 \354\235\230\354\241\264 \352\264\200\352\263\204.md" @@ -88,5 +88,26 @@ DB 커넥션을 가져오는 코드가 여기저기에 계속 중복되어 나 **전략 패턴** 위에서 작성했던 구조가 전략 패턴이라 한다. OCP 에도 적합하다. +## 1.4 제어의 역전(IoC) +``` +Ioc(Inversion of Control) +``` +지금까지 리팩토링한 Dao 에서 개선을 할 사항이 남아 있다. 바로 Dao 를 사용하는 클라이언트가 문제다. +클라이언트는 기존에 Dao 가 직접 담당하던 기능이었던 ** ConnectionMaker 구현 클래스** 를 떠맡고 있다. + +팩토리 +위의 두 기능을 DaoFactory 라는 객체를 새로 만들어 맡기자. +``` +팩토리(Factory) +분리시킬 기능을 담당할 클래스의 역할은 객체의 생성 방법을 결정학고 그렇게 만들어진 오브젝트를 돌려 주는 것인데, 이런 일을 하는 오브젝트를 팩토리라 한다. +``` + +클라이언트는 팩토리 객체를 통해 Dao 를 생성할 것이고, 이를 통해 두 가지 기능이 분리되었다. +UserDao 와 ConnectionMaker 는 각각 애플리케이션의 핵심적인 데이터 로직과 기술 로직을 담당하고 있고, DaoFactory 는 이런 애플리케이션의 오브젝트들을 구성하고 그 관계를 정의하는 책임을 맡고 있음을 알 수 있다. +즉, 전자는 실질적인 로직을 담당하는 컴포넌트라면, 후자는 애플리케이션을 구성하는 컴포넌트의 구조와 관계를 정의한 설계도 같은 역할을 한다고 볼 수 있다. + +### 1.4.2 오브젝트 팩토리의 활용 +DaoFactory 에서 여러 Dao 를 생성한다면 어떻게 되나 ? +각 팩토리 메소드마다 ConnectionMaker 을 생성하는 코드가 중복되어 나타난다. 코드 중복이 발생하면 이 코드를 수정할 때 의미없이 반복적으로 같은 코드를 수정해야 하기 때문에 이 부분 역시 DaoFactory 안의 새로운 메소드로 빼내는 것이 좋다. From c7a87bc2125a0501c968e44cf1155652ea3294c1 Mon Sep 17 00:00:00 2001 From: caoyu-dev Date: Mon, 19 Sep 2022 23:48:53 +0900 Subject: [PATCH 05/11] =?UTF-8?q?2=EC=9E=A5=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=8A=A4=ED=84=B0=EB=94=94=20=EB=82=B4=EC=9A=A9=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC=20(20220918)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0\353\224\224_\354\240\225\353\246\254.md" | 254 ++++++++++++++++++ 1 file changed, 254 insertions(+) create mode 100644 "ch2/2\354\236\245_\355\205\214\354\212\244\355\212\270_\354\212\244\355\204\260\353\224\224_\354\240\225\353\246\254.md" diff --git "a/ch2/2\354\236\245_\355\205\214\354\212\244\355\212\270_\354\212\244\355\204\260\353\224\224_\354\240\225\353\246\254.md" "b/ch2/2\354\236\245_\355\205\214\354\212\244\355\212\270_\354\212\244\355\204\260\353\224\224_\354\240\225\353\246\254.md" new file mode 100644 index 0000000..7a08da8 --- /dev/null +++ "b/ch2/2\354\236\245_\355\205\214\354\212\244\355\212\270_\354\212\244\355\204\260\353\224\224_\354\240\225\353\246\254.md" @@ -0,0 +1,254 @@ +# 2장 테스트 (토비님 이야기 정리) +테스트를 왜 만들어야 하는지에 대해 논쟁을 겪을 수 있다. +`당연히 해야 하는 거 아냐 ? 기본 아니야 ?` +테스트를 열심히 하는 사람은 한 번씩 가지게 되는 고민인 것 같다. +그런 생각을 하면서 왜 필요하지 ? 생각을 해야 한다. + +`켄트백` 의 `TDD(테스트 주도 개발)` 책을 보면 후반부에 `학습 테스트`를 소개하는 내용이 나온다. +**학습테스트 ! (한 번 해볼 것)** + +(망규 님) +2장을 읽다보니 `테스트가 없었을 떄 어떻게 검증을 했었는가 ?` 에 대한 생각이 들었고, +B-to-B 에서 테스트는 하나의 문서화가 될 수 있다는 것이 장점이다. +의미있는 테스트를 어떻게 작성할 수 있는가를 생각하게 한다. + +(토비 님) +`네거티브 테스트` 를 우선적으로 만드는 게 좋다고 생각한다. +이전에는 JUnit 테스트는 왜 public 으로만 만들어야 했는가 ? +원래 JUnit 은 클래스 하나에 테스트 메서드 하나만 만들어야 했다. (원래 설계) +→ 비슷한 테스트를 클래스 하나에 밀어넣게 되었다. utility ~? +소문자 test 로 시작하는 메서드는 테스트 메서드이다. +이것을 실행하는 프레임워크가 reflection 을 이용해서 테스트라고 시작하는지 찾아야 했는데 (중간 생략, 다음 구문이 이해가 빠름) + +``` +JUnit이 처음 만들어지던 때 사용했던 JDK1.1 에서는 리플렉션에서 public 메소드만 접근을 허용했습니다. 따라서 JUnit의 테스트 메소드는 모두 public일 수 밖에 없었습니다. 그게 관례가 되서 지금까지 내려온 것입니다. +``` + +JUnit 4 의 prefix 를 이용한 방식에서 → JUnit 5 @test 를 읽는 방식으로 바뀌었다. +public 으로 만들었던 전통이 있어서 junit 5 는 거의 새롭게 만든 프레임워크 +private 으로 만들거나 그러라는건 아니고 타이핑하기 편하게 default 그냥 만드는게 +public 굳이 만들어서 붙이는 분들이 계시는데 옛날부터 쓰셔서 그러신가보다 라고 생각하면 될 것 같음 ex) 김영한 님 + +테스트 시간 ? 수행 시간을 빨리 돌려야 한다 이건 다음에 얘기 +테스트를 만들어야 한다는 개념이 폭력적으로 나타날 때, 2007-8 즈음 +테스트 돌리는 동안 멍하니 보고만 있으면 잔소리를 했다. ex) 테스트 돌아가는 중에 다음 테스트를 위한 코드를 짜야지 +라이브 테스트 ? 켄트 백이 junit backs ??? 이클립스 플러그인 만들었엇고 +테스트 계속 짜고 있으면 백그라운드에서 계속 실행하고 있음 틀린거 계속 나타내주고 있음 +자바는 프레임워크 하나만 남아있는듯 ? 닷넷에서는 고급버전에서만 되는 것으로 알고 있음 + +해당 플러그인이 발전 못하는 이유 ? 2-3 돌리는 동안 커피 한잔 하고, 트위터도 좀 하고 이게 낙이어서 그런듯하다. +전체 테스트를 돌려놓고 sns 를 하는 분도 있는 듯 (ㅋㅋ) +테스트 돌려놓고 잘 작업할 수 있다. (토비님 생각) + +(토비님) +테스트 코드 작성하는 것을 처음 알게된 게 2003년 Jboss 에서 알게 됐다. 오픈 소스 ejb webserver 이런거 고도화된 서버였는데 소스 코드를 받아서 보다 보니깐 테스트 폴더가 있더라. +실제 서버가 띄워지는 것을 테스트 하는 것을 보면서 (거의 2시간 걸렸다) 희열을 느꼈다. 와 이런게 가능하구나. +ui 가 있는 화면을 레코딩하면서 기능을 테스트해 보는 (툴을 써서) - 워낙 잘 안됨 (예전에는 이런식의 테스트가 있었음) +코드로 테스트를 할 수 있다는 것을 보면서 너무 놀랐다. + +전세계 사람들이 JUnit → TDD 에 빠졌다. 2006 ~ 2010년 전세계에서 테스트 작성 뭉뚱그려 가지고 테스트를 한번도 작성해 보지 못한 사람에게 `테스트 작성해 보세요` 가 들어갔고, 커버리지 몇 십프로 채우지 않으면 인사고과 점수가 낮게 나오던 시절이 있었다. +한국은 한발 늦게 이런 시기가 왔지만, 아무튼 세계적으로 이런 시절이 있었다. + +그러다 테스트 회의론에 빠졌다. +해외에 합리적으로 일한다는 회사에서 `오늘도 회사에서 요구하는 커버리지를 채우기 위해 아무 의미도 없는 테스트를 작성하는 나를 보면서 환멸을 느낀다` 라는 글을 작성하는 사람이 생겼다. +한쪽에서는 TDD 테스트 작성에 환호를 했고, 한쪽에서는 이런 반응이 있었다. + +인포큐 ??? 일주일에 적어도 한 번은 테스트에 대한 글이 올라 왔었는데, +2011 ~ 2012 년 즈음에 TDD 테스트 작성 얘기가 싹 사라짐 (암흑기) +책이 나오는 즈음에 과도하게 열광하는 사람들, 푸쉬하는 사람들, 해봤는데 생산성 떨어져서 절망에 빠진 사람들이 있었다. +(토비 님은 그 시절에) 당연히 테스트를 해야 한다고 얘기를 하고 다녔다. + +그리고 나서 테스트 작성에 대한 관점이 서서히 새로 입문하시는 개발자들부터 시작해서, 모든 기술은 나오면 테스트를 작성하는 법부터 시작을 하고, 모든 언어에 테스트를 적용하고, 오픈 소스에도 테스트를 적용하는 등 테스트의 개념이 서서히 자리를 잡아 나가기 시작했다. +그러나 그 시절의 당연히 `테스트는 작성해야 한다` 까지는 못온 것 같다. + +그 사이에 테스트에 가볍게 접근하고 JUnit 으로 작성하는 것과 TDD 로 작성하는 것 구분 못하는 사람 많다. +테스트 코드 작성과 TDD 작성이 구분되지 않은채 퍼졌었다. (많이 퍼지던 시기에) +사람들이 열광헀다 → 실망 → 다시 테스트를 작성하기 시작하는 싸이클을 보면서 테스트에 대한 생각과 느낀점이 많다. +이 책을 쓰던 시절에는 한번이라도 테스트를 작성하면 좋겠다는 생각에 책을 썼다. + +스프링 이전에는 테스트 만들기가 너무 어려웠다. (비싼 툴) +Junit, TDD 서버 어플리케이션 테스트 하려면 너무 어려웠다. +스프링 많은 부분에서 지원하려고 노력을 많이 함. +DI 콜라보레이터를 Mock, 좋다. + +테스트 코드를 하나 돌렸더니 모든 게 다 돌아가는 ~ ?? 디버그 ??? +테스트 더블 ???? ????? Test Double +스턴트 더블(Stunt double) +스턴트를 하면서 다른 사람인 척 하면서 끼어들어 하는 거 ??? 테스트 대역 ??? +stuck, mock, spy, dummy, fake → **Imposter ?** (토비 님은 이 단어가 적절하지 아니한가 의견을 제시하셨다) + +테스트 대역 이라는 어려운 언어 쓰지 말고 임포스터가 어떤가 ? 다른 사람인척 하고 속이면서 행동을 하는 그런 사람을 임포스터라고 하니깐 + +(토비 님) +테스트하기 어려운 코드는 특정 구현의 코드가 종속이 많이 되어있는 경우가 그렇다. +느슨한 관계로 만들어주면 테스트하기 좋다. + +객체지향적 설계로 따라가는데 +테스트가 하기 어려운 코드가 좋은 코드일리는 없는건 확실하다. (강한 결합도가 높은 코드일리가 분명) + +**테스트 하기 쉽다 → 코드 엉망진창일 수도 있다.** +**기능을 잘 구현 못했으면 테스트 하기 아무리 쉽다고 해도 소용이 없는 거죠.** + +Q) 테스트를 위해서 비즈니스 코드를 손대는 경우 ? + +A) (김진영 님) 유지보수를 하는 것이 테스트 코드까지라고 생각한다. +과도한 테스트도 있음 +테스트 코드 조차도 잘 짜야 하는 코드라고 생각한다. +프로덕션 코드와 테스트 코드가 그렇게 다른가 ? 1급 시민, 2급, ~ 전 다 중요하다 +명세 관련해서 스웨거를 붙이는건 안좋아하는데 +테스트 코드를 위해서 deleteAll(), get() 같은 비즈니스 로직을 추가하는 건 좋았다. + +A) (연로그 님) 테스트 코드 때문에 비즈니스 로직을 추가하는 것은 안좋아한다. +더 많은 로직들이 테스트 때문에 복잡해지는 상황이 생긴다. +따라서 테스트 코드 때문에 비즈니스 로직을 추가하지 말자라는 나만의 기준이 생겼다. + +A) (토비 님) 복잡한 로직이 들어가지는 않겠죠. 2장에서는 db 테스트 할 수 있는 방법이 이것밖에 없다. 하지만 이거는 결국 유저를 다루는 dao 에서는 필요로하는 기능일 테니까 타협을 한 코드라고 볼 수 있다. +db 까지 건드리는 상황을 가지고 예제를 만들었는데 +롤백 테스트라는 것을 한다. 테스트 동안에 디비에 쌓았으면 테스트가 끝나면 롤백을 하는 +dao 에 delete 가 들어가는게 이상한 것은 아닌데 그런게 없어지죠 (3장, 5장에 나옴 그런 내용) +여기는 그냥 이렇게도 테스트할 수 있습니다. +하늘이 무너지는게 아니면 해야죠. 사용할 수 있다. +문제를 일으키는 것 같다. 지저분해진다. 느껴지면 대안을 찾으면 됨. (예를 들어,) 쿼리를 직접 날려서 없애도 되고 ,,, +2장 독자들의 상황과 수준을 고려해서 이렇게 책을 쓰게 됐다. +트렌젝션 테스트 ? 트렌젝션이 잘 걸렸는지 아닌지 테스트하는게 어렵다. +롤백이 되는지를 테스트해야 하는데 예외가 발생을 하고 롤백이 되가지고 트렌젝션이 다 돌아가지 않고 이전까지 디비에 다 남아 있으면 트렌젝션 테스트 실패 아니면 트렌젝션 테스트 성공 +예외를 데이터 셋팅 ?? 코드에다 이번 실행을 할 때 강제로 예외를 던져라 + +(뭔말이지) +트렌젝션이 잘 동작하는가 ? (뭔소리지?) +테스트 코드가 비즈니스 로직을 침범하는게 좋은 것은 아니다. ??? 결론인가??? + + +Q) 툴을 이용해서 테스트 작성 했는데 +테스트를 위해 @MockBean, @SpyBean을 사용하는 것이 좋은 디자인이 아님에도 테스트 코드를 작성하기 편해지기 때문에 사용하지 않는 편이 좋다는 [글]([https://jojoldu.tistory.com/320](https://jojoldu.tistory.com/320)) 을 봤습니다. 이에 대한 다른 분들의 의견이 궁금합니다. + +(토비 님) +이동욱 님 글은 정확히 말하면 테스트 얘기라기 보다는 다른 경우에도 적용이 될만한건데 어떤 의견이 궁금한건지 구체적으로 얘기 좀 더 해달라 + +(추가 Q) +목빈, 스파이빈을 사용하지 말자는 얘기는 아닌데 +2장 직접적인 내용이 아니라서 개인적으로 고민했던 부분이라서 남겼던 글 +궁금했던 거는 어떠한 툴들을 사용을 해서 정석적인 방법이 있는데 인터페이스에서 DI 주입 +모키토에 given when then +어떤 방법을 사용하고 그 이유는 뭐인지 어떤게 정석적인 방법이고, +spybean 의 경우에는 외부에 라이브러리를 가져와서 쓰는 +모킹하는 방법으로 사용한 적이 있는데 +구현하지 않는 코드에 대해서 +필요한 메서드, 필요하지 않은 메서드 ? ??? +설정을 해줘야 하는 케이스가 있었고 라이브러리 ~~?? +2번이랑 연관된 이야기 +어떤식으로 ????? ㅇ결과를 내는지 ? 어떻게 사용을 하는지 +모킹을 어떻게든 해야 하는 상황인데 컨트롤 할 수 없는 경우 직접 사용하게 할 수 없고 +mock 라이브러리 ~~ ? ? **인터페이스 30개쯤 다 만들어야 하는 경우** <- 이 내용이 촛점이다. +mock 이나 stub 을 종종 이용한다 ? + +(토비 님) +이동욱 님 글은 전에 본건데, 이 글의 요지는 여러 가지 외부 라이브러리를 모킹해서 이렇게 해서 접근하는거 어렵지 않은데 내 코드에서 직접 접근해야 하는 것을 가능하게 ~ 새로운 오브젝트를 두고 모킹은 간단하게 할 수 있으니 모킹을 할 수 있었다. (다른 이야기임) +직접 구현 ??? 쓰지 않을 것도 구현 해야 하네 ??? 기계 적으로 ???? +어뎁터 오브젝트를 하나 만들어서 쓴다 → 내 코드가 평생 의존을 할건지 설계 고민이 중요 +aws 의 기능을 사용하는 코드가 필요하다. 비즈니스 로직 코드에서 aws 빈을 등록해서 의존 +azure gcp 자체 클라우드 내부 서비스 이용할 수 있는데 내 코드로 바로 의존하는게 좋을까 ? +추상화된 계층을 넣는게 좋겠다. 이렇게 접근을 하면 괜찮은 접근법 +모킹을 해서 aws 10개 중 하나만 쓰는데 목을 하고 있네 ? aws 를 계속 쓸건데 ?? +중간에 뭐를 둔다 좋은건 아닌듯 ? +내가 만든 비즈니스 코드가 추상화 , 외부 라이브러리 끌어오는거 로우 레벨인 경우 +목빈 스파이빈 편하게 써라 +리모트, 비즈니스 코드에 직접 넣지 않는게 좋다. +스프링의 레스트 템플릿, 비즈니스 코어 로직이 담긴 코드에 직접 주입하는 구조 ㄴㄴ + +외부 api + +목을 쓸 떄 주의점 +목 라이브러리는 런타임에 클래스 동적으로 만들어서 로딩해서 하는 복잡한 구조 +시간이 오래걸림,, junit 은 하나의 클래스에 테스트 메소드 10개 매번 새로운 인스턴스를 만듬 +목을 사용하는 무언가를 넣으면 메소드마다 목을 새로 만들어서 +인스턴스를 테스트 클래스 한번 만들때 한번만 만들도록 하라 + +(???) 퍼 클래스 ?????? → 다른 기준 하나를 깨뜨리게 되는 것 + +Q) 인수테스트를 할 시 `사용자 시나리오`에 맞춰 수행해야 합니다. +이때 사용자를 프론트엔드로 설정하고, 특정 API에 대한 테스트를 진행하는 방법이 있습니다. +사용자를 우리 서비스의 유저로 설정하고, 여러 API 의 상호작용에 대해 테스트를 진행할 수도 있습니다. +사용자를 어떤 층으로 정해 테스트를 하는게 좋을지 궁금합니다 + +인수테스트 → 토비 님 별로 안좋아함 + +처음 단위 테스트라는 개념이 들어왔을 때, 혼란했다. +qa 테스트를 하시는 분들 입장에서는 단위 테스트라는 것은 기능 단위였다. 예를 들어, 회원 가입 +JUnit 에서 말하는 단위테스트는 무엇인가 ? + +단위테스트는 돌아가는 동안 db 엑세스를 하지 않는 등의 외부와 엑세스가 없어야 한다. +더 나아가는 경우 클래스 한개만 테스트해야 단위 테스트다라고 말하기도 한다. + +그러나 작은 메소드 하나, 큰 경우는 회원 가입 하나를 단위 테스트라고도 말한다. (토비 님) 난 이것도 공감한다. +db 엑세스 하면 안된다는 이유는 테스트 결과가 동일해야 한다. 독립적이어야 한다. +(극단적으로) 100 번 1000번 똑같은 결과가 나와야 한다. +예를 들어, 테스트 순서가 54321 이던 12345 이던 결과가 다 똑같아야 한다. → 이걸 전제로 깔고 하면 어떤식으로 해도 상관 없다. + +나중에는 `개발자 테스트` 라는 단어를 씀 (켄트백) +인수테스트 > 뭐라고 정의하느냐에 따라서 달라질듯 ? +테스트 코드로 어떻게 해보려고 했지만 시나리오가 계속 변해야 하니깐 안정성의 이유로 테스트 자동화가 힘드니깐 qa 한테 맡기는게 좋겠다. 이런 의견도 있다. + +(주승 님) +Q) (커다란 기능을 TDD로 개발한다면) 테스트 코드를 작성할 때 하나의 기능 내부 구현을 블랙박스로 보고 외부 인터페이스를 테스트 하려는 경향이 개인적으로 있습니다. 그런데 만약 TDD를 시도하는 경우인데 내부 구현에 참여하는 모듈이 다양하고 규모가 꽤 있는 경우 테스트 코드를 작성하고 테스트를 실행하기 까지 꽤 오랜 시간이 걸리는 경우가 있는 것 같습니다. 그래서 여러가지 문제를 만납니다. 여러 모듈이 있다보니 어디서 문제가 발생했는지 알기 어렵거나 테스트를 통해 즉시 확인 받으면서 코드를 작성하지 않으니 불안함이 이어지는 것도 같습니다. 이럴 때 블랙 박스 내부 구현 모듈 하나하나를 단위테스트 코드로 더 쪼개서 작성하는 편이신가요? (질문을 정리하다 보니 이와 같은 문제가 있다면 더 작은 단위의 내부 모듈로 테스트를 분리하는 것이 좋겠다는 생각이 들긴 합니다) +외부 구현 블랙박스 내부 구현 테스트 +TDD 모듈 많이 참여하고 규모가 좀 있는 경우 테스트 코드를 먼저 짜고 다 만들기 까지 텀이 길다. 어느 모듈에 문제가 있는지 ?? + +(토비 님) +TDD 를 많이 하시나요 ? (개인적으로 하고 있다) +(교육을 받는 분들) TDD 를 한다. 테스트는 TDD 로 작성해야 한다 ? 그냥 그런게 있다고 들었다. + +**테스트는 언제 만들어야 할까 ?** 결국은 이게 중요하다. + +모듈을 하나 개발하는데 일주일 정도 시간이 지났는데 기능이 다 완성이 안됐고 TDD 로 하면은 이 상황이 개선이 될까 ? +논쟁이 많고 어려운 주제다. +TDD 도 테스트를 작성하는 방법 중 하나라 생각한다. +테스트 작성하는 건 좋다 (모두 동감할 거고) +얼마나 시간이 지나서 작성하는게 좋은데 ? 테스트 작성은 코드를 작성하고 나서 빨리 하는게 좋지 않은가 ? +오늘 만든 코드는 이번주가 끝나기 전에 테스트 코드를 작성하자는 기준을 잡는 것도 괜찮다. (구글의 어떤 개발자, 금요일에 다른 일은 하지 않고 월 ~ 목요일에 만든 코드에 대한 테스트 코드를 작성) +고로 테스트를 빨리 만드는게 좋다. 빨리 검증하면 검증할수록 좋다. +코드를 작성하고 한 시간 이내 ? 10분 이내 ? +이런 생각을 하다 보면 어디까지 가냐면 **즉시 그 코드에 대한 피드백을 줄 수 있는 테스트 코드를 미리 작성해야 한다.** 는 생각까지 간다. +(TLP 테스트 라스트 ~ ) +TDD 테스트를 먼저 작성하고 코드 ~~ +시간의 문제라고 생각할 요지가 있어서 TDD 라 이름을 바꿈 +빨리 받을 수록 좋다. 얼마나 빨리 받는게 좋은가 ? 즉시 + +**즉시 받는 방법, 즉 미리 만들어 놓는 것이다. 따라서 극단까지 밀어붙이면 TDD 가 되는 것이다.** + +시간의 순서 ? 켄트백 TDD 서문에서 테스트 주도 개발은 코드를 만들면 뭘 만들지 생각하고 코드를 만들고 5분 이내에 테스트 ~ ㄴㄴ + +**내가 작성한 코드의 피드백을 받는 사이클을 컨트롤 하는 기법이다. 아이디어를 생각하고 그 코드가 작성이 되고 피드백을 받는 시간의 간격을 갭이라 하는데 어떻게 조절할 수 있는지 컨트롤할 수 있는지가 TDD 이다.** +**내가 그 피드백을 받는 갭을 의식하고 컨트롤 하고 있었으면 그것을 TDD라 한다. (켄트백)** +2주 정도 계속 코딩하다가 피드백을 받아도 괜찮다. (안정감이 있으면 괜찮다) +불안함이 있으면 더 빨리 피드백을 받을 수 있는 방법을 찾아라. + +블랙박스의 경계를 어디로 치느냐 결정하기 나름이 아닌가 ? +작은 모듈을 만들고 너무 덩치가 큰 모듈이 되지 않도록 나누면 피드백을 받을 수 있는 주기를 관리를 할 수 있는게 아닌가 ? +테스트를 언제 받는지는 중요하지 않다고 생각함 +일주일 동안 불안감을 느낀다 ?? 나중에 테스트 어떻게 하려고 그런가 ??? (어휴 그러면 안돼요 ~~~) +작게 쪼개서 만드는게 좋다. 블랙박스 테스트가 만능은 아니다. (화이트 박스 테스트는 블랙 박스 테스트의 반대말이라고 하는데 하얀 박스는 안이 안보이니깐 글래스 박스 테스트라고 하자는 의견이 있다) + +프라이빗 메소드를 테스트한다던가 외부에 공개하면 안되는 메소드를 테스트하는 것 ~?? +필요하면 프라이빗 메소드도 테스트 해라. 프라이빗 메소드 체크할 수 있는 툴이 많다. +더 좋은건 설계를 잘해서 분리하는 방향도 있다. + +Q) (제한된 시간과 포괄성의 트레이드오프) 현업에서 일하다보면 늘 시간이라는 제약을 마주합니다. 제한된 시간은 포괄적이라는 개념을 완벽하게 적용할 수 없게 하는 제약이 되는데요. 현업에서 제한된 시간 안에 포괄적인 테스트를 작성하는 지혜에 대해 나누어 보고 싶습니다. 예를 들면 어떤 종류의 테스트는 이럴 때 포기하기도 한다. 어떤 종류의 테스트는 꼭 사수한다와 같은 것들이요. (질문을 하고 답변을 생각해 보니) 저는 일정이 촉박한 경우는 모든 기능의 성공케이스만 일단 테스트 케이스를 작성하고 예외 케이스는 단위 테스트를 하면서 발견되는 예외 상황만 추가로 작성하는 등의 방법을 사용하는 것 같아요. +si 프로젝트 예외 테스트를 먼저하라는 글이 있는데 성공하는 테스트 케이스 먼저 진행하고 나중에 예외 테스트를 한다. + +(토비 님) +버그가 발생하면 보완 +제일 어려운 디버깅은 논리적인 버그를 잡는 일이다. +제일 쉬운 널포인트 디버깅은 exception 을 추적하기만 하면 된다. 금방 추적이 된다. 문제 해결이 쉽다. +논리적인 버그가 어려움. 프로그램 문제가 없는데 일주일에 한 번씩 틀어진다던가 + +포괄적인 테스트를 작성하다보면 해결되기는 한다. +정수를 입력 받는다. 입력값을 어떻게 받아야 포괄적인 테스트가 되는가 ? + +균등 분할, 경계값 분할 +랜덤한 값을 500번, 1000번 파라메터 값을 넣고 테스트를 하는 것 보다는 +양의 정수, 음에 정수, 0 (포괄적인 세 가지 경우) 를 놓고 테스트를 하는게 낫다. 분할을 잘하면 조합을 잘하면 테스트가 짧다. + +완벽함을 추구한다고 1000번이나 되는 테스트를 진행한다고 치자 랜덤한 숫자로만 테스트를 한다고 했는데 양수만 1000번이 나오는 테스트를 진행한다면 ? 음수가 들어면 꽝 +경계값, 숫자를 다루는 경우 반올림 오차에서 문제 생긴다. 쌓이면 큰 잘못된 값을 만들어낸다. +경계값 주변의 작은 숫자 테스트 From 21061362c3dda40bbc1d5b29a56842d2594e40ad Mon Sep 17 00:00:00 2001 From: caoyu-dev Date: Tue, 20 Sep 2022 10:02:45 +0900 Subject: [PATCH 06/11] =?UTF-8?q?[2=EC=A3=BC=EC=B0=A8]=202=EC=9E=A5=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20-=20=EC=A1=B0=EC=9C=A0=EB=AF=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0\353\224\224_\354\240\225\353\246\254.md" | 141 +++++++++--------- 1 file changed, 69 insertions(+), 72 deletions(-) diff --git "a/ch2/2\354\236\245_\355\205\214\354\212\244\355\212\270_\354\212\244\355\204\260\353\224\224_\354\240\225\353\246\254.md" "b/ch2/2\354\236\245_\355\205\214\354\212\244\355\212\270_\354\212\244\355\204\260\353\224\224_\354\240\225\353\246\254.md" index 7a08da8..77c580e 100644 --- "a/ch2/2\354\236\245_\355\205\214\354\212\244\355\212\270_\354\212\244\355\204\260\353\224\224_\354\240\225\353\246\254.md" +++ "b/ch2/2\354\236\245_\355\205\214\354\212\244\355\212\270_\354\212\244\355\204\260\353\224\224_\354\240\225\353\246\254.md" @@ -7,56 +7,61 @@ `켄트백` 의 `TDD(테스트 주도 개발)` 책을 보면 후반부에 `학습 테스트`를 소개하는 내용이 나온다. **학습테스트 ! (한 번 해볼 것)** -(망규 님) +### (망규 님) 2장을 읽다보니 `테스트가 없었을 떄 어떻게 검증을 했었는가 ?` 에 대한 생각이 들었고, B-to-B 에서 테스트는 하나의 문서화가 될 수 있다는 것이 장점이다. 의미있는 테스트를 어떻게 작성할 수 있는가를 생각하게 한다. -(토비 님) +### (토비 님) `네거티브 테스트` 를 우선적으로 만드는 게 좋다고 생각한다. 이전에는 JUnit 테스트는 왜 public 으로만 만들어야 했는가 ? -원래 JUnit 은 클래스 하나에 테스트 메서드 하나만 만들어야 했다. (원래 설계) -→ 비슷한 테스트를 클래스 하나에 밀어넣게 되었다. utility ~? +원래 설계에서의 JUnit 은 클래스 하나에 테스트 메서드 하나만 만들어야 했다. +→ 이러다가 비슷한 테스트를 클래스 하나에 밀어 넣게 되었다. utility 등 ~? + 소문자 test 로 시작하는 메서드는 테스트 메서드이다. -이것을 실행하는 프레임워크가 reflection 을 이용해서 테스트라고 시작하는지 찾아야 했는데 (중간 생략, 다음 구문이 이해가 빠름) +이것을 실행하는 프레임워크가 reflection 을 이용해서 테스트라고 시작하는지 찾아야 했는데 ~ (중간 생략, 다음 구문이 이해가 빠름) ``` -JUnit이 처음 만들어지던 때 사용했던 JDK1.1 에서는 리플렉션에서 public 메소드만 접근을 허용했습니다. 따라서 JUnit의 테스트 메소드는 모두 public일 수 밖에 없었습니다. 그게 관례가 되서 지금까지 내려온 것입니다. +JUnit이 처음 만들어지던 때 사용했던 JDK1.1 에서는 리플렉션에서 public 메소드만 접근을 허용했습니다. +따라서 JUnit의 테스트 메소드는 모두 public 일 수 밖에 없었습니다. 그게 관례가 되서 지금까지 내려온 것입니다. ``` JUnit 4 의 prefix 를 이용한 방식에서 → JUnit 5 @test 를 읽는 방식으로 바뀌었다. -public 으로 만들었던 전통이 있어서 junit 5 는 거의 새롭게 만든 프레임워크 -private 으로 만들거나 그러라는건 아니고 타이핑하기 편하게 default 그냥 만드는게 -public 굳이 만들어서 붙이는 분들이 계시는데 옛날부터 쓰셔서 그러신가보다 라고 생각하면 될 것 같음 ex) 김영한 님 - -테스트 시간 ? 수행 시간을 빨리 돌려야 한다 이건 다음에 얘기 -테스트를 만들어야 한다는 개념이 폭력적으로 나타날 때, 2007-8 즈음 -테스트 돌리는 동안 멍하니 보고만 있으면 잔소리를 했다. ex) 테스트 돌아가는 중에 다음 테스트를 위한 코드를 짜야지 -라이브 테스트 ? 켄트 백이 junit backs ??? 이클립스 플러그인 만들었엇고 -테스트 계속 짜고 있으면 백그라운드에서 계속 실행하고 있음 틀린거 계속 나타내주고 있음 +public 으로 만들었던 전통이 있어서 junit 5 는 거의 새롭게 만든 프레임워크이다. +그러나 private 으로 만들거나 그러라는건 아니고 타이핑하기 편하게 default 그냥 만들면 된다. +public 을 굳이 만들어서 붙이는 분들이 계시는데 옛날부터 쓰셔서 그러신가보다 라고 생각하면 된다. ex) 김영한 님 + +# 테스트 시간 ? +수행 시간을 빨리 돌려야 한다 이건 다음에 얘기를 더 해보도록 하자. +테스트를 만들어야 한다는 개념이 폭력적으로 나타날 때는 2007-8 즈음이었다. +테스트 돌리는 동안 멍하니 보고만 있으면 잔소리를 했다. ex) 테스트 돌아가는 중에 다음 테스트를 위한 코드를 짜야지 왜 멍하니 보고만 있나 + +## 라이브 테스트 ? +켄트 백이 junit 백이라는 이클립스 플러그인 만들었다. +테스트를 작성하고 있으면 백그라운드에서 테스트를 계속 실행하고 있다. 테스트 작성 도중에 틀린 내용을 계속 보여준다. 자바는 프레임워크 하나만 남아있는듯 ? 닷넷에서는 고급버전에서만 되는 것으로 알고 있음 해당 플러그인이 발전 못하는 이유 ? 2-3 돌리는 동안 커피 한잔 하고, 트위터도 좀 하고 이게 낙이어서 그런듯하다. -전체 테스트를 돌려놓고 sns 를 하는 분도 있는 듯 (ㅋㅋ) +전체 테스트를 돌려놓고 sns 를 하는 분도 있는 것 같다. 테스트 돌려놓고 잘 작업할 수 있다. (토비님 생각) -(토비님) +### (토비님) 테스트 코드 작성하는 것을 처음 알게된 게 2003년 Jboss 에서 알게 됐다. 오픈 소스 ejb webserver 이런거 고도화된 서버였는데 소스 코드를 받아서 보다 보니깐 테스트 폴더가 있더라. -실제 서버가 띄워지는 것을 테스트 하는 것을 보면서 (거의 2시간 걸렸다) 희열을 느꼈다. 와 이런게 가능하구나. +실제 서버가 띄워지는 것을 테스트 하는 것을 보면서 (거의 2시간 걸렸다) 희열을 느꼈다. `와 이런게 가능하구나.` ui 가 있는 화면을 레코딩하면서 기능을 테스트해 보는 (툴을 써서) - 워낙 잘 안됨 (예전에는 이런식의 테스트가 있었음) 코드로 테스트를 할 수 있다는 것을 보면서 너무 놀랐다. 전세계 사람들이 JUnit → TDD 에 빠졌다. 2006 ~ 2010년 전세계에서 테스트 작성 뭉뚱그려 가지고 테스트를 한번도 작성해 보지 못한 사람에게 `테스트 작성해 보세요` 가 들어갔고, 커버리지 몇 십프로 채우지 않으면 인사고과 점수가 낮게 나오던 시절이 있었다. 한국은 한발 늦게 이런 시기가 왔지만, 아무튼 세계적으로 이런 시절이 있었다. -그러다 테스트 회의론에 빠졌다. +**그러다 테스트 회의론에 빠졌다.** 해외에 합리적으로 일한다는 회사에서 `오늘도 회사에서 요구하는 커버리지를 채우기 위해 아무 의미도 없는 테스트를 작성하는 나를 보면서 환멸을 느낀다` 라는 글을 작성하는 사람이 생겼다. 한쪽에서는 TDD 테스트 작성에 환호를 했고, 한쪽에서는 이런 반응이 있었다. -인포큐 ??? 일주일에 적어도 한 번은 테스트에 대한 글이 올라 왔었는데, -2011 ~ 2012 년 즈음에 TDD 테스트 작성 얘기가 싹 사라짐 (암흑기) -책이 나오는 즈음에 과도하게 열광하는 사람들, 푸쉬하는 사람들, 해봤는데 생산성 떨어져서 절망에 빠진 사람들이 있었다. -(토비 님은 그 시절에) 당연히 테스트를 해야 한다고 얘기를 하고 다녔다. +인포큐 ??? 에서는 일주일에 적어도 한 번은 테스트에 대한 글이 올라 왔었을 정도로 테스트 부흥기가 있었는데, +2011 ~ 2012 년 즈음에 TDD 테스트 작성 얘기가 싹 사라졌다. (이때를 암흑기라 표현하심) +책이 나오는 즈음에 과도하게 열광하는 사람들, 푸쉬하는 사람들, 테스트를 해봤는데 생산성 떨어져서 절망(?)에 빠진 사람들이 있었다. +(그러나 토비 님은 그 시절에) `당연히 테스트를 해야 한다` 고 얘기를 하고 다녔다. 그리고 나서 테스트 작성에 대한 관점이 서서히 새로 입문하시는 개발자들부터 시작해서, 모든 기술은 나오면 테스트를 작성하는 법부터 시작을 하고, 모든 언어에 테스트를 적용하고, 오픈 소스에도 테스트를 적용하는 등 테스트의 개념이 서서히 자리를 잡아 나가기 시작했다. 그러나 그 시절의 당연히 `테스트는 작성해야 한다` 까지는 못온 것 같다. @@ -77,19 +82,18 @@ DI 콜라보레이터를 Mock, 좋다. 스턴트를 하면서 다른 사람인 척 하면서 끼어들어 하는 거 ??? 테스트 대역 ??? stuck, mock, spy, dummy, fake → **Imposter ?** (토비 님은 이 단어가 적절하지 아니한가 의견을 제시하셨다) -테스트 대역 이라는 어려운 언어 쓰지 말고 임포스터가 어떤가 ? 다른 사람인척 하고 속이면서 행동을 하는 그런 사람을 임포스터라고 하니깐 +테스트 대역 이라는 어려운 언어 쓰지 말고 `임포스터` 가 어떤가 ? 다른 사람인척 하고 속이면서 행동을 하는 그런 사람을 임포스터라고 하니깐 -(토비 님) +### (토비 님) 테스트하기 어려운 코드는 특정 구현의 코드가 종속이 많이 되어있는 경우가 그렇다. 느슨한 관계로 만들어주면 테스트하기 좋다. -객체지향적 설계로 따라가는데 -테스트가 하기 어려운 코드가 좋은 코드일리는 없는건 확실하다. (강한 결합도가 높은 코드일리가 분명) +객체지향적 설계를 따라간다고 해도 테스트하기 어려운 코드가 좋은 코드일리는 없는건 확실하다. (강한 결합도가 높은 코드일리가 분명) **테스트 하기 쉽다 → 코드 엉망진창일 수도 있다.** **기능을 잘 구현 못했으면 테스트 하기 아무리 쉽다고 해도 소용이 없는 거죠.** -Q) 테스트를 위해서 비즈니스 코드를 손대는 경우 ? +### Q) 테스트를 위해서 비즈니스 코드를 손대는 경우 ? A) (김진영 님) 유지보수를 하는 것이 테스트 코드까지라고 생각한다. 과도한 테스트도 있음 @@ -103,13 +107,13 @@ A) (연로그 님) 테스트 코드 때문에 비즈니스 로직을 추가하 따라서 테스트 코드 때문에 비즈니스 로직을 추가하지 말자라는 나만의 기준이 생겼다. A) (토비 님) 복잡한 로직이 들어가지는 않겠죠. 2장에서는 db 테스트 할 수 있는 방법이 이것밖에 없다. 하지만 이거는 결국 유저를 다루는 dao 에서는 필요로하는 기능일 테니까 타협을 한 코드라고 볼 수 있다. -db 까지 건드리는 상황을 가지고 예제를 만들었는데 -롤백 테스트라는 것을 한다. 테스트 동안에 디비에 쌓았으면 테스트가 끝나면 롤백을 하는 -dao 에 delete 가 들어가는게 이상한 것은 아닌데 그런게 없어지죠 (3장, 5장에 나옴 그런 내용) -여기는 그냥 이렇게도 테스트할 수 있습니다. +db 까지 건드리는 상황을 가지고 예제를 만들었다. +롤백 테스트라는 것을 한다. 테스트 동안에 디비에 쌓았으면 테스트가 끝나면 롤백을 하는 ~ +dao 에 delete 가 들어가는게 이상한 것은 아닌데 그런게 없어지죠 (3장, 5장에 나옴 그런 내용) 여기는 그냥 이렇게도 테스트할 수 있습니다. + 하늘이 무너지는게 아니면 해야죠. 사용할 수 있다. -문제를 일으키는 것 같다. 지저분해진다. 느껴지면 대안을 찾으면 됨. (예를 들어,) 쿼리를 직접 날려서 없애도 되고 ,,, -2장 독자들의 상황과 수준을 고려해서 이렇게 책을 쓰게 됐다. +`문제를 일으키는 것 같다. 지저분해진다.` 느껴지면 대안을 찾으면 됨. (예를 들어,) 쿼리를 직접 날려서 없애도 된다. +**2장 독자들의 상황과 수준을 고려해서 이렇게 책을 쓰게 됐다.** 트렌젝션 테스트 ? 트렌젝션이 잘 걸렸는지 아닌지 테스트하는게 어렵다. 롤백이 되는지를 테스트해야 하는데 예외가 발생을 하고 롤백이 되가지고 트렌젝션이 다 돌아가지 않고 이전까지 디비에 다 남아 있으면 트렌젝션 테스트 실패 아니면 트렌젝션 테스트 성공 예외를 데이터 셋팅 ?? 코드에다 이번 실행을 할 때 강제로 예외를 던져라 @@ -119,13 +123,12 @@ dao 에 delete 가 들어가는게 이상한 것은 아닌데 그런게 없어 테스트 코드가 비즈니스 로직을 침범하는게 좋은 것은 아니다. ??? 결론인가??? -Q) 툴을 이용해서 테스트 작성 했는데 -테스트를 위해 @MockBean, @SpyBean을 사용하는 것이 좋은 디자인이 아님에도 테스트 코드를 작성하기 편해지기 때문에 사용하지 않는 편이 좋다는 [글]([https://jojoldu.tistory.com/320](https://jojoldu.tistory.com/320)) 을 봤습니다. 이에 대한 다른 분들의 의견이 궁금합니다. +### Q) 툴을 이용해서 테스트 작성 했는데 테스트를 위해 @MockBean, @SpyBean을 사용하는 것이 좋은 디자인이 아님에도 테스트 코드를 작성하기 편해지기 때문에 사용하지 않는 편이 좋다는 [글]([https://jojoldu.tistory.com/320](https://jojoldu.tistory.com/320)) 을 봤습니다. 이에 대한 다른 분들의 의견이 궁금합니다. -(토비 님) +### (토비 님) 이동욱 님 글은 정확히 말하면 테스트 얘기라기 보다는 다른 경우에도 적용이 될만한건데 어떤 의견이 궁금한건지 구체적으로 얘기 좀 더 해달라 -(추가 Q) +### (추가 Q) 목빈, 스파이빈을 사용하지 말자는 얘기는 아닌데 2장 직접적인 내용이 아니라서 개인적으로 고민했던 부분이라서 남겼던 글 궁금했던 거는 어떠한 툴들을 사용을 해서 정석적인 방법이 있는데 인터페이스에서 DI 주입 @@ -142,59 +145,53 @@ spybean 의 경우에는 외부에 라이브러리를 가져와서 쓰는 mock 라이브러리 ~~ ? ? **인터페이스 30개쯤 다 만들어야 하는 경우** <- 이 내용이 촛점이다. mock 이나 stub 을 종종 이용한다 ? -(토비 님) +### (토비 님) 이동욱 님 글은 전에 본건데, 이 글의 요지는 여러 가지 외부 라이브러리를 모킹해서 이렇게 해서 접근하는거 어렵지 않은데 내 코드에서 직접 접근해야 하는 것을 가능하게 ~ 새로운 오브젝트를 두고 모킹은 간단하게 할 수 있으니 모킹을 할 수 있었다. (다른 이야기임) -직접 구현 ??? 쓰지 않을 것도 구현 해야 하네 ??? 기계 적으로 ???? +직접 구현 ??? 쓰지 않을 것도 구현해야 하네 ??? 기계적으로 ???? 어뎁터 오브젝트를 하나 만들어서 쓴다 → 내 코드가 평생 의존을 할건지 설계 고민이 중요 -aws 의 기능을 사용하는 코드가 필요하다. 비즈니스 로직 코드에서 aws 빈을 등록해서 의존 +**aws 의 기능을 사용하는 코드가 필요하다. 비즈니스 로직 코드에서 aws 빈을 등록해서 의존하는 구조의 ~** azure gcp 자체 클라우드 내부 서비스 이용할 수 있는데 내 코드로 바로 의존하는게 좋을까 ? -추상화된 계층을 넣는게 좋겠다. 이렇게 접근을 하면 괜찮은 접근법 +### 추상화된 계층을 넣는게 좋겠다. 이렇게 접근을 하면 괜찮은 접근법 모킹을 해서 aws 10개 중 하나만 쓰는데 목을 하고 있네 ? aws 를 계속 쓸건데 ?? 중간에 뭐를 둔다 좋은건 아닌듯 ? -내가 만든 비즈니스 코드가 추상화 , 외부 라이브러리 끌어오는거 로우 레벨인 경우 +내가 만든 비즈니스 코드가 추상화, 외부 라이브러리 끌어오는거 로우 레벨인 경우 목빈 스파이빈 편하게 써라 리모트, 비즈니스 코드에 직접 넣지 않는게 좋다. 스프링의 레스트 템플릿, 비즈니스 코어 로직이 담긴 코드에 직접 주입하는 구조 ㄴㄴ -외부 api - -목을 쓸 떄 주의점 -목 라이브러리는 런타임에 클래스 동적으로 만들어서 로딩해서 하는 복잡한 구조 -시간이 오래걸림,, junit 은 하나의 클래스에 테스트 메소드 10개 매번 새로운 인스턴스를 만듬 -목을 사용하는 무언가를 넣으면 메소드마다 목을 새로 만들어서 -인스턴스를 테스트 클래스 한번 만들때 한번만 만들도록 하라 +### 외부 api +**Mock 을 쓸 떄 주의점** +Mock 라이브러리는 런타임에 클래스 동적으로 만들어서 로딩해서 하는 복잡한 구조이다. +시간이 오래걸린다. junit 은 하나의 클래스에 테스트 메소드 10개 매번 새로운 인스턴스를 만듬 +목을 사용하는 무언가를 넣으면 메소드마다 목을 새로 만들어서 ~ +### 인스턴스를 테스트 클래스 한번 만들때 한번만 만들도록 하라 (???) 퍼 클래스 ?????? → 다른 기준 하나를 깨뜨리게 되는 것 -Q) 인수테스트를 할 시 `사용자 시나리오`에 맞춰 수행해야 합니다. -이때 사용자를 프론트엔드로 설정하고, 특정 API에 대한 테스트를 진행하는 방법이 있습니다. -사용자를 우리 서비스의 유저로 설정하고, 여러 API 의 상호작용에 대해 테스트를 진행할 수도 있습니다. -사용자를 어떤 층으로 정해 테스트를 하는게 좋을지 궁금합니다 +### Q) 인수테스트를 할 시 `사용자 시나리오`에 맞춰 수행해야 합니다. 이때 사용자를 프론트엔드로 설정하고, 특정 API에 대한 테스트를 진행하는 방법이 있습니다. 사용자를 우리 서비스의 유저로 설정하고, 여러 API 의 상호작용에 대해 테스트를 진행할 수도 있습니다. 사용자를 어떤 층으로 정해 테스트를 하는게 좋을지 궁금합니다. -인수테스트 → 토비 님 별로 안좋아함 +인수테스트 → 토비 님 별로 안좋아하시는 것 같다. 처음 단위 테스트라는 개념이 들어왔을 때, 혼란했다. -qa 테스트를 하시는 분들 입장에서는 단위 테스트라는 것은 기능 단위였다. 예를 들어, 회원 가입 -JUnit 에서 말하는 단위테스트는 무엇인가 ? +qa 테스트를 하시는 분들 입장에서는 단위 테스트라는 것은 기능 단위였다. **예를 들어, 회원 가입** -단위테스트는 돌아가는 동안 db 엑세스를 하지 않는 등의 외부와 엑세스가 없어야 한다. -더 나아가는 경우 클래스 한개만 테스트해야 단위 테스트다라고 말하기도 한다. +### 그렇다면 JUnit 에서 말하는 단위테스트는 무엇인가 ? +단위테스트는 돌아가는 동안 `db 엑세스를 하지 않는 등의 외부와 엑세스가 없어야 한다.` +더 나아가는 경우 `클래스 한개만 테스트해야` 단위 테스트다라고 말하기도 한다. +`그러나 작은 메소드 하나, 큰 경우는 회원 가입 하나`를 단위 테스트라고도 말한다. (토비 님) 난 이것도 공감한다. -그러나 작은 메소드 하나, 큰 경우는 회원 가입 하나를 단위 테스트라고도 말한다. (토비 님) 난 이것도 공감한다. db 엑세스 하면 안된다는 이유는 테스트 결과가 동일해야 한다. 독립적이어야 한다. (극단적으로) 100 번 1000번 똑같은 결과가 나와야 한다. 예를 들어, 테스트 순서가 54321 이던 12345 이던 결과가 다 똑같아야 한다. → 이걸 전제로 깔고 하면 어떤식으로 해도 상관 없다. -나중에는 `개발자 테스트` 라는 단어를 씀 (켄트백) +그래서 나중에는 켄트백이 `개발자 테스트` 라는 단어를 사용했다. + 인수테스트 > 뭐라고 정의하느냐에 따라서 달라질듯 ? 테스트 코드로 어떻게 해보려고 했지만 시나리오가 계속 변해야 하니깐 안정성의 이유로 테스트 자동화가 힘드니깐 qa 한테 맡기는게 좋겠다. 이런 의견도 있다. -(주승 님) -Q) (커다란 기능을 TDD로 개발한다면) 테스트 코드를 작성할 때 하나의 기능 내부 구현을 블랙박스로 보고 외부 인터페이스를 테스트 하려는 경향이 개인적으로 있습니다. 그런데 만약 TDD를 시도하는 경우인데 내부 구현에 참여하는 모듈이 다양하고 규모가 꽤 있는 경우 테스트 코드를 작성하고 테스트를 실행하기 까지 꽤 오랜 시간이 걸리는 경우가 있는 것 같습니다. 그래서 여러가지 문제를 만납니다. 여러 모듈이 있다보니 어디서 문제가 발생했는지 알기 어렵거나 테스트를 통해 즉시 확인 받으면서 코드를 작성하지 않으니 불안함이 이어지는 것도 같습니다. 이럴 때 블랙 박스 내부 구현 모듈 하나하나를 단위테스트 코드로 더 쪼개서 작성하는 편이신가요? (질문을 정리하다 보니 이와 같은 문제가 있다면 더 작은 단위의 내부 모듈로 테스트를 분리하는 것이 좋겠다는 생각이 들긴 합니다) -외부 구현 블랙박스 내부 구현 테스트 -TDD 모듈 많이 참여하고 규모가 좀 있는 경우 테스트 코드를 먼저 짜고 다 만들기 까지 텀이 길다. 어느 모듈에 문제가 있는지 ?? +### Q) (커다란 기능을 TDD로 개발한다면) 테스트 코드를 작성할 때 하나의 기능 내부 구현을 블랙박스로 보고 외부 인터페이스를 테스트 하려는 경향이 개인적으로 있습니다. 그런데 만약 TDD를 시도하는 경우인데 내부 구현에 참여하는 모듈이 다양하고 규모가 꽤 있는 경우 테스트 코드를 작성하고 테스트를 실행하기 까지 꽤 오랜 시간이 걸리는 경우가 있는 것 같습니다. 그래서 여러가지 문제를 만납니다. 여러 모듈이 있다보니 어디서 문제가 발생했는지 알기 어렵거나 테스트를 통해 즉시 확인 받으면서 코드를 작성하지 않으니 불안함이 이어지는 것도 같습니다. 이럴 때 블랙 박스 내부 구현 모듈 하나하나를 단위테스트 코드로 더 쪼개서 작성하는 편이신가요? (질문을 정리하다 보니 이와 같은 문제가 있다면 더 작은 단위의 내부 모듈로 테스트를 분리하는 것이 좋겠다는 생각이 들긴 합니다) 외부 구현 블랙박스 내부 구현 테스트 TDD 모듈 많이 참여하고 규모가 좀 있는 경우 테스트 코드를 먼저 짜고 다 만들기 까지 텀이 길다. 어느 모듈에 문제가 있는지 ?? -(토비 님) +### (토비 님) TDD 를 많이 하시나요 ? (개인적으로 하고 있다) (교육을 받는 분들) TDD 를 한다. 테스트는 TDD 로 작성해야 한다 ? 그냥 그런게 있다고 들었다. @@ -204,17 +201,17 @@ TDD 를 많이 하시나요 ? (개인적으로 하고 있다) 논쟁이 많고 어려운 주제다. TDD 도 테스트를 작성하는 방법 중 하나라 생각한다. 테스트 작성하는 건 좋다 (모두 동감할 거고) -얼마나 시간이 지나서 작성하는게 좋은데 ? 테스트 작성은 코드를 작성하고 나서 빨리 하는게 좋지 않은가 ? +**얼마나 시간이 지나서 작성하는게 좋은데 ? 테스트 작성은 코드를 작성하고 나서 빨리 하는게 좋지 않은가 ?** 오늘 만든 코드는 이번주가 끝나기 전에 테스트 코드를 작성하자는 기준을 잡는 것도 괜찮다. (구글의 어떤 개발자, 금요일에 다른 일은 하지 않고 월 ~ 목요일에 만든 코드에 대한 테스트 코드를 작성) -고로 테스트를 빨리 만드는게 좋다. 빨리 검증하면 검증할수록 좋다. +### 고로 테스트를 빨리 만드는게 좋다. 빨리 검증하면 검증할수록 좋다. 코드를 작성하고 한 시간 이내 ? 10분 이내 ? 이런 생각을 하다 보면 어디까지 가냐면 **즉시 그 코드에 대한 피드백을 줄 수 있는 테스트 코드를 미리 작성해야 한다.** 는 생각까지 간다. (TLP 테스트 라스트 ~ ) TDD 테스트를 먼저 작성하고 코드 ~~ 시간의 문제라고 생각할 요지가 있어서 TDD 라 이름을 바꿈 -빨리 받을 수록 좋다. 얼마나 빨리 받는게 좋은가 ? 즉시 +빨리 받을 록 좋다. 얼마나 빨리 받는게 좋은가 ? **즉시** -**즉시 받는 방법, 즉 미리 만들어 놓는 것이다. 따라서 극단까지 밀어붙이면 TDD 가 되는 것이다.** +### 즉시 받는 방법, 즉 미리 만들어 놓는 것이다. 따라서 극단까지 밀어붙이면 TDD 가 되는 것이다. 시간의 순서 ? 켄트백 TDD 서문에서 테스트 주도 개발은 코드를 만들면 뭘 만들지 생각하고 코드를 만들고 5분 이내에 테스트 ~ ㄴㄴ @@ -233,10 +230,10 @@ TDD 테스트를 먼저 작성하고 코드 ~~ 필요하면 프라이빗 메소드도 테스트 해라. 프라이빗 메소드 체크할 수 있는 툴이 많다. 더 좋은건 설계를 잘해서 분리하는 방향도 있다. -Q) (제한된 시간과 포괄성의 트레이드오프) 현업에서 일하다보면 늘 시간이라는 제약을 마주합니다. 제한된 시간은 포괄적이라는 개념을 완벽하게 적용할 수 없게 하는 제약이 되는데요. 현업에서 제한된 시간 안에 포괄적인 테스트를 작성하는 지혜에 대해 나누어 보고 싶습니다. 예를 들면 어떤 종류의 테스트는 이럴 때 포기하기도 한다. 어떤 종류의 테스트는 꼭 사수한다와 같은 것들이요. (질문을 하고 답변을 생각해 보니) 저는 일정이 촉박한 경우는 모든 기능의 성공케이스만 일단 테스트 케이스를 작성하고 예외 케이스는 단위 테스트를 하면서 발견되는 예외 상황만 추가로 작성하는 등의 방법을 사용하는 것 같아요. +### Q) (제한된 시간과 포괄성의 트레이드오프) 현업에서 일하다보면 늘 시간이라는 제약을 마주합니다. 제한된 시간은 포괄적이라는 개념을 완벽하게 적용할 수 없게 하는 제약이 되는데요. 현업에서 제한된 시간 안에 포괄적인 테스트를 작성하는 지혜에 대해 나누어 보고 싶습니다. 예를 들면 어떤 종류의 테스트는 이럴 때 포기하기도 한다. 어떤 종류의 테스트는 꼭 사수한다와 같은 것들이요. (질문을 하고 답변을 생각해 보니) 저는 일정이 촉박한 경우는 모든 기능의 성공케이스만 일단 테스트 케이스를 작성하고 예외 케이스는 단위 테스트를 하면서 발견되는 예외 상황만 추가로 작성하는 등의 방법을 사용하는 것 같아요. si 프로젝트 예외 테스트를 먼저하라는 글이 있는데 성공하는 테스트 케이스 먼저 진행하고 나중에 예외 테스트를 한다. -(토비 님) +### (토비 님) 버그가 발생하면 보완 제일 어려운 디버깅은 논리적인 버그를 잡는 일이다. 제일 쉬운 널포인트 디버깅은 exception 을 추적하기만 하면 된다. 금방 추적이 된다. 문제 해결이 쉽다. From 21f91c64e02747720cc70295fe145f1060c82771 Mon Sep 17 00:00:00 2001 From: caoyu-dev Date: Tue, 20 Sep 2022 10:33:53 +0900 Subject: [PATCH 07/11] =?UTF-8?q?2=EC=9E=A5=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0\353\224\224_\354\240\225\353\246\254.md" | 110 ++++++++---------- 1 file changed, 49 insertions(+), 61 deletions(-) diff --git "a/ch2/2\354\236\245_\355\205\214\354\212\244\355\212\270_\354\212\244\355\204\260\353\224\224_\354\240\225\353\246\254.md" "b/ch2/2\354\236\245_\355\205\214\354\212\244\355\212\270_\354\212\244\355\204\260\353\224\224_\354\240\225\353\246\254.md" index 77c580e..97439e0 100644 --- "a/ch2/2\354\236\245_\355\205\214\354\212\244\355\212\270_\354\212\244\355\204\260\353\224\224_\354\240\225\353\246\254.md" +++ "b/ch2/2\354\236\245_\355\205\214\354\212\244\355\212\270_\354\212\244\355\204\260\353\224\224_\354\240\225\353\246\254.md" @@ -14,9 +14,9 @@ B-to-B 에서 테스트는 하나의 문서화가 될 수 있다는 것이 장 ### (토비 님) `네거티브 테스트` 를 우선적으로 만드는 게 좋다고 생각한다. -이전에는 JUnit 테스트는 왜 public 으로만 만들어야 했는가 ? +과거의 JUnit 테스트는 왜 public 으로만 만들어야 했는가 ? 원래 설계에서의 JUnit 은 클래스 하나에 테스트 메서드 하나만 만들어야 했다. -→ 이러다가 비슷한 테스트를 클래스 하나에 밀어 넣게 되었다. utility 등 ~? +→ 이러다가 비슷한 테스트를 클래스 하나에 밀어 넣게 되었다. utility 등 소문자 test 로 시작하는 메서드는 테스트 메서드이다. 이것을 실행하는 프레임워크가 reflection 을 이용해서 테스트라고 시작하는지 찾아야 했는데 ~ (중간 생략, 다음 구문이 이해가 빠름) @@ -41,9 +41,9 @@ public 을 굳이 만들어서 붙이는 분들이 계시는데 옛날부터 쓰 테스트를 작성하고 있으면 백그라운드에서 테스트를 계속 실행하고 있다. 테스트 작성 도중에 틀린 내용을 계속 보여준다. 자바는 프레임워크 하나만 남아있는듯 ? 닷넷에서는 고급버전에서만 되는 것으로 알고 있음 -해당 플러그인이 발전 못하는 이유 ? 2-3 돌리는 동안 커피 한잔 하고, 트위터도 좀 하고 이게 낙이어서 그런듯하다. -전체 테스트를 돌려놓고 sns 를 하는 분도 있는 것 같다. -테스트 돌려놓고 잘 작업할 수 있다. (토비님 생각) +해당 플러그인이 발전 못하는 이유 ? 테스트를 돌리는 2 ~ 3분 동안 커피 한 잔 하고, 트위터도 좀 하고 이게 낙이어서 그런듯 하다. +더불어 전체 테스트를 돌려놓고 sns 를 하는 분도 있는 것 같다. +(토비님 생각) 테스트 돌려놓고 잘 작업할 수 있다. ### (토비님) 테스트 코드 작성하는 것을 처음 알게된 게 2003년 Jboss 에서 알게 됐다. 오픈 소스 ejb webserver 이런거 고도화된 서버였는데 소스 코드를 받아서 보다 보니깐 테스트 폴더가 있더라. @@ -58,7 +58,7 @@ ui 가 있는 화면을 레코딩하면서 기능을 테스트해 보는 (툴을 해외에 합리적으로 일한다는 회사에서 `오늘도 회사에서 요구하는 커버리지를 채우기 위해 아무 의미도 없는 테스트를 작성하는 나를 보면서 환멸을 느낀다` 라는 글을 작성하는 사람이 생겼다. 한쪽에서는 TDD 테스트 작성에 환호를 했고, 한쪽에서는 이런 반응이 있었다. -인포큐 ??? 에서는 일주일에 적어도 한 번은 테스트에 대한 글이 올라 왔었을 정도로 테스트 부흥기가 있었는데, +인포큐 (?) 에서는 일주일에 적어도 한 번은 테스트에 대한 글이 올라 왔었을 정도로 테스트 부흥기가 있었는데, 2011 ~ 2012 년 즈음에 TDD 테스트 작성 얘기가 싹 사라졌다. (이때를 암흑기라 표현하심) 책이 나오는 즈음에 과도하게 열광하는 사람들, 푸쉬하는 사람들, 테스트를 해봤는데 생산성 떨어져서 절망(?)에 빠진 사람들이 있었다. (그러나 토비 님은 그 시절에) `당연히 테스트를 해야 한다` 고 얘기를 하고 다녔다. @@ -68,47 +68,47 @@ ui 가 있는 화면을 레코딩하면서 기능을 테스트해 보는 (툴을 그 사이에 테스트에 가볍게 접근하고 JUnit 으로 작성하는 것과 TDD 로 작성하는 것 구분 못하는 사람 많다. 테스트 코드 작성과 TDD 작성이 구분되지 않은채 퍼졌었다. (많이 퍼지던 시기에) -사람들이 열광헀다 → 실망 → 다시 테스트를 작성하기 시작하는 싸이클을 보면서 테스트에 대한 생각과 느낀점이 많다. +`사람들이 열광헀다 → 실망 → 다시 테스트를 작성` 하기 시작하는 싸이클을 보면서 테스트에 대한 생각과 느낀점이 많다. 이 책을 쓰던 시절에는 한번이라도 테스트를 작성하면 좋겠다는 생각에 책을 썼다. 스프링 이전에는 테스트 만들기가 너무 어려웠다. (비싼 툴) Junit, TDD 서버 어플리케이션 테스트 하려면 너무 어려웠다. -스프링 많은 부분에서 지원하려고 노력을 많이 함. -DI 콜라보레이터를 Mock, 좋다. +스프링은 많은 부분에서 지원하려고 노력을 많이 했다. ex) DI 콜라보레이터를 Mock, 좋다. -테스트 코드를 하나 돌렸더니 모든 게 다 돌아가는 ~ ?? 디버그 ??? -테스트 더블 ???? ????? Test Double +### 테스트 더블(Test Double) 스턴트 더블(Stunt double) -스턴트를 하면서 다른 사람인 척 하면서 끼어들어 하는 거 ??? 테스트 대역 ??? -stuck, mock, spy, dummy, fake → **Imposter ?** (토비 님은 이 단어가 적절하지 아니한가 의견을 제시하셨다) - -테스트 대역 이라는 어려운 언어 쓰지 말고 `임포스터` 가 어떤가 ? 다른 사람인척 하고 속이면서 행동을 하는 그런 사람을 임포스터라고 하니깐 +스턴트를 하면서 다른 사람인 척 하면서 끼어들어 하는 스턴트 더블에서 비롯되었다. 테스트 대역 +ex) stuck, mock, spy, dummy, fake +테스트 대역 이라는 어려운 언어 쓰지 말고 `Imposter` 가 어떤가 ? 다른 사람인척 하고 속이면서 행동을 하는 그런 사람을 임포스터라고 하니깐 ### (토비 님) -테스트하기 어려운 코드는 특정 구현의 코드가 종속이 많이 되어있는 경우가 그렇다. +테스트하기 어려운 코드는 특정 구현의 코드가 종속이 많이 되어 있는 경우가 그렇다. 느슨한 관계로 만들어주면 테스트하기 좋다. -객체지향적 설계를 따라간다고 해도 테스트하기 어려운 코드가 좋은 코드일리는 없는건 확실하다. (강한 결합도가 높은 코드일리가 분명) +객체지향적 설계를 따라간다고 해도 테스트하기 어려운 코드가 좋은 코드일리가 없는 것은 확실하다. (강한 결합도가 높은 코드일리가 분명하기 때문) **테스트 하기 쉽다 → 코드 엉망진창일 수도 있다.** -**기능을 잘 구현 못했으면 테스트 하기 아무리 쉽다고 해도 소용이 없는 거죠.** +기능이 잘 구현되지 않았으면 테스트 하기 아무리 쉽다고 해도 소용이 없는 거다. ### Q) 테스트를 위해서 비즈니스 코드를 손대는 경우 ? -A) (김진영 님) 유지보수를 하는 것이 테스트 코드까지라고 생각한다. +### A) (김진영 님) +유지보수를 하는 것이 테스트 코드까지라고 생각한다. 과도한 테스트도 있음 테스트 코드 조차도 잘 짜야 하는 코드라고 생각한다. 프로덕션 코드와 테스트 코드가 그렇게 다른가 ? 1급 시민, 2급, ~ 전 다 중요하다 명세 관련해서 스웨거를 붙이는건 안좋아하는데 테스트 코드를 위해서 deleteAll(), get() 같은 비즈니스 로직을 추가하는 건 좋았다. -A) (연로그 님) 테스트 코드 때문에 비즈니스 로직을 추가하는 것은 안좋아한다. +### A) (연로그 님) +테스트 코드 때문에 비즈니스 로직을 추가하는 것은 안좋아한다. 더 많은 로직들이 테스트 때문에 복잡해지는 상황이 생긴다. 따라서 테스트 코드 때문에 비즈니스 로직을 추가하지 말자라는 나만의 기준이 생겼다. -A) (토비 님) 복잡한 로직이 들어가지는 않겠죠. 2장에서는 db 테스트 할 수 있는 방법이 이것밖에 없다. 하지만 이거는 결국 유저를 다루는 dao 에서는 필요로하는 기능일 테니까 타협을 한 코드라고 볼 수 있다. +### A) (토비 님) +복잡한 로직이 들어가지는 않겠죠. 2장에서는 db 테스트 할 수 있는 방법이 이것밖에 없다. 하지만 이거는 결국 유저를 다루는 dao 에서는 필요로하는 기능일 테니까 타협을 한 코드라고 볼 수 있다. db 까지 건드리는 상황을 가지고 예제를 만들었다. -롤백 테스트라는 것을 한다. 테스트 동안에 디비에 쌓았으면 테스트가 끝나면 롤백을 하는 ~ +롤백 테스트라는 것을 한다. 테스트 동안에 디비에 쌓았으면 테스트가 끝나면 롤백을 하는 형식의 테스트를 롤백 테스트라 한다. dao 에 delete 가 들어가는게 이상한 것은 아닌데 그런게 없어지죠 (3장, 5장에 나옴 그런 내용) 여기는 그냥 이렇게도 테스트할 수 있습니다. 하늘이 무너지는게 아니면 해야죠. 사용할 수 있다. @@ -120,7 +120,7 @@ dao 에 delete 가 들어가는게 이상한 것은 아닌데 그런게 없어 (뭔말이지) 트렌젝션이 잘 동작하는가 ? (뭔소리지?) -테스트 코드가 비즈니스 로직을 침범하는게 좋은 것은 아니다. ??? 결론인가??? +테스트 코드가 비즈니스 로직을 침범하는게 좋은 것은 아니다. 결론인가(?) ### Q) 툴을 이용해서 테스트 작성 했는데 테스트를 위해 @MockBean, @SpyBean을 사용하는 것이 좋은 디자인이 아님에도 테스트 코드를 작성하기 편해지기 때문에 사용하지 않는 편이 좋다는 [글]([https://jojoldu.tistory.com/320](https://jojoldu.tistory.com/320)) 을 봤습니다. 이에 대한 다른 분들의 의견이 궁금합니다. @@ -129,44 +129,33 @@ dao 에 delete 가 들어가는게 이상한 것은 아닌데 그런게 없어 이동욱 님 글은 정확히 말하면 테스트 얘기라기 보다는 다른 경우에도 적용이 될만한건데 어떤 의견이 궁금한건지 구체적으로 얘기 좀 더 해달라 ### (추가 Q) -목빈, 스파이빈을 사용하지 말자는 얘기는 아닌데 -2장 직접적인 내용이 아니라서 개인적으로 고민했던 부분이라서 남겼던 글 -궁금했던 거는 어떠한 툴들을 사용을 해서 정석적인 방법이 있는데 인터페이스에서 DI 주입 -모키토에 given when then -어떤 방법을 사용하고 그 이유는 뭐인지 어떤게 정석적인 방법이고, -spybean 의 경우에는 외부에 라이브러리를 가져와서 쓰는 -모킹하는 방법으로 사용한 적이 있는데 -구현하지 않는 코드에 대해서 -필요한 메서드, 필요하지 않은 메서드 ? ??? -설정을 해줘야 하는 케이스가 있었고 라이브러리 ~~?? -2번이랑 연관된 이야기 -어떤식으로 ????? ㅇ결과를 내는지 ? 어떻게 사용을 하는지 -모킹을 어떻게든 해야 하는 상황인데 컨트롤 할 수 없는 경우 직접 사용하게 할 수 없고 -mock 라이브러리 ~~ ? ? **인터페이스 30개쯤 다 만들어야 하는 경우** <- 이 내용이 촛점이다. +궁금했던 거는 어떠한 툴들을 사용을 해서 정석적인 방법이 있는데, 예를 들어 인터페이스에서 DI 주입하는 방법, +Mockito 에서는 given when then 의 방법 등 이중에서 **1) 어떤 방법을 사용하고 해당 방법을 사용하는 이유는 무엇인지, 또 2) 어떤게 정석적인 방법인지** 궁금하다. + +spybean 의 경우에는 외부에 라이브러리를 가져와서 쓰는 툴이다. +이를 모킹하는 방법으로 사용한 적이 있는데, 구현하지 않는 코드에 대해서 필요한 메서드, 필요하지 않은 메서드를 전부 물려서 사용하는게 맞는 건가 ? + +모킹을 어떻게든 해야하는 상황인데 컨트롤할 수 없는 경우 직접 사용하게 할 수 없고 결국 **인터페이스 30개쯤 다 만들어야 하는 경우** 가 생기는데 어떻게 해야 하나? (이 내용이 초점인 것 같다.) mock 이나 stub 을 종종 이용한다 ? ### (토비 님) -이동욱 님 글은 전에 본건데, 이 글의 요지는 여러 가지 외부 라이브러리를 모킹해서 이렇게 해서 접근하는거 어렵지 않은데 내 코드에서 직접 접근해야 하는 것을 가능하게 ~ 새로운 오브젝트를 두고 모킹은 간단하게 할 수 있으니 모킹을 할 수 있었다. (다른 이야기임) -직접 구현 ??? 쓰지 않을 것도 구현해야 하네 ??? 기계적으로 ???? -어뎁터 오브젝트를 하나 만들어서 쓴다 → 내 코드가 평생 의존을 할건지 설계 고민이 중요 -**aws 의 기능을 사용하는 코드가 필요하다. 비즈니스 로직 코드에서 aws 빈을 등록해서 의존하는 구조의 ~** -azure gcp 자체 클라우드 내부 서비스 이용할 수 있는데 내 코드로 바로 의존하는게 좋을까 ? -### 추상화된 계층을 넣는게 좋겠다. 이렇게 접근을 하면 괜찮은 접근법 -모킹을 해서 aws 10개 중 하나만 쓰는데 목을 하고 있네 ? aws 를 계속 쓸건데 ?? -중간에 뭐를 둔다 좋은건 아닌듯 ? -내가 만든 비즈니스 코드가 추상화, 외부 라이브러리 끌어오는거 로우 레벨인 경우 -목빈 스파이빈 편하게 써라 -리모트, 비즈니스 코드에 직접 넣지 않는게 좋다. -스프링의 레스트 템플릿, 비즈니스 코어 로직이 담긴 코드에 직접 주입하는 구조 ㄴㄴ +이동욱 님 글은 전에 본건데, 이 글의 요지는 여러 가지 외부 라이브러리를 모킹해서 접근하는 건 어렵지 않은데 내 코드에서 직접 접근해야 하는 것을 가능하게 ~ 새로운 오브젝트를 두고 모킹은 간단하게 할 수 있으니 모킹을 할 수 있었다. (다른 이야기임) +직접 구현해야 하나 ? 쓰지 않을 것도 구현해야 하네 ? 기계적으로 ? 등의 의문이 남는다. +`어뎁터 오브젝트를 하나 만들어서 쓴다` → 내 코드가 평생 의존을 할건지에 대한 설계 고민이 필요하다. +**aws 의 기능을 사용하는 코드가 필요하다. (비즈니스 로직 코드에서 aws 빈을 등록해서 의존하는 구조)** +나중에는 azure gcp 등 자체 클라우드 내부 서비스 이용할 수 있는데 내 코드로 바로 의존하는게 좋을까 ? +### (결국) 추상화된 계층을 넣는게 좋겠다. 이렇게 접근을 하는 것이 괜찮은 접근법이다. +모킹을 해서 aws 10개 기능 중 하나만 쓰는데 Mock 을 하고 있네 ? 그러나 이 aws 서비스를 서비스 종료 직전까지 계속 쓸거다 ? 이런 경우는 계속 쓰면 된다. 이런 상황에서는 중간에 뭐를 둔다는 것은 좋은 상황이 아닐 수 있다. +내가 만든 비즈니스 코드의 추상화 레벨을 따져 봐야 한다. 외부 라이브러리 끌어오는 내용이 로우 레벨인 경우 추상화를 사용하는 것이 낫다. +결론은 목빈 스파이빈 편하게 써라. 리모트, 비즈니스 코드에 직접 넣지 않는게 좋다. +스프링의 레스트 템플릿, 비즈니스 코어 로직이 담긴 코드에 직접 주입하는 구조는 ㄴㄴ. ### 외부 api **Mock 을 쓸 떄 주의점** Mock 라이브러리는 런타임에 클래스 동적으로 만들어서 로딩해서 하는 복잡한 구조이다. 시간이 오래걸린다. junit 은 하나의 클래스에 테스트 메소드 10개 매번 새로운 인스턴스를 만듬 -목을 사용하는 무언가를 넣으면 메소드마다 목을 새로 만들어서 ~ -### 인스턴스를 테스트 클래스 한번 만들때 한번만 만들도록 하라 - -(???) 퍼 클래스 ?????? → 다른 기준 하나를 깨뜨리게 되는 것 +목을 사용하는 무언가를 넣으면 각각의 메소드마다 목을 새로 만들어서 사용한다. +### 인스턴스를 테스트 클래스 한번 만들때 한번만 만들도록 하라. ### Q) 인수테스트를 할 시 `사용자 시나리오`에 맞춰 수행해야 합니다. 이때 사용자를 프론트엔드로 설정하고, 특정 API에 대한 테스트를 진행하는 방법이 있습니다. 사용자를 우리 서비스의 유저로 설정하고, 여러 API 의 상호작용에 대해 테스트를 진행할 수도 있습니다. 사용자를 어떤 층으로 정해 테스트를 하는게 좋을지 궁금합니다. @@ -217,16 +206,15 @@ TDD 테스트를 먼저 작성하고 코드 ~~ **내가 작성한 코드의 피드백을 받는 사이클을 컨트롤 하는 기법이다. 아이디어를 생각하고 그 코드가 작성이 되고 피드백을 받는 시간의 간격을 갭이라 하는데 어떻게 조절할 수 있는지 컨트롤할 수 있는지가 TDD 이다.** **내가 그 피드백을 받는 갭을 의식하고 컨트롤 하고 있었으면 그것을 TDD라 한다. (켄트백)** -2주 정도 계속 코딩하다가 피드백을 받아도 괜찮다. (안정감이 있으면 괜찮다) +2주 정도 계속 코딩하다가 피드백을 받아도 괜찮다. (안정감이 있으면 괜찮다) 불안함이 있으면 더 빨리 피드백을 받을 수 있는 방법을 찾아라. 블랙박스의 경계를 어디로 치느냐 결정하기 나름이 아닌가 ? 작은 모듈을 만들고 너무 덩치가 큰 모듈이 되지 않도록 나누면 피드백을 받을 수 있는 주기를 관리를 할 수 있는게 아닌가 ? -테스트를 언제 받는지는 중요하지 않다고 생각함 -일주일 동안 불안감을 느낀다 ?? 나중에 테스트 어떻게 하려고 그런가 ??? (어휴 그러면 안돼요 ~~~) +### 테스트를 언제 받는지는 중요하지 않다고 생각한다. +일주일 동안 불안감을 느끼면 나중에 테스트 어떻게 하려고 하는가 ? (어휴 그러면 안된다) 작게 쪼개서 만드는게 좋다. 블랙박스 테스트가 만능은 아니다. (화이트 박스 테스트는 블랙 박스 테스트의 반대말이라고 하는데 하얀 박스는 안이 안보이니깐 글래스 박스 테스트라고 하자는 의견이 있다) -프라이빗 메소드를 테스트한다던가 외부에 공개하면 안되는 메소드를 테스트하는 것 ~?? 필요하면 프라이빗 메소드도 테스트 해라. 프라이빗 메소드 체크할 수 있는 툴이 많다. 더 좋은건 설계를 잘해서 분리하는 방향도 있다. @@ -234,18 +222,18 @@ TDD 테스트를 먼저 작성하고 코드 ~~ si 프로젝트 예외 테스트를 먼저하라는 글이 있는데 성공하는 테스트 케이스 먼저 진행하고 나중에 예외 테스트를 한다. ### (토비 님) -버그가 발생하면 보완 +버그가 발생하면 보완하면 된다. 제일 어려운 디버깅은 논리적인 버그를 잡는 일이다. 제일 쉬운 널포인트 디버깅은 exception 을 추적하기만 하면 된다. 금방 추적이 된다. 문제 해결이 쉽다. -논리적인 버그가 어려움. 프로그램 문제가 없는데 일주일에 한 번씩 틀어진다던가 +논리적인 버그가 어려움. 프로그램 문제가 없는데 일주일에 한 번씩 틀어진다던가하는 문제는 잡기 참 어렵다. 포괄적인 테스트를 작성하다보면 해결되기는 한다. 정수를 입력 받는다. 입력값을 어떻게 받아야 포괄적인 테스트가 되는가 ? 균등 분할, 경계값 분할 -랜덤한 값을 500번, 1000번 파라메터 값을 넣고 테스트를 하는 것 보다는 +랜덤한 값을 500번, 1000번 파라메터 값을 넣고 테스트를 하는 것 보다는 양의 정수, 음에 정수, 0 (포괄적인 세 가지 경우) 를 놓고 테스트를 하는게 낫다. 분할을 잘하면 조합을 잘하면 테스트가 짧다. -완벽함을 추구한다고 1000번이나 되는 테스트를 진행한다고 치자 랜덤한 숫자로만 테스트를 한다고 했는데 양수만 1000번이 나오는 테스트를 진행한다면 ? 음수가 들어면 꽝 +완벽함을 추구한다고 1000번이나 되는 테스트를 진행한다고 치자 랜덤한 숫자로만 테스트를 한다고 했는데 양수만 1000번이 나오는 테스트를 진행한다면 ? 음수가 들어면 꽝이다. 경계값, 숫자를 다루는 경우 반올림 오차에서 문제 생긴다. 쌓이면 큰 잘못된 값을 만들어낸다. 경계값 주변의 작은 숫자 테스트 From 4e627e7724e1a005e6ea0cefe569013331aee54f Mon Sep 17 00:00:00 2001 From: caoyu-dev Date: Mon, 26 Sep 2022 01:42:17 +0900 Subject: [PATCH 08/11] =?UTF-8?q?[3=EC=A3=BC=EC=B0=A8]=203=EC=9E=A5=20?= =?UTF-8?q?=ED=85=9C=ED=94=8C=EB=A6=BF=20-=20=EC=A1=B0=EC=9C=A0=EB=AF=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...\212\244\355\204\260\353\224\224_\354\240\225\353\246\254.md" | 1 + 1 file changed, 1 insertion(+) create mode 100644 "ch3/3\354\236\245_\355\205\234\355\224\214\353\246\277_\354\212\244\355\204\260\353\224\224_\354\240\225\353\246\254.md" diff --git "a/ch3/3\354\236\245_\355\205\234\355\224\214\353\246\277_\354\212\244\355\204\260\353\224\224_\354\240\225\353\246\254.md" "b/ch3/3\354\236\245_\355\205\234\355\224\214\353\246\277_\354\212\244\355\204\260\353\224\224_\354\240\225\353\246\254.md" new file mode 100644 index 0000000..55e0bcc --- /dev/null +++ "b/ch3/3\354\236\245_\355\205\234\355\224\214\353\246\277_\354\212\244\355\204\260\353\224\224_\354\240\225\353\246\254.md" @@ -0,0 +1 @@ +# 3장 템플릿 From f8bf18f73807d4f6b823a75ec90c98c5674bd44c Mon Sep 17 00:00:00 2001 From: caoyu-dev Date: Thu, 29 Sep 2022 00:11:54 +0900 Subject: [PATCH 09/11] =?UTF-8?q?3=EC=9E=A5=20=ED=85=9C=ED=94=8C=EB=A6=BF?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...04\260\353\224\224_\354\240\225\353\246\254.md" | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git "a/ch3/3\354\236\245_\355\205\234\355\224\214\353\246\277_\354\212\244\355\204\260\353\224\224_\354\240\225\353\246\254.md" "b/ch3/3\354\236\245_\355\205\234\355\224\214\353\246\277_\354\212\244\355\204\260\353\224\224_\354\240\225\353\246\254.md" index 55e0bcc..e0ecaeb 100644 --- "a/ch3/3\354\236\245_\355\205\234\355\224\214\353\246\277_\354\212\244\355\204\260\353\224\224_\354\240\225\353\246\254.md" +++ "b/ch3/3\354\236\245_\355\205\234\355\224\214\353\246\277_\354\212\244\355\204\260\353\224\224_\354\240\225\353\246\254.md" @@ -1 +1,15 @@ # 3장 템플릿 +## (토비 님) +여러 종류의 템플릿으로 끝나는 기술들이 있고 여러분들이 접하실 가능성도 꽤 있고, 간단한 것만 쓰면 감춰져서 안보이는 부분에서 동작하는 경우도 많이 있다. +하지만 이제 여기에 적용된 원리들로 스프링이 추구하는 객체지향적인 설계 기법들을 이해할 수 있다. 이는 아주 오래된 전통적인 그런 것들을 응용한 것이다. (토비 님: 저는 사실 이거 보면서 되게 놀랍거든요) +왜냐하면 3장에서 제시하는 문제들을 해결하는 다양한 당시의 유명한 개발자들이 소개하는 기법들이 있었어요. +try-catch-final 이라는 반복되는 코드를 작성하고 깜빡해서 close 하는 것을 하나 잊은 상황에서 서버를 일주일 정도 돌린다면 ? +갑자기 어느날 리소스 풀이라고 나와서 서버가 꺼져버리는 현상을 목격할거고 밤을 세우고 심지어는 소스 코드를 전부 출력을 해서 형광펜으로 하나식 줄을 그어 가면서 `무엇을 빼먹었는가` 에 대해 체크하는 경우도 생긴다. 진짜 그런 일들이 많았다. +그 시절에 스펙(?)을 만든 사람은 `이는 간단한 객체지향적인 디자인 패턴 몇 개만 응용하면 해결되지 않는가` 라는 해답을 내놓았다. (토비 님: 사람들이 이 생각을 왜 못 했을까 ? 자바를 많이 하고 잘한다는 사람들이) +그중에는 안드로이드의 자바 라이브러리를 총지휘한 유명한 사람도 있는데, 그 사람도 그 사람이 만든 솔루션도 ~ 했거든요. (0사에 대한 이야기) 그 분이 00 에서 컨설턴트로 일하실 때 진짜 그런 상황을 많이 만났다. +(JDBC 로 개발을 진행한 상황) try-catch-final 의 어딘가에서 close 를 안하고 넘어가는게 있는데 A4 용지 한 페이지에 페이지가 네 장이 들어가는 형태로 수백장의 소스 코드를 출력한 다음에 형광펜 하나 잡고 try-catch-final 이 보이는 곳에다가 하나씩 밑줄 그어 가면서 close 빼먹은 곳이 있나 finally 빼먹은 데 있나 4-5 시간 정도를 진행. `이거 고치세요` 이러면 이 사람들한테 XX 원의 컨설팅 비용을 지불하였다. +2000년대 초반에 유명한 자바 커뮤니티 그 분이 올리신 글을 보고 깜짝 놀랐던 적이 있다. close 를 누락하는 등의 오류는 어찌보면 간단한 코드지만 실수할 수 있는 내용이고 쉽게 찾아내기도 힘들 수 있겠구나라는 것을 절실하게 느꼈다. +(예제 이야기) 그래서 3장에 나오는 내용을 스프링을 통해서 배우게 되어 되게 좋았다. 이렇게 접근해 나가는 동안 객체지향적인 어떤 원리들이 `어떤 문제를 해결하기 위해서 쓰였는가` 이를 쭉 풀어나가는 방식으로 3장을 구성을 했다. +2008 ~ 2010 년 초반에 이 책이 쓰인 당시에는 자바 6 까지 밖에 안나와서 자바 6 의 언어가 지원하는 내용만 사용을 했다. +그 이후의 자바 7, 8, ... , 19 까지 나왔는데, 버전이 점점 올라가면서 이 책에서 작성했던 샘플 코드를 좀 더 개선할 수 있는 여러 가지 좋은 방법들이 나왔다. +어쩌면 여기서 얘기하는 템플릿 콜백에 대한 완전히 새로운 방식의 접근 방법을 사용할 수 있게 되었다. 하지만 그럼에도 불구하고 최신 스프링에도 여전히 템플릿이 존재한다. 그게 어떤 의미가 있는 건지 이런 것들을 한번 쭉 얘기를 해보면 좋을 것 같다. From 58dc78717acac9b51517787f40f601075209f1b9 Mon Sep 17 00:00:00 2001 From: caoyu-dev Date: Fri, 30 Sep 2022 00:56:05 +0900 Subject: [PATCH 10/11] =?UTF-8?q?3=EC=9E=A5=20=ED=85=9C=ED=94=8C=EB=A6=BF?= =?UTF-8?q?=20=EC=88=98=EC=A0=952?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0\353\224\224_\354\240\225\353\246\254.md" | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git "a/ch3/3\354\236\245_\355\205\234\355\224\214\353\246\277_\354\212\244\355\204\260\353\224\224_\354\240\225\353\246\254.md" "b/ch3/3\354\236\245_\355\205\234\355\224\214\353\246\277_\354\212\244\355\204\260\353\224\224_\354\240\225\353\246\254.md" index e0ecaeb..87cf947 100644 --- "a/ch3/3\354\236\245_\355\205\234\355\224\214\353\246\277_\354\212\244\355\204\260\353\224\224_\354\240\225\353\246\254.md" +++ "b/ch3/3\354\236\245_\355\205\234\355\224\214\353\246\277_\354\212\244\355\204\260\353\224\224_\354\240\225\353\246\254.md" @@ -13,3 +13,33 @@ try-catch-final 이라는 반복되는 코드를 작성하고 깜빡해서 close 2008 ~ 2010 년 초반에 이 책이 쓰인 당시에는 자바 6 까지 밖에 안나와서 자바 6 의 언어가 지원하는 내용만 사용을 했다. 그 이후의 자바 7, 8, ... , 19 까지 나왔는데, 버전이 점점 올라가면서 이 책에서 작성했던 샘플 코드를 좀 더 개선할 수 있는 여러 가지 좋은 방법들이 나왔다. 어쩌면 여기서 얘기하는 템플릿 콜백에 대한 완전히 새로운 방식의 접근 방법을 사용할 수 있게 되었다. 하지만 그럼에도 불구하고 최신 스프링에도 여전히 템플릿이 존재한다. 그게 어떤 의미가 있는 건지 이런 것들을 한번 쭉 얘기를 해보면 좋을 것 같다. + +## (느낀점) +DB 접근에 대해 처음 배울 때 접하는 내용이 connection, preparedStatement, 그 다음에 JDBC 템플릿으로 넘어가게 된다. 그 패턴에 익숙해지니 SQL 문을 인터페이스로 분리하는 과정이 `오버엔지니어링이 아닌가 ?` 라 생각되었다. `SQL 문을 구현체로 추출하면 보기만 어렵지 않나 ?` 라는 생각도 했는데, 점점 JDBC 템플릿과 유사한 형태로 리팩토링이 진행됐다. + +## (토비 님) +3장은 JDBC 템플릿 그 자체가 목적이 아니고, 이런 류의 문제를 만나게 되면 자바에서는 `어떤 디자인 방법적인 설계로 해결을 할 수 있는가 ?` 를 생각하게 하는게 목적이다. +해당 챕터에서 나오는 DB 프로그램 작성에 대해 `이런 식으로 접근을 했어야 하나 ?` 라고 생각하는 것 보다는, 이런 코드로 작성을 해야 하던 시절에서 그 `문제를 해결하면서 어떤 접근 방법을 썼는가 ?` 에 대해 더 관심을 가져주는게 좋을 것 같다. + + +## (느낀점) +이너 클래스가 인상 깊었다. 잘못 사용하면 스파게티 코드가 되기 쉬운 부분이 있다. 직접 사용하기 보다는 `fake` 라는 이름의 로컬 클래스를 만들어서 쓰는 경우가 많다. + +## (토비 님) +요즘 언어 차원에서 콜백, 람다를 지원하는 언어들이 워낙 많아서 그냥 그렇게 시작하면 된다고 생각할 수 있는데, 사실 그게 전통적인 객체지향 언어에서 발전해 파생되는 결과물이라 생각한다. +그래서 이런 방향으로 접근 방법을 보는 것이 좋겠다는 생각이 든다. + + +## (느낀점) +처음에는 테스트만 통과할 수 있는 적당히 구현된 코드가 나온다. 그리고 `변하는 것` 과 `변하지 않는 것` 을 분리하며 템플릿 콜백 패턴을 구현하는 과정이 나온다. +그리고 어떤 설계에 대한 결정을 할 때, `왜 이런 선택을 하는지`, `뭐가 좋은지` 에 대한 이유를 설명한다. 예를 들면, `로컬 클래스를 선언하면 좋은 이유`, 그리고 `인터페이스를 거치지 않고 DI 를 하면 좋은 이유` 와 같은 내용인데 이런 이야기를 읽으면서 이유를 알게 된 것 같아서 되게 좋았다. +우리는 프레임워크에 사용자이면서 동시에 애플리케이션을 만든다. 우리가 애플리케이션을 만들 때도 우리가 사용하는 프레임워크처럼 변경에 유연하고 또 합체하기 쉬운 그런 것을 만들어야 한다. 이 프레임워크에 녹여져 있는 원리들을 배우고 또 적용하면 좋겠다. + +## (토비 님) +제가 이 책에서 강조하고 싶었던 것은 스프링이 어느 날 갑자기 `JDBC 개발은 템플릿으로 해` 라며 만들어지지 않았다는 것이다. 스프링을 만든 개발자가 평범한 자바 접근 방법을 사용하여 만들어진 코드를 보고 문제 의식을 느꼈고, 여기에 객체지향적인 원리를 적용하니 이런 형식의 코드가 만들어졌고, 간결해지니 프레임워크화가 되었다. +`우리가 만든 애플리케이션 코드에도 동일한 원리를 적용할 수 있지 않을까 ?` 사실 이런 내용을 이야기하고 싶었다. 그래서 스프링을 사용하면 `람다를 쓰는 코드` 를 많이 만들게 되는데, `람다를 받는 코드` 를 과연 만들어본 적이 있는지 이런 것을 한 번 생각해 볼 필요가 있을 것 같다. +남이 만든 라이브러리에 람다식을 넣는 건 쉽게 할 수 있는데, 내가 만든 코드에 내가 만든 또 다른 코드 내지는 다른 사람이 내가 만든 코드를 사용할 때 `람다식을 넣을 수 있도록 만들어 낼 수 있는가 ?` 이 부분에 대한 고민이 필요하다고 생각한다. 예를 들어, 제너레이트, 에노테이션. +템플릿 부분도 사실 이런 원리들을 적용한 코드를 가지고 고민을 풀어나가는 과정으로 나타내 굉장히 작은 스탭으로 서서히 진행시켰다. +무슨 뭐하러 로컬 클래스를 만들고 그냥 한방에 가지 이렇게 말씀하시는 분들도 있는데, 근데 저는 이 문제를 풀어나가는 과정의 모든 부분이 답을 준다는 생각을 했고, 해결할 수 있는 솔루션들을 찾아보고, 그 장단점을 비교해보고, 최종적으로 선택지를 결정해서 그 방향으로 한방에 전진하는 식의 문제 접근 과정을 보여주고 싶었다. + + From edef5ff10e93f7d75000b8ee762e35217b07df8a Mon Sep 17 00:00:00 2001 From: caoyu-dev Date: Sun, 2 Oct 2022 00:43:27 +0900 Subject: [PATCH 11/11] =?UTF-8?q?3=EC=9E=A5=20=ED=85=9C=ED=94=8C=EB=A6=BF?= =?UTF-8?q?=20=EC=88=98=EC=A0=953?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0\353\224\224_\354\240\225\353\246\254.md" | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git "a/ch3/3\354\236\245_\355\205\234\355\224\214\353\246\277_\354\212\244\355\204\260\353\224\224_\354\240\225\353\246\254.md" "b/ch3/3\354\236\245_\355\205\234\355\224\214\353\246\277_\354\212\244\355\204\260\353\224\224_\354\240\225\353\246\254.md" index 87cf947..1a83dbf 100644 --- "a/ch3/3\354\236\245_\355\205\234\355\224\214\353\246\277_\354\212\244\355\204\260\353\224\224_\354\240\225\353\246\254.md" +++ "b/ch3/3\354\236\245_\355\205\234\355\224\214\353\246\277_\354\212\244\355\204\260\353\224\224_\354\240\225\353\246\254.md" @@ -43,3 +43,110 @@ DB 접근에 대해 처음 배울 때 접하는 내용이 connection, preparedSt 무슨 뭐하러 로컬 클래스를 만들고 그냥 한방에 가지 이렇게 말씀하시는 분들도 있는데, 근데 저는 이 문제를 풀어나가는 과정의 모든 부분이 답을 준다는 생각을 했고, 해결할 수 있는 솔루션들을 찾아보고, 그 장단점을 비교해보고, 최종적으로 선택지를 결정해서 그 방향으로 한방에 전진하는 식의 문제 접근 과정을 보여주고 싶었다. +## (느낀점) +3장 읽으면서 `전략 패턴이랑 콜백 패턴 이래서 사용하게 되었구나` 를 가장 초창기에 쓰였던 코드부터 시작해서 순차적으로 보여주셔서 그 점이 정말 좋았습니다. + +## (토비 님) +`전략 패턴` 을 구성하는 것은 두 가지이다. `Context` 가 있고, Context 가 사용하는 `Strategy Interface` 가 있으면 된다. +`전략` 은 다른 말로 하면 `알고리즘` 이라 할 수 있다. 예를 들어, 어떤 코드가 돌아가는 문맥이 있을 때 문맥 중에서 어떤 특정한 알고리즘을 쓰는 부분이 있다고 하자. +이것을 Context 클래스 안에다가 내장을 시키지 않고 그냥 외부에서 통째로 다 받아서 해당 클래스를 가져다 쓰고 싶은 경우에 해당한다. +이것을 쉽게 표현하자면 `전략 패턴은 알고리즘을 통째로 바꿔치기 할 수 있게 만든 패턴` 이라고 할 수 있다. +그런데 자바에 나오는 라이브러리 중에 전략 패턴을 쓰는 것에 목적이 있는 것은 예를 들어, `sort` 에다 `Comparable Interface` 를 구현한 `Sorting` 을 할건데 어떤게 앞에 놓일지 어떤게 뒤에 놓일지에 대한 것을 결정하는게 하나의 알고리즘이라고 할 수 있다. +즉, 값을 두 개 주면서 순서가 어떻게 될건지만 정해주면 나머지 Sorting 알고리즘은 Sorting 이라는 `Context` 에서 알아서 처리를 하는데 `비교를 한다` 라는 비교 알고리즘 자체를 외부에서 주입을 받는 것에 있다. 이런게 전략 패턴이다. +전략 패턴은 전략 오브젝트를 `Context` 에다가 밀어 넣어주는 것 말한다. 그런데 이를 메소드의 파라미터로 넣기도 하고 아예 생성자에서 바로 그냥 내장시켜 버리기도 하는데 이것을 `콜백 템플릿` 또는 `템플릿 콜백 패턴` 이라고 붙여서 얘기를 한다. + +## `템플릿 콜백 패턴` 과 `전략 패턴` 의 차이점은 ? +디자인 패턴 얘기를 하면은 항상 이거부터 시작을 한다. 이게 `그` 디자인 패턴이다. +`그` 라는 건 뭐냐면 `GoF` 라고 하는 네 명의 사람이 쓴 `객체지향 디자인 패턴` 이라는 책을 말한다. 이는 처음으로 이제 객체지향 디자인 패턴이라는 컨셉을 가지고 설명한 책이다. +`그` 객체지향 디자인 패턴 책에 나오는 패턴이 아닌 그외의 패턴들도 많이 있다. 그러나 `전략 패턴` 은 바로 `그` 객체지향 디자인 패턴 책에 나오는 패턴이다. + +`템플릿 콜백 패턴` 은 `그` 책에 나오지 않는 한참 지나서 2000년대 초반에 스프링 책을 쓴 `로드 존슨` 이 디자인 테크닉의 일종으로 이름을 붙인 테크닉에 해당한다. +(토비 님: 그 사람이 만든건지 다른 사람들이 했던 말을 가져다 쓴건지 그건 제가 기억을 못하겠다) +스프링의 기원이 되는 코드들이 등장하는 책인데, 이 책에서는 `템플릿 콜백 패턴은 전략 패턴의 특별한 한가지 케이스다` 라고 설명을 하고 있다. +그러니까 템플릿 콜백 패턴은 전략 패턴이에요. 근데 어떤 전략 패턴이냐면 전략 패턴은 그 전략이라는 것은 오브젝트니까 그 안에 수많은 메서드를 가지고 있을 수 있잖아요. +전략을 바꿔치기 해야 되니까 추상 클래스를 가지거나 인터페이스를 구현을 해야 된다는 이야기이다. 그래야 그것을 상속하거나 다른 구현체로 바꿔치기할 수 있기 때문이다. + +템플릿은 전략 패턴의 `Context` 고, 콜백은 전략 패턴의 `전략` 에 대한 것이다. 사실은 이 이야기는 함수형 프로그래밍의 `Higher-order function` 또는 `고계함수` 라고도 불리는 것에서 유래됐다. +아무튼 많이들 아시니까. 많이들 들어보셨을텐데. ~해서 얘기하는 그 함수를 전달하는거 함수를 전달하는 파라미터로 전달하는 거 그거랑 사실상 기능적으로 똑같아요. 왜냐면 메서드가 딱 하나뿐이잖아요. +인터페이스의 메서드가 하나면 그 전략이 구현하는 메소드도 당연히 하나 뿐이다. 하나 뿐인 전략을 허용하는 것을 우리는 특별히 `템플릿 콜백` 이라고 부른다. 이게 의외로 사용 용도가 되게 많다. 사용이 많아지면서 굳이 이름을 붙인 거다. + +또 얼마 전에도 어떤 분이 `템플릿 콜백 패턴이라는게 뭐냐 ? 전략 패턴이랑 무슨 상관이지 ? 같은 건가 다른 건가 ?` 이런 질문을 하셨는데, `전략 패턴의 스페셜한 케이스` 다. +전략 인터페이스에 메서드가 하나 뿐인 것이다. 그러면 `요즘으로 치면 람다랑 비슷하네 ? 람다 리플렉션이랑 비슷하네 ?` 또는 `뭐 자바스크립트 콜백이나 클로져 함수 고계함수 이런 거랑 비슷하네 ?` 그렇다, 비슷하다. +하지만 실제 사용되어진 상황을 보면 기존에 클래스로 많이 만들던 오브젝트이기 때문에 가진 조금 더 다른 특징이 있기는 하다. 그 부분을 구분하여 기억하면 좋을 것 같다. + + +## (느낀점) +`람다식은 객체인가?` 라는 질문에 람다식도 어쨌든 함수형 인터페이스를 구현한 구현체라고 생각을 하기 때문에 객체가 맞다는 생각을 했다. +객체가 `상태` 또는 `행위` 를 가지고 있고, 식별자를 통해서 `식별` 할 수 있는 것이라 생각을 하고 있기 때문이다. + +함수형 프로그래밍이라는 것을 자바에서 활용하기 위해서 오브젝트를 가져다 쓰는 상황이 되었는데, `자바에서 함수형 프로그래밍을 도입하기 위해 객체의 개념을 가져다 썼다` 정도로 이해하면 되는가 ? + +## (토비 님) +네, 맞습니다. 그런데 람다가 나오기 전에 자바에서 주제가 `함수형 프로그래밍을 하자` 인 `Functional Programing For Java` 라는 책이 나왔었다. +그래서 주로 익명 클래스가 람다랑 되게 비슷하다. 람다식이라는 것은 결국은 익명 함수이다. 함수긴 함수인데 이름이 없다. +람다식에는 그냥 파라미터와 바디만 있으면 되는데, 이곳에 이름을 붙이지는 않는다. 또한, `익명 클래스` 도 `로컬 클래스` 에 이름을 생략한 형태이다. + +람다식이 익명 클래스보다 상당히 많이 발전된 형태이기는 하다. (인터페이스가 구현되지 않은 람다식을 가져다 대입해도 호출되는 형태 등) +하지만 이런 점들을 제외하고 나면 익명 클래스와 동작이 거의 비슷하다. 딱 하나 다른건 this 에 대한 정의가 좀 다른데 람다식은 잘 쓰지 않는다. +(~~) 한 단계 더 나아간 형태가 람다식이라고 생각하면 되는데, 이게 스트림이 같이 등장을 했기 때문에 람다식이 의미가 있다. +스트림 안에서 내부의 상태를 계속 바꾸어가는, 즉 함수를 계속 evaluate 하다가 최종적으로 함수가 리턴하는 값을 가지고 복잡한 연산을 처리하게 하는 스타일로 코드를 작성하도록 유도하며 설계한 것이 람다식이기 때문에 이런 스타일로 사용하는 것에서 람다식은 의미가 있다. + +**그러나 그냥 무조건 람다식으로 만들어야 된다는 것은 아니다.** +콜백 안에 복잡하고 고도화된 기능을 넣어놓고 콜백을 생성할 때 다른 의존 오브젝트를 주입하기도 하고 하지만 외부에 노출하는 퍼블릭 인터페이스는 단 하나 뿐인 경우에는 이것을 콜백으로 쓰겠어 하고 만들 수도 있다. (?) +이런 것은 람다식으로 대체가 불가능하죠. 왜냐하면 생성하는 시점에서 뭔가 고도화된 작업을 해야 되고 그다음에 여기다가 의미있는 이름을 붙여주는 것이 가독성을 위해서 중요하니까 람다식과 익명 클래스 혹은 일반 클래스의 ~ 콜백은 서로 대치해서 사용할 수 있어 어떤게 더 좋으냐 라는 절대적인 기준은 없는 거다. +`람다로 써도 문제가 없겠네 ?` 네, 그냥 쓰셔도 됩니다. 그런데 람다를 하나를 코드 중간에다가 식만 달랑 가져다만 놓으면 도대체 뭐하는 식인지가 이해되지 않는다. +그러면 타입을 가진 변수를 정의해서 그곳에 람다식을 작성하면 된다. 예제를 보면 `로우 매퍼` 라는 것이 나오는데 이런 것도 람다식으로 교체가 된다. +람다식이랑 익명 클래스(일반 클래스) 랑 거의 일대일로 교체 가능하다. 결국 익명 클래스가 어떻게 만들어지고 사용되는가에 대해서 판단해야 한다. 언제든지 람다로 바꿀 수 있는데 어떤 경우에 바꾸는지도 판단해야 한다. 어떤 때는 익명 클래스 쓰고, +어떤 때는 로컬 클래스 쓰고, 어떤 때는 그냥 일반 클래스 쓰고, 어떤 때는 중첩 클래스를 쓴다. 이것을 판단할 수 있는 구분 방법을 마련하라. + +[When to Use Nested Classes, Local Classes, Anonymous Classes, and Lambda Expressions](https://docs.oracle.com/javase/tutorial/java/javaOO/whentouse.html) + +잘 모르겠다면 오라클 문서를 보시면 된다. 오라클이 제공하는 `java tutorial` 은 자바 언어를 꼼꼼하게 알고 싶다면 읽는게 좋다고 생각한다. +그리고 필수는 아니지만 관례적으로 `Functional Interface` 에 에노테이션을 붙인다. `@FunctionalInterface` 을 붙이면 람다라 생각하면 된다. +스프링의 JDBC 템플릿 중에서 콜백 인터페이스를 스프링 5.0 에서 찾아보면 `@FunctionalInterface` 가 붙어있다. 그렇다는 이야기는 옛날 방식의 익명 클래스를 써도 되지만, 람다식으로 써도 좋다라는 뜻이다. +물론 좋다라고도 할 것도 없는게 `람다식으로 쓰는게 더 좋으니 써라` 라고 스프링이 적극적으로 권장을 하는거라고 생각하면 된다. +그래서 요즘에는 템플릿 콜백이라는 말을 안쓰게 되었다. +람다식을 받는 메서드는 `FunctionalInterface` 같은 메서드 하나짜리를 구현을 해야 하는데, 조금 더 정확하게 말하자면 인터페이스를 여러 개 구현해도 된다. +인터페이스를 3개 구현한 오브젝트가 있는데 이 3개의 인터페이스에 있는 메소드의 총 합계가 한 개면 얘는 람다식으로 쓰일 수 있다. 이 이야기는 + +``` +marker interface ← 마킹 인터페이스 +Serializable +``` + +구현한 인터페이스에 메서드가 없기 때문이다. 즉, 이 인터페이스는 내용이 없다. 만약에 어떠한 인터페이스를 만드는데 인터페이스가 임의의 메서드 하나를 갖고 있고 `Serializable` 같은 것을 상속했다면 +마킹 역할을 하기 위해서 인터페이스를 여러 개를 구현하고 있어도 얘를 갖다가 람다식으로 쓸 수 있는,, 어쨌든 전체 메서드의 합계가 하나면 된다. (토비 님: 이것을 가지고 재밌는 것을 할 수가 있어요. 이거에 대해서는 기회가 되면 보여드릴게요) +**요즘에는 이 마킹 역할을 에노테이션으로 많이 쓰는데, 인터페이스를 쓰는 게 훨씬 좋다.** 이것도 이펙티브 자바에서 되게 강조하는 내용 중 하나이다. +인터페이스는 타입이기 때문에 타입 체킹이 가능하다. 타입으로 받을 수도 있다. 하지만 에노테이션은 리플렉션을 쓰지 않으면 붙어있는 건지 아닌지 감별할 수 없게 코드가 지저분하게 된다. + + +## (느낀점) +3장의 내용은 하나의 템플릿 같은 느낌이었다. +문제를 해결해 나아가는데 있어서 객체지향적으로 생각했을 때 `변경이 되는 부분` 과 `안되는 부분` 을 명백하게 구분하는 것이 좋았다. +DI 할 때 인터페이스를 꼭 사용해야 한다는 생각을 가지고 있었는데 이번에 책을 읽으면서 꼭 인터페이스를 할 필요는 없구나, +인터페이스가 아니어도 되는 경우가 있구나 이런 부분은 좀 저의 생각을 깨는 부분이 있었다. + +## (토비 님) +OCP 는 절대 적용하자면 거의 모든 객체지향 디자인 원리를 가지고 ~. +중복이 전체적으로 일어나면 굉장히 간단하다. 그 부분만 extract 해서 재사용하면 된다. +그러나 중복이 부분만 일어난다면 `변하는 부분` 과 `분리시켜서 변하지 않는 부분` 을 구분하여 코드를 재사용하는 방향으로 가면 된다. +`변하는 부분` 을 `변하지 않는 부분` 에서 바라볼 때, 즉 템플릿에서 콜백을 바라볼 때, 어떻게 변하는지 템플릿이 모르게 만들면 된다. 이것이 객체지향적 설계나 프로그래밍의 핵심이다. +절차지향적인 방식, 즉 데이터 중심의 코드에서는 전역변수를 사용한다. 이는 데이터를 중앙에 두고 모든 코드들이 그 데이터를 다 읽는 구조이다. +한 곳에서만 고치면 괜찮은데 여기서도 고치고 저기서도 고치고 하다보니 글로벌하게 노출이 된다. +그런 과정에서 이런 생각을 하게 된다. `아 이게 그 고치는 코드 이외에 자주 변경이 일어나게 되는데 얘를 어떻게 하면은 감출 수가 있을까 ?` +그래서 나온 것이 캡슐화이다. 캡슐화를 데이터와 행위를 같이 모아놓는다는 의미로 많이들 설명하는데, 캡슐화에서 제일 중요한 것은 변하는 부분을 외부로부터 감추는 것이다. (~~) +`데이터를 내가 Array 로 만들어 놨는데, 나중에 보니깐 Collection 으로 바뀌었네 ?` +이런 상황이 오면 객체지향적이지 않고 캡슐화도 잘 안됐다고 보는 것이다. +내부의 어떤 코드에 변경이 일어날 때 그 변경이 외부에서 감춰지게 잘 만들어야 캡슐화가 잘되었고 객체지향적인 코드라고 표현한다. 이걸 다르게 이야기하면 OCP 라고 할 수 있다. +이는 기본적으로 코드를 개선할 때에 봐야하는 제일 중요한 내용 중 하나이다. **중복을 제거하고 변경된 부분과 변경되지 않는 부분을 잘 구분해야 한다.** + +Alan Kay(객체지향 프로그래밍 단어 창시자) 가 객체지향 프로그래밍이라는 것을 설명한 글이 있는데 이 사람이 강조하는 것은 이거다. **객체는 객체지향은 데이터를 없애는 거다.** +객체지향이란 것은 객체가 다른 객체한테 메세지를 보내서 그 메세지를 받는 객체가 메세지에 응답을 할 수 있게 어떤 명령과 같이 어떤 일을 수행하는 것이다. +이런 의미에서 람다도 메세지를 받아서 그 메세지를 해석하고 기능을 수행하는 코드라고 보기 때문에 객체라고 설명할 수 있다. + + +## 템플릿 메서드 패턴과 템플릿 콜백 패턴의 차이 +없다고 보는 게 맞는데, 유사성이 있기는 하다. 템플릿 쪽은 변하지 않는 것을 가리킨다. (이제 정리하기 힘들다 추후에 추가한다) + + +깃허브에 코드를 공유해 놨다. (봐야징)