yarn create

terminal
1
2
3
4
5
# npx create-react-app my-app
yarn create react-app my-app

# npx create-electron-app my-app
yarn create electron-app my-app
  • 보통 보일러플레이트로 프로젝트를 시작할 때 npx를 사용한다
  • yarn createcreate로 시작하는 패키지를 설치할 때 npx와 동일하게 동작한다

npx vs. npm i -g

  • 보일러플레이트로 시작할 때 크게 두 가지 방법이 있는데,
  • npx로 시작하는 방법과, 글로벌 설치 후 cli로 시작하는 방법이 있다
  • 보통 보일러플레이트 패키지는 npx를 권장하는데, 이유는 npx는 항상 최신 버전으로 설치해준다
  • 반면 글로벌로 설치해서 사용하는 경우, 시간이 지나면 구버전이 되고, 업데이트는 하려면 수동으로 해줘야 한다

참고

gh-pages 사용하기

열심히 만든 웹을 Github Pages를 통해 자랑해보자
이때 사용할 수 있는 간편한 패키지가 있다

terminal
1
2
3
4
5
6
npm i -D gh-pages
gh-pages -d dist

# or

npx gh-pages -d dist

gh-pages

  • Github Pages 기능을 통해 정적 웹을 호스팅 할 수 있다
  • gh-pages라는 패키지를 사용하면 간편하게 배포할 수 있다
  • gh-pages를 설치하고 gh-pages -d {빌드 디렉터리} 명령을 하면,
  • gh-pages라는 브랜치가 자동으로 생성되고 빌드 디렉터리의 내용이 깃헙 저장소에 올라간다
  • github pages 세팅도 자동으로 된다
  • package.json이 프로젝트 루트 디렉터리에 존재해야 한다
  • 바닐라 웹 프로젝트를 진행한 경우에도 gh-pages를 사용하고 싶다면,
  • npm init -y 명령으로 임시로 package.json을 생성하고
  • npx로 gh-pages를 실행하면 된다

옵션 사용하기

terminal
1
npx gh-pages -d dist -b dist --dotfiles --tag \"🚀update\"
  • -d dist dist 디렉터리를
  • -b dist 원격 레포의 dist 브랜치에
  • --dotfiles 닷 파일을 포함하고,
  • --tag \"🚀update\" 태그도 지정해 줄 수 있다
  • 더 많은 옵션은 -h로 확인

nextjs 배포하기

terminal
1
2
touch .nojekyll
npx gh-pages -t true -d out
  • .nojekyll 파일을 생성해 줘야 한다
  • gh-pages는 기본적으로 지킬 기반으로 동작한다
  • 그러면 _로 시작하는 파일들을 제대로 동작하지 않는다
  • 그래서 지킬을 사용하지 않는다는 것을 .nojekyll파일을 생성하여 알릴 수 있다
  • gh-pages로 배포할 때 -t true 옵션을 사용하는데,
  • .(dot)으로 시작하는 파일을 포함하여 배포하라는 뜻이다
  • 옵션을 사용하지 않는 다면 .nojekyll이 제외된다

참고

비공개 npm package 만들어보기

  • 깃허브 레지스트리를 통해 (비공개) 패키지를 만들어보자
  • PAT을 통해 퍼블릭하지 않게 패키지를 배포해 볼 수 있다
  • 물론, 공개 패키지도 가능하다
  • 패키지 생성은 동일하고, 패키지를 받을 때만 차이가 있다
  • PAT : personal access token

PAT 발급

저장소, 패키지 만들기

  • package.json에 필수적으로 채워야 하는 항목들이 있어서,

  • 브라우저로 github에 접속해서 저장소 하나를 만들어 로컬로 클론하고
    npm init -y 하는 것을 추천한다

  • 타입스크립트로 만든다

terminal
1
2
3
git clone https://github.com/chinsun9/hello-package.git

tsc --init

디렉터리 구조

디렉터리 구조
1
2
3
4
5
6
7
8
9
10
11
12
.
├── dist
│ ├── index.d.ts
│ └── index.js
├── src
│ └── index.ts
├── .gitignore
├── .npmignore
├── .npmrc
├── package.json
├── README.md
└── tsconfig.json
  • 디렉터리 구조는 마음대로

  • 나는 위처럼 구성했다

  • src 밑에 ts 코드를 작성하고,

  • tsc 명령으로 dist 아래 트랜스파일 된 js를 배포하는 방식이다

src 작성

src/index.ts
1
2
3
4
5
export class MyClassA {}

export class MyClassB {}

export class MyClassC {}
  • 원하는 기능을 구현하고 외부에서 사용 가능하도록 export키워드를 붙여준다

tsconfig.json 설정

  • 타입스크립트로 코드를 작성하고,
  • tsc로 트랜스파일한다. 결과로 js파일이 생성된다
  • 패키지를 인스톨한 사람들은 js파일을 사용하게 된다
tsconfig.json
1
2
3
4
5
6
7
8
9
10
11
{
"compilerOptions": {
// ...
"declaration": true /* Generates corresponding '.d.ts' file. */,
// ...
"outDir": "./dist",
"rootDir": "./src"
// ...
},
"include": ["src"]
}
  • tsc --init 기본값으로 생성된 상태에서 위 옵션만 수정해주었다
  • declaration ; d.ts파일을 생성해서 자동완성에 도움을 준다
  • outDir ; tsc 명령의 결과로 생성되는 js 위치
  • rootDir, include ; root ts 파일 위치

.gitignore

.gitignore
1
2
dist
node_modules
  • 커밋에서 무시할 디렉터리, 파일을 나열한다

.npmignore

.npmignore
1
2
3
src
test
tsconfig.json
  • 패키지를 publish 할 때 무시할 디렉터리, 파일을 나열한다

.npmrc

  • 워크스페이스에 위치해도 되고,
  • 어느 워크스페이스든 상관없이 전역적으로 적용하려면 ~/.npmrc에 넣어주면 된다
.npmrc
1
//npm.pkg.github.com/:_authToken=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  • 처음에 발급받은 토큰을 xxxx... 부분에 넣어준다
  • 자신의 토큰은 아무래도 전역으로 적용하는 게 좋아 보인다

package.json

package.json
1
2
3
4
5
6
7
8
9
10
11
12
{
"name": "@chinsun9/hello-package",
"version": "1.0.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"repository": "https://github.com/chinsun9/hello-package.git",
"author": "chinsun9 <chin_sung@naver.com>",
"license": "MIT",
"publishConfig": {
"registry": "https://npm.pkg.github.com/"
}
}
  • 몇 가지 수정사항이 있다
  • name, main, types, publishConfig를 적절하게 수정한다
  • name ; @chinsun9/처럼 골뱅이 자기 이름을 넣어준다
  • main ; 트랜스파일 결과로 메인 파일을 넣어준다
  • types ; 자동완성을 위해서 생성된 d.ts파일을 넣어준다
  • publishConfig ; registry를 깃허브로 하겠다는 설정이다

패키지 배포

  • npm publish
  • 저장소에 들어가서 패키지가 잘 배포되었는지 확인하다

배포한 패키지 인스톨해보기

  • github registry에 등록된 패키지는 퍼블릭 일지라도 PAT이 필요하다
  • 퍼블릭일 때 패키지 주인이 생성한 토큰이 아니다
  • 각자 자기 자신의 토큰으로 가능하다..!
  • 비공개 패키지라면 패키지 주인이 생성한 토큰이 있어야 한다
  • 패키지 주인은 read:packages 권한이 있는 토큰을 알려주면 다른 사람, 컴퓨터에서 패키지를 인스톨할 수 있다
  • 임시 디렉터리를 하나 만들고 npm init -y한다
  • .npmrc를 생성하고 내용을 추가한다
.npmrc
1
2
//npm.pkg.github.com/:_authToken=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
@chinsun9:registry=https://npm.pkg.github.com/
  • 워크스페이스의 .npmrc 또는 유저 홈 디렉터리의 .npmrc 에 위 내용을 추가한다

  • @chinsun9:registry=https://npm.pkg.github.com/

  • @chinsun9으로 시작하는 패키지는 https://npm.pkg.github.com/ 에서 가져오라고 알려주자

헷갈린 점

  • 퍼블릭한 패키지도 PAT가 필요했다
  • 당연히 퍼블릭한 패키지에는 토큰이 필요 없을 거라 생각했다
  • 그래서 어쩔 수 없이 패키지 오너의 토큰이 필요할 거라 생각했다
  • 근데, 패키지 주인이 생성한 토큰이 아니더라도 패키지를 인스톨할 수 있었다
  • 즉, github 계정을 가지고 있고, read:packages 토큰을 생성하면 퍼블릭 패키지를 인스톨할 수 있다

참고

내 첫 패키지

GitHub 패키지 저장소 호스팅: GitHub Package Registry 시작하기

d.ts 만들기

*rc 뜻

Moment.js utc 사용하기

1
2
const date1 = moment().utc().format('YYYY-MM-DD HH:mm:ss');
console.log(date1);
  • utc() 메서드를 이용한다

활용한 곳

  • rds는 기본적으로 utc를 사용한다
  • 그래서 현재 지역 시간으로 포맷팅 된 값을 그대로 db에 넣으면 안 된다
  • rds의 시간대를 변경하거나, insert, update 할 때 잘 가공해서 쿼리를 해야 한다

참고

forEach await 안되는 문제

상황

  • 배열로 저장되어 있는 데이터를 forEach로 순회하면서 db에 INSERT하는 쿼리를 만들려고 했다
  • 근데 비동기적으로 동작했다
1
2
3
await data.forEach(async (element: ArticlePreview, idx: number) => {
// insert query
});

해결

1
2
3
4
5
6
7
8
9
for (const element of data) {
// ...
try {
const a = await databaseConnection.query(queryString, [
// ...
]);
}
// ...
}
  • for of에서는 await을 통해서 순차적으로 처리가 가능했다

시도해보지 않은 다른 방향

참고

typescript에서 mathjs 사용해서 소수점 정확하게 계산하기

  • 부동소수점으로 인한 계산 오류가 있다
  • mathjs 라이브러리로 해결해보자

mathjs 설치

1
2
yarn add mathjs
yarn add @types/mathjs -D
  • mathjs만 설치하면 타입 정의가 없기 때문에
  • @types/mathjs 보조 라이브러리까지 설치

mathjs 임포트

example1.ts
1
import * as math from 'mathjs';
  • 그냥 기본값으로 사용하려면 import * as math from 'mathjs'로 불러와 사용
  • default export가 없어서 import math from 'mathjs'이런 식으로 사용불가
    • Attempted import error: "mathjs" does not contain a default export
example2.ts
1
2
3
import { create, all } from 'mathjs';

const mathF = create(all, { number: 'Fraction' }) as math.MathJsStatic;
  • 지정 config를 적용해 사용하려면 create 메서드 사용
  • create(all, { number: 'Fraction' }) as math.MathJsStatic
  • as 키워드로 타입을 명시하지 않으면 undefined 에러 발생
    • Cannot invoke an object which is possibly 'undefined'

소수점 계산

calc.ts
1
2
3
4
5
6
7
8
9
import { create, all } from 'mathjs';

const mathF = create(all, { number: 'Fraction' }) as math.MathJsStatic;

const mathjsCalc = (expr: string) => {
return mathF.number(mathF.evaluate(expr));
};

const result = mathjsCalc(`${value1} * ${value2} * 0.01`);
  • evaluate메서드를 사용해서 수식 string을 주면 알아서 계산하도록 했다
  • 이때 math ConfigOptions에서 number 옵션이 기본값(number)인 경우에 일반적인 계산이 된다
  • number 옵션에 Fraction을 주고 생성하게 되면 evaluate 메서드에서 숫자를 Fraction 타입으로 파싱 해서 계산하게 되어 우리가 기대하는 결과를 얻을 수 있다

참고

react로 만들어본 퇴근시간 계산기

기능

메인 페이지

  • 목표 시각까지 남은 시간 계산
  • url param을 통해 목표 시각 세팅
  • 목표 시각이 지나면 퇴근하라는 메시지를 뛰움

about 페이지

  • 링크 공유 기능
  • 퇴근 시간 설정 기능
  • 자동 클립보드 복사

깃허브 페이지 spa 세팅

배경이미지 출처

해결한 문제들

gsap 적용안됨

Share.tsx
1
2
3
4
5
import { Power3, TimelineLite } from 'gsap';
import CSSPlugin from 'gsap/CSSPlugin'; // 추가

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const C = CSSPlugin; // 추가
  • 로컬에서는 애니메이션이 잘 실행되었는데, 빌드 후 애니메이션이 안 나오는 문제 해결

후기

  • 어느 정도 리액트에 익숙해진 것 같다
  • 주로 사용하는 패턴이 생긴 것 같다
  • 엄청 간단해 보이지만 이것저것 신경 쓴 것 같은데, 기록을 안 해놨다 ㅠㅠ
  • 나는 코드에 조금의 변화가 생겨도 커밋을 했었는데,,
  • 이번에는 커밋을 안 하고 한 번에 몰아서 마지막에 파일별로 커밋을 했다
  • 그래서 해결한 문제들이 기억이 잘 안 난다.. ㅠㅠ

신경 썼던 것들

input element

  • about 페이지에서는 링크를 생성하는 부분이 있다
  • 처음에는 input width가 좁았었다. 그래서 고민했던 것이…
  • input 넓이보다 안의 내용이 더 길면은 끝부분이 감춰진다

  • input에서 포커스를 해제하면 위 그림처럼,
  • 계속 앞쪽으로 포커스가 자동으로 이동했다

  • 나는 input 내용의 가장 뒤쪽,
  • url param이 변하는 것에 포커스가 가도록 만들려고 이것저것 해봤는데…
  • 결국엔 그냥 input width를 늘려버리고 끝을 냈다…

github pages SPA

  • 깃허브 페이지는 기본적으로 SPA를 지원하지 않는다
  • react-router-dom 으로 여러 경로들을 만들어 두면 index 말고는 404 페이지로 리디렉트 된다
  • 그래서 찾아보니까,,.!
404.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var segmentCount = 1;

var l = window.location;
l.replace(
l.protocol +
'//' +
l.hostname +
(l.port ? ':' + l.port : '') +
l.pathname
.split('/')
.slice(0, 1 + segmentCount)
.join('/') +
'/?p=/' +
l.pathname
.slice(1)
.split('/')
.slice(segmentCount)
.join('/')
.replace(/&/g, '~and~') +
(l.search ? '&q=' + l.search.slice(1).replace(/&/g, '~and~') : '') +
l.hash
);
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
(function (l) {
if (l.search) {
var q = {};
l.search
.slice(1)
.split('&')
.forEach(function (v) {
var a = v.split('=');
q[a[0]] = a.slice(1).join('=').replace(/~and~/g, '&');
});
if (q.p !== undefined) {
window.history.replaceState(
null,
null,
l.pathname.slice(0, -1) + (q.p || '') + (q.q ? '?' + q.q : '') + l.hash
);
}
}
})(window.location);
  • 원리는 index 경로 외에 나머지 경로로 들어오면 404페이지를 반환하는데,
  • 커스텀 404페이지에서 스크립트를 통해서 index페이지로 리디렉트 하게 한다
  • 이때, url params 및 query string을 가공한다
  • index.html에서는 404.html로부터 넘겨받은 값으로 라우팅이 적용된 화면을 보여준다
  • 이걸 만든 사람은 정말 대단한 것 같다. ㄷㄷ;

소스코드

참고

github pages spa

SyntaxError: Unexpected end of input

  • vscode에서는 아무 에러, 경고없이 잘 실행되었는데,
  • SyntaxError-Unexpected-end-of-input 에러가 날 때가 있다
  • 나의 경우에는 api 서버가 host가 달라 cors mode로 리퀘스트를 보내야하는데
  • ‘no-cors’ mode로 보내서 생긴 에러이다
  • 그 외에도 api서버에서 연산이 잘못되어 응답을 반환하지 않을 때에도 똑같은 에러가 나왔다
  • 그냥 서버로부터 결과를 제대로 받지 않으면 생기는 오류 같다

Cannot use JSX unless the '--jsx' flag is provided.ts(17004)

  • react typescript에서 tsconfig가 계속 자동수정되는 문제 해결하기
오류 메시지 모음
1
2
3
4
5
6
Cannot use JSX unless the '--jsx' flag is provided.ts(17004)

Specify JSX code generation: 'preserve', 'react', 'react-jsx', 'react-jsxdev' or'react-native'. Requires TypeScript version 2.2 or later.

The following changes are being made to your tsconfig.json file:
- compilerOptions.jsx must be react-jsx (to support the new JSX transform in React 17)

  • .ts, .tsx 확장자의 아무 파일이나 열고,
  • F1 > TypeScript 검색
  • TypeScript: Select TypeScript Version... 선택
  • 주의 : (타입스크립트 관련 파일이여야 위 명령이 검색된다)

  • Use Workspace Version 을 선택해준다.
.vscode/settings.json
1
2
3
4
{
// (...)
"typescript.tsdk": "node_modules\\typescript\\lib"
}
  • .vscode/settings.json 가보면 이렇게 업데이트 된것을 확인할 수 있다

TMI

상황

  • react typescript eslint airbnb rule 로 개발하는데
  • yarn start로 시작을 하면 자동으로 tsconfig.json의
  • compilerOptions > jsx 가 자동으로 “react-jsx”로 업데이트 됬다
  • 그런데 나는 “react-jsx”일 때 빨간줄이 마구 그어졌다
  • 그래서 yarn start 이후에 수동으로 “react”로 변경해 주었었다…

해결

  • 워크스페이스의 typescript가 사용되지 않아서 발생한 문제였다
  • compilerOptions > jsx 가 “react-jsx” 여도 빨간줄이 안생긴다!

참고