<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>rohdoo_mani.log</title>
        <link>https://velog.io/</link>
        <description>해적왕이 될 사나이</description>
        <lastBuildDate>Mon, 15 Sep 2025 11:04:25 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>rohdoo_mani.log</title>
            <url>https://velog.velcdn.com/images/rohdoo_mani/profile/30cea186-e288-46c5-9732-f469e50f67b5/image.webp</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. rohdoo_mani.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/rohdoo_mani" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[운영체제 복습 (2주차)]]></title>
            <link>https://velog.io/@rohdoo_mani/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%EB%B3%B5%EC%8A%B5-2%EC%A3%BC%EC%B0%A8</link>
            <guid>https://velog.io/@rohdoo_mani/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%EB%B3%B5%EC%8A%B5-2%EC%A3%BC%EC%B0%A8</guid>
            <pubDate>Mon, 15 Sep 2025 11:04:25 GMT</pubDate>
            <description><![CDATA[<h4 id="io-작동-방식">I/O 작동 방식</h4>
<p>I/O 장치와 CPU는 동시에 작동할 수 있다. I/O 작업은 기본적으로 장치(Device)와 컨트롤러(Controller)의 로컬 버퍼 사이에서 데이터를 이동하는 것을 의미한다. </p>
<p>여기서 CPU는 직접 I/O 장치와 통신하는 대신, 컨트롤러의 버퍼와 메인 메모리 사이에서 데이터를 옮기는 역할을 한다.</p>
<p>각 I/O 장치는 장치 컨트롤러라는 전담 관리자를 가진다. 이 컨트롤러는 자체적인 로컬 버퍼와 특별한 <strong>레지스터(register)</strong>를 가지고 있어서, I/O 장치와의 통신을 제어한다.</p>
<p>A device driver for every device controller라는 문장의 의미는 다음과 같다.</p>
<p><strong>장치 드라이버(Device Driver)</strong>는 해당 장치 컨트롤러의 세부 사항을 알고 있는 소프트웨어이다.</p>
<p>이 드라이버는 운영체제(OS)의 나머지 부분에 <strong>균일한 인터페이스(uniform interface)</strong>를 제공한다.</p>
<p>예를 들어, open, close, read, write와 같은 표준화된 함수들을 통해 개발자가 복잡한 하드웨어의 작동 방식을 일일이 알지 못해도 쉽게 I/O 작업을 할 수 있게 한다. 즉, OS와 애플리케이션이 장치와 소통하는 통역사 역할을 한다.</p>
<h4 id="io-장치에-접근하는-방법">I/O 장치에 접근하는 방법</h4>
<p><strong>Memory Mapped I/O</strong></p>
<p>원리 : 메모리 공간과 I/O 장치의 주소 공간이 분리되지 않고, I/O 장치가 마치 일반적인 메모리처럼 취급된다.</p>
<p>작동 방식 : CPU는 I/O 장치에 접근할 때 load (읽기)나 store (쓰기)와 같은 일반적인 메모리 명령어(instruction)를 사용한다.</p>
<p>장점 : 별도의 특별한 I/O 명령어가 필요 없기 때문에 프로그래밍이 간편한다.</p>
<p>현황 : 강의 자료에 따르면 이 방식이 더 일반적으로 사용된다.</p>
<p><strong>Special I/O</strong></p>
<p>원리 : CPU가 I/O 장치를 위한 <strong>별도의 독립된 버스(separate bus)</strong>를 가진다.</p>
<p>작동 방식 : I/O 장치에 접근하기 위해서는 IN 또는 OUT과 같이 I/O 전용으로 설계된 <strong>특별한 명령어(special instructions)</strong>가 필요하다.</p>
<p>장점: 메모리 주소와 I/O 주소 공간이 완전히 분리되어 있어, 메모리 보호 기능과 관련하여 더 명확한 구분이 가능하다.</p>
<h4 id="polling-io">Polling I/O</h4>
<p>폴링은 programmed I/O라고도 불리며, 모든 I/O 작업이 CPU의 직접적인 제어 아래 이루어지는 방식이다.</p>
<p>작동 방식 : CPU가 I/O 장치의 상태를 계속해서(continuously) 확인하면서 작업이 완료되었는지 묻는 방법이다. 마치 &quot;끝났니? 끝났니?&quot; 하고 끊임없이 물어보는 것과 같다.</p>
<p>비효율성 : 이 방식의 가장 큰 문제는 CPU 낭비이다. CPU는 I/O 장치의 작업이 끝날 때까지 바쁜 대기(busy waiting) 상태에 빠진다. 이 때문에 CPU는 다른 중요한 작업(다른 프로그램 실행 등)을 할 수 없게 되어 전체 시스템의 효율성이 크게 떨어진다.</p>
<p><strong>폴링에서 인터럽트로의 전환</strong>
이러한 폴링의 문제를 해결하기 위해 <strong>인터럽트(Interrupt)</strong> 방식이 도입되었다.</p>
<p>작동 방식 : CPU가 I/O 작업을 시작하라고 지시한 뒤, 작업이 끝날 때까지 기다리지 않고 다른 일을 합니다. I/O 장치가 작업을 완료하면, 직접 CPU에게 &quot;저 끝났습니다!&quot;라고 신호를 보내는데, 이 신호가 바로 인터럽트이다.</p>
<p>효율성 : CPU는 인터럽트 신호가 올 때만 하던 작업을 잠시 멈추고 I/O 장치에게 관심을 기울이면 됩니다. 이 덕분에 CPU는 I/O 작업을 기다리느라 시간을 낭비하지 않고 다른 유용한 작업을 수행할 수 있게 됩니다.</p>
<p>비유</p>
<p>폴링 : 요리사가 냄비 앞에서 물이 끓었는지 계속 뚜껑을 열어보는 것. (CPU 낭비)</p>
<p>인터럽트 : 요리사가 냄비에 타이머를 맞춰두고 다른 요리를 하다가, 타이머가 울리면 그때 가서 냄비를 확인하는 것. (CPU 효율성 증대)</p>
<p><strong>Polling의 다른 문제</strong>
원인 : I/O 장치 컨트롤러의 로컬 버퍼는 크기가 제한되어 있다. CPU가 너무 느리게 버퍼의 데이터를 가져가면, 새로운 데이터가 계속 들어와 기존의 데이터를 덮어쓰거나 유실될 수 있다.</p>
<p>결과 : 이로 인해 데이터 유실이라는 심각한 문제가 발생할 수 있으며, 이는 폴링 방식이 고속 I/O 장치에 부적합한 이유 중 하나이다.</p>
<h4 id="인터럽트의-기본-원리">인터럽트의 기본 원리</h4>
<p>작동 방식 : CPU와 I/O 장치 사이에 전용 신호선(line)이 있다. I/O 장치가 작업을 완료하면 이 신호선을 통해 CPU에게 신호를 보낸다. 이 신호는 CPU에게 어떤 사건(event)이 발생했음을 알려주는 역할을 한다.</p>
<p>결과 : 이로써 CPU는 더 이상 I/O 장치를 계속 확인하지 않고도, 필요할 때만 I/O 작업에 응답할 수 있게 된다.</p>
<p><strong>Context</strong> : CPU가 현재 실행 중인 작업(프로세스 또는 스레드)에 대한 모든 정보를 담고 있는 상태
정보 : CPU 레지스터의 값, 메모리 상태, 파일 정보</p>
<h4 id="interrupt와-context의-관계">Interrupt와 Context의 관계</h4>
<p>인터럽트가 발생하면 CPU는 하던 작업을 잠시 멈추고 인터럽트 핸들러를 실행해야 한다. 이때 CPU가 기존에 하던 작업을 그대로 이어가려면, 인터럽트가 발생하기 직전의 컨텍스트를 안전하게 저장해야 한다.</p>
<p>저장 (Save Context) : CPU는 인터럽트가 발생하면 현재 실행 중이던 작업의 컨텍스트를 스택이나 특정 메모리 영역에 저장한다. 이 과정은 인터럽트 핸들러가 실행되는 동안 원래 작업의 상태가 손상되지 않도록 보호하는 역할을 한다.</p>
<p>복원 (Restore Context) : 인터럽트 핸들러가 자신의 작업을 모두 마치면, 저장해 두었던 컨텍스트를 다시 불러와 CPU 레지스터에 복원한다. 이렇게 하면 CPU는 인터럽트가 발생하기 전의 작업을 중단 없이 그대로 이어서 실행할 수 있다.</p>
<p>결론적으로, 컨텍스트는 CPU가 여러 작업을 오가며 효율적으로 멀티태스킹을 할 수 있게 해주는 핵심 정보이다.</p>
<h4 id="인터럽트-처리-절차">인터럽트 처리 절차</h4>
<ol>
<li><p>컨텍스트 저장 : CPU가 현재 실행 중인 작업의 상태(context)를 보존한다.</p>
</li>
<li><p>유형 결정 : 인터럽트의 유형을 파악합니다. 주로 벡터 인터럽트 시스템을 사용한다.</p>
</li>
<li><p>핸들러 실행 : 인터럽트를 처리하기 위한 ISR(Interrupt Service Routine) 또는 인터럽트 핸들러로 제어권을 넘긴다.</p>
</li>
</ol>
<h4 id="인터럽트-타임라인">인터럽트 타임라인</h4>
<p>인터럽트가 발생하면 CPU는 I/O 작업 완료를 기다리는 대신 다른 작업을 처리하고, 
I/O 장치가 작업(예: 데이터 전송)을 완료하면 CPU에 인터럽트 신호를 보낸다. 
이 신호를 받은 CPU는 하던 작업을 잠시 멈추고 인터럽트 처리에 돌입합니다. 
이로써 <strong>CPU와 I/O 장치가 동시에 실행(concurrent execution)</strong>될 수 있습니다.</p>
<h4 id="direct-memory-access-dma">Direct Memory Access (DMA)</h4>
<p>문제점 : 인터럽트 방식은 I/O 작업이 완료될 때마다 CPU가 개입해야 하므로, 특히 디스크 I/O처럼 대량의 데이터를 이동할 때는 오버헤드가 매우 크다. 즉, 한 바이트(byte)마다 인터럽트가 발생하여 비효율적이다.</p>
<p>해결책 : 이러한 높은 오버헤드를 해결하기 위해 DMA(Direct Memory Access) 방식이 도입되었습니다. <strong>DMA는 CPU의 개입 없이 I/O 장치와 메인 메모리 사이에서 직접 데이터를 전송합니다.</strong></p>
<p>작동 방식 : CPU는 DMA 컨트롤러에게 전송할 데이터의 시작 주소, 크기 등 필요한 정보를 알려주고 작업을 시작하도록 프로그래밍합니다. 그 후 CPU는 다른 작업을 계속 수행합니다. DMA 컨트롤러는 I/O 장치로부터 데이터를 블록(block) 단위로 직접 메모리로 전송합니다.</p>
<p>결과 : 전체 데이터 블록 전송이 완료되었을 때 단 한 번의 인터럽트만 발생시키므로, CPU의 낭비 시간을 최소화할 수 있습니다. 이는 고속 I/O 장치에 매우 유용합니다.</p>
<h4 id="현대-인터럽트-처리-modern-interrupt-handling">현대 인터럽트 처리 (Modern Interrupt Handling)</h4>
<p>현대 운영체제는 단순히 인터럽트를 처리하는 것을 넘어, 효율성과 안정성을 위해 더욱 정교한 메커니즘을 요구한다.</p>
<p>다중 레벨 인터럽트 : 중요도에 따라 높고 낮은 우선순위를 구별하는 다중 레벨 인터럽트를 사용한다. 이를 통해 우선순위가 높은 인터럽트가 낮은 우선순위의 처리를 선점(pre-empt)할 수 있다.</p>
<p>PIC(Peripheral Interrupt Controller): 인터럽트 관리를 위해 특별화된 PIC를 사용한다.</p>
<p><strong>두 가지 인터럽트 라인</strong></p>
<ol>
<li>마스킹 가능 인터럽트: 중요한 처리 전에 일시적으로 비활성화할 수 있다.</li>
</ol>
<ol start="2">
<li>마스킹 불가능 인터럽트: 심각한 오류(예: 복구 불가능한 메모리 오류)를 알리기 때문에 끌 수 없다.</li>
</ol>
<h4 id="인터럽트-벡터interrupt-vector">인터럽트 벡터(Interrupt Vector)</h4>
<p>인터럽트 벡터는 인터럽트가 발생했을 때 적절한 인터럽트 핸들러를 찾기 위한 메커니즘이다.</p>
<p>구조 : 인터럽트 신호에는 해당 인터럽트 벡터를 가리키는 주소(address)가 포함되어 있다.</p>
<p><strong>역할 : 이 인터럽트 벡터 테이블은 각기 다른 인터럽트 핸들러의 주소를 담고 있다.
따라서 OS는 인터럽트 번호를 이용해 인터럽트 벡터 테이블에서 해당 핸들러의 주소를 찾아 실행하게 된다.</strong></p>
<p>체이닝(Chaining): 만약 인터럽트 벡터의 항목 수보다 장치 수가 더 많을 경우, 하나의 주소에 여러 인터럽트 핸들러의 리스트를 연결하는 체이닝(chaining) 방식을 사용한다.</p>
<h4 id="traps-and-exceptions">Traps and Exceptions</h4>
<p><img src="https://velog.velcdn.com/images/rohdoo_mani/post/c2bae7f9-2f06-46b9-b31e-6c00ee439262/image.png" alt=""></p>
<p><strong>작동방식</strong></p>
<ol>
<li>실행중인 프로세스를 정지</li>
<li>Context 저장</li>
<li>핸들러 호출 : 컨텍스트 저장이 완료된 후에야 CPU는 해당 문제 처리를 위한 <strong>전용 핸들러(Handler)</strong>를 호출하여 실행.</li>
<li>상태 복원 : 핸들러의 작업이 끝나면, 저장해 두었던 컨텍스트를 다시 불러와 CPU 레지스터에 복원하고, 중단되었던 프로세스를 이어서 실행.</li>
</ol>
<p>2 단계는 인터럽트 발생 시 시스템의 안정성과 연속성을 보장하는 핵심적인 단계이다. 
<strong>이러한 과정이 없다면, 인터럽트가 발생할 때마다 기존 작업이 손상되어 시스템이 멈추거나 오작동할 수 있다.</strong></p>
<h4 id="protection의-필요성">Protection의 필요성</h4>
<p>보호 메커니즘이 필요한 주된 이유는 시스템의 올바른 작동을 보장하기 위해.
멀티태스킹 환경에서 여러 프로그램이 동시에 실행되면서 다양한 문제가 발생할 수 있다.</p>
<p>애플리케이션 프로그램 문제: 악의적이지 않더라도, 버그가 있는 프로그램이 다른 메모리 영역을 덮어쓰거나(scribbling into memory) 무한 루프에 빠져 CPU를 독점할 수 있다.</p>
<p>사용자 간 간섭: 다른 사용자가 무분별하게(Gluttonous) 자원을 사용하거나 악의적(Evil) 으로 시스템에 해를 가할 수도 있다.</p>
<h4 id="protecting-memory">Protecting Memory</h4>
<p><strong>주된 이유</strong></p>
<ol>
<li>다른 프로그램으로부터 프로그램 보호: 한 프로그램이 다른 프로그램의 데이터를 함부로 접근하지 못하게 함.</li>
</ol>
<ol start="2">
<li>사용자 프로그램으로부터 OS 보호: 사용자 프로그램이 OS 영역을 침범하지 못하게 막음.</li>
</ol>
<p>가장 단순한 메모리 보호 방법은 Base and Limit Registers를 사용하는 것.</p>
<p>Base Register (베이스 레지스터): 프로그램이 접근할 수 있는 메모리 영역의 시작 주소를 저장.</p>
<p>Limit Register (리미트 레지스터): 프로그램이 접근할 수 있는 메모리 영역의 <strong>크기(길이)</strong>를 저장.</p>
<p>이 두 레지스터는 프로그램이 실행되기 전에 운영체제(OS)가 값을 설정한다. 
프로그램이 실행되는 동안 CPU가 메모리에 접근할 때마다, 하드웨어는 해당 주소가 <strong>베이스 레지스터 값</strong>부터 베이스 레지스터 + 리미트 레지스터 값 사이에 있는지 확인한다. 
만약 유효한 범위를 벗어나면 하드웨어는 인터럽트를 발생시켜 해당 프로그램을 강제로 종료한다. 이를 통해 다른 프로그램이나 OS의 메모리 영역을 안전하게 보호할 수 있다.</p>
<h4 id="cpu-protection">CPU Protection</h4>
<p><strong>&quot;협력적 접근(Cooperative approach)&quot;</strong>은 프로그램이 <strong>시스템 콜(System Call)</strong>을 자발적으로 호출하여 제어권을 OS에 넘겨주는 방식이다. 이는 프로그램이 OS의 도움을 받기 위해 스스로 요청하는 경우이다.</p>
<p>하지만, 만약 프로그램이 악의적이거나 버그로 인해 시스템 콜을 호출하지 않고 <strong>무한 루프(infinite loop)</strong>에 빠지면 어떻게 될까? 이 경우 CPU 제어권을 되찾을 수 있는 다른 방법이 필요하다.</p>
<h4 id="timer-hardware">Timer Hardware</h4>
<p><strong>타이머(Timer)의 역할
*<em>타이머는 사용자 프로그램의 *</em>무한 루프(infinite loops)</strong>나 <strong>CPU 독점(CPU monopoly)</strong>을 방지하기 위해 사용된다. 이는 시스템의 안정성과 공정성을 보장하는 핵심 메커니즘이다.</p>
<p>비협력적 접근(Non-cooperative approach) : 타이머는 프로그램이 자발적으로 제어권을 반환하지 않을 때, OS가 강제로 제어권을 회수하는 방식.</p>
<p><strong>타이머 작동 방식</strong></p>
<ol>
<li>타이머 설정 : OS가 CPU 제어권을 사용자 프로그램에 넘기기 전에, 인터럽트가 발생할 시간을 타이머에 미리 설정. 이 시간 단위는 일반적으로 <strong>퀀텀(quantum)</strong>.</li>
</ol>
<ol start="2">
<li>프로그램 실행 : 설정된 시간이 지나면, 타이머는 자동으로 <strong>타이머 인터럽트(timer interrupt)</strong>를 발생시킨다.</li>
</ol>
<ol start="3">
<li>제어권 회수 : 이 인터럽트를 통해 OS는 CPU의 제어권을 다시 가져온다.</li>
</ol>
<p>이 과정을 통해 버그가 있거나 악의적인 프로그램이 무한 루프에 빠져도, OS는 반드시 CPU 제어권을 되찾을 수 있습니다.</p>
<p><strong>특권 명령어(Privileged Instruction)</strong>
타이머 값을 설정하는 작업은 매우 중요하므로, <strong>특권 명령어(privileged instruction)</strong>로 지정됩니다. <strong>이는 오직 OS만이 실행할 수 있는 명령어이며</strong>, 사용자 프로그램이 마음대로 타이머를 조작하여 CPU를 독점하는 것을 방지합니다.</p>
<p><strong>특권 명령어 사용 예시</strong>
하드웨어 자원에 대한 직접 접근 : 디스크나 프린터와 같은 I/O 장치에 직접 접근하는 명령어.</p>
<p>메모리 관리 상태 조작 : 페이지 테이블 포인터(page table pointers)나 TLB(translation look-aside buffer) 로딩 등 메모리 관리와 관련된 상태를 변경하는 명령어.</p>
<p>시스템 레지스터 접근 : 제어 레지스터(Control registers)나 인터럽트 핸들러 테이블(interrupt handler table)의 위치와 같은 시스템 레지스터에 접근하는 명령어.</p>
<p>CPU 모드 비트 설정 : 듀얼 모드(사용자/커널)를 구분하는 모드 비트를 설정하는 명령어.</p>
<p>시스템 정지 : 컴퓨터를 정지(halt)시키는 명령어.</p>
<p>타이머 설정 : 타이머 값을 설정하는 명령어 는 사용자 프로그램의 무한 루프를 방지하기 위해 오직 OS만 사용 가능하도록 제한.</p>
<h4 id="cpu-작동-모드-cpu-modes-of-operation">CPU 작동 모드 (CPU Modes of Operation)</h4>
<p>CPU 작동 모드는 운영체제가 자신과 다른 시스템 구성 요소를 보호하기 위한 메커니즘.</p>
<p>사용자 모드 (User mode): 일반적인 사용자 프로그램이 실행되는 모드. 이 모드에서는 특권 명령어 실행이 제한.</p>
<p>커널 모드 (Kernel mode): 운영체제가 실행되는 모드. 이 모드에서는 모든 명령어와 하드웨어 자원에 접근할 수 있다.</p>
<p>CPU는 하드웨어에 의해 제공되는 모드 비트(mode bit)를 통해 현재 실행 중인 모드를 식별한다. 모드 비트는 커널 모드일 때 0, 사용자 모드일 때 1과 같이 설정된다.</p>
<p><strong>모드 전환</strong></p>
<p><strong>사용자 → 커널</strong> : 사용자 프로그램이 시스템 콜을 호출하면, 트랩(trap)을 통해 모드 비트가 변경되어 커널 모드로 전환.</p>
<p><strong>커널 → 사용자</strong> : 시스템 콜 처리가 끝나면 RTI (return from interrupt) 명령어를 사용하여 모드 비트가 다시 변경되어 사용자 모드로 돌아옴.</p>
<p>사용자 프로그램은 원칙적으로 특권 명령어를 직접 실행할 수 없기 때문에, 이러한 작업이 필요할 때는 <strong>시스템 콜(System Call)</strong>을 통해 운영체제에게 대신 수행해 달라고 요청해야 한다.</p>
<p>두 아키텍처 모두 보호된 레지스터의 상태 비트(status bit)를 통해 현재의 모드를 설정하고 식별합니다. 
이 모드 비트는 <strong>시스템 콜을 통해 커널 모드로 전환</strong>되고,
<strong>RTI(Return from Interrupt) 명령어를 사용하여 다시 사용자 모드</strong>로 돌아옵니다.</p>
<h4 id="crossing-protection-boundary">Crossing Protection Boundary</h4>
<p>사용자 프로그램은 시스템의 중요 자원을 보호하기 위해 직접 하드웨어에 접근할 수 없다. 따라서 OS가 제공하는 특권 작업을 수행하려면, <strong>시스템 콜(System Call)</strong>이라는 특별한 메커니즘을 통해 OS에 요청해야 한다.</p>
<ol>
<li>사용자 모드에서 요청 : 사용자가 작성한 프로그램(user process)은 평소와 같이 사용자 모드에서 실행된다. 이 프로그램이 OS의 도움이 필요한 작업(예: 파일 열기)을 만나면, 라이브러리에 있는 시스템 콜 API를 호출한다.</li>
</ol>
<ol start="2">
<li>트랩(Trap) 발생 : 시스템 콜 API의 마지막 명령어는 trap(소프트웨어 인터럽트)이다. 이 명령어가 실행되면, CPU의 <strong>모드 비트(mode bit)</strong>가 사용자 모드(1)에서 커널 모드(0)로 전환된다.</li>
</ol>
<ol start="3">
<li>커널 모드에서 실행 : 이제 제어권이 OS의 커널로 넘어가고, 커널은 요청된 시스템 콜을 실행한다. 이 과정에서 커널은 모든 하드웨어와 자원에 대한 완전한 권한을 가진다.</li>
</ol>
<ol start="4">
<li>복귀 : 시스템 콜 처리가 끝나면, 커널은 return 명령어를 실행하여 모드 비트를 다시 사용자 모드(1)로 바꾸고 , 원래의 사용자 프로세스에게 제어권을 돌려준다.</li>
</ol>
<h4 id="system-call">System Call</h4>
<p>시스템 콜은 사용자 프로그램이 운영체제로부터 서비스를 받기 위해 사용하는 프로그래밍 인터페이스이다.</p>
<p><strong>API와 시스템 콜</strong>: 사용자는 API(예: printf())를 호출하고, 이 API는 내부적으로 시스템 콜 구현(예: write())을 호출한다. API는 사용자가 접근하기 쉽도록 높은 수준의 언어로 작성되지만, 실제로는 커널에 존재하는 시스템 콜을 실행하는 것이다.</p>
<p><strong>시스템 보호</strong>: OS는 시스템 콜을 통해 시스템을 보호.</p>
<ol>
<li><p>불법 요청 거부: 사용자 프로그램이 권한이 없는(illegal) 요청을 하면, OS는 이를 거부하고 에러를 반환하여 시스템의 안정성을 지킨다.</p>
</li>
<li><p>자원 할당: &quot;OS는 특정 자원의 몫을 부과한다&quot;는 말은 OS가 자원을 할당할 때 공정하게 분배한다는 뜻이다. 예를 들어, CPU 시간이나 메모리 같은 자원을 여러 프로그램에 공정하게 나누어 할당하여 한 프로그램이 자원을 독점하지 않도록 한다.</p>
</li>
<li><p>공정성 보장: 여러 프로그램이 동시에 자원을 요청할 때, OS는 공정성(fairness)을 고려하여 순서를 정하고 자원을 공유한다.</p>
</li>
</ol>
<p><strong>보호된 절차 호출</strong>: 시스템 콜은 일종의 보호된 프로시저(절차) 호출이다. 시스템 콜 루틴은 OS의 코드 안에 있으며, 실행은 항상 커널 모드에서 이루어진다. 따라서 사용자 모드에서 시스템 콜을 호출할 때 모드가 커널 모드로 바뀌었다가, 시스템 콜 처리가 끝나면 다시 사용자 모드로 돌아온다.</p>
<p><strong>시스템 콜의 접근 방식</strong>
&quot;API를 사용하는 프로그램에 의해 가장 접근된다&quot;는 말은, 대부분의 개발자는 시스템 콜을 직접 호출하지 않고, API를 통해 간접적으로 접근한다는 의미이다.</p>
<p>API의 역할: 프로그래밍 라이브러리(C 라이브러리, Java 라이브러리 등)는 개발자가 사용하기 쉬운 API를 제공한다. 이 API들은 내부적으로 해당 작업을 수행하기 위한 시스템 콜을 호출한다.</p>
<p>예시: C 언어에서 파일을 열기 위해 사용하는 fopen() 함수는 API이다. 이 함수를 호출하면, 라이브러리 내부에서는 실제로 파일을 열기 위한 open() 시스템 콜이 호출되어 OS에게 요청이 전달된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[데이터베이스 - chap.1]]></title>
            <link>https://velog.io/@rohdoo_mani/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-chap.1</link>
            <guid>https://velog.io/@rohdoo_mani/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-chap.1</guid>
            <pubDate>Wed, 10 Sep 2025 08:36:08 GMT</pubDate>
            <description><![CDATA[<h2 id="📝-데이터베이스-핵심-개념-정리">📝 데이터베이스 핵심 개념 정리</h2>
<h3 id="1-데이터베이스-기본-용어">1. 데이터베이스 기본 용어</h3>
<p>데이터(Data): 의미를 가지며 기록될 수 있는 알려진 사실들입니다.</p>
<p>데이터베이스(Database): 서로 관련있는 데이터들의 모임입니다.</p>
<p>DBMS(데이터베이스 관리 시스템): 데이터베이스의 생성과 관리를 담당하는 소프트웨어 패키지입니다.</p>
<p>데이터베이스 시스템(Database System): 데이터베이스와 이를 관리하는 소프트웨어(DBMS), 응용 프로그램을 모두 포함하는 용어입니다.</p>
<p>미니월드(Mini-world): 데이터베이스 구축의 대상이 되는 현실 세계의 일부분을 가리킵니다.</p>
<h3 id="2-데이터베이스의-구조-3단계-스키마-아키텍처">2. 데이터베이스의 구조: 3단계 스키마 아키텍처</h3>
<p>데이터베이스는 사용자와 물리적 저장 구조의 분리를 목표로 3단계 스키마 아키텍처를 사용하며, 이를 통해 데이터 독립성을 보장합니다.</p>
<p>외부 스키마(External Schema): 개별 사용자가 보는 데이터베이스의 관점입니다. 사용자마다 관심 있는 데이터의 일부만 </p>
<p>뷰(View) 형태로 제공하여 전체 복잡성으로부터 사용자를 보호합니다.</p>
<p>개념 스키마(Conceptual Schema): 조직 전체의 데이터베이스를 통합한 논리적 구조입니다. 데이터베이스에 저장될 모든 데이터, 관계, 제약 조건을 기술하며, 데이터의 물리적 저장 구조와는 독립적입니다.</p>
<p>내부 스키마(Internal Schema): 데이터가 물리적으로 저장되는 방식을 기술합니다. 데이터가 저장되는 파일, 인덱스 등 세부적인 물리적 정보를 포함하며, 하드웨어 및 운영체제에 종속적일 수 있습니다.</p>
<h3 id="3-데이터-독립성">3. 데이터 독립성</h3>
<p>3단계 스키마 아키텍처는 두 가지 유형의 데이터 독립성을 제공합니다.</p>
<p>논리적 데이터 독립성: 개념 스키마가 변경되어도 외부 스키마(사용자 뷰)는 영향을 받지 않습니다.</p>
<p>물리적 데이터 독립성: 내부 스키마(물리적 저장 구조)가 변경되어도 개념 스키마는 영향을 받지 않습니다.</p>
<h3 id="4-데이터베이스의-주요-특징">4. 데이터베이스의 주요 특징</h3>
<p><strong>자기 기술성(Self-describing)</strong>: DBMS는 데이터베이스 자체에 대한 정의(메타데이터)를 데이터 카탈로그에 스스로 저장합니다. 이로 인해 응용 프로그램과 데이터의 연결이 느슨해져, 데이터 구조가 변경되어도 응용 프로그램을 최소한으로 수정하거나 전혀 수정하지 않아도 됩니다. 이는 프로그램-데이터 독립성을 가능하게 합니다.</p>
<p><strong>데이터 추상화</strong>: 데이터 모델을 사용하여 저장 구조의 세부 사항을 숨기고 개념적인 뷰만 제공합니다.</p>
<p><strong>데이터 공유 및 다중 사용자 트랜잭션 처리</strong>: 여러 사용자가 동시에 데이터를 안전하게 공유할 수 있게 합니다.</p>
<p><strong>트랜잭션(Transaction)</strong>: 한 번 이상의 데이터베이스 접근을 포함하는 프로그램의 단위 또는 프로세스 수행으로, 원자성과 고립성을 만족해야 합니다.</p>
<p><strong>원자성(Atomicity)</strong>: 트랜잭션 내의 모든 연산이 하나의 단위로 묶여 부분적 실행이 불가능함을 의미합니다.</p>
<p><strong>고립성(Isolation)</strong>: DBMS가 다양한 기술(예: 잠금)을 사용해 각 트랜잭션이 마치 독립적으로 실행되는 것처럼 보이게 하는 기능입니다.</p>
<p><strong>동시성 제어(Concurrency Control)</strong>: 여러 트랜잭션이 동시에 실행되더라도 데이터의 일관성을 유지하는 기능입니다.</p>
<p><strong>복구(Recovery)</strong>: 시스템 장애 시에도 데이터 손실을 방지하는 기능입니다.</p>
<h3 id="5-dbms의-장점-및-사용-효과">5. DBMS의 장점 및 사용 효과</h3>
<p><strong>데이터 중복성 제어:</strong> 데이터 중복을 방지하고 데이터 일치성을 보장합니다.</p>
<p><strong>데이터 공유 및 동시 접근 보장:</strong> 다수 사용자 간의 데이터 공유와 동시 접근을 보장합니다.</p>
<p><strong>권한 없는 접근 통제:</strong> 보안 및 권한 서브시스템을 통해 접근을 통제합니다.</p>
<p><strong>지속성 기억 공간 제공:</strong> 프로그램 객체를 위한 영구적인 저장 공간을 제공합니다.</p>
<p><strong>효율적인 질의 처리:</strong> 저장 구조 및 탐색 기법을 통해 질의를 효율적으로 처리합니다.</p>
<p><strong>백업과 회복:</strong> 백업 및 회복 기능을 제공하여 데이터를 복구합니다.</p>
<p><strong>다양한 사용자 인터페이스 제공:</strong> 다양한 인터페이스를 통해 데이터에 접근할 수 있게 합니다.</p>
<p><strong>복잡한 관계 표현:</strong> 데이터 간의 복잡한 관계를 표현합니다.</p>
<p><strong>무결성 제약 조건 시행:</strong> 데이터의 정확성과 일관성을 유지하기 위한 제약 조건을 강제합니다.</p>
<p><strong>표준화된 데이터 관리:</strong> 업무 효율성을 증대시킵니다.</p>
<p><strong>데이터 구조 변경의 유연성:</strong> 데이터 구조가 변경되어도 사용자에게 미치는 영향이 거의 없습니다.</p>
<p><strong>응용 프로그램 개발 시간 단축:</strong> 응용 프로그램의 많은 부분을 DBMS가 처리해주어 개발 시간을 단축합니다.</p>
<p><strong>최신 정보 제공:</strong> 한 사용자의 갱신이 다른 사용자에게 즉시 반영됩니다.</p>
<p><strong>규모의 경제성:</strong> 통합된 DB 관리를 통해 비용을 절감합니다.</p>
<h3 id="6-dbms가-불필요한-경우">6. DBMS가 불필요한 경우</h3>
<p><strong>높은 초기 투자 비용과 오버헤드:</strong> DBMS의 기능(보안, 동시성 제어, 복구)이 필요하지 않은 경우, 오버헤드가 되어 성능에 부정적 영향을 줄 수 있습니다.</p>
<p><strong>단순하고 변경 가능성이 적은 응용:</strong> 응용 프로그램이 단순하고 잘 정의되어 있으며, 변경될 가능성이 적을 때.</p>
<p><strong>실시간 데이터 처리 요구사항:</strong> DBMS의 오버헤드로 인해 엄격한 실시간 데이터 처리 요구사항을 만족시키기 어려울 수 있습니다.</p>
<p><strong>다중 사용자 접근이 필요하지 않은 경우:</strong> 단일 사용자 환경에서는 DBMS의 복잡한 기능이 필요하지 않습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[운영체제 복습 (1주차)]]></title>
            <link>https://velog.io/@rohdoo_mani/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%EB%B3%B5%EC%8A%B5-1%EC%A3%BC%EC%B0%A8</link>
            <guid>https://velog.io/@rohdoo_mani/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%EB%B3%B5%EC%8A%B5-1%EC%A3%BC%EC%B0%A8</guid>
            <pubDate>Mon, 08 Sep 2025 07:46:07 GMT</pubDate>
            <description><![CDATA[<h3 id="컴퓨터-시스템의-요소">컴퓨터 시스템의 요소</h3>
<ol>
<li><p>H/W : 기본 컴퓨팅 자원을 제공(CPU, 메모리,I/O 장치, ..)</p>
</li>
<li><p>OS : 하드웨어의 사용을 조절하고 통합한다. 다양한 어플리케이션 프로그램 사이에서.</p>
</li>
<li><p>System &amp; Application Programs : 시스템 자원 안에 있는 s/w은 사용자의 컴퓨팅 문제를 해결하기위해사용된다.(워드 프로세서, 웹 브라우저, 비디오 게임 등..)</p>
</li>
<li><p>User : 사람, 기계, 다른 컴퓨터</p>
</li>
</ol>
<h3 id="os란-무엇인가">OS란 무엇인가</h3>
<h4 id="application-view">Application view</h4>
<p>: 프로그램을 실행하기 위한 실행 환경을 제공한다.</p>
<p>: 추상적인 관점 제공한다, 컴퓨터 시스템의 밑에있는 (H/W 캡슐화로 인해)</p>
<h4 id="system-view">System view</h4>
<p>: 컴퓨터 시스템의 다양한 자원을 관리한다.</p>
<p>: 장치를 바쁘게 유지한다.(효율적 측면)</p>
<p>: Sharing, Protection, Fairness, Efficiency, Concurrency</p>
<p>(resource : CPU, memory, I/O devices, Queues, Energy...)</p>
<h4 id="implementation-view">Implementation view</h4>
<p>: Highly-concurrent, event-driven software</p>
<p>핵심은 &quot;Highly-concurrent, event-driven software&quot; 라는 표현에 있다.  이는 운영체제가 여러 작업을 동시에 처리하고(highly-concurrent), 외부에서 발생하는 사건(event)에 반응하며 동작하는(event-driven) 소프트웨어라는 뜻</p>
<p><strong>Event-driven (이벤트 중심):</strong></p>
<p>사용자가 키보드를 누르거나 마우스를 클릭하는 것, 네트워크 패킷이 도착하는 것, 디스크에서 데이터를 읽는 작업이 완료되는 것 등은 모두 <strong>이벤트(Event)</strong>이다.</p>
<p>운영체제는 이러한 이벤트가 발생했을 때 적절한 처리를 해야 한다. </p>
<p>그림에서는 <strong>&#39;Interrupts(인터럽트)&#39;</strong>를 통해 하드웨어(Device 1, 2, 3)로부터 발생하는 이벤트를 OS가 감지하는 과정을 보여주고 있다.  하드웨어가 어떤 작업(예: I/O)을 마치면 CPU에 인터럽트를 걸어 OS에 알려준다.</p>
<p><strong>Highly-concurrent (고도의 동시성):</strong></p>
<p><strong>동시성(Concurrency)</strong>이란 여러 작업이 동시에 진행되는 것처럼 보이게 만드는 것을 의미한다.</p>
<p>그림에는 &#39;App 1&#39;, &#39;App 2&#39;, &#39;App 3&#39;과 같이 여러 개의 애플리케이션이 실행되고 있다. </p>
<p>애플리케이션은 <strong>&#39;System calls(시스템 호출)&#39;</strong>이라는 통로를 통해 운영체제에 특정 기능(예: 파일 읽기, 쓰기)을 요청한다. </p>
<p>이때 <strong>&#39;trap(트랩)&#39;</strong>이라는 메커니즘을 사용해 사용자 모드에서 커널 모드(운영체제가 실행되는 영역)로 전환하여 요청을 처리하게 된다. </p>
<p>운영체제는 이러한 시스템 호출과 인터럽트를 효율적으로 처리함으로써 여러 애플리케이션이 끊김 없이 동시에 실행되는 듯한 환경을 만들어냅니다.</p>
<p><strong>요약</strong>
Implementation view는 운영체제가 단순히 자원을 관리하는 것을 넘어, 하드웨어와 애플리케이션 사이의 복잡한 상호작용을 처리하기 위해 인터럽트와 시스템 호출이라는 이벤트 기반 메커니즘을 통해 고도의 동시성을 달성하는 소프트웨어라는 점을 강조한다.</p>
<h3 id="os의-역사">OS의 역사</h3>
<h4 id="1-early-systems-os-없는-초기-시스템">1. Early Systems (OS 없는 초기 시스템)</h4>
<p>특징: 이 시기에는 운영체제가 존재하지 않았습니다. 프로그래머가 메모리 스위치를 사용하여 직접 이진 코드를 입력하고, 모든 작업을 수동으로 처리했습니다. 컴파일러나 유틸리티는 나중에 추가되었습니다.</p>
<p>비효율성: 한 번에 하나의 작업만 실행할 수 있었고, 작업이 완료되면 다음 작업을 수동으로 준비해야 했습니다. 이는 매우 비효율적이었고, 컴퓨터 자원을 낭비하는 원인이 되었습니다.</p>
<h4 id="2-batch-processing-systems-2세대">2. Batch Processing Systems (2세대)</h4>
<p>등장 배경: Early Systems의 비효율성을 극복하기 위해 등장했습니다. 여러 작업을 한데 모아(batch) 한 번에 처리하는 방식이 도입되었습니다.</p>
<p>특징: 펀치 카드에 담긴 작업들을 작업자가 카드 리더에 넣어 순차적으로 실행했습니다. 시스템의 성능은 느린 I/O 장치(카드 리더, 프린터)의 속도에 크게 의존했습니다.</p>
<p>한계: 여전히 한 번에 하나의 작업만 실행했기 때문에, CPU가 계산을 하는 동안 I/O 장치가 유휴 상태가 되거나 반대로 I/O 작업 중 CPU가 놀게 되는 비효율성이 있었습니다.</p>
<h4 id="3-multi-programming-systems">3. Multi-programming Systems</h4>
<p>등장 배경: Batch Processing System의 가장 큰 문제점인 낮은 CPU 활용률을 개선하기 위해 등장했습니다.</p>
<p>핵심 원리: 여러 작업을 메인 메모리에 동시에 올려놓고, 하나의 작업이 I/O 작업을 위해 CPU를 기다리는 동안 OS가 다른 작업에 CPU를 할당했습니다. 이로 인해 CPU와 I/O 작업이 </p>
<p>겹쳐서(overlap) 진행되는 <strong>동시성(concurrency)</strong>이 가능해졌습니다.</p>
<p><strong>주요 기능</strong></p>
<p>Spooling: 느린 I/O 장치와 CPU 사이에 디스크 버퍼를 두어, CPU가 I/O 작업을 기다리지 않고 즉시 다음 작업을 처리할 수 있게 하는 기술입니다.</p>
<p>Protection: 메모리에 여러 작업이 동시에 존재하면서 발생할 수 있는 메모리 침범 문제를 해결하기 위해 도입되었습니다. 한 작업이 다른 작업의 메모리 영역에 접근하지 못하도록 보호하는 기능입니다.</p>
<p>결과: CPU 활용률과 시스템 처리량(Throughput)이 크게 향상되었습니다.</p>
<h4 id="4-time-sharing-systems-3세대">4. Time-sharing Systems (3세대)</h4>
<p>등장 배경: Multi-programming은 시스템 효율성을 높였지만, 사용자가 직접 상호작용하기 어려운 오프라인(off-line) 방식이었습니다. 사용자에게 빠른 응답성을 제공하기 위해 등장했습니다.</p>
<p>핵심 원리: 여러 사용자가 동시에 컴퓨터에 접속하여 상호작용할 수 있도록 온라인(on-line) 처리를 지원합니다. OS가 각 프로세스(사용자에 의해 생성된 실행 중인 프로그램)에 CPU 시간을 매우 짧게 할당하고 번갈아 가며 실행하는 </p>
<p>Time-slice 메커니즘을 사용했습니다.</p>
<p>결과: 사용자는 마치 컴퓨터를 단독으로 사용하는 것처럼 느끼게 되어 응답 시간(response time)이 크게 개선되었습니다. 이는 오늘날의 모든 PC 및 서버 운영체제(Linux, Windows 등)의 기반이 되는 개념입니다.</p>
<h4 id="5-4세대-운영체제-1980년대-이후">5. 4세대 운영체제 (1980년대 이후)</h4>
<p>원인: LSI(대규모 집적 회로) 및 VLSI(초대규모 집적 회로) 기술의 발전으로 컴퓨터 하드웨어가 크게 발전했습니다. 마이크로프로세서가 더 작고 빨라졌고, 저장 공간도 커지며 속도가 향상되었습니다. 또한, CPU의 작업 일부를 I/O 장치로 분담시키는 기술이 발전했습니다. 이로써 개인용 컴퓨터(PC)의 시대가 열렸습니다.</p>
<p><strong>현대 OS의 특징:</strong></p>
<p>GUI (그래픽 사용자 인터페이스): 사용자가 아이콘과 창을 통해 컴퓨터와 상호작용할 수 있게 되었습니다.</p>
<p>멀티미디어: 오디오 및 비디오와 같은 멀티미디어 콘텐츠를 처리하는 기능이 추가되었습니다.</p>
<p>인터넷 및 웹: 네트워크 기능이 OS의 필수적인 부분이 되었습니다.</p>
<p>네트워크/분산 시스템: 여러 컴퓨터를 네트워크로 연결하여 자원을 공유하고 협업하는 개념이 발전했습니다.</p>
<p>*<em>멀티프로세서 시스템 (SMP) *</em></p>
<p>특징: 여러 개의 CPU가 하나의 공유 메모리(Shared common memory)를 사용하는 시스템입니다. 오늘날의 듀얼/쿼드 코어 시스템과 같은 멀티코어 시스템이 여기에 속합니다.</p>
<p>장점:</p>
<p>고성능: 여러 CPU가 병렬로 작업을 처리하여 성능이 향상됩니다.</p>
<p>높은 신뢰성: 일부 CPU가 고장 나도 전체 시스템은 작동을 계속할 수 있습니다.</p>
<p>어려움: 각 CPU가 로컬 캐시를 가지고 있어 메모리 접근 충돌을 줄이지만 , 캐시 불일치(cache inconsistency) 문제가 발생할 수 있습니다. 이를 해결하기 위한 하드웨어 캐시 일관성 프로토콜이 필요합니다. 소프트웨어는 단일 CPU 시스템과 동일하지만 하드웨어 구현이 어렵습니다.</p>
<p>*<em>분산 시스템 (클러스터 시스템) *</em></p>
<p>특징: 네트워크로 연결된 여러 컴퓨터가 협력하여 단일 시스템처럼 작동하는 시스템입니다. 각 노드는 자체 로컬 메모리를 가집니다.</p>
<p>목표:</p>
<p>자원 공유: 원격 자원을 사용할 수 있습니다.</p>
<p>병렬 컴퓨팅: 여러 컴퓨터에서 동시에 프로세스가 실행됩니다.</p>
<p>높은 신뢰성: 일부 컴퓨터가 고장 나도 시스템 전체가 멈추지 않습니다.</p>
<p>어려움: 하드웨어는 기존의 개별 컴퓨터를 그대로 사용하기 때문에 상대적으로 쉽습니다. <strong>하지만, 사용자에게 원격과 로컬을 구분 짓지 않고 마치 하나의 시스템처럼 동작하는 것처럼 보이게 하려면 이를 지원하는 운영체제(OS)와 미들웨어와 같은 소프트웨어가 매우 중요하고 복잡해집니다.</strong> 클라우드 서버나 분산 파일 시스템(NFS)이 분산 시스템의 예입니다.</p>
<p>*<em>클라우드 컴퓨팅 *</em></p>
<p>특징: 분산 서버, 하이퍼바이저, 가상 머신(Virtual Machines, VM)으로 구성됩니다. 하이퍼바이저는 가상 머신을 관리하는 소프트웨어입니다.</p>
<p>서비스 유형:</p>
<p>SaaS (Software as a Service) </p>
<p>IaaS (Infrastructure as a Service) </p>
<p>PaaS (Platform as a Service) </p>
<p>DaaS (Desktop as a Service) </p>
<p>*<em>임베디드 시스템 *</em></p>
<p>특징: 특정 목적을 위해 설계된 하드웨어에 탑재되는 특수 목적의 소프트웨어 시스템입니다. 스마트폰, 가전제품, 자동차, 의료 기기 등이 여기에 속합니다.</p>
<p>고려사항:</p>
<p>모바일 환경: 무선 통신 및 배터리 전원 관리가 중요합니다.</p>
<p>실시간 시스템: 안전 및 임무 수행에 중요한 경우(예: 무기 시스템, 의료 기기), 실시간 처리가 필수적입니다.</p>
<p>비용 민감성: 가격 경쟁력이 중요합니다.</p>
<p>가혹 환경: HDD보다 충격에 강한 NVRAM(NAND flash)을 주로 사용합니다.</p>
<p>OS: VxWorks, WinCE, QNX 등 다양한 임베디드 OS가 사용됩니다. 모바일 기기에는 안드로이드, iOS, WebOS 등이 사용됩니다.</p>
<p>핵심 키워드: 임베디드 시스템은 유비쿼터스 컴퓨팅, 사물 인터넷(IoT) 등과 밀접하게 관련되어 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 - 1157번 ]]></title>
            <link>https://velog.io/@rohdoo_mani/%EB%B0%B1%EC%A4%80-1157%EB%B2%88</link>
            <guid>https://velog.io/@rohdoo_mani/%EB%B0%B1%EC%A4%80-1157%EB%B2%88</guid>
            <pubDate>Tue, 12 Aug 2025 12:13:33 GMT</pubDate>
            <description><![CDATA[<p>알파벳 대소문자로 된 단어가 주어지면, 이 단어에서 가장 많이 사용된 알파벳이 무엇인지 알아내는 프로그램을 작성하시오(단, 대문자와 소문자는 구분하지 않는다).</p>
<p>문자열을 입력받고, 이를 대문자 혹은 소문자로 전체 바꿔서 for문으로 각 알파벳의 개수를 알아내어 구한 문자를 대문자로 출력한다.</p>
<pre><code>s = input().upper()
s_list = list(set(s))
cnt = []
for i in s_list:
    count = s.count(i)
    cnt.append(count)
if cnt.count(max(cnt)) &gt;= 2:
    print(&quot;?&quot;)
else:
    print(s_list[cnt.index(max(cnt))])</code></pre><p>입력받은 문자열을 리스트 형태로 다시 만들고, 단어별 횟수를 세는 리스트를 만들어 문자마다 횟수를 구한다. 조건문으로 ? 또는 문자가 나오도록 하여 출력한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 - 1145번]]></title>
            <link>https://velog.io/@rohdoo_mani/%EB%B0%B1%EC%A4%80-1145%EB%B2%88</link>
            <guid>https://velog.io/@rohdoo_mani/%EB%B0%B1%EC%A4%80-1145%EB%B2%88</guid>
            <pubDate>Tue, 12 Aug 2025 10:58:58 GMT</pubDate>
            <description><![CDATA[<p>5개의 자연수 중에서 이 수의 적어도 대부분의 배수는 위의 수 중 적어도 세 개로 나누어지는 가장 작은 자연수이다. 서로 다른 다섯 개의 자연수가 주어질 때, 적어도 대부분의 배수를 출력하는 프로그램을 작성하시오. </p>
<p>이 문제를 보고, 구하고자 하는 수는 5개의 자연수 중에서 3개를 랜덤으로 뽑아서 곱하면 될것이라 생각하고 다음 코드를 썼다.</p>
<pre><code>import random

m_list = list(map(int, input().split()))

rand_sample = random.sample(m_list,3)

result = 1
#print(rand_sample)
for i in range(3):
    result *= rand_sample[i]

print(result)</code></pre><p>하지만 나의 코드가 틀린 이유를 어렵지 않게 찾아낼 수 있었다.</p>
<ol>
<li>세 개의 자연수를 골라 이것을 곱하여 배수를 찾았으나 이것이 가장 작은 자연수임을 확신할 수 없다는 것.</li>
<li>랜덤으로 뽑은 3개의 수의 최소공배수를 구하는 문제가 아니라, 5개의 수 중에서 3개 이상이 나누어떨어지는 가장 작은 자연수를 찾는 문제라는 것.</li>
</ol>
<p>따라서 random.sample을 쓰면 안되고 전체 리스트를 하나씩 매번 검사해야한다. while문을 써서 안에서 숫자의 개수가 3개가 되면 break하여 탈출하도록한다.</p>
<pre><code>import random

m_list = list(map(int, input().split()))

result = 1

while True:
    count = 0
    for i in range(len(m_list)):
        if (result % m_list[i] == 0):
            count += 1
    if count &gt;= 3:
        break
    result += 1

print(result)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[운영체제 Chap.1 - Overview (1)]]></title>
            <link>https://velog.io/@rohdoo_mani/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-Chap.1-Overview-1</link>
            <guid>https://velog.io/@rohdoo_mani/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-Chap.1-Overview-1</guid>
            <pubDate>Sat, 09 Aug 2025 02:38:01 GMT</pubDate>
            <description><![CDATA[<p>📘 1.1 What Operating Systems Do</p>
<p>✅ 컴퓨터 시스템의 구성 요소
컴퓨터 시스템은 네 가지 주요 구성 요소로 나뉜다:</p>
<p>하드웨어 (Hardware):</p>
<p>CPU, 메모리, I/O 장치 등으로 구성</p>
<p>계산과 처리를 위한 기본 자원을 제공</p>
<p>운영체제 (Operating System):</p>
<p>응용 프로그램들이 하드웨어를 사용할 수 있도록 제어 및 조정</p>
<p>응용 프로그램과 하드웨어 사이의 중개자 역할 수행</p>
<p>응용 프로그램 (Application Programs):</p>
<p>워드 프로세서, 스프레드시트, 컴파일러 등</p>
<p>사용자 작업을 처리하며, 자원을 어떻게 사용할지 결정</p>
<p>사용자 (Users):</p>
<p>시스템과 상호작용하는 개인 또는 그룹</p>
<p>✅ 운영체제의 역할
컴퓨터 자원을 적절하게 사용할 수 있는 실행 환경을 제공</p>
<p>사용자와 시스템 자원 사이에서 인터페이스를 제공</p>
<p>시스템 자원을 효율적이고 공정하게 배분하는 Resource Allocator</p>
<p>입출력 장치 제어 등 시스템의 동작을 관리하는 제어 프로그램 (Control Program)</p>
<p>✅ 운영체제에 대한 관점
사용자 관점</p>
<p>개인용 컴퓨터 (PC):</p>
<p>사용자 중심으로 설계되어 자원을 독점</p>
<p>사용의 편의성이 가장 중요한 목표</p>
<p>자원 효율성은 크게 고려하지 않음</p>
<p>다중 사용자 시스템 (터미널 기반):</p>
<p>여러 사용자가 동일한 시스템을 공유</p>
<p>CPU 시간, 메모리 등을 효율적으로 분배해야 함</p>
<p>자원 이용의 극대화가 설계의 핵심</p>
<p>워크스테이션 + 서버 환경:</p>
<p>각 사용자가 전용 자원을 가지되, 네트워크나 프린터 등 공유 자원 존재</p>
<p>사용의 용이성과 자원 효율성의 균형을 고려한 설계</p>
<p>시스템 관점</p>
<p><strong>리소스 관리자 (Resource Allocator)</strong>로서 시스템 자원의 효율적 배분을 담당</p>
<p>제어 프로그램으로서 하드웨어 장치의 동작을 감독하고 제어</p>
<p>✅ 운영체제의 정의
하나의 명확한 정의는 없음</p>
<p>컴퓨터 시스템을 구성하는 과정에서 발생하는 문제들을 해결하기 위한 방법으로 존재</p>
<p>본질적으로는 시스템에서 항상 동작하는 핵심 소프트웨어인 커널(Kernel) 이 운영체제의 핵심</p>
<p>📘 1.2 컴퓨터 시스템의 구성 (Computer-System Organization)</p>
<p>✅ 1. 컴퓨터 시스템의 연산 (Computer-System Operation)
현대의 범용 컴퓨터 시스템은 <strong>공통 버스(shared bus)</strong>를 통해 연결된:</p>
<p>여러 장치 제어기 (Device Controller): 예: 오디오, 비디오 장치</p>
<p>하나 이상의 CPU</p>
<p>그리고 공유 메모리(shared memory) 접근을 제공</p>
<p>🔹 부팅 과정
부트스트랩(Bootstrap) 프로그램:</p>
<p>컴퓨터가 켜질 때 처음 실행되는 프로그램</p>
<p>보통 ROM 또는 EEPROM에 저장되어 있음 (이런 저장 장치는 펌웨어라 불림)</p>
<p>시스템 초기화 (CPU 레지스터, 메모리, I/O 장치 상태 등)</p>
<p>운영체제의 커널(kernel) 을 찾아 메모리에 적재하고 실행 시작</p>
<p>UNIX 시스템에서는:</p>
<p>부팅 후 가장 먼저 실행되는 시스템 프로세스는 init</p>
<p>init 이후 시스템은 완전히 부팅된 상태가 되며, <strong>이벤트(event)</strong>를 기다리게 됨</p>
<p>🔹 인터럽트 (Interrupt)
사건 발생 → 하드웨어 또는 소프트웨어가 인터럽트를 통해 CPU에 알림</p>
<p>하드웨어 인터럽트: I/O 장치가 System Bus를 통해 발생시킴</p>
<p>소프트웨어 인터럽트: System Call을 통해 발생</p>
<p>인터럽트 처리 과정:</p>
<p>CPU가 현재 작업을 중단하고, 인터럽트 벡터에 정의된 고정된 주소로 이동</p>
<p>해당 위치에서 인터럽트 서비스 루틴(ISR) 실행</p>
<p>인터럽트 전에 수행 중이던 명령의 주소와 상태를 저장</p>
<p>ISR 종료 후, 저장된 주소를 Program Counter에 복원하고 작업 재개</p>
<p>✅ 2. 저장 장치 구조 (Storage Structure)
CPU는 메모리에서만 명령어를 가져올 수 있음 → 프로그램은 반드시 메모리에 있어야 함</p>
<p>🔹 메모리 종류
주 메모리 (RAM): 읽기/쓰기 가능, 휘발성</p>
<p>ROM/EEPROM: 읽기 전용 또는 제한된 쓰기 가능, 비휘발성 (예: Bootstrap Program 저장)</p>
<p>🔹 메모리 주소 및 명령
모든 메모리는 바이트 단위 배열 구조</p>
<p>각 바이트는 고유 주소를 가짐</p>
<p>CPU와 메모리 간 상호작용은 주로 Load/Store 명령어를 통해 수행:</p>
<p>Load: 메모리 → 레지스터</p>
<p>Store: 레지스터 → 메모리</p>
<p>🔹 주 메모리 한계 및 보조 저장 장치
주 메모리는:</p>
<p>크기가 작음 (모든 프로그램/데이터 저장 불가)</p>
<p>휘발성 (전원 차단 시 데이터 손실)</p>
<p>→ 이를 보완하기 위해 보조 저장 장치(Secondary Storage) 사용</p>
<p>대표 예시: 하드 디스크</p>
<p>요구사항: 대량 데이터의 영구적 저장</p>
<p>🔹 기타 저장 장치
캐시 메모리, SSD, CD-ROM, 자기 테이프 등</p>
<p>각 장치 간의 차이: 속도, 가격, 용량, 휘발성 여부</p>
<p>✅ 3. 입출력 구조 (I/O Structure)
🔹 운영체제와 입출력
장치의 다양성과 시스템 성능에 미치는 영향이 크기 때문에,
운영체제는 입출력 관리 코드에 상당한 비중을 둠</p>
<p><strong>디바이스 드라이버(device driver)</strong>를 통해 장치와 OS 간의 상호작용 수행</p>
<p>🔹 인터럽트 기반 I/O
소량 데이터 전송에는 적합하지만,
디스크 I/O와 같은 대량 전송에는 CPU Overhead가 큼</p>
<p>🔹 DMA (Direct Memory Access)
CPU의 개입 없이, 디바이스 ↔ 메모리 간 블록 단위 전송 수행</p>
<p>CPU는 블록 전송이 완료된 후에만 인터럽트를 받음</p>
<p>이 방식은 고속, 대량 데이터 전송 시 성능 향상에 유리</p>
<p><img src="https://velog.velcdn.com/images/rohdoo_mani/post/fddcb996-ddab-4162-b0b0-2721bc08c4d3/image.png" alt=""></p>
<table>
<thead>
<tr>
<th>항목</th>
<th>요약 설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>부트스트랩</strong></td>
<td>시스템 부팅 시 실행되는 초기 프로그램, 운영체제 커널을 메모리에 적재</td>
</tr>
<tr>
<td><strong>인터럽트</strong></td>
<td>하드웨어/소프트웨어가 CPU에 이벤트 발생을 알리는 메커니즘</td>
</tr>
<tr>
<td><strong>주 메모리</strong></td>
<td>RAM, 휘발성, 프로그램이 실행되기 위해 반드시 거쳐야 하는 영역</td>
</tr>
<tr>
<td><strong>보조 저장장치</strong></td>
<td>비휘발성, 데이터의 영구적 저장을 위한 저장 매체</td>
</tr>
<tr>
<td><strong>DMA</strong></td>
<td>CPU 개입 없이 대용량 데이터를 빠르게 전송할 수 있는 방식</td>
</tr>
</tbody></table>
<p>📘 1.3 컴퓨터 시스템 구조 (Computer-System Architecture)
✅ 1. 단일 처리기 시스템 (Single-Processor System)
하나의 주 CPU만 사용하여 범용 명령어(instruction)를 수행</p>
<p>보통 시스템에는 CPU 외에도 여러 개의 특수 목적 전용 처리기가 함께 존재</p>
<p>예: 디스크 제어기, 키보드 제어기, 그래픽 제어기 등</p>
<p>또는 데이터 전송을 위한 입출력 처리기 (I/O processor) 형태로 존재</p>
<p>✅ 2. 다중 처리기 시스템 (Multiprocessor System)
<strong>하나 이상의 CPU(또는 코어)</strong>를 가지며, 보통 버스, 메모리, I/O 장치 등을 공유</p>
<p>장점 3가지:</p>
<p>Throughput 향상 (처리량 증가)
→ 더 많은 작업을 동시에 수행할 수 있음
→ N개의 CPU → N배 성능은 아니며, Overhead 및 자원 경쟁 발생 가능</p>
<p>Economy of Scale (규모의 경제)
→ 여러 CPU가 <strong>하드웨어 자원(메모리, 디스크 등)</strong>을 공유하므로 비용 절감</p>
<p>Increased Reliability (신뢰성 향상)
→ 일부 CPU가 고장 나도 전체 시스템은 계속 작동 가능
→</p>
<p>Graceful Degradation: 속도만 느려지며 서비스 유지</p>
<p>Fault Tolerant: 고장에도 시스템이 완전히 문제 없이 동작 가능</p>
<p><img src="https://velog.velcdn.com/images/rohdoo_mani/post/7a0da0c0-2b50-4a40-9efe-83258604edba/image.png" alt=""></p>
<p>✅ 3. 다중 처리기 시스템의 구조적 유형
🔹 1) 대칭형(Symmetric) Multiprocessing - SMP
모든 CPU가 대등한 관계</p>
<p>각 CPU는 자신의 레지스터와 캐시를 가지고 있고, 메모리는 공유</p>
<p>🔹 2) 비대칭형(Asymmetric) Multiprocessing
하나의 CPU가 주 제어 역할, 나머지 CPU는 보조 역할 수행</p>
<p>주로 초기 설계 방식에서 사용됨</p>
<p>✅ 4. 메모리 접근 모델 (Memory Access Models)
모델    설명
UMA (Uniform Memory Access)    모든 CPU가 동일한 시간에 메모리에 접근 가능
NUMA (Non-Uniform Memory Access)    일부 메모리는 CPU에 가깝거나 멀어, 접근 시간이 달라짐</p>
<p>NUMA는 스케일 확장에 유리하지만, 성능 튜닝이 필요</p>
<p>✅ 5. 멀티코어 시스템 (Multicore System)
하나의 칩(SoC) 안에 여러 개의 CPU 코어를 포함</p>
<p>칩 내부 통신은 칩 간 통신보다 훨씬 빠름 → 효율성 증가</p>
<p>✔️ 모든 멀티코어 시스템은 다중처리기 시스템이지만,
모든 다중처리기 시스템이 멀티코어는 아님</p>
<p>✅ 6. 클러스터형 시스템 (Clustered Systems)
여러 대의 <strong>독립적인 컴퓨터 시스템(노드)</strong>를 네트워크로 연결해 하나의 시스템처럼 동작</p>
<p>노드들은 느슨하게 결합(Loosely Coupled)</p>
<p>각 노드는 단일 처리기 시스템 또는 멀티코어 시스템일 수 있음</p>
<p>🔹 클러스터의 목적: 고가용성(High Availability)
시스템 일부가 고장 나도 전체 서비스는 유지</p>
<p>🔹 클러스터의 구성 방식:
구성 방식    설명
비대칭형 클러스터링    한 노드는 Hot-Standby 상태로 대기 → 다른 노드가 고장 나면 takeover
대칭형 클러스터링    모든 노드가 동시에 작업을 수행하고 서로를 감시 → 리소스 효율성 높음</p>
<table>
<thead>
<tr>
<th>개념</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>SMP</strong></td>
<td>Symmetric Multiprocessing: CPU 간 대등한 관계</td>
</tr>
<tr>
<td><strong>NUMA</strong></td>
<td>CPU에 따라 메모리 접근 시간이 다름</td>
</tr>
<tr>
<td><strong>멀티코어</strong></td>
<td>하나의 칩에 여러 CPU 코어</td>
</tr>
<tr>
<td><strong>Graceful Degradation</strong></td>
<td>일부 고장에도 속도만 저하</td>
</tr>
<tr>
<td><strong>Fault Tolerant</strong></td>
<td>고장에도 시스템 완전 유지</td>
</tr>
<tr>
<td><strong>Clustered System</strong></td>
<td>여러 독립 시스템을 연결, 고가용성</td>
</tr>
<tr>
<td><strong>Hot-Standby</strong></td>
<td>대기 상태 노드가 고장 시 takeover</td>
</tr>
</tbody></table>
<p>📘 1.4 운영체제 구조 (Operating-System Structure)
✅ 1. 다중 프로그래밍 (Multiprogramming)
목적: CPU 이용률(Utilization) 극대화
→ 단일 사용자는 CPU나 I/O 장치를 항상 100% 활용할 수 없음
→ CPU가 항상 할 일을 가지도록 여러 작업을 메모리에 올려두고 관리</p>
<p>🔹 기본 아이디어
여러 <strong>작업(Job)</strong>을 메모리에 적재 (작업 풀은 디스크에 존재)</p>
<p>운영체제가 메모리에 있는 작업 중 하나를 선택하여 실행</p>
<p>실행 중 작업이 I/O 대기 상태가 되면 → 다른 준비 상태(ready)의 작업으로 전환</p>
<p>🔹 특징
CPU와 I/O 장치를 동시에 최대한 활용</p>
<p>단일 사용자 환경보다 자원 활용 효율이 높음</p>
<p>✅ 2. 멀티태스킹 (Multitasking, 시분할)
정의: 다중 프로그래밍의 논리적 확장</p>
<p>CPU가 매우 짧은 시간 간격으로 여러 작업을 번갈아 수행</p>
<p>각 사용자나 작업이 동시에 실행되는 것처럼 보이도록 함</p>
<p>🔹 필요 요소
CPU 스케줄링: 실행 준비 상태(ready queue)의 작업 중 누가 CPU를 쓸지 결정</p>
<p>다중 프로그래밍: 여러 작업을 메모리에 유지</p>
<p>✅ 3. 스케줄링 (Scheduling)
장기 스케줄링: 어떤 작업을 메모리로 불러올지 결정</p>
<p>단기 스케줄링(CPU Scheduling): CPU를 다음에 누가 쓸지 결정</p>
<p>✅ 4. 메모리 관리 기법
🔹 1) 스와핑 (Swapping)
메모리 공간 확보를 위해 프로세스 전체를 메모리 ↔ 디스크 간 교체</p>
<p>필요 시 프로세스를 다시 불러와 실행 (swap-in)</p>
<p>🔹 2) 가상 메모리 (Virtual Memory)
프로그램의 일부만 메모리에 올려도 실행 가능</p>
<p>장점:</p>
<p>프로그램 크기가 물리 메모리보다 커도 실행 가능</p>
<p>주 메모리를 크고 균등한 주소 공간으로 추상화</p>
<p><strong>논리 메모리(logical memory)</strong>와 <strong>물리 메모리(physical memory)</strong>를 분리</p>
<table>
<thead>
<tr>
<th>개념</th>
<th>목적</th>
<th>특징</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Multiprogramming</strong></td>
<td>CPU 이용률 극대화</td>
<td>여러 작업을 메모리에 두고 대기 작업을 실행</td>
</tr>
<tr>
<td><strong>Multitasking</strong></td>
<td>사용자 상호작용 향상</td>
<td>시분할로 여러 작업이 동시에 실행되는 것처럼 보임</td>
</tr>
<tr>
<td><strong>Swapping</strong></td>
<td>메모리 공간 확보</td>
<td>프로세스 전체를 디스크 ↔ 메모리 교체</td>
</tr>
<tr>
<td><strong>Virtual Memory</strong></td>
<td>메모리 추상화</td>
<td>일부만 메모리에 적재, 물리·논리 메모리 분리</td>
</tr>
</tbody></table>
<p>1.5 운영체제 연산 (Operating System Operations)</p>
<ol>
<li>Interrupt-driven 동작
현대 운영체제는 interrupt-driven 방식으로 동작.</li>
</ol>
<p>실행할 프로세스, I/O 서비스, 사용자 요청이 없으면 대기 상태에 들어감.</p>
<p>Trap (Exception)</p>
<p>소프트웨어가 생성하는 인터럽트.</p>
<p>발생 원인:</p>
<p>프로그램 오류 (예: 0으로 나누기, 잘못된 메모리 접근 등)</p>
<p>사용자 프로그램의 운영체제 서비스 요청 (시스템 콜)</p>
<ol start="2">
<li>이중 연산 모드 (Dual-mode Operation)
운영체제의 안정성과 보안을 위해 운영체제 코드와 사용자 코드 실행을 구분.</li>
</ol>
<p>모드 비트 (Mode bit): 하드웨어가 현재 모드를 식별.</p>
<p>모드 구분</p>
<p>사용자 모드 (User Mode)</p>
<p>사용자 애플리케이션 실행 시.</p>
<p>커널 모드 (Kernel Mode)</p>
<p>운영체제 서비스 실행 시.</p>
<p>모드 전환</p>
<p>사용자 애플리케이션이 OS 서비스를 요청 → 시스템 콜 발생 → 커널 모드로 전환.</p>
<p>Trap 또는 하드웨어 인터럽트 발생 시도 커널 모드로 전환.</p>
<p>부팅 과정</p>
<p>시스템 부팅 시 커널 모드에서 시작 → OS 로드 후 사용자 모드로 전환.</p>
<p>특권 명령 (Privileged Instructions)</p>
<p>커널 모드에서만 실행 가능.</p>
<p>잘못된 사용자 명령으로부터 운영체제와 다른 사용자 보호.</p>
<ol start="3">
<li>타이머 (Timer)
목적: 운영체제가 CPU 제어권(Control)을 유지하도록 보장.</li>
</ol>
<p>필요성:</p>
<p>사용자 프로그램이 무한 루프에 빠지는 상황 방지.</p>
<p>서비스 호출 실패로 OS 제어 불능 상태 방지.</p>
<p>동작 방식:</p>
<p>지정된 시간 이후 인터럽트 발생하도록 설정.</p>
<p>고정 시간 간격 또는 가변 타이머 사용 가능.</p>
<p>가변 타이머: 클럭이 tick할 때마다 카운터 감소 → 0이 되면 인터럽트 발생.</p>
<p>CPU 스케줄링이나 시분할 시스템에서 시간 제한을 구현하는 핵심 장치.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[PLT - Lecture 11]]></title>
            <link>https://velog.io/@rohdoo_mani/PLT-Lecture-11</link>
            <guid>https://velog.io/@rohdoo_mani/PLT-Lecture-11</guid>
            <pubDate>Fri, 13 Jun 2025 16:32:13 GMT</pubDate>
            <description><![CDATA[<h4 id="📘-11장-extensions-요약">📘 11장: Extensions 요약</h4>
<h4 id="✅-1-variable-initialization-변수-초기화">✅ 1. Variable Initialization (변수 초기화)</h4>
<p>📌 개념
변수 초기화는 C에서 int a = 1;처럼 새로운 변수에 값을 할당하는 구조입니다.
Racket에서도 같은 의미로 (let ([a 1]) ...) 형태를 사용합니다.</p>
<p>📌 AST 표현
racket</p>
<pre><code>(letC &#39;a (numC 10)     ; a = 10
  (plusC (idC &#39;a) (numC 5))) ; a + 5</code></pre><p>해석: &#39;a를 10으로 바인딩하고, 그 환경에서 a + 5를 수행하라.</p>
<p>📌 추상 구문 트리 (AST)</p>
<pre><code>
          letC
         /  |   \
      &#39;a  numC  plusC
           10    /  \
               idC  numC
                &#39;a   5</code></pre><h4 id="✅-2-desugaring-설탕-제거-letc를-appc로-변환">✅ 2. Desugaring (설탕 제거: letC를 appC로 변환)</h4>
<p>📌 아이디어
letC는 특별한 구조가 아니며, <strong>람다(lamC)</strong>와 함수 호출(appC)을 이용해서 똑같이 표현할 수 있습니다.</p>
<p>📌 변환 예시 (desugaring)
racket</p>
<pre><code>(letC &#39;a (numC 10) 
  (plusC (idC &#39;a) (numC 5)))</code></pre><p>⇨ 아래와 같이 변환됨</p>
<p>racket</p>
<pre><code>(appC 
  (lamC &#39;a (plusC (idC &#39;a) (numC 5))) 
  (numC 10))</code></pre><p>📌 인터프리터에서의 letC 해석
racket</p>
<pre><code>[letC (name val body)
  (interp (appC (lamC name body) val) env store)]</code></pre><p>즉, let은 함수의 호출(appC) 로 해석됩니다.</p>
<h4 id="✅-3-lists-리스트-구조">✅ 3. Lists (리스트 구조)</h4>
<p>📌 표현
racket</p>
<pre><code>(list 10 20 30)    ; 또는 (cons 10 (cons 20 (cons 30 empty)))</code></pre><p>📌 AST 예시
racket</p>
<pre><code>(consC (numC 10) 
  (consC (numC 20) 
    (consC (numC 30) emptyC)))</code></pre><p>📌 재귀적 구조
consC는 리스트의 head와 tail로 구성됨</p>
<p>재귀적으로 연결되어 리스트 전체를 구성함</p>
<p>📌 Value 타입 확장
racket</p>
<pre><code>(listV elems)   ; listof Value 타입으로 해석됨</code></pre><p>📌 해석 방식
racket</p>
<pre><code>[consC (head tail)
  (let ([head-val (interp head ...)])
    (listV (cons head-val (listV-elems (interp tail ...)))))]</code></pre><h4 id="✅-4-objects-객체-구조">✅ 4. Objects (객체 구조)</h4>
<p>📌 예시 (C 스타일)
c</p>
<pre><code>class Point2D {
  int x = 3;
  int y = 5;
  int add(int a) { return x + a; }
}</code></pre><p>📌 AST 표현
racket</p>
<pre><code>(objC 
  (list &#39;x &#39;y &#39;add) 
  (list (numC 3) (numC 5) 
        (lamC &#39;a (plusC (idC &#39;x) (idC &#39;a)))))</code></pre><p>📌 내부 구성
objC는 두 개의 리스트로 구성됨</p>
<p>names : 변수명/메서드명 (symbol 리스트)</p>
<p>vals : 해당 값 (numC 또는 lamC 같은 ExprC)</p>
<p>📌 의미
객체는 일종의 환경(environment) 으로 해석됨
즉, names와 vals를 짝지어 바인딩하면 환경처럼 작동함</p>
<p>📌 핵심 요약표</p>
<table>
<thead>
<tr>
<th>개념</th>
<th>표현</th>
<th>역할</th>
</tr>
</thead>
<tbody><tr>
<td><code>letC</code></td>
<td><code>(letC name val body)</code></td>
<td>지역 변수 선언, 초기화 후 body 실행</td>
</tr>
<tr>
<td>Desugaring</td>
<td><code>letC → appC(lamC ...)</code></td>
<td>let을 함수 호출로 단순화</td>
</tr>
<tr>
<td><code>list</code></td>
<td><code>consC head tail</code></td>
<td>재귀적 리스트 구성</td>
</tr>
<tr>
<td><code>listV</code></td>
<td>리스트 타입 결과값</td>
<td>Value 타입에 리스트 추가</td>
</tr>
<tr>
<td><code>objC</code></td>
<td><code>(objC names vals)</code></td>
<td>이름-값 환경 구조, 객체 표현</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[PLT - Lecture 10 (part.2)]]></title>
            <link>https://velog.io/@rohdoo_mani/PLT-Lecture-10-part.2</link>
            <guid>https://velog.io/@rohdoo_mani/PLT-Lecture-10-part.2</guid>
            <pubDate>Fri, 13 Jun 2025 15:20:23 GMT</pubDate>
            <description><![CDATA[<p>🔷 Result 기반 인터프리터의 전체 구조 및 동작 원리</p>
<h4 id="📌-1-기존-문제점-값만-리턴하는-인터프리터의-한계">📌 1. 기존 문제점: 값만 리턴하는 인터프리터의 한계</h4>
<p>기존의 인터프리터는 다음처럼 오직 값만 반환했습니다</p>
<p>racket</p>
<pre><code>(define (interp [expr : ExprC] [env : Env]) : Value
  ...)</code></pre><p>하지만 아래 같은 상황에서는 &quot;값만&quot;으론 부족합니다:</p>
<p>racket</p>
<pre><code>(set x 3)
(set x 5)</code></pre><p>위 예시처럼 변수에 값을 재할당하는 mutation (상태 변화) 를 표현하려면, 단순히 값만 반환해서는 변경된 메모리 상태(store) 를 추적할 수 없습니다.</p>
<h4 id="🆕-2-해결책-결과result를-값과-store로-구성">🆕 2. 해결책: 결과(Result)를 값과 store로 구성</h4>
<p>✅ Result 타입 도입
racket</p>
<pre><code>(define-type Result
  [vs (v : Value) (s : Store)])</code></pre><p>v: 계산 결과 (Value)</p>
<p>s: 계산 후의 메모리 상태 (Store)</p>
<p>vs는 value와 store를 하나의 단위로 포장한 결과</p>
<h4 id="🧠-3-인터프리터-시그니처-변경">🧠 3. 인터프리터 시그니처 변경</h4>
<p>✅ 이전
racket</p>
<pre><code>(define (interp expr env) : Value)</code></pre><p>✅ 이후 (store 추가 + 결과 타입 변경)
racket</p>
<pre><code>(define (interp [expr : ExprC] [env : Env] [store : Store]) : Result)</code></pre><p>이제 인터프리터는 store를 입력으로 받아, 계산된 value와 store를 함께 반환합니다.</p>
<h4 id="🧩-4-각-표현식-케이스별-구현-변경">🧩 4. 각 표현식 케이스별 구현 변경</h4>
<p>✅ numC (숫자 리터럴)
racket</p>
<pre><code>[numC (n)
  (vs (numV n) store)]</code></pre><p>숫자를 그대로 numV로 감싸고, store는 바뀌지 않으므로 그대로 반환.</p>
<p>✅ idC (변수)
racket</p>
<pre><code>[idC (n)
  (vs (fetch (lookup n env) store) store)]</code></pre><p>env로부터 변수 n의 위치를 lookup</p>
<p>그 위치에서 값을 fetch</p>
<p>값은 반환하되, store는 그대로 유지</p>
<p>✅ lamC (함수 정의)
racket</p>
<pre><code>[lamC (a b)
  (vs (closV a b env) store)]</code></pre><p>lambda는 클로저로 평가됨: (closV arg body env)</p>
<p>정의 당시의 환경도 함께 저장해야 나중에 적용 가능</p>
<p>store는 변화 없음</p>
<p>✅ seqC (순차 실행)
racket</p>
<pre><code>[seqC (e1 e2)
  (type-case Result (interp e1 env store)
    [vs (v1 s1)
      (interp e2 env s1)])]</code></pre><p>e1을 먼저 평가하여 결과 store인 s1을 얻음</p>
<p>s1을 반영하여 e2를 평가</p>
<p>최종 결과는 e2의 평가 결과</p>
<p>✅ setC (값 할당)
racket</p>
<pre><code>[setC (var val)
  (type-case Result (interp val env store)
    [vs (v s)
      (let ([loc (lookup var env)])
        (vs v (override-store (cell loc v) s)))])]</code></pre><p>val을 먼저 평가해 값 v와 store s를 얻음</p>
<p>변수 var의 위치를 lookup</p>
<p>store에 새로운 cell (loc → v)을 추가로 덮어쓰기 (mutation)</p>
<p>업데이트된 store와 함께 값 v 반환</p>
<p>✅ plusC (덧셈)
racket</p>
<pre><code>[plusC (l r)
  (type-case Result (interp l env store)
    [vs (vl sl)
      (type-case Result (interp r env sl)
        [vs (vr sr)
          (vs (num+ vl vr) sr)])])]</code></pre><p>l을 먼저 평가하고 store가 sl로 바뀜</p>
<p>그 상태에서 r을 평가해야 하므로 sl을 넘김</p>
<p>최종 결과는 vl + vr, store는 sr로 업데이트된 상태</p>
<p>✅ appC (함수 호출)
racket</p>
<pre><code>[appC (f a)
  (type-case Result (interp f env store)
    [vs (vf sf)
      (type-case Result (interp a env sf)
        [vs (va sa)
          (let ([loc (new-loc)])
            (interp (closV-body vf)
                    (extend-env (bind (closV-arg vf) loc)
                                (closV-env vf))
                    (override-store (cell loc va) sa)))]))]</code></pre><p>f를 해석하여 클로저 vf와 store sf 획득</p>
<p>a를 해석하여 인자값 va와 store sa 획득</p>
<p>new-loc을 생성하여 인자 이름 → 위치 바인딩 추가</p>
<p>환경과 store를 확장한 후 클로저 본문 실행</p>
<h4 id="🧾-5-핵심-연산-요약">🧾 5. 핵심 연산 요약</h4>
<p>연산    설명
lookup name env    name → location (Environment)
fetch loc store    location → value (Store)
override-store    store에 새로운 cell 추가 (앞에 덮어쓰기처럼 붙임)
extend-env    환경에 새로운 바인딩 추가
new-loc    새로운 위치 (메모리 주소) 생성 함수</p>
<h4 id="🧠-정리">🧠 정리</h4>
<p>Result 타입 도입은 “값 + 상태(store)” 동시 추적을 가능하게 한다.</p>
<p>이제 모든 ExprC는 store를 입력받고, 값을 계산하는 동시에 상태 변경 결과를 함께 출력한다.</p>
<p>이렇게 하면 mutable 상태, 변수 재할당, 순차적 실행 등이 정확히 해석 가능하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[오픈소스SW - chapter 7]]></title>
            <link>https://velog.io/@rohdoo_mani/%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4SW-chapter-7</link>
            <guid>https://velog.io/@rohdoo_mani/%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4SW-chapter-7</guid>
            <pubDate>Wed, 11 Jun 2025 12:02:10 GMT</pubDate>
            <description><![CDATA[<h4 id="스태시">스태시</h4>
<p>작업 브랜치를 변경하려면 워킹 디렉터리는 깨끗한(clean) 상태로 정리되어 잇어야 함
워킹 디렉터리에 작업 중인 내용이나 커밋되지 않은 변경 사항들이 남아 있으면 브랜치를 변경할 수 없음
현재 수정 작업을 멈추고, 다른 브랜치에 있는 코드를 수정하려면 <strong>스태시(stash)</strong> 기능을 사용할 수 있음
간단하게 설명 : &#39;안전한 보관&#39;</p>
<p>깃은 완료되지 않은 작업(커밋되지 않은 변경 내용)이 남아 있을 때, 현재 작업을 임시로 저장할 수 있는 <strong>스태시 기능을 제공</strong>함
스태시는 &#39;현재 워킹 디렉터리 내역을 별도의 스택 영역에 잠시 보관하라&#39;는 명령임
스태시는 브랜치를 이동할 때 작업 중인 내용 때문에 워킹 디렉터리가 충돌하는 것을 방지하는 데 사용함
스태시 명령을 실행하면 현재 작업 중인 내용은 임시 저장되고, 수정 전 마지막 커밋 상태로 돌아감
이전 커밋 후 작업하지 않은 상태의 워킹 디렉터리가 됨</p>
<p>스태시를 하려면 stash 명령어를 실행함
기본 명령어로 스태시를 실행하거나 옵션을 사용하여 추가 기능을 선택할 수 있음
스태시는 로컬 저장소에서만 사용 가능함</p>
<p>스태시의 임시 스택 영역에 작업 중인 코드 저장
스태시 명령어는 수정 중인 내역을 커밋하지 않고도 <strong>브랜치를 이동할 수 있게 워킹 디렉터리를 깨끗이 청소함</strong>
커밋 대신 스태시 명령을 실행하면 됨
스태시는 영구적인 커밋 기록대신 <strong>현재 작업들을 임시 스택 영역에 저장</strong>함</p>
<p>명령어</p>
<pre><code>$ git stash</code></pre><p>스태시 여러 개 생성 명령어</p>
<pre><code>$ git stash save</code></pre><p>스태시는 스택 구조로 여러 번 실행하여 저장할 수 있음
스태시가 여러 개 있을 때 각각의 스태시를 구별할 수 있도록 메시지도 추가할 수 있음</p>
<pre><code>$ git stash save &quot;WIP: 메시지~~~&quot;</code></pre><p>스태시의 저장 영역은 스택 구조임
스택 : FILO(First In Last Out) 구조, 마지막에 입력한 자료가 제일 먼저 출력되는 데이터 저장 방식임</p>
<p>스태시에 저장된 스택은 list 옵션을 사용하여 확인할 수 있음</p>
<pre><code>$ git stash list # 스태시 목록 </code></pre><p>stash 명령어로 저장된 객체들의 목록이 출력됨
스태시 이름은 stash@{번호} 형태로 순차적으로 부여됨
목록 번호는 0부터 시작</p>
<p>스태시를 실행한 후 방금 전의 스태시와 현재 워킹 디렉터리 간 차이를 확인</p>
<pre><code>$ git stash show # 내용 비교</code></pre><p>현재 워킹 디렉터리 내용과 스태시된 내용 간 파일 변화를 간략하게 출력함</p>
<pre><code>$ git stash show -p stash@{0} </code></pre><p>스태시를 사용하는 목적은 <strong>현재 워킹 디렉터리를 커밋하지 않고 임시로 저장하기 위해서</strong>임
임시 저장된 내용은 다른 수정 작업을 완료한 후에 다시 불러와 사용할 수 있음</p>
<p>임시 저장한 스태시 불러오기
버그ㅡ를 수정한 후 다시 원래의 작업 브랜치로 돌아옴
이전에 수정한 내역들이 남아 있지 않음
스태시에 임시 저장한 작업 내용들을 읽어 다시 적용할 수 있음</p>
<pre><code>$ git stash pop #스태시 읽기</code></pre><p>스택에서 저장된 작업 내용을 읽어 올 때는 제일 마지막에 저장된 내용을 읽어옴
데이터의 순차적 특징은 스택의 원리 때문</p>
<p>스태시의 스택에 저장된 작업 내용이 다시 적용됨
스태시는 스택에서 내용을 읽어 올 때 현재 브랜치의 워킹 디렉터리와 자동으로 병합함
자동 병합이 성공하면 읽어 온 내용을 스택에서 제거함
스태시에서 임시 저장된 작업 내용을 복원한 후 다시 스태시 목록을 확인함</p>
<pre><code>$ git stash list # 스태시 목록 확인</code></pre><p>스태시를 복원할 때 워킹 디렉터리의 상태는 깨끗해야 함
스택에 저장된 스태시 내용이 다시 워킹 디렉터리로 복구될 때, 수정된 작업 내용과 현재 워킹 디렉터리를 병합하기 때문임
복구되는 브랜치의 워킹 디렉터리가 깨끗하지 않다면 병합 과정에서 충돌이 발생할 가능성이 많음돌이 발생할 가능성이 많음</p>
<p>특히 스태시를 복원할 때 같은 파일에서 동일한 부분을 변경했다면 즉시 충돌이 발생함
스태시를 복원할 때 충돌이 생기면 직접 문제를 해결해야 함
복원하는 도중 충돌이 생기면 스태시는 스택에 저장된 내용을 자동으로 삭제하지 않음
직접 충돌을 해결한 후 스태시 목록을 수동으로 삭제해야 함
스태시 충돌이 예상된다면 스태시용 브랜치를 하나 생성해서 작업하는 것을 추천</p>
<p>새로운 브랜치를 생성한 후 스태시를 적용하는 명령어</p>
<pre><code>$ git stash branch 브랜치이름</code></pre><p>스태시 복사
스태시는 브랜치 작업들을 임시로 저장할 때 사용함
임시 저장된 작업을 스태시 명령 이전의 브랜치 상태로 되돌려 놓음
스태시를 사용한 저장과 복원은 서로 다른 브랜치에도 가능함
반드시 이전에 실행한 브랜치와 같은 브랜치에서 할 필요는 없음</p>
<p>다른 브랜치에서 스태시를 실행한 후 새로운 test 브랜치를 생성하여 스태시를 복원함
스태시 스택에 저장된 항목들은 어느 브랜치에서나 복원이 가능함
apply 옵션은 스택에 저장된 항목을 불러와 현재 브랜치로 복원함</p>
<pre><code>$ git stash apply</code></pre><p>스태시 복원 : 옵션 2개 pop, apply
pop 명령어 : 스택 내용을 복원한 후 스택 목록에서 자동으로 삭제함.
pop 명령어는 스택 내용을 워킹 디렉터리로 이동하는 것과 같음
apply 명령어 : 스택 목록을 읽은 후 자동으로 삭제하지 않기 때문에 반복적으로 스택에서 스태시 내용을 읽어올 수 있음
apply 명령어는 스태시 내용을 워킹 디렉터리로 복사하는 것과 같음</p>
<p>pop : 가져오기, apply : 복사</p>
<p>apply 명령어로 워킹 디렉터리를 복구할 때는 스택에서 자동으로 삭제되지 않는다. 이때 별도로 명령을 실행해야만 스태시 목록에서 삭제된다.</p>
<pre><code>$ git stash drop</code></pre><p>워킹 디렉터리 청소
clean 명령어를 사용해서 워킹 디렉터리에 있는 추적되지 않는 파일들을 찾아 삭제함
clean 명령어를 실행하는 순간 워킹 디렉터리의 추적되지 않는 모든 파일을 삭제함</p>
<pre><code>$ git clean</code></pre><pre><code>$ git clean -f $ 잘못 삭제하는 것을 방지하</code></pre><pre><code>$ git clean -n $ </code></pre><pre><code>$ git clean -d $ 추적되지 않는 파일만 별도로 삭제</code></pre><pre><code>$ git clean -x $ gitignore 파일에 등록한 파일은 삭제 X</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[오픈소스SW - chapter 6]]></title>
            <link>https://velog.io/@rohdoo_mani/%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4SW-chapter-6</link>
            <guid>https://velog.io/@rohdoo_mani/%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4SW-chapter-6</guid>
            <pubDate>Tue, 10 Jun 2025 17:09:45 GMT</pubDate>
            <description><![CDATA[<h4 id="새로운-작업">새로운 작업</h4>
<p>브랜치(branch) : 저장 공간 하나에서 가상의 또 다른 저장 공간을 만드는 것
커밋 : 파일의 수정 이력 관리에 사용
브랜치 : 프로젝트를 독립적으로 관리하는데 사용</p>
<p><strong>깃 브랜치 특징</strong> : 기존 폴더를 복제하는 것과 다르게 가상 폴더를 사용하여 개발 작업을 구분</p>
<p>깃의 브랜치는 작업 폴더를 실제로 복사하지 않고, 가상 폴더로 생성함
외부적으로는 물리적인 파일 하나만 있는 것으로 보임
생성된 작업 폴더는 물리적으로 복제된 구조보다 유연하게 처리할 수 있음
브랜치로 생성된 가상 폴더는 빠르게 공간이동이 가능</p>
<p><strong>독립적인 동작</strong>
브랜치를 이용하면 원본 폴더와 분리하여 독립적으로 개발 작업을 수행할 수 이음
기존에는 소스 코드의 작업 폴더를 별도로 생성함
물리적으로 복사된 각자의 폴더에서 코드를 작업한 후 소스 코드 2개를 다시 하나로 합쳐야 했음 - 매우 힘든 작업
깃과 같은 VCS를 이용하면 분리된 코드를 좀 더 쉽게 병합할 수 있음
분리된 브랜치에서 소스 코드를 각자 수정한 후 원본 코드에 병합하는 명령만 실행하면 됨
깃의 브랜치는 규모가 큰 코드 수정이나 병합을 처리할 때 매우 유용</p>
<p><strong>빠른 동작</strong>
보통 다른 VCS는 브랜치를 생성할 때 내부 파일 전체를 복사
파일 크기가 매우 크다면 브랜치를 생성하는 데 시간이 오래 걸림
깃의 브랜치 기능은 다른 VCS보다 가볍고, 브랜치 전환이 빠른것이 특징
깃은 <strong>Blob</strong> 개념을 도입하여 내부를 구조화함
브랜치를 변경할 때 포인터를 이용해서 빠르게 전환함
브랜치 명령을 사용하면 내부적으로 커밋을 하나 생성하여 브랜치로 할당함</p>
<p>다른 VCS는 파일 전체를 복사하지만, 깃은 41바이트를 가지는 해시(SHA1) 파일 하나만 만들면 됨 - 브랜치를 더 빠르게 생성 가능</p>
<h4 id="실습">실습</h4>
<p>기본 브랜치
모든 커밋과 이력은 브랜치에 기록
깃은 최소한 브랜치가 1개 이상 필요함
저장소를 처음 초기화하면 워킹 디렉터리는 master 브랜치 하나를 자동으로 생성됨
첫 번째 커밋은 master 브랜치에서 시작함</p>
<p>브랜치 목록 확인 명령어</p>
<pre><code>$ git branch</code></pre><p>깃은 master 브랜치를 기준으로 새로운 브랜치를 생성
브랜치는 공통된 커밋을 가리키는 지점
브랜치는 커밋처럼 SHA1 해시키를 가리킴
커밋의 SHA1 해시키는 기억하기가 어렵기 때문에 특정 커밋을 가리키는 별칭을 만드는 것임
이렇게 만든 별칭이 브랜치이다.
브랜치를 생성한다는 의미 : 기존 브랜치 또는 커밋에 새로운 연결 고리를 하나 더 만드는 것과 같음
<img src="https://velog.velcdn.com/images/rohdoo_mani/post/ef462b98-e170-41e5-9fca-2ac1d2e07abc/image.png" alt=""></p>
<p>새 브랜치를 생성하면 포인터만 있는 브랜치가 생성됨
일반적으로 브랜치 생성 명령을 실행하면 현재 커밋을 가리키는 HEAD를 기준으로 생성됨
HEAD는 현재 마지막 커밋을 가리킴
새롭게 브랜치가 생성되면 독립된 공간을 할당함
기존 브랜치의 소스 코드에 영향을 주지 않고 새로운 작업을 할 수 있음</p>
<p>브랜치 생성 = 사용자가 정의한 새로운 사용자 브랜치를 만듬
브랜치 : 또 하나의 개발 분기점
생성개수 제한 X</p>
<p>브랜치 생성 명령어</p>
<pre><code>$ git branch 브랜치이름 커밋ID</code></pre><p>branch 명령어 뒤에 브랜치 이름을 인자 값으로 추가함
브랜치 이름만 입력하면 현재 HEAD 포인터를 기준으로 새로운 브랜치를 생성함
직접 커밋 ID 인자 값을 지정하면, 지정한 커밋 ID를 기준으로 브랜치를 생성함</p>
<p>브랜치 이름은 슬래시(/)를 사용하여 계층적인 구조로 만들어서 사용할 수 있음
기호,마침표 - 시작 X
연속적인 마침표, 빈칸, 공백 문자, 물결, 캐럿, 물음표, 별표, 대괄호, 아스키 문자 - 포함 X</p>
<h4 id="브랜치-확인">브랜치 확인</h4>
<p>브랜치 확인 명령어</p>
<pre><code>$ git branch</code></pre><p>브랜치 해시 명령어</p>
<pre><code>$ git rev-parse 브랜치이름 # 현재 브랜치가 어떤 커밋 해시값을 가리키는지 확인</code></pre><p>브랜치 세부 사항 확인 명령어</p>
<pre><code>$ git branch -v</code></pre><h4 id="브랜치-이동">브랜치 이동</h4>
<p>브랜치 이동 명령어</p>
<pre><code>$ git checkout 브랜치이름</code></pre><p>깃은 하나의 워킹 디렉터리만 가지고 있다. 따라서 다른 브랜치에서 작업하고 싶다면 반드시 브랜치를 변경해서 워킹 디렉터리를 재설정해야한다.
장점 : 빠르게 포인터를 이용하여 브랜치를 이동할 수 있다는 점</p>
<p>원리
HEAD 정보는 항상 변경된 브랜치의 마지막 커밋을 가리킴
브랜치가 이동하면 HEAD 포인터도 함께 이동함</p>
<p>이전 브랜치로 이동 명령어</p>
<pre><code>$ git checkout -</code></pre><p>워킹 디렉터리 정리
체크아웃을 사용하여 브랜치를 이동할 때 주의해야 함
현재 작업하고 있는 워킹 디렉터리를 정리하고 넘어가야 함
워킹 디렉터리 안에서 작성하던 내용이 있고, 커밋을 하지 않았다면 체크아웃할 때 경고가 발생함.</p>
<p>워킹 디렉터리에서 작업하다 커밋하지 않고 남겨 둔 상태에서 다른 브랜치로 checkout하면 이처럼 브랜치 이동이 제한된다.
깃은 향후 충돌을 방지하려고 워킹 디렉터리에 작업이 남아 있다면 경고 메세지를 보여 주고 브랜치를 변경할 수 없게 제한한다.</p>
<h4 id="브랜치-공간">브랜치 공간</h4>
<p>브랜치 로그
로그를 출력할 때 브랜치 흐름도 같이 보려면 --graph 옵션을 함께 사용한다.
명령어, 옵션</p>
<pre><code>$ git log --graph --all # 모든 로그를 출력함</code></pre><h4 id="head-포인터">HEAD 포인터</h4>
<p>깃은 마지막 커밋을 가리키는 HEAD 포인터를 부모 커밋으로 대체하여 사용
브랜치를 이동하면 HEAD 포인트도 이동됨
브랜치가 여러 개면 HEAD 포인트도 여러 개임
브랜치마다 마지막 커밋 ID를 가리키는 HEAD 포인터가 하나씩 있음</p>
<p>상대적 위치
HEAD를 기준으로 상대적 커밋 위치도 지정할 수 있음
상대적 커밋 위치를 지정할 때는 캐럿(^)과 물결(~) 기호를 같이 사용함</p>
<p>AHEAD, BHEAD
원격 저장소와 연동하여 깃을 관리한다면 브랜치마다 HEAD가 2개 있음
(로컬 저장소 브랜치의 HEAD 포인터, 원격 저장소 브랜치의 HEAD 포인터)
둘은 물리적으로 서로 다른 저장소임
두 저장소의 마지막 커밋 위치가 일치하지 않을 수 있음
AHEAD, BHEAD는 서로 다른 저장소 간 HEAD 포인터의 위치 차이를 의미함</p>
<p>AHEAD
서버로 전송되지 않은 로컬 커밋이 있는 것
<img src="https://velog.velcdn.com/images/rohdoo_mani/post/40cd741b-e737-44fc-a941-fcdc40714bcb/image.png" alt="">
로컬 저장소의 HEAD 포인터를 기준으로 로컬 브랜치에 있는 커밋이 서버의 커밋 개수보다 많은 경우</p>
<p>BHEAD
로컬 저장소로 내려받지 않은 커밋이 있는 것
<img src="https://velog.velcdn.com/images/rohdoo_mani/post/5622bcd3-089a-4418-8f0e-a6b298166322/image.png" alt="">
다른 개발자가 코드를 수정하여 원격 저장소의 커밋이 자신의 로컬 저장소보다 더 최신 상태인 것을 의미함</p>
<h4 id="생성과-이동">생성과 이동</h4>
<p>자동 이동 옵션</p>
<pre><code>$ git checkout -b # 브랜치 생성과 이동을 한번에!</code></pre><p>이전 커밋으로 checkout하는 명령어</p>
<pre><code>$ git checkout HEAD~1 # 현재의 한 단계 전</code></pre><p>여러 단계 이전으로 이동하고싶으면 ~뒤의 숫자만 바꾸자.</p>
<h4 id="원격-브랜치">원격 브랜치</h4>
<p>리모트 브랜치
원격 저장소에 생성한 브랜치</p>
<p>로컬 저장소의 feature 브랜치를 원격 저장소의 function 브랜치로 push</p>
<pre><code>$ git push -u origin feature:function # 다른 이름으로 브랜치 전송</code></pre><p>업스트림 트래킹
업스트림(upstream) : 브랜치 추적을 다르게 표현한 것
업스트림 트래킹 : 로컬 저장소의 브랜치와 원격 저장소의 브랜치는 업로드할 수 있도록 매칭
<img src="https://velog.velcdn.com/images/rohdoo_mani/post/c1be77e4-f45b-4d0b-975f-6d42c15c2490/image.png" alt="">
트래킹 브랜치(업스트림)는 리모트 브랜치와 로컬 브랜치를 연결해 주는 중간다리 역할</p>
<p>트래킹 브랜치 목록 확인 명령어</p>
<pre><code>$ git branch -vv # 트래킹 브랜치 목록 확인 명령어</code></pre><p>업스트림 동작을 위한 트래킹 브랜치를 직접 명령어를 실행하여 생성하기</p>
<pre><code>$ git checkout --track origin/브랜치이름 # 새로운 업스트림 만들기</code></pre><p>원격 브랜치 복사 명령어</p>
<pre><code>$ git checkout -b 새이름 origin/브랜치 이름 
# 생성된 원격 저장소의 리모트 브랜치를 이용해 로컬에도 새로운 브랜치를 생성하여 동기화</code></pre><p>업스트림을 직접 설정하면 원격 저장소로 트래킹 브랜치가 설정됨</p>
<pre><code>$ git branch -u origin/브랜치이름 </code></pre><p>기존 브랜치를 특정 원격 브랜치로 추적함</p>
<pre><code>$ git fetch # 서버의 브랜치 정보 갱신</code></pre><pre><code>$ git branch -r # 원격 브랜치 목록</code></pre><h4 id="브랜치-전송">브랜치 전송</h4>
<p>깃의 push 작업은 로컬 저장소의 파일들을 원격 저장소로 전송함
파일뿐만 아니라 브랜치 정보와 커밋까지 모두 전송함</p>
<p>처음에는 커밋과 브랜치를 푸시하는 데 업스트림 설정이 필요함
원격 저장소 연결만으로 업스트림이 자동으로 설정되지는 않음</p>
<p>처음에는 수동으로 트래킹 브랜치와 업스트림 설정을 해야함 (명령어)</p>
<pre><code>$ git push --set-upstream origin master</code></pre><p>브랜치 페치
리모트 브랜치 페치는 일반적인 커밋 페치와 동일
리모트 브랜치가 페치되면 깃은 단순히 원격저장소별칭/브랜치 포인터만 생성
원격 저장소에서 페치된 커밋들을 새로운 로컬 브랜치로 반영하려면 병합 명령을 실행해야함</p>
<pre><code>$ git merge 원격저장소별칭/브랜치이름</code></pre><p>브랜치 삭제
해당 브랜치 내용과 커밋을 모두 삭제하면 됨
주의할 점 : 현재 자신이 있는 브랜치는 삭제 X</p>
<p>일반적인 삭제 방법
브랜치 삭제할때는 -d 옵션 사용</p>
<pre><code>$ git branch -d 브랜치이름</code></pre><p>-d 옵션은 스테이지 상태가 깨끗할 때만 삭제를 허용함
워킹 디렉터리에 작업한 기록이 있거나 add 명령어로 스테이지의 인덱스가 변경된 상태라면 삭제 X
병합되지 않은 브랜치는 -d 옵션으로 삭제 X</p>
<p>강제 삭제 방법</p>
<pre><code>$ git branch -D 브랜치이름</code></pre><p>-D 옵션으로 강제로 삭제</p>
<p>리모트 브랜치 삭제 방법</p>
<pre><code>$ git push origin --delete 리모트브랜치이름</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[오픈소스 SW - chapter 5]]></title>
            <link>https://velog.io/@rohdoo_mani/%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-SW-chapter-5</link>
            <guid>https://velog.io/@rohdoo_mani/%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-SW-chapter-5</guid>
            <pubDate>Tue, 10 Jun 2025 13:37:42 GMT</pubDate>
            <description><![CDATA[<h4 id="서버-저장소">서버 저장소</h4>
<p>서버 저장소 : 원격(remote) 저장소
서버 저장소는 로컬 저장소의 코드를 복제한 복사본이라고 할 수 있다.
서버를 이용해 코드를 안전하게 보관, 다른사람과 공유 &amp; 협업 가능</p>
<p>협업 저장소
깃은 여러 개발자와 협업하려고 탄생한 도구</p>
<p>원격 저장소가 있다면 언제 어디에서든지 개발을 이어서 할 수 있다.
원격 저장소에 작업한 코드를 업로드하고 다른 곳에서 업로드된 코드를 내려받아 작업을 이어나갈 수 있음</p>
<p>깃은 분산된 저장소 여러 개를 하나로 통합하고, 최신 코드를 배포할 수 있음
서버 저장소는 여러 컴퓨터에 동일한 깃 저장소를 복제하고, 작업한 결과물을 다시 서버로 통합
깃의 분산형 관리 체계는 다수의 사람과 협업하는 데 매력적임
새로운 멤버가 참여할 때, 지금까지 작업한 소스 코드의 마지막 버전을 공유해야 함(<strong>깃의 원격 저장소 주소만 알려주면 모두 해결됨</strong>)</p>
<h4 id="깃허브-서버-준비">깃허브 서버 준비</h4>
<p>직접 서버를 운영하지 않아도 전문적인 깃 호스팅으로 서버를 대체할 수 있음
호스팅을 받으면 직접 서버를 관리하지 않아도 쉽게 원격 저장소를 운영할 수 있음</p>
<h4 id="깃허브-연동-및-원격-등록">깃허브 연동 및 원격 등록</h4>
<p>로컬 저장소
원격 저장소에 연결하려면 먼저 로컬 저장소가 있어야 함
로컬 저장소를 원격 저장소에 연결하는 방법은 크게 두가지</p>
<ol>
<li>새로운 로컬 저장소를 생성하고 원격 저장소를 연결하기</li>
<li>기존 저장소를 연결하기</li>
</ol>
<p><strong>1 방법</strong>
로컬 저장소</p>
<pre><code>$ mkdir gitstudy05 # 새 폴더 만들기
$ cd gitstudy05 # 새 폴더로 이동
$ git init # 저장소를 깃으로 초기화

$ echo &quot;# gitstudy05&quot; &gt;&gt; README.md # 파일 생성
$ git add README.md # 스테이지에 등록
$ git commit -m &quot;first commit&quot; # 첫 번째 커밋</code></pre><p>프로토콜
서버와 통신하려면 프로토콜을 사용해야 함</p>
<p>Local(로컬)
로컬 컴퓨터에 원격 저장소를 생성하는 것
자신의 컴퓨터를 NFS(Network File System)등 서버로 이용할 때 편리
로컬 저장소를 서버로 이용할 때는 폴더 경로만 입력하면 됨</p>
<pre><code>$ git remote add 원격저장소별칭 폴더 경로</code></pre><p>로컬은 간단하게 원격 서버를 구축할 수 있을 뿐만 아니라 빠른 동작이 가능함
모든 자료가 자신의 컴퓨터에 집중되는 위험도 있다.</p>
<p>HTTP
깃은 HTTP 방식의 프로토콜 지원함
서버에 접속하려면 로그인 절차를 거쳐야 함
기존 아이디, 비밀번호로 접속자를 인증하여 처리할 수 있음
익명으로도 처리할 수 있음</p>
<p>SSH
깃에서 권장하는 프로토콜, 높은 수준의 보안 통신으로 처리하기 때문에 깃 서버를 좀 더 안전하게 운영할 수 있음
SSH 접속할 때는 인증서를 만들어 사용함
인증서를 만들어서 접속하면 별도의 회원 로그인 절차 X
인증서는 공개키 / 개인키로 구분.
공개키는 서버에 등록, 개인키는 로컬에 저장
익명으로 접속 X</p>
<p>Git
깃의 데몬 서비스를 위한 전용 프로토콜 방식을 의미
SSH와 유사하지만 인증 시스템이 없어 보안에 취약할 수 있음
잘 사용 X</p>
<p>원격 저장소의 리모트 목록 관리
깃은 원격 저장소(서버)를 관리하는 데 remote 명령어를 사용함
remote 명령어 사용 - 현재 연결된 원격 저장소 목록을 확인할 수 있음
동시에 등록, 취소 등 작업할 수 있음</p>
<p>원격 저장소 목록만 확인할 때 명령어</p>
<pre><code>$ git remote</code></pre><p>원격 저장소의 별칭 이름과 URL 확인할 때 명령어 </p>
<pre><code>$ git remote -v # 원격 저장소 목록 확인</code></pre><p>깃은 복수의 원격 저장소를 연결하여 사용 가능
리모트 저장소가 여러 개 있을 때는 목록을 모두 출력
저장소의 권한 정보까지는 알 수 없음</p>
<p>주소와 별칭
로컬 저장소에 원격 저장소(서버)를 등록하려면 서버 주소가 필요함
깃허브 같은 저장소를 이용해 보면 프로토콜 + 도메인 주소 형태로 된 것을 알 수 있음
로컬에 서버 저장소를 생성할 때는 폴더 경로를 사용할 수 있음
별칭 : 예를 들면 origin</p>
<p>원격 저장소와 연결하는 명령어</p>
<pre><code>$ git remote add 원격저장소별칭 원격저장소URL</code></pre><p>원격 저장소가 연결되면 fetch와 push 두 주소를 출력
push : 서버로 전송하는 동작
fetch : 서버에서 가지고 오는 동작
별칭 중복 선택 X</p>
<p>별칭 이름 변경 명령어</p>
<pre><code>$ git remote rename 변경전 변경후</code></pre><p>원격 저장소의 좀 더 상세한 정보 확인 명령어</p>
<pre><code>$ git remote show 원격저장소별칭</code></pre><p>원격 서버 삭제
로컬 저장소는 복수의 원격 저장소와 연결 가능</p>
<p>등록된 원격 저장소 삭제 명령어</p>
<pre><code>$ git remote rm 원격저장소별칭</code></pre><h4 id="서버-전송">서버 전송</h4>
<p>push : 서버에 전송
원격저장소로 커밋된 파일들을 업로드하는 동작</p>
<p>서버에 전송하는 명령어</p>
<pre><code>$ git push 원격저장소별칭 브랜치이름</code></pre><p>별칭 이름을 가지는 서버의 master 브랜치에 현재 브랜치를 업로드함
처음 push하면 서버에 새로운 master 브랜치를 생성함
로컬의 master 브랜치 안에 있는 소스 코드를 서버의 master 브랜치로 업로드
자신의 로컬 저장소를 백업하는 용도로 원격 저장소를 사용할 수 있음</p>
<h4 id="자동으로-내려받기">자동으로 내려받기</h4>
<p>clone(복제)
복제는 기존 저장소를 이용하여 새로운 저장소를 생성하는 방법 중 하나
clone 명령어 사용해서 복제
clone 명령어는 초기화 init 명령어 외에 원격 서버 접속에 필요한 추가 설정을 자동으로 수행함
서버의 연결 설정을 마친 후 서버 안에 있는 모든 커밋된 코드 이력들을 한 번에 내려받음</p>
<p>복제 후 원격 저장소의 갱신된 내용을 추가로 내려받으려면 pull 명령어 사용
pull 명령어는 로컬 저장소보다 최신인 갱신된 원격 저장소의 커밋 정보를 현재 로컬 저장소로 내려받음
명령어</p>
<pre><code>$ git pull</code></pre><h4 id="수동으로-내려받기">수동으로 내려받기</h4>
<p>원격 저장소의 내용을 내려받는 방법은 크게 pull, fetch
차이 : 병합의 자동 처리 여부</p>
<p>자동 병합
pull : 원격 서버에서 현재 커밋보다 더 최신 커밋 정보가 있을 때
내려받은 커밋 정보는 임시 영역에 저장
스테이지 영역이 아닌 원격 저장소를 위한 전용 임시 브랜치가 따로 있음
내려받은 최신 커밋들을 현재 브랜치로 자동으로 병합 처리함
병합은 원격 서버 파일과 로컬 파일을 하나로 합치는 과정임
여러 개발자와 협업하는 과정에서 pull 명령어가 자동으로 브랜치 병합을 하지 못하고 충돌이 발생하기도 함 &gt;&gt; fetch 방식 사용</p>
<p>fetch: 가져오기
원격 저장소에서 코드를 수동으로 내려받는 작업
원격 저장소에서 커밋된 코드를 임시 브랜치로 내려받음
내려받은 후 현재 브랜치와 자동 병합 X</p>
<pre><code>$ git fetch 원격저장소URL</code></pre><p>pull 명령어와 달리 fetch 명령어를 실행한 후에는 커밋이 추가된 것을 확인할 수 없음
fetch는 원격 저장소의 커밋들만 가지고 왔을 뿐 로컬 저장소에서 어떤 작업도 하지 않음</p>
<p>내려받은 커밋을 로컬 저장소에 적용하려면 병합 명령을 실행해야함.
merge 명령어 사용</p>
<pre><code>$ git merge 원격저장소별칭/브랜치이름</code></pre><h4 id="순서">순서</h4>
<p>원격 저장소에는 다수의 개발자가 동시에 커밋을 push할 수 없음
여러 명이 협력해서 개발할 때는 순차적으로 push해야 함</p>
<p>원격 저장소에 push하려면 자신의 로컬 저장소를 최신 상태로 유지해야함
커밋이 순차적이지 않을 때 깃은 push 동작을 거부
깃 거부 &gt;&gt; pull or fetch &amp; merge(자신의 로컬 저장소 갱신) &gt;&gt; push</p>
<p>충돌방지
깃이 최신 상태에서만 push를 허용하는 것은 충돌을 방지하기 위함
원격 저장소의 커밋을 내려받는 pull 작업은 내려받은 커밋들을 현재 브랜치로 자동 병합
이때 커밋 내용이 순차적이지 않으면 병합 과정에서 충돌이 발생함</p>
<p>순서 :
pull &gt;&gt; coding &gt;&gt; commit &gt;&gt; pull &gt;&gt; push</p>
<p>pull, push 를 자주 하여 충돌을 최소한으로 줄여 나가면서 작업할 것</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[오픈소스SW - chapter 4]]></title>
            <link>https://velog.io/@rohdoo_mani/%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4SW-chapter-4</link>
            <guid>https://velog.io/@rohdoo_mani/%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4SW-chapter-4</guid>
            <pubDate>Sun, 08 Jun 2025 08:06:27 GMT</pubDate>
            <description><![CDATA[<h4 id="코드의-변화">코드의 변화</h4>
<p>깃은 개발 중인 코드의 이력을 만들 수 있음
커밋(commit) : 코드 변화를 기록하는 것, 의미 있는 단위의 변경 작업들을 저장소에 기록하는 동작</p>
<p>고식적(conventional) 파일 관리 방법
의미 있는 변경을 할 때 파일을 복사함.
파일 복사 형태는 파일의 변경 내역을 기록하는 것보다 더 많은 파일을 생성하고 관리해야 하는 부작용이 있음
모든 내용이 중복되기 때문에 용량도 많이 차지함</p>
<p>Git(VCS)을 이용한 파일 관리 방법
깃의 commit은 새로 변경된 부분만 추출하여 저장
파일 이름을 변경하지 않고도 동일한 파일 이름으로 하나로 관리가 가능함
commit : 시간에 따라 <strong>변화되는 내용만 관리</strong>하고, 코드가 변화된 <strong>시간 순서에 따라서 영구적으로 저장</strong>한다.</p>
<h4 id="새-파일-생성-및-감지">새 파일 생성 및 감지</h4>
<pre><code>$ mkdir gitstudy04 # 새 폴더 gitstudy04 만들기
$ cd gitstudy04 # 만든 폴더로 이동
$ gir init # 저장소를 깃으로 초기화

$ code index.html # VS Code를 사용하여 파일 index.html 생성</code></pre><p><img src="https://velog.velcdn.com/images/rohdoo_mani/post/08c87f94-4601-4cd9-b0d6-2a9d17c068c3/image.png" alt=""></p>
<p>워킹 디렉터리에 새 파일이 생성됨
워킹 디렉터리에 새 파일이 추가되면 깃은 <strong>변화된 상태를 자동으로 감지</strong>함
이때 깃 상태를 확인할 수 있는 명령어 </p>
<pre><code>$ git status # 상태 확인</code></pre><h4 id="깃에-새-파일-등록">깃에 새 파일 등록</h4>
<p>워킹 디렉터리에 있는 파일은 깃이 자동으로 추적 관리하지 않음
커밋을 하려면 파일의 상태가 추적 가능해야 함
등록 : 워킹 디렉터리에 새로 추가된 untracked 상태의 파일을 tracked 상태로 변경하는 것
파일 등록 &gt;&gt; 워킹 디렉터리의 파일이 스테이지 영역에 추가됨
깃은 스테이지 영역의 관리 목록에 추가된 파일만 이력을 추적할 수 있음</p>
<p>스테이지에 등록 명령어</p>
<pre><code>$ git add index.html # 스테이지에 파일을 등록
$ git add . # 전체 파일과 폴더를 모두 등록</code></pre><p><img src="https://velog.velcdn.com/images/rohdoo_mani/post/ba5e3d60-86d7-4cac-8d9a-5247194a5343/image.png" alt=""></p>
<p>필요한 파일만 스테이지 영역에 등록하여 이력을 추적
<strong>스테이지 영역에 등록하지 않은 파일은 커밋 작업에 포함되지 않음</strong>
단 빈 폴더는 스테이지 영역에 등록할 수 X</p>
<p>파일 등록 취소
tracked &gt;&gt; untracked 상태 변경
파일 등록 취소 명령어</p>
<pre><code>$ git rm --cached index.html # 스테이지 삭제 rm 대신 reset도 가능</code></pre><p><img src="https://velog.velcdn.com/images/rohdoo_mani/post/ab9e473a-b081-48ef-a731-a1a60e74324e/image.png" alt=""></p>
<p>등록된 파일의 이름이 변경 명령어</p>
<pre><code>$ git mv index.html home.html # 파일 이름 변경</code></pre><h4 id="첫-번째-커밋">첫 번째 커밋</h4>
<p><img src="https://velog.velcdn.com/images/rohdoo_mani/post/682f93a8-bb04-4bbe-a6c8-5de559bb0a1e/image.png" alt="">
HEAD는 커밋을 가리키는 묵시적 참조 포인터
HEAD는 최종적인 커밋 작업의 위치를 가리킴
HEAD는 커밋될 때마다 한 단계씩 이동함</p>
<p>스냅샷
커밋은 파일 변화를 깃 저장소에 영구적으로 기록함
깃이 다른 버전 관리 도구와 다른점 : 스냅샷 방식(변화된 부분만 찾아 저장)
깃의 스냅샷은 HEAD가 가리키는 커밋을 기반으로 사진을 찍음
깃은 스냅샷 방식을 이용해 빠르게 버전의 차이점 처리, 용량을 적게 사용</p>
<p>파일 상태와 커밋
새롭게 생성된 파일을 커밋하려면 <strong>반드시 tracked 상태로 변경</strong>해야함
tracked 상태로 파일이 변경됨과 동시에 스테이지 영역에 등록함
tracked 상태인 파일을 수정하면 다시 modified 상태로 변경됨
modified는 untracked 상태
untracked 상태의 파일은 반드시 등록 명령(add)으로 다시 스테이지 상태로 재등록해야 함(재등록하면 tracked 상태로 변경됨)</p>
<p>커밋하기 전에는 status 명령어로 항상 상태를 확인하는 습관이 필요함
워킹 디렉터리가 깨끗하게 정리 X &gt;&gt; 커밋 명령어 수행 X</p>
<p><strong>커밋 명령어</strong></p>
<pre><code>$git commit</code></pre><p>깃의 커밋은 HEAD와 스테이지 영역 간 차이를 비교하여 새로운 객체를 생성함
생성된 객체를 깃 저장소에 기록</p>
<p>커밋은 변경된 파일 차이를 깃 저장소에 기록함
커밋을 할 때 생성된 객체를 기록하는 것과 동시에 이를 구별할 수 있는 <strong>메시지</strong>를 같이 작성해야 함 (꼬리표 개념)
커밋은 파일 이름을 여러 개 사용하지 않고 하나만 가짐
모든 커밋은 반드시 커밋 메시지를 작성해야 함</p>
<p><strong>파일 등록과 커밋을 동시에 실행하는 명령어</strong></p>
<pre><code>$ git commit -a</code></pre><h4 id="커밋-확인">커밋 확인</h4>
<ol>
<li>터미널에서 status 명령어를 실행해서 상태를 확인
(커밋을 하면 스테이지 영역은 초기화됨)</li>
<li>로그 기록 확인 명령어<pre><code>$ git log</code></pre>log 명령어는 시간 순으로 커밋 기록을 출력하는데, 최신 커밋 기록부터 내림차순으로 나열함</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[오픈소스SW - chapter 3]]></title>
            <link>https://velog.io/@rohdoo_mani/%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4SW-chapter-3</link>
            <guid>https://velog.io/@rohdoo_mani/%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4SW-chapter-3</guid>
            <pubDate>Sun, 08 Jun 2025 04:17:47 GMT</pubDate>
            <description><![CDATA[<h4 id="git-초기화-명령어-모음">git 초기화 명령어 모음</h4>
<pre><code>$ mkdir 폴더이름 # 새 폴더 만들기
$ cd 폴더이름 # 만든 폴더로 이동 (change directory)

$ git init 경로명 # 저장소 초기화

$ ls # 파일 목록 출력
$ ls -a # 폴더 안의 숨겨진 파일까지 출력</code></pre><h4 id="워킹-디렉터리">워킹 디렉터리</h4>
<p>깃은 VCS의 특성 때문에 저장 공간을 논리적으로 분리함
작업하는 공간(working), 임시로 저장하는 공간(stage), 실제로 저장, 기록하는 공간(repository)</p>
<p>깃이 다른 VCS보다 뛰어난 점 : 지정된 파일들의 모든 것을 추적하는 관리 시스템 (추적됨, 추적되지 않음 상태로 구분함)</p>
<p>tracked 상태
워킹 디렉터리에 새 파일을 추가하면 untracked 상태</p>
<pre><code>$ git add</code></pre><p>git add 명령어를 통해 tracked 상태로 변경해야함.
즉, 워킹 디렉터리 &gt;&gt; 스테이지 영역</p>
<p>깃은 요청받은 파일들만 추적 관리한다.
추적하는 파일 : tracked 상태로 표시
tracked / untracked 나누는 이유 : 시스템 부하를 줄이고, 더 효율적으로 파일 이력을 관리하기 위함.</p>
<h4 id="스테이지">스테이지</h4>
<p>임시로 저장하는 공간
워킹 디렉터리에서 제출된 tracked 파일들을 관리
스테이지는 커밋하려는 파일의 추적 정보만 기록
스테이지를 운영하는 이유 : 커밋을 빠르게 처리하기 위함
repository(저장소)는 스테이지 영역에서 가리키는 파일 내용을 기반으로 변경된 차이점만 기록함</p>
<p>파일들의 스테이지 상태 확인 명령어</p>
<pre><code>$ git status
$ git ls-files --stage</code></pre><p>스테이지 영역에 등록된 파일들은 또 다시 stage / unstage 상태로 구분
깃이 변화 이력을 기록하려면 파일들의 최종 상태가 stage 상태여야 함
unstage 상태 : 파일에 변화가 있음을 의미(스테이지 영역과 워킹 디렉터리 안의 파일 내용에 차이가 있을 때)</p>
<p>코드 변경 = 워킹 디렉터리에서 파일을 수정
파일 수정 &gt;&gt; 워킹 디렉터리와 스테이지 간 내용이 일치 X
스테이지는 수정한 파일과 원본 파일을 구분하기 위해 modified / unmodified 상태로 나눔</p>
<p>modified 상태
스테이지에 등록된 파일은 깃이 추적 관리함
tracked 상태인 파일이 수정되면 스테이지는 파일 상태를 modified 상태로 변경
<img src="https://velog.velcdn.com/images/rohdoo_mani/post/f607c181-0a7d-4602-80e6-5138683bd2e3/image.png" alt=""></p>
<p>수정된 파일은 스테이지에서 잠시 제외됨
깃은 수정 여부만 체크해 주기 때문에 modified 상태로 변경된 파일은 스테이지로 재등록해야 함
수정된 파일을 스테이지 영역으로 다시 적용하려면 git add 명령어 사용</p>
<p>unmodified 상태
tracked 상태이면서 스테이지에서 한 번도 수정하지 않은 원본 상태
수정 X 파일 : 재등록할 필요 X
스테이지에 등록후 수정을 안했다면 unmodified 상태
<img src="https://velog.velcdn.com/images/rohdoo_mani/post/eebec9b7-5336-49b3-92af-d8d5ade986b5/image.png" alt=""></p>
<h4 id="파일의-상태-확인">파일의 상태 확인</h4>
<p>상태 개념 : 깃의 분리된 저장 영역인 워킹 디렉터리와 스테이지, 추적 여부를 의미
깃이 다양한 저장 영역을 구분해서 가지고 있는 이유 : 파일들의 상태를 효율적으로 모니터링하기 위함.</p>
<p>$ git status : 상태 확인 명령어</p>
<h4 id="깃-저장소-복제">깃 저장소 복제</h4>
<p>외부에 있는 기존 프로젝트를 기반으로 저장소를 생성하고 싶다면 외부 저장소를 복제해서 생성할 수 있음.
외부 저장소를 이용해서 로컬 저장소를 생성하는 것 = &#39;깃 저장소 복제&#39;</p>
<p>공개 저장소
ex. 깃허브, 비트버킷
깃 호스팅 서비스는 공개된 저장소와 비공개된 저장소를 모두 지원함
공개된 저장소는 누구나 복제하여 코드를 내려받을 수 있음</p>
<p>다운로드 vs 복제
다운로드 : 깃의 변경 이력까지 가져오지 X
복제 : 깃의 변경 이력까지 가져옴 &gt;&gt; 최종 코드뿐만 아니라 중간에 커밋같은 변화의 모든 이력도 같이 내려받을 수 있다. <strong>일부코드를 변경하여 기여하는 것도 가능</strong></p>
<p>복제 명령어</p>
<pre><code>$ git clone 원격저장소URL 새폴더이름</code></pre><p>복제할 때 폴더 이름을 지정하지 않으면 공개 저장소에서 사용된 폴더와 동일한 이름으로 새 폴더를 만듬(다른 이름으로 복제하고 싶다면 새 폴더이름을 추가 인자로 적자.)</p>
<p>git clone 명령어를 사용하면 깃은 자동으로 깃 서버에 접속
저장소의 모든 소스 코드를 자동으로 내려받음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[PLT - Lecture 10 (1)]]></title>
            <link>https://velog.io/@rohdoo_mani/PLT-Lecture-10-1</link>
            <guid>https://velog.io/@rohdoo_mani/PLT-Lecture-10-1</guid>
            <pubDate>Sat, 07 Jun 2025 16:08:38 GMT</pubDate>
            <description><![CDATA[<p>🧭 1. 함수적 접근 (Functional Approach)
💡 핵심 개념:
변수(이름)는 변하지 않는 값을 나타낸다.</p>
<p>변수 이름은 환경(environment)을 통해 그 값에 연결된다.</p>
<p>한 번 바인딩된 값은 바뀌지 않는다. 상태(state)라는 개념이 없다.</p>
<p>🧱 작동 구조:
racket</p>
<pre><code>(define env (list (bind &#39;x (numV 3)) (bind &#39;y (numV 5))))
(lookup &#39;x env) → (numV 3)</code></pre><p>📌 특징:
매번 새로운 env를 생성해서 새로운 바인딩을 포함시킨다.</p>
<p>이전 바인딩은 남아 있어도 접근되지 않는다 (새 바인딩이 앞에 있음).</p>
<p>순수 함수 스타일: 입력만이 결과를 결정하며, 외부 세계의 변화가 없음.</p>
<p>🔁 2. 순차 실행과 상태 도입
왜 상태가 필요해졌는가?
racket</p>
<pre><code>(begin
  (set! x 5)
  (+ x 1))</code></pre><p>위 코드처럼 이전 표현식의 실행 결과가 다음 표현식에 영향을 주는 구조에서는,
<strong>상태(state)</strong>라는 개념이 반드시 필요해진다.</p>
<p>✨ 변화된 평가 방식:
racket</p>
<pre><code>M(expr, env, store) → (val, store&#39;)</code></pre><p>expr은 표현식</p>
<p>env는 이름 → 위치 매핑</p>
<p>store는 위치 → 값 매핑</p>
<p>결과는 값과 함께 새로운 상태(store&#39;)를 반환</p>
<p>🧠 3. Environment vs Store
구성    역할
Environment    symbol → location (변수 이름을 메모리 주소로)
Store    location → value (주소를 실제 값으로)</p>
<p>값을 얻기 위해서는:
symbol → location (via lookup) → location → value (via fetch)</p>
<p>📦 4. Store와 Cell 구조
store는 리스트로 구현되며, 각 원소는 (cell loc val) 형식:
racket</p>
<pre><code>(list (cell 0 (numV 3)) (cell 1 (numV 5)))
</code></pre><p>cell: 위치(location)와 값(value)의 쌍</p>
<p>numV: 숫자 값을 감싼 타입</p>
<p>이렇게 나누는 이유: 값 자체보다 위치를 통한 접근/업데이트를 구현하기 위해서</p>
<p>🔄 5. override-store – store 갱신
racket</p>
<pre><code>(override-store (cell 1 (numV 10)) store)
</code></pre><p>작동 방식:
override-store는 store 리스트 앞에 새로운 cell을 추가한다.</p>
<p>이는 기존 cell을 삭제하지 않고,
새로운 바인딩을 우선적으로 사용하도록 만드는 방식이다.</p>
<p>예시:
racket</p>
<pre><code>(override-store (cell 1 (numV 10))
                (list (cell 0 (numV 3)) (cell 1 (numV 5))))
→ (list (cell 1 (numV 10)) (cell 0 (numV 3)) (cell 1 (numV 5)))</code></pre><p>location 1에 대해 나중에 fetch를 하면 항상 맨 앞의 10이 나옴</p>
<p>기존 (cell 1 (numV 5))는 남아있지만 무시됨</p>
<p>🔍 6. fetch – 값 검색 함수
정의 요약:
racket</p>
<pre><code>(fetch loc store)
</code></pre><p>→ store를 앞에서부터 검사해 loc이 일치하는 첫 cell의 값을 반환
작동 방식:
racket</p>
<pre><code>define (fetch [loc : Location] [store : Store]) : Value
  (cond
    [(empty? store) (error &quot;not found&quot;)]
    [(equal? loc (cell-location (first store)))
     (cell-val (first store))]
    [else (fetch loc (rest store))]
    )]))</code></pre><p>✅ 입력: loc과 store</p>
<p>✅ 출력: value</p>
<p>✅ 동작: 처음 일치하는 셀에서 값 리턴 (덮어쓰기처럼 동작)</p>
<p>🧩 7. 전체 흐름 예시로 요약
racket</p>
<pre><code>(set! x 5)
(set! x (+ x 1))
x</code></pre><p>set! x 5</p>
<p>lookup x env → loc = 0</p>
<p>store에 (cell 0 (numV 5)) 추가됨</p>
<p>(+ x 1)</p>
<p>lookup x env → loc = 0</p>
<p>fetch 0 store → 5</p>
<p>결과: 6</p>
<p>override-store로 cell 0 (numV 6) 추가됨</p>
<p>x</p>
<p>fetch 0 store → 가장 앞의 cell 0 (numV 6) 선택</p>
<p>✅ 최종 요약
개념    요점
Functional Approach    바인딩은 불변, 상태 없음
Sequential State    상태 변화가 다음 계산에 영향
Environment    name → location
Store    location → value (cell 단위)
override-store    같은 위치에 새 값을 &quot;앞에 붙여&quot; 우선순위 부여
fetch    처음 일치하는 위치의 값을 반환</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[오픈소스SW -  chapter 1]]></title>
            <link>https://velog.io/@rohdoo_mani/%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-chap.1-%EA%B9%83%EA%B3%BC-%EB%B2%84%EC%A0%84-%EA%B4%80%EB%A6%AC</link>
            <guid>https://velog.io/@rohdoo_mani/%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-chap.1-%EA%B9%83%EA%B3%BC-%EB%B2%84%EC%A0%84-%EA%B4%80%EB%A6%AC</guid>
            <pubDate>Thu, 05 Jun 2025 12:17:22 GMT</pubDate>
            <description><![CDATA[<h3 id="11-버전-관리">1.1 버전 관리</h3>
<h4 id="버전-관리">버전 관리</h4>
<p>프로그래밍 개발 과정은 수많은 코드를 변경하고 테스트하는 것
지속적으로 변경되는 과정에서 코드는 잠시 불안정한 수정 상태와 안정된 상태를 반복한다.
개발자는 안정된 상태의 코드와 불안정한 상태의 코드를 인지하고, 항상 안정된 상태를 유지하도록 노력해야 한다.</p>
<h4 id="버전">버전</h4>
<p>버전 : 이전과 약간씩 다른 변화들을 구분하는 표시</p>
<p>서브버전
: 버전과 버전 사이에 변화된 것
: 개발 도중 임시로 작업한 것은 버전이 아님</p>
<p>버전관리의 필요성
: 개발하는 동안 코드는 일시적으로 불안정한 상태가 되고, 이후 정상적인 테스트와 동작을 확인하고 나면 다시 안정된 상태가 된다.
개발 또는 테스트하는 과정에서 불안정한 코드가 있다면 계속 이어서 작업하기 불안할 것이다.
이때 이전 상태로   돌아가 다시 시작할 수 있는 코드의 복귀(포인트) 지점이 필요하다.
복귀 지점을 안정된 코드 상태를 기준으로 설정함으로써 좀 더 자유롭고 안정적으로 개발할 수 있다.</p>
<h3 id="12-버전-관리-시스템">1.2 버전 관리 시스템</h3>
<h4 id="버전-관리-소프트웨어">버전 관리 소프트웨어</h4>
<p>버전 관리 시스템 (VCS, Version Control System)
: 코드와 콘텐츠의 변화를 관리하고 추적하는 소프트웨어 (ex. SCCS:최초의 버전 관리 시스템)
저장소(repository)
: VCS에서 버전 파일들을 관리하고 저장하는 공간</p>
<p>집중형 시스템
: 모든 소스 코드가 한곳에 집중되어 있는 형태
: 하나의 메인 중앙 서버에서 개발 구성원의 모든 소스 코드를 통합적으로 관리
: 클라이언트 - 서버 모델</p>
<p>장점 : 저장소 하나를 중심으로 관리 &gt;&gt; 시스템 운영에 수월
단점
: 중앙 저장 공간인 서버에 문제 발생 시, 소스 코드가 있는 메인 저장소에 모든 개발자가 접근할 수 없는 심각한 상황이 일어남
: 동시에 여러 개발자가 접근하면 충돌이 발생하기에 코드 수정을 안정적으로 할 수 있게 잠금 모델을 적용함
: 파일을 변경하려면 개발자들은 순서대로 대기하고 있어야 함</p>
<p>EX : SCCS(1970s 최초의 버전 관리), RCS(1980s 정/역 방향 개념 도입), CVS, 서브버전</p>
<p>분산형 시스템
: 저장소가 여러개
: 여러 저장소에 각 버전별 소스를 개별 보관
: 분산 저장소 - P2P(Peerto-Peer) 방식으로 공유하며, 각 개발자에게 공유 가능한 저장소 사본 제공
: 서버 - 각 저장소 자료를 동기화하고 중개하는 역할만 수행</p>
<p>장점 : 메인 서버에 문제가 생겨도 지속적으로 개발할 수 있음
단점 : 익숙해지는 데 시간이 걸림</p>
<p>EX : Git (현재 가장 많이 사용함. 오픈소스라서 무료사용 가능), 머큐리얼(파이썬 언어, 무료), 비트키퍼(1998, 유료)</p>
<h3 id="13-깃">1.3 깃</h3>
<h4 id="깃">깃</h4>
<p>2005년 리눅스 개발자가 개발함
대표적인 분산형 버전 관리 시스템
: 원격 저장소(remote repository)와 별개로 개발자 각각의 로컬 컴퓨터에 완벽한 복제본 소스 코드를 저장할 수 있음 - 매번 중앙 저장소를 조회하지 않아도 개발 진행 가능
: 네트워크나 인터넷이 연결되어 있지 않은 상태에서도 로컬 컴퓨터의 소스 코드만으로 버전을 관리할 수 있음 - 작업 후 인터넷 연결시 동기화만 하면 됨
: 원격 저장소로 많은 개발자의 저장소와 연결하거나 동기화 작업을 할 수 있음 - 직접 만든 새로운 소스 코드를 배포하거나 내려받은 소스 코드를 수정한 후 다시 병합(merge) 가능</p>
<h4 id="백업-기능">백업 기능</h4>
<p>분산형 깃 - 자신의 로컬 컴퓨터에서 독립적으로 소스의 버전 관리 가능
독립적 : 자체적으로 버전 기록, 관리 가능한 시스템
주의할 점 : 컴퓨터에 문제 - 개발한 모든 소스 잃을 수 있음 (외부 저장 장치에 백업함으로 예방)
외부 저장 장치에 백업하는 코드는 단순히 파일을 복사하는 것으로 기존 소스코드와는 동기화되지 않은 별개의 파일이다.
깃 사용 - 코드를 원격 저장소에 저장 가능
로컬 컴퓨터의 저장소를 동기화하여 원격 저장소에 백업
여러 공간에서 원격 저장소에 저장된 내용을 내려받아 프로젝트 개발 이어서 가능</p>
<h4 id="협업-개발">협업 개발</h4>
<p>기록과 책임
다수의 개발자와 코드를 공유하고 협업할 때 매우 유용
깃은 변경된 모든 이력을 저장
커밋(commit)을 거쳐 모든 코드의 수정 이력을 기록함
커밋은 신중하게.
커밋으로 저장된 원본 객체는 수정할 수 없음</p>
<p>원격공유
분산된 여러 저장소 간에 정보를 주고받으려면 중앙 서버가 필요함.
깃에서는 원격 저장소가 중앙 서버 역할
자신의 코드 저장소를 원격 서버에 push하여 저장(동기화)
또 다른 개발자의 소스를 원격 서버에서 pull 또는 fetch하여 언제든지 내려받을 수 있음</p>
<p>병합
깃은 하나의 소스코드를 여러 가지 브랜치로 분기하여 독립된 기능을 구현할 수 있음
독립적으로 구현된 소스를 주고받으며, 필요하다면 각 브랜치를 하나로 병합</p>
<p>공개
원격 저장소를 사용하여 개발 중인 코드를 외부로 공개할 수 있음
코드를 공개함으로써 내부 개발자가 만든 기능 한계를 극복하고 외부 개발자와 협업할 수 있음
공개된 프로젝트 - 많은 개발자 간의 협력으로 프로젝트를 빠르게 성장 및 발전시킬 수 있음
외부 개발자는 원격 저장소를 fork하여 소스 코드의 버그를 수정하거나 기능을 개선할 수 있음
수정한 소스 코드를 Pull Request하여 기존 코드에 병합할 수 있음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AVL, Balanced BST]]></title>
            <link>https://velog.io/@rohdoo_mani/AVL-Balanced-BST</link>
            <guid>https://velog.io/@rohdoo_mani/AVL-Balanced-BST</guid>
            <pubDate>Tue, 03 Jun 2025 13:00:41 GMT</pubDate>
            <description><![CDATA[<p>균형이진탐색트리(AVL, Balanced BST)는 모든 노드에 대해서 노드의 왼쪽 sub tree와 오른쪽 sub tree의 높이차가 1 이하인 BST를 뜻한다.</p>
<p><img src="https://velog.velcdn.com/images/rohdoo_mani/post/796ec0da-fdab-4c10-92ae-af532ed7c031/image.jpg" alt=""></p>
<pre><code>class Node:
    def __init__(self, key = None, parent = None, left = None, right = None):
        self.key = key
        self.parent = parent
        self.left = left
        self.right = right
        self.height = 0
    def __str__(self):
        return str(self.key)

class BST:
    def __init__(self):
        self.root = None
        self.size = 0
        self.height = 0
    def __len__(self):
        return self.height
    def __iter__(self):
        return self.root.__iter__()
    def __str__(self):
        return &quot; - &quot;.join(str(k) for k in self)
    def preorder(self, v): #MLR
        if v:
            print(v.key, end=&#39; &#39;)
            self.preorder(v.left)
            self.preorder(v.right)
    def inorder(self, v): #LMR
        if v:
            self.inorder(v.left)
            print(v.key, end= &#39; &#39;)
            self.inorder(v.right)
    def postorder(self, v): #LRM
        if v:
            self.postorder(v.left)
            self.postorder(v.right)
            print(v.key, end=&#39; &#39;)

    def find_loc(self, key): # key가 트리에 있으면 해당 노드 리턴, 없으면 해당 노드가 삽입될 곳의 부모노드 리턴 
        if self.size == 0: # 트리가 비었을때 None 리턴
            return None
        p = None
        v = self.root
        while v:
            if v.key == key:
                return v
            elif v.key &gt; key:
                p = v
                v = v.left
            else:
                p = v
                v = v.right
        return p # 삽입될 곳의 부모노드 p 리턴

    def search(self, key):
        p = self.find_loc(key)
        if p and p.key == key: # p가 있고, key값이 같을 때 p 리턴 
            return p
        else:
            return None # 없으면 None 리턴

    def insert(self, key):
        v = Node(key) # key값을 가진 노드 v 생성
        if self.size == 0: # 빈 트리이면 v는 루트노드
            self.root = v
        else:
            p = self.find_loc(key)
            if p and p.key != key: # p가 존재하지만 key값이 다를 때 v의 위치 정하기
                if p.key &gt; key:
                    p.left = v
                else:
                    p.right = v
                v.parent = p # p,v는 부모자식 관계 설정
        self.size += 1
        return v #삽입된 노드 v 리턴

    def update_node_height(self,v):
        if v:
            l = v.left.height if v.left else -1
            r = v.right.height if v.right else -1
            v.height = max(l, r) + 1

    def update_height(self, v):
        while v != None:
            self.update_node_height(v)
            v = v.parent

    def deleteByMerging(self, x):
        if x == None:
            return None
        a = x.left
        b = x.right
        pt = x.parent
        # c는 삭제될 x 위치에 올 노드
        # s는 균형이 깨질 가능성이 있는 첫 번째 노드
        if a == None: # x의 왼쪽 부트리가 없을 때
            c = b # x의 오른쪽 부트리인 b를 그대로 c위치로 이동
            s = pt # x가 삭제되었기때문에 균형이 깨질 가능성이 큰 노드는 x의 부모노드인 pt
        else: # a가 있을 때 
            c = a # a를 c자리에 올림
            m = a # m을 a에서 가장 큰 노드로 두고 그걸 찾기 위함.
            while m.right:
                m = m.right
            m.right = b # m의 오른쪽 자식으로 b를 연결 
            if b: # b가 있으면 b의 부모를 m으로 연결
                b.parent = m
            s = m # m에 b가 추가되었으므로 m은 균형이 깨질 수 있다.
        if self.root == x: # x가 루트노드이면 c를 루트노드로 올림 
            if c:
                c.parent = None
            self.root = c
        else: # x가 루트노드가 아닐 때 x자리에 c노드 넣기 
            if pt.left == x:
                pt.left = c
            else:
                pt.right = c
            if c: # c가 있으면 c의 부모를 pt로 연결
                c.parent = pt
        self.size -= 1
        return s

    def deleteByCopying(self, x):
        a = x.left
        b = x.right
        pt = x.parent
        if a: #x의 왼쪽 부트리만 있는 경우
            y = a
            while y.right: #y를 a에서 가장 큰 값으로 설정
                y = y.right
            x.key = y.key #값 Copy
            if y.left: #y의 왼쪽 부트리가 있는 경우
                if y == y.parent.right: #y가 y.parent의 오른쪽에 있을 때 
                    y.parent.right = y.left
                else: #y가 y.parent의 왼쪽에 있을 때  *****y가 x.left일때 즉, while문을 한번도 안돌았을 경우우
                    y.parent.left = y.left
                y.left.parent = y.parent
            else: #y가 리프노드일 경우 y와 y의 부모자식 관계를 없앰
                if y == y.parent.right:
                    y.parent.right = None
                else:
                    y.parent.left = None
            self.size -= 1
        elif a == None and b != None: #왼쪽은 없고 오른쪽만 있는 경우 
            y = b
            while y.left: #y를 b에서 가장 작은 값으로 설정
                y = y.left
            x.key = y.key #값 Copy
            if y.right: # x의 오른쪽 부트리에서 가장 작은 값 y에 오른쪽 자식이 있는 경우 
                if y == y.parent.left: #y가 y의 부모의 왼쪽에 있는 경우
                    y.parent.left = y.right
                else:
                    y.parent.right = y.right #y가 y의 부모의 오른쪽에 있는 경우 
                y.right.parent = y.parent
            else: #y가 리프노드일 경우 y와 y의 부모자식 관계를 없앰 
                if y == y.parent.left:
                    y.parent.left = None
                else:
                    y.parent.right = None
            self.size -= 1
        elif a == None and b == None: #x가 리프노드일 경우 
            if pt: #x의 부모노드가 있는 경우 
                if x == pt.right: #x가 x의 부모의 오른쪽 자식일 때 관계 지우기 
                    pt.right = None
                elif x == pt.left: #x가 x의 부모의 왼쪽 자식일 때 관계 지우기 
                    pt.left = None
            else: #x가 리프노드인데 부모노드가 없는 경우 = x는 루트노드
                self.root = None
            self.size -= 1
        return pt

    def rotateRight(self, z):
        if z == None: # 돌릴게 없다. 
            return
        x = z.left # z의 왼쪽 자식 x 
        if x == None: # x가 없으면 돌릴게 없다. 
            return
        b = x.right # x의 오른쪽 자식 b 
        x.parent = z.parent # 회전 시작. x의 부모가 z의 부모노드가 되도록. 
        if z.parent: # z의 부모가 있을때 z의 자리에 x를 넣음.
            if z.parent.left == z:
                z.parent.left = x
            else:
                z.parent.right = x
        x.right = z # x의 오른쪽에 z
        z.parent = x # x,z 부모자식관계 형성
        z.left = b #x의 오른쪽 자식이였던 b가 이젠 z의 왼쪽 자식으로
        if b: # b,z 부모자식관계 형성
            b.parent = z
        if z == self.root: # z가 루트노드였을 경우
            self.root = x
        self.update_node_height(x) # 높이 업데이트 
        self.update_node_height(z)

    def rotateLeft(self, x):
        if x == None:
            return
        z = x.right # z는 x의 오른쪽 자식
        if z == None:
            return
        b = z.left # b는 z의 왼쪽 자식 
        z.parent = x.parent # 회전 시작. z의 부모노드가 x의 부모노드가 되도록. 
        if x.parent: # x의 부모가 있을 때 x의 자리에 z를 넣음. 
            if x.parent.left == x:
                x.parent.left = z
            else:
                x.parent.right = z
        z.left = x # z의 왼쪽에 z
        x.parent = z # x,z 부모자식관계 형성성
        x.right = b # z의 왼쪽 자식이였던 b가 이젠 x의 오른쪽 자식으로
        if b: # b,x 부모자식관계 형성
            b.parent = x
        if x == self.root: # x가 루트노드였을 경우 
            self.root = z
        self.update_node_height(x) # 높이 업데이트 
        self.update_node_height(z)

class AVL(BST):
    def __init__(self):
        super().__init__()

    def rebalance(self, x, y, z): # rebalancing한 후 원래 z 노드 자리로 올라온 노드 리턴
        if z == None: # z가 없으면 아무것도 안함. 
            return
        if y == z.left and x == y.left: #왼쪽으로 z-y-x 일직선
            self.rotateRight(z)
            return y
        elif y == z.left and x == y.right: #왼쪽으로 z-y-x 삼각형
            self.rotateLeft(y)
            self.rotateRight(z)
            return x
        elif y == z.right and x == y.right: #오른쪽으로 z-y-x 일직선
            self.rotateLeft(z)
            return y
        elif y == z.right and x == y.left: #오른쪽으로 z-y-x 삼각형 
            self.rotateRight(y)
            self.rotateLeft(z)
            return x

    def insert(self, key):
        v = super(AVL, self).insert(key) #BST의 insert 호출
        current = v # v를 current라고 저장 (return v 하기 위함.)
        while current: # current가 있을 때 
            left_height = current.left.height if current.left else -1 # current의 오른쪽 왼쪽 부트리 높이 구하기
            right_height = current.right.height if current.right else -1
            if abs(left_height - right_height) &gt; 1: #균형이 깨졌는지 체크
                z = current #균형이 깨진 첫 번째 노드 z로 지정
                if left_height&gt;right_height: # 더 노드가 많은쪽에 z의 자식으로 y 지정 
                    y = z.left 
                else:
                    y = z.right
                if y: # y가 있을 때 
                    y_left_height = y.left.height if y.left else -1 # y의 오른쪽 왼쪽 부트리 높이 구하기 
                    y_right_height = y.right.height if y.right else -1
                    if y_left_height&gt;y_right_height: # 더 노드가 많은쪽에 y의 자식으로 x 지정 
                        x = y.left
                    else:
                        x= y.right
                    if x and y and z: # x, y, z가 다 있으면 균형맞추기 시작
                        self.rebalance(x,y,z)
                break # 균형 다 맞췄으면 중단 
            current = current.parent # 삽입된 노드의 부모로 균형이 깨질수 있는 노드 추적하기 
        return v # 삽입된 노드 리턴 

    def delete(self, u):
        v = super(AVL, self).deleteByCopying(u) #BST의 deleteByCopying 호출 
        while v: # v가 있는 경우 
            v_left_height = v.left.height if v.left else -1
            v_right_height = v.right.height if v.right else -1
            if abs(v_left_height - v_right_height) &gt; 1: # v에서 균형이 께졌는지 확인
                z = v # 균형이 깨진 첫 번째 노드 z로 지정 
                if z.left.height &gt;= z.right.height: # 더 노드가 많은 쪽에 z의 자식으로 y 지정
                    y = z.left
                else:
                    y = z.right
                if y: # y가 있을 때 
                    y_left_height = y.left.height if y.left else -1
                    y_right_height = y.right.height if y.right else -1
                    if y_left_height &gt;= y_right_height: # 더 노드가 많은 쪽에 y의 자식으로 x 지정 
                        x = y.left
                    else:
                        x = y.right
                    if x and y and z: # x, y, z가 다 있으면 균형맞추기 시작작
                        new_top = self.rebalance(x,y,z)
                        v = new_top.parent #균형 다 맞춰진 노드의 부모를 v로 재 지정 
                        continue # while문으로 돌아가서 부모노드를 추적하면서 rebalancing하기 
            w = v # 마지막으로 검사한 노드 기억하기
            v = v.parent # 루트노드까지 균형 재검사
        self.root = w # 회전으로 루트노드가 바뀌었을 때 루트노드 정확히 설정하기</code></pre><h4 id="avl-트리-수행시간-회전-횟수-정리">AVL 트리 수행시간, 회전 횟수 정리</h4>
<p><img src="https://velog.velcdn.com/images/rohdoo_mani/post/4bdcfc62-3144-4058-a229-b08d8964206f/image.jpg" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[BST (Binary Search Tree)]]></title>
            <link>https://velog.io/@rohdoo_mani/BST-Binary-Search-Tree</link>
            <guid>https://velog.io/@rohdoo_mani/BST-Binary-Search-Tree</guid>
            <pubDate>Tue, 03 Jun 2025 12:48:15 GMT</pubDate>
            <description><![CDATA[<p>각 노드의 왼쪽 sub tree의 key값은 노드의 key값보다 작아야하고, 오른쪽 sub tree의 key값은 노드의 key값보다 커야한다.</p>
<pre><code>class Node:
    def __init__(self, key = None, parent = None, left = None, right = None):
        self.key = key
        self.parent = parent
        self.left = left
        self.right = right
        self.height = 0
    def __str__(self):
        return str(self.key)

class BST:
    def __init__(self):
        self.root = None
        self.size = 0
        self.height = 0
    def __len__(self):
        return self.height
    def __iter__(self):
        return self.root.__iter__()
    def __str__(self):
        return &quot; - &quot;.join(str(k) for k in self)
    def preorder(self, v): #MLR
        if v:
            print(v.key, end=&#39; &#39;)
            self.preorder(v.left)
            self.preorder(v.right)
    def inorder(self, v): #LMR
        if v:
            self.inorder(v.left)
            print(v.key, end= &#39; &#39;)
            self.inorder(v.right)
    def postorder(self, v): #LRM
        if v:
            self.postorder(v.left)
            self.postorder(v.right)
            print(v.key, end=&#39; &#39;)

    def find_loc(self, key): # key가 트리에 있으면 해당 노드 리턴, 없으면 해당 노드가 삽입될 곳의 부모노드 리턴 
        if self.size == 0: # 트리가 비었을때 None 리턴
            return None
        p = None
        v = self.root
        while v:
            if v.key == key:
                return v
            elif v.key &gt; key:
                p = v
                v = v.left
            else:
                p = v
                v = v.right
        return p # 삽입될 곳의 부모노드 p 리턴

    def search(self, key):
        p = self.find_loc(key)
        if p and p.key == key: # p가 있고, key값이 같을 때 p 리턴 
            return p
        else:
            return None # 없으면 None 리턴

    def insert(self, key):
        v = Node(key) # key값을 가진 노드 v 생성
        if self.size == 0: # 빈 트리이면 v는 루트노드
            self.root = v
        else:
            p = self.find_loc(key)
            if p and p.key != key: # p가 존재하지만 key값이 다를 때 v의 위치 정하기
                if p.key &gt; key:
                    p.left = v
                else:
                    p.right = v
                v.parent = p # p,v는 부모자식 관계 설정
        self.size += 1
        return v #삽입된 노드 v 리턴

    def update_node_height(self,v):
        if v:
            l = v.left.height if v.left else -1
            r = v.right.height if v.right else -1
            v.height = max(l, r) + 1

    def update_height(self, v):
        while v != None:
            self.update_node_height(v)
            v = v.parent

    def deleteByMerging(self, x):
        if x == None:
            return None
        a = x.left
        b = x.right
        pt = x.parent
        # c는 삭제될 x 위치에 올 노드
        # s는 균형이 깨질 가능성이 있는 첫 번째 노드
        if a == None: # x의 왼쪽 부트리가 없을 때
            c = b # x의 오른쪽 부트리인 b를 그대로 c위치로 이동
            s = pt # x가 삭제되었기때문에 균형이 깨질 가능성이 큰 노드는 x의 부모노드인 pt
        else: # a가 있을 때 
            c = a # a를 c자리에 올림
            m = a # m을 a에서 가장 큰 노드로 두고 그걸 찾기 위함.
            while m.right:
                m = m.right
            m.right = b # m의 오른쪽 자식으로 b를 연결 
            if b: # b가 있으면 b의 부모를 m으로 연결
                b.parent = m
            s = m # m에 b가 추가되었으므로 m은 균형이 깨질 수 있다.
        if self.root == x: # x가 루트노드이면 c를 루트노드로 올림 
            if c:
                c.parent = None
            self.root = c
        else: # x가 루트노드가 아닐 때 x자리에 c노드 넣기 
            if pt.left == x:
                pt.left = c
            else:
                pt.right = c
            if c: # c가 있으면 c의 부모를 pt로 연결
                c.parent = pt
        self.size -= 1

    def deleteByCopying(self, x):
        a = x.left
        b = x.right
        pt = x.parent
        if a: #x의 왼쪽 부트리만 있는 경우
            y = a
            while y.right: #y를 a에서 가장 큰 값으로 설정
                y = y.right
            x.key = y.key #값 Copy
            if y.left: #y의 왼쪽 부트리가 있는 경우
                if y == y.parent.right: #y가 y.parent의 오른쪽에 있을 때 
                    y.parent.right = y.left
                else: #y가 y.parent의 왼쪽에 있을 때  *****y가 x.left일때 즉, while문을 한번도 안돌았을 경우우
                    y.parent.left = y.left
                y.left.parent = y.parent
            else: #y가 리프노드일 경우 y와 y의 부모자식 관계를 없앰
                if y == y.parent.right:
                    y.parent.right = None
                else:
                    y.parent.left = None
            self.size -= 1
        elif a == None and b != None: #왼쪽은 없고 오른쪽만 있는 경우 
            y = b
            while y.left: #y를 b에서 가장 작은 값으로 설정
                y = y.left
            x.key = y.key #값 Copy
            if y.right: # x의 오른쪽 부트리에서 가장 작은 값 y에 오른쪽 자식이 있는 경우 
                if y == y.parent.left: #y가 y의 부모의 왼쪽에 있는 경우
                    y.parent.left = y.right
                else:
                    y.parent.right = y.right #y가 y의 부모의 오른쪽에 있는 경우 
                y.right.parent = y.parent
            else: #y가 리프노드일 경우 y와 y의 부모자식 관계를 없앰 
                if y == y.parent.left:
                    y.parent.left = None
                else:
                    y.parent.right = None
            self.size -= 1
        elif a == None and b == None: #x가 리프노드일 경우 
            if pt: #x의 부모노드가 있는 경우 
                if x == pt.right: #x가 x의 부모의 오른쪽 자식일 때 관계 지우기 
                    pt.right = None
                elif x == pt.left: #x가 x의 부모의 왼쪽 자식일 때 관계 지우기 
                    pt.left = None
            else: #x가 리프노드인데 부모노드가 없는 경우 = x는 루트노드
                self.root = None
            self.size -= 1</code></pre><h4 id="bst-이진탐색트리에서의-수행시간">BST 이진탐색트리에서의 수행시간</h4>
<p>insert : O(h)
search, find_loc : O(h)
deleteByMerging, deleteMyCopying : O(h)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[논리회로 1장(Digital System & Binary Number)]]></title>
            <link>https://velog.io/@rohdoo_mani/%EB%85%BC%EB%A6%AC%ED%9A%8C%EB%A1%9C-1%EC%9E%A5Digital-System-Binary-Number</link>
            <guid>https://velog.io/@rohdoo_mani/%EB%85%BC%EB%A6%AC%ED%9A%8C%EB%A1%9C-1%EC%9E%A5Digital-System-Binary-Number</guid>
            <pubDate>Mon, 12 May 2025 12:13:17 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/rohdoo_mani/post/033e7b02-b7cb-42f7-ae9f-7a5baf102586/image.png" alt="">
<img src="https://velog.velcdn.com/images/rohdoo_mani/post/f957ab5e-fda3-432e-9558-626f6ed37eeb/image.png" alt="">
<img src="https://velog.velcdn.com/images/rohdoo_mani/post/d4be61e4-e88b-4442-96b9-4d607fd35eea/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[이산수학 (Discrete Mathmetics) Chapter 1]]></title>
            <link>https://velog.io/@rohdoo_mani/%EC%9D%B4%EC%82%B0%EC%88%98%ED%95%99-Discrete-Mathmetics-Chapter-1</link>
            <guid>https://velog.io/@rohdoo_mani/%EC%9D%B4%EC%82%B0%EC%88%98%ED%95%99-Discrete-Mathmetics-Chapter-1</guid>
            <pubDate>Mon, 12 May 2025 12:05:03 GMT</pubDate>
            <description><![CDATA[<p>Rosen의 이산수학 8판의 1장을 공부하며 요약한 정리본이다.
<img src="https://velog.velcdn.com/images/rohdoo_mani/post/e6642c98-8962-4686-98a5-9acb556542cc/image.png" alt="">
<img src="https://velog.velcdn.com/images/rohdoo_mani/post/fec4f753-aa38-4e14-b752-7abb55177621/image.png" alt="">
<img src="https://velog.velcdn.com/images/rohdoo_mani/post/1995a18a-002f-43c3-a16b-8a8051fd6b18/image.png" alt="">
<img src="https://velog.velcdn.com/images/rohdoo_mani/post/65b44c1b-5f2c-4dd6-92e8-103800d3e9bd/image.png" alt="">
<img src="https://velog.velcdn.com/images/rohdoo_mani/post/24fd9962-9e24-4fb3-83d2-ac24757d424b/image.png" alt="">
<img src="https://velog.velcdn.com/images/rohdoo_mani/post/c7d756e1-2e9e-407b-aebe-63e08b2f5afe/image.png" alt="">
<img src="https://velog.velcdn.com/images/rohdoo_mani/post/c45a6b38-1cc4-4ea9-a7e1-50c96ced2e42/image.png" alt="">
<img src="https://velog.velcdn.com/images/rohdoo_mani/post/5c2ec510-e500-48f4-8049-e82c4571eb5b/image.png" alt="">
<img src="https://velog.velcdn.com/images/rohdoo_mani/post/09ef0979-b773-4125-8231-c293d23eb220/image.png" alt="">
<img src="https://velog.velcdn.com/images/rohdoo_mani/post/ea62e52c-5726-42cf-af55-4b2494ac8116/image.png" alt="">
<img src="https://velog.velcdn.com/images/rohdoo_mani/post/85f3de65-e111-4434-b2b6-fa18c00c5685/image.png" alt="">
<img src="https://velog.velcdn.com/images/rohdoo_mani/post/e007c64c-5e06-4088-9138-ab8ae6922536/image.png" alt="">
<img src="https://velog.velcdn.com/images/rohdoo_mani/post/cf848bfa-e818-4f54-9352-ce91daa43987/image.png" alt="">
<img src="https://velog.velcdn.com/images/rohdoo_mani/post/c2535bc5-c31f-441c-b30e-a113fded486d/image.png" alt="">
<img src="https://velog.velcdn.com/images/rohdoo_mani/post/c1160c51-7fa3-4dcb-9cb7-5b2765450b02/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[PLT - Lecture 2 (parsing)]]></title>
            <link>https://velog.io/@rohdoo_mani/PLT-Lecture-2-parsing</link>
            <guid>https://velog.io/@rohdoo_mani/PLT-Lecture-2-parsing</guid>
            <pubDate>Sun, 06 Apr 2025 21:29:09 GMT</pubDate>
            <description><![CDATA[<h3 id="parsing">Parsing</h3>
<p>: 프로그래밍 언어의 형식을 분석하고, 데이터를 이해하며, 실행방식으로 변환하는 핵심 과정</p>
<h4 id="abstract-syntax-tree-ast">Abstract syntax tree (AST)</h4>
<p>: 분석을 위한 필요한 구문론적 구조를 제공
: 가장 대중적으로 사용되는 tree 구조
<img src="https://velog.velcdn.com/images/rohdoo_mani/post/0281dcb3-ce13-4f9f-b803-c62f255c5fde/image.png" alt=""></p>
<p>AST as List 선언
(node-name node-left node-right)</p>
<h4 id="exercise">EXERCISE</h4>
<ol>
<li>2+3 --&gt; (+ 2 3)</li>
</ol>
<p>2.
CFG : <add_expr> -&gt; <int>+<int>
code : 2+3
AST as list : (+ (int 2) (int 3))</p>
<p>3.
  code : A = 2+3
s-exp : (= A (+ 2 3))</p>
<p>4.
  begin
  a=2;
  c=a+3;
  end</p>
<p>  AST as list : (= a 2), (= c (+ a 3))
  <img src="https://velog.velcdn.com/images/rohdoo_mani/post/d9b49f8f-5c57-4636-97b1-360caf484389/image.png" alt="">
  AST의 마지막 자식은 항상 terminal이다.</p>
<h4 id="s-expr--prog-prog-assign-a-2-assign-c--a-3">S-expr : (prog (prog (assign a 2)) (assign c (+ a 3)))</h4>
<h3 id="parsing-method">Parsing Method</h3>
<ol>
<li><p>Top-down parsing
: start-symbol 에서 시작하여 점진적으로 입력문자와 일치할 때까지 확장한다.
: 더 복잡한 코드를 설명하기에 용이하다.
: 좌측 재귀 처리에 어려움이 있다.</p>
</li>
<li><p>Bottom-up parsing
: 입력 문자에서 시작해서, 점진적으로 start-symbol까지 줄어든다.
: 구현이 복잡하다.</p>
</li>
</ol>
]]></description>
        </item>
    </channel>
</rss>