2019년 10월 29일 화요일

Vuex 변수 재할당 문제

Vuex를 새롭게 사용하면서 몇시간동안 나를 미치게하는 문제가 발생했었다.

Vuex.Store{
 state : {
   myList : []
 },
  mutations : {
   setMyList( state, payload ) {
     state.myList = payload.newList;
   }
  },
  getters : {

    getMyList : state => { return state.myList }
  }
}

이러한 myList 데이터가 있었고, 초기 페이지 로드 이후 이 목록을 mutator로 추가한뒤, 추가된 myList 갯수만큼 화면에 v-for로 뿌려주는 아주 간단한 문제였으며, 예상과 다르지 않게 자알 동작했다. 돌이켜보면 이게 화근이다.

이후 mutate로 한번더 값을 변경한 다음,  다시 한번 getters를 호출했다. 그런데 왠걸?
업데이트가 전혀 작동하지 않는다.

이때부터 원인을 파악하기 위해 여기저기를 들쑤시고 다니면서 난장판을 만들며두시간을 허비했다.

마침내 state의 값은 변경되었으나, Vuex에서 바뀐값을 인지하지 못한다는 결론이 났고, 뒤늦게 해당 이슈로 검색을 진행 할 수 있게 되었다.

마침내 이슈에 대한 실마리를 얻었는데 :
Vue does not allow dynamically adding new root-level reactive properties to an already created instance.
"Vue 인스턴스단에서 동적으로 새 root-level단의 변경을 허용하지 않는다" 정도로 이해하면 될거 같다.

내가 하려는  행위는 Vue인스턴스에 있는 store(Vuex) 의 state 를 임의로 변경하려는 행위였으며, Vue는 변경을 감지하지 못했던 것이다. 이렇게 생각해보면 애초에 처음에도 똑바로 동작이 되지 않았다면 좀더 빨리 문제를 파악할 수 있었겠지만, 변경이 어느 타이밍에는 되나, 절대 보장할 수 없는 변경 방법이었던 것으로 파악된다.

이후 가이드에 따라 올바른 변경방법을 mutation에 적용했다.
mutations : {
  setMyList ( state, payload ) {
    Vue.set(state, 'mylist', payload.newList);
  }
}

이렇게함으로써 Vuex는 변경을 바로 파악할 수 있게 되었다.

오늘도 신나는 삽질 끄읏

2019년 6월 9일 일요일

SASS설정기 on NPM

어느덧 아무것도 모르던 css 작업을 실무에서 겪어가며, 점차 늘어나는 정신나간 분량의 css 코드들에 대해서 홀로 맞서기 버거워지기 시작하고있는 시점인 지금, 드디어 좀더 새련된 CSS처리방법을 강구해야겠다는 결심이 섰습니다.

유명한 후보로는 SASS 나 LESS.
둘의 각각의 특성에 따라 어느것이 더 배우면서, 적용하기 좋은지 짧은 비교의 시간을 가졌고, 비교당시 더 높은 점유율을 기록하는것으로 보이는 SASS가 당첨되었습니다.

LESS와 SASS는 모두 동일하게, css로 컨버팅 작업이 필요합니다. 그러므로 매번 서버를 리로드 하면서 컨버팅 또한 필요했기 때문에, SASS는 watch 라는 이름의 기능으로 핫리로드를 지원하고있었습니다.

하지만 매번 터미널 두개를 돌려서 watch를 돌리고 서버를 돌리기가 너무나도 귀찮은 나머지, 이 두 명령어를 동시에 날리게 하기 위해서 concurrently 를 사용하여 npm 명령어를 조금 조작할 것 입니다.

1. 우선 SASS를 설치합니다.
` npm i -g sass`
로 간단하게 sass를 설치하게 됩니다.

테스트를 위해 ./sass/default.scss 파일을 만들었습니다.

서버 node에서는 static 폴더를 ` ./public `으로 지정해 두고 있었습니다.
그러므로 css를 static으로 관리하기 위해 새 폴더를 만듭니다.
` ./public/css `

이제 ` default.scss ` 에서 예제 코드를 작성합니다.

body {
 background-color : red;
 container {
  background-color : orange;
 }
}

이제 완성된 scss 파일을 css로 변환해야 합니다.

` sass --watch ./sass/default.scss:./public/css/default.css `
변환이 완료됩니다.

2. 이제 이러한 복잡한 과정을 쉽게 조금씩 바꾸어 나갈것 입니다.
우선은 sass 폴더내의 모든 scss파일들을 public 폴더에 저장시킬것 입니다.
default.scss body.scss footer.scss 등의 여러파일이 있어도 한번의 명령어로
default.css body.css footer.css 로 바꿀수 있게 말이죠

간단합니다.
` scss --watch ./sass:./public/css `
단일 객체가 아닌 폴더를 지정하고, 출력물 폴더로 변경만 시켜주면 됩니다.

이제 watch를 프로젝트를 제시작 할때마다 치는 귀찮음을 해결해 볼 것 입니다.
물론 리눅스 유저라변 백그라운드로 손쉽게 처리할 수 있겠지만, 리눅스 알못인 저는 무언가 편한 작업을 요합니다.
npm은 우리가 js관련 작업 특히 node 작업에서 땔수 없는 메니져입니다.
npm의 추가 모듈을 이용해 볼 것 입니다.
` npm i -g  concurrently `

concurrently는 npm 명령어를 다중으로 실행 가능하게 돕는 모듈입니다.
프로젝트의 package.json 파일을 열어봅니다.

 "script" : 의 값들을 추가 할 것 입니다.
저는 npm start 라는 명령어로 ` node app.js ` 와 ` scss --watch ./sass:./public/css ` 를 동시에 실행하고 하나의 터미널에서 보이게 할 것 입니다.

` "script" : {
  "start" : " concurrently \" node app.js \" \" scss --watch ./sass:./public/css\" "
}
`
 이제 터미널에서 npm start 를 치게되면 두작업이 동시에 실행되는것을 볼 수 있습니다.

그런데 [0] 과 [1]로 실행중인 프로세스의 정보를 표기하는데 좀 보기가 어렵습니다.
배열명 말고 [Express] [Watch] 로 표시해 볼것 입니다.
 script : 에 start 의 값을 수정합니다.


` "script" : {
  "start" : " concurrently -n \"Express,Watch\" \" node app.js \" \" scss --watch ./sass:./public/css\" "
}
`
-n 으로 이름을 순서대로 정할 수 있습니다.

이제 로그를 보면 보기좋게 구분이 되기 시작합니다.

하지만 저는 변태입니다. 색깔을 넣어서 더 구분을 하고싶습니다.

-c 를 이용해 봅시다.

` "script" : {
  "start" : " concurrently -n \"Express,Watch\" -c \" red,yellow \" \" node app.js \" \" scss --watch ./sass:./public/css\" "
}
`
이제 [Express]는 빨강으로, [Watch]는 노랑으로 표기가 됩니다. 만족스럽네요.

추가적인 기능들은 스스로 들러보고 적용해 BOA효

2019년 5월 17일 금요일

[Webpack 공부 #2] Webpack with HTTP2?


...그리하여 Webpack의 가장 큰 사용목적인 모듈 패키징이 무엇인지 알기 위해, HTTP1.1에 대해서, 또 동시에 HTTP2에 대해서 알게되었습니다.

더 깊게 웹팩을 파기 전에, 저는 지금즘 한가지 생각이 들었는데
"그렇다면, HTTP2 방식을 사용하면 웹팩의 존재가 필요없지 않는가?"
였습니다.

결론적으로 웹팩과 HTTP2를 병행하게된다면, 더 높은 성능향상을 꽤할 수 있다 입니다.
어찌보면 당연한 말입니다.

특정 페이지를 로딩할때 필요한 리소스들이 아래와 같다고 한다면
index.html
default.css
default.js
favicon.co
title.png
총 5개의 리소스가 필요로 하며,
Http2를 이용한 서버에서 클라이언트가 처음으로 index.html을 요청하며 connection을 만들면,
서버는 5개의 스트림을 만들어 전송을 하게 됩니다.

하지만 웹팩을 통해 번들링을(예를들어) 통해 단 하나의 번들로 묶는다면,
스트림은 단 한번만 전송하면 되기 때문입니다.

또 이렇다고 마냥 모든 파일들을 번들링하기엔 석연찮은 구석도 있습니다.



번들링의 수준
번들링을 하게된다는것은 여러 파일들을 합쳐서 제공한다는 말입니다.
그렇다면 모든 js파일들을 번들링하는것은 옳은걸까요? 분명 connection/stream은 단 한번이니 좋을것 입니다. 하지만 다른쪽 성능을 생각해 본다면 어떨까요?

모던 js를 이용해 모듈화를 한 경우의 윂페이지라면, 페이지나 작업마다 사용하는 js파일들이 다를것 입니다. 이런 경우라면 모든 js파일들을 다운로드받고 싶지 않을것 입니다. 모듈화 한 이유가 없으니깐요.

모든 js파일들을 번들링 하게된다면, 모든 페이지마다 하나의 큰 파일을 받아야 할것입니다. 설상 그 js파일들이 쓸모없을지라도 말이죠.

그럼 특정 js파일들만 번들링을 하고, 필요에따라 제공하는것은 어떨까 하는 생각이 들것입니다. 번들링 된 파일로 stream 횟수는 적당히 줄어들며,불필요한 js를 로드하느라 성능 낭비를 할 필요도 없을거구요.

하지만 이 하이브리드 구조와, 번들링을 하지 않은 js파일들의 벤치마크를 했을때 결과는 놀랍게도 의도와 많이 달랐다고 주장합니다.

위의 글에 따르자면, 번들러를 사용할떄 쓰는 압축 기술은 대용량 파일의 압축에 더 유리하기 때문에, js 모듈따위의 작은 용량의 파일은 압축 비용이 원본비용보다 높은 문제를 지적합니다.

이러한 데이터들과 벤치마크를 통해 지금까지 중론처럼 나온 결론은
번들링은 HTTP1.1 과 HTTP2 를 막론하고 성능향상을 유도할 수 있으나,
과도한 번들링은 서버의 캐싱문제로 다른 퍼포먼스 이슈를 초래할 수 있습니다.
결과적으로 상황과 기능 등에 맞게 세분화 한 번들링 / 하지만 너무나도 세세한 경우라면 stream이 증가할 것이므로 너무 세세하지는 않게 적당한 번들링을 사용해야 한다는 것 입니다.




이제 기본적인 웹팩의 용도를 넘어 좀더 향상된 사용방법을 간단하게 훑어보았으며, 이제는 실제로 코드를 짜 봐야 할 시간인거 같군요.

[Webpack 공부 #1] Http1.1 and Http2

새로 취직하게 된 직장에서는 어렴풋이 모든것을 처음부터 시작해야하는것이 아닌가 하는 판단이 들기 시작하고 있습니다.
코드관리, 서버, 배포, 모든 환경을 스스로 정의해야할지도 모른다는 생각이 점차 들기 시작하여, 저는 개발환경 구축부터 서비스를 위한 환경구축에 대한 정보 탐색을 시작했습니다.

당장 머릿속에 떠돌던 몇가지 자동화 혹은 개발환경의 힌트들이 면접중에 떠올랐습니다.

GitLab Jira DevOps Docker Jenkins..

왜냐하면 저에게 '환경 구축'이라는 단어들에 대해서 매우 생소하고 무지렁이인 상태였기 때문입니다.

무지렁이가 할수있는것은 리서칭 이지요!

우선 요청도 있고 해서 먼저 리서칭 한것은


Webpack 입니다.

웹팩이란 js 모듈이나, 기타 리소스들을 하나로 묶는 모듈 번들러 입니다. 그럼 왜 js나 리소스들을 하나로 묶는가에 대해 알아보아야 합니다. 그것을 알기 위해서는 HTTP1.1에 대해서 알아봐야 합니다.

HTTP 1.1 vs HTTP 2

기본적인 웹 프로토콜에서는 1요청당 1응답만을 돌려줍니다. 이 말은 하나의 웹 페이지에 필요한 리소스 파일들이 아래와 같다고 하면 [ index.html | default.css | foo.js | favicon.jpg | index.png ] 우리는 HTTP 요청과 응답 handshake 를 5번 해야 모든 윂페이지 로딩이 정상적으로 끝나게 됩니다. 5번의 지연시간이 소요되는것 입니다. HTTP 1.1의 전송방식에 문제점은 몇가지가 있는데,
1. HOL Blocking ( Head of Line Blocking)
동일한 네트워크 큐에 있는 패킷들이 Head부분의 패킷에 의해 지연되는 형상을 야기하는데,
웹 환경에서 HOL Blocking은 두가지가 있습니다.
HTTP HOL Blocking
TCP HOL Blocking
HTTP1.1 에서 앞서 언급된 여러개의 리소스를 모두 받기 위해서는 하나의 파일당 하나의 요청을 주고받는 행위를 없에기 위해 pipelining이 나왔습니다. 이 기술은 단 한번의 커넥션을 통해 TCP socket을 연결하는 방식입니다. 소켓이 연결이 되면, 해당 소캣을 통해 리소스들을 하나하나 순차적으로 받는 방식입니다. 이 말은 HTTP1.1에서는 전송방식이 반드시 동기화 형식으로 차례로 받을수 밖에 없다는 문제점을 보여줍니다. 하지만 이방식 역시 socket 통신을 통해 이루어지며, 그러는 와중에 리소스 다운로드에 지연시간이 생길 수 있습니다. 이런 경우, 다음 리소스들은 현재 받고있는 리소스의 다운로드가 완료 될 때 까지 팬딩상태에 머무르게 되며, 이를 우리는 HTTP HOL Blocking 이라고 부릅니다. TCP의 특성상 보낸 데이터가 올바르게 전송되었는지 확인한 후, 실패시 다시 보낼 수 있게 설계되어있는데( 3way hand shacking) 이런 경우가 발생할시, TCP는 전송받은 패킷의 순서를 관리하기 위해, 재전송을 받을때 까지 다음 순서의 패킷을 받지 않게 되며, 이런경우 TCP HOL Blocking이 걸린 상태라고 부를 수 있습니다.

2. 높아지는 RTT(Round Trip Time) 위에서 언급되다싶이, HTTP1.1은 동시에 하나의 connection(물론 세팅을 통해 늘릴수 있다고도 한다)을, 동기적으로 처리하기 때문에, TCP구조인 HTTP의 특성상 3way-handshacking을 하나의 리소스를 받을때 마다 이루어지기 때문에 RTT가 증가됩니다.

3. Header의 비대화 HTTP 1.1 이 되어가며 Header에 입력되는 메타데이터들의 양이 늘어나게 됩니다.그리고 모든 connection을 통한 통신에서는 Header가 반드시 포함됩니다. 특정 경우에 따라 완벽히 동일한 Header가 몇번이고 반복적으로 전송되기도 할것 입니다( 하나의 동일한 높은 용량의 데이터인 경우 페킷을 나누어 전송할 것 이며, 이때 헤더는 페킷 순서와 끝 유무 정도를 재외하면 동일할 것 입니다)


이러한 여러가지 문제점들이 있었고, 솔루션 또한 여러가지 존재하게 되었었습니다.
하지만 근본적인 솔루션들은 아니었으며, HTTP2 가 이러한 문제들을 해결하기 위해 나타났습니다...
그리고 HTTP2는 1.1과 동일한 형태를 유지하되 속도적인 측면을 개선하기 위해 개발되었으므로, 기존 명세와는 큰 차이는 없습니다. 추가는 있겠지만..

그리고 HTTP2에서 소개하는 기술은 아래와 같습니다.
Multiplexed Streams
클라이언트는 하나의 connection에서 이제 여러개의 메세지들을 받을 수 있습니다. 그리고 이제 순서를 따지지 않으며, stream으로 받게 됩니다.
Stream Prioritization
순서를 따지지 않고 데이터들을 받을수 있다는것은, 보여줄 내용을 순서에 맞게 보낼 수 없다는 의미이기도 합니다. 예를들어 js파일을 먼저 받고싶지만, 순서를 따지지 않게되기 때문에, 어느 타이밍에 js파일을 받는지 알 수 없게 됩니다. 그러한 이유로 각각의 리소스파일에 우선순위인 가중치 를 부과 할 수 있으며, 이를통해 무엇을 우선적으로 받을 수 있습니다.
Server Push
이제 클라이언트의 요청이 있지 않아도 서버는 임의로 데이터를 전달 할 수 있습니다. 기존의 1.1 에서는 클라이언트는 html을 먼저 받은뒤, 이후에 추가적으로 무슨 리소스가 필요한지 파악을 할 수 있었고, 요청을 보낼 수 있었습니다. 하지만 Server Push를 이용하여, 클라이언트가 첫번째 요청을 하게되면, 서버측에서 추가적으로 필요한 리소스들을 찾아내어 클라이언트의 추가 요청없이 임의로 전송을 하는 방식입니다. 이렇게 함으로써 connection 추가 요청 수를 줄이는 방법으로 성능향상을 꽤합니다.
Header Compression
무겁고 중복적으로 전송되는 Header 다이어트를 목적으로, Header Table과 Huffman Encoding 기법을 사용하게 됩니다. Header Table는 처음 전송받은 Header를 특별한 Table에 저장 해 두며 또다시 헤더를 요청받게 되면, 기존에 저장된 헤더 table과 diff를 때려서, 다른 부분만 명세하여 전송됩니다. 이렇게 함으로써 중복되는 header 데이터 전송 낭비를 해결했으며, 상당한 수준의 로드타임 개선을 이루었습니다.


2019년 5월 7일 화요일

방어 코딩?

첫번째 회사에 근무할때 완전히 다른 파트를 담당하느라 바쁘긴 해도, 사수분이 내 코드의 리뷰를 이따금 해 주곤 했었다. 또 아이러니하게도 그런 리뷰를 받는 날에는 항상 시간에 좇겨 어쩔수없이 짜거나, 진퇴양난으로 인해 만들어진 자부심이라곤 눈꼽만큼도 남아있지 않은 슬픈 코드들이 항상 비류 대상이었고, 항상 부끄러운 배움의 시간이었다.

어쩌면 내가 울상이거나 한숨을 쉬거나 '이런 코드를 정말로 라이브시켜도 되나' 하는 표정이었을때 리뷰를 해주셨던게 아닐까 글을 쓰다보니그런 생각이 흘러간다.

아무튼 이러한 리뷰시간을 갖으면서, 나는 짧은 개발자 인생에서 처음듣는 단어가 있었는데 바로 방어코딩 이라고 그는 불렀다.

그당시 내 보수중인 코드는 spring java였었으며, expected value가 다를수 도 있었기 때문에, 또 내 개발 커리어의 경험에 따라 - 특히 그당시 javascript의 undef value 로 크게 혼이 난적이 있었기 때문에 - 나는 가지고오는 value나 params 를 검사부터 하는것을 지향했었다.

예컨데 이런 함수의 형태를 만들어야 한다면 :

private foo getFooValueFromBar ( Bar bar ) {
 return FooMaker(bar);
}

난 대게 이러한 방식으로 함수 안을 보강했었다.
foo myFoo = null;
if ( bar == null || bar == '' ) {
 throw new Error("givin param is null");
} else {
 myFoo = FooMaker(bar);
 if ( myFoo != null ) return myFoo;
 else throw new Error("returned foo is null");
}

난 아직도 이게 그렇게 틀린 코드라고 생각하지는 않는다.
사수 역시 동의했다. 예외를 모두 under control 하는점은 좋으나, 굳이 이런게 필요하지 않은 경우까지 모두 한다는것이 내 사수분의 의견이었다.

그의 주장은 이해할 수 있었다. 왜냐하면 그 당시 메인 프로젝트는 이미 수많은 코드들의 스파게티로 엉망인 상태였으며, 우리는 이 스파게티코드를 대대적으로 변경하는 v2.0 작업을 진행하기 전까지는 '최대한 덜 지저분하게 판을 벌려가며 새 기능을 넣을것' 이라는 둘만의 목표의식을 지나고 있었다. (하지만 잘 지켜지지는 못했다 적어도 난)


최근 내 여자친구의 사수가 방어코딩이라는 언급을 하며 코드를 소개한 적이 있었다.
그녀는 spring java를 담당하던 이전직장의 나와는 달리, javascript를 담당하는 포지션이었으며, 그녀의 사수는 최적화와 숏 코딩에 대한 상당한 지지와 자부심을 가진 자신감 넘치는 사수였다.

그는 반대로 방어코딩에 대하여 매우 긍정적이며, 오히려 더 세밀하게 해야한다는 취지의 주장을 했다( 적어도 그렇게 이해했다. 내가 들은 이야기는 아니니깐 말이다 ) 그 역시 외주를 통해 만든 말라비틀어진 스파게티 수준의 코드들로 surrounded되어, 매일을 히스테리성 발작을 보일정도로 괴로워하는 사람이었으며, 내 사수분과 같은 생각인 2.0까지만 이 코드들을 최대한 아름답게 보강하며, 이후에는 새 버전을 올리며 버리자는 주장을 했었다.

이러한 글을 쓰기전에 나는 어떻게 두명의 3년차 4년차 개발자 선배들이 각기 다른 의견을 제시하는지에 대해 생각을 해 봤었고, 무엇이 이들을 상반된 주장을 하게 되는가 하는 호기심에 정리를 해 보기 위해 쓴 단순한 글이다.

java개발자로 5년차가 되어가고 있던 그는, 한 스타트업의 모든 프로덕트를 혼자서 2년넘게 관리해 오며, 언제나 스스로를 낮추며 공부를 멈추지 않은 훌륭한 사수였으며,
javascript 개발자로 3년차를 보내고 있는 그는, 상당히 유능한 실력으로 인정받아, 그 역시 또 다른 스타트업의 프론트앤드를 총괄하고있는 팀장이며, 항상 자신감 넘치고, 배움에 대해서 즐거움을 느끼는 타입이다.

둘다 이전에 만들어진 '완성하기 급급한' 프로덕트들로 괴로워하며, 여유만 생긴다면 새 프로덕트로 갈아탈 기회만을 노리고 있는 사람들이었다.

그렇기에 난 개발환경과 언어에 따라 두 가지 상반된 의견이 나왔으리라 라는 추측을 하고있다.

개인적으로 느끼는 java와 javascript의 차이점중 하나로 이것을 드는데,
java는 예외발생에 대해서 매우 민감하게 반응을 하고 오류를 뱉고 죽어버리는 반면,
javascript는 예외가 발생해도 은근슬쩍 계속 작동이 되어버리기 떄문에, 실제 오류발생 부분에서 상당히 지난 뒤에, 완전히 코드가 꼬인뒤에에 모든게 폭발한 뒤에에 오류가 나타나버리는것이다.

그런이유로 java는 발생하는 예외에 대해 민감하기 때문에, 추가적으로 개발자가 공들여야 하는 예외사항의 범위가 작으나, javascript는 모든 예외사항을 다 고려해야하기 때문에, 후자가 더 방어코딩이 필요한것이 아닐까 한다.

그래서 방어코딩, 해야하는가 말아야 하는가의 결론은

언제나 그렇듯 '지금 내 상황에 맞게'  ...

CSS border 요약

Border

border 프로퍼티로 굵기, 스타일, 컬러 를 정의할 수 있습니다.

  • 굵기 는 border-width 로써 경계선의 굵기를 정의하며
border-top-width | border-right-width | border-bottom-width | border-left-width
로 각 위치별로 따로 정의할 수 있습니다.

  • border-style에서 경계선의 스타일을 정의할 수 있습니다.
style은
border-top-style | border-right-style| border-bottom-style| border-left-style
로 각 위치별로 따로 정의할 수 있습니다.

  • 마지막으로 border-color로 경계선의 색을 정의할 수 있습니다.


border-width : 1px;
border-style : dotted;
border-color : #000;
위 인자들을 이용해 만든 border는

border : 1px dotted #000;
로 줄일수 있습니다.



border-radius 를 사용하여 둥근 모서리를 만들수 있습니다.
이 값은 border에 둥근 모서리를 만들수 있게 해 주며, border가 설정되어있어야만 보입니다.

border-radius : 1px;
border : 1px solid #000;

위와같이 border값 설정이 필요합니다.

  • border-radius값은 아래의 네가지 프로퍼티들의 조합입니다.
border-top-left-radius | border-top-right-radius | border-bottom-right-radius | border-bottom-left-radius

그러므로 특정 위치만 둥근 모서리를 만들고 싶을때
border-top-left-radius : 1px; border-top-right-radius : 0px;
border-bottom-right-radius : 1px; border-bottom-left-radius : 0px;
혹은
border-radius : 1px 0 1px 0;
으로 표현 할 수 있습니다.

border-radius 의 인자값이 두가지 혹은 세가지로도 표현이 가능하며,
border-radius : param1, param2, param3, param4
param3의 default value는 param1과 동일하며,
param4의 default value는 param4와 동일합니다.


border-image 로 경계선에 이미지 띄우기
해당 프로퍼티는 세가지의 값을 받는데,

이미지 경로, 이미지를 자르는 시작점, 모서리가 아닌 영역에서의 이미지 처리방법 을 정의합니다.

  • 이미지 경로는 말 그대로 background에 쓰일 이미지 경로를 정의하며
  • 이미지를 자르는 시작점의 경우,
우선 border-image에 입력된 이미지는 3x3 으로 총 9개의 이미지가 slice됩니다.
이 slice된 영역들은 border의 각 꼭지점과 면을 담당하게 됩니다.
하지만 필요에따라 이 slice하는 영역을 변경해야하는 경우가 생기는데 이때 이곳에서 slice할 영역을 정의하는것 입니다. 30% 로 자른경우, 
이미지 총 길이에서 30%씩 start, end영역을 자르며, 나머지 영역인 40%는 면에 그리게됩니다.
만일 이 자리는 영역이 50% 가 넘어가게 되면, start와 end영역에만 그려지며, 면에는 그려지지 않습니다(남은 영역이 없기 때문에)
  • 이미지 처리방법의 경우에는 stretch와 repeat 가 있습니다. 위의 자르는 영역에서 나온 면 부분의 이미지를 늘리거나 반복시킵니다.

2019년 4월 18일 목요일

React.js 공부노트 #3 styled-component 삽질기...

리엑트 공부 와중에 알게된게, 후배 하나가 나처럼 리엑트 헛발질을 열심히 하고있다는 소식을 접했습니다.

그 친구는 이틀전부터 CSS를 조금더 편하게 관리하기 위해서 어떤것을 사용해아하는가 에 대해 여기저기 찔러보고 다녔었고, 저 또한 기본 CSS말고는 써본적이 없었기 떄문에 아는바가 전혀 없었습니다.

그러는와중 상당히 잘 정리되어있는 블로그를 참조하고 제가 이번에 써볼 CSS모듈이 무엇이 될지 곧 정할 수 있게 되었습니다. 해외 유명포럼 Reddit을 몇년동안 잘 사용해왔던 터라 reddit에서 사용중엔 styled-component에 단박에 눈이 꽂혔고, 기본 react 강의의 흐름에 너무 다르지 않은 -러닝커브가 그다지 높지 않아보이는(component별로 jsx에서 작성하므로)- 것이 두번째 선정이유이며, 마지막으로 scss에서 사용할 수 있는 부모자식 관계 방식또한 가능했기 떄문에 선정을 하게되었습니다.


공식 도큐먼트에 따라 잘 작성중이었으나, 한가지 문제점에 봉착했습니다.

Header.js
...

class Header extends React.Component {
 render() {
  return(
   <header>
    <Foo/>
   </header>
  )
 }
}
export default Header;


App.js
import Header from 'some_location';
...

class App extends React.Component {
 render() {
  retrun(
   <Bar>
    <Header/>
   </Bar>
  )
 }
}
export default App;

의 코드가 있었습니다.
App.js 에서 style-component 모듈을 적용시켜 보았으며,
적용을 원했던것은 Bar 와 Header 두 DOM element였습니다.

App.js 에서 class define 이전에 한줄의 코드를 작성하는데,
const Bar = style.div` color : red; `;

이 코드를 작성하게 되면, Bar DOM element의 css가 적용이 되었습니다.
Header element또한 같은 방법을 사용하면 된다고 생각한 저는 아래 코드를 작성했었고
const Header = style.div` color : green; `;

에러를 만나는데, 이미 Header는 선언되어있음 이라는 에러를 만나게 됩니다.

그도 그럴것이 App.js 에서는 Header.js 를 import 하면서 Header 라는 변수명을 가진체 나왔기 때문에였습니다.

그럼 이를 해결하기 위해서는 변수명을 틀리게 하면 된다 인데,
App.js 에서
import Header as myHeader from ' ... ';

를 하게 된다면
<Bar>
 <myHeader/>
</Bar>

로 바뀌게 될것이고, style 또한 인식하지 못하게 될것입니다.

이러한 이유로 해결방법을 20분정도 찾아보았으나 마땅히 맞는 이슈를 찾지 못합니다.

한참 골똘이 생각하다가, 문제점을 다시 되짚어 보다 엄청난 사실을 깨달았는데...

<Bar> 와 <Header> 는 동일한 타입이 아니었습니다. 이는 가시적으로 보기쉽게 정리가 되어있을뿐, Bar는 DOM element가 맞으나, Header의 선언부를 보게되면
<div>
  <Foo>
</div>

라는 element들이 모아서 Header라는 변수명을 갖고있을 뿐 이었습니다.
그러니깐 DOM element가 아니니깐 스타일링을 할 수 없던것 이었습니다;;

이런 당연하고 뻔한걸 놓치다니..

그렇다면 이제 해결법은 너무나 단순해 집니다.
Header.js 에서 <div>를 스타일링 하면 되는것 이었습니다..

2019년 4월 7일 일요일

Merge 와 Rebase 의 차이

https://backlog.com/git-tutorial/kr/intro/intro1_1.html 

에서 읽고 배운 글인데, 
급하게 실무에서 뛰던 당사에는 필요한 내용만이 급급한데, 
git 관련 검색하면 항상 top으로 나오면서도 크게 도움이 안되서 저 원숭이 마스코트에게 악감정이 있었습니다. 
어쨋든 지금은 잠깐의 휴식기도 생겼고, 같이 Git 기초 공부도 할겸 정독을 했고, 
상당히 도움이 되었지만, 여전히 초심자가 읽기는 힘들지 않을까? 하는 생각에 
나름대로 정리를 해서 올려봅니다.

Merge 와 Rebase 의 차이

기능적으로 merge와 rebase는 동일한 작업을 합니다. 
두 branch를 하나로 병합하는 기능을 작동합니다. 
하지만 몇가지 detail의 차이로 인해 두 기능이 나누어졌으며, 그 기능의 차이를 알아봅니다.

Merge

다른 branch 를 현재 banch에 합친다는점은 merge와 rebase 동일하나, 작동방식이 다릅니다.
전자인 merge의 경우의 합치는 방법은 기본적으로 fast-forward 방식으로 작동합니다.

이 방법은 HEAD master branch 에서 새 branch를 작업하여, marge 할 경우,
gir graph 상에서는, 하나의 master branch 변경 이력으로 변경처리가 되어 하나의 줄기만을 표시하게 됩니다.



(병합 대상 master 가 HEAD 인 경우의 병합 전)

(병합 후)

하지만 아래같은 상황에서는 이야기가 조금 달라지게 됩니다.
master branch 가 새 branch인 A branch가 merge 하려는 당시에 HEAD 가 아닌 상황입니다.


(병합할 master 가 HEAD가 아닌 경우)

이런 가지가 두개 가 된 경우에는, fast-forward 방식에 따라 A branch가 master에 합쳐진(merge)뒤, commit 되어야 합니다.


(merge 를 통해 아래와 같은 graph로 commint됩니다)



Rebase

위 이슈를 rebase 방법으로 봅니다.


( 위와 같이, 병합해야 할 master가 HEAD가 아닌경우 입니다 )


이번에는 rebase 병합을 하게 되면 아래와 같은 flow를 보이게 됩니다.


( rebase 방삭을 하는 경우 )

master branch의 위치는 변경되지 않으며, master branch에서 병합할 branch의 모든 commit 이력들을 차례로 commit 시킵니다.



( rebase 이후 상태, master의 위치는 병합 이전 상태에 머물러 있습니다 )

병합 대상의 branch는 graph 이력에서 사라지며, master graph로 흡수됩니다.
이때 master branch는 push 되지 않고 rebase 전 상태를 유지합니다.


( 다시한번 merge 하게되면 master가 HEAD를 보게 됩니다 )

이렇게 함으로써 최종 병합 작업이 끝이나게 됩니다.



결론적으로 merge와 rebase 의 차이는 ?

merge 의 경우는 병합할 branch들의 이력이 남아있게 되어, 디테일한 버전 관리가 가능하나, 더 복잡한 graph를 가지게 됩니다.
반면 rebase의 경우는 병합할 branch의 내역들을 master branch 하위로 변경처리를 하기떄문에, 결과적으로 보게 되는 graph는 단 하나의 줄기를 갖게 됩니다. 하지만 이는 merge의 graph와는 달리 명확하게 분석할 수 없게 될것 입니다.



훌륭한 Rebase 적용기

rebase를 사용할 수 있는 훌륭한 예를 하나 들어보겠습니다. 들어가기 앞서 이 또한 merge를 통해서 동일한 작업을 할 수 있습니다. 그러므로 merge를 사용하는가 / rebase를 사용하는거는 전적으로 팀의 결정에 따를것 같습니다.

B라는 이름의 master branch에서 추가 가능을 위해 O branch 를 만들어 작업을 하고있었습니다.

( O branch 는 develope branch로서 기능 추가 작업을 진행중 이었습니다 )

하지만 -항상 그렇듯- 서비스중인 B branch에서 중대한 문제점을 파악하게 되어 긴급 수정이 불가피해졌습니다.



(우리의 깃몽키는 B branch에서 헐레벌떡 X 브렌치를 따 버그픽싱을 합니다.)

X branch는 성공적으로 수정이 완료되어, B branch 에 merge 되어 퍼블리싱 됩니다.




다시 O branch 로 돌아와 기능추가작업을 계속 하려고 했으나, C branch의 커밋 내역이 O branch에서 또한 필요하다는 사실을 뒤늦게 파악했습니다.







( 이럴줄 알았다니깐! 그냥 O branch 작업하면서 X branch 작업 하면 될 정도로 작고 마이너 한건데, 일을 복잡하게 두번 만든단 말야.. )

이런 경우, 우리는 C branch 의 내역을 가져와야 합니다. merge 와 rebase 모두 가능하지만, rebase를 해 볼 수 있습니다.
O branch에서 C branch를 target으로 rebase를 합니다.








( Ta-Da! )

2019년 4월 1일 월요일

React.JS 공부노트 #2 proxy

official document를 통해 매우 쉽게 첫 프로젝트를 올리는대에 성공했으나,
back-end 와의 통신이 필요하기 때문에 몇가지 진입방법을 고민합니다.

첫번째는 매우 간단하게 API서버를 만들어 두 server(React front-end server and Express back-end serve) 를 운용하는 방법,
또 하나는 Express 서버를 중심으로 react를 render 시키는 법(server-side render) 이 있는것 같았습니다.


하지만 server-side render 의 경우는 손쉽게 도움을 받은 webpack, babel의 상당한 설정이 필요한 것으로 보여, 우선은 첫번째 방식인 API서버의 혼용을 선택!



그럼 이제 운용되는 서버는 두개가 되는데, 각각의 서버는 별도의 포트를 보유하게 됩니다. (혹은 별도의 서버 ip)
React에서 서버 API와 통신을 하기위해서는 몇가지 잘 알려진 방법이 있는데,
xhrHttpRequest , Ajax, Fetch 등이 있습니다.
이러한 통신으로 API서버의 주소를 알려주고 그 결과를 주고받아야 합니다.

하지만 맘편하게 바로 요청 url에다가 full path가 아닌 상태로 쓰게되면  API서버가 아닌 React서버에request를 날리게 됩니다. (/api/getUser 같은방식으로 말이죠).

이러한 문제를 해결하기 위해서는 full path만 있으면 문제없죠! ( http://foo.bar/api/getUser  처럼 말이죠)
하지만 라인을 한줄이라도 더 줄이고 더 쉽게 쓰기 위해서, 좀더 재미난 방식을 사용 할 수 있습니다.
바로 proxy 입니다.
React 서버에서 request가 들어오면 , proxy에서 먼저 middleware 처럼 요청을 낚아채어 작업을 할 수 있게 되는거죠.
그럼 우리는 full path url이필요없이 base domain url을 써 줘도 proxy를 통해 api서버로 요청을 분기처리 할 수 있게 됩니다.

이러한 작업을 위해서 기존의 프로젝트의 package.json 환경설정 파일에 새로운 kv쌍을 입력해 주면 됩니다
"proxy" : "your_api_server_URL"

하지만, 이렇게 할 경우 모든 요청을 proxy 를 통해 API서버로 분기시키게 됩니다.

만약 URL서버 하나가 아니고 또 다른 서버가 있고, 그 서버와의 통신또한 필요하다면?

그리하여 react에서는 조금더 자세히 설정할 수 있는 방법을 제시합니다.

http-proxy-middleware
이 모듈을 통해서, 우리는 조금 더 자세하게 요청을 분기처리할 수 있습니다.

이 모듈을 이용하기 위해서 두가지 작업이 필요로 합니다.
1. react프로젝트를 고급설정 할 수 있도록 eject 해야 합니다.

`npm run eject `

가장 쉽게 React 프로젝트를 생성하는 craete React app 을 통해서 만들어진 프로젝트는
Webpack, Babel 등의 설정을 기본적으로 해두어 패키징 한 상태로 제공합니다. 그 말은 우리가 해야할 설정들을 다수 대신 해 만들어놓은 것 입니다.

프로토타이핑이나, 공부를 할 때에는 크게 만질 일이 없을것이나, 추가적인 설정 혹은 라이브를 위한 단계 등에서는 우리는 Webpack이나 Babel의 설정을 좀더 디테일하게 잡아줘야 할 수 있는데, 이를 위해서 패키징되어있는 React 프로젝트를 언팩하는 코드입니다.
해당 명령어를 수행하면 언팩작업을 거치며 프로젝트 폴더에 수많은 새 코드들과 파일들이 생성이 됩니다.

2.http-proxy-middleware 설치
`npm i http-proxy-middleware --save`

이제 작업 준비는 끝이났습니다. http-proxy-middleware 모듈을 불러와 분기처리를 하기위해 새 js파일을 생성합니다. 위치는 React 프로젝트에서 default path 로 src/setupProxy.js 위치에 명시해 두었습니다만 따를 필요는 없습니다.

코드를 작성합니다
`
const proxy = requrie('http-proxy-middleware');
module.exports = app => {
 app.use( proxy('/context' , { target : 'your_api_server_url' } ) );
}
`
/context는 controller처럼 들어오는 요청의 url의 앞에 있는 string이 들어갑니다.
/api 라고 명시하고, target에 http://foo.bar/ 라고 세팅하게 된다면
/api/getUser 라는 요청을 React에서 보낼 경우 proxy를 통해
http://foo.bar/api/getUser 라는 url로 변형되어 url서버로 요청을 보내게 됩니다.

만일 /foo/getUser 라는 요청이 들어오게 된다면, /foo 라는 이름의 url을 proxy설정에 넣지 않았기 때문에, React 서버로 인지하여 처리하게 됩니다.
`
const proxy = requrie('http-proxy-middleware');
module.exports = app => {
 app.use( proxy('/context' , { target : 'your_api_server_url' } ) );
 app.use( proxy('/context2' , { target : 'your_api_server_url2' } ) );
}
`
위와 같이 한줄을 더 추가하게 된다면, url에 따라 또 다른 세번째 서버로 연결을 확장 시킬수 있습니다.

마지막으로 React에서 방금만든 js 파일을 mount 하기 위해서
./config/paths.js 파일에
proxySetup Key값의 value를 내가 만든 파일의 location으로 맞춰 주어야 합니다.

ReactJs 공부노트 #1


공부노트는 내 공부를 하다 내 생각을 적은것일뿐 이게 정답이 아니므로 이거보고 아 그런가 하고 그대로 배우면 당신은 바아보~



ReactJs는 VirtualDOM방식을  이용한 SPA 개발언어? 정도로 이해가 됩니다.
VirtualDOM이란 일반적인 HTML에서 그리는 DOM을 그리기 이전에 가상의 DOM영역에 먼저 연산한 뒤에, 마지막에 일반 DOM에 그리는 이중 버퍼와 같은 기능입니다.

예를들어 하나의 이미 렌더 됀 페이지에서 새 node들 3개를 추가하려고 하면,
일반적인 DOM에서는 각각의 node들을 추가하면서 연산과 렌더 두가지 작업을 모두 처리하게 됩니다.

하지만 리엑트에서는 가상 DOM에서 세 node들을 연산한 뒤, 단 한번의 렌더 작업을 거치게 됩니다.

이는 SPA 어플리케이션에서 상당한 퍼포먼스 gain을 보장할 수 있을것 으로 보이나, 개발자의 말에 따르면 크게 빠르지는 않다고 합니다. 따지고 보면 그럴것이 가상 DOM을 이중 버퍼로 쓰이는건데, 결코 빠를 수는 없죠.
다만 이 리엑트를 사용할 경우, 일반적은 DOM에서 치뤄야할 최적화를 위한 작업들을 모두 대신 맡아 처리해 줄수 있다는 점 입니다.

참조 출처



리엑트는 JSX문법을 사용하게 되는데, 이는 필수는 아닙니다. 하지만 JSX문법을 이용함으로써 훨씬더 수월하고, 최적화된 방법으로 코드를 작성할 수 있기 때문에 사랑받고 있습니다.


React의 객체 단위

React에서 가장 작읜 객체는 
element 입니다.

const element = <div> hello world </div>
이는 일반적인 DOM오브젝트와 달리 단순한 plain 오브젝트입니다.

이러한 엘리멘트는 모두 ReactDOM 에서 관리를 하기 때문에, root DOM 이라고 부릅니다.
이러한 생성된 엘리먼트는 아래의 코드를 통해서 DOM에 삽입이 가능합니다.
reactDOM.render(element, document.getElementById('root'));


이렇게 만들어진 element객체는 불변객체이므로, 생성된 다음 자식이나 속성을 변경 할 수 없습니다. 이는 특정 시점의 UI이기 떄문입니다, 그럼 UI의 내부값들이 바뀌어야 하는것 입니다.
물론 강제로 element의 객채의 속성이나 자식을 바꿀수 있습니다.
reactDOM.render() 를 통해서 말이죠. 하지만 실제로 사용중인 react앱에서 이러한 render()행위는 보통 한번만 사용됩니다. 올바른 업데이트 방법이 아닙니다.
UI를 변경하고, 그 side effect를 고려하는것 보다, 고정된 UI에 내부의 값들의 변형에 의해 일어나는 side effects들을 고려하는것이 더 값싸기 때문입니다.


그럼 UI내부의 값들을 처리해 주기 위해서는 무엇이 필요한가 인데, 그것은 바로
Component 입니다.

Es5방식으로 보자면 이러한 함수입니다.
function Foo ( props ) {
 return <div> { props.bar } </div>
}

하나의 인자값 props 에 있는 객체들을 통해서 reactElement를 반환하고 있습니다.

이제 이코드를ES6으로 변환하면
class foo extends React.Component {
 render() {
  return <div>{this.props.bar}</div>
 }
}
이 될 수 있습니다.


const element = <div> hello world </div>

이 코드는 React element를 DOM태그로 나타낸 것 입니다.
이제 우리는 Component로 React element를 표현 할 수 있습니다.

const element = <Foo bar="hello world" />

이는 Foo()함수에 bar 이라는 props 를 담아 실행하여,dom tag를 리턴하며, element에 reactElement를 입력받게 됩니다.

이러한 사용자정의 Component와 DefaultDOM태그와의 차이점이 있으며, 이를 잘 숙지해야 합니다.

defaultDOM태그는 항상 소문자로 시작하며,
Component태그는 항상 대문자로 시작합니다.

추가적인 몇가지 규칙이나 표현식은 이곳 에서 알 수 있습니다.

이러한 Component들은다른 Component들을 참조할 수 있습니다.

class Bar extends React.Component {
 render() {
  return(
   <div>{this.props.name}</div>
  );
 }
}
class Foo extends React.Component {
 render() {
  return(
   <div>
    <Bar name="a"/>
    <Bar name="b"/>
   </div>
  );
 }
}
ReactDOM.render( <Foo/>, document.getElementById('root') );

props 는 read-only 입니다. 이를 위반해서는 안됩니다.
실제로 초기 React에서는 props를 통한 값들의 제어에 관해 관여하지 않았으나, 이를통해 원하는 결과가 나오지 않기에, 새로운 정책으로 props의 사용을 제한하기 시작했다고 합니다.

그럼 변수들은 어디서 관리해야 하는거냐 하면, 바로

state 입니다.

이는 props와 비슷하지만 조금 다릅니다. props는 Component의 parameter로써 이용되었으며, state 의 경우는 Component가 생성될 때에 Constructure 에서 생성됩니다.

그리고 이 state또한 변경에 대해서 매우 확실한 경로를 통해서만 해야합니다.
setState(callback) 입니다.
반드시 callback형식으로 setState 함수를 사용해야만이, 실제로 state값이 바뀜과 동시에 render처리까지 될수 있도록 연결되기 때문입니다. 임의로 값을 바꾸는 행위는 render가 되지 않음을 명시해야 합니다.