<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>fraise_kim.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Fri, 18 Aug 2023 13:32:05 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>fraise_kim.log</title>
            <url>https://velog.velcdn.com/images/kk_y_oo1/profile/2c6e39f8-8c90-4861-a051-2c379b247774/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. fraise_kim.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/kk_y_oo1" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Docker 설치]]></title>
            <link>https://velog.io/@kk_y_oo1/Docker-%EC%84%A4%EC%B9%98</link>
            <guid>https://velog.io/@kk_y_oo1/Docker-%EC%84%A4%EC%B9%98</guid>
            <pubDate>Fri, 18 Aug 2023 13:32:05 GMT</pubDate>
            <description><![CDATA[<h2 id="docker-설치">Docker 설치</h2>
<br>

<p>1️⃣  <strong>도커 허브(<a href="https://hub.docker.com">https://hub.docker.com</a>)</strong>에 가서 회원가입하고 로그인
2️⃣  Repository 클릭 - 새로운 Repository 생성 
<img src="https://velog.velcdn.com/images/kk_y_oo1/post/002ced23-a7fd-43a3-9e2e-92d66962b0f5/image.png" alt=""></p>
<p>3️⃣  Repository 생성 후 아래 사이트에서 각자 OS에 맞는 도커를 설치하면 된다.
<img src="https://velog.velcdn.com/images/kk_y_oo1/post/624055df-6a1a-429b-bd37-8fbe98d37c92/image.png" alt=""></p>
<p>cf)  도커를 설치하면 시스템 상태에 따라 재시작 해야 할 수도 있으니 close 버튼 누르기 전에 잘 저장해 놓기. </p>
<br>

<p>4️⃣ 터미널에서 아래 코드를 입력하고 localhost로 접속하면 잘 실행 된다.</p>
<pre><code class="language-java">docker run -it --rm -p 80:80 docker/getting-started</code></pre>
<p><img src="https://velog.velcdn.com/images/kk_y_oo1/post/d619abb2-b936-4bd8-a824-a49b0f1dc68f/image.png" alt="">
<img src="https://velog.velcdn.com/images/kk_y_oo1/post/ac559583-7b54-47db-907a-5c7f6242a0ad/image.png" alt=""></p>
<ul>
<li><strong>docker -v</strong>로 설치 확인 가능</li>
</ul>
<br>

<hr>
<br>

<h2 id="도커를-이용한-mysql-개발-환경-구성">도커를 이용한 MySQL 개발 환경 구성</h2>
<ul>
<li><p>명령어를 터미널에 입력하고 뒤에 원하는 버전을 입력하면 된다.</p>
</li>
<li><p>ex)</p>
<pre><code>docker pull mysql:latest</code></pre><p><img src="https://velog.velcdn.com/images/kk_y_oo1/post/5f157c73-5ab9-461d-bc9e-6f0ac1c69033/image.png" alt=""></p>
</li>
<li><p>MySQL 도커 컨테이너 생성 및 실행</p>
<pre><code>docker run --name mysql-sample-container -e
MYSQL_ROOT_PASSWORD=&lt;password&gt; -d -p 3306:3306 mysql:
{version}</code></pre></li>
<li><p>현재 실행중인 도커 컨테이너 목록 출력</p>
<pre><code>docker ps -a</code></pre></li>
<li><p>MySQL 도커 컨테이너 접속</p>
<pre><code>docker exec -it {도커 컨테이너 이름} bash</code></pre></li>
<li><p>MySQL 접속</p>
<pre><code>mysql -u root -p</code></pre></li>
</ul>
<br>

<p>⛔️ <strong>에러 1</strong></p>
<blockquote>
<p><strong>docker: Cannot connect to the Docker daemon at unix:///Users/yeowonkim/.docker/run/docker.sock. Is the docker daemon running?.</strong></p>
</blockquote>
<ul>
<li>원인 : 도커가 실행되지 않고 있어 생기는 문제</li>
<li>해결 : 위에 설명했던 도커 실행을 명령하면 된다.<pre><code class="language-java">docker run -it --rm -p 80:80 docker/getting-started</code></pre>
</li>
</ul>
<br>

<p>⛔️ <strong>에러 2</strong></p>
<blockquote>
<p><strong>docker: Error response from daemon: Ports are not available: exposing port TCP 0.0.0.0:3306 -&gt; 0.0.0.0:0: listen tcp 0.0.0.0:3306: bind: address already in use.</strong></p>
</blockquote>
<ul>
<li>원인 : 포트가 이미 할당되어 있어서 발생한 에러</li>
<li>해결 : 해당 포트를 사용 중인지 확인한 후, 할당되어 있는 포트를 죽인 후 다시 docker 컨테이너를 재시작한다.<pre><code>sudo lsof -i : 포트번호</code></pre><pre><code>sudo kill -9 PID번호</code></pre><img src="https://velog.velcdn.com/images/kk_y_oo1/post/a32a34f3-393e-4e4c-8d00-b51088e85876/image.png" alt=""></li>
</ul>
<br>

<p>⛔️ <strong>에러 3</strong></p>
<blockquote>
<p><strong>docker: Error response from daemon: Conflict. The container name &quot;컨테이너 이름&quot; is already in use by container &quot;...&quot;. You have to remove (or rename) that container to be able to reuse that name.</strong></p>
</blockquote>
<ul>
<li><p>해결 
  1) 현재 실행 중인(?) 존재하는 컨테이너 리스트 보기</p>
<pre><code>docker ps -a</code></pre><p>2) 만약 지우고자 하는 컨테이너가 아직 running중이라면 먼저 멈추기리스트에 있는 컨테이너 중 지우고 싶은 컨테이너 지우기</p>
<pre><code>docker stop &lt;컨테이너명&gt;</code></pre><p>3) 리스트에 있는 컨테이너 중 지우고 싶은 컨테이너 지우기</p>
<pre><code>docker rm &lt;컨테이너명&gt;</code></pre></li>
</ul>
<br>
]]></description>
        </item>
        <item>
            <title><![CDATA[Docker & Docker Compose]]></title>
            <link>https://velog.io/@kk_y_oo1/Docker-Docker-Compose</link>
            <guid>https://velog.io/@kk_y_oo1/Docker-Docker-Compose</guid>
            <pubDate>Fri, 18 Aug 2023 12:45:26 GMT</pubDate>
            <description><![CDATA[<h2 id="docker-🐳">Docker 🐳</h2>
<ul>
<li>컨테이너 기술을 기반으로 한 일종의 가상화 플랫폼</li>
<li>부두에서 컨테이너를 옮기과 관리하는 직업인 ‘docker’에서 따온 이름처럼, 컨테이너를 잘 다룰 수 있게 도와주는 도구이다.</li>
<li>도커를 이용하면 1️⃣ 이미지를 실행시켜 컨테이너로 만들거나, 2️⃣ 생성된 컨테이너를 관리하거나 3️⃣ 컨테이너를 다시 이미지로 만드는 작업이 쉽게 가능해 진다.</li>
</ul>
<br>

<ul>
<li><p><strong>가상화</strong> : 물리적 자원인 하드웨어를 효율적으로 활용하기 위해 하드웨어 공간 위에 가상의 머신을 만드는 기술</p>
</li>
<li><p><strong>컨테이너</strong> : 컨테이너가 실행되고 있는 호스트 OS의 기능을 그대로 활용하면서 프로세스를 격리하여 독립 환경을 만드는 기술<br></p>
<p>  👉 Docker는 독립된 환경을 만들어서 하드웨어를 효율적으로 활용하는 기술</p>
</li>
</ul>
<br>

<hr>
<br>


<h2 id="가상화">가상화</h2>
<blockquote>
<p><strong>Hyperviser 기반의 가상화 vs Container 기반의 가상화</strong></p>
</blockquote>
<br>

<h3 id="가상화-1"><strong>가상화</strong></h3>
<ul>
<li>앞서 말했듯, 하나의 하드웨어를 여러 개의 가상 머신으로 분할해 효율적으로 사용하는 기술</li>
<li>분할된 가상머신들은 각각의 독립환경으로 구동 된다.</li>
<li>이 때, 베이스가 되는 기존의 환경을 Host OS, 그리고 가상 머신으로 분할된 각각의 환경을 Guest OS라고 부름.</li>
</ul>
<p><br><br></p>
<h3 id="hypervisor"><strong>Hypervisor</strong></h3>
<p><img src="https://velog.velcdn.com/images/kk_y_oo1/post/dc5bce07-e9ab-4df6-8ecc-741a9a5dd42e/image.png" alt=""></p>
<ul>
<li><p>가상 머신을 생성하기 위해서는 Hypervisor 또는 가상 머신 모니터라고 불리는 소프트웨어를 이용한다.</p>
<ul>
<li><p>호스트 하드웨어에 설치되어 호스트와 게스트를 나누는 역할을 하고,
각각의 게스트는 하이퍼바이저에 관리되며 시스템 자원을 할당받게 된다.</p>
</li>
<li><p>이때 하이퍼바이저에 의해 생성된 게스트(가상서버)는 호스트(물리서버)나 다른 게스트와 상호 간섭하지 않고, 완전히 분리된 환경에서 구동된다.</p>
</li>
<li><p>하이퍼바이저를 활용하면 마치 하드웨어가 여러 개인 것처럼 하나의 서버를 여러 명이 나눠서 쓸 수 있다. (컴퓨터 한 대에서 서로 다른 OS를 동시에 사용 가능)</p>
</li>
</ul>
</li>
</ul>
 <br>

<blockquote>
<p>⚠️ 가상 머신으로 무언가를 하려면, 하이퍼바이저를 반드시 거쳐야 한다는 점에서, 속도 저하가 필연적이다.
     또한 가상머신은 해당 환경을 구동하는데 필요한 파일을 모두 포함하고 있기 때문에, 가상머신을 배포할 때 만들어지는 이미지의 크기가 매우 커진다는 한계점이 있다.</p>
</blockquote>
<p><br><br></p>
<h3 id="container">Container</h3>
<p><img src="https://velog.velcdn.com/images/kk_y_oo1/post/443079fa-891f-40ad-87e5-f4e5bc21f3db/image.png" alt=""></p>
<ul>
<li>하이퍼바이저와 달리, 컨테이너는 가상의 OS를 만드는 것은 아니다.</li>
<li>컨테이너는 베이스 환경의 OS를 공유하면서 필요한 프로세스만 공유하는 방식.</li>
<li>커널을 공유하기 때문에 호스트 OS의 기능을 모두 사용할 수 있다.</li>
<li>그렇기 때문에 컨테이너 위에서는 호스트 OS와 다른 OS를 구동할 수 없다.</li>
<li>대신에 격리시킬 애플리케이션과 그것에 필요한 파일이나 특정 라이브러리 등 종속 항목만 포함하기 때문에, 배포를 위해서 생성되는 이미지의 용량이 작아진다는 장점이 있다.</li>
<li>운영체제가 아닌 프로세스이며, 하이퍼바이저를 거칠 필요가 없어 실행속도가 빠르다.</li>
</ul>
<p><br><br></p>
<h3 id="image">Image</h3>
<ul>
<li><p>이미지는 가상머신이나 컨테이너 또는 프로그램을 실행하는 데 필요한 파일과 라이브러리, 설정 등을 가지고 있는 파일이다..</p>
</li>
<li><p>즉, 이미지란 <strong>컨테이너 실행에 필요한 파일과 설정값 등을 포함하고 있는 것</strong>으로 상태값이 없으며, 변하지 않는다.</p>
</li>
<li><p>이미지는 레이어라는 계층 구조로 이루어져 있는데, 변경 사항이 생기면 새로운 레이어를 추가해서 기록한다.</p>
</li>
<li><p>이미지 전체를 새로 받지 않고 해당 레이어만 받는 것으로 이미지를 업데이트 할 수 있다는 장점이 있다.</p>
</li>
<li><p>이미지를 실행하면 프로세스, 즉 컨테이너가 된다. </p>
</li>
</ul>
<br>

<ul>
<li>컨테이너는 즉, 이미지를 실행한 상태이며 추가되거나 변하는 값들은 컨테이너에 저장되게 된다.</li>
<li>때문에 같은 이미지에서 여러개의 컨테이너를 생성할 수 있으며, 컨테이너의 상태가 변하거나 컨테이너가 삭제되더라도 이미지는 그대로 남아있게 된다. <br></li>
</ul>
<br>

<p> 👉 정리하면 이미지는 컨테이너 실행에 필요한 모든 정보를 가지고 있기 때문에, 의존성을 위해서 이것저것 설치하거나 충돌을 해결할 필요가 없다.</p>
<p><br><br></p>
<hr>
<br>

<h2 id="도커의-장점">도커의 장점</h2>
<ul>
<li>개발 과정에서 다른 라이브러리와 충돌하는 것을 방지하기 위해 격리된 환경이 필요할 경우, 완성된 서비스를 배포할 경우, 혹은 배포 중인 서비스를 받아서 실행해 볼 때도 유용하다.</li>
<li>특히 배포 과정에서 도커를 사용해 필요한 파일들만 포장해서 만들면 종속성 이슈에서도 벗어날 수 있다.</li>
<li>즉, 도커를 사용하면 서버가 늘어나는 경우 파일 받고, 필요한 라이브러리 설치하고 이런 불필요한 과정 없이,이미지를 실행해서 컨테이너로 만들면 된다.</li>
</ul>
<br>

<p>1️⃣ <strong>빠른 설치 (Rapid Application Deployment)</strong></p>
<p>2️⃣ <strong>어플리케이션 이식성(Portability Across Machines)</strong></p>
<p>   : 어플리케이션과 설치 파일들이 하나의 컨테이너에 존재하기 때문에 호스트의 커널이나 플랫폼 버전 등에 상관없이 도커만 실행할 수 있으면 사용 가능 </p>
<p>3️⃣ <strong>버전제어, 컴포넌트 재사용(Version Control &amp; Component Reuse)</strong></p>
<p>: 컨테이너 버젼 제어가 쉽고 변경 내역의 확인이 가능하며 롤백이 간단하다. </p>
<p>  이전 레이어에서 컴포넌트들을 재사용하기 때문에 도커를 가볍게 만들 수 있다.</p>
<p>4️⃣ <strong>쉬운 유지관리(Simplified Maintenance)</strong></p>
<p>   : 어플리케이션 종속성에 관한 문제들에 대해 유지관리가 쉽다.</p>
<br>

<hr>
<br>   

<h2 id="docker-hub">Docker Hub</h2>
<ul>
<li>도커에서 제공하는 이미지 저장소</li>
<li><a href="https://hub.docker.com/">https://hub.docker.com/</a></li>
<li>ex) Docker에서 제공하는 MySQL 이미지
  <img src="https://velog.velcdn.com/images/kk_y_oo1/post/3db99012-f355-4eec-a818-4b96d573a63f/image.png" alt=""></li>
</ul>
<p>  <br><br></p>
<h2 id="docker-compose">Docker Compose</h2>
<ul>
<li>다중 컨테이너를 정의하고 실행하기 위한 도구</li>
<li>다중 컨테이너를 구성하기 위해서 YML 파일을 사용한다.</li>
<li><a href="https://docs.docker.com/compose/">https://docs.docker.com/compose/</a></li>
</ul>
<p><img src="https://velog.velcdn.com/images/kk_y_oo1/post/a909197a-ed93-4999-8fca-1c237ac13076/image.png" alt=""></p>
<p><br><br><br></p>
<hr>
<br>

<p>📂 <strong>참고자료</strong>
<a href="https://velog.io/@markany/%EB%8F%84%EC%BB%A4%EC%97%90-%EB%8C%80%ED%95%9C-%EC%96%B4%EB%96%A4-%EA%B2%83-1.-%EB%8F%84%EC%BB%A4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80">https://velog.io/@markany/도커에-대한-어떤-것-1.-도커란-무엇인가</a></p>
<p><a href="https://velog.io/@zidru/%EB%8F%84%EC%BB%A4Docker%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90">https://velog.io/@zidru/도커Docker에-대해서-알아보자</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] build.gradle의 dependencies 정보 확인하기]]></title>
            <link>https://velog.io/@kk_y_oo1/Java-build.gradle%EC%9D%98-dependencies-%EC%A0%95%EB%B3%B4-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@kk_y_oo1/Java-build.gradle%EC%9D%98-dependencies-%EC%A0%95%EB%B3%B4-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 18 Aug 2023 10:44:55 GMT</pubDate>
            <description><![CDATA[<h3 id="dependencies-정보-확인">dependencies 정보 확인</h3>
<pre><code class="language-java">plugins {
    id &#39;java&#39;
}

group &#39;org.example&#39;
version &#39;1.0-SNAPSHOT&#39;

repositories {
    mavenCentral()
}

dependencies {
    implementation(&#39;org.apache.tomcat.embed:tomcat-embed-core:9.0.71&#39;)
    implementation(&#39;org.apache.tomcat.embed:tomcat-embed-jasper:9.0.71&#39;)

    implementation(&#39;javax.servlet:javax.servlet-api:4.0.1&#39;)

    implementation(&#39;ch.qos.logback:logback-classic:1.4.5&#39;)

    testImplementation &#39;org.junit.jupiter:junit-jupiter-api:5.8.1&#39;
    testRuntimeOnly &#39;org.junit.jupiter:junit-jupiter-engine:5.8.1&#39;
}

test {
    useJUnitPlatform()
}
</code></pre>
<ul>
<li><p>Maven Repository에 가서 확인할 수 있다.
  <a href="https://mvnrepository.com/">https://mvnrepository.com/</a></p>
</li>
<li><p>위의 코드에서 예를 들어, embed tomcat을 찾아 본다고 하면
<img src="https://velog.velcdn.com/images/kk_y_oo1/post/be91196d-ba84-42c6-898b-6334855df63d/image.png" alt="">
<img src="https://velog.velcdn.com/images/kk_y_oo1/post/3fe1fa96-9645-400f-b1fb-759020929963/image.png" alt=""></p>
</li>
<li><p>Maven / Gradle 등 맞는 것으로 dependencies에 넣어주면 된다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] NoUniqueBeanDefinitionException 원인과 해결 ]]></title>
            <link>https://velog.io/@kk_y_oo1/Spring-NoUniqueBeanDefinitionException-%EC%9B%90%EC%9D%B8%EA%B3%BC-%ED%95%B4%EA%B2%B0</link>
            <guid>https://velog.io/@kk_y_oo1/Spring-NoUniqueBeanDefinitionException-%EC%9B%90%EC%9D%B8%EA%B3%BC-%ED%95%B4%EA%B2%B0</guid>
            <pubDate>Tue, 15 Aug 2023 16:08:32 GMT</pubDate>
            <description><![CDATA[<h3 id="⛔️-nouniquebeandefinitionexception">⛔️ NoUniqueBeanDefinitionException</h3>
<ul>
<li>Autowired 는 타입(Type)으로 조회하기 때문에, 동일한 타입의 빈이 2개 이상일 때 문제가 발생한다.</li>
</ul>
<blockquote>
<p>org.springframework.beans.factory.<strong>UnsatisfiedDependencyException</strong> : Error creating bean with name &#39;filmServiceImpl&#39; defined in file [경로]: Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.<strong>NoUniqueBeanDefinitionException</strong>: No qualifying bean of type &#39;-경로.Movie&#39; available: <strong>expected single matching bean but found 2: comedyMovie, RomanceMovie</strong></p>
</blockquote>
<p><br><br></p>
<h3 id="💡solution1-primary">💡Solution1. @Primary</h3>
<ul>
<li>여러 빈이 매칭 되는 경우, @Primary가 우선순위이다.<pre><code class="language-java">@Component
@Primary
public class ComedyMovie implements Movie {
  ...
}</code></pre>
</li>
</ul>
<br>




<h3 id="💡solution2-qualifier">💡Solution2. @Qualifier</h3>
<ul>
<li>추가로 이름을 구분하여 사용하기 위한 용도 (실제 빈의 이름을 변경하는 것은 아님)</li>
</ul>
<pre><code class="language-java">@Component
@Qualifier(&quot;mainMovie&quot;)
public class ComedyMovie implements Movie {
    ...
}</code></pre>
<pre><code class="language-java">@Component
@Qualifier(&quot;subMovie&quot;)
public class RomanceMovie implements Movie {
    ...
}</code></pre>
<pre><code class="language-java">@Component
public class filmServiceImpl implements filmService {

    private final MemberRepository memberRepository;
    private final Movie movie;

    @Autowired
    public OrderServiceImpl(MemberRepository memberRepository, @Qualifier(&quot;mainMovie&quot;) Movie movie) {
        this.memberRepository = memberRepository;
        this.movie = movie;
    }

   ...
}</code></pre>
<ul>
<li><strong>조회 순서</strong>
@Qualifier -&gt; 빈 이름 매칭 -&gt; NoSuchBeanDefinitionException 예외 발생</li>
</ul>
<br>




<h3 id="💡solution3-autowired-필드-명">💡Solution3. @Autowired 필드 명</h3>
<ul>
<li><p>타입으로 조회했는데 여러 빈이 있는 경우, 필드명으로 추가 매칭한다.</p>
<pre><code class="language-java">@Component
public class filmServiceImpl implements filmService {

  private final MemberRepository memberRepository;
  private final Movie movie;

  @Autowired
  public OrderServiceImpl(MemberRepository memberRepository, Movie comedyMovie) {
      this.memberRepository = memberRepository;
      this.movie = comedyMovie;
  }

 ...
}</code></pre>
</li>
</ul>
<br>]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] Autowired 관련 오류]]></title>
            <link>https://velog.io/@kk_y_oo1/Spring-Autowired-%EA%B4%80%EB%A0%A8-%EC%98%A4%EB%A5%98</link>
            <guid>https://velog.io/@kk_y_oo1/Spring-Autowired-%EA%B4%80%EB%A0%A8-%EC%98%A4%EB%A5%98</guid>
            <pubDate>Mon, 14 Aug 2023 16:10:52 GMT</pubDate>
            <description><![CDATA[<h3 id="⛔️-unsatisfieddependencyexception--nosuchbeandefinitionexception">⛔️ UnsatisfiedDependencyException ... NoSuchBeanDefinitionException</h3>
<p><img src="https://velog.velcdn.com/images/kk_y_oo1/post/439a95e0-6ac5-4e1e-8990-028a75649217/image.png" alt=""></p>
<ul>
<li>스프링 빈 등록이 안 되어 있어 자동 주입할 대상이 없는데 자동 주입하려는 경우</li>
</ul>
<br>

<h3 id="💡-자동-주입-대상을-옵션으로-처리하기">💡 자동 주입 대상을 옵션으로 처리하기</h3>
<ul>
<li>@Autowired는 required 옵션의 기본값이 true이기 때문에,
  자동 주입 대상이 없으면 위와 같은 에러가 난다.</li>
<li>주입할 스프링 빈이 없어도 동작하기 위해서는 자동 주입 대상을 옵션으로 처리하면 된다.</li>
</ul>
<br>


<pre><code class="language-java">@Autowired(required = false)
public void case1(Board board1){
 // 현재 Board 클래스는 스프링 빈으로 등록 안 되어 있음
}

@Autowired
public void case2(@Nullable Board board2){
    ...
}

@Autowired
public void case3(Optional&lt;Board&gt; board3){
    ...
        }</code></pre>
<ul>
<li>case1 : 자동 주입할 대상이 없어 메서드 자체가 호출이 안된다.</li>
<li>case2 : 자동 주입할 대상이 없어도 호출은 되지만, null이 입력된다.</li>
<li>case3 : 자동 주입할 대상이 없어도 호출은 되지만, ‘Optional Empty’가 입력된다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] 컴포넌트 스캔에서의 중복 등록에 의한 오류]]></title>
            <link>https://velog.io/@kk_y_oo1/Spring-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%8A%A4%EC%BA%94%EC%97%90%EC%84%9C%EC%9D%98-%EC%A4%91%EB%B3%B5-%EB%93%B1%EB%A1%9D%EC%97%90-%EC%9D%98%ED%95%9C-%EC%98%A4%EB%A5%98</link>
            <guid>https://velog.io/@kk_y_oo1/Spring-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%8A%A4%EC%BA%94%EC%97%90%EC%84%9C%EC%9D%98-%EC%A4%91%EB%B3%B5-%EB%93%B1%EB%A1%9D%EC%97%90-%EC%9D%98%ED%95%9C-%EC%98%A4%EB%A5%98</guid>
            <pubDate>Sat, 12 Aug 2023 12:52:37 GMT</pubDate>
            <description><![CDATA[<h3 id="⛔️-컴포넌트-스캔에서의-중복-등록에-의한-오류-1">⛔️ 컴포넌트 스캔에서의 중복 등록에 의한 오류 (1)</h3>
<ul>
<li>컴포넌트 스캔에서 동일한 빈 이름을 등록한 경우</li>
</ul>
<p><img src="https://velog.velcdn.com/images/kk_y_oo1/post/b6881a05-6f75-49eb-9013-a403cd63ce3b/image.png" alt=""></p>
<blockquote>
<h4 id="conflictingbeandefinitionexception">ConflictingBeanDefinitionException</h4>
</blockquote>
<ul>
<li><p>Annotation-specified bean name &#39;service&#39; for bean class [spring01.core.order.OrderServiceImpl] conflicts with existing, non-compatible bean definition of same name and class [spring01.core.member.MemberServiceImpl]</p>
<p>=&gt; 중복된 빈 이름을 특정해 주고, 어떤 클래스들에서 중복된 빈 이름이 등록되었는지도 친절하게 알려 준다.</p>
<br>

</li>
</ul>
<p>💡 ** 해결 ** : 빈 이름을 다르게 지정해 주면 된다.</p>
<br>

<hr>
<br>

<h3 id="⚠️-컴포넌트-스캔에서의-중복-등록에-의한-오류-2">⚠️ 컴포넌트 스캔에서의 중복 등록에 의한 오류 (2)</h3>
<ul>
<li>컴포넌트로 자동 등록한 빈과 수동 등록한 빈이 충돌하는 경우</li>
</ul>
<blockquote>
<h4 id="overriding-bean-definition-for-bean-memorymemberrepository-with-a-different-definition-replacing">Overriding bean definition for bean &#39;memoryMemberRepository&#39; with a different definition: replacing</h4>
</blockquote>
<p> <img src="https://velog.velcdn.com/images/kk_y_oo1/post/3a54c31c-28d6-46ec-bd51-2ca1cc7dcb73/image.png" alt=""></p>
<p> =&gt; 이 경우 에러가 나지는 않지만, 실제로 의도한 빈 등록과 다르게 등록되어 다른 결과가 나올 수 있다. </p>
<br>



<blockquote>
<h4 id="a-bean-with-that-name-has-already-been-defined-in-file-경로-and-overriding-is-disabled">A bean with that name has already been defined in file [경로] and overriding is disabled.</h4>
</blockquote>
<p> <img src="https://velog.velcdn.com/images/kk_y_oo1/post/7f3478a4-d04d-4253-97a8-39586792916c/image.png" alt=""></p>
<p>만약 이 경우 오류가 나지 않게 하고 싶다면, application.properties 아래를 추가하면 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Algorithm / Python ] 백준 1946]]></title>
            <link>https://velog.io/@kk_y_oo1/Algorithm-Python-%EB%B0%B1%EC%A4%80-1946</link>
            <guid>https://velog.io/@kk_y_oo1/Algorithm-Python-%EB%B0%B1%EC%A4%80-1946</guid>
            <pubDate>Tue, 08 Aug 2023 09:30:43 GMT</pubDate>
            <description><![CDATA[<h1 id="silver-i-신입-사원---1946">[Silver I] 신입 사원 - 1946</h1>
<p><a href="https://www.acmicpc.net/problem/1946">문제 링크</a> </p>
<h3 id="성능-요약">성능 요약</h3>
<p>메모리: 70960 KB, 시간: 4964 ms</p>
<h3 id="분류">분류</h3>
<p>그리디 알고리즘, 정렬</p>
<h3 id="문제-설명">문제 설명</h3>
<p>언제나 최고만을 지향하는 굴지의 대기업 진영 주식회사가 신규 사원 채용을 실시한다. 인재 선발 시험은 1차 서류심사와 2차 면접시험으로 이루어진다. 최고만을 지향한다는 기업의 이념에 따라 그들은 최고의 인재들만을 사원으로 선발하고 싶어 한다.</p>

<p>그래서 진영 주식회사는, 다른 모든 지원자와 비교했을 때 서류심사 성적과 면접시험 성적 중 적어도 하나가 다른 지원자보다 떨어지지 않는 자만 선발한다는 원칙을 세웠다. 즉, 어떤 지원자 A의 성적이 다른 어떤 지원자 B의 성적에 비해 서류 심사 결과와 면접 성적이 모두 떨어진다면 A는 결코 선발되지 않는다.</p>

<p>이러한 조건을 만족시키면서, 진영 주식회사가 이번 신규 사원 채용에서 선발할 수 있는 신입사원의 최대 인원수를 구하는 프로그램을 작성하시오.</p>

<h3 id="입력">입력</h3>
 <p>첫째 줄에는 테스트 케이스의 개수 T(1 ≤ T ≤ 20)가 주어진다. 각 테스트 케이스의 첫째 줄에 지원자의 숫자 N(1 ≤ N ≤ 100,000)이 주어진다. 둘째 줄부터 N개 줄에는 각각의 지원자의 서류심사 성적, 면접 성적의 순위가 공백을 사이에 두고 한 줄에 주어진다. 두 성적 순위는 모두 1위부터 N위까지 동석차 없이 결정된다고 가정한다.</p>

<h3 id="출력">출력</h3>
 <p>각 테스트 케이스에 대해서 진영 주식회사가 선발할 수 있는 신입사원의 최대 인원수를 한 줄에 하나씩 출력한다.</p>



<p><img src="https://velog.velcdn.com/images/kk_y_oo1/post/d44acfda-2dbb-4476-940c-636ee9633568/image.png" alt=""></p>
<br>

<h3 id="풀이">풀이</h3>
<pre><code class="language-python">import sys
input = sys.stdin.readline

t = int(input())
for i in range(t):
    n = int(input())
    ranking = [list(map(int, input().split())) for _ in range(n)]
    ranking.sort()
    current_top = ranking[0][1]
    pass_num = 1

    for j in range(1, len(ranking)):
        if ranking[j][1] &lt; current_top:
            current_top = ranking[j][1]
            pass_num += 1

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

<h3 id="설명">설명</h3>
<ul>
<li><p>그리디 알고리즘</p>
</li>
<li><p>서류 심사 순위로 정렬 후, 해당 지원자보다 위에 있는 지원자들보다 순위가 낮은지만 확인하면 된다.</p>
<ul>
<li><p>서류 심사에서 n번째 순위인 참가자라면 n-1번째까지 다 확인하는 것이 아니라, 그 위의 지원자들 중 가장 낮은 순위를 업데이트 하면서 그 순위 한 번만 비교하면 된다.</p>
</li>
<li><p>ex) 첫 번째 테스트 케이스 정렬 후, current_top은 4-&gt; 3-&gt; 2 -&gt; 1 이런 식으로 갱신되며 이 값들만 비교 하면 된다.</p>
<p>  1 4
  2 3
  3 2
  4 1
  5 5</p>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Data Structure] Hash Table]]></title>
            <link>https://velog.io/@kk_y_oo1/Data-Structure-Hash-Table</link>
            <guid>https://velog.io/@kk_y_oo1/Data-Structure-Hash-Table</guid>
            <pubDate>Tue, 08 Aug 2023 07:36:42 GMT</pubDate>
            <description><![CDATA[<br>

<h2 id="1️⃣-hash-table">1️⃣ <strong>Hash Table</strong></h2>
<br>

<h3 id="해시-테이블의-구조"><strong>해시 테이블의 구조</strong></h3>
<ul>
<li>key에 value를 저장하는 데이터 구조<ul>
<li>key를 통해 바로 데이터를 받아오기 때문에, 다른 자료구조에 비해 검색 속도가 빨라진다.</li>
<li>보통 배열로 미리 해시 테이블의 크기를 생성한 후에 사용한다.</li>
<li>Python - Dictionary (파이썬에선는 해시를 별도로 구현하지 않고 딕셔너리를 사용)</li>
</ul>
</li>
</ul>
<pre><code> &lt;br&gt;</code></pre><h3 id="해시-테이블의-장단점"><strong>해시 테이블의 장단점</strong></h3>
<ul>
<li><p><strong>장점</strong></p>
<ul>
<li><p>검색 속도가 빠르다. (저장/읽기 속도)</p>
</li>
<li><p>데이터의 중복 처리가 쉽다.</p>
<p>   → 배열처럼 하나씩 데이터를 거쳐가며 확인할 필요 없이, 키에 대한 데이터만 확인하면 되므로</p>
</li>
</ul>
</li>
<li><p><strong>단점</strong></p>
<ul>
<li><p>저장공간이 일반적으로 더 많이 든다.<br></p>
<p> → 해시테이블이 데이터가 없더라도 저장공간을 마련해 둔다.</p>
</li>
<li><p>충돌(여러 키가 동일한 주소를 갖는 경우)을 해결하기 위한 별도의 조치가 필요하다.</p>
</li>
</ul>
</li>
<li><p><strong>활용</strong></p>
<ul>
<li><p>검색이 잦은 경우</p>
</li>
<li><p>저장, 삭제, 읽기가 많은 경우</p>
</li>
<li><p>캐시 구현 (중복 확인이 유용하기 때문에)</p>
<br>

</li>
</ul>
</li>
</ul>
<h3 id="용어"><strong>용어</strong></h3>
  <p align="center">  <img src="https://velog.velcdn.com/images/kk_y_oo1/post/79117d65-6dc2-44bd-bbb1-7a0d515012a7/image.png" height="500px" width="400px"></p>


<ul>
<li><strong>Hash</strong> : 임의의 값을 고정길이로 변환</li>
<li><strong>Hash Table</strong> : 키값이 해시 함수 연산을 거쳐 직접 접근이 가능하도록 한 데이터 구조</li>
<li><strong>Hashing Function</strong> : key를 넣으면 산술 연산을 실행해 찾고자 하는 데이터의 위치를 알아내는 함수</li>
<li><strong>Hash Value / Hash Address</strong> : key의 hashing function 연산 결과로 해시값을 구하고, 이를 통해 해시 테이블에서 해당 key에 대응하는 데이터의 위치를 일관성 있게 찾을 수 있다.</li>
<li><strong>Slot</strong> : 한 개의 데이터를 저장할 수 있는 공간</li>
</ul>
  <br>
---
<br>


<h2 id="2️⃣-해시테이블-구현하기-예제">2️⃣ <strong>해시테이블 구현하기 예제</strong></h2>
<p><img src="blob:https://velog.io/15696f1d-8d36-454e-8815-e76828caf7c3" alt="업로드중.."></p>
<pre><code class="language-python">hash_table = list([0 for i in range(8)])

def get_key(data):
    return hash(data)

def hash_func(key):
    return key % 5

def save_data(data, value):
    hash_address = hash_func(get_key(data))
    hash_table[hash_address] = value

def read_data(data):
    hash_address = hash_func(get_key(data))
    return hash_table[hash_address]</code></pre>
<br>

<hr>
<br>

<h2 id="3️⃣-충돌collision-해결-알고리즘">3️⃣ <strong>충돌(Collision) 해결 알고리즘</strong></h2>
<blockquote>
<p>추가 예정</p>
</blockquote>
<p><br><br><br></p>
<hr>
<br>

<h3 id="📂-참고-자료">📂 참고 자료</h3>
<p><a href="https://www.fun-coding.org/post/Chapter09-hashtable-live.html#gsc.tab=0">https://www.fun-coding.org/post/Chapter09-hashtable-live.html#gsc.tab=0</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] Bean 조회 관련 에러 ]]></title>
            <link>https://velog.io/@kk_y_oo1/Spring-Bean-%EC%A1%B0%ED%9A%8C-%EA%B4%80%EB%A0%A8-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@kk_y_oo1/Spring-Bean-%EC%A1%B0%ED%9A%8C-%EA%B4%80%EB%A0%A8-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Mon, 07 Aug 2023 10:17:42 GMT</pubDate>
            <description><![CDATA[<h3 id="nosuchbeandefinitionexception">NoSuchBeanDefinitionException</h3>
<ul>
<li>조회하고자 하는 스프링 빈이 없으면 예외가 발생한다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/kk_y_oo1/post/91000707-74ef-462e-b148-3e6c5182eecf/image.png" alt=""></p>
<h4 id="⛔️-문제-상황">⛔️ 문제 상황</h4>
<pre><code class="language-java">@Test
@DisplayName(&quot;빈 이름으로 조회&quot;)
    void findBeanByNameX() {
    MemberService memberService = ac.getBean(&quot;mService&quot;, MemberService.class);
    Assertions.assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
      }</code></pre>
<br>

<h4 id="💡-해결">💡 해결</h4>
<ul>
<li>org.junit.jupiter.api.Assertions의 assertionThrows 사용하여 조회하고자 하는 스프링 빈이 없으면 예외처리 한다.</li>
</ul>
<pre><code class="language-java">    @Test
    @DisplayName(&quot;빈 이름으로 조회 실패&quot;)
    void findBeanByNameX() {
//        MemberService memberService = ac.getBean(&quot;mService&quot;, MemberService.class);
        assertThrows(NoSuchBeanDefinitionException.class,
                () -&gt; ac.getBean(&quot;mService&quot;, MemberService.class));
    }</code></pre>
<br> 

<hr>
<br>


<h3 id="nouniquebeandefinitionexception-1">NoUniqueBeanDefinitionException (1)</h3>
<ul>
<li>Bean을 타입으로 조회할 경우, 같은 타입이 둘 이상이면 중복 오류가 발생한다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/kk_y_oo1/post/e7419695-9911-4cb3-b39d-3f6ae5e80eb9/image.png" alt=""></p>
<h4 id="⛔️-문제-상황-1">⛔️ 문제 상황</h4>
<ul>
<li><p>MemberRepository.class 타입으로 조회했는데, 조회한 SameBeanConfig.class 내에 해당 타입이 2개가 존재하여 생기는 오류이다.</p>
<pre><code class="language-java">
  AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SameBeanConfig.class);

  @Test
  @DisplayName(&quot;타입으로 조회&quot;)
  void findBeanByTypeDuplicate() {
      MemberRepository bean = ac.getBean(MemberRepository.class);
  }</code></pre>
</li>
</ul>
<br>

<h4 id="💡-해결-1">💡 해결</h4>
<ul>
<li>org.junit.jupiter.api.Assertions의 assertionThrows 사용하여 중복되는 스프링 빈이 있으면 예외처리 한다.</li>
<li>sol1) 빈 이름을 지정하여 조회</li>
<li>sol2) 해당되는 타입을 모두 조회</li>
</ul>
<pre><code class="language-java">    @Test
    @DisplayName(&quot;타입으로 조회 시 중복 오류 예외처리&quot;)
    void findBeanByTypeDuplicate() {
        assertThrows(NoUniqueBeanDefinitionException.class,
                () -&gt; ac.getBean(MemberRepository.class));
    }

    @Test
    @DisplayName(&quot;중복 타입 있는 경우, 빈 이름을 지정하기&quot;)
    void findBeanByName() {
        MemberRepository memberRepository = ac.getBean(&quot;memberRepository1&quot;, MemberRepository.class);
        assertThat(memberRepository).isInstanceOf(MemberRepository.class);

    }

    @Test
    @DisplayName(&quot;특정 타입을 모두 조회하기&quot;)
    void findAllBeansByType(){
        Map&lt;String, MemberRepository&gt; beansOfType = ac.getBeansOfType(MemberRepository.class);
        for (String key : beansOfType.keySet()) {
            System.out.println(&quot; key = &quot; + key + &quot; value = &quot; + beansOfType.get(key));
        }
        System.out.println(&quot;beansOfType = &quot; + beansOfType);
        assertThat(beansOfType.size()).isEqualTo(2);
    }</code></pre>
<br> 

<hr>
<br>

<h3 id="nouniquebeandefinitionexception-2">NoUniqueBeanDefinitionException (2)</h3>
<ul>
<li>상속 관계의 스프링 빈 조회 시 오류</li>
</ul>
<p><img src="https://velog.velcdn.com/images/kk_y_oo1/post/ddc7f13c-517f-4cf0-a1bf-b7f536814193/image.png" alt=""></p>
<h4 id="⛔️-문제-상황-2">⛔️ 문제 상황</h4>
<ul>
<li><p>상속관계의 스프링 빈을 조회할 때, 부모 타입으로 조회 시 자식이 둘 이상이면 중복으로 오류가 발생한다.</p>
<pre><code class="language-java">
  @Test
  @DisplayName(&quot;부모 타입으로 조회 시, 자식이 둘 이상 있으면 중복 오류가 발생한다.&quot;)
  void findBeanByParentTypeDuplicate() {
      DiscountPolicy bean = ac.getBean(DiscountPolicy.class);
  }</code></pre>
<pre><code class="language-java">  @Configuration
  static class TestConfig {

      @Bean
      public DiscountPolicy rateDiscountPolicy() {
          return new RateDiscountPolicy();
      }

      @Bean
      public DiscountPolicy fixDiscountPolicy() {
          return new FixDiscountPolicy();
      }
  }
</code></pre>
</li>
</ul>
<pre><code>
&lt;br&gt;

#### 💡 해결

- org.junit.jupiter.api.Assertions의 assertionThrows 사용하여 중복되는 스프링 빈이 있으면 예외처리 한다.
- sol1) 빈 이름을 지정하여 조회
- sol2) 해당되는 자식 타입을 모두 조회

``` java
        @Test
    @DisplayName(&quot;부모 타입으로 조회 시, 자식이 둘 이상 있으면 중복 오류가 발생한다.&quot;)
    void findBeanByParentTypeDuplicate() {
        assertThrows(NoUniqueBeanDefinitionException.class,
                () -&gt; ac.getBean(DiscountPolicy.class));
    }

    @Test
    @DisplayName(&quot;부모 타입으로 조회 시, 자식이 둘 이상 있으면 빈 이름을 지정하면 된다.&quot;)
    void findBeanByParentTypeBeanName() {
        DiscountPolicy rateDiscountPolicy = ac.getBean(&quot;rateDiscountPolicy&quot;, DiscountPolicy.class);
        assertThat(rateDiscountPolicy).isInstanceOf(RateDiscountPolicy.class);
    }

    @Test
    @DisplayName(&quot;부모 타입으로 모두 조회&quot;)
    void findBeanByParentType() {
        Map&lt;String, DiscountPolicy&gt; beansOfType = ac.getBeansOfType(DiscountPolicy.class);
        assertThat(beansOfType.size()).isEqualTo(2);
        for (String key : beansOfType.keySet()) {
            System.out.println(&quot;key = &quot; + key + &quot;value = &quot; + beansOfType.get(key));
        }
    }</code></pre><br> 
]]></description>
        </item>
        <item>
            <title><![CDATA[[Project] SSE 사용 이유와 구현]]></title>
            <link>https://velog.io/@kk_y_oo1/Project-SSE-%EC%82%AC%EC%9A%A9-%EC%9D%B4%EC%9C%A0%EC%99%80-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@kk_y_oo1/Project-SSE-%EC%82%AC%EC%9A%A9-%EC%9D%B4%EC%9C%A0%EC%99%80-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Thu, 27 Jul 2023 12:06:55 GMT</pubDate>
            <description><![CDATA[<h3 id="⏱️-sse-사용-이유">⏱️ <strong>SSE 사용 이유</strong></h3>
<ul>
<li><p>게시글 편집 상태 조회 및 알림 기능이 필요했다.</p>
</li>
<li><p>한 사람이 수정 중인 게시글에 대해서는 다른 사람이 수정하지 못하도록 하며,수정이 완료된 게시물에</p>
<p>  대해서는 수정 완료 되었음을 프론트에 보내주고, 나아가 알림 기능까지 구현하고자 했다.</p>
</li>
</ul>
<br>


<h3 id="🎛️-sse-사용-방향">🎛️ <strong>SSE 사용 방향</strong></h3>
<ul>
<li>게시글을 수정하기를 누르면 SSE emitter 객체가 생성되어, SSE가 연결된다.</li>
<li>SSE 연결은 크게 게시글에 연결되고, 게시글 안에 수정을 원하는 여러 멤버가 연결된다.</li>
<li>편집이 완료되면 SSE가 연결된 멤버들에게 event에 대한 data를 보내고, SSE 연결을 끊는다.</li>
</ul>
<br>

<h3 id="⛏️-구현">⛏️ <strong>구현</strong></h3>
<ul>
<li><p><strong>EmitterRepository에서 ConcurrentHashMap을 사용하여 SSE 구독 정보 저장</strong></p>
<pre><code class="language-java">  public static Map&lt;Long, SseEmitters&gt; postSseEmitters = new ConcurrentHashMap&lt;&gt;();

      public SseEmitters subscibePosts(Long postId) {
          if (postSseEmitters.get(postId) == null) {
              SseEmitters postSseEmittersMap = new SseEmitters();
              postSseEmitters.put(postId, postSseEmittersMap);
          }

          return postSseEmitters.get(postId);
      }</code></pre>
<ul>
<li>repository에서 Spring Data JPA 대신 HashMap 사용하여 데이터 저장 및 조회</li>
<li>Disk가 아닌 memory에 저장하기 때문에 disk I/O 작업이 발생하지 않아 속도가 빠르며, 휘발성이라는 점에서 사용함.</li>
</ul>
</li>
</ul>
<br>

<ul>
<li><h2 id="hashmap-사용-이유"><strong>HashMap 사용 이유</strong></h2>
</li>
</ul>
<pre><code>    **HashMap** 

    - 데이터를 저장할 때 Key와 Value 가 짝을 이루어 저장되는 자료구조
    - 데이터를 저장할 때는 키(Key) 값으로 해시함수를 실행한 결과를 통해 저장위치를 결정
    - HashMap은 특정 데이터의 저장위치를 해시함수를 통해 바로 알 수 있기 때문에 
      데이터의 추가, 삭제, 특히 검색이 빠르다는 장점

    - Spring Data JPA를 사용하여 데이터를 저장하고 조회하는 것보다 빠르고,
    - 멤버와 게시글에 대한 데이터의 저장위치를 해시함수를 통해 바로 알 수 있기 때문에 검색이 빠르다는 점에서 도입함.</code></pre><br>   

<ul>
<li><p><strong>Concurrent HashMap 사용 이유</strong></p>
<ul>
<li>Concurrent HashMap
: Thread-safety를 보장하면서 HashTable, HashMap과 동일한 기능을 가지는 클래스</li>
</ul>
<ul>
<li><p>Thread-safety : 여러 스레드로부터 동시에 접근이 이루어져도 프로그램의 실행에 문제 없음 (동시성 문제 해결) <br></p>
<ul>
<li><p>💡 <strong>동시성 문제를 해결하기 위해</strong></p>
<ul>
<li><p>HashMap은 기본적으로 동기화 되지 않는다. (=Thread-safe하지 않음)</p>
</li>
<li><p>동시성 문제가 없는 (여러 스레드로부터 동시에 접근이 이루어져도 프로그램의 실행에 문제 없는) ConcurrentHashMap을 사용 = Tread-safe</p>
<ul>
<li>하나의 쓰레드가 HashMap 객체를 iterate하는 동안, 다른 쓰레드가 객체의 내용을 추가/수정하려고 하면, HashpMapConcurrentModificationException이라는 런타임 예외가 발생한다.</li>
<li>반면 ConcurrentHashMap에서는 iterate할 때 객체 수정이 발생하더라도 예외가 발생하지 않는다.</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<br>

<h3 id="web-socket과의-차이점">Web Socket과의 차이점</h3>
<br>

<p><strong>🔎 SSE(Server Sent Event)</strong></p>
<p><img src="https://velog.velcdn.com/images/kk_y_oo1/post/dc4c151f-5164-47c5-ae33-3f541f51484e/image.png" alt=""></p>
<ul>
<li><p>서버- 클라이언트의 단방향 통신으로 지속적인 연결을 통해,  서버의 데이터를 실시간으로 클라이언트로 보낸다.</p>
<ul>
<li>처음 한 번 맺은 HTTP 연결을 통해 서버는 클라이언트로 지속적으로 데이터 전송 가능하다.</li>
</ul>
<hr>
<p> <strong>• 일반적인 HTTP 요청</strong> : request - respones ➡️ disconnect
 <strong>• SSE의 경우</strong> : request - data transmission - data transmission - data transmission -...  ➡️ disconnect</p>
<hr>
</li>
</ul>
<br>

<p> 🔛 <strong>WebSocket (Server push)</strong></p>
<ul>
<li><p>TCP 통신을 통해 서버와 브라우저가 실시간으로 데이터를 주고 받는 방법</p>
</li>
<li><p>TCP가 handshake를 통한 연결 지향 양방향 통신이기 때문에 실시간 양방향 통신이 가능하다.</p>
</li>
<li><p>채팅, 게임, 주식 등에 사용</p>
</li>
<li><p>계속해서 HTTP 요청을 보내지 않고, websocket은 연결을 유지하며 서버-클라이언트 통신 가능</p>
</li>
</ul>
<br>

<h3 id="자세한-사항">자세한 사항</h3>
<ul>
<li><a href="https://github.com/YeowonKIM/Knock-Project/blob/dev/src/main/java/com/project/comgle/post/entity/SseEmitters.java">https://github.com/YeowonKIM/Knock-Project/blob/dev/src/main/java/com/project/comgle/post/entity/SseEmitters.java</a></li>
<li><a href="https://github.com/YeowonKIM/Knock-Project/blob/dev/src/main/java/com/project/comgle/post/repository/EmitterRepository.java">https://github.com/YeowonKIM/Knock-Project/blob/dev/src/main/java/com/project/comgle/post/repository/EmitterRepository.java</a></li>
<li><a href="https://github.com/YeowonKIM/Knock-Project/blob/dev/src/main/java/com/project/comgle/post/service/SseService.java">https://github.com/YeowonKIM/Knock-Project/blob/dev/src/main/java/com/project/comgle/post/service/SseService.java</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Python] 백준 4195 친구 네트워크]]></title>
            <link>https://velog.io/@kk_y_oo1/Python-%EB%B0%B1%EC%A4%80-4195-%EC%B9%9C%EA%B5%AC-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC</link>
            <guid>https://velog.io/@kk_y_oo1/Python-%EB%B0%B1%EC%A4%80-4195-%EC%B9%9C%EA%B5%AC-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC</guid>
            <pubDate>Thu, 27 Jul 2023 11:33:57 GMT</pubDate>
            <description><![CDATA[<h1 id="gold-ii-친구-네트워크---4195">[Gold II] 친구 네트워크 - 4195</h1>
<p><a href="https://www.acmicpc.net/problem/4195">문제 링크</a> </p>
<h3 id="성능-요약">성능 요약</h3>
<p>메모리: 61296 KB, 시간: 9092 ms</p>
<h3 id="분류">분류</h3>
<p>자료 구조, 분리 집합, 해시를 사용한 집합과 맵</p>
<h3 id="문제-설명">문제 설명</h3>
<p>민혁이는 소셜 네트워크 사이트에서 친구를 만드는 것을 좋아하는 친구이다. 우표를 모으는 취미가 있듯이, 민혁이는 소셜 네트워크 사이트에서 친구를 모으는 것이 취미이다.</p>

<p>어떤 사이트의 친구 관계가 생긴 순서대로 주어졌을 때, 두 사람의 친구 네트워크에 몇 명이 있는지 구하는 프로그램을 작성하시오.</p>

<p>친구 네트워크란 친구 관계만으로 이동할 수 있는 사이를 말한다.</p>

<h3 id="입력">입력</h3>
 <p>첫째 줄에 테스트 케이스의 개수가 주어진다. 각 테스트 케이스의 첫째 줄에는 친구 관계의 수 F가 주어지며, 이 값은 100,000을 넘지 않는다. 다음 F개의 줄에는 친구 관계가 생긴 순서대로 주어진다. 친구 관계는 두 사용자의 아이디로 이루어져 있으며, 알파벳 대문자 또는 소문자로만 이루어진 길이 20 이하의 문자열이다.</p>

<h3 id="출력">출력</h3>
 <p>친구 관계가 생길 때마다, 두 사람의 친구 네트워크에 몇 명이 있는지 구하는 프로그램을 작성하시오.</p>

<br>


<h3 id="풀이">풀이</h3>
<pre><code class="language-py">test_case = int(input())

# Find(x): 재귀적으로 함수를 호출하며, x의 Root노드를 반환
def find(x):
    if x == parents[x]:
        return x
    else:
        parents[x] = find(parents[x])
        return parents[x]

# Union(x,y): x와 y의 Root노드를 찾고, y의 Root노드를 x로 한다.
def union(x, y):
    x = find(x)
    y = find(y)

    if x != y:
        parents[y] = x
        child_num[x] += child_num[y]


for _ in range(test_case):
    relations = int(input())
    parents = dict()
    child_num = dict()

    for _ in range(relations):
        x, y = input().split()

        if x not in parents:
            parents[x] = x
            child_num[x] = 1
        if y not in parents:
            parents[y] = y
            child_num[y] = 1

        union(x, y)
        print(child_num[find(x)])</code></pre>
<br>

<h3 id="설명">설명</h3>
<ul>
<li><strong>Union-Find의 개념을 알아야 한다.</strong></li>
</ul>
<pre><code>-  처음에 아이디어가 떠오르지 않아서 Union-Find를 사용하면 된다는 힌트를 얻고 아래 사이트를 통해 공부한 뒤 다시 풀이했다.
https://twpower.github.io/115-union-find-disjoint-set
- **Union-Find란?**
    - 서로소 집합(Disjoint Set) 그리고 병합 찾기 집합(Merge Find Set)이라고도 불리며, 여러 서로소 집합의 정보를 저장하고 있는 자료구조
   - 해당 문제에서는 Fred-Barney, Barney-Betty =&gt; Fred-Barney-Betty처럼 관계를 잇기 위해 부모 자식 관계를 활용할 수 있다.</code></pre><br>

<ul>
<li><strong>Dictionary 활용</strong><ul>
<li>parents : {자기 이름:부모} -&gt; 부모는 union 함수를 통해 갱신된다.</li>
<li>child_num : {자기 이름:숫자} -&gt; 상위 노드일수록 숫자에 자식의 수가 늘어남.</li>
</ul>
</li>
</ul>
<br>

<ul>
<li><strong>문제풀이 과정</strong></li>
</ul>
<img src = "https://velog.velcdn.com/images/kk_y_oo1/post/65326abe-7334-4364-912c-1be9a174acda/image.png" width = "60%" height = "60%">  

<img src = "https://velog.velcdn.com/images/kk_y_oo1/post/785fe727-c66e-4333-a516-dae02cc66d0b/image.png" width = "60%" height = "60%">


]]></description>
        </item>
        <item>
            <title><![CDATA[Log]]></title>
            <link>https://velog.io/@kk_y_oo1/Log</link>
            <guid>https://velog.io/@kk_y_oo1/Log</guid>
            <pubDate>Wed, 12 Jul 2023 13:05:01 GMT</pubDate>
            <description><![CDATA[<h3 id="1️⃣-log">1️⃣ Log</h3>
<ul>
<li><strong>로그(log)</strong> : 소프트웨어의 이벤트를 시스템의 상태 및 동작 정보를 시간 경과에 따라 기록하는 것</li>
<li>소프트웨어 개발 과정 혹은 개발 후에 동작상태를 파악하여 문제가 발생했을 때 진단하고 해결하는데 도움이 된다.</li>
<li><strong>로깅(logging)</strong> : 로그를 기록하는 행위</li>
<li>Java 언어에서는 다양한 로깅 라이브러리를 지원한다.</li>
</ul>
<br>


<h3 id="2️⃣-로깅-라이브러리">2️⃣ 로깅 라이브러리</h3>
<ul>
<li><p><strong>log4j</strong></p>
<ul>
<li>Apache의 자바 기반 로깅 프레임 워크로, 콘솔 및 파일 출력의 형태로 로깅이 가능하다.</li>
<li>xml이나 properties로 환경 구축</li>
</ul>
</li>
<li><p><strong>logback</strong></p>
<ul>
<li><p>log4j에 비해 필터링 정책과 기능이 향상되었다.</p>
<p>  ex) 로그인한 경우 logging 등</p>
</li>
<li><p>서버 재시작 없는 자동 리로딩을 지원한다.</p>
</li>
<li><p>Springboot에서 spring-boot-stater-web에 기본 포함되어 있다.</p>
</li>
</ul>
</li>
<li><p><strong>log4j2</strong></p>
<ul>
<li>log4j의 다음 버전으로, logback처럼 필터링 및 자동 리로딩을 지원한다.</li>
<li><strong>logback과의 차이</strong><ul>
<li>Multi Thread 환경에서 비동기 로거(Async Logger)의 경우 다른 로깅 프로엠워크보다 처리량이 더 많고 대기시간이 훨씬 짧다.</li>
<li>람다식 및 Lazy Evaluation을 지원한다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<br> 

<h3 id="3️⃣-slf4j">3️⃣ <strong>slf4j</strong></h3>
<ul>
<li><p>Simple Logging Facade For Java의 약자</p>
</li>
<li><p>자체 로깅 프레임워크가 아니라 logger 추상체로, 다른 로깅 프레임워크가 접근할 수 있도록 도와준다.</p>
<p>  (즉, log4j나 logback 같은 구현체의 인테페이스 역할을 한다.)</p>
</li>
<li><p>slf4j를 이용하면 코드를 일정하게 유지하면서 구현체의 전환을 통해 다른 로깅 프레임워크로의 전환을 쉽고 간단하게 할 수 있다.</p>
</li>
</ul>
<br>

<h3 id="4️⃣-로그-레벨">4️⃣ <strong>로그 레벨</strong></h3>
<table>
<thead>
<tr>
<th>TRACE</th>
<th>모든 메세지를 표시하기 위한 정보</th>
</tr>
</thead>
<tbody><tr>
<td>DEBUG</td>
<td>개발 시 디버그 용도</td>
</tr>
<tr>
<td>INFO</td>
<td>상태 변경, 서비스 동작 상태를 위한 정보</td>
</tr>
<tr>
<td>WARN</td>
<td>실행에는 문제가 없지만 향후 잠재적인 문제를 알려주는 정보</td>
</tr>
<tr>
<td>ERROR</td>
<td>의도하지 않은 오류 발생 정보. 요청을 처리하는 중에 문제가 발생하면 알려준다.</td>
</tr>
<tr>
<td>FATAL</td>
<td>아주 심각한 에러가 발생한 상태</td>
</tr>
</tbody></table>
<br>

<h3 id="5️⃣-로깅-라이브러리-성능-비교">5️⃣ <strong>로깅 라이브러리 성능 비교</strong></h3>
<p><img src="https://velog.velcdn.com/images/kk_y_oo1/post/c8445aa0-a42b-45e0-a516-c126573eb6bc/image.png" alt=""></p>
<ul>
<li>log4j2는 Spring Boot에 기본으로 적용되어있는 logback 이후에 나온 라이브러리로 성능이 더 뛰어나다.</li>
<li>멀티스레드 환경에서 Async Logger의 경우 Logback보다 처리량이 약 18배 더 높고 대기 시간이 훨씬 더 짧다.</li>
<li>성능에 대한 자세한 내용 : <a href="https://logging.apache.org/log4j/2.x/performance.html">https://logging.apache.org/log4j/2.x/performance.html</a></li>
</ul>
<br>



<hr>
<ul>
<li><p><strong>log4j</strong></p>
<ul>
<li><p>콘솔 및 파일 출력의 형태로 로깅을 도와주며 xml, properties로 환경을 구성할 수 있다.</p>
</li>
<li><p>설정 관련 : <a href="https://veneas.tistory.com/entry/Spring-Boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-%EB%A1%9C%EA%B7%B8-%EC%84%A4%EC%A0%95-log4j2">https://veneas.tistory.com/entry/Spring-Boot-스프링-부트-로그-설정-log4j2</a></p>
<pre><code class="language-python">import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

class Log4jLogger {
      private static Logger logger = LogManager.getLogger(Log4jLogger.class);

      void method() {
          logger.info(&quot;info log : {}&quot;, 1);
      }
}</code></pre>
</li>
</ul>
</li>
</ul>
<br>

<p><strong>log4j 구성</strong></p>
<table>
<thead>
<tr>
<th>Logger</th>
<th>출력할 메시지를 Appender에게 전달</th>
</tr>
</thead>
<tbody><tr>
<td>Appender</td>
<td>전달된 로그를 어디에 출력할 것인지 결정(Console, File, JDBC 등)</td>
</tr>
<tr>
<td>Layout</td>
<td>로그를 어떤 형식으로 출력할 것인지 결정</td>
</tr>
</tbody></table>
<hr>
<ul>
<li><p><strong>logback</strong></p>
<ul>
<li><p>SpringBoot에서는 기본 log여서 따로 의존성을 추가할 필요가 없다.</p>
<pre><code class="language-python">import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

class LogbackLogger {
      private static Logger logger = LoggerFactory.getLogger(LogbackLogger.class);

     void method() {
            logger.trace(&quot;Trace&quot;);
            logger.debug(&quot;Debug&quot;);
            logger.info(&quot;Info&quot;);
            logger.warn(&quot;Warn&quot;);
            logger.error(&quot;Error&quot;);
      }
}</code></pre>
</li>
</ul>
</li>
</ul>
<hr>
<p>📂 <strong>참고자료</strong></p>
<p><a href="https://0soo.tistory.com/241">https://0soo.tistory.com/241</a></p>
<p><a href="https://veneas.tistory.com/entry/Spring-Boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-%EB%A1%9C%EA%B7%B8-%EC%84%A4%EC%A0%95-log4j2">https://veneas.tistory.com/entry/Spring-Boot-스프링-부트-로그-설정-log4j2</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Python] 백준 10816]]></title>
            <link>https://velog.io/@kk_y_oo1/%EB%B0%B1%EC%A4%80-10816</link>
            <guid>https://velog.io/@kk_y_oo1/%EB%B0%B1%EC%A4%80-10816</guid>
            <pubDate>Wed, 12 Jul 2023 12:55:06 GMT</pubDate>
            <description><![CDATA[<h1 id="silver-iv-숫자-카드-2---10816">[Silver IV] 숫자 카드 2 - 10816</h1>
<p><a href="https://www.acmicpc.net/problem/10816">문제 링크</a> </p>
<h3 id="성능-요약">성능 요약</h3>
<p>메모리: 115648 KB, 시간: 848 ms</p>
<h3 id="분류">분류</h3>
<p>이분 탐색, 자료 구조, 해시를 사용한 집합과 맵, 정렬</p>
<h3 id="문제-설명">문제 설명</h3>
<p>숫자 카드는 정수 하나가 적혀져 있는 카드이다. 상근이는 숫자 카드 N개를 가지고 있다. 정수 M개가 주어졌을 때, 이 수가 적혀있는 숫자 카드를 상근이가 몇 개 가지고 있는지 구하는 프로그램을 작성하시오.</p>

<h3 id="입력">입력</h3>
 <p>첫째 줄에 상근이가 가지고 있는 숫자 카드의 개수 N(1 ≤ N ≤ 500,000)이 주어진다. 둘째 줄에는 숫자 카드에 적혀있는 정수가 주어진다. 숫자 카드에 적혀있는 수는 -10,000,000보다 크거나 같고, 10,000,000보다 작거나 같다.</p>

<p>셋째 줄에는 M(1 ≤ M ≤ 500,000)이 주어진다. 넷째 줄에는 상근이가 몇 개 가지고 있는 숫자 카드인지 구해야 할 M개의 정수가 주어지며, 이 수는 공백으로 구분되어져 있다. 이 수도 -10,000,000보다 크거나 같고, 10,000,000보다 작거나 같다.</p>

<h3 id="출력">출력</h3>
 <p>첫째 줄에 입력으로 주어진 M개의 수에 대해서, 각 수가 적힌 숫자 카드를 상근이가 몇 개 가지고 있는지를 공백으로 구분해 출력한다.</p>

<br>

<p><img src="https://velog.velcdn.com/images/kk_y_oo1/post/f3eaa992-5901-4f4b-88dc-d1cb9266d62d/image.png" alt=""></p>
<br>

<h3 id="풀이">풀이</h3>
<pre><code class="language-py">n = int(input())
cards = list(map(int, input().split()))
m = int(input())
targets = list(map(int, input().split()))

cnt = {}
for card in cards:
    if card in cnt:
        cnt[card] += 1
    else:
        cnt[card] = 1

for target in targets:
    answer = cnt.get(target)
    if answer == None:
        print(0, end=&quot; &quot;)
    else:
        print(answer, end=&quot; &quot;)</code></pre>
<br>

<h3 id="설명">설명</h3>
<ul>
<li>가지고 있는 카드의 숫자를 key로, 개수를 value로 하는 딕셔너리 cnt를 만든다.</li>
<li>개수를 구하고자 하는 카드의 숫자(target)를 키로 get 하여 value를 가져온다. <br> -&gt; 없으면 0, 있으면 값 출력</li>
</ul>
<br>

<h3 id="추가---파이썬-딕셔너리-내장함수--메서드">추가 - 파이썬 딕셔너리 내장함수 / 메서드</h3>
<ul>
<li><p><strong>내장함수</strong></p>
<ul>
<li>len(dict) : 딕셔너리 총 요소 길이</li>
<li>str(dict) : 사전을 문자열로 반환</li>
<li>type(variable) : 변수의 형태</li>
</ul>
</li>
<li><p><strong>메서드</strong></p>
<ul>
<li>dict.key() : 사전의 키 목록</li>
<li>dict.values() : 사전의 값 목록</li>
<li>dict.items() :  사전의 (key,value) 튜플 목록</li>
<li>dict.clear() : 사전의 모든 요소 제거</li>
<li>dict.copy() : 사전 복사</li>
<li>dict.fromkeys() : seq, value로 사전 생성</li>
<li>dict.get(key, default=None) : 키에 저장된 value 불러오기</li>
<li>dict.setdefault(key, default=None) : dict.get()과 유사하며,해당 key가 딕셔너리 내에 존재하면 value를 반환한다. 만약 존재하지 않으면 None을 반환한다. </li>
<li>dict.uptate(dict2) : 기존 딕셔너리에 dict2 추가</li>
</ul>
</li>
</ul>
<br>

<h3 id="추가2---딕셔너리-기본값-처리">추가2 - 딕셔너리 기본값 처리</h3>
<p><a href="https://www.daleseo.com/python-collections-defaultdict/">https://www.daleseo.com/python-collections-defaultdict/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Python] 백준 1874]]></title>
            <link>https://velog.io/@kk_y_oo1/%EB%B0%B1%EC%A4%80-1874</link>
            <guid>https://velog.io/@kk_y_oo1/%EB%B0%B1%EC%A4%80-1874</guid>
            <pubDate>Mon, 10 Jul 2023 13:01:23 GMT</pubDate>
            <description><![CDATA[<h1 id="silver-ii-스택-수열---1874">[Silver II] 스택 수열 - 1874</h1>
<p><a href="https://www.acmicpc.net/problem/1874">문제 링크</a> </p>
<h3 id="성능-요약">성능 요약</h3>
<p>메모리: 32840 KB, 시간: 3908 ms</p>
<h3 id="분류">분류</h3>
<p>자료 구조, 스택</p>
<h3 id="문제-설명">문제 설명</h3>
<p>스택 (stack)은 기본적인 자료구조 중 하나로, 컴퓨터 프로그램을 작성할 때 자주 이용되는 개념이다. 스택은 자료를 넣는 (push) 입구와 자료를 뽑는 (pop) 입구가 같아 제일 나중에 들어간 자료가 제일 먼저 나오는 (LIFO, Last in First out) 특성을 가지고 있다.</p>

<p>1부터 n까지의 수를 스택에 넣었다가 뽑아 늘어놓음으로써, 하나의 수열을 만들 수 있다. 이때, 스택에 push하는 순서는 반드시 오름차순을 지키도록 한다고 하자. 임의의 수열이 주어졌을 때 스택을 이용해 그 수열을 만들 수 있는지 없는지, 있다면 어떤 순서로 push와 pop 연산을 수행해야 하는지를 알아낼 수 있다. 이를 계산하는 프로그램을 작성하라.</p>

<h3 id="입력">입력</h3>
 <p>첫 줄에 n (1 ≤ n ≤ 100,000)이 주어진다. 둘째 줄부터 n개의 줄에는 수열을 이루는 1이상 n이하의 정수가 하나씩 순서대로 주어진다. 물론 같은 정수가 두 번 나오는 일은 없다.</p>

<h3 id="출력">출력</h3>
 <p>입력된 수열을 만들기 위해 필요한 연산을 한 줄에 한 개씩 출력한다. push연산은 +로, pop 연산은 -로 표현하도록 한다. 불가능한 경우 NO를 출력한다.</p>

<p><img src="https://velog.velcdn.com/images/kk_y_oo1/post/9a3d1db8-c13f-4398-ad80-3a30b7d05333/image.png" alt="">
<img src="https://velog.velcdn.com/images/kk_y_oo1/post/1071aa18-5d79-4aa2-b058-b76a7c320497/image.png" alt=""></p>
<br>

<h3 id="풀이">풀이</h3>
<pre><code class="language-py">n = int(input())
stack_seq = []
answer = []
cnt = 1
check = True

for i in range(n):
    num = int(input())
    while cnt &lt;= num:
        stack_seq.append(cnt)
        answer.append(&quot;+&quot;)
        cnt +=1

    if stack_seq[-1] == num:
        stack_seq.pop()
        answer.append(&quot;-&quot;)
    else:
        check = False
        break;

if check == False:
    print(&quot;NO&quot;)
else:
    for a in answer:
        print(a)</code></pre>
<br>

<h3 id="설명">설명</h3>
<ol>
<li>입력된 수(num)가 될 때까지 cnt를 증가시키면서 스택에 push한다. 이 때 num이 입력과 같다면 pop한다.</li>
<li>만약 for문을 다 거치지 못하면 입력과 같은 스택 수열을 만들 수 없으므로 check를 False로 바꾼다.</li>
<li>check가 False인 경우는 NO를, True인 경우는 답으로 추가한 리스트의 요소들을 하나씩 출력한다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Data Structure] Stack]]></title>
            <link>https://velog.io/@kk_y_oo1/Stack</link>
            <guid>https://velog.io/@kk_y_oo1/Stack</guid>
            <pubDate>Mon, 10 Jul 2023 06:58:08 GMT</pubDate>
            <description><![CDATA[<h2 id="1️⃣-stack-이란">1️⃣ <strong>Stack</strong> 이란?</h2>
<br>


<ul>
<li><p><strong>LIFO</strong> (Last in, First out) / <strong>FILO</strong> (First in, Last out) <br></p>
<p>  cf) Queue - FIFO</p>
 <br> 
</li>
<li><p><strong>활용</strong></p>
<ul>
<li><p>컴퓨터 내부의 프로세스 함수 동작 방식</p>
</li>
<li><p><strong>Process Stack</strong></p>
<p>  cf) 재귀 1000번까지 호출 가능</p>
<pre><code class="language-python">  # 재귀함수
  def recursion(n):
      if n &lt; 0:
          print(&quot;========&quot;)
      else:
          print(n)
          recursive(n-1)
          print(&quot;return&quot;, n)

  recursive(3)</code></pre>
<table>
<thead>
<tr>
<th>Data</th>
<th>Process</th>
</tr>
</thead>
<tbody><tr>
<td>-1</td>
<td>recursion(-1)</td>
</tr>
<tr>
<td>0</td>
<td>recursion(0)</td>
</tr>
<tr>
<td>1</td>
<td>recursion(1)</td>
</tr>
<tr>
<td>2</td>
<td>recursion(2)</td>
</tr>
<tr>
<td>3</td>
<td>recursion(3)</td>
</tr>
</tbody></table>
</li>
</ul>
</li>
</ul>
<br>


<img src = "https://velog.velcdn.com/images/kk_y_oo1/post/86affe82-6df2-4f04-939e-f26a29529bcf/image.png" width="100px" height="200px" >



<br>


<ul>
<li>push() : 데이터 넣기</li>
<li>pop() : 데이터 꺼내기</li>
</ul>
<br>

<ul>
<li><strong>장점</strong><ul>
<li>데이트의 저장 및 읽기 속도가 빠르다.</li>
<li>단순한 구조이기 때문에 구현이 쉽다.</li>
</ul>
</li>
<li><strong>단점</strong><ul>
<li>일반적으로 데이터의 최대 개수를 지정해야 한다.</li>
<li>미리 저장 공간을 확보하므로, 저장 공간이 낭비될 수 있다.</li>
</ul>
</li>
</ul>
<p><br><br></p>
<h2 id="2️⃣-파이썬에서의-스택-리스트-활용">2️⃣ <strong>파이썬에서의 스택 (리스트 활용)</strong></h2>
<pre><code class="language-python">stack_list = list()

stack_list.append(&quot;season1&quot;)
stack_list.append(&quot;season2&quot;)

stack_list           # 출력 : [&#39;season1&#39;, &#39;season2&#39;]

stack_list.pop()     # 출력 : &#39;season2&#39;</code></pre>
<br>

<pre><code class="language-python">stack = list()

def push(x):
    stack.append(x)

def pop():
    x = stack[-1]
    del stack[-1]
    return x

for i in range(1, 6):
    push(i)

pop()  # 출력 : 5</code></pre>
<h2 id="brbr"><Br><br></h2>
<p>사진 출처 : <a href="https://yoongrammer.tistory.com/45">https://yoongrammer.tistory.com/45</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Data Structure] Queue]]></title>
            <link>https://velog.io/@kk_y_oo1/Queue</link>
            <guid>https://velog.io/@kk_y_oo1/Queue</guid>
            <pubDate>Mon, 10 Jul 2023 06:09:40 GMT</pubDate>
            <description><![CDATA[<h2 id="queue란">Queue란?</h2>
<ul>
<li><p><strong>FIFO</strong> : First in, First out</p>
<ul>
<li><p>가장 먼저 넣은 데이터를 가장 먼저 꺼낼 수 있는 구조</p>
</li>
<li><p>ex) 가장 먼저 줄 선 사람이 가장 먼저 입장</p>
</li>
<li><p>스택(<strong>LILO</strong>)와 반대</p>
<img src = "https://velog.velcdn.com/images/kk_y_oo1/post/72deed1f-b85f-497e-87c1-e912f8ce0bac/image.png" width="200" height="100">


</li>
</ul>
</li>
</ul>
<br>


<ul>
<li><p>용어</p>
<ul>
<li><p><strong>Enqueue</strong> : 큐에 데이터를 넣기</p>
</li>
<li><p><strong>Dequeue</strong> : 큐에서 데이터 꺼내기</p>
<pre><code class="language-python"># 리스트로 큐의 enqueue, dequeue 구현

queue_list = list()

def enqueue(data):
  queue_list.append(data)

def dequeue():
  data = queue_list[0]
  del queue_list[0]
  return data

for i in range(10):
  enqueue(i)

dequeue()</code></pre>
<p><br><br></p>
</li>
</ul>
</li>
</ul>
<h2 id="파이썬-queue-라이브러리">파이썬 Queue 라이브러리</h2>
<ul>
<li><p>파이썬에서는 큐 자료구조로 Queue(), LifoQueue(), PriorityQueue()를 제공한다.</p>
<ul>
<li><p><strong>Queue()</strong> : 일반적인 큐</p>
</li>
<li><p><strong>LifoQueue()</strong> : Last in, First out (=stack)</p>
</li>
<li><p><strong>PriorityQueue()</strong> : 데이터마다 우선순위를 넣어, 해당 기준에서 높은 순으로 데이터를 출력한다.</p>
<br>
</li>
</ul>
</li>
<li><p><strong>Queue()</strong></p>
<pre><code class="language-python">  # Queue()로 큐 만들기
  import queue

  queue1 = queue.Queue()

  # 큐에 데이터 넣기
  queue1.put(&quot;ruffy&quot;)
  queue1.put(1)

  # 큐의 사이즈 확인
  queue1.qsize()  # 출력 : 2

  # 큐의 데이터 가져오기 (0번 인덱스 가져오고 지워짐)
  queue1.get()    # 출력 : ruffy</code></pre>
</li>
</ul>
<ul>
<li><p><strong>LifoQueue()</strong></p>
<pre><code class="language-python">  # LifoQueue()
  import queue
  queue2 = queue.LifoQueue()

  # Lifo 큐에 데이터 넣기
  queue2.put(&quot;Robin&quot;)
  queue2.put(52)

  # Lifo 큐의 사이즈 확인
  queue2.qsize()  # 출력 : 2

  # Lifo 큐의 데이터 가져오기 (마지막 인덱스 가져오고 지워짐)
  queue2.get()    # 출력 : 52</code></pre>
</li>
</ul>
<ul>
<li><p><strong>PriortiyQueue()</strong></p>
<pre><code class="language-python">  import queue
  queue3 = queue.PriorityQueue()

  # Priortiy 큐에 데이터 넣기 : (우선순위, 데이터), 튜플 형식
  # 우선순위가 높을 수록 숫자가 작음
  queue3.put((1, &quot;strawberry&quot;))
  queue3.put((10, &quot;banana&quot;))
  queue3.put((5, &quot;peach&quot;))

  # Priortiy 큐의 사이즈 확인
  queue3.qsize()  # 출력 : 3

  # Priortiy 큐에 데이터 넣기 
  queue3.get()    # 출력 : (1, &#39;strawberry&#39;)</code></pre>
</li>
</ul>
<br>

<h2 id="큐의-활용">큐의 활용</h2>
<ul>
<li>멀티 테스킹을 위한 프로세스 스케줄링 방식</li>
</ul>
<hr>
<p><strong>📌 자료 구조 연습</strong>
<a href="https://visualgo.net/en/list">https://visualgo.net/en/list</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] 자바 정규 표현식 (Pattern, Matcher)]]></title>
            <link>https://velog.io/@kk_y_oo1/%EC%9E%90%EB%B0%94-%EC%A0%95%EA%B7%9C-%ED%91%9C%ED%98%84%EC%8B%9D-Pattern-Matcher</link>
            <guid>https://velog.io/@kk_y_oo1/%EC%9E%90%EB%B0%94-%EC%A0%95%EA%B7%9C-%ED%91%9C%ED%98%84%EC%8B%9D-Pattern-Matcher</guid>
            <pubDate>Tue, 27 Jun 2023 11:57:17 GMT</pubDate>
            <description><![CDATA[<br>


<h3 id="1️⃣-정규표현식-regular-expression-이란">1️⃣ 정규표현식 (Regular Expression) 이란?</h3>
<ul>
<li><p>컴퓨터 과학의 정규언어로부터 유래한 것으로, 특정한 규칙을 가진 문자열의 집합을 표현하기 위해 쓰이는 형식 언어</p>
</li>
<li><p>입력값을 정해진 형식에 맞는지 검증할 때 사용</p>
<p>  ex) 전화번호, 주민등록번호, 이메일 등 사용자가 그 형식대로 입력했는지 검증해야 하는 경우</p>
</li>
</ul>
<br>

<h3 id="2️⃣-자주-사용하는-정규-표현식">2️⃣ 자주 사용하는 정규 표현식</h3>
<table>
<thead>
<tr>
<th>정규 표현식</th>
<th>숫자</th>
</tr>
</thead>
<tbody><tr>
<td>^[0-9]*$</td>
<td>숫자</td>
</tr>
<tr>
<td>^[a-zA-Z]*$</td>
<td>영문자</td>
</tr>
<tr>
<td>^[가-힣]*$</td>
<td>한글</td>
</tr>
<tr>
<td>\w+@\w+\.\w+(\.\w+)?</td>
<td>이메일</td>
</tr>
<tr>
<td>^\d{2,3}-\d{3,4}-\d{4}$</td>
<td>전화번호</td>
</tr>
<tr>
<td>\d{6} - [1-4]\d{6}</td>
<td>주민등록번호</td>
</tr>
<tr>
<td>^\d{3}-\d{2}$</td>
<td>우편번호</td>
</tr>
</tbody></table>
<br>

<h3 id="3️⃣-pattern-클래스">3️⃣ Pattern 클래스</h3>
<ul>
<li><p>정규 표현식에 대상 문자열을 검증하는 기능</p>
<ul>
<li>java.util.regex.Pattern 클래스의 matches()메소드 활용</li>
</ul>
</li>
<li><p><strong>matches() 메서드</strong></p>
<ul>
<li><p>첫번째 매개값은 정규표현식이고 두번째 매개값은 검증 대상 문자열이다.</p>
</li>
<li><p>검증 후 대상문자열이 정규표현식과 일치하면 true, 그렇지 않다면 false값을 리턴한다.</p>
<pre><code class="language-java">import java.util.regex.Pattern;

public class RegexExample {
  public static void main(String[] args)  {

          String pattern = &quot;^[0-9]*$&quot;; //숫자만
          String val = &quot;123456789&quot;; //대상문자열

          boolean regex = Pattern.matches(pattern, val);
          System.out.println(regex);
  }
}</code></pre>
</li>
</ul>
</li>
</ul>
<h3 id="4️⃣-pattern-클래스의-주요-메서드">4️⃣ Pattern 클래스의 주요 메서드</h3>
<ul>
<li>compile(String regex) : 주어진 정규표현식으로부터 패턴을 만든다.</li>
<li>matcher(CharSequence input) : 대상 문자열이 패턴과 일치할 경우 true를 반환한다.</li>
<li>asPredicate() : 문자열을 일치시키는 데 사용할 수있는 술어를 작성한다.</li>
<li>pattern() : 컴파일된 정규표현식을 String 형태로 반환한다.</li>
<li>split(CharSequence input) : 문자열을 주어진 인자값 CharSequence 패턴에 따라 분리한다.</li>
</ul>
<br>

<h3 id="5️⃣-matcher-클래스">5️⃣ Matcher 클래스</h3>
<ul>
<li>Matcher 클래스는 대상 문자열의 패턴을 해석하고 주어진 패턴과 일치하는지 판별할 때 주로 사용된다.</li>
<li>Matcher 클래스의 입력값으로는 CharSequence라는 새로운 인터페이스가 사용되는데, 이를 통해 다양한 형태의 입력 데이터로부터 문자 단위의 매칭 기능을 지원 받을 수 있다.</li>
<li>Matcher객체는 Pattern객체의 matcher() 메소드를 호출하여 받아올 수 있다.</li>
</ul>
<pre><code class="language-java">import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexExample {
    public static void main(String[] args)  {
            Pattern pattern = Pattern.compile(&quot;^[a-zA-Z]*$&quot;); //영문자만
            String val = &quot;abcdef&quot;; //대상문자열

            Matcher matcher = pattern.matcher(val);
            System.out.println(matcher.find());
    }
}</code></pre>
<br>

<h3 id="6️⃣-matcher-클래스의-주요-메서드">6️⃣ Matcher 클래스의 주요 메서드</h3>
<ul>
<li>matches() : 대상 문자열과 패턴이 일치할 경우 true 반환한다.</li>
<li>find() : 대상 문자열과 패턴이 일치하는 경우 true를 반환하고, 그 위치로 이동한다.</li>
<li>find(int start) : start위치 이후부터 매칭검색을 수행한다.</li>
<li>start() : 매칭되는 문자열 시작위치 반환한다.</li>
<li>start(int group) : 지정된 그룹이 매칭되는 시작위치 반환한다.</li>
<li>end() : 매칭되는  문자열 끝 다음 문자위치 반환한다.</li>
<li>end(int group) : 지정되 그룹이 매칭되는 끝 다음 문자위치 반환한다.</li>
<li>group() : 매칭된 부분을 반환한다.</li>
<li>group(int group) : 매칭된 부분중 group번 그룹핑 매칭부분 반환한다.</li>
<li>groupCount() : 패턴내 그룹핑한(괄호지정) 전체 갯수를 반환한다.</li>
</ul>
<br>

<h3 id="7️⃣-유효성-검사">7️⃣ 유효성 검사</h3>
<pre><code class="language-java">import java.util.regex.Pattern;

public class RegexExample {
    public static void main(String[] args)  {
          String name = &quot;홍길동&quot;;
          String tel = &quot;010-1234-5678&quot;;
          String email = &quot;test@naver.com&quot;;

          //유효성 검사
          boolean name_check = Pattern.matches(&quot;^[가-힣]*$&quot;, name);
          boolean tel_check = Pattern.matches(&quot;^01(?:0|1|[6-9])-(?:\\d{3}|\\d{4})-\\d{4}$&quot;, tel);
          boolean email_check = Pattern.matches(&quot;\\w+@\\w+\\.\\w+(\\.\\w+)?&quot;, email);

          //출력
          System.out.println(&quot;이름 : &quot; + name_check);      // true
          System.out.println(&quot;전화번호 : &quot; + tel_check);    // true
          System.out.println(&quot;이메일 : &quot; + email_check);   // true
    }
}</code></pre>
<br>

<h3 id="8️⃣-정규-표현식-문법">8️⃣ 정규 표현식 문법</h3>
<table>
<thead>
<tr>
<th>표현식</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>^</td>
<td>문자열 시작</td>
</tr>
<tr>
<td>$</td>
<td>문자열 종료</td>
</tr>
<tr>
<td>.</td>
<td>임의의 한 문자 (단 ₩은 넣을 수 없음)</td>
</tr>
<tr>
<td>*</td>
<td>앞 문자가 없을 수도 무한대로 많을 수도 있다.</td>
</tr>
<tr>
<td>+</td>
<td>앞 문자가 하나 이상</td>
</tr>
<tr>
<td>?</td>
<td>앞 문자가 없거나 하나 있다.</td>
</tr>
<tr>
<td>[ ]</td>
<td>- 문자의 집합이나 범위를 나타낸다. <br> - 두 문자 사이는 - 기호로 범위를 나타낸다.<br> - [] 내에서 ^가 선행하여 존재하면 not을 나타낸다.</td>
</tr>
<tr>
<td>{ }</td>
<td>횟수 혹은 범위를 나타낸다.</td>
</tr>
<tr>
<td>( )</td>
<td>소괄호 안의 문자를 하나의 문자로 인식</td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td>₩</td>
<td>정규표현식에서 ₩는 확장문자 (역슬래시 다음에 일반 문자가 오면 특수 문자로 취급하고, 특수문자가 오면 그 문자 자체를 의미한다.)</td>
</tr>
<tr>
<td>₩b</td>
<td>단어의 경계</td>
</tr>
<tr>
<td>₩B</td>
<td>단어가 아닌 것에 대한 경계</td>
</tr>
<tr>
<td>₩A</td>
<td>입력의 시작 부분</td>
</tr>
<tr>
<td>₩G</td>
<td>이전 매치의 끝</td>
</tr>
<tr>
<td>₩Z</td>
<td>입력의 끝이지만 종결자가 있는 경우</td>
</tr>
<tr>
<td>₩z</td>
<td>입력의 끝</td>
</tr>
<tr>
<td>₩s</td>
<td>공백 문자</td>
</tr>
<tr>
<td>₩S</td>
<td>공백 문자가 아닌 나머지 문자</td>
</tr>
<tr>
<td>₩w</td>
<td>알파벳이나 숫자</td>
</tr>
<tr>
<td>₩W</td>
<td>알파벳이나 숫자를 제외한 문자</td>
</tr>
<tr>
<td>₩d</td>
<td>숫자 [0-9]와 동일</td>
</tr>
<tr>
<td>₩D</td>
<td>숫자를 제외한 모든 문자</td>
</tr>
<tr>
<td>(?!)</td>
<td>앞 부분에 (?!) 옵션을 넣어주면 대소문자를 구분하지 않는다.</td>
</tr>
</tbody></table>
<br>

<h3 id="9️⃣-예시-코드--java-next-step--ch2-">9️⃣ 예시 코드 ( java-next-step / ch2 )</h3>
<pre><code class="language-java">import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static org.apache.logging.log4j.util.Strings.isBlank;

public class StringCalculator {
    public int add(String text) {
        if(isBlank(text)) return 0;
        return sum(toInts(split(text)));
    }

    private boolean isBlank(String text) {
        return text == null || text.isEmpty();
    }

    private String[] split(String text) {
        Matcher m = Pattern.compile(&quot;//(.)\n(.*)&quot;).matcher(text);
        if(m.find()) {
            String customDelimeter = m.group(1);
            return m.group(2).split(customDelimeter);
        }
        return text.split(&quot;,|:&quot;);
    }</code></pre>
<p><br><br></p>
<p><strong>📂 참고자료</strong></p>
<p><a href="https://coding-factory.tistory.com/529">https://coding-factory.tistory.com/529</a></p>
]]></description>
        </item>
    </channel>
</rss>