<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>A-beol.log</title>
        <link>https://velog.io/</link>
        <description>차근차근</description>
        <lastBuildDate>Tue, 31 Mar 2026 08:39:38 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>A-beol.log</title>
            <url>https://images.velog.io/images/choi-jiae/profile/02c762ee-46a0-4c0f-a04e-edacc1b701a0/social.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. A-beol.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/choi-jiae" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[이클립스 svn 연결 오류 (An error occured while accessing the repository entry)]]></title>
            <link>https://velog.io/@choi-jiae/svn-%EC%97%B0%EA%B2%B0</link>
            <guid>https://velog.io/@choi-jiae/svn-%EC%97%B0%EA%B2%B0</guid>
            <pubDate>Tue, 31 Mar 2026 08:39:38 GMT</pubDate>
            <description><![CDATA[<p>svn으로 형상관리를 하게 되어... svn을 연결하려고 했는데...!</p>
<pre><code>An error occured while accessing the repository entry</code></pre><p>이런 에러가 뜸</p>
<p><img src="https://velog.velcdn.com/images/choi-jiae/post/43d99fea-85ae-40b0-a3b0-f0c3c4a267ed/image.png" alt=""></p>
<h3 id="해결-방법">해결 방법</h3>
<p><img src="https://velog.velcdn.com/images/choi-jiae/post/721c232e-21a6-429e-9546-9dd15021f00f/image.png" alt="">
<img src="https://velog.velcdn.com/images/choi-jiae/post/1456506f-eb1e-495c-a1f7-62b54effa6c0/image.png" alt="">
window &gt; Preference &gt; SVN &gt; SVN Connector 에서
SVN Connector 를 사진에 보이는 것으로 변경했더니 해결!</p>
<p>참고
<a href="https://velog.io/@armton/ERR-eclipse-svnsubversive-An-error-occurred-while-accessing-the-repository-entry">[ERR] eclipse - svn(subversive) An error occurred while accessing the repository entry</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ColorThief를 활용하여 이미지에 따라 스타일 바꾸기]]></title>
            <link>https://velog.io/@choi-jiae/ColorThief%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%98%EC%97%AC-%EC%9D%B4%EB%AF%B8%EC%A7%80%EC%97%90-%EB%94%B0%EB%9D%BC-%EC%8A%A4%ED%83%80%EC%9D%BC-%EB%B0%94%EA%BE%B8%EA%B8%B0</link>
            <guid>https://velog.io/@choi-jiae/ColorThief%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%98%EC%97%AC-%EC%9D%B4%EB%AF%B8%EC%A7%80%EC%97%90-%EB%94%B0%EB%9D%BC-%EC%8A%A4%ED%83%80%EC%9D%BC-%EB%B0%94%EA%BE%B8%EA%B8%B0</guid>
            <pubDate>Thu, 20 Feb 2025 07:00:48 GMT</pubDate>
            <description><![CDATA[<h2 id="📌목표">📌목표</h2>
<p>배경에 들어가는 이미지의 명도에 따라 글씨색을 흰색/검은색으로 바뀌게 하고 싶었다. 아래의 사진처럼 밝은 사진에는 검은색 글씨, 어두운 사진에는 흰색 글씨를 사용하여 가독성이 좋아지도록 만들고 싶었다...!
(사진 상으로는 검은색 글씨가 오히려 잘 안 보이는 것 같지만 나중에 배경 그라데이션을 조정해 보는 것으로....ㅎㅎ)</p>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/choi-jiae/post/c19acc12-744a-484c-83b6-08744206dfad/image.png" alt="">☀️ 밝은 이미지</th>
<th><img src="https://velog.velcdn.com/images/choi-jiae/post/c4fce74a-16ed-4b6d-8049-49cdb7e262d8/image.png" alt="">🌜어두운 이미지</th>
</tr>
</thead>
</table>
<h3 id="🤔-무엇을-하면-될까">🤔 무엇을 하면 될까?</h3>
<h3 id="1-이미지의-대표-색-추출하여-명도-계산하기">1. 이미지의 대표 색 추출하여 명도 계산하기</h3>
<p>웹 이미지의 명도를 어떻게 계산하면 좋을까 고민하다가 <a href="https://lokeshdhakar.com/projects/color-thief/">ColorThief</a>라는 모듈과 <a href="https://en.wikipedia.org/wiki/Relative_luminance">Relative luminance</a> 계산법에 대해 알게 되었다. </p>
<p>ColorThief는 local 이미지나 웹 이미지의 컬러 팔레트를 추출할 수 있는 npm 모듈이다. javascript만 지원하고 있어서 typescript로 해당 모듈을 사용하려면 따로 type 명시를 해주어야 한다. 이 부분은 이후 에러 해결 파트에서 자세히 다루어보겠다!</p>
<p>Relative luminance는 색의 상대적인 밝기를 의미하는 것으로 해당 지표를 사용하여 색의 밝기 차이를 계산할 수 있다.</p>
<blockquote>
<p>Relative luminance = 0.2126<em>R + 0.7152</em>G + 0.0722*B</p>
</blockquote>
<h3 id="2-명도에-따라-적용하는-css-달라지게-하기">2. 명도에 따라 적용하는 css 달라지게 하기</h3>
<p>각 이미지 별 대표 색상에 대해 Relative luminance를 계산한 뒤에는 어두운 이미지를 결정하는 threshold를 지정해야 한다. WCAG에서 권장하는 최소 color contrast는 4.5:1 이므로 이 비율을 참고하기로 했다.</p>
<p>흰색의 Relative luminance를 계산하면 255이므로, 약 5:1 쯤 되는 50 이하일 때 해당 이미지를 어두운 이미지라고 분류하기로 했다. 해당 정보를 useState로 관리하고 state에 따라 적용되는 글자 색이 달라지도록 css를 작성했다.</p>
<pre><code>          &lt;div
            className={`review-top-content ${
              isDarkImage ? &quot;light-text&quot; : &quot;dark-text&quot;
            }`}
          &gt;
            &lt;div className=&quot;play-discription&quot;&gt;{play.genre}&lt;/div&gt;
            &lt;div className=&quot;play-name&quot;&gt;{play.title}&lt;/div&gt;
            &lt;div className=&quot;play-discription&quot;&gt;{play.period}&lt;/div&gt;
            &lt;div className=&quot;play-discription&quot;&gt;{play.location}&lt;/div&gt;
          &lt;/div&gt;</code></pre><h2 id="🚨-마주친-오류들">🚨 마주친 오류들...</h2>
<h3 id="1-color-thief-import-실패-typescript-버전-없음">1. color-thief import 실패 (typescript 버전 없음)</h3>
<p>ColorThief를 사용하려던 첫 순간부터 오류를 맛보았다. 분명 모듈을 설치하고 import를 한 것임에도 빨간줄이 뜨는 것이었다. 알고 보니 ColorThief는 javascript만을 지원하고 있어서 발생하는 오류였다. typescript 버전이 따로 없는 경우에는 직접 type 지정을 해주면 된다고 한다.</p>
<p><a href="https://github.com/lokesh/color-thief/issues/188">https://github.com/lokesh/color-thief/issues/188</a>
위 깃허브를 참고해서 type 지정을 해주었다.</p>
<blockquote>
<p>src &gt; types &gt; colorthief.d.ts</p>
</blockquote>
<p>파일을 생성하고 ts 파일에</p>
<pre><code>declare module &#39;colorthief&#39; {
    type Color = [number, number, number];
    export default class ColorThief {
      getColor: (img: HTMLImageElement | null) =&gt; Color;
      getPalette: (img: HTMLImageElement | null) =&gt; Color[];
    }
  }</code></pre><p>이 코드를 넣었더니 import 문제가 해결되었다.</p>
<h3 id="2-cors-오류">2. CORS 오류</h3>
<p>웹 이미지 url을 사용하려다보니 CORS 문제가 발생해 컬러 팔레트가 추출되지 않는 문제가 발생했다.</p>
<p><strong>시도 1</strong>
<a href="https://stackoverflow.com/questions/23123237/drawing-images-to-canvas-with-img-crossorigin-anonymous-doesnt-work">https://stackoverflow.com/questions/23123237/drawing-images-to-canvas-with-img-crossorigin-anonymous-doesnt-work</a>
위 스택오버플로우를 참고하여 image의 crossOrigin을 Anonymous로 설정해보았다.</p>
<pre><code>img.onload = () =&gt; {
 const result = colorThief.getColor(img);
const [r, g, b] = result;
const brightness = 0.2126 * r + 0.7152 * g + 0.0722 * b;;
setIsDarkImage(brightness &lt; 50);
};
img.src = play.image;
img.crossOrigin = &#39;Anonymous&#39;;</code></pre><p>그 결과 여전히 CORS 오류가 발생했다......ㅜㅜ</p>
<p><strong>시도 2</strong>
<a href="https://stackoverflow.com/questions/30731209/grabbing-color-palette-from-youtube-thumbnail/30741691#30741691">https://stackoverflow.com/questions/30731209/grabbing-color-palette-from-youtube-thumbnail/30741691#30741691</a>
기존 url 앞에 <code>&#39;http://cors-anywhere.herokuapp.com/&#39;</code>를 붙이면 CORS 오류를 우회할 수 있는 서비스가 있길래 시도해 보았다.</p>
<pre><code>img.onload = () =&gt; {
 const result = colorThief.getColor(img);
const [r, g, b] = result;
const brightness = 0.2126 * r + 0.7152 * g + 0.0722 * b;;
setIsDarkImage(brightness &lt; 50);
};
img.src = &#39;http://cors-anywhere.herokuapp.com/&#39;+play.image;
img.crossOrigin = &#39;Anonymous&#39;;</code></pre><p>CORS 오류는 나지 않지만 ColorThief를 사용할 때 403 오류가 발생하여 컬러 팔레트를 가지고 올 수 없는 문제는 여전했다....img url이 변경되어 403 오류가 발생한 것 같다.</p>
<p><strong>시도 3</strong>
<a href="https://webdoli.tistory.com/181">https://webdoli.tistory.com/181</a>
<a href="https://allorigins.win/">https://allorigins.win/</a>
CORS 오류를 우회할 수 있는 또 다른 서비스를 발견했다. </p>
<pre><code>  const fetchImage = async () =&gt; {
    try {
      const response = await fetch(
        `https://api.allorigins.win/get?url=${encodeURIComponent(play.image)}`
      );
      const data = await response.json();
      const img = new Image();
      img.src = data.contents;
      img.crossOrigin = &quot;Anonymous&quot;;
      img.onload = () =&gt; {
        const colorThief = new ColorThief();
        const color = colorThief.getColor(img);
        setColor(color);
        const [r, g, b] = color;
        const brightness = 0.2126 * r + 0.7152 * g + 0.0722 * b;
        console.log(&quot;brightness&quot;, brightness);
        setIsDarkImage(brightness &lt; 50);
        console.log(&quot;color&quot;, color);
      };
    } catch (error) {
      console.error(&quot;Error fetching image:&quot;, error);
    }
  };</code></pre><p>이 코드처럼 url 부분에 접근을 원하는 페이지의 url을 넣으면 response 값으로 해당 페이지에 대한 정보를 넘겨준다. response를 json으로 파싱한 후, contents값을 통해 이미지 url을 접근하면 CORS와 403 오류 없이 컬러 팔레트를 잘 받아올 수 있었다!!!</p>
<p><strong>참고</strong>
<a href="https://en.wikipedia.org/wiki/Relative_luminance">https://en.wikipedia.org/wiki/Relative_luminance</a>
<a href="https://stackoverflow.com/questions/596216/formula-to-determine-perceived-brightness-of-rgb-color">https://stackoverflow.com/questions/596216/formula-to-determine-perceived-brightness-of-rgb-color</a>
<a href="https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html">https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Swiper.js로 카드 뷰 만들기]]></title>
            <link>https://velog.io/@choi-jiae/Swiper.js%EB%A1%9C-%EC%B9%B4%EB%93%9C-%EB%B7%B0-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@choi-jiae/Swiper.js%EB%A1%9C-%EC%B9%B4%EB%93%9C-%EB%B7%B0-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Thu, 06 Feb 2025 13:57:53 GMT</pubDate>
            <description><![CDATA[<p>React 프로젝트에서 카드를 넘길 수 있는 뷰를 만드는 것이 필요해서 검색해보던 중 Swiper.js라는 효자를 찾았다... 덕분에 카드 뷰를 쉽게 구현할 수 있었다! 공식 사이트에 demo 소스 코드도 많아서 참고하기 좋았다.</p>
<p><strong>공식 사이트</strong>
<a href="https://swiperjs.com/react">https://swiperjs.com/react</a>
<a href="https://swiperjs.com/demos">https://swiperjs.com/demos</a></p>
<p>아무튼 Swiper.js로 이걸 만들 것임 .....👇렛츠고
<img src="https://velog.velcdn.com/images/choi-jiae/post/5bdf67c4-dd68-4092-ace7-4f6b1998e7ea/image.gif" alt="카드 뷰 동작 화면">
<del>화질이 이렇게 깨질줄은 몰랐는데</del></p>
<h3 id="1-설치">1. 설치</h3>
<pre><code>npm i swiper</code></pre><p>명령어로 설치해주면 된다.</p>
<h3 id="2-사용">2. 사용</h3>
<pre><code>import { Swiper, SwiperSlide } from &#39;swiper/react&#39;;
import &#39;swiper/css&#39;;</code></pre><p>필수로 import 해야하는 것은 Swiper, SwiperSlide, css파일이고</p>
<pre><code>import { Navigation, Pagination} from &#39;swiper/modules&#39;;
import &#39;swiper/css/navigation&#39;;
import &#39;swiper/css/pagination&#39;;</code></pre><p>이것들은 필요에 따라 선택적으로 import하면 된다.
navigation은 카드 슬라이드 양 옆에 화살표를 표시하는 모듈이고, Pagination은 카드 슬라이드 아래쪽에 점 표시로 페이지를 표시하는 모듈이다. 나머지 모듈은 공식 문서를 참고하면 설명이 잘 되어있다.</p>
<pre><code>const Home = () =&gt; {
    return (
        &lt;div className=&quot;home&quot;&gt;

            /* 생략 */

                    &lt;div className=&quot;play-cards&quot;&gt;
                        &lt;Swiper
                            spaceBetween={30}
                            slidesPerView={6}
                            modules={[Navigation]}
                            navigation={true}
                            className=&quot;mySwiper&quot;
                            slidesOffsetAfter={100}
                            slidesOffsetBefore={100}
                            &gt;
                            {
                            playList.map((play, index) =&gt; (
                                &lt;SwiperSlide key={index}&gt;
                                    &lt;PlayCard
                                    number={index + 1}
                                    image={play.image}
                                    title={play.title}
                                    genre={play.genre}
                                    review_num={play.review_num}
                                    /&gt;
                                &lt;/SwiperSlide&gt;
                            ))
                            }
                        &lt;/Swiper&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    );
}</code></pre><p>Swiper 컴포넌트 안에 필요한 슬라이드 카드 수 만큼 SwiperSlide를 생성하면된다. 슬라이드 카드의 내용은 SwiperSlide 안에 작성하면 된다. </p>
<h3 id="3-응용-css-수정">3. 응용 (css 수정)</h3>
<p>디테일한 디자인을 수정하고 싶다면 css 파일을 생성하여 다음과 같이 작성하면된다.</p>
<pre><code>.mySwiper {
    position: relative;
  }

 /*카드의 양옆에 흰색 그라데이션 삽입*/
.mySwiper::before,
.mySwiper::after {
  content: &#39;&#39;;
  position: absolute;
  top: 0;
  bottom: 0;
  width: 50px;
  z-index: 5;
  pointer-events: none;
}

.mySwiper::before {
  left: 0;
  background: linear-gradient(to right, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0));
}

.mySwiper::after {
  right: 0;
  background: linear-gradient(to left, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0));
}

/*화살표 설정*/
.swiper-button-prev,
.swiper-button-next {
  --swiper-navigation-color: #9F9F9F;
  --swiper-navigation-size: 35px;
  position:absolute;
  transform: translateY(-100%);
}</code></pre><p>demo를 참고하여 몇 가지 부분을 더 수정하면 원하는 카드 뷰를 쉽게 만들 수 있다!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[npm install 에러]]></title>
            <link>https://velog.io/@choi-jiae/npm-install-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@choi-jiae/npm-install-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Wed, 05 Feb 2025 08:04:35 GMT</pubDate>
            <description><![CDATA[<p>노트북을 바꾸고 환경 세팅을 다시 처음부터 하니 이전에는 발생하지 않았던 에러가 속속들이 나오기 시작했다......ㅜㅜ 하나 하나 고쳐봐야지.....</p>
<h3 id="문제-상황">문제 상황</h3>
<p>기존 repo를 git clone 후 <code>npm install</code>을 할 때 아래와 같은 에러가 발생했다. 사람마다 에러 메시지가 다양하던데, 나는 &#39;could not resolve&#39;라는 에러 메시지가 발생했다.
<img src="https://velog.velcdn.com/images/choi-jiae/post/e684308d-6be7-4057-98ff-79864fe9e3f1/image.png" alt=""></p>
<h3 id="문제-원인">문제 원인</h3>
<p>peer dependency라는 것이 충돌이 나서 발생한 문제였다.
그냥 dependency와 peer dependency의 차이점이 뭔지 찾아보니</p>
<p><strong>dependency</strong>
특정 모듈이 정상적으로 작동하기 위해 필요한 다른 라이브러리나 모듈.
모듈을 사용할 때 반드시 필요하기 때문에 모듈을 설치할 때 자동으로 설치됨.</p>
<p><strong>peer dependency</strong>
어떤 모듈이 특정 버전 또는 버전 범위의 다른 라이브러리와 함께 사용될 때 제대로 작동한다는 것을 나타냄.</p>
<p>위와 같은 차이점이 있었다! dependency는 모듈 안에 포함되는 것, peer dependency는 같은 선상에서 함께 돌아가는 모듈이라고 생각하면 될 것 같다.</p>
<p>npm6 버전에서는 peer dependency가 자동 설치 되지 않지만, npm7부터는 peer dependency를 자동 설치하고 충돌이 발생할 경우 에러를 발생시킨다고 한다. 이번에 node.js를 재설치하면서 npm이 업데이트 되어 발생한 문제 같다...! </p>
<h3 id="해결-방법">해결 방법</h3>
<pre><code>npm install --legacy-peer-deps</code></pre><p>이 명령어를 통해 이미 설치된 peer dependency와 npm7이 요구하는 버전의 peer dependency가 맞지 않을 때 발생하는 error를 무시할 수 있다.</p>
<h4 id="참고">참고</h4>
<p><a href="https://velog.io/@xmun74/%EC%97%90%EB%9F%AC-npm-i-%ED%95%98%EB%A9%B4-npm-ERR-code-ERESOLVE">https://velog.io/@xmun74/%EC%97%90%EB%9F%AC-npm-i-%ED%95%98%EB%A9%B4-npm-ERR-code-ERESOLVE</a>
<a href="https://stackoverflow.com/questions/64573177/unable-to-resolve-dependency-tree-error-when-installing-npm-packages">https://stackoverflow.com/questions/64573177/unable-to-resolve-dependency-tree-error-when-installing-npm-packages</a>
<a href="https://stackoverflow.com/questions/66239691/what-does-npm-install-legacy-peer-deps-do-exactly-when-is-it-recommended-wh">https://stackoverflow.com/questions/66239691/what-does-npm-install-legacy-peer-deps-do-exactly-when-is-it-recommended-wh</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JavaScript] 백준 11047번 동전 0]]></title>
            <link>https://velog.io/@choi-jiae/JavaScript-%EB%B0%B1%EC%A4%80-11047%EB%B2%88</link>
            <guid>https://velog.io/@choi-jiae/JavaScript-%EB%B0%B1%EC%A4%80-11047%EB%B2%88</guid>
            <pubDate>Fri, 15 Nov 2024 01:38:20 GMT</pubDate>
            <description><![CDATA[<h2 id="문제-풀이-아이디어">문제 풀이 아이디어</h2>
<p>동전의 개수를 최소한으로 사용하여 원하는 값을 만들면 된다. i번째 동전의 단위는 i-1번째 동전 단위의 배수이므로 그냥 단순하게 큰 단위부터 동전을 사용하면 최소 동전 개수를 구할 수 있다. </p>
<h2 id="코드-조각">코드 조각</h2>
<h3 id="입출력">입출력</h3>
<pre><code>(async () =&gt; {
    let rl = readline.createInterface({input:process.stdin});
    let data = [];
// data.length가 동전 단위 가짓수와 같아질 때까지 입력 받음
    for await (const line of rl){
        let data_len = data.length;
        if (data_len &gt; 0 &amp;&amp; data_len == data[0][0]){
            rl.close();
        }
        // split을 하지 않고 그냥 push하는 편이 계산이 쉬웠을 것 같다. 
        data.push(line.split(&#39; &#39;).map((el)=&gt;Number(el)));
    }

    solution(data);
    process.exit();

})();</code></pre><p>새로운 입출력 스타일을 도전해 보았다. 그냥 기존처럼 rl.on 방식을 사용하는 편이 더 편한 것 같다. </p>
<h3 id="solution">solution</h3>
<pre><code>const solution = (data) =&gt; {
    const N = data[0][0]
    const K = data[0][1]
    let remain = K;
    let cnt = 0;
    let i = N;

    while (remain &gt; 0){
        cnt += Math.floor(remain/data[i][0]);
        remain = remain%data[i][0];
        i--;
    }

    console.log(cnt);
}</code></pre><p>큰 단위부터 동전을 사용해서 원하는 숫자를 표현한다. 이때 몫 연산이 필요했는데, JS는 몫 연산을 지원하지 않아 &#39;Math.floor()&#39;로 직접 내림을 해줘야 한다. </p>
<h2 id="전체-코드">전체 코드</h2>
<pre><code>const solution = (data) =&gt; {
    const N = data[0][0]
    const K = data[0][1]
    let remain = K;
    let cnt = 0;
    let i = N;

    while (remain &gt; 0){
        cnt += Math.floor(remain/data[i][0]);
        remain = remain%data[i][0];
        i--;
    }

    console.log(cnt);
}


const readline = require(&#39;readline&#39;);

(async () =&gt; {
    let rl = readline.createInterface({input:process.stdin});
    let data = [];

    for await (const line of rl){
        let data_len = data.length;
        if (data_len &gt; 0 &amp;&amp; data_len == data[0][0]){
            rl.close();
        }
        data.push(line.split(&#39; &#39;).map((el)=&gt;Number(el)));
    }

    solution(data);
    process.exit();

})();</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[JavaScript] 백준 1764번 듣보잡]]></title>
            <link>https://velog.io/@choi-jiae/JavaScript-%EB%B0%B1%EC%A4%80-1764%EB%B2%88-%EB%93%A3%EB%B3%B4%EC%9E%A1</link>
            <guid>https://velog.io/@choi-jiae/JavaScript-%EB%B0%B1%EC%A4%80-1764%EB%B2%88-%EB%93%A3%EB%B3%B4%EC%9E%A1</guid>
            <pubDate>Fri, 08 Nov 2024 01:13:27 GMT</pubDate>
            <description><![CDATA[<p>JS로 코테를 준비하기로 마음먹었지만... 그새 마음이 바뀌어 python으로 백준 문제를 풀고 있었다. 하지만 프론트엔드 개발자의 길로 들어서기로 마음 먹은 이상, 이제 정말 JS로 코테 준비를 할 시기가 찾아 온 것 같다. <del>(이제는! 더 이상! 물러날 곳이 없! 다!)</del></p>
<p>그래서 지금부터라도 하루에 한 문제씩 JS로 백준 문제를 풀어보려 한다. 가장 일정이 빠른 코테에서 구름 IDE를 사용한다길래, JS 입출력 코드까지 연습하기 위해 프로그래머스 보다는 백준 문제 풀이를 택했다. python으로 solved.ac의 class 3 초반 정도까지 풀었는데 이어서 남은 문제를 JS로 풀이해보려 한다.</p>
<h3 id="문제-풀이-아이디어">문제 풀이 아이디어</h3>
<p>듣도 못한 사람들과 보도 못한 사람들을 각각 set으로 받아 둘의 교집합을 계산하면 되는 문제다.
아이디어는 금방 생각났지만...JS 문법과 자료형에 익숙하지 않아 서치를 해가면서 문제를 풀었다.</p>
<h3 id="코드-조각">코드 조각</h3>
<pre><code>const readline = require(&#39;readline&#39;);
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
});</code></pre><p>백준은 입출력 스켈레톤 코드를 제공하고 있지 않기 때문에 직접 입출력 코드를 작성해야 한다. fs를 사용하는 방법이 더 빠르다고는 하지만, 구름과 같은 타 플랫폼에서는 적용하기 어려운 방법인 것 같아 <code>readline</code> 라이브러리를 사용했다. </p>
<pre><code>// 입력을 받기 위한 array
const lines = [];

// rl.close() 가 호출되기 전까지 계속 입력을 받는다
rl.on(&#39;line&#39;, (line) =&gt; {

// 받은 입력은 공백으로 구분하여 한 줄 당 하나의 array로 저장하여 lines에 push 한다
    lines.push(line.split(&#39; &#39;))

// 입력 종료 조건 (조금 더 간단하게 입력을 받을 수 있을 것 같은데 아직 방법을 찾지 못했다) 
    if (lines.length &gt; 0 &amp;&amp; lines.length === Number(lines[0][0]) + Number(lines[0][1]) + 1){
        rl.close();
    }
}).on(&#39;close&#39;, () =&gt; {
    // solution 코드 작성 공간
    process.exit(); // 프로그램 종료
});</code></pre><p>입력을 저장하기 위한 코드이다. 문제를 풀기 쉽도록 입력을 잘 저장해 놓는 것과 제때 입력을 중지하는 것이 관건인 것 같다. 당시에는 solution 코드를 close 함수 내에 작성했는데 아예 solution 함수로 밖으로 빼서 작성하는게 깔끔하고 보기 편할 것 같다. </p>
<pre><code>// solution 코드
    const N = Number(lines[0][0]);
    const M = Number(lines[0][1]);
    const heared = [];
    const seen = new Set();

    for (let i = 0; i &lt; N; i++){
        heared.push(lines[i + 1][0]);
    }

    for (let i = 0; i &lt; M; i++){
        seen.add(lines[i + N + 1][0]);
    }

    const hearedAndSeen = heared.filter((e) =&gt; seen.has(e)).sort();
    console.log(hearedAndSeen.length);
    console.log(hearedAndSeen.join(&#39;\n&#39;));</code></pre><p>듣도 못한 사람을 array에 저장하고, 보도 못한 사람을 set에 저장했다. 그리고, 듣도 못한 사람 array를 순회하면서 보도 못한 사람 set에 해당 요소가 존재하는지 확인하고 존재하는 경우만 filtering 했다. <code>console.log()</code>는 시간이 오래 걸리기 때문에 모든 입력을 String에 모아두었다가 한 번에 출력하는게 좋다고 한다. </p>
<h3 id="전체-코드">전체 코드</h3>
<pre><code>const readline = require(&#39;readline&#39;);
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
});

const lines = [];

rl.on(&#39;line&#39;, (line) =&gt; {
    lines.push(line.split(&#39; &#39;))

    if (lines.length &gt; 0 &amp;&amp; lines.length === Number(lines[0][0]) + Number(lines[0][1]) + 1){
        rl.close();
    }
}).on(&#39;close&#39;, () =&gt; {
    const N = Number(lines[0][0]);
    const M = Number(lines[0][1]);
    const heared = [];
    const seen = new Set();

    for (let i = 0; i &lt; N; i++){
        heared.push(lines[i + 1][0]);
    }

    for (let i = 0; i &lt; M; i++){
        seen.add(lines[i + N + 1][0]);
    }

    const hearedAndSeen = heared.filter((e) =&gt; seen.has(e)).sort();
    console.log(hearedAndSeen.length);
    console.log(hearedAndSeen.join(&#39;\n&#39;));
    process.exit();
});</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[혼공컴운 12기] (아주 짧은) 활동 회고]]></title>
            <link>https://velog.io/@choi-jiae/%ED%98%BC%EA%B3%B5%EC%BB%B4%EC%9A%B4-12%EA%B8%B0-%EC%95%84%EC%A3%BC-%EC%A7%A7%EC%9D%80-%ED%99%9C%EB%8F%99-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@choi-jiae/%ED%98%BC%EA%B3%B5%EC%BB%B4%EC%9A%B4-12%EA%B8%B0-%EC%95%84%EC%A3%BC-%EC%A7%A7%EC%9D%80-%ED%99%9C%EB%8F%99-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sun, 18 Aug 2024 15:00:30 GMT</pubDate>
            <description><![CDATA[<p>작년 운영체제 수업을 들을 때 참고하려고 혼공컴운을 구매했었다. 하지만 처음 마음 가짐과 다르게 혼공컴운은 책장에 그대로 1년 내내 박혀있게 되었고, 하마터면 한 번도 책상에 펼쳐지지 못할 뻔 했다. 혼공컴운 12기를 마주치기 전까진. </p>
<p>처음엔 간식에 흥미가 끌려 시작했던 혼공컴운 12기 활동이었다. 내 공부를 하는데 간식도 받을 수 있다니, 이거 일석이조가 아닌가? 그리고 난 이미 책도 가지고 있는데? 신청하지 않을 이유가 없었다. 그렇게 CS 복습 겸 운 좋으면 간식도 받을 겸해서 가볍게 시작한 활동이었다. 6주차가 지난 지금 되돌아 보니, 가벼운 마음으로 시작한 것 치고는 상당히 많은 것들을 얻어갈 수 있었던 것 같다. 시험에 쫓겨 꾸역꾸역 밀어넣었던 지식들을 다시 한 번 꺼내어 스스로 차근차근 정리해보니 이제서야 그 지식이 정말 나의 것이 되어간다는 느낌을 받을 수 있었다. 매주 일정 분량을 공부하고 글로 다시 정리하는 일은 그리 쉽기만 한 일은 아니기에, 혼자 시작했더라면 금방 흐지부지 되었을 수도 있었을 것 같다는 생각도 든다. 혼공단에 참여하여 매주 검사를 받고 다른 참여자들의 공부 내용도 확인할 수 있어서 어렵지 않게 책 한 권을 끝까지 끝마칠 수 있었던 것 같다.  </p>
<p>1년간 잠들어 있던 책을 깨워 처음부터 끝까지 정독하고 정리하는 것에 성공하는 뿌듯한 경험을 해준 혼공단 12기가 너무 고맙다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[혼공컴운 12기] week 6 가상 메모리, 파일 시스템]]></title>
            <link>https://velog.io/@choi-jiae/%ED%98%BC%EA%B3%B5%EC%BB%B4%EC%9A%B4-12%EA%B8%B0-week-6-%EA%B0%80%EC%83%81-%EB%A9%94%EB%AA%A8%EB%A6%AC-%ED%8C%8C%EC%9D%BC-%EC%8B%9C%EC%8A%A4%ED%85%9C</link>
            <guid>https://velog.io/@choi-jiae/%ED%98%BC%EA%B3%B5%EC%BB%B4%EC%9A%B4-12%EA%B8%B0-week-6-%EA%B0%80%EC%83%81-%EB%A9%94%EB%AA%A8%EB%A6%AC-%ED%8C%8C%EC%9D%BC-%EC%8B%9C%EC%8A%A4%ED%85%9C</guid>
            <pubDate>Fri, 16 Aug 2024 15:23:24 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>✏️ 6주차 진도: Chapter 14 ~ 15</p>
</blockquote>
<ul>
<li>기본 숙제(필수): p. 400의 확인 문제 1번 풀고 인증하기</li>
<li>추가 숙제(선택): Ch.14(14-3) 프로세스가 사용할 수 있는 프레임이 3개 있고, 페이지 참조열이 &#39;2313523423&#39; 일 때 LRU 페이지 교체 알고리즘으로 이 페이지를 참조한다면 몇 번의 페이지 폴트가 발생하는지 풀어보기</li>
</ul>
<h2 id="chapter-14-가상-메모리">Chapter 14. 가상 메모리</h2>
<h3 id="연속-메모리-할당">연속 메모리 할당</h3>
<p>프로세스에 연속적인 메모리 공간을 할당하는 것. 프로세스의 크기만큼 메모리 주소를 할당받아 연속적으로 배치.</p>
<h3 id="스와핑">스와핑</h3>
<p>현재 실행되지 않는 프로세스를 임시로 보조기억장치로 쫓아내서 만들어진 빈 공간에 새로운 프로세스를 적재해서 실행하는 방식</p>
<h4 id="스왑-영역">스왑 영역</h4>
<p>메모리에 있던 프로세스들이 쫓겨나는 보조기억장치의 일부 영역</p>
<h4 id="스왑-아웃">스왑 아웃</h4>
<p>현재 실행되지 않는 프로세스가 메모리에서 스왑 영역으로 쫓겨나는 것</p>
<h4 id="스왑-인">스왑 인</h4>
<p>스왑 영역에 있던 프로세스가 다시 메모리로 옮겨 오는 것</p>
<h3 id="메모리-할당">메모리 할당</h3>
<p>메모리 공간에 프로세스를 어떻게 연속적으로 할당할 수 있을까?</p>
<h4 id="최초-적합">최초 적합</h4>
<p>메모리 내의 빈 공간을 순서대로 검색하다가 적재할 수 있는 공간을 발견하면 그 공간에 프로세스를 배치하는 방식. <strong>넣을 수 있는 공간 발견하면 무조건 배치하자!</strong></p>
<ul>
<li>검색 최소화</li>
<li>빠른 할당 가능</li>
</ul>
<h4 id="최적-적합">최적 적합</h4>
<p>빈 공간을 모두 검색해 본 후, 프로세스가 적재될 수 있는 공간 중 가장 작은 공간에 프로세스를 배치하는 방식. <strong>낭비되는 공간을 최대한 줄일 수 있게 배치하자!</strong></p>
<h4 id="최악-적합">최악 적합</h4>
<p>빈 공간을 모두 검색해 본 후, 프로세스가 적재될 수 있는 공간 중 가장 큰 공간에 프로세스를 배치하는 방식. <strong>가장 널널한 곳에 프로세스를 배치하자!</strong></p>
<h3 id="external-fragmentation외부-단편화">External Fragmentation(외부 단편화)</h3>
<p>연속 메모리 할당 환경에서는 전체 빈 공간이 충분함에도 불구하고 빈 공간들이 너무 쪼개어져 있어서 프로세스들을 적재하기 어려운 상황(external fragmentation)이 발생할 수 있다. -&gt; 메모리 낭비로 이어짐.</p>
<h4 id="external-fragmentation-해결-방법">external fragmentation 해결 방법</h4>
<ul>
<li><p>메모리 압축 : 여기 저기 흩어져 있는 빈 공간들을 하나로 모으는 방식. 빈 자리가 없어보이는 쓰레기통을 발로 밟아 공간을 만드는 것과 유사하다고 볼 수 있다.</p>
<ul>
<li>단점 : 작은 빈 공간들을 하나로 모으는 동안 시스템은 하던 일을 중지해야 한다. 또, 메모리에 있는 내용을 옮기는 작업은 많은 오버헤드를 발생시키며, 오버헤드 최소화에 대한 명확한 방법을 찾기 어렵다. 이를 보완하기 위해 나타난 것이 페이징 기법.</li>
</ul>
</li>
</ul>
<h3 id="가상-메모리">가상 메모리</h3>
<p>실행하고자 하는 프로그램을 일부만 메모리에 적재하여 실제 메모리 크기보다 더 큰 프로세스를 실행할 수 있게 하는 기술</p>
<h3 id="페이징">페이징</h3>
<p>프로세스의 <strong>논리 주소 공간</strong>을 <strong>페이지</strong>라는 일정한 단위로 자르고, 메모리 <strong>물리 주소 공간</strong>을 <strong>프레임</strong>이라는 페이지와 동일한 크기의 일정한 단위로 자른 뒤, 페이지를 프렘임에 할당하는 가상 메모리 관리 기법.</p>
<ul>
<li>페이지 단위로 스와핑이 일어남(페이지 아웃, 페이지 인)</li>
<li>프로세스 간 페이지를 공유할 수 있다.(쓰기 시 복사를 통해 자식 프로세스가 페이지를 수정할 때만 해당 페이지가 별도의 공간으로 복제된다.)</li>
</ul>
<h4 id="페이지-테이블">페이지 테이블</h4>
<p>페이지 번호와 프레임 번호를 짝지어 주는 일종의 이정표. 페이지 번호를 이용해 페이지가 적재된 프레임을 찾아서 프로세스를 순차적으로 실행할 수 있다.
CPU 내의 페이지 <strong>테이블 베이스 레지스터(PTBR)</strong>가 각 프로세스의 페이지 테이블이 적재된 주소를 가리키고 있다.</p>
<p>페이지 테이블을 메모리에 두면 메모리 접근 시간이 두 배로 늘어난다. 이 문제를 완화하기 위해 CPU 곁에 <strong>TLB</strong>라는 페이지 테이블의 캐시 메모리를 둔다. (페이지 번호가 TLB에 있을 경우 TLB 히트, 없을 경우 TLB 미스)</p>
<ul>
<li><strong>페이지 테이블 엔트리(페이지 테이블의 행)</strong>
페이지 번호, 프레임 번호, 유효 비트, 보호 비트, 참조 비트, 수정 비트가 있다.<blockquote>
<p><strong>유효 비트</strong> : 현재 해당 페이지에 접근 가능한지(페이지가 메모리에 적재되어 있는지)</p>
</blockquote>
</li>
<li><em>보호 비트*</em> : 페이지의 읽기, 쓰기, 실행 가능 여부를 나타냄(페이지 접근 권한 제한)</li>
<li><em>참조 비트*</em> : CPU가 이 페이지에 접근한 적잉 있는지 나타냄</li>
<li><em>수정 비트*</em> : 해당 페이지에 데이터를 쓴 적이 있는지 여부를 나타냄(페이지 아웃이 될 때 보조기억장치에 쓰기 작업을 해야하는지 알려준다)</li>
</ul>
<h4 id="internal-fragmentation-내부-단편화">Internal Fragmentation (내부 단편화)</h4>
<p>프로세스가 페이지 단위로 잘릴 때, 딱 맞게 잘리지 않고 애매하게 남은 부분에 의해 일어나는 메모리 낭비. 하나의 페이지 크기가 작다면 internal fragmentation의 크기는 작아지겠지만, 페이지 크기가 너무 작아진다면 페이지 테이블의 크기가 너무 커질 수도 있다. 따라서 페이지의 크기를 잘 조정하는 것이 중요하다!</p>
<h4 id="페이징에서의-주소-변환">페이징에서의 주소 변환</h4>
<p>페이징 시스템에서의 논리 주소는 page number(접근하고자 하는 페이지 번호)와 offset(접근하고자 하는 프레임의 시작 번지로부터 얼만큼 떨어져 있는가)으로 이루어져 있다.</p>
<h4 id="계층적-페이징">계층적 페이징</h4>
<p>페이지 테이블 자체를 페이징 하여 여러 단계의 페이지를 두는 방식</p>
<ul>
<li>모든 페이지 테이블을 항상 메모리에 유지할 필요가 없다.</li>
<li>2단계 페이징의 경우, 바깥 페이지 번호(CPU와 근접한 곳에 위치한 페이지 테이블 엔트리) | 안쪽 페이지 번호(페이지 테이블의 페이지 번호) | 변위 형식으로 이루어진 논리 주소를 사용한다.</li>
</ul>
<h3 id="요구-페이징">요구 페이징</h3>
<p>필요한 페이지만을 메모리에 적재하는 기법</p>
<h4 id="순수-요구-페이징">순수 요구 페이징</h4>
<p>아무런 페이지도 메모리에 적재하지 않은 채 실행부터 먼저 하는 방법. 첫 명령어를 실행할 때부터 페이지 폴트가 계속 발생하게 되고, 실행에 필요한 페이지가 어느 정도 적재된 이후부터는 페이지 폴트 발생 빈도가 떨어진다.</p>
<h3 id="페이지-교체-알고리즘">페이지 교체 알고리즘</h3>
<p>일반적으로 페이지 폴트 횟수가 적은 알고리즘이 좋은 페이지 교체 알고리즘이다. 페이지 폴트 횟수는 <strong>페이지 참조열(page reference string)</strong>을 통해 알 수 있다.</p>
<ul>
<li>페이지 참조열 : CPU가 참조하는 페이지들의 나열(연속해서 참조하는 경우는 제거)</li>
</ul>
<h4 id="fifo-페이지-교체-알고리즘">FIFO 페이지 교체 알고리즘</h4>
<p>메모리에 <strong>가장 먼저 들어온 페이지</strong>부터 페이지 아웃하는 알고리즘</p>
<ul>
<li>아이디어와 구현은 간단하나, 먼저 들어왔다는 이유만으로 프로그램 실행 내내 자주 참조 되는 페이지를 아웃시킬 수 있다는 단점이 있다.</li>
</ul>
<h4 id="2차-기회-페이지-교체-알고리즘">2차 기회 페이지 교체 알고리즘</h4>
<p>FIFO 알고리즘처럼 기본적으로 메모리에서 가장 오래 머물렀던 페이지를 대상으로 아웃할 페이지를 선정한다. 하지만, <strong>참조 비트</strong>가 1일 경우 바로 아웃시키지 않고 참조 비트를 0으로 바꾼 후 해당 페이지를 최근에 적재된 페이지라고 간주하는 방식으로 기회를 한 번 더 준다.</p>
<h4 id="최적-페이지-교체-알고리즘">최적 페이지 교체 알고리즘</h4>
<p>CPU에 의해 <strong>참조되는 횟수</strong>를 고려하는 페이지 교체 알고리즘. <strong>가장 오랫동안 사용하지 않을</strong> 페이지를 우선적으로 아웃시킨다.</p>
<ul>
<li>앞으로 오랫동안 사용하지 않을 페이지를 예측하는 것을 어려우므로 실제 사용하는 방법이라기 보다는 주로 다른 페이지 알고리즘의 이론상 기능을 평가하기 위해 사용한다.</li>
</ul>
<h4 id="lru-페이지-교체-알고리즘">LRU 페이지 교체 알고리즘</h4>
<p><strong>가장 오랫동안 사용하지 않은</strong> 페이지를 우선적으로 아웃 시키는 알고리즘. 최근에 사용되지 않은 페이지는 앞으로도 사용되지 않을 것이라는 아이디어를 토대로 만들어졌다.</p>
<h3 id="스래싱thrashing">스래싱(Thrashing)</h3>
<p>프로세스가 실제 실행되는 시간보다 페이징에 더 많은 시간을 소요(페이지 폴트가 자주 발생)하여 성능이 저해되는 문제</p>
<ul>
<li>근본적인 원인 : 각 프로세스가 필요로 하는 최소한의 프레임 수가 보장되지 않았기 때문!</li>
</ul>
<h3 id="프레임-할당-방식">프레임 할당 방식</h3>
<h4 id="정적-할당-방식">정적 할당 방식</h4>
<ul>
<li><p><strong>균등 할당</strong>
프로세스마다 프레임을 똑같이 나누어 할당하는 방식</p>
</li>
<li><p><strong>비례 할당</strong>
프로세스의 크기에 비례하여 프레임을 할당하는 방식</p>
</li>
</ul>
<h4 id="동적-할당-방식">동적 할당 방식</h4>
<p>프로세스를 실행하는 과정에서 배분할 프레임을 결정</p>
<ul>
<li><p><strong>작업 집합 모델을 사용하는 방식</strong>
프로세스가 일정 기간 동안 참조한 페이지 집합(작업 집합)을 기억하고, 해당 페이지 개수만큼 프레임을 할당해서 빈번한 페이지 교체를 방지한다.</p>
</li>
<li><p><strong>페이지 폴트 빈도를 사용하는 방식</strong>
페이지 폴트율이 너무 높으면 프레임을 더 할당해주고, 페이지 폴트율이 너무 낮으면 프레임을 다시 회수한다.</p>
</li>
</ul>
<h2 id="chapter-15-파일-시스템">Chapter 15. 파일 시스템</h2>
<h3 id="파일">파일</h3>
<p>보조기억장치에 저장된 관련 정보의 집합
이름, 파일을 실행하기 위한 정보, 파일 관련 부가 정보(속성, 메타데이터)로 구성되어 있다.</p>
<h3 id="디렉터리폴더">디렉터리(폴더)</h3>
<h4 id="1단계-디렉터리">1단계 디렉터리</h4>
<p>모든 파일이 하나의 디렉터리 아래에 있는 구조</p>
<h4 id="트리-구조-디렉터리">트리 구조 디렉터리</h4>
<p>최상위 디렉터리(루트)가 있고 그 아래 여러 서브 디렉터리가 있는 구조</p>
<h4 id="절대-경로">절대 경로</h4>
<p>루트 디렉터리에서 자기 자신까지 이르는 고유한 경로</p>
<h4 id="상대-경로">상대 경로</h4>
<p>현재 디렉터리부터 시작하는 경로</p>
<h3 id="파티셔닝">파티셔닝</h3>
<p>용량이 큰 저장 장치를 하나 이상의 논리적인 영역으로 나누는 작업</p>
<h3 id="포매팅">포매팅</h3>
<p>파일 시스템을 설정하여 어떤 방식으로 파일을 저장하고 관리할 것인지 결정하고, 새로운 데이터를 쓸 준비를 하는 작업</p>
<h3 id="파일-할당-방법">파일 할당 방법</h3>
<p>운영체제는 파일과 디렉터리를 블록 단위(섹터의 묶음 단위)로 읽고 쓴다.</p>
<h4 id="연속-할당">연속 할당</h4>
<p>보조기억장치 내 연속적인 블록에 파일을 할당하는 방식
연속 할당을 사용하는 파일 시스템에서는 디렉터리 엔트리에 파일 이름, 첫 번째 블록 주소, 길이를 저장한다.</p>
<ul>
<li>external fragmentation을 일으킬 수도 있다.</li>
</ul>
<h4 id="연결-할당">연결 할당</h4>
<p>각 블록 일부에 다음 블록의 주소를 저장하여 각 블록이 다음 블록을 가리키는 형태로 할당하는 방식 (연결 리스트로 관리)
연결 할당을 사용하는 파일 시스템에서는 디렉터리 엔트리에 파일 이름, 첫 번째 블록 주소, 길이를 저장한다.</p>
<ul>
<li>반드시 첫 번째 블록부터 하나씩 차례대로 읽어야 한다.(임의 접근 속도가 매우 느려짐)</li>
<li>하드웨어 고장이나 오류 발생 시 해당 블록 이후 블록은 접근할 수 없게 됨.</li>
</ul>
<h4 id="색인-할당">색인 할당</h4>
<p>파일의 모든 블록 주소를 색인 블록이라는 하나의 블록에 모아 관리하는 방식
색인 할당을 사용하는 파일 시스템에서는 디렉터리 엔트리에 파일 이름, 색인 블록 주소를 저장한다.</p>
<ul>
<li>파일 내 임의의 위치에 접근하기 쉽다.</li>
</ul>
<h3 id="파일-시스템">파일 시스템</h3>
<h4 id="fat-파일-시스템">FAT 파일 시스템</h4>
<p>각 블록이 가리키고 있는 다음 블록의 주소를 한데 모아 테이블 형태(파일 할당 테이블, File Allocation Table)로 관리하는 파일 시스템
디렉터리 엔트리에 파일 이름과 첫 번째 블록 주소를 저장한다.</p>
<ul>
<li>USB 메모리, SD 카드와 같은 저용량 저장 장치용 파일 시스템으로 많이 이용되고 있음</li>
</ul>
<h4 id="유닉스-파일-시스템">유닉스 파일 시스템</h4>
<p>i-node라는 색인 블록을 사용한다. i-node에는 파일 속성 정보와 열다섯 개의 블록 주소를 저장할 수 있다. 블록 주소 열두 개에는 직접 블록 주소를 저장하고, 이것으로 충분하지 않다면 열세 번째 주소에 단일 간접 블록 주소(파일 데이터를 저장한 블록 주소가 저장된 블록)를 저장한다. 단일 갑접 블록 주소로도 충분하지 않다면 열네 번째 주소에 이중 간접 블록 주소를 저장한다. 만약에 이것도 충분하지 않다면....! 열다섯 번째 주소에 삼중 간접 블록 주소를 지정하면 된다. </p>
<h2 id="✏️-기본-숙제">✏️ 기본 숙제</h2>
<h3 id="p-400-확인-문제-1번">p. 400 확인 문제 1번</h3>
<blockquote>
<p>메모리 할당 방식에 대한 설명으로 올바른 것을 다음 보기에서 찾아 써 보세요</p>
<blockquote>
<p><strong>최초 적합</strong> : 최초로 발견한 적재 가능한 빈 공간에 프로세스를 배치하는 방식
<strong>최악 적합</strong> : 프로세스가 적재될 수 있는 가장 큰 공간에 프로세스를 배피하는 방식
<strong>최적 적합</strong> : 프로세스가 적재될 수 있는 가장 작은 공간에 프로세스를 배치하는 방식</p>
</blockquote>
</blockquote>
<h2 id="📖추가-숙제">📖추가 숙제</h2>
<h3 id="ch1414-3-프로세스가-사용할-수-있는-프레임이-3개-있고-페이지-참조열이-2313523423-일-때-lru-페이지-교체-알고리즘으로-이-페이지를-참조한다면-몇-번의-페이지-폴트가-발생하는지-풀어보기">Ch.14(14-3) 프로세스가 사용할 수 있는 프레임이 3개 있고, 페이지 참조열이 &#39;2313523423&#39; 일 때 LRU 페이지 교체 알고리즘으로 이 페이지를 참조한다면 몇 번의 페이지 폴트가 발생하는지 풀어보기</h3>
<p><img src="https://velog.velcdn.com/images/choi-jiae/post/2a3e6a3d-bfe1-4208-b6d1-8048c28a225b/image.png" alt=""></p>
<p>답: 6번(맨처음에 메모리에 페이지 들여올 때 3번 추가)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[혼공컴운 12기] week 5 프로세스 동기화, 교착 상태]]></title>
            <link>https://velog.io/@choi-jiae/%ED%98%BC%EA%B3%B5%EC%BB%B4%EC%9A%B4-12%EA%B8%B0-week-5-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EB%8F%99%EA%B8%B0%ED%99%94-%EA%B5%90%EC%B0%A9-%EC%83%81%ED%83%9C</link>
            <guid>https://velog.io/@choi-jiae/%ED%98%BC%EA%B3%B5%EC%BB%B4%EC%9A%B4-12%EA%B8%B0-week-5-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EB%8F%99%EA%B8%B0%ED%99%94-%EA%B5%90%EC%B0%A9-%EC%83%81%ED%83%9C</guid>
            <pubDate>Sat, 10 Aug 2024 11:57:48 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>✏️ 5주차 진도: Chapter 12 ~ 13</p>
</blockquote>
<ul>
<li>기본 숙제(필수): p. 363의 확인 문제 1번 풀고 인증하기</li>
<li>추가 숙제(선택): Ch.12(12-1) 임계 구역, 상호 배제 개념을 정리하기</li>
</ul>
<h2 id="chapter-12-프로세스-동기화">Chapter 12 프로세스 동기화</h2>
<blockquote>
<p>동시다발적으로 실행되는 프로세스를 아무 기준 없이 마음대로 실행되도록 하면, 프로세스들이 접근하는 자원과 프로그램의 결과에 문제가 발생할 수 있다. 이러한 위험을 막기 위해서 프로세스들 사이의 수행 시기를 맞추는 <strong>동기화</strong>가 필요하다.</p>
</blockquote>
<h3 id="동기화">동기화</h3>
<ul>
<li><p>실행 순서 제어를 위한 동기화 : 프로세스를 올바른 순서대로 실행하기</p>
</li>
<li><p>상호 배제를 위한 동기화 : 동시에 접근해서는 안되는 자원에 한 번의 하나의 프로세스만 접근하도록 하기</p>
</li>
</ul>
<h4 id="공유-자원">공유 자원</h4>
<p>여러 프로세스가 함께 사용하는 공동의 자원. 전역 변수, 파일, 입출력장치, 보조기억장치 등</p>
<h4 id="임계-구역critical-section">임계 구역(critical section)</h4>
<p>동시에 실행하면 문제가 발생하는 자원에 접근하는 코드 영역
두 개 이상의 프로세스가 임계 구역에 진입하려고 하면 한 프로세스를 제외한 나머지 프로세스들은 대기해야 한다.</p>
<h4 id="race-condition">race condition</h4>
<p>두 개 이상의 프로세스가 동시에 임계 구역의 코드를 실행하여 발생하는 문제. 해당 문제가 발생하면 데이터의 일관성이 깨진다. </p>
<ul>
<li>저급 언어로 변환된 고급 언어를 실행하는 과정에서 문맥 교환이 일어날 수 있고, 이로 인해 race condition이 발생할 수 있다. </li>
</ul>
<h4 id="상호-배제를-위한-동기화를-위해-꼭-지켜져야-하는-것">상호 배제를 위한 동기화를 위해 꼭 지켜져야 하는 것</h4>
<ul>
<li>상호 배제(mutual exclusion) : 한 프로세스가 임계 구역에 진입했다면 다른 프로세스는 임계 구역에 들어올 수 없다.</li>
<li>진행(progress) : 임계 구역에 어떤 프로세스도 진입하지 않았다면 임계 구역에 진입하고자 하는 프로세스는 임계 구역에 들어갈 수 있어야 한다.</li>
<li>유한 대기(bounded waiting) : 한 프로세스가 임계 구역에 진입하고 싶다면 그 프로세스는 언제가는 임계 구역에 들어올 수 있어야 한다.(무한정 대기 X)</li>
</ul>
<h3 id="mutex-lockmutual-exclusion-lock">Mutex lock(MUTual Exclusion lock)</h3>
<p>상호 배제를 위한 동기화 도구
임계 구역에 진입하는 프로세스는 Mutex lock을 걸어 놓음으로써 현재 자신이 임계 구역에 있음을 알린다.</p>
<h4 id="acquire-함수">acquire 함수</h4>
<p>프로세스가 임계 구역에 진입하기 전에 호출하는 함수.</p>
<ul>
<li>임계 구역이 잠겨있으면(lock이 true) 임계 구역이 열릴 때(lock이 false)까지 <strong>임계 구역을 반복적으로 확인</strong></li>
<li>임계 구역이 열려 있으면 <strong>임계 구역을 잠금</strong></li>
</ul>
<h4 id="release-함수">release 함수</h4>
<p>임계 구역에서의 작업이 끝나고 호출하는 함수
<strong>잠긴 임계 구역을 열어</strong>준다.</p>
<pre><code>acquire();
// 임계 구역
release();</code></pre><ul>
<li>acquire 함수와 release 함수를 위와 같이 사용함으로써 하나의 프로세스만 임계 구역에 진입하게 할 수 있다.</li>
<li><strong>바쁜 대기</strong></li>
</ul>
<h3 id="semaphore">semaphore</h3>
<p>공유 자원이 여러 개 있는 상황에서도 적용이 가능한 동기화 도구
프로세스는 임계 구역 앞에서 멈춤 신호를 받으면 잠시 기다리고, 가도 좋다는 신호를 받으면 임계 구역에 들어가게 된다.</p>
<pre><code>// 전역 변수 S : 사용 가능한 공유 자원의 개수(임계 구역에 들어갈 수 있는 프로세스의 개수)
// wait 함수 : 기다려야 하는지 알려주는 함수
// signal 함수 : 기다리는 프로세스에게 이제 임계 구역에 들어가도 좋다고 신호를 주는 함수

wait () {
    while ( S &lt;= 0)  // 임계 구역에 들어갈 수 있는 프로세스가 없다면
    ; // 사용할 수 있는 자원이 있는지 계속 확인
    S --; // 들어갈 수 있는 프로세스가 있다면 진입시키고 S를 1 감소시킨다
 }

 signal () {
     S++; // 임계 구역에서의 작업을 마친 뒤 S 1 증가
  }

  wait()
  // 임계 구역
  signal()</code></pre><ul>
<li>실제로 세마포는 wait, signal 함수에서 대기 큐, 준비 큐를 사용한다. (사용할 수 있는 자원이 없을 경우 프로세스를 대기 큐에 삽입, signal 함수가 호출되면 대기 큐에 있는 프로세스를 준비 큐로 옮긴다.) -&gt; 바쁜 대기를 하지 않아도 됨!</li>
</ul>
<h4 id="semaphore를-이용해-프로세스-실행-순서를-제어하는-방법">semaphore를 이용해 프로세스 실행 순서를 제어하는 방법</h4>
<pre><code>S = 0

p1                        p2
// 임계 구역            wait()
signal()            // 임계 구역</code></pre><ul>
<li>이렇게 하면 p1, p2 호출 순서에 상관없이 p1이 p2보다 항상 먼저 실행된다.</li>
</ul>
<h3 id="monitor">monitor</h3>
<p>공유 자원과 공유 자원에 접근하기 위한 인터페이스(통로)를 묶어서 관리한다.</p>
<ul>
<li>일일이 waith와 signal을 명시해야하는 semaphore의 번거로움을 덜어주기 위해서 만들어짐!</li>
<li>공유 자원에 접근하고자 하는 프로세스를 큐에 삽입하고, 큐에 삽입된 순서대로 하나씩 인터페이스를 통해 공유 자원을 이용하도록 한다.</li>
<li>조건 변수를 사용해서 프로세스의 실행 순서를 제어한다.(모니터에 이미 진입한 프로세스가 잠시 실행이 중단되었을 경우, 이를 조건 변수 큐에 집어넣어서 관리한다.)</li>
</ul>
<blockquote>
<p>Java에서 메서드에 synchronized 키워드를 붙임으로써 모니터를 구현할 수 있다!</p>
</blockquote>
<h2 id="chapter-13-교착-상태">Chapter 13 교착 상태</h2>
<h3 id="교착-상태">교착 상태</h3>
<p>일어나지 않을 사건을 계속 기다리며 진행이 멈춰 버리는 현상</p>
<h3 id="자원-할당-그래프">자원 할당 그래프</h3>
<p>교착 상태가 발생했을 때의 사오항을 정확히 표현할 수 있는 그래프
<img src="https://velog.velcdn.com/images/choi-jiae/post/fa09640c-5a76-46de-9977-3c8dc0589ffe/image.png" alt=""></p>
<h4 id="교착-상태의-자원-할당-그래프">교착 상태의 자원 할당 그래프</h4>
<p><img src="https://velog.velcdn.com/images/choi-jiae/post/b93bb43c-a53a-48aa-b43f-f42de185dc1e/image.png" alt=""></p>
<ul>
<li>원의 형태를 띄고 있을 경우 <strong>교착 상태</strong></li>
</ul>
<h3 id="교착-상태-발생-조건">교착 상태 발생 조건</h3>
<p>아래 조건의 모두 만족될 때 교착 상태가 발생할 가능성이 생긴다.</p>
<ul>
<li>상호 배제 : 자원을 동시에 여러 프로세스가 사용할 수 없을 때</li>
<li>점유와 대기 : 자원을 할당 받은 상태에서 다른 자원을 할당받기를 기다리는 상태</li>
<li>비선점 : 다른 프로세스의 자원을 강제로 빼앗을 수 없을 때</li>
<li>원형 대기 : 자원 할당 그래프가 원의 형태로 그려질 때</li>
</ul>
<h3 id="교착-상태-예방">교착 상태 예방</h3>
<p>상호 배제, 점유와 대기, 비선점, 원형 대기 중 한 조건만이라도 일치하지 않도록 자원을 할당하면 교착 상태를 예방할 수 있다.</p>
<ul>
<li>상호 배제 없애기 : 현실성 떨어짐</li>
<li>점유와 대기 없애기 : 프로세스에 자원을 모두 할당하거나 아예 할당하지 않는 방식으로 자원을 분배하는 것으로 구현 가능. 하지만 자원의 활용률이 떨어진다는 문제점이 있다.</li>
<li>비선점 조건 없애기 : 선점이 불가능한 자원도 있음</li>
<li>원형 대기 조건 없애기 : 모든 자원에 번호를 붙이고, 오름차순으로 자원을 할당하는 것으로 구현 가능(5번 자원을 먼저 할당 받은 후에 이어서 1번 자원을 할당 받을 수 없게 함). 하지만 모든 자원에 번호를 붙이는 것은 쉬운 일이 아니며, 번호를 잘못 붙였다가 자원의 활용률이 떨어질 수도 있다.</li>
</ul>
<h3 id="교착-상태-회피">교착 상태 회피</h3>
<p>교착 상태가 발생하지 않을 정도로만(안전 상태를 유지하도록) 자원을 할당하는 방식</p>
<ul>
<li>안전 상태 : 교착 상태가 발생하지 않고 모든 프로세스가 성공적으로 실행을 마칠 수 있는 상태. 안전 순서열이 존재하는 상태.(이 순서대로만 실행하면 교착 상태 발생 X)</li>
<li>불안전 상태 : 교착 상태가 발생할 수도 있는 상황</li>
</ul>
<h3 id="교착-상태-검출-후-회복">교착 상태 검출 후 회복</h3>
<p>교착 상태 발생을 인정하고 사후에 조치하는 방식</p>
<ul>
<li>선점을 통한 회복 : 교착 상태가 해결될 때까지 한 프로세스씩 자원을 몰아준다.</li>
<li>프로세스 강제 종료를 통한 회복 : 가장 단순하면서 확실한 방식이지만, 많은 프로세스들이 작업 내역을 잃거나 교착 상태 제거 여부를 확인하는 과정에서 오버 헤드가 발생할 수 있다.</li>
</ul>
<h3 id="교착-상태-무시">교착 상태 무시</h3>
<ul>
<li>타조 알고리즘 : 드물게 발생하는 교착 상태를 그냥 무시하는 방법...(ㅋㅋ) 최대 효율을 위해서는 이런 방법이 적합할 때도 있다!</li>
</ul>
<h2 id="✏️-기본-숙제">✏️ 기본 숙제</h2>
<h3 id="p-363의-확인-문제-1번">p. 363의 확인 문제 1번</h3>
<blockquote>
<p>뮤텍스 락과 세마포에 대한 설명으로 옳지 않은 것을 고르세요.</p>
</blockquote>
<ol>
<li>뮤텍스 락은 임계 구역을 잠근 뒤 임계 구역에 진입함으로써 상호 배제를 위한 동기화를 이룹니다.</li>
<li>세마포는 공유 자원이 여러 개 있는 상황에서도 이용할 수 있습니다.</li>
<li>세마포를 이용해 프로세스 실행 순서 제어를 위한 동기화도 이룰 수 있습니다.</li>
<li>세마포를 이용하면 반드시 바쁜 대기를 해야 합니다.<blockquote>
<blockquote>
<p>답: 4번 (대기 큐, 준비 큐를 사용하면 바쁜 대기를 하지 않아도 된다.)</p>
</blockquote>
</blockquote>
</li>
</ol>
<h2 id="📖추가-숙제">📖추가 숙제</h2>
<h3 id="ch1212-1-임계-구역-상호-배제-개념을-정리하기">Ch.12(12-1) 임계 구역, 상호 배제 개념을 정리하기</h3>
<p>위쪽에 정리</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[혼공컴운 12기] week 4 운영체제 시작하기, 프로세스와 스레드, CPU 스케줄링]]></title>
            <link>https://velog.io/@choi-jiae/%ED%98%BC%EA%B3%B5%EC%BB%B4%EC%9A%B4-12%EA%B8%B0-week-4-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%99%80-%EC%8A%A4%EB%A0%88%EB%93%9C-CPU-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%A7%81</link>
            <guid>https://velog.io/@choi-jiae/%ED%98%BC%EA%B3%B5%EC%BB%B4%EC%9A%B4-12%EA%B8%B0-week-4-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%99%80-%EC%8A%A4%EB%A0%88%EB%93%9C-CPU-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%A7%81</guid>
            <pubDate>Sun, 28 Jul 2024 09:33:09 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>✏️ 4주차 진도: Chapter 09 ~ 11</p>
</blockquote>
<ul>
<li>기본 숙제(필수): p. 304의 확인 문제 1번 풀고 인증하기</li>
<li>추가 숙제(선택): Ch.11(11-2) 준비 큐에 A,B,C,D 순으로 삽입되었다고 가정했을 때, 선입 선처리 스케줄링 알고리즘을 적용하면 어떤 프로세스 순서대로 CPU를 할당받는지 풀어보기</li>
</ul>
<h2 id="chapter-09-운영체제-시작하기">Chapter 09 운영체제 시작하기</h2>
<blockquote>
<p>하드웨어를 조작하고 관리하는 기능을 대신 해주는 운영체제를 잘 알아야 오류가 났을 때 오류 메시지를 잘 이해할 수 있고 프로그램을 효율적으로 관리할 수 있다!</p>
</blockquote>
<h3 id="운영체제operating-system">운영체제(Operating System)</h3>
<p>실행할 프로그램에 시스템 자원을 적절히 할당하고, 프로그램이 올바르게 실행되도록 돕는 프로그램
메모리는 커널 영역과 사용자 영역으로 나뉘는데, 운영체제는 항상 커널 영역에 적재되어 실행된다.(커널이 있는 영역이라 커널 영역) 사용자 영역에는 주로 응용 프로그램이 적재된다.</p>
<h3 id="커널kernel">커널(kernel)</h3>
<p>운영체제의 핵심 서비스를 담당하는 부분
어떤 커널을 사용하는지에 따라 컴퓨터 전체의 성능도 달라질 수 있다.</p>
<h3 id="사용자-인터페이스">사용자 인터페이스</h3>
<p>운영체제가 제공하는 서비스 중 커널에 포함되지 않는 서비스 중 하나
윈도우 바탕화면처럼 사용자가 컴퓨터와 상호작용할 수 있는 통로! 그 종류에는 GUI와 CLI가 있다.</p>
<h3 id="이중-모드">이중 모드</h3>
<p>응용 프로그램은 직접 자원에 접근할 수 없고, 오직 운영체제를 통해서만 접근이 가능하다. CPU는 명령어를 사용자 모드와 커널 모드로 나누어서 이러한 작업을 실행한다.(플래그 레지스터의 슈퍼바이저 플래그를 통해 이를 구분한다.)</p>
<ul>
<li><p>사용자 모드
커널 영역의 코드를 실행할 수 <strong>없는</strong> 모드. 일반적인 응용 프로그램은 기본적으로 사용자 모드로 실행된다.</p>
</li>
<li><p>커널 모드
커널 영역의 코드를 실행할 수 <strong>있는</strong> 모드. 운영체제는 커널 모드로 실행되기 때문에 자원에 접근할 수 있다.</p>
</li>
<li><p>추가) 하이퍼 바이저 모드
가상 머신 상에서 작동하는 응용 프로그램은 하이퍼바이저 모드로 전환함으로써 가상 머신에 설치된 운영체제로부터 운영체제 서비스를 받을 수 있다.</p>
</li>
</ul>
<h3 id="시스템-호출">시스템 호출</h3>
<p>사용자 모드로 실행되는 프로그램이 운영체제를 통해 자원에 접근하려면 운영체제에 요청을 보내 커널 모드로 전환되어야 한다. 이때 이 요청을 <strong>시스템 호출(system call)</strong>이라고 한다.</p>
<ul>
<li>일종의 인터럽트</li>
</ul>
<h3 id="운영체제의-핵심-서비스">운영체제의 핵심 서비스</h3>
<ul>
<li><p>프로세스 관리
프로세스(실행 중인 프로그램)들이 적절히 번갈아가면서 실행될 수 있도록 관리</p>
</li>
<li><p>자원 접근 및 할당
프로세스들에게 공정하게 CPU 할당, 새로운 프로세스에게 메모리 할당, 메모리 부족할 경우 해결, 입출력 작업 수행</p>
</li>
<li><p>파일 시스템 관리
데이터를 파일과 디렉터리로 관리</p>
</li>
</ul>
<h2 id="chapter-10-프로세스와-스레드">Chapter 10 프로세스와 스레드</h2>
<h3 id="프로세스">프로세스</h3>
<p>보조 기억 장치에 저장된 프로그램을 메모리에 적재하고 실행하는 순간 해당 프로그램은 프로세스가 된다. -&gt; 프로세스를 생성한다.</p>
<ul>
<li><p>포그라운드 프로세스
사용자가 볼 수 있는 공간에서 실행되는 프로세스</p>
</li>
<li><p>백그라운드 프로세스
사용자가 보지 못하는 공간에서 실행되는 프로세스
백그라운드 프로세스 중, 사용자와 직접 상호작용하지 않는 프로세스를 데몬 또는 서비스라고 부른다.</p>
</li>
</ul>
<h3 id="프로세스-제어-블록pcb-process-control-block">프로세스 제어 블록(PCB, Process Control Block)</h3>
<p>프로세스와 관련된 정보를 저장하는 자료 구조 
프로세스의 실행 순서를 관리하고, 프로세스에 자원을 배분하기 위해 운영체제는 PCB에 적혀 있는 정보들로 프로세스를 식별한다. PCB는 프로세스 생성 시에 커널 영역에 생성되고, 실행이 끝나면 폐기된다.</p>
<h4 id="pcb에-담겨-있는-정보들">PCB에 담겨 있는 정보들</h4>
<ul>
<li><p>프로세스 ID (PID)
프로세스를 식별하기 위해 부여하는 고유한 번호. PID는 프로그램을 실행할 때마다 달라진다.</p>
</li>
<li><p>레지스터 값
이전까지 진행했던 작업들을 그대로 이어 실행하기 위해 이전에 사용했던 레지스터 값을 저장해놓는다.</p>
</li>
<li><p>프로세스 상태
CPU 이용 중인지, 대기 중인지 등등</p>
</li>
<li><p>메모리 관리 정보
프로세스가 어느 주소에 저장되어 있는지에 대한 정보(베이스 레지스터, 한계 레지스터, 페이지 테이블 정보)</p>
</li>
<li><p>사용한 파일과 입출력장치 목록</p>
</li>
</ul>
<h3 id="문맥-교환context-switching">문맥 교환(context switching)</h3>
<p>기존 프로세스의 context를 PCB에 백업하고 새로운 프로세스를 실행하기 위해 문맥을 PCB로부터 복구해서 새로운 프로세스를 시작하는 것</p>
<h4 id="문맥context">문맥(context)</h4>
<p>프로세스 수행을 재개하기 위해 기억해야 할 정보
PCB에 잘 기록되어 있다.</p>
<h3 id="⭐프로세스의-메모리-영역">⭐프로세스의 메모리 영역</h3>
<p>하나의 프로세스는 사용자 영역에 크게 <strong>코드 영역, 데이터 영역, 힙 영역, 스택 영역</strong>으로 나뉘어 저장된다.</p>
<h4 id="정적-할당-영역">정적 할당 영역</h4>
<ul>
<li><p>코드 영역(텍스트 영역)
기계어로 이루어진 명령어가 저장. 읽기 전용.</p>
</li>
<li><p>데이터 영역
프로그램이 실행되는 동안 유지할 데이터(전역 변수) 저장.</p>
</li>
</ul>
<h4 id="동적-할당-영역">동적 할당 영역</h4>
<ul>
<li><p>힙 영역
프로그래머가 직접 할당할 수 있는 저장 공간.
메모리의 낮은 주소에서 높은 주소로 할당된다.(아래에서 위로 자란다)
힙 영역에 메모리 공간을 할당했다면 언젠가는 해당 공간을 반환해야 한다. 그러지 않으면 메모리 낭비(메모리 누수)</p>
<blockquote>
<p>C 에서 동적 배열을 만들 때, 마지막에 해제를 해야 하는 이유!</p>
</blockquote>
</li>
<li><p>스택 영역
데이터를 일시적으로 저장하는 공간 (매개 변수, 지역 변수)
메모리의 높은 주소에서 낮은 주소로 할당된다.(위에서 아래로 자라난다.)</p>
</li>
</ul>
<h3 id="프로세스-상태">프로세스 상태</h3>
<ul>
<li>생성 상태: 프로세스를 생성 중인 상태</li>
<li>준비 상태: CPU 할당을 기다리고 있는 상태</li>
<li>실행 상태: CPU를 할당받아 실행하고 있는 상태</li>
<li>대기 상태: 입출력장치의 작업을 기다리는 상태</li>
<li>종료 상태: 프로세스가 종료된 상태</li>
</ul>
<h3 id="프로세스-계층-구조">프로세스 계층 구조</h3>
<p>프로세스는 실행 도중 시스템 호출을 통해 다른 프로세스를 생성할 수 있다. 
새 프로세스를 생성한 프로세스 -&gt; 부모 프로세스
부모 프로세스에 의해 생성된 프로세스 -&gt; 자식 프로세스</p>
<p>부모 프로세스와 자식 프로세스들을 트리 구조로 나열한 것을 <strong>프로세스 계층 구조</strong>라고 한다.</p>
<h3 id="프로세스-생성-기법">프로세스 생성 기법</h3>
<h4 id="fork">fork</h4>
<p>부모 프로세스가 자신의 복사본을 자식 프로세스로 생성해내는 시스템 호출(상속)</p>
<h4 id="exec">exec</h4>
<p>복사된 자신 프로세스가 자신의 메모리 공간을 다른 프로그램으로 교체하는 시스템 호출</p>
<h3 id="스레드">스레드</h3>
<p>프로세스를 구성하는 실행의 흐름 단위
한 프로세스의 스레드들은 프로세스의 자원을 공유한다.
최근 많은 OS는 CPU에 처리할 작업을 전달할 때 스레드 단위로 전달한다.(단, 리눅스는 프로세스와 스레드를 통일하여 task라는 이름으로 사용한다.)</p>
<h4 id="단일-스레드-프로세스">단일 스레드 프로세스</h4>
<p>한 번에 하나의 명령어만 처리하는 프로세스</p>
<h4 id="멀티-프로세스">멀티 프로세스</h4>
<p>여러 프로세스를 동시에 실행하는 것
단일 스레드 프로세스를 여러 개 동시에 실행</p>
<h4 id="멀티-스레드">멀티 스레드</h4>
<p>여러 스레드로 하나의 프로세스를 동시에 실행하는 것</p>
<h2 id="chapter-11-cpu-스케줄링">Chapter 11 CPU 스케줄링</h2>
<blockquote>
<p>프로세스의 우선 순위에 따라 CPU 자원을 할당하는 방법</p>
</blockquote>
<h3 id="스케줄링-큐">스케줄링 큐</h3>
<p>자원을 사용하고 싶어하는 프로세스들을 줄 세우는 곳. 꼭 선입선출이 아닐 수도 있다!</p>
<ul>
<li>준비 큐: CPU를 이용하고 싶은 프로세스를 관리</li>
<li>대기 큐: 입출력장치를 이용하고 싶은 프로세스를 관리</li>
</ul>
<h3 id="선점형과-비선점형-스케줄링">선점형과 비선점형 스케줄링</h3>
<h4 id="선점형-스케줄링preemptive-scheduling">선점형 스케줄링(preemptive scheduling)</h4>
<p>프로세스가 이미 자원을 사용하고 있는 중이어도,그 자원을 빼앗아 다른 프로세스에게 할당할 수 있는 스케줄링
프로세스들에게 골고루 자원을 분배할 수 있지만, context switching에서 오버헤드가 발생할 수 있다.</p>
<h4 id="비선점형-스케줄링non-preemptive-scheduling">비선점형 스케줄링(non-preemptive scheduling)</h4>
<p>프로세스가 이미 자원을 사용하고 있는 중이라면, 그 자원을 중간에 빼앗아 다른 프로세스에게 할당할 수 없는 스케줄링
context switching에서 발생하는 오버헤드가 적지만, 프로세스들에게 골고루 자원을 분배하기 어렵다.</p>
<h3 id="cpu-스케줄링-알고리즘">CPU 스케줄링 알고리즘</h3>
<h4 id="fcfs-스케줄링first-come-first-served-scheduling">FCFS 스케줄링(First Come First Served Scheduling)</h4>
<p>준비 큐에 삼입된 순서대로 CPU를 할당하는 비선점형 스케줄링
<strong>convoy effect</strong>: CPU를 오래 사용하는 프로세스가 먼저 준비 큐에 도착하여, 다른 프로세스가 CPU를 사용하기 위해 너무 오래 기다려야 하는 현상</p>
<h4 id="sjf-스케줄링shortest-job-first-scheduling">SJF 스케줄링(Shortest Job First Scheduling)</h4>
<p>준비 큐에 삽입된 프로세스들 중 CPU 이용 시간의 길이가 가장 짧은 프로세스부터 CPU를 할당하는 비선점형 스케줄링</p>
<h4 id="round-robin-scheduling">Round Robin Scheduling</h4>
<p>FCFS 스케줄링 + 타임 슬라이스(각 프로세스가 PCU를 사용할 수 있는 정해진 시간)</p>
<h4 id="srt-스케줄링shortest-remaining-time-scheduling">SRT 스케줄링(Shortest Remaining Time Scheduling)</h4>
<p>SJF + Round Robin Scheduling
정해진 타임 슬라이스만큼 CPU를 사용하되, CPU를 사용할 다음 프로세스로는 남아있는 작업 시간이 가장 적은 프로세스가 선택된다.</p>
<h4 id="우선순위-스케줄링">우선순위 스케줄링</h4>
<p>프로세스들에 우선순위를 부여하고, 가장 높은 우선순위를 가진 프로세스부터 실행하는 스케줄링.</p>
<ul>
<li>starvation: 우선 순위가 낮은 프로세스의 실행이 계속 연기되는 현상</li>
<li>aging: 오랜동안 대기한 프로세스의 우선순위를 점차 높이는 방식</li>
</ul>
<h4 id="다단계-큐-스케줄링">다단계 큐 스케줄링</h4>
<p>우선순위별로 준비 큐를 여러 개 사용하는 스케줄링 방식.
우선순위가 가장 높은 큐에 있는 프로세스들을 먼저 처리한다.
큐 마다 다른 타임 슬라이스, 다른 스케줄링 알고리즘을 사용할 수 있다. </p>
<h4 id="다단계-피드백-큐-스케줄링">다단계 피드백 큐 스케줄링</h4>
<p>다단계 큐 스케줄링 + 프로세스들이 큐 사이를 이동할 수 있다. CPU를 오래 사용해야 하는 프로세스는 점차 우선순위가 낮은 큐로 이동하게 된다. 또한, 어떤 프로세스가 낮은 우선순위 큐에서 너무 오래 기다린다면 높은 우선순위 큐로 이동시킬 수도 있다.</p>
<h2 id="✏️-기본-숙제">✏️ 기본 숙제</h2>
<h3 id="p-304의-확인-문제-1번">p. 304의 확인 문제 1번</h3>
<blockquote>
<p>다음은 프로세스 상태를 보여주는 프로세스 상태 다이어그램입니다. 1부터 5까지 올바른 상태를 적어보세요.</p>
<blockquote>
<p>1: 생성 상태, 2: 준비 상태, 3: 실행 상태, 4: 종료 상태, 5: 대기 상태</p>
</blockquote>
</blockquote>
<h2 id="📖추가-숙제">📖추가 숙제</h2>
<h3 id="ch1111-2-준비-큐에-abcd-순으로-삽입되었다고-가정했을-때-선입-선처리-스케줄링-알고리즘을-적용하면-어떤-프로세스-순서대로-cpu를-할당받는지-풀어보기">Ch.11(11-2) 준비 큐에 A,B,C,D 순으로 삽입되었다고 가정했을 때, 선입 선처리 스케줄링 알고리즘을 적용하면 어떤 프로세스 순서대로 CPU를 할당받는지 풀어보기</h3>
<blockquote>
<p>선입 선처리 스케줄링은 준비 큐에 들어오는 순서대로, 앞의 프로세스가 CPU를 모두 사용할 때까지 기다린 다음에 다른 프로세스에게 CPU를 할당한다. 따라서, A,B,C,D 순으로 CPU를 할당받게 된다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[혼공컴운 12기] week 3 메모리와 캐시 메모리, 보조 기억 장치, 입출력장치]]></title>
            <link>https://velog.io/@choi-jiae/%ED%98%BC%EA%B3%B5%EC%BB%B4%EC%9A%B4-12%EA%B8%B0-week-3-%EB%A9%94%EB%AA%A8%EB%A6%AC%EC%99%80-%EC%BA%90%EC%8B%9C-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EB%B3%B4%EC%A1%B0-%EA%B8%B0%EC%96%B5-%EC%9E%A5%EC%B9%98-%EC%9E%85%EC%B6%9C%EB%A0%A5%EC%9E%A5%EC%B9%98</link>
            <guid>https://velog.io/@choi-jiae/%ED%98%BC%EA%B3%B5%EC%BB%B4%EC%9A%B4-12%EA%B8%B0-week-3-%EB%A9%94%EB%AA%A8%EB%A6%AC%EC%99%80-%EC%BA%90%EC%8B%9C-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EB%B3%B4%EC%A1%B0-%EA%B8%B0%EC%96%B5-%EC%9E%A5%EC%B9%98-%EC%9E%85%EC%B6%9C%EB%A0%A5%EC%9E%A5%EC%B9%98</guid>
            <pubDate>Sat, 20 Jul 2024 15:09:05 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>✏️ 3주차 진도 Chapter 06 ~ 08</p>
</blockquote>
<ul>
<li>기본 숙제(필수): p. 185의 확인 문제 3번, p. 205의 확인 문제 1번 풀고 인증하기</li>
<li>추가 숙제(선택): Ch.07(07-2) RAID의 정의와 종류를 간단히 정리해 보기</li>
</ul>
<h2 id="chapter-06-메모리와-캐시-메모리">Chapter 06 메모리와 캐시 메모리</h2>
<h3 id="ram">RAM</h3>
<h4 id="특징">특징</h4>
<p>휘발성 저장 장치이기 때문에 전원을 끄면 저장된 내용이 사라진다. 비휘발성 저장 장치인 보조기억장치가 있지만, CPU는 보조기억장치에 직접 접근하지 못한다. 이러한 이유로 보조기억장치에는 보관할 프로그램을 저장하고, RAM에는 실행할 프로그램을 보조기억장치로부터 복사하여 저장한다.</p>
<h4 id="용량--성능">용량 &amp; 성능</h4>
<p>RAM 용량이 크면 클 수록 실행할 프로그램을 보조기억장치로부터 한꺼번에 많이 가져올 수 있어서 보조기억장치에 접근하는 시간을 줄일 수 있다. 하지만, RAM의 용량이 무조건 크다고 해서 프로그램 실행 속도가 항상 비례하여 증가하는 것은 아니다. RAM 용량이 동시에 실행시킬 수 있는 프로그램의 크기보다 훨씬 클 경우, 프로그램 실행 속도는 그다지 빨라지지 않는다. (CPU가 동시에 처리할 수 있는 프로그램의 개수에는 한계가 있으므로 무조건 미리 많이 가져온다고 좋은 게 아니다!)</p>
<h4 id="종류">종류</h4>
<ul>
<li><p>DRAM(Dynamin RAM)
시간이 지나면 저장된 <strong>데이터가 점차 사라지는</strong> RAM
데이터의 소멸을 막기 위해 일정 주기로 데이터를 다시 저장해야 한다. 주기적으로 재활성화를 해야 한다는 단점이 있지만, 소비 전력이 비교적 낮고, 저렴하고, 집적도가 높기 때문에 대용량으로 설계하기에 좋아서 주로 사용된다. </p>
</li>
<li><p>SRAM(Static RAM)
저장된 데이터가 변하지 않는 RAM
주기적으로 재활성화 해 줄 필요가 없기 때문에 일반적으로 DRAM보다 속도가 더 빠르다. 하지만 DRAM보다 더 비싸기 때문에 보통 주 메모리로 사용하지는 않고, 속도가 중요한 캐시 메모리에서 사용된다.</p>
</li>
<li><p>SDRAM(Synchronous Dynamic RAM)
클럭에 맞춰 동작하며 클럭마다 CPU와 정보를 주고 받을 수 있는 DRAM
SDR SDRAM(Single Data Rate SDRAM): 한 클럭에 한 번씩 정보를 주고 받을 수 있다.</p>
</li>
<li><p>DDR SDRAM(Double Data Rate SDRAM)
대역폭(데이터를 주고 받는 길의 너비)을 넓혀 속도를 빠르게 만든 SDRAM. 
한 클럭 당 두 번씩 정보를 주고 받을 수 있다.
DDR 뒤에 붙은 숫자가 하나씩 늘어날 수록 대역폭은 두 배가 된다.
DDR4 SDRAM: 최근 가장 흔히 사용되는 RAM.(1 클럭 당 정보 주고 받기 16번)</p>
</li>
</ul>
<h3 id="물리-주소--논리-주소">물리 주소 &amp; 논리 주소</h3>
<blockquote>
<p>물리 주소와 논리 주소가 각각 존재해야 하는 이유는?
<strong>메모리에 저장된 정보는 매번 바뀌기 때문!!</strong>
✅ 새로운 프로그램이 언제든 적재될 수 있고, 사용하지 않는 프로그램은 언제든 사라질 수 있다</p>
</blockquote>
<h4 id="물리-주소">물리 주소</h4>
<p>메모리 하드웨어가 사용하는 주소. 정보가 실제로 저장된 하드웨어상의 주소.</p>
<h4 id="논리-주소">논리 주소</h4>
<p>CPU와 실행 중인 프로그램이 사용하는 주소. 실행 중인 프로그램 각각에게 부여된 0번지부터 시작되는 주소</p>
<h4 id="논리-주소와-물리-주소-간의-변환">논리 주소와 물리 주소 간의 변환</h4>
<p>CPU가 메모리와 상호작용하기 위해서는 논리 주소와 물리 주소 간의 변환이 필요하다.
이러한 변환은 CPU와 주소 버스 사이에 위치한 <strong>메모리 관리 장치(MMU, Memory Management Unit)</strong>라는 하드웨어에 의해 수행된다.</p>
<blockquote>
<p><strong>⭐MMU가 주소 변환을 하는 방법</strong>
CPU가 발생시킨 논리 주소 + 베이스 레지스터 = 물리 주소</p>
</blockquote>
<p>베이스 레지스터는 프로그램의 첫 물리 주소(메모리에 적재된 프로그램의 시작점)을 저장하는 것이라고 이해하면 된다.</p>
<h4 id="메모리-보호-기법">메모리 보호 기법</h4>
<p>다른 프로그램의 영역을 침범할 수 있는 명령어를 막기 위해 <strong>한계 레지스터</strong>를 사용한다. 한계 레지스터에 논리 주소의 최대 크기를 저장하고, 만약 CPU가 한계 레지스터보다 높은 논리 주소에 접근하려고 하면 인터랩트(트랩)을 발생시켜 명령어의 실행을 중단한다.</p>
<blockquote>
<p>베이스 레지스터 값 &lt;= 프로그램의 물리 주소 &lt;= 베이스 레지스터 값 + 한계 레지스터 값</p>
</blockquote>
<blockquote>
<p>논리 주소 &lt;= 한계 레지스터 값</p>
</blockquote>
<h3 id="캐시-메모리">캐시 메모리</h3>
<p>CPU와 메모리 사이에 위치하고, 레지스터보다 용량이 크고 메모리보다 빠른 SRAM 기반의 저장 장치
CPU의 연산 속도와 메모리 접근 속도의 차이를 조금이나마 줄이기 위해서 만들어졌다. 캐시 메모리(SRAM)는 메모리(DRAM) 보다 속도가 빠르므로, 사용할 데이터를 미리 캐시 메모리에 저장해 놓으면 데이터 접근 시간을 줄일 수 있다!
CPU(코어)와 가까운 순서대로, L1 캐시, L2 캐시, L3 캐시 라고 부른다. L1, L2 캐시는 코어 내무에 있고 L3 캐시는 코어 외부에 있다. </p>
<h4 id="분리형-캐시">분리형 캐시</h4>
<p>접근 속도를 더 빠르게 하기 위해서 명령어만 저장하는 L1I 캐시와 데이터만 저장하는 L1D 캐시로 나누는 경우를 말한다.</p>
<h4 id="참조-지역성-원리">참조 지역성 원리</h4>
<p>자주 사용될 것으로 예측하여 캐시 메모리에 저장한 데이터가 실제로 CPU에서 활용될 경우를 <strong>캐시 히트</strong>라고 한다.
반대로, 자주 사용될 것으로 예측하여 캐시 메모리에 저장하였지만 실제로는 다른 데이터를 사용하게 되어 메모리에서 필요한 데이터를 직접 가져오게 되는 경우를 <strong>캐시 미스</strong>라고 한다.</p>
<blockquote>
<p>캐시 적중률 = 캐시 히트 횟수 / (캐시 히트 횟수 + 캐시 미스 횟수)</p>
</blockquote>
<p>캐시 메모리는 <strong>참조 지역성의 원리(localitiy of reference)</strong>에 따라 메모리로부터 가져올 데이터를 결정한다.</p>
<blockquote>
<ol>
<li>CPU는 최근에 접근했던 메모리 공간에 다시 접근하려는 경향이 있다.(시간 지역성)</li>
</ol>
</blockquote>
<ul>
<li>프로그램에서 변수에 지정된 값은 보통 여러 번 사용하기 때문</li>
</ul>
<ol start="2">
<li>CPU는 접근한 메모리 공간 근처를 접근하려는 경향이 있다.(공간 지역성)</li>
</ol>
<ul>
<li>프로그램에서 관련있는 데이터들은 한 데 모여서 저장되기 때문</li>
</ul>
<h2 id="chapter-07-보조기억장치">Chapter 07 보조기억장치</h2>
<h3 id="하드-디스크">하드 디스크</h3>
<p>자기적인 방식으로 데이터를 저장하는 보조기억장치</p>
<blockquote>
<p><strong>platter</strong>: 하드 디스크에서 데이터가 저장되는 곳. 동그란 원판처럼 생겼다. 0과 1의 역할을 하는 수많은 N극과 S극을 저장한다. platter는 양면을 모두 사용할 수 있고, track, sector, cylinder 단위로 나눌 수 있다. 연속된 정보는 보통 한 cylinder에 저장된다.
<strong>spindle</strong>: platter를 회전시키는 구성 요소. spindle이 platter를 돌리는 속도는 <strong>RPM</strong>(분당 회전수)이라는 단위로 표현된다.
<strong>head</strong>: platter를 대상으로 데이터를 읽고 쓰는 구성 요소. 바늘같이 생겼다.
<strong>disk arm</strong>: head를 원하는 위치로 이동시킨다.</p>
</blockquote>
<blockquote>
<p><strong>하드 디스크가 저장된 데이터에 접근하는 시간</strong> = 
<strong>seek time</strong>(접근하려는 데이터가 저장된 track까지 head를 이동시키는 시간) + 
<strong>rotational latency</strong>(head가 있는 곳으로 platter를 회전시키는 시간) +
<strong>transfer time</strong>(하드 디스크와 컴퓨터 간에 데이터를 전송하는 시간) </p>
</blockquote>
<ul>
<li>헤드가 트랙별로 달려 있는 하드 디스크를 <strong>다중 헤드 디스크</strong>라고 한다. 이 경우, seek time이 0이 된다.</li>
</ul>
<h3 id="플래시-메모리">플래시 메모리</h3>
<p>전기적으로 데이터를 읽고 쓸 수 있ㄴ느 반도체 기반의 저장 장치
플래시 메모리 기반의 보조기억장치에는 USB 메모리, SD카드, SSD가 있다.</p>
<h4 id="셀">셀</h4>
<p>플래시 메모리에서 데이터를 저장하는 가장 작은 단위
셀이 모여 페이지가 되고, 페이지가 모여 블록이 되고, 블록이 모여 플레인이 되고, 플레인이 모여 다이가 된다.
하나의 셀에 몇 bit를 저장할 수 있느냐에 따라 플래시 메모리 종류가 나뉜다.</p>
<ul>
<li><p><strong>SLC(Single Level Cell)</strong>
한 셀에 1bit 저장 가능한 플래시 메모리
수명이 가장 길지만 용량 대비 가격이 높아서 데이터를 읽고 쓰는 작업이 매우 많이 반복되며 고성능의 빠른 저장 장치가 필요한 경우에 SLC를 사용한다.</p>
</li>
<li><p><strong>MLC(Multiple Level Cell)</strong>
한 셀에 2bit 저장 가능한 플래시 메모리
SLC보다 일반적으로 속도와 수명은 떨어지지만, 용량 대비 가격이 저렴하다.</p>
</li>
<li><p><strong>TLC(Triple-Level Cell)</strong>
한 셀에 3bit 저장 가능한 플래시 메모리
한 셀로 여러 개의 bit를 저장할 수록 속도와 수명은 떨어지지만, 용량 대비 가격이 저렴해진다.</p>
</li>
</ul>
<h4 id="페이지">페이지</h4>
<p>플래시 메모리에서 읽기와 쓰기는 페이지 단위로 이루어지고, 삭제는 블록 단위로 이루어진다. (삭제 단위가 더 크다!)</p>
<blockquote>
<p><strong>페이지가 가질 수 있는 상태</strong>
<strong>Free 상태</strong> : 어떠한 데이터도 저장하고 있지 않은 상태
<strong>Valid 상태</strong> : 이미 유효한 데이터를 저장하고 있는 상태
<strong>Invalid 상태</strong> : 유효하지 않은 데이터를 저장하고 있는 상태</p>
</blockquote>
<ul>
<li>플래시 메모리는 덮어쓰기가 불가능하여 Valid 상태인 페이지에는 새 데이터를 저장할 수 없다. 따라서, 기존의 데이터를 수정하고 싶을 때, 기존의 데이터가 저장되어 있는 페이지를 Invalid 상태로 만들고, 새로운 데이터를 Valid 상태로 저장하는 방식으로 데이터를 수정한다.</li>
<li>이렇게 할 경우, 사용하지 않는 쓰레기값이 존재하게 되는데 이를 정리하기 위해 <strong>garbage collection</strong> 기능을 제공한다.</li>
</ul>
<blockquote>
<p><strong>garbage collection</strong></p>
</blockquote>
<ol>
<li>Valid 페이지들만 새로운 블록으로 옮긴다.</li>
<li>기존의 블록을 삭제한다.</li>
</ol>
<h3 id="raidredundant-array-of-independent-disks">RAID(Redundant Array of Independent Disks)</h3>
<h4 id="정의">정의</h4>
<p>데이터의 안전성 또는 높은 성능을 위해 여러 개의 물리적 보조기억장치를 마치 하나의 논리적 보조기억장치처럼 사용하는 기술</p>
<blockquote>
<p>1TB짜리 장치 5개를 마치 하나의 5TB 장치처럼 사용!</p>
</blockquote>
<h4 id="종류-1">종류</h4>
<p>RAID 구성 방법을 <strong>RAID 레벨</strong>이라고 한다.
대표적인 RAID 레벨에 대해 알아 보자.</p>
<ul>
<li><p><strong>RAID 0</strong>
여러 개의 보조기억장치에 데이터를 단순히 나누어 저장하는 구성 방식
<img src="https://velog.velcdn.com/images/choi-jiae/post/95f09b90-0917-4e12-acf1-cc529244e609/image.png" alt=""></p>
</li>
<li><p><strong>RAID 1</strong>
복사본을 만드는 방식, 미러링.
원복과 복사본 두 군데에 쓰기 때문에 쓰기 속도는 RAID 0보다 느리고, 같은 하드 디스크 개수를 사용할 때 사용 가능환 용량도 절반으로 줄어든다. 하지만 복사본이 있기에 복구가 매우 간단하다.
<img src="https://velog.velcdn.com/images/choi-jiae/post/411f3d89-26d8-4776-ba15-1bde15b24fd3/image.png" alt=""></p>
</li>
<li><p><strong>RAID 4</strong>
패리티 비트를 저장하는 하드 디스크를 따로 두는 방식
RAID 1보다 적은 하드 디스크로 데이터를 안전하게 보관할 수 있지만, 패리티를 저장하는 하드 디스크에 병목 현상이 발생할 수도 있다.
<img src="https://velog.velcdn.com/images/choi-jiae/post/f3824c68-3661-456c-a4f4-5b523e6d07a7/image.png" alt=""></p>
</li>
<li><p><strong>RAID 5</strong>
패리티 비트를 분산하여 저장하는 방식
<img src="https://velog.velcdn.com/images/choi-jiae/post/5925afa3-7a48-40f0-be78-f5282378db71/image.png" alt=""></p>
</li>
<li><p><strong>RAID 6</strong>
서로 다른 두 개의 패리티를 두는 방식
RAID 5보다 안전한 구성이나, 쓰기 속도는 느리다.
<img src="https://velog.velcdn.com/images/choi-jiae/post/2f53b429-159f-4af5-818f-5735d56b860c/image.png" alt=""></p>
</li>
</ul>
<h2 id="chapter-08-입출력장치">Chapter 08 입출력장치</h2>
<h3 id="장치-컨트롤러">장치 컨트롤러</h3>
<p>입출력장치의 종류는 너무 많고, 각 입출력장치마다 정보를 주고 받는 방식은 매우 다양하다. 또, 일반적으로 CPU와 메모리의 데이터 전송률은 높지만 입출력장치의 데이터 전송률은 낮다. 예외적으로 입출력장치의 전송률이 더 높은 경우도 있기는 하지만 이 경우에도 CPU나 메모리와 전송률이 비슷하지 않기 때문에 서로간의 통신이 어렵다. 
이러한 이유로 입출력장치는 컴퓨터에 직접 연결되지 않고 <strong>장치 컨트롤러(입출력 제어기, 입출력 모듈)</strong>라는 하드웨어를 통해 연결된다.</p>
<blockquote>
<p><strong>장치 컨트롤러의 역할</strong></p>
</blockquote>
<ul>
<li>CPU와 입출력장치 간의 통신 중개 - 일종의 번역가 역할</li>
<li>오류 검출 - 통신 중개 과정에서 오류를 잡아내기도 한다</li>
<li>데이터 버퍼링 - 버퍼에 데이터를 조금씩 모았다가 한꺼번에 내보내거나, 데이터를 한 번에 많이 받아 조금씩 내보내는 방법 (일종의 데이터 댐)</li>
</ul>
<blockquote>
<p><strong>장치 컨트롤러의 내부 구조</strong></p>
</blockquote>
<ul>
<li>데이터 레지스터: CPU와 입출력장치 사이에 주고받을 데이터가 담기는 레지스터. 버퍼 역할을 한다. 주고 받는 데이터가 많은 입출력장치에서는 RAM을 사용하기도 한다.</li>
<li>상태 레지스터: 입출력장치에 대한 상태 정보를 저장</li>
<li>제어 레지스터: 입출력장치가 수행할 내용에 대한 제어 정보와 명령을 저장</li>
</ul>
<h4 id="장치-드라이버">장치 드라이버</h4>
<p>장치 컨트롤러의 동작을 감지하고 제어함으로써 장치 컨트롤러가 컴퓨터 내부와 정보를 주고 받을 수 있게 하는 프로그램. 입출력장치를 연결하기 위한 소프트웨어적인 통로. (입출력장치를 동작시키는 방법을 제공)</p>
<h3 id="장치-컨트롤러가-cpu와-정보를-주고-받는-방법">장치 컨트롤러가 CPU와 정보를 주고 받는 방법</h3>
<h4 id="프로그램-입출력">프로그램 입출력</h4>
<p>프로그램 속 명령어로 입출력장치를 제어하는 방법
CPU가 장치 컨트롤러의 레지스터 값을 읽고 쓴다.</p>
<blockquote>
<p><strong>CPU가 장치 컨트롤러의 레지스터를 읽고 쓰는 방법</strong></p>
</blockquote>
<ul>
<li><strong>메모리 맵 입출력</strong>
메모리에 접근하기 위한 주소 공간과 입출력장치에 접근하기 위한 주소 공간을 <strong>하나의 주소 공간</strong>에서 나누어 쓰는 방법(ex. 1024개의 주소를 표현할 수 있을 때, 512개는 메모리 주소를, 나머지 512개는 장치 컨트롤러의 레지스터를 표현)</li>
<li><strong>고립형 입출력</strong>
메모리를 위한 주소 공간과 입출력장치를 위한 주소 공간을 <strong>아예 분리</strong>하는 방법
제어 버스를 메모리 읽기/쓰기 선과 입출력장치 읽기/쓰기 선 두 개로 나눈다. 입출력장치에 접근할 때, 메모리에 접근하는 명령어와는 다른 입출력 명령어를 사용해야 입출력장치 읽기/쓰기 선이 활성화된다.</li>
</ul>
<h4 id="인터럽트-기반-입출력">인터럽트 기반 입출력</h4>
<p>CPU가 장치 컨트롤러에 입출력 작업을 명령하면 장치 컨트롤러는 입출력장치를 제어하면서 입출력을 수행. 그동안 CPU는 다른 일을 한다.
장치 컨트롤러가 입출력 작업을 끝내고 CPU에게 인터럽트 요청 신호를 보내면 CPU는 하던 일을 백업하고 인터럽트 서비스 루틴을 실행한다.</p>
<blockquote>
<p><strong>인터럽트가 동시에 발생한 경우에는 어떻게 하나?</strong></p>
</blockquote>
<ul>
<li>인터럽트 비트를 비활성화 한 경우
순차적으로 인터럽트를 처리</li>
<li>인터럽트 비트가 활성화 되어 있거나 NMI(Non-Maskable Interrupt)가 발생한 경우
우선순위가 높은 인터럽트부터처리
최우선은 NMI, 나머지는 PIC(Programmable Interrupt Controller)를 이용해서 우선순위 판별</li>
</ul>
<h4 id="dmadirect-memory-access-입출력">DMA(Direct Memory Access) 입출력</h4>
<p>CPU를 거치지 않고 직접 메모리에 접근할 수 있는 입출력
DMA 입출력을 하기 위해서는 시스템 버스에 연결된 DMA 컨트롤러라는 하드웨어가 필요하다.</p>
<ul>
<li><p>CPU와 장치 컨트롤러 사이에 DMA라는 매개체가 있는 것! DMA는 CPU 대신 메모리에 직접 접근하여 정보를 읽거나 쓴다. 작업이 끝나면 DMA가 CPU에게 인터럽트를 건다. </p>
</li>
<li><p>하지만 시스템 버스는 공용이기 때문에 CPU와 DMA가 동시에 시스템 버스를 사용하지 못한다는 단점이 있다.(CPU가 시스템 버스를 사용하지 않을 때 DMA가 시스템 버스를 사용 -&gt; cycle stealing)</p>
<ul>
<li>이러한 단점을 완화하기 위해 DMA 컨트롤러와 장치 컨트롤러들을 <strong>입출력 버스</strong>라는 별도의 버스의 연결할 수 있다.(DMA가 메모리에 접근할 때만 시스템 버스 사용)</li>
</ul>
</li>
</ul>
<h2 id="✏️-기본-숙제">✏️ 기본 숙제</h2>
<h3 id="p-185의-확인-문제-3번">p. 185의 확인 문제 3번</h3>
<blockquote>
<p>다음 설명을 읽고 SRAM에 대한 설명인지 DRAM에 대한 설명인지 쓰세요</p>
<blockquote>
<ul>
<li>주로 캐시 메모리로 활용됩니다. <strong>SRAM</strong></li>
</ul>
</blockquote>
</blockquote>
<ul>
<li>주로 주기억장치로 활용됩니다. <strong>DRAM</strong></li>
<li>대용량화하기 유리합니다. <strong>DRAM</strong></li>
<li>집적도가 상대적으로 낮습니다. <strong>SRAM</strong></li>
</ul>
<h3 id="p-205의-확인-문제-1번">p. 205의 확인 문제 1번</h3>
<blockquote>
<p>다음 보기에 있는 저장 장치들로 저장 장치 계층 구조 도식도를 채우세요.</p>
<blockquote>
<p>위쪽부터, 레지스터 - 캐시 메모리 - 메모리 - 보조기억장치</p>
</blockquote>
</blockquote>
<h2 id="📖추가-숙제">📖추가 숙제</h2>
<h3 id="ch0707-2-raid의-정의와-종류를-간단히-정리해-보기">Ch.07(07-2) RAID의 정의와 종류를 간단히 정리해 보기</h3>
<ul>
<li>위쪽에 정리</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[혼공컴운 12기] week2 CPU의 작동 원리, CPU 성능 향상 기법]]></title>
            <link>https://velog.io/@choi-jiae/%ED%98%BC%EA%B3%B5%EC%BB%B4%EC%9A%B4-12%EA%B8%B0-week2-CPU%EC%9D%98-%EC%9E%91%EB%8F%99-%EC%9B%90%EB%A6%AC-CPU-%EC%84%B1%EB%8A%A5-%ED%96%A5%EC%83%81-%EA%B8%B0%EB%B2%95</link>
            <guid>https://velog.io/@choi-jiae/%ED%98%BC%EA%B3%B5%EC%BB%B4%EC%9A%B4-12%EA%B8%B0-week2-CPU%EC%9D%98-%EC%9E%91%EB%8F%99-%EC%9B%90%EB%A6%AC-CPU-%EC%84%B1%EB%8A%A5-%ED%96%A5%EC%83%81-%EA%B8%B0%EB%B2%95</guid>
            <pubDate>Sun, 14 Jul 2024 14:22:50 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>✏️ 2주차 진도: Chapter 04 ~ 05</p>
</blockquote>
<ul>
<li>기본 숙제(필수): p. 125의 확인 문제 2번, p. 155의 확인 문제 4번 풀고 인증하기</li>
<li>추가 숙제(선택): Ch.05(05-1) 코어와 스레드, 멀티 코어와 멀티 스레드의 개념을 정리하기</li>
</ul>
<h2 id="chapter-04-cpu의-작동-원리">Chapter 04 CPU의 작동 원리</h2>
<h3 id="04-1-alu와-제어장치">04-1 ALU와 제어장치</h3>
<h4 id="alu">ALU</h4>
<p>CPU의 구성 요소인 ALU라는 친구에 대해서 알아보자.
ALU는 CPU 내부에서 계산을 담당한다.</p>
<ul>
<li><p>ALU가 받아들이는 정보
레지스터를 통해 <strong>피연산자</strong>를 받고, 제어장치로부터 <strong>제어 신호(수행할 연산)</strong>을 받아들인다.</p>
</li>
<li><p>ALU가 내보내는 정보
연산을 수행한 <strong>결괏값(숫자, 문자, 메모리 주소)</strong>과 <strong>플래그(연산 경과에 대한 추가적인 정보)</strong>를 내보낸다.
결괏값은 바로 메모리에 저장되지 않고 일시적으로 레지스터에 저장된다.</p>
<blockquote>
<p>CPU가 메모리에 접근하는 속도 &lt;&lt;&lt;&lt;&lt;&lt; CPU가 레지스터에 접근하는 속도이기 때문에 프로그램 실행 속도를 높이기 위해 레지스터에 저장하는 것!</p>
</blockquote>
</li>
</ul>
<h4 id="alu가-내보내는-대표적인-플래그">ALU가 내보내는 대표적인 플래그</h4>
<ul>
<li>부호 플래그 : 1일 경우 음수, 0일 경우 양수</li>
<li>제로 플래그 : 1일 경우 0, 0일 경우 0이 아님</li>
<li>캐리 플래그 : 1일 경우 올림수나 빌림수가 발생, 0일 경우 발생X</li>
<li>오버플로우 플래그 : 1일 경우 오버플로우 발생, 0일 경우 발생X</li>
<li>인터럽트 플래그 : 1일 경우 인터럽트 가능, 0일 경우 불가능</li>
<li>슈퍼바이저 플래그 : 1일 경우 커널 모드로 실행 중, 0일 경우 사용자 모드로 실행 중</li>
</ul>
<p>플래그는 플래그 레지스터에 저장된다.</p>
<h4 id="제어장치">제어장치</h4>
<p>제어장치는 제어 신호를 내보내고 명령어를 해석하는 부품이다.</p>
<ul>
<li><p>제어장치가 받아들이는 정보</p>
</li>
<li><p><em>클럭 신호(clock)*</em> : 컴퓨터의 부품이 클럭 신호에 맞춰서 작동함</p>
</li>
<li><p><em>해석해야 할 명령어*</em> : 명령어 레지스터로부터 받아들임</p>
</li>
<li><p><em>플래그*</em> : 플래그 레지스터로부터 받아들임</p>
</li>
<li><p><em>제어 신호*</em> : 시스템 버스 중 제어 버스로부터 외부에서 전달된 제어 신호를 받아들임</p>
</li>
<li><p>제어장치가 내보내는 정보
  <strong>CPU 외부에 전달하는 제어 신호</strong> </p>
<ul>
<li>메모리에 전달</li>
<li>입출력 장치에 전달</li>
</ul>
</li>
</ul>
<p>   <strong>CPU 내부에 전달하는 제어 신호</strong></p>
<pre><code>- ALU에 전달
- 레지스터에 전달</code></pre><h3 id="04-2-레지스터">04-2 레지스터</h3>
<h4 id="레지스터-종류">레지스터 종류</h4>
<ul>
<li>프로그램 카운터, PC (명령어 포인터, IP) : 메모리에서 가져올 <strong>명령어의 주소</strong>를 저장, PC가 꾸준히 증가하기 때문에 CPU가 메모리 속 프로그램을 순차적으로 읽을 수 있는 것!</li>
<li>명령어 레지스터(IR) : 해석할 <strong>명령어</strong>를 저장</li>
<li>메모리 주소 레지스터(MAR) : <strong>메모리의 주소</strong>를 저장, 주소 버스로 보낼 값을 저장</li>
<li>메모리 버퍼 레지스터(MBR) : <strong>메모리에 쓰고 싶은 값</strong>이나 <strong>메모리로부터 전달받을 값</strong>을 저장, 데이터 버스로 주고 받을 값을 저장</li>
<li>범용 레지스터 : 자유롭게 사용 가능한 레지스터</li>
<li>플래그 래지스터 : <strong>플래그</strong>를 저장</li>
<li>스택 포인터 : 스택의 top을 가리키는 레지스터</li>
<li>베이스 레지스터 :</li>
</ul>
<h4 id="스택-주소-지정-방식">스택 주소 지정 방식</h4>
<p>스택과 스택 포인터를 이용한 주소 지정 방식
스택 포인터가 top을 가리키게 하여 스택에 데이터를 저장하거나 꺼낼 때 사용
스택은 메모리 안의 스택 영역에 있음</p>
<h4 id="변위-주소-지정-방식">변위 주소 지정 방식</h4>
<p>오퍼랜드 필드의 값(변위)와 특정 레지스터의 값을 더하여 유효 주소를 얻어내는 주소 지정 방식</p>
<ul>
<li><p>변위 주소 지정 방식을 사용하는 명령어는 연산 코드와 오퍼랜드 코드 외에도 레지스터 필드가 추가로 있다.</p>
</li>
<li><p>상대 주소 지정 방식
오퍼랜드와 프로그램 카운터의 값(읽어들일 명령어의 주소)을 더하여 유효 주소를 얻음</p>
</li>
<li><p>베이스 레지스터 주소 지정 방식
오퍼랜드와 베이스 레지스터의 값(기준 주소)을 더하여 유효 주소를 얻음</p>
</li>
</ul>
<h3 id="04-3-명령어-사이클과-인터럽트">04-3 명령어 사이클과 인터럽트</h3>
<h4 id="명령어-사이클">명령어 사이클</h4>
<p>하나의 명령어를 처리하는 정형화된 흐름
인출 사이클과 실행 사이클이 반복된다.
(인터럽트 사이클과 간접 사이클이 중간에 끼어있는 경우도 있다.)</p>
<ul>
<li><p>인출 사이클
메모리에 있는 명령어를 CPU로 가지고 오는 단계</p>
</li>
<li><p>실행 사이클
CPU로 가져온 명령어를 실행하는 단계</p>
</li>
<li><p>간접 사이클
명령어를 인출하여 CPU로 가져와도 곧바로 실행할 수 없는 경우.
메모리 접근이 더 필요한 경우(ex. 간접 주소 지정 방식)</p>
</li>
</ul>
<h4 id="인터럽트">인터럽트</h4>
<p>CPU의 작업을 잠시 중단시키는 신호</p>
<ul>
<li><p>동기 인터럽트(예외, exception)
CPU에 의해 발생하는 인터럽트
CPU가 명령어들을 수행하다가 예상치 못한 상황에 마주쳤을 때 발생하는 인터럽트</p>
</li>
<li><p>비동기 인터럽트(하드웨어 인터럽트)
주로 입출력장치에 의해 발생하는 인터럽트
작업을 끝낸 입출력장치는 CPU에 인터럽트를 보낸다.</p>
</li>
</ul>
<blockquote>
<p>** 하드웨어 인터럽트 처리 순서**</p>
<ol>
<li>입출력장치가 CPU에 <strong>인터럽트 요청 신호</strong>를 보낸다.</li>
</ol>
</blockquote>
<ul>
<li>인터럽트 하기 전에  CPU에게 보내는 신호<blockquote>
<ol start="2">
<li>CPU는 실행 사이클이 끝나고 명령어를 인출하기 전 항상 인터럽트 여부를 확인한다.</li>
<li>CPU가 인터럽트 요청을 확인하면 <strong>인터럽트 플래그</strong>를 통해 현재 인터럽트를 받아들일 수 있는 상황인지 확인한다.</li>
</ol>
</blockquote>
</li>
<li>인터럽트 플래그가 활성화되어 있을 경우에 인터럽트 요청 수용</li>
<li>하지만 정전이나 하드웨어 고장으로 인한 인터럽트일 경우, 우선순위가 가장 높아서 인터럽트 플래그에 상관없이 무조건 받아들인다.<blockquote>
<ol start="4">
<li>인터럽트를 받아들일 수 있다면 CPU는 지금까지의 작업을 스택에 백업한다.</li>
<li>CPU는 <strong>인터럽트 벡터</strong>를 참조해서 <strong>인터럽트 서비스 루틴(ISR, 인터럽트 핸들러)</strong>을 실행한다.</li>
</ol>
</blockquote>
</li>
<li>인터럽트 벡터: 입출력장치들의 인터럽트 서비스 루틴을 구분하기 위한 것. 인터럽트 서비스 루틴의 시작주소를 알 수 있다. 인터럽트 벡터는 인터럽트 요청을 보낸 대상으로 부터 전달받는다.</li>
<li>인터럽트 서비스 루틴: 인터럽트를 처리하기 위한 프로그램<blockquote>
<ol start="6">
<li>인터럽트 서비스 루틴 실행이 끝나면 백업해 둔 작업을 복구해서 실행을 재개한다.</li>
</ol>
</blockquote>
</li>
</ul>
<h2 id="chapter-05-cpu-성능-향상-기법">Chapter 05 CPU 성능 향상 기법</h2>
<h3 id="05-1-빠른-cpu를-위한-설계-기법">05-1 빠른 CPU를 위한 설계 기법</h3>
<h4 id="클럭">클럭</h4>
<p>클럭 속도가 높아지면 CPU는 명령어 사이클을 더 빠르게 반복하므로, 클럭 속도가 높은 CPU는 일반적으로 성능이 좋다. 그래서 클럭 속도가 CPU 속도 단위로 간주되기도 한다.
클럭 속도는 헤르츠(Hz) 단위로 측정한다. (1초에 클럭 한 번 반복 -&gt; 1Hz)
클럭 속도는 일정하지 않다. 고성능이 필요한 순간에는 간적으로 클럭 속도를 높일 수 있는데, 너무 많이 높일 경우에는 발열 문제가 심각해진다.</p>
<h4 id="코어">코어</h4>
<p>명령어를 실행하는 부품, 작은 개념의 CPU라고 볼 수 있다
-&gt; CPU는 명령어를 실행하는 부품인 코어를 여러 개 포함하는 부품</p>
<h4 id="멀티-코어">멀티 코어</h4>
<p>코어를 여러 개 포함하고 있는 CPU
명령어를 실행하는 부품인 코어가 많을 수록 CPU의 처리 속도는 빨라짐 -&gt; 하지만, 코어가 많다고 무조건 성능이 좋아지는 것은 아님. 코어마다 처리할 명령어를 얼마나 적절하게 분배하느냐에 따라 성능이 달라지는 것!</p>
<h4 id="스레드">스레드</h4>
<ul>
<li><p>하드웨어적 스레드, 논리 프로세서 (CPU에서 사용하는 개념)
하나의 코어에서 동시에 몇 개의 명령어를 처리할 수 있는가?</p>
<blockquote>
<p>한 코어 당 2개의 명령어를 동시에 처리할 수 있고(2개의 하드웨어 스레드), 그러한 코어가 2개 있으면 <strong>2코어 4스레드 CPU</strong></p>
</blockquote>
</li>
<li><p>소프트웨어적 스레드 (프로그램에서 사용하는 개념)
하나의 프로그램에서 독립적으로 실행되는 단위
여기서 독립적이란? 동시에 실행될 수 있는 부분을 말함</p>
</li>
</ul>
<h4 id="멀티-스레드">멀티 스레드</h4>
<p>하나의 코어로 여러 명령어를 동시에 처리하는 것</p>
<ul>
<li>멀티스레드 프로세서
하나의 명령어를 처리하기 위해 꼭 필요한 레지스터를 여러 개 가지고 있으면 여러 개의 명령을 동시에 처리할 수 있다. </li>
</ul>
<h3 id="05-2-명령어-병렬-처리-기법">05-2 명령어 병렬 처리 기법</h3>
<blockquote>
<p>CPU를 효율적으로 작동시키는 방법</p>
</blockquote>
<h4 id="명령어-파이프라인">명령어 파이프라인</h4>
<p>명령어 처리 과정 중 같은 단계가 겹치지만 않는다면 CPU는 각 단계를 동시에 실행할 수 있다.(성능 증가) -&gt; 마치 공장 생산 라인 처럼!</p>
<h4 id="파이프라인-위험">파이프라인 위험</h4>
<ul>
<li><p>데이터 위험
명령어 간 데이터 의존성에 의해 발생한다. 이전 명령어를 실행한 결과를 다음 명령에서 사용할 때 발생한다.</p>
</li>
<li><p>제어 위험
분기 등으로 인한 프로그램 카운터의 갑작스러운 변화에 의해 발생한다. PC의 값이 갑자기 변한다면, 미리 처리 중이었던 명령어들은 아무 쓸모가 없어지기 때문이다. -&gt; 이 위험을 완화하기 위해 분기 예측을 한다.</p>
</li>
<li><p>구조적 위험(자원 위험)
명령어들을 겹쳐 실행하는 과정에서 서로 다른 명령어가 동시에 ALU, 레지스터 등과 같은 CPU 부품을 사용하려고 할 때 발생한다.</p>
</li>
</ul>
<h4 id="슈퍼스칼라">슈퍼스칼라</h4>
<p>CPU 내부에 여러 개의 명령어 파이프라인을 포함한 구조.
매 클럭 주기마다 동시에 여러 명령어를 인출, 해석, 실행할 수 있있는 프로세서에서 가능한 구조이다. (ex. 멀티스레드 프로세서)</p>
<h4 id="비순차적-명령어-처리oooe-out-of-order-execution">비순차적 명령어 처리(OoOE, Out-of-order execution)</h4>
<p>오늘날 CPU 성능 향상에 크게 기여한 기법이자 대부분의 CPU가 차용하는 기법
명령어들을 순차적으로 실행하지 않는 기법
데이터 의존성이 전혀 없는(순서를 바꿔 처리해도 무방한) 명령어를 먼저 실행한다.</p>
<h3 id="05-3-cisc와-risc">05-3 CISC와 RISC</h3>
<h4 id="명령어-집합-구조isa-instruction-set-architecture">명령어 집합 구조(ISA, Instruction Set Architecture)</h4>
<p>CPU가 이해할 수 있는 명령어들의 모음이자 하드웨어가 소프트웨어를 어떻게 이해할지에 대한 약속
CPU마다 ISA가 다를 수 있다. -&gt; CPU마다 이해할 수 있는 명령어가 다를 수 있다.
ISA가 다르면 제어장치가 명령어를 해석하는 방식, 사용되는 레지스터의 종류와 개수, 메모리 관리 방법 등 많은 것이 달라진다.</p>
<h4 id="cisccomplex-instruction-set-computer">CISC(Complex Instruction Set Computer)</h4>
<p>명령어의 형태와 크기가 다양한 가변 길이 명령어를 활용한다.
-&gt; 상대적으로 적은 수의 명령어로도 프로그램을 실행할 수 있다. 따라서 컴파일된 실행 파일의 크기가 작다.
-&gt; 하지만 활용하는 명령어가 매우 복잡하고 다양한 기능을 제공하기에 명령어의 크기와 실행되기까지의 시간이 일정하지 않다. 그리고 복잡한 명령어 때문에 명령어 하나를 실행하는 데에 여러 클럭 주기를 필요로 한다. =&gt; 파이프라인을 구현하는 데 어려움. 실제로는 거의 자주 쓰이는 명령어만 쓰임.</p>
<h4 id="riscreduced-instruction-set-computer">RISC(Reduced Instruction Set Computer)</h4>
<p>CISC에 비해 명령어의 종류가 적다.
고정 길이 명령어를 활용한다.
-&gt; 명령어가 규격화되어 있고, 하나의 명령어가 1클럭 내외로 실행되기 때문에 RISC 명령어 집합은 명령어 파이프라이닝에 최적화되어 있다. 또한, 메모리에 직접 접근하는 명령어를 load, store 두 개로 제한할 만큼 메모리 접근을 단순화하고 최소화를 추구한다. 대신 레지스터를 적극 활용한다.
-&gt; 하지만 사용 가능한 명령어 개수가 CISC보다 적으므로, CISC보다 많은 명령으로 프로그램을 작동시킬 수 밖에 없다.`</p>
<h2 id="✏️-기본-숙제">✏️ 기본 숙제</h2>
<h3 id="p-125의-확인-문제-2번">p. 125의 확인 문제 2번</h3>
<blockquote>
<p>설명에 맞는 레지스터를 보기에서 찾아 빈칸을 채워 보세요.</p>
<blockquote>
<p><strong>플래그 레지스터</strong> : 연산 결과 혹은 CPU 상태에 대한 부가 정보를 저장하는 레지스터
<strong>프로그램 카운터</strong> : 메모리에서 가져올 명령어의 주소를 저장하는 레지스터
<strong>범용 레지스터</strong> : 데이터와 주소를 모두 저장할 수 있는 레지스터
<strong>명령어 레지스터</strong> : 해석할 명령어를 저장하는 레지스터</p>
</blockquote>
</blockquote>
<h3 id="p-155의-확인-문제-4번">p. 155의 확인 문제 4번</h3>
<blockquote>
<p>다음 그림은 멀티코어 CPU를 간략하게 도식화 한 그림입니다. 빈칸에 알맞은 용어를 써 넣으세요. (빈 칸은 명령어를 처리하는 부분을 가리키고 있음)</p>
<blockquote>
<p>답: 코어 </p>
</blockquote>
</blockquote>
<h2 id="📖추가-숙제">📖추가 숙제</h2>
<h3 id="ch0505-1-코어와-스레드-멀티-코어와-멀티-스레드의-개념을-정리하기">Ch.05(05-1) 코어와 스레드, 멀티 코어와 멀티 스레드의 개념을 정리하기</h3>
<p>위쪽에 정리</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JavaScript] 배열, 문자열 하나하나씩 출력하기]]></title>
            <link>https://velog.io/@choi-jiae/JavaScript-%EB%B0%B0%EC%97%B4-%EB%AC%B8%EC%9E%90%EC%97%B4-%ED%95%98%EB%82%98%ED%95%98%EB%82%98%EC%94%A9-%EC%B6%9C%EB%A0%A5%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@choi-jiae/JavaScript-%EB%B0%B0%EC%97%B4-%EB%AC%B8%EC%9E%90%EC%97%B4-%ED%95%98%EB%82%98%ED%95%98%EB%82%98%EC%94%A9-%EC%B6%9C%EB%A0%A5%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 08 Jul 2024 14:36:42 GMT</pubDate>
            <description><![CDATA[<h2 id="개념">개념</h2>
<h3 id="for-반복문">for 반복문</h3>
<pre><code> str = &#39;hello&#39;;
 for (i=0; i &lt; str.length; i++){
     console.log(str[i]);
 }</code></pre><h3 id="for-of-반복문">for of 반복문</h3>
<pre><code> str = &#39;hello&#39;;
 for (let i of str){
     console.log(i);
 }</code></pre><h3 id="foreach">.forEach</h3>
<pre><code>str = &#39;hello&#39;;
[...str].forEach(c =&gt; console.log(c))</code></pre><ul>
<li>forEach는 배열 안에 들어있는 각각의 요소에 콜백함수(<code>function(요소, 인덱스, array)</code>)를 실행한다.<pre><code>str = &#39;hello&#39;;
[...str].forEach(function(c, idx, str){
  console.log(c, idx, str);
});
// h 0 [&#39;h&#39;, &#39;e&#39;, &#39;l&#39;, &#39;l&#39;, &#39;o&#39;]
// e 1 [&#39;h&#39;, &#39;e&#39;, &#39;l&#39;, &#39;l&#39;, &#39;o&#39;]
// l 2 [&#39;h&#39;, &#39;e&#39;, &#39;l&#39;, &#39;l&#39;, &#39;o&#39;]
// l 3 [&#39;h&#39;, &#39;e&#39;, &#39;l&#39;, &#39;l&#39;, &#39;o&#39;]
// l 4 [&#39;h&#39;, &#39;e&#39;, &#39;l&#39;, &#39;l&#39;, &#39;o&#39;]</code></pre><h2 id="문제-풀이">문제 풀이</h2>
<h3 id="프로그래머스-level-0---문자열-돌리기">프로그래머스 level 0 - 문자열 돌리기</h3>
<pre><code>const readline = require(&#39;readline&#39;);
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});
</code></pre></li>
</ul>
<p>let input = [];</p>
<p>rl.on(&#39;line&#39;, function (line) {
    input = [line];
}).on(&#39;close&#39;,function(){
    str = input[0];
    for (i=0; i &lt; str.length; i++){
        console.log(str[i]);
    }
});
```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[혼공컴운 12기] week 1 컴퓨터 구조, 데이터 표현, 명령어]]></title>
            <link>https://velog.io/@choi-jiae/%ED%98%BC%EA%B3%B5%EC%BB%B4%EC%9A%B4-12%EA%B8%B0-week-1-%EC%BB%B4%ED%93%A8%ED%84%B0-%EA%B5%AC%EC%A1%B0-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%91%9C%ED%98%84-%EB%AA%85%EB%A0%B9%EC%96%B4</link>
            <guid>https://velog.io/@choi-jiae/%ED%98%BC%EA%B3%B5%EC%BB%B4%EC%9A%B4-12%EA%B8%B0-week-1-%EC%BB%B4%ED%93%A8%ED%84%B0-%EA%B5%AC%EC%A1%B0-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%91%9C%ED%98%84-%EB%AA%85%EB%A0%B9%EC%96%B4</guid>
            <pubDate>Tue, 02 Jul 2024 16:09:08 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>✏️ 1주차 진도: Chapter 01 ~ 03</p>
</blockquote>
<ul>
<li>기본 숙제(필수): p. 51의 확인 문제 3번, p. 65의 확인 문제 3번 풀고 인증하기</li>
<li>추가 숙제(선택): p. 100의 스택과 큐의 개념을 정리하기</li>
</ul>
<h2 id="chapter-01-컴퓨터-구조-시작하기">Chapter 01 컴퓨터 구조 시작하기</h2>
<h3 id="01-1-컴퓨터-구조를-알아야-하는-이유">01-1 컴퓨터 구조를 알아야 하는 이유</h3>
<blockquote>
<p>실력있는 개발자가 되기 위해서는 컴퓨터 구조를 반드시 알아야 한다!</p>
</blockquote>
<h4 id="1-문제-해결">1. 문제 해결</h4>
<p>같은 코드를 작성했는데도 컴퓨터 시스템에 따라 제대로 작동하지 않는 경우가 있을 수 있음!
컴퓨터 구조를 이해하고 있다면 문제 상황을 빠르게 진단할 수 있고, 문제 해결의 실마리를 다양하게 찾을 수 있다.</p>
<h4 id="2-성능-용량-비용">2. 성능, 용량, 비용</h4>
<p>프로젝트에 알맞은 서버 컴퓨터나 클라우드 서비스를 선택하려면 컴퓨터 구조에 대해 알아야 함. 무조건 싸거나 비싼 것이 좋은 게 아님!</p>
<h3 id="01-2-컴퓨터-구조의-큰-그림">01-2 컴퓨터 구조의 큰 그림</h3>
<h4 id="컴퓨터가-이해하는-정보">컴퓨터가 이해하는 정보</h4>
<p>컴퓨터는 0과 1로 표현된 정보만을 이해 (데이터, 명령어)
<strong>데이터</strong> 
컴퓨터가 이해하는 숫자, 문자, 이미지, 동영상과 같은 정적인 정보</p>
<p><strong>명령어</strong>
데이터를 움직이고 컴퓨터를 작동시키는 정보</p>
<h4 id="컴퓨터의-네-가지-핵심-부품">컴퓨터의 네 가지 핵심 부품</h4>
<p><strong>중앙처리장치(CPU)</strong>
메모리에 저장된 명령어를 읽고, 읽은 명령어를 해석, 실행하는 부품.</p>
<ul>
<li><p>산술논리연산장치(ALU, Arithmetic Logic Unit)</p>
<ul>
<li>컴퓨터 내부에서 수행되는 대부분의 계산을 ALU에서 수행함.</li>
</ul>
</li>
<li><p>레지스터</p>
<ul>
<li>CPU 내부의 작은 임시 저장 장치.</li>
<li>프로그램을 실행하는 데 필요한 값들을 임시로 저장.</li>
<li>여러 개의 레지스터가 존재하고, 각기 다른 이름과 역할을 가지고 있음</li>
</ul>
</li>
<li><p>제어장치(CU, Control Unit)</p>
<ul>
<li>제어 신호(ex. 메모리 읽기, 메모리 쓰기)라는 전기 신호를 내보내고 명령어를 해석하는 장치.</li>
</ul>
</li>
</ul>
<p><strong>주기억장치(메모리)</strong>
현재 실행되는 프로그램의 명령어와 데이터를 저장하는 부품
저장된 값에 빠르고 효율적으로 접근하기 위해 &#39;주소&#39;를 사용함.
가격이 비싸 저장 용량이 적고, 전원이 꺼지면 저장된 내용을 잃음.</p>
<p><strong>보조기억장치</strong>
메모리보다 크기가 크고 전원이 꺼져도 저장된 내용을 잃지 않는 메모리를 보조하는 저장 장치 (하드 디스크, SSD, USB, DVD, CD-ROM)</p>
<p><strong>입출력장치</strong>
마이크, 스피커, 프린터, 마우스, 키보드처럼 컴퓨터 외부에 연결되어 컴퓨터 내부와 정보를 교환하는 장치
보조기억장치를 메모리를 보조하는 특별한 기능을 수행하는 입출력장치라고 볼 수도 있음.</p>
<p><strong>메인보드와 시스템 버스</strong>
컴퓨터의 핵심 부품들은 모두 메인보드(마더보드)라는 판에 연결됨.
메인보드에 연결된 부품들은 버스라는 통로를 통해 서로 정보를 주고 받음. 컴퓨터의 핵심 부품을 연결하는 가장 중요한 버스는 시스템 버스이고, 시스템 버스는 주소 버스, 데이터 버스, 제어 버스로 구성되어 있음.</p>
<h2 id="chapter-02-데이터">Chapter 02 데이터</h2>
<h3 id="02-1-0과-1로-숫자를-표현하는-방법">02-1 0과 1로 숫자를 표현하는 방법</h3>
<h4 id="정보-단위">정보 단위</h4>
<p><strong>비트</strong>
0과 1을 나타내는 가장 작은 정보 단위
n bit는 2^n 가지의 정보를 표현할 수 있음</p>
<blockquote>
<p>1byte == 8bit
1KB == 1000byte
1MB == 1000kB
1GB == 1000MB
1TB == 1000GB</p>
</blockquote>
<p><strong>워드</strong>
CPU가 한 번에 처리할 수 있는 데이터 크기</p>
<h4 id="이진법">이진법</h4>
<p>0과 1만으로 모든 숫자를 표현하는 방법
1을 넘어가는 시점에서 자리 올림을 하여 숫자를 표현함</p>
<blockquote>
<p><strong>이진수 표기 방법</strong>
수학적 표기 : 1000_<strong>(2)</strong>
코드 상 표기 : <strong>0b</strong>1000</p>
</blockquote>
<h4 id="이진수의-음수-표현">이진수의 음수 표현</h4>
<p>이진수로 음수를 표현하는 방법 중 가장 널리 사용되는 방법은 <strong>2의 보수(two&#39;s complement)</strong> 를 구해 이 값을 음수로 간주하는 방법.</p>
<p><strong>2의 보수</strong>
어떤 수를 그보다 큰 2^n에서 뺀 값</p>
<blockquote>
<p>0b11의 보수는
0b100에서 0b11을 뺀 값인 0b01</p>
</blockquote>
<p>좀 더 쉽게 표현하자면,
모든 0과 1을 뒤집고 거기에 1을 더한 값</p>
<blockquote>
<p>0b11</p>
</blockquote>
<pre><code>11 -&gt; 00 -&gt; 01</code></pre><p>0b01</p>
<p>실제로 이진수만 봐서는 음수인지 양수인지 구분하기 어려움.
이를 구분하기 위해 사용하는 것이 <strong>flag</strong></p>
<p><strong>two&#39;s complement의 한계</strong></p>
<ul>
<li>0에 2의 보수를 취하면 자리올림이 발생함. (0000 -&gt; 10000)<ul>
<li>이 경우에는 자리 올림이 발생한 비트의 1을 버림.</li>
</ul>
</li>
<li>2^n에 2의 보수를 취하면 자기 자신이 나옴 (1000 -&gt; 1000)<ul>
<li>n bit로는 -2^n과 2^n을 동시에 표현할 수 없음.</li>
</ul>
</li>
</ul>
<h4 id="십육진법">십육진법</h4>
<p>수가 15를 넘어가는 시점에 자리 올림을 하는 숫자 표현 방식.
10, 11, 12, 13, 14, 15 대신 A, B, C, D, E, F를 사용.</p>
<ul>
<li>이진수에 비해 더 적은 자릿수로 더 많은 정보를 표현할 수 있음.</li>
<li>십육진수는 이진수로 변환하기 쉬움.<blockquote>
<p><strong>십육진수 표기 방법</strong>
수학적 표기 : 15_<strong>(16)</strong>
코드 상 표기 : <strong>0x</strong>15</p>
</blockquote>
</li>
</ul>
<h4 id="십육진수---이진수">십육진수 &lt;-&gt; 이진수</h4>
<p>십육진수는 한 글자당 16(2^4)종류의 숫자를 표현가능하므로, 십육진수 한 자리를 이진수로 표현하려면 4bit가 필요함.
십육진수의 각 자리를 따로 따로 이진수로 변환한 후 그 비트들을 이어 붙이면 십육진수를 2진수로 바꿀 수 있음!</p>
<blockquote>
<p>0x1A2B
0b 0001 1010 0010 1011 </p>
</blockquote>
<p>이진수를 십육진수로 바꿀 때는 이진수의 숫자들을 4개씩 끊어 십육진수로 변환한 후 이어 붙이면 됨.</p>
<h3 id="02-2-0과-1로-문자를-표현하는-방법">02-2 0과 1로 문자를 표현하는 방법</h3>
<h4 id="문자-집합과-인코딩">문자 집합과 인코딩</h4>
<p><strong>문자 집합</strong>
컴퓨터가 인식하고 표현할 수 있는 문자의 모음</p>
<p><strong>문자 인코딩</strong>
컴퓨터가 이해할 수 있도록 문자를 0과 1로 변환하는 과정
같은 문자 집합에 대해서도 다양한 인코딩 방법이 있을 수 있음.</p>
<p><strong>문자 디코딩</strong>
인코딩의 반대 과정.
0과 1로 이루어진 문자 코드를 사람이 이해할 수 있는 문자로 변환하는 과정.</p>
<h4 id="아스키-코드">아스키 코드</h4>
<p>초창기 문자 집합 중 하나로, 영어 알파벳, 아라비아 숫자, 일부 특수 문자, 제어 문자(space, Backspace, Escape 등)를 포함함.
각 문자들은 7bit로 표현됨.</p>
<ul>
<li>실제로는 8bit로 표현되지만, 8bit 중 1bit는 오류 검출을 위해 사용되는 비트(패리티 비트)</li>
</ul>
<h4 id="euc-kr">EUC-KR</h4>
<p>대표적인 한글 완성형 인코딩 방식
한글 한 글자에 2byte 코드가 부여됨.
집합에 정의되지 않은 한글은 표현 불가.</p>
<blockquote>
<p>완성형 인코딩 : 초성, 중성, 종성의 좋바으로 이루어진 완성된 하나의 글자에 고유한 코드를 부여하는 인코딩 방식
조합형 인코딩 : 초성을 위한 비트열, 중성을 위한 비트열, 종성을 위한 비트열을 할당하여 그것들의 조합으로 하나의 글자 코드를 완성하는 인코딩 방식</p>
</blockquote>
<h4 id="유니코드와-utf-8">유니코드와 UTF-8</h4>
<p><strong>유니코드</strong>
현대 문자를 표현할 때 가장 많이 사용되는 표준 문자 집합.
여러 나라의 문자를 광범위하게 표현할 수 있음.</p>
<p><strong>UTF-8</strong>
유니코드 문자에 부여된 값을 인코딩하는 방법 중 하나.</p>
<h2 id="chapter-03-명령어">Chapter 03 명령어</h2>
<h3 id="03-1-소스-코드와-명령어">03-1 소스 코드와 명령어</h3>
<h4 id="고급-언어와-저급-언어">고급 언어와 저급 언어</h4>
<p><strong>고급 언어</strong>
우리가 프로그램을 만들 때 사용하는 프로그래밍 언어
사람을 위한 언어</p>
<p><strong>저급 언어</strong>
컴퓨터가 직접 이해하고 실행할 수 있는 언어
명령어로 이루어져 있음.</p>
<ul>
<li><p>기계어</p>
<ul>
<li>0과 1의 명령어 비트로 이루어진 언어.</li>
</ul>
</li>
<li><p>어셈블리어</p>
<ul>
<li>0과 1로 표현된 명령어를 사람이 읽기 편한 형태로 번역한 언어</li>
<li>하드웨어와 밀접하게 맞닿아 있는 프로그램을 개발하는 임베디드 개발자, 게임 개발자, 정보 보안 분야 등의 개발자는 어셈블리어를 많이 이용.</li>
</ul>
</li>
</ul>
<h4 id="컴파일-언어와-인터프리터-언어">컴파일 언어와 인터프리터 언어</h4>
<p>고급 언어가 저급 언어로 변환되는 방식에는 <strong>컴파일</strong> 방식과 <strong>인터프리트</strong> 방식이 있음.</p>
<p><strong>컴파일 언어</strong>
컴파일 방식으로 작동하는 프로그래밍 언어
컴파일러에 의해 <strong>소스 코드 전체</strong>가 저급 언어로 변환되어 실행됨.
대표적인 컴파일 언어로는 C가 있음.
컴파일러가 소스 코드 내에서 오류를 하나라도 발견하면 해다 ㅇ소스 코드는 컴파일에 실패함.
컴파일러를 통해 저급 언어로 변환된 코드를 <strong>object code</strong>라고 함.</p>
<p><strong>인터프리터 언어</strong>
인터프리트 방식으로 작동하는 프로그래밍 언어
인터프리터에 의해 <strong>소스 코드가 한 줄씩 실행</strong>되는 고급 언어.
대표적인 인터프리터 언어로 Python이 있음.
인터프리터 언어는 컴퓨터와 대화하듯 소스 코드를 한 줄씩 실행하기 때문에 소스 코드 전체를 저급 언어로 변환하는 시간을 기다릴 필요가 없음.
소스 코드 N번째 줄에 문법 오류가 있어도 N-1번째 줄까지는 올바르게 수행됨.</p>
<p>일반적으로 인터프리터 언어는 컴파일 언어보다 느림.
-&gt; 한 번 컴파일을 실행해서 나온 object code는 바로 컴퓨터가 이해하고 실행할 수 있는데, 인터프리터 언어는 소스 코드를 한 줄 한 줄 해석하면서 실행해야 하기 때문.</p>
<h4 id="linking">Linking</h4>
<p><strong>목적 파일</strong>
object code로 이루어진 파일</p>
<p><strong>실행 파일</strong>
실행 코드로 이루어진 파일
.exe 확장자를 가진 파일이 대표적인 실행 파일</p>
<p>목적 파일이 실행 파일이 되기 위해서는 linking이라는 작업을 거쳐야 함.
-&gt; 목적 파일에 없는 외부 기능을 목적 파일과 연결 짓는 작업이 필요</p>
<h3 id="03-2-명령어의-구조">03-2 명령어의 구조</h3>
<h4 id="연산-코드와-오퍼랜드">연산 코드와 오퍼랜드</h4>
<p>명령어는 연산 코드와 오퍼랜드로 구성되어 있음.</p>
<p><strong>연산 코드(operation code)</strong></p>
<ul>
<li>명령어가 수행할 연산</li>
<li>연산자<blockquote>
<p><strong>기본적인 연산 코드 유형</strong></p>
<ol>
<li>데이터 전송</li>
<li>산술/논리 연산</li>
<li>제어 흐름 변경</li>
<li>입출력 제어</li>
</ol>
</blockquote>
</li>
</ul>
<p><strong>오퍼랜드(operand)</strong></p>
<ul>
<li>연산에 사용할 데이터(숫자나 문자 등을 나타내는 데이터) or 연산에 사용할 데이터가 저장된 위치(메모리나 레지스터 주소)</li>
<li>피연산자</li>
<li>오퍼랜드 필드를 주소 필드라 하기도 함.</li>
<li>오퍼랜드가 하나도 없는 명령어를 <strong>0-주소 명령어</strong> , 오퍼랜드가 1개인 명령어를 <strong>1-주소 명령어</strong> , 2개인 명령어를 <strong>2-주소 명령어</strong> , 3개인 명령어를 <strong>3-주소 명령어</strong>라고 함. </li>
</ul>
<blockquote>
<p><strong>명령어의 구조</strong>
operation code | operand</p>
</blockquote>
<h4 id="주소-지정-방식">주소 지정 방식</h4>
<p><strong>유효 주소(effective address)</strong>
연산 코드에 사용할 데이터가 저장된 위치</p>
<p><strong>주소 지정 방식</strong>
연산에 사용할 데이터 위치를 찾는 방법
유효 주소를 찾는 방법</p>
<ol>
<li>즉시 주소 지정 방식 (immediate addressing mode)</li>
</ol>
<ul>
<li>연산에 사용할 데이터를 오퍼랜드 필드에 직접 명시하는 방식</li>
<li>표현할 수 있는 데이터의 크기가 작아지는 단점이 있지만, 연산에 사용할 데이터를 메모리나 레지스터로부터 찾는 과정이 없기 때문에 속도가 빠름.</li>
</ul>
<ol start="2">
<li>직접 주소 지정 방식 (direct addressing mode)</li>
</ol>
<ul>
<li>오펴랜드 필드에 유효 주소를 직접적으로 명시하는 방식</li>
<li>표현할 수 있는 유효 주소에 제한이 생길 수 있음.</li>
</ul>
<ol start="3">
<li>간접 주소 지정 방식 (indirect addressing mode)</li>
</ol>
<ul>
<li>유효 주소의 주소를 오퍼랜드 필드에 명시하는 방식.</li>
<li>직접 주소 지정 방식보다 표현할 수 있는 유효 주소의 범위가 넓어짐.</li>
<li>두 번의 메모리 접근이 필요해서 느림.(유효 주소의 주소에 접근, 유효 주소에 접근)</li>
</ul>
<ol start="4">
<li>레지스터 주소 지정 방식 (register addressing mode)</li>
</ol>
<ul>
<li>연산에 사용할 데이터를 저장한 레지스터를 오퍼랜드 필드에 직접 명시 하는 방법</li>
<li>일반적으로 CPU 외부에 있는 메모리에 접근하는 것보다 CPU 내부에 있는 레지스터에 접근하는 것이 더 빠르므로, 직접 주소 지정 방식보다 빠르게 데이터데 접근 가능.</li>
<li>표현할 수 있는 레지스터 크기에 제한이 생길 수 있음.</li>
</ul>
<ol start="5">
<li>레지스터 간접 주소 지정 방식 (register indirect addressing mode)</li>
</ol>
<ul>
<li>연산에 사용할 데이터를 메모리에 저장하고, 유효 주소를 저장한 레지스터를 오퍼랜드 필드에 명시하는 방법</li>
<li>메모리에 접근하는 횟수는 1번</li>
</ul>
<h2 id="✏️-기본-숙제">✏️ 기본 숙제</h2>
<h3 id="p-51-확인-문제-3번">p. 51 확인 문제 3번</h3>
<blockquote>
<p>다음 설명의 빈칸에 들어갈 알맞은 내용을 써 보세요.</p>
<blockquote>
<p>프로그램이 실행되려면 반드시 ( )에 저장되어 있어야 합니다.</p>
</blockquote>
</blockquote>
<p>답: 메모리</p>
<h3 id="p-65-확인-문제-3번">p. 65 확인 문제 3번</h3>
<blockquote>
<p>1101_(2)의 음수를 2의 보수 표현법으로 구해보세요</p>
<blockquote>
<p>1101 -&gt; 0010 -&gt; 0011</p>
</blockquote>
</blockquote>
<p>답: 0011</p>
<h2 id="📖추가-숙제">📖추가 숙제</h2>
<h3 id="p-100-스택과-큐의-개념을-정리-하기">p. 100 스택과 큐의 개념을 정리 하기</h3>
<h4 id="스택">스택</h4>
<p>가장 나중에 저장한 데이터를 가장 먼저 빼내는 데이터 관리 방식 (LIFO)
데이터를 차곡 차곡 쌓는 형태로 저장하는 방식</p>
<ul>
<li>push : 스택에 새로운 데이터를 저장하는 명령어</li>
<li>pop : 스택에 저장된 데이터를 꺼내는 명령어</li>
</ul>
<h4 id="큐">큐</h4>
<p>가장 먼저 저장한 데이터를 가장 먼저 빼내는 데이터 관리 방식 (FIFO)
자동차 터널처럼 데이터를 저장하는 방식</p>
<p><strong>참고 자료</strong>
혼자 공부하는 컴퓨터구조 + 운영체제 (강민철, 한빛미디어)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JavaScript] 문자열 출력하기]]></title>
            <link>https://velog.io/@choi-jiae/JavaScript-%EB%AC%B8%EC%9E%90%EC%97%B4-%EC%B6%9C%EB%A0%A5%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@choi-jiae/JavaScript-%EB%AC%B8%EC%9E%90%EC%97%B4-%EC%B6%9C%EB%A0%A5%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 01 Jul 2024 10:05:30 GMT</pubDate>
            <description><![CDATA[<h2 id="개념">개념</h2>
<h3 id="입출력">입출력</h3>
<ul>
<li>프로그래머스에서 문자열 출력 문제를 풀 때 기본적으로 제공하는 코드를 분석해보자.<pre><code>// CommonJS 방식으로 readline 모듈을 불러옴
// readline은 데이터를 한 번에 한 줄씩 읽을 수 있음.
const readline = require(&#39;readline&#39;);
</code></pre></li>
</ul>
<p>// interface 객체 만들기
// console에서 표준 입출력 처리를 할 수 있음.
const rl = readline.createInterface({
    input: process.stdin, // standard input
    output: process.stdout // standard output
});</p>
<p>// input 변수 초기화
let input = [];</p>
<p>rl.on(&#39;line&#39;, function (line) {
    // 입력받은 값을 처리하는 코드
    input = [line];
}).on(&#39;close&#39;,function(){
    // 입력이 끝나고 실행되는 코드
    // 이 부분에 console.log를 찍어주면 출력이 된다.
    str = input[0];
});</p>
<pre><code>
### 문자열 반복 출력</code></pre><p>문자열.repeat(반복 횟수)</p>
<pre><code>- python의 ```문자열*숫자``` 와 같은 결과를 보인다.

### 대소문자 체크
- JavaScript에는 대소문자 체크 함수가 없다...!
- 따라서 대소문자 변환을 한 후, 기존 문자와 동일한지 체크하는 방식으로 대소문자를 체크해주어야 한다.

### 대소문자 변환</code></pre><p>문자열.toUpperCase();
문자열.toLowerCase();</p>
<pre><code>
### 문자열 포맷팅</code></pre><p>console.log(&#39;name: %s&#39;, &#39;하츄핑&#39;);</p>
<pre><code>- 파이썬과 비슷한 방식</code></pre><p>const a = 1;
const b = 2;
console.log(<code>${a} + ${b} = ${a + b}</code>;</p>
<pre><code>- 백틱(`)을 사용해서 문자열 포맷팅을 할 수 있음

### join</code></pre><p>const arr = [&#39;바람&#39;, &#39;비&#39;, &#39;물&#39;];</p>
<p>console.log(arr.join());
// 바람,비,물</p>
<p>console.log(arr.join(&#39;&#39;));
// 바람비물</p>
<p>console.log(arr.join(&#39;-&#39;));
// 바람-비-물</p>
<pre><code>
## 문제 풀이
### 프로그래머스 level 0 - 문자열 출력하기</code></pre><p>const readline = require(&#39;readline&#39;);
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});</p>
<p>let input = [];</p>
<p>rl.on(&#39;line&#39;, function (line) {
    input = [line];
}).on(&#39;close&#39;,function(){
    str = input[0];
    console.log(str)
});</p>
<pre><code>
### 프로그래머스 level 0 - a와 b 출력하기</code></pre><p>const readline = require(&#39;readline&#39;);
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});</p>
<p>let input = [];</p>
<p>rl.on(&#39;line&#39;, function (line) {
    input = line.split(&#39; &#39;);
}).on(&#39;close&#39;, function () {
    console.log(&#39;a = &#39; + Number(input[0]));
    console.log(&#39;b = &#39; + Number(input[1]))
});</p>
<pre><code>- 굳이 Number 형 변환을 해주지 않아도 된다.
- 또한, ```const [a, b] = line.split(&#39; &#39;)``` 이렇게 a, b값을 받을 수도 있다. (이때 a와 b를 scope 밖에서 미리 선언해야 한다.)

### 프로그래머스 level 0 - 문자열 반복해서 출력하기</code></pre><p>const readline = require(&#39;readline&#39;);
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});</p>
<p>let input = []</p>
<p>rl.on(&#39;line&#39;, function (line) {
    input  = line.split(&#39; &#39;);
}).on(&#39;close&#39;, function () {
    var answer = &#39;&#39;
    for (i = 0; i &lt; Number(input[1]); i++){
        answer += input[0]
    }
    console.log(answer);
});</p>
<pre><code>- 처음에는 repeat 함수를 알지 못해 이렇게 작성했었다.</code></pre><p>const readline = require(&#39;readline&#39;);
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});</p>
<p>let input = [];</p>
<p>rl.on(&#39;line&#39;, function (line) {
    input  = line.split(&#39; &#39;);
}).on(&#39;close&#39;, function () {
    const str = input[0];
    const n = Number(input[1]);
    console.log(str.repeat(n));
});</p>
<pre><code>- repeat 함수를 적용하면 반복문을 쓸 필요가 없다.

### 프로그래머스 level 0 - 대소문자 바꿔서 출력하기</code></pre><p>const readline = require(&#39;readline&#39;);
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});</p>
<p>let input = [];</p>
<p>rl.on(&#39;line&#39;, function (line) {
    input = [line];
}).on(&#39;close&#39;,function(){
    str = input[0];
    var answer = &#39;&#39;;</p>
<pre><code>for (i= 0 ; i &lt; str.length; i++){
    if (str[i] == str[i].toUpperCase()){
        answer += str[i].toLowerCase();
    } else {
        answer += str[i].toUpperCase();
    }
}
console.log(answer);</code></pre><p>});</p>
<pre><code>
### 프로그래머스 level 0 - 특수 문자 출력하기</code></pre><p>const readline = require(&#39;readline&#39;);
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});</p>
<p>rl.on(&#39;close&#39;, function () {
    console.log(&#39;!@#$%^&amp;*(\&#39;&quot;&lt;&gt;?:;&#39;);
});</p>
<pre><code>- js의 이스케이프 코드는 python과 동일하다~

### 프로그래머스 level 0 - 덧셈식 출력하기</code></pre><p>const readline = require(&#39;readline&#39;);
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});</p>
<p>let input = [];</p>
<p>rl.on(&#39;line&#39;, function (line) {
    input = line.split(&#39; &#39;);
}).on(&#39;close&#39;, function () {
    console.log( String(input[0]), &#39;+&#39;, String(input[1]), &#39;=&#39;,
                String(Number(input[0]) + Number(input[1])));
});</p>
<pre><code>- 다른 사람 풀이 보고 배운 것</code></pre><p>rl.on(&#39;line&#39;, function (line) {
    [a, b] = line.split(&#39; &#39;).map(Number);
}).on(&#39;close&#39;, function () {
    console.log(<code>${a} + ${b} = ${a + b}</code>);
});</p>
<pre><code>- map으로 Number 함수를 일괄 적용할 수 있다
- 백틱을 사용해서 문자열 포맷팅을 사용하자

### 프로그래머스 level 0 - 문자열 붙여서 출력하기</code></pre><p>const readline = require(&#39;readline&#39;);
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});</p>
<p>let input = [];</p>
<p>rl.on(&#39;line&#39;, function (line) {
    input = line.split(&#39; &#39;);
}).on(&#39;close&#39;, function () {
    str1 = input[0];
    str2 = input[1];
    console.log(str1+str2);
});</p>
<pre><code>- join 함수를 사용하면 input 개수에 관계없이 모두 붙여 쓸 수 있다. 

### 프로그래머스 level 0 - 홀짝 구분하기</code></pre><p>const readline = require(&#39;readline&#39;);
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});</p>
<p>let input = [];</p>
<p>rl.on(&#39;line&#39;, function (line) {
    input = line.split(&#39; &#39;);
}).on(&#39;close&#39;, function () {
    n = Number(input[0]);
    if (n%2 == 0){
        console.log(<code>${n} is even</code>);
    } else {
        console.log(<code>${n} is odd</code>);
    }
});
```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JavaScript] 조건문]]></title>
            <link>https://velog.io/@choi-jiae/JavaScript-switch-if-else</link>
            <guid>https://velog.io/@choi-jiae/JavaScript-switch-if-else</guid>
            <pubDate>Sun, 30 Jun 2024 15:34:26 GMT</pubDate>
            <description><![CDATA[<h2 id="개념">개념</h2>
<p>조건에 따라 실행 명령을 달리할 때 조건문을 쓴다.</p>
<h3 id="switch">switch</h3>
<pre><code>switch(조건)
{
    case 값1: 명령 1
        break
    case 값2: 명령 2
        break
    default: 명령 n
}</code></pre><ul>
<li>조건은 case문의 값과 일대일로 대응되어야 한다.</li>
<li>조건과 일치하는 case문의 값이 없다면, default의 명령이 실행된다.</li>
<li>default문을 제외한 모든 case문에는 반드시 마지막에 <code>break</code>을 붙여줘야 한다.</li>
</ul>
<h3 id="if-else">if else</h3>
<pre><code>if(조건){
    명령문
} else if (조건){
    명령문
}else{
    명령문
}</code></pre><h2 id="문제-풀이">문제 풀이</h2>
<h3 id="프로그래머스-level-0---수-조작하기-1">프로그래머스 level 0 - 수 조작하기 1</h3>
<pre><code>function solution(n, control) {
    var answer = n;
    for (i=0; i &lt; control.length; i++){
        const word = control[i];

        if (word == &#39;w&#39;) {
            answer += 1
        } else if (word == &#39;s&#39;){
            answer -= 1
        } else if (word == &#39;d&#39;){
            answer += 10
        } else {
            answer -= 10
        }
    }
    return answer;
}</code></pre><ul>
<li>switch 문으로도 풀었었는데 break을 빼먹어서 결과가 이상하게 나왔었다...</li>
</ul>
<h3 id="프로그래머스-level-0----마지막-두-원소">프로그래머스 level 0  - 마지막 두 원소</h3>
<pre><code>function solution(num_list) {
    last_idx = num_list.length - 1
    if (num_list[last_idx] &gt; num_list[last_idx-1]){
        num_list.push(num_list[last_idx]-num_list[last_idx-1])        
    } else {
        num_list.push(num_list[last_idx]*2)
    }

    return num_list;
}</code></pre><ul>
<li>다른 풀이 보고 배운 것
<code>const [a, b] = num_list.slice(-2)</code> 이렇게 마지막 두 원소를 가지고 와도 된다!</li>
</ul>
<h3 id="프로그래머스-level-0---이어-붙인-수">프로그래머스 level 0 - 이어 붙인 수</h3>
<pre><code>function solution(num_list) {
    var odd = &#39;&#39;
    var even = &#39;&#39;
    for (i = 0; i &lt; num_list.length; i++){
        var num = num_list[i]
        if (num % 2 == 0){
            even += String(num)
        } else {
            odd += String(num)
        }
    }
    var answer = Number(odd) + Number(even);
    return answer;
}</code></pre><h3 id="프로그래머스-level-0---홀짝에-따라-다른-값-반환하기">프로그래머스 level 0 - 홀짝에 따라 다른 값 반환하기</h3>
<pre><code>function solution(n) {
    var answer = 0;
    var start = 0;

    if (n % 2 == 0){
        for (i = 2; i &lt;= n; i += 2){
            answer += (i*i)
        }   
    } else {
        for (i = 1; i &lt;= n; i += 2){
            answer += i
        }
    }

    return answer;
}</code></pre><ul>
<li>다른 사람 풀이 보고 배운 것
수학 공식을 적극 활용하면 코드가 짧아진다! 단, 공식을 확실히 알고 있을 때만 쓰도록 하자.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JavaScript] JS로 코딩테스트 준비하기]]></title>
            <link>https://velog.io/@choi-jiae/JavaScript-JS%EB%A1%9C-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A4%80%EB%B9%84%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@choi-jiae/JavaScript-JS%EB%A1%9C-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A4%80%EB%B9%84%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 29 Jun 2024 14:51:02 GMT</pubDate>
            <description><![CDATA[<p>지금까지는 나에게 가장 익숙한 언어인 python을 사용하여 코딩테스트 문제를 풀어왔었다. 프론트엔드 코테는 js만 지원한다는 곳이 많아지고 있다는 사실은 알고 있었지만, 먼저 python으로 코딩테스트에 익숙해진 후에 python 코드를 js로 바꾸는 방식으로 js 코테를 준비하면 될 것이라고 생각했었다. 하지만 문득 이렇게 준비하다가는 js 코테 준비가 너무 늦어질 것 같다는 생각이 들었다. FE 개발자로 살아가려면 어차피 js를 많이 쓰게 될텐데 굳이 python이라는 필터를 하나 거쳐서 js 코드에 도달하는 것은 비효율적인 것 같았다. 한국에서 살아갈 아이가 한국어를 구사하기 위해 영어를 잘 구사하는 방법을 먼저 배우는 것과 다를 바가 없어보였다. 그래서 지금부터라도 js로 바로 아이디어를 옮겨보는 연습을 시작하려고 한다.</p>
<p>우선은, 프로그래머스 level 0 문제들을 풀면서 javascript 기본 문법에 대해 정리해보려 한다. level 0 문제들은 문제 풀이 아이디어를 떠올리는데에는 무리가 없기에 JavaScript 기본 개념 위주로 글을 작성해보겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JavaScript] Array]]></title>
            <link>https://velog.io/@choi-jiae/JavaScript-Array</link>
            <guid>https://velog.io/@choi-jiae/JavaScript-Array</guid>
            <pubDate>Sat, 29 Jun 2024 14:49:13 GMT</pubDate>
            <description><![CDATA[<h2 id="개념">개념</h2>
<h3 id="array">array</h3>
<ul>
<li>여러 자료를 묶어서 활용할 수 있는 자료구조</li>
<li>어떤한 종류의 자료형도 array의 요소가 될 수 있다.</li>
<li>배열의 길이는 고정이 아니다.</li>
</ul>
<h3 id="array-생성">array 생성</h3>
<ul>
<li>python과 동일하게 <code>[]</code> 안에 요소들을 넣어주면 된다.<pre><code>const array = [123, &#39;text&#39;, true, function(){}, [1, 2]]</code></pre></li>
</ul>
<h3 id="array-요소-접근">array 요소 접근</h3>
<ul>
<li>python과 동일<pre><code>const numbers = [23, 45, 65]
numbers[0] // 23</code></pre></li>
</ul>
<h3 id="array-길이">array 길이</h3>
<pre><code>배열.length</code></pre><h3 id="array-뒷부분에-요소-추가">array 뒷부분에 요소 추가</h3>
<ul>
<li>array 맨 뒷 부분에 요소 추가<pre><code>배열.push(요소)</code></pre></li>
<li>인덱스를 사용하는 방법<pre><code>배열[인덱스] = 요소</code></pre>만약 중간에 비어있는 부분이 생긴다면 그 구간은 그대로 아무것도 없는 empty가 됨.</li>
</ul>
<h3 id="array-요소-제거하기">array 요소 제거하기</h3>
<ul>
<li><p>인덱스 기반</p>
<pre><code>배열.splice(인덱스, 인덱스로부터 제거할 요소의 개수)</code></pre></li>
<li><p>value 기반</p>
<pre><code>배열.indexof(요소)</code></pre><p>로 해당 요소의 인덱스를 먼저 찾은 후, splice하면 된다.
indexOf()는 배열 내부에 요소가 있을 경우에만 인덱스를 return하고, 그렇지 않을 때는 -1을 return한다.</p>
</li>
<li><p>특정 value를 가진 요소 모두 제거하기</p>
<pre><code>const = items = [&#39;모자&#39;, &#39;바지&#39;, &#39;티셔츠&#39;, &#39;모자&#39;, &#39;모자&#39;]
items.filter((item) =&gt; item !== &#39;모자&#39;)
// [&#39;바지&#39;, &#39;티셔츠&#39;]</code></pre></li>
<li><p><code>filter</code>: 조건에 맞는 요소들만 걸러줌.</p>
</li>
</ul>
<h3 id="array의-특정-위치에-요소-추가">array의 특정 위치에 요소 추가</h3>
<pre><code>배열.splice(추가하고 싶은 위치 인덱스, 0, 추가하고 싶은 요소)</code></pre><ul>
<li>splice의 두 번째 매개변수에 0을 입력하면 array의 요소를 제거하지 않음.</li>
</ul>
<h2 id="문제-풀기">문제 풀기</h2>
<h3 id="프로그래머스-level-0---카운트-업">프로그래머스 level 0 - 카운트 업</h3>
<pre><code>function solution(start_num, end_num) {
    var answer = [];
    for (i = start_num ; i &lt;= end_num; i++ ){
        answer.push(i)
    }
    return answer;
}</code></pre><ul>
<li>array &amp; for문</li>
<li>for문의 i++는 {}안의 실행코드가 모두 실행된 후에 실행됨!</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Python] 프로그래머스 Level 2 프로세스]]></title>
            <link>https://velog.io/@choi-jiae/Python-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Level-2-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4</link>
            <guid>https://velog.io/@choi-jiae/Python-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Level-2-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4</guid>
            <pubDate>Fri, 28 Jun 2024 06:21:23 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>point</strong>
queue, max</p>
</blockquote>
<h3 id="생각의-흐름">생각의 흐름</h3>
<ul>
<li>priorities 만으로는 process의 위치를 파악하기 어려워서 process라는 list를 하나 더 만들었다. (list의 요소들이 곧 process의 위치)</li>
<li>현재 max_priority 보다 process의 priority가 작을 경우 dequeue &amp; enqueue를 진행했고, 같을 경우 dequeue를 진행하는 방식으로 실행순서를 계산했다.</li>
<li>중간에 list 길이 바뀌는 걸 고려하지 않아서 자꾸 index 에러가 났었다. -&gt; priorities를 remove하는 대신 해당 값을 0으로 바꾸는 것으로 해결!</li>
</ul>
<h3 id="풀이">풀이</h3>
<pre><code>def solution(priorities, location):
    process = [i for i in range(len(priorities))]
    answer = 0
    max_priority = max(priorities)

    while process:
        i = process.pop(0)

        if priorities[i] &lt; max_priority:
            process.append(i)

        else:
            answer += 1
            if i == location:
                break
            else:
                priorities[i] = 0
                max_priority = max(priorities)

    return answer</code></pre><h3 id="다른-풀이-보고-배운-점">다른 풀이 보고 배운 점</h3>
<ul>
<li>process라는 새로운 list 대신 priorities와 location을 묶은 list를 만들면 remove를 하더라도 index 에러가 날 이유가 없다!</li>
<li>python queue 모듈을 사용하는 방법을 자꾸 까먹어서 list로 사용하게 되는데, queue 를 사용하면 속도가 훨씬 빨라지니 기억해두자<pre><code>from collections deque
</code></pre></li>
</ul>
<p>queue = deque([4, 5, 6])
queue.append(7)
queue.popleft()
```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Python] 프로그래머스 Level 2 올바른 괄호]]></title>
            <link>https://velog.io/@choi-jiae/Python-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Level-2-%EC%98%AC%EB%B0%94%EB%A5%B8-%EA%B4%84%ED%98%B8</link>
            <guid>https://velog.io/@choi-jiae/Python-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Level-2-%EC%98%AC%EB%B0%94%EB%A5%B8-%EA%B4%84%ED%98%B8</guid>
            <pubDate>Fri, 28 Jun 2024 05:22:01 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>point</strong>
스택을 이용하면 좋은 문제</p>
</blockquote>
<h3 id="생각의-흐름">생각의 흐름</h3>
<ul>
<li>&#39; ( &#39; 가 나올 때마다 stack에 push를 하고, 그 짝인 &#39; ) &#39; 가 나올 때 stack에서 pop을 하는 방법을 쓰면 된다.</li>
<li>이때, pop을 정상적으로 할 수 없거나, 주어진 괄호를 모두 썼음에도 stack이 비어있지 않는 경우 잘못된 괄호라고 보고 False를 return 한다.</li>
</ul>
<h3 id="풀이">풀이</h3>
<pre><code>def solution(s):
    stack = []

    for i in s:
        if i == &#39;(&#39;:
            stack.append(i)
        else:
            if stack:
                stack.pop()
            else:
                return False

    if stack:
        return False
    else:
        return True</code></pre><h3 id="다른-사람-풀이-보고-배운-점">다른 사람 풀이 보고 배운 점</h3>
<pre><code>    if stack:
        return False
    else:
        return True</code></pre><p>이 부분을</p>
<pre><code>return len(stack) == 0</code></pre><p>이라고 작성해도 깔끔할 것 같다!</p>
]]></description>
        </item>
    </channel>
</rss>