<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>65535.log</title>
        <link>https://velog.io/</link>
        <description>순간은 기록하고 반복은 단순화하자 🚀</description>
        <lastBuildDate>Mon, 01 Dec 2025 01:43:59 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>65535.log</title>
            <url>https://velog.velcdn.com/images/junr_65535/profile/ec0fdd7a-543d-4f37-bd39-79a76afe1e13/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 65535.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/junr_65535" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[5장. 비동기 연동, 언제 어떻게 써야 할까]]></title>
            <link>https://velog.io/@junr_65535/5%EC%9E%A5.-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%97%B0%EB%8F%99-%EC%96%B8%EC%A0%9C-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%8D%A8%EC%95%BC-%ED%95%A0%EA%B9%8C</link>
            <guid>https://velog.io/@junr_65535/5%EC%9E%A5.-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%97%B0%EB%8F%99-%EC%96%B8%EC%A0%9C-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%8D%A8%EC%95%BC-%ED%95%A0%EA%B9%8C</guid>
            <pubDate>Mon, 01 Dec 2025 01:43:59 GMT</pubDate>
            <description><![CDATA[<h2 id="5장-비동기-연동-동기-방식의-문제점과-5가지-구현-방법-정리">5장. 비동기 연동: 동기 방식의 문제점과 5가지 구현 방법 정리</h2>
<h3 id="1-동기-연동-synchronous-integration-방식">1. 동기 연동 (Synchronous Integration) 방식</h3>
<p><strong>동기 방식</strong>은 코드가 순차적으로 실행되며, <strong>한 작업이 끝날 때까지 다음 작업이 진행되지 않는</strong> 방식입니다. 이는 코드의 흐름을 직관적으로 이해하고 디버깅하기 쉽다는 장점이 있습니다.</p>
<h4 id="동기-방식-연동의-문제점">동기 방식 연동의 문제점</h4>
<p>그러나 외부 서비스 연동 시 동기 방식을 사용하면 여러 문제가 발생합니다.</p>
<ol>
<li><strong>전체 기능 실패 위험:</strong> 외부 연동이 실패하면 해당 연동이 필수적이라고 가정했을 경우, 전체 기능(예: 포인트 지급 실패 시 로그인 실패)까지 사용할 수 없게 되어 <strong>전체 서비스가 마비</strong>될 수 있습니다.</li>
<li><strong>응답 시간 증가:</strong> 연동 서비스의 응답 시간이 길어지면 전체 응답 시간이 느려지며, 심한 경우 외부 연동 서비스로 인해 <strong>전체 서비스가 먹통</strong>이 되기도 합니다.</li>
</ol>
<h4 id="비동기-연동의-필요성">비동기 연동의 필요성</h4>
<p>다음 작업을 진행하기 위해 <strong>반드시 외부 연동 결과가 필요한 것이 아니라면</strong>, 동기 방식 대신 비동기 방식을 고려해야 합니다.</p>
<ul>
<li><strong>비동기 연동 (Asynchronous Integration):</strong> 한 작업이 끝날 때까지 기다리지 않고 바로 다음 작업을 처리하여, 외부 연동이 끝날 때까지 기다리지 않고 다음 작업을 진행할 수 있습니다.</li>
<li><strong>주요 이점:</strong> 사용자는 연동 시간에 소요되는 시간만큼 더 빠르게 응답을 받을 수 있으며, 연동 서비스에 문제가 생겨도 메인 서비스의 응답 시간은 증가하지 않습니다.</li>
</ul>
<h3 id="2-비동기-연동-구현-5가지-방식">2. 비동기 연동 구현 5가지 방식</h3>
<p>5장에서 다루는 비동기 연동 구현 방식은 다음과 같습니다:</p>
<ol>
<li>별도 스레드로 실행하기</li>
<li>메시징 시스템 이용하기</li>
<li>트랜잭션 아웃박스 패턴 사용하기</li>
<li>배치로 연동하기</li>
<li>CDC 이용하기</li>
</ol>
<h4 id="1-별도-스레드로-실행하기">(1) 별도 스레드로 실행하기</h4>
<p>가장 쉽고 간단하게 비동기 연동을 구현하는 방법입니다.</p>
<ul>
<li><strong>구현:</strong> 새로운 스레드를 생성하여 연동 코드를 실행하거나, 스레드 풀(<code>ExecutorService</code>)을 사용하거나, 스프링 프레임워크의 <code>@Async</code> 애너테이션 같은 프레임워크 기능을 이용할 수 있습니다.</li>
<li><strong>주의 사항:</strong><ul>
<li><code>@Async</code> 사용 시, 해당 메서드가 비동기로 실행된다는 사실을 호출하는 쪽에서 알기 어려울 수 있으므로, <strong>메서드 이름에 비동기 실행과 관련된 단어를 추가</strong>하여 가독성을 높이는 것이 좋습니다.</li>
<li><strong>익셉션 전파 불가:</strong> 별도 스레드로 실행되는 코드는 익셉션이 전파되지 않기 때문에, 연동 과정에서 발생한 오류를 <strong>코드 내부에서 직접 처리</strong>해야 합니다 (예: 재시도, 실패 로그 남기기).</li>
<li><strong>메모리 및 CPU 부하:</strong> 대량의 작업을 처리할 때 순간적으로 많은 스레드를 생성하면 메모리 사용량이 증가하고 CPU 스케줄링 부하가 발생할 수 있습니다. 스레드 풀을 사용하거나, 가상 스레드 또는 Go 언어의 고루틴 같은 경량 스레드를 고려할 수 있습니다.</li>
</ul>
</li>
</ul>
<h4 id="2-메시징-시스템-이용하기">(2) 메시징 시스템 이용하기</h4>
<p>서로 다른 시스템 간에 비동기로 연동할 때 주로 사용되는 방식입니다. 메시징 시스템이 두 시스템 사이에 위치하여 메시지를 주고받습니다.</p>
<ul>
<li><strong>작동 방식:</strong> 시스템 A(생산자)가 메시지를 생성하여 메시징 시스템에 전송하면, 메시징 시스템은 이 메시지를 시스템 B(소비자)에 전달합니다.</li>
<li><strong>주요 이점:</strong><ul>
<li><strong>버퍼 역할:</strong> 시스템 A의 트래픽이 증가하더라도 메시징 시스템이 메시지를 저장하는 버퍼 역할을 하므로, 시스템 B의 성능 저하가 시스템 A에 영향을 미치지 않습니다.</li>
<li><strong>확장 용이성:</strong> 시스템 A의 코드를 수정하지 않고도 메시징 시스템에 새로운 시스템 C를 연결하여 데이터를 수신하게 할 수 있어 확장이 용이합니다.</li>
</ul>
</li>
<li><strong>생산자 (Producer) 고려 사항:</strong><ul>
<li><strong>트랜잭션 연동:</strong> DB 트랜잭션이 완료된 (<strong>커밋/롤백된 후</strong>) 메시지를 전송해야, 트랜잭션 롤백으로 인해 잘못된 메시지가 발송되는 문제를 방지할 수 있습니다.</li>
<li><strong>메시지 전송 실패 처리:</strong> 타임아웃 등으로 전송에 실패할 경우, 오류를 무시하거나, 재시도(중복 메시지 전송 위험이 있음)하거나, 실패 로그를 남겨 후처리해야 합니다.</li>
</ul>
</li>
<li><strong>소비자 (Consumer) 고려 사항:</strong><ul>
<li><strong>중복 처리 방지:</strong> 생산자의 재시도나 소비자의 재수신으로 인해 중복 처리가 발생할 수 있으므로, <strong>메시지마다 고유 식별자(ID)를 부여</strong>하여 이미 처리한 메시지는 무시해야 합니다.</li>
<li><strong>멱등성 (Idempotent):</strong> 메시지 재수신에 따른 중복 처리에 대응하기 위해 <strong>API를 멱등성</strong>을 갖도록 구현해야 합니다.</li>
</ul>
</li>
<li><strong>메시지 종류:</strong><ul>
<li><strong>이벤트 (Event):</strong> 어떤 일이 발생했음을 알려주는 메시지 (&#39;주문함&#39;, &#39;배송을 완료함&#39;). 정해진 수신자가 없으며, 소비자 확장에 적합합니다.</li>
<li><strong>커맨드 (Command):</strong> 무언가를 요청하는 메시지 (&#39;포인트 지급하기&#39;, &#39;로그인 차단하기&#39;). 메시지를 수신할 기능(시스템)이 정해져 있습니다.</li>
</ul>
</li>
</ul>
<h4 id="3-트랜잭션-아웃박스-패턴-transactional-outbox-pattern-사용하기">(3) 트랜잭션 아웃박스 패턴 (Transactional Outbox Pattern) 사용하기</h4>
<p>메시지 데이터 유실을 방지하고 DB 트랜잭션과 메시지 전송을 안전하게 분리하는 패턴입니다.</p>
<ul>
<li><strong>핵심 원리:</strong> 실제 업무 로직의 DB 변경 작업과 메시지 데이터를 <strong>아웃박스 테이블(Outbox Table)</strong>에 추가하는 작업을 <strong>하나의 DB 트랜잭션 내에서</strong> 수행합니다.</li>
<li><strong>동작 과정:</strong><ol>
<li>업무 로직 수행 및 메시지 데이터를 아웃박스 테이블에 저장 (DB 트랜잭션 내).</li>
<li>트랜잭션 커밋. (트랜잭션 롤백 시 메시지 데이터도 함께 롤백되어 잘못된 메시지 전송 방지).</li>
<li>별도의 <strong>메시지 중계 프로세스</strong>가 주기적으로 아웃박스 테이블을 조회하여 메시징 시스템에 전송.</li>
<li>전송에 성공하면 아웃박스 테이블에 <strong>발송 완료 상태</strong>를 표기합니다.</li>
</ol>
</li>
<li><strong>순서 보장:</strong> 메시지 전송 순서가 중요하다면, 메시지 중계 프로세스가 특정 메시지 발송에 실패했을 때 루프를 멈추고 다음 재시도를 기다려야 합니다.</li>
</ul>
<h4 id="4-배치-전송-batch-transmission">(4) 배치 전송 (Batch Transmission)</h4>
<p>일정 간격으로 데이터를 전송하는 전통적인 비동기 연동 방법입니다. 메시징 시스템이 실시간 연동을 목표로 한다면, 배치는 특정 간격(예: 다음 날, 1시간 간격)으로 데이터를 전송합니다.</p>
<ul>
<li><strong>전형적인 과정:</strong><ol>
<li>DB에서 전송할 데이터를 조회합니다.</li>
<li>조회 결과를 <strong>파일(CSV, JSON 등)</strong>로 기록합니다.</li>
<li>FTP/SFTP/SCP와 같은 프로토콜을 이용해 파일을 연동 시스템에 전송합니다.</li>
</ol>
</li>
<li><strong>협의 사항:</strong> 파일 전송 시 <strong>송수신 주체, 시간, 경로 및 파일 이름 규칙</strong> 등을 사전에 협의해야 합니다.</li>
<li><strong>소비자 시스템 동작:</strong> 소비자 시스템은 처리 완료 후 같은 파일이 중복 처리되는 것을 막기 위해 <strong>파일을 다른 폴더로 이동(백업)</strong>해야 합니다.</li>
<li><strong>대안:</strong> 데이터 크기가 작거나 처리 항목이 적을 경우, 파일 대신 <strong>API를 이용해 일괄 전송</strong>하는 방식을 고려할 수 있으며, 같은 조직 내에서는 보안이 덜 엄격할 경우 읽기 전용으로 <strong>DB에 직접 접근</strong>하게 하는 방법도 있습니다.</li>
<li><strong>오류 대응:</strong> 전송에 실패하면 <strong>일정 시간 후에 재전송</strong>하는 기능을 구현해두는 것이 수작업을 줄이는 데 도움이 됩니다.</li>
</ul>
<h4 id="5-cdc-change-data-capture-이용하기">(5) CDC (Change Data Capture) 이용하기</h4>
<p>DBMS가 제공하는 데이터 변경 통지 기능을 활용하여, 변경된 데이터를 추적하고 대상 시스템에 전파하는 소프트웨어 설계 패턴입니다.</p>
<ul>
<li><strong>동작 원리:</strong> DB 데이터가 변경되면 (INSERT, UPDATE, DELETE), DB는 커밋된 변경분 데이터를 순서에 맞게 <strong>CDC 처리기</strong>에 전송합니다. CDC 처리기는 이 변경 데이터를 가공하거나 변환하여 대상 시스템(DB, 메시징 시스템, API 등)에 전파합니다.</li>
<li><strong>이점:</strong><ul>
<li><strong>코드 수정 최소화:</strong> 기존 시스템의 연동 코드를 추가하거나 수정하기 부담스러울 때 유용합니다.</li>
<li><strong>데이터 동기화:</strong> DB 간 데이터 동기화가 목적이라면 DB와 DB 사이에 CDC를 두어 데이터를 복제할 수 있습니다.</li>
<li><strong>확장성:</strong> 변경 데이터를 메시징 시스템에 전파하면 여러 시스템에 데이터를 전달할 수 있어 확장에 유리합니다.</li>
</ul>
</li>
<li><strong>참고:</strong> CDC는 데이터의 변경 분을 전달하며, 이는 이벤트 메시지와 유사하지만 변경 분을 통해 의미를 도출해야 하므로 이벤트처럼 정확하게 의미를 전달하지는 못합니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[7장 IO 병목, 어떻게 해결하지]]></title>
            <link>https://velog.io/@junr_65535/7%EC%9E%A5-IO-%EB%B3%91%EB%AA%A9-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%95%B4%EA%B2%B0%ED%95%98%EC%A7%80</link>
            <guid>https://velog.io/@junr_65535/7%EC%9E%A5-IO-%EB%B3%91%EB%AA%A9-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%95%B4%EA%B2%B0%ED%95%98%EC%A7%80</guid>
            <pubDate>Mon, 01 Dec 2025 01:42:21 GMT</pubDate>
            <description><![CDATA[<h3 id="블로킹">블로킹</h3>
<ul>
<li>블로킹이란 작업이 완료될때까지 스레드가 대기하는 것을 의미한다.</li>
<li>주로 데이터의 입출력 과정에서 블로킹이 발생하기 때문에 블로킹 IO 라고도 한다.</li>
</ul>
<h3 id="컨텍스트-스위칭">컨텍스트 스위칭</h3>
<ul>
<li>운영체제는 여러 스레드를 번갈아 가면서 CPU에 할당한다.</li>
<li>CPU가 스레드를 전환하려면 현재 실행중인 스레드의 상태를 기록하고 다음에 실행할 스레드의 상태 정보를 불러와야 한다.</li>
</ul>
<p>트래픽이 증가하면 다음 2가지 이유로 자원 효율이 떨어진다.</p>
<ul>
<li>IO대기와 컨텍스트 스위칭에 따른 CPU 낭비</li>
<li>요청마다 스레드를 할당함으로써 메모리 사용량이 높음</li>
</ul>
<p>→ 톰캣과 같은 요청마다 스레드를 할당하는 서버를 사용하면 CPU나 메모리 낭비가 많을 수 있음.</p>
<p>하지만 이정도의 트래픽을 일으키는 서버는 많지 않이게 현재에도 많이 쓰인다.</p>
<p>서버 성능을 높이는 다른 방법은 자원 효율을 높이는 것이다. IO 대기로 인한 CPU의 자원 낭비를 줄이고 요청을 처리하는데 드는 메모리를 줄이는 것이다.</p>
<ul>
<li>가상 스레드나 고루틴같은 경량 스레드 사용</li>
<li>논블로킹 또는 비동기 IO 사용</li>
</ul>
<h2 id="가상스레드-사용하기">가상스레드 사용하기</h2>
<ol>
<li>자바의 가상 스레드나 GO 언어의 고루틴을 사용하면 입출력동안 스레드가 대기하지 않고 다른 일ㅇ르 할 수 있다.</li>
<li>경량 스레드를 사용하면 OS가 사용하는 스레드가 아니라 런타임이 관리하는 스레드를 사용하기 때문에 OS가 관리하는 스레드보다 더 적은 자원을 사용한다.</li>
<li>JVM은 기본적으로 풀에 CPU 코어 개수만큼 플랫폼 스레드를 생성한다. </li>
<li>가상 스레드는 수백 바이트에서 수 KB, 수십 KB 까지 힙 메모리를 사용한다. 호출 스택의 깊이에 따라 사용하는데 메모리를 동적으로 늘렸다가 줄인다.</li>
<li>스레드를 생성하는 시간도 차이가 많이난다.</li>
<li>그렇기에 톰캣처럼 요청별 스레드를 생성하는 서버에서 가상 스레드를 사용하면 획기적으로 많은 메모리를 아낄수 있다.<ul>
<li>한개의 캐리어 스레드가 여러 가상 스레드를 실행하게 된다. 특정 가상 스레드가 캐리어 연결되는 것을 마운트, 연결이 해제되는 것을 언마운트 라고 한다.</li>
</ul>
</li>
</ol>
<h3 id="네트워크-io-와-가상-스레드">네트워크 IO 와 가상 스레드</h3>
<ol>
<li>가상 스레드는 실행하는 과정에서 블로킹되면 플랫폼 스레드와 언마운트되고 다른 실행 대기중인 플랫폼 스레드와 연결된 뒤 실행을 재개한다.</li>
<li>블로킹 연산에는 IO 기능, ReentrantLock, Thread.sleep() 등이 포함된다. 이들 연산을 사용해서 가상 스레드가 블로킹되면 플랫폼 스레드는 대기 중인 다른 가상 스레드를 실행한다.</li>
<li>반면에 자바 23 또는 이전 버전에서 synchronized 인해 블로킹되면 가상 스레드는 플랫폼 스레드로부터 언마운트 되지 않는다.</li>
<li>이렇게 가상 스레드가 플랫폼 스레드까지 블로킹할 때 이를 가상 스레드가 플랫폼 스레드에 고정됐다 라고 한다.</li>
</ol>
<h3 id="가상-스레드와-성능">가상 스레드와 성능</h3>
<ol>
<li>IO 중심 작업과 CPU 중심 작업이 있을 수 있으나 가상 스레드가 효과를 볼 수 있는 작업은 IO 중심 작업임.</li>
<li>IO 중심 작업일 때 플랫폼 스레드가 CPU 낭비 없이 효율적으로 가상 스레드를 실행할 수 있음</li>
<li>반면에 CPU 중심 작업에 가상 스레드가 쓰이면 성능 개선 효과를 얻을 수 없음 오히려 나빠짐</li>
<li>또한 IO 중심 작업이라고 해서 무조건 성능에 이점을 가지는 것은 아님.</li>
<li>스케줄링에 사용되는 플랫폼 스레드 개수보다 가상 스레드의 개수가 많아야 효과를 기대할 수 있음.</li>
<li>가상 스레드가 이점을 얻을 수 있는 부분은 처리량임.</li>
</ol>
<h3 id="가상-스레드의-장점">가상 스레드의 장점</h3>
<ul>
<li>가상스레드의 장점은 기존 코드를 크게 수정할 필요가 없다는 것임.</li>
</ul>
<h2 id="논블로킹-io-로-성능-높이기">논블로킹 IO 로 성능 높이기</h2>
<ul>
<li>사용자가 폭발적으로 증가하면 어느 순간 경량 스레드로는 한계가 온다. 이때에는 서버의 IO 구현 방식을 논블로킹 IO 로 변경해야한다.</li>
</ul>
<h3 id="논블로킹-io-의-동작과정">논블로킹 IO 의 동작과정</h3>
<ol>
<li>논블로킹 IO는 입출력이 끝날때까지 스레드가 대기하지 않는다.</li>
<li>논블로킹 IO 를 사용하는 코드는 대기하지 않고 다음 줄의 코드가 바로 실행된다.</li>
<li>그렇기에 논블로킹 IO 를 사용할 때에는 데이터 읽기를 바로 시도하기 보다 어떤 연산을 수행할 수 있는지 확인 후 해당 연산을 실행하는 방식을 사용한다.<ol>
<li>실행 가능한 IO 연산 목록을 구한다.</li>
<li>a에서 구한 IO 연산 목록을 차례로 순회한다.<ol>
<li>각 IO 연산을 처리한다.</li>
</ol>
</li>
<li>이 과정을 반복한다.</li>
</ol>
</li>
<li>논블로킹 IO 를 스레드 1개로 구현하면 동시성이 떨어진다.</li>
<li>보통 1개 채널에 대한 읽기 처리가 끝나야 다음 채널에 대한 읽기 처리를 진행하기 ㄸ매ㅜㄴ이다.</li>
</ol>
<h3 id="리액터-패턴">리액터 패턴</h3>
<p>리액터 패턴은 논블로킹 IO를 이용해서 구현할 때 사용하는 패턴 중 하나이다.</p>
<p>논블로킹 IO 로 구현된 네트워크 프레임워크의 문서를 읽다보면 보이는 리액터라는 단어가 이 패턴에서 말하는 리액터에 해당한다.</p>
<p>리액터는 이벤트를 대기하고 핸들러에 전달하는 과정을 반복하는데 이를 이벤트루프라고 한다.</p>
<p>논블로킹에 기반한 Nettty, Nginx, Node.js 등의 프레임워크나 서버는 이러한 리액터 패턴을 사용하고 있다.</p>
<p>리액터 패턴에서 이벤트 루프는 단일 스레드로 실행한다.</p>
<h3 id="언제-어떤-방법을-사용할까">언제 어떤 방법을 사용할까?</h3>
<p>논블로킹 IO 나 가상 스레드를 적용할 때는 먼저 다음을 검토해야한다.</p>
<ul>
<li>문제가 있는가?<ul>
<li>문제가 없는데 구현을 변경하는 것은 시간을 낭비하는 것이다.</li>
<li>게다가 논블로킹 IO/비동기IO 방식으로 구현하면 코드가 복잡해지고 유지보수난이도도 올라간다.</li>
</ul>
</li>
<li>문제가 있다면 네트워크 IO 관련 성능 문제인가?</li>
<li>구현 변경이 가능한가?</li>
</ul>
<p>우선 순위에 밀려 구현 변경이 불가능한 상황에는 구현 방식을 바꾸는 대신 서버 확장으로 문제를 해결해야한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[6장 동시성, 데이터가 꼬이기 전에 잡아야 한다.]]></title>
            <link>https://velog.io/@junr_65535/6%EC%9E%A5-%EB%8F%99%EC%8B%9C%EC%84%B1-%EB%8D%B0%EC%9D%B4%ED%84%B0%EA%B0%80-%EA%BC%AC%EC%9D%B4%EA%B8%B0-%EC%A0%84%EC%97%90-%EC%9E%A1%EC%95%84%EC%95%BC-%ED%95%9C%EB%8B%A4</link>
            <guid>https://velog.io/@junr_65535/6%EC%9E%A5-%EB%8F%99%EC%8B%9C%EC%84%B1-%EB%8D%B0%EC%9D%B4%ED%84%B0%EA%B0%80-%EA%BC%AC%EC%9D%B4%EA%B8%B0-%EC%A0%84%EC%97%90-%EC%9E%A1%EC%95%84%EC%95%BC-%ED%95%9C%EB%8B%A4</guid>
            <pubDate>Mon, 01 Dec 2025 01:41:56 GMT</pubDate>
            <description><![CDATA[<h1 id="6장-동시성-데이터가-꼬이기-전에-잡아야-한다">6장 동시성, 데이터가 꼬이기 전에 잡아야 한다.</h1>
<h3 id="서버와-동시실행">서버와 동시실행</h3>
<p>서버가 동시에 여러 클라이언트의 요청을 처리하는 방식에는 크게 두가지가 있다.</p>
<ol>
<li>클라이언트 요청마다 스레드를 할당해서 처리<ul>
<li>이 경우에는 경쟁 상태 등 여러 동시성 문제가 발생 할 수 있다.</li>
</ul>
</li>
<li>비동기 IO ( 또는 논블로킹 IO) 를 사용해서 처리<ul>
<li>해당 방법을 사용하더라도 단일 스레드 하나만을 사용하는 경우는 드물다.</li>
</ul>
</li>
</ol>
<p>→ 어떤 방식이던 서버는 동시 실행이 기본이므로 동시성 문제를 염두해두고 설계하자</p>
<blockquote>
<p>경쟁상태</p>
<p>경쟁 상태는 여러 스레드가 동시에 공유 자원에 접근 할 때 접근 순서에 따라 결과가 달라지는 상황을 말한다.</p>
</blockquote>
<p>→ 동시성 문제는 프로세스 수준, DB 수준에서 동시에 발생한다. 두 가지의 경우 어떻게 접근해야하는지 알아보자.</p>
<h2 id="프로세스-수준에서의-동시-접근-제어">프로세스 수준에서의 동시 접근 제어</h2>
<h3 id="lock-을-이용한-접근-제어">Lock 을 이용한 접근 제어</h3>
<p>Lock 을 사용하면 공유 자원에 접근하는 스레드를 하나로 제한할 수 있다.</p>
<p>로직은 다음과 같다.</p>
<ol>
<li><p>잠금을 획득</p>
</li>
<li><p>공유 자원에 접근 ( 임계 영역 )</p>
<blockquote>
<p><strong>임계영역이란?</strong>
 임계영역은 동시에 둘 이상의 프로세스나 스레드가 접근하면 안되는 공유자원에 접근하는 코드 영역</p>
</blockquote>
</li>
<li><p>잠금을 해제함.</p>
</li>
<li><p>JAVA 의 HashMap 은 다중 스레드 환경에서 안전하지 않다. ( 동시에 여러 스레드가 put() 과 같은 메서드를 호출하면 데이터의 유실이 일어날 수 있으므로)</p>
</li>
<li><p>이런 문제를 방지하려면 <code>ReentrantLock</code> 을 사용해서 임계 영역에 한번에 한 스레드만 접근할 수 있도록 제한하여야 한다.</p>
</li>
<li><p><code>synchronized</code> 또한 고려할 수 있다. <code>@synchronized</code> 하면 해당 어노테이션이 있는 코드 블록을 제어하고 코드 블록이 끝나면 자동으로 잠금을 풀어주기 때문이다.</p>
</li>
<li><p>하지만 ReentrantLock 은 잠금 획득 대기 시간을 지정하는 기능 등의 기능이 있따.</p>
</li>
<li><p>또한 JAVA 21 버전에 가상 스레드가 ReentrantLock 만 지원하고 synchronized는 자바 24 버전부터 지원한다.</p>
</li>
<li><p>다만 두 방식을 섞어 쓰지는 말자. 가능하면 하나만 사용하자</p>
</li>
</ol>
<h3 id="동시-접근-제어를-위한-구성-요소">동시 접근 제어를 위한 구성 요소</h3>
<ol>
<li>ReentrantLock 은 한 번에 하나의 스레드만 잠금을 할 수 있다.</li>
<li>잠금 외에도 동시 접근을 제어하는 수다능로 세마포어와 읽기 쓰기 잠금이 있다.</li>
</ol>
<h3 id="세마포어">세마포어</h3>
<ol>
<li>세마포어는 동시에 실행할 수 있는 스레드 수를 잠금한다.<ol>
<li>이진 세마포어와 counting 세마포어가 있는데 이진 세마포어는 동시 접근할 수 있는 스레드가 1개, 계수 세마포어는 지정한 수만큼 동시에 접근 가능</li>
</ol>
</li>
<li>자바 세마포어 구현체는 퍼밋이라고 표현한다.</li>
<li>로직은 다음과 같다<ol>
<li>세마포어에서 퍼밋 획득 ( 허용 가능 숫자 1 감소 )</li>
<li>코드 실행</li>
<li>세마포어에 퍼밋 반환 ( 허용 가능 숫자 1 증가 ) </li>
</ol>
</li>
<li>세마포어의 남아있는 퍼밋 개수가 0인 상태에서 퍼밋을 획득하려는 스레드는 다른 스레드가 퍼밋을 반환할 때 까지 대기한다.</li>
</ol>
<h3 id="읽기-쓰기-잠금">읽기 쓰기 잠금</h3>
<p><strong>읽기 쓰기 잠금 (Read-Write Lock)이란?</strong></p>
<p>일반적인 잠금(<code>Lock</code>이나 <code>synchronized</code>)은 공유 자원에 접근하는 스레드를 <strong>한 번에 단 하나</strong>로 제한합니다. 이 방식은 데이터의 무결성(integrity)을 보장하지만, 데이터를 변경하지 않는 단순한 <strong>읽기</strong> 작업마저도 동시에 수행될 수 없게 합니다.</p>
<p><strong>문제점:</strong> 만약 데이터에 대한 <strong>쓰기(변경)</strong> 빈도보다 <strong>읽기(조회)</strong> 빈도가 훨씬 높다면, 한 번에 하나의 스레드만 읽기를 수행할 수 있으므로 전체적인 <strong>읽기 성능이 떨어지는 문제</strong>가 발생합니다.</p>
<p><strong>해결책:</strong> <strong>읽기 쓰기 잠금</strong>은 이러한 성능상의 단점을 없애기 위해 고안된 동시 접근 제어 방식입니다. 이 잠금은 <strong>쓰기(데이터 변경)</strong>와 <strong>읽기(데이터 조회)</strong> 작업을 분리하여 동시성을 관리합니다.</p>
<p>읽기 쓰기 잠금의 핵심 특징 (성능 개선 원리)</p>
<p>읽기 쓰기 잠금은 다음과 같은 규칙을 가집니다:</p>
<ol>
<li><strong>쓰기 잠금 (Write Lock):</strong></li>
</ol>
<p>◦ <strong>배타적 접근:</strong> 한 번에 <strong>하나의 스레드</strong>만 쓰기 잠금을 획득할 수 있습니다. (데이터를 변경할 때는 오직 혼자서만 작업해야 데이터가 꼬이지 않기 때문입니다.)</p>
<ol start="2">
<li><strong>읽기 잠금 (Read Lock):</strong></li>
</ol>
<p>◦ <strong>공유 접근:</strong> 한 번에 <strong>여러 스레드</strong>가 읽기 잠금을 획득할 수 있습니다. (데이터를 조회만 하는 경우 여러 명이 동시에 봐도 문제가 되지 않습니다.)</p>
<ol start="3">
<li><strong>상호 배제 (쓰기/읽기 충돌 규칙):</strong></li>
</ol>
<p>◦ 어떤 스레드가 <strong>쓰기 잠금을 획득</strong>한 상태라면, 이 잠금이 해제될 때까지 다른 스레드는 <strong>읽기 잠금</strong>을 구할 수 없습니다. (쓰는 동안에는 읽을 수 없음).</p>
<p>◦ 어떤 스레드라도 <strong>읽기 잠금을 획득</strong>한 상태라면, 모든 읽기 잠금이 해제될 때까지 <strong>쓰기 잠금</strong>을 구할 수 없습니다. (읽는 동안에는 쓸 수 없음).</p>
<p>이러한 특징 덕분에 읽기 쓰기 잠금을 사용하면 <strong>동시에 여러 스레드가 데이터를 읽을 수 있게 되어</strong>, 일반 잠금을 사용했을 때 발생하는 읽기 성능 저하 문제를 완화할 수 있습니다</p>
<h3 id="원자적-타입">원자적 타입</h3>
<ol>
<li>Lock 을 사용하면 카운팅 변수 와 같은 공유 자원에 동시성 문제를 방지시킬 수 있다.</li>
<li>하지만 이를 사용하면 단일 스레드가 해당 자원을 점유하는 동안 다른 스레드가 대기하기에 CPU 효율이 낮아진다.</li>
<li>이를 해결하기 위한 방법으로 AtomicInteger, AtomicLong, AtomicBoolean 과 같은 원자적 타입을 사용하면 동시성 문제 없이 여러 스레드가 공유하는 데이터를 변경할 수 있다.</li>
<li>ㅇ이러한 타입은 내부적으로 CAS( 비교 후 교체 연산) 을 사용한다.</li>
<li>이를 통해 스레드를 멈추지 않고도 다중 스레드에서 동시성 문제를 해결할 수 있다.</li>
</ol>
<h3 id="동시성-지원-컬렉션">동시성 지원 컬렉션</h3>
<p>스레드에 안전한지 않은 컬렉션을 여러 스레드에 공유하면 동시성 문제가 발생한다.</p>
<p>자바에서는 Collections.synchronized 와 같이 동기화된 컬렉션을 생성하는 메서드를 제공한다.</p>
<p> 단, 자바 23 또는 이전 버전 기준으로 가상 스레드를 사용한다면 해당 동기화 컬렉션 변환 메서드를 사용하면 안된다. 내부적으로 synchronized 를 사용해서 동시 접근을 동기화 하기 때문이다. → 성능에 문제가 생길 수 있다. </p>
<p>다른 방법으로는 동시성 자체를 지원하는 컬렉션 타입을 사용하는 것이다.</p>
<p>ConcurrentHashMap 과 같은 타입은 잠금 범위를 최소화하므로 키의 해시 분포가 다르고 동시 수정이 많으면 더 나은 성느을 제공한다.</p>
<h2 id="db-와-동시성">DB 와 동시성</h2>
<h3 id="선점-잠금">선점 잠금</h3>
<ul>
<li><p>데이터에 먼저 접근한 트랜잭션이 잠금을 획득하는 방식</p>
<ul>
<li><p>분산 잠금?</p>
<p>  분산 잠금은 여러 프로세스가 도시에 동일한 자원에 접근하지 못하도록 막는 방법</p>
<p>  보통 레디스를 활용해서 구혀낳ㄴ다.</p>
</li>
</ul>
</li>
</ul>
<h3 id="비선점-잠금">비선점 잠금</h3>
<ul>
<li>명시적으로 잠금을 사용하지 않고 데이터를 조회하려는 시점에서의 값과 수정하려는 시점에서의 기준값(버전 컬럼)이 비교하는 방식으로 동시성 문제를 처리하는 방식<ul>
<li>잠금을 획득하기 위한 대기 시간이 없기 때문에 실패할 경우 사용자에게 더 빠르게 결과를 응답할 수 있음</li>
</ul>
</li>
</ul>
<h3 id="외부-시스템과-연동해야한다면">외부 시스템과 연동해야한다면?</h3>
<ul>
<li>선점 잠금을 고려하는게 좋음</li>
<li>만약 비선점 잠금을 사용해야한다면</li>
</ul>
<h3 id="증분-쿼리">증분 쿼리</h3>
<h2 id="잠금-사용-시-주의사항">잠금 사용 시 주의사항</h2>
<h3 id="1-잠금-해제하기">1. 잠금 해제하기</h3>
<p>잠금을 해제 하지 않으면 잠금을 시도하는 스레드가 무한정 대기하게 되므로 try-finally 형태의 코드를 사용하여 작므을 해제하는 코드를 넣는 습관을 들이자</p>
<h3 id="2-대기-시간-정하기">2. 대기 시간 정하기</h3>
<p>잠금 해제 시간을 정하면 사용자에게 잠금 획득 실패 에러를 빠르게 전달할 수 있다.</p>
<h3 id="3-교착-상태-피하기">3. 교착 상태 피하기</h3>
<p>대기 시간은 교착 상태를 피하는데도 효과를 볼 수 있다.</p>
<p>또한 지정한 순서대로 잠금을 획득하게 할 수 도 있다. 모든 스레드가 트랜잭션에서 같은 순서로 잠금을 획득한다는 규칙을 만들면 교착상태를 피하는데 도움을 준다.</p>
<ul>
<li>라이브락 : 아무것도 하지 않고 대기하는 교착 상태와는 다르게 지속적으로 대기상태를 빠져나가기 위해 진행되나 그것이 교착을 만들어내는 것 → 중재자 우선순위를 둬서 해결 가능</li>
<li>기아 상태<ul>
<li>우선순위를 두면 우선순위가 낮은 작업이 지속적으로 순위가 밀리므로 기아상태에 빠짐 → 작업의 우선순위를 높이거나 여러 프로세스나 스레드가 공유하는 자원을 독점하는 시간에 제한을 두어 가능한 작업을 실행할 수 있도록 해야함</li>
</ul>
</li>
</ul>
<h2 id="단일-스레드로-처리하기">단일 스레드로 처리하기</h2>
<p>동시성이 발생하는 이유는 여러 스레드가 동시에 동일 자원에 접근하기 때문이므로 차라리 한 스레드만 상태를 관리하게 하고 그 스레드를 사용할 수 있는 큐를 만드는 것이 더 나을 수 있음</p>
<p>두 스레드의 데이터 공유가 필요하면 콜백이나 큐와 같은 수단을 이용해서 데이터 복제본을 공유하게 할 수 있음</p>
<p>→ 성능에 문제가 될 수 있으나 임계 영역의 실행 시간이 짧고 동시 접근 스레드 수가 적을수록 잠ㄱ믕르 사용하는 구현 성능이 좋을 가능성이 높음</p>
<p>왜냐하면 큐나 채널을 사용하는데 드는 시간보다 잠금 획득과 해제에 드는 시간이 더 짧기 때문에</p>
<p>하지만 동시에 실행되는 작업이 많고 임계 영역의 실행 시간이 길어지면 큐나 채널을 이용한 방식이 더 비슷하거나 나은 성능을 낼 가능성이 있음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[4장 외부 연동이 문제일 때에 살펴봐야 할 것들]]></title>
            <link>https://velog.io/@junr_65535/4%EC%9E%A5-%EC%99%B8%EB%B6%80-%EC%97%B0%EB%8F%99%EC%9D%B4-%EB%AC%B8%EC%A0%9C%EC%9D%BC-%EB%95%8C%EC%97%90-%EC%82%B4%ED%8E%B4%EB%B4%90%EC%95%BC-%ED%95%A0-%EA%B2%83%EB%93%A4</link>
            <guid>https://velog.io/@junr_65535/4%EC%9E%A5-%EC%99%B8%EB%B6%80-%EC%97%B0%EB%8F%99%EC%9D%B4-%EB%AC%B8%EC%A0%9C%EC%9D%BC-%EB%95%8C%EC%97%90-%EC%82%B4%ED%8E%B4%EB%B4%90%EC%95%BC-%ED%95%A0-%EA%B2%83%EB%93%A4</guid>
            <pubDate>Mon, 01 Dec 2025 01:41:12 GMT</pubDate>
            <description><![CDATA[<h1 id="외부-연동-문제-설정법">외부 연동 문제 설정법</h1>
<h2 id="1-타임아웃-설정을-하자">1. 타임아웃 설정을 하자</h2>
<p>다음과 같은 상황이 있다고 생각해보자.</p>
<ul>
<li>A 서비스가 있고 이 서비스가 호출하는 B 서비스가 있다.</li>
<li>A 서비스의 스레드 풀은 100개이다.</li>
<li>B 서비스에 성능 문제가 생겨 응답 대기 시간이 60초를 넘기기 시작한다.</li>
</ul>
<p>A 서비스에 100명의 사용자가 요청을 보내면 톰캣은 A 서비스의 요청을 처리하기 위해 B서비스를 호출한다.</p>
<p>10초 후 새로운 100명의 사용자가 요청을 보낸다. 하지만 B 서비스의 요청에 응답이 오지 않았기 때문에 대기는 끝나지 않는다. 이렇게 서비스가 마비된다.</p>
<p>→ 연동 서비스에 대한 타임아웃을 설정하지  않으면 연동 서비스의 응답이 느려질 때에 처리량이 급격히 떨어진다.</p>
<p>타임아웃을 설정하여 사용자에게 지정 시간 이후 에러 화면을 보게 하자. 반응 없는 무한 대기 화면보다는 에러 화면이라돋 보는 게 낫다. 또한 서비스의 자원이 포화되기 전에 응답하게 되므로 연동 서비스 문제가 다른 기능에 주는 영향도 줄어든다.</p>
<h3 id="타임아웃의-종류">타임아웃의 종류</h3>
<p><img src="attachment:162b382d-90bc-4181-9643-536980b368f3:image.png" alt="image.png"></p>
<ol>
<li><p>연결 타임아웃(Connection Timeout)</p>
<p> 커넥션을 요청할때에 커넥션 수락을 얼마나 기다릴지 기다리는 시간</p>
</li>
<li><p>읽기 타임아웃(Read Timeout)</p>
<p> 커넥션이 되고 응답을 받기까지의 시간</p>
</li>
</ol>
<p>처음 연동하는 서비스라면 보통 이렇게 타임아웃을 설정해둔다.</p>
<ul>
<li>연결 타임아웃 : 3초 ~ 5초</li>
<li>읽기 타임아웃 : 5초 ~ 30초</li>
</ul>
<blockquote>
<p>소켓 타임아웃과 읽기 타임아웃</p>
<ul>
<li>읽기 타임아웃을 지정할 때에는 실제로 설정되는 값이 무엇인지 보고 결정해야함.</li>
</ul>
<ol>
<li>아파치의 HTTPClient 의 경우 소켓 타임아웃을 설정함<ol>
<li>소켓 타임아웃은 네트워크 패킷 단위를 기준으로 하므로 전체 응답 시간에 대한 타임아웃을 의미지는 않는다.</li>
</ol>
</li>
<li>OkHttp 는 읽기 타임아웃과는 별개로 호출 타임아웃을 설정 가능<ol>
<li>요청 시작부터 응답까지의 전체 시간 기준으로 설정</li>
</ol>
</li>
<li>소켓 타임아웃을 설정하면 패킷은 계속 수신되지만 전체 처리 시간이 오래 걸리는 경우 타임아웃 발생시킬 수 잇음.</li>
</ol>
<p><strong>OkHttp란?</strong></p>
<p>OkHttp는 HTTP 및 HTTP/2 클라이언트 라이브러리로, 안드로이드 및 자바 애플리케이션에서 네트워크 요청을 처리하는 데 널리 사용되는 오픈소스 라이브러리입니다. Square사에서 개발했으며, 효율적인 연결 관리와 다양한 타임아웃 설정 옵션을 제공합니다.</p>
<p><strong>&quot;소켓 타임아웃은 네트워크 패킷 단위를 기준으로 하므로 전체 응답 시간에 대한 타임아웃을 의미하지는 않는다&quot;의 의미</strong></p>
<p>이 문장은 소켓 타임아웃의 작동 방식을 설명하는 중요한 개념입니다:</p>
<ul>
<li><strong>소켓 타임아웃의 특성</strong>: 소켓 타임아웃은 각 네트워크 패킷이 도착하는 간격을 측정합니다. 즉, 패킷과 패킷 사이의 대기 시간을 기준으로 합니다.</li>
<li><strong>전체 응답 시간과의 차이</strong>: 만약 서버가 데이터를 천천히 보내더라도, 패킷이 계속해서 도착하기만 한다면 타임아웃이 발생하지 않습니다. 예를 들어, 소켓 타임아웃을 10초로 설정했다고 가정해봅시다. 서버가 총 5분 동안 응답을 보내는데, 매 5초마다 패킷을 보낸다면 타임아웃이 발생하지 않습니다. 전체 응답 시간은 5분이지만, 패킷 간 간격은 10초 미만이기 때문입니다.</li>
<li><strong>문제점</strong>: 이로 인해 전체 처리 시간이 매우 길어질 수 있습니다. 클라이언트는 서버로부터 느리게 전송되는 데이터를 계속 기다리게 되어, 의도하지 않게 매우 긴 시간 동안 연결이 유지될 수 있습니다.</li>
</ul>
<p><strong>OkHttp의 해결책</strong></p>
<p>OkHttp는 이러한 문제를 해결하기 위해 읽기 타임아웃과는 별개로 &quot;호출 타임아웃(Call Timeout)&quot;을 제공합니다. 호출 타임아웃은 요청 시작부터 응답 완료까지의 전체 시간을 기준으로 작동하므로, 서버가 아무리 패킷을 계속 보내더라도 전체 처리 시간이 설정된 시간을 초과하면 타임아웃이 발생합니다.</p>
</blockquote>
<h2 id="2-외부-설정-실패-시-재시도-설정법">2. 외부 설정 실패 시 재시도 설정법</h2>
<h3 id="재시도-시-문제가-없는-서비스만-재시도를-할-것">재시도 시 문제가 없는 서비스만 재시도를 할 것.</h3>
<ol>
<li>재시도를 할 때에는 재시도 해도 문제 없는 서비스인지 확인해야한다.</li>
<li>예를 들어 API 를 호출해 포인트가 차감되는 서비스의 경우 포인트 차감 처리가 여러번 될 수 있다.</li>
<li>재시도 해도 되는 조건은 다음과 같이 3가지가 있다.<ol>
<li>단순 조회 기능</li>
<li>연결 타임 아웃 (연결 타임 아웃의 경우 아직 연동 서비스에 연결되지 않았다는 의미이므로 재시도 괜찮다. 하지만 읽기 타임아웃의 경우에는 재시도 시 문제가 발생할 수 있다.)</li>
<li>멱등성을 가진 변경 기능 ( 상태 변경되는 API 를 재시도할 때에는 멱등성을 고려해야한다. )</li>
</ol>
</li>
</ol>
<h3 id="재시도-횟수와-간격을-생각할-것">재시도 횟수와 간격을 생각할 것.</h3>
<ol>
<li>재시도 할 때에는 재시도 횟수와 간격을 생각해야한다.</li>
<li>(대부분의 경우) 재시도의 횟수는 1~2회가 이상적이다.</li>
<li>재시도 간격의 경우에는 여러 차례 재시도 할 경우 간격을 늘리면서 한다.</li>
</ol>
<h3 id="재시도-폭풍retry-storm-의-안티패턴">재시도 폭풍(Retry Storm) 의 안티패턴</h3>
<ol>
<li>재시도는 성공 가능성을 높일 수 있지만 반대로 연동 서비스에는 더 큰 부하를 줄 수 있다.</li>
</ol>
<h2 id="3-동시-요청-제한">3. 동시 요청 제한</h2>
<ol>
<li>연동 서비스의 최대 처리량을 초과하는 트래픽을 감당하면 기하급수적으로 응답시간이 늘어나기 시작한다.</li>
<li>이러한 최대 처리량을 늘릴 수 없을 때에 연동 서비스에 임계치 이상의 요청을 보내는 방법은 최대 처리량을 초과하는 요청을 보내지 않는 것이다.</li>
<li>연동 서비스의 트래픽을 초과하는 요청은 503 상태 코드를 사용하여 과부하 상황임을 클라이언트에 알려 오류 메시지를 출력할 수 있다.</li>
</ol>
<blockquote>
<p>벌크 헤드 패턴</p>
<p>동시요청을 제한하는 방식은 벌크페드 패턴이라고 한다. 벌크헤드 패턴은 각 구성요소를 격리하여 한 구성요소의 장애가 다른 구성요소에 영향을 주지 않도록 ㅅ하는 설계 패턴이다.</p>
</blockquote>
<h2 id="4-서킷-브레이커">4. 서킷 브레이커</h2>
<ol>
<li><p>앞서 연동 서비스에 시간이 걸릴 경우에는 타임아웃을 설정하여 지정 시간 이상 걸릴 경우에는 에러 화면이 보여지는 방법을 설명했다.</p>
</li>
<li><p>하지만 연동 서비스가 정상 상태가 아님을 알 수 있다면 연동 서비스에 요청을 보내지 않고 바로 에러를 응답하는 것이 낫다.</p>
</li>
<li><p>(왜냐하면) 서버 입장에서는 읽기 타임아웃 시간동안 발생하는 응답시간을 줄일 수 있고 처리량도 감소시킬 수 있고 사용자 입장에서는 몇초 기다리는 것 보다 바로 에러 화면을 보는 것 보다 바로 에러화면을 보는 것이 낫기 때문이다.</p>
</li>
<li><p>서킷 브레이커가 작동하는 방식이 이와 같다. </p>
</li>
<li><p>서킷 브레이커는 누전 차단기처럼 과도한 트래픽이 발생하면 연동을 중지시키고 바로 에러를 응답한다.</p>
</li>
<li><p>서킷 브레이커는 닫힘, 열림, 반 열림 의 3가지 상태를 갖는다.</p>
<p> <img src="attachment:8c3a13e7-e664-4949-be8a-db0b1620f9d0:image.png" alt="image.png"></p>
<ol>
<li>서킷 브레이커는 닫힘 상태로 시작한다. 닫힘 상태일 때에는 모든 요청을 연동 서비스에 전달한다.</li>
<li>외부 연동 과정에서 오류가 발생하기 시작하면 지정한 임계치를 초과했는지 확인하고 초과했다면 열림 상태가 된다.<ol>
<li>임계치 기준<ul>
<li>시간 기준 오류 발생 비율 : 10초동안 오류 비율이 50% 초과</li>
<li>개수 기준 오류 발생 비율 : 100개 요청 중 오류 비율이 50% 초과</li>
</ul>
</li>
</ol>
</li>
<li>열림 상태가 되면 연동 요청은 수행하지 않고 바로 에러 응답을 리턴한다. 이는 지정된 시간동안 진행된다.</li>
<li>이 시간이 지나면 일정 개수, 일정 기간 동안 반 열림 상태로 전환된다. 일부 요청에 관해 연동을 시도한다.</li>
<li>이 기간 연동에 성공하면 다시 열림 상태로 전환되어 연동을 차단한다.</li>
</ol>
</li>
</ol>
<h2 id="5-외부-연동과-db-연동">5. 외부 연동과 DB 연동</h2>
<h3 id="외부-연동과-트랜잭션-처리">외부 연동과 트랜잭션 처리</h3>
<ol>
<li><p>DB 연동과 외부 연동을 함께 처리할 때에는 오류 발생 시 DB 트랜잭션을 어떻게 처리할지 알맞게 판단해야한다.</p>
<ol>
<li><p>예시 1. 외부 연동에 실패했을 때 트랜잭션을 롤백</p>
<ol>
<li>이 상황에서는 외부 연동에 실패했을 때 트랜잭션이 롤백되면 변경 데이터가 DB 에 저장되지 않는다.</li>
<li>하지만 읽기 타임아웃이 발생했을 경우에는 외부 서비스가 실제적으로 성공적으로 처리되었을 가능성을 염두해 둬야 한다.</li>
<li>이 경우 2가지 방법 중 하나를 검토해야한다.</li>
<li>첫 번째로 일정 주기로 두 시스템의 데이터가 일치하는 지 확인하고 보정하는 방법</li>
<li>성공 확인 API 를 호출하는 방법 ( 이 방식은 외부 서비스가 성공 여부를 알려주는 API 를 제공할 때만 사용할 수 있다.</li>
<li>이 방식의 형으로 취소 API 를 호출하는 방식도 있다. 읽기 타임아웃이 발생한 뒤 일정 시간 후에 취소 API 를 호출하는 식이다.</li>
<li>하지만 이것은 연동 서비스가 지원해야 하고 후속 API 호출에도 읽기 타임아웃이 발생할 수 있으므로 두 시스템간 일관성이 중요하다면 정기적으로 데이터 일치를 확인하는 프로세스를 갖추는 것이 바람직하다. </li>
</ol>
</li>
<li><p>예시 2. 외부 연동은 성공했지만 DB 연동에 실패해 트랜잭션을 롤백 </p>
<p> 외부 연동은 성공했지만 DB 연동에 실패된 경우에는 취소 API를 호출해서 외부 연동을 이전 상태로 되돌리는 것이 필요하다.</p>
</li>
</ol>
</li>
</ol>
<h3 id="외부-연동이-느려질-때-db-커넥션-풀-문제">외부 연동이 느려질 때 DB 커넥션 풀 문제</h3>
<ol>
<li>DB 트랜잭션 범위 안에서 외부 연동을 수행할 때에 트랜잭션 처리 외에도 주의해야할 점이 하나 더 있다.</li>
<li>바로 커넥션 풀 부족 현상이다.</li>
<li>DB 연동과 무관하게 외부 연동을 실행할 수 있다면 DB 커넥션을 사용하기 전에나 후에 외부 연동을 시도하는 방안을 고려해보면 좋다.</li>
<li>단 이 방식은 외부 연동이 트랜잭션 범위 밖에서 실행되기 때문에 롤백이 불가능ㅎ아다. 실패한 외부 연동에 대한 후처리가 이루어져야함을 주의하자</li>
</ol>
<h2 id="6-http-커넥션-풀">6. HTTP 커넥션 풀</h2>
<ol>
<li><p>전체 처리 시간에서 연결 시간이 차지하는 비중이 크다.</p>
</li>
<li><p>DB 도 DB 커넥션 풀을 통해 dB 연결 시간을 줄이는 것 처럼 HTTP 연결도 커넥션 풀을 사용하면 연결 시간을 줄일 수 있어 응답속도 향상에 도움이 된다.</p>
</li>
<li><p>HTTP 커넥션 풀을 사용할 때는 다음 3가지 를 고려하자</p>
<ol>
<li><p>HTTP 커넥션 풀 크기</p>
<p> 풀의 크기는 연동할 서비스의 성능에 따라 결정한다. 커넥션풀 크기를 무작정 늘리면 연동 서비스의 성능 저하가 우리 서비스 전체의 응답시간에 영향을 끼친다. 그렇기에 반드시 연동 서비스의 처리 능력을 고려하자</p>
</li>
<li><p>풀에서 HTTP 커넥션을 가져올 때까지 대기하는 시간</p>
<p> 커넥션 풀의 크기가 10개라면 11개의 외부 연동요청이 들어온 경우 10개는 커넥션을 확보해 실행되고 나머지 1개는 대기하게 된다. 대기 시간은 수 초 이내의 짧은 시간으로 설정하는 것이 좋다.</p>
</li>
<li><p>HTTP 커넥션 유지 시간</p>
<p> 끊어진 커넥션을 사용하면 에러가 발생하ㅡ로 연동 서비스에 맞춰 유지 시간을 적절히 설정하자.</p>
</li>
</ol>
</li>
</ol>
<h2 id="7-연동-서비스-이중화">7. 연동 서비스 이중화</h2>
<ol>
<li>서비스가 대량 트래픽을 처리할 만큼 성장했담녀 연동 서비스의 이중화를 고려하자.</li>
<li>연동 서비스를 이중화해두면 장애 시 대응할 수 잇다.</li>
<li>하지만 비용이 크기에 결정시 다음 2가지를 따져봐야한다.<ol>
<li>해당 기능이 서비스의 핵심 기능인지</li>
<li>이중화 비용이 감당 가능한 수준인지</li>
</ol>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[3장 성능을 좌우하는 DB 설계와 쿼리]]></title>
            <link>https://velog.io/@junr_65535/3%EC%9E%A5-%EC%84%B1%EB%8A%A5%EC%9D%84-%EC%A2%8C%EC%9A%B0%ED%95%98%EB%8A%94-DB-%EC%84%A4%EA%B3%84%EC%99%80-%EC%BF%BC%EB%A6%AC</link>
            <guid>https://velog.io/@junr_65535/3%EC%9E%A5-%EC%84%B1%EB%8A%A5%EC%9D%84-%EC%A2%8C%EC%9A%B0%ED%95%98%EB%8A%94-DB-%EC%84%A4%EA%B3%84%EC%99%80-%EC%BF%BC%EB%A6%AC</guid>
            <pubDate>Mon, 01 Dec 2025 01:39:58 GMT</pubDate>
            <description><![CDATA[<h3 id="조회-트래픽을-고려하여-인덱스-설계할-것">조회 트래픽을 고려하여 인덱스 설계할 것</h3>
<ol>
<li>테이블의 모든 데이터를 순차적으로 읽는 것을 풀스캔이라고 한다.</li>
<li>보통 데이터가 많을 때에 풀스캔이 발생하면 조회가 느려지므로 인덱싱을 잘 해둬야한다.</li>
<li>일반적인 시스템의 경우에는 조회 기능의 실행 비율이 높다.</li>
<li>(그러므로) <strong>조회 패턴을 기준으로 인덱스를 설계</strong>하는 것이 중요하다.</li>
<li>(예를 들면) 게시판의 경우 카테고리별로 게시글을 조회하는 패턴이 존재하므로 category 컬럼에 인덱스를 걸면 성능 개선이 일어날 수 있다.</li>
<li>(내가 작성한 글 보기 기능이 있는 경우에도) 풀스캔 방지를 위해 writerId 컬럼에 인덱스가 필요할 것이다.</li>
</ol>
<blockquote>
<p><strong><code>FULLTEXT 인덱스</code></strong></p>
<ul>
<li><p>게시글 제목 검색 등의 기능을 위한 쿼리는 다음과 같다.</p>
<pre><code class="language-sql">  SELECT id, category, writerId, title, content FROM article
  WHERE title like &#39;%검색어%&#39; order by id DESC limit 10</code></pre>
</li>
<li><p>허나 이러한 like 조건은 풀스캔을 유발한다.</p>
</li>
<li><p>(이를 사용하지 않기 위해) 엘라스틱 서치 와 같은 검색엔진을 사용하면 DB 를 사용하지 않고 검색 기능을 구현할 수 있다.</p>
</li>
<li><p>(만약 이와 같은 도구를 사용하는 것이 힘들다면) DB 가 제공하는 FULLTEXT 검색 기능을 활용하자.</p>
</li>
<li><p><strong><code>FULLTEXT 인덱스</code></strong>를 고려하면 풀스캔 없이 문자열 검색 쿼리를 실행할 수 있다</p>
</li>
</ul>
</blockquote>
<h3 id="단일-인덱스와-복합-인덱스-결정은-데이터-기반으로">단일 인덱스와 복합 인덱스 결정은 데이터 기반으로</h3>
<ol>
<li>인덱스를 단일로 할지, 복합으로 할지는 해당 테이블에 얼마나 많은 데이터가 쌓이는지를 기준으로 정해야한다.</li>
<li>하루에 50만건의 데이터가 쌓이는 경우에는 (요즘 기준으로) 많은 데이터는 아니지만 인덱스가 없는 경우 문제가 발생할 수 있는 규모다.</li>
<li>만약 사용자 활동 로그를 저장하는 log 테이블이 있고 userId 와 activitiDate 를 기준으로 조회하는 기능이 있다고 하면 둘다 인덱싱 할지 userId 만 인덱싱 할지 고민될 수 있다.</li>
<li>이럴때에는 <strong>사용자당 가질 수 있는 데이터가 얼마나 될지 가늠해보면 어떤 인덱스를 사용해야 할지 판단하는 데 도움</strong>이 된다.</li>
<li>개별 사용자가 일주일에 하루정도 방문하고 평균 활동 데이터가 5건이면 1년 활동해야 260건이다.</li>
<li>하지만 매일 방문하고 30번 이상의 활동을 하는 회원은 1년이면 1만 건이 넘는 활동 로그 데이터를 생성한다.</li>
<li>이럴때에는 userID 와 activityDate 칼럼을 복합인덱스로 생성해야 조회 성능 문제가 발생하지 않는다.</li>
</ol>
<h3 id="선택도를-고려한-인덱싱을-하라-항상은-아니고">선택도를 고려한 인덱싱을 하라. (항상은 아니고)</h3>
<ol>
<li>선택도란 인덱스에서 특정 칼럼의 고유한 값의 비율을 뜻한다.</li>
<li>고유한 값이 적어지면 그만큼 조회할때에 스캔할 내용이 많아짐으로 인덱스 효율이 낮아진다.</li>
<li>(하지만) 항상 선택도가 높아야하는 것은 아니다.</li>
<li>만약 실제 사용하는 기능의 쿼리가 하나의 컬럼을 항상 조회한다면 선택도에 상관없이 인덱싱하는게 좋다.</li>
</ol>
<blockquote>
<p>커버링 인덱스를 활용해라</p>
<p>커버링 인덱스는 특정 쿼리를 실행하는데 필요한 모든 쿼리를 포함하는 인덱스를 뜻한다.</p>
<p>실제 데이터를 읽어오는 과정이 생략되므로 쿼리 실행 시간이 빨라진다.</p>
<p>과다 사용은 금물 아래글을 보라.</p>
</blockquote>
<h3 id="인덱스는-필요한-만큼만-만들-것">인덱스는 필요한 만큼만 만들 것.</h3>
<ol>
<li>효과가 적은 인덱스를 만들면 오히려 데이터 추가, 변경, 삭제 시 인덱스 관리에 따른 비용이 발생하기에 안좋을 수 있다.</li>
<li>(또한) 인데스가 많아질수록 메모리와 디스크 사용량도 함께 증가한다.</li>
<li>(그러므로) 새로 추가할 쿼리가 기존에 존재하는 인덱스를 사용하지 않을 때에는 요구사항을 일부 변경할 수 있는지 검토해보자</li>
</ol>
<h2 id="성능-개선-방법">성능 개선 방법</h2>
<h3 id="1-미리-집계하기">1. 미리 집계하기</h3>
<ol>
<li>조회수 같은 기능이 필요하다면 sum 이나 count 같은 집계 쿼리를 조회시점에 실행하면 문제가 있을 수 있다.</li>
<li>이러한 집계 데이터는 미리 계산해서 별도 컬럼에 저장하자.</li>
</ol>
<h3 id="2-페이지-기준-목록-조회-대신-id-기준-목록-조회-사용하기">2. 페이지 기준 목록 조회 대신 ID 기준 목록 조회 사용하기</h3>
<ol>
<li><p>만약 id 가 AI라면 offset 기준으로 조회하는 것이 아닌 ID 기준으로 조회하는 것이 좋다.</p>
<pre><code class="language-sql"> select id, subject, writer, regdt
 from article
 order by id desc
 limit 10 offset 99990;</code></pre>
</li>
<li><p>만약 위와 같은 쿼리가 있다면 DB 는 99,991 부터 10개 세면 좋겠지만 어떤 값이 99,990인지 모르므로 데이터를 세는 시간만큼 실행 시간이 증가한다.</p>
</li>
<li><p>그러므로 특정 ID 를 기준으로 조회하면 좋다.</p>
</li>
</ol>
<blockquote>
<p><strong>꿀팁</strong></p>
<p>만약 프론트 개발자가 다음에 읽어올 데이터가 존재하는지 알려주는 속성을 응답 결과에 포함시켜 달라고 요청한다면 1개만 더 읽어 판단하면 된다.</p>
<p>예를 들어 10개를 조회하는 페이징이 있으면 11개를 조회하고 만약 조회한 데이터가 11개이면 다음에 읽을 데이터가 존재하므로 추가 데이터 존재 여부를 true 로 응답하면 된다.</p>
<h3 id="jpa-의-pagenation-도-그렇게-동작하나-no">JPA 의 pagenation 도 그렇게 동작하나? NO</h3>
<h3 id="🧩-책에서-제안한-방식">🧩 책에서 제안한 방식</h3>
<ul>
<li>1페이지에 10개씩 조회</li>
<li>실제 쿼리: <code>limit 11 offset 0</code></li>
<li>결과가 11개면 → 다음 페이지 존재 (<code>hasNext = true</code>)</li>
<li>결과가 10개 미만이면 → 마지막 페이지</li>
</ul>
<p>➡ 장점: <strong>count 쿼리를 안 쳐서 빠름</strong></p>
<p>➡ 단점: <strong>전체 개수를 알 수 없음(total count 불명)</strong></p>
<hr>
<h3 id="🧩-jpa의-기본-방식">🧩 JPA의 기본 방식</h3>
<ul>
<li>1페이지에 10개씩 조회</li>
<li>실제 쿼리: <code>limit 10 offset 0</code></li>
<li>별도로 <code>select count(*)</code> 실행</li>
<li>count 결과와 현재 offset으로 다음 페이지 존재 여부 계산</li>
</ul>
<p>➡ 장점: <strong>전체 페이지 수(total count)</strong> 를 알 수 있음</p>
<p>➡ 단점: <strong>count 쿼리 비용이 큼</strong> (특히 큰 테이블에서)</p>
<hr>
<h3 id="⚙️-3-정리">⚙️ 3. 정리</h3>
<table>
<thead>
<tr>
<th>구분</th>
<th>방식</th>
<th>장점</th>
<th>단점</th>
</tr>
</thead>
<tbody><tr>
<td><strong>JPA 기본 Page</strong></td>
<td>limit + count(*)</td>
<td>전체 개수, 페이지 수 알 수 있음</td>
<td>count 쿼리 부담</td>
</tr>
<tr>
<td><strong>limit+1 방식 (프론트 제안)</strong></td>
<td>limit + 1</td>
<td>빠름, 단순함</td>
<td>전체 개수 불명</td>
</tr>
</tbody></table>
<hr>
<h3 id="💡-4-실무-팁">💡 4. 실무 팁</h3>
<p>성능 이슈로 <code>count(*)</code> 쿼리를 피하고 싶다면</p>
<p><code>Slice&lt;T&gt;</code> 를 사용하는 방법이 있습니다.</p>
<pre><code class="language-java">Slice&lt;Post&gt; posts = postRepository.findAllByOrderByCreatedAtDesc(PageRequest.of(0, 10));
</code></pre>
<p>➡ <code>Slice</code> 는 count 쿼리를 날리지 않고,</p>
<p>내부적으로 <strong>limit+1</strong> 방식으로 다음 페이지 존재 여부(<code>hasNext()</code>)를 판단합니다.</p>
</blockquote>
<h3 id="조회-범위를-시간-기준으로-제한하기">조회 범위를 시간 기준으로 제한하기</h3>
<ol>
<li>뉴스를 보면 월별, 일별로 데이터를 분류해서 보여준다.</li>
<li>즉, 기사 조회 범위를 일자 기준으로 제한하는 것이다.</li>
<li>조회 범위를 제한하는 또 다른 방법은 최신 데이터만 조회하는 것이다.</li>
<li>특히 공지사항의 경우에는 며칠 또는 몇 달 이내의 공지는 읽을 수 있지만 3년 전 공지사항은 (거의) 아무도 읽지 않는다. (구글의 경우 보안활동 데이터는 최근 28일 데이터만 보여준다.)</li>
</ol>
<h3 id="전체-개수-세지-않기">전체 개수 세지 않기</h3>
<ol>
<li>목록을 표시하거나 조건에 해당하는 데이터 개수를 구하기 위해서는 count 함수를 사용해야한다.</li>
<li>데이터가 적을때에는 count 함수를 사용해도 된다.</li>
<li>(하지만) 데이터가 많을때에는 count 함수를 실행할 경우 문제가 된다.</li>
<li>커버링 인덱스를 사용하더라도 (조회된) 전체 인덱스를 스캔해야하고 만약 인덱싱이 되어있지 않은 경우 실제 데이터를 전부 읽어야한다.</li>
</ol>
<blockquote>
<p><strong>APM 프로그램 항상 쉽게 볼 수 있는 곳에 띄워두기</strong></p>
<ol>
<li>서비스를 진행할 경우 서비스가 정상일 때 으답시간 분포가 어떤 형태를 띠는지 감을 잡을 수 있다.</li>
</ol>
</blockquote>
<h3 id="오래된-데이터-삭제-및-분리-보관하기">오래된 데이터 삭제 및 분리 보관하기</h3>
<ol>
<li>과거 데이터를 삭제하면 데이터 개수를 일정하게 유지할 수 있어 성능 또한 일정 수준으로 유지된다.</li>
<li>하지만 과거 데이터를 삭제할 수 있는 케이스는 많지는 않다.<ol>
<li>로그인 시도 내역<ul>
<li>로그인 시도 내역은 장기간 보관할 필요 없기에 최근 180일 치 이상 된 데이터들만 분석에 사용한다면 이후 데이터들은 삭제하여 성능을 유지시킨다.</li>
<li>만약 더 많은 일자가 내부 관리 시스템에서 필요할 경우 서비스 DB 와 분석 DB 를 분리하여 180일 이전 데이터는 별도 DB 로 분리 보관하면 된다.</li>
</ul>
</li>
</ol>
</li>
</ol>
<blockquote>
<p>단편화, 최적화</p>
<ul>
<li><p>DELETE 쿼리를 사용해도 실제 사용하는 디스크 양은 같다.</p>
</li>
<li><p>하지만 데이터가 반복적으로 추가되고 변경되고 삭제되는 과정에서 데이터가 흩어져 저장되ㅗㄱ 빈 공간이 생기는 단편화 현상이 발생할 수 있다.</p>
</li>
<li><p>이를 방지하기 위해 최적화 작업을 한다.</p>
</li>
<li><p>질문답변.</p>
<h3 id="질문-1">질문 1.</h3>
<p>  서적에서 추가 데이터 페이징 처리를 구현할때에</p>
<pre><code>  만약 프론트 개발자가 다음에 읽어올 데이터가 존재하는지 알려주는 속성을 응답 결과에 포함시켜 달라고 요청한다면 1개만 더 읽어 판단하면 된다.

  예를 들어 10개를 조회하는 페이징이 있으면 11개를 조회하고 만약 조회한 데이터가 11개이면 다음에 읽을 데이터가 존재하므로 추가 데이터 존재 여부를 true 로 응답하면 된다.
</code></pre><p>  라고 했는데 JPA 의 pagenatnion 또한 count 쿼리를 활용하지 않고  동일하게 동작할까요?</p>
<h3 id="질문-2">질문 2</h3>
<p>  테이블 단편화를 해결하기 위해 <code>OPTIMIZE TABLE</code> 명령어(서적에서 언급한 최적화)를 사용할 수 있다고 알고 있습니다.
  하지만 운영 중인 서비스의 테이블에 이 명령어를 바로 실행하기 어려운 이유는 무엇이며, 어떤 대안을 고려할 수 있을까요?</p>
<p>  <code>힌트 : 서적에서 DML 을 운영중인 테이블에 사용하면 안된다고 한 이유와 같다.</code></p>
</li>
</ul>
</blockquote>
<h3 id="db-장비-확장하기">DB 장비 확장하기</h3>
<ul>
<li><p>수직 확장의 경우</p>
<p>  스케일 업을 통해 DB 장비의 성능을 빠르게 보충할 수 있는 방법도 있다.</p>
<p>  클라우드를 사용하면 빠른 시간안에 성능을 높일 수 있다.</p>
</li>
<li><p>수평 확장의 경우</p>
<ol>
<li>DB 를 스케일 아웃하는 경우에는 DB 가 처리할 수 있는 트래픽을 늘릴 수 있다.</li>
<li>조회 트래픽의 비중이 높은 서비스의 경우 Primary-Replica 구조를 사용해 처리량을 증가시킬 수 있다.</li>
</ol>
</li>
</ul>
<h3 id="별도의-캐시-서버-구성하기">별도의 캐시 서버 구성하기</h3>
<ol>
<li>트래픽이 급격히 증가할 경우 DB 만으로 트래픽을 처리하기 어려움으로 캐시 서버를 기본적으로 사용한다.</li>
<li>다만 캐시를 도입하면 코드를 수정해야할 수 있다. 하지만 코드수정에 드는 비용이 더 작다</li>
</ol>
<h2 id="주의사항">주의사항</h2>
<h3 id="쿼리-타임아웃">쿼리 타임아웃</h3>
<ol>
<li>응답 시간은 처리량에 큰 영향을 준다.</li>
<li>응답 시간이 길어지면 처리량은 반비례해 감소한다.</li>
<li>하지만 응답지연으로 인해 사용자는 재시도 하기에 서버 부하를 더욱 가중시킨다.</li>
<li>그러므로 쿼리 실행 시간을 제한하는 것이 합리적이다. (timeout)</li>
<li>Time out 시간은 서비스의 기능의 특성에 따라 다르게 설정해야 한다. (결제 처리 등의 경우에는 특히)</li>
</ol>
<h3 id="상태-변경-기능은-replica-db-에서-조회하지-않기">상태 변경 기능은 replica DB 에서 조회하지 않기</h3>
<ol>
<li>주 DB 는 상태 변경을 복제 DB 는 상태 조회 기능을 담당한다.</li>
<li>하지만 그렇다고 해서 모든 SELECT 쿼리를 복제 DB 에서 실행하면 안된다.</li>
<li>주 DB 와 복제 DB의 데이터가 일치하지 않을 수 있기 때문이다. (복제 지연으로 인한 실시간 일관성이 깨질 수 있기 때문에)</li>
<li>트랜잭션 문제가 일어날 수도 있다.</li>
<li>그러므로 변경 대상 데이터를 조회해야한다면 복제 DB가 아닌 주 DB 에서 조회하자.</li>
</ol>
<h3 id="배치-쿼리-실행-시간-증가">배치 쿼리 실행 시간 증가</h3>
<ol>
<li>배치 프로그램은 데이터를 일괄로 조회, 집계, 생성하는 작업을 수행한다.</li>
<li>한번에 처리하는 데이터가 많아질수록 배치 쿼리의 실행 시간도 함께 증가한다.</li>
<li>이 문제를 예방하려면 배치 쿼리의 실행 시간을 지속적으로 모니터링해야 한다. 이를 통해 병목 지점을 찾아 해결할 수 있다.</li>
<li>DB 사양을 증설하여 처리 성능을 높일 수 있다.</li>
<li>커버링 인덱스를 활용하여 쿼리 성능을 개선할 수 있다.</li>
<li>데이터를 일정 크기로 나누어 처리하는 방법도 효과적이다. 쿼리를 시간대별로 분할 실행하면 결과를 안정적으로 얻을 수 있다.</li>
</ol>
<h3 id="타입이-다른-컬럼-조인-주의">타입이 다른 컬럼 조인 주의</h3>
<ol>
<li>타입이 다른 경우 인덱스를 활용할 수 없다.</li>
<li>타입 변환이 이루어지기 때문이다.</li>
<li>그러므로 비교 대상 칼럼의 타입을 맞추면 쿼리 실행 중 발생하는 불필요한 타입변환을 줄일 수 있고 인덱스도 활용할 수 있다.</li>
</ol>
<h3 id="테이블-변경은-신중하게">테이블 변경은 신중하게</h3>
<ol>
<li>MySQL은 타입을 테이블을 변경할 때 새 테이블을 생성하고 원본 데이터를 복사한 뒤 복사가 완료되면 새 테이블로 대체한다.</li>
<li>이 과정에서는 DML 을 허용되지 않는다.</li>
</ol>
<h3 id="db-최대-연결-개수">DB 최대 연결 개수</h3>
<ul>
<li><p>다음과 같은 상황을 상정해보자</p>
<ul>
<li>api 서버는 3대</li>
<li>트래픽이 증가하고 있고 수평확장이 필요하다.</li>
<li>DB 서버의 CPU 사용률은 20% 수준으로 여유가 있다.</li>
</ul>
</li>
<li><p>트래픽 증가를 감당하기 위해 API 서버를 추가할 수 있다.</p>
</li>
<li><p>그런데 새로 추가한 API 서버에서 DB 커넥션 생성에 실패한다면?</p>
<p>  → DB 의 최대 연결개수를 확인해보자.</p>
<p>  하지만 DB 서버의 CPU 사용률이 70% 이상으로 높다면 연결 개수를 늘리면 안된다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[2장 느려진 서비스, 어디부터 봐야 할까]]></title>
            <link>https://velog.io/@junr_65535/%EC%82%AC%EB%82%B4-%EC%8A%A4%ED%84%B0%EB%94%94-%EC%A3%BC%EB%8B%88%EC%96%B4-%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%EB%B0%98%EB%93%9C%EC%8B%9C-%EC%95%8C%EC%95%84%EC%95%BC-%ED%95%A0-%EC%8B%A4%EB%AC%B4%EC%A7%80%EC%8B%9D-2%EC%9E%A5</link>
            <guid>https://velog.io/@junr_65535/%EC%82%AC%EB%82%B4-%EC%8A%A4%ED%84%B0%EB%94%94-%EC%A3%BC%EB%8B%88%EC%96%B4-%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%EB%B0%98%EB%93%9C%EC%8B%9C-%EC%95%8C%EC%95%84%EC%95%BC-%ED%95%A0-%EC%8B%A4%EB%AC%B4%EC%A7%80%EC%8B%9D-2%EC%9E%A5</guid>
            <pubDate>Mon, 01 Dec 2025 01:39:09 GMT</pubDate>
            <description><![CDATA[<h2 id="처리량과-응답시간">처리량과 응답시간</h2>
<p>클라이언트→ 서버 데이터 요청 과정</p>
<ol>
<li>서버에 연결 : TCP를 이용해서 서버에 연결한다.</li>
<li>데이터 전송: 정해진 규칙(프로토콜) 에 따라 데이터를 서버에 전송한다.</li>
</ol>
<h3 id="응답시간">응답시간</h3>
<ul>
<li><p>응답 시간의 측정 방법</p>
<ul>
<li>TTFB : 응답 데이터 중 첫번째 바이트가 도착할 때까지 걸린 시간</li>
<li>TTLB : 응답 데이터 중 마지막 바이트가 도착할 때까지 걸린 시간</li>
</ul>
</li>
<li><p>응답시간의 구성 요소</p>
<ul>
<li><p>로직 수행 시간 (if, for 등)</p>
</li>
<li><p>DB 연동(SQL 실행)</p>
</li>
<li><p>외부 API 연동</p>
</li>
<li><p>응답 데이터 생성(전송)</p>
<p>→ 보통 외부 API 를 연동하거나 DB를 연동하는 시간이 응답시간에 많은 부분을 차지하기 때문에 응답시간을 줄일 때 DB 연동과 API 연동 시간에 집중한다.</p>
</li>
</ul>
</li>
</ul>
<h3 id="처리량throughput">처리량(Throughput)</h3>
<p>단위 시간당 시스템이 처리하는 작업량</p>
<ul>
<li><p>처리량 표현법</p>
<ul>
<li>TPS<ul>
<li>초당 트랜잭션의 수 해당 초 동안 완료된 트랜잭셔느이 개수를 의미한다.</li>
<li>최대 TPS 는 시스템이 처리할 수 있는 최대 요청 수를 의미한다.<ul>
<li>요청이 최대 TPS 를 초과하면 최대 TPS 수를 처리한 이후 나머지 트랜잭션이 실행된다.</li>
</ul>
</li>
</ul>
</li>
<li>RPS<ul>
<li>초다 요청의 수</li>
</ul>
</li>
</ul>
</li>
<li><p>처리량을 늘리는 법.</p>
<ol>
<li><p>트래픽이 많은 시간대에 TPS 와 응답시간을 측정하기</p>
</li>
<li><p>목표 TPS 와 응답시간을 설정하기</p>
<p>→ 이를 가장 쉽게 알 수 있는 방법은 모니터링 시스템을 활용하는것임</p>
</li>
</ol>
</li>
<li><p>트래픽 모니터링 시스템</p>
</li>
</ul>
<h2 id="서버-성능-개선-기초">서버 성능 개선 기초</h2>
<p>서비스 초기에는 성능 문제가 잘 발생하지 않으나 점차 사용자가 늘면서 성능문제가 발생하기 시작함.</p>
<p>그때 보이는 지표는 다음과 같음</p>
<ul>
<li>순간적인 모든 사용자 요청에 대한 응답시간이 심각하게 느려짐</li>
<li>서버를 재시작하면 잠시 괜찮았다가 다시 응답시간이 느려짐</li>
<li>트래픽이 줄어들 때까지 심각한 상황이 계속됨</li>
</ul>
<p>이러한 지표가 보이는 이유는 시스템이 수용할 수 있는 최대 TPs 보다 더 높은 트래픽이 유입되기 때문</p>
<p>→ 보통 이러한 성능 문제는 주로 DB 연동이나 API 연동 과정에서 발생한다.</p>
<ul>
<li><p>수직확장과 수평확장</p>
<p>  수직확장(scale-up) : CPU, 메모리, 디스크 등의 자원을 증가시키는 것</p>
<p>  수평 확장(scale-out) : 서버를 추가로 투입해 TPS 를 높이는 방법, 로드밸런서가 필요하다.</p>
</li>
</ul>
<pre><code>&gt; 로드밸런서
&gt; 
&gt; 
&gt; 사용자의 트래픽을 각 서버에 골고루 분배해서 한 서버에 사용자 트래픽이 몰리지 않도록 하는 것
&gt; 
&gt; - 로드밸런서의 트래픽 분산 방식
&gt; - 로드밸런싱 알고리즘 종류
&gt;     - 정적인 방식
&gt;         - 해쉬 알고리즘
&gt;             - 특정 기준을 잡아 특정 서버에 매핑하고 고정적으로 트래픽을 분산해주는 방식
&gt;             - 일반적으로 사용되는 기준은 출발비의 IP가 됨.
&gt;         - 라운드 로빈 (RR) : 트래픽이 순차적으로 들어올때 서버에 순차적으로 하나씩 분산
&gt;             - 들어오는 트래픽을 서버 순서대로 배치하는 방식
&gt;             - 연결된 세션이 비교적 오래 사용되지 않은 경우에 채택하는 것이 좋음.
&gt;     - 동적인 방식
&gt;         - Leat Connection 알고리즘
&gt;             - 현재 매핑되어있는 커넥션이 가장 적은 서버로 세션을 연결해주는 방식
&gt;             - Keep Alive로 오랫동안 연결이 지속될경우 문제 발생 가능성 있음.
&gt;         - Weighted Round Robin
&gt;             - 서버의 가중치를 따라 요청을 더 분배하기도 하는 알고리즘</code></pre><p>→ 무턱대고 서버  추가만 한다고 해결되진 않음</p>
<p>DB 에서 문재가 일어나는지, 외부 API 가 문제인지 등의 문제 발생 지점을 파악하는것이 우선</p>
<h3 id="db-커넥션-풀">DB 커넥션 풀</h3>
<p>DB 를 사용하는 3단계</p>
<ol>
<li>DB 에 연결한다</li>
<li>쿼리를 실행한다</li>
<li>사용이 끝나면 연결을 종료한다.</li>
</ol>
<p>이때 네트워크 연결을 생성하고 종료하는데 걸리는 시간의 80% 이상이 DB 연결 및 종료에 쓰이게 되는것이 일반적.</p>
<p>이를 방지하기 위해 DB 커넥션 풀을 사용한다. 스프링 부트는 HikariCp를 사용</p>
<ul>
<li><p>커넥션 풀 연결 과정</p>
<h2 id="2️⃣-connection-pool-추가된-상세-과정"><strong>2️⃣ Connection Pool 추가된 상세 과정</strong></h2>
<h3 id="1-mysql-서버-소켓-생성--연결-요청"><strong>1. MySQL 서버 소켓 생성 &amp; 연결 요청</strong></h3>
<ul>
<li>기존과 동일하게 MySQL 서버는 3306 포트에서 연결을 대기.</li>
<li>클라이언트가 연결 요청을 하면 <strong>Connection Pool이 먼저 기존 연결을 확인</strong>.</li>
<li>사용 가능한 연결이 있으면 즉시 할당하고, 없으면 새로운 연결을 생성 후 추가.</li>
</ul>
<hr>
<h3 id="2-mysql-서버의-프로세스스레드-생성"><strong>2. MySQL 서버의 프로세스/스레드 생성</strong></h3>
<ul>
<li><p>MySQL은 기본적으로 Thread-Per-Connection 방식으로 작동하여 클라이언트 요청이 오면 새로운 스레드 또는 프로세스를 생성하지만, <strong>Connection Pool을 사용하면 기존 연결을 재사용하여 불필요한 스레드/프로세스 생성을 방지</strong>.</p>
<h3 id="변경점"><strong>변경점</strong></h3>
</li>
<li><p>기존 방식: 새로운 요청이 오면 새로운 스레드/프로세스 생성.</p>
</li>
<li><p>커넥션 풀 도입 후: 기존 연결을 재사용하여 서버 부하 감소.</p>
</li>
</ul>
<hr>
<h3 id="3-sql-데이터-송수신"><strong>3. SQL 데이터 송수신</strong></h3>
<ul>
<li>SQL 쿼리 송수신 과정은 동일하지만, <strong>Connection Pool을 사용할 경우 클라이언트가 작업을 끝낸 후 연결을 즉시 닫지 않고 다시 풀에 반환</strong>.</li>
<li>기존 방식에서는 클라이언트가 연결을 종료하면 MySQL 서버가 이를 해제하지만, Connection Pool이 있으면 클라이언트가 <code>close()</code>를 호출해도 물리적인 연결이 닫히지 않고 풀에 반환됨.</li>
</ul>
</li>
<li><p><strong>1️⃣ 기존 SQL 연결 방식의 단점</strong></p>
<p>  과거에는 <strong>클라이언트가 DB 서버에 접속할 때마다 새로운 연결을 생성</strong>하는 방식(<code>Thread-Per-Connection</code> 또는 <code>Fork-Per-Connection</code>)을 사용했음.</p>
<p>  하지만 이 방식은 <strong>성능 저하와 자원 낭비 문제</strong>를 발생시켰고, 이를 해결하기 위해 <strong>Connection Pool</strong> 개념이 등장하게 됨.</p>
<h3 id="✅-기존-방식의-문제점"><strong>✅ 기존 방식의 문제점</strong></h3>
<ol>
<li><strong>연결 생성 오버헤드 (Connection Overhead)</strong><ul>
<li>클라이언트가 DB에 연결할 때마다 <strong>TCP/IP 연결을 맺고(TCP 3-Way Handshake)</strong>, 인증 과정을 거쳐야 함.</li>
<li>이 과정에서 <strong>CPU, 메모리, 네트워크 자원을 불필요하게 소모</strong>함.</li>
</ul>
</li>
<li><strong>동시 연결 한계 (Connection Limit)</strong><ul>
<li>DB 서버는 <strong>동시에 처리할 수 있는 연결 수에 한계</strong>가 있음.</li>
<li>예를 들어, MySQL의 <code>max_connections</code> 기본값은 <strong>151개</strong>인데, 이 이상 연결하면 <strong>DB 접속 거부(Connection Refused)</strong>가 발생.</li>
</ul>
</li>
<li><strong>메모리 사용 증가 (Memory Consumption)</strong><ul>
<li>많은 클라이언트가 동시에 접속하면, <strong>DB 서버가 과부하 상태</strong>가 됨.</li>
<li>PostgreSQL처럼 <strong>Fork-Per-Connection</strong> 방식을 쓰는 경우, <strong>클라이언트가 많아질수록 메모리 사용량이 급증</strong>함.</li>
</ul>
</li>
<li><strong>연결 종료 시 오버헤드 (Disconnect Overhead)</strong><ul>
<li>클라이언트가 요청을 마치고 연결을 끊으면, <strong>TCP 4-Way Handshake</strong>를 수행하고 해당 프로세스 또는 스레드를 정리해야 함.</li>
</ul>
</li>
</ol>
</li>
</ul>
<h3 id="1-커넥션-풀-튜닝-🏊♂️">1. 커넥션 풀 튜닝 🏊‍♂️</h3>
<p>데이터베이스 커넥션을 맺는 과정은 비용이 많이 드는 작업입니다. <strong>커넥션 풀</strong>은 미리 일정 개수의 커넥션을 만들어두고 재사용함으로써 이 비용을 줄여주는 핵심 기술입니다.</p>
<h3 id="동적-크기-조절-minmax-설정">동적 크기 조절 (Min/Max 설정)</h3>
<p>서비스 트래픽은 항상 변동합니다. 예를 들어, 은행 서비스는 낮에, 게임 서비스는 저녁에 트래픽이 집중됩니다. 이런 패턴에 효율적으로 대응하기 위해 커넥션 풀의 크기를 동적으로 조절합니다.</p>
<ul>
<li><strong><code>minimum-idle</code> (Min)</strong>: 트래픽이 적을 때도 유지할 최소 커넥션 개수입니다. 유휴 커넥션을 너무 많이 유지하면 DB 서버에 불필요한 부하를 줍니다.</li>
<li><strong><code>maximum-pool-size</code> (Max)</strong>: 트래픽 급증 시 최대로 생성할 수 있는 커넥션 개수입니다. 이 값을 초과하면 클라이언트는 커넥션을 할당받기 위해 대기해야 합니다.</li>
</ul>
<h3 id="주요-시간-설정">주요 시간 설정</h3>
<ul>
<li><strong>커넥션 대기 시간 (<code>connection-timeout</code>)</strong>: 풀에 가용한 커넥션이 없을 때, 클라이언트가 커넥션을 얻기 위해 대기할 수 있는 최대 시간입니다. 이 시간을 초과하면 오류가 발생합니다.</li>
<li><strong>최대 유휴 시간 (<code>idle-timeout</code>)</strong>: 커넥션이 풀에서 사용되지 않고 대기할 수 있는 최대 시간입니다. 이 시간이 지나면 커넥션은 풀에서 제거되어 불필요한 자원 낭비를 방지합니다.</li>
<li><strong>최대 유지 시간 (<code>max-lifetime</code>)</strong>: 커넥션의 최대 수명 시간입니다. 이 시간이 지나면 사용 중이더라도 반납 시 풀에서 제거됩니다. MySQL의 <code>wait_timeout</code>(기본 8시간) 때문에 발생하는 커넥션 단절 문제를 예방하기 위해, <strong>이 값을 DB의 <code>wait_timeout</code>보다 짧게 설정하는 것이 매우 중요합니다.</strong></li>
</ul>
<hr>
<h3 id="2-서버-캐시-전략-⚡">2. 서버 캐시 전략 ⚡</h3>
<p>스케일 아웃/업은 비용이 많이 들기 때문에, 응답 시간을 줄이기 위한 효과적인 대안으로 <strong>캐시</strong>를 사용합니다. 캐시는 자주 조회되는 데이터를 <strong>키-밸류(Key-Value)</strong> 형태로 빠른 저장소(주로 메모리)에 보관하여 느린 DB 조회를 피하게 해줍니다.</p>
<h3 id="캐시-효율성-측정-hit-rate">캐시 효율성 측정: Hit Rate</h3>
<p>캐시의 효과성은 <strong>캐시 히트율(Cache Hit Rate)</strong>로 측정합니다.</p>
<ul>
<li><strong><code>Cache Hit Rate (%) = (캐시에서 데이터를 찾은 횟수 / 총 조회 횟수) * 100</code></strong></li>
<li>히트율이 높을수록 DB 부하를 효과적으로 줄이고 있다는 의미입니다.</li>
</ul>
<h3 id="캐시-교체삭제-정책">캐시 교체(삭제) 정책</h3>
<p>캐시의 메모리는 한정적이므로, 새로운 데이터 저장 공간이 부족할 때 어떤 데이터를 삭제할지 결정하는 규칙이 필요합니다.</p>
<ul>
<li><strong>LRU (Least Recently Used)</strong>: <strong>가장 오랫동안 사용되지 않은</strong> 데이터를 삭제합니다. &quot;한번 액세스한 데이터는 가까운 미래에 다시 액세스될 것이다&quot;라는 <strong>시간 지역성(Temporal Locality)</strong>에 기반한 가장 일반적인 정책입니다.</li>
<li><strong>LFU (Least Frequently Used)</strong>: <strong>가장 적게 사용된(액세스 빈도가 낮은)</strong> 데이터를 삭제합니다.</li>
<li><strong>FIFO (First-In, First-Out)</strong>: <strong>가장 먼저 들어온</strong> 데이터를 순서대로 삭제하는 가장 간단한 방식입니다.</li>
</ul>
<h3 id="캐시의-종류-로컬-캐시-vs-리모트-캐시">캐시의 종류: 로컬 캐시 vs 리모트 캐시</h3>
<table>
<thead>
<tr>
<th>구분</th>
<th>로컬 캐시 (Local Cache)</th>
<th>리모트 캐시 (Remote/Distributed Cache)</th>
</tr>
</thead>
<tbody><tr>
<td><strong>개념</strong></td>
<td>애플리케이션 서버의 메모리 내부에 저장</td>
<td>별도의 캐시 서버(프로세스)에 저장</td>
</tr>
<tr>
<td><strong>장점</strong></td>
<td>✅<strong>속도가 매우 빠름 (네트워크 오버헤드 없음)</strong>✅ 구현이 간단함</td>
<td>✅<strong>여러 서버 간 데이터 공유 및 일관성 유지</strong>✅ 훨씬 큰 데이터 저장 가능</td>
</tr>
<tr>
<td>✅ 애플리케이션 재시작 시에도 데이터 유지</td>
<td></td>
<td></td>
</tr>
<tr>
<td><strong>단점</strong></td>
<td>❌<strong>여러 서버 간 데이터 공유 불가</strong>❌ 서버 확장(스케일 아웃) 시 데이터 불일치 문제 발생</td>
<td></td>
</tr>
<tr>
<td>❌ 애플리케이션 메모리 소모</td>
<td>❌<strong>네트워크 지연 시간(Latency) 발생</strong>❌ 별도의 캐시 서버 구축 및 관리 비용 발생</td>
<td></td>
</tr>
<tr>
<td><strong>대표 사례</strong></td>
<td>EhCache, Caffeine, Guava Cache</td>
<td><strong>Redis</strong>, Memcached</td>
</tr>
<tr>
<td><strong>주요 용도</strong></td>
<td>·특정 서버 인스턴스에만 국한된 데이터</td>
<td></td>
</tr>
<tr>
<td>거의 변하지 않는 전역 설정 정보</td>
<td>·사용자 세션 정보</td>
<td></td>
</tr>
<tr>
<td>여러 서버가 공유해야 하는 데이터</td>
<td></td>
<td></td>
</tr>
<tr>
<td>DB 부하를 줄이기 위한 주력 캐시</td>
<td></td>
<td></td>
</tr>
</tbody></table>
<h3 id="캐시-사전-적재">캐시 사전 적재</h3>
<p>트래픽이 순간적으로 급등한다면 해당정보를 미리 캐시애두는 것도 고려 가능</p>
<h3 id="캐시-무효화">캐시 무효화</h3>
<p>유효하지 않은 데이터를 적절한 시점에 캐시에서 삭제하는 것이 중요함</p>
<p>변경에 민감한 데이터는 로컬 캐시가 아니라 리모트 캐시에 보관</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SECS - I 부터 HSMS 까지 03 (HSMS 프로토콜에 관하여)]]></title>
            <link>https://velog.io/@junr_65535/SECS-I-%EB%B6%80%ED%84%B0-HSMS-%EA%B9%8C%EC%A7%80-03-HSMS-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC</link>
            <guid>https://velog.io/@junr_65535/SECS-I-%EB%B6%80%ED%84%B0-HSMS-%EA%B9%8C%EC%A7%80-03-HSMS-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC</guid>
            <pubDate>Sun, 27 Apr 2025 13:00:17 GMT</pubDate>
            <description><![CDATA[<p>이전 글들에서 스마트 팩토리에서 CIM과 통신하기 위해 사용되는 SEMI 표준들 중 SECS 프로토콜, 그 중에서 SECS-I, SECS-II 프로토콜에 대해 알아보았다.</p>
<p>오늘은 SECS 메시지들을 TCP 로 통신할때 필요한 전송 표준인 HSMS 에 대해 알아보겠다.</p>
<h2 id="hsmshigh-speed-secs-message-service-란">HSMS(High Speed SECS Message Service) 란?</h2>
<p>기존에 SECS 통신은 RS-232라는 직렬 시리얼 통신 위에서 SECS-I를 통해 SECS-II 메시지들을 주고받으며 통신이 이루어졌다.</p>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/0e395e59-730a-4499-abb4-a97dbb2acd04/image.png" alt=""></p>
<p>하지만 점점 이더넷 기술 규격이 발달하고 널리 퍼짐에 따라 TCP/IP 프로토콜을 사용해서 통신을 하는 시대가 됐다.</p>
<p>그래서 이러한 TCP/IP 위에서도 안정적으로 SECS-II 메시지들을 사용해서 통신을 주고받을 수 있도록 규약을 만든 것이 HSMS 프로토콜이다.</p>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/5ab739b0-a4d9-4a7b-b39e-b648ddca453b/image.png" alt=""></p>
<p><em>→ HSMS란? TCP/IP 네트워크 상에서 SECS-II 메시지를 안정적으로 주고받기 위한 전송 프로토콜 이다</em>.</p>
<p>이제 HSMS 프로토콜의 구조를 살펴보자.</p>
<h2 id="hsms-프로토콜의-구조">HSMS 프로토콜의 구조</h2>
<p>기본적으로 SECS-I 프로토콜에서 크게 구조가 변하지 않았다.</p>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/ef73ef4b-0815-4c18-a54c-4a9bbf86fba6/image.png" alt=""></p>
<ul>
<li>A 영역(Length) : <code>4 byte</code>, B 영역과 C 영역의 길이를 나타낸다.</li>
<li>B 영역 (Header) : <code>10 byte</code>  (Session Id, Heder Byte 2, Header Byte 3, Ptype, Stype, System Byte)</li>
<li>C 영역 (Data) : <code>가변적</code> , SECS-II 메시지가 존재.</li>
</ul>
<h3 id="header-b영역">Header (B영역)</h3>
<p>HSMS 메시지의 헤더는 크게 Session Id, Heder Byte 2, Header Byte 3, Ptype, Stype, System Byte 로 나뉘어져 있다.</p>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/1c5d8ed2-f088-4af3-88fe-936cd3ac4364/image.png" alt=""></p>
<ul>
<li><p>Session ID</p>
<p>  Device ID, Host ID 각 통신 객체간의 관계를 규정하는 영역</p>
</li>
<li><p>Header Byte 2</p>
<p>  HSMS 메시지는 크게 Data Message와 Control Message로 나뉜다.</p>
<p>  메시지 유형은 SType 값으로 구분함 (0: Data Message, 그 외: Control Message)</p>
<ul>
<li><ol>
<li><p>Data Message (데이터 메시지)</p>
<p>실제 생산 장비와 호스트 간의 SECS-II 메시지를 전송할 때 사용</p>
<ul>
<li>구성 요소<ul>
<li>W-Bit (MSB): 응답 필요 여부를 표시</li>
<li>1: 응답 필요</li>
<li>0: 응답 불필요</li>
</ul>
</li>
<li>Stream Number (하위 7비트): <strong>SECS-II 메시지의 Stream 번호</strong></li>
<li>용도<ul>
<li>장비 상태 보고</li>
<li>명령 실행</li>
<li>데이터 수집</li>
</ul>
</li>
</ul>
</li>
</ol>
</li>
<li><ol start="2">
<li>Control Message (제어 메시지)<ul>
<li>특징<ul>
<li>일반적으로 Header Byte 2는 0으로 설정</li>
<li>특수한 경우 상태 코드 포함 가능</li>
</ul>
</li>
<li>용도<ul>
<li>연결 설정</li>
<li>연결 해제</li>
<li>링크 테스트</li>
</ul>
</li>
</ul>
</li>
</ol>
</li>
</ul>
</li>
<li><p><strong>Header Byte 3</strong></p>
<ul>
<li><p><strong>Data Message</strong>일 경우:</p>
<ul>
<li><p><strong>Function Number</strong> (8비트 전체 사용)</p>
<p>  → SECS-II 메시지에서 사용하는 <strong><code>Function 코드</code> 번호</strong>를 의미.</p>
<p>  → 즉, 특정 Stream 안에서 어떤 기능(명령/응답)인지를 구체적으로 지시하는 값.</p>
</li>
</ul>
</li>
<li><p><strong>Control Message</strong>일 경우:</p>
<ul>
<li><p><strong>Control Type</strong> (8비트 전체 사용)</p>
<p>  → 제어 메시지의 종류를 지정함. (예: 연결 요청, 연결 종료, 링크 테스트 등)</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>PType (Protocol Type)</strong></p>
<ul>
<li>항상 <strong>0</strong>으로 설정 (Reserved)</li>
<li>HSMS에서 통신 프로토콜 타입은 고정값을 가지며, 추가 정의 없이 <strong>0</strong>을 사용함.</li>
</ul>
</li>
<li><p><strong>SType (Session Type)</strong></p>
<ul>
<li>이 메시지가 <strong><code>어떤 종류의 통신 세션 메시지인지</code></strong>를 나타낸다.</li>
<li>대표적인 SType 예시:<ul>
<li>0: <strong>Data Message</strong> (SECS-II 데이터 전송용)</li>
<li>1: <strong>Select.req</strong> (연결 요청)</li>
<li>2: <strong>Select.rsp</strong> (연결 응답)</li>
<li>5: <strong>Linktest.req</strong> (링크 상태 테스트 요청)</li>
<li>6: <strong>Linktest.rsp</strong> (링크 상태 테스트 응답)</li>
<li>9: <strong>Separate.req</strong> (연결 종료 요청)</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>System Bytes (4 Byte)</strong></p>
<ul>
<li>송신자가 임의로 설정하고, 응답 시 동일하게 복사되어 돌아오는 값.</li>
<li><strong>요청-응답을 매칭</strong>하기 위한 용도로 사용됨.<ul>
<li>예를 들어, 송신자가 메시지를 보낼 때 System Byte를 1234로 설정했다면, 그 응답 메시지에도 1234가 붙어 와야 한다.</li>
<li>이렇게 해서 요청과 응답 쌍을 구분하고 관리할 수 있다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="control-message-와-data-message">Control Message 와 Data Message</h2>
<h3 id="1-data-message-데이터-메시지"><strong>1. Data Message (데이터 메시지)</strong></h3>
<ul>
<li><strong>목적</strong>: 실제 생산장비와 호스트(서버) 간에 <strong>업무용 데이터</strong>를 주고받기 위한 메시지.</li>
<li><strong>특징</strong>:<ul>
<li><strong>SType = 0</strong>.</li>
<li>SECS-II 메시지를 담고 있음. (장비 상태 보고, 명령 수행, 데이터 수집 등)</li>
<li><strong>Header Byte 2</strong>의 상위 1비트(W-Bit)가 응답 필요 여부를 표시:<ul>
<li><strong>1</strong> → 이 메시지에 대해 응답이 필요함.</li>
<li><strong>0</strong> → 이 메시지에 대해 응답이 필요 없음.</li>
</ul>
</li>
<li><strong>Header Byte 2</strong>의 하위 7비트는 <strong>Stream Number</strong> (SECS-II Stream 번호).</li>
<li><strong>Header Byte 3</strong>는 <strong>Function Number</strong> (SECS-II Function 번호).</li>
</ul>
</li>
<li><strong>구성 예시</strong>:</li>
</ul>
<pre><code>- Stream 2, Function 17 : 특정 명령 전송
- Stream 3, Function 1 : 상태 요청</code></pre><hr>
<h3 id="2-control-message-제어-메시지"><strong>2. Control Message (제어 메시지)</strong></h3>
<ul>
<li><strong>목적</strong>: <strong>통신 상태를 관리</strong>하기 위한 메시지. (연결, 해제, 유지 등)</li>
<li><strong>특징</strong>:<ul>
<li><strong>SType ≠ 0</strong> (0이 아닌 값들: 1, 2, 5, 6, 9 등)</li>
<li>SECS-II 데이터는 <strong>포함되지 않음</strong> (C 영역이 없음 또는 아주 짧음).</li>
<li>통신 자체를 유지하거나 제어하기 위한 간단한 명령을 보내는 것.</li>
<li><strong>Header Byte 2</strong>는 보통 <strong>0</strong>으로 설정.</li>
<li><strong>Header Byte 3</strong>는 <strong>Control Type</strong>을 나타냄. (어떤 제어 명령인지를 지정)</li>
</ul>
</li>
<li><strong>주요 Control Message 종류 (SType 기준)</strong>:</li>
</ul>
<table>
<thead>
<tr>
<th><strong>SType</strong></th>
<th><strong>의미</strong></th>
<th><strong>설명</strong></th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>Select.req</td>
<td>연결 요청</td>
</tr>
<tr>
<td>2</td>
<td>Select.rsp</td>
<td>연결 요청 응답</td>
</tr>
<tr>
<td>5</td>
<td>Linktest.req</td>
<td>링크 테스트 요청</td>
</tr>
<tr>
<td>6</td>
<td>Linktest.rsp</td>
<td>링크 테스트 응답</td>
</tr>
<tr>
<td>9</td>
<td>Separate.req</td>
<td>연결 해제 요청</td>
</tr>
<tr>
<td>- <strong>Control Message 구성 예시</strong>:</td>
<td></td>
<td></td>
</tr>
<tr>
<td>- <strong>Select.req</strong>: 장비와 호스트가 새 연결을 만들 때 보냄.</td>
<td></td>
<td></td>
</tr>
<tr>
<td>- <strong>Linktest.req</strong>: 통신이 정상인지 확인하고 싶을 때 보냄.</td>
<td></td>
<td></td>
</tr>
<tr>
<td>- <strong>Separate.req</strong>: 정상적으로 연결을 끊고 싶을 때 보냄.</td>
<td></td>
<td></td>
</tr>
</tbody></table>
<p>즉 정리하자면 Control Message냐 Data 메시지냐는 아래에 따라 알 수 있다.</p>
<table>
<thead>
<tr>
<th><strong>구분</strong></th>
<th><strong>Data Message</strong></th>
<th><strong>Control Message</strong></th>
</tr>
</thead>
<tbody><tr>
<td>목적</td>
<td>업무 데이터 전송</td>
<td>통신 제어 관리</td>
</tr>
<tr>
<td>SType</td>
<td>0</td>
<td>1, 2, 5, 6, 9 등</td>
</tr>
<tr>
<td>Header Byte 2</td>
<td>W-Bit + Stream Number</td>
<td>보통 0</td>
</tr>
<tr>
<td>Header Byte 3</td>
<td>Function Number</td>
<td>Control Type</td>
</tr>
<tr>
<td>SECS-II 메시지 포함 여부</td>
<td>포함 (C영역 사용)</td>
<td>거의 없음 (C영역 비워짐)</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[SECS - I 부터 HSMS 까지 02 (SECS-II 프로토콜에 관하여)]]></title>
            <link>https://velog.io/@junr_65535/SECS-I-%EB%B6%80%ED%84%B0-HSMS-%EA%B9%8C%EC%A7%80-02-SECS-II-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC</link>
            <guid>https://velog.io/@junr_65535/SECS-I-%EB%B6%80%ED%84%B0-HSMS-%EA%B9%8C%EC%A7%80-02-SECS-II-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC</guid>
            <pubDate>Sun, 20 Apr 2025 12:42:31 GMT</pubDate>
            <description><![CDATA[<p>지난번에는 SECS-I 에 관해 알아봤다.
<img src="https://velog.velcdn.com/images/junr_65535/post/3e1eb4b6-78e5-4111-8096-0d63611cd40f/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/4b838585-a59e-468d-977f-6fdb64082ea2/image.png" alt=""></p>
<p>오늘은 SECS-I 나 HSMS 위에서 상위 계층으로써 메시지의 구조를 약속한 규약인 SECS-II에 대해 알아보자... </p>
<p><code>SEMI E5</code> : 실제 송수신되는 메시지의 통신 내용(Content), Data 영역에 대한 프로토콜인 <code>SECS-II</code> 에 대한 표준</p>
<p>SECS-II는 SECS-I나 HSMS 통신 위에서 캡슐화되는 메시지의 구조와 표현 방식을 정의하는 포맷 규약이다</p>
<p>처음 메시지 구조를 봤을 때 xml 이나 JSON 과 비슷하다는 생각을 했다.</p>
<p>XML이나 JSON과 유사하지만, 중요한 차이점이 있다면 XML이나 JSON은 단순히 메시지의 구조만 정의하는 반면, SECS-II의 메시지는 <strong>사전에 정의된 특정한 의미</strong>를 포함한다.</p>
<h3 id="✅-secs-ii와-jsonxml-비교">✅ SECS-II와 JSON/XML 비교</h3>
<table>
<thead>
<tr>
<th>항목</th>
<th>SECS-II</th>
<th>JSON / XML</th>
</tr>
</thead>
<tbody><tr>
<td><strong>역할</strong></td>
<td>장비 ↔ 호스트 간 메시지 형식 정의</td>
<td>시스템 간 데이터 포맷 정의</td>
</tr>
<tr>
<td><strong>형식</strong></td>
<td>SxFy 구조 (e.g., S1F1, S2F41 등)</td>
<td>Key-Value 구조, 태그 구조</td>
</tr>
<tr>
<td><strong>전송 방법</strong></td>
<td>SECS-I 또는 HSMS로 전송</td>
<td>HTTP, WebSocket, MQTT 등 다양</td>
</tr>
<tr>
<td><strong>표현 방식</strong></td>
<td>바이너리 형식 + 데이터 타입 명시</td>
<td>문자열 기반</td>
</tr>
<tr>
<td><strong>의미 부여</strong></td>
<td>각 SxFy는 특정한 의미를 가짐 (e.g., 상태 요청, 커맨드 전송 등)</td>
<td>의미는 시스템 정의에 따라 다름</td>
</tr>
<tr>
<td><strong>표준화 여부</strong></td>
<td>SEMI 표준(SEMI E5)</td>
<td>JSON은 사실상 표준, XML은 W3C 표준</td>
</tr>
</tbody></table>
<hr>
<blockquote>
<p>그렇다면 왜 JSON/XML 같이 널리 쓰이는 포맷으로 통신하고 특정한 의미. 즉 시스템 정의만 따로 해서 사용하면 되는데 왜 SECS-II 라는 표준 규약을 사용할까?</p>
<p>그거야 secs I 와 secs II가 같은 시기에 개발되어서….라고도 할 수 있지만</p>
<p>적은 오버헤드와 고밀도로 데이터를 전송할 수 있기 때문에.. 가 될것같다.</p>
<p>의미를 포함한 데이터 포맷을 지정해뒀기에 하드웨어 수준에서 이를 해석하는데 오버헤드가 적다.</p>
<p>아래 블로그에 이러한 비교가 잘 되어있음.</p>
<p>애초에 SECS 프로토콜을 설계할때에 밀접하게 설계해뒀기에 SECS-I 위에서 오버헤드가 가장 적다.</p>
<p><a href="https://www.cimetrix.com/blog/secs-gem-series-protocol-layer">SECS/GEM series: Protocol Layer</a>
<a href="https://shoney.tistory.com/entry/SECS-GEM-%EC%8B%9C%EB%A6%AC%EC%A6%88-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C-%EB%A0%88%EC%9D%B4%EC%96%B4">SECS / GEM 시리즈 : 프로토콜 레이어</a></p>
</blockquote>
<hr>
<blockquote>
<p>보통 osi 7 layer에서 캡슐화를 할때에는
<img src="https://velog.velcdn.com/images/junr_65535/post/3e4b215e-3131-4309-8e77-268aa63597cb/image.png" alt=""></p>
</blockquote>
<blockquote>
<p>이런식으로 상위 레이어의 헤더 + 데이터 = 하위 레이어의 데이터 가 되는 경우가 많다.
그래서 캡슐화 시에 상위 레이어로부터 받은 헤더, 데이터 정보들은 하위 레이어들의 해석에 아무런 의미가 없다.
하지만 <code>HSMS + SECS II</code> 나 <code>SECS I + SECS II</code> 는 다르다.
얘는 HSMS의 헤더와 데이터가 SECS- II 의 포맷에 포함되어있음.
그래서 SECS - II 메시지를 파싱하려면 HSMS의 헤더랑 데이터 둘다 봐야한다.</p>
</blockquote>
<h2 id="1-stream--function">1. Stream / Function</h2>
<p>SECS-II 메시지는 스트림과 펑션에 의해 구분된다.</p>
<ul>
<li>Stream : 유사한 특성을 갖는 메시지의 그룹, 예약영역 + 사용자 영역 합쳐서 S1 ~ S127 까지 있다.</li>
<li>Function : 각 스트림 내에서 특정한 기능을 하는 메시지, 예약영역 + 사용자 영역 합쳐서 F1 ~ F255 까지 있다<ul>
<li>Stream 안에서 각 세부 기능에 관련된 개별 Msg</li>
<li>Primary Msg는 항상 홀수의 Function 번호를 갖는다.</li>
<li>Reply (Secondary) Msg는 Primary Msg Function 번호에 1을 더한 수로 모두 짝수이다.</li>
<li>Function 번호는 &#39;0&#39;은 Message Transaction 취소를 위해 예약되어 있다.</li>
</ul>
</li>
</ul>
<p>SEMI 에서는 특정한 의미를 갖는 SECS-II 메시지들을 이미 많이 만들어 두고 사용하고 있고 사용자 정의도 가능</p>
<aside>
💡

<h3 id="예약-영역">예약 영역</h3>
<ul>
<li>스트림은 S1 ~ S64 까지, 펑션은 F1 ~ F64 까지</li>
<li><code>SYF0</code> : (All Stream, Function 0) ; Message Transaction 취소</li>
<li><code>S0FY</code> : (Stream 0, All Function)</li>
</ul>
<h3 id="사용자-정의-영역">사용자 정의 영역</h3>
<ul>
<li>S64 ~ S127 (예약되지 않은 Stream)의 경우 사용자 정의 가능한 펑션은 F1 ~ F255 까지</li>
<li>S1 ~ S63 스트림(이미 예약된 Stream)의 경우 사용자 정의 가능한 펑션은 F65 ~ F255 까지</aside>

</li>
</ul>
<p>이전에 공부한 SECS-I 10 Byte 헤더의 3번, 4번 Byte가 Stream과 Function을 가리킵니다.
Stream이 예약영역과 사용자 영역을 합쳐 S1<del>S127까지 2^7개 사용 가능하고, Function이 F1</del>F256까지 사용 가능한 이유는 SECS-I 헤더의 3번 Byte에 W-bit(전송되는 메시지에 대한 송신측의 응답 여부를 표시하는 비트)가 할당되었기 때문으로 보입니다.
<img src="https://velog.velcdn.com/images/junr_65535/post/cd54a260-9a2f-4cdb-af19-18828ecf7c04/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/3287b36d-c0a5-4adb-8466-7ae9b414227d/image.png" alt=""></p>
<aside>
💡

<p>이후 배울 HSMS에서는 Stream 과 Function 을 HSMS 헤더의 B 영역인 ‘메시지헤더’ 라는 곳에서 볼 수 있다. 해당 B 영역이 SECS-I 의 헤더 구조와 아주 유사하다. </p>
<p>다만 Ptype과 Stype 이라고 하는 부분이 다른데 그건 <a href="https://blog.naver.com/yhol98/223478983236">**해당 블로그</a> 참조**</p>
<p>  <img src="https://velog.velcdn.com/images/junr_65535/post/ec522769-ee4c-499d-bb6e-acbd98961ca8/image.png" alt=""></p>
<p><strong>HSMS 헤더의 B 영역 데이터 구조</strong> </p>
<p>  <img src="https://velog.velcdn.com/images/junr_65535/post/061390e0-2ef4-44f8-8f38-0aaf8be6f847/image.png" alt=""></p>
<p><a href="https://idlecomputer.tistory.com/56">HSMS 프로토콜 이란</a></p>
</aside>

<h2 id="2-secs-프로토콜의-data-형식과-구조">2. SECS 프로토콜의 Data 형식과 구조</h2>
<p>SECS 프로토콜에서 통신되는 데이터들은 일반적으로 다음과 같은 구조를 가진다.</p>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/694d982d-0233-41d3-9016-50dc0d4bbfca/image.png" alt=""></p>
<h3 id="1-block-의-구조">1. Block 의 구조</h3>
<ul>
<li><p>SECS -I 의 경우 Block 구조</p>
<p>  <img src="https://velog.velcdn.com/images/junr_65535/post/66de6916-8a9a-4457-9401-b6b2d53cf8b2/image.png" alt=""></p>
<p>  <img src="https://velog.velcdn.com/images/junr_65535/post/e03bacdc-b651-4142-82d1-91f8c2138979/image.png" alt=""></p>
</li>
</ul>
<p>   이전글 참고 : <a href="https://velog.io/@junr_65535/SECS-I-%EB%B6%80%ED%84%B0-HSMS-%EA%B9%8C%EC%A7%80-01-SECS-I-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC">https://velog.io/@junr_65535/SECS-I-%EB%B6%80%ED%84%B0-HSMS-%EA%B9%8C%EC%A7%80-01-SECS-I-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC</a></p>
<p>   <code>checksum</code> : RS-232 시리얼 통신 위에서 돌아가기 때문에 Checksum 으로 Header와 Data 영역의 바이트 값을 이진 덧셈처리하여 CheckSum을 통해 오류 검출한다.</p>
<ul>
<li>HSMS의 경우 Block 의 구조는 아래와 같다.</li>
</ul>
<pre><code>![](https://velog.velcdn.com/images/junr_65535/post/5fe0ff2c-5c74-49db-af68-1fe42701648a/image.png)

HSMS 의 경우 TCP/IP 위에서 돌아가기 때문에 (TCP가 체크섬을 통해 무결성을 체크해주잖슴~ 참고: [6~9](https://www.notion.so/6-9-1a394635e71680038907f3d259b3734a?pvs=21) ) CheckSum 을 가지지 않는다. </code></pre><h3 id="2-data의-구조">2. Data의 구조</h3>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/73c0c224-4ecc-4180-9289-39e8d1645dd0/image.png" alt=""></p>
<ul>
<li>Data는 Header에 정의된 Stream과 Function 정보에 의해 정의되며 여러개의 List와 Item이 트리구조로 구성되어있다.</li>
<li>다만 SECS-II Msg는 항상 Data 영역을 포함하고 있진 않다. Header 의 스트림과 펑션으로만 정의되는 예약된 메시지들이 존재하기 때문이다.</li>
</ul>
<aside>
💡

<p>정리하자면</p>
<p>SECS-II는 Length + Header + Data (SECS-I를 이용할경우 + Checksum 추가)로 구성되어있으며</p>
<p>Length (HSMS - 4byte, SECS-I - 1byte)와 Header (10 byte)는 고정이고</p>
<p>Data영역은 가변적으로 구성되어있다.</p>
</aside>

<h3 id="3-list와-item-의-구조">3. List와 Item 의 구조</h3>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/19c1e115-b474-4560-8af3-e2ea2ceda91a/image.png" alt=""></p>
<ul>
<li><p>LIST</p>
<ul>
<li><p>다른 ITEM을 포함하는 컨테이너 역할</p>
</li>
<li><p>어떤 값을 가지는게 아니라 다른 ITEM들을 담는 그릇이라고 생각하면 됨</p>
</li>
<li><p>LIST 안에는 또 다른 LIST를 넣을 수 있다. (재귀적으로 트리 구조 형성 가능)</p>
</li>
<li><p>예시</p>
<pre><code>// list 안에 두개의 아이템이 들어있음.
&lt;L [2]
&lt;U1 1&gt;   // = 부호 없는 1바이트 정수 1
&lt;A &quot;OK&quot;&gt; // = ASCII 문자열 OK
&gt;</code></pre></li>
</ul>
</li>
<li><p>ITEM</p>
<ul>
<li><p>실제 데이터를 가지는 최소단위</p>
</li>
<li><p>항상 <strong>&quot;형식(Type)&quot; + &quot;길이(Length)&quot; + &quot;값(Value)&quot;</strong> 구조로 구성되어 있음</p>
</li>
<li><p>데이터 구조</p>
<p>  <img src="https://velog.velcdn.com/images/junr_65535/post/727dae95-c6f2-455d-961c-a54487853a18/image.png" alt=""></p>
</li>
</ul>
</li>
</ul>
<pre><code>    - #of Length Bytes : Length Bytes의 개수 (1 - 3)
    - Item Format Code : Item의 종류
    - Length Bytes :
    1. List : Sub Item의 개수
    2. Item : Item Body의 바이트 수
        - 1 Byte = ~ 255 Bytes
        - 2 Byte = ~ 65,536 Bytes
        - 3 Byte = ~ 16,777,215 Bytes</code></pre><h2 id="3--data-item-dictionary">3.  Data Item Dictionary</h2>
<blockquote>
<p>SECS-II에서 사용되는 데이터 구조(Item)의 이름, 형식, 의미 등을 정의한 공식 문서 또는 표준 사전</p>
</blockquote>
<p>SECS-II 메시지(SxFy)는 <code>&lt;L[2] &lt;U1 3&gt; &lt;A &quot;READY&quot;&gt;&gt;</code>와 같은 데이터 구조만으로는 그 의미를 파악하기 어려움.</p>
<p>따라서 <strong>&quot;S6F11의 첫 번째 필드는 CEID이고, 두 번째는 Report List이다&quot;</strong>와 같이 각 필드의 의미를 표준화하여 문서화한 것이 <strong>Data Item Dictionary이다...</strong></p>
<aside>
💡

<p>해당 부분은 실제 문서를 봐야지 도움이 될듯..</p>
</aside>

<p>아래와 같이 정의되어있다고 한다.</p>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/411d8dbf-fa55-4062-8a9d-494697b98f33/image.png" alt=""></p>
<ul>
<li>필드명 설명<ul>
<li><code>Name</code>: CEID, RPTID, VID 등 데이터 item의 이름</li>
<li><code>Format</code>: SECS-II 포맷 A, U1, I2, F4, L 등 데이터 형식</li>
<li><code>Description</code>: 항목의 의미 (예: Collection Event ID)</li>
<li><code>Optional/Required</code>: 해당 필드가 필수인지 여부</li>
<li><code>Where Used</code>: 이 항목이 포함되는 메시지(SxFy) 목록</li>
<li><code>Constraints</code>: 길이, 값의 범위 등 제약 조건</li>
</ul>
</li>
</ul>
<h1 id="느낀점">느낀점.</h1>
<p>SECS - I 과 SECS - II 가 진짜 밀접하게 설계되어있다고 느꼈다.. HSMS 개발할때 호환성 맞추기 위해 고생 꽤나 했을듯</p>
<ul>
<li><p>더 공부할점</p>
<ol>
<li><p>SECS-II의 스트림(S)과 펑션(F) 조합에 대한 상세 학습</p>
<ul>
<li>각 SxFy 메시지의 구체적인 용도와 의미</li>
<li>일반적으로 많이 사용되는 메시지 패턴</li>
</ul>
</li>
<li><p>Data Item Dictionary 심화 학습</p>
<ul>
<li>CEID, RPTID, VID 등 주요 데이터 아이템의 상세 의미와 용례</li>
</ul>
<p>GEM공부를 하면 아마 1번은 실제적인 용례들과 함께 공부하게 될듯 싶다.</p>
</li>
</ol>
</li>
</ul>
<h2 id="참고">참고.</h2>
<p><a href="https://www.cimetrix.com/blog/secs-gem-series-protocol-layer">https://www.cimetrix.com/blog/secs-gem-series-protocol-layer</a></p>
<p><a href="https://m.blog.naver.com/posionsnake/222147003877">https://m.blog.naver.com/posionsnake/222147003877</a></p>
<p><a href="https://developer-kus.tistory.com/22">https://developer-kus.tistory.com/22</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SECS - I 부터 HSMS 까지 01 (SECS-I 프로토콜에 관하여)]]></title>
            <link>https://velog.io/@junr_65535/SECS-I-%EB%B6%80%ED%84%B0-HSMS-%EA%B9%8C%EC%A7%80-01-SECS-I-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC</link>
            <guid>https://velog.io/@junr_65535/SECS-I-%EB%B6%80%ED%84%B0-HSMS-%EA%B9%8C%EC%A7%80-01-SECS-I-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC</guid>
            <pubDate>Fri, 11 Apr 2025 00:37:47 GMT</pubDate>
            <description><![CDATA[<aside>
💡

<p>CIM 과 AMHS 서비스 사이에서 HSMS 프로토콜로 통신이 이루어진다는데 해당 프로토콜에 대해 공부하면서 알게 된 점을 정리한 페이지입니다.</p>
</aside>

<p>반도체 공장 자동화를 위해 <code>SEMI</code>에서는 다양한 국제 표준과 가이드라인을 제정했다.</p>
<p>반도체 공정에는 여러 종류의 장비와 호스트 간의 통신이 필요하다. 이러한 장비들 간의 원활한 통신을 위해 SEMI는 국제 표준 통신 규약을 수립했다.</p>
<blockquote>
<p>“SEMI”는 반도체, 광전지 (PV), 전자 및 기타 첨단 기술 산업 등의 제조 공급망을 지원하는 글로벌 산업 협회</p>
</blockquote>
<p>그중 SEMI 가 만든 표준 프로토콜은 다음과 같은 종류가 있다.</p>
<p><code>SEMI E4</code> : 호스트와 설비의 통신을 주고받기 위해서 RS-232로 이루어지는 시리얼 통신 위에서 동작하는 프로토콜인 <code>SECS-I</code> 에 대한 표준</p>
<p><code>SEMI E5</code> : 실제 송수신되는 메시지의 통신 내용(Content), Data 영역에 대한 프로토콜인 <code>SECS-II</code> 에 대한 표준</p>
<p><code>SEMI E37</code> :  TCP/IP 통신 위에서 이루어지는 통신 프로토콜인 <code>HSMS(High Speed SECS)</code> 에 대한 표준</p>
<p>이러한 SECS 표준은 SECS-I, SECS-II, GEM, HSMS, EDA 순으로 발전해왔음.</p>
<h2 id="secs-i-semi-e4">SECS-I (SEMI E4)</h2>
<p>RS-232C 라는 시리얼 통신 물리 계층 매체 위에서 송수신하는 규약을 정의</p>
<ul>
<li>특징<ul>
<li>1 Start Bit(0) - 8Data bits - 1 Stop Bit (1) 구성</li>
<li>비동기 / 양방향 통신</li>
<li>Block을 통한 데이터 전송<ul>
<li>1 Block = Header(10bytes) + Data (1block은 최대 254 바이트)</li>
<li>1 Message 는 1~32,767 Block 으로 구성 (1 Msg는 최대 7,995,148 바이트의 Data로 구성)</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="transaction-과정">Transaction 과정</h3>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/cba20ff5-c50c-48f0-9fa1-bf7aa6ad5351/image.png" alt=""></p>
<p> SECS-I 프로토콜은 시리얼 통신 위에서 데이터를 주고받기 위해 handshake 과정을 주고받습니다.</p>
<table>
<thead>
<tr>
<th><strong>Control char</strong></th>
<th><strong>Hex Value</strong></th>
<th><strong>Meaning</strong></th>
</tr>
</thead>
<tbody><tr>
<td>ENQ</td>
<td>05</td>
<td>LINE BID</td>
</tr>
<tr>
<td>EOT</td>
<td>04</td>
<td>ACCEPT BID</td>
</tr>
<tr>
<td>ACK</td>
<td>06</td>
<td>POSITIVE ACKNOWLEDGE</td>
</tr>
<tr>
<td>NAK</td>
<td>15</td>
<td>NEGATIVE ACKNOWLEDGE</td>
</tr>
<tr>
<td>1. 먼저 Host와 설비 간 통신을 시작하기 위해 송신측은 <code>ENQ</code> 신호를 수신측에 전송합니다.</td>
<td></td>
<td></td>
</tr>
<tr>
<td>2. 수신측은 ENQ 신호를 받으면 정상 수신을 알리기 위해 <code>EOT</code> 신호를 송신측에 전송합니다.</td>
<td></td>
<td></td>
</tr>
<tr>
<td>3. EOT 신호를 받은 송신측은 본격적으로 <code>데이터 전송</code>을 시작합니다.</td>
<td></td>
<td></td>
</tr>
<tr>
<td>4. 수신부는 받은 데이터의 CheckSum을 확인한 후, 정상적으로 수신했다면 <code>ACK</code>를, 비정상적으로 수신했다면 <code>NAK</code>를 회신합니다.</td>
<td></td>
<td></td>
</tr>
</tbody></table>
<p>이러한 단계의 통신 과정을 BLOCK 이라고 부릅니다. </p>
<p>만약 Message가 길어서 다중 Block 으로 전송하게 될 경우 해당 과정을 반복하여 데이터를 보내게 됩니다.</p>
<h3 id="block-의-구조">Block 의 구조</h3>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/0eacbe53-ce21-41c1-b9fd-b9f7b4eb1274/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/fe5e7823-32a5-4e93-afc3-e03e46180684/image.png" alt=""></p>
<ul>
<li>1 Block = Header (10Byte) + Data (Max 254Byte)</li>
</ul>
<h3 id="header-의-구조">Header 의 구조</h3>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/e49337d8-c004-4a46-8eb8-1908b7369597/image.png" alt=""></p>
<ul>
<li>만약 메시지의 길이가 길어서 다중 블록으로 구성된다면 해당 헤더의 <code>Block No</code> Byte 에 각 블록에 대한 정보가 포함된다.</li>
</ul>
<h2 id="참고">참고</h2>
<ol>
<li><a href="http://hume.com/secs/">SECS Message Code Generation Tool</a> - SECS 메시지 코드 생성 도구</li>
<li><a href="https://program-developers-story.tistory.com/176">[SescGem] 반도체 통신 프로토콜 SEMI E5 (SECS-I, SECS-II, HSMS)</a> - SEMI 표준과 프로토콜에 대한 상세 설명</li>
<li><a href="https://developer-kus.tistory.com/20">SECS #2 - SECS-I(SEMI E4)</a> - SECS-I 프로토콜의 정의와 특징</li>
<li><a href="https://920416.tistory.com/95">[SECS] Semiconductor Equipment Communication Standard -Ⅰ(SECS-Ⅰ)</a> - RS-232를 통한 통신 방법</li>
<li><a href="https://idlecomputer.tistory.com/54">secs-I 프로토콜 이란?</a> - SECS-I 프로토콜의 기본 개념</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[OSI 7계층]]></title>
            <link>https://velog.io/@junr_65535/OSI-7%EA%B3%84%EC%B8%B5</link>
            <guid>https://velog.io/@junr_65535/OSI-7%EA%B3%84%EC%B8%B5</guid>
            <pubDate>Wed, 05 Mar 2025 00:37:13 GMT</pubDate>
            <description><![CDATA[<h1 id="osi-7계층과-주요-개념">OSI 7계층과 주요 개념</h1>
<h2 id="osi-7계층에-대해-설명해-주세요">OSI 7계층에 대해 설명해 주세요.</h2>
<p>OSI 7계층은 ISO에서 표준으로 정의된 네트워크 <strong>참조</strong> 모델임.</p>
<h3 id="왜-osi-7계층을-사용해야-할까">왜 OSI 7계층을 사용해야 할까?</h3>
<ol>
<li><p><strong>네트워크의 구성과 설계가 용이</strong></p>
<ul>
<li>각 계층의 목적에 맞게 프로토콜과 장비를 설계할 수 있음.</li>
</ul>
</li>
<li><p><strong>효율적인 문제점 파악과 해결</strong></p>
<ul>
<li>다른 시스템 간에 계층별 원인을 빠르게 파악할 수 있음. 또한 호환성 체크도 가능함.</li>
</ul>
</li>
</ol>
<p>💡 <strong>OSI 7계층 정리</strong></p>
<ol>
<li><strong>물리 계층</strong> : 1,0으로 표현되는 비트 신호를 주고받는 계층</li>
<li><strong>데이터 링크 계층</strong> : 네트워크 내 주변 장치 간의 정보를 올바르게 주고받기 위한 계층</li>
<li><strong>네트워크 계층</strong> : 메시지를 다른 네트워크에 속한 수신지까지 전달하는 계층</li>
<li><strong>전송 계층</strong> : 신뢰성 있고 안정성 있는 전송을 해야 할 때 필요한 계층</li>
<li><strong>세션 계층</strong> : 세션(통신을 주고받는 호스트의 응용 프로그램 간 연결 상태)을 관리</li>
<li><strong>표현 계층</strong> : 문자를 컴퓨터가 이해할 수 있는 코드로 변환하거나 압축, 암호화</li>
<li><strong>응용 계층</strong> : 사용자 및 사용자가 이용하는 응용 프로그램에 다양한 네트워크 서비스</li>
</ol>
<hr>
<h2 id="transport-layer와-network-layer의-차이">Transport Layer와 Network Layer의 차이</h2>
<h3 id="네트워크-계층network-layer">네트워크 계층(Network Layer)</h3>
<ul>
<li>데이터를 다른 네트워크에 전달하는 역할을 함.</li>
<li>주로 IP 주소를 사용하며, 데이터를 목적지까지 전달하는 <strong>경로 설정</strong>에 중점을 둠.</li>
</ul>
<h3 id="전송-계층transport-layer">전송 계층(Transport Layer)</h3>
<ul>
<li>데이터를 송신지와 수신지 간에 신뢰성 있게 전달하는 역할을 함.</li>
<li>데이터 전송의 <strong>신뢰성 보장</strong>과 <strong>오류 제어, 흐름 제어</strong> 등의 기능을 제공함.</li>
</ul>
<p>🧠 <strong>요약</strong></p>
<ul>
<li>네트워크 계층: 데이터 <strong>경로 설정</strong> 담당</li>
<li>전송 계층: 데이터 <strong>전송의 신뢰성</strong> 보장</li>
</ul>
<hr>
<h3 id="💡-더-알아보기-💡">💡 더 알아보기 💡</h3>
<h4 id="1-네트워크-계층">1. 네트워크 계층</h4>
<p>LAN에 한정된 물리, 데이터링크 계층의 통신 범위의 한계를 해결하기 위해 사용하는 계층.</p>
<ul>
<li><p>왜 물리, 데이터링크 계층이 LAN을 넘어서 통신하기 어려울까?</p>
<ol>
<li><p>다른 네트워크까지의 도달 경로를 파악하기 어려움
   → 네트워크 계층이 있어야 네트워크 간의 <strong>라우팅</strong>이 가능하다.</p>
<ul>
<li><p>라우팅 : 패킷이 이동할 최적의 경로를 결정하는 것.</p>
</li>
<li><p>라우터 : 라우팅을 수행하는 대표적인 장비</p>
<ol start="2">
<li>MAC 주소만으로는 모든 네트워크에 속한 모든 호스트의 위치를 특정하기 어려움</li>
</ol>
<p>MAC 주소와 IP 주소는 함께 사용되고, 기본적으로 IP 주소를 우선 활용
<img src="https://velog.velcdn.com/images/junr_65535/post/16d26f3b-9816-4944-8449-18d0e264c005/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/31d0f46b-668c-4faf-b035-2840b0097696/image.png" alt=""></p>
</li>
</ul>
</li>
</ol>
</li>
</ul>
<pre><code>    IGP : AS 내부에서 수행 RIP, OSPF

    - 거리벡터 활용 RIP, 즉 홉의 수가 가장 적은 경로를 최적의 경로라고 판단.
        - 홉 수가 적을수록 라우팅 테이블 상의 메트릭 값도 적어짐
        - 주기적으로 인접 라우터끼리 경로 정보 교환 및 라우팅 테이블 갱신, 특정 수신지까지 홉 수 계산
    - 링크 상태

    EGP : AS 외부에서 수행 </code></pre><h3 id="2-전송계층">2. 전송계층</h3>
<ul>
<li>네트워크 계층과 표현-응용계층 사이에 존재.</li>
</ul>
<ol>
<li><p>IP 한계 보완: 신뢰할 수 있는 통신과 연결형 통신 기능 제공</p>
<ul>
<li><p>IP의 특징</p>
<ul>
<li><p>신뢰할 수 없는 프로토콜</p>
<ul>
<li>패킷이 수신지까지 제대로 전송되었다는 보장을 하지 않음</li>
<li>통신 과정에서 패킷이 잘못 전송되어도 이를 확인하지 않고 재전송도 하지 않으며 순서대로 패킷이 도착할 것이라는 보장도 하지 않는다.</li>
</ul>
</li>
<li><p>비연결형 프로토콜</p>
<ul>
<li>송수신 호스트간에 사전 연결 수립 작업을 거치지 않음.</li>
<li>그저 수신지를 향해 패킷을 쏘기만 할 뿐.</li>
</ul>
<p>그렇다면 IP는 왜 이러한 신뢰할 수 없는 비 연결형 통신을 할까?</p>
</li>
<li><p>주된 이유는 신뢰할 수 있는 연결형 통신이 성능에 악영향을 끼치기 때문이다.</p>
</li>
<li><p>신뢰성 있는 전송이 모든 경우에 필요한 것은 아니다.</p>
</li>
</ul>
<h3 id="tcp">TCP</h3>
</li>
<li><p>연결형 통신을 가능하게 함</p>
<p>  송수신 동안에는 연결을 유지하고 송수신이 끝나면 연결을 종료</p>
</li>
<li><p>신뢰성 있는 통신을 가능하게 함.</p>
<ul>
<li>재전송을 통한 오류 제어, 흐름제어, 혼잡 제어 등 다양한 기능 활용</li>
</ul>
<h3 id="udp">UDP</h3>
</li>
<li><p>신뢰할 수 없는 통신, 비연결형 통신을 가능하게 함.</p>
</li>
<li><p>TCP보다 비교적 빠른 전송이 가능</p>
</li>
</ul>
</li>
<li><p>응용 계층의 프로세스(실행중인 프로그램) 식별: 포트 번호 활용</p>
<ul>
<li><p>포트 : 네트워크 상의 애플리케이션 식별 정보</p>
<p>  <img src="https://velog.velcdn.com/images/junr_65535/post/ccac6930-627c-4321-829c-961fae0eafb8/image.png" alt=""></p>
<ul>
<li><p>well-known port</p>
<p>  0~1023번까지의 포트</p>
<p>  시스템 포트, 범용적으로 사용되는 애플리케이션 프로토콜이 일반적으로 사용되는 포트 번호</p>
</li>
<li><p>registered port</p>
<p>  1024~49151번까지의 포트</p>
<p>  잘 알려진 포트에 비해서는 덜 범용적</p>
<p>  흔히 사용하는 애플리케이션 프로토콜에 할당하기 위해 사용</p>
</li>
<li><p>dynamic port</p>
<p>  특별히 관리되지 않은 포트 번호 범위: 자유롭게 사용 가능</p>
<p>  서버는 대부분 잘 알려진 포트와 등록된 포트 사용</p>
<p>  클라이언트(웹브라우저)는 대부분 동적 포트 사용</p>
<p>  <img src="https://velog.velcdn.com/images/junr_65535/post/2e3a942b-adf0-4560-8210-7f8d3bd3aeab/image.png" alt=""></p>
</li>
</ul>
</li>
</ul>
</li>
</ol>
<p><em>(출처: 강민철, &quot;혼자서 공부하는 네트워크&quot;)</em></p>
<hr>
<h2 id="l3-switch와-router의-차이">L3 Switch와 Router의 차이</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th>L3 스위치</th>
<th>라우터</th>
</tr>
</thead>
<tbody><tr>
<td>주요 역할</td>
<td>동일 네트워크 내 스위칭 + IP 기반 라우팅</td>
<td>서로 다른 네트워크 간 라우팅</td>
</tr>
<tr>
<td>처리 방식</td>
<td>하드웨어(ASIC) 기반, 속도가 빠름</td>
<td>소프트웨어 기반, 다양한 기능 지원</td>
</tr>
<tr>
<td>사용 상황</td>
<td>속도와 비용이 중요한 내부 네트워크</td>
<td>복잡한 라우팅이 필요한 대규모 네트워크</td>
</tr>
</tbody></table>
<p>🧠 <strong>정리</strong></p>
<ul>
<li><strong>L3 스위치</strong>: 빠른 성능과 비용 효율적, 내부 네트워크 최적</li>
<li><strong>라우터</strong>: 다양한 프로토콜과 안정성, 보안 기능 제공</li>
</ul>
<h4 id="참고">참고</h4>
<p><a href="https://www.youtube.com/watch?v=KA4-z6mBQxI&amp;ab_channel=ITBEST-PATH">OSI 7계층 개념 설명 (IT BEST-PATH)</a></p>
<hr>
<h2 id="각-layer의-데이터-명칭">각 Layer의 데이터 명칭</h2>
<table>
<thead>
<tr>
<th>계층</th>
<th>데이터 단위</th>
</tr>
</thead>
<tbody><tr>
<td>응용 계층, 표현 계층, 세션 계층</td>
<td>데이터(Data)</td>
</tr>
<tr>
<td>전송 계층</td>
<td>세그먼트(Segment), 데이터그램(Datagram)</td>
</tr>
<tr>
<td>네트워크 계층</td>
<td>패킷(Packet)</td>
</tr>
<tr>
<td>데이터 링크 계층</td>
<td>프레임(Frame)</td>
</tr>
<tr>
<td>물리 계층</td>
<td>비트(Bit)</td>
</tr>
</tbody></table>
<p>🧠 <strong>패킷 이름을 달리하는 이유</strong></p>
<ul>
<li>각 계층에서 처리하는 데이터의 단위가 다르기 때문임.</li>
<li>계층별로 데이터의 캡슐화(Encapsulation)와 역캡슐화(Decapsulation)를 거침.</li>
</ul>
<hr>
<h2 id="각각의-header의-packing-order에-대해-설명해-주세요">각각의 Header의 Packing Order에 대해 설명해 주세요.</h2>
<p><img src="blob:https://velog.io/c5444d7d-82de-476a-9a4d-e3647fdcb9b9" alt="업로드중..">
데이터가 송신될 때는 다음과 같은 순서로 헤더가 추가됩니다:</p>
<ol>
<li>응용/표현/세션 계층: 원본 데이터 생성</li>
<li>전송 계층: TCP/UDP 헤더 추가 (세그먼트/데이터그램 생성)</li>
<li>네트워크 계층: IP 헤더 추가 (패킷 생성)</li>
<li>데이터 링크 계층: 이더넷 헤더와 트레일러 추가 (프레임 생성)</li>
<li>물리 계층: 비트 단위로 변환하여 전송</li>
</ol>
<p>수신측에서는 이 과정이 역순으로 진행되어 각 계층에서 해당 헤더를 제거하며 데이터를 처리합니다. 이런 과정을 캡슐화(encapsulation)와 역캡슐화(decapsulation)라고 합니다.</p>
<h4 id="데이터-링크-계층은-헤더--페이로드-정보가-추가됨">데이터 링크 계층은 헤더 + 페이로드 정보가 추가됨!</h4>
<p><img src="attachment:e813d19b-01af-4533-98d1-655dfcb92aed:image.png" alt="image.png"></p>
<p>   대표적인 이더넷 네트워크에서 주고받는 프레임의 헤더와 패킹 오더를 설명하자면</p>
<p>   상위 계층의 캡슐화를 거쳐 송신된다.</p>
<ul>
<li><p>헤더 - 프리앰블, 수신지 MAC 주소, 송신지 MAC 주소, 타입/길이</p>
<ul>
<li><p><code>프리앰블</code> : 이더넷 프레임의 시작을 알리는 8 바이트 (64비트) 정보</p>
<ul>
<li><p>첫 7바이트는 10101010 값을 가지고 마지막 바이트는 10101011 값을 가짐</p>
<ul>
<li><p><code>송수신지 간의 동기화</code></p>
</li>
<li><p><code>MAC 주소</code></p>
</li>
<li><p>물리적 주소, 일반적으로 고유하고 변경되지 않음</p>
</li>
<li><p>네트워크 인터페이스마다 부여되는 6바이트 길이의 주소</p>
<ul>
<li>LAN내의 송수신지 특정</li>
<li>일반적으로 NIC 장치가 네트워크 인터페이스를 담당</li>
<li>한 컴퓨터에 MAC 주소 여러개 가능</li>
</ul>
</li>
<li><p><code>타입/길이</code></p>
</li>
<li><p>필드에 명시된 크기가 1500 이하일 경우 - 이 필드는 프레임의 <code>크기</code></p>
</li>
<li><p>필드에 명시된 크기가 1536 이상일 경우 이 필드는 <code>타입</code></p>
 <aside>
 💡

<p> 타입이란?</p>
<p> <img src="attachment:7f98caae-b01a-470e-add5-b8a9267f8d4f:image.png" alt="image.png"></p>
<ul>
<li>이더타입(ethertype)이라고도 함</li>
<li>어떤 정보를 캡슐화했는지 나타내는 정보</li>
<li>대표적으로 상위 계층에서 사용된 프로토콜이 명시</aside>

</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<ul>
<li><p>페이로드 - 데이터</p>
<ul>
<li>상위 계층에서 전달받거나 전달해야 할 내용</li>
<li>최대 크기 1500byte, 최소 크기 46 byte</li>
<li>46 byte 보다 작다면 크기 맞추기용 padding 이 채워짐.</li>
</ul>
</li>
<li><p>트레일러 - FCS(Frame Check Sequence)</p>
<p> <img src="attachment:f9646d1d-2b9b-432d-bdb2-c2c5c2edec8e:image.png" alt="image.png"></p>
<ul>
<li>오류 검출용 정보 : 수신한 이더넷 프레임에 오류가 있는지 체크하는 필드</li>
<li>CRC라는 오류 검출용 값이 명시됨</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="arpaddress-resolution-protocol에-대해-설명해-주세요">ARP(Address Resolution Protocol)에 대해 설명해 주세요.</h2>
<ul>
<li>IP 주소를 통해 MAC 주소를 알아내는 프로토콜임.</li>
<li>동일 네트워크 내의 송수신 대상의 IP 주소를 통해 MAC 주소를 알아낼 수 있음.</li>
</ul>
<p>🧠 <strong>동작 과정</strong></p>
<ol>
<li><strong>ARP 요청</strong>: 브로드캐스트 메시지를 통해 MAC 주소를 요청함.</li>
<li><strong>ARP 응답</strong>: 해당 호스트가 자신의 MAC 주소를 담아 응답함.</li>
<li><strong>ARP 테이블 갱신</strong>: ARP 테이블에 IP와 MAC 주소를 매핑하여 저장함.</li>
</ol>
<ul>
<li>다른 네트워크의 MAC 주소를 모를 경우, 각 네트워크별로 ARP가 수행됨.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Notion Innovators Summit 2024 후기]]></title>
            <link>https://velog.io/@junr_65535/Notion-Innovators-Summit-2024-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@junr_65535/Notion-Innovators-Summit-2024-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Sat, 07 Dec 2024 15:21:14 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/junr_65535/post/f61b2bb8-5787-46da-b7f9-a237b11861ae/image.png" alt=""></p>
<p>한국에서 진행되는 노션의 가장 큰 행사중 하나인 <strong><code>Notion Innovators Summit 2024</code></strong> 에 다녀왔다.
행사는 경희대학교에서 진행되었으며 이번 회차의 주제는 비즈니스, 스타트업, 교육 이었다.
<img src="https://velog.velcdn.com/images/junr_65535/post/f287070a-9dca-47bb-8994-5e4efb118383/image.png" alt="">
행사는 12월 7일 10시부터 18시까지 진행되었으며 주제가 비즈니스, 스타트업, 교육인만큼 여러 도메인이 계신 분들의 사례 공유 및 템플릿들을 볼 수 있었다.</p>
<h2 id="행사-참여-전">행사 참여 전.</h2>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/873c4a37-85ea-4073-bd7d-a6762eaee86b/image.png" alt=""></p>
<p>나같은 경우 위와 같이 기존의 노션 페이지 관리에서 <strong>PARA 기법</strong>에 따라 기록을 나누고 관리하려고 준비중인데 다른 사람들은 개발자나 PM으로서 이런 자신의 대쉬보드들을 어떻게 관리하는지 알아갈 수 있으면 좋겠다고 생각했다.</p>
<h1 id="행사-세션별-후기">행사 세션별 후기</h1>
<h2 id="각-세션별-느낀-점">각 세션별 느낀 점</h2>
<h3 id="1-교직에서의-notion-활용법-원정민"><strong>1. 교직에서의 Notion 활용법 (원정민)</strong></h3>
<ul>
<li>초등 교사라는 도메인에 맞춘 노션 활용이 현실적이고 실용적이었다.  </li>
<li>픽셀아트나 기행문 작성 같은 활동으로 학생들과 협업하는 방식이 인상적.  </li>
<li>노션 캘린더의 UI 한계점을 보며 개선 아이디어도 떠올랐다.  </li>
</ul>
<h3 id="2-notion-고객-성공-manager가-톺아주는-notion"><strong>2. Notion 고객 성공 Manager가 톺아주는 Notion</strong></h3>
<ul>
<li>노션의 새로운 기능(폼, 메일)과 향후 계획(JIRA 연동, 팀즈 지원)을 들을 수 있었다.  </li>
<li>커뮤니티 기반 성장의 중요성을 다시 느꼈다.  </li>
<li>B2B 영역에서 노션의 영향력과 활용도가 크게 확장될 가능성이 보였다.  </li>
</ul>
<h3 id="3-batiai를-활용한-notion-자동화-류장현-대표님"><strong>3. bati.ai를 활용한 Notion 자동화 (류장현 대표님)</strong></h3>
<ul>
<li>카카오톡, 메일, 캘린더와 실시간 연동되는 자동화 솔루션은 활용도가 높아 보였다.  </li>
<li>맞춤형 메시지 발송같은 기능들이 충분히 활용하기 쉽다면 부모님과 함께 했던 커튼 사업에 적용하기 좋아보인다.</li>
<li>자신의 회사 서비스가 Notion 기반으로 사용되고 있고 다른 도메인 지식이 없다면 간편하게 도입하기 좋은 서비스일 것 같다.</li>
<li>개인 프로젝트에서 아이디어를 도입할 가능성을 생각해봤다.  </li>
</ul>
<h3 id="4-노션으로-나다운-삶을-향해-나아가는-법-기록가-연"><strong>4. 노션으로 나다운 삶을 향해 나아가는 법 (기록가 연)</strong></h3>
<ul>
<li>노션을 활용해 자기 개발과 성장의 흔적을 남길 수 있다는 점이 인상적이었다.  </li>
<li>나만의 스토리를 기록하며 동기부여 도구로 활용 가능.  </li>
<li>실용적인 템플릿 제작 아이디어를 얻었다.  </li>
</ul>
<h3 id="5-노션을-활용한-교실-임세범"><strong>5. 노션을 활용한 교실 (임세범)</strong></h3>
<ul>
<li>노션에서 mermaid를 활용한 도식화 기능은 처음알았다.</li>
<li>확실히 교사들의 Notion 커뮤니티가 잘 활성화 되어있는 것 같다.<ul>
<li>노션은 컴퓨터와 가까이에서 일하는 사람일수록 더욱 잘 활용할 수 있게 된다 는 생각이 맞는듯.</li>
</ul>
</li>
</ul>
<h3 id="6-notion으로-personal-ai-persistant-만들어보기"><strong>6. Notion으로 Personal AI persistant 만들어보기</strong></h3>
<ul>
<li>비구조화된 데이터를 활용한 personal AI 구성 아이디어가 흥미로웠다.  </li>
<li>임베딩과 데이터 적재의 중요성을 깨달았다. </li>
<li>또한 Storm Parse 툴은 꽤나 유용하다.
흠. Notion의 마크다운 추출을 사용해서 임베딩하고 해당 임베딩한 툴을 Storm Parse AI 를 통해서 이미지와 함께 프롬프팅 해두고 이를 RAG 체인의 한 파트로 채용시킬 수 있다.<ul>
<li>하지만 Notion AI 가 사용자의 Personal AI Asistant 지향하고 점점 발전중인데  일반인이 이걸 구현시키는 노력보다 큰 아웃풋이 있을지 궁금하다.</li>
</ul>
</li>
<li>그래도 Storm Parse 사이트를 알게된건 아주 좋다.  </li>
</ul>
<h3 id="7-사용자-경험을-고려한-노션-템플릿-제작-프로세스-노션-너굴"><strong>7. 사용자 경험을 고려한 노션 템플릿 제작 프로세스 (노션 너굴)</strong></h3>
<ul>
<li>더블 다이아몬드 프로세스를 노션 템플릿 제작에 적용한 방식이 신선했다.  </li>
<li>스터디를 진행하면서 여러 템플릿을 커스텀하고 만들었던 경험이 있는데 해당 프로세스를 사용하면 어떨까?</li>
<li>&quot;템플릿 하단에 원본 데이터베이스를 삽입&quot; 한다는 것이 인상적이었다.</li>
</ul>
<h3 id="8-notion-ai로-남들보다-1시간-먼저-퇴근하는-비법"><strong>8. Notion AI로 남들보다 1시간 먼저 퇴근하는 비법</strong></h3>
<ul>
<li><p>노션은 DB로 AI, 자동화(Make 와 같은 툴) 세가지가 삼위일체로 있어야 자동화이다.</p>
</li>
<li><p>회사 일이나 여러 업무에서 아이젠아워 메트릭스의 C 항목들을 이 삼위일체로 해결할 수 있다면?</p>
  <img src="https://velog.velcdn.com/images/junr_65535/post/5097eb84-0f3e-4c51-8459-5b953e2144b6/image.png" alt="image" width="200">
</li>
<li><p>부모님이 커튼일에 대한 프로세스를 생각했을 때에 해당 부분을 적용해서 최적화할 수 있을듯.</p>
</li>
<li><p>흠. 과연 내가 내년에 이러한 세션을 맡게됐을 때에 인원을 파악하고 레벨을 파악해서 강의를 준비하고 배우고 싶은 점을 분석하는 과정을 꼭 거쳐야겠다.</p>
</li>
<li><p>아니 그런데 이러한 Make 자동화를 만드는 과정이 사실 가장 어렵고 반복적인 일 아닌가?</p>
<ul>
<li>이런 자동화 툴을 구성하는 것을 자동화하는 건 안되나? 흠….</li>
</ul>
</li>
</ul>
<h3 id="9-notion으로-사이드-프로젝트-런칭하기-노션-남매의-헌트님"><strong>9. Notion으로 사이드 프로젝트 런칭하기 (노션 남매의 헌트님)</strong></h3>
<ul>
<li>개발 중심이 아닌, 콘텐츠의 힘으로 브랜드와 비즈니스를 만들어가는 사이드 프로젝트들을 볼 수 있었다.</li>
<li>노션 활용에는 본인의 이해도뿐만 아니라 팀원들의 이해도도 중요하다는 것을 깨달았다.</li>
<li>앞으로 기업들에게 노션을 비롯한 협업 도구에 대한 이해도를 높이는 것이 중요할 것 같다.</li>
<li>블로깅을 하면서 단순한 정보 전달과 생동감 있는 콘텐츠 사이의 차이를 이해하게 되었다.</li>
</ul>
<h3 id="10-스타트업-비즈니스를-위한-노션-데이터베이스-설계-keep-code"><strong>10. 스타트업 비즈니스를 위한 노션 데이터베이스 설계 (Keep Code)</strong></h3>
<ul>
<li><p>KeepCode라는 노션 컨설팅 업체에서 다른 여러 케이스들의 최적화했던 경험과 노하우를 배웠다</p>
</li>
<li><p>노션 페이지를 만들었을 때</p>
<ul>
<li>고객과의 라포가 많이 필요한것 → 자동화보다 관계가 중요</li>
</ul>
</li>
<li><p>노션으로 이런 것도 가능하구나 하는 걸 실감했다</p>
</li>
<li><p>수제 케이크 주문 및 레시피 관리, 치과 기기 관리, 피티 내부 직원 성과 관리 등 실제 사례들을 보니 정말 다양한 분야에서 활용되고 있다는 게 놀라웠다</p>
</li>
<li><p>웹사이트 구축보다 이런 맞춤형 노션 솔루션이 소규모 비즈니스에 더 실용적일 것 같다는 생각이 들었다. 가격대가 궁금할 정도로...</p>
</li>
</ul>
<h3 id="11-make-story-with-notion-차지영-님"><strong>11. Make Story with Notion (차지영 님)</strong></h3>
<ul>
<li>40대 후반의 연사가 노션 엠베서더가 된 여정이 감동적이었다.  </li>
<li>나이와 상관없이 배움을 지속하는 열정을 배울 수 있었다.  </li>
<li>우리 부모님도 사용하실 수 있을지 생각하며 연사의 발표를 지켜봤다.</li>
</ul>
<h3 id="12-사랑은-노션을-만들고-노션은-사랑을-만듭니다-한바름-님"><strong>12. 사랑은 노션을 만들고, 노션은 사랑을 만듭니다. (한바름 님)</strong></h3>
<ul>
<li>커플 노션을 4년간 꾸준히 이어온 비결이 궁금했다.  </li>
<li>공통의 기록 공간을 유지하는 데 필요한 노력과 창의성이 대단했다.  </li>
</ul>
<h1 id="아쉬웠던-점들">아쉬웠던 점들</h1>
<h3 id="1-운영상의-문제">1. 운영상의 문제</h3>
<ul>
<li>기존 발표 시각보다 1시간 정도 지연되었고 연사들의 특강이 있을 때에 화면의 밝기나 마이크 배터리 없음 등 크고 작은 이슈들이 있었다.<h3 id="2-개발자-대상-세션-부족">2. 개발자 대상 세션 부족</h3>
</li>
<li>전체적으로 개발자분들의 참여도가 높아보였음에도 불구하고 이러한 분들을 윟나 깊이 있는 세션이 부족했떤 것들이 아쉬웠다. </li>
<li>예를 들어, Notion API를 활용한 웹사이트 구축이나 개인 프로젝트를 위한 고급 활용법 등이 추가되면 좋았을 듯하다.</li>
</ul>
<h3 id="취준생-개발자로서-느낀-점">취준생 개발자로서 느낀 점</h3>
<ul>
<li>노션은 도구 이상이다. 단순한 협업과 기록 툴을 넘어 비즈니스 관리, 심지어 퍼스널 브랜드 구축까지 가능하다는 걸 알게 됐다</li>
<li>교사분들의 발표를 보며 커뮤니티의 활성화와 정보공유가 아주 활발하다는 것이 인상깊었다.</li>
<li>내년에는 노션 사용자의 파이가 큰 개발자 입장에서 내가 잘하는 스터디 템플릿 제작, 공유 및 운영 프로세스를 체계화하여 내년에는 나도 이런 자리에 설 수 있도록 준비해보고 싶다. </li>
</ul>
<h4 id="ps-사은품-양보해주신-노션-남매의-헌트님-앞자리-말동무해주시고-취준생이라고-칼국수-사주신-선생님-감사합니다">ps. 사은품 양보해주신 노션 남매의 헌트님, 앞자리 말동무해주시고 취준생이라고 칼국수 사주신 선생님 감사합니다.</h4>
<div align= "center">
<img src="https://velog.velcdn.com/images/junr_65535/post/9ffb399f-4891-48f6-9284-c78d11523bb4/image.jpeg" width="300">
</div>

<h4 id="노션-서밋-행사에서-얻은-경희대-노션서밋-사진-피크민과-함께-이-글을-마칩니다">노션 서밋 행사에서 얻은 경희대 노션서밋 사진 피크민과 함께 이 글을 마칩니다.</h4>
<img src="https://velog.velcdn.com/images/junr_65535/post/9320b4c7-f03e-4341-85a2-ee7a856319bf/image.jpeg" width="150">


]]></description>
        </item>
        <item>
            <title><![CDATA[시스템 메모리 구조와 커널스택, 유저스택]]></title>
            <link>https://velog.io/@junr_65535/%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B5%AC%EC%A1%B0%EC%99%80-%EC%BB%A4%EB%84%90%EC%8A%A4%ED%83%9D-%EC%9C%A0%EC%A0%80%EC%8A%A4%ED%83%9D</link>
            <guid>https://velog.io/@junr_65535/%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B5%AC%EC%A1%B0%EC%99%80-%EC%BB%A4%EB%84%90%EC%8A%A4%ED%83%9D-%EC%9C%A0%EC%A0%80%EC%8A%A4%ED%83%9D</guid>
            <pubDate>Fri, 06 Dec 2024 19:59:59 GMT</pubDate>
            <description><![CDATA[<h3 id="서론">서론.</h3>
<p>CS_Study를 진행하면서 커널스택과 유저스택에 대한 이해도가 부족한 것 같아 다시 톺아보았다.</p>
<h2 id="시스템-메모리-구조"><strong>시스템 메모리 구조</strong></h2>
<p>인텔 8086 32비트 시스템은 다음과 같이 구성되어 있다.</p>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/154913a7-83d8-48fd-abf9-aaa440a5257b/image.png" alt=""></p>
<p>메모리는 크게 유저 공간(User Space)과 커널 공간(Kernel Space)으로 구분된다.</p>
<ul>
<li><strong>유저 영역(2GB)</strong>: 사용자 프로그램이 실행되는 공간으로, 일반적으로 프로세스마다 세그먼트라는 논리적인 섹션으로 나누어  독립적인 메모리 공간을 제공한다.</li>
<li><strong>커널 영역(2GB)</strong>: 운영체제가 관리하는 보호된 메모리 공간으로, 사용자 모드에서는 접근할 수 없다.</li>
</ul>
<h3 id="유저-영역-user-space">유저 영역 (User Space)</h3>
<ol>
<li><p><strong>Null Pointer 할당 영역 (64KB)</strong></p>
<ul>
<li>Null 포인터 접근을 방지하기 위한 보호 영역으로, 잘못된 메모리 접근 시 <strong>Segmentation Fault</strong>를 발생시킨다.</li>
<li>해당 영역은 어떠한 데이터도 저장되지 않으며, 프로세스의 오류를 조기에 감지하는 역할을 한다.</li>
</ul>
</li>
<li><p><strong>유저 영역 (2GB)</strong></p>
<p> 유저 영역은 프로그램 실행에 필요한 메모리를 제공하며, 다음과 같은 구성 요소로 나뉜다</p>
<ul>
<li><strong>코드 섹션</strong>: 실행 가능한 기계어 코드가 저장된다.</li>
<li><strong>데이터 섹션</strong>: 초기화된 전역 변수 및 정적 변수가 포함된다.</li>
<li><strong>BSS 섹션</strong>: 초기화되지 않은 전역 변수 및 정적 변수가 저장된다.</li>
<li><strong>힙(Heap)</strong>: 동적으로 할당되는 메모리 공간으로, 실행 중 프로그램이 메모리를 요청할 때 사용된다.</li>
<li><strong>스택(Stack)</strong>: 함수 호출 시 매개변수, 복귀 주소, 지역 변수 등을 저장하는 공간으로, LIFO(Last-In, First-Out) 방식으로 관리된다.</li>
</ul>
</li>
<li><p><strong>OFF-Limit 영역 (64KB)</strong></p>
<ul>
<li>유저 영역과 커널 영역 사이에 위치한 보호 공간으로, 유저 모드에서 커널 영역으로의 잘못된 접근을 방지한다.</li>
</ul>
</li>
</ol>
<h3 id="커널-영역-kernel-space">커널 영역 (Kernel Space)</h3>
<p>커널 영역은 다음과 같이 8가지 주요 영역으로 구성되어 있다:</p>
<ol>
<li>커널 코드와 데이터<ul>
<li><strong>커널 텍스트(segment)</strong>: 커널의 실행 코드가 위치하는 영역. 읽기 전용으로, 일반적으로 수정되지 않음.</li>
<li><strong>커널 데이터</strong>: 커널의 전역 변수와 정적 변수.<ul>
<li>초기화된 데이터와 초기화되지 않은 데이터(BSS 세그먼트) 포함.</li>
</ul>
</li>
</ul>
</li>
<li>페이지 테이블<ul>
<li><strong>전역 페이지 테이블</strong>: 사용자 공간과 커널 공간 모두에 대한 페이지 매핑 정보가 포함됨.</li>
<li><strong>공통 매핑</strong>: 모든 프로세스가 공통으로 커널 공간을 참조할 수 있도록 페이지 테이블에 매핑.</li>
</ul>
</li>
<li>커널 힙<ul>
<li><strong>kmalloc API</strong>: 커널에서 사용하는 동적 메모리 할당 영역.</li>
<li><strong>vmalloc 영역</strong>: 비연속적인 가상 메모리를 동적으로 할당받는 영역.</li>
</ul>
</li>
<li>I/O 메모리 맵핑<ul>
<li><strong>장치 메모리 매핑(Device Memory Mapping)</strong>: 하드웨어 장치와 상호작용하기 위해 사용되는 영역.</li>
<li><strong>메모리 매핑 파일</strong>: 하드웨어 레지스터를 참조.</li>
</ul>
</li>
<li>고정된 매핑(Fixed Mapping)<ul>
<li>특정 물리 메모리 또는 하드웨어를 커널 공간에 고정적으로 매핑.</li>
<li>예: APIC(Advanced Programmable Interrupt Controller)와 같은 프로세서 장치.</li>
</ul>
</li>
<li>스택 영역<ul>
<li><strong>커널 스택</strong>: 프로세스가 커널 모드로 전환될 때 사용.</li>
<li>프로세스 또는 스레드마다 별도로 할당되며, 컨텍스트 스위칭 시 작업 기록을 저장.</li>
</ul>
</li>
<li>모듈(Module) 영역<ul>
<li>동적으로 로드된 커널 모듈(예: 장치 드라이버)의 코드와 데이터가 저장됨.</li>
<li>커널 실행 중 동적으로 할당 및 해제.</li>
</ul>
</li>
<li>예약되지 않은 영역<ul>
<li>하드웨어 의존적이거나 특수 목적으로 사용될 수 있는 영역.</li>
<li>예: 커널 디버깅 데이터 또는 특정 장치와 관련된 데이터.</li>
</ul>
</li>
</ol>
<h1 id="유저-스택과-커널-스택">유저 스택과 커널 스택</h1>
<p>자 이제 커널 허옇게 보이던 커널스택과 유저스택이 보인다. 시스템 메모리의 유저영역의 프로세스마다 독립적인 세그먼트에 올라가는 스택 섹션이 유저스택이고 커널스택은 커널 공간의 프로세스마다 독립적으로 존재하는 섹션이었다. 다시 정리해보자.</p>
<h2 id="유저-스택-user-stack">유저 스택 (User Stack)</h2>
<ul>
<li><strong>위치</strong>: 프로세스의 사용자 주소 공간 내에 존재하며, 각 프로세스마다 독립적으로 할당된다.</li>
<li><strong>용도</strong>:<ul>
<li>함수 호출 시 매개변수, 복귀 주소, 지역 변수 등을 저장한다.</li>
<li>함수 호출과 복귀를 관리하며, 프로세스가 사용자 모드에서 실행되는 동안 사용된다.</li>
</ul>
</li>
<li><strong>특징</strong>:<ul>
<li>프로세스마다 고유하며, 가상 메모리 상에서 동적으로 크기가 조정될 수 있다.</li>
<li>사용자 프로세스의 코드 실행 흐름을 보조한다.</li>
</ul>
</li>
</ul>
<h2 id="커널-스택-kernel-stack">커널 스택 (Kernel Stack)</h2>
<ul>
<li><strong>위치</strong>: 커널 메모리 영역(32비트 시스템 기준 3GB~4GB)에 위치하며, 각 프로세스마다 고정된 크기로 할당된다.</li>
<li><strong>용도</strong>:<ul>
<li>커널 모드에서 함수 호출, 로컬 변수 저장, 인터럽트 처리 등에 사용된다.</li>
<li>프로세스 생성 시 프로세스별로 고정 크기로 할당되며, 일반적으로 8KB(32비트 기준)로 설정된다.</li>
</ul>
</li>
<li><strong>특징</strong>:<ul>
<li>커널 스택은 커널 모드에서만 접근 가능하며, 보안성과 안정성을 보장한다.</li>
<li>재귀 호출이나 과도한 지역 변수 사용은 커널 스택 오버플로우를 초래할 수 있으므로 제한적으로 사용해야 한다.</li>
</ul>
</li>
</ul>
<h2 id="유저-스택과-커널-스택의-연관성">유저 스택과 커널 스택의 연관성</h2>
<p>유저 스택과 커널 스택은 프로세스 실행 중 시스템 호출이나 인터럽트가 발생할 때 상호작용한다.</p>
<ol>
<li><strong>모드 전환</strong>:<ul>
<li>시스템 호출이나 인터럽트 발생 시, CPU는 유저 모드에서 커널 모드로 전환된다.</li>
<li>이 과정에서 스택 포인터가 유저 스택에서 커널 스택으로 변경된다.</li>
</ul>
</li>
<li><strong>정보 저장</strong>:<ul>
<li>전환 시, 프로세스 상태(예: 프로그램 카운터, CPU 레지스터 등)는 커널 스택에 저장된다.</li>
<li>이를 통해 유저 스택과 독립적으로 커널 작업이 안전하게 수행될 수 있다.</li>
</ul>
</li>
<li><strong>보안 및 안정성</strong>:<ul>
<li>유저 스택은 사용자 코드에 의해 수정될 가능성이 있으므로, 커널 작업은 커널 스택에서 보호된 환경에서 수행된다.</li>
</ul>
</li>
<li><strong>주소 공간 분리</strong>:<ul>
<li>커널 스택은 모든 프로세스에서 동일한 물리적 주소를 참조하며, 커널 코드가 일정한 메모리 주소에서 실행되도록 보장한다.</li>
</ul>
</li>
</ol>
<h1 id="컨텍스트-스위칭과-커널-스택">컨텍스트 스위칭과 커널 스택</h1>
<h3 id="컨텍스트-스위칭context-switching">컨텍스트 스위칭(Context Switching)</h3>
<p>컨텍스트 스위칭은 CPU가 실행 중인 프로세스 또는 스레드의 상태를 저장하고, 다른 프로세스 또는 스레드로 전환하는 작업이다. 이는 멀티태스킹 환경에서 필수적인 작업으로, 다음과 같은 과정을 포함한다.</p>
<ol>
<li><strong>현재 프로세스 상태 저장</strong>:<ul>
<li>실행 중인 프로세스의 CPU 레지스터, 프로그램 카운터(PC), 스택 포인터(SP) 등의 상태 정보를 커널 스택에 저장한다.</li>
<li>이 정보는 해당 프로세스의 PCB(Process Control Block)에 보관된다.</li>
</ul>
</li>
<li><strong>운영 체제 커널로 전환</strong>:<ul>
<li>하드웨어 인터럽트, 시스템 호출, 또는 스케줄러 호출로 인해 CPU 제어가 커널로 넘어간다.</li>
</ul>
</li>
<li><strong>새로운 프로세스 상태 복원</strong>:<ul>
<li>스케줄러는 다음 실행할 프로세스를 선택하고, 해당 프로세스의 커널 스택에 저장된 정보를 복원한다.</li>
</ul>
</li>
<li><strong>프로세스 실행 재개</strong>:<ul>
<li>새로운 프로세스가 선택되고, 복원된 상태 정보로 실행을 시작한다.</li>
</ul>
</li>
</ol>
<h3 id="컨텍스트-스위칭-중-커널-스택-활용">컨텍스트 스위칭 중 커널 스택 활용</h3>
<p>컨텍스트 스위칭 시, 커널 스택에는 다음 정보가 저장된다.</p>
<ol>
<li><strong>CPU 레지스터 상태</strong>:<ul>
<li>프로그램 카운터(PC), 스택 포인터(SP), 범용 레지스터 값 등.</li>
</ul>
</li>
<li><strong>프로세스 상태</strong>:<ul>
<li>PCB에 저장된 프로세스 상태 정보와 연계.</li>
</ul>
</li>
<li><strong>메모리 관리 정보</strong>:<ul>
<li>페이지 테이블, 세그먼트 테이블 등 메모리 매핑 관련 정보.</li>
</ul>
</li>
<li><strong>I/O 상태 정보</strong>:<ul>
<li>열려 있는 파일, 사용 중인 I/O 장치의 상태 정보.</li>
</ul>
</li>
<li><strong>스케줄링 정보</strong>:<ul>
<li>우선순위, CPU 사용 시간 등 스케줄링 결정에 필요한 정보.</li>
</ul>
</li>
</ol>
<h3 id="결론">결론.</h3>
<p>커널 스택과 유저 스택에 대해 다시 톺아보며, 각각의 역할과 구조를 명확히 이해할 수 있었다. </p>
<p>공부할수록 운영체제가 얼마나 정교하게 설계되어있는지 느낄 수 있었다…</p>
<p>KOCW도 하나 등록했다. 너무 깊게 파는건 아니겠지</p>
<h3 id="참고">참고.</h3>
<p>•    <a href="https://velog.io/%40dlgmlfo04/LinuxKernelInternalCH04">리눅스 커널 내부구조 - 메모리 관리</a></p>
<p>•    <a href="https://brewagebear.github.io/linux-kernel-internal-2/">리눅스 커널 메모리 관리 훑어보기</a></p>
<p>•    <a href="https://m.blog.naver.com/yyg1368/60147140388">Linux Kernel Memory Addressing</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[운영체제의 구조와 커널의 종류]]></title>
            <link>https://velog.io/@junr_65535/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C%EC%9D%98-%EA%B5%AC%EC%A1%B0%EC%99%80-%EC%BB%A4%EB%84%90%EC%9D%98-%EC%A2%85%EB%A5%98</link>
            <guid>https://velog.io/@junr_65535/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C%EC%9D%98-%EA%B5%AC%EC%A1%B0%EC%99%80-%EC%BB%A4%EB%84%90%EC%9D%98-%EC%A2%85%EB%A5%98</guid>
            <pubDate>Thu, 28 Nov 2024 15:15:58 GMT</pubDate>
            <description><![CDATA[<h3 id="운영체제란-무엇인가">운영체제란 무엇인가?</h3>
<p>운영체제는 사용자와 하드웨어 사이의 인터페이스 역할을 하는 소프트웨어의 집합이다.</p>
<p>여기서 <code>인터페이스</code> 라는 단어를 사용한 이유는 사용자가 하드웨어를 직접 조종하지 않고 운영체제를 통해 하드웨어 자원의 사용을 제어하고 조종하는 중개자 역할을 하기 때문</p>
<h1 id="운영체제의-구조">운영체제의 구조</h1>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/548c8c0d-7453-4cdf-8d05-7c45fa2aff0e/image.png" alt=""></p>
<p>운영체제는 크게 커널과 인터페이스로 이루어져 있다. 커널은 자원의 추상화와 관리가 이루어지는 공간이고 인터페이스는 각 커널과 상호작용하는 소프트웨어 계층 사이를 중개해주는 연결 장치를 말합니다.</p>
<h2 id="커널">커널</h2>
<p>커널은 운영체제의 핵심으로 하드웨어의 자원을 자원을 추상화하여 필요한 프로세스들에게 나눠주는 운영체제의 핵심 자원 관리자이다.</p>
<p>여기서 추상화를 사용한다는 것은, 하나의 하드웨어 자원을 논리적인 단위로 변환하여 여러 프로세스나 태스크들이 하드웨어를 직접 제어하지 않고도 사용할 수 있도록 중재자 역할을 한다는 의미이다.</p>
<h3 id="하드웨어-자원의-추상화">하드웨어 자원의 추상화</h3>
<p>커널은 각 하드웨어 자원들을 특정 관리자를 통해 추상화한다.</p>
<table>
<thead>
<tr>
<th>관리자</th>
<th>하드웨어</th>
<th>추상화 결과</th>
</tr>
</thead>
<tbody><tr>
<td><strong>태스크 관리자</strong></td>
<td>CPU</td>
<td>태스크(task)</td>
</tr>
<tr>
<td><strong>메모리 관리자</strong></td>
<td>메모리</td>
<td>페이지(page), 세그먼트(segment)</td>
</tr>
<tr>
<td><strong>파일 시스템 관리자</strong></td>
<td>디스크</td>
<td>파일(file)</td>
</tr>
<tr>
<td><strong>네트워크 관리자</strong></td>
<td>네트워크</td>
<td>소켓(socket)</td>
</tr>
</tbody></table>
<p>이러한 관리자를 통해서 컴퓨터의 하드웨어에 대한 세부 사항들을 몰라도 사용자는 쉽고 일관된 방식으로 컴퓨터를 사용할 수 있게 된다.</p>
<h3 id="커널의-종류">커널의 종류</h3>
<p>커널의 구조에 따라 하드웨어 자원을 관리하고 시스템 호출을 처리하는 방식이 달라진다.</p>
<p>커널의 종류는 구조에 따라 크게 모놀리식 커널, 계층형 커널, 마이크로 커널, 하이브리드 커널로 나눌 수 있다.</p>
<ol>
<li><p><strong>모놀리식 커널</strong></p>
<p> <img src="https://velog.velcdn.com/images/junr_65535/post/4285dc15-dc1a-4d73-9883-23b747e48ed2/image.png" alt=""></p>
</li>
</ol>
<pre><code>- 특징 :
    - 커널 내에 운영체제의 모든 주요 기능을 포함한다.
    - 단일 큰 블록으로 동작하고 커널 모드에서 모든 작업을 수행한다.
    - **`시스템 콜` : 시스템 콜이 호출되면 커널 내부에서 직접 처리된다.**
        - 예를 들어, `read()` 시스템 콜을 호출하면 파일 시스템 모듈이 커널 내부에서 동작하여 파일을 읽는 작업을 수행한다.
- 장점:
    - 커널 내부에서 모든 작업이 이루어지므로 모듈간 통신 비용이 적어져 오버헤드가 적다.
- 단점:
    - 하나의 오류가 커널 전체에 영향을 미칠 가능성이 높고 수정이 어려움.
- 예시 : Linux 커널 (전형적인 모놀리식 커널이다. 단, 해당 구조의 단점을 보완하기 위해 모듈화를 도입하여 모듈형 모놀리식 커널로 발전했다.), Unix 커널</code></pre><ol start="2">
<li><p><strong>계층형 커널(Layered kernel)</strong></p>
<p> <img src="https://velog.velcdn.com/images/junr_65535/post/dc37f7e3-95d9-48bb-aa6d-d708ecb96177/image.png" alt=""></p>
</li>
</ol>
<pre><code>- 특징:
    - 커널이 가진 모듈을 묶어 계층으로 나누고 계층간 인터페이스를 통한 통신을 통해 운영체제를 구현하는 방식이다.
    - 일반적으로 하위 계층은 하드웨어와 가깝고 상위 계층은 사용자와 가까운 추상화 수준을 가진다.
    - **`시스템 콜` : 시스템 콜이 호출되면** 해당 요청은 **계층적으로 전달되며 처리**된다.
        - 예를 들어, read() 시스템 콜은 파일 시스템 계층을 통해 디스크 I/O 계층으로 전달되어 최종적으로 하드웨어에 접근한다.
- 장점:
    - 모듈화: 각 계층별로 독립성이 보장되어 특정 계층을 수정하거나 교체해도 다른 계층에 영향을 주지 않는다.
    - 추상화: 하드웨어 세부사항을 상위 계층에서 숨길 수 있다.
- 단점:
    - 계층간 호출이 많아질수록 오버헤드가 증가하여 성능이 저하될 수 있다.
- 예시:
    - THE 운영체제: 1960년 다익스트라가 설계한 최초의 계층형 커널 운영체제
    - Windows NT(초기버전): HAL(Haredware Abstraction Layer)를 통해 하드웨어와 커널 간 계층화 구현</code></pre><ol start="3">
<li><p><strong>마이크로 커널</strong></p>
<p> <img src="https://velog.velcdn.com/images/junr_65535/post/49b30dd6-4eca-4f45-8562-d927e6c16e38/image.png" alt=""></p>
</li>
</ol>
<pre><code>- 특징 :
    - 커널은 최소한의 기능(프로세스 관리, 메모리 관리, IPC 등)만을 포함하여 kernel mode 에서 실행하고 나머지 기능(파일 시스템, 네트워크 등)은 **User mode**에서 실행한다.
    - 커널 모드와 유저 모드 사이의 통신에는 시스템콜을 사용하고 유저 모드 사이의 통신은 IPC를 사용함.
    - **`시스템 콜` :** 시스템 콜은 **커널에서 최소한의 작업**(예: IPC로 요청 전달)을 수행하고, 실제 작업은 **유저 모드 서비스에서 처리**된다.
        - 예를 들어 `read()` 시스템 콜을 호출하면 **파일 시스템 서버(유저모드)**로 요청을 전달하고 해당 서버가 작업을 처리한 후 결과를 반환한다.
- 장점:
    - 각 기능이 독립적으로 실행되어 오류가 전체에 미치는 영향이 적다.
- 단점:
    - 커널모드와 사용자 모드 간 빈번한 통신으로 성능이 떨어질 수 있다.
- 예시: Minix, QNX</code></pre><ol start="4">
<li><p><strong>하이브리드 커널</strong></p>
<p> <img src="https://velog.velcdn.com/images/junr_65535/post/9e15ca2a-4e43-46db-a797-e91c56172d6a/image.png" alt=""></p>
</li>
</ol>
<pre><code>- 특징:
    - 모놀리식 커널과 마이크로커널의 장점을 결합한 구조
    - 일부 기능은 커널 모드에서 실행되지만, 독립적인 모듈로 관리되어 유연성을 제공
    - **`시스템 콜` :** 시스템 콜은 커널 내부에서 처리되거나, 필요한 경우 일부 작업이 **사용자 모드 서비스로 전달**된다.
        - 예를 들어, read() 시스템 콜은 하드웨어와 직접 관련된 작업은 커널 내부에서 처리하고, 복잡한 파일 시스템 작업은 사용자 모드 서비스로 위임될 수 있다.
- 장점:
    - 성능과 안정성 사이의 균형 제공
- 단점:
    - 설계가 복잡하고, 모듈 간 통합 문제가 발생할 수 있음
- 예시: Windows NT, macOS</code></pre><h2 id="dual-mode">Dual Mode</h2>
<p>운영체제는 시스템의 안전성과 보안을 위해 CPU 실행 모드를 사용자 모드(User Mode)와 커널 모드(Kernel Mode)로 구분하여 관리한다. 이를 Dual Mode라고 한다.</p>
<h3 id="왜-이렇게-구분할까">왜 이렇게 구분할까?</h3>
<p>만약 이러한 듀얼 모드를 지원하지 않을 경우 (여러 응용 프로그램들이 CPU, 메모리 등의 자원에 직접적으로 접근하고 조작할 경우) 자원의 공유가 무질서하게 관리될 것이고 응용 프로그램들이 조금만 실수해도 컴퓨터 전체에 악영향을 끼칠 수 있을 것이다. 
 그렇기에 모드를 논리적으로 나누고 이 사이에 시스템 콜 인터페이스를 둠으로써 이를 막을 수 있다.</p>
<h3 id="사용자-모드-user-mode">사용자 모드 (User Mode)</h3>
<ul>
<li>일반적인 응용 프로그램이 실행되는 모드</li>
<li>하드웨어 자원에 직접 접근 불가능</li>
<li>커널 영역의 코드를 실행할 수 없음</li>
</ul>
<h3 id="커널-모드-kernel-mode">커널 모드 (Kernel Mode)</h3>
<ul>
<li>운영체제가 실행되는 모드</li>
<li>모든 종류의 명령어 실행 가능</li>
<li>하드웨어 자원에 직접 접근 가능</li>
</ul>
<h3 id="모드-전환">모드 전환</h3>
<ul>
<li>슈퍼바이저 플래그로 현재 모드 구분<ul>
<li>0: 사용자 모드</li>
<li>1: 커널 모드</li>
</ul>
</li>
<li>사용자 모드에서 실행 중인 프로그램이 시스템 자원을 필요로 할 때, 반드시 커널 모드로 전환되어야 한다.</li>
<li>이때 <strong><code>시스템 콜</code></strong>을 통해 모드의 전환이 이루어진다.</li>
</ul>
<h2 id="시스템-콜">시스템 콜</h2>
<p>시스템 콜은 커널이 사용자나 프로그램이 커널에 직접 접근하는 것을 막기위해 만들어진 인터페이스이다.</p>
<p>시스템 콜 인터페이스는 <strong>User Space와 Kernel Space의 경계</strong>에서 동작하며, 커널 내부의 적절한 모듈로 요청을 전달하는 역할을 한다.</p>
<p>앞서 User 모드 와 Kernal Space의 사이에 시스템 콜을 통해 모드의 전환이 이루어진다고 했다.</p>
<p>즉, User Interface의 작업들이 커널이 관리하는 자원에 접근해야 할 때, System Call Interface를 통해 Kernel Space의 자원 관리자에게 요청이 전달되어 처리되는 것이다.</p>
<h3 id="시스템-콜의-처리-위치">시스템 콜의 처리 위치</h3>
<p>시스템 콜은 <strong>모든 커널에서 기본적으로 커널 모드에서 실행</strong>되지만, 커널의 구조에 따라 시스템 콜의 처리 위치와 방식이 약간씩 달라진다.</p>
<ul>
<li><strong>모놀리식 커널</strong>: 시스템 콜은 커널 내부에서 직접 처리됨 (가장 빠름).</li>
<li><strong>마이크로커널</strong>: 커널은 시스템 콜을 유저 모드 서비스로 전달함 (더 안전하지만 느림).</li>
<li><strong>계층형 커널</strong>: 계층적으로 시스템 콜이 처리됨.</li>
<li><strong>하이브리드 커널</strong>: 필요에 따라 커널 또는 유저 모드 서비스에서 처리됨.</li>
</ul>
<p>우리가 사용하는 대부분의 상용 운영체제(예: Linux, Windows, macOS)는 <strong>모놀리식 커널 또는 하이브리드 커널 기반</strong>으로 시스템 콜을 처리한다.</p>
<h3 id="시스템-콜의-유형">시스템 콜의 유형</h3>
<p>시스템 콜은 커널에서 지원하는 서비스에 따라 유형이 나뉜다. 크게 프로세스 제어, 파일 조작, 장치 관리, 정보 유지, 통신 등으로 조작할 수 있다.</p>
<table>
<thead>
<tr>
<th><strong>카테고리</strong></th>
<th><strong>주요 작업</strong></th>
<th><strong>예시</strong></th>
</tr>
</thead>
<tbody><tr>
<td>프로세스 제어</td>
<td>프로세스 생성, 종료, 대기</td>
<td>fork(), exec(), wait()</td>
</tr>
<tr>
<td>파일 관리</td>
<td>파일 열기, 읽기, 쓰기, 닫기</td>
<td>open(), read(), write()</td>
</tr>
<tr>
<td>디바이스 관리</td>
<td>디바이스와 데이터 입출력</td>
<td>ioctl(), read(), write()</td>
</tr>
<tr>
<td>정보 유지</td>
<td>시스템 정보 조회 및 설정</td>
<td>getpid(), gettimeofday()</td>
</tr>
<tr>
<td>통신</td>
<td>프로세스 간 데이터 교환</td>
<td>pipe(), socket(), send()</td>
</tr>
<tr>
<td>메모리 관리</td>
<td>메모리 할당, 해제</td>
<td>mmap(), brk(), munmap()</td>
</tr>
<tr>
<td>보안</td>
<td>인증 및 권한 관리</td>
<td>setuid(), getuid(), chmod()</td>
</tr>
</tbody></table>
<blockquote>
<h4 id="서로-다른-시스템-콜의-구분">서로 다른 시스템 콜의 구분.</h4>
<ul>
<li><strong>시스템 콜 번호로 구분</strong>
  운영체제는 각 시스템 콜에 고유한 번호를 부여한다. 시스템 콜 인터페이스는 이 번호를 통해 어떤 호출인지 식별할 수 있다. (ex. <code>read()</code> 는 시스템 콜 번호 3 으로 식별 가능)</li>
<li><strong>시스템 콜 이름 으로 구분</strong>
  시스템 콜 인터페이스가 호출된 함수 이름으로 해당 작업을 구분 가능하다.</li>
<li><strong>인자(파라미터) 로 구분</strong>
  전달된 인자(파일 디스크립터, 크기 등)를 통해 작업의 세부 사항을 파악한다.</li>
</ul>
</blockquote>
<h2 id="shell-은-무엇인가"><strong>Shell</strong> 은 무엇인가?</h2>
<ul>
<li><p><strong>정의</strong>: 사용자가 운영체제와 상호작용할 수 있도록 <strong>명령 해석기(CI)를 포함한 인터페이스</strong></p>
<blockquote>
<p><strong>명령 해석기 ( Command Interpreter )</strong></p>
<p>사용자가 입력한 명령어를 해석하고 그에 따라 작업을 수행하는 프로그램</p>
<p><strong>역할</strong>: 명령어를 파싱(parse)하고, 해당 명령어에 맞는 작업을 시스템에 요청하거나 프로세스를 실행.</p>
<p>명령 인터프리터의 구현 방식 두가지</p>
<ol>
<li><strong>명령 인터프리터 자체가 명령을 실행하는 방식</strong>:<ul>
<li>명령 인터프리터 자체가 각 명령어를 처리하는 로직을 가지고 있음.</li>
<li>이 경우 명령어와 처리 로직이 인터프리터에 포함되어있어 새로운 명령어를 추가하려면 인터프리터 자체의 변경이 필요</li>
<li>예) echo</li>
</ul>
</li>
<li><strong>시스템 프로그램이 명령을 실행하는 방식</strong>:<ul>
<li>명령 인터프리터가 명령을 실행할 코드 자체를 가지고 있는 대신 명령을 입력받으면 해당 명령어가 가리키는 쉘 스크립트 파일이나 바이너리 파일을 통해 실행됨.</li>
<li>예) <code>rm {파일이름}</code> : <code>/bin/rm</code> 경로의 바이너리 파일이 <code>{파일이름}</code> 같은 파일을 삭제하는 수행.</li>
</ul>
</li>
</ol>
</blockquote>
</li>
<li><p>기본적으로 커널은 쉘로 감싸져있음</p>
</li>
<li><p><strong>운영체제의 필수 구성 요소</strong> 중 하나이며, 다양한 종류의 쉘이 존재.</p>
</li>
<li><p>또한 쉘 커맨드 라인들의 묶음인 쉘 스크립트를 통해 반복적인 작업을 자동화하고, 복잡한 시스템 작업을 간단히 수행할 수 있음.</p>
</li>
<li><p><strong>종류</strong>:</p>
<ul>
<li><strong>Bash (Bourne Again Shell)</strong>: 리눅스에서 기본적으로 많이 사용되며, <strong>강력한 스크립트 기능</strong>과 <strong>명령어 자동 완성</strong>을 제공한다.</li>
<li><strong>Zsh (Z Shell)</strong>: <strong>사용자 맞춤형 설정</strong>과 <strong>강력한 자동 완성</strong> 기능을 제공하며, <strong>플러그인</strong> 시스템으로 확장 가능하다.</li>
</ul>
</li>
</ul>
<h2 id="결론">결론.</h2>
<p>CS 스터디를 하면서 조사했던 운영체제 부분을 정리해보았다. 
아티클과 책들을 읽으며 정리하는 과정에서, 각 저자마다 커널과 시스템 콜의 역할을 다르게 설명하는 부분이 많아 교차검증에 시간이 많이 소요되었다. 
알고 보니 실제로 <strong>커널마다 시스템 콜의 동작 방식과 구조가 달랐기 때문</strong>이었다. </p>
<p>이를 통해 개념적인 이해와 실제 구현의 차이를 이해하는 것이 얼마나 중요한지 다시 한번 깨달았다.</p>
<h3 id="참고">참고</h3>
<p><a href="https://www.quora.com/What-is-the-difference-between-an-interpreter-and-a-shell">https://www.quora.com/What-is-the-difference-between-an-interpreter-and-a-shell</a>
<a href="https://imbf.github.io/computer-science(cs)/2020/09/03/Operating-System-Structures.html">https://imbf.github.io/computer-science(cs)/2020/09/03/Operating-System-Structures.html</a>
<a href="https://wikidocs.net/230921">https://wikidocs.net/230921</a>
<a href="https://hongong.hanbit.co.kr/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C%EB%9E%80-%EC%BB%A4%EB%84%90%EC%9D%98-%EA%B0%9C%EB%85%90-%EC%9D%91%EC%9A%A9-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-%EC%8B%A4%ED%96%89%EC%9D%84-%EC%9C%84%ED%95%9C/">https://hongong.hanbit.co.kr/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C%EB%9E%80-%EC%BB%A4%EB%84%90%EC%9D%98-%EA%B0%9C%EB%85%90-%EC%9D%91%EC%9A%A9-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-%EC%8B%A4%ED%96%89%EC%9D%84-%EC%9C%84%ED%95%9C/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CS_study] 스프링의 주요 특징 ]]></title>
            <link>https://velog.io/@junr_65535/CSstudy-%EC%8A%A4%ED%94%84%EB%A7%81%EC%9D%98-%EC%A3%BC%EC%9A%94-%ED%8A%B9%EC%A7%95</link>
            <guid>https://velog.io/@junr_65535/CSstudy-%EC%8A%A4%ED%94%84%EB%A7%81%EC%9D%98-%EC%A3%BC%EC%9A%94-%ED%8A%B9%EC%A7%95</guid>
            <pubDate>Wed, 20 Nov 2024 20:02:23 GMT</pubDate>
            <description><![CDATA[<h2 id="스프링의-주요-특징은-5가지이다">스프링의 주요 특징은 5가지이다.</h2>
<ol>
<li>POJO 기반의 구성</li>
<li>의존성 주입을 통한 객체간의 관계 구성</li>
<li>AOP 지원</li>
<li>편리한 MVC 구조</li>
<li>WAS 에 종속적이지 않은 개발 환경</li>
</ol>
<h3 id="pojo-기반의-구성">POJO 기반의 구성</h3>
<p>스프링은 객체간의 관계를 구성할 때에 별도의 API를 사용하지 않고 POJO의 구성만으로 가능하다.</p>
<p>즉. 일반적인 JAVA코드를 사용하여 객체간의 관계를 구성하기에 JAVA를 배울 때에 썼던 가장 일반적인 형태의 코드를 그대로 실행할 수 있다는 것이 장점이다.</p>
<blockquote>
<p><strong>POJO 객체를 사용하면 좋은점.</strong></p>
<p>특정한 라이브러리나 컨테이너의 기술에 종속적이지 않기에 가장 일반적인 JAVA코드로 실행할 수 있어 생산성과 테스트코드 작업에 유리하다.</p>
</blockquote>
<h3 id="의존성-주입di">의존성 주입(DI)</h3>
<p>한 객체가 다른 객체를 필요로 할 때에 해당 객체를 직접 생성하는 것이 아닌, 외부로부터 주입을 받을 수 있다.</p>
<p>이 경우 각 객체는 자신의 역할에 집중할 수 있다. 어떤 객체에 의존하든 자신의 역할은 변경되지 않는다는 것이다.</p>
<p>스프링에서는 <code>Application Context</code> 가 필요한 객체들을 생성하고 필요한 객체들을 주입하는 역할을 한다.</p>
<p><code>Application Context</code> 가 객체들을 관리하고 해당 객체와 객체사이의 의존관계를 설정할 수 있다. (이 객체를 Bean이라고 부른다.)</p>
<blockquote>
<p><strong>해당 Bean을 등록하는 방식으로</strong></p>
<ol>
<li>XML 설정</li>
<li>어노테이션 설정</li>
<li>JAVA 설정 </li>
</ol>
<p>방식이 있다. 각 객체들은 기본적으로 싱글톤 객체로 생성이 되며 객체들의 의존성을 Application Context 가 관리하게 된다.</p>
</blockquote>
<blockquote>
<p>스프링 Bean의 종류</p>
</blockquote>
<h3 id="aop-지원">AOP 지원</h3>
<p>기본적으로, 프레임워크는 <strong><em>개발자가 비즈니스 로직에만 집중</em></strong>할 수 있게 발전해왔다.</p>
<p>이를 실현하기 가장 좋은 방법이 반복적인 코드의 제거 이다.</p>
<p>스프링에서는 대부분의 시스템이 공통으로 가지고 있는 보안, 로그, 트랜잭션과 같은 비즈니스 로직이 아니지만 반드시 처리가 필요한 부분들을 가지고 있고 이를 <code>횡단 관심사</code> 라고 부른다.</p>
<p>이러한 횡단 관심사를 모듈로 분리해서 제작하는 것이 AOP(관점 지향 프로그래밍) 이다.</p>
<p>스프링에서는 AspectJ의 문법을 통해서 AOP 를 작성할 수 있다. </p>
<p><strong>장점</strong></p>
<ul>
<li>핵심 비즈니스 로직에만 개발자가 집중할 수 있음</li>
<li>각 프로젝트마다 다른 관심사를 적용할 때 코드의 수정을 최소화 시킬 수 있음</li>
<li>원하는 관심사의 유지보수가 수월한 코드를 구성할 수 있음</li>
</ul>
<ol>
<li><strong>AspectJ</strong>:</li>
</ol>
<ul>
<li>모든 Join Point(메서드 실행, 필드 접근, 객체 생성 등)를 지원한다.</li>
<li>컴파일 시 위빙이 가능해 실행 성능이 우수하다.</li>
<li>객체지향 프로그래밍의 모든 부분에 AOP를 적용할 수 있다.</li>
</ul>
<ol>
<li><strong>Spring AOP</strong>:</li>
</ol>
<ul>
<li>메서드 실행 Join Point만 지원하며, 필드 접근이나 객체 생성에는 적용할 수 없다.</li>
<li>프록시 기반 런타임 위빙만 지원해 실행 성능이 AspectJ보다 다소 낮다.</li>
<li>Spring의 DI 컨테이너와 통합되어 AOP 사용이 간편하다.</li>
</ul>
<blockquote>
<p>AspectJ 의 작동 방식</p>
<ol>
<li><p>포인트컷(Pointcut) 정의: 어떤 메서드에 관심사를 적용할지 지정</p>
<ul>
<li><p>횡단 관심사가 실행되는 실행 시점을 선택한다.</p>
<pre><code class="language-java">  // com.example.service 패키지의 모든 메서드 실행 시점을 포인트컷으로 정의하는 예시
  @Pointcut(&quot;execution(* com.example.service.*.*(..))&quot;)
  public void serviceLayerMethods() {}</code></pre>
</li>
<li><p>주요 포인트컷 유형</p>
<ul>
<li><code>execution()</code> : 메서드 실행 시점 지정</li>
<li><code>within()</code> : 특정 클래스나 패키지 내부의 모든 메서드 지정</li>
<li><code>this/target</code> : this는 프록시 객체 기준, target은 실제 대상 객체 기준으로 포인트컷을 적용</li>
<li><code>args()</code> : 특정 파라미터 타입에 대해 적용</li>
</ul>
</li>
</ul>
</li>
<li><p>어드바이스(Advice) 정의: 적용할 공통 기능(관심사)을 정의</p>
<ul>
<li>즉 포인트컷부터 선택된 지점에 실행할 횡단 관심사의 기능을 정의하는 부분이다.</li>
<li>AspectJ는 어드바이스의 실행 시점을 기준으로 5가지 유형으로 분류한다.<ul>
<li><code>@Before()</code>: 메서드 실행 전에 어드바이스를 실행</li>
<li><code>@After()</code>: 메서드 실행 후에 어드바이스를 실행 (예외 발생 여부와 관계없이)</li>
<li><code>@AfterReturning()</code>: 메서드가 정상적으로 실행된 후에 어드바이스를 실행</li>
<li><code>@AfterThrowing()</code>: 메서드 실행 중 예외가 발생했을 때 어드바이스를 실행</li>
<li><code>@Around()</code>: 메서드 실행 전후에 어드바이스를 실행, 가장 강력한 어드바이스 유형</li>
</ul>
</li>
</ul>
</li>
<li><p>위빙(Weaving): 포인트컷에 지정된 지점에 어드바이스를 삽입하는 과정</p>
<ul>
<li><p>AspectJ는 컴파일 시점, 클래스 로드 시점, 런타임 시점으로 위빙을 수행한다.</p>
</li>
<li><p><strong>컴파일 시 위빙(Compile-time Weaving)</strong>:</p>
<ul>
<li>AspectJ 컴파일러(ajc)를 사용하여 소스 코드 컴파일 시 어드바이스를 삽입한다.</li>
<li>결과적으로 바이트코드에 AOP 로직이 포함된다.</li>
</ul>
</li>
<li><p><strong>로드 시 위빙(Load-time Weaving)</strong>:</p>
<ul>
<li><p>클래스 로더가 클래스 바이트코드를 JVM에 로드하는 시점에 어드바이스를 삽입.</p>
</li>
<li><p>Spring AOP는 기본적으로 로드 시 위빙을 지원하지 않으며, AspectJ를 통합해야 사용 가능합니다.</p>
</li>
<li><p><code>javaagent</code> 옵션을 사용해 위빙을 수행.</p>
<blockquote>
<p><code>javaagent</code> 옵션이 뭐지? (gpt)</p>
<p><code>javaagent</code>는 Java 애플리케이션의 실행 시 JVM에 전달되는 옵션으로, 클래스 로딩 시점에 바이트코드를 조작할 수 있게 해주는 기능입니다. 주요 특징은 다음과 같습니다:</p>
<ul>
<li>클래스 로딩 시 바이트코드 변경: 클래스가 JVM에 로드될 때 해당 클래스의 바이트코드를 동적으로 수정할 수 있습니다.</li>
<li>비침투적인 코드 변경: 원본 소스 코드를 수정하지 않고도 애플리케이션의 동작을 변경할 수 있습니다.</li>
<li>AOP 구현에 활용: 로드 시 위빙을 구현하는 데 사용되어, 횡단 관심사를 효과적으로 분리할 수 있습니다.</li>
</ul>
</blockquote>
</li>
</ul>
</li>
<li><p><strong>런타임 위빙(Runtime Weaving)</strong>:</p>
<ul>
<li>런타임 시 프록시 패턴을 사용해 동적으로 어드바이스를 삽입.</li>
<li>Spring AOP는 AspectJ의 런타임 위빙 방식을 사용하며, 실제로는 프록시 객체를 생성해 어드바이스를 적용.</li>
</ul>
</li>
</ul>
</li>
</ol>
</blockquote>
<h3 id="트랜잭션-지원">트랜잭션 지원</h3>
<p>DB를 사용할때 가장 신경써야할 경우는 트랜잭션 처리이다. 스프링에서는 이런 트랜잭션의 관리를 어노테이션이나 XML로 설정할 수 있기 때문에 개발자가 매번 상황에 맞는 코드를 작성할 필요가 없도록 설정되어있다.</p>
<h2 id="스프링의-bean-생명주기">스프링의 Bean 생명주기</h2>
<pre><code class="language-java">AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(); 
    ac.register();//빈 추가로 등록
    ac.referesh();//등록하기 위한 갱신
    ac.close();//소멸 메서드, 이때 빈도 같이 소멸</code></pre>
<p>스프링의 Bean 생명주기는 크게 4단계로 나눌 수 있다: 빈 생성, 의존관계 주입, 초기화, 소멸. 각 단계에서 일어나는 일을 살펴보겠다.</p>
<h3 id="1-빈-생성">1. 빈 생성</h3>
<ol>
<li><p>스프링 컨테이너가 시작되면 먼저 <strong><code>컨텍스트</code></strong>라는 메모리 영역을 만든다. 이것이 <code>Application Context</code>이다.</p>
</li>
<li><p>스프링은 설정 파일(XML, Java Config, 어노테이션)에서 정의된 Bean들의 정보를 읽어 스프링 컨테이너에 등록한다.</p>
</li>
<li><p>Bean 정의에 따라 객체를 생성한다. 이 때 <strong>기본적으로 싱글톤 스코프</strong>가 적용되며, 필요에 따라 프로토타입, 요청(Request), 세션(Session) 등의 다른 스코프도 적용될 수 있다.</p>
<blockquote>
</blockquote>
</li>
</ol>
<h3 id="2-의존관계-주입-di">2. 의존관계 주입 (DI)</h3>
<ol>
<li><strong>스프링은 설정 정보를 기반으로 객체(Bean) 간의 의존관계를 주입</strong>한다.</li>
<li>생성자 주입, 세터 주입, 필드 주입 등의 방식으로 의존성이 주입된다.</li>
</ol>
<h3 id="3-초기화">3. 초기화</h3>
<ol>
<li><strong>의존관계 주입이 완료된 후, Bean 초기화 작업을 수행한다.</strong></li>
<li><code>@PostConstruct</code> 어노테이션이 붙은 메서드가 호출된다.</li>
<li><code>InitializingBean</code> 인터페이스의 <code>afterPropertiesSet()</code> 메서드가 호출된다.</li>
<li>Bean 설정에서 지정한 사용자 정의 초기화 메서드가 호출된다.</li>
<li>AOP가 적용되는 경우, 이 시점에서 프록시 객체가 생성된다.</li>
</ol>
<h3 id="4-사용">4. 사용</h3>
<ol>
<li>초기화가 완료된 Bean은 애플리케이션에서 사용 가능한 상태가 된다.</li>
<li>필요에 따라 <code>getBean()</code> 메서드로 Bean을 요청하거나, 의존성 주입을 통해 다른 Bean에서 사용한다.</li>
<li>싱글톤 Bean의 경우 애플리케이션 종료 시까지 계속 사용된다.</li>
</ol>
<h3 id="5-소멸">5. 소멸</h3>
<ol>
<li>애플리케이션 종료 시, 컨테이너는 Bean의 소멸 과정을 시작한다.</li>
<li><code>@PreDestroy</code> 어노테이션이 붙은 메서드가 호출된다.</li>
<li><code>DisposableBean</code> 인터페이스의 <code>destroy()</code> 메서드가 호출된다.</li>
<li>Bean 설정에서 지정한 사용자 정의 소멸 메서드가 호출된다.</li>
<li>이 과정에서 리소스 해제, 연결 종료 등의 정리 작업이 수행된다.</li>
</ol>
<p>이러한 생명주기 관리를 통해 스프링은 Bean의 안전한 생성, 초기화, 사용, 그리고 소멸을 보장하며, 개발자는 비즈니스 로직에 집중할 수 있게 된다.</p>
<p><strong>1. 싱글톤 스코프 (Singleton)</strong></p>
<ul>
<li><strong>기본값(Default Scope)</strong>: 별도로 설정하지 않으면 Singleton 스코프가 적용된다.</li>
<li><strong>특징</strong>:<ul>
<li>스프링 컨테이너 내에서 <strong>단 하나의 인스턴스만 생성</strong>된다.</li>
<li>컨테이너가 초기화될 때 빈이 생성되고, 애플리케이션 종료 시까지 유지된다.</li>
<li>애플리케이션 전체에서 <strong>공유</strong>되므로 상태를 가지는 필드는 사용에 주의해야 한다.</li>
</ul>
</li>
<li><strong>사용 예</strong>:<ul>
<li>주로 <strong>무상태(Stateless) Bean</strong>으로 사용한다.</li>
<li>Service, Repository 등 공유 가능한 객체에 적합하다.</li>
</ul>
</li>
<li><strong>설정 방법</strong>:<ul>
<li>XML: <bean scope="singleton"></li>
<li>어노테이션: 기본값으로 적용됨 (명시 필요 없음).</li>
</ul>
</li>
</ul>
<p><strong>2. 프로토타입 스코프 (Prototype)</strong></p>
<ul>
<li><strong>특징</strong>:<ul>
<li><strong>요청마다 새로운 객체를 생성</strong>한다.</li>
<li>컨테이너가 빈을 생성만 하고 이후의 생명주기는 컨테이너가 관리하지 않는다.</li>
<li><strong>의존성 주입으로 사용된 경우</strong>: 주입된 시점에 새로운 인스턴스가 생성된다.</li>
</ul>
</li>
<li><strong>사용 예</strong>:<ul>
<li>상태를 가지는 객체(예: 사용자별 데이터, 단기 객체) 생성에 적합하다.</li>
<li>동일한 클래스를 필요에 따라 여러 번 생성해야 할 때 사용한다.</li>
</ul>
</li>
<li><strong>설정 방법</strong>:<ul>
<li>XML: <bean scope="prototype"></li>
<li>어노테이션: @Scope(&quot;prototype&quot;)</li>
</ul>
</li>
</ul>
<p><strong>3. 요청(Request) 스코프</strong></p>
<ul>
<li><strong>특징</strong>:<ul>
<li>HTTP 요청이 들어올 때마다 새로운 인스턴스를 생성하고, 요청이 종료되면 소멸한다.</li>
<li><strong>웹 애플리케이션</strong>에서만 사용 가능하며, Spring MVC 컨텍스트에서 동작한다.</li>
<li>각 요청마다 별도의 빈 인스턴스를 제공한다.</li>
</ul>
</li>
<li><strong>사용 예</strong>:<ul>
<li>요청마다 다른 데이터를 저장하거나 처리해야 하는 경우.</li>
<li>예: HTTP 세션과 별개로 요청별 사용자 데이터를 저장.</li>
</ul>
</li>
<li><strong>설정 방법</strong>:<ul>
<li>XML: <bean scope="request"></li>
<li>어노테이션: @Scope(&quot;request&quot;)</li>
</ul>
</li>
</ul>
<p><strong>4. 세션(Session) 스코프</strong></p>
<ul>
<li><strong>특징</strong>:<ul>
<li>HTTP 세션과 생명주기가 동일하다.</li>
<li>세션이 유지되는 동안 같은 빈 인스턴스를 제공한다.</li>
<li><strong>웹 애플리케이션</strong>에서만 사용 가능하며, 주로 사용자별 데이터 저장에 활용한다.</li>
</ul>
</li>
<li><strong>사용 예</strong>:<ul>
<li>로그인한 사용자별 데이터를 유지해야 하는 경우.</li>
<li>예: 인증 정보, 사용자 설정 등.</li>
</ul>
</li>
<li><strong>설정 방법</strong>:<ul>
<li>XML: <bean scope="session"></li>
<li>어노테이션: @Scope(&quot;session&quot;)</li>
</ul>
</li>
</ul>
<p><strong>5. 애플리케이션(Application) 스코프</strong></p>
<ul>
<li><strong>특징</strong>:<ul>
<li>서블릿 컨텍스트(ServletContext)와 생명주기가 동일하다.</li>
<li><strong>웹 애플리케이션 전체에서 공유</strong>되는 빈 인스턴스를 제공한다.</li>
</ul>
</li>
<li><strong>사용 예</strong>:<ul>
<li>전역적으로 사용할 데이터를 저장하거나 관리해야 할 때.</li>
<li>예: 애플리케이션 설정 값, 공통 리소스.</li>
</ul>
</li>
<li><strong>설정 방법</strong>:<ul>
<li>XML: <bean scope="application"></li>
<li>어노테이션: @Scope(&quot;application&quot;)</li>
</ul>
</li>
</ul>
<p><strong>6. 웹소켓(WebSocket) 스코프</strong></p>
<ul>
<li><strong>특징</strong>:<ul>
<li>웹소켓 세션과 생명주기가 동일하다.</li>
<li>각 웹소켓 연결별로 별도의 빈 인스턴스를 생성한다.</li>
</ul>
</li>
<li><strong>사용 예</strong>:<ul>
<li>웹소켓 통신에서 사용자별 데이터를 저장하고 관리할 때.</li>
</ul>
</li>
<li><strong>설정 방법</strong>:<ul>
<li>어노테이션: @Scope(&quot;websocket&quot;)</li>
</ul>
</li>
</ul>
<p><strong>정리</strong></p>
<p><strong>스코프</strong>    <strong>생명주기</strong>    <strong>사용 예</strong></p>
<p><strong>Singleton</strong>    컨테이너 시작부터 종료까지    공통 서비스, Repository 등</p>
<p><strong>Prototype</strong>    빈 요청 시마다 새로 생성    상태를 가지는 객체, 비공유 객체</p>
<p><strong>Request</strong>    HTTP 요청 시작부터 종료까지    요청별 데이터</p>
<p><strong>Session</strong>    HTTP 세션 시작부터 종료까지    사용자별 데이터</p>
<p><strong>Application</strong>    애플리케이션(ServletContext) 시작부터 종료까지    전역 설정, 공통 리소스 관리</p>
<p><strong>WebSocket</strong>    웹소켓 세션 시작부터 종료까지    웹소켓 연결별 사용자 데이터</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Java Stream API 에 대해 알아보자]]></title>
            <link>https://velog.io/@junr_65535/Java-Stream%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/@junr_65535/Java-Stream%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Fri, 15 Nov 2024 15:30:53 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/junr_65535/post/04117b2d-ec3c-4c83-8fc5-602d5041cca9/image.png" alt=""></p>
<p>Java 8에서는 <strong>대량의 데이터</strong>를 효율적으로 처리하기 위해 <strong>Stream API</strong>가 도입되었다. 이는 데이터를 선언형 프로그래밍 방식으로 처리할 수 있게 하며, 다양한 데이터 연산을 간결하고 효율적으로 수행할 수 있는 기능을 제공한다.</p>
<hr>
<h3 id="stream과-for-루프의-성능-비교"><strong>Stream과 for 루프의 성능 비교</strong></h3>
<h4 id="1-stream의-성능">1. Stream의 성능</h4>
<p>Stream은 대용량 데이터를 처리할 때 더 효율적이다. 특히 병렬 처리가 가능하기 때문에 성능상의 이점을 제공한다. 
또한 종단 연산이 호출될때에 실행하고 (지연 연산) 중간 연산 과정에서 조건에 맞지 않는 데이터들이 추려지기 때문에 조건에 맞지 않는 데이터들은 이후 계산에 쓰이지 않으므로 대규모 데이터셋에 효율적일 수 있따.</p>
<h4 id="2-for-루프와의-비교">2. for 루프와의 비교</h4>
<p>그에 비해 for 루프는 작은 데이터셋이나 단순 연산을 수행할 때 빠르다. 이유는 <strong>오버헤드</strong>가 적기 때문이다.<br>for 루프는 별도의 객체 생성이나 함수 호출이 없어 실행 비용이 Stream보다 낮다.</p>
<blockquote>
<p><strong>오버헤드가 적다? 왜?</strong></p>
<p>for 루프는 별도의 추가 객체 생성이나 함수의 호출 없이 작동하기 때문에 실행시의 부가적인 비용이 stream에 비해 덜든다. ← <code>컴파일 시점에서 적용</code></p>
<p>Stream은 스트림객체를 생성하고 연산 체인을 설정하여 내부적으로 람다나 함수형 인터페이스를 사용하므로 초기세팅 비용에서 오버헤드가 발생할 수 있다는 뜻이다.</p>
</blockquote>
<h3 id="stream의-병렬-처리"><strong>Stream의 병렬 처리</strong></h3>
<p>Stream은 <code>parallel()</code> 메서드를 통해 병렬 처리가 가능하다. 그러나 병렬 처리를 사용할 때는 몇 가지 주의사항이 있다.</p>
<h4 id="1-스레드풀-공유-문제">1. 스레드풀 공유 문제</h4>
<p><code>parallel()</code> 메서드는 별도의 스레드풀을 생성하지 않고, <strong>공유 스레드풀(ForkJoinPool.commonPool)</strong>을 사용한다.<br>여러 병렬 스트림이 하나의 스레드풀을 공유하면 <strong>병목 현상</strong>이 발생할 수 있다.</p>
<h4 id="2-최적의-데이터-구조">2. 최적의 데이터 구조</h4>
<p>병렬 처리 성능을 극대화하려면 다음과 같은 <strong>참조 지역성이 높은</strong> 자료구조를 사용하는 것이 좋다:</p>
<ul>
<li><code>ArrayList</code>, <code>HashMap</code>, <code>HashSet</code>, <code>ConcurrentHashMap</code></li>
<li>원시 타입 배열(<code>int[]</code>, <code>long[]</code> 등)</li>
</ul>
<p>이들은 메모리에서 연속적으로 저장되므로, 캐싱 효율이 향상되어 처리 속도가 빨라진다.</p>
<blockquote>
<p><strong>캐싱 효율 향상된다?</strong>
메모리에서 연속적임으로 <code>Cache HitRate</code> 가 높다는 뜻이다. CPU 캐시 메모리는 인접한 메모리 공간을 함께 불러오기 때문에 배열 요소에 순차적으로 접근할 때에 메모리 접근 속도가 더 빠르다는 뜻이다.</p>
</blockquote>
<h4 id="3-잘못된-병렬-처리">3. 잘못된 병렬 처리</h4>
<p>parallel 사용 시 잘못된 병렬 처리를 할 경우 성능 향상은 커녕 성능이 내려갈 수 도 있다.
특히 <code>limit()</code> 메서드를 함께 사용하는 경우 병렬 처리 성능이 저하될 수 있다. 
그 이유는 parallel로 병렬 작업 수행 시 <code>limit(n)</code>이 n개의 결과만 가져오지 않기 때문이다. </p>
<p>예를 들어 <a href="https://ko.wikipedia.org/wiki/%EB%A9%94%EB%A5%B4%EC%84%BC_%EC%86%8C%EC%88%98">메르센 소수 문제</a> 를 둘 수 있다. 메르센 소수가 무엇인지는 궁금한 사람이 찾아보시라.
우리는 <strong>n번째 메르센 소수를 찾는 일이 1부터 n-1까지 메르센 소수를 찾는 일보다 훨씬 많은 비용이 드는 연산</strong>임을 알고만 있으면 된다.</p>
<p>아래는 20개의 메르센 소수를 구하는 JAVA코드이다.</p>
<pre><code class="language-java">public static void main(String[] args) {
    primes()
        .map(p -&gt; TWO.pow(p.intValueExact()).subtract(ONE)) // 메르센 수 생성: 2^p - 1
        .filter(mersenne -&gt; mersenne.isProbablePrime(50)) // 메르센 수가 소수인지 확인
        .limit(20) // 상위 20개의 메르센 소수만 선택
        .forEach(System.out::println); // 결과 출력
}</code></pre>
<p>해당 코드에서 성능을 높이기 위해서 스트림 파이프 라인의 <code>parallel()</code> 을 호출하면 병렬로 코드를 진행하여 성능이 좋아질까?</p>
<pre><code class="language-java">public static void main(String[] args) {
    primes()
        .parallel() // 병렬 스트림으로 변환
        .map(p -&gt; TWO.pow(p.intValueExact()).subtract(ONE)) // 메르센 수 생성: 2^p - 1
        .filter(mersenne -&gt; mersenne.isProbablePrime(50)) // 메르센 수가 소수인지 확인
        .limit(20) // 상위 20개의 메르센 소수만 선택
        .forEach(System.out::println); // 결과 출력
}</code></pre>
<p>아쉽게도 성능은 좋아지지 않는다. 오히려 CPU자원을 더 많이 잡아먹으면서 결과물은 나오지 않는다.
이유는 스트림 파이프 라인을 병렬화할때에 CPU코어가 남는다면 원소를 몇 개 더 처리한 후에 제한된 개수 이후의 결과를 버리는 알고리즘으로 돌아가기 때문이다.</p>
<p>해당 문제를 해결하려면 스레드에 분배하기 좋은 앞서 말했던 참조지역성이 좋은 자료구조를 채택하면 좋다. 또한 <code>Spliterator</code> 를 사용하면 작업할 스레드를 병렬적으로 나눌 수 있다.</p>
<pre><code class="language-java">import java.util.Spliterator;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

public class ParallelSpliteratorExample {
    public static void main(String[] args) {
        // 1부터 100까지의 숫자를 포함하는 스트림 생성
        Stream&lt;Integer&gt; stream = IntStream.range(1, 101).boxed();

        // 스트림에서 Spliterator 객체를 가져옴
        Spliterator&lt;Integer&gt; spliterator = stream.spliterator();

        // Spliterator를 사용하여 데이터를 병렬로 나누기
        Spliterator&lt;Integer&gt; split1 = spliterator.trySplit(); // 첫 번째 분할
        Spliterator&lt;Integer&gt; split2 = spliterator.trySplit(); // 두 번째 분할

        // 각 분할된 Spliterator로 병렬 스트림 생성
        Stream&lt;Integer&gt; parallelStream1 = StreamSupport.stream(spliterator, true);
        Stream&lt;Integer&gt; parallelStream2 = StreamSupport.stream(split1, true);
        Stream&lt;Integer&gt; parallelStream3 = StreamSupport.stream(split2, true);

        // 병렬 스트림의 각 요소를 처리하며 스레드 이름 출력
        System.out.println(&quot;Processing with spliterator 1:&quot;);
        parallelStream1.forEach(i -&gt; System.out.println(&quot;Thread &quot; + Thread.currentThread().getName() + &quot; is processing number: &quot; + i));

        System.out.println(&quot;\nProcessing with spliterator 2:&quot;);
        parallelStream2.forEach(i -&gt; System.out.println(&quot;Thread &quot; + Thread.currentThread().getName() + &quot; is processing number: &quot; + i));

        System.out.println(&quot;\nProcessing with spliterator 3:&quot;);
        parallelStream3.forEach(i -&gt; System.out.println(&quot;Thread &quot; + Thread.currentThread().getName() + &quot; is processing number: &quot; + i));
    }
}</code></pre>
<hr>
<h3 id="stream에서-사용-가능한-함수형-인터페이스"><strong>Stream에서 사용 가능한 함수형 인터페이스</strong></h3>
<p>Stream API는 <strong>함수형 인터페이스</strong>를 통해 다양한 연산을 지원한다. 주요 인터페이스는 다음과 같다:</p>
<ol>
<li><p><strong>Predicate<T></strong>  </p>
<ul>
<li>입력값을 테스트하여 <code>boolean</code> 결과를 반환한다.  </li>
<li><code>filter()</code> 메서드에서 사용된다.</li>
</ul>
</li>
<li><p><strong>Function&lt;T, R&gt;</strong>  </p>
<ul>
<li>입력값을 다른 타입으로 변환한다.  </li>
<li><code>map()</code> 메서드에서 사용된다.</li>
</ul>
</li>
<li><p><strong>Consumer<T></strong>  </p>
<ul>
<li>입력값을 소비하고 반환값이 없다.  </li>
<li><code>forEach()</code> 메서드에서 사용된다.</li>
</ul>
</li>
<li><p><strong>Supplier<T></strong>  </p>
<ul>
<li>입력 없이 값을 생성한다.  </li>
<li>Stream 초기화 시 사용된다.</li>
</ul>
</li>
<li><p><strong>BiFunction&lt;T, U, R&gt;</strong>  </p>
<ul>
<li>두 개의 입력값을 받아 결과를 반환한다.</li>
</ul>
</li>
<li><p><strong>UnaryOperator<T></strong>  </p>
<ul>
<li>같은 타입의 입력값을 변환한다.</li>
</ul>
</li>
<li><p><strong>BinaryOperator<T></strong>  </p>
<ul>
<li>같은 타입의 두 입력값을 연산한다.  </li>
<li><code>reduce()</code> 메서드에서 사용된다.</li>
</ul>
</li>
</ol>
<hr>
<h3 id="stream에서-외부-변수-사용-final-키워드"><strong>Stream에서 외부 변수 사용: final 키워드</strong></h3>
<p>Stream이나 람다식에서 <strong>외부 변수를 사용</strong>할 때는 반드시 해당 변수가 <strong>final</strong> 또는 <strong>사실상 final(effectively final)</strong>이어야 한다.<br>이는 <strong>스레드 안전성</strong>과 <strong>클로저(Closure)</strong> 개념 때문이다.</p>
<h4 id="이유">이유:</h4>
<p>람다 표현식이 외부 변수를 사용할 경우, 해당 변수는 변경되지 않아야 한다. 변경될 가능성이 있다면 스레드 간 데이터 경합이 발생할 수 있다.</p>
<h4 id="꼭-final을-붙여야-할까">꼭 final을 붙여야 할까?</h4>
<ul>
<li>명시적으로 <code>final</code>을 붙이지 않아도, <strong>변수가 변경되지 않으면</strong> 사실상 final로 간주되어 사용 가능하다.</li>
</ul>
<h4 id="예시-코드">예시 코드:</h4>
<pre><code class="language-java">List&lt;String&gt; names = Arrays.asList(&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;);

// 사실상 final인 변수
String prefix = &quot;Hello, &quot;;

names.stream()
    .map(name -&gt; prefix + name) // prefix 사용
    .forEach(System.out::println);</code></pre>
<p>위 예시에서 <code>prefix</code>는 변경되지 않으므로 람다식에서 안전하게 사용할 수 있다.</p>
<hr>
<h3 id="결론"><strong>결론</strong></h3>
<p>Java Stream은 대량 데이터를 효율적으로 처리할 수 있는 강력한 도구이다. 
성능을 극대화하려면 데이터 구조, 스레드에 대한 이해, 함수형 인터페이스를 잘 활용해야 한다. 
또한 외부 변수를 사용할 때는 final 키워드와 스레드 안전성을 고려하는 것이 중요하다. </p>
<p>  Stream API를 적절히 활용하면 간결하면서도 성능이 뛰어난 코드를 작성할 수 있을 것이다.</p>
<h3 id="참고">참고</h3>
<ol>
<li><a href="https://parkcheolu.tistory.com/15">Java 스트림과 병렬 스트림</a></li>
<li><a href="https://adjh54.tistory.com/167#2%20%EC%8A%A4%EB%A0%88%EB%93%9C%EC%9D%98%20%EA%B5%AC%EC%A1%B0-1">스레드의 구조와 병렬 처리</a></li>
<li><a href="https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%E2%9A%94%EF%B8%8F-%EC%93%B0%EB%A0%88%EB%93%9C-%EC%B0%A8%EC%9D%B4">프로세스와 스레드 차이</a></li>
<li><a href="https://inpa.tistory.com/entry/JCF-%F0%9F%A7%B1-ArrayList-vs-Vector-%EB%8F%99%EA%B8%B0%ED%99%94-%EC%B0%A8%EC%9D%B4-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0">ArrayList vs Vector 동기화 차이</a></li>
<li>Effective JAVA - 람다와 스트림</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CS_study] Synchronized 키워드에 대해 알아보자]]></title>
            <link>https://velog.io/@junr_65535/CSstudy-Synchronized-%ED%82%A4%EC%9B%8C%EB%93%9C%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/@junr_65535/CSstudy-Synchronized-%ED%82%A4%EC%9B%8C%EB%93%9C%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Fri, 15 Nov 2024 09:32:54 GMT</pubDate>
            <description><![CDATA[<p>Java에서 멀티스레드 프로그래밍을 하다 보면 동기화 문제가 자주 발생한다. </p>
<p>이때 Synchronized는 공유자원을 생성하여 해당 자원에 대한 동기화 영역을 생성하는데 도움을 준다.</p>
<h2 id="1-synchronized-키워드란">1. <strong>Synchronized 키워드란?</strong></h2>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/0add187b-a4ac-406b-8002-988f9478b70a/image.png" alt=""></p>
<p><code>Synchronized</code> : 자바에서 동기화 영역을 생성하는 키워드이다.</p>
<p>Synchrosized 처리된 객체나 메서드는 두 개 이상의 쓰레드가 동시 접근하는 것을 막는다.</p>
<p>즉 하나의 스레드가 해당 객체나 메서드를 사용하는 동안 다른 스레드가 접근하지 못하도록 Lock을 거는 것.</p>
<h3 id="그런데-두-개-이상의-스레드가-synchronized-처리된-객체나-메서드에-동시에-접근을-시도할때-어떤-스레드가-우선순위를-가지게--될까"><strong><em>그런데 두 개 이상의 스레드가 synchronized 처리된 객체나 메서드에 동시에 접근을 시도할때 어떤 스레드가 우선순위를 가지게  될까?</em></strong></h3>
<p>Java 스레드 스케줄러가 다음과 같은 원칙에 따라 어떤 스레드가 먼저 실행할지를 결정한다. </p>
<ol>
<li><p><strong>스레드 우선순위</strong></p>
<ul>
<li>자바 스레드는 우선순위를 가질 수 있음.</li>
<li>우선순위가 높은 스레드가 낮은 스레드보다 실행될 가능성이 큼.</li>
</ul>
</li>
<li><p><strong>JVM 스케줄링 정책</strong></p>
<ul>
<li>JVM 스레드 스케줄러가 운영체제의 스케줄러와 협력하여 실행 순서를 결정함.</li>
<li>타임 슬라이싱(Time Slicing)과 우선순위 기반 스케줄링 방식이 일반적.</li>
</ul>
</li>
<li><p><strong>Fairness 옵션</strong></p>
<ul>
<li><p><code>ReentrantLock</code> 클래스의 공정성 설정을 통해 대기 순서대로 접근 제어 가능.</p>
<pre><code class="language-java">  ReentrantLock lock = new ReentrantLock(true); // 공정성 설정</code></pre>
</li>
</ul>
</li>
</ol>
<hr>
<h2 id="2-synchronized-키워드-사용-위치별-동기화-방식-비교">2. <strong>Synchronized 키워드 사용 위치별 동기화 방식 비교</strong></h2>
<h3 id="1-인스턴스-메소드-동기화">1. <strong>인스턴스 메소드 동기화</strong></h3>
<ul>
<li><p><strong>설명</strong>: <code>synchronized</code> 키워드를 메소드 선언에 붙이면, 해당 메소드를 호출한 <strong>인스턴스(객체)</strong>를 기준으로 동기화가 이루어진다.</p>
</li>
<li><p><strong>특징</strong>:</p>
<ul>
<li>한 인스턴스에서 한 번에 한 스레드만 메소드를 실행할 수 있다.</li>
<li>다른 non-synchronized 메소드는 동시에 실행될 수 있다.</li>
</ul>
</li>
<li><p><strong>예제</strong>:</p>
<pre><code class="language-java">  public synchronized void add(int value) {
      this.count += value;
  }
</code></pre>
</li>
<li><p><strong>사용 시 주의점</strong>:</p>
<ul>
<li>객체당 한 스레드만 메소드를 실행하도록 보장한다.</li>
<li>클래스의 여러 인스턴스는 서로 독립적으로 동작한다.</li>
</ul>
</li>
</ul>
<hr>
<h3 id="2-스태틱-메소드-동기화">2. <strong>스태틱 메소드 동기화</strong></h3>
<ul>
<li><p><strong>설명</strong>: <code>static synchronized</code> 메소드는 해당 <strong>클래스 자체</strong>를 기준으로 동기화가 이루어진다.</p>
</li>
<li><p><strong>특징</strong>:</p>
<ul>
<li>클래스 레벨에서 동기화가 이루어진다.</li>
<li>해당 클래스의 모든 인스턴스가 공유하는 리소스에 대한 동기화가 필요할 때 사용한다.</li>
<li>한 번에 한 스레드만 static synchronized 메소드를 실행할 수 있다.</li>
</ul>
</li>
<li><p><strong>예제</strong>:</p>
<pre><code class="language-java">  public static synchronized void add(int value) {
      count += value;
  }
</code></pre>
</li>
<li><p><strong>사용 시 주의점</strong>:</p>
<ul>
<li>클래스 객체는 JVM 내에서 하나만 존재하므로, 클래스 간 자원을 보호해야 할 때 적합하다.</li>
</ul>
</li>
</ul>
<hr>
<h3 id="3-인스턴스-메소드-내부-블록-동기화">3. <strong>인스턴스 메소드 내부 블록 동기화</strong></h3>
<ul>
<li><p><strong>설명</strong>: 메소드 내부의 특정 코드 블록을 <code>synchronized</code> 키워드로 동기화할 수 있다.</p>
</li>
<li><p><strong>특징</strong>:</p>
<ul>
<li>동기화가 필요한 코드만 블록으로 지정할 수 있다.</li>
<li>동기화 기준은 <code>this</code> 객체(메소드를 호출한 객체) 또는 다른 객체로 설정할 수 있다.</li>
</ul>
</li>
<li><p><strong>예제</strong>:</p>
<pre><code class="language-java">  public void add(int value) {
      // 동기화되지 않은 코드
      synchronized(this) {
          this.count += value; // 이 부분만 동기화
      }
      // 동기화되지 않은 코드
  }
</code></pre>
</li>
<li><p><strong>사용 시 주의점</strong>:</p>
<ul>
<li>메소드 전체 동기화보다 더 세밀한 동기화가 가능하다.</li>
<li>다른 객체를 기준으로 동기화하면, 여러 스레드 간 독립적인 동작이 가능하다.</li>
</ul>
</li>
</ul>
<hr>
<h3 id="4-스태틱-메소드-내부-블록-동기화">4. <strong>스태틱 메소드 내부 블록 동기화</strong></h3>
<ul>
<li><p><strong>설명</strong>: 스태틱 메소드 내부의 특정 코드 블록을 동기화하며, <strong>클래스 객체</strong>를 기준으로 동기화가 이루어진다.</p>
</li>
<li><p><strong>특징</strong>:</p>
<ul>
<li>동기화 기준은 클래스 객체 (<code>ClassName.class</code>)이다.</li>
<li>클래스의 모든 스레드에 대해 동기화를 보장한다.</li>
</ul>
</li>
<li><p><strong>예제</strong>:</p>
<pre><code class="language-java">  public static void add(int value) {
      synchronized (MyClass.class) {
          count += value; // 이 부분만 동기화
      }
  }
</code></pre>
</li>
<li><p><strong>사용 시 주의점</strong>:</p>
<ul>
<li>클래스의 다른 스태틱 메소드와 동시 실행을 방지할 수 있다.</li>
</ul>
</li>
</ul>
<h3 id="종합-비교"><strong>종합 비교</strong></h3>
<table>
<thead>
<tr>
<th>유형</th>
<th>동기화 기준</th>
<th>동작 범위</th>
<th>특징</th>
</tr>
</thead>
<tbody><tr>
<td><strong>인스턴스 메소드</strong></td>
<td>호출한 객체 (<code>this</code>)</td>
<td>메소드 전체</td>
<td>객체 단위로 동기화되며, 한 객체당 한 스레드만 실행 가능하다.</td>
</tr>
<tr>
<td><strong>스태틱 메소드</strong></td>
<td>클래스 객체</td>
<td>메소드 전체</td>
<td>클래스 단위로 동기화되며, 모든 인스턴스가 공유한다.</td>
</tr>
<tr>
<td><strong>인스턴스 메소드 블록</strong></td>
<td>호출한 객체 (<code>this</code>)</td>
<td>특정 코드 블록</td>
<td>객체 단위로 동기화되며, 블록 내 코드만 한 번에 한 스레드만 실행 가능하다.</td>
</tr>
<tr>
<td><strong>스태틱 메소드 블록</strong></td>
<td>클래스 객체</td>
<td>특정 코드 블록</td>
<td>클래스 단위로 동기화되며, 특정 블록만 한 번에 한 스레드만 실행 가능하다.</td>
</tr>
</tbody></table>
<h2 id="3-synchronized의-효율성"><strong>3. Synchronized의 효율성</strong></h2>
<p>Synchronized는 데이터의 일관성을 보장해 주지만, 코드의 실행 시간이 길어질수록 다른 스레드의 대기 시간이 늘어나면서 시스템의 효율성이 떨어질 수 있다. </p>
<p>따라서 공유 객체를 사용하는 <strong>임계 영역(critical section)은 <code>꼭 필요한 부분에만</code> <code>최대한 작게</code></strong> 유지하는 것이 중요하다.</p>
<blockquote>
<p><strong>CleanCode 13.동시성</strong></p>
<p>p236, 동기화하는 부분을 최대한 작게 만들어라.임계 영역을 줄인다고 임계 영역의 크기를 키우지는 마라. 그러면 스레드간의 경쟁도 늘고 성능도 떨어진다.</p>
</blockquote>
<h2 id="4-synchronized-의-대체-기법">4. Synchronized 의 대체 기법.</h2>
<p>Java 5부터는 스레드 안전한 java.util.concurrent 패키지가 제공되면서 보다 정교한 동기화 제어가 가능해졌다. </p>
<p><code>ReentrantLock</code>, <code>ReadWriteLock</code>, <code>Semaphore</code>, <code>Atomic</code> 클래스들이 대표적이다.</p>
<ul>
<li>ReentrantLock<ul>
<li>synchronized보다 더 세밀한 제어 가능</li>
<li>락의 획득과 해제를 명시적으로 관리</li>
<li>공정성 설정 가능</li>
</ul>
</li>
<li>ReadWriteLock<ul>
<li>읽기와 쓰기를 구분하여 동기화 적용</li>
<li>다수의 스레드가 동시에 읽기 가능</li>
<li>쓰기 작업 시에만 쓰기 락 적용</li>
</ul>
</li>
<li>Semaphore<ul>
<li>스레드 수를 제어</li>
<li>특정 리소스에 접근 가능한 최대 스레드 수 제한</li>
</ul>
</li>
<li>Atomic 클래스들 (AtomicInteger, AtomicLong 등)<ul>
<li>원자적인 연산 제공</li>
<li>동기화 없이 안전한 연산 수행 가능</li>
</ul>
</li>
</ul>
<blockquote>
<p><strong>CleanCode 13.동시성</strong></p>
<p>p233, 언어가 제공하는 스레드 안전한 클래스를 검토하고 자바에서는 해당 클래스들을 익혀라.</p>
</blockquote>
<h2 id="5-thread-local">5. Thread Local?</h2>
<p><code>Synchronize</code> 가 스레드의 공유자원을 설정하는 키워드라면  <code>ThreadLocal</code>은 Java에서 각 스레드가 독립적으로 자원을 보유할 수 있도록 도와주는 클래스이다. </p>
<h3 id="threadlocal-사용법"><strong>ThreadLocal 사용법</strong></h3>
<ol>
<li><p><strong>ThreadLocal 객체 생성</strong></p>
<pre><code class="language-java"> private ThreadLocal&lt;String&gt; myThreadLocal = new ThreadLocal&lt;&gt;();
</code></pre>
<p> 각 스레드는 이 객체를 통해 독립된 값을 설정하고 가져올 수 있다.</p>
</li>
<li><p><strong>값 설정</strong></p>
<pre><code class="language-java"> myThreadLocal.set(&quot;Thread-specific value&quot;);
</code></pre>
<p> 현재 스레드에 특정 값을 저장한다.</p>
</li>
<li><p><strong>값 가져오기</strong></p>
<pre><code class="language-java"> String value = myThreadLocal.get();
</code></pre>
<p> 현재 스레드에 설정된 값을 반환한다.</p>
</li>
<li><p><strong>값 제거</strong></p>
<pre><code class="language-java"> myThreadLocal.remove();
</code></pre>
<p> 현재 스레드의 값을 초기화한다.</p>
</li>
</ol>
<hr>
<h3 id="특징"><strong>특징</strong></h3>
<ul>
<li><p><strong>독립적인 변수 관리</strong>: 각 스레드가 독립적으로 값을 보유하므로 변수 간섭이 없다.</p>
</li>
<li><p><strong>초기값 설정 가능</strong>: <code>ThreadLocal</code>의 <code>initialValue()</code> 메소드를 오버라이드하여 기본값을 제공할 수 있다.</p>
<pre><code class="language-java">  private ThreadLocal&lt;Integer&gt; threadLocal = new ThreadLocal&lt;&gt;() {
      @Override
      protected Integer initialValue() {
          return 0;
      }
  };
</code></pre>
</li>
</ul>
<hr>
<h3 id="inheritablethreadlocal"><strong>InheritableThreadLocal</strong></h3>
<ul>
<li><p>기본적으로 <code>ThreadLocal</code>은 부모 스레드의 값을 자식 스레드가 참조하지 못한다.</p>
</li>
<li><p><code>InheritableThreadLocal</code>을 사용하면 부모 스레드의 값을 자식 스레드에 전달할 수 있다.</p>
<pre><code class="language-java">  private InheritableThreadLocal&lt;String&gt; inheritableThreadLocal = new InheritableThreadLocal&lt;&gt;();
</code></pre>
</li>
<li><p>부모 스레드의 초기값을 자식 스레드가 물려받지만, 자식 스레드가 값을 변경해도 부모 스레드에는 영향을 미치지 않는다.</p>
</li>
</ul>
<hr>
<h3 id="장점"><strong>장점</strong></h3>
<ol>
<li><strong>스레드 안전성</strong>:  각 스레드가 고유한 값을 보유한다.</li>
<li><strong>사용 편의성</strong>: 스레드별로 데이터를 저장하고 관리하는 코드를 간결하게 작성할 수 있다.</li>
<li><strong>유연성</strong>: <code>initialValue()</code>와 <code>InheritableThreadLocal</code>을 활용해 초기값과 상속 관계도 커스텀하여 설정 가능하다.</li>
</ol>
<hr>
<h3 id="사용-예시"><strong>사용 예시</strong></h3>
<ol>
<li><p><strong>각 스레드에 고유 값 부여</strong></p>
<pre><code class="language-java"> private ThreadLocal&lt;Integer&gt; threadLocal = new ThreadLocal&lt;&gt;() {
     @Override
     protected Integer initialValue() {
         return 0;
     }
 };

 public void increment() {
     threadLocal.set(threadLocal.get() + 1);
 }

 public Integer getValue() {
     return threadLocal.get();
 }
</code></pre>
</li>
<li><p><strong>InheritableThreadLocal 사용</strong></p>
<pre><code class="language-java"> private InheritableThreadLocal&lt;String&gt; threadLocal = new InheritableThreadLocal&lt;&gt;();

 public void setParentValue(String value) {
     threadLocal.set(value);
 }

 public String getValue() {
     return threadLocal.get();
 }
</code></pre>
</li>
</ol>
<hr>
<h3 id="주의점"><strong>주의점</strong></h3>
<ol>
<li><strong>메모리 누수</strong>: 스레드가 종료되면 <code>ThreadLocal</code>에 저장된 값을 반드시 <code>remove()</code>로 제거해야 한다.</li>
<li><strong>ThreadLocal 변수 관리</strong>: 관리되지 않는 <code>ThreadLocal</code> 객체는 값이 참조되지 않더라도 GC에 의해 수거되지 않을 수 있다.</li>
</ol>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://parkcheolu.tistory.com/17">자바 스레드 로컬 - ParkCheolu</a></li>
<li>Clean Code. 13 동시성</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Amazon IAM 이란 무엇인가?]]></title>
            <link>https://velog.io/@junr_65535/Amazon-IAM-%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</link>
            <guid>https://velog.io/@junr_65535/Amazon-IAM-%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</guid>
            <pubDate>Tue, 12 Nov 2024 15:11:58 GMT</pubDate>
            <description><![CDATA[<aside>
💡

<p>사이드 프로젝트 중에 IAM 을 통해 협업해야할 일이 생겼다.</p>
</aside>

<h1 id="aws-identity-and-access-managementiam-이란">AWS Identity and Access Management(IAM) 이란?</h1>
<p>AWS 에서 사용자의 접근권한을 관리하는 서비스</p>
<p>IAM을 통해서 사용자는 root 계정의 암호나 액세스 키를 공유하지 않고도 AWS root 계정 내 자원을 사용하는 모든 공유 사용자들에게 <strong>세분화된 권한</strong>을 부여할 수 있다.</p>
<h3 id="어떤-것을-할-수-있나">어떤 것을 할 수 있나?</h3>
<ul>
<li><p>AWS 계정 관리 및 리소스, 사용자, 서비스의 권한 제어</p>
</li>
<li><p>root 계정을 공유하려는 IAM 사용자 생성 및 관리 &amp; 해당 계정의 보안-</p>
<p>  → 심지어 해당 IAM 사용자의 비밀번호 정책까지 수정 가능하다.</p>
</li>
<li><p>다른 계정과 리소스 공유</p>
<ul>
<li>Identity Federation (외부 인증 시스템과 연동하여 AWS 리소스에 접근할 수 있게 연동 가능)</li>
</ul>
</li>
<li><p>계정에 별명 부여 가능, 로그인 주소 생성 가능</p>
<p>  → 계정에 쉽게 기억할 수 있는 별명을 지정하고, 사용자 정의 로그인 URL을 만들 수 있다.</p>
</li>
<li><p>IAM은 글로벌 서비스</p>
<ul>
<li>모든 IAM 계정은 전체 리전에서 유일하다.</li>
</ul>
</li>
</ul>
<h2 id="iam-구성">IAM 구성</h2>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/5d6d937a-c96a-4e17-b5af-bf85a35b0adb/image.png" alt=""></p>
<h3 id="사용자-user">사용자 (User)</h3>
<ul>
<li>실제 AWS 를 사용하는 사람과 서비스 (EC2 그 자체도 사용자가 될 수 있다.)</li>
</ul>
<h3 id="그룹-group">그룹 (Group)</h3>
<ul>
<li>사용자의 집합<ul>
<li>그룹에 속한 사용자는 그룹에 부여된 권한 행사 가능</li>
<li>하나의 사용자는 최대 10개의 그룹에 속할 수 있다!</li>
</ul>
</li>
</ul>
<h3 id="역할-role">역할 (Role)</h3>
<blockquote>
<p>기존 그룹이 너무 많아져 임시적인 해당 권한을 행사하는 역할이라는 새로운</p>
</blockquote>
<ul>
<li>AWS 리소스에 부여하여 AWS 리소스가 무엇을 할 수 있는가를 정의</li>
<li>혹은 다른 사용자의 역할을 부여받아 사용가능</li>
<li>다른 자격에 대해 신뢰관계 구축 가능</li>
<li>역할을 바꾸어 가며 서비스를 사용 가능</li>
</ul>
<h3 id="정책-policy">정책 (Policy)</h3>
<ul>
<li><p>사용자와 그룹, 역할이 무엇을 할 수있는지에 관한 문서</p>
<ul>
<li><p>JSON 형식으로 정의</p>
</li>
<li><p>어떤 주제로 권한을 줄지에 따라 여러가지 정책 유형이 있을 수 있음.</p>
<h3 id="aws-iam-정책-유형">AWS IAM 정책 유형</h3>
<ul>
<li><strong>자격 증명 기반 정책:</strong> IAM 사용자, 그룹 또는 역할에 직접 연결되는 정책이다.<ul>
<li>관리형 정책: AWS에서 사전 정의한 정책이나 사용자가 생성한 재사용 가능한 정책이다.</li>
<li>인라인 정책: 특정 사용자, 그룹, 역할에 직접 포함된 정책이다.</li>
</ul>
</li>
<li><strong>리소스 기반 정책:</strong> AWS 리소스(예: S3 버킷, IAM 역할 신뢰 정책)에 직접 연결되는 정책이다.</li>
<li><strong>권한 경계:</strong> IAM 엔터티(사용자 또는 역할)에 대해 최대 권한을 설정하는 고급 기능이다.</li>
<li><strong>조직 SCP (서비스 제어 정책):</strong> AWS Organizations에서 조직 또는 조직 단위(OU)의 권한을 관리하는 정책이다.</li>
<li><strong>액세스 제어 목록 (ACL):</strong> 주로 S3 버킷에서 사용되며, 리소스에 접근할 수 있는 주체와 작업을 정의한다.</li>
<li><strong>세션 정책:</strong> 역할을 수임하거나 사용자를 위해 임시 세션을 생성할 때 권한을 추가로 제한하는 정책이다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="iam-리소스">IAM 리소스</h2>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/ef88e2ad-d892-40b6-ac34-22aebad793d1/image.png" alt=""></p>
<p>IAM 을 구성하는 리소스는 다음과 같다. 해당 리소스들은 IAM 콘솔에서 추가, 편집, 제거할 수있다.</p>
<ol>
<li><p><strong>Principal</strong> (주체)</p>
<p> AWS 리소스를 요청할 수 있는 주체이다.</p>
<ul>
<li><code>Root user</code>: AWS 계정 소유자의 계정으로 모든 권한을 가진다.<ul>
<li>관리 목적 이외에 다른 용도로 사용하면 안된다!<ul>
<li>탈취되었을 때 복구가 매우매우매우매우 어렵기 때문에 <code>MFA(일회용 패스워드) 사용하여 로그인</code>하는 것을 권장</li>
</ul>
</li>
</ul>
</li>
<li><code>IAM user</code> : 리소스에 접근할 수 있는 사용자. 어플리케이션도 포함된다.<ul>
<li>설정 시 콘솔 로그인 권한 부여 가능<ul>
<li>어플리케이션 같은 경우에 콘솔 로그인은 필요 없다.</li>
</ul>
</li>
<li>설정 시 Root 계정의 AWS 서비스 이용 가능<ul>
<li><code>Access Key</code></li>
<li><code>Secret Access Key</code></li>
</ul>
</li>
<li><code>Admin Access</code> 권한을 설정하더라도 별도의 설정을 하지 않으면 <strong>Billing 기능을 사용할 수 없</strong></li>
</ul>
</li>
<li><code>IAM role</code>: 리소스에 접근할 수 있는 역할</li>
</ul>
</li>
<li><p><strong>Entity</strong> (엔티티)</p>
<p> 주체 중에서 실제 AWS에서 리소스에 액세스하는 역할을 하는 요소들이다.</p>
<ul>
<li><code>IAM user</code>: AWS 계정 내에서 생성된 사용자로, 인증 후 리소스에 접근할 수 있다.</li>
<li><code>IAM role</code>: 특정 역할을 수행하도록 부여된 권한이다. 다른 계정의 서비스나 애플리케이션이 이 역할을 통해 AWS 리소스에 접근할 수 있다.</li>
</ul>
</li>
<li><p><strong>Identity</strong> (아이덴티티)</p>
<p> AWS 내에서 인증을 통해 리소스에 접근할 수 있는 모든 엔티티이다.</p>
<ul>
<li><code>IAM group</code>: 여러 사용자를 묶어 동일한 정책을 부여할 수 있도록 만든 그룹이다.</li>
</ul>
</li>
<li><p><strong>Resource</strong> (리소스)</p>
<p> AWS에서 관리되는 모든 리소스를 포함하는 큰 영역이다.</p>
<ul>
<li><code>Policy</code>: IAM 엔티티나 리소스에 접근할 수 있는 권한을 정의한 문서이다.</li>
<li><code>Instance profile</code>: EC2 인스턴스가 역할을 통해 리소스에 접근할 수 있게 하는 프로파일이다.</li>
<li><code>OIDC provider</code>: OpenID Connect 프로토콜을 사용하여 IAM 역할과 외부 ID 제공자 간 신뢰 관계를 설정할 때 사용된다.</li>
<li><code>기타 등등</code></li>
</ul>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/aa690669-d2c6-4b22-a1ca-2a1b6654d255/image.png" alt=""></p>
<h2 id="iam-권한-검증-절차">IAM 권한 검증 절차</h2>
<p>실제로 사용자가 AWS 에 접근할 때 어떤 과정으로 권한 검증이 이뤄질까?</p>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/142bd59a-723d-46ce-9a40-02ec5f5b67fd/image.png" alt=""></p>
<p>다음으로는 IAM 설정 과정에 대해 설명하겠다.</p>
<h3 id="참고">참고</h3>
<ul>
<li><a href="https://docs.aws.amazon.com/ko_kr/IAM/latest/UserGuide/intro-iam-features.html">AWS Identity and Access Management 왜 IAM을 사용해야 하나요? - AWS Identity and Access Management</a></li>
<li><a href="https://inpa.tistory.com/entry/AWS-%F0%9F%93%9A-IAM-%EA%B0%9C%EB%85%90-%EC%9B%90%EB%A6%ACuser-group-policy-role-IAM-%EA%B3%84%EC%A0%95-%EC%A0%95%EC%B1%85-%EC%83%9D%EC%84%B1">인파 - [AWS] IAM 개념 원리 &amp; IAM 계정 · 정책 생성하기</a></li>
<li><a href="https://www.youtube.com/watch?v=lcly_aIq1KI&amp;ab_channel=AWS%EA%B0%95%EC%9D%98%EC%8B%A4">AWS 강의실 쉽게 설명하는 AWS 기초 강좌 5: IAM 기초</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[트러블 슈팅] Permission to {Repogitory} denied to github-actions[bot].]]></title>
            <link>https://velog.io/@junr_65535/%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%85-Permission-to-Repogitory-denied-to-github-actionsbot</link>
            <guid>https://velog.io/@junr_65535/%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%85-Permission-to-Repogitory-denied-to-github-actionsbot</guid>
            <pubDate>Fri, 08 Nov 2024 15:14:54 GMT</pubDate>
            <description><![CDATA[<p><strong>문제 상황</strong></p>
<ul>
<li><p>어떤 것을 하려다가 문제가 발생했는가?</p>
<p>  간단한 프로젝트를 Actions로 브런치를 생성하여 푸시하는 과정에서 문제가 일어남.</p>
</li>
<li><p>발생한 환경, 프로그램</p>
<p>  <code>Vue.js</code> , <code>Github Actions</code> </p>
</li>
<li><p>발생한 문제(에러)</p>
<p>  <img src="https://velog.velcdn.com/images/junr_65535/post/503f9ed1-fff8-4ff8-87f5-16f0cb94199a/image.png" alt=""></p>
</li>
</ul>
<pre><code>```bash
Push the commit or tag
  /usr/bin/git push origin gh-pages
  remote: Permission to lee-JunR/LuckyDraw.git denied to github-actions[bot].
  fatal: unable to access &#39;https://github.com/lee-JunR/LuckyDraw.git/&#39;: The requested URL returned error: 403
  Error: Action failed with &quot;The process &#39;/usr/bin/git&#39; failed with exit code 128&quot;
```</code></pre><p><strong>원인</strong></p>
<ul>
<li><p>원인</p>
<p>  <code>403</code> 에러면 권한 문제이므로 해당 프로젝트 설정의 </p>
</li>
</ul>
<p><strong>최종 해결</strong></p>
<ul>
<li><p>해결방법</p>
<p>  <img src="https://velog.velcdn.com/images/junr_65535/post/e2fa5bf3-5727-4f19-a95d-918a36846f8c/image.png" alt=""></p>
</li>
</ul>
<pre><code>해당 Repo의 `Settings` &gt; `Actions` &gt; `General` &gt; `Workflow permissions` 에서 

**Read and write permissions 를 체크해준다.**</code></pre><blockquote>
<p><strong><code>4. Workflow permissions (워크플로우 권한)</code></strong></p>
<p>이 설정은 <strong>워크플로우가 실행될 때</strong> <code>GITHUB_TOKEN</code><strong>에 부여되는 기본 권한</strong>을 정의한다.</p>
<ul>
<li><p><strong>Read and write permissions (읽기 및 쓰기 권한)</strong>:</p>
<p>  워크플로우는 리포지토리 내에서 <strong>모든 범위에 대해 읽기 및 쓰기 권한</strong>을 가집니다. 이 옵션은 더 많은 제어 권한이 필요한 경우에 사용됩니다.</p>
</li>
<li><p><strong>Read repository contents and packages permissions (리포지토리 내용 및 패키지 읽기 권한)</strong>:</p>
<p>  워크플로우는 리포지토리의 <strong>내용 및 패키지 범위에 대해 읽기 권한만</strong> 가집니다. 보다 제한적인 권한이 필요한 경우에 사용됩니다.</p>
</li>
</ul>
</blockquote>
<h2 id="결과">결과</h2>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/89d450a4-62cb-488f-97f9-bb0c50a0429b/image.png" alt="">
<a href="https://lee-junr.github.io/LuckyDraw/">https://lee-junr.github.io/LuckyDraw/</a>
잘된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CS_study] JVM 이란 무엇인가?]]></title>
            <link>https://velog.io/@junr_65535/JVM-%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</link>
            <guid>https://velog.io/@junr_65535/JVM-%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</guid>
            <pubDate>Thu, 07 Nov 2024 17:59:50 GMT</pubDate>
            <description><![CDATA[<h1 id="서론">서론</h1>
<p>CS 스터디를 시작했다. 기본부터 시작하는 CS 스터디... 알고 있다고 생각한 개념도 설명하려니 알고 있는게 아니었다는 생각이 들었다. 휘발되기전에 정리해두자.</p>
<h2 id="high-level-programming-language-들의-실행-방식">High Level Programming Language 들의 실행 방식</h2>
<p>세상에는 크게 두 종류의 프로그래밍 언어가 있다.</p>
<ol>
<li>인터프리터 언어 - <code>Python</code></li>
</ol>
<ul>
<li>장점:<ul>
<li>빠른 개발 및 디버깅</li>
<li>시스템 이식성 좋음</li>
</ul>
</li>
<li>단점:<ul>
<li>상대적으로 느린 실행 속도</li>
<li>소스 코드 노출 위험</li>
</ul>
</li>
</ul>
<ol start="2">
<li>컴파일러 언어 - <code>C</code>, <code>JAVA</code></li>
</ol>
<ul>
<li>장점:<ul>
<li>빠른 실행 속도</li>
<li>코드 최적화 가능</li>
<li>소스 코드 보호</li>
</ul>
</li>
<li>단점:<ul>
<li>긴 개발 및 테스트 시간</li>
<li>플랫폼 종속성</li>
</ul>
</li>
</ul>
<p>하지만 Java는 단순한 컴파일러 언어가 아니다. Java는 코드를 바이트 코드로 변환하는 컴파일러와 JVM 실행 엔진 내의 인터프리터를 동시에 갖추고 있다.</p>
<h1 id="jvm이란-무엇인가">JVM이란 무엇인가?</h1>
<p>과거에는 윈도우에서 C 언어로 작성된 코드를 서버 컴퓨터나 다른 운영체제에서 실행하려면 각 운영체제에 맞는 기계어로 번역해주는 컴파일러가 필요했다. 이로 인해, “내 컴퓨터에서는 작동하지 않는데?“라는 문제가 빈번하게 발생했다.</p>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/ab8421a0-fe6f-4ac7-849d-6a3455f1abe0/image.png" alt=""></p>
<p>이 문제를 해결하기 위해 Java는 컴파일러와 운영체제 사이에 JVM이라는 한층의 레이어를 추가했다.
<img src="https://velog.velcdn.com/images/junr_65535/post/b08eb079-0d5f-491d-81e7-5810a9021249/image.png" alt=""></p>
<ol>
<li>기존 언어의 실행 방식: </li>
</ol>
<ul>
<li>소스코드 → OS에 맞는 컴파일러 → 기계어 → 해당 OS에서 실행</li>
</ul>
<ol start="2">
<li>Java의 실행 방식: </li>
</ol>
<ul>
<li>소스코드 → 컴파일러 → 바이트코드 → JVM → 해당 OS에서 실행</li>
</ul>
<p>Java는 소스 코드를 바이트 코드로 컴파일한 후, JVM이 이 바이트 코드를 읽어 각 OS에서 실행될 수 있는 형태로 해석한다. JVM 덕분에 Java는 플랫폼 독립적인 실행 환경을 제공할 수 있다.</p>
<p>프로그램을 실행하기 위해 JVM이라는 가상 머신으로 각 OS위에 한층의 가상화를 쌓아서 플랫폼 독립적인 실행 환경을 제공한 것이다.</p>
<h2 id="jvm의-주요-기능">JVM의 주요 기능</h2>
<ul>
<li><strong>바이트코드 해석 및 실행</strong>: Java 컴파일러가 생성한 바이트코드를 해석하고, 각 OS에 맞는 기계어로 변환해 실행한다.</li>
<li><strong>메모리 관리</strong>: 힙(Heap)과 스택(Stack) 메모리를 관리하며, 가비지 컬렉터(GC)를 통해 불필요한 메모리를 해제한다.</li>
<li><strong>보안 및 로드</strong>: 프로그램을 안전하게 로드하고 실행하기 위한 보안 메커니즘을 제공한다.</li>
</ul>
<h3 id="그럼-자바-말고-다른-언어는-jvm-위에-올릴-수-없나요">그럼, 자바 말고 다른 언어는 JVM 위에 올릴 수 없나요?</h3>
<p>자바뿐만 아니라 Kotlin, Groovy 등도 JVM 위에서 실행 가능하다. 이들 언어는 Java와 마찬가지로 바이트 코드로 컴파일되어 JVM에서 실행된다.</p>
<h3 id="반대로-jvm-계열-언어를-일반적으로-컴파일해서-사용할-순-없나요">반대로 JVM 계열 언어를 일반적으로 컴파일해서 사용할 순 없나요?</h3>
<p>가능할 것으로 생각된다. 만약 다른 프로그래밍 언어처럼 기계어 수준으로 일반적인 컴파일을 진행한다면, Java의 플랫폼 독립성과 동적 최적화의 장점을 잃게 되어 비효율적일 수 있다.</p>
<h3 id="vm을-사용함으로써-얻을-수-있는-장점과-단점에-대해-설명해-주세요">VM을 사용함으로써 얻을 수 있는 장점과 단점에 대해 설명해 주세요.</h3>
<ul>
<li><strong>장점</strong>:<ul>
<li><strong>플랫폼 독립성</strong>: JVM 덕분에 Java 프로그램은 다양한 운영체제에서 동일하게 실행될 수 있다.</li>
<li><strong>메모리 관리</strong>: 가비지 컬렉터를 통해 개발자가 직접 메모리 해제 작업을 하지 않아도 된다.</li>
</ul>
</li>
<li><strong>단점</strong>:<ul>
<li><strong>성능 오버헤드</strong>: 가상 머신 위에서 실행되기 때문에 네이티브 언어에 비해 성능이 떨어질 수 있다.</li>
<li><strong>시작 시간</strong>: JVM 초기 구동 시간이 다소 길어질 수 있다.</li>
</ul>
</li>
</ul>
<h3 id="jvm과-내부에서-실행되고-있는-프로그램은-부모-프로세스---자식-프로세스-관계를-갖고-있다고-봐도-무방한가요">JVM과 내부에서 실행되고 있는 프로그램은 부모 프로세스 - 자식 프로세스 관계를 갖고 있다고 봐도 무방한가요?</h3>
<h3 id="부모-프로세스---자식-프로세스-관계가-무엇인가">부모 프로세스 - 자식 프로세스 관계가 무엇인가?</h3>
<p>운영체제의 프로세스 관계에서 부모 프로세스는 다른 프로세스를 생성하고, 자식 프로세스는 부모의 메모리와 환경을 복사하여 독립적으로 실행된다. (각각 고유한 메모리 공간과 PID를 가짐)</p>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/24b6f18e-1c38-44c5-b741-6707ed354f0b/image.png" alt=""></p>
<p>하지만 JVM은 운영체제 내에서 독립적인 프로세스로 실행되며, Java 프로그램은 JVM 프로세스 내부에서 스레드로 동작한다. 
JVM이 실행하는 각 Java 프로그램의 스레드들은 동일한 메모리 공간(Heap)을 공유한다. 
따라서 JVM과 프로그램은 프로세스-스레드 관계로, 부모-자식 프로세스 관계가 아니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[KB_최종 프로젝트] 고도화 ]]></title>
            <link>https://velog.io/@junr_65535/KB%EC%B5%9C%EC%A2%85-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B3%A0%EB%8F%84%ED%99%94</link>
            <guid>https://velog.io/@junr_65535/KB%EC%B5%9C%EC%A2%85-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B3%A0%EB%8F%84%ED%99%94</guid>
            <pubDate>Mon, 04 Nov 2024 07:36:25 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/junr_65535/post/ca6e677c-6a54-45e2-a7de-beba8a6823d3/image.png" alt="">
KB IT’s Your Life 교육과정은 프로젝트 발표 이후로 1주일의 고도화 기간이 주어진다.</p>
<p>고도화를 하기 전에 KPT 회고를 진행했고 해당 회고를 바탕으로 고도화를 설계했다.</p>
<h2 id="keep-유지할-점">Keep (유지할 점)</h2>
<p>프로젝트에서 내가 해야할 과제가 주어지면 해당 과제를 풀어가는 과정을 연구노트 및 트러블 슈팅 페이지로 정리하며 문서화를 진행했다. </p>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/019e8c97-c39c-4ed8-a9d4-5f746ef5d848/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/6bdee31b-55bf-41c2-95bb-197c5dfcaabc/image.png" alt=""></p>
<p>내가 한 일을 문서화하면서 자신이 뭘 알고 있는지 다시 한번 확인할 수 있었고 다른 팀원에게 설명할때도 진행상황을 명확히 공유할 수 있어 좋았다.</p>
<h2 id="problem-문제점">Problem (문제점)</h2>
<p>프로젝트 진행 중 겪었던 주요 어려움은 팀원 간의 능력과 가용 시간 차이로 인한 공정하고 효율적인 업무 배분이었다. 구체적으로 다음과 같은 문제점들이 있었다.</p>
<ul>
<li>팀원별 기술 숙련도와 프로젝트 투자 가능 시간의 차이로 인한 업무 처리 시간과 효율성의 불균형</li>
<li>제한된 가용 시간을 가진 팀원들로 인한 일정 지연 발생</li>
<li>팀원들의 포트폴리오 요구사항을 고려한 업무 배정의 어려움</li>
<li>업무 분배의 형평성과 프로젝트의 효율적 진행 사이의 균형 유지의 어려움</li>
</ul>
<p>이러한 문제점들로 인해 전체 프로젝트 일정 관리에 문제가 생겼다.</p>
<h2 id="try-시도할-점">Try (시도할 점)</h2>
<p>그래서 우리팀은 일주일의 고도화 기간을 진행하기 전에  투표를 진행해서 해야할 일을 난이도 별로 세분화하여 리스트업하고  가용시간을 투표를 받아 업무를 분배하기로 했다!</p>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/35769022-0c47-4ef4-983f-f076ceaca66e/image.png" alt=""></p>
<ol>
<li>해야 할 일과 가용 시간을 명확히 확인하고 공정하게 분배하기</li>
<li>모든 팀원이 연구 노트를 작성하고, 이를 서로 읽고 공유하는 시간을 갖기</li>
</ol>
<p>이러한 노력을 통해 우리 팀은 고도화 기간 동안 더욱 효율적이고 공정한 프로젝트 진행을 할 수 있었습니다. 각 팀원의 강점을 살리면서도 모두가 참여할 수 있는 환경을 만들어 프로젝트의 질을 높일 수 있었습니다.</p>
<h2 id="고도화-기간-동안의-주요-개선-사항">고도화 기간 동안의 주요 개선 사항</h2>
<ol>
<li>동시성 개선 : 기존 Redis 기반으로 되어있던 코드를 레코드락으로 전환시켜서 어느정도의 성능차이가 있는지 알아보았음.</li>
<li>코드 리팩토링: 테스트 코드 최적화 및 중복 코드를 제거하고 모듈화를 개선하여 코드의 가독성과 유지보수성을 높였음</li>
</ol>
<h2 id="결론">결론</h2>
<p>다른 프로젝트를 할 때에도 고도화 기간을 따로 갖는것이 좋을 것 같다
발표 기한이 끝났다고 정말로 끝내는 프로젝트가 아니라 갈무리하는 경험을 통해서 프로젝트의 완성도를 챙길 수 있어 앞으로도 해당 기간을 갖고 진행할 예정이다.</p>
<p><img src="https://velog.velcdn.com/images/junr_65535/post/e5254d03-6f66-43fa-8e1b-12d39bedf28e/image.png" alt=""></p>
]]></description>
        </item>
    </channel>
</rss>