<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>youngjin_log</title>
        <link>https://velog.io/</link>
        <description>'개발사(社)' (주)영진</description>
        <lastBuildDate>Tue, 05 May 2026 17:30:24 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>youngjin_log</title>
            <url>https://velog.velcdn.com/images/younjin_02/profile/8aafee2e-0212-45f2-839d-d645438268e8/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. youngjin_log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/younjin_02" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[카카오테크 부트캠프] CS 프리코스 03. 명령어]]></title>
            <link>https://velog.io/@younjin_02/%EC%B9%B4%EC%B9%B4%EC%98%A4%ED%85%8C%ED%81%AC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-CS-%ED%94%84%EB%A6%AC%EC%BD%94%EC%8A%A4-03.-%EB%AA%85%EB%A0%B9%EC%96%B4</link>
            <guid>https://velog.io/@younjin_02/%EC%B9%B4%EC%B9%B4%EC%98%A4%ED%85%8C%ED%81%AC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-CS-%ED%94%84%EB%A6%AC%EC%BD%94%EC%8A%A4-03.-%EB%AA%85%EB%A0%B9%EC%96%B4</guid>
            <pubDate>Tue, 05 May 2026 17:30:24 GMT</pubDate>
            <description><![CDATA[<p>이번 장에서는 개발을 하면서 작성된 <strong>소스 코드들이 어떻게 컴퓨터를 동작시키는 명령어로 변환되는지</strong>에 대해 다룬다.</p>
<h3 id="1-핵심-개념-3개">1. 핵심 개념 3개</h3>
<ul>
<li>작성된 소스 코드들은 인간이 이해하는 고급 수준의 언어이고, 컴퓨터는 내부적으로 이 언어들을 저급 언어(기계어, 어셈블리어)로 변환하여 받아들인다.</li>
<li>고급 수준의 언어는 저급 언어로 변환되는 방식에 따라 컴파일 언어, 인터프리터 언어, 그리고 이 둘의 장점을 합친 하이브리드 언어로 변환된다.</li>
<li>컴퓨터의 명령어는 연산 코드(명령)와 operand(명령에 의해 움직이는 데이터)로 이루어지고, operand field에는 주로 사용될 데이터의 주소가 담긴다.</li>
</ul>
<h3 id="2-내-말로-설명하기">2. 내 말로 설명하기</h3>
<blockquote>
<p>개발할 때 쓰이는 <strong>프로그래밍 언어(ex. JAVA, Python, C)는 고급 수준의 언어이고, 컴퓨터는 이걸 저급 언어로 변환</strong>하여 받아들인다.</p>
</blockquote>
<p>즉, 컴퓨터가 이해하고 실행하는 언어를 저급 언어라고 한다. <strong>저급 언어에는 기계어와 어셈블리어</strong>가 있다.</p>
<ul>
<li><strong>기계어는 0과 1로 이루어진 명령어로 구성된 저급 언어</strong>이다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/1128f4a0-c1f9-467c-9c58-40574c2a5ff9/image.png" alt=""></p>
<ul>
<li><strong>0과 1로 이루어진 기계어를 읽기 편한 형태로 번역한 저급 언어가 어셈블리어이다.</strong></li>
</ul>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/c92ef2fe-ab62-40ce-911f-f6dc42fac021/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/79470da1-1ace-482b-ae64-4cf1de59624b/image.png" alt=""></p>
<p>경우에 따라서 어셈블리어를 직접 소스 코드에 넣어서 코드를 작성하는 경우도 있다.</p>
<blockquote>
<p>고급 언어는 <strong>저급 언어로 변환되는 방식에 따라 컴파일 언어, 인터프리터 언어로 구분</strong>된다. </p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/0ab886e0-821c-4593-9ed5-d23bed234e4e/image.png" alt=""></p>
<ul>
<li><strong>컴파일 언어</strong>: 컴파일 언어는 작성된 <strong>소스코드가 &#39;컴파일러&#39;에 의해 저급 언어로 변환</strong>되고, 이 과정을 &#39;컴파일&#39; 이라고 한다. </li>
</ul>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/452d331d-8c36-4339-8893-16a3a8ad9833/image.png" alt=""></p>
<p><strong>컴파일러는 소스 코드를 한 번에 통째로 번역한다.</strong> 쉽게 말하자면 컴파일 과정은 번역본을 한번에 다 미리 만들어두고 읽는 방식으로 이해하면 된다. 대표적인 컴파일 언어에는 C, C++, Go등이 있다.</p>
<ul>
<li><strong>인터프리터 언어</strong>: 소스 코드가 <strong>인터프리터에 의해 한 줄씩 읽어가며 바로바로 실행</strong>되는 언어이다. 따라서 소스 코드 전체가 저급 언어로 변환될 때까지 기다릴 필요가 없게 된다.</li>
</ul>
<p>매번 읽어들여야 하기에 실행 속도는 상대적으로 느리지만, 즉각적으로 코드 실행 결과를 확인할 수 있어 개발 속도는 상대적으로 빠르다. 대표적인 인터프리터 언어에는 python, Javascript 등이 있다.</p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/6671b040-d0e4-472a-916c-a15e03335228/image.png" alt=""></p>
<p>위 그림처럼 컴파일 언어의 경우, 전체 소스 코드 중 단 하나의 에러만 있어도 컴파일러가 즉시 중지하고 전체 소스 코드의 저급 언어 변환을 시도하지 않는다. 반면에 인터프리터 언어는 한줄한줄 읽어들이기 때문에 에러가 있는 부분 전까지는 코드가 실행된다. </p>
<blockquote>
<p><strong>컴파일 언어와 인터프리터 언어 방식의 장점을 합친 하이브리드 언어</strong> 형태도 존재한다.</p>
</blockquote>
<p>이러한 언어는 소스 코드를 바로 기계어로 변환시키지 않고 <strong>&#39;바이트코드&#39;로 1차 컴파일을 한 후에 가상 머신이 이를 읽어 실행하는 방식</strong>이다. 대표적인 언어에 Java(JVM 위에서 돌아간다), Kotlin이 있다.</p>
<blockquote>
<p>명령어는 &#39;<strong>연산 코드&#39;</strong>와 <strong>&#39;오퍼랜드(operand)&#39;</strong>로 구성된다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/394e9bfc-5015-4107-bfd2-5ed86807b75d/image.png" alt=""></p>
<p><strong>수행할 연산이 연산 코드</strong>이고, <strong>연산에 사용될 데이터 또는 저장된 위치가 operand</strong>이다. </p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/ce91c46c-6b89-408d-b1da-5e4b2c56394f/image.png" alt=""></p>
<p>따라서 위와 같은 어셈블리어도 연산 코드와 operand로 구성되어 있다는 것을 확인할 수 있다. operand에는 연산에 사용될 데이터 보다는 <strong>연산에 사용될 데이터가 저장된 &#39;위치&#39;가 주로 담겨서 다른 말로 &#39;주소 필드&#39;</strong>라고도 한다. 그 이유는 전체 명령어가 담길 수 있는 용량이 제한되어 있기 때문에 <strong>operand에 담길 수 있는 데이터의 크기 또한 제한</strong>되기 때문이다.</p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/b7227b0d-b5e0-46b1-9aaf-dc6135cbc1e6/image.png" alt=""></p>
<p>따라서 위 이미지처럼 실제 저장되는 위치는 따로 두고, opearnd에는 주소만 담기게 되는 것이다. 위 이미지에서 <strong>실제 데이터가 저장된 실제 위치인 &#39;10번지&#39;가 &#39;유효 주소&#39;</strong>가 된다. </p>
<blockquote>
<p>이처럼 <strong>명령어 연산에 사용될 데이터가 저장된 위치(주소)를 찾는 방법을 &#39;명령어 주소 지정 방식&#39;</strong>이라고 한다.</p>
</blockquote>
<p>여러 가지 대표적인 명령어 주소 지정 방식들을 알아보자.</p>
<ul>
<li><strong>즉시 주소 지정 방식</strong>: 연산에 사용될 데이터를 opearnd field에 직접적으로 명시하는 방식이다. 앞서 말했듯이 저장될 데이터의 크기가 작아져야 하지만 속도는 빠르다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/6d40bd38-4331-445f-96b5-cdff07ca6f75/image.png" alt=""></p>
<ul>
<li><strong>직접 주소 지정 방식</strong>: opearnd field에 <strong>유효 주소를 직접적으로 명시</strong>하는 방법이다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/d5bcaf1e-1d50-4c00-a196-25cdf44f9f7d/image.png" alt=""></p>
<ul>
<li><strong>간접 주소 지정 방식</strong>: operand field에 <strong>유효 주소의 주소를 명시</strong>하는 방식이다. 앞선 방식들에 비해 속도는 느려진다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/eca46776-1e88-4a12-a652-25355ec8f2d6/image.png" alt=""></p>
<ul>
<li><strong>레지스터 주소 지정 방식</strong>: 연산에 사용될 <strong>데이터가 저장된 레지스터를 명시</strong>한다. 레지스터는 CPU 내부에 위치하기 때문에 메모리에 접근하는 속도보다 훨씬 빠르게 접근 가능하다. 따라서 속도가 빠르다는 방식이 있다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/3ee64949-4393-41a3-90b2-91d7717e0669/image.png" alt=""></p>
<ul>
<li><strong>레지스터 간접 주소 지정 방식</strong>: 연산에 사용될 데이터를 메모리에 저장해두고, 그 메모리 주소를 저장한 레지스터를 opearnd field에 명시하는 방식이다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/65e6d71f-68f4-44d8-a7a1-d13e0292da3e/image.png" alt=""></p>
<blockquote>
<p>연산 코드의 종류는 크게 <strong>데이터 전송, 산술/논리 연산, 제어 흐름 변경, 입출력 제어</strong>가 있다.</p>
</blockquote>
<p>기본적으로 연산 코드의 종류와 생김새는 CPU마다 다르기 때문에, 외우려고 하지 말고 어떤 유형이 있는지 정도만 파악하고 넘어가자.</p>
<ol>
<li><strong>데이터 전송</strong></li>
</ol>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/01903853-ce83-4897-8506-0bee512d1d63/image.png" alt=""></p>
<ol start="2">
<li><strong>산술/논리 연산</strong></li>
</ol>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/0375c17f-0e91-43bb-ad30-879a780f4efb/image.png" alt=""></p>
<ol start="3">
<li><strong>제어 흐름 변경</strong></li>
</ol>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/73430c17-09aa-4001-b12d-e97141094ccb/image.png" alt=""></p>
<p>CALL과 RETURN의 경우 코드를 짜다가 함수를 &#39;호출&#39;해서 그 함수로 이동해서 함수를 실행하게 되는게 &#39;CALL&#39;이고, 그 함수를 실행하다 return값을 반환하고 다시 원래 실행하던 흐름의 주소로 돌아가서 계속 코드가 실행되는게 &#39;RETURN&#39;이라고 생각하면 될 것 같다.</p>
<ol start="4">
<li><strong>입출력 제어</strong></li>
</ol>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/81987865-4b3a-4ac7-9aaa-9d6a6cc324c8/image.png" alt=""></p>
<h4 id="c언어의-컴파일-과정">C언어의 컴파일 과정</h4>
<blockquote>
<p>C언어는 <strong>preprocessor(전처리기), compiler, assembler, linker</strong> 이 순서를 통해 컴파일이 이루어진다. </p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/c63fbbe7-f49d-4e5d-8b9d-d5ef74fb37d0/image.png" alt=""></p>
<p>이 과정은 &#39;컴파일 과정&#39;을 통해 한 번에 이루어지지만, 내부적으로는 위 순서를 거쳐서 이루어지는 것이다. </p>
<ol>
<li><strong>전처리 과정</strong>: 본격적인 컴파일 작업 전에 처리할 작업들을 일컫는다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/6183737b-0b61-4c5d-9666-a93afd75b9d9/image.png" alt=""></p>
<p>외부 선언된 라이브러리(ex. &lt;stdio.h&gt;)를 불러오는 과정도 전처리에서 일어난다.</p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/e26dd885-f380-4af0-8baa-f1d528699a74/image.png" alt=""></p>
<p>위와 같은 간단한 문자열을 출력하는 C코드의 전처리 과정만 끝난 &#39;.i&#39;의 확장자를 갖는 파일을 확인해보면,</p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/f90129c3-059c-434c-b5c6-054c9982119b/image.png" alt=""></p>
<p>이와 같이 stdio.h 라이브러리의 모든 소스 코드를 다 불러오게 된다. </p>
<ol start="2">
<li><strong>컴파일 과정</strong>: 컴파일러가 컴파일을 진행 즉, <strong>전처리 완료된 소스 코드를 저급 언어(어셈블리어)로 변환</strong>한다. </li>
</ol>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/f9c302eb-02b0-454b-8a40-56c1a76fdcda/image.png" alt=""></p>
<p>위 이미지가 아까 그 간단한 C코드를 컴파일한(.s 확장자) 어셈블리어로 변환된 모습이다.</p>
<ol start="3">
<li><strong>어셈블 과정</strong>: <strong>어셈블리어를 기계어로 변환</strong>한다. 목적 코드를 포함하는 목적 파일이 된다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/1b845fac-d117-402f-9171-365cf3d4c2ca/image.png" alt=""></p>
<p>위의 파일이 .o 확장자의 어셈블 과정이 이루어져 기계어로 변환된 모습이다.</p>
<ol start="4">
<li><strong>링킹 과정</strong>: 목적 파일을 실행 파일로 만드는 과정이다. </li>
</ol>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/7f91f020-8476-497c-a855-6af2bfbf9824/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/77c02807-af56-4e82-afe7-ee7021ba1210/image.png" alt=""></p>
<p>위처럼 서로를 <strong>참조하는 여러 파일들을 묶어주는 작업이 링킹</strong>이고, 이 과정을 거쳐 비로소 실행 파일(.exe)가 된다. 목적 파일, 실행 파일 둘 다 기계어로 작성된 파일이지만, 링킹 과정을 무조건 거쳐야 실행 파일이 된다.</p>
<h3 id="3-헷갈려서-ai에게-확인한-부분">3. 헷갈려서 AI에게 확인한 부분</h3>
<p>헷갈린 부분보다는 강의를 듣다가 어셈블리어를 소스 코드에 직접 쓰는 개발의 경우나, 개발자도 있다고 해서 이 부분을 좀 알아보았다.</p>
<blockquote>
<p>중간에 어셈블리어를 끼워 넣는 것을 <strong>&#39;inline assembly&#39;</strong>라고 부르며, 현대 컴파일러 환경에서 자주 쓰진 않지만 몇 가지 활용되는 경우가 있다.</p>
</blockquote>
<p>시스템/kernel을 만드는 프로그래머나, 실제 기기(자동차, IoT)와 같은 아주 작은 칩을 개발하는 개발자, 게임 개발자와 같이 <strong>특정 목적을 위해 매우 제한적인 환경에서의 개발을 할 때, CPU를 극한으로 쥐어짜거나 하드웨어를 직접 조작해야하는 상황</strong>일 때 주로 인라인 어셈블리를 한다고 한다. </p>
<h3 id="4-개발-또는-일상과-연결해-본-예시">4. 개발 또는 일상과 연결해 본 예시</h3>
<p>작성자 또한 Java를 주로 쓰는 백엔드 개발을 공부하고 있는 사람으로써, 단 한 줄 에러의 위험성을 잘 알고 있다. 그게 &#39;컴파일 과정&#39;때문이라는 것을 오늘 강의를 들으며 다시 한 번 체감할 수 있었다. 실제 서버 코드를 작성할 때 단 한 곳만 삐끗해도 서버 전체가 작동하지 않는 것을 여러 번 경험봤다..</p>
<h3 id="5-추가로-알아보고-싶은-내용">5. 추가로 알아보고 싶은 내용</h3>
<p>작성자는 주로 Java를 활용한 백엔드 개발을 주로해서, Java의 자세한 컴파일 과정에 대해서도 알아보았다.</p>
<blockquote>
<p>자바는 <strong>2단계의 컴파일 과정</strong>을 거친다. </p>
</blockquote>
<ol>
<li>소스 코드 파일(.java) 파일을 <strong>자바 컴파일러인 javac가 소스 코드를 번역</strong>한다.</li>
</ol>
<p>이 과정에서 C언어처럼 진짜 기계어가 아닌, <strong>JVM만 읽을 수 있는 ByteCode라는 중간 형태의 파일로 변환</strong>한다. 이 파일의 확장자가 .class이다.</p>
<p>2.** JVM이 실행되어 .class 파일을 메모리로 불러와 실제 CPU가 알아들을 수 있는 기계어로 실시간 번역을** 하면서 실행한다.</p>
<p>이 과정에서는 한줄씩 실행되는 인터프리터 방식과 덩어리째 번역하는 방식이 혼용된다. </p>
<h2 id="전공자-선택-작성-심화-질문">전공자 선택 작성: 심화 질문</h2>
<ul>
<li><p>이 개념은 왜 필요한가?: 개발자는 소스 코드를 작성함으로써 프로그램을 개발하는 사람이다. 스스로 작성한 코드를 컴퓨터에서 어떻게 받아들이고 번역하여 실행하는지를 알아야 더욱 좋은 코드를 작성할 수 있을 것 같다.</p>
</li>
<li><p>비전공자에게 설명한다면 어떤 순서로 설명할까?: 한 언어를 다른 언어로 &#39;번역&#39;하는 과정에 빗대어 여러가지 &#39;번역&#39;의 방법과, &#39;명령&#39;의 방법에 빗대어 설명하면 더 쉽게 이해시킬 수 있을 것 같다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[카카오테크 부트캠프] CS 프리코스 02. 컴퓨터의 데이터 처리 방법]]></title>
            <link>https://velog.io/@younjin_02/%EC%B9%B4%EC%B9%B4%EC%98%A4%ED%85%8C%ED%81%AC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-CS-%ED%94%84%EB%A6%AC%EC%BD%94%EC%8A%A4-02.-%EB%8D%B0%EC%9D%B4%ED%84%B0</link>
            <guid>https://velog.io/@younjin_02/%EC%B9%B4%EC%B9%B4%EC%98%A4%ED%85%8C%ED%81%AC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-CS-%ED%94%84%EB%A6%AC%EC%BD%94%EC%8A%A4-02.-%EB%8D%B0%EC%9D%B4%ED%84%B0</guid>
            <pubDate>Mon, 04 May 2026 07:23:56 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/younjin_02/post/963c590e-64be-4d82-92a1-49facb866e02/image.png" alt=""></p>
<p>컴퓨터는 기본적으로 0과 1로만 모든 데이터를 처리한다. 이번 장에서는 0과 1로 여러 가지 타입의 데이터들을 처리하는 법에 대해 다룬다.</p>
<h3 id="1-핵심-개념-3개">1. 핵심 개념 3개</h3>
<ul>
<li>컴퓨터는 숫자든 문자든 모든 신호를 <strong>이진수의 형태로 변환</strong>하여 받아들인다.</li>
<li>컴퓨터가 문자를 해석할 수 있게 푸는 과정을 encoding, 컴퓨터가 받아 들인 형태를 다시 인간이 읽을 수 있게 변환하는 과정을 decoding이라고 한다.</li>
<li>현대의 표준적인 IT 환경에서는 사실상 99.9% <strong>유니코드(Unicode)를 기준으로 문자를 인식하고 처리</strong>한다.</li>
</ul>
<h3 id="2-내-말로-개념-풀기">2. 내 말로 개념 풀기</h3>
<h4 id="1-컴퓨터가-숫자를-표현하는-방법">1. 컴퓨터가 숫자를 표현하는 방법</h4>
<blockquote>
<p><strong>bit는 0과 1을 표현하는 가장 작은 정보 단위</strong>다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/2ef87d47-4170-4a5c-9e03-30e4d851a536/image.png" alt=""></p>
<p>위 그림과 같이 n bit로 2^n가지의 정보 표현이 가능하다. bit가 가장 작은 기본 단위이며, bit 여러 개로 다양한 단위가 다음과 같이 있다.</p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/310529d2-72b7-416f-8225-fee0af194ad8/image.png" alt=""></p>
<p><strong>bit, byte, K, M, G, T</strong>, 이 순서대로 외우면 된다. </p>
<blockquote>
<p><strong>CPU가 한 번에 처리할 수 있는 정보의 크기 단위를 &#39;word&#39;</strong>라고 한다.</p>
</blockquote>
<ul>
<li>Half Word: 워드의 절반 크기</li>
<li>Full Word: 워드 크기</li>
<li>Double Word: 워드의 두 배 크기</li>
</ul>
<blockquote>
<p>컴퓨터의 숫자 처리 방법을 이해하기 위해서는 이진법을 이해해야 한다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/c9b2c152-cf31-48d7-bdfd-b449744c3bd1/image.png" alt=""></p>
<p>보통 2진법은 1000(2) 또는 0b1000 이런식으로 이진수라는 것을 나타낸다.</p>
<blockquote>
<p>이진수로 음수를 표현하는 방법에는 <strong>2의 보수</strong>가 활용된다.</p>
</blockquote>
<p>보수법 자체의 개념은 다소 헷갈리지만, 쉽게 그냥 <strong>원래 수의 모든 0과 1을 서로 바꾸고, 마지막에 1만 더해주면 된다.</strong></p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/507ba1d0-cbc9-4408-bc8b-0854d523247f/image.png" alt=""></p>
<p>하지만 여기서 궁금증이 생기게 된다. 위 그림에서 1011(2)의 보수는 0101(2)이 되는데, 이를 십진수로 변환하면 5가 된다. 즉, 1011의 보수와 5를 이진법으로 표현한 이진수가 형태가 아예 똑같게 되서 구별이 안되는데, 이를 <strong>CPU 내부의 flag register(register의 한 종류)가 이 이진수를 읽어 양수인지 음수인지 판별한다.</strong></p>
<blockquote>
<p>이진법으로는 숫자의 길이가 너무 길어져서 <strong>16진법</strong>도 자주 활용한다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/3c8fae8b-1841-4291-b297-316c4d02532e/image.png" alt=""></p>
<p>16진법도 15(16) 또는 0x15 이런식으로 표기한다.</p>
<ul>
<li>이진수를 16진수로 변환하기 위해서는 16진수 하나를 이진수로 변환후에 이어붙이면 된다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/9552f9fc-d350-4030-b0b8-72803ab53b84/image.png" alt=""></p>
<ul>
<li>16진수를 이진수로 변환하는건 반대로 이진수 4개(4bit)를 16진수로 바로 변환 후 합쳐주면 된다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/84143d20-6a74-48e2-bb72-22217c1ef7f1/image.png" alt=""></p>
<h4 id="2-컴퓨터가-문자를-표현하는-방법">2. 컴퓨터가 문자를 표현하는 방법</h4>
<p>컴퓨터의 문자 표기를 이해하기 위해서는 <strong>문자 집합, encoding과 decoding</strong>을 이해해야 한다. </p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/0b4c900e-c165-45fc-b809-e962179267f4/image.png" alt=""></p>
<blockquote>
<p>대표적인 문자 집합 중 하나로 <strong>ASCIII CODE</strong>가 있다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/eae48014-a6b4-4862-b40e-bf92f4e53b8e/image.png" alt=""></p>
<p>알파벳, 아라비아 숫자, 일부 특수 문자 및 제어 문자를 이용해 표현되고, <strong>8bit 크기로 표현되는데, 실제로는 7bit로 &#39;하나의 문자&#39;를 표현하고 나머지 1bit는 오류 검출을 위해 사용되는 패리티 비트다.</strong> 위의 표의 이미지처럼 <strong>A는 65로, a는 97로 인코딩</strong>되어 컴퓨터는 이 두 가지 문자를 각각 65를 이진수로 변환한 수, 97을 이진수로 변환한 수로 표현한다. </p>
<p>아스키 코드는 인코딩 방식이 간단하지만, <strong>7bit로 하나의 문자를 표현하기에 128개보다 많은 문자를 표현할 수 없어</strong> 영어만 표현 가능하고 한글을 포함한 다른 언어 문자나 특수 문자를 표현을 불가능하다. 따라서 각 언어별 인코딩 방식이 존재한다.</p>
<blockquote>
<p>한글은 초성, 중성, 종성의 조합으로 이루어져 있기에, <strong>완성형 인코딩 방식과 조합형 인코딩 방식</strong>이 존재한다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/0cf23dee-0a41-49fd-8779-af785720dd48/image.png" alt=""></p>
<p>완성형은 글자 하나하나에 코드를 부여하는 방식이고, 조합형은 자음과 모음 하나하나에 코드를 부여해 조합하는 방식이다. 여러 인코딩 방식을 알아보자.</p>
<blockquote>
<p><strong>EUC-KR</strong>: 글자 하나 하나에 2byte 크기의 코드를 부여하는 완성형 인코딩 방식이다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/970b561f-2742-4b39-95bb-a71d8ecb257f/image.png" alt=""></p>
<p>&#39;가&#39;는 b0a01로 인코딩되는 식이다. <strong>16bit기에 16진수로 표현</strong>된다. 이 방식으로 2350개 정도의 한글 표현이 가능한데, 이것만으로는 인코딩되지 않는 한글 문자도 있긴 하다. 이런식으로 언어별 인코딩 방식을 쓰게 되면 다중 언어 시스템을 개발할 때 번거로움이 생길 수 있게 된다.</p>
<blockquote>
<p><strong>유니코드 문자 집합</strong>이라는 통일된 문자 집합이 이러한 문제를 해결한다.</p>
</blockquote>
<p>유니코드는 <strong>통일된 문자 집합으로, 한글, 영어, 화살표와 같은 특수 문자, 이모티콘까지 표현 가능해서 현대 문자 표현에 있어 제일 많이 활용되는 방식</strong>이다. 유니코드의 인코딩 방식에 utf-8, utf-16, 등이 있다.</p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/93cf2a8d-711e-4c9b-9a57-bdea90ac1cc6/image.png" alt=""></p>
<p>위의 이미지는 다양한 화살표 기호들의 유니코드를 보여주는데, U+ 뒤에 있는 코드들을 인코딩 하는 방식에 utf-8, 16등의 인코딩 방식이 있는 것이다. </p>
<p>utf는 Unicode Transformation Format의 약자로, 가변 길이 인코딩 방식이다. 인코딩 결과에 따라 1byte~4byte가 될 수 있다.</p>
<h3 id="3-헷갈려서-ai에게-확인한-부분">3. 헷갈려서 AI에게 확인한 부분</h3>
<ul>
<li><p>처음 헷갈렸던 내용: 현대의 모든 컴퓨터 환경에서는 그럼 문자는 무조건 unicode를 써서 문자를 받아들이는지가 궁금했고, 컴퓨터는 왜 무조건 0과 1로만 데이터를 받아들이는지, 단순히 전기 신호이기 때문에 그런것인지가 궁금해서 AI한테 물어보았다.</p>
</li>
<li><p>AI 답변:</p>
</li>
</ul>
<blockquote>
<p>CPU 내부에 <strong>Transistor라는 아주 작은 전기 스위치 수십억 개가 빽빽하게 들어있고, 이게 전기 신호를 on(1), off(0) 두 가지 상태만을 받아들여서</strong> 컴퓨터는 이진수만 받아들인다. </p>
</blockquote>
<p>인류는 전압의 세기를 10가지로 나눠서 십진수를 표현할 수 있지 않을까 해서 시도도 해보았지만, <strong>현실 세계의 아주 미세한 전기 신호들은 주변의 여러 노이즈들에 민감하게 반응하기 때문에, 확실하게 on, off로만 전기 신호를 처리하는게 데이터를 신뢰성있게 주고받기 위한 최선의 선택이라고</strong> 받아들인 것이다. </p>
<blockquote>
<p>현대의 표준적인 IT 환경에서는 <strong>사실상 99.9% 유니코드(Unicode)를 기준으로 문자를 인식하고 처리</strong>한다.</p>
</blockquote>
<p>위에서 언급했다 싶이 초기에는 영어 알파벳과 몇 가지의 숫자 정도면 받아들이면 됐기에 ASCII CODE를 활용했지만, 전 세계로 컴퓨터가 보급되면서 각 언어별 인코딩 방식들이 생겨났다. 이 과정에서 <strong>언어들이 변환되는 과정에서 글자들이 깨지기 때문에, 유니코드가 모든 문자에 부여되고, utf-8 또는 utf-16과 같이 유니코드를 인코딩하는 방식을 사용</strong>한다. </p>
<h3 id="4-개발-또는-일상과-연결해-본-예시">4. 개발 또는 일상과 연결해 본 예시</h3>
<p>개발할 때 VSCode나 IntelliJ 터미널 창에서 원격에 commit, push 할 때 커밋 메시지를 주로 한글로 치는데, 영어보다 한글은 되게 버벅거리면서 입력된다는 느낌을 항상 받아서, 터미널은 유니코드를 활용한 인코딩 방식이 아닐까 해서 찾아보았다.</p>
<blockquote>
<p>터미널도 유니코드를 이용해 인코딩하지만, <strong>한글은 &#39;조합형&#39; 문자라는 특성을 가지고 있는 동시에 터미널이라는 옛날 환경의 특성</strong>이 결합된 결과이다.</p>
</blockquote>
<p>영어는 알파벳의 나열이기 때문에 글자 하나하나가 즉시 컴퓨터로 전송되지만, 한글은 자음, 모음을 조합해야 한다. 추가로 <strong>터미널은 본질적으로 완성된 문자들의 흐름을 순차적으로 받아들이는데에 최적화되어 있어 복잡한 입력을 처리하는 능력 자체가 조금 일반 환경에 비해 부족</strong>하다. 따라서 한글은 <strong>자음과 모음이 결합되는 순간마다 터미널 화면을 지웠다 다시 그리는 렌더링 작업을 반복</strong>해야 하기 때문에 우리 눈에 글자가 씹혀보이는 것처럼 보이는 것이다.</p>
<h2 id="전공자-선택-작성-심화-질문">전공자 선택 작성: 심화 질문</h2>
<p>전공자는 가능하다면 아래 질문 중 하나 이상에 답해 주세요.</p>
<ul>
<li>비전공자에게 설명한다면 어떤 순서로 설명할까?: &#39;컴퓨터&#39;는 하나의 기계이기 때문에, 전기 신호를 주어서 소통해야 한다. 따라서 전기 신호의 on, off를 각각 1과 0으로 표기하고, <strong>1과 0만 쓰는 이진법을 활용해 컴퓨터라는 기계에 신호를 보내고, 받고 하는 과정에서 다양한 해석 방식이 존재한다</strong>는 말을 써서 컴퓨터와의 소통 방식을 설명해보고 싶다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[카카오테크 부트캠프] CS 프리코스 01. 컴퓨터 구조]]></title>
            <link>https://velog.io/@younjin_02/%EC%B9%B4%EC%B9%B4%EC%98%A4%ED%85%8C%ED%81%AC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-CS-%ED%94%84%EB%A6%AC%EC%BD%94%EC%8A%A4-01.-%EC%BB%B4%ED%93%A8%ED%84%B0-%EA%B5%AC%EC%A1%B0</link>
            <guid>https://velog.io/@younjin_02/%EC%B9%B4%EC%B9%B4%EC%98%A4%ED%85%8C%ED%81%AC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-CS-%ED%94%84%EB%A6%AC%EC%BD%94%EC%8A%A4-01.-%EC%BB%B4%ED%93%A8%ED%84%B0-%EA%B5%AC%EC%A1%B0</guid>
            <pubDate>Sat, 02 May 2026 16:43:10 GMT</pubDate>
            <description><![CDATA[<p>운이 좋게도 휴학 후 카카오테크 부트캠프 클라우드 네이티브 교육 과정에 합격하게 되어 6개월간 해당 교육과정을 수강할 수 있는 좋은 기회를 얻게 되었다. 5월 12일부터 본격적인 교육과정이 시작하기 전에 기본적인 CS지식에 관한 프리코스를 수강하면서 TIL을 꾸준히 작성하려고 한다. 전공과목을 수강하면서 들었던 내용들이 있긴 하지만 복습차원으로 교육 전에 들어놓으면 더욱 좋을 것 같다. </p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/31cb8a4d-8b20-4e32-a2f5-b4a78c95f103/image.png" alt=""></p>
<h3 id="1-핵심-개념-3개">1. 핵심 개념 3개</h3>
<ul>
<li>개발을 할 때 중요하게 고려해야 하는 세 가지 요소에는 <strong>&quot;성능&quot;, &quot;비용&quot;, &quot;용량&quot;</strong>이 있다.</li>
<li>컴퓨터의 핵심 네 가지 부품은 <strong>CPU, Memory(RAM), 보조기억장치, 입출력장치</strong>이다.</li>
<li>이 <strong>핵심 부품은 메인 보드에 위치하며, 부품 간 정보 교환은 bus</strong>를 통해 이루어진다.</li>
</ul>
<h3 id="2-내-말로-설명하기">2. 내 말로 설명하기</h3>
<h4 id="2-1-컴퓨터-구조를-알아야-하는-이유">2-1. 컴퓨터 구조를 알아야 하는 이유</h4>
<blockquote>
<p><strong>컴퓨터를 &#39;미지의 대상&#39;에서 &#39;분석의 대상&#39;으로</strong></p>
</blockquote>
<p>좋은 개발자에게는 뛰어난 문제 해결 능력이 필수적이다. 이를 위해서는 단순 프로그래밍 문법을 잘 알거나, 코드를 잘 짜는 능력 외에도 <strong>탄탄한 CS 지식이 받쳐줘야 하며, 컴퓨터를 세세하게 분석해보려는 습관</strong>이 필요하다.</p>
<blockquote>
<p><strong>&#39;성능&#39;, &#39;용량&#39;, &#39;비용&#39;</strong>을 고려한 개발</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/a4d8db7c-5585-44f9-96d4-d402e0a448f0/image.png" alt=""></p>
<p>개발자는 위의 세 가지 요소를 고려해서 프로그램을 개발할 줄 알아야 한다. 서버 컴퓨터를 고를 때도, 실제 서버 컴퓨터 대신 클라우드 서비스를 활용할 때도, 이 세 가지 요소를 고려해 &#39;효율적인&#39; 계산을 해야 한다.</p>
<h4 id="2-2-컴퓨터-구조의-큰-그림">2-2. 컴퓨터 구조의 큰 그림</h4>
<blockquote>
<p>컴퓨터가 이해하는 정보: <strong>데이터, 명령어</strong></p>
</blockquote>
<ul>
<li><p>데이터에는 숫자, 문자, 이미지, 동영상과 같은 정적인 정보를 비롯한 컴퓨터와 주고 받는 내부에 저장된 정보를 통칭하는 말이다. 중요한 점은, 컴퓨터는 어떤 형식이건 <strong>0과 1(이진수)로만 데이터를 받아드린다.</strong></p>
</li>
<li><p><strong>컴퓨터는 결국 명령어를 처리하는 기계다.</strong></p>
</li>
</ul>
<blockquote>
<p>컴퓨터의 네 가지 핵심 부품: <strong>CPU, 메모리(RAM), 보조기억장치, 입출력장치</strong></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/9818fa07-eb06-41bf-b4d4-752558d08a6d/image.png" alt=""></p>
<p><strong>1. CPU</strong></p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/fb04cd27-8dfa-4b04-96a9-ec01b7931ba8/image.png" alt=""></p>
<blockquote>
<p><strong>메모리에 저장된 명령어를 읽고, 해석하여 실행</strong>하는 부품이다. </p>
</blockquote>
<p>CPU는 크게 3가지의 또 작은 구성요소로 이루어진다.</p>
<ul>
<li><strong>ALU</strong>: CPU 안의 연산을 수행한다</li>
<li><strong>Register</strong>: CPU 안의 작은 내부 저장장치</li>
<li><strong>제어장치</strong>: &#39;제어 신호(메모리 읽기 및 쓰기)&#39;를 통해 명령어를 해석하는 장치</li>
</ul>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/e596b0fc-f631-4abb-a5df-c1e2fe5ceade/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/dc69274f-e758-4854-8f57-0d1061f18983/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/5b6086d9-bfa9-4e7b-b594-208bca700c2d/image.png" alt=""></p>
<p>CPU의 가장 대표적인 작업을 간단하게 나열한 그림이다. 제어 신호를 통해 읽어들인 명렁어들은 레지스터에 저장되고, 제어장치는 이 명령어를 해석하며, ALU가 이 연산을 수행한다.</p>
<p><strong>2. RAM(메모리)</strong></p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/a24d98c8-4619-4909-814b-23c4e9246e04/image.png" alt=""></p>
<blockquote>
<p><strong>&#39;현재 실행&#39;되는 프로그램</strong>의 <strong>명령어와 데이터를 저장</strong>하는 부품이다.</p>
</blockquote>
<p>&#39;현재 실행되고 있는&#39;이 포인트다. 다른 말로 하면 process다.</p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/a2b8f0f0-9636-4955-9cf1-e0a42f9df126/image.png" alt=""></p>
<p>또한 메모리에는 <strong>&#39;주소&#39;가 있어 특정 명령어와 데이터의 위치를 주소를 통해 알 수 있다.</strong></p>
<p><strong>3. 보조기억장치</strong></p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/b31ade8a-0e00-48c0-994c-23be3b7c0460/image.png" alt=""></p>
<p>메모리의 경우는 장치의 전원이 꺼지면 다 잃어버리게 되는 <strong>휘발성 장치</strong>이다.</p>
<blockquote>
<p>보조기억장치는 메모리와 달리 <strong>전원이 꺼져도 보관될 프로그램을 저장하는 장치</strong>다.</p>
</blockquote>
<p>즉, 메모리는 실행할 프로그램을 저장하고, 보조기억장치는 보관해야 할 정보를 저장한다. </p>
<p><strong>4. 입출력장치</strong></p>
<blockquote>
<p><strong>컴퓨터 외부에 연결되어 컴퓨터 내부와 정보를 교환</strong>할 수 있는 부품이다. </p>
</blockquote>
<p>참고로 보조기억장치도 장기적으로 보관해야할 정보를 저장하는 특수한 입출력장치 중 하나라고 봐도 무관하다. <strong>보조기억장치 + 입출력장치를 다 통칭해서 block devices</strong>라고 하기도 한다. </p>
<p><strong>5. 메인보드</strong></p>
<blockquote>
<p>이 4가지 부품들이 모두 모이는 부품이 <strong>메인보드(마더보드)</strong>이다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/2933ae5b-3fb9-493d-9f47-cb9c3fe862f7/image.png" alt=""></p>
<p>메인보드 안에서는 <strong>&#39;bus&#39;라는 부품끼리의 정보를 주고받는 일종의 통로 역할을 하는 장치</strong>를 통해 정보를 주고받는다. 다양한 종류의 bus가 있고, 그 중에 컴퓨터 핵심 부품을 연결하는 버스는 <strong>System bus</strong>다.</p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/a28b6f13-8923-4706-9109-9d44f17b40da/image.png" alt=""></p>
<p>System bus안에도 여러 가지 종류의 bus가 있고, 각 bus는 각각의 해당되는 정보를 전달한다.</p>
<h3 id="3-헷갈려서-ai에게-확인한-부분">3. 헷갈려서 AI에게 확인한 부분</h3>
<p>이 파트를 들으면서 따로 개념적인 부분에서 AI에게 질문한 것은 없었다.</p>
<h3 id="4-개발-또는-일상과-연결해-본-예시">4. 개발 또는 일상과 연결해 본 예시</h3>
<p>어렸을 때 부터 컴퓨터가 고장나면 &#39;메인 보드가 나갔다&#39;라는 말을 많이 들어봤었는데, 이 메인보드가 컴퓨터의 핵심 부품 4가지를 포함하고 있는 부분이었다.</p>
<h3 id="5-추가로-알아보고-싶은-내용">5. 추가로 알아보고 싶은 내용</h3>
<p>내가 쓰고 있는 맥북에 있는 M칩도 CPU의 일종인지 궁금해서 AI한테 물어보았다.</p>
<blockquote>
<p>애플의 M칩은 <strong>CPU를 &#39;포함&#39;하지만, 단순한 CPU가 아닌 그 이상을 담은 SoC(System on Chip)&#39;</strong>이다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/c4cb421c-f543-4fce-8176-dde14edca4d0/image.png" alt=""></p>
<p>전통적인 컴퓨터 구조면 메인보드에 여러가지 부품을 따로 꽂아서 조립한다. <strong>SoC는 이와 달리 손톱만한 칩에 컴퓨터 구도에 필요한 핵심 부품들을 모조리 통합해놓은 구조</strong>다. CPU, GPU, NPU, 통합 메모리 등이 다 포함되어있다.</p>
<p>CPU, RAM, GPU가 물리적으로 떨어져 있으면 언급했다싶이 bus를 통해서 서로 데이터를 주고받아야 한다. 이렇게 되면 <strong>시간도 지연되고, 전력 소모도 커지는 bottleneck(병목 현상)이 발생</strong>하는데, 애플은 <strong>이 부품들을 하나의 칩에 통합시켜 데이터 간의 이동 거리를 극한으로 줄임으로써 통신 속도는 매우 빨라지고, 전력은 매우 적게 먹는 구조</strong>를 SoC를 통해 구현해낸 것이다.</p>
<h2 id="전공자-선택-작성-심화-질문">전공자 선택 작성: 심화 질문</h2>
<p>전공자는 가능하다면 아래 질문 중 하나 이상에 답해 주세요.</p>
<ul>
<li><p><strong>이 개념은 왜 필요한가?</strong>
컴퓨터를 다루는 사람이 기본적인 컴퓨터 구조를 알고, 이 구조에서 데이터가 어떤 통로로 오가는지는 알아야 한다고 생각한다.</p>
</li>
<li><p><strong>비전공자에게 설명한다면 어떤 순서로 설명할까?</strong>
일상에서 흔히 접할 수 있는 레고 장난감과 같은 예시를 들어 하나하나의 조각을 컴퓨터의 주요 부품으로 빗대어 설명하면 이해하기 쉬울 것 같다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[정보처리기사 실기] C언어]]></title>
            <link>https://velog.io/@younjin_02/%EC%A0%95%EB%B3%B4%EC%B2%98%EB%A6%AC%EA%B8%B0%EC%82%AC-%EC%8B%A4%EA%B8%B0-C%EC%96%B8%EC%96%B4</link>
            <guid>https://velog.io/@younjin_02/%EC%A0%95%EB%B3%B4%EC%B2%98%EB%A6%AC%EA%B8%B0%EC%82%AC-%EC%8B%A4%EA%B8%B0-C%EC%96%B8%EC%96%B4</guid>
            <pubDate>Sat, 11 Apr 2026 05:32:57 GMT</pubDate>
            <description><![CDATA[<p>한달여 전, 정보처리기사 필기시험 합격을 했다. 무난히 기출만 잘 풀어봐도 통과할 정도의 난이도였고, 평균 75점 정도 나왔던 기억이 있다.</p>
<p>이제 실기를 공부해보려고 한다. 최근에 실기 시험에서 <strong>프로그래밍 문제의 비중이 조금 높아지고, 난이도도 어려워진다</strong>는 얘기를 듣고, 프로그래밍쪽부터 공부를 하기로 계획했다. 아무래도 개념 문제는 너무 범위가 넓어서 나 포함 많은 사람들이 프로그래밍 및 SQL쪽 문제에서 최대한 많은 문제를 맞추는 전략으로 가는듯 하다.</p>
<h3 id="1-c의-특징">1. C의 특징</h3>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/7d9145da-26f5-425a-b1d5-0ccb9dbd6524/image.png" alt=""></p>
<blockquote>
<p>자바가 웹 애플리케이션이나 엔터프라이즈 백엔드 환경에서 주로 활약한다면, <strong>C는 하드웨어와 가장 가까운 곳, 시스템 레벨에서 주로 쓰인다.</strong></p>
</blockquote>
<p><strong>운영체제(OS)</strong>와 같은 Linux 커널이나 윈도우의 핵심 로직이 대부분 C로 작성되어 있고, 스마트 기기, 센서, 가전제품 등 <strong>CPU나 메모리 자원이 제한적인 하드웨어를 가볍고 빠르게 제어</strong>할 때 C가 주로 쓰인다. 작성자 본인도 학교에서 OS 과목을 수강하면서 pintOS 프로젝트를 C로 진행했는데, C를 잘 몰라 매우 힘들었던 기억이 있다. <del>망할 핀토스</del></p>
<p>작성자에게 제일 익숙한 언어인 자바와 비교해보자면, 자바는 JVM이라는 가상머신 위에서 돌아가서 가비지 컬렉터처럼 알아서 메모리를 관리해주는 기능이 있어 유연하게 개발하기 좋다. 하지만 C는 컴파일러를 통해 직접 기계어로 변환되기에 코드를 짜는 개발자가 직접 메모리 주소를 다루고 자원을 할당/해제해야한다. </p>
<h3 id="2-포인터">2. 포인터</h3>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/e6ac8063-8504-40d8-b04b-4a5203fff141/image.png" alt=""></p>
<p>C에는 그 유명한 포인터가 있다. 자바에는 없는 개념이라 좀 계속 헷갈리긴 한다.</p>
<blockquote>
<p>포인터란, <strong>주소를 저장하는 변수</strong>다.</p>
</blockquote>
<pre><code class="language-c">int a = 5;
int *p = &amp;a;</code></pre>
<p>위 코드의 경우에 a에는 5가 들어가있고, p에는 a의 주소가 들어가 있다고 생각하면 된다. </p>
<blockquote>
<p>&#39;<strong>&amp;</strong>&#39;가 &quot;<strong>주소를 꺼내라</strong>&quot;라는 뜻이고, &#39;<strong>***&#39;는 &quot;</strong>그 주소로 가서 실제 값을 꺼내라**&quot;이다.</p>
</blockquote>
<p>즉, 위의 코드를 다시 보면, a에는 5라는 &#39;값&#39;, &amp;a는 a의 주소, p는 주소를 담은 변수, *p는 그 주소 안의 값이 되는 것이다. </p>
<blockquote>
<p><strong>포인터도 자료형이 있다.</strong></p>
</blockquote>
<pre><code class="language-c">int *p; //int를 가리키는 포인터
char *c; //char을 가리키는 포인터</code></pre>
<p>이게 중요한 이유는, 포인터가 한 칸 이동할 때 <strong>이동 크기가 자료형마다 다르기 때문</strong>이다. </p>
<pre><code class="language-c">int arr[] = {10, 20, 30};
int *p = arr;</code></pre>
<p>시험에서는 포인터+1은 다음 칸 이동이라고 생각하면 편하다.</p>
<blockquote>
<p><strong>변수를 처음 &#39;선언할 때&#39;와 나중에 &#39;사용할 때&#39; *의 의미는 완전히 다르다.</strong></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/6c65d557-4800-4c57-8dde-d60bc631e74b/image.png" alt=""></p>
<p>이 차이를 모르면 매우 헷갈릴 수 있다.</p>
<blockquote>
<p><strong>배열 이름은 첫 번째 칸의 주소</strong>처럼 생각하면 된다</p>
</blockquote>
<pre><code class="language-c">int arr[] = {10, 20, 30};

printf(&quot;%d&quot;, arr[1]);
printf(&quot;%d&quot;, *(arr+1));</code></pre>
<p>위와 같은 코드가 있을 때, arr = &amp;arr[0]처럼 생각하면 되고, *arr = arr[0], *(arr+1) = arr[1] 라고 이해하면 된다. 배열 이름 자체가 &quot;첫 번째 원소의 주소&quot;가 되는 것이다.</p>
<pre><code class="language-c">printf(&quot;%d&quot;, arr[1]); // 20
printf(&quot;%d&quot;, *(arr+1)); // 20</code></pre>
<blockquote>
<p>이중 포인터는, <strong>&quot;포인터를 가리키는 포인터&quot;</strong>다.</p>
</blockquote>
<pre><code class="language-c">int a = 10;
int *p = &amp;a;
int **pp = &amp;p;</code></pre>
<p>p가 a의 주소를 담고, pp는 p의 주소를 담고, <em>pp는 p의 값(a의 주소), *</em>pp는 10이 되는 것이다. 이렇게 보니까 좀 헷갈리긴 한다. 이중 포인터의 개념을 정확히 알고가자.</p>
<h3 id="3-배열">3. 배열</h3>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/0787573c-a068-40d7-9505-8e16f8273f6c/image.png" alt=""></p>
<p>자바에서 배열은 객체 느낌이 강하지만, C 배열은 <strong>메모리에 값이 줄줄이 붙어서 저장된 것</strong>이라고 생각하면 된다. </p>
<pre><code class="language-c">int arr[3] = {1, 2, 3};</code></pre>
<p>이게 대표적인 C의 배열 선언이다. 선언할 때 자료형과 같이 쓰인 arr[3]의 3이 index가 아니라 3이 배열의 크기라는 것을 유의하자.</p>
<blockquote>
<p><strong>arr[i] = *(arr+i)</strong> </p>
</blockquote>
<ul>
<li>arr[0] = *arr</li>
<li>arr[1] = *(arr+1)</li>
</ul>
<p>이건 엄청 자주 나온다. arr이라는 배열의 이름 자체가 배열의 첫 번째 원소의 &#39;주소&#39;를 가리키므로, *가 붙으면 배열의 첫 번째 값이 되는 것이다.</p>
<p>C에도 2차원 배열이 있다. 또, 포인터를 저장하는 배열도 있다. </p>
<pre><code class="language-c">int *parr[2];</code></pre>
<p>위의 코드는 포인터 2개를 저장하는 배열을 선언하는 방식이다. 포인터를 저장하는데 왜 &#39;&amp;&#39;를 안 쓰고 &#39;<em>&#39;를 쓰냐고 묻는다면, <strong>&#39;&amp;&#39;은 이미 만들어져 있는 변수에게 가서 &#39;너의 메모리 주소를 가져와&#39;라는 &#39;동사적 성격&#39;</strong>을 띄고, &#39;</em>&#39;기호가 선언할 때 붙으면 &#39;이 배열은 &#39;주소&#39;를 저장할 방이구나&#39;라고 인식하게 되기에 *를 사용하는 것이다.</p>
<h3 id="4-c-문자열">4. C 문자열</h3>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/cce6a36a-0f50-4c1d-866c-f3f09a66158a/image.png" alt=""></p>
<blockquote>
<p><strong>C는 문자열이 &#39;문자 배열&#39;이다.</strong></p>
</blockquote>
<p>자바에는 &quot;ABC&quot;처럼 String 객체가 따로 존재했지만, C는 그렇지 않다. </p>
<pre><code class="language-c">char str[] = &quot;ABC&quot;;</code></pre>
<p>위의 코드처럼 str이라는 문자 배열에 A, B, C가 하나씩 순서대로 저장되고, 마지막에 <strong>&#39;\0&#39; 이라는 &#39;문자열 끝&#39;을 표시하는 원소</strong>가 들어간다.</p>
<pre><code class="language-c">char a[] = &quot;Art&quot;;
char *p = a;</code></pre>
<p>a는 문자 배열이고, p는 이 문자 배열의 첫 글자를 가리키는 포인터다. 시험에서 <strong>&#39;문자열의 시작점을 가리키는 의미&#39;</strong>로 잘 나오니 기억해두자.</p>
<blockquote>
<p><strong>strlen</strong>은 문자열 길이를 구하는 함수다.</p>
</blockquote>
<pre><code class="language-c">strlen(&quot;ABC&quot;) // 3</code></pre>
<pre><code class="language-c">for (i = 0; str[i] != &#39;\0&#39;; i++) //끝 표시가 나올 때까지 한 글자씩 보기</code></pre>
<pre><code class="language-c">while (*s) {
    *d = *s; //s가 가리키는 글자를 d가 가리키는 곳에 복사
    d++; // d 다음칸으로 이동
    s++; //s 다음칸으로 이동
}</code></pre>
<p>위의 코드는 문자열을 한 글자씩 복사하는 코드다. 시험에서는 위와 반대로 양쪽 끝에서 안쪽으로 가면서 바꾸는, 문자열 전체를 뒤집는 코드도 많이 나온다. </p>
<pre><code class="language-c">#include &lt;stdio.h&gt;
#include &lt;string.h&gt;

void reverseString(char *str) {
    // 1. 양 끝을 가리키는 포인터 설정
    char *start = str;                      // 문자열의 첫 글자를 가리킴
    char *end = str + strlen(str) - 1;      // 문자열의 마지막 글자를 가리킴
    char temp;                              // 값을 교환하기 위한 임시 빈 잔

    // 2. start와 end가 만나거나 엇갈리기 전까지 반복
    while (start &lt; end) {
        // 3. 값 교환 (Swap) 알고리즘
        temp = *start;   // 첫 글자를 임시 공간에 보관
        *start = *end;   // 마지막 글자를 첫 글자 위치에 덮어씀
        *end = temp;     // 보관해둔 첫 글자를 마지막 위치에 넣음

        // 4. 포인터 이동
        start++;         // 왼쪽 포인터는 오른쪽으로 한 칸 이동
        end--;           // 오른쪽 포인터는 왼쪽으로 한 칸 이동
    }
}

int main() {
    char arr[] = &quot;HELLO&quot;; // 뒤집을 문자열 배열

    reverseString(arr);

    printf(&quot;%s\n&quot;, arr);  // 출력 결과: OLLEH

    return 0;
}


// 인덱스를 사용하는 방식 (원리는 포인터와 100% 동일)
int left = 0;
int right = strlen(str) - 1;
char temp;

while (left &lt; right) {
    temp = str[left];
    str[left] = str[right];
    str[right] = temp;

    left++;
    right--;
}</code></pre>
<blockquote>
<p>*<em>C에서는 문자가 숫자처럼 움직인다. *</em></p>
</blockquote>
<p>자바에서도 char가 숫자값이 있긴 하지만, 잘 써먹진 않는 반면, C에서는 자주 사용한다.</p>
<pre><code>&#39;E&#39; - &#39;A&#39; // 4

&#39;3&#39; - &#39;0&#39; // 3</code></pre><p>A와 E 사이에 B, C, D 거리가 4이기 때문에 4가 출력된다. 또 아랫줄처럼 문자열을 숫자처럼 계산하는 식으로 사용도 가능하다.</p>
<h3 id="5-함수">5. 함수</h3>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/fbb33306-4729-44b5-9ff4-deea5ea89a7a/image.png" alt=""></p>
<blockquote>
<p>C는 기본적으로 *<em>값만 복사해서 넘긴다. *</em></p>
</blockquote>
<pre><code class="language-c">void swap(int a, int b) {
    int t = a;
    a = b;
    b = t;
}</code></pre>
<p>위의 코드는 a와 b를 바꾸는 함수가 아니다. 이 함수의 경우 <strong>원본이 아닌 복사본만 받게 된다.</strong> main에 있던 진짜 a,b가 들어가지 않고, 그 값을 복사한 &#39;새 변수&#39; a,b가 들어간다. </p>
<blockquote>
<p><strong>원본을 바꾸려면, 주소를 넘겨야 한다.</strong></p>
</blockquote>
<pre><code class="language-c">void swap(int *a, int *b)</code></pre>
<p>이렇게 주소를 받아야 원본 위치를 직접 바꿀 수 있다.</p>
<blockquote>
<p><strong>static 지역 변수는 함수가 끝나도 값이 유지된다.</strong></p>
</blockquote>
<pre><code class="language-c">int f() {
    int x = 0;
    x++;
    return x;
}</code></pre>
<p>이건 매번 호출할 때마다 x가 0으로 다시 시작하지만,</p>
<pre><code class="language-c">int f() {
    static int x = 0;
    x++;
    return x;
}</code></pre>
<p>static으로 선언하게 되면, 첫 번째 호출 1, 두 번째는 2, 이렇게 함수가 끝나도 값이 유지되어 누적이 된다. 시험에서 이걸 활용한 누적값 문제를 많이 낸다.</p>
<h3 id="6-구조체struct">6. 구조체(struct)</h3>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/abb447d9-de18-45e7-b70b-a173e1f4a466/image.png" alt=""></p>
<blockquote>
<p>구조체는 *<em>&#39;관련 있는 데이터의 묶음&#39; *</em>정도로 보면 된다.</p>
</blockquote>
<pre><code class="language-c">struct Person {
    char name[20];
    int age;
};</code></pre>
<p>위의 코드는 이름, 나이를 하나로 묶은 자료형이다. 자바의 class처럼 메서드, 상속 이런걸 굳이 생각할 필요는 없다. </p>
<pre><code class="language-c">//구조체 멤버 접근
p.age //변수 접근은  &#39;.&#39;

ptr-&gt;age // 포인터 접근은 &#39;-&gt;&#39;

(*ptr).age</code></pre>
<p>p가 구조체를 가리키는 포인터일 때 <strong>&#39;.&#39;이나 &#39;-&gt;&#39;를 사용해 접근</strong>한다. </p>
<blockquote>
<p><strong>구조체에 포인터가 합쳐지면 연결 리스트를 구현할 수 있다.</strong></p>
</blockquote>
<pre><code class="language-c">struct Node {
    int value; //일반 변수
    struct Node *next; //포인터 변수
};</code></pre>
<p>이렇게 되면 각 노드가 자기 값 하나, 다음 노드를 가리키는 주소 하나를 가진다는 뜻이다. 다만, 위의 코드가 구조체 안에 구조체가 들어간 형태는 아니다. <strong>next는 다음 노드의 메모리 주소를 담는 &#39;포인터 변수&#39;</strong>인 것이다.</p>
<ul>
<li>head : 첫 번째 노드</li>
<li>head-&gt;value : 첫 번째 노드 값</li>
<li>head-&gt;next : 두 번째 노드 주소</li>
<li>head-&gt;next-&gt;value : 두 번째 노드 값</li>
</ul>
<h3 id="7-bit-연산--shift-연산">7. Bit 연산 &amp; Shift 연산</h3>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/124ddd10-d891-4fd0-9be5-e348d87c8487/image.png" alt=""></p>
<p>비트 연산과 시프트 연산은 <strong>숫자를 이진수로</strong> 다루는 문제다. 다소 난이도가 있는 내용이긴 하지만, 정처기에서는 깊게까지는 안간다.</p>
<pre><code>35 &lt;&lt; 2 //140</code></pre><p>이건 왼쪽으로 2칸 민 형태인데, 시험에서는 왼쪽으로 1칸은 2배, 2칸은 4배라고 생각하면 된다. 반대로 &gt;&gt;은 보통 2로 나누는 느낌으로 본다.</p>
<blockquote>
<p><strong>비트 연산 기호</strong>
    •    <strong>&amp;</strong> : 둘 다 1일 때만 1
    •    <strong>|</strong> : 하나라도 1이면 1
    •    <strong>^</strong> : 서로 다를 때만 1
    •    <strong>~</strong> : 0과 1 뒤집기</p>
</blockquote>
<p>다른 건 몰라도 &amp;과 &gt;&gt;, &lt;&lt; 연산자는 꼭 기억하도록 하자.</p>
<blockquote>
<p>0x로 시작하면 16진수고, 2진수로 바꿔서 직접 보는게 맞다.</p>
</blockquote>
<h3 id="8-switch문의-함정">8. switch문의 함정</h3>
<blockquote>
<p>switch문에서, <strong>break가 없으면 아래의 코드도 계속 실행된다는걸 기억하자.</strong></p>
</blockquote>
<pre><code class="language-c">switch(a) {
    case 11:
        b += 2;
    default:
        b += 3;
        break;
}</code></pre>
<p>a가 11이면 case 11이 실행되지만, break;가 없어서 아래의 default도 실행된다. C에서 이걸 함정으로 많이 내니 switch문이 나오면 break가 어디있는지 확인해봐야 한다.</p>
<h3 id="9-malloc-동적-메모리-할당">9. malloc: 동적 메모리 할당</h3>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/97be8fdb-d455-480c-b59f-94a941d44231/image.png" alt=""></p>
<blockquote>
<p>*<em>malloc은 &#39;자바의 new&#39;와 똑같은 역할이다. *</em></p>
</blockquote>
<p>&#39;동적 메모리 할당&#39;은, <strong>프로그램이 실행되는 도중에 딱 필요한 크기의 공간만큼만 OS로부터 빌려오는 효율적인 방식</strong>이다. 여기에 대표적으로 활용되는게 malloc인 것이다.</p>
<p>반면에 정적 할당은 int arr[100]과 같이 미리 size를 정해놓고 할당해놓는 비효율적인 방식이다. </p>
<pre><code class="language-c">res = (int **)malloc(sizeof(int *) * rows);</code></pre>
<p>위 코드는 <strong>rows 개수만큼 포인터를 담을 공간을 만든다</strong>는 뜻이다. </p>
<blockquote>
<p>다 쓰면 <strong>&#39;free&#39;</strong>로 메모리도 반납해주어야 한다.</p>
</blockquote>
<p>자바의 경우, new로 새 객체를 선언하고 안쓰게 되면 가비지 컬렉터가 알아서 객체를 치워줘 메모리 걱정을 할 필요가 없지만, C언어는 따로 이런 기능이 없다. 따라서 free로 메모리를 해제해줘야, Memory leak(메모리 누수)가 발생하지 않는다. </p>
<h3 id="꼭-기억해야-할-것들">*꼭 기억해야 할 것들</h3>
<blockquote>
<p><strong>배열은 객체가 아니다.</strong></p>
</blockquote>
<p>C배열은 값들이 연속적으로 붙어있는 메모리 덩어리다. 배열 이름은 시험에서 거의 <strong>첫 번째 칸 주소</strong>처럼 쓰인다. </p>
<blockquote>
<p><strong>문자열은 String이 아니다.</strong></p>
</blockquote>
<p>C 문자열은 char 배열 + 마지막 &#39;\0&#39;이다.</p>
<blockquote>
<p><strong>함수에서 원본을 바꾸려면 주소를 넘거야 한다.</strong></p>
</blockquote>
<blockquote>
<p><strong>switch는 해당 case만 실행되지 않고, break가 없으면 계속 실행된다.</strong></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[정보처리기사] 4. 프로그래밍 언어 활용]]></title>
            <link>https://velog.io/@younjin_02/%EC%A0%95%EB%B3%B4%EC%B2%98%EB%A6%AC%EA%B8%B0%EC%82%AC-4.-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%96%B8%EC%96%B4-%ED%99%9C%EC%9A%A9</link>
            <guid>https://velog.io/@younjin_02/%EC%A0%95%EB%B3%B4%EC%B2%98%EB%A6%AC%EA%B8%B0%EC%82%AC-4.-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%96%B8%EC%96%B4-%ED%99%9C%EC%9A%A9</guid>
            <pubDate>Mon, 23 Feb 2026 18:12:26 GMT</pubDate>
            <description><![CDATA[<p>4과목에서는 프로그래밍 &#39;언어&#39;와 여러 SW 기술에 대해 다룬다. 정보처리기사에서 주로 다루는 C, Java, Python은 실기에서도 중요한 언어이므로 개념을 잘 공부해두는 것이 중요하다.
<img src="https://velog.velcdn.com/images/younjin_02/post/e71c6432-576a-4d63-9dbe-c31cbd2b2365/image.png" alt=""></p>
<p>목차는 위와 같고, 거의 2장과 3장에서만 시험에 나온다.</p>
<h2 id="1-데이터-타입">1. 데이터 타입</h2>
<h4 id="1-1-c와-java의-기본-데이터-타입">1-1. C와 Java의 기본 데이터 타입</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/45608abf-696b-4509-94f9-82b76f30c2e4/image.png" alt=""></p>
<p><strong>C에는 boolean 기본형이 없어</strong>, 보통 int 0/1로 처리한다. </p>
<h4 id="1-2-c의-구조체">1-2. C의 구조체</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/db2b9814-ab48-4dfe-80a2-dbf6443e2e7d/image.png" alt=""></p>
<blockquote>
<p>구조체란, <strong>자료의 종류가 다른 변수의 모임</strong>이다.</p>
</blockquote>
<p><strong>struct</strong>라는 예약어를 쓰고, <strong>&#39;서로 다른&#39;</strong> 자료형을 묶는게 포인트다. 배열과 자주 비교되는데, 배열은 &#39;같은&#39;자료형을 묶는다.</p>
<h4 id="1-3-python의-시퀀스-자료형">1-3. Python의 시퀀스 자료형</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/a822f50a-128f-4a7a-9e5f-99a5dea7a0fd/image.png" alt=""></p>
<ul>
<li><strong>List</strong>: <strong>&#39;가변&#39;</strong>적인 자료형으로, 요소의 추가/삭제/수정이 가능하다.</li>
<li><strong>Tuple</strong>: list와 달리 <strong>&#39;불변&#39;</strong>적인 자료형이다.</li>
<li><strong>range</strong>: <strong>연속된 숫자를 생성</strong>하며, 반복문에서 주로 사용된다.</li>
</ul>
<h4 id="변수명-사용-규칙">*변수명 사용 규칙</h4>
<pre><code>1.    영문자, 숫자, _(언더바) 사용 가능
2.    첫 글자는 숫자 불가
3.    공백 사용 불가
4.    * + - / 등 특수문자 사용 불가
5.    대소문자 구분
6.    예약어 사용 불가</code></pre><p>대부분은 다 아는 내용이지만 <strong>&#39;첫 글자는 숫자가 불가하다&#39;</strong>는 헷갈릴 수 있으니 기억해두도록 하자.</p>
<h4 id="garbage-collector">*Garbage Collector</h4>
<blockquote>
<p>선언만 하고 사용하지 않는 변수들이 점유한 메모리 공간을 강제로 해제하는 과정이다.</p>
</blockquote>
<p>이를 통해 <strong>자동으로 메모리를 관리</strong>한다. 대표적으로 자바와 파이썬이 이게 있고, C의 경우는 free를 사용해 직접 해제한다.</p>
<h2 id="2-연산자">2. 연산자</h2>
<h4 id="2-1-산술-연산자">2-1. 산술 연산자</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/871894cb-f445-4238-9c6a-09e11e8f9afa/image.png" alt=""></p>
<p>%는 정수만 연산 가능함이 포인트고, ++와 -- 연산자의 경우 전위나 후위냐가 중요하다. </p>
<pre><code class="language-c">a = 5;
b = ++a;  // a=6, b=6

a = 5;
b = a++;  // b=5, a=6 (먼저 연산에 사용하고, 그다음에 증가)</code></pre>
<h4 id="2-2-bit-연산자">2-2. Bit 연산자</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/3b170d22-8cd8-4636-851b-2c92ad2872f6/image.png" alt=""></p>
<p>Bit는 0과 1을 의미한다. 
<strong>^(XOR)</strong>은 모든 비트가 같으면 0, 하나라도 다르면 1이다.
or은 <strong>&#39;|&#39;</strong>를 더 많이쓴다. </p>
<p>시프트 연산은 왼쪽은 X2, 오른쪽은 /2이다.</p>
<pre><code>4 &lt;&lt; 1  → 8
8 &gt;&gt; 1  → 4</code></pre><h4 id="2-3-논리-연산자">2-3. 논리 연산자</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/ea029aaf-9d27-4bf0-b0cc-5eb121816fa4/image.png" alt=""></p>
<p><strong>비트 연산자의 &amp;와 논리 연산자의 &amp;&amp;</strong>가 의미는 같으나 무슨 연산자냐에 따라 기호가 다르니 유의해야 한다. </p>
<h4 id="2-4-조건-연산자삼항-연산자">2-4. 조건 연산자(삼항 연산자)</h4>
<blockquote>
<p><strong>조건 ? 참일때값 : 거짓일때값;</strong></p>
</blockquote>
<pre><code class="language-c">mx = a &lt; b ? b : a;</code></pre>
<p>위와 같이 조건 연산자는 if문을 대신할 수 있다.</p>
<h4 id="2-5-연산자-우선순위">2-5. 연산자 우선순위</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/922cf1aa-27aa-4cb0-85df-7ea58ee0c956/image.png" alt=""></p>
<ul>
<li>단항 연산자가 제일 높다</li>
<li>+-는 곱셈/나눗셈 보다 후순위다. </li>
<li><strong>비트 → 논리 순서 → 조건 순서</strong>이다.</li>
<li>대입은 거의 마지막이다.</li>
</ul>
<h2 id="3-데이터-입출력">3. 데이터 입/출력</h2>
<p>데이터 입/출력을 알기 전에 주요 서식 문자열부터 정리하자.
<img src="https://velog.velcdn.com/images/younjin_02/post/a7d3ab24-0489-4b0b-94a6-a342a6c9698e/image.png" alt=""></p>
<p>%s는 문자 하나도 출력 가능하다.</p>
<h4 id="3-1-c의-printf-함수">3-1. c의 printf() 함수</h4>
<pre><code class="language-c">printf(&quot;서식문자열&quot;, 변수);

printf(&quot;%d, %c&quot;, a, b); //ex</code></pre>
<p>a를 정수 출력하고, &#39;,&#39;도 출력하고, 공백 출력 후 b가 문자로 출력된다. 서식 안의 문자도 그대로 출력된다.</p>
<h4 id="3-2-java의-출력-함수">3-2. Java의 출력 함수</h4>
<p>자바는 난이도가 높은 언어답게 출력함수 종류가 3가지다.
또한 자바는 서식 문자열 없이도 출력 함수를 사용 가능하다.</p>
<pre><code class="language-java">System.out.printf(&quot;%d&quot;, r); //c 형식과 동일한 형태

System.out.print(r + s); //줄 바꿈 없음

System.out.println(r + &quot;은 소수&quot;); //출력 후 줄바꿈</code></pre>
<p><strong>줄바꿈이 이뤄난다는 말은, 출력 후 커서를 다음 줄의 처음으로 옮긴다는 뜻</strong>이다</p>
<h2 id="4-제어문--반복문">4. 제어문 &amp; 반복문</h2>
<h4 id="1-단순-if문">1. 단순 if문</h4>
<pre><code class="language-c">if (a &gt; b)
    printf(&quot;Gilbut&quot;);</code></pre>
<p>조건이 &#39;한 개&#39;일 때 사용하는 제어문이다.</p>
<h4 id="4-2-switch문">4-2. switch문</h4>
<blockquote>
<p><strong>조건에 따라 분기할 곳이 여러 개인 경우</strong> 간단하게 처리할 수 있는 제어문</p>
</blockquote>
<pre><code class="language-c">switch(a) {
    case 1:
        printf(&quot;바나나&quot;);
        break;
    case 2:
        printf(&quot;딸기&quot;);
        break;
    default:
        printf(&quot;없음&quot;);
}</code></pre>
<p><strong>&#39;break&#39;</strong>에 집중해야 한다. 만약 break가 없다면 switch문이 종료될 때 까지 모든 문장이 실행된다. 이를 <strong>fall-through</strong>라고 한다.</p>
<h4 id="4-3-for문반복문">4-3. for문(반복문)</h4>
<p><strong>정해진 횟수</strong>만큼 반복한다.</p>
<pre><code class="language-c">for (i = 1; i &lt;= 10; i++)
    sum = sum + i;</code></pre>
<h4 id="4-4-while문">4-4. while문</h4>
<p><strong>조건이 참</strong>일 동안 실행하는 반복 수행하는 제어문이다.</p>
<pre><code class="language-c">while(i &lt;= 10){
  i=i+1;
}</code></pre>
<p><strong>조건을 먼저 검사</strong>해서, 처음부터 조건이 거짓이면 아예 실행을 안 한다. </p>
<h4 id="do-while문">*do-while문</h4>
<pre><code class="language-c">do
  i=i+1;
while(i &lt;= 10);</code></pre>
<p>do가 있기 때문에 무조건 <strong>1번은 실행</strong>한다. 조건 뒤에 ; 붙는거를 체크해야 한다. </p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/454f9f1f-a48c-4013-8b3d-346a67e7aed2/image.png" alt=""></p>
<h2 id="5-배열">5. 배열</h2>
<h4 id="5-1-1차원-배열">5-1. 1차원 배열</h4>
<blockquote>
<p>변수를 일직선(선형)으로 나열한 것</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/b64fac29-fdaa-4f51-b8a1-2d7f26f2d79a/image.png" alt=""></p>
<p>인덱스는 항상 0부터 시작한다.</p>
<h4 id="5-2-2차원-배열">5-2. 2차원 배열</h4>
<blockquote>
<p>변수를 <strong>행(row) + 열(column)</strong>로 조합으로 평면 형태로 조합한 배열 (표 형태)</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/47d9b24c-e85b-4469-b232-f3b6cc452ab8/image.png" alt=""></p>
<h4 id="배열-형태의-문자열-변수">*배열 형태의 문자열 변수</h4>
<p>이건 C언어만의 특징이다. C언어에서는 <strong>큰따옴표로 묶인 글자는 글자 수에 관계없이 배열로 처리</strong>된다. <strong>문자열 끝에는 널 문자 &#39;\0&#39;가 자동으로 붙는다. ** 즉, 문자열 저장 방식이 **&#39;문자 배열&#39;</strong>인 것이다.</p>
<p>즉, &quot;love&quot;의 길이 자체는 4지만, 마지막에 \0이 들어가야 하기에 배열 크기는 5가 된다.</p>
<h2 id="6-포인터">6. 포인터</h2>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/81b6ea58-6dda-40c7-98ae-e5ee054c8ada/image.png" alt=""></p>
<blockquote>
<p>포인터란, <strong>다른 변수의 주소를 저장하는 변수</strong>를 말한다.</p>
</blockquote>
<pre><code class="language-c">int *a; //int형 데이터를 가리키는 포인터</code></pre>
<p>포인터 선언은 위와 같이 자료형을 먼저 쓰고, 변수명 앞에 &#39;*&#39;를 붙인다. </p>
<pre><code class="language-c">b = &amp;a; //b에 a의 주소를 저장</code></pre>
<p><strong>&amp;(참조 연산자)</strong>은 변수의 주소를 구한다는 뜻이다.</p>
<pre><code class="language-c">c = *a; //*a = a가 가리키는 값</code></pre>
<p>실행문에서 포인터 변수에 간접 연산자 *를 붙이면 해당 포인터 변수가 가리키는 곳의 값을 말한다. </p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/9137e44b-eb4f-4259-8513-d5537087ab26/image.png" alt=""></p>
<pre><code class="language-c">int a = 50;
int *b;
b = &amp;a;
*b = *b + 20;
printf(&quot;%d, %d&quot;, a, *b);</code></pre>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/9b90ad7b-b570-4337-a553-1615cdec9dfc/image.png" alt=""></p>
<p>위 코드의 실행 결과다. 작성자의 경우 처음에는 a는 50, b는 70이라고 생각했지만 아니다. <strong>b가 가리키는 곳에 20을 더하는 것이므로, 이 순간 a도 20을 더해주는 것이다.</strong> 따라서 둘 다 값이 70이 된다.</p>
<h4 id="포인터와-배열">*포인터와 배열</h4>
<blockquote>
<p><strong>배열을 포인터 변수에 저장한 후 포인터를 이용해 배열의 요소에 접근</strong>할 수 있다.</p>
</blockquote>
<pre><code class="language-c">int a[5];
int *b;

b = a;      // a[0] 주소
b = &amp;a[0];  // 윗줄과 같은 의미</code></pre>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/16c8e0fc-f0d4-44f7-bf05-4d9e850c0e55/image.png" alt=""></p>
<p>배열의 <strong>대표명만 지정</strong>하면 <strong>배열의 첫 번째 요소의 주소를 지정</strong>하는 것과 같다. 위 그림의 포인터 표기 방법도 잘 알아두자.</p>
<h2 id="7-python">7. Python</h2>
<h4 id="7-1-input-함수">7-1. input() 함수</h4>
<blockquote>
<p>키보드 입력값을 변수에 저장</p>
</blockquote>
<p>입력값은 무조건 <strong>문자열(str)로 저장</strong>한다.</p>
<pre><code class="language-python">a = input()</code></pre>
<p>여기서 중요한 건 숫자로 입력해도 문자열로 저장된다는 뜻이다. 위 코드에 10을 입력해도 &quot;10&quot;으로 저장된다.</p>
<p>따라서, *<em>input 함수에서 다른 자료형으로 저장하고 싶다면 형 변환을 해주어야 한다. *</em></p>
<pre><code class="language-python">a = int(input()) #1개 입력

a, b = map(int, input().split()) #2개 이상 입력</code></pre>
<p>두 번째 코드의 과정은 다음과 같다.
    1.    input() → 문자열
    2.    split() → 공백 기준 분리
    3.    <strong>map(int, …) → 정수 변환</strong>
    4.    a, b에 저장</p>
<p>2개 이상 입력을 형 변환하는 경우에는 <strong>map()</strong>을 사용한다는 것을 기억하자. </p>
<h4 id="7-2-print-함수">7-2. print() 함수</h4>
<p>기본 print의 역할이지만, <strong>sep, end</strong> 옵션을 주의하자.</p>
<pre><code class="language-python">print(82, 24, sep=&#39;-&#39;, end=&#39;,&#39;)

#출력 82-24,</code></pre>
<ul>
<li>sep → 값 사이 구분자</li>
<li>end → 문장 끝 문자</li>
</ul>
<h4 id="7-3-python의-list">7-3. python의 List</h4>
<p>파이썬의 리스트는 <strong>가변적</strong>인 자료형이기 때문에 선언할 때 크기를 적지 않는다. 인덱스는 똑같이 0부터 시작이지만, 배열과 달리 <strong>서로 다른 자료형을 저장 가능</strong>하다.</p>
<pre><code class="language-python">a = [10, &#39;mike&#39;, 23.45]
a[0] = 1 # 값 변경 가능</code></pre>
<h4 id="7-4-python의-dictionary">7-4. python의 dictionary</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/54b74bd8-a0a6-4d88-bb63-eebed15182b2/image.png" alt=""></p>
<blockquote>
<p><strong>key : value</strong> 형태</p>
</blockquote>
<p>연관된 값을 묶어서 저장하는 용도로 딕셔너리를 사용한다. 자바의 HashMap과 아주 유사한 자료구조다.</p>
<p>선언 형식 및 접근 방법은 다음과 같다.</p>
<pre><code class="language-python">a = {&#39;이름&#39;:&#39;홍길동&#39;, &#39;나이&#39;:25, &#39;주소&#39;:&#39;서울&#39;} //dict 선언

a[&#39;이름&#39;] # 대괄호 안에 key 지정

a[&#39;이름&#39;] = &#39;이순신&#39; # value값 변경 가능</code></pre>
<p>위의 코드가 딕셔너리를 선언하는 제일 기본적인 방법이고, <strong>dict()</strong> 함수를 활용할 수 있다.</p>
<pre><code class="language-python">person = dict(name=&quot;홍길동&quot;, age=20)</code></pre>
<p>추가로, <strong>dictionary comprehension</strong>이라는 리스트와 같은 반복 가능 객체를 가지고 딕셔너리를 간단하게 만들어내는 문법도 있다.</p>
<pre><code class="language-python">lst = [1, 2, 3]
dst = {i: i * 2 for i in lst}</code></pre>
<ul>
<li>데이터 추가/수정: dst[&quot;key&quot;] = value (이미 있으면 수정, 없으면 추가)</li>
<li>데이터 삭제: del dst[&quot;key&quot;]</li>
<li>모든 키 가져오기: dst.keys()</li>
<li>모든 값 가져오기: dst.values()</li>
</ul>
<h4 id="7-5-python의-range">7-5. python의 range</h4>
<blockquote>
<p>range는 <strong>연속된 숫자를 생성</strong>한다.</p>
</blockquote>
<p>리스트, 반복문 등에서 많이 사용된다.</p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/d7e98c55-31dd-46ef-8484-88664856ea10/image.png" alt=""></p>
<p><strong>끝값이 포함이 안된다</strong>는게 중요하다. 항상 시작부터 끝-1까지만 리스트에 저장된다. 기본 시작값은 0이다.</p>
<h4 id="7-6-python의-slicing">7-6. python의 slicing</h4>
<blockquote>
<p>문자열이나 리스트와 같은 <strong>순차형 객체에서 일부를 잘라(slicing) 반환</strong>하는 기능이다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/3775ef55-1911-4060-ad5f-e894151165f5/image.png" alt=""></p>
<p>slicing 또한 <strong>끝 인덱스는 포함 안된다.</strong> 슬라이싱은 원본 리스트를 변환하는게 아니라 <strong>새 리스트를 &#39;반환&#39;</strong>한다. </p>
<h4 id="7-7-python의-for문--while문">7-7. python의 for문 &amp; while문</h4>
<ul>
<li>for문: range와 list를 사용하는 방식이 있다.</li>
</ul>
<pre><code class="language-python">#range 사용
for i in range(1, 11): #1부터 10까지
    sum += i

#list 사용
for i in a: #리스트 요소를 하나씩 꺼냄
    sum += i</code></pre>
<ul>
<li>while문: 조건이 참일 동안 반복<pre><code class="language-python">while i &lt;= 10:
  i = i + 1</code></pre>
인지하지 못한 사실이 있는데, <strong>python은 &#39;:&#39;이 필수다</strong>. 주의하자. <del>작성자의 경우 자바만 써서 그런거다 원래 이정도는 안ㄷ</del></li>
</ul>
<h4 id="7-8-python의-class">7-8. python의 class</h4>
<pre><code class="language-python">class 클래스명:
    변수
    def 메서드(self, 매개변수):</code></pre>
<p><strong>self</strong>는 객체 자신을 의미한다. 따라서 <strong>self를 써야 함수 외부에 있는 클래스 내부의 다른 변수들을 사용할 수 있다</strong>.</p>
<pre><code class="language-python">class Cls:
    x = 10
    def add(self, a):
        return a + self.x

a = Cls()
a.x = 5
print(a.add(5))</code></pre>
<p>위의 코드를 설명해보자면, 객체 a를 생성하고, a.x=5인 객체 변수를 생성한다. 그리고 5를 넣게 되면 x값 5와 self x를 더해 10의 결과값이 출력되는 코드다.</p>
<p>파이썬의 클래스는 항상 기본 구조가 위와 같이 되는 것을 기억하자.</p>
<p>또한 파이썬 클래스는 <strong>클래스 없이 메소드만 사용 가능</strong>하다.</p>
<pre><code class="language-python">def calc(x, y):
    x *= y
    return x

a, b = 3, 4
a = calc(a, b)
print(a, b) // 12 4</code></pre>
<blockquote>
<p>파이썬의 객체는 <strong>class, <strong>init</strong>, self</strong>을 알아야 한다.</p>
</blockquote>
<pre><code class="language-python">class Node:
    def __init__(self, value):
        self.value = value
        self.children = []</code></pre>
<p>Node라는 객체에, 노드를 만들 때 value를 저장하고, 자식들을 담을 리스트를 만드는 코드다. </p>
<ul>
<li><strong><strong>init</strong></strong>: 객체가 만들어질 때 자동으로 실행되는 초기화 함수다. 자바의 생성자와 비슷하다.</li>
<li><strong>self</strong>: &#39;객체 자기 자신&#39;을 의미한다. </li>
</ul>
<h2 id="8-스크립트-언어">8. 스크립트 언어</h2>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/62506bc5-2506-4825-824f-b22c91270108/image.png" alt=""></p>
<blockquote>
<p>스크립트 언어란, <strong>인터프리터 방식</strong>으로, <strong>실행 즉시 해석</strong>되는 언어를 의미한다.</p>
</blockquote>
<p>자바와 같은 컴파일 언어의 정반대격이다. 종류를 외워야 한다. 다음과 같다.</p>
<p>파이썬이 &#39;컴파일 언어가 아니다&#39;라는 사실과, PHP는 &#39;서버용 스크립트&#39;라는 부분을 주의해서 기억하면 될 것 같다.</p>
<h4 id="쉘-스크립트에서-사용되는-제어문">*쉘 스크립트에서 사용되는 제어문</h4>
<ul>
<li>선택형: if, case</li>
<li>반복형: for, while, <strong>until</strong></li>
</ul>
<p>until은 조건이 참이 될 때 까지 반복할 때 사용된다.</p>
<h4 id="라이브러리">*라이브러리</h4>
<p>표준 라이브러리의 경우 언어에 기본 포함되어 있는 라이브러리로, 따로 설치가 필요 없으나, 외부 라이브러리의 경우 인터넷에 공유되어 있는 경우라 다운로드 후 설치가 필요하다.</p>
<p>C언의 <strong>stdlib.h</strong>라는 라이브러리가 있다. 역할은 다음과 같다.</p>
<ul>
<li>자료형 변환</li>
<li>난수</li>
<li>메모리 할당</li>
</ul>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/ee815f28-350a-4db7-8a77-251ceb58f24b/image.png" alt=""></p>
<h2 id="9-unix">9. UNIX</h2>
<blockquote>
<p>유닉스(Unix)는 벨 연구소에서 개발한 운영체제로, 다양한 현대적 컴퓨터 운영체제의 원형</p>
</blockquote>
<p>UNIX의 대표적인 특징을 정리해보자</p>
<ul>
<li>대부분 <strong>C언어</strong>로 작성되어 있어 이식성이 높다.</li>
<li>장치·프로세스 간 호환성 높다.</li>
<li><strong>다중 사용자(Multi-User) 및다중 작업(Multi-Tasking)을 지원</strong>한다.</li>
<li>트리 구조 파일 시스템</li>
</ul>
<h4 id="9-1-unix-kernel이-하는-일">9-1. UNIX kernel이 하는 일</h4>
<p>커널이라고 하면 운영체제의 핵심이다. 유닉스 커널은 다음과 같은 일을 한다.</p>
<ul>
<li>프로세스 관리(CPU 스케줄링)</li>
<li>기억장치(메모리) 관리</li>
<li>파일 시스템 관리</li>
<li>입출력 관리</li>
</ul>
<p>UNIX 쉘은 사용자의 명령어를 해석해주는 인터페이스 역할을 한다. </p>
<h4 id="9-2-unix-주요-명령어">9-2. UNIX 주요 명령어</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/03188e56-78bf-4eaf-93a6-56ebdb95587a/image.png" alt=""></p>
<p><strong>chmod(파일 권한 변경)</strong>와 <strong>chown(소유자 변경)</strong>이 생소하니 외워두자. </p>
<h2 id="10-os">10. OS</h2>
<h4 id="10-1-기억장치-배치-전략">10-1. 기억장치 배치 전략</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/6d952c14-491e-4f57-92e6-7b4a86e76c9b/image.png" alt=""></p>
<p>Best fit이 단편화(남는 공간)을 최소화시키고, Worst Fit이 남는 공간이 최대가 된다.</p>
<h4 id="10-2-페이지-교체-알고리즘---fifo">10-2. 페이지 교체 알고리즘 - FIFO</h4>
<blockquote>
<p>가장 먼저 들어온 페이지를 교체하는, FIFO, 즉 Queue 방식을 사용한다.</p>
</blockquote>
<p>페이지란, 가상 메모리를 사용하는 최소 크기 단위다. </p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/40d86e4e-0cb6-47be-b9b6-2d438302a37f/image.png" alt=""></p>
<p><strong>Hit</strong>이 이미 페이지에 참조 페이지가 존재해 부재가 없는 것이고, <strong>Fault</strong>가 해당 페이지 프레임에 참조하려는 페이지가 없는 경우다.</p>
<h4 id="10-3-thrashing">10-3. Thrashing</h4>
<blockquote>
<p>페이지 교체에 너무 많은 시간이 소요되는 현상</p>
</blockquote>
<p>프로세스 처리 시간보다 페이지 교체에 더 많은 시간이 할애되 CPU 사용률이 떨어지게 된다.</p>
<h4 id="10-4-프로세스-상태-및-상태-전이">10-4. 프로세스 상태 및 상태 전이</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/199f723f-4f1e-4b69-a409-3cb1dbc156a9/image.png" alt=""></p>
<p>상태는 아래와 같다.</p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/27f78709-a26c-4637-a6c3-8866191f553f/image.png" alt=""></p>
<p>상태 전이는 아래와 같다.</p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/4716e26e-b4ee-4f8a-8212-d6d3f30a1a14/image.png" alt=""></p>
<h4 id="10-5-sjfshort-job-first">10-5. SJF(Short Job First)</h4>
<blockquote>
<p>실행 시간이 가장 짧은 작업부터 먼저 실행</p>
</blockquote>
<p>즉, <strong>실행 시간이 가장 짧은</strong> process에게 CPU를 할당하는 기법이다. </p>
<p>중요한 건, <strong>비선점형(한 번 시작하면 끝날 때 까지 실행)</strong>이며, Ready Queue에 있는 것 중 <strong>실행 시간이 가장 짧은 것</strong>을 선택하게 된다.</p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/12e500b1-3621-4c95-bad0-506808f3e913/image.png" alt=""></p>
<h4 id="10-6-hrn">10-6. HRN</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/c06c495a-b773-4c26-b25b-e92c53f8d028/image.png" alt=""></p>
<p>즉, HRN은 <strong>기다린 시간을 반영</strong>해주는 스케줄링 기법이다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[정보처리기사] 3. DB 구축]]></title>
            <link>https://velog.io/@younjin_02/%EC%A0%95%EB%B3%B4%EC%B2%98%EB%A6%AC%EA%B8%B0%EC%82%AC-3.-DB-%EA%B5%AC%EC%B6%95</link>
            <guid>https://velog.io/@younjin_02/%EC%A0%95%EB%B3%B4%EC%B2%98%EB%A6%AC%EA%B8%B0%EC%82%AC-3.-DB-%EA%B5%AC%EC%B6%95</guid>
            <pubDate>Mon, 23 Feb 2026 17:02:55 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/younjin_02/post/92f43d18-f2b5-4944-89fb-d8072d55b677/image.png" alt=""></p>
<p>3과목은 데이터베이스 구축 관련한 내용이다. 안그래도 최근 프로젝트하면서 db 세팅하는데 2학년때 배웠던 전공과목이라 개념이 잘 생각이 안났었다.. 
위 이미지 기준으로 1, 2, 3장에서 많이 출제되니, 이 부분에 집중하자. 4장은 조금 난이도가 있고, 5과목은 아무래도 중요도가 많이 덜한편이다. <del>정리힘들다..</del></p>
<h2 id="1-db-설계-단계">1. DB 설계 단계</h2>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/49803a1d-ec77-47a9-b166-d0f21173aa98/image.png" alt=""></p>
<h4 id="1-1-개념적-설계정보-모델링-개념화">1-1. 개념적 설계(정보 모델링, 개념화)</h4>
<blockquote>
<p>제일 첫 단계로, <strong>현실 세계의 인식을 추상적인 개념(ER모델 등) 으로 표현하는 과정</strong>이다.</p>
</blockquote>
<p>중요한 점은, 2과목에서 다뤘던 <strong>&#39;개념 스키마&#39;</strong> 모델링과 <strong>&#39;트랜잭션 모델링&#39;</strong>을 병행 수행한다. DB를 만들기 전에 전체 구조를 그림(<strong>ERD</strong>)으로 그리는 단계라고 생각하면 된다.</p>
<h4 id="1-2-논리적-설계데이터-모델링">1-2. 논리적 설계(데이터 모델링)</h4>
<blockquote>
<p>개념 모델을 <strong>특정 DBMS가 지원하는 논리적 구조로 &#39;변환(mapping)&#39;</strong>하는 과정이다.</p>
</blockquote>
<p>논리적 자료 구조를 생성하고, <strong>트랜잭션 인터페이스를 설계</strong>하는 단계이다. 기존에 만들어뒀던 개념 스키마를 평가 및 정제한다. 쉽게 말하면, 앞 단계에서 그려뒀던 <strong>ERD를 테이블 구조로 변환</strong>하는 단계이다. 이 단계에서 테이블을 생성하고, 속성 및 정규화, 기본키/외래키를 결정한다. </p>
<h4 id="1-3-물리적-설계">1-3. 물리적 설계</h4>
<blockquote>
<p><strong>논리적 구조로 표현된 데이터를 실제 물리적 구조의 데이터로 변환하는 과정</strong>이다.</p>
</blockquote>
<p><strong>파일 저장 구조 및 접근 경로를 결정하고, 레코드 형식 및 순서, 인덱스를 설계</strong>한다. 이 단계에서는 DB를 실제 하드디스크에 어떻게 저장할지를 결정하는 단계인 것이다. 3학년 2학기 Big Data Management에서 배웠던 인덱스, 파티션, 클러스터링 등의 과정이 이 단계이다.</p>
<p>세 단계를 정리하면 아래와 같다.</p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/b7845bac-8929-4f1e-9a5d-c01354c35b1a/image.png" alt=""></p>
<h4 id="1-4-데이터-모델에-표시할-요소">1-4. 데이터 모델에 표시할 요소</h4>
<p>이것도 시험에 출제확률 높으니 잘 알아두자. 다음 세 가지가 데이터 모델에 꼭 들어간다.</p>
<ul>
<li>구조: 개체 및 타입 간의 관계로, 테이블 구조 및 관계 구조를 말한다.</li>
<li>연산: 실제 데이터를 처리하는 작업 명세를 말하며, DB 조작 도구다. 예를 들면 SELECT, INSERT, DELETE가 다음과 같다.</li>
<li>제약 조건(Constraints): 저장될 수 있는 실제 데이터의 논리적인 제한을 의마한다. 무결성 제약 및 데이터의 정확성을 유지한다. 주로 기본키, 외래키, 도메인에 대한 제약이 있다.</li>
</ul>
<h4 id="1-5-e-r-다이어그램">1-5. E-R 다이어그램</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/d31c2548-8e8c-4100-be63-7c5f5c89f2e0/image.png" alt=""></p>
<p>개발전 API 명세서와 더불어 꼭 필요한 문서다. 주요 구성 요소를 알아두자.
<img src="https://velog.velcdn.com/images/younjin_02/post/9b1b342c-c27f-47bd-8476-59a7fa10eb1a/image.png" alt=""></p>
<p>추가로 이것도 알아두자.</p>
<ul>
<li>밑줄 친 속성: 기본키</li>
<li>이중 사각형: 약한 개체</li>
<li>이중 마름모: 식별 관계</li>
</ul>
<h2 id="2-relation">2. Relation</h2>
<h4 id="2-1-테이블-구조">2-1. 테이블 구조</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/5f329ef4-7d83-47c1-b627-c52d7a82817d/image.png" alt=""></p>
<ol>
<li><p><strong>튜플</strong>
릴레이션을 구성하는 &#39;row&#39;를 말한다. 참고로 <strong>&#39;튜플 수 = 카디널리티(Cardinality)&#39;</strong>다.</p>
</li>
<li><p><strong>속성(Attribute)</strong>
릴레이션의 &#39;열&#39;로, db를 구성하는 가장 작은 논리적 단위다. <strong>&#39;속성의 수 = Degree(차수)&#39;</strong>인 것을 외워두자.</p>
</li>
<li><p><strong>도메인</strong>
&#39;하나의 속성이 가질 수 있는 값의 집합&#39;이다. 같은 타입의 <strong>원자값(더 이상 쪼갤 수 없는 값)</strong>들의 집합이다. 예를 들면, 성별 도메인은 {남, 여}, 학년 도메인은 {1,2,3,4} 이렇게 될 수 있다.</p>
</li>
</ol>
<h4 id="2-2-relation의-특징">2-2. Relation의 특징</h4>
<p>그림으로도 확인할 수 있듯이, relatioin=tabled이다. 테이블의 특징 및 규칙을 외워두자.</p>
<ul>
<li>튜플은 중복 불가하다.(행 중복 불가)</li>
<li>튜플 사이에는 순서가 없다.</li>
<li>속성의 명칭은 유일해야 한다(같은 이름의 열은 존재할 수 없다)</li>
<li>속성의 값은 원자값으로, 더 이상 분해할 수 없어야 한다.</li>
</ul>
<h4 id="2-3-key-종류-★★★">2-3. Key 종류 ★★★</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/79a0165b-d87b-44d5-8327-525f62be5867/image.png" alt=""></p>
<p>다양한 키 종류를 다 확실히 알고, 헷갈리지 않게 잘 정리해놓자.</p>
<ol>
<li><p><strong>후보키 (Candidate Key)</strong>
튜플을 유일하게 식별할 수 있는 속성의 부분집합으로, 중요한 건 &#39;유일성&#39;과 &#39;최소성&#39;을 만족해야 한다는 점이다. </p>
</li>
<li><p><strong>기본키 (Primary Key)</strong>
후보키 중에서 선택된 main key로, <strong>중복된 값을 가질 수 없으며, NULL값을 가질 수도 없다.</strong></p>
</li>
<li><p><strong>대체키 (Alternate Key)</strong>
후보키 중 기본키로 선택되지 않은 나머지 키들이며, 보조키라고도 한다.</p>
</li>
<li><p><strong>슈퍼키 (Super Key)</strong>
유일성은 만족하지만, <strong>&#39;최소성&#39;은 만족하지 않는 키</strong>를 말한다. 즉, 후보키에 다른 속성을 추가한 것이다. 예를 들면, 학번이 후보키라고 하면, (학번, 이름)은 슈퍼키가 된다.</p>
</li>
<li><p><strong>외래키 (Foreign Key)</strong>
다른 릴레이션의 기본키를 참조하는 속성 또는 속성들의 부분집합을 의미하며, 중요한 건 동일 도메인이어야 한다.</p>
</li>
</ol>
<p>헷갈리니까 다음 아래표도 참고해서 기억하자.</p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/9ea0c65c-3df6-44c4-aa97-083e521d359e/image.png" alt=""></p>
<h4 id="2-3-무결성integrity">2-3. 무결성(Integrity)</h4>
<blockquote>
<p><strong>무결성이란, 데이터의 정확성 및 일관성을 의미한다.</strong></p>
</blockquote>
<ol>
<li><p><strong>개체 무결성(Entity Integrity)</strong>: &#39;기본키&#39;는 반드시 값을 가져야 하며(NULL 불가), 중복이 불가능하다는 개념이다.</p>
</li>
<li><p><strong>참조 무결성 (Referential Integrity)</strong>: 외래키 값은 NULL이거나, 참조 릴레이션의 기본 키 값과 동일해야 한다. 즉, 참조를 할거면 다른 테이블의 기본 키를 참조하거나, 아예 참조를 하지 않게 값을 가지지 말아야 한다는 뜻이다.</p>
</li>
<li><p><strong>도메인 무결성</strong>: 속성 값은 정의된 도메인(타입·범위) 안에 있어야 한다.</p>
</li>
<li><p><strong>사용자 정의 무결성</strong>: 업무 규칙에 따라 사용자가 직접 정의한 제약이다.</p>
</li>
</ol>
<h4 id="2-4-관계-대수">2-4. 관계 대수</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/75e57486-fe44-4867-9885-bcf5e6adeffd/image.png" alt=""></p>
<blockquote>
<p>관계 대수란, <strong>원하는 정보를 얻기 위해
어떤 연산을 어떤 순서로 수행할지를 기술하는 절차적 언어</strong>이다.</p>
</blockquote>
<p>&#39;절차적 언어&#39;라는 것이 포인트며(외워두자 이렇게), 연산 순서를 명시한다. 위 표에서 SELECT, PROJECT, JOIN이 중요하니 기호를 잘 외워야한다.</p>
<ol>
<li><strong>순수 관계 연산자</strong>
다음과 같이 4개가 있다.</li>
</ol>
<ul>
<li><p><strong>SELECT(선택 연산)</strong>: 기호는 <strong>σ</strong>, 조건을 만족하는 튜플(행) 만 선택해서 새로운 릴레이션을 만드는 연산이다.</p>
<pre><code class="language-sql">SELECT * FROM Student WHERE 학년=3;
→ σ 학년=3 (Student)</code></pre>
</li>
<li><p><strong>PROJECT(투영 연산)</strong>: 기호는 <strong>π</strong>, 지정된 속성(열)만 중복을 제거해서 추출하는 연산이다.</p>
</li>
</ul>
<pre><code class="language-sql">SELECT 이름, 학과 FROM Student;
→ π 이름,학과 (Student)</code></pre>
<ul>
<li><p><strong>JOIN</strong>: 기호는 <strong>⋈</strong>, 공통 속성을 기준으로 두 릴레이션 결합해 새로운 릴레이션을 만드는 연산이다.</p>
</li>
<li><p><strong>DIVISION</strong>: 기호는 <strong>÷</strong>, 두 개의 릴레이션 R(X)와 S(Y)가 있을 때, <strong>R의 속성이 S의 속성값을 모두 가진 튜플에서 S가 가진 속성을 제외한 속성만을 구하는 연산</strong>이다. </p>
</li>
</ul>
<pre><code>수강(학생, 과목)
필수과목(과목)

→ 모든 필수과목을 다 수강한 학생 찾기</code></pre><ol start="2">
<li><strong>일반 집합 연산자</strong>
<img src="https://velog.velcdn.com/images/younjin_02/post/c68a8b7c-f9e3-41f7-aec4-1d331d65529b/image.png" alt=""></li>
</ol>
<p>여기서 교차곱(★★)이 중요하니, 살펴보자. 교차곱은 <strong>두 릴레이션의 모든 가능한 튜플 조합을 구하는 연산</strong>이다.</p>
<ul>
<li>교차곱의 차수(Degree)는 두 릴레이션의 차수를 더한 것과 같다.</li>
<li>교차곱의 카디널리티(튜플의 수)는 두 카디널리티를 곱한 것과 같다.</li>
</ul>
<h4 id="2-5-관계-해석">2-5. 관계 해석</h4>
<blockquote>
<p>코드(Codd)가 제안한 술어해석(Predicate Calculus) 기반
<strong>비절차적</strong> 언어다.</p>
</blockquote>
<p><del>뭔소리냐..</del> 처음 들어보는 개념이다. 일단 주요 기호부터 외워두자. 관계 대수와 달리 &#39;비절자척&#39;언어이며, 조건만 기술한다는 특징이 있다.
<img src="https://velog.velcdn.com/images/younjin_02/post/9982af06-9c35-47f7-9df1-42c982b0431c/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/f1c6b7fa-d39c-4e9a-ba22-59289c450a0a/image.png" alt=""></p>
<h2 id="3-정규화normalization">3. 정규화(Normalization)</h2>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/fe34044e-17cc-46e8-a880-bed271cf5640/image.png" alt=""></p>
<blockquote>
<p>함수적 종속 이론을 이용하여 <strong>잘못 설계된 릴레이션을 더 작은 릴레이션으로 분해하는 과정</strong></p>
</blockquote>
<p>정규화는 너무 유명한 만큼 시험에 꼭 나온다. 이 정규화의 정의 또한 그대로 기억해두자. </p>
<p>개념-논리-물리 설계 단계에서 <strong>&#39;논리적 설계 단계&#39;</strong>에서 이루어지며, 데이터의 중복을 제거해 <strong>이상(Anomaly)를 방지</strong>하며, <strong>저장 공간을 최소화</strong>하는데에 목적이 있다.</p>
<h4 id="3-1-이상anomaly">3-1. 이상(Anomaly)</h4>
<blockquote>
<p>정규화를 하지 않으면 발생하는, <strong>DB 내에 데이터들의 불필요하게 중복되어 릴레이션 조작시 곤란한 현상이 발생하는 것</strong>을 의미한다.</p>
</blockquote>
<ul>
<li><strong>삽입 이상</strong>: 데이터를 삽입할 때 원하지 않은 값도 함께 삽입되는 현상이다.</li>
<li><strong>삭제 이상</strong>: 튜플을 삭제할 때 상관없는 값들도 함께 삭제되는 현상이다.</li>
<li><strong>갱신 이상</strong>: 튜플에 있는 속성값을 갱신할 때 일부 튜플의 정보만 갱신되어 모순 생기는 현상으로, 같은 정보를 여러 군데 수정해야 하는 번거로움이 생긴다.</li>
</ul>
<h4 id="3-2-정규화-과정">3-2. 정규화 과정</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/8c254358-3152-4cf2-8993-f01ee94c2a96/image.png" alt=""></p>
<ol>
<li><p><strong>1NF(제 1 정규형)</strong>: 도메인이 원자값이 되는, 반복 속성을 제거하는 단계다.</p>
</li>
<li><p><strong>2NF(제2정규형)</strong>: 1NF를 만족하는 상황에서, &#39;부분적&#39;으로 함수 종속을 제거한다.</p>
</li>
<li><p><strong>3NF(제3정규형)</strong>: 2NF를 만족하는 상황에서, &#39;이행적 함수 종속을 제거&#39;한다. <strong>이행적 종속 관계란, A → B 이고 B → C 일 때, A → C 를 만족하는 관계</strong>를 말한다. </p>
</li>
<li><p><strong>BCNF (보이스-코드 정규형)</strong>: 모든 결정자가 후보키가 된다. 결정자이면서 후보키가 아닌 것을 제거한다. 3과 4 사이에 있는것이 포인트다.</p>
</li>
</ol>
<p>*결정자: 함수적 종속 X → Y 에서 Y를 결정하는 속성 X</p>
<ol start="4">
<li><strong>4NF</strong>: 다치 종속을 제거한다.</li>
</ol>
<p><strong>*다치 종속</strong>: 하나의 속성 X 값에 대해 여러 개의 Y 값이 독립적으로 존재하는 경우
ex.
A 하나에
B가 여러 개
C도 여러 개
근데 B와 C는 서로 관련 없음</p>
<ol start="5">
<li><strong>5NF</strong>: 조인 종속을 제거한다. </li>
</ol>
<h4 id="3-3-종속dependency">3-3. 종속(Dependency)</h4>
<ul>
<li><strong>함수적 종속 (Functional Dependency)</strong><blockquote>
<p>어떤 속성 X의 값이 정해지면 다른 속성 Y의 값이 <strong>&#39;하나&#39;</strong>로 결정되는 관계, 즉 X → Y인 관계다.</p>
</blockquote>
</li>
</ul>
<p>예를 들면, &#39;학번 → 이름&#39;의 관계가 있다고 한다면, 학번이 같으면 이름은 반드시 같아진다. 
<img src="https://velog.velcdn.com/images/younjin_02/post/b972e0fb-577d-4bbb-afc7-74de317adbdc/image.png" alt=""></p>
<ul>
<li>*<em>이행적 종속(Transitive Dependency) *</em><blockquote>
<p><strong>A → B
B → C
이면
A → C</strong></p>
</blockquote>
</li>
</ul>
<p>제거 단계는 &#39;3단계&#39;다.</p>
<h4 id="3-4-반정규화denormalization">3-4. 반정규화(Denormalization)</h4>
<blockquote>
<p>성능 향상을 위해 <strong>의도적으로 정규화 원칙을 위반(의도적 중복 허용)</strong>하는 것</p>
</blockquote>
<p>정규화를 많이 하게 되면, JOIN이 많아진다. 그렇게 되면 속도는 느려지게 되어 일부러 합치는 경우가 반정규화다. 테이블을 통합하거나, 중복 속성을 추가하거나, 중복 테이블을 추가하는 식으로 이루어진다.</p>
<h4 id="3-5-시스템-카탈로그">3-5. 시스템 카탈로그</h4>
<blockquote>
<p>DBMS가 <strong>데이터베이스의 구조 정보를 저장하는 시스템 테이블</strong></p>
</blockquote>
<p>메타데이터를 저장하며, 테이블 정보, 속성 정보, 사용자 정보 인덱스 정보가 포함된다. 시스템 그 자체에 관련이 있는 다양한 객체에 관한 정보를 포함하는 시스템 DB이다. </p>
<p><strong>중요한건 사용자가 이 내용을 검색할 수는 있어도, 갱신하진 못한다.</strong> </p>
<h2 id="4-transaction">4. Transaction</h2>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/5f915c2b-89cf-478f-99d7-5578c9ee7f3a/image.png" alt=""></p>
<blockquote>
<p>트랜잭션이란, <strong>데이터베이스의 상태를 변화시키는 하나의 논리적 기능 수행을 위한 &#39;작업 단위&#39; 또는 &#39;한 번에 모두 수행되어야 할 일련의 연산&#39;</strong>다.</p>
</blockquote>
<p>그림과 같이, 중간에 끊기면 안되는 <strong>&#39;한 번에 이루어져야 하는 실행 단위&#39;</strong>이다. &#39;모두 수행&#39;하거나 아니면 &#39;전혀 수행되지 않아야 한다.&#39;</p>
<p>실제로 작성자가 최근에 DBeaver로 프로젝트 DB 스키마를 지속적으로 수정하면서 트랜잭션을 rollback 하라는 오류가 제일 많이 떴었는데, 바로 이 트랜잭션의 중요한 원리 때문이다.</p>
<h4 id="4-1-트랜잭션의-상태">4-1. 트랜잭션의 상태</h4>
<ul>
<li><strong>활동(Active)</strong>: 트랜잭션이 실행중인 상태</li>
<li><strong>실패(Failed)</strong>: 실행에 오류가 발생하여 중단된 상태</li>
<li><strong>철회(Aborted)</strong>: 비정상적으로 종료되어 롤백 연산 수행한 상태</li>
<li><strong>★부분완료(Partially Committed)</strong>: 모두 성공적으로 실행한 후 커밋 연산이 실행되기 직전 상태</li>
<li><strong>완료(Committed)</strong>: 모두 성공적으로 실행한 후 커밋 연산 실행 후의 상태</li>
</ul>
<p>이렇게 5가지 상태가 있다. 상태 흐름은 다음 2가지가 있다.
<img src="https://velog.velcdn.com/images/younjin_02/post/e4dfaecf-3736-4bf1-b169-a5c7c100da91/image.png" alt=""></p>
<h4 id="4-2-트랜잭션-특성-acid-★★★">4-2. 트랜잭션 특성 (ACID) ★★★</h4>
<p>시험 100% 출제다.</p>
<ul>
<li><p><strong>원자성(Atomicity)</strong>: 트랜잭션의 연산은 DB에 모두 반영이 되도록 완료(Commit)하거나 전혀 반영되지 않도록 복구(Rollback) 되어야 한다.</p>
</li>
<li><p><strong>일관성(Consistency)</strong>: 트랜잭션이 정상적으로 성공하면 일관성 있는 데이터베이스 상태로 변환한다.</p>
</li>
<li><p><strong>독립성(Isolation)</strong>: 둘 이상의 트랜잭션이 동시에 병행 실행중에 <strong>다른 트랜잭션 연산이 끼어들 수 없음</strong></p>
</li>
<li><p><strong>영속성(Durability)</strong>: 성공적으로 완료된 트랜잭션 결과는 시스템이 고장나더라도 영구적으로 반영되어야 한다. </p>
</li>
</ul>
<p>TCL 명령어들도 기억해두자.</p>
<pre><code class="language-sql">COMMIT;           -- 트랜잭션 확정
ROLLBACK;         -- 트랜잭션 취소 (전체 또는 SAVEPOINT까지)
SAVEPOINT sp1;    -- 중간 저장점 설정
ROLLBACK TO sp1;  -- sp1 이후만 취소</code></pre>
<h2 id="5-index--view">5. Index &amp; View</h2>
<h4 id="5-1-index">5-1. Index</h4>
<blockquote>
<p>데이터 레코드를 <strong>빠르게 접근하기 위한 &lt;키 값, 포인터&gt; 쌍으로 구성</strong>된 자료 구조</p>
</blockquote>
<p>쉽게 말하면 책의 &#39;목차&#39;같이 빠르게 찾기 위한 구조로, DDL(데이터 정의어)로 생성/변경/삭제가 가능하다.</p>
<h4 id="5-2-view-★★">5-2. View ★★</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/6a19a49e-811c-43e5-8ff0-003558ffe2d9/image.png" alt=""></p>
<blockquote>
<p>기본 테이블로부터 유도된 가상 테이블</p>
</blockquote>
<p><strong>실제로 데이터를 저장하지 않고, 이름만 존재하며, 물리적으로 존재하지 않는다.</strong> 사용하는 이유는 보안(특정 열만 보여줄 수 있음)적인 이유도 있고, 복잡한 쿼리를 단순화할 수 있고, 사용자에게도 맞춤 제공이 가능하다. </p>
<p>인덱스와 달리 <strong>삽입, 삭제, 갱신에 제약이 있고, 독립적인 인덱스 생성 불가</strong>하다. 생성은 CREAE, 삭제는 DROP으로 한다.</p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/c4051818-fcf7-454a-b504-b45cc713a82d/image.png" alt=""></p>
<h2 id="6-partition--분산-db">6. Partition &amp; 분산 DB</h2>
<h4 id="6-1-partition">6-1. Partition</h4>
<blockquote>
<p><strong>테이블을 여러 조각으로 나누는 것</strong></p>
</blockquote>
<p>목적은 데이터의 규모가 커졌을 경우에 대용량 처리를 효율적으로 하기 위함과 동시에 이를 통해 성능을 향상시키는데 목적이 있다. <del>이거 프로젝트 PTSD..</del> </p>
<p>파티션 종류는 다음과 같다.</p>
<ol>
<li><p><strong>범위 분할 (Range Partitioning)</strong>
지정한 열의 값 기준 분할 방식이다. Ex. 날짜별로 1<del>3월/ 4</del>6월 분할</p>
</li>
<li><p><strong>해시 분할 (Hash Partitioning)</strong>
해시 함수 결과값에 따라 데이터를 분할한다.</p>
</li>
<li><p><strong>조합 분할 (Composite Partitioning)</strong>
범위 분할 후 해시 분할을 적용한 방식이다.</p>
</li>
<li><p><strong>목록 분할 (List Partitioning)</strong>
특정 값을 저장한 목록을 기준으로 한 분할 방식이다. Ex. 지역= 서울/부산/대구</p>
</li>
<li><p><strong>라운드 로빈 분할 (Round Robin)</strong>
순차적으로 균등하게 분배하는 방식이다.</p>
</li>
</ol>
<h4 id="6-2-분산-db">6-2. 분산 DB</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/aeb10f11-2ca5-4730-ba94-9d61327b5e53/image.png" alt=""></p>
<blockquote>
<p>논리적으로는 하나지만, <strong>물리적으로는 네트워크를 통해 연결된 여러 개의 사이트에 분산되어 있는 DB</strong>를 말한다.</p>
</blockquote>
<p>분산되어 있기에 설계 및 개발이 어려우며, <strong>분산 처리기, 분산 DB, 중요한 통신 네트워크</strong>가 구성 요소다.</p>
<p>분산 DB의 목표는 다음 4가지다. 거의 시험에 그대로 나온다.</p>
<ul>
<li><strong>위치 투명성</strong>: Access하려는 DB의 실제 위치를 몰라도, 명칭만으로 접근이 가능하다.</li>
<li><strong>중복 투명성</strong>: 데이터가 중복되어 여러 군데 분산되어 있어도, 사용자는 하나처럼 사용한다. </li>
<li><strong>병행 투명성</strong>: 동시에 다수의 트랜잭션이 발생해도 해당 트랜잭션의 결과에는 영향을 받지 않는다.</li>
<li><strong>장애 투명성</strong>: 여러 장애에도 트랜잭션을 정확히 처리할 수 있게 된다.</li>
</ul>
<h2 id="7-db-보안">7. DB 보안</h2>
<h4 id="7-1-암호화--복호화">7-1. 암호화 &amp; 복호화</h4>
<ul>
<li><strong>암호화 (Encryption)</strong>: 정보 보호를 목적으로 평문 → 암호문이 이루어지는 과정이다.</li>
<li><strong>복호화 (Decryption)</strong>: 암호문을 원래의 평문으로 바꾸는 과정이다.</li>
</ul>
<h4 id="7-2-접근-통제-기술">7-2. 접근 통제 기술</h4>
<ul>
<li><strong>DAC(임의 접근통제)</strong>: 데이터에 접근하는 사용자의 <strong>신원 기준</strong>에 따라 권한을 부여하는 방식</li>
<li><strong>MAC(강제 접근통제)</strong>: 주체와 객체의 <strong>등급</strong>을 비교해 접근 권한을 부여하는 방식으로, 군대 기밀 등급 같은 느낌이다.</li>
<li><strong>RBAC(역할 기반 접근통제)</strong>: 사용자 <strong>역할</strong> 기준으로, 권한을 역할에 부여하는 방식이다.</li>
</ul>
<h4 id="벨-라파둘라-모델-bell-lapadula-model">*벨-라파둘라 모델 (Bell-LaPadula Model)</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/3125b1fe-4193-4f8a-b9d7-2020bf8ac294/image.png" alt=""></p>
<p>군대의 보안 레벨처럼 정보의 <strong>&#39;기밀성&#39;에 따라 상하 관계가 구분된 정보를 보호</strong>하기 위해 사용하는 접근제어 모델로, 보안 취급자의 등급을 기준으로 Read와 Write 권한이 제한된다.</p>
<p>핵심 규칙은 다음과 같다.</p>
<ul>
<li><strong>No Read Up (상위 읽기 금지)</strong>: 자기 보다 높은 등급 정보 읽기 불가</li>
<li><strong>No Write Down (하위 쓰기 금지)</strong>: 자기 보다 낮은 등급에 쓰기 불가</li>
</ul>
<h2 id="8-storage">8. Storage</h2>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/3963495a-1451-4d2f-8b43-db6ceb0c2668/image.png" alt=""></p>
<h4 id="8-1-dasdirect-attached-storage">8-1. DAS(Direct Attached Storage)</h4>
<blockquote>
<p>서버와 저장장치를 <strong>전용 케이블로 직접 연결</strong>하는 것으로, 말 그대로 컴퓨터를 외장하드에 직접 연결하는 방식이다.</p>
</blockquote>
<p>네트워크를 사용하지 않고, 설치도 간단하며 비용도 저렴한 편이지만 확장성은 당연히 떨어진다.</p>
<h4 id="8-2-sanstorage-area-network">8-2. SAN(Storage Area Network)</h4>
<blockquote>
<p>서버와 저장장치를 연결하는 <strong>전용 네트워크를 구성</strong>하는 방식이다. </p>
</blockquote>
<p><strong>고속</strong>으로 데이터 처리가 가능하며, <strong>확장성</strong>도 좋다. 주로 기업용 대규모 환경에서 사용된다.</p>
<h4 id="8-3-nasnetwork-attached-storage">8-3. NAS(Network Attached Storage)</h4>
<blockquote>
<p>네트워크를 통해 여러 사용자가 <strong>파일 단위</strong>로 접근하는 저장 장치</p>
</blockquote>
<p>공유 폴더 전용 저장장치라고 생각하면 편하다. </p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/e19c5c6d-b0bb-4d72-ba81-1f6db632206b/image.png" alt=""></p>
<h2 id="9-sql">9. SQL</h2>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/17118310-8ff5-4a32-82a9-68f0a4b8c465/image.png" alt=""></p>
<h4 id="9-1-ddl-data-definition-language">9-1. DDL (Data Definition Language)</h4>
<blockquote>
<p>데이터베이스 구조를 <strong>정의, 변경, 삭제</strong>하는 언어</p>
</blockquote>
<p>스키마, 테이블, 뷰, 인덱스, 도메인 모두 생성, 변경, 삭제를 하는 언어다. 다음 세 가지가 주를 이룬다.</p>
<p>&#39;구조&#39;를 변경하며, <strong>자동으로 COMMIT</strong>이 발생해, <strong>ROLLBACK이 불가</strong>하다.</p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/0a1d5c74-6126-475e-82bf-8229083c5294/image.png" alt=""></p>
<p>ALTER은 &#39;정의&#39;를 변경하는 명령어다. DML이랑 혼동을 주의하도록 하자.</p>
<ul>
<li><p><strong>CREATE TABLE</strong>: 테이블 생성</p>
<pre><code class="language-sql">CREATE TABLE 테이블명 (
  속성명 데이터타입 [DEFAULT 기본값] [NOT NULL],
  ...
  PRIMARY KEY (속성명),
  UNIQUE (속성명),
  FOREIGN KEY (속성명)
      REFERENCES 참조테이블(속성명)
      ON DELETE 옵션
      ON UPDATE 옵션,
  CONSTRAINT 제약조건명 CHECK (조건식)
);</code></pre>
<p>기본키는 중복, NULL 다 안된다는 사실을 또또 기억하자(개체 무결성). UNIQUE는 중복만 안되고 NULL은 가능하다.</p>
</li>
<li><p><strong>ALTER TABLE</strong>: 테이블 구조 변경</p>
</li>
</ul>
<pre><code class="language-sql">ALTER TABLE 테이블명 ADD 속성명 데이터타입; //열 추가

ALTER TABLE 테이블명 ALTER 속성명 SET DEFAULT 값; //열 수정

ALTER TABLE 테이블명 DROP COLUMN 속성명; // 열 삭제</code></pre>
<ul>
<li><strong>DROP TABLE</strong>: 테이블 삭제<pre><code class="language-sql">DROP TABLE 테이블명 [CASCADE | RESTRICT];</code></pre>
<img src="https://velog.velcdn.com/images/younjin_02/post/5089a765-c235-4593-a28a-4a23e8d6da6b/image.png" alt=""></li>
</ul>
<p>위의 두 가지는 실제로도 자주 쓰는 명령어다.</p>
<h4 id="9-2-dml-data-manipulation-language">9-2. DML (Data Manipulation Language)</h4>
<blockquote>
<p>저장된 데이터를 <strong>조회, 삽입, 수정, 삭제</strong>하는 언어</p>
</blockquote>
<p>즉, 질의어를 통해 <strong>저장된 데이터를 실질적으로 처리</strong>하는 데 사용되는 언어다. </p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/0349a100-b7fb-4ee8-bcc9-23bc42f30aa1/image.png" alt=""></p>
<p>위의 4가지 다 <strong>&#39;튜플&#39;</strong>을 검색하고, 삽입하고, 변경하고, 삭제하는 것이다. 트랜잭션의 영향을 받는다.</p>
<ul>
<li><p><strong>INSERT</strong>: 테이블에 새로운 튜플(행) 추가</p>
<pre><code class="language-sql">INSERT INTO 테이블명 (속성1, 속성2, ...)
VALUES (값1, 값2, ...);</code></pre>
</li>
<li><p><em>VALUES는 필수*</em>이며, 속성 생략 시 테이블 정의 순서대로 입력한다. 속성 생략 시 테이블 정의 순서대로 입력한다.</p>
</li>
<li><p><strong>DELETE</strong>: 조건에 맞는 행 삭제</p>
<pre><code class="language-sql">DELETE FROM 테이블명
WHERE 조건;</code></pre>
<p>WHERE을 생략하면 전체가 삭제된다. </p>
</li>
<li><p><strong>UPDATE</strong>: 기존 데이터 수정</p>
<pre><code class="language-sql">UPDATE 테이블명
SET 속성 = 값
WHERE 조건;</code></pre>
<p>UPDATE, SET, WHERE 순서다.</p>
</li>
<li><p><strong>SELECT</strong>: 특정 조건을 가진 열을 조회</p>
<pre><code class="language-sql">SELECT [DISTINCT] 속성명
FROM 테이블명
WHERE 조건
GROUP BY 속성명
HAVING 조건
ORDER BY 속성명 [ASC | DESC];</code></pre>
<p>이 순서 그대로 외워두도록 하자. <strong>DISTINCT는 중복을 제거</strong>하는 문(중복된 튜플 중 첫 번째만 검색)이고, <strong>GROUP BY는 그룹화할 속성</strong>을 지정한다. <strong>HAVING은 그룹화의 조건</strong>을 의미하고, <strong>ORDERED BY는 정렬(ASC;오름차순 DESC;내림차순)</strong>이다.</p>
</li>
</ul>
<p>DML(데이터 조작문) 4가지는 정리하면 다음과 같다.
<img src="https://velog.velcdn.com/images/younjin_02/post/01279581-ef4b-4cbf-bd87-152a364ffc84/image.png" alt=""></p>
<h4 id="9-3-dcl-data-control-language">9-3. DCL (Data Control Language)</h4>
<blockquote>
<p>데이터의 <strong>보안, 무결성, 권한 제어</strong>에 사용하는 언어</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/df291788-0806-4406-bbfd-949e361e8327/image.png" alt=""></p>
<p>사용 예시는 아래와 같다.</p>
<pre><code class="language-sql">-- 권한 부여
GRANT SELECT, INSERT ON 학생 TO 홍길동;
GRANT ALL PRIVILEGES ON 학생 TO 홍길동 WITH GRANT OPTION;
-- WITH GRANT OPTION: 홍길동이 다른 사용자에게 재부여 가능

-- 권한 회수
REVOKE SELECT ON 학생 FROM 홍길동;
REVOKE SELECT ON 학생 FROM 홍길동 CASCADE;
-- CASCADE: 홍길동이 부여한 권한까지 연쇄 회수</code></pre>
<h4 id="9-4-조건-연산자-및-이외의-연산자들">9-4. 조건 연산자 및 이외의 연산자들</h4>
<ol>
<li><strong>조건 연산자 LIKE</strong>: &#39;문자&#39;의 패턴을 검색한다.<pre><code class="language-sql">WHERE 이름 LIKE &#39;김%&#39;; //김으로 시작
WHERE 이름 LIKE &#39;_철수&#39;; //한글자 + 철수</code></pre>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/011e4822-008c-4df8-84a0-955fbaa39f1d/image.png" alt=""></p>
<ol start="2">
<li><p><strong>BETWEEN</strong>: 두 값 사이 범위 검색</p>
<pre><code class="language-sql">WHERE 속성 BETWEEN 값1 AND 값2;</code></pre>
</li>
<li><p><strong>그룹 함수 (집계 함수)</strong>: GROUP BY 절에서 그룹별로 속성의 값을 집계할 때 사용한다. 종류는 다음과 같다.</p>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/83cf142e-84a4-4cb2-8151-bfdbcdfea7b9/image.png" alt=""></p>
<p>COUNT(*)는 NULL 포함, COUNT(컬럼)은 NULL 제외이다.</p>
<ol start="4">
<li><strong>집합 연산자</strong></li>
</ol>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/ba5b1dfa-535d-4024-ba65-3164f68c9755/image.png" alt=""></p>
<h4 id="trigger">*Trigger</h4>
<blockquote>
<p>INSERT, UPDATE, DELETE <strong>이벤트 발생 시 자동 실행되는 절차형 SQL</strong></p>
</blockquote>
<p>&#39;자동 실행&#39;되며 &#39;이벤트 기반&#39;이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[정보처리기사] 2. SW 개발]]></title>
            <link>https://velog.io/@younjin_02/%EC%A0%95%EB%B3%B4%EC%B2%98%EB%A6%AC%EA%B8%B0%EC%82%AC-2.-SW-%EA%B0%9C%EB%B0%9C</link>
            <guid>https://velog.io/@younjin_02/%EC%A0%95%EB%B3%B4%EC%B2%98%EB%A6%AC%EA%B8%B0%EC%82%AC-2.-SW-%EA%B0%9C%EB%B0%9C</guid>
            <pubDate>Sun, 08 Feb 2026 11:14:13 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/younjin_02/post/e3776bed-d99d-4577-9037-61504bbe9d18/image.png" alt=""></p>
<p>2과목인 SW 개발은 말 그대로 서비스를 &#39;개발&#39;하는데 필요한 지식에 초점이 맞춰져 있다. 1장에 여러 자료 구조 및 알고리즘(<del>트리, 정렬 등등.. 하기 싫다;</del>)과 개발 후 패키징과 테스트 관리, 유지 보수까지의 내용이 주를 이룬다. </p>
<p>작성자의 경우 자료 구조와 알고리즘은 2학년 2학기와 3학년 1학기에 각각 수강했으나, 개인적으로 교수님이 강의력이 여러모로 꽤 상당히 무지 엄청나게 매우 안좋았던 관계로 이 두 과목에 뭔지 모를 벽이 느껴지는 느낌이 있다. 이번 기회에 제대로 공부해서 이 느낌을 없애보도록 하자.</p>
<h2 id="1-자료-구조">1. 자료 구조</h2>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/d7aeb389-cb17-429b-9a63-2d5cd1c0cdc2/image.png" alt=""></p>
<p>자료 구조의 종류는 매우 다양하지만, 그 중에 중요한 구조 위주로 정리해보겠다. 선형 구조와 비선형 구조로 나누어서 정리해보자.</p>
<h3 id="1-1-선형-구조">1-1. 선형 구조</h3>
<p><strong>1. 배열(Array)</strong>
<img src="https://velog.velcdn.com/images/younjin_02/post/bdd61286-8e63-44ee-88a2-2f1a620e379c/image.png" alt="">
배열은 <strong>순차 리스트</strong>라는 용어로도 자주 쓰이며, 가장 기본이 되는 구조라고 할 수 있다. 핵심 특징으로는, 데이터가 저장된 순서와 메모리에 적재된 실제 물리적 순서가 일치하며, <strong>빈 공간 없이 데이터가 연속</strong>되어 붙어있다.</p>
<p>배열의 대표적인 장점으로는 <strong>Index 기반으로 원하는 데이터에 빠르게 접근 가능</strong>하다는 점이다(접근 시간 복잡도: $O(1)$). 반면에 <strong>삽입/삭제가 어렵다</strong>는 단점이 존재한다. 중간에 데이터를 삽입하거나 삭제하면 전체 데이터들이 밀거나 당겨지는 데이터 이동이 발생하며, 이 부분에서 시간이 소요되는게 가장 큰 단점이며, <strong>크기 또한 고정적(최초 정의된 크기 변경 불가)이라 미리 공간을 확보해놓아야 한다</strong>. </p>
<p><strong>2. 연결 리스트(Linked List, 선형 리스트)</strong>
<img src="https://velog.velcdn.com/images/younjin_02/post/6ff81000-8ddc-4c69-927d-fd1c5cf18963/image.png" alt=""></p>
<p>앞서 언급한 배열의 단점을 해결하기 위해 나온 자료 구조다. 배열과 비교해서 특징을 잘 알아두자.</p>
<p>연결 리스트는 자료들이 메모리에 흩어져 있어도 <strong>pointer(주소)로 연결</strong>되어 있다. 이를 <strong>Node 구조[데이터 | 포인터(다음 주소)]</strong>라고 한다. 포인터는 삽입/삭제 시 배열처럼 데이터를 밀어낼 필요 없이, 포인터만 다른 데이터와 연결해주면 되기 때문에 <strong>삽입/삭제를 쉽게 작동하게 만들어준다.</strong></p>
<p>하지만 반대로 <strong>데이터에 접근하고자 하면 첫 번째부터 순서대로 접근해야 하기 때문에 접근 속도가 느리며</strong>, 포인터를 저장할 추가 공간이 필요하다는 단점이 있다.
추가로 여러 가지 연결 리스트 종류는 다음과 같다.
<img src="https://velog.velcdn.com/images/younjin_02/post/3fc81019-4e20-41ca-b2f2-e50c4d1b0651/image.png" alt=""></p>
<p><strong>3. Stack</strong>
<img src="https://velog.velcdn.com/images/younjin_02/post/b0fd9c4d-8a9f-40d0-9c65-2fb4f2fe0f35/image.png" alt=""></p>
<blockquote>
<p><strong>&quot;나중에 들어온 놈이 먼저 나간다&quot; (LIFO: Last-In First-Out)</strong></p>
</blockquote>
<p>LIFO가 stack의 대표적 특징이며, <strong>그로 인해 한쪽 끝(Top, 가장 위에 있는 데이터)에서만 삽입(push)와 삭제(pop)가 모두 일어난다. 포인터는 Top이다.</strong></p>
<ul>
<li>overflow:꽉 찬 상태에서 데이터 삽입</li>
<li>underflow: 삭제할 수 없는 상태에서 데이터 삭제</li>
</ul>
<p>stack이 와닿지 않을 수 있는데, undo(Ctrl+Z)를 생각해보자. 가장 최근에 실행한 명령어를 취소하려면, 가장 마지막에 들어온 데이터를 없애야 한다. 그렇기에 stack은 꼭 필요한 자료구조다.</p>
<p>앞서 언급한 예시 외에도, stack이 필요한 곳은 여러 군데 더 있다.</p>
<ul>
<li><p><strong>재귀 호출(Recursion)</strong>: 재귀 함수의 경우, 자기 자신을 다시 호출하는 경우이므로, 바로 이전 함수의 지역변수 + 복귀위치를 기억해야 하며, 순서 또한 가장 마지막에 호출된 함수부터 종료된다. 이건 LIFO 구조이므로 여기서 stack이 활용된다.</p>
</li>
<li><p><strong>함수 호출 후 복귀 주소 저장</strong>: 함수가 A, B, C 순서로 호출된다고 할 때 C가 끝나면 B로, B가 끝나면 A로 복귀한다. 그렇기 때문에 복귀 구조를 순서대로 기억해야 하는데, 이때 복귀 주소를 stack에 저장해둔다.</p>
</li>
<li><p><strong>postfix(후위 표기법)</strong>: 후위 표기법이란 연산자를 맨 뒤에 쓰는 수식 표현 방식이다. 보통 3+4 이런식으로 중위 표기법을 쓰는데, 후위 표기법은 &#39;3 4 +&#39; 이런식으로 표기한다. 후위 표기법은 연산을 연산자가 있을 때 가장 최근의 두 수를 pop해서 연산 후 push하기 때문에 stack이 활용된다. </p>
</li>
<li><p><strong>Interrupt 처리</strong>: 인터럽트란, CPU가 작업 중일때, 급한 일이 생기면 잠깐 stop후 처리하는 신호이다. 이러한 인터럽트는 여러번 발생될 수 있는데, 가장 최근에 발생한 인터럽트부터 복귀해야 하기 때문에 여기서 stack이 활용된다.</p>
</li>
<li><p><strong>DFS(깊이 우선 탐색)</strong>: DFS는 한 방향으로 끝까지 탐색 후 더 갈 곳이 없으면 backtracking(되돌아가기)하게 되는데, 이때 이 백트래킹이 가장 마지막에 방문한 노드부터 돌아가게 된다. 따라서 이는 LIFO 구조이며, stack(재귀)를 활용한다.</p>
</li>
</ul>
<p><strong>4. Queue</strong>
<img src="https://velog.velcdn.com/images/younjin_02/post/9a5986d8-6cb7-43d5-9e45-972206e01813/image.png" alt=""></p>
<blockquote>
<p><strong>&quot;먼저 들어온 놈이 먼저 나간다&quot; (FIFO: First-In First-Out)</strong></p>
</blockquote>
<p>Queue의 기본 구조는 양쪽 끝이 뚫려 있는 터널 형태이다. 뒤(rear)는 데이터가 들어오는 곳(삽입, Enqueue)이며, 앞(Front)는 데이터가 나가는 곳(삭제, Dequeue)이다. 큐는 쉽게 그냥 선입선출 구조를 생각하면 된다.</p>
<p>대표적인 활용 예시들은 아래와 같다.</p>
<ul>
<li><p><strong>OS의 작업 scheduling</strong>: 여러 프로세스가 순서대로 CPU 사용 요청을 했을 경우, 먼저 대기한 프로세스부터 공정하게 실행된다. 따라서 이는 일반적인 대기열 구조로, queue이다.</p>
</li>
<li><p><strong>Spooling</strong>: 여러 문서가 동시에 출력 요청을 보내면, 먼저 요청한 문서부터 출력된다.</p>
</li>
<li><p><strong>Buffer</strong>: 데이터 처리 속도 차이를 완화하기 위한 임시 공간으로, FIFO 구조를 따른다.</p>
</li>
<li><p><strong>BFS(너비 우선 탐색)</strong>: 가까운 노드부터 탐색하는 방식으로, Queue를 사용하여 보통 구현한다. 반대로 DFS는 stack.</p>
</li>
</ul>
<p>*<em>5. Deque(데크) *</em>
<img src="https://velog.velcdn.com/images/younjin_02/post/b779f0e6-4abf-4ef4-9f0c-c7bee7149b4a/image.png" alt=""></p>
<p>Double-Ended Queue로, <strong>양쪽 끝에서 삽입과 삭제가 모두 가능한 자료구조</strong>다. 삽입 삭제가 한쪽만 가능한 stack과 queue와의 명확한 차이점이다. </p>
<h3 id="1-2-비선형-구조">1-2. 비선형 구조</h3>
<blockquote>
<p><strong>데이터가 1:1로 순차적으로 연결되지 않은 구조</strong>를 의미한다.</p>
</blockquote>
<p>즉, 트리나 그래프와 같이 하나의 노드가 여러 노드와 연결 가능한 구조다.</p>
<ol>
<li><strong>트리</strong>
<img src="https://velog.velcdn.com/images/younjin_02/post/2fd57dfb-f2a8-4ea5-a466-0fafaa0d7d4d/image.png" alt=""></li>
</ol>
<p>정점(Node)과 선분(Branch)를 이용해 <strong>사이클이 생성되지 않도록 구성한 그래프의 특수한 형태</strong>를 의미한다. 주요 용어를 알아두자.</p>
<ul>
<li><strong>차수(Degree)</strong>: 각 노드에서 뻗어나온 가지 수</li>
<li><strong>단말 노드(Terminal Node) = Leaf Node</strong>: 차수가 0인, 자식이 하나도 없는 노드</li>
</ul>
<ol start="2">
<li><strong>그래프</strong>
<img src="https://velog.velcdn.com/images/younjin_02/post/64f280dd-643d-4b00-b990-40c81a2204bd/image.png" alt="">
트리와 동일하게 노드와 간선으로 구성되있지만, 사이클이 가능하다.</li>
</ol>
<p>추가로 방향 그래프와 무방향 그래프의 <strong>최대 간선수</strong>를 꼭 기억해두자.
<img src="https://velog.velcdn.com/images/younjin_02/post/581dfb07-ffbb-47a9-a670-636f9d302959/image.png" alt=""></p>
<ul>
<li>방향 그래프: n(n-1)</li>
<li>무방향 그래프: n(n-1)/2 *A-B와 B-A는 동일하므로</li>
</ul>
<h4 id="이진-트리의-운행법">*이진 트리의 운행법</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/b6e3ff1b-0fc5-44db-b4c8-a15051994532/image.png" alt=""></p>
<p>Inorder, PreOrder, PostOrder 세 가지 방법 모두 알아두자.</p>
<ol>
<li><p><strong>Inorder</strong>: Left-&gt; Root -&gt; Right 순으로, Root가 중간에 들어간다. 위의 그림으로 정리하면, HDIBEAFCG 순서가 된다.</p>
</li>
<li><p><strong>PreOrder</strong>: Root -&gt; Left -&gt; Right 순으로, 루트를 가장 먼저 방문한다. 무조건 트리에 있는 Root들을 먼저 방문하고, left-&gt;Right순이라고 생각하자. ABDHIECFG 순서다. </p>
</li>
<li><p><strong>PostOrder</strong>: Left-&gt;Right-&gt;Root 순서로, Root가 가장 마지막에 온다. HIDEBFGCA 순서다.</p>
</li>
</ol>
<h4 id="수식-표기법-infix--postfix--prefix">*수식 표기법: Infix &amp; Postfix &amp; Prefix</h4>
<p>이 부분은 좀 까다롭긴 하지만 손으로 연습해보면 편할 것 같다.</p>
<ol>
<li><p><strong>Infix -&gt; Postfix</strong>: 일반 연산 방식(infix)에서, 연산자를 해당 피연산자 두 개의 오른쪽으로 이동시키는 방식이다.
<img src="https://velog.velcdn.com/images/younjin_02/post/bcc0fb8c-bc14-4797-91c3-25b395e50266/image.png" alt=""></p>
</li>
<li><p><strong>Infix -&gt; Prefix</strong>: 위와 동일한 상황에서 연산자를 왼쪽으로 이동시킨다.
<img src="https://velog.velcdn.com/images/younjin_02/post/1fc14286-421c-445a-8315-7ceba898b852/image.png" alt=""></p>
</li>
<li><p><strong>Postfix -&gt; Infix</strong>: 뒤에 가있는 연산자를 다시 피연산자의 가운데로 이동시킨다.</p>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/4212923c-8379-40f6-914d-aec4a02ab6ee/image.png" alt=""></p>
<h2 id="2-정렬">2. 정렬</h2>
<p>정렬은 말 그대로, 자료를 특정 기준으로 정렬하는 것을 의미한다. 세 가지 정렬 방식이 자주 나오므로, 아래 세 가지 방식의 차이점을 잘 알아두면 좋을 것 같다.</p>
<h4 id="1-삽입-정렬insertion-sort">1. 삽입 정렬(Insertion Sort)</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/8c9d94ec-e141-46f1-91bc-5c345cdf4ffd/image.png" alt=""></p>
<blockquote>
<p>정렬된 부분에 새 원소를 끼워넣는 방식이다.</p>
</blockquote>
<p>평균 시간복잡도가 <strong>O(n²)</strong>인 편이라 빠르기 보다는 &#39;안정적&#39;인 정렬에 더 적합해서, 작은 데이터에 더 적합한 정렬 방식이다.</p>
<h4 id="2-선택-정렬selection-sort">2. 선택 정렬(Selection Sort)</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/06eb33e6-22b9-4b3d-9af7-450d03fce7bd/image.png" alt=""></p>
<blockquote>
<p><strong>매번 “가장 작은 값”을 찾아 맨 앞으로 교환</strong>하는 방식이다.</p>
</blockquote>
<p>이 정렬의 경우 항상 시간 복잡도가 <strong>O(n²)</strong>이라 데이터 갯수와 무관하게 불안정한 정렬 방식이다.</p>
<h4 id="3-버블-정렬buuble-sort">3. 버블 정렬(Buuble Sort)</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/b80cf720-21b3-4c10-a071-4707ab68700c/image.png" alt=""></p>
<blockquote>
<p><strong>인접한 두 원소를 비교하며 큰 값을 뒤로 보내는 방식</strong>이다.</p>
</blockquote>
<p>구현이 가장 쉽고, 안정적인 정렬이지만, 비효율적이라 많이 쓰진 않는다.</p>
<h2 id="3-검색">3. 검색</h2>
<p>자료 구조에서 데이터를 찾아가는 방식으로, 여러 가지 검색 방식을 정리해보자.</p>
<h4 id="1-이진-검색이분-검색">1. 이진 검색(이분 검색)</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/a9838a42-78a8-41c7-ace4-df4eaeaee45c/image.png" alt=""></p>
<blockquote>
<p><strong>정렬된 데이터에서 가운데 값을 기준으로 절반씩 줄여가며 찾는 방법</strong></p>
</blockquote>
<p>이진 검색의 가장 중요한 점은, <strong>&#39;반드시 정렬되어 있어야 한다&#39;</strong>는 점이다. 정렬이 안되어 있으면 사용 불가하다. 동작 원리는 아래와 같다.</p>
<pre><code>1.    중간값 선택
2.    찾는 값과 비교
3.    작으면 왼쪽 탐색
4.    크면 오른쪽 탐색
5.    반복</code></pre><p>평균 시간복잡도는 <strong>O(log n)</strong>이다. 1024개 데이터 정도면 10번 정도의 검색에 끝나는 속도다. 선형 검색에 비해서는 훨씬 빠르다고 할 수 있을만한 속도이다.</p>
<h4 id="2-hashing">2. Hashing</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/e86aa37e-2b12-4df7-b48b-5bf5067d7e36/image.png" alt=""></p>
<blockquote>
<p><strong>키(Key)를 해시 함수(Hash Function)에 넣어
고정된 크기의 주소(해시값)로 변환</strong>하여 저장하는 방식</p>
</blockquote>
<p><strong>&#39;Key → Hash Function → 해시 주소 → Hash Table에 저장&#39;</strong>의 순서를 따른다.</p>
<p>쓰는 이유는 당연히, 빠르다. 평균 시간복잡도가 평균 <strong>O(1)</strong>로, 이진 검색보다도 훨씬 빠르다. 다음은 주요 용어다.</p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/2a23a79c-ea18-4c1f-a1dc-d0233e86061f/image.png" alt=""></p>
<p>주요 해싱 함수들을 알아보자.</p>
<ol>
<li><strong>제산법(Division Method)</strong></li>
</ol>
<ul>
<li>공식: <strong>H(K) = K mod Q</strong>
  •    K : 키 값
  •    Q : 테이블 크기보다 작은 소수
레코드 키 값(K)을 해시 테이블의 크기 보다 큰 수 중에서 가장 작은 소수로 나눈 나머지를 홈 주소로 삼는 방식이다. <strong>&#39;나머지 연산&#39;</strong>과 <strong>&#39;소수 사용&#39;</strong>에 포인트를 맞추자.</li>
</ul>
<ol start="2">
<li><strong>제곱법(Mid-Square Method)</strong>
키 값을 제곱한 후, 가운데 값을 잘라서 주소로 사용하는 방식
키의 모든 자릿수를 활용한다.</li>
</ol>
<ul>
<li>Ex. 123 → 123² = 15129 → 가운데 512 사용</li>
</ul>
<ol start="3">
<li><strong>폴딩법 (Folding Method)</strong>
키 값(K)를 여러 부분으로 나눈 후 각 부분의 값을 더하거나 XOR한 값을 홈 주소로 삼는다.</li>
</ol>
<ul>
<li>Ex. 123456 → 123 + 456 = 579</li>
</ul>
<ol start="4">
<li><strong>숫자 분석법 (Digit Analysis)</strong>
키 값을 이루는 숫자의 분포를 분석해서 비교적 고른 자리를 필요한 만큼 선택해서 홈 주소로 삼는 방식이다.</li>
</ol>
<p>비교표로 정리해보자.
<img src="https://velog.velcdn.com/images/younjin_02/post/be62d49b-9bee-44a6-9580-811cfc5a3209/image.png" alt=""></p>
<h2 id="4-db">4. DB</h2>
<p>타이밍 좋다. 마침 요즘 좀 볼륨 큰 프로젝트 하나 하면서 DB를 열심히 만지고 있다. </p>
<blockquote>
<p><strong>스키마란, DB의 구조</strong>를 말한다. 어떤 속성이 있고, 관계가 어떻게 되는지 정의한다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/15986e41-70c0-4975-bc89-fa36473401c1/image.png" alt=""></p>
<blockquote>
<p>스키마를 <strong>사용자 / 전체 설계 / 물리 저장 관점</strong>으로 세 개로 나눌 수 있는데, 이를 <strong>스키마 3계층 구조</strong>라 한다. </p>
</blockquote>
<ol>
<li><p><strong>외부 스키마</strong>
사용자의 관점에서 본 DB 구조로, 해당 사용자에게 필요한 데이터만 보여주며, 논리적 구조를 정의한 것이다. 따라서 유저마다 여러 개 존재 가능하다.</p>
</li>
<li><p><strong>개념 스키마</strong>
DB의 &#39;전체 논리적 구조&#39;이다. 모든 개체(Entity)와 관계 및 제약조건을 따지며, DB 전체 구조를 정의하기에 1개만 존재한다. </p>
</li>
<li><p><strong>내부 스키마</strong>
물리적 저장 장치의 입장에서 본 스키마로, 실제 저장될 레코드의 형식을 정의한다. 성능과 직접적인 연관이 있다.</p>
</li>
</ol>
<h2 id="5-통합-구현">5. 통합 구현</h2>
<p>DevOps, 배포, 유지보수와 밀접하게 연관된 부분을 주로 다루게 된다. 기본적인 내용부터 정리해보자.</p>
<blockquote>
<p><strong>빌드란, 소스코드를 실행 가능한 프로그램으로 만드는 과정</strong>을 의미한다. </p>
</blockquote>
<p>컴파일, 테스트 실행, 패키징, 배포 파일 생성이 이에 해당된다. 이런 빌드를 효율성 있게 도와주는 게 바로 <strong>&#39;빌드 자동화 도구&#39;</strong>다. 대표적으로 다음 4가지가 있다.</p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/ee16f6e4-3c6e-4bdd-94e1-b7289226777f/image.png" alt=""></p>
<p>Apache하면 Ant(XML 구조), 의존성 관리는 Maven, CI 도구는 Jenkins, Groovy는 Gradle(속도 빠름)으로 외우자. </p>
<h2 id="6-sw-패키징">6. SW 패키징</h2>
<blockquote>
<p>개발된 <strong>실행 파일을 묶어 배포용 설치 파일로 만드는 과정</strong>이다. </p>
</blockquote>
<p>이 부분에서 중요한 것은, 개발자 중심이 아닌 <strong>&#39;사용자 중심&#39;</strong>으로 패키징이 이루어져야 하며, 설치/업데이트를 고려해야 한다. </p>
<p>또, 보안을 철저히 고려해 암호화를 철저히 하는 동시에, <strong>DRM(디지털 저작권 관리)</strong> 연동도 고려해야 한다. </p>
<h4 id="6-1-drm디지털-저작권-관리">6-1. DRM(디지털 저작권 관리)</h4>
<blockquote>
<p><strong>Digital Rights Management</strong>의 약어로,
<strong>디지털 콘텐츠의 저작권 보호 및 사용 권한 관리 기술</strong>이다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/e7fa50eb-d58d-426f-9795-605bf65231cb/image.png" alt=""></p>
<ol>
<li><p><strong>클리어링 하우스</strong>
저작권에 대한 사용 권한, 라이선스 발급 및 <strong>&#39;결제 관리&#39;</strong>가 이루어지는 곳이다.</p>
</li>
<li><p><strong>콘텐츠 제공자</strong>
콘텐츠 원 저작자이다.</p>
</li>
<li><p><strong>패키저</strong>
콘텐츠를 암호화해서 메타데이터와 묶어 배포 가능한 형태로 제작한다.</p>
</li>
<li><p><strong>콘텐츠 분배자</strong>
암호화된 콘텐츠를 유통하는 곳이자, 사람이 될 수도 있다. 
Ex. OTT 플랫폼</p>
</li>
<li><p><strong>DRM 컨트롤러</strong>
실제 사용 권한을 제어하는 프로그램이다.</p>
</li>
</ol>
<h4 id="drm-기술-요소">*DRM 기술 요소</h4>
<ul>
<li>콘텐츠 암호화 및 키 관리</li>
<li>콘텐츠 고유 식별자 부여</li>
<li>라이선스 발급 및 관리(사용기간, 횟수 제한 등)</li>
<li>정책 관리 기술</li>
<li>크랙 방지 기술(무단 복제 방지 및 변조 방지)</li>
</ul>
<h4 id="6-2-sw-형상관리">6-2. SW 형상관리</h4>
<blockquote>
<p>소프트웨어 개발 과정에서 발생하는 <strong>변경 사항을 체계적으로 관리하는 활동</strong>이다.</p>
</blockquote>
<p>Git이 대표적인 형상 관리 도구다. 목적은 <strong>개발 비용 감소 및 오류 및 방해 요인 최소화</strong>에 있다. 또한 협업도 매우 용이해진다. </p>
<p>소스 코드 뿐만 아니라 프로젝트 분석서, 운영 및 설치 지침서도 형상 관리의 대상이다. Git 뿐만 아니라 <strong>CVS, Subversion(SVN)</strong>이 있다. </p>
<h4 id="sw-버전-관리-주요-기능">*SW 버전 관리 주요 기능</h4>
<ul>
<li><p><strong>체크 아웃</strong>: 서버에서 내 로컬로 가져오는 것을 의미한다. git pull을 생각하면 될 것 같다.</p>
</li>
<li><p><strong>체크 인</strong>: 수정한 파일을 저장소에 새 버전으로 등록하는 과정이다. git push를 생각하자.</p>
</li>
<li><p><strong>커밋</strong>: 변경 내용을 저장소에 반영하는 행위로, <strong>충돌이 발생할 경우 diff 도구로 수정 후 반영</strong>한다.</p>
</li>
</ul>
<h2 id="7-테스트-관리">7. 테스트 관리</h2>
<p>SW Test에는 <strong>&#39;파레토 법칙&#39;</strong>이라는 법칙이 있다.</p>
<blockquote>
<p>** &quot;소프트웨어 테스트에서 오류의 80%는 전체 모듈의 20%에서 발견된다&quot;**
80:20 법칙</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/cddaf7d4-93fd-42e2-8997-e436578b9fe4/image.png" alt=""></p>
<p>즉, <strong>SW의 결함은 &#39;특정 모듈&#39;에 집중</strong>되므로, 결함이 발생한 모듈을 집중적으로 테스트해서 결함을 고쳐야 한다는 뜻이다.</p>
<p>테스트의 종류를 알아보자.</p>
<h4 id="7-1-화이트박스-테스트">7-1. 화이트박스 테스트</h4>
<blockquote>
<p><strong>프로그램의 내부 구조(코드,논리)를 보고 테스트하는 방법</strong>이다.</p>
</blockquote>
<p>모듈의 원시 코드를 <strong>오픈 시킨 상태</strong>에서, 원시 코드의 논리적인 모든 경로(기초 경로, 수행 가능한 모든 경로)를 테스트하며 테스트 케이스를 설계하는 방법이다. </p>
<p>제어 구조 및 분기, 반복, 조건 검사를 주로 하게 된다. 테스트 종류는 아래와 같다.</p>
<ol>
<li><p><strong>기초 경로 검사</strong>
프로그램의 모든 독립 경로를 한 번 이상 실행해서 검사하는 방식이다.</p>
</li>
<li><p><strong>제어 구조 검사</strong></p>
</li>
</ol>
<ul>
<li>조건 검사: If 조건의 참/거짓이 모두 실행되는지 확인한다.</li>
<li>루프 검사: 반복문을 테스트 한다.</li>
<li>데이터 흐름 검사: 변수 정의 및 사용 흐름을 테스트한다.</li>
</ul>
<h4 id="7-2-블랙박스-테스트">7-2. 블랙박스 테스트</h4>
<blockquote>
<p><strong>내부 구조는 보지 않고 입력과 출력(기능)만으로 테스트</strong>하는 방법</p>
</blockquote>
<ol>
<li><p><strong>동치 분할 검사</strong>: 입력 데이터를 여러 그룹으로 나누어 대푯값만 테스트하는 방식이다.</p>
</li>
<li><p><strong>경계값 분석</strong>: 경계 근처 값(최소 최대의 근처 값)을 테스트한다.</p>
</li>
<li><p><strong>원인-효과 그래프 검사</strong>: 말 그대로 입력 조건(원인)과 결과(효과)에 대한 분석을 한다.</p>
</li>
<li><p><strong>오류 예측 검사</strong>: 개발자가 예상 가능한 오류를 테스트한다. </p>
</li>
<li><p><strong>비교 검사</strong>: 기존 시스템과 결과를 비교한다. </p>
</li>
</ol>
<h4 id="7-3-단위-테스트">7-3. 단위 테스트</h4>
<blockquote>
<p>코딩 직후 수행하는 <strong>소프트웨어 설계의 가장 작은 단위인 모듈 단위</strong>의 테스트 방식이다.</p>
</blockquote>
<p>단위 테스트는 당연히 개발자가 시행하게 되며, <strong>함수/클래스 단위</strong>로 이루어진다. 단위 테스트로는 알고리즘의 오류나, 잘못된 계산식, 무한 반복문같은 코딩 과정의 오류를 발견하기 쉬워진다.</p>
<h4 id="7-4-인수-테스트">7-4. 인수 테스트</h4>
<p>인수 테스트에는 알파 테스트와 베타 테스트가 있다.</p>
<ul>
<li><p><strong>알파 테스트</strong>: 사용자가 개발자 환경에서, 개발자 앞에서 테스트하는 방식이다.</p>
</li>
<li><p><strong>베타 테스트</strong>: 선정된 사용자 여러 명이 실제 사용자 환경에서 행하는 테스트 기법으로, 필드 테스팅이라고도 불린다. </p>
</li>
</ul>
<h4 id="7-5-통합-테스트">7-5. 통합 테스트</h4>
<blockquote>
<p><strong>모듈 간의 인터페이스 오류를 발견하기 위한 테스트</strong>이다.</p>
</blockquote>
<p>여기서 1과목에서 공부했던 stub과 Driver가 나온다.</p>
<ul>
<li><p><strong>하향식 통합 테스트(Top-Down)</strong>: 상위에서 하위 모듈로 가면서 테스트하는 방식으로, 하위 모듈을 대신하는 stub이 사용된다. Stub은 일시적으로 필요한 조건만 가지고 있는 <strong>&#39;시험용 모듈&#39;</strong>이다.</p>
</li>
<li><p><strong>상향식 통합 테스트(Bottom-Up)</strong>: 하위에서 상위로 가며, 상위 모듈을 대체하는 <strong>Driver</strong>가 활용된다. Driver는 <strong>테스트 대상의 하위 모듈을 호출하는 도구</strong>로, 매개변수를 전달하고, 모듈 테스트 수행 결과를 도출한다. </p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/7c8d6194-a055-4216-bb2c-ca330c80bae2/image.png" alt=""></p>
<p>지금까지 여러 테스트 방식을 정리했는데, 이 그림을 통해 흐름을 정리하자. 단위-&gt;통합-&gt;시스템-&gt;인수의 흐름이다. </p>
<h4 id="test-oracle">*Test Oracle</h4>
<p>테스트 결과가 올바른지 판단하는 기준(참값)을 의미하는데, 즉, 맞다/틀리다의 판단 기준이다.</p>
<p>입력이 2+3일때, 예상 결과를 5로 두고 직접 실행 결과를 판단하는 상황이 있다고 가정할 때, 여기서 설정한 <strong>예상 결과가 Test Oracle</strong>이다. 종류도 시험에 자주 나오니 외워두자.
<img src="https://velog.velcdn.com/images/younjin_02/post/97b7c845-c6b9-406a-a8a1-2dc8120153e7/image.png" alt=""></p>
<h4 id="클린-코드-작성의-원칙">*클린 코드 작성의 원칙</h4>
<p>뻔하지만 중요한 내용이니 잘 알아두자.</p>
<ul>
<li><strong>가독성</strong>: 누구나 쉽게 읽을 수 있어야 한다. <del>그게 가능함?</del></li>
<li><strong>단순성</strong>: 복잡하게 작성하지 않는다.</li>
<li><strong>의존성 배제</strong>: 모듈 간 영향을 최소화한다. 1과목에서도 나왔던 결합도를 떨어트려야 한다는 내용이다. </li>
<li><strong>중복 최소화</strong>: 코드의 중복을 최소화해야 한다.</li>
<li><strong>추상화</strong>: 상위 클래스/메소드/함수에는 간략하게 특성을 나타내고, 상세 내용은 하위 클래스/메소드/함수에서 구현해야 한다.</li>
</ul>
<p>추가로, &#39;외계인 코드(Alien code)&#39;라는게 있는데, 아주 오래되거나 참고문서 또는 개발자가 없어 유지보수 및 작업이 어려운 코드를 말한다. </p>
<h4 id="주요-최악의-시간-복잡도">*주요 최악의 시간 복잡도</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/571a14ce-e064-4ae9-a005-29e1a8d6c210/image.png" alt=""></p>
<p>여기서 최악의 시간 복잡도라는 말은, 이제 설명할 것들이 &#39;가장 나쁜 복잡도&#39;라는 뜻이 아니라, 이 <strong>알고리즘이 가질 수 있는 최악의 경우</strong>라는 뜻이다. 그리고 다음 두 가지는 최선, 평균, 최악의 시간 복잡도가 모두 동일한 알고리즘이다. </p>
<ul>
<li><strong>O(1)</strong>: 입력 크기와 무관하게 항상 일정하게 문제 해결에 &#39;하나의 단계&#39;만을 거치는 시간 복잡도로, 대표적으로 <strong>stack의 push&amp;pop과 Hash</strong>가 있다.</li>
<li><strong>O(nlog2n)</strong>: 여기서의 2는 밑이니 주의하자. 이 시간복잡도는 문제 해결에 필요한 단계가 딱 nlog2n의 단계를 수행하게 된다. <strong>병합 정렬이나 힙 정렬</strong>의 경우 항상 이 시간 복잡도를 가지게 된다. 병합 정렬은 데이터를 반으로 나누고 계속 정렬하면서 합치는 정렬이고, 힙 정렬은 heap 자료구조를 활용한 정렬인데, 힙이 완전 이진 트리 구조라 해당 시간 복잡도를 가지게 된다. </li>
</ul>
<h2 id="8-코드-품질-분석-및-시스템-통합-방식">8. 코드 품질 분석 및 시스템 통합 방식</h2>
<h4 id="8-1-소스-코드-품질-분석-도구정적-분석-도구">8-1. 소스 코드 품질 분석 도구(정적 분석 도구)</h4>
<blockquote>
<p>정적 분석이란, <strong>&#39;프로그램을 실행하지 않고 소스코드를 분석&#39;하여 오류나 규칙 위반을 찾는 방법</strong>을 말한다.</p>
</blockquote>
<p>실행 전 검사가 이루어지며, 코딩 표준 위반이나 잠재적 오류를 발견해준다. 대표적인 도구는 아래와 같다.
<img src="https://velog.velcdn.com/images/younjin_02/post/81ca8aa3-e043-4433-b257-097c866ae7b6/image.png" alt=""></p>
<h4 id="8-2-eai-구축-유형">8-2. EAI 구축 유형</h4>
<blockquote>
<p><strong>EAI = Enterprise Application Integration
(기업 내 시스템 통합 방식)</strong></p>
</blockquote>
<p>4가지 주요 통합 방식을 알아두자.</p>
<ol>
<li><p><strong>Point-to-Point(P2P)</strong>: 시스템을 직접 1:1로 연결하는 방식으로, 시스템이 증가하면 복잡도가 급증하게 된다.</p>
</li>
<li><p><strong>Hub &amp; Spoke</strong>: 단일 접점인 중앙 Hub 시스템을 통해 데이터를 전달하는 중앙 집중형 방식이다. </p>
</li>
<li><p><strong>Message Bus(ESB)</strong>: 애플리케이션 사이 미들웨어를 두어 처리하는 방식이다. 분산 구조다.</p>
</li>
<li><p><strong>Hybrid</strong>: Hub &amp; Spoke와 Message Bus의 혼합 방식이다. </p>
</li>
</ol>
<h4 id="8-3-통합-언어">8-3. 통합 언어</h4>
<p>대표적으로 JSON과 AJAX가 있다. </p>
<ol>
<li><strong>JSON(JavaScript Object Notation)</strong></li>
</ol>
<p><strong>속성–값(Attribute–Value) 쌍으로 구성된 텍스트 기반 데이터 교환 포맷</strong>이다. 가독성이 좋고, 경량이라 웹에서 데이터 전송에 많이 사용된다.</p>
<pre><code class="language-json">{
  &quot;name&quot;: &quot;Tom&quot;,
  &quot;age&quot;: 20
}</code></pre>
<ol start="2">
<li><strong>AJAX(Asynchronous JavaScript and XML)</strong></li>
</ol>
<p><strong>자바스크립트를 이용한 비동기 통신 기술</strong>이다. 클라이언트 서버간 XML 데이터를 제어 및 교환한다. 페이지 새로고침 없이 데이터 교환이 이루어지고, 요즘에는 이것도 JSON이 많이 쓰이는 편이다.</p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/b882fd30-a6e6-41e4-a54f-4006ba516bbb/image.png" alt=""></p>
<h2 id="9-인터페이스">9. 인터페이스</h2>
<p>2과목의 마지막으로, 인터페이스 구현 및 보안 관련한 내용이다.</p>
<h4 id="9-1-인터페이스-보안-기능-적용">9-1. 인터페이스 보안 기능 적용</h4>
<blockquote>
<p><strong>네트워크 트래픽을 암호화</strong>하여 보호한다.</p>
</blockquote>
<p>대표기술은 아래와 같다.</p>
<ul>
<li><strong>IPsec</strong>: IP 계층을 암호화하며, VPN에 사용된다.</li>
<li><strong>SSL/TLS</strong>: 전송 계층을 암호화하며, HTTPS 기반이다.</li>
<li><strong>S-HTTP</strong>: HTTP 보안 강화 방식이다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/fdb47694-7b75-48e8-b103-fe11612dd5b7/image.png" alt=""></p>
<p>아직 네트워크 개념이 생소하긴 한데, 이 부분은 뒤에서 네트워크 쪽 공부하고 다시 한번 확인해보자.</p>
<h4 id="9-2-tripwire">9-2. tripWire</h4>
<blockquote>
<p>시스템 파일의 &#39;<strong>무결성(Integrity)&#39;을 검사</strong>하는 보안 도구로, 중요한 파일의 상태를 미리 저장해놓고 해커 침입, <strong>백도어 설치, 설정 파일 변경을 탐지</strong>한다. </p>
</blockquote>
<p>&#39;무결성&#39;에 포인트를 맞추자.</p>
<h4 id="9-3-인터페이스-구현-검증-도구">9-3. 인터페이스 구현 검증 도구</h4>
<p>테스트 자동화 도구가 여러 개 있다,</p>
<ol>
<li><strong>xUnit</strong>: 단위 테스트 프레임워크다. </li>
</ol>
<ul>
<li>JUnit (Java)</li>
<li>CppUnit (C++)</li>
<li>NUnit (.NET)</li>
<li>HttpUnit 등
과 같은 언어를 지원한다.</li>
</ul>
<ol start="2">
<li><p><strong>STAF</strong>: 서비스 호출 및 컴포넌트 재사용 등 <strong>&#39;다양한 환경&#39;</strong>에서 테스트 지원하는 프레임워크다.</p>
</li>
<li><p><strong>FitNesse</strong>: 웹 기반 테스트 프레임워크다. </p>
</li>
<li><p><strong>NTAF</strong>: FitNesse + STAF 결합한 <strong>NHN(Naver)</strong>의 테스트 자동화 프레임워크다.</p>
</li>
<li><p><strong>warit</strong>: <strong>Ruby</strong> 기반 테스트 자동화 프레임워크다.</p>
</li>
</ol>
<h4 id="후기">후기</h4>
<p>여기까지가 2과목 정리내용이다. 1과목보다는 조금 더 내가 현재 하고 있는 공부랑 겹치는 내용이 많아서 더 이해하기 쉬웠던 것 같다. 그만큼 유용하기도 했다. 3과목은 이제 DB 구축(SQL) 관련된 내용이다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[정보처리기사] 1. SW 설계(2) - 객체지향, 모듈, 디자인 패턴,  요구사항 검증, Middleware]]></title>
            <link>https://velog.io/@younjin_02/%EC%A0%95%EB%B3%B4%EC%B2%98%EB%A6%AC%EA%B8%B0%EC%82%AC-1.-SW-%EC%84%A4%EA%B3%842-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%EB%AA%A8%EB%93%88-%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%9A%94%EA%B5%AC%EC%82%AC%ED%95%AD-%EA%B2%80%EC%A6%9D-Middleware</link>
            <guid>https://velog.io/@younjin_02/%EC%A0%95%EB%B3%B4%EC%B2%98%EB%A6%AC%EA%B8%B0%EC%82%AC-1.-SW-%EC%84%A4%EA%B3%842-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%EB%AA%A8%EB%93%88-%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%9A%94%EA%B5%AC%EC%82%AC%ED%95%AD-%EA%B2%80%EC%A6%9D-Middleware</guid>
            <pubDate>Tue, 03 Feb 2026 16:56:24 GMT</pubDate>
            <description><![CDATA[<p>계속해서 1과목 내용을 정리해보자. 원래 한 게시물에 다 담으려 했는데 생각보다 중요한 내용이 많아서 둘로 나누게 됐다..</p>
<h2 id="1-객체지향object-oriented">1. 객체지향(Object-Oriented)</h2>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/40ca1259-a033-4470-9438-484db1a67004/image.png" alt=""></p>
<blockquote>
<p>OOP, 객체 지향 프로그래밍이란 <strong>여러 독립적인 부품들의 조합, 즉 객체들의 유기적인 협력과 결합으로 파악하고자 하는 컴퓨터 프로그래밍의 패러다임</strong>을 의미한다.</p>
</blockquote>
<p>기본 구성 요소는 아래와 같다.</p>
<ul>
<li>Class: 객체를 만들기 위한 설계도</li>
<li>Object: 클래스를 바탕으로 실제로 메모리에 만들어진 실체(Instance)</li>
<li>Message: 객체들끼리 서로 일을 시키거나 소통하기 위해 보내는 <strong>주문(함수 호출)</strong></li>
</ul>
<p>객체지향에서 흔히 잘못 알 수 있는 함정 아닌 함정은, 객체 지향의 <strong>설계의 기본 단위 및 데이터 추상화의 기본 단위는 &#39;클래스&#39;</strong>이다. 객체는 이러한 설계도인 클래스를 사용해 만든 실체이다. 프로그램의 &#39;실행&#39; 단위가 객체인 것이다. 이 부분 헷갈리지 않게 주의하자.</p>
<p><strong>MVC(Model-View-Controller)도 객체지향에 해당하는 패턴</strong>이다. Model은 핵심 기능 및 데이터를 보관하고, View는 사용자에게 정보를 표시해주며, Controller는 유저로부터 입력된 변경 요청을 처리하는, 명확한 책임의 분리를 가진 패턴이다.</p>
<h4 id="1-1-객체지향의-4대-원칙">1-1. 객체지향의 4대 원칙</h4>
<p>이건 객체 지향의 근본이다. 작성자가 실제로 개발 동아리 면접에서도 질문 받은 적이 있고, &#39;객체지향&#39;하면 딱 떠오르는 개념인만큼, 매우 중요하다.</p>
<ol>
<li><strong>캡슐화(Encapsulation)</strong>
데이터(속성)와 기능(메서드)들을 하나의 캡슐로 만들어 <strong>데이터를 외부로부터 보호</strong>하는 것을 말한다. 캡슐화는 크게 두 가지 목적이 있다.</li>
</ol>
<ul>
<li>데이터 보호(data protection) – 외부로부터 클래스에 정의된 속성과 기능들을 보호</li>
<li>데이터 은닉(data hiding) – 내부의 동작을 감추고 외부에는 필요한 부분만 노출</li>
</ul>
<p>즉, 캡슐화는 외부로부터 클래스에 정의된 속성과 기능들을 보호하고, 필요한 부분만 외부로 노출될 수 있도록 하여 각 <strong>객체 고유의 독립성과 책임 영역을 안전하게 지키고자 하는 목적</strong>이 있다. 여기서도 알 수 있듯이 객체 지향의 지향점은 &#39;명확한 역할과 책임의 분리&#39;다. 그렇기에 캡슐화가 필요한 것이다.</p>
<p>자바를 예로 들어 캡슐화를 어떻게 구현하는지 설명해보겠다. <strong>접근제어자(access modifiers)</strong>를 활용해 클래스나 멤버들을 외부에서 접근하지 못하도록 접근을 제한하거나, <strong>getter/setter</strong>로 구현한다. 접근제어자를 통해 문을 잠그고, &#39;정식 출입증&#39;인 getter/setter를 통해서만 데이터를 주고받게 만든다고 생각하면 편하다.</p>
<ol start="2">
<li><p><strong>상속(Inheritance)</strong>
상속은 부모(상위) 클래스의 특징을 자식(하위) 클래스가 그대로 물려받는 것을 의미한다. 코드를 다시 짤 필요가 없게 되니 <strong>재사용성</strong>이 무척 좋아진다. </p>
</li>
<li><p><strong>다형성(Polymorphism)</strong>
다형성이란, 한자 이름 그대로 <strong>어떤 객체의 속성이나 기능이 상황에 따라 여러 가지 형태를 가질 수 있는 성질</strong>을 의미한다. 즉, 똑같은 이름의 명령이라도 각 객체가 자기 방식대로 다르게 동작하는 성질을 뜻한다. </p>
</li>
</ol>
<p>★ <strong>다형성의 핵심: 오버로딩 vs 오버라이딩</strong>
<img src="https://velog.velcdn.com/images/younjin_02/post/b1cf25bc-5dde-453d-a780-44e8f33ac55a/image.png" alt=""></p>
<p>얘는 자주 보지만 볼때마다 헷갈리는 것 같다. 이번에 확실히 개념을 정리해보자.</p>
<ul>
<li><strong>overloading</strong>: 한 클래스 내에서 <strong>이름은 같지만 매개변수(인수)가 다른 경우</strong>를 의미한다. 보통 같은 이름으로 다양한 입력값을 받기 위해 쓴다.</li>
<li><strong>overriding</strong>: 상위 클래스의 메서드를 하위 클래스에서 내용만 바꾸는, &#39;상속 관계&#39;를 바탕으로 이루어진다. </li>
</ul>
<p>자바 코드로 어떻게 구현하는지 살펴보자.
<img src="https://velog.velcdn.com/images/younjin_02/post/97047738-be11-4511-9d6f-7272c54ca34c/image.png" alt=""></p>
<ol start="4">
<li><strong>추상화(Abstarction)</strong>
복잡한 현실 세계의 사물이나 시스템에서 불필요한 지엽적인 특징은 제거하고, 우리가 <strong>구현하고자 하는 목적에 맞는 공통적이고 핵심적인 속성과 기능만을 추출하여 모델링</strong>하는 과정을 의미한다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/9ec05037-6b08-43bb-8181-6a0979651e5e/image.png" alt=""></p>
<p>그림에서 보는 것처럼 공동된&#39;이동수단&#39;이라는 상위 클래스를 만들고, 그 아래에 자식들이 상위 클래스를 받아 여러 메서드들을 구체화한다. </p>
<p>추상화의 유형에는 다음 세 가지가 있다.</p>
<ul>
<li><strong>과정 추상화</strong>:“ 어떻게(구현)”는 숨기고, “무엇을(기능)”만 보여준다. 즉, 처리 과정/알고리즘을 하나의 ‘기능’으로 묶는 것을 의미한다. 예를 들면 코드의 print() 함수가 내부적으로 버퍼나 드라이버가 무슨 작업을 하는지는 몰라도, &#39;출력한다&#39;만 알고 있으면 되는 원리이다.</li>
<li><strong>데이터(자료) 추상화</strong>: 자료 구조에서 ADT를 생각하면 된다. 데이터의 자세한 구조는 감추고 연산만 보여주는 추상황이다.</li>
<li><strong>제어 추상화</strong>: 프로그램의 흐름 제어를 &#39;단순한 형태&#39;로 표현하는 것으로, 조건문, 반복문, 넓게 보면 async/await, thread, semaphore 같은 동시성 제어도 포함될 수 있다.</li>
</ul>
<h4 id="1-2-객체지향-분석-방법론ooa">1-2. 객체지향 분석 방법론(OOA)</h4>
<blockquote>
<p><strong>객체지향 분석(OOA; Object Oriented Analysis)</strong>은 사용자의 요구사항을 분석하여 요구된 문제와 관련된 <strong>모든 클래스(객체), 이와 연관된 속성과 연산, 그들 간의 관계 등을 정의하여 모델링하는 작업</strong>을 의미한다. 객체는 클래스로부터 인스턴스화되고, 이 클래스를 식별하는 것이 객체지향 분석의 주요한 목적이다.</p>
</blockquote>
<p>객체 지향 분석 방법론은 총 4가지가 있다.</p>
<ol>
<li><p><strong>Rumbaugh 기법</strong>
바로 앞 게시물에서 설명했으니 생략하겠다. <del>객동기 객동기..</del>
가장 일반적으로 사용하는 방법으로, 객체 모델링(객체 다이어그램) -&gt; 동적 모델링(상태 다이어그램) -&gt; 기능 모델링(DFD)로 이어진다.</p>
</li>
<li><p><strong>Booch 방법론</strong></p>
</li>
</ol>
<p><strong>미시적(Micro) 개발 프로세스와 거시적(Macro) 개발 프로세스를 모두 사용</strong>하는 분석 방법으로, 클래스와 객체들을 분석 및 식별하고 클래스의 속성과 연산을 정의한다.</p>
<ul>
<li>미시적 프로세스: 클래스 하나하나를 자세히 보는 단계</li>
<li>거시적 프로세스: 시스템 전체 구조를 보는 단계</li>
</ul>
<ol start="3">
<li><strong>Coad and Yourdon 방법론</strong></li>
</ol>
<p><strong>E-R 다이어그램을 사용</strong>하여 객체의 행위를 모델링하며, 객체 식별, 구조 식별, 주제 정의, 속성과 인스턴스 연결 정의, 연산과 메시지 연결 정의 등의 과정으로 구성하는 기법이다. </p>
<p><strong>*E-R(Entity-Relationship) Diagram</strong>
<img src="https://velog.velcdn.com/images/younjin_02/post/8808d3ce-28a1-4d07-8f8a-fe63b84171c3/image.png" alt="">
현실 세계에 존재하는 데이터와 그들 간의 관계를 사람이 이해할 수 있는 형태로 명확하게 표현하기 위해 가장 널리 사용되고 있는 모델이다.</p>
<ol start="4">
<li><strong>Wirfs(워프스)-Brock(브록) 방법론</strong>
분석과 설계 간의 구분이 없고, 고객 명세서를 평가해서 설계 작업까지 연속적으로 수행하는 기법이다.</li>
</ol>
<h4 id="1-3-객체지향-설계-5원칙-solid-⭐️">1-3. 객체지향 설계 5원칙 (SOLID) ⭐️</h4>
<ol>
<li><p><strong>SRP(Single Responsibility Principle) - 단일 책임 원칙</strong>
한 객체는 단 하나의 책임을 지닌다. &#39;응집도는 높게, 결합도는 낮게&#39; 설계하는 것이 원칙이다.</p>
</li>
<li><p><strong>OCP(Open-Closed Principle) - 개방-폐쇄 원칙</strong></p>
</li>
</ol>
<p><strong>&quot;변경에는 닫혀있고, 확장에는 열려 있다&quot;</strong>는 법칙으로, 코딩 시 기존의 코드는 건드리지 않고 새로운 기능만 &#39;장착&#39;해야 한다는 원칙이다. </p>
<ol start="3">
<li><strong>LSP(Liskov Substitution Principle) - 리스코프 치환 원칙</strong></li>
</ol>
<p><strong>&quot;자식(Sub)는 부모(Super)로 치환될 수 있다&quot;</strong>는 법칙으로, 자식 클래스는 부모의 역할을 &#39;완벽히&#39; 대신할 수 있어야 한다.</p>
<ol start="4">
<li><strong>ISP(Interface Segregration Principle) - 인터페이스 분리 원칙</strong></li>
</ol>
<p><strong>사용하지 않는 인터페이스와 의존 관계를 맺거나 영향을 받지 않아야 한다</strong>는 원칙이다. 인터페이스 하나의 명확한 책임의 원칙이라고 생각하면 될 것 같다.</p>
<ol start="5">
<li><strong>DIP(Dependency Inversion Principle) - 의존 역전 원칙</strong></li>
</ol>
<p><strong>&quot;상위 모듈은 하위 모듈들에 의존해서 안되며(완벽하게 분리), 모든 것은 추상에 의존해야 한다&quot;</strong>는 원칙이다. 쉽게 얘기하면 추상성이 낮은 클래스보다 추상성이 높은(잘 변화하지 않는) 클래스와 의존 관계를 맺어야 한다는 뜻이다. </p>
<h2 id="2-모듈">2. 모듈</h2>
<blockquote>
<p>SW를 설계할 때 다음에 재사용할 수 있도록,혹은 관리하기 쉽도록 아예 <strong>기능별로 쪼개놓은 부품</strong>을 의미한다. </p>
</blockquote>
<p>모듈은 <strong>모듈화를 통해 분리된 시스템의 각 기능</strong>들이며, <strong>단독으로 컴파일 가능</strong>하며, 재사용이 가능하다는 특징이 있다. 다른 모듈에서의 접근 또한 가능하다.</p>
<p>모듈은 익숙한 단어면서도 낯선 단어인 것 같다. 여기서 그 &#39;결합도&#39;와 &#39;응집도&#39;가 나오니 모듈 개념이랑 엮어서 잘 기억해두자.</p>
<h4 id="2-1-결합도coupling-★★★">2-1. 결합도(coupling) ★★★</h4>
<p>결합도는 모듈과 모듈 사이가 얼마나 의존되어 있는가를 나타낸다. 결합도가 높으면 하나가 고장나면 결합된 옆에 있는 다른 모듈까지 고장나게 되므로, <strong>결합도가 낮을 수록 좋은 설계</strong>이다.</p>
<p>결합도 종류를 결합도가 최악→가장 좋은 순으로 <strong>&quot;내공은 외제를 쓰자&quot;</strong>로 외워보자.
<img src="https://velog.velcdn.com/images/younjin_02/post/6da62587-e62e-4e87-8513-5dc210daf540/image.png" alt=""></p>
<h4 id="2-2-응집도">2-2. 응집도</h4>
<p>응집도는 모듈 내부 부품이 얼마나 잘 뭉쳐있는가를 나타내는 지표로, <strong>응집도가 높을수록 좋은 설계</strong>이다.</p>
<p>응집도 종류는 &quot;귀(기)순병사를 교환하는 절차는 시간적으로 논의해야함&quot;으로 외워보자.
<img src="https://velog.velcdn.com/images/younjin_02/post/161920e1-d00e-4dee-b794-349d0b228ab8/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/334f8040-356d-4894-a120-de69ee6d3ecf/image.png" alt=""></p>
<h4 id="3-3-fan-in--fan-out">3-3. Fan-In &amp; Fan-Out</h4>
<blockquote>
<ul>
<li><strong>Fan-In</strong>: 어떤 모듈을 호출(제어)하는 모듈의 수(들어오는 화살표 개수)</li>
</ul>
</blockquote>
<ul>
<li><strong>Fan-Out</strong>: 어떤 모듈이 호출하는 호출하는 모듈의 수(나가는 화살표 개수)</li>
</ul>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/c8525a2b-ba7a-4f99-bf62-4a81d025f370/image.png" alt=""></p>
<p>팬인은 많을수록 재사용성이 높아져, 클수록 좋지만,
팬아웃은 많을 수록 공통 기능이 많아져 결합도가 증가하므로 적을수록 좋다.</p>
<h4 id="ns-chart">*NS Chart</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/3019af23-826f-4490-968e-77bac983b0ef/image.png" alt="">
<strong>구조적 프로그래밍</strong>을 표현하는 도형 기반 설계 기법이다. <strong>논리적 기술에 중점</strong>을 두고 도형을 이용한 표현 방법이다.</p>
<ul>
<li>GOTO나 화살표를 사용하지 않는다.</li>
<li>시각적으로 명확히 식별하는데 적합하며, 이해하기 쉽고, 코드 변환이 용이하다.</li>
</ul>
<h2 id="3-디자인-패턴">3. 디자인 패턴</h2>
<blockquote>
<p>디자인 패턴은 건축으로 비유했을 때, &#39;이런 상황에서는 이런 구조가 좋다&#39;라고 하는 일종의 설계 족보이다. 
OOP를 설계할 때 자주 발생하는 문제들을 피하기 위해 사용하는 패턴이다.</p>
</blockquote>
<p>디자인 패턴은 23가지 종류가 있는데, <del>드릅게 많ㄴ</del> 각 패턴이 있고, 그에 해당되는 각각의 방법이 있어서, 일단 해당 방법이 어느 패턴에 속하는지 정도로 암기해놓으면 좋을 것 같다. 정처기 필기에서는 대략 1문제 정도 출제된다고 한다.
<img src="https://velog.velcdn.com/images/younjin_02/post/f284e22c-4c03-4bef-94c8-3b9e9011d07e/image.png" alt=""></p>
<h4 id="3-1-생성-패턴">3-1. 생성 패턴</h4>
<p>객체가 생성되는 방식에 관여하는 패턴이다. &#39;추빌팩프싱&#39;</p>
<ul>
<li><strong>추상 팩토리(Abstract Factory)</strong>: 관련 있는 객체들을 그룹으로 묶어 한꺼번에 생성해 추상적으로 표현한다.</li>
<li>빌더(Builder): 작게 분리된 인스턴스를 건축하듯이 조합하여 객체를 생성한다.</li>
<li><strong>팩토리 메서드(Factory Method)</strong>: 객체 생성을 자식 클래스(서브 클래스)에서 결정하도록 미룬다. <strong>가상 생성자 패턴</strong>이라고도 부른다.(가장 출제 빈도 높음)</li>
<li>프로토타입(Prototype): 원본 객체를 복사해서 새로운 객체를 만듭니다.</li>
<li><strong>싱글톤(Singleton)</strong>: 시스템 전체에서 클래스의 인스턴스를 단 하나만 만들도록 제한합니다</li>
</ul>
<h4 id="3-2-구조-패턴">3-2. 구조 패턴</h4>
<p>클래스나 객체를 조합해 <strong>더 큰 구조를 만드는 패턴</strong>이다.</p>
<ul>
<li>어댑터(Adapter): 호환되지 않는 인터페이스를 연결해 줍니다.</li>
<li>브리지(Bridge): 구현부에서 추상층을 분리하여 독립적으로 확장하게 합니다.</li>
<li>컴포지트(Composite): 개별 객체와 복합 객체를 동일하게 다룹니다. (트리 구조)</li>
<li>데코레이터(Decorator): 객체에 새로운 기능을 동적으로 추가합니다.</li>
<li>퍼사드(Facade): 복잡한 서브시스템을 간편하게 사용하도록 상위 인터페이스를 제공합니다.</li>
<li>플라이웨이트(Flyweight): 많은 수의 작은 객체를 공유하여 메모리를 절약합니다.</li>
<li>프록시(Proxy): 실제 객체 대신 가짜 객체를 내세워 접근을 제어합니다.</li>
</ul>
<h4 id="3-3-행위-패턴">3-3. 행위 패턴</h4>
<p>객체들이 <strong>서로 어떻게 소통하고 책임을 분담하는지</strong>에 대한 패턴이다.</p>
<ul>
<li>커맨드(Command): 요청을 객체 형태로 캡슐화합니다.</li>
<li>옵저버(Observer): 한 객체의 상태 변화를 관찰 중인 다른 객체들에게 통지합니다.</li>
<li>상태(State): 객체의 상태에 따라 행동을 변경합니다.</li>
<li>전략(Strategy): 알고리즘을 캡슐화하여 필요할 때마다 교체해서 사용합니다.</li>
<li>템플릿 메서드(Template Method): 전체적인 알고리즘 구조는 부모가 정하고, 세부 처리는 자식이 정합니다.</li>
</ul>
<p>디자인 패턴의 장점은 재사용성이 높아지고 설계 변경이 쉬워진다는 점이다. 하지만 반대로 객체 수가 늘어나서 복잡해질 수 있다는 단점도 기억하도록 하자.</p>
<h2 id="4-인터페이스-요구사항-검증">4. 인터페이스 요구사항 검증</h2>
<blockquote>
<p>인터페이스 요구사항 검증은 설계 단계에서 오류를 미리 발견하여 나중에 큰 비용이 드는 것을 방지하는 아주 중요한 과정이다.</p>
</blockquote>
<p>다음 세 가지 방법은 &#39;누가, 얼마나 공식적으로 모이느냐&#39;에 따라 구분된다. 
<img src="https://velog.velcdn.com/images/younjin_02/post/98c2e5be-32b6-4c98-9fba-f3a2ef6a009f/image.png" alt=""></p>
<p>Walkthrough의 경우 <strong>&#39;회의 전&#39;</strong>에 명세서를 미리 배포한다는 점을 기억하자.</p>
<h2 id="5-middleware">5. Middleware</h2>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/3d978bc2-2e7f-405b-9e07-7f25f7c94b45/image.png" alt=""></p>
<blockquote>
<p>미들웨어란, 서로 다른 시스템(OS, 하드웨어, 프로토콜)끼리 소통을 할 때 <strong>중간에서 통역사 역할</strong>을 해주는 소프트웨어를 의미한다. </p>
</blockquote>
<p>즉, 클라이언트-서버 사이에서 데이터가 잘 오갈 수 있게 하며, 부하를 분산해주는 역할을 해준다. <strong>분산 컴퓨팅 환경에서 서로 다른 기종간을 연결</strong>하며, 위치 투명성을 제공한다. 내부 동작을 확인하려면 별도의 소프트웨어를 활용해야 한다. </p>
<p>대표적인 Middleware 5가지를 알아보자.</p>
<h4 id="5-1-dbdatabase-middleware">5-1. DB(Database Middleware)</h4>
<p>여기서 말하는 DB 미들웨어는 그냥 DB(데이터 저장소, ex. Oracle, MySQL)랑은 다른 개념이다. 주의하자.</p>
<ul>
<li>애플리케이션과 DB를 연결해주는 미들웨어</li>
<li>대표 기술: ODBC, JDBC, ORM</li>
<li>Ex. 자바 프로그램(App)이 oracle에 접속해서 데이터를 가져오는 상황</li>
</ul>
<h4 id="5-2-rpc-remote-procedure-call">5-2. RPC (Remote Procedure Call)</h4>
<ul>
<li>내 컴퓨터(Local)에 있는 프로그램을 돌리듯이, 멀리 있는 컴퓨터(Remote)의 프로그램을 호출하는 방식</li>
</ul>
<h4 id="5-3-mom-message-oriented-middleware">5-3. MOM (Message Oriented Middleware)</h4>
<ul>
<li>메시지를 기반으로 시스템 간에 데이터를 비동기적으로 전달해주는 미들웨어</li>
<li>비동기</li>
</ul>
<h4 id="5-4-tp-monitor-transaction-processing-monitor">5-4. TP-Monitor (Transaction Processing Monitor)</h4>
<ul>
<li>수많은 사람이 동시에 접속해서 데이터를 처리할 때, 오류 없이 안정적으로 처리되도록 하는 미들웨어</li>
</ul>
<h4 id="5-5-was-web-application-server">5-5. WAS (Web Application Server)</h4>
<ul>
<li><strong>웹 기반 응용 프로그램의 비즈니스 로직을 수행</strong>하고 DB 연동, 트랜잭션, 보안 등을 처리하는 서버</li>
<li>대표 Ex. Apache Tomcat</li>
</ul>
<p>단순 서버의 개념보다는, 웹 서버보다 한 단계 더 똑똑한 서버의 개념으로 생각하면 된다. 단순 웹 서버는 화면만 처리한다면, WAS는 계산, 판단 및 DB 통신을 담당한다. 동적인 로직을 담당이라고 보면 될 것 같다.</p>
<p>WAS는 3-Tier의 구조에서 다음과 같이 위치한다.</p>
<pre><code class="language-plain">[Client]
   ↓
[Web Server]
   ↓
[WAS]
   ↓
[DB]</code></pre>
<p>여기까지 1과목의 주요 내용(시험 출제율 높은 것 위주)을 정리해봤다. 앞서 언급했듯 바로 전학기 였던 3학년 2학기 과목의 SW Engineering 과목에 개발 지식 조금 추가한 느낌이여서 다소 새롭거나 어렵게 다가오지는 않았던 것 같다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[정보처리기사] 1. SW 설계(1) - SDLC, 요구사항 분석, UML, UI, SW Architecture]]></title>
            <link>https://velog.io/@younjin_02/%EC%A0%95%EB%B3%B4%EC%B2%98%EB%A6%AC%EA%B8%B0%EC%82%AC-1.-SW-%EC%84%A4%EA%B3%841-SDLC-%EC%9A%94%EA%B5%AC%EC%82%AC%ED%95%AD-%EB%B6%84%EC%84%9D-UML-UI-SW-Architecture</link>
            <guid>https://velog.io/@younjin_02/%EC%A0%95%EB%B3%B4%EC%B2%98%EB%A6%AC%EA%B8%B0%EC%82%AC-1.-SW-%EC%84%A4%EA%B3%841-SDLC-%EC%9A%94%EA%B5%AC%EC%82%AC%ED%95%AD-%EB%B6%84%EC%84%9D-UML-UI-SW-Architecture</guid>
            <pubDate>Fri, 30 Jan 2026 09:40:05 GMT</pubDate>
            <description><![CDATA[<p>본격적으로 정보처리기사가 되기 위한 공부를 해보려고 한다. 필기의 경우 기출이 중요하긴 하지만 그래도 기출 풀어보기 전에 작년에 밤새워가며 죽어라 했던 전공 과목들 기억들 되살릴겸 중요 개념부터 다잡고 가보자.</p>
<p>1과목은 소프트웨어 &#39;설계&#39;다. 어느정도 다 익숙한 내용들이긴 하다.
<img src="https://velog.velcdn.com/images/younjin_02/post/629a7a76-1952-4dfc-b4e5-38cafc1a2a96/image.png" alt=""></p>
<p>목차는 위와 같다. 1과목 소프트웨어 설계에서는 다음 4가지가 크게 중요 포인트다.</p>
<ul>
<li>SW 생명 주기</li>
<li>UML</li>
<li>객체 지향 구성 요소</li>
<li>모듈</li>
</ul>
<h2 id="1-소프트웨어-생명-주기">1. 소프트웨어 생명 주기</h2>
<blockquote>
<p><strong>소프트웨어 개발 생명주기(SDLC, Software Development Life Cycle)</strong>
: SW 제품이 <strong>기획 단계에서부터 배포, 유지 보수에 이르기까 거치는 전체 과정</strong>으로, 품질, 효율성, 비용 절감을 목표로 한다.</p>
</blockquote>
<h3 id="1-1-sdlc-주요-단계">1-1. SDLC 주요 단계</h3>
<ol>
<li><p><strong>요구사항 수집 및 분석</strong>
프로젝트의 목적과 목표를 정의하고, 사용자와 이해관계자의 요구사항을 분석한다. <strong>고객이 원하고 필요로 하는 기능을 명확히 파악하는 단계</strong>라고 생각하면 된다.</p>
</li>
<li><p><strong>설계 (Design)</strong>
요구사항을 바탕으로 <strong>시스템의 구조를 설계</strong>한다. 여기에는 데이터베이스 구조, 소프트웨어 아키텍처, UI 설계 등이 포함되며, 시스템의 큰 그림과 세부 설계를 포함한다.</p>
</li>
<li><p><strong>구현 (Coding)</strong>
설계된 내용을 바탕으로 실제 코드를 작성하여 <strong>소프트웨어를 개발</strong>한다. 개발된 소프트웨어의 기능이 올바르게 작동하는지 검증할 수 있도록 코딩 표준을 준수하며 진행한다.</p>
</li>
<li><p><strong>테스트 (Testing)</strong>
개발이 완료된 <strong>소프트웨어를 검증하고, 오류나 결함을 찾아 수정</strong>한다. 단위 테스트, 통합 테스트, 시스템 테스트, 사용자 수용 테스트 등 다양한 테스트 과정을 거친다.</p>
</li>
<li><p><strong>유지보수 (Maintenance)</strong></p>
</li>
</ol>
<p><strong>배포 이후 발생하는 문제를 해결하고, 기능을 개선하거나 추가</strong>한다. 사용자 피드백을 반영하여 개선할 뿐 아니라, 소프트웨어가 최신 기술에 맞게 지속적으로 발전할 수 있도록 리팩토링한다.</p>
<h3 id="1-2-sdlc-모델">1-2. SDLC 모델</h3>
<ol>
<li><strong>폭포수 모델(Waterfall Model)</strong>
<img src="https://velog.velcdn.com/images/younjin_02/post/db1fae81-591a-45bc-8976-bdb36559b546/image.png" alt=""></li>
</ol>
<ul>
<li>각 단계가 순차적으로 진행되는 모델</li>
<li><strong>보헴</strong>이 제시</li>
<li>간단하고 체계적, 요구사항이 고정된 프로젝트에 적합</li>
<li><strong>단계가 순차적이라 중간에 변경이 어렵고</strong>, 후반 단계에서 문제 발견되면 해결하기 어려움</li>
</ul>
<ol start="2">
<li><strong>Prototyping Model</strong>
<img src="https://velog.velcdn.com/images/younjin_02/post/2b7cce3a-a9e3-4f4b-999f-a610898d15f7/image.png" alt=""></li>
</ol>
<ul>
<li>초기에 prototype(시제품)을 제작하고 사용자에게 피드백을 받아 수정 및 보완을 거쳐 최종 제품을 완성하는 모델</li>
<li>요구사항 정확히 반영하는데 유리함</li>
<li>반복적인 프로토타이핑은 시간과 비용을 늘림</li>
</ul>
<ol start="3">
<li><strong>나선형(Spiral) 모델</strong>
<img src="https://velog.velcdn.com/images/younjin_02/post/1e694630-7965-4c0b-bfa9-cdaf6d774667/image.png" alt=""></li>
</ol>
<ul>
<li>위험 요소 분석과 반복 개발을 결합한 모델로, <strong>단계마다 위험을 평가하고 해결해 소프트웨어를 점진적으로 완성</strong></li>
<li>보헴이 제안</li>
<li>폭포수+프로토타입 장점에 위험 분석 기능 추가한 모형</li>
<li>위험 요소를 관리해 대규모 프로젝트에 적합</li>
<li>복잡하고 비용이 많이 발생하는 경향이 있음</li>
</ul>
<ol start="4">
<li><strong>RAD(Rapid Application Development) 모델</strong>
<img src="https://velog.velcdn.com/images/younjin_02/post/843d9572-0df8-4511-9b34-bd3ad9700648/image.png" alt=""></li>
</ol>
<ul>
<li><strong>빠른 개발을 위해 프로토타이핑과 사용자 피드백을 활용해 짧은 주기</strong>로 소프트웨어 개발하는 모델</li>
<li>짧은 시간, 사용자 요구에 유연하게 대응</li>
<li>복잡한 프로젝트에서는 관리가 어려움</li>
</ul>
<h4 id="agile-모델">*Agile 모델</h4>
<blockquote>
<p><strong>고객의 요구사항 변화에 유연하게 대응</strong>하는 모델</p>
</blockquote>
<ul>
<li>반복적, 점진적 개발(Iteration &amp; Increment)</li>
<li><strong>빠른 피드백 및 짧은 개발 주기</strong></li>
<li><strong>최소한의 문서화, 코드 중심</strong></li>
<li>지속적인 고객 협업 및 피드백 &amp; 테스트(TDD, CI/CD)</li>
<li>스타트업에서 주로 사용</li>
</ul>
<p>위에서 언급한 구조적 개발론에 해당하는 모델들과, 애자일 방법론을 비교한 표이다.
<img src="https://velog.velcdn.com/images/younjin_02/post/06544250-8161-46b1-96af-da8187789a8f/image.png" alt=""></p>
<p>추가로 설계 패러다임도 비교해보자. 구조적 개발과 객체지향을 비교한 표이다.
<img src="https://velog.velcdn.com/images/younjin_02/post/829dc1d6-5ac6-4658-9d07-0becb29e404a/image.png" alt="">
객체지향은 &#39;재사용성&#39;과 &#39;유지보수&#39;가 중요하다. 뒤에서 더욱 자세히 정리하겠다.</p>
<p>Agile에는 세부 모델들도 몇 가지 존재한다. 이것도 정리해보자.</p>
<ol>
<li><strong>Scrum</strong>: 팀원들이 정해진 기간내에 목표를 달성하기 위해 함께 달리는 방식</li>
</ol>
<ul>
<li>스프린트: 1-4주 단위의 짧은 개발 반복 주기</li>
<li>backlog: 우선순위가 매겨진 요구사항 목록</li>
</ul>
<ol start="2">
<li><strong>XP(Extreme Programming)</strong>: 개발의 <strong>극단적인(Extreme) 효율을 추구</strong>하며, 기술적인 실천 사항을 강조하는 동시에 고객의 요구사항에 즉각 대응한다.
<img src="https://velog.velcdn.com/images/younjin_02/post/5d714d59-679d-4535-ad31-d42898f3a09e/image.png" alt=""></li>
</ol>
<ul>
<li><strong>Pair Programming (짝 프로그래밍)</strong>: 개발자 둘이서 한 컴퓨터로 코딩</li>
<li>TDD (테스트 주도 개발): 코드 짜기 전에 테스트 케이스부터 만들기</li>
<li><strong>Refactoring (리팩토링)</strong>: 기능은 그대로 두되 코드 구조를 개선</li>
<li><strong>Collective Ownership</strong>: 공동 코드 소유</li>
<li><strong>CI(Continuous Integration)</strong>: 계속적인 통합</li>
</ul>
<p>XP는 5가지 핵심 가치를 기반으로 한다. 주요 포인트니 잘 외워두도록 하자.</p>
<blockquote>
<p><strong>&#39;용단커피존&#39;</strong></p>
</blockquote>
<ul>
<li>용기(courage)</li>
<li>단순성(simplicity)</li>
<li>의사소통(communication)</li>
<li>피드백</li>
<li>존중(Respect)</li>
</ul>
<ol start="3">
<li><strong>칸반(Kanban)</strong>: 업무가 정체되지 않게 시각적으로 관리하는 방식
화이트보드에 포스트잇을 붙여 관리하는 모습을 상상해보자</li>
</ol>
<ul>
<li>WIP(Work In Progress) 제한: 지금 하는 일의 개수를 제한해서 업무 과부화 방지</li>
</ul>
<ol start="4">
<li><strong>기능 중심 개발(FDD: Feature Driven Development)</strong>: 사용자에게 의미 있는 &#39;기능&#39;단위를 기준으로 개발 계획을 세우는 방식</li>
</ol>
<h2 id="2-요구사항-분석">2. 요구사항 분석</h2>
<blockquote>
<p>요구사항 분석이란, <strong>개발 대상에 대한 사용자의 요구사항을 이해하고 문서화(명세화)하는 활동</strong>을 의미한다.</p>
</blockquote>
<p>SW 개발의 실질적인 첫 단계이며, <strong>사용자 요구의 타당성을 조사</strong>하고, <strong>비용과 일정에 대한 제약</strong>을 설정한다. 그에 따른 <strong>목표와 해결 방식</strong>을 결정하는 것까지 포함된다.</p>
<p>또한, 요구사항 개발 프로세스는 다음과 같은 4단계를 따른다.</p>
<blockquote>
<p><strong>도출(Elicitation) -&gt; 분석(Analysis) -&gt; 명세(specification) -&gt; 확인(validation)</strong></p>
</blockquote>
<p>주요 비기능 요구사항에는 다음과 같은 5가지가 있다.</p>
<ul>
<li>성능 요구사항</li>
<li>보안 요구사항</li>
<li>품질 요구사항</li>
<li>제약 사항</li>
<li>인터페이스 요구사항</li>
</ul>
<p>요구사항 분석 단계에서, &#39;구조적 개발 방법론&#39;에서 사용하는 3가지 핵심 도구들을 알아보자.</p>
<ul>
<li>DFD(자료흐름도)</li>
<li>DD(자료사전)</li>
<li>Mini-Spec(소단위명세서)</li>
</ul>
<ol>
<li><strong>자료 흐름도(DFD, Data Flow Diagram)</strong>
<img src="https://velog.velcdn.com/images/younjin_02/post/e57c734e-440f-4153-a47a-c81dd6f7f7a3/image.png" alt=""></li>
</ol>
<p>데이터가 어디서 들어와서 어떻게 변하고 어디로 나가는지 보여준다. 시간 흐름은 알 수 없으며, <strong>데이터의 흐름에만 집중</strong>한다. 구성 요소도 외워두자.
<img src="https://velog.velcdn.com/images/younjin_02/post/9456924f-6619-4bab-a437-a1fbb35e2593/image.png" alt="">
<img src="https://velog.velcdn.com/images/younjin_02/post/97a4c82e-5ab6-44ed-9419-94d17fde09de/image.png" alt=""></p>
<ul>
<li>단말(Terminator)은, 시스템 바깥에 위치하며, 시스템과 데이터를 주고 받는 &#39;외부 개체&#39;이다.</li>
<li>입력 화살표가 있다고 해서 꼭 &#39;출력&#39; 화살표가 있어야 하는 것은 아니다.</li>
<li><strong>bubble chart</strong>라고도 함(process를 원으로 표시)</li>
<li>구조적 분석 기법에 이용됨</li>
</ul>
<ol start="2">
<li><strong>자료 사전(DD, Data Dictionary)</strong></li>
</ol>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/61f98fd0-d94e-4637-8b9c-d33c78d82426/image.png" alt="">
DFD에 등장하는 데이터들의 의미를 약속된 기호로 정의한다. 시험에 기호 의미를 묻는 문제가 자주 출제되니 이미지를 기억해두자.</p>
<ol start="3">
<li><strong>소단위 명세서(Mini-Spec)</strong>
<img src="https://velog.velcdn.com/images/younjin_02/post/f483a2f9-8685-441b-9ba6-3ce8d09f99f3/image.png" alt="">
자료 흐름도의 최하위 프로세스가 어떤 로직으로 돌아가는지 설명한다.</li>
</ol>
<h2 id="3-umlunified-modeling-language">3. UML(Unified Modeling Language)</h2>
<blockquote>
<p>UML은 시스템 개발자와 고객, 혹은 개발자 상호간의 <strong>의사소통을 원활하게 표준화하기 위한 대표적인 객체지향 모델링 언어</strong>이다.</p>
</blockquote>
<h4 id="3-1-uml-3대-구성-요소">3-1. UML 3대 구성 요소</h4>
<ul>
<li>사물(Things): 모델을 구성하는 기본 요소 (클래스, 인터페이스, 유스케이스 등)</li>
<li>관계(Relationships): 사물과 사물 사이의 연관성을 표현 (상속, 포함, 실체화 등) - 1:1, 1:N, N:N</li>
<li>다이어그램(Diagram): 사물과 관계를 시각적으로 모아놓은 그림</li>
</ul>
<h4 id="uml의-주요-관계">*UML의 주요 관계</h4>
<ul>
<li><strong>일반화(Generalization)</strong> 관계: 하나의 사물이 다른 사물에 비해 <strong>더 일반적인지 구체적인지를 표현</strong>한다. ex. Java의 extends(상속)</li>
<li><strong>의존(Dependency)</strong> 관계: 필요에 의해 서로에게 영향을 주는 <strong>짧은 시간 동안만 연관을 유지</strong>하는 관계를 표현한다.</li>
<li><strong>실체화(Realization)</strong> 관계: 사물이 할 수 있거나 해야 하는 기능으로 서로를 그룹화 할 수 있는 관계를 표현한다. <strong>인터페이스를 클래스가 구현</strong>하는 단계이다. ex. Java의 implements(구현)</li>
</ul>
<p>다이어그램 구조도는 아래와 같다.
<img src="https://velog.velcdn.com/images/younjin_02/post/3b48567c-2b83-4b3d-8752-f5c65621a0b9/image.png" alt=""></p>
<h4 id="3-2-구조적-다이어그램structural--static">3-2. 구조적 다이어그램(Structural / Static)</h4>
<p>시스템의 <strong>정적인 모습</strong>을 그린다. 건물의 설계도처럼 &#39;무엇이 있는가&#39;에 집중한다.</p>
<ol>
<li><strong>Class Diagram</strong>
<img src="https://velog.velcdn.com/images/younjin_02/post/7784aabe-170c-42dd-91e0-06a9e86fa84d/image.png" alt="">
시스템 내의 클래스들의 관계를 표현하며, 첫 번째 블록은 클래스의 이름, 두 번째 블록은 클래스의 &#39;속성&#39;, 세 번째 블록은 클래스의 메서드들을 나타낸다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/147d31a8-93d3-4867-b50b-55a2970ccfed/image.png" alt="">
클래스들간의 연관관계는 위와 같이 나타낸다. </p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/e2fa26b7-f5db-4100-a836-421b4bc31446/image.png" alt="">
이건 순서대로 Association(연관), Inheritance(상속), Realization/Implementation(실체화), Dependecny(의존), 집합(Aggregation), 합성(Composition)을 나타내는 화살표들이다. 이 화살표들은 객체 다이어그램에서도 똑같이 사용된다. </p>
<pre><code class="language-plain">연관(Association)     ─────          &quot;사용한다&quot;
집합(Aggregation)     ◇────          &quot;가지고 있다&quot; (부분이 독립 존재 가능)
합성(Composition)     ◆────          &quot;구성된다&quot; (부분이 독립 존재 불가)
일반화(Inheritance)   ────▷          &quot;상속이다&quot; (is-a)
의존(Dependency)      ----→          &quot;잠깐 사용한다&quot;
실체화(Realization)   ----▷          &quot;인터페이스 구현&quot;</code></pre>
<ol start="2">
<li><strong>Object Diagram(객체 다이어그램)</strong>
<img src="https://velog.velcdn.com/images/younjin_02/post/3c83f28b-ca5a-474f-b590-4599a5a82907/image.png" alt=""></li>
</ol>
<p><strong>&#39;특정 시점&#39;</strong>의 객체 간의 관계를 표현하는 다이어그램이다.</p>
<ol start="3">
<li><p>*<em>Component Diagram *</em>
<img src="https://velog.velcdn.com/images/younjin_02/post/7ae67ce0-57f3-4bc3-ad3b-dd42ed92e6cb/image.png" alt="">
물리적인 모듈(Component) 간의 구조와 의존성을 표현한다. </p>
</li>
<li><p><strong>Deployment(배치) Diagram</strong>
<img src="https://velog.velcdn.com/images/younjin_02/post/1957da91-f234-42bc-ba27-362331862171/image.png" alt=""></p>
</li>
</ol>
<p><strong>시스템의 물리적인 구조</strong>를 보여주며, 어떤 SW가 어떤 하드웨어에서 동작하고 있는지를 보여준다.</p>
<ol start="5">
<li><p><strong>Composite Structure(복합체 구조) 다이어그램</strong>
<img src="https://velog.velcdn.com/images/younjin_02/post/8147ad40-266c-4a43-af6c-83b6a87eb996/image.png" alt="">
클래스나 컴포넌트가 복합 구조를 갖는 경우 그 내부 구조를 표현한다.</p>
</li>
<li><p><strong>Package Diagram</strong>
<img src="https://velog.velcdn.com/images/younjin_02/post/285087ca-04ae-4066-ae79-135c85ec8438/image.png" alt="">
유스케이스나 클래스 드의 모델 요소들을 그룹화한 패키지들의 관계를 표현한다.</p>
</li>
</ol>
<p>구조적 다이어그램을 정리하면 다음과 같다.
<img src="https://velog.velcdn.com/images/younjin_02/post/2e17b84a-5273-453b-b850-cce53c5409ac/image.png" alt=""></p>
<h4 id="3-3-행위-다이어그램behaviroal--dynamic">3-3. 행위 다이어그램(Behaviroal / Dynamic)</h4>
<p>시스템의 <strong>&#39;동적인 모습&#39;</strong>을 그린다. 영화 시나리오처럼 &quot;어떻게 움직이는가&quot;에 집중한다.</p>
<ol>
<li><strong>Use Case Diagram</strong>
<img src="https://velog.velcdn.com/images/younjin_02/post/8d25351f-c207-4f94-9df5-abd5cd86629d/image.png" alt=""></li>
</ol>
<p><strong>사용자와 시스템 간의 상호작용</strong>을 &#39;기능 위주&#39;로 표현한다. 주로 사람이 주액터에 해당하고, <strong>주액터</strong>의 목적 달성을 위한 외부 시스템인 조직이나 기관이 <strong>부액터</strong>에 해당한다.</p>
<ol start="2">
<li><strong>Sequence Diagarm(순차 다이어그램)</strong>
<img src="https://velog.velcdn.com/images/younjin_02/post/4f0d91bc-1223-439f-986e-85a0209b0867/image.png" alt="">
객체 간 주고받는 메시지를 <strong>&#39;시간 순서&#39;</strong>에 따라 표현한다. &#39;시간 순서&#39;를 표현한다는 점에서 매우 중요하며, 가장 출제 비율이 높은 다이어그램이다.</li>
</ol>
<p>중요한 만큼, 5가지 구성 요소를 살펴 보자.
<img src="https://velog.velcdn.com/images/younjin_02/post/6c5ded61-0331-4358-865f-1100b4bfd463/image.png" alt=""></p>
<ul>
<li><p>Actor: 시스템으로부터 서비스를 받는 <strong>외부 존재</strong>, 사람 모양의 아이콘(stick figure)로 나타낸다.</p>
</li>
<li><p>Object(객체): 메시지를 주고 받는 실체로 &#39;이름:클래스명&#39; 형식으로 표기하며, 사각형(박스)로 나타낸다.</p>
</li>
<li><p><strong>Lifeline(생명선)</strong>: 객체가 메모리에 <strong>존재하는 기간</strong>을 의미하며, 객체 아래로 내려오는 점선으로 표기한다. 위에서 아래로 내려오며, 객체가 소멸하면 끝에 &#39;x&#39;표시를 한다.</p>
</li>
<li><p>Message(메시지): 객체 간에 주고 받는 데이터나 지시 사항으로, 실선, 점선, 화살표 등으로 표현한다. 메시지의 종류는 아래와 같다.
<img src="https://velog.velcdn.com/images/younjin_02/post/7d86bf18-a1b7-461f-bceb-cd91ca5ac127/image.png" alt=""></p>
</li>
<li><p><strong>Active Box(실행 상자)</strong>: 객체가 메시지를 받아 <strong>실제로 동작 중인 시간</strong>을 의미하며, 생명선 위에 그려지는 긴 직사각형으로 표기한다. 이 박스가 길다는 것은 해당 객체가 그만큼 오랫동안 일을 하고 있다는 것을 의미한다.</p>
</li>
</ul>
<ol start="3">
<li><p><strong>Communication Diagram</strong>
<img src="https://velog.velcdn.com/images/younjin_02/post/2142041e-8037-47a6-9e06-ae6eaed7cb2b/image.png" alt="">
객체 간의 메시지 전달 뿐만 아니라 <strong>&#39;연관 관계&#39;</strong> 자체에 집중한다.</p>
</li>
<li><p><strong>State Diagram(상태 다이어그램)</strong>
<img src="https://velog.velcdn.com/images/younjin_02/post/3c6472b5-4e22-427c-a75a-e7b906916b59/image.png" alt="">
하나의 객체가 가질 수 있는 <strong>&#39;상태의 변화&#39;</strong>를 표현한다. 다른 객체와의 상호 작용에 따른 상태 변화도 표시하며, 모든 가능한 상태와 전이를 표현할 수 있다.</p>
</li>
<li><p><strong>Activity Diagram(활동 다이어그램)</strong>
<img src="https://velog.velcdn.com/images/younjin_02/post/2c9c1757-c8f3-4748-814b-142f874c918b/image.png" alt="">
시스템이 어떤 기능을 수행하는지를 <strong>개체의 처리 로직이나 조건에 따른 처리의 흐름</strong>으로 순서대로 표현한다.</p>
</li>
</ol>
<p>행위 다이어그램을 정리하자면 아래와 같다.
<img src="https://velog.velcdn.com/images/younjin_02/post/9ba883bc-b44a-4b09-a293-51672bddb47b/image.png" alt=""></p>
<p><strong>*Stereotype</strong>
: UML의 기본 도형(사각형, 화살표)만으로는 표현하기 부족한 <strong>추가적인 의미</strong>를 덧붙일 때 사용한다.
<strong>&#39;Guilemet(길러멧)&#39;</strong>이라고 불리는 겹화살표(≪ ≫) 사이에 적는다.</p>
<ul>
<li>≪interface≫: 해당 클래스가 인터페이스임을 명시</li>
<li>≪include≫: 유스케이스 사이의 &#39;포함&#39; 관계</li>
<li>≪extend≫: 유스케이스 사이의 &#39;확장&#39; 관계</li>
<li>≪exception≫: 예외 처리 관련 내용임을 명시</li>
</ul>
<p><strong>*Rumbaugh(럼바우) 분석 기법(객·동·기)★</strong>
: 모든 SW 객체를 <strong>객체 -&gt;  동적 -&gt; 기능</strong> 모델링 순서로 분석하는 기법이다.</p>
<ol>
<li>1단계: 객체 모델링; 시스템의 정적 구조를 파악하며 <strong>객체 다이어그램</strong>을 사용한다.(객객)</li>
<li>2단계: 동적(Dynamic) 모델링; 객체들 간의 제어 흐름, 상태 변화를 파악하며 <strong>상태 다이어그램</strong>을 사용한다.(동상)</li>
<li>3단계: 기능 모델링; 어떤 데이터가 입력되어 어떤 결과가 나오는지 분석하며, <strong>자료 흐름도(DFD)</strong>를 사용한다. (기자)</li>
</ol>
<h2 id="4-user-interface-ui">4. User Interface (UI)</h2>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/13aa0dca-a978-4886-9862-8fa30e3011e9/image.png" alt=""></p>
<blockquote>
<p><strong>사용자 인터페이스(UI, User Interface)는 사용자와 디지털 기기/시스템 간의 상호장욕을 매개하는 시각적, 기능적 요소(버튼, 화면, 아이콘, 레이아웃)을 의미한다.</strong></p>
</blockquote>
<h4 id="4-1-ui-구분">4-1. UI 구분</h4>
<p>UI는 사용자와 시스템이 어떻게 소통하느냐에 따라 크게 3가지로 나눌 수 있다.</p>
<ol>
<li><strong>CLI(Command Line Inteface)</strong>: 명령어를 <strong>직접 텍스트로 입력</strong>하는 방식 ex. DOS, Linux Terminal</li>
<li><strong>GUI(Graphical User Interface)</strong>: <strong>마우스와 아이콘</strong>을 이용해 그래픽으로 소통 ex. Windows, macOS, 모바일 앱</li>
<li><strong>NUI(Natural User Interface)</strong>: <strong>신체 부위, 음성</strong> 등의 인간의 자연스러운 행동 이용 ex. Siri, 키네틱 게임, 지문 인식 등</li>
</ol>
<p>*VUI(Voice User Interface, 음성)
*OUI(Organic User Interface, 모든 사물이 인터페이스가 됨)</p>
<h4 id="4-2-ui-설계의-4대-원칙">4-2. UI 설계의 4대 원칙</h4>
<p>&#39;직유학유&#39;</p>
<ol>
<li><strong>직관성(Intuitiveness)</strong>: 굳이 설명하지 않아도 직관적으로 사용법을 알 수 있어야 한다.</li>
<li><strong>유효성(Efficiency)</strong>: 사용자가 원하는 <strong>목적을 정확하고 완벽하게 달성</strong>해야 한다(오류 없이)</li>
<li><strong>학습성(Learnability)</strong>: 처음 쓰는 사람도 배우기 쉬워야 하며, 한 번 배우면 잊어버리지 않아야 한다.</li>
<li><strong>유연성(Flexibility)</strong>: 사용자의 실수나 요구사항을 <strong>최대한 수용하고 오류를 방지</strong>해야 한다. </li>
</ol>
<h4 id="4-3-ui-설계-도구">4-3. UI 설계 도구</h4>
<ol>
<li><strong>Wirefrmae</strong>
<img src="https://velog.velcdn.com/images/younjin_02/post/09c687e3-905e-433b-890d-bbe8d1b5240a/image.png" alt=""></li>
</ol>
<ul>
<li>화면의 뼈대(레이아웃)만 그리는 단계. 텍스트와 상자 위주</li>
<li>정밀도는 낮음</li>
</ul>
<ol start="2">
<li><strong>Mockup</strong></li>
</ol>
<ul>
<li>정적인 형태와 실제 화면과 유사한 디자인 결과물</li>
<li>정밀도는 중간</li>
<li>실제 동작하지는 않음(버튼을 누른다고 화면이 넘어가지는 않음)</li>
</ul>
<ol start="3">
<li><strong>Storyboard</strong>
<img src="https://velog.velcdn.com/images/younjin_02/post/94401a39-5ce5-4a8b-b2a2-a87a46154616/image.png" alt=""></li>
</ol>
<ul>
<li>와이어프레임에 콘텐츠 설명, interaction을 상세히 적어놓은 문서</li>
<li>정밀도는 높은 편</li>
</ul>
<ol start="4">
<li><strong>Prototype</strong></li>
</ol>
<ul>
<li>실제 서비스처럼 동작이 가능하게 만든 동적 모형(시제품)</li>
<li>테스트용</li>
<li>정밀도는 매우 높은 편</li>
</ul>
<ol start="5">
<li><strong>Use Cse</strong></li>
</ol>
<ul>
<li>사용자 입장에서 시스템의 기능을 시나리오로 작성한 것</li>
<li>설계 도구 보다는 &#39;사용자 중심의 기능 정의서&#39;</li>
<li>논리적</li>
</ul>
<h4 id="isoiec-9126의-품질-특성">*ISO/IEC 9126의 품질 특성</h4>
<blockquote>
<p><strong>소프트웨어 품질을 6가지로 나눈 국제 표준</strong></p>
</blockquote>
<p>&#39;기신사효유이&#39;</p>
<ul>
<li><strong>기능성(Functionality)</strong>: &#39;기능이 제대로 작동하는가&#39;</li>
<li><strong>신뢰성(Reliability)</strong>: &#39;고장 없이 잘 돌아가며, 장애 발생 시 복구 가능한가&#39;</li>
<li><strong>사용성(Usability)</strong>: &#39;학습하기 쉬운가&#39;</li>
<li><strong>이식성(Portability)</strong>: &#39;다른 환경(다른 OS)에서도 실행 가능한가&#39;</li>
<li><strong>효율성(Efficiency)</strong>: &#39;자원 낭비 없이 빠른가(CPU 사용량)&#39;</li>
<li><strong>유지보수성(Maintainability)</strong>: &#39;고치기 쉬운가&#39;</li>
</ul>
<h2 id="5-software-architecture">5. Software Architecture</h2>
<blockquote>
<p>SW 아키텍처의 기본 설계 과정은 <strong>&#39;설계 목표 설정 -&gt; 시스템 타입 결정 -&gt; 아키텍처 패턴 적용 -&gt; 서브시스템 구체화 -&gt; 검토&#39;</strong>의 순서를 따른다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/86adc91a-bf58-4003-bb0b-f3bb8494c033/image.png" alt="">
<img src="https://velog.velcdn.com/images/younjin_02/post/f8125eb6-216f-4c8a-bfbf-890fa30f2a40/image.png" alt=""></p>
<blockquote>
<p><strong>SW Architecture는 상위&amp;하위 설계로 나눈다.</strong> </p>
</blockquote>
<p>각 상위 &amp; 하위에 어떤게 속하는지 꼭 기억해두자.</p>
<h4 id="5-1-상위-설계큰-개념">5-1. 상위 설계(큰 개념)</h4>
<p>시스템의 <strong>전체적인 구조</strong>를 결정한다.</p>
<ul>
<li><strong>아키텍처 설계</strong>: 시스템의 전체적이 구조 정의한다</li>
<li><strong>데이터 설계</strong>: 시스템에 필요한 정보를 어떤 구조로 저장할지 정의한다</li>
<li><strong>인터페이스 정의</strong>: 시스템 내부 구성 요소들이 서로 어떻게 대화할지 정한다</li>
<li><strong>UI 설계</strong>: 사용자가 보는 화면의 외관을 설계한다.</li>
</ul>
<h4 id="5-2-하위-설계모듈-설계">5-2. 하위 설계(모듈 설계)</h4>
<p>상위 구조에서 정한 큰 흐름의 내부를 구체적으로 설계한다.
<strong>&#39;하위 설계 - 모듈 설계 - 컴포넌트&#39;</strong>를 묶어서 외워두자.</p>
<ul>
<li><strong>모듈 설계</strong>: 각 구성 요소의 내부 기능을 정의한다.</li>
<li><strong>자료 구조 설계</strong>: 모듈 내부에서 데이터를 어떤 방식으로 처리할지 상세히 정한다.</li>
<li><strong>알고리즘 설계</strong>: 구체적인 로직이나 처리 순서를 설계한다. </li>
</ul>
<h4 id="5-3-하향식-설계-top-down-vs-상향식-설계-bottom-up">5-3. 하향식 설계 (Top-Down) vs 상향식 설계 (Bottom-Up)</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/6d227189-6a0e-439a-8751-bbc14293a7bf/image.png" alt=""></p>
<p>하향식 설계 및 상향식 설계를 할 때 테스트 환경의 보조 도구로 Stub과 Driver를 사용한다.
<img src="https://velog.velcdn.com/images/younjin_02/post/e2b81749-3e0e-415d-81b2-8960e1bf4f1a/image.png" alt=""></p>
<h4 id="hipo">*HIPO</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/a3a432be-8203-4974-b37b-253c8773bdca/image.png" alt=""></p>
<p><strong>하향식 소프트웨어 개발을 위한 문서화 도구</strong>이다. </p>
<ul>
<li>기호, 도표 등을 사용하므로 보기 쉽고, 이해하기도 쉽다. </li>
<li>기능과 자료의 의존 관계를 동시에 표현할 수 있다.</li>
</ul>
<h4 id="파이프-필터-패턴">*파이프-필터 패턴</h4>
<p>아키텍처 패턴 중 하나로, <strong>데이터를 단계별로 처리하는데, 각 단계가 필터 역할을 하고 데이터 전달 통로가 파이프라고 생각</strong>하면 된다. 데이터 변환으로 인한 오버헤드가 발생한다.</p>
<h4 id="후기">후기</h4>
<p>역시 기사라 그런지 내용이 많긴 한것 같다. 전공 공부하면서 다 한번씩 봤던 내용이라 쓱 보면 되겠지,, 했는데 확실히 시간 투자를 많이 할 필요가 있을 것 같다. 꼭 자격증 때문만은 아니더라도 앞으로 내가 더 공부할 여러 지식들의 기반이 되는 지식이니까 기초를 쌓자라는 느낌으로 더 열심히 공부해봐야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kotlin in Action] 04. 클래스, 객체, 인터페이스]]></title>
            <link>https://velog.io/@younjin_02/Kotlin-in-Action-04.-%ED%81%B4%EB%9E%98%EC%8A%A4-%EA%B0%9D%EC%B2%B4-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4</link>
            <guid>https://velog.io/@younjin_02/Kotlin-in-Action-04.-%ED%81%B4%EB%9E%98%EC%8A%A4-%EA%B0%9D%EC%B2%B4-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4</guid>
            <pubDate>Tue, 12 Aug 2025 19:06:58 GMT</pubDate>
            <description><![CDATA[<p>이번 장에서는 코틀린 클래스 다루는 방법을 살펴본다. 자바와 달리 코틀린은 <strong>인터페이스에 프로퍼티 선언이 들어갈 수 있고, 선언이 기본적으로 final이며 public이다.</strong></p>
<p>또한 <strong>내포 클레스(nested class, 중첩 클래스)는 기본적으로 내부 클래스가 아니다</strong>. 내포 클래스에는 외부 클래스에 대한 암시적 참조가 없다. 이외에 다양한 코틀린 만의 객체지향적인 특성은 앞으로 더 본격적으로 알아가보자. </p>
<h3 id="1-클래스-계층-정의">1. 클래스 계층 정의</h3>
<h4 id="11-코틀린-인터페이스">1.1. 코틀린 인터페이스</h4>
<blockquote>
<p>인터페이스란, <strong>&quot;이 기능을 제공할 거라고 약속만 하는 추상적 구조&quot;</strong>를 뜻하며, 함수 이름만 정의하고 내용은 쓰지 않는다.</p>
</blockquote>
<p>하지만 <strong>코틀린 인터페이스 안에는 추상 메서드 뿐만 아니라 구현이 있는 메서드도 정의할 수 있다.</strong> 다만 인터페이스에는 아무런 상태도 들어갈 수 없다. </p>
<pre><code class="language-kotlin">interface Clickable {
    fun click() //추상 메서드
}</code></pre>
<p>이 인터페이스를 받아 버튼을 클릭할 수 있는 클래스를 생성해보자.</p>
<pre><code class="language-kotlin">class Button: Clickable {
    override fun click() = println(&quot;I was clicked&quot;)
}

fun main(){
    Button().click() //I was clicked, Button에 괄호를 붙여야 객체가 생성됨
}</code></pre>
<blockquote>
<p><strong>코틀린의 상속이나 구성에서 모두 클래스 이름 뒤에 콜론(:)을 붙이고 인터페이스나 클래스 이름을 적는다.</strong></p>
</blockquote>
<p>클래스는 인터페이스를 원하는 만큼 개수 제한 없이 구현할 수 있지만, 클래스는 오직 &#39;하나만&#39; 확장 가능하다. </p>
<blockquote>
<p><strong>상위 클래스나 상위 인터페이스에 있는 프로퍼티나 메서드를 오버라이드 할 때는 꼭 override 변경자를 사용해야 한다.</strong> </p>
</blockquote>
<p>*<em>인터페이스 메서드는 디폴트 구현도 제공 가능하다. *</em> 즉, 내용이 비어있는 메서드만 넣을 수 있지 않고, 기본 출력 정도의 디폴트 구현은 가능하다는 말이다. </p>
<pre><code class="language-kotlin">interface Clickable {
    fun click()
    fun showoff() = println(&quot;I&#39;m clickable!&quot;) //디폴트 구현이 있는 메서드
}</code></pre>
<p>이렇게 디폴트 구현이 된 showOff 메서드의 경우에는 새로운 동작을 정의할 수도 있고 그냥 정의를 생략해서 디폴트 구현 그대로 사용할 수도 있다. 주의할 점은, 이런 <strong>디폴트 구현이 있는 메서드가 한 클래스에서 두 인터페이스에서 구현된다면, 어느 쪽도 선택되지 않고 컴파일러 오류를 발생시킨다.</strong> </p>
<p>따라서 코틀린 컴파일러는 두 메서드를 아우르는 구현을 하위 클래스에 직접 다음과 같이 구현하도록 한다.</p>
<pre><code class="language-kotlin">
class Button : Clickable, Focusable {
    override fun click() = println(&quot;I was clicked&quot;)

    override fun showOff() {
        super&lt;Clickable&gt;.showOff()
        super&lt;Focusable&gt;.showOff()
    }
}</code></pre>
<blockquote>
<p>위와 같이 상위 타입의 구현을 호출할 때는 <strong>super</strong>를 사용해 상위 타입의 이름을 &lt;&gt;안에 넣어 사용한다.(자바는 Clickable.super.showOff과 같은 형식)</p>
</blockquote>
<p>참고로, *<em>자바에서 코틀린의 인터페이스를 받아 구현할 때는 코틀린의 디폴트 구현을 사용할 수 없어 따로 디폴트 메서드를 직접 구현해주어야 한다. *</em></p>
<h4 id="12-open-final-abstract-변경자-기본적으로-final">1.2. open, final, abstract 변경자: 기본적으로 final</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/15466aaf-c455-456e-9096-cc4fec93af78/image.png" alt=""></p>
<p>기반 클래스의 개념부터 짚고 넘어가자. 다른 클래스가 상속해서 기능을 확장하거나 재정의할 수 있는 ‘출발점’ 클래스를 기반 클래스라고 정의한다. &#39;final&#39; 변경자는 상속이나 오버라이드가 불가하다는 의미이다. </p>
<blockquote>
<p>*<em>기본적으로 코틀린 클래스는 하위 클래스를 만들 수도 없고, 기반 클래스의 메서드를 하위 클래스가 오버라이드 할 수 없다. 즉, 기본적으로 코틀린에서 모든 클래스와 메서드는 기본적으로 final이다. *</em></p>
</blockquote>
<p>이는 자바와 다르다. 자바는 final로 지정하지 않는 한 모든 클래스가 상속할 수 있고, 모든 메서드를 하위 클래스에서 오버라이드 할 수 있는데, 이는 <strong>편리한 반면 문제가 될 수도 있기 때문에 코틀린에서는 이러한 방식을 채택하지 않았다.</strong></p>
<p>&#39;취약한 기반 클래스(fragile base class)&#39;, 기반 클래스를 상속하는 정확한 방법을 제시하지 않는 경우, 기반 클래스의 의도와 다른 방식으로 메서드를 오버라이드 할 위험이 존재한다. 따라서 코틀린은 이러한 위험을 피하고자 다음과 같은 철학을 따른다.</p>
<blockquote>
<p><strong>&quot;상속을 위한 설계와 문서를 갖춰라. 그럴 수 없다면 상속을 금지하라.&quot;</strong></p>
</blockquote>
<p>그래서 모든 클래스와 메서드는 기본적으로 &#39;final&#39;인 것이다. 따라서 <strong>어떤 클래스의 상속을 허용하려면 open 변경자를 붙여야 한다.</strong> </p>
<pre><code class="language-kotlin">open class RichButton : Clickable { //열려 있음. 다른 클래스가 이 클래스 상속 가능
    fun disable()  //final로 하위 클래스가 오버라이드 불가
    open fun animate() {}
    override fun click() {} //열려있음
}</code></pre>
<p>*<em>기반 클래스나 인터페이스의 멤버를 오버라이드 한 경우(위에서 마지막 줄)에는 기본적으로 open으로 간주된다. *</em> 이 함수를 오버라이드 하는 것을 금지하려면 명시적으로 final로 표시해주어야 한다. </p>
<blockquote>
<p><strong>클래스의 기본적인 상속 가능 상태가 final이기 때문에 대부분의 프로퍼티가 스마트 캐스트에 활용될 수도 있는 것</strong>이다. 클래스 프로퍼티의 경우 val이면서 커스텀 접근자가 없는 경우에만 스마트 캐스트가 가능하기 때문이다. </p>
</blockquote>
<p>abstract로 선언된 추상 클래스의 경우에는 항상 열려 있어서 open     변경자를 명시할 필요는 없다. </p>
<h4 id="13-가시성-변경자-기본적으로-공개">1.3. 가시성 변경자: 기본적으로 공개</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/b8c05ad8-3ab1-476a-bbd6-9c9f377bb06b/image.png" alt=""></p>
<blockquote>
<p>가시성 변경자란, <strong>코드 기반에 있는 선언에 대한 클래스 외부 접근을 제어</strong>함으로써, 그 <strong>클래스에 의존하는 외부 코드를 깨지 않고도 클래스 내부 구현을 변경</strong>할 수 있다. </p>
</blockquote>
<p>코틀린은 <strong>public, protected, private</strong> 변경자를 제공한다. 아무 변경자가 없으면 기본적으로 public이다.</p>
<ul>
<li>public: 누구나 볼 수 있음</li>
<li>protected: 하위 클래스에서만 볼 수 있음</li>
<li>private: 그 선언이 포함된 클래스 안에서만 볼 수 있음</li>
<li>internal: 모듈 안으로만 한정된 가시성 제공</li>
</ul>
<blockquote>
<p>모듈이란, 함께 컴파일되는 코틀린 파일의 집합을 의미한다. ex; gradle 소스 집합, Maven 프로젝트, IntelliJ IDEA module</p>
</blockquote>
<p>예시를 하나 들어보자. </p>
<pre><code class="language-kotlin">internal open class TalkativeButton {
  private fun yell() = println(&quot;Hey!&quot;)
  protected fun whisper() = println(&quot;Let&#39;s talk!&quot;)
}

fun TalkativeButton.giveSpeech() { //pubilc 멤버가 internal 수신 타입 접근 불가
  yell() //private 멤버이므로 접근할 수 없음
  whisper() //protected 멤버이므로 접근할 수 없음
}</code></pre>
<p>*<em>public 함수인 giveSpeech 안에서 그보다 가시성이 낮은 internal 가시성은 참조하지 못하게 한다. *</em> 이러한 규칙이 어떤 함수를 호출하거나 어떤 클래스를 확장할 때 필요한 모든 타입에 접근할 수 있도록 보장해준다. 위의 코드에서 컴파일 오류를 없애려면 TalktiveButton의 가시성을 public으로 바꾸거나 giveSpeech 함수의 가시성을 internal로 바꿔야 한다. </p>
<blockquote>
<p>protected 멤버는 오직 어떤 클래스나 그 클래스를 상속한 클래스 안에서만 보이기 때문에, *<em>클래스를 확장한 함수는 그 클래스의 private이나 protected 멤버에 접근할 수 없다. *</em></p>
</blockquote>
<h4 id="14-내부-클래스와-내포된-클래스-기본적으로-내포-클래스">1.4. 내부 클래스와 내포된 클래스: 기본적으로 내포 클래스</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/c13c62da-2bca-478c-811e-fbe6950d7615/image.png" alt=""></p>
<blockquote>
<p>*<em>코틀린의 내포 클래스(nested class)는 명시적으로 요청하지 않는 한 바깥쪽 클래스 인스턴스에 접근이 불가하다. 이게 외부 인스턴스에 접근이 가능한 inner 클래스와의 대표적인 차이점이다. *</em></p>
</blockquote>
<p>View 요소(UI를 구성하는 화면상의 모든 객체)를 하나 만드는 코드를 예시로 들어보겠다. view 상태를 <strong>직렬화(객체의 상태를 파일, 메모리, 네트워크 전송 등에 쓰기 좋은 형식인 바이트, 문자열 등으로 변환하는 과정)</strong>하기 위해 State 인터페이스를 선언하고 Serializable을 구현한다. </p>
<pre><code class="language-kotlin">import java.io.Serializable

interface State: Serializable

interface View {
    fun getCurrentState(): State
    fun restoreState(state: State) {}
}

class Button : View {
    override fun getCurrentState(): State = ButtonState()

    override fun restoreState(state: State) { /*...*/ }

    class ButtonState : State { /*...*/ }
}</code></pre>
<p>코틀린에서 내포된 클래스에 아무 변경자가 없으면 java static 내포 클래스와 같다. 이를 <strong>내부(inner) 클래스로 변경해서 바깥쪽 클래스에 대한 참조를 포함하게 만들고 싶다면 inner 변경자를 붙여야 한다.</strong> </p>
<table>
<thead>
<tr>
<th>구분</th>
<th>정의</th>
<th>자바(Java)</th>
<th>코틀린(Kotlin)</th>
</tr>
</thead>
<tbody><tr>
<td><strong>내포 클래스</strong><br>(바깥쪽 클래스에 대한 참조를 저장하지 않음)</td>
<td>외부 클래스 인스턴스 참조 없이 독립적으로 존재하는 클래스</td>
<td><code>static class A</code></td>
<td><code>class A</code></td>
</tr>
<tr>
<td><strong>내부 클래스</strong><br>(바깥쪽 클래스에 대한 참조를 저장함)</td>
<td>외부 클래스 인스턴스를 참조할 수 있는 클래스</td>
<td><code>class A</code></td>
<td><code>inner class A</code></td>
</tr>
</tbody></table>
<p>내부 클래스에서 바깥쪽 클래스의 인스턴스를 가리키는 참조를 표기할 때는 <strong>this@Outer</strong> 라고 표기해주면 된다. </p>
<h4 id="15-봉인된-클래스-확장이-제한된-클래스-계층-정의">1.5. 봉인된 클래스: 확장이 제한된 클래스 계층 정의</h4>
<p>상위 클래스를 표현하는 여러 하위 클래스들을 표현할 때 when식을 사용하면 편리하다. 하지만 반드시 else 분기를 넣어줘야만 한다.</p>
<p>하지만 디폴트 분기를 추가하게 되면 컴파일러가 when이 모든 경우를 처리하는지 제대로 검사할 수 없기 때문에 심각한 버그를 발생시킬 수 있다. *<em>코틀린은 이런 문제를 해결하고자 sealed 클래스(봉인된 클래스)를 상위 클래스에 변경자로 붙여 이를 상속한 하위 클래스의 가능성을 제한시킨다. 
*</em></p>
<blockquote>
<p><strong>sealed class(봉인 클래스)는 상속 가능한 자식 클래스의 종류를 “컴파일 시점에” 딱 정해 놓는 클래스</strong>다.</p>
</blockquote>
<pre><code class="language-kotlin">sealed class Expr {
class Num(val value: Int) : Expr()
class Sum(val left: Expr, val right: Expr) : Expr()
}

fun eval(e: Expr): Int =
    when (e) {
        is Num -&gt; e.value
        is Sum -&gt; eval(e.right) + eval(e.left)
    }</code></pre>
<p>when 식에서 sealed 클래스의 모든 하위 클래스를 처리한다면 디폴트 분기가 필요 없다. 컴파일러가 여러분이 모든 분기를 처리하는지 확인해준다.  또한 *<em>sealed 변경자는 클래스가 추상 클래스임을 명시해준다. *</em></p>
<blockquote>
<p><strong>코틀린에서는 클래스를 확장할 때나 인터페이스를 구현할 때 모두 콜론(:)을 사용한다는 것을 기억하자.</strong></p>
</blockquote>
<h3 id="2-뻔하지-않은-생성자나-프로퍼티를-갖는-클래스-선언">2. 뻔하지 않은 생성자나 프로퍼티를 갖는 클래스 선언</h3>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/b1e213f2-de2a-4796-b485-82693e9a0669/image.png" alt=""></p>
<p>객체지향 언어에서 클래스는 보통 생성자를 하나 이상 추가할 수 있는데, 코틀린에선 *<em>주 생성자(클래스 본문 밖에서 정의)와 부 생성자(클래스 본문 안에서 정의)를 구분한다. *</em> 또한 초기화 블록을 통해 초기화 로직을 추가할 수 있다. </p>
<h4 id="21-주-생성자와-초기화-블록">2.1. 주 생성자와 초기화 블록</h4>
<p>이전에 코틀린에서 클래스를 간단히 선언하는 방법을 공부했었다. </p>
<pre><code class="language-kotlin">class User(val nickname: String)</code></pre>
<p><strong>이렇게 클래스 이름 뒤에 오는 중괄호가 아닌, 괄호로 둘러싸인 코드를 주 생성자라고 한다.</strong> 위 코드를 명시적으로 푼 선언은 아래와 같다.</p>
<pre><code class="language-kotlin">class User constructor(_nickname: String) {
    val nickname: String

    init {
        nickname = _nickname
    }
}</code></pre>
<blockquote>
<p>&#39;<strong>constructor</strong>&#39; 키워드는 주 생성자나 부 생성자 정의를 시작할 때 사용한다.
 &#39;<strong>init</strong>&#39; 키워드는 초기화 블록을 시작한다.</p>
</blockquote>
<blockquote>
</blockquote>
<p>*<em>초기화 블록에는 클래스의 객체가 만들어질 때(인스턴스화 될 때) 실행될 초기화 코드가 들어가며, 주 생성자와 함께 사용된다. *</em> 주 생성자는 초기화 블록이 필요하다.</p>
<p>생성자 파라미터 맨 앞의 &#39;_&#39;는 프로퍼티와 생성자 파라미터를 구분해준다. 자바에서 흔히 쓰는 this.nickname = nickname의 this 역할이라고 보면 된다. constructor는 별다른 어노테이션이나 가시성 변경자가 없다면 생략하는 편이 낫다. 따라서 위의 명시적 선언을 다음과 같이 추려서 적을 수 있다. </p>
<pre><code class="language-kotlin">class User(_nickname: String) {
    val nickname = _nickname
}</code></pre>
<p><strong>초기화 블록 안에서만 주 생성자의 파라미터(위 코드에서 &#39;_nickname&#39;)를 참조할 수 있다는 점</strong>에 유의하자. 위 코드를 다시 한 번 간추려 val 키워드를 앞에 추가하는 식으로 더욱 간략히 쓸 수 있다. <del>아까 그냥 처음거잖아 ㅆ</del></p>
<pre><code class="language-kotlin">class User(val nickname: String)</code></pre>
<p><strong>클래스의 인스턴스를 만들려면 new 키워드 없이 생성자를 직접 호출하면 된다.</strong> </p>
<pre><code class="language-kotlin">
class User(val nickname: String,
           val isSubscribed: Boolean = true)

fun main() {
    val alice = User(&quot;Alice&quot;)
    println(alice.isSubscribed)
    val bob = User(&quot;Bob&quot;, false)
    println(bob.isSubscribed)
    val carol = User(&quot;Carol&quot;, isSubscribed = false)
    println(carol.isSubscribed)
}</code></pre>
<p>참고로 클래스를 정의할 때 별도로 생성자를 정의하지 않으면 컴파일러가 자동으로 아무 일도 하지 않는 인자가 없는 디폴트 생성자를 만들어준다. 만약 기반 클래스의 생성자가 아무 인자도 받지 않지만, 해당 클래스를 상속하는 하위 클래스는 반드시 <strong>기반 클래스의 생성자를 호출하여야 하는데, 이때 기반 클래스의 생성자가 아무 인자도 받지 않는다면 호출할 때 빈괄호를 추가해주면 된다</strong>. </p>
<pre><code class="language-kotlin">class RadioButton: Button()</code></pre>
<blockquote>
<p>*<em>인터페이스는 생성자가 없어 어떤 클래스가 인터페이스를 구현하는 경우에는 아무 괄호도 붙지 않는다. 이 특징으로 기반 클래스와 인터페이스를 쉽게 구별할 수 있다. *</em></p>
</blockquote>
<p>어떤 클래스를 외부에서 인스턴스화하지 못하게 막고 싶은 경우에 private 생성자를 붙이면 된다. 자바에서는 유틸리티 클래스(편의 기능을 모아둔 클래스, 주로 static으로 구성)이나 싱글턴 클래스(프로그램 전체에서 하나의 인스턴스만 존재하도록 보장하는 클래스)에 대해 private 생성자를 정의해서 클래스를 다른 곳에서 인스턴스화 하지 못하게 막는데, *<em>코틀린의 경우에는 정적 유틸리티 함수 대신 최상위 함수를 사용하거나, 싱글턴을 사용하고 싶은 경우에는 그냥 object로 객체를 선언해주면 된다. *</em></p>
<pre><code class="language-java">public class MathUtils { //java에서 유틸리티 클래스 정의
    public static int add(int a, int b) {
        return a + b;
    }
}

int result = MathUtils.add(3, 5); //사용</code></pre>
<pre><code class="language-kotlin">// MathUtils.kt
fun add(a: Int, b: Int): Int { //파일의 최상단(클래스 외부)에 정의
    return a + b
}

val result = add(3, 5) // 바로 호출 가능</code></pre>
<h4 id="22-부-생성자-상위-클래스를-다른-방식으로-초기화">2.2. 부 생성자: 상위 클래스를 다른 방식으로 초기화</h4>
<p>대부분의 경우에 간단한 주 생성자 문법으로만 클래스를 정의해도 괜찮지만, 때때로 부 생성자가 필요한 경우가 있다. 다음과 같은 예를 확인해보자. </p>
<pre><code class="language-java">public class Downloader { 
    public Downloader(String url){//java에서 생성자가 두 개인 경우
        //
    }

    public Downloader(URI uri){
        //
    }
}</code></pre>
<pre><code class="language-kotlin">open class Downloader { //위의 클래스를 코틀린에서 정의
    constructor(url: String?){ //?는 null 타입을 허용한다는 뜻
        //
    }

    constructor(uri: URI?){
        //
    }
}</code></pre>
<blockquote>
<p>부 생성자는 <strong>constructor 키워드로 시작</strong>하고, <strong>필요에 따라 얼마든지 많이 부 생성자를 선언</strong>해도 된다. </p>
</blockquote>
<pre><code class="language-kotlin">class MyDownloader: Downloader {
    constructor(url: String?) : super(url){

    }

    constructor(url: String?) : super(uri) {

    }
}</code></pre>
<p>여기서 두 개의 부 생성자는 <strong>super() 키워드를 사용해 자신에 대응하는 상위 클래스 생성자를 호출해 초기화를 위임(부모 클래스에게 초기화를 넘겨서 초기화를 맡김)</strong>한다. 반대로 this() 키워드를 사용해 같은 클래스의 다른 생성자에게 생성을 위임할 수 있다. </p>
<pre><code class="language-kotlin">class MyDownloader: Downloader {
    constructor(url: String?) : this(URI(url)){ //아래의 생성자에게 위임

    }

    constructor(url: String?) : super(uri) {

    }
}</code></pre>
<p>클래스에 주 생성자가 없다면 모든 부 생성자는 반드시 상위 클래스를 초기화하거나 다른 생성자에게 생성을 위임해야 한다는 걸 기억하자. 보통 부 생성자는 자바와의 상호운용성이 생성의 주 목적이다. </p>
<h4 id="23-인터페이스에-선언된-프로퍼티-구현">2.3. 인터페이스에 선언된 프로퍼티 구현</h4>
<p>코틀린에서는 인터페이스에 추상 프로퍼티 선언을 넣을 수 있다.</p>
<pre><code class="language-kotlin">interface USer {
  val nickname: String
}</code></pre>
<p>이는 이 인터페이스를 구현하는 클래스가 nickname의 값을 얻을 수 있는 방법을 제공해야 한다는 뜻이다. 이 인터페이스를 구현하는 방법을 몇 가지 살펴보자. </p>
<pre><code class="language-kotlin">fun getNameFromSocialNetwork(accountId: Int) = &quot;soc:$accountId&quot;

class PrivateUser(override val nickname: String) : User //주 생성자에 있는 프로퍼티

class SubscribingUser(val email: String) : User {
    override val nickname: String
        get() = email.substringBefore(&#39;@&#39;) //커스텀 게터
}

class SocialUser(val accountId: Int) : User {
    override val nickname = getNameFromSocialNetwork(accountId) //프로퍼티 초기화 식
}

fun main() {
    println(PrivateUser(&quot;test@kotlinlang.org&quot;).nickname) //test@kotlinlang.org
    println(SubscribingUser(&quot;test@kotlinlang.org&quot;).nickname) //test
} </code></pre>
<p>SubscribingUSer와 SocialUser의 nickname 구현 차이에 주의하라. 전자는 nickname이 호출될 때마다 substringBefore를 호출해 계산하는 커스텀 게터를 활용하고, SocialUser의 nickname은 객체 초기화를 계산한 데이터를 뒷받침하는 필드에 저장했다가 불러오는 방식을 활용한다. </p>
<blockquote>
<p>함수 대신 프로퍼티를 주로 사용하는 경우</p>
</blockquote>
<ul>
<li>예외를 던지지 않음</li>
<li>계산 비용이 적게 듬(최초 실행 후 결과를 cache해 사용할 수 있는 경우)</li>
<li>객체 상태가 바뀌지 않는 경우</li>
</ul>
<h4 id="24-getter와-setter에서-뒷받침하는-필드에-접근">2.4. Getter와 Setter에서 뒷받침하는 필드에 접근</h4>
<p>바로 앞에서 봤던 두 가지 유형을 합쳐 활용하는 유형의 프로퍼티를 만드는 방법을 살펴보자. 어떤 값을 저장하되 그 값을 변경하거나 읽을 때마다 정해진 로직을 실행하는 유형의 프로퍼티인데, 이런 경우엔 <strong>프로퍼티를 뒷받침하는 필드에 접근할 수 있어야 하므로 변경 가능한 프로퍼티를 정의하되 세터에서 프로퍼티를 값을 바꿀 때마다 약간의 코드를 추가</strong>한다. </p>
<pre><code class="language-kotlin">class User(val name: String) {
    var address: String = &quot;unspecified&quot; //가변 프로퍼티(var)
        set(value: String) {  //커스텀 setter
            println(
                &quot;&quot;&quot;  //원시 문자열만들기(&quot;&quot;&quot;)
                Address was changed for $name:
                &quot;$field&quot; -&gt; &quot;$value&quot;.
                &quot;&quot;&quot;.trimIndent()
            )
            field = value
        }
}

fun main() {
    val user = User(&quot;Alice&quot;)
    user.address = &quot;Christoph-Rapparini-Bogen 23, 80639 Muenchen&quot;
}</code></pre>
<blockquote>
<p>접근자의 본문에서는 <strong>&#39;field&#39;</strong>라는 식별자를 통해 뒷받침하는 필드에 접근할 수 있다. <strong>프로퍼티의 getter·setter 안에서만 접근 가능한, 해당 프로퍼티의 실제 저장소를 가리키는 특별한 키워드</strong>로, 커스텀 getter &amp; setter를 정의할 때 주로 사용한다. </p>
</blockquote>
<h4 id="25--접근자의-가시성-변경">2.5.  접근자의 가시성 변경</h4>
<p>접근자의 가시성은 기본적으로는 프로퍼티의 가시성과 같지만, 원한다면 get이나 set 앞에 가시성 변경자를 추가해서 접근자의 가시성을 변경할 수 있다. </p>
<pre><code class="language-kotlin">class LengthCounter {
    var counter: Int = 0
        private set // counter 프로퍼티에 대한 setter 접근 범위 제한

    fun addWord(word: String) {
        counter += word.length
    }
}

fun main() {
    val lengthCounter = LengthCounter()
    lengthCounter.addWord(&quot;Hi!&quot;)
    println(lengthCounter.counter) //3
}</code></pre>
<p>counter에 대한 접근성을 getter는 기본적으로 public으로 설정되어 어디서든 읽을 수 있지만, <strong>setter를 private으로 설정함으로써 클래스 내부에서만 counter를 수정할 수 있도록 해놓아 외부에서는 수정을 불가하게 해놓는다.</strong> </p>
<h3 id="3-컴파일러가-생성한-메서드-data-클래스와-클래스-위임">3. 컴파일러가 생성한 메서드: Data 클래스와 클래스 위임</h3>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/aae91847-5ce5-4360-b290-c1fd34b2b4c0/image.png" alt=""></p>
<p>자바에서 equals(두 객체 같은지 판단), hashCode(객체의 해시코드 제공), toString(객체에 대한 텍스트 표현 제공)과 같은 기계적으로 구현할 수 있는 몇 가지 메서드가 정의돼 있다. 이런 메서드들은 편리하긴 하지만, 코드베이스의 번잡성을 줄여주진 못하는데, *<em>코틀린 컴파일러는 이런 메서드들을 기계적으로 생성하는 작업을 보이지 않는 곳에서 해줄 수 있다. *</em> 이것 또한 코틀린의 특징이 잘 두드러지는 부분이다.</p>
<h4 id="31-모든-클래스가-정의해야-하는-메서드">3.1. 모든 클래스가 정의해야 하는 메서드</h4>
<p>앞서 언급한 세 가지 메서드가 코틀린에서 어떻게 자동으로 생성되고 사용되는지 예시를 통해 살펴보자. </p>
<pre><code class="language-kotlin">class Customer(val name: String, val postalCode: Int)</code></pre>
<p><strong>1. toString</strong>
자바처럼 코틀린의 모든 클래스도 인스턴스의 문자열 표현을 얻을 방법을 제공한다. 그 예는 아래와 같다.</p>
<pre><code class="language-kotlin">class Customer(val name: String, val postalCode: Int) { //두 개의 프로퍼티를 가진 클래스 선언
    override fun toString() = &quot;Customer(name=$name, postalCode=$postalCode)&quot; //커스텀 문자열을 반환하도록 재정의
}

fun main() {
    val customer1 = Customer(&quot;Alice&quot;, 34652)
    println(customer1) //Customer(name=Alice, postalCode=34562)
}</code></pre>
<p>위 코드를 보면, <strong>객체를 그냥 출력했을 뿐인데 문자열이 출력되는데, 그 이유는 println 내부에서 toString()을 자동으로 호출하기 때문</strong>이다. </p>
<p><strong>2. equals()</strong></p>
<p>자바에서는 &#39;==&#39;를 기본적으로 기본 타입과 참조 타입을 비교할 때 사용된다. 두 비교방식에 차이가 있기에, <strong>자바에서는 객체의 동등성을 판단할 때는 equals를 호출</strong>해야 한다. </p>
<blockquote>
<p>*<em>반면에 코틀린에서는 == 연산자가 두 객체를 비교하는 기본적인 방법으로서, 내부적으로 equals를 호출하면서 객체를 비교한다. *</em></p>
</blockquote>
<p>따라서 equals를 오버라이드 하면 ==를 통해 안전하게 그 클래스의 인스턴스를 비교할 수 있다. </p>
<pre><code class="language-kotlin">class Customer(val name: String, val postalCode: Int) {
    override fun equals(other: Any?): Boolean {
        if (other == null || other !is Customer)
            return false
        return name == other.name &amp;&amp;
               postalCode == other.postalCode
    }
    override fun toString() = &quot;Customer(name=$name, postalCode=$postalCode)&quot;
}

fun main() {
    val processed = hashSetOf(Customer(&quot;Alice&quot;, 342562))
    println(processed.contains(Customer(&quot;Alice&quot;, 342562)))
}
</code></pre>
<p>앞서 Ch2에서 다뤘지만 코틀린의 is연산자는 어떤 값의 타입을 검사한다. 위 코드에 나온 other과 null과 관련된 내용은 나중에 7장에서 더 자세히 다룬다. </p>
<p><strong>코틀린에서는 override 변경자는 필수여서 올바르지 않은 형태로 오버라이딩 되면 바로 컴파일러가 오류를 띄운다. ** 하지만 아직 Customer 클래스는 올바르게 작동하지 않을 확률이 높은데, 그 이유인 hashCode에 대해서 알아보자. 
**
3. hashCode()</strong></p>
<p>hashCode()는 자바와 코틀린 둘 다에서 객체의 “해시값(정수)”을 반환하는 메서드다. 이 값은 주로 HashSet, HashMap, HashTable 같은 해시 기반 컬렉션에서 객체를 빠르게 찾기 위해 사용된다.</p>
<blockquote>
<p>*<em>자바에서는 equals를 오버라이드 할 때 반드시 hashCode도 함께 오버라이드 해야한다. *</em></p>
</blockquote>
<p>JVM 언어(코틀린도 JVM 위에서 돌아감)에서는 <strong>&quot;equals()가 true를 반환하는 두 객체는 반드시 같은 hashCode()를 반환해야 한다.</strong>&quot;라는 규정이 있는데, 이를 어기고 hashCode를 구현하지 않은 경우에는 equals를 구현한 클래스는 제대로 동작하지 않는다. </p>
<p>따라서 Cusotmer 클래스에는 다음과 같이 hashCode를 구현해주어야 한다.</p>
<pre><code class="language-kotlin">override fun hashCode(): Int = name.hashCode() *31 + postalCode</code></pre>
<h4 id="32-데이터-클래스-모든-클래스가-정의해야-하는-메서드를-자동으로-생성">3.2. 데이터 클래스: 모든 클래스가 정의해야 하는 메서드를 자동으로 생성</h4>
<p>어떤 클래스가 데이터를 저장하는 역할만 수행하려면 toString, equals, hashCode를 반드시 오버라이드 해야하는데, *<em>코틀린은 data라는 변경자를 클래스 앞에 붙이면 필요한 메서드를 컴파일러가 자동으로 만들어주고, 이런 클래스를 &#39;데이터 클래스&#39;라고 한다. *</em></p>
<blockquote>
<p>데이터 클래스는 방금 언급한 세 메서드 뿐만 아니라 <strong>copy()</strong> 메서드를 포함한 여러가지 메서드들을 더 생성해준다. </p>
</blockquote>
<p>데이터 클래스의 프로퍼티 꼭 읽기 전용인 val일 필요는 없으나, 읽기 전용으로 만들어야 다중 스레드 프로그램과 같은 여러 상황에서 신경 쓸 사항이 적어지기 때문에, 읽기 전용으로 만들기를 권장한다. </p>
<blockquote>
<p><strong>copy 메서드는 객체를 복사하면서 일부 프로퍼티를 바꿀 수 있게 해주는 메서드이다.</strong></p>
</blockquote>
<p>복사를 하게 되면 객체를 메모리에서 직접 바꾸는 대신 원본과 다른 생명주기를 가지면서 원본에는 다른 영향을 전혀 끼치 않는다는 점이 copy 메서드를 쓰는 메리트이다. </p>
<pre><code class="language-kotlin">data class Customer(val name: String, val postalCode: Int)

fun main() {
    val bob = Customer(&quot;Bob&quot;, 973293)
    println(bob.copy(postalCode = 382555))
    //Customer(name=Bob, postalCode=382555)
}</code></pre>
<p>참고로 자바에도 데이터 클래스와 비슷한 역할을 하는 record(@JvmRecord)가 자바14부터 도입되었으나, copy와 같은 편의 메서드를 제공해주지는 않는다. </p>
<h4 id="33-클래스-위임-by-키워드">3.3. 클래스 위임: by 키워드</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/abf7440f-4ea0-498c-9c35-900c25856813/image.png" alt=""></p>
<p>보통 대규모 객체지향 시스템을 설계할 때의 대표적인 취약점은 &#39;상속&#39;에서 발생한다. 상속이 일어날 때 상위 클래스의 내용이 바뀌면서 전체적인 코드가 정상적으로 작동하지 못하는 경우가 발생할 수 있다.</p>
<p>앞서 언급했듯이 기본적으로는 <strong>final</strong>로 코드를 설정해서 상속을 제한하고, 상속을 가능하게 하려면 <strong>open</strong> 변경자를 붙이는 식으로 이런 혼란을 방지한다. </p>
<blockquote>
<p>하지만 *<em>종종 상속을 허용하지 않는 클래스에게 새로운 동작을 추가해야할 때가 있는데, 이럴 때 decorator 패턴을 주로 사용한다. *</em></p>
</blockquote>
<p>핵심은 상속을 허용하지 않는 클래스 대신 사용할 수 있는 새로운 클래스를 만들되, 기존 클래스와 같은 인터페이스를 데코레이터가 제공하고 기존 클래스를 데코레이터 내부 필드로 유지하는 것이다. 이때 <strong>새로 정의해야 하는 기능은 데코레이터의 메서드로 새로 정의</strong>하고, <strong>기존 기능이 그대로 필요한 부분은 데코레이터의 메서드가 기존 클래스의 메서드에게 요청을 전달(forwarding)</strong>한다. </p>
<p>하지만 치명적인 단점이 있다. 이를 활용하기 위한 준비 코드가 너무 길다.(그래서 보통 인텔리제이 IDEA가 기본적인 준비코드를 기본적으로 제공) 그래서 이런 &#39;위임&#39;의 기능을 코틀린에서 자체적으로 제공한다는 것이 코틀린의 또 하나의 대표적 장점이다. </p>
<blockquote>
<p>코틀린에서 <strong>“위임(Delegation)”</strong> 이란 자신이 해야 할 일을 다른 객체에 맡기는 것을 말한다. 코드에서 <strong>“이 기능은 저 객체한테 부탁할게”</strong> 하고 책임을 넘기는 방식이다. *<em>&#39;by&#39; *</em>키워드를 통해 위임을 실현한다. </p>
</blockquote>
<pre><code class="language-kotlin">interface Printer {
    fun printMessage()
}

class RealPrinter : Printer {
    override fun printMessage() = println(&quot;진짜 프린터가 출력합니다&quot;)
}

// Printer를 직접 구현하지 않고 RealPrinter에게 위임
class PrinterDelegate(printer: Printer) : Printer by printer

fun main() {
    val p = PrinterDelegate(RealPrinter())
    p.printMessage() // &quot;진짜 프린터가 출력합니다&quot;
}</code></pre>
<p>위임이란 <strong>“상위 클래스의 특정 메서드만 수정하고 싶을 때” 상속 대신 쓸 수 있는 기법 중 하나</strong>이며, 꼭 그럴 때만 쓰는 건 아니고, 유연하고 느슨한 결합을 위해 더 넓게 쓰인다. </p>
<h3 id="4-object-키워드-클래스-선언과-인스턴스-생성을-한꺼번에">4. Object 키워드: 클래스 선언과 인스턴스 생성을 한꺼번에</h3>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/509a2b2f-9c82-4c50-bde1-e94e2dbf1092/image.png" alt=""></p>
<p>코틀린에서는 object 선언을 사용하는 몇 가지 경우가 있다.</p>
<ul>
<li><strong>객체 선언(object declaration)</strong>: 싱글턴(하나의 인스턴스만 존재하도록 보장하는 클래스)을 정의하는 한 가지 방법</li>
<li><strong>동반 객체(companion object)</strong>: 어떤 클래스와 관련이 있지만 호출하기 위해 그 클래스의 객체가 필요하지 않은 메서드와 팩토리 메서드를 담을 때 쓰임</li>
<li><strong>객체 식</strong>: 자바의 익명 내부 클래스 대신 쓰임</li>
</ul>
<h4 id="41-객체-선언-싱글턴을-쉽게-만들기">4.1. 객체 선언: 싱글턴을 쉽게 만들기</h4>
<p>객체지향 시스템 설계 시 인스턴스가 하나만 필요한 클래스가 유용한 경우가 많은데, 자바에서는 보통 <strong>클래스의 생성자를 private으로 제한하고 정적인 필드에 그 클래스의 유일한 객체를 저장하는 sinlgeton pattern</strong>을 사용한다. </p>
<blockquote>
<p>*<em>코틀린에서는 객체 선언 기능을 통해 싱글턴을 언어에서 기본 제공한다. *</em></p>
</blockquote>
<p>코틀린에서 객체를 object 키워드로 시작 시 객체 선언 안에 프로퍼티, 메서드, 초기화 블록 등이 들어갈 수 있지만 <strong>생성자를 쓸 수는 없다.</strong> 싱글턴 객체는 객체 선언문이 있는 위치에서 생성자 호출 없이 즉시 만들어지기 때문이다. </p>
<p>객체 선언도 클래스나 인스턴스를 상속할 수 있다. </p>
<pre><code class="language-kotlin">object CaseInsensitiveFileComparator : Comparator&lt;File&gt; {
    override fun compare(file1: File, file2: File): Int {
        return file1.path.compareTo(file2.path,
                ignoreCase = true)
    }
}

fun main() {
    println(CaseInsensitiveFileComparator.compare(
        File(&quot;/User&quot;), File(&quot;/user&quot;)))
    val files = listOf(File(&quot;/Z&quot;), File(&quot;/a&quot;))
    println(files.sortedWith(CaseInsensitiveFileComparator))
}</code></pre>
<p>위의 코드가 싱글턴의 예시인 이유는, object 키워드를 사용함으로써     object 키워드로 정의된 CaseInsensitiveFileComparator는 클래스 선언 + 단일 인스턴스 생성이 동시에 된 것으로, CaseInsensitiveFileComparator라는 타입의 인스턴스가 프로그램 전체에서 단 하나만 존재하게 된다. 그래서 <strong>매번 new나 FileComparator() 같은 생성자를 호출하지 않고, CaseInsensitiveFileComparator라는 이름으로 바로 접근해서 쓸 수 있다.</strong></p>
<h4 id="42-동반-객체-팩토리-메서드와-정적-멤버가-들어갈-장소">4.2. 동반 객체: 팩토리 메서드와 정적 멤버가 들어갈 장소</h4>
<p>코틀린은 자바 static 키워드를 지원하지 않기에 따로 *<em>클래스 안에 정적인 멤버가 없다. *</em> 따라서 패키지 수준의 최상위 함수와 객체 선언을 활용한다. </p>
<blockquote>
<p><strong>팩토리 메서드</strong>는 객체를 생성하는 과정을 직접 생성자 호출(new, constructor)로 하지 않고, <strong>전용 메서드(보통 create, of, from, valueOf 등)</strong>를 통해서 객체를 만드는 기법이다. </p>
</blockquote>
<blockquote>
<p>클래스 안에 정의된 객체 중 하나에 <strong>companion</strong>이라는 표시를 붙이면, 객체 멤버에 접근할 때 자신을 감싸는 클래스의 이름을 통해 직접 사용할 수 있게 된다. 이것이 <strong>동반 객체</strong>다.</p>
</blockquote>
<p>코틀린의 동반 객체의 경우 컴파일러가 동반 객체를 내부적으로 static 필드로 바꿔주기 때문에 자바의 static처럼 클래스 이름을 통해 직접 사용이 가능해진다. 또한 *<em>동반 객체는 자신을 둘러싼 클래스의 모든 private 멤버에 접근할 수 있다. *</em></p>
<p>확실한 장점은 부 생성자 대신에 동반 객체를 활용해서 팩토리 메서드를 써서 객체지향의 장점을 살릴 수 있다는 점이다. </p>
<pre><code class="language-kotlin">class User(val nickname: String) {
    constructor(email: String) : this(email.substringBefore(&#39;@&#39;))
    constructor(accountId: Int) : this(getNameFromSocialNetwork(accountId))
}</code></pre>
<p>위처럼 부 생성자를 활용해 구현했을 경우에는 메서드 이름이 지정되지 않아 의도가 불분명해진다. </p>
<pre><code class="language-kotlin">class User private constructor(val nickname: String) {
    companion object {
        fun newSubscribingUser(email: String) = //팩토리 메서드
            User(email.substringBefore(&#39;@&#39;))

        fun newSocialUser(accountId: Int) =
            User(getNameFromSocialNetwork(accountId))
    }
}</code></pre>
<p>하지만 이런식으로 <strong>팩토리 메서드를 활용해 메서드의 의도를 분명하게 드러낸다면 가독성도 증가</strong>하고, 주 생성자를 private으로 막고 외부에서는 무조건 팩토리 메서드만 써서 객체 생성을 강제하는 식으로 캡슐화를 더욱 극대화할 수 있게 된다. </p>
<h4 id="43-동반-객체를-일반-객체처럼-사용">4.3. 동반 객체를 일반 객체처럼 사용</h4>
<p>동반 객체는 클래스 안에 정의된 일반 객체이기에, 다른 객체 선언처럼 이름을 붙이거나, 인터페이스를 상속하거나, 확장 함수나 프로퍼티를 정의할 수 있다. 
코틀린 표준 라이브러리의 Random 클래스에도 동반 객체가 존재한다.(기본 난수 생성기 제공)</p>
<blockquote>
<p>*<em>클래스에 동반 객체가 있으면 객체 안에 함수를 정의함으로써 클래스에 대해 호출할 수 있는 확장 함수를 만들 수 있다. *</em></p>
</blockquote>
<p>구체적으로 얘기하자면, C라는 클래스 안에 동반 객체가 있고, 그 안에 func를 정의하면 외부에서는 C.func()로 이 함수를 호출 가능하다는 말이다. </p>
<h4 id="44-객체-식-익명-내부-클래스를-다른-방식으로-작성">4.4. 객체 식: 익명 내부 클래스를 다른 방식으로 작성</h4>
<blockquote>
<p>** 자바의 익명 내부 클래스를 대신하는 익명 객체(anonymous object)를 정의할 때도 object 키워드를 쓴다. **
*익명 객체: 이름 없는 클래스를 즉석에서 정의하고 인스턴스를 생성하는 것</p>
</blockquote>
<p>익명 객체를 활용하면 따로 인터페이스를 구현하지 않아도 되고, 혹은 하나, 여러 개의 인터페이스를 구현할 수 도 있게 되기에 상당히 유연하다. </p>
<pre><code class="language-kotlin">val listener = object { //익명 객체 생성
    val name = &quot;temp&quot;
    fun printName() = println(name)
}

fun main() {
    listener.printName() // temp
}</code></pre>
<h3 id="5-인라인-클래스-부가-비용-없이-타입-안정성-추가">5. 인라인 클래스: 부가 비용 없이 타입 안정성 추가</h3>
<blockquote>
<p>인라인 클래스란, <strong>값을 감싸는 작은 클래스</strong>로서, <strong>런타임에서는 진짜 객체를 생성하지 않고 → 내부 값(primitive, reference)을 그대로 사용하게 최적화된 클래스</strong>를 말한다. </p>
</blockquote>
<p>인라인 클래스는 <strong>&#39;value&#39; 키워드를 사용하고 @JVMInline 어노테이션</strong>을 붙여 활용한다. 인라인 클래스를 사용함으로써 타입 안정성을 확실히 보장할 수 있고, 불필요하게 객체를 생성하는 비용을 줄일 수 있다. 아래의 예시를 보자. </p>
<pre><code class="language-kotlin">//userID, email 다 String이라 컴파일 시 잘못 넣어도 못잡음
fun sendMessage(userId: String, message: String) { ... }
fun sendEmail(email: String, message: String) { ... }

//인라인 클래스 활용 시 잘못된 타입 넣으면 컴파일 에러로 잡힘
@JvmInline value class UserId(val id: String)
@JvmInline value class Email(val address: String)

fun sendMessage(userId: UserId, message: String) { ... }
fun sendEmail(email: Email, message: String) { ... }</code></pre>
<p>즉, 쉽게 말하면 인라인 클래스는 값을 감싸고 있는 클래스지만, 런타임에 객체로 존재하지 않고 값 자체로 치환되기에, 쓸데없이 객체를 더 생성하지 않아도 되는 것이다. </p>
<p>하지만 명확한 제약이 존재한다. </p>
<ul>
<li><strong>인라인 클래스는 오직 하나의 프로퍼티만 가진다.</strong></li>
<li>*<em>인라인 클래스는 클래스 계층(상속받거나, 상속하거나)에 참여하지 않는다. *</em></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kotlin in Action] 03. 함수 정의와 호출]]></title>
            <link>https://velog.io/@younjin_02/Kotlin-in-Action-03.-%ED%95%A8%EC%88%98-%EC%A0%95%EC%9D%98%EC%99%80-%ED%98%B8%EC%B6%9C</link>
            <guid>https://velog.io/@younjin_02/Kotlin-in-Action-03.-%ED%95%A8%EC%88%98-%EC%A0%95%EC%9D%98%EC%99%80-%ED%98%B8%EC%B6%9C</guid>
            <pubDate>Sun, 27 Jul 2025 10:45:49 GMT</pubDate>
            <description><![CDATA[<h3 id="1-collection-만들기">1. Collection 만들기</h3>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/e2ff3e0f-5919-4791-89f4-4f012e12209b/image.png" alt=""></p>
<p>2장에서 setOf 함수를 통해 집합을 생성했었다. 이 함수는 <strong>원소의 순서는 중요하지 않은 컬렉션을 만드는 함수</strong>이다. setOf 사용 예는 아래와 같다.</p>
<pre><code class="language-kotlin">val set = setOf(1, 7, 53)

val list = listOf(1, 7, 53)
val map =  listOf(1 to &quot;one&quot;, 7 to &quot;seven&quot;, 53 to &quot;fifty-three&quot;)</code></pre>
<p>여기서 set, list, map이라는 컬렉션이 만들어지고, 각각의 컬렉션은 다 객체이다. <strong><em>코틀린에서는 기본 값을 제외한 모든 값을 객체라고 보면 된다.</em></strong></p>
<p>코틀린은 표준 자바 컬렉션 클래스를 사용한다. 즉, <strong>자바와 코틀린 컬렉션을 서로 변환할 필요 없다</strong>. 코틀린 컬렉션은 자바 컬렉션과 같은 클래스이긴 하지만 코틀린에서는 자바보다 더 많은 기능을 쓸 수 있다.</p>
<pre><code class="language-kotlin">fun main() {
    val strings = listOf(&quot;first&quot;, &quot;second&quot;, &quot;fourteenth&quot;)

    println(strings.last()) //fourteenth //마지막 원소 출력

    println(strings.shuffled()) //[fourteenth, second, first] //뒤섞기

    val numbers = setOf(1, 14, 2)
    println(numbers.sum()) //17 // 합계 계산
}</code></pre>
<h3 id="2-함수-호출">2. 함수 호출</h3>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/5a890308-ae63-4593-8aa5-92350463bb12/image.png" alt=""></p>
<p>자바 컬렉션에는 디폴트 toString 구현이 들어있고, 고정돼 있어 list 출력 시 자동으로 toString이 적용되어 출력된다. 하지만 디폴트 구현과 달리 원소 사이가 세미 콜론으로 되어 있는, (1; 2; 3)처럼 출력시키고 싶다면 어떻게 해야 할까.</p>
<p><strong>코틀린에는 이런 요구사항을 처리할 수 있는 함수가 표준 라이브러리에 이미 들어있다</strong>. 다음은 원소 사이에 구분자(separator)를 추가하고, StringBuilder의 맨 앞과 맨 뒤에 접두사와 접미사를 추가하는 제네릭 함수이다. </p>
<pre><code class="language-kotlin">fun &lt;T&gt; joinToString(
        collection: Collection&lt;T&gt;,
        separator: String,
        prefix: String,
        postfix: String
): String {

    val result = StringBuilder(prefix)

    for ((index, element) in collection.withIndex()) {
        if (index &gt; 0) result.append(separator)
        result.append(element)
    }

    result.append(postfix)
    return result.toString()
}

fun main() {
    val list = listOf(1, 2, 3)
    println(joinToString(list, &quot;; &quot;, &quot;(&quot;, &quot;)&quot;)) //함수 호출 부분
}</code></pre>
<p>책은 위의 코드에서, 함수 호출하는 구문을 더욱 간결하게 만들 수 없을지에 대한 고민을 제시한다. </p>
<h4 id="21-이름-붙인-인자">2.1. 이름 붙인 인자</h4>
<blockquote>
<p>*<em>코틀린으로 작성한 함수를 호출할 때는 함수에 전달하는 인자 중 일부의 이름을 명시할 수 있으며, 인자의 순서를 변경할 수도 있다. *</em></p>
</blockquote>
<pre><code class="language-kotlin">joinToString(collection, seperator=&quot;&quot;, prefix=&quot;&quot;, postfix=&quot;.&quot;)</code></pre>
<h4 id="22-디폴트-파라미터-값">2.2 디폴트 파라미터 값</h4>
<p>자바에서는 일부 클래스에서 오버로딩한 메시지가 너무 많아지다는 문제가 자주 발생한다. 오버로딩 메서드들은 하위 호환성 유지 및 API 사용자에게 편의를 더한다는 다양한 이유로 만들어지지만, 무슨 이유든 간에 중복이라는 치명적인 단점을 제공한다.</p>
<p><strong>코틀린에서는 바로 위에서 명시한 바와 같이 파라미터의 기본 값을 지정할 수 있어 이런 오버로드 중 상당수를 피할 수 있다</strong>. </p>
<pre><code class="language-kotlin">fun &lt;T&gt; joinToString(
    collection: Collection&lt;T&gt;,
    separator: String = &quot;, &quot;, //기본값이 지정된 파라미터들
    prefix: String = &quot;&quot;,
    postfix: String = &quot;&quot;
): String {

    val result = StringBuilder(prefix)

    for ((index, element) in collection.withIndex()) {
        if (index &gt; 0) result.append(separator)
        result.append(element)
    }

    result.append(postfix)
    return result.toString()
}

val list = listOf(1, 2, 3)

fun main() {
    joinToString(list, &quot;, &quot;, &quot;&quot;, &quot;&quot;) //1, 2, 3
    joinToString(list, &quot;, &quot;, &quot;&quot;, &quot;&quot;) // 1, 2, 3
    joinToString(list, &quot;; &quot;) //1; 2; 3
}</code></pre>
<p>이와 같이 코틀린에서는 기본값을 지정해놓으면 함수를 호출할 때 모든 인자를 쓸 수도 있고, 일부를 생략할 수도 있다. 추가적으로, <strong>이름 붙은 인자를 사용하는 경우에는 인자 목록의 중간에 있는 인자를 생략하거나, 지정하고 싶은 인자에 이름을 붙여서 순서와 관계없이 지정할 수 있다</strong>. </p>
<pre><code class="language-kotlin">fun main() {
    joinToString(list, postfix=&quot;; &quot;, prefix=&quot;# &quot;) //#1, 2, 3;
}</code></pre>
<p>위의 경우엔 separator 파라미터가 생략되어 원래 지정된 디폴트 값(,)로 설정되고, 나머지 파라미터들만 순서를 바꿔 지정해준 결과이다. 즉, 중요한 점은 <strong>함수의 디폴트 파라미터 값은 함수를 호출하는 쪽이 아니라 함수 선언 쪽에 인코딩 된다는 점이다</strong>. </p>
<h4 id="23-정적인-유틸리티-클래스-없애기-최상위-함수와-property">2.3. 정적인 유틸리티 클래스 없애기: 최상위 함수와 property</h4>
<p>객체지향 언어인 자바에서는 모든 코드를 클래스의 메서드로 작성해야만 한다는 사실을 알고 있다. 이런 경우에는 특별한 상태나 인스턴스 메서드가 없는 클래스가 생겨나는데, 보통 이런 클래스는 여러 정적 메서드를 모아두는 역할만 담당한다. </p>
<p>코틀린에서는 이런 무의미한 클래스를 필요로 하지 않는다. 함수를 소스 파일의 최상위 수준, 모든 다른 클래스의 밖에 위치시키면 된다. </p>
<pre><code class="language-kotlin">package strings //join.kt 파일

fun joinToString(): String{}</code></pre>
<p>이 join.kt 파일을 컴파일한 결과를 자바로 작성하면 다음과 같다.</p>
<pre><code class="language-java">package strings;

public class Joinkt {
  public static String joinToString(){}
}</code></pre>
<p><strong>코틀린 컴파일러가 생성하는 클래스의 이름이 최상위 함수가 들어있던 코틀린 소스 파일의 이름과 대응한다는 사실을 확인할 수 있다</strong>. </p>
<blockquote>
<p><strong>최상위 property</strong>
*property: 객체가 가지는 변수 또는 속성</p>
</blockquote>
<p><strong>프로퍼티 또한 함수와 마찬가지로 파일 최상위 수준에 위치시킬 수 있다.</strong> 최상위 수준에 위치시킨다는 말은 <strong>함수 바깥의 맨 위에서 정의되어 이 값이 정적 필드에 저장된다는 뜻이고, 이를 이용해 코드에서 상수를 정의할 수 있다</strong>. </p>
<p>디폴트 최상위 프로퍼티도 다른 모든 프로퍼티처럼 접근자 메서드를 통해 자바 코드에 노출된다(val은 getter, var은 getter와 setter). 만들어진 상수를 자바 코드에 public static final 필드로 노출시키고 싶다면, const 변경자를 추가하면 된다. </p>
<h3 id="3-확장-함수와-확장-프로퍼티">3. 확장 함수와 확장 프로퍼티</h3>
<p>완전히 코틀린으로만 이뤄진 프로젝트조차도 JDK나 안드로이드 프레임워크와 같은 자바 라이브러리 기반으로 만들어진다. 또한 코틀린을 기존 자바 프로젝트에 통합하는 경우에는 코트린으로 직접 변환할 수 없거나 미처 변환하지 못한 자바 코드를 처리할 수 있어야 한다. </p>
<blockquote>
<p><strong>코틀린의 확장 함수(extension function)가 이런 기존의 자바 API를 재작성하지 않고도 편리한 여러 기능을 제공할 수 있도록 해준다.</strong> </p>
</blockquote>
<p>쉽게 풀어 말하자면, 기존 자바 클래스(String, List, File 등)에 기능을 더하고 싶을 때 자바처럼 새 클래스를 만들거나 상속하거나 유틸 클래스를 만들 필요 없이, 코틀린에서는 그냥 확장 함수로 “간편하게” 추가할 수 있다는 뜻이다. 개념적으로 정의하자면, <strong>어떤 클래스의 멤버 메서드인 것처럼 호출할 수 있지만, 그 클래스의 밖에 선언된 함수다</strong>. 선언의 예시는 아래와 같다.
<img src="https://velog.velcdn.com/images/younjin_02/post/3c21bce1-de71-4dab-8311-cef3a3edda20/image.png" alt=""></p>
<p>그냥 추가 함수 이름 앞에 그 함수가 확장할 클래스의 이름을 덧붙이면 된다. 클래스에 우리의 메서드를 추가하는 것과 같다. 수신 객체를 통해 사용자가 확장 중인 타입의 메서드와 프로퍼티에 접근할 수 있다. <strong>자바 클래스로 컴파일된 클래스 파일이 있는 한, 이 클래스에 우리가 원하는 대로 확장을 추가할 수 있다.</strong></p>
<p>하지만 확장 함수가 캡슐화를 깨지는 않는다는 사실을 기억해야 한다. 클래스 안에 정의된 메서드와 달리 확장 함수 안에서는 클래스 내부에서만 사용할 수 있는 private이나 protected 멤버는 사용불가하다. </p>
<h4 id="31-import와-확장-함수">3.1. import와 확장 함수</h4>
<blockquote>
<p><strong>확장 함수를 정의했다고 해도 자동으로 프로젝트 안의 모든 소스코드에서 그 함수를 사용할 수는 없다. 다른 클래스나 함수와 마찬가지로 해당 함수를 import 해와야 한다.</strong></p>
</blockquote>
<p>주의할 점은, 상황에 따라 임포트 해올 때 확장 함수의 이름 충돌을 대비해 적절히 이름을 바꿔주는 것이 필요하다.</p>
<h4 id="32-자바에서-확장-함수-호출">3.2. 자바에서 확장 함수 호출</h4>
<blockquote>
<p><strong>자바에서는 정적 메서드를 호출하면서 첫 번째 인자로 수신 객체를 넣는 방식으로 확장 함수를 직접 호출한다.</strong></p>
</blockquote>
<pre><code class="language-kotlin">// MyExtensions.kt
package myutils

fun String.hello(): String { //확장 함수
    return &quot;Hello, $this&quot;
}</code></pre>
<p>이를 자바에서는 정적 함수와 같이 호출해주면 된다.</p>
<pre><code class="language-java">public static String hello(String receiver)</code></pre>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/7609d08c-f20c-4171-8c77-4a6af762d957/image.png" alt=""></p>
<p>즉, <strong>자바에서는 Kotlin의 확장 함수가 일반 static 함수처럼 컴파일되기 때문에, 원래 객체(=수신 객체)를 첫 번째 인자로 직접 전달해서 호출해야 한다는 뜻</strong>이다.</p>
<h4 id="33-확장-함수는-오버라이드-할-수-없다">3.3. 확장 함수는 오버라이드 할 수 없다</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/fde8584a-7c18-4ad4-81fc-cc18fed9abb6/image.png" alt=""></p>
<blockquote>
<p><strong>코틀린의 메서드 오버라이드도 일반적인 객체지향의 오버라이드와 마찬가지지만, 확장 함수는 오버라이드 할 수 없다.</strong></p>
</blockquote>
<p>뒤에서 더 자세히 다루지만, 코틀린에서는 하위 클래스에서 오버라이딩이 가능하게 하려면 상위 클래스 코드에 &#39;open&#39; 변경자를 추가해야 한다. </p>
<pre><code class="language-kotlin">open class View {
    open fun click() = println(&quot;view clicked&quot;)
}

open class Button: View(){
    override fun click() = println(&quot;Button Clicked&quot;) //click override
}</code></pre>
<p>책에서 이 부분을 좀 어럽게 설명하는 것 같은데, 바꿔 말하면 <strong>&quot;확장 함수는 &#39;외부에서 덧붙인 함수&#39;라서, 상속 구조와 아무 관련이 없기 때문에 오버라이드가 안 된다&quot;</strong> 는 뜻이다. 확장 함수를 기반 클래스와 하위 클래스에 대해 정의할 수는 있지만, 실제 호출될 함수는 그 변수에 저장된 객체의 타입에 결정되는 것이 아닌, 확장 함수를 호출할 때 수신 객체로 지정한 변수의 컴파일 시점의 타입에 의해 결정된다. <del>라고 책에서 말함; 어렵게도 말한다</del></p>
<pre><code class="language-kotlin">open class Shape

class Circle : Shape() //오버라이딩

// 확장 함수 정의
fun Shape.getName() = &quot;Shape&quot;
fun Circle.getName() = &quot;Circle&quot;

fun printName(shape: Shape) {
    println(shape.getName())
}

fun main() {
    val shape: Shape = Circle()
    printName(shape)  // ❗ 출력: Shape
}</code></pre>
<p>여기서의 shape.getName()은 클래스 타입을 기준으로 호출된다. 실제 객체가 Circle 이어도, 변수 타입이 Shape이면 Shape 확장 함수가 호출된다. 확장 함수가 오버라이딩 되지 않는다는 말은 이 말이다. </p>
<h4 id="34-확장-프로퍼티">3.4. 확장 프로퍼티</h4>
<p>확장 프로퍼티의 사용 예는 아래 코드와 같다. 크게 어려운 점은 없다.</p>
<pre><code class="language-kotlin">val String.lastChar: Char
    get() = this.get(length - 1)
var StringBuilder.lastChar: Char
    get() = this.get(length - 1)
    set(value) {
        this.setCharAt(length - 1, value)
    }

fun main() {
    val sb = StringBuilder(&quot;Kotlin?&quot;)
    println(sb.lastChar)
    sb.lastChar = &#39;!&#39;
    println(sb)
}</code></pre>
<h3 id="4-컬렉션-처리">4. 컬렉션 처리</h3>
<p>이번 절에서는 컬렉션 처리에서 가변 길이 인자, 중위 함수 호출, 라이브러리 지원에 대해 다룬다.
<img src="https://velog.velcdn.com/images/younjin_02/post/e6d7c69b-35e1-4f92-92b5-1d3d8f79f19c/image.png" alt=""></p>
<h4 id="41-자바-컬렉션-api-확장">4.1. 자바 컬렉션 API 확장</h4>
<pre><code class="language-kotlin">fun main() {
    val strings = listOf(&quot;first&quot;, &quot;second&quot;, &quot;fourteenth&quot;)

    strings.last() //fourteenth //마지막 원소 출력

    println(strings.shuffled()) //[fourteenth, second, first] //뒤섞기

    val numbers = setOf(1, 14, 2)
    println(numbers.sum()) //17 // 합계 계산
}</code></pre>
<p>이번 챕터의 맨 앞장에서 다뤘던 코드이다. 이제는 의문을 가져보자. 자바 라이브러리 클래스의 인스턴스인 컬렉션에 대해 코틀린이 어떻게 새로운 기능을 추가할 수 있었을까?</p>
<blockquote>
<p><strong>답은 명확하다. 모두 확장 함수이다.</strong></p>
</blockquote>
<p>코틀린 표준 라이브러리는 수많은 확장 함수를 포함한다. 이 부분에 대해서 굳이 모든 표준 라이브러리를 다 알 필요는 없다. </p>
<h4 id="42-가변-인자-함수">4.2. 가변 인자 함수</h4>
<blockquote>
<p><strong>컬렉션을 만들어내는 함수가 가지고 있는 특징은, 인자의 개수가 그때그때 달라질 수 있다는 점이다.</strong></p>
</blockquote>
<p>가변 인자를 만들 때는 가변 길이 인자, <strong>&#39;vararg&#39;</strong> 변경자를 붙여서 만든다.  <strong>이미 배열에 들어있는 원소를 가변 길이 인자로 넘길 때도 코틀린에서는 스프레드 연산자(*)를 활용한다.</strong> 배열 앞에 *를 붙여서 해당 배열을 펼쳐 배열을 넘길 수 있다. </p>
<h4 id="43-쌍-튜플-다루기-중위-호출과-구조-분해-선언">4.3. 쌍 튜플 다루기: 중위 호출과 구조 분해 선언</h4>
<p>맵을 만드려면 mapOf 함수를 사용한다. </p>
<pre><code class="language-kotlin">val map = mapOf(1 to &quot;one&quot;, 7 to &quot;seven&quot;, 53 to &quot;fifty-three&quot;)</code></pre>
<blockquote>
<p>여기서 to는 코틀린 키워드가 아닌, *<em>중위 호출(infix call)이라는 특별한 방식으로 to라는 일반 메서드를 호출한 것이다. *</em></p>
</blockquote>
<pre><code class="language-kotlin">1.to(&quot;one&quot;)
1 to &quot;one&quot;</code></pre>
<p>위의 두 호출은 동일하다. <strong>중위 호출은 두 개의 값 사이에 함수처럼 “연산자처럼” 함수를 호출하는 문법</strong>이다. 인자가 하나뿐인 일반 메서드나 인자가 하나뿐인 확장 함수에만 중위 호출을 사용할 수 있다. 함수를 중위 호출에 사용하게 허용하고 싶으면 infix 변경자를 함수 선언 앞에 추가해주면 된다.</p>
<blockquote>
<p><strong>구조 분해 선언(destructuring declaration)은 데이터 클래스나 Pair, Triple 같은 객체를 → 변수 여러 개로 나눠서 받는 문법</strong>이다.</p>
</blockquote>
<pre><code class="language-kotlin">data class Person(val name: String, val age: Int)

fun main() {
    val person = Person(&quot;Alice&quot;, 25)

    val (n, a) = person  // 구조 분해 선언!
    println(n)  // Alice
    println(a)  // 25
}</code></pre>
<h3 id="5-문자열과-정규식-다루기">5. 문자열과 정규식 다루기</h3>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/729cb6fc-65d0-4f5c-8588-b86a7893e46a/image.png" alt="">
코틀린 문자열은 자바 문자열과 똑같다. 코틀린 코드와 자바 코드 사이에서 각 메서드와 호출에 넘겨도 전혀 문제가 없다. </p>
<h4 id="51-문자열-나누기">5.1. 문자열 나누기</h4>
<p>정규식 개념을 먼저 짚고 넘어가자면,</p>
<blockquote>
<p>*<em>정규식(Regular Expression, Regex)이란 문자열에서 특정 패턴을 찾거나 처리하기 위한 방법이다. *</em> 즉, 텍스트를 찾고, 자르고, 검사하고, 치환하는 데 쓰는 패턴 언어다.</p>
</blockquote>
<p>자바의 split은 정규식을 구분 문자열로 받아 그 정규식에 따라 문자열을 나눈다. </p>
<pre><code class="language-java">String input = &quot;a.b.c&quot;;
String[] result = input.split(&quot;.&quot;);  // ❌ .은 정규식에서 &quot;모든 문자&quot;를 의미함

String input = &quot;a.b.c&quot;;
String[] result = input.split(&quot;\\.&quot;);  // ✅</code></pre>
<p>이런 뜻이다.<del>설명 대충한 거 아니다.</del> 즉, 점이 아닌 모든 문자 기준으로 잘린다.</p>
<blockquote>
<p><strong>코틀린에서는 자바의 split 대신에 split 확장 함수를 제공하며, String이 아닌 Regex 타입의 값을 받는다.</strong></p>
</blockquote>
<pre><code class="language-kotlin">fun main() {
    println(&quot;12.345-6.A&quot;.split(&quot;\\.|-&quot;.toRegex())) //정규식을 명시적으로 만듬
}</code></pre>
<p>이와 같이 toRegrex 확장 함수를 사용하여 문자열을 정규식으로 변환한다. 이외에도 코틀린 확장 함수는 여러 문자를 받을 수 있어 자바에 있는 단 하나의 문자만 받을 수 있는 메서드를 대신한다. </p>
<p>따라서 <strong>코틀린에서는 split 함수에 전달하는 값의 타입에 따라 정규식이나 일반 텍스트 중 어느 것으로 문자열을 분리하는지 쉽게 알 수 있다.</strong></p>
<h4 id="52-정규식과-3중-따옴표로-묶은-문자열">5.2. 정규식과 3중 따옴표로 묶은 문자열</h4>
<p>파일의 전체 경로명을 디렉터리, 파일 이름, 확장자로 구분하는 코드를 작성해보자. 코틀린 표준 라이브러리에는 어떤 문자열에서 구분 문자열이 맨 나중에 나타난 곳 뒤의 부분 문자열을 반환하는 함수가 있다. 코드는 아래와 같다.</p>
<pre><code class="language-kotlin">fun parsePath(path: String) {
    val directory = path.substringBeforeLast(&quot;/&quot;)
    val fullName = path.substringAfterLast(&quot;/&quot;)

    val fileName = fullName.substringBeforeLast(&quot;.&quot;)
    val extension = fullName.substringAfterLast(&quot;.&quot;)

    println(&quot;Dir: $directory, name: $fileName, ext: $extension&quot;)
}

fun main() {
    parsePath(&quot;/Users/yole/kotlin-book/chapter.adoc&quot;)
}</code></pre>
<p>코틀린에서는 정규식을 사용하지 않고도 문자열을 쉽게 파싱할 수 있다. 정규식은 강력하지만, 알아보기가 쉽진 않으므로 정규식이 필요할 때는 코틀린 라이브러리를 사용하면 도움이 된다. </p>
<pre><code class="language-kotlin">fun parsePathRegex(path: String) {
    val regex = &quot;&quot;&quot;(.+)/(.+)\.(.+)&quot;&quot;&quot;.toRegex()
    val matchResult = regex.matchEntire(path)
    if (matchResult != null) {
        val (directory, filename, extension) = matchResult.destructured
        println(&quot;Dir: $directory, name: $filename, ext: $extension&quot;)
    }
}

fun main() {
    parsePathRegex(&quot;/Users/yole/kotlin-book/chapter.adoc&quot;)
}</code></pre>
<p>이 예제에서 3중 따옴표를 썼는데, <strong>3중 따옴표 문자열에서는 문자열을 그대로 해석하므로, 복잡한 정규식도 이스케이프 없이 깔끔하게 쓸 수 있게 해주는 효과를 가진다</strong>. 위의 코드는 ch9에서 좀 더 자세히 다룰 예정이다. </p>
<h4 id="53-여러-줄-3중-따옴표-문자열">5.3. 여러 줄 3중 따옴표 문자열</h4>
<blockquote>
<p><strong>3중 따옴표 문자열에는 줄 바꿈을 포함해 아무 문자열이나 그대로(이스케이프 없이) 들어간다.</strong></p>
</blockquote>
<p>따라서 3중 따옴표를 쓰면 쉽게 줄 바꿈이 들어있는 텍스트를 문자열에 포함시킬 수 있다. <strong>여러 줄 문자열이 요긴하게 쓰일 수 있는 분야에는 테스트가 있다</strong>. 테스트에서는 보통 여러 줄의 텍스트 출력을 만들어내는 연산을 실행하고, 그 결과를 예상 결과와 비교해야 하는 경우가 있기 때문에, 이 분야에서 3중 따옴표가 좋은 해법이다. </p>
<pre><code class="language-kotlin">fun main() {
    val expectedPage = &quot;&quot;&quot;
        &lt;html lang=&quot;en&quot;&gt;
            &lt;head&gt;
            &lt;title&gt;A page&lt;/title&gt;
            &lt;/head&gt;
        &lt;body&gt;
            &lt;p&gt;Hello, Kotlin!&lt;/p&gt;
        &lt;/body&gt;
        &lt;/html&gt;
    &quot;&quot;&quot;.trimIndent()

    val expectedObject = &quot;&quot;&quot;
        {
            &quot;name&quot;: &quot;Sebastian&quot;,
            &quot;age&quot;: 27,
            &quot;homeTown&quot;: &quot;Munich&quot;
        }
    &quot;&quot;&quot;.trimIndent() //여러 줄 문자열의 공통된 들여쓰기만 자동으로 제거해주는 확장 함수
}</code></pre>
<p>참고로 인텔리제이에서는 3중 따옴표를 썼을 경우에는 따로 문법 하이라이팅 기능도 지원해 코딩을 더 편리하게 할 수 있다.</p>
<h3 id="6-로컬-함수와-확장">6. 로컬 함수와 확장</h3>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/8fbee619-fde9-4a3d-bcd3-bfc827b1cb8f/image.png" alt=""></p>
<p>코딩을 할 때 주요 원칙 중 하나는 <strong>&quot;반복을 피하라&quot;</strong>이다. 하지만 자바의 경우에는 이 원칙을 따르기는 쉽지 않다. 많은 경우 메서드 추출 리팩터링을 통해 긴 메서드를 부분부분 나눠 재활용 가능하다. 하지만 이런 경우에는 코드를 리팩터링하면 클래스 안에 작은 메서드가 많아져 코드를 이해하기 힘들어질 수 있다.</p>
<p>코틀린은 이 문제를 다음과 같이 해결한다.</p>
<blockquote>
<p><strong>함수에서 추출한 함수를 원래의 함수 내부에 내포시켜 문법적인 부가 비용을 들이지 않고도 깔끔하게 코드를 조직할 수 있다.</strong></p>
</blockquote>
<p>예시를 들어 이해해보자. 아래는 중복된 코드의 예시이다.</p>
<pre><code class="language-kotlin">class User(val id: Int, val name: String, val address: String)

fun saveUser(user: User) {
    if (user.name.isEmpty()) {
        throw IllegalArgumentException(
            &quot;Can&#39;t save user ${user.id}: empty Name&quot;)
    }

    if (user.address.isEmpty()) {
        throw IllegalArgumentException(
            &quot;Can&#39;t save user ${user.id}: empty Address&quot;)
    } //필드 검증의 중복

    // Save user to the database
}

fun main() {
    saveUser(User(1, &quot;&quot;, &quot;&quot;))
}</code></pre>
<p>이런식으로, 사용자의 필드를 검증할 때 만약 필요한 인자가 더 늘어난다면 더욱 코드는 복잡해질 것이다. 이런 경우 <strong>검증 코드를 로컬 함수로 분리해 코드 구조를 깔끔하게 유지</strong>할 수 있다. 어떻게 하는지 살펴보자. </p>
<pre><code class="language-kotlin">class User(val id: Int, val name: String, val address: String)

fun saveUser(user: User) { 

//한 필드를 검증하는 로컬 함수 정의
    fun validate(user: User, 
                 value: String,
                 fieldName: String) {
        if (value.isEmpty()) {
            throw IllegalArgumentException(
                &quot;Can&#39;t save user ${user.id}: empty $fieldName&quot;)
        }
    }

    //로컬 함수 호출해 각 필드 검증
    validate(user, user.name, &quot;Name&quot;)
    validate(user, user.address, &quot;Address&quot;)

    // Save user to the database
}

fun main() {
    saveUser(User(1, &quot;&quot;, &quot;&quot;))
}</code></pre>
<p>중복을 더 피할 수도 있다. *<em>로컬 함수는 자신이 속한 바깥 함수의 모든 파라미터와 변수를 사용할 수 있다. *</em></p>
<pre><code class="language-kotlin">class User(val id: Int, val name: String, val address: String)

fun saveUser(user: User) {
    fun validate(value: String, fieldName: String) { //saveUser 함수의 User 파라미터를 중복 사용X
        if (value.isEmpty()) {
            throw IllegalArgumentException(
                &quot;Can&#39;t save user ${user.id}: &quot; +
                    &quot;empty $fieldName&quot;)
        }
    }

    validate(user.name, &quot;Name&quot;)
    validate(user.address, &quot;Address&quot;)

    // Save user to the database
}

fun main() {
    saveUser(User(1, &quot;&quot;, &quot;&quot;))
}</code></pre>
<p><strong>확장 함수를 로컬 함수로 정의하는 것도 가능하다.</strong> 하지만 일반적으로 한 단계만 함수를 내포시키라고 권장한다. 내포된 함수의 깊이가 깊어지면 코드 읽기는 더 어려워질 것이다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kotlin in Action] 02. Kotlin Basic]]></title>
            <link>https://velog.io/@younjin_02/Kotlin-in-Action-02.-Kotlin-Basic</link>
            <guid>https://velog.io/@younjin_02/Kotlin-in-Action-02.-Kotlin-Basic</guid>
            <pubDate>Mon, 21 Jul 2025 09:36:46 GMT</pubDate>
            <description><![CDATA[<h3 id="1-기본-요소-함수와-변수">1. 기본 요소: 함수와 변수</h3>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/eb675457-8868-4a21-be68-003111e99016/image.png" alt=""></p>
<pre><code class="language-kotlin">fun main(){
    println(&quot;Hello World!&quot;)
}
</code></pre>
<p>다음은 코틀린으로 &quot;Hello World!&quot;를 출력하는 함수이다. 여기서 찾을 수 있는 코틀린 문법의 특성은 다음과 같다.</p>
<ul>
<li>함수를 선언할 때 <strong>fun</strong> 키워드를 사용한다.</li>
<li>함수를 모든 코틀린 파일의 최상위 수준에 정의할 수 있으므로 <strong>클래스 안에 함수를 넣을 필요는 없다</strong>. 즉, 자바차럼 꼭 클래스 안에 메서드처럼 넣을 필요가 없다.</li>
<li>main 함수를 애플리케이션의 진입점으로 지정할 수 있기에, <strong>main에는 인자가 굳이 없어도 된다</strong></li>
<li><strong>간결성을 강조하는 언어</strong>로서, 텍스트를 표시하고 싶을 때 그냥 <strong>println만 쓰면된다</strong>(코틀린 라이브러리는 자바 라이브러리 함수에 대해 더 간결한 구문을 사용할 수 있게 해주는 wrapper를 제공한다. println도 그중 하나)</li>
<li><strong>세미콜론 필요없다</strong>. 이는 최신 프로그래밍 언어의 트렌드.</li>
</ul>
<blockquote>
<p><strong>파라미터와 반환값이 있는 함수 선언</strong></p>
</blockquote>
<pre><code class="language-kotlin">fun max(a: Int, b: Int): Int {
    return if(a &gt; b) a else b
}

fun main() {
    println(max(1,2)) // 2
}</code></pre>
<p><strong>코틀린에서는 파라미터 이름이 먼저 오고, 그 뒤에 파라미터의 타입을 지정하며, 타입과 이름을 &#39;콜론(:)&#39;으로 구분한다</strong>. 함수의 반환 타입은 파라미터 목록을 닫는 괄호 다음에 오며, 이 또한 콜론으로 구분한다. 또한, <strong>코틀린의 if식은 자바의 삼항 연산자와 비슷</strong>하다.</p>
<ul>
<li>main 함수는 파라미터가 있는 경우든, 없는 경우든, <strong>어떤 경우에서도 아무 값도 반환하지 않는다</strong>(자바의 void와 비슷한 형태)</li>
<li>코틀린에서 if는 식이지 문이 아니다. 식은 값을 만들어내며 다른 식의 하위 요소로 계산에 참여할 수 있는 반면, 문은 자신을 둘러 싸고 있는 가장 안쪽 블록의 최상위 요소로 존재하며 아무런 값을 만들어내지 않는다.</li>
</ul>
<pre><code class="language-kotlin">
// if, when(자바의 switch, try-catch 모두 식으로 값을 반환하므로 바로 결과를 val에 할당 가능!
val x = if(myBoolean) 3 else 5
val direction = when (inputString) {
    &quot;u&quot; -&gt; UP
    &quot;d&quot; -&gt; DOWN
    else -&gt; UNKNOWN
}

val number = try {
    inputString.toInt()
} catch (e: NumberFormatException){
    -1
}</code></pre>
<p>반면에, 코틀린에서는 <strong>자바와 다르게 대입을 식이 아닌 문으로 취급한다</strong>. 즉, 대입 연산이 값을 반환하지 않기에 다른 식에  끼워넣을 수 없다.
<img src="https://velog.velcdn.com/images/younjin_02/post/61513e64-5c08-4d7c-9707-cc95327bf5eb/image.png" alt=""></p>
<pre><code class="language-kotlin">val a = 10      // val: 변경 불가능한 값
var b = 20      // var: 변경 가능한 값
b = 30          // var만 다시 대입 가능</code></pre>
<p>이런식으로 코틀린에서 대입은 그냥 위와 같이 <strong>변수에 값을 ‘할당’</strong>하는 형태로 사용하면 된다.</p>
<blockquote>
<p><strong>식 본문을 사용해 함수를 더 간결하게 정의</strong></p>
</blockquote>
<p>앞서 나왔던 함수를 등호를 사용해 다음과 같이 더 간결하게 나타낼 수도 있다.</p>
<pre><code class="language-kotlin">fun max(a: Int, b: Int): Int = if(a &gt; b) a else b</code></pre>
<ul>
<li>본문이 중괄호로 둘러 쌓인 함수: <strong>block body function</strong>(블록 본문 함수)</li>
<li>&quot; 등호와 식으로 &quot; : <strong>expression body function</strong>(식 본문 함수) // 코틀린에서는 식 본문 함수가 더 자주 쓰인다</li>
</ul>
<p>위의 함수 식에서 반환 타입 지정하는 부분을 제거하여 더욱 함수를 간추릴 수 있다.</p>
<pre><code class="language-kotlin">fun max(a: Int, b: Int) = if(a &gt; b) a else b</code></pre>
<p>식 본문 함수의 경우 굳이 사용자가 반환 타입을 적지 않아도 컴파일러가 함수 본문 식을 분석해서 식의 결과 타입을 함수 반환 타입으로 지정해준다. 이게 앞서 1장에서도 다뤘던 <strong>타입 추론(Type inference)</strong>다. <strong>타입 추론은 식 본문 함수에만 적용 가능하다는 것을 기억</strong>하자.</p>
<blockquote>
<p><strong>변수 선언</strong></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/30f4bd35-794b-485c-b207-72b87c63ee1c/image.png" alt=""></p>
<p>위의 이미지가 제일 기본적인 코틀린 변수 선언의 형태이다. 데이터 타입을 빼는 경우에는 앞서 언급했듯이 컴파일러에 의해 타입 추론이 발생한다.</p>
<p>변수 선언의 종류에는 다음 두 가지가 존재한다.</p>
<ul>
<li><strong>val</strong>: 읽기 전용 참조(read-only reference) 선언. val로 선언된 변수는 단 한번만 대입될 수 있다. 일단 초기화하고 나면 다른 값을 대입할 수 없다.</li>
<li><strong>var</strong>: 재대입 가능한 참조(reassignable reference)를 선언.</li>
</ul>
<p>val 자체가 읽기 전용이어서 한 번 대입된 다음에 그 값을 바꿀 수 없더라도, <strong>그 참조가 가리키는 객체 내부의 값은 변경될 수 있다</strong>. 또, 컴파일러 자체가 똑똑해 <strong>잠재적인 두 가지 대입 중(if-else 식) 하나만 실행될 수 있는 경우</strong>도 사용 가능하다. </p>
<pre><code class="language-kotlin">fun main(){
    val languages = mutableListOf(&quot;Java&quot;)
    languages.add(&quot;Kotlin&quot;) //참조가 가리키는 객체에 원소를 하나 추가하는 변경 수행
}</code></pre>
<p>var의 경우, 변수의 값을 변경할 수 있지만 <strong>변수의 타입은 고정된다</strong>.</p>
<blockquote>
<p><strong>문자열 템플릿($)</strong></p>
</blockquote>
<pre><code class="language-kotlin">fun main(){
   val input = readln()
    val name = if(input.isNotBlank()) input else &quot;Kotlin&quot;
    println(&quot;Hello, $name!&quot;)
}</code></pre>
<p>위는 문자열 템플릿(string template)을 기능을 보여주고 간단한 사용자 입력을 읽는 방법을 보여준다. 이는 다른 스크립트 언어와 비슷하게 <strong>변수 이름 앞에 $를 붙이면 변수를 문자열 안에 참조할 수 있다</strong>.
*$를 문자열에 넣으려고 하는 경우에는 백슬래쉬()를 사용해 escape시킨다.</p>
<p>문자열 템플릿과, if가 코틀린에서 식이라는 사실을 조합해 다음과 같은 코드의 형태를 만들어낼 수 있다.</p>
<pre><code class="language-kotlin">fun main(){
   val input = readln()
    println(&quot;Hello, ${if (input.isBlank()) &quot;someone&quot; else input}!&quot;)
}</code></pre>
<h3 id="2-행동과-데이터-캡슐화-class--property">2. 행동과 데이터 캡슐화: Class &amp; Property</h3>
<p>다른 객체지향 언어와 마찬가지로 코틀린도 <strong>클래스</strong>라는 추상화를 제공한다. 하지만 코틀린은 자바에 비해 훨씬 더 적은 양의 코드로 이를 구현할 수 있다. 아래는 자바 코드의 예시이다.</p>
<pre><code class="language-java">public class Person {
    private final String name;

    public Person(String name){
        this.name = name;
    }

    public String getName{
        return name;
    }
}</code></pre>
<p>이를 코틀린으로 변환하면 다음과 같다.</p>
<pre><code class="language-kotlin">class Person(val name: String)</code></pre>
<p>훨씬 간단해지는 걸 확인할 수 있다. <del>이제 자바 안쓸ㄹ</del></p>
<blockquote>
<p><strong>클래스와 데이터를 연관시킨다: Property(속성)</strong></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/193fcd3b-b53e-401f-95c2-bd9963c4b539/image.png" alt=""></p>
<p>자바에서는 필드와 접근자를 한데 묶어 property라고 부르며, 이를 활용하는 다양한 프레임워크가 있다. <strong>코틀린은 프로퍼티를 언어 기본 기능으로 제공하며, 코틀린 프로퍼티는 자바의 필드와 접근자 메서드(e.g. getter, setter)를 &#39;완전히&#39; 대신한다</strong>. 
변수와 동일하게 val이나 var을  활용하여 선언한다.</p>
<pre><code class="language-kotlin">class Person(
    val name: String,
    var isStudent: Boolean //쓸 수 있는 프로퍼티, 비공개 필드, 공개 게터,세터를 만들어냄 
)</code></pre>
<p>자바의 긴 getter와 setter 코드가 코틀린의 저 한줄에 다 숨어 들어가있다. 즉, 코틀린으로 저렇게 정의한 클래스를 자바에서도 똑같이 원래 클래스를 사용하는 방식대로 똑같이 사용할 수 있다. 아래는 코틀린으로 Person 클래스를 사용하는 예시이다.</p>
<pre><code class="language-kotlin">fun main() {
    val person = Person(&quot;Bob&quot;, true) //new 키워드 사용X
    println(person.name) //Bob
    println(person.isStudent) //true

    person.isStudent = false //졸업
    println(person.isStudent) //false
}</code></pre>
<p>위의 코드에서 확인할 수 있듯이, <strong>getter를 명시적으로 호출하는 대신 프로퍼티를 직접 사용함으로써 코틀린이 자동으로 getter를 호출하게 한다</strong>. 로직은 동일하지만 코드는 더 간단해졌다.</p>
<blockquote>
<p><strong>프로퍼티 값을 저장하지 않고 계산: 커스텀 접근자</strong></p>
</blockquote>
<p>프로퍼티 접근자의 커스텀 구현이 필요한 일반적인 경우는 어떤 프로퍼티가 같은 객체 안의 다른 프로퍼티에서 계산된 직접적인 결과인 경우가 있다. 다음은 프로퍼티에 접근할 때 마다 정사각형인지 여부를 계산하는 Rectangle 클래스이다.</p>
<pre><code class="language-kotlin">class Rectangle(val height: Int, val width: Int){
    val isSquare: Boolean
    get() = height == width //커스텀 getter
}</code></pre>
<p>이 클래스에서 height와 width, isSquare은 모두 클래스의 프로퍼티지만, <strong>isSquare은 나머지 두 개와 다르게 단순히 값을 저장하지 않고, 항상 계산해서 돌려줘야 하는 커스텀 접근자이므로, 따로 중괄호로 싸서 정의해준다</strong>.</p>
<blockquote>
<p><strong>코틀린 소스 코드 구조: 디렉토리와 패키지</strong></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/02f0453b-2b2f-47cb-a188-343520c845c3/image.png" alt=""></p>
<p>코틀린은 클래스를 조직화하기 위해 자바와 동일한 개념의 패키지를 사용한다. package 문이 있는 파일에 들어있는 모든 선언(클래스, 함수, 프로퍼티)는 해당 패키지 안으로 들어간다. </p>
<p>다른 패키지에 정의한 선언을 사용하려면 해당 선언을 불러오는 <strong>import</strong> 키워드를 사용해야 한다. <strong>코틀린은 함수 import와 클래스 import를 구분하지 않는다.</strong> 패키지 이름 뒤에 .<em>를 추가하면 패키지 안의 모든 선언을 임포트할 수 있다. 이를 *</em>star import**라고 한다. </p>
<p>자바에서는 패키지의 구조와 일치하는 디렉터리 계층 구조를 만들고, 클래스의 소스코드를 그 클래스가 속한 패키지와 같은 디렉터리에 위치시켜야 한다. 이에 반해 <strong>코틀린에서는 여러 클래스를 같은 파일에 넣을 수 있어서, 파일의 이름도 마음대로 정해서 디스크 상의 어느 디렉터리에든 위치시킬 수 있지만</strong>, 연동성을 고려해 자바의 패키지 구조를 따르는 것이 바람직하다. </p>
<p>가장 두드러지는 자바와의 디렉터리 구조의 차이점은, &#39;<strong>여러 클래스를 한 파일에 넣는 것을 주저하지 말아야 한다</strong>&#39;는 것이다. </p>
<h3 id="3-선택과-표현과-처리-enum--when">3. 선택과 표현과 처리: enum &amp; when</h3>
<p>enum은 자바 선언보다 코틀린 선언에 더 많은 키워드를 써야 하는 흔치 않은 예다. <strong>코틀린에서는 enum class를 사용하지만, 자바에서는 그냥 enum으로만 쓴다</strong>. </p>
<pre><code class="language-kotlin">enum class Color {
    RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET
}</code></pre>
<p>enum은 코틀린에서 소프트 키워드이다. class 앞에 붙을 때만 특별한 이넘의 의미를 지니고, 다른 곳에는 일반적인 이름으로 사용할 수 있다. 이와 반대되는 하드 키워드에는 class가 있다. </p>
<p>enum class 또한 <strong>일반적인 클래스와 마찬가지로 생성자와 프로퍼티 선언 문법을 사용할 수 있다</strong>. 여기서 코틀린에서 유일하게 세미콜론이 나오는 부분이 등장한다. <strong>이넘 클래스 안에 메서드를 정의하는 경우 반드시 이넘 상수 목록과 메서드 정의 사이에 세미콜론을 넣어야 한다</strong>. </p>
<p>when 식은(얘도 식이다), <strong>자바의 switch문과 같은 기능을 한다</strong>. </p>
<pre><code class="language-kotlin">fun getMnemonic(color: Color) =
    when (color) {
        Color.RED -&gt; &quot;Richard&quot;
        Color.ORANGE -&gt; &quot;Of&quot;
        Color.YELLOW -&gt; &quot;York&quot;
        Color.GREEN -&gt; &quot;Gave&quot;
        Color.BLUE -&gt; &quot;Battle&quot;
        Color.INDIGO -&gt; &quot;In&quot;
        Color.VIOLET -&gt; &quot;Vain&quot;
    }

fun main() {
    println(getMnemonic(Color.BLUE))
}</code></pre>
<p>자바와 다른 점은 각 분기의 끝에 break를 넣지 않아도 된다. 또한, 상수 값들을 Import하면 이넘 클래스 이름을 굳이 붙이지 않아도 된다. 그에 대한 예시는 아래와 같다.</p>
<pre><code class="language-kotlin">import ch02.colors.Color.*

fun measureColor() = ORANGE

fun getWarmthFromSensor(): String {
    val color = measureColor() //color = orange가 됨
    return when (color) {
        RED, ORANGE, YELLOW -&gt; //따라서 이 줄이 실행됨
            &quot;warm (red = ${color.r})&quot;
        GREEN -&gt;
            &quot;neutral (green = ${color.g})&quot;
        BLUE, INDIGO, VIOLET -&gt;
            &quot;cold (blue = ${color.b})&quot;
    }
}

fun main() {
    println(getWarmthFromSensor())
}
</code></pre>
<p>추가적으로 위의 예시에서 정의한 color 변수의 정의가 when의 조건안으로 정의되서 해당 변수가 when 식 안에서만 정의되도록 할 수도 있다.</p>
<p>when 식의 인자로 아무 객체나 사용하는 것도 가능하다. <del>아 좀 그만 가능해ㄹ..</del></p>
<pre><code class="language-kotlin">fun mix(c1: Color, c2: Color) =
        when (setOf(c1, c2)) {
            setOf(RED, YELLOW) -&gt; ORANGE
            setOf(YELLOW, BLUE) -&gt; GREEN
            setOf(BLUE, VIOLET) -&gt; INDIGO
            else -&gt; throw Exception(&quot;Dirty color&quot;) //일치하는 분기 조건이 없을 경우
        }

fun main() {
    println(mix(BLUE, YELLOW))
}</code></pre>
<p><strong>코틀린 표준 라이브러리는 인자로 전달받은 여러 객체들을 포함하는 집합(Set)을 생성해주는 setOf함수를 제공한다</strong>. 따라서 위의 코드에서 setOf의 안의 순서는 중요하지 않다. </p>
<blockquote>
<p><strong>스마트 캐스트: 타입 검사와 타입 캐스트 조합</strong></p>
</blockquote>
<p>본 책은 본격적으로 스마트 캐스트에 대해 알아보기 전, <strong>마커 인터페이스</strong>라는 개념을 다룬다. 책에서는 이것을 &quot;여러 타입의 식 객체를 아우르는 공통 타입 역할만 수행하는 인터페이스&quot;라고 표현하지만 <del>옮긴이 누구냐</del>, 좀 더 이해하기 쉽게 표현하자면, <strong>메서드는 없지만, 어떤 클래스를 특정한 용도로 쓸 수 있다는 “표시” 또는 “태그”를 붙이는 도구</strong> 정도로 생각하면 된다. </p>
<pre><code class="language-kotlin">interface Expr //마커 인터페이스
class Num(val value: Int) : Expr //Expr 인터페이스를 구현
class Sum(val left: Expr, val right: Expr): Expr</code></pre>
<p>Expr 인터페이스를 구현한다는 말은, <strong>Num과 Sum 클래스가 Expr이라는 스티커를 붙였다 정도로 이해하면 이해가 빠르다</strong>. Num이든 Sum이든 공통 타입으로 처리하기 위해 기능은 없고 <strong>&#39;표시&#39;역할만 하는 마커 인터페이스를 붙여 한 타입처럼 다루게 만드는 것이다</strong>. </p>
<p>인터페이스에 대해서는 4장에서 조금 더 본격적으로 다룬다. 마커 인터페이스가 갑자기 스마트 캐스트 얘기하는데 왜 튀어나오는지는.. 곧 다뤄보도록 하겠다.</p>
<p>코틀린에서는 is 검사를 사용해 어떤 변수의 구체적 타입을 검사할 수 있다. </p>
<pre><code class="language-kotlin">fun eval(e: Expr): Int {
    if(e is Num){
        val n = e as Num //불필요한 중복. 알아서 스마트 캐스트가 일어나기 때문
        return n.value
    }
    if(e is Sum){
        return eval(e.right) + eval(e.left)
    }
    throw IllegalArgumentException(&quot;Unkown Expression&quot;)
}</code></pre>
<p>위의 코드에서 스마트 캐스트를 확인할 수 있다. 스마트 캐스트를 먼저 정의하고 가자면, <strong>&quot;타입 체크(is) 후에 자동으로 형변환(cast)이 되는 코틀린의 기능&quot;</strong>이 스마트 캐스트이다. 위의 코드에서 val n = e as Num 줄을 굳이 적지 않아도, e.value라고 적어도 코틀린이 알아서 e를 Num 타입으로 캐스팅해준다. 이것이 바로 스마트 캐스트이다. 밑의 e.right와 e.left에서는 스마트 캐스트를 활용한 것이다. 다시 한번 쉽게 설명하자면, <strong>&quot;is는 단지 타입을 검사하는 문법일 뿐인데, 코틀린이 똑똑하게 그걸 감지해서 자동으로 캐스트까지 해주는 것&quot;</strong>이다.</p>
<p>참고로 명시적으로 타입 캐스팅하려면 <strong>as 키워드</strong>를 활용한다. </p>
<pre><code class="language-kotlin">val n = e as Num</code></pre>
<p>위의 스마트 캐스트가 활용된 코드를 더욱 간단하게 다음과 같이 만들 수 있다.</p>
<pre><code class="language-kotlin">fun eval(e: Expr): Int =
    if(e is Num) e.value
    else if(e is Sum) eval(e.right) + eval(e.left)
    else throw IllegalArugmentException(&quot;Unknown Expression&quot;)</code></pre>
<p>이를 when을 활용한 코드로 변환할 수 있는데, 그 코드는 다음과 같다.</p>
<pre><code class="language-kotlin">fun evalWithLogging(e: Expr): Int =
    when (e) {
        is Num -&gt; {
            println(&quot;num: ${e.value}&quot;)
            e.value
        }
        is Sum -&gt; {
            val left = evalWithLogging(e.left)
            val right = evalWithLogging(e.right)
            println(&quot;sum: $left + $right&quot;)
            left + right
        }
        else -&gt; throw IllegalArgumentException(&quot;Unknown expression&quot;)
    }</code></pre>
<p>참고로, <strong>각 블록의 맨 마지막에 있는 식이 그 블록이 반환할 값이 된다.</strong> </p>
<h3 id="4-iteration-while--for-루프">4. iteration: while &amp; for 루프</h3>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/915c7ebe-c320-47d9-b4db-deccaf66c317/image.png" alt="">
코틀린의 반복문은 자바, C# 등의 다른 언어에서 사용하는 방법과 아주 비슷하다. </p>
<blockquote>
<p><strong>조건이 참인 동안 코드 반복: while 루프</strong></p>
</blockquote>
<p>코틀린에는 while과 do-while 루프가 있다. while은 자바와 똑같이 조건이 참인 동안 본문을 반복 실행하고, break로 중단시키고, *<em>do-while문의 경우에는 처음에 무조건 본문을 한 번 실행하고, 그 다음부터는 조건이 참인 동안 본문을 반복 실행한다. *</em> 레이블도 지정해서 특정 루프로 빠져나가거나 진행되게 하여 가독성을 향상시킬 수도 있다.</p>
<blockquote>
<p><strong>수에 대해 iteration: 범위와 순열</strong></p>
</blockquote>
<p>코틀린에는 자바에 그 흔한 for(int i = 0; i &lt; 10; i++) 와 같은 고전 루프의 형태는 없다. <strong>대신해서 &#39;범위&#39;를 사용하는데, &#39;..&#39;연산자를 활용해서 다음과 같이 작성한다</strong>. 여기서 범위 폐구간(양쪽 끝 값이 범위에 포함)이다.</p>
<pre><code class="language-kotlin">val oneToTen = 1..10</code></pre>
<p>정수 범위를 갖고 수행할 수 있는 가장 단순한 작업은 <strong>범위에 속한 값을 일정한 순서로 반복하는 경우, 순열(progression)</strong>이다. 피즈버즈라는 게임을 코드로 구현했을 때, 순열이 다음과 같이 활용된다. </p>
<pre><code class="language-kotlin">fun fizzBuzz(i: Int) = when {
    i % 15 == 0 -&gt; &quot;FizzBuzz &quot;
    i % 3 == 0 -&gt; &quot;Fizz &quot;
    i % 5 == 0 -&gt; &quot;Buzz &quot;
    else -&gt; &quot;$i &quot;
}

fun main() {
    for (i in 1..100) { //1..100 사이 iteration
        print(fizzBuzz(i))
    }
}</code></pre>
<p>거꾸로 세는 것도 가능하다.</p>
<pre><code class="language-kotlin">for(i in 100 downTo 1 step2) // 100부터 2씩 감소</code></pre>
<blockquote>
<p><strong>in으로 Collection이나 범위의 원소 검사</strong></p>
</blockquote>
<p><strong>in 연산자를 사용해 어떤 값이 범위에 속하는지 검사할 수 있다. 반대로 !in을 사용해 어떤 값이 범위에 속하지 않는지 검사할 수 있다.</strong></p>
<pre><code class="language-kotlin">fun isLetter(c: Char) = c in &#39;a&#39;..&#39;z&#39; || c in &#39;A&#39;..&#39;Z&#39;
fun isNotDigit(c: Char) = c !in &#39;0&#39;..&#39;9&#39;</code></pre>
<p><strong>in과 !in은 when의 가지에서도 활용 가능하다</strong>. 검사하려는 범위가 다양할 때 도움이 된다.</p>
<pre><code class="language-kotlin">fun recognize(c: Char) = when (c) {
    in &#39;0&#39;..&#39;9&#39; -&gt; &quot;It&#39;s a digit!&quot;
    in &#39;a&#39;..&#39;z&#39;, in &#39;A&#39;..&#39;Z&#39; -&gt; &quot;It&#39;s a letter!&quot;
    else -&gt; &quot;I don&#39;t know...&quot;
}

fun main() {
    println(recognize(&#39;8&#39;))
}</code></pre>
<p>범위는 단순히 문자에만 국한되지 않고, 비<strong>교가 가능한 클래스의 인스턴스 객체를 사용해 범위를 만들 수 있다</strong>. 이에 대해 자세한 내용은 9장에서 다룬다.</p>
<blockquote>
<p>** 코틀린의 예외 던지고 잡아내기**</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/f6b73144-5fd0-48d9-b578-3a3d5fd9f450/image.png" alt=""></p>
<p>코틀린의 예외 처리는 자바나 다른 언어의 예외 처리와 비슷하다. 오류가 발생하면 던질(throw) 수 있고, 호출하는 쪽에서 그 예외를 잡아(catch) 처리할 수 있다. <strong>자바와 달리 코틀린의 throw는 &#39;식&#39;이므로 다른 식에 포함될 수 있다는 점을 기억하자.</strong></p>
<p>자바에서는 함수 선언 뒤에 &#39;throws IOException&#39;을 붙여서 <strong>명시적으로 처리해야만 하는 유형의 예외, 체크 예외</strong>를 모두 잡아 처리해야 한다. 하지만, 최신 JVM 언어의 트렌드와 마찬가지로 *<em>코틀린 또한 체크 예외와 언체크 예외를 구별하지 않는다. *</em>  이는 자바 프로그래머들이 의미 없이 예외를 다시 던지거나, 예외를 처리하지는 않고 그냥 무시하는 코드를 작성하는 경우가 자주 있어, 실제 오류 발생을 방지하지 못하는 경우를 막고자 코틀린을 이런식으로 개발하였다고 한다.</p>
<p>이러한 코틀린 언어의 설계 결정으로 코드를 작성하는 사람이 직접 잡아내고 싶은 예외와 그렇지 않은 예외를 결정하여 try-catch로 처리할 수 있다. *<em>try는 다른 문법들처럼 식으로 처리할 수 있어서 코드를 보다 더 간결하게 만들 수 있다. *</em></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kotlin in Action] 1. Kotlin: What and Why]]></title>
            <link>https://velog.io/@younjin_02/Kotlin-in-Action-1.-Kotlin-What-and-Why</link>
            <guid>https://velog.io/@younjin_02/Kotlin-in-Action-1.-Kotlin-What-and-Why</guid>
            <pubDate>Thu, 17 Jul 2025 10:11:14 GMT</pubDate>
            <description><![CDATA[<h3 id="1-kotlin이란">1. Kotlin이란</h3>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/e90a3958-41ef-4b24-8ab8-9bc1554d36fd/image.png" alt=""></p>
<blockquote>
<p><strong>Kotlin은 JVM(자바 가상머신) 플랫폼과 JVM외의 다른 플랫폼에서 돌아가는 현대 프로그래밍 언어이다</strong></p>
</blockquote>
<p>kotlin은 처음에 &#39;더 나은 자바&#39;의 목적으로 처음 개발된 언어이다. 그 후 최근 10년간, 코틀린은 단순 JVM을 훨씬 뛰어넘어 cross-platform 솔루션을 만들 수 있는 기술을 포함해, 더 많은 타깃을 제공해왔고, <strong>안드로이드는 이제 코틀린 우선 플랫폼</strong>이다. 아래는 간단한 코틀린 코드의 예시이다.</p>
<pre><code class="language-kotlin">data class Person( //data class
    val name: String, //읽기 전용 property
    val age: Int? = null //null이 될 수 있는 타입과 parameter 기본값
)

fun main() { //최상위 함수
    val persons = listOf( //이름붙은 파라미터
        Person(&quot;영희&quot;, age = 29),
        Person(&quot;철수&quot;), //, -&gt; 트레일링 comma
    )

    val oldest = persons.maxBy{ //lambda식
        it.age ?: 0 // null에 적용하는 엘비스 연산자
    }
    println(&quot;가장 나이가 많은 사람: $oldest&quot;) //문자열 템플릿
}</code></pre>
<h3 id="2-kotlin의-주요-특성">2. Kotlin의 주요 특성</h3>
<blockquote>
<p><strong>정적 타입 지정으로 인해 코틀린 성능, 신뢰성, 유지 보수성이 모두 좋아진다</strong></p>
</blockquote>
<p>정적 타입 지정 언어의 핵심적인 장점은, <strong>프로그램의 모든 식의 타입을 complile 시점에 알 수 있다는 점</strong>이다. Kotlin은 정적 타입 언어로, 모든 종류의 버그를 runtime에 프로그램이 갑자기 중단되면서 발견하는 대신, <strong>컴파일 시점에 미리 발견함으로써 에러를 미리 방지</strong>할 수 있어, 더 빠른 메서드 호출, 더 좋은 신뢰성과 유지 보수성을 보장한다. 반대로 동적 타입 지정 언어에는 Python이나 JavaScript가 있다.</p>
<p>더 좋은 점은, 그렇다고 해서 프로그래머가 미리 모든 변수의 타입을 명시할 필요는 없다. </p>
<pre><code class="language-kotlin">val x: Int = 1
val y = 1 </code></pre>
<p>위의 예시 코드에서처럼, 코틀린은 컴파일러가 문맥을 고려해 변수 타입을 결정하는 타입 추론(Type Inference)를 제공해, 프로그래머가 직접 타입을 선언해야 함에 따라 생기는 불편함을 없앨 수 있다. </p>
<blockquote>
<p><strong>함수형 프로그래밍과 객체지향 프로그래밍의 조합을 활용해 안전성과 유연성을 보장한다</strong></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/bfd3c4f7-f9b7-4872-b425-ace28ebf24c9/image.png" alt=""></p>
<p>함수형 프로그래밍은 함수를 일반 값처럼 다룰 수 있다(<strong>first-class</strong>). 또한, 일단 만들어지고 나면 내부 상태가 절대로 바뀌지 않는 점(<strong>immutability, 불변성</strong>), input이 같으면 항상 같은 값을 출력하는 특징(<strong>no side effect</strong>)을 지닌다. </p>
<p>함수형 접근 방식을 선택하면 일단 <strong>코드 중복을 피할 수 있다</strong>. 그리고 또 다른 유익성은 안전한 동시성이다. 앞서 언급한 불변성과 순수 함수를 사용한다면 안전하지 못한 <strong>데이터 변경이 발생하지 않는다고 확신</strong>할 수 있으니 복잡한 동기화 방식을 적용하지 않아도 된다.</p>
<p>마지막으로, <strong>테스트하기도 쉽다</strong>. </p>
<blockquote>
<p><strong>Coroutine을 제공해 동시성, 비동기 코드를 자연스럽고 구조적으로 사용한다</strong></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/4ee56b48-4443-41d5-8b3f-ac0ce8aef81e/image.png" alt=""></p>
<p>애플리케이션을 만들 때, <strong>동시성을 배제할 수는 없다</strong>. 백그라운드에서 오래 계산이 실행되는 동안에도 user interface는 반응성을 계속 유지해야 한다. 이에 따라 서버 측에서는 특정 요청이 평소보다 훨씬 오래 걸리더라도 자신에게 들어오는 다른 요청들을 처리해야 한다. </p>
<p>동시성 처리에는 callback, future, Promise와 같은 다양한 방식들이 있지만, <strong>코틀린은 coroutine이라고 불리는 suspendable computation을 사용해 접근해, 자신의 실행을 잠시 중단시키고, 나중에 중단했던 지점부터 작업을 계속 수행할 수 있게 해준다</strong>.</p>
<p>이 부분에 대한 자세한 내용은 뒤에서 다룰 예정<del>이니까 일단 넘어ㄱ</del></p>
<blockquote>
<p><strong>Kotlin은 Open Source이다</strong></p>
</blockquote>
<p>코틀린은 오픈 소스이며, Apache2 라이선스에 제공된다. 개발은 깃허브를 통해 열린 방식으로 이루어지고 있다.</p>
<h3 id="3--코틀린이-자주-쓰이는-분야">3.  코틀린이 자주 쓰이는 분야</h3>
<h4 id="31-백엔드-지원-서버-프로그래밍">3.1. 백엔드 지원: 서버 프로그래밍</h4>
<p>서버 프로그래밍이라고 하면 크게 다음과 같은 것들을 이야기 할 수 있다.</p>
<ul>
<li>브라우저에 HTML 페이지를 돌려주는 웹 애플리케이션</li>
<li>HTTP를 통해 모바일 애플리케이션에게 JSON API를 제공하는 백엔드 애플리케이션</li>
<li>RPC 프로토콜이나 메시지 버스를 통해 서로 통신하는 작은 서비스들로 이루어진 마이크로서비스</li>
</ul>
<p>보통 백앤드 프로그래밍을 할 때 자바를 많이 사용한다. <strong>코틀린의 가장 큰 장점 중 하나는, 자바 코드와 매끄럽게 상호운용할 수 있다는 점</strong>이다. 자바 클래스를 코틀린으로 확장해도 아무 문제가 없으며, 코틀린 클래스 안의 메서드나 필드에 특정 어노테이션을 붙여야 하는 경우에도 아무 문제가 없다.</p>
<p>또 다른 큰 장점은 <strong>코틀린 타입 시스템은 null을 정확히 추적</strong>하기 때문에 null pointer 예외로 인한 문제의 영향을 덜 받게 된다. 자바에서라면 실행 시점에 NullPointerException을 발생시켰을 대부분의 코드는 코틀린에서 컴파일 자체를 실패하기 때문에 오류를 확실히 수정할 수 있다.<del>(오 이건 좋ㄷ</del>
<img src="https://velog.velcdn.com/images/younjin_02/post/90aa6e39-771c-4c02-812c-b769e71abbe0/image.png" alt=""></p>
<p>Spring과 같은 현대적인 프레임워크도 처음부터 코틀린을 일급 시민으로 지원한다. 코틀린 자체 라이브러리 생태계 Ktor 프레임워크도 존재하며, 스페이스, 툴박스, 어도비 같은 회사가 이를 활용하고 있다. 케이토는 XML 파일이나 annotation을 활용해 경로 라우팅을 지정하는 대신, <strong>케이토가 제공하는 DSL을 통해 서버 애플리케이션 라우팅을 설정</strong>할 수 있다.</p>
<p>이처럼 검증된 업계 표준 프레임워크를 쓰든, 마이크로서비스를 위한 경량 프레임워크를 쓰든, 코틀린 생태계는 다양한 프레임워크를 지원한다. </p>
<h4 id="32-모바일-개발">3.2. 모바일 개발</h4>
<p>세계에서 가장 널리 쓰이는 모바일 운영체제인 안드로이드는 2017년부터 kotlin을 앱 빌딩을 위한 언어로 공식 지원하기 시작했다. 
<img src="https://velog.velcdn.com/images/younjin_02/post/ae39402e-208b-4448-b44a-e8918f33e88d/image.png" alt=""></p>
<p><strong>코틀린은 모바일 앱에 적합하다</strong>. 흔한 안드로이드 개발 작업을 훨씬 더 적은 코드로 달성할 수 있으며, 안드로이드 팀이 만든 안드로이드 KTX 라이브러리를 사용하면 수많은 안드로이드 API에 대한 코틀린 adapter를 제공받을 수 있어 안드로이드 프로그래밍 경험을 더 개선할 수 있다. <strong>기본적으로 자바보다 40% 가량 더 적은 코드양을 보장해 가독성 또한 뛰어나다</strong>. </p>
<p>코틀린은 <strong>특정 플랫폼에 국한되지 않는, 다중 플랫폼 언어</strong>이다.</p>
<ul>
<li>코틀린은 JS로 컴파일 할 수 있으며, node.js같은 런타임에서도 코틀린을 활용할 수 있다</li>
<li>코틀린/네이티브를 활용해 코틀린 코드를 이진 코드로 컴파일하여 iOS나 다른 플랫폼에서 스스로 실행되는 프로그램을 만들 수 있다</li>
<li>코틀린/와즘을 통해 코틀린 코드를 바이너리 형식으로도 컴파일 할 수 있다/ </li>
</ul>
<h3 id="4-코틀린의-철학">4. 코틀린의 철학</h3>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/7c1d1f58-0391-448e-a867-149ce6f196d3/image.png" alt=""></p>
<blockquote>
<p><strong>실용적 언어</strong></p>
</blockquote>
<p>코틀린은 대규모 시스템을 개발해본 다년간의 IT 업계 경험을 바탕으로 이뤄졌으며, 이에 따라 <strong>코틀린의 특성은 수많은 SW 개발자들의 사용에 잘 들어맞을 수 있도록 주의깊게 선택</strong>됐다. 더 나아가 이제는 10여년간의 피드백이 현재 최종 코틀린 버전에 반영돼 있다. 그러한 이유로 실제 프로젝트에서 문제를 해결할 때 코틀린이 큰 도움이 될 것이다. </p>
<p>코틀린은 어느 <strong>특정 프로그래밍 스타일이나 패러다임을 사용할 것을 강제로 요구하지 않는다</strong>. 프로그래머는 자신이 익숙한 프로그래밍 스타일이나 기법을 활용할 수 있다. </p>
<blockquote>
<p><strong>간결한 언어</strong></p>
</blockquote>
<p>코틀린은 코드의 의미를 최대한 전달하고, 프로그램 언어가 요구하는 구조를 만족시키기 위해 많은 노력을 기울였다. 자바에서 getter, setter, 생성자 파라미터 필드에 대입하기 위한 로직 등 <strong>OOP에 존재하는 여러가지 번거로운 준비 코드를 코틀린은 암시적으로 제공하기에 이러한 준비 코드로 지저분해질일이 없다</strong>. </p>
<blockquote>
<p><strong>안전한 언어</strong></p>
</blockquote>
<p>흔히 프로그래밍 언어가 안전하다는 것은, <strong>프로그램에서 발생할 수 있는 오류 중에서 일부 유형의 오류를 프로그램 설계가 원칙적으로 방지</strong>해준다는 뜻이다.</p>
<p>기본적으로 <strong>JVM에서 실행한다는 사실 자체가 이미 상당한 안정성을 보장해준다는 뜻</strong>이다. 메모리 안정성을 보장하며, 버퍼 오버플로우를 방지하고, 타입 안정성을 확보해준다. 앞서 언급했듯이 NullPointerException 오류를 방지해주며, smart cast(안전한 캐스팅 지원)을 지원한다.</p>
<blockquote>
<p><strong>좋은 상호운용성</strong></p>
</blockquote>
<p>코틀린은 라이브러리가 어떤 API를 제공하던지 코틀린에서 그 API를 활용할 수 있다. </p>
<p>자바 코드에서 코틀린 코드를 호출할 때도 아무런 노력 없이 이를 실현할 수 있다. 코틀린의 클래스나 메서드를 자바에서도 그대로 똑같이 사용할 수 있다. 이는 결국 <strong>자바와 코틀린 코드를 같은 프로젝트 안에서 원하는 대로 섞어쓸 수 있는 궁극적인 유연성을 제공</strong>해준다. </p>
<h3 id="5-코틀린-도구-사용">5. 코틀린 도구 사용</h3>
<p><strong>자바-코틀린 변환기</strong>는 코틀린을 막 처음 배우기 시작했을 때, 자바로 코드를 작성한 후에 이를 코틀린으로 통째로 변환하고 싶을 때 유용하게 활용할  수 있다. 인텔리제이 IDEA에서 변환기를 쉽게 활용할 수 있다.</p>
<h4 id="51-코틀린-컴파일">5.1. 코틀린 컴파일</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/4bda08de-969a-474f-b0cb-6087cc4a6462/image.png" alt=""></p>
<p>기본적인 코틀린의 컴파일 과정은 다음과 같다.
코틀린 소스 코드를 분석해서 .class 파일을 만들어내면, java 명령으로 해당 코드를 실행시킨다. 하지만 <strong>마지막에 이를 실행시키기 위해서는, Kotlin runtime library가 필요</strong>하다. 이 안에는 Int나 String과 같은 코틀린 기본 클래스 정의가 들어가 있고, 표준 자바 API에 대한 확장이 들어가 있다. </p>
<p>실제로 코틀린으로 개발을 진행하게 되면 <strong>프로젝트를 컴파일하기 위해 Maven, gradle 등의 빌드 시스템을 사용</strong>하게 된다. 이들은 <strong>IDE 없이도 커맨드 한 줄로 빌드, 테스트, 배포까지 자동으로 해주는 자동화 도구</strong>로, 다음과 같은 기능을 제공한다.</p>
<pre><code>1.    코드 컴파일 (source → .class/.jar)
2.    외부 라이브러리(의존성) 자동 다운로드 및 연결
3.    테스트 자동 실행
4.    패키징 &amp; 배포 준비
5.    동일한 방식으로 어디서나 빌드 가능하게 환경 통일</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Algorithm] 10. Dynamic Programming]]></title>
            <link>https://velog.io/@younjin_02/Algorithm-10.-Dynamic-Programming</link>
            <guid>https://velog.io/@younjin_02/Algorithm-10.-Dynamic-Programming</guid>
            <pubDate>Wed, 21 May 2025 12:18:34 GMT</pubDate>
            <description><![CDATA[<h2 id="1-dynamic-programming동적-계획법이란">1. Dynamic Programming(동적 계획법)이란?</h2>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/b71808f4-df6e-4e87-b9ae-2b88f2df5b4f/image.png" alt=""></p>
<blockquote>
<p><strong>복잡한 문제를 여러 개의 간단한 문제로 분리하여 부분의 문제들을 해결함으로써 최종적으로 복잡한 문제의 답을 구하는 방법</strong></p>
</blockquote>
<p>이 정의만 봤을 때는, 앞서 다뤘던 분할 정복(divide and conquer)와 매우 유사하다고 느낄 수 있다. 두 알고리즘 <strong>모두 큰 문제를 작은 문제로 나누어 해결한다는 점과 재귀를 활용한다는 점</strong>에서 그렇다. 하지만, <strong>하위 문제의 중복성과 결과 저장 방식에서 명백한 차이</strong>가 존재한다.
<img src="https://velog.velcdn.com/images/younjin_02/post/f769465f-2848-4b1e-a6ac-6cf28f7becdf/image.png" alt="">
<img src="https://velog.velcdn.com/images/younjin_02/post/2cdcd540-5441-4907-b9af-00c6f6c24cbe/image.png" alt=""></p>
<h4 id="원리와-구현-방식">원리와 구현 방식</h4>
<ol>
<li>큰 문제를 <strong>작은 문제로 나눌 수 있어야 한다</strong></li>
<li>작은 문제들이 반복돼 나타나고, 이 <strong>작은 문제들의 결과값은 항상 같아야 한다</strong></li>
<li>모든 <strong>작은 문제들을 한 번만 계산해 DP table에 저장</strong>하며, 추후 재사용할 때는 이 DP table을 이용한다. 이를 <strong>memoization 기법</strong>이라고 한다.</li>
<li><strong>top down</strong> 방식 &amp; <strong>bottom-up</strong> 방식으로 구현할 수 있다.</li>
</ol>
<h4 id="피보나치-수열">피보나치 수열</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/f9cb2971-4c35-42ed-b198-6159dafb815b/image.png" alt=""></p>
<p>DP의 대표적인 예시로 피보나치 수열을 들 수 있다. 피보나치 수열의 공식은 다음과 같다. <strong>D[N] = D[N-1] + D[N-2]</strong></p>
<p>이 수열이 DP에 해당하는지 확인하기 위해서는, <strong>큰 문제를 작은 문제로 나눌 수 있는지, 작은 문제가 계속 중복이 되는지를 확인</strong>할 수 있어야 한다. 피보나치 수열은 큰 문제를 작은 문제로 나누어 해결하는 문제이고, 계속 같은 문제가 중복되어 나타나므로, DP의 가장 대표적인 예시라고 할 수 있다. 추가적으로, <strong>점화식을 세우기 위해서는 전체 문제와 부분 문제간의 인과 관계를 파악할 수 있어야 한다</strong>. </p>
<h4 id="memoization-원리">memoization 원리</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/0f006835-5c47-4c79-8e01-57043f638e77/image.png" alt=""></p>
<p>memoization이란, 컴퓨터 프로그램을 실행할 때 <strong>이전에 계산한 값을 메모리에 저장해서 매번 다시 계산하지 않도록 하여 전체적인 실행속도를 빠르게 하는 기술</strong>을 의미한다. </p>
<p>위의 그림과 같이, 피보나치 수열에서는 중복 문제가 등장한다. 그렇기에 이런 작은 문제들을 DP table에 저장해 놓고, 나중에 같은 문제가 나왔을 때 DP table의 값을 이용하면 된다. 즉, 시간 복잡도 측면에서 많은 이득을 취할 수 있다.
<img src="https://velog.velcdn.com/images/younjin_02/post/175b02de-d9c9-49cf-b354-49f4b0b874a9/image.png" alt="">
<img src="https://velog.velcdn.com/images/younjin_02/post/6ac1885a-ec66-4f8b-a4b7-0e0e5153515f/image.png" alt=""></p>
<h4 id="top-down-방식">top-down 방식</h4>
<p>말 그대로 <strong>위에서부터 문제를 파악해 내려오는 방식으로, 주로 재귀함수 형태로 코드를 구현</strong>한다. 코드의 가독성이 좋고, 이해하기가 편리하다는 장점이 있다.
<img src="https://velog.velcdn.com/images/younjin_02/post/0d7bf66c-33c0-443d-8827-f1f2387cd3cb/image.png" alt=""></p>
<h4 id="bottom-up-방식">bottom-up 방식</h4>
<p><strong>가장 작은 부분 문제로부터 문제를 해결해가면서 점점 큰 문제로 확장해 나가는 방식</strong>이다. 주로 <strong>반복문</strong>의 형태로 구현된다. 
<img src="https://velog.velcdn.com/images/younjin_02/post/1cafb368-08c4-46ac-8eeb-885141336829/image.png" alt="">
위의 코드를 보면, for문을 2부터 돌리면서 2부터 점점 커지면서 값을 구해나가는 형태임을 확인할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Algorithm] 09. Divide and Conquer]]></title>
            <link>https://velog.io/@younjin_02/Algorithm-09.-Divide-and-Conquer</link>
            <guid>https://velog.io/@younjin_02/Algorithm-09.-Divide-and-Conquer</guid>
            <pubDate>Wed, 14 May 2025 06:48:38 GMT</pubDate>
            <description><![CDATA[<h2 id="1-분할정복divide-and-conquer이란">1. 분할정복(Divide and Conquer)이란?</h2>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/397b9a53-6b0f-4659-99be-983c1606494e/image.png" alt=""></p>
<blockquote>
<p><strong>&#39;큰 문제&#39;를 &#39;작은 문제&#39;로 나누어서 해결하는 알고리즘</strong></p>
</blockquote>
<p>조금 더 풀어 말하자면, 하나의 큰 문제를 작은 부분의 문제들로 나누고, 나눈 부분 문제를 해결하고 해결된 해들을 모아 원래의 문제를 해결해 나가는 방식이다. 따라서 크게 &#39;<strong>분할 &gt; 정복 &gt; 결합</strong>&#39;의 단계를 지닌다. </p>
<ol>
<li><strong>분할(Divide)</strong>: 첫번째로 문제를 ‘작은 부분 문제들로 나눈다.</li>
<li><strong>정복(Conquer)</strong>: 두번째로 각각의 ‘부분 문제’를 해결한다.</li>
<li><strong>결합(Combine)</strong>: 세번째로 ‘부분 문제’들의 해를 모아서 원래 문제의 해를 구한다.</li>
</ol>
<h2 id="2-재귀를-이용한-분할정복">2. 재귀를 이용한 분할정복</h2>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/81afbce3-e7cf-4e73-bbbe-f7ecbaff99c9/image.png" alt=""></p>
<blockquote>
<p>&#39;<strong>재귀&#39;를 활용하여 작은 부분으로 나누어 문제를 해결하지만, 일반적인 재귀와는 차이가 있다</strong></p>
</blockquote>
<p>분할정복은 작은 부분으로 문제를 해결해나가는 과정에서 재귀를 활용하지만, <strong>일반적인 재귀와는 달리 전체의 과정이 분할 후 해결해나가는 과정과, 결합해서 해결해나가는 과정으로 분리되어 있다는 점</strong>에서 차이가 존재한다. 또한, 일반적인 재귀가 가지는 대규모 문제를 해결 시 생기는 호출 스택의 문제를 해결하기에도 분할정복이 더 용이하다는 장점이 있다. 또한, <strong>부분 문제들이 독립적이므로 병렬 처리가 가능</strong>하다. </p>
<p>분할정복의 시간 복잡도는 일반적으로 O(nlogn)을 가진다.</p>
<h2 id="3-구현-방법">3. 구현 방법</h2>
<h4 id="1-quick-sort">1. Quick sort</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/1c80a54a-534f-4402-b2b1-0f174a4803b4/image.png" alt=""></p>
<p>퀵 정렬을 수행해가는 과정은 다음과 같다.</p>
<ol>
<li><p><strong>배열에서 pivot을 선택</strong>한다. 이때 pivot은 배열의 중간값, 첫 번째 값, 마지막 값 등 여러 기준으로 선택될 수 있다.</p>
</li>
<li><p>pivot을 기준으로 <strong>두 개의 서브 배열로 분할</strong>해, Piovt보다 작은 값을 왼쪽 서브 배열에, 큰 값은 오른쪽 서브 배열에 위치시킨다.</p>
</li>
<li><p>각 서브 배열을 <strong>quick sort</strong>한다.</p>
</li>
<li><p>정렬된 <strong>서브 배열을 결합</strong>한다.</p>
</li>
</ol>
<pre><code>public static void quickSort(int[] arr, int left, int right) {
    if (left &gt;= right) return;

    int pivot = partition(arr, left, right);

    quickSort(arr, left, pivot - 1);
    quickSort(arr, pivot + 1, right);
}

/**
 * 분할 적용 : 주어진 구간에서 피봇을 선택하고, 분할을 수행하는 함수
 */
public static int partition(int[] arr, int left, int right) {
    int pivot = arr[left];
    int i = left + 1;
    int j = right;

    while (i &lt;= j) {
        while (i &lt;= right &amp;&amp; arr[i] &lt; pivot) i++;
        while (j &gt;= left + 1 &amp;&amp; arr[j] &gt; pivot) j--;

        if (i &gt; j) {
            swap(arr, left, j);
        } else {
            swap(arr, i, j);
        }
    }
    return j;
}

/**
 * 값 변경 : 배열에서 두 원소의 위치를 바꾸는 함수
 */
public static void swap(int[] arr, int i, int j) {
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}</code></pre><h4 id="2-binary-search">2. Binary Search</h4>
<p>이진 검색은 분할 정복의 3단계 구현 과정을 그대로 적용시킬 수 있다.</p>
<ol>
<li>분할: <strong>배열을 절반으로</strong> 나눈다.</li>
<li>정복: 찾으려는 값이 <strong>절반 중 하나에 위치해있는지 확인</strong>하는 부분 문제를 해결한다.</li>
<li>결합: 위의 부분문제를 다 모아 원래 해결하려 했던 <strong>원래의 해를 구하는 과정을 수행</strong>한다.</li>
</ol>
<h4 id="3-merge-sort">3. Merge sort</h4>
<p>병합 정렬은 리스트를 두 개의 균등한 크기로 분할한 후, 각각을 정렬해 두 개의 정렬된 리스트를 합하여 전체를 정렬하는 방식으로 문제를 해결하는 방식이다.
과정은 다음과 같다.</p>
<ol>
<li>리스트를 두 개의 균등한 크기로 분할한다.</li>
<li>각 부분 리스트를 재귀적으로 merge sort한다.</li>
<li>두 부분 리스트를 다시 하나의 정렬된 리스트로 merge sort한다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Algorithm] 08. Greedy Algorithm]]></title>
            <link>https://velog.io/@younjin_02/08.-Greedy-Algorithm</link>
            <guid>https://velog.io/@younjin_02/08.-Greedy-Algorithm</guid>
            <pubDate>Sun, 11 May 2025 02:56:09 GMT</pubDate>
            <description><![CDATA[<h2 id="1-greedy-algorithm이란">1. Greedy Algorithm이란?</h2>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/75d472c4-cd64-4d08-9dfa-a80cc66d12f2/image.png" alt=""></p>
<blockquote>
<p><strong>현재 상태에서 보는 선택지 중 최선의 선택지가 전체 선택지 중 최선의 선택지라고 가정하는 알고리즘</strong></p>
</blockquote>
<p>위의 트리구조를 봤을 때, 그리디 알고리즘의 경우는 그 단계단계마다 무조건 제일 큰 쪽, 즉 최선의 선택지를 선택하는 것을 확인할 수 있다. </p>
<p>핵심 이론은 다음과 같다.</p>
<ol>
<li><strong>Solution Procedure</strong>:현재 상태에서 <strong>가장 최선이라고 생각하는 해를 선택</strong></li>
<li><strong>Feasibility Check</strong>: 현재 선택한 해가 전체 문제의 <strong>제약 조건에서 벗어나지 않는지를 검사</strong></li>
<li><strong>Solution Check</strong>: 현재까지 선택한 해 집합이 <strong>전체 문제를 해결할 수 있는지 검사</strong>한다. 전체 문제를 해결하지 못할 시, 1로 돌아가 반복</li>
</ol>
<h2 id="2-적용-예시">2. 적용 예시</h2>
<p>그리디 알고리즘의 대표적인 예시로는 거스름돈 문제가 있다.</p>
<blockquote>
<p>마트에서 계산을 하는 점원이 되었다. 
손님에게 거슬러줘야할 돈이 N원일 때 거슬러줘야 할 동전의 최소 개수를 구하라. 이때 
거스름돈으로 사용할 동전은 500원, 100원, 50원, 10원으로 무한히 존재하고, N은 10의 배수로 가정</p>
</blockquote>
<p>이때, 최소 개수를 구하기 위해서는 &#39;<strong>가장 큰 화폐 단위부터</strong>&#39; 돈을 거슬러 줘야 한다.
아래가 예시 코드이다.</p>
<pre><code>import java.util.Scanner;

public class Main
{
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int total = sc.nextInt();
        int minCoinCnt = 0;
        int coins[] = {500, 100, 50, 10};

        for (int coin : coins){
            minCoinCnt += (total/coin);
            total %= coin;
        }

        System.out.println(&quot;result = &quot; + minCoinCnt);
    }
}
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Algorithm] 07. Brute-Force algorithm]]></title>
            <link>https://velog.io/@younjin_02/Algorithm-07.-Brute-Force-algorithm</link>
            <guid>https://velog.io/@younjin_02/Algorithm-07.-Brute-Force-algorithm</guid>
            <pubDate>Wed, 30 Apr 2025 17:46:41 GMT</pubDate>
            <description><![CDATA[<h2 id="brute-force-algorithm이란">Brute force algorithm이란?</h2>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/f28cd273-80ad-42db-a229-bc2e4f5df42e/image.png" alt=""></p>
<blockquote>
<p>Brute(무식한), Force, 즉, 무식한 힘을 가진 알고리즘이란 뜻으로, *<em>가능한 모든 경우의 수를 모두 탐색하며 결과를 도출하는 알고리즘 *</em></p>
</blockquote>
<p>말 그대로, 무식하게 가능한 모든 경우의 수를 탐색하는 알고리즘으로서, <strong>전체 탐색</strong> 또는 <strong>완전 탐색</strong>이라고도 부른다. 대부분의 경우 <strong>반복문과 조건문을 통하여 결과를 도출</strong>해낸다. 대표적인 예시로, 숫자 자물쇠를 들 수 있다. 가능한 모든 숫자 조합을 하나씩 시도해보는 것, 이때 브루트 포스 알고리즘이 활용된다.</p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/0a1a612a-bdbc-4452-90e9-06cbc44c96ae/image.png" alt=""></p>
<p>알고리즘을 구현하기가 비교적 쉽고, 모든 경우의 수를 탐색하는 알고리즘이기에 <strong>정확성 100%를 보장한다는 확실한 장점</strong>이 있다. 하지만, <strong>메모리 효율면에서 매우 비효율적</strong>이며, <strong>실행 시간이 매우 오래걸린다</strong>(<strong>시간복잡도가 높음</strong>)는 확실한 단점이 존재한다. 따라서 가능한 경우의 수가 적을 때만 사용하는 것이 바람직하며, 경우의 수가 커지면 실행 시간이 급격하게 늘어나므로 다른 효율적인 알고리즘으로 대체해야 한다.</p>
<p>대표적으로 크게 두 가지 상황에서 사용한다.</p>
<ul>
<li><strong>프로세스 속도를 높이는데 사용할 수 있는 다른 알고리즘이 없는 경우</strong></li>
<li><strong>문제를 해결하는 여러 솔루션이 있고, 각 솔루션을 확인해야 하는 경우</strong></li>
</ul>
<p>이처럼 문제에 더 적절한 해결 방법을 찾기 전에 시도해보는 알고리즘이라고 할 수 있다.</p>
<h2 id="구현-방법">구현 방법</h2>
<h4 id="1-반복문-활용">1. 반복문 활용</h4>
<p>대표적인 방식으로, <strong>그저 for문을 여러번 중첩해서 구현시키는 방식</strong>이다. 보통 이중 for문까지는 주로 많이 쓰지만, 그 이상으로는 잘 중첩시키지 않는데, 이 알고리즘에서는 필요할 수도 있다. <del>그냥 딴거 쓰면 안ㄷ</del></p>
<pre><code>import java.io.*;
import java.util.StringTokenizer;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringTokenizer st = new StringTokenizer(br.readLine());

        int N = Integer.parseInt(st.nextToken());
        int M = Integer.parseInt(st.nextToken());

        int [] numberArray = new int[N];

        st = new StringTokenizer(br.readLine());
        br.close();

        for(int i = 0; i &lt; N; i++) {
            numberArray[i] = Integer.parseInt(st.nextToken());
        }
        bw.write( blackjack(numberArray, N, M) + &quot;\n&quot;);
        bw.flush();
        bw.close();

    }

    public static int blackjack(int[] arrays, int N, int M){
        int sum = 0; //3수의 합을 구하는 변수
        int temp = 0; //근사치를 저장하여 저장하는 변수 (이전 for문의 3개의 합을 더해서 계속 비교해준다.

        for(int i = 0; i &lt; N-2; i++) { //숫자가 겹치면 안되기 때문에 i는 0부터 시작하고 N-2까지
            for(int j = i+1; j &lt; N-1; j++) { // 다음 j는 i+1 부터 시작하고 N-1까지
                for(int k = j+1; k &lt; N; k++) { //다음 k는 j+1부터 시작 N까지 반복
                    sum = arrays[i] + arrays[j] + arrays[k]; //3수를 합쳐서
                    if(sum == M){ //수가 이미 같으면 바로 return 한다.
                        return sum;
                    }
                    if(sum &gt; temp &amp;&amp; sum &lt;= M){ //이전 근사치보다 큰데 M보다 작거나 같다는 것은 이전 근사치보다 M에 가깝다는 뜻
                        temp = sum;
                    }
                }
            }
        }
        return temp; // 완전히 같은 수가 없을 때는 temp(가장 근사치)반환
    }
}</code></pre><h4 id="2-재귀백트래킹">2. 재귀(백트래킹)</h4>
<p>동일한 코드를 반복하는 반복문을 피하고자, 재귀를 활용하여 조금 더 코드를 간단하게 구현할 수 있다. base case(기저 조건)을 잘 설정한 후에, 반복문의 중첩을 재귀로 풀어내는 방식이다.</p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/ed0fb4c6-6012-4559-9e5d-df4b4137257b/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Algorithm] 06. Minimum Spanning Tree(MST)]]></title>
            <link>https://velog.io/@younjin_02/Algorithm-06.-Minimum-Spanning-TreeMST</link>
            <guid>https://velog.io/@younjin_02/Algorithm-06.-Minimum-Spanning-TreeMST</guid>
            <pubDate>Wed, 16 Apr 2025 10:42:02 GMT</pubDate>
            <description><![CDATA[<h2 id="최소-신장-트리minimum-spanning-tree">최소 신장 트리(Minimum Spanning Tree)</h2>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/8455e645-f152-4707-9659-83262cd88a91/image.png" alt=""></p>
<blockquote>
<p>MST, 최소 신장 트리란 그래프에서 <strong>모든 노드를 연결할 때 사용된 에지들의 가중치의 합을 최소로 하는 트리</strong>이다.</p>
</blockquote>
<p>MST를 구현하는 대표적인 방식 두 가지는 <strong>&#39;크루스칼&#39;</strong>과 <strong>&#39;프림&#39;</strong>이 있다. MST는 다음과 같은 특징을 지닌다.</p>
<p>MST는 사이클이 포함되면 가중치의 합이 최소가 될 수 없으므로 <strong>사이클을 포함하지 않는다</strong>. N개의 노드가 있을 시, 에지의 개수가 N개가 되면 모든 노드를 연결하는 사이클이 생성되므로, <strong>N개의 노드가 있을 때 MST를 구성하는 에지의 개수는 항상 N-1개</strong>다.</p>
<h3 id="mst-핵심-이론---kruskals-algorithm">MST 핵심 이론 - Kruskal’s Algorithm</h3>
<h4 id="1-에지-리스트로-그래프를-구현하고-union-find-리스트-초기화">1. <strong>에지 리스트로 그래프를 구현하고 Union find 리스트 초기화</strong></h4>
<p>MST는 데이터를 노드가 아닌 에지 중심으로 저장해야 하므로 <strong>에지 리스트를 사용하여 그래프를 구현</strong>한다. </p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/635e451b-2d80-4206-ab47-1c0860f8a9cc/image.png" alt=""></p>
<p>에지 리스트로 그래프를 구현한 후에 &#39;union find&#39;로 리스트를 초기화하는 이유는, 사이클을 판별해내기 위해서이다. 따라서 MST를 구현하기 위해서는 <strong>기본적으로 Union find    알고리즘의 동작원리를 이해하고 있어야 한다</strong>. 초기화는 리스트의 인덱스를 해당 자리의 값으로 설정하여 초기화하면 된다.</p>
<h4 id="2-데이터를-가중치-기준으로-정렬">2. 데이터를 <strong>가중치 기준으로 정렬</strong></h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/cfe8d256-8332-4674-8170-d7e1d637a30d/image.png" alt=""></p>
<p>그래프 데이터를 가중치 기준 <strong>오름차순 정렬</strong>한다.</p>
<h4 id="3-가중치가-낮은-에지부터-연결-시도--반복">3. <strong>가중치가 낮은 에지부터 연결 시도 &amp; 반복</strong></h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/8712b06e-0bc3-4bf1-951b-336359519a22/image.png" alt=""></p>
<blockquote>
<p>매 연결 시도마다 <strong>find연산을 수행해서 대표 노드가 같은지 다른지 여부를 확인해 사이클 생성여부를 확인한 후 연결</strong>한다!</p>
</blockquote>
<p>위의 글처럼, find연산을 수행시켰을 때 대표 노드가 같지 않을 경우에 연결을 하고, 후에 Union 연산을 수행해 Union find 리스트를 수정시킨다. 반복하는 과정은 아래와 같다.</p>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/cdadd4a6-0277-4bb7-8cce-4b1ed08c1546/image.png" alt=""></p>
<p>위의 예시에서는 에지 4가 유니온 연산에서 사이클이 생성되서 에지 4는 연결 시도가 실패한다. 이렇게 모든 연산을 수행했을 때 에지의 개수가 <strong>노드의 개수 -1</strong>이라는 것을 위의 예시를 통해서도 확인할 수 있다.</p>
<h4 id="4-총-에지-비용-출력">4. 총 에지 비용 출력</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/978fa189-c832-4bdc-a5bf-193bca6623b2/image.png" alt=""></p>
<p><strong>총 에지의 개수가 N-1이 되면, 알고리즘을 종료하고 총 가중치의 합을 출력한다.</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Algorithm] 05. Traversal of Graph]]></title>
            <link>https://velog.io/@younjin_02/Algorithm-05.-Traversal-of-Graph</link>
            <guid>https://velog.io/@younjin_02/Algorithm-05.-Traversal-of-Graph</guid>
            <pubDate>Tue, 08 Apr 2025 14:03:14 GMT</pubDate>
            <description><![CDATA[<h3 id="1-dfs깊이-우선-탐색">1. DFS(깊이 우선 탐색)</h3>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/a32c7ba3-e7d3-4c06-bd41-8a184d4004bc/image.png" alt=""></p>
<blockquote>
<p>&#39;탐색할 <strong>한 쪽 분기를 정하여 최대 깊이까지 탐색을 마친 후</strong>, 다른 쪽 분기로 이동해 다시 탐색하는 알고리즘&#39;</p>
</blockquote>
<p>DFS(Depth-First Search), 깊이 우선 탐색은 그래프를 &#39;완전&#39; 탐색할 때 활용된다. 즉, <strong>그래프의 모든 정점을 방문하는 데 사용되는 알고리즘</strong>이다. 주로 <strong>Recursion</strong> 및 <strong>Stack *<em>자료 구조를 활용하여 구현하며, 시간복잡도는 *</em>O(V+E)</strong>로 표현된다. 
**노드 수 = V, 에지 수 = E</p>
<p>DFS는 대부분 재귀 함수를 이용하여 구현하기 때문에, <strong>Stack Overflow를 조심해야 한다</strong>. 이는 특정 함수가 계속 자신을 호출하여 계속 재귀적으로 함수가 호출되는 현상을 말한다.
<img src="https://velog.velcdn.com/images/younjin_02/post/1f8d0135-cb00-421d-bab7-15c898c5977f/image.png" alt=""></p>
<p>DFS는 보통 단절점 찾기, 단절선 찾기, 사이클 찾기, 위상 정렬 등에서 주로 활용된다.</p>
<h4 id="dfs-핵심이론">DFS 핵심이론</h4>
<p>DFS는 한 번 방문한 노드를 다시 방문하면 안 되기 때문에, <strong>노드 방문여부를 체크할 배열이 필요</strong>하다. 보통 처음에 ArrayList로 그래프를 표현하고, 이 방문 배열을 초기화한 후에 DFS 구현을 시작한다. 방문한 노드는 방문배열에 체크하여 다시 꼭 방문하지 않도록 설정해주어야 한다.</p>
<h3 id="2-bfs너비-우선-탐색">2. BFS(너비 우선 탐색)</h3>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/ad15ccc6-1846-4cf3-8cbd-4048b22f3632/image.png" alt=""></p>
<blockquote>
<p>&#39;시작 노드에서 출발해 시작 노드를 기준으로 <strong>가까운 노드를 먼저 방문하면서 탐색</strong>하는 알고리즘&#39;</p>
</blockquote>
<p>BFS(Breadth-First Search), 너비 우선 탐색 또한 <strong>그래프를 완전 탐색</strong>하는 알고리즘 방법이다. <strong>FIFO 탐색</strong>을 시행하며, 이를 바탕으로 <strong>Queue 자료구조</strong>를 주로 사용하여 구현한다. 시간복잡도는 동일하게 <strong>O(V+E)</strong>이다. 목표 노드에 도착하는 경로가 여러 개일때 최단 경로를 보장한다.</p>
<ul>
<li>BFS가 최단 경로를 보장하는 이유
<img src="https://velog.velcdn.com/images/younjin_02/post/cf334ea6-7219-4c6f-8fbc-b0b606b318a6/image.png" alt=""></li>
</ul>
<h4 id="bfs-핵심이론">BFS 핵심이론</h4>
<p>DFS와 마찬가지로 방문했던 노드는 다시 방문하지 않아야 하기에 방문배열이 필요하다. 또한 그래프를 ArrayList로 구현하는 것 또한 동일하다. 구현 방식은 다음과 같다</p>
<ol>
<li>시작할 노드 정하고, 사용할 자료구조 초기화</li>
<li>Queue에서 노드를 꺼낸 후, 꺼낸 노드의 인접 노드를 다시 Queue에 삽입(방문 배열 체크, 꺼낸 노드 탐색 순서 기록)</li>
<li>Queue에 남은 노드가 없을때 까지 반복 
<img src="https://velog.velcdn.com/images/younjin_02/post/0619ebfa-6bee-4420-8a8a-a14040f85c54/image.png" alt=""></li>
</ol>
<h3 id="3-binary-search이진-탐색">3. Binary Search(이진 탐색)</h3>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/bf3d334e-9e1d-4c2c-bf38-6086e6c4c641/image.png" alt=""></p>
<blockquote>
<p>&#39;대상 데이터의 중앙값과 찾고자 하는 값을 비교해 <strong>데이터의 크기를 절반씩 줄이면서 대상을 찾는 알고리즘</strong>&#39;</p>
</blockquote>
<p>이진 탐색은 앞의 두 가지 그래프 완전 탐색과는 다른, <strong>타깃 데이터를 탐색</strong>하는, 정렬된 데이터에서 원하는 데이터를 탐색할 때 사용하는 가장 일반적인 알고리즘이다. <strong>시간 복잡도는 O(logN)</strong>이다.</p>
<h4 id="이진-탐색-핵심-이론">이진 탐색 핵심 이론</h4>
<p>이진 탐색은 오름차순으로 정렬된 데이터에서 다음 4가지 과정을 반복한다.
<img src="https://velog.velcdn.com/images/younjin_02/post/68e09816-22f0-4a33-9ebd-c0e84cc637e0/image.png" alt=""></p>
<p>이진 탐색은 꼭 <strong>&#39;데이터가 정렬되있어야 한다는 점</strong>&#39;을 기억해야 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Algorithm] 04. Basic Graph Implementation]]></title>
            <link>https://velog.io/@younjin_02/Algorithm-04.-Basic-Graph-Implementation</link>
            <guid>https://velog.io/@younjin_02/Algorithm-04.-Basic-Graph-Implementation</guid>
            <pubDate>Wed, 02 Apr 2025 15:22:01 GMT</pubDate>
            <description><![CDATA[<h2 id="1-graph의-정의">1. Graph의 정의</h2>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/d4823cb1-3bc6-40f7-93e3-e44f455050ad/image.png" alt=""></p>
<blockquote>
<p><strong>&#39;그래프는 Node와 edge로 구성된 집합이다&#39;</strong></p>
</blockquote>
<ul>
<li>Node: 데이터를 표현하는 단위</li>
<li>Edge: Node를 연결</li>
<li>가중치(Weight): 에지에 부여된 숫자값</li>
<li>Tree 또한 그래프의 일종</li>
</ul>
<h4 id="grpah-algorithm에는-특징적으로-다음과-같은-세부-알고리즘들이-존재한다">Grpah Algorithm에는 특징적으로 다음과 같은 세부 알고리즘들이 존재한다</h4>
<ul>
<li>Union-Find: 노드들이 같은 그룹에 속해 있는지 확인할 때 사용, 싸이클 유무 판단</li>
<li>위상 정렬(Topological sort): 선후 관계가 있는 작업들에 유용, 비순환 그래프의 노드들을 순서대로 정렬하는 알고리즘</li>
<li>다익스트라(Dijkstra): 단일 출발점에서 모든 node까지의 최단 거리 계산, 간선 가중치가 음수가 없을 때 사용</li>
<li>벨만-포드(Bellman-Ford): 다익스트라와 유사하지만, 음수 가중치 허용</li>
<li>플로이드 워셜(Floyd-Warshall): 시작점이 없는, 모든 정점 쌍 간의 최단 거리 계산, DP 방식으로 구현</li>
<li><strong>최소 신장 트리(MST, Minimum Spanning Tree): 모든 노드를 최소 비용으로(최소 가중치 합으로) 연결하는 트리 구성</strong></li>
</ul>
<h2 id="2-graph의-표현">2. Graph의 표현</h2>
<blockquote>
<p>그래프를 구현하는 3가지 방법
<strong>1. 에지 리스트
2. 인접 행렬
3. 인접 리스트</strong></p>
</blockquote>
<h4 id="1-에지-리스트">1. 에지 리스트</h4>
<p><img src="https://velog.velcdn.com/images/younjin_02/post/937101dd-ffde-4caa-8148-38eb2d650eaa/image.png" alt=""></p>
<blockquote>
<p>에지 리스트는 <strong>&#39;에지&#39;</strong>를 중심으로 그래프를 표현한다. 에지 리스트는 배열에 <strong>출발 노드, 도착 노드</strong>를 저장하여 에지를 표현하며, 필요한 경우에 <strong>가중치</strong>도 저장하여 가중치가 있는 에지를 표현한다.</p>
</blockquote>
<ul>
<li><p>가중치 없는 그래프 표현
<img src="https://velog.velcdn.com/images/younjin_02/post/d69c20dc-d09b-4106-b629-ceb7f5c6a784/image.png" alt="">
가중치가 없는 경우에는 출발점과 도착점만 표현하면 되므로, 배열의 행은 2개면 충분하다. 예를 들면 1에서 2로 가는 에지는 [1,2]로 표현 가능하다. 주의해야할 부분은, 방향이 없는 그래프일 경우, [1,2],[2,1] 두 배열 모두 리스트에 넣어 방향이 없는 경우를 표현해줘야 한다.</p>
</li>
<li><p>가중치 있는 그래프 표현
<img src="https://velog.velcdn.com/images/younjin_02/post/245a4e7f-ccba-4863-8df7-3ae65845d6da/image.png" alt=""></p>
</li>
<li><p><em>행을 3개로 늘려 3번째 행에 가중치를 저장*</em>해주면 된다.</p>
</li>
</ul>
<p>에지 리스트는 구현하기는 쉽지만, <strong>특정 노드와 관련된 에지를 탐색하기에는 높은 시간 복잡도가 부여되는 문제</strong>가 있어, 이런 문제를 해결하기 위해 벨만 포드나 크루스칼과 같은 알고리즘을 활용한다. </p>
<h4 id="2-인접-행렬adjacency-matrix">2. 인접 행렬(Adjacency matrix)</h4>
<blockquote>
<p>인접 행렬은 2차원 배열을 이용하여 그래프를 표현한다. 노드가 N개인 경우, N*N배열을 형성해 그래프를 표현한다. </p>
</blockquote>
<ul>
<li><p>가중치 없는 그래프 표현
<img src="https://velog.velcdn.com/images/younjin_02/post/ebe9b53c-ae20-402e-a20a-b2ba5bf915b3/image.png" alt="">
위의 그림과 같이 가중치가 없으므로 1을 저장하여 특정 노드에서 특정 노드로 향하는 에지가 있음을 표현한다.</p>
</li>
<li><p>가중치 있는 그래프 표현
<img src="https://velog.velcdn.com/images/younjin_02/post/60fceeef-4f7d-4ee2-a5d6-1441fcda0eea/image.png" alt="">
위의 행렬 형태에서 가중치를 넣어주면 된다. </p>
</li>
</ul>
<p>인접 행렬 또한 에지 리스트처럼 구현은 쉽다. 하지만 인접 행렬 역시 특정 에지를 탐색하려면 N번 접근해야 한다는, 노드 개수에 비해 에지가 적을 때 <strong>시간 복잡도 면에서 확실한 단점이 존재</strong>한다. 또한 N<em>N배열을 형성해야 하기 때문에 *</em>공간 효율성 또한 떨어진다<strong>. 따라서 **경우에 따라 이를 적절히 활용하는 능력</strong>이 필요하다. </p>
<h4 id="3-인접-리스트arraylist">3. 인접 리스트(ArrayList)</h4>
<blockquote>
<p>ArrayList를 활용하여 그래프를 표현한다. <strong>실제 그래프를 표현하는데 있어 제일 많이 사용하는 방식이다</strong>.</p>
</blockquote>
<ul>
<li><p>가중치 없는 그래프 표현
<img src="https://velog.velcdn.com/images/younjin_02/post/1a93a106-33d6-479b-ba5f-6964ec5f7064/image.png" alt="">
인접 리스트가 그래프 표현에 있어서 제일 많이 사용되는 이유가 바로 드러난다. <strong>인접 리스트는 가변적인 자료구조</strong>이므로, 해당 이미지에서 1 리스트와 연결된 리스트의 요소 개수를 언제든 늘릴 수 있기 때문에, 그래프를 표현하기에 효과적이다. </p>
</li>
<li><p>가중치 있는 그래프 표현
<img src="https://velog.velcdn.com/images/younjin_02/post/7dc3b738-3dea-43c5-9b1c-c5e7af352335/image.png" alt="">
가중치가 있는 경우에는 다음과 같이 <strong>Node 클래스를 선언</strong>하여 활용한다. </p>
</li>
</ul>
<p>인접 리스트를 이용한 그래프 구현은 복잡한 편이지만, <strong>에지를 탐색하는 시간이 매우 뛰어나며, 노드 개수가 커도 공간 효율이 좋아 메모리 초과 에러도 발생하지 않는다</strong>. 따라서 보통의 코테에서는 인접 리스트를 가장 많이 활용한다. </p>
]]></description>
        </item>
    </channel>
</rss>