Prototype 이제는 이해하자

prototype은 왜 어려울까?

C++, Java와 같은 클래스 기반 객체지향 언어와 달리 자바스크립트는 프로토타입 기반 객체지향 언어입니다. 프로토타입을 사용하여 객체지향을 추구하기 때문에 자바스크립트를 사용함에 있어 프로토타입을 이해하는 것은 중요합니다. 최근 ECMA6 표준에서 Class 문법이 추가되었지만 C++, Java에서 말하는 클래스가 아닌 프로토타입을 기반으로 하여 만들어진 문법입니다.

자바스크립트의 프로토타입을 처음 공부하면서 prototype, [[prototype]], _proto_, 객체, 함수, prototype chain 과 같은 용어들을 접하게 되는데 공부할수록 서로 뒤엉켜지고, 모르는 것도 아닌 그렇다고 제대로 알고 있는것도 아닌 어중간한 상태가 됩니다.

자바스크립트를 사용한 경험이 있으시다면 아래의 코드와 같은 형태를 경험한적이 있으실겁니다. 지금부터 아래의 코드가 어떤 원리로 동작하게 되는지 알아보겠습니다.

prototype에 property 추가

먼저 프로토타입에 대해 이해하기 위해서는 객체(object)는 함수(function)로부터 시작된다라는 것을 알아야 합니다. 이는 prototype을 이해하는데 많은 도움을 줍니다.

객체(object)는 함수(function)로부터 시작된다

자바스크립트에서 primitive를 제외하고는 모두 객체(object)입니다.
앞으로 등장하는 Object와 Function은 function(즉, 생성자)입니다. object는 객체를 의미합니다.

다음의 코드를 분석하기 전 객체(object)는 함수(function)로부터 시작된다라는걸 다시한번 기억하겠습니다.

1
2
function Book() { }                // 함수
var jsBook = new Book(); // 객체 생성

위의 코드에서 Book이라는 함수를 통해서 jsBook이라는 객체를 생성했습니다. 이때 Book 함수를 생성자라고 합니다. 생성자는 새로 생성된 객체를 초기화하는 역할을 합니다. 코어 자바스크립트는 기본 타입에 대한 생성자를 내장하고 있는데 이는 다음 코드를 통해 확인이 가능합니다.

1
var cssBook = {};                  // 생성자 선언 없이 객체 생성

위에서는 리터럴 방식을 사용하여 객체를 생성하였습니다. 리터럴 방식 또한 결과적으로는 함수를 통하여 객체를 생성하게 됩니다. 자바스크립트 엔진이 해당 리터럴을 다음과 같이 해석합니다.

1
var cssBook = new Object();        // 객체 생성

따라서 결과적으로는 리터럴 방식으로 객체를 생성할때도 Object라는 함수(생성자)를 통해서 객체를 생성하게 됩니다. Object 뿐만 아니라 Array, Function, Date, RegExp 모두 함수입니다.

Object, Array, Function, Date, RegExp - 모두 함수(function)이다

배열도 객체이기 때문에(자바스크립트 배열은 객체의 특별한 형태입니다. 프로퍼티 이름이 정수로 사용되며, length 프로퍼티를 가집니다.) 객체를 생성할때와 마찬가지로 배열(객체)의 생성에도 함수가 관여하게 됩니다. 따라서 무심코 사용했던 배열의 리터럴 표현도 결국에는 자바스크립트 엔진이 다음과 같이 해석합니다.

1
2
3
var books = ['html', 'css', 'js'];                  // 배열(객체) 생성
// 엔진이 다음과 같이 해석합니다.
var books = new Array('html', 'css', 'js'); // 배열(객체) 생성

이제 객체(object)는 함수(function)로부터 시작된다라는 것을 알 수 있습니다.

함수(function) 생성시 발생하는 일

객체(object)는 함수(function)로부터 시작되기 때문에 사용자가 객체를 생성하기 위해 먼저 함수를 정의하게 됩니다. 이때 발생하는 일에 대해 알아보겠습니다. 여기서는 2가지를 기억해야 합니다.

1.함수를 정의하면 함수가 생성되며 Prototype object가 같이 생성 됩니다. 생성된 Prototype object는 함수의 prototype 속성을 통해 접근할 수 있습니다. (Prototype object같은 경우 함수 생성시에만 됩니다. 일반 객체 생성시에는 생성되지 않습니다.)

함수 prototype property

2.함수의 생성과 함께 생성된 Prototype objectconstructor__proto__를 갖고 있습니다. (cover property를 추가한것 처럼 사용자 임의로 추가 가능합니다.) constructor는 생성된 함수를 가리키며(여기서는 function Book을 가리킵니다.) **_proto_**는 Prototype Link로서 객체가 생성될 때 사용된 생성자(함수)의 Prototype object를 가리킵니다. Prototype Link는 뒤에서 자세하게 알아보겠습니다.

함수와 함께 생성된 Prototype object

다이어그램을 통해 확인하면 다음과 같습니다.

함수와 Prototype object의 관계

객체(object) 생성시 발생하는 일

이번에는 객체 생성시 발생하는 일에 대해 알아보겠습니다. 조금 전에 정의한 Book 함수(생성자)를 사용하여 jsBook이라는 객체를 생성해 보겠습니다.

Book 함수로 생성한 객체

생성자(함수)의 몸체 부분에 어떠한 코드도 작성하지 않았는데 이를 통해 생성한 jsBook 객체가 __proto__라는 프로퍼티를 갖고있습니다.

여기서 _proto_Prototype Link로서 **객체의 생성에 쓰인 생성자 함수의 Prototype object**를 가리키고 있습니다. 그렇기 때문에 Book 생성자 함수와 함께 생성된 Prototype object에 추가한 cover라는 프로퍼티가 보이는것을 확인할 수 있습니다.

조금 더 이해하기 쉽게 다이어그램으로 확인하면 다음과 같습니다.

함수, Prototype object, 객체의 관계

다이어그램에서도 확인할 수 있다시피 prototype property(함수 생성시 함께 생성된 Prototype object를 가리킴)는 함수객체만 가지며 __proto__는 객체라면 모두 갖고 있습니다.

이제 프로토타입 체인(Prototype Chain)에 대해 이해할 수 있는 준비가 되었습니다.

프로토타입 체인(Prototype Chain)

결론부터 말씀드리면 프로토타입 체인은 객체의 property를 사용할때 해당 property가 없다면, __proto__ property를 이용해 자신의 생성에 관여한 함수(생성자 함수)의 Prototype object에서 property를 찾습니다. 만약 Prototype object에도 해당 property가 없다면 다시 Prototype object의 _proto_ property를 이용해 Prototype object에서 property를 찾습니다. 이렇게 계속 반복이 이루어지며 해당 property를 찾게 된다면 값을 반환하고 찾지 못한다면 undefined를 반환합니다. 이렇게 __proto__ property를 통해 상위 프로토타입과 연결되어 있는 형태를 프로토타입 체인(Chain)이라고 합니다.

프로토타입 체인에 대해 알게되었으니 다시한번 처음 코드를 살펴보겠습니다.

프로토타입 체인 예제

이제 어떻게 jsBook에 cover라는 property를 추가하지 않았는데도 결과가 출력되는지 이해할 수 있습니다. 다음과 같이 동작할 것입니다.

프로토타입 체인 예제(cover property 찾는 과정)

또한 다음과 같이 프로토타입 체인의 최상위는 Object이기 때문에 Object.prototype의 property들을 모두 사용할 수 있습니다. 자주 사용하는 toString()과 valueOf() 모두 Object.prototype에 선언되어 있습니다.
(Book Prototype object는 객체이기 때문에 Object 생성자가 사용될 것입니다. 따라서 Book Prototype object의 **_proto_**는 Object Prototype object를 가리키게 됩니다.)

프로토타입 체인의 최상위 object
Object.prototype

Prototype object__proto__ 그리고 프로토타입 체인에 대해 이해하였으니 다음과 같은 코드도 이해할 수 있습니다. 잘 이해가 되지 않는다면 위의 다이어그램을 참고해보시기 바랍니다.

__proto__와 Prototype object의 관계

번외

프로토타입 체인의 최상위 object
혹시 다이어그램을 보면서 function Book의 _proto_ 는 무엇을 가리키고 있는지 궁금해 하셨을 분들을 위해 추적해보았습니다.

function(생성자)의 __proto__ 추적

다음 코드를 도식화 하면 다음과 같은 다이어그램이 나오게 됩니다.

function(생성자)의 __proto__ 추적

간단하게 포스팅을 하려했는데 주제가 주제인지라 길어졌습니다. 저도 프로토타입을 처음 공부하면서 어려움을 많이 겪었는데 조금이나마 도움이 되었으면 좋겠습니다.

1) 에밀리 개발 시작하기

Index

  1. 들어가기 앞서
  2. 인천대학교-에밀리?
  3. 에밀리와의 대화방식
  4. 현재의 에밀리
  5. 앞으로의 에밀리
  6. 마치며

## 들어가기 앞서 기술 블로그의 좋은 글과 책을 통해 배우는 것도 많지만, 개인 프로젝트를 하며 배우는 점도 많은것 같습니다. 이번에는 퇴근 후 저녁시간과 주말을 이용해 진행했던 [인천대학교-에밀리](http://plus.kakao.com/home/@인천대학교-에밀리) 프로젝트에 대해 포스팅하려 합니다. 이전에도 여러 개인 프로젝트를 해왔지만 대부분 중도 마무리되거나, 완성 후 실제적으로 운영하지는 않았습니다. 아무래도 완성을 해도 이용자가 없다보니 **내가만든걸 누군가가 사용한다는 기쁨**을 맛보지 못해 그만두었던것 같습니다. 그래서 그동안 개인 프로젝트를 하는데에는 공부했던 것을 토대로 무언가를 만들어보자는 목적이었다면, 이번에는 **사용자의 범위를 최대한 좁혀 모든 기능을 특정 사용자에 맞추고 두고두고 사용할 수 있는 그런 서비스**를 만들어 보고싶었습니다.

카카오 플러스친구 - 웨더뉴스

위의 사진은 카카오 플러스친구인 웨더뉴스에서 매일 오전 날씨정보를 제공해주는 모습입니다. 하루 한번 날씨 정보를 제공해주는데, 글을 작성하는 2017-03-10 기준으로 531,980명이 이용하고 있습니다. 날씨를 제공해주는 서비스는 이전부터 많이 제공되고 있었지만 제일 사용빈도가 높은 카카오톡 서비스와 결합하다 보니 더 사용자들이 많이 찾는것 같았습니다. 저 또한 굳이 인터넷 검색이나 다른 앱을 실행하지 않고도 정보를 얻을 수 있다는 점에 가장 큰 가치를 두고 사용하고 있었습니다. 이점이 큰 메리트라고 느꼈고 이를 토대로 인천대학교-에밀리 프로젝트를 기획하고 개발하게 되었습니다.


## 인천대학교-에밀리? 왜 하필 에밀리일까요? [네이버 웹툰 - 신의탑](http://comic.naver.com/webtoon/list.nhn?titleId=183559&weekday=mon)을 보면 다음과 같이 에밀리가 등장합니다.

신의탑 에밀리

신의탑 웹툰에서 에밀리는 사용자가 궁금해 하는것을 모두 알려줍니다. 이러한 에밀리의 특징과 사용자의 범위를 최대한 좁히기 위해 제가 다녔던 인천대학교의 학생을 사용자로 맞추어 인천대학교-에밀리를 개발하게 되었습니다.


## 에밀리와의 대화방식 `인천대학교 학생들을 위해 필요한 정보를 쉽게 제공해준다.`라는게 이번 서비스의 목표입니다. 후에 다시 한번 알아보겠지만 카카오톡의 플러스친구/옐로아이디는 `자동응답 API`를 제공하는데 이를 통해 `(1)사용자가 직접 메시지를 입력` 혹은 `(2)버튼을 통해 객관식으로 정해진 내용을 입력`하도록 할 수 있습니다. 사용자가 쉽고 빠르게 정보를 얻을 수 있게 하기 위해서는 메시지보다는 버튼을 통해 사용자가 이용할 수 있는 기능을 알려주고 직접 선택 할 수 있도록 하는게 편리하고 빠르다는 생각에 버튼 방식으로 개발하기로 하였습니다.
## 현재의 에밀리 현재 에밀리는 다음과 같은 사용 가능합니다. `열람실`, `메뉴`, `날씨` 정보를 제공하고 있습니다.
# 사용법 (에밀리의 간단한 사용방법을 알 수 있습니다.)
![인천대학교-에밀리(사용법)](/images/post/2017-03-10/emily-use.jpeg)
# 열람실 (실시간 지하 도서관의 열람실 정보와 좌석도(링크)를 제공합니다.)
![인천대학교-에밀리(사용법)](/images/post/2017-03-10/emily-readingroom.jpeg)
# 메뉴 (학생식당, 기숙사식당 등... 교내 식당의 당일 메뉴를 제공합니다.)
![인천대학교-에밀리(사용법)](/images/post/2017-03-10/emily-menu.jpeg)
# 날씨 (어제와 오늘 인천대학교 날씨 정보를 제공합니다.)
![인천대학교-에밀리(사용법)](/images/post/2017-03-10/emily-weather.jpeg)
## 앞으로의 에밀리 지금까지 제공하는 기능들은 대부분 학교 홈페이지 크롤링을 이용하거나 외부 API를 사용해 제공하는 기능였습니다. 앞으로도 정보제공에 관한 기능들도 추가할 것이지만, 단순한 정보제공 외에도 인천대학교 학생들이 생활하는데 있어 편리할만한 기능을 추가할 예정입니다. 가령 자신의 학교 시간표를 에밀리를 통해 제공받은 웹 페이지에서 작성한 후 저장하면, 그 후로는 에밀리를 통해 자신의 시간표에 대한 정보를 제공받을 수 있습니다. 다음 수업의 시간과 강의실 정보, 오늘 전체적인 시간표 보기 등... 사용자가 입력한 정보를 바탕으로 개개인에 맞게 정보를 제공할 생각입니다.
## 마치며 실제로 사용자가 이용할 수 있는 서비스를 만드는것은 정말 재밌는 일인것 같습니다. 앞으로 계속해서 기능을 추가할 것이며, 앞으로의 포스팅은 지금까지 에밀리를 만들었던 과정과, 앞으로 추가하며 작업하는 내용을 다룰 예정입니다. [인천대학교-에밀리](http://plus.kakao.com/home/@인천대학교-에밀리)를 사용해보시고 혹시나 궁금한 점이 있으시다면 댓글로 남겨주시면 답변드리겠습니다.

Screen 사용하기

Screen 이란?

terminal 또는 putty를 이용해 원격에서 작업하다 보면 여러개의 창을 띄우고 싶을 때가 많습니다. 이럴때 보통 여러개의 terminal을 띄워서 작업합니다. (저는 screen을 알기 전까지 그랬습니다…) 그런데 창을 하나, 둘 여러개 띄우고 작업을 하다보면 어느 창에서 어떤 작업을 하고 있었는지도 헷갈리기 시작하면서 관리의 어려움이 생기게 됩니다.

description
**screen**은 한 terminal로 한번만 로그인 한 후에 여러 쉘과 프로그램을 사용할 수 있습니다. 또한 **세션관리 기능**도 지원합니다. 세션관리 기능은 상당히 유용합니다. 예를 들면, 터미널을 통해 원격 서버에 접속하여 작업을 하다가 네트워크 장애로 연결이 끊어진다면 매우 난감할 수 있습니다. 이때 screen을 사용해서 작업중이 였다면 **세션을 유지**할 수 있기 때문에 해당 작업은 로컬에서 계속 진행되고 있으며 언제든지 다시 해당 세션을 통해 작업을 계속 할 수 있습니다. 더불어 하나의 서버에 여러명의 사용자가 접속하여 **해당 스크린을 공유**하여 같은 화면을 공유할 수도 있습니다.

description


description
screen은 하나의 프로세스 입니다. 따라서 무분별하게 생성하기 보다는 필요한 용도에 맞게 적당한 개수를 유지하며 사용하는것이 중요합니다.

Screen 실행 명령어

screen 관련 명령어에 대해 알아보겠습니다.

1
2
// screen 을 시작하는 기본 명령어 입니다. 기본 세션명으로 시작합니다.
$ screen
1
2
// 해당 세션명으로 스크린을 시작합니다.
$ screen -S 세션명
1
2
// 이전에 작업 했던 screen 목록을 불러와 세션명과 함께 보여 줍니다.
$ screen -list
1
2
3
// 이전에 작업 했던 세션이 있을 경우 해당 세션을 불러옵니다.
// 세션명을 주지 않았을 경우에는 이전 세션이 한개일 경우 그 작업을 불러오고, 여러개 일 경우에는 작업 리스트를 보여 줍니다.
$ screen -R 세션명
1
2
// 스크린을 삭제합니다.
$ screen -S 세션명 -X quit

Screen 실행 후 명렁어

Screen 실행 후의 명령어는 **Ctrl-a로 시작**합니다.

1
2
// 새로운 쉘을 생성(create) 하여 그 쉘로 이동합니다.
$ Ctrl-a, c
1
2
// 바로 전(previous) 창으로 이동합니다.
$ Ctrl-a, p
1
2
// 바로 다음(next) 창으로 이동합니다.
$ Ctrl-a, n
1
2
// 숫자에 해당하는 창으로 이동합니다.
$ Ctrl-a, 숫자
1
2
// 창번호 또는 창이름으로 이동합니다. (' => 싱글 쿼테이션)
$ Ctrl-a, '
1
2
// 창목록을 보여 줍니다. (방향키와 Enter를 통해 창 선택 후 이동가능, " => 더블 쿼테이션)
$ Ctrl-a, "
1
2
// 현재 창의 title을 수정합니다.
$ Ctrl-a, A
1
2
// screen의 명령행 모드로 전환합니다. (: => 콜론)
$ Ctrl-a, :
1
2
// 현재 작업을 유지하면서 screen 세션에서 빠져나옵니다.(detach)  세션이 종료 되지 않습니다.
$ Ctrl-a, d
1
2
// 해당 스크린을 삭제합니다.
$ Ctrl-a, k
1
2
// 해당 스크린에 lock을 겁니다. (해당 유저의 비밀번호를 입력해야 해제할 수 있다.)
$ Ctrl-a, x

다음은 창 관련 명령어입니다.

1
2
// 창을 분할합니다(split).
$ Ctrl-a, S
1
2
// 분할된 창으로 이동합니다.
$ Ctrl-a, Tab
1
2
// 분할된 구역중 현재 구역을 제외하고 나머지를 숨깁니다.
$ Ctrl-a, Q
1
2
// Ctrl-a, d(세션 유지) 와는 달리 세션을 완전히 종료합니다.
$ exit

Redux 사용하기

공부하고 반복해서 복습하고 마지막에는 스스로 정리해보는 시간을 갖는것이 스스로에게 많은 도움이 되는것 같습니다. 그래서 이번에는 **ReactNative에서 Redux를 사용하기**라는 주제로 포스팅을 하려합니다. Flux에서 Redux 그리고 ReactNative까지 정말 자세하고 친절하게 설명해주신 분들이 많습니다. 제가 공부하며 참고했던 좋은 글들과 강의는 React Native 글 모음에 따로 정리를 하였습니다. 이 글에서는 제가 그동안 Redux 공부하며 이해가 잘 되지 않았던 부분들에 중점을 맞춰서 정리해보고 React Native에 Redux를 적용시키는 것으로 마무리하려 합니다. 아직은 Redux에 대해 잘 안다고 말할 수 없지만, 이번 기회로 정리하며 다시 다잡으려 합니다. 혹시나 제가 잘못이하거나 틀린 부분이 있다면 댓글로 알려주시면 감사하겠습니다.

Flux와 Redux

Redux의 시작은 Flux

Redux는 페이스북에서 MVC 패턴의 단점을 보완하고자 만든 아키텍처인 Flux의 구현체중 하나입니다. Flux의 구현체는 Redux 외에도 Reflux, rx-flux 등… 여러 구현체가 있지만 그중 Redux가 가장 널리 사용되고 있습니다.

저는 Redux를 처음 접했을때 Flux와 많이 혼동했습니다. Redux와 Flux의 구성 요소와 역할에 대해 혼란스러웠고 정리가 잘 되지 않았는데 이는 Redux와 Flux의 다른점에 대해 집중하지 않았기 때문입니다. Redux는 Flux와 마찬가지로 애플리케이션의 상태를 예측 가능하게 합니다. 그러나 Redux는 Flux와 다른 특징이 있습니다. 바로 **핫 리로딩(hot reloading)**과 **시간 여행 디버깅(time travel debugging)**입니다. 이 두가지의 특징으로 인해 Redux는 Flux의 구성요소에는 없는 리듀서(reducer)가 생겨나고, 스토어(Store)의 역할이 조금 변하게 됩니다.

리듀서(Reducer)

Flux에서 스토어는 (1) 상태 변환을 위한 로직, (2) 현재 애플리케이션의 상태를 포함하고 있습니다. 스토어가 이 두 가지를 모두 갖고 있기 때문에 핫 리로딩시 문제가 발생합니다. 상태 변환을 위한 로직을 수정하기 위해 스토어 객체를 리로딩하면 스토어에 저장된 기존의 상태와 뷰를 비롯한 나머지 시스템과의 이벤트 구독이 사라지게 되어 문제가되기 때문입니다. 이를 해결하기 위해 Redux에서는 리듀서(reducer)가 새로운 구성요소로 추가됩니다.

리듀서는 스토어가 갖고 있던 상태 변환을 위한 로직을 대신 갖게됩니다. 따라서 스토어는 액션이 발생했을 때 어떤 상태 변화를 만들어야 하는지 알기위해 리듀서에게 요청합니다. 이렇게 기존 스토어에서 상태 변환을 위한 로직이 분리되었기 때문에 이제 핫 리로딩이 가능하게 됩니다.

리듀서는 첫 번째 인수로 기존 상태의 값 두 번째 인수로는 액션을 가집니다. 리듀서를 작성할 때는 주의사항이 있는데, 첫 번째 인수로서 기존 상태를 갖고 있는 state는 수정하지 않고 상태를 수정할 때는 새롭게 생성하여야 합니다. 이 주의사항으로 지킴으로써 각각의 액션이 발생할 때마다 새로운 상태의 객체가 생성되어 결과적으로는 Redux의 특징인 시간 여행 디버깅이 가능하게 됩니다.

스토어(Store)

Redux의 스토어는 Flux의 스토어와는 다소 차이가 있습니다. 먼저 Flux에서는 다수의 스토어를 가질 수 있었고, 각 스토어는 자신의 범위에 있는 애플리케이션의 상태를 변환할 수 있는 로직을 포함하고 있었습니다. 그러나 Redux는 하나의 스토어만을 가집니다. 또한 Redux의 스토어는 상태 트리(state tree) 전체를 유지하는 책임을 가지며, Flux의 디스패쳐(dispatcher)의 역할도 대신합니다.(Flux의 디스패쳐는 모든 스토어를 갖고 있고, 액션 생성자로부터 액션을 넘겨받으면 스토어에 전달합니다.) 따라서 Redux에서는 스토어에서 제공하는 dispatch 함수로 디스패쳐의 동작을 대신합니다.

Redux 구성요소

지금까지 Flux와 Redux의 차이점과 그로 인해 Redux가 가질 수 있게된 특징에 대해 알아보았습니다. 지금부터는 Redux의 구성요소를 살펴보겠습니다.

액션(Action)

액션은 애플리케이션의 상태를 갖고 있는 스토어로 전달하는 데이터 묶음입니다. store.dispatch() 를 통해 스토어에 액션을 전달할 수 있습니다.

액션은 평범한 자바스크립트 객체이며, type 속성을 갖고 있습니다. 이 type 속성은 액션을 전달받은 스토어가 애플리케이션의 상태변환 로직을 갖고 있는 리듀서를 참조할때 사용하게 됩니다.

액션 생성자(Action Creator)

액션 생성자는 액션을 만드는 함수입니다. Flux에서는 함수 내부에서 dispatch 함수를 통해 액션을 전달하지만 Redux의 액션 생성자는 단지 액션을 반환하기만 합니다. 실제 액션을 전달할때는 결과값을 dispatch 함수에 전달하거나 생성된 액션을 자동으로 보내주는 바인드된 액션 생성자를 만듭니다.

리듀서(Reducer)

액션은 무언가 일어나 상태가 변할것이라는 사실을 말할뿐, 그 결과 실제 애플리케이션의 상태가 어떻게 바뀌는지는 리듀서가 담당합니다. 리듀서는 이전 상태와 액션을 받아서 다음 상태를 반환하는 역할을 하는 순수 함수입니다. 따라서 항상 다음 상태를 계산해서 반환하는 역할만 합니다. API 호출이라던가, Date.now()나 Math.random() 과 같은 순수하지 않은 함수를 호출하는 일은 해서는 안됩니다.

Redux는 처음에 리듀서를 undefined 상태로 호출하여 초기 상태를 반환합니다. 리듀서는 서로 독립적으로 수행된다면 분리될 수 있고, 이 분리된 리듀서는 *루트 리듀서라는 하나의 객체로 조합될 수 있습니다. 결과적으로 처음에 undefined 상태로 호출되면 각각의 자식 리듀서들이 초기 상태를 반환하게 되고, 각각의 리듀서는 전체 상태에서 자신의 부분만을 관리합니다. 모든 리듀서의 state 매개변수는 서로 다르고, 자신이 관리하는 상태 부분에 해당합니다.

스토어(Store)

스토어는 “무엇이 일어날지”를 표현하는 액션과 이 액션에 따라 애플리케이션의 상태를 어떻게 수정할지를 나타는 리듀서를 함께 가져오는 객체입니다. 스토어는 다음과 같은 일들을 합니다.

  • 애플리케이션의 상태를 저장
  • getState()를 통해 상태에 접근
  • dispatch(action)을 통해 상태를 수정할 수 있게 함
  • subscribe(listener)를 통해 리스너를 등록

Redux의 스토어는 Flux와는 달리 하나의 스토어만을 가질 수 있기 때문에 데이터를 다루는 로직을 나누고 싶다면 여러개의 리듀서를 조합하여 대신할 수 있습니다.

뷰 레이어 바인딩(The view layer binding)

뷰 레이어 바인딩은 생성된 스토어를 뷰에 연결하기 위해 필요합니다. 뷰 레이어 바인딩은 connect() 을 통해 컴포넌트(뷰)가 애플리케이션의 상태 업데이트를 받을 수 있도록 모든 연결을 만들어줍니다.

루트 컴포넌트(Root component)

모든 React 애플리케이션은 루트 컴포넌트를 가집니다. 루트 컴포넌트는 계층 구조에서 가장 위에 위치하는 컴포넌트이며, 스토어를 생성하고 어떤 리듀서를 사용할지 알려주며 뷰 레이어 바인딩과 뷰를 불러옵니다.

Redux 사용 준비

애플리케이션을 생성하며 Redux의 구성요소들이 서로 연결됩니다.

  1. **combineReducers()**를 통해 다수의 리듀서를 하나로 묶은 후 루트 컴포넌트가 **createStore()**를 이용해 스토어를 생성할때 전달합니다.
  2. 루트 컴포넌트는 공급 컴포넌트와 스토어 사이를 연결함으로써 스토어와 컴포넌트 사이의 커뮤니케이션을 준비합니다. (이후 컴포넌트에서 connect()를 통해 상태 업데이트를 받을 수 있습니다.)

Redux 데이터 흐름

Redux의 아키텍쳐는 엄격한 일방향 데이터 흐름에 따라 전개되며 데이터의 흐름은 4단계에 따라 진행됩니다.

1. 액션 생성 후 스토어에 전달
액션은 무언가 일어나 상태가 변할 것이라는 내용을 담고 있는 객체입니다. 액션 생성자를 통해 액션을 생성한 후 store.dispatch(action)을 통해 스토어에 전달합니다.

2. 스토어가 리듀서를 호출
스토어는 리듀서에 현재의 상태 트리와 전달받은 액션을 두 가지 인수로 전달합니다.

3. 루트 리듀서가 각 리듀서의 출력을 합쳐 하나의 상태 트리 생성
각각의 상태를 다루는 리듀서에 의해 생성된 결과를 하나로 합쳐 루트 리듀서가 하나의 상태 트리를 생성합니다.

4. Redux 스토어가 루트 리듀서에 의해 반환된 상태 트리를 저장
새로운 상태 트리가 앱의 다음 상태입니다. store.subscribe(listener)를 통해 등록된 모든 리스너가 불러내지고 이들은 현재 상태를 얻기 위해 store.getState()를 호출합니다. connect()를 통해 컴포넌트에 스토어가 연결되어 있다면 컴포넌트는 이를 반영하고 자신의 setState()나 forceUpdate() 메소드를 실행해 자동적으로 render() 메소드를 호출합니다.

React Native에서 Redux 사용하기

이제 React Native에서 Redux를 사용해보겠습니다. React Native를 위한 개발환경 설정은 공식홈페이지를 참조하시기 바랍니다. 이 예제 프로젝트에서는 간단하게 Redux를 사용하는 법에 초점을 맞춰 진행해보겠습니다.
간단히 카운팅하는 앱을 만들어보려 합니다. 완성 화면은 다음과 같습니다.
result

프로젝트 생성

먼저 React Native 프로젝트를 생성합니다. (현재 버전은 0.41.2 입니다)

1
react-native init example

다음으로 redux를 사용하기 위해 필요한 모듈들을 설치합니다.

1
npm install redux react-redux --save

이제 Redux의 구성요소를 참고하여 다음과 같이 새로운 폴더 구조를 생성합니다. (앞으로 사용할 폴더와 파일만 명시하였습니다.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
example
├── __tests__/
├── android/
├── ios/
├── node_modules/
└── src/
├── actions/
├── index.js
├── countAction.js
└── types.js
├── components/
└── Count.js
├── reducers/
├── index.js
└── countReducer.js
└── app.js
├── index.android.js
├── index.ios.js
└── package.json

루트 컴포넌트 생성 & 설정

먼저 app.js 파일을 작성합니다. App 컴포넌트는 루트 컴포넌트로 사용될 것이며, 액션과 리듀서 파일을 작성후 스토어를 추가할것입니다.

다음은 Android와 iOS의 진입파일인 index.android.jsindex.ios.js을 수정하여 App 컴포넌트를 루트 컴포넌트로 등록합니다.

지금까지의 코드를 작성한 후 화면은 다음과 같습니다.
result1

Count 컴포넌트 생성

이제 Count.js 파일을 작성하여 Count 컴포넌트를 생성하겠습니다. Count 컴포넌트는 현재 카운트 되고 있는 숫자를 보여주는 텍스트와 카운트를 증가시키는 버튼으로 구성됩니다.

Count 컴포넌트를 app.js에서 사용합니다.

Count 컴포넌트를 추가한 후 화면입니다.
result2

Count 액션 생성

액션은 애플리케이션의 상태를 갖고 있는 스토어로 전달하는 데이터 묶음이라고 했습니다. 이 카운팅 앱에서는 액션 객체의 type을 통해서 증가인지 감소인지, payload를 통해서는 증가 혹은 감소할 값을 전달할 것입니다.

먼저 actions/types.js를 작성합니다. types.js 에는 액션의 타입으로 사용될 값을 상수로 정의합니다. 타입은 후에 리듀서에서도 사용되기 때문에 미리 상수로 정의하는것이 실수를 줄일수도 있고, 후에 액션을 관리하는데에도 유용합니다.

이제 countAction.js를 작성합니다. type과 payload로 이루어진 액션 객체 액션 생성자를 통해 생성되어 스토어로 전달됩니다.

마지막으로 index.js를 작성합니다. index.js 에서는 여러개의 액션을 하나의 객체로 묶어 컴포넌트 파일에서 쉽게 사용할 수 있도록 해주는 역할을 합니다.

Count 리듀서 생성

액션을 전달받은 스토어가 상태를 변경하기 위해 리듀서에게 어떠한 상태변환을 해야하는지 요청합니다. 리듀서에서는 이 요청을 처리할 수 있도록 코드를 작성해야 합니다.
countReducer.js를 작성합니다. 리듀서는 함수입니다. 첫 번째 인자로 이전의 상태를 전달받고, 두 번째 인자로는 액션을 전달받습니다. 전달 받은 액션의 type을 통해 새로운 상태를 반환하는것이 리듀서의 역할입니다.
애플리케이션 실행 후 Redux는 처음에 리듀서를 undefined 상태로 호출합니다. swtich문에서 default인 상태에서 초기 상태를 설정합니다.

액션과 마찬가지로 index.js를 작성합니다. 여러개의 리듀서를 묶어 컴포넌트 파일에서 쉽게 사용할 수 있도록 해주는 역할입니다.
redux 모듈의 combineReducer는 트리 구조로 분리된 여러개의 상태를 하나의 단일 상태 트리로 조합합니다.

루트 컴포넌트에 스토어 연결

생성한 액션과 리듀서를 애플리케이션에서 사용할 수 있도록 루트 컴포넌트에 설정해주어야 합니다. 처음 작성했던 app.js를 다음과 같이 수정합니다.
redux 모듈에 있는 createStore를 통해 스토어를 생성할 수 있습니다. store 생성시 인자로 리듀서를 필요로 합니다.
생성된 스토어를 React 에서 사용하기 위해 react-redux 모듈에서 Provider를 사용합니다.

Count 컴포넌트 바인딩

뷰 레이어 바인딩은 생성된 스토어를 뷰에 연결하기 위해 필요하다고 설명했습니다. 이제 생성한 액션과 리듀서를 Count 컴포넌트에서 사용할 수 있도록 connect를 통해 연결을 만들어줍니다.
connect 메서드는 Store의 state를 컴포넌트의 props로 전달하고 상태의 변화가 있을 때 자동으로 컴포넌트의 render를 재호출합니다. connect 메서드는 다음의 인자를 가집니다.

  • mapStateToProps : 스토어의 state를 해당 컴포넌트의 props로 전달(mapping)합니다.
  • mapDispatchToProps : 스토어의 dispatch를 props에 전달합니다. dispatch를 통해 액션생성자에서 생성한 액션을 스토어로 전달할 수 있습니다.

간단한 카운팅앱이 완성되었습니다. 버튼을 통해 count의 값을 변경할 수 있습니다.

포스팅을 마치며

MVC 패턴에 익숙해져있던 저에게 Redux는 머리로는 이해할 수 있었지만 가슴으로는? 이해기 쉽지 않았었습니다. 이번 기회에 다시 하번 정리를 하며 제가 처음에 혼란스러워했던 부분들을 최대한 설명해드리려 노력했습니다. 위의 예제가 좋은 예제라고는 말할 수 없지만 그래도 React Native에서 Redux가 어떻게 사용되는지 전체적인 구조를 보는데에는 나쁘지 않다고 생각합니다.
액션과 리듀서를 생성하고 사용하는 부분에 있어서는 여러가지의 방법이 있지만 최대한 Redux를 쉽게 이해할 수 있도록 작성해보았습니다. 좀 더 효율적으로 작성하는 법은 다음에 기회가 된다면 포스팅해보겠습니다.

시작하며 읽기 좋은 글 모음

React Native를 공부하고 사용하면서 도움이 되었던 글들을 모아놓았습니다. 각 카테고리별로 분류하였으며 처음 공부하시는 분들은 카테고리에서 위에서 아래로 순서대로 읽으시면 많은 도움이 될거라 생각합니다. 앞으로도 지속적으로 업데이트 할 예정입니다.


최근 업데이트 - 2017 / 05 / 30

React


Flux

  • Flux로의 카툰 안내서 - bestalign’s dev blog
    Redux는 Flux 아키텍처의 구현체중 하나 입니다. Redux를 공부하기전 Flux에 대해 알아볼 수 있습니다.

Redux

모바일 화면을 위해 Viewport 사용하기

오늘은 회사에서 새로 출시한 ‘eyePoker’ 앱의 이용 약관 페이지를 만들었습니다. 정말 간단해서 일찍 끝내고, 로컬에서 확인을 해보았는데 문제가 발생했습니다. 분명 데스크탑에서는 잘 보이던 것이 모바일에서는 저런 경고를 내뿜었습니다.
아래의 사진은 로컬에서 배포 후 모바일(갤럭시 노트5)을 통해 페이지에 접속한것입니다. 왼쪽사진 하단부분에 페이지를 모바일 친화적으로 바꾸세요.라는 문구를 가진 팝업 보입니다. 즉, 제가 만든 페이지가 모바일 친화적이지 않다는 것입니다. 그럼 모바일 친화적이라는 것은 어떤 것일까? 페이지를 모바일 친화적으로 바꾸라는 팝업을 클릭하면 오른쪽 사진과 같이 모바일에서 좀 더 보기 편하게 페이지가 변경되는것을 볼 수 있습니다.

![viewport를 사용하지 않았을 때](/images/post/2017-02-09/no_viewport.jpeg)
![모바일 브라우저 최적화로 변경](/images/post/2017-02-09/show_mobile.jpeg)

모바일 친화적

구글의 Mobile Friendly Websites 페이지에서 모바일 친화적인 사이트에 대해 설명하고 있습니다.
사이트가 모바일 친화적이라는 것은 간단하게 사용자가 모바일로 웹 페이지를 이용할 때 편하게 이용할 수 있음을 뜻합니다. 사이트의 데스크톱 버전을 휴대기기에서 보거나 사용한다면 콘텐츠를 읽기 위해 두 손가락으로 화면을 모으거나 확대할 경우가 생길 수가 있고 이는 사용 환경을 번거롭게 느끼게해 사용자가 사이트를 떠날 확률이 높기 때문에 사이트를 모바일 친화적으로 만드는것은 중요합니다.

Viewport

정의

모바일 브라우저들은 viewport 로 알려진 가상 window상에 페이지를 렌더링합니다. 즉 화면(Display) 상의 표시 영역을 뜻합니다. 모바일 Safari는 viewport meta태그를 도입해 웹 개발자들이 viewport 크기와 스케일을 조정할 수 있게 했고, 웹 표준은 아니지만 이제 대부분 모바일 브라우저들도 이를 지원합니다.
데스크탑과 모바일 브라우저의 viewport (출처: 사파리 웹 컨텐츠 가이드)

데스크탑과 모바일의 viewport는 다른점이 있습니다. 데스크탑의 viewport는 브라우저 창(visible area)의 viewport와 같고, 사용자가 브라우저 창의 크기를 조절하면서 viewport의 크기도 조절할 수 있습니다. 웹페이지가 viewport보다 크면, 스크롤을 하여 나머지 영역을 볼 수 있습니다. 반면에 모바일 viewport는 웹브라우저 창보다 크거나 작을 수 있고 상하좌우로 움직이거나, 더블탭, 줌인, 줌아웃을 통해 viewport의 배율을 변경할 수 있습니다.(크기가 아닙니다)

필요한 이유

모바일 브라우저에서 viewport가 중요한 이유는 모바일 브라우저가 웹 페이지를 브라우징 하는 특징에 있습니다. 현재 스마트폰 브라우저는 모바일 환경에서도 데스크탑 환경처럼 웹 페이지 전체를 자연스럽게 브라우징 할 수 있도록 풀브라우징을 지원합니다. 이 때문에 데스크탑에 기반하여 설계된 웹페이지를 모바일에서 보면 기본 viewport가 980px 이고, 이로 인해 내용이 작게 보입니다.(width가 980px 이상인 컨텐츠가 있다면 좌우로 스크롤 됨) 간단히 말하면 작은 화면의 모바일 단말기에 웹 페이지 모두를 표시하려하니 전체적인 페이지의 배율이 조정되는것 입니다. 결과적으로 모바일 화면에 맞도록 전체적인 페이지가 축소되어 보이지만 페이지의 컨텐츠는 배율축소가 발생해 가독성이 떨어지게 됩니다. 바로 이때 viewport를 설정하면 다양한 모바일 기기에서도 페이지의 너비나 화면 배율을 설정할 수 있습니다.
가로 375  픽셀을 가진 iPhone6 브라우저에서 가로 980 픽셀을 가진 페이지를 풀브라우징
가로 375  픽셀을 가진 iPhone6 브라우저에서 viewport 메타 태그 사용

사용 방법

head 태그 사이에 다음을 코드를 입력합니다. 기본적으로 데스크탑 브라우저에서는 viewport 메타 태그를 사용하지 않아 무시합니다.

1
<meta name="viewport" content="width=device-width, initial-scale=1.0">

1. viewport의 속성

  • width : viewport의 가로 크기를 조정한다. 일반적인 숫자값이 들어갈 수도 있고, device-width와 같은 특정한 값을 사용할 수도 있다. device-width는 100% 스케일에서 CSS 픽셀들로 계산된 화면의 폭을 의미한다.
  • height : viewport의 세로 크기를 조정한다.
  • initial-scale : 페이지가 처음 로딩될 때 줌 레벨을 조정한다. 값이 1일때는 CSS 픽셀과 기기 종속적인 픽셀 간의 1:1 관계를 형성한다.
  • minimum-scale : viewport의 최소 배율값, 기본값은 0.25이다.
  • maximum-scale : viewport의 최대 배율값, 기본값은 1.6이다.
  • user-scalable : 사용자의 확대/축소 기능을 설정, 기본값은 yes이다.

2. 정의된 속성 값

  • device-width : 기기의 가로 넓이 픽셀 값 (웹페이지의 가로(width) 값은 기기가 사용하는 가로 넓이 값(device-width) 만큼 적용하여 사용하라는 의미)
  • device-height : 기기의 세로 높이 픽셀 값

3. 주의사항

  • contents보다 작은 viewport width/height를 설정하면 무시된다.
  • viewport에서 initial-scale을 설정하지 않고 width/height를 설정하면 전체화면이 표시된다.
  • viewport에서 initial-scale도 width/height도 설정하지 않으면 width=980px/height=1091px이 된다.
  • 표시영역과 contents의 크기가 일치하지 않을때 initial-scale를 설정하면, 의도하지 않은 layout이 발생한다.

적용

![viewport를 사용](/images/post/2017-02-09/yes_viewport.jpeg)
![viewport 사용 후 폰트 변경](/images/post/2017-02-09/yes_viewport_with_font.jpeg)

위의 사진은 viewport를 적용한 후의 페이지 화면입니다. viewport를 적용하니 `페이지를 모바일 친화적으로 바꾸세요.`라는 문구를 가진 팝업은 사라졌지만 처음 모바일 친화적으로 바꾸어 보았던 페이지같이 폰트는 변경되지 않았습니다. 아마 브라우저 상에서 브라우저에 내장된 폰트를 사용해서 보여주었던것 같아 폰트까지 변경하니 오른쪽 화면과 같은 결과를 얻었습니다.

반응형 웹 디자인

패블릿, 태블릿, 데스크탑, 게임콘솔, 티비, 웨어러블 기기 등등 정말 다양한 화면 크기가 있습니다. 화면 크기는 언제나 변하기 때문에 사이트는 어떤 화면 크기에도 언제나 적응 가능하도록 설계되어야 합니다. 미디어 쿼리(@media)는 장치에 따라 각기 다른 레이아웃을 만들거나, 다른 미디어 종류에 따라 다른 css 코드를 작성하는 등, 반응형 웹 페이지를 작성할 때 사용합니다.
부트스트랩은 CSS 미디어 쿼리를 사용해 반응형으로 휴대폰에서부터 태블릿, 데스크탑까지 적용되는 사이트를 만들 수 있도록 도와주는 HTML, CSS, JS 프레임워크입니다. 저도 여러번 간단하게 사용했던 적이 있습니다. 적절한 렌더링과 확대/축소를 위해 부트스트랩 시작 페이지에서는 다음 코드를 <head>에 추가하라고 명시합니다.

1
<meta name="viewport" content="width=device-width, initial-scale=1.0">

지금까지는 이유를 알지 못하고 추가했던 메타 태그였는데 이제는 그 이유를 알 수 있습니다. width=device-width를 추가함으로써 전체적인 웹 페이지의 width가 설정되고 @media 에서 선언된 width의 범위에 따라 css가 적용되어 결과적으로 반응형 웹을 만들 수 있는것 입니다.

마무리

간단하게 사용방법만 알아보려했으나, viewport 메타 태그를 통해서 반응형 웹 디자인이 가능하다는 것을 알고 중요하다 생각해서 좀 더 공부하고 포스팅하였습니다. 한줄의 코드를 통해서 물고 물고 물어 여러가지를 다시 공부하고 더 잘 알게된것 같아 좋습니다. 저처럼 viewport 에 대해 자세히 알지 못하고 사용하셨던 분들께 도움이 되었으면 좋겠습니다.

참고 : MDN-viewport meta 태그를 이용해 모바일 브라우저상에서 레이아웃 조종하는 법, meta viewport(메타 뷰포트 태그) 사용법