# 서울숲_M_Day15 리뷰 레포트 ## 참석자 - 김동균, 남정호, 이영훈, 이정규 ## 1. 코드 동작 이해 - Cashier에서 주문을 받고 각 클래스에 이벤트를 호출하여 값을 넘겨준다. - Manager에서는 BaristaPool을 가지고 Barista에게 작업을 전달하고 및 시작/완료 이벤트를 전달받는다. - DashBoard에 Manager가 시작/완료 이벤트에 따라 각 작업의 상태를 변경해준다. ## 2. 코드 동작 개선 - once의 활용 - 이벤트 리스너 중에서는 최초 실행 한번만 실행되면 더이상 의미가 없이지는 이벤트가 다수 존재한다, 예를들어, 바리스타의 인원을 설정해주는 이벤트는 프로그램 실행 중 두번 이상 실행 될 일이 없다. 따라서 이런 이벤트들을 모두 once로 설정해주는 것이 더 효율적일 것이다. - setImmediate를 이용한 비동기 처리 - 이전에는 setTimeout을 이용하여 비동기 처리를 실행해주었다. 하지만, 코드가 즉시 비동기적으로 실행되는 경우는 setTimeout(cb, 0) 보다 setTimmediate를 사용하는 것이 더 직관적으로 보인다. - Publish - Subscribe 패턴를 활용 - 이 패턴을 이용하면 Observer 패턴을 이용할 때 보다 보다 객체간의 결합도를 낮출 수 있다는 장점이 있다. - 한 객체가 다른 객체를 직접적으로 참조하는 Observer 패턴의 경우 관계 참여자들의 알고리즘을 조금만 바꿔도 생각지 못한 side effect가 발생할 가능성이 있다. 이는, 옵저버 패턴이 연쇄적으로 이어 졌을 때 더욱 심해지는데, 이를 방지하기 위해 Publish-Subscriber 패턴으로 리팩토링하는 것이 좋을 것 같다. ## 3. Consideration ### 스스로 확인할 사항 #### Node.js 이벤트 루프와 이벤트 처리(Event Emitter) 방식에 대해 학습하고 정리한다. #### 김동균 - event loop - call stack에 바로 쌓인게 아닌 경우 event queue에 쌓게 되며 call stack이 다 비워진 후에야 event queue의 pop()이 이뤄져야 한다. - pop()이 된 함수는 call stack에 등록이 된다. - 이렇게 event queue를 비우는 과정을 event loop라고 한다. - Event Emitter - 객체에 event listener를 등록 할 수 있게 해주는 아주 고마운 객체이다. - 내가 직접 작성한 객체에 Event Emitter를 상속을 해주면 사용이 가능하다. - 이벤트 등록은 다음과 같다. ```javascript= class Customer extends EventEmitter { constructor(name, cnt) { super(); this.name = name; this.cnt = cnt; this.once('complete',() => { console.log(`====${this.name} 손님 주문하신 음료 모두 나왔습니다~!`); }); } } ``` - 이벤트 발생은 다음과 같다. ```javascript= customers.emit('complete'); ``` #### 남정호 - 이벤트루프는 메인스레드 겸 싱글스레드로서, 비즈니스 로직을 수행한다. 수행도중에 블로킹 IO작업을 만나면 커널 비동기 또는 자신의 워커쓰레드풀에게 넘겨주는 역할까지 한다. #### 이영훈 - Event Loop - Event Queue를 살펴보며 Call Stack이 비었을 경우 하나씩 Calll Stack에 올려 실행한다. - Node Application은 single thread이기에 event loop는 오직 한개의 task와 event를 처리한다. - 따라서 I/O가 많은 작업에 적합하고 CPU 작업량이 많은 작업에는 적합하지 않다. - Event Emitter - Event Listener를 통해 Event를 등록하거나 emit()을 통해 호출할 수 있다. - Event Emitter는 모든 listener들을 등록된 순서에 따라 동기적으로 호출된다. - 따라서 비동기로 호출하기위해서는 로직 내부에서 setImmediate()이나 process.nextTick()를 사용해야한다. - 비동기로 할 경우 이벤트가 병렬적으로 모든 listner에게 notify하므로 작업처리가 더 빨라질 것이다. ##### [process.Tick()](https://blog.outsider.ne.kr/739) #### 이정규 - 이벤트 루프 - 이벤트 루프는 콜 스택이 비어있는지 확인하면서 비어있다면 이벤트 큐에 있는 함수를 콜 스택으로 쌓아준다. - Event Emitter - 이벤트를 내보내는 모든 객체는 EventEmitter 클래스의 인스턴스이다. 이 객체는 하나 이상의 함수를 이벤트로 사용할 수 있도록 이름을 넣어 추가하는 eventEmitter.on() 함수를 사용할 수 있다. - EventEmitter 객체로 이벤트를 호출할 때, 해당 이벤트에 붙어 있는 모든 함수는 동기적으로 호출된다. 호출을 받은 리스너가 반환하는 결과는 어떤 값이든 무시되고 폐기된다.   #### Day14에서 워커를 사용한 멀티 스레드 방식과 단일 스레드에서 비동기 병렬 처리 방식에 대해 비교해서 학습하고 정리한다. #### 김동균 - 스레드가 많으면 당연히 원활하고 쾌적하게 프로그래밍을 할 수 있다. - 하지만 스레드 하나를 늘리는 비용이 어마어마하다. - 최소한의 비용(스레드)으로 최고의 퍼포먼스를 내기 위해서는 효율적인 스케쥴링을 통한 비동기 병렬 처리 방식이 하나의 방법이 될 수 있다. - 적절한 갯수의 스레드와 비동기 병렬처리가 함께 이루어진다면 상당히 좋을 것 같다. #### 남정호 - 멀티 스레드 방식은 각각의 스레드가 하나의 일을 맡아서 수행한다. - 반면 비동기 단일스레드 병렬 방식은 하나의 스레드가 여러가지 작업을 돌아가며 처리한다. - 본 과제에서 사용한 방법은 round robin 스케줄링이다. 하나의 스레드가 여러 작업을 빠르게 번갈아가며 처리하면, 프로세스의 관점에서 보았을 때, 마치 동시에 처리되는 것 처림 보이기 때문에, 비동기 병렬 처리라 칭한다. #### 이영훈 - Multi Thread - thread가 여러개인 곳에서 병렬적으로 돌아간다. - Call Stack 실행절차에서 memory에 대한 고려가 필요하다. - Asynchronous Parallel Process - 처리가 오래걸리는 작업( ex) I/O )을 지연시킬 뿐 다른 작업들을 blocking하고 callback이 불려지길 기다리지 않는다. - 작업이 끝나면 알려주기에 다른 작업들도 병렬적으로 실행되어진다. - 기다리는 동안 Call Stack을 사용하지 않아 작업들이 많아질 수 있지만 그만큼 복잡도가 증가한다. ##### [ref](https://www.quora.com/What-is-the-difference-between-asynchronous-programming-and-multi-threading) #### 이정규 - 멀티 스레드 방식은 프로세스의 자원과 상태를 공유하여 효율적으로 운영이 가능하지만, 하나의 스레드만 실행중일 때는 실행시간이 되려 지연될 수 있다 또한 스레드를 계속해서 늘릴경우 성능에 문제가 생길 수 있다. - 단일 스레드에서 비동기 병렬 처리 방식은 멀티 스레드 방식과 다르게 한 스레드에서 작업을 비동기적으로 처리하여 스레드를 늘리지 않아도 멀티 스레드인 것처럼 보이게 할 수 있다. ### 다같이 확인할 사항 - Event-Bus 패턴에 대해 학습하고 주문 담당자, 매니저, 주문 대기표, 바리스타 관계를 비교한다. - 이벤트-버스 패턴은 주로 이벤트를 처리하며 이벤트 소스, 이벤트 리스너, 채널 그리고 이벤트 버스의 4가지 주요 컴포넌트들을 갖는다. 이벤트 소스는 이벤트 버스를 통해 채널로 메시지를 발행하며, 리스너는 특정 채널에서 메시지를 구독한다. 리스너는 이전에 구독한 채널에 발행된 메시지에 대해 알림을 받는다. - 이벤트 소스 : 주문 담당자 - 이벤트 리스너 : 바리스타 - 채널 : 주문 대기표 - 이벤트 버스 : 매니저 ##### [ref](https://mingrammer.com/translation-10-common-software-architectural-patterns-in-a-nutshell/#7-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%B2%84%EC%8A%A4-%ED%8C%A8%ED%84%B4-event-bus-pattern) - Publish-subscribe 패턴에 대해 학습하고, 이벤트 처리 대신 구조에 적용할 수 있는지 확인한다. - 출판사(Publisher)와 구독자(Subscriber)를 통해서 메시지를 주고 받는 형태다. - 객체 간 결합도가 낮아지는 장점이 있다. - 출판과 구독의 여부만 선언하면 되므로 확장과 수정에 용이하다는 장점이 있다. - 위의 장점들에서 나오는 단점으로는 직관적이지 못할 수 있다는 점이 있다. - 이벤트 처리의 발생,반응과 Publish-subscribe 패턴의 출판,구독이 유사한 모습을 띄고 있다. - 이를 통해 이번 async cafe에 Publish-subscribe 패턴을 적용 시킬 수 있을 것 같다. - 이벤트가 발생한 곳에서는 Publish를, 이벤트가 반응되는 곳에는 subscribe를 적용할 것 같다. - Publish-subscribe와 Observer 패턴의 차이 두 패턴의 가장 큰 차이점은 중간에 Message Broker 또는 Event Bus가 존재하는지의 여부이다. - Observer패턴은 Observer와 Subject가 서로를 인지하지만 Pub-Sub패턴의 경우 서로를 전혀 몰라도 상관없다. - Observer패턴에 비해 Pub-Sub패턴이 더 결합도가 낮다. Publisher와 Subscriber가 서로의 존재를 알 필요가 없기 때문에 당연히 소스코드 역시 겹치거나 의존할 일이 없다. - Observer패턴은 대부분 동기(synchronous) 방식으로 동작하나 Pub-Sub패턴은 대부분 비동기(asynchronous) 방식으로 동작한다다. - Observer패턴은 단일 도메인 하에서 구현되어야 하나 Pub-Sub패턴은 크로스 도메인 상황에서도 구현 가능하다.이 역시 Broker라는 중간 매개체가 있기 때문인데 어플리케이션의 도메인이 다르더라도 MessageQueue(Broker)에 접근만 가능하다면 처리가 가능하기 때문이다. - 만드는 데 4초가 걸리는 허브티와 15초가 걸리는 팥빙수를 추가하기 위한 구조와 코드를 토론한다. - 커피류만 담당하는 `Barista` - 커피외 음료만 담당하는 `Maestro` - 음식류만 담당하는 `Chef` - 이런식으로 담당 메뉴를 정해주면서 우리가 구현한 알고리즘 대로 일을 분배해준다면 원활한 카페의 운영이 가능할 것 같다. - `Maestro`,`Chef` class를 Manager 부분에서 관리를 하여 Worker라는 class를 정의하고 `Maestro`,`Chef`,`Barista`가 상속받게 하여 Publish-subscribe를 Manager와 Worker의 관계에 적용하여 관리한다. #### 출처 - https://mingrammer.com/translation-10-common-software-architectural-patterns-in-a-nutshell/ - Publish-subscribe pattern 에 대해...|작성자 CeilingMonad` - https://jistol.github.io/software%20engineering/2018/04/11/observer-pubsub-pattern/ ### 자신이 미션을 하면서 학습한 내용 #### 김동균 - event 처리가 돌아가는 방식을 정말 정확히 이해할 수 있는 미션이었다. - event를 발생시키고 그에 대한 반응으로 또 다른 event를 발생시켜 정말 필요할 때만 실행이 되도록 하는 방식이 마치 도미노를 보는 것 같았다. - 또 on과 once의 용도를 깨달은 뒤 사용하니 더 효과적이게 프로그래밍을 할 수 있었던 것 같다. - class extends를 처음으로 적용해봤는데 상속이란게 이렇게 간편하구나 라는걸 느꼈지만 상속에 대한 개념이 아직 부족한 것 같다. 전역변수 사용을 없애고 상속과 주입을 통해 프로그램을 구현해보면 더더욱 좋을 것 같다. #### 남정호 - Event Emitter - 비 동기적으로 메시지를 주고 받을 수 있는 Event Emitter를 학습하였다. - Express - 파일의 실시간 업데이트 - 다른 파일의 데이터를 가지고 오는 코드를 실행하여 main.js 파일에 존재하는 객체를 app.js에 불러올 수 있었다. ```javascript const dashboard = require('../../main.js'); ``` - 위 코드에서 require 구문을 통해 main.js 에 존재하는 dashboard 객체를 가져 올 수 있다. ```javascript router.get('/', function(req, res, next) { res.send(dashboard.board); }); ``` - get request를 받으면 위 코드가 동작하게 되는데, request를 받은 시간의 데이터에 접근하기 때문에 데이터의 변화를 실시간으로 적용할 수 있다. #### 이영훈 - Event Emitter를 통해서 각 클래스에서 상속받아 event를 추가하고 호출할 수 있다는 것을 알게되었다. - Express를 통해서 query로 내가 원하는 페이지를 구성할 수 있다는 것을 알게되었다. - Multi Thread와 Asynchronous Parallel Process이 다른방식으로 여러작업을 처리할 수 있다는 것을 알게 되었다. #### 이정규 - Event Emitter - 이벤트를 내보내는 모든 객체는 EventEmitter 클래스의 인스턴스이다. 이 객체는 하나 이상의 함수를 이벤트로 사용할 수 있도록 이름을 넣어 추가하는 eventEmitter.on() 함수를 사용할 수 있다. - EventEmitter 객체로 이벤트를 호출할 때, 해당 이벤트에 붙어 있는 모든 함수는 동기적으로 호출된다. 호출을 받은 리스너가 반환하는 결과는 어떤 값이든 무시되고 폐기된다. - 단일/멀티 스레드와 동기/비동기 - 싱글쓰레드 - 동기방식일 경우 한 쓰레드가 일을 처리하는데, 이 일을 다 처리할 때까지 다른 일을 처리하지 못하고, 기다리는 방식이다. - 멀티쓰레드 - 동기는 쓰레드를 필요한 만큼 할당하여 일을 처리하여 싱글스레드 동기방식처럼 기다리지는 않지만 스레드를 많이 생성하다보면 성능에 무리가 생긴다.