<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Atmosphere.log</title>
        <link>https://velog.io/</link>
        <description>작게, 빠르게, 지속가능하게</description>
        <lastBuildDate>Fri, 10 Oct 2025 02:23:44 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. Atmosphere.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/park-rgb125" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[간단한 실험:permission denied]]></title>
            <link>https://velog.io/@park-rgb125/%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%8B%A4%ED%97%98permission-denied</link>
            <guid>https://velog.io/@park-rgb125/%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%8B%A4%ED%97%98permission-denied</guid>
            <pubDate>Fri, 10 Oct 2025 02:23:44 GMT</pubDate>
            <description><![CDATA[<p>간단한 실험. 사용자 gut 디렉토리에 testdir/a.txt를 만들고 이걸 test라는 사용자에서 접근해보기</p>
<pre><code>mkdir testdir
touch testdir/a.txt
chmod 600 testdir/a.txt
su test
cat testdir/a.txt</code></pre><p>이 상태로 접근해보려고 하면 당연히 이렇게 permission denied가 뜬다.
<img src="https://velog.velcdn.com/images/park-rgb125/post/dcdb3f93-f3c8-4c80-a95c-15aa44a70019/image.png" alt=""></p>
<p>문제1. testdir 디렉토리에 대한 other 접근 권한이 부여되지 않음
문제2. a.txt에 대한 other 접근 권한이 부여되지 않음</p>
<p>그래서 <code>chmod 605 testdir</code>(읽기권한r+실행권한x)로 바꿔주면
<img src="https://velog.velcdn.com/images/park-rgb125/post/2b4a41e9-f5c5-480a-9f37-363652270c3d/image.png" alt=""></p>
<p>읽기권한과 실행권한을 추가해주어 ls로 접근이 잘 되는것을 확인할 수 있다.</p>
<p>이제 <code>chmod 704 testdir/a.txt</code>로 a.txt파일을 읽을 수 있는 읽기 권한 까지 주면</p>
<p><img src="https://velog.velcdn.com/images/park-rgb125/post/1d06a74a-ff23-4769-b147-0ea5c50879c8/image.png" alt=""></p>
<p>(a.txt는 빈 파일이다)
문제없이 접근이 잘 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[터미널 / 사용자 / 세션 / 디렉토리 / 접속의 관계]]></title>
            <link>https://velog.io/@park-rgb125/%ED%84%B0%EB%AF%B8%EB%84%90-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%84%B8%EC%85%98-%EB%94%94%EB%A0%89%ED%86%A0%EB%A6%AC-%EC%A0%91%EC%86%8D%EC%9D%98-%EA%B4%80%EA%B3%84</link>
            <guid>https://velog.io/@park-rgb125/%ED%84%B0%EB%AF%B8%EB%84%90-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%84%B8%EC%85%98-%EB%94%94%EB%A0%89%ED%86%A0%EB%A6%AC-%EC%A0%91%EC%86%8D%EC%9D%98-%EA%B4%80%EA%B3%84</guid>
            <pubDate>Fri, 10 Oct 2025 01:29:14 GMT</pubDate>
            <description><![CDATA[<p>1️⃣<strong>터미널(terminal)</strong>:단순히 명령을 입력하는 인터페이스(껍데기)!
이 안에서 누가 로그인 했는지, 어디 디렉토리인지, 어떤 세션인지에 따라 전혀 다른 세상이 열린다!</p>
<p>2️⃣<strong>사용자(user)</strong>는 계정의 정체성!
리눅스의 user는 로그인 계정 = windows의 사용자 계정</p>
<pre><code>root
ubuntu
test

이 사용자별로 홈 디렉토리가 있음
/home/root
/home/ubuntu
/home/test</code></pre><p>3️⃣<strong>(중요)세션(session)</strong>: 누가, 어떤 계정으로, 어떤 터미널에서 접속했는가.
하나의 터미널에는 한 명의 사용자가 로그인 상태로 존재.
<code>ssh</code>, <code>su</code>, <code>sudo</code> 같은 명령은 이 “세션의 사용자 신분”을 바꾸는 것</p>
<p>한 컴퓨터에서 터미널 3개를 열면 이렇게 3개의 세션이 생김.</p>
<table>
<thead>
<tr>
<th align="left">터미널</th>
<th>로그인 방법</th>
<th align="center">현재 사용자</th>
<th align="right">홈 디렉토리</th>
<th>비고</th>
</tr>
</thead>
<tbody><tr>
<td align="left">터미널 A</td>
<td><code>ssh gut@172.24...</code></td>
<td align="center">gut</td>
<td align="right">/home/gut</td>
<td>SSH 세션</td>
</tr>
<tr>
<td align="left">터미널 B</td>
<td><code>ssh test@172.24...</code></td>
<td align="center">test</td>
<td align="right">/home/test</td>
<td>SSH 세션</td>
</tr>
<tr>
<td align="left">터미널 C</td>
<td><code>wsl</code></td>
<td align="center">gut</td>
<td align="right">/home/gut</td>
<td>로컬 WSL 세션</td>
</tr>
</tbody></table>
<p>4️⃣<strong><code>ssh</code>와 <code>su</code>의 차이</strong></p>
<table>
<thead>
<tr>
<th align="left">구분</th>
<th>SSH (<code>ssh</code>)</th>
<th>SU (<code>su</code>)</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>무엇을 바꾸는가</strong></td>
<td>“다른 컴퓨터”로 접속</td>
<td>“같은 컴퓨터 안에서” 사용자만 바꿈</td>
</tr>
<tr>
<td align="left"><strong>네트워크 필요</strong></td>
<td>필요 (IP/포트로 연결)</td>
<td>불필요</td>
</tr>
<tr>
<td align="left"><strong>디렉토리 이동</strong></td>
<td>원격 시스템의 홈 디렉토리로 이동</td>
<td>옵션에 따라 다름 (<code>su -</code>이면 이동)</td>
</tr>
<tr>
<td align="left"><strong>프로세스</strong></td>
<td>완전히 새 세션 (새 PID, 새 셸)</td>
<td>현재 터미널 내부에서 임시 신분 변경</td>
</tr>
<tr>
<td align="left"><strong>로그 남김</strong></td>
<td><code>/var/log/auth.log</code>에 SSH 로그인 기록</td>
<td>su/sudo 사용 흔적도 남지만 SSH보다는 내부 전환</td>
</tr>
</tbody></table>
<p>예시</p>
<pre><code># 현재 로컬 PC의 gut 계정에서,

ssh test@172.23.28.7
→ &quot;다른 컴퓨터의 test 계정&quot;으로 들어감 (IP 필요)

su test
→ &quot;같은 컴퓨터&quot; 안에서 test 계정으로 임시 전환</code></pre><p>5️⃣ <strong>“현재 디렉토리”는 사용자와 독립적인 상태</strong>
현재 디렉토리는 <strong>“지금 작업 중인 폴더 위치”</strong>를 말하고, 사용자와는 별개!</p>
<ul>
<li>pwd로 확인</li>
<li>cd로 이동</li>
</ul>
<p>예시</p>
<pre><code>cd /home/gut
su test</code></pre><p>이렇게 하면 여전히 /home/gut에 머무른 채 test 사용자로 바뀝니다.</p>
<p>반면에
<code>su - test</code></p>
<p>이렇게 하면 로그인 셸로 바뀌며 자동으로 /home/test로 이동합니다.</p>
<p>6️⃣ <strong>여러 터미널을 열었을 때의 관계</strong></p>
<p>터미널이 여러 개일 때는 각 터미널 = 각각 독립된 세션.
즉, 서로 영향을 주지 않는다.</p>
<p>예를 들어 두 창을 열어서:</p>
<p>창 1:
ssh <a href="mailto:gut@172.24.48.1">gut@172.24.48.1</a></p>
<p>창 2:
ssh <a href="mailto:test@172.24.48.1">test@172.24.48.1</a></p>
<p>→ 둘 다 같은 서버(같은 WSL)에 접속하지만,
서로 다른 로그인 세션이라 각자의 환경(whoami, pwd, ps)이 다르다.</p>
<p>심지어 <strong><em>같은 사용자로 접속해도 세션은 분리</em></strong>되어 있습니다:</p>
<pre><code>ssh gut@172.24.48.1    # 첫 번째 창
ssh gut@172.24.48.1    # 두 번째 창

→ 같은 계정이지만 프로세스·PID·환경변수·셸 인스턴스가 전부 다름.</code></pre><p>7️⃣ 요약</p>
<table>
<thead>
<tr>
<th align="left">개념</th>
<th>설명</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>터미널</strong></td>
<td>창, 입력창 그 자체</td>
<td>Ubuntu Terminal, CMD 창</td>
</tr>
<tr>
<td align="left"><strong>사용자(User)</strong></td>
<td>계정 이름</td>
<td>gut, test</td>
</tr>
<tr>
<td align="left"><strong>세션(Session)</strong></td>
<td>터미널+사용자 조합</td>
<td>gut@terminal1, test@terminal2</td>
</tr>
<tr>
<td align="left"><strong>SSH</strong></td>
<td>네트워크로 다른 머신/WSL에 접속</td>
<td>ssh test@IP</td>
</tr>
<tr>
<td align="left"><strong>SU</strong></td>
<td>같은 시스템 내 사용자 전환</td>
<td>su test / su - test</td>
</tr>
<tr>
<td align="left"><strong>현재 디렉토리</strong></td>
<td>지금 머물고 있는 폴더</td>
<td><code>pwd</code>로 확인</td>
</tr>
</tbody></table>
<p>(중요)
<strong>파일에 접근하기 위해서는,
그 파일이 위치한 모든 상위 디렉토리에 대한 ‘x(실행)’ 권한이 필요하다.</strong></p>
<p><strong>💡 추가 팁: 시각적으로 확인하기</strong></p>
<p>아래 명령으로 세션 상태를 명확히 볼 수 있습니다:</p>
<pre><code>whoami      # 현재 사용자
pwd         # 현재 디렉토리
hostname    # 접속한 시스템 이름 (WSL 이름 or 서버 이름)
echo $USER  # 환경변수에 저장된 사용자 이름</code></pre><p>이 네 가지를 항상 같이 쳐보세요.
그럼 “아, 지금은 누구로 어디 접속했고 어디 디렉토리에 있구나”가 한눈에 정리됩니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Docker + Flask 컨테이너 띄우기(트러블슈팅 포함)]]></title>
            <link>https://velog.io/@park-rgb125/Docker-Flask-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EB%9D%84%EC%9A%B0%EA%B8%B0%ED%8A%B8%EB%9F%AC%EB%B8%94%EC%8A%88%ED%8C%85-%ED%8F%AC%ED%95%A8</link>
            <guid>https://velog.io/@park-rgb125/Docker-Flask-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EB%9D%84%EC%9A%B0%EA%B8%B0%ED%8A%B8%EB%9F%AC%EB%B8%94%EC%8A%88%ED%8C%85-%ED%8F%AC%ED%95%A8</guid>
            <pubDate>Wed, 27 Aug 2025 04:45:01 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>👌목표:
flask를 사용한 flaskApp.py를 Dockerfile을 이용해 이미지화해서 컨테이너로 띄우기</p>
</blockquote>
<h3 id="1-flask-기본-코드">1. flask 기본 코드</h3>
<pre><code>from flask import Flask
from urllib.parse import quote as url_quote

app = Flask(__name__)

@app.route(&#39;/&#39;)
def hello():
    return &#39;hello flask world&#39;

if __name__ == &#39;__main__&#39;:
    app.run(host=&quot;0.0.0.0&quot;, port=3000)</code></pre><h3 id="2-dockerfile-작성">2. Dockerfile 작성</h3>
<pre><code>FROM python:3.9

COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt

COPY . /app

WORKDIR /app

CMD [ &quot;python&quot;,&quot;flaskApp.py&quot;]
</code></pre><p>이때 다른 dockerfile과는 다르게 requirements.txt라는걸 넣는데,
requirements.txt에는 이렇게 입력하자.</p>
<pre><code>Flask==2.0.3
Werkzeug==2.0.3</code></pre><p>이렇게 입력하는게 생각보다 중요하다(이유는 밑에서)</p>
<h3 id="3-docker-이미지만들고-컨테이너로-띄우기">3. docker 이미지만들고 컨테이너로 띄우기</h3>
<p><code>docker build -t flask-app .</code>
도커 이미지를 빌드한다.</p>
<p><code>docker run -d -p 3000:3000 flask-app</code>
도커 컨테이너를 실행한다.
-d : 백그라운드에서
-p : 포트포워딩
3000:3000 호스트포트:컨테이너포트
<img src="https://velog.velcdn.com/images/park-rgb125/post/36226fa1-4543-4c6a-8f8c-a69c1af0b554/image.png" alt="">
 잘 뜨는걸 확인할 수 있다.</p>
<h3 id="트러블슈팅">트러블슈팅</h3>
<h4 id="error1-importerror-cannot-import-name-url_quote-from-werkzeugurls">error1. ImportError: cannot import name &#39;url_quote&#39; from &#39;werkzeug.urls&#39;</h4>
<p> 진심 이거 때문에 1시간 정도 썼다.
 Flask와 Werkzeug의 버전 차이 문제로 인식하고 스택오버플로우랑 레딧 참고해서 <a href="https://stackoverflow.com/questions/77213053/why-did-flask-start-failing-with-importerror-cannot-import-name-url-quote-fr">링크</a>
 했는데도 계속 같은 오류가 나길래 gpt한테 물어봤더니
 <em>pip install flask==2.0.3 werkzeug==2.0.3로 다운그레이드 필요</em>
 라고 알려줬다.
근데? 이렇게 해도 안됬다.
그리고 <code>docker ps -a</code>해보니 세상에
<img src="https://velog.velcdn.com/images/park-rgb125/post/75878fc1-7ae3-4165-9b8a-ff8294806ee2/image.png" alt=""></p>
<p> <code>docker run</code>으로 생성된 컨테이너가 exited된채 한가득! 무한 제공 이벤트!^^</p>
<p> 그래서 <code>docker container prune</code>으로 exited된 컨테이너 다 지워줬다.</p>
<p> 다시 docker run해서 띄워보니 문제 해결!</p>
<h4 id="error2-err_empty_response">error2. ERR_EMPTY_RESPONSE</h4>
<p> 근데? 두번째 문제 발생.
 페이지가 로드가 안됬다. (뭐에요)</p>
<p> 이유를 찾아보니</p>
<blockquote>
<p>Flask를 기본 설정으로 실행하면 127.0.0.1(로컬 루프백) 에서만 바인딩돼서
도커 컨테이너 밖에서는 접근할 수 없다.</p>
</blockquote>
<p><code>app.run(host=&quot;0.0.0.0&quot;, port=3000)</code>
이렇게 수정해줬더니 잘 됬다.</p>
<h3 id="마무리">마무리</h3>
<p>다음에 계속~</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[python] 파이썬 달력 만들기]]></title>
            <link>https://velog.io/@park-rgb125/python-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EB%8B%AC%EB%A0%A5-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@park-rgb125/python-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EB%8B%AC%EB%A0%A5-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Thu, 07 Aug 2025 01:58:09 GMT</pubDate>
            <description><![CDATA[<p>파이썬 공부 겸 초급~마스터 까지의 미니 프로젝트를 하나씩 업로드해보려고한다.</p>
<p>첫번째는 <strong>달력 만들기.</strong></p>
<p>정말 nice 하게도 이미 module이 있어서 그걸 import해서 사용하기만 하면 되는게 좋았다.</p>
<h4 id="1-code">1. code</h4>
<pre><code class="language-import">
a = input(&quot;input Year:&quot;)

a = int(a)
print(calendar.calendar(a))</code></pre>
<h4 id="2-trouble-shooting-not-supported-between-instances-of-int-and-str">2. Trouble Shooting (not supported between instances of &#39;int&#39; and &#39;str&#39;)</h4>
<p>비교하려는 객체의 type이 맞지 않아 발생한 error라고 한다.</p>
<p>그래서 <code>type()</code>으로 변수 a의 타입을 확인하니 string이었다.
<code>input()</code>함수로 받는 값의 type은 기본적으로 string.
type casting을 해주면 되는데 정말 간단했다.
<code>a= int(a)</code>이렇게만 해주면 문제 없이 type casting(형 변환)이 된다.</p>
<h4 id="3-결과-사진">3. 결과 사진</h4>
<p><img src="https://velog.velcdn.com/images/park-rgb125/post/2a9932ec-2031-44e0-961d-bbfc133a8d35/image.png" alt="">
(중간에 Jul, Aug랑 Sep안보이는데 width가 좁아서 그런거다:)</p>
<h4 id="4-a-bit-more-advanced조금-더-발전된">4. A bit more advanced(조금 더 발전된)</h4>
<p>[A bit more advanced, 주로 기술이나 기능이 발전된 대에 쓴다고 한다]</p>
<p>심화에 <em>특정 달만 출력</em> 하는게 있어서 이걸 <strong>if문</strong>을 이용해 적용했다.</p>
<pre><code># 연도 전체 or 특정 달 출력
import calendar

choose = input(&quot;&#39;entire year&#39; or &#39;specific month&#39;: &quot;)
year = int(input(&quot;input Year: &quot;))

if (choose == &#39;entire year&#39;):
    print(calendar.calendar(year))
elif(choose == &#39;specific month&#39;):
    month = int(input(&quot;input month: &quot;))
    print(calendar.prmonth(year,month))
else:
    print(&quot;error&quot;)</code></pre><h4 id="5-finish마무리">5. finish(마무리)</h4>
<p>예전에 배운걸 간단한 것 부터 복습하니 재밌었다.
띄어쓰기 잘 지켜야하는건 약간 shell script를 닮은거같기도 하고..
나는 처음부터 이론 먼저 보고 공부하는 스타일보단
냅다 실습하면서 필요한 부분만 알아가는 스타일이
더 맞는 것 같기도 하다.
다음 미니 프로젝트는 [숫자 맞추기 게임]!
조만간 돌아옵니다 :)</p>
<blockquote>
<p>참고 블로그</p>
</blockquote>
<ul>
<li><a href="https://chae-developer.tistory.com/23">Python : calendar - 달력 관련 라이브러리</a></li>
<li><a href="https://blockdmask.tistory.com/432">[python] 파이썬 타입 변환, 형변환 (float, int, str, chr, bool)</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Dockerfile에서 WORKDIR은 뭘할까?]]></title>
            <link>https://velog.io/@park-rgb125/Dockerfile%EC%97%90%EC%84%9C-WORKDIR%EC%9D%80-%EB%AD%98%ED%95%A0%EA%B9%8C</link>
            <guid>https://velog.io/@park-rgb125/Dockerfile%EC%97%90%EC%84%9C-WORKDIR%EC%9D%80-%EB%AD%98%ED%95%A0%EA%B9%8C</guid>
            <pubDate>Fri, 01 Aug 2025 01:53:00 GMT</pubDate>
            <description><![CDATA[<h2 id="오늘의-질문은">오늘의 질문은?</h2>
<p>Dockerfile에서 WORKDIR은 어떤 역할을 할까?</p>
<h3 id="dockerfile">Dockerfile?</h3>
<p>docker image를 생성하기 위해 작성해야하는 파일이다.</p>
<h3 id="문법은-어떻게-되요">문법은 어떻게 되요?</h3>
<p>기본적으로 이렇게 쓴다. (node.js 기준)</p>
<pre><code>FROM node:20

WORKDIR /app/start

COPY package*.json ./

RUN npm install
COPY . .
EXPOSE 3000
CMD [&quot;node&quot;,&quot;app.js&quot;]</code></pre><p>각 명령어에 대한 설명은 이 블로그를 참고하자.</p>
<ul>
<li><a href="https://velog.io/@park-rgb125/Docker%EB%A1%9C-Node.js-%EC%84%9C%EB%B2%84-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EB%A7%8C%EB%93%A4%EA%B8%B0">Docker로 Node.js 서버 컨테이너 만들기</a></li>
</ul>
<h3 id="1-그래서-workdir이-왜요">1. 그래서 WORKDIR이 왜요?</h3>
<p>구글링으로 WORKDIR을 검색했을때 리눅스 명령어 cd와 비슷한 명령어래서 
&quot;아 그럼 실행하면 실제로 현재 위치가 바뀌거나 이동되나?&quot; 싶었다.
그래서 docker run으로 실행해봤는데도 바뀌는게 없었다.
뭐지? 싶어서 GPT에게 물어봤다.</p>
<blockquote>
<p>📌 알아둬야 할 핵심 개념 3가지
<strong>1. 컨테이너 내부 디렉토리 경로 설정</strong></p>
</blockquote>
<ul>
<li>Dockerfile 내 이후의 명령어(RUN, CMD, COPY, ADD)들이 어느 디렉토리 기준으로 실행될지를 정함.<blockquote>
</blockquote>
예시:<pre><code>WORKDIR /app/start
COPY . .  # =&gt; 컨테이너 내부의 /app/start 기준으로 현재 폴더가 복사됨</code></pre></li>
<li><em>2. 디렉토리가 없다면 자동 생성됨*</em></li>
<li>/app/start가 존재하지 않아도 자동으로 만들어짐. 단, 이건 컨테이너 이미지 안에서만 보임.</li>
<li>호스트(EC2, PC) 파일시스템에는 아무 변화도 없음. 그래서 밖에서 보면 아무것도 안 생긴 것처럼 보임.<blockquote>
</blockquote>
</li>
<li><em>3. 호스트랑 컨테이너는 다른 세계*</em></li>
<li>Dockerfile이나 컨테이너 내부에서 설정한 디렉토리는 호스트에 영향을 주지 않음.</li>
<li>Docker는 격리된 가상 환경을 만들기 때문이야. 외부에 /app/start가 생기지 않는 게 맞는 동작이야.</li>
</ul>
<h3 id="첫번째-요약">첫번째 요약</h3>
<p><img src="https://velog.velcdn.com/images/park-rgb125/post/8b531ee4-cf77-4a87-818b-160961368f12/image.png" alt=""></p>
<h3 id="그럼-직접-컨테이너-내부로-들어가볼까">그럼 직접 컨테이너 내부로 들어가볼까?</h3>
<p>좋은 생각이다. 들어가보자.</p>
<p><code>docker run -it --rm your-image-name sh</code>혹은
<code>docker run -it --rm your-container-name sh</code>로 컨테이너 내부에 들어갔다.</p>
<p>ls를 해보면?
<img src="https://velog.velcdn.com/images/park-rgb125/post/169b4ff0-11d6-4d40-9845-20b4a4d062bc/image.png" alt="">
뭔가 뜨는거같긴 하고?</p>
<h3 id="2-근데-왜-appstart가-안뜨죠">2. 근데 왜 /app/start가 안뜨죠?</h3>
<p>이때 가장 먼저 해야할 것은 <code>pwd</code>로 현재 위치 확인하기다.
<img src="https://velog.velcdn.com/images/park-rgb125/post/b56b87f8-1ae6-413e-8c88-9f29883b7c25/image.png" alt=""></p>
<p>pwd로 확인했더니 현재 경로가 <strong>/app/start</strong>임을 확인할 수 있었다.</p>
<h3 id="3-왜-프롬포트에-경로-명시가-안되지">3. 왜 프롬포트에 경로 명시가 안되지?</h3>
<p>이건 docker container 내부로 <code>sh</code>로 들어가면 이렇게 나온다고 한다.</p>
<p>sh는 <strong>PS1(프롬프트 표시 형식)</strong>이 단순해서 경로를 표시하지 않음.
<em>예: 그냥 # 만 나옴</em></p>
<p>흔히 우분투에서 보는 cli처럼 경로가 보이게 하려면 <code>bash</code>를 설치해주고 bash로 들어가면 된다.</p>
<pre><code>apt update &amp;&amp; apt install -y bash
bash</code></pre><p><img src="https://velog.velcdn.com/images/park-rgb125/post/a3a4e6cb-f2da-40b5-bc25-22d8dcd0d6fc/image.png" alt=""></p>
<p>경로가 잘 보이는 걸 확인할 수 있다!</p>
<h3 id="4-그냥-cd-치고-ls--al해도-아무것도-안나와요">4. 그냥 cd 치고 ls -al해도 아무것도 안나와요..</h3>
<p>왜이럴까 싶었다. 이유는 단순했다.
<strong>아무것도 안 넣어서요.</strong></p>
<table>
<thead>
<tr>
<th>디렉토리</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>/root</code></td>
<td>root 유저의 홈 디렉토리. 기본적으로 비어 있음.</td>
</tr>
<tr>
<td><code>/app/start</code></td>
<td>Dockerfile에서 <code>WORKDIR</code>와 <code>COPY</code>를 통해 지정된 작업 디렉토리.</td>
</tr>
<tr>
<td><code>ls -l</code> 결과가 0</td>
<td>그 디렉토리에 파일이 없다는 뜻. Docker는 필요 없는 건 만들지 않아.</td>
</tr>
</tbody></table>
<h3 id="마무리">마무리</h3>
<p>dockerfile쓰면서 이해가 안됬던 부분이라 실습하면서 이해되서 재밌었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[15단계로 배우는 도커와 쿠버네티스 요약 #1]]></title>
            <link>https://velog.io/@park-rgb125/15%EB%8B%A8%EA%B3%84%EB%A1%9C-%EB%B0%B0%EC%9A%B0%EB%8A%94-%EB%8F%84%EC%BB%A4%EC%99%80-%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EC%9A%94%EC%95%BD-1</link>
            <guid>https://velog.io/@park-rgb125/15%EB%8B%A8%EA%B3%84%EB%A1%9C-%EB%B0%B0%EC%9A%B0%EB%8A%94-%EB%8F%84%EC%BB%A4%EC%99%80-%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EC%9A%94%EC%95%BD-1</guid>
            <pubDate>Fri, 25 Jul 2025 06:21:16 GMT</pubDate>
            <description><![CDATA[<p>이 내용은 책 &lt;15단계로 배우는 도커와 쿠버네티스&gt; 중
약 ~77p까지를 요약한 것이다.</p>
<p>쿠버네티스: 컨테이너화 된 애플리케이션을 효율적으로 배포하고 운영하기 위해 설계된 오픈소스 플랫폼</p>
<p><strong>컨테이너를 쓰는 이유1</strong>: 애플리케이션 실행에 필요한 라이브러리나 운영체제 패키지등을 모두 담아서 불변의 실행환경을 만듬. 이렇게 하면 개발자든간, 테스트와 운영 환경 간의 차이를 없앨 수 있음.</p>
<p><strong>컨테이너를 쓰는 이유2</strong></p>
<ol>
<li>인프라의 사용률 향상</li>
<li>빠른 기동 시간</li>
<li>불변 실행 환경</li>
</ol>
<p>쿠버네티스는 크게 4가지의 기능 제공</p>
<ul>
<li>배포 계획에 맞춰 애플리케이션 신속 배포 가능</li>
<li>가동 중인 애플리케이션을 스케일 업/다운 할수 있음</li>
<li>새로운 버전의 애플리케이션을 무정지 업그레이드 가능</li>
<li>하드웨어 가동률을 높여 자원 낭비를 줄임</li>
</ul>
<p>쿠버네티스의 아키텍처: 클러스터 관리를 담당하는 마스터와 컨테이너화된 애플리케이션을 실제로 실행하는 노드 단 두 종류의 서버로 구성</p>
<p>가상 서버(ex VMWare)와 비교했을때 컨테이너는 경량, 빠른 기동, 이식성의 장점을 가진다.</p>
<p>도커는 도커 데몬 서버와 클라이언트인 도커 커맨드, 그리고 이미지의 보관소인 레지스트리로 구성된다.</p>
<ul>
<li><p>(1) 도커 데몬: 클라이언트인 도커 커맨드의 명령을 받아들여서 도커 오브젝트인 이미지, 컨테이너, 볼륨, 네트워크등을 관리</p>
</li>
<li><p>(2) 도커 클라이언트: 컨테이너를 조작하는 커맨드라인유저 인터페이스로 도커 데몬의 클라이언트다. 도커 api를 사용하여 도커 데몬의 요청을 보낸다.</p>
</li>
<li><p>(3) 이미지: 읽기 전용인 컨테이너의 템플릿, 컨테이너를 기동하기 위한 실행 파일과 설정 파일의 묶음,컨테이너를 실행하면 이미지에 담긴 미들웨어나 애플리케이션이 설정에 따라 기동.</p>
</li>
<li><p>(4) 컨테이너: 하나의 프로세스, 정확히는 &#39;실행 가능한 이미지의 인스턴스&#39;</p>
</li>
<li><p>(5) 도커 레지스트리: 컨테이너의 이미지가 보관되는 곳</p>
</li>
</ul>
<p><em>레지스트리와 리포지터리의 차이</em>
레지스티리: 리포지터리를 여러개 가지는 보관 서비스
리포지터리: 하나의 이미지에 대해 태그를 사요하여 다양한 출시 버전을 함께 보관</p>
<p>퍼블릭 레지스트리: 누구나 이용할 수 있도록 공개된 레지스트리
클라우드 레지스트리: 퍼블릭 클라우드가 제공하는 레지스트리 서비스
비공개 레지스트리: 회사나 팀 전용으로 레지스트리를 구축하여 운영하는 경우에 해당</p>
<p>쿠버네티스에서 컨테이너가 동작할 때까지의 흐름을 설명하면 다음과 같음</p>
<ol>
<li>docker build로 이미지를 빌드한다.</li>
<li>docker push로 이미지를 레지스트리에 등록한다.</li>
<li>kubectl커맨드로 매니페스트에 기재한 오브젝트들의 생성을 요청한다</li>
<li>매니페스트에 기재된 리포지터리로 부터 컨테이너의 이미지를 다운로드한다.</li>
<li>컨테이너를 파드 위에서 기동한다.</li>
</ol>
<p><strong>쿠버네티스는 도커를 컨테이너의 런타임 환경으로 사용함, 그래서 쿠버네티스를 설치할때 제일 먼저 도커를 설치해야함</strong></p>
<ul>
<li><p>오브젝트: k8s클러스터 내부의 엔티티로서, 파드, 컨트롤러, 서비스등의 인스턴스를 의미, 지정된 상태가 유지되도록 쿠버네티스에 의해 제어</p>
</li>
<li><p>워크로드: 오브젝트의 카테고리를 나타내는 용어로 컨테이너와 파드, 그리고 컨트롤러의 그룹을 의미</p>
</li>
<li><p>컨테이너: 쿠버네티스에서는 컨테이너를 독자적으로 실행하는 것이 불가능, 반드시 파드 내에서 실행해야한다.</p>
</li>
<li><p><strong>파드(pod): 컨테이너를 실행하기 위한 오브젝트. 한개 혹은 여러개의 컨테이너를 담을 수있다.</strong></p>
</li>
</ul>
<pre><code>컨트롤러: 파드의 실행을 제어하는 오브젝트
설정: 컨테이너 내 애플리케이션의 설정값이나 비밀번호 등의 정보된 배포된 네임스페이스로부터 취득하는 것이 좋음
서비스: 파드와 클라이언트를 연결하는 역할 수행
스토리지: 파드나 컨테이너는 실행 시에만 존재하는 일시적인 존재이기 때문에 중요한 데이터를 컨테이너의 파일 시스템에 저장해서는 안된다. 
데이터를 잃지 않기 위해서는 퍼시스턴트 볼륨을 사용하여 전원이 꺼져도 데이터가 유지되는 스토리지 시스템에 데이터를 저장해야한다. </code></pre><blockquote>
</blockquote>
<p><strong>파드(Pod)</strong>는 하나의 목적을 위해 만들어진 컨테이너를 부품처럼 조합할 수 있도록 설계됨</p>
<ol>
<li>파드 내부의 컨테이너들은 파드의 ip주소와 포트번호를 공유</li>
<li>파드의 내부 컨테이너들은 localhost로 서로 통신 가능</li>
<li>파드의 내부 컨테이너들은 system V 프로세스 통신이나 POSIX 공유 메모리를 사용하여 서로 통신 가능</li>
<li>파드의 내부 컨테이너들은 파드의 볼륨을 마운트하여 파일 시스템을 공유할 수 있다.
파드의 IP주소는 기동 시 부여(고정X),종료시 회수되어 다른 파드가 기동할때 사용됨. 파드에 요청을 보내고 싶은 경우에는 반드시 서비스를 사용해야함</li>
</ol>
<p>서비스는 클라이언트로부터 요청을 지정한 파드의 그룹에 부하분산하며 전송하는 역할을 담당</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[EC2에 Nginx /etc/nginx/sites-available 설정 중 오류들 해결]]></title>
            <link>https://velog.io/@park-rgb125/EC2%EC%97%90-Nginx-etcnginxsites-available-%EC%84%A4%EC%A0%95-%EC%A4%91-%EC%98%A4%EB%A5%98%EB%93%A4-%ED%95%B4%EA%B2%B0</link>
            <guid>https://velog.io/@park-rgb125/EC2%EC%97%90-Nginx-etcnginxsites-available-%EC%84%A4%EC%A0%95-%EC%A4%91-%EC%98%A4%EB%A5%98%EB%93%A4-%ED%95%B4%EA%B2%B0</guid>
            <pubDate>Thu, 24 Jul 2025 09:27:40 GMT</pubDate>
            <description><![CDATA[<h2 id="목표">목표</h2>
<p>EC2에 Nginx 설정
/etc/nginx/sites-available 설정법
프록시 설정: 80 → 3000번 포트(예시)</p>
<p><em>참고사항</em>
<strong>오늘 실습 전체의 흐름은 참고 첫번째 링크에 다 나와있다!</strong>
원래 nginx랑 certbot으로 https 적용을 한 포스트 안에 다하려고 했는데
아차차! nginx 설정만 해도 오류가 많아 나누게 되었다</p>
<p>실습을 진행하며 생긴 오류 순서대로 번호를 부여했고, 내용이 다 이어져있어 뒤에 내용까지 보면 이해가 될거라 생각한다.</p>
<p>이번 포스트는 약 2시간 넘게 오류만 고친 사람의 기록이다
(정신 찵이고 포스트 찵여.)</p>
<p>이번 실습은 GPT를 쓰지 않고 진행했다.(그래서 마지막 부분이 안풀리고 있다)</p>
<h3 id="1-could-not-get-lock-varlibdpkglock-frontend-it-is-held-by-process-884-unattended-upgr">1. Could not get lock /var/lib/dpkg/lock-frontend. It is held by process 884 (unattended-upgr)</h3>
<p><strong><code>sudo apt-get install nginx</code>를 시도하던 중 발생한 오류</strong></p>
<p>구글링 → process 884에 lock이 걸려있다. 이걸 지우면 해결. → <code>ps -ef</code>로 확인 →
<img src="https://velog.velcdn.com/images/park-rgb125/post/9c65120c-ec02-4c87-9593-387c18ecdb2c/image.png" alt="">
?
884번 프로세스는 없었음. 차분히 다시 <code>sudo apt-get install nginx</code>를 입력하니 다른 오류가 떴다.</p>
<h3 id="2-you-dont-have-enough-free-space-in-varcacheaptarchives">2. You don&#39;t have enough free space in /var/cache/apt/archives/.</h3>
<p><strong>공간이 부족하다!</strong></p>
<p>구글링 → <code>df -h</code>로 상태를 확인해보니 /dev/root의 용량을 100% 전부 다 썼음을 확인 할 수 있었다.
<img src="https://velog.velcdn.com/images/park-rgb125/post/1bea75dc-80ed-4e1e-b0c7-016d46882d04/image.png" alt=""></p>
<p>EC2 인스턴스 볼륨의 용량 부족문제 임을 확인 하고 볼륨 수정하여 8 GiB → 32GiB로 늘려줬다. (내 요금 ^_ㅜ)
볼륨만 수정했다고 끝이 아니다. 사용하던 파티션을 늘린 용량만큼 확장 해줘야한다. <code>lsblk</code>로 디스크 상태를 확인해보면,
<img src="https://velog.velcdn.com/images/park-rgb125/post/e3e69182-d844-46f6-86a6-5cb729154055/image.png" alt="">
밑에 있는 xvda1이 변경해줘야할 파티션이다. → xvda1 파티션을 확장해보자</p>
<p>growpart라는 파티션을 확장해 주는 명령어를 사용한다.</p>
<pre><code>#sudo growpart /dev/파티션명 파티션번호
sudo growpart /dev/xvda 1</code></pre><h3 id="3-1-cannot-create-directory-tmpgrowpart908-no-space-left-on-devicefailed-failed-to-make-temp-dir">3-1. cannot create directory ‘/tmp/growpart.908’: No space left on deviceFAILED: failed to make temp dir</h3>
<p>얘도 뭔가 <strong>저장공간</strong>의 문제.</p>
<blockquote>
<p>블록 디바이스에 남은 공간 없음 오류를 방지하려면 임시 파일 시스템 tmpfs를 /tmp 탑재 지점에 탑재합니다. 그러면 /tmp에 탑재된 10M tmpfs가 생성됩니다.</p>
</blockquote>
<p><code>sudo mount -o size=10M,rw,nodev,nosuid -t tmpfs tmpfs /tmp</code>
이렇게 입력 후 다시 <code>sudo growpart /dev/xvda1</code> 해주면 되는 줄 알았다.</p>
<h3 id="3-2-gpt-pmbr-size-mismatch-16777215--67108863-will-be-corrected-by-write-the-backup-gpt-table-is-not-on-the-end-of-the-device">3-2. GPT PMBR size mismatch (16777215 != 67108863) will be corrected by write. The backup GPT table is not on the end of the device.</h3>
<p><code>sudo sfdisk --list /dev/xvda</code>로 파티션 테이블을 확인하려 했는데 발생한 오류.</p>
<p>root로 전환 후 <code>parted</code>라는 명령어를 사용하여 GPT 정보를 수정하여 오류를 해결했다.</p>
<p><img src="https://velog.velcdn.com/images/park-rgb125/post/ca5095d8-8665-4194-a1c3-f20218494d96/image.png" alt=""></p>
<p>이렇게 입력해준 후 다시 fdisk를 해보면 에러메시지가 사라져있다.</p>
<p><em>(중간에 도커 이미지/컨테이너 크기땜에 부족한 줄 알고 지우려다가 도커 데몬 이 실행이 안되서 헤맨건 안비밀.. 여전히 모르겠다)</em></p>
<p>→ 이제 파일 시스템에 변경된 파티션 크기를 적용하자
<code>sudo resize2fs /dev/xvda1</code> 입력 후 다시 <code>df -h</code>로 확인해보면
<img src="https://velog.velcdn.com/images/park-rgb125/post/25c566cf-9ca9-493a-8b27-005da8acd077/image.png" alt="">
짠
사용공간이 늘어났음을 확인했다!</p>
<p>이제 다시 nginx 설치로 돌아가서,</p>
<p><code>sudo apt-get install nginx</code> 실행
근데 실행 중 failed가 자꾸 뜨고 안되길래 <code>apt-get update</code>해줬는데도 계속 오류가 떴다.</p>
<h3 id="4-some-index-files-failed-to-download-they-have-been-ignored-or-old-ones-used-instead">4. Some index files failed to download. They have been ignored, or old ones used instead.</h3>
<p>DNS 서버 확인 후 수정하라길래 그대로 했다.
<code>vi /etc/resolv.conf</code>에 <code>nameserver 8.8.8.8</code>을 추가하여 저장.</p>
<p>다시 <code>apt-get update</code>시도하니 업데이트가 잘 됬다.</p>
<h3 id="5-sudo-unable-to-resolve-host-ip주소-name-or-service-not-known">5. sudo: unable to resolve host ip주소: Name or service not known</h3>
<p>뭔가를 알 수 없다..?</p>
<p>/etc/hostname과 /etc/hosts 두 파일의 내용이 달라서 그렇다고 한다.
나는 /etc/hostname에 있는 ip주소를 /etc/hosts에 그대로 넣어줬다.</p>
<p>여기까지하면 그제야 <code>sudo apt-get install nginx</code>을 할 수 있다.</p>
<p><code>service nginx start</code>로 nginx 실행 후 <code>http://localhost:80</code>으로 접속(http가 80번 포트를 의미하기에 생략 가능)</p>
<p>/etc/nginx안에서 주요하게 봐야할 것</p>
<blockquote>
<ul>
<li>site-available/: nginx에서 관리되는 호스트 정보</li>
</ul>
</blockquote>
<ul>
<li>site-enabled/: site-abailable/에서 만든 사이트를 site-enabled/에 추가해야 활성화 됨. site-available/에 추가한 사이트를 site-enabled/에 심볼릭 링크하여 사이트를 활성화 할 수 있습니다.</li>
</ul>
<p>nginx 설치시 기본으로 설치된 설정 파일 제거 → 1. 서버 생성(참고 첫번째 링크에 있음) → 2. site-available 사이트 추가</p>
<p><code>vim /etc/nginx/site-available/server1</code>에 아래 코드 입력 후 저장</p>
<pre><code>server {
        listen 80 ;
        listen [::]:80;

        root /home/server1;
        server_name server1 www.server1.com;

        access_log /var/log/nginx/server1.access.log;
        error_log /var/log/nginx/server1.error.log;

        location /server1/ {
                proxy_pass http://127.0.0.1:3000/;
        }
}</code></pre><ol start="3">
<li>site-enabled 사이트 활성화</li>
</ol>
<p>마지막으로 site-available에 추가한 사이트를 site-enabled에 심볼릭 링크로 연결</p>
<p><code>ln -s /etc/nginx/sites-available/server1 /etc/nginx/sites-enabled/server1</code></p>
<pre><code class="language-nginx">
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful</code></pre>
<p><code>nginx -s reload</code>하면 <code>curl http://localhost:3000</code>시 응답이 잘 오는 것 처럼 웹사이트에도 응답이 떠야하는데 난 계속 <strong>404 NOT FOUND</strong>만 떴다.</p>
<h3 id="6-command-nvm-not-found">6. Command &#39;nvm&#39; not found</h3>
<p>shell 에서 nvm을 실행시킬 때 필요한 환경변수가 제대로 세팅이 되어있지 않아서 생긴 문제라고 한다.</p>
<p>자신이 쓰는 쉘에 따라 방법이 조금 다르다. 난 bash를 쓴다.</p>
<p>bash 인 경우: <code>vi ~/.bashrc</code>
zsh 인 경우: <code>vi ~/.zshrc</code></p>
<p>키보드 i 를 누르고 아래 코드를 입력하고 ESC 키를 누르고 :wq! 입력</p>
<pre><code>export NVM_DIR=&quot;$HOME/.nvm&quot;
[ -s &quot;$NVM_DIR/nvm.sh&quot; ] &amp;&amp; \. &quot;$NVM_DIR/nvm.sh&quot;  # This loads nvm
[ -s &quot;$NVM_DIR/bash_completion&quot; ] &amp;&amp; \. &quot;$NVM_DIR/bash_completion&quot;  # This loads nvm bash_completion</code></pre><p>그리고 터미널을 재로그인하거나, source 명령어로 스크립트 실행</p>
<p><code>source ~/.bashrc</code> or <code>source ~/.zshrc</code></p>
<h3 id="7--had-too-many-unstable-restarts-16-stopped-errored">7.  had too many unstable restarts (16). Stopped. &quot;errored&quot;</h3>
<p>pm2 log 찍으면 계속 나오는 에러메시지
<img src="https://velog.velcdn.com/images/park-rgb125/post/aade1c85-37b1-47cb-8d5b-e5c3de67f079/image.png" alt=""></p>
<p>뭘 하지도 않았는데 reload가 저렇게 많이 된다고? (당황)</p>
<p>찾아보니 오래된 버전의 라이브러리가 있다면 이럴 수도 있대서 지우고 다시 설치했다.</p>
<pre><code>pm2 kill
rm -rf node_modules
npm i
authbind --deep pm2 start app.js</code></pre><h3 id="여전히-해결이-안된-부분">여전히 해결이 안된 부분</h3>
<ul>
<li><p>localhost/server1로 접속한 웹사이트에 응답이 뜨지 않고 계속 <strong>404 NOT FOUND</strong>로 뜬다. 아무리 검색해도 안나와서 이건 GPT한테 후에 물어보고 고친 후 다시 포스트 업로드 해야겠음.</p>
</li>
<li><p>여전히 도커 데몬이 실행되지 않는다... 으악!</p>
</li>
<li><p>그리고 pm2 상태가 계속 바뀐다. 건들지 않아도 어쩔땐 online이다가 어쩔땐 errored였다가..</p>
</li>
</ul>
<p>쓰다보니 /etc/nginx/sites-available 보단 그냥 오류 나열에 가까워지게 된거같다.. 정리해서 쓰는게 너무 어렵다 일단 쓰고 봐야한다.</p>
<h3 id="궁금한-점">궁금한 점</h3>
<ul>
<li>분명 npm init으로 초기화를 했는데 왜 ./bin/www가 안생겼지?</li>
<li>내가 재접속을 안했는데 왜 reload 수가 늘어나지?</li>
<li>파티션과 디스크 쪽의 정확한 개념들과 자세한 설명 필요</li>
</ul>
<h4 id="이어서-해야할-것">이어서 해야할 것</h4>
<ul>
<li>certbot으로 https 적용</li>
<li>docker demon 안돌아가는 거 고치기</li>
<li>도커/쿠버네티스 책 첫파트 블로그 업로드</li>
<li>개인 도메인 복구</li>
<li>python 공부 → 블로그 업로드</li>
</ul>
<blockquote>
<p>참고</p>
</blockquote>
<ul>
<li><a href="https://blog.naver.com/pjt3591oo/222242046633">[nginx] nginx 시작하기 1/3 - 기초편
</a></li>
<li><a href="https://greensul.tistory.com/160">[ubuntu] Could not get lock /var/lib/apt/lists/lock. Error
</a></li>
<li><a href="https://modimodi.tistory.com/71">EC2 &#39;No space left on device&#39; 파일 시스템에 남은 공간 없을 때 EBS 볼륨 크기 늘리기</a></li>
<li><a href="https://hiseon.me/linux/linux-disk-error-fix/">리눅스 디스크 에러 복구</a></li>
<li><a href="https://while-programming.tistory.com/145">EC2 용량 늘리기</a></li>
<li><a href="https://velog.io/@ssssujini99/EC2ServerDocker-EC2-%EC%9A%A9%EB%9F%89-%EA%BD%89-%EC%B0%BC%EC%9D%84-%EB%95%8C-%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95-No-space-left-on-device-%ED%95%B4%EA%B2%B0">[EC2][Server][Docker] EC2 용량 꽉 찼을 때 해결 / No space left on device 해결</a></li>
<li><a href="https://eteo.tistory.com/882">[Ubuntu ] Some index files failed to download. 에러 해결</a></li>
<li><a href="https://jwcheong0420.github.io/2018/05/07/ubuntu-unable-to-resolve-host/">unable to resolve host 에러 해결 방법</a></li>
<li><a href="https://slow-run.tistory.com/34">bash: nvm: command not found 에러 해결</a></li>
<li><a href="https://holystory-dev.com/173">(에러핸들링) (AWS EC2) had too many unstable restarts</a></li>
<li><a href="https://yzlosmik.tistory.com/127">[ERROR] command not found: nvm</a></li>
<li><a href="https://sailer.tistory.com/entry/Nodejs-PM2-%EA%B0%84%EB%8B%A8-%EC%A0%95%EB%A6%AC">Node.js PM2 간단 정리</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[EC2 운영 자동화 스크립트+crontab 설정]]></title>
            <link>https://velog.io/@park-rgb125/EC2-%EC%9A%B4%EC%98%81-%EC%9E%90%EB%8F%99%ED%99%94-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8crontab-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@park-rgb125/EC2-%EC%9A%B4%EC%98%81-%EC%9E%90%EB%8F%99%ED%99%94-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8crontab-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Wed, 23 Jul 2025 03:43:49 GMT</pubDate>
            <description><![CDATA[<h3 id="목표">목표</h3>
<p>EC2에서 <code>docker-compose down &amp;&amp; docker-compose up -d</code>
를 자동으로 돌리는 bash script 만들기(crontab 이용)</p>
<h3 id="실습-전-궁금했던-것">실습 전 궁금했던 것</h3>
<p>전 포스트들을 보면 알 수 있듯 난 이미 deploy.yml에 docker-compose를 재시작하도록 코드를 작성해두었는데, 왜 EC2안에서 한번 더 해야할까? 라는 궁금증이 생겨 GPT에게 물어봤더니,</p>
<blockquote>
<p>GPT 답변:
그 deploy.yml이 GitHub Actions Workflow 파일이라면,
그건 <strong>&quot;내가 GitHub에 push할 때 EC2에 git pull하고, docker-compose 재시작하는 것&quot;</strong>을 자동화한 거지.
근데 그건 &quot;코드가 바뀌었을 때만&quot; 실행되잖아.
예를 들면: 코드 변경 → push → GitHub Actions → EC2에서 재시작
그런데 만약:
코드 변경 없이, EC2에서 컨테이너가 죽었다거나,
서버가 오래 켜져 있어서 리소스가 부족하다거나,
컨테이너가 이유 없이 멈췄다거나
이런 경우에도 주기적으로 EC2를 재시작하거나,
docker-compose를 재시작하게 하면
서버의 안정성이 높아지는 거야.</p>
</blockquote>
<p>요약하면 <strong>서버의 리소스 회수, 메모리 누수 방지, 컨테이너 안정화</strong>를 위해 자동화 스크립트를 작성해주는 게 좋다고 한다.</p>
<p>난 내 EC2를 항상 켜두진 않기 때문에(요금이 ^_ㅜ)
굳이 필요할까? 싶었지만 알아두면 좋을 것 같아 실습 해봤다.</p>
<h3 id="1-docker-compose-재시작-스크립트-작성하기">1. docker-compose 재시작 스크립트 작성하기</h3>
<p>간단하다
스크립트 파일을 하나 만들어 docker-compose down과 docker-compose up -d를 적어주면 된다</p>
<pre><code>#!/bin/bash

echo &quot;[$(date)] Restarting Docker containers...&quot; &gt;&gt; /home/ubuntu/restart.log

docker-compose down
docker-compose up -d

echo &quot;[$(date)] Restart complete.&quot; &gt;&gt; /home/ubuntu/restart.log

</code></pre><p>GPT가 로그 파일도 남겨두는게 좋다고 추천해줘서 로그파일을 남길 수 있도록 코드를 추가했다.</p>
<h3 id="2-crontab-작성-후-실행되는-지-확인하기">2. crontab 작성 후 실행되는 지 확인하기</h3>
<p>여기서 잠깐, crontab이 뭘까?</p>
<blockquote>
<p>윈도우의 스케쥴러와 비슷, &quot;특정 시간에 특정 작업&quot;을 해야할 때 crontab으로 작업을 설정해줄 수 있다.</p>
</blockquote>
<ul>
<li><code>crontab -e</code>를 입력 하면 텍스트 에디터를 고르는 창이 뜰텐데 nano로 선택하는게 편하다 :)</li>
<li>참고로 <code>crontab -l</code>을 하면 crontab에 어떤 내용이 들어있는지 알 수 있다.</li>
<li>주기 결정
<img src="https://velog.velcdn.com/images/park-rgb125/post/5811a6a0-1e3a-4836-8003-69f2b7ceb76b/image.png" alt=""> 
다섯개의 <em>을 모두 입력하면 *</em>매 분**마다 실행한다.<pre><code>#매주 금요일 오전 5시 45분에 test.sh 를 실행
45 5 * * 5 /home/script/test.sh
</code></pre></li>
</ul>
<p>#매일 매시간 0분, 20분, 40분에 test.sh 를 실행
0,20,40 * * * * /home/script/test.sh</p>
<p>#매주 수요일 오전 11시에 restart.sh를 실행
0 11 * * 3 /home/ubuntu/restart.sh</p>
<pre><code>원하는 주기 대로 시간을 입력해주고 저장하면 정해둔 시간마다 작업이 실행된다.

### UTC -&gt; KST로 바꾸기
crontab 실행 전 bash restart.sh로 실행 시켰을때 찍힌 로고의 date가 UTC 기준으로 떠서 정확한 시간이 맞는지 확인하기 쉽지 않았다.

바로 구글 검색을 해 ```tzdata``` 패키지를 알아냈고 설치했다.</code></pre><p>sudo apt -y install tzdata
sudo timedatectl set-timezone Asia/Seoul
timedatectl
date </p>
<pre><code>date를 찍어보면 _Wed Jul 23 12:23:27 KST 2025_ 라는 결과가 잘 나온다.

### [한번에 되는 건 없어요] 문제 해결
실습할 때 한번에 제대로 진행된 적이 없듯 오늘도 그랬다.
미리 설정해두지 않은 여러 절차가 있었다.

1. restart.sh를 실행 파일로 바꾸기: 정말 당연하고 간단한건데 까먹었었다.
```chmod +x restart.sh``` 해줘야한다.
2. PATH 명시하기:
bash로 실행시켰을땐 잘 됬었는데 crontab에선 실행이 안됬다. crontab은 기본 쉘과 기본 경로가 제한적 이어서 PATH를 명시해줘야 한다고 한다.
```PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin``` 첫 줄 밑에 적어주었다.
3. 스크립트 안에서 쓰는 명령어의 절대경로 필요: 2번까지 했음에도 안됬었다. 추가 방법이 명령어의 절대경로를 적어주는 거였는데,
```which docker-compose``` 이렇게 적어 docker-compose 명령어의 절대 경로를 확인 후 스크립트에 절대경로로 명령어를 적어주는 것이다.

수정코드</code></pre><p>#!/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin</p>
<p>LOGFILE=&quot;/home/ubuntu/restart.log&quot;
echo &quot;[$(date)] Restart script start&quot; &gt;&gt; $LOGFILE</p>
<p>sudo /usr/local/bin/docker-compose -f /home/ubuntu/Devops/docker-compose.yml down &gt;&gt; $LOGFILE 2&gt;&amp;1
sudo /usr/local/bin/docker-compose -f /home/ubuntu/Devops/docker-compose.yml up -d &gt;&gt; $LOGFILE 2&gt;&amp;1</p>
<p>echo &quot;[$(date)] Restart script complete&quot; &gt;&gt; $LOGFILE</p>
<pre><code>_참고: 2&gt;&amp;1은 에러도 같은 파일로 리다이렉트_

근데 docker의 절대경로는 ```usr/bin/docker```던데
왜 docker-compose는 ```usr/local/bin/docker-compose```일까? 신기하네..


작성 중에 vi 에디터를 써서 편집도 했는데,
단어 치환이 필요해서 검색으로 찾아내어 적었다.
%s/[원본단어]/[바꿀단어]/옵션 ex) ```%s/myapp/Devops/g```
g는 전역을 의미.

### 최종 확인
![](https://velog.velcdn.com/images/park-rgb125/post/4b2c491a-0fe2-40da-8713-e6a6d808b1d5/image.png)
restart.log를 cat했을때 로그가 잘 남아있는 걸 확인 할 수 있었다.
UTC -&gt; KST로 바뀐거 보니까 마음이 편안하다.

다음은 Nginx + HTTPS 복습을 할 것 이다.

&gt; 학습 참고 링크 출처:
- [리눅스 크론탭(Linux Crontab) 사용법](https://jdm.kr/blog/2)
- [[Linux] 리눅스 timezone 설정 UTC에서 KST로 변경하기](https://bosungtea9416.tistory.com/entry/Linux-%EB%A6%AC%EB%88%85%EC%8A%A4-timezone-%EC%84%A4%EC%A0%95-UTC%EC%97%90%EC%84%9C-KST%EB%A1%9C-%EB%B3%80%EA%B2%BD%ED%95%98%EA%B8%B0#:~:text=%ED%99%98%EA%B2%BD%EC%9D%80%20EC2%EC%9D%98%20Amazon%20Linux2%EC%9D%B4%EB%A9%B0%2C%20%EA%B8%B0%EB%B3%B8%EC%A0%81%EC%9C%BC%EB%A1%9C%20%EC%84%A4%EC%A0%95%EB%90%98%EC%96%B4%20%EC%9E%88%EB%8A%94,%EA%B0%99%EC%9D%B4%20%EC%88%98%EC%A0%95%20ZONE=%22UTC%22%20UTC=true%20%23%20/etc/localtime%20%ED%8C%8C%EC%9D%BC%EC%9D%84)
- [vi editor 텍스트 치환(변경)하기](https://cchhoo407.tistory.com/3)
- Chat GPT</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[GitHub Actions로 EC2 자동 배포]]></title>
            <link>https://velog.io/@park-rgb125/GitHub-Actions%EB%A1%9C-EC2-%EC%9E%90%EB%8F%99-%EB%B0%B0%ED%8F%AC</link>
            <guid>https://velog.io/@park-rgb125/GitHub-Actions%EB%A1%9C-EC2-%EC%9E%90%EB%8F%99-%EB%B0%B0%ED%8F%AC</guid>
            <pubDate>Tue, 15 Jul 2025 07:13:42 GMT</pubDate>
            <description><![CDATA[<h3 id="🎯-목표">🎯 목표</h3>
<p>GitHub에 Push하면 EC2 서버에서 자동으로 git pull + Docker Compose 재시작되는 흐름을 만든다.</p>
<h4 id="🧩-실습">🧩 실습</h4>
<h4 id="1-ec2에서-ssh-키-발급"><strong>1. EC2에서 SSH 키 발급</strong></h4>
<p>aws EC2 인스턴스가 이미 있다는 가정하에 진행한다.
인스턴스로 접속하여 해당 코드를 입력해 SSH 키를 발급한다.</p>
<pre><code>ssh-keygen -t rsa -b 4096</code></pre><h4 id="2-공개키를-github에-등록-settings--deploy-keys"><strong>2. 공개키를 GitHub에 등록 (Settings &gt; Deploy Keys)</strong></h4>
<p>SSH 키를 발급받으면 공개키와 비밀키를 함께 받을 수 있다.</p>
<p><em>공개키 확인방법</em></p>
<pre><code>cat ~/.ssh/id_rsa.pub</code></pre><p>여기로 들어가면</p>
<pre><code>ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA7... (생략) ...==</code></pre><p>요런 긴 문자열이 있다. 이 전체 문자열을 복사하자.</p>
<h4 id="3-github-레포지토리로-이동"><strong>3. GitHub 레포지토리로 이동</strong></h4>
<ul>
<li>GitHub에서 너의 프로젝트 레포로 이동</li>
<li>Settings &gt; Deploy keys로 이동</li>
<li>Add deploy key 클릭</li>
<li>Title: EC2 Deploy Key</li>
<li>Key: 복사한 공개키 붙여넣기</li>
<li>Allow write access 체크 (git pull이니 읽기만 필요하지만, push도 할거면 체크)</li>
</ul>
<h4 id="4-추가-완료"><strong>4. 추가 완료</strong></h4>
<p>이러면 EC2에서 git pull 할 때 비밀번호 없이 SSH 키로 인증할 수 있다.</p>
<h4 id="5--github-actions-워크플로우-만들기"><strong>5.  GitHub Actions 워크플로우 만들기</strong></h4>
<p>내가 GitHub에 푸시하면 → EC2가 자동으로 코드를 pull하고 → docker-compose를 재시작하게 만든다.
(이 부분이 제일 어려웠고 오래걸렸다..)</p>
<ul>
<li><p>로컬 프로젝트에 아래 경로에 디렉토리 만들기:
<code>.github/workflows/deploy.yml</code></p>
</li>
<li><p>그리고 deploy.yml에 다음 코드 넣기:</p>
<pre><code>name: Deploy to EC2
</code></pre></li>
</ul>
<p>on:
  push:
    branches: [ main ]</p>
<p>jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - name: SSH and deploy
      uses: appleboy/ssh-action@master
      with:
        host: ${{ secrets.EC2_HOST }}
        username: ${{ secrets.EC2_USER }}
        key: ${{ secrets.EC2_SSH_KEY }}
        script: |
          cd /path/to/your/project
          git pull origin main
          sudo docker-compose down
          sudo docker-compose up --build -d</p>
<pre><code>/path/to/your/project → EC2에서 내 프로젝트 경로로 바꿔준다.
- EC2에서 내 프로젝트 경로 확인하는 방법: EC2 접속 -&gt; git clone 해당 레포지토리 -&gt; 클론한 프로젝트로 cd 한후 pwd입력하면 내 프로젝트 경로가 나온다. 실습할 때 나는 ```cd /home/ubuntu/Devops```라고 썼다.

#### **6. GitHub Secrets 설정**
위에서 ${{ secrets.XXX }} 부분에 값이 있어야 실행된다.
- GitHub 레포 Settings &gt; Secrets and variables &gt; Actions
- New repository secret 클릭

아래 세가지를 등록</code></pre><p>EC2_HOST: EC2의 퍼블릭 IP
EC2_USER: ec2-user (Amazon Linux) or ubuntu (Ubuntu)
EC2_SSH_KEY: EC2의 private key (id_rsa의 내용)</p>
<pre><code>
참고: EC2의 퍼블릭IP는 인스턴스를 중지하고 실행할때마다 **매번 바뀐다.**
그래서 탄력적IP를 설정하여 IP를 고정하는 것도 편한 방법 중 하나다.
_물론 요금이 나온다 :).._

**그리고 이때!! 중요한게 있다.**
EC2_SSH_KEY에 EC2의 private key (id_rsa의 내용)을 등록할 때,
id_rsa의 내용은 대략 이렇다.</code></pre><p>-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA0V2Lt...
(중간 내용 수백 줄)
...QIDAQAB
-----END RSA PRIVATE KEY-----</p>
<pre><code>여기서  저 **BEGIN RSA~~랑 END RSA까지** 전부 복사해서 붙여넣어야한다. 안에 있는 중간 내용만 맞는 줄 알고 넣어서 deploy했다가 오류났었다 :(

아, 그리고 여기에서 말하지만, 난 배포 후 내가 EC2에 docker-compose를 설치하지 않음을 깨달았다. 여러분은 꼭 미리 EC2에 ```docker와 docker-compose```를 설치하기 바란다^^
#### _간단한 EC2에 docker 설치 방법_

1. docker 설치</code></pre><p>sudo apt update
sudo apt install docker.io -y</p>
<p>sudo systemctl start docker
sudo systemctl enable docker</p>
<pre><code>
2. docker-compose 설치, 실행권한 부여</code></pre><p>#docker-compose 설치
sudo curl -L &quot;<a href="https://github.com/docker/compose/releases/download/v2.24.6/docker-compose-$">https://github.com/docker/compose/releases/download/v2.24.6/docker-compose-$</a>(uname -s)-$(uname -m)&quot; -o /usr/local/bin/docker-compose</p>
<p>#실행권한 부여
sudo chmod +x /usr/local/bin/docker-compose</p>
<pre><code>
3. 설치확인</code></pre><p>docker-compose --version</p>
<p>```
정상설치되면 Docker Compose version v2.24.6
이렇게 버전이 나와야한다!</p>
<p>이렇게 설치한 후 다시 로컬 코드 수정 + git push 하면
GitHub Actions가 EC2에 접속해서 docker-compose up을 제대로 실행할 수 있다!.. 그치만 미리 추가 작업을 해줘야 오류 없이 잘 deploy할 수 있다.</p>
<h4 id="추가로-미리-해둬야할-작업에러-해결">추가로 미리 해둬야할 작업(에러 해결)</h4>
<p><strong>1. EC2내부에서만 .env파일을 만들자</strong>
겪은 에러: <code>env file /home/****/Devops/.env not found</code>
이유는?</p>
<ul>
<li>프로젝트의 docker-compose.yml 안에 <strong>환경변수 참조 (.env 파일)</strong>가 있거나, Node.js 앱 내에서 dotenv 패키지를 써서 .env 로드를 시도하고 있었다.</li>
<li>그런데 GitHub에는 .gitignore 때문에 .env가 없음.
그래서 EC2의 git pull 이후에도 .env 파일이 없다!
해결방법: <strong>EC2내부에서만 .env파일을 만들어서 필요한 키들을 적어주고 저장한다(보안이슈 때문에 github엔 가급적 push하지 않는다!)</strong></li>
</ul>
<p><strong>2. EC2 보안 그룹 확인</strong>
AWS 콘솔 → EC2 → 인스턴스 → 보안 → 보안그룹 → 인바운드 규칙에서
<strong>3000번 포트 (TCP)</strong>가 열려 있어야 한다.
없으면 추가하기</p>
<ul>
<li>포트 범위: 3000</li>
<li>프로토콜: TCP</li>
<li>소스: 0.0.0.0/0 (전부 허용, 테스트용) 또는 본인 IP</li>
</ul>
<h4 id="마무리-전체적인-흐름">마무리, 전체적인 흐름</h4>
<ul>
<li>로컬 VSCode에서 코드 수정</li>
<li>git add, commit, push</li>
<li>GitHub → Actions 자동 실행</li>
<li>EC2 SSH 접속 → git pull, docker-compose down, build, up -d</li>
<li>변경사항 반영된 서비스가 EC2에서 자동 재실행</li>
<li>http://퍼블릭IP:3000 접속 → 변경 확인
이렇게 구현을 완료했다.</li>
</ul>
<p>배포는 되는데 EC2에서 자동으로 git pull, docker-compose down, build, up -d 가 안되서 오류를 해결하느라 시간이 좀 소요되었다.</p>
<p>배포 흔적을 보여주고 싶었는데 사진이 자꾸 깨져서..
<a href="https://github.com/kgms217068/Devops/actions">여길 누르세요</a> 이 링크를 누르면 확인할 수 있다.
예전에 했을땐(compose-todo-api 였나?) 배포까지만 하고 변경사항 반영되서 재실행 하게 하는건 안했다. 이번엔 다 해서 뿌듯하다.</p>
<p>이제 여기에 프론트를 추가해도 재밌겠고.. 다른 데브옵스 공부를 해서 추가해도 재밌겠다. 2학기에 수업으로도 배워서 실습은 일단 여기까지 해볼까.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Nginx 프록시 컨테이너 추가+.env 파일 사용, 보안/유지성 개선]]></title>
            <link>https://velog.io/@park-rgb125/Nginx-%ED%94%84%EB%A1%9D%EC%8B%9C-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%B6%94%EA%B0%80.env-%ED%8C%8C%EC%9D%BC-%EC%82%AC%EC%9A%A9-%EB%B3%B4%EC%95%88%EC%9C%A0%EC%A7%80%EC%84%B1-%EA%B0%9C%EC%84%A0</link>
            <guid>https://velog.io/@park-rgb125/Nginx-%ED%94%84%EB%A1%9D%EC%8B%9C-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%B6%94%EA%B0%80.env-%ED%8C%8C%EC%9D%BC-%EC%82%AC%EC%9A%A9-%EB%B3%B4%EC%95%88%EC%9C%A0%EC%A7%80%EC%84%B1-%EA%B0%9C%EC%84%A0</guid>
            <pubDate>Mon, 14 Jul 2025 07:17:48 GMT</pubDate>
            <description><![CDATA[<h2 id="🎯-목표">🎯 목표</h2>
<p>프론트 요청을 Nginx → Node → Mongo로 전달하는 구조 완성
민감한 정보를 코드가 아닌 .env 파일에서 관리할 수 있다</p>
<h3 id="🧩-실습-세부내용">🧩 실습 세부내용</h3>
<ol>
<li><p>nginx 폴더 만들고, default.conf 생성:</p>
<pre><code>server {
listen 80;

location / {
 proxy_pass http://backend:3000;
}
}
</code></pre></li>
</ol>
<pre><code>2. ```docker-compose.yml```에 nginx 추가:</code></pre><p>  nginx:
    image: nginx:stable
    ports:
      - &quot;8080:80&quot;
    volumes:
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - backend</p>
<pre><code>3. 전체 실행: ```docker-compose up --build```
4. 접속 확인: http://localhost:8080 → “Hello with Mongo!”
근데 난 hello with mongo라고 안뜨더라.. 
connected는 됬는데 db error로 undefined라고 나온다.

5. ```.env``` 파일 생성:</code></pre><p>MONGO_URL=mongodb://mongo:27017/test</p>
<pre><code>6. ```docker-compose.yml``` 수정:</code></pre><p>  backend:
    ...
    env_file:
      - .env</p>
<pre><code>7. app.js 최상단에 dotenv 추가:</code></pre><p>require(&#39;dotenv&#39;).config();</p>
<p><code>주의: dotenv 가 설치되어 있어야 오류없이 작동한다
없다면</code>npm install dotenv```로 다운받기</p>
<ol start="8">
<li>다시 실행: <code>docker-compose down &amp;&amp; docker-compose up --build</code></li>
</ol>
<p>여전히 컨테이너가 쉽지 않다..</p>
<p>조만간 중급자용 책을 살 예정이다.
컨테이너 개념을 잘 이해하면 다른 사람들에게도 쉽게 설명해줄 수 있을 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Docker Compose로 MongoDB 연동 (Backend + DB)]]></title>
            <link>https://velog.io/@park-rgb125/Docker-Compose%EB%A1%9C-MongoDB-%EC%97%B0%EB%8F%99-Backend-DB</link>
            <guid>https://velog.io/@park-rgb125/Docker-Compose%EB%A1%9C-MongoDB-%EC%97%B0%EB%8F%99-Backend-DB</guid>
            <pubDate>Mon, 14 Jul 2025 06:47:09 GMT</pubDate>
            <description><![CDATA[<h2 id="🎯-목표">🎯 목표</h2>
<p>하나의 명령어로 Node.js + MongoDB 컨테이너를 동시에 실행할 수 있다</p>
<h3 id="🧩-실습-세부내용">🧩 실습 세부내용</h3>
<ol>
<li><p>루트에 docker-compose.yml 파일 만들기:</p>
<pre><code>version: &#39;3&#39;
services:
backend:
 build: .
 ports:
   - &quot;3000:3000&quot;
 depends_on:
   - mongo
 environment:
   - MONGO_URL=mongodb://mongo:27017/test

mongo:
 image: mongo:6
 ports:
   - &quot;27017:27017&quot;
</code></pre></li>
</ol>
<pre><code>참고로 yml 파일은 띄어쓰기가 중요한 것 같다.
이 docker-compose.yml을 보고 직접 따라쳤는데 띄어쓰기 문제 때문에
_services.ports must be a mapping_
이런 오류가 발생했었다.
GPT 가 알려준 코드 그대로 복붙해서 다시 docker-compose up하니 잘 실행된다.

2. app.js 수정 (Mongo 연결 포함):</code></pre><p>const express = require(&#39;express&#39;);
const mongoose = require(&#39;mongoose&#39;);
const app = express();
const PORT = 3000;</p>
<p>mongoose.connect(process.env.MONGO_URL)
  .then(() =&gt; console.log(&#39;MongoDB connected&#39;))
  .catch(err =&gt; console.error(&#39;DB error:&#39;, err));</p>
<p>app.get(&#39;/&#39;, (req, res) =&gt; res.send(&#39;Hello with Mongo!&#39;));
app.listen(PORT, () =&gt; console.log(<code>Server on ${PORT}</code>));</p>
<pre><code>3. ```npm install mongoose```

4. ```docker-compose up --build```

5. localhost:3000 접속 → 서버 정상 작동 확인

### 잘 모르고 했던 실수 하나

docker run으로 3000번 포트를 점유한 컨테이너를 실행 중에서
docker-compose up --build를 했더니
_port is already allocated_
라는 오류가 났었다.

```docker run으로 만든 컨테이너와
docker-compose up으로 만든 컨테이너는
다른 컨테이너지만, 서버(호스트)의 동일한 포트(예: 3000번)를 공유하기 때문에 충돌이 나.</code></pre><p>그렇다고 한다.</p>
<p>그리고 추가로</p>
<pre><code>docker-compose는 여러 컨테이너를 하나의 그룹처럼 실행하고 연결하는 도구라서,
가능하면 실습할 땐 docker run은 잠깐 테스트용,
docker-compose로 전체를 구성하는 게 더 깔끔하고 관리도 쉬워.</code></pre><p>이것도 알려주었다.</p>
<h4 id="참고1">참고1</h4>
<pre><code>🔧 Dockerfile vs docker-compose.yml

📌 Dockerfile
하나의 이미지(예: Node.js 앱 이미지)를 만드는 “레시피”

보통 FROM, COPY, RUN, CMD 같은 명령이 들어감

📌 docker-compose.yml
여러 개의 컨테이너를 정의하고 한 번에 띄움

예를 들어 Node.js 서버, MongoDB, Nginx가 각각 컨테이너 하나씩 필요할 때</code></pre><p>docker-compose는 여러개의 컨테이너를 한번에 정의한다.
컨테이너 한개 = 이미지 한개
컨테이너 한개에 여러개의 이미지 X</p>
<h4 id="참고-2">참고 2</h4>
<p><img src="https://velog.velcdn.com/images/park-rgb125/post/77abcf23-de85-412f-af8d-95188852ddcd/image.png" alt=""></p>
<p>한번 실습할때 컨테이너를 생성하고 끝난 후 중지하고 삭제하지 않으면 어느새 이렇게 컨테이너가 많아져있다.</p>
<p>특히 나는 docker run으로 컨테이너 실행 후 내 터미널에서 ctrl+c를 해서 종료했었는데, 사진을 보면 2일 지난 컨테이너도 여전히 살아있는걸 볼 수 있다. 쟤내들을 다 지우고 나니 그제서야 <code>docker-compose up build</code>가 제대로 실행됬다.</p>
<p>docker run에 -d 옵션을 붙이면
ex) <code>docker run -d -p 3000:3000 node-test</code></p>
<p>백그라운드에서 실행되기 때문에,
Ctrl + C는 단순히 내 터미널에서 연결을 끊는 것일 뿐,
컨테이너는 백그라운드에서 계속 running 상태이다.</p>
<ol>
<li>docker run으로 실행한 컨테이너는 docker stop으로 꼭 중지, docker kill(rm도 가능)로 삭제해주자</li>
<li>docker-compose up 으로 실행한 컨테이너는 docker-compose down으로 꼭 컨테이너를 말끔히 종료해주자</li>
</ol>
<p>앞으로 docker-compose down을 습관처럼 써야겠다..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Docker로 Node.js 서버 컨테이너 만들기]]></title>
            <link>https://velog.io/@park-rgb125/Docker%EB%A1%9C-Node.js-%EC%84%9C%EB%B2%84-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@park-rgb125/Docker%EB%A1%9C-Node.js-%EC%84%9C%EB%B2%84-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Mon, 14 Jul 2025 05:58:08 GMT</pubDate>
            <description><![CDATA[<h2 id="🎯-목표">🎯 목표</h2>
<p>Dockerfile을 이해하고 직접 만들 수 있다
docker build, docker run을 실행해 서버를 띄울 수 있다</p>
<h3 id="🧩-실습-세부내용">🧩 실습 세부내용</h3>
<ol>
<li><p>node-docker-test라는 폴더 생성</p>
</li>
<li><p>아래 구조로 파일 구성:</p>
</li>
</ol>
<p>node-docker-test/
├── app.js
├── package.json
├── Dockerfile</p>
<ol start="3">
<li>app.js 내용<pre><code>const express = require(&#39;express&#39;);
const app = express();
const PORT = 3000;
app.get(&#39;/&#39;, (req, res) =&gt; res.send(&#39;Hello Docker!&#39;));
app.listen(PORT, () =&gt; console.log(`Server on ${PORT}`));</code></pre></li>
</ol>
<ol start="4">
<li><p>package.json 만들기(터미널에서)</p>
<pre><code>npm init -y
npm install express</code></pre></li>
<li><p>Dockerfile 내용</p>
<pre><code>FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD [&quot;node&quot;, &quot;app.js&quot;]</code></pre><p>여기서 Dockerfile의 각 명령어들을 간단 정리하자면
FROM: 생성할 이미지의 베이스가 될 이미지를 결정하는 명령어,
이 명령어를 시작으로 Dockerfile을 작성한다.</p>
</li>
</ol>
<p>WORKDIR: 명령어를 실행할 디렉토리를 지정하는 명령어(= linux cd명령어)</p>
<p>EXPOSE: Dockerfile의 빌드로 생성된 이미지에서 노출할 포트를 지정하는 명령어,
그러나 EXPOSE를 설정한 이미지를 기반으로 컨테이너를 생성한다고해서 이 포트가 호스트의 포트와 바인딩하는 것은 아니며, 단지 해당 이미지를 사용하는 사용자에게 해당 포트를 사용한다고 문서화하는 목적을 가지는 것 뿐이다.</p>
<p><em>Q.만약 publish하고 싶으면?</em>
<em>docker run 명령어에 -p 옵션으로 포트를 지정</em></p>
<p>COPY: 현재 디렉토리 경로의 모든 파일과 디렉토리를 해당 디렉토리의 디렉토리에다가 모두 복사하는 명령어, 
<code>COPY &lt;src&gt; &lt;dest&gt;</code>
  src는 호스트 운영체제 경로, dest는 이미지 상에서 경로</p>
<p>RUN: 이미지를 만들기 위해 컨테이너 내부에서 명령어를 실행한다.</p>
<ol start="6">
<li><p>이미지 빌드</p>
<pre><code>docker build -t node-test .</code></pre></li>
<li><p>실행</p>
<pre><code>docker run -p 3000:3000 node-test</code></pre></li>
</ol>
<p>-p옵션으로 포트바인딩한다
3000:3000
[호스트의 포트] : [컨테이너의 포트]</p>
<ol start="8">
<li>브라우저 접속: <a href="http://localhost:3000">http://localhost:3000</a> → “Hello Docker!” 나오면 성공</li>
</ol>
<p><img src="https://velog.velcdn.com/images/park-rgb125/post/a0b09fae-81b4-44ed-be3b-cd046c8d32f0/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[윈도우에서 도커 실습(wsl2사용)]]></title>
            <link>https://velog.io/@park-rgb125/%EC%9C%88%EB%8F%84%EC%9A%B0%EC%97%90%EC%84%9C-%EB%8F%84%EC%BB%A4-%EC%8B%A4%EC%8A%B5wsl2%EC%82%AC%EC%9A%A9</link>
            <guid>https://velog.io/@park-rgb125/%EC%9C%88%EB%8F%84%EC%9A%B0%EC%97%90%EC%84%9C-%EB%8F%84%EC%BB%A4-%EC%8B%A4%EC%8A%B5wsl2%EC%82%AC%EC%9A%A9</guid>
            <pubDate>Fri, 23 May 2025 08:00:17 GMT</pubDate>
            <description><![CDATA[<p>wsl2에 docker를 설치하는 방법은
<a href="https://idea9329.tistory.com/1031">https://idea9329.tistory.com/1031</a>
이 블로그를 참고했다.</p>
<p>분명 도커가 잘 설치 되어있고
docker -v하면 버전도 잘 뜨는데 docker ps를 하려 할때마다</p>
<p><em>The command &#39;docker-compose&#39; could not be found in this WSL 2 distro</em>
이런 비슷한 오류가 떠서 또 구글링했다.</p>
<p><a href="https://whdgus928.tistory.com/176">https://whdgus928.tistory.com/176</a>
이 블로그를 보고 해결할 수 있었다.(구글링 짱)
캡쳐화면을 보여주고 싶은데 docker desktop이 실행이 안되서 아쉽 :(</p>
<h4 id="실습-ing-docker-run-">실습 ing (docker run )</h4>
<p>docker 실습을 위해선 image가 필요하다. 이는 도커 명령어로 내려받을 수 있으므로 별도로 설치할 필요는 없다.</p>
<blockquote>
<p>🤔 image가 뭐에요?
가상머신을 생성할 때 사용하는 iso파일과 비슷하다.
컨테이너를 생성하고 실행할 때 읽기 전용으로 사용된다.</p>
</blockquote>
<p>난 요리조리 검색하다가 archlinux라는걸 알게되어서 이걸 설치해주었다.
설치는 간단하다!
<a href="https://hub.docker.com/_/archlinux/">https://hub.docker.com/_/archlinux/</a>
여기로 가서 copy한 후 wsl2에 붙여넣기 혹은
직접</p>
<pre><code>docker pull archlinux</code></pre><p>라고 써줘도 좋다. 후자가 더 편하니 후자를 추천:) </p>
<p><img src="https://velog.velcdn.com/images/park-rgb125/post/0b3ca773-8fcf-4873-af44-0daeeb2c58eb/image.png" alt=""></p>
<p>이렇게 docker pull을 한 후</p>
<pre><code>docker images</code></pre><p>를 입력하면 추가한 archlinux 이미지가 보인다.</p>
<p><em>(잠시만 컨테이너 설명을 빼먹었어)</em></p>
<blockquote>
<p>🤔 container가 뭐에요?
프로세스 단위의 격리환경, 위에서 말한 image로 컨테이너를 생성하면 해당 이미지의 목적에 맞는 파일이 들어있는 파일 시스템과 격리된 시스템 자원 및 네트워크를 사용할 수 있는 독립된 공간이 생성됨&lt;-- 컨테이너</p>
</blockquote>
<p>이제 컨테이너를 생성하면서 동시에 실행을 시켜주자.
docker run 명령어를 이용하면 쉽다.</p>
<pre><code>docker run -i -t archlinux</code></pre><p><img src="https://velog.velcdn.com/images/park-rgb125/post/06afa480-76c8-4054-9590-9683d175f8b8/image.png" alt=""></p>
<p>실행했을때 무슨 프롬프트가 뜨긴해서 어 된건가..?
했는데 혹시 몰라 다른 창일 수 있잖아 해서</p>
<p>ls /etc 해보니 arch-relaese가 뜨길래 오케이~~ 했다.</p>
<p><img src="https://velog.velcdn.com/images/park-rgb125/post/eabd2939-91d6-4ef4-a6f7-133df72dd984/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/park-rgb125/post/5a1709b8-31c5-4965-b021-4daa803140e7/image.png" alt=""></p>
<h4 id="docker-create-attach">docker create, attach</h4>
<p><strong>지금 시작하세요!도커/쿠버네티스</strong> 라는 책을 참고하고 있어서 여기서 나온 docker create, attach라는 개념도 살짝 실습해봤다.</p>
<p><img src="https://velog.velcdn.com/images/park-rgb125/post/e04669ca-7368-4554-b952-c7d2e27b9f1b/image.png" alt=""></p>
<pre><code>docker create -i -t --name myarchlinux archlinux</code></pre><p>이렇게 &quot;myarchlinux&quot;라는 archlinux 이미지를 생성했다.</p>
<p>여기서 바로 docker run을 하려고 했더니 permission관련 오류가 떴다! 엥?</p>
<p>확인해보니
create명령어는 컨테이너를 생성만 할뿐 컨테이너로 들어가지 않는다고 한다.
들어가려면 docker start와 docker attach를 해주어야한다.</p>
<pre><code>docker start myarchlinux

docker attach myarchlinux</code></pre><p><img src="https://velog.velcdn.com/images/park-rgb125/post/1327b27f-dbda-4798-9a89-43e673032fce/image.png" alt=""></p>
<p>역시 실행이 잘된 것을 확인할 수 있었다.</p>
<blockquote>
<p>그다음은?
책의 내용을 따라
2.2.4 컨테이너를 외부에 노출
~
2.2.9 컨테이너 자원 할당 제한</p>
</blockquote>
<p>까지 실습할 예정이다.</p>
<p>난 지금껏 visual studio code 터미널 안에서만 docker를 썼었는데
(node.js + Dockerfile)
이렇게 wsl2에서 해보니까 약간 다른 느낌..</p>
<p>배워갈수록 더 다를 거같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TypeError: The comparison function must be either a function or undefined]]></title>
            <link>https://velog.io/@park-rgb125/TypeError-The-comparison-function-must-be-either-a-function-or-undefined</link>
            <guid>https://velog.io/@park-rgb125/TypeError-The-comparison-function-must-be-either-a-function-or-undefined</guid>
            <pubDate>Sun, 18 May 2025 08:27:25 GMT</pubDate>
            <description><![CDATA[<p><em>간단한 CRUD 실습 중 발생한 에러</em></p>
<pre><code>TypeError: The comparison function must be either a function or undefined: #&lt;Object&gt;
    at Array.sort (&lt;anonymous&gt;)
    at C:\Users\user\Desktop\dev\CRUD-1\app.js:16:42
    at process.processTicksAndRejections (node:internal/process/task_queues:105:5)  </code></pre><p>인터넷에서도 명확하게 잘 안나오길래 GPT한테 물어봤다.</p>
<p>문제였던 코드</p>
<pre><code>app.get(&#39;/diaries&#39;,async (req, res) =&gt; {
   const diaries = await Diary.find().sort({date: -1});
    res.render(&#39;diaries&#39;,{diaries}); //ejs 템플릿으로 넘김
})</code></pre><p>.sort({date: -1}); 이부분이 문제인가? 했는데</p>
<blockquote>
</blockquote>
<p>🔍 왜 틀렸던 걸까?
(await Diary.find()) → 이건 배열이 됨
.sort({ date: -1 }) → 자바스크립트 배열 .sort()는 비교 함수를 요구함
{ date: -1 }는 MongoDB 쿼리 형식이지 비교 함수가 아니야!
그래서 TypeError가 뜬 거야 😅</p>
<blockquote>
<p>🔁 정리 한 줄 요약
Diary.find().sort({ ... })는 MongoDB에서 정렬
(await Diary.find()).sort(...)는 JS 배열 정렬이므로 비교 함수가 필요함</p>
</blockquote>
<p>고쳐주니 잘 떴다 :]</p>
<p><img src="https://velog.velcdn.com/images/park-rgb125/post/48f69c97-dbf6-499c-a6f6-5bd7a58feca5/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[docker-compose로 여러 컨테이너 한번에 빌드(feat.mongoDB)]]></title>
            <link>https://velog.io/@park-rgb125/docker-compose%EB%A1%9C-%EC%97%AC%EB%9F%AC-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%ED%95%9C%EB%B2%88%EC%97%90-%EB%B9%8C%EB%93%9Cfeat.mongoDB</link>
            <guid>https://velog.io/@park-rgb125/docker-compose%EB%A1%9C-%EC%97%AC%EB%9F%AC-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%ED%95%9C%EB%B2%88%EC%97%90-%EB%B9%8C%EB%93%9Cfeat.mongoDB</guid>
            <pubDate>Fri, 09 May 2025 13:16:11 GMT</pubDate>
            <description><![CDATA[<p>docker-compose를 이용해 여러 컨테이너를 빌드하는 실습을 했다.</p>
<h4 id="docker-compose를-사용하는이유">docker-compose를 사용하는이유</h4>
<p>docker-compose : 단일 서버에서 여러개의 컨테이너를 하나의 서비스로 정의해 컨테이너의 묶음으로 관리할 수 있는 작업 환경을 제공하는 관리 도구</p>
<p>여러개의 컨테이너로 구성된 어플리케이션을 구축하기 위해 run 명령어를 여러번 사용할 수 있지만 각 컨테이너가 제대로 동작하는지 확인하는 테스트 단계에서는 이런식으로 일일이 여러개의 컨테이너를 실행하기는 번거롭다. 매번 run 명령어에 옵션을 설정해 cli로 컨테이너를 생성하기 보다는 여러개의 컨테이너를 하나의 서비스로 정리해 컨테이너 묶음으로 관리할 수 있다면 좀 더 편리할것. 도커 컴포즈는 컨테이너를 이용한 서비스의 개발과 CI를 위해 여러 개의 컨테이너를 하나의 프로젝트로서 다룰 수 있는 작업환경을 제공한다.
(출처: <a href="https://seosh817.tistory.com/387">https://seosh817.tistory.com/387</a>)</p>
<p>docker-compose실습을 위해 다른 프로젝트 한개가 더 필요했는데, 그건 mongoDB프로제그로 해결하기로 했다.</p>
<p>근데 mongoDB를 어떻게 쓰는지부터 익혀야해서, 가볍게 데이터베이스를 추가하는 것부터 시작했다.
mongoDB 설치부터!</p>
<pre><code>npm install express mongoose</code></pre><p>mongoDB와 mongoose의 차이는 이 <a href="https://velog.io/@shrup5/MongoDb%EC%99%80-Mongoose%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90">링크</a>를 참조하면 잘 알 수 있다.</p>
<h4 id="실습">실습</h4>
<p>전에 했던 compose-todo-app의 코드를 참고해 index.js에 app.get을 추가했다.</p>
<pre><code>app.get(&#39;/items&#39;, async (req, res) =&gt; {
  const items = await Item.find(); 
  res.json(items);
});

app.get(&#39;/debug&#39;, async (req, res) =&gt; {
  try {
    const items = await Item.find();
    if (items.length === 0) {
      return res.send(&#39;⚠️ 데이터 없음1&#39;);
    }
    res.json(items);
  } catch (err) {
    res.status(500).send(&#39;❌ 에러 발생: &#39; + err.message);
  }

});</code></pre><p>compose-todo-app프로젝트를 실습했을땐 별다른 프로그램 설치 없이 진행했어서 이번에도 그렇게 하려했는데 구글에 검색해보니 mongoDB를 다운받아야하고 mongosh를 다운받아야하고... 그래서 다 다운 받았다.</p>
<p>docker-compose.yml을 작성하고 프로젝트 구조에 문제가 있어서 조금 변경하고 등등</p>
<p><em>docker-compose.yml</em></p>
<pre><code>version: &quot;3.8&quot;

services:
  backend:
    build: .
    ports:
      - &quot;3000:3000&quot;
    depends_on:
      - mongo
    environment:
      - PORT=3000
      - MONGO_URL=mongodb://mongo:27017/testDB

  mongo:
    image: mongo
    ports:
      - &quot;27017:27017&quot;
    volumes:
      - mongo-data:/data/db 

volumes:
  mongo-data: {}      
</code></pre><p>맨 밑에 volumes은 gpt가 알려준 데이터 영구저장에 관련됬다. 터미널에서 mongosh로 접속해</p>
<pre><code>db.컬렉션명.insert();
db.item.insert([{&quot;name&quot;:&quot;소현&quot;,&quot;age&quot;:24},{&quot;name&quot;:&quot;신위&quot;,&quot;age&quot;:24}]);</code></pre><p>데이터를 삽입해준 후 mongoDB compass에서 connection을 지웠다가 다시 켜봤는데 데이터가 매번 지워져있었다. 그래서 저 volumes을 추가로 설정해줬다. yml 파일이라 그런지 띄어쓰기가 너무 엄격하다.</p>
<h4 id="문제1컨테이너-꺼짐">문제1(컨테이너 꺼짐)</h4>
<p>중간에 컨테이너 두개 중 한개가 자꾸 꺼지는 일이 발생했는데 캐시문제 때문에 그랬었다.</p>
<pre><code>docker-compose down

docker-compose build --no-cache

docker-compose up -d</code></pre><p>이렇게 순서대로 실행해주니 정상적으로 컨테이너가 실행되었다.</p>
<h4 id="문제2json데이터-화면에-안보임">문제2(json데이터 화면에 안보임)</h4>
<p>mongoDB에 저장되어있는 데이터를 불러와서 화면에 띄우기 까지 완성하는게 목표였다.
localhost:3000/items 을 찍어보면 분명히 [] 은 나오는데 계속 데이터가 안나와서 미치는 줄 알았다.</p>
<p><em>models/Item.js</em></p>
<pre><code>const mongoose = require(&#39;mongoose&#39;);

const itemSchema = new mongoose.Schema({
  name: String,
  age: Number,
});

module.exports = mongoose.model(&#39;Item&#39;, itemSchema, &#39;item&#39;); // 컬렉션 이름 고정</code></pre><p>여기서 컬렉션 이름 때문에 또 몇십분 썼다.
이 문제도 있었고, 위에서 언급한 mongoDB 데이터가 계속 지워지는 현상때문에 [] 이렇게 나오기도 했다.</p>
<p>결과물
<img src="https://velog.velcdn.com/images/park-rgb125/post/fe52418e-cf6a-4403-bb2c-4317fc2b2974/image.png" alt="">
JSON데이터가 화면에 잘 뜬것을 볼 수 있다.</p>
<p>컨테이너들 또한 잘 실행되었다.
<img src="https://velog.velcdn.com/images/park-rgb125/post/7710db0d-ff21-4090-9591-a43bec6cd50a/image.png" alt=""></p>
<p>이 실습에만 3시간 넘게 썼는데.. 다른 실습들은 과연 어떨까.
다음은 gitAction이나 다른 걸 실습해볼 계획이다.</p>
<p>모든 코드는
<a href="https://github.com/kgms217068/docker-rebuild">https://github.com/kgms217068/docker-rebuild</a>
여기에 업로드 되어있으니 참고바랍니다</p>
<p><em>데브옵스 엔지니어로써 일하기 위해~</em></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Dockerfile로 컨테이너 생성 및 실행 실습]]></title>
            <link>https://velog.io/@park-rgb125/Dockerfile%EB%A1%9C-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%83%9D%EC%84%B1-%EB%B0%8F-%EC%8B%A4%ED%96%89-%EC%8B%A4%EC%8A%B5</link>
            <guid>https://velog.io/@park-rgb125/Dockerfile%EB%A1%9C-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%83%9D%EC%84%B1-%EB%B0%8F-%EC%8B%A4%ED%96%89-%EC%8B%A4%EC%8A%B5</guid>
            <pubDate>Wed, 07 May 2025 02:45:32 GMT</pubDate>
            <description><![CDATA[<h4 id="1-npm-init--y로-npm-시작">1. npm init -y로 npm 시작</h4>
<h4 id="2-express-generator로-프로젝트-생성-express-docker-rebuild">2. express generator로 프로젝트 생성 (express docker-rebuild)</h4>
<p>(이때  SET DEBUG=docker-rebuild:* &amp; npm start를 실행하지 못하는 오류가 발생했는데,
이건 그냥 window powershell의 문제로 두개를 따로 실행해주면 된다.)</p>
<h4 id="3-첫번째-문제-발생error-cannot-find-module-http-errors">3. 첫번째 문제 발생,(Error: Cannot find module &#39;http-errors&#39;)</h4>
<p>npm install http-errors를 입력해 http-errors를 설치해준다</p>
<h4 id="4-npm-install로-각종-의존성-패키지들을-설치해준다">4. npm install로 각종 의존성 패키지들을 설치해준다</h4>
<h4 id="5-dockerfile만들기">5. Dockerfile만들기</h4>
<pre><code>FROM node:18
#기본 이미지를 node 18버전을 사용한다

WORKDIR /app
#컨테이너의 작업공간을 /app을 기준으로 한다. 
#작업 공간이란 해당 컨테이너로 파일을 넣거나 진입했을 때 기준이 되는 경로

COPY package*.json ./
#호스트 계정에 있는 package로 시작하는 파일명.json 파일을 컨테이너의 ./경로로 복사한다.

RUN npm install
#컨테이너 안에서 npm install 명령어를 수행한다.

COPY . ./
EXPOSE 3000
CMD [ &quot;npm&quot;,&quot;run&quot;,&quot;start&quot; ]
#호스트 계정에서 도커파일이 있는 경로의 모든 파일/디렉터리를 컨테이너의
#./경로로 복사.호스트 계정에서 도커파일이 있는 경로의 모든 파일/디렉터리를 컨테이너의 ./ 경로로 복사한다.

#마지막으로 3000번 포트를 노출하고 컨테이너가 실행될 때 npm run start명령어를 수행</code></pre><p>&quot;여기서 한가지 중요한 점은 Dockerfile을 이용하여 빌드하기 전에 반드시 node_modules를 삭제해주세요&quot;</p>
<p>이건 참고한 링크에서 이렇게 하랬는데 왠진 잘모르겠다.</p>
<h4 id="6-도커이미지-빌드-빌드란-dockerfile의-내용을-기반으로-도커-이미지를-만드는-것을-의미합니다-이렇게-만들어진-이미지를-이용하여-컨테이너를-생성하면-됩니다">6. 도커이미지 빌드: 빌드란 Dockerfile의 내용을 기반으로 도커 이미지를 만드는 것을 의미합니다. 이렇게 만들어진 이미지를 이용하여 컨테이너를 생성하면 됩니다.</h4>
<pre><code>docker build -t [이미지 이름:이미지 버전] [Dockerfile의 경로]
docker build -t Dockerfile .</code></pre><p>이렇게 했을때  The system cannot find the file specified. 
오류가 나서 검색해보니 docker desktop을 안켜서 그렇다고.. 해서 
키고 다시 해봤더니 
ERROR: invalid tag &quot;Dockerfile&quot;: repository name must be lowercase이런 오류가 나길래 아 소문자로 써야하나?하고
docker build -t dockerfile .로했더니 잘되는것 같다가 </p>
<p>ERROR: failed to solve: failed to read dockerfile: open Dockerfile: no such file or directory 이런오류가 발생했다.
몇분 삽질하다가 그냥 docker build -t dockerfile . 이렇게 했더니 바로 됨
이미지 명이랑 dockerfile의 이름을 무조건 같게는 안해도되는구나를 깨달았다.</p>
<p><img src="https://velog.velcdn.com/images/park-rgb125/post/6b61f94c-8d58-4ce1-9007-b301db0b3cc0/image.png" alt=""></p>
<p>이러고 이제 docker images로 확인해보면 이름이 잘 뜬다.
docker run을 하려는데 </p>
<pre><code>docker run -d -p 3000:3000 docker-rebuild</code></pre><p>이렇게 하니
Unable to find image &#39;docker-rebuild:latest&#39; locally
docker: Error response from daemon: pull access denied for docker-rebuild, repository does not exist or may require &#39;docker login&#39;
이런 오류가 떴다.</p>
<p>그래서 docker hub에서 로그인하고 터미널 내에서도 docker login을 해줬는데 또 같은 오류가 발생했다.
보니까 이름을 잘못적은거같아서 dockerfile로 바꿔주니 됬다. 근데 포트를 3000번으로 했더니 포트 할당 오류가 발생해 8080:80으로 바꿔주니 해시값이 나오면서 성공했다.</p>
<p>8080포트로 접근하려고했는데 오류가 나고 80포트로 접근하려니 예전에 설정해둔 nginx 페이지가 떠서 당황했다.</p>
<h4 id="개념-정리">개념 정리</h4>
<p>dockerfile 내 expose
EXPOSE 3000 : 이 컨테이너 내부에서 3000번 포트를 서비스로 열거야. 이건 단순한 정보 표기이고, 실제 포트를 열고 연결하려면 docker run -p 명령이 필요</p>
<pre><code>docker run -p 8080:80</code></pre><p>의미: 내 컴퓨터의 8080포트와 컨테이너 내부의 80포트를 연결해줘
localhost:8080 → 컨테이너 내부 80번 포트로 들어감
localhost:80 → 로컬 PC 자체에 이미 떠 있는 서비스(Nginx 등)가 응답</p>
<p>이렇게 뜨길래 기존에 실행중이던 컨테이너를 지우고 다시 빌드한 후 궁금한게 있어 gpt에게 물어봤다.
&quot;포트를 npm start할때 얘를 3000번대로 이미 사용중이라서 안되는건가? 그럼 nodejs 실행 포트를 바꿔야해?&quot;
로컬 PC에서 3000번 포트가 이미 점유 중인데
Docker 컨테이너도 -p 3000:3000 하려고 하면 → 충돌 나거나 접속이 안 돼
→ 그래서 Node.js 앱의 포트를 바꾸거나,
→ 또는 Docker가 바인딩하는 호스트 포트를 다른 걸로 정해야 해</p>
<p>난 docker가 바인딩하는 호스트 포트를 바꾸는 방법을 선택했다.
docker run -p 8080:3000 dockerfile
그래서 이렇게 한 후 localhost:8080으로 들어가니 접속이 잘됬다.
(컨테이너 내부:3000번에서 앱실행됨, 로컬 pc:localhost:8080으로 접속가능)</p>
<p>전에 실습했을때 docker-compose.yml도 했던거같아서.. 뭔 차이인지 궁금했다.
검색해보니
Dockerfile은 사용자가 이미지를 어셈블하기 위해 호출할 수 있는 명령이 포함된 간단한 텍스트 파일인 반면 Docker Compose는 다중 컨테이너 Docker 애플리케이션을 정의하고 실행하기 위한 도구입니다.</p>
<p>Docker Compose는 앱을 구성하는 서비스를 docker-compose.yml에 정의하여 격리된 환경에서 함께 실행할 수 있습니다. docker-compose up을 실행하여 하나의 명령으로 앱을 실행합니다. 프로젝트의 docker-compose.yml에 빌드 명령을 추가하면 Docker compose는 Dockerfile을 사용합니다. Docker 워크플로는 생성하려는 각 이미지에 적합한 Dockerfile을 빌드한 다음 compose를 사용하여 build 명령을 사용하여 이미지를 조합하는 것이어야 합니다.</p>
<p>이렇다고 한다
Dockerfile : 이미지 빌드
docker-compose.yml : 앱이 실행되는 동안 컨테이너 관리</p>
<p>docker-compose.yml을 사용하여 하는 것도 곧 도전해봐야지</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[
Master Theorem 마스터 정리 알고리즘 시간 복잡도 구하기 (최대한 쉽게)]]></title>
            <link>https://velog.io/@park-rgb125/Master-Theorem-%EB%A7%88%EC%8A%A4%ED%84%B0-%EC%A0%95%EB%A6%AC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8B%9C%EA%B0%84-%EB%B3%B5%EC%9E%A1%EB%8F%84-%EA%B5%AC%ED%95%98%EA%B8%B0-%EC%B5%9C%EB%8C%80%ED%95%9C-%EC%89%BD%EA%B2%8C</link>
            <guid>https://velog.io/@park-rgb125/Master-Theorem-%EB%A7%88%EC%8A%A4%ED%84%B0-%EC%A0%95%EB%A6%AC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8B%9C%EA%B0%84-%EB%B3%B5%EC%9E%A1%EB%8F%84-%EA%B5%AC%ED%95%98%EA%B8%B0-%EC%B5%9C%EB%8C%80%ED%95%9C-%EC%89%BD%EA%B2%8C</guid>
            <pubDate>Sat, 12 Apr 2025 14:23:49 GMT</pubDate>
            <description><![CDATA[<p>알고리즘 과제로 master theorem으로 T(n)에 대한 시간복잡도를 구하는 문제가 있어서 구글링 해봤는데</p>
<p>수포자인 내가 봤을땐 정말 하나도 이해되지 않았다.
점화식을 뭘 어떻게 해서 등비수열..뭐?
정말 모르겠어서 chat gpt에게 쉽게 설명해달라고 했다.
그리고 내가 과제를 풀기 위해 이해한 것만 적어본다</p>
<p>원래의 그 증명이야 당연히 이해가 안되고 그냥 푸는것도 어렵..</p>
<p>주의) 일부문제에만 적용될 수도 있다.</p>
<p><img src="https://velog.velcdn.com/images/park-rgb125/post/35e9d3fe-9a3e-4969-85a5-1f2122dedfd7/image.jpg" alt=""></p>
<p>우린 여기서 a,b,d 값을 구해 case 1, 2, 3 중 어디에 해당하는지 알아내면 된다.</p>
<p>단, f(n)이 nlogn인 경우는 d로 직접 표현할 수 없다.</p>
<p>예시 문제 2개를 보며 풀어보자.</p>
<ol>
<li><img src="https://velog.velcdn.com/images/park-rgb125/post/a1e63c91-0515-48d6-8a46-081ca3f31983/image.jpg" alt=""></li>
</ol>
<p>여기서 a = 2, b = 2, log_b(a)= 1, 그럼 d는 뭘까?
f(n)의 형태가 n의 몇 제곱인가를 찾는게 핵심이다.</p>
<p>f(n) = 2 , 2는 상수이므로 O(n^0) 즉 d = 0 (b^d = 1)
case에 비교해보면, a &gt; b^d &lt;=&gt; log_b(a) &gt; d 인 case 3에 맞다.
확인) 2 &gt; 1 &lt;=&gt; 1 &gt; 0 만족.</p>
<p>따라서 형태가 O(n^log_b*a)형태가 되는데 여기서 log_b(a) = 1 이었으니
O(n^1) =  O(n)이 된다.</p>
<p>따라서 문제 1의 시간 복잡도는 O(n)이다.</p>
<p>2.<img src="https://velog.velcdn.com/images/park-rgb125/post/4b749d9f-7a9e-449b-80bd-8f938ce7ba60/image.jpg" alt=""></p>
<p>여기서 a = 3, b = 2 , log_b(a) = 1.548, d는?
엥 이번엔 f(n)이 n^2이다. 상수의 형태가 아닌데 어떻게 구하지?
이때 d는 그냥 이 지수 값을 의미한다.
즉, f(n) = n^2 -&gt; d = 2 (b^d = 4)</p>
<p>case 1에 만족한다는 것을 알 수 있다.
확인) 3 &lt; 4 &lt;=&gt; 1.548 &lt; 2</p>
<p>따라서 형태가 T(n^d)가 되는데 d = 2이니 O(n^2)가 된다.</p>
<p>문제 2의 시간 복잡도는 O(n^2)이다.</p>
<hr>
<p>(이 글은 개인 기록용으로 작성되었습니다)</p>
<p>정말 쉽게 푼 만큼 이렇게 간단하지 않은 문제엔 적용이 안될 수도 있다. 그리고 이게 틀린 걸 수도 있다. 다른 분들이 설명한 것과 같이 보면서 이해해보시기 바랍니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[💻 실무 DevOps 기초 루트 따라하기 — Docker, DockerHub, GitHub Actions, AWS EC2까지]]></title>
            <link>https://velog.io/@park-rgb125/%EC%8B%A4%EB%AC%B4-DevOps-%EA%B8%B0%EC%B4%88-%EB%A3%A8%ED%8A%B8-%EB%94%B0%EB%9D%BC%ED%95%98%EA%B8%B0-Docker-DockerHub-GitHub-Actions-AWS-EC2%EA%B9%8C%EC%A7%80</link>
            <guid>https://velog.io/@park-rgb125/%EC%8B%A4%EB%AC%B4-DevOps-%EA%B8%B0%EC%B4%88-%EB%A3%A8%ED%8A%B8-%EB%94%B0%EB%9D%BC%ED%95%98%EA%B8%B0-Docker-DockerHub-GitHub-Actions-AWS-EC2%EA%B9%8C%EC%A7%80</guid>
            <pubDate>Mon, 31 Mar 2025 08:53:30 GMT</pubDate>
            <description><![CDATA[<p>chat gpt의 도움을 받으며 일단 배포 먼저 해보자!라는 생각으로 하나씩 해보고있다.</p>
<p>목표: Node.js + MongoDB 프로젝트를 GitHub Actions + DockerHub + Docker Compose + AWS EC2를 활용해 완전 자동화 + 실전 배포까지 해보기</p>
<p>github 레포지토리 2개에 push 되어있다.
devops-node-todo-api / compose-todo-api</p>
<p><a href="https://github.com/kgms217068/devops-node-todo-api">devops-node-todo-api</a>
<a href="https://github.com/kgms217068/compose-todo-api">compose-todo-api</a>
코드를 깃허브에 올려뒀으니 참고해서 보면 좋을 것 같다.</p>
<h2 id="전체-실습-흐름-요약">전체 실습 흐름 요약</h2>
<h3 id="1-nodejs--mongodb-todo-api-만들기">1. Node.js + MongoDB Todo API 만들기</h3>
<ul>
<li>express와 mongoose로 간단한 /todos API 구현</li>
<li>GET, POST, PUT, DELETE 라우트로 CRUD 구성</li>
<li>MongoDB는 로컬이 아닌 도커 내부의 mongo 서비스로 연결<pre><code>mongoose.connect(&#39;mongodb://mongo:27017/todos&#39;)</code></pre></li>
</ul>
<h3 id="2-docker로-백엔드-컨테이너화">2. Docker로 백엔드 컨테이너화</h3>
<ul>
<li>backend/Dockerfile 작성</li>
<li>Node.js 앱을 도커 이미지로 빌드 가능하게 구성
Dockerfile<pre><code>FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD [&quot;node&quot;, &quot;index.js&quot;]</code></pre>이후<pre><code>docker build -t devops-node-todo-api</code></pre>를 이용해 도커 이미지를 빌드해주고</li>
</ul>
<pre><code>docker run -p 3000:3000 devops-node-todo-api</code></pre><p>를 써서 도커 컨테이너를 실행해준다.
-p 3000:3000 -&gt; 내 컴퓨터의 3000번 포트를 컨테이너 안의 3000번 포트와 연결</p>
<h3 id="3-docker-compose로-전체-서비스-관리">3. Docker Compose로 전체 서비스 관리</h3>
<ul>
<li>docker-compose.yml로 백엔드 + MongoDB구성
docker-compose.yml<pre><code>services:
backend:
  build: ./backend
  ports:
    - &quot;3000:3000&quot;
  depends_on:
    - mongo
mongo:
  image: mongo
  ports:
    - &quot;27017:27017&quot;</code></pre></li>
<li>docker-compose up 으로 컨테이너 2개를 한번에 실행
이때 중간에 gpt가 알려준 index.js 코드 중 일부를 일부러 안썼었다. 아예 안쓰는게 또 다른 문제기도 했던..<pre><code>mongoose.connect(&#39;mongodb://mongo:27017/todos&#39;, {
useNewUrlParser: true,
useUnifiedTopology: true,
});</code></pre>useNewUrlParser랑 useUnifiedTopology는 MongoDB 최신 버전에서는 필요없는 옵션.
그러나 mongoose.connect() 자체는 필수다. Node.js 서버가 MongoDB에 데이터를 저장/읽기 하려면 Mongoose가 MongoDB 서버와 &quot;연결&quot;되어 있어야하기 때문.</li>
</ul>
<p>수정 코드</p>
<pre><code>// MongoDB 연결
mongoose.connect(&#39;mongodb://mongo:27017/todos&#39;)
  .then(() =&gt; console.log(&#39;✅ MongoDB 연결 성공!&#39;))
  .catch(err =&gt; console.error(&#39;❌ MongoDB 연결 실패:&#39;, err));</code></pre><p>이후 docker-compose up --build를 다시 하고 localhost:3000/todos에 접속하니 [] 이 떴다(정상출력)</p>
<p>그다음 VS Code REST Client 확장으로 POST /todos 요청도 보내봤다.</p>
<ul>
<li>requests.http(REST Client가 읽을 요청 스크립트 파일)을 따로 만들어서 진행</li>
</ul>
<p>여기에서의 문제는</p>
<p><strong>PUT과 DELETE가 되지 않음</strong>
하나의 .http 파일에 GET, POST, PUT , DELETE등 여러 요청을 연습용으로 다 넣을 수 있다.</p>
<ol>
<li>index.js에 PUT과 DELETE요청을 처리하는 코드를 넣지 않았기 때문이다.</li>
</ol>
<p>그래서 index.js에 PUT, DELETE 코드를 추가했다.</p>
<pre><code>// ✅ 할 일 수정 (PUT)
app.put(&#39;/todos/:id&#39;, async (req, res) =&gt; {
  const { id } = req.params;
  const updated = await Todo.findByIdAndUpdate(id, { text: req.body.text }, { new: true });
  if (updated) {
    res.json(updated);
  } else {
    res.status(404).json({ error: &quot;할 일을 찾을 수 없어요!&quot; });
  }
});

// ✅ 할 일 삭제 (DELETE)
app.delete(&#39;/todos/:id&#39;, async (req, res) =&gt; {
  const { id } = req.params;
  const deleted = await Todo.findByIdAndDelete(id);
  if (deleted) {
    res.json({ message: &quot;삭제 완료!&quot; });
  } else {
    res.status(404).json({ error: &quot;삭제할 항목을 찾을 수 없어요!&quot; });
  }
});</code></pre><p>수정한 전체 .http 코드</p>
<pre><code>### 전체 할 일 목록 조회
GET http://localhost:3000/todos
Accept: application/json

###

### 새로운 할 일 추가 (POST 요청)
POST http://localhost:3000/todos
Content-Type: application/json

{
  &quot;text&quot;: &quot;Docker Compose 실습 최고!&quot;
}

###

### 할 일 수정 (PUT 요청)
PUT http://localhost:3000/todos/여기에_실제_ID_입력
Content-Type: application/json

{
  &quot;text&quot;: &quot;수정된 내용입니다!&quot;
}

###

### 할 일 삭제 (DELETE 요청)
DELETE http://localhost:3000/todos/여기에_실제_ID_입력
</code></pre><ol start="2">
<li>requests.http에 실제ID를 틀리게 입력함
너무 간단한 오류.. 당연히 ID가 맞아야 넣든 지우든 할것이다.</li>
</ol>
<h3 id="4-github-actions--dockerhub-자동화-구축-cicd">4. Github Actions + DockerHub 자동화 구축 (CI/CD)</h3>
<ul>
<li>.github/workflows/docker.yml 작성</li>
<li>GitHub에 push → Docker 이미지 자동 빌드 → DockerHub에 업로드</li>
<li>GitHub Secrets에 DOCKER_USERNAME / DOCKER_PASSWORD 등록</li>
</ul>
<p>yml 코드 중 일부</p>
<pre><code>run: docker build -t ${{ secrets.DOCKER_USERNAME }}/compose-todo-api ./backend
run: docker push ${{ secrets.DOCKER_USERNAME }}/compose-todo-api</code></pre><p>난 DockerHub 로그인에서 조금 막혔었다.
실수: GitHub Secrets에 DOCKER_NAME이라고 쓰고 yml엔 DOCKER_USERNAME이라고 쓰기(불일치)
여러번의 push 끝에 성공
<img src="https://velog.velcdn.com/images/park-rgb125/post/ae22d379-0f19-4f9c-b770-e1f03defb295/image.png" alt=""></p>
<p>여기까지하면</p>
<p><em>GitHub에 코드 푸시 → 자동으로 최신 Docker 이미지가 빌드됨
DockerHub에 latest 태그로 업로드됨
이제 누구나 docker pull로 받아서 실행 가능함!</em></p>
<p>이 가능하다</p>
<h3 id="5-env로-환경변수-분리-env-적용-실습">5. .env로 환경변수 분리 (.env 적용 실습)</h3>
<ul>
<li>MongoDB 주소와 포트를 .env 파일로 분리</li>
<li>process.env를 통해 코드에서 참조
.env 코드<pre><code>PORT=3000
MONGO_URL=mongodb://mongo:27017/todos</code></pre></li>
</ul>
<h3 id="6-aws-ec2-서버에-배포">6. AWS EC2 서버에 배포</h3>
<ul>
<li>EC2 Ubuntu 인스턴스 생성 &amp; SSH 접속</li>
<li>Docker + Docker Compose 설치</li>
<li>GitHub에서 프로젝트 클론</li>
<li>.env 작성 후 docker-compose up으로 컨테이너 실행</li>
<li>외부 IP + 3000 포트로 API 확인</li>
</ul>
<p>EC2 인스턴스 만들기</p>
<ol>
<li><a href="https://aws.amazon.com">https://aws.amazon.com</a> 접속</li>
<li>EC2 &gt; 인스턴스 시작
 운영체제: Ubuntu 22.04 LTS
 인스턴스 유형: t2.micro (프리 티어)
 키페어 생성 → .pem 파일 저장</li>
<li>인스턴스 생성 후, 보안 그룹 설정에서 포트 열기
  22번: SSH
  3000번: Node.js 접속용 (커스텀 TCP)
  27017번: MongoDB 접속용 (선택)</li>
</ol>
<pre><code>chmod 400 이름.pem
ssh -i 이름.pem ubuntu@퍼블릭IP주소</code></pre><p>wsl에서 EC2로 접속할때
<img src="https://velog.velcdn.com/images/park-rgb125/post/48c00076-eb6c-4584-8e9f-b0903fe1b1b9/image.png" alt="">
이런 문구가 떠서 좀 당황했는데, 리눅스나 맥에나 SSH접근시 항상 물어보는 질문이라고 한다.</p>
<p><a href="https://info-lab.tistory.com/254">[Linux / Mac] SSH 접근시 &quot;Are you sure you want to continue connecting (yes/no)?&quot; 없이
</a></p>
<p>이 링크를 참고하여 해결했다.</p>
<p>접속성공
<img src="https://velog.velcdn.com/images/park-rgb125/post/ae0eab5b-f0c1-438a-8ee6-6a78afc08f12/image.png" alt=""></p>
<p>그다음 다운받은 .pem 파일을 wsl로 옮겨줘야하는데,</p>
<pre><code>explorer.exe .</code></pre><p>이 명령어를 wsl에 입력해서 할 수 있었다. 신기하게 윈도우에 파일 복사하듯이 ctrl+c ctrl+v가 가능했다.</p>
<p>배포 완료
<img src="https://velog.velcdn.com/images/park-rgb125/post/702f58c2-2932-4498-83d8-cf0e7c1dd713/image.png" alt="">
뭔가 이렇게 뜨더니</p>
<pre><code>curl http://localhost:3000/todos</code></pre><p>을 입력하니 [] 이 잘 나왔다.</p>
<blockquote>
<p>소감: 
이번 실습을 통해 단순한 API 서버를 넘어서 도커화 → 자동화 → 배포까지 한 번에 경험할 수 있어 좋았다. 아직도 잘 모르겠지만 계속 하다보면 조금이라도 알게되지 않을까.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[우분투 리눅스 22.04 한영키 전환 커서 검색창으로 옮겨갈때 해결법]]></title>
            <link>https://velog.io/@park-rgb125/%EC%9A%B0%EB%B6%84%ED%88%AC-%EB%A6%AC%EB%88%85%EC%8A%A4-22.04-%ED%95%9C%EC%98%81%ED%82%A4-%EC%A0%84%ED%99%98-%EC%BB%A4%EC%84%9C-%EA%B2%80%EC%83%89%EC%B0%BD%EC%9C%BC%EB%A1%9C-%EC%98%AE%EA%B2%A8%EA%B0%88%EB%95%8C-%ED%95%B4%EA%B2%B0%EB%B2%95</link>
            <guid>https://velog.io/@park-rgb125/%EC%9A%B0%EB%B6%84%ED%88%AC-%EB%A6%AC%EB%88%85%EC%8A%A4-22.04-%ED%95%9C%EC%98%81%ED%82%A4-%EC%A0%84%ED%99%98-%EC%BB%A4%EC%84%9C-%EA%B2%80%EC%83%89%EC%B0%BD%EC%9C%BC%EB%A1%9C-%EC%98%AE%EA%B2%A8%EA%B0%88%EB%95%8C-%ED%95%B4%EA%B2%B0%EB%B2%95</guid>
            <pubDate>Fri, 21 Mar 2025 11:33:06 GMT</pubDate>
            <description><![CDATA[<p>이거 땜에 몇시간 동안 서칭했는데 아무리 봐도 답이 안나와서 gpt한테 물어봤다가
그래도 답이 안나와서 fcitx를 설치하고 설정해주었더니 됬다...</p>
<p>fcitx설치 방법</p>
<pre><code>sudo apt install fcitx5 fcitx5-hangul</code></pre><p>설치 후 Settings &gt; Region &amp; Language &gt; Manage Installed Languages &gt; Language Support 창 하단의 Keyboard input method system을 Fcitx 5로 선택
<img src="https://velog.velcdn.com/images/park-rgb125/post/1ad8bc4b-89f6-4d90-9f59-09c2383f4d78/image.png" alt=""></p>
<p>그다음
<img src="https://velog.velcdn.com/images/park-rgb125/post/98e5c614-d86c-405f-9887-004b27a14df8/image.png" alt=""></p>
<p>위에 키보드 모양이 뜰거다. 누르고 configure을 누른다.
<img src="https://velog.velcdn.com/images/park-rgb125/post/f0782ed0-29e9-4dde-a6d9-6b6569c29c78/image.png" alt=""></p>
<p>available input method 검색창에 hangul 입력 하면 뜬다.
(language support에서 hangul을 설치했다면)
&lt; 모양 눌러서 왼쪽으로 옮겨준다</p>
<p><img src="https://velog.velcdn.com/images/park-rgb125/post/d19e551f-aa1d-42e5-8fd8-54c501d92c6e/image.png" alt=""></p>
<p>trigger input method 클릭하고 한영키 누르면 이렇게 hangul이라고 뜬다.
apply 누른 후 입력해보면 뭔 이상한 작은창이 위에 함께 뜰텐데 그게 좀 거슬리고 자동으로 한자변환도 되서 별로니 없애주도록 하자.</p>
<p><img src="https://velog.velcdn.com/images/park-rgb125/post/3024dc36-b06c-4c4c-bd20-e683c9c57fbb/image.png" alt=""></p>
<p>Show Input Method Information when switch input method를 체크 해제하면 된다.</p>
<p>난 한자 쓸일이 없을거같아서 hangul hanja도 hanja mode toggle key에서 빼긴 했다.</p>
<p>와... ibus진짜 ㅡㅡ</p>
<p>fcitx5의 문제점은 문제가 생기면 알아보도록 하자.</p>
<hr>
<p>출처
<a href="https://blog.litehell.info/post/fcitx5_for_101_key_keyboard_kde_laptop/">한글 입력을 위한 fcitx5 설치</a>
<a href="https://osg.kr/archives/405">Ubuntu 24.04 한글입력기 fcitx5 설치 및 설정 방법</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[failed to push some refs to 문제 해결]]></title>
            <link>https://velog.io/@park-rgb125/failed-to-push-some-refs-to-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0</link>
            <guid>https://velog.io/@park-rgb125/failed-to-push-some-refs-to-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0</guid>
            <pubDate>Fri, 21 Mar 2025 09:52:52 GMT</pubDate>
            <description><![CDATA[<p>ubuntu linux에서 깃허브로 push하려고 하던 중 오류가 발생했다.
<img src="https://velog.velcdn.com/images/park-rgb125/post/138dbdc5-2491-48c4-aeb6-f5b53f8db08e/image.png" alt=""></p>
<p>전에도 본 적 있어서 침착하게 서치 했더니</p>
<pre><code>git pull origin main</code></pre><p>하고 다시 push하면 된다길래 했는데 또 안됨</p>
<p>그리고 내가 깃 푸시 과정 중 제일 싫어하는 상황
&quot;staging 중인 커밋 사라짐&quot;</p>
<p><img src="https://velog.velcdn.com/images/park-rgb125/post/ff978906-8ad5-48be-8432-15934499ccfd/image.png" alt=""></p>
<p>이렇게 git log엔 보이지만 status에 안보이면 멘붕이 온다.</p>
<p><img src="https://velog.velcdn.com/images/park-rgb125/post/df266df4-90fd-46c0-a4eb-e650516f4149/image.png" alt=""></p>
<p>다른 블로그들을 또 찾아서</p>
<pre><code>git reset --hard HEAD^</code></pre><p>를 했더니 뭔가 되긴 한거같은데
엥 제 파일도 함께 사라졌어요</p>
<p>&quot;git reset --hard는 현재 작업 중인 내용(unstaged)이나 되돌아가고 싶은 커밋 이후의 작업 내용을 전부 날린다.&quot;</p>
<p>그렇군...</p>
<p>계속 찾아보다가 branch name 문제인가 싶어서 검색</p>
<p>master -&gt; main으로 변경방법</p>
<pre><code>git branch -m master main</code></pre><p><img src="https://velog.velcdn.com/images/park-rgb125/post/5d483d72-eb8e-4f0f-9ea0-2d85a746059c/image.png" alt=""></p>
<p>내 브랜치는 master였고, 위 명령어로 수정하니 push가 됬다. 야호!</p>
<p>그나저나 우분투 한영키 자주 에러 나는게 좀 거슬린다. 조만간 고쳐서 또 업로드 해야지</p>
<hr>
<p>참고 출처들
<a href="https://ksko1.tistory.com/71">[Git] git 커밋 한거 롤백 하는 방법</a>
<a href="https://algoroot.tistory.com/28">[Git, Github] error: src refspec main does not match any  완벽 해결</a>
<a href="https://systorage.tistory.com/entry/Git-%EC%9B%90%ED%95%98%EB%8A%94-%EC%A7%80%EC%A0%90%EC%9D%98-%EC%BB%A4%EB%B0%8B%EC%9C%BC%EB%A1%9C-%EB%90%98%EB%8F%8C%EC%95%84%EA%B0%80%EB%8A%94-%EB%B0%A9%EB%B2%95-git-reset-hard">[Git] 원하는 지점의 커밋으로 되돌아가는 방법 (git reset --hard)
출처: https://systorage.tistory.com/entry/Git-원하는-지점의-커밋으로-되돌아가는-방법-git-reset-hard</a></p>
]]></description>
        </item>
    </channel>
</rss>