-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontent.json
1 lines (1 loc) · 130 KB
/
content.json
1
{"meta":{"title":"Hexo","subtitle":null,"description":null,"author":"John Doe","url":"https://juuuuuuuuuuuuuu.github.io"},"pages":[],"posts":[{"title":"React.memo","slug":"react-memo","date":"2020-03-30T03:50:12.000Z","updated":"2020-03-30T05:22:41.120Z","comments":true,"path":"2020/03/30/react-memo/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2020/03/30/react-memo/","excerpt":"","text":"React.memo는 react에서 제공하는 hook이다.성능을 향상하는데 도움이 되는 hook이다. React.memo()React는 이전 렌더링 결과와 비교하여 DOM에 업데이트를 결정한다.렌더링 결과가 이전과 다르다면 DOM을 업데이트 한다. React.memo()는 기존에 클래스 컴포넌트에서 shouldComponentUpdate가 내재되어 있다고 생각하면 된다. (PureComponent와 같음)React.memo()로 래핑을 하면 컴포넌트를 렌더링하고 결과를 메모이징(Memoizing)한다.다음 렌더링이 일어날 때 props가 같다면 메모이징(Memoizing)된 내용을 재사용한다. 12345678910111213141516171819202122232425export function NameCard({ name, year }) { return ( <div> <div>name: {name}</div> <div>year: {year}</div> </div> );}export const MemoizedNameCard = React.memo(NameCard);// 첫 렌더이다. React는 MemoizedNameCard 함수를 호출한다.<MemoizedNameCard name=\"juyoung\" year=\"27\"/>// 다시 렌더링 할 때 React는 MemoizedNameCard 함수를 호출하지 않는다.// 리렌더링을 막는다.<MemoizedNameCard name=\"juyoung\" year=\"27\"/> props로 name, year이 변경되지 않으면 다음 렌더링 때 메모이징 된 내용을 그대로 사용하게 된다.메모이징을 재사용함으로써 리렌더링할 때 가상 DOM에서 달라진 부분을 확인하지 않아 성능상의 이점을 갖게 된다. 사용해야할 때??같은 props로 렌더링이 자주 일어나는 함수형 컴포넌트이다.위에 예시에 있는 코드가 해당한다. 사용하지 말아야 할 때??props가 변경되는 경우에는 쓸모가 없다. 1234567891011121314151617181920function MyApp({ store, cookies }) { return ( <div className=\"main\"> <header> <MemoizedLogout username={store.username} onLogout={() => cookies.clear()} /> </header> {store.content} </div> );}function Logout({ username, onLogout }) { return <div onClick={onLogout}>Logout {username}</div>;}const MemoizedLogout = React.memo(Logout); () => cookies.clear()는 MyApp 컴포넌트가 렌더링될 때마다 새롭게 props를 생성하고 있다.그래서 React.memo를 사용하더라도 계속 재렌더링하게 된다.이러한 경우에는 useCallback()을 이용해서 콜백 인스턴스를 보존시키는 방법이 있다. 1234567891011121314151617function MyApp({ store, cookies }) { const onLogout = useCallback(() => { cookies.clear() }, []); return ( <div className=\"main\"> <header> <MemoizedLogout username={store.username} onLogout={onLogout} /> </header> {store.content} </div> );} React.memo를 사용할 때는 props로 사용하는 콜백함수가 이전과 동일한 콜백함수 인스턴스를넘기는지 확인해야한다.","categories":[],"tags":[{"name":"react, javascript, hook","slug":"react-javascript-hook","permalink":"https://juuuuuuuuuuuuuu.github.io/tags/react-javascript-hook/"}]},{"title":"hiddenclass","slug":"hiddenclass","date":"2020-02-01T12:30:49.000Z","updated":"2020-02-04T07:48:09.139Z","comments":true,"path":"2020/02/01/hiddenclass/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2020/02/01/hiddenclass/","excerpt":"","text":"자바스크립트는 class의 개념이 없고 prototype이라는 개념이 있어 객체간의 상속이 가능하다.class 기반의 언어는 클래스에 속한 객체들이 모두 같은 필드 구조를 가진다.어떤 객체가 특정 프로퍼티에 접근할 때 어떤 변수에 접근하는지, 메모리의 어느 위치를 참조하면 되는지 컴파일 과정에서 미리 알 수 있다.offset값을 통해 바로 멤버변수에 접근가능하다.그러나 자바스크립트에는 클래스라는 개념이 존재하지 않으며 동적인 언어이기 때문에언제 바뀌는지 알 수 없어 각 객체들마다 필드구조를 각각 가지고 있어야한다.(중복되는 객체가 100개여도 100개의 필드구조)그러므로 성능상의 문제가 생기게 된다. V8 엔진자바스크립트의 성능상 이슈를 V8 엔진이 해결을 해준다.V8엔진은 자바스크립트 엔진으로 google에서 만들었고 chrome, safari 에서 사용한다.\b인터프리터를 사용하지않고 머신 코드로 변환하여 속도가 빠르다는 장점이 있다.대표적인 특징으로는 hidden class와 inline caching이 있다.이러한 특징으로 인해 자바스크립트 성능을 최적화하는데 도움을 준다고 한다. hidden class자바스크립트에는 클래스의 개념은 없지만 엔진 안쪽에 숨겨진 hidden class라는 것이 존재한다.1let obj = {}; 객체가 생성되면 히든클래스가 생성이 된다. 여기서는 C0라고 가정하겠다.12let obj = {};obj.a = 1; 그리고 a라는 프로퍼티를 추가하게되면 C1이라는 히든클래스가 생성되면서 obj의 히든클래스는 C1으로 바뀌게 된다.C0 히든클래스에는 a라는 프로퍼티가 추가되면 C1라는 히든 클래스도 이동한다라는 정보가 담기게 된다.바로 이게 전환테이블(m_transitionTable)이다.123let obj = {};obj.a = 1;obj.b = 2; b 프로터티가 추가되면 마찬가리고 C2라는 히든 클래스가 생성되면 obj의 히든클래스가 C2로 변경된다.그리고 C1에 전환 테이블에는 b라는 프로퍼티가 추가되면 C2라는 히든 클래스도 이동한다라는 정보가 담기게 된다. 1234567function foo(a) { this.x = a;}let a = foo(1);let b = foo(3); 이런경우에도 a에 대한 히든 클래스가 만들어지고 b도 동일 히든클래스를 공유하게 된다.그러나 실제 필드에 접근해서 값을 가지고 오려면 객체 -> hidden class -> 프로퍼티 테이블 -> 프로퍼티 비교 -> [오브젝트 + 오프셋] 의 단계를거쳐야 실제 필드에 접근할 수 있다.필드에 접근하기 까지 많은 단계를 거쳐야하기 때문에 성능에 영향을 미칠 수 있다.하지만 이러한 작업은 inline caching을 위한 준비이다. inline caching객체 필드에 접근할 때 hidden class를 사용한다면 결국 최종 목적은 필드의 오프셋값에 접근하는 것이다.바로 여기서 inline caching은 이 오프셋 값을 캐싱하겠다는 의미이다. 자바스크립트는 동적언어이지만 실제로는 안바뀌는게 더 많고 성능을 빠르게 하려면 루프를 돌려야한다는 가정을 하자. 123for (var i=0; i<10; i++) { arr[i].x = i;} 이렇게 루프문을 돌리면 i=0일 때는 캐싱된 값이 없어 느리지만 i=1부터는 값이 오프셋 값이 캐싱되어 클래스 기반의 언어와 똑같은 성능을 보이게 된다.단, arr[1]부터 arr[9]까지가 모든 같은 필드 구조를 가지고 있어야만 성립되는 내용이다.추가로 첫번째 수행에서 바로 캐싱되지 않고 두번 수행된 코드부터 캐싱이 된다. 이는 한번 수행된 코드는 한번만 수행될 가능성 높지만 두번 수행된 코드는 이후에 또 수행될 확률이 높기 때문이라고 한다.인라인 캐싱의 포인트는 동일한 유형의 객체가 동일한 히든 클래스를 공유한다는 것이다.만약 다른 프로퍼티가 중간에 추가되거나 하면 inline caching을 더이상 사용할 수 없다. 최적화 계획히든 클래스를 서로 공유할 수 있게끔 객체를 만들어야한다.만약 객체가 생성된 후 중간에 프로퍼티를 추가하게 되면 히든 클래스가 변경된다.아래의 코드와 같이 생성자에서 모든 프로퍼티를 할당할 수 있도록 하는 것이 좋다.123456789101112131415function Foo(a,b) { this.x = a; this.y = b;}let a = new Foo(1, 2);a.z = 1; // 최적화 Xfunction Bar(a,b, c) { this.x = a; this.y = b; this.z = c;}let a = new Bar(1, 2, 3); // 최적화 O","categories":[],"tags":[{"name":"javascript, hidden class, incline caching","slug":"javascript-hidden-class-incline-caching","permalink":"https://juuuuuuuuuuuuuu.github.io/tags/javascript-hidden-class-incline-caching/"}]},{"title":"slack commands 연동","slug":"slack-commands-연동","date":"2019-12-11T02:24:36.000Z","updated":"2019-12-11T02:34:18.195Z","comments":true,"path":"2019/12/11/slack-commands-연동/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/12/11/slack-commands-연동/","excerpt":"","text":"slack 채팅창에 /poll is this yours? yes no 텍스트를 입력하면 투표기능을 제공하고 있다. 외부 stash나 github에서 해당 텍스트를 코멘트로 입력을 하면 슬랙과 연동하여 투표기능을 사용하고 싶었다.그러나 slack 공식문서에서는 현재 제공을하지 않고 비공식 문서에서만 제공을 한다고 한다.참고: https://github.com/ErikKalkoken/slackApiDoc/blob/master/chat.command.md 그러나 비공식 문서이기 때문에 권장하지도 않고 더구나 파라미터로 legacy token값을 넣어야한다. legacy token값은 개인 고유의 토큰 값이며 해당 페이지에 들어가 (https://api.slack.com/custom-integrations/legacy-tokens) 발급받아야한다. 또한 새로 발급 받을 수도 있으며 공유하지 말라고 써있고, 추후에 사라질 수 도 있다. 그래서 slack app을 생성하여 토큰 값을 생성하였지만 scope에서 문제가 생기게 되었다. 오직 chat.commandAPI는 scope가 post로 되어 있어 legacy token값만 파라미터로 사용할 수 있다. 결국 팀원들의 legacy token값을 하드코딩으로 적용시켜 놓으려다가 github에서 새로 제공하고 있는 투표기능이 있다고 하여 다시 서치하기로 하였다.","categories":[],"tags":[{"name":"slack-api, javascript","slug":"slack-api-javascript","permalink":"https://juuuuuuuuuuuuuu.github.io/tags/slack-api-javascript/"}]},{"title":"요즘 프론트엔드 개발은 어떻게 하지? 후기","slug":"wanted","date":"2019-12-05T00:24:09.000Z","updated":"2019-12-05T00:58:20.899Z","comments":true,"path":"2019/12/05/wanted/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/12/05/wanted/","excerpt":"","text":"2019년 12월 4일 원티드에서 주최한 [요즘 프론트엔드 개발은 어떻게 하지?]에 참석하게 되었다.추첨을 통해서 받는다고 했는데 떨어져서 속상해하고 있었는데 갑자기 당일에 너무 가고 싶어 담당자에게메일을 보내고 추가로 가게 되었다.사실 요즘 무엇을 공부해야하고 다른 회사 개발자들은 어떻게 일을 하고 있는지 궁금해서 가고 싶었다.일찍 도착해서 위워크 1층에서 구경하고 있었다. 너무 좋았다. 주먹밥,홍루이젠,모자,칫솔 등 선물도 주었다. 맥주도 무한리필이어서 갔더 먹다가 중간중간에 화장실을 자주 갔다..ㅠ useABTest (원티드)원티드 에서는 프론트엔드 개발팀에서 주도적으로 ABTest를 한다고 한다.가설X => 테스트 => 검증 이런 과정이라고 한다.(사실 이해 잘 못함..)Google Optimize Javscript API를 통해 콜백함수로 유저의 행동을 분석한다고 한다.원티드에서 요구하는 것들은 1.개발실력, 2.ui/ux관심, 테스트, 주도적인 사람을 요구한다고 한다.react,redux,next,typescript,webpack,styled component,sass를 사용한다. 디자인 시스템 (배달의 민족)내가 이해한 디자인 시스템은 기존에는 기획-디자인-ui개발-개발-qa테스트(디자인,기능 qa)-배포이런waterfull방식이다.그러나 할일도 많고 프로젝트들이 너무 많아지다보면 각 페이지마다 노후한 페이지들이 생기게 된다.예를 들면 배민의 프로필 페이지등…그러다 보면 자주 업데이트 하는 페이지들과의 격차도 생기고 누군가 작업을 하기 부담스러워진다.공통된 컴포넌트들을 사용하며 디자인 qa하는 시간을 줄이자는 취지(?)에서 디자인 시스템을 도입한 것 같다. 디자이너들에게 코드 교육을 시키면서 비트맵이 아닌 sketch app과 같은 코드 변환툴을 사용하여도입했다고 한다.해외 사례로는 airbnb, spotify design 등이 있다고 한다.방식은 디자인 동시에 npm에 디자인을 업데이트하면 개발자들은 npm에 땡겨와서 바로 적용이 되는 시스템이다.개발자들이 일일히 html 코드를 옮길 필요가 없다.또한 바로 확인이 가능해서 업무 효율성 측면에서 좋다고 한다.그러나 아직 디자인된 코드를 리액트로 바로 변환하는 툴 같은것들은 만들어지지 않았다고 한다. 모노레포 (토스)토스는 프론트엔드 개발자가 총 10명이라고 한다.모놀리식과 마이크로 서비스의 장단점 설명, 그리고 토스는 모노레포 방식을 도입했다고 한다. 모노레포는 위의 것들의 장단점을 보완한 방식이다.하나의 git에 여러 패키지들을 연결하는 방식이다.프로젝트 구조는 portal 프로젝트에 libaray 폴더에는 여러 라이브러리, 서비스 폴더에는 각 서비스들이 있는 구조이다.카나리 배포?(전부다 배포하는것이 아니라 몇명한테만 배포하고 반응이 좋으면 점차적으로 여러사람들에게 배포)를 한다고 한다. 토스의 전체적인 구조라고 한다. 느낀점이런 곳에 와본게 처음이고 용기내서 질문도 해보고 나름 좋은 경험이었다고 생각한다.대부분의 회사들이 react,redux,ts는 기본으로 사용하고 있다고 한다.열심히 공부해야겠다. 그리고 문제 해결능력도 중요하고 기본기 탄탄하게!! 하라고 한다.","categories":[],"tags":[{"name":"conference","slug":"conference","permalink":"https://juuuuuuuuuuuuuu.github.io/tags/conference/"}]},{"title":"github과 프로젝트 연결","slug":"github","date":"2019-12-04T03:33:56.000Z","updated":"2019-12-04T03:52:58.319Z","comments":true,"path":"2019/12/04/github/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/12/04/github/","excerpt":"","text":"보통 github 개인 repository에 프로젝트를 연결해서 사용할 떄 소스트리를 사용하였다.그러다보니 react-create-app과 같이 프로젝트 생성이 바로 되는 프로젝트들은 github에 repository를 생성하게 되면폴더가 2개가 생기는 이슈가 있었다.그럴 때마다 해당 폴더를 옮기거나 하는식으로 했었는데 이번에 명령어를 사용하여 작업을 해보았다. 1. github repository 생성github 페이지로 가서 repository를 생성한다. 2. 로컬에서 프로젝트 경로로 들어간다 (명령어)1234567$ git init // .git 디렉토리 생성$ git add .$ git commit -m \"first commit\"$ git remote add origin [github주소] 이렇게 순차적으로 명령어를 치다보면 마지막에 github과 연결하는 부분에서 해당 에러가 발생한다. 1fatal: remote origin already exists 에러 해당 에러는 이미 remote origin이 존재하기 때문이라고 한다.그렇기 때문에 이미 존재하는 remote origin을 삭제하고 다시 github과 연결을 한다. 1$ git remote rm origin 그리고 난뒤 파일을 작업되어 있는 파일을 푸시하려고 하면1$ git push -u origin master 아래와 같이 에러가 발생한다. 12345! [rejected] master -> master (non-fast-forward)hint: Updates were rejected because the tip of your current branch is behindhint: its remote counterpart. Integrate the remote changes (e.g.hint: 'git pull ...') before pushing again.hint: See the 'Note about fast-forwards' in 'git push --help' for details. pull을 받을게 있다는 것이다.git pull origin 브랜치명 명령어로 pull을 받으려고 하면아래와 같이 에러가 발생한다. 1fatal: refusing to merge unrelated histories 이 이슈는 이미 존재하는 두 프로젝트이 기록이 있어 merge를 하라는 뜻이다. 1$ git pull origin 브런치명 --allow-unrelated-histories --allow-unrelated-histories 명령 옵션은 이미 존재하는 두 프로젝트의 기록(history)을 저장하는 드문 상황에 사용된다고 한다.즉, git에서는 서로 관련 기록이 없는 이질적인 두 프로젝트를 병합할 때 기본적으로 거부하는데, 이것을 허용해 주는 것이다. 그러면 이제 git push가 가능하다.","categories":[],"tags":[{"name":"github","slug":"github","permalink":"https://juuuuuuuuuuuuuu.github.io/tags/github/"}]},{"title":"expo error","slug":"expo-error","date":"2019-12-02T06:19:18.000Z","updated":"2019-12-02T06:27:14.808Z","comments":true,"path":"2019/12/02/expo-error/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/12/02/expo-error/","excerpt":"","text":"expo를 사용하여 react-native앱을 만드려고 하는데 npm start만 하면 아래와 같은 에러가 났다.구글링 결과 서버가 이미 켜져 있는 경우에 비슷한 에러가 난다고 하는데 모든 서버를 죽이고 해도 되지 않았다.사파리쪽 캐시관련해서 에러가 잡히는 것 같아 찾아보던 도중 simulator에 들어가 이것저것 구경하는 도중expo앱에 로그인을 했더니 해결되었다… 에러로그인해결","categories":[],"tags":[{"name":"react-native, expo","slug":"react-native-expo","permalink":"https://juuuuuuuuuuuuuu.github.io/tags/react-native-expo/"}]},{"title":"배열 함수","slug":"hof","date":"2019-11-19T07:57:30.000Z","updated":"2019-11-19T09:53:39.989Z","comments":true,"path":"2019/11/19/hof/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/11/19/hof/","excerpt":"","text":"잘 아는 줄 알았는데 사용하다보면 까먹거나 헷갈리는 부분들이 있었다.자바스크립트에서 배열을 다룰 때 주로 사용하는 함수들에 대해서 정리해보자. map배열.map((요소, 인덱스, 배열) => { return 요소 });여기서 주의할 점은 해당 배열의 length만큼 똑같이 리턴한다는 것이다. (가끔 이 부분을 깜빡했었음..ㅠ) 12345[1,2,3, -10].map(v => { if (v > 0) { return v; // [1,2,3,undefined]을 리턴한다. }}); forEach배열.map((요소, 인덱스, 배열) => { });기존에 for문이라고 생각하면 된다. 루프를 돌면 따로 리턴하는 값은 없다.기존에 for문과 차이점이라면 중간에 break를 사용하여 나올 수 없다.1[1,2,3].forEach(v => console.log(v)); // 1,2,3 some, everysome은 return true가 되는 순간 루프문을 나오게 된다.주로 해당 조건에서 어떠한 작업을 할 때 사용된다. 반환되는 값은 true이다.12345678let a = [];[1,2,3].some(v => { console.log(v) // 1, 2 까지만 찍힌다. if (v === 2) { a.push(2) return true; }}) every는 모든 배열을 다 순회한다.모든 요소가 true로 떨어지면 treu리턴, 하나라도 false로 떨어지면 false를 리턴한다.123456789// false 리턴[1,2,3].every(v => { return v < 2;})// true 리턴[1,2,3].every(v => { return v < 4;}) reduce배열.reduce((누적값, 현잿값, 인덱스, 요소) => { return 결과 }, 초깃값);초깃값을 적어주지 않으면 자동으로 0번째 인덱스의 값이 된다.이 함수는 주로 코딩 테스트 문제를 준비하면서 접하게 되었다.그 뒤로 자주 사용하고 있다. 매우 유용한 함수이다.주로 덧셈을 할 때 자주 사용하고, 누적값을 활용해서 다양하게 이용 가능하다.항상 return을 해줘야한다!! 12345678910111213141516171819[1,2,3].reduce((acc, cur) => { return acc += cur; // 최종 6을 리턴}, 0);[1,2,3].reduce((acc, cur) => { cur < 3 && acc.push(cur); return acc; // [1,2]}, []);// 위와 같은 똑같은 기능을 하는 부분인데 typescript를 사용하면서 논리연사자를 사용하려고 했는데 tslint설정으로// 따로 해주지 않아 if문으로 사용한 경우다. // 이 경우 if문에 해당하지 않는 경우 return 되는 값을 지정하지 않아 undefined로 리턴이 된다.[1,2,3].reduce((acc, cur) => { if (cur < 3) { acc.push(cur); return acc; } // undefined..}, []); 1234567891011// 각 gender별로 몇명이 있는지 확인하고 싶을 때let object = [{gender: 'female', name: 'july'}, {gender: 'male', name: 'kevin'}, {gender: 'female', name: 'cindy'}];object.reduce((acc, cur) => { if (acc[cur.gender]) { acc[cur.gender] +=1; } else { acc[cur.gender] = 1; } // 자주 까먹는다.. 무조건 값을 return 해줘야한다!! 꼭!! *** return acc; // {female: 2, male: 1}}, {}); filter조건이 true가 되는 요소를 리턴해 새로운 배열을 만든다.즉 기존 배열에 있는 length와 일치하지 않을 수 있다. (이 부분이 중간중간 헷갈렸던 부분…) 12const data = [1, 2, 3, 4, 5];data.filter(v => v>2); // [3,4,5] find, findIndex배열의 특정 요소를 탐색하고 싶을 때는 indexOf를 주로 사용하나 배열의 요소가 객체로 되어 있는 경우에 find, findIndex를 사용한다.find는 조건에 맞는 객체 자체를 반환해주며findIndex는 조건에 맞는 index의 값을 반환해준다. 123456789var commentList = [ {id : 1, child_count: 2, message: '코딩이 재미있다.'}, {id : 2, child_count: 3, message: '젊은 꼰대'}, {id : 3, child_count: 0, message: 'ㅎㅎㅎㅎ'}];commentList.find(v => v.child_count === 0); //{id: 3, child_count: 0, message: \"ㅎㅎㅎㅎ\"}commentList.findIndex(v => v.child_count === 0); //2","categories":[],"tags":[{"name":"javascript","slug":"javascript","permalink":"https://juuuuuuuuuuuuuu.github.io/tags/javascript/"}]},{"title":"enum, type assertion","slug":"enum-type-assertion","date":"2019-11-18T01:37:34.000Z","updated":"2019-11-18T01:37:34.744Z","comments":true,"path":"2019/11/18/enum-type-assertion/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/11/18/enum-type-assertion/","excerpt":"","text":"","categories":[],"tags":[]},{"title":"export/import","slug":"export-import","date":"2019-11-15T07:57:41.000Z","updated":"2019-11-19T08:30:54.791Z","comments":true,"path":"2019/11/15/export-import/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/11/15/export-import/","excerpt":"","text":"리액트를 import하는 코드를 보다보면 아래와 같이 나뉘어진다. 123import React from 'react';import * as React from 'react' 두가지의 성능상 차이점은 있는지 궁금했었다.첫번째 코드는 default로 React를 가져오는 거고 두 번째 코드는 모든 export된 요소들을 한번에 가져온다는 의미이다. 그 전에 모듈 시스템에 대해서 알아보도록 하자. 과거 자바스크립트자바스크립트는 패키지와 파일끼리 서로 의존하고 있는 경우가 있다.예를 들어 A파일에서는 jquery를 사용하고 B파일에서는 kakaoAPI를 사용하고 있는 경우원칙적으로 A파일에서는 kakaoAPI에 접근할 수 없어야하지만 웹에서 처음 시작할 때모든 스크립트를 로딩하기 때문에 다른 파일도 모든 패키지에 접근할 수 있었다. A파일1$ = null; B파일1234// error$('a').on('click', function () {}); 이렇게 스크립트에 삽입하여 문제가 발생할 수 있다. 이러한 문제점을 해결하기 위해 나온것들이 commonJS, requireJS 이다.의존성을 관리하기 위해 파일 첫부분에 어떤 패키지를 필요로 한다고 선언하여 선언한 패키지만 사용한다.1import React from 'react'; import, export 방식12345const a = 1;const b = 2;export { a };export const c = 3;export default b; a는 변수 선언 후 객체에 담아서 export b는 변수 선언 후 default라는 키워드를 붙인채 export c는 변수 선언과 동시에 export 1import d, { a, c as e} from 'A'; default로 export한 b는 괄호를 사용하지 않아도 import가 가능하며 변수명을 마음대로 지을 수 있다.여기서는 변수 d가 변수 b를 import한 것이다.a와 c는 export할 때 이름 그대로 하며 괄호를 사용해서 불러와야한다.하지만 괄호안에 변수 이름은 바꾸고 싶다면 as라는 키워드를 사용한다. 1import * as React from 'react'; 위에 코드는 *을 사용하여 export한 모든 것들을 다 모아서 import해주는 것이다.default도 객체의 멤버로 들어간다. typescript??Typescript를 사용할 때에 default로 export한 객체를 가져올 때는 tsconfig.json파일에"allowSyntheticDefaultImports": true 값을 설정해줘야한다.react를 임포트를 할때에는 * as React를 쓰는것이 올바른 방법이다. 1import React, { PureComponent } from 'react'; 이 코드는 React가 export default로 되어 있을 때 가능한 방식이다.버전에 따라서 수정될 수 도 있기 때문에 * as React를 사용하는 추세인 것 같다.","categories":[],"tags":[{"name":"javascript, react, nodeJS, CommonJs","slug":"javascript-react-nodejs-commonjs","permalink":"https://juuuuuuuuuuuuuu.github.io/tags/javascript-react-nodejs-commonjs/"}]},{"title":"typescript 정리","slug":"typescript","date":"2019-11-05T05:21:31.000Z","updated":"2019-11-15T08:19:41.986Z","comments":true,"path":"2019/11/05/typescript/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/11/05/typescript/","excerpt":"","text":"전부터 타입스크립트에 대해서 조금씩 찾아보다가 최근에 chrome-extension 프로젝트에 적용하기로 했다.타입스크립트에 대해서 이렇게 쓰는게 맞는지 잘 모르겠어서 내용들을 조금씩 정리해보려고 한다. 개요 마이크로소프트에서 개발하고 관리하는 오픈소스 라이브러리 어떤 브라우저, 호스트, 운영 체제에서 작동 자바스크립트의 상위 집합으로 ECMAScript 지원 런타임 환경이 아닌 컴파일 환경에서 타입체크를 해서 오류를 줄여줌 실행과정 typescript 코드를 TSC(TypeScript Complier)를 통해서 JavaScript로 변환하며 이때 타입 검사를 한다. JavaScript 코드를 node를 통해서 실행하여 process로 만든다. 타입 제어방법 타입 어노테이션(type annotation)을 이용하여 변수에 타입을 선언 설치 및 환경 세팅 먼저 typescript를 npm 에서 설치한다. 그 외에 프로젝트에 따라 ts-loader나 필요한 라이브러리를 부수적으로 설치한다. tsconfig.json 타입스크립트를 컴파일 할 때 필요한 설정을 관리","categories":[],"tags":[{"name":"typescript, javascript","slug":"typescript-javascript","permalink":"https://juuuuuuuuuuuuuu.github.io/tags/typescript-javascript/"}]},{"title":"date 관련 정리","slug":"date","date":"2019-10-23T15:00:00.000Z","updated":"2019-10-24T09:00:00.022Z","comments":true,"path":"2019/10/24/date/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/10/24/date/","excerpt":"","text":"UTCUTC는 Coordinated Universal Time의 줄임말로 국제 표준시간이다. GMTGMT는 Greenwich Mean Time의 줄임말로 런던을 기점으로하고 웰링턴에서 종점으로 설정 되는 협정 세계시간이다. UTC vs GMT UTC와 GMT는 일상에서는 공통으로 같은 시간이다. UTC가 좀더 정확한 시간이라고 한다. ISO 8601 UTC 그리고 Timezone과 함께 문자열의 형태로 시간을 표현하는 방법을 기술해놓은 표준이다. YYYY-MM-DDTHH:mm:ss.sssZ KST KST(Korea Standard Time)는 UTC에 9시간을 더한 시간이다. 얼마전 쿠키값 세팅되는 시간을 23:59:59으로 지정해놓았는데 분명 콘솔창에는 23:59:59로 찍히지만 저장된 만료시간을 보면 14:59:59 로 저장이 되었다.그래서 23:59:59로 바꾸기 위해 열심히 서치를 하다가 찾아낸 정보는JavaScript Cookie expires time must be GMT/UTC 였다.쿠키 만료시간을 세팅할 때 자바스크립트가 로컬에서 지정한 시간(KST)을 UTC로 바꾼다는 이야기였다.그래서 아예 UTC로 바꾼 시간을 넘기면 되겠다는 생각으로 아래와 같이 코드를 수정했다. 12345678910// 기존코드 const date = new Date(); const expires = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59); $.cookie(cookieName, specialPricePopup.chgTimestamp, { expires });// UTC로 변환한 코드 const date = new Date(); const expires = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59)); $.cookie(cookieName, specialPricePopup.chgTimestamp, { expires });` 그러나, 위의 코드는 잘못되었다. UTC로 날짜를 생성하고 다시 로컬기준으로 시간으로 바꾸는 것이다.그래서 쿠키 만료시간에 23:59:59가 세팅은 되지만.. 결국은 잘못된 코드다.알고보니 쿠키값 세팅은 UTC 기준으로 되고 브라우저?자바스크립트?의 시간도 UTC 기준으로 돌아가기 때문에 기존 코드대로 해야 쿠키값이 해당 시간에만료가 되는 것이었다.","categories":[],"tags":[{"name":"javscript","slug":"javscript","permalink":"https://juuuuuuuuuuuuuu.github.io/tags/javscript/"}]},{"title":"javascript 엔진","slug":"javascript","date":"2019-09-19T03:18:41.000Z","updated":"2019-09-19T07:57:08.524Z","comments":true,"path":"2019/09/19/javascript/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/09/19/javascript/","excerpt":"","text":"자바스크립트 엔진의 대표적 예로는 google V8 엔진이 있다.V8엔진은 chrome, nodeJS에서 주로 사용한다. V8 엔진V8엔진에는 주로 메모리힙과 콜스택으로 이루어져 있다.memory heap: 메모리 할당이 일어나는 곳call stack: 코드 실행에 따라 호출 스택이 쌓이는 곳 런타임프로그램이 실행되고 있는 때 존재하는 곳을 의미한다.web browser에서 작동하는 자바스크립트 측면도 있고 node.js라는 환경에서 구동되는 측면이 존재한다.browser와 node.js를 런타임이라고 볼 수 있다.자바스크립트 엔진 외에 dom,ajax,setTimeout과 깉이 브라우저에서 제공해주는 api를 Web API라고 한다. 호출스택자바스크립트는 싱글 쓰레드 기반 언어이다.호출스택은 프로그램 상에서 어디에 있는지 기록하는 자료구조이다.호출스택에 쌓인 함수들이 차근차근 위에서부터 실행이 된다.초반에 리액트 공부를 시작했을 때 setState가 계속 실행이 되었을 때 아래와 같은 스택 허용치 넘는 에러가 자주 났었다… 동시성 & 이벤트 루프호출 스택에서 시간이 엄청 오래걸리는 함수가 있으면 브라우저는 그 동안 아무런 작업을 못하고 대기상태가 된다.결국 에러를 띄우면서 페이지 종료 팝업이 뜬다.(과거 파일뷰어에서 200-300장이 넘는 이미지 파일들을 렌더링할 때 났던 에러였다…)페이지 렌더링 동작을 방해하지 않고 브라우저의 응답도 끊지 않으면서 연산량이 많은 코드를 그러면 어떻게 실행해야할까??답은 비동기 콜백이다!과거에는 ajax를 사용하여 콜백함수로 처리했다.그러다보니 콜백 지옥에 빠지기도 하고 가독성도 떨어지고 수정하기도 힘들었다.그래서 나온 것들이 promise,async 등이 있다.","categories":[],"tags":[{"name":"javascript","slug":"javascript","permalink":"https://juuuuuuuuuuuuuu.github.io/tags/javascript/"}]},{"title":"babel","slug":"babel","date":"2019-09-17T03:42:36.000Z","updated":"2019-09-17T05:42:05.952Z","comments":true,"path":"2019/09/17/babel/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/09/17/babel/","excerpt":"","text":"babel은 특정 버전 ECMAScript를 하위 버전 ECMAScript로 변환해주는 트랜스파일러다.즉, es5 이상의 문법을 es5 지원 브라우저에서 해석할 수 있도록 해주는 트랜스파일러다.(ES6+ 문법은 에버그린 브라우저만 지원하는 기능이기 때문에 구형 브라우저에서는 호환이 안되는 문제가 있는데이를 babel이 해결해준다.) babel-cli먼저 @babel-cli을 설치한다. (babel7 버전부터는 @를 붙인다.)@babel-cli는 트랜스파일을 진행하는 코어 기능만 있고 실제 코드 변환시에는 플러그인들이 필요하다.예를 들어 화살표 함수를 사용하는데 화살표 함수를 트랜스파일할 수 있는 플러그인을 설치해야한다.일일히 설치할 수 없으니 프리셋을 설치하여 해결할 수 있다. 1npm install @babel-cli babel-preset-env프리셋과 플러그인들을 모와 관리하고 있는 모듈이다.env로 설정하면 @babel-preset-latest로 현재 지원 가능한 가장 최식 버전의 프리셋을 사용 할 수 있다..babelrc에서 env에 맞는 target브라우저를 설정할 수 도 있다. 12345678910111213141516171819202122// .babelrc 파일{ \"preset\": [ [\"env\", { \"targets\": { \"chrome\": 52, } }] ]}// .babelrc 파일{ \"preset\": [ [\"env\", { \"targets\": { \"browsers\": [\"last 2 versions\", \"not ie <= 10\"] } }] ]}` 배열형태로 지원 브라우저를 정의할 수 있다. 1npm install @babel-preset-env babelrc.babelrc라는 파일명으로 프로젝트의 바벨 관련 설정을 등록할 수 있다.package.json에서 babel이란 키로 설정을 추가할 수도 있지만 .babelrc을 쓰는게 더 좋은 것 같다. 1234567891011{ \"presets\": [ [\"env\", { \"targets\": { \"browsers\": [\"last 2 versions\", \"not ie <= 9\"] } }] ], \"plugins\": [\"transform-object-rest-spread\"] }` 타켓 브라우저를 설정할 수 있다.또한 아직 정식버전에 포함되지 않는 스펙들은 plugin배열로 추가 가능하다.(사용가능한 플러그인: https://babeljs.io/docs/en/plugins/) babel-polyfillbabel을 사용해서 es6 코드를 에버그린 브라우저에서 사용가능하다.그러나 IE8과 같은 구형브라우저에는 promise,map,set 등등 객체나 메소드 들이 없기 때문에 사용할 수 없다.그래서 @babel/polyfill을 설치해야한다. 12npm install @babel/polyfill` 그리고 코드 최상단에 한번만 추가를 해야한다. 1import '@babel/polyfill'; 웹팩을 사용한다면 임포트를 하지 않고 entry에 적어도 된다.1234// webpack.config.js{ entry: ['@babel/polyfill', './app.js'],} babel-loader웹팩과 바벨을 연동할 수 있게 한다. 1npm install @babel-loader babel-loader를는 node_module에 있는 내용은 제외하고 적용된다.또한 entry에서 babel-polyfill 설정도 가능하다. 123456789101112131415161718192021222324252627282930module.exports = (env, options) => { const config = { entry: { app: ['babel-polyfill', './src/index.js'] }, module: { rules: [ { test: /\\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader' } } ] }, // .... 중략 .... optimization: { splitChunks: { cacheGroups: { commons: { test: /[\\\\/]node_modules[\\\\/]/, name: 'vendors', chunks: 'all' } } } } } }","categories":[],"tags":[{"name":"babel, javascript, webpack","slug":"babel-javascript-webpack","permalink":"https://juuuuuuuuuuuuuu.github.io/tags/babel-javascript-webpack/"}]},{"title":"자바스크립트 scope","slug":"scope","date":"2019-09-16T00:30:00.000Z","updated":"2019-09-16T09:17:25.949Z","comments":true,"path":"2019/09/16/scope/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/09/16/scope/","excerpt":"","text":"es6문법 관련하여 정리를 하다가 어렴풋이 알고 있었던 scope에 대해 재정리를 해보았다. scope는 범위라는 뜻으로 프로그래밍 언어에서 변수나 메소드의 참조 범위를 의미한다.자바스크립트에는 전역 스코프와 지역 스코프가 존재한다.전역 스코프(global scope): script 내 어느 곳이든 참조 가능지역 스코프(local scope): 정의된 함수 내에서만 참조 가능, 함수 외부에서는 접근 불가 javscript scope의 특징은 다음과 같다. 변수명 중복 허용 var 키워드 생략 function scope lexical 특징 변수 호이스팅 변수명 중복 허용변수명이 중복되어도 에러가 나지 않는다. 12345678function A() { let a = 10; if (true) { let a = 3; console.log(a); // 3 } console.log(a); // 10} var 키워드 생략var 키워드 생략시 전역변수로 정의가 된다.123456var x = 'global';function ex() { x = 'change';}ex();alert(x); // 'change' function scope자바스크립트는 블록 단위가 아닌 함수단위로 scope를 정의한다. (var 해당)함수 스코프 레벨의 변수인 var는 메모리 누수, 디버깅이 어렵고 가독성이 떨어지는 문제점이 있다.이러한 문제점울 피하고자 블록 스코프 변수인 let,const가 탄생했다.블록 스코프는 {}로 경계를 구분하여 자신이 정의한 블록과 하위 블록에서만 접근이 가능하다. 1234567891011function A() { var a = 0; if (true) { var b = 10; for (var c = 0; c < 5; c ++) { console.log(c); // 0,1,2,3,4 } console.log(c); // 5 } console.log(b); // 10} 12345678910111213141516171819202122function A() { let a = 0; if (true) { let b = 10; for (let c = 0; c < 5; c ++) { console.log(c); // 0,1,2,3,4 } console.log(c); // Uncaught Reference Error: c is not defined } console.log(b); // 10}let foo = \"I'm foo\";if(true) { let bar = \"I'm bar\"; console.log(foo); //I'm foo console.log(bar); //I'm bar}console.log(foo); //I'm fooconsole.log(bar); //Uncaught ReferenceError: bar is not defined. lexical 특성스코프는 함수 실행 시점이 아닌 정의 시점에서 생성된다.정적 스코프라고 한다. 12345678function A() { var a = 10; B();}function B() { return a; // Uncaught Reference Error} 변수 호이스팅변수의 정의가 선언과 할당으로 분리되는 것을 의미한다.변수가 함수내에 정의되면 선언이 함수 최상단으로 이동변수가 전역에서 정의되면 선언이 컨텍스트 최상단으로 이동호이스팅은 함수 선언 방식에서만 적용가능하다.1234567891011121314function A() { console.log(name); // undefined let name = 'ji'; console.log(name); // ji}// 실제로 코드 실행시 function A() { let name; console.log(name); // undefined name = 'ji'; console.log(name); // ji} 추가적으로 변수 외에도 함수 선언문만 호이스팅이 가능하다.함수 표현식은 호이스팅이 되지 않는다. 그 이유는 마찬가지로 변수에 함수를 초기화 시키기 때문에선언문이 호이스팅 되어 상단으로 올려진다 해도 함수가 아닌 변수로 인지되기 때문이다.12345// 함수 선언문A();functio A() { console.log('hi');} 12345// 함수 표현식hositing(); // hositing of object is not a functionlet hoisting = functio A() { console.log('hi');}","categories":[],"tags":[{"name":"javascript","slug":"javascript","permalink":"https://juuuuuuuuuuuuuu.github.io/tags/javascript/"}]},{"title":"EventHandler (jquery)","slug":"eventhandler","date":"2019-09-02T14:07:03.000Z","updated":"2019-09-03T09:19:56.088Z","comments":true,"path":"2019/09/02/eventhandler/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/09/02/eventhandler/","excerpt":"","text":".on(events [,selector][,data],handler)특정요소에 이벤트 바인딩 하기 위해 사용한다.파라미터에 대해 알아보자. eventsevents는 하나 이상의 공백으로 구분된 이벤트 유형 ‘click keydown’.을 통해 커스텀한 이벤트 가능 ‘click.myPlugin’ selector이벤트를 트리거하는 선택된 요소의 하위항목이다. 예를 들어 div안에 여러 버튼이 있는 경우 일일히 버튼마다 이벤트를 걸지않고div에 이벤트를 걸고 버튼속성에 이벤트를 트리거한다.이럴 때 참 유용한 것 같다. 123$('[data-container=div]').on('click', '[data-button]', (e) => { const type = $(e.currentTarget).attr('data-button'); // 각 버튼의 속성값이 들어온다.}); data이벤트가 트리거 될 때 핸들러로 전달될 데이터이다.123$('#div').on('click', {msg: 'message'}, (e) => { console.log(e.data.msg); // message}); handler이벤트가 트리거 될 때 실행될 함수이다. 이 외에도 on함수로 여러개 이벤트 등록할 수도 있다.123456$('#div').on({ mouseenter: function() { }, mouseleave: function () { },}) 또한 보통 코드를 작성할 때 이미 이벤트가 있는 경우를 대비해서 한번 초기화(off)하고서 이벤트를 다시 건다(on).123$('#div').off('click').on('click', () => {});","categories":[],"tags":[{"name":"javascript, jquery, eventHandler","slug":"javascript-jquery-eventhandler","permalink":"https://juuuuuuuuuuuuuu.github.io/tags/javascript-jquery-eventhandler/"}]},{"title":"es6 문법 정리","slug":"es6","date":"2019-09-01T08:21:57.000Z","updated":"2019-09-01T09:13:34.784Z","comments":true,"path":"2019/09/01/es6/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/09/01/es6/","excerpt":"","text":"ECMAScript 2015(ES6)는 매년마다 업데이트 되고 있으며 현재 ES7,ES8,ES9,ES10 까지 나온상태이다.그 중에서도 ES6는 현재 모든 프론트엔드 개발자들이 사용하고 있으며 표준화되어 있다.ES6를 사용하면서도 정확한 개념을 잘 모르고 사용한 것들을 정리해보았다. arrows기존 function을 대신해서 화살표(=>)를 사용하여 함수를 선언할 수 있다.그러나 완전히 function을 대신하지는 않는다. 1234567891011const abc = function(a) { return a + 1;};// arrow functionconst abc = a => { return a + 1;};// 매개변수 1개일 때는 소괄호 생략 가능 및 함수 한줄 표현가능시 중괄호 생략 및 자동 returnconst abc = a => a + 1; 1) this 변경항상 이 부분이 헷갈렸다. function으로 선언했을 때 this와 화살표 함수로 선언했을 때 this의 차이점!!!화살표 함수는 자신을 포함하는 외부 scope에서 this를 계승받는다.즉, 자신만의 this를 생성하지 않는다. (lexical this)화살표 함수에서는 내부함수여도 this가 화살표 함수가 선언된 곳에서 상위의 context를 가리치게 된다. 1234567891011121314151617181920212223242526//function 예제function Text(text) { this.text = text;}setText.prototype.array = function(arr) { console.log(this.text); // hi return arr.map(function(v) { return `${this.text} ${v}`; // undefined aa, undefined bb });};const text = new Text('hi');Text.array(['aa', 'bb']);// 화살표 함수 예제function Text(text) { this.text = text;}Text.prototype.array = function(arr) { return arr.map = v => return `${this.text} ${v}`;});const text = new Text('hi');Text.array(['aa', 'bb']); // hi aa, hi bb 2) 화살표함수 사용하면 안되는 케이스객체의 this를 바인딩하지 않고 전역객체가 바인딩된다.즉, 객체의 메소드를 정의할 때는 사용하면 안된다. 123456789101112131415const obj1 = { name: 'Lee', sayName: () => { console.log(`hi + ${this.name}`); }};obj1.sayName(); // hi +const obj1 = { name: 'Lee', sayName: function() { console.log(`hi + ${this.name}`); }};obj1.sayName(); // h i+ Lee 마찬가지로 생성자 함수로 사용할 수 없다.또한 addEvent Listener함수에서 콜백함수에서 사용하면 this는 상위 컨텍스트를 가르킨다. 12345678const btn = document.getElementById('button');btn.addEventListner('click', function() { console.log(this); // this === btn 객체});btn.addEventListner('click', () => { console.log(this); // this === window}); 3) arguments 대신 args 사용매개변수를 지정하지 않아도 arguments라는 프로퍼티가 함수에 자동으로 생성되어 사용가능했었다. es6에는 arguments는 사라지고 args가 생겼다.(매개변수지정필요)매개변수 부분에 rest파라미터 (…)을 사용하여 가변인자 함수 내부에 배열로 전달 할 수 있다. 123456789const foo = function() { console.log(arguments);};foo(1, 2); // {'0': 1, '1': 2}const foo2 = (...args) => { console.log(args);};foo(1, 2); // [1,2] Destructuring(구조분해할당)배열과 객체에서 패턴 매칭을 통한 데이터 바인딩을 제공한다.할당 실패시에는 undefined값이 자동으로 할당된다. 1) array Destructuring (배열 구조분해할당)1const [a, b, ...c] = [1, 2, 3, 4, 5, 6]; // a: 1, b: 2, c: [3,4,5,6] …을 사용하여 나머지 변수들에 배열 구조분해할당이 가능하다. 2) object Destructuring (객체 구조분해할당)12const { a, b } = { a: 1, b: 2 }; //a: 1, b: 2const { p, q } = { e: 3, q: 2 }; // p: undefined, q: 2, 객체의 키값이 동일할 때 구조분해할당이 가능하다.","categories":[],"tags":[{"name":"javascript, ES6","slug":"javascript-es6","permalink":"https://juuuuuuuuuuuuuu.github.io/tags/javascript-es6/"}]},{"title":"헷갈리는 논리 연산자 (&&, ||)","slug":"grammar","date":"2019-08-13T03:31:46.000Z","updated":"2019-08-13T05:16:30.154Z","comments":true,"path":"2019/08/13/grammar/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/08/13/grammar/","excerpt":"","text":"코드를 작성시 간결하고 가독성이 좋게 하는 방법에는 여러가지들이 있다.그 중에서도 논리 연산자는 코드를 간결하게 작성할 수 있다.그러나 조금 헷갈릴 때가 있어 정리가 필요했다. && 연산자A && B A와 B 모두 true이면 결과는 true이다.A가 false이면 B를 체크하지 않고 false를 반환한다. && 연산자 활용 예1234567function check(value) { if (value && value > 10) { return true; } else { return false; }} 위의 코드는 아래와 같이 간략하게 수정할 수 있다.value가 false이면 false를 반환한다. (value >10은 체크하지 않는다.)value가 true이고 value > 10 이 false이면 false반환한다.또한 위의 조건을 충족한 경우 true를 반환한다. 123function check(value) { return value && value >10;} 123456let useName;if (userId && userId.token) { useName = userId.name;} else { useName = null;} userId 가 참이고 userId.token이 참이면 userId.name값을 userName에 넣는다.만약 거짓이면 null을 userName에 넣는다.1const userName = userId && userId.token && userId.name; || 연산자A || B A 또는 B중 하나라도 true이면 true 반환한다. || 연산자 활용 예1234567function documentTitle(theTitle) { if (!theTitle) { theTitle = \"Untitled Document\"; } else { theTitle = theTitle; }} 아래와 같이 나타낼 수 있다. 123function documentTitle(theTitle) { theTitle = theTitle || \"Untitled Document\";} && 와 || 활용 예12345if (userName) { logIn(userName);} else { signUp();} userName이 true이면 logIn 함수를 실행하고 false인 경우 signUp함수를 실행한다.삼항 연산자라 비슷하다고 보면 된다. 12345// 논리 연산자userName && logIn(userName) || signUp();//삼항 연산자userName ? logIn(userName) : signUp();","categories":[],"tags":[{"name":"javascript","slug":"javascript","permalink":"https://juuuuuuuuuuuuuu.github.io/tags/javascript/"}]},{"title":"hoisting이란?","slug":"hoisting","date":"2019-07-02T05:08:42.000Z","updated":"2019-07-02T05:37:18.815Z","comments":true,"path":"2019/07/02/hoisting/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/07/02/hoisting/","excerpt":"","text":"hoisting이란 자바스크립트의 기본동작 중 하나이다.사전적 정의로는 끌어올리다라는 뜻이다. 123console.log(a); // undefinedlet a = 10;console.log(a); // 10 첫번째 콘솔에서 a가 undefined가 나온 이유는 자바스크립트 엔진 구동시 선언문을 가장 최우선으로 헤석한다.호이스팅 개념으로 작성하면 아래와 같다.1234let a;console.log(a); // undefineda = 10;console.log(a); // 10 변수가 함수 내에서 정의되면 선언이 함수의 최상위변수가 함수 밖에서 정의되면 선언이 전역 컨텍스트의 최상위 아래의 코드와 같다.1234567let a = 10;function A () { console.log(a); // undefined let a = 12; console.log(a); //12} 함수 선언문은 호이스팅이 되지만 함수 표현식은 호이스팅이 되지 않는다. 123456789101112console.log(typeof funcA); //functionconsole.log(typeof funcB); //undefinedfuncA(); // afuncB(); // 스크립트 에러 발생function funcA() { console.log('a)}const funcB = function() { console.log(b)} funcA는 함수선언문이기 때문에 호이스팅이 되어 위와 같이 출력된다.그러나 funcB는 타입을 체크하는 콘솔에는 funcB의 변수르 선언한것이므로 undefined가 찍히고함수를 실행을 하면 funcB is not a function이라는 에러가 발생한다.호이스팅 개념으로 작성하면 아래와 같다. 1234567891011121314function funcA() { console.log('a)}const funcB;console.log(typeof funcA); //functionconsole.log(typeof funcB); //undefinedfuncA(); // afuncB(); // 스크립트 에러 발생 funcB = function() { console.log(b)}","categories":[],"tags":[{"name":"javascript","slug":"javascript","permalink":"https://juuuuuuuuuuuuuu.github.io/tags/javascript/"}]},{"title":"Hooks","slug":"hook","date":"2019-06-04T05:56:08.000Z","updated":"2019-06-04T06:54:04.108Z","comments":true,"path":"2019/06/04/hook/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/06/04/hook/","excerpt":"","text":"기존 함수형 컴포넌트는 상태관리를 할 수 없었다.그러나 react 16.8 부터 hooks는 상태 관리가 가능한 useState, 렌더링 직후 작업 설정하는 useEffect 등 여러 기능들이제공하고 있다. 상태관리가 가능한 useStateconst [상태값, 상태를 설정하는 함수] = useState(기본값)useState에 기본값을 설정하면 [상태값, 상태를 설정하는 함수]를 리턴한다. 123456789101112131415161718192021//16.8 버전부터 useState 사용가능import React, { useState } from \"react\";// 함수형 컴포넌트const Counter = () => { //useState에 0을 넣는 것 ==> 기본값을 0 으로 설정 //useState() 호출후 반환하는 배열 ==> [상태값, 상태를 설정하는 함수] const [value, setValue] = useState(0); return ( <div> <p> 현재 카운터 값은 <b>{value}</b> 입니다. </p> <button onClick={() => setValue(value + 1)}>+1</button> <button onClick={() => setValue(value - 1)}>-1</button> </div> );};export default Counter; 렌더링 될때마다 특정 작업을 수행하도록 설정할 수 있는 useEffect생명주기에 따라 특정 작업을 수행할 수 있다.123456//// *** componentDidMountuseEffect(() => {}, [])///// *** componentDidUpdateuseEffect(() => {}, [배열안에 검사하고 싶은 값 (현재 상태관리되고 있는 값 / props로 전달받은 값)])///// *** componentUnMount, componentWillUpdateuseEffect(() => {}, return () => {}); 123456789101112131415161718192021222324252627282930313233343536373839import React, { useState, useEffect } from 'react';const Info = () => { const [name, setName] = useState(''); const [nickname, setNicname] = useState(''); useEffect(() => { console.log(\"렌더링이 완료되었습니다\") console.log({name, nickname}) return () => { console.log(\"cleanup\"); console.log(name); } }, [name]) const onChangeName = e => { setName(e.target.value) } const onChangeNickName = e => { setNicname(e.target.value) } return( <div> <div> <input value={name} onChange={onChangeName} /> <input value={nickname} onChange={onChangeNickName}/> </div> <div> <b>이름: </b>{name} <b>닉네임: </b>{nickname} </div> </div> )}export default Info; 컴포넌트 업데이트 로직을 외부로 분리가능한 useReducer바로 위에 Info예제를 보면 onchange이벤트를 각각 잡아서 함수를 생성하고 있다.useReducer를 사용하면 기존에 redux에서 사용하던 action에 따라 상태값을 바꿀 수 있다.const [state(현재 가르키고 있는 상태), dispatch(액션을 발생시키는 함수)] = useReducer(리듀서 함수, 기본값)12345678910111213141516171819202122232425262728import React, { useReducer } from \"react\";function reducer(state, action) { switch(action.type) { case 'INCREMENT': return { value: state.value + 1 } case 'DECREMENT': return { value: state.value - 1 } default: return state; }}const Counter_reducer = () => { const [state, dispatch] = useReducer(reducer, { value: 0 }); return ( <div> <p> 현재 카운터 값은 <b>{state.value}</b> 입니다. </p> <button onClick={() => dispatch({ type: 'INCREMENT' })}>+1</button> <button onClick={() => dispatch({ type: 'DECREMENT' })}>-1</button> </div> );};export default Counter_reducer; 렌더링 성능 최적화 useMemo렌더링하는 과정에서 특정값이 바뀌었을 때만 연산을 실행한다.숫자, 문자열, 객체처럼 일반 값을 재사용할 때 useMemo를 사용한다.12345678910111213141516171819202122232425262728293031323334353637383940414243444546import React, { useState, useMemo, useCallback, useRef } from 'react';const getAverage = numbers => { console.log('평균값 계산중..'); if (numbers.length === 0) return 0; const sum = numbers.reduce((a, b) => a + b); return sum / numbers.length;};const Average = () => { const [list, setList] = useState([]); const [number, setNumber] = useState(''); const onChange = e => { setNumber(e.target.value); }); // 컴포넌트가 처음 렌더링 될 때만 함수 생성 const onInsert = e => { const nextList = list.concat(parseInt(number)); setList(nextList); setNumber(''); }; // number혹은 list가 바뀌었을 때만 함수 생성 //렌더링 성능 최적확: useMeomo //렌더링하는 과정에서 특정값이 바뀌었을 때만 연산 실행, (list배열의 내용이 바뀔때마다 getAverage함수 호출) const avg = useMemo(() => getAverage(list), [list]); return ( <div> <input value={number} onChange={onChange} ref={inputEl} /> <button onClick={onInsert}>등록</button> <ul> {list.map((value, index) => ( <li key={index}>{value}</li> ))} </ul> <div> <b>평균 값:</b> {avg} </div> </div> );};export default Average; 위와 같이 useMemo를 사용하지 않으면 렌더링 될때마다 getAverage라는 평균값을 계산하는 함수가 실행된다.useMemo에 두번쩨 파라미터 []에 list가 들어간것은 list값이 바꼈을 때에만 실행하라는 의미이다.즉 onInsert함수를 통해서 숫자가 추가될 때 getAverage가 실행된다. 렌더링 성능 최적화 useCallback기존에는 리렌더링 할 때마다 함수를 새로 생성하지만 useCallback는 이벤트 핸들러 함수를 필요할 때 생성한다.useCallback(생성하고 싶은 함수, 배열: 어떤 값이 바뀌었을 때 함수를 새로 생성해주어야 하는지 명시)1234/// **** 컴포넌트 처음 렌더링 될 때useCallback(() => {}, [])/// **** number나 list값이 바뀌었을 때useCallback(() => {}, [number, list]) 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950import React, { useState, useMemo, useCallback, useRef } from 'react';const getAverage = numbers => { console.log('평균값 계산중..'); if (numbers.length === 0) return 0; const sum = numbers.reduce((a, b) => a + b); return sum / numbers.length;};const Average = () => { const [list, setList] = useState([]); const [number, setNumber] = useState(''); const inputEl = useRef(null);// useCallback: 렌더링 성능 최적화, 이벤트 핸들러 함수 필요할 때만 생성// 기존은 리렌더링 할때마다 함수 새로 생성...// useCallback(생성하고 싶은 함수, 배열: 어떤 값이 바뀌었을 때 함수를 새로 생성해주어야하는지 명시) const onChange = useCallback(e => { setNumber(e.target.value); }, []); // 컴포넌트가 처음 렌더링 될 때만 함수 생성 const onInsert = useCallback(e => { const nextList = list.concat(parseInt(number)); setList(nextList); setNumber(''); // useRef로 만든 객체 안의 current값이 실제 엘리먼트 inputEl.current.focus(); }, [number, list]); // number혹은 list가 바뀌었을 때만 함수 생성 //렌더링 성능 최적확: useMeomo //렌더링하는 과정에서 특정값이 바뀌었을 때만 연산 실행, (list배열의 내용이 바뀔때마다 getAverage함수 호출) const avg = useMemo(() => getAverage(list), [list]); return ( <div> <input value={number} onChange={onChange} ref={inputEl} /> <button onClick={onInsert}>등록</button> <ul> {list.map((value, index) => ( <li key={index}>{value}</li> ))} </ul> <div> <b>평균 값:</b> {avg} </div> </div> );};export default Average; 위에 예제에 추가로 useRef를 사용했다.useRef는 함수형 컴포넌트에서 ref를 쉽게 사용할 수 있게 해준다. 참고: https://github.com/juuuuuuuuuuuuuu/react-test","categories":[],"tags":[{"name":"javascript, react, hook","slug":"javascript-react-hook","permalink":"https://juuuuuuuuuuuuuu.github.io/tags/javascript-react-hook/"}]},{"title":"테스트 코드","slug":"testing","date":"2019-05-28T13:18:03.000Z","updated":"2019-09-03T09:19:56.089Z","comments":true,"path":"2019/05/28/testing/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/05/28/testing/","excerpt":"","text":"사실 얼마전까지만해도 단위 테스트를 왜 작성해야하는지 와닿지 않았다.코드를 테스팅하는것도 알고리즘 문제를 풀면서 여러 사이트에서 제공해주는 거로 처음 접했었다.실제로 알고리즘 문제를 사이트에서 풀 때 이렇게 하면 되겠지 했는데 여러 케이스별로 테스팅 코드를 작성해보니 문제들이 많았다.테스팅의 중요성을 느끼게 되었다. 먼저 이 포스팅에는 리액트에서 테스팅 코드를 작성하는 방법에 대해서 정리할 것이다.후에 직접 프로젝트를 하나 만들어서 직접 테스팅 코드를 구현할 것이다. ###Jest페이스북에서 만든 테스팅 도구이다.기본적으로 CRA로 설치하면 포함되어 있다.jest메소드 확인 : https://jestjs.io/docs/en/expect.html ###react-test-renderer스냅샷 테스팅을 하는 도구이다.컴포넌트를 주어진 설정으로 렌더링하고 그 결과물을 파일로 저장한다.그리고 다음번 테스팅할 때 이전의 결과물과 일치하는지를 확인한다.컴포넌트 내부 메소드를 호출시키고 다시 렌더링시켜서 그 결과물도 스냅샷을 저장시켜 각 상황에 모두 이전에 렌더링했던결과와 일치하는지를 비교할 수 있다. ###enzymeairbnb에서 만든 리액트 컴포넌트 테스팅도구이다.세밀하게 테스팅가능하며 dom 이벤트 시뮬레이트 및 모든 라이프사이클 확인이 가능하다.","categories":[],"tags":[{"name":"javscript, jest, enzyme","slug":"javscript-jest-enzyme","permalink":"https://juuuuuuuuuuuuuu.github.io/tags/javscript-jest-enzyme/"}]},{"title":"todolist 목록들","slug":"todolist","date":"2019-05-28T03:15:58.000Z","updated":"2019-05-28T03:34:07.622Z","comments":true,"path":"2019/05/28/todolist/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/05/28/todolist/","excerpt":"","text":"자바스크립트 배경 히든 클래스 테스팅 코드 redux 프로젝트 mobx 포스팅 typescript 프로젝트","categories":[],"tags":[]},{"title":"parameter vs arguments","slug":"arguments","date":"2019-04-25T06:24:33.000Z","updated":"2019-09-03T02:40:34.703Z","comments":true,"path":"2019/04/25/arguments/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/04/25/arguments/","excerpt":"","text":"parameter는 매개변수이다.함수를 정의하는 부분에 포함되어 있는 고유한 특성이다. argument는 전달인자이다.함수를 호출될 때 제공되는 값이다. 12345function test(x, y) { // x, y는 parameter return x + y;}test(1, 2); // 1,2는 arguments","categories":[],"tags":[{"name":"javascript","slug":"javascript","permalink":"https://juuuuuuuuuuuuuu.github.io/tags/javascript/"}]},{"title":"http/https","slug":"http-https","date":"2019-04-18T08:57:44.000Z","updated":"2019-04-25T09:46:44.084Z","comments":true,"path":"2019/04/18/http-https/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/04/18/http-https/","excerpt":"","text":"Mixed Content 즉, 혼합 컨텐츠 이슈이다.혼합 컨텐츠는 https로 요청한 자원과 http로 요청한 자원이 섞여 있는 것을 의미한다.최초로 html이 안전한 https 연결을 통해 로드를 하는데 다른 리소스(이미지,동영상,스타일시트,스크립트 등)이 안전하지 않은http로 연결이 될 때 발생하는 이슈이다. 혼합 콘텐츠의 종류수동적(passive) 혼합 콘텐츠이미지나 오디오와 같은 콘텐츠를 http로 요청하는 것이다.공격자가 중간에 가로채어 파일을 수정하더라도 사이트 전체에 상호작용이 일어나는 콘텐츠가 아니므로 능동적 혼한 콘텐츠보다영향이 덜하다. 그러나 음란물로 변경될 수 있는 위험은 있다. 크롬 개발자 모드에서 노란색으로 표현이 된다. 능동적(active) 혼합 콘텐츠브라우저가 실행하게 되는 스크립트, CSS, ifreame, 플래시 등의 콘텐츠를 http로 요청하는 것이다.공격자가 중간에 가로채어 코드를 수정하면 사이드 전체에 영향이 가게 되어 위험하다.스크립트를 악의적으로 변경하여 이상한 URL로 리다이렉트되거나, 사용잦의 정보를 빼내는 스크립트 등으로 사용될 수 있어 위험하다.크롬 개발자 모드에서 빨간색으로 보여준다. http란?html과 같은 문서를 웹 브라우저가 웹 서버에 요청하는 프로토콜이다.즉 텍스트 교환을 하는 것이다. http는 암호화가 되어 있지 않아 내가 있는 네트워크에 누군가가 접속을 해서 내용을 가로챌 수 있다. https란?http와 동일하지만 암호화되어 있다.즉 인터넷 상에서 정보를 암호화하는 프로토콜을 이용하여 웹 브라우저와 서버가 데이터를 주고 받는 통신 규약이다.암호화를 할때에는 2개의 키가 필요하다. 하나의 키는 공개키로 공개키 저장소에 등록을 해 놓고 다른 하나는 서버만 알 수 있는 개인키를 소유하고 있다. https 통신의 흐름에 대해서 알아보자공개키 저장소라는 CA(Certificate Authority)이다.CA는 민간기업이지만 아무나 운영할 수 없고 신회성이 검증된 기업만 운영이 가능하다고 한다.가끔씩 동사무소 사이트같은 곳을 들어가면 https지만 주의요함, 안전하지 않은 사이트등의 알림이 뜨는 경우가 있다.이는 신뢰할 수 없는 CA 기업을 통해서 인증서를 발급받은 사이트라는 것이다. ㄷ ㄷ ;;세계적으로 신뢰할 수 있는 CA기업의 공개키는 브라우저가 이미 알고 있다고 한다.","categories":[],"tags":[{"name":"javascript, cors, http, https","slug":"javascript-cors-http-https","permalink":"https://juuuuuuuuuuuuuu.github.io/tags/javascript-cors-http-https/"}]},{"title":"jquery 이벤트","slug":"event","date":"2019-04-16T14:15:42.000Z","updated":"2019-04-17T06:35:02.989Z","comments":true,"path":"2019/04/16/event/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/04/16/event/","excerpt":"","text":"jquery를 공부하면서 알게 된 이벤트에 대해서 정리해보자 1. on / off 차이element에 이벤트를 정의할 때는 on을 사용한다.on의 첫번째 인자에는 object형태로 여러개의 이벤트들이 바인딩 될 수 있다.두번째 인자는 선택한 요소의 자손을 필터링하여 이벤트를 트리거한다.off는 이벤트 핸들러를 제거할 때 사용한다. 12345678910111213141516171819//기본 사용법 element.on('click', function() {});//여러 이벤트 바인딩 element.on({ 'click' : function(){...}, 'onchange' : function(){...} });// bad code : 이벤트 바인드 개수가 li 요소 개수만큼 비효율적 $(\"ul li\").on(\"click\", function(){});// good code : ul 태그 하나에 이벤트를 붙인다. 하나의 이벤트 바인딩 $('ul').on('click', 'li', function() {});//이벤트 핸들러 제거 element.off('click', function() {}); 작업을 하던 도중 이벤트가 트리거 될때가 있었다. 이 때는 on/off를 사용하여 해결하였다.클릭을 할 때마다 이벤트가 두번 발생하여 아래와 같이 해결하였다.이 외에도 여러 방법들이 있다.참고: https://stackoverflow.com/questions/3070400/jquery-button-click-event-is-triggered-twice12element.off().on('click', function() {}); 2. css에서 이벤트 막기여러개의 태그가 겹쳐져있을 때 특정 태그에 이벤트를 제거하고 싶을 때가 있다.이 때 pointer-events: none;으로 css속성을 주면 된다.그러나 이 속성은 IE 10이하에서는 안된다고 한다.대체 방법으로는 일일히 태그 찾아서 해당 이벤트일 때마 액션을 취해야한다. 3. input이벤트input을 실시간으로 이벤트를 감지할때 react에서는 onchange이벤트를 사용했다.그러나 자바스크립트에서 onchange는 포커스가 해제될 때 발생을 한다. keyup같은 경우는특정 모바일이나 디바이스에 따라 문제가 있다.jquery의 input 이벤트는 input이나 textarea요소에 값이 변경이 되면 동기적으로 발생하는 이벤트이다.모던 브라우저에서 모두 작동을 하나 IE8이하에서는 작동하지 않는다.","categories":[],"tags":[{"name":"javascript, jquery","slug":"javascript-jquery","permalink":"https://juuuuuuuuuuuuuu.github.io/tags/javascript-jquery/"}]},{"title":"window vs document","slug":"window-document","date":"2019-03-27T08:09:43.000Z","updated":"2019-03-27T08:16:56.000Z","comments":true,"path":"2019/03/27/window-document/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/03/27/window-document/","excerpt":"","text":"window는 브라우저 전체를 담당하는 객체이다. 모든 객체의 조상이다. 전역객체라고 한다.document는 html에 관한 것들을 담당하는 객체이다.태그를 선택하는 방법에는 id, name, tag, class가 있다.document.getElementById, document.getElementsByClassName, document.getElementsByName, document.getElementsByTagName css선택자로 선택을 할 수 있다.document.querySelector(선택자), document.querySelectorAll(선택자) document.createDocumentFragment()는 가짜 document를 만드는 것이다.document의 태그로 조작하는것은 성능이 매우 떨어진다.보통 반복문을 통해 추가할때 가짜 document을 만들어 여기에 추가하고 한번에 document에 추가한다. 123456var div = document.createElement('div');var text = document.createTextNode('텍스트');var fragment = document.createDocumentFragment();div.appendChild(text);fragment.appendChild(div);document.body.appendChild(fragment);","categories":[],"tags":[]},{"title":"프로토타입이란?","slug":"prototype","date":"2019-03-27T06:00:10.000Z","updated":"2019-03-27T06:43:26.000Z","comments":true,"path":"2019/03/27/prototype/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/03/27/prototype/","excerpt":"","text":"자바스크립트의 모든 객체들은 부모 역할을 하는 객체와 연결이 되어 있다.부모 역할을 하는 객체를 prototype이라고 한다. 자바스크립트에서 객체를 생성하는 방법에는 객체 리터럴을 통해서나 생성자를 통해서 만들 수 있다.1234567function A() {}let AObject = new A(); //함수로 객체 생성let obj = {}; // 객체 리터럴로 생성 AObject.prototypeobj.prototype 위와 같이 객체를 생성하면 constructor와 __proto__를 가지고 있다.constructor는 생성되었던 함수이며 함수는 object이기 때문에 object의 prototype또한 상속 받아 사용할 수 있다.이러한 것을 프로토타입 체이닝이라고 한다.__proto__는 prototype Link이다. 이번에 people.prototype.gender 속성을 추가했다.lee라는 객체에 gender가 없는데 female이라는 값이 찍히는 것을 볼 수 있다. __proto__을 타고 가보면 객체가 생성될 때 조상이었던 함수의 prototype object를 가리키고 있다.즉, lee가 gender속성을 가지고 있지 않기 때문에 gender라는 속성을 찾을 때 까지 상위 프로토타입을 검색한다.또한 최상위의 object의 prototype object에 도달함에도 찾지 못할때는 undefined를 리턴한다.이렇게 상위 프로토타입과 연결되어 있는 형태를 프로토타입 체인이라고 한다. 쉽게 그림으로 나타내면 이런식이 되는거다.","categories":[],"tags":[]},{"title":"webpack이란?","slug":"webpack","date":"2019-03-10T10:55:22.000Z","updated":"2019-03-10T12:16:27.000Z","comments":true,"path":"2019/03/10/webpack/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/03/10/webpack/","excerpt":"","text":"웹팩이란 한 프로그램으로 작동하는 하나의 파일을 여러 파일로 분할하고 구성할 수 있는 자바스크립트 모듈이다.브라우저가 이해할 수 있도록 번들로 묶고 변환하는 과정을 거친다.프로젝트 전체를 한단위로 분석한다. 지정된 메인 파일에서 시작하여 require 또는 import문을 참고해 프로젝트의 모든 의존성을 조사하고 로더를 이용해 처리한 후번들로 묶은 자바스크립트 파일을 생성한다. webpack에 대한 옵션 및 세팅에 대해 알아보자. entry의존성 트리의 루트 노드가 되는 진입점이다.entry point는 상황에 따라 하나가 될 수 있고 여러개가 될 수 있다.Key-value 쌍으로 object 형태로 정의되며 key는 식별자, value는 해당 파일이 위치한 경로이다. outputentry로 부터 만들어낸 Build 결과물이 생성될 위치를 정의한다.output.path를 통해 위치에 대한 상대경로 정의, output.filename과 output.chunkfilename으로 build된파일의 이름을 정의한다. resolverequire에서 모듈명을 어떻게 해석할지 정의하는 옵션, 관련된 모듈을 어느 위치에 있는 파일에서 가져올지 정의해준다. module의존성 트리내에서 각 모듈을 어떻게 핸들링할지 정의하는 옵션이다.modules.rules 식별자에 배열 형태로 각 로더 정의해주며 주의할 점은 배열의 오른쪽에서 왼쪽 순서로 적용되며각로더들을 module.rules[i].test에 정규식을 통해 들어오는 소스파일을 컨트롤할 수 있다.그러나 모든 파일이 모듈 옵션내 loader를 거쳐서 build 시키기 때문에 해당 시점에서 시간이 많이 걸리는 이슈가 있었다.그래서 loader를 거치면서 build상황에서 최적화된 속도를 낼 수 있도록 도와주는 플러그인인 happypack을 사용하였다. pluginloader와 plugin은 비슷해보이지만 다르다.loader는 각 모듈을 어떻게 불러올 것인지?pluglin은 building이 끝난 후 최종적으로 나온 bundle파일에 대한 옵션을 주고 싶을 때 사용한다.플러그인에 대해서 알아보도록 하자. Webpack.ProvidePlugin자바스크립트에서 필요한 모듈을 import / require하지 않고 해당 plugin에 정의함으로써자동으로 로드 될 수 있도록 도와주는 플러그인이다. Webpack.DefinePlugin컴파일 시간에 구성할 수 있는 전역 상수를 만드는 플러그인이다.주로 process.env를 사용하여 빌드 상태가 development인지 production 상태인지 구분한다. Uglifyjs-webpack-plugin번들링된 자바스크립트 파일에 대해 uglify같은 옵션을 주기 위해 사용되고 있는 플러그인이며 cache설정, sourceMap사용여부, mangle(난독화), comments(주석제거여부), beautify(가독성적용),unused(tree-shacking적용), drop_console(console로그 제거) 등 많은 옵션을 사요할 수 있다. Webpack.LoaderOptionPluginbuild시 사용되고 있는 loader들에게 공통적으로 옵션을 넣어주는 플러그인으로 minimize옵션을 공통적으로 주기 위해 사용한다. Html-webpack-plugin번들링된 html파일을 최소화 시키기 위해 webpack에서 공통적으로 사용하고 있는 플러그인이며 build가 끝나고 만들어진 html파일에 대한 파일명을 재정의 시킬 수 도 있다. Webpack.NameModulesPlugindevelopment상황에서만 사용되는 플러그인으로 HMR(Hot Module ReplaceMent)이 활성화 되어 있을 때 모듈의 상대 경로를 표시해주는 플러그인이다. Webpack.HotModuleReplacementPlugindevelopment 상황에서 webpack-dev-server 함께 사용되는 중요한 플러그인으로 Hot Module Replacement인 코드 변경을 감지하고자동으로 recompiling하여 페이지를 refresh시켜주는 역할을 지원하는 플러그인이다. Webpack.HashedModuleIdsPluginBundle된 파일에 대한 Hash값 적용을 지원하는 플러그인으로, 모듈의 상대 경로를 기반으로 4개의 문자열을 모듈 ID로 생성시켜주며,production 상황에서 사용된다. transfer-webpack-pluginBuild 폴더가 만들어질 때 Build 폴더 안에 www 폴더를 넣기 위해 사용하는 플러그인이다. extract-text-webpack-pluginrequire를 통해 정의된 css파일을 하나의 bundle 파일로 Build 시키기 위해 필요한 플러그인으로,style-loader와 css-loader 등과 같이 사용된다. Webpack.optimize.CommonsChunkPlugin여러 Entry Point에서 사용하는 공통 모듈을 별도의 파일로 분리하는 플러그인으로, 분리된 공통 모듈은 초기에 한 번만 다운받으며,추후 사용 시에는 캐싱되어 사용됨으로써, 페이지 로딩 속도를 빠르게 도와준다.","categories":[],"tags":[]},{"title":"포트폴리오","slug":"portfolio","date":"2019-02-10T06:07:52.000Z","updated":"2019-03-11T14:00:46.000Z","comments":true,"path":"2019/02/10/portfolio/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/02/10/portfolio/","excerpt":"","text":"1. 공통 UI 컴포넌트1) 컴포넌트 가이드 사이드 제공 : 예제 , 속성 제공 ▲ 컴포넌트 가이드 ( 예제, 속성 ) 2) 컴포넌트 개발 및 안정화, 고도화를 위한 유지보수-input 관련 컴포넌트: 자동완성 컴포넌트(debouncing을 사용한 검색어 처리), select 컴포넌트-provider 컴포넌트: app.js 상단에 제공(다이얼로그, 액션바)-interaction 컴포넌트: 알람 컴포넌트(css transition 속성을 통한 애니메이션 처리, immutable 라이브러리를 사용한 배열 처리)-virtualbox 컴포넌트: 스크롤 위치를 계산하여 dom에 렌더링 처리-그 외 12건 (subheader, pagenation, chatbot, button, radio button, checkbox, toggle)-회계 페이지 특화 포커싱 처리 컴포넌트 개발 (자동완성 컴포넌트, input, swtich 컴포넌트) ▲ Input 관련 컴포넌트 (select 컴포넌트, 자동완성 컴포넌트) ▲ Provider 컴포넌트 (다이얼로그, 액션바 컴포넌트) ▲ 인터렉션 컴포넌트 (알람, 팝오버 컴포넌트) ▲ virtutalBox 컴포넌트 ▲ subHeader 컴포넌트 ▲ Switch 관련 컴포넌트(북마크, 체크박스, 라디오, 토글) ▲ 회계 페이지 특화 포커싱 처리 컴포넌트 개발 3) 차트 컴포넌트- 타임라인 컴포넌트 - timeline.js를 커스텀(스타일, 이벤트 속성)하여 타임라인 컴포넌트 구현- 트리맵 컴포넌트 - d3라이브러리를 커스텀(툴팁기능추가, 길이 및 색상 속성) 하여 treemap차트 구현 ▲ 타임라인 컴포넌트 ▲ 트리맵 컴포넌트 2. API 모니터링실시간 데이터를 반영한 API 모니터링툴실시간으로 움직이는 동적 그래프 제공시간별 조회 및 드래그 기능을 통한 구간 검색 기능각종 외부 라이브러리 차트를 커스텀하여 구현(echart, d3, rickshaw) ▲ 메인화면 ( echart 라이브러리 사용 및 실시간 데이터 배열 처리) ▲ 각 서비스별 세부 화면 ( rickshaw 차트 소스 커스텀 및 드래그 기능 추가, 스타일 커스텀 ) ▲ 그 외 세부페이지 구현 (d3 라이브러리를 사용한 세부페이지 구현) 3. 화상대화webRTC 를 사용한 화상대화 기능 구현채팅, 문서 공유 및 화면공유, 문서뷰어 등 기능참여한 부분: 사용자간 통신 및 문서뷰어 기능 ▲ 사용자간 통신 ▲ 문서 뷰어 기능 4. 파일뷰어문서 뷰어 어플리케이션 구현다양한 보기모드(한페이지, 여러 페이지, 페이지 맞춤기능) 및 확대,축소,프린트 기능파일간 이동 기능 (키보드 좌우 나 좌우 네비게이션을 통한 탐색가능)pdfjs라이브러리를 사용하여 PDF 변환 및 그 외 파일은 한컴 서버로 변환virtual scroll 기능 구현을 통한 페이징 처리 ▲ 파일 뷰어 6. 웹 쇼핑몰 구현자바기반으로 쇼핑몰 구현 (웹/앱)사용자의 개성에 맞게 코디기능 (드래그앤 드랍), 날씨에 따른 코디 추천(알고리즘), SNS 기능(앱)참여한 부분: 프로젝트 설계 및 서버구축(스프링 90%, DB연동 100%) ▲ 코디하기 기능","categories":[],"tags":[]},{"title":"graphql","slug":"graphql","date":"2019-02-08T01:58:27.000Z","updated":"2019-02-10T06:07:01.000Z","comments":true,"path":"2019/02/08/graphql/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/02/08/graphql/","excerpt":"","text":"GraphQL(Graph Query Language)이란 페이스북에서 만든 쿼리 언어이다.기존에 RESTful API는 overfetch(요청한것보다 더 많은 정보를 받음) 나 underfetch(rest하나를 완성하려고 많은 소스를 요청 예를 들어, 인스타그램의 경우좋아요api, 사용자 api, 게시물 api등등) 의 문제점들이 있었다. 또한 ios, android 에서 필요한 정보들이 달라각각 API를 구현하는 것이 힘들었다. GraphQL vs RESTfulGraphQL는 전체 API를 위해서 단 하나의 Endpoint만 사용하며 사용자의 응답의 구조를 내가 원하는 대로 바꿀 수 있다.또한 서로 다른 모양의 다양한 요청들에 대해 응답할 수 있어야 할 때, 대부분의 요청이 CRUD(Create-Read-Update-Delete) 에 해당할 때 사용한다.RESTful API는 리소스마다 하나의 Endpoint를 가지며 주로 api를 작성할때 이미 정해놓은 구조로만 응답이 오게 된다.또한 HTTP 와 HTTPs 에 의한 Caching 을 잘 사용하고 싶을 때, File 전송 등 단순한 Text 로 처리되지 않는 요청들이 있을 때, 요청의 구조가 정해져 있을 때주로 사용한다. 그러나 API를 섞어서 사용자 정보등륵은 resful을 사용하고 정보수정은 graphql을 사용한다면api의 품질을 떨어뜨릴 수 있다.현재 프로젝트에 어떤것을 사용할지 결정하는 것이 중요하다. GraphQL 사용법1. 프로젝트 세팅먼저 graphql-yoga를 설치한다. 서버는 nodemon을 사용한다.typeDefs는 타입에 대한 정의이다. 여기서는 타입들이 있는 경로이다.resolvers는 쿼리를 해결하는 것이다.12345678import {GraphQLServer } from 'graphql-yoga';import resolvers from './graphql/resolvers';const server = new GraphQLServer({ typeDefs: \"graphql/schema.graphql\", resolvers}); graphql/schema.graphql 파일 type을 지정해주는 부분은 어떤 타입으로 받을 건지 각각의 보내는 파라미터까지 타입을 지정해준다.!는 필수값을 의미한다. Query는 단순히 값을 가져올때 사용하고 수정이나 삭제등 변형이 가해지는 것들은 Mutation으로 분리한다.1234567891011121314151617181920212223242526type Movie { id: Int! name: String! score: Int!}type Movie2 { id: Int! title: String! rating: Float! summary: String! language: String! medium_cover_image: String!}type Query { movies: [Movie]! movie(id: Int!): Movie apiMoive(limit: Int, rating: Float): [Movie2]!}type Mutation { addMovie(name: String!, score: Int!): Movie! deleteMovie(id: Int!): Boolean!} 2. db세팅12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849let movies = [ { id: 0, name: \"Star Wars - The new one\", score: 1 }, { id: 1, name: \"Avengers - The new one\", score: 8 }, { id: 2, name: \"The Godfather I\", score: 99 }, { id: 3, name: \"Logan\", score: 2 }];export const getMovies = () => movies;export const getById = id => { const filteredMovies = movies.filter(movie => movie.id === id); return filteredMovies[0];};export const deleteMovie = id => { const cleanedMovies = movies.filter(movie => movie.id !== String(id)); if (movies.length > cleanedMovies.length) { movies = cleanedMovies; return true; } else { return false; }};export const addMovie = (name, score) => { const newMovie = { id: `${movies.length + 1}`, name, score }; movies.push(newMovie); return newMovie;}; 이런식으로 db를 세팅할 수 있으며 restful api를 감쌀 수 도 있다. 123456789101112131415import fetch from \"node-fetch\";const API_URL = \"https://yts.am/api/v2/list_movies.json\";export const getAPIMovies = (limit, rating) => { let REQUEST_URL = API_URL; if (limit > 0) { REQUEST_URL += `limit=${limit}`; } if (rating > 0) { REQUEST_URL += `&minimum_rating=${rating}`; } return fetch(REQUEST_URL) .then(res => res.json()) .json(json=> json.data.moives)} 3.resolvers 세팅resolvers는 쿼리를 쿼리를 해결해 주는 역할을 한다.123456789101112131415import { getById, getMovies, addMovie, deleteMovie, getAPIMovies } from './db';const resolvers = { Query: { movies: () => getMovies(), movie: (_, { id }) => getById(id), APIMovies: (_, { rating, limit }) => getAPIMovies(limit, rating) }, Mutation: { addMovie: (_, { name, score }) => addMovie(name, score), deleteMovie: (_, { id }) => deleteMovie(id) }};export default resolvers; 서버를 실행하면원하는 정보만 쿼리를 날려서 사용하면 된다. 생각보다 너무 간단하고 쉽다. 또한 GraphQL에서 제공해주고 있는 툴은 현재 내가 가지고 있는 API들을 한눈에 볼 수 있으며너무 편리하다. 기회가 되면 사용하는 프로젝트에 적용해보고 싶다. github 저장소: https://github.com/juuuuuuuuuuuuuu/graphql.git","categories":[],"tags":[{"name":"GraphQL","slug":"graphql","permalink":"https://juuuuuuuuuuuuuu.github.io/tags/graphql/"}]},{"title":"react-router v4","slug":"v4","date":"2019-01-22T07:49:59.000Z","updated":"2019-02-07T06:02:49.000Z","comments":true,"path":"2019/01/22/v4/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/01/22/v4/","excerpt":"","text":"기존 프로젝트 버전 업하는 작업을 하기전 테스트를 해보았다. 기존버전 신규버전 react 15.3.2 react 16.7.0 webpack 1.13.2 webpack 4.19.0 babel-loader 6.2.5 babel-loader: 7.0.0 babel-core 6.14.0 babel-core 7.0.1 Babel7.babelrc 파일에 설정을 해줘야한다.12345678910111213141516{ \"presets\": [ \"@babel/preset-env\", \"@babel/preset-react\" ], \"plugins\": [ \"@babel/plugin-transform-object-assign\", \"@babel/plugin-proposal-class-properties\", \"@babel/plugin-transform-async-to-generator\", \"@babel/plugin-transform-regenerator\", \"@babel/plugin-syntax-export-default-from\", \"transform-export-extensions\", \"@babel/plugin-syntax-dynamic-import\", \"@babel/plugin-syntax-object-rest-spread\" ]} babel7+ 버전부터는 babel-plugin-syntax-trailing-function-commas 를 사용하여 파라미터들을 나열할 때 마지막 파라미터에 콤마(,)를 허용한다.그러나 마지막 파라미터에 object spread 연산자가 있는 경우 콤마 (,) 를 허용하지 않는다. 123456789101112const { style, color, width, // 가능} = this.props; const { style, color, ...other, ---> 이 부분을 ...other로 수정} = this.props; Router v4 - browserRouter react-router v4부터는 라우터가 컴포넌트 개념이라고 생각하면 된다. hashRouter를 사용하는 경우 현재 프로젝트와 동일하게 작업을 하면 된다. 그러나 browserRouter를 사용하는 경우에는 아래와 같이 webpack.config 파일 설정이 필요하다. 12345678module.exports = { ... devServer: { contentBase: path.join(__dirname,'src'), disableHostCheck: true, // realgrid때문에 host파일 설정을 하는데 설정을 해줘야 lululala.co.kr이런식으로 접근할 수 있다. historyApiFallback: true // browserRouter사용하는 경우 /밑에 app안에 정의된 router 로 이동할 수 있다. },} browserRouter를 사용하는 경우에 www.lululala.co.kr/rocket/LUXButton –> 이 경우 build된 파일의 경로를 바로 잡지 못해 아래와 같은 에러가 발생한다.이는 해당 주소를 서버에 요청하면 서버 쪽 라우터에서 먼저 연결할 곳이 있는지 확인해보고 없으면 오류가 발생한다.이를 해결하기 위해서는 webpack에서 publicPath 설정을 해야한다.publicPath란 서비스가 시작되는 루트경로를 제외한 상대경로를 설정하는 것이다. 즉, 웹팩에 의해 빌드될 때 사용되는 경로이다. 기존 publicPath 적용 후 Router Splitting기존에는 react-loadable 라이브러리를 사용하여 페이지가 로더될 때와 로딩될 때를 구분하여 사용할 수 있었다.1234567891011import loadable from 'react-loadable'; const AboutComponentPage = () => { return import('./about');} const AsynAboutComponent = loadable( { loader: AboutComponentPage, // 로드될 페이지 loading: Loading //로딩 될때}); react 16.6 부터 react에서 지원하는 Lazy, Suspense를 사용하면 된다.dynamic import 를 통해서 사용한다. 아직 로딩이 되지 않은 lazy컴포넌트의 fallback을 suspense 를 통해 표시할 수 있다. 123456789101112import React, { Suspense, lazy } from 'react'const ErrorDialogComponent = lazy(() => import('./Documents/LUXErrorDialog/ErrorDialogDocument'));const Loading = () => <div>loading...</div> render{ return ( <Suspense fallback={<Loading />}> <Route path={`${this.props.match.path}/LUXButton`} render={(props) => <ErrorDialogComponent {...props} />} onEnter={scrollToTop}/> </Suspense> )} webpack41. 기본적으로 설정되어 있는 development모드와 production모드가 생김 2. 빌드 속도가 빨라짐 3. webpack core와 webpack-cli가 분리됨 4. CommonsChunkPlugin이 deprecated 되고 SplitChunksPlugin이 내장되어 있음 splitChunk 는 대형 프로젝트에서 거대한 번들 파일을 적절히 분리하고 나눌 수 있다. 파일 사이즈, 비동기 요청 횟수 등의 옵션에 따라 자동으로 분리할 수 있고 정규식에 따라서 특정 파일들만 분리할 수 있다. 또한 특정 엔트리 포인트를 분리할 수 있다. 번들 파일을 적절히 분리하면 브라우저 캐시를 전략적으로 활용할 수 있으며 초기 로딩속도를 최적화 할 수 있다. cacheGroups 는 특정 파일을 청크로 분리할 때 사용된다. commons은 청크를 분리한다. test 를 사용해 대상이 되는 파일을 정규식으로 잡는다. name은 청크로 분리할 때 이름으로 사용될 파일명이다. 여기서는 output.filename옵션에 [name]에 대치될 내용이다. chunks는 모듈의 종류에 따라 청크에 포함할지 말지를 결정하는 옵션이다. 옵션으로는 initial, async, all이 있다. all 은 test조건에 포함되는 모든 것을 분리하겠다는 뜻이다. initial은 초기 로딩에 필요한 경우 async는 import를 이용해 다이나믹하게 사용되는 경우에 분리한다. common은 모듈이 여러 청크 사이에서 두 번 이상 공유되는 경우 생성 한 다음 새 청크를 형성하는 경우이를 피할 수 있다. AsyncChunkNames 플러그인은 import 요청한 파일 이름을 분석하여 청크 이름을 추측하여 청크파일 이름을 생성한다. 파일명의 이름과 같게 생성을 한다. 12345678910111213141516171819202122232425262728module.exports = (env, options) => { const config = { //.. output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') }, optimization: { splitChunks: { cacheGroups: { commons: { test: /[\\\\/]node_modules[\\\\/]/, name: 'vendors', chunks: 'all' }, common: { name: 'common', minChunks: 2, chunks: \"all\", priority: 10, reuseExistingChunk: true, enforce: true }, } } } }} 참조: https://meetup.toast.com/posts/153 :https://itnext.io/react-router-and-webpack-v4-code-splitting-using-splitchunksplugin-f0a48f110312","categories":[],"tags":[{"name":"react-router v4, react16, wepack4, babel7","slug":"react-router-v4-react16-wepack4-babel7","permalink":"https://juuuuuuuuuuuuuu.github.io/tags/react-router-v4-react16-wepack4-babel7/"}]},{"title":"this란","slug":"this","date":"2019-01-17T02:39:09.000Z","updated":"2019-01-17T04:04:54.000Z","comments":true,"path":"2019/01/17/this/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/01/17/this/","excerpt":"","text":"this란 무엇인가?자바스크립트 개발자로 어느덧 2년차가 되어가지만 this가 뭐냐고 물으면 답을 못할 것 같다.그러므로 this에 대해 정리해보자. ###1. 실행문맥this란 실행문맥 즉, 호출자가 누구인지를 의미한다. 123456const caller = { f: function() { alert(this === window) },}caller.f() // false, 호출자는 caller 객체 ###2. 생성자 함수내의 this는 new를 통해 만들어진 새로운 변수1234567891011function NewObject(name, color) { this.name = name this.color = color this.isWindow = function() { return this === window }}const newObj = new NewObject('nana', 'yellow')console.log(newObj.name) // nanaconsole.log(newObj.color) // yellow ###3. bind, arrow function123456789function Family(firstName) { this.firstName = firstName const names = ['bill', 'mark', 'steve'] names.map((value, index) => { console.log(value + ' ' + this.firstName) })}const kims = new Family('kim') 실행하면 콘솔에 this.firstName값이 undefined로 찍히게 된다.이는 map의 context(this)로 바인딩 되지 않기 때문이다. 그래서 bind.this(this)를 붙이거나ES6부터는 arrow function(=>)을 사용하면 된다. 123456789function Family(firstName) { this.firstName = firstName const names = ['bill', 'mark', 'steve'] names.map((value, index) => { console.log(value + ' ' + this.firstName) })}const kims = new Family('kim')","categories":[],"tags":[{"name":"javascript","slug":"javascript","permalink":"https://juuuuuuuuuuuuuu.github.io/tags/javascript/"}]},{"title":"process_env","slug":"process-env","date":"2019-01-15T06:31:37.000Z","updated":"2019-01-15T07:21:59.000Z","comments":true,"path":"2019/01/15/process-env/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/01/15/process-env/","excerpt":"","text":"a라는 프로젝트에서 b,c의 라이브러리를 땡기고 있는 상황에서 에러가 발생하였다.b라이브러리가 참조하고 있는 d 프로젝트에서 전역상수인 process.env.NODE_ENV 값을 찾지 못해 발생한 에러였다.a프로젝트는 process.env.NODE_ENV값이 없기 때문이었다.이전에 대충 알기는 했는데 막상 에러가 나니까 어디서 발생한지 몰랐다.이번 기회에 알아보자… 먼저 webpack.config.js이 파일에 DefinePlugin이라는 로더를 사용해서 정의한다.DefinePlugin은 컴파일 타임에 구성할 수 있는 전역상수를 만든다.12345678plugins: [ new webpack.DefinePlugin({ 'process.env': { NODE_ENV: JSON.stringify(process.env.NODE_ENV), BUILD_ENV: JSON.stringify(process.env.NODE_ENV) } }),], 이렇게 process.env 값을 전역 상수로 설정하고 필요한 곳에서 마음대로 꺼내 쓸 수있다.보통 빌드 상태가 dev인지 production인지 분기처리할 때 사용한다.package.json 파일을 보면 아래와 같다. 1234567891011121314151617181920{\"start\": \"npm run play && NODE_ENV=dev\",\"play\": \"NODE_ENV=dev webpack-dev-server --config webpack-dev-server.config.js --progress --inline --colors\"}``` npm start 명령어를 실행하면 `NODE_ENV` 의 값은 dev가 된다.만약 명령어부분에 따로 설정을하지 않은 경우 로더 설정하는 부분에 아래와 같이 값을 고정시켜놓으면 된다. ``` javascriptplugins: [ new webpack.DefinePlugin({ 'process.env': { NODE_ENV: JSON.stringify('production'), BUILD_ENV: JSON.stringify('production') } }),], 한가지 더 종종 큰 프로젝트를 실행하다보면 아래와 같은 문구를 볼 수 있다.1\"FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory\" 메모리 문제이다. 그럴때는 해결책으로 install을 하면 된다.12npm install -g increase-memory-limitincrease-memory-limit 또는 아예 스크립트 start 명령어에 NODE_OPTIONS=--max_old_space_size= 설정을 해주면 된다. 오늘도 또한 느낀점은 내가 약 2년정도 동안 참 한게 많이 없고 많이 많이 부족한것을 느끼게 되었다… 후.. 열심히 공부하자.","categories":[],"tags":[{"name":"webpack","slug":"webpack","permalink":"https://juuuuuuuuuuuuuu.github.io/tags/webpack/"}]},{"title":"몰랐던 사실 (컴포넌트 생성)","slug":"surprise1","date":"2019-01-14T06:00:49.000Z","updated":"2019-01-14T06:04:33.000Z","comments":true,"path":"2019/01/14/surprise1/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/01/14/surprise1/","excerpt":"","text":"얼마전에 엄청 놀라운 것을 발견했다.react 16.4버전 이상 미지원 react-tap-event-plugin 를 지원하지 않아 onTouchTap이 작동하지 않게 되어 찾아보던 중material-ui 신규 라이브러리에서 알게된 사실이다.123456const btn = 'button';render() { return( <btn/> )} 이렇게하면 버튼 태그가 생성된다. 마찬가지로 span, input 다 만들어진다.새롭게 하나 배웠다.","categories":[],"tags":[]},{"title":"forLoop","slug":"forloop","date":"2019-01-14T05:21:37.000Z","updated":"2019-01-14T06:06:37.000Z","comments":true,"path":"2019/01/14/forloop/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/01/14/forloop/","excerpt":"","text":"기존에 배열같은 경우는 반복문을 사용할 때 용도에 따라서 forEach 와 map을 사용했었다.그런데 Object같은 경우는 반복문을 어떻게 사용해야하는지 순간 까먹었었다.그래서 반복문에 대해서 정리가 필요했다. forEachforEach문 밖으로 리턴값을 받지 못한다. 사용할때는 for문안에서 어떤한 작업을 하고 싶을 때 사용한다. 123let arr = [1,2,3];let b = arr.forEach((v) => { return v;})console.log(\"b\", b); //undefiend map새로운 배열을 리턴한다.성능면에서도 map이 더 우수하다. map사용을 권장하는 분위기이다. 123let arr = [1,2,3];let b = arr.forEach((v) => { return v + 1;})console.log(\"b\", b); //[2,3,4] for in객체의 속성들을 반복하여 작업수행 가능하다. key값에 접근할 수만있다.여기서 hasOwnProperty로 각각의 object값들을 빼도 된다.hasOwnProperty는 자신의 고유 속성, 즉 상속받은 프로퍼티가 아닌 순수 자신의 속성인 경우에만 값을 true로 반환한다. 12345678910111213var obj = { a: 1, b: 2, c: 3};for (let i in obj) { console.log(i); // a, b, c console.log(obj[i]); //1,2,3 if (obj.hasOwnProperty(i)) { console.log(obj[i]) }} for ofES6에 추가된 반복구문이다. 컬렉션 객체가 [Symbol.iterator]속성을 가지고 있어야만한다. 12345let iterable = [10, 20, 30];for (let value of iterable) { console.log(value); // 10, 20, 30} 객체를 반복문 돌리는 방법 12345var obj = { first: \"John\", last: \"Doe\" };Object.keys(obj).forEach(function(key) { console.log(key, obj[key]);}); 123for (const key of Object.keys(obj)) { console.log(key, obj[key]);} 123Object.entries(obj).forEach( ([key, value]) => console.log(key, value));","categories":[],"tags":[{"name":"for in/ for of문","slug":"for-in-for-of문","permalink":"https://juuuuuuuuuuuuuu.github.io/tags/for-in-for-of문/"}]},{"title":"timelinejs","slug":"timelinejs","date":"2019-01-10T02:44:51.000Z","updated":"2019-01-10T04:47:53.000Z","comments":true,"path":"2019/01/10/timelinejs/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2019/01/10/timelinejs/","excerpt":"","text":"TimeLineJS라이브러리를 사용하여 차트 컴포넌트를 만들게 되었다.https://timeline.knightlab.com/ 참고 외부 라이브러리를 커스텀하면서 가장 힘들었던 점은 컴포넌트화 작업을 하는 부분이었다.나만 쓰는게 아니라 다른 개발자들이 사용할 수 있게 표준화된 컴포넌트를 만드는 일은 참 힘들었다.아래는 개발하면서 힘들었던 점들에 대해 짚어볼 것이다. 해당 라이브러리는 javascript파일로 되었있지만 모든 기능들을 한 파일에 다 모아놓았다.일일히 다 수정하기에는 너무 일이 커질 거 같아서 필요한 속성들만 받을 수 있게 수정해놓았다.그런데 문제는 babel7.x.x 버전은 ES5 변환 불가능한 것들은 그냥 그대로 읽는거 같다. 하지만 우리 회사 전체 프로젝트는babel 6.x.x 버전은 모든 파일을 ES5로 변환하는 것 같다.(babel: browser가 인식할 수 있는 ES5 문법으로 변환시켜주는 transpiler이다.)해결책으로는 jsx파일로 만들었다. 왜냐하면 공통으로 webpack-dev-server.config.js 파일에 js파일만 babel로 컴파일하기로되어있기 때문이다.그러나 큰 문제가 또 발생했다. 개발된 프로젝트를 build돌리면 jsx파일도 컴파일을 시키는 것이다.일일히 수동으로 빌드 돌릴 때마다 jsx파일을 바꿔치기 할 수 도 없다.해결책으로는 package.json에 빌드명령에 npm run build:babel && npm run build:copy-files 이렇게 되어 있다.src하위 폴더를 다 빌드시키고 copy-files라는 파일에 있는 것들도 함께 빌드를 시키라는 명령어이다.그래서 copy-files.js 파일에 가서 복사할 파일의 경로를 추가하였다. 123456789101112131415161718192021222324252627/* eslint-disable no-console */const files = [ 'README.md', 'CHANGELOG.md', 'LICENSE', './src/ARITimeLine/ARITimeLineLib.jsx' // 이 부분!!! ];Promise.all( files.map((file) => copyFile(file))) .then(() => createPackageFile());function copyFile(file) { const buildPath = resolveBuildPath(file); return new Promise((resolve) => { fse.copy( file, buildPath, (err) => { if (err) throw err; resolve(); } ); }) .then(() => console.log(`Copied ${file} to ${buildPath}`));} 이 방법이 맞나 싶기는 한데 timelinejs라이브러리를 일일히 다 커스텀하기에는 아무래도 일 이 커질 것 같다. 컴포넌트화 시키고 다른 프로젝트에서 import해서 쓰는데 문제가 발생했다.Minified React error 에러였다. 찾아보니 react 에서 와이어를 통해 보내는 바이트 수를 줄이기 위해 전체 에러를 보내는 것을피한다고 한다. 결국 타임라인을 개발한 프로젝트로 가서 오류를 보니 render에서 return할 때 timeline라이브러리를 리턴하는데이게 object 형태로 되어 있어 발생한 에러였다. 항상 return할때는 배열이나 Fragments를 사용하여 자식목록을 그룹화한다.내가 사용한 방법은 react-addons-create-fragment를 사용했는데 더이상 안쓰인다. react 16버전부터는 Fragment를 지원한다.그러나 우리회사는 react 15버전이므로 사용했다. 12345678910111213141516171819202122232425import createFragment from 'react-addons-create-fragment';export function createChildFragment(fragments) { const newFragments = {}; let validChildrenCount = 0; let firstKey; // Only create non-empty key fragments for (const key in fragments) { //object의 key값만을 뽑아서 반복문 돌릴 때는 'for in'사용 let currentChild = fragments[key]; if (typeof currentChild === 'object') { createChildFragment(currentChild) } if (typeof currentChild === 'object') return; if (currentChild) { if (validChildrenCount === 0) firstKey = key; newFragments[key] = currentChild; validChildrenCount++; } } if (validChildrenCount === 0) return undefined; if (validChildrenCount === 1) return newFragments[firstKey]; return createFragment(newFragments);} 이렇게 해결을 하였다!!! 사실 내가 한 방법이 맞는지 모르겠지만 잘 돌아가니 우선 덮어두기로…","categories":[],"tags":[{"name":"TimeLineJS","slug":"timelinejs","permalink":"https://juuuuuuuuuuuuuu.github.io/tags/timelinejs/"}]},{"title":"Next.js","slug":"next-js","date":"2018-12-19T00:05:43.000Z","updated":"2018-12-20T08:50:42.000Z","comments":true,"path":"2018/12/19/next-js/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2018/12/19/next-js/","excerpt":"","text":"Next.js란 리액트 프레임워크이다.서버사이드 랜더링(SSR)을 지원하며 프론트 엔드를 위한 서버이다. 실제로 서버에서 사용할 코드를 주며 빌드되지 않고서버위에서 동작한다.CRA는 빌드된 파일이 소스로 올라가며 클라언트 랜더링이다. 서버사이드 렌더링(SSR)과 클라이언트 렌더링의 차이점?서버사이드 렌더링(SSR)은 웹사이트 접속해서 나에게 보여기지 전에 서버가 모든 데이터(모든 html)를 가져와서 보인다.그래서 페이지 이동을 할 때 불필요하게 로딩 페이지가 있을 필요가 없다.즉, 백엔드에 변환된 html이 복사되어 클라이언트에게 전달한다. next.js위에 백엔드 노드 서버가 있고 웹사이트를 갈 때마다react.js를 가져온다(익스프레스 서버)아래 그림은 next.js프로젝트의 index.html파일이다. 클라이언트 렌더링은 서버는 작은 html과 js파일을 주고 클라이언트에서 js파일을 읽어서 로딩을 한다. js파일을 받는데렌더링 되는동안에는 클라이언트에서는 화면을 볼 수 없다. 실제로 확인해보면 해당 페이지를 누를 때마다 해당 js파일을 불러오고 있다.최초에는 index.js파일만 불러온다. about을 누르면 해당 about.js파일을 불러오는 것을 볼 수 있다.이게 바로 서버사이드랜더링이다. 페이지를 동적으로 생성한다.서버가 실제로 props로 각 데이터들을 가지고 script태그에 넣는 것이다.react앱은 NEXT_DATA script를 hydrate하게 프로그래밍하게 되어있다..? 서버를 기반으로 돌아가게 하려면 server.js에 서버를 세팅한다. app.js => app컴포넌트는 일반 컴포넌트들 보다 최상위에 존재한다. 기존에 HoC랑 같다. 그래서 헤더영역이나 공통으로모든 페이지마다 들어가야할 요소를 app.js에 세팅한다. document.js => document를 통째로 서버쪽에서 렌더링 되는걸 바꾸고 싶을 때 사용한다.","categories":[],"tags":[]},{"title":"setState","slug":"setstate","date":"2018-12-17T05:27:37.000Z","updated":"2019-04-17T07:39:01.433Z","comments":true,"path":"2018/12/17/setstate/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2018/12/17/setstate/","excerpt":"","text":"react에서 setState는 상태값 변화를 위해 사용한다.그러나 setState가 비동기적으로 일어나기 때문에 아래와 같이 코드를 작성하면 this.state.count의 값이 +1을 하기 전의 값이 나올 수도 있다.123this.setState({ count: this.state.count + 1,}); 그래서 함수형 setState를 사용한다. 12345this.setState(prevState => { return { count: prevState.count + 1, } }) 기존에 객체를 복사한 다음에 업데이트한 값을 setState하는 방식이다. 123let all = Object.assign({}, this.state.all);all.name = 'hi';this.setState({all}); 기존의 Object.assign을 대신해서 아래와 값이 사용하면 된다.123456this.setState(prevState => ({ all: { ...prevState.all, name: 'hi', }})) function tabHandler (index) Preturn function tabClickEvent(event) Pconosle.log(index);{ }","categories":[],"tags":[]},{"title":"context-api","slug":"context-api","date":"2018-12-17T04:49:59.000Z","updated":"2018-12-18T00:16:27.000Z","comments":true,"path":"2018/12/17/context-api/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2018/12/17/context-api/","excerpt":"","text":"Context API는 props를 직접 전달하는 것과 바로 상태 전달이 가능하다.store를 두고서 상태값들을 서로 공유할 수 있다.기존의 redux 보다는 사용방법이 훨씬 간단하다. 우선 store공간을 만든다.(store.js)context는 createContext라는 함수를 통해 만든다.이 함수를 호출하면 Provider와 Consumer라는 컴포넌트들이 반환된다.1234import React from \"react\";const Store = React.createContext(null);export default Store; Context를 프로젝트에 적용하려면 최상단을 Provider로 감싸야한다.Provider 내에서 사용할 값은 “value”라고 부른다.클래스가 생성되었을 때 store가 value를 얻게 된다.그래서 Provider에 사용할 함수는 반드시 생성자에 명시해야한다.생성자에서 state 값 뿐만 아니라 함수도 선언해줘야한다.12345678910111213141516171819202122232425262728293031323334import React, { Component } from \"react\";import AppPresenter from \"./AppPresenter\";import Store from \"store\";class AppContainer extends Component { constructor() { super(); this._changeMessage = () => { if (this.state.message === 'hello') { this.setState({ message: 'bye bye', }) } else { this.setState({ message: 'hello', }) } } this.state = { message: 'hi', changeMessage: this.__changeMessage, } } render(){ return( <Store.Provider value={this.state}> <AppPresenter/> </Store.Provider> ) }}export default AppContainer; Consumer는 Context를 사용해야할 때 사용한다.Consumer는 함수가 아닌 다른 child를 사용하면 안된다. 123456789101112131415161718192021import React, { Fragment } from \"react\";import Notification from \"Components/Notification\";import Store from 'store';const AppPresenter = () => ( <Fragment> <Store.Consumer> {store => (<Notification key={store.notification[key].id} id={store.notification[key].id} text={store.notification[key].text} seen={store.notification[key].seen} onClick={store.changeMessage} /> ) } } </Store.Consumer> </Fragment>);export default AppPresenter; context가 여러개 있을 때 Provider를 각각 생성해서 최상위에 여러겹 쌓는게 마음에 걸린다.자바스크립트 내장함수인 reduce를 사용하여 깔끔하게 정리할 수 있다.123456789101112131415161718192021import React from 'react';import { SampleProvider } from './contexts/sample';import { AnotherProvider } from \"./contexts/another\";const AppProvider = ({contexts, children}) => contexts.reduce( (prev, context) => React.createElement(context, { children: prev }), children);const App = () => { return ( <AppProvider contexts={[SampleProvider, AnotherProvider]} > </AppProvider> );};export default App; 또한 Consumer에 사용되는 로직을 쉽게 재사용할 수 있도록 HoC를 사용한다면 매우 편리해진다. 1234567891011121314151617181920212223import React, { Component, createContext } from 'react';const Context = createContext();const{ Provider, Consumer } = Context;//HoC사용function useSample(WrappedComponent) { return function UseSample(props) { return ( <Consumer> { ({state, actions}) => ( <WrappedComponent value={state.value} setValue={actions.setValue} /> ) } </Consumer> ) }} 위의 코드를 필요한 하위 컴포넌트에서 가져다 쓰면 된다!!!1234567891011121314import React from 'react';import {useSample} from \"../contexts/sample\";const Receives = ({value}) => { return ( <div> 현재 설정된 값: {value} </div> );};export default useSample(Receives); redux에 비해 훨씬 더 간단하긴 하지만 redux는 단순힌 전역 상태 관리 그이상의 가치가 있다고 한다.액션기반 앱 상태 업데이트 로직 작성부터, 미들웨어, 강력한 개발자도구까지…Suspense API 또한 요즘 대세라고 한다.기존에는 리액트는 데이터가 있을 때까지 항상 로딩상태를 가지지만 Suspense API는 데이터가 있을 때까지 렌더링 되지 않는다고 한다.","categories":[],"tags":[]},{"title":"react16 주요기능","slug":"react16","date":"2018-12-04T04:05:14.000Z","updated":"2019-01-17T01:48:52.000Z","comments":true,"path":"2018/12/04/react16/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2018/12/04/react16/","excerpt":"","text":"##react 16의 주요기능에 대해서 알아보자 1.return값기존의 react는 항상 return할 때마다 span태그나 배열로 감싸야했다.12345678render() {return ( <span> <Header/> <Footer/> </span> ) } react16에서는 Fragment라는 것을 지원한다.또한 <>로도 대체 가능하다. 123456789101112import React, { Component, Fragment } from 'react';class Example extends Component { render() { return ( <Fragment> <Header/> <Footer/> </Fragment> ); }} return 할때 string으로도 가능하다.123render() { return \"string\" } 2.Portals기존에는 리액트의 루트 밖에서 변경할 수 없었다.그러나 createPortal은 리액트 루트 밖에서 렌더할 때 사용할 수 있다.주로 iframe이나 html을 변경하지 못하거나 리액트 플러그인을 만들 때 사용한다. index.html파일12<div id=\"root\"></div><div id=\"portal\"></div> id가 root인 애는 리액트 루트이며 portal 리액트 루트 밖에서 이용할 것이다. app.js파일1234ReactDOM.render( <Main/>, document.getElementById(\"root\")); Main파일1234567891011import React, { Component, Fragment } from 'react';import { createPortal } from 'react-dom';const Message = () => return \"mess\"class Main extends Component { render() { return ( createPortal(<Message/>, document.getElementById('portal')) ) }} 이렇게 하면 실제로 portal이라는 아이디 값에 Message라는 컴포넌트가 매핑된다. 3.Error Boundaries부모에서 child의 에러를 잡을 수 있다.이를 통해 에러를 구분하고 대처할 수 있다.componentDidCatch를 사용해서 에러를 잡을 수 있다. 1234567891011121314151617181920212223242526272829303132333435class ErrorMaker extends Component { state = { friends: ['ju', 'young'] } componentDidMount = () => { setTimeout(() => { this.setState({ friends: null, }) }, 2000) } render() { const { friends } = this.state; return friends.map(friend => `${friend}`) }}const ErrorfallBack = () => \"Sorry\";class ExamplePortal extends Component { state = { hasError: false}; componentDidCatch = (error, info) => { this.setState({ hasError: true, }); console.log(`catched ${error} info: ${JSON.stringify(info)}`) } render() { return {hasError ? <ErrorfallBack/> : <ErrorMaker/>} }} ExamplePortal 이라는 부모 컴포넌트에서 처음엔 ErrorMaker를 보여주다가 2초가 지난 후에에러가 발생하면 ErrorfallBack 컴포넌틀르 보여준다.에러에 대한 처리를 이런식으로 하면 코드가 난잡해지며 보기 좋지 않다.그래서 나온게 HOC이다…??? –>hoc정확하게 다시 알아보기!!(강의 돌려보기) 4. HOC(higher-order component)HOC는 컴포넌트를 보호한다. 12345678910111213141516171819202122232425262728293031323334const BoundaryHOC = ProtectedComponent => class Boundary extends Component { state = { hasError: false, }; componentDidCatch = () => { this.setState({ hasError: true, }) } render () { const { hasError } = this.state; if (hasError) { return <ErrorfallBack/> } else { return <ProtectedComponent/> } } } const PErrorMaker = BoundaryHOC(ErrorMaker) //컴포넌트 보호 class ExamplePortal extends Component { render() { return ( <Fragment> <PErrorMaker/> </Fragment> ); } } export default BoundaryHOC(ExamplePortal); //클래스도 보호 가능 5. createRef, forwardRef기존에 ref는 자식요소에 접근할 때 이용하곤 했다.React 16.3부터는 createRef 를 사용한다. 12345678910111213141516171819202122232425import React, { Component } from 'react';import Test from './Test';class App extends Component { constructor() { super(); this.app = React.createRef(); } componentDidMount() { if (this.app) { this.app.current.handleRef(); } } render() { return ( <div> <Test ref={this.app}/> </div> ); }}export default App; 123456789101112131415161718192021222324import React, { Component } from 'react';class Test extends Component { constructor() { super(); this.div = React.createRef(); } handleRef = () => { if (this.div) { console.log(\"hi\") } } render() { return ( <div ref={this.div}> test </div> ); }}export default Test; 부모요소가 app이고 test가 자식요소이다. app.js에서 didmount에서 자식요소의 함수에 접근을 하면 콘솔창에‘hi’가 찍히는 것을 볼 수 있다.그런데 만약 HOC로 test컴포넌트로 보호되어 있으면 어떻게 될까?이때는 forwardRef를 사용해야 한다. createRef는 HOC로 만들어진 컴포넌트의 속성에 ref가 있으면 리턴되는컴포넌트의 ref를 가져오기 못한다. forwardRef는 이러한 문제를 해결하기 위해서 HOC내부에서 사용하면 리턴되는컴포넌트에 전달할 수 있다. Test.js파일에서 withHOC라는 HOC로 컴포넌트가 보호되어있다는 가정하(export default withHOC(Test))에 실행해보면 된다.마찬가지로 ‘hi’가 나오는 것을 볼 수 있다. 12345import React from 'react'; const withHOC = Component => React.forwardRef((props, ref) => { return <Component {...props} ref={ref} /> }); export default withHOC;","categories":[],"tags":[]},{"title":"webpack.cofig파일 경로 꼬임 현상","slug":"problem","date":"2018-11-28T08:52:58.000Z","updated":"2018-11-28T09:04:54.000Z","comments":true,"path":"2018/11/28/problem/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2018/11/28/problem/","excerpt":"","text":"이거 땨문에 오후 내내 잡아먹었다. 자꾸 node_modules내에 있는 라이브러리가 현재 A라는 프로젝트의 abc 폴더의 경로를 보고 있었다.분명 최근에 A라는 프로젝트에서 세팅이나 이런것들이 수정된 것들이 없고 다른 쪽에서 땡겨서 쓸 때도 문제가 없었는데 말이다. 기존 구조는 A 프로젝트 - B 프로젝트 - C 프로젝트 이렇게 되어있었다.그런데 5일전 C라는 프로젝트에서 D 프로젝트를 가져다 쓰고 있었다. ( A 프로젝트 - B 프로젝트 - C 프로젝트 - D 프로젝트) 그리고 A라를 프로젝트의 webpack.config파일에 경로가 문제였다.1234567const path = require('path');resolve { alias: { 'abc': path.resolve(__dirname, 'abc'); }} 12345678const path = require('path');resolve { alias: { 'abc/lib': path.resolve(__dirname, 'node_modules/abc/lib'); 'abc': path.resolve(__dirname, 'abc'); }} 빌드를 돌면서 D라는 프로젝트에서 해당 경로를 abc로 잡는것!!이것이 문제였다. 결국 경로를 저렇게 수정했다!여기서도 abc1먼저 써야한다. abc2를 먼저 뜨면 덮어쓰더라…이렇게 고쳐서 해결했숩니다!!","categories":[],"tags":[]},{"title":"클로저(closure)란?","slug":"closer","date":"2018-11-23T04:32:21.000Z","updated":"2020-02-14T02:14:00.730Z","comments":true,"path":"2018/11/23/closer/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2018/11/23/closer/","excerpt":"","text":"클로저란 과연 무엇인가?closure 폐쇄, 닫는다의 의미이다. 자바스크립트를 다루면서 책에는 나왔지만 실제로 아직 사용을 해본 적은 없다. 클로저는 함수 내부에 함수를 작성하는 것이다.클로저를 사용하는 이유는 다음과 같다. 1. 사이드 이펙트(side effect) 제어우선 side effect는 함수에서 값을 반환할 때를 제외하고 무언가를 행할 때를 가르킨다. ( ajax요청이나 timeout생성, console.log선언하는 것도…)보통 ajax, timeout과 같이 코드 흐름을 방해하는 것들이 신경쓰일 때 사용한다.12345678910var arr = []for(var i = 0; i < 5; i++){ //i의 값은 외부함수의 변수가 아님 arr[i] = function() { return i; //결국 i에 5가 담기게 되는 이유 ==> 배열을 돌게 되면 마지막으로 i =5가 되는데 return하고 있는 i의 값은 외부함수의 지역변수가 아님 }}for(var index in arr) { console.log(arr[index]()); // 5가 다섯번찍히게 됨} 위의 예제는 var가 함수 스코프를 따르기 때문에 5가 나오긴한다. let인 경우 0.1.2.3.4가 찍힌다.하지만 이 예제의 요점은 아니다. 1234567891011var arr = []for(var i = 0; i < 5; i++){ arr[i] = function(id){ //외부함수 return function() { // 내부함수 return id; } }(i); //외부 함수를 즉석에서 실행함}for(var index in arr) { console.log(arr[index]());} 이 예제에서 보면 외부함수가 실행되면 외부함수의 스코프는 끝이 나기 때문에 외부함수의 인자인 id 값은 메모리에서 정리가 되어야한다.그러나 리턴이 되면서 내부함수를 실행되면서 id값인 함수의 인자와 지역변수값이 내부함수의 클로저 객체로 남아 외부함수의 인자와 변수에접근이 가능하다. 그러나 클로저를 남발하면 위험하다. 가비지컬렉션 대상이 되어여할 객체들이 메모리상에 남아있어 오버플로우가 발생할 수 있다. 2. private 변수 생성private한 변수로 접근가능하기 때문에 안전하다. 1234567891011121314151617function Hello(name) { this._name = name;}Hello.prototype.say = function() { //prototype을 통해 객체를 다룸 console.log('Hello, ' + this._name);}var hello1 = new Hello('a');var hello2 = new Hello('b');var hello3 = new Hello('c');hello1.say(); // 'Hello, a'hello2.say(); // 'Hello, b'hello3.say(); // 'Hello, c'hello1._name = 'anonymous'; //외부에서 접근 가능함hello1.say(); // 'Hello, anonymous' 원하는 시점에서 내부 클로저를 실행할 수 있으며 private변수를 가질 수 있다.위의 코드는 바로 변수에 접근해서 속성값들을 바꿀 수 있어 안전하지 않다.123456789101112131415161718function hello(name) { var _name = name; return function() { console.log('Hello, ' + _name); };}var hello1 = hello('a');var hello2 = hello('b');var hello3 = hello('c');hello1(); // 'Hello, a'hello2(); // 'Hello, b'hello3(); // 'Hello, c'hello1 = null; // 메모리가 소모됨, 그러므로 사용이 끝나면 참조를 제거한다!hello2 = null;hello3 = null; 클로저를 사용한 예제는 private한 변수 이므로 아무리 속성값을 위와 같이 바꾸려고 해도 바뀌지 않는다.또한 사용이 끝나면 참조를 제거한다. 업무에 적용한 부분업무를 진행하면서 클로저를 사용할 일이 많지는 않았다.calculateCoords함수에서 외부함수의 참조변수를 내부함수인 return하는 함수에서 접근할 수 있기 때문에루프문을 돌때마다 참조변수를 넣을 필요가 없다!123456789101112131415 $img.siblings('map').children().each(function () { const $area = $(this); // 원본 coords const originCoords = $area.data('origin-coords').split(','); // 업데이트된 coords 속성 추가 const updateCoords = originCoords.map(calculateCoords(imgWidth, imgHeight, wPercent, hPercent)); $area.attr('coords', updateCoords.toString()); });});function calculateCoords(imgWidth, imgHeight, wPercent, hPercent) { return function (location, i) { return i % 2 === 0 ? parseInt(((location / imgWidth) * 100) * wPercent) : parseInt(((location /imgHeight * 100) * hPercent); }}","categories":[],"tags":[{"name":"javascript","slug":"javascript","permalink":"https://juuuuuuuuuuuuuu.github.io/tags/javascript/"}]},{"title":"iterator/generator와 async/await비교","slug":"iterator","date":"2018-11-23T04:03:24.000Z","updated":"2018-11-23T04:22:46.000Z","comments":true,"path":"2018/11/23/iterator/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2018/11/23/iterator/","excerpt":"","text":"ES8부터 도입된 async/await는 iterator/generator로 부터 파생되었다.async/await의 사용하기 전에 iterator/generator부터 알아보자! iterator내부적으로 반복처리[Symbol.iterator] 반복되는 규칙을 내부적으로 처리하는 것을 만드는 부분 123456789101112131415161718//피보나치 수열 예제let fibonacci = { maxStep: 20, [Symbol.iterator]() { let pre = 0, cur = 1, step = 0, maxStep = this.maxStep; return { next() { [pre, cur] = [cur, pre + cur]; return {done: step ++ >= maxStep, value: cur} //상태값과 value를 리턴해줌 } } }}for (let n of fibonacci) { console.log(n) // 1,2,3,5,8,13,21,34 ....} generatorgenerator는 iterator의 특징이자 단점인 무한 반복을 해결해준다.next해서 접근 가능 하며 기존의 {done, value}를 자동으로 리턴해준다.사용법은 function앞에 (반복기)을 붙이고 yield는 value를 정해준다.yield가 있는 시점에서 함수의 실행을멈추며 yield도 가능하다(문자열, 배열, 다른 반복기 값 가능) 12345678910111213function * factorialGenerator() { let cur = 1; let count = 1; while(true) { [count, cur] = [count + 1, cur * count] yield cur; }}const factorial = factorialGenerator();factorial.next().value // 1factorial.next().value // 2 123456789101112131415let myObj = {};myObj[Symbol.iterator] = function* () { yield \"1\"; //단일 값이 전달 yield* [2,3]; //yield* 로 값을 반환하면 --> 해당 값이 다시 하나의 iterator로 취급되어 내부루프 처리하듯 순서대로 처리함 //배열 yield* \"45\"; //문자열 yield* innerGenerator(); //다른 반복기 값 yield \"9\";}function* innerGenerator() { yield 6; yield* new Set([\"7\", \"8\"]);}console.log([...myObj]) // [\"1\", 2, 3, \"4\", \"5\", 6, \"7\", \"8\", \"9\"] async/await비동기를 동기처럼 보이게 한다. try, catch문을 사용해한다.기존의 * => async, yield => await로 바뀐다. 아래 코드는 async/await로 작성하는게 generator로 코드를 짜는것 보다 훨씬 가독성도 높고 코드가 간단하다.123456789101112131415161718192021function getUser() { return new Promise((resolve, reject) => { resolve('test'); })}function* generator() { const users = yield getUser(); console.log(\"user\", users) return users;}const iterator = generator();const iteration = iterator.next();//generator함수 실행iteration.value.then( resolve => { const nextIteration = iterator.next(resolve); nextIteration; }) 12345678910111213function getUser() { return new Promise((resolve, reject) => { resolve('test'); })}async function generator2() { const users = await getUser(); console.log(users) return users;}generator2();","categories":[],"tags":[]},{"title":"hexo 포스팅시 404뜨는 경우","slug":"cause","date":"2018-11-23T02:51:19.000Z","updated":"2018-11-23T02:52:46.000Z","comments":true,"path":"2018/11/23/cause/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2018/11/23/cause/","excerpt":"","text":"hexo new post [파일명] 파일명은 한글로 작성했다..너무나도 당연하게 안되는건데 한시간동안 헤맷다..파일명은 영어로 작성하되 md파일에서 title만 내가 원하는 타이틀로 바꾸면 된다…","categories":[],"tags":[]},{"title":"promise와 async/await비교","slug":"promise-async","date":"2018-11-23T02:23:12.000Z","updated":"2019-11-15T08:19:41.997Z","comments":true,"path":"2018/11/23/promise-async/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2018/11/23/promise-async/","excerpt":"","text":"싱글스레드인 자바스크립트는 비동기처리를 위해 콜백을 사용한다.그러나 비동기를 중첩시켜서 사용을 하면 에러,예외 처리가 어려워져 중첩도로 인해 복잡성이 증가한다.이러한 단점을 해결하기 위해 나온게 promise이다. promise- ES6 문법- resolve 결과가 then, reject가 catch문으로 가게 됨 async/await- ES8 문법- 코드의 간결성- 동기식으로 처리- await뒤에는 항상 프로미스를 반환해야한다. 차이점 정리 1. 에러처리 promise문에서 getJSON을 통해서 데이터를 받아오는 부분과 JSON.parse하는 부분에서 catch문을 사용해서 에러를 잡아야한다. 반면에 async/await 구문에서는 try, catch에서 모든 에러를 다 한번에 잡을 수 있다. 코드가 훨씬 깔끔해질 수 있다. 그러나 실무에서 에러가 발생할때 정확하게 어느 구문에서 발생했는지 알 수 없다. 123456789101112makerequest = () => { try { this.getJSON('a') .then(result =>{ const data = JSON.parse(result); }).catch((err) => { console.log(1, err) }) } catch(err) { console.log(2, err) }} 12345678makerequest = async() => { try { JSON.parse(await this.getJSON('a')) } catch (err) { console.log(\"Err\", err) }} 2. 중간값 처리 예를 들어서 사용자의 정보를 받아온 다음 그 정보를 바탕으로 새로운 api를 호출해야하는 경우가 있다. 이 경우 async/await를 사용하면 좀 더 편하게 코드를 짤 수 있다. 사실 이부분이 async/await를 사용하는 가장 큰 이유라고 생각한다. 12345678910makeRequest = () => { return this.getJSON() .then(data => { return this.makeAnotherRequest(data) .then(moreData => { console.log(moreData) return moreData }); })} 12345makeRequest = async() => { const data = await this.getJSON() const moreData = await this.makeAnotherRequest(data) console.log(moreData)} 3. 디버깅 promise는 then블록 내에 디버깅을 찍으면 동기적으로 이동하기 때문에 다음 then으로 이동하지 않는다. 반면 async/await는 바로 다음 await로 이동을 하기 때문에 디버깅하는데 편리하다. async/await에서 동시에 비동기적으로 값을 가져와야할 때?이때는 Promise.all()을 사용한다.123456789101112131415161718const job = (x) => { return new Promise((resolve, reject) => { setTimeout(() => { console.log(\"x\", x) resolve(x); }, x * 100); }) }timeStamp = async() => { try { for (const result of await Promise.all([job(3), job(6), job(9)])) { console.log(result) //총 걸리는 시간은?? 15초이다! } } catch(err) { console.log(err) }} 그러나 ES9에 도입된 for await가 이를 대체해준다.for await는 for of문에서 처리가능하며 반복적인 비동기 작업을 쉽게 처리해준다. 1234567891011timeStamp = async() => { const promises = [3, 6, 9].map((timer) => ( new Promise((res, req) => { // 총 걸리는 시간은 15초! setTimeout(() => res(timer), timer * 1000); }) )) for await (const result of promises) { console.log(result); }} 결론 promise를 대체해서 어떤 경우에서든지 async/await를 사용하는 것을 좋지 않다. 새로운 문법들이 계속 등장하는데 꼭 좋은 것만은 아니라고 한다. 적재적소로 필요할 때 사용하는게 제일 좋다. 본인한테 맞고 잘 사용할 수 있는게 좋다. 새로운 것들이 등장하는 것은 단지 이용을 편리하게 해주는것이므로…","categories":[],"tags":[]},{"title":"Hello World","slug":"hello-world","date":"2018-11-02T04:39:39.000Z","updated":"2018-11-02T04:39:39.000Z","comments":true,"path":"2018/11/02/hello-world/","link":"","permalink":"https://juuuuuuuuuuuuuu.github.io/2018/11/02/hello-world/","excerpt":"","text":"Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub. Quick StartCreate a new post1$ hexo new \"My New Post\" More info: Writing Run server1$ hexo server More info: Server Generate static files1$ hexo generate More info: Generating Deploy to remote sites1$ hexo deploy More info: Deployment","categories":[],"tags":[]}]}