<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>ryu-backend.log</title>
        <link>https://velog.io/</link>
        <description>공부한 내용을 정리한 블로그입니다 &amp; 백엔드 개발자</description>
        <lastBuildDate>Wed, 29 May 2024 11:25:38 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. ryu-backend.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/ryu-backend" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Java] - 메모리구조]]></title>
            <link>https://velog.io/@ryu-backend/Java-%EB%A9%94%EB%AA%A8%EB%A6%AC%EA%B5%AC%EC%A1%B0</link>
            <guid>https://velog.io/@ryu-backend/Java-%EB%A9%94%EB%AA%A8%EB%A6%AC%EA%B5%AC%EC%A1%B0</guid>
            <pubDate>Wed, 29 May 2024 11:25:38 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>자바 프로그램이 실행되면 JVM은 OS로부터 메모리를 할당받고, 그 메모리를 용도에 따라서 여러 영역으로 나누어 관리를 한다.
JVM의 메모리 공간은 크게 Method(Static), Stack, Heap 영역으로 구분되고 데이터 타입에 따라 각 영역에 나눠서 할당 되게 된다.</p>
</blockquote>
<br>
<br>

<h2 id="자바-변수의-종류">자바 변수의 종류</h2>
<p>메모리 영역을 알기 전에 자바에서 선언 위치에 따른 변수의 종류를 알아보자.
변수는 크게 네 종류가 있다. 클래스 변수, 인스턴스 변수, 지역변수, 매개변수이다.</p>
<pre><code class="language-java">public class prc {


    private int state = 0;      //인스턴스 변수

    public static int gage = 100;   // 클래스변수

    public static void main(String[] args) {       //매개변수

        int a = 10;  // 지역변수

    }
}</code></pre>
<p>(1) 클래스 변수(static 변수)</p>
<ul>
<li>선언위치 : 클래스 영역</li>
<li>객체를 공유하는 변수로 여러 객체에서 공통으로 사용하고 싶을 때 정의</li>
</ul>
<p>(2) 인스턴스 변수</p>
<ul>
<li>선언위치 : 클래스 영역</li>
<li>클래스 영역에서 static이 아닌 변수</li>
<li>개별적인 저장 공간으로 객체 / 인스턴스 마다 다른 값 저장 가능</li>
<li>객체/인스턴스 생성만 하고 참조 변수가 없는 경우 가비지 컬렉터에 의해 자동 제거됨</li>
</ul>
<p>(3) 지역 변수</p>
<ul>
<li>선언위치 : 메서드 영역</li>
<li>메서드 내에서 선언되고 메서드 수행이 끝나면 소멸되는 변수초깃값을 지정한 후 사용할 수 있음</li>
</ul>
<p>(4) 매개 변수</p>
<ul>
<li>선언위치 : 메서드 영역</li>
<li>메서드 호출 시 &#39;전달하는 값&#39;을 가지고 있는 인수(지역 변수처럼 선언된 곳부터 수행이 끝날 때까지 유효함)</li>
</ul>
<br>
<br>

<h3 id="각-변수의-생성시기">각 변수의 생성시기</h3>
<ul>
<li>클래스변수 : 클래스가 메모리에 올라갈 때</li>
<li>인스턴스 변수 : 인스턴스가 생성되었을 때</li>
<li>지역변수/매개변수 : 위치하고 있는 메서드가 수행되었을 때</li>
</ul>
<br>
<br>

<h2 id="methodstatic-영역">Method(Static) 영역</h2>
<ul>
<li>JVM이 동작해서 클래스가 로딩될때 생성</li>
<li>JVM이 읽어들인 클래스와 인터페이스에 대한 런타임 상수 풀, 멤버 변수(필드), 클래스 변수(Static 변수), 상수(final), 생성자와 메소드 등을 저장하는 공간</li>
<li>Method 영역의 데이터는 프로그램의 시작부터 종료가 될 때까지 메모리에 남아있다. 그러나 static 데이터를 무분별하게 많이 사용할 경우 메모리 부족 현상이 일어날 수 있게 된다.</li>
<li>static 메소드, static이 아닌 메소드 모두 메소드 영역에 저장된다.</li>
</ul>
<br>

<h2 id="stack-영역">Stack 영역</h2>
<ul>
<li>메소드 내에서 정의하는 기본 자료형에 해당되는 지역변수의 데이터 값이 저장되는 공간</li>
<li>메소드가 호출될 때 스택 영역에 스택 프레임이 생기고 그 안에 메소드를 호출</li>
<li>원시 타입의 데이터에 해당되는 지역변수, 매개변수 데이터 값이 저장된다.</li>
<li>메소드가 호출 될때 메모리에 할당되고 종료되면 사라짐</li>
<li>후입선출의 특성을 가지며, 스코프의 범위를 벗어나면 스택 메모리에서 사라진다.<ul>
<li>스택 프레임이란?
하나의 메서드에 필요한 메모리 덩어리를 묶어서 스택 프레임(Stack Frame)이라고 한다.
하나의 메서드당 하나의 스택 프레임이 필요하며, 메서드를 호출하기 직전 스택프레임을 자바 Stack에 생성한 후 메서드를 호출하게 된다.
스택 프레임에 쌓이는 데이터는 메서드의 매개변수, 지역변수, 리턴값 등이 있다.만일 메서드 호출 범위가 종료되면 스택에서 제거된다.</li>
</ul>
</li>
</ul>
<br>

<h2 id="heap-영역">Heap 영역</h2>
<ul>
<li>JVM이 관리하는 프로그램 상에서 데이터를 저장하기 위해 런타임 시 동적으로 할당하여 사용하는 영역</li>
<li>참조형 데이터 타입을 갖는 객체(인스턴스), 인스턴스 변수, 배열 등이 저장되는 공간</li>
<li>Heap 영역은 Stack 영역과 다르게 보관되는 메모리가 호출이 끝나더라도 삭제되지 않고 유지된다.<ul>
<li>그러나 어떤 참조 변수도 Heap 영역에 있는 인스턴스를 참조하지 않게 된다면, 가비지 컬렉터에 의해 메모리에서 청소된다.</li>
</ul>
</li>
<li>Stack은 스레드 갯수마다 각각 생성되지만, Heap은 몇개의 스레드가 존재하든 상관없이 단 하나의 Heap 영역만 존재</li>
</ul>
<br>
<br>

<h3 id="마무리">마무리</h3>
<ul>
<li><p>마지막 코드가 실행되면 main 스택 프레임은 스택 영역에서 제거된다.</p>
<ul>
<li>스택 영역은 메서드의 끝을 알리는 닫는 중괄호를 만나면 자동으로 메모리에서 제거된다.</li>
<li>그러나 힙 영역에는 여전히 객체 데이터가 메모리에 상주되게 된다.</li>
<li>가비지 컬렉터는 힙 영역에 참조되지 않고 남아버린 고아 객체들을 식별해 힙 영역을 청소해준다.</li>
<li>추가로 코드 실행이 모두 끝나면 Method(Static) 영역도 비워지게 된다.</li>
</ul>
</li>
<li><p>힙 메모리는 애플리케이션의 모든 부분에서 사용되며, 반면에 스택 메모리는 하나의 스레드가 실행될 때 사용. 그래서 힙과 메소드 공간에 저장된 객체는 어디서든지 접근이 가능하지만, 스택 메모리는 다른 스레드가 접근할 수 없다.</p>
</li>
<li><p>언제든지 객체가 생성되면 항상 힙 공간에 저장되며, 스택 메모리는 힙 공간에 있는 객체를 참조만 한다. 즉, 스택 메모리는 원시 타입의 지역변수와 힙 공간에 있는 객체 참조 변수만 갖고 있다.</p>
</li>
<li><p>스택메모리의 생명주기는 매우 짧으며, 힙 메모리는 애플리케이션의 시작부터 끝까지 살아남는다.</p>
</li>
<li><p>자바 스택 메모리 사이즈는 힙 메모리와 비교했을 때 매우 적다. 하지만 스택 메모리는 간단한 메모리 할당 방법(후입선출)을 사용하므로 힙 메모리보다 빠르다.</p>
</li>
</ul>
<br>
<br>

<p>참고 : <a href="https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EA%B7%B8%EB%A6%BC%EC%9C%BC%EB%A1%9C-%EB%B3%B4%EB%8A%94-%EC%9E%90%EB%B0%94-%EC%BD%94%EB%93%9C%EC%9D%98-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%98%81%EC%97%AD%EC%8A%A4%ED%83%9D-%ED%9E%99">https://inpa.tistory.com/entry/JAVA-☕-그림으로-보는-자바-코드의-메모리-영역스택-힙</a> [Inpa Dev 👨‍💻:티스토리]</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[알고리즘-퀵정렬(Quick Sort)]]></title>
            <link>https://velog.io/@ryu-backend/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%ED%80%B5%EC%A0%95%EB%A0%ACQuick-Sort</link>
            <guid>https://velog.io/@ryu-backend/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%ED%80%B5%EC%A0%95%EB%A0%ACQuick-Sort</guid>
            <pubDate>Fri, 24 May 2024 09:38:41 GMT</pubDate>
            <description><![CDATA[<h2 id="퀵-정렬quick-sort">퀵 정렬(Quick Sort)</h2>
<blockquote>
<p>퀵 정렬은 분할 정복 방법을 통해 주어진 배열을 정렬한다. 즉, 문제를 작은 2개의 문제로 분리하고 각각을 해결한 다음, 결과를 모아서 원래의 문제를 해결하는 전략이다.
불안정 정렬에 속하며, 다른 원소와의 비교만으로 정렬을 수행하는 비교 정렬에 속한다. </p>
</blockquote>
<br>
<br>

<h3 id="process">Process</h3>
<ol>
<li>배열 가운데서 하나의 원소를 고른다. 이렇게 고른 원소를 pivot이라고한다.</li>
<li>pivot 앞에는 pivot보다 값이 작은 모든 원소들이 오고, pivot 뒤에는 pivot보다 값이 큰 모든 원소들이 오도록 pivot을 기준으로 배열을 둘로 나눈다. 이렇게 배열을 둘로 나누는 것을 분할이라고 한다. 분할을 마친 뒤에 pivot은 더 이상 움직이지 않는다.</li>
<li>분할된 두 개의 작은 배열에 대해 재귀적으로 이 과정을 반복한다.</li>
</ol>
<ul>
<li>재귀 호출이 한번 진행될 때마다 최소한 하나의 원소는 최종적으로 위치가 정해지므로, 이 알고리즘은 반드시 끝난다는 것을 보장한다.</li>
</ul>
<br>
<br>

<h3 id="codejava---가장-우측-인덱스를-pivot으로-설정">Code(Java) - 가장 우측 인덱스를 pivot으로 설정</h3>
<pre><code class="language-java">public class main {


    public static void main(String[] args) {
        int a[]={4,6,1,2,10,13,55};

    quickSort(a,0,6);
    for(int i =0;i&lt; a.length;i++)
        System.out.println(a[i]);

    }

    public static void quickSort(int a[],int p, int r){
        if(p&lt;r){
            int q=partition(a,p,r);       //분할 ,q = pivot의 위치
            quickSort(a,p,q-1);       //왼쪽 부분배열 정렬
            quickSort(a,q+1,r);       // 오른쪽 부분배열 정렬
        }
    }
    public static int partition(int a[],int p, int r){
        /*
        배열 a[p...r]의 원소들을 a[r]을 기준으로 양쪽으로 재배치하고 a[r]이 자리한 위치를 return 한다.
         */
        int x =a[r];
        int i = p-1;
        int j =p;
        while(j&lt;r){
            if(a[j]&lt;x){
                i=i+1;
                swap(a,i,j);
            }
            j++;
        }
        swap(a,i+1,j);
        return i+1;
    }

    private static void swap(int[] a, int i, int j) {
        int temp=0;
        temp=a[i];
        a[i]=a[j];
        a[j]=temp;
    }


}</code></pre>
<br>
<br>
<br>

<h3 id="시간복잡도">시간복잡도</h3>
<ol>
<li><p>최선의 경우 (Best Case):</p>
<p>피벗이 항상 배열을 반으로 정확히 나누는 경우입니다.
이 경우, 매 단계에서 배열을 절반으로 나누므로, 배열의 크기 N이 계속 절반으로 줄어듭니다.
시간복잡도: O(N log N)</p>
</li>
<li><p>평균 경우 (Average Case):</p>
<p>대부분의 경우 피벗이 배열을 적절하게 나누는 경우입니다.
배열이 비교적 균등하게 나누어진다면, 재귀 호출의 깊이는 log N이 되고, 각 단계마다 배열의 모든 요소를 한 번씩 비교하게 됩니다.
시간복잡도: O(N log N)</p>
</li>
<li><p>최악의 경우 (Worst Case):</p>
<p>피벗이 항상 배열의 가장 큰 값이나 가장 작은 값이 되는 경우입니다.
이 경우 배열은 한 쪽으로만 계속 분할되며, 재귀 호출의 깊이는 배열의 크기 N과 같습니다.
시간복잡도: O(N^2)</p>
<br>


</li>
</ol>
<p>&lt;정리&gt;
최선의 경우: O(N log N)
평균 경우: O(N log N)
최악의 경우: O(N^2)</p>
<p>퀵정렬의 최악의 경우를 피하기 위해서는, 피벗을 잘 선택하는 전략이 중요합니다. 흔히 사용하는 피벗 선택 방법으로는 랜덤 피벗 선택, 중앙값 피벗 선택 등이 있습니다. 이러한 방법들은 평균적인 성능을 최적화하여 최악의 경우 발생 확률을 줄여줍니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[디자인패턴(2) - 템플릿 메소드 패턴]]></title>
            <link>https://velog.io/@ryu-backend/%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B42-%ED%85%9C%ED%94%8C%EB%A6%BF-%EB%A9%94%EC%86%8C%EB%93%9C-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@ryu-backend/%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B42-%ED%85%9C%ED%94%8C%EB%A6%BF-%EB%A9%94%EC%86%8C%EB%93%9C-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Tue, 14 May 2024 06:48:37 GMT</pubDate>
            <description><![CDATA[<h3 id="템플릿-메소드-패턴">템플릿 메소드 패턴</h3>
<blockquote>
<p>GoF의 디자인 패턴에 의하면, 템플릿 메소드 패턴이란 알고리즘의 구조를 메소드에 정의하고, 하위 클래스에서 알고리즘 구조의 변경없이 알고리즘을 재정의 하는 패턴이다. 알고리즘이 단계별로 나누어 지거나, 같은 역할을 하는 메소드 이지만 여러곳에서 다른 형태로 사용이 필요한 경우 유용한 패턴이다.</p>
</blockquote>
<blockquote>
<p>토비의 스프링에 의하면, 템플릿 메소드 패턴이란 상속을 통해 슈퍼클래스의 기능을 확장할 때 사용하는 가장 대표적인 방법. 변하지 않는 기능은 슈퍼클래스에 만들어 두고 자주 변경되며 확장할 기능은 서브클래스에서 만들도록 한다.</p>
</blockquote>
<br>
⭐ 둘 다 하위 클래스에서 사용되지만, 변하지 않는 기능을 상위 클래스에 저장해 놓고, 확장할 기능은 서브클래스에서 만들도록 설계한다는 내용을 담고 있다.

<br>
<br>
<br>
<br>

<h3 id="템플릿-메소드-패턴의-예제">템플릿 메소드 패턴의 예제</h3>
<p>만약 두 개의 라면을 끓이는 과정이 아래와 같다고 가정하자.</p>
<p>&lt;신라면&gt; </p>
<ol>
<li>물을 끓인다.</li>
<li>면을 넣는다.</li>
<li>계란을 넣는다.</li>
<li>4분 기다린다.<br>

</li>
</ol>
<p>&lt;너구리&gt;</p>
<ol>
<li>물을 끓인다.</li>
<li>면을 넣는다.</li>
<li>콩나물을 넣는다.</li>
<li>5분 기다린다.</li>
</ol>
<br>
<br>

<h3 id="java-코드-구현">Java 코드 구현</h3>
<pre><code class="language-java">public class ShinRamen {                 // 신라면 조리 방법
    public void boilWater() {
        System.out.println(&quot;물을 끓인다.&quot;);
    }

    public void putNoodles() {
        System.out.println(&quot;면을 넣는다.&quot;);
    }

    public void putEgg() {
        System.out.println(&quot;계란을 넣는다.&quot;);
    }

    public void waitForMinutes() {
        System.out.println(&quot;4분 기다린다.&quot;);
    }
}</code></pre>
<pre><code class="language-java">public class RaccoonRamen {              // 너구리 조리 방법
    public void boilWater() {
        System.out.println(&quot;물을 끓인다.&quot;);
    }

    public void putNoodles() {
        System.out.println(&quot;면을 넣는다.&quot;);
    }

    public void putBeanSprouts() {
        System.out.println(&quot;콩나물을 넣는다.&quot;);
    }

    public void waitForMinutes() {
        System.out.println(&quot;5분 기다린다.&quot;);
    }
}</code></pre>
<p>위 두개의 코드에서, 물을 끓이는 것과 면을 넣는 행위는 완전히 동일한 코드이다. 따라서 Ramen이라는 클래스를 정의하여 중복된 코드를 제거할 수 있다. 그리고 추가로 &#39;무언가&#39;를 넣는 행위와 &#39;x분&#39; 기다린다는 행위가 유사하다. 템플릿 메소드 패턴은 이것도 추상화하는것이 목표이다.</p>
<br>
<br>

<pre><code class="language-java">public abstract class Ramen {            //추상클래스 선언

    public final void makeRamen() {            // 템플릿 메소드
        boilWater();
        putNoodles();
        putExtra();
        waitForMinutes();
    }

    private void boilWater() {
        System.out.println(&quot;물을 끓인다.&quot;);
    }

    private void putNoodles() {
        System.out.println(&quot;면을 넣는다.&quot;);
    }

    protected abstract void putExtra();       // 추상메소드

    protected abstract waitForMinutes()       // 추상메소드

    public void eat() {                       // 훅 메소드
        System.out.println(&quot;냄비에 담습니다.&quot;);
    }

    }</code></pre>
<ul>
<li>템플릿 메소드 : 알고리즘의 구조를 정의하고 구체적인 단계를 호출하며 각 단계의 실행 순서를 결정<ul>
<li>보통 final로 선언하여 하위 클래스에서 재정의할 수 없도록 한다. 이는 알고리즘의 구조를 변경하지 않고 구체적인 단계만 오버라이딩 할 수 있도록 한다.</li>
</ul>
</li>
</ul>
<br>

<ul>
<li>훅 메소드 : 템플릿 메소드에서 호출되는 메소드로, 기본 구현이 제공되어도 되고 필요에 따라 하위 클래스에서 재정의 될 수 있다.<ul>
<li>알고리즘의 일부가 아니며 선택적으로 사용한다. 하위 클래스에서 필요에 따라 오버라이딩 하여 기능을 확장하여 변경할 수 있다.</li>
</ul>
</li>
</ul>
<br>

<ul>
<li>추상 메소드 : 상위 클래스에서 선언만 되고 구현은 반드시 하위 클래스에서 구현되어야 한다.</li>
</ul>
<br>
<br>

<h3 id="코드-수정">코드 수정</h3>
<pre><code class="language-java">public class ShinRamen extends Ramen {

    @Override
    public void putExtra() {
        System.out.println(&quot;계란을 넣는다.&quot;);
    }

    @Override
    public void waitForMinutes() {
        System.out.println(&quot;4분 기다린다.&quot;);
    }
}</code></pre>
<pre><code class="language-java">public class RaccoonRamen extends Ramen {

    @Override
    public void putExtra() {
        System.out.println(&quot;콩나물을 넣는다.&quot;);
    }

    @Override
    public void waitForMinutes() {
        System.out.println(&quot;5분 기다린다.&quot;);
    }
}</code></pre>
<br>
<br>
<br>

<h3 id="정리">정리</h3>
<ul>
<li>재사용성이 크게 증가한다.</li>
<li>공통된 코드들은 템플릿 메소드에 넣어둔다.</li>
<li>반드시 구현해야 되는 추상메서드와 선택적 오버라이딩이 가능한 훅 메소드를 따로 둔다.</li>
<li>템플릿 메소드가 추상메소드, 훅 메소드를 호출하는 구조로 되어있어 전체 알고리즘 구조를 보호하면서도 부분적인 수정이 가능해진다.</li>
</ul>
<br>
<br>

<p>참고 : <a href="https://steady-coding.tistory.com/384">https://steady-coding.tistory.com/384</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[디자인패턴(1) - 어댑터패턴, 싱글톤패턴]]></title>
            <link>https://velog.io/@ryu-backend/%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B41</link>
            <guid>https://velog.io/@ryu-backend/%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B41</guid>
            <pubDate>Fri, 03 May 2024 17:37:36 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>디자인 패턴이란 일종의 설계 기법이며, 설계 방법이다.
목적 : SW 재사용성, 호환성, 유지 보수성을 보장
특징 : 아이디어이며, 특정한 구현이 아니다 / 프로젝트에 항상 적용해야 하는것은 아니지만, 추후 재사용, 호환, 유지 보수시 발생하는 문제 해결을 예방하기 위해 패턴을 만들어 둔 것.</p>
</blockquote>
<br>
<br>

<h2 id="원칙--solid객체-지향-설계-원칙">원칙- SOLID(객체 지향 설계 원칙)</h2>
<ol>
<li>SRP - 단일 책임 원칙 : 한 클래스는 하나의 책임만 가져야 함.</li>
<li>OCP - 개방/폐쇄 원칙 : 확장에는 열려있고, 수정에는 닫혀있어야 함.</li>
<li>LSP - 리스코프 치환 원칙 : 하위 타입은 항상 상위 타입을 대체 할 수 있어야 함.</li>
<li>ISP - 인터페이스 분리 원칙 : 인터페이스 내에 메소드는 최소한 일수록 좋다.( 하나의 일반적인 인터페이스 보다 여러 개의 구체적인 인터페이스가 낫다.) + SRP와 같은 문제에 대한 두 가지 다른 해결책임.</li>
<li>DIP - 의존관계 역전 원칙 : 구체적인 클래스보다 상위 클래스, 인터페이스, 추상클래스와 같이 변하지 않을 가능성이 높은 클래스와 관계를 맺어라. DIP 원칙을 따르는 가장 인기 있는 방법은 의존성 주입(DI)임.</li>
</ol>
<br>
<br>

<h2 id="분류⭐">분류(⭐)</h2>
<ol>
<li>생성 패턴 : 객체의 생성 방식 결정
EX) DBConnection을 관리하는 Instance를 하나만 만들 수 있도록 제한하여, 불필요한 연결을 막음</li>
<li>구조 패턴 : 객체간의 관계를 조직
EX) 2개의 인터페이스가 서로 호환이 되지 않을 때, 둘을 연결해주기 위해서 새로운 클래스를 만들어서 연결시킬 수 있도록 함.</li>
<li>행위 패턴 : 객체의 행위를 조직, 관리, 연합
EX) 하위 클래스에서 구현해야 하는 함수 및 알고리즘들을 미리 선언하여, 상속시 이를 필수로 구현하도록 함.</li>
</ol>
<img src = "https://gmlwjd9405.github.io/images/design-pattern/types-of-designpattern.png">

<br>
<br>

<h2 id="어댑터-패턴">어댑터 패턴</h2>
<blockquote>
<p>클래스를 바로 사용할 수 없는 경우가 있다.주로 다른 곳에서 개발했다거나, 수정할 수 없을때. 이에 중간에서 변환 역할을 해주는 클래스가 필요하다 -&gt; 어댑터 패턴
사용방법 : 상속
호환되지 않은 인터페이스를 사용하는 클라이언트 그대로 활용 가능
향후 인터페이스가 바뀌더라도, 변경 내역은 어댑터에 캡슐화 되므로 클라이언트 바뀔 필요X</p>
</blockquote>
<br>
<br>

<ul>
<li>어댑터는 필요로 하는 인터페이스로 바꿔주는 역할을 한다.</li>
<li>예를 들어 업체에서 제공한 클래스가 기존 시스템에 맞지 않는다면? --&gt; 기존 시스템을 수정할 것이 아니라, 어댑터를 활용해 유연하게 해결하자!</li>
</ul>
<br>
<br>

<h3 id="코드로-어댑터-패턴-이해하기">코드로 어댑터 패턴 이해하기</h3>
<blockquote>
<p>오리와 칠면조 인터페이스 생성
만약 오리 객체가 부족해서 칠면조 객체를 대신 사용해야 한다면?
두 객체는 인터페이스가 다르므로, 바로 칠면조 객체를 사용하는 것은 불가능함
따라서 칠면조 어댑터를 생성해서 활용해야 함</p>
</blockquote>
<ul>
<li>Duck.java<pre><code class="language-java">package AdapterPattern;
</code></pre>
</li>
</ul>
<p>public interface Duck {
    public void quack();
    public void fly();
}</p>
<pre><code>
- Turkey.java
```java
package AdapterPattern;

public interface Turkey {
    public void gobble();
    public void fly();
}</code></pre><ul>
<li>MallardDuck.java<pre><code class="language-java">package AdapterPattern;
</code></pre>
</li>
</ul>
<p>public class MallardDuck implements Duck {</p>
<pre><code>@Override
public void quack() {
    System.out.println(&quot;Quack&quot;);
}

@Override
public void fly() {
    System.out.println(&quot;I&#39;m flying&quot;);
}</code></pre><p>}</p>
<pre><code>- WildTurkey.java
```java
package AdapterPattern;

public class WildTurkey implements Turkey {

    @Override
    public void gobble() {
        System.out.println(&quot;Gobble gobble&quot;);
    }

    @Override
    public void fly() {
        System.out.println(&quot;I&#39;m flying a short distance&quot;);
    }
}</code></pre><ul>
<li>TurkeyAdapter.java<pre><code class="language-java">package AdapterPattern;
</code></pre>
</li>
</ul>
<p>public class TurkeyAdapter implements Duck {</p>
<pre><code>Turkey turkey;

public TurkeyAdapter(Turkey turkey) {
    this.turkey = turkey;
}

@Override
public void quack() {
    turkey.gobble();
}

@Override
public void fly() {
    turkey.fly();
}</code></pre><p>}</p>
<pre><code>- DuckTest.java
```java
package AdapterPattern;

public class DuckTest {

    public static void main(String[] args) {

        MallardDuck duck = new MallardDuck();
        WildTurkey turkey = new WildTurkey();
        Duck turkeyAdapter = new TurkeyAdapter(turkey);

        System.out.println(&quot;The turkey says...&quot;);
        turkey.gobble();
        turkey.fly();

        System.out.println(&quot;The Duck says...&quot;);
        testDuck(duck);

        System.out.println(&quot;The TurkeyAdapter says...&quot;);
        testDuck(turkeyAdapter);

    }

    public static void testDuck(Duck duck) {

        duck.quack();
        duck.fly();

    }
}
/* 출력결과
The turkey says...
Gobble gobble
I&#39;m flying a short distance
The Duck says...
Quack
I&#39;m flying
The TurkeyAdapter says...
Gobble gobble
I&#39;m flying a short distance
*/</code></pre><br>
<br>

<h2 id="싱글톤-패턴">싱글톤 패턴</h2>
<blockquote>
<p>애플리케이션이 시작될 때, 어떤 클래스가 최초 한 번만 메모리를 할당(static)하고 해당 메모리에 인스턴스를 만들어 사용하는 패턴 즉, 싱글톤 패턴은 &#39;하나&#39;의 인스턴스만 생성하여 사용하는 디자인 패턴이다.(인스턴스가 필요할 때, 똑같은 인스턴스를 만들지 않고 기존의 인스턴스를 활용하는 것)</p>
</blockquote>
<ul>
<li><p>생성자가 여러번 호출되어도, 실제로 생성되는 객체는 하나이며 최초로 생성된 이후에 호출된 생성자는 이미 생성한 객체를 반환시키도록 만드는 것이다.</p>
</li>
<li><p>Java에서는 생성자를 private으로 선언해 다른 곳에서 생성하지 못하도록 만들고, getInstance() 메소드를 통해 받아서 사용하도록 구현한다.</p>
</li>
<li><p>왜 쓸까? - 객체를 생성할 때마다 메모리 영역을 할당받아야 한다. 하지만 한번의 new를 통해 객체를 생성한다면 메모리 낭비를 방지할 수 있다.
또한 싱글톤으로 구현한 인스턴스는 &#39;전역&#39;이므로, 다른 클래스의 인스턴스들이 데이터를 공유하는 것이 가능한 장점이 있다.</p>
</li>
<li><p>그럼 많이 사용하는 경우는 언제? - 주로 공통된 객체를 여러개 생성해서 사용해야하는 상황 : DB에서 커넥션 풀, 스레드 풀, 캐시, 로그 기록 객체 등등</p>
</li>
</ul>
<h3 id="기본-싱글톤-패턴">기본 싱글톤 패턴</h3>
<pre><code class="language-java">public class Singleton { 

    private static Singleton instance; // 단 하나의 인스턴스만 사용

    private Singleton() {} // private 생성자. 외부에서 인스턴스 생성못함.

    public static Singleton getInstance() { // 단하나의 인스턴스만 사용

        if (instance == null){ //여러 스레드에서 이 곳을 동시에 실행하면 문제 발생
            instance = new Singleton(); 
        } 
    return instance; 
    } 
}</code></pre>
<ul>
<li>단점도 있을까? - SOLID 원칙 중의 OCP(개방/폐쇄 원칙) 에서 만약 싱글톤 인스턴스가 혼자 너무 많은 일을 하거나, 많은 데이터를 공유시키면 다른 클래스들 간의 결합도가 높아지는데, 이때 OCP에 위반된다.
결합도가 높아지면, 유지보수가 힘들고 테스트도 원활하게 진행할 수 없는 문제점이 발생한다.
또한, 멀티 스레드 환경에서 동기화 처리를 하지 않았을 때, 인스턴스가 2개가 생성되는 문제도 발생할 수 있다.</li>
</ul>
<br>
<br>

<h3 id="멀티스레드-환경에서-안전한-싱글톤-만드는-법">멀티스레드 환경에서 안전한 싱글톤 만드는 법</h3>
<br>



<ol>
<li><p>Lazy Initialization(게으른 초기화)</p>
<pre><code class="language-java">public class ThreadSafe_Lazy_Initialization{

 private static ThreadSafe_Lazy_Initialization instance;

 private ThreadSafe_Lazy_Initialization(){} //private으로 생성자를 만들어 외부에서의 생성을 막음

 public static synchronized ThreadSafe_Lazy_Initialization getInstance(){
     if(instance == null){
         instance = new ThreadSafe_Lazy_Initialization();
     }
     return instance;
 }
</code></pre>
</li>
</ol>
<p>}</p>
<pre><code>- 필드의 초기화 시점을 그 값이 처음 필요할 때까지 늦춘다. 값이 전혀 쓰이지 않으면 초기화도 결코 일어나지 않는다. 대부분의 경우에는 초기화 지연보다는 일반적으로 클래스를 생성하면서 초기화 하는것이 좋다. 초기화 비용이 크고, 내부적으로 필드 사용 빈도가 적다면 초기화 지연이 적절하다.
- 기본 싱글톤 패턴의 동기화 문제는 해결했지만, 성능상에 문제가 있다.
   - 여러 client의 요청이 있을 때 getInstance() 메서드에 static synchronized를 적용했기에 싱글톤 클래스 자체를 락을 걸었다. 즉, 클래스를 사용할 때 단 하나의 스레드만 사용하게 되는 것이다.

   &lt;br&gt;

2. Lazy Initialization + Double-checked Locking

```java
public class ThreadSafe_Lazy_Initialization{
    private volatile static ThreadSafe_Lazy_Initialization instance;

    private ThreadSafe_Lazy_Initialization(){}

    public static ThreadSafe_Lazy_Initialization getInstance(){
        if(instance == null) { //첫번째 검사는 락을 사용안함. 이미 초기화되어있다면 바로 리턴하여 동기화 비용을 없애준다.
            synchronized (ThreadSafe_Lazy_Initialization.class){ //위와 동일한 static synchronized
                if(instance == null){ //두번째 검사는 락을 사용한다. 초기화되어있지 않기 때문에
                    instance = new ThreadSafe_Lazy_Initialization();
                }
            }
        }
        return instance;
    }
}</code></pre><ul>
<li>double checked 방식은 필드가 초기화된 후로는 동기화하지 않으므로 해당 필드는 반드시 volatile로 선언해서 가시성을 확보해야 한다. 특히 멀티 코어 환경에서 동작한다면 스레드 별 cpu cache와 메인 메모리 간에 동기가 이뤄지지 않을 수 있다.</li>
<li>예를 들어 첫번째 스레드가 instance를 생성하고 synchronized를 벗어남.<ul>
<li>두번째 스레드가 synchronized 블록에 들어와서 null 체크를 하는 시점에서, 첫번째 스레드에서 생성한 instance가 cpu cache가 있는 working memory에만 존재하고 main memory에는 존재하지 않을 경우(메인 메모리에 반영이 안된 경우)</li>
<li>또는, main memory에 존재하지만 두번째 스레드의 working memory에 존재하지 않을 경우</li>
<li>즉, 메모리 간 동기화(스레드가 메인메모리와 cpu cache간에 데이터를 일관되게 유지하는 것)가 완벽히 이루어지지 않은 상태라면 두번째 스레드는 인스턴스를 또 생성하게 된다.</li>
<li><strong>메인 메모리는 모든 스레드가 공유하는 공간이며, cpu cache는 각 스레드가 자주 액세스하는 데이터를 저장하는 임시 저장소이다.</strong></li>
<li>여기서 <strong>volatile이란? cpu메모리 영역에 캐싱된 값이 아니라 항상 최신의 값을 가지도록 메인 메모리 영역에서 값을 참조하는 키워드!</strong></li>
</ul>
</li>
</ul>
<pre><code class="language-java">   public class Singleton {
    private volatile static DoubleCheckedSingleton instance;
    }</code></pre>
<ul>
<li>하지만 이 방법도 완벽하지는 않다.</li>
</ul>
<br>
<br>


<p>참고 : <a href="https://dev-coco.tistory.com/109">https://dev-coco.tistory.com/109</a></p>
<p><a href="https://gyoogle.dev/blog/design-pattern/Singleton%20Pattern.html">https://gyoogle.dev/blog/design-pattern/Singleton%20Pattern.html</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[MSA란?]]></title>
            <link>https://velog.io/@ryu-backend/MSA%EB%9E%80</link>
            <guid>https://velog.io/@ryu-backend/MSA%EB%9E%80</guid>
            <pubDate>Thu, 02 May 2024 07:35:03 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>MSA는 소프트웨어 개발 기법 중 하나로, 애플리케이션 단위를 &#39;목적&#39;으로 나누는 것이 핵심이다.</p>
</blockquote>
<br>

<h2 id="monolithic-vs-msa">Monolithic vs MSA</h2>
<p>MSA가 도입되기 전, Monolithic 아키텍처 방식으로 개발이 이루어졌다. Monolithic은 &#39;한 덩어리&#39;에 해당하는 구조로 이루어져있다. 즉, 모든 기능을 하나의 애플리케이션에서 비즈니스 로직을 구성하여 운영한다. 따라서 개발을 하거나 환경설정에 있어서 간단하다는 장점이 있어 작은 사이즈의 프로젝트에서는 유리하다. 그러나, 시스템이 점점 확장되거나 대규모 프로젝트에서는 단점들이 존재한다. </p>
<ul>
<li>빌드/테스트 시간의 증가 : 하나를 수정해도 시스템 전체를 빌드해야 함. 즉, 유지보수가 힘듬</li>
<li>작은 문제가 시스템 전체에 문제를 일으킴 : 만약 하나의 서비스 부분에 트래픽 문제로 서버가 다운되면, 모든 서비스 이용이 불가능함</li>
<li>확장성에 불리 : 서비스마다 이용률이 다를 수 있다. 하나의 서비스를 확장하기 위해 전체 프로젝트를 확장해야함</li>
</ul>
<br>




<br>
<br>

<h2 id="msa란">MSA란?</h2>
<p>MSA는 좀 더 세분화 시킨 아키텍처라고 말할 수 있다. 한꺼번에 비즈니스 로직을 구성하던 Monolithic 방식과는 다르게 기능(목적)별로 컴포넌트를 나누고 조합할 수 있도록 구축한다.
<img src="https://www.redhat.com/cms/managed-files/monolithic-vs-microservices.png" >
<img src = "https://miro.medium.com/max/1250/1*V_mvV0mbfcBoCcirDgsvZw.png" ></p>
<p>MSA에서 각 컴포넌트는 API를 통해 다른 서비스와 통신을 하는데, 모든 서비스는 각각 독립된 서버로 운영하고 배포하기 때문에 서로 의존성이 X. 더불어, 하나의 서비스에 문제가 생겨도 다른 서비스에는 영향을 끼치지 않으며 서비스 별로 부분적인 확장이 가능한 장점이 있다.</p>
<br>
<br>

<h2 id="무조건-msa을-써야할까">무조건 MSA을 써야할까?</h2>
<p>그렇지만은 않다. MSA는 서비스 별로 호출할 때 API로 통신하므로 속도가 느리다. 그리고 서비스 별로 통신에 맞는 데이터로 맞추는 과정이 필요하기도 하다. Monolithic방식은 하나의 프로세스 내에서 진행되기 때문에 속도 면에서는 MSA보다 훨씬 빠를것이다. 또한, MSA는 DB또한 개별적으로 운영되기 때문에 트랜잭션으로 묶기 힘들다.</p>
<br>
<br>

<h2 id="정리">정리</h2>
<p>서비스별로 분리를 하면서 얻을 수 있는 장점도 있지만, 그만큼 체계적으로 준비돼 있지 않으면 MSA로 인해 오히려 프로젝트의 성능이 떨어질 수도 있다. 프로젝트 목적, 현재 상황에 맞는 아키텍처 방식이 무엇인지 설계할 때 부터 잘 고민해서 선택해야한다.</p>
<br>

<p>🔎 참고 : <a href="https://gyoogle.dev/blog/computer-science/software-engineering/MSA.html">https://gyoogle.dev/blog/computer-science/software-engineering/MSA.html</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[백엔드 개발자의 필수 역량, 배포 자동화 ]]></title>
            <link>https://velog.io/@ryu-backend/%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%9D%98-%ED%95%84%EC%88%98-%EC%97%AD%EB%9F%89-%EB%B0%B0%ED%8F%AC-%EC%9E%90%EB%8F%99%ED%99%94</link>
            <guid>https://velog.io/@ryu-backend/%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%9D%98-%ED%95%84%EC%88%98-%EC%97%AD%EB%9F%89-%EB%B0%B0%ED%8F%AC-%EC%9E%90%EB%8F%99%ED%99%94</guid>
            <pubDate>Sun, 28 Apr 2024 06:15:45 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>배포 자동화는 개발자가 반복적인 배포 과정에서 발생할 수 있는 오류를 줄이고, 더 빠르고 안정적인 서비스 제공을 가능하게 한다.</p>
</blockquote>
<BR>


<h3 id="배포-자동화란">배포 자동화란?</h3>
<p>  코드 변경 사항을 자동으로 서버에 반영하는 과정. 이는 개발자가 수동으로 서버에 접속하여 코드를 업데이트 하는 번거로움을 줄이고, 개발 효율성을 크게 향상시킴</p>
  <br>

<p>  배포 자동화의 핵심은 CI/CD 파이프라인이다. CI는 지속적 통합을, CD는 지속적 배포/전달을 의미한다. 이 두 과정을 통해 코드 변경 사항이 자동으로 테스트되고, 빌드되어 배포된다.</p>
  <br>
 <br>

<h3 id="배포-자동화를-위한-도구와-기술">배포 자동화를 위한 도구와 기술</h3>
<p>  대표적인 도구로는 Jenkins, Travis CI, GitHub Actions 등이 있다. 예를 들어 GitHub Actions는 GitHub 저장소에 있는 코드 변경 사항이 푸시될 때마다 자동으로 빌드와 테스트, 배포 과정을 실행할 수 있게 해준다. 이는 개발자가 별도의 서버를 구축하거나 관리할 필요 없이 배포 자동화를 구현할 수 있게 해준다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[알고리즘 - 버블정렬, 선택정렬, 삽입정렬]]></title>
            <link>https://velog.io/@ryu-backend/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%B2%84%EB%B8%94%EC%A0%95%EB%A0%AC-%EC%84%A0%ED%83%9D%EC%A0%95%EB%A0%AC-%EC%82%BD%EC%9E%85%EC%A0%95%EB%A0%AC</link>
            <guid>https://velog.io/@ryu-backend/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%B2%84%EB%B8%94%EC%A0%95%EB%A0%AC-%EC%84%A0%ED%83%9D%EC%A0%95%EB%A0%AC-%EC%82%BD%EC%9E%85%EC%A0%95%EB%A0%AC</guid>
            <pubDate>Thu, 25 Apr 2024 11:46:02 GMT</pubDate>
            <description><![CDATA[<h1 id="✅-버블정렬">✅ 버블정렬</h1>
<blockquote>
<p>버블정렬(Bubble Sort) : 서로 인접한 두 원소의 대소를 비교하고, 조건에 맞지 않다면 자리를 교환하며 정렬하는 알고리즘이다. (맨 오른쪽부터 최대값을 채워넣어 점점 줄인다)</p>
</blockquote>
<br>

<h3 id="동작과정">동작과정</h3>
<ol>
<li>1회전에 첫 번째 원소와 두 번째 원소를, 두 번째 원소와 세 번째 원소를, 세 번째 원소와 네 번째 원소를, … 이런 식으로 (마지막-1)번째 원소와 마지막 원소를 비교하여 조건에 맞지 않는다면 서로 교환한다.</li>
<li>1회전을 수행하고 나면 가장 큰 원소가 맨 뒤로 이동하므로 2회전에서는 맨 끝에 있는 원소는 정렬에서 제외되고, 2회전을 수행하고 나면 끝에서 두 번째 원소까지는 정렬에서 제외된다. 이렇게 정렬을 1회전 수행할 때마다 정렬에서 제외되는 데이터가 하나씩 늘어난다.</li>
</ol>
<br>

<h3 id="구현코드--java">구현코드 ( Java)</h3>
<pre><code class="language-Java">void bubbleSort(int[] arr) {
    int temp = 0;
    for(int i = 0; i &lt; arr.length; i++) {       
        for(int j= 1 ; j &lt; arr.length-i; j++) { 
            if(arr[j-1] &gt; arr[j]) {             
                // swap(arr[j-1], arr[j])
                temp = arr[j-1];
                arr[j-1] = arr[j];
                arr[j] = temp;
            }
        }
    }
    System.out.println(Arrays.toString(arr));
}</code></pre>
<br>

<ul>
<li>시간복잡도 : 최선,평균,최악의 경우 모두 O(N^2)이다.</li>
<li>공간복잡도 : O(N)</li>
<li>장점 : 구현이 매우 간단하고, 코드가 직관적, 안정정렬이다.</li>
<li>단점 : 시간복잡도 측면에서 굉장히 비효율적</li>
</ul>
<br>
<br>

<h1 id="✅-선택정렬">✅ 선택정렬</h1>
<blockquote>
<p>선택정렬(Selection Sort) : 해당 순서에 원소를 넣을 위치는 이미 정해져 있고, 어떤 원소를 넣을지 선택하는 알고리즘이다.
(맨 왼쪽부터 최솟값을 채워넣어 점점 줄인다)</p>
</blockquote>
<br>

<h3 id="동작과정-1">동작과정</h3>
<ol>
<li>주어진 배열 중에 최소값을 찾는다.</li>
<li>그 값을 맨 앞에 위치한 값과 교체한다. (pass)</li>
<li>맨 처음 위치를 뺀 나머지 배열을 같은 방법으로 교체한다.</li>
</ol>
<br>

<h3 id="구현코드--java-1">구현코드 ( Java)</h3>
<pre><code class="language-Java">void selectionSort(int[] arr) {
    int indexMin, temp;
    for (int i = 0; i &lt; arr.length-1; i++) {        
        indexMin = i;
        for (int j = i + 1; j &lt; arr.length; j++) {  
            if (arr[j] &lt; arr[indexMin]) {           
                indexMin = j;
            }
        }
         swap(arr[indexMin], arr[i])
        temp = arr[indexMin];
        arr[indexMin] = arr[i];
        arr[i] = temp;
  }
  System.out.println(Arrays.toString(arr));
}</code></pre>
<br>

<ul>
<li>시간복잡도 : 최선,평균,최악의 경우 모두 O(N^2)이다.</li>
<li>공간복잡도 : O(N)</li>
<li>장점 : 단순하다. 버블정렬에 비해 비교적으로 효율적이다, 제자리 정렬</li>
<li>단점 : 시간복잡도 측면에서 버블정렬과 마찬가지로 굉장히 비효율적, 불안정정렬</li>
</ul>
<br>
<br>

<h1 id="✅-삽입정렬">✅ 삽입정렬</h1>
<blockquote>
<p>삽입정렬(Insertion Sort) : 2번째 원소부터 시작하여 그 앞(왼쪽)의 원소들과 비교하여 삽입할 위치를 지정한 후, 원소를 뒤로 옮기고 지정된 자리에 자료를 삽입하여 정렬하는 알고리즘이다. </p>
</blockquote>
<br>

<h3 id="동작과정-2">동작과정</h3>
<ol>
<li>정렬은 2번째 위치(index)의 값을 temp에 저장한다.</li>
<li>temp와 이전에 있는 원소들과 비교하며 삽입해나간다.</li>
<li>&#39;1&#39;번으로 돌아가 다음 위치(index)의 값을 temp에 저장하고, 반복한다.</li>
</ol>
<br>

<h3 id="구현코드--java-2">구현코드 ( Java)</h3>
<pre><code class="language-Java">void insertionSort(int[] arr)
{
   for(int index = 1 ; index &lt; arr.length ; index++){
      int temp = arr[index];
      int prev = index - 1;
      while( (prev &gt;= 0) &amp;&amp; (arr[prev] &gt; temp) ) {   
         arr[prev+1] = arr[prev];
         prev--;
      }
      arr[prev + 1] = temp;                          
   }
   System.out.println(Arrays.toString(arr));
}</code></pre>
<br>

<ul>
<li>시간복잡도 : 평균,최악의 경우 모두 O(N^2)이다. 그러나, 최선의 경우 O(N)이다.</li>
<li>공간복잡도 : O(N)</li>
<li>장점 : 단순하다. 대부분의 원소가 이미 정렬되어 있는 경우, 매우 효율적임, 제자리 정렬, 안정정렬, 이전의 두 알고리즘에 비교하여 상대적으로 빠르다</li>
<li>단점 : 평균과 최악의 시간복잡도가 O(N^2)으로 비효율적, 이전의 두 알고리즘과 마찬가지로, 배열의 길이가 길어질수록 비효율적임.</li>
</ul>
<br>
<br>

<p>참고 : <a href="https://gyoogle.dev/blog/algorithm/Insertion%20Sort.html">https://gyoogle.dev/blog/algorithm/Insertion%20Sort.html</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] - Thread(동기화)]]></title>
            <link>https://velog.io/@ryu-backend/Java-Thread%EB%8F%99%EA%B8%B0%ED%99%94</link>
            <guid>https://velog.io/@ryu-backend/Java-Thread%EB%8F%99%EA%B8%B0%ED%99%94</guid>
            <pubDate>Sat, 30 Mar 2024 07:58:23 GMT</pubDate>
            <description><![CDATA[<h2 id="스레드-2편">스레드 2편</h2>
<blockquote>
<p>이전 포스팅에서 스레드에 대한 기초 개념을 알아보았다. 여기서 멀티스레드로 구현을 하다보면, 동기화는 때에 따라 필수적이다. 여러 스레드가 같은 프로세스 내의 자원을 공유하면서 작업할 때 서로의 작업이 다른 작업에 영향을 주기 때문에 이와같은 상황엔 동기화가 필수적이다. 
 스레드의 동기화를 위해선, 임계영역과 잠금(lock)을 활용한다.
 임계영역을 지정하고, 임계영역을 가지고 있는 lock을 단 하나의 스레드에게만 빌려주는 개념이다. 따라서 임계구역 안에서 수행할 코드가 완료되면, lock을 반납해야한다.</p>
</blockquote>
 <br>
 <br>





<h3 id="스레드-동기화-방법">스레드 동기화 방법</h3>
<ul>
<li><p>임계영역 : 공유 자원에 단 하나의 스레드만 접근하도록(하나의 프로세스에 속한 스레드만 가능)</p>
</li>
<li><p>뮤텍스 : 공유 자원에 단 하나의 스레드만 접근하도록(서로 다른 프로세스에 속한 스레드도 가능)</p>
</li>
<li><p>이벤트 : 특정한 사건 발생을 다른 스레드에게 알림</p>
</li>
<li><p>세마포어 : 한정된 개수의 자원을 여러 스레드가 사용하려고 할 때 접근 제한</p>
</li>
<li><p>대기 가능 타이머 : 특정 시간이 되면 대기 중이던 스레드 깨움</p>
<br>
<br>


</li>
</ul>
<h3 id="예제코드동기화x">예제코드(동기화x)</h3>
<p> 아래의 코드는 의도적으로 계좌에 동시 접근이 가능하도록 만든 코드이다. 출금할 금액이 계좌 잔액보다 크면, 출금을 못하도록 설정하였으나, 실행 과정에서 의도적으로 스레드가 동시에 접근할 수 있도록 하였다. </p>
<pre><code class="language-Java">public class ThreadSyncEx {
    public static void main(String[] args) {
        Runnable thread = new CreateThread();
        // 2개의 작업 스레드 생성
        Thread thread1 = new Thread(thread);
        Thread thread2 = new Thread(thread);

        thread1.setName(&quot;스레드1&quot;);
        thread2.setName(&quot;스레드2&quot;);

        thread1.start();
        thread2.start();
    }
}

class Money {
    // 현재 가지고 있는 금액
    private int myMoney = 10000;

    public int getMyMoney() {
        return myMoney;
    }

    public boolean withdraw(int money) {
        // 가지고 있는 금액이 출금할 금액보다 크거나 같을 때만 출금
        if (myMoney &gt;= money) {
            // 스레드 동시 접근을 위한 코드
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                System.out.println(e);
            }

            // 출금
            myMoney -= money;

            return true;
        }
        return false;
    }
}

class CreateThread implements Runnable {
    Money myMoney = new Money();

    public void run() {
        while (myMoney.getMyMoney() &gt; 0) {
            // 1000 ~ 5000원씩 출금
            int money = (int)(Math.random() * 5 + 1) * 1000;

            // 출금 실행 코드. 실패시 true 반환
            boolean denied = !myMoney.withdraw(money);

            // 출금 과정 출력
            if (denied) {
                System.out.println(&quot;출금 거부&quot;);
            } else {
                System.out.printf(&quot;스레드: %s 출금: %d원  남은금액: %d원\\n&quot;,
                    Thread.currentThread().getName(), money, myMoney.getMyMoney());
            }
        }
    }
}
// 출력 (실행할 때마다 결과는 달라진다.)
스레드: 스레드1 출금: 5000원  남은금액: 5000원
스레드: 스레드2 출금: 2000원  남은금액: 5000원
스레드: 스레드2 출금: 1000원  남은금액: 4000원
출금 거부
스레드: 스레드1 출금: 1000원  남은금액: 3000원
스레드: 스레드2 출금: 2000원  남은금액: -2000원
스레드: 스레드1 출금: 3000원  남은금액: -2000원
</code></pre>
<p>결과를 보면 스레드가 출금을 동시에 실시하여 남은 금액이 3000원임에도 불구하고, 2000원 출금과 3000원 출금이 동시에 발생하여 -2000원되는 상황이 발생했다.
스레드 동기화를 하지 않으면 실제 서비스나 프로그램을 이용할 때, 이러한 상황이 실제로 일어날 수 있다.</p>
<h3 id="임계영역과-lock">임계영역과 lock?</h3>
<p>임계 영역은 둘 이상의 스레드가 동시에 접근해서는 안되는 코드 영역이다. 즉, 하나의 스레드만이 코드를 실행할 수 있는 영역이다. lock은 임계 영역을 포함하고 있는 객체에 접근할 수 있는 권한을 의미한다.</p>
<p><strong>🔎 동작과정</strong></p>
<ul>
<li>먼저, synchronized 키워드를 통해 동시 접근이 가능한 영역을 임계 영역으로 설정하여 동시 접근을 못하도록 설정한다.</li>
<li>스레드가 임계영역에 접근하게 되면, 해당 스레드는 lock을 얻게된다. 이후 해당 스레드가 lock을 반납하기 이전에는 다른 스레드는 해당 임계 영역에 접근하지 못하게 한다.</li>
<li>synchronized 키워드는 메서드 전체를 임계 영역으로 설정하는 방법과 특정 코드 블록을 임계 영역으로 설정하는 방법이 있다. </li>
</ul>
<br>
<br>

<h4 id="메서드-전체를-임계영역으로-설정">메서드 전체를 임계영역으로 설정</h4>
<p> synchronized 키워드를 임계 영역으로 지정할 메서드의 반환 타입 앞에 입력하여 메서드 전체를 임계 영역으로 설정할 수 있다.
설정한 메서드가 호출되었을 때, 메서드를 실행할 스레드는 메서드가 포함된 객체의 락(Lock)을 얻으며, 다시 락(Lock)을 반납하기 전까지는 다른 스레드는 해당 메서드를 실행하지 못한다.</p>
<pre><code class="language-Java">class Money {
    private int myMoney = 10000;

    public int getMyMoney() {
        return myMoney;
    }

    // 메서드 전체를 임계영역으로 설정
    public synchronized boolean withdraw(int money) {
        if (myMoney &gt;= money) {
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                System.out.println(e);
            }
            myMoney -= money;
            return true;
        }
        return false;
    }
}
</code></pre>
<br>
<br>

<h4 id="특정-영역을-임계-영역으로-설정">특정 영역을 임계 영역으로 설정</h4>
<p>임계영역으로 지정할 코드 상단에 synchronized 키워드를 쓰고 소괄호 () 안에 해당 영역이 포함된 객채의 참조를 입력하여 지정할 코드까지 중괄호{}로 묶으면 해당 영역으로 설정된다.</p>
<pre><code>class Money {
    private int myMoney = 10000;

    public int getMyMoney() {
        return myMoney;
    }

    public boolean withdraw(int money) {
        // 메서드 전체를 임계영역으로 설정
        synchronized (this) {
            if (myMoney &gt;= money) {
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {
                    System.out.println(e);
                }
                myMoney -= money;
                return true;
            }
            return false;
        }
    }
}
// 출력
스레드: 스레드1 출금: 1000원  남은금액: 9000원
스레드: 스레드2 출금: 2000원  남은금액: 7000원
스레드: 스레드1 출금: 4000원  남은금액: 3000원
출금 거부
출금 거부
스레드: 스레드1 출금: 3000원  남은금액: 0원
</code></pre><p>잔액이 음수가 발생할 경우가 생기더라도 스레드 동기화로 인해 출금이 거부된다!!</p>
<br>
<br>

<p>출처 : <a href="https://ittrue.tistory.com/173">https://ittrue.tistory.com/173</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] - Thread]]></title>
            <link>https://velog.io/@ryu-backend/JAVA-Thread</link>
            <guid>https://velog.io/@ryu-backend/JAVA-Thread</guid>
            <pubDate>Fri, 29 Mar 2024 08:44:53 GMT</pubDate>
            <description><![CDATA[<h3 id="스레드란">스레드란?</h3>
<blockquote>
<p>스레드 설명에 앞서 프로세스는 무엇일까? 프로세스는 독립적인 실행 단위이고, 스레드는 프로세스 내에서 실행되는 실행흐름이다. 스레드는 프로세스 내에서 동시에 실행될 수 있으므로 프로그램의 성능을 향상시키는 데 사용할 수 있음.</p>
</blockquote>
<h4 id="멀티-스레드의-활용">멀티 스레드의 활용</h4>
<ul>
<li>대용량 데이터의 분할 병렬 처리</li>
<li>애플리케이션의 UI 네트워크 통신</li>
<li>다수의 클라이언트 요청을 받는 서버</li>
</ul>
<BR>

<p>  모든 자바 애플리케이션은 메인스레드가 main() 메소드를 실행하면서 시작된다.</p>
  <br>
 main() 메소드를 실행시키는 주체는 메인스레드 이며, 메인스레드는 필요에따라 작업스레드를 만들어 병렬로 코드를 실행할 수 있다.


  </br>
</br>

</br>











<h3 id="작업스레드-생성방법">작업스레드 생성방법</h3>
<p>1.Runnable 인터페이스를 상속한 클래스 만들기 (일반적으로 이 방법을 사용)
2. Thread 클래스를 상속한 클래스를 만들기
⭐ 둘다 run() 메소드를 오버라이드 해야 함
<br></p>
<h3 id="1-runnable-인터페이스를-상속한-클래스-만들기">1. Runnable 인터페이스를 상속한 클래스 만들기</h3>
<pre><code class="language-Java">public class Main {

    public static void main(String[] args) {
        Runnable print100 = new PrintNum(100);

        Thread thread1 = new Thread(print100);
        Thread thread2 = new Thread(print100);

        thread1.start();
        thread2.start();
    }
}

class PrintNum implements Runnable{
    private int lastNum;

    public PrintNum(int n){
        lastNum = n;
    }

    @Override
    public void run(){
        for (int i = 0; i &lt;= lastNum; i++) {
            System.out.println(&quot; &quot; + i);
        }
    }
}</code></pre>
<p>  Runnable 인터페이스를 구현한 클래스를 만들기 Runnable 인터페이스를 구현한 클래스를 만들고, 이 클래스의 인스턴스를 Thread 클래스의 생성자에 전달하여 Thread 객체를 만든다. 이때, Thread 객체를 start() 메서드로 시작시키면 Runnable 인터페이스의 run() 메서드가 호출되어 작업 스레드가 실행된다.
  <br></p>
<h3 id="2-thread-클래스를-상속한-클래스를-만들기">2. Thread 클래스를 상속한 클래스를 만들기</h3>
<pre><code class="language-Java">public class Main {

    public static void main(String[] args) {
        PrintChar thread1 = new PrintChar(&#39;A&#39;, 10);
        PrintChar thread2 = new PrintChar(&#39;B&#39;, 10);

        thread1.start();
        thread2.start();
    }
}

class PrintChar extends Thread{
    private char charToPrint;
    private int times;

    public PrintChar(char c, int t){
        charToPrint = c;
        times = t;
    }

    @Override
    public void run(){
        for (int i = 0; i &lt; times; i++) {
            System.out.println(i);
        }
    }
}</code></pre>
<p>PrintChar 클래스</p>
<p>문자 하나와 숫자 하나를 인자로 받아서, 해당 문자를 숫자만큼 반복해서 출력하는 스레드이다.</p>
<br>


<p><strong>❗ 주의 : Thread를 실행할 때 start()와 run() 의 차이??</strong></p>
<p>  run()을 호출하면 생성된 스레드 객체를 실행하는 것이 아니라, 단순 스레드 클래스 내부의 run 메소드를 실행시킨다. 따라서 main 함수의 스레드를 그대로 사용해서 run 메소드를 실행하기 때문에 새로운 스레드가 생기지 않고 <strong>병렬처리가 불가능하다.</strong>
<br>
하지만 start()는 새로운 스레드를 실행하는데 필요한 call stack을 생성한 다음에 run()을 호출해, 생성된 call stack에 run()이 첫번째로 저장되게 한다. 즉, start()를 호출하면 스레드를 새롭게 생성해서 해당 스레드를 runnable 한 상태로 만든 후 run() 메소드를 실행하게 된다. 
<strong>결론은 start()를 호출해야만 멀티스레드로 병렬 처리가 가능하다!!!</strong></p>
<br>
<br>


<p><strong>⭐ 그러면 왜 추상 메서드로 run()밖에 존재하지 않는 Runnable은 왜 사용하는 것일까?</strong></p>
<p>Thread를 바로 사용하려면 상속을 받아야 한다. Java는 다중 상속을 허용하지 않기 때문에 Thread 클래스를 바로 상속받는 경우 다른 클래스를 상속받지 못한다. 그러나 Runnable 인터페이스를 구현한 경우에는 다른 인터페이스를 추가로 구현할 수 있을 뿐만 아니라, 다른 클래스도 상속받을 수 있다. 
 따라서 클래스의 확장성이 중요하다면 Runnable 인터페이스를 구현해 Thread에 주입하는 방식이 좋을 수 있다.
 <br>
<br></p>
<p>참고 : <a href="https://hyeo-noo.tistory.com/293">https://hyeo-noo.tistory.com/293</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DB] - 튜닝,클러스터링,리플리케이션]]></title>
            <link>https://velog.io/@ryu-backend/DB-%ED%8A%9C%EB%8B%9D%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81%EB%A6%AC%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98</link>
            <guid>https://velog.io/@ryu-backend/DB-%ED%8A%9C%EB%8B%9D%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81%EB%A6%AC%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98</guid>
            <pubDate>Wed, 27 Mar 2024 08:48:45 GMT</pubDate>
            <description><![CDATA[<h3 id="db-튜닝이란">DB 튜닝이란?</h3>
<blockquote>
<p>DB 튜닝이란 DB의 구조나, DB 자체, 운영체제 등을 조정하여 DB 시스템의 전체적인 성능을 개선하는 작업
튜닝은 DB 설계 튜닝 -&gt; DBMS 튜닝 -&gt; SQL 튜닝 단계로 진행</p>
</blockquote>
<ul>
<li><p>DB 설계 튜닝(모델링 관점)</p>
<ul>
<li>DB 설계 단계에서 성능을 고려하여 설계</li>
<li>데이터 모델링, 인덱스 설계</li>
<li>데이터파일, 테이블 스페이스 설계</li>
<li>데이터베이스 용량 산정</li>
<li>튜닝 사례 : 반정규화, 분산파일배치</li>
</ul>
<BR>
</li>
<li><p>DBMS 튜닝(환경관점)</p>
<ul>
<li>성능을 고려하여 메모리나 블록 크기 지정</li>
<li>CPU, 메모리 I/0에 관한 관점</li>
<li>튜닝 사례 : Buffer 크기, Cache 크기</li>
</ul>
<br>

<ul>
<li>SQL 튜닝(App관점)<ul>
<li>SQL 작성 시 성능 고려</li>
<li>Join, Indexing, SQL Execution Plan</li>
<li>튜닝 사례 : Hash/ Join</li>
</ul>
</li>
</ul>
</li>
</ul>
 <br>


<h3 id="delete-truncate-drop-의-차이">DELETE, TRUNCATE, DROP 의 차이?</h3>
<ul>
<li>DELETE: 테이블에서 특정 조건을 만족하는 행(레코드)을 삭제하는 데 사용됩니다. 이는 특정 행을 삭제하는 데 사용되며, 테이블의 데이터만 영향을 줍니다. 삭제 후 되돌릴수 있습니다.</li>
</ul>
<ul>
<li><p>TRUNCATE: 특정 테이블의 모든 데이터를 삭제하는 데 사용됩니다. TRUNCATE 문을 사용하면 테이블의 모든 행이 삭제되지만 테이블 자체는 삭제되지 않습니다. TRUNCATE는 일반적으로 DELETE보다 더 빠르게 실행되지만, 롤백(rollback)이 불가능하며, 트리거(trigger)를 실행하지 않습니다. 테이블 용량이 줄어들고 인덱스 등도 삭제됩니다. </p>
</li>
<li><p>DROP: 특정 테이블이나 데이터베이스 객체를 삭제하는 데 사용됩니다. DROP 문은 테이블, 뷰(view), 인덱스(index), 프로시저(procedure), 함수(function) 등과 같은 객체를 삭제할 수 있습니다. 이 명령을 사용하면 해당 객체와 관련된 모든 데이터가 삭제됩니다. 롤백이 불가능합니다.</p>
<BR>


</li>
</ul>
<BR>




<h3 id="db-클러스터링--리플리케이션-">DB 클러스터링 &amp; 리플리케이션 ?</h3>
<p>  📌 <strong>클러스터링</strong></p>
<blockquote>
<p>클러스터링이란 여러 개의 DB를 수평적인 구조로 구축하는 방식입니다. 동기 방식으로 사용되며, 다음과 같은 장단점이 있습니다.</p>
</blockquote>
  <img src="https://user-images.githubusercontent.com/63634505/133868115-03263a5c-d337-4707-a2b6-afcc43c6f552.png">










<p>  👀 <strong>장점</strong> :</p>
<ul>
<li><p>DB 간의 데이터를 동기화하여 항상 일관성있는 데이터를 얻을 수 있다.</p>
<ul>
<li>1개의 DB가 죽어도 다른 DB가 살아 있어 시스템을 장애없이 운영할 수 있다.(높은 가용성)</li>
<li>기존에 하나의 DB서버에 몰리던 부하를 여러 곳으로 분산시킬 수 있다.(로드밸런싱)</li>
</ul>
<BR>


</li>
</ul>
<p>👀 <strong>단점</strong> : </p>
<ul>
<li>저장소 하나를 공유하면 병목현상이 발생할 수 있다.</li>
<li>병목현상 : 전체 시스템이 성능이나 용량이 하나의 구성요소로 인해 제한을 받는 현상</li>
<li>서버를 동시에 운영하기 위한 비용이 많이 든다.</li>
</ul>
<BR>
<BR>







<p>  📌 <strong>리플리케이션</strong></p>
<blockquote>
<p>리플리케이션이란 여러 개의 DB를 권한에 따라 수직적인 구조로 구축하는 방식입니다. 비동기 방식으로 사용되며, 다음과 같은 장단점이 있습니다.</p>
</blockquote>
  <img src="https://user-images.githubusercontent.com/63634505/133868152-6a82b34f-1994-484b-805e-cd0b2f9c4d5b.png">









<p>  👀 <strong>장점</strong> :</p>
<ul>
<li><p>DB 요청의 60~80% 정도가 읽기 작업이기 때문에 Replication만으로도 충분히 성능을 높일 수 있다.</p>
</li>
<li><p>비동기 방식으로 운영되어 지연 시간이 거의 없다.</p>
<br>



</li>
</ul>
<p>  👀 <strong>단점</strong> : </p>
<ul>
<li>노드들 간 데이터 동기화가 보장되지 않아 일관성있는 데이터를 얻지 못할 수 있다.<ul>
<li>Master DB가 다운되면 복구 및 대처가 까다롭다.</li>
</ul>
</li>
</ul>
  <BR>







  <BR>

<h3 id="⭐-정리">⭐ <strong>정리</strong></h3>
<p>   _클러스터링은 DB 서버만을 늘려 처리하는 것이고 리플리케이션은 DB 서버와 스토리지 모두 복제하여 구성하는 방법이다. 둘 중 어느것이 좋다고 단정 지을 수는 없다. 무엇보다 DB를 무조건 확장하는 것은 최대한 피해야 한다. 각자의 서비스에 맞춰서 적절하게 선택한다면 좀 더 성능이 좋은 서비스를 제공할 수 있다.
  _</p>
<BR>
<BR>
<BR>


<p>  참고 : </p>
  <BR>

<p><a href="https://tecoble.techcourse.co.kr/post/2021-09-18-replication_clustering/">https://tecoble.techcourse.co.kr/post/2021-09-18-replication_clustering/</a></p>
  <BR>
    https://dev-coco.tistory.com/158]]></description>
        </item>
        <item>
            <title><![CDATA[코테-3(Python)]]></title>
            <link>https://velog.io/@ryu-backend/%EC%BD%94%ED%85%8C-3Python</link>
            <guid>https://velog.io/@ryu-backend/%EC%BD%94%ED%85%8C-3Python</guid>
            <pubDate>Thu, 21 Mar 2024 14:10:08 GMT</pubDate>
            <description><![CDATA[<h3 id="스택-자료구조">스택 자료구조</h3>
<blockquote>
<p>먼저 들어온 데이터가 나중에 나가는 선입후출의 자료구조 --&gt; 쌓는 개념
입구와 출구가 동일한 형태로 스택을 시각화<br>파이썬에서의 스택은 리스트이다.</p>
</blockquote>
<ul>
<li>stack=[] # 스택선언</li>
<li>삽입 : .append(원소값) , 삭제 : .pop() - 맨 마지막 원소 리턴후 삭제 , pop(i) : 리스트의 i번째 원소를 리턴하고 삭제 </li>
<li>최상단 원소부터 출력 : print(stack[::-1]) # 결과는 리스트 형태</li>
<li>최하단 원소부터 출력 : print(stack)    # 결과는 리스트 형태</li>
</ul>
<br>

<h3 id="큐-자료구조">큐 자료구조</h3>
<blockquote>
<p>먼저 들어온 데이터가 먼저 나가는 선입선출의 자료구조 --&gt;터널 개념
입구와 출구가 모두 뚫려 있는 형태로 시각화 --&gt; 대기열
파이썬에서는 큐를 쓸려면 from collections import deque</p>
</blockquote>
<ul>
<li>from collections import deque
queue = deque() # 큐 구현을 위해 deque 라이브러리를 사용</li>
<li>삽입 : .append(원소값) , 삭제 : .popleft()</li>
<li>print(queue) # 먼저 들어온 순서대로 출력  # 실행결과 deque([!,!,!,!])</li>
<li>queue.reverse() # 역순으로 바꾸기</li>
</ul>
<br>

<h3 id="재귀함수">재귀함수</h3>
<blockquote>
<p>무한루프를 이용하는 게 아니라면 재귀 함수를 문제 풀이에서 사용할 때는 재귀 함수의 종료 조건을 반드시 명시해야함 종료시에는 스택과 같이 나중함수부터 종료됨</p>
</blockquote>
<pre><code class="language-python">def refunc(i):
    # 100번째 호출을 했을 때 종료되도록 종료 조건 명시
    if i ==100:
        return
    print(i,&#39;번째 재귀함수에서&#39;,i+1,&#39;번째 재귀함수를 호출합니다.&#39;)
    refunc(i+1)
    print(i,&#39;번째 재귀함수를 종료합니다.&#39;)

refunc(1)</code></pre>
<h3 id="dfs">dfs</h3>
<blockquote>
<p>깊이 우선탐색, 스택자료구조(혹은 재귀함수)를 사용 
동작 과정: </p>
</blockquote>
<ol>
<li>탐색 시작 노드를 스택에 삽입하고 방문 처리를 합니다.</li>
<li>스택의 최상단 노드에 방문하지 않은 인접한 노드가 하나라도 있으면 그 노드를 스택에 넣고 방문 처리합니다. 방문하지 않은 인접 노드가 없으면 스택에서 최상단 노드를 꺼냄.</li>
<li>더 이상 2번의 과정을 수행할 수 없을 때까지 반복</li>
</ol>
<pre><code class="language-python"># 각 노드가 연결된 정보를 표현(2차원 리스트)
graph = [
[], # 1번 노드부터 시작하기에 인덱스상 0 번위치는 비워둔다.
[2,3,8], # 1번노드와 인접한 노드번호
[1,7],...]   # 2번 노드와 인접한 노드번호
visited=[False]*9
def dfs(map,i,visited):
    visited[i]=True
    print(i, end=&quot; &quot;)
    for k in map[i]:
        if not visited[k]:
            dfs(map,k,visited)

dfs(map,1,visited)
...</code></pre>
<h3 id="bfs">bfs</h3>
<blockquote>
<p>너비 우선 탐색, 그래프에서 가장 가까운 노드부터 우선적으로 탐색하는 알고리즘
큐 자료구조를 이용하며, 특정 조건에서의 최단경로 찾기 등에서 쓰인다. 구체적인 동작 과정은 다음과 같다.</p>
</blockquote>
<ol>
<li>탐색 시작 노드를 큐에 삽입하고 방문 처리를 한다.</li>
<li>큐에서 노드를 꺼낸 뒤에 해당 노드의 인접 노드 중에서 방문하지 않은 노드를 모두 큐에 삽입하고 방문 처리한다.</li>
<li>더 이상 2번의 과정을 수행할 수 없을 때까지 반복한다.</li>
</ol>
<pre><code class="language-python">
from collections import deque
# 각 노드가 연결된 정보를 표현(2차원 리스트)
graph = [
[], # 1번 노드부터 시작하기에 인덱스상 0 번위치는 비워둔다.
[2,3,8], # 1번노드와 인접한 노드번호
[1,7],...]   # 2번 노드와 인접한 노드번호
visited=[False]*9
def bfs(map,i,visited):
    #큐 구현을 위해 deque 라이브러리 사용
    queue = deque([start])
    # 현재 노드를 방문 처리
    visited[start]=True
    # 큐가 빌 때까지 반복
    while queue:
        # 큐에서 하나의 원소를 뽑아 출력하기
        v=queue.popleft()
        print(v,end=&quot; &quot;)
        # 아직 방문하지 않은 인접한 원소들을 큐에 삽입
        for i in map[v]:
            if not visited[i]:
                queue.append(i)
                visited[i]=True

bfs(map,1,visited)
...</code></pre>
<br>

<ul>
<li><p>2차원 배열 초기화 : n,m을 안다고 가정하면 
arr=[[0]*m for _ in range(n)]</p>
</li>
<li><p>from itertools import cycle 을 쓰면 반복가능한 객체를 순서대로 무한히 반복하는 이터레이터를 생성하는 함수이다.</p>
</li>
</ul>
<pre><code class="language-python"> import itertools
 emp_pool = itertools.cycle([&#39;김은경&#39;, &#39;이명자&#39;, &#39;이성진&#39;])
 next(emp_pool)
&#39;김은경&#39;
 next(emp_pool)
&#39;이명자&#39;
 next(emp_pool)
&#39;이성진&#39;
 next(emp_pool)
&#39;김은경&#39;
 next(emp_pool)
&#39;이명자&#39;
...
</code></pre>
<br>

<ul>
<li>순열, 조합을 쓸때 여러개의 튜플형태로 반환이 되므로 튜플의 형태를 없애고 튜플안에 있는 값을 문자열로 이어서 붙이고 싶으면 다음과 같이 한다. </li>
</ul>
<ol>
<li>순회가능한 객체가 문자열로 이루어진 경우
import itertools</li>
</ol>
<pre><code>items = [&#39;A&#39;, &#39;B&#39;, &#39;C&#39;]
nPr = itertools.permutations(items, 2) # 리스트에서 2개의 원소를 골라 순서정해 나열
print(list(nPr))
 [(&#39;A&#39;, &#39;B&#39;), (&#39;A&#39;, &#39;C&#39;), (&#39;B&#39;, &#39;A&#39;), (&#39;B&#39;, &#39;C&#39;), (&#39;C&#39;, &#39;A&#39;), (&#39;C&#39;, &#39;B&#39;)]

# items의 모든 원소를 가지고 순열을 만든다
print(list(map(&#39;&#39;.join, itertools.permutations(items))))
&gt;&gt;&gt; [&#39;ABC&#39;, &#39;ACB&#39;, &#39;BAC&#39;, &#39;BCA&#39;, &#39;CAB&#39;, &#39;CBA&#39;]

# 2개의 원소를 가지고 순열을 만든다
print(list(map(&#39;&#39;.join, itertools.permutations(items, 2))))
&gt;&gt;&gt; [&#39;AB&#39;, &#39;AC&#39;, &#39;BA&#39;, &#39;BC&#39;, &#39;CA&#39;, &#39;CB&#39;]
</code></pre><h3 id="❗-주의할점--숫자-형태의-경우-mapstr리스트명의-과정이-추가로-들어가야함">❗ 주의할점 : 숫자 형태의 경우 map(str,리스트명)의 과정이 추가로 들어가야함</h3>
<pre><code class="language-python">import itertools

# 숫자형태의 경우
nums=[3,1,2,4]
print(list(map(&#39;&#39;.join, itertools.permutations(map(str,nums), 2))))
&gt;&gt;&gt;    [&#39;31&#39;, &#39;32&#39;, &#39;34&#39;, &#39;13&#39;, &#39;12&#39;, &#39;14&#39;, &#39;23&#39;, &#39;21&#39;, &#39;24&#39;, &#39;43&#39;, &#39;41&#39;, &#39;42&#39;]</code></pre>
<br>
코드 출처 : https://jungeun960.tistory.com/173]]></description>
        </item>
        <item>
            <title><![CDATA[코테-2(Python)]]></title>
            <link>https://velog.io/@ryu-backend/%EC%BD%94%ED%85%8C-2Python</link>
            <guid>https://velog.io/@ryu-backend/%EC%BD%94%ED%85%8C-2Python</guid>
            <pubDate>Wed, 20 Mar 2024 06:37:44 GMT</pubDate>
            <description><![CDATA[<h3 id="파이썬-리스트를-문자열로-변환">파이썬 리스트를 문자열로 변환</h3>
<p>arr = [&#39;a&#39;,&#39;b&#39;,&#39;c&#39;,&#39;d&#39;]
print(&#39;&#39;,join(arr))
#실행결과
abcd</p>
<blockquote>
<p>join 함수를 쓰면 리스트를 문자열로 변환할 수 있다.
추가로 ,&#39;&#39;사이에 어떤 기호를 넣어주면 리스트의 각 요소와 요소 사이에 추가된다.</p>
</blockquote>
<p>print(&#39;/&#39;,join(arr))
#실행결과
a/b/c/d</p>
<ul>
<li>&#39;&#39;.join(리스트)&#39;&#39;.join(리스트)를 이용하면 매개변수로 들어온 [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;] 이런 식의 리스트를 &#39;abc&#39;의 문자열로 합쳐서 반환해주는 함수인 것입니다.</li>
<li>&#39;구분자&#39;.join(리스트)&#39;구분자&#39;.join(리스트)를 이용하면 리스트의 값과 값 사이에 &#39;구분자&#39;에 들어온 구분자를 넣어서 하나의 문자열로 합쳐줍니다.&#39;_&#39;.join([&#39;a&#39;, &#39;b&#39;, &#39;c&#39;]) 라 하면 &quot;a_b_c&quot; 와 같은 형태로 문자열을 만들어서 반환해 줍니다.</li>
</ul>
<br>
<br>

<h3 id="구현-시뮬레이션-및-완탐-에서는-2차원-공간에서의-방향벡터가-자주-활용">구현, 시뮬레이션 및 완탐 에서는 2차원 공간에서의 방향벡터가 자주 활용</h3>
<pre><code class="language-python">

#동,북,서,남
dx=[0,-1,0,1]
dy=[1,0,-1,0]

#현재위치
x,y=2,2

for i in range(~):
    nx=x+dx[i]
    ny=y+dy[i]
#위치이동
x,y=nx,ny

##또는 방향벡터정의를 steps=[(-2,-1),(-1,-2),(2,-1)...]이렇게도 가능하다</code></pre>
<h3 id="코테에서-알아두면-좋은-기본-함수">코테에서 알아두면 좋은 기본 함수</h3>
<ul>
<li>enumerate() : 반복문에서 해당 함수를 이용하면, 리스트의 원소와 인덱스를 동시에 사용할 수 있다.
ex) for index, element in enumerate(리스트):</li>
</ul>
<br>

<ul>
<li>zip() : 매개변수로 받은 배열들을 같은 index 끼리 매칭하여 묶어준다.
ex) list(zip([1,2,3],[a,b,c])) #result : [(1,a),(2,b),(3,c)]</li>
</ul>
<br>

<ul>
<li>filter(): list에서 조건에 맞는 원소만 추출한다.
ex) list(filter(lambda x:x&gt;=n, 배열))</li>
</ul>
<br>

<ul>
<li><p>cmp_to_keys() : 비교함수 return 값이 1이면 a를 앞으로, -1이면 b를 앞으로(같으면0)</p>
<p>ex) import functools
배열.sort(key=functools.cmp_to_key(비교함수))</p>
<p> def 비교함수 (a,b):</p>
<pre><code>return a-b</code></pre></li>
</ul>
<br>

<ul>
<li>파이썬에서의 set은 집합을 의미하고 집합의 특성을 활용하여, 중복제거 등 유용한 작업을 수행 가능하다.</li>
</ul>
<blockquote>
<p>주의할점 : 집합을 만들고 나면 출력 형태는 {} 이지만, 선언 시 a={}라고 하면 안됨. 이는 {} 이 자체론 dictionary를 의미하기 때문이다.</p>
</blockquote>
<ul>
<li>리스트에서 중복을 삭제하고 싶을땐 set(리스트)</li>
</ul>
<br>

<ul>
<li>집합에 원소를 추가하거나 제거하는 작업 <ul>
<li>집합.add(원소) # 한 개 추가</li>
<li>집합.update(배열/집합) # 여러 개 추가</li>
<li>집합.remove(원소) # 하나 삭제(원소가 없으면 KeyError)</li>
<li>집합.discard(원소) # 하나 삭제(원소가 없어도 에러 발생x)</li>
</ul>
</li>
</ul>
<br>
<br>

<ul>
<li>keys() : 딕셔너리의 모든 키를 반환. list() 함수를 사용하여 리스트로 변환 가능</li>
<li>values() : 딕셔너리의 모든 값을 반환. list() 함수를 사용하여 리스트로 변환 가능</li>
</ul>
<br>

<ul>
<li><p>Counter 객체는 덧셈 뺄셈 가능
counter1 = Counter([&quot;A&quot;, &quot;A&quot;, &quot;B&quot;])
counter2 = Counter([&quot;A&quot;, &quot;B&quot;, &quot;B&quot;])</p>
<p>counter1 + counter2
Counter({&#39;A&#39;: 3, &#39;B&#39;: 3})</p>
<ul>
<li>뺄셈
counter1 - counter2
Counter({&#39;A&#39;: 1})
단, 뺄샘의 결과로 0이나 음수가 나온 경우에는 최종 카운터 객체에서 제외가 되니 이 부분 주의해서 사용해야 한다.</li>
</ul>
</li>
<li><p>2개의 딕셔너리를 합치는 방법</p>
<ul>
<li>key값이 겹치지 않을 때 : 딕셔너리1.update(딕셔너리2)- 만약 겹친다면 중복된 기존 key값은 update의 인수로 넣어준 값으로 대체됨</li>
<li>key값이 겹칠 때 : Counter객체로 딕셔너리 감싸고 더하기 연산 </li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DB] Lock & Elastic Search]]></title>
            <link>https://velog.io/@ryu-backend/DB-Lock-Elastic-Search</link>
            <guid>https://velog.io/@ryu-backend/DB-Lock-Elastic-Search</guid>
            <pubDate>Fri, 15 Mar 2024 09:04:32 GMT</pubDate>
            <description><![CDATA[<h2 id="db-lock">DB Lock</h2>
<blockquote>
<p>DB Lock은 트랜잭션 처리의 순차성을 보장하기 위한 기법</p>
</blockquote>
<p>-공유락(LS) Read Lock라고도 하는 공유락은 트랜잭션이 읽기를 할 때 사용하는 락이며, 데이터를 읽기만하기 때문에 같은 공유락 끼리는 동시에 접근 가능</p>
<blockquote>
</blockquote>
<p>-배타락(LX)Write Lock라고도 하는 베타락은 데이터를 변경할 때 사용하는 락이며, 트랜잭션이 완료될 때까지 유지되며, 베타락이 끝나기 전까지 어떠한 접근도 허용x</p>
<br>
<br>

<h2 id="elastic-search">Elastic Search</h2>
<blockquote>
<p>쉽게말해서 Elastic Search는 프로그램으로서의 검색엔진이다.(현재 가지고 있는 데이터 중에서 특정한 데이터를 검색할 수 있는 기능을 제공) </p>
</blockquote>
<br>

<h3 id="⭐-검색엔진의-핵심-개념indexinverted-index">⭐ 검색엔진의 핵심 개념(Index,Inverted-Index)</h3>
<ul>
<li>Index : 특정한 데이터가 어느 위치에 있는지 미리 저장해두어 검색 시에 빠른 속도로 찾을 수 있는것 (ex. 책의 목차)</li>
<li>Inverted-Index : 키워드를 통해 문서를 찾아내는 방식. 검색성능이 매우 빠르다. </li>
</ul>
<br>
<img src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F6274453b-d62e-4bff-bfab-44a524084751%2FUntitled.png?table=block&id=e5cf7d99-dc2e-464d-9c80-bb61d11b5759&spaceId=b453bd85-cb15-44b5-bf2e-580aeda8074e&width=2000&userId=80352c12-65a4-4562-9a36-2179ed0dfffb&cache=v2">

<ul>
<li><p>위 그림은 색인을 사용한 검색이다.</p>
</li>
<li><p>일반적으로 위의 테이블 데이터에서 한줄씩 like %fox 검색을 통해 데이터를 검색</p>
</li>
<li><p>하지만 이렇게 작은 데이터셋에서 검색을 할 때는 상관이 없지만 , 만일 매우 많은 데이터가 존재한다고 가정하면 검색을 위해서 우리의 검색엔진은 1번부터 순서대로 끝까지 검색해서 최악의 경우 엄청난 속도지연이 발생 --&gt; 이러한 현상을 해결하기 위해 역색인(Inverted-Index)필요</p>
<br>
<img src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F82f3e885-265b-44dc-8e27-85fe77bcfcf8%2FUntitled.png?table=block&id=8f89ff20-33cf-4896-91de-d3b74c1eedd7&spaceId=b453bd85-cb15-44b5-bf2e-580aeda8074e&width=2000&userId=80352c12-65a4-4562-9a36-2179ed0dfffb&cache=v2">
</li>
<li><p>위 그림은 역색인을 사용한 검색이다.</p>
</li>
<li><p>역색인을 사용하는 검색 엔진에서는 추출된 각 키워드를 term이라고 한다.</p>
</li>
<li><p>이렇게 역색인이 있으면, fox를 포함하고 있는 문서의 id를 바로 얻어올 수 있다.</p>
<ul>
<li>문서의 id는 PK, 물리적 주소등에 해당됨</li>
</ul>
</li>
<li><p>역색인을 사용하면 데이터가 늘어나도 찾아가야 할 행이 늘어나는 것이 아니라 역색인이 가르키는 id의 배열 값이 추가되는 것이므로 큰 속도의 저하 없이 여전히 빠른 속도로 검색 가능</p>
</li>
</ul>
<br>
<br>

<h3 id="db가-있는데-굳이-검색엔진">DB가 있는데 굳이 검색엔진?</h3>
<ul>
<li><p>관계형 데이터베이스는 단순 텍스트 매칭에 대한 검색만을 제공</p>
<ul>
<li>요즘 최신 버전에서는 N-GRAM 기반의 FULL-TEXT 검색을 지원하지만, 한글 검색의 경우에 아직 많이 빈약함</li>
</ul>
</li>
<li><p>텍스트를 여러 단어로 변형하거나 텍스트의 특질을 이용한 동의어나 유의어를 활용한 검색 가능</p>
</li>
<li><p>Elastic Search에서는 RDBMS에서 불가능한 비정형 데이터의 색인과 검색이 가능</p>
</li>
<li><p>역색인 지원으로 매우 빠른 검색이 가능 </p>
</li>
<li><p>요즘은 Elastic Search가 제공하는 강력하는 기능을 활용하기 위해 DB에 저장된 데이터 중 검색 및 분석하고 싶은 DATA만 Elastic Search로 Indexing하고, Kibana를 활용해 분석 및 reporting을 시각화 하는것이 추세. 이를 위해 RDBMS에 저장된 DATA와 Elastic Search로 Indexing한 DATA의 동기화를 유지하는것이 검색기능을 활용하기 위한 필수 요건.</p>
<br>
<br>


</li>
</ul>
<h2 id="💡-요약">💡 요약</h2>
<blockquote>
<p>RDBMS와 Elastic Search는 사용하는 목적성이 다름</p>
</blockquote>
<p> RDBMS는 어플리케이션에서 데이터 저장 용도 및 간단한 데이터 조회 용도로 사용</p>
<blockquote>
</blockquote>
<p> Elastic Search는 검색 시스템 트래픽이 늘어나고 요구사항이 늘어남에 따라 이는 RDBMS로 커버하기 힘들기에 커버하기 위한 검색에 특화된 해결 시스템</p>
<blockquote>
</blockquote>
<p> RDBMS로 쌓인 많은 데이터는 검색을 좀 더 효과적으로 하기 위한 데이터 구조로 변형하여 Elastic Search와 같은 검색 엔진에 저장. 이를 보통 데이터 파이프라인이라고 부름.</p>
 <br>
 <br>

<p> 참고 : <a href="https://velog.io/@jakeseo_me/%EC%97%98%EB%9D%BC%EC%8A%A4%ED%8B%B1%EC%84%9C%EC%B9%98-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-1-%EC%97%98%EB%9D%BC%EC%8A%A4%ED%8B%B1%EC%84%9C%EC%B9%98%EB%8A%94-%EA%B2%80%EC%83%89%EC%97%94%EC%A7%84%EC%9D%B4%EB%8B%A4">https://velog.io/@jakeseo_me/%EC%97%98%EB%9D%BC%EC%8A%A4%ED%8B%B1%EC%84%9C%EC%B9%98-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-1-%EC%97%98%EB%9D%BC%EC%8A%A4%ED%8B%B1%EC%84%9C%EC%B9%98%EB%8A%94-%EA%B2%80%EC%83%89%EC%97%94%EC%A7%84%EC%9D%B4%EB%8B%A4</a> </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DB] Trigger, Index에 대하여 ]]></title>
            <link>https://velog.io/@ryu-backend/DB-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EA%B0%9C%EB%A1%A0%EB%82%B4-%EA%B8%B0%EC%A4%80-%EC%83%9D%EC%86%8C%ED%95%9C-%EA%B0%9C%EB%85%90%EB%93%A4</link>
            <guid>https://velog.io/@ryu-backend/DB-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EA%B0%9C%EB%A1%A0%EB%82%B4-%EA%B8%B0%EC%A4%80-%EC%83%9D%EC%86%8C%ED%95%9C-%EA%B0%9C%EB%85%90%EB%93%A4</guid>
            <pubDate>Wed, 13 Mar 2024 08:38:39 GMT</pubDate>
            <description><![CDATA[<h3 id="trigger">Trigger</h3>
<ul>
<li>트리거는 특정 테이블에 대한 이벤트에 반응해 INSERT, DELETE, UPDATE와 같은 DML 이 실행되었을 때, DB에서 자동으로 동작하도록 작성된 프로그램</li>
<li>사용자가 직접 호출하는 것이 아닌, 데이터베이스에서 자동적으로 호출한다는 것이 큰 장점</li>
</ul>
<br>

<h3 id="index">Index</h3>
<ul>
<li>Index란 table을 처음부터 끝까지 검색하는 방법인 FTS와는 달리 인덱스를 검색하여 해당 자료의 테이블을 액세스 하는 방법 &amp; 추가적인 쓰기 작업과 저장 공간을 활용하여 DB Table의 검색속도를 향상시키기 위한 자료구조</li>
<li>EX) DB를 책으로 비유하면 DATA는 책의 내용, DATA가 저장된 RECORD의 주소는 INDEX 목록에 있는 페이지 번호</li>
<li>인덱스는 항상 정렬된 상태를 유지하기 때문에 원하는 값을 검색하는데 빠름, 하지만 새로운 값을 추가하거나 삭제, 수정하는 경우에는 쿼리문 실행 속도 느려짐</li>
<li>즉, 인덱스는 데이터의 저장 성능을 희생하고 그대신 데이터의 검색 속도를 높이는 기능<br>

</li>
</ul>
<p>❓ DBMS의 Index 관리 기법</p>
<ul>
<li><strong>해시 테이블</strong><ul>
<li>컬럼의 값으로 생성된 해시를 기반으로 인덱스를 구현 , (Key,Value)로 데이터를 저장하는 자료구조 중 하나로 빠른 데이터 검색이 필요할 때 유용</li>
<li>시간복잡도는 O(1)이지만 해시테이블이 사용되는 경우는 제한적이다. 등호연산에만 특화되었기 때문에 부등호 연산이 자주 사용되는 DB 검색을 위해서는 적합X</li>
<li>위와 같은 이유로 DB의 인덱스에서는 B+Tree가 일반적으로 사용됨</li>
</ul>
</li>
</ul>
<br>

<ul>
<li><p><strong>B+Tree</strong></p>
<ul>
<li><p>DB의 인덱스를 위해 자식 노드가 2개 이상인 B-Tree를 개선시킨 자료구조.</p>
</li>
<li><p>리프노드(데이터노드)만 인덱스와 함께 데이터(Value)를 가지고 있고, 나머지 노드(인덱스노드)들은 데이터를 위한 인덱스(Key)만을 갖는다</p>
</li>
<li><p>리프노드들은 LinkedList로 연결되어 있다.</p>
</li>
<li><p>데이터 노드 크기는 인덱스 노드의 크기와 달라도 상관없음</p>
</li>
<li><p>데이터베이스의 인덱스 컬럼은 부등호를 이용한 순차 검색 연산이 자주 발생될 수 있다. 이러한 이유로 BTree의 리프노드들을 LinkedList로 연결하여 순차검색을 용이하게 하는 등 BTree를 인덱스에 맞게 최적화함</p>
</li>
<li><p>비록 B+Tree는 O(log2n)의 시간복잡도를 갖지만 해시테이블보다 인덱싱에 더욱 적합한 자료구조가 되었다.</p>
<br>
<br>
<br>

</li>
</ul>
</li>
</ul>
<p>참고자료 : <a href="https://mangkyu.tistory.com/96">https://mangkyu.tistory.com/96</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DB] 제 52회 SQLD 응시]]></title>
            <link>https://velog.io/@ryu-backend/%EC%A0%9C-52%ED%9A%8C-SQLD-%EC%9D%91%EC%8B%9C</link>
            <guid>https://velog.io/@ryu-backend/%EC%A0%9C-52%ED%9A%8C-SQLD-%EC%9D%91%EC%8B%9C</guid>
            <pubDate>Sun, 10 Mar 2024 07:23:47 GMT</pubDate>
            <description><![CDATA[<h2 id="sqld-응시-후기">SQLD 응시 후기</h2>
<blockquote>
<p>SQL 개발자(SQLD*, SQL Developer)란 데이터베이스와 데이터 모델링에 대한 지식을 바탕으로 응용 소프트웨어를 개발하면서 데이터를 조작하고 추출하는데 있어서 정확하고 최적의 성능을 발휘하는 SQL을 작성할 수 있는 개발자를 말한다.
<br></p>
</blockquote>
<p> 전체적으로 기본개념들을 살짝씩 꼬아서 내고 SQL 해석하는 문장은 그닥 어렵진 않았던것같다. 특히 윈도우함수와 그룹함수에 대한 깊은 이해가 필요했다.
 <br>
 공부는 김앤북의 내일은 SQL 개발자로 개념을 다지고 유명한 노랭이 책으로 기출문제풀이를 하고 오답노트를 작성하며 헷갈리는 개념과 더 공부가 필요한 개념들을 파일로 작성해 출력해서 시험 직전에 고사장에서 봤다.</p>
<p> <a href="https://drive.google.com/file/d/1Z0Bnh-L9wVCZO7VimWQxBy9-Mr57cF1u/view?usp=drive_link">오답노트 파일 &lt;-- click!</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Network] JWT에 대하여(+쿠키,세션)]]></title>
            <link>https://velog.io/@ryu-backend/JWT%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC%EC%BF%A0%ED%82%A4%EC%84%B8%EC%85%98</link>
            <guid>https://velog.io/@ryu-backend/JWT%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC%EC%BF%A0%ED%82%A4%EC%84%B8%EC%85%98</guid>
            <pubDate>Thu, 29 Feb 2024 08:26:24 GMT</pubDate>
            <description><![CDATA[<h2 id="jwt를-들어가기전--쿠키세션이란">JWT를 들어가기전 , 쿠키&amp;세션이란</h2>
<br>
<br>


<h1 id="쿠키">쿠키</h1>
<blockquote>
<p> 서버가 클라이언트를 인증을 확인하는 방식은 대표적으로 쿠키, 세션, 토큰 3가지가 있다.
 쿠키는 key = value 형식의 문자열 데이터 묶음이며 브라우저는 이 문자열 데이터 조각들을 저장해 놓았다가 동일한 서버에 재요청할 때 쿠키 데이터를 전송한다. 쿠키는 주로 세션관리, 개인화, 트래킹에 사용된다.</p>
</blockquote>
<h3 id="👀-쿠키-인증-방식">👀 쿠키 인증 방식</h3>
<p><img src="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCIAv6%2FbtsdFfxN0LE%2FTolCFnU87P9RIZw32O8Tn1%2Fimg.png" alt="">
<br></p>
<p>단점 :  쿠키 인증방식은 보안에 취약하다! 요청시 쿠키의 값을 그대로 보내기 때문이다. </p>
 <br>
<br>



<h1 id="세션">세션</h1>
<blockquote>
<p>위와 같은 보안 위험을 해결하기 위해서 세션인증 추가(중요정보는 서버측에서 관리하는 방식) 클라이언트가 서버의 요청을 보낼 때, 서버는 저장해둔 세션id를 활용한다. 흔히 아는 로그인할때 요청이 성공하면 서버는 세션id를 서버에 저장하고 클라이언트는 브라우저의 세션id를 저장한다. 이후 다음 요청이 서버로 들어올 때 마다 쿠키의 세션 id의 유효성 검사를 하는것이다.</p>
</blockquote>
<h3 id="❗-헷갈리지-말기">❗ 헷갈리지 말기</h3>
<ul>
<li>클라이언트는 세션 id를 쿠키를 통해서 기억</li>
<li>브라우저에 세션id를 저장해둔 클라이언트는 다음요청때마다 헤더의 cookie에 세션id를 담아서 전송</li>
<li>서버는 클라이언트가 보낸 요청의 쿠키에 담긴 세션id와, 세션 스토리지에 담긴 세션id를 대조해 인증을 거침.(세션은 쿠키를 기반으로 한다.)</li>
</ul>
<h3 id="👀-세션-인증-방식">👀 세션 인증 방식</h3>
<p><img src="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIxzBc%2FbtsdIofTKZc%2FNJgyhajYWf2xavKxok7uWk%2Fimg.png" alt=""></p>
<p> 단점 : 서버에서 세션 저장소를 사용해서 대용량 트래픽일시 서버에 부하가 심해진다.
<br>
<br></p>
<h1 id="토큰jwt">토큰(JWT)</h1>
<blockquote>
<p>기존의 세션 인증은 서버가 db에 세션정보를 가지고 있어야 되서 오버헤드등 문제점이 있었는데 토큰은 클라이언트에 저장되기 때문에 서버의 부담을 줄인다. JWT는 인증에 필요한 정보들을 암호화시킨 JSON 토큰이며 JWT 토큰을 HTTP 헤더에 실어 서버가 클라이언트를 식별하는 방법이다. Header + Payload + Signature로 구성된다.</p>
</blockquote>
<br>

<h3 id="👀-토큰-인증-방식jwt">👀 토큰 인증 방식(JWT)</h3>
<p><img src="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcyjhpE%2FbtsdQOrOohR%2FIPZfZMzk9KcoGQYfRk4G7K%2Fimg.png" alt=""></p>
<p> 하지만 이마저도 보안의 위험이 있기 때문에 토큰을 AccessToken, RefreshToken을 나누어 각각 유효기간을 다르게 해 피해를 최소화 한다. </p>
<br>

<h3 id="👀-토큰-인증-방식jwt---개선">👀 토큰 인증 방식(JWT) - 개선</h3>
<p><img src="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoicEl%2FbtsdPeEPU16%2FdCk5sJA6GHYFSS6rgiukk1%2Fimg.png" alt=""></p>
<br>

<h2 id="⭐-총정리">⭐ 총정리</h2>
<p> 쿠키 &amp; 세션</p>
<ul>
<li><p>장점 : cookie만 사용하는 거보다 보안 향상, 서버쪽에서 session 통제가능, 네트워크 부하 낮음</p>
</li>
<li><p>단점 : 세션 저장소 사용으로 인한 서버 부하</p>
<p>JWT</p>
<ul>
<li>장점 : 인증을 위한 별도의 저장소가 필요X , 별도의 I/0 작업없는 빠른 인증처리, 확장성이 좋음</li>
<li>단점 : 토큰의 길이가 늘어나면 늘어날수록 네트워크 부하, 특정 토큰을 강제로 만료시키기 힘듬.</li>
</ul>
</li>
</ul>
<br>
<br>

<p>참고 : <a href="https://jhbljs92.tistory.com/entry/1-JWT-%ED%86%A0%ED%81%B0-%EC%9D%B8%EC%A6%9D%EA%B3%BC-%EC%BF%A0%ED%82%A4-%EC%84%B8%EC%85%98-%ED%86%A0%ED%81%B0">https://jhbljs92.tistory.com/entry/1-JWT-%ED%86%A0%ED%81%B0-%EC%9D%B8%EC%A6%9D%EA%B3%BC-%EC%BF%A0%ED%82%A4-%EC%84%B8%EC%85%98-%ED%86%A0%ED%81%B0</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[IntelliJ] IntelliJ 'Cannot resolve symbol * ' 에러 ]]></title>
            <link>https://velog.io/@ryu-backend/IntelliJ-Cannot-resolve-symbol-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@ryu-backend/IntelliJ-Cannot-resolve-symbol-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Sat, 24 Feb 2024 14:11:21 GMT</pubDate>
            <description><![CDATA[<p>예전에 했던 프로젝트의 코드를 볼일이 생겨서 오랜만에 들어가봤더니 온통 &quot;Cannot resolve symbol.. &quot; 이런 에러가 무수히 나왔다.</p>
<p>해결 방법은 다음과 같다.</p>
<ul>
<li>Gradle Refresh 실행 (나는 이걸로 해결했다.)</li>
<li>캐시를 지우고 다시 실행</li>
<li>빌드를 다시한다.</li>
<li>Gradle Build 설정을 IntelliJ IDEA로 바꾼다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Java -Optional API]]></title>
            <link>https://velog.io/@ryu-backend/Java-Optional-API</link>
            <guid>https://velog.io/@ryu-backend/Java-Optional-API</guid>
            <pubDate>Mon, 12 Feb 2024 09:18:01 GMT</pubDate>
            <description><![CDATA[<h2 id="optional-api">Optional API</h2>
<blockquote>
<p>개발할때 가장 많이 발생하는 예외 중 하나가 NullPointerException(NPE)이다.
이를 피하려면 NULL 여부 검사를 필수적으로 해야하는데 만약 NULL 검사를 해야하는 변수가 많은 경우 가독성이 떨어진다. 하지만 Java8에서 부터 Optional&lt;<T>T&gt;를 제공하여 NPE를 방지할수 있도록 도와주고 Optional 클래스의 메소드를 통해 NULL을 컨트롤 할 수 있다.</p>
</blockquote>
<br>



<pre><code class="language-java">import java.util.Optional;

public class Main {
    public static void main(String[] args) {
        // 예시로 사용할 객체
        String nullableString = null;

        // 기존의 null 체크 방식
        if (nullableString != null) {
            System.out.println(&quot;Length: &quot; + nullableString.length());
        } else {
            System.out.println(&quot;Nullable string is null.&quot;);
        }

        // Optional을 사용한 null 체크
        Optional&lt;String&gt; optionalString = Optional.ofNullable(nullableString);
        if (optionalString.isPresent()) {
            System.out.println(&quot;Length: &quot; + optionalString.get().length());
        } else {
            System.out.println(&quot;Nullable string is null.&quot;);
        }

        // 람다식과 Optional을 이용한 null 체크와 처리
        optionalString.ifPresentOrElse(
            str -&gt; System.out.println(&quot;Length: &quot; + str.length()),
            () -&gt; System.out.println(&quot;Nullable string is null.&quot;)
        );
    }
}</code></pre>
<ul>
<li>null인지 아닌지 확실하지 않을 때 Optional.ofNullable() 메소드를 사용하여 Nullable 객체를 Optional 객체로 변환하고, isPresent() 메소드로 null 여부를 확인한다.</li>
<li>ifPresent() 메소드를 사용하여 값이 존재할 때만 동작하도록 처리할 수 있다.</li>
<li>Optional.ofNullable() 메소드는 주어진 값이 null이 아니면 해당 값을 갖는 Optional 객체를 생성하고, 값이 null이면 빈 Optional 객체를 생성한다. 이 메소드는 주로 Nullable한 객체를 Optional 객체로 변환할 때 사용된다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Java -try-with-resources]]></title>
            <link>https://velog.io/@ryu-backend/CS-4-try-with-resources</link>
            <guid>https://velog.io/@ryu-backend/CS-4-try-with-resources</guid>
            <pubDate>Mon, 12 Feb 2024 08:13:56 GMT</pubDate>
            <description><![CDATA[<h2 id="try-with-resources">try-with-resources</h2>
<blockquote>
<ul>
<li>try-with-resources는 try-catch-finally의 문제점(<strong>가독성, 실수 가능성, 자원누수</strong>)을 보완하기 위해 나온 개념</li>
</ul>
</blockquote>
<ul>
<li><p>try( ... ) 안에 자원 객체를 전달하면, try 블록이 끝나고 자동으로 자원 해제 해주는 기능</p>
</li>
<li><p>따로 finally 구문이나 모든 catch 구문에 종료 처리를 하지 않아도 되는 장점</p>
</li>
<li><p>Java에서 외부 자원에 접근하는 경우 외부자원을 사용한 뒤 제대로 자원을 닫아줘야 한다. 자원을 닫을 때 try-catch-finally 보다 try-with-resources 구문을 사용하면 코드의 가독성이 더 증가한다.</p>
</li>
</ul>
<br>

<h3 id="try-catch-finally로-자원해제">try-catch-finally로 자원해제</h3>
<pre><code class="language-java">FileOutputStream out = null;       
try {            
    out = new FileOutputStream(&quot;exFile.txt&quot;);           
    // ... 이후 입출력 로직 처리 ...        
}catch (FileNotFoundException e) {           
e.printStackTrace();        
}finally {            
    if(out != null) { //스트림이 null인지 체크                
        try {                    
            out.close();                
        }catch (IOException e) {                    
            e.printStackTrace();                
        }            
    }        
}
</code></pre>
<br>

<h3 id="try-with-resources로-자원-쉽게-해제">try-with-resources로 자원 쉽게 해제</h3>
<pre><code class="language-java">try(FileOutputStream out = new FileOutputStream(&quot;exFile.txt&quot;)) {
    // ...이후 입출력 로직 처리...
}catch (IOException e) {
    e.printStackTrace();
} //실행 결과는 1번째 코드예시와 동일</code></pre>
<ul>
<li>try(...)에 자원 객체를 전달하면, try 코드 블록이 끝나고 자동으로 자원을 해제한다. 즉, 따로 finally 구문이나 모든 catch 구문에 종료처리를 하지 않아도 된다.</li>
</ul>
<ul>
<li>try-with-resources를 사용하기 위해서는(try에 전달할 수 있는 자원은) close() 를 정의하기 위한 AutoCloseable 인터페이스를 구현해야한다. cf) 복수의 자원객체도 가능하다.</li>
</ul>
<ul>
<li>AutoCloseable 인터페이스를 구현한 대표적인 클래스(따로 구현X)<pre><code>   - FileInputStream / FileOutputStream: 파일 입출력에 사용됩니다.
   - BufferedReader / BufferedWriter: 버퍼링된 문자 입력 및 출력에 사용됩니다.
  - Scanner: 입력 스트림에서 텍스트를 읽는 데 사용됩니다.
  - Socket / ServerSocket: 네트워크 통신에 사용됩니다.
  - ZipInputStream / ZipOutputStream: ZIP 파일의 압축 해제 및 압축에 사용됩니다.
  - ObjectInputStream / ObjectOutputStream: 객체의 직렬화 및 역직렬화에 사용됩니다.</code></pre></li>
</ul>
<br>
<br>

<h3 id="autocloseable-인터페이스-구현-예제">AutoCloseable 인터페이스 구현 예제</h3>
<ul>
<li><p>AutoCloseable 인터페이스를 구현하는 클래스에서는 close() 메서드만 오버라이딩하면 된다.(자원해제시 사용하는 메서드)</p>
<pre><code class="language-java">public class MyResource implements AutoCloseable {
  // 다른 멤버 변수, 생성자, 메서드 등...

  @Override
  public void close() throws Exception {
      // 자원 해제 코드
  }
}
public class Main {
  public static void main(String[] args) {
      try (MyResource resource = new MyResource()) {
          System.out.println(&quot;Inside try block&quot;);
      } catch (Exception e) {
          System.out.println(&quot;Exception caught: &quot; + e);
      }
  }
}/*실행결과 : Inside try block 
           MyResource closed */
</code></pre>
</li>
</ul>
<pre><code>
**참조 : ** https://dev-coco.tistory.com/20</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Java -Error vs Exception (throw&throws)]]></title>
            <link>https://velog.io/@ryu-backend/CS-3-Error-vs-Exception-throwthrows</link>
            <guid>https://velog.io/@ryu-backend/CS-3-Error-vs-Exception-throwthrows</guid>
            <pubDate>Fri, 09 Feb 2024 09:31:40 GMT</pubDate>
            <description><![CDATA[<h2 id="error--exception">Error &amp; Exception</h2>
<br>

<blockquote>
<ul>
<li>Error : 일반적으로 프로그래머가 처리할 수 없는 심각한 오류 , 컴파일 시점에 체크 불가능, 발생하면 프로그램은 비정상 종료되며 예측 불가능한 UncheckedException에 속한다 ex) 메모리 부족, 스택 오버플로우 등 <br>
</li>
</ul>
</blockquote>
<ul>
<li>Exception : Error보다 비교적 경미한 오류이며, try-catch를 이용해 프로그램의 비정상 종료를 막을 수 있다.</li>
</ul>
<br>

<h3 id="checkedexception-vs-uncheckedexception">CheckedException vs UncheckedException</h3>
<ul>
<li><p>CheckedException은 실행하기 전에 예측 가능한 예외를 말하고, 반드시 예외 처리를 해야한다. 컴파일러가 체크하는 예외이기 떄문에 반드시 예외 처리해야한다.
ex) IOException , ClassNotFoundException</p>
</li>
<li><p>UncheckedException은 실행하고 난 후에 알 수 있는 예외를 마하고, 따로 예외처리를 하지않아도 된다. 주로 프로그래머의 실수나 프로그램의 오류로 발생하는 예외이므로 예기치 않은 상황에서 발생 가능하며, 코드 상에서 사전에 예측하기 어렵다. 
ex)  NullPointerException, ArrayIndexOutOfBoundException 등</p>
</li>
</ul>
<br>
<br>

<h3 id="throw-vs-throws">throw vs throws</h3>
<ul>
<li><p>throw는 예외를 직접 발생시키는 데 사용됩니다.
메소드나 블록 내에서 예외를 생성하고 해당 예외를 호출자에게 던질 때 사용됩니다.
일반적으로 사용자가 직접 예외를 생성하고 던질 때, 또는 특정 조건이 충족될 때 예외를 발생시키는 데 사용됩니다.</p>
</li>
<li><p>throws는 메소드 선언부에서 사용되며, 해당 메소드가 어떤 예외를 던질 수 있는지 명시합니다.
메소드가 특정 예외를 발생시킬 수 있다는 것을 호출자에게 알리는 역할을 합니다.
메소드에 throws를 사용하면 해당 메소드를 호출할 때 예외 처리를 강제하게 됩니다. 호출하는 쪽에서 예외 처리를 해야 합니다.</p>
</li>
</ul>
<pre><code class="language-java">package study; 
public class MyException extends Exception { 

    public MyException() {        
        super(&quot;내가 만든 예외&quot;);    
    }
}
</code></pre>
<pre><code class="language-java">package study; 
public class ExceptionTest {     
    static void callException() throws MyException {    
        throw new MyException();    
    }    

    public static void main(String[] args) {   
        try {            
            callException();        
        }catch (MyException e) {     
            System.out.println(e.getMessage());     
        }finally {        
            System.out.println(&quot;시스템 종료&quot;);        
        }    
    }
}

실행결과 : 내가 만든 예외
          시스템 종료
          Process finished with exit code 0


//코드출처: https://dev-coco.tistory.com/18 [슬기로운 개발생활:티스토리]</code></pre>
]]></description>
        </item>
    </channel>
</rss>