4. SPA
single-page application
The page does not reload at any point in the process,
nor does control transfer to another page,
although modern web technologies can provide the perception
and navigability of separate logical pages in the application.
63. PRE-RENDER
Search engines and social networks are always trying to crawl your pages,
but they only see the javascript tags...
it render your javascript in a browser, save the static HTML,
and you return that to the crawlers!
64. 검색엔진 로봇
(crawler)
정적인 페이지를 엔진에 제공
js를 지원하지 않는 검색엔진을 위해서 제공하는 페이지이다.
node서버에서 정적인 페이지를 만들어
검색엔진이 데이타를 가져갈 수 있도록 한다.
Node server
/home
image
link
HTML
meta
싱글페이지를 왜 REACT로 개발했는지 이유를 간단하게 살펴보고
리액트와 싱글페이지가 만났을 때 동작하는 방법을 알아볼꺼예요.
그리고 실무에서 고민하게 되는 것들을 같이 살펴볼게요.
마지막으로 프리렌더를 리액트가 어떻게 지원해주는지 살펴보겠습니다.
리액트를 사용한 싱글페이지의 구조
싱글페이지 어플리케이션이란
페이지를 다른 페이지로 이동하지 않고,
사용자의 인터렉션에 따라 컨텐츠를 제공하는 모던웹 기술입니다.
간단하게 말해도 한페이지에서 다양한 컨텐츠로 update해서 보여준다는 이야기죠.
그래서 url 맵핑해주는 router부터 data를 변경하는 부분까지 FE기술로 구성하게 돼요.
때문에 JS렌더링을 못하는 검색엔진로봇이 오면 정보를 하나도 못가져가는 치명적인 단점이 있죠.
리액트는 싱글페이지가 해야할일들을 다 해주지 않아요.
View만 제공해주죠.
다들 리액트는 다름 프레임워크보다 사용하기 쉽고, 공부 많이 안해도 된다고 하잖아요.
그 이유가 바로! View만 제공하기 때문에 공부할 부분이 적어요. 조금만 제공해주니까 당연히 다른 프레임워크보다 공부할 부분이 적고 쉽죠.
예를 들어 사용자가 컨트롤하는 인터렉션에 따라서 많은 뷰와 모델등 이에 따라서 상호작용이 많이 일어나야한다면
다른 프레임워크를 사용하는게 좋아요.
하지만
모델이나 컨트롤러의 역할이 작거나 해당프로젝트에서 익숙한 라이브러리를 사용할 수 있다면
리액트는 가볍고 좋아요.
----- Meeting Notes (15. 11. 14. 10:55) -----
제가 만들 어플리케이션은 지식블로그 이기때문에
컴트롤러나 모델같은 것이 할 이 많지 않아요.
다른 프레임워크보다 공부비용을 최소화하면서
컴포넌트화하고 DOM 성능을 향상시키는 장점을 얻을 수 있는거죠.
그리고 싱글페이지를 개발할때 제일 고민되는 부분을 해결해 줘요.
Js 렌더링을 못하는 검색엔진 로봇들을 위한 정적인 페이지를 따로 제공해야하는데요.
서비스용으로 만들어놓으면 서버사이드에서도 렌더링이 가능해요.
바로
검색엔진 대응하기가 쉽다는거죠.
리액트를 사용한 싱글페이지의 구조
프록시 설정부터 페이지 구성, 검색엔진 대응까지 총 5단계로 이루어지는데요.
여기서 react를 사용하는 부분은
UI단위를 컴포넌트화하는 4단계,
검색엔진을 위한 static 페이지를 만드는 pre-render 단계입니다.
딱 두 단계뿐입니다.
이게 무슨말이냐…
잘모르시겠죠?
각 과정을 살펴볼게요.
제일 먼저 할일은 프록시 설정인데요.
User agencyd에 따라서 제공할 서비스를 구분해요.
일반유저와 검색엔진 로봇을 구분하는데
일반유저가 왔을대는 어드민이나 실 서비스를 제공하구요.
로봇이 왔을때는 로봇도 정보를 제대로 가져갈 수 있도록
노드에 서버를 띄워 정적인 프리 렌더 페이지를 제공합니다.
그 다음에 할일은
각각의 서비스가 가지고 있는 url에 따라서 어느 페이지를 보여줄지 페이지정보를 가지고 있는 js파일들을 연결해 주는 일입니다.
결국 서비스의 모든 url 맵핑이 담긴 파일입니다.
서버 대신 클라이언트에서 javascript를 사용해 라우팅 설정을 핸들링하는 것이죠.
실제 코드를 살펴볼게요.
Url따라 페이지를 구성할 js를 실행시키기도 하지만
url에 포함된 파라미터나 쿼리도 매개변수로 전달하여 페이지의 옵션으로 사용할 수 있어요.
이렇게 각 url에 따라서 어떤 화면으로 연결시킬 것인지 정하는 역할입니다.
화면을 구성할 컴포넌트를 모으고
컴포넌트에서 사용할 data를 주입하고
컴포넌트간에 이벤트를 주고 받아야한 다면
그 해당 이벤트도 여기서 지정하는 역할이죠.
만들어 놓은 컴포넌트를 어디에 놓을지 정하고
어떤 데이타를 사용할껀지 조립하는 과정이라고 보면 될 것 같아요.
Page controller는 페이지마다 하나씩 있어요.
컴포넌트에 필요한 아이들을 하나로 묶어서 커스텀 태그로 사용할 수 있도록
리액트 컴포넌트를 만들어요.
JSX문법을 사용해서 template에 바로 데이터를 주입하고 이벤트도 바인딩 할꺼예요.
Template을 로드하고
이벤트를 바인딩 시키기위해서 요소를 하나하나씩 찾아서 캐쉬하는 일이 생략돼죠.
리액트 컴포넌트로 만들었으니
재사용성과 DOM성능에 큰 기대를 걸어볼게요!
자 이런 과정들을 거쳐서 페이지를 구성하게 돼요.
페이지에서 필요한 컴포넌트들을 모아주기만 하면 끝납니다.
보통 싱글페이지 개발과 다른 점이 하나있어요.
Data만 변경하고 DOM의 어디를 업데이트하고 제거, 추가해야하는지는 제가 안할꺼예요.
변경되는 부분은
React가 알아서 update할 테니까요.
그럼 컴포넌트들만 만들어 놓으면 페이지마다 가져다 쓰기만 하면 된다는 이야기.
4가지 정도만 알아볼게요.
제일 먼저 해야할일은 컴포넌트를 나누는 일이예요.
어떻게 나눌까요?
** d2화면을 보여주고 각 컴포넌트 구성 설명.
제가 사이트를 하나 보여줄껀데요.
어떻게 화면을 나눌지 한번 생각해보세요.
크게 나눌까요? 데이터 연관관계에 따라서?
아니면 여기저기 다 사용할지도 모르니까 확장성을 생각해서 조각조각 나눠볼까요?
안돼요. 그럼 그냥 부트스랩처럼되버려요.
디자인이 나오잖아요.
페이지 디자인은 이미 헤더, 컨텐츠, 풋터로 나눠져 있고,
기능별로 이쁘게 그려져서 나와요.
디자인 상의 UI단위로 크게크게 먼저 나누세요.
그다음에 각 관리해야하는 상태가 생기면
그때 상태 단위로 컴포넌트를 하나더 나누세요.
헤더를 예를 들면
검색에 토글이 하나 생기고,
메뉴를 누르면 그에 해당하는 상태가 더 생겨요.
그때 나누는 거죠. 각 UI에서 관리해야할 상태가 생길때요.
어떻게?
크게크게 보이는대로.
그 다음 관리해야하는 상태가 생긴다면 그때 한번더 나누자.
그리고 여기저기 재사용하려고 욕심내서 쪼개면
가져다쓰기가 불편하니까
그건 주의하자.
이 고민은 js개발자가 마크업 담당자가 따로 있다면 더더 고민하게 되는일이예요.
잘 못하면 내가 나눈 컴포넌트랑 마크업구조가 달라서 협업이 안되는 상태가 발생하거든요.
한 컴포넌트에 마크업 + 이벤트 + data를 다 넣을건데
마크업 구조랑 내가 나눈 컴포넌트랑 다르면 망함이예요.
수정 요청도 한두번이지… 미리 협의하시면 협업에 도움이 돼요.
다른건 몰라도 규칙중에 이것만은 마크업 담당자에게 꼬옥 전달해주세요.
한 태그에 두가지 정보가 들어가는 경우, text + text라도
<span> 태그로 감싸달라구요.
안 그러면 스타일이 틀어지는 경우가 발생해요.
예를들면 한 문장으로 표현되지만 목적이 달라 두개의 데이터를 이어 써야할 때 주의하자.
또하나 html에서는 닫지 않아도 되는 태그들도 selfclose를 해줘야합니다.
이런식으로 간단한 JSX문법이지만 없으면 error나요.
이벤트나 어트리뷰트를 넣었는데 동작하지 않는다면 이걸 의심하세요.
규칙을 지키지 않아도 에러는 나지 않지만
기대한 이벤트나 스타일이 나타나지 않을 수 있어요.
Event, 어트리뷰트는 카멜표기법으로 표기합니다.
리액트뿐만 아니라 FE의 기능이 많아지면서
모델뿐만 아니라 view의 모델의 관리도 필요해졌어요.
이처럼 리액트에서는 이 둘을 구분해서 관리하도록 state와 props를 제공하는데요.
상태값은 state로 관리합니다.
컴포넌트, 개인의 data, 자신의 생태 값이예요.
사용자가 화면을 동작할 때 화면에 나타나는 상태값 같은 거죠.
내 개인의 상태이기 때문에 변경도 가능해요.
주의할 점이 있어요. Render함수안에서는 setState호출이 불가능해요.
왜냐면 setState를 호출하면 render를 호출하고 가상돔이 비교해서 update하는 방식이거든요.
서로 참조가 일어나 문제가돼요.
자 이번엔 부모에서 넘어오는 값이예요.
Ajax 응답으로 넘어온 도메인 값이기도 하고,
함수를 전달할 수도 있어요.
이건 자식 컴포넌트가 할일을 하고
부모가 할일을 전달 받을때 사용해요.
그림으로 살펴보면
Props를 통해 도메인값과 함수를
Component에 주입시킵니다.
그리고 state는 컴포넌트가 가지고 있는 값이 되는거예요.
자 그럼 예제를 살펴볼게요.
Value따라서 이메일이 유효한지 컴포넌트 자체적으로 확인하고
그 유효성에 따라서
ajax 콜 or 에러 메시지 state 값으로 화면을 업데이트할테구요.
Props로 전달받은 부모에게 알립니다.
이벤트가 일어났다고
이렇게 이벤트를 전파하고
부모는 서버에 구독하기를 요청합니다.
서버에서 전달받은 값으로 “중복”, “성공”, “실패”는 props로 주입되고
이에 따라 상태 메시지를 표현한다.
한 컴포넌트의 이벤트가 발생함에 따라서 다른 컴포넌트에서도 해야할 일이 있을 수 있어요.
예를 들면 메뉴와 검색의 토글이 되는 경우처럼요.
메뉴가 열리면 검색이 닫히는 것처럼요.
첫번째는 자기가 할일을 그냥 이벤트 바인딩 시킵니다.
똑같이….
검색의 활성화/ 비활성화의 값, 즉 상태값을 변경하고
자기 할일을 합니다.
먼저 자기가 할일을 다하고
부모에서 넘어온 props를 통해서 부모에게 나 toggle 했어.
너도 할일해~ 라고 부모의 이벤트를 실행합니다.
부모로부터 도메인데이타와 처리해야하는 함수를 전달받고,
각 컴포넌트는 자기의 상태를 관리하게 돼요.
그리고 자기일을 다 마치면 부모에게 props로 전달받은 이벤트를 함수를 실행시키는 거죠.
자. 좋아요 고민고민해서 다 만들었어요.
리액트에 기대했던 것들이 얼마나 도움이 됐는지 볼까요?
컴포넌트를 단위별로 이쁘게 만들어 놓고
페이지에서 가져다 쓰기만 하면 된다고 했어요.
그런데 재상용 안되는거 아니야? 손이 더 많이 가는거 아니고?
Home에서 필요한건 7개의 컴포넌트예요.
커스텀 태그를 하나씩 불러옵니다.
7개를 불러왔고 여기에 각각 데이터와 이벤트를 넣은거예요.
일단 컴포넌트를 불러와 사용은 했네요.
다음 페이지에서는 5개가 필요하겠네요.
그럼 페이지에서도. 만들어놓은 컴포넌트 5개를 가져옵니다.
재사용성 어때요?
싱글페이지를 구성하면서 변경되는 부분을 개발자가 하지 않고 리액트가 해줍니다.
이것만 해도 복잡성이 떨어지고,
누가 봐도 어떻게 페이지가 구성되었는지 한눈에 알아볼 수 있어요.
어떠신가요? 한번 써볼만 하죠?
싱글페이지 어플리케이션 개발과정 중 마지막 단계인 prerender 가 남았는데요.
어떻게 할까요? Static 페이지를 따로 개발해야할까요?
React로 어떻게 구성하는지 알아볼게요.
진짜 만들어 놓은 컴포넌트의 재사용이 편해요.
모델과 이벤트만 주입시켜서 사용하면 되거든요.
제일 기대했던 가상dom의 성능은 얼마나 될까요?
얼마나 변경되는 부분만 자알~ 바꾸는지 살펴볼게요.
크롬툴을 보면 변경되는 부분이 표시가 돼요.
어때요?
변경되는 부분들을 잘 바꿔주는것 같아요.
그런데 놀라운 사실이 있어요.
저는 가상돔이 해봤자 컴포넌트 단위로 업데이트를 시켜줄지 알았어요.
생각보다 너무 똑똑해요.
Props나 state값이 변경되면 컴포넌트 전체가 변경되는 것이 아니고
DOM은 형태를 유지하고 진짜 변하는 곳만 바꿔줘요.
변경된 컴포넌트를 전체적으로 다시 렌더링하는 것이 아니다.
변경되는 data만 바꾸거나 해당 부분의 DOM의 삭제추가를 한다.
라이브러리를 사용하고 싶은데 어디에 초기화하죠?
컴포넌트가 업데이트될 때 라이브러리 함수도 호출해야하는데요….
리액트 툴.
Props 데이터로 바인딩해서 템플릿을 보통 출력하기 때문에 default값이 없거나 사용해야하는 값이 없으면
컴포넌트 자체가 출력이 안돼요.
그럼 하나씩 디버깅을 해야하는데 이런 귀찮은 상황을 사전에 방지하기 위해서 propTypes라는 타입을 지정할 수 있어요.
미리 컴포넌트에서 사용할 데이터나 전달할 이벤트를 타입을 지정하는 거예요.
기본적인 string, number 등 타입 외에도 필수값이나 메시지도 지정할 수 있어요.
이건 개발자 모드에서 warning으로 체크해죠.
https://facebook.github.io/react/docs/reusable-components.html#prop-validation
리액트를 사용한 싱글페이지의 구조
Google 검색엔진을 제외하고 다른 로봇들은 js를 렌더링하지 않고 static한 페이지의 정보만 가져가요.
동적인 데이터를 미리 불러와 검색엔진이 사이트에 접속했을때 정보들을 가져갈 수 있도록 해주는 렌더링을
prerender라고 하는데요.
User agency가 로봇이라면 node 서버로 보내요.
로봇이 데이터를 가져갈 수 있도록 정적인 페이지를 보내는 거죠.
아니예요.
리액트는 동일한 컴포넌트를 서버사이드와 클라이언트 사이드 렌더링을 다 제공해줘요.
똑같은 컴포넌트에서 동적인 핸드링이나 이벤트를 제외하고 정적인 페이지로 만들어주는 똑똑한 아이죠.
브라우저에서는 사용자가 정보를 이쁘고 자알~ 볼수있도록 구성하고
리액트가 dom을 컨트롤할수 있도록 id도 포함됩니다.
이 함수를 이용하면 서버사이드 렌더링이 가능하지만
브라우저 이벤트는 자동으로 제거가 안돼요.
Node에서 브라우저 이벤트를 호출하게되면서 오류가 발생하는거죠.
이 특징을 활용하면 이 이슈를 해결할 수 있어요.
리액트는 컴포넌트 사이클에 맞춰 함수들을 제공하는데요.
Dom 생성, update, dom 제거 에 따른 함수들이 있어요.
여기서 서버사이드 렌더링은 딱!
렌더링까지만, updating 전까지만 함수들을 호출해요.
브라우저 이벤트를 서버사이드에서 호출하는 함수에서는 사용을 안하는 방법이예요.
compononentWillMount()후에 발생하는 함수에
브라우저 이벤트를 호출하는거죠.
만약에 꼭!!!! 이전에 호출을 해야한다면
클라이언트 사이드인지 서버사이드인지 옵션값을 통해서 분기를 할 수도 있어요.