text:
<repo_name>1the-Ji/TIL-3<file_sep>/Java/String,StringBuffer,Stringbuilder.md
# String, StringBuffer, StringBuilder Class

## 1. String 클래스

 몇몇 언어에서는 문자열을 char형의 배열로 다루지만, 자바에서는 문자열을 위한 클래스를 제공한다. 그 것이 String 클래스이다.

 ### 1.1 변경 불가능한(immutable) 클래스

![image](https://user-images.githubusercontent.com/22893111/71968566-88a88100-3248-11ea-98ec-8fae7852cbf6.png)

String 클래스의 내부를 보면, 문자열을 저장하기 위해서 문자형 배열 참조변수(char[]) value를 인스턴스 변수로 정의해놓고 있다. 인스턴스 생성 시 생성자의 매개변수로 입력받는 문자열은 이 **인스턴스 변수(value)에 문자형 배열(char[])로 저장**되는 것이다.

한번 생성된 String 인스턴스가 갖고 있는 문자열은 읽어 올 수만 있고, 변경할 수는 없다.
 `+` 연산자를 이용해서 문자열을 결합하는 경우, 인스턴스 내의 문자열이 바뀌는 것이 아니라 **새로운 문자열이 담긴 String 인스턴스가 생성되어 메모리 공간을 차지**하게 된다. **GC(Garbage Collector)가 호출되기 전까지 heap에 계속 쌓이므로 메모리 관리 측면에서 치명적이다.**

## 1.2 문자열의 비교

문자열을 만들 때는 문자열 리터럴을 지정하는 방법과 String 클래스의 생성자를 사용해서 만드는 방법이 있다.

~~~ java
String str1 = "abc";
String str2 = "abc";
String str3 = new String("abc");
String str4 = new String("abc");
~~~

String 클래스의 생성자를 이용한 경우에는 new 연산자에 의해서 메모리 할당이 이루어지기 때문에 항상 새로운 String 인스턴스가 생성된다. 그러나 문자열 리터럴은 이미 존재하는 것을 재사용하는 것이다.

![image](https://user-images.githubusercontent.com/22893111/71973339-15a40800-3252-11ea-8daa-b1f8028b7806.png)

## 2. StringBuffer 클래스와 StringBuilder 클래스

**String 클래스는 인스턴스를 생성할 때 지정된 문자열을 변경할 수 없지만 StringBuffer 클래스는 변경이 가능하다.** 내부적으로 문자열 편집을 위한 버퍼(buffer)를 가지고 있으며, StringBuffer 인스턴스를 생성할 때 그 크기를 지정할 수 있다.

StringBuffer 클래스는 String 클래스와 유사한 점이 많다. StringBuffer 클래스는 String 클래스와 같이 문자열을 저장하기 위한 char형 배열의 참조변수를 인스턴스 변수로 선언해 놓고 있다. StringBuffer 인스턴스가 생성될 때, char형 배열이 생성되며 이 때 생성된 char형 배열을 인스턴스 변수 value가 참조하게 된다.

### 2.1 StringBuffer의 생성자

StringBuffer 클래스의 인스턴스를 생성할 때, 적절한 길이의 char형 배열이 생성되고, 이 배열은 문자열을 저장하고 편집하기 위한 공간(buffer)으로 사용된다.

![image](https://user-images.githubusercontent.com/22893111/71974436-d4612780-3254-11ea-841c-f8476cf9627b.png)

위는 StringBuffer 생성자 코드다.

- public StringBuffer(): 버퍼의 크기를 지정하지 않은 것으로, 16개의 문자를 저장할 수 있는 크기의 버퍼를 생성한다.
- public StringBuffer(int capacity): capacity 크기의 버퍼를 생성한다.
- Public StringBuffer(String str): 지정한 문자열(`str`) 의 길이보다 16이 더 크게 버퍼를 생성한다.

아래의 코드는 StringBuffer 클래스의 일부인데, 버퍼의 크기를 변경하는 내용의 코드이다. StringBuffer 인스턴스로 문자열을 다루는 작업을 할 때, 버퍼의 크기가 작업하려는 문자열의 길이보다 작을 때는 내부적으로 버퍼의 크기를 증가시키는 작업이 수행된다. 배열의 길이는 변경될 수 없으므로 새로운 길이의 배열을 생성한 후에 이전 배열의 값을 복사해야 한다.

~~~ java
//새로운 길이(newCapacity)의 배열을 생성한다.
char newValue[] = new char[newCapacity];

//배열 value의 내용을 배열 newValue로 복사한다
System.arraycopy(value, 0, newValue, 0, count); // count는 문자열의 길이
value = newValue;	// 새로 생성된 배열의 주소를 참조변수 value에 저장
~~~

이렇게 함으로써 StringBuffer 클래스의 인스턴스 변수 value는 길이가 증가된 새로운 배열을 참조하게 된다.

### 2.2 StringBuilder의 변경

아래와 같이 StringBuilder를 생성해보자.

StringBuffer sb = new StringBuffer("abc");

![image](https://user-images.githubusercontent.com/22893111/71976070-c7dece00-3258-11ea-992b-1140c5423643.png)

그리고 sb에 문자열 "123"을 추가하면,

sb.append("123");

![image](https://user-images.githubusercontent.com/22893111/71976139-f2308b80-3258-11ea-92aa-bb14cb9c90be.png)

`append()` 는 반환타입이 StringBuffer인데 자신의 주소를 반환한다. 그래서 아래와 같은 문장이 수행되면, sb에 새로운 문자열이 추가되고 sb자신의 주소를 반환하여 sb2에는 sb의 주소인 0x100이 저장된다.

~~~ java
StringBuffer sb2 = sb.append("ZZ");	// sb의 내용 뒤에 "ZZ" 추가
System.out.println(sb);	// abc123ZZ
System.out.println(sb2); // abc123ZZ
~~~

### 2.3 StringBuilder란?

StringBuffer는 멀티쓰레드에 안전(thread safe)하도록 동기화되어있다. 따라서 **멀티쓰레드로 작성된 프로그램이 아닌 경우, StringBuffer의 동기화는 불필요하게 성능만 떨어뜨리게 된다.**

그래서 **StringBuffer에서 쓰레드의 동기화만 뺀 StringBuilder가 추가된 것이다.** StringBuilder는 StringBuffer와 완전히 똑같은 기능으로 작성되어 있어서, 소스코드에서 StringBuffer 대신 StringBuilder를 사용하도록 바꾸기만 하면 된다.

## 3. 결론

- String class

  문자열 연산이 적을 때 사용하면 좋다. 불변하기 때문에 멀티쓰레드환경에서 동기화를 신경쓸 필요가 없음.

- StringBuffer vs StringBuilder

  공통: 둘 다 문자열 연산이 많을 때 사용하면 좋다.

  차이점: StringBuffer는 thread safe하도록 동기화되어 있어서 멀티쓰레드 환경에서 사용, StringBuilder는 StringBuffer에서 쓰레드의 동기화만 뺀 것이므로 멀티쓰레드 환경이 아닐 때 사용<file_sep>/Article/2020-01/25.md
# 2020-01-25 토요일

## [경영진들이 알아야 할 데이터 분석의 오해와 진실](http://www.bloter.net/archives/368902)

- 빅데이터 처리나 데이터 분석은 체감할 수 있는 성과가 바로 나오는 성격의 일이 아니다. 시간을 갖고 꾸준하게 해야, 일정 시점이 지나 효과를 느낄 수 있는 경우가 대부분이다. 방향을 잘못 잡으면 시간을 갖고 꾸준히 한다 해도 효과는 없는 경우도 적지 않다는 후문이다. 요약하면 빨리 빨리와 대충대충은 데이터 분석과는 ‘상극의 코드’일 수 있다.

- 스타벅스코리아 데이터사이언티스트 출신인 차현나씨가 쓴 책 <데이터읽기의 기술>을 보면 데이터 분석 역량은 해당 회사의 기업 문화와 밀접한 관련이 있다. 경영진이 데이터 분석을 어떻게 바라보느냐가 해당 업무의 퍼포먼스를 좌우한다. 잘 모르는 임원이 수시로 참견하고 끼어들면 결과는 그저 그럴 수 밖에 없다.

- 저자에 따르면 적지 않은 기업 임원들이 빅데이터에 대해 이런저런 착각들을 하고 있는 것 같다. 그 중 하나는 다음과 같은 착각이다.

  > 빅데이터를 경험하지 못한 임원들이 하는 착각 중 하나는 조금이라도 숫자를 다루던 사람을 빅데이터 조직에 발령내면 그 일을 할 수 있겠지라는 것이다.

- 데이터 분석 후 당연해 보이는 결과가 나오면, 빅데이터를 잘 모르는 많은 임원들은 뻔한거 아니냐?는 식의 반응을 보인다. 천리길도 한걸음부터라고 처음에는 당연한 결과를 도출하고, 시간이 가면서 좀더 통찰력이 있는 결과물을 뽑아 내는게 순서라는 것을 가슴으로 느끼지 못한데 따른 결과다.

  > 데이터 분석을 하는 사람들은 관계자들과 분석 내용을 리뷰할 때 이거 내가 다 알던 건데 뭐가 새롭느냐라는 이야기를 듣는 일이 자주 있다. 실제로는 한번도 데이터로 분석한 적이 없는 내용인데도 그렇다. 빅데이터를 분석했으면 알지 못하던 새로운 걸 찾아내주길 바라는 기업 관계자들이 많다. 돈과 시간과 인력을 들였으니 내가 몰랐던 획기적인 이야이를 짜잔하고 들고 와주길 바라는 마음도 이해가 간다. 그러나 늘 그런 것은 아니지만 데이터로 처음 분석을 시작할 땐 그야말로 상식적인 이야기를 듣게 될 것이다. 지금까지는 감으로 가정으로 알던 내용을 처음으로 데이터로 증명하는 시간이기 때문이다. 보통 그때의 증명은 상식에 대한 증명이 될 가능성이 높다.<file_sep>/Link/README.md
# 읽어보면 좋은 링크 모음

- [프레임 워크 공부를 멈춰라](https://medium.com/@jongyoungpark/프레임워크-공부를-멈춰라-1afa37644474)
- [ATDD](https://boorownie.github.io/2019-11-27/agail_and_atdd)
- [도커의 개념과 실습](https://subicura.com/2017/01/19/docker-guide-for-beginners-1.html)
- [Spring Rest Docs 적용: 우아한형제들 기술 블로그](http://woowabros.github.io/experience/2018/12/28/spring-rest-docs.html)
- [intellij에서 entity 기반 ERD 그리기](https://gmlwjd9405.github.io/2019/10/28/intellij-jpa-erd.html)
- [지방대 개발 비전공자가 배달의민족 리드 개발자가 되기까지 - 이동욱님](https://www.youtube.com/watch?v=V9AGvwPmnZU)
- [체대 출신 개발자의 2019년 회고](https://ryan-han.com/post/memoirs/memoirs2019/)
- [도커 Docker 기초 확실히 다지기](https://futurecreator.github.io/2018/11/16/docker-container-basics/?fbclid=IwAR2qU0Hcyvys7IFLjxkKx3foitQYtRZJ7Aa2HnWl1SshOYCMQZn4xC2wyjQ)
- [10만명 접속을 허용하는 시스템 만들기](https://brunch.co.kr/@jowlee/102)
- [로드 밸런서](https://nesoy.github.io/articles/2018-06/Load-Balancer)
- [Garbage Collection - 이상민님(naver d2글)](https://d2.naver.com/helloworld/1329)
- [Java Optional 바르게 쓰기](http://homoefficio.github.io/2019/10/03/Java-Optional-바르게-쓰기/?fbclid=IwAR0b2FZ6DQKfIJT_eyCyKIke2V-lrwgU9N0PibWdXgH7q71dqlyViVgrhTU)
- [개발자를 위한 레디스 튜토리얼 01 - NHN](https://meetup.toast.com/posts/224?fbclid=IwAR23Q9Y4XWAJLxikebwvAfsLDY9VQsvQgYGtekvKsaW9plGUvWkqaL0dvEo)
- [개발자를 위한 레디스 튜토리얼 02 - NHN](https://meetup.toast.com/posts/225?fbclid=IwAR25Xj87Z_HGyWtCddv-rqK-fDCUEUmYdKb1sGgg0W2ilTbpQLjjQuv9cRQ)
- [AOP 1편-이동욱님](https://jojoldu.tistory.com/69)
- [AOP 2편-이동욱님](https://jojoldu.tistory.com/70)
- [AOP 3편-이동욱님](https://jojoldu.tistory.com/71?category=635883)
- [AOP 4편-이동욱님](https://jojoldu.tistory.com/72)
- [Java Static](https://mangkyu.tistory.com/47)
- [JVM 구조와 자바 런타임 메모리 구조](https://jeong-pro.tistory.com/148)
- [우아한테크세미나 - 우아한CRUD(영상)](https://www.youtube.com/watch?v=BlEzbGTQ9Zs&feature=youtu.be)
- [네이버 애플리케이션의 전자서명 원리](https://d2.naver.com/helloworld/744920)
- [Junit5 인스턴스 생성 및 순서 관련 - cheeseyun님](https://cheese10yun.github.io/junit5/)
<file_sep>/Article/2019-12/13.md

# 2012-12-13 금요일

## [딜리버리히어로, 배달의민족 인수](http://www.bloter.net/archives/364375)
<file_sep>/Database/SQL첫걸음/28.인덱스.md
# 28강. 인덱스

## 1. 인덱스

인덱스는 테이블에 붙여진 **색인**이라 할 수 있습니다. 인덱스의 역할은 **검색속도의 향상**입니다.

여기서 '검색'이란 SELECT 명령에 WHERE 구로 조건을 지정하고 그에 일치하는 행을 찾는 일련의 과정을 말합니다. 테이블에 인덱스가 지정되어 있으면 효율적으로 검색할 수 있으므로 WHERE로 조건이 지정된 SELECT 명령의 처리 속도가 향상됩니다.

인덱스의 구조도 목차나 색인과 비슷합니다. 목차나 색인에 제목・키워드별 페이지 번호가 적혀있듯, 데이터베이스의 인덱스에는 검색 시에 쓰이는 키워드와 대응하는 데이터 행의 장소가 저장되어 있습니다.

## 2. 검색에 사용하는 알고리즘

데이터베이스의 인덱스에 쓰이는 대표적인 알고리즘으로는 **'이진 트리(binary tree)'**가 있으며, 그 다음으로 **'해시'**가 유명합니다.

탐색 방법으로 말하면 **'이진 탐색(binary search)'**입니다.

- 풀 테이블 스캔(full table scan)

  인덱스가 지정되지 않은 테이블을 검색할 때는 풀 테이블 스캔이라는 불리는 검색 방법을 사용합니다.

- 이진 탐색(binary search)

  이진 탐색은 차례로 나열된 집합에 대해 유효한 검색 방법입니다. 처음부터 순서대로 조사하는 것이 아니고 집합을 반으로 나누어 조사하는 검색방법입니다.

> 대량의 데이터를 검색할 때는 이진 탐색이 빠르다!

**이진 트리**

이진 탐색은 고속으로 검색할 수 있는 탐색 방법이지만 데이터가 미리 정렬되어 있어야 합니다. 일반적으로 테이블에 인덱스를 작성하면 데이터와 별개로 인덱스용 데이터가 저장장치에 만들어집니다. 이때 이진 트리라는 데이터 구조로 작성됩니다.

## 3. 유일성

이진 트리에서는 집합 내에 중복하는 값을 가질 수 없습니다.

이진 트리에서 '같은 값을 가지는 노드를 여러 개 만들 수 없다'라는 특성은 키에 대하여 유일성을 가지게 할 경우에만 유용합니다. 그래서 기본키 제약은 이진 트리로 인덱스를 작성하는 데이터베이스가 많은 것 같습니다.  (?)

(더 많은 정보: https://lalwr.blogspot.com/2016/02/db-index.html)<file_sep>/Article/2019-12/11.md
# 2019-12-11 수요일

## [MS ‘팀즈’ 리눅스도 지원한다](http://www.bloter.net/archives/364102)

- 마이크로소프트(MS)가 오피스 협업 도구인 ‘팀즈’ 리눅스 버전을 정식으로 선보였다. 리눅스 사용자는 팀즈 리눅스 클라이언트 패키지를 .deb나 .rpm 형식으로 내려받아 사용할 수 있다.
- 팀즈는 MS가 개발한 오피스365 기반의 협업 도구다. 오피스365 문서 및 비즈니스 프로세스 논의, 채팅, 화상회의 등 공동작업을 지원한다.
- 그동안 MS는 자마린을 앞세워 윈도우와 맥OS, 리눅스, 안드로이드, iOS 등을 지원하는 크로스 플랫폼 끌어안는 행보를 보였다. 이제는 개발환경뿐 아니라 앱까지도 크로스 플랫폼을 시도하는 것으로 보인다.
<file_sep>/Article/2020-01/23.md
# 2020-01-23 목요일

## [풀려는 자와 막으려는 자](http://www.bloter.net/archives/368781)

- 미국 뉴욕 맨해튼 지방검사장과 사이버범죄 수사대가 아이폰 잠금 해제를 전담하는 첨단과학수사연구소 운영을 위해 1천만달러(116억원)를 투자한다. 군 경력의 기술 전문가로 구성된 연구소는 수사 대상자의 아이폰 데이터 추출을 시도한다.
- 연구소는 초당 2600만개의 임의 암호를 생성하고 잠금 해제를 시도하는 슈퍼컴퓨터와 열을 가하지 않고 메모리 칩을 꺼내는 로봇, 데이터 추출이 가능한 상태로 기기를 복원하는 장치 등 다양한 특수 장비를 갖춘다. 잠금 해제 가능성이 높은 알고리즘은 무엇인지 어떤 아이폰 잠금을 우선 풀지 등의 모든 작업은 전용 소프트웨어를 통해 관리된다.
- 애플은 FBI 항의를 받은 뒤 아이클라우드 백업 데이터 100% 암호화 계획을 포기한 것으로도 알려진다. 1월21일(현지시간) [<로이터통신>](https://www.reuters.com/article/us-apple-fbi-icloud-exclusive/exclusive-apple-dropped-plan-for-encrypting-backups-after-fbi-complained-sources-idUSKBN1ZK1CT)에 따르면 애플은 2년 전 아이폰 데이터를 아이클라우드에 백업할 때 이를 완전히 암호화하려던 계획을 철회했다. 이용자가 아이폰 데이터를 아이클라우드에 저장할 때 데이터 유출을 차단하기 위해 전과정을 암호화할 계획을 FBI가 반대 입장을 내며 철회한 것이다. 수사 대상자의 증거를 확보할 가장 효율적인 수단을 잃게 된다는 이유에서다. 개인정보를 보호하려는 애플과 상황에 따라 애플을 움직이려는 미 수사당국의 움직임을 보여주는 대립이다. 이번 논쟁 외에도, 애플과 미 정부의 입장 차이는 개인 기기 내 기업 데이터의 안전에도 영향을 미친다.<file_sep>/Article/2019-12/24.md
# 2019-12-24 화요일

## [엣지컴퓨팅 뜨니 통신-클라우드 공룡들 합종연횡 가속](http://www.bloter.net/archives/365415)

- 클라우드가 주도해온 기업용 IT인프라의 시장에 또 하나의 대형 변수가 등장했다. 엣지컴퓨팅이다. 지난해까지만 해도 향후 성장 잠재력으로 관심을 받았던 엣지컴퓨팅은 최근들어 업계 판세를 실제로 뒤흔드는 수준으로 진화했다.
- 실시간 데이터 처리를 위해서는 데이터가 발생하는 물리적인 위치 근처에 관련 컴퓨팅 인프라를 투입하는 것이 현실적이고, 엣지컴퓨팅은 엣지 컴퓨팅은 각종 기기 근처에 소규모 서버들, 이른바 엣지 네트워크를 배치해 지연시간(레이턴시) 문제를 해결하는데 초점이 맞춰져 있다.<file_sep>/Article/2019-12/27.md

# 2019-12-17 금요일

## [어떤 변화를 가져올까, 2020년 IT 키워드 6가지](http://www.bloter.net/archives/365714)
- 2020년 1월14일 윈도우7 지원 종료
- 울트라북은 잊어라 “아테나 프로젝트”
- “폴더블 전성시대”
- 초고화질 디스플레이 ‘마이크로LED’
- ‘1억화소’ 스마트폰 카메라
<file_sep>/Article/2019-12/22.md
# 2019-12-22 일요일
## [“애플, 극비리에 위성 기술 개발 추진”…왜?](http://www.bloter.net/archives/365128)
- 애플이 비밀 프로젝트 팀을 통해 독자적인 위성 통신 기술을 개발하고 있다는 정황이 포착됐다.  이동통신서비스 회사들을 우회해 사용자들에게 직접 인터넷 서비스를 제공하는데 사용할 가능성도 나오고 있어 향후 행보가 주목된다.
- 애플은 비밀 프로젝트 팀을 통해 통신 위성 및 차세대 무선 기술을 개발 중이다. 이는 사용자들의 기기에 데이터를 바로 보내는 것이 프로젝트의 목표일 가능성이 높다는 것을 의미한다. 잠재적으로 이동통신 회사들에 대한 의존도를 줄이거나 전통적인 네트워크 없이도 기기들을 서로 연결할 수 있을 것이라고 블룸버그통신은 전했다. 애플은 향상된 지도 및 새로운 기능을 위해 보다 정확한 기기 위치 추적을 지원하는 차원에서 위성을 검토하는 것일 수도 있다고 덧붙였다.
<file_sep>/Article/2019-11/21.md
# 2019-11-21 목요일

## [“서비스 모든 곳에 AI를”…라인이 AI를 대하는 법](http://www.bloter.net/archives/361846)

## [[현장] 라인 기술을 경험하라…’라인 디벨로퍼 데이 2019′](http://www.bloter.net/archives/361784)
<file_sep>/Article/2019-11/25.md

# 2019-11-25 월요일

## [NHN 토스트, CSA STAR 획득…클라우드 보안 신뢰성↑](http://www.bloter.net/archives/362195)

- Summary
  - NHN의 통합 클라우드 서비스 ‘토스트(TOAST)’가 클라우드 보안 연합(CSA)이 주관하는 클라우드 보안 인증 ‘CSA STAR(Security, Trust & Assurance Registry)’를 획득
  - CSA STAR는 클라우드 서비스 제공업체의 각 보안 항목에 대한 이행 여부와 보안 수준 및 경영 시스템의 효율성을 평가하는 클라우드 보안 인증 프로그램
  - 토스트는 국내 정보보호 및 개인정보보호 관리체계 인증인 ISMS/PIMS 인증과 더불어 국제 표준인 ISO27001(정보보호 관리체계), ISO29100(개인정보보호 관리체계), ISO27017(클라우드 서비스 정보보호 관리체계), ISO27018(클라우드 서비스 개인정보보호 관리체계) 인증을 보유하고 있다. 이번에 CSA STAR를 획득하면서 보안 신뢰성을 강화
  - 이 같은 토스트의 높은 보안 신뢰성과 안정성을 바탕으로, NHN는 KB국민은행의 알뜰폰 서비스 ‘리브모바일(Liiv M)’에 ‘토스트 시큐어(TOAST Secure)’ 서비스를 제공하고 있다. 리브모바일은 토스트를 통해 구축된 KB금융클라우드 ‘클레이온(CLAYON)’에서 구동된다. 리브모바일의 각 서비스는 토스트에서 제공하는 컨테이너 오케스트레이션 플랫폼을 통해 실행 및 운영

## [티머니, 미래 대중교통 결제수단은 ‘안면인식, QR’](http://www.bloter.net/archives/362189)

- Summary
  - 티머니가 ‘한-아세안 혁신성장 쇼케이스 2019’에서 안면인식, QR 등을 활용한 미래 대중교통 결제 솔루션을 선보인다.
  - 한-아세안 혁신성장 쇼케이스 2019는 ‘미래∙상생’을 주제로 4차 산업혁명 관련 4개 분야 스마트 농업(Smart Farming), 미래 환경 에너지(Future Environment&Energy), 미래 모빌리티(Future Mobility), 스마트 라이프(Smart Life)에 대한 특별 전시다. 미래 신산업 분야 한-아세안 협력 기업 37개곳이 참여했다.<file_sep>/Article/2019-12/6.md
# 2019-12-06 금요일

## [블록체인으로 모바일 전자증명을…2020년 ‘이니셜’ 뜬다](http://www.bloter.net/archives/363747)
- 국내 주요 금융서비스 기업과 이동통신사가 모여 블록체인 기술을 활용한 모바일 전자증명 생태계를 열겠다고 나섰다. 서울 중구 더 플라자 호텔에 열린 ‘코리아 DID 이니셜 데이’에서 KEB하나은행∙우리은행∙코스콤∙SK텔레콤∙LG유플러스∙KT∙삼성전자∙현대카드∙BC카드∙신한은행∙NH농협은행 등 11개사가 모여 탈중앙 식별자(DID, Decentralized Identifiers) 기반 서비스 혁신 사례를 발표하고 생태계 활성화 방안에 대해 논의했다.
- 앞서 지난 7월 이들 기업은 블록체인 네트워크 ‘이니셜 DID 연합’ 협약식을 갖고, 공동으로 블록체인 네트워크를 구축하고 이를 기반으로 한 모바일 전자증명 사업을 출범한다고 밝힌바 있다. 이니셜 DID 연합은 과학기술정보통신부와 한국인터넷진흥원(KISA)이 주관하는 ‘2019 블록체인 민간주도 국민 프로젝트’를 계기로 11개사가 결성한 컨소시엄으로, ICT 기업과 금융 기업의 강점을 융합해 온 국민이 편리하고 안전하게 이용할 수 있는 모바일 전자증명 서비스 상용화를 추진하고 있다.
<file_sep>/Article/2019-11/20.md
# 2019-11-20 수요일

## [요기요, 신입 개발자 채용…12월5일까지 모집](http://www.bloter.net/archives/361601)

- Summary
  - 배달앱 요기요, 배달통, 푸드플라이 운영사 딜리버리히어로 코리아가 신입 개발자 공개채용 ‘루키 히어로(Rookie Hero)’ 3기를 모집
  - 11.20 ~ 12.5
  - 딜리버리히어로 코리아는 실무능력과 성장 가능성만을 두고 공정하게 합격자를 선발하기 위해 블라인드 방식으로 채용을 진행
  - [딜리버리히어로 코리아 채용 페이지](https://deliveryhero.co.kr/career)(deliveryhero.co.kr/career)에서 이름, 연락처, 이메일 주소만 간단히 입력하면 지원이 완료

## [라임, GS칼텍스·GS리테일과 파트너십…전동킥보드 충전 네트워크 구축](http://www.bloter.net/archives/361630)

- Summary
  - 글로벌 마이크로 모빌리티 기업 라임(Lime)은 11월19일 GS칼텍스, GS리테일과 파트너십을 체결하고 전동킥보드 충전 네트워크를 도입하기로 했다고 20일 밝힘
  - 향후 전동킥보드 충전 네트워크는 전국 주요 도시 GS칼텍스 주유소, GS파크24의 주차장, GS25 편의점으로 확대될 예정

<file_sep>/Article/2020-01/5.md
# 2020-01-05 일요일

## [양자 컴퓨팅 개발에 뛰어든 ‘IBM·구글·MS·인텔·AWS’](http://www.bloter.net/archives/366230)
- 빅데이터, 인공지능(AI) 시대가 열리면서 더 많은 데이터를 더 빨리 처리해야할 필요가 급증했다. 이에 따라 현재 컴퓨팅 기술보다 획기적으로 빨리 데이터를 처리할 수 있는 양자 컴퓨팅을 향한 거대 IT기업들의 행보도 급물살을 타고 있다.
- 기존의 디지털 컴퓨터는 정보를 스위치처럼 0 아니면 1로 처리한다. 양자 컴퓨터는 1과 0 외에도 중첩과 얽힘 상태의 양자역학 원리를 이용해 정보를 기록하고 처리한다. 기존 컴퓨터가 0과 1이라는 두 가지 신호만을 이용해 연산한다면, 양자 컴퓨터는 0이면서 동시에 1인 중첩된 정보도 처리한다. 단 한 번에 모든 숫자의 연산을 수행한다. 그 덕에 기존 컴퓨팅으로는 1백만년이 걸릴 300자리 정수(1천비트 숫자) 소인수분해가 양자 컴퓨터에서는 하루 안에 끝난다.
- IBM, 구글, 마이크로소프트(MS), 인텔, 아마존웹서비스(AWS) 등 글로벌 IT 기업이 양자 컴퓨팅 개발 레이스에 뛰어든 배경이다. 이들 기업은 금융, 화학, 의료, 물류, 제약, 자동차, 항공 우주 분야 등 데이터를 활용하는 영역에서 양자 컴퓨팅이 확산될 것으로 보고 있다. 암 치료용 약물을 발견하고, 머신러닝 학습을 개선하며, 물류 알고리즘을 개발하고, 비행 제어 시스템 버그를 잡을 때 양자 컴퓨터를 활용할 수 있을 것으로 전망한다. 기존 슈퍼 컴퓨터로 해결하지 못했던 각종 디지털 난제를 해결할 수 있는 기술로도 양자 컴퓨팅을 주목하고 있다.
- 양자 컴퓨팅 기술 연구는 시작 단계다. 대중화되려면  많은 시간이 필요한 상황이다. 하지만  거대 IT기업들이 양자컴퓨팅에 쏟아붓는 실탄을 늘리면서 나름 의미 있는 결과물도 나오기 시작했다. 초전도를 이용한 작은 규모의 양자 컴퓨터가 나왔고, 클라우드 서비스에서 양자 컴퓨팅을 경험할 수 있는 환경도 공개됐다.
<file_sep>/Article/2019-11/30.md
# 2019-11-30 토요일

## [버스용 기가급 와이파이 나올까…첫 시연 성공](http://www.bloter.net/archives/362917)

## [“사진 속 그 옷, 쇼핑몰서 찾아드려요”](http://www.bloter.net/archives/362933)
<file_sep>/Article/2019-12/17.md
# 2019-12-17 화요일

## [오픈뱅킹 18일 전면 시행…핀테크 혁신 탄력 받나](http://www.bloter.net/archives/364787)

- 국내 주요 은행 10여곳 대상으로 이뤄지던 오픈뱅킹 서비스가 핀테크 사업자로까지 전면 확대 시행된다.
- 오픈뱅킹은 금융 서비스를 편리하게 개발할 수 있도록 은행의 금융서비스를 표준화된 형태로 제공하는 인프라다. 지난 2월 금융위원회가 발표한 ‘금융결제 인프라 혁신 방안’에 따라 그동안 폐쇄적이었던 금융결제망을 은행권은 물론 핀테크 사업자에게 전면 개방하는게 핵심이다. 오픈뱅킹이 활성화 되면 기존 금융서비스에 새로운 IT기술을 접목시킨 다양한 핀테크 서비스를 빠르고 편리하게 출시할 수 있을 것으로 관련 업계는 기대하고 있다.

<file_sep>/Article/2019-12/14.md
 # 2019-12-14 토요일

 ## [구글 ‘실시간 통역’ 시작, 폰만 있으면 외국인과 대화 OK](http://www.bloter.net/archives/364399)
 - 외국어를 배우지 않아도 외국인과 실시간으로 대화할 수 있는 시대가 곧 열릴지도 모르겠다. 구글이 ‘구글 어시스턴트’를 활용한 실시간 통역 서비스에 나섰다.
 - 구글은 12월13일부터 구글 어시스턴트 ‘실시간 통역 모드’를 시작한다고 밝혔다. 사용자가 입력한 단어나 문장을 번역해 주는 것이 아니라 주고 받는 대화를 스마트폰을 통해 자동으로 실시간 통역하는 게 특징이다. 기존 구글 번역 서비스 사용 편의성을 더 끌어올렸다.
 - 구글 어시스턴트를 호출하고 실시간 통역을 원하는 언어를 말하거나 입력하면, 실시간 통역이 이뤄진다. 예를 들어, “헤이 구글, 실시간 통역 모드 실행”, “헤이 구글, 영어 통역 도와줘”라는 식으로 말하면 된다. 통역은 말을 인식해 자동으로 번역하는 식으로 이뤄진다. 스마트폰에서 구글 어시스턴트 실시간 통역 모드를 실행한 채 대화를 주고 받으면, 어시스턴트가 이를 알아듣고 자동으로 상대방 언어로 순차적으로 통역한다.
<file_sep>/Article/2020-01/20.md
# 2020-01-20 월요일

## [네이버, 클라우드 플랫폼으로 국내 SW업체 해외 진출 지원한다](http://www.bloter.net/archives/368535)

- 네이버 기업용 솔루션 부문 자회사인 네이버 비즈니스 플랫폼(이하 NBP, 대표이사 박원기)이 대중소기업&농어업협력재단(사무총장 김순철) 및 한국SW산업협회(이하 KOSA, 회장 이홍구)와 국내 중소 규모 소프트웨어 회사들의 글로벌 시장 진출을 지원하기 위해 협력한다.
- 대중소농어업협력재단에서 주관하는 ‘대중소기업 동반진출 사업’은 해외 진출 역량이 부족한 중소기업을 위해 대기업의 해외 네트워크 및 인프라를 활용, 공동 마케팅 및 해외 판로를 개척할 수 있도록 지원하는 사업이다. 이 사업에서 NBP는 자사 퍼블릭 클라우드 서비스인 ‘네이버 클라우드 플랫폼을 통해 해외 진출에 필요한 네트워크 및 인프라를 중소기업에게 제공하며, 다양한 공동 마케팅도 지원한다.

- 이날 세미나에 참석한 NBP 박기은 CTO는 동남아 시장에서 NBP의 글로벌 리전 및 SaaS 솔루션 역량을 강조하며 “동남아 지역에 가장 많은 글로벌 리전을 보유하고 있어 진출 기업들에게 가장 안정적인 서비스를 제공할 수 있을 것이고, 동남아 1위 SaaS 업체인 데스케라(Deskera)와도 협력하고 있어 적극적인 지원이 가능하다“고 밝혔다.<file_sep>/Article/2019-11/29.md
# 2019-11-29 금요일

## [‘co’, ‘go’ 빠진 숫자 도메인 시대 온다](http://www.bloter.net/archives/362956)

## [네이버 바이브, AI로 다음곡 자동추천한다](http://www.bloter.net/archives/362885)
<file_sep>/seminar/KSUG 2019.md
# KSUG 2019

# 1. 더 자바: 코드를 테스트하는 다양한 방법 - 백기선님

인프런에 올라온 강의랑 다르지 않다.

- JUnit5
- Mockito
- TestContainer
- (Docker, Docker-Compose, jib)
- (docker-compose-rule)
- Chaos Monkey

## JUnit 5: With Spring Boot

스프링 부트에 JUnit5 들어옴

@DisplayName: 테스트를 보기 쉽게 해줌

Class 위에도 @DisplayName을 붙일 수 있다.

### JUnit 5: assertAll()

여러 개의 테스트를 한번에 실행할 수 있음.

ex)

~~~ java
assertEquals();
assertEquals();
...

assertAll(
	() -> assertEquals(),
  () -> assertEquals(),
  ...
)
~~~

assertEquals(expected. actual, message ...);

assertEquals(기대했던 값,실제 값 )

### JUnit4 애노테이션과 비교

@BeforeClass/ @AfterClass -> @BeforeAll/ @AfterAll

### 그밖에

@Tag

@Nested

@RepeatedTest

@ParameterizedTest

Dependency Injection

@TestInstance(LifeCycle.PER_CLASS)

테스트 인스턴스는 매번 만들어짐(테스트 메서드 실행할 때마다)

@RunWith 애노테이션을 안써도 됨 => 쓰면 안됨

## Mockito: With Spring Boot

**@RequiredArgsConstructor** : `final`이나 `@NonNull`인 필드 값만 파라미터로 받는 생성자를 만들어줌

### Mock을 만드는 방법

@ExtendWith(MockitoExtention.class)

@Mock

@MockBean: **기존에 사용되던 Bean의 껍데기만 가져오고 내부의 구현 부분은 모두 사용자에게 위임**한 형태

### Mockito: Subbing

Mock객체가 어떻게 동작해야되는지 미리 정의해 놓는 것

- when() 메서드 - when 메서드 대신 given 메서드 써도됨

Given, when, then 형태로 테스트 구현하는 것도 괜찮은 방법

### Mockito: Verify

Verify: 원하는 메소드가 특정 조건으로 실행되었는지를 검증

### 그 밖에

final 타입, 이늄, 메서드 Mocking

Stricker 모드

### 개발 및 테스트를 Production과 최대한 비슷하게 만들 수 없을까?

테스트는 H2 Database를 사용하도록 -> postgresSQL -> TestContainers를 사용해서 컨테이너를 관리하도록 하자.

TestContainers 의존성 추가, @TestContainers 애노테이션, @Container 애노테이션 사용

### 로컬에서는 재현하기 힘든 운영 환경 이슈

- 디스크 장애로 인한 DB에러
- 온갖 네트워크 이슈

### Chaos Monky for Spring Boot

# 2. 더 자바: 코드를 조작하는 다양한 방법

## 바이트코드 조작

- .java 파일(소스 코드)를 컴파일 한 뒤 생성된 .class 파일에 들어있는 내용
- 클래스로더가 읽어들여 사용하는 코드
- Instruction set for the JVM

### 언제 왜 쓰는가?

- 스칼라 같은 새로운 언어의 컴파일러
- 소스 코드에 있을 법한 버그나 안 좋은 습관을 찾아내거나 복잡도를 분석
- ...
- **스프링 컴포넌트 스캔**
- 하이버네이트 ORM 프록시를 사용한 Lazy Loading

### 스프링 컴포넌트 스캔

- AnnoationConfigApplicationContext의 scan 메서드

ByteBuddy를 사용해서 바이트코드를 조작할 수 있다.

기존 코드를 건드리지 않고 몰래 조작하려면?
.java 파일 -> .class 파일 -> **트랜스포머** -> 클래스 로더

# 3. 자바한정: null 서바이벌 가이드 1 - 박성철님

코더는 설계를 하는 사람이다. 산출물은 코드고 도구는 프로그래밍 언어.

## 차례

- null과 null 안전성
- null을 안전한 코딩법
- null에 안전하다고 점검해주는 도구

## 1. null과 null 안전성

---

'나는 그것을 10억 달러짜리 실수라고 부릅니다. **null** 참조를 창안한 것이 1965년이었습니다.' - 토니 호어

#### Null 참조의 기원: 레코드 핸들링

- 레코드 핸들링
- 토니 호어 경, 65년 창안, 65년 발표
- 레코드와 객체지향의 효시가 된 논문
- 복잡한 구조의 자료를 표현하고 다루는 법을 설명
- 시뮬라 67, 스몰톡 같은 OOP 언어와 알골 계열 언어의 복합 데이터 타입에 영향을 끼침
- 참조(Reference) - 레코드 간의 관계를 표현
- **null** - 관계가 없음을 나타내는 특수 값
- null인 값을 사용하려고 하면 오류
- 두 참조값이 null일 때 두 참조는 동일하다고 판단

> '널 값이 모든 타입의 멤버가 되도록 했고, 따라서 참조 변수를 사용할 때마다 널을 확인해야 한다. 이것이 아마도 10억 달러짜리 실수일 것이다.' - 토니 호어

소프트웨어 결함 통계 중 NullPointerException이 두번째로 많다.

#### 자바에서 null이 문제인 이유

- 모호한 null의 의미: 초기화되지 않음, 정의되지 않음, 값이 없음, 오류, null 값?
- 모든 참조의 기본 상태 또는 값
- 모든 참조는 null일 가능성이 있음- 불안감, 제거 불가능한 불확실성, 잠재적 오류 가능성

#### null 안전성 언어 지원

- 2000년 대 중반부터 null 안전성이 중요한 화두가 됨
- null 안전 연산자: null을 안전하고 쉽게 다루도록 도와주는 연산자
  - Null 병합 연산자(Null coalescing operator)
  - Null 조건 연산자(Safe navigation operator)
- Null 안전한 타입 시스템: null 안전하다는 사실을 보장해주는 타입 시스템(런타임이 아니라 컴파일 타임에 null에 안전하다는 것을 보장)

**Null 병합 연산자**

- 첫 인자가 null 이면 null을 반환하고 아니면 두번째 인자를 반환하는 연산자
- 3항 연산자의 단축형: a?:b는 a!=null?a:b와 동일
- 통칭 '엘비스 연산자'
- a ?: b : 그루비, 코틀린,PHP
- a ?? b : 자바스크립트, C#
- a or b : 파이썬
- a // b : perl

**Null 조건 연산자**

- 첫 인자가 null 이면 null을 반환하고 아니면 두번째 인자의 작업을 실행하는 이항 연산자
- 명시적인 null 확인과 값 할당을 반복하지 않고 메서드 호출을 연이어 할 수 있도록 해줌

- 코틀린: val name=article?.author?.name

**Type System 강화**: 컴파일 타임에 null 안전성 보장

Null 가능 타입

- null 가능을 선택적 타입 속성으로 취급
- 기본으로 모든 타입은 null 불가능
- null은 여전히 모든 타입에 대입 가능한 특수한 값
- 코틀린, C#

Null 타입과 타입 결합(Union)

- null은 Null 타입의 값
- 특정 타입과 Null 타입을 결합해서 Null 가능하도록 선언 가능

Optional 사용

- 기본으로 모든 타입이 Null 불가능
- 값이 없을 수 있음은 Optional이나 Maybe 같은 펑터를 사용해서 표현
- 주로 함수형 프로그래밍 언어의 방식

## 2. null 안전한 코딩 법

---

#### null 안전한 좋은 코딩 법

1. 기본적으로 Null을 쓰지 말자
2. null 문맥을 제한된 범위 안에 가두자
3. API에 Null을 최대한 쓰지 말자
4. Null 객체를 활용하자
5. null을 명시적으로 표현하자
6. 계약에 의한 설계(design by contract)를 적용하자
7. 구조체는 펑터(Functor)를 활용하자
8. 객체의 기본값을 유용하게 만들자

#### 1. 기본적으로 null을 쓰지 말자

- '무조건적인 null 사용은 만악의 근원이다'
- null 안전 규칙
  - Null 을 쓰지 말라
- null 가능 !=null 사용
- 객체 필드, 매개변수, 반환 값은 가능한 null 불가능으로 취급

#### 2. null 문맥을 제한된 범위 안에 가두자

> OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things - <NAME>

- CS 대원칙, '큰 문제는 제어 가능한 작은 문제로 나누어 정복하고 다시 통합한다'
- 상태, null을 포함한 부작용은 지역적으로 제한할 경우 큰 문제가 안됨
- 클래스와 메서드를 작게 만들자
- 설계가 잘 된 코드에서는 null의 위험도 관리 가능

**좋은 캡슐화**

- 높은 응집성: '한 가지 책임만 갖는다(SRP)'
  - 모든 필드가 객체와 생애주기가 같다
  - 모든 메서드가 모든 필드를 대상으로 작업한다
- 낮은 결합
  - 디미터 법칙, '묻지 말고 시켜라'
  - 인터페이스에 의존

**디미터 법칙**

- 객체의 과도한 결합을 낮출 수 있는 형식적 규칙
- '묻지 말고 시켜라'로 자연스럽게 연결
- 내부 구현을 외부로 유출되지 않게 막아줌
- 디미터 법칙 예: DDD 에그리것(Aggregate)

#### 3. API에 null을 최대한 쓰지 말아라

- null로 지나치게 유연한 메서드를 만들지 말고 명시적인 메서드를 만들어라
- null을 반환하지 말라
  - 빈 컬렉션이나 null 객체를 활용해라
  - 반환 값이 없을 수도 있음 명시적으로 Optional로 표현하라
  - 오류 상황에는 null을 반환하지 말고 예외를 던져라
- 선택적 매개변수는 Null 대신 정적 다형성(메서드 추가 정의:overload)를 사용해서 표현하라

#### 4. null 객체를 활용하자

Null 객체 (특수 사례 패턴: Special Case Pattern)

- 인터페이스는 동일하지만 아무 일도 하지 않는, 일종의 더미 객체
- 이 객체와 협력하는 객체는 더미라는 사실을 몰라야 함
- 타입 안전하면서 의미를 표현할 수 있는 동일 타입의 특수 상황용 객체를 null 대신 반환(Special Case Pattern)
- 리스코프 치환 원칙 주의
- 디미터 법칙과 '묻지 말고 시켜라'를 잘 시켜야 유용

#### 5. Null을 명시적으로 표현하자

- null은 참조 값이 없음 나타내는 암시적 표현 방법
- java.util.Optional은 값이 없음을 명시적으로 표현하는 컨테이너 객체
- 받는 측에서 값이 없음을 인지하고 이에 대한 처리를 놓치지 않을 수 있음
- 메서드 반환값으로만 사용하기를 권고
- 불필요한 기본 타입의 박싱/언박싱을 피하자

>  "Optional - The Mother of All Bikesheds" - <NAME>

	1. 절대로 Optional 변수와 반환값에 Null을 사용하지 마라
 	2. Optional에 값이 들어 있다는 걸 확신하지 않는 한 Optional.get()을 쓰지 말라
 	3. Optional.isPresent(), get() 외 API를 가능한 사용하라
     ...

#### 6. 계약에 의한 설계(Design by Contract)를 적용하자

- API 규약을 소비자와 제공자 사이에 지켜야 할 엄격한 계약으로 여기는 설계 방법
- 형식적 규약 외에 사전 조건과 사후 조건과 유지 조건을 포함

####  7. 구조체에는 펑터(Functor)를 활용하자

> '객체는 추상화로 데이터를 뒤에 숨기고 대신 이 데이터를 다루는 행위를 노출한다. 데이터 구조체는 데이터를 노출하고 의미 있는 함수를 갖지 않는다.' - 로버트 C 마틴

- 자바 != 순수 OOP 언어, 자바 클래스의 인스턴스가 모두 객체일 필요는 없다
- 자바 클래스를 데이터 구조체로 적절히 사용 가능 (예: DTO)
- 구조체는 객체가 아니므로 디미터 법칙 적용 대상이 아님

**Optional = 펑터(Functor)**

#### 8. 객체의 기본값을 유용하게 만들자

- 객체를 생성했을 때 갖는 기본 값이 쓰기에 문제 없어야 한다
- 실행 전에 모든 필드가 초기화되어야 한다
- 실행 시점에 null인 필드는 초기화되지 않았다는 의미가 아닌, 값이 없다는 의미여야 한다
- 객체 필드의 생명주기는 모두 객체의 생명주기와 같아야 한다

## 3. null에 안전하다고 점검해주는 도구

- 정적 분석 또는 어노테이션 프로세싱
  - JSR 305
  - 전문 정적 분석 도구(파인드 벅스 & 스팟 벅스)
  - IDE 지원
  - 어노테이션 프로세서(Lombok, Nullaway)

- 타입 시스템 확장
  - JSR 308

<file_sep>/Article/2019-11/27.md
# 2019-11-27 수요일

## [오늘을 만드는 기술이 궁금하다면…’NHN 포워드 2019′ 현장](http://www.bloter.net/archives/362596)

## [요기요, 홈플러스 익스프레스도 배달한다](http://www.bloter.net/archives/362563)
<file_sep>/Article/2019-11/24.md

# 2019-11-24 일요일

## [LG 로봇, 이번에는 빕스 1호점에 취직](http://www.bloter.net/archives/362091)

- Summary
  - 클로이 셰프봇은 직접 국수를 만들어 고객에게 제공한다. 고객이 국수 코너인 ‘라이브 누들 스테이션’에서 원하는 재료를 그릇에 담아 셰프봇에게 건네면 셰프봇은 뜨거운 물에 국수 재료를 삶아 다시 그릇에 담고 육수를 부어 요리를 완성한다. 클로이 셰프봇은 1분에 국수 한 그릇을 조리할 수 있다.
  - 노진서 LG전자 로봇사업센터장(전무)은 “CJ푸드빌과 협업을 통해 고객에게 차별화된 가치를 제공할 수 있게 됐다”라며 “앞으로도 식음료 산업 분야에서 로봇의 활용도를 제고해 고객 가치를 높이고 로봇 사업의 경쟁력을 향상시킬 수 있는 방안을 지속적으로 모색할 것”<file_sep>/Article/2020-02/26.md
# 2020-02-26

## [“장벽 낮추고, 투자 늘리고”…금융위 ‘핀테크·디지털금융 혁신과제’ 발표](http://www.bloter.net/archives/372399)

- 금융위원회(금융위)가 핀테크 기업 지원 강화 및 디지털금융 혁신을 위한 제도 마련을 골자로 하는 ‘핀테크·디지털금융 혁신과제’를 발표했다.

- 디지털금융 고도화, 데이터 경제 활성화, 핀테크 신산업·신서비스 육성, 핀테크·디지털 규제개혁, 핀테크·디지털금융 혁신기반 강화 정책 등을 통해 지속가능한 디지털금융 시장을 만들겠다는 전략이다.

- 핀테크 기업의 금융업 진입장벽 완화

  - 먼저 금융위는 금융규제 샌드박스 운영(1년간 100건 이상)과 연계해 금융업 진입장벽 완화 및 맞춤형 규제개혁 등 규제혁신노력을 지속할 방침이다.
  - 규제 샌드박스 테스트를 완료한 혁신금융사업자의 안정적인 금융업 진입여건을 조성하기 위해 하반기에 ‘금융혁신법 개정안’을 국회에 제출할 계획이다. 또한 소규모·특화 금융회사 신설이 용이해지도록 개별 금융업의 인·허가단위를 세분화하고 진입요건도 완화하겠다고 밝혔다.

- #### 디지털금융 고도화 위해 ‘전자금융거래법’ 개정 추진

  - 금융위는 금융업 진입장벽 완화를 위해 ‘전자금융거래법’ 개정을 추진한다. 디지털금융 전반의 혁신과 디지털리스크 관리체계 구축을 통해 지속 가능한 금융혁신과 안정을 도모하기 위해서다.
  - 전자금융거래법 개정을 통해 모든 은행이 참가기관 등에게 은행 자금이체 기능을 표준화(API)해 제공하는 의무 규정을 마련한다. 동시에 은행결제망을 이용하는 참가기관 등에 대해 처리 순서, 처리 시간, 비용(이용료) 등에서의 차별행위를 금지할 예정이다. 안정적 시스템 운영과 결제 인프라 혁신 확장성 및 안정성 확보를 통해 오픈뱅킹 고도화를 이뤄가겠다는 전략이다
  - 또한 핀테크 유니콘 기업이 활발하게 출현할 수 있도록 디지털 금융산업 도약을 위한 제도적 기반을 만들겠다고 밝혔다. 간편결제 송금, 계좌기반의 다양한 서비스 등이 가능한 금융 플랫폼 육성을 위해 마이페이먼트(MyPayment), 종합지급결제사업자를 도입한다.
  - 아울러 디지털 금융거래의 기반인 실명확인·인증 규제를 개선해 생체정보, 분산신원확인 등 혁신적인 인증 서비스, 간편결제 선불 충전·이용한도를 확대하는 방안 등도 마련한다.

- 데이터 중심의 금융 신사업 육성

  - 금융위는 흩어진 개인의 신용정보를 통합 조회관리하고, 신용평점·재무관리까지 지원하는 마이데이터 산업을 도입할 예정이다. 오는 4월 중 안전한 데이터 이동을 위한 표준 API 구축 등 마이데이터 서비스 체계를 차질없이 구축하고, 허가방안을 마련해 발표할 계획이다.
  - 또한 전문·특화 신용평가(CB)사 등 데이터 신산업 분야 사업자의 신용평가가 원활히 안착할 수 있도록 개인신용평가 체계를 선진화한다. 금융위는 등급제로 운영해 오던 개인신용평가 체계를 점수제로 4분기 안에 전환할 방침이다.<file_sep>/Java/Collections/unmodifiable.md
## 종류
- Collections.unmodifiableList()
- Collections.unmodifiableCollection()
- Collections.unmodifiableMap()
- Collections.unmodifiableSet()
- Collections.unmodifiableSortedSet()
- Collections.unmodifiableSortedMap()
- 등등 ...

## 용도

Map, List, Set 등의 자료구조를 읽기 전용으로 생성하고 싶을 때 사용.

## 어떻게?

Collections의 Unmodifiable~ 내부를 보면

add, put, remove 등의 메서드를 호출하면 UnsupportedOperationException 예외를 던지도록 했다.

읽기 전용이므로 value의 추가, 수정, 삭제 등의 작업은 할 수 없다.

## [Immutable vs Unmodifiable collection](https://stackoverflow.com/questions/8892350/immutable-vs-unmodifiable-collection)

<file_sep>/Article/2020-01/21.md
# 2020-1-21 화요일

## [델-메가존, 하이브리드 클라우드 겨냥 서비스 사업 동맹](http://www.bloter.net/archives/368579)

- 한국델테크놀로지스(Dell Technologies, 대표 김경진)와 국내 클라우드 관리 기업(MSP)인 메가존 클라우드(대표 이주완)가 클라우드 서비스 모델 개발과 공동 대고객 활동 협력을 골자로 하는 업무협약(MOU)을 맺었다고 1월21일 밝혔다.
- 양사가 협력해 제공할 하이브리드 및 프라이빗 클라우드 관리 서비스(Cloud Managed Service)는 ▲서비스형 소프트웨어 정의 데이터센터(SDDC-as-a-service) ▲하이브리드 클라우드 백업 및 재해복구 서비스(Data Protection-as-a-service) ▲서비스형 컨테이너(Container-as-a-service) 등으로 구성된다.
- 이중 ‘서비스형 소프트웨어 정의 데이터센터는 프라이빗 혹은 하이브리드 클라우드 도입을 원하는 기업들을 대상으로 빠르고 안정적으로 서비스형 인프라(IaaS)를 구축하도록 돕는 서비스다.
- 델 테크놀로지스 하이퍼 컨버지드 인프라(HCI) 솔루션인 ‘델 EMC V엑스레일(Dell EMC VxRail)’과 V엑스레일 상에서 구동되는 VCF(VMware Cloud Foundation)에 기반하며 여기에 메가존 클라우드의 컨설팅과 관리 및 마이그레이션 서비스 등이 결합된다. VM웨어 소프트웨어 정의(SDN) 솔루션인 NSX와 델 테크놀로지스의 다양한 오픈 네트워킹(Open Networking) 솔루션을 통해 SDN 구축을 지원한다.<file_sep>/Article/2019-12/3.md
# 2019-12-3 화요일

## [카카오엔터프라이즈 출범, B2B 시장 겨냥](http://www.bloter.net/archives/363312)
- 카카오엔터프라이즈가 공식 출범했다.
- 카카오엔터프라이즈는 지난 5월 카카오의 사내 독립기업(CIC)으로 조직 개편됐던 AI LAB이 분사한 회사다. AI랩은 그동안 카카오 안에서 인공지능(AI), 겸색, 챗봇 등을 개발하고 운영해 왔다. 이번 출범을 계기로 B2B 사업에 적극 나선다는 계획이다.
- 카카오엔터프라이즈는 합리적인 비용과 안정성, 편리성을 갖춘 서비스에 대한 수요가 큰 엔터프라이즈 IT 시장에서 서비스형플랫폼(PaaS), 서비스형소프트웨어(SaaS) 분야의 대표 사업자로 성장할 계획이다. 이를 위해 자동차와 주택 등 건설산업 중심으로 진행하고 있는 카카오의 인공지능 플랫폼 ‘카카오i’를 유통, 소비재, 엔터테인먼트 등 폭 넓은 영역으로 확장한다는 전략이다.
- 기존 협업 비즈니스 툴인 ‘아지트’도 카카오엔터프라이즈가 맡아서 운영한다. 메신저 사업 분야 기술을 살려 기업용 메신저 시장을 공략하겠다는 계획으로 보인다. 카카오엔터프라이즈는 업무 효율성을 향상시킬 수 있도록 인공지능과 고도화된 검색 기능을 기업용 메신저와 함께 제공할 예정이다. 아울러 기업 고객의 ‘카카오i’ 활용 및 다양한 클라우드 구축 요청에 대응할 수 있도록 적극적인 R&D 투자와 지원도 예고했다.
- 카카오엔터프라이즈는 “‘모든 것에 AI를 더하여 연결하고, 문제를 해결하고, 새로운 가치를 창조한다’라는 슬로건을 바탕으로 카카오톡 기반의 운영 노하우와 인공지능 기술을 기반으로 전 산업분야의 디지털 트랜스포메이션을 선도할 예정이다”라고 밝혔다.

<file_sep>/Spring/서버 템플릿 엔진과 머스테치 소개.md
# 서버 템플릿 엔진과 머스테치 소개 - by <스프링 부트와 AWS로 혼자 구현하는 웹 서비스> 책

## 서버 템플릿 엔진?

 일반적으로 웹 개발에 있어 템플릿 엔진이란, **지정된 템플릿 양식과 데이터**가 합쳐져 HTML 문서를 출력하는 소프트웨어를 이야기합니다. 즉, 쉽게 말해서 웹 사이트의 화면을 어떤 형태로 만들지 도와지는 양식이라고 이해하면 됩니다. 예전에 스프링이나 서블릿을 사용해본 사람은 JSP, Freemarker 등을 떠올릴테고, 요즘 개발을 시작한 사람들은 리액트, 뷰의 View 파일들을 떠올릴 것입니다.

 둘 모두 결과적으로 **지정된 템플릿과 데이터를** 이용하여 HTML을 생성하는 템플릿 엔진입니다.

다만, 전자는 서버 템플릿 엔진이라 불리며, 후자는 클라이언트 템플릿 엔진이라 불립니다.

서버 템플릿 엔진을 이용한 화면 생성은 서버에서 Java 코드로 문자열을 만든 뒤 이 문자열을 HTML로 변환하여 **브라우저로 전달합니다.**

반면에 자바스크립트는 브라우저 위에서 작동합니다. 브라우저에서 작동될 때는 서버 템플릿 엔진의 손을 벗어나 제어할 수가 없습니다.

 흔히 이야기하는 Vue.js나 React.js를 이용한 SPA(Single Page Application)는 브라우저에서 화면을 생성합니다. 즉, **서버에서 이미 코드가 벗어난 경우입니다** .

## 머스테치란?

머스테치는 **수많은 언어를 지원하는 가장 심플한 템플릿 엔진**입니다.

루비, 자바스크립트, 파이썬, PHP, 자바, 펄, Go, ASP 등 현존하는 대부분 언어를 지원하고 있습니다. 그러다 보니 자바에서 사용될 때는 서버 템플릿 엔진으로, 자바스크립트에서 사용될 때는 클라이언트 템플릿 엔진으로 모두 사용할 수 있습니다.

 자바 진영에는 JSP, Velocity, Freemarker, Thymeleaf 등 다양한 서버 템플릿 엔진이 존재합니다.

머스테치의 장점

- 문법이 다른 템플릿 엔진보다 심플
- 로직 코드를 사용할 수 없어 View와 서버의 역할을 명확하게 분리됨
- Mustache.js와 Mustache.java 2가지가 다 있어, 하나의 문법으로 클라이언트/서버 템플릿을 모두 사용 가능<file_sep>/Article/2019-12/30.md
# 2019-12-30 월요일
## [수익 없이 성장만 하는 스타트업 거품이 꺼지고 있다](http://www.bloter.net/archives/365863)
- 고객 기반을 늘리기 위해 적자를 감수하고 무료와 할인으로 대표되는 공격적인 마케팅에 무게를 둬왔던 미국 스타트업들이 이제 투자자들로부터 수익을 내야 한다는 압박을 실질적으로 받기 시작하는 모양이다.
- 투자 받은 돈을 앞세운 수익 없는 성장 전략은  투자자들에게 점점 더 먹혀들기 힘든 분위기가 마련되고 있다는 것. 벤처캐피털 관계자들 사이에선 무료와 할인 같은 보조금은 ‘거품이 끝났다’는 얘기까지 나오고 있다. 특히 승차 공유나 배달 등 이른바 온라인 투 오프라인(Online to offline: O2O)이나 이커머스 관련 스타트업들을 향해 “수익을 낼 수 있겠느냐?”라는 물음표가 몰리는 양상이다.
- 12월27일(현지시간) <월스트리트저널(WSJ)> 보도에 따르면 수익을 내지 못하면서도 고객을 끌어들이기 위해 무료나 할인에 초점을 맞추는 스타트업들의 전략은 투자자들의 선호 리스트 에서 멀어지고 있다.
- 벤처캐피털로부터 투자를 유치한 뒤 이를 고객 유치를 위해 보조금으로 쏟아붓는 스타트업들은 지난 10여년간 빠르게 확산됐다. 차세대 거대 소비자 테크 기업에 자금을 대기 위해 투자자들이 앞다퉈 뛰어든 결과였다.
- 하지만 최근들어 분위기는 달라졌다. 몇년간 투자했던 회사들이 참담한 성적표를 보여주면서 투자자들과 이사회 멤버들은 이들 회사들에게 수익을 낼 수 있는 비즈니스 모델을 생각하도록 압박하고 있다고 <월스트리트저널>은 전했다. 투자자들은 스타트업들이 고객 할인에 따른 비용을 확보하기 위해 투자를 유치하는 것에 덜 의존하길 바라고 있다고 <월스트리트저널>은 덧붙였다
<file_sep>/Spring/@RequestBody는 왜 기본생성자가 필요하며, Setter는 필요없을까? #1.md
> Springboot로 토이 프로젝트를 진행중 Request DTO(requestBody로 오는)에 `@NoArgsConstructor`를 빠뜨려서 에러가 났다. (습관적으로 적어오던 어노테이션...)
> 그런데 `@RequestBody`로 넘어오는 객체에는 기본 생성자가 왜 필요한지, Setter는 왜 불필요한지 **이유**를 정확히 모르고 있었다.
> 이번 기회에 해당 내용들을 구체적으로 알아보자.

`@RequestBody`에 왜 기본생성자가 필요하고, Setter는 불필요한지 알아보기 위해선 Spring 내부에서 어떻게 처리하는지 확인해보면 됩니다.`@RequestBody` 까지 처리하는 flow를 보고싶다면 [여기](https://m.blog.naver.com/PostView.nhn?blogId=duco777&logNo=220605479481&proxyReferer=https%3A%2F%2Fwww.google.com%2F)를 확인하면 좋을 것 같다. 그리고 이동욱님의 [글](https://jojoldu.tistory.com/407)에는 Setter가 왜 필요없는지에 대해 적혀있어서 도움을 받아 적습니다.

 개발 환경과 코드는 아래와 같습니다.
 > OS: Mac OS
 >  IDE: Intellij
 >  Spring(boot) Version: SpringBoot 2.2.2
 >  etc: h2, JPA, Lombok

**Posts Entity**
~~~ java
@Getter
@NoArgsConstructor
@Entity
public class Posts {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(length = 500, nullable = false)
    private String title;

    @Column(columnDefinition = "TEXT", nullable = false)
    private String content;

    private String author;

    @Builder
    public Posts(final String title, final String content, final String author) {
        this.title = title;
        this.content = content;
        this.author = author;
    }
}
~~~

**PostsSaveRequestDto**
~~~ java
@Getter
@NoArgsConstructor
public class PostsSaveRequestDto {

    private String title;
    private String content;
    private String author;

    @Builder
    public PostsSaveRequestDto(final String title, final String content, final String author) {
        this.title = title;
        this.content = content;
        this.author = author;
    }

    public Posts toEntity() {
        return Posts.builder()
            .title(title)
            .content(content)
            .author(author)
            .build();
    }
}
~~~
**PostsApiController**
~~~ java
@RequiredArgsConstructor
@RestController
public class PostsApiController {

    private final PostsService postsService;

    @PostMapping("/api/v1/posts")
    public Long savePosts(@RequestBody final PostsSaveRequestDto requestDto) {
        return postsService.save(requestDto);
    }
}
~~~
**PostsService**
~~~ java
@RequiredArgsConstructor
@Service
public class PostsService {

    private final PostsRepository postsRepository;

    @Transactional
    public Long save(final PostsSaveRequestDto requestDto) {
        return postsRepository.save(requestDto.toEntity())
            .getId();
    }
}
~~~
위와 같이 구현해 두었습니다.

# `@RequestBody` 바인딩을 어디서 하는지 알아보자

Request의 Body를 읽고 객체로 binding하는 과정을 알아보기 위해 아래와 같은 과정을 진행했습니다. `AbstractMessageConverterMethodArgumentResolver`의 `readWithMessageConverters` 메서드를 Breakpoint를 잡은 이유는 `@RequestBody`, `@ResponseBody`의 처리를 하는 `RequestResponseBodyMethodProcessor`에서 해당 메서드를 호출하기 때문입니다.
1. request의 body를 binding하는 과정을 알아보기 위해 아래와 같이 해당 메서드에 Breakpoint를 잡았습니다.
![image.png](https://images.velog.io/post-images/conatuseus/b2fd6520-3db4-11ea-9149-31a630ce5639/image.png)

2. Postman을 통해 아래와 같이 POST 요청을 해봅니다.
![image.png](https://images.velog.io/post-images/conatuseus/1760bf20-3db6-11ea-a4b8-eb6deb732db9/image.png)

3. 그리고 계속 진행하다보면 **messageConverter들 중에서 적합한 HttpMessageConverter를 찾아 body를 읽는 부분**이 있습니다.
messageConverters에 10개의 컨버터가 저장되어 있는데, 이 중 REQUEST의 ContentType으로 읽을 수 있는 컨버터를 찾고, 해당 컨버터로 body를 읽는 메서드를 호출해 저장하는 과정이 포함되어 있습니다.
우리는 JSON으로 요청으므로 **MappingJackson2HttpMessageConverter**가 Body를 읽을 것입니다.
![image.png](https://images.velog.io/post-images/conatuseus/69a7b710-3db7-11ea-a4b8-eb6deb732db9/image.png)

4. 이제 **해당 컨버터(MappingJackson2HttpMessageConverter)가 어떻게 읽는지 `read()` 메서드를 이해하면 궁금증 2개를 해결할 수 있다는 것**을 알 수 있습니다.
해당 메서드를 타고 들어가보면, Jackson 라이브러리의 ObjectMapper를 사용해 body를 읽는 것을 확인할 수 있습니다. (이렇게 코드를 파다보면 공부할 것이 너무 많아지네요😅)
![image.png](https://images.velog.io/post-images/conatuseus/57127420-3dba-11ea-a4b8-eb6deb732db9/image.png)

---
여기까지는 제가 **어디서 RequestBody를 바인딩하는지 찾고 해석했느냐**에 대한 내용이었습니다.

☛ 참고
Jackson 라이브러리는 `spring-boot-starter-web`에 포함되어 있습니다.

![image.png](https://images.velog.io/post-images/conatuseus/13319df0-3dc2-11ea-8cb9-370758fc36b2/image.png)

> `@RequestBody`에 대한 궁금증은 Jackson 라이브러리의 ObjectMapper가 어떻게 바인딩하는지 알아봐야한다는 결론이 나왔습니다. 따라서 이번 글에서는 여기까지만 적고, 다음 글에서는 ObjectMapper를 알아가보겠습니다.<file_sep>/암호학/README.md
# 대칭키 방식

- 대칭키(Symmetric Key) 암호화 방식은 **암호화와 복호화에 같은 암호 키(대칭키)를 사용하는 암호화 알고리즘**
- 장점
  - 암호화 및 복호화가 빠르다.

- 단점

  - 암호화한 정보를 보낼 때 암호키도 함께 보내야해서 분실 또는 노출 위험이 있다.

- 대칭키 암호 시스템

  - DES, AES 등

# 공개키 방식(=비대칭키 방식)

- 공개키(Public Key) 암호화 방식은, **암호화와 복호화에 사용하는 암호키를 분리한 방식**
- 자신이 가지고 있는 고유한 암호키(비밀키, Private Key)로만 복호화할 수 있는 암호키(공개키, Public Key)를 대중에 공개한다.
- 공개키 암호화 방식 시나리오
  1. A가 웹 상에 공개된 'B의 공개키'를 이용하여 평문을 암호화
  2. B는 자신의 비밀키로 복호화한 평문을 확인하고, A의 공개키로 응답을 암호화하여 A에게 보냄
  3. A는 자신의 비밀키로 암호화된 응답문을 복호화함
- 장점
  - 암호키를 주고받아야하는 대칭키 방식의 문제점을 해결
- 단점
  - 암복호화가 매우 복잡: 암호화하는 키와 복호화하는 키가 서로 다르기 때문
- 공개키 암호 시스템
  - RSA, ECC 등

![image](./images/public_key.png)

<file_sep>/Article/2019-12/12.md
# 2019-12-12 목요일

## [KT-소방청, 5G 기반 응급의료 서비스 개발](http://www.bloter.net/archives/364300)
- KT가 소방청, 세브란스병원과 함께 5G 기반 응급의료 서비스를 개발한다고 12월12일 밝혔다. 119 신고부터 구급차, 병원 응급실까지 이어지는 응급의료체계 전단계에 5G 및 IT 기술을 적용해 골든타임을 확보한다는 계획이다.
- 우선 KT와 소방청은 119 신고센터와 응급환자가 5G 및 무선통신 기반으로 영상통화를 할 수 있는 ‘5G 기반 119 영상통화 신고 서비스’를 12일 전국 상용화했다. 이를 통해 음성통화 위주의 신고로는 알 수 없었던 정확한 상황을 파악할 수 있게 될 것으로 기대된다.
- 또 KT는 세브란스 병원과 협업해 5G와 인공지능(AI) 기반 응급의료 시스템을 개발 중이다. 과학기술정보통신부(과기정통부)가 추진 중인 5G+ 전략의 일환으로, 구급차 내부에 5G 환경을 구축하고 360도 카메라와 실시간 현장 대응 카메라 등을 통해 구급대원과 의료진이 실시간으로 소통할 수 있도록 지원하는 게 목표다.
<file_sep>/Article/2019-12/19.md

# [한국은행, 디지털화폐 발행 검토](http://www.bloter.net/archives/364960)
<file_sep>/Spring/Annotation.md

# Spring

- `@EnableWebSecurity`

  Spring Security 설정들을 활성화시켜 줍니다.

## JPA

# Lombok

## `Data` 어노테이션

`Data` 어노테이션이 어떤 것들을 해 주는지 확인하기 위해 먼저 Document를 찾아봤다.

![image](https://user-images.githubusercontent.com/22893111/71266190-3ed82380-238b-11ea-905e-88ea3bb0e804.png)

 해석하자면, `@ToString`, `@EqualsAndHashCode`, `@Getter`, `@Setter,` `@RequiredArgsConstructor` 이 5개의 어노테이션  역할을 모두 해준다.

즉, toString, equals, hashCode, getter, setter 메서드를 자동생성 해주거나, (non-null이거나 final인 필드 값만 받는) 생성자를 만들어 준다.

처음에 이것을 보고 만능이라고 느꼈지만, 생각해보니 dto, vo 정도에서는 사용할 수 있겠지만 엔티티에서는 사용하면 안된다.

왜냐하면 엔티티에서는 Setter를 사용하지 않도록 배웠다. (의도 파악의 어려움, 객체의 일관성 등 때문)

그리고 엔티티에 `@ToString` 을 사용하게 되면 부모, 자식간 toString 메서드가 무한으로 호출될 가능성이 높다.

따라서 `@Data` 어노테이션 사용을 지양하도록 하자!
<file_sep>/Article/2020-04/13.md
# [영국, 애플·구글과 코로나19 감염 추적 모바일앱 개발 협력](http://www.bloter.net/archives/377610)

- 애플과 구글이 코로나19에 감염된 이들과 접촉한 사용자들에게 이 같은 사실을 스마트폰 알람 공지 형태로 알려주는 모바일 앱을 5월 선보일 예정인 가운데, 영국 의료 정책을 담당하는 국민보건서비스(NHS)도 애플, 구글과 협력하고 있다는 보도가 나왔다.
- 영국 정부는 선보일 스마트폰앱이 코로나19에 따른 봉쇄(Lock Down: 록다운) 조치를 끝내는데 핵심적인 역할을 할 것으로 기대하고 있다. 다음달 하순부터는 엄격한 사회적 거리두기 정책을 해제하기 시작하는 것이 가능해지기를 바라고 있다라고 <더선데이타임스>는 전했다.
- 애플과 구글이 힘을 합쳐 개발할 모바일 앱은 다음달 첫 버전이 공개될 예정이다. 이 앱은 근거리 무선 통신 기술인 블루투스를 활용해 특정 사용자가 코로나19 양성 판정을 받으면 지난 14일치 위치 데이터를 찾아 이 사람과 감염을 우려할 만한 수준으로 가깝게 접촉한 이들을 확인한 뒤 당사자에게 스마트폰 알람 형태로 알려준다.
- 이 같은 개념은 올초 싱가포르에서도 많이 활용됐고, 일부 전문가들로부터 코로나19로 사회적 거리를 유지하고 있는 이들이 일상으로 돌아갔을 때 만일의 사태를 막는 유용한 수단이 될 수 있다는 평가를 받고 있다.<file_sep>/Article/2019-11/23.md

# 2019-11-23 토요일

## [제 주인 찾아간 카카오뱅크](http://www.bloter.net/archives/362011)
- Summary
  + 카카오가 한국카카오은행(이하 카카오뱅크)의 최대주주가 됐다
  + 카카오뱅크의 최대주주가 된 카카오는 카카오뱅크가 보여준 혁신과 변화를 이어갈 수 있도록 기술 협력과 투자를 확대해 나간다. 카카오 공동체(계열사)의 다양한 플랫폼/서비스와 협력을 강화함으로써 카카오뱅크 상품 및 서비스 분야에서 소비자의 편익을 증대하고, 경쟁력을 높인다. 이를 통해 카카오톡을 통한 간편 송금, 모임 통장 초대 기능, 카카오 인공지능(AI) 기술을 활용한 카카오뱅크 상담 챗봇 등과 같은 카카오와 카카오뱅크의 협력 성공 사례를 지속해서 만들어 낼 계획

## [지금 OTT 플랫폼은 오리지널 콘텐츠 쟁탈전](http://www.bloter.net/archives/361951)
<file_sep>/회고/2019년 회고.md
# 20대의 마지막 회고
2019년이 끝난지 5일이 지났다. 이동욱님의 책을 따라해보는게 재밌어서 끝내느라 회고가 늦어졌다.
2019년에는 많은 것을 경험하고, 배운 의미있는 한 해였다.
20대 마지막을 마무리 짓고, 30대의 시작을 생각해보는 시간을 가져본다.

## [Baekjoon Online Judge](https://www.acmicpc.net/)의 친구 및 랭킹 API 개발

알고리즘만 잘하면 취업을 할 수 있을거라는 생각을 가지고 있었다. 그래서 알고리즘 문제만 매일매일 풀었다.
취업을 하려고 IT회사들의 공고를 살펴보니 내 생각이 틀렸다는 것을 뒤늦게 알았다. 안드로이드/IOS/프론트엔드/백엔드 개발자 등 수많은 직종이 있었지만 내가 지원할 수 있는 곳은 없었다. (알고리즘에 대한 배신감 +1)

그래서 **'나는 무슨 개발자를 하지?'** 라는 고민이 생겼다.
그 고민은 가장 친한 친구에게서 답을 얻을 수 있었다. 옛말에 "친구 따라 강남 간다"라는 말이 있듯이, 친구가 서버 개발자라서 만만한게 보고 나도 서버 개발자가 되야겠다고 마음 먹었다.

채용공고를 보니 10이면 9이 **Spring framework를 이용한 서버 개발 경험**이 필요했다.
![image.png](https://images.velog.io/post-images/conatuseus/e4334a10-2fa4-11ea-b9b9-8539e36b6f66/image.png)

서버가 뭔지, Spring framework는 또 뭔지 너무 막막했다.
친구에게 물어보고 답을 얻은 후, 그럼 일단 나도 스프링을 사용해서 뭘 만들어봐야겠구나 생각했다.당시 매일 친구들과 알고리즘 푼 문제 수를 비교하는 습관이 있었는데, 친구들의 푼 문제 수를 보려면 친구들 각각의 아이디를 검색해서 클릭을 몇 번씩 해야되는 불편함이 있었다. 그래서 **친구 기능과 친구들과의 랭킹 비교 기능**이 있는 서비스가 떠올라서 바로 시작했다.

프론트는 개발하지 못했지만, 우여곡절 끝에 스프링 프레임워크를 사용해서 해당 API들을 구현할 수 있었다. 엄청난 기능은 아니었지만, 뿌듯하고 정말 재밌었다. 이 경험을 통해 친구 때문이 아닌 내가 진짜 서버 개발자가 되고 싶다는 생각을 가졌다.

## 우아한테크코스
> 우아한 테크코스는 매 레벨마다 회고했으니 짧게 작성한다.

위 API를 구현하고, 나는 아직 공부할 것이 많구나라는 생각을 가질 때 우아한테크코스 백엔드 과정 교육생 모집하는 것을 발견했다. 이것은 인생 절호의 기회라 생각한 나는 바로 지원했고, 코딩테스트 및 프리코스를 진행 후 우아한테크코스의 크루가 되었다.

우아한테크코스의 캡틴인 포비와 코치님들 곁에서 엄청난 교육을 들을 수 있었다. TDD, Spring framework, 읽기 좋은 코드, 프레임워크 만들기 등...
페어 프로그래밍, 팀 프로젝트를 진행하며 얻는 것도 많았다. 협업을 처음해본 나로써는 함께하는 재미도 있었고, 같이 문제를 해결해가는 것, 서로 의견 충돌시 해결하는 것 등...

반면에 아쉬운 점도 있었다. 교육 중 우아한형제들 채용과정을 진행 했고, 인생 첫 면접이었던 나는 멘탈이 부셔졌다. "내가 백엔드 개발자가 되는게 맞는걸까?" 나의 길이 아닌 것 같다는 생각까지 들었다. 그리고 다른 회사의 채용과정까지 진행됬는데 서류에서 탈락하다보니 절망을 느꼈다. 그래서 프로젝트 기간동안 집중하지 못했고, 팀원들에게 폐를 끼친 것 같다.
그래도 이런 경험을 통해 멘탈이 조금 더 강화된 것 같다.

## 기록들...

![image.png](https://images.velog.io/post-images/conatuseus/b3792bf0-2faa-11ea-9fe9-4b8953092a7b/image.png)

2019년 11월 14일부터, 1일 1커밋을 시작했다. 매주 주말에 놀고있는 내모습을 발견하고, 이를 개선하고자 하는 것이 이유다. 1일 1커밋을 진행하면서 조금이라도, 한 가지라도 공부하는 습관이 들여졌다.
1일 1커밋을 위한 commit을 하는 상황도 아주 가끔 생긴다. 이것은 앞으로 내가 조금 더 생각해보고 해결해나가야 할 방안인 것 같다.

8월부터 velog에도 비공개까지 포함해서 20개 정도의 글을 작성했다. 5개월간 20개 정도니까 한 달에 4개의 글을 작성하는 것 같다.
현재는 책을 읽으며 작성한 글이 8~90% 정도인데, 앞으로는 개발하면서 만난 문제의 해결 등 **팩트와 나의 의견**이 들어간 글을 주로 작성하는게 목표다.

## 졸업
2019년 8월, 대학교를 졸업에 성공(?)했다. 입학한지 8년하고도 6개월만에 졸업하니 속이 후련하다.
비록 우아한테크코스 교육에 집중하기 위해 졸업식은 가지 않았지만, 많은 주변 사람들의 축하를 받아 감사하다. 이 글을 읽으시는 분이 대학생이라면... 1학년때부터 열심히 하세요. (놀면 저처럼 8년 넘어서 졸업하십니다.)

## 2020년의 목표
- **서버 개발자로 취업하기**
: 하루 빨리 좋은 곳에서 좋은 사람들과 개발하고 싶다.
- **개발자 정기 모임 주기적으로 참여하기**
: 오프라인 개발자 모임에 참여해보고 싶다. 많은 것을 배울 수 있고, 성장할 수 있는 기회가 될 것 같다.
- **건강 챙기기**
: 이제 건강을 더 챙길 나이가 된 것 같다. 운동을 시작하자
- **하나의 프로젝트를 꾸준히 운영해보기**
: 여태 프로젝트는 개발을 다 하면 끝이었다. 하나의 프로젝트를 꾸준히 운영해보자
- **기본기 탄탄히 다지기**
: CS 지식을 다시 공부하면서 탄탄히 다지자
<file_sep>/Spring/@RequestBody는 왜 기본생성자가 필요하며, Setter는 필요없을까? #2.md
> [이전 글](https://velog.io/@conatuseus/RequestBody%EC%97%90-%EA%B8%B0%EB%B3%B8-%EC%83%9D%EC%84%B1%EC%9E%90%EB%8A%94-%EC%99%9C-%ED%95%84%EC%9A%94%ED%95%9C%EA%B0%80)에서는 어떻게 `@RequestBody`를 처리하는지를 알아보기 위한 **과정**을 설명했습니다. 이번 글에서는 `@RequestBody`를 바인딩하는 ObjectMapper에 대해 알아보고, 결론을 짓겠습니다.
> 참고로 아래 사진들에서 현재 위치(class)는 다음과 같이 찾으시면 됩니다. 첫번째 사진을 예로 들어, `AbstractJackson2HttpMessageConverter > readJavaType()` 에서 AbstractJackson2HttpMessageConver가 현재 클래스 이름이며, readJavaType()은 메서드명입니다.

# ObjectMapper란?
**Jackson의 Document에서 ObjectMapper**를 보면 다음과 같이 설명되어 있습니다.

> _"ObjectMapper는 기본 POJO(Plain Old Java Objects) 또는 범용 JSON Tree Model(JsonNode)에서 JSON을 읽고 쓰는 기능과 변환 수행을 위한 기능을 제공합니다.
> 또한 다양한 스타일의 JSON 컨텐츠와 함께 작동하고 다형성 및 객체 동일성과 같은 고급 객체 개념을 지원하도록 Customizing할 수 있습니다."_

쉽게 말해, **Jackson의 ObjectMapper는 Java Object ←→ JSON 파싱을 쉽게 해주는 클래스** 입니다.
ObjectMapper 클래스를 살펴보면, `serialize`와 `deserialize`라는 단어가 포함된 메서드가 많이 있습니다.
Json을 Java 오브젝트로 파싱하는 것을 **직렬화(serialize)**라고 하며, 반대는 **역직렬화(deserialize)**라고 합니다.

# ObjectMapper가 `@RequestBody`를 binding하는 과정 살펴보기

지난 글에서 아래 그림과 같이 AbstractJackson2HttpMessageConverter가 `objectMapper`의 `readValue()` 메서드를 호출한다는 것까지 확인했습니다.

![image.png](https://images.velog.io/post-images/conatuseus/d426c4b0-3ed8-11ea-8609-9b6fc89aa201/image.png)

---
이제 `objectMapper.readValue()` 메서드 내부를 한번 살펴보겠습니다.
![image.png](https://images.velog.io/post-images/conatuseus/98c73550-3ee0-11ea-a6b8-eb179f450c1c/image.png)

objectMapper의 `readValue()`내에서는 **Request의 Body가 null인지만 확인**하고, 위 사진과 같이 **`_readMapAndClose()` 메서드를 호출**합니다.
이 `_readMapAndClose()` 메서드는 여러가지 작업들을 하는데, **핵심은 JsonDeserializer를 찾고 해당 JsonDeserializer로 deserialize(역직렬화)를 해서 Object를 반환해준다는 것**입니다.
위 코드에서는 `deser`가 찾은 deserializer이고, 해당 deserializer로 역직렬화하는 메서드를 호출하는 것이 빨간색 부분입니다. `PostsSaveRequestDto`는 Bean이므로 찾아진 deserializer는 BeanDeserializer로 보여집니다.

---
이제 deser.deserialize() 내부로 들어가봅시다.

![image.png](https://images.velog.io/post-images/conatuseus/677e9d60-3ee7-11ea-8557-e73ccea7dc99/image.png)

먼저 `p.isExpectedStartObjectToken()` 메서드는 Json이 "{"로 시작하는지 확인하는 메서드입니다. 그리고 `_vanillaProcessing`가 맞는지 확인하는데, `_vanillaProcessing`의 주석을 보니
"*특수 기능이 전혀 없음을 나타내는 플래그로 가장 간단한 처리가 가능합니다.*" 라고 적혀있습니다.
다음에는 `_objectIdReader`가 null이 아닌지 확인하는데, `_objectIdReader`는 deserializer가 처리하는 값에 Object Id를 사용하려는 경우에 사용됩니다. 현재는 Object Id를 사용하지 않으므로 `if(_objectIdReader !=null)` 내부로 들어가지 않습니다.

---
`deserializeFromObject()` 메서드 내부를 들어가봅시다.

![image.png](https://images.velog.io/post-images/conatuseus/58bec1e0-3ee9-11ea-a9b9-25498ef943d9/image.png)

**위 그림의 코드 위에는 조건문과 처리과정**이 있지만, 조건문 내부를 들어가지 않으므로 생략하겠습니다.
이제 대망의 **핵심부분**입니다. 바로 Object를 생성하고, 필드를 넣어주는 부분에 도착했습니다. 하지만 여기서도 모두 메서드를 호출하기 때문에 봐야할 것들이 많이 남았습니다...😂

---
먼저 Object를 생성하는 메서드인 `createUsingDefault()`를 살펴봅시다. 메서드명만 봐도 default 생성자로 생성하겠다는 느낌이 확 옵니다.

![image.png](https://images.velog.io/post-images/conatuseus/4e32c180-3eea-11ea-95e4-0f27f07d39d7/image.png)

먼저 `_defaultCreator` 가 null인지 확인합니다. 여기서 defaultCreator란 기본 생성자를 말합니다(default constructor, @NoArgsConstructor)

### 기본생성자가 Null이면, 즉 기본 생성자가 없으면 왜 안되는지 여기서 파악할 수 있을 것이라 생각했습니다.
만약 default생성자가 없었다면, `_defaultCreator == null`을 만족했을 것이고 `super.createUsingDefault()` 메서드가 호출될 것입니다.
현재는 `@NoArgsConstructor` 어노테이션을 DTO에 작성했기 때문에 위의 조건에 만족하지 않았을 것입니다.

_defaultCreator.call() 내부를 알아보기 전에, `@NoArgsConstructor`를 지운 후에 super.createUsingDefault() 메서드가 호출되는지 확인해보았습니다.

---
### 하지만 createUsingDefault() 메서드가 호출되기 전에 문제가 발생했다.
당연히 `createUsingDefault()` 메서드 내부에서 예외가 발생할 것이라고 생각했다. 왜냐하면 해당 메서드 내에 defaultCreator가 null인지 확인하는 부분이 존재했기 때문이다.

`@NoArgsConstructor` 어노테이션을 지우고, if(_defaultCreator == null)에 breakpoint를 찍고 디버깅을 돌렸는데... **해당 breakPoint에서 디버깅이 걸리지 않았습니다**...😡
이전에 **조건문에 걸리지 않는다고 생략했던 곳**에서 걸린 것이었습니다. 아래는 deserializeFromObject() 메서드의 생략된 윗부분 코드입니다.

![image.png](https://images.velog.io/post-images/conatuseus/23ab34b0-3eef-11ea-a9b9-25498ef943d9/image.png)

`@NoArgsConstructor`을 삭제하고 디버깅 한 결과 위 사진의 빨간색 부분에서 문제가 발생했습니다.
default생성자가 없으므로 _nonStandardCreation 조건을 만족합니다. 따라서 `deserializeFromObjectUsingNonDefault` 메서드를 호출해서 Object를 가져옵니다. 메서드명만 봐도 default생성자를 사용하지 않고 Object로 deserialize한다는 것을 알 수 있습니다.

![](https://images.velog.io/images/conatuseus/post/171eff75-ea77-4101-bb3a-dfb777ec4399/image.png)

그 메서드의 내부를 보면 **delegateSerializer를 사용하도록** 되어 있습니다. 하지만 저의 DTO는 delegate를 하지 않아서 delegateSerializer를 못 가져오기 때문에 오류가 발생한 것입니다.(property도 설정해준 것이 아니라서)

### 결국 default 생성자가 없어서 오류가 생기는 것은 맞다.
하지만, default 생성자가 없더라도 property 기반 클래스(property 관련 어노테이션이 적용된)면 필요 없을 것입니다. 또는 delegate를 한다면.

---

다시 돌아와서, `_defaultCreator.call()`를 들어가보면 다음과 같이 인스턴스를 생성하는 것을 알 수 있습니다.

![](https://images.velog.io/images/conatuseus/post/a9d53705-bd65-407b-a5d2-a4f6cde5001a/image.png)

![image.png](https://images.velog.io/post-images/conatuseus/a2f05dc0-3ef6-11ea-b2aa-9ff9b912552a/image.png)

## 결론
이번 글에서는 `@RequestBody`에 왜 기본 생성자(`@NoArgsConstructor`)가 필요없는지 알아봤습니다.
제가 이번 글을 작성하면서 내린 결론은 다음과 같습니다.
**1. 일반적인 상황에서는 기본 생성자가 꼭 필요하다.**
**2. 왜냐하면, RestController에서 @RequestBody를 바인딩을 하기 위해 ObjectMapper를 사용하는데 기본 생성자로 DTO를 생성하기 때문이다.**
**3. 하지만 `@JsonProperty`, `@JsonAutoDetect` 등을 사용한 Property 기반 클래스이거나, 생성자가 위임된 경우라면 필요하지 않다.**

**Object를 생성하는 것까지 확인했으니 이제 주입하는 과정을 보면
"`@Setter`는 왜 필요 없는지" 에 대해 알 수 있을 것 같습니다. 다음 글에서는 해당 내용을 알아보겠습니다.**

> 이번 글을 작성하면서 코드를 이해하는데 시간이 많이 걸렸고, 코드를 파면 팔수록 모르는 것들이 많이 나와서 힘들었지만 재밌고 유익했습니다.
> 잘못된 정보가 있다거나, 이해가 안되는 부분이 있다면 댓글 남겨주시면 감사하겠습니다.
> 읽어주셔서 감사합니다.

- 참고: https://fasterxml.github.io/jackson-databind/javadoc/2.7/com/fasterxml/jackson/databind/ObjectMapper.html<file_sep>/Article/README.md
# BLOTER에서 읽은 것을 기록하는 공간

## list

- [2019.11](https://github.com/Conatuseus/TIL/tree/master/Article/2019-11)
- [2019.12](https://github.com/Conatuseus/TIL/tree/master/Article/2019-12)

<file_sep>/Article/2020-03/3.md
# 2020-03-03

## [자율주행차 뛰어든 카카오모빌리티, “빠르면 올해 시범서비스”](http://www.bloter.net/archives/373091)

- 카카오모빌리티가 국토교통부로부터 자율주행차 임시운행허가를 받았다고 3월3일 밝혔다. 모빌리티 데이터를 기반으로 한 기술 연구를 넘어 이용자 실생활에 자율주행 기반 서비스를 접목하기 위한 본격 채비에 나서겠다는 포부다.
- 카카오모빌리티는 국토교통부의 임시운행허가에 따라 이르면 3월 중순부터 레벨4에 해당하는 자율주행차 기술 테스트에 본격 돌입할 예정이다. 자율주행 레벨 4는 차량 주행 시 운전자의 개입 없이 차량이 스스로 경로를 설정하고 안전하게 운행을 하는 단계다. 운전자가 반드시 탑승을 해야 하는 점에서 완전 자율주행 단계인 레벨 5와는 구분되며, 지정된 구역에서만 운행이 가능하다.
- 카카오모빌리티의 자율주행 임시운행은 연구와 실험 단계를 넘어 2400만명이 가입한 카카오T 플랫폼을 통해 자율주행 기반의 모빌리티 서비스를 바로 접목시킨다는 점에서 차별점을 가지고 있다. 카카오T에서 호출, 탑승, 이동, 하차, 결제까지 이뤄지는 자율주행 기반의 모빌리티 서비스를 실생활에 가장 빠르게 접목해 상용화할 수 있기 때문이다.
- 카카오모빌리티는 자율주행 기반 서비스 상용화에 핵심이 되는 ▲도로, 차량 및 보행자, 신호등과 같은 주행 환경에 대한 ‘인지 기술’ ▲ 인지 결과를 토대로 목적지에 도달하기 위한 차로 유지, 차선 변경, 갓길 정차 등 주행 방법을 결정하는 ‘판단 기술’ ▲조향, 가속, 감속 등 정교한 ‘차량 제어 기술’ 등을 검증할 예정이다. 여기에 ▲차량과 승객을 효율적으로 연결하는 ‘AI 기반 배차 알고리즘’ ▲지도상의 차량 위치 추정, 차선 단위의 길안내 등을 통해 최적의 경로를 제공하는 ‘내비게이션 기술’ ▲차량 위치 정확도를 높이는 ‘측위 기술’을 접목함으로써 자율주행 플랫픔 기술을 더욱 빠르게 고도화해, 연내 특정 지역에 한해 시범서비스를 진행한다는 계획이다.

<file_sep>/Books/HTTP완벽가이드/10장.HTTP:2.0.md

# 10장. HTTP/2.0

## 10.1 HTTP/2.0의 등장 배경

HTTP/1.1의 메시지 포맷은 구현의 단순성과 접근성에 주안점을 두고 최적화되었다. 그러다 보니 성능은 어느 정도 희생시키지 않을 수 없었다. 커넥션 하나를 통해 요청 하나를 보내고 그에 대해 응답 하나만을 받는 HTTP의 메시지 교환 방식은 단순함 면에서는 더할 나위 없었지만, 응답을 받아야만 그다음 요청을 보낼 수 있기 때문에 심각한 회전 지연(latency)을 피할 수 없었다. 이 문제를 회피하기 위해 병렬 커넥션이나 파이프라인 커넥션이 도입되었지만 성능 개선에 대한 근본적인 해결책은 되지 못했다.

2009년, 구글은 웹을 더 빠르게 하겠다는 목표 아래 SPDY 프로토콜을 내놓았다. SPDY는 헤더를 압축하여 대역폭을 절약했고, 하나의 TCP 커넥션에 여러 요청을 동시에 보내 회전 지연을 줄이는 것이 가능했으며, 클라이언트가 요청을 보내지 않아도 서버가 능동적으로 리소스를 푸시하는 기능도 갖추고 있다.

마침내 2012년 10월, HTTP 작업 그룹은 SPDY의 초안을 그대로 가져와서 HTTP/2.0 초안을 만들기 시작했다.

2013년 11월, HTTP/2.0은 여덟 번째 초안까지 나와 있으며, 아직까지는 SPDY의 특징들을 거의 그대로 유지하고 있다. 크게 변경된 점이라면 헤더를 압축할 때 더이상 deflate 알고리즘을 사용하지 않게 되었다는 것 정도다.

## 10.2 개요

HTTP/2.0은 서버와 클라이언트 사이의 TCP 커넥션 위에서 동작한다. 이때 TCP 커넥션을 초기화하는 것은 클라이언트다.

HTTP/2.0 요청과 응답은 길이가 정의된 한 개 이상의 프레임에 담긴다. 이때 HTTP 헤더는 압축되어 담긴다.

프레임들에 담긴 요청과 응답은 스트림을 통해 보내진다. 한 개의 스트림이 한 쌍의 요청과 응답을 처리한다. 하나의 커넥션 위에 여러 개의 스트림이 동시에 만들어질 수 있으므로, 여러 개의 요청과 응답을 동시에 처리하는 것 역시 가능하다.

HTTP/2.0은 이들 스트림에 대한 흐름 제어와 우선순위 부여 기능도 제공한다.

HTTP/2.0은 기존의 요청-응답과는 약간 다른 새로운 상호작용 모델인 서버 푸시를 도입했다. 이를 통해 서버는 클라이언트에게 필요하다고 생각하는 리소스라면 그에 대한 요청을 명시적으로 받지 않더라도 능동적으로 클라이언트에게 보내줄 수 있다.

기존 웹 애플리케이션들과 호환성을 최대한 유지하기 위해, HTTP/2.0은 요청과 응답 메시지의 의미를 HTTP/1.1과 같도록 유지하고 있다. HTTP/1.1에서와 마찬가지로 HTTP/2.0에서도 Content-Length 헤더는 본문의 길이를 의미하며, 404 Not Found 응답은 리소스를 찾을 수 없음을 의미한다. 다만 이를 표현하는 문법은 변경되었다. 예를 들어, Content-Length 헤더 이름은 ':content-length'가 되었으며, 상태줄을 통해 표현하던 404 Not Found는 '404' 값을 갖고 있는 ':status' 헤더로 표현하게 되었다.

## 10.3 HTTP/1.1과의 차이점

### 10.3.1 프레임

HTTP/2.0에서 모든 메시지는 프레임에 담겨 전송된다. 아래 그림과 같이 모든 프레임은 8바이트 크기의 헤더로 시작하며, 뒤이어 최대 16383바이트 크기의 페이로드가 온다.

![image](https://user-images.githubusercontent.com/22893111/76612700-2d548100-6560-11ea-8493-c281df9a9369.png)

프레임 헤더의 각 필드는 다음과 같다.

- R: 예약된 2비트 필드. 값의 의미가 정의되어 있지 않으며, 반드시 0이어야 한다. 받는 쪽에서는 이 값을 무시해야 한다.
- 길이: 페이로드의 길이를 나타내는 14비트 무부호 정수(unsigned integer). 이 길이에 프레임 헤더는 포함되지 않는다.
- 종류: 프레임 종류
- 플래그: 8비트 플래그. 플래그 값의 의미는 프레임의 종류에 따라 다르다.
- R: 예약된 1비트 필드. 첫 번째 R과 마찬가지로 값의 의미가 정의되어 있지 않으며, 반드시 0이어야 한다. 받는 쪽에서는 이 값을 무시해야 한다.
- 스트림 식별자: 31비트 스트림 식별자. 특별히 0은 커넥션 전체와 연관된 프레임임을 의미한다.

### 10.3.2 스트림과 멀티플렉싱

스트림은 HTTP/2.0 커넥션을 통해 클라이언트와 서버 사이에서 교환되는 프레임들의 독립된 양방향 시퀀스다.

한 쌍의 HTTP 요청과 응답은 하나의 스트림을 통해 이루어진다. 클라이언트는 새 스트림을 만들어 그를 통해 HTTP 요청을 보낸다. 요청을 받은 서버는 그 요청과 같은 스트림으로 응답을 보낸다. 그러고 나면 스트림이 닫히게 된다.

HTTP/1.1에서는 한 TCP 커넥션을 통해 요청을 보냈을 때, 그에 대한 응답이 도착하고 나서야 같은 TCP 커넥션으로 다시 요청을 보낼 수 있다. 따라서 웹브라우저들은 회전 지연을 줄이기 위해 여러 개의 TCP 커넥션을 만들어 동시에 여러 개의 요청을 보내는 방법을 사용한다. 그러나 그렇다고 TCP 커넥션을 무한정 만들 수는 없기에 한 페이지에 보내야 할 요청이 수십에서 수백에 달하는 오늘날에는 회전 지연이 늘어나는 것을 피하기 어렵다. 파이프라인 커넥션을 통해 이것을 피할 수는 있으나 그다지 널리 구현되어 있지 않다.

그러나 HTTP/2.0에서는 하나의 커넥션에 여러 개의 스트림이 동시에 열릴 수 있다. 따라서 하나의 HTTP/2.0 커넥션을 통해 여러 개의 요청이 동시에 보내질 수 있기 때문에 이 문제는 쉽게 해결될 수 있다.

뿐만 아니라 스트림은 우선순위도 가질 수 있다. 예를 들어 사용자가 웹브라우저로 어떤 웹페이지를 보려고 할 때, 네트워크 대역폭이 충분하지 않아 프레임의 전송이 느리다면, 웹브라우저는 보다 중요한 리소스를 요청하는 스트림에게 더 높은 우선순위를 부여할 수 있을 것이다.

 <file_sep>/JPA/JPA프로그래밍/영속성컨텍스트_2.md
# 영속성 컨텍스트의 특징
- **영속성 컨텍스트와 식별자 값**
영속성 컨텍스트는 엔티티를 식별자 값(@Id로 테이블의 기본 키와 매핑한 값)으로 구분한다.
따라서 **영속 상태는 식별자 값이 반드시 있어야 한다!**
- **영속성 컨텍스트와 데이터베이스 저장**
이전 포스팅에서 말했듯이 JPA는 보통 트랜잭션을 커밋하는 순간 영속성 컨텍스트에 새로 저장된 엔티티를 데이터베이스에 반영한다. 이를 **플러시(flush)** 라고 한다.
- **영속성 컨텍스트가 엔티티를 관리하면 얻게되는 장점**
  + 1차 캐시
  + 동일성(identity) 보장
  + 트랜잭션을 지원하는 쓰기 지연(transactional write-behind)
  + 변경 감지(Dirty Checking)
  + 지연 로딩(Lazy Loading)

  ---
## 엔티티 조회, 1차 캐시

![image.png](https://images.velog.io/post-images/conatuseus/5f4d8c20-d08b-11e9-9f4f-632c92c19a43/image.png)
~~~ java
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");

em.persist(member);
~~~
위 코드에서 em.persist(member) 을 실행하게 되면 member 엔티티가 영속 상태가 된다.
그리고 위 그림에서 보이는 것과 같이 그 엔티티는 영속성 컨텍스트의 1차 캐시에 저장된다.
> 1차 캐시란?
영속성 컨텍스트는 내부에 캐시를 가지고 있는데 이것을 **1차 캐시**라 한다. 영속 상태의 엔티티는 모두 이곳에 저장된다. 쉽게 말해 영속성 컨텍스트 내부에 Map이 하나 있는데 (1차 캐시), 키는 @Id로 매핑한 식별자고 값은 엔티티 인스턴스다.

### 1차 캐시가 존재해서 얻는 이점
#### 1차 캐시에서 조회
~~~ java
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");

//1차 캐시에 저장
em.persist(member);

//1차 캐시에서 조회
Member findMember = em.find(Member.class, "member1");
~~~
엔티티를 조회하는 find 메서드가 실행되면
1. 1차 캐시에서 식별자 값으로 엔티티를 찾는다.
2. 만약 찾는 엔티티가 1차 캐시에 있으면 데이터베이스를 조회하지 않고 메모리에 있는 1차 캐시에서 엔티티를 조회
3. 1차 캐시에 찾는 엔티티가 없으면 데이터베이스에서 조회

아래 그림은 위 코드를 실행했을 때 일어나는 상황

![image.png](https://images.velog.io/post-images/conatuseus/996d15e0-d08d-11e9-9f4f-632c92c19a43/image.png)

#### 데이터베이스에서 조회

`em.find()`를 호출했는데 엔티티가 1차 캐시에 없으면 엔티티 매니저는 데이터베이스를 조회해서 엔티티를 생성한다. 그리고 1차 캐시에 저장한 후에 영속 상태의 엔티티를 반환한다.

![image.png](https://images.velog.io/post-images/conatuseus/d39dcef0-d095-11e9-a3f7-3fdb1399ac5a/image.png)

위 그림을 분석해보자!
1. `em.find(Member.class, "member2")`를 실행한다
2. 하지만 1차 캐시에는 member1만 있고, member2는 없는 상태다. 그래서 데이터베이스에서 member2를 조회한다
3. 조회한 데이터로 member2 엔티티를 생성해서 1차 캐시에 저장한다(영속 상태)
4. 조회한 엔티티를 반환한다
위 과정 후 member1나, member2에 대해 find를 요청하면 **데이터베이스에 접근하지 않고, 1차 캐시에 있는 엔티티를 반환할 것이다.**
1차 캐시로 인해 데이터베이스에 접근할 빈도가 줄어듦으로 성능상 이점을 얻을 수 있다.
> 1차 캐시는 한 트랜잭션 안에서만 효과가 있기 때문에 성능상 이점이 크지는 않다.

#### 영속 엔티티의 동일성 보장
~~~ java
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");

System.out.println(a == b);
~~~
현재 member1이 영속성 컨텍스트에 존재하는 상황에서
위 코드의 a == b 가 참일까 거짓일까?
정답은 **참**이다.

member1을 두 번 find해서 찾은 객체인데 왜 같을까?
영속성 컨텍스트는 1차 캐시에 있는 같은 엔티티 인스턴스를 반환하기 때문에 참이다!
따라서 **영속성 컨텍스트는 성능상 이점과 엔티티의 동일성을 보장한다**

만약 영속성 컨텍스트에 member1이 없는데 위 코드를 실행한다면 결과가 같을까?
그렇다.
처음 member1에 대한 find 요청 시 엔티티 매니저는 데이터베이스에서 member1을 조회해서 1차 캐시에 저장하고 반환한다. 두번째 find 요청시에는 영속성 컨텍스트가 1차 캐시에 있는 member1 엔티티 인스턴스를 반환하므로 같은 결과가 된다.

---
## 엔티티 등록
### 트랜잭션을 지원하는 쓰기 지연
~~~ java
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
// 엔티티 매니저는 데이터 변경 시 트랜잭션을 시작해야 한다
transaction.begin(); // 트랜잭션 시작

em.persist(memberA);
em.persist(memberB);
// 여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.

transaction.commit(); // 트랜잭션 커밋
~~~
 위는 엔티티 매니저를 사용해서 엔티티를 영속성 컨텍스트에 등록하는 코드다.
엔티티 매니저는 트랜잭션을 커밋하기 직전까지 데이터베이스에 엔티티를 저장하지 않고 내부 쿼리 저장소에 INSERT SQL을 모아둔다. 그리고 트랜잭션을 커밋할 때 모아둔 쿼리를 데이터베이스에 보내서 저장시킨다.
이를 트랜잭션을 지원하는 쓰기 지연이라 한다.

코드가 실행되는 과정을 그림으로 알아보자!

![image.png](https://images.velog.io/post-images/conatuseus/d4a2fb30-d09b-11e9-a657-a958e5af4073/image.png)

 위는 em.persist(memberA)가 실행되면 일어나는 일에 대한 그림이다.
 em.persist(memberA)가 실행되면, 영속성 컨텍스트는 1차 캐시에 memberA에 대한 엔티티를 저장하면서 동시에 JPA가 이 entity를 분석해서 쓰기 지연 SQL 저장소에 INSERT 쿼리를 저장한다.
 (DB에 아직 INSERT 쿼리를 보내지 않는다!!)

![image.png](https://images.velog.io/post-images/conatuseus/51c8cae0-d09c-11e9-b275-49c1db32880d/image.png)

 위는 em.persist(memberB)가 실행되면 일어나는 일에 대한 그림이다.
 em.persist(memberB)가 실행되면, memberA와 같은 작업이 실행된다.
 (DB에 아직 INSERT 쿼리를 보내지 않는다!!)

 그럼 쓰기 지연 SQL 저장소에 저장된 쿼리들이 DB에 날라가는 시점은 언제인가?
그 시점은 코드에서 보이는 트랜잭션을 커밋하는 순간이다.

![image.png](https://images.velog.io/post-images/conatuseus/eb6c9c30-d09c-11e9-b0db-1597a34a142f/image.png)

 트랜잭션을 commit하면, 엔티티 매니저는 영속성 컨텍스트를 플러시(flush) 한다. 플러시는 영속성 컨텍스트의 변경 내용을 데이터베이스에 동기화하는 작업인데 이때 등록, 수정, 삭제한 엔티티를 데이터베이스에 반영한다.

 ---
 ## 엔티티 수정
 ### SQL 수정 쿼리의 문제점
 SQL을 사용하면 수정 쿼리를 직접 작성해야 한다. 그런데 프로젝트가 점점 커지고 요구사항이 늘어나면서 수정 쿼리도 점점 추가된다.
  이런 방식의 문제점은 수정 쿼리가 많아지는 것은 물론이고 비지니스 로직을 분석하기 위해 SQL을 계속 확인해야 한다. 결국 직접적이든 간접적이든 비지니스 로직이 SQL에 의존하게 된다.

 ### 변경 감지
~~~ java
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin() // 트랜잭션 시작

// 영속 엔티티 조회
Member memberA = em.find(Member.class, "memberA");

// 영속 엔티티 데이터 수정
memberA.setUsername("hi");
memberA.setAge(10);

transaction.commit(); // 트랜잭션 커밋
~~~

 위는 JPA의 엔티티 수정 코드다.
`em.update()` 같은 메서드가 없이 memberA에 대한 setter로만 변경했는데 어떻게 데이터베이스의 엔티티가 수정되는 걸까?
그 이유는 JPA가 **변경 감지(dirty checking)** 기능이 있기 때문이다.
변경 감지는 엔티티의 변경사항을 데이터베이스에 자동으로 반영하는 기능이다.

![image.png](https://images.velog.io/post-images/conatuseus/b5d57200-d0a0-11e9-90a8-3bdc8e61daef/image.png)

 위 그림을 단계별로 살펴보자.
 (JPA는 엔티티를 영속성 컨텍스트에 보관할 때, 최초 상태를 복사해서 저장해두는데 이것을 **스냅샷**이라 한다)
 1. 트랜잭션을 커밋하면 엔티티 매니저 내부에서 먼저 플러시(flush())가 호출된다.
 2. 엔티티와 스냅샷을 비교해서 변경된 엔티티를 찾는다.
 3. 변경된 엔티티가 있으면 수정 쿼리를 생성해서 쓰기 지연 SQL 저장소에 보낸다.
 4. 쓰기 지연 저장소의 SQL을 데이터베이스에 보낸다.
 5. 데이터베이스 트랜잭션을 커밋한다.

 > **변경 감지**는 영속성 컨텍스트가 관리하는 영속 상태의 엔티티에만 적용된다
 비영속, 준영속처럼 영속성 컨텍스트의 관리를 받지 못하는 엔티티는 값을 변경해도 데이터베이스에 반영되지 않는다

그럼 변경 감지로 인해 실행된 UPDATE SQL의 쿼리는 변경된 부분만 수정 쿼리가 생성될까?

아니다.
JPA의 기본 전략은 엔티티의 모든 필드를 업데이트 한다. 모든 필드를 업데이트하면 데이터베이스에 보내는 데이터 전송량이 증가하는 단점이 있지만, 다음과 같은 장점으로 인해 모든 필드를 업데이트 한다.
- 모든 필드를 사용하면 수정 쿼리가 항상 같다. 따라서 애플리케이션 로딩 시점에 수정 쿼리를 미리 생성해두고 재사용할 수 있다.
- 데이터베이스에 동일한 쿼리를 보내면 데이터베이스는 이전에 한 번 파싱된 쿼리를 재사용할 수 있다.

---
## 엔티티 삭제
~~~ java
Member memberA = em.find(Member.class, "memberA");
em.remove(memberA);
~~~
 em.remove()에 삭제 대상 엔티티를 넘겨주면 엔티티를 삭제한다.
물론 엔티티를 즉시 삭제하는 것이 아니라, 엔티티 등록과 비슷하게 삭제 쿼리를 쓰기 지연 SQL 저장소에 등록한다.
이후 트랜잭션을 커밋해서 플러시를 호출하면 실제 데이터베이스에 삭제 쿼리를 전달한다.
참고: em.remove(memberA)를 호출하는 순간 memberA는 영속성 컨텍스트에서 제거된다.

---

# 플러시
플러시(`flush()`)는 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영한다.
플러시를 실행하면 다음과 같은 일이 일어난다.
 1. 변경 감지가 동작해서 영속성 컨텍스트에 있는 모든 엔티티를 스냅샷과 비교해서 수정된 엔티티를 찾는다.
 2. 수정된 엔티티는 수정 쿼리를 만들어 쓰기 지연 SQL 저장소에 등록한다.
 3. 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송한다.(등록, 수정, 삭제 쿼리)

 ---

## 영속성 컨텍스트를 플러시 하는 3가지 방법
- **em.flush() - 직접 호출**
  엔티티 매니저의 flush() 메서드를 직접 호출해서 영속성 컨텍스트를 강제로 플러시한다.

- **트랜잭션 커밋 - 플러시 자동 호출**
  데이터베이스에 변경 내용을 SQL로 전달하지 않고 트랜잭션만 커밋하면 어떤 데이터도 데이터베이스에 반영되지 않는다. 따라서 트랜잭션을 커밋하기 전에 꼭 플러시를 호출해서 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영해야 한다. JPA는 이런 문제를 예방하기 위해 트랜잭션을 커밋할 때 플러시를 자동으로 호출한다.
- **JPQL 쿼리 실행 - 플러시 자동 호출**
  JPQL이나 Criteria 같은 객체지향 쿼리를 호출할 때도 플러시가 실행된다.

---

## 플러시 모드 옵션
- **FlushModeType.AUTO**: 커밋이나 쿼리를 실행할 때 플러시(기본값)
- **FlushModeType.COMMIT**: 커밋할 때만 플러시
  JPQL 쿼리 실행시 플러시가 자동으로 일어나는데, 만약 JPQL에서 사용할 테이블이 이전 작업들과 전혀 관련이 없다면 이 옵션으로 플러시를 커밋할 때로 변경할 수 있다.

## 플러시는!
- **영속성 컨텍스트를 비우지는 않음!!**
- 영속성 컨텍스트의 변경내용을 데이터베이스에 동기화
- 트랜잭션이라는 작업 단위가 중요 -> 커밋 직전에만 동기화하면 됨

---

# 준영속 상태
 영속성 컨텍스트가 관리하는 영속 상태의 엔티티가 영속성 컨텍스트에서 분리된(detached) 것을 **준영속 상태**라 한다. 따라서 **준영속 상태의 엔티티는 영속성 컨텍스트가 제공하는 기능을 사용할 수 없다.**
준영속 상태로 만드는 3가지 방법
- em.detach(entity): 특정 엔티티만 준영속 상태로 전환
- em.clear(): 영속성 컨텍스트를 완전히 초기화
- em.close(): 영속성 컨텍스트를 종료

각각에 대해 자세히 알아보자
## 엔티티를 준영속 상태로 전환: detach()
~~~ java
Member member = new Member();
member.setId("memberA");
member.setUsername("회원A");

// member 엔티티를 영속 상태로
em.persist(member);

// member 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태
em.detach(member);

transaction.commit();
~~~
위 코드를 실행하면 데이터베이스에 member가 저장될까?
아니다. member 엔티티를 영속 상태로 만들고 다시 deach()를 통해 준영속 상태로 전환시켰기 때문에 영속성 컨텍스트가 지원하는 어떤 기능도 동작하지 않는다. (쓰기 지연 SQL 저장소의 INSERT SQL도 제거됨)

## 영속성 컨텍스트 초기화: clear()
~~~ java
// 엔티티 조회, 영속 상태
Member member = em.find(Member.class, "memberA");

em.clear();  // 영속성 컨텍스트 초기화

// 준영속 상태
member.setUsername("changeName");
~~~
위 코드를 실행하면 데이터베이스의 memberA 이름이 바뀔까?
아니다. clear을 통해 영속성 컨텍스트를 초기화 했으므로 member.setUsername 메서드를 실행할 때는 영속성 컨텍스트가 지원하는 변경 감지가 동작하지 않는다.

## 영속성 컨텍스트 종료: close()
 영속성 컨텍스트를 종료하면 해당 영속성 컨텍스트가 관리하던 영속 상태의 엔티티가 모두 준영속 상태가 된다.

<file_sep>/JPA/JPA_Auditing.md

 현재 진행중인 프로젝트에서 **해당 데이터의 생성시간과 수정시간을 관리해야 할 부분이 있었습니다.**
예를 들어, 주문 도메인에서 주문한 시간과 주문 내용을 수정한 시간이 필요했습니다.
그리고 결제 도메인에서는 결제 요청한 시간과 결제 내용을 수정한 시간이 필요했습니다.

현재 개발 초기 단계이지만 벌써 두 곳에서 **생성시간/수정시간**이 필요했습니다.
언제 만들어졌는지, 언제 수정되었는지 등은 차후 더 필요해질 것이라 생각 생각했습니다.

 여기저기 찾아보다가 **<스프링 부트와 AWS로 혼자 구현하는 웹서비스 - 이동욱님>** 책에 해당 내용이 있었습니다. 이제 그 내용을 읽고 적용한 것을 같이 공유하고자 합니다.

 # JPA Auditing으로 생성시간/수정시간 자동화하기

  보통 엔티티는 해당 데이터의 생성시간과 수정시간을 포함합니다.
그렇다 보니 매번 DB에 insert하기 전, update하기 전에 날짜 데이터를 등록/수정하는 코드가 여기저기 들어갑니다. 아래와 같이 말이죠.

~~~ java
// 생성일 추가 코드 예제
public void savePosts() {
  ...
  posts.setCreateDate(new LocalDate());
  postsRepository.save(posts);
  ...
}
~~~

이런 단순하고 반복적인 코드가 모든 테이블과 서비스 메서드에 포함되어야 한다고 생각하면 어마어마하게 귀찮고 코드가 지저분해집니다. 그래서 이 문제를 해결할 수 있는 것이 JPA Auditing입니다.

# 적용하기

~~~ java
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseTimeEntity {

    @CreatedDate
    private LocalDateTime createdDate;

    @LastModifiedDate
    private LocalDateTime modifiedDate;

    public LocalDateTime getCreatedDate() {
        return createdDate;
    }

    public LocalDateTime getModifiedDate() {
        return modifiedDate;
    }
}
~~~
 먼저 **BaseTimeEntity** 클래스를 만듭니다.
 이 클래스는 모든 Entity의 상위 클래스가 되어 **Entity 들의 createdDate, modifiedDate를 자동으로 관리하는 역할**입니다.

- `@MappedSuperclass` : JPA Entity 클래스들이 BaseTimeEntity를 상속할 경우 필드들(createdDate, modifiedDate)도 칼럼으로 인식하도록 합니다.
- `@EntityListeners(AuditingEntityListener.class)`: BaseTimeEntiy 클래스에 Auditing 기능을 포함시킵니다.
- `@CreatedDate`: Entity가 생성되어 저장될 때 시간이 자동 저장됩니다.
- `@LastModifiedDate`: 조회한 Entity의 값을 변경할 때 시간이 자동 저장됩니다.

그리고 저는 Order 클래스가 BaseTimeEntity를 상속하도록 했습니다.
현재 개발 초기 단계라서 다른 데이터 없이 id, 생성시간, 수정시간만 가지도록 했습니다.

~~~ java
@Entity
@Table(name = "ORDERS")
public class Order extends BaseTimeEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    public Order() {
    }
}
~~~

마지막으로 JPA Auditing 어노테이션들을 모두 활성화할 수 있도록 Application 클래스에 활성화 어노테이션을 추가합니다.

~~~ java
@EnableJpaAuditing
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(CaffeineApplication.class, args);
    }
}
~~~

- `@EnableJpaAuditing`: JPA Auditing을 활성화 하기 위한 어노테이션입니다.

# 테스트 코드 작성해서 확인 해보기

~~~ java
@SpringBootTest
public class OrderTest {

    @Autowired
    OrderRepository orderRepository;

    @Test
    public void BaseTimeEntity_등록() {
        //given
        LocalDateTime now = LocalDateTime.of(2019, 12, 6, 0, 0, 0);
        orderRepository.save(new Order());

        //when
        List<Order> orders = orderRepository.findAll();

        //then
        Order order = orders.get(0);

        System.out.println(">>>>> createDate = "+order.getCreatedDate() + ", modifiedDate = "+order.getModifiedDate());

        assertThat(order.getCreatedDate()).isAfter(now);
        assertThat(order.getModifiedDate()).isAfter(now);
    }
}
~~~

테스트는 다음과 같이 잘 통과하는 것을 확인할 수 있습니다.
![image.png](https://images.velog.io/post-images/conatuseus/047f1120-182e-11ea-88bb-b9fccacb343d/image.png)

 앞으로 추가될 엔티티들은 더이상 등록일/수정일로 고민할 필요 없이 `BaseTimeEntity`만 상속받으면 자동으로 해결됩니다.

<file_sep>/Books/HTTP완벽가이드/4장.커넥션 관리.md
# 4장 커넥션 관리

이 장에서 배울 내용은 다음과 같다

- HTTP는 어떻게 TCP 커넥션을 사용하는가
- TCP 커넥션의 지연, 병목, 막힘
- 병렬 커넥션, keep-alive 커넥션, 커넥션 파이프라인을 활용한 HTTP의 최적화
- 커넥션 관리를 위해 따라야 할 규칙들

## 4.1 TCP 커넥션

전 세계 모든 HTTP 통신은, 지구상의 컴퓨터와 네트워크 장비에서 널리 쓰이고 있는 패킷 교환 네트워크 프로토콜들의 계층화된 집합인 TCP/IP를 통해 이루어진다. 세계 어디서든 클라이언트 애플리케이션은 서버 애플리케이션으로 TCP/IP 커넥션을 맺을 수 있다. 커넥션이 맺어지면 클라이언트와 서버 컴퓨터 간에 주고받는 메시지들은 손실 혹은 손상되거나 순서가 바뀌지 않고 안전하게 전달된다.

죠의 컴퓨터 가게에서 전동공ㅇ구의 최신 가격 목록을 가져온다고 해보자.

> http://www.joes-hardware.com:80/power-tools.html

이 URL을 입력받은 브라우저는 아래 그림과 같은 단계를 수행한다. 1~3단계에서 URL을 통해 서버의 IP 주소와 포트 번호를 가져온다. 4단계에서 웹 서버가 TCP 커넥션을 맺고 5단계에서는 그 커넥션을 통해서 요청 메시지가 전달된다. 6단계에서 응답을 읽고 7단계에서 커넥션이 끊어진다.

![image](https://user-images.githubusercontent.com/22893111/72061509-616db480-3319-11ea-9ee5-71a851559e5e.png)

### 4.1.1 신뢰할 수 있는 데이터 전송 통로인 TCP

HTTP 커넥션은 몇몇 사용 규칙을 제외하고는 TCP 커넥션에 불과하다. TCP 커넥션은 인터넷을 안정적으로 연결해준다.

TCP는 HTTP에게 신뢰할 만한 통신 방법을 제공한다. TCP 커넥션의 한쪽에 있는 바이트들은 반대쪽으로 순서에 맞게 정확히 전달된다.

### 4.1.2 TCP 스트림은 세그먼트로 나뉘어 IP 패킷을 통해 전송된다

TCP는 IP 패킷이라고 불리는 작은 조각을 통해 데이터를 전송한다. HTTP는 아래 그림과 같이 'IP, TCP, HTTP'로 구성된 '프로토콜 스택'에서 최상위 계층이다. HTTP에 보안 기능을 더한 HTTPS는 TLS 혹은 SSL이라 불리기도 하며 HTTP와 TCP 사이에 있는 암호화 계층이다. (아래 그림은 HTTP와 HTTPS 네트워크 프로토콜 스택)

![image](https://user-images.githubusercontent.com/22893111/72062036-59624480-331a-11ea-960b-8c59c4f80024.png)

HTTP가 메시지를 전송하고자 할 경우, 현재 연결되어 있는 TCP 커넥션을 통해서 메시지 데이터의 내용을 순서대로 보낸다. TCP는 세그먼트라는 단위로 데이터 스트림을 잘게 나누고, 세그먼트를 IP 패킷이라고 불리는 봉투에 담아서 인터넷을 통해 데이터를 전달한다. 이 모든 것은 TCP/IP 소프트웨어에 의해 처리된다.

 각 TCP 세그먼트는 하나의 IP 주소에서 다른 IP 주소로 IP 패킷에 담겨 전달된다. 이 IP 패킷들 각각은 다음을 포함한다.

- IP 패킷 헤더
- TCP 세그먼트
- TCP 데이터 조각

IP 헤더는 발신지와 목적지 IP 주소, 크기, 기타 플래그를 가진다. TCP 세그먼트 헤더는 TCP 포트 번호, TCP 제어 플래그, 그리고 데이터의 순서와 무결성을 검사하기 위해 사용되는 숫자 값을 포함한다.

![image](https://user-images.githubusercontent.com/22893111/72062606-764b4780-331b-11ea-9a35-d32b3dc87570.png)

### 4.1.3 TCP 커넥션 유지하기

컴퓨터는 항상 TCP 커넥션을 여러 개 가지고 있다. TCP는 포트 번호를 통해서 이런 여러 개의 커넥션을 유지한다.

포트 번호는 회사 직원의 내선전화와 같다. 회사의 대표 전화번호는 안내 데스크로 연결되고 내선전화는 해당 직원으로 연결되듯이 IP 주소는 해당 컴퓨터에 연결되고 포트 번호는 해당 애플리케이션으로 연결된다. TCP 커넥션은 네 가지 값으로 식별한다.

<발신지 IP주소, 발신지 포트, 수신지 IP주소, 수신지 포트>

이 네 가지 값으로 유일한 커넥션을 생성한다. 서로 다른 두 개의 TCP 커넥션은 네 가지 주소 구성요소 값이 모두 같을 수 없다.

### 4.1.4 TCP 소켓 프로그래밍

운영체제는 TCP 커넥션의 생성과 관련된 여러 기능을 제공한다. 아래 표는 소켓 API의 주요 인터페이스를 보여준다. 이 소켓 API는 HTTP 프로그래머에게 TCP와 IP의 세부사항들을 숨긴다. 소켓 API는 유닉스 운영체제용으로 먼저 개발되었지만, 지금은 소켓 API의 다양한 구현체들 덕분에 대부분의 운영체제와 프로그램 언어에서 이를 사용할 수 있게 되었다.

| 소켓 API 호출                | 설명                                                       |
| ---------------------------- | ---------------------------------------------------------- |
| s = socket(<parameters>)     | 연결이 되지 않은 익명의 새로운 소켓 생성                   |
| bind(s, <local IP:port>)     | 소켓에 로컬 포트 번호와 인터페이스 할당                    |
| connect(s, <remote IP:port>) | 로컬의 소켓과 원격의 호스트 및 포트 사이에 TCP 커넥션 생성 |
| listen(s, ...)               | 커넥션을 받아들이기 위해 로컬 소켓에 허용함을 표시         |
| s2 = accept(s)               | 누군가 로컬 포트에 커넥션을 맺기를 기다림                  |
| n = read(s, buffer, n)       | 소켓으로부터 버퍼에 n바이트 읽기 시도                      |
| n = write(s, buffer, n)      | 소켓으로부터 버퍼에 n바이트 쓰기 시조                      |
| close(s)                     | TCP 커넥션을 완전히 끊음                                   |
| shutdown(s, <side>)          | TCP 커넥션의 입출력만 닫음                                 |

아래 그림은 웹브라우저가 HTTP를 이용하여 죠의 컴퓨터 가게에서 power-tools.html을 내려받는 방법을 보여준다.

![image](https://user-images.githubusercontent.com/22893111/72063779-cdeab280-331d-11ea-9fdb-0b2d5f1fb417.png)

## 4.2 TCP의 성능에 대한 고려

HTTP는 TCP 바로 위에 있는 계층이기 떄문에 HTTP 트랜잭션의 성능은 그 아래 계층인 TCP 성능에 영향을 받는다. 이 절에서는 TCP 커넥션의 성능에 관련된 주요 고려사항을 집중적으로 다룬다.

### 4.2.1 HTTP 트랜잭션 지연

아래 그림은 HTTP의 주요 커넥션, 전송, 처리의 지연을 보여준다.

![image](https://user-images.githubusercontent.com/22893111/72137269-db5f7580-33cd-11ea-9670-bb8c129d8efe.png)

트랜잭션을 처리하는 시간은 TCP 커넥션을 설정하고, 요청을 전송하고, 응답 메시지를 보내는 것에 비하면 상당히 짧다는 것을 알 수 있다. 클리아언트나 서버가 너무 많은 데이터를 내려받거나 복잡하고 동적인 자원들을 실행하지 않는 한, 대부분의 HTTP 지연은 TCP 네트워크 지연 때문에 발생한다.

HTTP 트랜잭션을 지연시키는 원인은 여러 가지가 있다.

1. 클라이언트는 URI에서 웹 서버의 IP주소와 포트 번호를 알아내야 하는데, 해당 호스트에 방문한 적이 없으면 시간이 더 걸릴 것이다.
2. 클라이언트는 TCP 커넥션 요청을 서버에게 보내고 서버가 커넥션 허가 응답을 회신하기를 기다린다.
3. 커넥션이 맺어지면 클라이언트는 HTTP 요청을 새로 생성된 TCP 파이프를 통해 전송한다. 웹 서버는 데이터가 도착하는 데로 TCP 커넥션에서 요청 메시지를 읽고 처리한다. 요청 메시지가 인터넷을 통해 전달되고 서버에 의해서 처리되는 데 까지는 시간이 소요된다.
4. 웹 서버가 HTTP 응답을 보내는 것 역시 시간이 소요된다.

이런 TCP 네트워크 지연은 하드웨어의 성능, 네트워크와 서버의 전송 속도, 요청과 응답 메시지의 크기, 클라이언트와 서버 간의 거리에 따라 크게 달라진다. 또한 TCP 프로토콜의 기술적인 복잡성도 지연에 큰 영향을 끼친다.

### 4.2.2 성능 관련 중요 요소

여기서부터는 다음에 대해 알아볼 것이다.

- TCP 커넥션의 핸드셰이크 설정
- 인터넷의 혼잡을 제어하기 위한 TCP의 느린 시작(slow-start)
- 데이터를 한데 모아 한 번에 전송하기 위한 네이글(nagle) 알고리즘
- TCP의 편승(piggyback) 확인 응답(acknowledgment)을 위한 확인응답 지연 알고리즘
- TIME_WAIT 지연과 포트 고갈

### 4.2.3 TCP 커넥션 핸드셰이크 지연

 어떤 데이터를 전송하든 새로운 TCP 커넥션을 열 때면, TCP 소프트웨어는 커넥션을 맺기 위한 조건을 맞추기 위해 연속으로 IP 패킷을 교환한다. 작은 크기의 데이터 전송에 커넥션이 사용된다면 이런 패킷 교환은 HTTP 성능을 크게 저하시킬 수 있다.

![image](https://user-images.githubusercontent.com/22893111/72203274-bd1c7700-34ac-11ea-8cbd-7be2af022e79.png)

TCP 커넥션이 핸드셰이크를 하는 순서

1. 클라이언트는 새로운 TCP 커넥션을 생성하기 위해 작은 TCP 패킷을 서버에게 보낸다. 그 패킷은 'SYN'라는 특별한 프래그를 가지는데, 이 요청이 커넥션 생성 요청이라는 뜻이다. (a)
2. 서버가 그 커넥션을 받으면 몇 가지 커넥션 매개변수를 산출하고, 커넥션 요청이 받아들여졌음을 의미하는 'SYN'과 'ACK' 플래그를 포함한 TCP 패킷을 클라이언트에게 보낸다. (b)
3. 마지막으로 클라이언트는 커넥션이 잘 맺어졌음을 알리기 위해서 서버에게 다시 확인응답 신호를 보낸다. 오늘날의 TCP는 클라이언트가 이 확인응답 패킷과 함께 데이터를 보낼 수 있다. (c)

 HTTP 트랜잭션이 아주 큰 데이터를 주고받지 않는 평범한 경우에는, SYN/SYN+ACK 핸드셰이크(그림에서 a,b)가 눈에 띄는 지연을 발생시킨다. TCP의 ACK 패킷(c)은 HTTP 요청 메시지 전체를 전달할 수 있을 만큼 큰 경우가 많고, 많은 HTTP 서버 응답 메시지는 하나의 IP 패킷에도 담길 수 있다.

 결국, 크기가 작은 HTTP 트랜잭션은 50% 이상의 시간을 TCP를 구성하는 데 쓴다. 이후 절에서는 이러한 TCP 구성으로 인한 지연을 제거하기 위해서 HTTP가 이미 존재하는 커넥션을 어떻게 재활용하는지 알아볼 것이다.

### 4.2.4 확인응답 지연

인터넷 자체가 패킷 전송을 완벽히 보장하지는 않기 때문에, TCP는 성공적인 데이터 전송을 보장하기 위해서 자체적인 확인 체계를 가진다.

 각 TCP 세그먼트는 순번과 데이터 무결성 체크섬을 가진다. 각 세그먼트의 수신자는 세그먼트를 온전히 받으면 작은 확인응답 패킷을 송신자에게 반환한다. 만약 송신자가 특정 시간 안에 확인응답 메시지를 받지 못하면 패킷이 파기되었거나 오류가 있는 것으로 판단하고 데이터를 다시 전송한다.

 확인응답은 그 크기가 작기 때문에, TCP는 같은 방향으로 송출되는 데이터 패킷에 확인응답을 '편승(piggyback)' 시킨다. TCP는 송출 데이터 패킷과 확인응답을 하나로 묶음으로써 네트워크를 좀 더 효율적으로 사용한다. 확인응답이 같은 방향으로 가는 데이터 패킷에 편승되는 경우를 위해서, 많은 TCP 스택은 '확인응답 지연' 알고리즘을 구현한다. 확인응답 지연은 송출할 확인응답을 특정 시간동안 버퍼에 저장해 두고, 확인응답을 편승시키기 위한 송출 데이터 패킷을 찾는다. 만약 일정 시간 안에 송출 데이터 패킷을 찾지 못하면 확인응답은 별도 패킷을 만들어 전송된다.

하지만 요청과 응답 두 가지 형식으로만 이루어지는 HTTP 동작 방식은, 확인 응답이 송출 데이터 패킷에 편승할 기회를 감소시킨다. 따라서 확인응답 지연 알고리즘으로 인한 지연이 자주 발생한다. 운영체제에 따라 다르지만, 지연의 원이이 되는 확인응답 지연 관련 기능을 수정하거나 비활성화할 수 있다.

### 4.2.5 TCP 느린 시작(slow start)

TCP의 데이터 전송 속도는 TCP 커넥션이 만들어진 지 얼마나 지났는지에 따라 달라질 수 있다. TCP 커넥션은 시간이 지나면서 자체적으로 '튜닝'되어서, 처음에는 커넥션의 최대 속도를 제한하고 데이터가 성공적으로 전송됨에 따라서 속도 제한을 높여나간다. 이렇게 조율하는 것을 TCP 느린 시작이라고 부르며, 인터넷의 급작스러운 부하와 혼잡을 방지하는 데 쓰인다.

 TCP 느린 시작은 TCP가 한 번에 전송할 수 있는 패킷의 수를 제한한다. 간단히 말해 패킷이 성공적으로 전달되는 각 시점에 송신자는 추가로 2개의 패킷을 더 전송할 수 있는 권한을 얻는다. 이를 '혼잡 윈도를 연다(opening the congestion window)'라고 한다.

 이 혼잡제어 기능 때문에, 새로운 커넥션은 이미 어느 정도 데이터르르 주고받은 '튜닝'된 커넥션보다 느리다. '튜닝'된 커넥션은 더 빠르기 때문에, HTTP에는 이미 존재하는 커넥션을 재사용하는 기능이 있다. 이 장의 뒤에서 HTTP의 "지속 커넥션"을 다룰 것이다.

### 4.2.6 네이글(Nagle) 알고리즘과 TCP_NODELAY

애플리케이션이 어떤 크기의 데이터든지 TCP 스택으로 전송할 수 있도록, TCP는 데이터 스트림 인터페이스를 제공한다. 하지만 각 TCP 세그먼트는 40바이트 상당의 플래그와 헤더를 포함하여 전송하기 때문에, TCP가 작은 크기의 데이터를 포함한 많은 수의 패킷을 전송한다면 네트워크 성능은 크게 떨어진다.

네이글 알고리즘은 네트워크 효율을 위해서, 패킷을 전송하기 전에 많은 양의 TCP 데이터를 한 개의 덩어리로 합친다. 네이글 알고리즘은 세그먼트가 최대 크기가 되지 않으면 전송을 하지 않는다. 다만 다른 모든 패킷이 확인응답을 받았을 경우에는 최대 크기보다 작은 패킷의 전송을 허락한다. 다른 패킷들이 아직 전송 중이면 데이터는 버퍼에 저장된다. 전송되고 나서 확인응답을 기다리던 패킷이 확인응답을 받았거나 전송하기 충분할 만큼의 패킷이 쌓였을 때 버퍼에 저장되어 있던 데이터가 전송된다.

 네이글 알고리즘은 HTTP 성능 관련해 여러 문제를 발생시킨다.

1. 크기가 작은 HTTP 메시지는 패킷을 채우지 못하기 때문에, 앞으로 생길지 생기지 않을지 모르는 추가적인 데이터를 기다리며 지연될 것이다.
2. 네이글 알고리즘은 확인응답 지연과 함께 쓰일 경우 형편없이 동작한다. 네이글 알고리즘은 확인응답이 도착할 때까지 데이터의 전송을 멈추고 있는 반면, 확인응답 지연 알고리즘은 확인응답을 100~200밀리초 지연시킨다.

HTTP 애플리케이션은 성능 향상을 위해서 HTTP 스택에 TCP_NODELAY 파라미터 값을 설정하여 네이글 알고리즘을 비활성화하기도 한다. 이 설정을 했다면, 작은 크기의 패킷이 너무 많이 생기지 않도록 큰 크기의 데이터 덩어리를 만들어야 한다.

### 4.2.7 TIME_WAIT의 누적과 포트 고갈

TIME_WAIT 포트 고갈은 성능 측정 시에 심각한 성능 저하를 발생시키지만, 보통 실제 상황에서는 문제를 발생시키지 않는다.

TCP 커넥션의 종단에서 TCP 커넥션을 끊으면, 종단에서는 커넥션의 IP 주소와 포트 번호를 메모리의 작은 제어영역(control block)에 기록해 놓는다. 이 정보는 같은 주소와 포트 번호를 사용하는 새로운 TCP 커넥션이 일정 시간 동안에는 생성되지 않게 하기 위한 것으로, 보통 세그먼트의 최대 생명주기에 두 배 정도(보통 2분)의 시간 동안만 유지된다. 이는 이전 커넥션과 관련된 패킷이 그 커넥션과 같은 주소와 포트 번호를 가지는 새로운 커넥션에 삽입되는 문제를 방지한다. 실제로 이 알고리즘은 특정 커넥션이 생성되고 닫힌 다음, 그와 같은 IP 주소와 포트 번호를 가지는 커넥션이 2분 이내에 또 생성되는 것을 막아준다.

 클라이언트가 서버에 접속할 때마다, 유일한 커넥션을 생성하기 위해 새로운 발신지 포트를 쓴다. 하지만 사용할 수 있는 발신지 포트의 수는 제한되어 있고 2MSL초 동안 커넥션이 재사용될 수 없으므로,  초당 500개로 커넥션이 제한된다. 서버가 초당 500개 이상의 트랜잭션을 처리할 만큼 빠르지 않다면 TIME_WAIT 포트 고갈은 일어나지 않는다. 이 문제를 해결하기 위해 부하를 생성하는 장비를 더 많이 사용하거나 클라이언트와 서버가 더 많은 커넥션을 맺을 수 있도록 여러 개의 가상 IP 주소를 쓸 수도 있다.

## 4.3 HTTP 커넥션 관리

### 4.3.1 흔히 잘못 이해하는 Connection 헤더

HTTP는 클라이언트와 서버 사이에 프록시 서버, 캐시 서버 등과 같은 중개 서버가 놓이는 것을 허락한다. HTTP 메시지는 클라이언트에서 서버까지 중개 서버들을 하나하나 거치면서 전달 된다.

어떤 경우에는, 두 개의 인접한 HTTP 애플리케이션이 현재 맺고 있는 커넥션에만 적용될 옵션을 지정해야 할 때가 있다. HTTP Connection 헤더 필드는 커넥션 토큰을 쉼표로 구분하여 가지고 있으며, 그 값들은 다른 커넥션에 전달되지 않는다. 예를 들어, 다음 메시지를 보낸 다음 끊어져야 할 커넥션은 `Connection: close` 라고 명시할 수 있다.

Connection 헤더에는 다음 세 가지 종류의 토큰이 전달될 수 있기 때문에 혼란스러울 수 있다.

- HTTP 헤더 필드 명은, 이 커넥션에만 해당되는 헤더들을 나열한다.
- 임시적인 토큰 값은, 커넥션에 대한 비표준 옵션을 의미한다.
- close 값은, 커넥션이 작업이 완료되면 종료되어야 함을 의미한다.

커넥션 토큰이 HTTP 헤더 필드 명을 가지고 있으면, 해당 필드들은 현재 커넥션만을 위한 정보이므로 다음 커넥션에 전달하면 안 된다. Connection 헤더에 있는 모든 헤더 필드는 메시지를 다른 곳으로 전달하는 시점에 삭제되어야 한다. Connection 헤더는 홉별(hop-by-hop) 헤더 명을 기술하는데, 이것을 '헤더 보호하기'라 한다. Connection 헤더에 명시된 헤더들이 전달되는 것을 방지하기 때문이다.

HTTP 애플리케이션이 Connection 헤더와 함께 메시지를 전달 받으면, 수신자는 송신자에게서 온 요청에 기술되어 있는 모든 옵션을 적용한다. 그리고 다음 홉(hop)이 메시지를 전달하기 전에 Connection 헤더와 Connection 헤더에 기술되어 있던 모든 헤더를 삭제한다.

### 4.3.2 순차적인 트랜잭션 처리에 의한 지연

커넥션 관리가 제대로 이루어지지 않으면 TCP 성능이 매우 안 좋아질 수 있다. 예를 들어 3개의 이미지가 있는 웹페이지가 있다고 해보자. 브라우저가 이 페이지를 보여주려면 네 개의 HTTP 트랜잭션을 만들어야 한다. 하나는 해당 HTML을 받기 위해, 나머지 세 개는 이미지를 받기 위한 것이다. 각 트랜잭션이 새로운 커넥션을 필요로 한다면, 커넥션을 맺는데 발생하는 지연과 함께 느린 시작 지연이 발생할 것이다.

![image](https://user-images.githubusercontent.com/22893111/72679106-4c4e0e00-3aef-11ea-814f-be78511c73f3.png)

HTTP 커넥션의 성능을 향상시킬 수 있는 여러 최신 기술이 있다.

- 병렬(parallel) 커넥션: 여러 개의 TCP 커넥션을 통한 동시 HTTP 요청
- 지속(persistent) 커넥션: 커넥션을 맺고 끊는 데서 발생하는 지연을 제거하기 위한 TCP 커넥션의 재활용
- 파이프라인(pipelined) 커넥션: 공유 TCP 커넥션을 통한 병렬 HTTP 요청
- 다중(multiplexed) 커넥션: 요청과 응답들에 대한 중재

## 4.4 병렬 커넥션

HTTP는 클라이언트가 여러 개의 커넥션을 맺음으로써 여러 개의 HTTP 트랜잭션을 병렬로 처리할 수 있게 한다.

### 4.4.1 병렬 커넥션은 페이지를 더 빠르게 내려 받는다

단일 커넥션의 대역폭 제한과 커넥션이 동작하지 않고 있는 시간을 활용하면, 객체가 여러 개 있는 웹페이지를 더 빠르게 내려받을 수 있을 것이다. 하나의 커넥션으로 객체들을 로드할 때의 대역폭 제한과 대기 시간을 줄일 수 있다면 더 빠르게 로드할 수 있을 것이다. 각 커넥션의 지연 시간을 겹치게 하면 총 지연 시간을 줄일 수 있고, 클라이언트의 인터넷 대역폭을 한 개의 커넥션이 다 써버리는 것이 아니라면 나머지 객체를 내려받는 데에 남은 대역폭을 사용할 수 있다.

### 4.4.2 병렬 커넥션이 항상 더 빠르지는 않다

클라이언트의 네트워크 대역폭이 좁을 때는 대부분 시간을 데이터를 전송하는 데만 쓸 것이다. 여러 개의 객체를 병렬로 내려받는 경우, 이 제한된 대역폭 내에서 각 객체를 전송받는 것은 느리기 때문에 성능상의 장점은 거의 없어진다.

또한 다수의 커넥션은 메모리를 많이 소모하고 자체적인 성능 문제를 발생시킨다. 복잡한 웹페이지는 수십 개에서 수 백 개의 객체를 포함한다. 클라이언트가 수백 개의 커넥션을 열 수도 있겠지만, 서버는 다른 여러 새용자의 요청도 함께 처리해야 하기 때문에 수백 개의 커넥션을 허용하는 경우는 드물다.

브라우저는 실제로 병렬 커넥션을 사용하긴 하지만 적은 수(6~8개)의 병렬 커넥션만을 허용한다. 서버는 특정 클라이언트로부터 과도한 수의 커넥션이 맺어졌을 경우, 그것을 임의로 끊어버릴 수 있다.

### 4.4.3 병렬 커넥션은 더 빠르게 '느껴질 수' 있다

병렬 커넥션이 페이지를 항상 더 빠르게 로드하지는 않는다. 병렬 커넥션이 실제로 페이지를 더 빠르게 내려받는 것은 아니지만, 화면에 여러 개의 객체가 동시에 보이면서 내려받고 있는 상황을 볼 수 있기 때문에 사용자들은 더 빠르게 내려받고 있는 것처럼 느낄 수 있다.

## 4.5 지속 커넥션

웹 클라이언트는 보통 같은 사이트에 여러 커넥션을 맺는다. 예를 들어 웹페이지에 첨부된 이미지들 대부분은 같은 웹 사이트에 있고, 상당수의 하이퍼링크도 같은 사이트를 가리킨다. 따라서 서버에 HTTP 요청을 하기 시작한 애플리케이션은 웹페이지 내의 이미지 등을 가져오기 위해서 그 서버에 또 요청하게 될 것이다. 이 속성을 사이트 지역성(site locality)라 부른다.

 따라서 HTTP/1.1을 지원하는 기기는 처리가 완료된 후에도 TCP 커넥션을 유지하여 앞으로 있을 HTTP 요청에 재사용할 수 있다. 처리가 완료된 후에도 계속 연결된 상태로 있는 TCP 커넥션을 지속 커넥션이라고 부른다. 비지속 커넥션은 각 처리가 끝날 때마다 커넥션을 끊지만, 지속 커넥션은 클라이언트나 서버가 커넥션을 끊기 전까지는 트랜잭션 간에도 커넥션을 유지한다.

해당 서버에 이미 맺어져 있는 지속 커넥션을 재사용함으로써, 커넥션을 맺기 위한 준비작업에 따르는 시간을 절약할 수 있다. 게다가 이미 맺어져 있는 커넥션은 TCP의 느린 시작으로 인한 지연을 피함으로써 더 빠르게 데이터를 전송할 수 있다.

### 4.5.1 지속 커넥션 vs 병렬 커넥션

병렬 커넥션은 여러 객체가 있는 페이지를 더 빠르게 전송한다. 하지만 병렬 커넥션은 다음과 같은 몇 가지 단점이 있다.

- 각 트랜잭션마다 새로운 커넥션을 맺고 끊기 때문에 시간과 대역폭이 소요된다.
- 각각의 새로운 커넥션은 TCP 느린 시작 때문에 성능이 떨어진다.
- 실제로 연결할 수 있는 병렬 커넥션의 수에는 제한이 있다.

지속 커넥션은 병렬 커넥션에 비해 몇 가지 장점이 있다. 커넥션을 맺기 위한 사전 작업과 지연을 줄여주고, 튜닝된 커넥션을 유지하며, 커넥션의 수를 줄여준다. 하지만 지속 커넥션을 잘못 관리할 경우, 계속 연결된 상태로 있는 수많은 커넥션이 쌓이게 될 것이다. 이는 로컬의 리소스 그리고 원격의 클라이언트와 서버의 리소스에 불필요한 소모를 발생시킨다.

두 가지 지속 커넥션 타입이 있는데, HTTP/1.0+에서는 'keep-alive' 커넥션이 있고 HTTP/1.1에는 '지속' 커넥션이 있다.

### 4.5.2 HTTP/1.0+의 Keep-Alive 커넥션

keep-alive 커넥션의 성능상의 장점은 아래 그림에서 볼 수 있다. 같은 네 개의 HTTP 트랜잭션에 대해서, 연속적으로 네 개의 커넥션을 생성하여 처리하는 방식과 하나의 지속 커넥션으로만 처리하는 방식을 비교하였다. 커넥션을 맺고 끊는 데 필요한 작업이 없어서 시간이 단축되었다.

![image](https://user-images.githubusercontent.com/22893111/72875919-a7267600-3d38-11ea-9f6f-8d988a5f8214.png)

### 4.5.3 Keep-Alive 동작

keep-alive 커넥션을 구현한 클라이언트는 커넥션을 유지하기 위해서 요청에 Coneection: Keep-Alive 헤더를 포함시킨다. 이 요청을 받은 서버는 그다음 요청도 이 커넥션을 통해 받고자 한다면, 응답 메시지에 같은 헤더를 포함시켜 응답한다. 응답에 Coneection: Keep-Alive 헤더가 없으면, 클라이언트는 서버가 keep-alive를 지원하지 않으며, 응답 메시지가 전송되고 나면 서버 커넥션을 끊을 것이라 추정한다.

![image-20200122171218836](/Users/conatuseus/Library/Application Support/typora-user-images/image-20200122171218836.png)

### 4.5.4 Keep-Alive 옵션

Keep-Alive 헤더는 커넥션을 유지하기를 바라는 요청일 뿐이다. 클라이언트나 서버가 keep-avlie 요청을 받았다고 해서 무조건 그것을 따를 필요는 없다. 언제든지 현재의 kepp-alive 커넥션을 끊을 수 있으며 keep-alive 커넥션에서 처리되는 트랜잭션의 수를 제한할 수도 있다.

keep-alive의 동작은 Keep-Alive 헤더의 쉼표로 구분된 옵션들로 제어할 수 있다.

- timeout 파라미터는 Keep-Alive 응답 헤더를 통해 보낸다. 이는 커넥션이 얼마간 유지될 것인지를 의미한다.
- max 파라미터는 Keep-Alive 응답 헤더를 통해 보낸다. 이는 커넥션이 몇 개의 HTTP 트랜잭션을 처리할 때까지 유지될 것인지를 의미한다.
- Keep-Alive 헤더는 진단이나 디버깅을 주목적으로 하는, 처리되지는 않는 임의의 속성들을 지원하기도 한다. 그 문법은 이름[=값] 같은 식이다.

Keep-Alive 헤더 사용은 선택사항이지만, Connection: Keep-Alive 헤더가 있을 때만 사용할 수 있다.

~~~ http
// 서버가 약 5개의 추가 트랜잭션이 처리될 동안 커넥션을 유지하거나, 2분 동안 커넥션을 유지하라는 내용의 Keep-Alive 응답 헤더
Connection: Kepp-Alive
Keep-Alive: max=5, timeout=120
~~~

### 4.5.5 Keep-Alive 커넥션 제한과 규칙

- keep-alive는 HTTP/1.0에서 기본으로 사용되지는 않는다. 클라이언트는 keep-alive 커넥션을 사용하기 위해 Connection: Keep-Alive 요청 ㅇ헤더를 보내야 한다.
- 커넥션을 계속 유지하려면 모든 메시지에 Connection: Keep-Alive 헤더를 포함해 보내야 한다.
- 클라이언트는 Connection: Keep-Alive 응답 헤더가 없는 것을 보고 서버가 응답 후에 커넥션을 끊은 것임을 알 수 있다.
- 커넥션이 끊어지기 전에 엔티티 본문의 길이를 알 수 있어야 커넥션을 유지할 수 있다. 이 말은 엔티티 본문이 정확한 Content-Length 값과 함께 멀티파트 미디어 형식을 가지거나 청크 전송 인코딩으로 인코드 되어야 함을 뜻한다. keep-alive 커넥션에서 잘못된 Content-length 값을 보내는 것은 좋지 않은데, 트랜잭션이 끝나는 시점에 기존 메시지의 끝과 새로운 메시지의 시작점을 정확히 알 수 없기 때문이다.
- 프락시와 게이트웨이는 Connection 헤더의 규칙을 철저히 지켜야 한다. 프락시와 게이트웨이는 메시지를 전달하거나 캐시에 넣기 전에 Connection 헤더에 명시된 모든 헤더 필드와 Connection 헤더를 제거해야 한다.
- 정석대로라면, keep-alive 커넥션은 Connection 헤더를 인식하지 못하는 프락시 서버와는 맺어지면 안 된다. 이는 뒤에서 설명할 멍청한(dumb) 프락시로 인해 발생할 문제를 예방하기 위한 것이지만, 현실적으로 그것이 쉽진 않다.

### 4.5.6 Keep-Alive와 멍청한(dumb) 프락시

웹 클라이언트의 요청에 Connection: Keep-Alive 헤더가 있으면, 클라이언트가 현재 연결하고 있는 TCP 커넥션을 끊지 않고 계속 유지하려는 것이다. 헤더 이름이 'connection'인 것도 이 때문이다. 클라이언트가 웹 서버에 요청을 보내는 중이라면, 그 요청으로 생긴 커넥션이 keep-alive가 되기를 원한다는 의미로 Connection: Keep-Alive 헤더를 전송한다. 서버가 keep-alive를 지원한다면 Connection: Keep-Alive를 응답에 포함할 것이고, 지원하지 않으면 포함하지 않을 것이다.

#### Connection 헤더의 무조건 전달

특히 문제는 프락시에서 시작되는데, 프락시는 Connection 헤더를 이해하지 못해서 해당 헤더들을 삭제하지 않고 요청 그대로를 다음 프락시에 전달한다. 오래되고 단순한 수많은 프락시들이 Connection 헤더에 대한 처리 없이 요청을 그대로 전달한다. 웹 클라이언트가 무조건 전달을 하는 멍청한 프락시를 거쳐 웹 서버에 메시지를 전송한다고 생각해보자. 그 상황은 아래 그림에서 볼 수 있다.

![image](https://user-images.githubusercontent.com/22893111/76606245-4277e280-6555-11ea-84ce-7b181ea46562.png)

이 그림에서 일어나는 일은 다음과 같다.

1. (a) 웹 클라이언트는 프락시에 Connection: Keep-Alive 헤더와 함께 메시지를 보내고, 커넥션을 유지하기를 요청한다. 클라이언트는 커넥션을 유지하자는 요청이 받아들여졌는지 확인하기 위해 응답을 기다린다.
2. (b) 멍청한 프락시는 요청받은 HTTP의 Connection 헤더를 이해하지 못한다. 프락시는 keep-alive가 무엇인지 무르기 때문에, 다음 서버에 메시지를 그대로 전달한다. 하지만 Connection 헤더는 홉별(hop-by-hop) 헤더다(이는 오직 한 개의 전송 링크에만 적용되며 다음 서버로 전달되어서는 안된다). 여기서부터 문제가 시작된다.
3. (b)에서 보이는 것처럼, 전달된 HTTP 요청이 서버에 도착한다. 웹 서버가 프락시로부터 Connection: Keep-Alive 헤더를 받으면, 웹 서버는 프락시(기존에 클라이언트로부터 받았을 때와 같이)가 커넥션으르 유지하자고 요청하는 것으로 잘못 판단하게 된다. (c)와 같이 웹 서버는 문제될 게 없기 때문에, 프락시와 커넥션을 유지하는 것에 동의를 하고 Connection: Keep-Alive 헤더를 포함하여 응답한다. 웹 서버는 프락시와 keep-alive 커넥션이 맺어져 있는 상태로 keep-alive 규칙에 맞게 통신을 하는 것으로 판단한다. 하지만 프락시는 keep-alive를 전혀 이해하지 못한다.
4. (b)에서 멍청한(dumb) 프락시는 서버로부터 받은 Connection: Keep-Alive 헤더를 포함하고 있는 응답 메시지를 클라이언트에게 전달한다. 클라이언트는 이 헤더를 통해 프락시가 커넥션을 유지하는 것에 동의했다고 추정한다. 이 시점에 클라이언트와 서버는 커넥션을 유지하고 있다고 생각하지만, 정작 프락시는 keep-alive를 전혀 이해하지 못한다.
5. 프락시는 keep-alive를 전혀 모르지만, 받았던 모든 데이터를 그대로 클라이언트에게 전달하고 나서 서버가 커넥션을 끊기를 기다린다. 하지만 서버는 프락시가 자신에게 커넥션을 유지하기를 요청한 것으로 알고 있기 때문에 커넥션을 끊지 않는다. 따라서 프락시는 커넥션이 끊어지기 전까지는 계속 커넥션이 끊어지기를 기다리게 된다.
6. (d)와 같이 클라이언트가 응답 메시지를 받으면, 다음 요청을 보내기 시작하는데, 커넥션이 유지되고 있는 프락시에 그 요청을 보낸다. 프락시는 같은 커넥션상에서 다른 요청이 오는 경우는 예상하지 못하기 때문에, 그 요청은 프락시로부터 무시 되고 브라우저는 아무런 응답 없이 로드 중이라는 표시만 나온다.
7. 이런 잘못된 통신 때문에, 브라우저는 자신이나 서버가 타임아웃이 나서 커넥션이 끊길 때까지 기다린다.

#### 프락시와 홉별 헤더

이런 종류의 잘못된 통신을 피하려면, 프락시는 Connection 헤더와 Connection 헤더에 명시된 헤더들은 절대 전달하면 안 된다. 따라서 프락시가 Connection: Keep-Alive 헤더를 받으면 Connection 헤더뿐만 아니라 Keep-Alive란 이름의 헤더도 전달하면 안 된다.

또한, Connection 헤더 값으로 명시되지 않는, Proxy-Authenticate, Proxy-Connection, Transfer-Encoding, Upgrade와 같은 홉별 헤더들 역시 전달하거나 캐시하면 안 된다.

### 4.5.7 Proxy-Connection 살펴보기

넷스케이프의 브라우저 및 프락시 개발자들은 모든 웹 애플리케이션이 HTTP 최신 버전을 지원하지 않아도, 모든 헤더를 무조건 전달하는 문제를 해결할 수 있는 차선책을 제시하였다. 그 차선책은, 클라이언트의 요청이 중개서버를 통해 이어지는 경우 모든 헤더를 무조건 전달하는 문제를 해결하기 위해 Proxy-Connection이라는 헤더를 사용하는 것이다. Proxy-Connection은 프락시를 별도로 설정할 수 있는 현대의 브라우저들에서 지원하고 있으며, 많은 프락시들도 이것을 인식한다.

멍청한 프락시는 Connection: Keep-Alive 같은 홉별 헤더를 무조건 전달하기 때문에 문제를 일으킨다. 홉별 헤더들은 한 개의 특정 커넥션에서 쓰이고 그 이후에는 전달하면 안 된다. 홉별 헤더를 전달받은 서버가 그 헤더를 자신과 프락시 간의 커넥션에 대한 것으로 오해하면서 문제가 생기는 것이다.

넷스케이프는 멍청한 프락시 문제를 해결하기 위해 브라우저에서 일반적으로 전달하는 Connection 헤더 대신 비표준인 Proxy-Connection 확장 헤더를 프락시에게 전달한다. 프락시가 Proxy-Connection 헤더를 무조건 전달하더라도 웹 서버는 그것을 무시하기 때문에 별문제가 되지 않는다. 하지만 영리한 프락시(지속 커넥션 핸드셰이킹을 이해할 수 있는)라면, 의미 없는 Proxy-Connection 헤더를 Connection헤더로 바꿈으로써 원하던 효과를 얻게 될 것이다.

아래 그림의 a~b는 무조건 전달로 인해 Proxy-Connection 헤더가 웹 서버에 전달되더라도 아무런 문제가 생기지 않는다는 것으르 보여준다. Proxy-Connection 헤더가 웹 서버에 전달되더라도 클라이언트와 프락시 사이 혹은 프락시와 서버 사이에 keep-alive 커넥션이 맺어지지 않는다. e~h에서 영리한 프락시는 Proxy-Connection 헤더가 keep-alive를 요청하는 것임을 인식하여, keep-alive 커넥션을 맺기 위해 자체적으로 Connection: Keep-Alive 헤더를 웹 서버에 전송한다.

![image](https://user-images.githubusercontent.com/22893111/76608927-dd72bb80-6559-11ea-90e8-eee4f9c9b7bb.png)

이 방식은 클라이언트와 서버 사이에 한 개의 프락시만 있는 경우에만 동작한다. 하지만 멍청한 프락시의 양옆에 영리한 프락시가 있다면 아래 그림과 같이 잘못된 헤더를 만들어내는 문제가 발생한다.

![image](https://user-images.githubusercontent.com/22893111/76609221-5bcf5d80-655a-11ea-98c4-eeec99d43832.png)

게다가 문제를 발생시키는 프락시들은 방화벽, 캐시 서버, 혹은 리버스 프락시 서버 가속기와 같이 네트워크상에서 '보이지 않는' 경우가 많다.

<file_sep>/Spring/@RequestBody는 왜 기본생성자가 필요하며, Setter는 필요없을까? #3.md
> [이전 글](https://velog.io/@conatuseus/RequestBody에-왜-기본-생정자는-필요하고-Setter는-필요-없을까-2-ejk5siejhh)에서는 RestController에서 `@RequestBody` 바인딩을 Jackson 라이브러리의 ObjectMapper가 하는 것을 확인했습니다.
> 그리고 RequestBody를 생성할 때, DTO가 Property기반이 아니거나 Delegate를 한 상태가 아니라면 **기본 생성자**로 생성한다는 것을 알아냈습니다.
> 이번 글에서는 **기본 생성자로 생성하는데, 어떻게 해당 DTO에 Setter가 필요없지?** 라는 **의문점**을 풀어보도록 하겠습니다.

![](https://images.velog.io/images/conatuseus/post/627d3022-d0b7-4839-b739-360d9642eb32/image.png)

저는 RequestBody로 들어오는 RequestDto에 위와 같이 `@NoArgsConstructor`, `Getter` 어노테이션만 작성했습니다.

위와 한다면 `Setter`가 없는데 스프링이 어떻게 DTO에 값을 넣어줄까 의문을 가졌습니다. 이 문제를 해결하기 위해 ObjectMapper에 대해 좀 더 알아볼 필요가 있었습니다.

## 어떻게 ObjectMapper가 JSON Field와 Java Field를 매칭시킬까?
이를 알아보기 위해 [여기](http://tutorials.jenkov.com/java-json/jackson-objectmapper.html#how-jackson-objectmapper-matches-json-fields-to-java-fields)를 참고했습니다.

![](https://images.velog.io/images/conatuseus/post/3b3585e4-11e9-459e-bae7-96191dbfdad8/image.png)

이를 해석해 보면, 다음과 같습니다.
> 기본적으로 Jackson은 JSON 필드의 이름을 Java 오브젝트의 **getter 및 setter 메소드**와 일치시켜 JSON 오브젝트의 필드를 Java 오브젝트의 필드에 매칭합니다. Jackson은 getter 및 setter 메소드 이름의 "get"및 "set"부분을 제거하고 나머지 이름의 첫 문자를 소문자로 변환합니다.
> 예를 들어 brand라는 JSON 필드는 getBrand () 및 setBrand ()라는 Java getter 및 setter 메소드와 일치합니다. engineNumber라는 JSON 필드는 getEngineNumber () 및 setEngineNumber ()라는 getter 및 setter와 일치합니다.

쉽게말해 **동일한 이름의 변수를 직접 찾는 것이 아니라, Getter와 Setter를 통해 찾아서 매칭한다**는 것입니다.

## DTO에 필드 값을 주입하는 과정을 살펴보자
변수의 매칭은 getter와 setter를 하는 것을 알아냈습니다. 그럼 그렇게 매칭한 것을 어떻게 주입시키는지 알아볼 차례입니다.
왜냐하면 setter를 사용해서 주입시킬 것이라 생각했었기 때문입니다.

### 실제로 getter와 setter로 프로퍼티를 가져오는지 확인하기
![](https://images.velog.io/images/conatuseus/post/9b795348-64c8-4f96-85ce-0d2e23ae2ad2/image.png)

BeanDeserializer를 생성하기 위한 `BeanDeserializerFactory`에서 해당 작업을 수행하는 것을 확인할 수 있었습니다.
위의 addBeanProps 메서드를 들어가보면 아래와 같이 Setter와 Getter로 Bean의 프로퍼티를 찾는 것을 알 수 있습니다. (사진에 안보이지만 아래에는 Getter로 찾는 부분이 있습니다.)
![](https://images.velog.io/images/conatuseus/post/96fd36ce-92ef-4fc7-8283-e159c49ca89f/image.png)

### 그럼 값 주입은 어떻게?
`BeanDeserializer`에서 DTO에 주입하는 코드를 찾았습니다.
아래 코드에서 p는 JsonParser입니다. propertyName으로 이전에 저장했던 프로퍼티를 가져옵니다. 그리고 이제 JsonParser와 Bean을 `deserializeAndSet` 메서드로 넘겨 값을 저장해주는 것입니다.
![](https://images.velog.io/images/conatuseus/post/905254c3-c67c-4247-bb9f-145ff8c3e12b/image.png)

이제 `prop.deserializeAndSet` 메서드로 들어가봅시다!
![](https://images.velog.io/images/conatuseus/post/79fd292a-9c04-47a0-af3a-a7dff7aec838/image.png)

여기서 이유를 찾았습니다!! 🎉
`_field`는 `java.lang.reflect` 패키지의 Field 자료형입니다. 결국 **reflection을 사용해서 값을 주입해주므로 `Setter`는 필요하지 않는 것**입니다.

### 여러 상황 테스트

> 테스트에서는 reflection을 사용해 value값을 보여드리기 위해 dto의 변수들을 모두 public으로 변경했습니다.

1. Setter & Getter 모두 없는 경우 테스트
![](https://images.velog.io/images/conatuseus/post/b4e9d640-99a7-48ae-b3cf-2c031e27b441/image.png)
objectMapper가 바인딩하는데 DTO에 setter와 getter가 없으니 찾지 못해 오류가 납니다.

2. Setter만 있는 경우 테스트
![](https://images.velog.io/images/conatuseus/post/26b93ae3-8513-480c-ae4e-5e58b3b08be4/image.png)

3. Getter만 있는 경우 테스트
![](https://images.velog.io/images/conatuseus/post/1b8846f5-abfb-405c-9847-67ea385af1c9/image.png)

이 테스트들을 통해, Setter와 Getter 모두 없는 경우에는 `ObjectMapper`가 바인딩하는데 오류가 생기며
**Setter, Getter 둘 중 하나만 있으면 된다는 것**을 알았습니다.

## 3번째 글을 쓰면서 내리는 결론
- `@RestController`에서 `@RequestBody`의 바인딩은 Jackson라이브러리의 **`ObjectMapper`**가 해준다.
- `ObjectMapper`는 `@RequestBody`가 Property로 구현되어 있거나 생성을 위임한 경우가 아니라면 **기본 생성자**로 생성한다.
- 따라서 두 상황이 아니라면 기본 생성자는 꼭 필요하다. (`@NoArgsConstructor`가 필요한 이유)
- ObjectMapper는 **Setter와 Getter로 DTO의 필드**를 가져온다.
- 그리고 setter를 사용하는 것이 아니라 **reflection을 사용해서 필드 값들을 넣어준다.**
- 따라서 DTO에 사용할 필요가 거의 없는 `Setter`보다 프로덕션 코드에 사용되거나 테스트에서 필요한 `Getter`를 넣는게 좋겠다는 것이 저의 의견입니다. (쉽게 말해, Setter 또는 Getter 둘 중 하나만 있으면 되는데 Getter를 넣자!)

> 오타 또는 잘못된 내용이 있으면 댓글이나 메일 주시면 정말 감사하겠습니다.
>
> 참고한 자료
> - https://fasterxml.github.io/jackson-databind/javadoc/2.7/com/fasterxml/jackson/databind/ObjectMapper.html
> - http://tutorials.jenkov.com/java-json/jackson-objectmapper.html#how-jackson-objectmapper-matches-json-fields-to-java-fields <file_sep>/Article/2019-12/9.md
# 2019-12-09 월요일

- SK텔레콤과 통계청이 지난 5월 체결한 ‘빅데이터 기반의 통계 개발 및 품질 향상을 위한 업무협약’의 첫 결과물이 나왔다.
- 양사는 통계청이 보유한 인구∙가구 관련 공공 빅데이터와 SKT의 앞선 모바일 빅데이터 분석 역량을 결합해 모바일 빅데이터 기반의 유동인구 지도 시범 서비스를 시작했다. ‘통신모바일 빅데이터로 본 유동인구 지도서비스‘는 전국 시군구별 주중, 주말 월별 일평균 유동인구를 GIS 기반으로 제공한다.
- 통신모바일 빅데이터로 본 유동인구 지도서비스는 SKT 이동통신 기지국의 모바일 빅데이터를 기반으로 지도상에 선택한 지역의 인구 유입·유출 현황을 시각적으로 확인할 수 있다. 유입·유출인구수는 SKT 모바일 위치정보와 시장점유율을 이용하여 전체를 추정했다.
<file_sep>/Article/2019-11/README.md
## 2019.11

## List

- [16-토요일](https://github.com/Conatuseus/TIL/blob/master/Article/2019-11/16.md)
  - Topic: 네이버는 왜 기술 스타트업에 투자할까? 넷플릭스, 월 결제액 260억...유료 가입자 200만명
- [17-일요일](https://github.com/Conatuseus/TIL/blob/master/Article/2019-11/17.md)
  - Topic: 페이스북 페이, 리브라와 다른 점은
- [18-월요일](https://github.com/Conatuseus/TIL/blob/master/Article/2019-11/18.md)
  - Topic: 라인,야후재팬 경영통합 합의, 우아한형제들 서빙로봇 '딜리' 랜탈 프로그램 출시
- [19-화요일](https://github.com/Conatuseus/TIL/blob/master/Article/2019-11/19.md)
  - Topic: 실리콘벨리에서 주목하는 '원페이지' 협업툴
- [20-수요일](https://github.com/Conatuseus/TIL/blob/master/Article/2019-11/20.md)
  - Topic: 요기요 신입 개발자 채용, 라임, GS칼텍스·GS리테일과 파트너십…전동킥보드 충전 네트워크 구축
- [21-목요일](https://github.com/Conatuseus/TIL/blob/master/Article/2019-11/21.md)
  - Topic: 라인 기술을 경험하라…’라인 디벨로퍼 데이 2019′, “서비스 모든 곳에 AI를”…라인이 AI를 대하는 법
- [22-금요일](https://github.com/Conatuseus/TIL/blob/master/Article/2019-11/22.md)
  - Topic: "무단 녹음·인터넷 접속" 보안 뚫린 안드로이드폰…삼성도, “5G 품질, 3D 실사 지도로 높인다”
- [23-토요일](https://github.com/Conatuseus/TIL/blob/master/Article/2019-11/23.md)
  - Topic: 제 주인 찾아간 카카오뱅크, 지금 OTT 플랫폼은 오리지널 콘텐츠 쟁탈전
- [24-일요일](https://github.com/Conatuseus/TIL/blob/master/Article/2019-11/24.md)
  - Topic: LG 로봇, 이번에는 빕스 1호점에 취직
- [25-월요일](https://github.com/Conatuseus/TIL/blob/master/Article/2019-11/25.md)
  - Topic: NHN 토스트, CSA STAR 획득…클라우드 보안 신뢰성↑,  티머니, 미래 대중교통 결제수단은 ‘안면인식, QR’
- [26-화요일](https://github.com/Conatuseus/TIL/blob/master/Article/2019-11/26.md)
  - Topic: 티맥스오에스가 만든 협업 도구 ‘클라우드스페이스’
- [27-수요일](https://github.com/Conatuseus/TIL/blob/master/Article/2019-11/27.md)
  - Topic: NHN FORWARD, 요기요, 홈플러스 익스프레스도 배달한다
- [28-목요일](https://github.com/Conatuseus/TIL/blob/master/Article/2019-11/28.md)
  - Topic: 네트워크부터 DB·앱까지….구글 신규 클라우드 솔루션 5종 출시
- [29-금요일](https://github.com/Conatuseus/TIL/blob/master/Article/2019-11/29.md)
  - Topic: 'co', 'go' 빠진 숫자 도메인 시대 온다,  네이버 바이브, AI로 다음곡 자동추천한다
- [30-토요일](https://github.com/Conatuseus/TIL/blob/master/Article/2019-11/30.md)
  - Topic: 버스용 기가급 와이파이 나올까…첫 시연 성공, “사진 속 그 옷, 쇼핑몰서 찾아드려요”
<file_sep>/Article/2019-12/20.md
# 2019-12-20 금요일

## [토스, LG유플러스 PG사업부 인수](http://www.bloter.net/archives/365092)

- 토스를 운영하는 비바리퍼블리카는 LG유플러스의 전자결제사업부문(PG) 인수에 관한 주식매매계약(SPA)을 체결했다고 12월20일 밝혔다.
- 토스는 이번 인수를 통해, 빠르게 성장하는 모바일 커머스 시장에서 LG유플러스의 8만여 가맹점을 기반으로 소비자의 결제 접점을 확대해 나가고, 가맹점과 사용자 모두에게 혁신적 결제 서비스 경험을 제공한다는 계획이다.
- 토스 이승건 대표는 “핀테크 분야에서 토스가 쌓아온 경험과 역량을 바탕으로 결제 서비스 산업에 새로운 혁신을 만들고 싶다”라고 인수 배경을 밝혔다.

## [대형 클라우드는 오픈소스SW 회사들을 위협하는가](http://www.bloter.net/archives/365067)

- 세계 최대 퍼블릭 클라우드 서비스 업체인 아마존웹서비스(AWS)와 데이터베이스 관련 오픈소스 소프트웨어 프로젝트를 이끄는 회사들간 신경전이 심상치 않다.
- 일부 오픈소스 프로젝트 개발사들은 AWS가 오픈소스 소프트웨어 혁신에 무임승차하고 있다는 비판을 쏟아내기 시작했고, 이에 대해 AWS는 “근거가 없는 주장”이라고 받아치고 나서면서 양측간 갈등은 점점 첨예해지는 양상이다.
- 뉴욕타임스에 따르면 몇몇 오픈소스 데이터베이스 회사들은 AWS가 다른 이들이 개척한 오픈소스 소프트웨어를 자사 서비스에 통합하고 있는 전술에 대해 비판을 쏟아냈다. AWS가 혁신을 위해 오픈소스 소프트웨어를 노천채굴(strip mines)하는 것과 같은 행위를 하고 있다는 것이었다. 오픈소스 소프트웨어에 기여하기 보다는 혜택을 가져가는데 집중하고 있다는 얘기였다.<file_sep>/Java/GC(Garbage Collection).md
# GC(Garbage Collection) - 가비지 컬렉션

<file_sep>/JPA/JPA프로그래밍/영속성컨텍스트_1.md
> 이번 글에서는
**엔티티 매니저 팩토리, 엔티티 매니저, 영속성 컨텍스트의 개념, 엔티티의 생명 주기**에 대해 알아보겠습니다.
이 시리즈 글은 김영한 님의 [강의](https://www.inflearn.com/course/ORM-JPA-Basic), [책](https://book.naver.com/bookdb/book_detail.nhn?bid=9252528)을 보고 적은 것임을 알려드립니다. (강추)

# 영속성 컨텍스트

## 먼저 알고 가기 :  엔티티 매니저 팩토리와 엔티티 매니저

![image.png](https://images.velog.io/post-images/conatuseus/38dac7b0-cff5-11e9-825d-1977adc755dc/image.png)

<시나리오>
1. 새로운 고객의 요청이 올때마다 엔티티 매니저 팩토리는 엔티티 매니저를 생성함
2. 엔티티 매니저는 내부적으로 데이터베이스 커넥션을 사용해서 DB를 사용

 **엔티티 매니저 팩토리**
- 엔티티 매니저를 만드는 공장
- 생성하는 비용이 커서 한 개만 만들어 애플리케이션 전체에서 공유
- 여러 스레드가 동시에 접근해도 안전

 **엔티티 매니저**
- 엔티티의 CRUD 등 엔티티와 관련된 모든 일을 처리
- 엔티티 매니저는 여러 스레드가 동시에 접근하면 동시성 문제가 발생하므로 스레드 간에 절대 공유하면 안됨

## 영속성 컨텍스트란!?
영속성 컨텍스트란 **엔티티를 영구 저장하는 환경** 이라는 뜻이다.
> EntityManger.persist(member);

이전 포스팅에서 이 코드는 member 엔티티를 저장한다고 했었다. 하지만 정확하게 얘기하면 **데이터베이스에 저장하는게 아니라 엔티티 매니저를 사용해서 회원 엔티티를 영속성 컨텍스트에 저장**하는 코드다.

영속성 컨텍스트는 엔티티 매니저를 생성할 때 하나만 만들어진다. 엔티티 매니저를 통해 영속성 컨텍스트에 접근할 수 있고, 영속성 컨텍스트를 관리할 수 있다.

## 엔티티의 생명주기

 엔티티의 4가지 상태
 - 비영속(new/transient): 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
 - 영속(managed): 영속성 컨텍스트에 관리되는 상태
 - 준영속(detached): 영속성 컨텍스트에 저장되었다가 분리된 상태
 - 삭제(removed): 삭제된 상태

이제 각 상태에 대해 자세히 알아보자.

### 비영속(new/transient)
**엔티티 객체가 생성된 순수 객체 상태
아직 영속성 컨텍스트나 데이터베이스와는 전혀 관계가 없는 상태**

![image.png](https://images.velog.io/post-images/conatuseus/00365d80-cff9-11e9-9e8e-a99e1a48ecbd/image.png)

### 영속(managed)

**엔티티 매니저를 통해 엔티티를 영속성 컨텍스트에 저장하면, 영속성 컨텍스트가 엔티티를 관리하므로 영속 상태가 된다.**

![image.png](https://images.velog.io/post-images/conatuseus/a79e0c30-cff9-11e9-afa7-5d9d2cd90530/image.png)

> EntityManager.persist(member)

위 코드를 수행하면 member 엔티티는 영속성 컨텍스트에 의해 관리되는 **영속 상태**가 됨
위 코드가 수행된다고 데이터베이스에 저장되는 것은 아님. (트렌잭션이 commit하는 시점에 영속성 컨텍스트에 있는 엔티티들에 대한 쿼리가 날라간다)

### 준영속(detached)
 영속성 컨텍스트가 관리하던 영속 상태의 엔티티를 영속성 컨텍스트가 관리하지 않으면 준영속 상태가 됨
 > 엔티티를 준영속 상태로 만들려면?
 - EntityManager.detach(entity) : 엔티티를 준영속 상태로 만듬
 - EntityManager.close() : 영속성 컨텍스트를 닫음
 - EntityManager.clear() : 영속성 컨텍스트를 초기화

### 삭제
 엔티티를 영속성 컨텍스트와 데이터베이스에서 삭제
 > EntityManager.remove(entity)

### 그림으로 보는 엔티티 생명주기

![image.png](https://images.velog.io/post-images/conatuseus/3861eed0-d482-11e9-9b0f-dd1a4f570095/image.png)
<file_sep>/Article/2019-12/8.md
# 2019-12-08 일요일

## [“우버, 한국서는 택시사업 주력…불법은 안 한다”](http://www.bloter.net/archives/363680)
<file_sep>/Article/2019-11/26.md

# 2019-11-26 화요일

## [티맥스오에스가 만든 협업 도구 ‘클라우드스페이스’](http://www.bloter.net/archives/362394)
- Summary
  - 통합커뮤니케이션 도구, 생산성 도구 시장에 운영체제 및 클라우드 전문기업 티맥스오에스가 뛰어들었다. ‘만들고 모일수록 더 즐거워진다’라며 클라우드 플랫폼 위에서 스마트워크&라이프 앱 서비스를 지원하는 ‘클라우드스페이스‘ 서비스를 공개
  - 클라우드스페이스는 마이크로 서비스 아키텍처(MSA)를 기반으로 다양한 앱 서비스의 신속한 개발 및 확장할 수 있는 클라우드 앱 개발 플랫폼이다. 티맥스 그룹사의 모든 클라우드 기술을 총 집약했다고 한다.

<file_sep>/Java/dependencies.md

- google guava

```
implementation('com.google.guava:guava:23.5-jre')
```

<file_sep>/Article/2019-12/2.md
# 2019-12-2 월요일

## [동남아로 간 차량호출 서비스 엠블, 56억원 투자 유치](http://www.bloter.net/archives/363152)
- 모빌리티 플랫폼인 엠블(MVL)을 개발하는 엠블랩스(MVL LABS)가 SV인베스트먼트, 센트랄, 심원 등으로부터 약 56억원 규모 시리즈A 투자를 유치했다.
- 엠블랩스는 싱가포르와 캄보디아, 그리고 베트남에서 차량호출 서비스 ‘타다(TADA)’를 운영하고 있다.
- 타다는 블록체인을 기반으로 한 수수료가 없는 차량호출 서비스로, 2018년 12월 싱가포르에서 앱 다운로드 수 1위를 차지했다. 현재 이용자 약 50만명과 함께 6만명 이상의 운전기사를 보유하고 있다. 엠블랩스는 블록체인 기반 새로운 모빌리티 생태계 엠블(MVL)을 개발 중이다.
- 기존 그랩, 우버 등 동남아시아 모빌리티 시장을 선점한 회사들은 고 수수료 정책으로 택시기사들의 반발을 크게 산 바 있다. 엠블랩스는 이 점을 포착해 수수료가 아닌 간편결제 분야에서 비즈니스 모델을 개척, 동남아시아 차량호출 서비스 점유율을 확보했다고 설명했다.
<file_sep>/Books/HTTP완벽가이드/3장.HTTP 메시지.md
# 3장 HTTP 메시지
HTTP가 인터넷의 배달원이라면, HTTP 메시지는 무언가를 담아 보내는 소포와 같다.
이번장에서 공부할 내용

- 메시지가 어떻게 흘러가는가
- HTTP 메시지의 세 부분(시작줄, 헤더, 개체 본문)
- 요청과 응답 메시지의 차이
- 요청 메시지가 지원하는 여러 기능(메서드)들
- 응답 메서드가 반환하는 여러 상태 코드들
- 여러 HTTP 헤더들은 무슨 일을 하는가

## 3.1 메시지의 흐름

**HTTP 메시지는 HTTP 애플리케이션 간에 주고받는 데이터의 블록들**이다. 이 데이터의 블록들은 **메시지의 내용과 의미를 설명하는 텍스트 메타 정보로 시작하고 그 다음에 선택적으로 데이터가 올 수 있다.** 이 메시지는 클라이언트, 서버, 프락시 사이를 흐른다. '인바운드', '아웃바운드', '업스트림', '다운스트림'은 메시지의 방향을 의미하는 용어다.

### 3.1.1 메시지는 원 서버 방향을 인바운드로 하여 송신된다

HTTP는 인바운드와 아웃바운드라는 용어를 트랜잭션 방향을 표현하기 위해 사용한다. 메시지가 **원 서버로 향하는 것은 인바운드**로 이동하는 것이고, 모든 처리가 끝난 뒤에 메시지가 **사용자 에이전트로 돌아오는 것은 아웃바운드**로 이동하는 것이다.

![image](https://user-images.githubusercontent.com/22893111/71709716-2f9daf00-2e3c-11ea-8547-6a26d4264674.png)

### 3.1.2 다운스트림으로 흐르는 메시지

HTTP 메시지는 강물과 같이 흐른다. 요청 메시지냐 응답 메시지냐에 관계 없이 모든 메시지는 다운스트림으로 흐른다. 메시지의 발송자는 수신자의 업스트림이다.

![image](https://user-images.githubusercontent.com/22893111/71709845-bfdbf400-2e3c-11ea-9e96-f2852b441153.png)

## 3.2 메시지의 각 부분

HTTP 메시지는 단순한, 데이터의 구조화된 블록이다. 각 메시지는 클라이언트로부터의 요청이나 서버로부터의 응답 중 하나를 포함한다. 메시지는 **시작줄, 헤더 블록, 본문** 이렇게 세 부분으로 이루어진다. 시작줄은 이것이 어떤 메시지인지 서술하며, 헤더 블록은 속성을, 본문은 데이터를 담고 있다. 본문은 아예 없을 수도 있다.

![image](https://user-images.githubusercontent.com/22893111/71710065-be5efb80-2e3d-11ea-9d8c-2fd1630862b1.png)

 시작줄과 헤더는 그냥 줄 단위로 분리된 아스키 문자열이다.

엔티티 본문이나 메시지 본문은 단순히 선택적인 데이터 덩어리이다. 시작줄이나 헤더와는 달리, 본문은 텍스트나 이진 데이터를 포함할 수도 있고 그냥 비어있을 수도 있다.

### 3.2.1 메시지 문법

모든 HTTP 메시지는 요청 메시지나 응답 메시지로 분류된다. 요청 메시지는 웹 서버에 어떤 동작을 요구한다. 응답 메시지는 요청의 결과를 클라이언트에게 돌려준다. 요청과 응답 모두 기본적으로 구조가 같다. 아래 그림은 GIF image를 가져오기 위한 요청과 응답 메시지를 보여준다.

![image](https://user-images.githubusercontent.com/22893111/71710154-362d2600-2e3e-11ea-8f4a-f7620fe8d35e.png)

요청 메시지의 형식은 다음과 같다

~~~ http
<메서드> <요청 URL> <버전>
<헤더>

<엔티티 본문>
~~~

응답 메시지의 형식은 다음과 같다. (시작줄에서만 문법이 다르다)

~~~ http
<버전> <상태 코드> <사유 구절>
<헤더>

<엔티티 본문>
~~~

각 부분에 대해 알아보자.

#### 메서드

클라이언트 측에서 버서가 리소스에 대해 수행해주길 바라는 동작이다. 'GET', 'HEAD', 'POST'와 같이 한 단어로 되어 있다.

#### 요청 URL

요청 대상이 되는 리소스를 지칭하는 완전한 URL 혹은 URL의 경로 구성요소다. 완전한 URL이 아닌 URL의 경로 구성요소라고 해도, 클라이언트가 서버와 직접 대화하고 있고 경로 구성요소가 리소스를 가리키는 절대 경로이기만 하면 대체로 문제가 없다. 서버는 URL에서 생략된 호스트/포트가 자신을 가리키는 것으로 간주할 것이다.

#### 버전

이 메시지에서 사용 중인 HTTP의 버전이다. 형식은 다음과 같다.

> HTTP/<메이저>.<마이너>

#### 상태 코드

요청 중에 무엇이 일어났는지 설명하는 세 자리 숫자다. 각 코드의 첫 번째 자릿수는 상태의 일반적인 분류(성공, 에러 등)를 나타낸다.

#### 사유 구절

숫자로 된 상태 코드의 의미를 사람이 이해할 수 있게 설명해주는 짧은 문구로, 상태 코드 이후부터 줄바꿈 문자열까지가 사유 구절이다. 사유 구절은 오로지 사람에게 읽히기 위한 목적으로만 존재한다.

#### 헤더들

이름, 콜론(:), 선택적인 공백, 값, CRLF가 순서대로 나타나는 0개 이상의 헤더들. 이 헤더의 목록은 빈 줄(CRLF)로 끝나 헤더 목록의 끝과 엔티티 본문의 시작을 표시한다.

#### 엔티티 본문

엔티티 본문은 임의의 데이터 블록을 포함한다. 모든 메시지가 엔티티 본문을 갖는 것은 아니므로, 때때로 메시지는 그냥 CRLF으로 끝나게 된다.

### 3.2.2 시작줄

모든 HTTP 메시지는 시작줄로 시작한다. 요청 메시지의 시작줄은 무엇을 해야 하는지 말해준다. 응답 메시지의 시작줄은 무슨 일이 일어났는지 말해준다.

#### 요청줄

요청 메시지는 서버에게 리소스에 대해 무언가를 해달라고 부탁한다. 요청 메시지의 시작줄, 혹은 요청줄에는 서버에서 어떤 동작이 일어나야 하는지 설명해주는 메서드와 그 동작에 대한 대상을 지칭하는 요청 URL이 들어 있다. 또한 요청 줄은 클라이언트가 어떤 HTTP 버전으로 말하고 있는지 서버에게 알려주는 HTTP 버전도 포함한다.

#### 응답줄

응답 메시지는 수행 결과에 대한 상태 정보와 결과 데이터를 클라이언트에게 돌려준다. 응답 메시지의 시작줄 혹은 응답줄에는 응답 메시지에서 쓰인 HTTP의 버전, 숫자로 된 상태 코드, 수행 상태에 대해 설명해주는 텍스트로 된 사유 구절이 들어 있다.

#### 메서드

요청의 시잘줄은 메서드로 시작하며, 서버에게 무엇을 해야 하는지 말해준다.

| 메서드  | 설명                                                        | 메시지 본문이 있는가? |
| ------- | ----------------------------------------------------------- | --------------------- |
| GET     | 서버에서 어떤 문서를 가져온다                               | X                     |
| HEAD    | 서버에서 어떤 문서에 대해 헤더만 가져온다                   | X                     |
| POST    | 서버가 처리해야 할 데이터를 보낸다                          | O                     |
| PUT     | 서버에 요청 메시지의 본문을 저장한다                        | O                     |
| TRACE   | 메시지가 프록시 서버를 거쳐 서버에 도달하는 과정을 추적한다 | X                     |
| OPTIONS | 서버가 어떤 메서드를 수행할 수 있는지 확인한다              | X                     |
| DELETE  | 서버에서 문서를 제거한다                                    | X                     |

#### 상태 코드

메서드가 서버에게 무엇을 해야 하는지 말해주는 것처럼, 상태 코드는 클라이언트에게 무엇이 일어났는지 말해준다. 상태 코드는 응답의 시작줄에 위치한다. 예를 들어, `HTTP/1.0 200 OK` 라는 줄에서 상태 코드는 200이다.

 상태 코드들은 세 자리 숫자로 된 그들의 코드값을 기준으로 묶인다.

| 전체 범위 | 정의된 범위 | 분류            |
| --------- | ----------- | --------------- |
| 100-199   | 100-101     | 정보            |
| 200-299   | 200-206     | 성공            |
| 300-399   | 300-305     | 리다이렉션      |
| 400-499   | 400-415     | 클라이언트 에러 |
| 500-599   | 500-505     | 서버 에러       |

#### 사유 구절

사유 구절은 응답 시작줄의 마지막 구성요소다. 이것은 상태 코드에 대한 글로 된 설명을 제공한다. 사유 구절은, 애플리케이션 개발자들이 그들의 사용자에게 요청 중에 무슨 일이 일어났는지 알려주기 위해 넘겨줄 수 있는, 상태 코드의 사람이 이해하기 쉬운 버전이다.

#### 버전 번호

버전 번호는 HTTP/x.y 형식으로 요청과 응답 메시지 양쪽 모두에 기술된다. 이것은 HTTP 애플리케이션들이 자신이 따르는 프로토콜의 버전을 상대방에게 말해주기 위한 수단이 된다.

버전 번호는 어떤 애플리케이션이 지원하는 가장 높은 HTTP 버전을 가리킨다.

### 3.2.3 헤더

시작줄 다음에는 0개~여러 개의 HTTP 헤더가 온다.

HTTP 헤더 필드는 요청과 응답 메시지에 추가 정보를 더한다. 기본적으로 이름/값 쌍 목록이다.

#### 헤더 분류

- 일반 헤더: 요청과 응답 양쪽에 모두 나타날 수 있음
- 요청 헤더: 요청에 대한 부가 정보를 제공
- 응답 헤더: 응답에 대한 부가 정보를 제공
- Entity 헤더: 본문 크기와 콘텐츠, 혹은 리소스 그 자체를 서술
- 확장 헤더: 명세에 정의되지 않은 새로운 헤더

### 3.2.4 엔티티 본문

HTTP 메시지의 세 번째 부분은 선택적인 엔티티 본문이다. HTTP 메시지는 이미지, 비디오, HTML 문서, 소프트웨어 애플리케이션 등 여러 종류의 디지털 데이터를 실어 나를 수 있다.

## 3.3 메서드

### 3.3.1 안전한 메서드(Safe Method)

HTTP는 안전한 메서드라 불리는 메서드의 집합을 정의한다. GET과 HEAD 메서드는 안전하다고 할 수 있는데, 이는 GET이나 HEAD 메서드를 사용하는 HTTP 요청의 결과로 서버에 어떤 작용도 없음을 나타낸다.

### 3.3.2 GET

GET은 주로 서버에게 리소스를 달라고 요청하기 위해 쓰인다.

![image](https://user-images.githubusercontent.com/22893111/71712879-8d38f800-2e4a-11ea-8998-c96b8d6facd7.png)

### 3.3.3 HEAD

HEAD 메서드는 정확히 GET처럼 동작하지만, 서버는 응답으로 헤더만을 돌려준다. 엔티티 본문은 결코 반환되지 않는다. 이는 클라이언트가 리소스를 실제로 가져올 필요 없이 헤더만을 조사할 수 있도록 해준다. HEAD를 사용하면

- 리소스를 가져오지 않고도 그에 대해 무엇인가(타입이라거나)를 알아낼 수 있다.
- 응답의 상태 코드를 통해, 개체가 존재하는지 확인할 수 있다.
- 헤더를 확인하여 리소스가 변경되었는지 검사할 수 있다.

![image](https://user-images.githubusercontent.com/22893111/71712944-e1dc7300-2e4a-11ea-96de-0c0f2f95d8ac.png)

### 3.3.4 PUT

PUT 메서드는 서버에 문서를 쓴다. PUT 메서드의 의미는, 서버가 요청의 본문을 가지고 요청 URL의 이름대로 새 문서를 만들거나, 이미 URL이 존재한다면 본문을 사용해서 교체하는 것이다.

![image](https://user-images.githubusercontent.com/22893111/71713023-35e75780-2e4b-11ea-8b5c-2c5a76a6351e.png)

### 3.3.5 POST

POST 메서드는 서버에 입력 데이터를 전송하기 위해 설계되었다. 실제로, HTML 폼을 지원하기 위해 흔히 사용된다. 채워진 폼에 담긴 데이터는 서버로 전송되며, 서버는 이를 모아서 필요로 하는 곳에 보낸다.

![image](https://user-images.githubusercontent.com/22893111/71713071-6dee9a80-2e4b-11ea-843d-e065c3a5c69d.png)

### 3.3.6 TRACE

클라이언트가 어떤 요청을 할 때, 그 요청은 방화벽, 프록시, 게이트웨이 등의 애플리케이션을 통과할 수 있다. TRACE 메서드는 클라이언트에게 자신의 요청이 서버에 도달했을 때 어떻게 보이게 되는지 알려준다.

TRACE 요청은 목적지 서버에서 '루프백(loopback)' 진단을 시작한다. 요청 전송의 마지막 단계에 있는 서버는 자신이 받은 요청 메시지를 본문에 넣어 TRACE 응답을 되돌려준다. 클라이언트는 자신과 목적지 서버 사이에 모든 HTTP 애플리케이션의 요청/응답 연쇄를 따라가면서 자신이 보낸 메시지가 망가졌거나 수정되었는지, 만약 그렇다면 어떻게 변경되었는지 확인할 수 있다.

![image](https://user-images.githubusercontent.com/22893111/71713176-ef462d00-2e4b-11ea-9f52-3a5a6d4e5720.png)

### 3.3.7 OPTIONS

OPTIONS 메서드는 웹 서버에게 여러 가지 종류의 지원 범위에 대해 물어본다. 서버에게 특정 리소스에 대해 어떤 메서드가 지원되는지 물어볼 수 있다.

![image](https://user-images.githubusercontent.com/22893111/71713236-3502f580-2e4c-11ea-8cca-37a92becaf30.png)

### 3.3.8 DELETE

DELETE 메서드는 서버에게 요청 URL로 지정한 리소스를 삭제할 것을 요청한다.

![image](https://user-images.githubusercontent.com/22893111/71713267-582da500-2e4c-11ea-928a-bf51bcaca7b8.png)

## 3.4 상태 코드

상태 코드는 클라이언트에게 그들의 트랜잭션을 이해할 수 있는 쉬운 방법을 제공한다.

### 3.4.1 100-199: 정보성 상태 코드

정보성 상태 코드는 HTTP/1.1에서 도입되었다. 이들은 비교적 새로운 것이며, 복잡함을 감수할 만한 가치가 있는지에 대해 논란이 되고 있다. 그래서 책에 있는 간단한 표만 적어본다.

| 상태코드 | 사유 구절           | 의미                                                         |
| -------- | ------------------- | ------------------------------------------------------------ |
| 100      | Continue            | 요청의 시작 부분 일부가 받아들여졌으며, 클라이언트는 나머지를 계속 이어서 보내야 함을 의미한다. 이것을 보낸 후, 서버는 반드시 요청을 받아 응답해야 한다. |
| 101      | Switching Protocols | 클라이언트가 Upgrade 헤더에 나열한 것 중 하나로 서버가 프로토콜을 바꾸었음을 의미한다. |

### 3.4.2 200-299: 성공 상태 코드

| 상태 코드 | 사유 구절                     | 의미                                                         |
| --------- | ----------------------------- | ------------------------------------------------------------ |
| 200       | OK                            | 요청은 정상이고, 엔티티 본문은 요청된 리소스를 포함하고 있다 |
| 201       | Created                       | 서버 개체를 생성하라는 요청을 위한 것. 응답은, 생성된 리소스에 대한 최대한 구체적인 참조가 담긴 Location 헤더와 함께, 그 리소스를 참조할 수 있는 여러 URL을 엔티티 본문애 포함해야 한다. 서버는 상태 코드를 보내기에 앞서 반드시 객체를 생성해야 한다. |
| 202       | Accepted                      | 요청은 받아들여졌으나 서버는 아직 그에 대한 어떤 동작도 수행하지 않았다. 서버가 요청의 처리를 완료할 것인지에 대한 어떤 보장도 없다. 이것은 단지 요청이 받아들이기에 적법해 보인다는 의미일 뿐이다. 서버는 엔티티 본문에 요청에 대한 상태와 가급적이면 요청의 처리가 언제 완료될 것인지에 대한 추정도 포함해야 한다. |
| 203       | Non-Authoritative Information | 엔티티 헤더가 아닌 리소스의 사본에서 왔다. 중개자가 리소스의 사본을 갖고 잇었지만 리소스에 대한 메터타 정보(헤더)를 검증하지 못한 경우 이런 일이 발생할 수 있다.<br /> 이 응답 코드는 필수적으로 사용되어야 하는 것은 아니다. |
| 204       | No Content                    | 응답 메시지는 헤더와 상태줄을 포함하지만 엔티티 본문은 포함하지 않는다. 주로 웹브라우저를 새 문서로 이동시키지 않고 갱신하고자 할 때 사용한다. |
| 205       | Reset Content                 | 주로 브라우저를 위해 사용되는 또 하나의 코드. 브라우저에게 현재 페이지에 있는 HTML 폼에 채워진 모든 값을 비우라고 말한다. |
| 206       | Partial Content               | 부분 혹은 범위 요청이 성공했다. 206 응답은 Content-Range와 Date 헤더를 반드시 포함해야 하며, Etag와 Content-Location 중 하나의 헤더도 반드시 포함해야 한다. |

### 3.4.3 300-399: 리다이렉션 상태 코드

리다이렉션 상태 코드는 클라이언트가 관심있어 하는 리소스에 대해 다른 위치를 사용하라고 말해주거나 그 리소스의 내용 대신 다른 대안 응답을 제공한다. 만약 리소스가 옮겨졌다면, 클라리언트에게 리소스가 옮겨졌으며 어디서 찾을 수 있는지 알려주기 위해 리다이렉션 상태 코드와 Location 헤더를 보낼 수 잇다.

![image](https://user-images.githubusercontent.com/22893111/71715621-59170480-2e55-11ea-80a3-bd1bf1e53023.png)

| 상태 코드 | 사유 구절          | 의미                                                         |
| --------- | ------------------ | ------------------------------------------------------------ |
| 300       | Multiple Choices   | 클라이언트가 동시에 여러 리소스를 가리키는 URL을 요청한 경우, 그 리소스의 목록과 함께 반환한다. 사용자는 목록에서 원하는 하나를 선택할 수 있다. |
| 301       | Moved Permanently  | 요청한 URL이 옮겨졌을 때 사용한다. 응답은 Location 헤더에 현재 리소스가 존재하고 있는 URL을 포함해야 한다. |
| 302       | Found              | 301 상태 코드와 같다. 그러나 클라이언트는 Location 헤더로 주어진 URL을 임시로 가리키기 위한 목적으로 사용해야 한다. 이후의 요청에서는 원래 URL을 사용해야 한다. |
| 303       | See Other          | 클라이언트에게 리소스를 다른 URL에서 가져와야 한다고 말해주고자 할 때 쓰인다. 새 URL은 응답 메시지의 Location 헤더에 들어있다. 이 상태 코드의 주 목적은 POST 요청에 대한 응답으로 클라이언트에게 리소스의 위치를 알려주는 것이다. |
| 304       | Not Modified       | 클라이언트는 헤더를 이용해 조건부 요청을 만들 수 있다. 만약 클라이언트가 GET과 같은 조건부 요청을 보냈고 그 요청한 리소스가 최근에 수정된 일이 없다면, 이 코드는 리소스가 수정되지 않았음을 의미하게 된다. |
| 305       | Use Proxy          | 리소스가 반드시 프록시를 통해서 접근되어야 함을 나타내기 위해 사용한다. 프록시의 위치는 Location 헤더를 통해 주어진다. 클라이언트는 이 응답을 특정 리소스에 대한 것이라고만 해석한다. |
| 307       | Temporary Redirect | 301 상태 코드와 비슷하다. 그러나 클라이언트는 Location 헤더로 주어진 URL을 리소스를 임시로 가리키기 위한 목적으로 사용해야 한다. 이후의 요청에서는 원래의 URL을 사용해야 한다. |

### 3.4.4 400-499: 클라이언트 에러 상태 코드

| 상태 코드 | 사유 구절                     | 의미                                                         |
| --------- | ----------------------------- | ------------------------------------------------------------ |
| 400       | Bad Request                   | 클라이언트가 잘못된 요청을 보냈다고 말해준다                 |
| 401       | Unauthorized                  | 리소스를 얻기 전에 클라이언트에게 스스로를 인증하라고 요구하는 애용의 응답을 적절한 헤더와 함께 반환한다 |
| 402       | Payment Required              | 현재 이 상태 코드는 쓰이지 않지만, 미래에 사용될 가능성을 위해 준비해두었다 |
| 403       | Forbidden                     | 요청이 서버에 의해 거부되었음을 알려주기 위해 사용한다. 만약 서버가 왜 요청이 거부되었는지 알려주고자 한다면, 서버는 그 이유를 설명하는 엔티티 본문을 포함시킬 수 있다. |
| 404       | Not Found                     | 서버가 요청한 URL을 찾을 수 없음을 알려주기 위해 사용한다.   |
| 405       | Method Not Allowed            | 요청한 URL에 대해, 지원하지 않는 메서드로 요청받았을 때 사용한다. 요청한 리소스에 대해 어떤 메서드가 사용 가능한지 클라이언트에게 알려주기 위해, 요청에 Allow 헤더가 포함되어야 한다. |
| 406       | Not Acceptable                | 클라이언트는 자신이 어떤 종류의 엔티티를 받아들이고자 하는지에 대해 매개변수로 명시할 수 있다. 이 코드는 주어진 URL에 대한 리소스 중 클라이언트가 받아들일 수 있는 것이 없는 경우 사용한다. |
| 407       | Proxy Authentication Required | 401 상태 코드와 같으나, 리소스에 대해 인증을 요구하는 프록시 서버를 위해 사용된다. |
| 408       | Request Timeout               | 클라이언트의 요청을 완수하기에 시간이 너무 많이 걸리는 경우, 서버는 이 상태 코드로 응답하고 연결을 끊을 수 있다. |

이 외에도 409~417까지 상태코드가 있다.

### 3.4.5 500-599: 서버 에러 상태 코드

| 상태 코드 | 사유 구절                 | 의미                                                         |
| --------- | ------------------------- | ------------------------------------------------------------ |
| 500       | Internal Server Error     | 서버가 요청을 처리할 수 없게 만드는 에러를 만났을 때 사용한다 |
| 501       | Not Implemented           | 클라이언트가 서버의 능력을 넘는 요청을 했을 때 사용한다 (ex: 서버가 지원하지 않는 메서드를 사용) |
| 502       | Bad Gateway               | 프록시나 게이트웨이처럼 행동하는 서버가 그 요청 응답 연쇄에 있는 다음 링크로부터 가짜 응답에 맞닥뜨렸을 때 사용한다 (ex: 만약 자신의 부모 게이트웨이에 접속하는 것이 불가능할 때) |
| 503       | Service Unavailable       | 현재는 서버가 요청을 처리해 줄 수 없지만 나중에는 가능함을 의미하고자 할 때 사용한다. 만약 서버가 언제 그 리소스를 사용할 수 있게 될지 알고 있다면, 서버는 Retry-After 헤더를 응답에 포함시킬 수 있다. |
| 504       | Gateway Timeout           | 상태 코드 408과 비슷하지만, 다른 서버에게 요청을 보내고 응답을 기다리다 타임아웃이 발생한 게이트웨이나 프록시에서 온 응답이라는 점이 다르다. |
| 505       | HTTP Version No Supported | 서버가 지원할 수 없거나 지원하지 않으려고 하는 버전의 프로토콜로 된 요청을 받았을 때 사용한다. |

## 3.5 헤더

헤더와 메서드는 클라이언트와 서버가 무엇을 하는지 결정하기 위해 함께 사용된다. 이 절에서는 표준 HTTP 헤더와 명시적으로 HTTP/1.1 명세에 정의되지 않은 몇몇 헤더의 목적에 대해 간략히 설명한다.

헤더에는 특정 종류의 메시지에만 사용할 수 있는 헤더와, 더 일반 목적으로 사용할 수 있는 헤더, 그리고 응답과 요청 메시지 양쪽 모두에서 제공하는 헤더가 있다.

### 3.5.1 일반 헤더(General Headers)

일반 헤더는 클라이언트와 서버 양쪽 모두가 사용한다. 이들은 클라이언트, 서버, 그리고 어딘가에 메시지를 보내는 다른 애플리케이션들을 위해 다양한 목적으로 사용된다.

| 헤더                     | 설명                                                         |
| ------------------------ | ------------------------------------------------------------ |
| Connection               | 클라이언트와 서버가 요청/응답 연결에 대한 옵션을 정할 수 있게 해준다 |
| Date                     | 메시지가 언제 만들어졌는지에 대한 날짜와 시간을 제공한다     |
| MIME-Version             | 발송자가 사용한 MIME 버전을 알려준다                         |
| Trailer chunked transfer | 인코딩으로 인코딩된 메시지의 끝 부분에 위치한 헤더들의 목록을 나열한다 |
| Transfer-Encoding        | 수신자에게 안전한 전송을 위해 메시지에 어떤 인코딩이 적용되었는지 말해준다 |
| Upgrade                  | 발송자가 업그레이드 하길 원하는 새 버전이나 프로토콜을 알려준다 |
| Via                      | 이 메시지가 어떤 중개자(프록시, 게이트웨이)를 거쳐 왔는지 보여준다 |

#### 일반 캐시 헤더

HTTP/1.0은 HTTP 애플리케이션에게 매번 원 서버로부터 객체를 가져오는 대신 로컬 복사본으로 캐시할 수 있도록 해주는 최초의 헤더를 도입했다. 최신 버전의 HTTP는 매우 풍부한 캐시 매개변수의 집합을 가지고 있다. 기본적인 캐시 헤더를 알아보자.

| 헤더          | 설명                                                         |
| ------------- | ------------------------------------------------------------ |
| Cache-Control | 메시지와 함께 캐시 지시자를 전달하기 위해 사용한다           |
| Pragma        | 메시지와 함께 지시자를 전달하는 또 다른 방법. 캐시에 국한되지 않는다 |

### 3.5.2 요청 헤더

요청 헤더는 요청 메시지에서만 의미를 갖는다. 그들은 요청이 최초 발생한 곳에서 누가 혹은 무엇이 그 요청을 보냈는지에 대한 정보나 클라이언트의 선호나 능력에 대한 정보를 준다.

| 헤더       | 설명                                                |
| ---------- | --------------------------------------------------- |
| Client-IP  | 클라이언트가 실행된 컴퓨터의 IP를 제공한다          |
| From       | 클라이언트 사용자의 메일 주소를 제공한다            |
| Host       | 요청의 대상이 되는 서버의 호스트 명과 포트를 준다   |
| Referer    | 현재의 요청 URI가 들어있었던 문서의 URL을 제공한다  |
| User-Agent | 요청을 보낸 애플리케이션의 이름을 서버에게 말해준다 |

(이 외에도 UC-Color, UC-CPU, UA-OS 등이 있다)

#### Accept 관련 헤더

클라이언트는 Accept 관련 헤더들을 이용해 서버에게 자신의 선호와 능력을 알려줄 수 있다. 즉, 클라이언트가 무엇을 원하고 무엇을 할 수 있는지, 그리고 무엇보다도 원치 않는 것은 무엇인지 알려줄 수 있다. 클라이언트는 그들이 원하는 것을 얻을 수 있으며, 서버는 클라이언트가 사용할 수도 없는 것을 전송하는 데 시간과 대역폭을 낭비하지 않을 수 있다.

| 헤더            | 설명                                                  |
| --------------- | ----------------------------------------------------- |
| Accept          | 서버에게 서버가 보내도 되는 미디어 종류를 말해준다    |
| Accept-Charset  | 서버에게 서버가 보내도 되는 문자집합을 말해준다       |
| Accept-Encoding | 서버에게 서버가 보내도 되는 인코딩을 말해준다         |
| Accept-Language | 서버에게 서버가 보내도 되는 언어를 말해준다           |
| TE              | 서버에게 서버가 보내도 되는 확장 전송 코딩을 말해준다 |

#### 조건부 요청 헤더

클라이언트는 요청에 몇몇 제약을 넣기도 한다. 예를 들어, 클라이언트가 이미 어떤 문서의 사본을 갖고 있는 상태라면, 클라이언트는 서버에게 그 문서를 요청할 때 자신이 갖고 있는 사본과 다를 때만 전송해 달라고 요청하고 싶을 수 있을 것이다.

조건부 요청 헤더를 사용하면, 클라이언트는 서버에게 요청에 응답하기 전에 먼저 조건이 참인지 확인하게 하는 제약을 포함시킬 수 있다.

| 헤더                | 설명                                                         |
| ------------------- | ------------------------------------------------------------ |
| Expect              | 클라이언트가 요청에 필요한 서버의 행동을 열거할 수 있게 해준다 |
| If-Match            | 문서의 엔티티 태그가 주어진 엔티티 태그와 일치하는 경우에만 문서를 가져온다 |
| If-Modified-Since   | 주어진 날짜 이후에 리소스가 변경되지 않았다면 요청을 제한한다 |
| If-None-Match       | 문서의 엔티티 캐그가 주어진 엔티티 태그와 일치하지 않는 경우에만 문서를 가져온다 |
| If-Range            | 문서의 특정 범위에 대한 요청을 할 수 있게 해준다             |
| If-Unmodified_Since | 주어진 날짜 이후에 리소스가 변경되었다면 요청을 제한한다     |
| Range               | 서버가 범위 요청을 지원한다면, 리소스에 대한 특정 범위를 요청한다 |

#### 요청 보안 헤더

HTTP는 자체적으로 요청을 위한 간단한 인증요구/응답 체제를 갖고 있다. 그것은 요청하는 클라이언트가 어느 정도의 리소스에 접근하기 전에 자신을 인증하게 함으로써 트랜잭션을 약간 더 안전하게 만들고자 한다.

| 헤더          | 요청                                                         |
| ------------- | ------------------------------------------------------------ |
| Authorization | 클라이언트가 서버에게 제공하는 인증 그 자체에 대한 정보를 담고 있다 |
| Cookie        | 클라이언트가 서버에게 토큰을 전달할 때 사용한다.             |
| Cookie2       | 요청자가 지원하는 쿠키의 버전을 알려줄 때 사용한다           |

#### 프록시 요청 헤더

| 헤더                | 설명                                                         |
| ------------------- | ------------------------------------------------------------ |
| Max-Forwards        | 요청이 원 서버로 향하는 과정에서 다른 프록시나 게이트웨이로 전달될 수 있는 최대 횟수. TRACE 메서드와 함께 사용된다 |
| Proxy-Authorization | Authorization과 같으나 프록시에서 인증을 할 때 쓰인다        |
| Proxy-Connection    | Connection과 같으나 프록시에서 연결을 맺을 때 쓰인다         |

### 3.5.3 응답 헤더

응답 헤더는 클라이언트에게 부가 정보를 제공한다. 누가 응답을 보내고 있는지 혹은 응답자의 능력은 어떻게 되는지 알려주며, 더 나아가 응답에 대한 특별한 설명도 제공할 수 있다. 이 헤더들은 클라이언트가 응답을 잘 다루고 나중에 더 나은 요청을 할 수 있도록 도와준다.

| 헤더        | 설명                                                         |
| ----------- | ------------------------------------------------------------ |
| Age         | 응답이 얼마나 오래되었는지                                   |
| Public      | 서버가 특정 리소스에 대해 지원하는 요청 메서드의 목록        |
| Retry-After | 현재 리소스가 사용 불가능한 상태일 때, 언제 가능해지는지 날짜 또는 시각 |
| Server      | 서버 애플리케이션의 이름과 버전                              |
| Title       | HTML 문서에서 주어진 것과 같은 제목                          |
| Warning     | 사유 구절에 있는 것보다 더 자세한 경고 메시지                |

#### 협상 헤더

| 헤더          | 설명                                                         |
| ------------- | ------------------------------------------------------------ |
| Accept-Ranges | 서버에 자원에 대해 받아들일 수 있는 범위의 형태              |
| Vary          | 서버가 확인해 보아야 하고 그렇기 때문에 응답에 영향을 줄 수 있는 헤더들의 목록. ex) 서버가 클라이언트에게 보내줄 리소스의 가장 적절한 버전을 선택하기 위해 살펴보아야 하는 헤더들의 목록 |

#### 응답 보안 헤더

| 헤더               | 설명                                                         |
| ------------------ | ------------------------------------------------------------ |
| Proxy-Authenticate | 프록시에서 클라이언트로 보낸 인증요구의 목록                 |
| Set-Cookie         | 진짜 보안 헤더는 아니지만, 보안에 영향은 줄 수 있다. 서버가 클라이언트를 인증할 수 있도록 클라이언트 측에 토큰을 설정하기 위해 사용한다 |
| Set-Cookie2        | Set-Cookie와 비슷하게 RFC 2965로 정의된 쿠키                 |
| WWW-Authenticate   | 서버에서 클라이언트로 보낸 인증요구의 목록                   |

### 3.5.4 엔티티 헤더

엔티티 헤더는 엔티티와 그것의 내용물에 대한, 개체의 타입부터 시작해서 주어진 리소스에 대해 요청할 수 있는 유효한 메서드들까지, 광범위한 정보를 제공한다.

| 헤더     | 설명                                                         |
| -------- | ------------------------------------------------------------ |
| Allow    | 이 엔티티에 대해 수행될 수 있는 요청 메서드들을 나열한다     |
| Location | 클라이언트에게 엔티티가 실제로 어디에 위치하고 있는지 말해준다. 수신자에게 리소스에 대한 위치(URL)를 알려줄 때 사용한다 |

#### 콘텐츠 헤더

콘텐츠 헤더는 엔티티의 콘텐츠에 대한 구체적인 정보를 제공한다. 콘텐츠의 종류, 크기, 기타 콘텐츠를 처리할 때 유용하게 활용될 수 있는 것들이다.

| 헤더             | 설명                                                         |
| ---------------- | ------------------------------------------------------------ |
| Content-Base     | 본문에서 사용된 상대 URL을 계산하기 위한 기저 URL            |
| Content-Encoding | 본문에서 적용된 어떤 인코딩                                  |
| Content-Language | 본문을 이해하는데 가장 적절한 자연어                         |
| Content-Length   | 본문의 크기나 길이                                           |
| Content-Location | 리소스가 실제로 어디에 위치하는지                            |
| Content-MD5      | 본문의 MD5 체크섬(checksum)                                  |
| Content-Range    | 전체 리소스에서 이 엔티티가 해당하는 범위를 바이트 단위로 표현 |
| Content-Type     | 이 본문이 어떤 종류의 객체인지                               |

#### 엔티티 캐싱 헤더

일반 캐싱 헤더는 언제 어떻게 캐시가 되어야 하는지에 대한 지시자를 제공한다. 엔티티 캐싱 헤더는 엔티티 캐싱에 대한 정보를 제공한다. 예를 들면, 리소스에 대해 캐시된 사본이 아직 유효한지에 대한 정보와, 캐시된 리소스가 더 이상 유효하지 않게 되는 시점을 더 잘 추정하기 위한 단서 같은 것이다.

| 헤더          | 설명                                                        |
| ------------- | ----------------------------------------------------------- |
| ETag          | 이 엔티티에 대한 엔티티 태그                                |
| Expires       | 이 엔티티가 더 이상 유효하지 않아 원본을 받아와야 하는 일시 |
| Last-Modified | 가장 최근 이 엔티티가 변경된 일시                           |

<file_sep>/Article/2019-12/7.md
# 2019-12-07 토요일

## [사진 한 장으로 움직이는 얼굴 영상을 만든다고?](http://www.bloter.net/archives/363707)
- 글로벌 영상 메신저 ‘아자르’를 개발 운영 중인 하이퍼커넥트가 사진 한 장으로 실제같이 움직이는 얼굴 영상을 만들 수 있는 기술을 구현했다. 얼굴 사진 한 장만 있으면 해당 인물의 모습을 이용자에게 씌워 다양한 표정을 연출할 수 있게 되는 셈이다
- 기존에도 안면 재현 기술은 존재했지만, 재현하고자 하는 얼굴이 움직임을 부여하는 얼굴의 영향을 받아 부자연스럽게 변형되는 한계가 있었다. 또 고품질의 재현 영상을 만들려면 타깃의 얼굴과 윤곽이 비슷한 사람을 드라이버로 사용하거나, 각 타깃의 얼굴에 맞춰 딥러닝 모델을 추가로 학습시키는 등의 과정을 거쳐야 하는 탓에 기술 상용화가 어려웠다.
- 하이퍼커넥트는 독자 개발 기술을 적용해 문제를 해결했다. 우선, 사진에 움직임을 부여하는 드라이버의 얼굴에서 필요한 정보만 집중할 수 있는 구조(image attention block)를 딥러닝 모델에 적용했다. 또 얼굴에서 정체성을 나타내는 정보와 표정과 움직임을 나타내는 정보를 분리했다. 이를 통해 재현하고자 하는 얼굴에 드라이버의 표정 및 움직임 정보만 입히는 기술을 고안했다.
- 하이퍼커넥트 AI랩을 총괄하는 하성주 디렉터는 “기존 안면 재현 기술 연구는 구동 시간이 오래 걸리고 데이터가 많이 필요한 모델을 개발해 영상 품질을 높이거나, 모델의 속도를 높이기 위해 영상 품질 손실을 감수하는 방향으로 이뤄져 왔다”라며 “AI랩의 이번 연구는 영상 품질과 구현 속도를 동시에 높일 수 있는 획기적인 접근법을 제시해, 얼굴 재현 기술의 상용화 가능성을 열었다는 데 의의가 있고, 앞으로도 새로운 사람을 만나는 경험을 혁신할 수 있는 다양한 기술을 개발해, 연결을 통해 사회적·문화적 가치를 만들어간다는 하이퍼커넥트의 목표 실현에 기여하겠다”라고 밝혔다.
<file_sep>/Article/2019-12/10.md
# 2019-12-10 화요일

## [비밀번호 없는 세상을 꿈꾸는 ‘라인페이’](http://www.bloter.net/archives/363913)
- 지문인식이나 얼굴인식을 활용한 생체 인증 서비스, 페더레이션 기반 싱글사인온(SSO)과 같은 보안 기술이 등장했다. 이렇게 다양한 기술 가운데 신기은 엔지니어가 주목한 건 FIDO(Fast Identity Online) 인증이다.
- FIDO 인증은 사용자 단말기에 저장된 키(개인키)를 기반으로, 해당키를 단말이 보유하고 있는지를 인증 서버에서 검증하는 방식이다. 해당키는 단말 내부에 안전하게 저장되며, 다양한 기술로 보호가 되어 있기 때문에 해커가 인증 서버를 공격해 데이터를 탈취해도 안심할 수 있다. 다만, 지원 가능한 플랫폼이 제한적이다. FIDO 기술만으로 모든 플랫폼을 커버하기 힘들다.
- 신기은 엔지니어가 근무하는 라인은 이미 라인페이에 FIDO 기반 생체인증을 도입했다. iOS 사용자는 페이스ID나 터치ID를 활용해 인증, 인증 확인을 수행할 수 있다. 이 기능은 조만간 안드로이드 라인페이 앱에도 적용될 예정이다.
<file_sep>/Article/2019-12/15.md
# 2019-12-15

## [테라, ‘미미페이’로 몽골 간편결제 시장 문 두드려](http://www.bloter.net/archives/364421)

<file_sep>/Java/쓰레드.md

# 쓰레드

## 1. 프로세스와 쓰레드

프로세스(process)란 간단히 말해 '실행 중인 프로그램'이다. 프로그램을 실행하면 OS로부터 실행에 필요한 자원(메모리)을 할당받아 프로세스가 된다.

프로세스는 프로그램을 수행하는 데 필요한 데이터와 메모리 등의 자원 그리고 쓰레드로 구성되어 있으며 프로세스의 자원을 이용해서 실제로 작업을 수행하는 것이 바로 **쓰레드**이다.

그래서 모든 프로세스에는 최소한 하나 이상의 쓰레드가 존재하며, 둘 이상의 쓰레드를 가진 프로세스를 **'멀티쓰레드 프로세스'**라고 한다.

하나의 프로세스가 가질 수 있는 쓰레드의 개수는 제한되어 있지 않으나 쓰레드가 작업을 수행하는데 개별적인 메모리 공간(호출스택)을 필요로 하기 때문에 프로세스의 메모리 한계에 따라 생성할 수 있는 쓰레드의 수가 결정된다.

### 멀티태스킹과 멀티쓰레딩

윈도우나 유닉스를 포함한 대부분의 OS는 멀티태스킹(다중작업)을 지원하기 때문에 여러 개의 프로세스가 동시에 실행될 수 있다.

이와 마찬가지로 멀티쓰레딩은 하나의 프로세스 내에서 여러 쓰레드가 동시에 작업을 수행하는 것이다. CPU의 코어가 한 번에 단 하나의 작업만 수행할 수 있으므로, 실제로 동시에 처리되는 작업의 개수는 코어의 개수와 일치한다. 그러나 처리해야하는 쓰레드의 수는 언제나 코어의 개수보다 훨씬 많기 때문에 각 코어가 아주 짧은 기간 동안 여러 작업을 번갈아 가며 수행함으로써 여러 작업들이 모두 동시에 수행되는 것처럼 보이게 한다.

그래서 프로세스의 성능이 단순히 쓰레드의 개수에 비례하는 것은 아니며, 하나의 쓰레드를 가진 프로세스 보다 두 개의 쓰레드를 가진 프로세스가 오히려 더 낮은 성능을 보일 수 있다.

### 멀티쓰레딩의 장단점

장점

- CPU의 사용률을 향상시킨다.
- 자원을 보다 효율적으로 사용할 수 있다.
- 사용자에 대한 응답성이 향상된다.
- 작업이 분리되어 코드가 간결해진다.

단점

- 멀티쓰레드 프로세스는 여러 쓰레드가 같은 프로세스 내에서 자원을 공유하면서 작업을 하기 때문에 발생할 수 있는 동기화(synchronization), 교착상태(deadlock)와 같은 문제들을 고려해서 신중히 프로그래밍해야 한다.

## 2. 쓰레드의 구현과 실행

쓰레드를 구현하는 방법은 **Thread 클래스를 상속받는 방법**과 **Runnable 인터페이스를 구현하는 방법**, 모두 두 가지가 있다. 어느 쪽을 선택해도 별 차이는 없지만 Thread 클래스를 상속받으면 다른 클래스를 상속받을 수 없기 때문에, Runnable 인터페이스를 구현하는 방법이 일반적이다.

Runnable 인터페이스를 구현하는 방법은 재사용성(reusability)이 높고 코드의 일관성을 유지할 수 있기 때문에 보다 객체지향적인 방법이라 할 수 있다.

~~~ java
// Thread클래스를 상속
class MyThread extends Thread {
  public void run() {	// Thread클래스의 run()을 오버라이딩
     /*
     	작업 내용
     */
  }
}
~~~

~~~ java
// Runnable인터페이스를 구현
class MyThread implements Runnable {
  public void run() {	// Runnable인터페이스의 run()을 구현
    /*
    	작업 내용
    */
  }
}
~~~

Runnable 인터페이스는 오로지 run()만 정의되어 있는 간단한 인터페이스이다.

~~~ java
public interface Runnable {
  public abstract void run();
}
~~~

쓰레드를 생성하는 두 가지 방법을 코드로 보자.

~~~ java
class ThreadEx1 {
  public static void main (String args[]) {
    ThreadEx1_1 t1 = new ThreadEx1_1();

    Runnable r = new ThreadEx1_2();
    Thread t2 = new Thread(r);

    t1.start();
    t2.start();
  }
}

class ThreadEx1_1 extends Thread {
  public void run() {
    for (int i=0; i < 5; i++) {
      System.out.println(getName()); // 조상인 Thread의 getName()을 호출
    }
  }
}

class ThreadEx1_2 implements Runnable {
  public void run() {
    for(int i=0; i < 5; i++) {
      System.out.println(Thread.currentThread().getName());
    }
  }
}
~~~

Thread클래스를 상속받으면, 자손 클래스에서 조상인 Thread 클래스의 메서드를 직접 호출할 수 있지만, Runnable을 구현하면 Thread클래스의 static메서드인 currentThread()를 호출하여 쓰레드에 대한 참조를 얻어 와야만 호출이 가능하다.

~~~ java
static Thread currentThread(); // 현재 실행중인 쓰레드의 참조를 반환한다.
String getName(); // 쓰레드의 이름을 반환한다.
~~~

### 쓰레드의 실행 - start()

쓰레드를 생성했다고 해서 자동으로 실행되는 것이 아니라, start()를 호출해야 쓰레드가 실행된다.

사실은 start()가 호출되었다고 해서 바로 실행되는 것이 아니라, 일단 **실행대기 상태**에 있다가 자신의 차례가 되어야 실행된다.

그리고 **한 번 실행이 종료된 쓰레드는 다시 실행할 수 없다**. 즉, 하나의 쓰레드에 대해 start()가 한 번만 호출될 수 있다는 뜻이다.

만일 쓰레드의 작업을 한 번 더 수행해야 한다면 아래와 같이 새로운 쓰레드를 생성한 다음에 start()를 호출해야 한다.

~~~ java
ThreadEx1_1 t1 = new ThreadEx1_1();
t1.start();
t1 = new ThreadEx1_1();
t1.start();
~~~

## 3. start()와 run()

이제 `start()` 와 `run()`의 차이와 쓰레드가 실행되는 과정에 대해서 자세히 살펴보자.

main메서드에서 run()을 호출하는 것은 생성된 쓰레드를 실행시키는 것이 아니라 단순히 클래스에 선언된 메서드를 호출하는 것일 뿐이다.

![image](https://user-images.githubusercontent.com/22893111/74931219-7febc300-5422-11ea-95a9-90eba5691e66.png)

반면에 start()는 새로운 쓰레드가 작업을 실행하는데 필요한 호출스택(call stack)을 생성한 다음에 `run()`을 호출해서, 생성된 호출 스택에 `run()`이 첫 번째로 올라가게 한다.

모든 쓰레드는 독립적인 작업을 수행하기 위해 자신만의 호출스택을 필요로 하기 때문에, 새로운 쓰레드를 생성하고 실행시킬 때마다 새로운 호출스택이 생성되고 쓰레드가 종료되면 작업에 사용된 호출스택은 소멸된다.

![image](https://user-images.githubusercontent.com/22893111/74931468-01dbec00-5423-11ea-9285-c1d5f50cb726.png)

1. main 메서드에서 쓰레드의 start()를 호출한다.

![image](https://user-images.githubusercontent.com/22893111/74931536-220bab00-5423-11ea-8f6c-8597a347e7ae.png)

2. start()는 새로운 쓰레드를 생성하고, 쓰레드가 작업하는데 사용될 호출스택을 생성한다.

![image](https://user-images.githubusercontent.com/22893111/74931567-318af400-5423-11ea-883f-1e89112d579f.png)

3. 새로 생성된 호출스택에 run()이 호출되어, 쓰레드가 독립된 공간에서 작업을 수행한다.

![image](https://user-images.githubusercontent.com/22893111/74931577-38b20200-5423-11ea-9f87-31de1f4d219c.png)

4. 이제는 호출스택이 2개이므로 스케줄러가 정한 순서에 의해서 번갈아 가면서 실행된다.

호출 스택에서는 가장 위에 있는 메서드가 현재 실행중인 메서드이고 나머지 메서드들은 대기상태에 있다. 그러나 위의 그림에서와 같이 쓰레드가 둘 이상일 때는 호출스택의 최상위에 있는 메서드일지라도 대기상태에 있을 수 있다.

스케줄러는 실행대기중인 쓰레드들의 우선순위를 고려하여 실행순서와 실행시간을 결정하고, 각 쓰레드들은 작성된 스케줄에 따라 자신의 순서가 되면 지정된 시간동안 작업을 수행한다.

이 때 주어진 시간동안 작업을 마치지 못한 쓰레드는 다시 자신의 차례가 돌아올 때까지 대기상태에 있게 되며, 작업을 마친 쓰레드, 즉 run()의 수행이 종료된 쓰레드는 호출스택이 모두 비워지면서 이 쓰레드가 사용하던 호출스택은 사라진다.

이는 마치 자바프로그램을 실행하면 호출스택이 생성되고 main메서드가 처음으로 호출되고, main메서드가 종료되면 호출스택이 비워지면서 프로그램도 종료되는 것과 같다.

### main쓰레드

main 메서드의 작업을 수행하는 것도 쓰레드이며, 이를 mian쓰레드라고 한다.

~~~ java
public class ThreadEx2 {
    public static void main(String[] args) {
        ThreadEx2_1 t1 = new ThreadEx2_1();
        t1.start();
    }
}

class ThreadEx2_1 extends Thread {
    @Override
    public void run() {
        throwException();
    }

    private void throwException() {
        try {
            throw new Exception();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
~~~

실행 결과

~~~ java
java.lang.Exception
  	at ThreadEx2_1.throwException(ThreadEx2.java:15)
  	at ThreadEx2_1.run(ThreadEx2.java:10)
~~~

새로 생성한 쓰레드에서 고의로 예외를 발생시키고 printStatckTrace()를 이용해서 예외가 발생한 당시의 호출스택을 출력하는 예제다. 호출스택의 첫 번째 메서드가 main 메서드가 아니라 run 메서드인 것을 확인하자.

한 쓰레드가 예외가 발생해서 종료되어도 다른 쓰레드의 실행에는 영향을 미치지 않는다.

~~~ java
public class ThreadEx3 {
    public static void main(String[] args) {
        ThreadEx3_1 t1 = new ThreadEx2_1();
        t1.run();
    }
}

class ThreadEx3_1 extends Thread {
    @Override
    public void run() {
        throwException();
    }

    private void throwException() {
        try {
            throw new Exception();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
~~~

실행 결과

~~~ java
java.lang.Exception
  	at ThreadEx3_1.throwException(ThreadEx3.java:15)
  	at ThreadEx3_1.run(ThreadEx2.java:10)
  	at ThreadEx3.main(ThreadEx3.java:4)
~~~

이전 예제와 달리 쓰레드가 새로 생성되지 않았다. 그저 ThreadEx3_1 클래스의 run()이 호출되었을 뿐이다.

## 4. 싱글쓰레드와 멀티쓰레드

 두 개의 작업을 하나의 쓰레드(th1)로 처리하는 경우와 두 개의 쓰레드(th1, th2)로 처리하는 경우를 가정해보자. 하나의 쓰레드로 두 작업을 처리하는 경우는 한 작업을 마친 후에 다른 작업을 시작하지만, 두 개의 쓰레드로 작업 하는 경우에는 짧은 시간동안 2개의 쓰레드(th1, th2)가 번갈아 가면서 작업을 수행해서 동시에 두 작업이 처리되는 것과 같이 느끼게 한다.

![image](https://user-images.githubusercontent.com/22893111/75008298-08b63d80-54bb-11ea-99dd-6c59789857eb.png)

 위의 그래프에서 알 수 있듯이 하나의 쓰레드로 두개의 작업을 수행한 시간과 두개의 쓰레드로 두  개의 작업을 수행한 시간은 거의 같다. 오히려 두 개의 쓰레드로 작업한 시간이 싱글쓰레드로 작업한 시간보다 더 걸리게 되는데 그 이유는 쓰레드간의 작업 전환(Context Switching)에 시간이 걸리기 때문이다.

작업 전환을 할 때는 현재 진행 중인 작업의 상태, 예를 들면 다음에 실행해야할 위치(PC, 프로그램 카운터) 등의 정보를 저장하고 읽어 오는 시간이 소요된다. 참고로 쓰레드의 스위칭에 비해 프로세스의 스위칭이 더 많은 정보를 저장해야하므로 더 많은 시간이 소요된다.

두 쓰레드가 서로 다른 자원을 사용하는 작업의 경우에는 싱글쓰레드 프로세스보다 멀티쓰레드 프로세스가 더 효율적이다. 예를 들면 사용자로부터 데이터를 입력받는 작업, 네트워크로 파일을 주고받는 작업, 프린터로 파일을 출력하는 작업과 같이 외부기기와의 입출력을 필요로 하는 경우가 이에 해당한다.

## 5. 쓰레드의 우선순위

쓰레드는 **우선순위(priority)**라는 속성(멤버변수)을 가지고 있는데, 이 우선순위의 값에 따라 쓰레드가 얻는 실행시간이 달라진다. 쓰레드가 수행하는 **작업의 중요도에 따라 쓰레드의 우선순위를 서로 다르게 지정하여 특정 쓰레드가 더 많은 작업시간을 갖도록 할 수 있다.**

예를 들어 파일전송기능이 있는 메신저의 경우, 파일다운로드를 처리하는 쓰레드보다 채팅내용을 전송하는 쓰레드의 우선순위가 더 높아야 사용자가 채팅하는데 불편함이 없을 것이다. 대신 파일다운로드 작업에 걸리는 시간은 더 길어질 것이다.

이처럼 시각적인 부분이나 사용자에게 빠르게 반응해야하는 작업을 하는 쓰레드의 우선순위는 다른 작업을 수행하는 쓰레드에 비해 높아야 한다.

### 쓰레드의 우선순위 지정하기

~~~ java
void setPriority(int newPriority);	// 쓰레드의 우선순위를 지정한 값으로 변경한다.
int getPriority();	// 쓰레드의 우선순위를 반환한다.

public static final int MAX_PRIORITY = 10;	// 최대 우선순위
public static final int MIN_PRIORITY = 1;	// 최소 우선순위
public static final int NORM_PRIORITY = 5;	// 보통 우선순위
~~~

쓰레드가 가질 수 있는 우선순위의 범위는 1~10이며 숫자가 높을수록 우선순위가 높다.

한 가지 더 알아두어야 할 것은 **쓰레드의 우선순위는 쓰레드를 생성한 쓰레드로부터 상속받는다는 것**이다. main메서드를 수행하는 쓰레드는 우선순위가 5이므로 main메서드 내에서 생성하는 쓰레드의 우선순위는 자동적으로 5가 된다.

## 6. 쓰레드 그룹(thread group)

쓰레드 그룹은 **서로 관련된 쓰레드를 그룹으로 다루기 위한 것**으로, 폴더를 생성해서 관련된 파일들을 함께 넣어서 관리하는 것처럼 쓰레드 그룹을 생성해서 쓰레드를 그룹으로 묶어서 관리할 수 있다.

또한 폴더 안에 폴더를 생성할 수 잇듯이 쓰레드 그룹에 다른 쓰레드 그룹을 포함 시킬 수 있다. 사실 쓰레드 그룹은 보안상의 이유로 도입된 개념으로, 자신이 속한 쓰레드 그룹이나 하위 쓰레드 그룹은 변경할 수 있지만 다른 쓰레드 그룹의 쓰레드를 변경할 수는 없다. ThreadGroup을 사용해서 생성할 수 있으며, 주요 생성자와 메서드는 다음과 같다.

| 생성자 / 메서드                               | 설명                                                         |
| --------------------------------------------- | ------------------------------------------------------------ |
| ThreadGroup (String name)                     | 지정된 이름의 새로운 쓰레드 그룹을 생성                      |
| ThreadGroup (ThreadGroup parent, String name) | 지정된 쓰레드 그룹에 포함되는 새로운 쓰레드 그룹을 생성      |
| int activeCount()                             | 쓰레드 그룹에 포함된 활성상태에 있는 쓰레드의 수를 반환      |
| int activeGroupCount                          | 쓰레드 그룹에 포함된 활성상태에 있는 쓰레드 그룹의 수를 반환 |
| void checkAccess()                            | 현재 실행중인 쓰레드가 쓰레드 그룹을 변경할 권한이 있는지 체크. 만일 권한이 없다면 SecurityException을 발생 |
| void destroy()                                | 쓰레드 그룹과 하위 쓰레드 그룹까지 모두 삭제한다. 단, 쓰레드 그룹이나 하위 쓰레드 그룹이 비어있어야 한다. |
| int enumerate(Thread[] list)                  | 쓰레드 그룹에 속한 쓰레드 또는 하위 쓰레드 그룹의 목록을 지정된 배열에 담고 그 개수를 반환 |
| int getMaxPriority()                          | 쓰레드 그룹의 최대우선순위를 반환                            |
| String getName()                              | 쓰레드 그룹의 이름을 반환                                    |

쓰레드를 쓰레드 그룹에 포함시키려면 Thread의 생성자를 이용해야한다.

~~~ java
Thread(ThreadGroup group, String name);
Thread(ThreadGroup group, Runnable target);
Thread(ThreadGroup group, Runnable target, String name);
Thread(ThreadGroup group, Runnable target, String name, long stackSize);
~~~

모든 쓰레드는 반드시 쓰레드 그룹에 포함되어 있어야 하기 때문에, 위와 같이 쓰레드 그룹을 지정하는 생성자를 사용하지 않은 쓰레드는 기본적으로 자신을 생성한 쓰레드와 같은 쓰레드 그룹에 속하게 된다.

자바 어플리케이션이 실행되면, JVM은 main과 system이라는 쓰레드 그룹을 만들고 JVM운영에 필요한 쓰레드들을 생성해서 이 쓰레드 그룹에 포함시킨다. 예를 들어 main 메서드를 수행하는 main이라는 이름의 쓰레드는 main쓰레드 그룹에 속하고, 가비지컬렉션을 수행하는 Finalizer쓰레드는 system쓰레드 그룹에 속한다.

우리가 생성하는 모든 쓰레드 그룹은 main쓰레드 그룹의 하위 쓰레드 그룹이 되며, 쓰레드 그룹을 지정하지 않고 생성한 쓰레드는 자동적으로 main쓰레드 그룹에 속하게 된다.

그 외에 Thread의 쓰레드 그룹과 관련된 메서드는 다음과 같다.

~~~ java
ThreadGroup getThreadGroup();	// 쓰레드 자신이 속한 쓰레드 그룹을 반환한다.
void uncaughtException(Thread t, Throwable e);	// 쓰레드 그룹의 쓰레드가 처리되지 않은 예외에 의해 실행이 종료되었을 때, JVM에 의해 이 메서드가 자동적으로 호출된다.
~~~

## 7. 데몬 쓰레드(daemon thread)

데몬 쓰레드는 **다른 일반 쓰레드의 작업을 돕는 보조적인 역할을 수행하는 쓰레드**이다. 일반 쓰레드가 모두 종료되면 데몬 쓰레드는 강제적으로 자동 종료되는데, 그 이유는 데몬 쓰레드는 일반 쓰레드의 보조역할을 수행하므로 일반 쓰레드가 모두 종료되고 나면 데몬 쓰레드의 존재의 의미가 없기 때문이다.

이 점을 제외하고는 데몬 쓰레드와 일반 쓰레드는 다르지 않다. 데몬 쓰레드의 예로는 가비지 컬렉터, 워드프로세서의 자동저장, 화면자동갱신 등이 있다.

데몬 쓰레드는 무한루프와 조건문을 이용해서 실행 후 대기하고 있다가 특정 조건이 만족되면 작업을 수행하고 다시 대기하도록 작성한다.

데몬 쓰레드는 일번 쓰레드의 작성방법과 실행방법이 같으며 다만 쓰레드를 생성한 다음 실행하기 전에 `setDaemon(true)`를 호출하기만 하면 된다. 그리고 데몬 쓰레드가 생성한 쓰레드는 자동적으로 데몬 쓰레드가 된다는 점도 알아두자.

~~~ java
boolean isDaemon();	// 쓰레드가 데몬 쓰레드인지 확인한다.
										// 데몬 쓰레드이면 true를 반환한다.
void setDaemon(boolean on);	// 쓰레드를 데몬 쓰레드로 또는 사용자 쓰레드로 변경한다.
														// 매개변수 on의 값을 true로 지정하면 데몬 쓰레드가 된다.
~~~

~~~ java
class ThreadEx10 implements Runnable {
  static boolean autoSave = false;

  public static void main(String[] args) {
    Thread t = new Thread(new ThreadEx10());
    t.setDaemon(true);
    t.start();

    for (int i=1; i <= 10; i++) {
      try {
        Thread.sleep(1000);
      } catch(InterruptedException e) {
      }
      System.out.println(i);

      if(i==5) {
        autoSave = true;
      }
    }
  }

  public void run() {
    while(true) {
      try {
        Thread.sleep(3 * 1000);	// 3초마다
      }	catch(InterruptedException e) {
      }

      if(autoSave) {
        autoSave();
      }
    }
  }

  public void autoSave() {
    System.out.println("작업파일이 자동저장되었습니다.");
  }
}
~~~

~~~ markdown
// 실행결과
1
2
3
4
5
6
작업파일이 자동저장되었습니다.
7
8
작업파일이 자동저장되었습니다.
9
10
프로그램을 종료합니다.
~~~

3초마다 변수 autoSave의 값을 확인해서 그 값이 true이면, autoSave()를 호출해서 일을 무한히 반복하도록 쓰레드를 작성하였다. 만일 이 쓰레드를 데몬 쓰레드로 설정하지 않았다면, 이 프로그램은 강제종료하지 않는 한 영원히 종료되지 않을 것이다.

setDaemon메서드는 반드시 start()를 호출하기 전에 실행되어야 한다. 그렇지 않으면 IllegalThreadStateException이 발생한다.

## 8. 쓰레드의 실행 제어

쓰레드 프로그래밍이 어려운 이유는 동기화(synchronization)와 스케줄링(scheduling) 때문이다. 효율적인 멀티쓰레드 프로그램을 만들기 위해서는 보다 정교한 스케줄링을 통해 프로세스에게 주어진 자원과 시간을 여러 쓰레드가 낭비없이 잘 사용하도록 프로그래밍 해야 한다.

쓰레드의 스케줄링을 잘하기 위해서는 쓰레드의 상태와 관련 메서드를 잘 알아야 하는데, 먼저 쓰레드의 스케줄링과 관련된 메서드는 다음과 같다.

| 메서드                                                       | 설명                                                         |
| ------------------------------------------------------------ | ------------------------------------------------------------ |
| static void sleep(long miilis)<br />static void sleep(long millis, int nanos) | 지정된 시간동안 쓰레드를 일시정지 시킨다. 지정한 시간이 지나고 나면 자동적으로 다시 실행대기상태가 된다. |
| void join()<br />void join(long millis)<br />void join(long millis, int nanos) | 지정된 시간동안 쓰레드가 실행되도록 한다. 지정된 시간이 지나거나 작업이 종료되면 join()을 호출한 쓰레드로 다시 돌아와 실행을 계속한다. |
| void interrupt()                                             | sleep()이나 join()에 의해 일시정지상태인 쓰레드를 깨워서 실행대기상태로 만든다. 해당 쓰레드에서는 InterruptedException이 발생함으로써 일시정지상태를 벗어나게 된다. |
| void stop()                                                  | 쓰레드를 즉시 종료시킨다.                                    |
| void suspend()                                               | 쓰레드를 일시 정지시킨다. resume()을 호출하면 다시 실행대기상태가 된다. |
| void resume()                                                | suspend()에 의해 일시정지상태에 있는 쓰레드를 실행대기상태로 만든다. |
| static void yield()                                          | 실행 중에 자신에게 주어진 실행시간을 다른 쓰레드에게 양보(yield)하고 자신은 실행대기상태가 된다. |

위 메서드들은 예제를 통해 자세히 학습할 것이고, 이제 쓰레드의 상태에 대해서 알아보자.

| 상태                   | 설명                                                         |
| ---------------------- | ------------------------------------------------------------ |
| NEW                    | 쓰레드가 생성되고 아직 start()가 호출되지 않은 상태          |
| RUNNABLE               | 실행 중 또는 실행 가능한 상태                                |
| BLOCKED                | 동기화블럭에 의해서 일시정지된 상태(lock이 풀릴 때까지 기다리는 상태) |
| WAITING, TIMED_WAITING | 쓰레드의 작업이 종료되지는 않았지만 실행가능하지 않은(unrunnable) 일시정지 상태. TIME_WAITING은 일시정지시간이 지정된 경우를 의미한다. |
| TERMINATED             | 쓰레드의 작업이 종료된 상태                                  |

![image](https://user-images.githubusercontent.com/22893111/75136614-934fa480-5728-11ea-8dac-ae6452af0b37.png)

1. 쓰레드를 생성하고 `start()`를 호출하면 바로 실행되는 것이 아니라 실행대기열에 저장되어 자신의 차례가 될 때까지 기다려야 한다. 실행대기열은 큐(queue)와 같은 자료 구조로 먼저 실행대기열에 들어온 쓰레드가 먼저 실행된다.
2. 실행대기상태에 있다가 자신의 차례가 되면 실행상태가 된다.
3. 주어진 실행시간이 다되거나 `yield()`를 만나면 다시 실행대기상태가 되고 다음 차례의 쓰레드가 실행상태가 된다.
4. 실행 중에 `suspend()`, `sleep()`, `wait()`, `join()`, I/O block에 의해 일시정지상태가 될 수 있다. I/O block은 입출력작업에서 발생하는 지연상태를 말한다. 사용자의 입력을 기다리는 경우를 예로 들 수 있는데, 이런 경우 일시정지 상태에 있다가 사용자가 입력을 마치면 다시 실행대기상태가 된다.
5. 지정된 일시정지시간이 다되거나(time-out), `notify()`, `resume()`, `interrupt()`가 호출되면 일시정지상태를 벗어나 다시 실행대기열에 저장되어 자신의 차례를 기다리게 된다.
6. 실행을 모두 마치거나 `stop()`이 호출되면 쓰레드는 소멸된다.

### `sleep(long millis)` - 일정시간동안 쓰레드를 멈추게 한다.

`sleep()`은 지정된 시간동안 쓰레드를 멈추게 한다.

~~~ java
static void sleep(long millis);
static void sleep(long millis, int nanos);
~~~

예를 들어, 쓰레드가 0.0015초 동안 멈추게 하려면 다음과 같이 한다.

~~~ java
try {
  Thread.sleep(1, 500000);
} catch(InterruptedException e) {}
~~~

`sleep()`에 의해 일시정지 상태가 된 쓰레드는 시간이 다 되거나 interrupt()가 호출되면(InterruptedException발생), 잠에서 깨어나 실행대기 상태가 된다.

 그래서 `sleep()`을 호출할 때는 항상 `try-catch`문으로 예외를 처리해줘야 한다. 매번 예외처리를 해주는 것이 번거롭기 때문에, 아래와 같이 `try-catch`를 포함하는 새로운 메서드를 만들어서 사용하기도 한다.

~~~ java
void delay(long millis) {
  try {
    Thread.sleep(millis);
  } catch (InterruptedException e) {}
}
~~~

### `interrupt()`와 `interrupted()` - 쓰레드의 작업을 취소한다.

진행 중인 쓰레드의 작업이 끝나기 전에 취소시켜야 할 때가 있다. 예를 들어 큰 파일을 다운로드 받을 때 시간이 너무 오래 걸리면 중간에 다운로드를 포기하고 취소할 수 있어야 한다. `interrupt()`는 쓰레드에게 작업을 멈추라고 요청한다. 단지 멈추라고 요청만 하는 것일 뿐 쓰레드를 강제로 종료시키지는 못한다. `interrupt()`는 그저 쓰레드의 interrupted상태(인스턴스 변수)를 바꾸는 것일 뿐이다.

그리고 `interrupted()`는 쓰레드에 대해 `interrupt()`가 호출되었는지 알려준다. `interrupt()`가 호출되지 않았다면 false를, 호출되었다면 true를 반환한다.

`isInterrupted()`도 쓰레드의 `interrupt()`가 호출되었는지 확인하는데 사용할 수 있지만, `interrupted()`와 달리 `isInterrupted()`는 쓰레드의 interrupted상태를 false로 초기화하지 않는다.

~~~ java
void interrupt();	// 쓰레드의 interrupted 상태를 false에서 true로 변경
boolean isInterrupted();	// 쓰레드의 interrupted상태를 반환
static boolean interrupted();	// 현재 쓰레드의 interrupted상태를 반환 후, false로 변경
~~~

쓰레드가 sleep(), wait(), join()에 의해 '일시정지 상태(WAITING)'에 있을 때, 해당 쓰레드에 대해 interrupt()를 호출하면, sleep(), wait(), join()에서 InterruptedException이 발생하고 쓰레드는 '실행대기 상태(RUNNABLE)'로 바뀐다. 즉, 멈춰있던 쓰레드를 깨워서 실행가능한 상태로 만드는 것이다.

### `suspend()`, `resume()`, `stop()`

`suspend()`는 `sleep()`처럼 쓰레드를 멈추게 한다. `suspend()`에 의해 정지된 쓰레드는 `resume()`을 호출해야 다시 실행대기 상태가 된다. `stop()`은 호출되는 즉시 쓰레드가 종료된다.

`suspend()`, `resume()`, `stop()`은 쓰레드의 실행을 제어하는 가장 손쉬운 방법이지만,  `suspend()`와 `stop()`이 **교착상태(deadlock)을 일으키기 쉽게 작성되어있으므로 사용이 권장되지 않는다.** 그래서 이 메서드들은 모두 'deprecated'되었다.

### `yield()` - 다른 쓰레드에게 양보한다

`yield()`는 쓰레드 자신에게 주어진 실행시간을 다음 차례의 쓰레드에게 양보(yield)한다.

예를 들어 스케쥴러에 의해 1초의 실행시간을 할당받은 쓰레드가 0.5초의 시간동안 작업한 상태에서 `yield()`가 호출되면, 나머지 0.5초는 포기하고 다시 실행대기 상태가 된다.

`yield()`와 `interrupt()`를 적절히 사용하면, 프로그램의 응답성을 높이고 보다 효율적인 실행이 가능하게 할 수 있다.

~~~ java
// 1번
while(!stopped) {
  if(!suspended) {
    ...
    try {
      Thread.sleep(1000);
    } catch (InterruptedException e) {}
  }
}
~~~

~~~ java
// 2번
while(!stopped) {
  if(!suspended) {
    ...
    try {
      Thread.sleep(1000);
    } catch (InterruptedException e) {}
  }
  else {
    Thread.yield();
  }
}
~~~

1번 코드에서 만일 suspended의 값이 true라면, 즉 잠시 실행을 멈추게 한 상태라면, 쓰레드는 주어진 실행시간을 그저 while문을 의미없이 돌면서 낭비하게 될 것이다. 이런 상황을 **'바쁜 대기상태(busy-waiting)'**이라고 한다.

그러나 2번 코드는, 같은 경우에 `yield()`를 호출해서 남은 실행시간을 while문에서 낭비하지 않고 다른 쓰레드에게 양보(yield)하게 되므로 더 효율적이다.

### `join()` - 다른 쓰레드의 작업을 기다린다.

쓰레드 **자신이 하던 작업을 잠시 멈추고 다른 쓰레드가 지정된 시간동안 작업을 수행하도록 할 때 `join()`을 사용**한다.

~~~ java
void join();
void join(long millis);
void join(long millis, int nanos);
~~~

시간을 지정하지 않으면, 해당 쓰레드가 작업을 모두 마칠 때까지 기다리게 된다. **작업 중에 다른 쓰레드의 작업이 먼저 수행되어야할 필요가 있을 때 `join()`을 사용한다.**

~~~ java
try {
  th1.join();	// 현재 실행중인 쓰레드가 쓰레드 th1의 작업이 끝날때까지 기다린다.
} catch (InterruptedException e) {}
~~~

`join()`도 `sleep()`처럼 `interrupt()`에 의해 대기상태에서 벗어날 수 있으며, `join()`이 호출되는 부분을 try-catch문으로 감싸야 한다. `join()`은 여러모로 `sleep()`과 유사한 점이 많은데, `sleep()`과 다른 점은 `join()`은 현재 쓰레드가 아닌 특정 쓰레드에 대해 동작하므로 static메서드가 아니라는 것이다.

## 9. 쓰레드의 동기화

싱글쓰레드 프로세스의 경우 프로세스 내에서 단 하나의 쓰레드만 작업하기 때문에 프로세스의 자원을 가지고 작업하는데 별문제 없지만, 멀티쓰레드 프로세스의 경우 여러 쓰레드가 같은 프로세스 내의 자원을 공유해서 작업하기 때문에 서로의 작업에 영향을 주게된다. 만일 쓰레드A가 작업하던 도중에 다른 쓰레드B에게 제어권이 넘어갔을 때, 쓰레드A가 작업하던 공유데이터를 쓰레드B가 임의로 변경하였다면, 다시 쓰레드A가 제어권을 받아서 나머지 작업을 마쳤을 때 원래 의도했던 것과는 다른 결과를 얻을 수 있다.

이러한 일이 발생하는 것을 방지하기 위해서 한 쓰레드가 특정 작업을 끝마치기 전까지 다른 쓰레드에 의해 방해받지 않도록 하는 것이 필요하다. 그래서 도입된 개념이 **'임계 영역(critical section)'**과 **'잠금(락, lock)'**이다.

공유 데이터를 사용하는 코드 영역을 임계 영역으로 지정해놓고, 공유 데이터(객체)가 가지고 있는 lock을 획득한 단 하나의 쓰레드만이 이 영역 내의 코드를 수행할 수 있게 한다. 그리고 해당 쓰레드가 임계 영역 내의 모든 코드를 수행하고 벗어나서 lock을 반납해야만 다른 쓰레드가 반납된 lock을 획득하여 임계 영역 코드를 수행할 수 있게 된다.

이처럼 **한 쓰레드가 진행 중인 작업을 다른 쓰레드가 간섭하지 못하도록 막ㅈ는 것을 '쓰레드의 동기화(synchronization)'**라고 한다.

자바에서는 synchronized블럭을 이용해서 쓰레드의 동기화를 지원했지만, JDK1.5부터는 'java.util.concurrent.locks'와 'java.util.concurrent.atomic' 패키지를 통해서 다양한 방식으로 동기화를 구현할 수 있도록 지원하고 있다.

### 9.1 synchronized를 이용한 동기화

먼저 가장 간단한 동기화 방법인 synchronized 키워드를 이용한 동기화에 대해서 알아보자. 이 키워드는 임계 영역을 설정하는데 사용되며 두 가지 방식이 있다.

~~~ java
// 1. 메서드 전체를 임계 영역으로 지정
public synchronized void calcSum() {
  //...
}

// 2. 특정한 영역을 임계 영역으로 지정
synchronized (객체의 참조변수) {
  //...
}
~~~

첫 번째 방법은 메서드 앞에 `synchronized`를 붙이는 것인데, `synchronized`를 붙이면 메서드 전체가 임계 영역으로 설정된다. 쓰레드는 **synchronized메서드가 호출된 시점부터 해당 메서드가 포함된 객체의 lock을 얻어 작업을 수행하다가 메서드가 종료되면 lock을 반환**한다.

두 번째 방법은 메서드 내의 코드 일부를 블럭{}으로 감싸고 블럭 앞에 'synchronized(참조변수)'를 붙이는 것인데, 이때 참조변수는 락을 걸고자 하는 객체를 참조하는 것이어야 한다. 이 블럭을 synchronized블럭이라고 부르며, 이 블럭의 영역 안으로 들어가면서부터 쓰레드는 지정된 객체의 lock을 얻게 되고, 이 블럭을 벗어나면 lock을 반납한다.

두 방법 모두 lock의 획득과 반납이 모두 자동적으로 이루어지므로 우리가 해야 할 일은 임계 영역만 설정해주는 것뿐이다.

**임계 영역은 멀티쓰레드 프로그램의 성능을 좌우하기 때문에 가능하면 메서드 전체에 락을 거는 것보다 synchronized블럭으로 임계 영역을 최소화**해서 보다 효율적인 프로그램이 되도록 노력해야 한다.

~~~ java
public void withdraw (int money) {
  if(balance >= money) {
    try {
      Thread.sleep(1000);
    } catch (Exception e) {}
    balance -= money;
  }
}
~~~

위는 은행계좌(account)에서 잔고(balance)를 확인하고 임의의 금액을 출금(withdraw)하는 메서드의 예이다. 잔고가 출금하려는 금액보다 큰 경우에만 출금하도록 되어 있지만 실제로 멀티쓰레드로 실행해보면 잔고가 음수인 것을 볼 수 있다. 이유는 한 쓰레드가 if문의 조건식을 통과하고 출금하기 바로 직전에 다른 쓰레드가 끼어들어서 출금을 먼저 했기 때문이다.

따라서 동기화를 하기 위해 synchronized키워드를 붙여야 한다. 이 메서드 전체에 synchronized키워드를 붙이는 방법, 내부에 synchronized블럭을 사용하는 방법을 보자.

~~~ java
public synchronized void withdraw (int money) {
  if(balance >= money) {
    try {
      Thread.sleep(1000);
    } catch (Exception e) {}
    balance -= money;
  }
}
~~~

~~~ java
public void withdraw (int money) {
  synchronized(this) {
    if(balance >= money) {
      try {
        Thread.sleep(1000);
      } catch (Exception e) {}
      balance -= money;
    }
  }
}
~~~

### 9.2 `wait()`와 `notify()`

synchronized로 동기화해서 공유 데이터를 보호하는 것 까지는 좋은데, 특정 쓰레드가 객체의 락을 가진 상태로 오랜 시간을 보내지 않도록 하는 것도 중요하다. 만일 계좌에 출금할 돈이 부족해서 한 쓰레드가 락을 보유한 채로 돈이 입금될 때까지 오랜 시간을 보낸다면, 다른 쓰레드들은 모두 해당 객체의 락을 기다리느라 다른 작업들도 원활히 진행되지 않을 것이다.

이러한 상황을 개선하기 위해 고안된 것이 바로 `wait()`와 `notify()`이다. **동기화된 임계 영역의 코드를 수행하다가 작업을 더 이상 진행할 상황이 아니면, 일단 `wait()`를 호출하여 쓰레드가 락을 반납하고 기다리게 된다.** 그러면 다른 쓰레드가 락을 얻어 해당 객체에 대한 작업을 수행할 수 있게 된다. 나중에 작업을 진행할 수 있는 상황이 되면 `notify()`를 호출해서, 작업을 중단했던 쓰레드가 다시 락을 얻어 작업을 진행할 수 있게 한다.

이는 마치 빵을 사려고 빵집 앞에 줄을 서있는 것과 유사한데, 자신의 차례가 되었는데도 자신이 원하는 빵이 나오지 않았으면, 다음 사람에게 순서를 양보하고 기다리다가 자신이 원하는 빵이 나오면 통보를 받고 빵을 사가는 것이다.

차이가 있다면, 오래 기다린 쓰레드가 락을 얻는다는 보장이 없다는 것이다. `wait()`이 호출되면, 실행중이던 쓰레드는 해당 객체의 대기실(waiting pool)에서 통지를 기다린다. `notify()`가 호출되면, 해당 객체에 대기실에 있던 모든 쓰레드 중에서 임의의 쓰레드만 통지를 받는다. `notifyAll()`은 기다리고 있는 모든 쓰레드에게 통보를 하지만, 그래도 lock을 얻을 수 있는 것은 하나의 쓰레드일 뿐이고 나머지 쓰레드는 통보를 받긴 하겠지만, lock을 얻지 못하면 다시 lock을 기다리는 신세가 된다.

`wait()`와 `notify()`는 특정 객체에 대한 것이므로 Object클래스에 정의되어 있다.

~~~ java
void wait();
void wait(long timeout);
void wait(long timeout, int nanos);
void notify();
void notifyAll();
~~~

waiting pool은 객체마다 존재하는 것이므로 `notifyAll()`이 호출된다고 해서 모든 객체의 waiting pool에 있는 쓰레드가 깨워지는 것은 아니다. `notifyAll()`이 호출된 객체의 waiting pool에 대기 중인 쓰레드만 해당된다는 것을 기억하자.

> **wait(), notify(), notifyAll()**
>
> - Object에 정의되어 있다.
> - 동기화 블록(synchronized블록) 내에서만 사용할 수 있다.
> - 보다 효율적인 동기화를 가능하게 한다.

### 9.3 Lock과 Condition을 이용한 동기화

동기화할 수 있는 방법은 synchronized블럭 외에도 'java.util.concurrent.locks'패키지가 제공하는 lock클래스들을 이용하는 방법이 있다.

synchronized블럭으로 동기화를 하면 자동적으로 lock이 잠기고 풀리기 때문에 편리하다. 심지어 synchronized블럭 내에서 예외가 발생해도 lock은 자동적으로 풀린다. 그러나 때로는 같은 메서드 내에서만 lock을 걸 수 있다는 제약이 불편하기도 하다. 그럴 때 이 lock클래스를 사용한다. lock클래스의 종류는 다음과 같이 3가지가 있다.

> ReentrantLock: 재진입이 가능한 lock. 가장 일반적인 배타 lock
>
> ReentrantReadWriteLock: 읽기에는 공유적이고, 쓰기에는 배타적인 lock
>
> StampedLock: ReentrantReadWriteLock에 낙관적인 lock의 기능을 추가

ReentrantLock은 가장 일반적인 lock이다. 'reentrant(재진입할 수 있는)'이라는 단어가 앞에 붙은 이유는 우리가 앞서 wait() & notify()에서 배운 것처럼, 특정 조건에서 lock을 풀고 나중에 다시 lock을 얻고 임계영역으로 들어와서 이후의 작업을 수행할 수 있기 때문이다.

ReentrantReadWriteLock은 읽기를 위한 lock과 쓰기를 위한 lock을 제공한다. ReentrantLock은 배타적인 lock이라서 무조건 lock이 있어야만 임계영역의 코드를 수행할 수 있지만, ReentrantReadWriteLock은 읽기 lock이 걸려있으면, 다른 쓰레드가 읽기 lock을 중복해서 걸고 읽기를 수행할 수 있다. 읽기는 내용을 변경시키지 않으므로 동시에 여러 쓰레드가 읽어도 문제가 되지 않는다. 그러나 읽기 lock이 걸린 상태에서 쓰기 lock을 거는 것은 허용되지 않는다. 반대의 경우도 마찬가지다. 읽기를 할 때는 읽기 lock을 걸고, 쓰기 할 때는 쓰기 lock을 거는 것일 뿐 lock을 거는 방법은 같다.

StampedLock은 lock을 걸거나 해지할 때 '스탬프(long타입 정수값)'를 사용하며, 읽기와 쓰기를 위한 lock외에 '낙관적 읽기 lock(optimistic reading lock)'이 추가된 것이다. 읽기 lock이 걸려있으면, 쓰기 lock을 얻기 위해서는 읽기 lock이 풀릴 때까지 기다려야하는데 비해 '낙관적 읽기 lock'은 쓰기 lock에 의해 바로 풀린다. 그래서 낙관적 읽기에 실패하면, 읽기 lock을 얻어서 다시 읽어 와야 한다. **무조건 읽기 lock을 걸지 않고, 쓰기와 읽기가 충돌할 때만 쓰기가 끝난 후에 읽기 lock을 거는 것**이다.

StampedLock을 이용한 낙관적 읽기의 예

~~~ java
int getBalance() {
  long stamp = lock.tryOptimisticRead();	// 낙관적 읽기 lock을 건다

  int curBalance = this.balance;	// 공유 데이터인 balance를 읽어온다

  if(!lock.validate(stamp)) {	// 쓰기 lock에 의해 낙관적 읽기 lock이 풀렸는지 확인
    stamp = lock.readLock();	// lock이 풀렸으면, 읽기 lock을 얻으려고 기다린다

    try {
      curBalance = this.balance;	// 공유 데이터를 다시 읽어온다
    } finally {
      lock.unlockRead(stamp);	// 읽기 lock을 푼다
    }
  }

  return curBalance;	// 낙관적 읽기 lock이 풀리지 않았으면 곧바로 읽어온 값을 반환
}
~~~

#### ReentrantLock의 생성자

~~~ java
ReentrantLock();
ReentrantLock(boolean fair);
~~~

생성자 매개변수를 true로 주면, lock이 풀렸을 때 가장 오래 기다린 쓰레드가 lock을 획득할 수 있게, 즉 공정(fair)하게 처리한다. 그러나 공정하게 처리하려면 어떤 쓰레드가 가장 오래 기다렸는지 확인하는 과정을 거칠 수밖에 없으므로 성능은 떨어진다. 대부분의 경우 굳이 공정하게 처리하지 않아도 문제가 되지 않으므로 공정함보다 성능을 선택한다.

~~~ java
void lock();	// lock을 잠근다
void unlock();	// lock을 해지한다
boolean isLocked();	// lock이 잠겼는지 확인한다
~~~

자동적으로 lock의 잠금과 해제가 관리되는 synchronized블럭과 달리, ReentrantLock과 같은 lock클래스들은 수동으로 lock을 잠그고 해제해야 한다.

~~~ java
synchronized(lock) {
  // 임계영역
}
~~~

~~~ java
lock.lock();
// 임계영역
lock.unlock();
~~~

임계 영역 내에서 예외가 발생하거나 return문으로 빠져 나가게 되면 lock이 풀리지 않을 수 있으므로 unlock()은 try-finally문으로 감싸는 것이 일반적이다.

~~~ java
lock.lock(); // ReentrantLock lock = new ReentrantLock();
try {
  // 임계 영역
} finally {
  lock.unlock();
}
~~~

이 외에도 tryLock()이라는 메서드가 있는데, 이 메서드는 lock()과 달리, 다른 쓰레드에 의해 lock이 걸려 있으면 lock을 얻으려고 기다리지 않는다. 또는 지정된 시간만큼만 기다린다. lock을 얻으면 true를 반환하고, 얻지 못하면 false를 반환한다.

~~~ java
boolean tryLock();
boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException
~~~

#### ReentrantLock과 Condition

앞서 wait() & notify() 예제에 요리사 쓰레드와 손님 쓰레드를 구분해서 통지하지 못한다는 단점을 기억할 것이다. Condition은 이 문제점을 해결하기 위한 것이다.

wait() & notify()로 쓰레드의 종류를 구분하지 않고, 공유 객체의 waiting pool에 같이 몰아넣는 대신, 손님 쓰레드를 위한 Condition과 요리사 쓰레드를 위한 Condition을 만들어서 각각의 waiting pool에서 따로 기다리도록 하면 문제는 해결된다.

### 9.4 volatile

싱글 코어 프로세서가 장착된 컴퓨터에서는 앞의 예제가 아무런 문제없이 실행될 것이다. 그러나 요즘엔 대부분 멀티 코어 프로세서가 장착된 컴퓨터를 사용하기 때문에 이 예제에서 문제가 발생할 가능성이 있다. 그 이유는 멀티 코어 프로세서에서는 코어마다 별도의 캐시를 가지고 있기 때문이다.

![image](https://user-images.githubusercontent.com/22893111/76160588-c0f80d00-616e-11ea-83fa-19b5bdf557c0.png)

코어는 메모리에서 읽어온 값을 캐시에 저장하고 캐시에서 값을 읽어서 작업한다. 다시 같은 값을 읽어올 때는 먼저 캐시에 있는지 확인하고 없을 때만 메모리에서 읽어온다.

 그러다보니 도중에 메모리에 저장된 변수의 값이 변경되었는데도 캐시에 저장된 값이 갱신되지 않아서 메모리에 저장된 값이 다른 경우가 발생한다. 그래서 변수 stopped의 값이 바뀌었는데도 쓰레드가 멈추지 않고 계속 실행되는 것이다.

~~~ java
boolean suspended = false;  ->  volatile boolean suspended = false;
~~~

그러나 위의 오른쪽과 같이 변수 앞에 volatile을 붙이면, 코어가 변수의 값을 읽어올 때 캐시가 아닌 메모리에서 읽어오기 때문에 캐시와 메모리간의 값의 불일치가 해결된다.

변수에 volatile을 붙이는 대신에 synchronized블럭을 사용해도 같은 효과를 얻을 수 있다. 쓰레드가 synchronized블럭으로 들어갈 때와 나올 때, 캐시와 메모리간의 동기화가 이루어지기 때문에 값의 불일치가 해소되기 때문이다.

#### volatile로 long과 double을 원자화

JVM은 데이터를 4 byte단위로 처리하기 때문에, int와 int보다 작은 타입들은 한 번에 읽거나 쓰는 것이 가능하다. 즉, 단 하나의 명령어로 읽거나 쓰기가 가능하다는 뜻이다. 하나의 명령어는 더 이상 나눌 수 없는 최소의 작업단위이므로, 작업의 중간에 다른 쓰레드가 끼어들 틈이 없다.

그러나, 크기가 8 byte인 long과 double타입의 변수는 하나의 명령어로 값을 읽거나 쓸 수가 없기 때문에, 변수의 값을 읽는 과정에 다른 쓰레드가 끼어들 여지가 있다. 다른 쓰레드가 끼어들지 못하게 하려고 변수를 읽고 쓰는 모든 문장을 synchronized블럭으로 감쌀 수도 있지만, 더 간단한 방법은 변수를 선언할 대 volatile을 붙이는 것이다.

~~~ java
volatile long sharedVal;	// long타입의 변수를 원자화
volatile double sharedVal;	// double타입의 변수를 원자화
~~~

volatile은 해당 변수에 대한 읽거나 쓰기가 원자화된다. 원자화라는 것은 작업을 더 이상 나눌 수 없게 한다는 의미인데, synchronized블럭도 일종의 원자화라고 할 수 있다. 즉, synchronized블럭은 여러 문장을 원자화함으로써 쓰레드의 동기화를 구현한 것이라고 보면 된다.

**volatile은 변수의 읽거나 쓰기를 원자화 할 뿐, 동기화하는 것은 아니라는 점에 주의**하자. 동기화가 필요할 때 synchronized블럭 대신 volatile을 쓸 수 없다.

~~~ java
volatile long balance;	// 인스턴스 변수 balance를 원자화 한다

synchronized int getBalance() {	// balance의 값을 반환한다
  return balance;
}

synchronized void withdraw(int money) {	// balance의 값을 변경
  if(balance >= money) {
    balance -= money;
  }
}
~~~

인스턴스변수 balance를 volatile로 원자화했으니까, 이 값을 읽어서 반환하는 메서드 getBalance()를 동기화할 필요가 없다고 생각할 수 있다. 그러나 getBalance()를 synchronized로 동기화하지 않으면, withdraw()가 호출되어 객체에 lock을 걸고 작업을 수행하는 중인데도 getBalance()가 호출되는 것이 가능해진다. 출금이 진행 중일 때는 기다렸다가 출금이 끝난 후에 잔고를 조회할 수 있도록 하려면 getBalance()에 synchronized를 붙여서 동기화를 해야 한다.

<file_sep>/Database/SQL첫걸음/29.인덱스 작성과 삭제.md
# 29강. 인덱스 작성과 삭제

~~~ sql
// 인덱스 작성, 삭제 SYNTAX
CREATE INDEX
DROP INDEX
~~~

인덱스는 데이터베이스 객체의 하나로 DDL을 사용해서 작성하거나 삭제합니다. 표준 SQL에는 CREATE INDEX 명령은 없습니다. 인덱스 자체가 데이터베이스 제품에 의존하는 선택적인 항목으로 취급됩니다. 하지만 대표적인 데이터베이스 제품에는 모두 인덱스 구조가 도입되어 있으며, 모두 비슷한 관리 방법으로 인덱스를 다룰 수 있습니다.

## 1.  인덱스 작성

인덱스는 `CREATE INDEX` 명령으로 만듭니다. 인덱스에 이름을 붙여 관리하는데, 데이터베이스 객체가 될지 테이블의 열처럼 취급될지는 데이터베이스 제품에 따라 다릅니다. (Oracle, DB2 : 인덱스는 스키마 객체, SQL Server, MySQL: 인덱스는 테이블 내의 객체)

인덱스를 작성할 때는 해당 인덱스가 어느 테이블의 어느 열에 관한 것인지 작성할 필요가 있습니다. 이때 열은 복수로도 지정할 수 있습니다.

~~~ sql
CREATE INDEX 인덱스명 ON 테이블명 (열명1, 열명2, ...)
~~~

## 2. 인덱스 삭제

인덱스는 `DROP INDEX` 명령으로 삭제합니다. DROP 할 때는 다른 객체와 동일하게 인덱스 이름만 지정하면 됩니다. 다만 테이블 내 객체로서 작성하는 경우에는 테이블 이름도 지정합니다(이때 인덱스를 구성하는 열은 지정할 필요가 없습니다).

~~~ sql
// DROP INDEX (스키마 객체의 경우)
DROP INDEX 인덱스명

// DROP INDEX (테이블 내 객체의 경우)
DROP INDEX 인덱스명 ON 테이블명
~~~

인덱스는 테이블에 의존하는 객체입니다. 따라서 `DROP TABLE` 로 테이블을 삭제하면 테이블에 작성된 인덱스도 자동으로 삭제됩니다.

> 인덱스를 작성해두면 **검색이 빨라집니다.**
>
> 하지만 INSERT 명령의 경우에는 인덱스를 최신 상태로 갱신하는 처리가 늘어나므로 처리속도가 조금 떨어집니다.

## 3. EXPLAIN

인덱스 작성을 통해 쿼리의 성능 향상을 기대할 수 있습니다. 이때 실제로 인덱스를 사용해 검색하는지 확인하려면 `EXPLAIN` 명령을 사용합니다.

~~~ sql
EXPLAIN SQL 명령
~~~

EXPLAIN 명령의 문법은 간단합니다. EXPLAIN에 뒤이어 확인하고 싶은 SELECT 명령 등의 SQL 명령을 지정하면 됩니다.

## 4. 최적화

SELECT 명령을 실행할 때 인덱스의 사용 여부를 선택한다는 것을 알았습니다. 이는 데이터베이스 내부의 최적화에 의해 처리되는 부분입니다. 내부 처리에서는 SELECT 명령을 실행하기에 앞서 **실행 계획**을 세웁니다. 실행계획에서는 '인덱스가 지정된 열이 WHERE 조건으로 지정되어 있으니 인덱스를 사용하자'와 같은 처리가 이루어집니다. EXPLAIN 명령은 이 실행계획을 확인하는 명령입니다.

실행계획에서는 인덱스의 유무뿐만 아니라 인덱스를 사용할 것인지 여부에 대해서도 데이터베이스 내부의 최적화 처리를 통해 판단합니다. 이때 판단 기준으로 인덱스의 품질도 고려합니다.<file_sep>/JPA/JPA프로그래밍/임베디드_타입.md
# 임베디드 타입(복합 값 타입)

 새로운 값 타입을 직접 정의해서 사용할 수 있는데, JPA에서는 이것을 임베디드 타입(embedded type)이라 합니다.
중요한 것은 직접 정의한 임베디드 타입도 `int`, `String`처럼 값 타입이라는 것입니다.

 ***임베디드 타입을 사용하지 않았을 때와 사용했을 때를 비교해 봅시다.***

~~~ java
// 임베디드 타입 사용하지 않았을 때
@Entity
public class Member {

  @Id @GeneratedValue
  private Long id;
  private String name;

  // 근무 기간
  @Temporal(TemporalType.DATE)
  Date startDate;
  @Temporal(TemporalType.DATE)
  Date endDate;

  // 집 주소 표현
  private String city;
  private String street;
  private String zipcode;
  // ...
}
~~~

 위는 평범한 회원 엔티티입니다.

누군가에게 이 엔티티를 설명하라면 이렇게 이야기할 것입니다.

>"회원 엔티티는 이름, 근무 시작일, 근무 종료일, 주소 도시, 주소 번지, 주소 우편 번호를 가진다."

 하지만 이런 설명은 단순히 정보를 풀어둔 것 뿐이고, 근무 시작일과 우편 번호는 서로 아무 관련이 없습니다. 따라서 다음과 같이 설명하는게 더 정확합니다.

> "회원 엔티티는 이름, 근무 기간, 집 주소를 가진다."

 **회원이 상세한 데이터를 그대로 가지고 있는 것은 객체지향적이지 않으면 응집력만 떨어뜨립니다.**

대신에 근무 기간, 주소 같은 타입이 있다면 코드가 더 명확해질 것입니다.

~~~ java
// 임베디드 타입 사용
@Entity
public class Member {

  @Id @GeneratedVAlue
  private Long id;
  private String name;

  @Embedded
  private Period workPeriod;	// 근무 기간

  @Embedded
  private Address homeAddress;	// 집 주소
}
~~~

~~~ java
// 기간 임베디드 타입
@Embeddable
public class Peroid {

  @Temporal(TemporalType.DATE)
  Date startDate;
  @Temporal(TemporalType/Date)
  Date endDate;
  // ...

  public boolean isWork (Date date) {
    // .. 값 타입을 위한 메서드를 정의할 수 있다
  }
}
~~~

~~~ java
@Embeddable
public class Address {

  @Column(name="city") // 매핑할 컬럼 정의 가능
  private String city;
  private String street;
  private String zipcode;
  // ...
}
~~~

## 임베디드 타입 사용하는 방법

- `@Embeddable`: 값 타입을 정의하는 곳에 표시
- `@Embedded`: 값 타입을 사용하는 곳에 표시
- **임베디드 타입은 기본 생성자가 필수**

임베디드 타입을 포함한 모든 값 타입은 엔티티의 생명주기에 의존하므로 엔티티와 임베디드 타입 관계를 UML로 표현하면 **컴포지션(composition) 관계**가 됩니다.

> 하이버네이트는 임베디드 타입을 컴포넌트(components)라 합니다.

![image.png](https://images.velog.io/post-images/conatuseus/8c9227f0-1a86-11ea-81c4-6f5e57ac36c2/image.png)

<회원-컴포지션 관계 UML (출처: 김영한님 강의)>

## 임베디드 타입의 장점

- 재사용
- 높은 응집도
- Period 객체의 `isWork()` 메서드처럼 해당 값 타입만 사용하는 의미있는 메서드를 만들 수 있습니다.

## 임베디드 타입과 테이블 매핑

![image.png](https://images.velog.io/post-images/conatuseus/a7d7d0a0-1a86-11ea-bdac-0d8b2621ee12/image.png)

<임베디드 타입을 사용한 회원-테이블 매핑 (출처: 김영한님 강의)>

 임베디드 타입은 엔티티의 값일 뿐입니다.
따라서 값이 속한 엔티티의 테이블에 매핑합니다. 예제에서 **임베디드 타입을 사용하기 전과 후에 매핑하는 테이블은 같습니다.**

임베디드 타입 덕분에 **객체와 테이블을 아주 세밀하게(fine-grained) 매핑하는 것이 가능**합니다. **잘 설계한 ORM 애플리케이션은 매핑한 테이블의 수보다 클래스의 수가 더 많습니다.**

## 임베디드 타입과 연관관계

 임베디드 타입은 값 타입을 포함하거나 엔티티를 참조할 수 있습니다.

~~~ java
@Entity
public class Member {

  @Embedded
  Address address;	// 임베디드 타입 포함
	@Embedded
  PhoneNumber phoneNumber;	// 임베디드 타입 포함

  // ...
}

@Embeddable
public class Address {
  String street;
  String city;
  String state;
  @Embedded
  Zipcode zipcode; // 임베디드 타입 포함
}

@Embeddable
public class Zipcode {
  String zip;
  String plusFour;
}

@Embeddable
public class PhoneNumber {

  String areaCode;
  String localNumber;

  @ManyToOne
  PhoneServiceProvider provider;	// 엔티티 참조
}

@Entity
public class PhoneServiceProvider {
  @Id
  String name;
 	// ...
}
~~~

값 타입인 Address가 값 타입인 Zipcode를 포함하고, 값 타입인 PhoneNumber가 엔티티 타입인 PhoneServiceProvider를 참조합니다.

위 코드에 대한 연관관계는 다음과 같습니다.

![image-20191209220434702](/Users/conatuseus/Library/Application Support/typora-user-images/image-20191209220434702.png)

## `@AttributeOverride`: 속성 재정의

임베디드 타입에 정의한 매핑정보를 재정의하려면 엔티티에 `@AttributeOverride`를 사용하면 됩니다.

예를 들어 회원에게 주소가 하나 더 필요하면 어떻게 해야 할까요?

~~~ java
// 같은 임베디드 타입을 가지고 있는 회원
@Entity
public class Member {

  @Id @GeneratedValue
  private Long id;
  private String name;

  @Embedded
  Address homeAddress;

  @Embedded
  Address companyAddress;
}
~~~

 위 코드의 문제점은 테이블에 매핑하는 컬럼명이 중복되는 것입니다. 이때는 아래와 같이 `@AttributeOverrides`를 사용해서 매핑정보를 재정의해야 합니다.

~~~ java
@Entity
public class Member {

  @Id @GeneratedValue
  private Long id;
  private String name;

  @Embedded
  Address homeAddress;

  @Embedded
  @AttributeOverrides({
    @AttributeOverride(name="city", column=@Column(name="COMPANY_CITY")),
    @AttributeOverride(name="street", column=@Column(name="COMPANY_STREET")),
    @AttributeOverride(name="zipcode", column=@Column(name="COMPANY_ZIPCODE"))
  })
  Address companyAddress;
}
~~~

생성된 테이블은 다음과 같습니다.

~~~ SQL
CREATE TABLE MEMBER (
	COMPANY_CITY varchar(255),
  COMPANY_STREET varchar(255),
  COMPANY_ZIPCODE varchar(255),
  city varchar(255),
  street varchar(255),
  zipcode varchar(255),
  ...
)
~~~

`@AttributeOverride` 를 사용하면 어노테이션을 너무 많이 사용해서 엔티티 코드가 지저분해집니다. 다행히도 한 엔티티에 같은 임베디드 타입을 중복해서 사용하는 일은 많지 않습니다.

> `@AttributeOverrides`는 엔티티에 설정해야 합니다. 임베디드 타입이 임베디드 타입을 가지고 있어도 엔티티에 설정해야 합니다.

## 임베디드 타입과 null

 임베디드 타입이 null이면 매핑한 컬럼 값은 모두 null이 됩니다.

~~~ java
member.setAddress(null); // null 입력
em.persist(member);
~~~

회원 테이블의 주소와 관련된 CITY, STREET, ZIPCODE 컬럼 값은 모두 null이 됩니다.
<file_sep>/README.md
# 📝 TIL - Today I Learned.

 하루동안 개발 관련하여 학습하거나 경험한 내용을 기록한다.

## 📖 Categories
- [Java](#Java)
- [Spring](#Spring)
- [DesignPattern](#DesignPattern)
- [Kotlin](#Kotlin)
- [Network](#Network)
- [Database](#Database)
- [JPA](#JPA)
- [Books](#Books)
- [Docker](#Docker)
- [Git](#Git)
- [Link](#Link)
- [Seminar](#Seminar)
- [Article](#Article)

---

### Java
- [Collections](https://github.com/Conatuseus/TIL/tree/master/Java/Collections)
  - [unmodifiable](https://github.com/Conatuseus/TIL/blob/master/Java/Collections/unmodifiable.md)

### Spring
- [왜 생성자 주입을 사용해야 하는가](https://github.com/Conatuseus/TIL/blob/master/Spring/왜_생성자_주입을_사용해야하는가.md)
- [그레이들 프로젝트를 스프링 부트 프로젝트로 변경하기](https://github.com/Conatuseus/TIL/blob/master/Spring/%EA%B7%B8%EB%A0%88%EC%9D%B4%EB%93%A4%20%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%A5%BC%20%EC%8A%A4%ED%94%84%EB%A7%81%20%EB%B6%80%ED%8A%B8%20%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%A1%9C%20%EB%B3%80%EA%B2%BD%ED%95%98%EA%B8%B0.md)

### JPA
- [JPA프로그래밍](https://github.com/Conatuseus/TIL/tree/master/JPA/JPA프로그래밍)
  - [JPA 소개](https://github.com/Conatuseus/TIL/blob/master/JPA/JPA프로그래밍/JPA소개.md)
  - [영속성컨텍스트_1](https://github.com/Conatuseus/TIL/blob/master/JPA/JPA프로그래밍/영속성컨텍스트_1.md)
  - [영속성컨텍스트_2](https://github.com/Conatuseus/TIL/blob/master/JPA/JPA프로그래밍/영속성컨텍스트_2.md)
  - [엔티티매핑_1](https://github.com/Conatuseus/TIL/blob/master/JPA/JPA프로그래밍/엔티티매핑_1.md)
  - [엔티티매핑_2](https://github.com/Conatuseus/TIL/blob/master/JPA/JPA프로그래밍/엔티티매핑_2.md)
  - [연관관계 매핑 기초 1](https://github.com/Conatuseus/TIL/blob/master/JPA/JPA프로그래밍/연관관계_매핑_기초_1.md)
  - [연관관계 매핑 기초 2](https://github.com/Conatuseus/TIL/blob/master/JPA/JPA프로그래밍/연관관계_매핑_기초_2.md)
  - [임베디드 타입](https://github.com/Conatuseus/TIL/blob/master/JPA/JPA프로그래밍/임베디드_타입.md)
- [JPA Auditing 적용하기](https://github.com/Conatuseus/TIL/blob/master/JPA/JPA_Auditing.md)

### Books

#### HTTP완벽 가이드

- [1장. HTTP 개관](https://github.com/Conatuseus/TIL/blob/master/Books/HTTP%EC%99%84%EB%B2%BD%EA%B0%80%EC%9D%B4%EB%93%9C/1%EC%9E%A5.HTTP%EA%B0%9C%EA%B4%80.md)
- [2장. URL과 리소스](https://github.com/Conatuseus/TIL/blob/master/Books/HTTP%EC%99%84%EB%B2%BD%EA%B0%80%EC%9D%B4%EB%93%9C/2%EC%9E%A5.URL%EA%B3%BC%20%EB%A6%AC%EC%86%8C%EC%8A%A4.md)
- [3장.HTTP 메시지](https://github.com/Conatuseus/TIL/blob/master/Books/HTTP%EC%99%84%EB%B2%BD%EA%B0%80%EC%9D%B4%EB%93%9C/3%EC%9E%A5.HTTP%20%EB%A9%94%EC%8B%9C%EC%A7%80.md)

#### 스프링 부트와 AWS로 혼자 구현하는 웹서비스

- [공부한 내용 정리 및 코드 보기](https://github.com/Conatuseus/freelec-springboot2-webservice)

### Docker
- [Docker_사용해보기](https://github.com/Conatuseus/TIL/blob/master/Docker/Docker_%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0.md)

### Git
- [.ignore 설정하기](https://github.com/Conatuseus/TIL/blob/master/Git/gitignore.md)

### Link
- [프레임 워크 공부를 멈춰라](https://medium.com/@jongyoungpark/프레임워크-공부를-멈춰라-1afa37644474)
- [ATDD](https://boorownie.github.io/2019-11-27/agail_and_atdd)
- [도커의 개념과 실습](https://subicura.com/2017/01/19/docker-guide-for-beginners-1.html)
- [Spring Rest Docs 적용: 우아한형제들 기술 블로그](http://woowabros.github.io/experience/2018/12/28/spring-rest-docs.html)
- [intellij에서 entity 기반 ERD 그리기](https://gmlwjd9405.github.io/2019/10/28/intellij-jpa-erd.html)
- [지방대 개발 비전공자가 배달의민족 리드 개발자가 되기까지 - 이동욱님](https://www.youtube.com/watch?v=V9AGvwPmnZU)

### Seminar
- [KSUG 2019 하반기](https://github.com/Conatuseus/TIL/blob/master/seminar/KSUG%202019.md)

### Article
- [2019.11](https://github.com/Conatuseus/TIL/tree/master/Article/2019-11)
- [2019.12](https://github.com/Conatuseus/TIL/tree/master/Article/2019-12)
<file_sep>/Books/HTTP완벽가이드/1장.HTTP개관.md
# 1장: HTTP 개관

전 세계의 웹 브라우저, 서버, 웹 애플리케이션은 모두  HTTP(Hypertext Transfer Protocol)를 통해 서로 대화한다. HTTP는 현대 인터넷의 공용어이다.

 이 장은 HTTP를 간결하게 설명한다.

## 1.1 HTTP: 인터넷의 멀티미디어 배달부

 HTTP는 전 세계의 웹 서버로부터 대량(수십억 개의 이미지, HTML 페이지, 텍스트 파일 등)의 정보를 빠르고, 간편하고, 정확하게 사람들의 PC에 설치된 웹브라우저로 옮겨준다.

 HTTP는 **신뢰성 있는 데이터 전송 프로토콜을 사용**하기 때문에, 데이터가 전송 중 손상되거나 꼬이지 않음을 보장한다. 신뢰성 있는 전송 덕분에 사용자는 인터넷에서 얻은 정보가 손상된 게 아닌지 염려하지 않아도 되고, 개발자는 인터넷의 결함이나 약점에 대한 걱정 없이 애플리케이션 고유의 기능을 구현하는 데 집중할 수 있다.

## 1.2 웹 클라이언트와 서버

 **웹 콘텐츠는 웹 서버에 존재**한다. 웹 서버는 HTTP 프로토콜로 의사소통하기 때문에 보통 HTTP 서버라 불린다.
이들 **웹 서버는 인터넷의 데이터를 저장하고, HTTP 클라이언트가 요청한 데이터를 제공**한다.

![image.png](https://images.velog.io/post-images/conatuseus/0f450370-1da2-11ea-b1aa-07aa188456af/image.png)

위 처럼 클라이언트는 서버에게 HTTP 요청을 보내고 서버는 요청된 데이터를 HTTP 응답으로 돌려준다. HTTP 클라이언트와 HTTP 서버는 월드 와이드 웹의 기본 요소다.

 예를 들어 "http://www.oreilly.com/index.html" 페이지를 열어볼 때, 웹 브라우저는 HTTP 요청을 www.oreilly.com 서버로 보낸다. 서버는 요청을 받은 객체(이 경우 "/index.html")을 찾고, 성공했다면 그것의 타입, 길이 등의 정보와 함께 HTTP 응답에 실어서 클라이언트에게 보낸다.

## 1.3 리소스

**웹 서버는 웹 리소스를 관리하고 제공**한다. 웹 리소스는 웹 콘텐츠의 원천이다.
가장 단순한 웹 리소스는 웹 서버 파일 시스템의 정적 파일이다. 정적 파일은 텍스트 파일, HTML 파일, 마이크로소프트 워드 파일, JPEG 이미지 파일, 그 외 모든 종류의 파일을 포함한다.

 그러나 리소스는 반드시 정적 파일이어야 할 필요는 없다. 리소스는 요청에 따라 콘텐츠를 생산하는 프로그램이 될 수도 있다. 이들 동적 콘텐츠 리소스는 사용자가 누구인지, 어떤 정보를 요청했는지, 몇 시인지에 따라 다른 콘텐츠를 생성한다.

### 1.3.1 미디어 타입

인터넷은 수천 가지 데이터 타입을 다루기 때문에, **HTTP는 웹에서 전송되는 객체 각각에 신중하게 MIME 타입이라는 데이터 포맷 라벨을 붙인다**. MIME(Multipurpose Internet Mail Extensions, 다목적 인터넷 메일 확장)은 원래 각기 다른 전자메일 시스템 사이에서 메시지가 오갈 때 겪는 문제점을 해결하기 위해 설계되었다.

 **웹 서버는 모든 HTTP 객체 데이터에 MIME 타입을 붙인다**.**웹 브라우저는 서버로부터 객체를 돌려받을 때, 다룰 수 있는 객체인지 MIME 타입을 통해 확인한다.** 대부분의 웹 브라우저는 잘 알려진 객체 타입 수백 가지를 다룰 수 있다. 이미지 파일을 보여주고, HTML 파일을 분석하거나 포맷팅하고, 오디오 파일을 컴퓨터의 스피커를 통해 재생하고, 특별한 포맷의 파일을 다루기 위해 외부 플러그인 소프트웨어를 실행한다.

![image.png](https://images.velog.io/post-images/conatuseus/18715750-1da2-11ea-b1aa-07aa188456af/image.png)

MIME 타입은 사선(/)으로 구분된 주 타입(primary object type)과 부 타입(specific subtype)으로 이루어진 문자열 라벨이다.

예를 들면,

- HTML로 작성된 텍스트 문서는 text/html 라벨이 붙는다.
- Plain ASCII 텍스트 문서는 text/plain 라벨이 붙는다.
- JPEG 이미지는 image/jpeg가 붙는다.
- GIF 이미지는 image/gif가 된다.

수백 가지의 잘 알려진 MIME 타입과, 그보다 더 많은 실험용 혹은 특정 용도의 MIME 타입이 존재한다.

### 1.3.2 URI

웹 서버 리소스는 각자 이름을 갖고 있기 때문에, 클라이언트는 관심 있는 리소스를 지목할 수 있다.
서버 리소스 이름은 통합 자원 식별자(uniform resource identifier), 혹은 URI로 불린다. URI는 인터넷의 우편물 주소 같은 것으로, 정보 리소스를 고유하게 식별하고 위치를 지정할 수 있다.

  '죠의 컴퓨터 가게'의 웹 서버에 있는 이미지 리소스에 대한 URI라면 이런 식이다.
http://www.joes-hardware.com/specials/saw-blade.gif

 아래는 죠의 컴퓨터 가게 서버에 있는 GIF 형식의 톱날 그림 리소스에 대한 URI가 HTTP 프로토콜에서 어떻게 해석되는지 보여준다. HTTP는 주어진 URI로 객체를 찾아온다. URI에는 두 가지가 있는데, URL과 URN이라는 것이다.

![image.png](https://images.velog.io/post-images/conatuseus/1e3c7ed0-1da2-11ea-aeb6-1311c4c815d2/image.png)

### 1.3.3 URL

**통합 자원 지시자(uniform resource locator, URL)**는 리소스 식별자의 가장 흔한 형태다. URL은 특정 서버의 한 리소스에 대한 구체적인 위치를 서술한다. URL은 리소스가 정확히 어디에 있고 어떻게 접근할 수 있는지 분명히 알려준다. 다음 표는 URL의 몇 가지 예다.

| URL                                  | 설명                           |
| ------------------------------------ | ------------------------------ |
| http://www.oreilly.com/index.html    | 오라일리 출판사 홈페이지의 URL |
| http://www.yahoo.com/images/logo.gif | 야후 웹 사이트 로고의 URL      |

대부분의 URL은 세 부분으로 이루어진 표준 포맷을 따른다.

- URL의 첫 번째 부분은 스킴(scheme)이라고 불리는데, 리소스에 접근하기 위해 사용되는 프로토콜을 서술한다. 보통 HTTP 프로토콜(http://)이다.
- 두 번째 부분은 서버의 인터넷 주소를 제공한다. (예: www.joes-hardware.com)
- 마지막은 웹 서버의 리소스를 가리킨다. (예: /specials/saw-blade.gif)

오늘날 대부분의 URI는 URL이다.

### 1.3.4 URN

URI의 두 번째 종류는 **유니폼 리소스 이름(uniform resource name, URN)**이다. URN은 콘텐츠를 이루는 한 리소스에 대해, 그 리소스의 위치에 영향 받지 않는 **유일무이한 이름 역할**을 한다. 이 위치 독립적인 URN은 리소스를 여기저기로 옮기더라도 문제없이 동작한다. 리소스가 그 이름을 변하지 않게 유지하는 한, 여러 종류의 네트워크 접속 프로토콜로 접근해도 문제없다.

 URN은 여전히 실험 중인 상태고 아직 널리 채택되지 않았다. 효율적인 동작을 위해 URN은 리소스 위치를 분석하기 위한 인프라 지원이 필요한데, 그러한 인프라가 부재하기에 URN 채택이 더 늦춰지고 있다.

## 1.4 트랜잭션

**HTTP 트랜잭션은 요청 명령(클라이언트에서 서버로 보내는)과 응답 결과(서버가 클라이언트에게 돌려주는)로 구성**되어 있다. 이 상호작용은 아래 그림과 같이 HTTP 메시지라고 불리는 정형화된 데이터 덩어리를 이용해 이루어진다.

![image.png](https://images.velog.io/post-images/conatuseus/253996a0-1da2-11ea-aeb6-1311c4c815d2/image.png)

### 1.4.1 메서드

HTTP는 HTTP 메서드라고 불리는 여러 가지 종류의 요청 명령을 지원한다. 모든 HTTP 요청 메시지는 한 개의 메서드를 갖는다. **메서드는 서버에게 어떤 동작이 취해져야 하는지 말해준다**(웹 페이지 가져오기, 게이트웨이 프로그램 실행하기, 파일 삭제하기 등)

 아래는 가장 많이 사용되는 HTTP 메서드이다.

| HTTP 메서드 | 설명                                                         |
| ----------- | ------------------------------------------------------------ |
| GET         | 서버에서 클라이언트로 지정한 리소스를 보내라                 |
| PUT         | 클라이언트에서 서버로 보낸 데이터를 지정한 이름의 리소스로 저장하라 |
| DELETE      | 지정한 리소스를 서버에서 삭제하라                            |
| POST        | 클라이언트 데이터를 서버 게이트웨이 애플리케이션으로 보내라  |
| HEAD        | 지정한 리소스에 대한 응답에서, HTTP 헤더 부분만 보내라       |

### 1.4.2 상태 코드

**모든 HTTP 응답 메시지는 상태 코드와 함께 반환**된다. 상태 코드는 **클라이언트에게 요청이 성공했는지 아니면 추가 조치가 필요한지 알려주는 세 자리 숫자**다.

 아래는 흔히 쓰이는 상태 코드이다.

| HTTP 상태 코드 | 설명                                          |
| -------------- | --------------------------------------------- |
| 200            | 좋다. 문서가 바르게 반환되었다                |
| 302            | 다시 보내라. 다른 곳에 가서 리소스를 가져가라 |
| 404            | 없음. 리소스를 찾을 수 없다.                  |

HTTP는 각 숫자 상태 코드에 텍스트로 된 "사유 구절(reason phrase)"도 함께 보낸다. ex) 200 OK, 302 Found, 404 Not Found

> HTTP 메서드, HTTP 상태 코드는 3장에서 자세히 설명한다.

### 1.4.3 웹페이지는 여러 객체로 이루어질 수 있다

**애플리케이션은 보통 하나의 작업을 수행하기 위해 여러 HTTP 트랜잭션을 수행한다.** 예를 들어, 웹 브라우저는 시각적으로 풍부한 웹페이지를 가져올 때 대량의 HTTP 트랜잭션을 수행한다. 페이지 레이아웃을 서술하는 HTML '뼈대'를 한 번의 트랜잭션으로 가져온 뒤, 첨부된 이미지, 그래픽 조각, 자바 애플릿 등을 가져오기 위해 추가로 HTTP 트랜잭션들을 수행한다. 이 리소스들은 아래 그림과 같이 다른 서버에 위치할 수도 있다. 이와 같이 **'웹페이지'는 보통 하나의 리소스가 아닌 리소스의 모음**이다.

![image.png](https://images.velog.io/post-images/conatuseus/37101610-1da2-11ea-aeb6-1311c4c815d2/image.png)

## 1.5 메시지

 HTTP 메시지는 단순한 줄 단위의 문자열이다. 이진 형식이 아닌 일반 텍스트이기 때문에 사람이 읽고 쓰기 쉽다.
아래 그림은 간단한 트랜잭션에 대한 HTTP 메시지를 보여주고 있다.

![image.png](https://images.velog.io/post-images/conatuseus/b7023e30-1e3c-11ea-991d-53f8029a7cfa/image.png)

 웹 클라이언트에서 웹 서버로 보낸 HTTP 메시지를 요청 메시지라 부른다. 서버에서 클라이언트로 가는 메시지는 응답 메시지라고 부른다. 그 외에 다른 종류의 HTTP 메시지는 없으며, HTTP 요청과 응답 메시지의 형식은 굉장히 비슷하다.

 HTTP 메시지는 다음의 세 부분으로 이루어진다.

- **시작줄**

  메시지의 첫 줄은 시작줄로, 요청이라면 무엇을 해야하는지 응답이라면 무슨 일이 일어났는지 나타낸다.

- **헤더**

  시작줄 다음에는 0개 이상의 헤더 필드가 이어진다. 각 헤더 필드는 쉬운 구문분석을 위해 쌍점(:)으로 구분되어 있는 하나의 이름과 하나의 값으로 구성된다. 헤더 필드를 추가하려면 그저 한 줄을 더하기만 하면 된다. 헤더는 빈 줄로 끝난다.

- **본문**

  빈 줄 다음에는 어떤 종류의 데이터든 들어갈 수 있는 메시지 본문이 필요에 따라 올 수 있다. 요청의 본문은 웹 서버로 데이터를 실어 보내며, 응답의 본문은 클라이언트로 데이터를 반환한다.

> HTTP 메시지도 3장에서 자세히...

## 1.6 TCP 커넥션

어떻게 메시지가 TCP(Transmission Control Protocol, 전송 제어 프로토콜) 커넥션을 통해 한 곳에서 다른 곳으로 옮겨가는지 이야기 해보자.

### 1.6.1 TCP/IP

**HTTP는 애플리케이션 계층 프로토콜이다. HTTP는 네트워크 통신의 핵심적인 세부사항에 대해서 신경쓰지 않고, 대신 대중적이고 신뢰성 있는 인터넷 전송 프로토콜인 TCP/IP에게 맡긴다.**

 TCP는 다음을 제공한다.

- 오류 없는 데이터 전송
- 순서에 맞는 전달 (데이터는 언제나 보낸 순서대로 도착한다)
- 조각나지 않는 데이터 스트림 (언제든 어떤 크기로든 보낼 수 있다)

인터넷 자체가 전 세계의 컴퓨터와 네트워크 장치들 사이에서 대중적으로 사용되는 TCP/IP에 기초하고 있다. TCP/IP는 **TCP와 IP가 이루는, 패킷 교환 네트워크 프로토콜의 집합**이다. TCP/IP는 각 네트워크와 하드웨어의 특성을 숨기고, 어떤 종류의 컴퓨터나 네트워크든 서로 신뢰성 있는 의사소통을 하게 해 준다.

 일단 TCP 커넥션이 맺어지면, 클라이언트와 서버 컴퓨터 간에 교환되는 메시지가 없어지거나, 손상되거나, 순서가 뒤바뀌어 수신되는 일은 결코 없다.

 네트워크 개념상, HTTP 프로토콜은 TCP 위의 계층이다. HTTP는 자신의 메시지 데이터를 전송하기 위해 TCP를 사용한다. 이와 유사하게 TCP는 IP 위의 계층이다.

![image.png](https://images.velog.io/post-images/conatuseus/dc995270-1e3f-11ea-9f59-1f22f0af0146/image.png)

### 1.6.2 접속, IP 주소 그리고 포트번호

HTTP 클라이언트가 서버에 메시지를 전송할 수 있게 되기 전에, **인터넷 프로토콜(Internet Protocol, IP) 주소와 포트번호를 사용해 클라이언트와 서버 사이에 TCP/IP 커넥션을 맺어야 한다.**

 TCP 커넥션을 맺는 것은 다른 회사 사무실에 있는 누군가에게 전화를 거는 것과 다소 비슷하다. 먼저 회사의 전화번호를 누른다. 이렇게 하면 그 회사로 연결된다. 그 다음엔 전화를 걸고자 하는 상대방이 쓰는 번호를 누른다.

 **TCP에서는 서버 컴퓨터에 대한 IP 주소와 그 서버에서 실행 중인 프로그램이 사용 중인 포트번호가 필요**하다.

 HTTP 서버의 IP 주소와 포트번호는 어떻게 알아낼 수 있을까?

URL을 사용하면 된다. URL이란 리소스에 대한 주소이고, URL은 그 리소스를 가지고 있는 장비에 대한 IP 주소를 알려줄 수 있다. 몇 가지 URL을 살펴보자.

- http://172.16.31.10:80/index.html
- http://www.netscape.com:80/index.html
- http://www.netscape.com/index.html

첫 번째 URL은 IP 주소 '172.16.31.10'와 포트번호 '80'을 갖고 있다.

두 번째 URL에는 숫자로 된 IP 주소가 없다. 대신 글자로 된 도메인 이름 혹은 호스트 명을 갖고 있다. 호스트 명은 IP 주소에 대한 이해하기 쉬운 형태의 별명이다. 호스트 명은 도메인 이름 서비스(Domain Name Service, DNS)라 불리는 장치를 통해 쉽게 IP로 변환될 수 있으므로 모든 준비는 끝난 것이다.

마지막 URL은 포트번호가 없다. HTTP URL에 포트번호가 빠진 경우에는 기본값 80이라고 가정하면 된다.

 IP주소와 포트번호를 이용해 클라이언트는 TCP/IP로 쉽게 통신할 수 있다. 다음 그림은 웹 브라우저가 어떻게 HTTP를 이용해서 멀리 떨어진 곳에 있는 서버의 단순한 HTML 리소스를 사용자에게 보여주는지 묘사하고 있다.

![image.png](https://images.velog.io/post-images/conatuseus/372c9e80-1e41-11ea-a302-c7e2be375068/image.png)

 순서는 다음과 같다.

(a) 웹 브라우저는 서버의 URL에서 호스트 명을 추출한다.

(b) 웹 브라우저는 서버의 호스트 명을 IP로 변환한다.

(c) 웹 브라우저는 URL에서 포트번호(있다면)를 추출한다.

(d) 웹 브라우저는 웹 서버와 TCP 커넥션을 맺는다.

(e) 웹 브라우저는 서버에 HTTP 요청을 보낸다.

(f) 서버는 웹 브라우저에 HTTP 응답을 돌려준다.

(g) 커넥션이 닫히면, 웹 브라우저는 문서를 보여준다.

## 1.7 프로토콜 버전

- HTTP/0.9

  1991년의 HTTP 프로토타입은 HTTP/0.9로 알려져 있다. 이 프로토콜은 심각한 디자인 결함이 다수 있고 구식 클라이언트하고만 같이 사용할 수 있다. HTTP/0.9는 오직 GET 메서드만 지원하고, 멀티미디어 콘텐츠에 대한 MIME 타입이나, HTTP 헤더, 버전 번호는 지원하지 않는다. HTTP/0.9는 원래 간단한 HTML 객체를 받아오기 위해 만들어진 것이다.

- HTTP/1.0

  1.0은 처음으로 널리 쓰이기 시작한 HTTP 버전이다. HTTP/1.0은 버전 번호, HTTP 헤더, 추가 메서드, 멀티미디어 객체 처리를 추가했따. HTTP/1.0은 시각적으로 매력적인 웹페이지와 상호작용하는 폼을 실현했고 이는 월드 와이드 웹을 대세로 만들었다. HTTP/1.0은 결코 잘 정의된 명세가 아니다. HTTP가 상업적, 학술적으로 급성장하던 시기에 만들어진, 잘 동작하는 용례들의 모음에 가깝다.

- HTTP/1.0+

  1990년대 중반, 월드 와이드 웹이 급격히 팽창하고 상업적으로도 성공하면서 여러 유명 웹 클라이언트와 서버 들은 그에 따른 요구를 만족시키기 위해 발 빠르게 HTTP에 기능을 추가해갔다. 오래 지속되는 "keep-alive" 커넥션, 가상 호스팅 지원, 프락시 연결 지원을 포함해 많은 기능이 공식적이진 않지만 사실상의 표준으로 HTTP에 추가되었다.

- HTTP/1.1

  HTTP/1.1은 HTTP 설계의 구조적 결함 교정, 두드러진 성능 최적화, 잘못된 기능 제거에 집중했다. 뿐만 아니라 HTTP/1.1은 더 복잡해진 웹 애플리케이션과 배포를 지원한다. HTTP/1.1은 현재의 HTTP 버전이다.

- HTTP/2.0

  HTTP/2.0은 HTTP/1.1 성능 문제를 개선하기 위해 구글의 SPDY 프로토콜을 기반으로 설계가 진행 중인 프로토콜이다. 더 자세한 것은 10장에서..

## 1.8 웹의 구성요소

- **프락시**

  클라이언트와 서버 사이에 위치한 HTTP 중개자

- **캐시**

  많이 찾는 웹페이지를 클라이언트 가까이에 보관하는 HTTP 창고

- **게이트웨이**

  다른 애플리케이션과 연결된 특별한 웹 서버

- **터널**

  단순히 HTTP 통신을 전달하기만 하는 특별한 프락시

- **에이전트**

  자동화된 HTTP 요청을 만드는 준지능적(semi-intelligent) 웹클라이언트

### 1.8.1 프락시

**웹 보안, 애플리케이션 통합, 성능 최적화를 위한 중요한 구성요소**인 HTTP 프락시 서버에 대해 살펴보자.

아래 그림에서 보다시피, **프락시는 클라이언트와 서버 사이에 위치하여, 클라이언트의 모든 HTTP 요청을 받아 서버에 전달한다.** 이 애플리케이션은 사용자를 위한 프락시로 동작하며 사용자를 대신해서 서버에 접근한다.

![image.png](https://images.velog.io/post-images/conatuseus/b63bdcc0-1e43-11ea-98e4-1781e2016754/image.png)

프락시는 주로 보안을 위해 사용된다. 즉, 모든 웹 트래픽 흐름 속에서 신뢰할 만한 중개자 역할을 한다. 또는 프락시는 요청과 응답을 필터링한다. 예를 들어, 회사에서 무엇인가를 다운 받을 때 애플리케이션 바이러스를 검출하거나 초등학교 학생들에게서 성인 콘텐츠를 차단한다. 프락시는 6장에서 자세히...

### 1.8.2 캐시

**웹 캐시와 캐시 프락시는 자신을 거쳐 가는 문서들 중 자주 찾는 것의 사본을 저장해 두는**, 특별한 종류의 HTTP 프락시 서버다. 다음번에 클라이언트가 같은 문서를 요청하면 그 캐시가 갖고 있는 사본을 받을 수 있다.

 클라이언트는 멀리 떨어진 웹 서버보다 근처의 캐시에서 훨씬 더 빨리 문서를 다운 받을 수 있다. HTTP는, 캐시를 효율적으로 동작하게 하고 캐시된 콘텐츠를 최신 버전으로 유지하면서 동시에 프라이버시도 보호하기 위한 많은 기능을 정의한다. 7장에서 캐싱 기술에 대해...

![image.png](https://images.velog.io/post-images/conatuseus/204c4460-1e44-11ea-9f59-1f22f0af0146/image.png)

### 1.8.3 게이트웨이

게이트웨이는 **다른 서버들의 중개자로 동작하는 특별한 서버**다. 게이트웨이는 주로 HTTP 트래픽을 다른 프로토콜로 변환하기 위해 사용된다. 게이트웨이는 언제나 스스로가 리소스를 갖고 있는 진짜 서버인 것처럼 요청을 다룬다. 클라이언트는 자신이 게이트웨이와 통신하고 있음을 알아채지 못할 것이다.

 HTTP/FTP 게이트웨이는 FTP URI에 대한 HTTP 요청을 받아들인 뒤, FTP 프로토콜을 이용해 문서를 가져온다. 받아온 문서는 HTTP 메시지에 담겨 클라이언트에게 보낸다.

![image.png](https://images.velog.io/post-images/conatuseus/6290af00-1e44-11ea-9f59-1f22f0af0146/image.png)

### 1.8.4 터널

터널은 두 커넥션 사이에서 날(raw) 데이터를 열어보지 않고 그대로 전달해주는 HTTP 애플리케이션이다. HTTP 터널은 주로 비 HTTP 데이터를 하나 이상의 HTTP 연결을 통해 그대로 전송해주기 위해 사용된다.

 HTTP 터널을 활용하는 대표적인 예로, 암호화된 SSL 트래픽을 HTTP 커넥션으로 전송함으로써 웹 트래픽만 허용하는 사내 방화벽을 통과시키는 것이 있다.

### 1.8.5 에이전트

 사용자 에이전트는 사용자를 위해 HTTP 요청을 만들어주는 클라이언트 프로그램이다. 웹 요청을 만드는 애플리케이션은 뭐든 HTTP 에이전트다. 지금까지 우리는 한 가지 종류의 HTTP 에이전트, 웹 브라우저에 대해서만 이야기했다. 그러나 사용자 에이전트에는 여러 가지 종류가 더 있다.

 예를 들어, 사람의 통제 없이 스스로 웹을 돌아다니며 HTTP 트랜잭션을 일으키고 콘텐츠를 받아오는 자동화된 사용자 에이전트가 있다. 이들 자동화된 에이전트는 보통 '스파이더'나 '웹로봇'과 같이 다채로운 이름을 가지고 있다.

<file_sep>/JPA/JPA프로그래밍/연관관계_매핑_기초_1.md
> 이번 글에서는 **<연관관계가 필요한 이유, 단방향 연관관계>**에 대해 알아보겠습니다.
이 시리즈 글은 김영한 님의 [강의](https://www.inflearn.com/course/ORM-JPA-Basic), [책](https://book.naver.com/bookdb/book_detail.nhn?bid=9252528)을 보고 적은 것임을 알려드립니다. (강추)

# 1. 연관관계가 필요한 이유

 > <시나리오>
 > - 회원과 팀이 있다.
 > - 회원은 하나의 팀에만 소속될 수 있다.
 > - 회원과 팀은 다대일 관계다.

 ## 객체 테이블에 맞추어 모델링(참조 대신 외래 키를 그대로 사용)

  ![image.png](https://images.velog.io/post-images/conatuseus/13593cd0-ded0-11e9-aea8-29ebe9a2cb44/image.png)

아래는 회원과 팀 클래스입니다.
~~~ java
@Entity
public class Member {

  @Id @GeneratedValue
  private Long id;

  @Column(name = "USERNAME")
  private String name;

  @Column(name = "TEAM_ID")
  private Long teamId;
  ...
  }

@Entity
public class Team {

  @Id @GeneratedValue
  private Long id;
  private String name;
  ...
  }

~~~

아래는 팀과 회원을 저장하는 코드입니다.
~~~ java
//팀 저장
Team team = new Team();
team.setName("wooteco");
em.persist(team);

//회원 저장
Member member = new Member();
member.setName("conas");
member.setTeamId(team.getId());
em.persist(member);
~~~

이 상황에서 만약 **회원의 팀을 찾으려면 어떻게 할까요?**

방법은 다음과 같습니다.
~~~ java
Member findMember = em.find(Member.class, member.getId());

Long findTeamId = findMember.getId();
Team findTeam = em.find(Team.class, findTeamId);
~~~

연관관계가 없다면 위와 같이, member의 team을 가져오는데 계속 꺼내와야합니다.
member를 꺼내오고, id를 가져와서 다시 팀을 가져오는... 회원의 팀을 가져오기 위해 **많은 비용**이 듭니다.
그리고 **객체 지향스럽지 않은 코드**가 됩니다.

**객체를 테이블에 맞추어 데이터 중심으로 모델링하면, 협력 관계를 만들 수 없습니다.**
- **테이블은 외래 키로 조인**을 사용해서 연관된 테이블을 찾습니다.
- **객체 참조**를 사용해서 연관된 객체를 찾습니다.
- 테이블과 객체 사이에는 이런 큰 간격이 있습니다.

# 2. 단방향 연관관계

회원과 팀의 관계를 통해 다대일 단방향 관계를 알아보겠습니다.

![image.png](https://images.velog.io/post-images/conatuseus/7933e2a0-ded3-11e9-8f36-9fe4d9580a6d/image.png)

- **객체 연관 관계**
	+ 회원 객체(Member)는 Member.team 필드(멤버 변수)로 팀 객체와 연관관계를 맺습니다.
    + 회원 객체와 팀 객체는 **단방향 관계**입니다. 회원은 Member.team 필드를 통해 팀을 알 수 있지만, 팀 객체로 소속된 회원들을 알 수 없기 때문입니다. member ➡ team 의 조회는 member.getTeam()으로 가능하지만, team ➡ member 를 접근하는 필드는 없습니다.

- **테이블 연관관계**
	+ 회원 테이블은 TEAM_ID 외래 키로 팀 테이블과 연관관계를 맺습니다.
    + 회원 테이블과 팀 테이블은 **양방향 관계**입니다. 회원 테이블의 TEAM_ID 외래 키를 통해서 회원과 팀을 조인할 수 있고, 반대로 팀과 회원도 조인할 수 있습니다. 예를 들어, MEMBER 테이블의 TEAM_ID 외래 키 하나로 `MEMBER JOIN TEAM` 과 `TEAM JOIN MEMBER` 둘 다 가능합니다.
    + **회원과 팀을 조인하는 SQL**
    	~~~ SQL
        SELECT *
     	FROM MEMBER M
     	JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
    	~~~
    + **팀과 회원을 조인하는 SQL**
    	~~~ SQL
     	SELECT *
     	FROM TEAM T
     	JOIN MEMBER M ON T.TEAM_ID = M.TEAM_ID
     	~~~

- **객체 연관관계와 테이블 연관관계의 가장 큰 차이**
  참조를 통한 연관관계는 언제나 단방향입니다. 객체간에 연관관계를 양방향으로 만드록 싶으면 반대쪽에도 필드를 추가해서 참조를 보관해야 합니다. 이렇게 양쪽에서 서로 참조하는 것을 양방향 연관관계라고 합니다.
  하지만 정확히 이야기하면 이것은 **양방향 관계**가 아니라 **서로 다른 단방향 관계 2개**입니다. 반면에 테이블은 외래 키 하나로 양방향으로 조인할 수 있습니다.

- **객체 연관관계 vs 테이블 연관관계 정리**
	+ 객체는 참조(주소)로 연관관계를 맺습니다.
    + 테이블은 외래 키로 연관관계를 맺습니다.
    + 참조를 사용하는 객체의 연관관계는 단방향입니다.
    + 외래 키를 사용하는 테이블의 연관관계는 양방향입니다. (A JOIN B가 가능하면 반대로 B JOIN A도 가능)

 ## 연관관계 사용
**Member, Team class**
~~~ java
@Entity
public class Member {

  @Id @GeneratedValue
  private Long id;

  @Column(name = "USERNAME")
  private String name;

  @ManyToOne
  @JoinColumn(name = "TEAM_ID")
  private Team team;

  // Getter, Setter ...
}

@Entity
public class Team {

  @Id @GeneratedValue
  private Long id;
  private String name;

  // Getter, Setter ...
}
~~~

**연관관계 저장**
~~~ java
//팀 저장
Team team = new Team();
team.setName("TeamA");
em.persist(team);

//회원 저장
Member member = new Member();
member.setName("conas");
member.setTeam(team); //단방향 연관관계 설정, 참조 저장
em.persist(member);
~~~
이전의 코드와 다른점은 member에 teamId를 넣지 않고, team을 넣어준다는 것입니다.

**참조로 연관관계 조회 - 객체 그래프 탐색**
~~~ java
//조회
Member findMember = em.find(Member.class, member.getId());
//참조를 사용해서 연관관계 조회
Team findTeam = findMember.getTeam();
~~~

**연관관계 수정**
~~~ java
// 새로운 팀B
Team teamB = new Team();
teamB.setName("TeamB");
em.persist(teamB);

// 회원1에 새로운 팀B 설정
member.setTeam(teamB);
~~~

 ## @ManyToOne, @JoinColumn
 **@ManyToOne**: 다대일(N:1) 관계라는 매핑 정보입니다. 위에서 봤던 회원과 팀은 다대일 관계입니다. 연관관계를 매핑할 때 이렇게 다중성을 나타내는 어노테이션을 필수로 사용해야 합니다.

 속성|기능|기본값
 :---|:---|:---
 optional|false로 설정하면 연관된 엔티티가 항상 있어야 한다|true
 fetch|글로벌 패치 전략을 설정한다. (자세한 내용은 추후에)|@ManyToOne=FetchType.EAGER, @OneToMany=FetchType.LAZY
 cascade|영속성 전이 기능을 사용한다.(자세한 내용은 추후에)|
 targetEntity|연관된 엔티티의 타입 정보를 설정한다. 이 기능은 거의 사용하지 않음|

 **@JoinColumn**: 외래 키를 매핑할 때 사용합니다.

 속성|기능|기본값
 :---|:---|:---
 name|매핑할 외래 키 이름|"필드명" + "_" + "참조하는 테이블의 기본 키 컬럼명"
 referencedColumnName|외래 키가 참조하는 대상 테이블의 컬럼명|참조하는 테이블의 기본 키 컬럼명
 foreignKey(DDL)|외래 키 제약조건을 직접 지정할 수 있다. 이 속성은 테이블을 생성할 때만 사용한다.|
 unique|@Column의 속성과 같다|
 nullable|@Column의 속성과 같다|
 insertable|@Column의 속성과 같다|
 updatable|@Column의 속성과 같다|
 columnDefinition|@Column의 속성과 같다|
 table|@Column의 속성과 같다|

 > **JoinColumn 어노테이션은 생략 가능합니다.**
 @JoinColumn을 생략하면 외래 키를 찾을 때 기본 전략을 사용합니다.
 **기본 전략**: 필드명 + _ + 참조하는 테이블의 컬럼명

<file_sep>/Article/2019-11/19.md
# 2019-11-19 화요일

## [실리콘밸리에서 주목하는 ‘원페이지’ 협업툴](http://www.bloter.net/archives/358442)

- Summary
  - 협업은 오늘날 비즈니스 성공의 핵심 요소
  - 미국 같은 선도 시장의 경우 메신저는 ‘메신저형 협업툴’로, 그룹웨어는 차세대 그룹웨어로, 협업 커뮤니케이션은 ‘팀 협업툴’로 진화한 도구를 조합해 사용
  - 메신저를 사용하게 되면서 지식 근로자들은 평균 15분에 한번 꼴로 업무에 지장을 받는다고 함
  - 메신저 알림은 내가 원하는 시간에 울리는 것이 아니라, 상대방이 원할 때 나의 집중을 깨고 울리기 마련이고, 업무 효율은 떨어질 수밖에 없음
  - 실리콘 밸리에서 떠오르고 있는 서비스들은 우선 ‘트위스트(Twist)’처럼 메신저의 특징인 ‘실시간성’을 의도적으로 낮추는 방향을 택하고 있음
  - 메시지와 대화의 수발신, 즉 커뮤니케이션으로 협업의 효율성을 높이려고 하기보다 서비스를 실제 업무의 진행 방식과 워크플로우에 맞게 구현하는 방향으로 진화하고 있음<file_sep>/Article/2019-12/23.md
# 2019-12-23 월요일

## [한컴그룹, CES2020서 블록체인-스마트시티 솔루션 등 선보여](http://www.bloter.net/archives/365235)
- 한글과컴퓨터그룹이 오는 1월 7일부터 10일까지 미국 라스베이거스에서 열리는 소비자가전쇼(CES)2020에 3년 연속으로 참가한다.
- 한컴그룹은 이번 행사에서 한글과컴퓨터, 한컴MDS, 한컴로보틱스, 한컴위드, 한컴모빌리티, 아큐플라이AI 등 그룹 관계사들이 제공하는 블록체인, 인공지능, 로봇, 스마트시티 관련 제품 및 솔루션을 선보일 예정이다.
- 블록체인과 관련해서는 잠재력을 염두에 두고 출생 등록, 디지털 신분 증명, 학력 및 취업 이력 검증, 의료기록 관리, 디지털 자산 거래 등을 블록체인과 접목해 구현 가능한 미래 서비스들 위주로 ‘라이프 블록체인’ 모델을 제시한다.
- 스마트시티와 관련해서는 인공지능과 빅데이터 기반으로 도시 정책 설정 및 관리가 가능한 스마트시티 플랫폼인 ‘인텔리전트 시티 플랫폼(ICP)’과 사물인터넷(IoT) 디바이스 관리에 특화된 개방형 플랫폼 ‘네오아이디엠(NeoIDM)’을 전시하고 해외 스마트시티 시장 공략에 속도를 낸다는 방침이다.
<file_sep>/Git/gitignore.md

# .gitignore란?
 **깃에서 특정 파일 혹은 디렉토리를 관리 대상에서 제외할 때 사용하는 파일.**
 이 파일 안에 기입된 내용들은 모두 깃에서 관리하지 않겠다는 것을 의미합니다. 예를 들어 자동으로 생성되는 로그파일, 프로젝트 설정 파일 등을 관리 대상에서 제외할 수 있습니다.

# 인텔리제이에 플러그인 설치하기
인텔리제이에서는 .gitignore 파일에 대한 기본적인 지원이 없는 대신 플러그인에서 .gitignore 지원을 하고 있습니다.

.ignore 플러그인에서 지원하는 기능
- 파일 위치 자동완성
- 이그노어 처리 여부 확인
- 다양한 이그노어 파일 지원(.gitignore, .npmignore, .dockerignore 등등)

![image.png](https://images.velog.io/post-images/conatuseus/12694ff0-1f39-11ea-b8fb-8b8d39340afe/image.png)

Plugins의 Marketplace탭에서 .ignore 플러그인을 설치해줍니다.

# 사용하기

![image.png](https://images.velog.io/post-images/conatuseus/68572540-1f39-11ea-af4c-3f9f13d48807/image.png)

위와 같이 .gitignore 파일을 최상단 디렉토리에 생성합니다.
그리고 아래와 같이 작성합니다.
.gitignore 파일의 `.gradle`, `.idea`는  .gradle 파일과, .idea파일을 깃 체크 대상에서 제외한다는 뜻입니다.

![image.png](https://images.velog.io/post-images/conatuseus/c7d7ff80-1f39-11ea-af4c-3f9f13d48807/image.png)

> 문법은 [여기](https://nesoy.github.io/articles/2017-01/Git-Ignore)에 잘 나와있습니다.

# .gitignore가 정상적으로 작동하지 않는다면?

~~~ java
// 현재 Repository의 cache를 모두 삭제
git rm -r --cached .
// .gitignore에 넣은 파일 목록들을 제외하고 다른 모든 파일을 다시 track하도록 설정
git add .
// commit
git commit -m "fixed untracked files"
~~~

> 참고
>
> https://jojoldu.tistory.com/307
> https://nesoy.github.io/articles/2017-01/Git-Ignore
<file_sep>/Docker/README.md
# 목표
2019년 12월, 한달 내에 Docker 마스터.

# List
- [docker 사용해보기](https://github.com/Conatuseus/TIL/blob/master/Docker/Docker_%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0.md)
<file_sep>/Books/HTTP완벽가이드/2장.URL과 리소스.md
# 2장. URL과 리소스
URL(Uniform Resource Location)은 인터넷의 리소스를 가리키는 표준이름입니다,
URL은 전자정보 일부를 가리키고 그것이 어디에 있고 어떻게 접근할 수 있는지 알려줍니다.

**이번 글에서 다룰 내용**
- URL 문법, 여러 URL 컴포넌트가 어떤 의미를 가지며 무엇을 수행하는지
- 여러 웹 클라이언트가 지원하는 상대 URL과 확장 URL 같은 단축 URL에 대해서
- URL의 인코딩과 문자 규칙
- 여러 인터넷 정보 시스템에 적용되는 공통 URL 스킴
- 기존 이름은 유지하면서 객체들을 다른 장소로 옮기는 것을 가능하게 해주는 URN(Uniform Resource Name)을 포함한 URL의 미래

## 2.1 인터넷의 리소스 탐색하기
URL은 **브라우저가 정보를 찾는데 필요한 리소스의 위치를 가리키며, URL을 이용해 사람과 애플리케이션이 인터넷상의 수십억 개의 리소스를 찾고 사용하며 공유**할 수 있습니다. 그리고 URL을 통해 사람이 HTTP 및 다른 프로토콜을 통해 접근할 수 있습니다. 사용자는 브라우저에 URL을 입력하고 브라우저는 화면 뒤에서 사용자가 원하는 리소스를 얻기 위해서 적절한 프로토콜을 사용하여 메세지를 전송합니다.

 http://www.joes-hardware.com/seasonal/index-fall.html 이라는 URL을 불러오고 싶다고 해보자

- URL의 첫 부분인 `http`는 URL의 스킴입니다. 스킴은 **웹 클라이언트가 리소스에 어떻게 접근하는지** 알려줍니다. 이 경우에, URL이 HTTP 프로토콜을 사용합니다.
- URL의 두 번째 부분인 `www.joes-hardware.com`은 서버의 위치입니다. 이는 **웹 클라이언트가 리소스를 어디에 호스팅 되어 있는지** 알려줍니다.
- URL의 세 번째 부분인 `/seasonal/index-fall.html`은 리소스의 경로입니다. 경로는 **서버에 존재하는 로컬 리소스들 중에서 요청받은 리소스가 무엇인지** 알려줍니다.

![image.png](https://images.velog.io/post-images/conatuseus/7a6edbe0-271a-11ea-abb6-2547b77625f4/image.png)

URL은 **HTTP 프로토콜이 아닌 다른 가용한 프로토콜을 사용할 수도 있습니다.**

mailto:<EMAIL> 는 이메일 주소를 가리키며,

ftp://ftp.lots-o-books.com/pub/complete-price-list.xls 는 FTP(File Transfer Protocol) 서버에 올라가 있는 파일을 가리키고,

rtsp://www.joes-hardware.com:554/interview/cto_video 는 스트리밍을 제공하기 위해 비디오 서버에 호스팅하고 있는 영화를 가리킵니다.

이렇게 **URL은 인터넷에 있는 어떤 리소스든지 가리킬 수 있습니다.**

 URL을 사용하면 **리소스를 일관된 방식으로 지칭할 수 있습니다.** 대부분의 URL은 동일하게 `스킴://서버위치/경로` 구조로 이루어져 있습니다. 따라서 인터넷상의 모든 리소스를 가리키고 가져오기 위해, 그리고 모든 사람이 같은 방식으로 이름을 써서 리소스를 찾을 수 있도록, 단일 방식의 작명 규칙을 가진 것입니다.

 ## 2.2 URL 문법
 URL로 인터넷 상의 모든 리소스를 찾을 수 있지만, 그 리소스들은 다른 스킴(예를 들어 HTTP, FTP, SMTP)을 통해 접근할 수 있으며, URL 문법은 스킴에 따라서 달라집니다. 하지만 대부분의 URL은 일반 URL 문법을 따르며, 서로 다른 URL 스킴도 형태와 문법 면에서 매우 유사합니다.

 `<스킴>://<사용자 이름>:<비밀번호>@<호스트>:<포트>/<경로>;<파라미터>?<질의>#<프래그먼트>`

 대부분의 URL 스킴의 문법은 위와 같이 9개 부분으로 나뉩니다. 하지만 이 모든 컴포넌트를 가지는 URL은 거의 없고, 가장 중요한 세가지 컴포넌트는 **스킴, 호스트, 경로**입니다.

 ### 2.2.1 스킴: 사용할 프로토콜
 스킴은 **주어진 리소스에 어떻게 접근하는지 알려주는 중요한 정보입니다.** 이는 URL을 해석하는 애플리케이션이 어떤 프로토콜을 사용하여 리소스를 요청해야 하는지 알려줍니다.

 ### 2.2.2 호스트와 포트
 애플리케이션이 인터넷에 있는 리소스를 찾으려면, 리소스를 호스팅하고 있는 장비와 그 장비 내에서 리소스에 접근할 수 있는 서버가 어디에 있는지 알아야 합니다. URL의 호스트와 포트 컴포넌트는 그 두 가지 정보를 제공해줍니다.
 호스트 컴포넌트는 접근하려고 하는 리소스를 가지고 있는 인터넷상의 호스트 장비를 가리킵니다.
 해당 값은 위에서와 같이(`www.joes-hardware.com`) 호스트 명이나 IP 주소로 제공합니다.
 포트 컴포넌트는 서버가 열어놓은 네트워크 포트를 가리킵니다. 내부적으로 TCP 프로토콜을 사용하는 HTTP는 기본 포트로 80을 사용합니다.

 ### 2.2.3 사용자 이름과 비밀번호
 많은 서버가 자신이 가지고 있는 데이터에 접근을 허용하기 전에 사용자 이름과 비밀번호를 요구합니다. 좋은 예로 FTP 서버가 있습니다.

 1. ftp://ftp.prep.ai.mit.edu/pub/gnu
 	: 사용자 이름이나 비밀번호 컴포넌트가 없이 표준 스킴, 호스트, 경로만 있습니다. 애플리케이션이 FTP와 같이 사용자 이름과 비밀번호를 요구하는 URL 스킴을 사용한다면, 그 값들이 삽입되어 있지 않을 경우 기본 사용자 이름과 비밀번호 값을 넣어놓을 것입니다. 예를 들어, 사용자 이름과 비밀번호를 기술하지 않고 FTP URL에 접근하면, 기본 사용자 이름 값으로 `anonymous`, 비밀번호는 브라우저마다 가지고 있는 기본 값을 사용합니다.
 2. ftp://anonymous@ftp.<EMAIL>.mit.edu/pub/gnu
 	: 사용자 이름이 `anonymous`로 되어 있습니다. `@` 문자는 URL로부터 사용자 이름과 비밀번호 컴포넌트를 분리합니다.
 3. ftp://anonymous:my_passwd@ftp.prep.ai.mit.edu/pub/gnu
 	: 사용자 이름(`anonymous`)과 비밀번호(`my_passwd`)를 `:` 문자로 분리하여 모두 기술하였습니다.
 4. http://joe:joespasswd@www.joes-hardware.com/sales_info.txt

### 2.2.4 경로

URL의 경로 컴포넌트는 리소스가 서버의 어디에 있는지 알려줍니다. 해당 결로는 아래의 예와 같이 계층적 파일 시스템 경로와 유사한 구조를 가집니다.

> http://www.joes-hardware.com:80/seasonal/index-fall.html

이 경로는 `/seasonal/index-fall.html`로 유닉스 파일 시스템의 파일 경로와 유사합니다. 경로는 서버가 리소스의 위치를 찾는데 사용하는 정보입니다. HTTP URL에서 경로 컴포넌트는 `/`  문자를 기준으로 경로조각으로 나뉩니다. 각 경로조각은 자체만의 파라미터 컴포넌트를 가질 수 있습니다.

### 2.2.5 파라미터

많은 스킴이 객체에 대한 호스트 및 경로 정보만으로는 리소스를 찾지 못합니다. 서버가 어떤 포트를 열어놓고 있는지, 리소스에 접근하기 위해 사용자 이름과 비밀번호를 명시했는지 여부 외에도 많은 프로토콜이 더 많은 정보를 요구합니다.

URL을 사용하는 애플리케이션이 리소스에 접근하려면 프로토콜 파라미터가 필요합니다. 프로토콜 파라미터가 없으면, 다른 한편에 있는 서버는 그 요청을 잘못 처리하거나 처리를 하지 않을 것입니다.

URL의 파라미터 컴포넌트는, 애플리케이션이 서버에 정확한 요청을 하기 위해 필요한 입력 파라미터를 받는데 사용합니다. 이 컴포넌트는 이름/값 쌍의 리스트로 URL 나머지 부분들로부터 `;` 문자로 구분하여 URL에 기술합니다. 이를 통해 애플리케이션이 리소스에 접근하는데 필요한 어떤 추가 정보든 전달할 수 있습니다.

> http://www.joes-hardware.com/hammers;sale=false/index.html;graphics=true

위 URL에는 hammers와 index.html이라는 두 개의 경로조각이 있습니다. Hammers 경로조각은 값이 false인 sale이라는 파라미터를 가집니다. index.html 경로조각은 값이 true인 graphics란 파라미터를 가집니다.

### 2.2.6 질의 문자열

데이터베이스 같은 서비스들은 요청받을 리소스 형식의 범위를 좁히기 위해서 질문이나 질의를 받을 수 있습니다.

죠의 컴퓨터 가게가 판매되지 않은 상품의 재고 리스트가 있고, 어떤 상품들이 비축되어있는지 조회할 수 있다고 해봅시다. 다음 URL은 아이템 번호 12731의 재고가 있는지 확인하기 위해서 웹 데이터베이스 게이트웨이에 질의하는데 사용됩니다.

> http://www.joes-hardware.com/inventory-check.cgi?item=12731

물음표(?)의 우측에 있는 값들을 질의 컴포넌트라 부릅니다. URL의 질의 컴포넌트는 게이트웨이를 가리키는 URL의 경로 컴포넌트와 함께 전달하고 있습니다. 보통 게이트웨이는, 다른 애플리케이션에 접근하려고 할 때 거치는 통로라고 할 수 있습니다.

![image](https://user-images.githubusercontent.com/22893111/71613411-aee77480-2be9-11ea-8fe2-1a29e1ea57b4.png)

### 2.2.7 프래그먼트

HTML 같은 리소스 형식들은 본래의 수준보다 더 작게 나뉠 수 있습니다. 예를 들어 절(paragraph)이 포함된 용량이 큰 한 개의 텍스트 문서의 경우, 그 리소스에 대한 URL은 텍스트 문서 전체를 가리키겠지만, 이상적으로는 리소스 안에 있는 특정 절을 가리킬 수 있어야 합니다.

리소스의 특정 부분을 가리킬 수 있도록, URL은 리소스 내의 조각을 가리킬 수 있는 프래그먼트 컴포넌트를 제공합니다. 예를 들어 URL은 HTML 문서에 있는 특정 이미지나 일부분을 가리킬 수 있습니다.

프래그먼트는 URL의 오른쪽에 `#` 문자에 이어서 옵니다. 예를 들면 다음과 같습니다.

> http://www.joes-hardware.com/tools.html#drills

이 예에서 drills라는 프래그먼트는 죠의 컴퓨터 가게 웹 서버에 위치한 /tools.html 웹페이지의 일부를 가리킵니다. 그 부분을 'drills'라고 기술하였습니다.

일반적으로 HTTP 서버는 객체 일부가 아닌 전체만 다루기 때문에, 클라이언트는 서버에 프래그먼트를 전달하지 않습니다. 브라우저가 서버로부터 전체 리소스를 내려받은 후, 프래그먼트를 사용하여 당신이 보고자 하는 리소스의 일부를 보여줍니다.

![image](https://user-images.githubusercontent.com/22893111/71613548-89a73600-2bea-11ea-82ff-d81eafee77a3.png)

## 2.3 단축 URL

웹 클라이언트는 몇몇 단축 URL을 인식하고 사용합니다. 상대 URL은 리소스 안에 있는 리소스를 간결하게 기술하는데 사용할 수 있습니다. 많은 브라우저가 사용자가 기억하고 있는 URL 일부를 입력하면 나머지 부분을 자동으로 입력해주는 URL '자동 확장'을 지원합니다.

### 2.3.1 상대 URL

URL은 상대 URL과 절대 URL 두 가지로 나뉩니다. 절대 URL는 리소스에 접근하는데 필요한 모든 정보를 가지고 있습니다.

그와 달리 상대 URL은 모든 정보를 담고 있지는 않습니다. 상대 URL로 리소스에 접근하는데 필요한 모든 정보를 얻기 위해서는, 기저(base)라고 하는 다른 URL을 사용해야 합니다.

 상대 URL은 URL을 짧게 표기하는 방식입니다. 아래는 상대 URL이 포함된 HTML 문서 중 일부입니다.

~~~ html
<p>Joe's Hardware Online has the largest selection of <a href="./hammers.html"> hammers </a>a> on earth</p>
~~~

위는 http://www.joes-hardware.com/tools.html가 가리키는 리소스인 HTML 문서의 내용을 보여줍니다.  `./hammers.html` URL을 가리키는 하이퍼링크가 있습니다. 이는 문서의 URL을 기준으로 상대경로로 해석될 수 있습니다. 이 경우 죠의 컴퓨터 가게 웹 서버의 /tools.html 리소스를 기준으로 상대경로로 명시되었습니다.

상대 URL 문법에 따르면, HTML 작성자는 URL에 스킴과 호스트 그리고 다른 컴포넌트들을 모두 입력하지 않아도 됩니다. 그 정보는 컴포넌트가 포함된 리소스의 기저 URL에서 알아낼 수 있습니다. 다른 리소스에 대한 URL 역시 이 상대 URL로 기술할 수 있습니다.

위의 기저 URL은 다음과 같습니다.

> http://www.joes-hardware.com/tools.html

이 URL을 기저 URL로 사용하여, 상대 URL에서는 기술하지 않은 정보를 추측할 수 있습니다. 필요한 리소스가 `./hammers.html` 이라는 것을 알지만 스킴이나 호스트는 모릅니다. 기저 URL을 사용하면 스킴은 http이고 호스트는 www.joes-hardware.com라는 것을 추측할 수 있습니다.

![image](https://user-images.githubusercontent.com/22893111/71614067-09ce9b00-2bed-11ea-9fe4-a6d5329f23d8.png)

상대 URL은 프래그먼트이거나 URL의 일부입니다. URL을 처리하는 브라우저 같은 애플리케이션은 상대 URL과 절대 URL 간에 상호 변환을 할 수 있어야 합니다.

상대 URL을 사용하면 리소스 집합을 쉽게 변경할 수 있습니다. 그리고 문서 집합의 위치를 변경하더라도, 새로운 기저 URL에 의해서 해석될 것이기 때문에 위치를 변경하더라도 잘 동작할 것입니다.

### 기저 URL

변환 과정의 첫 단계는 기저 URL을 찾는 것입니다. 기저 URL은 상대 URL의 기준이 됩니다. 그것을 가져오는 몇 가지 방법이 있습니다.

#### 1. 리소스에서 명시적으로 제공

어떤 리소스들은 기저 URL을 명확하게 기술하기도 합니다. 예를 들어 HTML 문서에서는 그 안에 있는 모든 상대 URL을 변경하기 위해서 기저 URL을 가리키는 <BASE> HTML 태그를 기술할 수 있습니다.

#### 2. 리소스를 포함하고 있는 기저 URL

만약 상대 URL이 기저 URL이 명시되지 않은 리소스에 포함된 경우, 해당 리소스의 URL을 기저 URL로 쓸 수 있습니다.

#### 3. 기저 URL이 없는 경우

기저 URL이 없는 경우도 있습니다. 이런 경우는 절대 URL만으로 이루어져 있다는 뜻입니다. 하지만 불완전하거나 깨진 URL일 수도 있습니다.

![image-20191231165630960](/Users/conatuseus/Library/Application Support/typora-user-images/image-20191231165630960.png)<file_sep>/JPA/JPA프로그래밍/엔티티매핑_2.md
> 이번 글에서는 <기본키 매핑, 필드와 컬럼 매핑>에 대해 알아보겠습니다.
이 시리즈 글은 김영한 님의 [강의](https://www.inflearn.com/course/ORM-JPA-Basic), [책](https://book.naver.com/bookdb/book_detail.nhn?bid=9252528)을 보고 적은 것임을 알려드립니다. (강추)

# 기본 키 매핑
---

JPA가 제공하는 데이터베이스 기본 키 생성 전략은 다음과 같습니다.
- **직접 할당**: 기본 키를 애플리케이션에서 직접 할당, **@Id만 사용**
- **자동 생성(GeneratedValue)**: 대리 키 사용 방식
 + IDENTITY: 기본 키 생성을 데이터베이스에 위임한다, MySQL
 + SEQUENCE: 데이터베이스 시퀀스를 사용해서 기본 키를 할당한다. ORACLE. @SequenceGenerator 필요
 + TABLE: 키 생성 테이블을 사용한다. 모든 DB에서 사용. @TableGenerator 필요
 + AUTO: 방언에 따라 자동 지정, 기본 값

 > 이제 자동 생성하는 방법들에 대해 자세히 알아보자

 ## IDENTITY 전략
 ---
 IDENTITY는 기본 키 생성을 데이터베이스에 위임하는 전략입니다.
 주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용됩니다. IDENTITY 전략은 **데이터베이스에 값을 저장하고 나서야 기본 키 값을 구할 수 있을 때 사용합니다.**

 ~~~ java
 // IDENTITY 매핑 코드
@Entity
public class Member{

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  ...
  }
 ~~~

 ~~~ java
private static void logic(EntityManager em) {
  Member member = new Member();
  em.persist(member);
  System.out.println("member.id = " + member.getId());
}
// 출력: member.id = 1
 ~~~

 위 코드를 처음 보고, **"트랜잭션 커밋을 하지 않았는데 실행되나?" 라는 의문**을 가졌습니다.
 하지만 아래와 같은 이유를 보고 가능하다는 것을 알았습니다.
 > ❗주의
 엔티티가 영속 상태가 되려면 식별자가 반드시 필요합니다. 그런데 IDENTITY 식별자 생성 전략은 엔티티를 데이터베이스에 저장해야 식별자를 구할 수 있으므로 em.persist()를 호출하는 즉시 INSERT SQL이 데이터베이스에 전달됩니다. 따라서 이 전략은 **트랜잭션을 지원하는 쓰기 지연이 동작하지 않습니다.**

 ## SEQUENCE 전략
 ---
 데이터베이스 **시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트입니다.** SEQUENCE 전략은 이 시퀀스를 사용해서 기본 키를 생성합니다. 이 전략은 시퀀스를 지원하는 오라클, PostgreSQL, DB2, H2 데이터베이스에 사용됩니다.
 ~~~ sql
// 시퀀스 DDL
CREATE TABLE MEMBER (
	ID BIGINT NOT NULL PRIMARY KEY,
    DATA VARCHAR(255)
)

// 시퀀스 생성
CREATE SEQUENCE MEMBER_SEQ START WITH 1 INCREMENT BY 1;
 ~~~

 ~~~ java
// 시퀀스 매핑 코드
@Entity
@SequenceGenerator(
  name = "MEMBER_SEQ_GENERATOR",
  sequenceName = "MEMBER_SEQ", //매핑할 데이터베이스 시퀀스 이름
  initialValue = 1, allocationSize = 1)
public class Member{

  @Id
  @GeneratedValue(strategy = GenerationType.SEQUENCE,
                  generator = "MEMBER_SEQ_GENERATOR")
  private Long id;
  ...
  }
 ~~~

 ~~~ java
// 시퀀스 사용 코드
private static void logic(EntityManager em) {
  Member member = new Member();
  em.persist(member);
  System.out.println("member.id = " + member.getId());
}
// 출력: member.id = 1
 ~~~

 시퀀스 사용 코드는 IDENTITY 전략과 같지만 내부 동작 방식은 다릅니다.
 SEQUENCE 전략은 em.persist()를 호출할 때 먼저 데이터베이스 시퀀스를 사용해서 식별자를 조회합니다.
 그리고 조회한 식별자를 엔티티에 할당한 후에 엔티티를 영속성 컨텍스트에 저장합니다. 이후 트랜잭션을 커밋해서 플러시가 일어나면 엔티티를 데이터베이스에 저장합니다.
 반대로 이전에 설명했던 IDENTITY 전략은 먼저 엔티티를 데이터베이스에 저장한 후에 식별자를 조회해서 엔티티의 식별자에 할당합니다.

 **@SequenceGenerator 속성**

 속성|기능|기본값
 ---|---|---
 name|식별자 생성기 이름|필수
 sequenceName|데이터베이스에 등록되어 있는 시퀀스 이름|hibernate_sequence
 initialValue|DDL 생성 시에만 사용됨. 시퀀스 DDL을 생성할 때 처음 시작하는 수를 지정한다.|1
 allocationSize|시퀀스 한 번 호출에 증가하는 수(성능 최적화에 사용)|**50**
 catalog, schema|데이터베이스 catalog, schema 이름||

 > ❗주의: allocationSize의 기본값이 50인 것

 ## TABLE 전략
 ---
 TABLE 전략은 키 생성 전용 테이블을 하나 만들고 여기에 이름과 값으로 사용할 컬럼을 만들어 데이터베이스 시퀀스를 흉내내는 전략입니다.
 - 장점: 모든 데이터베이스에 적용 가능
 - 단점: 성능이 떨어짐

 ~~~ sql
// TABLE 전략 키 생성 DDL
create table MY_SEQUENCE(
	sequence_name varchar(255) not null ,
    next_val bigint,
    primary key ( sequence_name )
)
 ~~~

 sequence_name 컬럼을 시퀀스 이름으로 사용하고 next_val 컬럼을 시퀀스 값으로 사용합니다.

 ~~~ java
// TABLE 전략 매핑 코드
@Entity
@TableGenerator(
  name = "BOARD_SEQ_GENERATOR",
  table = "MY_SEQUENCES",
  pkColumnValue = "BOARD_SEQ", allocationSize = 1)
public class Member {

  @Id
  @GeneratedValue(strategy = GenerationType.TABLE,
                  generator = "BOARD_SEQ_GENERATOR")
  private Long id;
  ...
  }
 ~~~
 ~~~ java
// TABLE 전략 매핑 사용 코드
private static void logic(EntityManager em) {
  Member member = new Member();
  em.persist(member);
  System.out.println("member.id = " + member.getId());
}
// 출력: member.id = 1
 ~~~

 **@TableGenerator 속성**

 속성|설명|기본값
 ---|---|---
 name|식별자 생성기 이름|필수
 table|키생성 테이블명|hibernate_sequences
 pkColumnName|시퀀스 컬럼명|sequence_name
 valueColumnName|시퀀스 값 컬럼명|next_val
 pkColumnValue|키로 사용할 값 이름|엔티티 이름
 initialValue|초기 값, 마지막으로 생성된 값이 기준|0
 allocationSize|시퀀스 한 번 호출에 증가하는 수|**50**
 catalog, schema|데이터베이스 catalog, schema 이름||
 uniqueConstraints(DDL)|유니크 제약 조건을 지정할 수 있다||

 ## 권장하는 식별자 전략
 ---
 - 데이터베이스 기본 키 제약 조건: null 아님, 유일, **변하면 안된다**
 - 하지만 미래까지 이 조건을 만족하는 자연키는 찾기 어렵다. 대리키(대체키)를 사용하자.
 - 예를 들어 주민등록번호도 기본 키로 적절하지 않다. (회원 테이블의 기본 키로 주민등록번호를 사용하고 있는데, 나라에서 주민등록번호를 보관하지 말라고 한다면 기본키를 바꾸면서 연관된 테이블도 바꿔야하는 참사가 일어날 수 있음)
 - **권장: Long형 + 대체키 + 키 생성전략 사용** (결론적으로, AUTO_INCREMENT나 SEQUENCE 오브젝트 등을 사용하자)

 ## SEQUENCE 전략과 TABLE 전략의 allocationSize 기본값이 50인 이유?
 ---
 결론은 **데이터베이스에 여러번 접근하는 것을 방지해서 성능적으로 장점이 있기 때문입니다.**
  - **SEQUENCE 전략에서는...**
    SEQUENCE 전략은 데이터베이스 시퀀스를 통해 식별자를 조회하는 추가 작업이 필요합니다. 따라서 다음과 같이 **데이터베이스와 2번의 통신을 합니다.**
    1. 식별자를 구하려고 데이터베이스 시퀀스를 조회한다.
       ex) SELECT MEMBER_SEQ.NEXTVAL ...
    2. 조회한 시퀀스를 기본 값으로 사용해 데이터베이스에 저장한다.
       ex) INSERT INTO MEMBER ...

    위와 같이 시퀀스에 접근하는 것을 줄이기 위해 JPA는 @SequenceGenerator.**allocationSize**를 사용합니다. 간단히 설명하면, 여기에 설정한 값만큼 한 번에 시퀀스 값을 증가시키고 나서 그만큼 메모리에 시퀀스 값을 할당합니다.
    예를들어, allocation 값이 50이면 시퀀스를 한 번에 50을 증가시킨 다음에 1 ~ 50까지는 메모리에 시퀀스 값을 할당합니다. 그리고 51이 되면 시퀀스 값을 100으로 증가시킨 다음 51 ~ 100까지 메모리에서 식별자를 할당합니다.

    이 최적화 방법은 시퀀스 값을 선점하므로 여러 JVM이 동시에 동작해도 기본 키 값이 충돌하지 않는 장점이 있습니다. 반면에 데이터베이스에 직접 접근해서 데이터를 등록할 때 시퀀스 값이 한 번에 많이 증가한다는 점을 염두해두어야 합니다. 이런 상황이 부담스럽고 INSERT 성능이 중요하지 않으면 allocationSize의 값을 1로 설정하면 됩니다.

- **TABLE 전략에서는...**
  TABLE 전략은 값을 조회하면서 SELECT 쿼리를 사용하고 다음 값으로 증가시키기 위해 UPDATE 쿼리를 사용합니다. 이 전략은 SEQUENCE 전략과 비교해서 데이터베이스와 한 번 더 통신하는 단점이 있습니다. 이 전략도 최적화하기 위해 allocationSize를 사용하면 되고, 방법은 SEQUENCE 전략과 같습니다.

 # 필드와 컬럼 매핑
 ---
 JPA가 제공하는 필드와 컬럼 매핑용 어노테이션들을 레퍼런스 형식으로 보여드리겠습니다.

 필드와 컬럼 매핑 분류

 분류|매핑 어노테이션|설명
 :---|:---|:---
 필드와 컬럼 매핑|@Column|컬럼을 매핑
 |@Enumerated|자바의 enum 타입을 매핑
 |@Temporal|날짜 타입을 매핑
 |@Lob|BLOB, CLOB 타입을 매핑
 |@Transient|특정 필드를 데이터베이스에 매핑하지 않음
 기타|@Access|JPA가 엔티티에 접근하는 방식을 지정

 ## @Column
 ---
 @Column은 객체 필드를 테이블 컬럼에 매핑합니다. 가장 많이 사용되고 기능도 많습니다. 속성 중에 name, nullabe이 주로 사용되고 나머지는 잘 사용되지 않는 편입니다.
 insert, updatable 속성은 데이터베이스에 저장되어 있는 정보를 읽기만 하고 실수로 변경하는 것을 방지하고 싶을 때 사용합니다.

 속성|기능|기본값
 :---|:---|:---
 name|필드와 매핑할 데이틀의 컬럼 이름|
 insertable|엔티티 저장 시 이 필드도 같이 저장한다. false로 설정하면 이 필드는 데이터베이스에 저장하지 않는다. false 옵션은 읽기 전용일 때 사용한다.|true
 updatable|엔티티 수정 시 이 필드도 같이 수정한다. false로 설정하면 데이터베이스에 수정하지 않는다. false 옵션은 읽기 전용일 때 사용한다.|true
 table|하나의 엔티티를 두 개 이상의 테이블에 매핑할 때 사용한다. 지정한 필드를 다른 테이블에 매핑할 수 있다.| 현재 클래스가 매핑된 테이블
 nullable(DDL)|null 값의 허용 여부를 설정한다. false로 설정하면 DDL 생성 시에 not null 제약조건이 붙는다|true
 unique(DDL)|@Table의 uniqueConstraints와 같지만 한 컬럼에 간단히 유니크 제약조건을 걸 때 사용한다. 만약 두 컬럼 이상을 사용해서 유니크 제약조건을 사용하려면 클래스 레벨에서 @Table.uniqueConstraints를 사용해야 한다.|
 columnDefinition(DDL)|데이터베이스 컬럼 정보를 직접 줄 수 있다.|필드의 자바 타입과 방언 정보를 사용해서 적절한 컬럼 타입을 생성한다.
 length(DDL)|문자 길이 제약조건, String 타입에만 사용한다.|255
 precision, scale(DDL)|BigDecimal 타입에서 사용한다(BigInteger도 사용할 수 있다). precision은 소수점을 포함한 전체 자릿수를, scale은 소수의 자릿수다. 참고로 double, float 타입에는 적용되지 않는다. 아주 큰 숫자나 정밀한 소수를 다루어야 할 때만 사용한다.|precision=19, scale=2

 > **@Column을 생략한다면?**
 컬럼 어노테이션을 생략하면 대부분 @Column 속성의 기본값이 적용되는데, 자바 기본 타입일 때는 nullable 속성에 예외가 있습니다.
 **@Column 생략 시**: 객체 타입일 때는 nullable이 기본값인 true가 되지만, **자바 기본 타입일 때는 JPA가 not null 제약조건을 추가합니다.**
 **@Column 사용 시**: **자바 기본 타입에 대해 not null 제약조건을 추가하지 않습니다. 따라서 자바 기본 타입에 @Column을 사용하면 nullable = false로 지정하는 것이 안전합니다.**

 ## @Enumerated
 ---
 자바의 enum 타입을 매핑할 때 사용합니다.

 속성|기능|기본값
 :---|:---|:---
 value| **EnumType.ORDINAL**: enum 순서를 데이터베이스에 저장, **EnumType.STRING**: enum 이름을 데이터베이스에 저장|EnumType.ORDINAL

 ❗ 기본값인 ORDINAL은 enum의 순서가 바뀌면 안되므로 EnumType.STRING을 권장합니다.

 ## @Lob
 ---
 데이터베이스 BLOB, CLOB 타입과 매핑합니다.

 @LOB에는 지정할 수 있는 속성이 없습니다. 대신에 매핑하는 필드 타입이 문자면 CLOB으로 매핑하고 나머지는 BLOB으로 매핑합니다.
 - CLOB: String, char[], java.sql.CLOB
 - BLOB: byte[], java.sql.BLOB

 ## @Transient
 ---
 이 필드는 매핑하지 않습니다. 따라서 데이터베이스에 저장하지 않고 조회하지도 않습니다. 객체에 임시로 어떤 값을 보관하고 싶을 때 사용합니다.

 ## @Access
 ---
 JPA가 엔티티 데이터에 접근하는 방식을 지정합니다.
 - 필드 접근: AccessType.FIELD는 필드에 직접 접근합니다. 필드 접근 권한이 private이어도 접근할 수 있습니다.
 - 프로퍼티 접근: AccessType.PROPERTY로 지정합니다. 접근자(getter)를 사용합니다.
<file_sep>/OOP/OCP 5대 원칙.md

# SRP(Single Responsebility Principle): 단일 책임 원칙

**객체는 단 하나의 책임만 가져야 한다.**

설계를 잘한 프로그램은 기본적으로 새로운 요구사항과 프로그램 변경에 영향을 받는 부분이 적다. 다시말해, 응집도는 높고 결합도는 낮은 프로그램을 뜻한다. 만약 한 클래스가 수행할 수 있는 기능, 즉 책임이 많아진다. 책임이 많아지면 클래스 내부의 함수끼리 강한 결합을 발생할 가능성이 높아진다. 이는 유지보수에 비용이 증가하게 되므로 따라서 책임을 분리시킬 필요가 있다.

# OCP(Open-Closed Principle): 개방 폐쇄 원칙

**기존의 코드를 변경하지 않으면서 기능을 수정하거나 추가할 수 있어야 한다.**

OCP에 만족하는 설계를 할 때 **변경되는 것이 무엇인지에 초점**을 맞춘다. 자주 변경되는 내용은 수정하기 쉽게 설계 하고, 변경되지 않아야 하는 것은 수정되는 내용에 영향을 받지 않게 하는 것이 포인트다. 이를 위해 **자주 사용되는 문법이 인터페이스(Interface)**이다

# LSP(Liskov Substitution Principle): 리스코프 치환 원칙

**자식 클래스는 최소한 자신의 부모 클래스에서 가능한 행위는 수행할 수 있어야 한다.**

LSP는 부모 클래스와 자식 클래스 사이의 행위가 일관성이 있어야 한다는 의미다.
즉, LSP를 만족하면 프로그램에서 부모 클래스의 인스턴스 대신에 자식 클래스의 인스턴스로 대체해도 프로그램의 의미는 변화되지 않는다.

상속 관계에서는 일반화 관계(IS-A)가 성립해야 한다. 일반화 관계에 있다는 것은 일관성이 있다는 것이다. 따라서 리스코프 치환 원칙은 일반화 관계에 대해 묻는 것이라 할 수 있다.

LSP를 만족시키는 간단한 방법은 재정의하지 않는 것이다.
즉, 부모 클래스에서 상속받은 메서드들이 자식 클래스에 오버라이드, 즉 재정의되지 않도록 하면 된다.

# ISP(Interface Segregation Principle): 인터페이스 분리 원칙

**한 클래스는 자신이 사용하지 않는 인터페이스는 구현하지 말아야 한다. 하나의 일반적인 인터페이스보다는, 여러 개의 구체적인 인터페이스가 낫다.**

예시)

![image](https://user-images.githubusercontent.com/22893111/74603087-18b4d280-50f3-11ea-9de7-e390e5962734.png)

- 복합기 기능을 제공하는 클래스는 매우 비대해질 가능성이 크다.
- 하지만 복합기의 모든 기능을 클라이언트가 동시에 사용하는 경우는 거의 없다.
- 팩스 기능의 변경으로 인해 프린터 기능만 이용하는 클라이언트가 영향을 받을 수 있다.
- 이렇게 클라이언트와 무관하게 발생한 변화로 클라이언트 자신이 영향을 받지 않으려면 범용의 인터페이스보다는 클라이언트에 특화된 인터페이스를 사용해야 한다.

ISP를 만족하는 복합기 클래스 다이어그램

![image](https://user-images.githubusercontent.com/22893111/74603089-1c485980-50f3-11ea-8de0-375dd84c2d59.png)

- 복합기를 사용하는 객체들마다 자신이 관심을 갖는 메서드들만 있는 인터페이스를 제공받도록 설계했다.
- 인터페이스가 일종의 방화벽 역할을 수행
- 클라이언트는 자신이 사용하지 않는 메서드에 생긴 변화로 인한 영향을 받지 않게 된다.

# DIP(Dependency Inversion Principle): 의존 역전 원칙

**의존 관계를 맺을 때 변화하기 쉬운 것 또는 자주 변화하는 것보다는 변화하기 어려운 것, 거의 변화가 없는 것에 의존하라는 것.**

DIP를 만족하는 설계 예시

![image](https://user-images.githubusercontent.com/22893111/74603210-8a415080-50f4-11ea-82b4-7330de04b89c.png)

- 객체지향 관점에서 변하기 어려운 추상적인 것들을 표현하는 수단
  - **추상 클래스**
  - **인터페이스**

- **DIP를 만족시키는 방법**
  - 어떤 클래스가 도움을 받을 때 구체적인 클래스보다는 **인터페이스나 추상 클래스**와 의존 관계를 맺도록 설계해야 한다.<file_sep>/JPA/JPA프로그래밍/엔티티매핑_1.md
> 이번 글에서는 객체와 테이블 매핑, 데이터베이스 스키마 자동 생성에 대해 알아보겠습니다.
이 시리즈 글은 김영한 님의 [강의](https://www.inflearn.com/course/ORM-JPA-Basic), [책](https://book.naver.com/bookdb/book_detail.nhn?bid=9252528)을 보고 적은 것임을 알려드립니다. (강추)

# 엔티티 매핑
---
 JPA를 사용하는 데 가장 중요한 일은 엔티티와 테이블을 정확하게 매핑하는 것입니다.
 따라서 매핑 어노테이션을 숙지하고 사용해야 합니다. JPA는 다양한 매핑 어노테이션을 지원하는데 크게 4가지로 분류할 수 있습니다.

 |매핑 종류|대표 어노테이션|
 |---|-----|
 |**객체와 테이블 매핑**|@Entity, @Table|
 |**기본 키 매핑**|@Id|
 |**필드와 컬럼 매핑**|@Column|
 |**연관관계 매핑**|@ManyToOne, @JoinColumn|

 ## 객체와 테이블 매핑
 ---
 ### @Entity
 - JPA를 사용해서 테이블과 매핑할 클래스는 **@Entity** 어노테이션을 필수로 붙여야 합니다.
 - **@Entity**가 붙은 클래스는 JPA가 관리하고, **엔티티**라 부릅니다.
 - @Entity 적용 시 **주의사항**
  + **기본 생성자가 필수**(파라미터가 없는 public 또는 protected 생성자)
  + final 클래스, enum, interface, inner 클래스에는 사용 불가
  + 저장할 필드에 final 사용 불가

@Entity 속성 정리

|속성|기능|기본값|
|---|---|---|
|name|JPA에서 사용할 엔티티 이름을 지정한다. 보통 기본값인 클래스 이름을 사용한다. 만약 다른 패키지에 이름이 같은 엔티티 클래스가 있다면 이름을 지정해서 충돌하지 않도록 해야 한다.|설정하지 않으면 클래스 이름을 그대로 사용|

 ### @Table
 @Table은 엔티티와 매핑할 테이블을 지정합니다. 생략하면 매핑한 엔티티 이름을 테이블 이름으로 사용합니다.

 @Table 속성 정리

 |속성|기능|기본값|
 :---|:---|:---|
 name|매핑할 테이블 이름|엔티티 이름을 사용|
 catalog|catalog 기능이 있는 데이터베이스에서 catalog를 매핑한다||
 schema|schema 기능이 있는 데이터베이스에서 schema를 매핑한다||
 uniqueConstraints (DDL)|DDL 생성 시에 유니크 제약조건을 만든다. 2개 이상의 복합 유니크 제약조건도 만들 수 있다. 참고로 이 기능은 스키마 자동 생성 기능을 사용해서 DDL을 만들 때만 사용된다.||

 ## 데이터베이스 스키마 자동 생성
 ---
 JPA는 **데이터베이스 스키마를 자동으로 생성하는 기능을 지원**합니다. (애플리케이션 실행 시점에 자동 생성)
 JPA는 데이터베이스 방언을 활용해서 데이터베이스에 맞는 적절한 DDL을 생성합니다.
 하지만 이렇게 **생성된 DDL은 개발 장비에서만 사용해야 합니다.** 생성된 DDL은 운영서버에서는 사용하지 않거나, 적절히 다듬은 후에 사용해야 합니다.

 ### hibernate.hbm2ddl.auto 속성

 옵션|설명
 ---|---
 create|기존 테이블을 삭제하고 새로 생성한다. **DROP + CREATE**
 create-drop|create 속성에 추가로 애플리케이션을 종료할 때 생성한 DDL을 제거한다. **DROP + CREATE + DROP**
 update|데이터베이스 테이블과 엔티티 매핑정보를 비교해서 변경 사항만 수정한다.
 validate|데이터베이스 테이블과 엔티티 매핑정보를 비교해서 차이가 있으면 경고를 남기고 애플리케이션을 실행하지 않는다. 이 설정은 **DDL을 수정하지 않는다.**
 none|자동 생성 기능을 사용하지 않으려면 hibernate.hbm2ddl.auto 속성 자체를 삭제하거나 유효하지 않은 옵션 값을 주면 된다(참고로 none은 유효하지 않은 옵션 값)

 ### 사용 방법
 persistence/xml에 다음 속성을 추가하면 됩니다. value에는 create, create-drop, update, validate 등의 옵션을 넣어주면 됩니다.
 > <property name="hibernate.hbm2ddl.auto" value="여기에 옵션" />

 ### 주의사항
 - **운영 장비에는 절대 create, create-drop, update를 사용하면 안됩니다.**
   이 옵션들은 운영 중인 데이터베이스의 테이블이나 컬럼을 삭제할 수 있기 때문입니다.
 - 개발 초기 단계는 create 또는 update
 - 테스트 서버는 update 또는 validate
 - 스테이징과 운영 서버는 validate 또는 none

<file_sep>/Article/2019-12/5.md
# 2019-12-05 목요일

## [카카오로 체크인? 대한항공-카카오 MOU 체결](http://www.bloter.net/archives/363615)
- 카카오톡에서 항공권 검색부터 결제, 탑승까지 해결할 수 있게 될까.
- 카카오는 12월5일 경기도 분당구에 위치한 카카오 판교오피스에서 카카오 여민수 공동대표, 배재현 부사장과 대한항공 우기홍 대표, 하은용 부사장이 참석한 가운데 양사가 업무협약(MOU)을 체결했다고 밝혔다. 이를 통해 양사는 ▲플랫폼 ▲멤버십 및 핀테크 ▲커머스 ▲콘텐츠 ▲디지털 전환(Digital Transformation) 등 다양한 분야에서 각자 보유한 역량을 활용해 이용자에게 새로운 가치를 제공하는데 협력할 방침이다.
- 우선 항공권을 찾는 과정에서부터, 결제, 탑승에 이르는 전 과정이 모바일 환경에서 편리하게 진행될 수 있도록 자원을 적극 활용하는 방안에 대해 논의한다. 카카오 공동체가 보유한 콘텐츠를 대한항공 기내에서도 즐길 수 있도록 엔터테인먼트 사업 협력을 추진하고, 커머스 플랫폼 협업 및 양사가 보유한 상품의 판매 확대를 위한 상호 협력 등 구체적 방안도 협의하기로 했다.
- 또 카카오의 고도화된 인공지능(AI) 기술을 활용해 항공 산업의 효율성과 생산성을 극대화하고, 시장 변화에 발빠르게 대응할 수 있는 협력 방안도 모색할 방침이다.
<file_sep>/OS/교착상태(Deadlock).md
# 1. 교착상태 원리
교착상태(deadlock)란 프로세스들의 집합이 더 이상 진행을 못하고 **영구적으로** 블록되어 있는 상태로 정의된다. 교착상태는 시스템 자원에 대한 경쟁 도중에 발생할 수도 있고 프로세스 간 통신 도중에 발생할 수도 있다. 집합 내의 한 프로세스가 특정 사건의 발생을 기다리며 대기하고 있고, 이 사건이 집합 내의 다른 블록된 프로세스에 의해 발생될 수 있을 때 이 프로세스의 집합은 교착상태가 된다. 교착상태가 영구적인 이유는 **기다리던 사건이 결코 발생하지 않기 때문**이다.

교착상태는 **두 개 이상의 프로세스들이 서로 충돌되는 자원 요구를 할 때 발생한다.** 아래 그림은 교착상태의 대표적인 예로, 교통 교착상태이다. 교차로의 네 구역(a,b,c,d)은 제어가 필요한 공유 자원이 된다. 사거리 교차로에서 각 차들이 원하는 자원은 다음과 같다.

> 1번 차: 구역 a, b가 필요
> 2번 차: 구역 b, c가 필요
> 3번 차: 구역 c, d가 필요
> 4번 차: 구역 a, d가 필요

차들이 거의 동시에 직진하게 되면 그림의 (b)처럼 각 차들이 자원을 가지고 다른 자원(a,b,c,d)을 필요로 하기 때문에 교착상태에 빠지는 것이다.
![](https://images.velog.io/images/conatuseus/post/ea9e61f9-b6d7-4b2d-80c7-07358ce933b0/image.png)

# 2. 교착상태 조건
1. **상호 배제(mutual exclusion) 조건**
한 순간에 한 프로세스만이 자원을 사용할 수 있다. 즉 한 프로세스에 의해 점유된 자원을 다른 프로세스들이 접근할 수는 없다.
2. **점유대기(hold and wait) 조건**
이미 자원을 보유한 프로세스가 다른 자원을 요청하며 기다리고 있다.
3. **비선점(no preemption) 조건**
프로세스에 의해 점유된 자원을 다른 프로세스가 강제적으로 빼앗을 수 없다.
4. **환형 대기(circular wait) 조건**
프로세스들 간에 닫힌 연결(closed chain)이 존재한다. 즉 자원 할당 그래프에서 환형이 만들어 지는 것이다. 닫힌 연결에서 블록된 프로세스가 자원을 점유하고 있는데, 이 자원을 체인 내부의 다른 프로세스가 원하며 대기하고 있다.

여러 가지 면에서 고려해 볼 때 위 조건들은 바람직한 조건들이다. 예를 들어, 상호 배제는 수행 결과의 일관성과 데이터베이스의 무결성을 보장하기 위해 반드시 필요하다. 또한 선점도 임의대로 수행되어서는 안 된다. 예를 들어, 특히 데이터 자원이 연관되어 있는 경우, 선점은 롤백(rollback) 복구 기법의 지원이 있어야만 가능하다. 즉, 프로세스가 다시 재수행될 수 있도록 프로세스의 상태와 사용하던 자원의 상태를 안정적인 이전의 상태로 복원할 수 있어야 선점이 가능한 것이다.

조건 4는 조건 1~3의 결과에 의해 발생한다. 즉, 조건 1~3의 복잡한 상호작용의 결과 해결할 수 없는 환경 대기 상태가 발생하게 되는 것이다. 교착상태의 정의가 바로 해결할 수 없는 환형 대기 상태이다. 환형 대기 상태가 해결될 수 없는 이유는 조건 1~3이 지켜지기 때문이다. 결국 위 네 가지 조건이 교착상태가 발생할 수 있는 필요충분조건이다.

 교착상태를 해결하기 위한 다양한 접근 방법들 중 대표적인 것은 3가지가 있다.
첫 번째 접근 방법은 **예방(prevent)** 으로 교착상태의 발생 조건 1~4 중에 하나를 시스템에서 허용하지 않는 것이다.
두 번째 접근 방법은 교착상태 **회피(avoid)** 로 현재 자원 할당의 상태에 따라 안전하게 자원 할당을 결정하는 것이다.
세 번째 접근 방법은 교착상태 **발견(detect)** 으로, 교착상태가 발생하면 그것을 발견하고 회복하는 것이다. 이제부터 각 방법을 살펴보자.

# 3. 교착상태 예방
교착상태 예방 전략은 운영체제를 설계할 때 교착상태가 발생할 가능성을 없애는 것이다. 구체적으로 이전에 설명했던 교착상태가 발생하기 위한 네 가지 필요충분조건 중에 하나를 설계 단계에서 배제하는 것이다.

## 3.1 상호 배제
시스템을 설계할 때 상호 배제 조건을 없앨 수는 없다. 상호 배제 조건은 공유 자원의 일관성을 유지하기 위해 반드시 필요하기 때문에, 자원 접근에서 상호 배제가 필요하면 운영체제가 이를 지원해 주어야 한다. 예를 들어, 파일 같은 자원의 경우, 다수의 읽기 접근은 허용되지만 쓰기 접근은 한 시점에 하나만 배타적으로 허용되어야 한다. 다수의 프로세스들이 쓰기 권한을 얻으려고 할 때 교착상태가 발생할 수도 있다.

## 3.2 점유대기
점유대기 조건은 다음과 같은 방법으로 없앨 수 있다. **프로세스는 자신이 사용할 모든 자원을 한순간에 요청**한다. 만일 모든 자원을 할당받을 수 있으면 계속 수행한다. 반면 하나의 자원이라도 할당받을 수 없으면, 어떠한 자원도 할당받지 않은 채 대기한다. 이 방법은 가능하기는 하지만 다음과 같은 비효율성이 나타난다.

	1. 프로세스는 모든 자원을 할당받기 위해 오랜 기간 기다릴 수 있다.
	실제로는 프로세스가 일부 자원만 할당받고 수행을 시작할 수 있으며, 이후 다른 자원은 수행 중에 할당받아도 되는 경우가 있다.
	하지만 이 방법을 적용하면 프로세스는 모든 자원의 할당이 가능해질 때까지 대기해야 한다.
	2. 한꺼번에 할당받은 자원 중 일부는 실제 수행이 끝날 때쯤에 사용될 수 있다. 그렇다면 그 자원들은 프로세스의 시작 시점과 실제 사용 시점 사이에서 실제로 이용되지 않으면서 점유되어 있다는 의미가 된다. 그 동안 다른 프로세스들은 그 자원을 사용할 수 없다.
	3. 프로세스가 미래에 사용될 모든 자원을 미리 알기는 상당히 어렵다.

## 3.3 비선점
비선점 조건은 다음과 같은 두 가지 방법으로 없앨 수 있다.
첫째, 자원을 점유한 프로세스가 다른 자원을 요청했을 때 할당받을 수 없다면, 일단 자신이 점유한 자원을 반납한다. 이후 프로세스는 원래 자원과 새로 원하는 자원을 함께 요청한다.
둘째, 한 프로세스에서 다른 프로세스가 점유한 자원을 원하면, 운영체제는 다른 프로세스가 점유한 자원을 강제적으로 반납시키고 그것을 원하는 프로세스에게 할당할 수 있다. 두 번째 방법은 프로세스들이 서로 다른 우선순위를 갖고 있을 때 교착상태를 예방할 수 있다.

 이 방법은 자원의 상태를 저장하고 복구하기 쉬운 자원에 적용할 수 있다. 이러한 자원의 대표적인 예는 프로세서이다.

## 3.4 환형대기
환형대기 조건은 자원들의 할당 순서를 정하면 없앨 수 있다. 만일 프로세스가 자원 R을 할당받았다면, 이후 이 프로세스는 자원 R 다음 순서를 갖는 자원들을 요청할 수 있는 것이다.

예를 들어 두 자원 Ri와 Rj가 있으며, 운영체제가 i<j인 할당 순서를 정했다고 하자. 이때 i<j는 Ri가 먼저 할당되어야 하며 그 이후에 Rj가 할당될 수 있다는 것을 의미한다. 프로세스 P가 Ri를 할당하고 Rj를 요청하였다고 하자. 이 상황에서 교착상태가 발생하려면 다른 프로세스 Q는 Rj를 할당하고 Ri를 요청하여야 한다. 하지만 이것은 운영체제의 자원 할당 순서에 위배되기 때문에 나타날 수 없다. 결국 자원들의 할당 순서를 정하면 교착상태가 발생하지 않는 것이다.

점유대기의 경우처럼, **환형대기 조건을 없애는 것은 프로세스의 수행 지연과 불필요한 자원 할당 거부를 야기할 수 있다.**

# 4. 교착상태 회피
교착상태를 해결할 수 있는 또 다른 방법은 회피이다. 회피는 예방과 약간 다른 접근 방법을 사용한다. 교착상태 예방은 교착상태 발생을 위해 필요한 네 가지 조건 중에 하나를 예방하는 방법이다. 예방은 자원 사용과 프로세스 수행에 비효율성을 야기할 수 있다. 반면에, **교착상태 회피**는 교착상태 발생 조건 중 1~3은 허용한다. 또한 예방처럼 자원 할당 순서를 미리 정하지도 않는다. 그 대신 **자원을 할당할 때 교착상태가 발생 가능한 상황으로 진행하지 않도록 고려한다. 따라서 회피 방법은 예방 방법에 비해 더 많은 병행성을 제공한다(자원 사용의 효율성이 높다).** 교착상태 회피는 프로세스가 자원을 요청하고 그 자원이 사용 가능할 때 그대로 할당해 주지 않는다. 할당 전에 이 할당이 교착상태를 발생시킬 가능성이 있는지 동적으로 조사한다. 만일 교착상태를 발생시킬 가능성이 있다면 자원을 할당하지 않는다. 사실 회피 방법은 현재 자원의 가용 개수와 프로세스의 자원 요구량을 미리 알고 있어야 가능하다.

교착상태 회피를 위한 2가지 기법
- 프로세스가 시작하려 할 때 요구하는 자원 할당이 교착상태 발생의 가능성이 있으면, 프로세스를 시작시키지 않는다.
- 수행 중인 프로세스가 요구하는 추가적인 자원 할당이 교착상태 발생의 가능성이 있으면, 자원을 할당하지 않는다.

## 4.1 프로세스 시작 거부
새로운 프로세스와 기존의 프로세스들이 요구하는 자원의 개수가 그 자원의 전체 개수보다 적을 경우에 수행을 허용하는 것이다. 이 정책은 최악의 경우, 즉 모든 프로세스들이 동시에 최대 자원을 요구하여 사용함을 가정하고 있으며, 따라서 효율적이지는 않다.

## 4.2 자원 할당 거부
교착상태 회피를 위한 자원 할당 거부 방법은 **은행원 알고리즘**이라 불린다. [은행원 알고리즘 자세히 알아보기](https://jhnyang.tistory.com/102)
은행원 알고리즘의 정의는 다음과 같다.
> - 운영체제는 자원의 상태를 감시하고, 사용자 프로세스는 사전에 자신의 작업에서 필요한 자원의 수를 제시하는 교착상태 회피 알고리즘
- 운영체제는 안정상태를 유지할 수 있는 요구만을 수락하고 불안전 상태를 초래할 사용자의 요구는 나중에 만족될 수 있을 때까지 계속 거절하는 교착상태 회피 알고리즘

**안전한 상태**란 교착상태가 발생하지 않도록 프로세스에게 자원을 할당할 수 있는 진행 경로가 존재함을 의미한다(결국 모든 프로세스가 자신의 자원 요구량을 모두 만족하여 수행을 마칠 수 있음을 의미).
**불안전한 상태**란 그러한 진행 경로가 없는 상태이다.

간단한 예시를 보자.

| 프로세스 | Max  | Allocation |
| -------- | ---- | ---------- |
| P0       | 3    | 1          |
| P1       | 12   | 5          |
| P2       | 4    | 1          |

초기에 10이라는 자원이 있었다.
프로세스 P0는 최대 3이 필요하고, 현재 1의 리소스를 사용하고 있다. 프로세스 P1은 최대 10이 필요하고 현재 5를 할당받고, P2는 최대 4가 필요하며 현재 1을 할당 받았다.
따라서 현재 P0은 2, P1은 5, P2는 3이 필요하다.

**그럼 이것이 안전한 상태일까?**
현재 남은 자원은 10(초기) - 7(Allocation합) = 3 이다.

P0 -> P2 -> P1
P2 -> P0 -> P1
이라는 진행 경로가 존재하므로 **안전한 상태**이다.

### 장점
 교착상태 회피는 교착상태 예방에 비해 자원 할당이 더 자유롭다. 따라서 시스템에서 자원 효율이 높아진다. 또한 후에 논의될 교착상태 발견에 비해 자원을 선점하고 프로세스 수행의 롤백(rollback)이 필요 없다는 장점이 있다.

### 단점
- 각 프로세스들이 사용할 최대 자원 요구량을 미리 운영체제에 알려주어야 한다.
- 프로세스들은 서로 독립적이어야 한다. 즉 프로세스들 중에 한 프로세스는 다른 프로세스에 비해 먼저 수행되어야 한다는 것과 같은 수행 순서 종속 관계가 없어야 한다.
- 자원 개수가 고정되어 있어야 한다.
- 자원을 선점한 채 종료되는 프로세스는 없어야 한다.

# 5. 교착상태 발견
교착상태 예방 전략은 교착상태가 발생하지 않도록 하기 위해 자원 접근에 대한 제한과 프로세스의 수행에 제한을 둔다. 반면, **교착상태 발견 전략**은 자원 접근에 대한 제한이나 프로세스의 행위에 제한을 두지 않는다. 즉 자원 할당이 가능한 상황이면 항상 할당을 해 준다. 단 교착상태 발견 방법은 주기적으로 시스템에 환형 대기조건이 발생하였는지 검사하고, 만일 발생하였으면 그것을 해결해 준다.

## 5.1 교착상태 발견 알고리즘
교착상태 발견은 자원 할당이 요구될 때마다 매번 수행될 수도 있고, 주기적으로 가끔 수행될 수도 있다. 자원 할당이 요구될 때마다 매번 수행되면 교착상태를 빠른 시점에 발견할 수 있으며, 또한 시스템의 상태가 점진적으로 변하기 때문에 발견 알고리즘도 간단해 진다는 장점이 있다.
반면, 매번 시도될 때마다 발생하는 처리 비용이 커진다는 단점이 있다.

## 5.2 교착상태 회복 알고리즘
교착상태가 발견되면 이를 해결하기 위한 기법이 필요하다. 교착상태 회복 방법에는 다음과 같은 것이 있다.

1. 교착상태에 포함되어 있는 모든 프로세스들을 중지시킴. (실제로 많은 운영체제에서 사용하고 있는 방법 중에 하나)
2. 교착상태에 포함되어 있는 각 프로세스의 수행을 롤백시킴. 즉, 미리 정의된 특정 체크포인트 시점까지 되돌린 후 다시 수행시킨다. 이 방법의 단점은 다시 수행시켰을 때 다시 교착상태에 빠질 수 있다는 것이다. 하지만 병행 처리의 비결정 특성은 이 가능성을 적게 한다.
3. 교착상태가 없어질 때까지 교착상태에 포함되어 있는 프로세스들 하나씩 종료시킴. 종료시킬 프로세스의 선택은 비용이 가장 적은 것으로 한다. 하나씩 종료시킨 후 교착상태 발견 알고리즘을 다시 수행시켜보면 아직 교착상태가 존재하는지 여부를 알 수 있다.
4. 교착상태가 없어질 때까지 교착상태에 포함되어 있는 자원을 하나씩 선점시킴. 자원 선택은 3에서와 같이 비용이 가장 적은 자원부터 선택한다. 그리고 하나씩 선점한 후 교착상태 발견 알고리즘을 수행시켜 아직 교착상태가 존재하는지 파악한다. 이때 자원을 선점 당한 프로세스는 자원을 할당 받기 전 시점으로 롤백하고, 그 시점부터 다시 수행한다.

방법 3에서 프로세스의 선택은 다음과 같은 기준을 적용할 수 있다.
- 지금까지 사용한 처리기 시간이 적은 프로세스부터
- 지금까지 생산한 출력량이 적은 프로세스부터
- 이후 남은 수행시간이 가장 긴 프로세스부터
- 할당받은 자원이 가장 적은 프로세스부터
- 우선순위가 낮은 프로세스부터<file_sep>/Article/2019-11/17.md

# 2019-11-17 일요일

## [페이스북 페이, 리브라와 다른 점은](http://www.bloter.net/archives/361228)

- Summary
  - 페이스북은 암호화폐 프로젝트 ‘리브라’ 멤버로 참여하고 있습니다. 리브라 프로젝트의 목적은 전세계 사람들이 저렴한 수수료로 송금할 수 있는 암호화폐를 발행하는 것
  - 11월12일 페이스북이 새로운 결제 서비스 ‘페이스북 페이’를 [출시](https://about.fb.com/news/2019/11/simplifying-payments-with-facebook-pay/)
  - 페이스북 페이는 페이스북과 페이스북 메신저 내에서 돈을 송금하고 결제를 할 수 있도록 해주는 서비스
  - 현재는 미국에서만 사용 가능
  - 추후 페이스북 페이를 이용할 수 있는 지역을 확대하고 페이스북이 운영 중인 인스타그램과 왓츠앱에서도 이용할 수 있게 하겠다고 밝힘
  - 페이스북 페이와 리브라 다른 점
    - 페이스북 코인과 리브라 서비스 운영 주체가 다름.
      페이스북 코인의 서비스 제공자는 페이스북입니다. 반면 리브라는 오픈소스 프로젝트로 스위스 제네바에 기반을 둔 비영리 단체
    - 운영 주체가 다르기에 서비스가 지원되는 플랫폼도 다름.
      페이스북 페이는 별도 앱 설치 없이 페이스북 내에서 사용할 수 있다면, 리브라에는 암호화폐를 보관할 수 있고 송금할 수 있는 전자 지갑 ‘칼리브라’가 존재<file_sep>/Spring/왜_생성자_주입을_사용해야하는가.md
 현재 진행중인 프로젝트에 팀원의 코드를 리뷰하면서,
필드에 `@Autowired` 어노태이션을 붙여 의존성 주입하는 것을 발견했습니다.

 올해 초 nhn 세미나에서 생성자 주입을 사용하도록 배웠지만 comment를 작성하는데 이유를 설명하지 못했습니다. (역시 사람은 망각의 동물...)

그래서 이번 기회에 **'왜 생성자 주입을 해야하는가'** 라는 주제로 정리해보겠습니다.

먼저, 의존성 주입 방법 3가지에 대해 먼저 알아봅시다.

# 의존성 주입 방법 3가지

## Field Injection

~~~ java
@Controller
public class UserController {

  @Autowired
  private UserService userService;
}
~~~

세미나에서 배우기 전 사용하던 방식입니다. 의존성을 주입하고자 하는 필드에 `@Autowired` 어노테이션만 추가하면 됩니다.

 가장 간단하고 가독성도 좋아보입니다.

## Setter Injection

~~~ java
@Controller
public class UserController {

  private UserService userService;

  @Autowired
  public void setUserService(UserService userService) {
    this.userService = userService;
  }
}
~~~

Setter Injection은 Setter에 `@Autowired` 어노테이션을 추가하는 방법입니다.

## Constructor Injection

~~~ java
@Controller
public class UserController {

  private final UserService userService;

  public Usercontroller(final UserService userService) {
    this.userService = userService;
  }
}
~~~

위 3가지 방법을 보면 처음엔 누구나 Field Injection을 사용할 것입니다. 하지만 왜 Constructor Injection을 권장하는지 알아봅시다,

# Field Injection의 문제점

## 단일 책임 원칙 위반

Field Injection은 의존성을 추가하기 매우 쉽습니다. 그래서 잘못된 것을 인지하지 못하고 의존성을 마구마구 집어 넣을 수 있습니다.

하지만, 의존성 주입에 생성자를 사용한다면?

의존성 주입을 위해 생성자의 인자가 추가될 것이고, 생성자의 인자가 너무 많아지면 우리는 설계나 어떤 것이 잘못되었다는 것을 확실히 느낄 수 있을 것입니다.

**많은 의존성을 가진다는 것은  그 클래스에 책임이 많다는 것을 의미합니다.** 이는 단일 책임 원칙, 관심사의 분리를 위반하는 것일 수 있고, 그 클래스에 대한 검사와 리팩토링이 필요하다는 것입니다.

> **쉽게 말해서, Field Injection은 의존성을 추가하기 쉬워서 단일 책임 원칙을 위반하는지 모를 수 있다.
> 단일 책임 원칙 위반 가능성이 높아진다!**

## 의존성이 숨는다

DI 컨테이너를 사용하는 것은 클래스가 더 이상 자신의 의존성을 관리할 책임이 없다는 것을 의미합니다.

의존성을 획득하기 위한 책임은 그 클래스에서 추출됩니다.

이제 다른 누군가는 의존성을 제공할 책임이 있습니다. 즉, DI 컨테이너를 제공하거나 테스트에서 수동으로 할당합니다.

클래스가 더 이상 의존성을 획득할 책임이 없는 경우, 메서드 또는 생성)를 사용하여 클래스를 명확하게 의사소통해야 합니다.

하지만 Field Injection은 숨은 의존성만 제공해줍니다.

## DI Container Coupling

DI 프레임워크의 핵심 아이디어 중 하나는 관리되는 클래스가 사용된 DI 컨테이너에 의존하지 않아야 한다는 것입니다. 즉, 필요한 모든 의존성을 전달한다면 독립적으로 인스턴스화할 수 있는 단순한 POJO여야 합니다.

 이렇게 하면 DI 컨테이너를 시작하지 않고 단위 테스트에서 인스턴스화하고 별도로 테스트할 수 있습니다.

컨테이너 결합이 없는 경우 클래스를 관리형 또는 비관리형으로 사용하거나 심지어 새로운 DI 프레임워크로 전환할 수 있습니다.

하지만 Field Injection을 사용하면 필요한 모든 의존성을 인스턴스화할 수 있는 직접적인 방법을 제공하지 않습니다.

## 불변성

Field Injection을 사용하면 필드에 final을 사용할 수 없게 됩니다.

~~~ java
@Autowired
private final UserService userService;	// 잘못된 코드
~~~

## Setter Injection VS Constructor Injection

### **Setter Injection**

Setter Injection은 선택적인 의존성을 사용할 때 유용합니다. 상황에 따라 의존성 주입이 가능합니다. 스프링 3.x 다큐먼테이션에서는 Setter Injection을 추천했었습니다.

### **Constructor Injection**

Constructor Injection은 필수적인 의존성 주입에 유용합니다. 게다가 final을 선언할 수 있으므로 객체가 불변하도록 할 수 있습니다. 또한 위에서 언급했듯이 순환 의존성도 알 수 있습니다. 그로인해 나쁜 디자인 패턴인지 아닌지 판단할 수 있습니다.
스프링 4.3버전부터는 클래스를 완벽하게 DI 프레임워크로부터 분리할 수 있습니다. 단일 생성자에 한해 @Autowired를 붙이지 않아도 됩니다. 이러한 장점들 때문에 스프링 4.x 다큐먼테이션에서는 더이상 Setter Injection이 아닌 Constructor Injection을 권장합니다. 굳이 Setter Injection을 사용한다면, 합리적인 디폴트를 부여할 수 있고 선택적인(optional) 의존성을 사용할 때만 사용해야한다고 말합니다. 그렇지 않으면 not-null 체크를 의존성을 사용하는 모든 코드에 구현해야합니다.

> 참고 자료
>
>  https://www.vojtechruzicka.com/field-dependency-injection-considered-harmful/
>
>  https://zorba91.tistory.com/238
<file_sep>/Spring/AOP.md

# AOP란?

AOP는 **애플리케이션 전체에 걸쳐 사용되는 기능을 재사용**하도록 지원하는 것입니다.

AOP(Aspect-Oriented Programming)란 단어를 번역하면 **관점(관심) 지향 프로그래밍**으로 됩니다.

![image](https://user-images.githubusercontent.com/22893111/78748855-fd09d200-79a7-11ea-8fe2-9e313a7cc4ce.png)

(핵심기능에서 바라본 관점)

각각의 Service는 핵심기능 관점에서 봤을 때는 Board, User, XXX 등 **공통된 요소가 없습니다.**

이런 괌점에서 각각의 Service는 각자 코드를 구현하고 있습니다. 하지만, 이 관점을 돌려서 **부가기능** 이란 관점에서 바라보면 상황이 달라집니다.

![image](https://user-images.githubusercontent.com/22893111/78748870-0430e000-79a8-11ea-86e9-6353b6af26c7.png)

부가기능의 관점에서 바라보면 각각의 Service는 수행시간 측정을 나타내는 before, after라는 메서드가 공통되는 것을 알 수 있습니다.

**AOP는 여기서부터 시작합니다**

기존에 OOP에서 바라보던 관점을 다르게 하여 부가기능적인 측면에서 보았을 때 공통된 요소를 추출하자는 것입니다.

이때 가로(횡단) 영역의 공통된 부분을 잘라냈다고 하여, AOP를 **크로스 컷팅(Cross-Cutting)** 이라고 불리기도 합니다.

AOP라고 해서 전에 없던 새로운 개념이 등장한 것이 아니라, 결국은 **공통된 기능을 재사용하는 기법**입니다.

OOP에선 공통된 기능을 재사용하는 방법으로 상속이나 위임을 사용합니다. 하지만 전체 어플리케이션에서 여기저기 사용되는 **부가기능**들을 상속이나 위임으로 처리하기에는 깔끔하게 모듈화가 어렵습니다. 그리고 Java는 다중상속을 지원하지 않으므로 문제가 발생합니다.

그래서 이 문제를 해결하기 위해 AOP가 등장하게 됩니다.

**AOP의 장점**은 2가지입니다.

- 어플리케이션 전체에 흩어진 공통 기능이 하나의 장소에서 관리된다는 점
- 다른 서비스 모듈들이 본인의 목적에만 충실하고 그외 사항들은 신경쓰지 않아도 된다는 점

# AOP 용어

**타겟(Target)**

부가기능을 부여할 대상을 얘기합니다.

여기선 핵심기능을 담당하는 getBoards, getUsers를 하는 Service 들을 얘기합니다.

**애스펙트 (Aspect)**

객체지향 모듈을 오브젝트라 부르는 것과 비슷하게 부가기능 모듈을 애스펙트라고 부르며, **핵심기능에 부가되어 의미를 갖는** 특별한 모듈이라 생각하시면 됩니다.

애스펙트는 부가될 기능을 정의한 **어드바이스**와 어드바이스를 어디에 적용할지를 결정하는 **포인트컷**을 함께 갖고 있습니다.

**어드바이스(Advice)**

실질적으로 부가기능을 담은 구현체를 얘기합니다.

어드바이스의 경우 타겟 오브젝트에 종속되지 않기 때문에 순순하게 **부가기능에만 집중**할 수 있습니다.

어드바이스는 애스팩트가 '무엇'을 '언제' 할지를 정의하고 있습니다.

**포인트컷(PointCut)**

부가기능이 적용될 대상(메서드)를 선정하는 방법을 얘기합니다.

즉, 어드바이스를 적용할 조인포인트를 선별하는 기능을 정의한 모듈을 얘기합니다.

**조인포인트(JoinPoint)**

어드바이스가 적용될 수 있는 위치를 얘기합니다.

다른 AOP 프레임워크와 달리 Spring에서는 **메서드 조인포인트만 제공**하고 있습니다. 따라서 Spring 프레임워크 내에서 조인포인트라 하면 **메서드**를 가리킨다고 생각하면 됩니다.

**프록시(Proxy)**

타겟을 감싸서 타겟의 요청을 대신 받아주는 랩핑(Wrapping) 오브젝트입니다. 호출자(클라이언트)에서 타겟을 호출하게 되면 타겟이 아닌 타겟을 감싸고 있는 프록시가 호출되어, 타겟 메서드 실행전에 선처리, 타겟 메서드 실행 후, 후처리를 실행시키도록 구성되어 있습니다.

![image](https://user-images.githubusercontent.com/22893111/78749901-21ff4480-79aa-11ea-8041-d9443e3ac8a6.png)

**인트로덕션(Introduction)**

타겟 클래스에 코드 변경없이 신규 메서드나 멤버변수를 추가하는 기능을 얘기합니다.

**위빙(Weaving)**

지정된 객체에 애스펙트를 적용해서 새로운 프록시 객체를 생성하는 과정을 얘기합니다.

예를 들면 A라는 객체에 트랜잭션 애스팩트가 지정되어 있다면, A라는 객체가 실행되기전 커넥션을 오픈하고 실행이 끝나면 커넥션을 종료하는 기능이 추가된 프록시 객체가 생성되고, 이 프록시 객체가 앞으로 A 객체가 호출되는 시점에서 사용됩니다. 이때의 프록시객체가 생성되는 과정을 **위빙**이라 생각하시면 됩니다

# 사용법

~~~ java
@Aspect
public class Performance {

    @Around("execution(* com.blogcode.board.BoardService.getBoards(..))")
    public Object calculatePerformanceTime(ProceedingJoinPoint proceedingJoinPoint) {
        Object result = null;
        try {
            long start = System.currentTimeMillis();
            result = proceedingJoinPoint.proceed();
            long end = System.currentTimeMillis();

            System.out.println("수행 시간 : "+ (end - start));
        } catch (Throwable throwable) {
            System.out.println("exception! ");
        }
        return result;
    }
}
~~~

![image](https://user-images.githubusercontent.com/22893111/78750103-83271800-79aa-11ea-963f-bb51e7d5e267.png)

@Around는 **어드바이스**입니다.

앞서 설명드린 것처럼 어드바이스는 애스펙트가 '무엇을', '언제' 할지를 의미하고 있습니다.

여기서 '무엇'은 calculatePerformanceTime() 메서드를 나타냅니다.

그리고 '언제'는 @Around가 되는데, 이 '언제'라는 시점의 경우 @Around만 존재하지 않고 총 5가지의 타입이 존재합니다.

- @Before (이전)
  - 어드바이스 타겟 메서드가 호출되기 전에 어드바이스 기능을 수행
- @After (이후)
  - 타겟 메서드의 결과에 관계없이(즉 성공, 예외 관계없이) 타겟 메서드가 완료되면 어드바이스 기능을 수행
- @AfterReturning (정상적 반환 이후)
  - 타겟 메서드가 성공적으로 결과값을 반환 후에 어드바이스 기능을 수행
- @AfterThrowing (예외 발생 이후)
  - 타겟 메서드가 수행 중 예외를 던지게 되면 어드바이스 기능을 수행
- @Around (메서드 실행 전후)
  - 어드바이스가 타겟 메서드를 감싸서 타겟 메서드 호출전과 후에 어드바이스 기능을 수행

예를 들어 타겟 메서드의 이전 시점에서만 어드바이스 메서드를 수행하고 싶다면,

~~~ java
@Before("포인트컷 표현식")
public void 어드바이스메서드() {
  ...
}
~~~

식으로 작성하면 됩니다.

여기서 주의해야할 점은 @Around의 경우 **반드시 proceed() 메서드가 호출**되어야 한다는 것입니다.

proceed() 메서드는 타겟 메서드를 지칭하기 때문에 proceed 메서드를 실행시켜야만 타겟 메서드가 수행이 된다는 것을 잊지 맙시다.

다음으로 알아볼것은 @Around 다음에 작성된 `"execution(* com.blogcode.board.BoardService.getBoards(..))"` 입니다.
어드바이스의 value로 들어간 이 문자열을 **포인트컷 표현식** 이라고 합니다.
포인트컷 표현식은 2가지로 나눠지는데, `execution`을 **지정자**라고 부르며
`(* com.blogcode.board.BoardService.getBoards(..))`는 **타겟 명세**라고 합니다.
포인트컷 지정자는 execution을 포함하여 총 9가지가 있습니다.

- args()
  - 메소드의 인자가 타겟 명세에 포함된 타입일 경우
  - ex) args(java.io.Serializable) : 하나의 파라미터를 갖고, 그 인자가 Serializable 타입인 모든 메소드
- @args()
  - 메소드의 인자가 타겟 명세에 포함된 어노테이션 타입을 갖는 경우
  - ex) @args(com.blogcode.session.User) : 하나의 파라미터를 갖고, 그 인자의 타입이 @User 어노테이션을 갖는 모든 메소드 (@User User user 같이 인자 선언된 메소드)
- execution()
  - 접근제한자, 리턴타입, 인자타입, 클래스/인터페이스, 메소드명, 파라미터타입, 예외타입 등을 전부 조합가능한 가장 세심한 지정자
  - 이전 예제와 같이 풀패키지에 메소드명까지 직접 지정할 수도 있으며, 아래와 같이 특정 타입내의 모든 메소드를 지정할 수도 있다.
  - ex) execution(* com.blogcode.service.AccountService.*(..) : AccountService 인터페이스의 모든 메소드
- within()
  - execution 지정자에서 클래스/인터페이스까지만 적용된 경우
  - 즉, 클래스 혹은 인터페이스 단위까지만 범위 지정이 가능하다.
  - ex) within(com.blogcode.service.*) : service 패키지 아래의 클래스와 인터페이스가 가진 모든 메소드
  - ex) within(com.blogcode.service..*) : service 아래의 모든 **하위패키지까지** 포함한 클래스와 인터페이스가 가진 메소드
- @within()
  - 주어진 어노테이션을 사용하는 타입으로 선언된 메소드
- this()
  - 타겟 메소드가 지정된 빈 타입의 인스턴스인 경우
- target()
  - this와 유사하지만 빈 타입이 아닌 타입의 인스턴스인 경우
- @target()
  - 타겟 메소드를 실행하는 객체의 클래스가 타겟 명세에 지정된 타입의 어노테이션이 있는 경우
- @annotation
  - 타겟 메소드에 특정 어노테이션이 지정된 경우
  - ex) @annotation(org.springframework.transaction.annotation.Transactional) : Transactional 어노테이션이 지정된 메소드 전부

다양한 지정자가 등장하였지만 실제로 execution과 @annotation을 주로 사용하는 것으로 알고 있습니다.

<file_sep>/Article/2019-12/1.md
# 2019-12-1 일요일

## [사라진 업비트 이더리움, 어디로 가나](http://www.bloter.net/archives/362989)
- 11월27일 오후 1시 6분, 국내 암호화폐 거래소 업비트에서 이더리움 34만2천개가 알 수 없는 지갑으로 출금됐습니다. 이상 거래로 옮겨진 이더리움을 시가로 환산하면 약 580억원에 달합니다
- 업비트에서 출금된 이더리움 34만2천개는 하나의 지갑(주소: 0xa09871aeadf4994ca12f5c0b6056bbd1d343c029)으로 옮겨졌었습니다. 그러나 다음 날인 28일 오후 4시경, 다른 지갑으로 자금을 분산시키려는 움직임이 포착됐습니다. 이더스캔 입출금 내역에 따르면, 이 작업은 같은 날 오후 5시 53분까지 진행됐습니다. 총 4개의 다른 지갑으로 13번에 걸쳐 자금을 출금시킨 것입니다. 4개 지갑 중 비교적 작은 금액이 출금된 지갑은 거래소 바이낸스의 암호화폐 개인 지갑입니다.
- 다른 지갑으로 출금이 원활히 진행되는지 확인하는 것처럼, 처음에는 이더리움 10개만 세 개의 지갑으로 출금됐습니다. 그 후 순차적으로 이더리움 천개, 만개, 십만개까지 각각 지갑으로 이동 시켜 자금을 분산했습니다. 다른 지갑으로 옮겨진 이더리움은 다시 여러 개 지갑으로 출금됐습니다. 블록체인 업계에서는 이러한 행위가 이루어지는 까닭을 탈취한 암호화폐의 추적을 어렵게 하기 위해서라고 추측하고 있습니다. 이처럼 자금을 여러 곳으로 분산 시켜 세탁하는 행위를 ‘믹싱 앤 텀블러(Mixing and Tumbler)’라고 부르기도 합니다.
<file_sep>/Article/2019-12/25.md
# 2019-12-25 수요일

## [“2020 글로벌 금융, 거대 테크 기업 공세 속 시장 통합 가속”](http://www.bloter.net/archives/365535)
- 2019년 글로벌 핀테크 시장은 거대 기술 기업들의 거세진 공세가 강화됐고, 업체 간 인수합병(M&A)이 늘면서 시장 통합 열기도 그 어느때보다 고조됐다.
- 애플은 골드만삭스와 손잡고 직접 신용카드를 내놨고 구글은 시티그룹과 협력해 내년부터 수표 발행이 가능한 은행 계좌 서비스를 제공한다고 발표했다.
- 눈에 많이 띄지 않았던 업체간 M&A도 올해들어 본격화되는 양상을 보였다. 피서브, 피델리티 내셔널 인포메이션 서비스, 글로벌 페이먼트가 일련의 M&A로 미국 결제 처리 시장 판도를 바꿨고 최근에는 페이팔 홀딩스가 쇼핑 가격 비교 스타트업 ‘허니(Honey Science Corporation)’를 40억달러 규모에 인수한다고 밝혔고, 찰스슈왑은 TD아메리트레이드 홀딩스를 손에 넣기 위해 260억달러라는 거액을 베팅했다.
- 블룸버그통신에 따르면 관련 업계는 핀테크 시장에서 업체들간 빅딜은 2020년 더욱 가속화될 것으로 보고 있다.
- 시장 분석 업체 CB인사이트의 린드세이 데이비스 수석 인텔리전스 애널리스트는 “자산관리는 신세대 고객과 성장하는 핀테크 앱 군단과의 경쟁이라는 압력에 놓인 기존 업체들이 주도하는 통합을 보게 될 가능성이 높다”면서 “찰스슈왑이 온라인 증권사인 TD아메리트레이드를 인수한 것은 앞으로 있을 보다 많은 전략적인 통합의 시작일 뿐”이라고 말했다.
<file_sep>/JPA/JPA프로그래밍/JPA소개.md
# 객체와 관계형 데이터베이스의 차이
1. **상속**
 객체는 상속관계가 있지만, 관계형 데이터베이스는 상속 관계가 없다.
2. **연관관계**
객체는 reference(참조)를 가지고 있다. (예를들어, 연관된 객체를 getter로 가져올 수 있음)
관계형 데이터베이스는 PK(Primary Key)나 FK(Foreign Key)로 join을 해서 필요한 데이터를 찾을 수 있다.
3. **데이터 타입**

4. **데이터 식별 방법**

---
# JPA
- Java Persistence API
- 자바 진영의 ORM 기술 표준

> **_ORM_**
- Object-relational mapping(객체 관계 매핑)
- 객체는 객체대로 설계
- 관계형 데이터베이스는 관계형 데이터베이스대로 설계
- ORM 프레임워크가 중간에서 매핑
- 대중적인 언어에서 대부분 ORM 기술이 존재

## _JPA는 애플리케이션과 JDBC 사이에서 동작_

![image.png](https://images.velog.io/post-images/conatuseus/275a0cf0-d525-11e9-84e2-776d9795540a/image.png)

JAVA 애플리케이션에서 JPA에게 명령하면, JPA는 JDBC API를 사용해서 SQL을 만들어서 DB에 보낸다.

## _JPA 동작_
### 저장

![image.png](https://images.velog.io/post-images/conatuseus/42874ba0-d525-11e9-84e2-776d9795540a/image.png)

- JPA에게 Member객체를 넘기면 JPA가 객체를 분석
- INSERT query 생성 (**JPA가 query를 만듬!**)
- JPA가 JDBC API를 사용해서 INSERT query를 DB에 보냄
- **패러다임의 불일치 해결**

### 조회

![image.png](https://images.velog.io/post-images/conatuseus/50003d50-d525-11e9-a8d8-f563f0e9c9d2/image.png)

- JPA에게 PK값으로 find 요청
- JPA는 SELECT query 생성
- JDBC API를 통해 DB에 보내고 결과를 받음
- ResultSet 매핑
- **패러다임 불일치 해결**

---

# JPA를 왜 사용해야 하는가?
>-  SQL 중심적인 개발에서 객체 중심으로 개발
- 생산성
- 유지보수
- 패러다임의 불일치 해결
- 성능
- 데이터 접근 추상화와 벤더 독립성
- 표준

## _생산성_
- 저장: jpa.persist(member)
- 조회: Member member = jpa.find(memberId)
- 수정: member.setName(“변경할 이름”)
- 삭제: jpa.remove(member)

CRUD가 다 정의되어 있어서 편하다.

## _유지보수_
기존에는 필드 변경시 모든 SQL을 수정했어야 했다.
(예를들어, 필드 한 개가 추가되면 그것을 사용하는 모든 곳을 변경해야 한다)

JPA를 사용하면 필드만 추가하면 된다. (db에 column이 추가되었다는 가정 하에)

## _JPA와 패러다임의 불일치 해결_
1. JPA와 상속
2. JPA와 연관관계
3. JPA와 객체 그래프 탐색
4. JPA와 비교하기

### JPA와 상속

![image.png](https://images.velog.io/post-images/conatuseus/f788e880-cb14-11e9-a25c-55cf3ad51801/image.png)

### 저장
`jpa.persist(album);` 을 명령하면 JPA가 알아서 처리해준다.
JPA는 persist 명령을 받고, ITEM table, ALBUM table 두 군데 모두 INSERT 쿼리를 날린다.

### 조회
`jpa.find(Album.class, albumId);`라는 명령을 하면 JPA가 알아서 처리해준다.
JPA는 Item과 Album을 join해서 찾아옴.

### JPA와 연관관계, 객체 그래프 탐색
> 연관관계 저장
> member.setTeam(team);
> jpa.persist(member);

>객체 그래프 탐색
>Member member = jpa.find(Member.class, memberId);
>Team team = member.getTeam();

연관 관계를 저장하고, 가져올 때는 마치 java collection에 넣었던 것처럼 꺼내올 수 있다.

### JPA와 비교하기
~~~ java
String memberId = "100";
Member member1 = jpa.find(Member.class, memberId);
Member member2 = jpa.find(Member.class, memberId);

member1 == member2;  // 같다
~~~
동일한 트랜잭션에서 조회한 엔티티는 같음을 보장!
(1차 캐시를 사용하기 때문)

---

# JPA의 성능 최적화 기능
1. 1차 캐시와 동일성(identity) 보장
2. 트랜잭션을 지원하는 쓰기 지연(transactional write-behind)
3. 지연 로딩(Lazy Loading)

## 1차 캐시와 동일성 보장
- 같은 트랜잭션 안에서는 같은 엔티티를 반환 - 약간의 조회 성능 향상
- DB Isolation Level이 Read Commit이어도 애플리케이션에서 Repeatable Read 보장

~~~ java
String memberId = "100";
Member m1 = jpa.find(Member.class, memberId);
Member m2 = jpa.find(Member.class, memberId);

println(m1 == m2);  // true
~~~
위의 코드에서 **SQL 1번만 실행한다!**

## 트랜잭션을 지원하는 쓰기 지연 - INSERT
- 트랜잭션을 커밋할 때까지 INSERT SQL을 모음
- JDBC BATCH SQL 기능을 사용해서 한번에 SQL 전송

~~~ java
transaction.begin(); // [트랜잭션] 시작

em.persist(memberA);
em.persist(memberB);
em.persist(memberC);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
//커밋하는 순간 데이터베이스에 INSERT SQL을 모아서 보낸다.
transaction.commit(); // [트랜잭션] 커밋
~~~

 ## 지연 로딩과 즉시 로딩
 - 지연 로딩: 객체가 실제 사용될 때 로딩
 - 즉시 로딩: JOIN SQL로 한번에 연관된 객체까지 미리 조회

![image.png](https://images.velog.io/post-images/conatuseus/ce486c90-cb17-11e9-a0ea-b5cd0c515265/image.png)

지연 로딩은 member를 조회 했을 때, member만 가져온다.
그리고 3번째 줄에서 team.getName을 했을 때, jpa가 db에 team에 대한 query를 날려서 가져온다.
즉,**필요한 시점에 쿼리를 날려 값을 가져온다.**

즉시 로딩은 member를 조회 했을 때, member와 연관된 객체까지 모두 가져온다.

> 따라서 member를 가져왔을 때 member만 쓴다고 하면 지연 로딩이 좋고,
> member를 가져왔을 때 member와 연관 객체를 같이 쓰는 경우엔 즉시 로딩이 좋다.<file_sep>/Article/2020-02/25.md
# 2020-02-25 화요일

## [‘3분이면 OK!’ 카카오페이증권 계좌 오픈…펀드도 판매](http://www.bloter.net/archives/372305)

- 카카오페이증권이 증권 계좌 개설을 시작하고 펀드 상품 판매에 나섰다.

- #### 늘어난 송금 한도, 예탁금 한도 제한 없어져

- #### 기존 카카오페이는 선불전자지급수단으로 송금 한도가 20세 이상 기준 1일/1회 200만원까지 송금할 수 있다. 카카오페이머니를 이용한 계좌송금・톡송금・QR송금 모두 마찬가지다. 단, 연결된 내 계좌로 송금(출금)할 경우 1회 2천만원, 1일 최대 1천만원까지 송금할 수 있다.

- #### 카카오페이증권으로 계좌를 전환하면, 송금한도가 늘어난다. 카톡・QR송금은 1회 200만원, 계좌송금은1회 500만원, 카톡・계좌송금은 1일 500만원, 연결된 내 계좌로 송금은 1회 1천만원, 1일 1억원까지 가능하다.

- #### 기존 카카오페이머니와 달리 카카오페이증권계좌는 한도 제한 없이 예탁금을 보유할 수 있다. 카카오페이머니는 200만원을 초과해 보유할 수 없다. 기존 금융 상품과 다르게 사용자가 자유롭게 입출금 해도 주 단위로 평균 보유액에 대해 기본 세전 연 1.1% 혜택이 제공된다.

- #### 사전 신청자에 한해 오는 5월 31일까지는 카카오페이머니가 자동 예탁되는 계좌 잔액에 대해 카카오페이증권이 제공하는 세전 최대 연 5%의 수익(예탁금 이용료)이 적용된다. 증권 계좌로 예탁되기 때문에 1인당 최고 5천만 원까지 예금자 보호도 받을 수 있다.

<file_sep>/Spring/그레이들 프로젝트를 스프링 부트 프로젝트로 변경하기.md
![image.jpg](https://github.com/Conatuseus/TIL/blob/master/images/%EC%9D%B4%EB%8F%99%EC%9A%B1%EB%8B%98%20%EC%B1%85/Springboot.jpg?raw=true)

> 이번 글은 이동욱님의 <스프링 부트와 AWS로 혼자 구현하는 웹 서비스>의 **그레이들 프로젝트를 스프링 부트 프로젝트로 변경하기**를 따라해보며 내용을 공유하고자 합니다.
>
> 평소 스프링 이니셜라이저를 통해 프로젝트를 생성해와서 build.gradle의 코드에서 dependency 외에 무슨 역할을 하는지를 잘 모르기 때문에 공부의 필요성을 느꼈습니다.

# 그레이들 프로젝트를 스프링 부트 프로젝트로 변경하기

 먼저, intellij에서 그레이들 기반의 자바 프로젝트를 생성합니다. 인텔리제이에서 `build.gradle` 파일을 열어보면 다음과 같은 간단한 코드들만 있습니다.

~~~ java
plugins {
    id 'java'
}

group 'com.conatuseus.book'
version '1.0-SNAPSHOT'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
}
~~~

이 코드들은 **자바 개발에 가장 기초적인 설정**만 되어있는 상태입니다. 이제 스프링 부트에 필요한 설정들을 하나씩 추가해봅시다.

먼저 build.gradle 맨 위에 위치할 코드입니다.

~~~ java
buildscript {
    ext {
        springBootVersion = '2.1.7.RELEASE'
    }
    repositories {
        mavenCentral()
        jcenter()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

~~~

 위 코드는 이 프로젝트의 플러그인 의존성 관리를 위한 설정입니다.

 `ext` 라는 키워드는 build.gradle에서 사용하는 전역변수를 설정하겠다는 의미인데, 여기서는 springBootVersion 전역변수를 생성하고 그 값을 '2.1.7.RELEASE'로 하겠다는 의미입니다. 즉, **spring-boot-gradle-plugin라는 스프링 부트 그레이들 플러그인의 2.1.7.RELEASE를 의존성으로 받겠다는 의미**입니다.

 다음은 앞서 선언한 플러그인 의존성들을 적용할 것인지를 결정하는 코드입니다.

~~~ java
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
~~~

 `io.spring.dependency-management` 플러그인은 스프링 부트의 의존성들을 관리해 주는 플러그인이라 꼭 추가해야 합니다.

 위 4개의 플러그인은 자바와 스프링 부트를 사용하기 위해서 필수 플러그인이니 항상 추가하면 됩니다. 나머지 코드는 다음과 같습니다.

```java
repositories {
    mavenCentral()
    jcenter()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
```

`repositories` 는 각종 의존성(라이브러리)들을 어떤 원격 저장소에서 받을지를 정합니다. 기본적으로 mavenCentral을 많이 사용하지만, 최근에는 **라이브러리 업로드 난이도** 때문에 jcenter도 많이 사용합니다.

mavenCentral은 이전부터 많이 사용하는 저장소지만, 본인이 만든 라이브러리를 업로드하기 위해 많은 과정과 설정이 필요하다보니 개발자들이 직접 만든 라이브러리를 업로드하는 것이 힘들어 점점 공유가 안되는 상황이 발생했습니다.

 이런 문제점을 개선하여 최근에 나온 것이 jcenter입니다. 그리고 jcenter에 라이브러리를 업로드하면 mavenCentral에도 업로드될 수 있도록 자동화를 할 수 있습니다. 여기서는 mavenCentral, jcenter 둘 다 등록해서 사용하겠습니다.

`dependencies` 는 프로젝트 개발에 필요한 의존성들을 선언하는 곳입니다. 여기서는 org.springframework.boot:spring-boot-starter-web과 org.springframework.boot:spring-boot-starter-test를 받도록 선언되어 있습니다.

 의존성 코드는 직접 작성해도 되고, 자동완성으로 만들어도 됩니다. 단, **특정 버전을 명시하면 안됩니다.** 버전을 명시하지 않아야만 맨 위에 작성한 'org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}'의 버전을 따라가게 됩니다.

 이렇게 관리할 경우 **각 라이브러리들의 버전 관리가 한 곳에 집중되고, 버전 충돌 문제도 해결되어 편하게 개발을 진행할 수 있습니다.**

> 책에서는 dependencies에 compile, testCompile을 사용하였지만 저는 implementation을 사용했습니다.
> 이유는 [document](https://docs.gradle.org/current/userguide/java_library_plugin.html) 를 보시면 알 수 있을 것 같습니다!

이 코드를 모두 적용한 전체 코드는 다음과 같습니다.

~~~ java
buildscript {
    ext {
        springBootVersion = '2.1.7.RELEASE'
    }
    repositories {
        mavenCentral()
        jcenter()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group 'com.conatuseus.book'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
    jcenter()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
~~~

그리고 Gradle 탭을 보면 아래와 같이 의존성이 잘 받아졌는지 확인할 수 있습니다.

![image.png](https://images.velog.io/post-images/conatuseus/845c2dc0-1e72-11ea-b71c-d18bc94cecdf/image.png)

> 마치며...
>
>gradle을 파보진 않았지만, 현재 진행중인 프로젝트들의 gradle을 이해하는데 많은 도움이 될 것 같다.
검색하다보니 gradle과 maven에 대해 공부할 것들이 생각보다 더 많은 것 같다.
조만간 gradle과 maven을 공부하는 시간을 가져야겠다.

<file_sep>/Article/2019-11/28.md
# 2019-11-28 목요일

## [네트워크부터 DB·앱까지….구글 신규 클라우드 솔루션 5종 출시](http://www.bloter.net/archives/362655)
<file_sep>/Article/2019-12/4.md
# 2019-12-4 수요일

## [‘결제’로 대동단결한 네이버-페이코-라인, 대만 시장 노린다](http://www.bloter.net/archives/363492)
- 간편금융 플랫폼 기업 NHN페이코는 라인페이 대만 법인이 주최한 ‘모바일 결제 크로스보더 얼라이언스(Mobile Payment Cross-Border Alliance): 국경 없는 결제 경험’ 포럼에 한국 대표 파트너사로 참석해 대만 시장 진출 계획을 시사했다.
<file_sep>/JPA/JPA프로그래밍/README.md
# JPA 프로그래밍

김영한님의 인프런 강좌, <JPA 프로그래밍> 책을 보고 기록하는 공간입니다.

## List

1. [JPA 소개](https://github.com/Conatuseus/TIL/blob/master/JPA/JPA프로그래밍/JPA소개.md)
2. [영속성컨텍스트_1](https://github.com/Conatuseus/TIL/blob/master/JPA/JPA프로그래밍/영속성컨텍스트_1.md)
3. [영속성컨텍스트_2](https://github.com/Conatuseus/TIL/blob/master/JPA/JPA프로그래밍/영속성컨텍스트_2.md)
4. [엔티티매핑_1](https://github.com/Conatuseus/TIL/blob/master/JPA/JPA프로그래밍/엔티티매핑_1.md)
5. [엔티티매핑_2](https://github.com/Conatuseus/TIL/blob/master/JPA/JPA프로그래밍/엔티티매핑_2.md)
6. [연관관계 매핑 기초 1](https://github.com/Conatuseus/TIL/blob/master/JPA/JPA프로그래밍/연관관계_매핑_기초_1.md)
7. [연관관계 매핑 기초 2](https://github.com/Conatuseus/TIL/blob/master/JPA/JPA프로그래밍/연관관계_매핑_기초_2.md)

   [임베디드 타입](https://github.com/Conatuseus/TIL/blob/master/JPA/JPA프로그래밍/임베디드_타입.md)
<file_sep>/Article/2020-01/26.md
# 2020-01-26 일요일

## [AWS에 맞설 탈중앙화 클라우드 플랫폼 현실로?](http://www.bloter.net/archives/368931)

- 블록체인 기술을 활용해 아마존웹서비스(AWS), 구글, 마이크로소프트 같은 대규모 퍼블릭 클라우드 플랫폼 회사들의 아성에 도전하려는 스타트업 디피니티가 제한된 형태지만 그동안 개발해온 기술을 시연 형태로 선보여 눈길을 끈다.
- 디피티니는 최근 자사 탈중앙화 클라우드 서비스 기반으로 돌아가는 소셜 네트워크 서비스인 링크드업을 시연했다. 링크드업은 링크드인 오픈소스 소프트웨어 버전 성격이다. 단지 시연일 뿐이었지만 링크드업은 디피니티 분산 컴퓨팅 아키텍처에서 애플리케이션이 어떻게 돌아가는지에 대한 프리뷰를 제공했다는 점에서 관심을 받았다.
- 앞서 디피니티는 지난해 11월 자사 플랫폼에서 쓰일 소프트웨어 개발 키트(SDK) 알파 버전과 자사 플랫폼 스마트 컨트랙트 프로그래밍 언어에 대한 세부 내용을 공개한 바 있다. 이번 시연은 11월 발표 이후 중요한 개발 업데이트로 평가받는 모습이다.
- AWS, 마이크로로소프트 애저, 구글 클라우드 같은 플랫폼들은 이미 퍼블릭 클라우드 서비스 시장의 주류로 자리를 굳혀가는 모습이다. 이런 상황에서 탈중앙화 기반 플랫폼이 이들 거대 클라우드 서비스들이 확보한 지위를 위협하는 것은 만만한 일은 아니다.
- 그럼에도 디피니티가 관심을 끄는 것은 구글 출신들을 포함해 고급 엔지니어들이 주도하는데다 안드레센 호로위치, 폴리체인캐피털 등 실리콘밸리 유력 벤처 캐피털들이 주요 투자사로 참여하고 있기 때문이다. 디피니티는 현재까지 1억6000만달러 규모의 자금을 유치했다.<file_sep>/Article/2019-12/16.md

 # 2019-12-16 월요일

 ## [‘산타토익’ 뤼이드, ‘AI 서밋 뉴욕 2019’서 교육 AI 비전 제시](http://www.bloter.net/archives/364520)

 ## [글로벌 푸드 배달 서비스 시장 재편 가속…우버, 인도 사업 매각 임박](http://www.bloter.net/archives/364580)
<file_sep>/Spring/Querydsl를 추천하는 이유-이동욱님.md
> 규모가 있는 프로젝트에서의 데이터 조회는 FK의 조인, 복잡한 조건 등으로 인해 이런 Entity 클래스만으로 처리하기 어려워 조회용 프레임워크를 추가로 사용합니다. 대표적 예로 querydsl, jooq, MyBatis 등이 있습니다. 조회는 위 3가지 프레임워크 중 하나를 통해 조회하고, 등록/수정/삭제 등은 SpringDataJpa를 통해 진행합니다. 이동욱님은 querydsl를 추천합니다.

## Querydsl을 추천하는 이유

- 타입 안전성이 보장됩니다.

  단순한 문자열로 쿼리를 생성하는 것이 아니라, 메서드를 기반으로 쿼리를 생성하기 때문에 오타나 존재하지 않는 컬럼명을 명시할 경우 IDE에서 자동으로 검출됩니다. 이 장점은 Jooq에서도 지원하는 장점이지만, MyBatis에서는 지원하지 않습니다.

- 레퍼런스가 많습니다.

  많은 회사와 개발자들이 사용하다보니 그만큼 국내 자료가 많습니다. 어떤 문제가 발생했을 때 여러 커뮤니티에 질문하고 그에 대한 답변을 들을 수 있다는 것이 큰 장점입니다.<file_sep>/Article/2019-12/18.md
# 2019-12-18

## [이세돌, AI와 은퇴 경기 첫 대국 승리…92수만에 불계승](http://www.bloter.net/archives/364797)
<file_sep>/Article/2019-12/21.md
# 2019-12-21 토요일

## [이커머스 키우는 페이스북, 라이브 쇼핑까지 넘본다](http://www.bloter.net/archives/365134)
- 페이스북이 자사 마켓플레이스 서비스내에서 라이브 쇼핑 기능을 개발하기 위한 일환으로 올초해 소규모 비디오 쇼핑 스타트업을 인수한 것으로 확인됐다.
- 페이스북은 지난 몇년간 이커머스 분야에서 거점을 확보하기 위해 이런저런 시도들을 했지만 인상적인 행보는 보여주지 못했다. 하지만 크레이그리스트처럼 중고 제품을 사고 팔 수 있는 기능을 제공하는 마켓플레이스의 경우 출시 3년여만에 나름 기반을 확보했다. 마켓플레이스 월간 사용자수는 10억명 가까이에 이른다.
- 페이스북 산하 사진 공유 앱인 인스타그램도 2020년 성장 동력으로 쇼핑을 전진배치하는 모습이다. 인스타그램은 최근 사용자들이 앱에서 브랜드 제품을 바로 구입할 수 있는 기능도 추가했다.
<file_sep>/OS/세마포어와 뮤텍스.md
# 세마포어와 뮤텍스
오늘은 **세마포어와 뮤텍스의 개념과 차이점**에 대해 알아보겠습니다.
잘못된 내용이 있으면 댓글로 피드백 주시면 정말 감사하겠습니다.

## 정의
**세마포어와 뮤텍스**은
**"여러 프로세스나 쓰레드가 공유 자원에 접근하는 것을 제어하기 위한 방법"**
으로 정의할 수 있습니다. 즉, **병행 처리를 위한 프로세스 동기화 기법**입니다.
예를 들자면 교차하는 철도에서 열차의 진행가능 여부를 나타내는 신호등이 세마포어나 뮤텍스가 될 수 있습니다. (둘의 차이는 뒤에서 알아보겠습니다)

## 세마포어의 동작 원리
세마포어의 동작 원리를 알려면 세마포어 변수, semWait 연산, semSignal 연산에 대해 먼저 알아야 합니다.

세마포어는 정수 값을 가지는 변수로 볼 수 있습니다. 그 정수 값은 접근할 수 있는 최대 허용치 만큼 동시에 사용자 접근을 할 수 있게 합니다.

**semWait 연산**: 세마포어 값을 감소시킵니다. 만일 값이 음수가 되면 semWait를 호출한 프로세스는 블록됩니다. 음수가 아니면, 프로세스는 계속 수행될 수 있습니다.

**semSignal 연산**: 세마포어 값을 증가시킵니다. 만약 값이 양수가 아니면(0이거나 음수면), semWait 연산에 의해 블록된 프로세스들을 깨웁니다.

~~~ java
struct semaphore {
	int count;
    	queueType queue;
};

void semWait (semaphore s) {
	s.count--;
    if (s.count < 0) {
    	/* 이 구역으로 들어왔다는 것은 현재 프로세스(혹은 쓰레드)가 공유 자원에 접근할 수 없다는 것을 의미*/
    	/* 요청한 프로세스를 s.queue에 연결 */
        /* 요청한 프로세스를 블록 상태로 전이 시킴*/
    }
}

void semSignal (semaphore s) {
	s.count++;
    if (s.count <= 0) {
    	/* count가 0보다 작거나 같다는 것은 대기하고 있는 프로세스(또는 스레드)가 존재한다는 것을 의미*/
        /* s.queue에 연결되어 있는 프로세스를 큐에서 제거 */
        /* 프로세스의 상태를 실행 가능으로 전이시키고 ready list에 연결 */
    }
}
~~~

세마포어는 위와 같이 되어 있고, 이것을 활용하면 다음과 같이 구현할 수 있습니다.

~~~ java
const int n = /* 프로세스 개수 */;
semaphore s = 1;

void P (int i) {
	while (true) {
    	semWait(s);

        /* 임계 영역(Critical Section)*/

        semSignal(s);

        /* 임계 영역 이후 코드 */
    }
}

void main() {
	parbegin (P(1), P(2), ..., P(n));
}
~~~

## 세마포어의 종류
세마포어는 유지할 수 있는 값의 범위에 따라 이진 세마포어와 범용 세마포어(또는 카운팅 세마포어)로 구분됩니다.
위에서 봤던 세마포어를 **카운팅 세마포어(counting semaphore)** 또는 **범용 세마포어(general semaphore)** 라고 부릅니다. 세마포어의 초기값이 0이상의 수입니다.

**이진 세마포어(binary semaphore)** 는 세마포어의 초기 값이 0또는 1만 가질 수 있는 세마포어 입니다. (이후에 알아볼 것이지만 뮤텍스가 이진 세마포어와 비슷한 것이라 생각하고 계시면 됩니다)

이렇게 세마포어의 초기 값에 따라 범용 세마포어, 이진 세마포어로 나뉘는 것을 공부했습니다.

<참고>
세마포어는 큐에 프로세스들이 여러 개 있을 때 어떤 프로세스부터 깨울지에 따라 **강성 세마포어**와 **약성 세마포어**로 나뉩니다. 큐에서 선입선출로 꺼낸다면 강성 세마포어이며, 프로세스들이 큐에서 제거되는 순서를 특별히 명시하지 않은 세마포어를 약성 세마포어라고 합니다.

## 뮤텍스
뮤텍스는 세마포어와 마찬가지로 **병행 처리를 위한 동기화 기법** 중 하나입니다. 이진 세마포어와 같이 초기값을 1과 0으로 가집니다.
임계영역에 들어갈 때 `락(lock)`을 걸어 다른 프로세스(혹은 쓰레드)가 접근하지 못하도록 하고, 임계영역에서 나와 해당 락을 `해제(unlock`) 합니다.

간단히 코드로 보자면 다음과 같습니다.

~~~ java
mutex = 1;

void lock () {
	while (mutex != 1) {
    	/* mutex 값이 1이 될 때까지 기다립니다.*/
    }
    /* 이 구역에 도착했다는 것은 mutex 값이 1이라는 것입니다.
       따라서 이제 뮤텍스 값을 0으로 만들어 다른 프로세스(혹은 쓰레드)가 접근하지 못하도록 막아야 합니다.
    */
    mutex = 0;
}

void unlock() {
	/* 임계 구역에서 나온 프로세스는 다른 프로세스가 접근할 수 있도록 락을 해제합니다.*/
	mutex = 1;
}
~~~

## 뮤텍스와 세마포어의 차이는?

- 세마포어는 공유 자원에 **세마포어의 변수만큼의 프로세스(또는 쓰레드)가 접근**할 수 있습니다. 반면에 뮤텍스는 **오직 1개만의 프로세스(또는 쓰레드)만 접근**할 수 있습니다.

- 현재 수행중인 프로세스가 아닌 **다른 프로세스가 세마포어를 해제할 수 있습니다.** 하지만 뮤텍스는 **락(lock)을 획득한 프로세스가 반드시 그 락을 해제**해야 합니다.

> 참고 자료: 도서 <운영체제 내부구조 및 설계 원리><file_sep>/회고/README.md
# 회고
- 해당 디렉토리에서는 년도별 회고만 기록합니다.
- 분기별 회고는 [블로그](https://velog.io/@conatuseus)에서 읽어보실 수 있습니다.
<file_sep>/Article/2020-03/7.md
# 2020-03-07

## [‘타다’ 막아세운 국회, ‘타다금지법’ 통과](http://www.bloter.net/archives/373709)

- 여객자동차 운수사업법 개정안(일명 ‘타다금지법’)이 결국 국회 본회의를 통과했다. 법안이 통과될 경우 타다 측은 조만간 주요 서비스를 중단하겠다고 선언한 터라 후폭풍이 만만치 않을 전망이다.
- 국회는 3월6일 오후 본회의를 열고 모빌리티 업계 초미의 관심사였던 여객운수법 개정안(박홍근 더불어민주당 의원 발의)을 재석 의원 185인 중 찬성 168인, 반대 8인, 기권 9인으로 가결했다. 이로써 현행 타다 서비스는 불법이 됐다. 법안은 공포 후 1년6개월의 유예기간이 적용된다.
- 신설되는 플랫폼 운송사업은 플랫폼사업자가 기여금을 내면 새로운 형태의 면허를 발급해주는 방식이다. 허가 대수는 총량에 따라 관리 받는다. 대신 외관·부제·요금 등 각종 규제에서 자유로운 서비스가 가능하다. 타다는 이같이 규제가 강화되면 사업이 불가하다며 법안에 반대해왔다. 또 법안 발의 과정에서 타다와 같은 11인승 승합차 기반 기사 포함 렌터카 모델의 사업을 제한하는 조항이 들어가면서 이 법안은 ‘타다 금지법’으로 불렸다.
- 그러나 타다는 규제가 강화되면 수익을 내기 어렵다고 보고, 조만간 타다 베이직 서비스를 중단하겠다고 선언했다. 지난 2018년 10월 출시된 종합 모빌리티 플랫폼 ‘타다’는 불과 9개월 만에 가입자 100만명을 모으며 인기를 얻었다. 타다의 핵심 서비스인 타다 베이직은 렌터카의 경우 11인승 이상 15인승 이하 승합차만 예외적으로 운전자 알선이 허용되는 조항(여객자동차운수사업법 시행령 18조 1항)을 사업 근거로 삼았다.
- 여객법 개정안이 통과되면서 여객자동차 운수사업의 일종으로 ‘플랫폼 운송사업’ 제도가 신설된다. 다양한 플랫폼 택시사업을 할 수 있는 법적 토대가 마련된 셈이다. 타다를 제외한 모빌리티 업계는 법안의 통과를 요구해왔다. 최소한의 사업근거가 갖춰져야 모빌리티 산업이 활성화될 수 있다는 기대 때문이다.
- 동시에 업계의 우려도 크다. 기여금 수준과 납부 방식, 총량 제한 등 모빌리티 산업의 숨통을 막을 위험요소가 남아 있어서다. 특히 택시업계가 플랫폼 운송사업의 기여금·총량 등에 촉각을 곤두세우고 있어, 시행령을 통해 세부 규정을 정하는 과정에서 모빌리티 업계와 택시업계 간 진통이 예상된다.
- 코리아스타트업포럼은 “앞길은 전혀 알 수 없는 절벽 위에 스타트업을 세워놓고 퇴로는 막아버린 법”이라고 규탄했다. 또 “스타트업이 사업을 잘 해도 사업을 확장할 수 없을 것이라는 총량 규제, 소비자의 선택을 받는 서비스가 되더라도 수익을 낼 수 없을 것이라는 기여금 규제에 대한 우려가 팽배하다”라며 “이미 자본력 있는 기업만 (플랫폼 운송사업에) 진출할 수 있고 스타트업은 엄청난 진입장벽에서 시작해야 한다. 이에 대해 주무부처인 국토부가 답해줘야 한다”라고 말했다.<file_sep>/Article/2019-11/22.md

# 2019-11-22 금요일

## [“무단 녹음·인터넷 접속” 보안 뚫린 안드로이드폰…삼성도](http://www.bloter.net/archives/361783)
- Summary
  + 안드로이드폰에는 다양한 앱이 사전 설치 형태로 시스템에 탑재
  + 미국 보안업체 크립토와이어에 따르면 사전 설치 앱에 개인 정보를 갈취하는 취약점 146개가 발견돼 사용자의 주의가 요구됨
  + 146건의 취약점, 삼성전자 33개로 가장 많아
  + 크립토와이어가 발견한 취약점에는 여러 가지 문제를 포함한다. 무단 음성녹음과 잘못된 명령 실행, 시스템과 무선 통신 설정 변경이 대표적
  + 이번에 발견된 취약점의 가장 큰 문제는 기기에 내장된 사용자가 따로 손을 쓸 수 없다는 점이다. 안드로이드 운영체제 고유의 문제이기도 하고 구글도 인지하고 있다.

## [“5G 품질, 3D 실사 지도로 높인다”](http://www.bloter.net/archives/361952)
- Summary
  + SK텔레콤은 전국 구석구석을 3D로 구현한 지도를 활용해 5G 네트워크 설계 정교화 작업을 진행
  + SKT는 21일 국토교통부 산하 공간정보산업진흥원과 ‘3D 기반 공간 정보 데이터 활용을 위한 업무 협약’을 체결함으로써 전국의 공간 데이터를 활용한 3D 커버리지 설계 시스템을 고도화할 수 있게 됐다. 공간 데이터는 3D 전국 지형 모델을 비롯해 항공사진, 위성영상 및 시각화된 행정 공간 정보까지를 총망라한다.
<file_sep>/Article/2019-11/18.md
# 2019-11-18 월요일

## [라인·야후재팬 ‘경영통합’ 합의…아시아 ‘메가플랫폼’ 탄생](http://www.bloter.net/archives/361379)

- Summary
  - 네이버 자회사 라인(LINE)과 소프트뱅크 손자회사 야후재팬이 경영통합에 합의
  - “이번 경영통합이 핀테크 분야의 성장을 가속화하고, 기술을 통한 새로운 사업영역 진출 가능성을 높인다고 판단"
  - 거래가 예정대로 완료될 경우 네이버와 소프트뱅크는 각각 50%씩 출자해 라인을 합작회사로 만든다

## [우아한형제들 서빙로봇 ‘딜리’…“2년 계약, 월 90만원”](http://www.bloter.net/archives/361340)

- Summary
  - 배달의민족 운영사 우아한형제들이 매장 내 서빙로봇 ‘딜리’ 렌탈 프로그램을 정식 출시
  - 딜리는 실내 레스토랑 전용 자율주행 로봇
  - 점원이 딜리의 선반에 음식을 올려놓고 테이블 번호를 누르면, 딜리는 주문자의 테이블까지 최적의 경로로 이동
  - 김요섭 우아한형제들 로봇딜리버리셀 이사는 “서빙로봇 딜리의 궁극적인 목표는 가게의 운영 효율성을 높이는 것”이라며 “서빙로봇이 단순반복 업무, 야간 근무 등 어려운 일을 맡으면 점원은 사람만이 할 수 있는 일에 집중할 수 있고 고객 서비스 퀄리티는 전반적으로 향상된다”

<file_sep>/JPA/JPA프로그래밍/연관관계_매핑_기초_2.md
> 이번 글에서는 <양방향 연관관계와 연관관계의 주인>에 대해 알아보겠습니다.
이 시리즈 글은 김영한 님의 강의, 책을 보고 적은 것임을 알려드립니다. (강추)
오타 및 피드백 환영합니다.

# 양방향 연관관계
---
이전 글에서는 회원에서 팀으로만 접근하는 다대일 단방향 매핑을 알아 보았습니다. 이번에는 팀에서 회원으로 접근하는 관계를 접근하는 관계를 추가해서, 양방향 연관관계로 매핑을 해보겠습니다.

![image.png](https://images.velog.io/post-images/conatuseus/3a0867c0-e529-11e9-9426-0f575ede7eb4/image.png)

먼저 객체 연관관계를 살펴보겠습니다.
위 그림을 보면, 회원과 팀은 다대일 관계입니다. 반대로 팀에서 회원은 일대다 관계입니다. 일대다 관계는 여러 건과 연관관계를 맺을 수 있으므로 컬렉션을 사용해야 합니다.

 객체 연관관계는 다음과 같습니다.
 - 회원 ➡ 팀 (Member.team)
 - 팀 ➡ 회원 (Team.members)

![image.png](https://images.velog.io/post-images/conatuseus/a5a9f6b0-e529-11e9-9444-f17ee2e6d8aa/image.png)

위는 테이블 연관관계입니다.
이 그림을 처음 봤을 때 **'TEAM 테이블에 왜 members가 없지...?', '책의 오타인가..?'** 라고 생각했습니다.
하지만 강의를 더 듣고나서 위처럼 되는 것이 옳음을 알았습니다. 이제 TEAM 테이블이 위처럼 되는 이유를 살펴봅시다.

내가 소속된 팀 이름을 알고 싶으면 TEAM 테이블의 TEAM_ID로 MEMBER 테이블에 조인하면 됩니다.
팀에 소속된 멤버들을 알고 싶으면 MEMBER의 TEAM_ID로 TEAM 테이블에 join하면 됩니다.
따라서 **데이터베이스 테이블은 외래 키 하나로 양방향으로 조회할 수 있습니다.**

 ## 양방향 연관관계 매핑
 이제 코드를 통해 양방향 관계를 매핑하는 것을 알아봅시다.

 ~~~ java
@Entity
public class Member {

  @Id
  @Column(name = "MEMBER_ID")
  private String id;

  private String username;

  @ManyToOne
  @JoinColumn(name="TEAM_ID")
  private Team team;

  // 연관관계 설정
  public void setTeam(Team team) {
    this.team = team;
  }

  // Getter, Setter ...
}
~~~

~~~ java
@Entity
public class Team {

  @Id
  @Column(name = "TEAM_ID")
  private String id;

  private String name;

  @OneToMany(mappedBy = "team")
  private List<Member> members = new ArrayList<Member>();

  // Getter, Setter ...

}
~~~
팀과 회원은 일대다 관계입니다. 따라서 팀 엔티티에 `List<Member> members`를 추가했습니다. 그리고 일대다 관계를 매핑하기 위해 `@OneToMany` 매핑 정보를 사용했습니다. `@OneToMany`의 mappedBy 속성은 양방향 매핑일 때 사용하는데, 반대쪽 매핑의 필드 이름을 값으로 주면 됩니다. 반대쪽 매핑이 Member.team이므로 team을 값으로 주었습니다. mappedBy는 뒤에서 더 알아보도록 하겠습니다.

## 일대다 컬렉션 조회
  앞에서 배운 내용을 토대로 이제 일대다 방향으로 객체 그래프 탐색을 해보겠습니다.

  ~~~ java
// member1: (id=1, username="회원1", team="team1")
// member2: (id=2, username="회원2", team="team1")

Team team = em.find(Team.class, "team1");
List<Member> members = team.getMembers(); // (팀 -> 회원) 객체 그래프 탐색

for (Member member : members) {
  System.out.println("member.username = " + member.getUsername());
}

// === 결과 ===
// member.username = 회원1
// member.username = 회원2
  ~~~

 # 연관관계의 주인
 ---
 이전까지 @ManyToOne, @OneToMany에 대해 알아봤습니다. Team의 @OneToMany에 `mappedBy` 속성은 왜 있을까요?

  엄밀히 이야기하면 객체에는 양방향 연관관계라는 것이 없습니다. 서로 다른 단방향 연관관계 2개를 애플리케이션 로직으로 잘 묶어서 양방향인 것처럼 보이게 할 뿐입니다. 반면에 데이터베이스 테이블은 외래 키 하나로 양쪽이 서로 조인할 수 있습니다. 따라서 **테이블은 외래 키 하나만으로 양방향 연관관계를 맺습니다.**

**객체 연관관계 = 2개**
- 회원 ➡ 팀 : 연관관계 1개(단방향)
- 팀 ➡ 회원 : 연관관계 1개(단방향)
**테이블 연관관계 = 1개**
- 회원 ↔ 팀 : 연관관계 1개(양방향)

**엔티티를 양방향 연관관계로 설정하면 객체의 참조는 둘인데 외래 키는 하나입니다. 따라서 둘 사이에 차이가 발생합니다.** 그렇다면 둘 중 어떤 관계를 사용해서 외래 키를 관리해야 할까요?
 이런 차이로 인해 JPA에서는 **두 객체 연관관계 중 하나를 정해서 테이블의 외래키를 관리해야 하는데 이것을 연관관계의 주인**이라 합니다.

 ## 양방향 매핑의 규칙: 연관관계의 주인
 양방향 연관관계 매핑 시 지켜야할 규칙이 있는데 두 연관관계 중 하나를 연관관계의 주인으로 정해야 합니다. **연관관계의 주인만이 데이터베이스 연관관계와 매핑되고 외래 키를 관리(등록, 수정, 삭제)할 수 있습니다. 반면에 주인이 아닌 쪽은 읽기만 할 수 있습니다.**
 어떤 연관관계를 주인으로 정할지는 `mappedBy` 속성을 사용하면 됩니다.
 - 주인은 `mappedBy` 속성을 사용하지 않는다.
 - 주인이 아니면 mappedBy 속성을 사용해서 속성의 값으로 연관관계의 주인을 지정해야 한다.

![image.png](https://images.velog.io/post-images/conatuseus/019d12b0-e5a0-11e9-be70-77c02759e378/image.png)

 그렇다면 Member.team, Team.members 둘 중 어떤 것을 연관관계의 주인으로 정해야 할까요?

 - 회원 ➡ 팀(Member.team) 방향
 ~~~ java
class Member {

  @ManyToOne
  @JoinColumn(name = "TEAM_ID")
  private Team team;
  // ...
}
~~~

- 팀 ➡ 회원(Team.members) 방향
~~~ java
class Team {

  @OneToMany
  private List<Member> members = new ArrayList<>();
  // ...
}
~~~

 **연관관계의 주인을 정한다는 것은 사실 외래 키 관리자를 선택하는 것입니다.**
 그림을 보면, 여기서는 회원 테이블에 있는 TEAM_ID 외래 키를 관리할 관리자를 선택해야 합니다. 만약 회원 엔티티에 있는 Member.team을 주인으로 선택하면 자기 테이블에 있는 외래 키를 관리하면 됩니다. 하지만 팀 엔티티에 있는 Team.members를 주인으로 선택하면 물리적으로 전혀 다른 테이블의 외래 키를 관리해야 합니다. 왜냐하면 이 경우 Team.members가 있는 Team 엔티티는 TEAM 테이블에 매핑되어 있는데 관리해야할 외래 키는 MEMBER에 있기 때문입니다.

 ## 연관관계의 주인은 외래 키가 있는 곳
  **연관관계의 주인은 테이블에 외래 키가 있는 곳으로 정해야 합니다.** 여기서는 회원 테이블이 외래 키를 가지고 있으므로 Member.team이 주인이 됩니다. 주인이 아닌 Team.members에는 `mappedBy="team"` 속성을 사용해서 주인이 아님을 설정해야 합니다. 여기서 `mappedBy`의 값으로 사용된 team은 연관관계의 주인인 Member 엔티티의 team 필드를 말합니다.
 ![image.png](https://images.velog.io/post-images/conatuseus/45ee5cf0-e5a3-11e9-8a66-09d286f71b9a/image.png)

 정리하면, 연관관계의 주인만 데이터베이스 연관관계와 매핑되고 외래 키를 관리할 수 있습니다. 주인이 아닌 반대편은 읽기만 가능하고 외래 키를 변경하지 못합니다.

> 데이터베이스 테이블의 다대일, 일대다 관계에서는 항상 **다** 쪽이 외래 키를 가집니다. 다 쪽인 `@ManyToOne`은 항상 연관관계의 주인이 되므로 `mappedBy`를 설정할 수 없습니다. 따라서 `@ManyToOne`에는 `mappedBy` 속성이 없습니다.

# 양방향 연관관계 저장
---
 양방향 연관관계를 사용해서 팀1, 회원1, 회원2를 저장해보겠습니다.
 ~~~ java
public void save() {

  // 팀1 저장
  Team team1 = new Team("team1", "팀1");
  em.persist(team1);

  // 회원1 저장
  Member member1 = new Member("member1", "회원1");
  member.setTeam(team1);  // 연관관계 설정 team1 -> team1
  em.persist(member1);

  // 회원2 저장
  Member member2 = new Member("member2", "회원2");
  member2.setTeam(team1); // 연관관계 설정 member2 -> team1
  em.persist(member2);
}
~~~
 위 코드를 보면, 팀1을 저장하고 회원1, 회원2에 연관관계의 주인인 Member.team 필드를 통해서 회원과 팀의 연관관계를 설정하고 저장했습니다.
 데이터베이스에서 회원 테이블을 조회해보겠습니다. ( SELECT * FROM MEMBER; )

 MEMBER_ID|USERNAME|TEAM_ID
 ---|---|---
 member1|회원1|team1
 member2|회원2|team1

 TEAM_ID 외래 키에 팀의 기본 키 값이 저장되어 있습니다.
 양방향 연관관계는 연관관계의 주인이 외래 키를 관리합니다. 따라서 주인이 아닌 방향은 값을 설정하지 않아도 데이터베이스에 외래 키 값이 정상 입력됩니다.

 team1.getMembers().add(member1);  // 무시(연관관계의 주인이 아님)
 team1.getMembers().add(member2); // 무시(연관관계의 주인이 아님)
 이런 코드가 추가로 있어야 할 것 같지만 Team.members는 연관관계의 주인이 아닙니다. 주인이 아닌 곳에 입력된 값은 외래 키에 영향을 주지 않습니다.

 # 양방향 연관관계의 주의점
 ---

 ~~~ java
Member member1 = new Member("member1", "회원1");
em.persist(member1);

Member member2 = new Member("member2", "회원2");
em.persist(member2);

Team team1 = new Team("team1", "팀1");
team1.getMembers().add(member1);
team1.getMembers().add(member2);

em.persist(team1);
~~~
회원1, 회원2를 저장하고 팀의 컬렉션에 담은 후에 팀을 저장했습니다. 과연 위 코드가 정상적으로 저장되었을까요?
위 코드를 실행하고 데이터 베이스에서 회원 테이블을 조회하면 다음과 같은 결과가 나옵니다.

MEMBER_ID|USERNAME|TEAM_ID
---|---|---
member1|회원1|null
member2|회원2|null

외래 키 TEAM_ID에 team1이 아닌 null 값이 입력되어 있는데, **연관관계의 주인이 아닌 Team.members에만 값을 저장했기 때문입니다.** **다시 한번 강조하지만 연관관계의 주인만이 외래 키의 값을 변경할 수 있습니다.**
연관관계의 주인인 Member.team에 아무 값도 입력하지 않았기 때문에, TEAM_ID 외래 키의 값도 null이 저장됩니다.

## 순수한 객체까지 고려한 양방향 연관관계
 그렇다면 정말 연관관계의 주인에만 값을 저장하고 주인이 아닌 곳에는 값을 저장하지 않아도 될까요?
 사실은 **객체 관점에서 양쪽 방향에 모두 값을 입력해주는 것이 안전합니다.** 양쪽 방향 모두 값을 입력하지 않으면 JPA를 사용하지 않는 순수한 객체 상태에서 심각한 문제가 발생할 수 있습니다.

 예를 들어 JPA를 사용하지 않고 엔티티에 대한 테스트 코드를 작성한다고 가정해봅시다. ORM은 객체와 관계형 데이터베이스 둘 다 중요합니다. 데이터베이스뿐만 아니라 객체도 함께 고려해야 합니다.
 ~~~ java
public void test(){

  Team team1 = new Team("team1", "팀1");
  Member member1 = new Member("member1", "회원1");
  Member member2 = new Member("member2", "회원2");

  member1.setTeam(team1);
  member2.setTeam(team2);

  List<Member> members = team1.getMembers();
  System.out.println("members.size = " + members.size());
}
// 결과: member.size = 0
~~~
 코드를 보면 Member.team에만 연관관계를 설정하고 반대 방향은 연관관계를 설정하지 않았습니다. 마지막 줄에서 팀에 소속된 회원이 몇 명인지 출력해보면 결과는 0이 나옵니다. 이것은 우리가 기대하는 양방향 연관관계의 결과가 아닙니다.
 따라서 양방향은 양쪽다 관계를 설정해야 합니다. 이처럼 회원➡팀 을 설정하면 다음 코드처럼 반대방향인 팀➡회원도 설정해야 합니다.
 양쪽 모두 관계를 설정한 코드를 보겠습니다.

~~~ java
public void test(){

  Team team1 = new Team("team1", "팀1");
  Member member1 = new Member("member1", "회원1");
  Member member2 = new Member("member2", "회원2");

  member1.setTeam(team1);
  team1.getMembers().add(member1);

  member2.setTeam(team2);
  team1.getMembers().add(member2);

  List<Member> members = team1.getMembers();
  System.out.println("members.size = " + members.size());
}
// 결과: member.size = 2
~~~
양쪽 모두 관계를 설정하니 기대했던 결과값 2가 나옵니다.
이제 JPA를 사용해서 완성한 예제를 보겠습니다.

~~~ java
public void test(){

  Team team1 = new Team("team1", "팀1");
  em.persist(team1);

  Member member1 = new Member("member1", "회원1");
  member1.setTeam(team1);
  team1.getMembers().add(member1);
  em.persist(member1);

  Member member2 = new Member("member2", "회원2");
  member2.setTeam(team2);
  team1.getMembers().add(member2);
  em.persist(member2);
}
~~~
위와 같이 양쪽에 연관관계를 설정했습니다.
순수한 객체 상태에서도 동작하며, 테이블의 외래 키도 정상 입력됩니다.

## 연관관계 편의 메서드
양방향 연관관계는 결국 양쪽 다 신경 써야 합니다. member.setTeam(team)과 team.getMembers().add(member)를 각각 호출하다 보면 실수로 둘 중 하나만 호출해서 양방향이 깨질 수 있습니다.

그래서 Member 클래스의 setTeam() 메서드를 수정해서 코드를 리팩토링 해보겠습니다.
~~~ java
public class Member{

  private Team team;

  public void setTeam(Team team) {
    this.team = team;
    team.getMembers().add(this);
  }
  // ...
}
~~~
`setTeam()` 메서드 하나로 양방향 관계를 모두 설정하도록 변경했습니다.
이렇게 수정한 메서드를 사용하는 코드를 보겠습니다.

~~~ java
public void test() {

  Team team1 = new Team("team1", "팀1");
  em.persist(team1);

  Member member1 = new Member("member1", "회원1");
  member1.setTeam(team1);
  em.persist(member1);

  Member member2 = new Member("member2", "회원1");
  member2.setTeam(team1);
  em.persist(member2);
}
~~~
이렇게 **한 번에 양방향 관계를 설정하는 메서드를 연관관계 편의 메서드**라 합니다.

## 연관관계 편의 메서드 작성 시 주의사항
~~~ java
member.setTeam(team1);
member.setTEam(team2);
Member findMember = teamA.getMember(); // member1이 여전히 조회된다.
~~~

 teamB로 변경할 때 teamA ➡ member1 관계를 제거하지 않았기 때문에 teamA.getMember() 메서드를 실행했을 때 member1이 남아있습니다. 따라서 연관관계를 변경할 때는 기존 팀이 있으면 기존 팀과 회원의 연관관계를 삭제하는 코드를 추가해야 합니다.
 아래는 Member 클래스 setTeam() 메서드입니다.

 ~~~ java
public void setTeam(Team team){

  if(this.team != null) {	// this.team이 null이 아니면 이 member객체는 team이 있음을 의미
    this.team.getMembers().remove(this);		// 해당 팀의 멤버에서 삭제
  }
  this.team = team;
  team.getMembers().add(this);
}
~~~
<file_sep>/Article/2019-11/16.md
# 2019-11-16 토요일

주말이라 그런지 기사가 거의 없다. 며칠 전꺼라도 적어보자.

## [네이버는 왜 기술 스타트업에 투자할까](http://www.bloter.net/archives/361096)

- summary
  - “기술 스타트업은 시장을 열고, 기술적 특이점을 만드는 등 중요한 역할을 하지만, 국내 전체 스타트업 투자 중 10분의 1에도 못 미치는 게 현실”
  - 기술 스타트업을 “시장의 문제를 독보적인 기술적 성취를 통해 해결하는 스타트업”으로 정의하며, 시장을 여는 역할을 한다는 점에서 기술 스타트업의 중요성을 짚었다.
  - 기술 스타트업이 어려움을 겪는 이유: 기술 예측이 어렵다는 점, 기술의 가치를 고객과 파트너 기업에 전달하기 어렵다는 점, 기술의 비고정성, 역량의 불균형, 늦은 해외 진출로 인한 낮은 도달률 등

## [넷플릭스, 월 결제액 260억…유료 가입자 200만명](http://www.bloter.net/archives/360788)

- summary
  - 2018년 4월, 유료 이용자 40만 -> 2019년 10월, 유료 이용자 200만
  - 20대 38%, 30대 31%, 50대 이상 13%
  - 나도 하루빨리 사용자가 많은 서비스를 개발하고 싶다

<file_sep>/Docker/Docker_사용해보기.md
Docker의 명령은 `docker run`, `docker push`와 같이 **`Docker <명령>`** 형식이며, 항상 root 권한으로 실행해야 합니다.

# search 명령으로 이미지 검색하기

Docker는 [Docker Hub](https://registry.hub.docker.com)을 통해 이미지를 공유하는 생테계가 구축되어 있습니다.
유명 리눅스 배포판, 오픈 소스 프로젝트(Redis, Nginx 등)의 Docker 이미지는 모두 Docker Hub에서 구할 수 있습니다.

![image.png](https://images.velog.io/post-images/conatuseus/650350b0-1775-11ea-bca6-193faaf8cc7a/image.png)

# pull 명령으로 이미지 받기

Docker Hub에서 우분투 리눅스 이미지를 받아보겠습니다.

`sudo docker pull ubuntu:latest`

Docker pull <이미지 이름>:<태그> 형식입니다. latest를 설정하면 최신 버전을 받습니다.
ubuntu:14.04, ubuntu:12.10처럼 태그를 지정해 줄 수도 있습니다.

![image.png](https://images.velog.io/post-images/conatuseus/767df480-1775-11ea-af24-bff57c817829/image.png)

# images 명령으로 이미지 목록 출력하기

이제 받은 이미지의 목록을 출력해보겠습니다.

`sudo docker images`

![image.png](https://images.velog.io/post-images/conatuseus/7f48a060-1775-11ea-af24-bff57c817829/image.png)

**docker images** 명령은 모든 이미지 목록을 출력합니다.

# run 명령으로 컨테이너 생성하기

이미지를 컨테이너로 생성한 뒤 Bash 셸을 실행해보겠습니다.

`sudo docker run -i -t --name hello ubuntu /bin/bash`

**docker run <옵션> <이미지 이름> <실행할 파일> 형식입니다.

여기서는 ubuntu 이미지를 컨테이너로 생성한 뒤 ubuntu 이미지 안의 /bin/bash를 실행합니다. 이미지 이름 대신 이미지 ID를 사용해도 됩니다.

> **-i**(interactive), **-t**(Pseudo-try) 옵션을 사용하면 실행된 Bash 셸에 입력 및 출력을 할 수 있습니다.
>
> **--name** 옵션으로 컨테이너 이름을 지정할 수 있습니다. 이름을 지정하지 않으면 Docker가 자동으로 이름을 생성하여 지정합니다.

![image.png](https://images.velog.io/post-images/conatuseus/87b59be0-1775-11ea-bca6-193faaf8cc7a/image.png)

이제 호스트 OS와 완전히 격리된 공간이 생성되었습니다. `cd`,`ls` 명령으로 컨테이너 내부를 보면 호스트 OS와는 다르다는 것을 알 수 있습니다. `exit`를 입력하여 Bash 셸에서 빠져나옵니다. 우분투 이미지에서 /bin/bash 실행 파일을 직접 실행했기 때문에 여기서 빠져나오면 컨테이너가 정지됩니다.

# ps 명령으로 컨테이너 목록 확인하기

`sudo docker ps -a`

**docker ps** 형식입니다. **-a** 옵션을 사용하면 정지된 컨테이너까지 모두 출력하고, 옵션을 사용하지 않으면 실행되고 있는 컨테이너만 출력합니다.

![image.png](https://images.velog.io/post-images/conatuseus/904dc610-1775-11ea-b432-675960e51cd4/image.png)

# start 명령으로 컨테이너 시작하기

`sudo docker start hello`

**docker start <컨테이너 이름>** 형식입니다. 컨테이너 이름 대신 컨테이너 ID를 사용해도 됩니다.

![image.png](https://images.velog.io/post-images/conatuseus/98448020-1775-11ea-b432-675960e51cd4/image.png)

hello 컨테이너가 시작된 것을 확인할 수 있습니다.

# restart 명령으로 컨테이너 재시작하기

OS 재부팅처럼 컨테이너를 재시작해보겠습니다

`sudo docker restart hello`

**docker restart <컨테이너 이름>** 형식입니다. 컨테이너 이름 대신 컨테이너 ID를 사용할 수 있습니다.

![image.png](https://images.velog.io/post-images/conatuseus/9d34d800-1775-11ea-bca6-193faaf8cc7a/image.png)

# attach 명령으로 컨테이너에 접속하기

이제 시작한 컨테이너에 접속해보겠습니다.

`sudo docker attach hello`

**docker attach <컨테이너 이름>** 형식입니다. 컨테이너 이름 대신 컨테이너 ID를 사용할 수 있습니다.

우리는 /bin/bash를 실행했기 때문에 명령을 자유롭게 입력할 수 있지만, DB나 서버 애플리케이션을 실행하면 입력은 할 수 없고 출력만 보게 됩니다.

Bash 셸에서 `exit` 또는 Ctrl+D 를 입력하면 컨테이너가 정지됩니다.

![image.png](https://images.velog.io/post-images/conatuseus/a1538800-1775-11ea-af24-bff57c817829/image.png)

# exec 명령으로 외부에서 컨테이너 안의 명령 실행하기

현재 컨테이너가 /bin/bash로 실행된 상태입니다. 이번에는 /bin/bash를 통하지 않고 외부에서 컨테이너 안의 명령을 실행해보겠습니다.

`sudo docker exec hello echo "Hello World"`

**docker exec <컨테이너 이름> <명령> <매개변수>** 형식입니다. 컨테이너 이름 대신 컨테이너 ID를 사용할 수 있습니다. 컨테이너가 실행되고 있는 상태에서만 사용할 수 있으며 정지된 상태에서는 사용할 수 없습니다.

컨테이너 안의 `echo` 명령을 실행하고 매개 변수로 "Hello World"를 지정했기 때문에 Hello World가 출력됩니다. docker exec 명령은 이미 실행된 컨테이너에 apt-get, yum 명령으로 패키지를 설치하거나, 각종 데몬을 실행할 때 활용할 수 있습니다.

# stop 명령으로 컨테이너 정지하기

이번에는 컨테이너를 정지해보겠습니다.

![image.png](https://images.velog.io/post-images/conatuseus/a84bbdd0-1775-11ea-af24-bff57c817829/image.png)

`sudo docker ps` 명령을 통해 hello 라는 이름을 가진 컨테이너가 실행 중인 것을 확인할 수 있습니다.

그리고 `sudo docker stop hello` 명령을 통해 컨테이너를 정지하고 다시 확인해보니 hello 컨테이너가 정지한 것을 알 수 있습니다.

**docker stop <컨테이너 이름>** 형식입니다.

# rm 명령으로 컨테이너 삭제하기

생성된 컨테이너를 삭제해보겠습니다.

`sudo docker rm hello`

**docker rm <컨테이너 이름>** 형식입니다.

![image.png](https://images.velog.io/post-images/conatuseus/ac3924a0-1775-11ea-af24-bff57c817829/image.png)

삭제 명령 후에 `sudo docker ps -a` 명령을 실행해보면 hello 컨테이너가 삭제된 것을 볼 수 있습니다.

# rmi 명령으로 이미지 삭제하기

이번에는 이미지를 삭제해보겠습니다.

`sudo docker rmi ubuntu:latest`

**docker rmi <이미지 이름>:<태그>** 형식입니다. 이미지 이름 대신 이미지 ID를 사용해도 됩니다.

**docker rmi ubuntu**처럼 이미지 이름만 지정하면 **태그**는 다르지만 ubuntu 이름을 가진 모든 이미지가 삭제됩니다.

![image.png](https://images.velog.io/post-images/conatuseus/b01c5240-1775-11ea-af24-bff57c817829/image.png)

<file_sep>/Article/2020-02/22.md
# 2020-02-25 토요일

## [이베이, 티켓 판매 이어 항목 광고 사업도 매각 추진](http://www.bloter.net/archives/372017)

- 대형 이커머스 업체인 이베이가 항목 광고(classified-ads) 사업을 매각하기 위한 행보를 시작했다. 현실화될 경우 매각 규모는 100억 달러 정도일 것이라고 월스트리트저널(WSJ)이 내부 사정에 정통한 소식통들을 인용해 2월21일(현지시간) 보도했다.
- 보도에 따르면 사모펀드인 TGP, 블랙스톤그룹, 그리고 전략적인 입찰자(strategic bidders)인 나스퍼스, 독일 출판 회사이 악셀 스프링거 등이 이베이 항목 광고 사업에 관심을 보였다고 <WSJ>은 전했다. 나스퍼스와 사모펀드인 KKR앤코가 지원하는 악셀 스프링거는 항목 광고 사업을 소유한 회사들이다.
- 이베이는 이들 회사 외에 다른 잠재 인수자들과도 접촉하기 시작했다고 <WSJ>은 덧붙였다.
- 이베이 항목 광고는 미국 크레이그리스트와 유사한 서비스로 사용자들이 상품과 서비스를 지역 커뮤니티에 올릴 수 있도록 하고 있다. 이베이는 지난해부터 분사 또는 합작사 설립 등을 포함해 항목 광고 사업에 대한 옵션들을 검토해왔다.
- 최근 이베이를 둘러싼 비즈니스 상황은 만만치 않다. 특히 핵심 사업인 마켓플레이스 부문이 아마존의 부상 속에 몇년전보다 입지가 약화됐다는 평이다. 이베이 현재 기업 가치는 300억 달러 규모다. 쇼피파이 같은 후발 이커머스 플랫폼 업체들과 비교해도 적은 수준이다.
- 최근 뉴욕증권거래소 모회사인 인터콘티넨탈 익스체인지(ICE)가 마켓플레이스 사업을 보고 이베이 인수를 타진했지만 주주들의 제동에 걸려 발을 뺀 것으로 전해졌다.
- 이런 가운데 이베이가 항목 광고 사업을 매각할 경우 핵심인 마켓플레이스 사업 부문에만 집중할 수 있게 된다. 지난해 이베이는 티켓 판매 회사인 스텁허브를 비아고고 인터테인먼트에 40억5천만달러에 매각했다.
- 이베이의 최근 행보는 행동주의 헤지펀드인 엘리어트 매니지먼트와 스타보드 밸류가 지난해 회사 지분을 확보하고 이사회에 참여한 것과 무관치 않다. 이들 업체는 이베이를 상대로 티켓 판매와 항목 광고 사업 매각을 검토할 것을 요구해왔다.

<file_sep>/Article/2020-02/05.md
# 2020-02-05

# [카카오페이, 증권업 진출…바로투자증권 인수 승인](http://www.bloter.net/archives/370074)

카카오페이의 바로투자증권 인수가 마무리 됐다. 지난 2019년 바로투자증권 지분 60%를 취득하며 대주주 변경승인을 신청한 지 1년여만이다.

금융위원회는 2월 정례회의에서 카카오페이의 바로투자증권 대주주 변경승인 신청을 승인한다고 의결했다.

승인 걸림돌이 될 것으로 보였던 김범수 카카오 의장의 공정거래법 위반 관련 형사소송은 이번 심사에 영향을 끼치지 않았다.

금융위원회는 “공정거래위원회의 의결내용과 법원의 1심 및 2심 판결내용을 볼 때 심사에 중대한 영향을 미치지 않는 것으로 판단해 중단되어 있던 심사업무를 진행하기로 지난 12월11일 결정했다”라며 “지배구조법령상 승인요건에 대한 금융감독원의 심사결과를 바탕으로, 카카오페이가 재무건전성, 부채비율, 대주주의 사회적 신용 등 법령상 요건을 모두 충족한다고 판단했다”라고 밝혔다.

카카오페이는 공정거래위원회 신고와 매매 대금 납입을 마치는대로 바로 증권사 업무를 시작할 수 있다. 본격적으로 증권업에 진출하는 셈이다. 사명으로는 ‘카카오증권’이 유력한 것으로 전해졌다.

카카오페이는 인수 작업이 마무리되면 카카오페이의 플랫폼 전문성·경쟁력과 바로투자증권의 투자·금융 포트폴리오가 가진 강점을 살려 투자 중개 및 다양한 금융상품을 선보일 것으로 보인다.

branch_name: refs/heads/master

repo_language: Markdown