<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>YB_Engineer.log</title>
        <link>https://velog.io/</link>
        <description>개발 공부를 막 시작한 주니어입니다.</description>
        <lastBuildDate>Thu, 07 Apr 2022 07:13:09 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>YB_Engineer.log</title>
            <url>https://images.velog.io/images/yb_engineer/profile/9b91c0d2-6c07-4561-90ad-89605f202a84/social.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. YB_Engineer.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/yb_engineer" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Database | DB 이름 변경하기]]></title>
            <link>https://velog.io/@yb_engineer/Database-DB-%EC%9D%B4%EB%A6%84-%EB%B3%80%EA%B2%BD%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@yb_engineer/Database-DB-%EC%9D%B4%EB%A6%84-%EB%B3%80%EA%B2%BD%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 07 Apr 2022 07:13:09 GMT</pubDate>
            <description><![CDATA[<br>

<p>프로젝트 개발 단계에서 사용하던 데이터베이스를 그대로 운영 DB로 사용하려고 한다.</p>
<br>

<p><strong>MySQL</strong> 기준으로 <code>5.1.7</code> 버전에서는 <strong>RENAME DATABASE</strong> 명령어를 통해서 DB 이름을 쉽게 바꿀 수 있었으나,</p>
<p>데이터 손실 위험성이 발견되어 <code>5.1.23</code> 버전 이후로 위 명령어가 삭제되었다고 한다.</p>
<br>

<p>그렇다면...</p>
<br>

<p>Database 이름을 바꾼다? → 불가능!!!</p>
<p><strong>새로운 Database를 생성하고, 기존 Database의 데이터를 옮겨야 한다.</strong></p>
<br>

<pre><code class="language-mysql"># 우분투 서버에서 MySQL 접속
mysql -uroot -p

# 새로운 DB 생성
CREATE DATABASE &#39;DB명&#39;;

# 특정 사용자에게 DB 권한 부여
GRANT ALL PRIVILEGES ON &#39;DB명&#39;.&#39;TABLE명&#39; TO &#39;사용자명&#39;@&#39;host&#39; IDENTIFIED BY &#39;PW&#39;;

# 권한부여 명령어 적용
FLUSH PRIVILEGES;</code></pre>
<ul>
<li>우분투 서버에서 직접 MySQL에 접속하여, 새로운 DB 생성, 사용자에게 권한부여, 권한부여 명령어 적용 등의 쿼리를 수행한다.</li>
</ul>
<br>

<pre><code class="language-mysql"># 기존 DB.TABLE → 신규 DB.TABLE 변경
RENAME TABLE &#39;기존DB&#39;.&#39;TABLE명&#39; TO &#39;신규DB&#39;.&#39;TABLE명&#39;;</code></pre>
<ul>
<li>테이블 갯수가 많을 경우, 좀 더 효율적인 방식을 쓰면 좋으나, 우리 프로젝트의 경우 테이블이 6개여서 위 쿼리를 6번 수행하였다.</li>
</ul>
<br>

<p>모든 명령어, 쿼리 적용 이후 데이터가 정상적으로 신규 DB에 넘어온 것을 확인하였다.</p>
<p><img src="https://velog.velcdn.com/cloudflare/yb_engineer/e01f64a3-aaee-4642-91d6-e4edfc44c56d/image.png" alt=""></p>
<br>

<hr>
<h5 id="참고-자료">참고 자료</h5>
<ul>
<li><a href="https://ryean.tistory.com/41">https://ryean.tistory.com/41</a></li>
<li><a href="https://nickjoit.tistory.com/144">https://nickjoit.tistory.com/144</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Database | Redis 오류 발생]]></title>
            <link>https://velog.io/@yb_engineer/Database-Redis-%EC%98%A4%EB%A5%98-%EB%B0%9C%EC%83%9D</link>
            <guid>https://velog.io/@yb_engineer/Database-Redis-%EC%98%A4%EB%A5%98-%EB%B0%9C%EC%83%9D</guid>
            <pubDate>Sat, 02 Apr 2022 10:27:25 GMT</pubDate>
            <description><![CDATA[</br>

<p>빅데이터 추천 서비스 개발 프로젝트를 진행하면서, 우리팀은 <strong>Redis</strong>를 활용하여 회원가입 시 <strong>이메일 인증</strong> 기능을 구현하였다.</p>
<br>

<p><strong>이메일 인증</strong> 기능은 다음 흐름으로 진행된다.</p>
<ol>
<li>사용자가 본인이 자주 쓰는 or 회원가입하길 원하는 <strong>이메일 주소</strong>를 입력한다.</li>
<li>클라이언트에서 서버로 이메일 인증 API 요청을 보낸다.</li>
<li>Spring Boot 서버에 작성되어 있는 <strong>SMTP</strong> 설정에 따라서 이메일이 생성되고, 해당 주소로 이메일이 전송된다.</li>
<li>...</li>
</ol>
<br>

<p>정상적으로 작동하던 이메일 인증 기능이 갑자기 안된다는 이슈가 발생했다.</p>
<p><img src="https://media.vlpt.us/images/yb_engineer/post/11f1cf1a-e14b-4928-a515-32d38e307dd0/image.png" alt=""></p>
<p>사용자가 입력한 <strong>이메일</strong>과 난수값으로 생성된 <strong>인증코드</strong>는 정상적으로 로그에 찍히지만, 그 아래에 보이는 오류는 무엇인가...</p>
<br>

<h5 id="redissystemexception-">RedisSystemException ???</h5>
</br>

<p>관련하여 구글링해보면 아래 이미지와 같은 내용을 볼 수 있다.</p>
<p><img src="https://media.vlpt.us/images/yb_engineer/post/c362a2e0-c270-4d13-94e8-edee95f1a815/image.png" alt=""></p>
<br>

<p><strong>no</strong> 로 파라미터 값을 바꾸면 입력/수정/삭제가 가능하지만 데이터가 다 날아간다는 내용...</p>
<br>

<p>딱 봐도 위험한 내용이다.</p>
<br>

<p>지난 프로젝트에서 이메일 인증 구현을 내가 했기 때문에, <strong>Redis</strong> 설치 및 <code>.conf</code> 설정에 대한 기억이 남아 있었다.</p>
<p>이번에 <code>.conf</code> 파일에 설정을 몇 가지 안했던 것도 기억이 났다.</p>
<br>

<p>바로 <code>.conf</code> 파일을 수정하자!</p>
<br>

<pre><code class="language-bash"># Redis 최대 사용 메모리 설정
maxmemory 1g

# 최대 사용 메모리 초과 시 오래된 데이터 삭제 및 최근 데이터 사용 설정
maxmemory-policy allkeys-lru</code></pre>
<p>최대 사용 메모리 설정을 해주고,</p>
<br>

<pre><code class="language-bash"># Redis 재시작
sudo systemctl restart redis-server.service</code></pre>
<p>Redis를 재시작한다.</p>
<br>

<p><strong>Swagger</strong>로 다시 API 요청을 해보니 메일이 전송된다.</p>
<p><img src="https://media.vlpt.us/images/yb_engineer/post/39a4bc7e-7fd1-4cb4-b330-b13ae19bb99d/image.png" alt=""></p>
<p><img src="https://media.vlpt.us/images/yb_engineer/post/6a4d8c84-4f70-416e-a4e1-eb1ba83472c3/image.png" alt=""></p>
<p>메일이 정상적으로 온 것도 확인할 수 있다.</p>
<br>

<p>해결. 끝.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Server | Ubuntu Timezone 변경하기]]></title>
            <link>https://velog.io/@yb_engineer/Server-Ubuntu-Timezone-%EB%B3%80%EA%B2%BD%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@yb_engineer/Server-Ubuntu-Timezone-%EB%B3%80%EA%B2%BD%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 30 Mar 2022 10:08:29 GMT</pubDate>
            <description><![CDATA[<p>AWS EC2 인스턴스를 실행하면, 초기 설정은 <strong>영국 표준시</strong>로 되어 있다.</p>
<pre><code class="language-bash"># Server Timezone 확인
date</code></pre>
<br>

<p>대한민구, 서울 기준의 시간으로 바꾸고 싶다면 어떻게 해야할까?</p>
<br>

<pre><code class="language-bash"># Timezone 변경하기
sudo dpkg-reconfigure tzdata</code></pre>
<ul>
<li>설정화면에서 <strong>Asia</strong> → <strong>Seoul</strong> 선택하기</li>
</ul>
<p><img src="https://images.velog.io/images/yb_engineer/post/8274f2f1-bb5b-42d0-8957-8ed7b3609de5/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/yb_engineer/post/10bd19cb-ccd6-437f-8e08-b24691b51945/image.png" alt=""></p>
<br>

<p>타임존 변경 후 다시 <code>date</code> 명령어를 입력하면, 우리나라 표준시에 맞는 타임존을 확인할 수 있다.</p>
<p><img src="https://images.velog.io/images/yb_engineer/post/6ee6da61-fae9-4c6d-87f7-f478dc648c59/image.png" alt="">
<br></p>
<p>시간대 변경 후 <strong>Cron</strong> 을 재시작해야 한다.</p>
<pre><code class="language-bash"># Cron 재시작
sudo service cron restart</code></pre>
<br>

<hr>
<h4 id="참고-자료">참고 자료</h4>
<ul>
<li><a href="https://syssurr.tistory.com/272">https://syssurr.tistory.com/272</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Server | Cron]]></title>
            <link>https://velog.io/@yb_engineer/Server-Cron</link>
            <guid>https://velog.io/@yb_engineer/Server-Cron</guid>
            <pubDate>Wed, 30 Mar 2022 10:07:32 GMT</pubDate>
            <description><![CDATA[<p>웹 서비스를 개발하고 <strong>AWS EC2</strong> 서버에 배포를 하다보면, 다양한 이유로 <strong>스케쥴링</strong>을 해야 하는 경우가 있다.</p>
<br>

<p><em>데이터베이스 백업, 파이썬 스크립트 실행 등...</em></p>
<br>

<p>이럴 때 사용하는 것이 <strong>Cron</strong>이다.</p>
<br>

<h3 id="cron-이란">Cron 이란?</h3>
<ul>
<li>유닉스 계열 컴퓨터 OS의 <strong>시간 기반 잡 스케쥴러</strong></li>
<li>고정된 시간, 날짜, 간격에 주기적으로 실행할 수 있도록 스케쥴링할 때 사용함</li>
</ul>
<p><img src="https://images.velog.io/images/yb_engineer/post/33418df7-d6c8-4e59-a52b-0bb913860e4b/image.png" alt="">
<br></p>
<pre><code class="language-bash"># Cron 실행
vim /etc/crontab</code></pre>
<p>위 코드를 실행하면 스케쥴링 코드를 작성할 수 있는 crontab 파일이 열린다.</p>
<br>

<p><img src="https://images.velog.io/images/yb_engineer/post/ad97ae4b-d357-4385-b8d1-4cc2e435e642/image.png" alt=""></p>
<ul>
<li>Cron Expressions<ul>
<li>리눅스/유닉스 기반 Cron에는 <strong>5개</strong>의 필드가 사용된다.</li>
<li>내가 원하는 시간, 주기에 원하는 파일, 코드 등을 실행할 수 있다.</li>
<li><img src="https://images.velog.io/images/yb_engineer/post/9cea7443-b127-4d43-9a31-3f48479d89ec/image.png" alt=""></li>
</ul>
</li>
</ul>
<br>

<p><strong>✨ 주의 사항 ✨</strong></p>
<ul>
<li>Crontab으로 작성할 경우, <strong>쉘 스크립트</strong>나 <strong>Python 코드</strong> 내에 작성된 경로는 반드시, <strong>절대경로</strong>로 작성해야 한다.</li>
<li>상대경로 XXX. 절대경로로 작성하기!!!</li>
<li>예를 들어, <code>./test.csv</code> 와 같이 <code>.</code> 을 활용한 상대경로로 작성하면 정해진 시간에 크론이 실행될 때 정상적으로 실행되지 않는다.</li>
</ul>
<br>

<p><strong>cron</strong>을 사용해서 DB 백업, 추천 알고리즘 Python 코드 등의 스케쥴링을 작성할 수 있었다.</p>
<br>

<hr>
<h4 id="참고-자료">참고 자료</h4>
<ul>
<li><a href="https://any-ting.tistory.com/87">https://any-ting.tistory.com/87</a></li>
<li><a href="https://m.blog.naver.com/myohyun/222030669275">https://m.blog.naver.com/myohyun/222030669275</a></li>
<li><a href="https://madplay.github.io/post/a-guide-to-cron-expression">https://madplay.github.io/post/a-guide-to-cron-expression</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Algorithm | 백준 1956.운동 (python)]]></title>
            <link>https://velog.io/@yb_engineer/Algorithm-%EB%B0%B1%EC%A4%80-1956.%EC%9A%B4%EB%8F%99-python</link>
            <guid>https://velog.io/@yb_engineer/Algorithm-%EB%B0%B1%EC%A4%80-1956.%EC%9A%B4%EB%8F%99-python</guid>
            <pubDate>Mon, 21 Mar 2022 12:01:13 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>본 문제의 저작권은 BAEKJOON에 있습니다.</p>
<p><a href="https://www.acmicpc.net/problem/1956">백준 1956.운동 링크</a></p>
</blockquote>
</br>

<h4 id="문제">문제</h4>
<p>V개의 마을와 E개의 도로로 구성되어 있는 도시가 있다. 도로는 마을과 마을 사이에 놓여 있으며, 일방 통행 도로이다. 마을에는 편의상 1번부터 V번까지 번호가 매겨져 있다고 하자.</p>
<p>당신은 도로를 따라 운동을 하기 위한 경로를 찾으려고 한다. 운동을 한 후에는 다시 시작점으로 돌아오는 것이 좋기 때문에, 우리는 사이클을 찾기를 원한다. 단, 당신은 운동을 매우 귀찮아하므로, 사이클을 이루는 도로의 길이의 합이 최소가 되도록 찾으려고 한다.</p>
<p>도로의 정보가 주어졌을 때, 도로의 길이의 합이 가장 작은 사이클을 찾는 프로그램을 작성하시오. 두 마을을 왕복하는 경우도 사이클에 포함됨에 주의한다.</p>
</br>

<h4 id="입력">입력</h4>
<p>첫째 줄에 V와 E가 빈칸을 사이에 두고 주어진다. (2 ≤ V ≤ 400, 0 ≤ E ≤ V(V-1)) 다음 E개의 줄에는 각각 세 개의 정수 a, b, c가 주어진다. a번 마을에서 b번 마을로 가는 거리가 c인 도로가 있다는 의미이다. (a → b임에 주의) 거리는 10,000 이하의 자연수이다. (a, b) 쌍이 같은 도로가 여러 번 주어지지 않는다.</p>
</br>

<h4 id="출력">출력</h4>
<p>첫째 줄에 최소 사이클의 도로 길이의 합을 출력한다. 운동 경로를 찾는 것이 불가능한 경우에는 -1을 출력한다.</p>
</br>

<h4 id="코드">코드</h4>
<pre><code class="language-python">import sys

V, E = map(int, input().split())

INF = sys.maxsize
arr = [[INF] * V for _ in range(V)]

for _ in range(E):
    a, b, c = map(int, input().split())
    arr[a-1][b-1] = c

for k in range(V):
    for i in range(V):
        for j in range(V):
            arr[i][j] = min(arr[i][j], arr[i][k] + arr[k][j])

ans = INF
for i in range(V):
    ans = min(ans, arr[i][i])

if ans == INF:
    print(-1)
else:
    print(ans)</code></pre>
</br>

<h4 id="풀이">풀이</h4>
<ul>
<li><p><strong>플로이드 워셜 알고리즘</strong>을 사용해야 한다.</p>
<ul>
<li>플로이드 워셜 알고리즘이란?<ul>
<li>모든 정점에 관한 최단 경로를 구하는 알고리즘</li>
<li><code>i → j</code> 로 가는 경로는 <code>i → j</code> 로 바로 가거나, <code>i → k → j</code> 와 같이 <code>k</code> 정점을 거쳐서 가거나 둘 중 하나이다.</li>
</ul>
</li>
</ul>
</li>
<li><p>알고리즘 구현 순서는 다음과 같다.</p>
<ol>
<li>삼중 <code>for</code> 반복문 수행</li>
<li>거쳐가는 정점 <code>k</code>를 첫번째 반복문으로 한다.</li>
<li>삼중 반복문이 종료되고 나서, <code>arr[i][i]</code> 값이 <code>i → i</code> 로 돌아오는 사이클 경로의 최소값이다.</li>
</ol>
</li>
</ul>
</br>

<h4 id="참고">참고</h4>
<ul>
<li><a href="https://yuuj.tistory.com/61">https://yuuj.tistory.com/61</a></li>
<li><a href="https://pacific-ocean.tistory.com/280">https://pacific-ocean.tistory.com/280</a></li>
<li><a href="https://claude-u.tistory.com/335">https://claude-u.tistory.com/335</a></li>
<li><a href="https://tooo1.tistory.com/312">https://tooo1.tistory.com/312</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Algorithm | 백준 2805.나무 자르기 (python)]]></title>
            <link>https://velog.io/@yb_engineer/Algorithm-%EB%B0%B1%EC%A4%80-2805.%EB%82%98%EB%AC%B4-%EC%9E%90%EB%A5%B4%EA%B8%B0-python</link>
            <guid>https://velog.io/@yb_engineer/Algorithm-%EB%B0%B1%EC%A4%80-2805.%EB%82%98%EB%AC%B4-%EC%9E%90%EB%A5%B4%EA%B8%B0-python</guid>
            <pubDate>Sun, 13 Mar 2022 09:35:00 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>본 문제의 저작권은 BAEKJOON에 있습니다.</p>
<p><a href="https://www.acmicpc.net/problem/2805">백준 2805.나무 자르기 링크</a></p>
</blockquote>
</br>

<h4 id="문제">문제</h4>
<p>상근이는 나무 M미터가 필요하다. 근처에 나무를 구입할 곳이 모두 망해버렸기 때문에, 정부에 벌목 허가를 요청했다. 정부는 상근이네 집 근처의 나무 한 줄에 대한 벌목 허가를 내주었고, 상근이는 새로 구입한 목재절단기를 이용해서 나무를 구할것이다.</p>
<p>목재절단기는 다음과 같이 동작한다. 먼저, 상근이는 절단기에 높이 H를 지정해야 한다. 높이를 지정하면 톱날이 땅으로부터 H미터 위로 올라간다. 그 다음, 한 줄에 연속해있는 나무를 모두 절단해버린다. 따라서, 높이가 H보다 큰 나무는 H 위의 부분이 잘릴 것이고, 낮은 나무는 잘리지 않을 것이다. 예를 들어, 한 줄에 연속해있는 나무의 높이가 20, 15, 10, 17이라고 하자. 상근이가 높이를 15로 지정했다면, 나무를 자른 뒤의 높이는 15, 15, 10, 15가 될 것이고, 상근이는 길이가 5인 나무와 2인 나무를 들고 집에 갈 것이다. (총 7미터를 집에 들고 간다) 절단기에 설정할 수 있는 높이는 양의 정수 또는 0이다.</p>
<p>상근이는 환경에 매우 관심이 많기 때문에, 나무를 필요한 만큼만 집으로 가져가려고 한다. 이때, 적어도 M미터의 나무를 집에 가져가기 위해서 절단기에 설정할 수 있는 높이의 최댓값을 구하는 프로그램을 작성하시오.</p>
</br>

<h4 id="코드">코드</h4>
<pre><code class="language-python">N, M = map(int, input().split())

trees = list(map(int, input().split()))

left, right = 0, max(trees)

while left &lt;= right:
    mid = (left + right) // 2
    total = 0

    for tree in trees:
        if tree &gt;= mid:
            total += tree - mid

    if total &gt;= M:
        left = mid + 1
    else:
        right = mid - 1

print(right)</code></pre>
</br>

<h4 id="풀이">풀이</h4>
<pre><code class="language-python">&#39;이분탐색&#39;을 시행하여, 최대 높이를 구한다.
이분탐색으로 문제를 풀지 않으면, 시간초과 에러가 발생한다.

이분탐색은 시작점과 끝점을 설정하고, 중간값을 통해 결과를 도출하는 탐색 방법이다.

1. N개의 나무를 trees 리스트에 저장한다.
2. 시작(start, left)을 0으로 끝(end, right)을 max(trees)로 설정한다.
3. left의 값이 right의 값을 넘을 때까지 while문을 반복한다.
4. left와 right의 합의 절반값을 mid로 설정한다.
5. total 변수를 0으로 선언한다.
6. trees 리스트를 순회하면서 각 tree의 높이가 mid 값보다 크거나 같으면, total 변수에 tree - mid 값을 더해준다.
7. for문 종료 이후, total 변수가 M보다 크면 left값을 조정하고, total 변수가 M보다 작으면 right 값을 조정한다.
8. 4~7번을 3번 조건을 만족할 때까지 반복한다.
9. right 값을 출력한다.</code></pre>
<h4 id="참고">참고</h4>
<p><a href="https://jinu0418.tistory.com/116">https://jinu0418.tistory.com/116</a></p>
<p><a href="https://claude-u.tistory.com/446">https://claude-u.tistory.com/446</a></p>
<p><a href="https://deep-learning-study.tistory.com/603">https://deep-learning-study.tistory.com/603</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2022.02.18 | 싸피 메이트 PJT 회고]]></title>
            <link>https://velog.io/@yb_engineer/2022.02.18-%EC%8B%B8%ED%94%BC-%EB%A9%94%EC%9D%B4%ED%8A%B8-PJT-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@yb_engineer/2022.02.18-%EC%8B%B8%ED%94%BC-%EB%A9%94%EC%9D%B4%ED%8A%B8-PJT-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Fri, 18 Feb 2022 09:28:42 GMT</pubDate>
            <description><![CDATA[<p>지난 6주간 참여했던 프로젝트에 대한 회고글을 작성하려고 한다. 따라서 이 글은 개발 개념이나 기술에 관한 것이 아니다. 6주간 진행했던 프로젝트에서 느꼈던 점이나 다음 프로젝트에서 보완 개선할 내용 등에 대해서 서술한다.</p>
<hr>
<h3 id="프로젝트-개요">프로젝트 개요</h3>
<h5 id="서비스명">서비스명</h5>
<ul>
<li>싸피 메이트 (SSAFY MATE)</li>
</ul>
<br>

<h5 id="일정">일정</h5>
<ul>
<li>2022.01.04 ~ 2022.02.18</li>
</ul>
<br>

<h5 id="기획-및-목적">기획 및 목적</h5>
<ul>
<li>삼성청년소프트웨어아카데미 2학기 프로젝트 과정에서 팀빌딩을 원활하게 하기 위함.</li>
<li>공통/특화/자율 프로젝트 등 총 3번의 프로젝트를 진행하는데, 싸피 교육생 간 프로필 확인 및 보유 기술 스택 등을 확인하여 좀 더 쉽게 팀을 구성할 수 있다.</li>
</ul>
<br>

<h5 id="서비스-소개-영상">서비스 소개 영상</h5>
<p><a href="https://youtu.be/0q4h3c69dXg"><img src="http://img.youtube.com/vi/0q4h3c69dXg/0.jpg" alt="Video Label"></a></p>
<hr>
<h3 id="pjt-회고">PJT 회고</h3>
<p>6주간 서비스를 개발하면서 정말 많이 성장했다고 생각한다. 물론 아직 갈 길이 한참 멀긴하지만, 스스로 모르는 내용이나 우리 서비스에 필요한 개념들을 공부하고 적용해보는 시간이었다. 또한, 내가 어떤 부분을 더 보완하고 개선해야 되는지에 대해서도 배울 수 있는 유익한 기간이었다.</p>
<br>

<h5 id="성장한-부분">성장한 부분</h5>
<ul>
<li>Java 언어와 Spring Boot 프레임워크를 사용해보면서 <strong>새로운 언어/기술을 습득</strong>할 수 있었다.</li>
<li>MySQL, H2, Redis 등 <strong>다양한 DB를 사용</strong>해볼 수 있었다. <ul>
<li>관계형, 비관계형, 메모리 기반 DB 등 다양한 것을 사용함</li>
</ul>
</li>
<li><strong>RESTful API</strong>에 대해서 다시한번 개념 정립 및 개발<ul>
<li>1학기 관통프로젝트에서 Python - Django를 사용하여 RESTful API를 만들었었다. 그 경험을 살려서 다른 언어/프레임워크를 활용하여 우리 서비스에 맞는 API 서버를 개발하였다. 기술은 달라도 RESTful 하다는 개념은 동일하기 때문에 지난 학기에서 이해가지 않았던 부분이나 부족했던 부분을 보완하면서 개발할 수 있었다.</li>
</ul>
</li>
<li><strong>SMTP</strong> 통신으로 이메일 인증 구현, <strong>Spring Security, JWT</strong> 활용하여 인증 구현<ul>
<li>조금 생소하지만 중요한 개념이었다. 직접 부딪히고 공부하면서 우리 서비스에 맞게 구현할 수 있었다.</li>
</ul>
</li>
<li><strong>Convention</strong> 작성 및 적용<ul>
<li>Git 작성을 위한 컨벤션과 더불어 코드 컨벤션을 작성하여 팀원 간 공유하였다. 이 과정이 있었기 때문에 팀원 간 코드 및 문서의 통일성을 어느정도 지킬 수 있었다.</li>
<li>팀원이 6명이나 되는 개발과정이 처음이었기 때문에 컨벤션을 작성하고 적용하는 과정이 매우 유익했다.</li>
</ul>
</li>
<li><strong>배포 과정</strong>을 경험함<ul>
<li>가장 소중한 경험이었다고 생각한다. AWS EC2 인프라 환경 구축, 프론트 / 백 API 서버 / 백 채팅 서버 배포, GitHub Webhook과 Jenkins 연동, Docker를 활용하여 이미지 생성 및 컨테이너 띄우기, Nginx 웹서버 연결 등 처음해보는 부분이었다.</li>
<li>하지만 직접 내가 역할을 맡아서 진행하면서 가장 많이 배울 수 있었던 부분이라고 생각한다.</li>
<li>이제 막 시작한 단계이지만 <strong>배포</strong>의 영역도 지속적으로 공부해야 겠다.</li>
</ul>
</li>
<li><strong>실사용자</strong> 받기<ul>
<li>우리 서비스의 주요 타겟층은 싸피 교육생이다. </li>
<li>남들보다 개발 일정을 앞당겨서 배포하고, 도메인을 구입하여 사용자의 유입을 용이하게 하였다.</li>
<li>배포한 약 10일의 기간동안 싸피 교육생 <strong>170</strong>명이 우리 서비스에 가입하였다. 또한 Github Issue 탭에 버그, 이슈 리포팅을 해준 교육생도 있다.</li>
<li>오픈 채팅, 구글 폼 등을 통해 의견을 받았고 즉각적으로 대응하면서 실제 서비스 운영에 대해서 경험해볼 수 있었다.</li>
</ul>
</li>
</ul>
<br>

<h5 id="보완--개선할-부분">보완 / 개선할 부분</h5>
<ul>
<li><strong>코드 리팩토링</strong><ul>
<li>혼자 작업하면서도 코드의 개선이 필요했으나, 협업을 하면서 코드 리팩토링이 정말 필요하다는 생각을 많이 했다. 개발자 성향에 따라서 코드 작성 스타일이 다르기 때문에 코드 컨벤션을 명확하게 하여 우리 서비스의 코드가 통일성을 유지하도록 해야한다.</li>
</ul>
</li>
<li><strong>테스트</strong><ul>
<li>프로젝트 기획 단계에서 JUnit을 활용하여 테스트를 진행하자고 했었다. 하지만 실제로 개발을 진행하면서 전혀 활용해보지 못한 것이 아쉽다.</li>
<li>실제 개발한 API들은 Swagger와 Postman을 사용하여 테스트를 진행하였다. 하지만, 무수히 많은 테스트를 반복하면서 효율성이 떨어졌다. 다음 프로젝트에서는 단위, 통합 테스트 등을 진행하기 위해서 JUnit과 같은 기술을 사용해봐야겠다.</li>
</ul>
</li>
<li><strong>소통</strong> 많이 하기<ul>
<li>정말 다행스러운 것은 우리 팀은 프로젝트를 진행하면서 싸우지 않았다는 것이다. 물론 의견을 나누고 맞춰가는 과정에서 삐걱거림이 없었다고는 할 수 없다. 하지만 팀원들 모두가 양보하고 상대방을 인정하려는 자세가 있었기 때문에 서비스를 완성하고 프로젝트를 무사히 마칠 수 있었다.</li>
<li>하지만 좀 더 소통을 많이 할 걸... 이라는 아쉬움은 남는다. 분명 내 의견을 좀 더 피력하지 못했던 적이 많다. &quot;&quot;내 말이 맞아!&quot; 가 아닌, &quot;난 이렇게 생각하는데 어때?&quot; 라는 말을 좀 더 하고 내 의견을 어필했으면 더 좋았을 것이다.</li>
</ul>
</li>
</ul>
<hr>
<h3 id="마무리">마무리</h3>
<p>아쉬움과 후련함이 공존한 프로젝트였다. 프론트엔드 친구들이 훌륭해서 다른 팀보다 훌륭한 서비스 화면, 기능을 구현할 수 있었다. 백엔드 친구들이 함께 해주어 어려웠지만 DB 설계, API 개발, 배포 등의 과정을 진행할 수 있었다. 실제 사용자를 받아서 서비스 운영을 경험해볼 수 있었다. </p>
<br>

<p>좋은 팀원들을 만나 좋은 서비스를 기획, 개발, 배포하고 운영할 수 있는 정말 좋은 시간이었다.</p>
<br>

<p>하지만 완벽한 개발, 서비스란 세상에 없기 때문에 여기서 만족하지는 않겠다. 나 또한 완벽한 개발자가 아니고 이제 막 시작한 주니어 개발자이기 때문에 더더욱 성장하기 위해 매일매일 노력할 것이다.</p>
<br>

<p><strong>하루에 한걸음씩! 꾸준히 성장하는 개발자 손영배입니다 :D</strong> 라는 슬로건을 지키기 위해 노력하자!</p>
<h5 id=""></h5>
]]></description>
        </item>
        <item>
            <title><![CDATA[Linux | 기초 명령어]]></title>
            <link>https://velog.io/@yb_engineer/Linux-%EA%B8%B0%EC%B4%88-%EB%AA%85%EB%A0%B9%EC%96%B4</link>
            <guid>https://velog.io/@yb_engineer/Linux-%EA%B8%B0%EC%B4%88-%EB%AA%85%EB%A0%B9%EC%96%B4</guid>
            <pubDate>Sun, 13 Feb 2022 07:52:17 GMT</pubDate>
            <description><![CDATA[<p>리눅스에 관한 완전 쌩기초 명령어를 정리한다.</p>
<h3 id="이동">이동</h3>
<h5 id="pwd-print-working-directory">pwd (print working directory)</h5>
<ul>
<li>현재 나의 위치를 확인하는 명령어</li>
</ul>
<pre><code class="language-bash">pwd</code></pre>
<br>

<h5 id="ls-list">ls (list)</h5>
<ul>
<li>현재 나의 위치에 있는 파일과 폴더들을 보여주는 명렁어<ul>
<li><code>-a</code> 옵션을 사용하면 숨김폴더, 파일까지 확인 할 수 있다.</li>
<li><code>.</code> 은 현재폴더 /  <code>..</code> 은 상위폴더를 의미한다.</li>
</ul>
</li>
</ul>
<pre><code class="language-bash">ls</code></pre>
<br>

<h5 id="cd-change-directory">cd (change directory)</h5>
<ul>
<li>해당 위치로 이동하는 명령어<ul>
<li><code>..</code>은 상위 폴더를 의미한다.</li>
</ul>
</li>
</ul>
<pre><code class="language-bash">cd &lt;folder name&gt;</code></pre>
<hr>
<br>

<h3 id="생성">생성</h3>
<h5 id="mkdir-make-directory">mkdir (make directory)</h5>
<ul>
<li>폴더를 생성하는 명령어</li>
</ul>
<pre><code class="language-bash">mkdir &lt;folder name&gt;</code></pre>
<br>

<h5 id="touch">touch</h5>
<ul>
<li>파일을 생성하는 명령어</li>
</ul>
<pre><code class="language-bash">touch &lt;file name&gt;</code></pre>
<hr>
<br>

<h3 id="삭제">삭제</h3>
<h5 id="rm-remove">rm (remove)</h5>
<ul>
<li>파일 삭제<ul>
<li>폴더를 삭제하기 위해서는 <code>-r</code> 옵션을 정해줘야 함 (recursive)</li>
<li><code>-f</code> 옵션을 통해 강제로 삭제할 수 있다.</li>
</ul>
</li>
</ul>
<pre><code class="language-bash">rm &lt;file name&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Database | SQL 기초]]></title>
            <link>https://velog.io/@yb_engineer/Database-SQL-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@yb_engineer/Database-SQL-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Sun, 13 Feb 2022 07:46:00 GMT</pubDate>
            <description><![CDATA[<h3 id="sql-structured-query-language">SQL (Structured Query Language)</h3>
<ul>
<li>RDBMS의 데이터 관리를 위해서 설계된 특수 목적의 프로그래밍 언어</li>
<li>데이터베이스 스키마 생성 및 수정</li>
<li>자료의 검색 및 관리</li>
<li>데이터베이스 객체 접근 조정 관리</li>
</ul>
<br>

<h3 id="sql-분류">SQL 분류</h3>
<ul>
<li>DDL (Data Definition Language)<ul>
<li>데이터 정의 언어</li>
<li>관계형 데이터베이스 구조(테이블, 스키마)를 정의하기 위한 명령어</li>
<li>예시<ul>
<li>CREATE</li>
<li>DROP</li>
<li>ALTER</li>
</ul>
</li>
</ul>
</li>
<li>DML (Data Manipulation Language)<ul>
<li>데이터 조작 언어</li>
<li>데이터를 저장, 조회, 수정, 삭제 (CRUD) 등을 하기 위한 명령어</li>
<li>예시<ul>
<li>INSERT (C)</li>
<li>SELECT (R)</li>
<li>UPDATE (U)</li>
<li>DELETE (D)</li>
</ul>
</li>
</ul>
</li>
<li>DCL (Data Control Language)<ul>
<li>데이터 제어 언어</li>
<li>데이터베이스 사용자의 권한 제어를 위해 사용하는 명령어</li>
<li>예시<ul>
<li>GRANT</li>
<li>REVOKE</li>
<li>COMMIT</li>
<li>ROLLBACK</li>
</ul>
</li>
</ul>
</li>
</ul>
<br>

<h3 id="sql-statement">SQL statement</h3>
<pre><code class="language-sql">-- 테이블 생성 및 삭제
CREATE TABLE &lt;테이블명&gt; (컬럼...);
DROP TABLE &lt;테이블명&gt;;

-- CRUD
# CREATE
INSERT INTO &lt;테이블명&gt; (컬럼1, 컬럼2, ...) VALUES (값1, 값2, ...);

# READ
SELECT 컬럼1, 컬럼2, ... FROM &lt;테이블명&gt;;
SELECT 컬럼1, 컬럼2, ... FROM &lt;테이블명&gt; LIMIT 숫자;
SELECT 컬럼1, 컬럼2, ... FROM &lt;테이블명&gt; LIMIT 숫자 OFFSET 숫자;
SELECT 컬럼1, 컬럼2, ... FROM &lt;테이블명&gt; WHERE 조건;
SELECT DISTINCT 컬럼 FROM &lt;테이블명&gt;;

# DELETE
DELETE FROM &lt;테이블명&gt; WHERE 조건;

# UPDATE
UPDATE &lt;테이블명&gt; SET 컬럼1=값1, 컬럼2=값2, ... WHERE 조건;</code></pre>
<br>

<h4 id="autoincrement">AUTOINCREMENT</h4>
<ul>
<li>컬럼 attribute</li>
<li>이전에 삭제된 행의 값을 재사용하는 것을 방지</li>
<li>SQLite에서는 사용되지 않음</li>
<li>테이블을 생성하는 단계에서 설정 가능</li>
</ul>
<pre><code class="language-sql">CREATE TABLE &lt;테이블명&gt; (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    ...
    );</code></pre>
<br>

<h3 id="sqlite-aggregate-functions">SQLite Aggregate Functions</h3>
<ul>
<li><p>COUNT</p>
<ul>
<li>그룹의 항목 수를 가져옴</li>
</ul>
</li>
<li><p>AVG</p>
<ul>
<li>값 집합의 평균 값을 계산</li>
</ul>
</li>
<li><p>MAX</p>
<ul>
<li>그룹에 있는 모든 값의 최대값을 가져옴</li>
</ul>
</li>
<li><p>MIN</p>
<ul>
<li>그룹에 있는 모든 값의 최소값을 가져옴</li>
</ul>
</li>
<li><p>SUM</p>
<ul>
<li>모든 값의 합을 계산</li>
</ul>
<pre><code class="language-sqlite">SELECT COUNT(컬럼) FROM &lt;테이블명&gt;;
SELECT AVG(컬럼) FROM &lt;테이블명&gt;;
SELECT MAX(컬럼) FROM &lt;테이블명&gt;;
SELECT MIN(컬럼) FROM &lt;테이블명&gt;;
SELECT SUM(컬럼) FROM &lt;테이블명&gt;;</code></pre>
</li>
</ul>
<br>

<h4 id="like">LIKE</h4>
<ul>
<li>패턴 일치를 기반으로 데이터를 조회하는 방법</li>
<li>sqlite는 패턴 구성을 위한 2개의 wildcards를 제공<ul>
<li>% (percent sign)<ul>
<li>0개 이상의 문자</li>
<li>이 자리에 문자열이 있을 수도, 없을 수도 있다.</li>
</ul>
</li>
<li>_ (underscore)<ul>
<li>임의의 단일 문자</li>
<li>반드시 이 자리에 한 개의 문자가 존재해야 한다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code class="language-sql">SELECT * FROM 테이블 WHERE 컬럼 LIKE &#39;와일드카드패턴&#39;;</code></pre>
<br>

<h4 id="order-by">ORDER BY</h4>
<ul>
<li>조회 결과 집합을 정렬</li>
<li>SELECT 문에 추가하여 사용</li>
<li>정렬 순서를 위한 2개의 키워드 제공<ul>
<li>ASC - 오름차순 (default)</li>
<li>DESC - 내림차순</li>
</ul>
</li>
</ul>
<pre><code class="language-sql">SELECT * FROM 테이블 ORDER BY 커럼 ASC;
SELECT * FROM 테이블 ORDER BY 컬럼1, 컬럼2 DESC;</code></pre>
<br>

<h4 id="group-by">GROUP BY</h4>
<ul>
<li>행 집합에서 요약 행 집합을 만듦</li>
<li>SELECT 문의 optional 절</li>
<li>선택된 행 그룹을 하나 이상의 열 값으로 요약 행으로 만듦</li>
<li>문장에 WHERE 절이 포함된 경우 반드시 WHERE 절 뒤에 작성해야 함</li>
</ul>
<pre><code class="language-SQL">SELECT 컬럼1, aggregate_function(컬럼2) FROM 테이블 GROUP BY 컬럼1, 컬럼2;</code></pre>
<br>

<h4 id="alter-table">ALTER TABLE</h4>
<ul>
<li>테이블 이름 변경</li>
</ul>
<pre><code class="language-sql">ALTER TABLE current_table_name RENAME TO new_table_name;</code></pre>
<ul>
<li>테이블 컬럼 명 변경</li>
</ul>
<pre><code class="language-SQL">ALTER TABLE table_name
RENAME COLUMN current_name TO new_name;</code></pre>
<ul>
<li>테이블에 새로운 컬럼 추가</li>
</ul>
<pre><code class="language-sql">ALTER TABLE 테이블이름 ADD COLUMN 컬럼이름 데이터타입설정;</code></pre>
<br>]]></description>
        </item>
        <item>
            <title><![CDATA[Database | utf8 → utf8mb4 인코딩 변경하기]]></title>
            <link>https://velog.io/@yb_engineer/Database-utf8-utf8mb4-%EC%9D%B8%EC%BD%94%EB%94%A9-%EB%B3%80%EA%B2%BD%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@yb_engineer/Database-utf8-utf8mb4-%EC%9D%B8%EC%BD%94%EB%94%A9-%EB%B3%80%EA%B2%BD%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 13 Feb 2022 07:38:49 GMT</pubDate>
            <description><![CDATA[<p>프로젝트를 배포하기 전 여러가지 테스트를 진행하던 중 텍스트를 입력하는 부분, 예컨대 &#39;자기소개&#39;, &#39;팀소개&#39;, &#39;채팅&#39; 등에서 이모티콘/이모지 입력이 안되는 오류를 발견하였다.</p>
<p>문제의 원인은 DB의 특정 컬럼 <strong>Character Set</strong> 이 <code>utf8</code> 로 설정되어 있어서이다.</p>
<p><code>utf8</code> 인코딩이 3 Bytes로 처리되는 반면, 이모지는 65,536 범위를 넘어가서 4 Bytes로 처리되기 때문에 DB에 입력이 안되는 것이다.</p>
<br>

<p>문제 해결을 위해 프로젝트에 사용하는 MySql의 <strong>데이터베이스</strong>, <strong>테이블</strong>, <strong>컬럼</strong> 에 대한 인코딩 수정을 진행하였다.</p>
<br>

<pre><code class="language-sql"># 데이터베이스 수정
ALTER DATABASE [database_name] CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;</code></pre>
<br>

<pre><code class="language-sql"># 테이블 수정
ALTER TABLE [db.table_name] CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;</code></pre>
<br>

<pre><code class="language-sql"># 컬럼 수정
ALTER TABLE [db.table_name] CHANGE [column_name] [column_name] VARCHAR(***) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;</code></pre>
</br>

<p>오류를 수정하면서 보니, 데이터베이스의 인코딩 설정만 변경한다고 해서 오류가 잡히는 것이 아니었다. <strong>DB 자체의 인코딩과 더불어 이모지가 사용될만한 테이블/컬럼에 대한 수정이 필요</strong>했다.</p>
<p>또한, 특정 테이블에서는 다르게 맞물려 있는 문제때문에 테이블 자체에 대한 인코딩 수정이 안될 때도 있었다. 이때는, <strong>특정 테이블 - 특정 컬럼만 따로 골라서 인코딩을 수정</strong>했다. 위의 3번째 코드 블럭의 경우이다.</p>
<br>

<p><img src="https://images.velog.io/images/yb_engineer/post/fa80a510-7ef2-4612-8cee-79b1e433b67c/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/yb_engineer/post/ddc5f114-6e49-4ff7-b20e-18b4bcf43a97/image.png" alt=""></p>
<p>정상적으로 이모지가 작성되고, DB에도 잘 저장되는 것을 확인할 수 있다.</p>
<hr>
<h4 id="참고자료">참고자료</h4>
<p><a href="https://solbel.tistory.com/1062">https://solbel.tistory.com/1062</a></p>
<p><a href="https://nakanara.tistory.com/230">https://nakanara.tistory.com/230</a></p>
<p><a href="https://blog.naver.com/deersoul6662/222621541412">https://blog.naver.com/deersoul6662/222621541412</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Server | EC2 & Docker Deploy with Jenkins Ⅲ]]></title>
            <link>https://velog.io/@yb_engineer/Server-EC2-Docker-Deploy-with-Jenkins-rckdi742</link>
            <guid>https://velog.io/@yb_engineer/Server-EC2-Docker-Deploy-with-Jenkins-rckdi742</guid>
            <pubDate>Fri, 04 Feb 2022 06:39:02 GMT</pubDate>
            <description><![CDATA[<br>

<p>2편에서 진행한 Docker 및 Jenkins 설치가 정상적으로 완료되었다면, 마지막으로 Web Hook으로 GitHub와 Jenkins를 연결하고 Jenkins 자동 배포 과정을 진행해보자.</p>
<br>

<h3 id="5-github---jenkins-web-hook">5. GitHub - Jenkins Web Hook</h3>
<p>웹 개발에서 Web Hook이란?</p>
<p>사용자 정의 callback 을 이용하여 웹페이지나 애플리케이션의 동작을 향상시키거나 변경하는 방법이다.</p>
<br>

<p>앞서 우리는 Github Repository의 Master 브랜치에 코드 푸쉬나 풀리퀘스트(PR) 같은 Action이 일어날 때 이를 Jenkins에서 캐치해야 한다고 이야기했다.</p>
<p>GitHub 과 Jenkins는 엄연히 다른 소프트웨어이고 서비스인데 한 쪽에서 일어난 변화를 어떻게 캐치할 수 있을까? </p>
<p>이 때 사용하는 것이 바로 <strong>Web Hook</strong>이다. GitHub 과 Jenkins를 Web Hook으로 연결해여 상태 변화를 캐치. 빌드. 배포할  수 있다.</p>
<br>

<p>지금부터 Web Hook 설정에 대해서 알아보자.</p>
<br>

<ol>
<li>먼저 Jenkins &gt; Jenkins 관리 &gt; 플러그인 관리에서 <strong>GitHub Integration Plugin</strong> 을 설치한다.</li>
</ol>
<p><img src="https://images.velog.io/images/yb_engineer/post/6c059587-b9b2-456f-b594-e6eaca8dd768/image.png" alt=""></p>
<br>

<ol start="2">
<li>다음으로, GitHub에서 개인 Token을 발급받아야 한다.</li>
</ol>
<p><img src="https://images.velog.io/images/yb_engineer/post/e64f16f0-fb47-4f83-a1b2-9f18e6b0e9a7/image.png" alt=""></p>
<p>깃허브 개인 프로필을 확인하는 메뉴에서 Settings에 들어간다. 메뉴 중 <strong>Developer settings</strong> 에서 <strong>Personal access tokens</strong> 메뉴를 클릭한다.</p>
<br>

<p><img src="https://images.velog.io/images/yb_engineer/post/8a9ea003-5866-4c9b-8b93-57ffe25dfd59/image.png" alt=""></p>
<p><strong>Generate new token</strong> 을 클릭하여 새로운 토큰을 생성한다. Note, Expiration을 설정하고 Select scopes를 설정한다. 나의 경우,  <code>repo</code> 와 <code>admin:repo_hook</code> 을 선택하였다.</p>
<br>

<br>

<hr>
<h5 id="🙋♂️-잠깐-">🙋‍♂️ 잠깐 !!!</h5>
<p>아래와 같이 EC2 서버 자체에 Git Repository를 Clone할 때 권한 문제가 발생할 수 있다. 이 문제 역시 Personal access token을 통해서 문제를 해결 할 수 있다.</p>
<ul>
<li>참고 자료 : <a href="https://curryyou.tistory.com/344">https://curryyou.tistory.com/344</a></li>
</ul>
<p><img src="https://images.velog.io/images/yb_engineer/post/6721f15c-f11d-48e5-9e4e-b6bc9a3d434e/image.png" alt=""></p>
<hr>
<br>

<p>Token 생성이 완료되면 다시 EC2 서버로 돌아와서 Jenkins 관련 키를 생성한다.</p>
<ul>
<li>SSH 키 생성</li>
</ul>
<pre><code class="language-bash">$ sudo mkdir /var/lib/jenkins/.ssh
$ sudo ssh-keygen -t rsa -f /var/lib/jenkins/.ssh/id_rsa</code></pre>
<br>

<p>키 생성이 완료되면 이를 GitHub Deploy Key로 등록해야 한다.</p>
<p>우리 프로젝트가 업로드 되어 있는 GitHub Repository &gt; Settings &gt; Deploy Keys &gt; Add deploy key 를 클릭한다.</p>
<p><img src="https://images.velog.io/images/yb_engineer/post/0d7604a4-8620-4416-88c5-1eb125322264/image.png" alt=""></p>
<br>

<pre><code class="language-bash">$ cat id_rsa.pub</code></pre>
<p>위 명령어로 Public Key의 값을 찾고 해당하는 값을 <strong>Key</strong> 로 등록한다.</p>
<br>

<p>그리고 GitHub에 Web Hook 설정을 하기 위해서 GitHub Repository &gt; Settings &gt; Webhooks &gt; Add webhook을 클릭한다.</p>
<p><img src="https://images.velog.io/images/yb_engineer/post/40ca778c-42ea-40f7-b6b3-ae1d8dd5b411/image.png" alt=""></p>
<br>

<ul>
<li>Payload URL : <code>http://{IP or Domain}:{Jenkins Port}/github-webhook</code> 으로 작성해야 한다.</li>
<li>Content type : <code>application/json</code> 으로 설정</li>
<li>Which events ~<ul>
<li>Let me select individual events 로 설정 후 개인이 원하는 상황/Action에서의 Web Hook 설정이 가능하다</li>
</ul>
</li>
</ul>
<p><img src="https://images.velog.io/images/yb_engineer/post/367d3e47-6314-449d-82dd-cf589a6f37c7/image.png" alt=""></p>
<br>

<ul>
<li>Jenkins Credential 등록</li>
</ul>
<p>Jenkins 관리 &gt; Manage Credentials &gt; Credentials &gt; Domains (global) &gt; Add credentials 클릭하고,</p>
<p>종류는 <code>SSH Username with private key</code>로 선택한다.</p>
<p>Private Key &gt; Enter directly 체크 후, 프라이빗 키를 입력한다.</p>
<p>Public Key를 찾을 때 처럼 <code>cat</code> 명령어를 사용하여 Private Key를 찾고 값을 넣어준다.</p>
<pre><code class="language-bash"># Private Key 찾기
$ cat id_rsa</code></pre>
<br>

<p>마지막으로 Jenkins에서 프로젝트 설정을 진행한다.</p>
<h5 id="jenkins-item-생성하기">Jenkins Item 생성하기</h5>
<ul>
<li><p>Item 생성하기 - <code>Freestyle project</code></p>
</li>
<li><p>Github 정보 입력</p>
<ul>
<li>소스코드 관리 &gt; Git</li>
<li><code>http</code> 로 시작하는 Github Repo 주소를 입력하면 아래와 같은 오류가 발생하게 된다.</li>
</ul>
</li>
</ul>
<p><img src="https://images.velog.io/images/yb_engineer/post/b59c584f-8eab-4413-8b8f-a56c2656d07f/image.png" alt=""></p>
<p>GitHub Repository 주소를 <code>https</code> 타입 대신 <code>ssh</code> 타입으로 하여 Git URL을 설정하면 오류를 해결할 수 있다.</p>
<p><img src="https://images.velog.io/images/yb_engineer/post/6135a973-df91-44ca-8a16-333cb9cc0e79/image.png" alt="">
에러가 사라진 것을 볼 수 있다.</p>
<br>

<p><strong>Credentials</strong>는 직전에 Private Key를 등록할 때 생성한 Credential을 설정한다.</p>
<br>

<p>Web Hook을 이용하여 GitHub와 Jenkins를 연결하였기 때문에 <strong>빌드 유발</strong>은 &#39;GitHub hook trigger for GITScm polling&#39; 으로 설정한다.</p>
<p><img src="https://images.velog.io/images/yb_engineer/post/4fc307a5-498e-497d-a2bb-d63b1fd89b80/image.png" alt=""></p>
<br>

<p><strong>Build</strong> 관련 shell script를 작성하고 나면 Jenkins 설정은 끝이 난다.</p>
<h5 id="build---execute-shell">Build - Execute shell</h5>
<pre><code class="language-bash"># 1. gradle build
chmod +x gradlew       # Permission Denied 권한 오류를 해결하기 위한 명령어
./gradlew clean build</code></pre>
<pre><code class="language-bash"># 2. Docker build
docker build --tag==[태그이름] .</code></pre>
<pre><code class="language-bash"># 3. Docker run
docker ps -q --filter name=[태그이름] | grep -q . &amp;&amp; docker rm -f $(docker ps -aq --filter name=[태그이름])
docker run -d --name 태그이름 -p [EC2포트]:[Docker포트] [태그이름]:latest</code></pre>
<br>

<p>1번과 2번은 기본적인 gradle 빌드와 docker 빌드 명령어이다. </p>
<p>Ubuntu에서 사용자에 따른 권한 오류 문제가 자주 발생한다. Gradle 빌드를 할 때에도 권한 오류가 발생할 수 있으므로, <code>chmod</code> 권한부여 명령어를 선행하여 작성해준다.</p>
<p><img src="https://images.velog.io/images/yb_engineer/post/2a45cb02-947e-4f43-a5a3-6c45d6271b63/image.png" alt=""></p>
<br>

<p>3번 Docker Run 명령어의 경우,</p>
<p>첫째줄은 기존 실행중인 Docker Container 중에서 [태그이름]이 포함되어 있는 컨테이너를 조회하고, 삭제하는 명령어이다.</p>
<p>둘째줄은 [태그이름]을 지정하고 EC2포트와 Docker포트를 연결하면서 Docker 이미지를 실행시키는 명령어이다. 내가 진행한 프로젝트의 경우 Spring Boot 프로젝트를 8081 포트로 할당하였기 때문에 <code>8081:8081</code> 로 설정하였다.</p>
<br>

<p>위 과정이 문제 없이 진행된다면, 실제로 코드 작성을 하거나 Pull Request를 보내서 Git Hub를 업데이트 해보자.</p>
<p>아래와 같이 자동으로 Build &amp; Run이 진행되고 과정 중에 오류가 없다면 정상적으로 실행되었다는 메세지를 볼 수 있을 것이다.</p>
<p><img src="https://images.velog.io/images/yb_engineer/post/3482bc1e-ef99-4c13-a2e2-12165fcb0987/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Server | EC2 & Docker Deploy with Jenkins Ⅱ]]></title>
            <link>https://velog.io/@yb_engineer/Server-EC2-Docker-Deploy-with-Jenkins-g0qeqehf</link>
            <guid>https://velog.io/@yb_engineer/Server-EC2-Docker-Deploy-with-Jenkins-g0qeqehf</guid>
            <pubDate>Fri, 04 Feb 2022 06:35:22 GMT</pubDate>
            <description><![CDATA[<br>

<p>지난 1편에 이어서, 배포 과정을 계속하기 위해서는 AWS EC2 서버에 Docker와 Jenkins가 깔려있어야 한다. 먼저 Docker를 깔아보자.</p>
<br>

<h3 id="3-docker-설치">3. Docker 설치</h3>
<pre><code class="language-bash">$ sudo apt install docker.io

$ sudo systemctl status docker</code></pre>
<p>위 명령어를 통해서 EC2 서버에 Docker를 설치하고 정상적으로 설치되었는지 상태  확인을 해보자.</p>
<br>

<p>Docker 뿐만 아니라 Ubuntu 서버에서는 파일이나 디렉토리 작성/수정/삭제와 같은 행동에 대해서 권한 오류가 빈번히 발생한다. Docker의 경우에도 아래와 같은 권한 오류가 발생할 수 있다.</p>
<br>

<p><img src="https://images.velog.io/images/yb_engineer/post/33c2a5ee-e845-44a0-9b0b-e7c43043fe7d/image.png" alt=""></p>
<br>

<p><img src="https://images.velog.io/images/yb_engineer/post/b0adf209-9fc3-4e55-bda8-ddd5bd4ed83c/image.png" alt=""></p>
<br>

<p>권한을 부여하는 <code>chmod</code> 명령어로 파일에 대한 권한 변경을 할 수 있고, 위와 같은 오류를 해결할 수 있다.</p>
<ul>
<li>참고 자료 - <a href="https://github.com/occidere/TIL/issues/116">https://github.com/occidere/TIL/issues/116</a></li>
</ul>
<pre><code class="language-bash">$ sudo chmod 666 /var/run/docker.sock</code></pre>
<br>

<p>권한 오류를 해결하고 <code>docker run</code> 명령어를 입력하면 아래와 같이 정상적으로 Spring Boot 서버가 실행되는  것을 확인할 수 있다.</p>
<p><img src="https://images.velog.io/images/yb_engineer/post/007b285c-f5e0-4108-9606-dcb7eed2d1eb/image.png" alt=""></p>
<ul>
<li><code>docker run -p [EC2포트]:[docker포트] [컨테이너 ID]</code></li>
</ul>
<br>

<br>

<hr>
<h5 id="🙅♂️-잠깐">🙅‍♂️ 잠깐!!!</h5>
<p><code>chmod</code> 명령어를 서버 루트나 특정 디렉토리에서 잘못 사용하게 되면 서버 자체에 대한 접근이 제한될 수 있으므로 주의해서 사용하자.</p>
<hr>
<br>

<br>

<h5 id="docker-기본-명령어">Docker 기본 명령어</h5>
<pre><code class="language-bash">$ docker images # 도커 이미지 확인

$ docker rm &lt;id&gt; # 도커 컨테이너 삭제

$ docker rmi &lt;id&gt; # 도커 이미지 삭제

$ docker run  &lt;image 이름&gt; # 도커 이미지 실행</code></pre>
<br>

<hr>
<br>

<p>기본적으로 Docker를 설치하고 정상적으로 작동하는 것을 확인했다면, 다음으로 Docker 컨테이너로 Jenkins를 띄우기 위해서 Jenkins를 설치해보자.</p>
<br>

<h3 id="4-jenkins-설치">4. Jenkins 설치</h3>
<br>

<ul>
<li>패키지 업데이트</li>
</ul>
<pre><code class="language-bash">$ wget -q -O - https://pkg.jenkins.io/debian/jenkins.io.key | sudo apt-key add -
echo deb http://pkg.jenkins.io/debian-stable binary/ | sudo tee /etc/apt/sources.list.d/jenkins.list
$ sudo apt update</code></pre>
<br>

<ul>
<li>GPG Error 발생시<ul>
<li>Error 로그에 나타난 16 자리의 키를 활용. 아래 코드를 작성한다.</li>
</ul>
</li>
</ul>
<pre><code class="language-bash">$ sudo apt-key adv --keyserver  keyserver.ubuntu.com --recv-keys [16자리키]</code></pre>
<br>

<p>이후 <code>sudo apt update</code> 명령어로 업데이트를 재시도한다.</p>
<br>

<ul>
<li>Jenkins 설치 및 실행 확인</li>
</ul>
<pre><code class="language-bash">$ sudo apt-get install jenkins
$ sudo systemctl status jenkins</code></pre>
<br>

<p>Jenkins 접속 포트 변경이 필요할 시 아래 코드로 포트  변경이 가능하다. 나의 경우 9000번 포트로 변경하고 사용하였다.</p>
<pre><code class="language-bash">$ sudo vi /etc/default/jenkins

# 포트 변경
HTTP_PORT = 9000</code></pre>
<br>

<p>위  설정이 다 끝나면 Jenkins 서비스를 재시작 하자.</p>
<pre><code class="language-bash">$ sudo service jenkins restart</code></pre>
<br>

<p>Jenkins에 접속하기 위해서는 초기 비밀번호가 필요하다.  아래 명령어로 초기  비밀번호를 미리 확인해두자.</p>
<pre><code class="language-bash">$ sudo cat /var/lib/jenkins/secrets/initialAdminPassword</code></pre>
<br>

<ul>
<li><code>EC2 도메인:9000</code> 포트로 접속 후, 초기비밀번호 입력</li>
</ul>
<p><img src="https://images.velog.io/images/yb_engineer/post/347ed32d-eb27-4214-b4ef-0d5f75693e5b/image.png" alt=""></p>
<br>

<ul>
<li>플러그인 설치</li>
</ul>
<p><img src="https://images.velog.io/images/yb_engineer/post/9352a950-f5f3-439d-bf2f-182824988322/image.png" alt=""></p>
<br>

<ul>
<li>플러그인 설치가 완료되면 젠킨스 설치 끝!</li>
</ul>
<p><img src="https://images.velog.io/images/yb_engineer/post/45512a0d-6642-421a-91cf-c033721a7437/image.png" alt=""></p>
<br>

<p>간단하게 EC2 서버에 Jenkins를 설치하고, 초기 비밀번호를 입력하고 접속 후 플러그인 설치까지 완료하였다.</p>
<p>위 과정이 완료되면 다음 단계로 넘어갈 수 있다.</p>
<hr>
<br>

<h4 id="server--ec2--docker-deploy-with-jenkins-ⅲ-에서-계속"><a href="https://velog.io/@yb_engineer/Server-EC2-Docker-Deploy-with-Jenkins-rckdi742">Server | EC2 &amp; Docker Deploy with Jenkins Ⅲ</a> 에서 계속...</h4>
]]></description>
        </item>
        <item>
            <title><![CDATA[Server | EC2 & Docker Deploy with Jenkins Ⅰ]]></title>
            <link>https://velog.io/@yb_engineer/Server-EC2-Docker-Deploy-with-Jenkins</link>
            <guid>https://velog.io/@yb_engineer/Server-EC2-Docker-Deploy-with-Jenkins</guid>
            <pubDate>Fri, 04 Feb 2022 06:33:13 GMT</pubDate>
            <description><![CDATA[<br>

<blockquote>
<p>Spring Boot 기반의 프로젝트를 진행하고 있다. </p>
<p>AWS EC2 클라우드 서버에 배포를 하고, Docker를 이용하여 컨테이너화하여 이미지를 띄우고, Jenkins와 GitHub 저장소를 연결하여 자동으로 배포하는 간단한 CI/CD 과정을 경험하면서 다양한 오류와 시행착오를 겪었다.</p>
<p>이 과정들을 정리해놓지 않으면 당장 며칠 뒤면 무조건 잊어버릴 것이라는 생각에 기록으로 남기고자 한다.</p>
</blockquote>
<br>

<p>본격적인 기록 작성에 앞서, 전반적인 흐름을 그림으로 표현해 보았다.</p>
<p><img src="https://images.velog.io/images/yb_engineer/post/3f8e9cb3-a078-42cd-8b0a-1c5340b60710/image.png" alt=""></p>
<p><strong>왼쪽</strong> 에서 <strong>오른쪽</strong> 으로 흐름이 이어진다고 보면 된다.</p>
<br>

<p>전체적인 배포 과정은 다음과 같다.</p>
<ol>
<li><strong>Local</strong> 에서 개발을 진행한다.</li>
<li><strong>GitHub (또는 GitLab) Repository</strong>에 코드 Push나 Pull Request와 같은 Action이 발생한다.</li>
<li>GitHub Repository와 EC2에서 Docker 컨테이너로 띄워진 Jenkins는 <strong>Web Hook</strong>으로 연결되어 있다. Action이 발생하면 Web Hook을 통해서 Jenkins에서 이를 캐치하고 사전에 설정된대로 빌드가 이루어진다.</li>
<li>Gradle을 통해서 Spring Boot 프로젝트에 대한 <strong>Build</strong>가 이루어지고, Dockerfile을 통해서 <strong>Docker Image</strong>가 생성된다.</li>
<li>이후 만들어진 <strong>Docker Image</strong>가 <strong>Run</strong>(실행)되면서 새로운 Docker 컨테이너로 <strong>Spring Boot 프로젝트를 띄운다</strong>.</li>
</ol>
<br>

<p>참고로 데이터베이스(DB)의 안정성을 위해서 MySQL과 Redis DB는 Docker 컨테이너가 아닌, EC2 서버 자체에 설치하였다.</p>
<br>

<p>지금부터 각 과정에 대한 설명과 그 안에서 겪었던 여러 오류에 대해서 기록하겠다.</p>
<hr>
<br>

<h3 id="1-local에서-개발-진행">1. Local에서 개발 진행</h3>
<p>다들 알고 있듯이 이 과정에 대해서는 특별히 설명할 부분이 없다. 개발자들이 진행하는 프로젝트의 성격도 다르고, 그에 따른 기술 스택.. 사용 언어나 프레임워크, 라이브러리 등이 모두 천차만별일 것이다.</p>
<br>

<p>내가 진행하고 있는 프로젝트를 기준으로 하면,</p>
<ul>
<li><p>Language - <code>JAVA 8</code></p>
</li>
<li><p>Framework - <code>Spring Boot</code></p>
</li>
<li><p>Build Tool - <code>Gradle 7.3.2</code></p>
</li>
<li><p>DB - <code>MySQL</code>, <code>Redis</code>, <code>H2</code></p>
</li>
<li><p>Library - <code>SMTP</code>, <code>Gson</code>, <code>jwt</code> ...</p>
</li>
<li><p><code>JPA</code>, <code>Hibernate</code>, <code>JUnit</code> ...</p>
</li>
</ul>
<p>등 다양한 프레임워크, 라이브러리, 툴을 활용하고 있다.</p>
<br>

<p>기획한 프로젝트에 따라서 각자 맡은 파트에 대한 개발을 성실하고 열심히 진행하면 된다!</p>
<br>

<p>그리고 사실 이제 개발을 배우기 시작한 햇병아리 주니어라서 이 부분에 대해서 이렇다 저렇다 할 짬밥이 안된다..</p>
<br>

<p>최선을 다해서 수없이 많은 오류에 부딪혀 가며 개발을 진행하자💪</p>
<hr>
<br>

<h3 id="2-github-repository">2. GitHub Repository</h3>
<p>개발자라면 누구나 GitHub 혹은 GitLab 같은 서비스를 이용하고 있을 것이다.</p>
<p>작게는 개인이 학습한 내용이나 작성한 코드를 올릴 수 있고, 크게는 회사에서 진행하는 프로젝트나 운영하는 서비스의 방대한 코드를 올리면서 버전 관리를 할 수 있는 서비스이다.</p>
<p>더 자세한 얘기를 하기에는 이 글은 Git 이나 GitHub보다는 <strong>&quot;배포&quot;</strong> 에 초점이 맞추어져 있으므로 넘어가도록 하겠다.</p>
<p>우리의 <strong>구</strong> 선생님은 모르는 것이 없기 때문에 혹시 Git이나 GitHub를 모른다면 당장 선생님에게 질문하도록 하자.</p>
<br>

<br>

<p>일반적으로 1단계 - 로컬에서 코드를 작성하고나면 개발자들은 GitHub Repository에 코드를 Push하게 된다.</p>
<p>우리팀의 경우 깃헙 저장소의 브랜치를 크게 master | develop | feature branch 3가지로 분기하였다. </p>
<p><strong>feature/{기능}</strong> 브랜치를 따서 각 기능에 대한 코드를 작성하고, 코드 구현이 완료되면 이를 <strong>develop</strong> 브랜치에 Merge한다. Bug fix나 Refactoring이 끝난 코드는 다시 <strong>master</strong> 브랜치에 이관하면서 배포 준비를 한다.</p>
<br>

<p>다른 브랜치에서 Push나 Pull Request와 같은 상황이 발생하는 것은 무시하고 <strong>master</strong> 브랜치에서 Push나 PR 같은 Action 이 일어날 때 이를 캐치하려고 한다.</p>
<p>그 이유는 master 브랜치가 배포를 하기 위한 브랜치이고 또 배포를 할 수 있는 브랜치이기 때문이다. 코드에 버그가 없고 배포하여 사용자들이 이용할 수 있는 상태라고 여겨지는 branch이기 때문에 해당 브랜치에 상태 변화가 일어나는 것을 캐치할 것이다. 물론 master 브랜치에 있는 코드에도 버그가 있을 수 있어서 이를 <strong>hotfix</strong> 브랜치에서 잡아내기도 한다.</p>
<br>

<p>다음 단계에서는 우리 프로젝트 소스 코드가 올라가 있는 GitHub Repository의 master 브랜치와 CI/CD Tool인 Jenkins 간 연결에 대해서 살펴볼 것이다.</p>
<hr>
<br>

<h4 id="server--ec2--docker-deploy-with-jenkins-ⅱ-에서-계속"><a href="https://velog.io/@yb_engineer/Server-EC2-Docker-Deploy-with-Jenkins-g0qeqehf">Server | EC2 &amp; Docker Deploy with Jenkins Ⅱ</a> 에서 계속...</h4>
]]></description>
        </item>
        <item>
            <title><![CDATA[객체지향의 사실과 오해 | 1장. 협력하는 객체들의 공동체]]></title>
            <link>https://velog.io/@yb_engineer/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%EC%9D%98-%EC%82%AC%EC%8B%A4%EA%B3%BC-%EC%98%A4%ED%95%B4-1%EC%9E%A5.-%ED%98%91%EB%A0%A5%ED%95%98%EB%8A%94-%EA%B0%9D%EC%B2%B4%EB%93%A4%EC%9D%98-%EA%B3%B5%EB%8F%99%EC%B2%B4</link>
            <guid>https://velog.io/@yb_engineer/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%EC%9D%98-%EC%82%AC%EC%8B%A4%EA%B3%BC-%EC%98%A4%ED%95%B4-1%EC%9E%A5.-%ED%98%91%EB%A0%A5%ED%95%98%EB%8A%94-%EA%B0%9D%EC%B2%B4%EB%93%A4%EC%9D%98-%EA%B3%B5%EB%8F%99%EC%B2%B4</guid>
            <pubDate>Tue, 21 Sep 2021 08:31:25 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>최근 프로그래밍을 공부하면서 단순히 &#39;코딩&#39;을 잘하는 것이 중요한 것이 아니라는 사실을 많이 느끼고 있다.
 컴퓨터 구조, 운영체제, 자료구조와 알고리즘 같이 Computer Science에 대한 기본적인 지식이 기반이 되면서 동시에 코드를 잘 짜는, 프로그래밍을 잘하는 사람이 회사에 필요한 인재이며 소위 말하는 몸값 높은 개발자가 될 수 있는 길이라고 생각한다.
 주말마다 CS 스터디를 하고, 원서 읽기 스터디를 하고 있지만 별개로 어떤 책을 읽으면 개발자로써 한걸을 더 나아갈 수 있을까 고민하던 차에 싸피에서 같이 공부하는 상필이가 이 책을 추천해주었다.
 책을 한 번 읽어보자! </p>
</blockquote>
<br>
 앞으로 책의 챕터별로 내용과 내 생각을 정리하고자 한다. 모든 내용은 객체지향의 사실과 오해 책에서 발췌한 것이며 저자 조영호 님에게 저작권이 있음을 밝힌다. 이제 겨우 1장 읽었지만 나같은 비전공자 개발 초짜가 읽으면서 이해하기 쉽게 잘 쓰신 것 같다. 😁
<br><br>

<h1 id="목차">목차</h1>
<h3 id="1장-협력하는-객체들의-공동체">1장. 협력하는 객체들의 공동체</h3>
<ol>
<li>협력하는 사람들</li>
<li>역할, 책임, 협력</li>
<li>협력 속에 사는 객체</li>
<li>객체지향의 본질<br>

</li>
</ol>
<p>*<em>객체 지향의 핵심은 협력, 역할, 책임 이다. *</em></p>
<h3 id="협력하는-사람들">협력하는 사람들</h3>
<p>대부분의 사람들은 &quot;객체지향이란, 실세계를 직접적이고 직관적으로 모델링할 수 있는 패러다임&quot; 이라고 한다 (나도 이렇게 배운 것 같다).
즉 객체 지향이란, 현실세계에 존재하는 사물에 대한 추상화. 라는 것이 대부분 사람들이 객체 지향을 정리하는 한 문장이다. 사실 나도 이 얘기를 들으면서, 이게 뭔소리인지 의아했었다.
물론 위 문장이 틀린 말은 아니다. 객체 지향이라는 개념을 이해하기 위해서는 실세계를 비유로 들고, 실세계를 모방으로 한다는 말 자체가 효과적이라고 한다.</p>
<p>저자는 실세계의 예시를 통해 객체지향을 설명한다.</p>
<p>이른 아침 출근해서 사내 카페에서 커피를 주문하는 사람과 주문을 받는 캐셔 그리고 커피를 제조하는 바리스타 예시를 통해서 객체지향의 핵심을 이해할 수 있다.</p>
<blockquote>
<p>커피를 주문하는 사람과 캐셔 그리고 바리스타는 각자의 역할(role)과 책임(responsibility) 속에서 커피를 주문하고 제조하는 협력(collaboration)의 과정을 이루어 간다.</p>
</blockquote>
<p>이 때, 요청(request)과 응답(response)을 통해서 협력이 이루어지고 이를 통해서 인간은 거대하면서 복잡한 문제를 해결할 수 있는 공동체를 형성할 수 있다. </p>
</br>

<h3 id="역할-책임-협력">역할, 책임, 협력</h3>
<p>시스템은 역할과 책임을 수행하는 <strong>객체</strong>로 분할되고 시스템의 기능은 객체 간 연쇄적인 요청-응답의 흐름으로 구성된 협력으로 구현된다.</p>
<p>객체의 역할은 다음과 같은 특징을 지닌다.</p>
<ul>
<li>여러 객체가 동일한 역할을 수행할 수 있다.</li>
<li>역할은 대체 가능(substitutable)하다.</li>
<li>각 객체는 책임을 수행하는 방법을 자율적으로 선택할 수 있다.</li>
<li>하나의 객체가 동시에 여러 역할을 수행할 수 있다.</li>
</ul>
<p>대체 가능한 역할과 책임은 객체지향 패러다임의 중요한 기반을 제공하는 다형성과도 깊이 연관되어 있다.</p>
</br>

<h3 id="협력-속에-사는-객체">협력 속에 사는 객체</h3>
<p>객체지향 애플리케이션의 윤곽을 결정하는 것은 <strong>역할, 책임, 협력</strong> 이지만 실제로 협력에 참여하는 주체는 <strong>객체</strong> 이다.</p>
<p><strong>객체</strong> 는 두 가지 덕목을 갖추어야 한다.</p>
<ol>
<li>&quot;협력적&quot; 이어야 한다.<ul>
<li>다른 객체의 명령에 따라 행동하는 수동적인 존재를 의미하는 것은 X.</li>
<li>객체는 다른 객체의 요청에 응답할 뿐이며, 어떤 방식으로 응답할지는 객체 스스로 판단하고 결정한다.</li>
<li>요청에 응할지 여부조차 객체 스스로 결정할 수 있다.<br></li>
</ul>
</li>
<li>&quot;자율적&quot; 이어야 한다.<ul>
<li>공동의 목표를 달성하기 위해 협력에 참여하지만 스스로의 결정과 판단에 따라 행동하는 자율적인 존재</li>
<li>커피 주문의 예시에서도 캐셔는 손님에게 음료를 주문받는 절차나 바리스타에게 커피 주문 내역을 전달하는 방법을 스스로 결정한다.</li>
</ul>
</li>
</ol>
<p>객체는 <strong>상태(state)</strong> 와 <strong>행동(behavior)</strong> 을 함께 지닌 실체이다.
객체는 행동을 위해 필요한 상태를 포함하면서 동시에 특정한 행동을 수행하는 방법으로 스스로 결정할 수 있어야 함.
→ 바리스타는 커피 제조방법을 기억하고 있으며, 자신이 알고 있는 방법에 따라 스스로 커피를 제조한다.</p>
<p>&quot;자율적인 객체로 구성된 공동체는 유지보수가 쉽고, 재사용이 용이하다.&quot;</p>
<p>객체지향의 세계에서는 <strong>메세지(message)</strong> 를 통해서 요청-응답을 주고 받는다.</p>
<ul>
<li>전송하는 객체 = 송신자(sender)</li>
<li>수신하는 객체 = 수신자(receiver)</li>
</ul>
<p>수신된 메시지를 처리하는 방법 &gt; <strong>메서드(method)</strong></p>
<p>외부의 요청이 무엇인지를 표현하는 <strong>메시지</strong>와 요청을 처리하기 위한 구체적인 방법인 <strong>메서드</strong>를 분리하는 것은 객체의 자율성을 높이는 핵심 메커니즘이며, <strong>캡슐화(encapsulation)</strong>라는 개념과 관련되어 있다.</p>
</br>

<h3 id="객체지향의-본질">객체지향의 본질</h3>
<p>저자는 다음과 같이 객체지향의 개념을 정리하고 있다.</p>
<ul>
<li>객체지향이란 시스템을 상호작용하는 <strong>자율적인 객체들의 공동체</strong>로 바라보고 객체를 이용해 시스템을 분할하는 방법이다.</li>
<li>자율적인 객체란, <strong>상태</strong>와 <strong>행위</strong>를 함께 지니며 스스로 자기 자신을 책임지는 객체를 의미한다.</li>
<li>객체는 시스템의 행위를 구현하기 위해 다른 객체와 <strong>협력</strong>한다. 각 객체는 협력 내에서 정해진 <strong>역할</strong>을 수행하며 역할은 관련된 <strong>책임</strong>의 집합이다.</li>
<li>객체는 다른 객체와 협력하기 위해 메시지를 전송하고, <strong>메시지</strong>를 수신한 객체는 메시지를 처리하는데 적합한 <strong>메서드</strong>를 자율적으로 선택한다.</li>
</ul>
<p>객체지향 세계에서 클래스(class)와 클래스 사이의 상속이라는 개념도 마찬가지로 중요하지만, 결국 객체지향의 핵심은 <strong>객체</strong>이다.
클래스는 객체들의 협력관계를 코드로 옮기는 도구에 불과하다..</p>
<h4 id="객체지향은-객체를-지향하는-것이지-클래스를-지향하는-것이-아니다"><em>객체지향은 객체를 지향하는 것이지 클래스를 지향하는 것이 아니다.</em></h4>
]]></description>
        </item>
        <item>
            <title><![CDATA[Algorithm | SWEA 4880.토너먼트 카드게임 (python)]]></title>
            <link>https://velog.io/@yb_engineer/Algorithm-SWEA-4880.%ED%86%A0%EB%84%88%EB%A8%BC%ED%8A%B8-%EC%B9%B4%EB%93%9C%EA%B2%8C%EC%9E%84-python</link>
            <guid>https://velog.io/@yb_engineer/Algorithm-SWEA-4880.%ED%86%A0%EB%84%88%EB%A8%BC%ED%8A%B8-%EC%B9%B4%EB%93%9C%EA%B2%8C%EC%9E%84-python</guid>
            <pubDate>Mon, 13 Sep 2021 13:34:52 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>본 문제의 저작권은 SW Expert 아카데미에 있습니다.</p>
<p><a href="https://swexpertacademy.com/main/learn/course/lectureProblemViewer.do">SWEA 4880.토너먼트 카드게임 링크</a></p>
</blockquote>
</br>

<h4 id="문제">문제</h4>
<pre><code>사다리 게임이 지겨워진 알고리즘 반 학생들이 새로운 게임을 만들었다. 가위바위보가 그려진 카드를 이용해 토너먼트로 한 명을 뽑는 것이다. 게임 룰은 다음과 같다.

1번부터 N번까지 N명의 학생이 N장의 카드를 나눠 갖는다. 전체를 두 개의 그룹으로 나누고, 그룹의 승자끼리 카드를 비교해서 이긴 사람이 최종 승자가 된다.

그룹의 승자는 그룹 내부를 다시 두 그룹으로 나눠 뽑는데, i번부터 j번까지 속한 그룹은 파이썬 연산으로 두개로 나눈다.

두 그룹이 각각 1명이 되면 양 쪽의 카드를 비교해 승자를 가리고, 다시 더 큰 그룹의 승자를 뽑는 방식이다.

다음은 4명이 카드를 비교하는 경우로, 숫자 1은 가위, 2는 바위, 3은 보를 나타낸다. 만약 같은 카드인 경우 편의상 번호가 작은 쪽을 승자로 하고, 처음 선택한 카드는 바꾸지 않는다.

N명이 학생들이 카드를 골랐을 때 1등을 찾는 프로그램을 만드시오.</code></pre><br>

<h4 id="코드">코드</h4>
<pre><code class="language-python"># 가위바위보 게임
def RPS(left, right):
    l, r = cards[left-1], cards[right-1]

    # 같을 경우, 인덱스가 낮은 사람이 이긴다.
    if l == r:
        return left
    # 가위
    elif l == 1:
        if r == 3:
            return left
        elif r == 2:
            return right
    # 바위
    elif l == 2:
        if r == 1:
            return left
        elif r == 3:
            return right
    # 보
    elif l == 3:
        if r == 1:
            return right
        elif r == 2:
            return left

# card game 함수
def cardGame(low, high):
    if low == high:
        return low
    else:
        # 중간 인덱스 설정
        mid = (low + high) // 2

        l = cardGame(low, mid)
        r = cardGame(mid+1, high)
        return RPS(l, r)

# 테스트 케이스 T 입력
T = int(input())
for tc in range(1, T+1):
    # 인원수 N 입력
    N = int(input())

    # N개의 카드 리스트 입력
    cards = list(map(int, input().split()))

    # 결과값 ans
    ans = cardGame(1, N)

    # 결과 출력
    print(&#39;#{} {}&#39;.format(tc, ans))</code></pre>
<br>

<h4 id="풀이">풀이</h4>
<p> 문제를 굉장히 오랜만에 다시 봐서, 전혀 모르겠다. 다른 사람들이 푼 코드를 참고하여 <strong>재귀</strong> 형식으로 코드를 구현하였는데 잘 이해가 되지 않는다.</p>
<p> 코드는 가위바위보 게임에서 이긴 사람을 찾는 함수 <code>RPS</code> 와 좌-우 사람이 게임을 진행해서 승자를 찾는 <code>cardGame</code> 함수로 이루어져 있다. 특히, <code>cardGame</code> 함수 내부에서 재귀 호출이 이루어지는데, 다른 사람 코드를 보면 이 부부분의 <code>l</code> , <code>r</code> 을 <strong>Divide and Conquer</strong> 방식으로 푼 케이스도 있었다. 근데 코드 자체는 매우 비슷했다.</p>
<br>

<h4 id="결론">결론</h4>
<p> 이 문제는 내가 푼 문제가 아니다. 다른 사람한테 다시 물어보고 처음부터 다시 풀어봐야겠다. 2021.09.13</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Algorithm | SWEA 4408.자기 방으로 돌아가기 (python)]]></title>
            <link>https://velog.io/@yb_engineer/Algorithm-SWEA-4408.%EC%9E%90%EA%B8%B0-%EB%B0%A9%EC%9C%BC%EB%A1%9C-%EB%8F%8C%EC%95%84%EA%B0%80%EA%B8%B0-python</link>
            <guid>https://velog.io/@yb_engineer/Algorithm-SWEA-4408.%EC%9E%90%EA%B8%B0-%EB%B0%A9%EC%9C%BC%EB%A1%9C-%EB%8F%8C%EC%95%84%EA%B0%80%EA%B8%B0-python</guid>
            <pubDate>Sat, 28 Aug 2021 07:54:01 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>본 문제의 저작권은 SW Expert 아카데미에 있습니다.</p>
<p><a href="https://swexpertacademy.com/main/code/problem/problemDetail.do?contestProbId=AWNcJ2sapZMDFAV8&amp;categoryId=AWNcJ2sapZMDFAV8&amp;categoryType=CODE&amp;problemTitle=4408&amp;orderBy=FIRST_REG_DATETIME&amp;selectCodeLang=ALL&amp;select-1=&amp;pageSize=10&amp;pageIndex=1">SWEA 4408.자기 방으로 돌아가기 링크</a></p>
</blockquote>
</br>

<h4 id="문제">문제</h4>
<pre><code>고등학교 학생들이 학교에서 수련회를 갔다. 수련회에 간 학생들은 친구들과 음주가무를 즐기다가 밤 12시가 되자 조교들의 눈을 피해 자기방으로 돌아가려고 한다.

제 시간에 자기방으로 돌아가지 못한 학생이 한 명이라도 발견되면 큰일나기 때문에 최단 시간에 모든 학생이 자신의 방으로 돌아가려고 한다.

숙소는 긴 복도를 따라 총 400개의 방이 아래와 같이 배열되어 있다.

모든 학생들은 현재 위치에서 자신의 방으로 돌아가려고 하는데, 만약 두 학생이 자기방으로 돌아가면서 지나는 복도의 구간이 겹치면 두 학생은 동시에 돌아갈 수 없다.

예를 들어 (방1 -&gt; 4) 와 (방3 -&gt; 6) 은 복도 구간이 겹치므로 한 사람은 기다렸다가 다음 차례에 이동해야 한다. 이동하는 데에는 거리에 관계없이 단위 시간이 걸린다고 하자.

각 학생들의 현재 방 위치와 돌아가야 할 방의 위치의 목록이 주어질 때, 최소 몇 단위시간만에 모든 학생들이 이동할 수 있는지를 구하시오.</code></pre><p><img src="https://images.velog.io/images/yb_engineer/post/22c973b1-7065-4420-ae1f-43c99b58183f/swea_4408_%EC%9E%90%EA%B8%B0%EB%B0%A9%EC%9C%BC%EB%A1%9C%EB%8F%8C%EC%95%84%EA%B0%80%EA%B8%B0.PNG" alt=""></p>
</br>

<h4 id="코드">코드</h4>
<pre><code class="language-python"># 테스트 케이스 수
T = int(input())

# T만큼 반복
for tc in range(1, T+1):

    # 돌아가야 할 학생 수 N 입력
    N = int(input())

    # 복도 리스트 생성
    corridor = [0] * 200

    # N만큼 반복하며
    for _ in range(N):
        # 현재방 s, 돌아갈 방 e를 입력
        s, e = map(int, input().split())

        # s가 e보다 작거나 같으면
        if s &lt;= e:
            # 반복을 s -&gt; e 로 순회하며 복도 리스트의 i번째 인덱스에 1씩 더해준다.
            for i in range((s-1)//2, (e-1)//2+1):
                corridor[i] += 1
        # s가 e보다 클 경우                
        else:
            # 반복을 e -&gt; s 로 순회하며 복도 리스트의 i번째 인덱스에 1씩 더해준다.
            for i in range((e-1)//2, (s-1)//2+1):
                corridor[i] += 1

    # 복도의 0번째 값을 최대값으로 초기화
    max_value = corridor[0]
    # 복도 리스트를 순회하면서 최대값을 찾아준다. (최대값 = 모든 학생들이 방으로 돌아가는 최소 단위시간)
    for corr in corridor:
        if corr &gt; max_value:
            max_value = corr

    # 결과 출력
    print(&#39;#{} {}&#39;.format(tc, max_value))</code></pre>
</br>

<h4 id="풀이">풀이</h4>
<p>학생들이 자기 방으로 돌아가기 위해서는 복도(corridor)를 지나가야 한다. 따라서 <strong><code>0~200</code> 의 인덱스 범위를 갖는 복도 리스트를 생성</strong>하고, 학생들이 출발하는 방 / 도착하는 방의 인덱스를 따라서 복도 리스트에 1씩 더하여 준다. 최종적으로 모든 학생들이 각자 방으로 돌아가고 나서 복도 리스트의 최대값을 구하면 된다. 이는, <strong>최대값이 모든 학생들이 방으로 돌아가는 최소 단위시간</strong>이기 때문이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Algorithm | SWEA 1859.백만장자 프로젝트 (python)]]></title>
            <link>https://velog.io/@yb_engineer/Algorithm-SWEA-1859.%EB%B0%B1%EB%A7%8C%EC%9E%A5%EC%9E%90-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-python</link>
            <guid>https://velog.io/@yb_engineer/Algorithm-SWEA-1859.%EB%B0%B1%EB%A7%8C%EC%9E%A5%EC%9E%90-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-python</guid>
            <pubDate>Sat, 28 Aug 2021 07:46:54 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>본 문제의 저작권은 SW Expert 아카데미에 있습니다.</p>
<p><a href="https://swexpertacademy.com/main/code/problem/problemDetail.do?contestProbId=AV5LrsUaDxcDFAXc&amp;categoryId=AV5LrsUaDxcDFAXc&amp;categoryType=CODE&amp;problemTitle=1859&amp;orderBy=FIRST_REG_DATETIME&amp;selectCodeLang=ALL&amp;select-1=&amp;pageSize=10&amp;pageIndex=1">SWEA 1859.백만장자 프로젝트 링크</a></p>
</blockquote>
</br>

<h4 id="문제">문제</h4>
<pre><code>25년 간의 수행 끝에 원재는 미래를 보는 능력을 갖게 되었다. 이 능력으로 원재는 사재기를 하려고 한다. 다만 당국의 감시가 심해 한 번에 많은 양을 사재기 할 수 없다.
다음과 같은 조건 하에서 사재기를 하여 최대한의 이득을 얻도록 도와주자.

1. 원재는 연속된 N일 동안의 물건의 매매가를 예측하여 알고 있다.
2. 당국의 감시망에 걸리지 않기 위해 하루에 최대 1만큼 구입할 수 있다.
3. 판매는 얼마든지 할 수 있다.

예를 들어 3일 동안의 매매가가 1, 2, 3 이라면 처음 두 날에 원료를 구매하여 마지막 날에 팔면 3의 이익을 얻을 수 있다.</code></pre></br>

<h4 id="코드">코드</h4>
<pre><code class="language-python"># 테스트 케이스 개수 T 입력
T = int(input())

# T만큼 반복
for tc in range(1, T+1):
    # 자연수 N의 개수 입력
    N = int(input())
    # N 리스트 입력
    N_list = list(map(int, input().split()))

    # N_list의 마지막 값을 최대값으로 설정
    max_value = N_list[-1]
    # 이익 변수 초기화
    profit = 0

    # N-2번째 인덱스부터 0번째 인덱스까지 1씩 감소하면서 반복 순회
    for i in range(N-2, -1, -1):
        # 만약 현재 매매가가 최대값보다 크면 최대값을 변경
        if N_list[i] &gt;= max_value:
            max_value = N_list[i]
        # 현재 매매가가 최대값보다 크지 않으면 차익을 profit 변수에 더해준다
        else:
            profit += max_value - N_list[i]

    # 결과 출력
    print(&#39;#{} {}&#39;.format(tc, profit))</code></pre>
</br>

<h4 id="풀이">풀이</h4>
<p><strong>거꾸로 계산하기</strong> <br><strong>마지막날 매매가를 최대값으로 설정</strong>한다. 하루씩 앞으로 <strong>날짜를 거슬러 오면서 그 날의 매매가와 비교</strong>를 하는데, 만약 i번째 날의 매매가가 현재 최대값보다 크면 최대값을 i번째 날 매매가로 변경해준다. 반대로 i번째 날의 매매가가 현재 최대값보다 크지 않다면 최대값과 i번째 날 매매가의 차이 즉, 이익을 계산해서 총이익에 더해준다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Algorithm | SWEA 1974.스도쿠검증 (python)]]></title>
            <link>https://velog.io/@yb_engineer/Algorithm-SWEA-1974.%EC%8A%A4%EB%8F%84%EC%BF%A0%EA%B2%80%EC%A6%9D-python</link>
            <guid>https://velog.io/@yb_engineer/Algorithm-SWEA-1974.%EC%8A%A4%EB%8F%84%EC%BF%A0%EA%B2%80%EC%A6%9D-python</guid>
            <pubDate>Sat, 28 Aug 2021 07:36:29 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>본 문제의 저작권은 SW Expert 아카데미에 있습니다.</p>
<p><a href="https://swexpertacademy.com/main/code/problem/problemDetail.do?contestProbId=AV5Psz16AYEDFAUq&amp;categoryId=AV5Psz16AYEDFAUq&amp;categoryType=CODE&amp;problemTitle=1974&amp;orderBy=FIRST_REG_DATETIME&amp;selectCodeLang=ALL&amp;select-1=&amp;pageSize=10&amp;pageIndex=1">SWEA 1974.스도쿠검증 링크</a></p>
</blockquote>
</br>

<h4 id="문제">문제</h4>
<pre><code>스도쿠는 숫자퍼즐로, 가로 9칸 세로 9칸으로 이루어져 있는 표에 1 부터 9 까지의 숫자를 채워넣는 퍼즐이다.
같은 줄에 1 에서 9 까지의 숫자를 한번씩만 넣고, 3 x 3 크기의 작은 격자 또한, 1 에서 9 까지의 숫자가 겹치지 않아야 한다.
입력으로 9 X 9 크기의 스도쿠 퍼즐의 숫자들이 주어졌을 때, 아래와 같이 겹치는 숫자가 없을 경우, 1을 정답으로 출력하고 그렇지 않을 경우 0 을 출력한다.</code></pre><p><img src="https://images.velog.io/images/yb_engineer/post/24b9198c-eaf8-422d-ac00-de2b5cf0af23/swea_1974_%EC%8A%A4%EB%8F%84%EC%BF%A0%EA%B2%80%EC%A6%9D.PNG" alt=""></p>
<h4 id="코드">코드</h4>
<pre><code class="language-python"># 스도쿠 검증 함수 생성
def checkSudoku(M):
    for i in range(9):
        row_num = [0] * 10
        col_num = [0] * 10
        for j in range(9):
            # 가로 검사
            row = M[i][j]
            # 세로 검사
            col = M[j][i]

            # 이미 사용된 숫자라면, 0을 리턴
            if row_num[row]: return 0
            if col_num[col]: return 0

            # 아니라면, row_num과 col_num의 각 숫자 위치를 1로 변경
            row_num[row] = 1
            col_num[col] = 1

            # 3x3 행렬 검사
            if i % 3 == 0 and j % 3 == 0:
                square = [0] * 10
                for r in range(i, i+3):
                    for c in range(j, j+3):
                        num = M[r][c]
                        if square[num]: return 0
                        square[num] = 1

    # 반복문이 정상적으로 다 수행된다면, 올바른 스도쿠이므로 1을 리턴
    return 1

# 테스트 케이스 개수 T 입력
T = int(input())
for tc in range(1, T+1):
    # 검사를 위한 행렬 입력
    mat = [list(map(int, input().split())) for _ in range(9)]

    # checkSudoku() 함수를 사용해서 return 값을 result에 저장
    result = checkSudoku(mat)

    # 결과 출력
    print(&#39;#{} {}&#39;.format(tc, result))</code></pre>
</br>

<h4 id="풀이">풀이</h4>
<p>가로, 세로 한 줄 안에서 <strong>1~9의 숫자가 중복으로 사용된다면 온전한 스도쿠가 아니다</strong>. 또한 행렬의 1, 4, 7번째 (인덱스로 따지자면 0, 3, 6) 행/열에서 시작되는 3x3 행렬 안에서도 1~9의 숫자가 중복으로 사용되어서도 안된다. <br> <code>for문</code> 의 중첩을 통해 <strong><code>i</code> 번째 가로줄과 세로줄을 동시에 검사</strong>하고, <strong>행렬의 인덱스가 <code>0, 3, 6</code> 즉, 3으로 나누어 떨어질 때 <code>3x3</code> 행렬을 검사</strong>한다. 반복문 내부에서 중복되는 숫자가 하나라도 나오면 바로 <code>0</code> 을 리턴하고 함수를 종료하고, <code>for 문</code> 이 끝까지 정상적으로 순회한다면 온전한 스도쿠라고 볼 수 있기 때문에 <code>1</code> 을 리턴한다.
<br></p>
<blockquote>
<p>2021.08.28</p>
<p>알고리즘 문제를 매번 풀때마다, 문제를 제대로 이해하지 못하거나 너무 꼬아서 생각하는 경우가 많다.</p>
<p>스도쿠 문제도 각각 가로줄, 세로줄, 3x3 행렬을 최대한 복잡하지 않게끔 반복문 코드를 짜서 검사하면 되는 문제이다. 하지만 나 스스로 문제를 너무 어렵게 만드는 경향이 있는 것 같다. 😒</p>
<p>최대한 간단하고 쉽게 생각하자!</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Algorithm | SWEA 1961.숫자배열회전 (python)]]></title>
            <link>https://velog.io/@yb_engineer/Algorithm-SWEA-1961.%EC%88%AB%EC%9E%90%EB%B0%B0%EC%97%B4%ED%9A%8C%EC%A0%84-python</link>
            <guid>https://velog.io/@yb_engineer/Algorithm-SWEA-1961.%EC%88%AB%EC%9E%90%EB%B0%B0%EC%97%B4%ED%9A%8C%EC%A0%84-python</guid>
            <pubDate>Sat, 28 Aug 2021 06:13:13 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>본 문제의 저작권은 SW Expert 아카데미에 있습니다.</p>
<p><a href="https://swexpertacademy.com/main/code/problem/problemDetail.do?contestProbId=AV5Pq-OKAVYDFAUq&amp;categoryId=AV5Pq-OKAVYDFAUq&amp;categoryType=CODE&amp;problemTitle=1961&amp;orderBy=FIRST_REG_DATETIME&amp;selectCodeLang=ALL&amp;select-1=&amp;pageSize=10&amp;pageIndex=1&amp;&amp;&amp;&amp;&amp;&amp;&amp;&amp;&amp;">SWEA 1961.숫자배열회전 링크</a></p>
</blockquote>
</br>

<h4 id="문제">문제</h4>
<pre><code>N x N 행렬이 주어질 때,

시계 방향으로 90도, 180도, 270도 회전한 모양을 출력하라.</code></pre></br>

<h4 id="코드">코드</h4>
<pre><code class="language-python"># 테스트 케이스 T 입력
T = int(input())
for tc in range(1, T+1):
    # 행렬의 N 값 입력
    N = int(input())

    # NxN 행렬 입력
    arr = [list(map(int, input().split())) for _ in range(N)]

    # 90도, 180,도 270도 회전한 행렬 초기화
    arr_90 = [[0 for _ in range(N)] for _ in range(N)]
    arr_180 = [[0 for _ in range(N)] for _ in range(N)]
    arr_270 = [[0 for _ in range(N)] for _ in range(N)]

    # arr 행렬 90도 회전
    for i in range(N):
        for j in range(N):
            arr_90[i][j] = arr[N-1-j][i]

    # arr_90 행렬을 90도 회전하면 arr_180 행렬
    for i in range(N):
        for j in range(N):
            arr_180[i][j] = arr_90[N-1-j][i]

    # arr_180 행렬을 90도 회전하면 arr_270 행렬
    for i in range(N):
        for j in range(N):
            arr_270[i][j] = arr_180[N-1-j][i]

    # 결과 출력
    print(&#39;#{}&#39;.format(tc))
    for i in range(N):
        for a in range(N):
            print(arr_90[i][a], end=&#39;&#39;)
        print(end=&#39; &#39;)
        for b in range(N):
            print(arr_180[i][b], end=&#39;&#39;)
        print(end=&#39; &#39;)
        for c in range(N):
            print(arr_270[i][c], end=&#39;&#39;)
        print()</code></pre>
</br>

<h4 id="풀이">풀이</h4>
<p>테스트 케이스 <code>NxN 행렬</code> 을 입력받으면 그 행렬을 <strong>시계방향으로 90도 / 180도 / 270도 회전한 행렬을 출력하는 문제</strong>이다. <strong>원본 행렬 <code>arr</code> 의 인덱스를 <code>(0,0)</code> 부터 <code>(N-1, N-1)</code> 까지 그려놓고 시계방향으로 90도 회전시킨 행렬의 인덱스를 계산해서 <code>for문</code> 을 반복</strong>하면 된다. <br>또한, 원본 행렬을 각각 90도 180도 270도 돌리려고 하면 인덱스를 계산하는 것에 있어서 어려움을 겪을 수 있다. 따라서 <strong>원본 행렬의 90도 회전 행렬을 구하고 → 90도 회전한 행렬의 90도 회전 행렬을 구하고 → 다시 그 행렬의 90도 회전 행렬을 구하면 각각 90도 회전 / 180도 회전 / 270도 회전 행렬을 찾을 수</strong> 있다. <br>오히려 회전한 행렬을 구하는 부분보다, 마지막에 출력 예시에 맞게끔 코드를 짜는 부분이 더 어려웠다. 한 행 안에서 각 행렬의 <code>0~N</code> 열을 띄어쓰기 없이 / 행렬 사이에는 공백을 주고 출력해야 한다. <br>글로 풀어서 쓰는데도 무슨 말인지 모르겠다. 위 코드를 천천히 살펴보면 이해할 수 있을 것이다.😂</p>
<p align="center">
  <img style="margin:50px 0 10px 0" src="https://images.velog.io/images/yb_engineer/post/b11c397a-fcca-47e9-923f-1ce1f172a274/swea_1961_%EC%88%AB%EC%9E%90%EB%B0%B0%EC%97%B4%ED%9A%8C%EC%A0%84_%EB%82%B4%ED%92%80%EC%9D%B4.jpg" alt="행렬 회전 풀이 이미지" width="50%" height="30" />
  3x3 행렬을 각각 90도, 180도, 270도 회전했을 때 (i, j) 인덱스
</p> ]]></description>
        </item>
        <item>
            <title><![CDATA[Algorithm | SWEA 4831. 전기버스 (python)]]></title>
            <link>https://velog.io/@yb_engineer/Algorithm-SWEA-4831.-%EC%A0%84%EA%B8%B0%EB%B2%84%EC%8A%A4-python</link>
            <guid>https://velog.io/@yb_engineer/Algorithm-SWEA-4831.-%EC%A0%84%EA%B8%B0%EB%B2%84%EC%8A%A4-python</guid>
            <pubDate>Thu, 26 Aug 2021 02:18:44 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>본 문제의 저작권은 SW Expert 아카데미에 있습니다.</p>
</blockquote>
</br>

<h4 id="문제">문제</h4>
<pre><code>A도시는 전기버스를 운행하려고 한다. 전기버스는 한번 충전으로 이동할 수 있는 정류장 수가 정해져 있어서, 중간에 충전기가 설치된 정류장을 만들기로 했다.

버스는 0번에서 출발해 종점인 N번 정류장까지 이동하고, 한번 충전으로 최대한 이동할 수 있는 정류장 수 K가 정해져 있다.

충전기가 설치된 M개의 정류장 번호가 주어질 때, 최소한 몇 번의 충전을 해야 종점에 도착할 수 있는지 출력하는 프로그램을 만드시오.

만약 충전기 설치가 잘못되어 종점에 도착할 수 없는 경우는 0을 출력한다. 출발지에는 항상 충전기가 설치되어 있지만 충전횟수에는 포함하지 않는다.</code></pre><p><img src="https://images.velog.io/images/yb_engineer/post/68871cfa-3c7f-4db6-bd03-950ce3e62c37/swea_4831_%EC%A0%84%EA%B8%B0%EB%B2%84%EC%8A%A4.PNG" alt=""></p>
</br>

<h4 id="코드">코드</h4>
<pre><code class="language-python"># 테스트 케이스 수 입력
T = int(input())

# T만큼 테스트 케이스 반복
for tc in range(1, T+1):
    # K : 한번 충전으로 최대한 이동할 수 있는 정류장 수
    # N : 종점 정류장
    # M : 충전기가 설치된 정류장 개수
    K, N, M = list(map(int, input().split()))

    # 충천지가 설치된 정류장 리스트 입력
    charge_station = list(map(int, input().split()))
    # 충전 횟수 count와 현재 위치 current 변수 초기화
    count = current = 0

    # 종점에 도착할 때까지 반복
    while current + K &lt; N:
        # K 범위 안에서 현 위치를 조정하면서 이동
        for step in range(K, 0, -1):
            # 현재 위치 + 이동 거리만큼 이동했을 때 충전기가 있는 정류장일 경우
            if (current + step) in charge_station:
                # 현재 위치를 변경
                current += step
                # 충전 횟수 +1
                count += 1
                # for 문을 종료
                break
        # 충전기 설치가 잘못되어 종점에 도착할 수 없는 경우 count를 0으로 하고 while문을 종료
        else:
            count = 0
            break

    # 결과 출력
    print(&#39;#{} {}&#39;.format(tc, count))</code></pre>
</br>

<h4 id="풀이">풀이</h4>
<p>종점에 도착할 때까지 충전을 몇 번할 수 있는지를 세어줘야 하는 문제이다. <strong>현재 위치에서 K 범위 만큼 이동거리를 조절</strong>하면서 충전기가 있으면 현재위치를 변경하고 count를 1씩 늘려준다. 충전기 설치가 잘못되어 있으면 while문을 더이상 반복하지 않고 count 값을 0으로 초기화하고 종료한다.</p>
]]></description>
        </item>
    </channel>
</rss>