<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>guri_coding.log</title>
        <link>https://velog.io/</link>
        <description>https://medium.com/@jinsung1048 미디엄으로 이전하였습니다.</description>
        <lastBuildDate>Tue, 04 Oct 2022 10:17:56 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>guri_coding.log</title>
            <url>https://images.velog.io/images/guri_coding/profile/e1f2ce26-78bc-47dd-85c3-715356bf886d/social.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. guri_coding.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/guri_coding" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Spring] 테스트 주도 개발과 JUnit5 Annotations]]></title>
            <link>https://velog.io/@guri_coding/Spring-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A3%BC%EB%8F%84-%EA%B0%9C%EB%B0%9C%EA%B3%BC-JUnit5-Annotations</link>
            <guid>https://velog.io/@guri_coding/Spring-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A3%BC%EB%8F%84-%EA%B0%9C%EB%B0%9C%EA%B3%BC-JUnit5-Annotations</guid>
            <pubDate>Tue, 04 Oct 2022 10:17:56 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/guri_coding/post/c4378cba-9de9-46a4-8c7a-e37f9a894785/image.png" alt=""></p>
<p>JUnit5가 Spring을 사용할 때 테스트코드 작성을 도와주는 라이브러리인 것은 대부분의 사람들이 알것이다. 그러나 JUnit5 를 사용하더라도 단순히 사용하는 것과 왜 사용해야하는지 알고서 사용하는 것은 큰차이가 존재한다. 따라서 JUnit5를 사용하기 전에 TDD(Test Driven Development) 에 대하여 이해하고자 한다. </p>
<h2 id="tdd란">TDD란?</h2>
<p><strong>TDD(Test Driven Development)</strong>는 <strong>테스트 주도 개발</strong>의 약자로 개발 구현에 앞서 테스트 코드를 먼저 작성하고 테스트코드를 통과하기 위한 개발 프로세스를 가져가는 것이라고 할 수 있다. 이처럼 TDD를 설명하는 다양한 문장이 존재하는데 아래와 같다.</p>
<blockquote>
<p>테스트가 개발을 이끌어간다.
개념부터 코드로 테스트를 자동화한다. 
결정과 피드백 사이의 갭을 조절하는 것 =&gt; 우리가 코드를 어떻게 작성해야지라는 결정을 내리고 코드를 짜면서 에러라는 피드백을 받게 되기에 이 과정의 갭을 줄인다고 할 수 있다.</p>
</blockquote>
<h3 id="왜-tdd를-사용해야할까">왜 TDD를 사용해야할까?</h3>
<p>가령 우리가 작성한 백엔드 코드가 시간이 지나 레거시가 될 수 있다. 그래서 이 코드를 새로운 코드로 바꾸어야지! 하고 리팩토링을 했는데 오류가 나게 되었고 또한 수정한 곳이 너무 많아 어디서 오류가 나는지 알 수 없게 되는 결과가 나오게 된다. 이와 같은 상황이 나오기 전에 테스트 코드를 작성하게 되면 우리가 수정한 코드의 위치를 바로 알 수 있다. <strong>TDD는 우리가 코드를 작성하고 나서 배포하기 전 기능 안정성의 문지기의 역할을 하게 되는 것</strong>이다. </p>
<p>또한, 협업을 하는 과정에서 내가 코드를 작성하는 일보다는 다른 사람의 코드를 보고 수정하는 일이 더 많다. 이 과정에서 우리가 수정한 동료의 코드를 테스트할 때 기능에 대한 안정성을 우리가 미리 작성해놓은 Test Case가 점검하게 된다. <strong>따라서 같이 협업하는 동료에게 설득력을 줄 수 있어 개발 과정에서 의사결정이 빨라지고 좋은 결과물을 만들 수 있게 된다.</strong></p>
<h3 id="그렇다면-tdd가-무조건-좋은거야">그렇다면 TDD가 무조건 좋은거야??</h3>
<p>사실 TDD 주도 개발을 하다보면 추가적인 테스트 코드를 작성해야하는 시간과 노력이 훨씬 더 많이 필요하게 된다. 단기적 성과를 목적으로 빠르게 MVP를 만들어야 하는 과정에서 좋은 코드를 작성하고 기능의 안정성을 체크하는 것보다 아이템 핵심 기능에 대한 시장의 피드백 및 테스트가 더 급할 때가 존재한다. 이 과정에서 굳이 유지보수 비용을 늘릴 필요는 없게 되는 것이다. </p>
<p>하지만 투입되는 노력의 총량과 유지보수의 비용이 많아지더라도 TDD를 해야하는 확실한 이유는 <strong>&quot;기능의 안정성&quot;</strong> 때문이다. 이 &quot;기능의 안정성&quot;은 개발자에게 내가 짠 코드가 맞다는 확신을 주게 된다. 따라서 개발 과정 내 피드백과 협력이 증진해 결함이 줄어들고 코드 복잡도도 떨어져 장기적으로 유지보수 비용이 덜 들어가게 된다.</p>
<h3 id="tdd에는-어떤-종류가-있을까">TDD에는 어떤 종류가 있을까??</h3>
<p>테스트 주도 개발에는 크게 3가지의 종류가 존재한다. </p>
<ol>
<li>Unit Test (단위 테스트)</li>
<li>Integration Test (통합 테스트)</li>
<li>Acceptance Test (인수 테스트)</li>
</ol>
<h4 id="단위-테스트">단위 테스트</h4>
<p><strong>가장 작은 단위의 테스트로 예상대로 동작하는지 확인하는 테스트</strong>이다. 보통 Method 단계에서 실행되어 A 라는 함수를 실행했을 때 B라는 결과가 나오는 정도로 테스트한다. <strong>즉각적인 피드백이 나온다는 장점이 존재</strong>한다. </p>
<h4 id="통합-테스트">통합 테스트</h4>
<p><strong>통합테스트는 단위 테스트보다 더 큰 동작을 달성하기 위해 여러 모듈들을 모다 이들이 의도대로 협력하는지 확인하는 테스트</strong>이다. 우리가 외부 라이브러리도 가져와서 사용하고 또한 결제 모듈 같은 외부 요소도 함께 묶어 검증을 할 때 사용한다. 단위 테스트에서 알기 어려운 버그를 찾을 수 있는 장점이 있지만 어디서 에러가 발생하는지 확인이 쉽지 않아 유지보수가 힘들다.</p>
<h4 id="인수-테스트">인수 테스트</h4>
<p>인수 테스트는 사용자 스토리에 맞춰 수행하는 테스트로 실제 사용자가 서비스를 써보고 비즈니스 상에서 어떤 문제점이 있는지 피드백을 받는 테스트이다. <strong>&quot;누가, 어떤 목적으로, 무엇을 하는지&quot;에 대한 요소가 중심으로 소프트웨어 내부 구조보다는 사용자 관점에서 테스트를 진행</strong>한다.</p>
<h2 id="junit5">JUnit5</h2>
<p>우리가 JUnit5를 활용하여 테스트하는 용도는 주로 &quot;단위 테스트&quot;이다. Repository, Service 계층을 타겟으로 주로 실행하고 추가적으로 WebMvcTest로 Controller 계층에서 일부 &quot;통합 테스트&quot;도 진행하게 된다. </p>
<h3 id="junit5에-대하여">JUnit5에 대하여</h3>
<blockquote>
<p>JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage</p>
</blockquote>
<p>JUnit5는 위 3가지가 합쳐진 새로운 버전이라고 볼 수 있다. </p>
<ul>
<li>JUnit Platform : JVM 상에서 테스트 프레임워크 실행을 도와주는 환경을 제공</li>
<li>JUnit Jupiter : 테스트 코드를 작성할 수 있는 프로그래밍 메소드를 제공</li>
<li>JUnit Vintage : 플랫폼 기반 JUnit3 혹은 JUnit4 같은 요소도 테스트할 수 있도록 함</li>
</ul>
<pre><code class="language-java">import static org.junit.jupiter.api.Assertions.assertEquals;
import example.util.Calculator;
import org.junit.jupiter.api.Test;

class MyFirstJUnitJupiterTests {

    private final Calculator calculator = new Calculator();

    @Test
    void addition() {
        assertEquals(2, calculator.add(1, 1));
    }

}</code></pre>
<p>여기서 assertEquals는 assertEquals(expected, actual) 로 기대되는 요소를 2를 넣고 실제로 실행한 코드는 actual 상에서 발생 해 기대값과 실행 결과를 비교하게 된다.</p>
<h3 id="annotations">Annotations</h3>
<p>어노테이션은 JUnit5 JUnit Jupiter가 테스트 환경을 제공해주는 테스트 코드의 꽃이라고 볼 수 있다. </p>
<pre><code class="language-java">org.junit.jupiter.api</code></pre>
<h4 id="1-test">1. @Test</h4>
<p>@Test는 이 함수가 테스트 함수임을 나타내는 것으로 다른 속성들까지 선언하지는 않는다. 단순히 선언을 함으로써 자식 요소는 오버라이딩하지 않는 이상 상속이 된다. </p>
<pre><code class="language-java">@Test
void 테스트() {

}</code></pre>
<h4 id="2-parameterizedtest">2. @ParameterizedTest</h4>
<p>서로 다른 인자들로 여러 번의 테스트를 수행할 때 사용한다. @Test 처럼 사용이 되지만 추가적으로 적어도 이 인자들이 어디서 오는지 그 Source 를 선언할 필요가 있다. </p>
<pre><code class="language-java">@ParameterizedTest
@ValueSource(strings = { &quot;racecar&quot;, &quot;radar&quot;, &quot;able was I ere I saw elba&quot; })
void palindromes(String candidate) {
    assertTrue(StringUtils.isPalindrome(candidate));
}</code></pre>
<p>@ValueSource에서 제공해주는 단어들을 기반으로 이 단어가 팰린드롬인지 아닌지 판별을 하게 된다. </p>
<h4 id="3-repeatedtest">3. RepeatedTest</h4>
<p>동적으로 테스트를 해보고자 하는 경우에 사용하는 어노테이션이다. 우리가 특정 메소드를 반복적으로 수행할 경우 이 RepeatedTest를 사용하게 된다. </p>
<pre><code class="language-java">@RepeatedTest(10)
void 반복_테스트() {

}</code></pre>
<p><code>반복_테스트</code>는 결과적으로 라이프사이클을 돌면서 10번을 수행하게 된다. </p>
<h4 id="4-displayname">4. DisplayName</h4>
<p>우리가 수행하는 함수의 프로세스를 DisplayName을 사용해서 설명할 수 있다. </p>
<pre><code class="language-java">@Test
@DisplayName(&quot;실제로 나오는 이름&quot;)
void 함수_이름() {

}</code></pre>
<h4 id="5-beforeeach">5. BeforeEach</h4>
<p>@Test, @RepeatedTest, @ParameterizedTest가 각각 실행되기 이전에 실행시켜주는 어노테이션이다. </p>
<pre><code class="language-java">@BeforeEach
void 이전에_실행() {
    System.out.println(&quot;테스트 실행!!&quot;)
}

@Test
void test1() {
    System.out.println(&quot;Test 1&quot;)
}

@Test
void test2() {
    System.out.println(&quot;Test 2&quot;)
}</code></pre>
<p>그러면 결과적으로 콘솔에 <code>&quot;테스트 실행!!&quot; -&gt; &quot;Test 1&quot; -&gt; &quot;테스트 실행!!&quot; -&gt; &quot;Test 2&quot;</code> 순으로 나오게 된다. </p>
<h4 id="6-aftereach">6. AfterEach</h4>
<p>AfterEach는 BeforeEach와 반대로 @Test 실행 이후 나오게 되는 요소이다. </p>
<pre><code class="language-java">@Test
void test1() {
    System.out.println(&quot;Test 1&quot;)
}

@Test
void test2() {
    System.out.println(&quot;Test 2&quot;)
}

@AfterEach
void 이후에_실행() {
    System.out.println(&quot;테스트 실행!!&quot;)
}</code></pre>
<p>그러면 결과적으로 콘솔에 <code>&quot;Test 1&quot; -&gt; &quot;테스트 실행!!&quot; -&gt; &quot;Test 2&quot; -&gt; &quot;테스트 실행!!&quot;</code> 순으로 나오게 된다. </p>
<h4 id="7-beforeall">7. BeforeAll</h4>
<p>BeforeAll은 모든 @Test가 실행되기 전에 실행되도록 하는 어노테이션이다. </p>
<pre><code class="language-java">@BeforeAll
void 이전에_실행() {
    System.out.println(&quot;테스트 실행!!&quot;)
}

@Test
void test1() {
    System.out.println(&quot;Test 1&quot;)
}

@Test
void test2() {
    System.out.println(&quot;Test 2&quot;)
}</code></pre>
<p>그러면 결과적으로 콘솔에 <code>&quot;테스트 실행!!&quot; -&gt; &quot;Test 1&quot; -&gt; &quot;Test 2&quot;</code> 순으로 나오게 된다. </p>
<h4 id="8-afterall">8. AfterAll</h4>
<p>AfterAll은 모든 @Test가 실행되고 나서 사용하는 어노테이션이다. </p>
<pre><code class="language-java">@Test
void test1() {
    System.out.println(&quot;Test 1&quot;)
}

@Test
void test2() {
    System.out.println(&quot;Test 2&quot;)
}

@AfterAll
void 이후에_실행() {
    System.out.println(&quot;테스트 실행!!&quot;)
}</code></pre>
<p>그러면 결과적으로 콘솔에 <code>&quot;Test 1&quot; -&gt; &quot;Test 2&quot; -&gt; &quot;테스트 실행!!&quot;</code> 순으로 나오게 된다. </p>
<h4 id="9-disabled">9. Disabled</h4>
<p>Disabled는 우리가 작성한 테스트 함수 중에 실행하지 않게 하고자하는 함수나 클래스가 있으면 쓰는 어노테이션이다. </p>
<pre><code class="language-java">@Test
void test1() {
    System.out.println(&quot;Test 1&quot;)
}

@Disabled
void test2() {
    System.out.println(&quot;Test 2&quot;)
}

@Test
void test3() {
    System.out.println(&quot;Test 3&quot;)
}</code></pre>
<p>그러면 결과적으로 콘솔에 <code>Test 1 -&gt; Test 3</code>이 나오게 된다.</p>
<h4 id="10-order">10. Order</h4>
<p>우리가 수행하고자 하는 함수에 실행 순서를 지정할 수 있다. </p>
<pre><code class="language-java">@TestMethodOrder(OrderAnnotation.class)
class OrderedTestsDemo {

    @Test
    @Order(1)
    void nullValues() {
        // perform assertions against null values
    }

    @Test
    @Order(2)
    void emptyValues() {
        // perform assertions against empty values
    }

    @Test
    @Order(3)
    void validValues() {
        // perform assertions against valid values
    }

}</code></pre>
<p><code>@TestMethodOrder(OrderAnnotation.class)</code>를 상단에 선언하고 <code>@Order(번호)</code>로 실행 순서를 지정할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] 의존관계 자동 주입]]></title>
            <link>https://velog.io/@guri_coding/Spring-%EC%9D%98%EC%A1%B4%EA%B4%80%EA%B3%84-%EC%9E%90%EB%8F%99-%EC%A3%BC%EC%9E%85</link>
            <guid>https://velog.io/@guri_coding/Spring-%EC%9D%98%EC%A1%B4%EA%B4%80%EA%B3%84-%EC%9E%90%EB%8F%99-%EC%A3%BC%EC%9E%85</guid>
            <pubDate>Tue, 27 Sep 2022 07:38:30 GMT</pubDate>
            <description><![CDATA[<h2 id="다양한-의존관계-주입-방법">다양한 의존관계 주입 방법</h2>
<ol>
<li>생성자 주입</li>
<li>수정자 주입 (setter 주입)</li>
<li>필드 주입</li>
<li>일반 메소드 주입</li>
</ol>
<h3 id="생성자-주입">생성자 주입</h3>
<ul>
<li>정의 : 이름 그대로 생성자를 통해 의존 관계를 주입 받는 방법</li>
<li>특징<ul>
<li>생성자 호출 시점에 딱 1번만 호출되는 것이 보장됨</li>
<li><strong>불변, 필수</strong> 의존 관계에 사용</li>
</ul>
</li>
</ul>
<pre><code class="language-java">@Component
public class OrderService {

    private final MemberRepository memberRepository;

    @Autowired
    public OrderService (MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

}</code></pre>
<blockquote>
<p>스프링빈에서는 생성자가 1개만 있으면 @Autowired가 생랼되어도 자동으로 주입된다. </p>
</blockquote>
<h3 id="수정자-주입setter-주입">수정자 주입(setter 주입)</h3>
<ul>
<li>setter라 불리는 필드의 값을 변경하는 수정자 메서드를 통해서 의존관계를 주입하는 방법</li>
<li>특징<ul>
<li>선택, 변경 가능성이 있는 의존관계에 사용</li>
<li>자바빈 프로퍼티 규약의 수정자 메서드 방식을 사용하는 방법</li>
</ul>
</li>
</ul>
<pre><code class="language-java">@Component
public class OrderService {

    private MemberRepository memberRepository;

    @Autowired
    public void setMemberRepository(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

}</code></pre>
<blockquote>
<p>@Autowired는 주입할 대상이 없으면 오류가 발생하기에 주입할 대상이 없어도 동작하게 하려면 @Autowired(required = false) 로 지정하면 된다. </p>
</blockquote>
<h3 id="필드-주입">필드 주입</h3>
<ul>
<li>이름 그대로 필드에 바로 주입하는 방법이다.</li>
<li>특징<ul>
<li>코드가 간결해서 많은 개발자들을 유혹하지만 외부에서 변경이 불가능해서 테스트학 힘들다는 치명적인 단점이 있다.</li>
<li>DI 프레임워크가 없으면 아무것도 할 수 없다.</li>
<li>사용하지 말자!<ul>
<li>애플리케이션의 실제 코드와 관계없는 테스트 코드</li>
<li>스프링 설정을 목적으로 하는 @Configuration 같은 곳에서만 특별한 용도로 사용</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code class="language-java">@Component
public class OrderService {

    @Autowired
    private MemberRepository memberRepository;

}</code></pre>
<blockquote>
<p>순수한 자바 테스트 코드에서는 @Autowired가 동작하지 않는다.</p>
</blockquote>
<h3 id="일반-메서드-주입">일반 메서드 주입</h3>
<ul>
<li>일반 메서드를 통해서 주입 받을 수 있다. </li>
<li>특징<ul>
<li>한번에 여러 필드를 주입 받을 수 있다.</li>
<li>일반적으로 잘 사용하지 않는다.</li>
</ul>
</li>
</ul>
<pre><code class="language-java">@Component
public class OrderService {

    private final MemberRepository memberRepository;

    @Autowired
    public void init(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

}</code></pre>
<h2 id="옵션-처리">옵션 처리</h2>
<ul>
<li>자동 주입 대상을 옵션으로 처리하는 방법은 다음과 같다.<ul>
<li>@Autowired(required = false) 자동 주입할 대상이 없으면 수정자 메서드 자체가 호출 안됨</li>
<li>org.springframework.lang.@Nullable 자동 주입할 대상이 없으면 null 이 입력된다.</li>
<li>Optional&lt;&gt; 자동 주입할 대상이 없으면 Optional.empty 가 입력된다.</li>
</ul>
</li>
</ul>
<h2 id="결론--생성자-주입하기">결론 : 생성자 주입하기</h2>
<h3 id="왜-생성자로-해야할까">왜 생성자로 해야할까??</h3>
<h4 id="1-불변">1. 불변</h4>
<ul>
<li>대부분의 의존관계 주입은 한번 일어나면 애플리케이션 종료시점까지 의존관계를 변경할 일이 없다. 오히려 대부분의 의존관계는 불변해야한다.</li>
<li>수정자 주입을 사용하면, set 메소드를 public으로 사용</li>
<li>생성자 주입은 객체를 생성할 때 딱 1번만 호출되므로 이후에 호출되는 일이 없다. 따라서 불변하게 설계할 수 있다. </li>
</ul>
<h4 id="2-누락">2. 누락</h4>
<ul>
<li>프레임워크 없이 순수한 자바 코드를 단위 테스트 하는 경우에 의존 관계가 없으면 어떤 값을 필수로 주입해야 하는지 알 수 있다. </li>
</ul>
<h4 id="3-final-키워드">3. final 키워드</h4>
<ul>
<li>생성자 주입을 사용하면 final 키워드를 사용할 수 있다. 그래서 생성자에서 혹시라도 값이 설정되지 않는 오류를 컴파일 시점에 막아준다. </li>
</ul>
<pre><code class="language-java">@Component
public class OrderService {

    private final MemberRepository memberRepository;

    @Autowired
    public OrderService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

}</code></pre>
<blockquote>
<p>수정자 주입을 포함한 나머지 주입 방식은 모두 생성자 이후에 호출되므로, 필드에 final 키워드를 사용할 수 없다. 오직 생성자 주입 방식만 final 키워드를 사용할 수 있다. </p>
</blockquote>
<h4 id="요약">요약</h4>
<ul>
<li>생성자 주입 방식을 선택하는 이유는 &quot;프레임워크에 의존하지 않고, 순수한 자바 언어의 특징을 잘 살리는 방법&quot;이기 때문이다.</li>
<li>기본으로 생성자 주입을 사용하고 필수 값이 아닌 경우에는 수정자 주입 방식을 옵션으로 부여하면 된다. 생성장 주입과 수정자 주입을 동시에 사용할 수 있다.</li>
</ul>
<h2 id="lombok을-사용한-간단한-방법">Lombok을 사용한 간단한 방법</h2>
<p>롬복에서 @RequiredArgsConstructor를 사용하면 컴파일 시점에 생성자 코드를 자동으로 생성해주기에 코드가 간결해진다. </p>
<pre><code class="language-java">@Component
@RequiredArgsConstructor를
public lcass OrderService {

    private final MemberRepository memberReposiory;

}</code></pre>
<h2 id="우리가-조회하고자-하는-빈이-2개-이상일-때">우리가 조회하고자 하는 빈이 2개 이상일 때</h2>
<ul>
<li>@Autowired는 타입으로 조회한다. 그래서 우리가 Coffee라는 객체를 2개의 객체에 상속시키고 빈으로 등록하게 되면 오류가 발생한다.</li>
</ul>
<pre><code>@Autowired
private Coffee coffee;</code></pre><pre><code>@Component
public class IceAmericano implements Coffee {}</code></pre><pre><code>@Component
public class HotAmericano implements Coffee {}</code></pre><p>이 경우 실행하면 <code>NoUniqueBeanDefinitionException</code> 에러가 발생한다. 따라서 이를 해결하디 위한 방식을 알아보자. </p>
<ol>
<li>@Autowired 필드 명 매칭</li>
<li>@Qualifier -&gt; @Qualifier 끼리 매칭 -&gt; 빈 이름 매칭</li>
<li>@Primary 사용</li>
</ol>
<h3 id="1-autowired-필드명-매칭">1. @Autowired 필드명 매칭</h3>
<p>기존에 @Autowired 로 작성한 코드에서 명칭을 변경함</p>
<pre><code class="language-java">@Autowired
private Coffee coffee;

// 필드 명을 빈 이름으로 ㅇ변경
@Autowired
private Coffee iceAmericano;</code></pre>
<h3 id="2-qualifier-사용">2. @Qualifier 사용</h3>
<p><code>@Qualifier</code>는 추가 구분자를 붙여주는 방식으로 주입 시 추가적인 방법을 제공하는 것이지 빈 이름을 변경하는 것은 아니다. </p>
<pre><code class="language-java">@Component
@Qualifier(&quot;IceAmericano&quot;)
public class IceAmericano implements coffee {}

@Component
@Qualifier(&quot;HotAmericano&quot;)
public class HotAmericano implements coffee {}</code></pre>
<p>@Qualifier를 사용하게 되면 빈 이름을 적어주어 생성자, 수정자를 자동으로 주입할 때 편하게 할 수 있다.</p>
<pre><code class="language-java">@Autowired
public OrderService (@Qualifier(&quot;HotAmericano&quot;) Coffee coffee) {
    this.coffee = coffee;
}

@Autowired
public Coffee setCoffee(@Qualifier(&quot;HotAmericano&quot;) Coffee coffee) {
    return coffee;
}</code></pre>
<h3 id="3-primary-사용">3. @Primary 사용</h3>
<ul>
<li>@Primary는 우선순위를 정하는 방법으로 @Autowired 시에 여러 빈이 매칭되면 @Primary 가 우선권을 가진다. </li>
</ul>
<pre><code class="language-java">@Component
@Primary
public class IceAmericano implements coffee {}

@Component
public class HotAmericano implements coffee {}</code></pre>
<ul>
<li>@Qualifier 와 @Primary가 있는데 @Qualifier는 이름을 각각 붙여줘야 하기에 @Primary보다 번거롭다. 그러나 스프링 빈을 등록하고 조회하는 과정에서 명시적으로 잘 정의를 한다면 @Qualifier도 상관이 없다. </li>
<li>@Primary 는 기본값 처럼 동작하는 것이고, @Qualifier 는 매우 상세하게 동작한다. 이런 경우 어떤 것이 우선권을 가져갈까? 스프링은 자동보다는 수동이, 넒은 범위의 선택권 보다는 좁은 범위의 선택권이 우선 순위가 높다. 따라서 여기서도 @Qualifier 가 우선권이 높다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] 스프링 컨테이너와 스프링 빈]]></title>
            <link>https://velog.io/@guri_coding/Spring-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EC%99%80-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B9%88</link>
            <guid>https://velog.io/@guri_coding/Spring-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EC%99%80-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B9%88</guid>
            <pubDate>Mon, 19 Sep 2022 06:33:22 GMT</pubDate>
            <description><![CDATA[<p>스프링에는 빈이라는 요소가 존재한다. 이 빈은 스프링 컨테이너에 의해 관리되는 자바 객체로 <code>ApplicationContext.getBean()</code>으로 얻어질 수 있는 객체를 의미한다. </p>
<h2 id="스프링-빈을-생성하는-방법">스프링 빈을 생성하는 방법</h2>
<ol>
<li>Component Scan 으로 스프링 빈을 찾기</li>
<li>빈 설정 파일에 직접 빈을 등록하기</li>
</ol>
<h3 id="component-scan">Component Scan</h3>
<p>컴포넌트 스캔은 <code>@Componnent</code>를 명시하여 빈을 추가하는 방식으로 클래스 위에 <code>@Component</code>를 붙이면 스프링이 알아서 스프링 컨테이너에 빈을 등록한다. </p>
<pre><code class="language-java">@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}</code></pre>
<p>위 코드 상에서 <code>@SpringBootApplication</code> 내부를 보면 <code>@ComponentScan</code>이라는 어노테이션이 존재한다. 이 어노테이션은 <code>@Component</code>로 등록된 인스턴스의 경로를 알려주는 역할을 하고 <code>@Component</code>는 실제로 찾아서 빈으로 등록할 클래스를 의미한다. 이러한 요소들은 Spring 상에서 IoC 컨테이너로 인해 빈으로 등록된다.</p>
<blockquote>
<p>그렇다면 어떤 요소를 <code>@Component</code>가 등록하려고 할까??</p>
</blockquote>
<ol>
<li>@Component</li>
<li>@Contoller or @RestController : 스프링 MVC 컨트롤러 꼐층</li>
<li>@Repository : 스프링 데이터 접근 계층</li>
<li>@Service : 비즈니스 로직을 작성하는 계층</li>
<li>@Configuration : 스프링 설정 정보로 인식하여 스프링 빈이 싱글톤을 유지하도록 처리함</li>
</ol>
<h4 id="java-코드로-등록하기">Java 코드로 등록하기</h4>
<p>자바 코드로 빈을 등록하는 과정에는 2개의 어노테이션만 사용하면 된다. 하나는 @Configuration, 다른 하나는 @Bean 이다. </p>
<pre><code class="language-java">@Configuration
public class AppConfig {

    @Bean
    public Repository repository() {
        return new Repository();
    }

    @Bean
    public Service service() {
        return new ServiceImpl(repository());
    }

}</code></pre>
<p>여기서 <code>@Bean</code>은 클래스가 아니라 메소드에 붙여야 한다.</p>
<h2 id="bean의-속성과-범위">Bean의 속성과 범위</h2>
<p>앞서 Bean을 등록하는 방법을 알게 되었다. 구체적으로 Bean에는 어떤 속성들이 있고 어떤 범위가 있는지 알아보자.</p>
<h3 id="bean-의-속성">Bean 의 속성</h3>
<blockquote>
<p>속성에 설정된 정보를 토대로 객체가 생성이 된다. </p>
</blockquote>
<ol>
<li>id : Bean의 식별자</li>
<li>class : 자바의 클래스 명</li>
<li>scope : 객체의 범위로 기본은 싱글톤이다.</li>
<li>constructor-arg : 객체 생성 시 생성자에 전달할 인자 (Constructor Injection)</li>
<li>property : 생성시 bean setter에 전달할 인자 (Setter Injection)</li>
<li>init-method : 객체 생성 시 실행되는 함수</li>
<li>destory-method : 객체 소멸 시 호출되는 함수</li>
</ol>
<h3 id="bean의-범위">Bean의 범위</h3>
<p>Bean 같은 경우는 메모리 절약을 위하여 기본적으로 싱글톤 객체로 생성이 된다. 따라서 한번 생성이  되면 모든 객체가 동일한 객체를 참조하는 방식을 사용한다. 이외에 다른 범위를 살펴보자.</p>
<h4 id="bean-scope">Bean Scope</h4>
<ol>
<li>Singleton : 하나의 Bean에 대해서 Spring IoC Container 내에 단 하나만 존재</li>
<li>Prototype : 요청마다 새로운 객체를 생성하여 하나의 Bean에 대해서 다수의 객체가 존재</li>
<li>Request : Http Request 하나에 대한 생명 주기 내 단 하나의 객체만 존재</li>
<li>Session : Http Session과 동일한 생명 주기 내 단 하나의 객체만 존재</li>
<li>Global Session : global Http Session의 생명 주기 내 단 하나의 객체만 존재</li>
</ol>
<p>이렇게 많은 종류의 Bean이 존재하는데 우리는 Bean을 사용함에 있어 적절한 선택을 하는 것이 중요하다. </p>
<h4 id="싱글톤으로-적합한-객체">싱글톤으로 적합한 객체</h4>
<ol>
<li><p>상태가 없는 공유 객체 : 상태를 가지고 있지 않으면 동기화 비용이 없기에 매번 이 객체를 참조하는 곳에서는 새로운 객체를 생성할 이유가 없다.</p>
</li>
<li><p>읽기용으로만 상태를 가진 공유 객체 : 1번과 유사하게 상태를 가지고 있으나 읽기 전용이므로 여전히 동기화 비용이 들지 않는다.</p>
</li>
<li><p>공유가 필요한 상태를 지닌 공유 객체 : 객체 간의 반드시 공유해야할 상태를 지닌 객체가 하나 있다면, 이 경우에는 해당 상태의 쓰기를 가능한 동기화 할 경우 싱글톤도 적합하다.</p>
</li>
<li><p>쓰기가 가능한 상태를 지니면서도 사용빈도가 매우 높은 객체 : 애플리케이션 안에서 정말로 사용 빈도가 높다면, 쓰기 접근에 대한 동기화 비용을 감안하고서라도 싱글톤을 고려할만하다. </p>
<p> 1) 장시간에 걸쳐 많은 매우 많은 객체가 생성될 때
 2) 해당 객체가 매우 작은 양의 쓰기 상태를 가지고 있을 때
 3) 객체 생성비용이 매우 클 때</p>
</li>
</ol>
<h4 id="비싱글톤으로-적합한-객체">비싱글톤으로 적합한 객체</h4>
<ol>
<li>쓰기가 가능한 상태를 지닌 객체 : 쓰기가 가능한 상태가 많아서 동기화 비용이 객체 생성 비용보다 크다면 싱글톤으로 적합하지 않다.</li>
<li>상태가 노출되지 않은 객체 : 일부 제한적인 경우, 내부 상태를 외부에 노출하지 않는 빈을 참조하여 다른 의존 객체와는 독립적으로 작업을 수행하는 의존 객체가 있다면 싱글톤보다 비싱글톤 객체를 사용하는 것이 더 낫다.</li>
</ol>
<h3 id="싱글톤-빈은-thread-safe-한가">싱글톤 빈은 Thread-Safe 한가?</h3>
<p>추가적으로 Bean에서 싱글톤을 사용하지만 우리는 동기화도 같이 처리해주는구나 생각할 때가 있다. 그러나 스프링은 싱글톤 레지스트리를 통해 private, static 변수 등의 코드 없이 비즈니스 로직에 집중하고 테스트 코드에 용이한 싱글톤 객체를 제공해주는 것 뿐으로 동기화 문제는 개발자가 처리해야 한다. 만약에 싱글톤 빈이 상태를 가지게 되고 아무런 동기화 처리를 하지 않는다면 멀티스레드 환경에서 부작용이 발생할 수 있다. </p>
<h2 id="bean-configuration-component-비교">@Bean, @Configuration, @Component 비교</h2>
<h3 id="bean-configuration">@Bean, @Configuration</h3>
<ul>
<li>개발자가 컨트롤이 불가능한 외부 라이브러리들을 Bean으로 등록하고 싶은 경우에 사용된다.</li>
<li>메소드 또는 어노테이션 단위에 붙일 수 있다. </li>
<li>수동으로 스프링 컨테이너에 빈을 등록하는 방법이다 .</li>
<li>유지보수성을 높이기 위해 애플리케이션 전범위적으로 사용되는 클래스나 다형성을 활용하여 여러 구현체를 등록할 때 사용한다.</li>
<li>1개 이상의 @Bean을 제공하는 클래스의 경우 반드시 @Configuration을 명시해주어야 싱글톤이 보장된다.</li>
</ul>
<h3 id="component">@Component</h3>
<ul>
<li>개발자가 직접 컨트롤이 가능한 클래스들의 경우에 사용된다.</li>
<li>클래스 또는 인터페이스 단위에 붙일 수 있다. </li>
<li>자동으로 스프링 컨테이너에 빈을 등록하는 방법이다.</li>
<li>@Component 하위 어노테이션으로 @Configuration, @Controller, @Service, @Repository 등이 존재한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Javascript] Scope의 이해]]></title>
            <link>https://velog.io/@guri_coding/Javascript-Scope%EC%9D%98-%EC%9D%B4%ED%95%B4</link>
            <guid>https://velog.io/@guri_coding/Javascript-Scope%EC%9D%98-%EC%9D%B4%ED%95%B4</guid>
            <pubDate>Thu, 15 Sep 2022 05:31:35 GMT</pubDate>
            <description><![CDATA[<p>자바스크립트는 단순한 언어로 여겨져 왔다. 우리가 변수에 다른 타입을 넣어도 실행이 되기도 하고 변수의 범위가 다양하다는 인식이 있다. 그러나 코드를 작성하는 과정에서 전역과 지역 변수에 대한 이해가 없으면 추후 중복에 대한 오류로 인식하기가 어려운 경우가 존재한다. 그래서 우리는 자바스크립트를 사용할 때 변수가 어디서부터 어디까지 유효한지 그 유효한 범위를 잘 설정하여 사용할 필요가 있다. 그래서 스코프에 대한 이해가 먼저 필요하다.</p>
<h2 id="스코프scope란">스코프(Scope)란?</h2>
<blockquote>
<p>식별자 접근 규칙에 따른 유효 범위</p>
</blockquote>
<ul>
<li>스코프의 정의는 &quot;식별자 접근 규칙에 따른 유효 범위&quot;이다.</li>
<li>식별자(변수, 함수, 클래스)에 접근할 수 있는 범위가 존재한다.</li>
<li>범위는 중괄호 또는 함수에 의해 나누어진다. </li>
<li>각각을 Block Scope와 Function Scope로 나눠진다. </li>
</ul>
<pre><code class="language-javascript">// test.js

var global_variable = &quot;전역 변수&quot;;
function local() {
    let local_variable = &quot;지역 변수&quot;;

      console.log(global_variable, local_variable);
}

local()
// console.log =&gt; &quot;전역 변수&quot;&quot;지역 변수&quot;

console.log(global_variable, local_variable);
                          // ~~~~~~~~~~~~~~ 
                          // 지역 변수는 local() 함수 내에서만
                          // 오류가 발생을 하게 된다. </code></pre>
<p>위 예시처럼 우리는 범위에 따른 변수의 사용 위치를 잘 이해하고 사용해야 한다. </p>
<h3 id="스코프의-주요-규칙">스코프의 주요 규칙</h3>
<h4 id="1-안쪽-스코프에서-바깥쪽-스코프로-접근할-수-있지만-반대는-불가능하다">1. 안쪽 스코프에서 바깥쪽 스코프로 접근할 수 있지만 반대는 불가능하다.</h4>
<ul>
<li>바깥쪽 스코프에서 선언한 식별자는 안쪽 스코프에서 사용 가능하다.</li>
<li>반면, 안쪽에서 선언한 식별자는 바깥쪽 스코프에서는 사용할 수 없다.</li>
</ul>
<h4 id="2-스코프는-중첩이-가능하다">2. 스코프는 중첩이 가능하다.</h4>
<h4 id="3-전역-스코프와-지역-스코프">3. 전역 스코프와 지역 스코프</h4>
<ul>
<li>가장 바깥쪽의 스코프를 전역 스코프(Global Scope)라고 부른다.</li>
<li>전역이 아닌 다른 스코프는 전부 지역 스코프(Local Scope)이다.</li>
</ul>
<h4 id="4-지역-변수는-전역-변수보다-우선순위가-더-높다">4. 지역 변수는 전역 변수보다 우선순위가 더 높다.</h4>
<ul>
<li>전역 스코프에서 선언한 변수는 전역 변수이다.</li>
<li>지역 스코프에서 선언한 변수는 지역 변수이다.</li>
<li>지역 변수는 전역 변수보다 더 높은 우선순위를 가진다. </li>
</ul>
<h3 id="스코프의-종류">스코프의 종류</h3>
<ul>
<li>스코프는 대표적으로 블록 스코프와 함수 스코프가 있다. </li>
<li>화살표 함수는 함수 스코프가 아니고 블록 스코프로 취급된다. </li>
</ul>
<h4 id="block-scope">Block Scope</h4>
<ul>
<li>ES6 이전에는 자바스크립트는 Global Scope와 Funciton Scope만 존재하였다.</li>
<li>그러나 ES6 이후 let, const를 통하여 Block Scope를 지원할 수 있게 되었다. </li>
<li>블록 스코프란 중괄호 {} 안에 선언된 변수들로 외부 블록에서는 접근할 수 없는 요소이다.</li>
</ul>
<pre><code class="language-javascript">{
  let x = 2;
}
// x는 여기서 사용될 수 없다. </code></pre>
<pre><code class="language-javascript">{
  var x = 2;
}
// x는 여기서 사용될 수 있다. </code></pre>
<h4 id="local-scope">Local Scope</h4>
<ul>
<li>자바스크립트에서 선언된 변수는 함수 내에서 지역 변수가 된다. </li>
</ul>
<pre><code class="language-javascript">// carName 사용 불가능

function myFunction() {
  let carName = &quot;Volvo&quot;;
  // carName 사용 가능
}

// carName 사용 불가능</code></pre>
<ul>
<li>지역 변수는 함수 스코프도 될 수 있다. 내부 함수로서 사용되는 것이다. </li>
</ul>
<h4 id="function-scope">Function Scope</h4>
<ul>
<li>자바스크립트는 각 함수가 새로운 스코프를 만들어낼 수 있다.</li>
<li>함수 내부에서 정의된 변수는 외부에서 접근할 수 없다. </li>
</ul>
<pre><code class="language-javascript">function myFunction() {
  var carName = &quot;Volvo&quot;;   // Function Scope
}</code></pre>
<h3 id="스코프와-var-let-const-키워드">스코프와 var, let, const 키워드</h3>
<ul>
<li>변수 선언 키워드 세 가지의 &quot;차이점&quot; 그리고 &quot;스코프 유효 범위&quot;는 아래와 같다. </li>
</ul>
<blockquote>
<p><strong>유효 범위</strong>, <strong>값 재할당</strong>, <strong>재선언</strong></p>
</blockquote>
<h4 id="const-키워드">const 키워드</h4>
<ul>
<li>유효 범위 : 블록 스코프 / 함수 스코프</li>
<li>값 재할당 : 불가능</li>
<li>재선언 : 불가능</li>
</ul>
<h4 id="let-키워드">let 키워드</h4>
<ul>
<li>유효 범위 : 블록 스코프 / 함수 스코프</li>
<li>값 재할당 : 가능</li>
<li>재선언 : 불가능</li>
</ul>
<h4 id="var-키워드">var 키워드</h4>
<ul>
<li>유효 범위 : 함수 스코프</li>
<li>값 재할당 : 가능</li>
<li>재선언 : 가능</li>
</ul>
<blockquote>
<p>블록 단위로 스코프를 구분했을 때, 훨씬 더 예측 가능한 코드를 작성할 수 있으므로 let 키워드의 사용이 권장된다. </p>
</blockquote>
<h3 id="전역-객체window의-이해">전역 객체(window)의 이해</h3>
<ul>
<li>Window 객체는 only 브라우저에만 존재하는 객체이다.</li>
<li>브라우저의 창을 의미하는 객체이다.</li>
<li>따라서 함수 선언식으로 함수를 선언하거나 var 키워드로 변수를 선언하면 window 객체에 속해진다.</li>
<li>예를 들어, var는 전역 적으로 선언되었으면 window의 변수로 사용이 가능하다.</li>
</ul>
<pre><code class="language-javascript">var carName = &quot;Volvo&quot;;
// window.carName 의 형태로 사용할 수 있다.</code></pre>
<pre><code class="language-javascript">let carName = &quot;Volvo&quot;;
// window.carName 의 형태로 사용할 수 없다.</code></pre>
<h3 id="1-전역-변수는-최소화하기">1. 전역 변수는 최소화하기</h3>
<ul>
<li><p>가장 바깥 스코프에 정의한 변수가 전역 변수로 어디서든 접근이 가능하다.</p>
</li>
<li><p>전역 변수를 최소화하면 side effect(의도하지 않은 로직에 의해 문제 발생)을 줄일 수 있다. </p>
</li>
<li><p>그런데 그러한 전역 변수를 심지어 var로 선언하는 경우 문제가 될 수 있다.</p>
<ul>
<li>전역 변수는 어디서나 접근이 가능하다.</li>
<li>var 키워드는 블록 스코프를 무시한다.</li>
<li>재선언과 재할당을 할 수 있다.</li>
<li>전역 변수를 var로 선언해서 브라우저의 내장 기능을 못하게 만들 수도 있다.</li>
</ul>
</li>
</ul>
<h4 id="1-전역-변수-객체를-하나-만들어-사용하기">1) 전역 변수 객체를 하나 만들어 사용하기</h4>
<pre><code class="language-javascript">let store = {};

store.food = {
    kind: &quot;야식&quot;
      name: &quot;야채 곱창&quot; 
};

// 다른 파일
console.log(store.food.kind);</code></pre>
<h4 id="2-즉시-실행-함수를-만들어-사용하기">2) 즉시 실행 함수를 만들어 사용하기</h4>
<ul>
<li>즉시 실행함수는 전역 변수를 만드는 것이 아니라 즉시 실행되고 사용하고 나서 전역에서 바로 사라진다.</li>
</ul>
<pre><code class="language-javascript">(function() {
    let store = {};

    store.food = {
        kind: &quot;야식&quot;
        name: &quot;야채 곱창&quot; 
    };

    console.log(store.food.kind);
})();

console.log(store.food.kind); // 이 함수는 인식하지 못하고 에러를 반환한다.</code></pre>
<h3 id="2-선언-없는-변수-할당-금지">2. 선언 없는 변수 할당 금지</h3>
<ul>
<li><p>선언 없이 변수를 할당하면 해당 변수는 var로 선언한 전역 변수처럼 취급된다.</p>
</li>
<li><p>실수를 방지하기 위해 Strict Mode를 사용할 수 있다. </p>
<ul>
<li>Strict Mode는 브라우저가 엄격하게 작동하도록 만들어준다.</li>
<li>&quot;선언 없는 변수 할당&quot;의 경우도 에러로 판단해준다.</li>
<li>적용하려면, js 파일 상단에 &#39;use strict&#39;라고 입력하면 된다. (따옴표 포함)</li>
</ul>
</li>
</ul>
<h2 id="예제들">예제들</h2>
<h3 id="전역-변수와-지역-변수가-중복-선언된-경우">전역 변수와 지역 변수가 중복 선언된 경우</h3>
<pre><code class="language-javascript">var a = &quot;global&quot;;

function scope() {
    var a = &quot;local&quot;;
      console.log(a);
}

scope(); // local
console.log(a) // global</code></pre>
<p>전역 영역에서는 전역 변수만이 참조 가능하고 함수 내 지역 영역에서는 전역과 지역 변수 모두 참조 가능하지만 변수명이 중복된 경우 <strong>지역 변수를 우선</strong>하여 참조하게 된다.</p>
<h3 id="내부-함수의-경우">내부 함수의 경우</h3>
<pre><code class="language-javascript">var x = &#39;global&#39;;

function foo() {
  var x = &#39;local&#39;;
  console.log(x);

  function bar() {  // 내부함수
    console.log(x); // local
  }

  bar();
}

foo(); // local, local
console.log(x); // global</code></pre>
<p>내부 함수에서는 자신을 포함하고 있는 함수가 먼저 우선시 되어 local을 반환하게 된다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[GraphQL이란 무엇인가?]]></title>
            <link>https://velog.io/@guri_coding/GraphQL%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</link>
            <guid>https://velog.io/@guri_coding/GraphQL%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</guid>
            <pubDate>Fri, 26 Aug 2022 06:59:23 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/guri_coding/post/e636a1f1-f2e3-4ab7-9d5c-c41c6df2a79a/image.png" alt=""></p>
<p>최근에, 앱 개발이 점점 복잡해지면서 데이터베이스로부터 끌어와야 하는 작업들이 많아지게 되었다. UI 향상을 위해서 클라이언트 개발에서 담당하는 기능이 늘어나게 되고 이에 따라 서버의 API를 호출하는 명세의 수정 작업들도 증가하였다. 이는 기존에 우리가 API를 호출하는 방식이 REST API인데 복잡성이 증가할 수록 점점 까다로워졌다. 이러한 배경에 의해 GraphQL이 생성되고 클라이언트-서버 통신 과정에서 응용 웹 개발자들에게 또다른 대안책을 제시하게 되었다. </p>
<h2 id="graphql-이란">GraphQL 이란?</h2>
<p>공식적으로 나온 GraphQL의 표현은 아래와 같다. </p>
<blockquote>
<p>GraphQL is a query language and a server-side runtime (typically served over HTTP)</p>
</blockquote>
<p>세세하게 보면 <strong>GraphQL is a query language</strong> 라는 문장이 있다. 우리가 흔히 Query를 짠다고 하는 말은 SQL 과 같은 데이터베이스에서 데이터를 가지고 오기 위해 SQL 문을 작성하는 것으로 이해할 수 있다. 그러나 여기서의 Query는 다른 의미를 가지고 있다. </p>
<blockquote>
<p>GraphQL is a language for querying data. Unlike most query languages (such as SQL), you don&#39;t use GraphQL to querya particular type of data store. Instead, <strong>you use GraphQL to query data from any number of different sources.</strong></p>
</blockquote>
<p>이 말처럼 Query를 한다는 것은 서버에 있는 자원을 쿼리와 같은 형식으로 가져오게 된다는 뜻이다. 우리가 흔히 Rest API의 특징으로 살펴보게 되면 <strong>리소스를 통해 서버의 자원을 가져옴</strong> 과 내용을 볼 수 있다. </p>
<pre><code>/v1/get/user/{id}
/v1/get/posts</code></pre><p>위와 같이 url path와 요청 method를 통해서 가져온다. 그러나 GraphQL의 방식은 이와 달리 <code>/graphql</code>이라는 하나의 url로 정의할 수 있다. 기본적으로 GraphQL는 2가지의 핵심 타입을 가지고 있다.</p>
<ol>
<li>Query</li>
<li>Mutation</li>
</ol>
<p><img src="https://velog.velcdn.com/images/guri_coding/post/053f6ba6-6fee-41bd-93f1-77a406bc8d25/image.png" alt=""></p>
<p>Query는 REST API의 <code>GET</code> 방법과 같이 데이터를 가져오는 것이고 Mutatoins은 시스템에 변화를 유발하는 신호를 보내는 의미로 REST API의 <code>POST</code>, <code>DELETE</code> 방식과 같다. </p>
<h2 id="runtime">Runtime</h2>
<p>Runtime 파트는 GraphQL API가 서버에서 운영되는 사실에 초점을 두고 있다. GraphQL API 상에서 우리는 엄격히 형식화된 스키마로 데이터 구조를 잡게 된다. GraphQL에서는 어디서 데이터가 오는지는 중요하지 않다. </p>
<p><img src="https://velog.velcdn.com/images/guri_coding/post/0d0c7b34-7444-4a1a-94af-dcabda38db77/image.png" alt=""></p>
<h2 id="어떻게-동작할까">어떻게 동작할까?</h2>
<h3 id="1-graphql-schema-제작">1. GraphQL Schema 제작</h3>
<p>서버 상에서 우리는 APP UI를 제작하기 위해 필요한 데이터를 기반으로 GraphQL 스키마를 제작한다. 만약 우리가 게시판을 만든다고 하면 우리는 USER, POST, COMMENT를 짜게 된다. </p>
<h3 id="2-데이터-소스로-resolver를-연결">2. 데이터 소스로 resolver를 연결</h3>
<p>각 타입 마다 우리는 GraphQL 상에서 데이터를 연결할 Resolver 함수를 작성하게 된다. 우리가 사용자 데이터를 가져온다고 하면 소셜 로그인을 통해 가져올 수도 있고 DB 상에서 가져올 수도 있게 된다. </p>
<h3 id="3-웹사이트에-쿼리문을-물어보기">3. 웹사이트에 쿼리문을 물어보기</h3>
<p><img src="https://velog.velcdn.com/images/guri_coding/post/0a66e4aa-c2ef-4859-839b-9f1f57530adb/image.png" alt=""></p>
<p>위와 같이 쿼리를 작성했을 때 받아와지는 형식을 볼 수 있는 사이트가 있어 참고하면 된다. </p>
<h2 id="rest-vs-graphql">REST vs GraphQL</h2>
<ul>
<li><strong>형태 정의 및 데이터 요청 방식</strong> : 자원에 대한 접근을 할 때 REST API는 형태를 정의하고 요청방법이 연결되어 있지만 GraphQL 상에서는 분리되어 있다.</li>
<li><strong>자원의 크기와 형태 결정 주체</strong> : REST 상에서는 서버쪽에서 결정하지만, GraphQL은 자원에 대한 정보만 정의하고 필요한 요소들은 Client 요청 시 결정한다.</li>
<li><strong>작업의 유형</strong> : REST 상에서는 url과 method가 결정하지만 GraphQL은 Schema가 Resource를 나타내고 Query, Mutation 타입이 작업의 유형을 나타낸다. </li>
<li><strong>요청 횟수</strong> : REST는 여러 자원에 접근할 때 여러 번의 요청이 필요하지만 GraphQL에서는 한번의 요청에서 여러 Resource에 접근할 수 있다. </li>
<li><strong>작업 처리 방식</strong> : REST에서 각 요청은 해당 엔드포인트에 정의된 핸들링 함수를 호출하여 작업을 처리하지만, GraphQL에서는 요청 받은 각 필드에 대한 resolver를 호출하여 작업을 처리한다. </li>
</ul>
<h2 id="왜-graphql을-써야할까">왜 GraphQL을 써야할까??</h2>
<p><img src="https://velog.velcdn.com/images/guri_coding/post/38881a27-3d72-45f2-9086-91f495821181/image.png" alt=""></p>
<p>위 그림처럼 graphql은 자원에 접근하는 주체가 서버에서 클라이언트로 옮겨가게 함을 알 수 있다. 물론 아직 제대로 써보지 않아서 공감이 되지는 않지만 GraphQL은 쿼리를 결정하는 주체가 클라이언트가 되어 클라이언트의 의사결정 속도를 빠르게 할 수 있고 서버에서의 작업을 줄여준다는 특징이 있다. </p>
<p>또한 의사소통 관계도 많이 달라지게 된다. 클라이언트와 서버가 명세를 보면서 어떻게 구현해야 하는지에 대한 논의가 많아지게 되었지만 좀더 클라이언트 주도적으로 바뀌게 되어 클라이언트와 서버의 소통시간 보다 기능을 구현하는 기획자와 클라이언트의 관계가 좀더 달라지지 않을까 싶다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[알고리즘 개념  - Trie]]></title>
            <link>https://velog.io/@guri_coding/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EA%B0%9C%EB%85%90-Trie</link>
            <guid>https://velog.io/@guri_coding/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EA%B0%9C%EB%85%90-Trie</guid>
            <pubDate>Fri, 17 Jun 2022 13:56:29 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/guri_coding/post/1bf39926-7865-4a57-96d0-a1b7f0c7c50c/image.jpg" alt=""></p>
<h2 id="trie-트라이">Trie 트라이</h2>
<p>알고리즘 문제를 풀다가 보면 문자열의 접두사를 활용해 문제를 풀어야 하는 요소가 존재했다. 그 때마다 항상 트라이라는 것을 한번 외워봐야지 하지만 뒤로 미루는게 습관이 되서 안하다 안하다 결국 이제야 하게 되었다. 잘 정리하는 것보다는 내가 학습을 하는 것에 초점을 두어 글을 작성하도록 하겠다. </p>
<h3 id="트라이-개념">트라이 개념</h3>
<ul>
<li>트라이란 문자열을 저장하고 효율적으로 탐색하기 위한 트리 형태의 자료구조 이다. Radix Tree or Prefix Tree라고도 부른다.</li>
</ul>
<h3 id="파이썬-구현하기">파이썬 구현하기</h3>
<pre><code>class Node(object):
    def __init__(self, key, data=None):
        self.key = key
        self.data = data
        self.children = {}

class Trie(object):
    def __init__(self):
        self.head = Node(None)

    def insert(self, string):
        curr_node = self.head

        for char in string:
            if char not in curr_node.children:
                curr_node.children[char] = Node(char)
            curr_node = curr_node.children[char]

        cur_node.data = string

    def search(self, string):
        curr_node = self.head
        for char in string:
            if char in curr_node.children:
                curr_node = curr_node.children[char]
            else:
                return False

        if curr_node.data != None:
            return True</code></pre><ul>
<li>이렇게 한번 따라서 적어봤는데 손으로 자주 필사해보며 외워야겠다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[알고리즘 스터디 - DFS/BFS 3문제 풀이(feat. 섬의 개수, 물통, 다리 만들기2)]]></title>
            <link>https://velog.io/@guri_coding/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8A%A4%ED%84%B0%EB%94%94-DFSBFS-3%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4feat.-%EC%84%AC%EC%9D%98-%EA%B0%9C%EC%88%98-%EB%AC%BC%ED%86%B5-%EB%8B%A4%EB%A6%AC-%EB%A7%8C%EB%93%A4%EA%B8%B02</link>
            <guid>https://velog.io/@guri_coding/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8A%A4%ED%84%B0%EB%94%94-DFSBFS-3%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4feat.-%EC%84%AC%EC%9D%98-%EA%B0%9C%EC%88%98-%EB%AC%BC%ED%86%B5-%EB%8B%A4%EB%A6%AC-%EB%A7%8C%EB%93%A4%EA%B8%B02</guid>
            <pubDate>Thu, 21 Apr 2022 06:39:39 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/guri_coding/post/b19102ea-1531-47ad-ac07-88a75d37095e/image.jpg" alt=""></p>
<h2 id="백준-4963번--섬의-개수---실버-2">백준 4963번 : 섬의 개수 - 실버 2</h2>
<p>이 문제는 x, y 좌표를 이용해 연결되어 있는 섬을 판별하기 위하여 BFS 알고리즘을 유도하는 문제이다. 주의할 점은 보통 상하좌우 4 방향으로 움직이지만 여기서는 대각선도 이동할 수 있어 8 방향으로 움직인다는 것이다. </p>
<h3 id="알고리즘-코드">알고리즘 코드</h3>
<pre><code>import sys

def bfs(x, y, w, h, land):
    dx = [0, 0, -1, 1, 1, -1, 1, -1]
    dy = [-1, 1, 0, 0, -1, -1, 1, 1]

    q = [(x, y)]

    while q:
        current_x, current_y = q.pop(0)

        for i in range(8):
            new_x, new_y = current_x+dx[i], current_y+dy[i]

            if 0 &lt;= new_x &lt; w and 0 &lt;= new_y &lt; h:
                if land[new_y][new_x] == 1:
                    land[new_y][new_x] = 0
                    q.append((new_x, new_y))

    return 1

while True:
    w, h = map(int, sys.stdin.readline().split())

    if w == 0 and h == 0:
        break

    land = []
    for _ in range(h):
        land.append(list(map(int, sys.stdin.readline().split())))

    answer = 0
    for i in range(h):
        for j in range(w):
            if land[i][j] == 1:
                answer += bfs(j, i, w, h, land)

    print(answer)</code></pre><h2 id="백준-2251번--물통---골드-5">백준 2251번 : 물통 - 골드 5</h2>
<p>물통 같은 경우 BFS/DFS가 visited로 중복 계산을 피하는 것처럼 수많은 경우의 수를 구하고 물통 A가 비어있을 때 C의 용량을 구하는 것이다. 살짝 경우의 수를 작성하는 것이 시간이 걸릴 수  있지만 그래도 실행 시간은 매우 짧다. </p>
<h3 id="알고리즘-코드-1">알고리즘 코드</h3>
<pre><code>import sys

a, b, c = map(int, sys.stdin.readline().split())

result = []
status = []
q = [(0, 0, c)]

while q:
    first, second, third = q.pop(0)ㅁ

    if first == 0 and third not in result:
        result.append(third)

    if (first, second, third) not in status:
        status.append((first, second, third))
        # a exists
        if first != 0:
            # a-&gt;b
            if (b-second) &lt;= first:
                q.append((first - (b-second), b, third))
            else:
                q.append((0, second+first, third))

            # a-&gt;c
            if (c-third) &lt;= first:
                q.append((first-(c-third), second, c))
            else:
                q.append((0, second, third+first))

        # b exists
        if second != 0:
            # b-&gt;a
            if (a-first) &lt;= second:
                q.append((a, second-(a-first), third))
            else:
                q.append((first+second, 0, third))

            # b-&gt;c
            if (c-third) &lt;= second:
                q.append((first, second-(c-third), c))
            else:
                q.append((first, 0, third+second))

        # c exists
        if third != 0:
            # c-&gt;a
            if (a-first) &lt;= third:
                q.append((a, second, third-(a-first)))
            else:
                q.append((first+third, second, 0))

            # c-&gt;b
            if (b-second) &lt;= third:
                q.append((first, b, third-(b-second)))
            else:
                q.append((first, second + third, 0))

print(*sorted(result))</code></pre><h2 id="백준-17472번--다리-만들기-2">백준 17472번 : 다리 만들기 2</h2>
<p>이 문제 같은 경우는 솔직히 너무 어려웠다. 진작에 질문 검색에서 다른 반례들을 찾아보며 해결했어야 했는데 안보고 하다가 나중에 다른 사람들이 올려준 테스트 케이스를 기반으로 수정에 수정을 거듭해 10번 시도하고 드디어 성공 했다. 알고리즘 구성은 <strong>BFS -&gt; Brute Force -&gt; Kruskal</strong> 이 3가지로 알고리즘을 순서대로 잘 쓰면 되지만 문제는 어떤 경우에 실패하는지 생각하지 못하기 때문에 어렵다. 알고리즘이 사고력을 요하는 문제임을 다시 깨닫게 된 문제였다. 열심히 머리를 굴리자</p>
<h3 id="알고리즘-코드-2">알고리즘 코드</h3>
<pre><code>import sys

# n, m input
n, m = map(int, sys.stdin.readline().split())

# sea, land
land = []
visited = [[False] * m for _ in range(n)]

for _ in range(n):
    land.append(list(map(int, sys.stdin.readline().split())))

# position of island
island = []

# Using BFS, find an individual algorithm
def bfs(x, y):
    q = [(x, y)]
    coordinate = set()
    coordinate.add((x, y))

    dx = [0, 0, 1, -1]
    dy = [1, -1, 0, 0]

    while q:
        current_x, current_y = q.pop(0)

        for i in range(4):
            new_x = current_x + dx[i]
            new_y = current_y + dy[i]

            if 0 &lt;= new_x &lt; m and 0 &lt;= new_y &lt; n and land[new_y][new_x] == 1 and visited[new_y][new_x] == False:
                visited[new_y][new_x] = True
                coordinate.add((new_x, new_y))
                q.append((new_x, new_y))
    return coordinate

for i in range(n):
    for j in range(m):
        if land[i][j] == 1 and visited[i][j] == False:    
            island.append(bfs(j, i))


# Using Brute force, find the distance of two islands
edges = []

for i in range(len(island)):
    for j in range(len(island)):
        if i != j:
            min_distance = 101

            for x, y in island[i]:
                for next_x, next_y in island[j]:
                    if x == next_x:
                        if abs(y - next_y) &gt; 2:
                            flag = False
                            bigger = max(y, next_y)
                            smaller = min(y, next_y)

                            for k in range(smaller+1, bigger):
                                if land[k][x] == 1:
                                    flag = True
                                    break

                            if flag == False:
                                min_distance = min(min_distance, abs(y - next_y)-1)

                    if y == next_y:
                        if abs(x - next_x) &gt; 2:
                            flag = False
                            bigger = max(x, next_x)
                            smaller = min(x, next_x)

                            for k in range(smaller+1, bigger):
                                if land[y][k] == 1:
                                    flag = True
                                    break

                            if flag == False:
                                min_distance = min(min_distance, abs(x - next_x)-1)

            if min_distance != 101 and (min_distance, j, i) not in edges:
                edges.append((min_distance, i, j)) 

edges.sort()
# Using mst Algorithm, find the minimum spanning tree
parent = [i for i in range(len(island))]

answer = 0

def find(x):
    if parent[x] != x:
        parent[x] = find(parent[x])
    return parent[x]

def union(x, y):
    x = find(x)
    y = find(y)

    if x &lt;= y:
        parent[y] = x
    else:
        parent[x] = y

edges.sort()

for edge in edges:
    weight, x, y = edge
    if find(x) != find(y):
        union(x, y)
        answer += weight

wrong = False
for i in range(len(island)):
    for j in range(len(island)):
        if find(i) != find(j):
            wrong = True
            break
    if wrong:
        break

if edges and wrong == False:
    print(answer)
else:
    print(-1)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[알고리즘 스터디 - 정렬 3문제 풀이 (feat. 통나무 건너뛰기, 색종이 올려 놓기, 장난감 조립)]]></title>
            <link>https://velog.io/@guri_coding/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8A%A4%ED%84%B0%EB%94%94-%EC%A0%95%EB%A0%AC-3%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-feat.-%ED%86%B5%EB%82%98%EB%AC%B4-%EA%B1%B4%EB%84%88%EB%9B%B0%EA%B8%B0-%EC%83%89%EC%A2%85%EC%9D%B4-%EC%98%AC%EB%A0%A4-%EB%86%93%EA%B8%B0-%EC%9E%A5%EB%82%9C%EA%B0%90-%EC%A1%B0%EB%A6%BD</link>
            <guid>https://velog.io/@guri_coding/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8A%A4%ED%84%B0%EB%94%94-%EC%A0%95%EB%A0%AC-3%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-feat.-%ED%86%B5%EB%82%98%EB%AC%B4-%EA%B1%B4%EB%84%88%EB%9B%B0%EA%B8%B0-%EC%83%89%EC%A2%85%EC%9D%B4-%EC%98%AC%EB%A0%A4-%EB%86%93%EA%B8%B0-%EC%9E%A5%EB%82%9C%EA%B0%90-%EC%A1%B0%EB%A6%BD</guid>
            <pubDate>Wed, 20 Apr 2022 12:58:23 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/guri_coding/post/73723bf2-cea2-451e-a16c-73c2af76b9b3/image.jpg" alt=""></p>
<p>정렬 문제도 풀이를 하게 되었는데 정렬에 위상정렬도 들어가 있고 퀵 정렬, 힙 정렬 등 다양한 것들이 존재를 하여 일부로 조금 섞게 되었다. </p>
<h2 id="백준-11497번--통나무-건너뛰기---실버-1">백준 11497번 : 통나무 건너뛰기 - 실버 1</h2>
<p>여러 개의 통나무 길이가 주어지는데 통나무 길이 간의 간격을 줄여 원형으로 배치하는 문제이다. 여기서 난이도란 옆칸의 통나무 길이 차이가 최소가 되도록 하고 그 중 가장 큰 길이 차이를 말한다. 이번에 힙 정렬 중 최대 힙을 사용하여 가장 큰 통나무를 가운데에 놓고 왼쪽은 작은 것, 오른쪽은 큰 것을 놓아 구성을 하고 갱신할 때마다 난이도를 새로 생성하게 되었다. </p>
<h3 id="알고리즘-코드">알고리즘 코드</h3>
<pre><code>import sys, heapq

t = int(sys.stdin.readline())

result = []

for _ in range(t):
    n = int(sys.stdin.readline())
    tree = list(map(int, sys.stdin.readline().split()))

    h = []
    for i in tree:
        heapq.heappush(h, (-i, i))

    if n &gt;= 3:
        difference = 0
        center = heapq.heappop(h)[1]
        right = heapq.heappop(h)[1]
        left = heapq.heappop(h)[1]

        difference = max(difference, center-left)
        difference = max(difference, center-right)
        while h:
            if len(h) == 0:
                break

            if len(h) == 1:
                final = heapq.heappop(h)[1]
                difference = max(difference, right-final)
                break

            first = heapq.heappop(h)[1]
            second = heapq.heappop(h)[1]

            difference = max(difference, left-second)
            difference = max(difference, right-first)

            right, left = first, second

        result.append(difference)
    elif n == 2:
        result.append(abs(tree[0]-tree[1]))

    else:
        result.append(tree[0])

for i in result:
    print(i)</code></pre><h2 id="백준-2643번--색종이-올려-놓기---골드-4">백준 2643번 : 색종이 올려 놓기 - 골드 4</h2>
<p>이번 문제 같은 경우에는 정렬 + 가장 긴 증가하는 부분 수열(LIS) 결합 문제로 색종이의 최대 개수도 100장으로 작았기에 맘 편히 Python 내장 함수를 마구 썼다. 먼저, 입력을 받을 때 가장 작은 부분이 오도록 먼저 정렬을 하고 paper 배열에 집어 넣었다. 그 다음, x를 기준으로 sort를 하게 되면 우리가 이 종이가 fit한지 살펴보려면 y값만 기준으로 판단하면 된다. 여기서 y 값들만 비교하여 DP의 가장 긴 증가하는 부분 수열로 최장 길이를 찾으면 된다. 최종 코드는 아래와 같다. (근데 DP도 선택 정렬과 유사하게 O(N^2)로 판단을 하는 구만 나중에 다른 방식도 공부해봐야겠다.) </p>
<h3 id="알고리즘-코드-1">알고리즘 코드</h3>
<pre><code>import sys

n = int(sys.stdin.readline())
paper = []

for _ in range(n):
    width = list(map(int, sys.stdin.readline().split()))
    paper.append(sorted(width))

paper.sort()

dp = [1 for _ in range(n)]

for i in range(n):
    for j in range(i):
        if paper[i][1] &gt;= paper[j][1]:
            dp[i] = max(dp[i], dp[j] + 1)

print(max(dp))</code></pre><h2 id="백준-2637번--장난감-조립---골드-2">백준 2637번 : 장난감 조립 - 골드 2</h2>
<p>이 문제 같은 경우 최종 장난감을 만들기 위한 중간 부품 및 기본 부품들이 존재하고 중간 부품은 기본 부품 및 다른 중간 부품을 통하여 만들 수 있다. 서로 어떤 것을 하나 만들기 위하여 선행되어 필요한 부품들이 존재하는 것을 알 수 있다. 여기서 위상정렬을 사용하는 것을 알 수 있었지만 중요한 것은 <strong>최종적으로 필요한 기본 부품들의 수를 구하는 것</strong>이다. 아래의 코드만 위상 정렬 아래의 넣으면 최종 개수를 알 수 있다.</p>
<pre><code>if now == n:
    part[next] += number
else:
    if now in middle:
        part[next] += (part[now] * number)</code></pre><h3 id="알고리즘-코드-2">알고리즘 코드</h3>
<pre><code>from collections import deque
import sys

n = int(sys.stdin.readline())
m = int(sys.stdin.readline())

degree = [0] * (n+1)
graph = [[] for _ in range(n+1)]
part = [0] * n
middle = set()

for _ in range(m):
    x, y, k = map(int, sys.stdin.readline().split())
    graph[x].append([y, k])
    middle.add(x)
    degree[y] += 1

def topology_sort():
    result = []
    q = deque()

    for i in range(1, n+1):
        if degree[i] == 0:
            q.append(i)

    while q:
        now = q.popleft()
        result.append(now)

        for next, number in graph[now]:
            degree[next] -= 1

            if degree[next] == 0:
                q.append(next)

            if now == n:
                part[next] += number
            else:
                if now in middle:
                    part[next] += (part[now] * number)

topology_sort()

for i in range(1, n):
    if i not in middle:
        print(i, part[i])</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[알고리즘 스터디 - 그리디 3문제 풀이 (feat. 블로그2, 우체국, 공항)]]></title>
            <link>https://velog.io/@guri_coding/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8A%A4%ED%84%B0%EB%94%94-%EA%B7%B8%EB%A6%AC%EB%94%94-3%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-feat.-%EB%B8%94%EB%A1%9C%EA%B7%B82-%EC%9A%B0%EC%B2%B4%EA%B5%AD-%EA%B3%B5%ED%95%AD</link>
            <guid>https://velog.io/@guri_coding/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8A%A4%ED%84%B0%EB%94%94-%EA%B7%B8%EB%A6%AC%EB%94%94-3%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-feat.-%EB%B8%94%EB%A1%9C%EA%B7%B82-%EC%9A%B0%EC%B2%B4%EA%B5%AD-%EA%B3%B5%ED%95%AD</guid>
            <pubDate>Wed, 20 Apr 2022 10:03:00 GMT</pubDate>
            <description><![CDATA[<h2 id="백준-20365번--블로그2---실버-2">백준 20365번 : 블로그2 - 실버 2</h2>
<p><a href="https://www.acmicpc.net/problem/20365">블로그2</a> 같은 경우에는 블로그에다가 문제 풀이 결과를 색칠하는 문제로 가장 적게 색칠을 하는 수를 구하는 것이다. </p>
<p><img src="https://velog.velcdn.com/images/guri_coding/post/f4ae05b4-b4d5-4b35-8f82-a316e734772b/image.png" alt=""></p>
<p>위 사진처럼 색깔을 덮어써서 최소의 경우의 수를 구하는 것이다. </p>
<p><strong>Test Case</strong></p>
<pre><code>1. N = 8 color = BBRBRBBR / answer = 4
2. N = 6 color = BRBBRR / answer = 3
3. N = 6 color = BRRRRB / answer = 2</code></pre><p>이 케이스의 경우처럼 <strong>BB가 연속이면 B, RR이면 R로 하나의 경우</strong>로 쳐서 계산을 해야 한다는 것이다. 이 경우를 고려해서 문제를 풀었을 때 내 코드는 아래와 같다.</p>
<h3 id="내-코드">내 코드</h3>
<pre><code>n = int(input())
color = input()

blue, red = 0, 0

final = color[0]
if final == &quot;R&quot;:
    red += 1
else:
    blue += 1

for i in range(1, n):
    if color[i] == &quot;B&quot;:
        if final[-1] == &quot;B&quot;:
            continue
        else:
            final += color[i]
            blue += 1
    else:
        if final[-1] == &quot;R&quot;:
            continue
        else:
            final += color[i]
            red += 1

answer = 1
if blue &gt;= red:
    answer += red
else:
    answer += blue

print(answer)</code></pre><h3 id="다른-사람-코드">다른 사람 코드</h3>
<pre><code>import sys
input = sys.stdin.readline 

n = int(input())
p = input()
anw = p[0]

for i in p:
    if i != anw[-1]:
        anw += i

print(min(anw.count(&#39;B&#39;), anw.count(&#39;R&#39;)) + 1)</code></pre><p>내 코드를 돌렸을 때는 너무 많은 시간이 걸렸는데 다른 사람 코드를 보고 이렇게 최적화 할 수 있구나 하면서 나는 그러지 못한게 너무 아쉽다. </p>
<h2 id="백준-2141번--우체국---골드-4">백준 2141번 : 우체국 - 골드 4</h2>
<p>이 문제 같은 경우는 그리디인줄 모를 정도로 정렬 후 계산하는 문제만 존재하였다. 그럼에도 불구하고 골드 4인 이유는 테스트 케이스가 한 개만 있어서 다른 경우의 수를 생각하기 어렵기 때문이다. 예를 들어, 기존 테스트 케이스를 보게 되면 당연히 거리 순대로 인구 수와 동일하게 나온다고 생각할 수 있다.</p>
<pre><code>3
1 3
2 5
3 3</code></pre><p>하지만 그것만 보면 실패를 하게 된다. 왜냐하면 실제 입력은 순서대로 나오지 않고 섞이기 때문에 정렬하는 과정이 필요하다. 그리고 이 문제의 핵심은 <strong>오른쪽, 왼쪽이 전체 인구 수의 중간이 되는 최적의 위치를 찾는 것</strong>이다. 그래서 이를 적용하면 바로 문제가 풀리게 된다. 물론 4번만에 풀게 되었다.</p>
<h3 id="알고리즘-코드">알고리즘 코드</h3>
<pre><code>import sys

n = int(sys.stdin.readline())
village = []
total = 0

for _ in range(n):
    x, a = map(int, sys.stdin.readline().split())
    village.append([x, a])
    total += a

village.sort()

temp = 0
for i in range(n):
    temp += village[i][1]
    if (temp &gt;= (total+1)//2):
        print(village[i][0])
        break</code></pre><h2 id="백준-10775번--공항---골드-2">백준 10775번 : 공항 - 골드 2</h2>
<p>이 문제는 공항에서 비행기가 착륙을 하는데 도킹할 수 있는 게이트의 최대 개수를 찾는 것이다. 여기서 일일이 빈 게이트를 찾는 것은 어려움이 존재하기 때문에 비어있는 게이트를 찾기 위하여 유니온-파인드 함수를 사용하여 찾게 된다. 근데 문제는 이게 왜 그리디일까 라는 의문이다. 물론, 그리디라고 말을 하면 가장 먼저 찾은 것을 도킹 해준다고 할 수 있지만 아직은 그리디인지는 의심이 간다. 그래도 분리 집합 (Disjoint Set)을 학습할 수 있어 좋았다.</p>
<pre><code>import sys

g = int(sys.stdin.readline())
p = int(sys.stdin.readline())

def find(x):
    if gate[x] != x:
        gate[x] = find(gate[x])

    return gate[x]

def union(a, b):
    a = find(a)
    b = find(b)

    if a &lt; b:
        gate[b] = a
    else:
        gate[a] = b

gate = [i for i in range((g+1))]

answer = 0
for i in range(1, p+1):
    docking = int(sys.stdin.readline())
    parent = find(docking)

    if parent == 0:
        break
    else:
        answer += 1
        union(parent, parent-1)

print(answer)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[알고리즘 스터디 - 구현 2문제 풀이(feat. Python)]]></title>
            <link>https://velog.io/@guri_coding/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8A%A4%ED%84%B0%EB%94%94-%EA%B5%AC%ED%98%84-2%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4feat.-Python</link>
            <guid>https://velog.io/@guri_coding/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8A%A4%ED%84%B0%EB%94%94-%EA%B5%AC%ED%98%84-2%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4feat.-Python</guid>
            <pubDate>Mon, 18 Apr 2022 14:01:02 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/guri_coding/post/5ecd5d34-2b6e-40d7-b621-8494f844ba2b/image.jpg" alt=""></p>
<p>이번에 취업 스터디를 만들면서 모든 기업이 그렇듯이 코테 통과가 우선이고 여기서 수많은 사람들이 탈락하기에 중점을 알고리즘 문제 풀이 습관에 두었다. </p>
<p>또한, CS 및 이력서 점검도 사실 전에 스터디를 해보는 결과 한번 외우고 세팅이 되면 어렵지 않은 부분이라 초점을 알고리즘으로 두어 이코테에 나온 문제 유형 분포에 따라서 과제도 세팅을 하였다. 오늘의 주제는 <strong>구현</strong>으로 실버1, 골드4 단계별로 문제를 풀고 익숙해지기로 했다. </p>
<h2 id="백준-20207번--달력---실버-1">백준 20207번 : 달력 - 실버 1</h2>
<pre><code>n = int(input())

calender = [0] * 366

for _ in range(n):
    s, e = map(int, input().split())

    for index in range(s, e+1):
        calender[index] += 1

x_len = 0
y_len = 0

answer = 0

for i in range(366):
    if calender[i] != 0:
        x_len = max(x_len, calender[i])
        y_len += 1
    else:
        answer += x_len * y_len
        x_len = 0
        y_len = 0


answer += x_len * y_len
print(answer)</code></pre><h2 id="백준-22856번--트리-순회---골드-4">백준 22856번 : 트리 순회 - 골드 4</h2>
<p>문제를 풀었는데 계속 46%에서 틀렸다고 떠서 다른 사람들거를 보며 따라 치며 가져오게 되었다ㅠㅠ</p>
<pre><code>import sys
sys.setrecursionlimit(10**7)

left = dict()
right = dict()

N = int(input())
parent = [0] * (N+1)
node_count = 0
for _ in range(N):
    a, b, c = map(int, input().split())
    left[a] = b
    right[a] = c

    if b != -1:
        parent[b] = a
        node_count += 1
    if c != -1:
        parent[c] = a
        node_count += 1

# 마지막 노드 구하는 파트
last_node = 0
def traverse(node):
    global last_node
    if node == -1:
        return
    traverse(left[node])
    last_node = node
    traverse(right[node])

traverse(1)
edge_count = node_count * 2
movement = 0
# 마지막 노드까지 이동 경로의 거리 구함
while last_node != 1:
    movement += 1
    last_node = parent[last_node]
print(edge_count - movement)</code></pre><ul>
<li>구현 문제는 주어진 논리 구조에 따라서 알고리즘을 스스로 만들어가는 능력을 평가하는 것으로 실버 1 문제가 바로 이 것에 해당한다. 그리고 골드 4문제 같은 경우는 기존에 다른 알고리즘+알고리즘 구현을 요구하는 것임을 알 수 있었다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[알고리즘 스터디 - 백준 7620번 : 편집 거리 (feat. Python)]]></title>
            <link>https://velog.io/@guri_coding/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8A%A4%ED%84%B0%EB%94%94-%EB%B0%B1%EC%A4%80-7620%EB%B2%88-%ED%8E%B8%EC%A7%91-%EA%B1%B0%EB%A6%AC-feat.-Python</link>
            <guid>https://velog.io/@guri_coding/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8A%A4%ED%84%B0%EB%94%94-%EB%B0%B1%EC%A4%80-7620%EB%B2%88-%ED%8E%B8%EC%A7%91-%EA%B1%B0%EB%A6%AC-feat.-Python</guid>
            <pubDate>Sun, 10 Apr 2022 08:04:49 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/guri_coding/post/18f84991-02fb-4106-9c6a-ff4f21b030fe/image.jpg" alt=""></p>
<h2 id="문제-해석">문제 해석</h2>
<ol>
<li><p>문자열 2개가 주어진다</p>
</li>
<li><p>편집 과정에서 할 수 있는 명령은 아래와 같다.</p>
<ul>
<li>추가 (&#39;a&#39;) : 한 글자를 출력하고 입력 문자열을 건드리지 않는다.</li>
<li>삭제 (&#39;d&#39;) : 입력 문자열 맨 앞 한 글자를 삭제하고 아무 것도 출력하지 않는다.</li>
<li>수정 (&#39;m&#39;) : 입력 문자열 맨 앞 한 글자를 삭제하고 다른 글자로 바꿔 출력한다.</li>
<li>복사 (&#39;c&#39;) : 맨 앞 글자를 삭제하고 삭제한 그 글자를 출력한다.</li>
</ul>
</li>
<li><p>첫 번째 문자열을 두 번째 문자열로 바꾸는 가장 짧은 편집 스크립트를 작성해야 함</p>
</li>
</ol>
<h3 id="어떤-알고리즘을-써야할까">어떤 알고리즘을 써야할까?</h3>
<ol>
<li>dp를 이용하여 편집 거리를 구함</li>
<li>가장 작은 수부터 되돌아가면서 배열의 저장</li>
<li>배열의 저장한 것을 반대로 출력</li>
</ol>
<h2 id="알고리즘-코드">알고리즘 코드</h2>
<blockquote>
<p>이 문제 Python3, Pypy 둘다 적용해도 풀리지 않는다. 메모리 초과가 나타나고 Python으로 풀었던 사람이 아예 없다. 그래도 일단 테케는 통과했으니 이 문제는 여기까지,,</p>
</blockquote>
<pre><code>n = input()
m = input()

dp = [[0] * (len(n) + 1) for _ in range(len(m) + 1)]

for i in range(1, len(m) + 1):
    dp[i][0] = i

for j in range(1, len(n) + 1):
    dp[0][j] = j

for i in range(1, len(m) + 1):
    for j in range(1, len(n) + 1):
        if m[i-1] == n[j-1]:
            # 두 문자가 같은 경우, 추가 연산이 필요하지 않으므로 (i-1, j-1)에서 가져온다. &#39;c&#39; 
            dp[i][j] = dp[i-1][j-1]
        else:
            # 두 문자가 다른 경우, (교체 &#39;m&#39;| 추가 &#39;a&#39; | 삭제 &#39;d&#39;) 세 연산 중 cost가 가장 적은 것을 기록한다. 
            dp[i][j] = min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]) + 1

len_n = len(n)
len_m = len(m)

trace_array = []

while len_n &gt; -1 and len_m &gt; -1:
    index_pos = min(dp[len_m-1][len_n], dp[len_m-1][len_n-1], dp[len_m][len_n-1])
    if index_pos == dp[len_m][len_n]:
        trace_array.append(&quot;c &quot; + n[len_n-1])
        len_n -= 1
        len_m -= 1
    else:
        if index_pos == dp[len_m][len_n-1]:
            trace_array.append(&quot;d &quot; + n[len_n-1])
            len_n -= 1
        elif index_pos == dp[len_m-1][len_n-1]:
            trace_array.append(&quot;m &quot; + m[len_m-1])
            len_n -= 1
            len_m -= 1
        else:
            trace_array.append(&quot;a &quot; + m[len_m-1])
            len_m -= 1

for index in range(len(trace_array)-2, -1, -1):
    print(trace_array[index])</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[알고리즘 스터디 - 백준 23269번 : 엘리베이터 조작 (feat. Python)]]></title>
            <link>https://velog.io/@guri_coding/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8A%A4%ED%84%B0%EB%94%94-%EB%B0%B1%EC%A4%80-23269%EB%B2%88-%EC%97%98%EB%A6%AC%EB%B2%A0%EC%9D%B4%ED%84%B0-%EC%A1%B0%EC%9E%91-feat.-Python</link>
            <guid>https://velog.io/@guri_coding/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8A%A4%ED%84%B0%EB%94%94-%EB%B0%B1%EC%A4%80-23269%EB%B2%88-%EC%97%98%EB%A6%AC%EB%B2%A0%EC%9D%B4%ED%84%B0-%EC%A1%B0%EC%9E%91-feat.-Python</guid>
            <pubDate>Thu, 07 Apr 2022 05:36:24 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/cloudflare/guri_coding/14193797-9881-4ae3-b587-2071733ff368/%E1%84%8B%E1%85%A1%E1%86%AF%E1%84%80%E1%85%A9%E1%84%85%E1%85%B5%E1%84%8C%E1%85%B3%E1%86%B7%20%E1%84%85%E1%85%A9%E1%84%80%E1%85%A9.jpg" alt=""></p>
<h2 id="문제-해석">문제 해석</h2>
<ol>
<li>1층부터 N층까지 사람들이 엘리베이터를 기다리고 있고</li>
<li>각 사람 마다 가고 싶어하는 층이 존재하고 있고</li>
<li>엘리베이터에는 1명만 탑승할 수 있다</li>
</ol>
<p><strong>입력</strong></p>
<ol>
<li>건물 층 수 N</li>
<li>N개의 정수 A1~AN, 그 층에 존재하는 사람들이 가고 싶어하는 층이 나온다.</li>
</ol>
<h3 id="어떤-알고리즘을-써야할까">어떤 알고리즘을 써야할까??</h3>
<p>일단 문제를 해석하기에 앞서서 알고리즘 분류별 문제에서 선정했기에 위상정렬 문제인 것은 잘 알고 있었다. 그래서 처음에는 위상정렬을 적용해 아래와 같은 코드로 작성하여 테스트케이스를 통과했다.</p>
<pre><code># if everyone arrives in destination, degree should have only zero
while elevator:
    if len(set(degree)) == 1 or len(set(visited)) == 1:
        break

    # get the current person and find next destination
    current_person = elevator.pop(0)
    visited[current_person] = True

    # go to destination and add the visiting place
    result.append(floor[current_person])
    degree[floor[current_person]] -= 1

    # find the next person
    next_person = floor[current_person]

    # if there is no person in that floor
    if visited[next_person]:
        # find the place you didn&#39;t visit
        # if there is no where to go, break
        for i in range(1, n+1):
            if visited[i] == False:
                elevator.append(i)
                result.append(i)
                break

        if len(set(degree)) == 1 or len(set(visited)) == 1:
            break
        # continue
    # if you did not visit a place, add next_person and decrease the degree
    else:
        if len(set(visited)) != 1:
            elevator.append(next_person)</code></pre><p>근데 왠걸? 시간초과가 나타나게 되었고 자꾸 다른 부분을 수정을 해도 실패하거나 시간초과가 뜨게 되었다. 그래서 아예 다른 방식으로 생각을 하게 되었다.</p>
<p><strong>DFS를 사용해 갈 수 있는 곳까지 방문</strong></p>
<p>알고리즘 분류를 보니까 깊이 우선 탐색이 존재함을 알게 되어 DFS를 사용하여 결과 값에 계속 집어넣으며 수정을 거듭해서 테스트케이스를 통과하여 제출을 했다.</p>
<p>또 왠걸? 2%까지 가다가 틀리게 되었다. 그래서 멘붕이 왔었는데 좀 생각해보니 위상정렬로 분류로 되어 있었다는 것은 반드시 이러한 부분을 사용해야 되는 것이 아닐까 싶어서 degree가 가장 작은 노드부터 시작하는 위상정렬의 특성과 자동으로 정렬해주는 우선순위 큐 heap을 사용하였다. </p>
<h4 id="peusdo-code-작성하기">Peusdo Code 작성하기</h4>
<ol>
<li>n, floor를 입력받기</li>
<li>각 floor마다 가고자 하는 곳의 degree를 늘려줌</li>
<li>1층부터 시작함으로 1층부터 깊이우선탐색을 진행</li>
<li>방문하지 않은 층들을 찾아 elevator로 삽입해줌</li>
<li>degree가 가장 작은 것부터 트리에서 꺼내 dfs를 진행하고 결과를 출력</li>
</ol>
<h2 id="알고리즘-코드">알고리즘 코드</h2>
<blockquote>
<p>Visual Studio Code에서 한글로 작성하면 자꾸 깨져가지고 귀찮아서 영어로 주석을 달아놓았다. 그리고 Python으로 작성한 글은 검색해보니 나만 있던데 조회수 잘 나오겠지??</p>
</blockquote>
<pre><code>import heapq

# get values from input
n = int(input())
floor = [0] + list(map(int, input().split()))

# result means the elevator buttons 
result = []

# for get sequence, using degree
degree = [0] * (n+1)
for i in floor:
    degree[i] += 1

# for finding the person who did not use elevator, declare visited
visited = [True] + [False] * (n)

# using dfs, find the deepest path of tree
def dfs(x):
    if visited[x]:
        return
    visited[x] = True
    result.append(floor[x])
    dfs(floor[x])

# starting point is first floor
dfs(1)

elevator = []

# by using heap, start the smallest value of degree
for i in range(1, n+1):
    if visited[i] == False:
        heapq.heappush(elevator, (degree[i], i))

while elevator:
    priority, current = heapq.heappop(elevator)

    if visited[current]: continue
    # if there is place elevator didn&#39;t visit, go to that place
    result.append(current)
    dfs(current)

# print the output
print(len(result))
print(*result)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[오류 디버깅] 맥에서 vscode includePath를 찾을 수 없습니다.]]></title>
            <link>https://velog.io/@guri_coding/%EC%98%A4%EB%A5%98-%EB%94%94%EB%B2%84%EA%B9%85-%EB%A7%A5%EC%97%90%EC%84%9C-vscode-includePath%EB%A5%BC-%EC%B0%BE%EC%9D%84-%EC%88%98-%EC%97%86%EC%8A%B5%EB%8B%88%EB%8B%A4</link>
            <guid>https://velog.io/@guri_coding/%EC%98%A4%EB%A5%98-%EB%94%94%EB%B2%84%EA%B9%85-%EB%A7%A5%EC%97%90%EC%84%9C-vscode-includePath%EB%A5%BC-%EC%B0%BE%EC%9D%84-%EC%88%98-%EC%97%86%EC%8A%B5%EB%8B%88%EB%8B%A4</guid>
            <pubDate>Tue, 05 Apr 2022 04:56:04 GMT</pubDate>
            <description><![CDATA[<p>말 그대로 맥북 vscode에서 c파일을 작성하고 #include을 설정하였는데 stdio.h 파일을 불러 올수 없고 includePath를 설정해달라는 요청이 들어와서 이게 무슨 오류인가 살펴보면서 디버깅을 진행하게 되었다.</p>
<h3 id="1-c_cpp_propertiesjson-파일-수정하기">1. c_cpp_properties.json 파일 수정하기</h3>
<pre><code>gcc --version</code></pre><p>터미널을 켜서 위 명령어를 치면 아래의 결과가 나온다.</p>
<p><img src="https://velog.velcdn.com/cloudflare/guri_coding/0b714d90-5a3a-46a0-b826-c9e6ebc3421b/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-04-05%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%201.48.57.png" alt=""></p>
<p>여기서 <strong>InstalledDir</strong> 에 나와있는 경로를 복사해서 vscode에 c_cpp_properties.json 파일 <strong>includePath</strong>에 추가하면 된다.</p>
<p><img src="https://velog.velcdn.com/cloudflare/guri_coding/413f0cd3-59e5-4346-8dce-bd5eeb7ac54d/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-04-05%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%201.51.14.png" alt=""></p>
<h3 id="2-xcode를-다시-빌드하기">2. xcode를 다시 빌드하기</h3>
<p>원래 1번만 하면 된다고 나와서 이를 적용했는데도 제대로 수정이 되지 않아서 다시 한번 조사를 해보았는데 내 vscode는 Xcode에서 c 설정 파일을 가져오도록 되어 있다. </p>
<pre><code>xcode-select --install</code></pre><p>위 명령어를 치게 되면 새롭게 빌드하게 된다. </p>
<p><img src="https://velog.velcdn.com/cloudflare/guri_coding/60eccda1-ed3a-4639-a90b-b65e8598d3a8/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-04-05%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%201.53.59.png" alt=""></p>
<p>그러면 최종적으로 vscode에서 path를 잘 인식해서 가져오게 된다!! 디버깅하기 귀찮아서 미뤄왔는데 결국에는 해결을 하게 되었다. 소리질러!!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[알고리즘 스터디 - 백준 2146번 : 다리 만들기(feat. Python)]]></title>
            <link>https://velog.io/@guri_coding/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8A%A4%ED%84%B0%EB%94%94-%EB%B0%B1%EC%A4%80-2146%EB%B2%88-%EB%8B%A4%EB%A6%AC-%EB%A7%8C%EB%93%A4%EA%B8%B0feat.-Python</link>
            <guid>https://velog.io/@guri_coding/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8A%A4%ED%84%B0%EB%94%94-%EB%B0%B1%EC%A4%80-2146%EB%B2%88-%EB%8B%A4%EB%A6%AC-%EB%A7%8C%EB%93%A4%EA%B8%B0feat.-Python</guid>
            <pubDate>Tue, 29 Mar 2022 04:07:34 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/guri_coding/post/5e4362b0-0487-4a16-83d8-373d5e4beabc/%E1%84%8B%E1%85%A1%E1%86%AF%E1%84%80%E1%85%A9%E1%84%85%E1%85%B5%E1%84%8C%E1%85%B3%E1%86%B7%20%E1%84%85%E1%85%A9%E1%84%80%E1%85%A9.jpg" alt=""></p>
<h2 id="문제-해석">문제 해석</h2>
<ul>
<li>NxN 크기의 이차원 평면상에 여러 섬이 존재하고 있음</li>
<li>여기서 두 개의 섬을 이을 때 가장 짧은 다리의 길이를 출력하여야 함</li>
</ul>
<h3 id="어떤-알고리즘을-써야할까">어떤 알고리즘을 써야할까?</h3>
<ul>
<li>다리 만들기 2를 하고 나서 돌아와서 보니까 과정이 줄었다는 것을 알 수 있다.</li>
<li>BFS로 섬을 구분하고 브루트 포스로 계산을 하면 되는 것이다.</li>
</ul>
<h2 id="알고리즘-코드">알고리즘 코드</h2>
<pre><code>from collections import deque

n = int(input())
land = [list(map(int, input().split())) for _ in range(n)]

def bfs(x, y):
    dx = [0, 0, -1, 1]
    dy = [-1, 1, 0, 0]

    q = deque()
    q.append((x, y))

    coordinate = [(x, y)]
    land[x][y] = 0

    while q:
        x_, y_ = q.popleft()

        for i in range(4):
            new_x = x_ + dx[i]
            new_y = y_ + dy[i]

            if 0 &lt;= new_x &lt; n and 0 &lt;= new_y &lt; n and land[new_x][new_y] == 1:
                land[new_x][new_y] = 0
                q.append((new_x, new_y))
                coordinate.append((new_x, new_y))

    return coordinate

island = []

for i in range(n):
    for j in range(n):
        if land[i][j] == 1:
            island.append(bfs(i, j))

answer = 10001
for i in range(len(island)):
    for j in range(i+1, len(island)):
        for x, y in island[i]:
            for new_x, new_y in island[j]:
                answer = min(answer, abs(new_x-x) + abs(new_y-y))

print(answer-1)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[알고리즘 스터디 - 백준 1717번 : 집합의 표현]]></title>
            <link>https://velog.io/@guri_coding/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8A%A4%ED%84%B0%EB%94%94-%EB%B0%B1%EC%A4%80-1717%EB%B2%88-%EC%A7%91%ED%95%A9%EC%9D%98-%ED%91%9C%ED%98%84</link>
            <guid>https://velog.io/@guri_coding/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8A%A4%ED%84%B0%EB%94%94-%EB%B0%B1%EC%A4%80-1717%EB%B2%88-%EC%A7%91%ED%95%A9%EC%9D%98-%ED%91%9C%ED%98%84</guid>
            <pubDate>Sat, 26 Mar 2022 06:09:33 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/guri_coding/post/05bacb60-a0c2-4cfb-95af-bbe1033a4f12/%E1%84%8B%E1%85%A1%E1%86%AF%E1%84%80%E1%85%A9%E1%84%85%E1%85%B5%E1%84%8C%E1%85%B3%E1%86%B7%20%E1%84%85%E1%85%A9%E1%84%80%E1%85%A9.jpg" alt=""></p>
<h2 id="문제-해석">문제 해석</h2>
<ul>
<li>초기 0~n까지의 숫자가 각각 총 n+1개의 집합을 이루고 있을 때 2가지 연산을 진행함</li>
</ul>
<p>1) 합집합 연산 - 0 a b 형태로 주어짐
2) 두 원소가 같은 집합에 포함되어 있는지 확인 - 1 a b의 형태로 이루어짐</p>
<ul>
<li>이 문제 같은 경우 집합을 사용하여 직접 연산을 할 수 있지만 각 집합마다 루트가 뭔지 모르는 경우가 있기에 유니온-파인드를 사용한다.</li>
</ul>
<p>1) 합집합은 유니온으로 대응하여 루프 노드를 통일
2) 같은 집합 여부는 파인드로 대응하여 루프 노드가 다르면 NO 같으면 YES를 함</p>
<h2 id="알고리즘-코드">알고리즘 코드</h2>
<ul>
<li>시간초과가 많이 발생한 문제로 이를 해결하기 2가지를 했다.</li>
</ul>
<p>1) sys.setrecursionlimit(1000000) 설정
2) parent[x] = find_parent(parent[x])로 find 함수 변경 </p>
<ul>
<li>2번 사항은 내가 잘못 알고 있던 것일 수도 있다. </li>
</ul>
<pre><code>import sys
sys.setrecursionlimit(1000000)
input = sys.stdin.readline

n, m = map(int, input().split())

parent = [i for i in range(n+1)]

def find_parent(x):
    if parent[x] != x:
        parent[x] = find_parent(parent[x])
    return parent[x]

def union(x, y):
    x = find_parent(x)
    y = find_parent(y)

    if x &lt; y:
        parent[y] = x
    else:
        parent[x] = y

for _ in range(m):
    calculate, x, y = map(int, input().split())

    if calculate == 0:
        union(x, y)
    else:
        root_x = find_parent(x)
        root_y = find_parent(y)

        if root_x == root_y:
            print(&quot;YES&quot;)
        else:
            print(&quot;NO&quot;)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[알고리즘 스터디 - 백준 20040번 : 사이클 게임]]></title>
            <link>https://velog.io/@guri_coding/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8A%A4%ED%84%B0%EB%94%94-%EB%B0%B1%EC%A4%80-20040%EB%B2%88-%EC%82%AC%EC%9D%B4%ED%81%B4-%EA%B2%8C%EC%9E%84</link>
            <guid>https://velog.io/@guri_coding/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8A%A4%ED%84%B0%EB%94%94-%EB%B0%B1%EC%A4%80-20040%EB%B2%88-%EC%82%AC%EC%9D%B4%ED%81%B4-%EA%B2%8C%EC%9E%84</guid>
            <pubDate>Sat, 26 Mar 2022 05:43:06 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/guri_coding/post/d5f80798-2703-471d-b223-702d8c3499e3/%E1%84%8B%E1%85%A1%E1%86%AF%E1%84%80%E1%85%A9%E1%84%85%E1%85%B5%E1%84%8C%E1%85%B3%E1%86%B7%20%E1%84%85%E1%85%A9%E1%84%80%E1%85%A9.jpg" alt=""></p>
<h2 id="문제-해석">문제 해석</h2>
<ul>
<li>사이클 게임은 두 명의 플레이어가 차례대로 돌아가며 진행하는 게임</li>
<li>선 플레이어/홀수, 후 플레이어/짝수</li>
</ul>
<p><strong>사이클 C : 플레이어가 그린 선분들의 부분집합</strong></p>
<ul>
<li>C에 속한 임의의 선분의 한 끝점에서 출발하여 모든 선분들 한 번씩만 지나서 출발점으로 되돌아올 수 있다. </li>
</ul>
<blockquote>
<p>점의 개수 n과 m 번째 차례까지의 게임 진행 상황에서 언제 처음으로 사이클이 완성된 것인지 출력</p>
</blockquote>
<ul>
<li>여기서 중요한 단어는 &#39;사이클&#39;이다. 사이클이라는 것은 트리 속에서 루프 노드가 서로 다른 것이 아니고 루프 노드가 서로 같을 때 사이클이 발생한 것이라고 말할 수 있다.</li>
<li>따라서 유니온 파인드 함수를 통하여 루프 노드가 같아지는 시점을 찾아 반환해주면 된다. </li>
</ul>
<h2 id="알고리즘-코드">알고리즘 코드</h2>
<pre><code>import sys

input = sys.stdin.readline

n, m = map(int, input().split())

# 부모노드 생성
parent = [i for i in range(n+1)]

def find(parent, x):
    if parent[x] != x:
        return find(parent, parent[x])
    return parent[x]

def union(parent, a, b):
    a = find(parent, a)
    b = find(parent, b)

    if a == b:
        return True
    else:
        if a &lt; b:
            parent[b] = a
        else:
            parent[a] = b
    return False

answer = 0

for i in range(m):
    a, b = map(int, input().split())
    # 최초 사이클 생성이기 때문에 answer가 갱신되기 전에 해야하고 
    # 또한, union을 했을 때에도 True가 나오는 지점을 알 수 있다.
    if union(parent, a, b) and answer == 0:
        answer = i + 1

print(answer)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[알고리즘 스터디 - 백준 2623번 : 음악프로그램]]></title>
            <link>https://velog.io/@guri_coding/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8A%A4%ED%84%B0%EB%94%94-%EB%B0%B1%EC%A4%80-2623%EB%B2%88-%EC%9D%8C%EC%95%85%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8</link>
            <guid>https://velog.io/@guri_coding/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8A%A4%ED%84%B0%EB%94%94-%EB%B0%B1%EC%A4%80-2623%EB%B2%88-%EC%9D%8C%EC%95%85%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8</guid>
            <pubDate>Sun, 27 Feb 2022 14:24:29 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/guri_coding/post/8ce4a337-e5da-436f-9373-d0d0c0cba478/%E1%84%8B%E1%85%A1%E1%86%AF%E1%84%80%E1%85%A9%E1%84%85%E1%85%B5%E1%84%8C%E1%85%B3%E1%86%B7%20%E1%84%85%E1%85%A9%E1%84%80%E1%85%A9.jpg" alt=""></p>
<h2 id="문제-해석">문제 해석</h2>
<blockquote>
<p>보조 PD들이 만든 순서들이 입력으로 주어질 때, 전체 가수의 순서를 정하는 프로그램</p>
</blockquote>
<ul>
<li>[1, 4, 3], [6, 2, 5, 4], [2, 3] 각각 보조 PD들이 정한 순서인데 이를 만족하는 경우는 [6, 2, 1, 5, 4, 3]이나 [1, 6, 2, 5, 4, 3]이 존재한다.</li>
<li>남일이가 보조 PD의 순서를 만족하지 못하면 0을 출력</li>
</ul>
<p><strong>입력</strong></p>
<ol>
<li>N, M : 가수의 수, 보조 PD의 수</li>
<li>보조 PD가 담당한 가수의 수가 나오고 보조 PD가 정한 순서들이 나옴</li>
</ol>
<h2 id="알고리즘-코드">알고리즘 코드</h2>
<pre><code>from collections import deque

N, M = map(int, input().split())

degree = [0] * (N+1)
graph = [[] for _ in range(N+1)]

for _ in range(M):
    singer = list(map(int, input().split()))
    for i in range(1, singer[0]):
        graph[singer[i]].append(singer[i+1])
        degree[singer[i+1]] += 1

def topology_sort():
    result = []
    q = deque()
    # 초기에 진입차수 0인 노드들 큐에 넣기
    for i in range(1, N+1):
        if degree[i] == 0:
            q.append(i)

    # 큐가 빌때 까지 반복
    while q:
        node = q.popleft()
        # 꺼낸 원소는 위상 정렬 결과값에 append
        result.append(node)
        # 꺼낸 노드랑 연결된 노드들 탐색
        for next in graph[node]:
            degree[next] -= 1
            # 새롭게 진입차수가 0이 된 노드들 큐에 넣기
            if degree[next] == 0:
                q.append(next)
    return result

is_cycle = topology_sort()

if len(is_cycle) == N:
    for i in is_cycle:
        print(i)
else:
    print(0)</code></pre><p>이번 문제는 순서를 정하려면 사이클이 없는 순서가 나와야 하고 어떤 특정 요소가 다른 요소보다 더 먼저 나와야 하는 <strong>위상 정렬</strong>알고리즘을 사용해야 한다. 그래서 입력 받은 요소를 graph에 들어갈 수 있게끔 바꿔주고 사용을 하게 되는데 <strong>이번 문제의 핵심은 위상 정렬이 되는지 안되는지 판별하는 것</strong>이다. </p>
<p>위상 정렬이 되지 않는 경우는 그래프의 사이클이 존재하는 경우인데 이를 확인하려고 찾아보니 2가지가 존재하고 있었다.</p>
<p>1) 진입차수에 0이 없는 경우
2) 위상 정렬을 한다음 노드의 개수가 일치하지 않는 경우이다.</p>
<p>이 때 1번을 사용했을 때는 틀렸는데 2번 요소로 했을 때 길이를 비교해보니 정답을 출력할 수 있었다. 이번 문제에서 내가 놓친 것은 이 문제가 어떤 유형인지 파악하는 것이고 잘한 것은 위상 정렬을 응용하여 풀었다는 것이다. 다음에는 더 발전해야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] Github Action + AWS CodeDeploy를 이용하여 프로젝트 자동 배포하기]]></title>
            <link>https://velog.io/@guri_coding/Spring-Github-Action-AWS-CodeDeploy%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%9E%90%EB%8F%99-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@guri_coding/Spring-Github-Action-AWS-CodeDeploy%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%9E%90%EB%8F%99-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 21 Feb 2022 10:22:37 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/guri_coding/post/c1c63de6-d978-4e41-afa0-b377663b0f68/Spring%20Boot%20logo.png" alt=""></p>
<p>이번에 Spring 백엔드 프로젝트를 하는데 단순히 MVC Pattern을 적용해서 API를 만드는 것 이상으로 무엇인가를 하고 싶었다. 그것은 바로 Github Action과 AWS를 이용해 자동 통합 및 배포할 수 있는 CI/CD를 구축하는 것이다. Docker까지 같이 적용하고자 했으나 안써본 것들로 한번 해보면서 또 배움의 기회를 얻으려 하였다. </p>
<h3 id="전체적인-순서">전체적인 순서</h3>
<ol>
<li>Github으로 푸시를 하면 Github Action이 자동으로 실행된다.</li>
<li>그 과정에 jar, Shell Script 파일을 압축해서 S3로 업로드를 한다.</li>
<li>EC2에 설치되어 있는 CodeDeploy가 S3에 업로드된 파일을 EC2로 가져와 배포를 진행한다.</li>
<li>작성한 Shell Script 파일을 통해 jar를 실행시켜 자동화 배포가 진행되는 것이다.</li>
</ol>
<h2 id="1-github-actions-설정하기">1. Github Actions 설정하기</h2>
<p><img src="https://images.velog.io/images/guri_coding/post/81eaa48f-851b-4144-bdd4-f396f52d38eb/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-21%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%203.06.51.png" alt=""></p>
<ul>
<li>내 Repository-Action을 들어가면 내 프로젝트에 맞는 방식을 추천한다. 나는 java with Gradle로 진행하였다. 그러면 그 결과 아래와 같이 나오게 된다.</li>
</ul>
<p><img src="https://images.velog.io/images/guri_coding/post/c6309e41-6d6e-4228-a9d0-76a19cc5e129/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-21%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%203.13.51.png" alt=""></p>
<ul>
<li>여기서 중점적으로 나오는 부분은 name, run, shell로 실제 우리가 푸시를 할 때 실행되는 순서와 요소들이다.</li>
</ul>
<pre><code>name: cafein

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2

    - name: Set up JDK 11
      uses: actions/setup-java@v2
      with:
        java-version: &#39;11&#39;
        distribution: &#39;temurin&#39;

    - name: Grant execute permission for gradlew
      run: chmod +x gradlew

    - name: Build with Gradle
      run: ./gradlew clean build</code></pre><p><img src="https://images.velog.io/images/guri_coding/post/b7aed61c-c456-441f-a448-f7f52fe1a86f/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-21%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%203.16.02.png" alt=""></p>
<ul>
<li>그 결과 .github/workflows가 나타났음을 알 수 있다. 그리고 Actions에 다시 들어가면 잘 실행됨을 알 수 있다.</li>
</ul>
<p><img src="https://images.velog.io/images/guri_coding/post/65b8c664-d085-4855-8e9d-c6325cff219c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-21%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%203.54.40.png" alt=""></p>
<h2 id="2-aws-s3에-빌드한-jar-업로드-하기">2. AWS S3에 빌드한 jar 업로드 하기</h2>
<ul>
<li>먼저 Deploy를 위한 IAM 사용자를 등록해줘야 한다.</li>
</ul>
<p><img src="https://images.velog.io/images/guri_coding/post/ffd5c5b5-d71b-4be5-a4fd-979048907922/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-21%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%203.57.20.png" alt=""></p>
<ul>
<li>여기서 중요한 것은 S3와 CodeDeploy로 이 2개만 있어도 된다.</li>
</ul>
<p><img src="https://images.velog.io/images/guri_coding/post/f33a3813-3dcf-44c5-a463-aaab093a400c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-21%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%207.17.47.png" alt=""></p>
<ul>
<li>위처럼 권한 설정을 하고나면 ACCESS_KEY와 SECRET_ACCESS_KEY가 생기는데 이를 Github Settings에다가 등록해줘야 한다.</li>
</ul>
<p><img src="https://images.velog.io/images/guri_coding/post/0c3c4f99-a974-496d-b8c0-3bec69ef7e41/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-21%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.01.57.png" alt=""></p>
<ul>
<li>위 요소에서 New Repository secret을 클릭해 아래의 3개를 적용한다.</li>
</ul>
<p><img src="https://images.velog.io/images/guri_coding/post/4d1053cf-2722-49d3-96ba-5adabac9c450/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-21%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.03.39.png" alt=""></p>
<ul>
<li>그런 다음에는 S3를 생성하고 환경 설정 부분에 새로 추가해줘야 한다. 나는 cafein-deploy라고 버킷을 만들었다.</li>
</ul>
<p><img src="https://images.velog.io/images/guri_coding/post/8718510c-f247-4a32-8f12-c4415dae0063/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-21%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.11.29.png" alt=""></p>
<ul>
<li>새롭게 gradle.yml을 수정해주고 push를 해준다.</li>
</ul>
<pre><code>name: cafein

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

env:
  S3_BUCKET_NAME: cafein-deploy

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2

    - name: Set up JDK 11
      uses: actions/setup-java@v2
      with:
        java-version: &#39;11&#39;
        distribution: &#39;temurin&#39;

    - name: Grant execute permission for gradlew
      run: chmod +x gradlew

    - name: Build with Gradle
      run: ./gradlew clean build

    - name: Make zip file
      run: zip -r ./$GITHUB_SHA.zip .
      shell: bash

    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: ${{ secrets.AWS_REGION }}

    - name: Upload to S3
      run: aws s3 cp --region ap-northeast-2 ./$GITHUB_SHA.zip s3://$S3_BUCKET_NAME/$GITHUB_SHA.zip</code></pre><ul>
<li>그러면 새로운 Github Actions가 생성된다.</li>
</ul>
<p><img src="https://images.velog.io/images/guri_coding/post/42943a9e-bd6e-4f72-87bd-115465b32730/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-21%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.15.10.png" alt=""></p>
<ul>
<li>그리고 S3에도 zip파일이 잘 업로드 된 것을 알 수 있다.</li>
</ul>
<p><img src="https://images.velog.io/images/guri_coding/post/f28109d1-137c-4515-be05-332f0ca9ea30/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-21%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.15.17.png" alt=""></p>
<h2 id="3-codedeploy-설치-및-설정하기">3. CodeDeploy 설치 및 설정하기</h2>
<ul>
<li><p>앞서 본 것은 CI 과정으로 서버로 배포하는 것은 아니다. 이제 서버로 배포를 하는 CD과정을 거치려면 CodeDeploy를 사용해야 한다.</p>
</li>
<li><p>ec2 역할을 설정해줘야 하는데 실행하고자 하는 ec2 - 작업 - 보안 - IAM 역할 수정 - 새로운 역할 만들기에서 아래와 같은 역할을 생성하여 부여해야 한다. 그리고 재부팅 한다.</p>
</li>
</ul>
<p><img src="https://images.velog.io/images/guri_coding/post/79beb347-39d6-405c-b5d3-ff7983a93ae6/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-21%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.53.22.png" alt=""></p>
<ul>
<li>역할 생성을 할 때 EC2를 연동한다.</li>
</ul>
<p><img src="https://images.velog.io/images/guri_coding/post/8cfdc793-3635-4779-bcc6-8867261c6aa7/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-21%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.54.57.png" alt=""></p>
<ul>
<li>S3와 CodeDeploy 사용</li>
</ul>
<p><img src="https://images.velog.io/images/guri_coding/post/21260837-f693-4888-bdef-3c18efbedb97/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-21%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.55.58.png" alt=""></p>
<pre><code>chmod 400 key이름.pem
ssh -i &quot;key이름.pem&quot; ubuntu@ip주소</code></pre><ul>
<li>먼저 여기로 ubuntu 서버에 접속한 다음 CodeDeploy에 필요한 파일을 설치한다. 나는 AWS Ubuntu 서버로 실행하였다. EC2의 ubuntu 버전이 16.04 이상이므로 아래와 같이 진행했다.</li>
</ul>
<pre><code>sudo apt update
sudo apt install ruby-full
sudo apt install wget

cd /home/ubuntu

sudo apt install awscli
aws s3 cp s3://aws-codedeploy-ap-northeast-2/latest/install . --region ap-northeast-2

chmod +x ./install
sudo ./install auto

# 이걸로 설치 확인을 할 수 있다.
sudo service codedeploy-agent status</code></pre><ul>
<li>이제 CodeDeploy를 설정해줘야 한다. 여기서 Name-EC2 이름이고 중요한 것은 로드밸런싱을 해제해야 한다는 것이다.</li>
</ul>
<p><img src="https://images.velog.io/images/guri_coding/post/c027f01e-89c3-4772-a9e2-147ff274b33a/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-21%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.01.15.png" alt=""></p>
<p><img src="https://images.velog.io/images/guri_coding/post/9ad1aa0b-26d4-4359-9ed7-206423a0c632/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-21%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.06.50.png" alt=""></p>
<p><img src="https://images.velog.io/images/guri_coding/post/224cb7b3-473d-4ac4-8b97-cd67e8f4641f/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-21%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.21.33.png" alt=""></p>
<p><img src="https://images.velog.io/images/guri_coding/post/61cce6e0-30cf-46b7-a27d-a125aa45b37a/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-21%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.21.40.png" alt=""></p>
<h2 id="4-스크립트-추가하기">4. 스크립트 추가하기</h2>
<h4 id="1-appspecyml-설정">1) appspec.yml 설정</h4>
<pre><code>version: 0.0
os: linux
files:
  - source:  /
    destination: /home/ubuntu/action
    overwrite: yes

permissions:
  - object: /
    pattern: &quot;**&quot;
    owner: ubuntu
    group: ubuntu

hooks:
  ApplicationStart:
    - location: scripts/deploy.sh
      timeout: 60
      runas: ubuntu</code></pre><h4 id="2-github-actions-yml-추가">2) Github Actions yml 추가</h4>
<pre><code>- name: Code Deploy
  run: aws deploy create-deployment --application-name [만든 어플리케이션 이름] --deployment-config-name CodeDeployDefault.AllAtOnce --deployment-group-name [생성한 배포 그룹 이름] --s3-location bucket=$S3_BUCKET_NAME,bundleType=zip,key=$GITHUB_SHA.zip</code></pre><blockquote>
<p>주의할 점 : Amazon Linux가 ubuntu인지 아닌지에 따라 appspec.yml 경로와 owner, group, runas가 ubuntu나 ec2-user로 구분이 되기에 사용하는 ec2 환경에서 <a href="https://docs.aws.amazon.com/ko_kr/AWSEC2/latest/UserGuide/managing-users.html">사용자 계정 관리</a>를 참고해야 한다.</p>
</blockquote>
<h2 id="5-최종-확인">5. 최종 확인</h2>
<p><img src="https://images.velog.io/images/guri_coding/post/5014898d-01f5-4a34-b318-a212862ed07f/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-21%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%206.13.57.png" alt=""></p>
<ul>
<li>일단 Github Actions에서도 내가 주문한 것들이 잘 실행됨을 알 수 있다.</li>
</ul>
<p><img src="https://images.velog.io/images/guri_coding/post/28a87073-1f65-4bd3-ba66-6c6240e2786e/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-21%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%206.09.38.png" alt=""></p>
<p><img src="https://images.velog.io/images/guri_coding/post/72483e3e-67a8-4742-8d17-3bbeb2894d0b/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-21%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%206.11.01.png" alt=""></p>
<ul>
<li>그리고 CodeDeploy 상에서도 잘 배포가 됨을 알 수 있다.</li>
</ul>
<p><img src="https://images.velog.io/images/guri_coding/post/59334148-db14-49cb-ad11-e507636940e6/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-21%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%207.15.44.png" alt=""></p>
<blockquote>
<p>Spring 기반 Github Action 및 AWS를 연동하는 과정에서 자동배포를 하면서 Shell Script도 써보고 많이 배우게 되었다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[알고리즘 스터디 - 백준 1987번 : 알파벳]]></title>
            <link>https://velog.io/@guri_coding/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8A%A4%ED%84%B0%EB%94%94-%EB%B0%B1%EC%A4%80-1987%EB%B2%88-%EC%95%8C%ED%8C%8C%EB%B2%B3</link>
            <guid>https://velog.io/@guri_coding/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8A%A4%ED%84%B0%EB%94%94-%EB%B0%B1%EC%A4%80-1987%EB%B2%88-%EC%95%8C%ED%8C%8C%EB%B2%B3</guid>
            <pubDate>Tue, 15 Feb 2022 06:09:01 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/guri_coding/post/74743da8-d779-469c-928c-c9aefd71b775/%E1%84%8B%E1%85%A1%E1%86%AF%E1%84%80%E1%85%A9%E1%84%85%E1%85%B5%E1%84%8C%E1%85%B3%E1%86%B7%20%E1%84%85%E1%85%A9%E1%84%80%E1%85%A9.jpg" alt=""></p>
<h2 id="문제-해석">문제 해석</h2>
<blockquote>
<p>R x C로 되어 있는 보드판 위에서 말이 지나갈 수 있는 최대의 칸 수를 구하기</p>
</blockquote>
<ul>
<li>각 칸마다 알파벳이 존재하는데 지금까지 지나온 알파벳과 다른 알파벳만 지나갈 수 있다</li>
<li>R, C 둘다 1이상 20이하의 값으로 구성되어 있다</li>
</ul>
<h3 id="어떤-알고리즘을-써야할까">어떤 알고리즘을 써야할까?</h3>
<ul>
<li>일단, 상하좌우로 움직이는 방식에는 좌표를 이용한 BFS, DFS가 존재하고 있다.</li>
<li>기존 visited 배열이 True, False를 쓰는 것처럼 알파벳으로 방문 안했던 곳으로 새로 가는 방식으로 가면 된다. </li>
</ul>
<h2 id="알고리즘-코드">알고리즘 코드</h2>
<pre><code>from collections import deque

R, C = map(int, input().split())
board = [list(input().strip()) for _ in range(R)]

dx = [0, 0, -1, 1]
dy = [-1, 1, 0, 0]

answer = 1

def bfs(x, y):
    global answer
    q = deque()
    q.append((x, y, board[x][y]))

    while q:
        x_, y_, alpha_ = q.pop()

        for i in range(4):
            new_x = x_ + dx[i]
            new_y = y_ + dy[i]

            if ((0 &lt;= new_x &lt; R) and (0 &lt;= new_y &lt; C)):
                if board[new_x][new_y] not in alpha_:
                    q.append((new_x, new_y, alpha_ + board[new_x][new_y]))
                    answer = max(answer, len(alpha_) + 1)

bfs(0, 0)
print(answer)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[알고리즘 스터디 - 백준 1202번 : 보석 도둑]]></title>
            <link>https://velog.io/@guri_coding/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8A%A4%ED%84%B0%EB%94%94-%EB%B0%B1%EC%A4%80-1202%EB%B2%88-%EB%B3%B4%EC%84%9D-%EB%8F%84%EB%91%91</link>
            <guid>https://velog.io/@guri_coding/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8A%A4%ED%84%B0%EB%94%94-%EB%B0%B1%EC%A4%80-1202%EB%B2%88-%EB%B3%B4%EC%84%9D-%EB%8F%84%EB%91%91</guid>
            <pubDate>Tue, 08 Feb 2022 07:52:01 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/guri_coding/post/ba05f8ed-46f5-4514-bad1-ef84fa124daa/%E1%84%8B%E1%85%A1%E1%86%AF%E1%84%80%E1%85%A9%E1%84%85%E1%85%B5%E1%84%8C%E1%85%B3%E1%86%B7%20%E1%84%85%E1%85%A9%E1%84%80%E1%85%A9.jpg" alt=""></p>
<h2 id="문제-해석">문제 해석</h2>
<blockquote>
<p>상덕이가 훔칠 수 있는 보석의 최대 가격을 구하라</p>
</blockquote>
<p><strong>입력</strong></p>
<ol>
<li>N, K : 보석 개수, 가방 개수</li>
<li>M, V : 보석의 무게, 보석의 가격</li>
<li>C : 각 가방의 담을 수 있는 최대 무게(근데 각 가방에는 한 개의 보석만 넣어야 함)</li>
</ol>
<h3 id="어떤-알고리즘을-써야할까">어떤 알고리즘을 써야할까?</h3>
<p>일단 당장 생각나는 알고리즘은 동적계획법의 배낭문제를 적용하는 것이다. 근데 기존 배낭 문제와의 차이점은 기존 문제는 하나의 가방에 가격을 최대로 하도록 보석을 여러개 집어 넣는 거지만 이번 문제는 각 가방에 하나씩 넣는 거라 쉬울 수 있으면서 어려울 수 있다.</p>
<p>만약 보석의 가치를 최대로 하려면 보석의 가격을 기준으로 리스트를 정렬하고 가방의 무게에도 우선순위를 넣어 거기에 맞춰서 넣으면 될 것 같다. 그리디 알고리즘도 한번 사용해보자. </p>
<h2 id="알고리즘-코드">알고리즘 코드</h2>
<pre><code>import heapq

N, K = map(int, input().split())

jewelry = []
for _ in range(N):
    M, V = map(int, input().split())
    heapq.heappush(jewelry, [M, V])

bag = []
for _ in range(K):
    C = int(input())
    heapq.heappush(bag, C)

answer = 0
enable_jewelry = []

for _ in range(K):
    c = heapq.heappop(bag)

    while jewelry and c &gt;= jewelry[0][0]:
        [w, v] = heapq.heappop(jewelry)
        heapq.heappush(enable_jewelry, -v)

    if enable_jewelry:
        answer -= heapq.heappop(enable_jewelry)
    elif not jewelry:
        break

print(answer)</code></pre>]]></description>
        </item>
    </channel>
</rss>