<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>raccoon-ccoder.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Tue, 23 Aug 2022 15:43:38 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>raccoon-ccoder.log</title>
            <url>https://velog.velcdn.com/images/raccoon-ccoder/profile/7e33cf75-afd8-4b55-bc88-2058de2028e3/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. raccoon-ccoder.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/raccoon-ccoder" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[AWS] EC2에 React + Node.js + Express + NGINX 앱 배포 (2) - Express 설정 및 NGINX를 이용한 배포]]></title>
            <link>https://velog.io/@raccoon-ccoder/AWS-EC2%EC%97%90-React-Node.js-Express-NGINX-%EC%95%B1-%EB%B0%B0%ED%8F%AC-2-Express-%EC%84%A4%EC%A0%95-%EB%B0%8F-NGINX%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EB%B0%B0%ED%8F%AC</link>
            <guid>https://velog.io/@raccoon-ccoder/AWS-EC2%EC%97%90-React-Node.js-Express-NGINX-%EC%95%B1-%EB%B0%B0%ED%8F%AC-2-Express-%EC%84%A4%EC%A0%95-%EB%B0%8F-NGINX%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EB%B0%B0%ED%8F%AC</guid>
            <pubDate>Tue, 23 Aug 2022 15:43:38 GMT</pubDate>
            <description><![CDATA[<p>이번엔 React 프로젝트에 Express를 이용한 서버 설정 및 EC2에 NGINX를 이용한 배포를 하며 삽질 및 오류 해결을 정리한 글이다.</p>
<h3 id="1-react-프로젝트-express-설정">1. React 프로젝트 Express 설정</h3>
<ul>
<li>먼저 express, cors를 설치한다.</li>
<li>root 경로에 server.js 파일 생성 후 작성한 코드다. <a href="https://codingapple.com/unit/nodejs-react-integration/">참고 링크</a><pre><code>const express = require(&#39;express&#39;)
const path = require(&#39;path&#39;)
const app = express()
</code></pre></li>
</ul>
<p>app.use(express.json())
const cors = require(&#39;cors&#39;)
app.use(cors())</p>
<p>app.use(express.static(path.join(<strong>dirname, &#39;./build&#39;)))
app.get(&#39;/&#39;, (res, req) =&gt; {
  req.sendFile(path.join(</strong>dirname, &#39;./build/index.html&#39;))
})</p>
<p>app.get(&#39;*&#39;, (res, req) =&gt; {
  req.sendFile(path.join(__dirname, &#39;./build/index.html&#39;))
})</p>
<p>app.listen(8000, () =&gt; {
  console.log(&#39;Listening on 8000&#39;)
})</p>
<pre><code>
****

### 2. Nginx 설치
- AWS EC2 인스턴스 환경에 따라 Nginx를 설치한다.</code></pre><p>$ sudo yum install nginx // Amazon Linux 1 환경
$ sudo amazon-linux-extras install nginx1.12 // Amazon Linux 2 환경</p>
<pre><code>
****

### 3. React 앱 build
- 프로젝트 내부 root에서 `yarn install`후 `build`를 생성한다. (두 과정은 새로운 버전 배포할 때마다 이뤄져야 한다.)</code></pre><p>$ yarn install
$ yarn run build</p>
<pre><code>사실 여기서부터 삽질의 연속이었다... 

### 오류와 삽질의 연속...
`build`를 하면 `Creating an optimized production build..`에서 멈추고 진행이 끝나지 않았다. 꽤나 큰 규모의 플젝이라면 빌드시 소스맵을 생성하게 되면 메모리 부족 현상일 일어날 수 있다고 해서 소스맵 관련 설정을 추가했다.

#### 빌드시 GENERATE_SOURCEMAP 옵션 off
하지만 이 방법도 해결되진 않았다. 그래도 나중에 참고할 일이 있을 것 같아 기록한다.
[소스맵이란?](https://velog.io/@racoon/React-build-%EC%8B%9C-sourcemap-%EC%A0%9C%EA%B1%B0%ED%95%98%EA%B8%B0) 
[스택오버플로우](https://github.com/coreui/coreui-free-react-admin-template/issues/178)
[관련 링크](https://progdev.tistory.com/26)</code></pre><p>// GENERATE_SOURCEMAP=false 추가
&quot;scripts&quot;: {
    &quot;start&quot;: &quot;node server.js&quot;,
    &quot;build&quot;: &quot;GENERATE_SOURCEMAP=false react-app-rewired build&quot;,
    ... 중략
  },</p>
<pre><code>
#### 메모리 스왑을 통한 메모리 부족 현상 처리
aws ec2 프리티어의 t2.micro를 사용하는데 이건 RAM이 1GB밖에 안돼서 빌드 규모가 커져도 진행이 안된다고 한다. 그래서 부족한 부분을 디스크의 일부를 대신 사용하도록 설정해줬다.</code></pre><ol>
<li>sudo dd if=/dev/zero of=/mnt/swapfile bs=1M count=2048</li>
<li>sudo mkswap /mnt/swapfile</li>
<li>sudo swapon /mnt/swapfile<pre><code>그러고 build 실행하니 아래 에러 발생…
&gt; FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
</code></pre></li>
</ol>
<p>heap size 때문에 문제가 발생해 리눅스 서버에서 bashrc에 아래 코드로 설정을 추가했다.</p>
<pre><code>vi ~/.bashrc

// bashrc에 아래 설정 추가
export NODE_OPTIONS=&quot;--max-old-space-size=8192&quot;

source ~/.bashrc</code></pre><h4 id="filezilla로-build-파일-전송">filezilla로 build 파일 전송</h4>
<p>위의 방법으로도 해결이 안되어서 생각해낸 방법이 build 파일만 filezilla를 통해 ec2에 전송하는 것이었다. filezilla 다운로드 및 접속은 <a href="https://zlzzlzz2l.tistory.com/47">링크</a>를 통해 설치했다.</p>
<p>근데 react 플젝 폴더 권한이 root여서 filezilla에서 root로 로그인해야했다. (터미널에서 해당 폴더 권한을 711로 바꿔줘도 ec2-user로 변경되지 않았음)</p>
<p>filezilla에서 root 로그인을 위한 설정을 터미널에서 하다가 에러를 만났다.</p>
<blockquote>
<p>$ service sshd restart
Error: No space left on device</p>
</blockquote>
<p>그래서 <a href="https://soyeondev.tistory.com/40">링크</a>를 참고해 볼륨을 수정해줬다.
그런데 리사이즈하다가 또 에러를 만났다...</p>
<blockquote>
<p>$ sudo resize2fs /dev/xvda 1
resize2fs: Device or resource busy while trying to open /dev/xvda</p>
</blockquote>
<p>그래서 아래의 방법으로 변경해줬다. <a href="https://stackoverflow.com/questions/24014493/unable-to-resize-root-partition-on-ec2-centos">관련 링크</a></p>
<pre><code>$ sudo resize2fs /dev/xvda 1
resize2fs: Device or resource busy while trying to open /dev/xvda</code></pre><h4 id="드디어-해결">드디어 해결!</h4>
<p>아까 막혔던 filezilla에서 root 로그인을 위한 설정을 다시 했다.</p>
<pre><code>$ service sshd restart</code></pre><p>근데 웬걸? root 계정에서 쉽게 ec2-user로 권한 설정 변경이 되는 것 아닌가?</p>
<pre><code>$ sudo chown -R ec2-user /home/ec2-user/kollus-analytics-portal-sample</code></pre><p>그 후 로컬에서 프로젝트 build 후 build된 파일은 filezilla를 통해 ec2에 옮겼다.</p>
<h4 id="로컬에서-build시-제대로-안되는-경우">로컬에서 build시 제대로 안되는 경우</h4>
<p>npm run build시 어떤 지시사항이 나오는 경우가 있다.
<a href="https://velog.velcdn.com/images/raccoon-ccoder/post/701b2ae5-e418-4088-b0e8-cf3b5738ec83/image.png"></a>
그래서 위 사진처럼 <code>serve -s build</code>명령어를 입력했다.
만약 안되는 경우 serve 패키지가 없는 거라 <code>npm i -g serve</code>로 먼저 설치한다.
따라해보니 Serving이라는 메세지와 함께 5000번 포트에서 제대로 동작한다.</p>
<hr>
<h3 id="4-nginx-설정">4. NginX 설정</h3>
<p>Nginx와 관련된 설정 파일들은 /etc 하위에 존재하므로, sudo 권한을 이용해서 파일 생성과 수정 등을 해야 한다.</p>
<h4 id="nginxconf-수정">nginx.conf 수정</h4>
<ul>
<li>nginx.conf 내에서 React 프로젝트의 build로 바로 이어지게 설정할 수도 있지만, 설정들을 깔끔하게 관리하기 위해서 일반적으로 선호되는 방식은 /etc/nginx 내에 sites-enabled 디렉토리를 생성하여 여기에 각 서비스의 설정을 넣고 nginx.conf가 이들을 확인하도록 하는 것이다.</li>
<li>지금은 앞서 말한 대로 sites-enabled 디렉토리에 따로 설정을 만들어줄 것이므로, <strong>server 블럭 행들은 모두 주석 처리(행 앞에 #)</strong> 하도록 한다. 그리고 server 블럭 바로 위에 아래와 같이 <code>include /etc/nginx/sites-enabled/*.conf;</code>를 추가함으로써 sites-enabled 하위의 설정 파일들을 포함하도록 한다.<pre><code>$ sudo vi /etc/nginx/nginx.conf
</code></pre></li>
</ul>
<p>// /etc/nginx/nginx.conf 파일 수정
...</p>
<p>include /etc/nginx/conf.d/<em>.conf;
(✨ 코드 추가) include /etc/nginx/sites-enabled/</em>.conf;</p>
<h1 id="server-">server {</h1>
<h1 id="listen-------80-default_server">listen       80 default_server;</h1>
<h1 id="listen-------80-default_server-1">listen       [::]:80 default_server;</h1>
<h1 id="server_name--_">server_name  _;</h1>
<h1 id="root---------usrsharenginxhtml">root         /usr/share/nginx/html;</h1>
<pre><code># Load configuration files for the default server block.</code></pre><h1 id="include-etcnginxdefaultdconf">include /etc/nginx/default.d/*.conf;</h1>
<h1 id="location--">location / {</h1>
<h1 id="">}</h1>
<h1 id="error_page-404-404html">error_page 404 /404.html;</h1>
<h1 id="location--40xhtml-">location = /40x.html {</h1>
<h1 id="-1">}</h1>
<h1 id="error_page-500-502-503-504-50xhtml">error_page 500 502 503 504 /50x.html;</h1>
<h1 id="location--50xhtml-">location = /50x.html {</h1>
<h1 id="-2">}</h1>
<h1 id="-3">}</h1>
<p>...</p>
<pre><code>#### sites-available, sites-enabled 설정
일반적인 방식은 sites-available 디렉토리에 필요한 파일들을 작성한 후 이들과 연결되는 symbolic link(symlink)를 sites-enabled에 추가하는 것이다. 때문에 /etc/nginx 내부에 두 디렉토리를 모두 생성해 준다. 그리고 sites-available 내에 원하는 이름으로 설정 파일을 생성해 열도록 한다.</code></pre><p>$ sudo mkdir /etc/nginx/sites-available
$ sudo mkdir /etc/nginx/sites-enabled
$ sudo vi /etc/nginx/sites-available/원하는 파일명.conf</p>
<pre><code>
지금은 도메인 등록도 되어있지 않고, HTTPS를 위한 준비도 되어있지 않으므로, HTTP에 해당하는 port 80에 대해서 아주 기본 설정만 포함한다.
</code></pre><p>server {
  listen 80;
  location / {
    root /home/ec2-user/#######/#######/build;
    index index.html index.htm;
    try_files $uri $uri/ /index.html;
  }
}</p>
<pre><code>- location 뒤의 `/`는 directive라고 하는 부분인데, IP 주소나 도메인의 뒷부분인 URI에 대응된다. `/`만 쓰면 `‘/’`로 시작하는 모든 URI에 해당한다는 것으로 현재 설정은 이 인스턴스의 IP 주소로 port 80을 통해 들어오는 모든 URL을 연결시켜주는 것이다.
- root 행에는 아까 Git을 통해 가져온 프로젝트 내부의 `build 디렉토리 경로`를 입력한다.

#### sites-enabled &gt; symlink 생성 및 Nginx 설정 테스트
- 아래와 같은 내용이 출력되면 설정의 문법이 정상적이라는 것이다.</code></pre><p>$ sudo ln -s /etc/nginx/sites-available/이전에 설정한 파일명.conf /etc/nginx/sites-enabled/이전에 설정한 파일명.conf
$ sudo nginx -t</p>
<p>nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful</p>
<pre><code>
#### Nginx 동작
이제 Nginx를 동작시키고, 웹 브라우저에서 AWS EC2 인스턴스 IP 주소를 입력하면 build한 React 앱으로 연결되는 것을 확인할 수 있다.
(AWS EC2 인스턴스 IP 주소로 바로 연결할 때 https로 들어가지는데, 현재는 http만 설정했기 때문에, http로 수정하면 들어가질 것이다.)</code></pre><p>$ sudo systemctl start nginx</p>
<pre><code>만약, `500 Internal Server Error`가 발생한다면, build까지의 경로로 접근할 때 거치는 디렉토리들에 대해 외부에서의 실행 권한이 없어서 발생하는 문제일 가능성이 높으므로, 홈 디렉토리인 /home/ec2-user의 권한을 others의 실행 권한을 포함한 711로 설정한다.</code></pre><p>$ chmod 711 /home/ec2-user</p>
<pre><code>
#### Express 서버 구동
나의 경우 프로젝트 서버 관련 파일이 server.js고 package.json에 서버 구동 관련 설정을 해놨다.
따라서 ec2에서 `yarn start`명령어를 통해 서버를 구동시켰다.
``` tsx
&quot;scripts&quot;: {
    &quot;start&quot;: &quot;node server.js&quot;, // (*)
    &quot;build&quot;: &quot;react-app-rewired build&quot;,
    &quot;test&quot;: &quot;react-app-rewired test&quot;,
    &quot;eject&quot;: &quot;react-scripts eject&quot;,
      ... 중략
  },</code></pre><hr>
<h3 id="큰-흐름">큰 흐름</h3>
<ul>
<li>로컬에서 프로젝트 <code>build</code></li>
<li>프로젝트 업뎃시 ec2에서 git pull (main에 머지한 거 아니면 특정 브랜치 선택!)</li>
<li>혹시 pull 하고 ec2에서 플젝 내에 node_modules 폴더 없다면 <code>yarn install</code></li>
<li>빌드 파일만 따로 fileZilla 통해서 전달 (원래는 ec2에서 yarn run build)</li>
<li>nginx 동작 시키기 <code>sudo systemctl start nginx</code></li>
<li>express 서버 동작시키기 <code>yarn start</code><img src="https://velog.velcdn.com/images/raccoon-ccoder/post/9a63bcd7-b9a7-4022-9e34-95d5f6ea95d9/image.png" alt=""></li>
<li>혹여나 ec2 용량이 부족하다면 아래 명령어 실행
```</li>
</ul>
<ol>
<li>sudo dd if=/dev/zero of=/mnt/swapfile bs=1M count=2048</li>
<li>sudo mkswap /mnt/swapfile</li>
<li>sudo swapon /mnt/swapfile
```</li>
</ol>
<ul>
<li>마지막으로 aws ip주소로 접속~ -&gt; <code>http://aws ip 주소:8000/</code>(port번호는 플젝에서 express 8000으로 설정했기 때문에 마지막이 8000)</li>
</ul>
<p>이로써 Nginx로 React 앱 배포 성공 !!!</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[AWS] EC2에 React + Node.js + Express + NGINX 앱 배포 (1) - AWS EC2 인스턴스 생성]]></title>
            <link>https://velog.io/@raccoon-ccoder/AWS-EC2%EC%97%90-React-Node.js-Express-NGINX-%EC%95%B1-%EB%B0%B0%ED%8F%AC-1-AWS-EC2-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%EC%83%9D%EC%84%B1</link>
            <guid>https://velog.io/@raccoon-ccoder/AWS-EC2%EC%97%90-React-Node.js-Express-NGINX-%EC%95%B1-%EB%B0%B0%ED%8F%AC-1-AWS-EC2-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%EC%83%9D%EC%84%B1</guid>
            <pubDate>Tue, 23 Aug 2022 12:49:56 GMT</pubDate>
            <description><![CDATA[<p>AWS EC2 인스턴스 생성 및 git hub 연동 (ssh) 방법에 대해 정리한 글이다.</p>
<br />

<h3 id="1-인스턴스-생성">1. 인스턴스 생성</h3>
<ul>
<li>AWS EC2 접속해 나의 경우 무난한 linux 서버를 선택했다.</li>
<li>인스턴스 유형은 프리티어, 나머지 옵션은 기본값을 선택했다.</li>
<li>보안 그룹에서 Nginx로 react를 배포하기에 http를 추가했고 node.js 개발에서 사용한 서버 포트 번호를 설정했다. (가려져 있는 건 사용중인 ip다)
<img src="https://velog.velcdn.com/images/raccoon-ccoder/post/725f5fd0-e81a-40c0-949e-2f0fc326518b/image.png" alt=""><ul>
<li>키페어 생성 및 설정 <ul>
<li>프라이빗 키(.pem)을 보안된 위치인 .ssh 하위 디렉터리에 저장<ul>
<li>필자의 경우 ssh 폴더가 존재하지 않아 따로 생성했다. <a href="https://superuser.com/questions/635269/cant-find-ssh-directory-in-my-terminal">관련 링크</a></li>
<li>프라이빗 키(.pem)의 권한 설정</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code>// ssh 하위 디렉토리에 프라이빗 키 이동
mv ~/Downloads/key이름.pem ~/.ssh/key이름.pem

// 프라이빗 키 권한 설정
chmod 400 ~/.ssh/key이름.pem</code></pre><hr>
<h3 id="2-인스턴스-연결">2. 인스턴스 연결</h3>
<ul>
<li>AWS 페이지 &gt; 인스턴스 선택 &gt; 연결 &gt; ssh 클라이언트 연결 방법에 나와 있는 예시 복사 후 터미널에 입력<pre><code>ssh -i [프라이빗 키(.pem)경로] [AMI의 사용자 이름]@[인스턴스의 퍼블릭 DNS]</code></pre></li>
</ul>
<hr>
<h3 id="3-github-연동-ssh">3. github 연동 (ssh)</h3>
<ul>
<li>ssh key 생성을 위해 .ssh 폴더로 이동 후 keygen 명령어를 통해 .pub의 공개키를 생성<pre><code>cd ~/.ssh
ssh-keygen -t rsa -C github계정 메일(example@github.com)</code></pre></li>
<li>ls 명령어 입력 &gt; id_rsa.pub 파일 생성됨<pre><code>cat id_rsa.pub
ssh-rsa 어쩌구저쩌구~~~ 내 메일 계정</code></pre></li>
<li>github에 ssh key 등록 (settings &gt; SSH and GPG keys &gt; New SSH Key 후 복붙)</li>
</ul>
<hr>
<h3 id="4-필요한-package-설치">4. 필요한 package 설치</h3>
<ul>
<li>root 권한으로 설치가 가능하기에 전환 후 필요한 패키지 설치 <a href="https://docs.aws.amazon.com/ko_kr/sdk-for-javascript/v2/developer-guide/setting-up-node-on-ec2-instance.html">참고 AWS 공식문서</a><pre><code>sudo su
</code></pre></li>
</ul>
<p>// yarn, node.js 설치
curl -o- <a href="https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh">https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh</a> | bash
. ~/.nvm/nvm.sh
nvm install --lts
node -e &quot;console.log(&#39;Running Node.js &#39; + process.version)&quot;</p>
<p>// git 설치
sudo yum update // 초기에 package 들을 update 해준다.
sudo yum install git</p>
<pre><code>- 마지막으로 프로젝트 코드를 clone한다.</code></pre><p>git clone [url]</p>
<p>// 특정 branch만 clone하고 싶은 경우
git clone -b {branch_name} --single-branch {저장소 URL}
ex) git clone -b javajigi --single-branch <a href="https://github.com/javajigi/java-racingcar">https://github.com/javajigi/java-racingcar</a></p>
<pre><code></code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[TIW_220823]]></title>
            <link>https://velog.io/@raccoon-ccoder/TIW220823</link>
            <guid>https://velog.io/@raccoon-ccoder/TIW220823</guid>
            <pubDate>Mon, 22 Aug 2022 13:55:16 GMT</pubDate>
            <description><![CDATA[<h2 id="🦝-2022-08-23-화">🦝 2022-08-23 (화)</h2>
<h3 id="오늘-한-것">오늘 한 것</h3>
<ul>
<li>오늘 한 일</li>
</ul>
<br />

<h3 id="오늘-배운-것">오늘 배운 것</h3>
<h3 id="뭐-배웠지">뭐 배웠지</h3>
<p><strong>기술명</strong></p>
<ul>
<li>서술</li>
</ul>
<br />

<h3 id="에러-해결">에러 해결</h3>
<h3 id="에러명">에러명</h3>
<p><strong>원인</strong></p>
<ul>
<li>원인 쓰자</li>
</ul>
<p><strong>해결</strong></p>
<ul>
<li>해결방법</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Error] Express : listen EADDRINUSE: address already in use :::5000]]></title>
            <link>https://velog.io/@raccoon-ccoder/Error-Express-listen-EADDRINUSE-address-already-in-use-5000</link>
            <guid>https://velog.io/@raccoon-ccoder/Error-Express-listen-EADDRINUSE-address-already-in-use-5000</guid>
            <pubDate>Mon, 22 Aug 2022 13:52:27 GMT</pubDate>
            <description><![CDATA[<p>react + express 프로젝트에서 서버를 구동하던 중 만난 에러다.</p>
<h3 id="에러-원인">에러 원인</h3>
<p>5000번 포트가 현재 다른 프로세스에서 사용중이기에 해당 포트를 사용하는 현재 서비스를 시작할 수 없다는 에러 메세지 (에러 포트 번호가 5000번이 아닐 수도 있음)
<img src="https://velog.velcdn.com/images/raccoon-ccoder/post/de49a03c-fa56-4978-ae88-514072672bc4/image.png" alt=""></p>
<br />

<h3 id="해결-방법">해결 방법</h3>
<p>5000번 포트를 사용하는 프로세스를 찾고, 필요하지 않은 경우 종료를 해야 한다.</p>
<h4 id="1-macos에서-포트-사용하는-프로세스-찾기-및-죽이기">(1) macOS에서 포트 사용하는 프로세스 찾기 및 죽이기</h4>
<pre><code class="language-tsx">// 터미널에 활성화된 프로세스 리스트 출력
lsof

// 특정 포트 사용 중인 프로세스 조회
lsof -i TCP:5000

COMMAND     PID          USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
ControlCe  1495 baekjeongyeon   23u  IPv4 0xb1ac9a99ec85b0cb      0t0  TCP *:commplex-main (LISTEN)
ControlCe  1495 baekjeongyeon   24u  IPv6 0xb1ac9a9ebac2a583      0t0  TCP *:commplex-main (LISTEN)
Google    13434 baekjeongyeon   46u  IPv6 0xb1ac9a9ec6ffc883      0t0  TCP localhost:51824-&gt;localhost:commplex-main (CLOSE_WAIT)

// 원하는 프로세스 죽이기 (kill 명령어로 강제로 종료 및 -9는 kill의 강제 종료 시그놀인 9번을 사용한다는 의미)
kill -9 1495</code></pre>
<p>그런데 자세히 보면 command 종류가 node가 아닌 ControlCe였다..!</p>
<h4 id="2-macos-monterey-관련-이슈-해결">(2) macOS Monterey 관련 이슈 해결</h4>
<p>Monterey로 업데이트 하면서 생긴 <a href="https://developer.apple.com/forums/thread/682332">문제</a> 같다. </p>
<ul>
<li><p>시스템 환경 설정 - 공유 - AirPlay 수신 모드 항목의 체크를 해제
<img src="https://velog.velcdn.com/images/raccoon-ccoder/post/fdb04be2-6e52-4343-a174-d33324e41373/image.png" alt=""></p>
</li>
<li><p>이후에 5000번 포트 사용 프로세스 조회시 깔끔하게 없어짐
<img src="https://velog.velcdn.com/images/raccoon-ccoder/post/01506690-c043-4f63-aefe-e4a31806b366/image.png" alt=""></p>
</li>
</ul>
<h3 id="참고-자료reference">참고 자료(Reference)</h3>
<p><a href="https://developer.apple.com/forums/thread/682332">apple.developer</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIW_220822]]></title>
            <link>https://velog.io/@raccoon-ccoder/TIW220822</link>
            <guid>https://velog.io/@raccoon-ccoder/TIW220822</guid>
            <pubDate>Mon, 22 Aug 2022 09:48:36 GMT</pubDate>
            <description><![CDATA[<h2 id="🦝-2022-08-22-월">🦝 2022-08-22 (월)</h2>
<h3 id="오늘-한-것">오늘 한 것</h3>
<ul>
<li>Node.js + Express로 임시 서버 생성 및 리액트 연결<pre><code>// terminal
npm install nodemon -g
</code></pre></li>
</ul>
<p>// project
npm install express nodemon concurrently</p>
<pre><code>- AWS EC2 인스턴스 생성 및 서버에 NGINX를 통한 배포 (진행중)

&lt;br /&gt;

### 오늘 배운 것
### nginx와 express는 무엇이고 왜 쓰는지?
**nginx**
- 동시 접속 처리에 특화된 웹서버로 클라이언트로부터 http 요청을 받아 요청에 해당하는 파일을 http 통신을 통해 응답해주는 프로그램
- 웹서버 특성상, WAS와는 다르게 정적 컨텐츠 호스팅에 특화되어있고 이 밖에 리버스 프록시, 캐싱, 로드 밸런싱, 미디어스트리밍 등 유용한 여러 역할을 수행한다.
- 가벼움과 높은 성능 지향

**리버스 프록시 서버**
- 내부 애플리케이션과 외부 클라이언트 사이에 자리 잡아 클라이언트 요청을 적절한 서버로 보내주는 역할
- Node.js나 tomcat 같은 많은 어플리케이션들은 스스로 서버와 같은 역할을 하지만 Nginx에서 제공하는 로드 밸런싱, 보안, 가속화 등의 이유로 Nginx 사용
![](https://velog.velcdn.com/images/raccoon-ccoder/post/3402eab4-8e20-4607-9f9e-98310b712348/image.png)
![](https://velog.velcdn.com/images/raccoon-ccoder/post/8e5b945f-0589-4b77-bf2c-c00081f45913/image.png)

**express**
- Node.js를 위한 빠르고 개방적인 간결한 웹 프레임워크
- 쉽게 말하면 NodeJS를 사용하여 쉽게 서버를 구성할 수 있게 만든 클래스와 라이브러리의 집합체

### node.js 사용시 nodemon 설치해서 사용하는데 nodemon은 뭐지?
nodemon
- node.js를 이용하는 파일들은 수정을 해도 반영이 바로 안되고 서버를 재시작해줘야 반영이 되기 때문에 번거롭다. nodemon은 코드가 수정될 경우 자동으로 서버를 재시작 해주기 때문에 편리하게 사용할 수 있다.

### concurrently 라이브러리는 뭐고 언제 써?
concurrently
- 리액트와 익스프레스를 동시 실행해주는 역할을 한다.

&lt;br /&gt;

### 에러 해결
### Error: listen EADDRINUSE: address already in use :::5000
원인 -  5000번 포트가 현재 다른 프로세스에서 사용중이기에 해당 포트를 사용하는 현재 서비스를 시작할 수 없다는 에러 메세지 (에러 포트 번호가 5000번이 아닐 수도 있음)

해결 - 5000번 포트를 사용하는 프로세스를 찾고, 필요하지 않은 경우 종료를 하거나 macOS Monterey 관련 이슈일 경우, 시스템 환경 설정 - 공유 - AirPlay 수신 모드 항목의 체크를 해제한다.

### Can&#39;t find .ssh directory 
mkdir -p ~/.ssh 로 직접 생성하기





</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[TIW_220811]]></title>
            <link>https://velog.io/@raccoon-ccoder/TIW220811</link>
            <guid>https://velog.io/@raccoon-ccoder/TIW220811</guid>
            <pubDate>Thu, 11 Aug 2022 09:49:42 GMT</pubDate>
            <description><![CDATA[<h2 id="🦝-2022-08-11-목">🦝 2022-08-11 (목)</h2>
<h3 id="오늘-한-것">오늘 한 것</h3>
<ul>
<li>차트 css 수정 및 부제목 추가</li>
<li>card collapse하는 설정 버튼 css 수정 및 아이콘 hover시 nametag 띄워주는 tooltip 추가</li>
<li>최초 렌더링시 화면을 가득 채우기 위한 차트 필터 초기값 설정</li>
</ul>
<h3 id="오늘-배운-것">오늘 배운 것</h3>
<h4 id="initializestate---recoil에서-state-initialization-하기">initializeState - Recoil에서 state initialization 하기</h4>
<pre><code>function MyApp() {
  function initializeState({set}:MutableSnapshot) {
    set(myAtom, &#39;foo&#39;);
  }

  return (
    &lt;RecoilRoot initializeState={initializeState}&gt;
      ...
    &lt;/RecoilRoot&gt;
  );
}</code></pre><ul>
<li>MutableSnapshot을 통해 상태를 초기화하기 위한 선택적인 initializeState
 prop을 취한다. 이는 모든 atom을 미리 알고 있고, 상태가 동기적으로 설정되어야하는 서버사이드렌더링과 호환되는 경우 지속되는 상태를 로딩하는데에 유용할 수 있다.<ul>
<li>나는 비동기 처리를 위해 react-query를 사용하였기에 queryClient.fetchQuery로 데이터를 불러와 set함수를 통해 초기값을 설정하였다.</li>
</ul>
</li>
</ul>
<pre><code class="language-jsx">const initializeState = async ({set}: MutableSnapshot) =&gt; {
  const broadcastKeyList = await queryClient.fetchQuery(&#39;api 주소&#39;)

  set(broadcastTimeAtom, {
    broadcastKey: broadcastKeyList[broadcastKeyList.length - 1],
    timeGroup: &#39;5m&#39;,
    timeGroupNumber: 60 * 5,
  })

  set(joinChartFilterAtom, [...broadcastKeyList.slice(-5)])
}

ReactDOM.render(
  &lt;React.StrictMode&gt;
    &lt;RecoilRoot initializeState={initializeState}&gt;
      // 생략
    &lt;/RecoilRoot&gt;
  &lt;/React.StrictMode&gt;,
  document.getElementById(&#39;root&#39;)
)</code></pre>
<br />

<h4 id="queryclientfetchquery---쿼리를-가져오고-캐시하는데-사용할-수있는-비동기-메서드">queryClient.fetchQuery - 쿼리를 가져오고 캐시하는데 사용할 수있는 비동기 메서드</h4>
<ul>
<li>useQuery와 거의 흡사하지만 <a href="https://tanstack.com/query/v4/docs/reference/QueryClient#queryclientfetchquery">일부 옵션</a>은 해당 메서드에서 사용이 불가하다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Error] TypeScript : Element implicitly has an 'any' type because expression of type 'string' can't be used to index]]></title>
            <link>https://velog.io/@raccoon-ccoder/Error-TypeScript-Element-implicitly-has-an-any-type-because-expression-of-type-string-cant-be-used-to-index</link>
            <guid>https://velog.io/@raccoon-ccoder/Error-TypeScript-Element-implicitly-has-an-any-type-because-expression-of-type-string-cant-be-used-to-index</guid>
            <pubDate>Tue, 09 Aug 2022 15:23:48 GMT</pubDate>
            <description><![CDATA[<p>React(CRA), TypeScript 기반의 프로젝트에서 객체의 프로퍼티를 읽다가 만난 컴파일 에러다.</p>
<h3 id="에러-원인">에러 원인</h3>
<p>TypeScript는 기본적으로 객체의 프로퍼티를 읽을 때, <code>string</code>타입의 key 사용을 허용하지 않는다. 아래의 코드에서 에러가 발생한 이유는 <code>string literal</code>타입만 허용되는 곳에 string 타입을 사용했기 때문이다.</p>
<pre><code class="language-jsx">// 객체를 기본값으로 가진 recoil atom 생성
const chartFilterAtom = atom({
  key: &#39;chartFliterAtom&#39;,
  default: {
    chatCount: true,
    joinCount: true,
    likeCount: true,
    joinAndLeave: true,
    chatTotal: true,
  },
});

// atom 상태를 변경할 수 있는 setChartFilter 함수에서 이전 상태를 filters라는 인자로 전달 받아 string 타입의 key로 객체 프로퍼티 접근 시 에러 발생
const [chartFilter, setChartFilter] = useRecoilState(chartFilterAtom);
setChartFilter(filters =&gt; {
    for(const key in filters) {
        console.log(`${filters[key]}`) // error !
    }
})</code></pre>
<br />

<h3 id="해결-방법">해결 방법</h3>
<h4 id="index-signature-선언하기">Index Signature 선언하기</h4>
<p>아래 코드처럼 객체에 <code>index signature</code>를 추가함으로써 string타입과 literal type 모두를 사용해서 객체에 접근할 수 있다.
아래처럼 index signature가 선언된 경우 모든 프로퍼티가 해당 타입에 속해야 한다.</p>
<pre><code class="language-jsx">interface IChartFilter {
  [key: string]: boolean // index signature 추가, key는 정해진 키워드가 아니며 변경 가능하다.
  chatCount: boolean
  joinCount: boolean
  likeCount: boolean
  joinAndLeave: boolean
  chatTotal: boolean

  // Error case
  iAmWrongType: number // error! Property &#39;iAmWrongType&#39; of type &#39;number&#39; is not assignable to string index type &#39;boolean&#39;.
}

const chartFilterAtom = atom&lt;IChartFilter&gt;({
  key: &#39;chartFliterAtom&#39;,
  default: {
    chatCount: true,
    joinCount: true,
    likeCount: true,
    joinAndLeave: true,
    chatTotal: true,
  },
})

const [chartFilter, setChartFilter] = useRecoilState(chartFilterAtom);
setChartFilter(filters =&gt; {
    for(const key in filters) {
        console.log(`${filters[key]}`) // ok
    }
})</code></pre>
<br />


<h3 id="번외-string과-string-literal의-차이">[번외] String과 String literal의 차이</h3>
<p>아래의 TypeScript 코드에서 <code>b</code>와 <code>c</code>는 string 타입이지만 <code>a</code>는 <code>&quot;Hello World&quot;</code>타입이다.
<code>b</code>변수는 let으로 선언되어 재할당이 가능해 어떤 문자열이든 넣을 수 있기에 컴파일러는 이 변수를 string 타입으로 추론한다. 그리고 <code>c</code>변수는 명시적으로 string 타입으로 선언했기에 string 타입이다.</p>
<pre><code class="language-jsx">const a = &quot;Hello World&quot;
let b = &quot;Hello World&quot;
const c: string = &quot;Hello World&quot;</code></pre>
<p>하지만 a의 경우 컴파일러는 string이 아닌 더 좁은 타입(narrowed type)으로 선언한 것으로 추론한다. 이것을 <a href="https://www.typescriptlang.org/docs/handbook/literal-types.html#literal-narrowing">Literal Narrowing</a>이라고 한다.</p>
<p>따라서 <code>a</code>의 타입을 string이 아닌 string타입을 좁혀 만든 <code>string litreal type</code>이다. 여기서 &#39;좁힌다&#39;의 의미는 무한대의 경우의 수를 가질 수 있는 string 타입보다 훨씬 구체적인 string의 부분 집합, <code>&quot;Hello World&quot;</code>만을 허용하는 타입을 선언했다는 뜻이다.</p>
<p>아래와 같이 명시적으로 literal type을 선언하면 let으로 선언된 변수도 <code>&quot;Hello World&quot;</code>타입만을 허용하도록 만들 수 있다.</p>
<pre><code class="language-jsx">type HelloWorldType = &quot;Hello World&quot; // literal type

let a: HelloWorldType = &quot;Hello World&quot; // ok
a = &quot;hahaha&quot; // compile error: &quot;hahaha&quot;는 &quot;Hello World&quot;가 아니기 때문.</code></pre>
<h4 id="string-literal의-유용함">String Literal의 유용함</h4>
<p>string literal 타입은 열거형 타입처럼 사용할 때 매우 유용하다. 예를 들어 마우스 이벤트를 처리하는 함수가 있다고 하자. 마우스 이벤트의 종류는 이미 정해져 있을 것이다. JavaScript의 방법대로 이벤트 이름을 <code>string</code>타입으로 받을 수 있다. 하지만 오타 혹은 유효하지 않은 이벤트 이름으로 인해 발생하는 런타임에러를 사전에 방지할 수 없다.</p>
<pre><code class="language-jsx">function handleEvent(event: string) {}
handleEvent(&quot;click&quot;)
handleEvent(&quot;clock&quot;) // compile error: 오타. 컴파일 타임에 발견할 수 없다.
handleEvent(&quot;hover&quot;) // compile error: 유효하지 않은 이벤트 이름. 마찬가지로 컴파일 타임에 걸러낼 수 없다.</code></pre>
<p>다음의 예제과 같이 string literal 타입 조합만을 허용하도록 하도록 수정한다. 여기서 <code>|</code>은 <a href="https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html">union type</a>을 의미하며 두 개의 타입 이상을 결합할 수 있다.</p>
<pre><code class="language-jsx">type EventType = &quot;mouseout&quot; | &quot;mouseover&quot; | &quot;click&quot;
function handleEvent(event: EventType) {}
handleEvent(&quot;click&quot;)
handleEvent(&quot;hover&quot;) // compile error: Argument of type &#39;&quot;hover&quot;&#39; is not assignable to parameter of type &#39;EventType&#39;.</code></pre>
<p>이렇게 string literal 타입을 활용하면 <code>&quot;clock&quot;</code>과 어이 없는 오타를 컴파일 타임에 알 수 있으며, IDE에서 제공하는 suggestion 기능(ctrl + space) 편리함을 누릴 수도 있다.</p>
<h4 id="index-signature-선언으로-string-key를-이용한-객체-접근">Index Signature 선언으로 String key를 이용한 객체 접근</h4>
<pre><code class="language-jsx">type ObjType = {
  [index: string]: string
  foo: string
  bar: string
}

const obj: ObjType = {
  foo: &quot;hello&quot;,
  bar: &quot;world&quot;,
}

const propertyName1 = &quot;foo&quot;
const propertyName2: string = &quot;foo&quot;

console.log(obj[propertyName1]) // ok
console.log(obj[propertyName2]) // ok</code></pre>
<p>위와 같이 객체에 index signature를 추가함으로써 string타입과 literal type 모두를 사용해 객체에 접근할 수 있게 되었다. 참고로 index signature가 선언된 경우 모든 프로퍼티가 규칙을 따라야 하며 그렇지 않으면 에러 가 발생한다.</p>
<pre><code class="language-jsx">type ObjType = {
  [key: string]: string
  foo: string
  bar: number // error! Property &#39;bar&#39; of type &#39;number&#39; is not assignable to string index type &#39;string&#39;.
}</code></pre>
<br />

<h3 id="참고-자료reference">참고 자료(Reference)</h3>
<p><a href="https://stackoverflow.com/questions/57086672/element-implicitly-has-an-any-type-because-expression-of-type-string-cant-b">stckoverflow</a>
<a href="https://soopdop.github.io/2020/12/01/index-signatures-in-typescript/">String과 String literal의 차이</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIW_220810]]></title>
            <link>https://velog.io/@raccoon-ccoder/TIW220810</link>
            <guid>https://velog.io/@raccoon-ccoder/TIW220810</guid>
            <pubDate>Tue, 09 Aug 2022 14:51:25 GMT</pubDate>
            <description><![CDATA[<h2 id="🦝-2022-08-10-수">🦝 2022-08-10 (수)</h2>
<h3 id="오늘-한-것">오늘 한 것</h3>
<ul>
<li>대시보드 header에 card 컴포넌트들 collapse 효과 여부의 전역 상태를 관리하는 버튼 구현</li>
<li>차트 컴포넌트 필터 목록 역순으로 수정</li>
<li>Bootstrap card css를 미리 만들어두었지만... 부장님과 상의 끝에 다른 쪽으로 방향을 바꾸는 것이 낫겠다 싶어 수정 또 수정..!</li>
<li>바 레이스 차트 필터 버튼 클릭시 드롭다운 안되는 에러 해결  -&gt; Card 레이아웃 컴포넌트가 차트 컴포넌트 내에 포함되는 구조였고 차트 필터 변경시 컴포넌트가 리렌더링 되면서 Card 컴포넌트도 리렌더링되어 bootstrap으로 제작된 버튼 기능 오류 발생 -&gt; 아예 대시보드 페이지에서 레이아웃 컴포넌트 + 차트 컴포넌트 이런 식으로 사용하는 것이 나은지 고민이다... </li>
</ul>
<h3 id="오늘-배운-것">오늘 배운 것</h3>
<h4 id="리액트-라이브러리-사용시-label-태그에서-for---htmlfor-속성-사용">리액트 라이브러리 사용시 label 태그에서 for -&gt; htmlFor 속성 사용</h4>
<blockquote>
<p>Type &#39;{ children: string; className: string; for: string; }&#39; is not assignable to type &#39;DetailedHTMLProps&lt;LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement&gt;’</p>
</blockquote>
<ul>
<li>class -&gt; className인것처럼 label에서는 for -&gt; <code>htmlFor</code> 사용해야 한다.</li>
</ul>
<h4 id="usequery에서-받은-데이터-관련-옵션">useQuery에서 받은 데이터 관련 옵션</h4>
<pre><code>{
      select: data =&gt; data.reverse()
}</code></pre><ul>
<li><code>select</code> : data 받아와서 reverse()하고 싶은 경우 옵션 설정</li>
</ul>
<br />


]]></description>
        </item>
        <item>
            <title><![CDATA[TIW_220809]]></title>
            <link>https://velog.io/@raccoon-ccoder/TIW220809</link>
            <guid>https://velog.io/@raccoon-ccoder/TIW220809</guid>
            <pubDate>Tue, 09 Aug 2022 14:47:21 GMT</pubDate>
            <description><![CDATA[<h2 id="🦝-2022-08-09-화">🦝 2022-08-09 (화)</h2>
<h3 id="오늘-한-것">오늘 한 것</h3>
<ul>
<li>Bootstrap card css 생성 및 레이아웃 적용</li>
<li>card header에 차트 드래그 앤 드랍 및 삭제 버튼 디자인 적용 및 기능 구현</li>
<li>차트 드래그시 놓여질 위치 css 적용 (className=<code>draggable-source--is-dragging</code>)</li>
</ul>
<h3 id="오늘-배운-것">오늘 배운 것</h3>
<h4 id="typescript에-string-key로-객체에-접근하는-방법">TypeScript에 string key로 객체에 접근하는 방법</h4>
<p>TypeScript에서는 기본적으로 객체의 프로퍼티를 읽을 때, string타입의 key 사용을 허용하지 않기에 아래처럼 코드를 작성하면 컴파일 에러를 만나게 된다.</p>
<pre><code class="language-jsx">const obj = {
  foo: &quot;hello&quot;,
}

let propertyName = &quot;foo&quot;

console.log(obj[propertyName]) // compile error!</code></pre>
<p>따라서 객체에  index signature를 추가하여 해결한다.</p>
<pre><code class="language-jsx">type ObjType = {
  [index: string]: string
  foo: string
  bar: string
}

const obj: ObjType = {
  foo: &quot;hello&quot;,
  bar: &quot;world&quot;,
}

const propertyName1 = &quot;foo&quot;
const propertyName2: string = &quot;foo&quot;

console.log(obj[propertyName1]) // ok
console.log(obj[propertyName2]) // ok</code></pre>
<br />


<h3 id="참고-자료reference">참고 자료(Reference)</h3>
<p><a href="https://soopdop.github.io/2020/12/01/index-signatures-in-typescript/">TypeScript에서 string key로 객체에 접근하기</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIW_220808]]></title>
            <link>https://velog.io/@raccoon-ccoder/TIW220808</link>
            <guid>https://velog.io/@raccoon-ccoder/TIW220808</guid>
            <pubDate>Mon, 08 Aug 2022 10:02:46 GMT</pubDate>
            <description><![CDATA[<h2 id="🦝-2022-08-08-월">🦝 2022-08-08 (월)</h2>
<h3 id="오늘-한-것">오늘 한 것</h3>
<ul>
<li>포틀릿을 통한 차트 이동 구현</li>
<li>gh-pages 배포시 cors 문제 해결 (야매로 한 거라 다시 봐야할 듯 싶다)</li>
<li>Bootstrap Card 레이아웃 생성 및 Card header에 있는 특정 아이콘 클릭시에만 차트 드래그 앤 드랍 구현</li>
</ul>
<h3 id="오늘-배운-것">오늘 배운 것</h3>
<h4 id="shopifydraggable-draggable-속성">@shopify/draggable Draggable 속성</h4>
<p><code>handle</code> -  특정 버튼 클릭시에만 drag &amp; drop 가능하게 구현
<code>plugins: [Plugins.SortAnimation]</code> -  drag &amp; drop 시 부드러운 애니메이션 및 주변 컴포넌트 상하좌우 움직이게 적용</p>
<pre><code class="language-jsx">&lt;div class=&quot;box red&quot;&gt;
    &lt;div class=&quot;draggable-handle&quot;&gt;
&lt;/div&gt;

const sortable = new Sortable(containerEl, {
  draggable: &#39;.box&#39;,
  handle: &#39;.draggable-handle&#39;, // handle에 작성한 클래스 가진 요소를 눌렀을 때만 컴포넌트 이동 가능
  sortAnimation: {
    duration: 200,
    easingFunction: &#39;ease-in-out&#39;,
    horizontal: true
  },
  plugins: [Plugins.SortAnimation] // 상하 좌우로 주변 컨테이너 이동 (1.0.0@beta12버전부터 추가)
});</code></pre>
<h4 id="echarts-공백-제거-방법">echarts 공백 제거 방법</h4>
<p><code>grid</code> -  속성으로 원하는 만큼의 공백을 제거한다.</p>
<pre><code>grid: {
  left: 0,
  top: 0,
  right: 0,
  bottom: 0
}</code></pre><br />

<h3 id="에러-해결">에러 해결</h3>
<h4 id="react-프로젝트-gh-pages에-배포시-cors-문제-해결">react 프로젝트 gh-pages에 배포시 cors 문제 해결</h4>
<p>개발 단계에서는 http-proxy-middleware 모듈을 이용해 proxy 설정으로 CORS 문제를 해결했지만 배포 단계에서는 지원이 되지 않기에 CORS 문제가 발생했다. 여러 방법을 사용해봤지만 다 안되었다... 
당장 배포해서 회의 때 진행 상황을 보고해야 했기 때문에 일단 급하게 아래와 같은 방법을 사용했다.</p>
<p><strong>해결 방법</strong></p>
<p>axios 설정에서 <code>withCredentials</code>값을 false로 수정한다.</p>
<pre><code>axios({
    method: &#39;get&#39;,
    url: `https://hello`,
    withCredentials: false, // withCredentials를 false로 설정
    params: {
      access_token: SECRET_TOKEN,
    },
  });</code></pre><br />

<br />

<h3 id="참고-자료reference">참고 자료(Reference)</h3>
<p><a href="https://echarts.apache.org/en/option.html#grid">echarts grid</a>
<a href="https://stackoverflow.com/questions/50949594/axios-having-cors-issue">CORS - stackoverflow</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIW_220801]]></title>
            <link>https://velog.io/@raccoon-ccoder/TIW220801</link>
            <guid>https://velog.io/@raccoon-ccoder/TIW220801</guid>
            <pubDate>Sun, 31 Jul 2022 15:32:55 GMT</pubDate>
            <description><![CDATA[<h2 id="🦝-2022-08-01-월">🦝 2022-08-01 (월)</h2>
<h3 id="오늘-한-것">오늘 한 것</h3>
<h3 id="에러-해결">에러 해결</h3>
<h4 id="터미널에서-git-id-password로-로그인-안되는-현상-해결">터미널에서 Git id, password로 로그인 안되는 현상 해결</h4>
<ul>
<li>2021.08월 이후 id/pw 방식이 아닌 token을 이용해 로그인하기에 발생하는 문제로 아래의 방법으로 해결</li>
</ul>
<p><strong>해결 방법</strong></p>
<ol>
<li>깃헙 접속해 로그인</li>
<li>Settings &gt; Developer setting &gt; Personal access tokens에서 새 토큰 생성</li>
<li>Note란에는 원하는 사용처 입력 후 expiration에서 사용 기간 선택 후 scope에서 repo 선택</li>
<li>token 선택되면 꼭 다른 곳에 복사 !!!! (한번 보고 보여지지 않음)</li>
<li>로컬 컴퓨터의 깃에 등록<pre><code>git config --global user.name &#39;깃헙 ID&#39;
git config --global user.password &#39;복사한 토큰&#39;</code></pre></li>
</ol>
<p>global로 등록하지 않고 git pull 사용할 때 다시 id/pw 입력하라 하면 본인 깃헙 아이디, 복사한 토큰을 패스워드로 전달하면 된다.</p>
<br />

<h4 id="git-clone-후-npm-i-시-생기는-에러-eresolve-could-not-solve---라이브러리-충돌-문제">git clone 후 npm i 시 생기는 에러 (ERESOLVE could not solve) - 라이브러리 충돌 문제</h4>
<ul>
<li>아래 에러를 보면 <a href="mailto:react-query-devtools@2.6.3">react-query-devtools@2.6.3</a>의 <a href="mailto:react-query@3.39.2">react-query@3.39.2</a> 버전과 설치된 <a href="mailto:react-query@3.34.12">react-query@3.34.12</a> 버전 차이로 인해 충돌이 일어났다.
<img src="https://velog.velcdn.com/images/raccoon-ccoder/post/97bef3f4-b288-4b46-a9d9-853a3af547e6/image.png" alt=""></li>
</ul>
<h4 id="해결-방법">해결 방법</h4>
<p>해당 프로젝트에서 react-quert-devtools 라이브러리가 필요하지 않아 삭제하였고 package-lock.json 파일, node_modules 폴더 삭제 후 react-query는 2버전으로 낮춘 후 패키지를 다시 설치했다.</p>
<pre><code>npm i --legacy-peer-deps</code></pre><br />

<h3 id="참고-자료reference">참고 자료(Reference)</h3>
<p><a href="https://stackoverflow.com/questions/66020820/npm-when-to-use-force-and-legacy-peer-deps">stackoverflow</a>
<a href="https://github.com/raccoon-ccoder/TIL/blob/main/Error/%5BReact%5D%20ERESOLVE%20unable%20to%20resolve%20dependency%20tree.md">내 깃헙 정리 자료</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React Test] jest-dom custom matcher, rtl 쿼리 종류 링크]]></title>
            <link>https://velog.io/@raccoon-ccoder/React-Test-jest-dom-custom-matcher-rtl-%EC%BF%BC%EB%A6%AC-%EC%A2%85%EB%A5%98-%EB%A7%81%ED%81%AC</link>
            <guid>https://velog.io/@raccoon-ccoder/React-Test-jest-dom-custom-matcher-rtl-%EC%BF%BC%EB%A6%AC-%EC%A2%85%EB%A5%98-%EB%A7%81%ED%81%AC</guid>
            <pubDate>Sat, 30 Jul 2022 15:50:55 GMT</pubDate>
            <description><![CDATA[<p><a href="https://github.com/testing-library/jest-dom#table-of-contents">@testing-library/jest-dom에서 제공하는 custom matcher 목록</a>
<a href="https://testing-library.com/docs/dom-testing-library/cheatsheet/">React Testing Library 쿼리 함수 종류</a>
<a href="https://www.daleseo.com/jest-fn-spy-on/">jest.fn() 사용법</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Error] Not implemented: HTMLCanvasElement.prototype.getContext (without installing the canvas npm package)]]></title>
            <link>https://velog.io/@raccoon-ccoder/Error-Not-implemented-HTMLCanvasElement.prototype.getContext-without-installing-the-canvas-npm-package</link>
            <guid>https://velog.io/@raccoon-ccoder/Error-Not-implemented-HTMLCanvasElement.prototype.getContext-without-installing-the-canvas-npm-package</guid>
            <pubDate>Sat, 30 Jul 2022 15:47:21 GMT</pubDate>
            <description><![CDATA[<p>apache-echarts 라이브러리를 이용한 React 프로젝트에서 테스트 코드를 실행하던 중 만난 에러다.</p>
<h3 id="해결-방법">해결 방법</h3>
<h4 id="1-jest-canvas-mock-라이브러리-설치">1. jest-canvas-mock 라이브러리 설치</h4>
<pre><code class="language-json">npm i --save-dev jest-canvas-mock</code></pre>
<h4 id="2-packagejson에-아래-설정-추가">2. package.json에 아래 설정 추가</h4>
<pre><code class="language-json">{
  &quot;jest&quot;: {
    &quot;setupFiles&quot;: [
      &quot;jest-canvas-mock&quot;
    ],
  }
}</code></pre>
<h3 id="참고-자료reference">참고 자료(Reference)</h3>
<p><a href="https://www.npmjs.com/package/jest-canvas-mock">jest-canvas-mock 라이브러리</a>
<a href="https://stackoverflow.com/questions/48828759/unit-test-raises-error-because-of-getcontext-is-not-implemented">stackoverflow</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[jest] jest + react-app-wired + customize-cra에서 절대 경로 및 별칭 사용시 인식 안되는 문제 해결]]></title>
            <link>https://velog.io/@raccoon-ccoder/jest-jest-react-app-wired-customize-cra%EC%97%90%EC%84%9C-%EC%A0%88%EB%8C%80-%EA%B2%BD%EB%A1%9C-%EB%B0%8F-%EB%B3%84%EC%B9%AD-%EC%82%AC%EC%9A%A9%EC%8B%9C-%EC%9D%B8%EC%8B%9D-%EC%95%88%EB%90%98%EB%8A%94-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0</link>
            <guid>https://velog.io/@raccoon-ccoder/jest-jest-react-app-wired-customize-cra%EC%97%90%EC%84%9C-%EC%A0%88%EB%8C%80-%EA%B2%BD%EB%A1%9C-%EB%B0%8F-%EB%B3%84%EC%B9%AD-%EC%82%AC%EC%9A%A9%EC%8B%9C-%EC%9D%B8%EC%8B%9D-%EC%95%88%EB%90%98%EB%8A%94-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0</guid>
            <pubDate>Sat, 30 Jul 2022 15:41:16 GMT</pubDate>
            <description><![CDATA[<p>React(CRA), TypeScript 기반의 프로젝트에서 react-app-wired, customize-cra 라이브러리를 사용하고 jest로 테스트 코드를 작성하려 했는데 기존에 설정했던 절대 경로 및 별칭 인식이 되지 않아 에러를 겪고 아래와 같은 해결 방법을 사용했다.</p>
<h3 id="해결-방법">해결 방법</h3>
<h4 id="1-packagejson에-jest-관련-절대-경로-및-별칭-설정">1. package.json에 jest 관련 절대 경로 및 별칭 설정</h4>
<pre><code class="language-jsx">&quot;jest&quot;: {
    &quot;moduleNameMapper&quot;: {
      &quot;@test/(.*)&quot;: &quot;&lt;rootDir&gt;/src/test/$1&quot;,
      &quot;@/(.*)&quot;: &quot;&lt;rootDir&gt;/src/$1&quot;
    }
  }</code></pre>
<h4 id="2-프로젝트-root에-jestconfigjs-파일-생성-후-설정">2. 프로젝트 root에 jest.config.js 파일 생성 후 설정</h4>
<pre><code class="language-jsx">module.exports = {
  &quot;roots&quot;: [
    &quot;&lt;rootDir&gt;/&quot;
  ],
  &quot;transform&quot;: {
    &quot;^.+\\.tsx?$&quot;: &quot;ts-jest&quot;
  }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIW_220729]]></title>
            <link>https://velog.io/@raccoon-ccoder/TIW220729</link>
            <guid>https://velog.io/@raccoon-ccoder/TIW220729</guid>
            <pubDate>Thu, 28 Jul 2022 12:16:45 GMT</pubDate>
            <description><![CDATA[<h2 id="🦝-2022-07-29-금">🦝 2022-07-29 (금)</h2>
<h3 id="오늘-한-것">오늘 한 것</h3>
<ul>
<li>채팅 api 연동 및 데이터 차트 컴포넌트에 뿌려주는 작업 드디어 끝!! 피드백 받으면서 api에 대해 모호했던 것들을 확실히 이해할 수 있었다. </li>
</ul>
<br />


<h3 id="오늘-배운-것">오늘 배운 것</h3>
<h4 id="datetoisostring">Date.toISOString()</h4>
<p>자바스크립트 Date 객체를 ISOString(<code>yyyy-mm-ddThh:mm:ss</code>)형식의 문자열로 반환하는 방법
사용하는 api에는 time 데이터가 해당 형식으로 이뤄져 있는데 이상하게 현재 시간으로 조회해도 이전의 시간으로 변형되어 왜 그런건지 이해를 못해 개발을 하는데 좀 헤맸었다.
이유는 우리나라 Time Zone이 아니라 <code>UTC</code>타임존(zero offset)을 사용하기 때문이다.
UTC를 기준으로 Date 객체를 가공하고 싶다면 setHours()가 아닌 <code>setUTCHours()</code>를 사용해야 한다.</p>
<pre><code>const date = new Date();
console.log(date.toISOString()); // 2022-07-29T14:16:27.820Z
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[TIW_220728]]></title>
            <link>https://velog.io/@raccoon-ccoder/TIW220728</link>
            <guid>https://velog.io/@raccoon-ccoder/TIW220728</guid>
            <pubDate>Thu, 28 Jul 2022 12:12:49 GMT</pubDate>
            <description><![CDATA[<h2 id="🦝-2022-07-28-목">🦝 2022-07-28 (목)</h2>
<h3 id="오늘-한-것">오늘 한 것</h3>
<ul>
<li>차트 컴포넌트 2개와 백엔드 채팅 api 연동을 통한 데이터 화면에 뿌리는 작업을 진행했다. 사용할 api가 한정적이었고 사용할 차트에 어떤 데이터를 어떻게 보여주는 것이 좋을지 고민하는 시간이 많았던 것 같다. </li>
</ul>
<br />

<h3 id="오늘-배운-것">오늘 배운 것</h3>
<ul>
<li>VSCode의 gitLens라는 extension을 사용해봤는데 코드마다 히스토리가 남겨져서 굉장히 편리했었다.</li>
<li>한 컴포넌트 내에서 여러 api를 호출해야 했어서 react-query의 useQueries 메소드 사용을 했다. 타입스크립트로 작성을 했기에 리턴타입에 있어서 에러가 있었고 관련 설정 파일을 별도로 작성해야 했다. 이 부분은 따로 글을 작성할 예정이다.</li>
<li>useEffect + setInterval 사용시 setInterval에서 시간 보장 문제로 인해 useInterval이라는 커스텀 훅을 배웠다. 몰랐던 부분도 많아서 이것도 별도로 글을 작성할 예정이다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[React CRA 기반 프로젝트 proxy 설정]]></title>
            <link>https://velog.io/@raccoon-ccoder/React-CRA-%EA%B8%B0%EB%B0%98-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-proxy-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@raccoon-ccoder/React-CRA-%EA%B8%B0%EB%B0%98-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-proxy-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Tue, 26 Jul 2022 14:07:44 GMT</pubDate>
            <description><![CDATA[<p>현재 회사에서 진행중인 프로젝트는 CRA 기반으로 만들어졌기에 eject를 통해 직접 webpack을 건드리지 않고 다른 방법으로 proxy를 설정했으며 내가 해본 방법으로는 크게 2가지가 있다.</p>
<h3 id="1-packagejson에서-간단한-설정">1. package.json에서 간단한 설정</h3>
<p><a href="https://create-react-app.dev/docs/proxying-api-requests-in-development/">react-scripts 공식 문서</a>를 참고해 package.json에 간단하게 설정</p>
<pre><code class="language-jsx">// package.json
{
    &quot;proxy&quot;: &quot;http://localhost:5051&quot;
}</code></pre>
<br />

<h3 id="2-http-proxy-middleware-라이브러리-사용">2. http-proxy-middleware 라이브러리 사용</h3>
<p>해당 프로젝트에서는 websocket도 사용할 예정이었기에 다음과 같은 이유로 라이브러리를 선택하였다.</p>
<ul>
<li>websocket으로 서버와 통신시 protocol을 http가 아닌 ws를 사용</li>
<li>아래와 같이 proxy를 설정하면 websocket 요청들의 protocol이 http로 바뀐 상태로 서버에 전달되기에 websocket 사용 불가<pre><code class="language-jsx">ws://apiserver.com:9000/socketio?...</code></pre>
</li>
</ul>
<p>따라서 http-proxy-middleware 모듈로 해결하였으며 socket.io에 대해서는 다른 url을 사용해야 하므로 router라는 옵션을 활용했다.</p>
<pre><code class="language-jsx">// src/setupProxy.js
const { createProxyMiddleware } = require(&#39;http-proxy-middleware&#39;)
module.exports = app =&gt; {
  app.use(
    createProxyMiddleware(
      [&#39;/api&#39;, &#39;/socket.io&#39;],
      {
        target: &#39;https://도메인.com&#39;,
        changeOrigin: true,
        ws: true,
        router: {
          &#39;/socket.io&#39;: &#39;ws://nginx:80&#39;
        },
        pathRewrite: {
          &#39;^/api/&#39;: &#39;&#39;
        }
      }
    )
  )
}</code></pre>
<h3 id="참고-자료reference">참고 자료(Reference)</h3>
<p><a href="https://github.com/chimurai/http-proxy-middleware">http-proxy-middleware 라이브러리</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIW_220726]]></title>
            <link>https://velog.io/@raccoon-ccoder/TIW220726-kj948f4k</link>
            <guid>https://velog.io/@raccoon-ccoder/TIW220726-kj948f4k</guid>
            <pubDate>Tue, 26 Jul 2022 13:58:15 GMT</pubDate>
            <description><![CDATA[<h2 id="🦝-2022-07-26-화">🦝 2022-07-26 (화)</h2>
<h3 id="오늘-한-것">오늘 한 것</h3>
<ul>
<li>사용중인 Metronic template 폴더 구조와 동일하게 변경</li>
<li>CRA 기반 프로젝트 proxy 설정 및 회사 api 연동해서 라인차트에 데이터 렌더링</li>
</ul>
<br />


<h3 id="에러-해결">에러 해결</h3>
<h4 id="proxy-설정-후-url-변경되지-않는-문제">proxy 설정 후 url 변경되지 않는 문제</h4>
<ul>
<li>proxy 설정을 했음에도 불구하고 프로젝트를 실행하면 반영되지 않았다.</li>
<li>구글링으로 해결방법을 찾았으며 방법은 간단했지만 해결 후 bootstrap 관련 문제로 좀 골치아팠다.</li>
<li>결론적으로 무작정 폴더를 날리는 건 썩 좋은 방법이 아닌 것 같다...</li>
<li><a href="https://stackoverflow.com/questions/48291950/proxy-not-working-for-react-and-node">관련 링크</a></li>
</ul>
<p><strong>해결 방법</strong></p>
<pre><code>package.json에서 proxy 설정
react app 끄기
아래 명령어 차례로 입력

# rm -r package-lock.json
# rm -r node_modules
# npm install</code></pre><br />

<h4 id="proxy-설정-후-bootstrap-문제">proxy 설정 후 bootstrap 문제</h4>
<ul>
<li>위 방법으로 proxy 인식 안되는 문제 해결 후 프로젝트를 다시 돌리니 아래와 같은 오류를 만났다.<pre><code class="language-jsx">./src/_metronic/assets/sass/style.scss (./node_modules/css-loader/dist/cjs.js??ref--5-oneOf-6-1!./node_modules/postcss-loader/src??postcss!./node_modules/resolve-url-loader??ref--5-oneOf-6-3!./node_modules/sass-loader/dist/cjs.js??ref--5-oneOf-6-4!./src/_metronic/assets/sass/style.scss)
SassError: Undefined variable.
  ╷
142 │       values: $utilities-border-colors
  │               ^^^^^^^^^^^^^^^^^^^^^^^^
  ╵
node_modules\bootstrap\scss\_utilities.scss 142:15  @import
src\_metronic\assets\sass\_init.scss 19:9           @import
src\_metronic\assets\sass\style.scss 9:9            root stylesheet</code></pre>
<h4 id="해결-방법">해결 방법</h4>
</li>
<li>src/_metronic/assets/sass/_init.scss 파일에서 아래와 같이 설정 </li>
<li>&#39;scss/maps&#39;, &#39;scss/helpers&#39; 및 &#39;scss/utilities/api&#39; 파일을 가져오지 않아 생긴 문제 같다.</li>
<li><a href="https://github.com/twbs/bootstrap/issues/36785">관련 링크</a><pre><code class="language-jsx">// 변경전
// Bootstrap initializaton
@import &#39;~bootstrap/scss/functions&#39;;
@import &#39;~bootstrap/scss/variables&#39;;
@import &#39;~bootstrap/scss/mixins&#39;;
@import &#39;~bootstrap/scss/utilities&#39;;
</code></pre>
</li>
</ul>
<p>// 변경 후
// Bootstrap initializaton
@import &quot;<del>bootstrap/scss/functions&quot;;
@import &quot;</del>bootstrap/scss/variables&quot;;
@import &quot;<del>bootstrap/scss/maps&quot;;
@import &quot;</del>bootstrap/scss/mixins&quot;;
@import &quot;<del>bootstrap/scss/utilities&quot;;
@import &quot;</del>bootstrap/scss/helpers&quot;;
@import &quot;~bootstrap/scss/utilities/api&quot;;</p>
<pre><code></code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[TIW_220725]]></title>
            <link>https://velog.io/@raccoon-ccoder/TIW220725</link>
            <guid>https://velog.io/@raccoon-ccoder/TIW220725</guid>
            <pubDate>Sun, 24 Jul 2022 11:57:15 GMT</pubDate>
            <description><![CDATA[<h2 id="🦝-2022-07-25-월">🦝 2022-07-25 (월)</h2>
<h3 id="오늘-한-것">오늘 한 것</h3>
<ul>
<li>프로젝트에 연동할 api 명세서 정리</li>
<li>첫 코드 리뷰</li>
<li>kibana 교육 (사용법 및 kibana 기본 용어)</li>
</ul>
<h3 id="새로-알게-된-것">새로 알게 된 것</h3>
<ul>
<li>git graph, gitflow actions sidebar 사용</li>
<li>팀장님께서 알려주신 익스텐션인데 작업 단위로 flow를 볼 수 있어서 정말 편하다.</li>
</ul>
<p>gitflow actions sidebar의 경우 간략한 사용법은 다음과 같다.</p>
<ol>
<li>프로젝트 소스 제어 </li>
<li>하단 <code>gitflow action</code></li>
<li><code>feature:start</code> (내가 할 작업 브랜치 자동 생성) </li>
<li><code>feature:finish</code>  - 완전히 코딩이 끝나고 더이상 커밋할 작업이 없을 경우 사용 (브랜치 자동으로 main 머지 해줌)</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[querySelectorAll로 filter 메서드 사용하기]]></title>
            <link>https://velog.io/@raccoon-ccoder/querySelectorAll%EB%A1%9C-filter-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@raccoon-ccoder/querySelectorAll%EB%A1%9C-filter-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 24 Jul 2022 08:03:44 GMT</pubDate>
            <description><![CDATA[<p>리액트에서 테스트 코드를 작성하다가 알게 된 방법으로 엄청 기초인데 몰랐던 것을 반성하며 잊지 않기 위해 글을 작성한다.    </p>
<p><code>querySelectorAll</code>라는 Document 메소드는 <code>NodeList</code>를 반환하는데 <code>Array</code>타입이 아니라 filter, reduce와 같은 Array 타입의 메소드를 사용할 수 없다.
따라서 NodeList를 <code>Array.from()</code>를 사용해 Array로 변환하여 사용하면 된다. </p>
<h3 id="arrayfrom">Array.from()</h3>
<p>유사 배열 객체(array-like object)나 반복 가능한 객체(iterable object)를 얕은 복사로 진짜 array 생성해준다.</p>
<pre><code class="language-jsx">const arr = Array.from(container.querySelectorAll(&#39;[text-anchor=&quot;start&quot;]&#39;));
const res = arr.filter(e =&gt; !isNaN(Number(e.innerHTML)));</code></pre>
]]></description>
        </item>
    </channel>
</rss>