리액트 SPA 쉽게 공유해보기

내가 만든 리액트 앱을 친구들에게 공유하기 위한 간단한 방법을 소개한다
방법은 2가지가 있다

  • nodejs express
  • github pages

방법1. nodejs express

terminal
1
2
3
git clone https://github.com/chinsun9/serve-spa-expressjs.git
cd serve-spa-expressjs
npm i
  • 위 저장소를 클론하고, 종속성을 설치한다
  • index.js 에서 portstaticDir을 내 상황에 맞게 수정한다

port

index.js:5
1
const port = 5000; // 원하는 포트로 수정

staticDir (spa path)

index.js:7
1
const staticDir = path.join(__dirname, '../build'); // 빌드된 spa 경로, index.js를 기준으로 빌드된 리액트앱 상대경로를 path.join 두번째 인자에 넣어준다

소스코드

방법2. github pages

  • 깃허브 페이지를 통해 배포할 수 있다
  • 하지만 깃허브 페이지에서는 SPA를 지원하지 않는다
  • 1페이지짜리 리액트 앱이면 상관없지만,
  • react-router-dom을 사용하면서 url이 변화하는 경우
  • index.html과 404.html에 스크립트를 추가해줘야 한다
  • github pages에서 spa처럼 동작하게 해주는 마법 ; https://github.com/rafgraph/spa-github-pages#usage-instructions
  • demo page

gh-pages 설치

terminal
1
yarn add -D gh-pages

package.json 수정

package.json
1
2
3
4
5
6
7
8
9
{
// ...
"scripts": {
// ...
"deploy": "gh-pages -d build"
},
// ...
"homepage": "https://chinsun9.github.io/hello-react/"
}
  • deploy 스크립트 추가, homepage 필드를 추가한다
  • 이때 homepage필드의 값은
  • https://{username}.github.io/{repo}/로 한다

Router에 basename 추가

index.js
1
2
3
4
5
6
7
8
ReactDOM.render(
<React.StrictMode>
<Router basename={process.env.PUBLIC_URL}>
<App />
</Router>
</React.StrictMode>,
document.getElementById('root')
);
  • 최상위 Router에 basename={process.env.PUBLIC_URL}을 추가한다

404.html 추가

index.html 수정

build && deploy

terminal
1
2
yarn build
yarn deploy
  • gh-pages 브랜치가 생성되어 있고,
  • 깃허브 페이지 기능이 활성화되어있는 것을 확인할 수 있을 것이다

소스코드

참고

nodejs web dockerizing

  • 기존에 만들었었던 Nodejs Express WEB 프로젝트를 도커라이징 해보았다
  • 얄팍한 코딩사전님의 가장 쉽게 배우는 도커를 보고 시도했다
  • 도커가 이런거구나 하고 느낀 순간이였다.. 마치 신세계…
  • 이번 도커라이징을 통해서 내 프로젝트를 도커가 설치되어있는 다른컴퓨터에서 매우 쉽게 돌려볼 수 있다는걸 알게되었다

기존 상태 설명

디렉터리 구조
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.
├── node_modules/
├── web
│ ├── bin
│ │ └── www
│ ├── public/
│ ├── routes/
│ ├── utils/
│ ├── views/
│ └── app.js
├── .gitattributes
├── .gitignore
├── package-lock.json
├── package.json
├── README.md
└── sqldump.sql
  • 그냥 node express 서버와 db덤프 파일인 sqldump.sql 가 구분없이 한 디렉터리에 있었다

벡엔드와 데이터베이스로 분리하기

  • backend, database 폴더를 만든다
  • backend폴더로 sqldump.sql와 깃허브, 프리티어 세팅 등을 제외한 99.9999%의 소스파일들을 이동한다
  • database폴더로 sqldump.sql을 이동
sqldump.sql 이동
1
2
3
database
└── scripts
└── sqldump.sql
  • 이런식으로 node express 서버와 db를 분리할 수 있었다

Dockerfile 작성하기

backend

backend/Dockerfile
1
2
3
4
5
6
7
8
FROM node:12.18.3

WORKDIR /usr/src/app

COPY . .
RUN npm i

CMD ["npm", "start"]
  • 내가 로컬에서 사용하는 환경을 토대로
  • 내 앱에 맞는 노드버전을 가져와서
  • /usr/src/app 라는 워크디렉터리에
  • backend 아래있는 모든 파일들을 복사해오고
  • RUN 명령어로 npm i로 종속성을 설치한다
  • 그리고 CMD 명령으로 backend 서버를 실행시킨다

database

database/Dockerfile
1
2
3
4
5
6
7
8
FROM mariadb:10.5

ENV MYSQL_USER mysql_user
ENV MYSQL_PASSWORD 1234
ENV MYSQL_ROOT_PASSWORD 1234
ENV MYSQL_DATABASE webtest

COPY ./scripts/ /docker-entrypoint-initdb.d/
  • 내가 로컬에서 사용했던 마리아디비 버전 그대로 가져왔다
  • 데이터베이스 관련 환경변수들을 정의해주고
  • scripts 디렉터리에 있는 dump파일을 /docker-entrypoint-initdb.d/ 로 복사한다
  • /docker-entrypoint-initdb.d/ 는 데이터베이스를 생성할 때,
  • 하위 sql을 가지고 데이터베이스를 초기화해준다

후기

  • 메모리 딸린다…
  • 처음에는 좋아서 이것저것 다 도커라이징할려고 했는데,
  • 하다보니까 메모리가 딸려서 마우스가 뚝뚝 끊겼다…

참고

AWS ELB를 사용할 때 express morgan 로깅 주의점

  • morgan 로깅할 때, 사용자의 ip를 기록하는
  • :remote-addr 토큰은 req.ip 를 사용한다
  • ELB, 로드밸런서를 사용하면 사용자의 ip가 아니라, ELB의 ip가 기록된다
  • 그래서 사용자의 ip를 가져오려면 x-forwarded-for 헤더의 내용을 봐야한다
1
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;

morgan custom

app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
(...)

app.use(
morgan(
function (tokens, req, res) {
return [
req.headers['x-forwarded-for'] || req.connection.remoteAddress,
req.ip, // == :remote-addr
tokens.method(req, res),
tokens.url(req, res),
tokens.status(req, res),
].join(' ');
},
{
skip: function (req, res) {
return res.statusCode < 400;
},
}
)
);

(...)
  • 이런식으로 커스텀할 수 있다
  • req.headers['x-forwarded-for']의 내용은
1
X-Forwarded-For: <client>, <proxy1>, <proxy2>
  • 이런식으로 가장 앞의 ip가 사용자 ip이다
  • 뒤로 ,로 나열되는 ip는 프록시임으로 당황하지 말자
1
2
3
4
5
{
skip: function (req, res) {
return res.statusCode < 400;
},
}
  • morgan(f(), {})
  • morgan 두번째 인자로 옵션을 넣을 수 있다
  • skip을 주면 특정 경우에서 로깅을 하지 않는 옵션이다
  • return res.statusCode < 400;이런식으로 하면 400이하의 스테이터스 코드일 때,
  • 로깅을 하지 않는다는 의미이다
  • 이 경우 404, 500 같은 에러들만 로깅된다

참고