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가 되지 않음을 명시해야 합니다.