깃플 스터디 프로젝트

스터디프로젝트 시작
깃플 신규 입사자에게 주어진다는 스터디 프로젝트.
그 험난(?)했던 2주간의 여정을 소개합니다.


시나리오

  • 사용자 인증

    • 회원 가입
    • 로그인
    • 로그아웃
  • 콘텐츠 관리

    • 콘텐츠 조회
    • 콘텐츠 등록
    • 콘텐츠 갱신
    • 콘텐츠 삭제
    • 전체 콘텐츠 키워드 5개 뽑기

주어진 기술 스택

  • Front

    • Angular 4 (feat. TypeScript)
    • Bootstrap 4
  • Server

    • Node.js + Express
    • mongoose
    • Node-restful
    • Passport.js + jwt
    • Lodash

( 프로젝트 코드는 여기에 있습니다. )

각 기술스택에 대한 특징과 소개는 먼저 입사하신 선배님이 잘 정리해놓은 글 이 있으므로 생략하기로 하고, 이 글에서는 직접 프로젝트를 하며 생각하고 느꼈던 점, 알게 된 점에 대해 편안하고 솔직히 써보겠습니다.


앵린이 현재 상황

  • Angular 4 : 전혀 모름. 컴포넌트 기반이라고 들음.
  • Typescript : 문서만 봤음
  • Node.js + Express : 써본적 있는 것 같은데 가물가물함.
  • mongoose : 마찬가지로 전생의 기억
  • Passport.js : 인증하는거(?)
  • jwt : Client side 에서 헤더에 넣어 날리는거다(?)
  • Node-restful : node도 알고 restful 도 아는데 이건 뭔지 모름.

스터디 프로젝트 시작.

Angular tutorial : 앵린이의 여행

공식 사이트의 Getting Started 를 읽어보고 무작정 튜토리얼을 따라해본다.
튜토리얼이 잘 되어있으니 폼 만들기까지는 쭉 따라해보라는 앵린이 담당 선임 개발자님.
(내부적으로는 PD님으로 호칭 통일되어 있습니다. 수평적 구조 🤗)
Tour of Heroes 에서 기본적인 문법과 Service, Navitation, 서버에서 데이터 가져오기 등을 배운다.
(*한글 문서도 있습니다)
Angular Tutorial 바로가기

Observable? 🧐

CLI 로 생성한 Angular 패키지에는 RxJS가 기본으로 포함 되어있고,
이 튜토리얼에서도 서버와 통신시에 RxJS 를 사용한다.
Observable 이라는 개념이 와 닿지 않아서 질문 찬스를 씀.
설명은 화이트보드에 그리면서 해야 제 맛

Observable을 구독(subscribe) 해두면, 구독 취소(unsubscribe) ( 혹은 error, complete 호출) 전까지 데이터가 전달되는 방식이다.
쉽게 이해하기 어려웠는데, 일단 이벤트 리스너와 비슷한 개념으로 생각하고 코드를 작성하며 확인해보기로 하였다.

1
2
3
4
5
6
7
8
9
10
11
12
13

postContent():void{
this.contentService.postNewContent(this.token,this.postingModel).subscribe(
result =>{
if(result?._id){
this.router.navigate(['/contents-list']); //redirect
}else{
window.alert("글이 등록되지 않았습니다. 다시 시도해주세요.")
}
}
)
}

클릭시 글을 등록하는 부분의 코드인데, contentsService 의 Observable 을 리턴하는 함수를 구독하고, 통신 결과가 오면 결과에 따라 리스트로 Redirect 시키거나 알림 창을 띄우도록 작성하였다.
(이렇게 그냥 넘어가려 했으나 Observable 은 이후에 다시 나오게 되는데…)

Global state 가 필요한 것 같은데요.. 🙄

튜토리얼에서 배운대로 신나게 프로젝트를 구성하고 각 페이지를 만든 것 까지는 좋았으나, 한 가지 문제에 직면하게 된다.
최상단 템플릿인 app.component.html 에 네비게이션과 router-outlet (라우터 모듈에서 설정된 페이지가 이 태그 자리에 들어가게 된다)를 모두 때려넣은 앵린이.
최상단 템플릿이라고 해도 라우터 태그에게 무언가 데이터(!)를 인자(!)로 넘겨줄 수 는 없는것이었다.
게다가 라우터에 설정한 페이지들끼리도 (최상단 템플릿(부모)의 아래에 있는, sibling 페이지들…) 서로 정보를 주고 받을 수 없는 상황.
로그인 페이지에서 로그인을 해도, 네비게이션에서는 로그인 여부를 알 수 없는것…

이럴 때 방법은

  1. 메뉴 구조를 바꾸기. 부모-자식 컴포넌트간에는 데이터를 넘겨 주고 받을 수 있다.
  2. 어느 컴포넌트에서도 접근 가능한 무언가의 저장소를 만들기.

Redux 의 store 를 생각하며 2번 방법을 궁리해보았다.
인터넷 세상에는 역시 비슷한 생각을 하는 사람들이 있는지, Redux 와 비슷한 구조로 Global state 를 관리 할 수 있는 모듈이 많이 있었다.
그렇지만 익숙치 않은 환경에 뭔가를 또 추가한다는것이 맘에 걸려 1안을 고민하던 중, 천사같은 선임 개발자분께서 또 묘안을 주심.
RxJS 의 Observable 을 사용하여 notify-subscribe 하면, 각자 컴포넌트에서 무슨 일이 일어났는지 알 수 있을거라고.
Gitple 실제 코드에서도 위와 같은 원리로 작성한 모듈을 사용하고 있었고, 그 코드 일부를 배껴(!)서 원하던 바와 비슷하게 구현하게 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

//login.component.ts

onSubmit():void{
this.userService.login().subscribe( (loginData:User) => {
...
this._state.notify('login',loginData);
...
})
}

//app.component.ts

this._state.subscribe('login', (userData) => {
...
this.userData = userData;
...
});

_state.notify 로 pub , _state.subscribe 로 sub (된다…! 👏 👏👏)

하지만 이렇게 사용 할 경우, pub 이 일어나야 sub 이 일어나는 특성상
온전히 Global store 처럼 언제나 저장된 그 상태를 가지고 올 수 있는 것은 아니므로 사용시 주의가 필요하다.

(*참고: 저는 state가 필요한 각 페이지의 onInit() 안에서 subscribe 해두었는데, 실제 그 페이지를 진입할 때 sub 하기 시작하므로 , 그 이전에 일어난 pub 에 대해서는 알 수 없었습니다. 잘못된 내용일 경우 수정하겠습니다.)

이렇게 해서 발급받은 token(jwt) 은 localStorage 에 고이 모셔두었다가,
사용자 인증이 필요한 활동(내 게시물 수정, 삭제, 새로고침시 다시 로그인하기 등)에 요긴하게 사용한다.

갑자기 나타난 Docker 🐳

여차저차하여 프론트단은 어느정도 마무리가 되고(물론 더미데이터로),
서버단 작업을 시작해야 하는 날이 되었다.
mongoDB는 자체적으로 제공해주는 서버에 업로드하는 형식으로만 사용해왔던 지난날..(무료 플랜 감사감사)
그럼 로컬에 설치해서 써보자며, 이왕 하는거 Docker Descktop 어플리케이션을 사용해서 만들면 아주 편하고 빠르게 만들 수 있다고 설치를 도와주셨다.
Docker 의 이미지와 컨테이너를 class 에 비교하여 설명해주신 흔적

실제로 Gitple 도 Docker 를 이용하여 안정적인 서비스를 하고 있다고 합니다 🌝

CRUD 한방에 해결해주는 Node-restful

비록 스터디 프로젝트이긴 하지만, 일반 업무 플로우와 동일하게 github 에 프로젝트 카드를 생성해주셨더랬다.
카드의 Server 란에 “node-restful” 이란 항목이 있었는데, 대체 무엇인가 여쭤봤더니 CRUD 한번에 만들어주니까, 찔러(!) 보면 알거라고 해주셨다.
정말일까? node-restful이 내 익스프레스에 대해 뭘 안다고 알아서 만들어주는걸까.
그런데 정말 모델을 만들고, endpoint 를 지정했더니 해당 url 의 CRUD (그리고 url/:id 의 CRUD 까지)가 가능해졌다.
비슷한 모듈이 이미 널리 쓰이고 있다는데, 유행에 뒤쳐져서 살아온건지, 이게 너무나 새롭고 신기했다. 이 날 좋아서 소리지르고 퇴근함.👾

똑똑한 요약러 node-summarizer 🧠

전체 콘텐츠 중 인기 top5 를 뽑는 미션이 있었는데, 여기에 사용할 모듈이 바로 node-summarizer 이다.
node-summarizer 의 요약 전략은 크게 2가지가 있는데, 빈도를 기준으로 하는것과, 텍스트 랭크를 기준으로 하는것이다.
인기 “단어”를 추출해야하기 때문에 문장 길이는 1로 설정하였고,
컨텐츠의 제목과 내용을 모두 모아서 요약처리 하였다.

1
2
3
4
5
6
7

{
summary: "", //String of the summary
sentence_list: [], //List of all of the tokenized sentences in the given text
nouns_and_adjactive_map: Map //Map of all of the sentences with the values being a list of nouns and adjactives in the sentence
}

위와 같은 결과를 돌려주는데, nouns_and_adjactive_map 의 텍스트 배열을 이용하여 top5를 추출하였다.
top5 페이지에 표시되는 frequency는 위의 nouns_and_adjactive_map 배열 내 등장한 빈도를 나타낸다.


Code Review + TODOS

스터디 프로젝트 완성 후, 코드 리뷰가 이루어졌다.
그동안 만들었던 기능을 시연해 보이면서 설명을 하는 시간을 가졌는데,
이렇게 나의 코드를 샅샅이(!) 공개하며 설명한 것은 처음이라 부끄럽기도 했다.
물론 리뷰를 했다고 끝이 아니다…
리뷰에서 드러난 누락된 기능과 몇 가지 모듈 추가하기, 그리고 잘 모르면서 사용했던 부분을 좀더 공부해보기로 하였다.
그 내용 중 하나인 Angular 의 Life cycle 에 대하여 정리한 내용을 아래에 붙여본다.😊(부끄…)
라이프사이클 설명과 흐름(글씨가 비뚤비뚤...😊)


프로젝트를 마치며

스터디 프로젝트에 주어지는 기간은 본래 2주인데, 리뷰 이후 수정까지 하면 2주를 훌쩍 넘긴 기간이 소요되었다.

사실 시작하기 전부터 많은 걱정이 앞섰다.
‘2주 지났는데 아무것도 한게 없으면 어떡하지’
‘지금 이거 궁금한데 너무 멍청하고 기초적인 질문이면 어떡하지’
‘나 이렇게 못하는데 합격시킨거 후회하시면 어떡하지(😭!!)’ …등등

이번 스터디 프로젝는 실제 서비스에 비하면 너무나 보잘것 없는… 간단한 프로젝트였다. (라고 썼지만 사실 매우 헤맸으며 오래걸림 😭… )
그렇지만 새로운 발견을 하는 재미도 있었고, 만들어진 화면이 동작하는 걸 지켜보는 뿌듯함도 있었다.

규모야 어쨌든 실제 서비스와 동일한 기술 스택을 이용하여 처음부터 무언가를 만들어 보는 경험은 중요한 것 같다.
아마 앞으로 업무에 적응하는데에도 도움이 많이 될 듯 하다.

더불어 엉뚱한 질문에도 한결같은 자상한 답변을 받고,
진심어린 격려와 조언(그리고 썰렁한 농담🤣) 을 들을 수 있었던 지난 며칠은 너무나 행복했다고! 꼭! 적어두고 싶다.
앞으로도 깃플에서 행복한 개발 라이프가 계속되길 바라며…🌷

공유하기