# 2021-02-02(Tue)

| 항목         | 내용                                                                                                 |
| ---------- | -------------------------------------------------------------------------------------------------- |
| 학습 날짜      | 2021-02-02(화)                                                                                      |
| 학습 시간      | 11:00\~24:00                                                                                       |
| 학습 범위 및 주제 | project 미팅, backbone, ActionCable                                                                  |
| 학습 목표      | appearance 유저 상태바를 만든다.                                                                            |
| 동료 학습 방법   | eunhkim, sanam, jujeong, yohlee님과 미팅을 진행하였다. sanam, jujeong님과 appearance\_view를 liveshare로 함께 구현함. |

## 상세 학습 내용

팀원들과 미팅을 진행했다. 아래 내용은 미팅하며 노션에 정리한 것들\[트렌젠던스] 본 프로젝트 킥오프 2차 미팅

## 1. 뷰에 대한 주요 용어 정리

* 컨테이너뷰
  * 기존의 메인뷰(MainView) 객체가 컨테이너뷰로 이름이 변경되었다.
  * 컨테이너뷰(#container-view) 안에서 교체되는 뷰들을 메인 뷰라고 명명한다.
  * 컨테이너 뷰에

    컨테이너 뷰는 메인 뷰들을 관리하는 영역 뷰이다. el을 #container-view로 가지고, 메인 뷰들을 렌더링시켜서 자신의 영역 안에 포함시킨다. 컨테이너뷰가 커렌트 뷰가 길드뷰인 상태에서 NavView의 토너먼트 버튼을 누르면 컨테이너뷰의 커렌트 뷰가 토너먼트 뷰 인스턴스로 대체된다.
* 뷰 엘리먼트와 뷰 객체
  * new View로 만들어지는 것은 뷰 객체이다. 뷰 객체가 렌더링하는 요소는 뷰 엘리먼트이다. 뷰 엘리먼트가 DOM에 추가되면 DOM 엘리먼트가 된다.
* 영구뷰
  * 영구뷰는 윈도우 상에서 한번 로딩된 후 윈도우가 종료될 때까지 사라지지 않는 뷰 객체를 일컫는 내부소통용 용어다.
  * 영구뷰는 AppView의 변수로 관리된다.
  * 영구뷰에 속하는 것은 하단 와이어프레임 좌측상단에 노란색 태그가 붙어있는 뷰 엘리먼트를 렌더링하는 뷰 객체이다.
  * 영구뷰는 정적 el을 가진다. (영구뷰의 el요소는 엘리먼트를 새로 생성해서 참조하는 것이 아니라, 기존 엘리먼트를 참조한다.)
  * default로 hide되어 있다가 필요한 순간에 render된다.
* 뷰가 정적/동적 el을 가진다.
  * 뷰가 정적 el을 가지는 경우는 엘리먼트를 새로 생성해서 el로 참조하는 것이 아니라, 기존에 이미 생성되어있는 엘리먼트를 el로 참조하는 것을 의미한다.
  * 뷰가 동적 el을 가지는 경우는 el요소 없이 새로운 엘리먼트로 생성되어서 DOM에 삽입되는 것을 의미한다.
* 사이드바 / 챗룸사이드바
  * 사이드바는 물리명이 아니라 논리명이다. AppearanceView와 MainButtonsView가 렌더링되는 영역을 논리적으로 가리킨다. 변수명으로 쓰이지는 않는다.
  * 챗룸의 좌측에 렌더링되는 유사 사이드바는 챗룸 사이드바로 부른다.
* 어피어런스 뷰
  * 어피어런스 뷰는 유저의 목록과 접속 상태를 관리한다. 클릭시 유저에 대한 메뉴를 렌더링한다.
* 싱글뷰 / 멀티뷰
  * 뷰 객체가 다른 뷰 객체를 가지고 있지 않을 때 싱글 뷰라고 한다. 반대로 하나의 뷰 객체가 여러 뷰 객체를 함께 렌더링해야 하는 경우 멀티뷰라고 명명한다.
  * ex) SignInView = 싱글메인뷰, DirectChatView = 멀티영구뷰
* 루트 뷰와 차일드 뷰
  * 메인 뷰는 루트 뷰와 차일드 뷰로 구분된다. 루트 뷰란 메인 뷰가 자기 자신을 가리키고, 차일드 뷰는 변수로서 메인 뷰에 속하는 뷰들을 의미한다.
* 종속 뷰와 쉐어 뷰

  종속뷰는 특정 메인 뷰에만 속하는 차일드뷰이고, 쉐어뷰는 여러 메인 뷰에서 쓰이는 차일드뷰이다.
* 프론트 모델과 백 모델
  * 모델이라는 단어가 백본과 레일즈에서 혼용되므로 혼란을 막기위해 용어를 구분한다.
  * 백본 모델은 프론트 모델, 레일즈 모델은 백 모델

1. **sign** : 정상적으로 회원가입 및 로그인, 로그아웃이 가능하다.
2. **appearance** : 친구추가가 가능하고, 친구 및 online 상태인 유저들의 접속상태를 조회할 수 있다. 마우스를 유저 위에 올렸을 때 프로필 뷰가 호버링되어야 한다. 클릭했을 때 서브 메뉴가 떠야 한다. 유저 메뉴들 중 유저정보 버튼은 유저 인덱스 메인 뷰로 라우팅되어야 하고, 유저 차단하기 버튼을 눌렀을 때에는 서버상에 Ban 데이터가 생성되어야 한다. 필요하다면 front에서 Ban 정보를 보관해야 한다.
3. **user\_index** : 유저 인덱스 뷰를 조회할 수 있고, 자신의 인덱스 뷰(홈 메뉴를 클릭하여 이동할 수 있는 뷰)에서는 name 및 프로필 이미지 변경이 가능해야 한다. MatchHistory는 정상적으로 보여줘야 한다. GuildInviation과 관련해서는 들어온 초대가 없을 경우의 페이지를 렌더링한다. 다른 유저에 대한 인덱스 뷰에서 길드 초대 버튼은 존재하되 동작하지 않는다.
4. **persist(front only)** : info, input, alert 모달 뷰를 작성하여 필요하다면 다른 뷰에서 사용할 수 있도록 한다. error main view를 작성하여 문제가 발생할 경우 route될 수 있도록 한다. 기타 필요한 영구 뷰들을 작성한다.
5. **game(ladder)** : ladder 게임을 구현한다. nav bar에서 ladder 버튼을 눌렀을 때 game index view로 이동한다. 매칭 후 게임을 진행한다. 경기 규칙은 클래식으로 진행된다. 유저가 이탈하는 경우에 대해서 적절하게 처리한다. game index view안에 게임 규칙(info modal view)을 볼 수 있는 버튼이 있어야 한다. 게임 승패에 대한 보상 포인트 결정 알고리즘(=래더링 시스템)이 작성되어야 한다. 게임은 중계 기능을 고려하여 확장성 있게 작성해야 한다.
6. 위의 사항들에 대해 로그아웃 및 로그아웃을 반복하더라도 정확하게 동작한다. 점진적 기능 추가에 대비해 수정에 닫혀있고 확장에 열려있도록 코드를 작성한다.
7. 채팅(DM, 그룹)과 기본적인 게임(듀얼, 래더)이 가능한 서비스를 먼저 완성한다. 영구 뷰, 로그인 및 회원가입과 chat, userIndex, gameIndex 등의 메인 뷰가 포함된다.
8. 게임 중계 기능을 포함한다.
9. 토너먼트를 추가한다.
10. guild, war와 관련된 기능들을 추가한다.
11. admin을 추가한다.

## 2. 뷰 설계에 대한 컨벤션 정리

* *(뷰 성격에 따른 close 메서드의 책임, fetch에 따른 callback 메서드 분리, 모델 접근에 대한 attributes와 get 사용, ...)*

  **디렉토리 구조**

  * 템플릿 및 뷰 디렉토리 구조를 통일한다. 뷰 폴더의 뷰 파일과 같은 템플릿 폴더의 위치에 해당 템플릿 파일이 있는 것이 보장되어야 한다.
  * 루트에 App.js, router.js, Helper.js와 같은 파일들이 있다.
  * Chat, Guild, Admin와 같이 모듈 단위로 폴더를 생성하고 해당하는 메인 뷰와 종속 뷰만 관리한다.
  * 여러 모듈 사이에서 공유되는 뷰들은 share 폴더에 관리한다.
  * 영구 뷰들은 따로 persist 폴더에 관리한다.
  * 모듈을 맡았을 때에 그 모듈의 책임자가 자신의 책임을 수행한다는 것은 서비스를 아무리 이용해도 정확하게 동작하는 것, 메모리 누수를 발생시키지 않는 것이다. 그 과정에서 액션 케이블을 사용한다면 필요한 순간에 적절하게 채널에 연결하고 해제하는 것이다. 로그아웃 버튼을 눌렀을 때, 서비스 안에서 새로고침을 눌렀을 때, 서비스 안에서 뒤로가기를 눌렀을 때, 브라우저 탭을 종료했을 때, 브라우저에서 다른 웹서비스로 이동했을 때, 브라우저 자체를 종료했을 때에도 자신이 담당한 영역에 문제가 발생하지 않고, 정상적으로 유저가 로그아웃을 했을 때와 같은 결과를 보장하는 것이다.
  * initialize에 최대한 많은 것을 넣지 않고 render 메서드 내에서 차일드 뷰를 생성한다. render 메서드가 연속적으로 호출되는 경우를 고려해서 설계되어야 한다.

  **뷰 el 정의 (동적 vs 정적)**

  * 영구 뷰 : 영구 뷰가 삽입될 영역은 index.html.erb 안에 \<div id="">\</div> 태그로 보장된다. 따라서 **정적 el**을 사용하여, hide했다가 필요할 때 렌더링하는 구조로 사용한다.
  * 메인 뷰 : 메인 뷰가 삽입될 영역은 index.html.erb 안에 \<div id="container-view">\</div> 태그로 보장된다. 하지만, 메인 뷰는 교체되면서 삭제되기 때문에 container-view 태그를 보존하기 위해서 **동적 el**로 생성하여 삽입되어야 한다.
  * 일반 차일드 뷰와 리스트 차일드 뷰 : 부모 뷰의 템플릿에 해당 뷰가 들어갈 태그가 보장되어 있어야 한다. setElement를 이용하여 html()로 삽입한다.
  * 엘리먼트 차일드 뷰(리스트 차일드 뷰에 들어가는 요소): 리스트 차일드 뷰 내부 요소로 append를 하는 것이 자연스러우므로, 동적으로 el을 생성하여 부모 뷰의 el에 append로 삽입한다.

  **close 메서드의 책임**

  * 모든 뷰에 close를 메서드를 구현하자. 뷰의 close 메서드가 기본적으로 져야 할 책임은 자신에게 종속된 모든 차일드뷰의 close를 메서드를 호출하고, 자신이 on한 eventListener를 해제하고, 자신을 remove하는 것이다. 모델에 대해서는 listenTo를 사용하고, DOM 요소에 대해서는 최대한 events 해시를 이용하여 누수 위험이 있는 on 사용을 자제한다.
  * 영구 뷰의 close 메서드는 자식 뷰들에 대해 close를 호출하지만 자신을 remove하지는 않는다. 대신 자신을 대상으로 hide()를 호출시켜 외관상 모습을 감춘다.
  * 채널을 가진 뷰들은 close 메서드에 대해 자기가 관리하는 채널에 대해 disconnect하는 것을 책임진다.

  **서브 뷰 생성 위치**

  * **기본적으로 render 메서드 내에서 새롭게 서브 뷰를 생성한다.**
  * render 메서드가 여러 번 연속적으로 호출될 가능성이 있을 경우 view 중복으로 인한 문제가 발생하므로 이에 따라 적절히 처리한다.

  **채널 생성 위치**

  * 기본적으로 채널을 관리하는 뷰에 채널을 할당한다.
  * 노티피케이션 채널은 앱 객체에서 관리한다.

  **fetch에 따른 callback 메서드 분리**

  * fetchContainer에 대한 then의 코드가 길어져 가독성을 해칠 경우 successCallback과 failCallBack을 따로 정의하여 메서드 실행시 전달한다.

  **모델 접근**

  * model의 경우 back db에 구현된 컬럼은 `get`과 `set`을 이용하여 `attributes`를 제어한다.
  * back db에 구현되지 않은 컬럼은 `dot(.)`을 이용하여 제어한다.

  **internal.js**

  * internal.js의 App에 Model, Collection, View, Channel 메서드 등 파일로 구현된 모든 객체를 등록한다.f

  **Helper**

  * fetchContainer 메서드를 fetch로 변경한다.

## 3. 디스코드 채널 정리

백업 카테고리를 만들어서 불필요한 채널들을 몰아놓고, 의논하여 꼭 필요한 채널만 남기고 생성해봅시다.

**꼭 필요한 채널**

* **랜덤, 아무말**
* **liveshare**
* **환경 링크, 프론트 링크, 백 링크, 일반 링크**
* **announcement**(emoji를 이용한 read 표현 필수)
* **회의때말해요(제안 및 이슈)**
* **개인일정**
* **github**

  *(backup 폴더 생성 및 불필요한 폴더 몰아넣기, 의논하여 꼭 필요한 채널만 남기고 생성하기, 각 음성 채널 사용 용도 명확히 하기, ...)*

### **4. 회의 주기 및 시간**

* (권장) 데일리 스크럼(10\~15분) 11시
* (필수) 안건을 다루는 회의는 화, 금 오후 2시

### **5. 프로젝트 보드 카드 생성 및 개발 방식 결정**

*(카드 섹션 생성, 카드를 섹션 단위로 이동하는 타이밍, 코드 리뷰 방식, 필요한 라벨 생성, ...)*

* 백로그에 개발 계획을 정리해놓고, 화요일 회의에서 1주일간 구현할 영역의 카드들을 생성한다.
* 해당 주차에 해결하는 것이 좋다고 판단하는 문제들은 not started 섹션에 카드로 등록하고, 언제 해결할지 상의가 따로 필요하다고 판단하거나 급하지 않은 문제들은 backlog에 남긴다.
* 이슈는 issues 탭에서 템플릿을 이용하여 등록한다. project를 설정해야만 대시보드에 등록되기 때문에 까먹지 않고 설정하도록 한다.
* 카드를 만들 때에는 체크리스트를 사용하여 구현항목을 명세하면 좋겠다. 가급적 완료된 것들은 체크하여 해당 카드의 진척도를 서로 공유할 수 있도록 하자.
* 완료한 카드는 close하여 completed에 모은다. 이후 문제가 발생할 경우 reopen하여 not started로 끌어온다. 급한 이슈가 아니라면 backlog에 기록하여 reopen시기를 조율한다.
* **bug, feature, refactor, front, back, extra(회색)** 라벨을 사용한다.
* **모듈명**은 제목 접두사로 포함한다. ex) \[guild] 길드리스트 구현
* 리뷰방식

### **6. 깃허브 브랜치 및 merge 전략**

develop 브랜치의 최신화 상태에 대해 항상 숙지해야 한다. 그래서 현재 자신이 개발하고 있는 브랜치와 충돌 지점이 발생하고, 그 해결 방식이 자신의 모듈 범위에서 해결할 수 없는 문제일 때에는 이슈를 레이즈해야 한다. 그 외의 이슈에 대해서는 그 때 그 때 develop 브랜치를 pull 받아서 동기화하는 것을 권장하지만, 개인의 영역으로 남겨둔다.

통합 브랜치

* main: 개발이 끝나고 배포가능한 브랜치
* develop: 개발이 진행 중이며, 가장 최신 브랜치

토픽 브랜치

* 모듈의 메인 브런치는 **모듈명(guild)**&#xC73C;로 표현한다. 이슈 브랜치는
* 모듈명\_라벨명#이슈넘버
* 의 형식으로 브랜치로 네이밍한다.
* * master, develop, guild, guild\_feat#23, guild\_fix#21
  * develop
  * **guild**
  * **guild\_feat#23**
  * **guild\_fix#21**
  * *(브랜치 네이밍, 완성된 기능을 develop 브랜치로 merge할 때 현재는 물론 차후 충돌 이슈가 없을지 개발중인 다른 모듈 브랜치와 비교하기 등)*
  * * 리뷰 & merge 권한
    * 1. 모듈 브랜치의 merge에 대해서는 모듈 작업자간의 리뷰
      2. 완성된 모듈 브랜치를 develop 브랜치로 merge하는 과정에서 모든 사람의 리뷰
      3. 모든 사람이 approve한 경우에 merge하고, 마지막으로 approve하는 사람이 merge할 수 있다. pending이 있을 경우 의견을 존중하여 문제를 해결하고, 의견 차이로 인해 문제가 해소되지 않은 경우에만 그 모듈에 대해 책임을 가진 사람이 결정한다.

### **7. 특정 영역에 대한 멤버별 책임 검토**

*(모델, 컨트롤러, 퍼블리싱, 백본 비즈니스 로직, 백본 렌더링 & 클로즈, QA...)*

* 레일즈

  모델 & 시드 & 액티브 어드민

  * 유효성 검사가 모델 차원에서 1차적으로 진행되어야 한다.
  * 데이터베이스에 접근하는 회수를 줄인다.
  * 즉시 연관 로딩(eager loading)을 잘 사용한다.
  * collection get 요청이 backend model의 인스턴스를 그대로 주는 것으로 해결할 수 없는 문제들이 대다수이다. 일부 컬럼만 요구하거나, 연관된 다른 모델의 데이터까지 함께 요구하는 경우가 많다. to\_sym, to\_detail과 같은 방식으로 front API에 효율적으로 대응하기 위한 serialize 방법이 필요하다.
  * **\*iwoo, jujeong\***
  * 템플릿과 DOM 요소에 부여되는 클래스, ID, CSS를 관리하는 퍼블리싱
    * **\*eunhkim\***
    * 백본 렌더링 & 클로즈, 백본의 전체적 아키텍쳐 관리
      * **\*eunhkim\***
      * 환경/빌드
        * docker-compose를 이용해서 up, down, start, stop, run을 할 때 원활하게 작동하지 않는 문제들에 대한 솔루션을 제공해야 한다. bundle, gem, webpack에 대한 이슈들 역시 같이 담당한다. 평가할 때 docker-compose up을 했는데 안 된다? ....
        * **\*sanam, yohlee\***

### **8. 프로젝트 카드 셋팅유저 상태바 구현**

라우터타고 가는 페이지 단위로 구현하는게 서로 영향을 주지 않을 것이다. (수정에서 닫혀있음..)

그 외에 이슈카드를 생성할 때 일관된 스타일로 생성할 수 있도록 템플릿을 만들어서 팀원들에게 공유하였다.

## 유저 상태바 구현

이후엔 sanam, jujeong과 apperance 유저 상태바를 만들었다.

백본 뷰에 백본 모델을 넣어서 템플릿에 적용하고 싶을 경우, 아래처럼 해시에 넣어서 생성해야한다.

```
this.online_user_unit = new App.View.UserUnitView({ model: user });
```

rails에서 strongParameter에 배열을 넘기기 위해서는 [이 링크를 참고](https://stackoverflow.com/questions/16549382/how-to-permit-an-array-with-strong-parameters) 하자.

포인트는 아래 두가지다.

배열을 가지는 parameter가 permit 메소드의 가장 마지막에 위치해야한다. 아니면 array 키 해시로 아래처럼 적어줄 필요가 있다.

```
params.permit(:foo, {array: [:key1, :key2]}, :bar)
```

아래 예시를 참고하자.

```
Parameters: {"link"=>{"title"=>"Something", "time_span"=>[{"start"=>"2017-05-06T16:00:00.000Z", "end"=>"2017-05-06T17:00:00.000Z"}]}}
```

클라이언트가 위처럼 parameter를 보낸다고치면, 아래처럼 작성해줘야한다.

```
params.require(:link).permit(:title, time_span: [[:start, :end]])
```

## **학습 내용에 대한 개인적인 총평**

미팅도 여러번 진행하다보니 진행이 점점 빨라지는 느낌이다. 이제사 모듈별 분기를 나눌 수 있는 요건을 다 갖춘 느낌이 든다.

한편 setElement에 대해서 시행착오를 겪어놓은 상태였기 때문에, 프론트 진행이 원할했다. 이벤트 핸들링이 좀 복잡한 느낌이 있는데 따로 고민해봐야겠다.

## **다음 학습 계획**

* AppearanceView 구현 완료
