2019 js every where 세미나 후기

js every where 을 참석하며
이번 세미나를 참여하며 생각하지 못한 부분에 대해서 여러가지를 배울수 있었습니다. 물론 중간중간 공사소음으로 인해 텐션이 푹 떨어지고 마지막에는 살짝 아쉬웠지만 만족스러운 세미나 였습니다. 그리고 마이크로소프트의 경복궁 풍경에... 놀라게 되었던 하루 입니다...
1. CRA 로 SSR 만들기 feat typeScript - 승형수
발표세션 예제 자료 : https://github.com/huskyhoochu/ssr-server
server-side Rendering 이란?
해당 요청 페이지에 대한 모든 HTML을 웹서버에서 전부 만들어 전송 하는 방식 입니다.
BUT 수많은 사람들이 하나의 서버에 들어오게 되면 그 연산에 대한 부담을 한정된 서버 자원으로 커버하기에는 부담이 있습니다.
client-side-rendering
최근 브라우저와 컴퓨터의 연상 성능이 증가하여 이런 연산을 브라우저와 컴퓨터에 넘기는 개념이 클라이언트 랜더링(Client rendering) 서버는 처음 접근시 커넥션만 연결해주면 됩니다. 최소한의 html 과 화면 전체 랜더링을 위한 js 만 필요로 합니다.
BUT **클라이언트 랜더링(Client rendering)**은 다음과 같은 단점이 존재합니다.
- 크롤러 들이 검색엔진 최적화를 위해서 들어올때 전부 랜더링 되어있지 않은 단점.
- 사용자의 초기 로딩 경험이 느리다는 단점.
단점 해결 방법
**초기 렌더링( SEO 에 필요한 )**을 마친 HTML + 일부 업데이트를 위한 JS 연산처리를 하게 되면 검색엔진 대응에 장점이 생기게 됩니다.
구현방법
react 패키지와 react-dom 이 따로 있는 이유는 서버에서는 ReactDomServer.rendertoString() 으로 html String 으로 변환 하기 위해서 입니다. 클라이언트에서 build/index.html 는 react 런타임 코드와 페이지 코드가 붙어있기 때문에 모든 리소스를 static 으로 서버에 제공하면 됩니다.
ReactDomServer.rendertoString() 으로 번들된 html 을 build/index.html 에 넣어주면 됩니다.
import fs from 'fs'; import express from 'express'; export default ( html: (req: express.Request) => { helmet: any, html: string, preloadedState: JSON }, htmlPath: string ) => (req: express.Request, res: express.Response) => { const result = html(req); fs.readFile(htmlPath, 'utf8', (err, data) => { if (err) { return res.status(400).send(err); } return res.send( data .replace( '<div id="root"></div>', `<div id="root">${result.html}</div>` ) .replace( '<title>React App</title>', result.helmet.title.toString(), ) .replace( '<script type="text/javascript">window.__PRELOADED_STATE__={}</script>', `<script type="text/javascript">window.__PRELOADED_STATE__=${ result.preloadedState }</script>`, ), ); })
Functional component SSR
** funtional 컴포넌트는** ssr 단계에서 만들어 지지 않습니다. 이를 원한다면 클래스 컴포넌트로 사용해야 합니다..
checkList
- 서버코드를 분리하자.
- asset-manifest.json - build 안에 담긴 보물
- typeScript를 사용하는 편이 좋은 이유
서버코드를 분리하자.
renderToString 함수가 서버 코드와 섞이기 때문에 bootstrap.js 파일을 추가로 작성해 각자의 코드를 독립된 환경에서 관리하여 유지보수를 깔끔하게 합시다.!
asset-manifest.json - build 안에 담긴 보물
build 내부에 모든 정보가 있기때문에 이를통해 실제 빌드되어 있는 정적파일의 주소를 알 수 있습니다. 이는 key 와 value 로 구성되어 있습니다.
typeScript를 사용하는 편이 좋은 이유
javaScript 로 작업할 경우 바벨 같은 트랜스파일러가 있어야 합니다. 또한 각종 babel 플러그인 설정도 해주어야 합니다. 그러나 이를 typeScript로 구현하면 타입의 안정성 + 컴파일을 통해 commonJS 파일로 쓰기 때문에 바벨을 사용하지 않아도 되는 장점이 있습니다. 또한 typeScript 작업시 conf 파일을 새로 만들어야 하는데 이는 기존 CRA 파일을 복사해서 몇가지 ts용 설정만 추가해주면 됩니다.
extra 필수 패키지 설치 이슈
- redux : 서버사이드 단계에서 데이터를 미리 주입하고 싶을때만 사용하는것을 추천드립니다. index.html 에 인라인으로 window 전역 store 객체 삽입 - 이는 공식문서에서 권하는 방식입니다.
소감
기존에는 SSR 을 직접 하려다 Next.js 라는 좋은 대체제가 있어 끝까지 파지 않았던 영역인데 이번 기회로 좀더 여러가지 팁과 핵심 개념들을 알게되어 만족스러웠습니다.
2. 나의 javaScript 사용기
첫 js 는 접한시기
2014년 여름 퇴사를 하며 4번째 맴버로 스타트업에 입사. (최초 개발자-컴퓨터 관련된 모든것을 해주..는..) 이전에는 sk플래닛에서 Spring과 앵귤러로 사내 표준 시스템을 만드는 일을 하는 작업 진행 이후 스타트업에 입사해 javaScript를 기반으로 서비스를 제작.
각종 자바스크립트 기술 적용을 언제 무엇을 해보았을까.
- 16년 3월 huiseoul v1 에 React(15.0v) 적용
- 16년 8월 reactNative로 6주만에 IOS APP 제작 (Mobx, graphQL 이용) - redux를 안쓰고 Mobx 를 쓴 이유는 redux가 어려워서..
- 16년 9월 **lambda node.js **기반 적용
- 16년 11월 reactNative에 TypeScript 적용
- 18년 4월 serverless backed architecture 적용 , lambda, node.js 기반.
javaScript 에 대해 느끼는것
Fast
- 물론 성능측면에서의 실행속도도 빠름.
- 자바스크립트는 스크립트 언어이기때문에 브라우저만 있어도 바로 원하는 대로 코딩 가능 또한 내가 무엇을 만들때 이에 대한 피드백이 아주 빠르다.
- 이런 이유로 짧은 시간에 많은 사람들의 주목을 받은것이라 생각.
Great
- TS, RN, React, Mobx 등등 수없이 많은 생태계 환경이 구축되어 있음.
- 이러한 많은 것들이 나온 이유는 수많은 사람들이 js 생태계 로 들어오게 되었기 때문.
We
- 잘 구축된 생태계를 튜토리얼 한두번 해본정도로 js를 잘하는것 같다고 생각을 해선 안된다. 생태계가 대단한 것이지 이것을 깔짝 거린것으로 대단한 사람인건 크나큰 착각.
- 빠르게 변화하는 js환경에서 적응하는것이 전부가 아니다. 이를 정확하게 이해하고** 프로덕션 레벨에서 어떻게 유지할지** 디버그는 어떻게 할지? 이런 deep 한 사고를 먼저 하는것이 핵심!
PASSION x GROWTH
- 많은 참여자들이 수많은 좋은 생태계를 구축한것처럼 개인으로서 열정을 가지고 수많은 고난을 겪어 나가며 성장하는것이 중요.
- 적응하는것도 중요하지만 문제를 해결하는 능력을 키워내는것이 더 중요.! 즉 시작과 끝이 중요한게 아닌 과정에도 큰 포커스를 두어야 한다.
소감
기술적인 세션은 아니지만 당연하지만 놓치고 있던부분에 있어 다잡는 기회가 되었고 경험에서 나온 조언과 여러 이야기를 들으며 생각이 많아지는 세션이였습니다. :)
개인적으로 게속 대기업에서 있다 스타트업으로 완전 바꾸게 된 계기가 궁금하였습니다.
answer : 대기업에서는 사실 재미있었으나 선입연구원 시절 팀장님이 너무 힘든 모습들을 보며.(정치하고 아부하고 술먹고 하는게...) 전환 결심 ps. 대기업 팀장 도 엄청나게 잘해야 갈수 있...다고 하셨습니다.
3. serverless 프레임워크로 nuxt 앱 배포하기
git 예시 주소
https://github.com/wan2land/js2019-serverless-nuxt
nuxt framework
Nuxt : vue 로 만들어진 프레임워크.
Nuxt 옵션
Nuxt 는 다음과 같은 옵션을 제공합니다.
-
single page application : 서버렌더링 필요없이 정적 호스팅용 옵션 - 거의 사용하지 않습니다.
-
statically generated : 미리 실행해서 html 생성, 정적 호스팅에 업로드 , seo 가능. 그러나 api 나 로그인 이 부분이 작업하기 어렵습니다.
-
server side render : 기본값으로 node.js 로 실행하는 방식
1,2 번의 경우 s3 호스팅 만으로 배포가 가능하지만 3번은 node.js 를 직접 돌리기 때문에 ec2 등으로 배포 가능 합니다.
serverless framework
aws, gcp , azure 에서 돌아가는 서비스 + 인프라 구성을 쉽게 구성 할 수 있습니다.
서버리스의** http Event** 는 api gateway를 사용하는데 보통 api용도로 사용합니다.
왜 serverless 에 nuxt를 올려야 하는가 ?!
- 과금은 사용한 만큼만 : ec2의 최소단위는 머신단위이지만. 서버리스는 함수를 요청한 만큼만 과금
- auto scale : 오토스케일링이 엄청 편함
- 무중단 배포 : 로드밸런스, 블루,그린 배포 전략 같은 것이 없어도 배포하면 바로 반영
- Nuxt serverless 구현의 편리함
정적 파일 처리
nuxt의 빌드된 파일을 정적파일로 따로 관리하게 되면 다음과 같은 이점이 생기게 됩니다.
-
비용절감: 기존대비 90% 가까운 비용 절감 효과.! s3 쓰자.
-
용량 장점 : 람다에서 소스코드 최대용량은 250MB 그러나 이미지 폰트파일용량이 엄청 크기때문에 이를 S3로 처리해 용량을 최소화!
-
무중단 배포(?!) : 아래와 같은 이슈가 자동으로 해결 됩니다.
정적파일 처리 안한상태에서 배포시 문제점
Nuxt 에서 필요한 부분만 js 를 불러오기 때문에 다음과 같은 현상이 존재합니다.
- 사용자가 웹사이트 호출 (버전1)
- html, js, css 로드
- 개발자가 웹사이트를 개선해서 버전2 배포 (사용자가 아직 버전1 웹사이트에 머물러있음)
- 사용자가 버전1 웹사이트에서 다른 페이지 클릭
- 버전1 에 대한 추가 js 호출
- 이미 웹사이트는 버전2 로 변경되었기에 에러 발생 - 새로고침 하면 해결되긴 합니다.
정적파일 배포는?
정적파일을 따로 관리하게 되면 배포할때마다 정적파일을 따로 올려주어야 하지만 serverless-nuxt 플러그인을 사용하면 정적파일을 배포시 자동으로 업로드 해주게 됩니다.!
s3에 클라우드 프론트 적용
s3는 특정 리전에 종속되면 브라질에서 호출해도 해당 리전에서 가저오기 때문에 클라우드프론트 로 적용해주면 가장 최적화된 CDN으로 호출 해주게 됩니다.
lambda 리전 종속
사실 lambda도 리전에 종속되기 때문에 이를 edge를 적용해 lambda도 위의 cdn 최적화를 할 수 있습니다.
https
api gateway는 https 만 지원합니다. 즉 http 호출시 https 로 Redirect를 하지 못하기 때문에 https를 꼭 적어주어야 들어갈 수 있습니다. 원래 아파치나 nignx 를 사용하면 리다이렉트 시키면 되는 간단하지만 serverless 는 이를 지원하지 못했습니다 .
그러나 2019. 06. 12 일 부터 ALB event 를 통해 지원하도록 변경되었습니다.!!
소감
정적파일과 cdn 을 이용한 각종 비용,트래픽 절감과 자동화에 대한 플러그인 지식등 여러면에서 도움이 되었습니다.
궁금증.
serverless 의 과금방식은 콜수 단위인데 그렇다면 콜수가 엄청 많을 경우는 비용측면에서 어떤게 유리할까..? 이건 추후 알아보도록 하려 합니다.!!! 질문하려했는데.. 너무 질문자가 많아서 시간이 지나버려서 ...ㅠㅠㅠ
4. react hooks + ts + funtional = 아름다움
처음 제작은 16.5v 클래스컴포넌트 기반에 ts를 적용하지 않고 재사용 로직은 HOC 을 사용해 mvp 모델을 구현하였으나 유지보수에 여러 어려움이 있어 다음과 같이 스펙을 변경 하였다고 합니다.
- 버전 :16.8v
- 컴포넌트 :functional 컴포넌트
- type : typeScript
- 중복로직 : hooks
class component
class MyComponent extends React.Component{ constructor(props) { super(props); this.state = { count: 0 }; } componentDidMount(){ alert(this.props.name); } onPress(){ this.setState({count:this.state.count + 1}) }; render(){ return( <view> <Button onPress={this.onPress()}> <Text>count: {this.state.count}</Text> </Button> </view> ) } }
functional 컴포넌트
class MyComponent = ({name}) => ( const [count, setCount] = useState(0); // 0은 count의 초기 값 <view> <Button onPress={{/*this.onPress*/}}> <Text>count: {/*this.count*/}</Text> </Button> </view>
- functional 컴포넌트는 결국 함수 이기때문에 함수 내의 로직은 렌더링이 수행될떄마다 매번 계산됩니다. 때문에 무거운 작업은 functional 컴포넌트에선 작업하면 속도면에서 아주 좋지 않습니다.
- functional 컴포넌트 안에 새로운 함수를 만들면 게속 생성, 삭제하는 오버헤드가 발생해서 사용하지 않는 것을 추천드립니다.
- 함수형 컴포넌트에서는 this.prop 으로 가져올 수 없습니다.
hooks
class MyComponent = ({name}) => ( const [count, setCount] = useState(0); // 0은 count의 초기 값 <view> <Button onPress={{/*this.onPress*/}}> <Text>count: {count}</Text> </Button> </view>
- useState는 초기값을 인자로 받고 getter 와 setter를 담은 배열을 리턴합니다.
- useState의 생성자에 함수를 전달하면 최초 한번만 연산이 수행됩니다.
- ** 모든 hook**은 함수의 최상위 레벨에서 호출되어야 하므로 함수 안에서 호출 하면 안됩니다..
const init () => { const [count, setCount] = useState(0); } // x
hooks useCallback
class MyComponent = ({name}) => ( const [count, setCount] = useState(0); // 0은 count의 초기 값 const onPress = useCallback() => setCount(count+1),[count]); <view> <Button onPress={{onPress}}> <Text>count: {count}</Text> </Button> </view>
- useCallback은 호출된 함수, 재계산을 위한 의존성 목록을 인자로 받습니다.
- useCallback에는 async 함수도 지원합니다.
- useCallback 2번째 인자로 빈배열 전달시 콜백 함수는 최초상태로 고정되고 다음에는 재계산되지 않습니다..
hooks useEffect
class MyComponent = ({name}) => ( const [count, setCount] = useState(0); // 0은 count의 초기 값 const onPress = useCallback() => setCount(count+1),[count]); useEffect(() => {alert(name);},[]); <view> <Button onPress={{onPress}}> <Text>count: {count}</Text> </Button> </view>
- useEffect의 두번째 인자가 빈배열이면 componentDidMount 에 해당 됩니다.
- useEffect 보다 렌더링이 먼저되기때문에 useEffect 로 초기값을 정하면 안됩니다.
- useEffect 에서 리턴을 함수로 하게 되면 이는 componentWillUnMount 에 해당 됩니다.
- useEffect에서 2번째 인자를 정하면** 해당 인자**가 변할때 마다 호출되는 componentDidUpdate 에 해당 됩니다.
useEffect(() => {alert(name);},[name]); // name 의 componentDidUpdate 효과
- useEffect 의 두번째 배열을 빈배열로 주는것와 아에 안주는 것은 아에 다릅니다. 아에 안주게 될 경우 함수는 렌더링 될때마다 재호출 됩니다.
- useEffect 에서는 **async await **지원을 하지 않습니다. 단 async await 을 사용하는 함수를 래핑해서 사용하는건 가능합니다.
ts
사실 타입스크립트의 경우 팁 보단 튜토리얼의 느낌이 강해 따로 정리하지 않았습니다.
소감
개인적으로는 ts + hooks + functional component 3가지를 통해 실무에서 어떠한 이슈가 있었고 이를 효율적으로 작성하는 방식? 을 기대했지만 약간 각각의 가이드 설명 같은 느낌을 받아 아쉬었습니다. 물론 각** ts ** ,**hooks **, ** functional component **에 대해선 정말 간결하게 잘 설명해 주셨습니다.!