├── DataStructuresAndAlgorithm
├── README.md
└── QuickSort
│ ├── images
│ └── quicksort1.jpg
│ └── README.md
├── JPA_기초
├── images
│ ├── cover.jpg
│ ├── jpa_internal.jpg
│ ├── readCommitted.png
│ └── repeatableRead.png
└── README.md
├── 서버_성능_향상
├── images
│ ├── osi7layer.jpg
│ ├── sharding.png
│ ├── spof_server.jpg
│ ├── node_internal.png
│ ├── cache_duplicate.jpg
│ ├── failover_failback.png
│ ├── node_architecture.png
│ ├── session_duplicate.jpg
│ ├── horizontal_partioning.png
│ ├── tomcat_architecture1.png
│ ├── tomcat_architecture2.png
│ └── gabia_scale_up_scale_out.jpg
└── README.md
├── 스프링부트_프로젝트_생성하는 방법
├── images
│ ├── jdk.jpg
│ ├── maven.jpg
│ ├── searchPom.jpg
│ ├── import.project.jpg
│ ├── import.step1.jpg
│ ├── project.name.jpg
│ ├── start.spring.jpg
│ └── import.project2.jpg
└── README.md
├── 토비의 스프링 정독 및 요약
├── ch3
│ ├── images
│ │ ├── jvm.png
│ │ ├── jdk1.jpg
│ │ └── jdk2.png
│ └── README.md
├── ch2
│ ├── images
│ │ └── test types.png
│ └── ch2.md
├── ch4_예외
│ └── README.md
└── ch1
│ └── ch1.md
├── 2018OKKYCON
├── assets
│ ├── 1539919676385.png
│ ├── 1539919874850.png
│ ├── 1539920636634.png
│ ├── 1539927554626.png
│ ├── 1539927655096.png
│ ├── 1539927672446.png
│ ├── 1539927903049.png
│ ├── 1539928109569.png
│ ├── 1539928229357.png
│ ├── 1539928258802.png
│ ├── 1539928384168.png
│ └── 1_IbHgZrKYCUSeIbL_PywObQ.png
└── readme.md
├── Mysql_5.6_설치_윈도우10
├── images
│ ├── mysql.jpg
│ ├── add_path.jpg
│ ├── mysql_zip.jpg
│ ├── mysql_root.jpg
│ ├── mysql_start.jpg
│ ├── system_menu.jpg
│ ├── system_properties.jpg
│ └── environment_variables.jpg
└── README.md
├── 백기선님_스프링_핵심기술_강의_정리
├── images
│ ├── proxy.jpg
│ └── beanConfig.jpg
└── readme.md
├── README.md
├── 백기선님_스프링_기반_REST_API_개발
├── images
│ └── autoscroll.jpg
└── readme.md
├── 자바_DTO란_무엇이며_왜_쓰는가
├── images
│ └── spring-web-app-architecture.png
└── README.md
├── Handlebars_버전_에러
└── README.md
├── 자바_POJO란_무엇인가
└── README.md
└── 용어 정리
└── readme.md
/DataStructuresAndAlgorithm/README.md:
--------------------------------------------------------------------------------
1 | # Data Structures And Algorithm
2 | * 코딩테스트에 앞서 기초 점검을 위한 repo
--------------------------------------------------------------------------------
/JPA_기초/images/cover.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/JPA_기초/images/cover.jpg
--------------------------------------------------------------------------------
/서버_성능_향상/images/osi7layer.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/서버_성능_향상/images/osi7layer.jpg
--------------------------------------------------------------------------------
/서버_성능_향상/images/sharding.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/서버_성능_향상/images/sharding.png
--------------------------------------------------------------------------------
/JPA_기초/images/jpa_internal.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/JPA_기초/images/jpa_internal.jpg
--------------------------------------------------------------------------------
/JPA_기초/images/readCommitted.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/JPA_기초/images/readCommitted.png
--------------------------------------------------------------------------------
/서버_성능_향상/images/spof_server.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/서버_성능_향상/images/spof_server.jpg
--------------------------------------------------------------------------------
/JPA_기초/images/repeatableRead.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/JPA_기초/images/repeatableRead.png
--------------------------------------------------------------------------------
/서버_성능_향상/images/node_internal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/서버_성능_향상/images/node_internal.png
--------------------------------------------------------------------------------
/스프링부트_프로젝트_생성하는 방법/images/jdk.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/스프링부트_프로젝트_생성하는 방법/images/jdk.jpg
--------------------------------------------------------------------------------
/토비의 스프링 정독 및 요약/ch3/images/jvm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/토비의 스프링 정독 및 요약/ch3/images/jvm.png
--------------------------------------------------------------------------------
/2018OKKYCON/assets/1539919676385.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/2018OKKYCON/assets/1539919676385.png
--------------------------------------------------------------------------------
/2018OKKYCON/assets/1539919874850.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/2018OKKYCON/assets/1539919874850.png
--------------------------------------------------------------------------------
/2018OKKYCON/assets/1539920636634.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/2018OKKYCON/assets/1539920636634.png
--------------------------------------------------------------------------------
/2018OKKYCON/assets/1539927554626.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/2018OKKYCON/assets/1539927554626.png
--------------------------------------------------------------------------------
/2018OKKYCON/assets/1539927655096.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/2018OKKYCON/assets/1539927655096.png
--------------------------------------------------------------------------------
/2018OKKYCON/assets/1539927672446.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/2018OKKYCON/assets/1539927672446.png
--------------------------------------------------------------------------------
/2018OKKYCON/assets/1539927903049.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/2018OKKYCON/assets/1539927903049.png
--------------------------------------------------------------------------------
/2018OKKYCON/assets/1539928109569.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/2018OKKYCON/assets/1539928109569.png
--------------------------------------------------------------------------------
/2018OKKYCON/assets/1539928229357.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/2018OKKYCON/assets/1539928229357.png
--------------------------------------------------------------------------------
/2018OKKYCON/assets/1539928258802.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/2018OKKYCON/assets/1539928258802.png
--------------------------------------------------------------------------------
/2018OKKYCON/assets/1539928384168.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/2018OKKYCON/assets/1539928384168.png
--------------------------------------------------------------------------------
/Mysql_5.6_설치_윈도우10/images/mysql.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/Mysql_5.6_설치_윈도우10/images/mysql.jpg
--------------------------------------------------------------------------------
/백기선님_스프링_핵심기술_강의_정리/images/proxy.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/백기선님_스프링_핵심기술_강의_정리/images/proxy.jpg
--------------------------------------------------------------------------------
/서버_성능_향상/images/cache_duplicate.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/서버_성능_향상/images/cache_duplicate.jpg
--------------------------------------------------------------------------------
/스프링부트_프로젝트_생성하는 방법/images/maven.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/스프링부트_프로젝트_생성하는 방법/images/maven.jpg
--------------------------------------------------------------------------------
/토비의 스프링 정독 및 요약/ch3/images/jdk1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/토비의 스프링 정독 및 요약/ch3/images/jdk1.jpg
--------------------------------------------------------------------------------
/토비의 스프링 정독 및 요약/ch3/images/jdk2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/토비의 스프링 정독 및 요약/ch3/images/jdk2.png
--------------------------------------------------------------------------------
/Mysql_5.6_설치_윈도우10/images/add_path.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/Mysql_5.6_설치_윈도우10/images/add_path.jpg
--------------------------------------------------------------------------------
/Mysql_5.6_설치_윈도우10/images/mysql_zip.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/Mysql_5.6_설치_윈도우10/images/mysql_zip.jpg
--------------------------------------------------------------------------------
/서버_성능_향상/images/failover_failback.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/서버_성능_향상/images/failover_failback.png
--------------------------------------------------------------------------------
/서버_성능_향상/images/node_architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/서버_성능_향상/images/node_architecture.png
--------------------------------------------------------------------------------
/서버_성능_향상/images/session_duplicate.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/서버_성능_향상/images/session_duplicate.jpg
--------------------------------------------------------------------------------
/스프링부트_프로젝트_생성하는 방법/images/searchPom.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/스프링부트_프로젝트_생성하는 방법/images/searchPom.jpg
--------------------------------------------------------------------------------
/Mysql_5.6_설치_윈도우10/images/mysql_root.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/Mysql_5.6_설치_윈도우10/images/mysql_root.jpg
--------------------------------------------------------------------------------
/Mysql_5.6_설치_윈도우10/images/mysql_start.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/Mysql_5.6_설치_윈도우10/images/mysql_start.jpg
--------------------------------------------------------------------------------
/Mysql_5.6_설치_윈도우10/images/system_menu.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/Mysql_5.6_설치_윈도우10/images/system_menu.jpg
--------------------------------------------------------------------------------
/백기선님_스프링_핵심기술_강의_정리/images/beanConfig.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/백기선님_스프링_핵심기술_강의_정리/images/beanConfig.jpg
--------------------------------------------------------------------------------
/서버_성능_향상/images/horizontal_partioning.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/서버_성능_향상/images/horizontal_partioning.png
--------------------------------------------------------------------------------
/서버_성능_향상/images/tomcat_architecture1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/서버_성능_향상/images/tomcat_architecture1.png
--------------------------------------------------------------------------------
/서버_성능_향상/images/tomcat_architecture2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/서버_성능_향상/images/tomcat_architecture2.png
--------------------------------------------------------------------------------
/토비의 스프링 정독 및 요약/ch2/images/test types.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/토비의 스프링 정독 및 요약/ch2/images/test types.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # study
2 |
3 | 학습한 내용을 티스토리 블로그에 포스팅하기 위해 모아둔 마크다운 글입니다.
4 |
5 | 블로그: [hardlearner.tistory.com](https://hardlearner.tistory.com)
6 |
--------------------------------------------------------------------------------
/서버_성능_향상/images/gabia_scale_up_scale_out.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/서버_성능_향상/images/gabia_scale_up_scale_out.jpg
--------------------------------------------------------------------------------
/스프링부트_프로젝트_생성하는 방법/images/import.project.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/스프링부트_프로젝트_생성하는 방법/images/import.project.jpg
--------------------------------------------------------------------------------
/스프링부트_프로젝트_생성하는 방법/images/import.step1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/스프링부트_프로젝트_생성하는 방법/images/import.step1.jpg
--------------------------------------------------------------------------------
/스프링부트_프로젝트_생성하는 방법/images/project.name.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/스프링부트_프로젝트_생성하는 방법/images/project.name.jpg
--------------------------------------------------------------------------------
/스프링부트_프로젝트_생성하는 방법/images/start.spring.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/스프링부트_프로젝트_생성하는 방법/images/start.spring.jpg
--------------------------------------------------------------------------------
/백기선님_스프링_기반_REST_API_개발/images/autoscroll.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/백기선님_스프링_기반_REST_API_개발/images/autoscroll.jpg
--------------------------------------------------------------------------------
/스프링부트_프로젝트_생성하는 방법/images/import.project2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/스프링부트_프로젝트_생성하는 방법/images/import.project2.jpg
--------------------------------------------------------------------------------
/2018OKKYCON/assets/1_IbHgZrKYCUSeIbL_PywObQ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/2018OKKYCON/assets/1_IbHgZrKYCUSeIbL_PywObQ.png
--------------------------------------------------------------------------------
/Mysql_5.6_설치_윈도우10/images/system_properties.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/Mysql_5.6_설치_윈도우10/images/system_properties.jpg
--------------------------------------------------------------------------------
/Mysql_5.6_설치_윈도우10/images/environment_variables.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/Mysql_5.6_설치_윈도우10/images/environment_variables.jpg
--------------------------------------------------------------------------------
/DataStructuresAndAlgorithm/QuickSort/images/quicksort1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/DataStructuresAndAlgorithm/QuickSort/images/quicksort1.jpg
--------------------------------------------------------------------------------
/자바_DTO란_무엇이며_왜_쓰는가/images/spring-web-app-architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/david-learner/java-study/HEAD/자바_DTO란_무엇이며_왜_쓰는가/images/spring-web-app-architecture.png
--------------------------------------------------------------------------------
/Handlebars_버전_에러/README.md:
--------------------------------------------------------------------------------
1 | # Handlebars 버전 에러
2 |
3 | ## 에러 원인
4 |
5 | Spring boot 2.0.3을 사용중에 `handlebars-spring-boot-starter` 버전 0.2.x를 사용하니깐 다음과 같은 로그를 뱉어냈다.
6 |
7 | >org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'handlebarsBeanPostProcessor' defined in class path resource
8 |
9 | ## 에러 해결 방법
10 |
11 | handlebars 버전을 0.2.x에서 0.3.0으로 변경하기.
12 |
13 | 나는 의존성 관리를 위해 빌드도구 Maven을 사용하고 있으므로 다음과 같이 handlebars의 버전을 0.3.0으로 수정했다.
14 |
15 | ```xml
16 |
17 | pl.allegro.tech.boot
18 |
19 | handlebars-spring-boot-starter
20 |
21 | 0.3.0
22 |
23 | ```
--------------------------------------------------------------------------------
/자바_DTO란_무엇이며_왜_쓰는가/README.md:
--------------------------------------------------------------------------------
1 | # 자바 DTO란 무엇이며 왜 쓰는가
2 |
3 | ## DTO 정의
4 |
5 | DTO는 Data Transfer Object(데이터 전송 객체)다.
6 |
7 | 서로 다른 계층간 데이터 전달이 필요할 경우 DTO를 사용한다.
8 |
9 | 예를 들어 Spring Web Application 개발한다면 아래와 같이 Web Layer에서 Service Layer로 데이터를 전달하려고 할 때 DTO를 사용할 수 있다.
10 |
11 | 
12 |
13 | ## DTO와 Persistent Object
14 |
15 | Persistent Object는 Database에서 가져온 데이터를 담고 있는 객체정도로 생각하자.
16 |
17 | 일반적으로 Persistent Object의 클래스 복잡도가 크게 높지 않다면 Persistent Object는 DTO의 역할을 포함해도 괜찮다.
18 |
19 | 하지만 복잡도가 증가한다면 데이터 전달에 초점을 맞춘 객체를 만들어야 하는데 그것이 DTO가 되는 것이다.
20 |
21 | 나는 아직까지 DTO를 별도로 분리할 만큼의 복잡도를 경험해보지 않았지만 위에서 언급한 Spring Web Application Architecture를 최대한 따르려는 노력과 일부러 DTO를 만들어보는 연습을 하고 있다.
22 |
23 | ## 결론
24 |
25 | 처음부터 복잡도가 높아질 것을 예상하여 DTO를 만들게 되면 초기 리팩토링하는 단계에서 귀찮아진다.
26 | 따라서 개발 초기에는 Persistent Object에 DTO 역할을 포함시키고 이후에 복잡도가 증가하면 DTO를 따로 분리해 내는 것이 개발 속도 저하를 막을 수 있다.
27 |
28 | 평소에 점점 복잡해지는 Persistent Object에서 DTO를 분리하는 연습을 해두면 좋다.
29 |
30 | ### 참고문서
31 |
32 | * https://www.slipp.net/questions/22
33 | * https://www.slipp.net/wiki/pages/viewpage.action?pageId=2031636
34 | * https://www.slipp.net/questions/23
35 | * https://www.petrikainulainen.net/software-development/design/understanding-spring-web-application-architecture-the-classic-way/
--------------------------------------------------------------------------------
/자바_POJO란_무엇인가/README.md:
--------------------------------------------------------------------------------
1 | # POJO란 무엇인가
2 |
3 | ## POJO의 등장
4 |
5 | EJB라는 기업용 애플리케이션 개발을 돕는 것이 있었다.
6 | 기업용 애플리케이션을 개발할 때 애플리케이션의 핵심 로직을 구현하는 것이외에도 고려해야할 사항이 너무 많은 것이 문제다.
7 |
8 | EJB의 목적은 기업용 애플리케이션 개발시 고려해야할 사항을 줄이는 것이였고 실제로 그렇게 되었다. 하지만 또 다른 복잡한 문제들이 생겼고 사람들은 지쳐갔다.
9 | 이 때 등장한 것이 POJO다.
10 |
11 | POJO 프로그래밍은 객체지향 원리에 따라 만들어진 자바 언어의 기본에 충실하게 비즈니스 로직을 구현하는 것을 말한다.
12 |
13 | 그리고 단순히 EJB 이전의 프로그래밍으로 돌아가는 것이 아니라 EJB에서 좋았던 것들을 그대로 가져와서 더 나은 방식으로 개발할 수 있게 돕는 POJO기반 프레임워크가 등장하기 시작했다.
14 |
15 | POJO 기반 프레임워크로는 Hibernate과 Spring이 있다.
16 |
17 | ## POJO의 정의
18 |
19 | POJO는 Plain Old Java Object의 약자이며 평범하고 오래된 자바 객체라는 뜻이다.
20 |
21 | 자바 언어 명세에서 강제하는 것을 제외하고 어떠한 제약이 없는 자바 객체를 말한다.
22 |
23 | 따라서 상속이나 인터페이스의 구현을 강제할 수 없다.
24 | 애노테이션도 포함될 수 없다는데 왜 그런지 잘 모르겠다.
25 |
26 | > 1. Extend prespecified classes, as in
27 | >
28 | > ```public class Foo extends javax.servlet.http.HttpServlet { ...```
29 | > 2. Implement prespecified interfaces, as in
30 | >
31 | > `public class Bar implements javax.ejb.EntityBean { ...`
32 | > 3. Contain prespecified annotations, as in
33 | >
34 | > `@javax.persistence.Entity public class Baz { ...`
35 | >
36 | > wikipedia - POJO
37 |
38 | ## 결론
39 | POJO를 이용한 POJO 프로그래밍은 객체지향의 장점을 살려 프로그래밍할 수 있는 것이다.
40 |
41 | ### 참고자료
42 |
43 | * http://itewbm.tistory.com/entry/POJOPlain-Old-Java-Object
44 | * http://bsnippet.tistory.com/17
45 | * https://en.wikipedia.org/wiki/Plain_old_Java_object
46 | * https://www.quora.com/What-is-the-difference-between-POJO-JavaBeans-VO
--------------------------------------------------------------------------------
/스프링부트_프로젝트_생성하는 방법/README.md:
--------------------------------------------------------------------------------
1 | # 스프링부트 프로젝트 생성하는 방법 (인텔리제이)
2 |
3 | 1. [https://start.spring.io](https://start.spring.io) 에 접속한다.
4 |
5 | 
6 | 2. (1) - 빌드도구
7 |
8 | (2) - 사용할 언어
9 |
10 | (3) - 사용할 Spring boot 버전
11 |
12 | 3. Project Meta Data : 프로젝트 설명
13 |
14 | (4) - Group : 프로젝트 패키지가 지정하는 이름으로 생성된다.
15 |
16 | (5) - Artifact : 프로젝트 폴더 이름이 지정하는 이름으로 생성된다.
17 |
18 | 4. Dependencies
19 |
20 | (6) - 의존성 추가 : 필요한 라이브러리들을 추가할 수 있다.
21 |
22 | 여기서 지정하면 자동으로 빌드도구들의 설정에 추가된다.
23 |
24 | 5. Generate Project를 누르면 내가 설정한 것에 맞게 세팅된 프로젝트가 다운받아 진다.
25 |
26 | 6. 다운받은 프로젝트 압축파일를 자신만의 작업폴더에 압축을 풀어준다.
27 |
28 | 7. IntelliJ라면 shift를 연속으로 2번 눌러 검색창을 띄우고 `import`라고 검색하면 다음과 같이 제일 마지막에 `import project`라고 나온다.
29 |
30 | 
31 |
32 | 또는 IntelliJ 초기화면에서도 import project를 선택해도 된다.
33 |
34 | 
35 |
36 | 8. 이전에 압축을 풀어둔 프로젝트 폴더를 선택한다.
37 |
38 | 9. Import Project from external model을 선택하고 프로젝트 생성시 선택했던 빌드도구를 선택한다.
39 |
40 | 나는 Maven이었으므로 Maven을 선택했다.
41 |
42 | 
43 |
44 | 10. Search for projects recursively에 체크한다. 빌드도구 설정파일을 찾는 것이다. maven의 경우 pom.xml을 찾게 된다.
45 |
46 | 
47 |
48 | 11. 기본적으로 체크되어 있을 것이다. 만약 체크되어있지 않으면 체크해주고 넘어가자.
49 |
50 | 
51 |
52 | 12. JDK를 선택한다. 등록된 JDK가 없으면 +를 눌러 JDK를 추가해주자. JDK가 설치된 경로만 설정해주면 알아서 추가된다.
53 |
54 | 
55 |
56 | 13. 프로젝트 이름을 설정한다. start.spring.io에서 프로젝트 생성시 Artifact 항목에 기재한 것이 그대로 반영된다.
57 |
58 | 
59 |
60 | 14. 초기 구동시 각종 의존성 패키지를 받아오느라 시간이 조금 걸린다.
--------------------------------------------------------------------------------
/토비의 스프링 정독 및 요약/ch4_예외/README.md:
--------------------------------------------------------------------------------
1 | # 토비의 스프링 챕터 4 예외
2 |
3 | * 언제 checked, unckecked 예외를 쓰는가?
4 | * checked : 일반적인 예외, 애플리케이션 로직상에서 예외조건이 발견되거나 예외상황이 발생할 때 checked 예외를 던진다
5 | * unckecked : 시스템 장애나 프로그램상의 오류
6 | * Spring에서는 어느 방식을 선호하며 그 이유는 무엇인가?
7 |
8 | #### 잘못된 예외처리
9 |
10 | ``` java
11 | try {
12 | // execute sql
13 | } catch (SQLException e) {
14 | // do nothing
15 | }
16 | ```
17 |
18 | * 위 코드에서는 예외를 잡고서는 아무것도 하지 않는다. 예외 발생을 무시해버리고 정상적인 상황인 것처럼 다음 라인으로 넘어가겠다는 분명한 의도가 있는 게 아니라면 연습 중에도 절대 만들어서는 안 되는 코드다
19 |
20 | ``` java
21 | } catch (SQLException e) {
22 | System.out.println(e);
23 | }
24 | ```
25 |
26 | ``` java
27 | } catch (SQLException e) {
28 | e.printStackTrace();
29 | }
30 | ```
31 |
32 | * catch 블록을 이용해 화면에 메시지를 출력한 것은 예외를 처리한 게 아니다
33 | * 모든 예외는 적절하게 복구되든지 아니면 작업을 중단시키고 운영자 또는 개발자에게 분명하게 통보돼야 한다(throw new Exception(e))
34 |
35 | #### Runtime Exception
36 |
37 | * 런타임 예외는 예상하지 못했던 예외상황에서 발생하는 게 아니기 때문에 굳이 catch나 throws를 사용하지 않아도 되도록 만든 것이다
38 |
39 | #### 예외처리 방법
40 |
41 | * 예외복구
42 | * 예외로 인해 기본 작업 흐름이 불가능하면 다른 작업 흐름으로 자연스럽게 유도해주는 것
43 |
44 | * 예외처리 회피
45 | * 예외를 잡은 후에 로그를 남기고 다시 예외를 던지는 것
46 | * 다른 오브젝트에게 예외처리 책임을 분명히 지게 하거나, 자신을 사용하는 쪽에서 예외를 다루는 게 최선의 방법이라는 분명한 확신이 있어야 한다
47 |
48 | * 예외전환
49 | * 예외회피와는 달리 발생한 예외를 그대로 넘기는 게 아니라 적절한 예외로 전환해서 던진다
50 | * 다루기 힘든 상세한 예외정보를 의미 있고 일관성 있는 예외로 전환해서 추상화해주려는 용도
51 | * 구현체별로 비슷한 의도를 가진 예외를 하나의 추상화된 예외보 감싸는 것
52 | * 중첩 예외(nested exception) 사용하기
53 | * 왜?
54 | * 예외를 처리하기 쉽고 단순하게 만들기 위해 포장(wrap)하기
55 | * 주로 ckecked 예외를 unchecked 예외로 만들기 위해 사용한다
56 |
57 |
58 | #### 애플리케이션 예외
59 |
60 | * 애플리케이션 자체 로직에 의해 의도적으로 발생시키고, 반ㅂ드시 catch 해서 무엇인가 조치를 취하도록 요구하는 예외
61 | * 예외상황에서 비즈니스적인 의미를 띈 예외를 던지도록 만드는 것
62 | * 잔고부족일 경우 InsufficientBalanceException을 던지게 만드는 것
63 |
64 | #### 낙관적인 락킹(Optimistic locking)
65 |
66 | * 서로 다른 명령(?)이 동일한 데이터에 접근한 뒤 해당 데이터를 업데이트 하려 할 때, 앞에서 이미 데이터를 변경해버렸다면 뒤늦게 업데이트 명령을 내린 것은 변경된 데이터를 읽어서 업데이트를 다시 시도한다
--------------------------------------------------------------------------------
/용어 정리/readme.md:
--------------------------------------------------------------------------------
1 | # 용어 정리
2 |
3 | ## 서버
4 |
5 | * Web Server
6 | * WAS (Web Applicatoin Server)
7 | * Apache
8 | * Web Server의 한 종류
9 | * C언어로 개발됨
10 | * Httpd(Http Daedom)라고 불리기도 함
11 | * 로드밸런싱 가능함
12 | * Apache Tomcat
13 | * WAS의 한 종류
14 | * Java로 개발됨
15 | * Tomcat 5.5부터는 Apache(Web Server)의 native 모듈을 이용하여 Apache와 비슷한 성능으로 정적(static) 파일을 처리할 수 있음
16 | * Servlet Container를 통해 Java로 작성된 Servlet들의 life cycle을 관리할 수 있음
17 |
18 | ## 기타
19 |
20 | * JPA
21 | * Java Persistence API
22 | * 자바 진영에서 만든 ORM 표준
23 | * JPA의 구현체로는 Hibernate, TopLink, JDO가 있다
24 | * Hibernate
25 | * Hibernate ORM의 줄임말
26 | * Java를 위한 Object/Realation Mapping Framework
27 | * ORM
28 | * Object-Realation Mapping
29 | * OOP를 지원하는 시스템과 다른 시스템 사이에서 발생하는 타입 불일치를 해결하기 위해 데이터를 변환하는 프로그래밍 기술
30 |
31 | * ORM, JPA, Hibernate ORM
32 | * 자바 진영에서 ORM 표준으로 지정한 것이 JPA
33 | * JPA에 기술된 spec에 따라 구현된 구현체가 Hibernate ORM(줄여서 Hibernate)
34 |
35 | ## 자바
36 |
37 | * 클래스파일(.class)
38 | * 바이트코드
39 | * native
40 | * JNI(Java Native Interface)를 사용하는 native code로 작성된 메서드에 붙이는 접근제어자(modifier)
41 | * 오직 메서드에만 붙일 수 있다
42 | * C나 C++로 작성된 메서드는 native method 또는 foreign method 라고 부른다
43 | * native 키워드의 목적
44 | * 시스템의 성능 향샹
45 | * To achieve misson level / memory level communication
46 | * 자바가 아닌 언어로 작성된 기존 코드를 사용하기 위해서
47 | * reference
48 | * 레퍼런스란 메모리에 저장된 객체의 위치를 가르키는 것이다
49 | * C나 C++에서 사용되는 pointer와 100% 동일하지 않다 (pointer vs reference에 대해 조사하기 [링크](https://stackoverflow.com/questions/57483/what-are-the-differences-between-a-pointer-variable-and-a-reference-variable-in))
50 |
51 | * 참고
52 | * Tomcat : http://limmmee.tistory.com/4
53 | * Apache : https://askubuntu.com/questions/248404/is-there-any-difference-between-apache2-and-httpd
54 | * Tomcat VS Apache : https://stackoverflow.com/questions/38194756/usage-difference-between-apache-and-apache-tomcat
55 | * JPA : https://en.wikipedia.org/wiki/Java_Persistence_API
56 | * JPA : 책<자바 ORM 표준 JPA 프로그래밍>, 9p
57 | * ORM : https://en.wikipedia.org/wiki/Object-relational_mapping
58 | * native : https://www.geeksforgeeks.org/native-keyword-java/
--------------------------------------------------------------------------------
/Mysql_5.6_설치_윈도우10/README.md:
--------------------------------------------------------------------------------
1 | # Mysql 5.6 설치 윈도우10
2 |
3 | 1. 우선, Mysql 5.6 버전부터 다운받아야 합니다.
4 |
5 | 쉬운 설치와 여러 플러그인을 제공하기 위해 Mysql Installer를 제공하지만 Installer를 통하여 설치했을 때 Character set 설정이 제대로 적용되지 않는 문제가 발생합니다.
6 |
7 | [https://dev.mysql.com/downloads/mysql/5.6.html](https://dev.mysql.com/downloads/mysql/5.6.html)
8 |
9 | 위 링크로 들어가셔서 `Mysql Installer`가 아닌 `Mysql 5.6 zip`파일을 다운받으셔서 설치를 진행하시면 됩니다.
10 |
11 | 
12 |
13 | 본인 컴퓨터의 bit는 `내PC` 우클릭하시면 속성에서 볼 수 있습니다.
14 |
15 | 2. 다운받는 Mysql 5.6 zip파일을 적절한 곳에 압축을 풉니다. 저는 C드라이브 아래에 압축을 풀었습니다.
16 |
17 | 3. 콘솔(CMD)의 어느 곳에서든지 mysql을 실행할 수 있게 하려면 `환경변수`에 `mysql의 bin폴더 위치`를 등록해야 합니다.
18 | 1. `내PC` 우클릭하여 `속성`을 선택합니다.
19 |
20 | `내PC`가 바탕화면에 없으신 분들은 `제어판`에서 `시스템`을 선택하시면 됩니다.
21 |
22 | 2. 좌측 4번째 항목을 선택해주세요. 저는 영문 윈도우라서 `Advanced System Settings`이라고 되어 있습니다.
23 |
24 | 
25 |
26 | 4. 시스템 속성 항목에서 저는 `Advanced`라고 표시되어 있지만 아마도 한글 윈도우에서는 `고급`이라고 표시되어 있을 겁니다. 해당 항목에 들어가셔서 `환경변수` 버튼을 눌러 환경변수 메뉴로 진입합니다.
27 |
28 | 
29 |
30 | 5. 아래쪽 리스트에서 `PATH`를 찾아 선택한 후 `Edit` 또는 `편집`을 누릅니다.
31 |
32 | 
33 |
34 | 6. `NEW`를 눌르면 경로를 입력할 수 있게 커서가 깜빡입니다. 아까 압축 풀어둔 mysql폴더 아래에 있는 bin폴더의 경로를 추가해줍니다. 저는 `C:\mysql-5.6\bin`을 추가했습니다.
35 |
36 | 주의사항
37 |
38 | 만약 환경변수 설정 후 mysql 폴더를 옮기면 환경변수가 제대로 동작하지 않습니다. 다시 경로를 수정해야하는 귀찮은 상황이 생기니 미리 mysql 폴더를 적절한 곳으로 옮기시면 좋습니다.
39 |
40 | 7. 콘솔(CMD)을 `관리자 권한`으로 열어 mysql 설치와 환경변수 등록이 제대로 되었는지 다음과 같이 확인해봅시다.
41 |
42 | 1. `net start mysql` 명령을 입력하면 mysql이 실행됩니다. 만약 `access denied` 에러가 발생하면 콘솔(CMD)창을 `관리자 권한`으로 열지 않아서 발생하는 것입니다. 반대로 `net stop mysql` 명령을 입력하면 mysql 실행이 중지됩니다.
43 |
44 | 
45 |
46 | 1. 정상적으로 실행되었다면 `mysql` 명령을 입력하면 다음과 같이 mysql에 접속됩니다.
47 |
48 | 
49 |
50 | 8. mysql에 접속한 상태에서 `exit` 명령을 내려 mysql에서 빠져나와 root 계정으로 다시 접속해보겠습니다. mysql을 빠져나왔다면 `mysql -u root -p`까지만 입력하시고 엔터치시면 password를 입력하라고 합니다. root 계정의 초기 비밀번호는 없습니다. 그냥 엔터 한 번 더 입력해주시면 mysql에 접속합니다.
51 |
52 | 
53 |
54 |
--------------------------------------------------------------------------------
/DataStructuresAndAlgorithm/QuickSort/README.md:
--------------------------------------------------------------------------------
1 | # Quick Sort (퀵 정렬)
2 | ### 참고자료
3 | * [퀵정렬 유튜브 영문](https://www.youtube.com/watch?v=3DV8GO9g7B4) - step by step으로 화이트보드에 설명해줘서 아주 쉽게 이해됨
4 | * 
5 | * [퀵정렬 샘플코드 참고](https://www.geeksforgeeks.org/java-program-for-quicksort/)
6 |
7 |
8 | ``` java
9 | public class QuickSort {
10 | // input은 정렬할 배열, low는 input 배열의 시작 원소 인덱스, high는 input 배열의 끝 원소 인덱스
11 | public int[] sort(int[] input, int low, int high) {
12 | int pi; // partitioning index - pivot으로 쓰기도 하지만 pivot보다는 partitioning index가 더 사용목적에 맞는 이름
13 |
14 | // low가 high보다 크거나 같으면 sorting 종료
15 | if (low < high) {
16 | pi = partition(input, low, high);
17 | sort(input, low, pi - 1); // pi를 기준으로 좌측은 pivot보다 작은 수들의 모임
18 | sort(input, pi + 1, high); // pi를 기준으로 우측은 pivot보다 큰 수들의 모임
19 | }
20 | return input;
21 | }
22 |
23 | private int partition(int[] input, int low, int high) {
24 | int pivot = input[high]; // pivot을 정하는 여러 기준이 있지만 제일 우측의 원소를 pivot으로 선택함
25 | int i = low - 1; // pivot보다 작은 수들 중 제일 오른쪽에 위치한 원소를 가리키는 변수
26 | for (int j = low; j < high; j++) {
27 | if (input[j] <= pivot) {
28 | i++; // pivot보다 작은 수를 발견했으므로 pivot보다 작은 수들 중 제일 오른쪽에 위치하는 원소를 가리키는 변수인 i를 +1하여 작은 수들 중 제일 오른쪽에 위치한 원소를 가리키는 인덱스가 되게 한다
29 |
30 | int temp = input[j];
31 | input[j] = input[i];
32 | input[i] = temp;
33 | }
34 | }
35 |
36 | // 더이상 비교할 게 없고 pivot 이었던 제일 오른쪽의 원소와 i+1의 인덱스를 가지는 원소와 swap한다
37 | int temp = input[high];
38 | input[high] = input[i+1];
39 | input[i+1] = temp;
40 |
41 | return i+1;
42 | }
43 |
44 | // main에 작성하기보다 test로 작성해서 실행하는 편, main은 코드 돌려보실 분들 편의상
45 | public static void main(String[] args) {
46 | QuickSort quickSort = new QuickSort();
47 | int[] input = {8, 6, 1, 3, 2};
48 | int[] output = {1, 2, 3, 6, 8};
49 | System.out.println("Input : " + Arrays.toString(input));
50 | System.out.println("Output : " + Arrays.toString(quickSort.sort(input, 0, input.length - 1)));
51 | }
52 | }
53 | ```
--------------------------------------------------------------------------------
/토비의 스프링 정독 및 요약/ch2/ch2.md:
--------------------------------------------------------------------------------
1 | # 챕터2 테스트
2 |
3 | 145p
4 |
5 | - 스프링이란
6 |
7 | IoC/DI를 이용해 객체지향 프로그래밍 언어의 근본과 가치를 손쉽게 적용, 사용할 수 게 돕는 기술
8 |
9 | 복잡한 엔터프라이즈 애플리케이션을 효과적으로 개발하기 위한 기술
10 |
11 | 149p
12 |
13 | - 작은 단위의 테스트
14 | - 한꺼번에 너무 많은 것을 몰아서 테스트하면 테스트 수행 과정도 복잡해지고 오류가 발생했을 때 정확한 원인을 찾기가 힘들어진다
15 | - 테스트는 가능하면 작은 단위로 쪼개서 집중해서 할 수 있어야 한다
16 | - 관심사의 분리, 테스트의 관심이 다르다면 테스트할 대상을 분리하고 집중해서 접근해야 한다
17 |
18 | - 단위 테스트(unit test)
19 | - 150p, 왜 단위 테스트를 하는가? 코드가 원래 의도한 대로 동작하는지 빨리 확인받기 위해서(feedback)
20 | - 작은 단위의 코드에 대해 테스트를 수행하는 것
21 | - 여기서 `단위`는 크기와 범위가 정해져있지 않다
22 | - 크게보면 사용자 관리 기능을 모두 통틀어서 하나의 단위로 볼 수 있고, 작게 보자면 메소드 하나만 가지고 하나의 단위라고 생각할 수 있다
23 |
24 | > 메소드를 하나의 관심으로 보려면 메소드 또한 관심사의 분리가 잘 이루어져야 한다
25 |
26 | - 통합 테스트(integration test)
27 |
28 | 테스트 종류와 목적
29 |
30 | [http://softwaretestingfundamentals.com/software-testing-levels/](http://softwaretestingfundamentals.com/software-testing-levels/)
31 |
32 | 
33 |
34 | 테스트 도구
35 |
36 | [https://www.oss.kr/info_test/show/b3f50bf5-7d67-486f-bc70-d426d6f01dc4](https://www.oss.kr/info_test/show/b3f50bf5-7d67-486f-bc70-d426d6f01dc4)
37 |
38 | 151p
39 |
40 | - 자동화된 테스트
41 | - 테스트는 자동으로 수행되도록 코드로 만들어지는 것이 중요하다
42 | - 테스트를 잘 짰다는 가정하에 자동화된 테스트는 사람의 책임이 낮아지고 테스트의 책임이 높아진다
43 |
44 | 156p
45 |
46 | - 새로 도입한 기술의 적용에 문제가 없는지 확인할 수 있는 가장 좋은 방법은 빠르게 실행 가능하고 스스로 테스트 수행과 기대하는 결과에 대한 확인까지 해주는 코드로 된 자동화된 테스트를 만들어 두는 것이다
47 |
48 | 168p
49 |
50 | - 테스트를 안 만드는 것도 위험한 일이지만, 성의 없이 테스트를 만드는 바람에 문제가 있는 코드인데도 테스트가 성공하게 만드는 건 더 위험하다
51 |
52 | 175p
53 |
54 | - 개발자는 머릿속으로 이 코드가 잘 돌아가는 케이스를 상상하면서 코드를 만드는 경우가 일반적이다
55 | - 스프링의 창시자인 로드존슨은 "항상 네거티브 테스트를 먼저 만들라"는 조언을 했다
56 | - 테스트를 작성할 때 부정적인 케이스를 먼저 만드는 습관을 들이는 게 좋다
57 |
58 | > 만약 테스트 초보자라면 우선 긍정적인 케이스를 만들고 테스트를 만드는 것에 익숙해지면 부정적인 케이스를 먼저 만드는 습관을 들이는 것도 좋은 방법인 것 같다
59 |
60 | 177p
61 |
62 | - TDD에서는 테스트를 작성하고 이를 성공시키는 코드를 만드는 작업의 주기를 가능한 한 짧게 가져가도록 권장한다
63 |
64 | 188p
65 |
66 | @Autowired
67 |
68 | - @Autowired가 붙은 인스턴스 변수와 일치하는 컨텍스트 내의 빈을 찾는다.
69 | - 타입이 일치하는 빈이 있으면 인스턴스 변수에 주입해준다
70 | - 동일타입으로 선언된 빈이 2개 이상이면 빈의 이름과 변수의 이름이 동일한 것을 찾아 주입해준다 (우선순위 : 타입, 이름)
71 |
72 | 190p
73 |
74 | - AOP
75 |
76 | 새로운 기능을 넣기 위해 기존 코드는 전혀 수정할 필요 없다
77 |
78 | 추가했던 기능이 필요없어지면 언제든지 간단한 수정으로 제거할 수 있다
79 |
80 | 이러한 기법을 일반화 한 것이 AOP다
81 |
82 | 192p
83 |
84 | - @DirtiesContext
85 |
86 | 테스트 시 애플리케이션 컨텍스트를 새롭게 생성해줘야 하는 경우는 언제인가?
87 |
88 | 196p
89 |
90 | - 스프링 컨테이너 없이 테스트할 수 있는 방법을 가장 우선적으로 고려하자, 테스트 수행 속도가 가장 빠르고 테스트 자체가 간결하다
91 | - 여러 오브젝트와 복잡한 의존관계를 갖고 있는 오브젝트를 테스트해야 할 경우가 있다. 이때는 스프링의 설정을 이용한 DI 방식의 테스트를 이용하면 편리하다
92 | - 테스트 설정을 따로 만들었다고 하더라도 때로는 예외적인 의존관계를 강제로 구성해서 테스트해야 할 경우가 있다. 이때는 컨텍스트에서 DI 받은 오브젝트에 다시 테스트 코드로 수동 DI 해서 테스트하는 방법을 사용하면 된다. 꼭 @DritestContext 붙이기
93 |
94 | 197p
95 |
96 | 학습 테스트의 목적
97 |
98 | - 테스트를 만들려고 하는 기술이나 기능에 대해 얼마나 제대로 이해하고 있는지
99 | - 그 사용 방법을 바로 알고 있는지
100 | - 빠르고 정확하게 사용법을 익히는 것
--------------------------------------------------------------------------------
/토비의 스프링 정독 및 요약/ch3/README.md:
--------------------------------------------------------------------------------
1 | # 토비의 스프링 챕터 3 템플릿
2 |
3 | 209p, 개방 폐쇄 원칙(OCP)
4 |
5 | * 확장에는 열려 있고 변경에는 닫혀 있는 객체지향 설계의 원칙
6 |
7 | 템플릿
8 |
9 | * 변경이 거의 일어나지 않으며 일정판 패턴으로 유지되는 특성을 가진 부분을 독립시켜서 효과적으로 활용할 수있도록 하는 방법
10 |
11 | 227p, 내부 클래스
12 |
13 | * 특정 메소드에서만 사용되며, 메소드 로직에 강하게 결합되어 있다면 메소드 내에 로컬 클래스로 만들어 사용할 수 있다
14 |
15 | 234p
16 |
17 | * 스프링 빈의 설정은 클래스 레벨이 아니라 런타임 시에 만들어지는 오브젝트 레벨의 의존관계에 따라 정의된다
18 |
19 | 236p, DI 와 IoC의 차이
20 |
21 | * 의존관계 주입(DI)이라는 개념을 충실히 따르자면, 인터페이스를 사이에 둬서 클래스 레벨에서 의존관계가 고정되지 않게 하고, 런타임 시에 의존할 오브젝트와의 관계를 다이내믹하게 주입해주는 것이 맞다
22 |
23 | * 그러나 스프링의 DI는 넓게보자면 객체의 생성과 관계설정에 대한 제어권한을 오브젝트에서 제거하고 외부로 위임했다는 IoC라는 개념을 포괄한다
24 |
25 | * DI는 IoC의 수많은 기법 중 하나다
26 | * IoC를 구현하는 많은 방법 중에 DI가 포함된다
27 | * 질문, DI 하나만으로 IoC를 구현했다고 할 수 있는가? 자답하자면, No 아니다. 왜냐하면 IoC를 적용하면 오브젝트는 스스로가 어디서 만들어져서 어떻게 이용되는지 몰라야 한다. 이는 DI로만 할 수 없고 Container의 개념이 필요하다. Container에 내가 등록되어 있어야 하고, 의존관계 설정 정보에 따라 런타임에 오브젝트 간 의존관계가 맺어지기 때문이다
28 |
29 | Dependency Injection VS Dependency Inversion
30 |
31 | getClass().getResource("resourceName")
32 | * 패키지에 경로에 있는 resourceName에 해당하는 파일의 URL을 불러온다
33 | * URL과 Path 차이는?
34 |
35 | ClassPath
36 | * JVM이 프로그램을 실행할 때 클래스 파일을 찾는 데 기준이 되는 파일 경로들.
37 |
38 | * 서버에서의 classPath, 로컬에서의 classPath
39 | * 별도로 지정해줄 수 있다
40 | * 아무런 classpath가 없다면,
41 |
42 | ClassLoader
43 | * JVM안에서 동적으로 자바 클래스들을 로딩하는 JRE의 한 부분
44 | * 런타임에 클래스를 읽어올 수 있게 하는 기술
45 | * 왜 동적 로딩(런타임에 클래스를 읽어오는 것)이 중요한가? http://soul0.tistory.com/421
46 | * 자바 프로그램은 한 개 혹은 그 이상의 클래스들의 조합으로 실행된다
47 | * 프로그램 실행시 모든 클래스 파일이 한 번에 JVM 메모리에 로딩되지 않고 요청되는 순간 로딩된다(누가 요청하는가?)
48 | * 클래스 로더는 '.class' 바이트 코드를 읽어 들여 class 객체를 생성하는 역할을 담당한다(class 객체를 생성한다는 말이 뭔가?)
49 | * 클래스 로더는 클래스가 요청될 때 파일로부터 파일로부터 읽어 메모리로 로딩하는 역할을 하며 JVM의 중요한 요소 중 하나다
50 | * 클래스 로더는 classpath라는 환경변수에 등록된 디렉토리에 있는 모든 클래스들을 먼저 JVM에 로딩한다 (위에서 모든 클래스는 로딩하지 않고 요청될 때 로딩한다고 했는데..) JVM에 로딩된 클래스만이 JVM에서 개체로 사용할 수 있다
51 | * https://m.blog.naver.com/PostView.nhn?blogId=choigohot&logNo=40192701035&proxyReferer=https%3A%2F%2Fwww.google.com%2F
52 |
53 | JVM
54 |
55 | * https://javatutorial.net/jvm-explained
56 | 
57 |
58 | JVM, JRE, JDK
59 |
60 | * https://wikidocs.net/257
61 | 
62 | 
63 |
64 | 바이너리코드, 바이트코드
65 |
66 | Try with resources
67 |
68 | * 왜 리소스는 바깥에서 선언될 수 없는가?
69 |
70 | * 바깥에 선언해서 finally{}에서 널체크를 한 뒤 닫아주는 게 일반적인 로직
71 | * 그러나 try with resources에서는 리소스가 null이 아니면 알아서 리소스를 close해준다
72 | * 따라서 굳이 바깥에서 선언할 필요가 없다
73 | * https://stackoverflow.com/questions/35372148/try-with-resource-when-autocloseable-is-null
74 |
75 | JUnit : @Rule TemporaryFolder
76 |
77 | * 메이븐 패키지의 Resource 폴더는 jar로 묶이는 데, 테스트 데이터를 굳이 저기에 넣을 이유가 없다
78 | * JUnit에서 지원하는 방법으로 임시 폴더와 임시 파일을 만들어 테스트를 수행 후 다시 지우는 방법을 쓰는 게 합리적으로 보인다
79 | * https://stackoverflow.com/questions/28673651/how-to-get-the-path-of-src-test-resources-directory-in-junit
80 |
81 | 커밋 메시지 남기기
82 |
83 | * git commit -e
84 | * 기본 편집기를 열어서 커밋 메시지를 남길 수 있다
85 | * 첫 줄은 제목, 제목 아래에 한 줄 빈칸을 두고, 그 아래줄부터 기록하는 것은 메시지의 본문이다
86 | * #으로 시작하면 주석
87 | * 커밋 메시지 구조
88 |
89 | ```text
90 | 제목
91 | # 한 줄 공백
92 | 본문
93 | ```
94 |
95 | https://stackoverflow.com/a/27358418/10709777
96 |
97 | 제네릭스(Generics)
98 |
99 | * 타입이 메소드의 파라미터 타입이 되게 하는 기술
100 | * 자바에 타입 파라미터라는 개념을 도입시킴
101 | * A type variable, , can be any non-primitive type you specify: any class type, any interface type, any array type, or even another type variable.The most commonly used type parameter names are:
102 | * E - Element (used extensively by the Java Collections Framework)
103 | * K - Key
104 | * N - Number
105 | * T - Type
106 | * V - Value
107 |
108 | Callback interface를 뽑아두니 재사용율(?)이 높다. 무작정 뽑은 인터페이스가 아니라 고정된 부분과 변하는 부분을 잘 분리한 덕분인듯하다
109 | 나눠야 하는 경계를 잘 파악하는 게 인터페이스를 뽑는 데 중요한 것 같다
110 |
111 | QueryForInt();
112 |
113 | * int 결과값을 반환하는 query를 위한 메소드
114 | * Spring 3.2에서 deprecated됨
115 | * QueryForObject(sql, type class)로 동일한 결과를 낼 수 있음
116 |
--------------------------------------------------------------------------------
/토비의 스프링 정독 및 요약/ch1/ch1.md:
--------------------------------------------------------------------------------
1 | # 챕터1 오브젝트와 의존관계
2 |
3 | 노트의 목적은 최대한 핵심만 간추려 적는 것
4 |
5 | 재독을 위해 빠르게 살펴보기 위해서
6 |
7 | 대신 해당 페이지를 덧붙여 나중에 추가로 찾아볼 수 있게 하자
8 |
9 |
10 |
11 | ## 책 목록
12 |
13 | - 리팩토링(마틴파울러)
14 | - GoF의 디자인 패턴(에릭 감마 외)
15 | - 헤드 퍼스트 디자인 패턴(에릭 프리먼)
16 |
17 |
18 |
19 | 55p
20 |
21 | - 자바빈(빈)
22 | - 디폴트 생성자 : 파라미터가 없는 생성자, 리플렉션을 이용해 오브젝트를 생성할 때 필요하기 때문
23 | - 프로퍼티 : 자바빈이 노출하는 이름을 가진 속성, set/get메소드로 수정 또는 조회할 수 있는 필드
24 |
25 |
26 | 60p
27 |
28 | ## 관심사의 분리(Separation of Concerns)
29 |
30 | - 변화
31 |
32 | 객체지향의 세계에서는 모든 것이 변한다. 오브젝트에 대한 설계와 이를 구현한 코드가 변한다.그래서 개발자가 객체를 설계할 때 가장 염두에 둬야 할 사항은 바로 미래의 변화를 어떻게 대비할 것인가이다.
33 |
34 | - 절차지향 VS 객체지향
35 |
36 | 객체지향이 절차지향에 비해 초기에 좀 더 많은, 번거로운 작업을 요구하는 이유는 객체지향 기술 자체가 지니는, 변화에 효과적으로 대처할 수 있다는 기술적인 특징 때문이다
37 |
38 | - 변화 대응
39 |
40 | 변화에 대응하기 위한 가장 좋은 방법은 변화의 폭을 최소한으로 줄이는 것이다
41 |
42 | 어떻게 변경이 일어날 때 필요한 작업을 최소화하고, 그 변경이 다른 곳에 문제를 일으키지 않게 할 수 있었을까? 그것은 `분리와 확장을 고려한 설계`가 있었기 때문이다
43 |
44 |
45 | 62p
46 |
47 | 어떻게 하면 나의 메소드/클래스에서 관심사를 분리할 수 있는가? 내가 만든 메소드를 분석해서 어떤 기능이 담겨있고 이렇게 묶어야 하는 이유를 메소드 주석으로 적어보자
48 |
49 |
50 |
51 | 64p
52 |
53 | - 리팩토링
54 |
55 | 기존의 코드를 외부의 동작방식에는 변화 없이 내부 구조를 변경해서 재구성하는 작업 또는 기술을 말한다
56 |
57 |
58 | 67p
59 |
60 | - 템플릿 메소드 패턴
61 |
62 | 변하지 않는 기능은 슈퍼클래스에 만들어두고 자주 변경되며 확잘할 기능은 서브클래스에서 만들도록 하는 것
63 |
64 | - 팩토리 메소드 패턴
65 |
66 | **서브클래스**에서 구체적인 오브젝트 생성 방법을 결정하게 하는 것
67 |
68 | (팩토리 메소드 패턴은 템플릿 메소드 패턴을 포함하고 있다, '**서브클래스**'의 메소드로부터 객체를 생성하는 방법)
69 |
70 |
71 | 90p
72 |
73 | - 컴포넌트
74 |
75 | **Object-oriented view**
76 |
77 | A component is viewed as a set of one or more cooperating classes. Each problem domain class (analysis) and infrastructure class (design) are explained to identify all attributes and operations that apply to its implementation. It also involves defining the interfaces that enable classes to communicate and cooperate.
78 |
79 |
80 | 92p
81 |
82 | - IoC(Inversion of Control, 제어의 역전)
83 |
84 | 프로그램 제어의 흐름 구조가 뒤바뀌는 것
85 |
86 | 모든 오브젝트가 능동적으로 자신이 사용할 클래스를 결정하고, 언제 어떻게 그 오브젝트를 만들지를 스스로 관장한다. 모든 종류의 작업을 사용하는 쪽에서 제어하는 구조다
87 |
88 | 제어의 역전이란 이런 제어 흐름의 개념을 꺼꾸로 뒤집는 것이다. 오브젝트가 자신이 사용할 오브젝트를 스스로 선택하지 않는다. 당연히 생성하지도 않는다. 또 자신도 어떻게 만들어지고 어디서 사용되는지를 알 수 없다. 모든 제어 권한을 자신이 아닌 다른 대상에게 위임하기 때문이다. 프로그램의 시작을 담당하는 main()과 같은 엔트리 포인트를 제외하면 모든 오브젝트는 이렇게 위임받은 제어 권한을 갖는 특별한 오브젝트에 의해 결정되고 만들어진다
89 |
90 |
91 | 93p
92 |
93 | - 라이브러리 VS 프레임워크
94 |
95 | 라이브러리는 필요한 기능을 미리 만들어둔 것
96 |
97 | 프레임워크는 ?
98 |
99 | 라이브러리를 사용하는 애플리케이션 코드는 애플리케이션 흐름을 직접 제어한다
100 |
101 | 프레임워크는 거꾸로 애플리케이션 코드가 프레임워크에 의해 사용된다. 프레임워크가 흐름을 주도하는 중에 개발자가 만든 애플리케이션 코드를 사용하도록 만드는 방식이다
102 |
103 |
104 | 95p
105 |
106 | - 빈 팩토리
107 |
108 | 빈의 생성과 관계설정 같은 제어를 담당하는 IoC 오브젝트
109 |
110 | - 애플리케이션 컨텍스트
111 |
112 | 빈 팩토리를 더 확장한 것, 애플리케이션 컨텍스트는 별도의 정보를 참고해서 빈의 생성, 관계설정 등의 제어 작업을 총괄한다
113 |
114 |
115 | 96p
116 |
117 | - `@Configuration`
118 |
119 | 애플리케이션 컨텍스트 또는 빈 팩토리가 사용할 설정정보라는 표시
120 |
121 | - `@Bean`
122 |
123 | 오브젝트 생성을 담당하는 IoC용 메소드라는 표시
124 |
125 |
126 | 102p
127 |
128 | - 애플리케이션 컨텍스트 오브젝트는 하나의 애플리케이션에서 보통 여러 개가 만들어져 사용된다
129 |
130 | 왜 여러 개가 만들어져야 하는가?
131 |
132 |
133 | 서비스 오브젝트
134 |
135 | 클라이언트 오브젝트의 요청을 처리하는 오브젝트
136 |
137 | (DDD start 171p) DDD에서 응용 서비스의 역할은 도메인 객체를 사용해서 **사용자의 요청을 처리하는 것**
138 |
139 |
140 |
141 | 서블릿
142 |
143 | 자바 기반 서버에서 동작하는 프로그램
144 |
145 | 사용자 요청에 대한 기능을 제공한다
146 |
147 | 자바 클래스이며, 다양한 형태로 구현되어 쓰인다
148 |
149 | HttpServlet, DispatherServlet 등
150 |
151 |
152 |
153 | 108p
154 |
155 | 서버에서 클래스 로더를 어떻게 구성하고 있느냐에 따라 싱글톤 클래스임에도 하나 이상의 오브젝트가 만들어질 수 있다
156 |
157 |
158 |
159 | 111p
160 |
161 | - 스프링 빈의 스코프
162 |
163 | singleton, prototype, request, session
164 |
165 |
166 | 117p
167 |
168 | - DI
169 |
170 | 자신이 사용할 오브젝트에 대한 선택과 생성 제어권을 외부로 넘기고 자신은 수동적으로 주입받은 오브젝트를 사용한다
171 |
172 |
173 | 127p
174 |
175 | - DI 종류
176 |
177 | field injection
178 |
179 |
180 |
181 | constructor injection
182 |
183 | 생성자 주입을 통해 생성된 객체는 유효한 상태를 보장한다
184 |
185 | 클라이언트 오브젝트에서 서비스 오브젝트(여기서는 생성자 주입을 사용하는 객체)를 사용하려면 서비스 오브젝트를 생성해야하고, 서비스 오브젝트를 생성하려면 생성자에 의존관계에 해당하는 객체를 주입해야 하기 때문이다
186 |
187 |
188 |
189 | setter injection
190 |
191 | 생성자 주입에서 생성자의 인자가 많아지면 다루기 쉽지 않다
192 |
193 | 한 번 구성된 객체를 변경할 수 없다. 객체가 생성될 때 모든 주입에 관한 것이 결정되기 때문에.
194 |
195 | 따라서 스프링의 경우 setter injection을 추천한다
196 |
197 |
198 |
199 | [https://blog.outsider.ne.kr/753](https://blog.outsider.ne.kr/753)
200 |
201 | > 생성자 기반의 DI냐? setter 기반의 DI냐?
202 | >
203 | > 생성자 기반 DI와 Setter 기반 DI를 섞어서 사용할 수 있으므로 경험에 의하면 강제적인 의존성은 생성자 아규먼트를 사용하고 선택적인 의존성은 setter를 사용하는 것이 좋다. setter에 @Required 어노테이션을 사용해서 의존성이 꼭 필요한 setter를 만들 수 있다.
204 | >
205 | > 스프링 팀은 보통 setter 주입을 더 좋아한다. 왜냐하면, 생성자 아규먼트가 많아지면 다루기가 쉽지 않기 때문이다. 특히 프로퍼티가 선택적이면 더욱 다루기가 어렵다. setter 메서드는 나중에 다시 설정하거나 다시 주입해야 하는 클래스의 객체들을 만들 수도 있다. JMX MBean을 통한 관리는 강제적인 유즈케이스이다.
206 | >
207 | > 몇몇 순수주의자들은 생성자 기반의 주입을 좋아한다. 모든 객체의 의존성을 제공한다는 것은 객체가 항상 클라이언트한테 완전히 초기화된 상태를 리턴한다는 것을 의미한다. 이 방법은 단점은 객체가 재구성이나 재주입하기가 쉽지 않아진다는 것이다.
208 | >
209 | > 개별 클래스에 적절한 DI를 사용해라. 때때로 소스코드가 없는 서드파티 클래스를 다룰 때는 어떤 DI를 사용할 지 직접 선택해야 한다. 레거시 클래스는 어떤 setter 메서드도 제공하지 않을 수 있으므로 생성자 주입이 이용 가능한 유일한 DI일 것이다.
210 |
--------------------------------------------------------------------------------
/백기선님_스프링_핵심기술_강의_정리/readme.md:
--------------------------------------------------------------------------------
1 | # 백기선님의 스프링 핵심기술 인프런 강의 정리
2 |
3 | ## IoC 컨테이너 1부 : 스프링 IoC 컨테이너와 빈
4 |
5 | * 스프링이란?
6 |
7 | 소규모 애플리케이션 또는 기업용 **애플리케이션을 자바로 개발하는 데** 있어 유용하고 **편리한 기능을 제공하는 프레임워크**
8 |
9 | * IoC란?
10 |
11 | Inversion of Control, 의존 관계 주입(Dependency Injection)이라고도 한다.
12 |
13 | 의존 객체를 직접 생성하지 않고 주입받아 사용한다.
14 |
15 | 생성자 주입의 경우 스프링이 없어도 개발자가 직접 주입할 수 있다.
16 |
17 | > DI의 종류
18 | > * Constructor Injection
19 | > * Setter Injection
20 | > * Interface-based Injection
21 | > * Service Locator Injection
22 |
23 | * BeanFactory
24 |
25 | IoC의 가장 핵심 클래스
26 |
27 | * Bean
28 |
29 | Spring IoC 컨테이너가 관리하는 객체
30 |
31 | * 해당 객체가 Bean인지 아닌지 어떻게 알 수 있는가?
32 |
33 | IoC 컨테이너에 등록되었는가 아닌가에 따라 달라진다
34 |
35 | * 컨테이너로부터 의존성 주입을 받으려면 Bean이어야 한다
36 |
37 | * Spring IoC 컨테이너에 등록된 Bean들은 기본적으로 Singleton Scope를 가지는 Bean으로 등록된다
38 |
39 | * Scope
40 | * Singleton : 하나만 생성되어 재사용한다
41 | * ProtoType : 매번 새로운 객체를 생성한다
42 | * Singleton의 이점
43 | * 하나만으로 재사용하기 때문에 메모리 절약
44 | * 이미 컨테이너에서 만들어진 객체이기 때문에 Runtime시 성능 최적화에 유리함
45 |
46 | * 라이프 사이클 인터페이스
47 | * Spring IoC 컨테이너에 등록된 Bean에만 해당된다
48 | * Bean이 생성, 소멸될 때 다른 처리를 할 수 있다
49 |
50 | * ApplicationContext
51 | * BeanFactory를 상속받는다
52 | * BeanFactory에 비해 다양한 기능을 가지고 있다
53 |
54 | ## IoC 컨테이너 2부 : Application Context 와 다양한 빈 설정
55 |
56 | * SpringBoot의 장점
57 |
58 | dependency 관리
59 |
60 | * Spring IoC 컨테이너는 **빈 설정 파일**이 있어야 한다
61 |
62 | 
63 |
64 | * Application.xml에 하나씩 빈을 등록하는 것은 고전적인 방식
65 | * 빈의 스코프
66 | * protoType : 매번 새로운 객체를 생성
67 | * request : 요청당 새로운 객체를 생성
68 | * session : 세션당 새로운 객체를 생성
69 | * singleton : 하나의 객체로 재사용함
70 |
71 | * xml 파일에서 일일이 빈을 등록하는 게 번거로워서 등장한 것이 " "component-scan"이다
72 |
73 | * @Component 애노테이션이 붙은 클래스는 빈으로 등록된다
74 | * @Component를 확장한 애노테이션 4가지
75 | * Controller
76 | * Repository
77 | * Service
78 | * Indexed
79 |
80 | * 의존성 주입을 받을 때 사용되는 애노테이션
81 | * Autowired
82 | * Inject : 또 다른 의존성 주입이 필요함
83 | * Resource
84 |
85 | 위의 각 애노테이션의 차이점은 다음에서 확인
86 | [링크1](https://www.linkedin.com/pulse/difference-between-inject-vs-autowire-resource-pankaj-kumar), [링크2](https://www.sourceallies.com/2011/08/spring-injection-with-resource-and-autowired/#more-2350)
87 |
88 | * Java 파일을 이용한 빈 설정
89 | * @Configuration을 클래스에 붙여줘야 한다
90 | * @Configutation은 해당 클래스가 설정파일임을 알려준다
91 |
92 | * Setter 주입 VS Constructor 주입
93 | * Setter 주입을 받게 되면 주입 받는 클래스 내부에서 애노테이션을 통한 주입을 받을 수 있다, 그러나 생성자 주입을 받는 객체는 생성자에서 넘어온 객체를 통해서만 주입받을 수 있다
94 |
95 | * @SpringBootApplication 애노테이션은 ApplicationContext를 대신 생성해준다
96 |
97 | ## IoC 컨테이너 3부 : `@Autowired`
98 |
99 | * 클래스의 역할별(`@Service`, `@Controller`, `@Repository`) 애노테이션을 붙이는 이유는?
100 |
101 | AOP를 이용하여 애노테이션별로 기능을 부여할 수 있기 때문이다
102 |
103 | * 생성자, 세터, 필드에 `@Autowired`를 붙이면 객체 생성할 때 해당 애노테이션이 붙은 곳으로 의존 주입을 시도한다
104 | * 만약 자동으로 주입받지 않으려면 `@Autowired`(required = false)로 설정하면 된다. 기본값은 true
105 | * 생성자 주입의 경우 의존 주입받을 클래스가 빈으로 등록되어있지 않으면 객체가 생성되지 않는다. 그러나 세터나 필드의 경우 `@Autowired`(required = false)를 설정하면 일단 객체는 생성된다. 그러나 세터나 필드에 주입을 시도한다면 주입되지 않는다.
106 | * 왜 빈으로 등록되지 않으면 주입을 할 수 없는건가?
107 |
108 | 동일한 타입의 빈이 여러 개라면?
109 |
110 | * 에러 발생함
111 | * 해결방법 3가지
112 | * 여러 동일 빈 중 우선 사용할 빈에 `@Primary` 애노테이션을 붙인다
113 | * 주입받는 곳에 `@Qualifier`("빈 이름")을 붙여준다. 이 때 빈의 이름은 소문자로 시작해야 한다
114 | * 해당 타입의 모든 빈을 주입받을 수 있다
115 |
116 | ```java
117 | @Autowired
118 | xxxxRepository;
119 | ```
120 |
121 | List로 받으면 된다, List말고 다른 컬렉션으로도 가능할까?
122 |
123 | `@Autowired`는 타입만으로 주입받는 것이 아니라 이름으로도 빈을 찾는다, 다만 이름의 경우 빈의 이름과 주입받는 field의 이름이 동일해야 한다. 추천하는 방법은 아니다
124 |
125 | ## IoC 컨테이너 4부 : `@Component`와 컴포넌트 스캔
126 |
127 | * basePackage
128 | * basePackage : 문자열로 지정해야 한다, type-safe 하지 않음
129 | * basePackageClasses : 문자열로 전달된 클래스를 기준으로 스캔한다.
130 |
131 | * 빈 주입이 제대로 이뤄지지 않는다면 컴포넌트 스캔 범위를 잘 체크하자, 다른 패키지일 경우 스캔범위에 따라서 스캔되지 않을 수 있다
132 | * 컴포넌트 스캔에 **필터**를 걸면 필요없는 것들을 걸러낼 수 있다
133 |
134 | 컴포넌트 스캔의 2가지 특징
135 |
136 | * 스캔범위
137 | * 필터
138 |
139 | **내가 직접 빈을 등록해야 하는 상황일 때**, functional 하게 빈을 등록해줄 수도 있다 (구동시간 단축 효과), 다만 모든 빈을 functional 하게 등록하면 다시 컴포넌트 스캔이 등장하게 된 계기와 동일해지므로 남발해선 안 된다.
140 | > 내가 직접 빈을 등록해야 하는 상황은 언제일까?
141 |
142 | ## IoC 컨테이너 5부 : 빈의 스코프
143 |
144 | #### 스코프
145 |
146 | * 빈이 생성되어 사용되는 방식
147 |
148 | #### 스코프의 종류
149 |
150 | * Singleton
151 | * 애플리케이션 전체에 딱 하나만 생성되어 재사용한다
152 | * Prototype
153 | * 매번 생성하여 사용한다, 다만 각 애노테이션에 따라 생성되는 시점이 다르다
154 | * Request
155 | * Session
156 | * Websocket
157 | * ...
158 |
159 | #### 스코프의 문제상황
160 |
161 | * 프로토타입 빈이 싱글톤 빈을 참조하면?
162 | * 아무 문제가 없다
163 |
164 | * 싱글톤 빈이 프로토타입 빈을 참조하면?
165 | * 프로토타입 빈이 업데이트 되지 않는다. 즉, 새롭게 생성되어야 하는 빈이 생성되지 않고 이전과 동일한 빈이 사용된다
166 | * 해결방법
167 | * scoped-proxy
168 | * object-provider
169 | * provider(표준)
170 |
171 | #### ProxyMode
172 |
173 | * Proxy로 감싸자
174 | * 만약 TARGET이 CLASS면 CLASS기반 Proxy로 감싼다
175 | * 다른 빈들이 해당 빈을 사용할 때 프록시로 감싸진 프록시 빈을 사용하게 한다
176 | * 프록시로 감싸게 되면 싱글톤 빈 안에서 프로토타입 빈을 사용에 대해서 프로토 타입 빈은 매번 새로운 빈이 생성될 수 있다
177 |
178 | 
179 | http://seanzhou1023.blogspot.com/2015/10/4-spring-in-action-aspect-oriented.html
180 |
181 | #### TARGET_CLASS
182 |
183 | * cglib라는 써드파티 라이브러리로 가능한 것이다
184 | * JDK기반 프록시는 Interface까지만 프록시로 감쌀 수 있다
185 |
186 | #### 프록시 인스턴스
187 |
188 | * 빈으로 등록되어 있어서 필요한 곳에서 주입받으면 된다
189 |
190 | #### 프록시 패턴
191 |
192 | * 프록시 빈은 실제 빈(프록시에 의해 감싸진 빈)을 상속해서 만든 것이다. 따라서 실제 빈과 같은 타입으로 받을 수 있다
193 |
194 | #### Object Factory
195 |
196 | * 이를 이용하면 스프링코드가 프로덕트 코드에 침투하게 된다
197 | * 취향차이지만 코드 중간에 삽입되기보다는 애노테이션 선언부에만 스프링 코드가 들어가는 프록시를 이용한 방법이 나아보인다
198 |
199 |
200 | #### 싱글톤 주의점
201 |
202 | * 싱글톤의 경우 thread-safe하게 코딩해야 한다
203 | * 단점은 애플리케이션 구동시 싱글톤으로 지정된 빈을 다 만들어야 해서 구동시간이 길어질 수 있다
204 |
205 |
--------------------------------------------------------------------------------
/백기선님_스프링_기반_REST_API_개발/readme.md:
--------------------------------------------------------------------------------
1 | # 백기선님 스프링 기반 REST API 개발 발표 후기
2 |
3 | 강연정보
4 |
5 | * 강연자 : [백기선님](https://www.facebook.com/whiteship)
6 | * 강연 슬라이드 : [링크](https://www.slideshare.net/whiteship/rest-api-development-with-spring)
7 | * 소스코드 저장소 : [링크](https://github.com/keesun/study/tree/master/ksug201811restapi)
8 | * 강연안내 : [링크](http://www.ksug.org/seminar/20181110)
9 | * 일시&장소 : 2018.11.10 네이버 D2 스타트업 팩토리
10 |
11 | ## Session 1-2
12 |
13 | deview 2017의 [그런 REST API로 괜찮은가](https://www.youtube.com/watch?v=RP_f5dMoHFc) 세션에서 감명받았다
14 |
15 | #### 당부 말씀
16 |
17 | * 내가 하는 코딩에 집착하지마라
18 | * 각자의 상황과 환경에 맞는 방식으로 접근하라
19 | * 내가 spring security를 이용해서 oauth로 로그인을 구현한다고 해서 꼭 spring security를 쓰는 게 아니라 각자의 언어나 개발환경에 따라 사용자 로그인이라는 목적을 달성하면 된다. 즉, **무엇을 하려는지 의도에 집중해라**
20 |
21 | #### (영상) REST의 self-descriptive message란
22 |
23 | * 메시지에서 주어진 것만으로 메시지의 내용을 모두 해석할 수 있어야 한다
24 | * Content-Type:applicaion/json 은 기호들만 해석가능하고 키값은 해석할 수 없다. 그래서 self-descriptive 하지 않다
25 | * json의 이런점을 어떻게 해결할 수 있나?
26 | * 방법1 : Media Type을 정의하자
27 | * 방법2 : Profile
28 | * 헤더의 링크에 profile relation을 링크할 수 있지만 일부 클라이언트(브라우저)에서 제대로 지원하지 못한다
29 | * 따라서 헤더의 link대신에 바디에 실어서 보낼 것이다
30 | * 그리고 HAL을 이용할 것이다
31 | * HATEOAS : 하이퍼링크를 통해 상태가 전이
32 | * 데이터만 주고 링크가 없으면 HATEOAS가 맞는가? X
33 | * 데이터와 링크 둘 다 필요하다
34 | * HATEOAS 링크 정보들은 상황에 따라 변해야 한다
35 | * 로그인 사용자에게는 게시글을 쓸 수 있는 링크를 준다
36 | * 로그인 하지 않은 사용자에게는 게시글을 쓸 수 있는 링크를 주지 않는다
37 | * json의 링크에 해당 json에 대해 설명해놓은 profile link가 추가되어 있다
38 |
39 | #### 롬복
40 |
41 | * 롬복에서 `@EqualsAndHashcode`에서 옵션을 `of = id`로 제한한다
42 | * eqauls와 hashcode 자동생성될 때 기본값은 모든 필드를 가지고 생성한다
43 | * 이 때 순환참조가 발생할 수 있고, 이로인해 스택오버플로우가 발생한다
44 | * 그래서 생성될 때 비교 기준이 되는 것을 id로 제한한다
45 | * 롬복에서 builder만 추가하면 디폴트 생성자가 생성되지 않아서 자바빈 규약에 따르는 클래스를 만들수 없다
46 | * 따라서 디폴트 생성자를 생성하기 위해 `@AllArgsConstructor`, `@NoArgsConstructor`을 추가한다
47 | * 롬복으로 생성한 코드는 테스트커버리지에 포함되지 않아서 테스트커버리지가 낮아진다
48 | * 아마존의 어떤 팀은 테스트 커버리지를 중요시 여기지만 롬복을 사용하고 있었다, 롬복때문에 테스트 커버리지가 낮게 나왔다
49 | * 이를 극복하기 위해 롬복으로 생성되는 게터, 세터 등의 코드를 테스트 커버리지에 포함시킬 수 있게 롬복을 커스텀해서 쓰고 있다
50 | * Spring의 Meta Annotation
51 | * 롬복 구조상 지원하지 않는다
52 | * 지원하려면 구조를 갈아 엎어야 한다
53 |
54 | * Intellij에서 현재 편집중인 파일을 IDE 좌측 패키지 탐색창에서 편집중인 파일이 선택되어 자동으로 따라다니게 만드려면 AutoScrollToSource/AutoScrollFromSource을 활성화시키자
55 |
56 | 
57 |
58 | #### Slice Test
59 |
60 | 계층(layer)별 테스트
61 |
62 | * 스프링 부트에서는 계층별 테스트 만드는 데 유용한 애노테이션을 제공한다
63 | * 웹과 관련된 것들만 테스트하고 싶다면? `@WebMvcTest`
64 | * 이 때 웹과 관련된 빈만 등록되고 repository등 관련없는 빈들은 등록되지 않는다
65 | * JPA의 경우 `@DataJpaTest`
66 |
67 | * Created(201) 응답을 보낼 때에는 header의 location에 uri를 줘야 한다
68 | * HATEOAS가 제공하는 linkTo를 사용해서 uri를 만든다
69 | * ObjectMapper를 주입받아서 Event객체를 String화해서 요청을 보낸다
70 | * id를 Long이 아닌 Integer로 해도 21억개를 확보할 수 있다
71 | * Event savedEvent = repository.save(event) 이후에 event를 쓰면 제대로 동작하지 않는 경우가 있다. 저장한 뒤 event와 관련된 작업을 할거라면 savedEvent를 사용하자
72 |
73 | ``` java
74 | // eventService
75 | public Event example(Event event) {
76 | Event savedEvent = repository.save(event);
77 | //이후부터는 event 대신 저장한 뒤 리턴된 savedEvent 사용하기
78 | }
79 | ```
80 |
81 | * DTO를 도메인 객체로 쉽게 복사하기
82 | * ModelMapper 의존성 추가하여 사용
83 |
84 | ``` java
85 | Event event = modelMapper.map(eventDto, Event.class);
86 | ```
87 |
88 | * SpringBootTest 애노테이션을 붙여도 mocking된 repository가 테스트에 있으면 mocking된 repository를 사용하기 때문에 null이 리턴될 수 있다
89 | * Enum타입을 @Enumerate으로 매핑할 때 EnumType.ORDINAL보다 EnumType.STRING으로 맵핑하는 게 좋다
90 | * validation 애노테이션으로 걸러낼 수 없는 경우 validator를 만들어서 검증한다
91 |
92 | #### Validation
93 |
94 | * domain validation - 입력 값이 DTO라면..?
95 | * DTO에 대한 validator를 만든다
96 | * 에러 발생 상황에 대한 테스트를 만들 때, 에러 발생하는 코드에 브레이크포인트 찍어두고 디버깅 중 에러 객체를 까보고 뭐가 들었는지 보면서 테스트를 만든다
97 |
98 | #### Erros
99 |
100 | * Errors라는 객체는 자바빈 스펙을 준수하지 않는다
101 | * 따라서 스프링의 빈 serializer가 json으로 변환해줄 수 없다
102 | * 자바빈 스펙을 준수한다면 bean serializer가 json으로 바꿔준다
103 | * 해결방법은 serializer를 만들어주자
104 | * @JsonComponent
105 | * EventSerializer라는 클래스는 `JsonSerializer` 상속받아서 구현
106 |
107 | #### 스프링 HATEOAS
108 |
109 | * Resource - 데이터와 링크가 들어있다
110 | * 우리가 보낼 데이터(Event객체)를 Resource객체(spring hateoas)로 감싸야 한다
111 | * 데이터(Event객체)를 Resource에 넣고 데이터에 대한 링크도 추가하자
112 | * 링크 추가 방법
113 | * `new Link("문자열")`는 type safe하지 않다
114 | * `linkTo`는 type safe하다
115 |
116 | #### 테스트
117 |
118 | * Junit5에 테스트 이름을 다르게 적용하는 애노테이션이 있다 `@Displayname`
119 | * 테스트 목록에 메서드명 대신 애노테이션에 적용된 이름이 나온다
120 | * Spring의 RESTdocs는 테스트를 가지고 문서를 만든다
121 | * json이 formatting 되지 않은 채 만들어진다
122 | * RestDoc json formatting 방법
123 | * RestDocsMockMvcConfigurationCustomizer를 커스텀해서 빈으로 등록하자
124 | * RequestFields 테스트할 때, 문서화 되지 않으면 실패를 띄운다
125 | * relaxedRequestFiels라고 relaxed를 붙이면 문서화하지 않는 것에 대해서 넘어간다
126 |
127 | ## Session 3-4
128 |
129 | #### asciidoc
130 |
131 | * asciidoc에 템플릿을 넣어주고 RestDoc으로 만들어진 snippets들을 asciidoc의 템플릿에 들어갈 수 있게 한다
132 | * asciidoc tempate + snippets = docs.html
133 | * maven-resouce plugin, 생성된 docs를 static 아래에 위치시켜준다
134 | * 이 때 static디렉토리는 source쪽이 아니라 build쪽이다
135 | * springboot는 classpath아래 static에 있는 것들을 정적파일로 제공한다
136 |
137 | #### 테스트
138 |
139 | * 테스트는 h2, 애플리케이션을 띄울 때에는 postgres를 사용하게 만들고 싶다면?
140 | * `@DataJpaTest`가 적용된 테스트는 테스트 동작시 application.properties의 설정을 따르지 않고 h2를 쓴다
141 | * 테스트를 위한 properties파일을 추가하려면 (intellij기준) test/resource 디렉토를 테스트 리소스 디렉토리로 등록해야 한다
142 | * 그런데 source쪽 properties파일을 test쪽 propertiest가 덮어 쓴다
143 | * 만약 test properties가 source쪽 properties를 완전 덮어쓰는 것을 방지하고 source쪽 properties의 일부만 덮어쓰려면 profile을 사용하자
144 |
145 | #### HATEOAS
146 |
147 | * PageResourceAssembler는 Page정보를 Resource로 바꿔준다
148 | * Assembler가 page의 데이터와 링크를 알아서 추가하는데, 링크는 first, prev, self, next, last가 추가된다
149 |
150 | ### Security를 위한 준비작업
151 |
152 | #### 인증정보를 가진 상태에서 어떻게 HATEOAS를 변경할 것인가
153 |
154 | * 스프링 시큐리티는 모든 요청을 기본적으로 block한다
155 | * 인증 여부에 따라 HATEOAS의 링크정보가 달라질 수 있어야 한다
156 | * UserDetails보다 스프링 시큐리티에서 구현해놓은 User를 사용하면 내가 신경써야 하는 부분이 적어진다
157 | * 스프링 시큐리티는 서블릿필터가 모든 요청을 가로챈다. 이후 인증을 할지 말지, 서블릿 필터를 적용할지 말지를 판단한다
158 |
159 | #### 시큐리티 구분
160 |
161 | * 서블릿필터를 적용하지 않은 것
162 | * 서블릿필터를 적용하되 로그인이 필요하지 않은 것
163 | * 서블릿필터를 적용하되 로그인이 필요한 것
164 |
165 | #### oauth 토큰 발급 받는 방법
166 |
167 | * 대략 6가지 정도가 있음
168 | * password-grant type은 서비스를 제공하는 입장에서만 사용해야 한다. 사용자의 password가 토큰에 입력되어야 하기 때문이다
169 | * 생성자 주입을 받다가 순환참조가 발생했을 때, 일단 autowired로 필드 주입을 받으면 순환참조는 끊긴다
170 |
171 | #### 클라이언트 id,secret을 외부 설정파일로 빼는 방법
172 |
173 | (넋놓고 보느라 기록을 못했네요)
174 |
175 | #### 응답으로 주는 json에 사용자의 password가 실려갈 때 어떻게 할 것인가
176 |
177 | * serializer를 만들어 쓸거다
178 | * AccountSerializer
179 | * 주의 @JsonSerializer에서 fasterxml로 시작하는 패키지를 import해줘야 한다
180 |
181 | #### 오늘 한 것들
182 |
183 | * 입력값제한
184 | * Validation
185 | 단순 validation
186 | 로직 validation
187 | * 에러 보낼 때 에러에 대한 데이터도 같이 보내는 것
188 | * 페이지 정보도 Resource로 담아서 보내는 것
189 | * 인증하고 인증걸고 현재 사용자에게 접근
190 | * 현재 사용자 정보에 접근해서 hateoas 정보를 제한하는 방법
191 |
192 | ---
193 |
194 | #### 피드백 및 감사
195 |
196 | 우선, 표를 양도해주셔서 좋은 기회를 제공해주신 [김도형](https://www.facebook.com/catharsis88)님께 진심으로 감사드립니다.
197 |
198 | 평소 유튜브나 인프런을 통해 백기선님 강의를 보고 있었지만 오프라인으로 진행되는 라이브코딩을 통해 가끔씩 터져나오는 에러를 해결하는 과정을 볼 수 있어 더욱 좋았습니다.
199 |
200 | [2018 OKKYCON - The Real TDD](https://github.com/david-learner/java-study/tree/master/2018OKKYCON)가 끝난지 얼마되지 않은 이 시점에서 생각지도 못하게 REST API 개발을 TDD로 볼 수 있어서 여러 인사이트를 얻을 수 있었고 너무 좋았습니다. 시간이 부족한 관계로 모든 부분을 TDD로 하지 않으셨지만 나중에라도 강의가 올라온다면 TDD로 할 수 있는 부분을 TDD로 해주시면 학습하는 입장에서는 좋을 것 같다고 생각했습니다.
201 |
202 | KSUG 일꾼단(운영진..?)분들께서 진행의 모든 부분을 세심하게 챙겨주셔서 발표 듣는 데 전혀 불편함이 없었으며 유익한 발표를 해주신 백기선님께 감사드립니다.
203 |
204 | 최대한 듣고 이해한 내용을 쓰려고 했지만 제가 부족한 관계로 의도와 다르게 기록된 부분이나 틀린 부분이 있을 수 있습니다. 잘못된 부분을 알려주시면 수정할 수 있도록 하겠습니다.
--------------------------------------------------------------------------------
/서버_성능_향상/README.md:
--------------------------------------------------------------------------------
1 | # 서버 추가를 통한 성능 향상
2 | writer : learner
3 |
4 | ## Scalability(확장성)이란
5 |
6 | 비즈니스 요구에 맞게 시스템을 확장할 수 있는 용이성을 말한다.
7 |
8 | * 수평적 확장을 기준으로 확장성 예시
9 |
10 | 100명의 요청을 처리할 수 있는 서버 1대를 사용하고 있을 때, 1000명의 요청을 처리할 수 있게 서버를 n대로 확장시키기 쉬우면 확장성이 높은 것이고 그렇지 않으면 확장성이 낮은 것이다.
11 |
12 |
13 | ## Scale Up, Scale Out 각각의 장단점 및 차이점
14 |
15 | 
16 | ref : http://library.gabia.com/contents/infrahosting/1222
17 |
18 | ### Scale out
19 |
20 | * 서버 대수를 늘려 처리 능력을 확장하는 것이다.
21 | * 수평적 확장이라 말한다.
22 | * 예시
23 |
24 | AWS에서 EC2 인스턴스를 1대만 가지고 서비스 중이다. 이 때, 더 많은 트래픽을 처리하기 위해 EC2 인스턴스를 2대 더 추가한다면 scale out에 해당한다.
25 | * 장점
26 | * scale out 대비 성능 증가에 따른 비용이 적게 든다.
27 | * 1대의 서버에 장애가 생겨도 다른 서버에서 대신 요청을 처리할 수 있다.
28 |
29 | * 단점
30 | * n대의 서버에 트래픽을 균등하게 배분하기 위해 `Load Balancer`와 같은 장비가 필요하다.
31 | * n대의 서버가 동일한 요청에 대해 일관된 응답을 하려면 모든 서버는 동일한 데이터를 가지고 있어야 한다. 따라서 n대의 서버는 데이터 동기화에 따른 오버헤드가 발생한다. (별도의 DB, Session 서버를 두지 않는 이상)
32 |
33 | ### Scale up
34 |
35 | * 현재 운용중인 서버의 사양을 높여 처리 능력을 확장하는 것이다.
36 | * 수직적 확장이라 말한다.
37 | * 예시
38 |
39 | AWS에서 EC2 인스턴스를 1대만 가지고 서비스 중이다. 이 때, 더 많은 트래픽을 처리하기 위해 현재 운용중인 EC2 인스턴스의 사양(Cpu, RAM, SSD 등)을 업그레이드한다면 scale up에 해당한다.
40 | * 장점
41 | * `Load Balancer`와 같은 별도의 트래픽 분배 장치를 둘 필요가 없다.
42 | * n대의 서버로 분리하는 것이 아니라 1대의 서버의 사양만 증가시키므로 분산처리에 대해 고민하지 않아도 되어 서버 구축이 용이하다.
43 |
44 | * 단점
45 | * 장애 발생시 서비스가 중단될 수 있다.
46 | * sacle out 대비 확장 비용 증가폭이 크다.
47 |
48 | ## SPOF(Single Point of Failure) : 단일 장애점
49 |
50 | * 시스템 구성 요소 중에서 동작하지 않으면 전체 시스템이 중단되는 요소
51 | * 예시
52 |
53 | 기상청의 웹 서버는 1대의 날씨 DB서버를 통해 날씨 정보를 사용자들에게 알려준다. 이 때 날씨 DB서버가 고장나면 웹 서버는 사용자가 요청하는 날짜에 대한 날씨를 제공하지 못하므로 시스템이 중단된다.
54 |
55 | * 서버 구조상 이중화 되지 않은 곳은 SPOF가 발생할 수 있다.
56 |
57 | ## SPOF 발생 지점 찾기
58 |
59 | 
60 | ref : pobi
61 |
62 | * 2번, WS(NGINX)
63 | * 3번, WAS(Tomcat)
64 | * 4번, DB(MySql)
65 | * 1번과 2번 사이, Router - 외부망과 연결되는 지점인데 라우터가 고장나면 외부로부터 오는 요청을 서버에서 받을 수 없다.
66 |
67 | ## SPOF 이슈 해결 방법
68 |
69 | * SPOF가 일어나는 모든 지점을 이중화한다.
70 | * Failover를 통해 장애가 일어난 서버가 자동으로 대기 서버로 전환되어 서비스가 정상동작되게 한다.
71 |
72 | ### Failover
73 |
74 | * 장애발생시 대기중인 서버를 운영서버로 전환하는 것
75 |
76 | ### Failback
77 |
78 | * 장애발생한 서버가 운영서버에서 제외되고, 장애 복구된 다음 다시 서비스 그룹에 포함되는 것
79 | * 아래 그림을 보면 죽었던 node A가 master가 아닌 slave로 서비스 그룹에 포함된 것을 볼 수 있다.
80 |
81 | 
82 | ref : https://www.cubrid.org/manual/ko/9.1.0/ha.html
83 |
84 | ## Load Balancing
85 |
86 | ### Load Balancing
87 |
88 | * 부하 분산을 위해서 가상(virtual) IP를 통해 여러 서버에 접속하도록 분배하는 기능이다.
89 |
90 | ### Load Balancer
91 |
92 | * n대의 서버에게 균등하게 트래픽을 분산시키는 행위를 하는 장비(H/W, S/W)
93 | * H/W로는 L4스위치, L7스위치 등이 있다.
94 | * S/W로는 HAProxy가 있다.
95 |
96 | ### L4, L7의 역할 및 차이점
97 |
98 | * L4
99 | * Transport Layer에서 TCP/UDP의 포트를 기준으로 패킷을 구분하여 서버에 전송한다.
100 | * IP주소가 동일하여도 포트가 다르면 패킷을 서로 다른 서버에 보낼 수 있다.
101 |
102 | * L7
103 | * IP주소, TCP/UDP port정보, 패킷 내용까지 참조하여 각 패킷을 서로 다른 서버에 전송할 수 있다.
104 |
105 | * 차이점
106 |
107 | L4와 L7 스위치 모두 로드밸런싱이라는 핵심기능을 가지고 있지만 커버하는 Layer 특성상(L4는 Layer4까지, L7은 Layer7까지) L4보다 L7 스위치가 더 많은 정보를 바탕으로 정교한 로드밸런싱이 가능하다.
108 |
109 | 
110 |
111 | ### HAProxy
112 |
113 | * 기존 하드웨어 스위치를 대체하는 소프트웨어 로드밸런서다.
114 | * 네트워크 스위치에서 제공되는 L4, L7 기능 및 로드밸런서 기능을 제공한다.
115 |
116 | ### Proxy Server
117 |
118 | * **proxy server**는 실서버와 클라이언트 사이를 중계해주는 역할을 하는 서버다. 이 때 중계기로서 대신 요청을 주고 받는 기능을 proxy라고 한다.
119 |
120 | * **forward proxy**
121 | * 클라이언트의 요청을 받아 단순히 요청하는 곳으로 중계하는 역할
122 | * 단, 클라이언트는 외부 서버에 대해 요청을 보낼 때 forward proxy server로 요청을 보내게 되어 있어야 한다.
123 | * **reverse proxy**
124 | * 외부에서 직접적으로 접근하지 못하는 서버 자원을 대신 요청하여 중계하는 역할
125 | * revere proxy server뒤에서 실제 자원을 제공하는 서버는 별도로 있지만, 마치 reverse proxy server가 제공하는 것처럼 보여진다.
126 |
127 | ## Database
128 |
129 | ### master/slave 구조
130 |
131 | * 하나의 DB에 부하가 몰리는 것을 방지하며 Master의 내용을 slave에 복제하여 백업 기능을 가지는 구조다.
132 |
133 | * 일반적으로 master DB에 insert/update/delete가 수행되며 slave에는 select 쿼리가 수행된다.
134 |
135 | ### Sharding
136 |
137 | * sharding에 앞서 partioning에 대해 알아보자. **partioning**(이하 분할)이란 퍼포먼스(performance), 가용성(availability) 또는 정비용이성(maintainability)을 목적으로 데이터들을 다수의 테이블로 쪼개는 행위다.
138 |
139 | * partioning에는 Column별로 쪼개는 수직 분할과 record별로 쪼개는 수평 분할이 있다.
140 |
141 | * 수평 파티셔닝은 sharding과 비슷해보이지만 일반적으로 하나의 DB내에서 record를 기준으로 table을 분리한다. 이로인해 각 테이블의 크기가 감소하고 인덱스의 크기가 감소하므로 성능 향상을 기대할 수 있다.
142 |
143 | 
144 | ref : https://www.cubrid.org/manual/ko/9.3.0/shard.html
145 |
146 | * **sharding**이란 물리적으로 다른 데이터베이스에 데이터를 수평 분할방식으로 분산 저장하는 것이다. sharding의 경우 성능상 이유뿐 아니라 하나의 데이터베이스 인스턴스에 담지 못하는 큰 데이터를 분산하여 처리하기 위해 사용한다.
147 |
148 | 
149 | ref : https://www.cubrid.org/manual/ko/9.3.0/shard.html
150 |
151 | ## N대의 서버 Session 이슈
152 |
153 | 
154 | ref : pobi
155 |
156 | * Quiz : 사용자가 1번 서버에 로그인 요청을 보내 로그인 하면 sessionId가 "abc"란 이름으로 서버에 session이 생성된다. 로그인을 완료한 후 다음 요청에 대해 Load Balancer가 1번 서버가 아닌 2번 서버로 요청을 보낸다면 어떻게 될까?
157 |
158 | * 2번 서버의 세션 테이블에도 동일한 사용자에 대한 sessionId가 등록된다. 즉, 1번 서버의 세션 테이블과 중복되는 것이다.
159 |
160 | * 서버가 1대에서 n대로 증가하는 경우 발생하는 session 이슈를 해결할 수 있는 방법은?
161 |
162 | * n대의 서버가 서로 session table을 동기화하여 동일한 상태를 유지한다. (session clustering)
163 | * 별도의 session 서버를 두어 관리한다.
164 | * 운영 서버 앞단에 로드밸런서를 두고 sticky session을 이용하여 정해진 서버로 보낸다.
165 |
166 | * **sticky session**
167 |
168 | * 사용자 요청에 대한 응답을 보낼 때, cookie에 해당 서버에 대한 정보를 담아서 다음 요청 때 로드밸런서가 cookie에 저장된 서버 정보를 통해 동일한 서버에 사용자가 연결되게 하는 기술
169 |
170 | * **session clustering**
171 |
172 | * n대의 서버가 서로 세션 정보를 공유하여 모든 서버 해당 사용자를 인식하고 요청을 처리할 수 있다.
173 |
174 | * tomcat의 경우 multicast 방식으로 session 정보를 전파하는 session clustering도 지원한다.
175 |
176 | * session server
177 |
178 | * n대의 서버가 세션을 참조하기 위한 서버
179 | * n대의 서버가 세션 정보를 위해 빈번하게 접근하므로 redis와 같은 in-memory 기반의 데이터 저장소가 쓰인다.
180 |
181 | ## N대의 서버 Cache 이슈
182 |
183 | 
184 | ref : pobi
185 |
186 | * 현재 자신이 구현하고 있는 서비스 중에서 cache를 적용하면 좋은 데이터가 있다면 무엇인가?
187 | * 자주 변경되지 않는 static file들(html, css, js, image 등)
188 | * 페이스북의 경우 첫 화면에 보여줄 피드 일부(검증X)
189 |
190 | * n대의 서버에서 중복 관리되는 cache 데이터를 효과적으로 관리하기 위한 방법은?
191 | * 캐시 데이터만 관리하는 별도의 캐시 서버를 구축한다.
192 |
193 | * Memcached 또는 redis를 활용한 cache 적용 전략은?
194 | * 만약 cache 서버가 1대라면 자주 사용되는 데이터 위주로 cache를 적용한다.
195 | * cache 서버가 2대 이상이라면 ?
196 | 1. range, 범위로 구분하여 데이터를 분산하여 cache를 적용한다. 범위가 너무 크면 cache 서버별 사용 리소스가 크게 차이날 수 있다. 예를 들어 1번 서버 range에 속하는 데이터가 집중 cache hit되면 1번 서버 사용 리소스가 높아지는 것이다.
197 | 2. consistent hashing, 애초에 웹 캐시를 위해 고안된 방법이다. 자세한 방법은 [링크](https://www.joinc.co.kr/w/man/12/hash/consistent)
198 | 3. indexed, 캐싱된 데이터가 어디있는지 index해둔 서버를 별도로 구축하는 방법
199 |
200 | ## Node.js VS tomcat
201 |
202 | ### Node.js
203 |
204 | * Node.js는 single thread 기반으로 동작하는 고성능 비동기 IO(Async / Non-blocking IO)를 지원하는 네트워크 서버이다.
205 |
206 | * event 기반 프로그래밍 모델을 사용하고 있다.
207 |
208 | * Javascript 기반이고 개발구조가 단순화되어 있어 빠르게 개발이 가능하다. FrontEnd(FE) 개발자 입장에서는 Javascript로 BackEnd(BE) 개발이 가능하다보니 굳이 백엔드를 위해 Javascript를 다루지 않는 엔지니어를 뽑을 필요 없으며 성능상의 이점보다 Learning Cruve나 조직내 FE/BE 기술통합의 이점을 더 크게 생각할 수 있다.
209 |
210 | ### single thread 기반인 Node.js의 동작방식은?
211 |
212 | 
213 |
214 | * Node.js는 V8 Javascript 엔진을 기본으로 동작한다. 구글의 chrome 브라우저에서 사용된다.
215 |
216 | 
217 |
218 | * Node.js가 사용자 요청을 처리하는 내부 동작은 위 그림과 같다.
219 | 1. 사용자 요청은 Node.js의 **event Queue**에 들어간다.
220 | 2. single thread로 동작하는 **event loop**는 event queue에서 요청을 하나 꺼내어 처리한다.
221 | 3. 이 때, DB, File IO와 같이 **blocking IO**가 발생하는 일들은 내부의 **thread pool**에 있는 하나의 thread에 위임한다. 즉, 내부적으로는 node.js도 multi thread를 활용하는 것이다. 만약 내부적으로 multi thread를 이용하지 않는다면 blocking IO가 필요한 요청이 올 때마다 event loop는 해당 요청을 처리하기 위해 잠시 멈추어야 한다. sinlge thread의 빠른 처리 이점이 사라지는 것이다.
222 | 4. 처리된 결과는 다시 사용자에게 응답으로 반환된다.
223 |
224 | ### multi thread 기반인 Tomcat의 동작방식은?
225 |
226 | 
227 | 
228 | * tomcat의 내부는 위의 그림과 같다.
229 | * 사용자의 요청이 서버에 도달하면, socket을 추상화시킨 HTTP Connector를 통해 사용자의 요청이 웹 애플리케이션까지 도달한다.
230 | * Conncetor들은 Thread Pool에서 관리되며, 한 사용자의 요청은 하나의 Connector가 담당한다. 동시다발적으로 다수의 사용자가 요청을 보내면 다수의 Connector가 사용자의 요청을 처리한다.
231 | * A라는 connector가 사용자B의 요청을 처리하고, 사용자B에게 보낼 응답까지 담당한다.
232 | * Connetor는 tomcat 서버 시작시 최소 개수를 정할 수 있으며, 필요하면 지정한 최대 개수만큼 Connector를 생성할 수 있다. 사용량이 적어져 사용자의 요청을 처리하지 않는 connector들은 idle 상태로 전환되며 최소 개수까지 줄어들 수 있다. 이는 모두 tomcat 서버에서 세팅이 가능하다.
233 |
234 | ### 각 서버의 장단점을 비교하고 사용하기 적합한 방식은?
235 |
236 | * single thread 기반 nodejs는 CPU 작업이 많은 웹 애플리케이션에서는 사용을 자제해야 한다. blocking io function을 사용하는 작업은 내부의 multi thread에 맡기지만 그게 아닌 작업들은 single thread로 처리한다. 이 때 CPU 사용이 많은 하나의 요청을 처리하는 데 작업 시간이 길어질테고, 뒤에 다른 요청들이 지연될 것이다. 그러나 websocket.io을 이용한 채팅서비스를 비교적 쉽게 만들 수 있거나 Single Page App 개발에 적합하다.
237 |
238 | * multi thread 기반 tomcat은 대규모의 사용자 요청과 로직의 복잡도가 높아 CPU 사용률이 높은 작업의 경우 tomcat을 사용하면 좋다. 다만, thread pool에서 대기중인 connector들이 즉각적으로 요청 처리 후 사라지는 게 아니라 비교적 자원낭비가 있을 수 있다.
239 |
240 | ## Cache
241 | cache란 빈번히 사용되는 데이터를 특정 장소(일반적으로 읽기 속도가 더 빠른 저장장치)에 복사해두어 데이터 요청에 대해 더 빠른 응답을 할 수 있게 하는 기술
242 |
243 | ### 어떤 성격의 데이터를 cache하는 것이 적합한가?
244 | * 변화가 적은 static file들
245 | * image, css, js 등
246 | * 게시판의 최신 글들
247 | * 보통 게시판을 접속하면 사용자들이 처음보는 화면은 최신 게시글들 이기 때문에
248 |
249 | ### 캐시 교체 알고리즘인 FIFO, LRU, LFU 는 무엇이며 어느 경우에 적합한가?
250 |
251 | 아래의 알고리즘들은 효율적인 캐시 데이터 저장공간 운용을 위해 캐시 데이터를 삭제할 때 어떤 데이터를 삭제할지 고르는 기준이 된다.
252 |
253 | * FIFO (First In First Out)
254 | * 먼저 저장되었던 순서대로 데이터를 교체한다. 제일 먼저 저장되었던 캐시 데이터가 제일 먼저 교체된다.
255 | * FIFO는 상대적으로 매우 단순한 구조를 가진다. 비교적 복잡한 캐시 교체 알고리즘 연산을 수행할만큼 자원이 허락하지 않는다면 FIFO 알고리즘 사용을 고려할 수 있다. 일관된 순서를 유지하며 데이터를 사용할 경우 효율적이다.
256 |
257 | * LRU (Least Recently Used)
258 | * 가장 오래 사용되지 않은 데이터를 교체한다. 일반적인 상황에서 가장 효율이 좋은 알고리즘.
259 |
260 | * LFU (Least Frequently Used)
261 | * 가장 적게 사용된 데이터를 교체한다. 몇 번 사용되었는지 count 해줘야 한다.
262 |
263 | ### 참고
264 | https://docs.microsoft.com/ko-kr/biztalk/core/what-is-scalability
265 | http://www.ktword.co.kr/abbr_view.php?m_temp1=868
266 | http://library.gabia.com/contents/infrahosting/1222
267 | http://idchowto.com/?p=29915
268 | https://m.blog.naver.com/PostView.nhn?blogId=islove8587&logNo=220548900044&proxyReferer=https%3A%2F%2Fwww.google.co.kr%2F
269 | http://www.thisisgame.com/webzine/news/nboard/4/?n=54955
270 | https://www.slideshare.net/heungrae_kim/14-jco-by-javacafe?qid=0b505c3f-9f9e-4392-9484-4eee1c72ea19&v=&b=&from_search=2
271 | https://www.cubrid.org/manual/ko/9.1.0/ha.html
272 | http://klero.tistory.com/entry/L2-L3-L4-L7-%EC%8A%A4%EC%9C%84%EC%B9%98-%EA%B5%AC%EB%B6%84-%EB%B0%8F-%EA%B8%B0%EB%B3%B8%EC%A0%81%EC%9D%B8-%EC%84%A4%EB%AA%85
273 | https://www.freeism.co.kr/wp/archives/698
274 | https://sarc.io/index.php/miscellaneous/758-osi-7-l4-l7
275 | http://soul0.tistory.com/140
276 | https://d2.naver.com/helloworld/284659
277 | https://ko.wikipedia.org/wiki/%ED%94%84%EB%A1%9D%EC%8B%9C_%EC%84%9C%EB%B2%84
278 | http://happymemoryies.tistory.com/13
279 | http://idess.tistory.com/6
280 | https://code.i-harness.com/ko/q/36d98
281 | http://httpd.apache.org/docs/2.4/mod/mod_proxy.html#page-header
282 |
283 | * db
284 |
285 | * https://www.xpressengine.com/forum/20657238
286 | * http://theeye.pe.kr/archives/1917
287 | * https://www.cubrid.org/manual/ko/9.3.0/shard.html
288 |
289 | * session
290 | * http://bcho.tistory.com/794
291 | * http://12bme.tistory.com/196
292 |
293 | * cache
294 | * https://charsyam.wordpress.com/2016/07/27/%EC%9E%85-%EA%B0%9C%EB%B0%9C-%EC%99%9C-cache%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94%EA%B0%80/
295 | * http://ojava.tistory.com/70
296 | * https://www.joinc.co.kr/w/man/12/hash/consistent
297 | * https://www.slideshare.net/OnGameServer/ss-10451675
298 |
299 | * Nodejs, Tomcat
300 | * https://developers.google.com/v8/
301 | * http://bcho.tistory.com/881
302 | * http://windowx.tistory.com/entry/apache-tomcat-diagram
303 | * http://linux.systemv.pe.kr/tomcat-7-%EA%B5%AC%EC%A1%B0/
304 | * https://www.slideshare.net/jieunsys/ss-56543446
305 | * http://jang8584.tistory.com/14
306 | * https://tomcat.apache.org/tomcat-6.0-doc/config/executor.html
307 | * http://ttlnews.blogspot.com/2016/02/tomcat-7-request-processing-threading.html
308 |
309 | * Cache
310 | * https://ko.wikipedia.org/wiki/%EC%BA%90%EC%8B%9C
311 | * http://onecellboy.tistory.com/260
312 | * https://github.com/clojure/core.cache/wiki/FIFO
313 | *
--------------------------------------------------------------------------------
/JPA_기초/README.md:
--------------------------------------------------------------------------------
1 | # JPA 프로그래밍 기본기 다지기
2 |
3 | 연사
4 |
5 | * 김영한님
6 | * 우아한 형제들 재직중
7 | * 저서 [<자바 ORM 표준 JPA 프로그래밍>](http://www.kyobobook.co.kr/product/detailViewKor.laf?ejkGb=KOR&mallGb=KOR&barcode=9788960777330&orderClick=LAG&Kc=) (a.k.a. 국내 JPA 바이블)
8 |
9 | 
10 |
11 | 일시 및 장소
12 |
13 | * 2018년 11월 22일 목요일
14 | * SK플래닛 T아카데미 (서울대연구공원단지내 SK연구동)
15 |
16 | ## 왜 JPA인가
17 |
18 | * 데이터베이스 세상의 헤게모니는 관계형 데이터베이스가 들고 있다
19 | * 지금 시대는 객체를 관계형 데이터베이스로 관리한다
20 | * NoSQL, MongoDB 등은 특수한 경우가 아니라면 관계형 데이터베이스가 메인이며 서브로 가져다 쓰는 정도
21 | * 관계형 데이터베이스로 개발하다보면 CRUD의 무한반복
22 | * 특정 클래스에 필드 추가시, 해당 클래스의 필드와 관련된 CRUD 쿼리를 다 수정해야 한다, 휴먼에러 발생확률도 높다
23 | * 엔티티 신뢰 문제, DAO에서 Member를 가져왔지만 Member가 들고 있어야 하는 정보를 들고 있는지 모른다, 이 때 member.getTeam()을 했을 때 팀의 정보가 없으면 NullPointerException이 터질 수 있다
24 | ```java
25 | Class Member {
26 | private Long id;
27 | private String name;
28 | private Team team;
29 | // getter & setter
30 | }
31 | ```
32 | * 객체와 관계형 데이터베이스는 패러다임이 다르다
33 | * 그러나 객체를 저장할 수 있는 현실적인 대안은 관계형 데이터 베이스, 많이 봐줘야 NoSQL정도
34 | * 객체 -> SQL 변환 -> SQL 실행(관계형 데이터베이스에 저장), 이 때 객체를 관계형 데이터베이스에 저장하기 위한 SQL 변환 작업, 즉 SQL Mapper의 역할을 개발자가 하고 있다
35 | * 패러다임의 차이
36 | * 상속
37 | * (객체) 객체 상속 관계
38 | * (관계형 데이터베이스) Table 슈퍼타입 서브타입 관계, 사실 관계형 데이터베이스는 상속이 없다 (특정 DB에서는 상속이 있긴 하지만 객체에서의 상속과 다르다)
39 | * 그래서 개발자의 편의를 위해 DB에 저장할 객체는 상속관계를 만들지 않는다, SuperDTO를 만들고 여기에 필요한 모든 상태 값을 넣어두고 DB에 저장한다
40 | * 연관관계
41 | * 객체는 레퍼런스를 통해 다른 객체를 참조한다
42 | * 테이블은 외래키를 통해 다른 테이블을 참조한다
43 | * 객체의 참조는 방향성을 가진다
44 | * 테이블의 참조는 단방향을 가지지 않는다, 참조하고 있는 키(외래키)를 통해 양쪽 테이블을 참조할 수 있다
45 | * 패러다임이 변하면 코딩량이 폭주하고, 코드가 길어질수록 버그도 폭주한다
46 |
47 | * 객체 그래프 탐색
48 | * 객체는 자유롭게 객체 그래프를 탐색할 수 있어야 한다
49 | * 관계형 데이터베이스의 경우 처음 실행하는 SQL에 따라 탐색 범위가 결정된다
50 | > * `SELECT * FROM member;`라면 member테이블에 해당하는 데이터만 탐색할 수 있다. 즉, 탐색 범위가 member테이블로 한정되는 것이다
51 | > * 만약 member테이블과 team테이블을 Full Outer Join을 하게될 경우 탐색범위는 member와 team으로 한정되며, 이전에 member만 조회할 때보다 탐색범위가 늘어나게 된다
52 |
53 | * 객체지향적으로 설계할수록 맵핑량이 늘어난다
54 | > * 객체지향적으로 설계한다는 것은 필요에 따라서 객체의 책임과 역할에 맞게 객체를 생성(또는 분리)하게 되는 것이며 객체가 늘어날수록 이에 대응하는 테이블 또는 테이블 설계가 복잡해진다
55 | > * 테이블이 늘어나고 테이블 설계가 복잡해질수록 객체의 상태를 DB에 저장하기 위한 맵핑 코드는 늘어날 수 밖에 없는 것이다
56 |
57 | * JPA
58 | * 자바 진영 ORM 표준
59 | * java 애플리케이션과 jdbc 사이에서 동작한다
60 |
61 | ||
62 | |:--:|
63 | |*[how-to-manage-data-persistence-with-mongodb-and-jpa](https://www.developer.com/java/data/how-to-manage-data-persistence-with-mongodb-and-jpa.html)*|
64 |
65 | * 단순히 쿼리만 대신 짜는 게 아니라 패러다임 불일치를 해결한다
66 | * DB에서 데이터를 조회할 경우 해당 데이터를 반환하는게 아니라 반환되는 데이터에 해당 하는 객체에 데이터를 담아서 객체를 반환한다
67 | * EJB의 엔티티 빈(JPA 초창기 기술, 자바 표준) -> 하이버네이트(오픈 소스) -> JPA(자바 표준)
68 | * EJB가 너무 좋지 않아서 ~~구려서~~ 로드존슨이 화가 나서 ~~빡쳐서~~ 스프링 프레임워크를 만들고, Gavin King은 EJB의 Entity Bean을 대체할 Hibernate를 개발한다
69 | * ORM
70 | * Object-Relational Mapping
71 | * 객체는 객체대로 설계
72 | * 관계형 데이터베이스는 관계형 데이터베이스대로 설계
73 | * ORM 프레임워크가 중간에서 맵핑한다(개발자의 노가다 대체)
74 |
75 | * 왜 JPA를 사용해야 하는가?
76 | * 생산성
77 | * 객체의 필드 수정시 setFoo("bar")를 해주는 것만으로도 DB의 데이터가 수정될 수 있으며, 트랜잭션 커밋시 변경된 사항을 찾아서 update해준다
78 | * 연관관계
79 | * `member.setTeam(team);` `jpa.persist(member);` 했을 때, member와 team 모두 저장된다
80 | > * member만 저장했을 뿐인데, JPA는 member가 가지고 있는 team 객체에 대해서도 저장한다
81 | * DB로부터 member 객체만 반환받았는데 이 때, member객체가 가지고 있는 team객체에 대해서도 접근할 수 있다. `Team team = member.getTeam();` 명령을 내렸을 때 JPA는 어떤 방식으로 team객체를 DB로부터 가져오는가? 기본적으로는 team객체를 사용하려고 할 때(`member.getTeam()`), team이 없으면 team을 가져오는 쿼리를 날린다. 이를 지연 로딩(Lazy Loading)이라고 한다. 만약 member객체를 DB로부터 가져올 때 team객체에 대한 것도 함께 가져오고 싶다면 즉시 로딩(Eager Loading)을 사용하면 된다
82 | * JPA와 비교
83 | * 동일한 트랜잭션 내에서 조회한 엔티티는 동일성을 보장한다
84 | > * A라는 트랜잭션 내에서는 조회한 엔티티 apple객체가 A 트랜잭션 외부에서 변경이 되어도 apple객체는 변경되지 않는 상태를 유지함을 통해 동일성을 보장하는 것이다
85 | > * 설정을 통해 엔티티(apple객체)가 변경되었을 때 변경된 엔티티 사용을 위해 트랜잭션을 끊고 새로운 트랜잭션을 시작할 수도 있다
86 | * JPA의 성능 최적화 기능
87 | * 중간에 JPA Layer가 있어서 느린게 아닌가요? 오히려 중간에 JPA Layer가 있어서 성능 최적화를 포함하여 더 많은 것을 할 수 있다
88 | * 1차 캐시와 동일성(identity)를 보장
89 | * 같은 트랜잭션 안에서는 같은 엔티티를 반환함으로 약간의 조회 성능 향상
90 | * transaction isolation level이 read committed여도 애플리케이션에서 repeatable read를 보장한다
91 | > * read committed, repeatable read 차이 [참고1](https://stackoverflow.com/questions/4034976/difference-between-read-commit-and-repeatable-read), [참고2](https://lng1982.tistory.com/287)
92 | > * read committed
93 | > 
94 | > 1. 트랜잭션1에서 x라는 것을 DB로부터 읽었을 때 x의 값은 10이다.
95 | > 2. 트랜잭션2에서는 x의 값을 20으로 수정했고 commit을 했다. 이후에 트랜잭션1에서 x를 읽으면 변경된 값 20을 읽어온다.
96 | > 3. 만약 트랜잭션2에서 x의 값을 20으로 수정 후 commit을 하지 않았다면, 이후에 트랜잭션1에서 다시 x를 읽었을 때 값은 수정되기 전의 x의 값인 10을 읽게 된다
97 | > * repeatable read
98 | > 
99 | > 1. 트랜잭션1에서 x를 DB로부터 읽어온 값이 10이라면 다른 트랜잭션에서 x의 값이 수정된 후 commit되어도 처음에 읽어온 x의 값(x의 상태)은 트랜잭션1이 끝나기 전까지 동일하게 유지된다
100 | > 2. JPA가 동일 트랜잭션 내에서 엔티티의 동일성을 보장한다는 말은 이것을 두고 하는 말이다
101 | * 트랜잭션을 지원하는 쓰기 지연
102 | * 트랜잭션을 커밋할 때까지 insert query을 모은다
103 | * jdbc batch sql 기능을 사용해서 sql 전송한다
104 | * 동일한 타입의 객체 insert query를 여러 번 하게 되면 모아서 커밋할 때 한 방에 insert한다
105 | * 지연 로딩과 즉시 로딩 차이점
106 | ``` java
107 | // lazy
108 | Member member = memberDAO.find(memberId); // select * from member
109 | Team team = member.getTeam();
110 | String teamName = team.getName(); // select * from team; team객체가 필요한 시점에 로딩(select)이 발생한다
111 |
112 | // eager
113 | Member member = memberDAO.find(memberId); // select M.*, T.* from member join team ... 멤버와 팀의 조인쿼리로 멤버와 팀의 데이터를 불러온다
114 | Team team = member.getTeam();
115 | String teamName = team.getName();
116 | ```
117 | * ORM은 객체와 관계형 데이터베이스 두 기둥위에 있는 기술
118 | * 객체지향적으로도 잘 짜야하고, 관계형 데이터베이스(그 중에서도 query)를 잘 다룰 수 있어야 한다
119 |
120 | ## JPA 기초와 매핑 (실습)
121 |
122 | #### H2 데이터베이스
123 |
124 | * http://h2database.com
125 | * 최고의 실습용 DB
126 | * 1.5mb
127 |
128 | * 영속성 컨텍스트
129 | * @Entity : JPA가 관리할 객체를 엔티티라 한다
130 | * @Id : Primary Key 지정
131 |
132 | #### JPA 설정 파일
133 |
134 | * persistence.xml
135 | * DB 접속 정보, Query와 관련된 설정을 할 수 있다
136 | * `show_sql = true`이면 생성되는 sql을 볼 수 있다
137 | * javapersistence패키지는 자바 표준
138 | * hibernate.dialect : 방언
139 | * 방언이란 SQL 표준을 지키지 않거나 특정 데이터베이스만의 고유한 기능
140 | * JPA는 특정 데이터베이스에 종속적이지 않은 기술
141 | * 각각의 데이터베이스가 제공하는 SQL 문법과 함수는 조금씩 다르다
142 |
143 | * JPA는 Getter/Setter가 있어야 데이터를 읽고 쓸 수 있다? 없어도 된다
144 | * SpringBoot를 쓰면 알아서 처리하는 부분이 많기 때문에 JPA 내부적으로 어떻게 작동하는지 자세히 알 수 없다
145 | * 표준 스펙에 따라 동작하는 것을 한 번 볼 필요가 있다
146 | * 트랜잭션마다 엔티티매니저를 새로 생성해야 한다
147 | * 엔티티 매니저 팩토리는 하나만 생성해서 애플리케이션 전체에서 공유해야 한다
148 | * 엔티티 매니저는 쓰레드간에 공유하면 안된다, 사용하고 버려야 한다
149 | * DB 커넥션당 엔티티 매니저 하나가 바인딩(묶이는 것)되기 때문에 돌려쓰면 다른 커넥션이랑 엮여서 문제가 생길 수 있다
150 | * JPA의 모든 데이터 변경은 트랜잭션 안에서 실행
151 |
152 | ## 필드와 컬럼 매핑
153 |
154 | ### 데이터베이스 스키마 자동 생성
155 |
156 | * DDL을 애플리케이션 실행 시점에 자동 생성
157 | * 테이블 중심 -> 객체 중심
158 | * 데이터베이스 방언을 활용해서 데이터베이스에 맞는 적절한 DDL 생성
159 | * 이렇게 생성된 DDL은 개발 장비에서만 사용
160 | * 생성된 DDL은 운영서버에서는 사용하지 않거나, 적절히 다듬은 후 사용
161 | * hibdernate.hbm2ddl.auto 설정
162 | * (몇천만건)운영DB에서 update를 사용하면 장애로 이어진다
163 | * 운영장비에서는 절대 create, create-drop, update를 사용하면 안 된다
164 | * 개발초기 create or update
165 | * 테스트는 update or validate
166 | * 스테이징과 운영은 validate or none
167 | * WAS가 붙어있는 운영DB에서는 create 등을 막아둬야 한다
168 | * @Enumerated의 옵션은 EnumType.STRING으로 사용하자
169 | * [우아한형제들 기술블로그 이동욱님 글 참고](http://woowabros.github.io/tools/2017/07/10/java-enum-uses.html)
170 | * @Temporal : 날짜 맵핑할 때 사용한다
171 | * @Lob : 컨텐츠 길이가 긴 경우 DB에 데이터를 바이너리로 밀어넣을 때 사용한다, 타입을 String으로 쓰면 CLOB, Byte를 쓰면 BLOB
172 | > * [CLOB](https://techterms.com/definition/clob) : 대용량의 문자 기반 데이터를 저장하는 데 적합하다
173 | > * [BLOB](https://techterms.com/definition/blob) : 대용량의 바이너리 데이터를 저장하는 데 적합하다, 사진, 동영상 등의 자료를 바이너리화해서 DB에 넣을 때 사용하는 타입이다
174 | * @Transient : DB에 매핑하고 싶지 않은 필드에 사용한다
175 | #### 키 생성 전략
176 | * 직접 할당 : 기본 키를 애플리케이션에서 직접 할당한다
177 | * AUTO : 대리 키를 사용
178 | * IDENTITY : 기본 키 생성을 데이터베이스에 위임한다
179 | * SEQUENCE : 데이터베이스에 시퀀스를 생성하고 이를 이용하여 기본 키를 생성한다
180 | > * 시퀀스란 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트다
181 | > * 서로 다른 테이블이지만 동일한 시퀀스를 사용하여 기본 키를 생성한다면 동일한 키가 생성되지 않는다. 즉, id가 기본 키라면 양쪽 테이블에 id값이 같은 게 존재하지 않는다
182 | * TABLE : 키 생성 테이블을 사용하여 기본 키를 생성한다
183 |
184 | #### 권장하는 식별자 전략
185 |
186 | * 기본 키 제약 조건 : not null, 유일해야 하고, 변하면 안된다
187 | * 현재 위 조건을 만족할 수 있지만 미래까지 이 조건을 만족하는 자연키는 찾기 어렵다, 대리키(대체키)를 사용하자
188 | * **권장 : Long + 대체키 + 키 생성전략 사용**
189 | * 대체키 -> 비즈니스와 전혀 관련없는 키를 잡아서 써라, 예를들면 autoincrement나 oracle의 sequence나 등등등..
190 | * UUID를 쓸 수도 있지만 성능이슈가 있으므로 잘 알아보고 써야 한다
191 | * 이론으로는 비즈니스키를 조합해서 쓰게 배웠는데 실무에서는 너무 많은 변수가 있었다
192 |
193 | #### 객체
194 |
195 | * 왜 id를 Long(Wrapper Class)로 쓰는가? primitive type의 경우 0으로 세팅된다, wrapper의 경우 null로 된다, 상관없지만 primitive type의 경우 id가 0으로 세팅되므로 id만 놓고 봤을 때 새로운 객체인지 이전에 쓰였던 객체인지 사람이 판단할 수 없다. 0도 값으로 사용될 수 있기 때문에.
196 | * 객체를 데이터 중심으로 모델링하면 객체지향적인 설계. 즉, 객체간 협력 관계를 만들 수 없다
197 | * 테이블과 객체 사이의 간극
198 |
199 | ### 단방향 매핑
200 |
201 | * Member는 Team 필드를 가진다, Team은 Member 필드를 가지지 않는다. 즉, Member만 Team을 참조할 수 있다
202 |
203 | ```java
204 | public Class Member {
205 | private Long id;
206 | private String name;
207 | private Team team;
208 | // getter & setter
209 | }
210 |
211 | public class Team {
212 | private Long id;
213 | private String name;
214 | // getter & setter
215 | }
216 | ```
217 |
218 | * 현업에서는 연관관계 매핑시 LAZY로 다 세팅한 다음, 진짜 필요한 곳에서 최적화를 위해 EAGER로 교체한다
219 | * 단방향으로만 제대로 매핑해두면 양방향으로 뚫는 건 일도 아니다, 단지 코드 몇 줄만 추가하면 된다
220 | * 속단해서 최적화하지 않고 근거있는 데이터를 바탕으로 최적화하는 것이 중요하다
221 |
222 | ### 양방향 매핑
223 |
224 | * DB는 변하는 게 없다, 애초에 DB는 하나의 키(외래키)로만 양쪽 참조가 가능하기 때문이다
225 | * mappedBy는 반대편에 나와 관련있는 필드의 이름을 적어준다
226 | * 아무리 코드를 잘짜도 설계를 못하면 미래가 없다
227 |
228 | ### 연관관계의 주인과 mappedBy
229 |
230 | * 객체와 테이블간에 연관관계를 맺는 차이를 이해해야 한다
231 | * 객체 연관관계
232 | * 회원 -> 팀 연관관계 1개(단방향)
233 | * 팀 -> 회원 연관관계 1개(단방향)
234 | * 객체의 참조는 사실 양방향이 없다, 단방향 2개로 양방향처럼 쓰는 것이다
235 |
236 | * 테이블 연관관계
237 | * 테이블은 외래키 하나로 두 테이블의 연관관계를 관리할 수 있다
238 |
239 | * 양방향 연관관계 매핑 규칙
240 | * 연관관계의 주인만이 외래 키를 관리(등록, 수정)할 수 있으며 주인이 아닌 쪽은 읽기만 가능하다
241 | * 연관관계의 주인이란 외래 키 관리자를 말한다
242 | > * 아래 코드에 따르면 member객체는 team필드를 통해 team객체를 참조할 수 있고 team객체는 members필드를 통해 member를 참조할 수 있다.
243 | > * 아래와 같이 연관관계가 매핑된다면 외래 키는 어느 테이블에 생성될까? Member 테이블일까, Team테이블일까?
244 | > * Member 테이블에 생성된다. 만약 Team 테이블에 외래 키가 생성된다면 다음과 같은 문제가 발생한다
245 |
246 | ##### MEMBER TABLE
247 |
248 | ID|MEMBER_NAME
249 | --|-----------
250 | 1 |david
251 | 2 |joseph
252 | 3 |ruth
253 |
254 | ##### TEAM TABLE
255 |
256 | ID|TEAM_NAME|MEMBER_ID
257 | --|---------|---------
258 |
259 | > * 아무런 데이터가 입력되지 않은 상태에서 team 데이터를 하나 추가한다
260 | > * `INSERT INTO TEAM VALUES (1, 'Israel',1);`
261 |
262 | ID|TEAM_NAME|MEMBER_ID
263 | --|---------|---------
264 | 1 |Israel |1(david)
265 |
266 | > * 이 상태에서 TEAM 이름이 Israel인 것에 대해 MEMBER_ID를 추가할 방법은 없다.
267 | > * TEAM 테이블의 PK는 ID이고 이 상태에서 MEMBER_ID를 추가하려면 `INSERT INTO TEAM VALUES (1, 'Israel',2);`와 같은 쿼리가 필요한데, PK가 중복되므로 제약조건 위반이 발생하며 데이터를 삽입할 수 없다
268 | > * 이러한 문제 때문에 데이터베이스에서 테이블을 생성할 때 테이블 간의 관계가 다대일(N:1)일 경우, 다(N)쪽에 외래 키가 생성된다. 만약 일(1)쪽에 외래 키가 생성된다면 위에 언급한 이유로 동일한 TEAM에 대해 MEMBER_ID를 하나 밖에 넣지 못 한다
269 |
270 | ```java
271 | public Class Member {
272 | private Long id;
273 | private String name;
274 | @ManyToOne
275 | @JoinColumn(name = "TEAM_ID")
276 | private Team team; // Member Table에 외래 키 TEAM_ID가 생성된다
277 | // getter & setter
278 | }
279 |
280 | public class Team {
281 | private Long id;
282 | private String name;
283 | @OneToMany(mappedBy = "TEAM_ID")
284 | private List members; // mappedBy로 외래 키 관리 주체를 지정하지 않으면 team_id와 member_id 칼럼으로 구성된 TEAM_MEMBER 테이블이 생성된다
285 | // getter & setter
286 | }
287 | ```
288 |
289 | * 주인은 mappedBy 속성을 사용하지 않는다
290 | * 주인이 아닌 쪽에서 mappedBy 속성을 이용해 주인을 지정해야 한다
291 | * 그렇다면 누구를 주인으로 지정해야하나? 외래 키가 있는 곳을 주인으로 정해라
292 | * 왜? 인지부조화를 막기 위해서
293 | * 엔티티를 양방향으로 설정하면 객체의 참조는 둘인데 외래 키는 하나다 따라서 둘 사이에 차이가 발생한다
294 | * 여기서는 Member객체의 team필드가 연관관계의 주인
295 | * 단방향으로 다 설계하여 끝내버린다 그리고 이후에 양방향이 필요한 곳은 코드 몇 줄로 양방향을 뚫어준다
296 | * 외래 키가 아닌 곳이 주인이면 db에 넣을 때 jpa에서 team에서 insert가 나가고, member에서 update가 또 나간다
297 | * member가 주인이면 한방에 나간다
298 |
299 | * 양방향 매핑에서 ManyToMany대신, 일대다-다대일로 매핑한다
300 | * 상속관계 매핑 어노테이션, 상속관계처럼 테이블을 나눌지, 통으로 만들지
301 |
302 | ## JPA
303 |
304 | * 연관관계
305 | * 내부구조
306 |
307 | ## JPA 내부 구조
308 |
309 | * 영속성 컨텍스트
310 | * 엔티티를 영구 저장하는 환경 이라는 뜻
311 | * 엔티티 매니저와 영속성 컨텍스트
312 | * 영속성 컨텍스트는 논리적인 개념
313 | * 눈에 보이지 않는다
314 | * 엔티티 매니저를 통해서 영속성 컨텍스트에 접근하게 된다, 결국 엔티티 매니저는 영속성 컨텍스트다
315 | * 비영속(new/transient)
316 | * 멤버 객체를 생성하기만 하고 persist하기 전
317 | * 영속(Managed)
318 | * 생성된 객체를 persist한 상태
319 | * 준영속
320 | * 왜 영속상태가 필요한가?
321 | * 그냥 디비에 넣어버리면 안 되는가?
322 | * 왜 중간상태가 필요한가?
323 | * 영속성 컨텍스트의 이점
324 | * 1차 캐시
325 | * 동일성 보장
326 | * 왜 객체를 ==했을 때 동일하다고 나오는가?
327 | * em.find()로 멤버1을 찾을 때 디비를 뒤지는 게 아니라 **영속 컨텍스트 내의 1차 캐시를 뒤져서 있으면 바로 반환한다, 1차 캐시라는 것은 글로벌 캐시가 아니라 쓰레드 하나에 잠깐 존재하는 것이다, 트랜잭션을 시작할 때 생겨서 끝날 때 사라진다**
328 | * em.find()로 멤버2를 찾을 때 **1차 캐시에 없으면 디비를 뒤진다, 그리고 디비에서 찾아서 1차 캐시에 저장한 뒤 반환한다**
329 | * em.flush()는 쓰기 지연 SQL 저장소에 있는 쿼리를 실행한다
330 | * 트랜잭션 commit될 때 flush와 commit을 순서대로 진행된다
331 | * Dirty Checking(변경 감지)
332 | * 1차 캐시가 생성되는 시점에 스냅샷을 뜬다
333 | * flush 될 때, 1차 캐시에 저장된 것과 스냅샷 상태를 비교해서 변경된 게 있으면 update 쿼리를 만들어 날린다
334 | * 왜 update를 별도로 만들지 않고 스냅샷 등을 사용해서 update를 구현하는가? 사상(spirit)때문이다, 자바에서 컬렉션에 add했을 때 우리는 업데이트를 하지 않는다, 즉시 컬렉션에 반영된다 이와 같이 구현하기 위해서 스냅샷과 같은 것들이 필요한 것이다
335 | * 1차 캐시에서 관리되는 것을 영속 상태라고 한다
336 | * em.find로 찾아오면 영속 상태를 반환한다
337 | * 처음 저장할 때에는 persist가 필요하지만 이후로는 알아서 관리한다
338 | * **트랜잭션 커밋 시점에**
339 | * 플러시
340 | * 변경 감지
341 | * 수정된 엔티티 쓰지 지연 저장소 등록
342 | * 쓰기 지연 저장소의 쿼리를 데이터베이스에 전송(등록, 수정, 삭제 쿼리)
343 | * 한 방에 쿼리가 나가려면 몇가지 조건이 맞아야 한다
344 | * JPQL 실행시 자동으로 플러시를 호출한다, 즉 persist된 것들에 대해 조회 쿼리를 날리면 commit전에는 조회할 수 없다, 따라서 플러시를 통해 쌓여있는 영속성 컨텍스트를 저장한 뒤 JPQL을 실행한다
345 | * 플러시는 영속성 컨텍스트를 비우지 않는다
346 | * 영속 컨텍스트를 비우려면 clear()
347 |
348 | * 준영속 상태
349 | * detach 특정 엔티티만 준영속 상태로 전환
350 | * clear 영속성 컨텍스트를 완전히 초기화
351 | * close 영속성 컨텍스트를 종료
352 | * 스프링의 경우 컨트롤러로 나가는 순간 준영속으로 바뀐다, 그렇다면 레이지 로딩이 되지 않는다
353 | * em.find할 때 LAZY로 설정되어 있으면 프록시 객체로 반환된다, 그리고 실제로 사용되는 코드를 만날 때 실제 객체를 채운다
354 | * FetchJoin
355 | * 조회하는 시점에 한 방에 가져온다
356 | * 프록시와 즉시로딩 주의
357 | * 가급적 지연 로딩을 사용
358 | * 즉시 로딩을 적용하면 예상하지 못한 SQL이 발생한다
359 | * 즉시 로딩은 JPQL은 N+1 문제를 일으킨다
360 | * 지연 로딩을 사용하려면 프록시 객체가 필요하고, 영속 컨텍스트이어야 한다
361 | * 영속성 컨텍스트로 관리되는 애들만 LAZY로딩이 가능하다
362 | * 이 때, 영속 컨텍스트로 관리되지 않는데, Lazy로딩하면 LazyInitializationException이 발생한다
363 | * 필요하다면 영속 컨텍스트가 닫히기 전에 터치를 해두던가 해야 한다
364 |
365 | ## JPA와 객체지향 쿼리
366 |
367 | * JPA도 결국 쿼리를 써야 하는 상황이 있다
368 | * JPQL은 SQL이랑 거의 동일하다 다만 대상이 객체임
369 | * JPA는 엔티티 객체를 중심으로 개발하기 좋다
370 | * 문제는 검색 쿼리
371 | * 검색을 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색
372 | * JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어를 제공한다
373 | * JPQL은 엔티티 대상으로 검색
374 | * SQL은 테이블 대상으로 검색
375 | * JPQL 사용시 별칭은 필수
376 | * 세타조인
377 | * 페치조인
378 | * 엔티티 객체 그래프를 한 번에 조회하는 법
379 | * select m from Member m join fetch m.team을 하게 되면 멤버를 가져올 때, team을 가져오게 한다
380 | * 페치조인 꼭 해보기
381 | * **N+1**
382 | * 불러오는 쿼리(1) + 아래에서 루프돌 때 n번 돌게된다
383 | * 이를 해결하려면 **페치조인으로 한 방에 불러올 것들 다 불러오기**
384 | * @NamedQuery 를 쓰게 되면 컴파일 시점에 쿼리의 잘못된 것을 잡아준다, 원리는 문자열을 SQL로 파싱해서 오류를 찾는다, SpringDataJpa에서는 @Query
385 | * 조회해서 DTO로 바로 뽑아서 API로 쏘는 경우
386 | * Entity를 바로 API로 노출하지 마라, Entity스펙이 변경되면 API가 변경된다, DTO가 일종의 Interface가 되는 것, 외부에 API로 나가는 스펙은 DTO로 나가야 한다
387 |
388 | ## Spring Data JPA
389 |
390 | * 지루하게 반복되는 CRUD 문제를 세련된 방법으로 해결
391 | * 개발자는 인터페이스만 작성
392 | * 스프링 데이터 JPA가 구현체를 자동 생성
393 | * 따라서 JpaRepository를 상속받아서 인터페이스를 만들면, 관례에 따라서 객체의 속성을 조회하는 쿼리를 애플리케이션 로딩이 생성하며 확인한다
394 |
395 | ## QueryDSL
396 |
397 | * SQL, JPQL에 오류가 생길시 컴파일 타임에 잡을 방법이 없다
398 | * QueryDSL이 JPQL로 바꿔준다
399 | * 자바코드로 쿼리를 짤 수 있다
400 | * QueryDSL은 동적 쿼리
401 | * JPQL은 정적 쿼리
402 |
403 | ## 실무경험
404 |
405 | * SpringDataJpa와 QueryDSL을 꼭 쓴다
406 | * SpringBoot, SpringDataJpa, QueryDSL 기본으로 깔고 간다
407 | * Junit 통합 테스트시에 H2 DB 메모리 모드
408 | * 로컬 PC에는 H2(or MySQL) DB 서버 모드로 실행
409 | * 개발운영은 MySQL, Oracle
410 | * 테스트, 통합테스트 시에 CRUD는 믿고 간다, 내가 짜지 않고 프레임웤에서 자동화하기 때문에
411 |
412 | ## QnA
413 |
414 | * 왜 SpringBoot를 쓰는지?
415 | * 편하니깐
416 | * 그러나 디폴트 옵션이 너무 많이 깔려있어서 조심해야 한다
417 | * Spring Open Session In View 같은 이슈
418 |
419 | ## 후기
420 |
421 | * JPA를 접하고 나서 JPA만 사용하며 개인프로젝트를 진행하고 있는데 김영한 개발자님이 쓰신 책과 구글링에 의지하여 JPA를 사용하고 있었다. 책에서 쉽게 이해가지 않는 것을 6시간에 걸쳐 풀어주셨는데 연관관계, JPA의 내부 동작 원리, JPA를 더 잘 활용하기 위한 방법 등을 알 수 있었다. SpringBoot를 이용하여 JPA를 쓰고 있다면 JPA 내부 동작에 대해 잘 모를 수 있는데, SpringBoot 없이 JPA만 가지고 객체를 저장하며 조회해본 덕분에 트랜잭션과 JPA의 동작에 대해 조금 더 선명한 그림을 그릴 수 있었다.
422 | * 강의 중 강력한 팩트가 있었는데 JPA는 객체지향과 데이터베이스라는 두 기둥으로 세워진 것이라고 말씀하셨다. 즉, 둘 다 잘 알아야 한다는 것이다. 최근에 DB 공부를 소홀히 한 탓에 코딩테스트에서 애먹었지만 JPA 교재를 보면서 데이터베이스 관련 개념을 다시 정리하고 학습해야겠다.
423 | * 강의 들으면서 받아적었던 것을 되새기면서 샘플코드도 다시 작성해보고 제대로 이해하지 못하는 용어나 헷갈리는 것을 보충하다보니 2/3밖에 정리하지 못했지만 일단 업로드하고 지속적으로 수정하려 한다.
424 | * 강의도 강의였지만 강의 다 끝나고 지하철역까지 내려가는 길에 20여분 정도 김영한 개발자님과 이런저런 얘기할 수 있었던 시간 너무 유익하고 좋았다:)
425 |
--------------------------------------------------------------------------------
/2018OKKYCON/readme.md:
--------------------------------------------------------------------------------
1 | # 2018 OKKYCON - The Real TDD
2 |
3 | 연사진
4 |
5 | - [정진욱 - 테스트하기 쉬운 코드로 개발하기](#jung)
6 | - [박재성 - 의식적인 연습으로 TDD, 리팩토링 연습하기](#park)
7 | - [한성곤 - 코드 품질을 위한 테스트 주도 개발](#han)
8 | - [이혜승 - 테알못 신입은 어떻게 테스트를 시작했을까?](#leehye)
9 | - [양완수 - 테스트를 돌보기 위한 간단한 실천 방법, 효과](#yang)
10 | - [이규원 - 당신들의 TDD가 실패하는 이유 (Live Coding)](#lee)
11 |
12 |
13 |
14 | ## 정진욱 - 테스트하기 쉬운 코드
15 |
16 | ### 테스트하기 쉬운 코드란?
17 |
18 | - 같은 입력에 항상 같은 결과를 반환하는 코드
19 |
20 | - 외부상태를 변경하지 않는 코드
21 |
22 |
23 |
24 | 예제 - 컨퍼런스 등록 Web Api
25 |
26 | 1. ConferenceRegistration 유효성 검사
27 | 2. **이미 등록된 좌석 수 DB에서 읽어오기**
28 | 3. 요청한 좌석 수가 확보 가능한지 판단
29 | 4. **등록정보저장**
30 | 5. HTTP결과반환
31 |
32 | 위 5가지 중 테스트하기 어려운 것은?
33 |
34 | DB와 연동이 필요한 항목인 2, 3번이다
35 |
36 |
37 |
38 | **테스트하기 힘든 코드를 포함하고 있는 메서드는 테스트하기 힘들다**
39 |
40 | **테스트하기 쉬운 코드와 어려운 코드는 분리하자**
41 |
42 | 
43 |
44 |
45 | ### 테스트 하기 쉬운 코드로 개발하는 방법
46 |
47 | - 메소드를 분리한다
48 |
49 | - 두 부류(테스트 하기 쉬운 코드, 어려운 코드)코드는 어디서 만나야 하는가?
50 |
51 | 두 부류의 코드는 최대한 가장 자리에 위치시키자 (예외 : 로깅, 퍼사드)
52 |
53 | * 제일 안쪽에 있는 메서드가 테스트하기 어려우면 이를 호출하는 메서드들도 테스트하기 어려움즉, 바깥으로 테스트의 어려움이 전파됨
54 | * 따라서 테스트하기 어려운 메서드를 가장 바깥으로 빼서 테스트하기 쉬운 코드랑 만나게 하자, 그러면 테스트 하기 어려운 코드가 전파하는 악영향을 줄일 수 있다
55 |
56 | - 두 부류의 코드가 만나는 가장자리는 어떻게 테스트하는가?
57 |
58 | - 수동테스트
59 | - 웹의 경우 postman 같은 것으로 요청을 날린다
60 | - DB의 경우 query를 날린다
61 | - 다만 이렇게 수동으로 가능한 경우는 자동화가 필요없을 정도로 간단한 것들이 대부분이다
62 | - 자동테스트
63 | - 이미 작성된 프로덕션 코드의 사용을 강제하자
64 | - 실제 클래스 대신 목(Mock) 사용을 위해 이음새(Seam)가 있어야 함
65 | - 목 사용을 통해 작성된 코드 사용을 강제할 수 있음
66 | - 행위 검증 (목 사용)
67 | - 행위가 노출되었는가?
68 | - Mockist
69 | - 불필요한 추상화 유발 가능성
70 | - 아웃사이드 인 방식
71 | - 상태 검증
72 | - 결과 값이 무엇인가?
73 | - Classicist
74 | - 불필요한 추상화 필요없음
75 | - 인사이드 아웃
76 | - 목 사용의 문제점
77 | - 목을 남발할 가능성이 크다
78 | - 대부분 목 사용 예제는 간단하다
79 | - 실제 프로젝트에 적용하면 한꺼번에 많은 목을 다루면서 진행하기 힘들다
80 | - 적당 수의 목 사용에 대한 답을 찾기 어렵다
81 | - 상태 검증에 대한 문제 극복 방안
82 | - TDD를 통한 사전 테스트가 아니라 사후테스트를 하자
83 | - 두 부류의 코드가 맞물려 잘 돌아가는 로직이다
84 | - 난해한 코드가 아니다
85 |
86 | 두 부류의 코드를 분리해서 각각 테스트하고 가장자리에 맞물려 돌아가는 코드는 주로 수동테스트한다
87 |
88 | 두 부류의 코드를 섞어 놓고 테스트가 어렵다고 포기하지 말자
89 |
90 |
91 |
92 | TDD 원칙 중 하나
93 |
94 | 테스트를 만족하는 정도만 코드를 작성하라
95 |
96 | 그 이상을 작성하게 되면 테스트 커버 안에 들지 않는다
97 |
98 | 테스트1 < 코드1 + 코드2 + 코드3
99 |
100 | TDD는 점진적 향상이라는 것이 있는데 이 장점을 잃는다
101 |
102 |
103 |
104 | **테스트 하기 어렵다는 것은 테스트가 불가능 하다는 것이 아니다**
105 |
106 |
107 |
108 | ### 질문
109 |
110 | * private 메서드 테스트는 어떻게 해야하는가?
111 |
112 | 같은 기능을 테스트하더라도 3가지 메서드를 건드리기보다 1가지 메서드를 건드리는 것이 좋다
113 |
114 | 어떤 것을 public으로 할 것인지 고민해보면 좋을 것 같다
115 |
116 |
117 |
118 | * 레거시에 대해서는 목을 어떻게 써야 하는가?
119 |
120 | 인터페이스를 만들고 목에 대한 seam을 만듦
121 |
122 | 새로 생성된 코드는 최대한 레거시와의 의존도를 낮춤
123 |
124 | 캐릭터라이제이션이라는 테스트 기법(?)을 통해 기존 코드에 대해 요구사항만 나열해두고 테스트함
125 |
126 |
127 |
128 | 정진욱 연사님 Blog : http://jwchung.github.io/testing-oh-my
129 |
130 |
131 |
132 | ## 박재성(자바지기) - 의식적인 연습으로 TDD, 리팩토링 연습하기
133 |
134 | TDD에서 리팩토링과 설계 얘기가 안 나올 수 없다
135 |
136 | TDD < 리팩토링, 어쩌면 리팩토링이 더 중요할지도
137 |
138 |
139 |
140 | ### 의식적인 연습이란?
141 |
142 | TDD, 리팩토링을 잘 하려면... 연습, 연습, 연습 ...
143 |
144 | 무조건 연습을 많이 한다고 잘할 수 있을까?
145 |
146 | 1년차 프로그래머의 프로그래밍 역량 VS 10년차 프로그래머의 프로그래밍 역량
147 |
148 | 프로그래밍 역량만 놓고 보았을 때 항상 10년차가 낫다고 보장할 수 있나?
149 |
150 |
151 |
152 | TDD, 리팩토링 5~6년 도전 후에야 다음과 같은 것이 생겼다
153 |
154 | - **테스트하기 쉬운 코드와 테스트하기 어려운 코드를 보는 눈**
155 | - **테스트하기 어려운 코드를 테스트하기 쉬운 코드로 설계하는 감(sense)**
156 |
157 |
158 |
159 | 좀 더 효과적으로 연습할 수 있는 방법은 없을까?
160 |
161 | 책<1만 시간의 재발견> : "목적 의식 있는 연습"에 얼마나 많은 시간을 투자했느냐?
162 |
163 |
164 |
165 | 의식적인 연습의 7가지 원칙
166 |
167 | 1. 효과적인 훈련 기법이 수립되어 있는 기술 연마
168 | 2. 개인의 **컴포트 존을 벗어난 지점에서 진행, 자신의 현재 능력을 살짝 넘어가는 작업을 지속적으로 시도**
169 | 3. **명확하고 구체적인 목표**를 가지고 진행
170 |
171 | 4. 신중하고 계획적이다. 즉, 개인이 온전히 집중하고 '의식적'으로 행동할 것을 요구
172 | 5. **피드백과 피드백에 따른 행동 변경을 수반**
173 | 6. 효과적인 심적 표상을 만들어내는 한편으로 심적 표상에 의존
174 | 7. **기존에 습득한 기술의 특정 부분을 집중적으로 개선함으로써 발전시키고, 수정**하는 과정을 수반
175 |
176 |
177 |
178 | 스포츠 영역은 오래되었기 때문에 방법론적인 부분들이 잘 정립되어 있다.
179 |
180 | 코치가 선수보다 운동은 못할 수 있어도 **연습방법 설계, 피드백을 줄 수 있는 사람**으로써 선수와 다른 역할을 한다
181 |
182 |
183 |
184 | **의식적인 연습을 통해 효과적으로 연습하자**
185 |
186 |
187 |
188 | ### 의식적인 연습으로 TDD, 리팩토링 연습 과정
189 |
190 | #### 1단계 - 단위 테스트 연습
191 |
192 | TDD와 단위테스트는 다르다
193 |
194 | **TDD보다 단위 테스트를 먼저 연습해라**
195 |
196 | 내가 사용하는 API 사용법을 익히기 위한 학습 테스트에서 시작
197 |
198 | - 자바 String 클래스의 다양한 메소드 사용법
199 | - 자바 ArrayList 클래스에 대한 메소드 사용법
200 |
201 | API를 테스트하므로써 assertThat()에 대한 사용법을 익히는 것 (즉, xUnit에 익숙해지기)
202 |
203 | 만약, API 학습 테스트가 싫다면 Input과 Output이 명확한 Util성 메소드들로 단위테스트해보자
204 |
205 |
206 |
207 | #### 2단계 - TDD 연습
208 |
209 | **지켜야 할 원칙1**
210 |
211 | 회사 프로젝트에 연습하지 말고 장난감 프로젝트를 활용한다
212 |
213 |
214 |
215 | **지켜야 할 원칙2**
216 |
217 | 웹, 모바일 UI나 DB, 외부API 등에 의존관계를 가지지 않는 것으로 장난감 프로젝트를 활용한다.
218 |
219 |
220 |
221 | 인풋과 아웃풋에 대한 정의를 먼저해야 TDD를 하기 좋다
222 |
223 |
224 |
225 | **TDD 싸이클**
226 |
227 | 
228 |
229 |
230 |
231 | **처음에 TDD를 연습한다면 1단계, 2단계만 하고 3단계인 Refactoring은 안해도 좋다**
232 |
233 | **익숙하지 않은 데, 한 번에 하려면 힘들 수 있다**
234 |
235 |
236 |
237 | **TDD 연습**
238 |
239 | 어려운 문제를 해결하는 것이 목적이 아니라 **TDD를 연습하는 것이 목적**이다
240 |
241 | 난이도가 낮거나 본인에게 익숙한 문제로 연습하는 것이 좋다
242 |
243 |
244 |
245 | **리팩토링 연습**
246 |
247 | * **메소드 분리**
248 |
249 | 테스트 코드는 변경하지 말고 테스트 대상 코드(프로덕션 코드)를 개선하는 연습을 한다
250 |
251 | 처음에 메소드 분리하려면 머리 아프다
252 |
253 |
254 |
255 | * **정량적인 연습방법**을 통한 메소드 분리 연습
256 | * 한 메서드에 오직 한 단계의 들여쓰기(indent)만 한다.
257 |
258 | 인덴트가 2단계인 곳은 리팩토링 포인트로 생각하면 된다
259 |
260 | * else를 쓰지마라
261 |
262 | - 코드가 짧아진다
263 | - 읽을 때 좋다
264 | - else를 쓰면 로직을 파악하기 위해 메서드를 if-else를 통째로 읽어야 한다.
265 |
266 | * 로컬변수가 필요한가?
267 |
268 | * 바로 다른 메서드의 파라미터로 넘겨줄 수는 없나?
269 |
270 | * Compose method패턴
271 |
272 | * 메소드의 추상화 레벨을 맞춘다.
273 | * 추상화 레벨을 둬서 메소드를 분리했을 때, 필요하다면 로직 파악을 위해 메소드를 타고 들어가서 보면 된다.
274 |
275 |
276 |
277 | **연습은 과하게 한다**
278 |
279 | 하던대로 하면 잘 안 느껴진다
280 |
281 | 마치 달릴 때 모래주머니 차고 달리듯 과하게 해야 느낄 수 있다
282 |
283 | 이렇게 극단적인 연습은 전혀 다른 설계를 불러올 수 있다
284 |
285 | 일정 압박을 받으며 연습하면 잘 안 된다
286 |
287 | 그러므로 장난감 프로젝트로 진행하자
288 |
289 |
290 |
291 | * **클래스 분리**
292 |
293 | * 모든 원시값과 문자열을 포장한다
294 |
295 | 예) 숫자로 사용될 값을 사용자 입력 받는다, 이 때 숫자로 사용될 String value를 Positive라는 클래스로 만들어 0 이상을 보장하는 객체로 만들어 쓰는 것
296 |
297 | * 일급 콜렉션을 쓴다
298 | * 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다
299 |
300 |
301 |
302 | **장난감 프로젝트 난이도 높이기**
303 |
304 | TDD, 리팩토링 연습하기 좋은 프로그램 요구사항
305 |
306 | - 게임과 같이 요구사항이 명확한 프로그램으로 연습
307 | - 의존관계(UI, DB, 외부API 등)가 없는 것
308 |
309 | 단, 프로그램에서 UI는 콘솔로만
310 |
311 |
312 |
313 | **의존관계를 추가를 통한 난이도 높이기**
314 |
315 | 실제 현장과 같은 코드를 TDD하기
316 |
317 | 이 때 필요한 역량은
318 |
319 | - 테스트하기 쉬운 코드와 테스트하기 어려운 코드를 보는 눈
320 | - 테스트하기 어려운 코드를 테스트하기 쉬운 코드로 설계하는 감(sense)
321 |
322 |
323 |
324 | **한 단계 더 나아간 연습을 하고 싶다면**
325 |
326 | - 컴파일 에러 최소화하면서 리팩토링
327 | - ATDD 기반으로 통합테스트하기
328 | - 레거시 애플리케이션에 테스트 기반으로 리팩토링하기
329 |
330 |
331 |
332 | **구체적인 연습 목표 찾기**
333 |
334 | * **책<쏘트웍스 앤솔러지**>
335 |
336 | 객체지향 생활체조 규칙
337 |
338 | * 책<**클린코드**>
339 |
340 | 메소드 인수 개수, 클래스 크기
341 |
342 |
343 |
344 | 하드웨어 비용보다 사람 몸값이 더 부담이다
345 |
346 | 읽기 좋은 코드를 만드는 것이 경쟁력이다
347 |
348 |
349 |
350 | **TDD, 리팩토링 연습을 위해 필요한 것은?**
351 |
352 | - 조급함 대신 마음의 여유
353 | - 나만의 장난감 프로젝트
354 | - 같은 과제를 반복적으로 구현할 수 있는 인내력
355 |
356 |
357 |
358 | ### 질문
359 |
360 | **컴포트존을 극복하는 노하우는?**
361 |
362 | 삶의 여유와 에너지가 있어야 한다
363 |
364 | 컴포트존을 벗어나는 것은 스트레스 받는 것이다
365 |
366 | 즉, 연습할 수 있는 여유시간을 만들어야 한다
367 |
368 | 남들하는 거 다하면서 회사다니며 무엇을 바라는가, 1~2년만 각오를 하고 연습을 하자
369 |
370 |
371 |
372 | **테스트 코드를 빡빡하게 짤수록 요구사항이 변경되었을 시 테스트 코드를 바꿔야하는 게 많은데 이를 어떻게 극복할 수 있는가?**
373 |
374 | 대부분 설계가 잘못된 경우가 많다
375 |
376 | 시간이 부족한 경우 윗선에 말해 시간을 확보해야 한다
377 |
378 | 이에 대한 부분들을 우리는 전문가로써 책임을 다해야 한다
379 |
380 |
381 |
382 | ## Test Driven Development for Code Quality - 삼성SDS 한성곤
383 |
384 | 코드레벨 표준화, 측정, 지표
385 |
386 |
387 |
388 | 코드품질적인 측면에서 TDD가 어떤 장점을 가지고 있는지?
389 |
390 | * TDD는 테스트에 대한 공수가 든다, 단기적으로 측정하기 힘들고 장기적으로 측정해야 한다
391 |
392 |
393 |
394 | ### Overview of TDD
395 |
396 | TDD cycle
397 |
398 | What are the benefits of TDD
399 |
400 | - From Test (테스트관점)
401 | - 동작하는 코드에 대한 자신감
402 | - 회귀테스트를 통한 자유로운 리팩토링
403 | - 코드에 대한 지식이 증가 : 테스트 코드 작성하는 것에 대한 자체로 프로덕션 코드에 대한 지식을 높인다
404 | - **개발 생산성 향상**
405 | - 테스트를 통한 빠른 피드백
406 | - 디버깅을 줄일 수 있다
407 | - From Test-first (테스트 우선 관점)
408 | - 과도한 설계를 피하고, 간결한 인터페이스를 가짐
409 | - 불필요한 기능(Gold-plating)을 줄임
410 | - 실행 가능한 문서를 가짐
411 | - 코드 품질을 높일 수 있다
412 | - Sonar Cloud - Sonar Cube
413 | - 테스트 커버리지 기준으로 어떤 장점이 있는가?
414 | - 테스트를 통해 코드품질을 높일 수 있지만 다른 방법들도 있다
415 |
416 |
417 |
418 | ### Think more about TDD
419 |
420 | 코드품질이란?(소프트웨어 품질)
421 |
422 | Software Quality
423 |
424 | - External Quality
425 | - IBM은 TDD를 통해 실제 릴리즈를 진행했다, KLOC 40% 에러 defact 줄음
426 | - MS도 TDD와 Non TDD로 구분되게 실험했다
427 | -
428 | - Internal Quality
429 | - 인터널 품질을 잡으려면 개발 컨벤션을 잘 지킬 수 있어야 한다
430 | - Code Quality Metrics
431 | - 복잡도, 결합도, 응집도
432 |
433 | the seen versus off the unseen
434 |
435 | ### Success with TDD
436 |
437 | 저의 부족함으로 각종 수식과 처음들어본 용어들이 난무하여 제대로 받아적을 수 없었습니다..
438 |
439 |
440 |
441 | ## 테알못 신입은 어떻게 테스트를 시작했을까? - 이혜승
442 |
443 | 10개월차 주니어
444 |
445 | 테스트로 개발한지 5개월차
446 |
447 | TDD로 개발한 기간은 더 짧다
448 |
449 |
450 |
451 | 초보로써 TDD, 테스트를 하면서 배운 것과 실패한 것 그리고 경험담
452 |
453 |
454 |
455 | ### 방법에 대한 이야기
456 |
457 | TDD (Test First)
458 |
459 | 테스트를 먼저 작성하고 프로덕션 코드를 만드는 것
460 |
461 | Test Last
462 |
463 | 이미 구현된 코드에 테스트만 붙여두는 것
464 |
465 |
466 |
467 | TDD의 red는 실패,
468 |
469 | Test Last의 red는 버그
470 |
471 |
472 |
473 | 단위 테스트 코드 작성
474 |
475 | * 프로덕션 코드는 전혀 수정하지 않는다.
476 |
477 | * 테스트를 만들 때 의도한 인풋과 아웃풋이 나오는지 확인하면 된다.
478 |
479 |
480 |
481 | 리팩토링
482 |
483 | * 테스트 코드는 전혀 수정하지 않는다.
484 |
485 |
486 |
487 | **테스트 가능한 부분을 분리하는 기준**
488 |
489 | - 중요도가 높은 비즈니스 로직이 포함된 부분
490 |
491 | - 버그가 발견된 부분 (과거X)
492 |
493 | - 결합이 낮고 논리는 복잡한 부분
494 |
495 |
496 |
497 | 외부 의존도가 낮은 독립적인 함수를 만들기 위해 외부 의존을 가지는 애들을 함수의 파라미터로 빼준다.
498 |
499 |
500 |
501 | ### 경험에 대한 이야기
502 |
503 | 좋은 점
504 |
505 | - 불안함이 감소한다
506 |
507 | - 리팩토링을 안정적으로 할 수 있다
508 |
509 | - 추가적인 요구사항을 받아도 스트레스 받지 않는다
510 |
511 | - 스펙 문서 기능을 한다. 함수를 사용할 때 테스트를 통해 프로덕션 코드를 이해할 수 있다.
512 |
513 | - 디자인 개선 효과(설계 얘기)
514 |
515 | - 자바스크립트의 경우 반환 값이 2개이상일 수 있다. 반환 값이 1개만 되도록 테스트를 통해 바로 잡을 수 있다.
516 |
517 | - 학습 동기부여
518 |
519 | - 설계에 대한 고민을 자연스럽게 하게 된다
520 |
521 | - 개발 생산성 향상
522 |
523 | - 디버깅 하는 시간 줄어들음
524 | - 테스트 안 해서 아낀 시간 <= 테스트 안 해서 발생한 버그를 고친 시간
525 | - 비즈니스 로직의 허점을 사전에 발견할 수 있다.
526 | - 테스트를 만들면서 비즈니스 로직, 요구사항 등에 대해 깊은 생각을 하게 된다. 이 과정에서 부족한 로직을 보충할 수 있다
527 |
528 | - 집중력 향상 효과
529 |
530 | - 한 번에 하나에 집중할 수 있게 한다.
531 | - 개인적으로는 자바 웹 프로그래밍 하다보면 한 번에 하나씩 집중할 수 없었다
532 |
533 |
534 |
535 | ### 실수
536 |
537 | - 테스트 자체가 목적이 되어버렸거나 (커버리지를 높이기 위한)
538 |
539 | - 불필요한 테스트를 만드는 것
540 |
541 | - (불필요한 테스트를 만들지 않기 위해) 기준을 만들자
542 | - 비즈니스 로직과 관련된 버그를 낼 가능성이 낮거나 없는 것은
543 | - 테스트를 유지함으로써 얻는 이익 < 테스트 유지와 관리에 드는 비용일 때
544 | - UI의 경우 굉장히 다양하게 변하는데 이에 대한 테스트를 만들다보면 조금의 변화에도 테스트가 깨진다.
545 | - 테스트가 단언하고 있는 내용이 사용자에게 중요한 가치를 주는 것이 아닐 때
546 |
547 | 위와 같은 이유일 때는 테스트를 만들지 않는다.
548 |
549 | - 필요하지만 검증 방식이 잘못된 테스트
550 |
551 | - 데이터에도 문제가 없고 비즈니스 로직에도 문제가 없는데 UI가 변경됨에 따라 테스트가 깨지면 스트레스 받는다
552 |
553 | - 검증력이 떨어지는 테스트
554 |
555 | - 테스트 제목과 검증의 불일치
556 |
557 | - 테스트를 앞서가는 프로덕션 코드
558 |
559 | - 테스트 통과만을 위해 비즈니스로직을 작성하는 것은 TDD 원칙을 위반하는 것
560 |
561 |
562 |
563 | ### 고민
564 |
565 | - 테스트 픽스처의 생성? 관리?
566 |
567 | - 테스트와 리팩토링을 통해 함수를 분리하는데 분리되는 함수가 많다보니 내 마음이 불안해지더라
568 |
569 | 이에 대한 본인 회사 CTO의 답변
570 |
571 | **추상화 수준이 낮다**
572 |
573 | 아주 구체적인 작은 단위의 함수들로만 분리되어 있고 그들이 조합되어 만들어지는 큰 수준의 기능은 설계되지 않았다. 그래서 추상화 수준이 낮아졌다.
574 |
575 | **높은 응집, 낮은 결합(함수 분리)**
576 |
577 | 단일 기능을 가진 함수로 분리함으로써 낮은 결합은 달성했다
578 |
579 |
580 |
581 | ## 테스트를 돌보기 위한 간단한 실천 방법, 효과 - 양완수
582 |
583 | 자주 들은 질문들과 그에 대한 답변
584 |
585 | * TDD를 실무에서 많이 하느냐?
586 |
587 | 많이 안 한다
588 |
589 | * Unit Test를 작성을 하고 있느냐?
590 |
591 | 하는 부분이 있고 하지 않는 부분이 있다
592 |
593 | * Unit Test를 통해 얻고 있는 장점을 충분하게 느끼고 있느냐?
594 |
595 | 충분히 느낀다
596 |
597 |
598 |
599 | TDD에 대해서 이야기하지 않습니다
600 |
601 | 테스트하기 좋은 설계에 대해서 이야기하지 않습니다
602 |
603 | 제어할 수 없는 것에 대해서 이야기하지 않습니다 (시간, DB, Networking, Mocking etc)
604 |
605 |
606 |
607 | 테스트 코드는 프로젝트 코드가 사용되는 최초의 장소이며 고객이다
608 |
609 | 모든 역사는 테스트 코드부터 시작된다
610 |
611 |
612 |
613 | 그럼에도 불구하고 당신이 테스트를 거들떠보지 않는 이유는?
614 |
615 | 귀찮다, 릴리즈 날짜, 재미가 없다
616 |
617 |
618 |
619 | 언제 테스트 코드가 만들어질까?
620 |
621 | 팀 내 git log 분석해보니깐 프로젝트 마무리 시점이나 윗선에서 커버리지 낮다고 했을 때 테스트가 만들어지더라
622 |
623 |
624 |
625 | ### 테스트가 나를 고통스럽게 하는 것
626 |
627 | - 숨겨진 본질
628 |
629 | - 낮은 추상화
630 | - 들쭉날쭉한 추상화
631 | - 끊어진 논리
632 | - 알 수 없는 의도
633 |
634 | - 욕심쟁이
635 |
636 | - **테스트가 실패하는 이유는 단 하나 (단정문은 1개여야 한다)**
637 | - **하나의 테스트는 오직 한 가지만 똑바로 검사해야 한다**
638 |
639 | - 인지능력의 과부화
640 |
641 | - 흩어진 코드와 데이터
642 | - 매직넘버
643 |
644 | - 깨지기 쉬운 것들
645 |
646 | - 높은 결합
647 | - 낮은 응집
648 |
649 | 모든 객체는 의존을 가지는 데 의존이 높으면 다른 것들에 의해 영향을 많이 받는다
650 |
651 |
652 |
653 | **추상이란?**
654 |
655 | **문맥(Context) 위에서 오직 관심 있는 것들에 대해서만 집중하여 명확하게 하는 것**
656 |
657 | 1+12 = 13, 맞을 수도 있고 틀릴수도 있다, 시계의 경우 1+12는 13 => 즉 1이다
658 |
659 | 추상은 굉장히 context가 중요한 것이다
660 |
661 |
662 |
663 | **의도를 드러낼 수 있는 가장 좋은 방법이 추상화다**
664 |
665 | 낮은 추상화에 의미를 부여해야 한다
666 |
667 |
668 |
669 | ### 테스트
670 |
671 | 어떤 데이터 픽스처를 가지고 있는가?
672 |
673 | 테스트 메서드 내에서 부차적인 정보(픽스처)를 없애고 한 메서드 안에서 추상화레벨을 맞추려고 했다. 즉, 픽스처는 setUp()으로 뺐다
674 |
675 |
676 |
677 | **테스트를 이해한다는 것은 테스트를 하는 환경을 이해한다는 것**
678 |
679 | * setUp메서드 안에 테스트할 대상을 옮겨둔다
680 |
681 | ```java
682 | // 기존
683 | Option option = new Option();
684 | option.setDiscoutPercent("xx");
685 | option.setSalePrice("xx");
686 | ...
687 |
688 | // 변경
689 | Options options = new Options(new Option("aa", "bb"), new Option("cc", "dd") ...);
690 | ```
691 |
692 |
693 |
694 | * product, option을 다음 추상에서는 productEnv개체를 만들어 사용하게 한다, 이렇게 추상화하는 과정에서 여러 헬퍼메서드들이 생긴다
695 |
696 | * 헬퍼메서드와 테스트 메서드와는 연관이 없다. 헬퍼메서드들을 productEnv로 옮겨서 하나의 클래스내에서 다 처리할 수 있게 한다
697 |
698 | * 테스트 대상을 dut(device under test)로 명명한다.
699 |
700 | * 잘못 만들어진 테스트
701 |
702 | 테스트가 깨졌는 데 깨진 이유를 찾으려 테스트 메서드를 탐험해야 할 때, 테스트 메서드가 여러 개를 한 번에 검증하려고 하면 탐험해야 하는 상황이 생긴다
703 |
704 | * 테스트에서 추구하는 대칭성
705 |
706 | * 테스트 메서드 이름을 만들 때 그냥 테스트1, 테스트2 이렇게 만들고 내부 로직은 딱 1가지 일만하게 한다. 그러면 자연스럽게 테스트 메서드의 이름이 드러난다
707 |
708 |
709 |
710 | **거의 대부분 테스트를 잘 짜려고 했지 설계에 온전히 집중하지 않았다**
711 |
712 | * 테스트를 가지고 현재 비즈니스를 이해하고 새로운 피처가 추가될 때 어디에 추가되어야 할지 명확해진다
713 |
714 | * T**DD보다 더 중요시 되어야 하는 것은 테스트이다, 테스트 코드를 잘 짜야 한다**
715 |
716 | * 테스트를 위해 수년간 수련했다
717 |
718 |
719 |
720 | **도대체 테스트가 나에게 주는 가치는 무엇인가?**
721 |
722 | **배움**이다
723 |
724 | 테스트 짜다가 소리 지를 때가 있다
725 |
726 | "이거지", "이렇게 되니깐 응집력이 높아지지" 등
727 |
728 | 테스트가 우리를 가르친다, 테스트를 통해 나의 실력이 올라간다
729 |
730 |
731 |
732 | 양완수 Helly - 쿠팡, 셀러 리스팅팀, 이커머스
733 |
734 | Slipp에서 활동했었다
735 |
736 | (자기소개 제일 마지막에 한 게 인상적이었는데, 이유는 더 인상적이었다. 발표가 망하면 이름도 말 안하고 그냥 내려가려고 하셨다고.)
737 |
738 |
739 |
740 | ### 질문
741 |
742 | **지저분한데 픽스처를 어떻게 넣을 것인가?**
743 |
744 | 리팩토링하자니 시간이 없고, 픽스처를 넣자니 어렵다, 격리하자
745 |
746 |
747 |
748 | ## 당신의 TDD가 항상 실패하는 이유 - 이규원
749 |
750 | 너는 항상 성공하냐?
751 |
752 | 아뇨, 저도 실패해요. 하지만 항상 실패하지 않아요.
753 |
754 |
755 |
756 | 우리 프로젝트는
757 |
758 | - 스타트업이 아니다
759 | - 비즈니스 서비스를 만든다
760 | - 글로벌 시장
761 | - 많은 경쟁사들이 있다
762 | - Message-Driven
763 | - Multitenancy
764 | - Scalable
765 | - Responsive
766 |
767 |
768 |
769 | ### TDD가 실패하는 이유
770 |
771 | 왜 실패하는 것 같냐?
772 |
773 | 너희는 준비가 안 됬다!!
774 |
775 |
776 |
777 | 충분히 준비되지 않은 상태 -> 성급한 시작 -> 성급한 결과 -> 실패
778 |
779 |
780 |
781 | #### 개인이 TDD에서 실패하는 이유
782 |
783 | **당신은 이렇게 하지 않았다**
784 |
785 | 프로그래머가 하는 일은 사이언스보다 엔지니어링에 가깝다
786 |
787 | 프로그래머는 과학적 이론, 방법들을 통해 정해진 자원 안에서 문제를 해결하는 것
788 |
789 |
790 |
791 | **다른 세미나에서 들었던 질문** 그에 대한 답변
792 |
793 | 왜 TDD를 하는 사람은 모든 코드를 TDD로만 작성하려 하는가?
794 |
795 | 왜 TDD 프로세스를 따르지 않으면 비난하는가?
796 |
797 | 답변 : (TDD 잘 하시는 분들) **우린 안 그러는데?**
798 |
799 |
800 |
801 | TDD를 많이 하지만 항상 TDD를 하지 않는다
802 |
803 | 과학자가 아니라 우리는 엔지니어다
804 |
805 | TDD가 도움이 된다면 TDD를 적극 사용한다
806 |
807 | 그러나 그렇지 않다면 TDD를 하지 않는다
808 |
809 | (포비가 항상 말씀하시는 유연한 사고, 이 바탕에서 자기만의 논리, 기준이 있어야 한다)
810 |
811 |
812 |
813 | 만약 TDD를 사용할만한 준비가 되지 않은 팀이라면 성급히 도전하지말고
814 |
815 | 현재 잘 사용하고 있는 방법으로 프로덕트를 만들어가자
816 |
817 |
818 |
819 | 그렇다면 우리는 어디에 TDD를 적용해야 할까?
820 |
821 | 우리가 보호해야할 것은?
822 |
823 | - AWS (X)
824 | - Spring (X)
825 |
826 | 얘네들은 우리가 하지 않아도 알아서 잘 굴러간다
827 |
828 |
829 |
830 | - 도메인 (O)
831 |
832 | 우리는 도메인에 가장 많은 집중을 해야 한다
833 |
834 |
835 |
836 | 우리가 제어할 수 없는 것들
837 |
838 | 외부 세상
839 |
840 | - 실 세계
841 | - 인프라
842 | - 외부 서비스
843 | - 레거시(과거에 작성된 코드)
844 |
845 | 제어할 수 없는 것에 에너지를 쏟는 것은 낭비다
846 |
847 |
848 |
849 | 우리가 보호해야 할 것을 외부세계와 잘 분리해야 한다
850 |
851 |
852 |
853 | ### **설계**
854 |
855 | - 낮은 결합
856 | - 높은 응집
857 |
858 | 을 통해 도메인 모델을 보호할 수 있는 설계를 해야 한다
859 |
860 |
861 |
862 | 구현 테스트
863 |
864 | 코드가 이루려는 가치를 테스트하지 않고 각 구현에 맞는 테스트를 만드니깐 리팩토링될 때마다 테스트가 깨지고 이에 따라 수정, 재작성에 대해서 비용이 증가한다
865 |
866 | 
867 |
868 |
869 |
870 | **설계를 테스트하라**
871 |
872 | 
873 |
874 |
875 |
876 | 정보 숨김(Information Hiding) - David Parnas
877 |
878 | 어려운 설계 결정과 변경될 가능성이 높은 설계 결정들을 다른 모듈로부터 숨기는 것
879 |
880 |
881 |
882 | 즉, 더러운 디자인(잘못된 설계)을 정보 숨김으로써 다른 모듈에게 보여지는 것을 숨겨라
883 |
884 |
885 |
886 | **인터페이스에 테스트 접근**
887 |
888 | 인터페이스의 구현에 테스트를 맞추면 매번 수정해야 할 것이다
889 |
890 | **테스트를 할 때 구현(implementation)이 아니라 설계(interface)에 맞춰야 한다**
891 |
892 |
893 |
894 | **레거시와 함께 살기**
895 |
896 | 이xxxxx에서 근무할 때 혼자 TDD했다 그리고 고민했다.
897 |
898 | 레거시가 있지만 어떻게 새롭고 깔끔한 코드를 넣을 수 있을까?
899 |
900 | 내 코드를 Adapter Layer로 감싸서 레거시와 연결한다
901 |
902 | 
903 |
904 |
905 |
906 | ### 당신의 팀이 TDD에 실패하는 이유
907 |
908 | **프로세스**
909 |
910 | - 점진
911 | - 반복
912 | - fail-fast
913 |
914 | 우리는 항상 성공할 수 없다
915 |
916 | 빨리 실패해야 빨리 고칠 수 있다
917 |
918 | 빨리 실패해야 수정할만한 시간을 얻을 수 있다
919 |
920 |
921 |
922 | **반복주기**
923 |
924 | - 계획
925 | - 실행
926 | - **평가**
927 | - 필요하다면 어떠한 도구를 통해서든 피드백을 받아야 한다
928 |
929 | **우리는 대부분 계획과 평가가 없이 실행(프로덕션 코드에 덤비는 것)만 한다.**
930 |
931 | 무슨 코드를 만들지 계획을 세우지 않고 코딩만 한다.
932 |
933 | 코딩이 끝나면 평가없이 commit해버린다.
934 |
935 |
936 |
937 | **문화**
938 |
939 | - 공유
940 | - 목표
941 | - 지식
942 |
943 | 큰 목표에 함께 눈 높이를 맞추고 있어야 팀내 작은 부분을 맞춰나갈 수 있다
944 |
945 |
946 |
947 | **아키텍처**
948 |
949 | - 낮은 결합
950 | - 높은 응집
951 | - 도메인 모델 보호
952 |
953 |
954 |
955 | **도메인 모델과 플랫폼**
956 |
957 | 
958 |
959 |
960 |
961 | 왜 자바 개발자들은 기승전"스프링"인가?
962 |
963 | 스프링과 도메인 모델이 강하게 결합되어서 그렇다
964 |
965 | 이는 도메인 모델이 보호받이 못하는 것이다
966 |
967 |
968 |
969 | 
970 |
971 | 도메인 모델은 플랫폼 독립적으로 작성되어야 한다
972 |
973 | 플랫폼은 도메인 모델을 호스팅하는 역할만 해야지 도메인 모델을 깊숙히 파고 들어서는 안 된다
974 |
975 |
976 |
977 | **아키텍처 사례**
978 |
979 | 
980 |
981 |
982 |
983 | 스프링을 쓰지 말자는 얘기가 아니다
984 |
985 | 스프링을 잘 활용하면서 우리 도메인 모델 코드를 오염시키지 않을 수 있다
986 |
987 |
988 |
989 | 각 레이어의 크기가 중요도
990 |
991 |
992 |
993 | ### 개발하기 전에 분명히 해야하는 것
994 |
995 | **목적**
996 |
997 | - 소프트웨어 사용자에게 어떤 가치를 전달할 것인가?
998 |
999 | 우리가 고통받지 않으려면 아주 명확해야 한다
1000 |
1001 | 이렇게 명확하게 정의된 기능이 사용자에게 좋은 가치를 전달할 것이라는 근거가 있어야 한다
1002 |
1003 |
1004 |
1005 | 심플한 그림이라도 그림을 그리고 얘기할 때랑 아닐때랑 서로의 생각이 너무 큰 차이가 난다
1006 |
1007 | 그리고 이를 바탕으로 개발을 한다
1008 |
1009 |
1010 |
1011 | **분석**
1012 |
1013 | - (슬라이드를 훅..넘겨버리셨..)
1014 |
1015 |
1016 |
1017 | **작업설계**
1018 |
1019 | - 소프트웨어 변경은 어떤 세부 작업들이 있는가?
1020 | - 각 작업들은 어떤 순서로 진행되어야 하는가?
1021 | - 각 작업들은 누가 담당하는가?
1022 |
1023 |
1024 |
1025 | **ATDD + TDD**
1026 |
1027 | 두개를 결합하여 개발을 진행
1028 |
1029 |
1030 |
1031 | **코드 설계**
1032 |
1033 | - 작업에는 어떤 코드 변경(commit)이 필요한가?
1034 |
1035 | - 항상 그러는건 아니지만, 코드 설계를 할 때 테스트케이스 이름 목록을 뽑기도 한다
1036 |
1037 |
1038 |
1039 | **코드**
1040 |
1041 | **리팩터**
1042 |
1043 | - 의도노출
1044 | - 중복제거
1045 |
1046 | **피드백**
1047 |
1048 | - 단위 테스팅
1049 | - 코드 리뷰
1050 | - 기능 테스팅
1051 | - 수동 테스팅
1052 | - 사용자 반응 수집
1053 |
1054 |
1055 |
1056 | ### 질문
1057 |
1058 | **노멀한 코찔찔이들이 어떻게 스페셜 코찔찔이들이 되었는가?**
1059 |
1060 | **페어링**
1061 |
1062 | 코딩뿐만 아니라 태스크를 나누는 작업도 페어링을 했다
1063 |
1064 | 처음엔 CTO 주도로 나누고 다음 번엔 코찔찔이들에게 주도를 넘겨줬다
1065 |
1066 | 이 모든 것이 TDD를 준비하는 과정이다
1067 |
1068 |
1069 |
1070 | UserStory는 비즈니스 팀이 러프하게 개발팀으로 넘겨준다
1071 |
1072 | 그걸 바탕으로 상세히 그림을 그린다, 논리적 오류는 없는지 체크한다
1073 |
1074 | 이 때 만약 문제가 발생하면 비즈니스 팀과 소통해 명확하게 한다
1075 |
1076 | 초기분석을 끝낸 것을 목록화 한다.
1077 |
1078 |
1079 |
1080 | 내 일을 끝냈다면 내게 할당되지 않은 유저스토리를 선택해서 진행한다
1081 |
1082 |
1083 |
1084 | 항상 그렇지는 않지만 초기에는 페어링을 통해 태스크를 쪼개는 것을 연습했다
1085 |
1086 |
1087 |
1088 | **통합 테스트가 필요한 경우 어떻게 하는가?**
1089 |
1090 | 단위테스트로는 불안감이 완벽히 해소되지 않는다
1091 |
1092 | 결국 단위테스트가 유기적으로 결합되어서 동작해야 요구사항을 만족하는 것이다
1093 |
1094 | 그에 대한 해결책으로 통합테스트와 수동테스트를 하는 것이다
1095 |
1096 | 단위 테스트는 잘 해놓고 기능테스트는 깨지는 경우
1097 |
1098 |
1099 |
1100 | 단위 테스트로 부족하기에 수동, 기능 테스트를 한다
1101 |
1102 |
1103 |
1104 | **외부와 연동되는 경우에는 어떻게 하는가?**
1105 |
1106 | 현재 서비스는 양쪽으로 서비스 통합이 필요하므로 페이크 서비스를 만들어서 우리의 서비스가 그것들와 통합이 될 수 있는지 확인한다
1107 |
1108 |
1109 |
1110 | ## 패널토크 Q&A
1111 |
1112 | **수많은 방법론 중에 왜 TDD인가요?**
1113 |
1114 | **왜 테스트를 하고 테스트에서 어떤 가치를 느끼는가?**
1115 |
1116 | 테스트를 하게 되면 프로덕션 코드에 대해 애정을 가지게 된다
1117 |
1118 | 테스트 덕분에 프로덕션 코드에 대한 신중함이 생긴다
1119 |
1120 | **가장 빠른 피드백**이 다른 방법론보다 나은 것 같다
1121 |
1122 |
1123 |
1124 | **어떻게 TDD를 다른 개발자들에게 전파&설득할 수 있는가?**
1125 |
1126 | 양완수
1127 |
1128 | 사실 TDD를 한다는 것은 상당히 외롭다
1129 |
1130 | 품질을 보증하는 방법인데 왜 우리(비즈니스쪽, 관리쪽)가 알아야 해?라는 뉘앙스가 다수다
1131 |
1132 | 테스트 또는 TDD가 주는 이점은 설계적인 관점이다
1133 |
1134 | 프로덕션에 많은 영향을 주는 게 테스트이다
1135 |
1136 | 테스트는 의사가 수술 전 손을 씻듯 개발자에게 아주 당연하게 일어나야 하는 부분이다
1137 |
1138 |
1139 |
1140 | 박재성
1141 |
1142 | 본인이 주니어 개발자라 영향력이 적다면 굳이 팀내에 억지로 전파하려 애쓰지말자
1143 |
1144 | 우선 나를 위한 활동으로 TDD를 하자, 남들이 하든 말든 그것은 그 다음 얘기이다
1145 |
1146 | 내가 꾸준히 만들어둔 클린코드를 통해 다른 사람이 그 클린코드를 통한 이점을 조금이라도 맛보아 나에게 물어온다면 그 때 전파해보자
1147 |
1148 |
1149 |
1150 | 애자일 컨설팅 김창준
1151 |
1152 | 개인적인 관점에서 TDD는 삶의 만족도를 높여준다
1153 |
1154 | 내가 하는 일에 대해 매분 단위로 피드백을 받는 것이 **직무탈진**에서 조금은 자유롭다
1155 |
1156 | 이 때 TDD는 피드백의 수단이 된다
1157 |
1158 | TDD가 어디서 생겼는가?
1159 |
1160 | 애자일이란 환경이 갖춰졌을 때 TDD가 의미가 있을 수 있다
1161 |
1162 |
1163 |
1164 | 한 에피소드
1165 |
1166 | 유명한 개발 회사 서버 개발자
1167 |
1168 | TDD 몇 년간 연습해서 나의 코드에 반영하기 시작
1169 |
1170 | 3년 후에는 컨퍼런스까지
1171 |
1172 | 그리고 나의 코드 결함도와 팀내 다른 구성원의 코드 결함도를 차트로 보여줬다
1173 |
1174 | > ??? : 그래서 팀원들이 TDD를 하나요?
1175 | >
1176 | > 아니요..
1177 |
1178 | TDD가 정말 효과를 보려면 나혼자보다 같이할 때 더 좋다
1179 |
1180 | 애자일은 모를 때 시작하는 것이다
1181 |
1182 | 개인을 위해서 하는 것도 맞지만 팀내에서 함께 시작하는 고민을 하는 것도 필요하다
1183 |
1184 | 오히려 내가 전문가가 아닐 수록 상대가 디펜시브하게 나오지 않고 협력할 가능성이 높다
1185 |
1186 |
1187 |
1188 | 박재성
1189 |
1190 | 리더입장에서 TDD를 하자고 하면 그래도 실패할 가능성이 높다
1191 |
1192 | 팀의 문제점을 팀원들이 인식하게 해서 TDD든 페어프로그래밍이든 어떤 활동을 하자고 하는 말을 리더가 하는 게 아니라 팀원들이 하자고 건의할 수 있는 환경을 리더가 조성해야 한다
1193 |
1194 | 팀원들이 말하게 하는 것이 중요
1195 |
1196 | 가장 효과적일 수 있는 활동들을 **하나씩** 적용
1197 |
1198 | 그리고 이렇게 활동의 적용을 통해 할 수 있다는 경험을 해보는 것이 중요하다
1199 |
1200 | 주니어일 때 작은 변화를 위한 시도를 계속해서 근육을 만들고
1201 |
1202 | 이러한 근육을 바탕으로 더 큰 변화를 만들어 갈 수 있다고 생각한다.
1203 |
1204 | 외롭고 힘든 싸움이니 너무 집착은 하지말고
1205 |
1206 |
1207 |
1208 | 김창준
1209 |
1210 | TDD를 도입할 때도 작게 쪼개서 하나씩 도입해보자
1211 |
1212 | TDD에서는 테스트 순서도 중요하다
1213 |
1214 | 어떤 순서냐에 따라서 종착지가 바뀔 수 있다
1215 |
1216 | 첫 시작을
1217 |
1218 | 작지만 트리비얼(사소하게)하게 선택했을 때 코너로 몰릴 수 있다
1219 |
1220 | 작지만 에센셜한 것(중요한 것)을 선택해야 한다
1221 |
1222 |
1223 |
1224 | **코드커버리지를 어떤 지표로써 사용하면 좋은가?**
1225 |
1226 | 한상곤
1227 |
1228 | 커버리지에 집착하지 말자
1229 |
1230 | 프로덕션 코드를 이해하는 마음으로 시작하자
1231 |
1232 | 명확하게 드러나고 경험한 도메인에서는 프로덕션 코드에 집중하는 게 필요하고
1233 |
1234 | 잘 모르고 모호한 것에 대해서는 TDD를 통해 일반화하는 것이 필요하다
1235 |
1236 | 매트릭스에 집중하기보다 프로덕션 코드에 집중을 하자
1237 |
1238 |
1239 |
1240 | 테스트에 관한 책 추천
1241 |
1242 | 정진욱
1243 |
1244 | - art of unit testing
1245 |
1246 | - xUnitPatterns
1247 | - 테스트에서 일어나는 모든 상황을 패턴으로 만들어 놓았다
1248 | - data Arrange
1249 |
1250 | 책을 통해 나의 지식을 검증하는 것 그리고 내가 하는 방법보다 더 나은 부분을 부분을 볼 수 있다
1251 |
1252 |
1253 |
1254 | 박재성
1255 |
1256 | - 테스트주도개발
1257 | - Refactoring
1258 |
1259 | 책을 봐도 한계가 있다, 결국 본인이 조금씩 해야 한다
1260 |
1261 |
1262 |
1263 | 한상곤
1264 |
1265 | Youtube로 리팩토링, TDD에 대한 KATA
1266 |
1267 |
1268 |
1269 | 양완수
1270 |
1271 | TDD, 테스트를 책을 보고 얻은 역량은 매우 적더라
1272 |
1273 | 내가 의지적으로 테스트, TDD를 통해 성장하고자 하는 것이 커야 한다
1274 |
1275 |
1276 |
1277 | **TDD, 테스트는 내 표현으로 종합예술이다**
1278 |
1279 | (아.. 여기서 큰 깨달음! 항상 자기언어로 표현하라는 말을 듣고 고민했는데 이 한 문장으로 해소되었다)
1280 |
1281 |
1282 |
1283 | **도메인 모델과 스프링을 접점을 줄여야 하는 이유는?**
1284 |
1285 | 도메인 모델을 다른 플랫폼을 옮겨야 할 상황이라면?
1286 |
1287 | 제일 중요한 도메인 모델 개발에 집중해야 한다
1288 |
1289 |
1290 |
1291 | 박재성
1292 |
1293 | 의존이 없는 상태로 연습을 해야 한다.
1294 |
1295 | 그리고 연습하면서 하나씩 DB나 기타등등을 붙여보자
1296 |
1297 |
1298 |
1299 | 양완수
1300 |
1301 | 스프링은 POJO를 지향함에도 불구하고 어노테이션들이 도메인으로 많이 침투하고 있다
1302 |
1303 |
1304 |
1305 | 정진욱
1306 |
1307 | 도메인 모델, DTO, Persistance 오브젝트를 분리할 수 있는가?
1308 |
1309 |
1310 |
1311 | 박재성
1312 |
1313 | 스프링과 별개로 위의 3가지를 분리할 수 있다 다만 중복이 생긴다
1314 |
1315 | 3가지 로직이 짬뽕이 될 수 있다 그러나 그것이 스프링단까지 내려와서는 안 된다
1316 |
1317 |
1318 |
1319 | 한상곤
1320 |
1321 | DDD를 만든 사람도 프레임워크가 도메인 모델에 침투하는 것은 어쩔 수 없다고 한다.
1322 |
1323 |
1324 |
1325 | **레거시를 어떻게?**
1326 |
1327 | 이규원
1328 |
1329 | 가만히 두다가 비즈니스적으로 문제가 생겼을 때 건들이자
1330 |
1331 | 그나마도 최소한으로 건들이자
1332 |
1333 |
1334 |
1335 | 레거시에 리팩토링하지말고
1336 |
1337 | 새로 추가되는 것을 TDD로 접근
1338 |
1339 |
1340 |
1341 | 박재성
1342 |
1343 | 혼자 나서서 레거시 뜯다가 고통받지말고
1344 |
1345 | 팀의 문화가 그렇게 될 때 다같이 나설 수 있어야 한다
1346 |
1347 | 그렇게 될 때 점진적으로 리팩토링해보자
1348 |
1349 |
1350 |
1351 | 양완수
1352 |
1353 | 슬픈 이야기
1354 |
1355 | 모든 레거시를 다 뿌수겠다라고 굳은 의지와 함께 도전했으나 나에게 돌아오는 것은 연봉동결
1356 |
1357 | 착한 레거시와 나쁜 레거시를 구분하자
1358 |
1359 | 회사는 돈을 벌어야 한다
1360 |
1361 | 잘 돌아가는 착한 레거시는 냅두자(ㅠㅠ)
1362 |
1363 |
1364 |
1365 | 김창준
1366 |
1367 | TDD든 테스트든 뭐든
1368 |
1369 | ROI를 생각해야 한다
1370 |
1371 | 팀에서 의미없는 일을 하고 있을 수 있다
1372 |
1373 |
1374 |
1375 | ---
1376 |
1377 | 모든 내용을 다 옮기지 못하였고 저의 부족한 지식으로 일부 적지 못한 내용도 있으며 잘못 옮겨져 연사분들의 의도가 제대로 전달되지 않을 수 있으나 이렇게 기록하여 공유할 수 있다는 것만으로도 만족합니다:)
1378 |
1379 |
1380 |
1381 | 개인적으로 TDD, 테스트, 리팩토링에 관심이 있었고 이번 컨퍼런스를 통해 현업에 계신 분들의 현실적인 고민을 들을 수 있어서 좋았습니다.
1382 |
1383 |
1384 |
1385 | 그리고 한 가지 확실한 것은 뭐든 의식적인 연습이 필요하며 많은 시간에 걸쳐 훈련한 덕분에 TDD를 비교적 자유롭게 다루시는 것을 보았습니다.
1386 |
1387 |
1388 |
1389 | 다음에도 좋은 내용의 컨퍼런스가 열렸으면 좋겠네요!
1390 |
--------------------------------------------------------------------------------