Jekyll2023-10-25T17:00:25+09:00https://sincerity.page/feed.xmlThe World As I See ItLee Chanhachan771788@gmail.comFitMate (Advanced) 프로젝트 정리 및 분석2023-09-20T00:00:00+09:002023-09-20T00:00:00+09:00https://sincerity.page/categories/Projects/fitmate/Projects-%082023_Summer_Project<h2 id="개요">개요</h2>
<ol>
<li>
<p>프로젝트 이름</p>
<p><strong>Fit Mate</strong>: 개인 맞춤형 운동을 위한 헬스케어 서비스</p>
</li>
<li>
<p>내용</p>
<p>기존의 FitMate에서 프론트 페이지 리뉴얼과 기능적 개선, 백엔드 서버의 비기능적 퍼포먼스 향상을 시도한 프로젝트</p>
</li>
<li>
<p>플랫폼</p>
<p>Web Application</p>
</li>
<li>
<p>제안 배경</p>
<p>a. 기존 프로젝트의 프론트 페이지 디자인 개선 필요<br />
b. 기존 프로젝트가 기능적인 구현에만 집중한 관계로 안정적인 배포환경을 조성하지 못했음<br />
c. JWT, Radis 등 다양한 기술들을 활용해보는 경험</p>
</li>
<li>
<p>기존 프로젝트 대비 주요 변경 사항</p>
<p>a. 팀 개편: 프론트엔드 2명, 백엔드 2명, 디자인 2명 (총 6명)<br />
b. 프론트 디자인 적용<br />
c. 프론트 페이지 다시 구현<br />
d. 체성분 변화 기록과 추천 기록 기능 축소<br />
e. 내 운동, 내 보조제로 플레이리스트 처럼 운동 루틴 관리할 수 있는 기능 추가<br />
f. 백엔드 서버 비기능적 요구사항에 대한 완성도 제고</p>
</li>
</ol>
<hr />
<p>구현한 코드는 다음 GitHub Page에 가면 확인할 수 있다.</p>
<p style="margin: 0">
<a href="https://github.com/Fitness-Mate" target="_blank">FitnessMate</a>
</p>
<hr />
<p><strong>진행 기간</strong></p>
<p>2023.07 ~ 2023.09</p>
<h2 id="소감">소감</h2>
<p>먼저 좋은 점부터… 디자인 팀이 있고, 프론트를 할 줄 아는 팀원이 있으니 확실히 결과물이 달라보였다. 지난번 프로젝트에서 디자인 회의는 정말 주먹구구 식이었지만, 이번 프로젝트의 디자인 회의는 figma라는 디자인 툴을 가지고 이부분은 픽셀이 어떻고 플로팅을 어떻게 하고, 호버 모션은 또 어떻고… 디자인이 뭔지, UX를 어떻게 고려해야 하는지 아는 팀원들이 많아서 아이디어를 내고 설득하는게 신났다. 그리고 그 과정에서 UI/UX 적인 부분에 대해서 많이 배운 것 같다. 프론트도 마찬가지다. 다소 복잡한 아이디어를 제안해도 그것을 어떻게 구현해야 하는지 경험이 있는 분이 계셔서 회의에서 아이디어 교환이 원활했던 것 같다. 백은 지난 프로젝트랑 동일하게 더할 나위 없었던 것 같다.</p>
<p>아쉬운 점으로는…
생각보다 진행이 더뎌서 아쉬웠다.<br />
이전 프로젝트는 맨땅에서부터 시작하는 거였지만, 이번 프로젝트는 실행가능한 레퍼런스가 프론트와 디자인에게 주어졌다.
여기에 더해 이전 프로젝트의 결과물을 시연하고 설명하고 질문 받고, 작성했던 보고서, ppt, api list 들을 새로 참가한 팀원들에게 모두 주었다. 디자인 측에서 기능들을 누락한 일이 몇번 있었다. 그 중 몇 개는 프로젝트 일정상 도저히 구현할 수 없게 되어버리는 것도 있었다. 결과물은 정말 훌륭했지만 그 과정에서 디자인이 확정되지 못하고 자꾸 변경되었기 때문에 프로젝트 일정이 연장되었다고 생각한다. 그리고 프로젝트 마감 예정기한 2주 전에 새로운 기능이 제안되었기 때문에 결국 총 3주가 연장되었다.<br />
처음 겪는 프로젝트 마감 연장이다. 왜 그랬을까? 난 뭘 했어야 했을까… 회의가 부족했던것 같기도 하다. 우리(프로젝트 원년 멤버인 지성과 나)는 프론트와 거의 실시간으로 소통하며 작업을 했다. 오류가 의심되거나 궁금한 점이 있다면 언제든지 카톡방에 질문을 남겼고, 30분 내에 원인 파악과 향후 어떻게 할 것인지에 대해 답변이 달렸다. API는 문서로 남겼고 지속적으로 업데이트 되었다. 그러나 백과 디자인은 아니었다. 가끔 figma 를 보고 디자인 오류에 대해 질문하는게 거의 다였고 일주일에 한번 있는 전체 회의에서 소통 다운 소통을 했다. 그런데 이게 이상한 것은 아니라고 생각한다. 디자인은 프론트와 원활하게 작업해야 한다고 생각한다. 디자인의 요구사항은 거의 프론트에서 구현이 필요한 것이었고, 백 또한 디자인에 기능적으로 어떻게 해달라 요구할 거리가 잘 없었다. 아무튼… 조금 디자인에 관심을 가졌어야 했었다.</p>
<h2 id="부하테스트">부하테스트</h2>
<h3 id="테스트-배경">테스트 배경</h3>
<p>FitMate 서버 애플리케이션의 개발은 두 번의 개발 프로젝트를 통해 개발되고 향상되었다. 2023.03부터 2023.7월까지 진행된 1차 프로젝트는 기능적인 요구사항을 충족하는 것을 주안점으로 하는 프로젝트였으며 성공적으로 개발을 완료한 뒤 본인 소유의 NAS 상에 도커를 활용하여 배포 환경 조성과 배포가 이루어졌다. 2023.07부터 2023.09까지 진행된 2차 프로젝트는 서비스의 UX/UI 리뉴얼에 맞추어 변경된 기능적 요구사항을 업데이트하고 여러 비기능적인 요구사항 충족을 통해 SW의 품질을 향상하는 것이 목표였다. 2차 프로젝트가 완료된 지금, 1차 프로젝트의 결과물인 단일 컨테이너로 구성되었던 서버와 새로 개발한 Kubernetes 분산 서버와의 성능 비교를 위해 본 테스트를 계획하였다.</p>
<h3 id="서버의-구조">서버의 구조</h3>
<p>프로젝트에서 개발한 서버의 아키텍처를 간단히 표현하면 다음과 같다.</p>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/first_project_architecture.png" />
<figcaption>첫 번째 프로젝트의 서버 아키텍쳐 도해 (단일 서버)</figcaption>
</figure>
</div>
<p>NAS에 MariaDB, Docker를 설치하고 개발된 서버를 배포하였다. 모든 서비스 백엔드의 구성요소가 온프레미스 방식으로 한대의 물리적인 NAS 머신에 집약된 구조이다.</p>
<p>2차 프로젝트에서 개발한 서버의 아키텍처는 다음과 같다.</p>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/second_project_architecture.png" />
<figcaption>두 번째 프로젝트의 서버 아키텍쳐 도해 (분산 서버)</figcaption>
</figure>
</div>
<p>1차와 달리 2차에서는 Kubernetes cluster 구축을 위해 여러 대의 노드 머신들이 필요했고, 이러한 요구사항은 NAS의 제한적인 하드웨어 자원으로는 구현이 불가능했다. 따라서 서비스의 기반을 AWS cloud로 이전한다는 결정을 내렸다. 2차 프로젝트에서는 이용자 메일 인증 기능이 추가되었는데 여기서 이용자 계정 생성 시 메일 인증을 위해 필요한 SMTP 서버는 Synology 사의 NAS 기반 SMTP 서비스를 활용하기 위해 NAS에 구축되었다. 하지만 서비스 내에서 FitMate 메인 백엔드 서버와 통신하며 인증 번호를 생성하고 이를 NAS SMTP에게 유저 주소로 전송을 명령하는 서버가 따로 필요하기 때문에 그러한 역할을 하는 서버를 클러스터 내에 따로 만들어 놓았다. 클러스터 내에 SMTP 서버는 트래픽이 많지 않을 것으로 예상하여 1개의 pod만 생성하도록 했고, 오토 스케일링이 적용되지 않는다. FitMate의 메인 백엔드 서버가 존재하는 pod의 경우 pod의 부하에 따라 2개에서 5개까지 스케일아웃될 수 있도록 오토 스케일링 규칙을 적용했다. 클러스터 외부에는 상술한 메일 인증 기능을 위한 SMTP 서비스가 존재하고, 메인 서버 pod에서 발급되는 JWT 토큰과 자주 사용되는 서비스 데이터를 보관하는 Radis 서버가 존재한다. 그 외에 서비스 데이터를 저장하는 AWS RDB, AWS S3 서비스가 존재한다.</p>
<h3 id="테스트-설계">테스트 설계</h3>
<h4 id="테스트-환경">테스트 환경</h4>
<ul>
<li>
<p>Kubernetes Cluster</p>
<ul>
<li>
<p>EC2 Node (1 Master Node, 2 Worker Nodes)</p>
<p>t3.small: 2 vCPU, 2 GiB memory, Ubuntu 22.04</p>
</li>
<li>
<p>Pod (2 ~ 4 Pods)</p>
<p>limits: 500m CPU, 512MiB memory<br />
requests: 400m CPU, 400Mib memory</p>
</li>
</ul>
</li>
<li>
<p>Single server A</p>
<p>t3.small: 2 vCPU, 2 GiB memory, Ubuntu 22.04</p>
</li>
<li>
<p>Single server B</p>
<p>t2.micro: 1 vCPU, 1GiB memory, Ubuntu 22.04</p>
</li>
</ul>
<h4 id="testing-tool">Testing Tool</h4>
<ul>
<li>
<p>Apache Jmeter 5.6.2</p>
<p>API 요청 생성 및 TPS 측정 도구</p>
</li>
<li>
<p>Kubernetes Metrics</p>
<p>Kubernetes Cluster 의 Node와 Pod 상태 관제</p>
</li>
<li>
<p>Docker Metrics</p>
<p>단일서버의 Container 상태 관제</p>
</li>
</ul>
<h4 id="테스트에-활용할-api">테스트에 활용할 API</h4>
<p>테스트에서는 각 서버의 퍼포먼스를 검증하기 위해 서비스의 3가지 API를 활용할 예정이다.</p>
<p>A. 단순히 데이터를 읽는 작업 API (Read)</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-> GET /user/private
</code></pre></div></div>
<p>B. JOIN이 많은 작업 API (Join)</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-> POST /supplements/search/list/{pageNum}
</code></pre></div></div>
<p>C. DB write 작업 API</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-> POST /myfit/routines/supplement/{supplementId}
</code></pre></div></div>
<p>각각의 API를 수만에서 수백 만회 실행하며 평균 TPS를 살펴보겠다.</p>
<h4 id="테스트-계획">테스트 계획</h4>
<ul>
<li>
<p>Test A</p>
<p>2개의 Pod가 동작 중인 Kubernetes Cluster와 단일서버 A를 대상으로 3가지 API를 실행한다. 이때 각 API를 지속해서 호출하는 500명의 유저(쓰레드)를 300초에 걸쳐 생성한다. 300초가 지나 유저가 모두 생성된 뒤를 기준으로 TPS를 관측한다.</p>
</li>
<li>
<p>Test B</p>
<p>2개의 Pod가 동작 중이고 Pod의 부하에 따라 4개까지 증가하는 Kubernetes Cluster와 단일서버 B를 대상으로 3가지 API를 실행한다. Test A와 동일하게 500명의 유저(쓰레드)를 300초에 걸쳐 생성한다. 300초가 지나 유저가 모두 생성된 뒤를 기준으로 TPS를 관측한다.</p>
</li>
</ul>
<h3 id="테스트-결과-분석">테스트 결과 분석</h3>
<h4 id="test-a-결과">Test A 결과</h4>
<p><strong>Single Server A vs Kubernetes Cluster (2 Pods)</strong></p>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/testA/Single_A_Read_TPS.png" />
<figcaption>1.1.1. Single A - Read - Transactions per Second<br />Avg TPS after 300 sec: <b>313.9</b> TPS</figcaption>
</figure>
</div>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/testA/Single_A_Join_TPS.png" />
<figcaption>1.1.2. Single A - Join - Transactions per Second<br />Avg TPS after 300 sec: <b>200.8</b> TPS</figcaption>
</figure>
</div>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/testA/Single_A_Write_TPS.png" />
<figcaption>1.1.3. Single A - Write - Transactions per Second<br />Avg TPS after 300 sec: <b>15.5</b> TPS</figcaption>
</figure>
</div>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/testA/Kube_2_Read_TPS.png" />
<figcaption>1.2.1. Kubernetes cluster (2 Pods) - Read - Transactions per Second<br />Avg TPS after 300 sec: <b>191.8</b> TPS</figcaption>
</figure>
</div>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/testA/Kube_2_Join_TPS.png" />
<figcaption>1.2.2. Kubernetes cluster (2 Pods) - Join - Transactions per Second<br />Avg TPS after 300 sec: <b>183.2</b> TPS</figcaption>
</figure>
</div>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/testA/Kube_2_Write_TPS.png" />
<figcaption>1.2.3. Kubernetes cluster (2 Pods) - Write - Transactions per Second<br />Avg TPS after 300 sec: <b>8.0</b> TPS</figcaption>
</figure>
</div>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/testA/Single_A_Idle_status.png" />
<figcaption>1.3.1. Single A - Idle - container status</figcaption>
</figure>
</div>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/testA/Single_A_Read_status.png" />
<figcaption>1.3.2. Single A - Read - container status</figcaption>
</figure>
</div>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/testA/Single_A_Join_status.png" />
<figcaption>1.3.3. Single A - Join - container status</figcaption>
</figure>
</div>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/testA/Single_A_Write_status.png" />
<figcaption>1.3.4. Single A - Write - container status</figcaption>
</figure>
</div>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/testA/Kube_2_Idle_status.png" />
<figcaption>1.3.5. Kubernetes cluster (2 Pods) - Idle - Cluster status<br />fitmate-deployment-.... 가 서버의 pod이다.</figcaption>
</figure>
</div>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/testA/Kube_2_Read_status.png" />
<figcaption>1.3.6. Kubernetes cluster (2 Pods) - Read - Cluster status</figcaption>
</figure>
</div>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/testA/Kube_2_Join_status.png" />
<figcaption>1.3.7. Kubernetes cluster (2 Pods) - Join - Cluster status</figcaption>
</figure>
</div>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/testA/Kube_2_Write_status.png" />
<figcaption>1.3.8. Kubernetes cluster (2 Pods) - Write - Cluster status</figcaption>
</figure>
</div>
<h4 id="test-a-결과-분석">Test A 결과 분석</h4>
<p><strong>Avg TPS after 300 sec</strong></p>
<table>
<thead>
<tr>
<th style="text-align: center"> </th>
<th style="text-align: center">Read</th>
<th style="text-align: center">Join</th>
<th style="text-align: center">Write</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center"><strong>single A</strong></td>
<td style="text-align: center">313.9 TPS</td>
<td style="text-align: center">200.8 TPS</td>
<td style="text-align: center">15.5 TPS</td>
</tr>
<tr>
<td style="text-align: center"><strong>Kubernetes Cluster(2 Pods)</strong></td>
<td style="text-align: center">191.8 TPS</td>
<td style="text-align: center">183.2 TPS</td>
<td style="text-align: center">8.0 TPS</td>
</tr>
</tbody>
</table>
<p><strong>(Single - Kube) / Single</strong></p>
<table>
<thead>
<tr>
<th style="text-align: center"> </th>
<th style="text-align: center">Read</th>
<th style="text-align: center">Join</th>
<th style="text-align: center">Write</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center"><strong>(Single - Kube) / Single</strong></td>
<td style="text-align: center"><span style="color:blue">-38.9%</span></td>
<td style="text-align: center"><span style="color:blue">-8.8%</span></td>
<td style="text-align: center"><span style="color:blue">-48.5%</span></td>
</tr>
</tbody>
</table>
<p>먼저 TPS 측정 결과를 보면 Kubernetes cluster가 서버(Pod)가 2개 병렬이기 때문에 더 높은 TPS를 보여줄 것으로 기대했지만, 실제 결과는 Single A 서버가 Kubernetes cluster 서버보다 모든 측정 항목에서 더 높은 TPS 퍼포먼스를 보인다. 이에 대해 2가지 원인이 예상된다.</p>
<ol>
<li>Kube cluster의 로드밸런서의 처리 한계로 인한 병목현상</li>
<li>서버 자체의 오버헤드로 인한 Pod 자원 부족</li>
</ol>
<p>첫 번째 로드밸런서는 우리 서비스가 AWS의 상용 로드밸런서를 사용하고 있었기 때문에 이정도의 부하를 견디지 못할 확률은 매우 낮아 보였다. 더군다나 이어서 있는 테스트에서 클러스터가 이것보다 나은 퍼포먼스를 보여줬기에 로드밸런서가 원인일 확률은 매우 낮다. 때문에 두 번째 원인인 Pod 내 자원 부족으로 인한 병목현상에 초점을 두고 서버를 모니터링해 보았다. 먼저 Idle 상태의 Single A 서버의 컨테이너를 보면 0.1%대의 매우 낮은 CPU 점유율과 42%의 높은 메모리 점유율을 보여준다. 테스트 시작 후 300초가 경과한 시점에서 CPU 점유율은 71%로 극적인 상승을 보이지만, 메모리 점유율은 거의 변동이 없다는 것을 확인할 수 있다. Kubernetes Cluster의 경우 Idle 상황이든 테스트 상황이든 메모리 사용률이 높지만 maximum 값에 달하지는 않는 반면, CPU의 경우 테스트 상황에서 100% 사용된다. 자원의 총량은 단일 서버 환경과 분산서버 환경이 같지만, 서버가 기본적으로 동작하기 위해 필요한 CPU 자원이 분산서버 Pod의 0.5 Core 자원 상한 환경에서 페널티로 작용했을 것이다. 이에 대해서는 후속적인 테스트를 통해 가설을 검증해 보겠다.</p>
<p>그 외에 Write 연산에서 두 서버 모두 시간 경과에 따라 퍼포먼스가 저하되는 것을 확인할 수 있다. 이는 DB 서버의 I/O가 병목의 원인으로 보인다. 이는 뒤에서 Test B의 결과와 함께 조금 더 자세히 분석해 보겠다.</p>
<h4 id="test-b-결과">Test B 결과</h4>
<p><strong>Single Server B vs Kubernetes Cluster (2~4 Pods)</strong></p>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/testB/Single_B_Read_TPS.png" />
<figcaption>2.1.1. Single B - Read - Transactions per Second<br />Avg TPS after 300 sec: <b>385.1</b> TPS</figcaption>
</figure>
</div>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/testB/Single_B_Join_TPS.png" />
<figcaption>2.1.2. Single B - Join - Transactions per Second<br />Avg TPS after 300 sec: <b>322.4</b> TPS</figcaption>
</figure>
</div>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/testB/Single_B_Write_TPS.png" />
<figcaption>2.1.3. Single B - Write - Transactions per Second<br />Avg TPS after 300 sec: <b>13.2</b> TPS</figcaption>
</figure>
</div>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/testB/Kube_24_Read_TPS.png" />
<figcaption>2.2.1. Kubernetes cluster (2~4 Pods) - Read - Transactions per Second<br />Avg TPS after 300 sec: <b>343.0</b> TPS</figcaption>
</figure>
</div>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/testB/Kube_24_Join_TPS.png" />
<figcaption>2.2.2. Kubernetes cluster (2~4 Pods) - Join - Transactions per Second<br />Avg TPS after 300 sec: <b>334.3</b> TPS</figcaption>
</figure>
</div>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/testB/Kube_24_Write_TPS.png" />
<figcaption>2.2.3. Kubernetes cluster (2~4 Pods) - Write - Transactions per Second<br />Avg TPS after 300 sec: <b>7.8</b> TPS</figcaption>
</figure>
</div>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/testB/Single_B_Idle_status.png" />
<figcaption>2.3.1. Single B - Idle - container status</figcaption>
</figure>
</div>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/testB/Single_B_Read_status.png" />
<figcaption>2.3.2. Single B - Read - container status</figcaption>
</figure>
</div>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/testB/Single_B_Join_status.png" />
<figcaption>2.3.3. Single B - Join - container status</figcaption>
</figure>
</div>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/testB/Single_B_Write_status.png" />
<figcaption>2.3.4. Single B - Write - container status</figcaption>
</figure>
</div>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/testB/Kube_24_Idle_status.png" />
<figcaption>2.3.5. Kubernetes cluster (2~4 Pods) - Idle - Cluster status<br />fitmate-deployment-.... 가 서버의 pod이다.</figcaption>
</figure>
</div>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/testB/Kube_24_Read_status.png" />
<figcaption>2.3.6. Kubernetes cluster (2~4 Pods) - Read - Cluster status</figcaption>
</figure>
</div>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/testB/Kube_24_Join_status.png" />
<figcaption>2.3.7. Kubernetes cluster (2~4 Pods) - Join - Cluster status</figcaption>
</figure>
</div>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/testB/Kube_24_Write_status.png" />
<figcaption>2.3.8. Kubernetes cluster (2~4 Pods) - Write - Cluster status</figcaption>
</figure>
</div>
<h4 id="test-b-결과-분석">Test B 결과 분석</h4>
<p><strong>Avg TPS after 300 sec</strong></p>
<table>
<thead>
<tr>
<th style="text-align: center"> </th>
<th style="text-align: center">Read</th>
<th style="text-align: center">Join</th>
<th style="text-align: center">Write</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center"><strong>single A</strong></td>
<td style="text-align: center">385.1 TPS</td>
<td style="text-align: center">322.4 TPS</td>
<td style="text-align: center">13.2 TPS</td>
</tr>
<tr>
<td style="text-align: center"><strong>Kubernetes Cluster(2~4 Pods)</strong></td>
<td style="text-align: center">343.0 TPS</td>
<td style="text-align: center">334.3 TPS</td>
<td style="text-align: center">7.8 TPS</td>
</tr>
</tbody>
</table>
<p><strong>(Single - Kube) / Single</strong></p>
<table>
<thead>
<tr>
<th style="text-align: center"> </th>
<th style="text-align: center">Read</th>
<th style="text-align: center">Join</th>
<th style="text-align: center">Write</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center"><strong>(Single - Kube) / Single</strong></td>
<td style="text-align: center"><span style="color:blue">-10.9%</span></td>
<td style="text-align: center"><span style="color:red">3.7%</span></td>
<td style="text-align: center"><span style="color:blue">-40.9%</span></td>
</tr>
</tbody>
</table>
<p>Test A와는 달리 영역별로 TPS 성능 우위가 다르다. 단순 DB read의 경우 Kubernetes Cluster가 Single B보다 10.9% 낮은 TPS를 보여주지만, Join 연산이 많은 경우 Kubernetes Cluster가 Single B에 비해 3.7%로 아주 근소하게 높은 TPS를 보여준다. Write는 Kubernetes Cluster가 Single B보다 40.9% 낮은 성능을 보인다.</p>
<p>이에 대해 좀 더 정확한 분석을 하려면 Test A의 결과와 비교해 보아야 한다. 먼저 Test B는 Test A와 비교해 자원(CPU, memory)의 총량이 2배인 서버 환경에서 진행되었다. 단일 서버에 대해 스케일 업(HW 성능 향상)이 있었고, 분산 서버에 대해서는 스케일 아웃(Auto scaling을 통한 Pod 증가)이 있었다. 이 때문에 두 서버 모두 퍼포먼스가 있을 것이라고 예상할 수 있겠지만, 다음 표에 나와 있듯 꼭 그렇지만은 않다.</p>
<p><strong>TPS difference between Test A and B</strong></p>
<table>
<thead>
<tr>
<th style="text-align: center"> </th>
<th style="text-align: center">Read</th>
<th style="text-align: center">Join</th>
<th style="text-align: center">Write</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center"><strong>single A, single B</strong></td>
<td style="text-align: center"><span style="color:red">22.7%</span></td>
<td style="text-align: center"><span style="color:red">60.6%</span></td>
<td style="text-align: center"><span style="color:blue">-14.8%</span></td>
</tr>
<tr>
<td style="text-align: center"><strong>Kube Cluster 2Pod, 2~4 Pod</strong></td>
<td style="text-align: center"><span style="color:red">78.8%</span></td>
<td style="text-align: center"><span style="color:red">82.3%</span></td>
<td style="text-align: center"><span style="color:blue">-2.5%</span></td>
</tr>
</tbody>
</table>
<p>Write에서 두 종류의 서버에서 모두 소폭의 퍼포먼스 하락이 있었고, Read와 Join에서 퍼포먼스 향상이 있었다.</p>
<p>Read의 경우 단일 서버에서 CPU 자원이 2배로 향상됐음에도 TPS 22.7% 향상되는 것에 그쳤다. 그런데도 status를 확인해 보면 CPU는 100% 모두 사용하고 있는 것을 확인할 수 있다. 예상되는 원인으로는 Read의 측정을 위해 사용하는 GET:/user/private API는 요청 헤더로 전달한 JWT 토큰에서 User ID를 해석하고 이 User ID를 이용해 DB에 저장된 User 정보를 읽어서 반환하는 과정으로 처리가 진행된다. 여기서 JWT 토큰으로부터 User ID를 해독하는 과정이 SHA-256 암호를 복호화하는 과정인데 SHA-256 복호화가 아무리 빠르다고 한들 CPU에 가해지는 부담을 무시할 수 있을 정도는 아닐 것이다. 아마 이 과정이 CPU의 활용률을 높이는 원인이지 않을까 생각한다.</p>
<p>Join의 경우 두 종류의 서버에서 상당한 퍼포먼스 향상이 있었다. 분산서버의 경우 수평 확장이 있었던 만큼 2배에 가까운 퍼포먼스 향상을 볼 수 있었다. 하지만 그럼에도 향상이 100%에 못 미치는 이유는 API 요청이 모든 Pod가 공용으로 사용하는 Ctrl Plane과 로드밸런서를 거치기 때문에 Pod의 증가가 온전히 퍼포먼스 증가로 연결되지 못했기 때문으로 보인다. 단일 서버의 경우 60.6%의 성능 향상이 있었다. 여기서 눈여겨볼 점은 바로 status이다. status의 CPU 사용률을 보면 163.5%라고 나타난다. CPU와 memory가 모두 여유가 있음에도 활용하지 못하는 이유는 DB 서버의 Join 연산 처리 한계로 병목현상이 발생했기 때문일 것이다.</p>
<p>마지막으로 Write 요청의 경우 Kubernetes Cluster의 퍼포먼스가 Single Server의 퍼포먼스보다 절반을 조금 넘는 것을 확인 할 수 있다. 자원 활용도를 본다면 Test A, B 모두에서 두 서버 모두 한계치에 가까운 CPU 자원을 활용하고 있지만 CPU 자원이 2배가 되었다고 퍼포먼스가 향상되지 않았고 오히려 약간 하락했다. 원인을 한참 고민해 보았다. Test에 사용한 Write API는 유저의 내 운동 목록에 운동을 하나 추가하는 API이다. 이를 처리하기 위해 서버 내부에선 굉장히 많은 동작이 이루어진다. 요청을 받은 서버는 먼저 요청에 포함된 JWT 토큰을 해독하여 User ID를 찾고 DB조회를 통해 User 정보를 읽어온다. User 정보는 유저의 운동목록 정보를 포함하고 있고, 여기서 첫 번째 운동 목록을 찾은 다음 비로소 해당 목록에 API로 전송한 운동을 추가하는 Write 하는 작업이 이루어진다. API는 단순히 DB를 작성하는 것 뿐만 아니라 그 과정에 조회와 Join이 상당히 많으며 이는 테스트 결과에 상당히 복합적인 영향을 미치었다.</p>
<p>그렇다면 이 실험 결과가 무의미한 것인가? 아니다. 우리의 실험 목적은 단순히 분산서버와 단일서버의 성능 비교가 아니라 우리가 새로 개발한 Kubernetes Cluster 서버와 기존 서버와의 성능 비교가 목적이다. 지금 얻을 테스트 결과를 통해 우리는 새로 개발한 서버의 DB Write 퍼포먼스가 기존 서버에 비해 낮다는 결론을 얻었고, 과정에서 알게 된 원인으로 지목되는 복잡한 인과관계들을 분석하여 향후 성능 개선을 하면 된다.</p>
<h3 id="추가-테스트">추가 테스트</h3>
<h4 id="추가-test-계획">추가 Test 계획</h4>
<p>처음 계획했던 2번의 Kubernetes Cluster의 Read 테스트에서 Pod에 CPU 자원을 충분히 할당하지 않아 병목 현상이 발생했다는 결론을 내렸었다. 이를 검증하기 위해 Pod에 할당되는 자원의 상한을 CPU 950m, memory 900 Mib로 각각 90%, 125% 증가시킨 뒤 2개의 Pod를 생성하고 오토스케일링 없이 Read의 퍼포먼스를 확인하겠다. 4개의 Pod를 생성해서도 테스트하고 싶지만, AWS 비용을 분담해야 하기 때문에 비싼 자원을 마음대로 쓰기가 어려웠다. 그래서 아쉽지만 2개 Pod로 타협을 보았다.</p>
<p>Test B의 Single B의 Join에서 서버의 CPU, memory 모두 충분히 활용되지 않았었다. 이를 두고 네트워크 또는 DB 서버의 처리 한계가 병목지점이라는 잠정적인 결론을 내렸다. 이를 확인하기 위해 Pod의 CPU 한도를 높게 설정한 Kubernetes클러스터에서 동일한 테스트를 진행하고 TPS가 증가의 증가 여부를 확인해 보겠다.</p>
<ul>
<li>
<p>Additional Test B</p>
<p>Write API 실험의 결과는 새로 개발한 서버의 퍼포먼스가 기존의 것보다 낮다는 것을 말해준다. 그 원인을 좀 더 세밀하게 분석하기 위해 복잡한 Join 과정 없이 정말 단순하게 DB Write만 진행하는 test API를 구현해 Single A, Single B, Kubernetes Cluster (CPU 950m, memory 900Mib, 2Pod)를 환경에서 테스트해 보겠다.</p>
</li>
</ul>
<h4 id="추가-test-결과">추가 Test 결과</h4>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/addtest/read_TPS.png" />
<figcaption>3.1.1. Kubernetes (CPU 950m, Mem 900Mib, 2Pod) - Read - TPS<br />Avg TPS after 300 sec: <b>358.3</b> TPS</figcaption>
</figure>
</div>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/addtest/join_TPS.png" />
<figcaption>3.1.2. Kubernetes (CPU 950m, Mem 900Mib, 2Pod) - Join - TPS<br />Avg TPS after 300 sec: <b>358.3</b> TPS</figcaption>
</figure>
</div>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/addtest/read.png" />
<figcaption>3.2.1. Kubernetes (CPU 950m, Mem 900Mib, 2Pod) - Read - cluster status</figcaption>
</figure>
</div>
<div class="post_img_block">
<figure style="width:80%; margin: 1em">
<img src="/assets/images/projects/fitness_mate/test/addtest/join.png" />
<figcaption>3.2.2. Kubernetes (CPU 950m, Mem 900Mib, 2Pod) - Join - cluster status</figcaption>
</figure>
</div>
<h4 id="추가-test-결과-분석">추가 Test 결과 분석</h4>
<table>
<thead>
<tr>
<th style="text-align: center"> </th>
<th style="text-align: center">Read</th>
<th style="text-align: center">Join</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center"><strong>Single A</strong></td>
<td style="text-align: center">313.9 TPS</td>
<td style="text-align: center">200.8 TPS</td>
</tr>
<tr>
<td style="text-align: center"><strong>Single B</strong></td>
<td style="text-align: center">385.1 TPS</td>
<td style="text-align: center">322.4 TPS</td>
</tr>
<tr>
<td style="text-align: center"><strong>Kube (CPU 500m, Mem 400Mib, 2 Pods)</strong></td>
<td style="text-align: center">191.8 TPS</td>
<td style="text-align: center">183.2 TPS</td>
</tr>
<tr>
<td style="text-align: center"><strong>Kube (CPU 500m, Mem 400Mib, 2~4 Pods)</strong></td>
<td style="text-align: center">343.0 TPS</td>
<td style="text-align: center">334.3 TPS</td>
</tr>
<tr>
<td style="text-align: center"><strong>Kube (CPU 900m, Mem 940Mib, 2 Pods)</strong></td>
<td style="text-align: center"><strong>358.3 TPS</strong></td>
<td style="text-align: center"><strong>384.4 TPS</strong></td>
</tr>
</tbody>
</table>
<p>Read에서 Kube 2~4 Pod에 비해 4.5% 성능 향상이 있었다. 자원 총량이 같은 Single B보다 여전히 성능이 뒤처진다. WAS의 기본적인 구동에 필요한 CPU 오버헤드를 낮춘 것에 대해 4.5% 성능 향상은 기대에는 못 미치는 수치지만, 향상은 확실해 보인다.</p>
<p>Join에서는 Kube 2~4 Pod에 비해 15.0% 성능 향상이 있었다. DB의 처리 한계가 병목지점이 맞았다면 Kube 2~4 Pod 또는 Single B와 비슷한 퍼포먼스를 보였어야 한다. DB 서버의 처리 한계가 아닌 것일까? 명확한 결론은 알 수 없었다.</p>
<h3 id="정리">정리</h3>
<p>Kubernetes 상에 CPU 500m의 자원 제한으로 WAS의 숫자를 늘리는 것 보다 900m의 제한으로 좀 더 충분하게 CPU 자원을 주어 환경을 구축하는 것이 좋다는 결론을 얻었다. 또한 약 1,700m의 CPU 제한을 넘어서면 WAS가 병목이 아니게 된다는 것을 알 수 있었다.</p>
<p>처음 성능 테스트를 계획했을 때 이렇게 복잡해질 줄은 몰랐다. 그냥 모놀리식 단일 서버보다 MSA 분산 서버가 좋을 것이라는 잘못된 생각을 가지고 테스트를 설계했고, 결과로 나오는 모든 표지들이 한 방향을 가리킬 것으로 생각했다. 하지만 결론은 뒤죽박죽이었고 원인은 사방에 흩어져 있었다. 확실한 사전 준비 부족이었다. 아쉬웠던 점을 나열해 보자면</p>
<p>1.많은 양의 데이터를 획득했지만, 경험과 지식이 없어 이를 어떻게 조합하여 유의미한 결론에 접근하기 어려웠다.</p>
<ol>
<li>초반 설계를 잘못하여 잘못된 테스트를 하며 허비된 시간이 너무 많았다. 시간은 시간대로 낭비되고, 정성껏 모은 데이터를 휴지통에 넣을 때 상당히 속이 쓰리었다.</li>
<li>더 좋은 인스턴스를 대여하여 테스트를 하고 싶었지만, RDB Burst Token 소진, 트래픽에 대한 요금 등 예상하지 못했던 변수들이 있어 시도하지 못했던 것도 큰 아쉬움으로 남는다.</li>
</ol>
<p>이번 부하 테스트는 성공보다는 성장통에 가까운 것 같다. 보고서에 기록하진 않았지만, 결론에 닿지 못한 수많은 테스트들이 있었고, 그 속엔 불완전하지만 유의미한 배움이 있었다. 당장은 취업에 집중해야 하지만, 취업 시즌이 끝나고 서버 아키텍처에 대해 좀 더 공부한 뒤 경험을 살려 좀 더 나은 부하 테스트를 해봐야겠다.</p>Lee Chanhachan771788@gmail.comChatGPT를 활용한 운동, 보조제 추천 서비스 (후속 프로젝트)FitMate2023-06-19T00:00:00+09:002023-06-19T00:00:00+09:00https://sincerity.page/categories/Projects/Projects-Graduation_Project<h2 id="개요">개요</h2>
<ol>
<li>
<p>프로젝트 이름</p>
<p><strong>Fit Mate</strong>: 개인 맞춤형 운동을 위한 헬스케어 서비스</p>
</li>
<li>
<p>내용</p>
<p>Open AI의 Chat GPT API를 사용하여 사용자의 체성분 데이터와 요구사항을 바탕으로 DB에 있는 운동 및 보조제 정보를 탐색, 맞춤형 운동, 보조제를 추천해주는 웹서비스.</p>
</li>
<li>
<p>플랫폼</p>
<p>Web Application</p>
</li>
<li>
<p>제안 배경</p>
<p>a. 최신 기술을 반영한 풀스텍 웹어플리케이션 제작
b. 헬스, 홈트레이닝, 필라테스 등 자기관리에 대한 관심 증가
c. 사용자의 상황에 따른 맞춤형 운동 추천 서비스의 부재</p>
</li>
<li>
<p>제안 목적 및 주요 기능</p>
<p>a. 개인 맞춤형 운동 및 보조제 추천 기능 제공
b. 사용자의 체성분 데이터 변화량에 따른 시각 자료 제공
c. 운동 방법, 영상 등의 운동 기본 정보 제공
d. 보조제 가격, 성분 등의 보조제 기본 정보 제공</p>
</li>
</ol>
<hr />
<p>구현한 코드는 다음 GitHub Page에 가면 확인할 수 있다.</p>
<p style="margin: 0">
<a href="https://github.com/Fit-Mate" target="_blank">FitMate</a>
</p>
<hr />
<p><strong>진행 기간</strong></p>
<p>2023.03.02 ~ 2023.06.07</p>
<h2 id="소감">소감</h2>
<p>본 프로젝트는 본인 외에 2명의 같은 학과 학생과 진행한 졸업 프로젝트로 지금까지 진행했던 프로젝트 중 가장 스케일이 큰 프로젝트였다.</p>
<p>처음 2주 가량은 프로젝트 주제를 두고 많은 고민과 토의가 있었다. 지도 교수가 데이터베이스 전문이었기 때문에 데이터베이스 성능 개선을 우선적으로 염두해두고 세부적인 내용을 논의했지만 성능 개선을 시도하기 위해선 DBMS 전반에 대해 깊고 넓게 알아야 했다. 다들 의욕이 충만했기 때문에 “지금부터 열심히 알아가면 되지!”, “지도교수가 잘 가이드해줄거야!” 로 각자 분야를 정해서 발표식으로 DBMS 스터디를 시작했지만, 지도교수와의 회의 끝에 지금 시작하기엔 시간이 너무 적다는 결론이 나와 웹 서비스를 제작하는 것으로 프로젝트의 방향을 돌렸다.</p>
<p>주제를 정하는 회의에서 최근 화두가 되는 Chat GPT를 사용하는 것이 시의성이 있을 것 같으며, 세명 모두 운동에 관심이 있었기 때문에 내가 제시한 FitMate 에 대한 초기 아이디어가 긍정적인 반응을 얻었다.</p>
<p>수차례 회의를 통해 FitMate의 기능적인 요구사항들, ChatGPT API에 대한 검증을 거쳐 아이디어가 어느정도 윤곽이 잡혔다. 기능적인 내용 외에 구체적으로 구현을 어떻게 할 것인가에 관련된 내용이 논의되기 시작했고, 여기서 구현해야할 내용이 크게 React를 활용한 Web Front-end, Spring을 활용한 Web Back-end 두가지로 나뉘게 되었다. 역할 분담을 해야했는데 세명 모두 Backend 분야기 때문에 약간 곤란한 상황이 되었다. 역할 배분을 논의 하기 위해 과업에 대해 조금 더 명확히 하는 회의가 진행되었고, Front 를 진행하는 사람을 문서작업에서 배려해주기로 협의했다. 팀원 중 유일하게 React 사용 경험이 있었기에 “내가 해야 하나?”를 고민하던 와중에 팀원 중 한명이 자원했다.
나와 다른 팀원은 Front를 맡기로 한 팀원에게 감사를 표했다.</p>
<p>역할 분담은 다음과 같다. (실명은 언급하지 않겠다.)<br />
Front: 팀원 A
Back: 나, 팀원 B</p>
<p>Back-end 서버를 구현하기 전에 API 명세서를 먼저 정의하고 구현에 들어가는게 Front의 혼란이 적겠다고 생각해서 Back-end를 맡은 나와 B는 1주에 걸쳐서 API 명세서를 노션으로 제작했으며 그 과정에서 조금 더 서비스에 대해 구체적인 사항들을 정할 수 있었다. 이 시기에 A는 React에 대한 강의를 들으며 활용할 기술에 대해 학습했다.</p>
<p>API 명세서가 완성되고 A에게 공유하며 우리들이 설계한 서비스의 구체적인 모습들에 대해 공유하는 시간을 가졌고, 추가적으로 서비스에 대한 통일된 이해를 바탕으로 나와 B는 E-R Diagram, Class Diagram 만들었고, A는 와이어프레임을 만들어 서로 공유하고 설명하며 서비스에 대한 이해를 굳혔다.</p>
<p>다음 확정된 Diagram들을 바탕으로 데이터베이스를 설계하고, Spring application 설계와 구현을 진행했다. 외부 API(Chat GPT, DeepL)를 애플리케이션 내에서 활용하기 위해 API 활용에 대한 테스트를 각자 하나씩 맡아서 진행했다. 나는 Chat GPT에 대한 테스트를 맡았는데 fine-tunning 을 할지 여부를 판단하기 위해 직접 fine-tunning을 시도해보았지만, 고작 십수개의 학습 데이터를 넣는 것으로는 원하는 결과를 얻을 수 없었다. 이 과정을 통해 fine-tunning에 엄청난 양의 데이터와 고도로 숙련된 전문가가 필요하다는것을 느끼며, fine-tunning은 포기했다. 결론적으로 Chat GPT의 수많은 모델 중 비용과 입력 가능 토근수를 고려하여 gpt-3.5-turbo 모델을 사용하기로 결정하고, 그것을 활용하는 기능을 Spring application 내에 구현했다.
request 전송부터 Chat GPT의 응답이 도착하기까지 약 40초 가량의 시간이 소요되었기 때문에 이를 따로 멀티 쓰레드 작업으로 분리하였고, client 에게는 Chat GPT의 응답을 기다리는 동안 다른 작업을 할 수 있도록 요청이 생성되어 진행중임을 알리고, 생성된 요청을 열람할 수 있는 ID를 response 를 보내는 식으로 연결을 40초간 기다리는 일을 방지했다.</p>
<p>B는 번역을 담당하는 DeepL API를 테스트하는 것을 맡았으며 DeepL이 아직 한국에 정식 출시하지 않았기 떄문에 API 키를 받을 수 없다는것을 발견한다. B는 Rapid API라는 API 우회 사용 서비스를 활용해 DeepL API를 간접적으로 활용할 수 있는 키를 확보하고 이를 활용해 DeepL의 기능을 활용할 수 있도록 하는 기능을 Spring application 내에 구현했다.</p>
<p>나와 B는 많은 시도 끝에 각자 맡은 보충제, 운동 추천에 대한 영문 질의 템플릿 또한 만들었고, 이를 Chat GPT request를 보내고, response를 받아 DeepL을 이용해 번역하는 기능을 구현완료했다.</p>
<p>가장 서비스에 핵심이 되는 부분은 이렇게 끝났고 그 외에 API들을 프론트와 주 3회 이상의 회의를 가지며 구현해 나갔다.</p>
<p>이후에는 프론트와 백엔드를 통합하는 테스트를 진행 한 뒤 내 소유의 NAS에 배포환경을 만들어 배포했다.</p>Lee Chanhachan771788@gmail.comChatGPT를 활용한 운동, 보조제 추천 서비스타협2023-06-10T00:00:00+09:002023-06-10T00:00:00+09:00https://sincerity.page/random/daydream/DayDream-Reconciliation<h1 id="들어가기-전에">들어가기 전에</h1>
<p>작년 5월에 비슷한 고민을 했었다.<br />
그때 당시엔 나름 만족스러운 답을 얻었는데 한편으로는 고민에서 도출해 낸 답이 단지 미봉책임과 이 고민을 다시 마주칠 것임을 알고 있었다.<br />
그리고 한 달 전에 이 고민을 다시 마주했을 때 올 것이 왔구나라는 생각이 들었다. 다시 칼을 들고 머릿속을 헤집어야 할 시간이다.</p>
<p>이 글은 <a href="https://sincerity.page/random/daydream/DayDream-overcoming_existential_dilemma_4/">실존적 딜레마 극복을 위한 노력 - 4</a> 의 후속이다.</p>
<h1 id="허무">허무</h1>
<p>나는 과거에 “영속성을 잃은 나는 도대체 무엇을 향해 살아야 하는가?”라는 질문에 대해 “내 존재 동안 내가 향유할 수 있는 진한 존재의 경험을 위해 살겠다.”는 답변을 만들어 공허와 회의주의를 가까스로 상대해 왔다. 그렇게 공허함을 일정 받아들이고, 배고픈 소크라테스를 선택했다.</p>
<p>최근 궁극의 본질이 무엇인가에 대해 “모든 현상은 단순히 물리법칙(우리가 아직 발견하지 못한 것을 포함한)의 역사이며, 궁극의 본질은 물리법칙이다.”라는 결론을 도출했다. 섬뜩한 칼날 같은 결론이었다. 그래도 크게 와 닿지는 않았다. 점점 내가 찾은 궁극의 본질이라는 것이 어떤 의미를 내포하는지 알게 되면서 거대한 허무를 다시 마주했다.</p>
<ol>
<li>
<p>자유의지의 부재</p>
<p>“모든 현상은 물리적인 법칙을 철저하게 준수한다. 따라서 우주의 어떤 상태 A에 대해 다음 상태 A’는 결정되어 있다.” 자유의지가 개입할 여지가 없다. 자유의지는 착각이다.</p>
</li>
<li>
<p>객관적 가치(value)의 부재</p>
<p>절대적이고, 초월적인 것은 없다는 말과 비슷하다. 동물적인 본능에 의해 느끼는 원초적인 가치든, 사회적으로 형성되고 협의가 된 가치든 모든 객관적 가치의 근원은 인간의 주관이다.</p>
</li>
<li>
<p>의식의 부재</p>
<p>나의 존재함을 느끼는 내 의식은 신경세포들이 만들어 낸 허상의 나이다.</p>
</li>
<li>
<p>나의 경계 부재</p>
<p>나라고 할 수 있는 것은 내 육체이다. 그런데 내 뇌에 전극을 집어넣어 신경세포처럼 행동하며 신경세포에 자극을 주며 내 의식에 영향을 준다면, 그 전극을 조종하는 장치와 내 신경세포들의 합이 내가 될 것이다.</p>
</li>
</ol>
<p>그 외에도 수많은 허무가 있다.</p>
<h1 id="타협">타협</h1>
<p>“진실은 중요하지만, 사랑 없는 진실은 견디기 힘들다”라는 영화<두 교황>의 문장처럼 내가 찾은 “자유의지는 허상이며 결국 모두 물리법칙의 역사다.”는 결론은 견디기 힘들었다. “물리법칙의 역사”일 뿐인 세상에 특별한 의미를 부여하는 것은 매트릭스에서 파란 약을 먹는 것 같았다. 그런데 더 이상 버틸 수 없다. 배부른 돼지가 되기보단 배고픈 소크라테스가 되겠다는 신념을 꺾고 내 한계를 인정해야겠다.</p>
<p>내가 가진 장난감이 마음에 안 들지만 그렇다고 내가 뭘 할 수 있는가? 칭얼대며 울고만 있지는 않겠다.<br />
나의 인지는 인간 육신에 종속되어 있다. 인간은 무의미한 감각적 자극에 열광한다.<br />
나의 인간성과 그로 인한 한계를 인정하고 수용하겠다.<br />
무가치함과 공허함의 파도는 적당히 외면하며 그때그때 추스르겠다. 내 행복을 위해.</p>
<p>나는 과연 이 안에서 평안을 찾을 수 있을까.</p>Lee Chanhachan771788@gmail.comreconciliation - 실존적 딜레마 극복 5부애자일 프랙티스2023-06-08T00:00:00+09:002023-06-08T00:00:00+09:00https://sincerity.page/random/book/Books-Practices_of_an_Agile_Developer<h2 id="애자일-프랙티스">애자일 프랙티스</h2>
<p><strong>도서 이름:</strong> 애자일 프랙티스<br />
<strong>지은이:</strong> 밴캣 수브라마니암<br />
<strong>출판사:</strong> 인사이트</p>
<h2 id="내용">내용</h2>
<p>애자일 개발방법론으로 프로젝트를 하는 조직에서 일하며 느낀점과 애자일 개발 방법을 현실에서 실행하기 위한 노하우 등을 담은 책이다.</p>
<h2 id="감상">감상</h2>
<h3 id="인상깊은-구절">인상깊은 구절</h3>
<p><strong>p.55</strong></p>
<p class="text-center"><strong><em>“하늘이 무너질 것 같은 위기 상황인데 팀의 다른 사람들은 동의하지 않는다면, 여러분이 옳다는 근거를 충분히 설명하지 못했다고 생각해라.”</em></strong></p>
<p class="text-center"><strong><em>“하늘이 무너질 것 같은 위기 상황인데 팀의 다른 사람들은 동의하지 않는다면, 그 사람들 말이 맞다고 생각해라.”</em></strong></p>
<p>좋은 마인드다. 문제의 원인을 자신에서 찾고, 상대방의 경험과 집단의 의사를 존중하는 것.<br />
<em>상사와 의견 갈등이 있을 경우 당신은 어떻게 할 것인가?</em> 라는 면접 단골 질문이 있다. 이런 면접 질문에 대한 답변을 하는데도 응용할 수 있을 것 같다.</p>
<p><strong>상사와 의견 갈등이 있을 경우 당신은 어떻게 할 것인가?</strong><br />
<strong>-></strong> 설득이 통하지 않는다면, 내가 옳다는 근거를 충분히 설명하지 못했다고 생각하여 정량적인 자료를 더 보충하며, 팀원들과 좀 더 상의하겠다.</p>
<p><strong>그래도 안 통한다면?</strong><br />
<strong>-></strong> 앞선 과정에서 팀원들 또한 같은 문제를 인식한다면, 팀원들과 함께 상사를 설득하겠다. 만약 팀원들이 크게 공감하는 반응을 보이지 않았다면, 상사의 의견이 맞다고 생각하겠다.</p>
<p class="text-center"><strong><em>‘여러분의 용기있는 입장이, 상황을 이해할만한 배경지식이 부족한 의사 결정권자들의 저항에 부딪친다면, … 그들이 이해할 만한 용어로 바꿔 발표할 필요가 있다. ‘더 깔끔한 코드’만으로는 비즈니스를 하는 사람들에게 동기부여를 하지 못한다. 투자 수익이 좋고, 소송을 피하고, 고객 저변을 확대할 수 있다고 말하는 편이 훨씬 나은 주장이다.’</em></strong></p>
<p>좋은 주장이다. 작업에 대해 설득이 필요하다면 그들에게 이 작업이 어떤 결과를 불러오는지 그들의 언어로 설득한다.</p>
<hr />Lee Chanhachan771788@gmail.comPractices of an Agile Developer - 밴캣 수브라마니암, 엔디 헌트자유의지2023-03-31T00:00:00+09:002023-03-31T00:00:00+09:00https://sincerity.page/random/book/Books-Free_Will<h2 id="자유의지">자유의지</h2>
<p>자유의지, Mark Balaguer, 한울</p>
<h2 id="읽기-전">읽기 전</h2>
<p>나는 자유의지는 없다고 생각한다.</p>
<p>우주의 모든 현상은 그것을 이루는 법칙을 벗어나지 않는다.</p>
<p>우주를 이루는 법칙이 설령 변화무쌍하더라도 그 변화무쌍함 마져 그 법칙의 속성 중 하나라고 보고 그 법칙의 일부로 편입시킨다면 만물은 우주의 어떤 법칙을 준수한다.</p>
<p>어떤 상태 A가 있고 A에서 그것을 이루는 법칙(F)을 준수하는 시간상 가장 가까운 다음 상태 F(A)이 있다고 하자</p>
<p>이 A상태에서 다음 상태 F(A)가 되는 것이 정해져있고, 연달아서 F(F(A)), F(F(F(A))) … 등 모든 후속 상태들이 정해져있다.</p>
<p>어떤 계 내에서 임의의 시점의 상태는 계의 탄생 시점부터 정해져있다.</p>
<p>따라서 우리들이 스스로 무언가를 결정하고 이미 결정되지 않은 것들을 결정한다는 자유의지는 우리의 인지적 착오이다.</p>
<p>라고 생각한다.</p>
<p>반박을 듣기 위해 책을 읽는다.</p>
<h2 id="감상">감상</h2>
<p>아주 아주 아주 마음에 드는 책이다. 방해되지 않을 정도의 재치가 있는 예시를 들어 개념을 설명하고,</p>
<p>논리적으로 굉장히 꼼꼼히 주장을 이어나간다.</p>
<p>혼자 고민했던 생각들이 정리되어지고, 미심쩍었던 부분들이 썩은 부분으로 판명되어 도려내어진다.</p>
<p>이걸 모두가 읽고 토론을 할 수 있다니 너무 좋다.</p>
<h2 id="자유의지에-대한-내-생각">자유의지에 대한 내 생각</h2>
<p>가끔 책에서 논리를 전개하는데 있어 우리의 의식을 쪼개어 지지 않는 하나의 큰 덩어리로 본다는 느낌이 든다.(5장 갈린 결정 파트, 7장 무의식 파트 등)<br />
마치 그 덩어리 의식 자체가 자연적인 단위인 것처럼 우리의 결정은 무의식이 전체를 관장하거나 의식이 전체를 관장하지 않고<br />
의식의 뒷편에 항항 무의식이 줄을 당기고 풀며 영향력을 행사한다. 어떤 것이든 무의식의 영향으로부터 완전히 자유로울 수 없다<br />
그러기에 “완전히 내가 내린, 그리고 (무의식을 포함해)그것을 하도록 만든 것이 아무것도 없다”는 글쓴이가 옹호하는 자유의지는 존재하지 않는다.</p>
<p>그러나 의식은 쪼개어 질 수 있는 작은 기전들의 집합이다.<br />
의식의 크기가 작은 사람부터 큰 사람들, 사람을 넘어서는 작은 종 부터 큰 종에 이르기 까지 스펙트럼으로 분포한다.<br />
스펙트럼의 어느 지점에 npd 자유의지(non predetermined free will)가 주어지는가를 결정하는 것이 가능한가?<br />
어느 밴드에만 npd 자유의지가 존재한다고 한다면, 우주에서 의식을 가진 특수한 존재들만 결정되지 않은 것을 결정할 특권을 주는것으로 이어진다.<br />
우리를 둘러 싼 우주의 법칙의 적용에 있어서 특수성을 부정하기에 (특수한 대상에게만 따로 적용되는 법칙이 있다는 것을 부정하기에)<br />
npd자유의지의 존재에 대한 문제는 사실 인간에게 국한된 문제라기보단 생명체 전체로 확장되어야 하며,<br />
큰 의식에서부터 작은 의식까지 그리고 심지어는 의식이 없는 지능이라는 것이 없는 무생물에게까지 결정되지 않은 것을 결정할 수 있는 권한이 있어야 하지만, 의식이 없는데 그게 가능할리가…</p>
<p>결정되지 않은 것이 있다는 가정이 npd의 기반인데 이는 양자이론에 기반을 둔다.<br />
양자역학은 확률로 현상을 표현하고, 그렇기에 결정된 것이 없다고 하지만<br />
나는 양자역학 아래에 확률장난을 멈출 수 있게 하는 우리가 아직 모르는 어떤 것이 있다라고 생각한다.<br />
그리고 양자역학이 자유의지의 존재를 가능케한다라고 하려면 양자역학에 의한 확률적인 상태를 우리의 자유의지에 의해 결정될 수 있어야 한다.<br />
심령술같은 유사과학으로 들린다.</p>
<p>결론적으로 <br />
나는 결정론을 믿으며 흄이 주창했던 “결정론과 양립 가능한 자유의지”만 존재한다고 생각한다.</p>
<h2 id="선정된-주제">선정된 주제</h2>
<ul>
<li>
<p>토론</p>
<p>(NPD)자유의지는 없다
찬반랜덤</p>
</li>
<li>
<p>토의</p>
<p>왜 자유의지를 받아들여야 할까?</p>
<ol>
<li>결정론은 참인가?</li>
<li>결정론과 자유의지는 양립가능한가?</li>
<li>자유의지는 도덕적 책임의 필요조건인가?</li>
<li>자유의지는 우리(인간)에게 이로운가?</li>
<li>자유의지에서 비물리적인 영혼 (믿음, 욕구)에 대한 추가 의견</li>
<li>자유의 의지가 일각의 주장처럼 환상이라면, 인간은 다른동물들과 무엇으로 구분되는가?</li>
</ol>
</li>
</ul>Lee Chanhachan771788@gmail.com자유의지 - 마크 발라규어좋아하는 시 3개2023-03-13T00:00:00+09:002023-03-13T00:00:00+09:00https://sincerity.page/random/daydream/DayDream-Three_poems<p>좋아하는 시 3개를 소개합니다.</p>
<hr />
<ol>
<li>
<p>내 그대를 생각함은 항상 그대가 앉아 있는 배경에서 해가 지고 바람이 부는 일처럼 사소한 일일 것이나 언젠가 그대가 한없이 괴로움 속을 헤매일 때에 오랫동안 전해오던 그 사소함으로 그대를 불러보리라.</p>
</li>
<li>
<p>진실로 진실로 내가 그대를 사랑하는 까닭은 내 나의 사랑을 한없이 잇닿은 그 기다림으로 바꾸어 버린 데 있었다. 밤이 들면서 골짜기엔 눈이 퍼붓기 시작했다. 내 사랑도 어디쯤에선 반드시 그칠 것을 믿는다. 다만 그때 내 기다림의 자세를 생각하는 것뿐이다. 그 동안에 눈이 그치고 꽃이 피어나고 낙엽이 떨어지고 또 눈이 퍼붓고 할 것을 믿는다.</p>
</li>
</ol>
<ul>
<li>황동규 ‘즐거운 편지’</li>
</ul>
<hr />
<p>인생을 꼭 이해할 필요는 없다<br />
인생은 축제와 같은 것<br />
하루하루를 일어나는 그대로 맞이하라<br />
길을 걷는 아이가 바람이 불 때마다<br />
꽃잎들의 선물을 받아들이듯<br /></p>
<p>아이는 꽃잎을 모아 간직하는 일에는<br />
관심이 없다<br />
머리카락에 행복하게 머문 꽃잎들을<br />
가볍게 떼어 내고<br />
아름다운 젊은 시절을 맞이하며<br />
새로운 꽃잎으로 손을 내밀 뿐<br /></p>
<ul>
<li>라이너 마리아 릴케 ‘인생’ (류시화 옮김)</li>
</ul>
<hr />
<p>언제나 이타카를 마음에 두라<br />
너의 목표는 그곳에 이르는 것이니<br />
그러나 서두르지는 마라<br />
비록 네 갈 길이 오래더라도<br />
늙고 나서야 그 섬에 이르는 것이 더 나으니<br />
길 위에서 너는 이미 풍요로워졌으니<br />
이타카가 너를 더 풍요롭게 해주기를 기대하지 마라.</p>
<p>이타카는 아름다운 모험을 선사했고<br />
이타카가 없었다면 네 여정은 시작되지도 않았으리니<br />
이제 이타카는 너에게 줄 것이 하나도 없다.<br /></p>
<ul>
<li>콘스탄티노스 카바피 ‘이타카’ 中 일부</li>
</ul>
<hr />Lee Chanhachan771788@gmail.com황동규 '즐거운 편지', 라이너 마리아 릴케 '인생', 콘스탄티노스 카바피 '이타카'프랑켄슈타인2023-02-22T00:00:00+09:002023-02-22T00:00:00+09:00https://sincerity.page/random/book/Books-Frankenstein<h2 id="프랑켄슈타인">프랑켄슈타인</h2>
<p>프랑켄슈타인, 메리 셸리, 더 클래식</p>
<h2 id="감상">감상</h2>
<ul>
<li>
<p>2권의 9장까지 읽었다.</p>
<p>영화 <패신저스>가 떠오른다. <br />
프랑켄슈타인이 자신의 피조물에게 ‘나와 같은 여자를 만들어 달라’ 라는 요구를 받는 장면이다.</p>
<p>홀로 감내하기 어려운 고통이 도사리는 세상에 자신과 같은 처지의 여자를 창조해서 서로 동정하며 살게 해달라는 요구.<br />
그리하면 영원히 인간들의 앞에 나타나지 않겠다는 약속.</p>
<p>프랑켄슈타인은 피조물의 제안을 받아들인다.</p>
</li>
<li>
<p>끝까지 읽었다.</p>
<p>특별하게 감상은 남지 않는다. 공상 속 대상에게 도덕적 잣대를 들이대기도 적절하지 않고…<br />
마지막에 있는 작품 해설에 <프랑켄슈타인>이 페미니즘적인 도서라고 하는데, 여성 작가가 집필한 것이라 그럴 수 있겠군… 정도?</p>
</li>
</ul>
<h2 id="발제">발제</h2>
<ol>
<li>
<p>피조물은 창조주에게 자신의 행복을 요구할 권리가 있는가? (토론)</p>
</li>
<li>
<p>창조주의 책임, 피조물의 의무, 부모의 책임, 자식의 도리… 등 근본적인 의무와 권리들은 당연한 것인가? (토의)</p>
<ul>
<li>인간의 기본권은 본질적으로 당연한 것인가?<br /></li>
<li>법과 제도 도덕은 무엇에 기반하고 그것들이 지키려고 하는 것은 과연 무엇인가?</li>
</ul>
</li>
</ol>
<h2 id="선정된-주제">선정된 주제</h2>
<ol>
<li>
<p>피조물은 창조주에게 자신의 행복을 요구할 권리가 있는가? (토론)</p>
</li>
<li>
<p>생명 창조는 비윤리적인가? (토의)</p>
</li>
</ol>Lee Chanhachan771788@gmail.com프랑켄슈타인 - 메리 셸리 독서기록메아리 없는 외침2022-11-25T00:00:00+09:002022-11-25T00:00:00+09:00https://sincerity.page/random/daydream/DayDream-Fear_of_Empty_Echo<h2 id="메아리-없는-외침">메아리 없는 외침</h2>
<p>처음으로 자유기고 페이지에 글을 적습니다.</p>
<p>지금까지 쓴 건 뭐냐구요?</p>
<p>그것들은 모두 어딘가에 적고, 자유기고 페이지에는 옮겨 넣기만 한 글입니다.</p>
<p>제목이 참 저답지 않은 것 같습니다. 아마 내용도 그럴 거에요.</p>
<p>한 달 전에 끔찍한 악몽을 꾸었습니다.</p>
<p><em>“네 말이 너무 현학적이고 빙빙 돌아서 이해가 가지 않는다.”</em></p>
<p>꿈에서 들은 그 예상치 못한 말은 머릿속 한구석에 자리 잡습니다.</p>
<p>저의 공포는 바로</p>
<p>나의 말과 글에 개성이 과하게 묻어 세상에 닿지 않는 것입니다.</p>
<p>듣는 것이 지루한 말.</p>
<p>읽는 것이 피곤한 글.</p>
<p>메아리 없는 외침.</p>
<p>반짝이는 것을 쫓아 생각의 토끼굴에 들어가는 것도 좋지만</p>
<p>무엇보다 당신들과 편안하게 이야기할 수 있는 사람이고 싶습니다.</p>
<p>근데 이젠 별로 걱정하지 않습니다.</p>
<p>그냥 걱정이 스르르 사라졌어요.</p>
<p>관심 있는 글이면 보겠죠 아니면 뒤로가기를 누르거나요.</p>
<p>무엇보다…</p>
<p>당신들은 내 글이 어떻든, 그 너머의 진심엔 악의가 없음을 알잖아요.</p>Lee Chanhachan771788@gmail.comFear of Empty Echo궁극의 본질2022-11-16T00:00:00+09:002022-11-16T00:00:00+09:00https://sincerity.page/random/daydream/DayDream-Ultimate_Essence<h2 id="궁극의-본질">궁극의 본질</h2>
<p><틀을 깨는 생각에 대한 단상>에서 본질을 두고 이렇게 말한 적 있다.</p>
<p><em>본질에 가까운 지식일수록 모호해지고, 입체적인 모습을 갖기에 …</em><br />
이를 두고 독서 동아리 내에서 반박 의견이 나왔다.<br />
본질에 가까울수록 오히려 단순명료해진다는 의견이었다. 예시로 물리법칙이 제시되었다.</p>
<p>본질이라는 단어에 어떤 것의 핵심적인 것, 즉 현상보다 지식의 부피가 적다는 것이 내재하여 있다.<br />
여기까지는 반박이 주장하는 내용과 나의 주장이 같다.<br />
그러나 반박은 “명료해진다” 나는 “모호해진다”라고 표현한 부분에서 대립이 발생하는데<br />
처음엔 물리법칙 또한 듣는 이를 이해시키기 어렵고, E = mc^2라고 단순하게 표현할 수 있지만, 그 기호들이 내포하고 있는 의미가 절대 가볍지 않다<br />
라는 생각이 들어 반박할 수 있어 보였다.</p>
<p>그러나 곰곰이 생각해보니 모호해지는 것은 충분히 본질에 도달하지 않았기 때문이 아닌가? 라는 생각으로 이어졌다.<br />
어쩌면 궁극의 본질은 물리법칙이 아닐까?</p>
<p>나의 사고, 우리의 진화 과정, 거대한 천체의 운동까지 외부의 의지가 개입되지 않았다.<br />
남은 것은 물리법칙뿐, 모든 현상은 그로부터 떨어져 나온 각질이거나 그것에 근간을 두고,<br />
동시에 그 일부이다.</p>
<p>모든 현상은 물리법칙의 역사이다.</p>
<p>반박 의견을 내어준 물리학과 친구에게 고마웠다.</p>
<h2 id="단순하고-치명적인">단순하고 치명적인</h2>
<p><strong>모든 현상은 물리법칙의 역사이다</strong></p>
<p>모든 행위의 의미를 거두어갈 차가운 칼날의 느낌이 든다.</p>Lee Chanhachan771788@gmail.comultimate essence밑바닥부터 시작하는 딥러닝2022-11-01T00:00:00+09:002022-11-01T00:00:00+09:00https://sincerity.page/categories/AI/AI-Deep_Learning_from_Scratch<h2 id="개요">개요</h2>
<p>도서명 : 밑바닥부터 시작하는 딥러닝<br />
저자 : 사이토 고키<br />
출판사 : 한빛미디어</p>
<p>바닥부터 시작하는 강화학습과 같은 경로로 읽게 되었다.<br />
지능의 본질과 구현은 흥미에 의해 읽게된 책이지만, 이 책은 실질적 필요에 의해 읽게 되었다.<br />
좋아하는 분야라 그런지 기대가 된다.</p>
<p>그런데 2일동안 책의 90%를 읽어야 한다.<br />
팀플 팀원들과 그렇게 약속을 했는데 과연 우리들이 짧은 시간에 기술 서적을 모두 숙지는 아니더라도 안다고 할 수 있을 정도로 빠르지만, 깊게 읽을 수 있을까?</p>
<p>시간이 촉박해서 메모를 하기 어려울 것 같아 아쉽다.</p>
<h2 id="메모">메모</h2>Lee Chanhachan771788@gmail.com교수님 권장도서