<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>moon-july5.log</title>
        <link>https://velog.io/</link>
        <description>꾸준함으로 성장하는 개발자 지망생 </description>
        <lastBuildDate>Sun, 28 May 2023 01:56:01 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. moon-july5.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/moon-july5" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Jenkins CI/CD 구축 (5)]]></title>
            <link>https://velog.io/@moon-july5/Jenkins-CICD-%EA%B5%AC%EC%B6%95-5</link>
            <guid>https://velog.io/@moon-july5/Jenkins-CICD-%EA%B5%AC%EC%B6%95-5</guid>
            <pubDate>Sun, 28 May 2023 01:56:01 GMT</pubDate>
            <description><![CDATA[<p>마지막으로 배포 파일을 만들어 빌드시키기만 하면됩니다.</p>
<p>먼저 배포용 쉘 스크립트를 만들겠습니다.</p>
<ol>
<li>ec2 서버에서 webapps 디렉토리를 만들고, 그 안에 shell 내용을 작성합니다.<pre><code>sudo mkdir /home/ec2-user/webapps
sudo vi /home/ec2-user/webapps/start.sh</code></pre><pre><code class="language-shell">#!/bin/bash
</code></pre>
</li>
</ol>
<p>REPOSITORY={jar 파일이 생성된 경로}
echo &quot;REPOSITORY = $REPOSITORY&quot;
cd $REPOSITORY</p>
<p>PROJECT_NAME={프로젝트 명}</p>
<p>echo &quot;PROJECT_NAME = $PROJECT_NAME&quot;</p>
<p>PROJECT_PID=$(pgrep -f $PROJECT_NAME)
echo &quot;PROJECT_PID = $PROJECT_PID&quot;</p>
<p>if [ -z $PROJECT_PID ]; then
    echo &quot;no running project&quot;
else
    kill -9 $PROJECT_PID
    sleep 3
fi</p>
<p>JAR_NAME=$(ls -tr $REPOSITORY/*SNAPSHOT.jar | tail -n 1)
echo &quot;JAR_NAME = $JAR_NAME&quot;</p>
<p>java -jar <br>        -Dspring.config.location=classpath:/application.yml,/home/ec2-user/webapps/application.yml <br>        $JAR_NAME &amp;</p>
<pre><code>
2. 젠킨스 프로젝트의 **[구성] -&gt; [Build Steps]**에서 아래의 이미지처럼 작성합니다. 
![jenkins-1](https://velog.velcdn.com/images/moon-july5/post/2391c011-e6c4-4dba-824b-f53b65ae70e5/image.png)

3. **[빌드 후 조치]**에서 아래의 이미지처럼 작성합니다.
![jenkins-2](https://velog.velcdn.com/images/moon-july5/post/9a51251d-d0c9-4d6e-abba-a1403de9c31c/image.png)

4. 위의 이미지처럼 작성이 끝나면, **[고급]**을 눌러 아래의 이미지처럼 `Exec in pty`를 체크합니다.
![jenkins-3](https://velog.velcdn.com/images/moon-july5/post/6fe3ec05-f9be-4e8c-bef3-7a65e843a498/image.png)

5. 젠킨스에서  Publish over SSH를 설정하기 위해 **[Jenkins 관리] -&gt; [시스템 설정] -&gt; [Publish over SSH]**에서 이미지처럼 작성합니다.
* key - aws 인스턴스 접속할 때 쓰던 pem키의 내용
* hostname - aws 인스턴스의 퍼블릭 ip
* username - aws 유저 이름
* Remote Directory - 생성한 배포파일이 업로드 될 경로
![jenkins-4](https://velog.velcdn.com/images/moon-july5/post/3c72f358-900c-4ae4-b1bb-dfcc3107c314/image.png)

6. 이제 테스트해서 성공까지 뜨면 배포 설정이 끝납니다.
![jenkins-5](https://velog.velcdn.com/images/moon-july5/post/da5d8147-fa36-4160-b0c0-eb3251061d89/image.png)

7. ec2 서버에서 `ps -ef | grep jar` 명령어를 입력하여 jar 파일이 정상적으로 실행되었는지 확인하시면 됩니다.

이상으로 젠킨스로 CI/CD 구축을 해봤습니다.</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Jenkins CI/CD 구축 (4)]]></title>
            <link>https://velog.io/@moon-july5/Jenkins-CICD-%EA%B5%AC%EC%B6%95-4</link>
            <guid>https://velog.io/@moon-july5/Jenkins-CICD-%EA%B5%AC%EC%B6%95-4</guid>
            <pubDate>Sat, 27 May 2023 14:41:02 GMT</pubDate>
            <description><![CDATA[<p>이제 <code>웹훅(Webhook)</code> 환경을 구축해야 합니다.</p>
<p>웹훅은 깃허브에 변화사항이 발생하면 젠킨스에게 알림을 보내주기 때문에 젠킨스에서는 주기적으로 레포지토리의 변화사항을 체크할 필요가 없습니다.</p>
<p>먼저, 젠킨스에 두 가지의 플러그인을 설치해야 합니다.</p>
<ol>
<li>젠킨스에서 <strong>[Jenkins 관리] -&gt; [Plugin Manager]</strong>에서 플러그인을 설치합니다.</li>
</ol>
<ul>
<li>GitHub Integration</li>
<li>Publish Over SSH
<img src="https://velog.velcdn.com/images/moon-july5/post/994a1318-5d85-4216-a0e5-801c090b63be/image.png" alt="jenkins-1"></li>
</ul>
<ol start="2">
<li><p>설치 완료 후, 젠킨스 프로젝트를 클릭하여 <strong>[구성] -&gt; [빌드 유발]</strong> <code>GitHub hook trigger for GITScm polling</code> 항목을 체크합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/848983f6-efd6-4bcd-96b9-d9140fe612f3/image.png" alt="jenkins-2"></p>
</li>
<li><p><strong>프로젝트 repository -&gt; settings</strong> 에서 Webhook을 추가합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/f99a6724-772d-4c50-9f33-2442b16f9c95/image.png" alt="jenkins-3"></p>
</li>
<li><p>아래의 이미지처럼 내용 작성 후, [Add webhook]을 클릭합니다. 
<img src="https://velog.velcdn.com/images/moon-july5/post/58851b7a-7f11-49e1-aab5-b6d5f669e7a7/image.png" alt="jenkins-4"></p>
</li>
<li><p>아래의 이미지처럼 Payload URL 앞에 체크 표시가 있어야 정상적으로 웹훅이 등록이 된 것입니다. 
<img src="https://velog.velcdn.com/images/moon-july5/post/45b0de9b-bc1e-42fa-8d9b-7ec592dfe1f2/image.png" alt="jenkins-5"></p>
</li>
<li><p>그 후, 테스트용 push를 해보고 웹훅 빌드도 정상적으로 SUCCESS 나오면 성공입니다.</p>
</li>
</ol>
<p>이상으로 Github Webhook 환경을 구축했습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Jenkins CI/CD 구축(3)]]></title>
            <link>https://velog.io/@moon-july5/Jenkins-CICD-%EA%B5%AC%EC%B6%953</link>
            <guid>https://velog.io/@moon-july5/Jenkins-CICD-%EA%B5%AC%EC%B6%953</guid>
            <pubDate>Sat, 27 May 2023 12:31:25 GMT</pubDate>
            <description><![CDATA[<p>지금까지 젠킨스를 설치하고, nginx 프록시 설정, 젠킨스 홈 화면으로 접속하는 것까지 해봤습니다.</p>
<p>이번에는 젠킨스와 GitHub 연동을 해보겠습니다.</p>
<p>GitHub에 연동하기 전에 키 생성할 필요가 있습니다.</p>
<ol>
<li>키 생성하기 전에 젠킨스 사용자를 확인합니다.<pre><code>ps aux | grep jenkins</code></pre></li>
<li>젠킨스가 사용자로 실행 중이면 현재 사용자를 젠킨스로 전환합니다.<pre><code>sudo -u jenkins /bin/bash</code></pre>그러면 아래와 같이 bash 쉘로 전환이 될 것입니다.<pre><code>bash-4.2$</code></pre></li>
<li><code>bash-4.2$</code>로 전환이 되었으면 <code>.ssh</code> 디렉토리를 하나 생성하고 이동합니다.<pre><code>$ mkdir /var/lib/jenkins/.ssh
$ cd /var/lib/jenkins/.ssh</code></pre></li>
<li>이제 이 디렉토리에 ssh 키를 생성합니다.<pre><code>ssh-keygen -t rsa -f /var/lib/jenkins/.ssh/[프로젝트명]</code></pre>비밀번호는 입력하지 않고 Enter로 넘기면 됩니다.</li>
</ol>
<hr>
<p>이제 GitHub Repository에 이동합니다.</p>
<ol>
<li>아래와 같이 이동 후, [Add deploy key]를 클릭합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/94f06210-06ad-422a-a4aa-f054b2e15cc3/image.png" alt="jenkins-1"></li>
<li><code>ls -al</code>을 입력하여 공개키가 생성되었는지 확인합니다. <pre><code>bash-4.2$ ls -al</code></pre></li>
<li>생성한 공개키 코드를 cat 통해 복사합니다.<pre><code>cat /var/lib/jenkins/.ssh/파일명.pub</code></pre></li>
<li>공개키 코드를 아까 GitHub deploy key에 복사합니다. 그리고 Add Key를 누릅니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/df4f17e2-cd38-4b92-8286-18dd05eec6c8/image.png" alt="jenkins-2"></li>
</ol>
<hr>
<p>이제 다시 젠킨스로 이동합니다.</p>
<ol>
<li><p>아까 공개키 등록을 했으니, 젠킨스에 비밀키를 등록합니다. 그 전에 아까 위에서 생성한 키 파일에서 비밀키 코드를 복사합니다.</p>
<pre><code>cat /var/lib/jenkins/.ssh/[파일명]</code></pre><p><img src="https://velog.velcdn.com/images/moon-july5/post/2c1d9640-1097-425a-82fc-d46e1e5b1877/image.png" alt="jenkins-3"></p>
</li>
<li><p>이제 젠킨스에서 <code>Jenkins 관리 -&gt; Credentials -&gt; Stores scoped to Jenkins의 Domains의 global 클릭 -&gt; 왼쪽 사이드바의 Add Credentials</code> 클릭 후 아래의 이미지 처럼 복사한 비밀키 코드를 복사합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/b8d8b8ee-912a-418e-bef5-70f2516141ad/image.png" alt="jenkins-4"></p>
</li>
<li><p>설정이 완료되었으면, 젠킨스에서 [새로운 Item]을 클릭합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/881f995f-be4e-41af-8e0e-0d18933421b9/image.png" alt="jenkins-5"></p>
</li>
<li><p>item 이름을 입력 후, [Freestyle project]를 클릭합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/ace4e646-57a2-4f2b-a5fd-d1123147b0fc/image.png" alt="jenkins-6"></p>
</li>
<li><p>소스코드 관리에서 연동할 프로젝트의 깃 레파지토리를 작성하고 Crredentials에는 금방 만든 Username을 선택합니다. 
<img src="https://velog.velcdn.com/images/moon-july5/post/33af8bae-0b46-4732-9618-4580d6f689c9/image.png" alt="jenkins-7"></p>
</li>
</ol>
<p>이 부분에서 Repository 연결이 안될 수 있습니다.</p>
<p>이는 아래의 이미지처럼 Github에 들어가서 <strong>[Settings] -&gt; [Developer settings] -&gt; [Personal access tokens] -&gt; [token]</strong>로 이동합니다. 그 후 토큰을 생성합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/d50bb1f6-4c44-4c0e-9d68-1bf37429246e/image.png" alt="jenkins-10"></p>
<p>생성한 토큰 값을 [Repository URL]에 다음과 같이 입력합니다.</p>
<pre><code>https://토큰 값@github.com/레포지터리 경로</code></pre><ol start="6">
<li><p>설정을 완료하고 대시보드에 가시면 프로젝트가 생성되어있는데 프로젝트명 우측 화살표 클릭하고 <code>build now</code>를 클릭합니다.</p>
</li>
<li><p>콘솔 출력에서 확인해보면 맨 아래의 출력문에서 <strong>Finished : SUCCESS</strong>가 뜨면 성공입니다.</p>
</li>
</ol>
<h3 id="💡-build-멈춤-현상">💡 build 멈춤 현상</h3>
<p>젠킨스 build 시, 멈춤 현상이 발생할 수 있습니다.</p>
<p>AWS 프리티어 서버(t2.micro)를 사용하게 될 경우, 사양 문제로 build가 멈출 수 있습니다. 그리고 서버의 속도 또한 급격하게 저하되는 현상도 발생합니다.</p>
<p>아래의 링크를 참고하여 해결하였지만 이는 메모리 Swap을 통해 해결한 것이기 때문에 임시방편이며, 서버의 실행 성능이 떨어질 수 있습니다.
<a href="https://velog.io/@kku64r/ec2freetier">[jenkins] AWS EC2 프리티어 jenkins build 멈춤 현상</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Jenkins CI/CD 구축(2)]]></title>
            <link>https://velog.io/@moon-july5/Jenkins-CICD-%EA%B5%AC%EC%B6%952</link>
            <guid>https://velog.io/@moon-july5/Jenkins-CICD-%EA%B5%AC%EC%B6%952</guid>
            <pubDate>Sat, 27 May 2023 05:07:27 GMT</pubDate>
            <description><![CDATA[<p><a href="https://velog.io/@moon-july5/Jenkins-CICD-%EA%B5%AC%EC%B6%951">Jenkins CI/CD 구축(1)</a>에서 젠킨스 설치까지 마쳤습니다.</p>
<p>이제 <code>nginx</code>를 통해 프록시 등록을 해보고 젠킨스에 접속하는 것까지 해보겠습니다.</p>
<ol>
<li><code>amazon-linux-extras</code>에서 nginx를 지원하는지 확인합니다.<pre><code>amazon-linux-extras list | grep nginx</code></pre><pre><code>$ amazon-linux-extras list | grep nginx
38  nginx1=latest            enabled      [ =stable ]
</code></pre></li>
</ol>
<pre><code>2. nginx가 아닌 nginx1이므로 nginx1로 설치합니다.</code></pre><p>sudo yum clean metadata &amp;&amp; sudo amazon-linux-extras install nginx1</p>
<pre><code>3. nginx 설치가 끝나면 nginx.conf 파일에서 프록시 설정을 합니다.</code></pre><p>sudo vim /etc/nginx/nginx.conf</p>
<pre><code></code></pre><p>server {
    listen       80;
    listen       [::]:80;
    server_name  _;
    root         /usr/share/nginx/html;</p>
<pre><code># Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;

// 추가
location / {
            proxy_pass http://localhost:8080;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $http_host;
}


error_page 404 /404.html;
location = /40x.html {
}

error_page 500 502 503 504 /50x.html;
location = /50x.html {
}</code></pre><p>}</p>
<pre><code>4. 이제 nginx를 실행합니다. </code></pre><p>sudo systemctl start nginx</p>
<pre><code>5. nginx가 정상적으로 실행됐는지 확인합니다.</code></pre><p>sudo systemctl status nginx</p>
<pre><code></code></pre><p>$ sudo systemctl status nginx
● nginx.service - The nginx HTTP and reverse proxy server
   Loaded: loaded (/usr/lib/systemd/system/nginx.service; disabled; vendor preset: disabled)
   Active: active (running) since Tue 2023-05-16 16:41:21 KST; 1 weeks 3 days ago
  Process: 27947 ExecStart=/usr/sbin/nginx (code=exited, status=0/SUCCESS)
  Process: 27943 ExecStartPre=/usr/sbin/nginx -t (code=exited, status=0/SUCCESS)
  Process: 27942 ExecStartPre=/usr/bin/rm -f /run/nginx.pid (code=exited, status=0/SUCCESS)
 Main PID: 27954 (nginx)
   CGroup: /system.slice/nginx.service
           ├─27954 nginx: master process /usr/sbin/nginx
           └─27955 nginx: worker process</p>
<pre><code>Active : active (running) 상태면 정상적으로 실행이 된 것을 의미합니다.

6. 인스턴스의 **인바운드 규칙**에 80포트를 추가합니다.
![jenkins-1](https://velog.velcdn.com/images/moon-july5/post/0089fd5b-353d-401f-bde2-4eeef8fe9e92/image.png)

7. 인스턴스의 퍼블릭 IP로 접속해보시면 아래처럼 젠킨스로 접속할 수 있습니다.
![jenkins-2](https://velog.velcdn.com/images/moon-july5/post/21e19043-40ec-4e24-bb15-2b03a1f58a52/image.png)

8. 이제 비밀번호를 입력해야 하는데, cat 명령어를 통해 비밀번호를 확인한 후, 비밀번호를 복사하여 입력합니다.</code></pre><p>sudo cat /var/lib/jenkins/secrets/initialAdminPassword</p>
<pre><code>
9. 비밀번호를 입력하면, 아래와 같은 화면으로 이동하게 될 것입니다. 여기서 **[Install Suggested Plugins]**를 선택하여, 플러그인을 설치합니다. 
![jenkins-3](https://velog.velcdn.com/images/moon-july5/post/944b9fd2-cfd9-4573-b06b-df952d55b1b9/image.png)

10. 설치가 완료되면 젠킨스를 접속할 때 사용할 계정을 생성합니다. 그 후, url 설정이 나올 것인데, 넘어가시고 start 하시면 됩니다.

아마 젠킨스 홈 화면으로 이동한 것을 확인할 수 있을 것입니다.

이상으로 nginx를 통해 프록시 등록을 해보고 젠킨스에 접속하는 것까지 해봤습니다.</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Jenkins CI/CD 구축(1)]]></title>
            <link>https://velog.io/@moon-july5/Jenkins-CICD-%EA%B5%AC%EC%B6%951</link>
            <guid>https://velog.io/@moon-july5/Jenkins-CICD-%EA%B5%AC%EC%B6%951</guid>
            <pubDate>Sat, 27 May 2023 02:47:17 GMT</pubDate>
            <description><![CDATA[<p>이번 프로젝트에서는 처음으로 <code>젠킨스(Jenkins)</code>를 이용하여 CI/CD를 구축했습니다. </p>
<p>여태까지, <code>Travis CI</code>나 <code>GitHub Actions</code>를 사용하여 CI/CD를 구축했는데, 어떤 식으로 구축을 했는지 기억이 안났고 어떤 식으로 동작하는지 제대로 몰랐기 때문에 이번에 젠킨스로 CI/CD를 구축하면서 어떤 과정을 거쳐서 구축했는지 정리의 필요성을 느껴 이렇게 게시글을 작성하게 되었습니다.   </p>
<p>젠킨스로 CI/CD를 구축하기 전에, CI/CD에 대해서 알 필요가 있습니다. </p>
<h2 id="📌-cicd란">📌 CI/CD란?</h2>
<p><code>CI(Continuous Integration)</code>는 지속적 통합을 의미하며, <strong>코드를 커밋하고 빌드했을 때 정상적으로 작동하는지 반복적으로 검증해 애플리케이션의 신뢰성을 높이는 작업</strong>입니다.</p>
<p><code>CD(Continuous Deployment)</code>는 지속적 배포를 의미하며, CI 과정에서 생성된 신뢰할 수 있는 애플리케이션을 <strong>실제 상용 환경에서 자동으로 배포하는 작업</strong>입니다.</p>
<p>즉, <code>CI/CD</code>는 <strong>변경 사항을 계속 추적해 좀 더 안정화된 애플리케이션을 만들고, 이를 배포하는 과정을 자동화해 시스템을 안정적으로 운영하는 데 가장 많이 쓰이는 개념</strong>입니다.</p>
<p>그래서 CI/CD가 어떤 식으로 동작하는지 설명하자면, 개발자가 소스를 커밋하고 푸시하면 CI 단계로 들어갑니다.</p>
<p>CI 단계에서는 애플리케이션이 자동 빌드되고 테스트를 거쳐 배포할 수 있는 애플리케이션인지 확인합니다.</p>
<p>테스트를 통과하면 신뢰할 수 있는 애플리케이션으로 간주하고 CD 단계로 넘어갑니다.</p>
<p>CD 단계에서는 애플리케이션을 미리 설정한 파일을 통해 배포합니다.</p>
<p>그래서 젠킨스가 무엇인지 궁금할 수 있습니다.</p>
<h2 id="📌-젠킨스jenkins">📌 젠킨스(Jenkins)?</h2>
<p>CI/CD를 제공하는 툴이 매우 많지만, 그 중에서 <code>젠킨스(Jenkins)</code>가 대표적이라고 할 수 있습니다.</p>
<p>젠킨스는 오픈 소스 CI/CD 도구로, 사용자가 직접 UI에서 작업을 구성하거나 작업 순서를 코드로 정의할 수 있습니다.</p>
<p>오랜 시간 동안 많은 사람이 사용하고 있어서 사용에 필요한 정보를 찾기 쉽고 활용 방법과 플러그인 개발 관련 커뮤니티 활동이 활발해서 다양한 사용 환경, 언어 및 빌드 도구와 연계할 플러그인이 필요할 경우 인터넷에서 대부분의 플러그인을 쉽게 찾을 수 있습니다.</p>
<p>그러면 이 젠킨스를 사용하여 CI/CD를 구축해 보겠습니다.</p>
<h2 id="📌-젠킨스-설치">📌 젠킨스 설치</h2>
<ol>
<li><p>먼저 yum을 update 해줍니다.</p>
<pre><code>sudo yum update -y</code></pre></li>
<li><p>이제 젠킨스를 설치해야 하는데 yum 패키지에는 기본적으로 젠킨스가 포함되어있지 않습니다. 그래서 yum패키지 저장소에 젠킨스를 수동으로 등록해줘야 합니다.</p>
<pre><code>sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo</code></pre></li>
<li><p>젠킨스 저장소 키를 등록합니다.</p>
<pre><code>sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key</code></pre></li>
<li><p>이제 젠킨스를 설치합니다.</p>
<pre><code>sudo yum install jenkins</code></pre></li>
<li><p>젠킨스를 실행시킵니다.</p>
<pre><code>sudo systemctl start jenkins</code></pre></li>
<li><p>젠킨스가 정상적으로 실행이 됐는지 아래의 명령어를 통해 확인합니다.</p>
<pre><code>sudo systemctl status jenkins</code></pre><pre><code>[ec2-user@ip-172-31-34-142 ~]$ sudo systemctl status jenkins
● jenkins.service - Jenkins Continuous Integration Server
Loaded: loaded (/usr/lib/systemd/system/jenkins.service; disabled; vendor preset: disabled)
Active: active (running) since Fri 2023-05-05 15:09:22 KST; 3 weeks 0 days ago
Main PID: 14192 (java)
CGroup: /system.slice/jenkins.service
        └─14192 /usr/bin/java -Djava.awt.headless=true -jar /usr/share/java/jenkins.war --webroot=%C/jenkins/war --httpPort=9090

 ...</code></pre></li>
</ol>
<p><strong>Active : active (running)</strong> 상태면 정상적으로 실행되고 있다는 의미입니다.</p>
<p>이상으로 CI/CD가 무엇이고, 젠킨스에 대해서, 그리고 젠킨스 설치법에 대해서 간단히 알아봤습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Boot S3 연동]]></title>
            <link>https://velog.io/@moon-july5/Spring-Boot-S3-%EC%97%B0%EB%8F%99</link>
            <guid>https://velog.io/@moon-july5/Spring-Boot-S3-%EC%97%B0%EB%8F%99</guid>
            <pubDate>Thu, 25 May 2023 07:48:06 GMT</pubDate>
            <description><![CDATA[<p>Spring Boot에서 S3를 설정하기 전에 <a href="https://velog.io/@moon-july5/AWS-S3-%EB%B2%84%ED%82%B7-%EC%83%9D%EC%84%B1">AWS S3 버킷 생성</a> 글 처럼 S3 버킷 생성과 버킷 정책 설정, 액세스 키(Access Key)를 발급받아야 합니다.</p>
<p>먼저, S3 관련 의존성을 추가합니다.</p>
<h2 id="📌-buildgrdle-의존성-추가">📌 build.grdle 의존성 추가</h2>
<p>저 같은 경우는 다음과 같은 의존성을 추가했습니다.</p>
<pre><code>implementation &#39;com.amazonaws:aws-java-sdk-s3:1.12.3&#39;</code></pre><p>이 의존성이 아니더라도 아래와 같은 의존성을 추가해도 괜찮은 것 같습니다.</p>
<pre><code>implementation &#39;org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE`</code></pre><h2 id="📌-applicationyml-설정">📌 application.yml 설정</h2>
<pre><code>cloud:
  aws:
    region:
      static: ap-northeast-2
    credentials:
      access-key: [발급받은 액세스 키]
      secret-key: [발급받은 시크릿 키]
    s3:
      bucket: [버킷 이름]</code></pre><p>버킷을 생성했을 때, <strong>설정한 이름</strong>과 IAM을 추가하면서 발급받은 <strong>액세스 키</strong>와 <strong>시크릿 키</strong>를 <code>application.yml</code> 파일에 정보를 작성합니다.</p>
<p>💡 위와 같은 정보들은 Github에 절대 push하시면 안됩니다!</p>
<p>여기까지 Spring Boot에서 AWS S3를 사용하기 위한 설정이였습니다. </p>
<p>이제는 PDF같은 파일들을 S3에 업로드할 수 있도록 구현해 보겠습니다.</p>
<hr>
<h2 id="📌-s3config">📌 S3Config</h2>
<pre><code>@Configuration
public class S3Config {

    @Value(&quot;${cloud.aws.credentials.access-key}&quot;)
    private String accessKey;
    @Value(&quot;${cloud.aws.credentials.secret-key}&quot;)
    private String secretKey;
    @Value(&quot;${cloud.aws.region.static}&quot;)
    private String region;

    @Bean
    public AmazonS3 amazonS3Client() {
        AWSCredentialsProvider awsCredentialsProvider = new AWSStaticCredentialsProvider(
                new BasicAWSCredentials(accessKey, secretKey));
        return AmazonS3ClientBuilder.standard()
                .withRegion(region)
                .withCredentials(awsCredentialsProvider)
                .build();
    }
}</code></pre><ul>
<li><p>바로 전에 <code>application.yml</code>에 S3 및 IAM에 대한 설정을 완료했습니다. 이제 이 설정 값들을 연결할 필요가 있습니다.</p>
</li>
<li><p>설정 값들을 <code>@Value</code>를 통해 불러옵니다.</p>
</li>
<li><p>S3 관련 의존성에서 제공하는 <code>AmazonS3</code>를 이용하여 파일들을 업로드하는데, 설정 값들을 <code>amazonS3Client()</code> 메서드를 통해 등록합니다.</p>
</li>
</ul>
<p>이제 PDF 파일을 업로드하는 코드를 보겠습니다.</p>
<h2 id="📌-pdf-파일-업로드">📌 PDF 파일 업로드</h2>
<pre><code>@Service
@RequiredArgsConstructor
public class PdfParsingImpl implements PdfParsingService {

    private final ResponseService responseService;

    private final AmazonS3 amazonS3Client;

    @Value(&quot;${cloud.aws.s3.bucket}&quot;)
    private String bucket;

    ...

    @Override
    public CommonResponse pdfParsing(MultipartFile multipartFile) throws IOException, PDFValidationException {

        String fileExtension = &quot;&quot;;
        try {
            String fileName = multipartFile.getOriginalFilename();
            fileExtension = Objects.requireNonNull(fileName).substring(fileName.lastIndexOf(&quot;.&quot;) + 1);
        } catch (NullPointerException e) {
            return responseService.getFailResponse(400, &quot;파일이 없거나 잘못된 접근입니다.&quot;);
        }

        if (fileExtension.equals(&quot;pdf&quot;)) {
            String key = uploadFileToS3(multipartFile);
            S3Object s3Object = amazonS3Client.getObject(bucket, key);
            InputStream inputStream = s3Object.getObjectContent();

            // pdf 파싱
             ...

         } else {
            return responseService.getFailResponse(404, &quot;파일형식이 잘못되었습니다&quot;);
        }
    }

     public String uploadFileToS3(MultipartFile multipartFile) {
        String key = multipartFile.getOriginalFilename();
        InputStream inputStream;
        try {
            inputStream = multipartFile.getInputStream();
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentLength(multipartFile.getSize());
            amazonS3Client.putObject(new PutObjectRequest(bucket, key, inputStream, metadata));
        } catch (IOException e) {
            throw new PDFValidationException();
        }
        return key;
    }   </code></pre><ul>
<li><p>S3관련 인터페이스인 <code>AmazonS3</code>와 application.yml에 설정한 버킷 이름을 <code>@Value</code>를 통해 불러옵니다.</p>
</li>
<li><p>pdf 파일인 <code>MultipartFile</code>을 입력받으면 그 파일이 pdf 확장자가 맞는 지 파일 이름을 얻어와서 검사할 필요가 있습니다. 확장자가 pdf가 아니면 다른 응답을 반환합니다.</p>
</li>
<li><p><code>uploadFileToS3()</code> 메서드는 S3에 파일을 업로드 하는 역할을 합니다. 관련된 코드들을 한 줄씩 설명해 보겠습니다.</p>
<ul>
<li>객체에서 원본 파일 이름을 가져와 <code>key</code> 변수에 저장합니다. 이 키는 S3에서 업로드된 파일을 구분하기 위해 사용됩니다.</li>
<li><code>MultipartFile</code> 객체에서 InputStream을 가져와 <code>inputStream</code> 변수에 저장합니다. 이 스트림은 파일 데이터를 읽을 수 있는 스트림입니다.</li>
<li><code>ObjectMetadata</code> 객체를 생성합니다. 이 객체는 업로드된 파일의 메타데이터를 나타냅니다.</li>
<li><code>ObjectMetadata</code> 객체의 <code>setContentLength()</code> 메서드를 사용하여 업로드된 파일의 크기를 설정합니다. <code>getSize()</code> 메서드는 MultipartFile 객체의 크기를 반환합니다.</li>
<li>amazonS3Client를 사용하여 S3 버킷에 파일을 업로드합니다. <code>PutObjectRequest</code> 생성자에는 <strong>버킷 이름, 파일 키, 입력 스트림 및 메타데이터</strong>가 전달됩니다.</li>
<li>마지막으로 업로드된 파일의 <code>key(원본 파일 이름)</code>를 반환합니다.</li>
</ul>
</li>
<li><p><code>uploadFileToS3()</code> 메서드에서 반환된 key를 이용하여 버킷에서 업로드한 파일을 얻어오고, pdf 파싱을 위해 파일의 데이터를 읽어옵니다.</p>
</li>
</ul>
<p>파일을 업로드하게 되면 아래와 같이 S3 버킷 안에 업로드된 것을 확인할 수 있습니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/5ef8e496-fb0b-4714-b4b4-bd9fadee827a/image.png" alt=""></p>
<p>이상으로 Spring Boot와 S3 연동과 S3 버킷에 파일을 업로드하는 것까지 해봤습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS S3 버킷 생성]]></title>
            <link>https://velog.io/@moon-july5/AWS-S3-%EB%B2%84%ED%82%B7-%EC%83%9D%EC%84%B1</link>
            <guid>https://velog.io/@moon-july5/AWS-S3-%EB%B2%84%ED%82%B7-%EC%83%9D%EC%84%B1</guid>
            <pubDate>Thu, 25 May 2023 03:13:16 GMT</pubDate>
            <description><![CDATA[<p>먼저, <code>S3(Simple Storage Service)</code>라는 것은 <strong>순수하게 파일들을 저장하고 접근 권한을 관리, 검색 등을 지원하는 파일 서버의 역할</strong>을 합니다.</p>
<p>이미지 파일을 비롯한 정적파일들을 관리하거나 자바 웹 애플리케이션의 JAR나 WAR같은 배포 파일들을 관리하는 등의 기능을 지원합니다.</p>
<p>이번 프로젝트에서 PDF 파일들을 업로드하게 되면 S3의 버킷 안에 저장되도록 구현하였으며, 저장된 파일을 갖고 파싱을 하여 사용하였습니다.</p>
<p>그러면 먼저 S3 버킷을 생성해 보겠습니다.</p>
<ol>
<li><p>AWS 서비스에서 S3를 검색하여 이동합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/97e96952-45c7-48b5-8d0e-97627a858792/image.png" alt="s3-1"></p>
</li>
<li><p><strong>[버킷 만들기]</strong>를 클릭합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/89680eeb-9ede-4124-9fb7-9c29891cfc63/image.png" alt="s3-2"></p>
</li>
<li><p>본인이 원하는 버킷명을 작성합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/6e0d5a13-133f-40c1-8505-a483efe4c404/image.png" alt="s3-3"></p>
</li>
<li><p>기본적으로 퍼블릭 액세스 차단이 되어있는데, 이것을 해제합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/b98a8d63-289b-47e1-a3f7-efb7ad76b96e/image.png" alt="s3-4"></p>
</li>
</ol>
<p>버킷이 생성되면 버킷 목록에서 확인할 수 있습니다.</p>
<p>이제 이 만들어진 버킷에 버킷 정책을 설정합니다.</p>
<h2 id="📌-버킷-정책-설정">📌 버킷 정책 설정</h2>
<ol>
<li><p>방금 만든 버킷을 클릭하여 들어가 <strong>[권한 -&gt; 버킷 정책 -&gt; 편집]</strong>을 클릭합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/5d2ff024-5a92-464f-b07e-7c02b810a900/image.png" alt="s3-5"></p>
</li>
<li><p><code>버킷 ARN</code>을 복사 해두고 <strong>정책 생성기</strong>를 클릭합니다. 
<img src="https://velog.velcdn.com/images/moon-july5/post/f5c5ad2e-0dfb-4a56-a6c2-436a3cbbdd2d/image.png" alt="s3-6"></p>
</li>
<li><p><strong>정책 생성기</strong>에 들어가 아래와 같이 설정한 후에, <strong>Add Statement</strong>를 클릭합니다. 
<code>Actions</code>에는 <code>DeleteObject</code>, <code>GetObject</code>, <code>PutObject</code>를 체크합니다. 
<img src="https://velog.velcdn.com/images/moon-july5/post/1f0bf96e-6001-49b6-aeb1-3eabb80e65bd/image.png" alt="s3-7"></p>
</li>
<li><p><strong>Add Statement</strong>를 클릭하게 되면 위에 추가한 정보들이 보이게 되며, 아래 <strong>Generate Policy</strong>를 클릭하여, 나온 정책을 복사합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/ee8daa13-3879-4981-a0de-cb42dbb14b22/image.png" alt="s3-8"></p>
</li>
<li><p>버킷 정책 편집에서 복사한 정책을 붙여넣기 하며, Resource 끝에 <code>/*</code>을 추가합니다. 
<img src="https://velog.velcdn.com/images/moon-july5/post/92fa3238-a7ad-4065-bbb0-a5e828324ea6/image.png" alt="s3-9"></p>
</li>
</ol>
<h2 id="📌-aws-iam-설정">📌 AWS IAM 설정</h2>
<p>AWS S3에 접근하는 것을 허용하기 위해 <code>IAM(Identity and Access Management)</code>을 생성해야 합니다.
IAM은 AWS에서 제공하는 서비스의 접근 방식과 권한을 관리합니다.</p>
<ol>
<li><p>AWS에서 IAM을 검색하여 이동합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/b33f4f10-c88b-4b3a-9ef2-59c391dccec1/image.png" alt="iam-1"></p>
</li>
<li><p>IAM 왼쪽 사이드바에서 <strong>[사용자 -&gt; 사용자 추가]</strong>를 차례로 클릭합니다. 
<img src="https://velog.velcdn.com/images/moon-july5/post/e828378d-f393-4c49-98dc-0c7f490d6cd5/image.png" alt="iam-2"></p>
</li>
<li><p>사용자 이름을 입력합니다. 
<img src="https://velog.velcdn.com/images/moon-july5/post/8a529d0c-48c2-40c0-8f30-410b67e99495/image.png" alt="iam-3"></p>
</li>
<li><p>직접 정책 연결을 클릭하고, <code>AmozonS3FullAccess</code> 를 선택하고 다음을 클릭합니다. 
<img src="https://velog.velcdn.com/images/moon-july5/post/d2c27c5e-a2ba-4b3a-80eb-5623971894b0/image.png" alt="iam-4"></p>
</li>
<li><p>사용자 생성을 클릭하면 사용자가 생성됩니다.</p>
</li>
</ol>
<hr>
<p>이제, 외부에서 접속할 수 있도록 사용자의 엑세스 키(Access key)를 만들어 줄 필요가 있습니다.</p>
<ol start="6">
<li><p>생성한 사용자 이름을 클릭하며, <strong>[보안 자격 증명] -&gt; [액세스 키 만들기]</strong>를 클릭합니다. 
<img src="https://velog.velcdn.com/images/moon-july5/post/d31d327d-ed3f-468e-bffb-ec50eccf0b54/image.png" alt="iam-5"></p>
</li>
<li><p>아무거나 클릭하고 다음을 클릭합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/ba9b65de-f355-4058-9158-0aad796fe715/image.png" alt="iam-6"></p>
</li>
<li><p>태그를 입력하고 <strong>[액세스 키 만들기]</strong>를 클릭합니다. 
<img src="https://velog.velcdn.com/images/moon-july5/post/496d7b8e-e26b-4233-909c-854affb9c862/image.png" alt="iam-7"></p>
</li>
<li><p>이제 생성된 공개키와 비밀키를 확인할 수 있을 것입니다.
(생성 완료 화면이 아니면 비밀 엑세스 키를 볼 수 없기 때문에 <code>.csv</code> 파일로 받아두는 것이 좋습니다.)</p>
</li>
</ol>
<p>이상으로 AWS S3 버킷을 생성 후, 정책 설정, IAM까지 설정을 해봤습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS RDS를 내 PC에서 접속]]></title>
            <link>https://velog.io/@moon-july5/AWS-RDS-%EB%82%B4-PC%EC%97%90%EC%84%9C-%EC%A0%91%EC%86%8D</link>
            <guid>https://velog.io/@moon-july5/AWS-RDS-%EB%82%B4-PC%EC%97%90%EC%84%9C-%EC%A0%91%EC%86%8D</guid>
            <pubDate>Wed, 24 May 2023 12:02:58 GMT</pubDate>
            <description><![CDATA[<p>내 PC에서 RDS로 접근하기 위해서 <strong>RDS의 보안 그룹 설정</strong>이 추가적으로 필요합니다.</p>
<ol>
<li><p>RDS의 세부정보 페이지에서 <strong>[VPC 보안 그룹]</strong>의 링크를 클릭합니다.
또 다른 창에서 EC2에 <strong>[보안 그룹]</strong>을 클릭합니다. 
보안 그룹 목록 중에서 <strong>EC2에서 사용된 보안 그룹의 그룹 ID를 복사</strong>합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/e54e181f-a482-490c-bb9c-ce5433dd198a/image.png" alt="rds-1"></p>
</li>
<li><p>RDS의 세부 페이지에서 링크를 통해 들어간 곳에서 <strong>[인바운드 규칙] -&gt; [인바운드 규칙 편집]</strong>을 클릭합니다. 규칙 유형에서 <code>MYSQL/Aurora</code>를 선택하면 자동으로 3306 포트가 선택됩니다. 그리고 EC2 보안 그룹의 ID를 추가하고 저장합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/26e841fc-c5f2-4304-b9bf-fb714177ddcb/image.png" alt="rds-2"></p>
</li>
<li><p>RDS 정보 페이지에서 엔드 포인트를 확인하고, 미리 복사해 둡니다.<img src="https://velog.velcdn.com/images/moon-july5/post/62d77d86-05eb-4a77-bb07-943b23a9777c/image.png" alt="rds-3"></p>
</li>
<li><p>로컬에서 원격 데이터베이스로 붙을 때 <code>MySQL</code>의 대표적인 GUI 클라이언트로 Workbench, SQLyog(유료) 등이 있습니다. 본인이 가장 좋아하는 툴을 사용하며, 저는 Workbench를 사용했습니다.</p>
</li>
<li><p>MySQL Workbench의 홈에서 + 버튼을 클릭하여 <strong>본인이 생성한 RDS의 정보를 차례로 등록</strong>합니다. 여기서 Hostname에는 방금 전에 복사한 <strong>RDS의 엔드 포인트</strong>를 등록합니다. 그리고 마스터 계정과 비밀번호를 등록한 뒤, 화면 아래의 <strong>[Test Connection]</strong>을 클릭해 연결 테스트를 합니다. 
<img src="https://velog.velcdn.com/images/moon-july5/post/d223839f-4407-4230-a5da-152d9f54fc97/image.png" alt="rds-4"></p>
</li>
<li><p>이제 MySQL Workbench에서 사용할 데이터베이스를 생성한 뒤, 생성한 데이터베이스를 클릭합니다.
그리고 데이터베이스가 선택된 상태에서 아래의 쿼리로 현재의 <code>character_set</code>, <code>collation</code> 설정을 확인합니다.</p>
<pre><code>show variables like &#39;c%&#39;;</code></pre><p><img src="https://velog.velcdn.com/images/moon-july5/post/06ba6561-373e-4fba-94d5-40136b520dc5/image.png" alt="rds-5"></p>
</li>
</ol>
<p>다른 필드들은 <strong>utf8mb4</strong>가 잘 적용됐지만 다른 필드들이 제대로 적용되지 않았다면 직접 변경할 필요가 있습니다.</p>
<pre><code>ALTER DATABASE 데이터베이스명
CHARACTER SET = &#39;utf8mb4&#39;
COLLATE = &#39;utf8mb4_general_ci&#39;;</code></pre><ol start="7">
<li>이제 EC2에 ssh 접속을 진행합니다. Window에서는 putty로 진행합니다. 접속되었다면 MySQL 접근 테스트를 위해 <strong>MySQL CLI</strong>를 설치하겠습니다.<pre><code>sudo yum intall mysql</code></pre></li>
<li>설치가 되었으면 로컬에서 접근하듯이 계정, 비밀번호, 호스트 주소를 사용해 RDS에 접속합니다.<pre><code>mysql -u 계정 -p -h Host주소</code></pre>저 같은 경우에는 다음과 같습니다.</li>
</ol>
<pre><code class="language-shell"># mysql -u 계정 -p -h 엔드포인트 
mysql -u moon -p -h finalproject.civfgcdxitth.ap-northeast-2.rds.amazonaws.com</code></pre>
<p>패스워드를 입력하라는 메시지가 나오면 패스워드를 입력합니다. </p>
<p>그러면 EC2에서 RDS로 접속되는 것을 확인할 수 있습니다.</p>
<p>RDS에 접속되었으면 실제로 생성한 RDS가 맞는지 간단한 쿼리를 실행합니다.</p>
<pre><code>show databases;</code></pre><p>이상으로 AWS RDS를 PC에서 접속해 봤습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS RDS 파라미터 설정]]></title>
            <link>https://velog.io/@moon-july5/AWS-RDS-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@moon-july5/AWS-RDS-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Wed, 24 May 2023 04:44:34 GMT</pubDate>
            <description><![CDATA[<p>RDS를 처음 생성하면 몇 가지 설정을 필수로 해야 합니다.</p>
<ul>
<li>TimeZone</li>
<li>Character Set</li>
<li>Max Connection</li>
</ul>
<p>이 3개의 설정을 진행해 보겠습니다.</p>
<ol>
<li><p><strong>[파라미터 그룹]</strong>에서 <strong>[파라미터 그룹 생성]</strong>을 클릭합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/50e097f1-861b-4177-8fd9-69fe22020039/image.png" alt="rds-1"></p>
</li>
<li><p>전에 생성한 <code>MySQL</code>과 같은 버전의 DB 엔진을 선택하고 <strong>파라미터 그룹 이름</strong>을 적습니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/ddd8819e-59c1-434b-9d70-27e40fa780d7/image.png" alt="rds-2"></p>
</li>
<li><p>생성이 완료되면 파라미터 그룹 목록에서 생성된 그룹을 볼 수 있습니다. 해당 <strong>파라미터 그룹을 클릭</strong>합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/f91a2790-f1d3-4127-a31a-d7111f955680/image.png" alt="rds-3"></p>
</li>
<li><p>이동한 상세 페이지의 우측 상단의 <strong>[파라미터 편집]</strong>을 클릭하여 편집 모드로 전환합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/5d96da80-2202-4362-9cf3-0ab6dff56c9a/image.png" alt="rds-4"></p>
</li>
<li><p>편집모드로 되었다면 <code>time_zone</code>을 검색하여 다음과 같이 <strong>[Asia/Seoul]</strong>을 선택합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/9fd6f97e-79b1-4fed-bc9f-78b1a0dbf754/image.png" alt="rds-5"></p>
</li>
<li><p>다음은 <code>Character Set</code>을 변경합니다.</p>
</li>
</ol>
<ul>
<li>Character_set_client</li>
<li>Character_set_connection</li>
<li>Character_set_database</li>
<li>Character_set_filesystem</li>
<li>Character_set_results</li>
<li>collation_connection</li>
<li>collation_server</li>
</ul>
<p>위의 항목들을 중에서 <code>Character</code> 항목들은 모두 <strong>utf8mb4</strong>로, <code>collation</code> 항목들은 <strong>utf8mb4_general_ci</strong>로 변경합니다. utf8과 utf8mb4의 차이는 이모지 저장 가능 여부입니다. </p>
<p>utf8은 이모지를 저장할 수 없지만 utf8mb4는 이모지를 저장할 수 있으므로 utf8mb4를 많이 사용합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/c8157e0d-9092-42da-8922-f83a31678759/image.png" alt="rds-6"></p>
<ol start="7">
<li><p>마지막으로 <code>Max Connection</code>을 수정합니다.
RDS의 Max Connection은 인스턴스 사양에 따라 자동으로 정해집니다. 
넉넉한 값으로 지정하여 우측 상단의 <strong>[변경 사항 저장]</strong> 버튼을 눌러 최종 저장합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/19becc90-e36e-431d-881d-55ab01137b7c/image.png" alt="rds-7"></p>
</li>
<li><p><strong>생성된 파라미터 그룹을 데이터베이스에 연결</strong>하기 위해 RDS 데이터베이스에서 [수정] 버튼을 클릭합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/1a4f1736-c724-486e-bc42-338ddfec6e6e/image.png" alt="rds-8"></p>
</li>
<li><p>옵션 항목에서 DB 파라미터 그룹은 default로 되어있을 것이며, DB 파라미터 그룹을 방금 생성한 신규 파라미터 그룹으로 변경합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/7e43dd5b-0e0a-4190-82a8-237b2648b3d5/image.png" alt="rds-9"></p>
</li>
</ol>
<p>저장을 누르게 되면 수정 사항이 요약된 것을 볼 수 있을 것이며, 여기서 반영 시점을 <strong>[즉시 적용]</strong>으로 합니다.</p>
<p>[예약된 다음 유지 관리 기간에 적용]으로 하면 지금 하지 않고, 새벽 시간대에 진행하게 되기 때문에 지금 서비스가 완전히 오픈된 상태가 아니기 때문에 즉시 적용합니다.</p>
<p>그럼 수정중 상태가 나오게 되며 새로운 파라미터 그룹이 반영될 것입니다.</p>
<p>이상으로 AWS RDS 파라미터 설정을 했습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS RDS 인스턴스 생성]]></title>
            <link>https://velog.io/@moon-july5/AWS-RDS-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%EC%83%9D%EC%84%B1</link>
            <guid>https://velog.io/@moon-july5/AWS-RDS-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%EC%83%9D%EC%84%B1</guid>
            <pubDate>Wed, 24 May 2023 03:46:29 GMT</pubDate>
            <description><![CDATA[<p><code>RDS(Relational Database Service)</code>는 <strong>AWS에서 지원하는 클라우드 기반 관계형 데이터베이스</strong>입니다. 하드웨어 프로비저닝, 데이터베이스 설정, 패치 및 백업과 같은 잦은 운영 작업을 자동화하여 개발자가 개발에 집중할 수 있게 지원하는 서비스 입니다.</p>
<p>추가적으로 조정 가능한 용량을 지원하여 예상치 못한 양의 데이터가 쌓여도 비용만 추가로 내면 정상적으로 서비스가 가능한 장점도 있습니다.</p>
<p>팀 프로젝트를 하면서 이 RDS를 이용하여 데이터베이스 환경을 구축하였습니다.</p>
<ol>
<li><p>AWS 검색 창에서 RDS를 입력하여 검색합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/4de27416-169a-4269-aa18-35713ce12d12/image.png" alt="rds-1"></p>
</li>
<li><p>RDS 대시보드에서 [데이터베이스 생성] 버튼을 클릭합니다. 
<img src="https://velog.velcdn.com/images/moon-july5/post/009fdbc4-88f0-4b80-917c-0e6b6420d588/image.png" alt="rds-2"></p>
</li>
<li><p>엔진 유형에서 저 같은 경우에는 <code>MySQL</code>을 선택했습니다. 제가 가장 잘 사용하는 데이터베이스이고 Oracle보다 MySQL, MariaDB, PostgreSQL이 사용량 대비 가격이 더 낮다고 알고 있기 때문에 선택했습니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/7b376249-610f-4904-bfb5-70b153c7ad76/image.png" alt="rds-3"></p>
</li>
<li><p>버전같은 경우는 최신버전으로 선택하였으며, 그 다음 <strong>프리티어</strong>를 선택합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/c0ba03b0-9622-4b22-aead-91fe380a49ff/image.png" alt="rds-4"></p>
</li>
<li><p>자신만의 <strong>DB 인스턴스 이름</strong>과 <strong>사용자 정보</strong>를 등록합니다. 여기서 사용된 사용자 정보로 실제 Database에 접근하게 됩니다. <img src="https://velog.velcdn.com/images/moon-july5/post/866bdc6f-37a8-4fca-9d71-ff0de8bf13c4/image.png" alt="rds-5"></p>
</li>
<li><p>기본적인 <strong>db.t2.micro</strong>로 선택합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/593cc781-6612-49f1-97b2-13deebc9a64a/image.png" alt="rds-6"></p>
</li>
<li><p>프리티어이기 때문에 기본적인 설정을 합니다.</p>
</li>
</ol>
<p><strong>[스토리지 자동 조정] 부분은 체크 해체</strong>하는 것이 좋을 수 있습니다. 왜냐하면 개발을 진행하다 최대값이 초과되면 자동으로 스토리지가 늘어나서 과금될 가능성이 있습니다.<img src="https://velog.velcdn.com/images/moon-july5/post/4a7aae61-f725-40ae-b446-dd6f8258d249/image.png" alt="rds-7"></p>
<ol start="8">
<li><p>보안 그룹을 설정하며, <strong>[퍼블릭 액세스]</strong>에 <strong>[예]</strong>로 선택합니다. 
[아니요]를 선택하면 퍼블릭 IP 주소가 할당되지 않기 때문에 외부에서 접근할 수 없습니다. 또한 <strong>VPC 보안 그룹을 새로 생성</strong>하여 이름을 정합니다. 
<img src="https://velog.velcdn.com/images/moon-july5/post/4c90fe65-22b9-49bd-ac4b-fdb46aa2a9d1/image.png" alt="rds-8"></p>
</li>
<li><p>Database 이름을 정합니다. 그리고 설정이 완료되었으면 <strong>[데이터베이스 생성]</strong>을 눌러 생성합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/a587064b-7d38-49df-829e-ecb90a95bb18/image.png" alt="rds-9"></p>
</li>
<li><p>생성 완료 까지는 시간이 걸릴 수 있습니다.
생성이 완료되면 성공했다는 메시지가 출력이 될 것입니다.</p>
</li>
</ol>
<p>여기까지 RDS 인스터스 생성 과정이였습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS EC2 서버 접속]]></title>
            <link>https://velog.io/@moon-july5/AWS-EC2-%EC%84%9C%EB%B2%84-%EC%A0%91%EC%86%8D</link>
            <guid>https://velog.io/@moon-july5/AWS-EC2-%EC%84%9C%EB%B2%84-%EC%A0%91%EC%86%8D</guid>
            <pubDate>Tue, 23 May 2023 11:45:23 GMT</pubDate>
            <description><![CDATA[<p>저 같은 경우에는 생성한 EC2 서버에 접속하기 위해 Window에서는 <strong><a href="https://putty.org/">putty</a></strong> 사이트에 접속하여 putty를 다운받아 사용했습니다.</p>
<ol>
<li>다운을 받은 후, <strong>puttygen.exe</strong> 파일을 실행합니다.putty는 pem 키로 사용이 안되기 때문에 pem 키를 ppk 파일로 변환해야 합니다. </li>
</ol>
<p><strong>puttygen</strong>은 이 과정을 진행해 주는 클라이언트입니다. puttygen 화면에서 <strong>[conversions] -&gt; [Import key]</strong>를 선택합니다. 그리고 전에 생성한 <strong>.pem</strong> 키를 불러옵니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/89f0aa9b-07e0-4327-85b0-b83391c182c4/image.png" alt="putty-1">
2. 아래와 같이 자동으로 변환이 진행하고 <strong>[Save private key]</strong> 버튼을 클릭하여 <strong>ppk</strong> 파일을 생성합니다.
경고 메시지가 뜨면 [예]를 클릭하고 넘어갑니다. 그 후 ppk 파일이 저장될 위치와 이름을 등록합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/1246c3e3-5a1b-4883-87b7-bd2036e69cbe/image.png" alt="putty-2">
3. ppk 파일이 생성되었으면 <strong>putty.exe</strong> 파일을 실행하여 다음과 같이 항목을 작성합니다. 
<img src="https://velog.velcdn.com/images/moon-july5/post/a171dd32-8d73-4497-aec8-58980d3f78ab/image.png" alt="putty-3">
4. 항목들을 모두 작성했다면 왼쪽에서 <strong>[Connection] -&gt; [SSH] -&gt; [Auth]</strong>을 클릭하여 ppk 파일을 로드할 수 있는 화면으로 이동 후, <strong>[Browse...]</strong> 버튼을 클릭하여 ppk 파일을 불러옵니다.<img src="https://velog.velcdn.com/images/moon-july5/post/9badb99f-18f2-46b0-9d1f-77c74bd35781/image.png" alt="putty-4">
5. <strong>[Session]</strong> 탭으로 이동하여 <strong>[Saved Sessions]</strong>에 현재 설정들을 저장할 이름을 등록하고 <strong>[Save]</strong> 버튼을 클릭합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/92d4f38a-5e8f-41c9-9599-30a4d211b894/image.png" alt="putty-5">
6. 저장한 뒤 [Open]을 클릭하면 SSH 접속 알림이 등장합니다. 그 후 [Accept]를 클릭하면 SSH 접속이 성공한 것을 확인할 수 있습니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/f22e1892-d9ec-43d4-b02e-bbf2ec607e6c/image.png" alt="putty-6"></p>
<p>여기서 추가적으로 자바 기반의 웹 애플리케이션이 작동하기 위해 필수로 해야 하는 설정들이 있습니다.</p>
<p>이번 프로젝트에서 <strong>JAVA 11</strong> 버전을 사용했기 때문에  Amazon Linux 2 AMI에서 JAVA 11을 설치하는 명령어를 통해 설치를 진행합니다.</p>
<pre><code>sudo amazon-linux-extras install -y java-openjdk11</code></pre><p>그 후 <strong>java --version</strong> 명령어로 자바가 제대로 설치되었는지 확인합니다.</p>
<p>이상으로 AWS EC2 서버에 접속하는 것까지 해봤습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS EC2 서버 생성]]></title>
            <link>https://velog.io/@moon-july5/AWS-EC2-%EC%84%9C%EB%B2%84-%EC%83%9D%EC%84%B1</link>
            <guid>https://velog.io/@moon-july5/AWS-EC2-%EC%84%9C%EB%B2%84-%EC%83%9D%EC%84%B1</guid>
            <pubDate>Tue, 23 May 2023 07:37:13 GMT</pubDate>
            <description><![CDATA[<p>이번에 미니 프로젝트 또는 기업 연계 프로젝트들을 하면서 제가 어떤 식으로 서버 배포를 했는지 잊지 않으려고 그 과정들을 정리하여 작성을 해보겠습니다.    </p>
<p>먼저, 서버를 생성하기 위해 <strong>AWS(Amazon Web Service)</strong>를 이용할 것입니다.</p>
<h2 id="📌-aws">📌 AWS??</h2>
<p>AWS는 <strong>아마존에서 개발한 클라우드 컴퓨팅 플랫폼</strong>입니다.</p>
<p>그래서 <strong>클라우드 컴퓨팅(Cloud Computing)</strong>은 무엇인지 궁금할 수 있습니다. 간단하게 말하자면, <strong>개인용 컴퓨터보다 성능이 좋을 수 있는 컴퓨터나 저장장치 등의 컴퓨터 자원을 다른 곳에서 빌려 사용할 수 있도록 처리해주는 IT 기술</strong>입니다.</p>
<p>이 클라우드 컴퓨팅은 여러 가지 장점이 있지만 그 중에서 <strong>최소의 시간으로 서비스를 게시하거나 배포</strong>할 수 있다는 점이 큰 장점입니다.</p>
<p>그래서 AWS는 전 세계에 분포된 데이터 센터에서 다양한 기능을 제공하는 클라우드 플랫폼입니다.</p>
<p>주요 서비스로는 뒤에 설명할 EC2와 같은 컴퓨팅 서비스, 네트워킹 서비스, 스토리지 서비스, 데이터베이스 서비스 등 많은 서비스가 존재합니다.</p>
<h2 id="📌-aws-ec2">📌 AWS EC2</h2>
<p>AWS EC2(Elastic Compute Cloud)는 <strong>가상화 서버</strong>이며, 다양한 형태의 타입과 서비스에 따라 적합한 사양을 선택할 수 있습니다. 그리고 <strong>사용량만큼 비용을 지불</strong>하는 컴퓨팅 서비스입니다.</p>
<h2 id="📌-인스턴스-생성">📌 인스턴스 생성</h2>
<p>현재 저 같은 경우는 AWS에서 <strong>Free Tier</strong>를 사용하고 있기 때문에, 사양이 <strong>t2.micro</strong>만 사용가능하고, <strong>월 750 시간의 제한</strong>이 존재합니다.</p>
<p>이제 AWS EC2에서 인스턴스를 생성해 보겠습니다.</p>
<p>💡 그 전에, EC2를 생성하기 전에, 본인의 <code>리전(Region)</code>을 확인하며, 서울로 되어있지 않다면 서울로 변경합니다. <img src="https://velog.velcdn.com/images/moon-july5/post/5dcd8ca6-7cf3-4455-b95b-d0ff288ec39f/image.png" alt="ec2"></p>
<ol>
<li><p>AWS 검색 창에서 <strong>EC2</strong>를 입력하여 서비스를 클릭하고, <strong>인스턴스</strong> -&gt; <strong>인스턴스 시작</strong>을 클릭합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/f874a56c-0125-4098-b668-b26f77e48e25/image.png" alt="ec2-1"></p>
</li>
<li><p>EC2 인스턴스의 이름을 입력합니다. <img src="https://velog.velcdn.com/images/moon-july5/post/dbb45cc1-0485-49ef-aa34-ceff72105332/image.png" alt="ec2-2"></p>
</li>
<li><p><strong>AMI(Amazon Machine Image)</strong>는 EC2 인스턴스를 시작하는 데 필요한 정보를 이미지로 만들어 준 것을 말합니다. 인스턴스라는 가상머신에 운영체제 등을 설치할 수 있게 구워 넣은 이미지로 생각하면 되며, 저는 아마존 리눅스 2 AMI를 선택했습니다. (처음에 최신 버전인 Amazon Linux 2023을 선택하여 생성했지만 서버 안에 필요한 프로그램들을 설치가 되질 않아 서버 버전을 바꿨습니다.)
<img src="https://velog.velcdn.com/images/moon-july5/post/bc5ea633-338f-4e6c-9e47-9a891f73cbae/image.png" alt="ec2-3"></p>
</li>
<li><p>인스턴스 유형에서는 프리티어로 표기된 t2.micro를 선택합니다. t2는 요금 타입을 말하며, micro는 사양을 말합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/4b9f2822-16ac-4cb9-877a-ce622705a844/image.png" alt="ec2-4"></p>
</li>
<li><p>인스턴스로 접근하기 위해서는 pem 키(비밀키)가 필요합니다. 그래서 [새 키 페어 생성]을 클릭합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/748992d8-57d1-4dfe-9974-35d2f91840bd/image.png" alt="ec2-5"></p>
</li>
<li><p>키 페어 생성에서 원하는 이름을 적고 키 페어 생성을 클릭합니다. 그러면 원하는 위치에 .pem을 다운받을 수 있습니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/06e66ad4-3ba2-4ece-9988-e722920b19f9/image.png" alt="ec2-6"></p>
</li>
<li><p>ssh이면서 포트 항목이 22인 경우는 AWS EC2에 터미널로 접속할 때를 의미합니다. 지정된 IP에서만 ssh 접속이 가능하도록 구성하는 것이 안전하며, 본인의 IP를 기본적으로 추가합니다. 
<img src="https://velog.velcdn.com/images/moon-july5/post/1ed4300d-a489-4cc3-b6cb-0919e6cf2d21/image.png" alt="ec2-7"></p>
</li>
<li><p>스토리지를 설정합니다. 흔히 하드디스크라고 부르는 서버의 디스크를 말하며, 서버의 용량을 얼마나 정할지 선택하는 단계입니다. 여기서 기본값은 8GB인데, 30GB까지 프리티어로 가능하기 때문에 최대치인 30GB로 설정합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/f8fb741e-99a8-4496-9de1-a19c51334864/image.png" alt="ec2-8"></p>
</li>
<li><p>설정을 완료하게 되면, 우측에 간단하게 지금까지 설정한 인스턴스 요약이 나올 것입니다. 이제 더 설정할 부분이 없으면 인스턴스 시작을 클릭합니다. 
<img src="https://velog.velcdn.com/images/moon-july5/post/07c14b28-d72e-4951-9cef-ae43d55888e4/image.png" alt="ec2-9"></p>
</li>
</ol>
<p>생성하게 되면 인스턴스가 실행중인 것을 확인하실 수 있을 것입니다.</p>
<hr>
<p>인스턴스도 하나의 서버이기 때문에 IP가 존재합니다.</p>
<p>여기서 인스턴스를 중지하고 다시 시작할 때도 새 IP가 할당됩니다. 이렇게 되면 접속해야 하는 IP가 변경되어 PC에서 접근할 때마다 IP 주소를 확인해야 합니다. </p>
<p>굉장히 번거롭기 때문에 인스턴스의 IP가 매번 변경되지 않고 고정 IP를 가지게 해야 합니다. 그래서 고정 IP를 할당할 필요가 있습니다.</p>
<p>AWS의 고정 IP를 <strong>탄력적 IP(Elastic IP)</strong>라고 합니다. </p>
<h2 id="📌-탄력적-ip-할당">📌 탄력적 IP 할당</h2>
<p>현재 생성된 인스턴스 정보를 확인해보면, [탄력적 IP 주소]가 비어있는 것을 확인할 수 있습니다.</p>
<p>그러면 이 탄력적 IP를 할당받도록 해보겠습니다.</p>
<ol>
<li><p>[탄력적 IP]를 클릭하여 우측 상단에 [탄력적 IP 주소 할당]을 클릭합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/bf1162c5-a339-4eae-b320-62b3e09b8576/image.png" alt="ip-1"></p>
</li>
<li><p>여기서 설정하지 않고 바로 [할당]을 클릭합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/aff667ab-2554-4f6c-b9f0-608a826a1673/image.png" alt="ip-2"></p>
</li>
<li><p>탄력적 IP 주소가 할당되었다는 메시지가 출력된 것을 확인할 수 있습니다.
여기서 [이 탄력적 IP 주소 연결]을 클릭합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/4a115c16-0f50-443c-95fd-0268c7228dee/image.png" alt="ip-3"></p>
</li>
<li><p>설정 화면에 들어가게 되면 현재 내 인스턴스 목록과 프라이빗 IP 주소를 확인할 수 있습니다. 그대로 [연결]을 클릭합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/7b329981-b6e1-47e7-bbd3-111941093dbd/image.png" alt="ip-4"></p>
</li>
</ol>
<p>여기서 인스턴스 정보를 확인해보면 탄력적 IP주소가 할당된 것을 확인할 수 있고 퍼블릭 IP 주소도 기존 값에서 탄력적 IP 주소로 자동으로 변경된 것을 확인할 수 있습니다.</p>
<p>여기까지 EC2 인스턴스 생성 과정입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[객체 지향 설계의 5가지 원칙]]></title>
            <link>https://velog.io/@moon-july5/%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%EC%84%A4%EA%B3%84%EC%9D%98-5%EA%B0%80%EC%A7%80-%EC%9B%90%EC%B9%99</link>
            <guid>https://velog.io/@moon-july5/%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%EC%84%A4%EA%B3%84%EC%9D%98-5%EA%B0%80%EC%A7%80-%EC%9B%90%EC%B9%99</guid>
            <pubDate>Sun, 21 May 2023 11:47:07 GMT</pubDate>
            <description><![CDATA[<p>이 객체 지향 설계의 5가지 원칙이 스프링과 관련이 있기 때문에 정리를 해보겠습니다. </p>
<p>클린코드로 유명한 로버트 마틴이 좋은 객체 지향 설계의 5가지 원칙을 정리했습니다.</p>
<ul>
<li><p><strong>SRP</strong> : 단일 책임의 원칙(Single Responsibility Priciple)</p>
</li>
<li><p><strong>OCP</strong> : 개방-폐쇄 원칙(Open/Closed Principle)</p>
</li>
<li><p><strong>LSP</strong> : 리스코프 치환 원칙(Liskov Substitution Principle)</p>
</li>
<li><p><strong>ISP</strong> : 인터페이스 분리 원칙(Interface Segregation Principle)</p>
</li>
<li><p><strong>DIP</strong> : 의존관계 역전 원칙(Dependency Inversion Principle)</p>
</li>
</ul>
<h2 id="📌-srp-single-responsibility-priciple-단일-책임의-원칙">📌 SRP (Single Responsibility Priciple, 단일 책임의 원칙)</h2>
<p><strong>한 클래스는 하나의 책임만 가져야 한다는 원칙</strong>입니다.</p>
<p>하나의 책임이라는 것은 모호할 수 있습니다.</p>
<p><strong>중요한 기준은 변경</strong>입니다. 변경이 있을 때 파급 효과가 적으면 단일 책임 원칙을 잘 따른 것이라고 볼 수 있습니다.</p>
<h2 id="📌-ocp-openclosed-principle-개방-폐쇄-원칙">📌 OCP (Open/Closed Principle, 개방-폐쇄 원칙)</h2>
<p><strong>소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다는 원칙</strong>입니다.</p>
<p><strong>다형성을 활용하여 인터페이스를 구현한 새로운 클래스를 하나 만들어서 새로운 기능을 구현</strong>합니다.</p>
<p>구현 객체를 변경하려면 <strong>클라이언트 코드를 변경해야 합니다.</strong></p>
<p>분명 다형성을 사용했지만 OCP 원칙을 지킬 수 없습니다. 이 문제를 해결하기 위해 <strong>객체를 생성하고, 연관 관계를 맺어주는 별도의 조립, 설정자가 필요합니다.</strong></p>
<p>이 문제점은 <strong>스프링</strong>이 해결할 수 있습니다.</p>
<h2 id="📌-lsp-liskov-substitution-principle-리스코프-치환-원칙">📌 LSP (Liskov Substitution Principle, 리스코프 치환 원칙)</h2>
<p><strong>프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다는 원칙</strong>입니다.</p>
<h2 id="📌-isp-interface-segregation-principle-인터페이스-분리-원칙">📌 ISP (Interface Segregation Principle, 인터페이스 분리 원칙)</h2>
<p><strong>특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보나 낫다는 원칙</strong>입니다.</p>
<p>인터페이스가 명확해지고 대체 가능성이 높아집니다.</p>
<h2 id="📌-dip-dependency-inversion-principle-의존관계-역전-원칙">📌 DIP (Dependency Inversion Principle, 의존관계 역전 원칙)</h2>
<p><strong>구현 클래스에 의존하지 말고, 인터페이스에 의존하라는 원칙</strong>입니다.</p>
<p><strong>클라이언트가 인터페이스에 의존해야 유연하게 구현체를 변경할 수 있고,</strong> 구현체에 의존하게 되면 변경이 아주 어려워 질 것입니다.</p>
<p>이상으로 객체 지향 설계의 5가지 원칙에 대해서 간단히 알아봤습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[IoC(Inversion of Control)]]></title>
            <link>https://velog.io/@moon-july5/IoCInversion-of-Control</link>
            <guid>https://velog.io/@moon-july5/IoCInversion-of-Control</guid>
            <pubDate>Sat, 29 Apr 2023 05:49:04 GMT</pubDate>
            <description><![CDATA[<p><code>IoC</code>란 <strong>제어의 역전</strong>이라는 것인데, 제어가 역전 됐다는 의미입니다. 어떤 뜻인지 이해 못할 수도 있는데 <strong>스프링에게 주도권을 빼앗겼다!</strong> 라는 뜻도 됩니다.</p>
<p>스프링을 사용하기 전에 개발자가 프로그램의 흐름을 제어하는 주체가 됐지만 스프링에서는 프로그램의 흐름을 프레임워크가 주도하게 됩니다. 객체의 생성부터 삭제까지의 생명주기 관리를 컨테이너가 맡아서 하게 된 것입니다.</p>
<p>스프링 IoC 컨테이너가 관리하는 객체들을 <code>Bean</code> 이라고 부릅니다. 스프링은 이러한 <code>Bean</code>들의 의존성을 관리하고, 객체를 만들어 주며, <code>Bean</code>으로 등록을 해 주고, 이렇게 만들어진 것들을 관리합니다.</p>
<blockquote>
<p>🔎 IoC 컨테이너?? DI 컨테이너??
객체를 생성하고 관리하면서 의존관계를 연결해 주는 것을 IoC 컨테이너 또는 DI 컨테이너라고 합니다.
의존관계 주입에 초점을 맞춰 최근에는 주로 DI 컨테이너라고 하며, 어셈블러, 오브젝트 팩토리 등으로 불리기도 합니다.</p>
</blockquote>
<h3 id="💡-프레임워크-vs-라이브러리">💡 프레임워크 vs 라이브러리</h3>
<ul>
<li><p>프레임워크가 내가 작성한 코드를 제어하고, 대신 실행하면 그것은 프레임워크가 맞습니다.</p>
</li>
<li><p>하지만 내가 작성한 코드가 직접 제어의 흐름을 담당한다면 그것은 프레임워크가 아니라 라이브러리입니다.</p>
</li>
</ul>
<p>이상으로 IoC에 대해서 간단하게 알아봤습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[DI(Dependency Injection)]]></title>
            <link>https://velog.io/@moon-july5/DIDependency-Injection</link>
            <guid>https://velog.io/@moon-july5/DIDependency-Injection</guid>
            <pubDate>Fri, 21 Apr 2023 04:02:37 GMT</pubDate>
            <description><![CDATA[<p><strong>의존</strong>이라는 단어에서 알 수 있듯이 DI는 의존을 처리하는 방법에 대한 내용입니다. </p>
<p>일단 DI는 <code>의존(Dependency)</code>에 대한 설계 패턴이므로, 의존의 의미를 알아야 합니다.</p>
<p>어떤 프로그램을 개발할 때, 단 한 개의 클래스에 모든 코드를 몰아넣는 경우는 없습니다. </p>
<p>예를 들어, 어떤 A 클래스의 print() 메서드가 존재하며, 이 메서드 안에는 BufferedReader 클래스를 사용하여 내용을 출력하고 있습니다. 이것은 print() 메서드를 실행하기 위해 BufferedReader 클래스가 필요하다는 의미입니다. </p>
<p>이렇게 print 기능을 사용하기 위해 다른 클래스(또는 타입)를 필요로 할 때 이를 <code>의존(Dependency)</code>한다고 말합니다.</p>
<p>아까 예에서 A 클래스가 BufferedReader 클래스에 의존한다고 말할 수 있습니다.</p>
<p>의존하는 객체를 직접 생성해서 지역 변수로 보관하거나 의존하는 타입을 필드로 정의하기도 합니다.</p>
<p>아니면 의존하는 타입의 객체를 직접 생성하지 않고 생성자를 통해서 의존 객체를 전달받을 수 있습니다.</p>
<p>아래의 코드는 <strong>의존하는 타입의 객체를 직접 생성하는 방식의 코드</strong>입니다.</p>
<pre><code class="language-java">public class FileEncryptor{
    // 의존 객체를 직접 생성
    private Encryptor encryptor = new Encryptor();

    public void encrypt(File src, File target) throws IOException{
        try(
            FileInputStream is = new FileInputStream(src);
            FileOutputStream out = new FileOutputStream(target)) {
            ...
            encryptor.encryptor(data, 0, len);
          }
    }
}</code></pre>
<p>위 코드는 객체를 직접 생성하고 있습니다. </p>
<p>그런데 갑자기 요구사항의 변화로 <code>Encryptor</code> 클래스의 하위 클래스인 <code>FastEncryptor</code> 클래스를 사용해야 하는 상황이 왔다고 가정하면 이 경우 아래의 코드처럼 <code>FileEncryptor</code> 클래스의 코드를 변경해줘야 합니다.</p>
<pre><code class="language-java">public class FileEncryptor{
    // Encryptor 대신에 FastEncryptor를 사용하려면 변경
    private Encryptor encryptor = new FastEncryptor();</code></pre>
<p>만약 <code>Encryptor</code> 클래스 대신에 <code>FastEncryptor</code> 클래스의 객체를 사용해야 하는 코드가 많다면, 그에 비례해서 변경해주어야 하는 코드의 양도 증가하는 끔찍한 상황이 발생할 것입니다.</p>
<p>그래서 <strong>의존하는 타입의 객체를 직접 생성하는 방식의 코드는 개발 생산성이 전체적으로 낮아지는 상황이 발생합니다.</strong></p>
<p>의존 객체를 직접 생성하는 방식과 달리 <code>DI(Dependency Injection)</code>는 <strong>의존 객체를 외부로부터 전달받는 구현 방식</strong>입니다. 아까 말했듯이 이 방식은 <code>생성자</code>를 이용해서 의존 객체를 전달받는 방식이 DI에 따라 구현한 것입니다.</p>
<pre><code class="language-java">public class FileEncryptor{
    private Encryptor encryptor;

    public FileEncryptor(Encryptor encryptor){
        // 생성자로 전달받은 객체를 필드에 할당
        this.encryptor = encryptor;
    }

    public vodi encryptor(File src, File target) throws IOException{
        ...
        // DI 방식으로 전달받은 객체를 사용
        encryptor.encrypt(data, 0, len);
        ...
    }
}</code></pre>
<p>위 코드는 생성자를 통해서 <code>Encryptor</code> 타입의 객체를 전달받고 있습니다.</p>
<p>이는 <code>FileEncryptor</code> 객체를 생성하는 부분에서 <code>Encryptor</code> 객체를 전달해주어야 함을 의미합니다.</p>
<pre><code class="language-java">// 다른 코드에서 의존 객체를 생성
Encrpytor enc = new Encryptor();
// 의존 객체를 전달해 줌
FileEncryptor fileEnc = new FileEncryptor(enc);</code></pre>
<p>위와 같은 방식을 <code>Dependency Injection(의존 주입)</code>, <code>DI</code>라고 부릅니다.</p>
<p>또 다른 말로는 객체를 연결(wire)한다는 표현을 씁니다. 위 코드에서 <code>FileEncrpytor</code> 객체에 <code>Encryptor</code> 객체를 연결한다고 말하기도 합니다.</p>
<p>그럼 여기서 누가 객체를 생성하고 객체들을 서로 연결해주는 것일까요?</p>
<p>그런 역할을 수행하는 것이 바로 <code>조립기</code>입니다.</p>
<pre><code class="language-java">public clas Assembler{
    private FileEncryptor fileEnc;
    private Encryptor enc;

    public Assembler(){
        enc = new Encryptor();
        fileEnc = new FileEncryptor(enc);
    }

    public FileEncryptor fileEncryptor(){
        return fileEnc;
    }
}</code></pre>
<p>위 코드는 간단한 조립기의 구현 코드예 입니다.</p>
<p>이제 <code>FileEncryptor</code>가 필요한 곳에서는 조립기를 이용해서 <code>FileEncryptor</code>를 구하면 됩니다.</p>
<pre><code class="language-java">Assembler assembler = new Assembler();
FileEncryptor fileEnc = assembler.fileEncryptor();
fileEnc.encrypt(srcFile, targetFile);</code></pre>
<p>만약 <code>FileEncryptor</code>가 사용해야 할 <code>Encryptor</code> 객체의 타입이 <code>FastEncryptor</code>로 변경하려면 조립기의 코드를 바꾸면 됩니다. <code>FastEncryptor</code> 객체를 사용하도록 변경해주어야 하는 곳이 많더라도 변경되는 부분은 조립기로 제한됩니다.</p>
<p><strong>스프링(Spring)</strong>이 바로 이 조립기에 해당합니다.</p>
<p>또한 <code>DI</code>를 사용할 때는 의존하는 클래스의 구현이 완성되어 있지 않더라도 테스트를 할 수 있다는 장점이 있습니다.</p>
<p>이상으로 DI에 대해서 간단히 알아봤습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2023.04.10 ~ 2023.04.14 스터디]]></title>
            <link>https://velog.io/@moon-july5/2023.04.10-2023.04.14-%EC%8A%A4%ED%84%B0%EB%94%94</link>
            <guid>https://velog.io/@moon-july5/2023.04.10-2023.04.14-%EC%8A%A4%ED%84%B0%EB%94%94</guid>
            <pubDate>Sun, 16 Apr 2023 04:12:11 GMT</pubDate>
            <description><![CDATA[<p>한 주 동안 해결한 알고리즘 문제들중에서 몇몇 문제에 대해서 배운 점이나 주의해야할 점을 기록하겠습니다.</p>
<p>(모두 <a href="https://www.acmicpc.net/">백준</a>에서 알고리즘 문제를 풀었습니다.)</p>
<hr>
<h2 id="📌-7569번-토마토---🏅gold-ⅴ">📌 7569번 토마토 - 🏅Gold Ⅴ</h2>
<ul>
<li><p>문제 링크 : <a href="https://www.acmicpc.net/problem/7569">토마토</a></p>
</li>
<li><p>알고리즘 분류 : <strong>BFS</strong></p>
</li>
<li><p>언어 : <strong>JAVA</strong></p>
</li>
<li><p>다른 사람의 코드를 참고하지 않고 해결 ✅</p>
</li>
<li><p><a href="http://boj.kr/c6427e8e5ddb47718c1b92465f342718">소스 코드 링크</a></p>
</li>
</ul>
<p>이런 비슷한 류의 문제를 풀어봤기 때문에 풀 수 있었던 것 같습니다. 아마 모르는 상태로 이 문제를 풀었다면 못 풀었을 것 같습니다.</p>
<p>이 문제는 <strong>익은 토마토</strong>를 기준으로 <strong>안 익은 토마토</strong>가 영향을 받아 익게 되는 점(확산) 때문에 <code>BFS</code>를 떠올릴 수 있습니다.</p>
<p>그렇기 때문에 토마토들의 상태를 입력받을 때, <strong>익은 토마토의 좌표</strong>를 미리 <code>Queue</code>에 저장합니다.</p>
<p>주의해야할 점은 격자모양 상자 하나 안에 여러 개의 토마토가 담길 것인데, 이 상자가 여러 개 쌓일 수 있기 때문에 x축, y축 뿐만 아니라 <strong>z축</strong> 까지 고려하여 탐색을 해야 합니다.</p>
<p>또한, <code>BFS</code>를 통해 <strong>익은 토마토</strong>를 기준으로 주변에 <strong>안 익은 토마토</strong>를 익게 하면서, 이전에 있던 좌표 값에서 +1을 하면서 몇 일 걸리는지 계산합니다.</p>
<p>모든 과정이 끝나면, 상자안에 토마토가 안 익은게 있는지 전부 탐색합니다.</p>
<hr>
<h2 id="📌-1780번---종이의-개수-🥈-silver-ⅱ">📌 1780번 - 종이의 개수 🥈 Silver Ⅱ</h2>
<ul>
<li><p>문제 링크 : <a href="https://www.acmicpc.net/problem/1780">종이의 개수</a></p>
</li>
<li><p>알고리즘 분류 : <strong>재귀</strong></p>
</li>
<li><p>언어 : <strong>JAVA</strong></p>
</li>
<li><p>다른 사람의 코드를 참고하지 않고 해결 ❌</p>
</li>
<li><p><a href="http://boj.kr/eb651e589a4d49f697684467c16056d0">소스 코드 링크</a></p>
</li>
</ul>
<p>재귀에 익숙치 않아서 생각하기 힘들었던 문제였습니다.</p>
<p>먼저, 한 <code>size</code>안에 같은 숫자(-1, 0, 1)만 있는지 체크할 필요가 있습니다. 같은 숫자면 그대로 그 숫자들의 개수를 count만 해주면 됩니다.</p>
<p>그게 아니면 종이를 9등분 해야하기 때문에 <code>size</code>를 3으로 나눠야 합니다.</p>
<p>9등분 된 종이를 같은 숫자만 존재하는지 확인하기 위해 <code>재귀 호출</code>을 합니다.</p>
<p>여기서 배운점은 3으로 나눈 <code>size</code>를 잘 응용해서 재귀 호출하여 탐색할 때마다 시작하는 인덱스를 정해주면 된다는 것을 배웠습니다.</p>
<hr>
<h2 id="📌-1520번---내리막길-🏅-gold-ⅲ">📌 1520번 - 내리막길 🏅 Gold Ⅲ</h2>
<ul>
<li><p>문제 링크 : <a href="https://www.acmicpc.net/problem/1520">내리막길</a></p>
</li>
<li><p>알고리즘 분류 : <strong>DFS</strong>, <strong>DP</strong></p>
</li>
<li><p>언어 : <strong>JAVA</strong></p>
</li>
<li><p>다른 사람의 코드를 참고하지 않고 해결 ❌</p>
</li>
<li><p><a href="http://boj.kr/34726ca6c0fb495987cbd6dcd8c0b561">소스 코드 링크</a></p>
</li>
</ul>
<p>이 문제는 <code>DFS</code>와 <code>DP</code>를 응용한 문제입니다. </p>
<p>어떻게 보면 기본적인 <code>DFS</code>를 구현할 때 큰 틀은 비슷합니다. 여기에 <code>DP</code>의 <strong>메모이제이션</strong>을 사용하여 현재 까지의 경로의 수를 저장해야 합니다.</p>
<blockquote>
<p>메모이제이션을 사용하지 않고 그냥 <code>DFS</code>를 사용하게 되면 시간초과 발생</p>
</blockquote>
<p>먼저, <code>DP</code>를 위한 배열을 모두 -1로 초기화해야 할 필요가 있습니다. 이는 어찌보면 방문 처리와 비슷한 역할을 합니다.(-1은 아직 방문하지 않은 칸입니다.)</p>
<p>중요한 점은 시작점에서부터 끝점까지 <code>DFS</code> 탐색을 하는건 똑같지만 이미 탐색해서 경로의 개수가 파악된 지점을 또 탐색하게 되면, 저장된 경로의 개수를 반환하면 됩니다.</p>
<p>왜 이렇게 하나면, 특정 지점에서 끝 점까지 경로의 개수가 저장되지 않았다면 끝 점까지 경로를 구하는데, 경로의 개수를 중복해서 세야하기 때문입니다.</p>
<p>그래서, 끝 점까지 탐색이 완료되면 <code>DP</code>를 사용하여 경로의 개수를 저장하고 다른 지점에서 또 탐색됐을 때는 이 경로를 더해주는 방식으로 구현해야 합니다.</p>
<hr>
<h2 id="📌-2146번---다리-만들기-🏅-gold-ⅲ">📌 2146번 - 다리 만들기 🏅 Gold Ⅲ</h2>
<ul>
<li><p>문제 링크 : <a href="https://www.acmicpc.net/problem/2146">다리 만들기</a></p>
</li>
<li><p>알고리즘 분류 : <strong>BFS</strong></p>
</li>
<li><p>언어 : <strong>JAVA</strong></p>
</li>
<li><p>다른 사람의 코드를 참고하지 않고 해결 ❌</p>
</li>
<li><p><a href="http://boj.kr/a3937cf6ae0c4eae87d752be74f28ee8">소스 코드 링크</a></p>
</li>
</ul>
<p>이 문제에서 육지와 바다가 존재하는데, 이 육지를 구분해야할 필요가 있습니다.</p>
<p>예를 들어, 어떤 육지는 숫자 2로, 또 다른 육지는 숫자 3으로 변환하여 저장하는 것입니다.</p>
<p>왜냐하면, 육지와 육지 사이에 다리를 만드려고 코드를 구현하려고 하는데, 어떤 한 육지에서 계속 탐색하다가 바다를 건너 다른 육지까지 이동했는데, 이게 다른 육지인지 코드상에서 구분할 수 없기 때문입니다.</p>
<p>그래서 각각 육지를 나눴을 때, 이 문제를 수월하게 해결할 수 있습니다.</p>
<p>그리고, 이 구분된 육지를 가지고 <code>BFS</code> 탐색을 합니다.</p>
<p>이동하려는 위치가 바다면, 이동하려는 좌표와 다리의 개수를 +1해서 <code>Queue</code>에 저장합니다.</p>
<p>이런 식으로 과정이 진행되다가 이동하려고 하는 위치가 <strong>바다도 아니고 현재 육지와 다르다면 다리의 개수를 반환</strong>하도록 합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Cookie, Session, Token]]></title>
            <link>https://velog.io/@moon-july5/Cookie-Session-Token</link>
            <guid>https://velog.io/@moon-july5/Cookie-Session-Token</guid>
            <pubDate>Fri, 14 Apr 2023 06:13:30 GMT</pubDate>
            <description><![CDATA[<p>먼저, <code>Cookie</code>, <code>Session</code>, <code>Token</code>에 대해서 설명하기에 앞서 <code>HTTP</code> 프로토콜 특징에 대해서 알아야 할 필요가 있습니다.</p>
<p><code>HTTP</code> 프로토콜은 요청에 따른 <strong>응답을 받으면 끊어집니다.(Connectionless)</strong>, 즉, 한번 클라이언트와 서버가 서로 연결이 이뤄지고 요청 응답이 완료되었으면 연결이 끊기는데 왜냐하면 <code>HTTP</code> 자체가 <strong>인터넷 상에서 불특정 다수와 연결되는 통신 환경을 만들기위해 설계되었기 때문입니다.</strong></p>
<p>서버가 다수의 클라이언트와 연결이 끊기지 않고 계속 연결되어 있으면, 연결 유지를 위한 리소스가 너무 많이 필요하게 되어, 서버 관리 측면에 있어서는 단점으로 작용하기 때문입니다.</p>
<p>그리고 <code>HTTP</code> 프로토콜은 <strong>통신이 종료되면 어떠한 상태 정보도 남지 않습니다.(Stateless)</strong></p>
<p>따라서, 로그인하고 다른 웹페이지로 접근 시, 로그인 상태가 유지되지 않아 계속 중복되는 과정을 거쳐야할 수 있게 됩니다. </p>
<p>이러한 불필요한 작업을 제거하기 위해 <code>쿠키(Cookie)</code>가 등장하였습니다.</p>
<h2 id="📌-쿠키cookie">📌 쿠키(Cookie)</h2>
<p>쿠키는 웹사이트에 접속할 때, 서버쪽에서 웹 브라우저(클라이언트) 메모리에 정보를 기록하는 <code>key-value</code> 쌍의 데이터 파일입니다. </p>
<p>즉, <strong>웹 브라우저(클라이언트)는 이러한 데이터 파일들을 저장해 놓았다가, 동일한 서버에 재 요청 시 저장된 데이터를 함께 전송합니다.</strong> </p>
<p>쿠키는 두 요청이 동일한 브라우저에서 들어왔는지 아닌지를 판단할 때 주로 사용합니다. 이를 이용하면 사용자의 로그인 상태를 유지할 수 있습니다. </p>
<p>쿠키는 항상 고정된 값을 가지진 않고, <strong>사용자가 웹 사이트를 접근할 때마다 수시로 정보가 바뀝니다.</strong></p>
<h3 id="💡-쿠키cookie-구성-요소">💡 쿠키(Cookie) 구성 요소</h3>
<ul>
<li><strong>Name</strong> : 쿠키의 이름</li>
<li><strong>Value</strong> : 쿠키에 저장된 값</li>
<li><strong>Expires</strong> : 쿠키가 언제 삭제되는지 만료일을 설정</li>
<li><strong>Domain</strong> : 쿠키가 사용되는 도메인을 지정. 이 값이 현재 탐색 중인 도메인과 일치하지 않을 경우, &quot;타사 쿠키&quot;로 간주되어 브라우저에서 거부. 이렇게 해서 한 도메인에서 다른 도메인에 대한 쿠키를 사용하지 못하게 설정</li>
<li><strong>Path</strong> : 쿠키가 반환할 경로 설정</li>
<li><strong>Secure</strong> : 보안 연결 설정</li>
<li><strong>HttpOnly</strong> : HTTP외에 다른 통신 사용 가능 설정</li>
</ul>
<h3 id="💡-쿠키cookie-종류">💡 쿠키(Cookie) 종류</h3>
<table>
<thead>
<tr>
<th align="center">쿠키이름</th>
<th align="center">특징</th>
</tr>
</thead>
<tbody><tr>
<td align="center">Session Cookie</td>
<td align="center">보통 만료시간(Expire date)를 설정하고 메모리에만 저장되며 브라우저 종료시 쿠키를 삭제</td>
</tr>
<tr>
<td align="center">Persistent Cookie</td>
<td align="center">장기간 유지되는 쿠키, 파일로 저장되어 브라우저 종료와 관계없이 사용</td>
</tr>
<tr>
<td align="center">Secure Cookie</td>
<td align="center">HTTPS에서만 사용, 쿠키 정보가 암호화되어 전송</td>
</tr>
<tr>
<td align="center">Third-party Cookie</td>
<td align="center">방문한 도메인과 다른 도메인의 쿠키 보통 광고 베너 등을 관리할 때 유입 경로를 추적하기 위해 사용</td>
</tr>
</tbody></table>
<h3 id="💡-쿠키cookie-동작-방식">💡 쿠키(Cookie) 동작 방식</h3>
<p><img src="https://velog.velcdn.com/images/moon-july5/post/5b5b70db-9092-4615-b634-28078d7bcb0a/image.png" alt="cookie-1">1. 클라이언트가 페이지를 요청
2. 서버에서 쿠키를 생성하고 HTTP 헤더에 쿠키를 포함하여 응답하며, 브라우저가 종료되어도 쿠키 만료 기간이 있다면 클라이언트에서 보관
3. 같은 요청을 할 경우, HTTP 헤더에 쿠키를 함께 요청
4.서버에서 쿠키를 읽어 이전 상태 정보를 변경할 필요가 있을 때, 쿠키를 업데이트하여 변경된 쿠키를 HTTP 헤더에 포함시켜 응답</p>
<h3 id="💡-쿠키cookie-단점">💡 쿠키(Cookie) 단점</h3>
<ul>
<li><p>쿠키에 대한 정보를 매번 헤더에 추가시켜 보내기 때문에 상당한 트래픽을 발생시킵니다.</p>
</li>
<li><p>쿠키같은 경우는 브라우저에 정보가 저장되기 때문에, 쿠키가 유출되면 보안 문제가 발생합니다.</p>
<h2 id="📌-세션session">📌 세션(Session)</h2>
<p>세션은 쿠키의 단점을 보완하여 등장하였습니다.</p>
</li>
</ul>
<p>세션은 쿠키를 기반으로 하고 있습니다.</p>
<p>데이터 파일을 브라우저에 저장하는 쿠키(Cookie)와 달리 세션(Session)은 <strong>서버 측에서 관리</strong>합니다.</p>
<h3 id="💡-세션session의-동작-방식">💡 세션(Session)의 동작 방식</h3>
<ol>
<li><p>클라이언트가 서버에 접속 시 <code>Session ID</code>를 발급 받습니다.</p>
</li>
<li><p>클라이언트는 <code>Session ID</code>에 대해 쿠키를 사용해서 저장하고 가지고 있습니다.</p>
</li>
<li><p>클라이언트는 서버에 요청할 때, 이 쿠키의 <code>Session ID</code>를 같이 서버에 전달해서 요청합니다.</p>
</li>
<li><p>서버는 <code>Session ID</code>를 전달 받아서 별다른 작업없이 <code>Session ID</code>로 세션에 있는 클라이언트 정보를 가져와서 사용합니다.</p>
</li>
<li><p>클라이언트 정보를 가지고 서버 요청을 처리하여 클라이언트에게 응답합니다.</p>
</li>
</ol>
<blockquote>
<p>세션은 각 클라이언트에게 고유 ID를 부여하고 <code>Session ID</code>로 클라이언트를 구분해서 클라이언트의 요구에 맞는 서비스를 제공합니다. 그리고 보안 면에서는 쿠키보다 우수합니다.</p>
</blockquote>
<h3 id="💡-세션session의-단점">💡 세션(Session)의 단점</h3>
<ul>
<li><p>클라이언트의 수가 많아지면 서버에 저장해야할 Session 또한 많아지기 때문에 서버의 과부하를 일으킬 수 있습니다.</p>
</li>
<li><p>서버가 여러대 일 경우, 한 개의 클라이언트를 위한 세션 정보를 다른 서버들이 모두 가지고 있어야 서버를 분산해서 운영할 때도, 클라이언트에 대한 인증 절차가 가능해집니다. 그렇기 때문에 성능 저하의 요인이 됩니다.</p>
</li>
</ul>
<h2 id="📌-토큰token">📌 토큰(Token)</h2>
<p>토큰은 <strong>보호해야될 데이터들을 토큰으로 치환하여 원본 데이터 대신 토큰을 기반으로 서버 클라이언트간 인증을 진행하는 방식입니다.</strong></p>
<p>즉, 인증을 위해 사용되는 암호화된 문자열이라 할 수 있다.</p>
<p>이러한 토큰 기반의 인증 방식은 <strong>상태를 유지하지 않는 Stateless입니다.</strong></p>
<h3 id="💡-토큰token의-동작-방식">💡 토큰(Token)의 동작 방식</h3>
<p><img src="https://velog.velcdn.com/images/moon-july5/post/f84a7219-ede8-44bc-9693-5460e223e5d5/image.png" alt="token"> </p>
<ol>
<li><p>클라이언트가 서버에게 로그인 요청을 보냅니다.</p>
</li>
<li><p>서버는 클라이언트의 정보를 검증한 후, 사인된(signed) 토큰을 생성해서 응답합니다. 여기서 signed는 해당 토큰이 서버에서 정상적으로 발급된 토큰임을 증명하는 <code>signature</code>를 지니고 있습니다.</p>
</li>
<li><p>클라이언트 측에서 전달받은 토큰을 저장해두고, 서버에 요청할 때마다 해당 토큰을 함께 서버에 전달합니다.</p>
</li>
<li><p>서버는 토큰을 검증 후, 이에 대한 응답을 합니다.</p>
</li>
</ol>
<p>이러한 토큰 기반의 인증은 <strong>쿠키(Cookie)의 단점인 보안 문제와 세션(Session)의 단점인 서버 분산 관리 시 모든 서버가 클라이언트의 정보를 각각 모두 가져야 한다는 점을 모두 해결해 줄 수 있습니다.</strong></p>
<p>인증 토큰 값 하나만 있으면 관련 모든 서버에서 인증되며, 브라우저에 직접 저장하는 방식도 아니어서 보안상의 큰 이점을 갖습니다.</p>
<p>또한 가장 큰 장점은 <code>무상태(Stateless)</code>이며, <code>확장성(Scalability)</code>이 있다는 점입니다.</p>
<p>토큰은 클라이언트 측에 저장하기 때문에 완전히 Stateless하며, 서버를 확장하기에 매우 적합한 환경을 제공합니다.</p>
<p>대표적인 토큰 기반의 인증 방식은 <code>JWT(Json Web Token)</code>이 있습니다.</p>
<p>이상으로 Cookie, Session, Token에 대해서 간단히 알아봤습니다.</p>
<h3 id="참고">참고</h3>
<ul>
<li><p><a href="https://sdy-study.tistory.com/44">https://sdy-study.tistory.com/44</a>
(Cookie, Session, Token 이란?)</p>
</li>
<li><p><a href="https://interconnection.tistory.com/74">https://interconnection.tistory.com/74</a>
(쿠키와 세션 개념)</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[DNS(Domain Name System)]]></title>
            <link>https://velog.io/@moon-july5/DNSDomain-Name-System</link>
            <guid>https://velog.io/@moon-july5/DNSDomain-Name-System</guid>
            <pubDate>Mon, 10 Apr 2023 01:02:47 GMT</pubDate>
            <description><![CDATA[<p>먼저, 네트워크 프로토콜은 크게 두 가지로 나눌 수 있습니다.</p>
<ul>
<li><p><strong>데이터 프로토콜</strong> : 실제로 데이터를 실어나르는 프로토콜</p>
</li>
<li><p><strong>컨트롤 프로토콜</strong> : 이 데이터 프로토콜이 잘 동작하도록 도와주는 컨트롤 프로토콜</p>
</li>
</ul>
<p>컨트롤 프로토콜은 통신에 직접 관여하지 않지만 처음 통신 관계를 맺거나 유지하는 데 큰 역할을 합니다.</p>
<p>TCP/IP 프로토콜 체계를 유지하기 위한 주요 컨트롤 프로토콜에는 <code>ARP</code>, <code>ICMP</code>, <code>DNS</code>가 있습니다.</p>
<p>이 중에서 <code>DNS(Domain Name System)</code>는 <strong>도메인 주소를 IP 주소로 변환하는 역할</strong>을 합니다.</p>
<p>IP 주소보다 도메인 주소를 이용하는 것이 일반 사용자에게 더 익숙하고 서버 IP 변경에 쉽게 대처할 수 있습니다.</p>
<h2 id="📌-dns">📌 DNS</h2>
<p>우리는 흔히 사이트에 접속할 때, <code>www.google.com</code> 같은 도메인 주소를 입력하게 됩니다. 하지만 실제 네트워크 통신에서는 숫자로 구성된 IP 주소를 이용합니다. </p>
<p>사용자는 이 숫자로 구성된 IP 주소를 모두 외우기는 어렵고, <strong>IP 주소보다 의미 있는 문자열로 구성된 도메인 주소가 우리가 인식하고 기억하기 더 쉽습니다.</strong></p>
<p>도메인 주소를 이용하면 하나의 IP 주소를 이용해 여러 개의 웹 서비스를 운영할 수 있고, 서비스 중인 IP 주소가 변경되더라도 도메인 주소 그대로 유지해 접속 방법 변경 없이 서비스를 그대로 유지할 수 있습니다. 또한, 도메인을 이용하면 지리적으로 여러 위치에서 서비스할 수도 있습니다. </p>
<p>물론 서비스를 도메인 주소를 사용하더라도 실제로 패킷을 만들어 통신하려면 네트워크 계층의 IP 주소를 알아야 하고 이를 위해 <strong>문자열로 된 도메인 주소를 실제 통신에 필요한 IP 주소로 변환</strong>하는 <code>DNS(Domain Name Server)</code> 정보를 네트워크 설정 정보에 입력해야 합니다.</p>
<p>아래의 이미지는 <code>www.naver.com</code>에 접속하기 위한 절차입니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/15b404d7-fa05-4a47-a79d-5a76fac3e354/image.png" alt="dns-1"> 사용자가 웹 브라우저에 naver.com을 입력하면 DNS 서버에 <code>naver.com</code>의 주소가 무엇인지 질의하고 DNS 서버는 <code>naver.com</code>의 IP 주소가 <code>202.179.177.21</code>이라고 사용자에게 알려줍니다. 사용자는 DNS로 응답받은 <code>202.179.177.21</code>이라는 IP 주소를 이용해 실제 <code>naver.com</code>에 접속하게 됩니다.</p>
<h2 id="📌-dns-구조와-명명-규칙">📌 DNS 구조와 명명 규칙</h2>
<p>도메인은 계층 구조입니다.</p>
<p>역트리 구조로 최상위 루트부터 <code>Top-Level</code> 도메인, <code>Second-Level</code> 도메인, <code>Third-Level</code> 도메인과 같이 하위 레벨로</p>
<p>원하는 주소를 단계적으로 찾아갑니다. 우리가 도메인 주소를 사용할 때는 각 계층의 경계를 &quot;.&quot;으로 표시하고 뒤에서 앞으로 해석합니다.</p>
<p><code>www.naver.com</code> 의 경우, 맨 뒤에 생략된 루트(.)를 시작으로 <code>Top-Level</code>인 com, <code>Second-Level</code>인 naver, <code>Third-Level</code>인 www와 같이 뒤에서 앞으로 해석합니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/3d62ab44-d8fc-4588-95d3-a04148b6fa4c/image.png" alt="dns-2"> 도메인 계층은 최대 128계층까지 구성할 수 있습니다.</p>
<p>계층별 길이는 최대 63바이트까지 사용할 수 있고 도메인 계층을 구분하는 구분자 &quot;.&quot;을 포함한 전체 도메인 네임의 길이는 최대 255바이트까지 사용할 수 있습니다. 문자는 알파벳, 숫자, &quot;ㅡ&quot;만 사용할 수 있고 대소문자 구분이 없습니다.</p>
<h2 id="📌-dns-동작-방식">📌 DNS 동작 방식</h2>
<p>도메인을 IP 주소로 변환하려면 DNS 서버에 도메인 쿼리하는 과정을 거쳐야 합니다.</p>
<p>하지만 DNS 서버없이 로컬에 도메인과 IP 주소를 직접 설정해 사용할 수도 있습니다. <strong>로컬에서 도메인과 IP 주소를 관리하는 파일</strong>을 <code>hosts</code> 파일이라고 합니다. <code>hosts</code> 파일에 도메인과 IP 주소를 설정해두면 해당 도메인 리스트는 항상 DNS 캐시에 저장됩니다.</p>
<ul>
<li><p>도메인을 쿼리하면 DNS 서버에 쿼리를 하기 전 로컬에 있는 DNS 캐시 정보를 먼저 확인합니다.</p>
</li>
<li><blockquote>
<p>동일한 도메인을 매번 질의하지 않고 캐시를 통해 성능을 향상시키기 위해서입니다. </p>
</blockquote>
</li>
<li><p>이런 DNS 캐시 정보에는 기존 DNS 조회를 통해 확인한 동적 DNS 캐시와 함께 <code>hosts</code> 파일에 저장되어 있는 정적 DNS 캐시가 함께 저장되어 있습니다. </p>
</li>
<li><p>DNS 캐시 정보에 필요한 도메인 정보가 없으면 DNS 서버로 쿼리를 수행하고 DNS 서버로부터 응답을 받으면 그 결과를 캐시에 먼저 저장합니다. 전에 쿼리를 한 번 수행한 DNS 정보는 캐시부터 조회하므로 DNS 서버에 별도로 쿼리하지 않고 캐시 정보를 사용합니다.</p>
</li>
<li><p>간단하게 <code>naver.com</code>이라는 도메인을 쿼리하기 위해 먼저 로컬 캐시를 조회하고 로컬 캐시에 없으면 DNS 서버로 다시 쿼리해 도메인 쿼리를 수행하는 과정을 거친다는 의미입니다.</p>
</li>
</ul>
<p>지금까지 클라이언트 관점에서 DNS 질의 과정을 설명했다면 반대로 DNS 시스템 관점에서 도메인에 대한 결과값을 클라이언트에 보내주는 과정을 살펴보겠습니다.</p>
<p>전 세계 도메인 정보를 DNS 서버 하나에 저장할 수 없습니다. 왜냐하면 데이터의 양이 너무 방대하기 때문에 실시간으로 도메인 리스트를 업데이트할 수 없기 때문입니다.</p>
<p>그래서 DNS 서버는 분산된 데이터베이스로 서로 도와주도록 설계되었는데 자신이 가진 도메인 정보가 아니면 다른 DNS에 질의해 결과를 받을 수 있습니다.</p>
<ul>
<li>DNS 기능을 서버에 올리면 DNS 서버는 기본적으로 루트 DNS 관련 정보를 가지고 있습니다. 클라이언트의 쿼리가 자신에게 없는 정보라면 <code>루트 DNS</code>에 쿼리하고 루트 DNS에서는 쿼리한 도메인의 <code>TLD</code> 값을 확인해 해당 <code>TLD</code>값을 관리하는 DNS가 어디인지 응답합니다.</li>
</ul>
<ul>
<li><p>예를 들어, &#39;naver.com&#39; 이라는 도메인을 클라이언트가 DNS 서버에 쿼리했다면 DNS 서버는 루트 DNS에 다시 쿼리하고 루트 DNS는 <code>.com</code>에 대한 정보를 관리하는 DNS 주소 정보를 DNS 서버에 응답합니다.</p>
</li>
<li><p>이 응답을 받은 DNS 서버는 <code>.com</code>을 관리하는 DNS 서버에 <code>naver.com</code>에 대해 쿼리합니다. </p>
</li>
<li><p>DNS 서버는 마지막으로 <code>naver.com</code>을 관리하는 DNS에 쿼리하고 <code>naver.com</code>에 대한 최종 결괏값을 받게 됩니다.</p>
</li>
</ul>
<p>처음 쿼리를 받은(클라이언트에 DNS 서버로 설정된) DNS 서버는 이 정보를 클라이언트에 응답합니다.</p>
<p>이 과정을 보면 <strong>클라이언트에서 처음 질의를 받은 DNS가 중심이 되어 책임지고 루트 DNS부터 상위 DNS에 차근차근 쿼리를 보내 결괏값을 알아낸 후 최종 결괏값만 클라이언트에 응답합니다.</strong></p>
<p>클라이언트는 한 번의 쿼리를 보내지만 이 요청을 받은 DNS 서버는 여러 단계로 쿼리를 상위 DNS 서버에 보내 정보를 획득합니다. </p>
<p>호스트가 DNS 서버에 질의했던 방식을 <code>재귀적 쿼리(Recursive Query)</code>라고 하고 DNS 서버가 루트 NS와 TLS NS에 질의한 방식을 <code>반복적 쿼리(Iterative Query)</code>라고 합니다.</p>
<h3 id="💡-재귀적-쿼리recursive-query와-반복적-쿼리iterative-query">💡 재귀적 쿼리(Recursive Query)와 반복적 쿼리(Iterative Query)</h3>
<ul>
<li><p><code>재귀적 쿼리</code>는 쿼리를 보낸 클라이언트에 서버가 최종 결괏값을 반환하는 서버 중심 쿼리입니다.</p>
</li>
<li><p><code>반복적 쿼리</code>는 최종값을 받을 때까지 클라이언트에서 쿼리를 계속 진행하는 방식입니다.</p>
</li>
</ul>
<p>일반적으로 재귀적 쿼리는 클라이언트와 로컬 DNS 간에서 사용하고 반복적 쿼리는 로컬 DNS 서버와 상위 DNS 구간에서 사용합니다. 이때 로컬 DNS는 클라이언트로 동작해 상위 DNS에 반복적으로 쿼리합니다. </p>
<p>이상으로 DNS에 대해서 간단히 알아봤습니다.</p>
<h3 id="참고">참고</h3>
<ul>
<li>IT 엔지니어를 위한 네트워크 입문 (고재성, 이상훈 지음)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[NAT(Network Address Translation)]]></title>
            <link>https://velog.io/@moon-july5/NATNetwork-Address-Translation</link>
            <guid>https://velog.io/@moon-july5/NATNetwork-Address-Translation</guid>
            <pubDate>Sat, 08 Apr 2023 08:33:36 GMT</pubDate>
            <description><![CDATA[<p><code>NAT</code>는 이름 그대로 <strong>네트워크 주소를 변환하는 기술</strong>입니다.</p>
<p><code>NAT</code>는 기본적으로 하나의 네트워크 주소에 다른 하나의 네트워크 주소로 변환하는 1:1 변환이 기본이지만 IP 주소가 고갈되는 문제를 해결하기 위해 1:1 변환이 아닌 여러 개의 IP를 하나의 IP로 변환하기도 합니다. </p>
<p>여러 개의 IP를 하나의 IP로 변환하는 기술도 NAT 기술 중 하나이고 NAT로 통칭하여 불리기도 하지만 실제 공식 용어는 <code>NAPT(Network Address Port Translation)</code>입니다.</p>
<p>실무에서는 <code>PAT(Port Address Translation)</code>라는 용어로 더 많이 사용됩니다.</p>
<p><code>NAT</code>가 가장 많이 사용되는 경우는 <strong>사설 IP 주소에서 공인 IP 주소로 전환하는 경우</strong>입니다.</p>
<h3 id="💡-공인-ip와-사설-ip">💡 공인 IP와 사설 IP?</h3>
<p>인터넷에 접속하려면 IP 주소가 있어야 하고 이 IP는 전 세계에서 유일해야 하는 식별자입니다.</p>
<p>이런 IP 주소를 <code>공인 IP</code>라고 하며, 인터넷에 연결하지 않고 개인적으로 네트워크를 구성한다면 <code>공인 IP</code>를 할당받지 않고 네트워크를 구축할 수 있습니다.</p>
<p>이때 사용하는 IP 주소를 <code>사설 IP</code> 주소라고 합니다.</p>
<h2 id="📌-nat의-용도와-필요성">📌 NAT의 용도와 필요성</h2>
<ol>
<li><strong>IPv4 주소 고갈 문제의 솔루션</strong>으로 <code>NAT</code>가 사용됩니다.</li>
</ol>
<p>-&gt; 외부에 공개해야 하는 서비스에 대해서는 공인 IP를 사용하고 외부에 공개할 필요가 없는 일반 사용자의 PC나 기타 종단 장비에 대해서는 사설 IP를 사용해 꼭 필요한 곳에만 효율적으로 IP를 사용할 수 있게 되었습니다.</p>
<ol start="2">
<li>보안을 강화하는 데 NAT 기술을 사용합니다.</li>
</ol>
<p>-&gt; 외부와 통신할 때 내부 IP를 다른 IP로 변환해 통신하면 외부에 사내 IP 주소 체계를 숨길 수 있습니다.</p>
<ol start="3">
<li><p>IP 주소 체계가 같은 두 개의 네트워크 간 통신을 가능하게 해줍니다.</p>
</li>
<li><p>불필요한 설정 변경을 줄일 수 있습니다.</p>
</li>
</ol>
<h2 id="📌-snat와-dnat">📌 SNAT와 DNAT</h2>
<p>NAT를 이용해 네트워크 주소를 변환할 때 어떤 IP 주소를 변환하는지에 따라 두 가지로 구분합니다.</p>
<ul>
<li>SNAT(Soruce NAT) : <code>출발지 주소</code>를 변경하는 NAT</li>
<li>DNAT(Destination NAT) : <code>도착지 주소</code>를 변경하는 NAT</li>
</ul>
<p><code>SNAT</code>는 <strong>사설에서 공인으로 통신할 때 많이 사용합니다.</strong></p>
<p>공인 IP 주소의 목적지에서 출발지로 다시 응답을 받으려면 출발지 IP 주소 경로가 필요한데 공인 대역에서는 사설 대역으로의 경로를 알 수 없으므로 공인 IP의 목적지로 서비스를 요청할 때 출발지에서는 사설 IP를 별도의 공인 IP로 NAT하여 서비스를 요청해야 합니다.</p>
<p>다른 경우는 보안상 <code>SNAT</code>를 사용할 때입니다. </p>
<p>회사에서 다른 대외사와 통신 시 내부 IP 주소가 아니라 별도의 다른 IP로 전환해 전송함으로써 대외에 내부의 실제 IP 주소를 숨길 수 있습니다.</p>
<p><code>DNAT</code>는 <strong>로드 밸런서에서 많이 사용합니다.</strong></p>
<p>사용자는 서비스 요청을 위해 로드 밸런서에 설정된 서비스 VIP(Virtual IP)로 서비스를 요청하고 로드 밸런서에서는 서비스 VIP를 로드 밸런싱될 서버의 실제 IP로 <code>DNAT</code>해 내보냅니다. </p>
<p>사내가 아닌 대외망과의 네트워크 구성에도 <code>DNAT</code>를 사용합니다.</p>
<p>사내 IP 주소는 중앙에서 일괄적으로 관리되므로 IP가 중복되는 경우가 없지만 사내가 아닌 대외망과의 연동에서는 IP가 중복될 수 있습니다. 이 경우, 대외망에 NAT 장비를 이용해 대외사의 IP를 특정 IP 대역으로 NAT합니다.</p>
<h2 id="📌-동적-nat와-정적-nat">📌 동적 NAT와 정적 NAT</h2>
<p><strong>출발지와 목적지의 IP를 미리 매핑해 고정해높은 NAT</strong>를 <code>정적 NAT</code>라고 합니다.</p>
<p>반대로 <strong>출발지나 목적지 어느 경우든 사전에 정해지지 않고 NAT를 수행할 때 IP를 동적으로 변경하는 것</strong>을 <code>동적 NAT</code>라고 합니다.</p>
<p><code>동적 NAT</code>는 출발지와 목적지가 모두 정의된 것이 아니라 다수의 IP 풀에서 정해지므로 최소한 출발지나 목적지 중 한 곳이 다수의 IP로 구성된 IP 풀이나 레인지(Range)로 설정되어 있습니다. </p>
<p>NAT가 필요할 때 IP 풀에서 어떤 IP로 매핑될 것인지 판단해 NAT를 수행하는 시점에 NAT 테이블을 만들어 관리합니다.</p>
<p><code>정적 NAT</code>는 출발지와 목적지 매핑 관계가 특정 IP로 사전에 정의된 것이므로 1:1 NAT라고 부릅니다.</p>
<table>
<thead>
<tr>
<th align="center"></th>
<th align="left">동적 NAT</th>
<th align="left">정적 NAT</th>
</tr>
</thead>
<tbody><tr>
<td align="center">NAT 설정</td>
<td align="left">1:N, N:1, N:M</td>
<td align="left">1:1</td>
</tr>
<tr>
<td align="center">NAT 테이블</td>
<td align="left">NAT 수행 시 생성</td>
<td align="left">사전 생성</td>
</tr>
<tr>
<td align="center">NAT 테이블 타임아웃</td>
<td align="left">동작</td>
<td align="left">없음</td>
</tr>
<tr>
<td align="center">NAT 수행 정보</td>
<td align="left">실시간으로만 확인하거나 별도 변경 로그 저장 필요</td>
<td align="left">별도 필요 없음 (설정 = NAT 내역)</td>
</tr>
</tbody></table>
<p>이상으로 NAT에 대해서 간단히 알아봤습니다.</p>
<h3 id="참고">참고</h3>
<ul>
<li>IT 엔지니어를 위한 네트워크 입문 (고재성, 이상훈 지음)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[UDP]]></title>
            <link>https://velog.io/@moon-july5/UDP</link>
            <guid>https://velog.io/@moon-july5/UDP</guid>
            <pubDate>Tue, 04 Apr 2023 15:04:01 GMT</pubDate>
            <description><![CDATA[<p>TCP/IP 프로토콜 스택에서 전송 계층(Transport)은 <code>TCP</code>와 <code>UDP</code>가 담당합니다.</p>
<p><a href="https://velog.io/@moon-july5/TCP">TCP</a>에 대해서는 바로 전에 설명했습니다.</p>
<p>다시 설명하자면 전송 계층의 목적은 <strong>목적지를 찾아가는 주소가 아니라 애플리케이션에서 사용하는 프로세스를 정확히 찾아가고 데이터를 분할한 패킷을 잘 쪼개 보내고 잘 조립하는 것</strong>입니다.</p>
<p>패킷을 분할하고 조합하기 위해 TCP 프로토콜에서는 <code>시퀀스 번호</code>화 <code>ACK 번호</code>를 사용합니다.</p>
<p>바로 아래의 이미지는 TCP 세그먼트 헤더 포맷입니다.
<img src="https://velog.velcdn.com/images/moon-july5/post/9bc839d4-e326-47f2-82b4-8ef1cf346989/image.png" alt="tcp"></p>
<p>이제 <code>UDP</code>에 대해서 설명하겠습니다.</p>
<p><code>UDP</code>는 <code>TCP</code>와 달리 전송 계층 프로토콜이 가져야 할 특징이 거의 없습니다.</p>
<p><code>UDP</code>에는 신뢰성 있는 통신을 위해 3방향 핸드셰이크로 연결을 미리 확립하고 데이터 분할, 조립하기 위해 패킷 번호를 부여하고 수신된 데이터에 응답하는 작업, 데이터를 특정 단위(윈도 사이즈)로 보내고 또한 유실 발생 시 시퀀스 번호와 ACK 번호를 비교하여 파악하여 재전송하는 기능 등 <strong>이러한 특징이 없습니다.</strong></p>
<p>즉, 여태 설명 했던 특징들은 <code>TCP</code>만 해당되며 <code>UDP</code>에는 이런 기능이 전혀 없습니다.</p>
<p>데이터 통신은 데이터 전송의 <strong>신뢰성</strong>이 핵심입니다.</p>
<p>하지만 <code>UDP</code>는 데이터 전송을 보장하지 않는 프로토콜이므로 제한된 용도로만 사용됩니다.</p>
<p><code>UDP</code>는 <strong>음성 데이터나 실시간 스트리밍과 같이 시간에 민감한 프로토콜</strong>이나 <strong>애플리케이션을 사용하는 경우</strong>나 <strong>사내 방송</strong>, <strong>증권 시세 데이터 전송에 사용되는 멀티캐스트처럼 단방향으로 다수의 단말과 통신해 응답을 받기 어려운 환경</strong>에서 주로 사용됩니다.</p>
<p>음성, 동영상은 디지털 환경에서는 연속된 데이터가 아닌 매우 짧은 시간 단위로 잘게 분할한 데이터가 전송되는 형태입니다. 동영상은 1초 동안 30 ~ 60회의 정지 영상이 빠르게 바뀌면서 우리 눈에는 마치 움직이는 영상처럼 보이는 원리를 이용합니다. 중간에 데이터가 몇 개 유실되는 것보다 재전송하기 위해 잠시 동영상이나 음성이 멈추는 것을 더 이상하게 느낄 수 있습니다.</p>
<p>이렇게 데이터를 전송하는 데 신뢰성보다 <strong>일부 데이터가 유실되더라도 시간에 맞추어 계속 전송하는 것이 중요한 화상회의 시스템과 같은 서비스</strong>인 경우, <code>UDP</code>를 사용하게 됩니다. </p>
<p><code>UDP</code>는 중간에 데이터가 일부 유실되더라도 그냥 유실된 상태로 데이터를 처리해버립니다.</p>
<p><code>UDP</code>는 <code>TCP</code>와 달리 통신 시작 전, 3방향 핸드셰이크와 같이 사전에 연결을 확립하는 절차가 없습니다.</p>
<p>그 대신 <code>UDP</code>에서 첫 데이터는 리소스 확보를 위해 <strong>인터럽트(Interrupt)</strong>를 거는 용도로 사용되고 유실됩니다.</p>
<p>그래서 <code>UDP</code> 프로토콜을 사용하는 애플리케이션이 대부분 이런 상항을 인지하고 동작하거나 연결 확립은 <code>TCP</code> 프로토콜을 사용하고 애플리케이션끼리 모든 준비를 마친 후 실제 데이터만 <code>UDP</code>를 이용하는 경우가 대부분입니다.</p>
<table>
<thead>
<tr>
<th align="left">TCP</th>
<th align="left">UDP</th>
</tr>
</thead>
<tbody><tr>
<td align="left">연결 지향(Connection Oriented)</td>
<td align="left">비연결형(Connectionless)</td>
</tr>
<tr>
<td align="left">오류 제어 수행함</td>
<td align="left">오류 제어 수행 안 함</td>
</tr>
<tr>
<td align="left">흐름 제어 수행함</td>
<td align="left">흐름 제어 수행 안 함</td>
</tr>
<tr>
<td align="left">유니캐스트</td>
<td align="left">유니캐스트, 멀티캐스트, 브로드캐스트</td>
</tr>
<tr>
<td align="left">전이중(Full Duplex)</td>
<td align="left">반이중(Hlf Duplex)</td>
</tr>
<tr>
<td align="left">데이터 전송</td>
<td align="left">실시간 트래픽 전송</td>
</tr>
</tbody></table>
<p>이상으로 UDP에 대해서 간단히 알아봤습니다.</p>
<h3 id="참고">참고</h3>
<ul>
<li>IT 엔지니어를 위한 네트워크 입문 (고재성, 이상훈 지음)</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>