<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>DelightWorld.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Tue, 07 Apr 2026 15:46:25 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>DelightWorld.log</title>
            <url>https://velog.velcdn.com/images/talking_tomato/profile/1499123a-fb0d-438b-94e8-e4d0dc952080/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. DelightWorld.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/talking_tomato" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[AWS와 화해하기#1 - 네트워크와 인스턴스]]></title>
            <link>https://velog.io/@talking_tomato/AWS%EC%99%80-%ED%99%94%ED%95%B4%ED%95%98%EA%B8%B0-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC%EC%99%80-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4</link>
            <guid>https://velog.io/@talking_tomato/AWS%EC%99%80-%ED%99%94%ED%95%B4%ED%95%98%EA%B8%B0-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC%EC%99%80-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4</guid>
            <pubDate>Tue, 07 Apr 2026 15:46:25 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가며">들어가며</h2>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/a5469d81-d754-4670-a7e1-09099e59d332/image.png" alt=""></p>
<p>오늘의 주제는 바로 <strong>AWS</strong>이다.  </p>
<p>예전에도 AWS를 공부해보고 싶어 이것 저것 시도해보았지만 악랄한 아마존의 요금 폭탄과 EC2 생성에서 마주치는 알아들을 수 없는 설정값 때문에 잠시 거리를 두기로 했었다. 
<del>(저 로고의 웃는 모습이 약오른다.)</del></p>
<p>그러던 중 최근 비용 걱정 없이 AWS를 사용해 볼 기회가 생기게 되었다.
이 포스팅에서는 내가 AWS 인프라를 E2E로 구축하며 공부하고 배운 것을 누군가에게 나누어 보려고 한다.</p>
<p>AWS가 뭔지부터 설명하지는 않을 것이다. </p>
<p>제목에서도 나타나듯이 EC2 생성하다가 머리아파서 잠시 AWS와 멀어진 사람들을 위한 포스팅이니 기본적으로 AWS와 EC2가 뭔지는 알고 오면 좋을 것 같다.</p>
<p>공부를 시작한지 1주일 밖에 지나지 않아 부족한 점도 많겠지만 최대한 내가 이해한 대로 설명해보도록 하겠다.</p>
<h2 id="네트워크와-인스턴스">네트워크와 인스턴스</h2>
<hr>
<p>내가 처음 AWS를 접했을 때 가장 어렵고 복잡했던 것은 무수히 많은 서비스들과 설정 지옥이었다.</p>
<p>간단한 EC2 하나 띄우는 데에도 설정해줘야 하는 것들이 뭐 그리 많은지.. 
그리고 참고한 블로그 글들과 현재의 GUI 버전이 달라 너무 헷갈렸다. </p>
<p>이 포스팅에서는 <font color="green">단순히 EC2를 생성하는 것이 아닌 전체 인프라를 이해하고 구축하는 것</font>을 목표로 하기 때문에 네트워크부터 먼저 설명해보고자 한다.</p>
<h3 id="1-vpc">1. VPC</h3>
<p>가장 먼저 알아야 하는 키워드는 <strong>Virtual Private Cloud (VPC)</strong>이다.</p>
<p>VPC에 대한 설명을 먼저 하는 이유는 이 서비스가 가장 <strong>바깥쪽의 그릇 역할</strong>이기 때문이다.
큰 개념부터 시작하여 점점 세부적으로 들어가보자.</p>
<p><strong>VPC</strong>는 <font color="green">퍼블릭 클라우드 내에 존재하는 논리적으로 격리된 프라이빗 네트워크</font>이다.
쉽게 말해 AWS에서 제공해주는 <strong>개인 가상 내부망</strong>이라고 이해하면 된다.</p>
<p>기본적으로 사용자가 생성하는 리소스는 대부분 VPC 안에 들어가게 된다.
물론 예외는 있긴 하지만 그건 뒤에서 설명하도록 하겠다.</p>
<p>만약 당장 이해가 가지 않는다면 EC2 같은 리소스를 담을 <strong>가장 바깥쪽의 보따리</strong>라고 생각하고 넘어가자.</p>
<p><strong>Region</strong>과 <strong>Availability Zone</strong> 개념에 대해서도 연관되어 있지만 이 글에서는 논리적인 개념을 이해하는 것을 목표로 하기에 따로 설명하지 않겠다. 만약 관심이 있으면 개인적으로 찾아보길 바란다.</p>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/ad6dbf54-61b4-4e85-8dfb-52abe338851e/image.png" alt=""></p>
<p>위 사진은 AWS Docs에서 VPC를 설명할 때 나오는 그림이다.
VPC 안에 Subnet들과 Instances들이 들어가 있는 것을 볼 수 있다.</p>
<h3 id="2-internet-gateway-igw">2. Internet Gateway (IGW)</h3>
<p>아까 VPC는 프라이빗 네트워크라고 이야기했었다. 
이 말은 기본적으로 외부 인터넷과 격리된 상태라는 것이다.</p>
<p>이 VPC와 그 안의 EC2들이 인터넷과 연결되기 위해서는 <strong>Internet Gateway (IGW)</strong>가 필요하다.
IGW는 가장 쉽게 말해 <strong>구멍을 뚫어주는 것</strong>이다.</p>
<p>아까 VPC를 보따리에 비교한 것을 기억하는가? </p>
<p>보따리 안(VPC)에서 바깥 세상(인터넷)과 상호작용을 하려면 입구를 열어야 하는 것과 같이 VPC도 인터넷에 접근하려면 입구가 필요하다.</p>
<p>그러나 여기서 주의할 점은 IGW가 생긴다고 해서 자동으로 인터넷으로 연결이 되는 것은 아니다.
실제로 인터넷에 연결하기 위해서는 몇 가지 조건이 더 필요하다. 이건 밑에서 설명하겠다.
지금은 그냥 입구 혹은 구멍 정도로 이해하면 된다.</p>
<p>IGW는 VPC에 붙이는 것이다. 당연한 이야기다! 구멍은 보따리에 뚫는 것이니까.</p>
<blockquote>
<h4 id="여기까지의-결론">여기까지의 결론</h4>
<p>AWS 인프라를 생성하려면 먼저 <strong>VPC</strong>부터 만들면 된다. 
그리고 인터넷 통신이 필요하다면 그 <strong>VPC</strong>에 <strong>IGW</strong>를 만들어서 붙여라!</p>
</blockquote>
<h3 id="3-subnet">3. Subnet</h3>
<p>VPC를 생성하고 IGW를 붙였다면 이제 Subnet을 만들 차례다.
서브넷은 <font color="green">VPC의 IP 주소 범위를 더 작은 단위로 분할한 논리적 네트워크 세그먼트</font>이다.</p>
<p>쉽게 말해 VPC라는 <strong>보따리 안의 짐들을 비닐봉지로 서로 묶는 것</strong>이다.</p>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/cc162d26-c871-4359-b4fa-e3da4f67138b/image.png" alt=""></p>
<p>사실 서브넷이란 개념은 네트워크에서 중요하고 유명한 개념이다. 
네트워크를 공부했던 사람이라면 익숙한 서브넷 마스크부터 시작하여 CIDR 개념까지 모두 다루려면 끝이 없기에 이건 네트워크 시간에 따로 공부하기로 하고 지금은 AWS의 네트워크 구조에서의 서브넷을 다뤄보자.</p>
<p>서브넷을 생성하는 이유에는 몇 가지가 있지만 가장 큰 목적은 <strong>네트워크 분리</strong>이다.
외부(인터넷)에서 접근할 수 있는 네트워크와 외부에서 접근할 수 없는 네트워크를 분리하거나 역할별로 네트워크를 분리할 수 있다. 또한 위에서 넘어갔던 AZ 분리도 중요한 목적 중 하나다.</p>
<p>서브넷은 크게 2가지로 나눌 수 있다. 바로 <strong>Public Subnet</strong>과 <strong>Private Subnet</strong>이다.
정확한 정의로는 Public Subnet은 <font color="green">IGW가 존재하는 VPC상에 있고 라우팅 테이블에 <code>0.0.0.0/0 -&gt; IGW</code> 규칙이 있는 서브넷</font>이고 Private Subnet은 <font color="green">그 규칙이 없는 서브넷</font>이다.</p>
<p>아까 위에서 IGW를 생성한다고 자동으로 인터넷에 연결되는 것이 아니라고 말한것을 기억하는가?
인터넷에 연결되기 위해서는 IGW가 존재해야 하고 Public Subnet 즉, 라우팅 테이블에 <code>0.0.0.0/0 -&gt; IGW</code> 규칙이 있어야 한다. 그러나 아직 인터넷에 연결되기 위해서는 몇 가지 조건이 더 필요하다. 이건 밑에서 차차 설명하도록 하겠다.</p>
<blockquote>
<h4 id="여기까지의-결론-1">여기까지의 결론</h4>
<p><strong>VPC</strong> 내부에는 <strong>Subnet</strong>을 설정할 수 있다!
Subnet에는 <strong>Route Table</strong>을 연결할 수 있다!
Route Table의 <code>0.0.0.0/0 -&gt; IGW</code> 규칙 유무에 따라 <strong>Public/Private 서브넷</strong>이 나눠진다.</p>
</blockquote>
<h3 id="4-ec2와-public-ip">4. EC2와 Public IP</h3>
<p>외부에서 접근 가능한 인스턴스를 만들기 위해서는 그 인스턴스를 식별할 수 있는 Public IP가 필요하다.</p>
<p>어떤 인스턴스가 Public IP를 가지는 방법은 2가지가 있다.</p>
<p>첫 번째는 <strong>EC2 생성 시 설정을 통해 부여하는 것</strong>이다.
두 번째는 <strong>Elastic IP(EIP)를 연결하는 것</strong>이다.</p>
<p>여기서 Elastic IP (EIP)는 고정된 공인 IP라고 보면 된다.
기본 Public IP는 인스턴스를 중지/재시작하면 변경될 수 있지만, EIP는 계속 유지된다.</p>
<p>그렇다면 이제 EC2를 인터넷에 연결시키기 위한 준비가 모두 끝났다.</p>
<ol>
<li>VPC에 IGW가 연결되어 있고</li>
<li>Subnet에 <code>0.0.0.0/0 -&gt; IGW</code> 규칙이 있고 (=Public Subnet이고)</li>
<li>EC2에 Public IP가 존재한다.</li>
</ol>
<p><font color="gray">(보안 설정도 필요하지만 여기서는 제외하겠다. 보안 설정은 추후 설명하겠다.)</font></p>
<p>위 3개의 조건이 만족한다면 이제 외부에서 접근 가능한 인스턴스를 생성한 것이다.</p>
<blockquote>
<h4 id="여기까지의-결론-2">여기까지의 결론</h4>
<p>인스턴스에는 Public IP를 붙일 수 있다!
EC2 생성 시 설정하거나 EIP를 연결하여 붙이는 방법이 있다!</p>
</blockquote>
<h3 id="5-nat-gateway">5. NAT Gateway</h3>
<p>이제 Public Subnet에 있는 EC2는 인터넷을 사용할 수 있게 되었다.
그렇다면 Private Subnet은 인터넷을 사용하지 못하는 것일까?
Private Subnet에서도 Docker Hub에서 이미지를 pull하거나 apt install로 패키지를 설치하는 경우가 있을 수 있다.</p>
<p>이때 사용하는 것이 <strong>NAT Gateway</strong>이다.
NAT Gateway는 Private IP를 Public IP로 변환해서 외부와 통신하게 해주는 장치이다.
이를 이용하면 외부에서 내부로의 접근은 불가능하지만 내부에서 외부로 요청하고 응답을 받는 것은 가능한 시스템을 만들 수 있다.</p>
<p>즉, 아웃바운드만 가능한 구조라고 이해하면 된다.</p>
<p>Private Subnet에 있는 EC2가 외부로 요청을 보낼 때, NAT Gateway를 통해 트래픽이 나가게 된다.</p>
<p>NAT Gateway는 <strong>Public Subnet에 생성</strong>되어야 하며, <strong>Elastic IP</strong>를 가진다.</p>
<p>그리고 <strong>Private Subnet의 Route Table에는 <code>0.0.0.0/0 → NAT Gateway</code> 규칙이 필요</strong>하다.</p>
<p>이렇게 설정하면 Private Subnet에 있는 인스턴스도 NAT Gateway를 통해 외부로 나갈 수 있게 된다.</p>
<blockquote>
<h4 id="여기까지의-결론-3">여기까지의 결론</h4>
<p>Private Subnet에서 인터넷을 사용하려면 NAT Gateway를 사용하면 된다!
NAT Gateway는 Public Subnet에 생성해야 한다!
인터넷을 사용하려는 Subnet의 Route Table에는 <code>0.0.0.0/0 → NAT Gateway</code> 규칙이 필요하다.</p>
</blockquote>
<h2 id="정리하며">정리하며</h2>
<p>지금까지 기본적인 네트워크 설정과 인스턴스 접근 방법을 설명했다.
만약 여기까지 잘 따라왔다면 아래의 개념들을 이해하고 있을 것이다.</p>
<ul>
<li>VPC의 개념과 IGW 개념</li>
<li>Public Subnet과 Private Subnet</li>
<li>Public IP 설정과 각 Subnet에서의 인터넷 트래픽 흐름</li>
</ul>
<p>이제 기본적인 트래픽의 흐름을 알았으니 다음 글에서는 흐름을 제어하는 보안 설정에 대해 알아보겠다.
원래 하나의 글로 작성하려 했지만 생각보다 내용이 많아져 분리하게 되었다.</p>
<p>다음 글에서는 <strong>보안 설정과 SSM, Route53, S3</strong> 등을 설명할 예정이다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[LIO-SAM 맨땅에 머리박기 (ROS1 noetic)]]></title>
            <link>https://velog.io/@talking_tomato/LIO-SAM-%EB%A7%A8%EB%95%85%EC%97%90-%EB%A8%B8%EB%A6%AC%EB%B0%95%EA%B8%B0-ROS1-noetic</link>
            <guid>https://velog.io/@talking_tomato/LIO-SAM-%EB%A7%A8%EB%95%85%EC%97%90-%EB%A8%B8%EB%A6%AC%EB%B0%95%EA%B8%B0-ROS1-noetic</guid>
            <pubDate>Thu, 08 May 2025 07:32:36 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가며">들어가며</h2>
<hr>
<p>프로젝트에서 LIO-SAM 기반의 3D SLAM 알고리즘을 사용해야 하는 일이 생겼다.</p>
<p>ROS를 거의 처음 다뤄보는 나로써는 상당히 막막한 일이었지만 개발자라면 자고로 머리부터 들이밀어야 한다는 마음으로 프로젝트를 시작해 보았다.</p>
<p>혹시 나처럼 고통받고 있는 누군가에게 이 글이 도움이 되길 바라며 LIO-SAM을 설치하고 데이터를 수집하는 과정에서 일어난 우여곡절을 함께 나눠보고자 한다.</p>
<h2 id="개발환경-구축하기">개발환경 구축하기</h2>
<hr>
<h3 id="1-linux-개발환경-구축-ubuntu-2004">1. Linux 개발환경 구축 (Ubuntu 20.04)</h3>
<p>이번 프로젝트에서는 ROS noetic 버전을 사용하여야 했기 때문에 Ubuntu 20.04 버전을 사용하기로 하였다.</p>
<p>Docker를 사용하여도 되지만 우리는 LG그램 노트북에 Ubuntu를 바로 올려서 사용하기로 하였다.</p>
<p>우분투를 설치하는 방법은 구글에 검색해 보면 많이 나오기에 따로 설명하지는 않겠다.</p>
<p>만약 나와 같은 환경에서 시작하고 싶은 사람은 Ubuntu 공식 홈페이지에서 20.04 버전 이미지를 다운받고 부팅 USB를 만들어 원하는 PC에 설치하면 된다.</p>
<p>부팅 USB는 잘 보관해 두자. 나중에 사용할 일이 있을지도 모른다..</p>
<h3 id="2-ros-설치-ros1-noetic">2. ROS 설치 (ROS1 noetic)</h3>
<p>상술한 것처럼 이번 프로젝트에서 ROS1 noetic 버전 사용이 필수적인 상황이었다.</p>
<p>돌아보면 이게 비극의 시작이었던 것 같지만 프로젝트에서 사용하는 모터 드라이버가 지원하는 버전이 noetic 뿐인지라 어쩔 수 없는 일이었다. (만약 Noetic 버전을 사용할 필요가 없는 사람이라면 LIO-SAM github에서 명시하는 테스트 된 버전으로 진행하는 것이 정신건강에 좋을 수 있다. 그렇다면 이 글도 읽을 필요가 없을 것이다..)</p>
<p>ROS는 <a href="https://wiki.ros.org/noetic/Installation/Ubuntu">공식문서</a>를 참고하여 설치하였다.</p>
<h4 id="1-패키지-저장소-등록">1. 패키지 저장소 등록</h4>
<pre><code class="language-bash">sudo sh -c &#39;echo &quot;deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main&quot; &gt; /etc/apt/sources.list.d/ros-latest.list&#39;</code></pre>
<p>여기서 주의할 점이 있다.</p>
<p>위에서 Ubuntu 이미지를 설치하는 방식으로 시작하였다면 <code>lsb_release</code> 패키지가 설치되어 있지 않아 오류가 날 것이다. 만약 위의 코드를 먼저 실행시켰고 오류가 났다면 축하한다. 이제 당신의 Ubunru에서는 어떤 <code>apt install</code> 명령어도 작동하지 않을 것이다.</p>
<p>아마 이를 복구하는 방법이 있을 것 같긴 한데 나는 그냥 우분투를 새로 까는 방법을 선택하였다. 아까 보관해 둔 부팅 USB를 다시 꺼내보자...</p>
<p>위 명령어를 무사히 실행시키려면 먼저 저 <code>lsb</code> 패키지를 설치하여야 한다. 참고로 <code>lsb_release</code>는 현재 우분투의 버전 이름을 확인하는 명령어이다.</p>
<pre><code class="language-bash">sudo apt-get install lsb</code></pre>
<p>이제 위 패키지를 다운로드하고 다시 패키지 저장소를 등록해 주자.</p>
<h4 id="2-키-셋업">2. 키 셋업</h4>
<pre><code class="language-bash">sudo apt install curl # if you haven&#39;t already installed curl
curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -</code></pre>
<h4 id="3-설치">3. 설치</h4>
<pre><code class="language-bash">sudo apt update</code></pre>
<pre><code class="language-bash">sudo apt install ros-noetic-desktop-full</code></pre>
<h4 id="4-환경변수-설정">4. 환경변수 설정</h4>
<p>설치가 이제 끝났다. 설치 이후에 환경변수를 추가해 주기 위해 </p>
<pre><code class="language-bash">source /opt/ros/noetic/setup.bash</code></pre>
<p>위 명령어를 실행시켜 주자. 이 명령어는 ROS 환경을 현재의 셀 세션에 등록해 주는 명령어이다.
매번 터미널을 열 때마다 실행해 줘야 하지만 상당히 귀찮기 때문에 <code>.bashrc</code> 파일에 등록하여 자동화하는 방법도 있다.</p>
<pre><code class="language-bash">echo &quot;source /opt/ros/noetic/setup.bash&quot; &gt;&gt; ~/.bashrc
source ~/.bashrc</code></pre>
<p>위 명령어를 입력하였다면 터미널을 열 때마다 자동으로 ROS 환경이 등록될 것이다.
혹여 여러 ROS 버전을 동시에 사용해야 한다면 위 자동화 명령어를 입력하면 안 된다.</p>
<h2 id="lio-sam-설치-및-빌드하기">LIO-SAM 설치 및 빌드하기</h2>
<hr>
<h3 id="1-작업공간-설정하기">1. 작업공간 설정하기</h3>
<p>LIO-SAM을 설치하기 전에 작업공간을 만들어 주자. 방법은 <a href="https://wiki.ros.org/catkin/Tutorials/create_a_workspace">공식문서</a>에도 잘 설명되어 있다.</p>
<pre><code class="language-bash">mkdir -p ~/catkin_ws/src/</code></pre>
<p>꼭 위 명령어로 작업공간을 만들 필요는 없다. 원하는 경로에 원하는 이름으로 설정하여도 된다. 다만 폴더 내에 src 폴더는 꼭 만들어주자. </p>
<pre><code class="language-bash">mkdir -p {원하는 경로와 폴더 이름}/src</code></pre>
<p>그리고 해당 폴더로 이동 후 기본 세팅을 해주자.</p>
<pre><code class="language-bash">cd ~/catkin_ws/
catkin_make</code></pre>
<p>여기서 <code>catkin_make</code> 명령어는 사실 빌드 명령어이다. 그래서 지금 꼭 실행시켜야 할 필요는 없지만 공식문서에서 하라고 하니 한번 실행해 보자. 빌드가 되면서 다양한 결과 폴더가 생기는 것을 확인할 수 있다. (코드가 없기 때문에 별 의미는 없다)</p>
<h3 id="2-lio-sam-설치-및-빌드하기">2. LIO-SAM 설치 및 빌드하기</h3>
<p>이제 LIO-SAM을 설치해 보자. 설치 방법은 <a href="https://github.com/TixiaoShan/LIO-SAM">github 레포지토리</a>에 잘 설명되어 있다.</p>
<h4 id="1-의존성-설치-ros-패키지">1. 의존성 설치 (ros 패키지)</h4>
<pre><code class="language-bash">sudo apt-get install -y ros-noetic-navigation
sudo apt-get install -y ros-noetic-robot-localization
sudo apt-get install -y ros-noetic-robot-state-publisher</code></pre>
<p>공식 문서에는 kinetic으로 설정되어 있기에 noetic으로 변경하여 설치해 준다.</p>
<h4 id="2-의존성-설치-gtsam">2. 의존성 설치 (gtsam)</h4>
<pre><code class="language-bash">sudo add-apt-repository ppa:borglab/gtsam-release-4.0
sudo apt install libgtsam-dev libgtsam-unstable-dev</code></pre>
<p>나의 경우 이 gtsam 패키지는 추후 빌드 시에 많은 문제를 일으켰었다. 당신이 운이 좋거나 깨끗한 우분투에서 설치를 진행하고 있다면 별일은 없을 것이다.</p>
<h4 id="3-코드-다운로드-및-빌드">3. 코드 다운로드 및 빌드</h4>
<p>모든 코드는 src 폴더 안에 있어야 하므로 src 폴더로 이동 후 <code>git clone</code>을 해준다.</p>
<pre><code class="language-bash">cd ~/catkin_ws/src
git clone https://github.com/TixiaoShan/LIO-SAM.git</code></pre>
<p>이후 다시 워크스페이스 폴더로 돌아가 빌드를 해준다.</p>
<pre><code class="language-bash">cd ..
catkin_make</code></pre>
<p>여기까지 잘 진행했다면 아마 빌드가 진행되는 듯하면서 에러가 발생했을 것이다.</p>
<p>아주 정상적인 현상이니 괜찮다. 에러는 뜨지 않는데 되는 게 아무것도 없는 경우보다는 낫지 않은가?</p>
<p>지금부터 하나하나 고쳐 나가보도록 하자.</p>
<h2 id="에러-해결">에러 해결</h2>
<hr>
<h3 id="1-opencv-에러">1. OpenCV 에러</h3>
<p>아마 첫 번째로 뜨는 에러는 opencv 에러일 것이다.</p>
<pre><code class="language-bash">In file included from /home/ubicomp/Desktop/woojin-main-ws/src/LIO-SAM/src/imuPreintegration.cpp:1:
/home/ubicomp/Desktop/woojin-main-ws/src/LIO-SAM/include/utility.h:18:10: fatal error: opencv/cv.h: No such file or directory
   18 | #include &lt;opencv/cv.h&gt;
      |          ^~~~~~~~~~~~~
compilation terminated.
make[2]: *** [LIO-SAM/CMakeFiles/lio_sam_imuPreintegration.dir/build.make:63: LIO-SAM/CMakeFiles/lio_sam_imuPreintegration.dir/src/imuPreintegration.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:812: LIO-SAM/CMakeFiles/lio_sam_imuPreintegration.dir/all] Error 2
make[1]: *** Waiting for unfinished jobs....</code></pre>
<p>이 에러는 LIO-SAM이 테스트 된 버전(Ununtu 18.04)에서는 OpenCV 3.x 버전을 사용하지만, 현재 버전(Ubuntu 20.04)에서는 OpenCV 4.x 버전을 사용하여 생긴 문제이다.</p>
<p>다행스럽게도 이 에러는 <a href="https://github.com/TixiaoShan/LIO-SAM/issues/206">이 이슈</a>에서 다루어졌기 때문에 우리는 그저 앞선 사람들을 따라가기만 하면 된다. (오픈소스 만세)</p>
<p>에러 해결을 위해서는 <code>LIO-SAM/include/utility.h</code> 파일을 수정해야 한다. <code>#include &lt;opencv/cv.h&gt;</code> 구문을 <code>#include &lt;opencv2/opencv.hpp&gt;</code>로 변경해 주게 되면 에러가 해결되는 것을 볼 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/7f082d3a-dac6-4f49-a238-ae29689f5cee/image.png" alt=""></p>
<h3 id="2-c14-에러">2. C++14 에러</h3>
<p>opencv를 고치고 신나게 <code>catkin_make</code> 명령어를 다시 입력해보면 말 그대로 에러가 뿜어져 나오는 것을 보게 될 것이다.</p>
<pre><code class="language-bash">In file included from /usr/include/pcl-1.10/pcl/pcl_macros.h:77,
                 from /usr/include/pcl-1.10/pcl/PCLHeader.h:10,
                 from /usr/include/pcl-1.10/pcl/point_cloud.h:47,
                 from /home/ubicomp/Desktop/woojin-main-ws/src/LIO-SAM/include/utility.h:20,
                 from /home/ubicomp/Desktop/woojin-main-ws/src/LIO-SAM/src/imageProjection.cpp:1:
/usr/include/pcl-1.10/pcl/pcl_config.h:7:4: error: #error PCL requires C++14 or above
    7 |   #error PCL requires C++14 or above
      |    ^~~~~</code></pre>
<p><code>PCL Requires C++14 or above</code> 로 시작하는 이 에러는 위의 에러와 동일하게 개발 환경 버전 차이로 발생하는 것이다. </p>
<p>다시 말해 <code>CMakeLists.txt</code>에 설정된 C++ 표준이 과거 PCL 버전으로 맞춰져 있어 오류가 발생한 것이다.</p>
<p>에러 해결을 위해서는 <code>LIO-SAM/CMakeLists.txt</code>에서 <code>set(CMAKE_CXX_FLAGS &quot;-std=c++11&quot;)</code> 구문을 <code>set(CMAKE_CXX_FLAGS &quot;-std=c++14&quot;)</code> 로 변경해 주면 된다.
<img src="https://velog.velcdn.com/images/talking_tomato/post/42c5388a-d0ff-4d23-aa24-340d294c5d74/image.png" alt=""></p>
<h3 id="3-no-member-named-serialize-에러">3. no member named ‘serialize’ 에러</h3>
<p>이 에러는 어떨 때는 발생하지 않고 어떨 때는 발생하는 이상한 에러였다.</p>
<pre><code class="language-bash">/usr/include/flann/util/serialization.h:34:14: error: ‘class std::unordered_map&lt;unsigned int, std::vector&lt;unsigned int&gt; &gt;’ has no member named ‘serialize’
   34 |         type.serialize(ar);
      |         ~~~~~^~~~~~~~~</code></pre>
<p>정확한 발생 원인과 해결 방안은 정의하지 못하였다. 다만 위의 이슈 페이지에 비슷한 내용이 있었기에 해당 지시를 따라보았다.</p>
<p>에러 해결을 위해서는 <code>LIO-SAM/include/utility.h</code> 파일을 수정하면 되는데 <code>#include &lt;opencv2/opencv.hpp&gt;</code> 구문을 라인 33번(<code>#include &lt;pcl_conversions/pcl_conversions.h&gt;</code> 밑)으로 옮기게 되면 정상적으로 빌드되는 것을 확인하였다.</p>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/8e071827-6339-47a7-a4ec-93cdfe48dfc9/image.png" alt=""></p>
<p>이를 바탕으로 의존성 순서 문제로 패키지가 정상적으로 불러와지지 않아서 발생하는 것이라고 추측하였다.</p>
<p>여기까지 문제없이 진행되었다면 이제 알고리즘을 돌리기 위한 기본 설정은 끝난 것이다.</p>
<hr>
<h3 id="번외-1-dso-missing-from-command-line-에러">번외 1. <strong>DSO missing from command line</strong> 에러</h3>
<p>빌드 과정에서 프로젝트 초기에 발생하여 나를 4일동안 삽질하게 만들었던 에러이다. 
패키지의 경로 설정이나 버전 충돌 문제인 듯한데 구글링 결과 너무나도 다양한 방법이 있었지만, 해결되는 방법은 없었다. 
나의 경우 우분투를 새로 설치하는 것으로 문제를 해결하였다. (부팅 USB를 다시 꺼내보자..)</p>
<h3 id="번외2-could-not-found-tf-에러">번외2. Could not found tf 에러</h3>
<p>사실 이 에러는 대부분의 사람에게 발생하지 않을 것이다. 나도 도대체 왜 발생한 줄 모르겠는 에러로 그냥 ros 설치가 날아간 건지 모든 ros 명령어가 인식되지 않았다. 간단히 ros 재설치로 해결하였다.</p>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/477bca34-e4d4-4194-87a9-871340924aec/image.png" alt=""></p>
<h2 id="lio-sam-테스트">LIO-SAM 테스트</h2>
<hr>
<p>이제 빌드된 코드가 정상적으로 작동하는지 확인해 보려고 한다.</p>
<p>Github 레포지토리를 확인해 보면 다양한 샘플 데이터를 제공하고 있다. 이 데이터를 가지고 테스트를 진행해 보자.</p>
<p>테스트는 3가지의 데이터셋을 가지고 진행해 보려고 한다. 사용할 데이터셋은 <a href="https://drive.google.com/drive/folders/1gJHwfdHCRdjP7vuT556pv8atqrCJPbUq">여기</a>서 다운로드 받으면 된다.</p>
<h3 id="0-기본-설정">0. 기본 설정</h3>
<p>테스트를 시작하기 전에 처음 ROS 설치 후에 <code>source</code> 명령어를 입력하였던 것을 기억할 것이다. 
이와 마찬가지로 빌드한 소스와 경로를 시스템에 적용하기 위해서는 동일하게 <code>source</code> 명령어를 작성하여야 한다.</p>
<pre><code class="language-bash">cd ~/catkin_ws
source devel/setup.bash</code></pre>
<p>해당 명령어를 작성하게 되면 빌드한 파일과 경로가 시스템에 적용되어 실행할 수 있게 된다.</p>
<h3 id="1-velodyne-park-dataset">1. Velodyne Park Dataset</h3>
<p>처음으로 실행해 볼 데이터셋은 <code>Park Dataset</code>이다. </p>
<pre><code class="language-bash">roslaunch lio-sam run.launch</code></pre>
<p>데이터셋을 다운로드한 후 LIO-SAM을 실행시킨다.</p>
<pre><code class="language-bash">rosbag play {파일 경로}/park_dataset.bag -r 3</code></pre>
<p>이후 위 명령어를 실행시키면 LIO-SAM이 정상적으로 동작하는 것을 확인할 수 있다.</p>
<p>(비록 테스트 데이터지만 뭔가 작동하니까 행복하지 않은가? 소소한 행복을 함께 느껴보자.)</p>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/3f72dc1c-a1cc-4990-8be7-a7c523370b71/image.png" alt=""></p>
<h3 id="2-velodyne-campus-dataset-small">2. Velodyne Campus Dataset (small)</h3>
<p>다음으로는 <code>Campus</code> 데이터셋이다. 이 데이터셋은 실행해 보기 전 약간의 설정 수정이 필요했다. </p>
<p>위에서 실행한 데이터셋은 IMU 데이터가 정확한 포맷으로 들어오지 않아 내부적으로 변환이 필요하였지만, 이 데이터셋은 IMU 데이터를 ROS REP105 standard 포맷으로 보내주어 따로 내부적인 변환이 필요 없다고 한다.</p>
<p>따라서 데이터셋을 실행하기 위해서는 <code>LIO-SAM/config/params.yaml</code> 에서 <code>imuTopic</code> 값을 <code>imu_correct</code> 로 변경하고 <code>extrinsicRot</code> 와 <code>extrinsicRPY</code>를 단위행렬로 변경해 주어야 한다.
<img src="https://velog.velcdn.com/images/talking_tomato/post/bf5a7e09-f30e-4147-9ab0-08b1a7530fef/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/c0d89bdf-fbc0-4010-b23e-a2fb5ae14a2a/image.png" alt=""></p>
<p>이후 위에서와 동일하게 LIO-SAM을 실행시키고 데이터셋을 재생해 주면 된다.</p>
<pre><code class="language-bash">roslaunch lio-sam run.launch</code></pre>
<pre><code class="language-bash">rosbag play {파일 경로}/campus_small_dataset.bag -r 3</code></pre>
<p>그러면 이렇게 결과가 나올 것이다!
<img src="https://velog.velcdn.com/images/talking_tomato/post/29a8d5d2-38e1-47cf-b7ea-7091a512c414/image.png" alt=""></p>
<h3 id="3-ouster-rooftop-dataset">3. Ouster Rooftop Dataset</h3>
<p>마지막으로 실행시켜 볼 데이터셋은 Ouster 라이다로 측정된 Rooftop 데이터셋이다.</p>
<p>위의 두 데이터셋은 Velodyne으로 측정 되었지만 이 데이터셋은 Ouster 라이다로 측정되었다.</p>
<p>이 데이터셋을 실행시켜보기 위해서는 <code>LIO-SAM/config/params.yaml</code> 에서 라이다 설정을 변경해 주어야 한다.</p>
<p>먼저 <code>sensor</code> 값을 <code>ouster</code> 로 변경하고 <code>N_SCAN</code> 값을 <code>128</code>, <code>Horizon_SCAN</code> 값을 <code>1024</code>로 변경해준다.</p>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/2a6a737b-cb53-49c2-b3aa-ccac447e2259/image.png" alt=""></p>
<p>이후에 위에서 2번째 데이터셋을 테스트할 때 변경하였던 설정을 다시 원상복귀 해주어야 한다.
만약 2번 데이터셋을 실행하지 않았다면 넘어가도 된다.</p>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/fc594c2a-d6ee-4ac6-9d05-e685f8d2219b/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/15af0303-4d6c-465b-ac58-0eda2da06882/image.png" alt=""></p>
<p>다 수정하였다면 이제 실행해보도록 하자.</p>
<pre><code class="language-bash">roslaunch lio-sam run.launch</code></pre>
<pre><code class="language-bash">rosbag play {파일 경로}/rooftop_ouster_dataset.bag -r 3</code></pre>
<p>그럼 이런 결과가 나온다.</p>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/36452a6f-b166-45a9-8541-0fd9d08ca923/image.png" alt=""></p>
<h2 id="lio-sam-데이터-수집">LIO-SAM 데이터 수집</h2>
<hr>
<p>이제 제공되는 데이터셋만 돌리던 안락한 생활은 끝났다. 이제 실제 센서로 데이터를 수집할 차례이다.</p>
<h3 id="0-센서-드라이버-설치">0. 센서 드라이버 설치</h3>
<p>나는 이 프로젝트에서는 Ouster 라이다, iAHRS IMU 센서를 사용하였다.</p>
<p>이 센서들을 사용하기 위해서는 ROS 드라이버 설치가 필요하다.</p>
<h4 id="1-ouster-lidar">1. Ouster LiDAR</h4>
<p>먼저 Ouster LiDAR이다. 라이다를 컴퓨터에 연결하고 데이터를 받아오는 부분은 이미 많은 사람들이 다루었기 때문에 따로 설명하지는 않겠다. 나는 <a href="https://stupidly-honest.tistory.com/67">이 블로그 글</a>을 참고하였다. 자세한 부분까지 잘 설명되어 있어 쉽게 할 수 있을 것이다.</p>
<p>마찬가지로 설치를 완료하고 센서가 잘 돌아가는지 확인하려면 아래 명령어를 입력해보면 된다.</p>
<pre><code class="language-bash">roslaunch ouster_ros sensor.launch</code></pre>
<p>그럼 자동으로 <code>rviz</code>가 실행되면서 PCD 데이터를 시각화 해줄 것이다.</p>
<p>위의 LIO-SAM에서 처럼 새로운 터미널을 열었다면 <code>source</code> 명령어를 입력하는 것을 잊지 말자.</p>
<pre><code class="language-bash">cd ~/catkin_ws
source devel/setup.bash</code></pre>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/9dceddb2-9717-450f-98ca-e2a6da655d58/image.png" alt=""></p>
<h4 id="2-iahrs-imu">2. iAHRS IMU</h4>
<p>다음으로는 IMU 센서의 드라이버를 설치하여야 한다. 마찬가지로 <a href="https://github.com/wookbin/iahrs_driver">드라이버 github</a>에 잘 정리되어 있다.
절대 귀찮아서 설명하지 않는 것은 아니다.</p>
<h3 id="1-ouster-lidar--iahrs-imu">1. Ouster LiDAR + iAHRS IMU</h3>
<p>드라이버 설치가 끝났다면 이제 실제 데이터를 수집해보도록 하자.</p>
<p>데이터를 수집하기 위해서는 LIO-SAM의 설정 파일을 변경해주어야 한다.
여기서 변경하는 값들은 개인의 센서에 따라 달라질 수 있으니 확인 후 변경해주어야 한다.</p>
<p>먼저 <code>pointCloudTopic</code>을 <code>ouster/points</code>로 변경해 주었다.
그리고 <code>imuTopic</code>을 <code>imu/data</code>로 변경하였다.</p>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/2d9d38aa-72e9-4cec-b2dd-dec48949d3b0/image.png" alt=""></p>
<p>다음으로는 <code>sensor</code>를 <code>ouster</code>로 변경하였다.
우리는 OS1-32-U 모델을 사용하고 있었기 때문에 <code>N_SCAN</code>은 <code>32</code>로 <code>Horizon_SCAN</code>은 <code>1024</code>로 설정하였다.
<img src="https://velog.velcdn.com/images/talking_tomato/post/17b5b5e9-08fc-42bf-b487-d1295d6ad895/image.png" alt=""></p>
<p>실행하기 전에 위에서 설치한 <code>ouster_ros</code>의 <code>sensor.launch</code> 파일에서도 동일하게 설정해주자.
<img src="https://velog.velcdn.com/images/talking_tomato/post/3890c36d-11ea-4e18-b7ca-78ea5c4853ff/image.png" alt=""></p>
<p>이제 두근거리는 마음으로 SLAM을 돌려보자. 과연 성공적으로 작동할까?</p>
<h4 id="에러1--point-cloud-is-not-in-dense-format">에러1 : Point cloud is not in dense format</h4>
<p>당연하게도 한 번에 될리가 없다. 에러 메세지를 한 번 확인해보자.</p>
<pre><code class="language-bash">[lio_sam_imageProjection-2] restarting process
process[lio_sam_imageProjection-2]: started with pid [17045]
[ INFO] [1743588602.428283737]: ----&gt; Image Projection Started.
[ERROR] [1743588602.844451925]: Point cloud is not in dense format, please remove NaN points first!</code></pre>
<p>ouster 라이다에서 내보내는 <code>is dense</code> 항목이 <code>false</code>라고 한다.</p>
<pre><code>rostopic echo /ouster/points/is_dense -n 30</code></pre><p>위 명령어를 실행하면 현재 발행중인 ouster 라이다의 토픽을 볼 수 있다. </p>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/756ce22d-b35f-4157-b9ba-2da84786f9dd/image.png" alt=""></p>
<p>안타깝게도 정말 <code>False</code>가 뜨고 있다.
테스트 데이터도 확인해보자.</p>
<pre><code>rosbag play -r 3 dataset/rooftop_ouster_dataset.bag</code></pre><pre><code>rostopic echo /points_raw/is_dense -n 3</code></pre><p><img src="https://velog.velcdn.com/images/talking_tomato/post/e1c1a5f2-94d5-4eda-b688-301847940945/image.png" alt=""></p>
<p>테스트 데이터에서는 정상적으로 <code>True</code>가 뜨는 것을 확인할 수 있다.</p>
<p>해당 오류를 일으키는 코드를 확인해보면 <code>imageProjection.cpp</code> 파일에서 해당 예외를 처리하고 있다. 코드대로라면 들어온 데이터에서<code>is_dense</code> 값을 뽑아서 처리한다는 것을 알 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/318ee8dd-5031-479e-931a-cc3edc489bb2/image.png" alt=""></p>
<p>그렇다면 저 <code>is_dense</code> 값은 라이다에서 생성하여 토픽으로 넘겨준다는 것이다. 해당 값을 생성하는 코드를 찾아보자.</p>
<p><code>ouster_ros</code> 패키지의 <code>point_cloud_compose.h</code>파일을 확인해보면 코드가 있다. is_dense는 포인트 측정 간에 값 중 NaN 값이 들어있으면 false로 판단되는 것이다.</p>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/b2380926-59ce-47d8-a843-d8f2b92cd1d9/image.png" alt=""></p>
<p>그렇다면 측정값에서 NaN을 없애면 되는 것 아니겠는가? 사실 공식 SDK를 수정하는 것이 맞는 선택일지는 잘 모르겠지만 지금 당장은 다른 방법이 없으니 한 번 해보도록 하자. 일단 돌아가면 장땡 아니겠는가?</p>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/79bd183a-e0aa-437f-a760-0d38b2a13e8d/image.png" alt=""></p>
<pre><code class="language-python">for (int i = 0; i &lt; 3; ++i) {
    if (std::isnan(xyz(i))) {
        xyz(i) = 0.0f;
    }
}</code></pre>
<p>위와 같이 값에 필터를 씌워 NaN 값을 강제로 0으로 대체했다.</p>
<p>글로 보았을 때에는 금방 해결한 것 처럼 보이지만 사실 엄청난 삽질이 있었다. 하지만 반쯤은 뻘짓이었으니 넘어가도록 하겠다. 어쨌든 돌아가는 하나의 경우를 발견하였지 않은가?</p>
<h4 id="에러2--imu-센서-축-설정-문제">에러2 : IMU 센서 축 설정 문제</h4>
<p>이후 코드를 돌려보면 rviz 화면에 뭐가 뜨기 시작한다! </p>
<p>여기서부터는 사용하는 장비에 따라 정상적으로 코드가 실행되는 사람도 있을 것이고 나와 같이 별자리마냥 요상한 그림이 그려지고 있는 사람이 있을 수도 있다. 혹은 뭔가 뜨긴 하는데 공간이 뒤틀리고 있는 사람도 있을 것이다.</p>
<p>만약 한 번에 정상적으로 뜨는 사람들은 축하한다. 더 이상 글을 읽을 필요가 없다. 성공을 자축하며 떠나길 바란다.</p>
<p>그러나 그렇지 않은 사람은 나와 함께 조금 더 달려가보자.</p>
<p>참고로 아래는 나의 결과 화면이다. <del>별자리가 하나 생겼다.</del></p>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/97c67d1d-a5ef-4c48-8548-1d3d9f2af0de/image.png" alt=""></p>
<p>엄청난 삽질 끝에 알아낸 원인은 imu 설정 문제였다. 위 화면에서 정상적인 결과가 나온 사람들은 운이 좋게도 imu 센서의 축 정렬이 설정된 값과 동일하였던 것이다.</p>
<p><a href="https://github.com/TixiaoShan/LIO-SAM">깃허브</a>에 보면 IMU 센서 축 관련 내용이 있다.</p>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/82a7aaf9-77d9-493d-8483-0ee87c6c35eb/image.png" alt=""></p>
<p>바로 이 화면이다.</p>
<p>여기서는 라이다의 축과 부착된 IMU 축이 달라서 따로 설정을 해줬다. 위에서 2번 데이터셋을 실행할 때 <code>extrinsicRot</code>와 <code>extrinsicRPY</code>를 설정한걸 기억하는가? 그것이 IMU 센서의 축을 라이다와 동일하게 맞춘 것이다. </p>
<p>그래서 우리는 <code>extrinsicRot</code>와 <code>extrinsicRPY</code>를 우리 IMU 센서에 맞게 축을 수정해주어야 한다. 만약 IMU 센서와 라이다의 축이 동일하다면 아래와 같이 단위행렬로 변경하면 된다.</p>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/9dc3f2ae-99d1-424f-bd9c-a7bca9c6c4bc/image.png" alt=""></p>
<p>그리고 실행해보면 별자리가 사라지고 나름 맵의 형태를 띄는 것을 볼 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/384d63c6-f3cf-4b93-adc9-1296b0d76f16/image.png" alt=""></p>
<h4 id="에러3--imu-센서-드리프트-현상">에러3 : IMU 센서 드리프트 현상</h4>
<p>그러나 나의 경우에는 IMU 센서의 성능 문제로 지속적인 드리프트가 발생하였다. 위 사진이 약간 이상한 것도 드리프트가 발생해서 그렇다. 이 문제는 <a href="https://github.com/gaowenliang/imu_utils">imu_utils</a> 툴을 사용하여 imu 센서의 노이즈 값을 찾은 후 파라미터를 수정해주어야 한다.</p>
<p>자세한 적용 방식은 위의 깃허브 페이지에 자세히 설명되어 있으니 현재는 설명하지 않고 추후에 기회가 된다면 추가하도록 하겠다.</p>
<h2 id="결론">결론</h2>
<hr>
<p>결론적으로 나는 노이즈 값을 찾아서 적용하였지만 센서의 성능 문제인지 계속 드리프트가 일어나 IMU를 사용하는 SLAM 알고리즘을 포기하고 <a href="https://github.com/PRBonn/kiss-slam">KISS-SLAM</a>을 사용하였다. 이건 파이썬 기반이라 버전 탈 염려도 없고 잘 돌아간다... 만세! ROS와 연동하는 문제가 있긴 하지만 이건 <a href="https://github.com/PRBonn/kiss-icp">KISS-ICP</a>를 사용하거나 직접 ROS 패키지를 만들면 해결되는 문제이다.</p>
<p>이렇게 LIO-SAM을 적용해보려던 시도를 한 번 적어보았다. 결론적으로는 다른 알고리즘을 사용하게 되었지만 시도한 과정을 정리할 겸 누군가에게 도움이 될까 하여 포스트를 정리해본다. 다들 즐겁고 막힘없는 코딩 생활 되길 바란다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[탐욕 알고리즘(Greedy algorithm)]]></title>
            <link>https://velog.io/@talking_tomato/%ED%83%90%EC%9A%95-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98Greedy-algorithm</link>
            <guid>https://velog.io/@talking_tomato/%ED%83%90%EC%9A%95-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98Greedy-algorithm</guid>
            <pubDate>Tue, 06 Feb 2024 05:12:40 GMT</pubDate>
            <description><![CDATA[<h2 id="그리디-알고리즘">그리디 알고리즘</h2>
<p>탐욕 알고리즘, 다른말로 그리디 알고리즘이란 각 분기마다 현 상황에서 최적의 선택을 고르는 알고리즘을 말합니다. 완전 탐색 알고리즘과 비슷하지만 모든 선택지를 고려하여 그 중 최적해를 찾는 방법이 아닌 지금 당장 가장 좋은 방법만을 선택하는 것입니다.
<img src="https://velog.velcdn.com/images/talking_tomato/post/70ba40ae-ce97-4f8f-b1a6-8e04394b96b3/image.png" alt=""></p>
<h2 id="그리디-알고리즘의-사용">그리디 알고리즘의 사용</h2>
<p>그리디 알고리즘은 많은 경우 최적해를 찾지 못합니다. 그러나 2가지의 경우에서는 그리디 알고리즘을 사용하는 것이 좋은 방법일 수 있습니다.</p>
<ol>
<li>탐욕법을 사용하도 항상 최적해를 구할 수 있는 문제를 만난 경우, 탐욕법은 다른 알고리즘에 비해 수행 시간이 월등히 빠르기 때문에 유용</li>
<li>시간이나 공간적 제약으로 인해 다른 방법으로 최적해를 찾기 어려운 경우 최적해 대신 근사해를 찾는 것으로 타협하는 경우</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[DFS와 BFS]]></title>
            <link>https://velog.io/@talking_tomato/DFS%EC%99%80-BFS</link>
            <guid>https://velog.io/@talking_tomato/DFS%EC%99%80-BFS</guid>
            <pubDate>Thu, 01 Feb 2024 05:04:45 GMT</pubDate>
            <description><![CDATA[<p>DFS와 BFS는 그래프 탐색 알고리즘 중 하나입니다.</p>
<h2 id="깊이-우선-탐색dfs-depth-first-search">깊이 우선 탐색(DFS, Depth-First Search)</h2>
<p>DFS는 Depth-First Search, 깊이 우선 탐색이라고도 불리며 그래프의 한 노드에서 시작하여 해당 분기의 노드를 완전히 탐색하고 다음 분기로 넘어가는 방식을 말합니다.</p>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/a8a39921-b725-46fc-bd29-2228163fc88d/image.gif" alt=""></p>
<p>쉽게 말해 루트 노드에서 시작하여 한 방향으로 갈 수 있을 때까지 계속 가다가 더 이상 갈 수 없게 되었을 떄 가장 가까운 분기로 돌아와 다시 탐색하는 방법입니다. 보통 모든 노드를 방문하여야 하는 경우 사용하게 됩니다.</p>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/f0776c4b-5f6e-46ce-a962-500bdaed7c30/image.png" alt=""></p>
<p>DFS의 시간복잡도는 V를 정점의 수, E를 간선의 수라 할 때 $O(|V| + |E|)$이다.</p>
<h2 id="너비-우선-탐색bfs-breadth-first-search">너비 우선 탐색(BFS, Breadth-First Search)</h2>
<p>BFS는 Breadth-First Search, 너비 우선 탐색이라고도 불리며 그래프의 한 노드에서 시작하여 인접한 노드를 먼저 탐색하고 그 후에 멀리 떨어져 있는 노드를 탐색하는 방식을 말합니다.</p>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/6fe0d0c4-ca3d-4772-8a29-f2b41d9e4190/image.gif" alt=""></p>
<p>루트 노드에서 시작하여 해당 노드의 이웃 노드를 모두 방문하고 그 후 다음 노드로 넘어가는 방법입니다. 보통 최단경로를 구하거나 임의의 경로를 구하는 상황에 사용하게 됩니다.</p>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/4225bffb-3380-437f-a9af-31d7da40f3d0/image.png" alt=""></p>
<p>BFS의 시간복잡도는 $O(|V| + |E|)$로 DFS와 동일합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[정렬 알고리즘]]></title>
            <link>https://velog.io/@talking_tomato/%EC%A0%95%EB%A0%AC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</link>
            <guid>https://velog.io/@talking_tomato/%EC%A0%95%EB%A0%AC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</guid>
            <pubDate>Mon, 29 Jan 2024 05:16:16 GMT</pubDate>
            <description><![CDATA[<h2 id="정렬-알고리즘이란">정렬 알고리즘이란</h2>
<p>정렬 알고리즘은 원소들을 번호순이나 사전 순서와 같이 일정한 순서대로 열거하는 알고리즘을 뜻한다. 정렬 알고리즘에는 다양한 종류가 있지만 시간 복잡도와 공간 복잡도를 고려하여 올바른 알고리즘을 선택하는 것이 중요하다.</p>
<h2 id="선택-정렬">선택 정렬</h2>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/969caebb-8a4f-4458-bef7-54045c831c44/image.gif" alt="선택정렬 알고리즘 애니메이션"></p>
<blockquote>
<p>시간복잡도(최악/최선/평균) : $O(n^2)$</p>
</blockquote>
<p>선택 정렬은 아직 처리되지 않은 데이터들 중 가장 작은 데이터를 골라 맨 앞의 데이터와 변경하는 방식으로 다음과 같은 순서로 이루어진다.</p>
<ol>
<li>주어진 리스트에서 최솟값을 찾는다.</li>
<li>그 값을 맨 앞에 위치한 값과 교체한다.</li>
<li>맨 처음 위치를 뺸 나머지 리스트를 같은 방법으로 교체한다.</li>
</ol>
<h2 id="삽입-정렬">삽입 정렬</h2>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/3616cb1e-191f-405d-9dbb-59c9de6540f8/image.gif" alt="삽입정렬 알고리즘 애니메이션"></p>
<blockquote>
<p>시간복잡도(최악/평균) : $O(n^2)$
시간복잡도(최선) : $O(n)$</p>
</blockquote>
<p>삽입 정렬은 자료 배열의 모든 요소를 앞에서부터 차례대로 이미 정렬된 배열 부분과 비교하여, 자신의 위치를 찾아 삽입하는 방식으로 다음과 같은 순서로 이루어진다.</p>
<ol>
<li>주어진 리스트에서 2번째 요소와 앞의 요소를 비교한다.</li>
<li>해당 요소를 적절한 위치에 삽입한다.</li>
<li>앞의 리스트를 제외한 나머리 리스트를 같은 방법으로 삽입한다.</li>
</ol>
<h2 id="버블-정렬">버블 정렬</h2>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/294483ff-2abf-4128-8b95-a79abae40907/image.gif" alt="버블정렬 알고리즘 애니메이션"></p>
<blockquote>
<p>시간복잡도(최악/평균) : $O(n^2)$
시간복잡도(최선) : $O(n)$</p>
</blockquote>
<p>버블정렬은 기본적으로 배열의 두 수 $(a, b)$를 선택하여 만약 그 두 수가 정렬되었다면 놔두고 아니라면 두 수를 바꾸는 방식으로 다음과 같은 순서로 이루어진다.</p>
<ol>
<li>배열의 첫 두 요소를 비교한다.</li>
<li>만약 정렬되어 있다면 그대로 두고 아니라면 위치를 바꾸어 정렬한다.</li>
<li>이를 배열의 처음부터 끝까지 여러 번 반복한다.</li>
<li>만약 배여르이 변화가 없으면 정렬된 것으로 정의한다.</li>
</ol>
<h2 id="병합-정렬">병합 정렬</h2>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/c032f9af-2adf-4139-8648-584b6e29f3c3/image.gif" alt="병합정렬 알고리즘 애니메이션"></p>
<blockquote>
<p>시간복잡도(최악/최선/평균) : $O(nlogn)$</p>
</blockquote>
<p>병합정렬은 다음과 같은 순서로 이루어진다.</p>
<ol>
<li>정렬되지 않은 리스트를 각각 하나의 원소만 포함하는 n 개의 부분리스트로 분할한다.</li>
<li>부분리스트가 하나만 남을 때까지 반복해서 병합하며 저렬된 부분리스트를 생성한다.</li>
<li>마지막으로 남은 부분리스트가 정렬된 리스트이다.</li>
</ol>
<p>퀵 정렬보다는 느리고 공간을 많이 잡아먹지만 안정 정렬에 속하여 동일 값일 때 기존의 순서가 보장된다는 장점이 있다.</p>
<h2 id="퀵-정렬">퀵 정렬</h2>
<p><img src="https://velog.velcdn.com/images/talking_tomato/post/6b7f1c95-3ec2-43bb-af3a-3a35024e4322/image.gif" alt=""></p>
<blockquote>
<p>시간복잡도(최악) : $O(n^2)$
시간복잡도(최선/평균) : $O(nlogn)$</p>
</blockquote>
<p>퀵 정렬은 다음과 같은 순서로 이루어진다.</p>
<ol>
<li>리스트 가운데서 하나의 원고를 고른다. 이렇게 고른 원소를 피벗이라고 한다.</li>
<li>피벗 앞에는 피벗보다 값이 작은 모든 원소들이 오고, 피벗 뒤에는 피벗보다 값이 큰 모든 원소들이 오도록 분할한다.</li>
<li>분할된 두 리스트에 대해 위 과정을 반복한다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] 생성자에서의 예외 처리]]></title>
            <link>https://velog.io/@talking_tomato/Java-%EC%83%9D%EC%84%B1%EC%9E%90%EC%97%90%EC%84%9C%EC%9D%98-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@talking_tomato/Java-%EC%83%9D%EC%84%B1%EC%9E%90%EC%97%90%EC%84%9C%EC%9D%98-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Sun, 30 Jul 2023 03:49:07 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이 시리즈는 개발 도중 알게 된 간단한 정보들을 정리하는 시리즈입니다.</p>
</blockquote>
<p>생성자에서 예외가 생기면 어떻게 해야할까? </p>
<p>생성자는 return값이 없기 때문에 예외를 사용하여 처리하여야 한다. 기본적으로 자바에서 유효하지 않은 데이터가 전달되면 자동으로 예외를 발생시켜 객체가 인스턴스화 되지 않도록 방지한다. </p>
<p>그러나 예외가 발생되기 전 생성자가 데이터에 엑세스 한 경우, 부분적으로 생성된 생성자가 남아있을 수 있으므로 try-with-resources 등을 사용해 리소스를 해제하는 것이 좋다.</p>
<p>아래의 코드는 생성자 처음 부분에 데이터를 검사하여 예외를 처리하는 코드이다. </p>
<pre><code class="language-java">public Item(ItemStack item) throws IllegalArgumentException {
    if (!isItem(item)) {
        throw new IllegalArgumentException(&quot;Invalid item&quot;);
    }

    this.material = item.getType().toString();

    ItemMeta itemMeta = item.getItemMeta();

    this.name = itemMeta.getDisplayName();
    this.lore = itemMeta.getLore();
    this.customModelData = itemMeta.getCustomModelData();
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[OOP] 객체 지향 설계의 5원칙 S.O.L.I.D]]></title>
            <link>https://velog.io/@talking_tomato/OOP-%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%EC%84%A4%EA%B3%84%EC%9D%98-5%EC%9B%90%EC%B9%99-S.O.L.I.D</link>
            <guid>https://velog.io/@talking_tomato/OOP-%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%EC%84%A4%EA%B3%84%EC%9D%98-5%EC%9B%90%EC%B9%99-S.O.L.I.D</guid>
            <pubDate>Sat, 29 Jul 2023 07:32:01 GMT</pubDate>
            <description><![CDATA[<h2 id="객체-지향-설계의-5원칙-solid">객체 지향 설계의 5원칙 S.O.L.I.D</h2>
<p>SOLID 원칙이란 객체 지향 프로그래밍 및 설계의 다섯 가지 기본 원칙을 말한다.</p>
<ul>
<li>SRP(Single Responsibility Principle): 단일 책임 원칙</li>
<li>OCP(Open Closed Priciple) : 개방 폐쇄 원칙</li>
<li>LSP(Listov Substitution Priciple) : 리스코프 치환 원칙</li>
<li>ISP(Interface Segregation Principle) : 인터페이스 분리 원칙</li>
<li>DIP(Dependency Inversion Principle) : 의존 역전 원칙</li>
</ul>
<p>좋은 소프트웨어란 확장성이 뛰어난 것을 의미한다. 뛰어난 확장성을 가지려면 좋은 설계가 필요하다. 그렇다면 좋은 설계란 무엇인가? 좋은 설계는 소프트웨어에 변경점이 있을 때 영향을 받는 범위가 적은 설계를 뜻한다. </p>
<p>SOLID 객체 지향 설계 원칙을 적용하면 코드의 확장성이 높아지고 유지 보수가 쉬워진다. 또한 코드의 복잡도가 줄어들어 생산성을 높일 수 있다. 다만 모든 원칙을 다 지키는 것은 쉬운 일이 아니며 꼭 모든 원칙을 지켜야 하는 것은 아니다.</p>
<h2 id="srpsingle-responsibility-principle">SRP(Single Responsibility Principle)</h2>
<blockquote>
<p>클래스는 단 하나의 책임만 가져야 한다</p>
</blockquote>
<p>어떠한 클래스가 단 하나의 책임만 가져야 한다는 것은 코드를 변경해야 하는 이유가 단 하나여야 한다는 의미이다. 하나의 클래스가 하나의 기능만 담당하고 있으면 코드를 변경해야 하는 이유가 &#39;해당 기능을 수정할 때&#39; 하나뿐이니 단일 책임 원칙을 준수하고 있다고 볼 수 있다.</p>
<h2 id="ocpopen-closed-priciple">OCP(Open Closed Priciple)</h2>
<blockquote>
<p>확장에는 열려있어야 하고, 변경에는 닫혀 있어야 한다</p>
</blockquote>
<p>어떠한 기능을 추가할 때 기존 클래스를 확장하여 쉽게 구현하면서도 기존의 코드는 수정을 최소화해야 한다는 원칙이다. 즉 기존의 코드를 수정하지 않고도 기능을 추가할 수 있도록 설계해야 한다는 의미이다. </p>
<h2 id="lsplistov-substitution-priciple">LSP(Listov Substitution Priciple)</h2>
<blockquote>
<p>하위 타입 객체는 상위 타입 객체에서 가능한 행위를 수행할 수 있어야 한다</p>
</blockquote>
<p>서브 타입(자식)은 언제나 상위 타입(부모)으로 교체할 수 있어야 한다는 원칙이다. 즉, 부모 타입으로 메서드를 실행해도 의도대로 실행되도록 구성을 해줘야 한다는 것이다. 이러한 원칙을 잘 지키려면 부모 메서드의 오버라이딩을 할 때 주의를 기울여야 한다.</p>
<h2 id="ispinterface-segregation-principle">ISP(Interface Segregation Principle)</h2>
<blockquote>
<p>자신이 사용하는 메소드에만 의존해야 한다</p>
</blockquote>
<p>인터페이스를 각각 사용별로 분리해야한다는 설계 원칙이다. 이는 SRP와 비슷한 데 SRP가 클래스를 분리하는 것이라면, ISP는 인터페이스를 분리하는 것이라고 보면 된다. 결국 필요로 하는 인터페이스들을 분리함으로써, 사용하지 않는 인터페이스에 변경이 발생하더라도 영향을 받지 않도록 만들어야 하는 것이 핵심이라고 볼 수 있다.</p>
<h2 id="dipdependency-inversion-principle">DIP(Dependency Inversion Principle)</h2>
<blockquote>
<p>고수준의 코드는 저수준의 코드에 의존해서는 안 된다</p>
</blockquote>
<p>의존 관계를 맺을 때, 구체화된 클래스에 의존하기 보다는 추상 클래스나 인터페이스에 의존해야 한다는 원칙으로 저수준 모듈이 변경되어도 고수준 모듈은 변경이 필요없는 형태로 만들어야 한다는 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[GOF] 디자인 패턴]]></title>
            <link>https://velog.io/@talking_tomato/Design-pattern-%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@talking_tomato/Design-pattern-%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Thu, 27 Jul 2023 13:22:41 GMT</pubDate>
            <description><![CDATA[<h2 id="1-디자인-패턴이란">1. 디자인 패턴이란?</h2>
<blockquote>
<p>프로그램을 설계할 때 발생하였던 문제점들을 객체 간의 상호 관계 등을 이용하여 해결할 수 있도록 하나의 규약 형태로 만들어 놓은 것</p>
</blockquote>
<p>디자인 패턴이란 프로그램을 설계할 때 발생하였던 문제점들을 객체 간의 상호 관계 등을 이용하여 해결할 수 있도록 하나의 규약 형태로 만들어 놓은 것을 의미한다. 즉 과거의 소프트웨어 개발 과정에서 반복적으로 일어나는 문제를 해결하는 설계 방안을 정리하고 이름을 붙여 이후에 재사용하기 좋은 형태로 정리한 것이다.</p>
<h2 id="2-디자인-패턴의-종류">2. 디자인 패턴의 종류</h2>
<blockquote>
<p>생성 패턴, 구조 패턴, 행위 패턴</p>
</blockquote>
<p>디자인 패턴은 크게 3가지로 나눌 수 있다.</p>
<p>먼저 <strong>생성 패턴(Creational Pattern)</strong>이다. 객체의 생성에 관련된 패턴으로 객체의 생성과 조합을 캡슐화해 특정 객체가 생성되거나 변경되어도 프로그램 구조에 영향을 크게 받지 않도록 유연성을 제공한다.</p>
<p>다음은 <strong>구조 패턴(Structural Pattern)</strong>이다. 구조 패턴은 클래스나 객체를 조합해 더 큰 구조를 만드는 패턴이다. 예를 들어 서로 다른 인터페이스를 지닌 2개의 객체를 묶어 단일 인터페이스를 제공하거나 객체들을 서로 묶어 새로운 기능을 제공하는 패턴이다.</p>
<p>마지막으로 <strong>행위 패턴(Behavioral Parrern)</strong>이다. 객체나 클래스 사이의 알고리즘이나 책임 분배에 관련된 패턴으로 한 객체가 혼자 수행할 수 없는 작업을 여러 개의 객체로 어떻게 분배하는지, 또 그렇게 하면서도 객체 사이의 결합도를 최소화하는 것에 중점을 두는 방식이다.</p>
<h2 id="3-생성-패턴">3. 생성 패턴</h2>
<blockquote>
<p>객체의 생성에 관련된 패턴</p>
</blockquote>
<ul>
<li>싱글톤 패턴 : 하나의 클래스에 오직 하나의 인스턴스만 가지는 패턴<h2 id="4-구조-패턴">4. 구조 패턴</h2>
<blockquote>
<p>클래스나 객체를 조합해 더 큰 구조를 만드는 패턴</p>
</blockquote>
</li>
</ul>
<h2 id="5-행위-패턴">5. 행위 패턴</h2>
<blockquote>
<p>객체나 클래스 사이의 알고리즘이나 책임 분배에 관련된 패턴</p>
</blockquote>
<ul>
<li>전략 패턴 : 실행 중에 알고리즘 전략을 선택하여 객체 동작을 실시간으로 바뀌도록 할 수 있게 하는 패턴</li>
</ul>
<hr>
<p>이후 공부하며 추가 예정</p>
]]></description>
        </item>
    </channel>
</rss>