<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>코모동현</title>
        <link>https://velog.io/</link>
        <description>생각은 깊게, 실행은 단순하게.</description>
        <lastBuildDate>Sat, 05 Jul 2025 17:04:17 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>코모동현</title>
            <url>https://velog.velcdn.com/images/comodoking_0128/profile/83cbe48c-19fb-4b4a-bb67-5e9c80e9f0c3/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 코모동현. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/comodoking_0128" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[나는 어떤 역량을 키워야하는가?]]></title>
            <link>https://velog.io/@comodoking_0128/%EB%82%98%EB%8A%94-%EC%96%B4%EB%96%A4-%EC%97%AD%EB%9F%89%EC%9D%84-%ED%82%A4%EC%9B%8C%EC%95%BC%ED%95%98%EB%8A%94%EA%B0%80</link>
            <guid>https://velog.io/@comodoking_0128/%EB%82%98%EB%8A%94-%EC%96%B4%EB%96%A4-%EC%97%AD%EB%9F%89%EC%9D%84-%ED%82%A4%EC%9B%8C%EC%95%BC%ED%95%98%EB%8A%94%EA%B0%80</guid>
            <pubDate>Sat, 05 Jul 2025 17:04:17 GMT</pubDate>
            <description><![CDATA[<p>오만하게 들릴 수 있지만, 현재의 나는 꽤 괜찮은 방향으로 성장하고 있다고 생각한다.
그리고 내가 못하는 영역도 AI를 통해 빠르게 학습하여 적용할 수 있는 시대이다.
기술적 격차는 점점 빠르게 메워진다.</p>
<p>그렇다면 다음으로 키워야 할 역량은 무엇일까?</p>
<h3 id="모르는-사람들과-프로젝트를-해볼-것">모르는 사람들과 프로젝트를 해볼 것</h3>
<p>내가 잘 아는 사람들과만 프로젝트를 하면 사고방식이 고착화되기 쉽다. 익숙한 사람과의 작업은 의사소통이 빠르고 편하지만, 그만큼 새로운 관점에 둔감해질 수 있다. 처음 보는 사람들과 협업하면, 내가 당연하다고 여긴 방식이 그렇지 않을 수 있음을 체감하게 된다. </p>
<h3 id="개발-문외한에게-백엔드-지식-설명해볼-것">개발 문외한에게 백엔드 지식 설명해볼 것</h3>
<p>현재의 나는 부소마고를 재학 중이기에, 주변인들과 개발과 관련된 대화를 할 때, 불편함을 별로 겪지 않는다.
하지만 개발자와만 이야기하면, 기술적인 언어를 당연하게 쓰게 된다. 하지만 현업에서는 개발자와만 대화하는가? 아니다.
기획자, 디자이너, 마케터, 때론 고객과도 직접 소통해야 한다.
기술적인 설명을 그들의 언어로 풀어내지 못하면, 오해가 생기고 결국 잘못된 방향으로 구현될 수 있다.
내가 만든 시스템의 구조와 의도를 비개발자도 이해할 수 있게 설명할 수 있어야, 진짜로 내가 그 기술을 이해하고 있는 것이다.</p>
<h1 id="앞으로의-나">앞으로의 나</h1>
<p>나는 앞으로 위에서 설명한 2가지를 실천하려 한다.</p>
<p>기술만으로는 한계가 있다. 같은 코드라도, 사람을 설득할 수 있는 사람과 그렇지 못한 사람의 결과는 다르다. 결국 도메인을 이해하고, 문제를 정확하게 정의하고, 커뮤니케이션을 통해 합의를 이끌어낼 수 있는 사람이 강하다.</p>
<p>전공자냐 아니냐는 중요하지 않다. 도메인을 깊게 이해하지 않으면, 전공자도 기술자에 머무르고 만다. 문제를 정의하고 해석할 줄 아는 사람만이 진짜 백엔드다.</p>
<p>결국 말하고 싶은 것은 하나다.</p>
<h4 id="소프트-스킬을-키울-것">소프트 스킬을 키울 것</h4>
<p>오늘은 사고가 확장된 것만 같아 기분이 좋은 날이다.
코드를 잘 짜는 것에서 멈추지 않고, 이제는 그 코드가 어떤 사람들과 어떤 문제를 풀기 위해 존재하는지를 더 넓게 바라보게 된 날이다.
성장에는 끝이 없다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[백엔드 개발자에게 SQL과 ORM은 어떤 의미인가?]]></title>
            <link>https://velog.io/@comodoking_0128/%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%97%90%EA%B2%8C-SQL%EA%B3%BC-ORM%EC%9D%80-%EC%96%B4%EB%96%A4-%EC%9D%98%EB%AF%B8%EC%9D%B8%EA%B0%80</link>
            <guid>https://velog.io/@comodoking_0128/%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%97%90%EA%B2%8C-SQL%EA%B3%BC-ORM%EC%9D%80-%EC%96%B4%EB%96%A4-%EC%9D%98%EB%AF%B8%EC%9D%B8%EA%B0%80</guid>
            <pubDate>Mon, 19 May 2025 13:22:21 GMT</pubDate>
            <description><![CDATA[<h3 id="orm이-전부일까-sql을-잊은-백엔드는-어떻게-무너지는가">ORM이 전부일까? SQL을 잊은 백엔드는 어떻게 무너지는가</h3>
<blockquote>
<h4 id="그들은-객체를-신처럼-숭배했다">그들은 객체를 신처럼 숭배했다.</h4>
<p>그들은 객체의 나라에서 태어나
클래스와 메서드를 경전처럼 배웠다.
그리고 외쳤다.</p>
</blockquote>
<hr>
<p>“관계형 데이터베이스? 그것은 과거의 유물이다.”</p>
<hr>
<p>그들은 눈부신 추상화의 탑을 올렸다.
서비스, 리포지토리, 엔티티…
의존성과 패턴으로 치장된 신전에서
SQL 따위는 입에 담지도 않았다.
<strong>그러나 탑은 무너졌다.</strong>
JOIN 하나로 끝날 것을,
API 세 번 호출하며 CPU를 태웠기 때문이다.</p>
<h4 id="1-sql-백엔드-개발의-바닥을-이루는-언어">1. SQL: 백엔드 개발의 바닥을 이루는 언어</h4>
<p>SQL은 Structured Query Language의 줄임말로, 관계형 데이터베이스에서 데이터를 조작하고 정의하기 위한 언어다. 백엔드 개발자는 데이터와 떨어질 수 없다. 로그인부터 게시물 작성, 통계 집계까지 대부분의 기능은 결국 DB와의 상호작용을 필요로 한다.</p>
<p>이 상호작용의 핵심에 SQL이 있다. 흔한 예를 들면 다음과 같다:</p>
<pre><code>조회: 특정 사용자의 게시글 목록을 최신순으로 정렬해서 가져오기
삽입: 새로운 회원 정보를 DB에 저장
성능 튜닝: 느린 API 응답 시간의 원인을 SQL 쿼리에서 찾고 인덱스를 추가</code></pre><p>SQL을 잘 모르면, ORM이 생성하는 쿼리를 무작정 신뢰하게 된다. 이런 신뢰는 위험하다.</p>
<p>SQL은 백엔드 개발자의 &#39;도구&#39;라기보단 &#39;기초 체력&#39;이다.</p>
<h4 id="2-orm-백엔드-생산성을-높이는-강력한-추상화-도구">2. ORM: 백엔드 생산성을 높이는 강력한 추상화 도구</h4>
<p>ORM은 Object-Relational Mapping의 약자로, 객체와 테이블 간의 매핑을 통해 SQL 없이도 DB를 다룰 수 있게 해주는 기술이다. 대표적으로 Java 진영에선 JPA(Hibernate), Python에선 SQLAlchemy 등이 있다.</p>
<p>ORM이 제공하는 가장 큰 장점은 다음과 같다:</p>
<pre><code>생산성: 반복적인 SQL 작성 없이 객체를 다루듯 데이터를 조작할 수 있음
유지보수성: 도메인 중심 설계를 할 수 있어 코드가 더 읽기 쉬워짐
추상화: DBMS에 독립적인 코드 작성 가능 (물론 완전히는 아님)</code></pre><p>내가 JPA를 처음 썼을 땐 단순한 CRUD 기능을 너무 편하게 짤 수 있어서 감탄했었다. 특히 @Entity나 @Repository 같은 어노테이션만 붙이면 작동하는 구조는 학습 비용 대비 효율이 높다. 이런 추상화는 대규모 프로젝트에서 개발 속도와 안정성을 동시에 높여준다.</p>
<h4 id="3-orm의-한계-모든-것을-추상화할-순-없다">3. ORM의 한계: 모든 것을 추상화할 순 없다</h4>
<p>하지만 ORM은 만능이 아니다. 특히 성능 측면에선 오히려 문제를 일으킬 수 있다.</p>
<pre><code>N+1 문제: 지연 로딩에서 잘못된 fetch 전략으로 인해 수십 수백 개의 쿼리가 발생
복잡한 쿼리: 집계 함수, 윈도우 함수, 복잡한 조건부 조인 등은 ORM으로 작성하기 불편하거나 불가능
디버깅 어려움: 쿼리가 자동 생성되기 때문에 성능 이슈가 있을 때 원인 파악이 어렵다</code></pre><p>ORM만으로 모든 것을 해결하려다 보면, 결국 기술이 아니라 구조에 발목 잡히는 상황이 생긴다.
ORM에 대한 이해도가 작으면 여러 문제가 발생할 수 있다.
예를 들어, 컬렉션(리스트)형태의 객체를 조회할 때 N+1 문제를 해결하기 위해 단순 fetch join을 사용하게 되면 카티션 곱 문제가 발생하게 된다. 따라서 @Fetch(FetchMode.SUBSELECT) 를 사용하면 이러한 문제를 해결할 수 있다.</p>
<p><strong>위 문제처럼 ORM은 객체를 다루는 기술이기 떄문에 이해도가 부족하면 성능 이슈가 발생할 수 있다.</strong></p>
<h4 id="4-sql과-orm을-균형-있게-쓰는-법">4. SQL과 ORM을 균형 있게 쓰는 법</h4>
<p>가장 현실적인 접근은 ORM과 SQL을 &#39;상황에 따라&#39; 조화롭게 활용하는 것이다.</p>
<p>단순 CRUD → ORM 사용<br>복잡한 통계, 집계 → Native Query
성능 최적화 → JPQL + Index + 튜닝된 SQL</p>
<p>예를 들어 Spring JPA에선 @Query(nativeQuery = true)로 SQL을 직접 쓸 수 있고, 필요하면 JdbcTemplate도 함께 쓸 수 있다.</p>
<p>결국 중요한 건 SQL을 잘 알고, ORM을 전략적으로 쓸 수 있는 능력이다. ORM은 뼈대를 세우기 좋지만, 그 뼈대가 어디까지 유연하게 움직일 수 있는지는 SQL 실력에 달려 있다.</p>
<h4 id="마무리">마무리</h4>
<p>정리하자면, 백엔드 개발자에게 SQL은 기초 체력이고, ORM은 도구다. 둘 다 알아야 한다. 특히 MSA 구조나 고성능 API, 복잡한 데이터 흐름을 다루는 상황이라면 더더욱 그렇다. <strong>ORM은 결국 SQL 위에 얹어진 얇은 추상화에 불과하니까.</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SOLID 원칙, 객체지향 설계를 단단하게 만드는 다섯 가지 원칙]]></title>
            <link>https://velog.io/@comodoking_0128/SOLID-%EC%9B%90%EC%B9%99-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%EC%84%A4%EA%B3%84%EB%A5%BC-%EB%8B%A8%EB%8B%A8%ED%95%98%EA%B2%8C-%EB%A7%8C%EB%93%9C%EB%8A%94-%EB%8B%A4%EC%84%AF-%EA%B0%80%EC%A7%80-%EC%9B%90%EC%B9%99</link>
            <guid>https://velog.io/@comodoking_0128/SOLID-%EC%9B%90%EC%B9%99-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%EC%84%A4%EA%B3%84%EB%A5%BC-%EB%8B%A8%EB%8B%A8%ED%95%98%EA%B2%8C-%EB%A7%8C%EB%93%9C%EB%8A%94-%EB%8B%A4%EC%84%AF-%EA%B0%80%EC%A7%80-%EC%9B%90%EC%B9%99</guid>
            <pubDate>Mon, 21 Apr 2025 11:51:33 GMT</pubDate>
            <description><![CDATA[<p>객체지향 설계를 잘 한다는 건 결국 &quot;변화에 유연하게 대응할 수 있는 시스템을 만든다&quot;는 말과 같다.
A가 바뀌었는데 B, C, D, E...가 줄줄이 다 깨지면, 그건 <strong>소프트웨어가 아니라 그냥 딱딱웨어다.</strong></p>
<p>그런 의미에서 객체지향 설계를 잘 하고 싶다면, SOLID 원칙은 한 번쯤 제대로 고민해볼 만한 기준점이다.
지금부터 SOLID 다섯 가지 원칙을 간단한 예시 코드와 함께 정리해본다.
모든 걸 완벽하게 지키는 건 어렵다. 하지만 최대한 의식하고 적용하려고 노력하는 것, 그게 실력 향상의 길이라고 생각한다.</p>
<h1 id="1-srp---단일-책임-원칙">1. SRP - 단일 책임 원칙</h1>
<p><strong>하나의 클래스는 하나의 책임만 가져야 한다.</strong></p>
<p>여러 기능을 하나의 클래스에 우겨 넣으면 나중에 수정할 때 다른 기능까지 깨질 가능성이 높다. 유지보수도 어렵고 테스트도 힘들어진다.
책임을 나누면 기능별로 독립적인 수정이 가능해지고, 테스트 코드도 작게 쪼갤 수 있다.</p>
<p>물론 &quot;하나의 책임&quot;이란 게 상황마다 다르다. 무조건 기능 하나만 있어야 한다는 게 아니라, 그 클래스가 담당하는 변화의 이유가 한 가지여야 한다는 의미로 이해하면 좋다.</p>
<h1 id="2-ocp---개방-폐쇄-원칙">2. OCP - 개방 폐쇄 원칙</h1>
<p>*<em>확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다.
*</em>
코드를 확장할 땐 새로운 기능을 추가하는 쪽으로 가야지, 기존 코드를 고치는 쪽으로 가면 유지보수가 힘들어진다.</p>
<p>예를 들어 새로운 결제 수단이 추가됐을 때, 기존 if 문을 계속 추가하는 방식은 OCP를 위반하는 구조다. 반면 새로운 클래스만 추가하면 되는 구조라면 OCP를 잘 지킨 거다.</p>
<p>확장성 있는 구조를 위해선 추상화, 즉 인터페이스나 추상 클래스를 잘 활용해야 한다.</p>
<h1 id="3-lsp---리스코프-치환-원칙">3. LSP - 리스코프 치환 원칙</h1>
<p><strong>부모 타입을 사용하는 곳에 자식 타입을 넣어도 문제가 없어야 한다.</strong></p>
<p>상속 구조를 만들 땐 항상 이 원칙을 염두에 둬야 한다. 부모 클래스에 정의된 규칙이 자식 클래스에서도 당연히 지켜질 거라고 기대하게 된다. 그런데 자식이 그 기대를 깨면 문제가 된다.</p>
<p>이 원칙을 지키지 않으면 다형성이 깨지고, 코드를 이해하거나 사용하는 데 혼란이 생긴다.
필요하다면 기능을 인터페이스로 쪼개서 각각 다른 타입으로 나누는 것도 방법이다.</p>
<h1 id="4-isp---인터페이스-분리-원칙">4. ISP - 인터페이스 분리 원칙</h1>
<p><strong>클라이언트가 사용하지 않는 메서드에 의존하지 않게 해야 한다.</strong></p>
<p>인터페이스가 너무 많은 기능을 담고 있으면, 어떤 구현체는 일부 기능만 필요함에도 불구하고 불필요한 메서드까지 구현해야 한다. 이건 명백한 낭비고, 유지보수 포인트가 늘어나는 원인이 된다.</p>
<p>필요한 기능만 쏙쏙 분리해서 최소한의 인터페이스를 제공하는 게 좋다.</p>
<h1 id="5-dip---의존-역전-원칙">5. DIP - 의존 역전 원칙</h1>
<p><strong>상위 모듈은 하위 모듈에 의존하면 안 되고, 둘 다 추상화에 의존해야 한다.</strong></p>
<p>구체 클래스에 의존하는 구조는 테스트하기 어렵고 변경에 취약하다. 반면 추상화에 의존하면 구현체만 갈아끼우는 식으로 테스트나 확장이 가능하다.</p>
<p>DIP를 지키기 위해선 결국 의존성 주입이 필요하다. 직접 생성하지 말고 외부에서 주입받는 구조를 만드는 게 핵심이다.</p>
<h1 id="설계-사상">설계 사상</h1>
<p>SOLID, KISS, YAGNI, DRY, LoD 등.. 많은 설계 사상이 있다.
설계 사상은 개발자의 정신이고, 디자인 패턴은 이러한 설계 사상의 유산이다.</p>
<p>설계 사상은 절대적인 것은 아니나, 나같은 주니어 개발자들한테 성장의 밑거름이 될 것이다. </p>
<h3 id="처음부터-solid-다-지키면서-개발하는-사람은-없다">처음부터 SOLID 다 지키면서 개발하는 사람은 없다.</h3>
<p>하지만 계속 의식하고 연습하다 보면, 어느 순간 자연스럽게 그렇게 설계하고 있는 자신을 보게 될 거다.</p>
<blockquote>
<p>왜 안 되는지 궁금하면, 일단 한 번 안 지켜보고 <strong>개발해보면 된다</strong>. 그럼 안 지켜야 할 이유보다, 지켜야 할 이유가 먼저 보일 거다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[경계를 알면 길이 보인다.]]></title>
            <link>https://velog.io/@comodoking_0128/%EA%B2%BD%EA%B3%84%EB%A5%BC-%EC%95%8C%EB%A9%B4-%EA%B8%B8%EC%9D%B4-%EB%B3%B4%EC%9D%B8%EB%8B%A4</link>
            <guid>https://velog.io/@comodoking_0128/%EA%B2%BD%EA%B3%84%EB%A5%BC-%EC%95%8C%EB%A9%B4-%EA%B8%B8%EC%9D%B4-%EB%B3%B4%EC%9D%B8%EB%8B%A4</guid>
            <pubDate>Mon, 14 Apr 2025 14:32:40 GMT</pubDate>
            <description><![CDATA[<p>개발을 하다 보면 비슷해 보이는 개념들이 자주 튀어나온다.
웹서버, WAS, API 서버, REST, RESTful API.
이걸 분명하게 구분하지 못하면 코드 외적인 부분에서 자꾸 멈춘다.
이번 글은 그 정리다.</p>
<h2 id="웹서버-정적인-건-내가-한다">웹서버: &quot;정적인 건 내가 한다&quot;</h2>
<p>웹서버는 말 그대로 <strong>웹 페이지를 띄워주는 서버</strong>다.
HTML, CSS, JS, 이미지 같은 정적 파일을 서빙한다.
예시: Apache, Nginx
역할: “브라우저가 요청한 HTML 파일? 여기 있다.”</p>
<p>한 문장으로 요약하자면:
요청 들어오면, 파일 찾아서 넘겨주는 애.
<strong>“가볍게 처리할 수 있는 건 가볍게 처리하자.”</strong></p>
<h2 id="was-동적인-건-내-차례야">WAS: &quot;동적인 건 내 차례야&quot;</h2>
<p>WAS는 Web Application Server다.
<strong>프로그램을 실행시켜 동적인 데이터를 응답하는 서버다.</strong></p>
<p>예시: Tomcat, JBoss
역할: “로그인 요청? 그럼 DB랑 얘기 좀 해볼게.”</p>
<p>요청이 단순한 파일이 아니라 로직 실행이 필요할 때 등장한다.
웹서버가 요청을 넘겨주면, WAS가 실제 처리를 한다.</p>
<p><strong>“정적은 넘기고, 동적은 계산하라.”</strong></p>
<h2 id="api-서버-데이터만-주고받자">API 서버: &quot;데이터만 주고받자&quot;</h2>
<p><strong>API 서버는 프론트엔드와 백엔드가 데이터를 주고받는 중간 다리다.</strong>
HTML 같은 UI는 주지 않는다. 대신 <strong>JSON 같은 순수 데이터만 주고받는다.</strong>
사용 예시: 모바일 앱, SPA 프론트엔드와 연결
구조: 프론트에서 요청 → API 서버 → DB 조회 → 응답(JSON)
실제로는 <strong>WAS가 API 서버 역할을 하는 경우가 많다.</strong>
즉, <strong>역할이 완전히 독립된 건 아니다. 경계는 흐릿할 수 있다.</strong></p>
<p><strong>“보여주는 건 프론트의 몫, 나는 값만 넘긴다.”</strong></p>
<h2 id="rest-api-규칙을-지키는-대화">REST API: &quot;규칙을 지키는 대화&quot;</h2>
<p>REST는 하나의 설계 철학이다.
자원을 URI로 표현하고, HTTP 메서드로 행위를 나타낸다.</p>
<p>예시:</p>
<pre><code class="language-bash">GET /users/1         사용자 1번 정보 조회  
POST /users          사용자 생성  
PUT /users/1         사용자 1번 수정  
DELETE /users/1      사용자 1번 삭제</code></pre>
<p>REST의 핵심은 일관성과 예측 가능성이다.
<strong>&quot;URI만 봐도 무슨 일을 하려는지 알 수 있게 만들자&quot;는 철학.</strong>
**
“혼란 없는 설계가 좋은 설계다.”**</p>
<h2 id="restful-api-rest-철학을-끝까지-밀어붙인-버전">RESTful API: &quot;REST 철학을 끝까지 밀어붙인 버전&quot;</h2>
<p>REST를 따르긴 따르는데, 애매하게 따르는 경우가 많다.
<strong>RESTful API는 REST의 원칙을 철저히 지킨 API를 뜻한다.</strong></p>
<blockquote>
<p><strong>지켜야 할 대표 원칙들:</strong>
URI에는 명사만 쓴다 (/getUserInfo X, /users O)
소문자만 사용하고, 언더스코어 대신 하이픈 사용
HTTP 메서드는 의미에 맞게 사용 (조회는 GET, 생성은 POST 등)
상태를 서버에 저장하지 않는다 (Stateless)</p>
</blockquote>
<p><strong>“제대로 따르지 않을 거면, 애초에 따르지 마라.”</strong>
마지막으로, 개념을 구분하면 사고가 정리된다.
사고가 정리되면 설계가 쉬워진다.
설계가 쉬워지면 구현은 덤이다.</p>
<h1 id="애매함을-없애면-실수도-줄어든다"><strong>“애매함을 없애면, 실수도 줄어든다.”</strong></h1>
]]></description>
        </item>
        <item>
            <title><![CDATA[도커 네트워크]]></title>
            <link>https://velog.io/@comodoking_0128/%EC%B2%AB%EB%B2%88%EC%A7%B8-%EB%8F%84%EC%BB%A4-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC%EC%99%80-%EB%9B%B0%EC%96%B4%EB%86%80%EB%8B%A4</link>
            <guid>https://velog.io/@comodoking_0128/%EC%B2%AB%EB%B2%88%EC%A7%B8-%EB%8F%84%EC%BB%A4-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC%EC%99%80-%EB%9B%B0%EC%96%B4%EB%86%80%EB%8B%A4</guid>
            <pubDate>Mon, 07 Apr 2025 07:32:56 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>컨테이너는 스스로를 가둔다.
그러나 네트워크는 그 감옥에 다리를 놓는다.
연결은 의지다.</strong></p>
</blockquote>
<h3 id="가상-네트워크">가상 네트워크</h3>
<p>물리적인 장치 없이 <strong>소프트웨어만으로 구성된 네트워크이다.</strong>
Docker 컨테이너 간의 통신을 관리하고 격리하기 위한 기능을 제공하는 것이다.
같은 호스트 내에서 실행 중인 컨테이너 간 연결할 수 있도록 돕는 <strong>논리적 네트워크 개념이다.</strong></p>
<h3 id="가상-인터페이스">가상 인터페이스</h3>
<blockquote>
<p><strong>네트워크 인터페이스(Network Interface)</strong> 란 <strong>컴퓨터나 장치가 네트워크와 상호작용하</strong>기 위한 연결점이며, 데이터를 주고받을 수 있는 하드웨어 또는 소프트웨어 구성 요소입니다. 네트워크 인터페이스는 컴퓨터와 네트워크 간의 <strong>통신을 가능하게</strong> 하며, 데이터 전송과 수신을 처리합니다.</p>
</blockquote>
<p>알다시피, 도커는 <strong>소프트웨어 프로그램</strong>이다. 
이전 글에서 보았던 네트워크 인터페이스는 <strong>랜카드</strong>를 이용하여 통신을 하였다.
도커에서는 <strong>eth0과 veth</strong>라는 가상 네트워크 인터페이스를 두어 통신한다.
<img src="https://velog.velcdn.com/images/comodoking_0128/post/c2149b2f-7f62-4a75-88f6-45609cb043a0/image.png" alt="">
veth는 일반적인 네트워크 인터페이스와는 달리 패킷을 전달받으면, <strong>자신에게 연결된 다른 네트워크 인터페이스로 패킷을 보내주는 식으로 동작</strong>하기 때문에 항상 쌍으로 생성해줘야 한다.
한 쪽에서 다른 쪽으로 패킷을 전송할 수 있으며, 한 쪽에 다운된 경우 나머지 한 쪽도 정상적으로 기능하지 않는 것이 특징입니다.
도커에서는 실행중인 컨테이너 수 만큼 veth 로 시작하는 인터페이스가 생성됩니다.</p>
<h3 id="도커-네트워크-드라이버">도커 네트워크 드라이버</h3>
<p>도커 네트워크 드라이버는 Native와 Remote로 나뉜다.
Native Driver는 Bridge, Host, None, Overlay를 사용하고,
Remote Driver는 3rd party 드라이버로 외부에서 잘 만들어진 드라이버를 사용한다.
이 중 우리가 살펴볼 네트워크 드라이버는 총 3가지로 Native Driver에 속하는 Bridge, Host, None이다.
<img src="https://velog.velcdn.com/images/comodoking_0128/post/2671f199-a683-41cc-bfb1-eff009e05c36/image.png" alt=""></p>
<h4 id="bridge">Bridge</h4>
<p>포트를 연결해 컨테이너 애플리케이션의 <strong>Port 를 외부에 노출</strong>하는 방식이 bridge 네트워크이다.
아무런 네트워크 드라이버를 지정하지 않으면 default 로 docker0 이라는 bridge 네트워크를 사용한다.</p>
<h4 id="host">Host</h4>
<p>host 네트워크는 도커가 제공해주는 가상 네트워크 인터페이스(veth) 을 사용하는 것이 아니라 이름 그대로 <strong>host의 네트워크에 붙어서 사용한다.</strong>
그래서 bridge 네트워크 처럼 포트 바인딩을 할 필요가 없으며 <strong>호스트 네트워크에 접근하면 컨테이너 또한 접근할 수 있다.</strong></p>
<h4 id="none">None</h4>
<p>None 네트워크는 해당 컨테이너가 네트워크 기능이 필요 없을 때, 혹은 커스텀 네트워크를 사용해야 되는 경우가 있을 때 네트워크 드라이버를 none 으로 설정하고 사용할 수 있다.
<strong>즉 외부 네트워크와의 연결이 단절된다.</strong></p>
<h3 id="도커-네트워크-구조">도커 네트워크 구조</h3>
<p>앞서 언급했던 것처럼 도커 컨테이너도 결국 네트워크를 구성한다. 따라서 <strong>컨테이너마다의 IP와 포트가 존재한다.</strong> 
도커는 호스트로부터 실행되는 컨테이너에 172.17.0.x 의 IP 을 순차적으로 할당한다.
<img src="https://velog.velcdn.com/images/comodoking_0128/post/4b597a87-f36a-4fdb-adc4-87932b7f0697/image.png" alt=""></p>
<p>도커 컨테이너에 아무런 설정을 하지 않는다면 <strong>외부에서 접근할 수 없으며</strong> 오로지 해당 도커 컨테이너를 구동시킨 호스트에서만 접근 가능합니다.</p>
<p>그러면 도커 컨테이너는 외부와 통신할 수 없는걸까?</p>
<p>외부 네트워크에 컨테이너 어플리케이션을 노출하기 위해서는 e<strong>th0의 IP/PORT 을 호스트의 IP/PORT에 바인딩시켜야한다.</strong></p>
<p>즉, <strong>eth0에 대응되는 vethXXXX이라는 이름의 veth interface와 브릿지 네트워크에 컨테이너의 interface가 바인딩되는 형태로 통신합니다.</strong></p>
<p>*<em>veth 인터페이스는 사용자가 직접 생성할 필요는 없고 컨테이너가 생성될 때 도커 엔진이 자동으로 생성된다. *</em>
도커 컨테이너가 실행될 때 네트워크 드라이버를 따로 지정하지 않으면 docker0라고 하는 브릿지 네트워크를 기본으로 사용하게된다. 
*<em>이 브릿지 네트워크의 역할은 veth와 호스트의 eth0의 다리 역할을 합니다. *</em></p>
<p>내가 작성한 네트워크 시리즈 글을 읽고있는 중이라면 이러한 형태를 보았을 것이다.
포트끼리 연결하고 바인딩한다? 그렇다 이는 <strong>포트포워딩</strong>이다.</p>
<h3 id="도커-네트워크-문법">도커 네트워크 문법</h3>
<h4 id="도커-네트워크-명령어">도커 네트워크 명령어</h4>
<p><strong>네트워크 목록 확인</strong></p>
<pre><code>docker network ls</code></pre><p><strong>네트워크 생성</strong></p>
<pre><code>docker network create &lt;네트워크명&gt;</code></pre><p><strong>네트워크 삭제</strong></p>
<pre><code>docker network rm &lt;네트워크명&gt;</code></pre><p><strong>컨테이너에 네트워크 연결</strong></p>
<pre><code>docker network connect &lt;네트워크명&gt; &lt;컨테이너명&gt;</code></pre><p><strong>컨테이너에서 네트워크 분리</strong></p>
<pre><code>docker network disconnect &lt;네트워크명&gt; &lt;컨테이너명&gt;</code></pre><p><strong>네트워크 상세 정보 확인</strong></p>
<pre><code>docker network inspect &lt;네트워크명&gt;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[DNS 지연때문에 인생도 지연됨. 진심임.]]></title>
            <link>https://velog.io/@comodoking_0128/DNS-%EB%92%A4-%EC%95%88%ED%96%88%EC%9C%BC</link>
            <guid>https://velog.io/@comodoking_0128/DNS-%EB%92%A4-%EC%95%88%ED%96%88%EC%9C%BC</guid>
            <pubDate>Sun, 06 Apr 2025 17:15:27 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>“주소는 진실이고, 이름은 망각이다.
세상은 끊임없이 IP를 바꾸지만,
나는 이름으로 그것을 불러낸다.
이름이 사라지면,
연결도 끊긴다.”</strong></p>
</blockquote>
<h1 id="dns란">DNS란?</h1>
<p>도메인 네임 시스템(Domain Name System, DNS)은 호스트의 도메인 이름을 호스트의 네트워크 주소로 바꾸거나 그 반대의 변환을 수행할 수 있도록 하기 위해 개발되었다.</p>
<h2 id="dns의-동작과정">DNS의 동작과정</h2>
<p><img src="https://velog.velcdn.com/images/comodoking_0128/post/fd749e4a-797d-40a8-af91-b0ad29c14146/image.png" alt=""></p>
<ol>
<li>웹 브라우저에 도메인을 입력하면 Local DNS에게 요청을 보낸다.</li>
<li>만약 Local DNS에게 캐싱되어 있는 응답이 있다면 그대로 응답한다.</li>
<li>캐싱되어 있지 않다면 Local DNS가 Root DNS에게 요청을 보낸다.</li>
<li>Local DNS는 Root DNS에게 TLD DNS 서버의 주소를 응답받는다.</li>
<li>Local DNS는 해당 TLD DNS 서버에게 요청을 보낸다.</li>
<li>TLD 서버는 해당 도메인 이름의 Authoritative DNS Server의 IP 주소를 반환한다.</li>
<li>Local DNS는 Authoritative DNS Server에게 요청을 보낸다.
Authoritative DNS Server는 해당 도메인 이름에 대한 IP 주소를 가지고 있다. 이를 반환하게 된다.</li>
<li>Local DNS 서버는 해당 도메인을 캐싱한다.</li>
<li>이후 도메인과 매핑되어있는 아이피 주소로 이동하게 된다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[포트와의 알콩달콩 연애 회고록]]></title>
            <link>https://velog.io/@comodoking_0128/%ED%8F%AC%ED%8A%B8</link>
            <guid>https://velog.io/@comodoking_0128/%ED%8F%AC%ED%8A%B8</guid>
            <pubDate>Sun, 06 Apr 2025 16:54:48 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>“우리는 모두 누군가의 포트다.
정신적 연결을 기다리는 열린 소켓…”</strong></p>
</blockquote>
<h2 id="포트란">포트란?</h2>
<p><strong>포트는 논리적인 접속장소</strong>를 뜻하며, 인터넷 프로토콜인 TCP/IP를 사용할 때 클라이언트 프로그램이 네트워크 상의 특정 서버 프로그램을 지정하는 방법으로 사용한다.
<img src="https://velog.velcdn.com/images/comodoking_0128/post/a6b2cb4e-f465-4dc6-9496-50edc5c60489/image.png" alt="">
위 그림을 보면 한 서버 인스턴스에서 두 서버를 실행 중이다.
클라이언트들이 IP 주소와 포트를 이용하여 접근하고 있다.
<strong>만약 포트가 없었다면 어떻게 두 서버를 구별할까?</strong>
IP 주소만으로는 어느 서버로 요청을 보내는지 알 수 없다.</p>
<p>다음은 자주 사용되는 포트이다.</p>
<table>
<thead>
<tr>
<th>번호</th>
<th>프로토콜</th>
<th>통신 프로토콜</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>80</td>
<td>HTTP</td>
<td>TCP</td>
<td>웹 서버 접속</td>
</tr>
<tr>
<td>443</td>
<td>HTTPS</td>
<td>TCP</td>
<td>웹 서버 접속(SSL)</td>
</tr>
<tr>
<td>25</td>
<td>SMTP</td>
<td>TCP</td>
<td>메일 서버 간 메일 전송</td>
</tr>
<tr>
<td>22</td>
<td>SSH</td>
<td>TCP</td>
<td>컴퓨터 원격 로그인</td>
</tr>
<tr>
<td>53</td>
<td>DNS</td>
<td>UDP</td>
<td>DNS 질의</td>
</tr>
</tbody></table>
<h2 id="네트워크-인터페이스란">네트워크 인터페이스란?</h2>
<p><strong>컴퓨터와 네트워크 사이의 상호 연결 지점</strong>을 의미한다.
네트워크 인터페이스는 실제로 물리적 실체를 가질 수도 있고, 소프트웨어만으로도 구현될 수 있다.
일반적으로는 물리적인 네트워크 인터페이스를 뜻한다.
따라서 조금 더 자세히 알아보겠다.</p>
<h3 id="네트워크-인터페이스-카드">네트워크 인터페이스 카드</h3>
<p><img src="https://velog.velcdn.com/images/comodoking_0128/post/95b7e8b5-3c47-4ddf-8e7b-dd5ab9bf7738/image.png" alt=""></p>
<p>네트워크 인터페이스 카드는 <strong>컴퓨터에 전용 네트워크를 제공하는 회로기판</strong>이다.
네트워크 인터페이스 카드는 OSI 7계층 중 물리 계층(1계층)과  데이터링크 계층(2계층)에 속한 장비로, 기본적으로 <strong>네트워크에서 컴퓨터로 전달된 전기신호를 Bit로 변환하는 역할을 한다.</strong>
그리고 <strong>프로세서에 인터럽트 요청을 보내 Bit를 CPU까지 전달하는 역할</strong>을 한다.
반대로 전송할 Bit를 전기신호로 변환하기도 한다.</p>
<h1 id="포트포워딩">포트포워딩</h1>
<h3 id="포트-포워딩이-왜-필요할까">포트 포워딩이 왜 필요할까?</h3>
<p>사설 IP를 가진 컴퓨터는 공인 IP를 가진 공유기를 거쳐 인터넷을 할 수 있게 된다는 것은 저번 글에서 기술하였다.
<img src="https://velog.velcdn.com/images/comodoking_0128/post/b69bacc7-a50e-4277-80ef-4037ae23983f/image.png" alt="">
만약 위와 같은 그림에서 외부에서 컴퓨터 1의 웹서버에 접근하기 위해서는 어떻게 해야할까?
<strong>컴퓨터 1의 IP는 불변하지 않고 유일하지 않은 사설 IP이기 때문에 IP를 알려줄 수도 없다.</strong> 그래서 공인 IP를 알려줘야 한다. 
&#39;공인 IP:포트 주소&#39;를 입력하더라도 컴퓨터 1에 다다를 수는 없다. 
<strong>공인 IP의 포트 주소로 들어오더라도 연결은 공유기에 도달하면 끝난다</strong>. 
왜냐하면 공인 IP로 들어온 포트 주소의 연결을 컴퓨터 1, 2, 3 중 어디로 보내줘야 할 것인지를 판단할 수 없기 때문이다. 
따라서 미리 경로들을 지정해줘야한다. 감이 왔을 것이라고 생각된다.
이렇듯 *<em>공인 IP의 포트가 어떤 사설 IP로 전송하게 되는지를 설정하는 것이 포트포워딩이다. *</em></p>
<blockquote>
<h4 id="참고">참고</h4>
<table>
<thead>
<tr>
<th></th>
<th>NAT</th>
<th>포트포워딩</th>
</tr>
</thead>
<tbody><tr>
<td>형식</td>
<td>아웃바운드</td>
<td>인바운드</td>
</tr>
<tr>
<td>패킷 속 도착지 여부</td>
<td>True</td>
<td>False</td>
</tr>
<tr>
<td><strong>NAT의 패킷에는 도착지(Destination)의 정보가 포함되어있다.</strong></td>
<td></td>
<td></td>
</tr>
<tr>
<td>하지만 <strong>포트포워딩의 패킷에는 도착지(Destination)의 정보가 포함되어있지 않다.</strong></td>
<td></td>
<td></td>
</tr>
<tr>
<td>NAT는 아웃바운드 형식이고,</td>
<td></td>
<td></td>
</tr>
<tr>
<td>포트포워딩은 인바운드 형식이다.</td>
<td></td>
<td></td>
</tr>
</tbody></table>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[IQ 추적?
IP에 대해 알아보자]]></title>
            <link>https://velog.io/@comodoking_0128/IP%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C</link>
            <guid>https://velog.io/@comodoking_0128/IP%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C</guid>
            <pubDate>Sun, 06 Apr 2025 16:05:33 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>&quot;모든 패킷은 목적지를 향한다.
그러나 때로는
경로를 잃은 채 방황한다.
그것이 우리의 삶이다.&quot;</strong></p>
</blockquote>
<h2 id="공인망과-사설망">공인망과 사설망</h2>
<h3 id="공인-ip-public-ip">공인 IP (Public IP)</h3>
<p>인터넷 사용자의 로컬 네트워크를 식별하기 위해 <strong>ISP(인터넷 서비스 공급자) 가 제공하는 IP 주소</strong>이다.
<strong>외부에 공개되어 있는 IP 주소</strong>이다.</p>
<ul>
<li><strong>공인 IP는 전세계에서 유일한 IP 주소</strong>를 갖는다.</li>
</ul>
<h3 id="사설-ip-private-ip">사설 IP (Private IP)</h3>
<p><strong>일반 가정이나 회사 내 등에 할당된 네트워크의 IP 주소</strong>이며 로컬 IP, 가상 IP라고도 한다.
IPv4의 주소부족으로 <em>서브넷팅된 IP이기 떄문에 *</em>라우터에 의해 로컬 네트워크상의 PC나 장치에 할당된다.**</p>
<blockquote>
<h4 id="서브넷팅이란">*서브넷팅이란?</h4>
<p>원본 네트워크를 여러개의 네트워크로 분리하여 네트워크 할당하는 작업이다.</p>
</blockquote>
<p>사설IP 주소는 다음 3가지 주소대역으로 고정된다.
각각의 주소대역은 클래스 나뉘고, 클래스마다 할당할 수 있는 IP의 갯수가 다르다.</p>
<ul>
<li>클래스 A: 10.0.0.0/8</li>
<li>클래스 B: 172.16.0.0/12</li>
<li>클래스 C:192.168.0.0/16</li>
</ul>
<p>사설 IP 주소 후반부에 있는 &#39;/n&#39;은 해당 주소를 판별할 때 총 32bit의 IP주소 중 첫 몇 bit까지 읽어내야 하는지를 뜻한다. 이를 <strong>CIDR 표기법</strong>이라고 한다.
<img src="https://velog.velcdn.com/images/comodoking_0128/post/e7c22420-196e-4daa-bcc9-13de3ec3fa42/image.png" alt="">
위와 같이 하나의 옥텟은 8비트로 이루어져 있으며, 일반적으로 사용하는 IPv4의 주소는 4개의 옥텟으로 이루어져 있다. 따라서 CIDR는 0~32, 총 32비트까지 사용이 가능하다.
<img src="https://velog.velcdn.com/images/comodoking_0128/post/a28c1395-2d98-476f-af40-a6bd190e0f67/image.png" alt=""></p>
<p>CIDR가 &#39;/24&#39;의 경우 위와 같이 앞에서부터 24비트 이후에 오는 4번째 옥텟을 전부 사용할 수 있다는 표현이다. 하나의 옥텟은 8비트로, 2의 8제곱인 256개이기 때문에 192.168.0.0/24일때, 192.168.0.0 ~ 192.168.0.255까지 사용이 가능하다.</p>
<blockquote>
<h4 id="옥텟이란">옥텟이란?</h4>
<p><strong>정보 전달의 단위.</strong> 현재의 컴퓨터에서는 1바이트(Byte)가 8비트(bit)로 구성되어 있지만, 바이트의 정의상 반드시 8비트일 필요는 없었다. 과거에는 1바이트가 8비트가 아닌 컴퓨터도 실제로 존재하였다. 그런데, 때로는 <strong>정확하게 8비트임을 명시해야 하는 경우</strong>가 존재하는데, 이때 사용하는 용어가 옥텟이다.</p>
</blockquote>
<p>특히 네트워크 관련된 분야에서는 서로 연결되는 두 대의 장비가 1바이트=8비트라는 보장을 할 수 없으므로, 옥텟이라는 표현을 사용하여 확실하게 8비트임을 명시한다.</p>
<p>다음은 공인 IP와 사설 IP의 차이점이다.</p>
<table>
<thead>
<tr>
<th></th>
<th align="center">공인 IP</th>
<th align="center">사설 IP</th>
</tr>
</thead>
<tbody><tr>
<td><strong>할당 주체</strong></td>
<td align="center">ISP</td>
<td align="center">라우터(공유기)</td>
</tr>
<tr>
<td><strong>고유성</strong></td>
<td align="center">인터넷 상에서 유일한 주소</td>
<td align="center">하나의 네트워크 안에서 유일</td>
</tr>
<tr>
<td><strong>공개 여부</strong></td>
<td align="center">내/외부 접근 가능</td>
<td align="center">외부 접근 불가능</td>
</tr>
</tbody></table>
<p><strong>사설 IP 주소만으로는 인터넷에 직접 연결할 수 없다. 라우터를 통해 1개의 공인 IP만 할당</strong>하고, <strong>라우터에 연결된 개인 PC는 사설 IP</strong>를 각각 할당 받아 인터넷에 접근할 수 있게 된다. </p>
<h2 id="nat">NAT</h2>
<p>NAT는 <strong>사설망에서 외부 통신을 하는 방법</strong> 중 하나이다.
<img src="https://velog.velcdn.com/images/comodoking_0128/post/6397c8ac-f954-4ade-95cd-492690d8b984/image.png" alt=""></p>
<p>사설 IP만 가지고는 인터넷에 접속할 수 없기 때문에 <strong>NAT(Network Adress Translation)</strong>이란 개념이 등장한다.
보통, <strong>사설 IP -&gt; 공인 IP</strong>, <strong>공인 IP -&gt; 사설 IP</strong>로 변환하여 사설망이 외부 통신을 할 수 있게 된다.
즉, <strong>사설 IP를 사용하고 있는 컴퓨터가 공인 IP에 해당하는 외부 세계와 통신을 할 수 있게 된 것이다.</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[면접관이 '광대승천'하는 프로세스&스레드 지식! ]]></title>
            <link>https://velog.io/@comodoking_0128/%EB%B9%85%EB%8D%B0%EC%9D%B4%ED%84%B0%EC%99%80-AI%EC%8B%9C%EB%8C%80%EC%97%90-%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%EC%82%B4%EC%95%84%EB%82%A8%EA%B8%B0-%EC%9C%84%ED%95%9C-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%99%80-%EC%8A%A4%EB%A0%88%EB%93%9C-CS%EC%A7%80%EC%8B%9D-%EA%B8%B0%EB%B3%B8</link>
            <guid>https://velog.io/@comodoking_0128/%EB%B9%85%EB%8D%B0%EC%9D%B4%ED%84%B0%EC%99%80-AI%EC%8B%9C%EB%8C%80%EC%97%90-%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%EC%82%B4%EC%95%84%EB%82%A8%EA%B8%B0-%EC%9C%84%ED%95%9C-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%99%80-%EC%8A%A4%EB%A0%88%EB%93%9C-CS%EC%A7%80%EC%8B%9D-%EA%B8%B0%EB%B3%B8</guid>
            <pubDate>Thu, 20 Mar 2025 06:23:14 GMT</pubDate>
            <description><![CDATA[<p>프로세스와 스레드 개념은 개발자한테 밀접하게 닿아있고, 많은 기업들의 기술면접에도 자주 언급되는 주제이기도 하다.
그러면 프로세스와 스레드에 대해 알아보자.</p>
<h2 id="프로세스">프로세스</h2>
<p> 프로세스는 현재 실행 중이거나 곧 실행 가능한 <strong>PCB</strong>를 가진 프로그램이다.
<strong>PCB</strong>에 대해 알아보기 앞서, 프로그램과 프로세스의 차이점에 대해 알아보자.
 프로그램은 디스크에 저장된 <strong>수동적 실체</strong>이다. 잘 와닿지 않을 수 있는데 그냥 <strong>정적인 코드 덩어리</strong>라고 생각하면 된다.
 프로세스는 프로그램 카운터와 프로세스 제어 블록(PCB)을 가지며, 실행 흐름을 제어하는 <strong>능동적 실체</strong>이다.
쉽게 생각하면 메모리에 적재되기 전에는 프로그램이고, 적재된 후에는 프로세스가 되는 것이다.
프로세스는 프로세서에 의해 수행되는 프로그램 단위로, 다음과 같은 특징을 가진다.</p>
<ul>
<li>실행 중인 프로그램</li>
<li>비동기적 프로그램</li>
<li>살아 있는 프로그램</li>
<li>프로세스 제어 블럭(PCB)를 가지는 프로그램</li>
<li>언제든지 실행 가능한 프로그램</li>
</ul>
<h3 id="프로세스의-메모리-구조">프로세스의 메모리 구조</h3>
<p>프로세스의 메모리는 정적(코드, 데이터), 동적(힙, 스택) 영역으로 구성되었다.
<strong>1. 코드(Code)영역</strong> : 실행 가능한 프로그램 코드 영역, CPU가 프로세스 실행 시 해당 내역 참조
<strong>2. 데이터 (Data)영역</strong> : 전역변수, 정적 변수, 작업 공간 등을 담음
<strong>3. 힙(Heap)영역</strong> : 동적 메모리(malloc(), free()) 등에 사용되는 변수를 담는 영역
<strong>4. 스택(Stack)영역</strong> : 지역변수, 인자 리스트, 복귀 주소, 스택 프레임 등을 담음</p>
<h3 id="프로세스-제어-블록-pcb">프로세스 제어 블록 (PCB)</h3>
<p>PCB는 운영체제가 프로세스를 관리하기 위한 정보가 담긴 자료구조이다.</p>
<h4 id="pcb의-주요정보">PCB의 주요정보</h4>
<ul>
<li>프로세스 상태</li>
<li>프로세스 ID</li>
<li>프로그램 카운터</li>
<li>레지스터 정보</li>
<li>메모리 관리 정보</li>
<li>계정 정보</li>
<li>입출력 정보<h4 id="pcb의-역할">PCB의 역할</h4>
</li>
<li>프로세스 상태 저장</li>
<li>문맥 교환</li>
</ul>
<h4 id="프로세스-상태">프로세스 상태</h4>
<ul>
<li>** 생성 상태 (new)**</li>
<li><ul>
<li>PCB가 생성되는 상태</li>
</ul>
</li>
<li><strong>준비 상태 (ready)</strong></li>
<li><ul>
<li>프로세서(CPU) 할당을 기다리는 상태</li>
</ul>
</li>
<li><strong>실행 상태 (run)</strong></li>
<li><ul>
<li>프로세스가 CPU를 할당 받아 실행 중인 상태</li>
</ul>
</li>
<li><strong>대기 상태 (wait)</strong></li>
<li><ul>
<li>특정 자원이 필요하며 대기 중인 상태</li>
</ul>
</li>
<li><strong>지연 준비 상태 (Suspend ready)</strong></li>
<li><ul>
<li>준비 상태에 있던 프로세스가 메모리 부족으로 인해 하드디스크로 스왑됨.</li>
</ul>
</li>
<li><ul>
<li>메모리가 확보되면 다시 준비 상태로 전이됨.</li>
</ul>
</li>
<li><strong>지연 대기 상태 (Suspend ready)</strong></li>
<li><ul>
<li>대기 상태에서 메모리 부족으로 인해 스왑된 상태</li>
</ul>
</li>
<li><ul>
<li>요청한 자원이 할당되면 지연 준비 상태로 전이됨</li>
</ul>
</li>
</ul>
<h4 id="프로세스-상태-전이">프로세스 상태 전이</h4>
<p><strong>디스패치(dispatch)</strong> : 준비 상태 -&gt; 실행 상태
<strong>할당 시간 초과(time runout)</strong> : 실행 상태 -&gt; 준비 상태
<strong>블록(block)</strong> : 실행 상태 -&gt; 대기 상태
<strong>웨이크업(wake up)</strong> : 대기 상태 -&gt; 준비 상태
<img src="https://velog.velcdn.com/images/comodoking_0128/post/e90dee20-74f7-4e6d-b2eb-e0423738c15f/image.jpeg" alt=""></p>
<h1 id="프로세스와-스레드">프로세스와 스레드</h1>
<h3 id="프로세스-1">프로세스</h3>
<ul>
<li>실행 중인 프로그램을 의미하며, 독립적인 메모리 공간을 가진다.</li>
<li>운영체제에 의해 관리되며, 서로 다른 프로세스 간에는 메모리르 공유하지 않는다.</li>
<li>하나 이상의 스레드를 포함할 수 있으며, 각각의 프로세스는 별도의 실행 흐름을 가진다.</li>
</ul>
<h3 id="스레드">스레드</h3>
<ul>
<li>프로세스 내에서 실행되는 작은 실행 단위</li>
<li>같은 프로세스 내에서 메모리와 자원을 공유하며, 다중 작업을 수행할 수 있다.</li>
</ul>
<h3 id="커널-스레드">커널 스레드</h3>
<ul>
<li>운영체제 커널이 직접 관리하는 스레드</li>
<li>시스템 호출을 통해 커널에서 스레드를 생성하고 관리한다.<h3 id="사용자-스레드">사용자 스레드</h3>
</li>
<li>사용자 공간에서 관리되는 스레드</li>
<li>커널에 직접적인 개입없이 사용자 수준에서 생성 및 관리 가능</li>
</ul>
<h1 id="느낀점">느낀점</h1>
<p>내가 아는 스레드는 운영체제와 밀접한 스레드 보다는 자바 문법상으로 배우는 스레드의 느낌이 강했는데, 스레드에 대해 공부하고 나니 사고가 확장되는 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring RedisTemplate null 반환 오류]]></title>
            <link>https://velog.io/@comodoking_0128/Spring-RedisTemplate-null-%EB%B0%98%ED%99%98-%EC%98%A4%EB%A5%98</link>
            <guid>https://velog.io/@comodoking_0128/Spring-RedisTemplate-null-%EB%B0%98%ED%99%98-%EC%98%A4%EB%A5%98</guid>
            <pubDate>Sat, 15 Mar 2025 17:42:17 GMT</pubDate>
            <description><![CDATA[<p>오늘 JWT 리프레시 토큰 로테이트를 구현하려고 했다. 리프레시 토큰을 Redis에 저장해두고, 엑세스 토큰을 발급받을 때 리프레시 토큰을 새로 발급하고 Redis에 저장되어있는 리프레시 토큰을 업데이트하여 서버가 주도권을 가지고 보안을 강화하는 방식으로 진행하려고 했는데... 아이러니하게도 Redis에서 값을 조회했을 때, 계속 null이 반환됐다.</p>
<p>4시간을 온전히 문제 해결에 쏟았지만, 여전히 문제가 해결되지 않았고, 심지어 그 해결책이 웹 검색을 통해 겨우 나왔다. 그 원인이 뭘까? 바로 트랜잭션 문제였다.</p>
<h3 id="문제의-핵심">문제의 핵심</h3>
<p>Redis에서는 트랜잭션을 사용하면 명령어가 <strong>커맨드 버퍼</strong>에 저장되고, 트랜잭션이 커밋 또는 롤백될 때까지 결과가 반환되지 않는다. 그래서 트랜잭션이 아직 진행 중인 상태에서 값을 조회하게 되면, 커맨드 버퍼에 남아있는 값은 아직 반영되지 않은 상태여서 <strong>null</strong>이 반환된다.</p>
<h4 id="왜-null이-반환되는-걸까">왜 null이 반환되는 걸까?</h4>
<p>트랜잭션이 진행 중일 때, Redis는 명령어를 즉시 실행하는 게 아니라 먼저 버퍼에 저장한다. 그리고 트랜잭션이 끝날 때까지 그 값은 다른 명령에서 조회할 수 없다. 결국, 트랜잭션 커밋이 완료되기 전에 값을 조회하면 예상치 못한 null이 반환되는 것이다.</p>
<h3 id="해결책">해결책</h3>
<p>문제를 해결하려면 트랜잭션이 완료된 후에 해당 값을 조회해야 한다. 즉, 트랜잭션 커밋 후에 데이터를 다시 요청하면 정상적으로 값이 반환된다.</p>
<h3 id="결론">결론</h3>
<p>리프레시 토큰 로테이트를 구현할 때, 트랜잭션 처리에 대한 이해가 부족해서 발생한 문제였다. Redis에서 트랜잭션을 사용할 때는 그 결과가 커밋되거나 롤백될 때까지 다른 세션에서 해당 값을 조회할 수 없다는 점을 유의해야 한다.</p>
<p>이 글을 읽는 사람들은 Redis를 이용할 때 나처럼 삽질하지 않기를 바란다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[도커에 대해 알아보자]]></title>
            <link>https://velog.io/@comodoking_0128/%EB%8F%84%EC%BB%A4%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@comodoking_0128/%EB%8F%84%EC%BB%A4%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Thu, 13 Mar 2025 02:30:29 GMT</pubDate>
            <description><![CDATA[<h1 id="서론">서론</h1>
<p>도커는 brew install docker를 통해 설치할 수 있다.
이 글에서는 굳이 설치하는 방법을 서술하지 않으려 한다.
사실 도커를 못들어본 개발자는 없을 것이다.
사용하려면 어떤 개념들을 알아야할까?</p>
<h1 id="본론">본론</h1>
<h3 id="컨테이너">컨테이너</h3>
<p><strong>컨테이너</strong>는 미니 컴퓨터라고 생각해도 된다.
하나의 호스트 OS 위에서 마치 각각의 <strong>독립적인 프로그램</strong>처럼 관리되고 실행한다.
불필요하게 OS를 만드는 작업 및 인프라를 독립적으로 나눌 필요가 없어서 확장성이 빠르고 좋다.</p>
<h3 id="이미지">이미지</h3>
<p>이미지는 <strong>개발환경</strong>을 포장해준 것이라고 생각하면 된다.
개발자가 직접 개발환경을 구축하는 부담을 도커가 대신 하게 된다.</p>
<pre><code>docker pull &lt;image명&gt; 명령어를 통해 손쉽게 개발환경을 가져올 수 있다.</code></pre><p>가져올 수 있는 개발환경은 <a href="https://hub.docker.com/">https://hub.docker.com/</a> 여기서 볼 수 있다. 
그 이름과 같이 도커허브는 이미지 정보를 모으는 곳이다.</p>
<p>또한 이미지 다운로드가 진행될 때, 이미지는 <strong>레이어</strong> 형식으로 저장된다.</p>
<p>레이어란 기존 이미지에 추가적인 파일이 필요할 때 다시 다운로드 받는 방법이 아닌 해당 파일을 추가하기 위한 개념이다. 
<img src="https://velog.velcdn.com/images/comodoking_0128/post/e140701c-fbfd-418f-951a-04d9d8b12734/image.png" alt=""></p>
<h3 id="간단한-명령어들">간단한 명령어들</h3>
<pre><code>docker run &lt;이미지명&gt; //run 명령어 실행 시 pull, create, start가 순차적으로 실행되게 된다.
docker ps // 도커 실행 중인 컨테이너 확인
docker ps -a // 도커 모든 컨테이너 확인
dokcer stop &lt;컨테이너명&gt; // 도커 멈추기</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[좋은 이력서의 특징]]></title>
            <link>https://velog.io/@comodoking_0128/%EC%A2%8B%EC%9D%80-%EC%9D%B4%EB%A0%A5%EC%84%9C%EC%9D%98-%ED%8A%B9%EC%A7%95</link>
            <guid>https://velog.io/@comodoking_0128/%EC%A2%8B%EC%9D%80-%EC%9D%B4%EB%A0%A5%EC%84%9C%EC%9D%98-%ED%8A%B9%EC%A7%95</guid>
            <pubDate>Wed, 12 Mar 2025 00:13:52 GMT</pubDate>
            <description><![CDATA[<h1 id="1-자기소개">1. 자기소개</h1>
<p>내가 어떤 개발자인지 2 ~ 3줄 이내로 작성
나머지 글들을 이 2~3 줄에 신빙성을 가미하기 위해 작성</p>
<h1 id="2-배포">2. 배포</h1>
<p>프로젝트를 한번 하고 방치하는 것이 아니라 배포를 하고 꾸준히 유지보수를 하자
회사는 개발역량뿐만 아니라 서비스 운영 능력까지 봄.</p>
<h1 id="3-난이도">3. 난이도</h1>
<p>프로젝트를 막 다 적지말고 자기를 대표할 수 있는 프로젝트 2개정도만 작성하자. 
너무 쉬운 난이도의 프로젝트까지는 굳이?</p>
<h1 id="4-리드미">4. 리드미</h1>
<p>팀 프로젝트를 하면 레포지토리의 리드미는 팀 중심으로 써지게 된다.
fork 해와서 내가 기여한 부분들을 작성하자.</p>
<h1 id="5-배운점">5. 배운점</h1>
<p>배운점은 무조건 &#39;고민&#39;이 들어가야한다.
고민을 통해 배운점을 작성해야함</p>
<h1 id="6-코드">6. 코드</h1>
<p>말만 잘하는 것이 아니라 코드로 구현할 줄 알아야함</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[GNN]]></title>
            <link>https://velog.io/@comodoking_0128/GNN</link>
            <guid>https://velog.io/@comodoking_0128/GNN</guid>
            <pubDate>Fri, 07 Mar 2025 11:17:36 GMT</pubDate>
            <description><![CDATA[<p>GNN은 Graph Neural Network의 약자로, 그래프 데이터를 학습하는 알고리즘이다. 
그래프는 점과 점을 잇는 선으로 이루어진 데이터 구조이다.
관계나 상호작용을 나타낼 때 주로 쓰인다.</p>
<h1 id="인접행렬">인접행렬</h1>
<p>그래프를 컴퓨터에서 표현하기 위해서는 주로 <strong>인접행렬</strong>이 쓰인다.</p>
<p>a[i][j] : 노드 i에서 노드 j로 가는 간선이 있으면 1, 아니면 0으로 표현된다.
다음 예를 보자.
<img src="https://velog.velcdn.com/images/comodoking_0128/post/d9723c4c-eff1-44ec-925b-99747b412b08/image.jpeg" alt="">
위와 같은 그래프는 아래 행렬처럼 표현된다.
<img src="https://velog.velcdn.com/images/comodoking_0128/post/00e68109-46b2-459e-9495-b81f6bb62acb/image.jpeg" alt=""></p>
<p>그러면 방향이 없는 무향 그래프이면 어떻게 될까?
<img src="https://velog.velcdn.com/images/comodoking_0128/post/53a4e239-afda-4eda-9f8b-c3df3df271c8/image.jpeg" alt="">
<img src="https://velog.velcdn.com/images/comodoking_0128/post/9cccbb0f-d272-4a99-914c-e7813e233923/image.jpeg" alt="">
위 그림들을 보고 인접행렬에 대해 이해했을 것이라 생각한다.</p>
<h1 id="gnn">GNN</h1>
<p>그러면 그래프를 분석하기 힘든 이유는 무엇일까?</p>
<ol>
<li>고정된 형태가 아니다.</li>
<li>유클리드 공간에 있지 않다. 좌표계로 표현할 수 없다.</li>
<li>데이터가 커질수록 시각화가 어려워진다.</li>
</ol>
<p>하지만 단점만 있는 것은 아니다.
그래프는 관계, 상호작용같은 추상적인 개념들을 표현하기에 탁월하다.</p>
<p>GNN의 핵심은 점이 이웃과의 연결에 의해 정의된다는 것이다. 만약 어떤 점의 이웃과 연결을 다 끊으면 그 점은 고립되고 아무 의미를 갖지 않게 된다.</p>
<p>GNN을 통해 그래프의 점들 사이의 관계를 파악하고 두 점 사이에 얼마나 연관성이 있을지 예측할 수 있다.</p>
<h2 id="gnn-동작과정">GNN 동작과정</h2>
<p>GNN은 그래프의 구조를 이해하고, 각 노드가 가진 특징을 고려하면서, 이웃 노드의 정보를 반영하여 자신을 업데이트하고 이 과정을 반복하면서 점점 더 많은 정보를 학습하게 됨.</p>
<p>각 노드는 자기 자신과 이웃 노드들의 정보를 합쳐서 새로운 표현을 생성한다.
예를 들어, A라는 노드는 B, C와 연결되어 있다면, A는 B와 C의 정보를 받아들이고 업데이트한다.</p>
<p>이 과정은 여러 번 반복될 수 있으며, 각 노드는 자신의 이웃 정보와 자신의 정보를 바탕으로 새로운 특성을 만듦.</p>
<p>마지막에는 모든 노드가 자신의 특성을 업데이트하고, 그래프 전체의 정보를 고려한 결과를 도출함.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[참고용 링크]]></title>
            <link>https://velog.io/@comodoking_0128/%EC%B0%B8%EA%B3%A0%EC%9A%A9-%EB%A7%81%ED%81%AC</link>
            <guid>https://velog.io/@comodoking_0128/%EC%B0%B8%EA%B3%A0%EC%9A%A9-%EB%A7%81%ED%81%AC</guid>
            <pubDate>Sat, 18 Jan 2025 11:47:36 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h3 id="취업">취업</h3>
</blockquote>
<h4 id="주니어-개발자를-위한-취업-정보--httpsgithubcomjojoldujunior-recruit-scheduler">주니어 개발자를 위한 취업 정보 : <a href="https://github.com/jojoldu/junior-recruit-scheduler">https://github.com/jojoldu/junior-recruit-scheduler</a></h4>
<h4 id="채용공고-보기--httpszighangcomitutm_sourcelanding">채용공고 보기 : <a href="https://zighang.com/it?utm_source=landing">https://zighang.com/it?utm_source=landing</a></h4>
<h4 id="it-자격증-캘린더-----httpsit-ranknetlifyappcalendar">IT 자격증 캘린더 :    <a href="https://it-rank.netlify.app/calendar">https://it-rank.netlify.app/calendar</a></h4>
<h4 id="기술인터뷰--httpsgithubcomjaeyeophaninterview_question_for_beginner">기술인터뷰 : <a href="https://github.com/JaeYeopHan/Interview_Question_for_Beginner">https://github.com/JaeYeopHan/Interview_Question_for_Beginner</a></h4>
<h4 id="httpsgithubcomwoovictoryready-for-tech-interview"><a href="https://github.com/WooVictory/Ready-For-Tech-Interview">https://github.com/WooVictory/Ready-For-Tech-Interview</a></h4>
<h4 id="httpsgithubcomdevham76tech-interview-study"><a href="https://github.com/devham76/tech-interview-study">https://github.com/devham76/tech-interview-study</a></h4>
<hr>
<h4 id="로드맵-참고용--httpsgithubcomkamranahmedsedeveloper-roadmap">로드맵 (참고용) : <a href="https://github.com/kamranahmedse/developer-roadmap">https://github.com/kamranahmedse/developer-roadmap</a></h4>
<hr>
<blockquote>
<h3 id="아티클">아티클</h3>
</blockquote>
<h4 id="분야별-개발-아티클-모음--httpsgithubcomintegerousgoquality-dev-contents">분야별 개발 아티클 모음 : <a href="https://github.com/Integerous/goQuality-dev-contents">https://github.com/Integerous/goQuality-dev-contents</a></h4>
<h4 id="회사들의-기술-블로그-엿보기--httpswwwcodenarycokr">회사들의 기술 블로그 엿보기 : <a href="https://www.codenary.co.kr/">https://www.codenary.co.kr/</a></h4>
<h4 id="어떤-정보든-카테고리별로-단일화-되어있는-사이트들을-정리합니다--httpsgithubcomdding-guseful-infotabreadme-ov-file">어떤 정보든 카테고리별로 단일화 되어있는 사이트들을 정리합니다 : <a href="https://github.com/dding-g/useful-info?tab=readme-ov-file">https://github.com/dding-g/useful-info?tab=readme-ov-file</a></h4>
]]></description>
        </item>
        <item>
            <title><![CDATA[특성 스케일링 방법]]></title>
            <link>https://velog.io/@comodoking_0128/%ED%8A%B9%EC%84%B1-%EC%8A%A4%EC%BC%80%EC%9D%BC%EB%A7%81-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@comodoking_0128/%ED%8A%B9%EC%84%B1-%EC%8A%A4%EC%BC%80%EC%9D%BC%EB%A7%81-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Sat, 04 Jan 2025 09:19:50 GMT</pubDate>
            <description><![CDATA[<p>머신러닝에서 대개의 알고리즘들은 입력된 숫자 특성들의 스케일이 다르면 제대로 작동하지 않습니다.</p>
<p>이를 위해 <strong>특성 스케일링</strong>을 거쳐줘야 합니다.</p>
<p>특성 스케일링을 거치면 경사하강법에서 Local Minima에 빠질 위험을 줄이고, 스케일이 큰 특성의 영향력이 커지는 것을 방지할 수 있습니다.</p>
<p>특성 스케일링에는 다음과 같은 방법들이 있습니다.</p>
<p>정규화 ( Normlization )</p>
<p>표준화 ( Standardization )</p>
<p>하나씩 알아볼까요?</p>
<h2 id="정규화">정규화</h2>
<ul>
<li>일반적으로 0 ~ 1의 범위를 가집니다.</li>
<li>사이킷런의 MinMaxScaler 추정기를 통해 쉽게 구현할 수 있습니다. feature_range 매개변수를 통해 0 ~ 1이 아닌 범위에 들도록 값을 조정할 수 있습니다.</li>
<li>정규화는 데이터 - 최소값 / 최대값 - 최소값 를 통해 스케일을 조정합니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/comodoking_0128/post/387660b5-4ce5-47a8-9d2c-15247c79ed5c/image.png" alt=""></p>
<pre><code class="language-python">from sklearn.preprocessing import MinMaxScaler
min_max_scaler = MinMaxScaler(feature_range=(-1, 1)) # -1 ~ 1로 값의 범위를 조정했습니다.
scaled_data = min_max_scaler.fit_transform(data)</code></pre>
<h2 id="표준화">표준화</h2>
<ul>
<li>항상 평균이 0 입니다.</li>
<li>사이킷런의 StandardScaler 추정기를 통해 쉽게 구현할 수 있습니다.</li>
<li>표준화는 데이터 - 평균 / 표쥰편차를 통해 스케일을 조정합니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/comodoking_0128/post/d70a93e4-f8a0-4e61-9fcc-2777a370843c/image.png" alt="표준화 수식"></p>
<pre><code class="language-python">from sklearn.preprocessing import StandardScaler
standard_scaler = StandardScaler()
data_std_scaled = standard_scaler.fit_transform(data)</code></pre>
<h2 id="변환">변환</h2>
<p>특성 분포의 꼬리가 두꺼울 때 (평균에서 멀리 떨어진 값이 지수적으로 줄어들지 않는 경우) 스케일링과 표준화는 대부분의 값을 작은 범위로 압축합니다. 하지만 일반적으로 머신러닝 모델에게 그다지 효과적이지 않습니다. 따라서 특성을 스케일링 하기 전에 두꺼운 꼬리를 줄이도록 변환하고 분포가 대략적으로 대칭이 되도록 만들어야 합니다. </p>
<p>오른쪽 꼬리가 두꺼운 양수 특성인 경우에는 일반적으로 특성을 제곱근을 취해 변환할 수 있습니다. (0 ~ 1 사이의 값에서는 특성을 거듭제곱하여 변환할 수 있습니다.)</p>
<p>만약 멱법칙 분포처럼 특성 분포의 꼬리가 아주 길고, 두껍다면  특성을 로그 값으로 바꾸는 것이 도움이 될 수 있습니다. 로그값을 취하고나면 가우스 분포(정규 분포)에 가까워집니다. </p>
<blockquote>
<p>멱법칙 분포
<img src="https://velog.velcdn.com/images/comodoking_0128/post/504c8dc9-97e5-44d5-9257-99414a6dfeee/image.png" alt="멱법칙 분포">
가우스 분포(정규분포)
<img src="https://velog.velcdn.com/images/comodoking_0128/post/bdce1471-c5fe-4268-ba24-36718c08c129/image.png" alt=""></p>
</blockquote>
<p>꼬리가 두꺼운 특성을 처리하는 또 다른 방법은 특성을 구간화(Buketizing 또는 Binning)하는 것입니다. 분포를 거의 동일한 크기의 버킷으로 나눠 범주화하는 것입니다. 거의 동일한 크기의 버킷을 사용하면 균등 분포인 특성을 만듭니다. 따라서 추가적으로 스케일링을 할 필요도 없어집니다. 또는 버킷 개수로 나누어 0 ~ 1 사이의 범위로 만들 수 있습니다.</p>
<p>특성이 멀티모달 분포(다붕 분포, 정점이 2개이상 나타나는 분포)일때 구간화를 사용하면 도움이 될 수 있습니다.</p>
<p>다만, 범주형 데이터이니 수치형 데이터로 한번 더 변환시켜서 사용해야합니다. 범주형 데이터를 수치형 데이터로 변환시키는 과정은 다음에 설명하겠습니다.</p>
<p>이런 방법을 사용하면 회귀 모델이 특성값의 여러 범주에 대해서 다양한 규칙을 쉽게 학습할 수 있습니다. </p>
<p>멀티모달 분포를 변환하는 또 다른 방법은 특성간의 유사도를 나타내는 특성을 추가하는 것입니다. 유사도 측정은 일반적으로 입력값과 중심점 사이의 거리에만 의존하는 방사 기저 함수를(RBF) 사용합니다.</p>
<p>방사 기저 함수에 대해서는 아직 제대로 이해가 되지 않아 참고 자료를 올려둡니다. <a href="https://m.blog.naver.com/sw4r/221497261535">https://m.blog.naver.com/sw4r/221497261535</a></p>
<p>가장 널리 사용되는 RBF는 가우스 RBF입니다. 사이킷런의 rbf_kernel() 함수를 사용하면 유사도를 재는 새로운 가우스 RBF 특성을 손쉽게 만들 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ 감상문 ]생성형 AI 시대의 코딩 없는 개발자 시대: 로우코드와 협력하다]]></title>
            <link>https://velog.io/@comodoking_0128/%EA%B0%90%EC%83%81%EB%AC%B8-%EC%83%9D%EC%84%B1%ED%98%95-AI-%EC%8B%9C%EB%8C%80%EC%9D%98-%EC%BD%94%EB%94%A9-%EC%97%86%EB%8A%94-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EC%8B%9C%EB%8C%80-%EB%A1%9C%EC%9A%B0%EC%BD%94%EB%93%9C%EC%99%80-%ED%98%91%EB%A0%A5%ED%95%98%EB%8B%A4</link>
            <guid>https://velog.io/@comodoking_0128/%EA%B0%90%EC%83%81%EB%AC%B8-%EC%83%9D%EC%84%B1%ED%98%95-AI-%EC%8B%9C%EB%8C%80%EC%9D%98-%EC%BD%94%EB%94%A9-%EC%97%86%EB%8A%94-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EC%8B%9C%EB%8C%80-%EB%A1%9C%EC%9A%B0%EC%BD%94%EB%93%9C%EC%99%80-%ED%98%91%EB%A0%A5%ED%95%98%EB%8B%A4</guid>
            <pubDate>Mon, 30 Dec 2024 09:00:59 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.samsungsds.com/kr/insights/low-code-for-non-coding-developers.html">https://www.samsungsds.com/kr/insights/low-code-for-non-coding-developers.html</a></p>
<h3 id="요약"><strong>요약</strong></h3>
<p>현재는 기술 과도기에 와있습니다.
생성형 AI의 발전에 따라 머지않아 코딩은 거의 완전 자동화가 될 것이고, UX디자이너가 사실상 프론트엔드 개발자가 될 수도 있을 것입니다.
조금 과장된 표현을 하자면, 코파일럿에 적절한 질문을 할 수 있는 것만으로도 신속하게 앱을 만들거나 솔루션을 배포할 수 있습니다.</p>
<p>개발자가 아닌 사람이 만든 앱이 기업 전반에 급격하게 확산되고 있습니다.
그렇다면 코딩하지 않는 개발자에게는 무엇이 중요해질까요? 서비스형 통합플랫폼 데이터업체 부미(Boomi)의 최고 제품 및 기술 책임자 에드 마코스키는 전통적인 코딩 전문 기술과 함께 로우코드/노코드 플랫폼 활용 능력, AI 기술을 통합하는 방법에 대한 이해, 이런 툴을 사용한 팀 내에서의 효과적인 협업까지 포함하도록 스킬셋이 발전할 것이라고 강조했습니다.
로우 코드와 코파일럿의 조합을 통해 개발자는 프로그래밍 언어를 배우는 데 시간을 할애할 필요 없이 비즈니스 성과를 지원하는 데 집중할 수 있게 됩니다.</p>
<p>소프트웨어 개발에는 많은 반복 작업이 수반됩니다. 이는 부인할 수 없는 사실입니다. 로우코드 플랫폼은 이런 작업을 간소화해 개발자가 더 나은 결과를 도출하고, 핵심 문제 해결에 집중하도록 돕습니다.</p>
<p>--이후 기사 내용은 로우코드/노코드를 통해 할 수 있는 성과들에 대한 글이라 생략하겠습니다--</p>
<h3 id="생각">생각</h3>
<p>로우코드·노코드 플랫폼, 생성형 AI가 나날이 발전하는 만큼, 개발에 대한 진입장벽이 많이 낮아질 것이라고 생각합니다. 
그에 따라 시간이 지날 수록, 기업들은 개발자들에게 개발 실력보다 비즈니스 프로세스 인지능력과 CS 능력을 더 높은 가치로서 요구할 것입니다.
왜냐하면, 개발실력이 출중하지 않더라도 비즈니스 프로세스 인지능력, 즉 내가 무엇을 만들 것인지, 또는 어떻게 만들 것인지를 인지하는 능력이 있다면, 앞서 글에서 살펴본 노코드/로우코드 플랫폼을 이용하거나 생성형AI의 도움을 받아 서비스를 창출해낼 수 있는 시대에 와있기 때문입니다.
또한 문제에 직면했을 때, 트러블슈팅을 원할히 해결할 수 있도록 도와주는 데 있어서 CS지식이 큰 기여를 한다고 생각합니다.</p>
<p>앞서 말한 것들을 종합하여, 제가 더 출중한 개발자가 되기위해서는 클린코드나 프레임워크 등 기술적인 면모들도 중요하지만 커뮤니케이션 능력과 비즈니스 프로세스 인지 능력을 기르는 데 노력하고 CS적인 지식들을 점진적으로 넓혀나가야할 것입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[피마 인디언 당뇨병 예측 분류]]></title>
            <link>https://velog.io/@comodoking_0128/%ED%94%BC%EB%A7%88-%EC%9D%B8%EB%94%94%EC%96%B8-%EB%8B%B9%EB%87%A8%EB%B3%91-%EC%98%88%EC%B8%A1-%EB%B6%84%EB%A5%98</link>
            <guid>https://velog.io/@comodoking_0128/%ED%94%BC%EB%A7%88-%EC%9D%B8%EB%94%94%EC%96%B8-%EB%8B%B9%EB%87%A8%EB%B3%91-%EC%98%88%EC%B8%A1-%EB%B6%84%EB%A5%98</guid>
            <pubDate>Sun, 08 Dec 2024 05:49:29 GMT</pubDate>
            <description><![CDATA[<h3 id="1-문제-정의-및-기획">1. 문제 정의 및 기획</h3>
<p>많은 사람들이 데이터 분석을 공부할 때 피마 인디언 당뇨병 예측으로 시작한다.
나도 본격적으로 데이터 분석을 공부 해보기로 했다.</p>
<p>미국 애리조나주 그랜드캐년에서 사진을 찍으면 가장 잘 나오는 위치인 피마포인트는 그 지역에 살고 있는 인디언 부족 피마의 이름에서 유래한 것이다.</p>
<blockquote>
<p>피마 포인트의 경관<img src="https://velog.velcdn.com/images/comodoking_0128/post/f237bad5-7968-4af6-84e4-27ec5b5d9aac/image.png" alt=""></p>
</blockquote>
<p>피닉스대 연구팀은 1979년에 피마 인디언의 당뇨병 유병률이 다른 어떤 인구 집단보다 높다는 논문을 발표했다. 미국 미네소타주 로체스터와 비교하면 19배나 높을 정도였다.</p>
<p>아메리카 대륙의 인디언들은 조상들이 유라시아 대륙에서 베링해를 거쳐 넘어왔다. 춥고 음식이 부족한 상태에서 목적지도 확실치 않은 채 베링해를 넘어오는 것은 목숨을 건 여정이었다. 음식이 부족한 상태에서 음식을 섭취할 기회가 생기면 저장해 놓아야만 가혹한 환경 조건을 이겨낼 수 있었으므로 저장 능력을 키운 이들만 살아남을 수 있었을 것이다.</p>
<p> 연구팀은 멕시코  피마 인디언의 생활 방식이 애리조나에서와 유사해지기 시작하면서 멕시코 피마 인디언의 비만과 당뇨병 유병률이 증가하고 있음을 발견했다. 두 집단이 제2형 당뇨병을 유발할 수 있는 유전자를 공통적으로 가지고 있지만 생활습관의 차이가 비만과 당뇨병 발병에 차이를 가져온 것이다. </p>
<p> 인용 : <a href="https://m.dongascience.com/news.php?idx=66211">https://m.dongascience.com/news.php?idx=66211</a></p>
<h3 id="2-데이터-수집">2. 데이터 수집</h3>
<p>데이터셋은 캐글의 Pima Indians Diabetes Database를 받았다.
출처 : <a href="https://www.kaggle.com/datasets/uciml/pima-indians-diabetes-database">https://www.kaggle.com/datasets/uciml/pima-indians-diabetes-database</a></p>
<blockquote>
<pre><code class="language-python">import pandas as pd
data = pd.read_csv(&quot;/content/diabetes.csv&quot;)
data.head()</code></pre>
</blockquote>
<pre><code>![](https://velog.velcdn.com/images/comodoking_0128/post/1e37888e-65cd-4de6-b062-d3e61ccd73c4/image.png)
#### 데이터셋 확인
Pregnancies: 임신 횟수
Glucose: 포도당 부하 검사 수치
BloodPressure: 혈압(mm Hg)
SkinThickness: 팔 삼두근 뒤쪽의 피하지방 측정값(mm)
Insulin: 혈청 인슐린(mu U/ml)
BMI: 체질량지수(체중(kg)/키(m))^2
DiabetesPedigreeFunction: 당뇨 내력 가중치 값
Age: 나이
Outcome: 클래스 결정 값( 0 또는 1 )



### 3. 데이터 분석 및 인사이트 도출


&gt;```python
data[&#39;Outcome&#39;].value_counts()</code></pre><p><img src="https://velog.velcdn.com/images/comodoking_0128/post/439dc900-1bce-42dc-9a8f-72aa11d1dc28/image.png" alt="">
음성이 양성보다 상대적으로 많다.</p>
<pre><code class="language-python">data.info()</code></pre>
<p><img src="https://velog.velcdn.com/images/comodoking_0128/post/2964261d-6e3f-4082-9a1e-5b8285632c92/image.png" alt="">
모두 768개의 값으로 결측값은 존재하지 않는다.</p>
<pre><code class="language-python">data.describe()</code></pre>
<p><img src="https://velog.velcdn.com/images/comodoking_0128/post/e8eb873e-8437-4f1f-ba9a-ce21de555bf4/image.png" alt="">
BMI 지수의 평균이 32에 육박하는데 이는 과체중에 해당하는 범위이다.</p>
<pre><code class="language-python">import seaborn as sns
correlation_matrix = data.corr()
sns.heatmap(correlation_matrix, annot=True, cmap=&#39;Blues&#39;, fmt=&#39;.1f&#39;)</code></pre>
<p><img src="https://velog.velcdn.com/images/comodoking_0128/post/304e8f63-0dbe-4181-b0d4-880b17c941b7/image.png" alt="">
상관관계 히트맵을 그려본 결과, 당뇨병과 포도당 부하 검사 수치가 가장 큰 상관관계를 갖는 것을 알게 되었다.</p>
<pre><code class="language-python">import matplotlib.pyplot as plt
import seaborn as sns
plt.figure(figsize=(10, 6))
sns.histplot(data=data, x=&#39;Glucose&#39;, hue=&#39;Outcome&#39;, kde=True, bins=30)
plt.title(&#39;Diabetes and Glucose Load Test Histogram&#39;)
plt.xlabel(&#39;Glucose Load Test Value&#39;)
plt.ylabel(&#39;Frequency&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/comodoking_0128/post/1d8e0a43-e1d2-4057-844e-d5e2a2d42877/image.png" alt="">
음성군들은 포도당 부하 검사 수치 50 ~ 130 정도의 밀집해있고, 양성군들은 130이상을 넘기고 밀집해있다.</p>
<h3 id="4-모델-학습">4. 모델 학습</h3>
<p>로지스틱 회귀를 사용하여 예측해보기로 했다.</p>
<pre><code class="language-python">from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
x = data.iloc[:, :-1]
y = data.iloc[:, -1]
train_input , test_input, train_target, test_target = train_test_split(x, y, test_size=0.2, stratify=y)
lr = LogisticRegression(max_iter=500)
lr.fit(train_input, train_target)</code></pre>
<h3 id="5-모델-평가-및-개선">5. 모델 평가 및 개선</h3>
<blockquote>
<h4 id="평가함수">평가함수</h4>
</blockquote>
<pre><code class="language-python">from sklearn.metrics import confusion_matrix, accuracy_score, precision_score, recall_score, f1_score, roc_auc_score
def get_clf_eval(test_target, pred=None, pred_proba=None):
    confusion = confusion_matrix(test_target, pred) #혼동행렬
    accuracy = accuracy_score(test_target, pred) # 정확도
    precision = precision_score(test_target, pred) # 정밀도
    recall = recall_score(test_target, pred) # 재현율
    f1 = f1_score(test_target, pred) # F1 점수
    if pred_proba is not None:
        roc_auc = roc_auc_score(test_target, pred_proba[:, 1])  # ROC AUC (예측 확률 사용)
    else:
        roc_auc = roc_auc_score(test_target, pred) 
    print(&quot;Confusion Matrix:\n&quot;, confusion)
    print(&quot;Accuracy: &quot;, accuracy)
    print(&quot;Precision: &quot;, precision)
    print(&quot;Recall: &quot;, recall)
    print(&quot;F1 Score: &quot;, f1)
    print(&quot;ROC AUC: &quot;, roc_auc)</code></pre>
<blockquote>
<h4 id="평가">평가</h4>
</blockquote>
<pre><code class="language-python">pred = lr.predict(test_input)
get_clf_eval(test_target, pred)</code></pre>
<p><img src="https://velog.velcdn.com/images/comodoking_0128/post/0846853e-91f9-49bb-abfc-32fbb10ff50c/image.png" alt="">
성능이 크게 떨어지지는 않지만 아쉬운 감이 있다.</p>
<p>그래서 머신러닝의 국밥 그레디언트 부스팅을 이용해 다시 한번 학습시켜보았다.</p>
<blockquote>
<h4 id="그레디언트-부스팅">그레디언트 부스팅</h4>
</blockquote>
<pre><code class="language-python">from sklearn.ensemble import GradientBoostingClassifier
gb = GradientBoostingClassifier(n_estimators=100, learning_rate=0.1, max_depth=3)
gb.fit(train_input, train_target)
pred = gb.predict(test_input)
get_clf_eval(test_target, pred)</code></pre>
<p><img src="https://velog.velcdn.com/images/comodoking_0128/post/a3c0608e-a00d-4a0a-a62a-d17b1e15c17e/image.png" alt="">
로지스틱 회귀를 사용한 모델보다 성능이 개선된 모습을 보여준다.</p>
<p><em>제일 먼저 히스토그램 기반 그레디언트 부스팅을 사용해보았지만 데이터셋이 작은 탓인지 그레디언트 부스팅보다 성능이 낮은 모습을 보여주었다.</em></p>
<h3 id="6-결과-시각화-및-커뮤니케이션">6. 결과 시각화 및 커뮤니케이션</h3>
<blockquote>
<h4 id="로지스틱-모델과-그레디언트-부스팅-모델을-비교해보겠다">로지스틱 모델과 그레디언트 부스팅 모델을 비교해보겠다.</h4>
</blockquote>
<pre><code class="language-python">models = [&#39;Logistic Regression&#39;, &#39;Gradient Boosting&#39;]
accuracy = [accuracy_score(test_target, lr.predict(test_input)), accuracy_score(test_target, gb.predict(test_input))]
precision = [precision_score(test_target, lr.predict(test_input)), precision_score(test_target, gb.predict(test_input))]
recall = [recall_score(test_target, lr.predict(test_input)), recall_score(test_target, gb.predict(test_input))]
f1 = [f1_score(test_target, lr.predict(test_input)), f1_score(test_target, gb.predict(test_input))]
roc_auc = [roc_auc_score(test_target, lr.predict_proba(test_input)[:, 1]), roc_auc_score(test_target, gb.predict_proba(test_input)[:, 1])]
metrics_df = pd.DataFrame({
    &#39;Model&#39;: models,
    &#39;Accuracy&#39;: accuracy,
    &#39;Precision&#39;: precision,
    &#39;Recall&#39;: recall,
    &#39;F1 Score&#39;: f1,
    &#39;ROC AUC&#39;: roc_auc
})
metrics_df.set_index(&#39;Model&#39;, inplace=True)
metrics_df.plot(kind=&#39;bar&#39;, figsize=(10, 6))
plt.title(&#39;Model Performance Comparison&#39;)
plt.ylabel(&#39;Score&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/comodoking_0128/post/86592c2f-e926-4da1-aedf-f69d59bfad35/image.png" alt="">
그레디언트 부스팅 모델이 전반적으로 성능이 우수하지만 특히 재현율에서 성능이 월등히 좋다는 것을 알 수 있다.</p>
<pre><code class="language-python">from sklearn.metrics import ConfusionMatrixDisplay
fig, axes = plt.subplots(1, 2, figsize=(12, 6))
# 로지스틱 회귀 혼동행렬 시각화한 것
cm_lr = confusion_matrix(test_target, lr.predict(test_input))
ConfusionMatrixDisplay(cm_lr, display_labels=[&#39;True&#39;, &#39;False&#39;]).plot(ax=axes[0], cmap=&#39;Blues&#39;)
axes[0].set_title(&#39;Logistic Regression&#39;)
# 그레디언트 부스팅 혼동행렬 시각화한 것
cm_gb = confusion_matrix(test_target, gb.predict(test_input))
ConfusionMatrixDisplay(cm_gb, display_labels=[&#39;True&#39;, &#39;False&#39;]).plot(ax=axes[1], cmap=&#39;Reds&#39;)
axes[1].set_title(&#39;Gradient Boosting&#39;)
plt.tight_layout()
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/comodoking_0128/post/b9902e8e-83d0-4482-bb7e-44c4a9e8d2bb/image.png" alt="">
위 이미지에서 로지스틱 회귀는 119개, 그레디언트 부스팅은 125개의 데이터를 예측 성공한 것을 볼 수 있다.</p>
<h3 id="⚡️-피드백-환영합니다-⚡️">⚡️ 피드백 환영합니다 ⚡️</h3>
<p>직접 찾아보며 하는 것은 처음이다 보니 오류가 많을 수 있습니다.
오류를 발견하시면 알려주시면 감사하겠습니다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[모델 평가하기]]></title>
            <link>https://velog.io/@comodoking_0128/%EB%AA%A8%EB%8D%B8-%ED%8F%89%EA%B0%80%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@comodoking_0128/%EB%AA%A8%EB%8D%B8-%ED%8F%89%EA%B0%80%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 08 Dec 2024 04:06:56 GMT</pubDate>
            <description><![CDATA[<h2 id="평가함수">평가함수</h2>
<pre><code class="language-python">from sklearn.metrics import confusion_matrix, accuracy_score, precision_score, recall_score, f1_score, roc_auc_score

def get_clf_eval(y_test, pred=None, pred_proba=None):
    confusion = confusion_matrix(y_test, pred) #혼동행렬
    accuracy = accuracy_score(y_test, pred) #정확도
    precision = precision_score(y_test, pred) #정밀도
    recall = recall_score(y_test, pred) #재현율
    f1 = f1_score(y_test, pred) #F1점수

    print(&quot;Confusion Matrix:\n&quot;, confusion)
    print(&quot;Accuracy: &quot;, accuracy)
    print(&quot;Precision: &quot;, precision)
    print(&quot;Recall: &quot;, recall)
    print(&quot;F1 Score: &quot;, f1)
</code></pre>
<p>모델을 평가하기 위해 찾아보니 위와 같은 경우가 많았다.
뭐가 뭔지 하나씩 뜯어보겠다.</p>
<h2 id="혼동행렬">혼동행렬</h2>
<p><img src="https://velog.velcdn.com/images/comodoking_0128/post/881371c0-33b6-4d23-98d6-72d86c8acc34/image.png" alt="">
어떤 개인이나 <strong>모델</strong>, 검사도구, 알고리즘의 <strong>진단·분류·판별·예측 능력을 평가</strong>하기 위하여 고안된 표</p>
<p>코로나 19를 주제로 하여 예를 들어 보자. 어떤 사람이 코로나 감염이 의심되어 검사를 했을 때, 검사 결과는 양성 혹은 음성으로 나올 것이다. 여기서 양성이 나왔다는 것은 그 사람이 코로나에 감염되었다고 예측한다는 의미이고, 음성은 코로나에 감염되지 않았다고 예측한다는 의미가 된다. <strong>문제는 현실이 진단 결과와 다를 수 있다는 데 있다</strong>. 의료진의 뒷목을 잡게 만드는 상황은 두 가지로, 검사 결과가 양성인데 실제로는 감염되지 않았던 경우, 그리고 검사 결과는 음성인데 실제로는 감염자였던 경우다. 이런 상황들이 많을수록 그 검사 키트는 못 믿을 물건이 되고 만다. 그렇다면 검사 키트를 만드는 업체에서는 이런 두 가지 상황은 최소한으로 줄이면서, 감염자는 정확히 양성으로, 비감염자는 정확히 음성으로 판정할 수 있는 검사 키트를 만들고자 할 것이다.</p>
<p>혼동행렬은 모델의 예측 결과를 실제값과 비교하여 분류 모델의 성능을 평가하는 도구로, 2개의 클래스(양성, 음성)에 대해 4개의 값을 계산한다:</p>
<p>True Positive (TP): 실제 양성 클래스인 데이터 중에서 모델이 양성이라고 예측한 수
True Negative (TN): 실제 음성 클래스인 데이터 중에서 모델이 음성이라고 예측한 수
False Positive (FP): 실제 음성 클래스인 데이터 중에서 모델이 양성이라고 잘못 예측한 수
False Negative (FN): 실제 양성 클래스인 데이터 중에서 모델이 음성이라고 잘못 예측한 수
혼동행렬을 통해 각 클래스에 대해 모델이 어떻게 예측했는지 시각적으로 확인할 수 있다.</p>
<p>출처
<a href="https://namu.wiki/w/%ED%98%BC%EB%8F%99%ED%96%89%EB%A0%AC">https://namu.wiki/w/%ED%98%BC%EB%8F%99%ED%96%89%EB%A0%AC</a></p>
<h2 id="정확도">정확도</h2>
<p>정확도는 전체 샘플의 개수들 중에서 얼마나 나의 알고리즘이 정답이라고 예측한 샘플이 포함되었는지의 비율을 의미한다. 예를 들어서, 내 알고리즘이 90% 정확하다면, 100개의 샘플들 중에서 90개만 정확하게 분류 하는 것이다. 
<img src="https://velog.velcdn.com/images/comodoking_0128/post/6bcf9e5b-344b-4162-92e1-28ce81e3cd9f/image.png" alt=""></p>
<p>높은 정확도는 모델이 잘 작동한다고 판단할 수 있다. 하지만 데이터가 불균형할 경우, 예를 들어 한 클래스의 비율이 매우 높을 때는 정확도가 높아도 모델이 불균형을 잘 반영하지 못할 수 있다. <em># 이를 샘플링 편향이라 함.</em></p>
<h2 id="정밀도">정밀도</h2>
<p>정밀도란 모델이 True라고 분류한 것 중에서 실제 True인 것의 비율이다.</p>
<p><img src="https://velog.velcdn.com/images/comodoking_0128/post/679fc14b-bf4d-437d-bdd3-40fc9d7ae905/image.png" alt=""></p>
<p>정확도와 정밀도의 차이로는, 정확도는 전체 샘플에 대해서 모델이 올바르게 예측한 비율이고 정밀도는 오로지 양성 샘플에 대해서 올바른 비율로 각자가 대상하는 데이터 범위가 다르다.</p>
<p>Positive 정답률, PPV(Positive Predictive Value)라고도 불린다.</p>
<h2 id="재현율">재현율</h2>
<p>재현율이란 실제 True인 것 중에서 모델이 True라고 예측한 것의 비율이다. </p>
<p><img src="https://velog.velcdn.com/images/comodoking_0128/post/b80b05fc-509b-48e2-a951-aba890871d6f/image.png" alt="">
Precision이나 Recall은 모두 실제 True인 정답을 모델이 True라고 예측한 경우에 관심이 있으나, 바라보고자 하는 관점만 다르다. 정밀도는 모델의 입장에서, 그리고 재현율은 실제 정답의 입장에서 정답을 정답이라고 맞춘 경우를 바라보고 있다.</p>
<h2 id="f1-점수">F1 점수</h2>
<p>정밀도와 재현율 모두 완벽한 평가지표가 아니다.
F1점수는 어느 한쪽으로 치우치지 않고, 정밀도와 재현율을 결합한 지표이다.
F1 점수는 정밀도와 재현율의 조화 평균으로, 0과 1사이의 값이며 1에 가까울 수록 모델의 성능이 좋다는 것을 나타낸다.
<img src="https://velog.velcdn.com/images/comodoking_0128/post/1edb5d83-0ec7-4f23-9235-602500aa95d6/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CS]  폭발적인 병렬처리기법 ~ 파이프라이닝!]]></title>
            <link>https://velog.io/@comodoking_0128/CS-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B4%EB%8B%9D</link>
            <guid>https://velog.io/@comodoking_0128/CS-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B4%EB%8B%9D</guid>
            <pubDate>Mon, 02 Dec 2024 07:18:59 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/comodoking_0128/post/e665c830-8a87-4fe9-af92-f3ea0c24cd28/image.gif" alt=""></p>
<h3 id="빠른-cpu를-만들기-위해-cpu를-쉬지않게-만들어보겠다">빠른 CPU를 만들기 위해 CPU를 쉬지않게 만들어보겠다.</h3>
<h2 id="-챕터-1--명령어-파이프라인-">-<del>챕터 1 : 명령어 파이프라인</del>-</h2>
<p>명령어 파이프라인을 알아보기 앞서, 하나의 명령어가 처리되는 전체 과정을 비슷한 시간 간격으로 나누어 보자. 명령어 처리 과정을 클럭 단위로 나누어 보면 일반적으로 다음과 같이 나눌 수 있다.</p>
<blockquote>
<p>1 . 명령어 인출
2 . 명령어 해석
3 . 명령어 실행
4. 결과 저장</p>
</blockquote>
<p>여기서 중요한 점은 같은 단계가 겹치지만 않는다면 <strong>CPU는 각 단계를 동시에 실행 할 수 있다.</strong></p>
<p>무슨 말인지 한번 살펴보자.</p>
<p>CPU는 명령어를 다음과 같이 처리한다.
<img src="https://velog.velcdn.com/images/comodoking_0128/post/4fcafe68-a671-482e-9926-8f483549709c/image.png" alt=""></p>
<p>하지만 <strong>명령어 파이프라이닝 기법</strong>을 사용하면 다음과 같이 처리할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/comodoking_0128/post/9a7759a0-611d-4d6d-8251-d5eed431740a/image.png" alt=""></p>
<p>처음보다 효율적으로 처리할 수 있단 것을 알 수 있다.</p>
<p>파이프라이닝이 높은 성능을 가져오기는 하지만, 특정 상황에서는 성능 향상에 실패하는 경우도 있다! 이러한 상황을 <strong>파이프라인 위험</strong>이라고 한다.</p>
<h2 id="-챕터-2--파이프라인-위험-">-<del>챕터 2 : 파이프라인 위험</del>-</h2>
<p>파이프라인 위험에는 크게 <strong>데이터 위험</strong>, <strong>제어 위험</strong>, <strong>구조적 위험</strong>이 있다.
<img src="https://velog.velcdn.com/images/comodoking_0128/post/fa9b43d5-9d22-4587-9bee-080539862175/image.png" alt=""></p>
<h3 id="-챕터-2-1--데이터-위험-">-<del>챕터 2-1 : 데이터 위험</del>-</h3>
<p>데이터 위험은 명령어 간 <strong>데이터 의존성</strong>에 의해 발생한다.
모든 명령어를 동시에 처리할 수는 없다.</p>
<p>어떤 명령어는 이전 명령어를 끝까지 실행해야만 실행할 수 있는 경우가 있다.</p>
<p>예를 들어 다음과 같은 명령어가 있다.
<em>R은 레지스터를 뜻함.</em></p>
<p>명령어 1 : <strong>R1</strong> = R2 + R3 
명령어 2 : R4 = <strong>R1</strong> + R5</p>
<p>명령어 1를 실행해야만 명령어 2를 수행할 수 있다. 
만약 명령어 1의 실행이 끝나기 전에 명령어 2를 인출하면 원치않은 R1 값을 읽어들일 수 있다. 따라서 <strong>명령어 2는 명령어 1의 데이터에 의존적이다</strong>.
이처럼 데이터 의존성에 의해 파이프라인이 제대로 작동하지 않는 것을 <strong>데이터 위험</strong>이라고 한다.</p>
<h3 id="-챕터-2-2--제어-위험-">-<del>챕터 2-2 : 제어 위험</del>-</h3>
<p>제어 위험은 주로 분기 등으로 인한 <strong>프로그램 카운터의 갑작스러운 변화</strong>에 의해 발생한다.
기본적으로 프로그램 카운터는 <strong>현재 실행 중인 명령어의 다음 주소</strong>를 가르킨다.
하지만 프로그램 실행 흐름이 바뀌어 <del>ex) 인터럽트</del> 명령어가 실행되면서 <strong>프로그램 카운터 값에 갑작스러운 변화가 생긴다면 명령어 파이프라인에서 이미 처리 중이던 명령어들은 쓸모가 없어지게 된다.</strong>
이를 <strong>제어 위험</strong>이라고 한다.
<img src="https://velog.velcdn.com/images/comodoking_0128/post/46260704-bbff-4559-825c-22369c578a42/image.png" alt="">
참고로 이를 해결하기 위해 사용하는 기술 중 하나가 <strong>분기 예측</strong>이다.
분기 예측은 프로그램이 어디로 분기할지 미리 예측한 후 그 주소를 인출하는 기술이다.</p>
<h2 id="-챕터-3--슈퍼스칼라-">-<del>챕터 3 : 슈퍼스칼라</del>-</h2>
<p>오늘날의 대부분의 CPU에서는 여러 개의 파이프라인을 이용한다. 
이처럼 CPU 내부에 <strong>여러 개의 명령어 파이프라인을 포함</strong>한 구조를 <strong>슈퍼스칼라</strong>라고 한다.
<img src="https://velog.velcdn.com/images/comodoking_0128/post/544fe95f-a8b3-4b1e-b6b8-1f56bd938707/image.png" alt="">
슈퍼스칼라 구조로 명령어 처리가 가능한 CPU를 <strong>슈퍼스칼라 프로세서</strong> 또는 <strong>슈퍼스칼라 CPU</strong>라고 한다.</p>
<p>슈퍼스칼라 프로세서는 이론적으로 파이프라인 개수에 비례하여 프로그램 처리 속도가 빨라진다. 하지만 실제로는 파이프라인 위험 등의 문제들을 직면할 수 있어 <strong>반드시 파이프라인 개수에 비례하여 처리 속도가 빨라지지는 않는다.</strong> 이 때문에 슈퍼스칼라 CPU는 파이프라인 위험을 방지하기 위해 고도로 설계되어야 한다.</p>
<h2 id="-챕터-4--비순차적-명령어-처리-">-<del>챕터 4 : 비순차적 명령어 처리</del>-</h2>
<p>오늘날 CPU 성능 향상에 큰 기여를 한 기법이자 대부분의 CPU가 차용하는 기법이다.
지금까의 파이프라이닝, 슈퍼스칼라는 순차적으로 진행되었다.
하지만 파이프라인 위험과 같은 문제들로 인해 이따금씩 명령어를 곧바로 처리하지 못하기도 하였다.
<strong>만약 모든 명령어를 순차적으로만 처리한다면 이런 예상치 못한 상황에서 명령어 파이프라인은 멈춰버리게 된다.</strong>
<img src="https://velog.velcdn.com/images/comodoking_0128/post/a01990e2-0f48-4c8f-a994-c6e11bad681a/image.png" alt=""></p>
<p>사진에서도 2번 명령어가 처리되기 전까지 모든 명령어가 지연되는 상황이다.
<strong>괜히 데이터 의존성도 없는 명령어들까지 피해를 보는 실정이다</strong>.</p>
<p>그렇기에 명령어를 순차적으로만 실행하지않고 <strong>순서를 바꿔 실행해도 무방한 명령어를 먼저 실행하여</strong> 명령어 파이프라인이 멈추는 것을 방지하는 기법, <strong>비순차적 명령어 처리 기법</strong>을 사용한다.</p>
<p><img src="https://velog.velcdn.com/images/comodoking_0128/post/feb16581-9aac-43f8-bd90-3088f19c1fd3/image.png" alt="">
데이터 의존성이 있는 3번 명령어를 <strong>순서를 바꿔 뒤늦게 실행함으로써 효율적으로 병렬 처리를 할 수 있게 되었다.</strong></p>
<p>이처럼 비순차적 명령어 처리가 가능한 CPU는 <strong>명령어들이 어떤 명령어와 데이터와 의존성을 가지고 있는지, 순서를 바꿔 실행할 수 있는 명령어에는 어떤 것들이 있는지를 판단할 수 있어야 한다.</strong></p>
<h1 id="-정리-">-<del>정리</del>-</h1>
<p>오늘은 CPU 내부에서 명령어를 병렬처리하여 성능을 높히는 법을 알게되었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[이중해싱법 && 체인법]]></title>
            <link>https://velog.io/@comodoking_0128/%EC%9D%B4%EC%A4%91%ED%95%B4%EC%8B%B1%EB%B2%95-%EC%B2%B4%EC%9D%B8%EB%B2%95</link>
            <guid>https://velog.io/@comodoking_0128/%EC%9D%B4%EC%A4%91%ED%95%B4%EC%8B%B1%EB%B2%95-%EC%B2%B4%EC%9D%B8%EB%B2%95</guid>
            <pubDate>Tue, 19 Nov 2024 02:04:23 GMT</pubDate>
            <description><![CDATA[<h3 id="이중해싱법">이중해싱법</h3>
<blockquote>
<p>선형조사법은 클러스터링 문제, 이차조사법은 2차 클러스터링 문제를 겪고있었습니다. 따라서 이중해싱법은 클러스터링 문제를 해결한 조사방법입니다. 
오버플로우가 발생함에 따라 항목을 저장할 다음 위치를 결정할 때, <strong>원래 해시 함수와 다른 별개의 해시 함수를 이용하는 방법입니다.</strong></p>
</blockquote>
<h4 id="코드-구현">코드 구현</h4>
<pre><code>#include &lt;stdio.h&gt;
int i,k,n=8;
int doublehash(int key) //별개의 해시함수
{
    if(key&gt;20) return 4;
    else return 5;
}
int hash(int key)
{
    return key%n;
}
int main()
{
    int key;
    int list[8]={0,0,10,3,2,5,0,0};
    scanf(&quot;%d&quot;,&amp;key);
    int index=hash(key);
    while(1)
    {
        if(list[index]==0)
        {
            list[index]=key;
            break;
        }
        else
        {
            k++;
            index=(hash(key)+doublehash(key)*k)%n;
        }
    }
    printf(&quot;%d&quot;,index);
    return 0;
}</code></pre><h3 id="체인법">체인법</h3>
<blockquote>
<p>선형 조사법이 탐색 시간이 많이 걸리는 이유는 충돌 때문에 해시 주소가 다른 키하고도 비교를 해야 하는데 있습니다. <strong>만약 해시 주소가 같은 키만을 하나의 리스트로 묶어둔다면 불필요한 비교는 하지 않아도 될 것입니다.</strong>
이러한 부분 덕분에 체인법은 성능이 좋습니다.
하지만 리스트의 공간이 따로 필요하게 됩니다.</p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>