<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>calm0_0.log</title>
        <link>https://velog.io/</link>
        <description>공부한 내용들을 정리하는 블로그</description>
        <lastBuildDate>Sat, 16 Dec 2023 14:51:14 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>calm0_0.log</title>
            <url>https://velog.velcdn.com/images/calm0_0/profile/78641104-282b-49ea-a2d9-ffebfdb50443/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. calm0_0.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/calm0_0" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[OS 스터디를 마치며]]></title>
            <link>https://velog.io/@calm0_0/OS-%EC%8A%A4%ED%84%B0%EB%94%94%EB%A5%BC-%EB%A7%88%EC%B9%98%EB%A9%B0</link>
            <guid>https://velog.io/@calm0_0/OS-%EC%8A%A4%ED%84%B0%EB%94%94%EB%A5%BC-%EB%A7%88%EC%B9%98%EB%A9%B0</guid>
            <pubDate>Sat, 16 Dec 2023 14:51:14 GMT</pubDate>
            <description><![CDATA[<p>평소 CS 중에 가장 약한 부분이 운영체제였다. </p>
<p>그러다가 운영체제 관련 스터디를 발견하게 되어 참여하게 되었다. </p>
<p>스터디는 5주 동안 진행되었고</p>
<p>주차마다 정해진 범위 안의 질문들에 대해 공부하고 스터디 모임 시간에 서로 질문하고 답하면서 스터디를 진행했다.</p>
<p>같이 스터디를 통해 공부하다보니 서로 부족한 부분을 피드백해 줄 수 있어서 좋았다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[OS] 운영체제 정리 (5)]]></title>
            <link>https://velog.io/@calm0_0/OS-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%EC%A0%95%EB%A6%AC-5</link>
            <guid>https://velog.io/@calm0_0/OS-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%EC%A0%95%EB%A6%AC-5</guid>
            <pubDate>Wed, 06 Dec 2023 14:47:50 GMT</pubDate>
            <description><![CDATA[<h2 id="연속-메모리-할당">연속 메모리 할당</h2>
<hr>
<h3 id="스와핑swapping">스와핑(Swapping)</h3>
<hr>
<p>메모리에서 사용되지 않는 일부 프로세스를 보조기억장치로 내보내고, 그렇게 해서 생긴 빈 공간에 다른 프로세스를 적재하여 실행하는 방식을 스와핑(swapping) 이라고 합니다.</p>
<p>이때 프로세스들이 내보내지는 보조기억장치의 일부 영역을 스왑 영역(swap space) 이라고 합니다.</p>
<p>현재 실행되지 않는 프로세스가 메모리에서 스왑 영역으로 옮겨지는 것을 스왑 아웃(swap-out), 반대로 스왑 영역에 있던 프로세스를 다시 메모리로 옮겨오는 것을 스왑 인(swap-in) 이라고 합니다.</p>
<p>스와핑을 이용하면 프로세스들이 필요한 메모리 공간의 크기가 실제 메모리 공간의 크기보다 큰 경우에도 프로세스들을 동시 실행할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Docker] Dockerfile 사용법]]></title>
            <link>https://velog.io/@calm0_0/Docker-Dockerfile-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@calm0_0/Docker-Dockerfile-%EC%82%AC%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Fri, 01 Dec 2023 02:49:08 GMT</pubDate>
            <description><![CDATA[<h2 id="dockerfile">Dockerfile</h2>
<hr>
<h3 id="dockerfile-이란">Dockerfile 이란</h3>
<p>Dockerfile 이란 Docker 이미지를 만들게 해주는 파일이다.</p>
<h3 id="dockerfile-예시">Dockerfile 예시</h3>
<p>Node.js의 Express.js 프로젝트를 Docker 이미지로 만들기 위해 다음과 같이 Dockerfile을 작성하였다.</p>
<pre><code class="language-Docker">FROM node:latest

WORKDIR /usr/src/app

COPY package*.json ./

RUN npm install

COPY . .

EXPOSE 3000
CMD [ &quot;node&quot;, &quot;app.js&quot; ]</code></pre>
<h3 id="각-명령문의-의미">각 명령문의 의미</h3>
<h4 id="from-베이스-이미지-생성">FROM: 베이스 이미지 생성</h4>
<p>컨테이너에서 사용할 초기 이미지를 지정한다. 이 베이스 이미지를 기반으로 컨테이너에 추가적인 세팅을 할 수 있다. </p>
<pre><code class="language-Dcoker">FROM [이미지명]:[태크명]

FROM node:alpine
FROM ubuntu  </code></pre>
<p>태그명을 적지 않으면 latest 버전을 사용한다.</p>
<h4 id="workdir-작업-디렉토리-지정">WORKDIR: 작업 디렉토리 지정</h4>
<p>컨테이너의 작업 디레토리로 전환하기 위해 사용한다.</p>
<pre><code class="language-Docker">WORK [작업 디렉토리의 절대 경로]

WORKDIR /usr/src/app</code></pre>
<p>WORKDIR 실행 이후에 실행되는 명령문들은 지정한 디렉토리를 기준으로 실행된다. </p>
<h4 id="copy-파일-복사">COPY: 파일 복사</h4>
<pre><code class="language-Docker">COPY [복사할 파일 경로] [컨테이너에서 파일이 위치할 경로]

COPY package*.json ./</code></pre>
<p>Dockerfile이 위치한 디렉토리를 기준으로 복사할 파일을 지정하여 컨테이너 내부의 해당 경로에 파일을 복사한다.</p>
<h4 id="run-명령문-실행">RUN: 명령문 실행</h4>
<p>이미지 생성 과정에서 실행하는 명령어이다.</p>
<p>의존성, 패키지 설치와 같은 명령어를 RUN을 이용해 실행한다.</p>
<pre><code class="language-Docker">RUN [명령문]

RUN npm install</code></pre>
<h4 id="cmd-컨테이너-시작-시-실행되는-명령어">CMD: 컨테이너 시작 시 실행되는 명령어</h4>
<p>CMD는 컨테이너가 시작할 때마다 실행할 명령어를 설정한다.</p>
<p>Dockerfile에서 한 번만 사용할 수 있다. (여러 개를 사용한다면 마지막 명령만 적용)</p>
<pre><code class="language-Docker">CMD [&quot;&lt;파라미터1&gt;&quot;, &quot;&lt;파라미터2&gt;&quot;]
CMD &lt;전체 커맨드&gt;

CMD [ &quot;node&quot;, &quot;app.js&quot; ]
CMD node app.js</code></pre>
<h4 id="expose">EXPOSE</h4>
<p>컨테이너 내부에서 사용 중인 포트를 문서화하는 역할이다.</p>
<p>컨테이너에서 사용할 포트를 지정하는 것은 아니다. 포트를 열어두려면 docker run -p 옵션을 사용해야 한다.</p>
<pre><code class="language-Docker">EXPOSE [포트 번호]

EXPOSE 3000</code></pre>
<h3 id="dockerfile-읽어보기">Dockerfile 읽어보기</h3>
<p>이제 아까 보았던 예제가 어떻게 구성되어 있는지 확인해보자.</p>
<pre><code class="language-Docker">FROM node:latest  # node.js latest 버전을 베이스 이미지로 생성한다

WORKDIR /usr/src/app  # 컨테이너의 작업 디렉토리를 /usr/src/app 설정하고

COPY package*.json ./  # package*.json 을 작업 디렉토리의 현재 경로로 복사하여

RUN npm install  # npm install 명령어로 패키지를 설치한다

COPY . .  # Dockerfile이 위치한 경로의 파일들을 작업 디렉토리의 현재 경로에 복사한다

EXPOSE 3000  # 컨테이너 내부에서 3000번 포트를 활용할 것을 문서화

CMD [ &quot;node&quot;, &quot;app.js&quot; ]  # 컨테이너 시작 시 node app.js 명령어를 실행한다.</code></pre>
<h3 id="빌드-및-실행">빌드 및 실행</h3>
<p>작성된 Dockerfile 은 다음과 같이 빌드하고 실행할 수 있다.</p>
<pre><code class="language-bash">$ docker build -t hello-server ./  # 현재 경로의 Dockerfile을 빌드
$ docker image ls  # 이미지 생성 확인

$ docker run -d -p 3000:3000 hello-server  # 생성한 이미지를 컨테이너로 실행
$ docker ps  # 컨테이너 실행 확인</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Magic Number 처리하기]]></title>
            <link>https://velog.io/@calm0_0/Magic-Number-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@calm0_0/Magic-Number-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 30 Nov 2023 19:35:02 GMT</pubDate>
            <description><![CDATA[<h3 id="하드코딩이란">하드코딩이란</h3>
<p>소스 코드에 데이터를 직접 입력해서 저장하는 것을 말한다.</p>
<p>파일 경로, URL, IP 주소, 비밀번호 등이 하드코딩의 대상이 될 수 있다.</p>
<pre><code class="language-typescript">// 파일 경로를 직접 넣어준 경우
const file: File = new File(&quot;C:\documents\test.txt&quot;);

// 의미 있는 특정한 숫자(4)를 그대로 사용한 경우
pulbic move: void(randomNumber: number) {
  if (randomNumber &gt; 4) {
      this.position++;
  }
}
</code></pre>
<br>

<h3 id="매직-넘버magic-number">매직 넘버(Magic Number)</h3>
<p>매직 넘버(Magic Number)는 하드 코딩된 일정한 값을 의미하는 숫자나 문자열 등을 의미한다.</p>
<h3 id="매직-넘버의-문제점">매직 넘버의 문제점</h3>
<h4 id="코드의-가독성이-떨어진다">코드의 가독성이 떨어진다.</h4>
<p>만약, 하드코딩된 숫자를 작성하지 않은 사람이 접한다면 무엇을 의미하는지 어떤 규칙이 적용되고 있는지 파악하기 어려워진다.</p>
<h4 id="유지-보수하기가-어려워진다">유지 보수하기가 어려워진다.</h4>
<p>다음은 12개월 할부만 가능한 신용카드의 할부 계산 코드이다.</p>
<pre><code class="language-typescript">static calculateMonthlyPrincipal(principal: number) {
  return principal / 2;
}

static calculateMonthlyInterest(principal: number) {
  return principal * 0.1 / 12;
}</code></pre>
<p>요구사항으로 할부가 10개월만 가능하게 변경된다면?</p>
<p>할부 개월 수를 의미하는 12를 모두 찾아내어 하나하나 10으로 수정하는 작업을 해야만 한다.</p>
<p>이처럼 하드코딩을 하게 되면 유지 보수하기 어려운 코드가 된다.</p>
<br>

<h3 id="매직-넘버-처리하기">매직 넘버 처리하기</h3>
<h4 id="상수-활용">상수 활용</h4>
<pre><code class="language-typescript">const INSTALLMENT_MONTHS = 12;
const INTEREST_RATE = 0.1;

static calculateMonthlyPrincipal(principal: number) {
  return principal / INSTALLMENT_MONTHS;
}

static calculateMonthlyInterest(principal: number) {
  return principal * INTEREST_RATE / INSTALLMENT_MONTHS;
}</code></pre>
<p>이전 예제를 다음과 같이 변경했다.</p>
<p>의미가 불분명한 매직 넘버를 상수로 선언하여 의미를 파악하기가 좀 더 쉬워졌다. </p>
<p>또한 아까처럼 할부 개월 수가 바뀌는 요구사항에도 상수 값만 변경하면 된다. 즉, 변화에 대응하기 더 쉬워지고 유지 보수하기 좋은 코드가 된다. </p>
<h4 id="enum">enum</h4>
<p>매직 넘버를 피하는 방법으로는 연관된 상수를 enum으로 관리할 수도 있다.</p>
<p>다음은 프로젝트에서 네이버, 구글 등 인증 유형을 다음과 같이 enum 타입으로 두고 사용한 예시이다.</p>
<pre><code class="language-typescript">export enum UserProvider {
  GOOGLE = &#39;GOOGLE&#39;,
  NAVER = &#39;NAVER&#39;,
  LOCAL = &#39;LOCAL&#39;,
}</code></pre>
<br>

<h3 id="마치며">마치며</h3>
<p>코드를 작성하면서 하면서 종종 놓치는 부분인 것 같다. 코드를 작성하는 나의 입장에서는 너무 당연해서 넘어가거나 깜빡하여 하드 코딩된 부분들이 남아있는 경우가 있었다.</p>
<p>앞으로는 이런 부분들을 고민하면서 하드코딩을 줄이고 더 깔끔한 코드를 작성해야겠다..</p>
<br>
<br>

<p><strong>Reference</strong>
<a href="https://tecoble.techcourse.co.kr/post/2020-05-07-avoid-hard-coding/">https://tecoble.techcourse.co.kr/post/2020-05-07-avoid-hard-coding/</a>
<a href="https://dev-dmsgk.tistory.com/30">https://dev-dmsgk.tistory.com/30</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[OS] 운영체제 정리 (4)]]></title>
            <link>https://velog.io/@calm0_0/OS-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%EC%A0%95%EB%A6%AC-4</link>
            <guid>https://velog.io/@calm0_0/OS-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%EC%A0%95%EB%A6%AC-4</guid>
            <pubDate>Wed, 29 Nov 2023 14:20:23 GMT</pubDate>
            <description><![CDATA[<ul>
<li>병행성(동시성)에대해 설명해주세요.</li>
<li>병렬성에 대해 설명해주세요.</li>
</ul>
<h2 id="프로세스-동기화">프로세스 동기화</h2>
<hr>
<p>동시다발적으로 실행되는 많은 프로세스는 서로 데이터를 주고 받으며 협력하며 실행될 수 있습니다. 협력하며 실행되는 프로세스들은 실행 순서와 자원의 일관성을 보장해야 하기에 반드시 동기화(synchronization)되어야 합니다.</p>
<p>동기화(synchronization): 작업들 사이의 수행 시기를 맞추는 것</p>
<h3 id="프로세스-동기화의-의미">프로세스 동기화의 의미</h3>
<p>프로세스 동기화란 특정 자원에 접근할 때 한 개의 프로세스만 접근하게 하거나, 프로세스를 올바른 순서대로 실행하게 하는 것을 의미합니다.</p>
<p>동기화에는 실행 순서 제어를 위한 동기화, 상호 배제를 위한 동기화가 있습니다.</p>
<h3 id="실행-순서-제어를-위한-동기화">실행 순서 제어를 위한 동기화</h3>
<h3 id="상호-배제를-위한-동기화">상호 배제를 위한 동기화</h3>
<ul>
<li>Critical Section에 대해 설명해주세요.</li>
<li>Race Condition이 무엇인가요?</li>
<li>Race Condition을 어떻게 해결할 수 있나요?</li>
<li>Mutual Exclusion에 대해 설명해주세요.</li>
<li>Mutual Exclusion을 할 수 있는 방법은?</li>
</ul>
<br>

<h2 id="동기화-기법">동기화 기법</h2>
<hr>
<h3 id="뮤텍스">뮤텍스</h3>
<p>뮤텍스는 임계 구역을 잠금으로써 프로세스 간 상호 배제(한 프로세스가 임계 구역에 진입했을 때 다른 프로세스가 진입하지 못함)를 달성한다.</p>
<p>예를 들어, 하나의 탈의실에 사람들(프로세스)이 줄을 서고 이용하는 상황</p>
<p>밖에서 탈의실에 사람이 있는지 확인하려면?</p>
<p>일단 탈의실을 열어 보고 자물쇠가 걸려 있다면 안에 사람이 있다고 판단하고 기다린다.</p>
<p>자물쇠가 걸려 있지 않다면 탈의실을 이용한다.</p>
<p>이 자물쇠 기능을 코드로 구현한 것이 &#39;뮤텍스 락&#39;이다.</p>
<p>자물쇠 역할: 프로세스들이 공유하는 전역 변수 lock
임계 구역을 잠그는 역할: acquire(), 프로세스가 임계 구역에 진입하기 전에 호출한다. 임계 구역이 잠겨 있다면 임계 구역이 열릴 때 까지 임계 구역을 반복적으로 확인하고, 열려 있다면 임계 구역을 잠그는 함수이다.
임계 구역의 잠금을 해제하는 역할: release(), 임계 구역에서의 작업이 끝나고 호출하는 함수이다. 잠금을 풀어준다.
프로세스는 다음과 같이 임계 구역을 보호한다.</p>
<p>락을 획득할 수 없다면 기다린다. (반복적으로 확인한다.)
락을 획득할 수 있다면 임계 구역을 잠근 뒤 임계 구역에서의 작업을 진행한다.
임계 구역에서 나올 때는 임계 구역의 잠금을 해제한다.</p>
<h3 id="세마포어">세마포어</h3>
<p>세마포어는 공유 자원이 여러 개 있는 임계 구역 문제도 해결할 수 있는 동기화 도구이다.</p>
<p>탈의실이 여러 개 있는 상황.</p>
<p>공유 자원이 여러 개 있을 경우 여러 개의 프로세스가 각각 공유 자원에 접근이 가능해야 한다.</p>
<p>세마포어는 다음과 같이 구현된다.</p>
<p>임계 구역에 진입할 수 있는 프로세스의 개수(사용 가능한 공유 자원의 수)를 나타내는 전역 변수 S
wait(): 임계 구역에 진입할 수 있는 프로세스 개수가 0개 이하라면, 사용할 수 있는 자원이 있는지 반복적으로 확인하고, 진입 가능한 프로세스 개수가 하나 이상이면 S를 1 감소시키고 임계 구역으로 진입한다.
signal(): 임계 구역에서의 작업을 마친 뒤 S를 1 증가시킨다.
S = 2, 프로세스 A, B, C</p>
<p>프로세스 A가 wait() 호출. S = 2 이므로, 1 감소 시키고 임계 구역 진입
프로세스 B가 wait() 호출. S = 1 이므로, 1 감소 시키고 임계 구역 진입
프로세스 C가 wate() 호출. S = 0 이므로, 무한히 반복하며 S 확인
프로세스 A의 임계 구역 작업 종료. signal() 호출. S를 1 증가
프로세스 C가 S = 1 임을 확인. S를 1 감소 시키고 임계 구역 진입</p>
<ul>
<li><p>뮤텍스(Mutex)와 이진 세마포어의 차이에 대해 설명해주세요.</p>
</li>
<li><p>모니터에 대해 설명해주세요.</p>
</li>
</ul>
<br>

<ul>
<li><p>데드락이 무엇인가요?</p>
</li>
<li><p>데드락 발생 조건 4가지를 설명해 주세요.</p>
</li>
<li><p>데드락 회피 방법은 무엇이 있나요?</p>
</li>
</ul>
<br>
<br>

<p><strong>Reference</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[OS] 운영체제 정리 (3)]]></title>
            <link>https://velog.io/@calm0_0/OS-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%EC%A0%95%EB%A6%AC-3</link>
            <guid>https://velog.io/@calm0_0/OS-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%EC%A0%95%EB%A6%AC-3</guid>
            <pubDate>Wed, 22 Nov 2023 14:55:26 GMT</pubDate>
            <description><![CDATA[<h2 id="cpu-스케줄링cpu-scheduling이란">CPU 스케줄링(CPU scheduling)이란</h2>
<hr>
<p>CPU 스케줄링이란 운영체제가 <strong>프로세스</strong>들에게 효율적으로 <strong>CPU 자원을 배분</strong>하는 것을 말합니다. </p>
<p>즉, 준비 큐에 있는 프로세스들 중 어떤 프로세스에게 CPU를 할당할지 결정하는 것입니다.</p>
<h3 id="스케줄러의-종류">스케줄러의 종류</h3>
<ul>
<li>단기 스케줄러(CPU 스케줄러): 준비 상태의 프로세스 중에서 실행할 프로세스를 선택하여 CPU를 할당합니다.</li>
<li>중기 스케줄러: 너무 많은 프로세스에게 메모리를 할당해 시스템의 성능이 저하되는 경우에 대비해 메모리에 적재된 프로세스의 수를 동적으로 조절합니다. </li>
<li>장기 스케줄러(작업 스케줄러): 어떤 프로세스를 준비 큐에 삽입할지를 결정하는 역할을 합니다.</li>
</ul>
<h3 id="선점형--비선점형-스케줄링">선점형 / 비선점형 스케줄링</h3>
<p>CPU 스케줄링은 크게 선점형 스케줄링(preemptive scheduling)과 비선점형 스케줄링(non-preemptive scheduling)으로 구분할 수 있습니다.</p>
<p>선점형 스케줄링이란 프로세스가 CPU를 비롯한 자원을 사용하고 있더라도 운영체제가 프로세스로부터 <strong>강제로 자원을 빼앗아 다른 프로세스에 할당</strong>할 수 있는 방식을 말합니다.</p>
<p>반면에, 비선점형 스케줄링이란 하나의 프로세스가 자원을 사용하고 있다면 해당 프로세스가 종료되거나 스스로 대기 상태에 진입하기 전까지 <strong>다른 프로세스가 끼어들 수 없는</strong> 스케줄링 방식을 의미합니다.</p>
<h4 id="장점과-단점">장점과 단점</h4>
<p>선점형 스케줄링은 더 급한 프로세스가 언제든 끼어들어 사용할 수 있어 어느 한 프로세스의 <strong>자원 독점을 막고</strong> 프로세스들에 <strong>골고루 자원을 배분</strong>할 수 있다는 장점이 있지만, 문맥 교환 과정에서 <strong>오버헤드</strong>가 발생할 수 있습니다.</p>
<p>비선점형 스케줄링은 문맥 교환 횟수가 적어 이로 인한 <strong>오버헤드는 선점형 스케줄링보다 적지만</strong>, 하나의 프로세스가 자원을 사용 중이라면 당장 자원을 사용해야 하는 프로세스가 있어도 기다려야 합니다. 즉, 모든 프로세스가 <strong>골고루 자원을 사용할 수 없다</strong>는 단점이 있습니다.</p>
<br>

<h2 id="cpu-스케줄링-알고리즘">CPU 스케줄링 알고리즘</h2>
<hr>
<h3 id="선입선출-스케줄링fcfs-first-come-first-served">선입선출 스케줄링(FCFS, First Come First Served)</h3>
<p>준비 <strong>큐에 들어간 순서대로</strong> 프로세스들을 처리하는 비선점형 스케줄링 방식입니다. 즉, CPU를 먼저 요청한 프로세스부터 CPU를 할당하는 방식입니다.</p>
<p>CPU를 오래 사용하는 프로세스가 먼저 도착하면 다른 프로세스들은 기다려야 하기 때문에 <strong>평균 대기 시간이 길어질 수 있다</strong>는 단점이 있습니다. </p>
<h3 id="최단-작업-우선-스케줄링sjf-shortest-job-first">최단 작업 우선 스케줄링(SJF, Shortest Job First)</h3>
<p>준비 큐에 들어간 프로세스들 중에 <strong>CPU 이용 시간이 가장 짧은 프로세스부터 실행</strong>하는 스케줄링 방식을 말합니다.</p>
<p>시간이 짧은 프로세스부터 실행하기 때문에 FCFS 스케줄링보다 <strong>평균 대기 시간이 감소</strong>합니다.</p>
<h3 id="라운드-로빈-스케줄링round-robin">라운드 로빈 스케줄링(Round Robin)</h3>
<p>라운드 로빈 스케줄링은 <strong>정해진 타임 슬라이스만큼의 시간 동안 돌아가며 CPU를 이용</strong>하는 선점형 스케줄링 방식입니다.</p>
<p>큐에 삽입된 프로세스들은 <strong>순서대로 정해진 시간만큼만 CPU를 이용</strong>하고, 정해진 시간이 지나도 프로세스가 완료되지 않았다면 <strong>다시 큐의 맨 뒤에 삽입</strong>합니다.</p>
<p>라운드 로빈 스케줄링에서는 타임 슬라이스의 크기가 중요합니다. <strong>타임 슬라이스가 지나치게 크면 FCFS와 같게 되고, 지나치게 작으면 문맥 교환이 잦아져서 오버헤드가 증가</strong>합니다.</p>
<h3 id="최소-잔여-시간-우선-스케줄링srt-shortest-remaining-time">최소 잔여 시간 우선 스케줄링(SRT, Shortest Remaining Time)</h3>
<p>프로세스들이 정해진 타임 슬라이스만큼 CPU를 사용하되, CPU를 사용할 다음 프로세스로는 <strong>남아있는 작업 시간이 가장 적은 프로세스를 선택</strong>하는 방식입니다.</p>
<h3 id="우선순위-스케줄링priority-scheduling">우선순위 스케줄링(priority scheduling)</h3>
<p>프로세스들에 우선순위를 부여하고, 가장 높은 <strong>우선순위를 가진 프로세스부터 실행</strong>하는 스케줄링 알고리즘입니다.</p>
<p>이때 우선 순위가 낮은 프로세스들이 무한정 기다리는 <strong>기아(starvation) 현상</strong>이 발생할 수 있습니다.</p>
<p>이러한 기아 현상은 오랫동안 대기한 프로세스의 우선순위를 점차 높이는 <strong>에이징(aging) 기법</strong>으로 해결할 수 있습니다. </p>
<h3 id="다단계-큐-스케줄링multilevel-queue">다단계 큐 스케줄링(multilevel queue)</h3>
<p>우선순위별로 준비 큐를 여러 개 사용하는 스케줄링 방식입니다.</p>
<p>우선순위가 가장 높은 큐에 있는 프로세스들을 먼저 처리하고, 우선순위가 가장 높은 큐가 비어 있으면 그다음 우선순위 큐에 있는 프로세스들을 처리합니다.</p>
<p>큐를 여러 개 두어 프로세스 유형별로 우선순위를 구분하여 실행할 수 있습니다. 또한 큐마다 타임 슬라이스를 다르게 지정할 수 있고, 다른 스케줄링 알고리즘을 사용할 수도 있습니다.</p>
<h3 id="다단계-피드백-큐-스케줄링mutilevel-feedback-queue">다단계 피드백 큐 스케줄링(mutilevel feedback queue)</h3>
<p>다단계 큐 피드백 스케줄링 알고리즘은 어떤 프로세스의 <strong>CPU 이용 시간이 길면 낮은 우선순위 큐로 이동</strong>시키고, 어떤 프로세스가 낮은 우선순위 큐에서 너무 <strong>오래 기다린다면 높은 우선순위 큐로 이동</strong>시킬 수 있는 알고리즘입니다.</p>
<p>새로 준비 상태가 된 프로세스가 있다면 우선순위가 가장 높은 큐에 삽입되고 타임 슬라이스 동안 실행됩니다. 만약 프로세스가 해당 큐에서 실행이 끝나지 않으면 다음 우선순위 큐에 삽입합니다. 이 과정을 통해 CPU를 오래 사용해야 하는 프로세스는 점차 우선순위가 낮아집니다.</p>
<p>즉, CPU를 비교적 오래 사용하는 CPU 집중 프로세스들은 자연스레 우선순위가 낮아지고, CPU를 비교적 적게 사용하는 입출력 집중 프로세스들은 우선순위가 높은 큐에서 실행이 끝나게 됩니다.</p>
<p>또한 큐 사이를 이동할 수 있는 방식이기 때문에 낮은 우선순위 큐에서 장시간 대기하는 프로세스가 있다면 점차 우선순위가 높은 큐로 이동시키는 에이징 기법을 적용하여 기아 현상을 예방할 수 있습니다.</p>
<br>
<br>

<p><strong>Reference</strong>
<a href="https://www.yes24.com/Product/Goods/111378840">혼자 공부하는 컴퓨터 구조+운영체제</a>
<a href="https://dar0m.tistory.com/247">https://dar0m.tistory.com/247</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[OOP] 원시값 포장과 일급 컬렉션]]></title>
            <link>https://velog.io/@calm0_0/%EC%9D%BC%EA%B8%89-%EC%BB%AC%EB%A0%89%EC%85%98%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@calm0_0/%EC%9D%BC%EA%B8%89-%EC%BB%AC%EB%A0%89%EC%85%98%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Wed, 22 Nov 2023 13:05:17 GMT</pubDate>
            <description><![CDATA[<h2 id="원시값-포장">원시값 포장</h2>
<hr>
<p>원시 값 포장이란 원시 타입으로 선언된 변수를 의미 있는 객체로 포장하여 선언하는 방법을 말한다.</p>
<pre><code class="language-typescript">class User {
  private name: string;
  private age: number;

  //...
}</code></pre>
<p>위와 같이 원시 타입으로 선언된 변수 name, age 를</p>
<pre><code class="language-typescript">class User {
  private name: Name;
  private age: Age;

  constructor(name: Name, age: Age) {
      this.name = name;
    this.age = age;
  }
}

class Name {
  private name: string;

  constructor(name: string) {
      if(name.length &lt; 2) {
      throw new Error(&#39;이름은 두 글자 이상&#39;);
    }
  }
}

public class Age {
  private age: number;

  constructor(age: number) {
    if (age &lt; 0) {
      throw new Error(&#39;나이는 0살 이상&#39;);
    }
  }
}
</code></pre>
<p>객체로 포장하여 선언했다.</p>
<p>이렇게 하면 User 클래스는 사용자 그 자체 상태만 관리하고 이름, 나이 등 상태값은 Name, Age 클래스에서 스스로 관리할 수 있다. </p>
<p>원시값을 객체로 포장하면 자신의 상태를 객체 스스로 관리할 수 있어 책임이 명확해진다. 또한 코드의 유지보수에 도움이 된다.</p>
<br>

<h2 id="일급-컬렉션">일급 컬렉션</h2>
<hr>
<p>일급 컬렉션이란 Collection을 Wrapping하면서, Wrapping한 Collection 외 다른 멤버 변수가 없는 상태를 말한다.</p>
<pre><code class="language-typescript">interface Order {
  item: string;
  price: number;
}

class Orders {
  constructor(private orders: Order[]) {
    if (!orders.length) {
      throw Error(&#39;주문은 비어있을 수 없습니다.&#39;);
    }
  }

  get total() {
    return this.orders
      .map((order) =&gt; order.price)
      .reduce((previous, current) =&gt; previous + current);
  }

  add(item: string, price: number) {
    this.orders.push({ item, price });
  }

  printTotal() {
    console.log(`총 금액: ₩${this.total}`);
  }
}

const orders = new Orders([
  { item: &#39;치킨&#39;, price: 18000 },
  { item: &#39;콜라&#39;, price: 2000 },
  { item: &#39;치킨무&#39;, price: 1000 },
]);

orders.add(&#39;피자&#39;, 15000);

orders.printTotal();</code></pre>
<p>이렇게 일급 컬렉션을 사용하면 &#39;주문 목록&#39;이라는 비즈니스에 종속적인 자료구조를 만들 수 있다. 또한 주문 목록에 관련한 상태와 행위를 한 곳에서 관리할 수 있다.</p>
<br>
<br>

<p><strong>Reference</strong>
<a href="https://tecoble.techcourse.co.kr/post/2020-05-29-wrap-primitive-type/">https://tecoble.techcourse.co.kr/post/2020-05-29-wrap-primitive-type/</a>
<a href="https://woonjangahn.gitbook.io/logs/typescript/oop/4">https://woonjangahn.gitbook.io/logs/typescript/oop/4</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Docker] 도커 CLI 명령어 정리]]></title>
            <link>https://velog.io/@calm0_0/Docker-Docker-CLI-%EB%AA%85%EB%A0%B9%EC%96%B4-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@calm0_0/Docker-Docker-CLI-%EB%AA%85%EB%A0%B9%EC%96%B4-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Wed, 22 Nov 2023 13:02:39 GMT</pubDate>
            <description><![CDATA[<h3 id="pull">pull</h3>
<p>Docker Hub에 있는 Repository의 이미지를 다운받는 명령어</p>
<pre><code class="language-bash">$ docker pull IMAGE_NAME[:TAG]

$ docker pull ubuntu  # 우분투 이미지 다운</code></pre>
<h3 id="run">run</h3>
<p>이미지를 실행시켜 컨테이너를 띄우는 명령어</p>
<pre><code class="language-bash">$ docker run IMAGE_NAME</code></pre>
<p>다양한 옵션이 있다.</p>
<pre><code class="language-bash">$ docker run \
    --rm \  # 컨테이너 종료 시 자동으로 컨테이너 삭제
    -it \  # 터미널 인터렉티브로 실행. 셸을 띄울 때 사용
    --name &quot;container-name&quot;  # 컨테이너 명 지정
    -d  \  # 백그라운드 포워딩, 실행 후 터미널에서 바로 빠져나오게 해준다.
    -p 8000:8000 \  # 호스트 포트와 컨테이너 내부 포트를 연결
    -v /foo/bar:/tmp/foo/bar \  # 호스트 파일 시스템과 컨테이너 내부 파일 시스템 연결
    -e ENV_NAME=&quot;value&quot; \  # 환경 변수를 전달</code></pre>
<h3 id="ps">ps</h3>
<p>실행 중인 컨테이너의 목록을 보여주는 명령어</p>
<pre><code class="language-bash">$ docker ps
$ docker ps -a  # 중지된 컨테이너까지 보여줌</code></pre>
<h3 id="exec">exec</h3>
<p>실행 중인 컨테이너를 사용하는 명령어이다. 컨테이너 CLI를 이용할 때 주로 사용한다.</p>
<pre><code class="language-bash">$ docker exec [컨테이너명/ID] [COMMAND]  

$ docker exec [컨테이너명/ID] ls  # 해당 컨테이너의 디렉토리를 보여줌
$ docker exec -it {container id} {sh, bash, zsh, powershell}  # 실행 후 터미널에서 명령어를 계속 적어 나갈 때 사용 (control + D 로 쉘에서 빠져나올 수 있다.)</code></pre>
<h3 id="stop">stop</h3>
<p>실행 중인 컨테이너를 중지시키는 명령어</p>
<pre><code class="language-bash">$ docker stop [컨테이너명]</code></pre>
<h3 id="kill">kill</h3>
<p>컨테이너 실행을 즉시 중지시키는 명령어</p>
<h3 id="images">images</h3>
<p>로컬에 저장된 도커 이미지 목록을 보여준다.</p>
<pre><code class="language-bash">$ docker images </code></pre>
<h3 id="rm">rm</h3>
<p>정지된 컨테이너를 지울 때 사용한다.</p>
<pre><code class="language-bash">$ docker rm [컨테이너명]
$ docker rm --force [컨테이너명]  # 강제로 중지
$ docker rm -f $(docker ps -aq)  # 실행 중 + 중지된 도커 컨테이너들을 지우기</code></pre>
<h3 id="docker-rmi">docker rmi</h3>
<p>로컬에 있는 도커 이미지를 삭제할 때 사용한다.</p>
<pre><code class="language-bash">$ docker rmi [이미지 이름]
$ docker rmi $(dcoker images -q)  # 모든 이미지 삭제</code></pre>
<h3 id="docker-build">docker build</h3>
<p>지정된 도커 파일에서 이미지를 빌드할 때 사용한다.</p>
<pre><code class="language-bash">$ docker build [path to Dockerfile]

$ docker build .  # 현재 디렉터리에 Dockerfile을 이용</code></pre>
<br>
<br>

<p><strong>Reference</strong>
<a href="https://velog.io/@dnflekf2748/Docker-CLI">https://velog.io/@dnflekf2748/Docker-CLI</a>
<a href="https://velog.io/@hojin9622/docker-%EB%AA%85%EB%A0%B9%EC%96%B4">https://velog.io/@hojin9622/docker-%EB%AA%85%EB%A0%B9%EC%96%B4</a>
<a href="https://seorenn.github.io/note/docker-cli-commands.html">https://seorenn.github.io/note/docker-cli-commands.html</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[OS] 운영체제 정리 (2)]]></title>
            <link>https://velog.io/@calm0_0/OS-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%EC%A0%95%EB%A6%AC-2</link>
            <guid>https://velog.io/@calm0_0/OS-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%EC%A0%95%EB%A6%AC-2</guid>
            <pubDate>Wed, 15 Nov 2023 12:54:35 GMT</pubDate>
            <description><![CDATA[<h2 id="프로세스">프로세스</h2>
<hr>
<h3 id="프로그램program">프로그램(Program)</h3>
<p>프로그램이란 원하는 어떠한 일을 하기 위해 계획된 방법과 절차에 의해 작성된 일련의 순서나 목록을 말합니다. </p>
<p>즉, 컴퓨터 프로그램은 컴퓨터에서 실행될 때 <strong>특정 작업을 수행</strong>하는 일련의 <strong>명령어들의 모음</strong>입니다.</p>
<p>보조기억 장치에 저장되어 있던 프로그램이 메모리에 올라와 실행되면 프로세스가 됩니다.</p>
<h3 id="프로세스process">프로세스(Process)</h3>
<p>프로세스란 <strong>실행 중인 프로그램</strong>을 말합니다.</p>
<p>보조기억장치에 저장된 <strong>프로그램</strong>을 <strong>메모리에 적재</strong>하고 <strong>실행</strong>하는 순간 그 프로그램은 프로세스가 됩니다.</p>
<h3 id="프로세스-제어-블록pcb-process-control-block">프로세스 제어 블록(PCB, Process Control Block)</h3>
<p>프로세스 제어 블록은 <strong>프로세스와 관련된 정보를 저장</strong>하는 자료 구조입니다.</p>
<p>운영체제는 빠르게 번갈아 수행되는 프로세스의 실행 순서를 관리하고, 프로세스에 CPU를 비롯한 자원을 배분하기 위해 프로세스 제어 블록을 이용합니다.</p>
<p>PCB는 커널 영역에서 생성되며, 프로세스 생성 시에 만들어지고 실행이 끝나면 폐기됩니다.</p>
<h4 id="pcb에-담긴-정보들">PCB에 담긴 정보들</h4>
<ul>
<li><strong>프로세스 ID (PID)</strong>: 특정 프로세스를 식별하기 위한 고유한 번호</li>
<li><strong>레지스터 값</strong>: 해당 프로세스가 실행하며 사용했던 레지스터 값** (프로세스는 자신의 실행 차례가 돌아오면 이전까지 사용했던 레지스터의 값들을 복원하여 이전 작업들을 이어서 실행합니다.)</li>
<li><strong>프로세스 상태</strong>: 준비, 실행, 대기 등 프로세스의 상태 정보</li>
<li><strong>CPU 스케줄링 정보</strong>: 프로세스가 언제, 어떤 순서로 CPU를 할당받을지에 대한 정보</li>
<li><strong>메모리 관리 정보</strong>: 프로세스의 메모리 주소를 알 수 있는 정보</li>
<li><strong>사용한 파일과 입출력 장치 목록</strong>: 프로세스가 실행 중에 입출력 장치나 파일을 사용한 기록 </li>
</ul>
<h3 id="문맥-교환context-switching">문맥 교환(Context Switching)</h3>
<h4 id="문맥context-이란">문맥(Context) 이란</h4>
<p>실행 중이던 프로세스에서 다른 프로세스로 <strong>실행 순서가 넘어갈 때</strong>, 실행되던 프로세스는 지금까지의 중간 <strong>정보를 백업</strong>해야 합니다. 그래야 다음 차례가 왔을 때 이전까지 실행했던 내용에 이어 <strong>실행을 재개</strong>할 수 있습니다.</p>
<p>이렇게 하나의 <strong>프로세스 수행을 재개하기 위해 기억해야 할 정보</strong>를 문맥(context) 이라고 합니다. </p>
<p>한 프로세스의 문맥은 해당 프로세스의 <strong>PCB에 표현</strong>되어 있습니다.</p>
<h4 id="문맥-교환context-switching-이란">문맥 교환(Context Switching) 이란</h4>
<p>프로세스 간 <strong>실행을 전환</strong>하는 것을 문맥 교환이라고 합니다.</p>
<p>프로세스가 CPU를 사용할 수 있는 <strong>시간이 다 되거나</strong> 예기치 못한 상황이 발생하여 <strong>인터럽트</strong>가 발생하면,</p>
<p>운영체제는 <strong>기존 프로세스의 문맥을 PCB에 백업</strong>하고, 
새로운 프로세스를 실행하기 위해 <strong>문맥을 PCB로 부터 복구</strong>하여 
<strong>새로운 프로세스를 실행</strong>합니다.</p>
<p>문맥 교환이 자주 발생하면 여러 프로세스가 끊임없이 빠르게 번갈아 가며 실행되어 사용자 입장에서는 프로세스들이 동시에 실행되는 것처럼 보입니다. 하지만 문맥 교환을 너무 자주하면 오버헤드가 발생할 수 있습니다.</p>
<h3 id="프로세스의-메모리-공간">프로세스의 메모리 공간</h3>
<p>프로세스의 메모리 영역은 크게 코드 영역, 데이터 영역, 힙 영역, 스택 영역으로 나뉘어 있습니다.</p>
<h4 id="코드-영역code-segment">코드 영역(code segment)</h4>
<p><strong>실행할 수 있는 코드</strong>(기계어로 이루어진 명령어)가 저장됩니다. 여기에는 데이터가 아닌 CPU가 실행할 명령어가 담겨 있기 때문에 쓰기가 금지되어 있습니다.</p>
<h4 id="데이터-영역data-segment">데이터 영역(data segment)</h4>
<p>데이터 영역은 전역 변수 등 <strong>프로그램을 실행하는 동안 유지할 데이터</strong>가 저장되는 공간입니다.</p>
<h4 id="힙-영역heap-segment">힙 영역(heap segment)</h4>
<p>프로그래머가 <strong>직접 할당</strong>할 수 있는 저장 공간입니다.</p>
<h4 id="스택-영역stack-segment">스택 영역(stack segment)</h4>
<p>데이터를 <strong>일시적으로 저장</strong>하는 공간입니다. (매개 변수, 지역 변수 등)</p>
<p>코드 영역과 데이터 영역은 크기가 고정된 <strong>정적 할당 영역</strong>이고, 힙 영역과 스택 영역은 실시간으로 크기가 변할 수 있는 <strong>동적 할당 영역</strong>입니다.</p>
<h3 id="프로세스-상태">프로세스 상태</h3>
<h4 id="생성-상태new">생성 상태(new)</h4>
<p>프로세스를 생성 중인 상태입니다. 이제 막 메모리에 적재되어 PCB를 할당받은 상태입니다. </p>
<h4 id="준비-상태ready">준비 상태(ready)</h4>
<p>생성 상태를 거쳐 준비 상태가 됩니다.</p>
<p>준비 상태는 당장이라도 CPU를 할당받아 실행할 수 있지만, 자신의 차례가 아니라 기다리고 있는 상태입니다.</p>
<p>준비 상태인 프로세스가 실행 상태로 전환되는 것을 디스패치(dispatch)라고 합니다.</p>
<h4 id="실행-상태running">실행 상태(running)</h4>
<p>CPU를 할당받아 실행 중인 상태입니다.</p>
<p>할당된 일정 시간 동안만 CPU를 사용할 수 있으며, 할당된 시간이 지나면(타이머 인터럽트가 발생하면) 다시 준비 상태가 됩니다.</p>
<p>실행 도중 입출력장치를 사용하여 입출력장치의 작업이 끝나기를 기다려야 한다면 대기 상태가 됩니다.</p>
<h4 id="대기-상태blocked">대기 상태(blocked)</h4>
<p>프로세스가 실행 중에 입출력 작업을 요청한 경우, 입출력 작업은 CPU에 비해 처리 속도가 느리기 때문에, 프로세스는 입출력장치가 작업을 끝낼 때까지(입출력 완료 인터럽트를 받을 때까지) 기다려야 합니다.</p>
<p>이렇게 입출력장치의 작업을 기다리는 상태를 대기 상태라고 합니다.</p>
<p>입출력 작업이 완료되면 해당 프로세스는 다시 준비 상태로 CPU 할당을 기다립니다.</p>
<h4 id="종료-상태terminated">종료 상태(terminated)</h4>
<p>프로세스가 종료된 상태입니다. 프로세스가 종료되면 운영체제는 PCB와 프로세스가 사용한 메모리를 정리합니다.</p>
<h3 id="프로세스-생성-기법">프로세스 생성 기법</h3>
<h4 id="fork">fork()</h4>
<p>fork()는 자기 자신 프로세스의 <strong>복사본을 자식 프로세스로 생성</strong>하는 시스템 콜입니다.</p>
<p>부모 프로세스는 fork 시스템 콜을 통해 자신의 <strong>복사본</strong>을 자식 프로세스로 생성합니다. PID나 메모리 위치는 다르지만, 자식 프로세스는 부모 프로세스의 복사본이기 때문에 <strong>부모 프로세스의 자원들이 상속</strong>됩니다.</p>
<h4 id="exec">exec()</h4>
<p>exec()는 자신의 메모리 공간을 새로운 프로그램으로 덮어쓰는 시스템 콜입니다.</p>
<p>자식 프로세스는 exec 시스템 콜을 통해 새로운 프로그램으로 전환됩니다. exec를 호출하면 코드 영역과 데이터 영역의 내용이 실행할 프로그램의 내용으로 바뀌고, 나머지 영역은 초기화됩니다.</p>
<p>예를 들어, bash 셸에서 ls 명령어를 입력하면,</p>
<ul>
<li>셸 프로세스는 fork를 통해 자신과 동일한 자식 프로세스를 생성하고,</li>
<li>자식 프로세스는 exec를 통해 ls 명령어를 실행하기 위한 프로세스로 전환되어 실행합니다.</li>
<li>셸의 복사본으로 만들어진 자식 프로세스는 ls 명령어를 실행하기 위한 프로세스로 바뀌고, 메모리 공간에는 ls 명령어를 실행하기 위한 내용들이 채워집니다.</li>
</ul>
<br>

<h2 id="스레드">스레드</h2>
<hr>
<h3 id="스레드thread-란">스레드(Thread) 란</h3>
<p>스레드란 <strong>프로세스 안에서 실행</strong>되는 <strong>여러 흐름의 단위</strong>입니다.</p>
<p>하나의 프로세스는 여러 스레드를 가질 수 있으며, 스레드를 이용하면 하나의 프로세스에서 여러 부분을 동시에 실행할 수 있습니다.</p>
<h3 id="스레드의-메모리-공간">스레드의 메모리 공간</h3>
<p>프로세스 안에서 스레드들은 각각 <strong>레지스터와 스택</strong>만 따로 할당받고, 코드/데이터/힙 영역은 <strong>서로 공유</strong>하며 사용합니다.</p>
<p>즉, 스레드들은 실행에 필요한 최소한의 정보(레지스터, 스택)만을 유지한 채 프로세스 자원을 공유하면 실행됩니다.</p>
<h3 id="스레드-제어-블록tcb-">스레드 제어 블록(TCB) ?</h3>
<h3 id="사용자-수준의-스레드와-커널-수준의-스레드-">사용자 수준의 스레드와 커널 수준의 스레드 ?</h3>
<br>

<h2 id="멀티-프로세스와-멀티-스레드">멀티 프로세스와 멀티 스레드</h2>
<hr>
<p><strong>여러 프로세스를 동시에 실행</strong>하는 것 멀티 프로세스(multiprocess),
<strong>여러 스레드로 프로세스를 동시에 실행</strong>하는 것 멀티 스레드(multithread) 라고 합니다.</p>
<p>프로세스끼리는 기본적으로 자원을 공유하지 않지만, 스레드끼리는 같은 프로세스 내의 자원을 공유한다는 차이가 있습니다.</p>
<p>그래서 멀티 프로세스 환경에서는 하나의 프로세스에 문제가 생겨도 다른 프로세스에 지장이 적거나 없다는 장점이 있습니다. 하지만 각각 독립된 메모리 영역을 가지고 있어, 작업량이 많을 수록 오버헤드가 발생할 수 있습니다.</p>
<p><em>(오버헤드(overhead)는 어떤 처리를 하기 위해 들어가는 간접적인 처리 시간 · 메모리 등을 말한다.)</em></p>
<p>반면에 스레드들은 프로세스의 자원을 공유하기 때문에 여러 프로세스를 병렬 실행하는 것보다 <strong>메모리를 더 효율적으로 사용</strong>할 수 있으며 서로 <strong>협력과 통신</strong>에 유리합니다. 하지만, 하나의 스레드에 문제가 생기면 자원을 공유하기 때문에 프로세스 전체에 문제가 생길 수 있습니다.</p>
<br>

<h2 id="ipcinter-process-communication">IPC(Inter-Process Communication)</h2>
<hr>
<p>프로세스는 매우 독립적으로 기본적으로 자원을 공유하지 않지만, 경우에 따라서 프로세스가 협력을 해야만 효율적으로 실행되는 경우도 있습니다. </p>
<p>그래서 프로세스끼리도 자원을 공유하고 데이터를 주고받는 협력 메커니즘이 존재합니다.</p>
<p>이렇게 프로세스 간 자원을 공유하고 데이터를 주고받는 것을 프로세스 간 통신(IPC) 라고 합니다.</p>
<p>IPC에는 크게 두 가지 방법이 있습니다.</p>
<h3 id="message-passing">Message passing</h3>
<p>프로세스가 서로 메시지를 주고받으며 실행되는 방식입니다. 하지만 프로세스는 독립적이기 때문에 자기 메모리 주소 공간만 볼 수 있으며, 다른 프로세스에게 직접 메시지를 전달하는 방법은 원칙적으로는 존재하지 않습니다. 그래서 커널을 통해(시스템 콜을 통해) 메시지를 전달합니다.</p>
<h3 id="shared-memory">Shared memory</h3>
<p>프로세스들이 서로 공유하는 메모리 영역을 두어 데이터를 주고 받을 수 있습니다. 프로세스들이 공유할 수 있는 메모리 영역을 공유 메모리(shared memory) 라고 합니다.</p>
<p>프로세스 생성 시 기본적으로 메모리 공간을 공유하지 않기 때문에 이를 위한 시스템 콜을 해야 합니다.</p>
<br>
<br>

<p><strong>Reference</strong>
<a href="https://www.yes24.com/Product/Goods/111378840">혼자 공부하는 컴퓨터 구조+운영체제
</a>
<a href="https://higunnew.tistory.com/26">https://higunnew.tistory.com/26</a>
<a href="https://gyoogle.dev/blog/computer-science/operating-system/Process%20vs%20Thread.html">https://gyoogle.dev/blog/computer-science/operating-system/Process%20vs%20Thread.html</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[OS] 운영체제 정리  (1)]]></title>
            <link>https://velog.io/@calm0_0/OS-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%EC%A0%95%EB%A6%AC-1</link>
            <guid>https://velog.io/@calm0_0/OS-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%EC%A0%95%EB%A6%AC-1</guid>
            <pubDate>Wed, 08 Nov 2023 14:52:02 GMT</pubDate>
            <description><![CDATA[<h2 id="운영체제의-정의-및-역할">운영체제의 정의 및 역할</h2>
<hr>
<h3 id="운영체제란">운영체제란</h3>
<p>운영체제(OS, Operating System)는 컴퓨터 시스템의 <strong>자원</strong>들을(CPU, 메모리, 보조기억장치, 입출력장치 등) 효율적으로 <strong>관리</strong>하며, 프로그램에 필요한 자원을 <strong>할당</strong>하고, 프로그램이 올바르게 <strong>실행</strong>되도록 돕는 <strong>시스템 소프트웨어</strong>이다.</p>
<p>운영체제는 <strong>커널 영역</strong>에 적재되어 실행되며, <strong>사용자 영역</strong>에 적재된 응용 프로그램들에 자원을 할당하고 이들을 올바르게 실행되도록 한다.</p>
<h3 id="운영체제의-역할">운영체제의 역할</h3>
<h4 id="프로세스-관리">프로세스 관리</h4>
<p>운영체제에서 작동하는 응용 프로그램을 관리하는 기능이다. 일반적으로 CPU는 한 번에 하나의 프로세스만 실행할 수 있고, 각 프로세스는 상태나 사용하고자 하는 자원이 다양하다. 그래서 운영체제는 다양한 프로세스를 일목요연하게 관리하고 실행할 수 있어야 한다.</p>
<h4 id="자원-접근-및-할당">자원 접근 및 할당</h4>
<p>모든 프로세스는 실행을 위해 자원을 필요로 하며, 운영체제는 프로세스들이 사용할 자원(CPU, 메모리, 입출력장치 등)에 접근하고 조작함으로써 프로세스에 필요한 자원을 할당해 준다.</p>
<h4 id="파일-시스템-관리">파일 시스템 관리</h4>
<p>운영체제는 파일 시스템을 통해 보조기억장치 속 데이터를 파일과 디렉터리로 관리한다.</p>
<br>

<h2 id="운영체제의-유형">운영체제의 유형</h2>
<hr>
<h3 id="시분할-시스템time-sharing-system">시분할 시스템(Time sharing system)</h3>
<p>CPU의 <strong>작업 시간</strong>을 여러 프로그램들이 <strong>조금씩 나누어</strong> 쓰는 시스템을 시분할 시스템이라고 한다.</p>
<p>하나의 CPU는 같은 시점에 여러 작업을 동시에 수행할 수 없기 때문에 CPU의 전체 사용 시간을 <strong>작은 작업 시간량</strong>으로 나누어 그 시간량 동안만 <strong>번갈아가면서 CPU를 할당</strong>하여 각 프로세스를 처리한다.</p>
<h3 id="다중-프로그래밍-시스템multi-programming-system">다중 프로그래밍 시스템(Multi-programming system)</h3>
<p>CPU와 달리 메모리의 경우 여러 프로그램들이 조금씩 메모리 공간을 보유하며 여러 프로그램들을 동시에 메모리에 올려 놓을 수 있다. </p>
<p><strong>메모리 공간을 분할</strong>해 여러 프로그램들을 <strong>동시에 메모리에 올려놓고</strong> 처리하는 시스템을 다중 프로그래밍 시스템이라고 한다.</p>
<p>하나의 메모리에 여러 개의 프로그램을 올려 놓고 하나의 CPU와 대화하면서 동시에 처리한다.</p>
<p>메인 메모리의 크기가 커야 하며 CPU 사용률과 처리량이 증가하지만 반응시간이 늦다.</p>
<h3 id="다중-처리기-시스템multi-processor-system">다중 처리기 시스템(Multi-processor system)</h3>
<p><strong>여러 개의 CPU</strong>와 <strong>하나의 주기억장치</strong>를 이용하여 <strong>다수의 프로그램을 동시에 처리</strong>하는 방식이다.</p>
<p>하나의 CPU가 고장나더라도 다른 CPU를 이용하여 작업을 처리할 수 있어 시스템의 <strong>신뢰성과 안정성</strong>이 높다.</p>
<p>CPU가 여러 개라 <strong>병렬 처리</strong>가 가능하고 <strong>처리 속도</strong>는 빨라지지만, 운영 체제 입장에서는 <strong>여러 개의 CPU를 컨트롤</strong> 해야하므로 더 복잡한 메커니즘을 필요로 한다.</p>
<br>

<h2 id="커널--시스템-콜">커널 &amp; 시스템 콜</h2>
<hr>
<h3 id="커널kernel">커널(Kernel)</h3>
<p>자원에 접근하고 조작하는 기능, 프로그램이 올바르고 안전하게 실행되게 하는 기능 등 <strong>운영체제의 핵심 서비스</strong>를 담당하는 부분을 <strong>커널(Kernel)</strong>이라고 한다.</p>
<h3 id="유저-모드user-mode--커널-모드kernel-mode">유저 모드(user mode) &amp; 커널 모드(kernel mode)</h3>
<p><strong>운영체제</strong>는 응용 프로그램들이 <strong>자원에 접근</strong>하려고 할 때 오직 <strong>자신을 통해서만</strong> 접근하도록 하여 <strong>자원을 보호</strong>한다. </p>
<p>만약 응용 프로그램들이 CPU, 메모리, 하드 디스크 등에 마음대로 접근하여 조작할 수 있다면 자원의 관리가 어렵고, 응용 프로그램이 실수하게 되면 컴퓨터 전체에 큰 악영향을 미칠 수 있기 때문이다.</p>
<p>이러한 운영체제의 문지기 역할은 <strong>CPU가 명령어를 실행하는 모드</strong>를 크게 <strong>유저 모드</strong>와 <strong>커널 모드</strong>로 구분함으로써 구현된다.</p>
<h4 id="유저-모드">유저 모드</h4>
<p>유저 모드는 <strong>운영체제 서비스를 제공받을 수 없는</strong> 실행 모드이다. 즉, 커널 영역의 코드를 실행할 수 없는 모드이다. 일반적인 응용 프로그램은 기본적으로 유저 모드로 실행된다.</p>
<p>유저 모드로 실행 중인 CPU는 입출력 명령어와 같이 <strong>하드웨어 자원에 접근하는 명령어를 실행할 수 없다</strong>. 그래서 유저 모드로 실행 중인 응용 프로그램은 자원에 접근할 수 없다.</p>
<h4 id="커널-모드">커널 모드</h4>
<p>반면에 커널 모드는 <strong>운영체제 서비스를 제공받을 수 있는</strong> 실행 모드이다. 즉, 커널 영역의 코드를 실행할 수 있다. CPU가 커널 모드로 명령어를 실행하면 자원에 접근하는 명령어를 비롯한 모든 명령어를 실행할 수 있다.</p>
<h3 id="시스템-콜system-call">시스템 콜(System call)</h3>
<p>사용자 모드로 실행되는 프로그램이 자원에 접근하려면 운영체제에 요청을 보내 <strong>커널 모드로 전환</strong>되어야 한다. 이때 <strong>운영체제 서비스를 제공받기 위한 요청</strong>을 시스템 콜(system call)이라고 한다.</p>
<h4 id="시스템-콜의-작동">시스템 콜의 작동</h4>
<p>예를 들어 응용 프로그램이 실행되는 중에 하드 디스크에 접근하여 데이터를 저장하려면,</p>
<ul>
<li>응용 프로그램은 하드 디스크에 데이터를 저장하는 <strong>시스템 콜을 발생</strong>시켜 <strong>커널 모드로 전환</strong>하고,</li>
<li>운영체제 내의 하드 디스크에 데이터를 저장하는 코드를 실행함으로써 하드 디스크에 <strong>접근</strong>한다.</li>
<li>하드 디스크에 접근이 끝났다면 다시 <strong>유저 모드로 복귀</strong>하여 실행을 계속해 간다.</li>
</ul>
<p>일반적으로 응용 프로그램은 실행 과정에서 운영체제 서비스를 자주 이용한다. 그래서 빈번하게 시스템 콜을 발생시키고 유저 모드와 커널 모드를 오가며 실행된다.</p>
<br>

<h2 id="인터럽트interrupt">인터럽트(Interrupt)</h2>
<hr>
<h3 id="인터럽트란">인터럽트란</h3>
<p>CPU가 수행 중인 <strong>작업</strong>이 <strong>방해</strong>를 받아 <strong>잠시 중단</strong>될 때, <strong>CPU의 작업을 방해하는 신호</strong>를 인터럽트(interrupt)라고 한다.</p>
<p>프로그램의 실행 중에 <strong>예기치 않은 상황이 발생</strong>할 경우 현재 실행 중인 <strong>작업을 즉시 중단</strong>하고, 발생된 상황을 <strong>먼저 처리가 필요하다고 CPU에 알리는 것</strong>이다.</p>
<p>키보드 마우스 등 입출력장치로 인해 발생하는 인터럽트, 0으로 나누기와 같이 잘못된 명령이나 데이터를 사용할 때 발생하는 인터럽트, 프로세스 오류 등으로 발생한다.</p>
<h3 id="인터럽트-발생-처리-과정">인터럽트 발생 처리 과정</h3>
<p>프로그램 실행 중에 인터럽트가 발생하면,</p>
<ul>
<li>현재 수행 중인 프로그램을 <strong>멈추고</strong>, 현재 상태를 <strong>백업</strong>해둔다.</li>
<li>인터럽트 핸들러 함수가 모여 있는 <strong>인터럽트 백터</strong>로 가서 <strong>인터럽트를 처리</strong>한다.</li>
<li>인터럽트 처리를 마치면, 백업해둔 주소로 돌아가 <strong>다시 프로그램을 실행</strong>한다.</li>
</ul>
<h3 id="폴링polling">폴링(polling)</h3>
<p><strong>폴링(polling)</strong>이란 입출력장치의 상태는 어떤지, 처리할 데이터가 있는지 <strong>주기적으로 확인하는 방식</strong>이다.</p>
<p>폴링 방식은 인터럽트 방식보다 <strong>CPU의 부담이 더 크다.</strong> 인터럽트는 CPU가 인터럽트 요청을 받을 때까지 온전히 다른일에 집중할 수 있지만, 폴링의 경우 폴링을 하는 시간에는 원래 하던일에 집중할 수 없기 때문이다.</p>
<br>

<h2 id="dmadirect-memory-access">DMA(Direct Memory Access)</h2>
<hr>
<p>DMA(Direct Memory Access)는 <strong>입출력장치와 메모리가 CPU를 거치지 않고 데이터를 주고 받는 입출력 방식</strong>이다.</p>
<p>입출력장치와 메모리 사이에 전송되는 모든 데이터가 CPU를 거친다면 CPU의 부담이 너무 커진다. DMA는 <strong>CPU의 부하를 줄이기 위해</strong> 사용한다.</p>
<p>DMA 입출력을 하기 위해서는 시스템 버스에 연결된 DMA 컨트롤러라는 하드웨어가 필요하다.</p>
<h3 id="dma-입출력-과정">DMA 입출력 과정</h3>
<ul>
<li>CPU는 DMA 컨트롤러에 입출력장치의 주소, 수행할 연산, 메모리의 주소 등과같은 정보로 입출력 작업을 명령한다.</li>
<li>DMA 컨트롤러는 CPU 대신 장치 컨트롤러와 상호작용하며 입출력 작업을 수행한다. 필요한 경우, DMA 컨트롤러가 메모리에 직접 접근하여 정보를 읽거나 쓴다.</li>
<li>입출력 작업이 끝나면 DMA 컨트롤러는 CPU에 인터럽트를 걸어 작업이 끝났음을 알린다.</li>
</ul>
<br>

<h2 id="동기--비동기">동기 &amp; 비동기</h2>
<hr>
<h3 id="동기synchronous">동기(Synchronous)</h3>
<p>synchronous: 동시에 발생하는</p>
<p>컴퓨터 공학에서의 동기: 현재 작업의 응답과 다음 작업의 요청이 동시에 일어나는 것</p>
<ul>
<li>어떤 작업을 요청했을 때 그 작업이 종료될 때까지 기다린 후 다음 작업을 수행한다.</li>
<li>데이터를 주고받는 &#39;순서&#39;가 중요할때 사용된다.</li>
<li>요청한 작업만 처리하면 되기 때문에 전체적인 수행 속도는 빠를 수 있다. (일만 하면 된다)</li>
<li>한 작업에 대한 시간이 길어질 경우, 전체 응답이 지연될 수 있다.</li>
</ul>
<h3 id="비동기asynchronous">비동기(Asynchronous)</h3>
<p>asynchronous: 동시에 발생하지 않는</p>
<p>컴퓨터 공학에서의 비동기: 현재 작업의 응답과 다음 작업의 요청이 동시에 일어나지 않는 것</p>
<ul>
<li>어떤 작업을 요청했을 때 그 작업이 종료될 때까지 기다리지 않고(작업을 위임하고), 다음 작업을 수행한다. 요청했던 작업이 끝나면 결과를 받고, 그에 따른 추가 작업이 있다면 수행한다.</li>
<li>요청 순서에 상관없이, 동시에 다수의 작업을 처리할 수 있다.</li>
<li>작업이 끝날 때 따로 이벤트를 감지하고 결과를 받아 그에 따른 추가 작업을 해줘야하기 때문에, 비교적 느릴 수 있다.</li>
<li>I/O 작업이 잦고, 빠른 응답속도를 요구하는 프로그램에 적합하다.</li>
</ul>
<br>
<br>

<p><strong>Reference</strong></p>
<p><a href="https://www.yes24.com/Product/Goods/111378840">혼자 공부하는 컴퓨터 구조+운영체제</a>
<a href="https://velog.io/@nayeon15/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C%EC%9D%98-%ED%8A%B9%EC%A7%95%EA%B3%BC-%EC%A2%85%EB%A5%98">https://velog.io/@nayeon15/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C%EC%9D%98-%ED%8A%B9%EC%A7%95%EA%B3%BC-%EC%A2%85%EB%A5%98</a>
<a href="https://velog.io/@yoonuk/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C%EC%9D%98-%EB%B6%84%EB%A5%98">https://velog.io/@yoonuk/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C%EC%9D%98-%EB%B6%84%EB%A5%98</a>
<a href="https://matice.tistory.com/27">https://matice.tistory.com/27</a>
<a href="https://gyoogle.dev/blog/computer-science/operating-system/Interrupt.html">https://gyoogle.dev/blog/computer-science/operating-system/Interrupt.html</a>
<a href="https://velog.io/@soyeon207/%EB%8F%99%EA%B8%B0-%EB%B9%84%EB%8F%99%EA%B8%B0-blocking-non-blocking">https://velog.io/@soyeon207/%EB%8F%99%EA%B8%B0-%EB%B9%84%EB%8F%99%EA%B8%B0-blocking-non-blocking</a>
<a href="https://github.com/WeareSoft/tech-interview/blob/master/contents/os.md#%EB%8F%99%EA%B8%B0%EC%99%80-%EB%B9%84%EB%8F%99%EA%B8%B0">https://github.com/WeareSoft/tech-interview/blob/master/contents/os.md#%EB%8F%99%EA%B8%B0%EC%99%80-%EB%B9%84%EB%8F%99%EA%B8%B0</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[디자인 패턴] MVC 패턴]]></title>
            <link>https://velog.io/@calm0_0/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-MVC-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@calm0_0/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-MVC-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Tue, 07 Nov 2023 14:02:31 GMT</pubDate>
            <description><![CDATA[<h2 id="mvc-패턴이란">MVC 패턴이란</h2>
<hr>
<p>MVC 패턴이란 애플리케이션을 모델(Model), 뷰(View), 컨트롤러(Controller) 세 가지 역할로 구분한 소프트웨어 디자인 패턴이다.</p>
<p>애플리케이션의 구성 요소를 세 가지 역할로 구분하기 때문에 각각의 구성 요소에만 집중해서 개발할 수 있으며 유지 보수가 편리하다는 장점이 있다. 하지만 애플리케이션이 복잡해질수록 모델과 뷰의 관계가 복잡해진다는 단점이 있다.</p>
<br>

<h2 id="mvc-패턴의-구조-및-역할">MVC 패턴의 구조 및 역할</h2>
<hr>
<p>MVC 패턴은 모델(Model), 뷰(View), 컨트롤러(Controller) 세 개의 컴포넌트로 이루어지며 각 컴포넌트는 고유한 역할을 수행한다.</p>
<p><img src="https://velog.velcdn.com/images/calm0_0/post/3d3bf28b-8d28-4a75-9f7f-19ee7414f934/image.jpg" alt=""></p>
<h3 id="model">Model</h3>
<p>모델은 애플리케이션의 데이터와 비즈니스 로직을 관리하는 영역이다.</p>
<p>모델은 애플리케이션이 무엇을 할 것인지, 포함해야할 데이터가 무엇인지를 정의한다. </p>
<p>컨트롤러의 지시에 따라 데이터베이스와 연동하여 관련된 비즈니스 로직을 처리하고 필요한 데이터를 컨트롤러로 보낸다. </p>
<p>데이터 검색, 저장, 삭제, 업데이트 등의 작업을 수행한다.</p>
<p>모델은 다른 컴포넌트들에 대해 알지 못하며, 자기 자신이 무엇을 수행하는지만 알고 있다.</p>
<h3 id="view">View</h3>
<p>뷰는 사용자에게 무엇을 화면(UI)에 보여주기 위한 역할을 한다. </p>
<p>컨트롤러로부터 받은 모델이 처리한 데이터나 작업 결과를 가지고 사용자에게 출력할 화면을 만든다.</p>
<p>주의할 점은 모델로부터 받은 데이터를 뷰에서 별도로 저장하지 않는다.</p>
<p>뷰 또한 다른 컴포넌트들에 대해 알지 못하며, 자기 자신이 무엇을 수행하는지만 알고 있다.</p>
<h3 id="controller">Controller</h3>
<p>컨트롤러는 모델과 뷰 사이를 이어주는 다리 역할을 한다. </p>
<p>클라이언트의 입력(요청)에 따라 필요한 로직의 실행을 모델에 지시하고, 그 결과를 가지고 화면을 생성하도록 뷰에 전달한다. 다음과 같은 흐름을 가진다.</p>
<ol>
<li>클라이언트가 보낸 요청을 컨트롤러가 받는다.</li>
<li>컨트롤러가 모델에 필요한 로직의 실행을 지시한다.</li>
<li>지시를 받은 모델이 처리 결과를 컨트롤러에 보낸다.</li>
<li>컨트롤러가 모델에서 받은 결과를 가지고 뷰에 표시의 실행을 지시한다.</li>
<li>뷰에서 화면을 표시한다.</li>
</ol>
<p>즉, 클라이언트의 요청에 대해 모델과 뷰를 결정하여 전달하는 일종의 조정자 역할을 한다.</p>
<p>그래서 컨트롤러는 다른 컴포넌트들에 대해 알고 있고, 자기 자신 외에 모델과 뷰가 무엇을 수행하는지 알고 있다.</p>
<br>

<h2 id="mvc-패턴의-예시">MVC 패턴의 예시</h2>
<hr>
<p><del>간단한 예제 코드 추가 예정</del></p>
<br>

<h2 id="mvc-패턴의-장점">MVC 패턴의 장점</h2>
<hr>
<p>MVC 패턴은 역할별로 독립적이기 때문에 작업 구분도 명확하고 병렬 개발이 가능하다.</p>
<p>각 컴포넌트는 자신이 맡은 역할만 수행한 후 다른 컴포넌트로 결과만 넘겨주면 되기 때문에 시스템 결합도를 낮출 수 있다.</p>
<p>유지보수 시에도 특정 컴포넌트만 수정하면 되기 때문에 보다 쉽게 시스템 변경이 가능하다.</p>
<p>기능별로 코드를 분리하여 하나의 파일에 코드가 모이는 것을 방지하여 코드의 가독성, 확장성, 재사용성이 증가한다.</p>
<br>

<h2 id="mvc-패턴의-한계">MVC 패턴의 한계</h2>
<hr>
<p>MVC 패턴은 기능별로 코드를 구분한다. 그러나 이 때문에 코드 기술에 제약이 늘어나기도 해서 오히려 시간이 더 걸려 버리는 경우도 있다. 이 때문에 소규모의 개발에는 적합하지 않은 경우도 있다.</p>
<p>복잡한 대규모 애플리케이션의 경우 컨트롤러에 다수의 뷰와 모델이 복잡하게 연결되기 때문에 컨트롤러가 지나치게 커지는 현상이 발생한다. 이러한 현상을 Massive-View-Controller 현상이라고 하며 이를 보완하기 위해 MVP, MVVM 등의 다양한 패턴들이 등장했다.</p>
<br>
<br>

<p><strong>Reference</strong>
<a href="https://www.youtube.com/watch?v=ogaXW6KPc8I">https://www.youtube.com/watch?v=ogaXW6KPc8I</a>
<a href="https://junhyunny.github.io/information/design-pattern/mvc-pattern/">https://junhyunny.github.io/information/design-pattern/mvc-pattern/</a>
<a href="https://developer.mozilla.org/ko/docs/Glossary/MVC">https://developer.mozilla.org/ko/docs/Glossary/MVC</a>
<a href="https://www.devkuma.com/docs/mvc/">https://www.devkuma.com/docs/mvc/</a>
<a href="https://tecoble.techcourse.co.kr/post/2021-04-26-mvc/">https://tecoble.techcourse.co.kr/post/2021-04-26-mvc/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DB] Lock ]]></title>
            <link>https://velog.io/@calm0_0/DB-Lock</link>
            <guid>https://velog.io/@calm0_0/DB-Lock</guid>
            <pubDate>Wed, 01 Nov 2023 13:50:59 GMT</pubDate>
            <description><![CDATA[<h2 id="db-lock">DB Lock</h2>
<h3 id="db-lock-이란">DB Lock 이란</h3>
<p>데이터베이스에서 여러 사용자들이 같은 데이터를 동시에 접근하는 동시성을 제어하여 데이터의 무결성과 일관성을 지키기 위한 방법이다.</p>
<h3 id="lock의-종류">Lock의 종류</h3>
<h4 id="shared-lock공유-lock-또는-read-lock">Shared Lock(공유 Lock 또는 Read Lock)</h4>
<p>보통 데이터를 읽을 때 사용한다. 원하는 데이터에 lock을 걸었지만 다른 세션에서 읽을 수 있다. 공유 Lock을 설정한 경우 추가로 공유 Lock을 설정할 수 있지만, 배타적 Lock은 설정할 수 없다. 즉, 내가 보고 있는 데이터는 다른 사용자가 볼 수 있지만, 변경할 수는 없다.</p>
<h4 id="exclusive-lock배타적-lock-또는-write-lock">Exclusive Lock(배타적 Lock 또는 Write lock)</h4>
<p>보통 데이터를 변경할 때 사용한다. 이름처럼 해당 Lock이 해제되기 전까지는 다른 공유 Lock, 배타적 Lock을 설정할 수 없다. 즉, 읽기 기와 쓰기가 불가능하다.</p>
<h3 id="lock의-설정-범위">Lock의 설정 범위</h3>
<h4 id="데이터베이스">데이터베이스</h4>
<ul>
<li>전체 데이터베이스를 기준으로 Lock을 설정한다.</li>
<li>1개의 세션만이 DB의 데이터에 접근이 가능하며 일반적으로 잘 사용하지 않는다.</li>
<li>DB의 소프트웨어 버전을 업그레이드하거나 DB 업데이트를 진행할 경우 사용</li>
</ul>
<h4 id="파일">파일</h4>
<ul>
<li>데이터베이스 파일을 기준으로 Lock을 설정한다.</li>
<li>(파일이란 테이블, row등과 같은 실제 데이터가 쓰여지는 물리적인 저장 공간)</li>
<li>잘 사용되지 않는다.</li>
</ul>
<h4 id="테이블">테이블</h4>
<ul>
<li>테이블을 기준으로 Lock을 설정한다.</li>
<li>테이블의 모든 행을 업데이트 하는 등의 전체 테이블에 영향을 주는 변경을 수행할 때 유용하다.</li>
<li>DDL(CREATE, ALTER, DROP 등) 구문과 함께 사용되며 DDL Lock이라고도 부른다.</li>
</ul>
<h4 id="컬럼">컬럼</h4>
<ul>
<li>컬럼을 기준으로 Lock을 설정한다.</li>
<li>하지만 Lock 설정 및 해제의 리소스가 많이 들기 때문에 잘 사용하지 않고, 지원하는 DBMS도 많지 않다.</li>
</ul>
<h4 id="행">행</h4>
<ul>
<li>1개의 행을 기준으로 Lock을 설정한다.</li>
<li>DML에 대한 Lock으로 가장 기본으로 사용하는 Lock이다.</li>
</ul>
<h3 id="블로킹blocking">블로킹(Blocking)</h3>
<p>Lock 간의 경합(Race Condition)이 발생하여 특정 Transaction이 작업을 진행하지 못하고 멈춰선 상태를 말한다. 공유 락 끼리는 블로킹이 발생하지 않지만, 베타락은 블로킹을 발생시킨다.</p>
<blockquote>
<p>경쟁 상태(Race Condition)이란 두 개 이상의 서로 다른 프로세스(혹은 스레드)들이 하나의 자원(리소스)에 접근하기 위해 경쟁하는 상태를 말한다.</p>
</blockquote>
<p>블로킹을 해소하기 위해서는 이전 트랙잭션이 완료(commit or rollback) 되어야 한다.</p>
<p>뒤에 들어온 트랜잭션은 이전 트랜잭션이 마무리되어야 이후 진행이 가능하다.
성능에 좋지 않은 영향을 미치기 때문에 경합을 최소화해야 한다.
그러므로 트랜잭션 처리 시간을 짧게 가져가는 것이 좋다.</p>
<h3 id="교착상태deadlock">교착상태(DeadLock)</h3>
<p>교착상태는 두 트랜잭션이 각각 Lock을 설정하고 서로의 Lock에 접근하여 값을 얻어오려고 할 때 이미 각각의 트랜잭션에 의해 Lock이 설정되어 있기 때문에 양쪽 트랜잭션 모두 영원히 처리가 되지않게 되는 상태를 의미한다.</p>
<h4 id="해결-방법">해결 방법</h4>
<ol>
<li>Dead Lock이 감지되면 둘 중 하나의 트랜잭션을 강제 종료한다</li>
<li>Dead Lock 방지를 위해 접근 순서 규칙을 정해 접근 순서를 동일하게 한다. </li>
</ol>
<p><strong>Reference</strong>
<a href="https://hstory0208.tistory.com/entry/%EB%9D%BDLock%EC%9D%B4%EB%9E%80-Lock%EC%9D%98-%EC%A2%85%EB%A5%98%EC%99%80-%EA%B5%90%EC%B0%A9%EC%83%81%ED%83%9CDeadLock">https://hstory0208.tistory.com/entry/%EB%9D%BDLock%EC%9D%B4%EB%9E%80-Lock%EC%9D%98-%EC%A2%85%EB%A5%98%EC%99%80-%EA%B5%90%EC%B0%A9%EC%83%81%ED%83%9CDeadLock</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DB] 트랜잭션 격리수준]]></title>
            <link>https://velog.io/@calm0_0/%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EA%B2%A9%EB%A6%AC%EC%88%98%EC%A4%80</link>
            <guid>https://velog.io/@calm0_0/%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EA%B2%A9%EB%A6%AC%EC%88%98%EC%A4%80</guid>
            <pubDate>Tue, 31 Oct 2023 11:06:13 GMT</pubDate>
            <description><![CDATA[<h2 id="트랜잭션-격리-수준transaction-isolation-level">트랜잭션 격리 수준(Transaction Isolation Level)</h2>
<hr>
<p>트랜잭션 격리 수준(Transaction Isolation Level) 이란 여러 트랜잭션이 동시에 처리될 때, 특정 트랜잭션이 다른 트랜잭션에서 변경하거나 조회하는 데이터를 볼 수 있게 허용할지 말지를 결정하는 것을 말한다.</p>
<p>격리 수준은 크게 4가지로 나뉜다.</p>
<ul>
<li>READ UNCOMMITED</li>
<li>READ COMMITTED</li>
<li>REPEATABLE READ</li>
<li>SERIALIZABLE</li>
</ul>
<p>순서대로 뒤로 갈 수록 각 트랜잭션 간 데이터 격리 정도가 높아지며, 동시 처리 성능도 떨어진다.</p>
<br>

<h2 id="read-uncommitted">READ UNCOMMITTED</h2>
<hr>
<p>READ UNCOMMITTED 에서는  트랜잭션에서의 변경 내용이 커밋이나 롤백 여부에 상관없이 다른 트랜잭션에 보여진다.</p>
<p><img src="https://velog.velcdn.com/images/calm0_0/post/c74f34ae-4617-4010-894a-b1c9106a6643/image.png" alt=""></p>
<p>다음과 같이 사용자 A의 트랜잭션 작업이 완료되지 않았는데, 사용자 B가 해당 데이터에 접근하고 있다.</p>
<p>그런데 사용자 A는 커밋이 아닌 롤백을 수행했고, id = 51 의 데이터로 작업 중이던 사용자 B가 다시 데이터를 조회했을 때는 결과가 존재하지 않게 되었다.</p>
<p>이렇게 어떤 트랜잭션에서 처리한 작업이 완료되지 않았는데도 다른 트랜잭션에서 볼 수 있는 현상을 더티 리드(Dirty read) 라고 한다. 더티 리드는 데이터가 나타났다가 사라졌다 하는 현상을 초래하므로 시스템에 상당한 혼란을 준다.</p>
<p>때문에 RDMS 표준에서는 트랜잭션의 격리 수준으로 인정하지 않을 정도로 정합성에 문제가 많으며, 최소한 READ COMMITTED 이상의 격리 수준을 사용할 것을 권장한다.</p>
<br>

<h2 id="read-committed">READ COMMITTED</h2>
<hr>
<p>READ COMMITTED 는 커밋된 데이터만 조회할 수 있는 격리 수준이다. </p>
<p>데이터를 변경했더라도 커밋이 완료된 데이터만 다른 트랜잭션에서 조회할 수 있기 때문에 더티 리드 같은 현상은 발생하지 않는다. </p>
<p><img src="https://velog.velcdn.com/images/calm0_0/post/cfce560a-65a1-4eaa-b5e2-d13aa3935a64/image.png" alt=""></p>
<p>사용자 A가 트랜잭션을 시작하여 데이터를 변경하고 아직 커밋되지 않은 상태이다. 테이블은 먼저 갱신되고 언두 로그로 변경 전의 데이터가 백업된다. </p>
<p>이때 사용자 B가 해당 데이터를 조회하면 READ COMMITTED 에서는 커밋된 데이터만 조회할 수 있으므로, 언두 로그에서 변경 전의 데이터를 찾아서 반환한다. </p>
<p>사용자 A가 트랜잭션을 커밋하면 그때부터 다른 트랜잭션에서도 새롭게 변경된 값을 참조할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/calm0_0/post/a2083fe8-5735-4795-9486-9e05d0cbe0a2/image.png" alt=""></p>
<p>하지만 Non-Repeatable Read(반복 읽기 불가능) 이라는 부정합 문제가 발생한다.</p>
<p>사용자 B가 트랜잭션을 시작하고 name =&quot;Minkyu&quot; 인 레코드를 조회했다. 현재 테이블에는 조건에 맞는 레코드가 없으므로 결과 데이터가 없다.</p>
<p>그러다가 사용자 A가 UPDATE 문을 수행하여 해당 조건을 만족하는 레코드가 생겼고, A는 커밋까지 완료했다.</p>
<p>이때 사용자 B가 다시 동일한 조건으로 레코드를 조회하면, 이번에는 결과가 나오게 된다. 이렇게 반복 읽기를 수행할 때 다른 트랜잭션의 커밋 여부에 따라 조회 결과가 달라지는 현상을 Non-Repeatable Read 라고 한다.</p>
<p>이러한 현상은 일반적으로는 크게 문제가 되지 않을 수 있지만 하나의 트랜잭션에서 동일 데이터를 여러 번 읽고 변경하는 작업이 금전적인 처리와 연결되면 문제가 될 수도 있다. 
예를 들어, 어떤 트랜잭션에서는 오늘 입금된 총합을 계산하고 있는데, 다른 트랜잭션에서 계속해서 입금 내역을 커밋하는 상황이라면, READ COMMITTED 에서는 같은 트랜잭션일지라도 조회할 때마다 입금된 내역이 달라지므로 문제가 생길 수 있다. 
따라서 격리 수준이 어떻게 동작하는지, 어떠한 결과가 나오는지 예측할 수 있어야 한다.</p>
<br>

<h2 id="repeatable-read">REPEATABLE READ</h2>
<hr>
<p>REPEATABLE READ 는 MySQL의 InnoDB 스토리지 엔진에서 기본적으로 사용되는 격리 수준이다. 이 격리 수준에서는 NON-REPEATABLE READ 부정합이 발생하지 않지만, 새로운 레코드가 추가되는 경우에 부정합이 생길 수 있다.</p>
<p>(InnoDB 스토리지 엔진을 사용할 경우 PHANTOM READ 부정합이 발생하지 않는다.)</p>
<p>REPEATABLE READ 는 언두(Undo) 영역에 백업된 이전 데이터를 통해 트랜잭션 내에서는 동일한 결과를 보여 주도록 보장한다. READ COMMITTED 에서도 언두 영역에 백업된 이전 데이터를 보여 주지만, 두 격리 수준에는 언두 영역을 활용하는 방식이 다르다. REPEATABLE READ 격리 수준은 ‘언두 영역에 백업된 레코드의 여러 버전 가운데 몇 번째 버전을 보여 주냐’에 차이가 있다.</p>
<p>쉽게 말하면 언두 영역에 백업된 모든 데이터에는 변경을 발생한 트랜잭션의 번호가 포함되어 있는데, REPEATABLE READ 격리 수준에서는 실행 중인 트랜잭션보다 작은 트랜잭션에서 변경한 데이터만 보게 하여 NON-REPEATABLE READ 문제를 해결한다.</p>
<p><img src="https://velog.velcdn.com/images/calm0_0/post/e26a9d29-24ab-42a0-8dd8-c9939c948355/image.png" alt=""></p>
<p>사용자 B의 트랜잭션은(10) 사용자 A의 트랜잭션(12)이 시작하기 전에 이미 시작되었다. 이때 REPEATABLE READ 에서는 트랜잭션 번호를 참고하여 자신보다 먼저 실행된 트랜잭션의 데이터만을 조회한다. 때문에 동일한 결과를 반환하고 있다.</p>
<p>앞서 언급했듯이 REPEATABLE READ 는 새로운 레코드의 추가까지는 막지 않는다. 따라서 SELECT 로 조회한 경우 트랜잭션이 끝나기 전에 다른 트랜잭션에 의해 추가된 레코드가 발견될 수 있는데, 이를 유령 읽기(Phantom Read) 라고 한다. 하지만 MVCC 덕분에 일반적인 조회에서 Phantom Read 는 발생하지 않는다. 왜냐하면 자신보다 나중에 실행된 트랜잭션이 추가한 레코드는 무시하면 되기 때문이다. </p>
<p>그렇다면 Phantom Read 가 발생하는 상황은 언제일까? 바로 잠금이 사용되는 경우이다.</p>
<p><img src="https://velog.velcdn.com/images/calm0_0/post/332c0d09-53da-4748-a27e-3cb706aa8227/image.png" alt=""></p>
<p>SELECT … FOR UPDATE 구문은 베타적 잠금(비관적 잠금, 쓰기 잠금)을 거는 것이다. 읽기 잠검을 걸려면 SELECT FOR SHARE 구문을 사용한다. 잠금은 트랜잭션이 커밋 또는 롤백될 때 해제된다. </p>
<p>사용자 B가 데이터를 조회하는데 SELECT FOR UPDATE를 이용해 쓰기 잠금을 걸었다. 그리고 사용자 A가 새로운 레코드를 INSERT 하고 커밋했다. id = 50인 레코드만 잠금이 걸린 상태이고, 사용자 A의 요청은 잠금 없이 즉시 실행된다. </p>
<p>이때 사용자 B가 동일한 쓰기 잠금 쿼리로 다시 한번 데이터를 조회하면, 이번에는 2건의 데이터가 조회된다. 동일한 트랜잭션 내에서도 새로운 레코드가 추가되는 경우에 조회 결과가 달라지는데, 이렇듯 다른 트랜젹션에서 수행한 작업에 의해 레코드가 안보였다 보였다 하는 현상을 Phantom Read(유령 읽기)라고 한다. </p>
<p>SELECT FOR UPDATE 쿼리는 SELECT 하는 레코드에 쓰기 잠금을 걸어야 하는데, 언두 레코드는 잠글 수 없다. 따라서 SELECT FOR UPDATE나 SELECT FOR SHARE로 레코드를 조회하는 경우에는 언두 영역의 데이터가 아니라 테이블의 레코드를 가져오게 되고, 이로 인해 Phaontom Read 가 발생하는 것이다.</p>
<br>

<h2 id="serializable">SERIALIZABLE</h2>
<hr>
<p>SERIALIZABLE 은 가장 엄격한 격리 수준으로 트랜잭션을 순차적으로 진행시킨다.</p>
<p>읽기 작업도 공유 장금(읽기 잠금)을 획득해야만 하며, 여러 트랜잭션이 동일한 레코드에 동시에 접근할 수 없다.</p>
<p>때문에 어떠한 데이터 부정합 문제도 발생하지 않지만 트랜잭션이 순차적으로 처리되어야 하므로 동시 처리 성능이 매우 떨어진다.</p>
<p>가장 안전하지만 가장 성능이 떨어지므로 극단적으로 안전한 작업이 필요한 경우가 아니라면 사용하지 않는다.</p>
<br>

<h2 id="요약">요약</h2>
<hr>
<p><img src="https://velog.velcdn.com/images/calm0_0/post/e88b058b-bd5d-48c9-ae15-ee92443ac0b5/image.PNG" alt=""></p>
<br>
<br>

<p><strong>Reference</strong>
<a href="https://mangkyu.tistory.com/299">https://mangkyu.tistory.com/299</a>
<a href="https://steady-coding.tistory.com/562">https://steady-coding.tistory.com/562</a>
<a href="https://www.yes24.com/Product/Goods/103415627">Real MySQL 8.0 (1권)</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[MySQL] InnoDB 스토리지 엔진]]></title>
            <link>https://velog.io/@calm0_0/InnoDB-%EC%8A%A4%ED%86%A0%EB%A6%AC%EC%A7%80-%EC%97%94%EC%A7%84</link>
            <guid>https://velog.io/@calm0_0/InnoDB-%EC%8A%A4%ED%86%A0%EB%A6%AC%EC%A7%80-%EC%97%94%EC%A7%84</guid>
            <pubDate>Tue, 31 Oct 2023 11:05:37 GMT</pubDate>
            <description><![CDATA[<h2 id="mysql-서버의-구조">MySQL 서버의 구조</h2>
<hr>
<p>MySQL 서버는 크게 MySQL 엔진과 스토리지 엔진으로 구성되어 있다. </p>
<p>MySQL 엔진은 요청된 SQL문을 분석하고 최적화하는 등 데이버테이스의 두뇌에 해당하는 역할을 하고, 실제 데이터를 디스크에 저장하거나 데이터를 읽어오는 작업은 스토리지 엔진이 담당한다.</p>
<h3 id="mysql-엔진">MySQL 엔진</h3>
<p>MySQL 엔진은</p>
<ul>
<li>클라이언트로부터 커넥션 및 쿼리 요청을 처리하는 <strong>커넥션 핸들러</strong>,</li>
<li>SQL 문법 오류를 탐지하고 SQL문을 MySQL이 인식할 수 있는 토큰 단위로 나눠 파싱하는 <strong>SQL 파서</strong>,</li>
<li>쿼리의 최적화된 실행 계획을 수립하는 <strong>옵티마이저</strong></li>
</ul>
<p>등으로 이루어져 있다.</p>
<h3 id="스토리지-엔진">스토리지 엔진</h3>
<ul>
<li>실제 데이터를 디스크에 저장하거나 조회하는 부분을 담당한다.</li>
<li>스토리지 엔진은 MySQL 엔진과 플러그인 형태로 연동/분리가 가능하다.</li>
<li>MySQL 서버에서 MySQL 엔진은 하나지만 스토리지 엔진은 동시에 여러 개를 사용할 수 있다.</li>
<li>InnoDB, IyISAM, MEMORY 등이 있다.</li>
</ul>
<h3 id="핸들러-api">핸들러 API</h3>
<p>MySQL 엔진의 쿼리 실행기에서 데이터를 쓰거나 읽어야 할 때 스토리지 엔진에 보내는 요청을 핸들러 요청이라 하고, 이 때 사용되는 API를 <strong>핸들러 API</strong> 라고 한다.</p>
<p>즉, MySQL 엔진과 스토리지 엔진은 핸들러 API를 통해 데이터를 주고 받는다.</p>
<h3 id="쿼리-실행-구조">쿼리 실행 구조</h3>
<ul>
<li>사용자의 요청으로 들어온 쿼리 문장을 <strong>쿼리 파서</strong>를 통해 MySQL이 인식할 수 있는 토큰으로 분리해 트리 형태의 구조로 만든다. (쿼리 문장의 기본 문법 오류는 이 과정에서 발견된다.)</li>
<li><strong>전처리기</strong>에서 파서 과정에서 만들어진 파서 트리를 기반으로 쿼리 문에 구조적 문제점이 있는지 확인한다. 해당 객체의 존재 여부나 접근 권한 등을 확인하는 과정을 수행한다. (없는 테이블, 칼럼에 접근하는 등의 오류는 이 단계에서 걸러진다.)</li>
<li><strong>옵티마이저</strong>를 통해 쿼리 문장을 저렴한 비용으로 가장 빠르게 처리할지를 결정한다.</li>
<li>실행 엔진이 옵티마이저에서 수립된 실행 계획대로 각 핸들러에게 지시한다. (ex. 임시 테이블 만들어 -&gt; WHERE 절과 일치하는 레코드 읽어와 -&gt; 읽은 레코드들을 임시 테이블에 저장해 -&gt; 임시 테이블에서 데이터 읽어와...)</li>
<li>핸들러(=스토리지 엔진)은 받은 지시대로 작업을 수행한다.</li>
</ul>
<br>

<h2 id="innodb-스토리지-엔진">InnoDB 스토리지 엔진</h2>
<hr>
<p>InnoDB 스토리지 엔진은 MySQL에서 가장 많이 사용되는 스토리지 엔진이다. MySQL의 스토리지 엔진 중에서 거의 유일하게 레코드 기반 잠금을 제공하며, 때문에 높은 동시성 처리가 가능하고 안정적이며 성능이 뛰어나다.</p>
<p><img src="https://velog.velcdn.com/images/calm0_0/post/b8129d07-7677-4619-bcb5-6f22634b0dae/image.png" alt=""></p>
<h3 id="innodb의-특징">InnoDB의 특징</h3>
<h4 id="프라이머리-키에-의한-클러스터링">프라이머리 키에 의한 클러스터링</h4>
<ul>
<li>InnoDB의 모든 테이블은 기본적으로 프라이머리 키를 기준으로 클러스터링되어 자장된다.</li>
<li>즉, 프라이머리 키 값의 순서대로 디스크에 저장된다.</li>
<li>모든 세컨더리 인덱스는 레코드 주소 대신 프라이머리 키의 값을 논리적인 주소로 사용한다.</li>
<li>프라미어리 키가 클러스터링 인덱스이기 때문에 프라이머리 키를 이용한 레인지 스캔이 상당히 빨리 처리될 수 있다.</li>
<li>때문에 쿼리 실행 계획에서 다른 보조 인덱스보다 프라이머리 키가 선택될 확률이 높다.</li>
</ul>
<h4 id="외래-키-지원">외래 키 지원</h4>
<ul>
<li>InnoDB 스토리지 엔진 레벨에서만 지원하는 기능으로 MyISAM이나 MEMORY 테이블에서는 사용할 수 없다.</li>
<li>외래 키는 여러 제약사항 때문에 서비스용 데이터베이스에서는 생성하지 않는 경우도 있지만, 개발 환경의 데이터베이스에서는 좋은 가이드 역할을 할 수 있다.</li>
</ul>
<h4 id="mvccmulti-version-concurrency-control">MVCC(Multi Version Concurrency Control)</h4>
<p>MVCC는 하위 레코드에 대해 여러 개의 버전이 동시에 관리된다는 의미이며, 시스템에서 설정한 트랜잭션 격리 수준에 따라 다르게 처리된다.</p>
<p>예를 들어, 다음과 같이 테이블에 한 건의 레코드를 변경한다면</p>
<pre><code class="language-mysql">UPDATE member 
SET m_area = &#39;경기&#39; 
WHERE m_id = 12;</code></pre>
<p>InnoDB 의 버퍼 풀과 데이터 파일엔 다음과 같이 기록되어 있다.</p>
<ul>
<li>버퍼 풀) id = 12, ..., m_area = &#39;경기&#39; (업데이트 되어있음)</li>
<li>언두 로그) id = 12, m_area = &#39;서울&#39; (변경 전 값만 언두 로그로 복사)</li>
<li>데이터 파일) id = 12, ..., m_area = ? (체크포인트나 Write 스레드에 의해 업데이트 되어있을 수도 있고 아닐 수도 있음)</li>
</ul>
<p>아직 커밋이나 롤백되지 않은 상태에서 다른 사용자가 해당 레코드를 조회한다면..</p>
<p>격리 수준이 READ_UNCOMMITTED 라면 -&gt; 버퍼 풀이 현재 가지고 있는 변경된 데이터를 읽어서 반환한다.
그 이상의 격리 수준이라면 (READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE) -&gt; 아직 커밋되지 않았기 때문에 변경 이전의 내용을 보관하고 있는 언두 영역의 데이터를 반환한다.</p>
<h4 id="잠금-없는-일관된-읽기non-locking-consistent-read">잠금 없는 일관된 읽기(Non-Locking Consistent Read)</h4>
<p>앞에서 살펴본 MVCC 기술을 이용해 잠금을 걸지 않고 읽기 작업을 수행한다. </p>
<p><img src="https://velog.velcdn.com/images/calm0_0/post/47265007-bf03-42e3-98cf-060243af4978/image.png" alt=""></p>
<p>격리 수준이 SERIALIZABLE 이 아니라면 순수한 읽기(SELECT) 작업은 다른 트랜잭션의 변경 작업과 관계없이 항상 잠금을 대기하지 않고 바로 실행된다.</p>
<p>다음과 같이 특정 사용자가 레코드를 변경하고 아직 커밋하지 않아도, 이 변경 트랜잭션이 다른 사용자의 SELECT 작업을 방해하지 않는다. 이를 <strong>잠금 없는 일관된 읽기</strong> 라고 표현하며, InnoDB에서는 변경 전 데이터를 읽기 위해 언두 로그를 사용한다.</p>
<h4 id="자동-데드락-감지">자동 데드락 감지</h4>
<p>내부적으로 잠금이 교착 상태에 빠지지 않았는지 체크하기 위해 잠금 대기 목록을 그래프 형태로 관리한다.</p>
<p>데드락 감지 스레드를 통해 주기적으로 잠금 대기 그래프를 검사해 교착 상태에 빠진 트랜잭션들을 찾아서 그 중 하나를 강제 종료한다.</p>
<p>이때, 언두 로그를 더 적게 가진 트랜잭션을 먼저 종료한다. (언두 레코드가 적다 = 롤백할 때 언두를 처리할 내용이 적다 -&gt; 트랜잭션 강제 롤백으로 인한 서버의 부하도 덜 하다)</p>
<h4 id="자동화된-장애-복구">자동화된 장애 복구</h4>
<p>InnoDB에는 손실이나 장애로부터 데이터를 보호하기 위한 여러 메커니즘이 탑재되어 있다. </p>
<p>이러한 메커니즘을 활용해 MySQL 서버가 시작될 때 완료되지 못한 트랜잭션이나 디스크에 일부만 기록되 데이터 페이지 등에 대한 복구 작업이 자동으로 진행된다.</p>
<p>자동으로 복구될 수 없는 손상이 있다면 자동 복구를 멈추고 MySQL 서버가 종료된다.</p>
<h3 id="innodb-버퍼-풀">InnoDB 버퍼 풀</h3>
<p>InnoDB 스토리지 엔진에서 가장 핵심적인 부분으로, 디스크의 데이터 파일이나 인덱스 정보를 메모리에 캐시해 두는 공간이다.</p>
<p>쓰기 작업을 지연시켜 일괄 작업으로 치리할 수 있게 하는 버퍼 역할도 같이 한다.</p>
<h3 id="언두-로그">언두 로그</h3>
<p>트랜잭션과 격리 수준을 보장하기 위해 DML(INSERT, UPDATE, DELETE)로 변경되기 이전 버전의 데이터를 별도로 백업해둔다. 이렇게 백업된 데이터를 <strong>언두 로그(Undo Log)</strong> 라고 한다.</p>
<ul>
<li>트랜잭션 보장 : 트랜잭션이 롤백되면 언두 로그에 백업해둔 이전 버전의 데이터를 이용해 복구한다.</li>
<li>격리 수준 보장 : 특정 커넥션에서 데이터를 변경하는 중에 다른 커넥션에서 데이터를 조회하면 격리 수준에 맞게 변경 중인 레코드를 읽지 않고 언두 로그에 백업해둔 데이터를 읽어서 반환하기도 한다.</li>
</ul>
<p>언두 로그는 InnoDB 에서 매우 중요한 역할을 담당하지만 관리 비용도 많이 필요하다.</p>
<h3 id="체인지-버퍼">체인지 버퍼</h3>
<p>레코드가 INSERT 되거나 UPDATE 될 때는 데이터 파일을 변경하는 작업 + 해당 테이블에 포함된 인덱스를 업데이트하는 작업이 필요하다. 그런데 인덱스를 업데이트하는 작업은 랜덤하게 디스크를 읽는 작업이 필요하므로 테이블에 인덱스가 많다면 이 작업은 상당히 많은 자원을 소모하게 된다.</p>
<p>InnoDB 는 변경해야 할 인덱스 페이지가 버퍼 풀에 있으면 바로 업데이트를 수행하지만 디스크로부터 읽어와서 업데이트해야 한다면 이를 즉시 실행하지 않고 임시 공간에 저장해 두고 바로 사용자에게 결과를 반환하는 형태로 성능을 향상시키는데 이때 사용하는 임시 메모리 공간을 체인지 버퍼(Change Buffer)라고 한다.</p>
<h3 id="리두-로그">리두 로그</h3>
<p>MySQL 서버가 비정상적으로 종료되었을 때 데이터 파일에 기록되지 못한 데이터를 잃지 않게 해주는 안전장치 역할을 한다.</p>
<h3 id="어댑티브-해시-인덱스">어댑티브 해시 인덱스</h3>
<p>사용자가 직접 설정한 인덱스가 아닌 InnoDB 에서 사용자가 자주 요청하는 데이터에 대해 자동으로 생성하는 인덱스이다.</p>
<p>B-Tree의 검색 시간을 줄이기 위해 도입된 기능이다. 자주 읽히는 데이터 페이지의 키 값을 이용해 해시 인덱스를 만들고 필요할 때마다 어댑티브 해시 인덱스를 검색해 레코드가 저장된 데이터 페이지를 즉시 찾아갈 수 있게 해 준다. (B-Tree 처럼 루트 노드부터 리프 노드까지 찾아가는 비용이 없어지고 쿼리 성능이 빨라짐)</p>
<p>어뎁티브 해시 인덱스는 버퍼 풀에 올려진 데이터 페이지에 대해서만 관리되고, 버퍼 풀에서 해당 데이터 페이지가 없어지면 어댑티브 해시 인덱스에서도 해당 페이지의 정보는 사라진다.</p>
<p>성능 향상에 크게 도움이 되지 않는 경우</p>
<ul>
<li>디스크 읽기가 많은 경우</li>
<li>특정 패턴의 쿼리가 많은 경우(LIKE 패턴 검색이나 조인)</li>
<li>매우 큰 데이터를 가진 테이블의 레코드를 폭넓게 읽는 경우</li>
</ul>
<p>성능 향상에 도움이 되는 경우</p>
<ul>
<li>디스크의 데이터가 InnoDB 버퍼 풀 크기와 비슷한 경우(디스크 읽기가 많지 않은 경우)</li>
<li>동등 조건 검색(동등 비교와 IN 연산자)이 많은 경우</li>
<li>쿼리가 데이터 중에서 일부 데이터에만 집중되는 경우</li>
</ul>
<br>
<br>

<p><strong>Reference</strong>
<a href="https://www.yes24.com/Product/Goods/103415627">Real MySQL 8.0 (1권)</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[NestJS] e2e test 환경에서 Auth Guard에 대한 처리]]></title>
            <link>https://velog.io/@calm0_0/NestJS-e2e-test-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-Auth-Guard%EC%97%90-%EB%8C%80%ED%95%9C-%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@calm0_0/NestJS-e2e-test-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-Auth-Guard%EC%97%90-%EB%8C%80%ED%95%9C-%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Fri, 27 Oct 2023 11:29:24 GMT</pubDate>
            <description><![CDATA[<h2 id="막혔던-부분">막혔던 부분</h2>
<hr>
<p>구현하고 있던 NestJS 프로젝트에서 e2e 테스트 코드를 작성하고 있었다. </p>
<p>다음과 같이 Guard를 통해 로그인한 사용자만이 접근할 수 있는 엔드 포인트에 대해 처리를 해야 했다.</p>
<pre><code class="language-typescript">  @Post()
  @UseGuards(JwtAuthGuard)
  async writeComment(
    @Body() createCommentDto: CreateCommentDto,
    @GetUser() user: User,
  ) {
    const newComment = await this.commentsService.writeComment(createCommentDto, user);

    return newComment;
  }</code></pre>
<p>현재 프로젝트에서는 인증에 passport 모듈을 사용하고 있었고, 소셜 로그인(Google OAuth 2.0) 위한 Strategy, 사용자 인증을 위한 JWT Strategy, 토큰 재발급을 위한 Strategy 가 있었고 필요한 엔드 포인트에 알맞는 Guard 를 적용했다.</p>
<p>그런데 테스트 환경에서 로컬 로그인이 아닌 소셜 로그인 과정을 어떻게 접근하고 처리해야 할지 어려웠던 것 같다. </p>
<p>그래서 해결 방안을 찾아보던 중 Guard 를 오버라이드할 수 있다는 것을 알게 되었고 이를 적용해보았다.</p>
<br>

<h2 id="해결-방법">해결 방법</h2>
<hr>
<pre><code class="language-typescript">  beforeAll(async () =&gt; {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    })
    .overrideGuard(JwtAuthGuard)
    .useValue(mockAuthGuard)
    .compile();

    app = moduleFixture.createNestApplication();
    setUpTestingAppModule(app);
    await app.init();
    ...
  });</code></pre>
<p>다음과 같이 생성한 테스팅 모듈에 .overrideGuard() 메서드로 Guard 를 오버라이드 할 수 있다.</p>
<pre><code class="language-typescript">import { ExecutionContext } from &quot;@nestjs/common&quot;;

export const mockAuthGuard = {
  canActivate: (context: ExecutionContext) =&gt; {
    const request = context.switchToHttp().getRequest();
    request[&#39;user&#39;] = { id: 1 };
    return true;
  }
}</code></pre>
<p>NestJS 의 Guard 는 canActivate() 를 구현해야 한다. 따라서 해당 메서드를 오버라이드하는 객체 안에 포함시키고, 기존 Auth Guard 에서 requeest 객체에 user 정보를 넣어주던 작업을 수행하고 리턴했다.</p>
<p>이제 테스팅 모듈의 app 객체로 받는 요청은 JwtAuthGuard 인증을 통과한 채로 전달되었고, 이제 사용자 인증이 필요한 엔드 포인트에 대해 인증이 된 상태로 테스트할 수 있게 되었다.</p>
<br>
<br>

<p><strong>Reference</strong>
<a href="https://stackoverflow.com/questions/57629191/nestjs-mock-jwt-authentication-in-e2e-tests">https://stackoverflow.com/questions/57629191/nestjs-mock-jwt-authentication-in-e2e-tests</a>
<a href="https://velog.io/@atoye1/Nest.js-e2e-test%EC%97%90%EC%84%9C-authGuard-%EC%9A%B0%ED%9A%8C%ED%95%98%EA%B8%B0">https://velog.io/@atoye1/Nest.js-e2e-test%EC%97%90%EC%84%9C-authGuard-%EC%9A%B0%ED%9A%8C%ED%95%98%EA%B8%B0</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Jest] Exceeded timeout 에러, Database 관련 에러 해결 ]]></title>
            <link>https://velog.io/@calm0_0/Jest-Exceeded-timeout-%EC%97%90%EB%9F%AC-Database-%EA%B4%80%EB%A0%A8-%EC%97%90%EB%9F%AC-%ED%95%B4%EA%B2%B0</link>
            <guid>https://velog.io/@calm0_0/Jest-Exceeded-timeout-%EC%97%90%EB%9F%AC-Database-%EA%B4%80%EB%A0%A8-%EC%97%90%EB%9F%AC-%ED%95%B4%EA%B2%B0</guid>
            <pubDate>Fri, 27 Oct 2023 09:39:43 GMT</pubDate>
            <description><![CDATA[<p>NestJS 로 진행 중인 프로젝트에서 Layer 테스트 코드를 작성하였다. 전체적으로 테스트를 돌리던 중에 다음 에러가 발생했다.</p>
<p><img src="https://velog.velcdn.com/images/calm0_0/post/a8ad9196-2272-4c94-bf84-e6c3992dd583/image.PNG" alt=""></p>
<p>특정 테스트에서 기본 설정된 timeout 을 넘겨서 발생한 에러인 것 같다. package.json 에서 테스트 스크립트에 timeout 을 넉넉하게 설정했다.</p>
<pre><code class="language-json">  &quot;scripts&quot;: {
    ...
    &quot;test&quot;: &quot;jest --testTimeout=10000&quot;,
    ...
  },</code></pre>
<p>timeout 관련 에러는 해결되었지만 테스트가 불규칙하게 실패하고 다음과 같이 데이터베이스 관련 에러가 보였다.</p>
<p><img src="https://velog.velcdn.com/images/calm0_0/post/d865e2e8-4862-4b1e-9336-1d8d8707fc3a/image.PNG" alt=""></p>
<p>현재 리포지토리 테스트는 직접 데이터베이스와 연동하여 테스트하고 있었고, 테스트마다 데이터베이스를 drop 하고 있었다. </p>
<p>테스트는 병렬적으로 실행되는데 테스트 코드에는 데이터베이스를 초기화하고 데이터를 넣고, 삭제하는 작업이 있어 데이터베이스 싱크가 맞지 않아 불규칙하게 실패했던 것이다.</p>
<pre><code class="language-json">  &quot;scripts&quot;: {
    ...
    &quot;test&quot;: &quot;jest --testTimeout=10000 --runInBand&quot;,
    ...</code></pre>
<p>다음과 같이 test scripts 에 --runInBand 옵션을 넣어주었다. 해당 옵션은 테스트를 실행하는 하위 프로세스의 작업자 풀을 만들지 않고 현재 프로세스에서 모든 테스트를 순차적으로 실행한다. 때문에 더 이상 데이터베이스 싱크가 맞지 않아 테스트가 실패하지 않았다.</p>
<p><img src="https://velog.velcdn.com/images/calm0_0/post/ef8e4815-9e5e-4e2e-9408-dd4042019141/image.PNG" alt=""></p>
<p>이제 테스트가 정상적으로 작동했다.</p>
<br>
<br>

<p><strong>Reference</strong>
<a href="https://bobbyhadz.com/blog/jest-exceeded-timeout-of-5000-ms-for-test">https://bobbyhadz.com/blog/jest-exceeded-timeout-of-5000-ms-for-test</a>
<a href="https://runebook.dev/ko/docs/jest/cli">https://runebook.dev/ko/docs/jest/cli</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WEB] WAS 와 WS 의 차이]]></title>
            <link>https://velog.io/@calm0_0/WEB-WAS-%EC%99%80-WS-%EC%9D%98-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@calm0_0/WEB-WAS-%EC%99%80-WS-%EC%9D%98-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Fri, 27 Oct 2023 08:05:48 GMT</pubDate>
            <description><![CDATA[<p>WS(Web Server)는 클라이언트로부터 HTTP 요청을 받아 정적인 컨텐츠를 제공하고, WAS(Web Application Server)는 동적인 컨텐츠를 제공한다. </p>
<p>WS 와 WAS를 살펴보기 전에 정적 페이지와 동적 페이지에 대해 알아보자.</p>
<h2 id="정적-페이지와-동적-페이지">정적 페이지와 동적 페이지</h2>
<hr>
<h3 id="정적-페이지satatic-pages">정적 페이지(Satatic Pages)</h3>
<ul>
<li>정적 페이지(Static Pages)는 데이터베이스에서 정보를 가져오거나 별도의 서버에서의 처리 작업이 없이도 사용자들에게 보여지는 페이지.</li>
<li>어떠한 사용자가 접속하던 간에 동일한 페이지를 보여준다. (대부분 웹 사이트의 홈 화면에 접속하면 서로가 같은 페이지를 본다.)</li>
<li>ex) html, css, js, image 파일과 같이 컴퓨터에 저장되어 있는 파일들</li>
</ul>
<h3 id="동적-페이지dynamic-pages">동적 페이지(Dynamic Pages)</h3>
<ul>
<li>서버가 데이터베이스에서 정보를 가져와서 처리하는 것처럼, 어떠한 요청에 의하여 서버가 일을 수행하고 해당 결과가 포함된 파일을 보여주는 페이지.</li>
<li>사용자마다 다른 페이지가 보여질 수 있다. (예를 들어 사용자 프로필 페이지는 유저마다 다른 결과 화면이 보여진다.) </li>
</ul>
<br>

<h2 id="web-server">Web Server</h2>
<hr>
<p><img src="https://velog.velcdn.com/images/calm0_0/post/bad68352-0eb9-4c3a-8021-2d15ac1ef799/image.jpeg" alt=""></p>
<ul>
<li>웹 서버(Web Server)는 클라이언트로부터 HTTP 요청을 받아 정적인 컨텐츠를 제공하는 서버를 말한다.</li>
<li>다른 역할로는 동적인 컨텐츠가 필요한 요청이 클라이언트로부터 들어왔을 때, 해당 요청을 WAS에 보내고, WAS가 처리한 결과를 클라이언트에 응답한다.</li>
<li>ex) Apache, Nginx 등</li>
</ul>
<br>

<h2 id="web-application-server">Web Application Server</h2>
<hr>
<ul>
<li>웹 애플리케이션 서버(Web Application Server)는 DB 조회나 다양한 로직 처리를 요구하는 동적인 컨텐츠를 제공하기 위해 만들어진 Appication Server 이다.</li>
<li>Web Server + Web Container<ul>
<li>(웹 컨테이너는 동적인 데이터들을 처리하여 정적인 페이지로 생성해주는 소프트웨어 모듈)</li>
</ul>
</li>
<li>프로그램 실행 환경과 DB 접속 기능을 제공, 비지니스 로직을 수행</li>
<li>ex) Tomcat 등 </li>
</ul>
<p><img src="https://velog.velcdn.com/images/calm0_0/post/c2cfbdc2-b4a5-4dd8-b2b1-47a7ee9d197f/image.jpeg" alt=""></p>
<p>node.js 환경을 예로 들면, </p>
<ul>
<li>클라이언트가 /myinfo 경로로 요청한다.</li>
<li>WAS에서 DB에 접근하여 필요한 데이터를 가져온다.</li>
<li>원하는 데이터를 가공하여 myinfo.ejs 파일로 해당 데이터를 보내준다.</li>
<li>ejs 파일을 html 로 바꿔준 다음에 웹 서버로 전송한다.</li>
<li>웹 서버는 절달받은 html 요소를 클라이언트에 보내준다.</li>
</ul>
<br>

<h2 id="ws-와-was-를-구분하는-이유">WS 와 WAS 를 구분하는 이유</h2>
<hr>
<h3 id="web-server-가-필요한-이유">Web Server 가 필요한 이유</h3>
<p>예를 들어, 이미지 파일과 같은 정적 파일들은 HTML 문서가 플라이언트로 보내질 때 함께 가지 않는다.</p>
<p>클라이언트는 HTML 문서를 먼저 받고, 그에 맞게 필요한 이미지 파일들을 다시 버서로 요청하고 나서야 이미지 파일을 받아온다.</p>
<p>웹 서버를 통해 정적 파일들을 애플리케이션 서버까지 가지 않고 앞단에서 빠르게 보내줄 수 있다.</p>
<p>따라서 웹 서버에서는 정적 컨텐츠만 처리하도록 기능을 분배하여 서버의 부담을 줄일 수 있다.</p>
<h3 id="web-application-server-가-필요한-이유">Web Application Server 가 필요한 이유</h3>
<p>웹 페이지는 정적 컨텐츠와 동적 컨텐츠가 모두 존재하는데, 웹 서버만을 사용한다면 유저가 원하는 요청에 대한 결과값을 모두 미리 만들어 놓고 서비스를 해야 한다. 이는 비효율적이다.</p>
<p>따라서 WAS를 통해 요청에 맞는 데이터를 DB에서 가져와 비지니스 로직에 맞게 그때 그때 결과를 만들어 제공함으로써 자원을 효율적으로 사용할 수 있다.</p>
<h3 id="웹-서버와-웹-애플리케이션을-조합하여-사용한다면">웹 서버와 웹 애플리케이션을 조합하여 사용한다면</h3>
<ul>
<li>기능을 분리하여 서버의 부하를 줄일 수 있다.<ul>
<li>WAS는 DB 조회나 다양한 로직을 처리하느라 바쁘기 때문에 단순한 정적 컨텐츠는 웹 서버에서 빠르게 클라이언트에 제공하는 것이 좋다.</li>
</ul>
</li>
<li>물리적으로 분리하여 보안 강화<ul>
<li>SSL 에 대한 암복호화 처리를 웹 서버에서 수행</li>
</ul>
</li>
<li>여러 대의 WAS를 연결 가능<ul>
<li>로드 밸런싱을 위해서 웹 서버를 사용</li>
</ul>
</li>
<li>여러 웹 애플리케이션 서비스 가능<ul>
<li>예를 들어, 하나의 서버에 PHP 애플리케이션, Java 애플리케이션을 함께 사용하는 경우</li>
</ul>
</li>
<li>접근 허용 IP 관리, 2대 이상의 서버에서의 세션 관리 등도 웹 서버에서 처리하면 효율적이다.</li>
</ul>
<br>
<br>

<p><strong>Reference</strong>
<a href="https://melonicedlatte.com/web/2019/06/23/210300.html">https://melonicedlatte.com/web/2019/06/23/210300.html</a>
<a href="https://gmlwjd9405.github.io/2018/10/27/webserver-vs-was.html">https://gmlwjd9405.github.io/2018/10/27/webserver-vs-was.html</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[NestJS] module에 환경 변수 사용하기]]></title>
            <link>https://velog.io/@calm0_0/NestJS-module%EC%97%90-%ED%99%98%EA%B2%BD-%EB%B3%80%EC%88%98-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@calm0_0/NestJS-module%EC%97%90-%ED%99%98%EA%B2%BD-%EB%B3%80%EC%88%98-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 25 Oct 2023 23:06:06 GMT</pubDate>
            <description><![CDATA[<p>NestJS에서 환경 변수에 접근할 때 dotenv를 사용하다가 아래와 같이 ConfigModule 을 사용하려고 했다.</p>
<pre><code class="language-typescript">@Module({
  imports: [
    ConfigModule.forRoot(configModuleOptions),
    TypeOrmModule.forRoot({
      type: &#39;mysql&#39;,
      host: configService.get(&#39;DB_HOST&#39;),
      port: +configService.get&lt;number&gt;(&#39;DB_PORT&#39;),
      username: configService.get(&#39;DB_USERNAME&#39;),
      database: configService.get(&#39;DB_DATABASE&#39;),
      password: configService.get(&#39;DB_PASSWORD&#39;),
      entities: [__dirname + &#39;/**/*.entity{.ts,.js}&#39;],
      synchronize: false,  
    }),
    ...
  ],
})

export class AppModule {}</code></pre>
<p>하지만 환경 변수 값을 읽지 못하고 에러가 발생했다. 애플리케이션이 bootstraping 될 때, 즉 모듈의 생성 시점에는 config module을 사용할 수 없었고, 그래서 환경 변수 값을 불러오지 못했던 것이다. </p>
<p>애플리케이션이 bootstraping 될 때 TypeOrmModule 에서 동적 구성을 허용할 수 있도록 forRoot() 대신 forRootAsync() 를 사용함으로써 해결했다.</p>
<pre><code class="language-typescript">@Module({
  imports: [
    ConfigModule.forRoot(configModuleOptions),
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      useClass: TypeOrmConfigService,
      inject: [ConfigService],
    }),
    ...
  ],
})

export class AppModule {}</code></pre>
<p>TypeOrmModule 에서 ConfigModule 을 imports, ConfigService 를 inject 하고 있다. 이는 ConfigModule을 직접 사용하면서, useClass 의 TypeOrmConfigService 에서 설정을 반환하는데, 내부에서 ConfigService 를 사용하겠다는 것이다. 이렇게 useClass 키워드로 모듈을 동적으로 만들어 문제를 해결했다.</p>
<br>
<br>

<p><strong>Reference</strong>
<a href="https://kscodebase.tistory.com/553">https://kscodebase.tistory.com/553</a>
<a href="https://4sii.tistory.com/417">https://4sii.tistory.com/417</a>
<a href="https://stackoverflow.com/questions/52570212/nestjs-using-configservice-with-typeormmodule">https://stackoverflow.com/questions/52570212/nestjs-using-configservice-with-typeormmodule</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[AWS] 프로젝트에 ELB 적용하기]]></title>
            <link>https://velog.io/@calm0_0/TIL-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90-ELB-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@calm0_0/TIL-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90-ELB-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 25 Oct 2023 19:51:31 GMT</pubDate>
            <description><![CDATA[<p>진행하던 프로젝트에서 ELB를 사용해보았다. 프로젝트를 진행하면서 지금까지의 아키텍처 구성은 다음과 같다.</p>
<p><img src="https://velog.velcdn.com/images/calm0_0/post/0dddd40d-6d34-49ab-80c1-aebe110f337e/image.PNG" alt=""></p>
<p>로드벨런서는 AWS의 ELB 중 ALB(Application Load Balancer)를 사용하였고, Route 53에서 도메인을 ALB에 연결했다.</p>
<p>로드 밸런서에서는 ACM에서 제공하는 SSL 인증서를 발급받아 HTTPS 적용했다. 서버 앞단의 로드 밸런서에서 HTTPS로 요청을 받기 때문에 각 인스턴스에서 일일이 HTTPS를 위한 설정을 하지 않아도 되었다.</p>
<p><img src="https://velog.velcdn.com/images/calm0_0/post/1286bc4e-4704-4c6e-8e18-54cc149ce31c/image.PNG" alt=""></p>
<p>다음과 같이 Target Group에 두 개의 인스턴스를 등록하고 로드 밸런서와 연결했다.</p>
<p>대상 그룹의 헬스 체크, 모니터링 탭에서 대상 그룹에 속한 인스턴스의 상태를 확인할 수 있다. </p>
<p>로드 밸런서에서 트래픽이 적절히 분산되고 있는지 직접 확인해보기 위해 헬스 체크용 API에 IP 주소를 반환하도록 하고 로드 밸런서의 도메인으로 여러 번 접속해보았다.</p>
<p><img src="https://velog.velcdn.com/images/calm0_0/post/6f45f695-205d-4462-bae9-7c2345bc5f98/image.PNG" alt=""></p>
<p>정상적으로 로드 밸런싱이 되는 것을 확인할 수 있었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DB] Connection Pool]]></title>
            <link>https://velog.io/@calm0_0/DB-Connection-Pool</link>
            <guid>https://velog.io/@calm0_0/DB-Connection-Pool</guid>
            <pubDate>Wed, 25 Oct 2023 10:20:40 GMT</pubDate>
            <description><![CDATA[<h2 id="db-connection">DB Connection</h2>
<hr>
<p>데이터베이스 커넥션은 애플리케이션에서 데이터베이스에 접근하기 위해 진행하는 일련의 과정이다. </p>
<p>데이터베이스는 대부분의 프로그래밍 언어를 위한 드라이버를 지원하며, 애플리케이션에서는 DB 드라이버를 사용하여 데이터베이스와 연결을 맺는다.</p>
<h3 id="예제-코드">예제 코드</h3>
<p>다음은 Node.js 과 MySQL 를 연동한 예제이다.</p>
<pre><code class="language-node.js">const mysql      = require(&#39;mysql&#39;);

const connection = mysql.createConnection({  // 1.
  host     : &#39;localhost&#39;,
  user     : &#39;&lt; MySQL username &gt;&#39;,
  password : &#39;&lt; MySQL password &gt;&#39;,
  database : &#39;my_db&#39;
});

connection.connect();  // 1.

// 2.
connection.query(&#39;SELECT * from Users&#39;, (error, rows, fields) =&gt; {
  if (error) throw error;
  console.log(&#39;User info is: &#39;, rows);
});

connection.end();  // 3.</code></pre>
<ol>
<li>DB 서버와 커넥션을 맺은 다음,</li>
<li>DB에 접근하여 필요한 작업을 수행한다.</li>
<li>작업이 끝나면 연결을 종료한다.</li>
</ol>
<p>주의할 점은 작업이 끝나고 연결을 해제(connection.end())하지 않으면 계속해서 DB 서버와 연결된 상태를 유지하게 된다. 때문에 불필요한 자원(네트워크 및 메모리)이 낭비된다. </p>
<h3 id="db-connection의-생애-주기">DB Connection의 생애 주기</h3>
<p>애플리케이션에서 데이터베이스에 접근하기 위해선 먼저 다음과 같이 데이터베이스 커넥션을 획득해야 한다.</p>
<p><img src="https://velog.velcdn.com/images/calm0_0/post/47edf1a0-6d7f-456a-85a7-19de688c6d21/image.png" alt=""></p>
<ol>
<li>애플리케이션에서 DB 드라이버를 통해 커넥션을 조회한다.</li>
<li>DB 드라이버가 DB와 TCP/IP 연결을 맺는다. (3 way handshake 같은 TCP 통신을 위한 네트워크 동작이 발생)</li>
<li>TCP/IP 연결이 성립되면, DB 드라이버는 ID, PW 등 기타 부가 정보를 DB에 전달한다.</li>
<li>이를 통해 DB는 내부 인증을 완료하고, 내부에 DB 세션을 생성한다.</li>
<li>DB는 커넥션 생성이 완료되었다는 응답을 보낸다.</li>
<li>DB 드라이버는 커넥션 객체를 생성해서 클라이언트에 반환한다.</li>
</ol>
<p>이후 TCP 소켓을 사용하여 DB와 데이터 통신을 한다. 데이터 전송이 종료되면, 데이터베이스 커넥션을 닫고 TCP 연결을 해제한다.(4 way handshake 발생) </p>
<h3 id="db-connection의-문제점">DB Connection의 문제점</h3>
<p>위와 같이 물리적으로 DB 서버에 연결하여 커넥션 객체를 생성하는 작업은 복잡하고 비용이 많이 발생한다. </p>
<p>데이터베이스는 최초 접속 시 TCP 통신을 하며, TCP 통신의 연결 수립/해제 작업인 3 way handshake / 4 way handshake 를 수행하기 때문이다.</p>
<p>애플리케이션에서 클라이언트의 요청이 들어올 때마다 데이터베이스와 연결을 맺고 해제하는 반복적인 작업을 해야 한다는 것은 굉장히 비효율적이다. 이러한 문제점을 해결하기 위한 방법 중 하나가 데이터베이스 &#39;커넥션 풀&#39;이다.</p>
<br>

<h2 id="connection-pool">Connection Pool</h2>
<hr>
<p>Connection Pool 이란 WAS(Web Application Server) 실행 시 DB와 연결해놓은 일정량의 Connection 객체들을 먼저 생성해 Pool 이라는 공간(캐시)에 저장해둔다. 그리고 DB 연결 요청이 있을 때마다 Pool 에서 Connection 객체를 꺼내 사용하고 사용이 끝나면 다시 Pool 에 반납하는 방식을 말한다.</p>
<p><img src="https://velog.velcdn.com/images/calm0_0/post/7d525938-ef03-4b52-8f05-66dcdb42b613/image.png" alt=""></p>
<h3 id="예제-코드-1">예제 코드</h3>
<pre><code class="language-node.js">const mysql = require(&#39;mysql&#39;);

// 커넥션 풀 생성
const connection = mysql.createPool({
  host     : &#39;localhost&#39;,
  user     : &#39;&lt; MySQL username &gt;&#39;,
  password : &#39;&lt; MySQL password &gt;&#39;,
  database : &#39;my_db&#39;,
  connectionLimit: 40  // 커넥션 풀에 몇 개의 커넥션을 두는지
});

...

// 커넥션 풀에서 커넥션 객체 가져오기
connection.getConnection((error, connection) =&gt; {
      // 작업을 수행한 후
    connection.query(&#39;SELECT * FROM users&#39;, (error, result, fields) =&gt; {
        if (error) {
            throw error;
        }

          console.log(result);
          // 커넥션 풀에 커넥션 반환
          connection.release();
    })
});
</code></pre>
<p>다음과 같이 커넥션 풀을 생성한다. 커넥션 풀에는 몇 개의 커넥션 객체를 둘지 정할 수 있다. </p>
<p>DB에 접근할 작업이 필요한 경우 커넥션 객체를 얻어(getConnection()) DB에 접근하여 데이터를 가져온다. </p>
<p>이후 작업이 끝나면 release() 로 커넥션 풀에 커넥션 객체를 반환한다. </p>
<h3 id="장점-및-특징">장점 및 특징</h3>
<ul>
<li>미리 생성된 커넥션을 가져다 쓰는 방식이기 때문에 매 요청마다 커넥션을 생성하는 데 필요한 연결 시간이 소비되지 않는다.</li>
<li>커넥션을 재사용할 수 있으며, 서버 환경에 따라 생성되는 커넥션 수를 조절할 수 있다.</li>
</ul>
<br>
<br>

<p><strong>Reference</strong>
<a href="https://velog.io/@mooh2jj/%EC%BB%A4%EB%84%A5%EC%85%98-%ED%92%80Connection-pool%EC%9D%80-%EC%99%9C-%EC%93%B0%EB%8A%94%EA%B0%80">https://velog.io/@mooh2jj/%EC%BB%A4%EB%84%A5%EC%85%98-%ED%92%80Connection-pool%EC%9D%80-%EC%99%9C-%EC%93%B0%EB%8A%94%EA%B0%80</a>
<a href="https://hudi.blog/dbcp-and-hikaricp/">https://hudi.blog/dbcp-and-hikaricp/</a>
<a href="https://1-7171771.tistory.com/119">https://1-7171771.tistory.com/119</a>
<a href="https://poiemaweb.com/nodejs-mysql">https://poiemaweb.com/nodejs-mysql</a>
<a href="https://javacan.tistory.com/entry/78">https://javacan.tistory.com/entry/78</a>
<a href="https://bitkunst.tistory.com/entry/Nodejs-MySQL-5-%EC%BB%A4%EB%84%A5%EC%85%98-%ED%92%80-Connection-Pool">https://bitkunst.tistory.com/entry/Nodejs-MySQL-5-%EC%BB%A4%EB%84%A5%EC%85%98-%ED%92%80-Connection-Pool</a></p>
]]></description>
        </item>
    </channel>
</rss>