Apache Zeppelin 설치 및 빌드하기

맥북 포맷하여 제플린 빌드가 날아간 기념으로 Apache Zeppelin을 설치 및 빌드하는 튜토리얼을 적어본다.

  1. Apache Zeppelin 깃헙에 들어간다. 나는 컨트리뷰션을 할 예정이니 저장소를 바로 클론하지 말고 내 계정에 Fork (내 계정에 복제 라고 생각하면 된다)를 먼저 한다. (컨트리뷰션 튜토리얼은 다른 글에 작성 예정)

https://github.com/apache/zeppelin 깃헙 저장소 우상단의 ‘Fork’를 누른다.

2. 포크하여 내계정이름/zeppelin 으로 변경된 저장소에서 ‘Code’ 버튼을 누르고 클립보드 버튼을 눌러 포크된 저장소 URL 을 복사한다.

3. 포크를 원하는 폴더에 git clone 을 해준다. (git clone https://github.com/내계정명/zeppelin.git 명령어 입력)

4. 제플린 설치 가이드에 들어가보면 JDK 1.8을 먼저 설치해야 된다 나와있다.

5. 구글에 JDK 1.8 을 검색하면 제일 위에 나오는 오라클 홈페이지에서 본인의 OS에 맞는 파일을 다운로드 해준다 (누르면 오라클 계정 입력하라 나오는데 회원가입 해야한다 흑흑)

6. 잘 다운로드 되었는지 확인하기 위해 자바 설치경로를 체크해본다. 맥은 /usr/libexec/java_home -v 1.8 를 입력하면 나온다. 보통
/Library/Java/JavaVirtualMachines
에 설치되어있다. 방금 1.8을 설치해서 /Library/Java/JavaVirtualMachines/jdk1.8.0_271.jdk/Contents/Home 로 반환되었다.

7. 경로를 알았으니 JAVA_HOME 을 설정하기 위해 홈으로 빠져나와(cd ~) 배쉬 설정파일을 접근한다. 각자 사용하는 shell마다 파일 이름이 다를것이다. 나는 zsh등이 아닌 기본 bash를 사용해서 vi .bash_profile 명령어를 입력하였다.

8. i를 눌러 인서트모드로 바꾼 후, 다음과 같이 입력하고 저장(esc누르고 :wq) 한다

JAVA_HOME=여러분의경로
PATH=$PATH:$JAVA_HOME/bin
export JAVA_HOME
export PATH

9. 설정이 저장되었으면 source .bash_profile  를 입력해 환경설정을 저장한다. 그리고 echo $JAVA_HOME 명령어를 입력했을 때 아까 적은 자바 경로가 제대로 나오면 성공!

10. 나는 프론트엔드만 컨트리뷰션할거라 백엔드는 도커로 띄울것이다. 도커가 없는 분들은 도커를 다운로드 한다.

11. Docker Desktop 이 잘 설치되었는지 확인한다

12. 공식문서에 나와있는 오피셜 도커를 띄우는 명령어는 docker run -p 8080:8080 –rm –name zeppelin apache/zeppelin:0.9.0 이다. 하지만 나는 포트포워딩을 사용할것이기 때문에
docker run -e ZEPPELIN_ADDR=0.0.0.0 -p 8081:8080 –rm –name zeppelin apache/zeppelin:0.9.0

로 포트를 살짝 바꿔서 띄운다.

13. 이제 프론트엔드를 띄울것이다. 새로운 angular 프론트를 사용할것이기 때문에 cd zeppelin-web-angular/ 로 들어간다. 먼저 README.md를 읽어본다.

서버 프록시를 사용하려면 .env 파일에 포워드하고싶은 포트를 입력하라고 적혀있다.

14. 아까 서버를 8081포트에 띄웠으니 .env 파일을 만들어 SERVER_PROXY=http://localhost:8081 라고 적어주고 저장한다.

15. npm install 명령어로 의존성을 설치해준다.

16. npm start 를 통해 서버를 켠다.

17. :4200번 포트에 프론트엔드가 잘 뜬것을 볼 수 있다!

GitHub Actions로 Vuepress 배포하기

내 TIL 사이트는 Vuepress로 말아져 있다

http://milooy.github.io/TIL/
커스텀하기 편하고 가볍다.
정적 사이트 장점을 살려 GitHub Pages에 배포해뒀다.

사이트를 배포하는 가장 기본적인 방법은
매번 글을 작성할때마다 로컬에서 빌드 -> gh-pages 브랜치에 force push 하는건디
증맬 귀찮은 일이다.

package.json에 deploy 스크립트를 만들어서 가-끔 생각날때마다 돌려주곤 했는데…

"scripts": {
    "dev": "vuepress dev documents",
    "build": "vuepress build documents",
    "commit": "vuepress build documents && git add build -f && git commit -m 'Subtree commit'",
    "deploy": "git push origin `git subtree split --prefix build master`:gh-pages --force"
  },

GitHub Actions를 사용해
코드를 푸시할때마다 자동으로 위 스크립트가 돌도록 업데이트를 해봤다.
(그전까진 왜 안한겨.. 생각보다 나의 참을성이 좋구머잉)

하는법 간단히 기록해두기~

Step 1: Vuepress로 사이트를 만든다

다큐멘테이션 보면 금방 만들어용 https://vuepress.vuejs.org/
다 만들면 GitHub 저장소에 푸시하셈.

Step 2: 저장소에 GitHub Pages 세팅하기.

저장소 Settings탭 -> GitHub Pages 섹션에 가서
Source를 ‘gh-pages’로 바꾼다. 그러면 gh-pages브랜치에 있는 index.html 읽어서 사이트를 보여줌.

image

Step 3: workflow 파일 생성

저장소 루트에 .github/workflows/main.yml 파일을 만든다. main말고 원하는 다른 이름 써도 무방.

아래 코드를 복사하고 저장한다.
jenkey2011이란 깃헙 유저가 배포한 action 가져다 쓴거다.
https://github.com/marketplace/actions/vuepress-deploy

Dockerfile, Docker Hub 이라고 생각함 될듯. 선언적 문법 매력적이다. 일해라 절해라 적어두기.

name: Build and Deploy
on: [push]
jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout
      uses: actions/checkout@main

    - name: Vuepress deploy
      uses: jenkey2011/vuepress-deploy@1.0.1
      env:
        ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
        BUILD_SCRIPT: yarn && yarn build
        TARGET_BRANCH: gh-pages
        BUILD_DIR: build/

Step 4: GitHub 저장소에 환경변수 세팅

위 yaml 파일 보면 ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}를 쓰는데, GitHub 저장소에 ACCESS_TOKEN 환경변수를 세팅해줘야 한다.

먼저 내가 나라는걸 증명하는 프라이빗 토큰을 발급받자.
Settings > Developer settings > Personal access tokens에 들어가
https://github.com/settings/tokens

Generate new token 하면 된다(기존에 만들어놨다면 재활용 해도 된다)
image

이렇게 만든 토큰을
저장소 Settings > Secrets > New secret에 key는 ACCESS_TOKEN라 적고 value에 토큰을 복사 붙여넣기 하면 된다. 고럼 yaml 파일이 돌면서 저장소 환경변수를 참고해감.

image

Step 5: Happy TIL-ing!

yaml 파일을 GitHub에 올린다. 코드를 Push할때마다 자동으로 GitHub액션이 돌며 위에 선언한 Yaml코드가 실행된다.
저장소 Actions 탭에서 확인 가능!

image

npx create-react-app 에서 A template was not provided. This is likely because you’re using an outdated version of create-react-app. 에러가 날 때

Problem

CRA 공식 문서대로 npx create-react-app my-app 명령어를 입력했는데

A template was not provided. This is likely because you're using an outdated version of create-react-app.

에러가 나면서 빈 react app이 생성되었다.
훗 뭐 예전에 글로벌로 설치했던 cra 때문이겠지 하고 npm uninstall -g create-react-app 돌리고 재실행했는데 여전히 위에 에러가 났다.

npm list -g 로 글로벌 모듈을 봐도 create-react-app이 없는데 무슨 일이지… 했음

Solution

which create-react-app

명령어로 컴에 아직 cra가 남아있는지 찾는다. 나는 /usr/local/bin/create-react-app 에 있더라.

rm -rf /usr/local/bin/create-react-app

위 명령어로 가뿐하게 지워준다.
다시 npx create-react-app my-app 하면 이젠 template 껴져서 제대로 된다 🙂

[일일코딩 #35] String incrementer

Question

스트링에서 넘버만 따다가 +1 하는것. 자릿수 지키기.

https://www.codewars.com/kata/string-incrementer/javascript

Your job is to write a function which increments a string, to create a new string.

If the string already ends with a number, the number should be incremented by 1.
If the string does not end with a number. the number 1 should be appended to the new string.
Examples:

foo -> foo1

foobar23 -> foobar24

foo0042 -> foo0043

foo9 -> foo10

foo099 -> foo100

Attention: If the number has leading zeros the amount of digits should be considered.

My answer

정규식으로 number만 따다가 +1 해주고 원래 자릿수를 구해서 0을 그만큼 채워줬다.
마음에 안 듦.

function incrementString (str) {
    const numRegex = /[0-9]+/.exec(str);
    if (numRegex) {
        const numStr = numRegex.pop();

        const newNum = Number(numStr) + 1;
        const zeroNums = numStr.length - newNum.toString().length;
        const zeroStr = (zeroNums > 0) ? Array(zeroNums).fill("0").join("") : "";

        return str.replace(numStr, `${zeroStr}${newNum}`)
    }
    return `${str}1`;
}

Others' answer

Azuaron, Nakid..

function incrementString (input) {
    // 맨끝이 not a number면 그냥 + "1" 
    if(isNaN(parseInt(input[input.length - 1]))) return input + '1';

    // "001"을 넘겼다면 match는 001, p1은 00, p2는 1
    return input.replace(/(0*)([0-9]+$)/, function(match, p1, p2) {
        var up = parseInt(p2) + 1;

        // +1한 숫자 자릿수가 원본자릿수보다 크다면 0을 하나 뺀만큼 앞에 붙여주고 아니면 원본 0갯수만큼 붙여주기
        return (up.toString().length > p2.length) ? p1.slice(0, -1) + up : p1 + up;
    });
}
  • String.replace 첫번째 인자에 정규식을 넘길수 있고, 두 번째 인자에 함수를 넘길 수 있구나!
  • 정규식에 ()으로 그룹을 지어서 두번째 이후 인자로 받을 수 있구나
  • String.slice(0, -1)은 마지막 한글자를 떼는거구나

# [일일코딩 #34] Two sum

이건 쉬웠당 15분도 안걸린듯
몇년전에도 풀었던거같다 유명한 문제자너 이름도 익숙하구

Question

링크

Given an array of integers, return indices of the two numbers such that they add up to a specific target.

You may assume that each input would have exactly one solution, and you may not use the same element twice.

Example:

Given nums = [2, 7, 11, 15], target = 9,

Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].

My answer

어제도 썼던 indexOf 써서 풀었당
for문을 돌리면서 target에서 el을 뺀 diff값이 나머지 array에 있는지 확인하면 됨.

var twoSum = function(nums, target) {
    for(let i=0; i<nums.length; i++) {
        const foundIdx = nums.indexOf(target - nums[i]);
        if (foundIdx >= 0 && foundIdx !== i) {
            return [i, foundIdx]
        }
    }
};

옛날에 푼걸 봐볼까

걍 똑같이 풀었네..

var twoSum = function(nums, target) {
  for (var i = 0; i <nums.length; i++) {
    var lastIdx = nums.lastIndexOf(target - nums[i]);
    if(lastIdx > 0) return new Array(i, lastIdx);
  }
};

[일일코딩 #33] Remove Duplicates from Sorted Array

일일코딩 #32가 2017년 6월이었고, 오늘의 일일코딩 #33은 2019년 11월이다. 2년이 넘었다.
2년만에 푼 알고리즘 문제는 아주 노답이었다고 할 수 있다.
Leetcode easy난이도 문제인데 한시간 걸렸거든.
와 정말 부끄럽다.

영어 설명을 제대로 읽지 않은 탓도 있다.
Array의 duplicated된 엘리먼트를 지우라기에 당연히 지워진 array를 반환하는줄 알았지.
그런데 반환값은 Array의 length뿐이었고 Params로 온 Array의 reference를 받아 원본 Array를 중복 없이 만드는거였다. 메모리 새로 안쓰고.

담엔 잘 읽어.

Question

Leetcode 링크
Given a sorted array nums, remove the duplicates in-place such that each element appear only once and return the new length.

Do not allocate extra space for another array, you must do this by modifying the input array in-place with O(1) extra memory.

즉 아래와 같다.

removeDuplicates([1,1,2]); // returns 2 (원본 Array는 [1, 2]로 modify됨)

removeDuplicates([0,0,1,1,1,2,2,3,3,4]); // returns 4 (원본 Array는 [0, 1, 2, 3]로 modify됨)

My answer

While문을 돌면서 해당 element가 Array의 맨 마지막에 있는지 확인했다.
맨 마지막이면 살려주고 아니면 splice로 제거.
정렬된 배열이라 다 뭉쳐있을테니 각 뭉치의 제일 오른쪽 애들만 남긴다는 아이디어였다.

var removeDuplicates = function(nums) {
    var count = nums.length - 1;
    while(count >= 0) {
        if(count == nums.lastIndexOf(nums[count])) {
            count--;
        } else {
            nums.splice(count, 1);
        }
    }

    return nums.length;
};

잘 되긴 한다.

성능은 후지다. 이 퍼센트가 뭔가 했더니 하위 5%, 18%더라.

Runtime: 224 ms, faster than 5.75% of JavaScript online submissions for Remove Duplicates from Sorted Array.
Memory Usage: 37.8 MB, less than 18.75% of JavaScript online submissions for Remove Duplicates from Sorted Array.

처음에 param으로 받은 Array를 reference로 고쳐야한단걸 몰랐을때는
Array.prototype.reduce로 접근했다. prev, next를 돌며 앞뒤가 다르면 새로운 Array에 push해줬음. 그건 그것나름대로 제일 앞 or 제일 뒤가 edge case라는 헛점은 있더라 – 무튼 안됨.

다른 사람들은 어케 했는지 보자

Others’ answer

Best practice

var removeDuplicates = function(nums) {
    if (nums.length == 0) return 0;
    var i = 0;
    for (var j = 1; j < nums.length; j++) {
        if (nums[j] != nums[i]) {
            i++;
            nums[i] = nums[j];
        }
    }
    return i + 1;
}

첨엔 이해가 안갔다
이건 [0,0,1,1,1,2,2,3,3,4][ 0, 1, 2, 3, 4, 2, 2, 3, 3, 4 ]로 만들텐데 그럼 뒤에 더러운 숫자들이 남지 않는가?

근데 다시 Question을 읽어보니 뒤에 더러운 숫자가 있던 없던 반환한 lengthNum 길이만큼만 일치하면 되는거더라구.
문제가 여러모로 깔끔하지 못하네…

Answer by @Qfab

var removeDuplicates = function(nums) {
    nums.splice(0, nums.length, ...(new Set(nums)));
};

변태코드

CRA(Create React App)에 tslint, eslint, prettier 적용하기

지금 만드는 토이프로젝트에 tslint, eslint, prettier을 적용한 겸 세팅값을 블로그에도 적어둔다.

CRA로 만든 프로젝트에 따로 webpack config를 eject 하지 않고 위에 3개를 적용할 수 있는 방법임.

Step 1: 온갖 devDependencies를 설치한다.

npm i -D @types/react typescript-eslint/eslint-plugin typescript-eslint/parser eslint eslint-config-prettier eslint-config-react eslint-loader eslint-plugin-prettier prettier tslint typescript

다만 여러분이 이 글을 보는 시점에 https://github.com/prettier/prettier-atom/issues/505#issuecomment-516791742 이 Issue가 해결되지 않았다면 eslint는 5.16.0 버전으로 설치한다(19년 9월 기준으로 v6임)

// eslint를 5.16.0 으로 박아둔 버전
npm i -D @types/react typescript-eslint/eslint-plugin typescript-eslint/parser eslint@5.16.0 eslint-config-prettier eslint-config-react eslint-loader eslint-plugin-prettier prettier tslint typescript

왜냐면 에디터 prettier에서 dependency 에러가 나더라. Failed to load plugin 'prettier' declared in 'CLIOptions': Cannot find module 'eslint-plugin-prettier' 라고 나옴. eslint를 5.16버전으로 다운그레이딩하면 이 에러가 사라진다. 곧 Fix PR 만든다고 했으니 미래에는 에러 해결이 되었길…

내 package.json은 다음과 같다

"devDependencies": {
    "@types/react": "^16.9.2",
    "@typescript-eslint/eslint-plugin": "^2.3.0",
    "@typescript-eslint/parser": "^2.3.0",
    "eslint": "^5.16.0",
    "eslint-config-prettier": "^6.3.0",
    "eslint-config-react": "^1.1.7",
    "eslint-loader": "^3.0.0",
    "eslint-plugin-prettier": "^3.1.1",
    "prettier": "^1.18.2",
    "tslint": "^5.20.0",
    "typescript": "^3.6.3"
  },

Step 2: eslint 셋업을 하는 .eslintrc.json 파일을 만든다

eslint의 세부사항을 셋업할 수 있는 파일을 루트폴더에 생성한다. 잘 보면 타입스크립트와 프리티어 세팅 모두 박혀있다.
.eslintrc.json

{
  "extends": [
    "eslint:recommended",
    "plugin:react/recommended",
    "plugin:@typescript-eslint/recommended",
    "prettier/@typescript-eslint",
    "plugin:prettier/recommended"
  ],
  "plugins": ["react", "@typescript-eslint", "prettier"],
  "env": {
    "browser": true,
    "jasmine": true,
    "jest": true
  },
  "settings": {
    "react": {
      "pragma": "React",
      "version": "detect"
    }
  },
  "parser": "@typescript-eslint/parser"
}

Step 3: 몇몇 파일에 eslint를 disable시킬 .eslintignore 파일을 만든다

서비스워커 파일이나 tests 폴더나 legacy 소스들에는 eslint를 끄고싶은 분들도 있을거다. ignore할 수 있는 파일을 만든다.

.eslintignore

src/registerServiceWorker.js
src/**/__tests__/**

Step 4: 타입스크립트 린트 세팅 파일인 tslint.json을 만든다

TSlint 셋업을 해준다. 요거 키면 .tsx 파일들에 빨간줄이 무지막지하게 나온다.

tslint.json

{
    "defaultSeverity": "error",
    "extends": [
        "tslint:recommended"
    ],
    "jsRules": {},
    "rules": {
      "quotemark": false
    },
    "rulesDirectory": []
}

Step 5: 예쁘게 코드 포매팅을 해주는 프리티어 세팅 파일인 .prettierrc 를 만든다

이렇게 프리티어 세팅을 해주고 VSCode에서 mac기준 shift + option + f를 누르면 세팅값대로 코드를 예쁘게 성형해준다. 입맛대로 세팅을 해준다. 프리티어 다큐멘테이션 참고하삼 https://prettier.io/docs/en/options.html 아래는 제 세팅

.prettierrc

{
  "singleQuote": true,
  "semi": true,
  "useTabs": false,
  "tabWidth": 2,
  "trailingComma": "all",
  "printWidth": 80,
  "arrowParens": "always",
  "orderedImports": true
}

(optional) Step 6: 이제 다했당. package.json에 eslint 바로 고쳐주는 스크립트를 짜둔다

lint:fix 란 이름으로 eslint fix 스크립트를 짜두면 언제든 npm run lint:fix 명령어로 린트 픽스를 할 수 있다. (하지만 난 파일마다 수동으로 단축키를 통해 고치는걸 선호하지)

"scripts": {
    ...
    "lint:fix": "eslint './src/**/*.{ts,tsx}'"
  }

끗!
이제 열시미 js랑 tsx 파일 린트에러를 고치면 됩니다.

적용한 파일은 https://github.com/milooy/TellMe/commit/d2c5520b9d4c0ebfac2629ca2f02066afbeb61b8 참고

[React] External component를 StyledComponent로 감쌌을 때 ‘Warning: Unknown props’ 워닝 해결

Problem

에러가 났을 때 빨간 글자색을 보여주기 위해 StyledComponent에 hasError 프로퍼티를 넘기고

<StyledField hasError={hasError} />

이를 스타일링만 하기 위해 StyledComponent에서만 받아 썼더니

const StyledField = styled(Field)`
  color: ${({ hasError }) => (hasError ? "red" : "black")};
`;

Warning: React does not recognize the errorMessage prop on a DOM element. 에러가 났당. 컴포넌트를 감싼에서만 prop을 쓰고 그 안쪽의 “에서는 errorMessage prop을 안써서 나는 워닝이다.
Screen Shot 2019-09-19 at 4.56.18 PM

Solution

spread operator(...rest)로 hasError를 제외한 나머지 properties만 Field 컴포넌트에 내려보내주면 된다. 코드 드럽네

const StyledField2 = styled(({ hasError, children, ...rest }) => (
  {children}
))`
  color: ${({ hasError }) => (hasError ? "red" : "black")};
`;

Refer

https://github.com/styled-components/styled-components/issues/305

Github Pages로 배포할 폴더 변경하기

결론: subtree를 만들어서 gh-pages브랜치에 원하는 폴더만 올리면 된다.

  1. .gitignore에서 배포 원하는 폴더를 주석처리 (e.g. /public, /dist)
  2. 원하는 폴더를 add하고 commit한다: git add 폴더이름 &amp;&amp; git commit -m "Initial subtree commit"
  3. 서브트리로 gh-pages에 푸시 해준다: git subtree push --prefix 폴더이름 origin gh-pages

두번째 푸시부터 안되는 분들은 아래 3번째 명령어를 써보셔요

  1. git add docs && git commit -m “Subtree commit”
  2. git subtree push –prefix docs origin
  3. git push origin git subtree split --prefix docs master:gh-pages –force

refer: https://gist.github.com/cobyism/4730490