<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>도브로</title>
    <link>https://kimdozzi.tistory.com/</link>
    <description>끝까지 포기하지 않으면, 내가 다 이겨!</description>
    <language>ko</language>
    <pubDate>Fri, 19 Jun 2026 04:38:39 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>kimdozzi</managingEditor>
    <image>
      <title>도브로</title>
      <url>https://tistory1.daumcdn.net/tistory/5054969/attach/632466b71eed40bca1b35b40bf12063b</url>
      <link>https://kimdozzi.tistory.com</link>
    </image>
    <item>
      <title>JavaScript에서 Class가 결국 함수라는 게 어떤 의미일까?</title>
      <link>https://kimdozzi.tistory.com/304</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;JavaScript에서 Class는 Function이다.&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트의 클래스는 내부적으로 함수로 구현되어 있습니다. Person 클래스를 통해 어떤 의미인 지 이해해보도록 하겠습니다. Person 클래스는 name 필드와 생성자, sayHello 메소드를 갖고 있습니다. 먼저, 생성자 함수를 살펴보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1735449825203&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Person {
  name: string;

  constructor(name) {
    this.name = name;
  }

  sayHello() {
    console.log(`Hello, I'm ${this.name}`);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자는 자바스크립트 내부적으로 아래와 같은 함수의 형태를 가집니다. this는 새로 생성되는 객체를 가리키고, constructor 내부의 코드가 여기서 실행됩니다. name이라는 매개 변수를 받아서 새로 생성될 객체의 필드(name)에 해당 값을 저장하고, 암묵적으로 return this가 발생하게 되면, 생성자 함수가 종료됩니다. &amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1735449840397&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Person(name) {
  this.name = name;
  // return this; 이 부분
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sayHello 메서드를 JavaScript로 변환하면 아래와 같은 코드가 됩니다. 그 이유는 JavaScript는 프로토타입 기반 언어로서, 클래스의 메서드를 각 인스턴스에 직접 추가하는 대신 prototype에 추가하는 것이 메모리 효율성과 상속 구현을 위한 핵심적인 방식이기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를&amp;nbsp;들어,&amp;nbsp;Person&amp;nbsp;클래스의&amp;nbsp;sayHello&amp;nbsp;메서드를&amp;nbsp;각&amp;nbsp;인스턴스에&amp;nbsp;직접&amp;nbsp;추가한다면,&amp;nbsp;Person&amp;nbsp;클래스의&amp;nbsp;인스턴스를&amp;nbsp;1000개&amp;nbsp;만들&amp;nbsp;때&amp;nbsp;sayHello&amp;nbsp;메서드도&amp;nbsp;1000개가&amp;nbsp;생성됩니다.&amp;nbsp;이는&amp;nbsp;엄청난&amp;nbsp;메모리&amp;nbsp;낭비가&amp;nbsp;됩니다.&amp;nbsp;sayHello&amp;nbsp;메소드를&amp;nbsp;Person.prototype에&amp;nbsp;한&amp;nbsp;번만&amp;nbsp;추가하고,&amp;nbsp;모든&amp;nbsp;Person&amp;nbsp;인스턴스는&amp;nbsp;이&amp;nbsp;하나의&amp;nbsp;메서드를&amp;nbsp;공유하여&amp;nbsp;사용합니다.&amp;nbsp;인스턴스에서&amp;nbsp;sayHello를&amp;nbsp;호출하면,&amp;nbsp;먼저&amp;nbsp;인스턴스&amp;nbsp;자체에서&amp;nbsp;이&amp;nbsp;메서드를&amp;nbsp;찾고,&amp;nbsp;없으면&amp;nbsp;prototype으로&amp;nbsp;올라가서&amp;nbsp;찾습니다.&amp;nbsp;이를&amp;nbsp;`프로토타입&amp;nbsp;체인`이라고&amp;nbsp;합니다.(프로토타입&amp;nbsp;체인은&amp;nbsp;객체가&amp;nbsp;속성이나&amp;nbsp;메소드를&amp;nbsp;찾을&amp;nbsp;때,&amp;nbsp;탐색하는&amp;nbsp;연결된&amp;nbsp;객체들의&amp;nbsp;경로를&amp;nbsp;말합니다.)&lt;/p&gt;
&lt;pre id=&quot;code_1735450418315&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;Person.prototype.sayHello = function () {
  console.log(`Hello, I'm ${this.name}`);
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;인스턴스 생성 후 sayHell() 실행 과정&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;new Person()으로 인스턴스를 생성하면, 이 인스턴스의 proto는 자동으로 Person.prototype을 가리킵니다.&lt;/li&gt;
&lt;li&gt;person.sayHello()를 호출하면, JavaScript 엔진은 먼저 person 객체 자체에서 sayHello 메서드를 찾습니다.&lt;/li&gt;
&lt;li&gt;찾지 못하면 person.proto(즉, Person.prototype)에서 찾습니다.&lt;/li&gt;
&lt;li&gt;여기서 sayHello 메서드를 찾아 실행합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정말일까?&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;놀랍게도 정말입니다. 클래스는 함수입니다. 이것은 JavaScript의 역사적 배경과 설계 철학에 깊이 연관되어 있다고 합니다. JavaScript는 1995년 Brendan Eich에 의해 단 10일 만에 만들어졌습니다. 당시 Java가 인기 있었기에, JavaScript도 객체 지향 프로그래밍을 지원해야 했습니다. 하지만 시간 제약과 브라우저의 경량화 요구사항으로 인해, Java처럼 완전한 클래스 기반 시스템을 구현하기는 어려웠고, 프로토타입 기반 언어로 설계하게 되었다고 합니다. 기존에는 class 문법이 존재하지 않았습니다. 하지만, 기존의 프로토타입 기반 상속이 직관적이지 않고, 더 명확한 객체 지향 프로그래밍 패턴을 제공하기 위해 ES6에서 도입되었다고 합니다. 결론적으로, JavaScript가 클래스를 함수로 구현한 것은 단순한 설계 선택이 아니라, 언어의 특성과 요구사항, 그리고 실용적인 이점들을 고려한 결과였습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1735450729370&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Person {
    name: string;
  
    constructor(name:string) {
      this.name = name;
    }
  
    sayHello() {
      console.log(`Hello, I'm ${this.name}`);
    }
  }

  
  console.log(typeof Person); // function
  console.log(Person === Person.prototype.constructor); // true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;위키피디아 - 자바스크립트&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://namu.wiki/w/JavaScript&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;나무위키 - 자바스크립트&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-ES6-Class-%EB%AC%B8%EB%B2%95-%EC%99%84%EB%B2%BD-%EC%A0%95%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;자바스크립트 클래스 문법 - 완벽 가이드&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Classes&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Classes - mdn&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Backend/TypeScript</category>
      <author>kimdozzi</author>
      <guid isPermaLink="true">https://kimdozzi.tistory.com/304</guid>
      <comments>https://kimdozzi.tistory.com/304#entry304comment</comments>
      <pubDate>Sun, 29 Dec 2024 20:45:18 +0900</pubDate>
    </item>
    <item>
      <title>추상클래스와 제네릭 (실습)</title>
      <link>https://kimdozzi.tistory.com/305</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;요구사항에 맞게 코드를 작성하는 과제입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;1. 클래스 구조&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- PublicOfficer: 추상 클래스로, 공무원의 기본 속성과 메서드를 정의&lt;br /&gt;-&amp;nbsp;하위&amp;nbsp;클래스:&amp;nbsp;Police,&amp;nbsp;Firefighter,&amp;nbsp;Doctor가&amp;nbsp;상속&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;2. 핵심 static 클래스들&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- CentralArchives: 모든 공무원 정보를 중앙 저장소에서 관리&lt;br /&gt;- TrainingCenter: 공무원 객체 생성을 담당하는 팩토리 클래스 (제네릭 활용)&lt;br /&gt;-&amp;nbsp;Extractor:&amp;nbsp;특정&amp;nbsp;타입의&amp;nbsp;공무원&amp;nbsp;목록을&amp;nbsp;필터링하는&amp;nbsp;유틸리티&amp;nbsp;클래스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;3. 주요 기능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 공무원 등록/제거 (CentralArchives)&lt;br /&gt;- 타입별 공무원 목록 조회 (Extractor)&lt;br /&gt;- 근속연수(짝수/홀수)별 필터링 기능&lt;br /&gt;-&amp;nbsp;제네릭을&amp;nbsp;활용한&amp;nbsp;타입&amp;nbsp;안전성&amp;nbsp;보장&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1735451302549&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. PublicOfficer 를 상속 받는 클래스를 추가하세요. (Police, Firefighter 외 1개)
// - 속성을 추가하여도 무방합니다.
// 2. CentralArchives 클래스를 static 으로 구현하세요.
// - PublicOfficer[] 를 가지고 있는 중앙 저장소입니다.
// - 해당 필드는 외부에서는 읽기만 가능해야 합니다.
// - register, remove 메소드 구현
// - PublicOfficer[] 에 추가, 삭제합니다.
// - 모든 생성되는 Officer 는 CentralArchives 에 등록되어야 합니다.
// 3. TrainingCenter 클래스를 static 으로 구현하세요.
// - PublicOfficer 를 상속받은 클래스를 생성하는 팩토리 클래스입니다.
// - Police 뿐만 아니라 다른 Class 에 대한 train 함수도 구현해야 합니다.
// 4. Extractor 클래스를 static 으로 구현하세요.
// - CentralArchives 에서 필요한 직업의 정보를 추출하는 Utility 클래스입니다.
// - getPoliceList 메소드 구현합니다. (다른 class getList 메소드도 구현해야 합니다.)

// **추가 요구 사항**
// 5. 짝수/홀수 근속연수에 따라 Officer들을 반환해주세요.
// 6. TrainingCenter와 Extractor의 중복되는 메소드를 제네릭을 활용해서 리팩토링하세요.

abstract class PublicOfficer {
  public name: string;
  public yearsOfService: number;

  constructor(name: string, yearsOfService: number) {
    // 초기화
    this.name = name;
    this.yearsOfService = yearsOfService;
  }

  public abstract introduce(): void;

  public toString(): string {
    return `Name: ${this.name}, 
                Years of service: ${this.yearsOfService}`;
  }
}

class Police extends PublicOfficer {
  public introduce(): void {
    // 필요한 구현
    console.log(super.toString());
  }
}

class Firefighter extends PublicOfficer {
  public introduce(): void {
    // 필요한 구현
    console.log(super.toString());
  }
}

class Doctor extends PublicOfficer {
  public introduce(): void {
    // 필요한 구현
    console.log(super.toString());
  }
}

class CentralArchives {
  private static officers: PublicOfficer[] = [];
  private constructor() {}

  public static get allOfficers(): ReadonlyArray&amp;lt;PublicOfficer&amp;gt; {
    // officers 반환
    return this.officers as ReadonlyArray&amp;lt;PublicOfficer&amp;gt;;
  }

  public static register(officer: PublicOfficer): void {
    // officer 추가
    this.officers.push(officer);
  }

  public static remove(officer: PublicOfficer): void {
    // officer 제거
    const index = this.officers.indexOf(officer);
    if (index &amp;gt; -1) {
      this.officers.splice(index, 1);
    }
  }
}

class TrainingCenter {
  private constructor() {}

  public static train&amp;lt;T extends PublicOfficer&amp;gt;(
    constructor: new (name: string, yearsOfService: number) =&amp;gt; T,
    name: string,
    yearsOfService: number
  ) {
    const officer = new constructor(name, yearsOfService);
    CentralArchives.register(officer);
    return officer;
  }
}

type Constructor&amp;lt;T&amp;gt; = new (...args: any[]) =&amp;gt; T;

class Extractor {
  private constructor() {}

  public static getPoliceList(): Police[] {
    return this.filterByType(Police);
  }

  public static getFirefighterList(): Firefighter[] {
    return this.filterByType(Firefighter);
  }

  public static getDoctorList(): Doctor[] {
    return this.filterByType(Doctor);
  }

  private static filterByType&amp;lt;T extends PublicOfficer&amp;gt;(
    type: Constructor&amp;lt;T&amp;gt;
  ): T[] {
    // 어떤 인자를 받든 T 타입의 인스턴스를 만들 수 있는 생성자
    return CentralArchives.allOfficers.filter(
      (officer): officer is T =&amp;gt; officer instanceof type
    ); // 이 조건문이 true를 반환하면 officer는 T 타입이다
  }

  // 짝수/홀수 근속연수 조회
  public static filterByYearsOfService&amp;lt;T extends PublicOfficer&amp;gt;(
    type: Constructor&amp;lt;T&amp;gt;,
    isEven: boolean
  ): T[] {
    return this.filterByType(type).filter((officer) =&amp;gt;
      isEven
        ? this.isEvenYearsOfService(officer)
        : !this.isEvenYearsOfService(officer)
    );
  }

  public static isEvenYearsOfService(officer: PublicOfficer): boolean {
    return officer.yearsOfService % 2 === 0;
  }

  // police
  public static getPoliceEvenYearsOfService(): Police[] {
    return this.filterByYearsOfService(Police, true);
  }

  public static getPoliceOddYearsOfService(): Police[] {
    return this.filterByYearsOfService(Police, false);
  }

  // firefighter
  public static getFirefighterEvenYearsOfService(): Firefighter[] {
    return this.filterByYearsOfService(Firefighter, true);
  }

  public static getFirefighterOddYearsOfService(): Firefighter[] {
    return this.filterByYearsOfService(Firefighter, false);
  }

  // doctor
  public static getDoctorEvenYearsOfService(): Doctor[] {
    return this.filterByYearsOfService(Doctor, true);
  }

  public static getDoctorOddYearsOfService(): Doctor[] {
    return this.filterByYearsOfService(Doctor, false);
  }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Backend/TypeScript</category>
      <author>kimdozzi</author>
      <guid isPermaLink="true">https://kimdozzi.tistory.com/305</guid>
      <comments>https://kimdozzi.tistory.com/305#entry305comment</comments>
      <pubDate>Sun, 29 Dec 2024 14:49:15 +0900</pubDate>
    </item>
    <item>
      <title>아이템13. type과 interface 차이점 알기</title>
      <link>https://kimdozzi.tistory.com/303</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트에서 named type을 정의하는 방법은 두 가지가 있습니다. 대부분의 경우에는 타입을 사용해도 되고 인터페이스를 사용해도 됩니다. 그러나 타입과 인터페이스 사이에 존재하는 차이를 분명하게 알고, 같은 상황에서는 동일한 방ㅂ버으로 명명된 타입을 정의해 일관성을 유지해야 합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1735110615310&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type TState = {
    name: string;
    capital: string;
}

interface IState {
    name: string;
    capital: string;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;type과 interface의 비슷한 점&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 인덱스 시그니처는 인터페이스와 타입에서 모두 사용할 수 있다.&lt;/h3&gt;
&lt;pre id=&quot;code_1735110739235&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type TDict = { [key:string]: string};
interface IDict {
    [key:string]: string;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 함수 타입은 인터페이스와 타입으로 정의 가능하다.&lt;/h3&gt;
&lt;pre id=&quot;code_1735111058694&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type TFunction = (x: string) =&amp;gt; string;
interface IFunction {
    (x: string): string;
}

const toStrType: TFunction = x =&amp;gt; '@' + x;
console.log(toStrType(&quot;hello&quot;));

const toStrInterface: IFunction = x =&amp;gt; '@' + x;
console.log(toStrInterface(&quot;hello&quot;));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Generic 사용이 둘 다 가능하다.&amp;nbsp;&lt;/h3&gt;
&lt;pre id=&quot;code_1735111312881&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type TPair&amp;lt;T&amp;gt; = {
    x: T;
    y: T;
}
const numberTPair: TPair&amp;lt;number&amp;gt; = {
    x:10,
    y:20,
}
console.log(numberTPair.x + &quot; &quot; + numberTPair.y);


interface IPair&amp;lt;T&amp;gt; {
    x: T;
    y: T;
}
const numberIPair: IPair&amp;lt;number&amp;gt; = {
    x:20,
    y:30,
}
console.log(numberIPair.x + &quot; &quot; + numberIPair.y);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 인터페이스는 타입을 확장할 수 있고, 타입은 인터페이스를 확장할 수 있다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 주의할 점은 인터페이스는 유니온 타입 같은 복잡한 타입을 확장하지는 못한다는 것입니다. 복잡한 타입을 확장하려면 type과 &amp;amp;를 사용해야 합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1735112190885&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type TState = {
    name: string;
    capital: string;
}

interface IState {
    name: string;
    capital: string;
}

// 두 코드는 동일한 기능을 합니다. 
type TStateWithPop = IState &amp;amp; { population: number};

interface IStateWithPop extends TState {
    population: number;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 클래스를 구현(implements)할 때는, 타입과 인터페이스 둘 다 가능하다.&lt;/h3&gt;
&lt;pre id=&quot;code_1735112381575&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type TState = {
    name: string;
    capital: string;
}

interface IState {
    name: string;
    capital: string;
}

class StateT implements TState {
    name: string = &quot;&quot;;
    capital: string = &quot;&quot;;
}

class StateI implements IState {
    name: string = &quot;&quot;;
    capital: string = &quot;&quot;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;type과 interface의 다른 점&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 유니온 타입은 있지만, 유니온 인터페이스는 없다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터페이스는 타입을 확장할 수 있지만, 유니온은 할 수 없습니다. 그런데 유니온 타입을 확장하는 게 필요할 때가 있습니다. 아래와 같은 예시를 보면, 유니온 타입의 장점이 확연하게 드러납니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1735113022708&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type RequestState = 'idle' | 'loading' | 'success' | 'error';

type RequestData&amp;lt;T&amp;gt; = {
    state: RequestState;
    data?: T;
    error?: string;
}

interface User {
    id: number;
    name: string;
}

const userRequest: RequestData&amp;lt;User&amp;gt; = {
    state: 'success',
    data: {id:1, name: &quot;John&quot;}
};

const failedRequest: RequestData&amp;lt;User&amp;gt; = {
    state: 'error',
    error: &quot;Failed to fetch user&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터페이스로 구현해면 되지 않냐구요? 인터페이스로 구현하고, 타입을 사용했을 경우와 비교해보도록 하겠습니다. 한 눈에 봐도 복잡한 코드임을 알 수 있습니다. 이런 경우에는 타입스크립트의 type을 최대한 활용하는 것이 적절해 보입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1735113173323&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface RequestStateMap {
    readonly IDLE: 'idle';
    readonly LOADING: 'loading';
    readonly SUCCESS: 'success';
    readonly ERROR: 'error';
}

const RequestState: RequestStateMap = {
    IDLE: 'idle',
    LOADING: 'loading',
    SUCCESS: 'success',
    ERROR: 'error'
} as const;

interface RequestData&amp;lt;T&amp;gt; {
    state: RequestStateMap[keyof RequestStateMap];
    data?: T;
    error?: string;
}

interface User {
    id: number;
    name: string;
}

const userRequest: RequestData&amp;lt;User&amp;gt; = {
    state: 'success',
    data: {id:1, name: &quot;John&quot;}
};

const failedRequest: RequestData&amp;lt;User&amp;gt; = {
    state: 'error',
    error: &quot;Failed to fetch user&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. type에는 없지만, interface에는 있는 선언 병합(declaration merging)&lt;/h3&gt;
&lt;pre id=&quot;code_1735113517070&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface IState {
    name: string;
    address: string;
}

interface IState {
    population: number;
}

// 정상
const mergedInterface: IState = {
    name: &quot;Kim&quot;,
    address: &quot;Seoul&quot;,
    population: 500_000,
}

// Duplicate identifier 'TState'.
// type TState = {
//     name: string;
//     address: string;
// }

// type TState = {
//     population: number;
// }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이펙티브 타입스크립트&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://typescript-kr.github.io/pages/interfaces.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;타입스크립트 GitBook&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Backend/TypeScript</category>
      <author>kimdozzi</author>
      <guid isPermaLink="true">https://kimdozzi.tistory.com/303</guid>
      <comments>https://kimdozzi.tistory.com/303#entry303comment</comments>
      <pubDate>Sun, 29 Dec 2024 14:16:56 +0900</pubDate>
    </item>
    <item>
      <title>Interface (인터페이스)</title>
      <link>https://kimdozzi.tistory.com/302</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;기본적인 인터페이스 사용 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입 검사는 printLabel을 호출합니다. printLabel 함수는 string 타입 label을 갖는 객체를 하나의 매개변수로 가집니다. 이 객체가 실제로는 더 많은 프로퍼티를 갖고 있지만, 컴파일러는 최소한 필요한 프로퍼티가 있는지와 타입이 잘 맞는지만 검사합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734842342804&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function printLabel(labeledObj: {label: string}) {
    console.log(labeledObj.label);
}

let myObj = {size:10, label: &quot;Size 10&quot;};
printLabel(myObj);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LabeledValue 인터페이스는 숫자 타입의 size 프로퍼티와 문자열 타입의 label 프로퍼티를 가지고 있습니다. 다른 언어처럼 printLabel에 전달한 객체가 이 인터페이스를 구현해야 한다고 명시적으로 얘기할 필요가 없고, 여기서 중요한 것은 형태뿐입니다. 함수에 전달된 객체가 요구 조건을 충족하면, 허용됩니다. 타입 검사는 프로퍼티들의 순서를 요구하지 않습니다. 단지, 인터페이스가 요구하는 프로퍼티들이 존재하는지와 프로퍼티들이 요구하는 타입을 가졌는지만 확인합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1734846329771&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface LabeledValue {
    size: number,
    label: string,
}

function printLabel(labeledObj: LabeledValue) {
    console.log(labeledObj.size);
    console.log(labeledObj.label);
}

let myObj = {size:10, label: &quot;Size is 10.&quot;};
printLabel(myObj);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;선택적 프로퍼티 (Optional Properties)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터페이스의 모든 프로퍼티가 필요한 것은 아닙니다. 어떤 조건에서만 존재하거나 아예 없을 수도 있습니다. SquareConfig 인터페이스의 프로퍼티들은 선택적, 필수 프로퍼티를 가지고 있습니다. 이는 TypeScript의 타입 시스템에서 프로퍼티에 ?를 붙이느냐 안 붙이느냐로 결정되며, &quot;객체 내부에 최소 1개의 필수 프로퍼티가 있어야 한다&quot;는 규칙은 없습니다. 각 프로퍼티의 필수 여부는 단순히 해당 프로퍼티에 ? 연산자가 있는지 없는지에 따라 결정됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1734846408051&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface SquareConfig {
    color?: string, 	// 선택적 프로퍼티
    size: { 		// 필수 프로퍼티 
        width?: number;	// 선택적 프로퍼티
        height: number; // 필수 프로터피
    }
}

function createSquare(config: SquareConfig): {color: string; area: number} {
    let newSquare = {
        color: &quot;white&quot;,
        area: 100
    };

    if (config.color) {
        newSquare.color = config.color;
    }

    const width = config.size.width ?? 10;
    newSquare.area = width * config.size.height;

    return newSquare;
} 

let mySquare = createSquare({color: &quot;black&quot;, size: {height:  50}});
console.log(mySquare)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 아래와 같은 코드도 동작할까요? 정답은 '네! 동작합니다!' 하지만, 논리적으로 개선이 필요해보이는 코드입니다. size는 필수 프로퍼티이지만, 그 내부의 모든 프로퍼티가 선택적이라면&amp;nbsp; size가 필수 프로퍼티일 이유가 없는 것 같습니다. 단순하게 이런 코드도 동작한다는 작은 예시일 뿐, 실제 프로젝트에서 아래와 같은 코드는 작성하지 않을 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1734847478051&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface SquareConfig {
    color?: string, 	
    size: { 		
        width?: number;	
        height?: number; 
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;size 프로퍼티의 내부 프로퍼티 둘 중 하나는 반드시 존재해야 한다면 어떻게 할까요? 아래와 같이 작성할 수 있습니다.정말 유연하죠?&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734847670563&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface SquareConfig {
    color?: string,
    size: {
        width: number;  
        height?: number; 
    } | {
        width?: number;
        height: number;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;읽기 전용 프로퍼티 (Readonly properties)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일부 프로퍼티들은 객체가 처음 생성될 때만 수정 가능합니다. 프로퍼티 이름 앞에 readonly를 넣어서 이를 지정할 수 있습니다. TypeScript에서 ReadonlyArray&amp;lt;T&amp;gt;는 일반 배열(Array&amp;lt;T&amp;gt;)에서 배열을 변경할 수 있는 모든 베서드들을 제거한 타입입니다.&amp;nbsp; 하지만, arr = [] 와 같은 동작은 허용됩니다. 그 이유는 arr이 let으로 선언되었기 때문입니다. 만약 변수 자체로 수정 불가능하게 하고 싶다면 const를 사용해야 합니다. arr 자체를 재할당은 가능하지만, arr 내부의 프로퍼티들은 Readonly로 선언되었기에, 삽입/삭제 연산이 불가능하고 인덱스를 통한 요소 수정이 불가능한 것입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수는 const, 프로퍼티는 readonly를 사용한다고 생각하면 되겠습니다. 여기서 프로퍼티란 객체가 갖고 있는 키-값을 의미합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1734848273296&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface Point {
    readonly x: number;
    readonly y: number;

}

let p1: Point = {x:10, y:20};
// p1.x = 20; 불가능

let arr: ReadonlyArray&amp;lt;Number&amp;gt; = [1,2,3,45];
// arr[2] = 1;       불가능
// arr.push(10);     불가능
// arr.length = 100; 불가능
arr = [];
console.log(arr);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;여기서 의문!! 배열이 왜 객체인가요?&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바/타입스크립트에서 배열은 특별한 형태의 객체입니다. 배열의 각 요소는 인덱스를 키로 가지는 프로퍼티입니다. 내부적으로 아래와 같은 형태라고 합니다. 정말인지 한번 확인해볼까요?&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734848724751&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let arr = [10, 20, 30];

let arrAsObject = {
    '0': 10,
    '1': 20,
    '2': 30,
    length: 3
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정말 신기합니다.. 자바스크립트의 세계.... 무튼 배열에 대해 다시 한번 정의하자면, 배열은 숫자 인덱스를 가진 특별한 객체라고 보면 되겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734848823324&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let arr = [10, 20, 30];

console.log(arr[0]);     
console.log(arr['0']);   // 인덱스를 문자열로 접근해도 동일

console.log(Object.keys(arr)); 
console.log(Object.values(arr)); 

console.log(arr.length);  

// 10
// 10
// [ '0', '1', '2' ]
// [ 10, 20, 30 ]
// 3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;초과 프로퍼티 검사&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초과 프로퍼티 검사란 타입스크립트에서 객체 리터럴을 인터페이스나 타입에 할당할 때, 정의되지 않은 프로퍼티가 있는지 검사하는 것입니다. 개발자에 따라 개발 방식이 상이하겠지만, 타입 제한이 느슨하면 유지보수도 어려울 거고, 의도하지 않은 동작을 초래할 수도 있을 것입니다. 이러한 이유로 타입스크립트의 장점을 극대화하는 중요한 기능이라고 생각합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734850963892&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface SquareConfig {
    color?: string;
    width?: number;
    height?: number;
    [provName: string]: any; // 문자열 인덱스 서명 방식에서만 사용.
}
function createSquare(config: SquareConfig): {color: string; area: number} {
    let newSquare = {
        color: &quot;white&quot;,
        area: 100
    };

    if (config.color) {
        newSquare.color = config.color;
    }

    const width = config.width ?? 10;
    const height = config.height ?? 20;
    newSquare.area = width * height;

    return newSquare;
} 

// 방법1. 타입 단언 사용
// let mySquare = createSquare({ colour: &quot;red&quot;, width: 100 } as SquareConfig);

// 방법2. 문자열 인덱스 서명 추가
let mySquare = createSquare({ colour: &quot;red&quot;, width: 100 });

// 방법3. 변수 할당 후 전달 
// 초과 프로퍼티 검사는 객체 리터럴을 직접 할당할 때만 발생하며, 변수를 통해 전달할 때는 검사하지 않습니다.
// let squareOptions = { colour: &quot;red&quot;, width: 100 };
// let mySquare = createSquare(squareOptions);

console.log(mySquare);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;함수 타입&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터페이스에 함수 타입을 정의할 수 있습니다. JAVA에서 인터페이스를 정의하면 해당 인터페이스를 구현하는 클래스는 필수적으로 추상 메소드들을 구현해야 했습니다. 자바스크립트는 파라미터와 반환 타입만을 정의하여 개발자가 보다 유연하게 인터페이스를 활용할 수 있도록 도와줍니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734852019763&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface SearchFunc {
    (source: string, substring: string): number;
}

let mySearch: SearchFunc;
mySearch = function(source: string, sub: string) {
    let result = source.search(sub);
    return result;
}

const result = mySearch(&quot;hello world&quot;, &quot;World&quot;);
console.log(result);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;인덱서블 타입 (Indexable Types)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터페이스로 함수 타입을 설명하는 방법과 유사하게, a[10] 이나 ageMap[&quot;daniel&quot;] 처럼 타입을 인덱스로 기술할 수 있습니다. 인덱서블 타입은 인덱싱 할 때 해당 반환 유형과 함께 객체를 인덱싱하는 데 사용할 수 있는 타입을 기술하는 인덱스 시그니처를 가지고 있습니다. &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;&lt;i&gt;인덱서블 타입은 미리 정확히 알 수 없는 키-값 쌍을 가진 객체를 다룰 때 사용합니다.&lt;/i&gt;&lt;/b&gt;&lt;/span&gt; API 응답, 환경 변수, 설정 객체 등이 대표적인 예시입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;인덱스 시그니처&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[indexName: indexer type] : return type 형식으로 선언합니다. name과 같이 직접 표기한 것은 똑같이 명시해야 하지만 인덱서블 타입은 추가를 원할 때 사용하기 때문에 옵셔널 타입이라고 볼 수 있습니다. 다만 인덱서블 타입은 string과 number 두 가지만 사용할 수 있으며 선언된 즉시 인터페이스 안의 값들은 모두 인덱서 타입의 서브 타입이 되어야 합니다. 따라서 보통의 경우, 인덱서블의 리턴 타입을 확장하거나 any를 사용합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734913269564&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface StringDictionary {
	name: string;
	[key: string]: string; // 인덱스 시그니처
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;인덱서 타입의 서브 타입이 되는 경우&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1735104327066&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface NumberOrStringDictionary {
    [index: string]: number | string;
    length: number;    // 성공, length는 숫자입니다
    name: string;      // 성공, name은 문자열입니다
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;조금 더 복잡한 값을 가진 인덱서블 타입&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1735103852766&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface EmployeeIndex {
  [key:string]: {id: number, name: string}; // key가 string, value가 {id, name}
}

const employees: EmployeeIndex = {
  first: {id: 1, name: &quot;park&quot;}, 
  second: {id: 2, name: &quot;jin&quot;},
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;배열의 인덱싱에 사용&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1735103953224&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface StringList {
  [index: number]: string;
}

let words: StringList = [];

words[0] = &quot;hi&quot;;
// words[1] = 3; // ERROR! 'number' 형식은 'string'형식에 할당 불가.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;클래스 타입 (Class Types)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;인터페이스 구현하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 언어에서 인터페이스를 사용하는 가장 일반적인 방법입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1735104431054&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface ClockInterface {
    currentTime: Date;
}

class Clock implements ClockInterface {
    currentTime: Date = new Date();
    constructor(h: number, m: number) { }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;클래스의 이중성과 그 해결 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스는 정적 측면에서 클래스 자체의 메서드와 인스턴스 생성을 위한 생성자를 가질 수 있고, 인스턴스 측면에서 생성된 인스턴스의 메서드와 생성된 인스턴스의 속성을 가질 수 있습니다. 이해를 돕기 위해 간단한 예시로 알아보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1735106399236&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. 먼저 '강아지 만드는 설계도'의 형태를 정의
interface DogConstructor {
    new (name: string): DogInterface;
}

// 2. '강아지가 할 수 있는 행동들'을 정의
interface DogInterface {
    bark(): void;
    sit(): void;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트에서는 Static 타입과 인스턴스 타입, 두 가지를 명확하게 구분해서 다뤄야 합니다. 왜 그럴까요?&lt;/p&gt;
&lt;pre id=&quot;code_1735106519687&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 에러!
class Poodle implements DogConstructor {
    constructor(name: string) {}
    bark() { console.log(&quot;짖다!&quot;); }
    sit() { console.log(&quot;앉다&quot;); }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러가 나는 이유는 implements는 강아지가 할 수 있는 행동들, 다시 말해서 인스턴스 타입만 검사할 수 있는데 DogConstructor는 강아지를 만드는 설계도, Static 타입(생성자)을 검하려고 하기 때문입니다. 그래서 이러한 해결책을 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1735106823026&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// '강아지 공장' 만들기
interface DogInterface {
    bark(): void;
    sit(): void;
}

interface DogConstructor {
    new (name: string): DogInterface;
}

function createDog( constructor: DogConstructor, name: string): DogInterface {
    return new constructor(name);
}

//  '할 수 있는 행동'만 신경 쓰면 됩니다
class Poodle implements DogInterface {
    constructor(name: string) {
        this.name = name;
    }
    
    name: string;
    
    bark() {
        console.log(&quot;왈왈!&quot;);
    }
    
    sit() {
        console.log(&quot;앉았습니다&quot;);
    }
}

const myPoodle = createDog(Poodle, &quot;푸들이&quot;);
myPoodle.bark();  // &quot;왈왈!&quot; 출력&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론은 클래스의 두 가지 측면(생성자, 인스턴스)을 이해하고 이를 명확히 구분해서 다루라는 것입니다. 그리고 이를 위해 팩토리 패턴을 사용해서 타입의 안전성을 높이고, 코드 관리를 용이하게 하라는 것입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 예제가 필요하다면 아래 링크를 참고하시면 됩니다. '클래스의 스태틱과 인스턴스의 차이점' 이라는 소제목에서 확인할 수 있고, 주석은 직접 공부하면서 작성한 것입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://typescript-kr.github.io/pages/interfaces.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://typescript-kr.github.io/pages/interfaces.html&lt;/a&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1735105874073&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 클래스의 생성자 형태를 정의하는 인터페이스
// new 키워드는 이 인터페이스가 생성자를 위한 것임을 나타냄. 
// hour와 minute를 받아서 ClockInterface 타입의 인스턴스를 반환하는 생성자여야 함을 명시.
interface ClockConstructor {
    new (hour: number, minute: number): ClockInterface;
}

// 시계 인스턴스가 가져야 할 메서드를 정의하는 인터페이스
// tick() 메소드를 반드시 구현해야 함.
interface ClockInterface {
    tick(): void;
}

// 시계 인스턴스를 생성하는 팩토리 함수 
// ctor: 생성자 함수를 받는다. (DigitalClock or AnalogClock)
// hour, minute: 시계 초기값을 받는다.
// 반환값으로 ClockInterface를 구현한 인스턴스를 줌.
function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {
    return new ctor(hour, minute);
}

class DigitalClock implements ClockInterface {
    constructor(h: number, m: number) { }
    tick() {
        console.log(&quot;beep beep&quot;);
    }
}
class AnalogClock implements ClockInterface {
    constructor(h: number, m: number) { }
    tick() {
        console.log(&quot;tik tok&quot;);
    }
}


// 디지털 시계 생성 요청
let digital = createClock(DigitalClock, 12, 17);

// 실행 순서:
// 1. createClock 함수가 호출됨
// 2. 함수는 DigitalClock 생성자, 12, 17을 매개변수로 받음
// 3. 함수 내부에서 new DigitalClock(12, 17) 실행
// 4. DigitalClock의 constructor(12, 17) 실행
// 5. 새로운 DigitalClock 인스턴스가 반환됨
// 6. 이 인스턴스가 digital 변수에 할당됨

// tick 메서드 호출
digital.tick();  // &quot;beep beep&quot; 출력

// 실행 순서:
// 1. digital 객체의 tick 메서드 찾기
// 2. DigitalClock 클래스에 정의된 tick 메서드 실행
// 3. &quot;beep beep&quot; 콘솔 출력&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;인터페이스 확장&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스처럼, 인터페이스들도 extend가 가능합니다. 이는 한 인터페이스의 멤버를 다른 인터페이스에 복사하는 것을 가능하게 해주는데, 인터페이스를 재사용성 높은 컴포넌트로 쪼갤 때, 유연함을 제공해줍니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1735107285227&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface Shape {
    color: string;
}

interface PenStroke {
    penWidth: number;
}

interface Square extends Shape, PenStroke {
    sideLength: number;
}

let square = {} as Square;
square.color = &quot;blue&quot;;
square.sideLength = 10;
square.penWidth = 5.0;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;하이브리드 타입&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트의 동적이고 유연한 특성을 보여주는 기능입니다. 이러한 기능을 사용하는 이유는 유연한 API 설계가 가능하고, 함수와 객체의 장점을 모두 활용할 수 있다고 합니다. 지금까지 JAVA로 백엔드 개발을 해 온 필자는 혼란스럽습니다....... 코드가 엄청 복잡해질 것 같은데 실무에서 하이브리드 타입을 직접적으로 사용하는 지 궁금해지네요....&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1735108060714&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Counter 인터페이스 정의
interface Counter {
    (start: number): string;     // Counter는 함수로도 호출될 수 있음 (start를 받아서 string 반환)
    interval: number;            // interval이라는 숫자 속성을 가짐
    reset(): void;               // reset이라는 메서드를 가짐
}

// Counter 타입의 객체를 생성하는 함수
function getCounter(): Counter {
    // 빈 함수를 만들고 Counter 타입으로 타입 단언(as)
    let counter = (
        function (start: number) {
            console.log(`함수 호출: ${start}`)
         }
    ) as Counter;
    
    // counter 객체에 interval 속성 추가
    counter.interval = 123;
    
    // counter 객체에 reset 메서드 추가
    counter.reset = function () {
        console.log(&quot;RESET&quot;);
     };
    
    // 완성된 counter 객체 반환
    return counter;
}

// Counter 인스턴스 생성
let c = getCounter();

// Counter 사용
c(10);              // 함수로 호출
c.reset();          // 메서드 호출
c.interval = 5.0;   // 속성 값 변경
console.log(c, c.interval);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;클래스를 확장한 인터페이스&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스를 상속받은 인터페이스라는 개념도 존재합니다. (그만....... 제발 멈춰!!!)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터페이스 타입이 클래스 타입을 확장하면, 클래스의 멤버는 상속받지만 구현은 상속받지 않습니다. 이것은 인터페이스가 구현을 제공하지 않고, 클래스의 멤버 모두를 선언한 것과 마찬가지입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터페이스는 기초 클래스의 private과 protected 멤버도 상속받습니다. 이것은 인터페이스가 private 혹인 protected 멤버를 포함한 클래스를 확장할 수 있다는 뜻이고, 인터페이스 타입은 그 클래스나 하위 클래스에 의해서만 구현될 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1735108975167&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Control {
    private state: any;
}

interface SelectableControl extends Control {
    select(): void;
}

class Button extends Control implements SelectableControl {
    select() { }
}

class TextBox extends Control {
    select() { }
}

// Class 'Apple' incorrectly implements interface 'SelectableControl'.
// Types have separate declarations of a private property 'state'.
// class Apple implements SelectableControl {
//     private state: any;
//     select() { }
// }


// Apple 클래스가 SelectableControl 인터페이스를 올바르게 구현하려면 Control 클래스를 상속받아야 합니다.
class Apple extends Control implements SelectableControl {
    select() { }
}

class Car {

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://typescript-kr.github.io/pages/interfaces.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://typescript-kr.github.io/pages/interfaces.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@hb-developer/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-tnsk260u&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@hb-developer/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-tnsk260u&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ppassongssong.tistory.com/28&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ppassongssong.tistory.com/28&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/TS-%F0%9F%93%98-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%F0%9F%92%AF-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://inpa.tistory.com/entry/TS-%F0%9F%93%98-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%F0%9F%92%AF-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eunhye919.tistory.com/111&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://eunhye919.tistory.com/111&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://redjen8.github.io/posts/ts-handbook/interface/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://redjen8.github.io/posts/ts-handbook/interface/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Backend/TypeScript</category>
      <category>interface</category>
      <category>TypeScript</category>
      <category>인덱서블 타입</category>
      <category>인덱스 시그니처</category>
      <category>인터페이스</category>
      <category>타입스크립트</category>
      <author>kimdozzi</author>
      <guid isPermaLink="true">https://kimdozzi.tistory.com/302</guid>
      <comments>https://kimdozzi.tistory.com/302#entry302comment</comments>
      <pubDate>Wed, 25 Dec 2024 15:47:39 +0900</pubDate>
    </item>
    <item>
      <title>Iteration Protocol (+Symbol)</title>
      <link>https://kimdozzi.tistory.com/300</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;이터레이션 프로토콜&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ES6에서 도입된 이터레이션 프로토콜은 데이터 컬렉션을 순회하기 위한 프로토콜입니다. ES6에서 도입되었으며 크게 두 가지로 구분됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 이터러블 프로토콜&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Symbol.iterator 메서드를 구현하여 이터레이터를 반환하고, 자바스크립트의 대표적인 이터러블한 데이터 컬렉션은 Array, String, Map, Set 등이 있습니다. for ... of 문으로 순회가 가능하며, ...(스프레드 연산자)를 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 이터레이터 프로토콜&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;next() 메소드를 갖고 있습니다. next() 메소드는 value와 done 프로퍼티를 가진 객체를 반환합니다. value는 현재 순회 중인 값을 나타내고, done은 순회 완료 여부를 나타내는 boolean 값입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 Symbol에 대해서 먼저 알면 이터레이션 프로토콜에 대한 이해가 더 쉽습니다. Symbol은 ES6에서 도입된 원시 타입으로, 이터레이션 프로토콜의 핵심인 Symbol.iterator와 같은 내장 심볼들을 통해 객체의 특별한 동작을 정의할 수 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Symbol과 Global Symbol Resgistry&lt;/h3&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;Symbol&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;Symbol 값은 Symbol 함수를 호출하여 생성합니다. 심볼 값은 자바스크립트 런타임 환경에서 Symbol 함수에 의해 동적으로 생성되며 다른 값과 중복되지 않는 고유한 값입니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&amp;nbsp;&lt;i&gt;&lt;b&gt;즉, 변경 불가능한 원시 타입의 값입니다.&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;모든 Symbol() 호출은 각각 고유한 심볼을 반환하는 것이 보장됩니다. 주어진 키를 가진 심볼이 전역 심볼 레지스트리에 존재하면 그 심볼을 반환하고, 없으면 새로 생성한 후 전역 심볼 레지스트리에 등록하고 반환합니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Symbol 함수에 들어가는 문자열 인자는 심볼 값에 대한 Description으로서 선택적으로 넣을 수 있습니다. 이 문자열은 디버깅 용도로만 사용되며 심볼 값 생성에 영향을 주지 않습니다. 심볼 값도 객체처럼 메소드를 사용하면 암묵적으로 래퍼 객체를 생성합니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;여기서 말하는 래퍼 객체란 일시적으로 임시 객체를 만들어서 메소드나 프로퍼티를 사용할 수 있게 한 후, 역할이 끝나면 다시 원시 값으로 돌아오는 것을 말합니다. 사용이 끝난 래퍼 객체를 GC의 대상이 됩니다.&amp;nbsp;&lt;/u&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Description 프로퍼티와 toString은 Symbol.prototype의 프로퍼티이고, 심볼 값은 문자열이나 숫자 타입으로 변환되지 않습니다. 단, boolean 타입으로는 변환이 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1734841971295&quot; class=&quot;stylus&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const a = Symbol('a')
a.description // &quot;a&quot;
a.toString() // &quot;Symbol(a)&quot;
!a // false
a + 'hi' // Uncaught TypeError&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;Global symbol registry&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;자바스크립트 엔진이 관리하는 전역 심볼 레지스트리는 사용 가능한 모든 심볼이 저장되어 있습니다. 레지스트리에 접근할 수 있는 함수로는 Symobl.for, Symbol.keyFor 메소드가 있습니다. 이 메소드들은 전역 심볼 레지스트리 테이블과 런타임 환경 사이에서 심볼 값을 전해주는 역할을 합니다. 전역 심볼 레지스트리는 대부분 자바스크립트 컴파일 인프라에 내장되어 있고, 레지스트리 내용은 자바스크립트 런타임 환경에서는 Symbol.for, Symbol.keyFor 메소드를 사용하지 않고서는 접근이 불가능합니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Symbol.for&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Symbol.for 메소드는 인수로 전달받은 문자열을 키로 사용해 전역 심볼 레지스트리에 해당 키와 일치하는 심볼 값을 검색하게 됩니다. 이미 심볼이 있으면 해당 심볼을 반환하고, 없으면 새로 생성하여 반환합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1734841971296&quot; class=&quot;javascript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log(Object.getOwnPropertySymbols(Array.prototype));
console.log(Array[Symbol.species] === Array);

// [ Symbol(Symbol.iterator), Symbol(Symbol.unscopables) ]
// true&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1734841971297&quot; class=&quot;javascript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const symbolA:any = Symbol('A'); // 매번 새로운 고유한 symbol 생성, 전역 레지스트리와 관계 없이 항상 새로운 값.
const symbolB:any = Symbol('A');

console.log(symbolA === symbolB); // false, 서로 다른 고유한 symbol 값을 가짐

const symbolC:any = Symbol.for('A'); // 전역 레지스트리 검색 -&amp;gt; 없음 -&amp;gt; 생성 -&amp;gt; 저장 후 반환.
const symbolD:any = Symbol.for('A'); // 레지스트리 검색 -&amp;gt; 있음 -&amp;gt; 반환.
console.log(symbolC === symbolD) // true, 같은 symbol 참조

// false
// true&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Symbol.keyFor&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;symbol.keyFor 메소드는 심볼 값을 인수로 받아서 전역 레지스트리에 저장된 심볼 키 값을 가져옵니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734841971298&quot; class=&quot;reasonml&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log(Symbol.keyFor(symbolC));

// A&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Symbol 함수는 호출될 때 마다 심볼 값을 생성하지만 전역 심볼 레지스트리에 등록되어 관리되지 않고, Symbol.for 메소드를 사용하면 문자열 키를 통해 전역에서 중복되지 않는 심볼 값을 하나만 생성하여, 레지스트리를 통해 값을 공유할 수 있습니다. 정리하자면, Symbol()은 정말 고유한 값이 필요할 때 사용하고, Symbol.for()는 공유와 재사용이 필요할 때 사용한다고 보면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이터러블 프로토콜&amp;nbsp;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 이터러블은 Symbol.iterator 메소드를 구현하거나 프로토타입 체인에 의해 상속한 객체이다.&lt;/h4&gt;
&lt;pre id=&quot;code_1734837244437&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const arr = [1,2,3];

console.log(Symbol.iterator in arr);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 이터러블 프로토콜을 준수한 배열은 for...of 문에서 순회 가능하다.&lt;/h4&gt;
&lt;pre id=&quot;code_1734837233396&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for(const item of arr) {
    console.log(item);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 스프레드 연산자를 사용 가능하다.&lt;/h4&gt;
&lt;pre id=&quot;code_1734837326173&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const arr1 = [1,2,3];
const arr2 = [4,5,6];

// 복사
const copyArr = [...arr1]; // [1,2,3]

// 배열 결합
const combiedArr = [...arr1, ...arr2]; // [1,2,3,4,5,6]

// 중간에 값 추가
const newArr = [...arr1, 7, ...arr2]; // [1,2,3,7,4,5,6]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이터레이터 프로토콜&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;next()&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이터레이터 프로토콜은 next 메소드를 소유하고, next 메소드를 호출하면 이터러블을 순회하며 value, done 프로퍼티를 갖는 이터레이터 리절트 객체 (IteratorResult) 를 반환합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734837381142&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const array = [1,2,3];
// array[Symbol.iterator] 는 배열의 `Symbol.iterator`라는 내장 메서드에 접근하는 것.
const iter = array[Symbol.iterator](); 

let iterResult = iter.next();
console.log(iterResult)
iterResult = iter.next();
console.log(iterResult)
iterResult = iter.next();
console.log(iterResult)
iterResult = iter.next();
console.log(iterResult)

// result:
// { value: 1, done: false }
// { value: 2, done: false }
// { value: 3, done: false }
// { value: undefined, done: true }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;사용자 정의 이터러블&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Symbol.iterator 메소드를 구현하여 자신만의 이터러블을 만들 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734839989391&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const customIterable = {
    [Symbol.iterator]() {
        let count: number = 1;
        return {
            next() {
                return count &amp;lt;= 3 
                ? {value: count++, done: false}
                : {value: undefined, done: true};
            }
        }
    }
}

for(const num of customIterable) {
    console.log(num);
}

// 1
// 2
// 3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 기본적인 이터러블, 이터레이터 프로토콜에 대해 알아보았습니다. 자바스크립트를 처음 접하면서 자바스크립트 언어의 유연한 특성이 너무 혼란스러웠습니다. 하지만 사람은 적응하는 동물아니겠습니까!!!! 타입스크립트를 자유자재로 사용하는 그 날 까지 열심히 공부해보려고 합니다. 파이팅!!!!!! (이펙티브 타입스크립트 책도 구매했습니다 ㅎㅎㅎㅎ)&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 자료&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Symbol.iterator&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Symbol.iterator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Symbol&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Symbol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://roseline.oopy.io/dev/javascript-back-to-the-basic/symbol-usage&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://roseline.oopy.io/dev/javascript-back-to-the-basic/symbol-usage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tc39.es/ecma262/#sec-ecmascript-language-types-symbol-type&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://tc39.es/ecma262/#sec-ecmascript-language-types-symbol-type&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Backend/TypeScript</category>
      <author>kimdozzi</author>
      <guid isPermaLink="true">https://kimdozzi.tistory.com/300</guid>
      <comments>https://kimdozzi.tistory.com/300#entry300comment</comments>
      <pubDate>Mon, 23 Dec 2024 09:04:26 +0900</pubDate>
    </item>
    <item>
      <title>non-null 단언 연산자(!), null 병합 연산자(??) 그리고 옵셔널 체이닝(.?)</title>
      <link>https://kimdozzi.tistory.com/301</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;! (Non-null assertion operator)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;!연산자는 개발자가 &quot;이 값은 확실히 null/undefined가 아니다&quot;라고 TypeScript 컴파일러에게 알려주는 것입니다. 하지만 실제 런타임에서는 아무런 체크도 하지 않기 때문에, 확실히 보장될때만 사용해야 하며, 가능하다면 if 문으로 null 체크를 하거나 옵셔널 체이닝을 사용하는 것이 더 안전한 방법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Null 체크 우회&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;name은 string | null 이지만, 개발자가 이 시점에서 확실히 null이 아님을 알고 있을 때 사용하면 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734845148343&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. NULL 체크 우회
let name: string | null = &quot;Ecount&quot;;
let nameLength = name!.length;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 초기화 체크 우회&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스 필드 name이 생성자에서 직접 초기화되지 않고, 다른 메소드에서 초기화될 것임을 알려줍니다. !가 없으면 TypeScript는 name이 생성자에서 초기화되지 않았다고 경고를 줍니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734845630354&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class User {
    name!: string;
    constructor() {
        this.initalize();
    }
    private initialize() {
        this.name = &quot;ecount&quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;! 연산자는 타입 체크를 우회하므로 신중하게 사용해야 합니다. 가능하면 타입 가드나 조건문으로 명시적 체크를 하는 것이 더 안전하며, 특히 외부 데이터를 다룰 때는 ! 사용을 피하고 명시적인 null 체크를 하는 것이 더 좋습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1734845744650&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 위험한 방법
function funcA(user: User | null) {
    const name = user!.name;  // 런타임 에러 가능성!
}

// 안전한 방법
function funcB(user: User | null) {
    if (user === null) {
        return;
    }
    const name = user.name;  // 타입 가드 사용
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;?. (Optional Chaining)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵셔널 체이닝은 객체, 배열, 함수에 사용할 수 있습니다. 접근하는 주체의 프로퍼티가 null 또는 undefined일 수 있다면 if 문을 사용하지 않고 넘어가게 하는 방법입니다. 객체가 null 또는 undefined이면 undefined를 리턴하고, 그렇지 않은 경우 데이터 값을 리턴합니다. 보통의 경우 아래와 같은 코드에서 타입 에러가 발생하지만,&amp;nbsp; &amp;amp;&amp;amp; 연산자를 사용하면 문제를 해결할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1734845195886&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. &amp;amp;&amp;amp;
// 에러가 발생하는 코드
let user = {};

alert(user.address.phone); // TypeError

// 에러가 발생하지 않는 코드
let user = {};

alert(user &amp;amp;&amp;amp; user.address &amp;amp;&amp;amp; user.address.phone); // undefined&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;?.의 경우 앞의 대상이 undefined이거나 null일 때 평가를 중단하고 undefined를 반환합니다. 장점은 코드를 간소화할 수 있고, 안전하게 객체에 접근이 가능합니다. 단점은 실제로는 문제가 있는 코드인데 에러가 발생하지 않아 디버깅이 어려울 수 있고, 타입 안정성이 저하됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1734845238164&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let user = {};

alert(user?.address?.street); // undefined&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;?? (Nullish Coalescing)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;널 병합 연산자는 왼쪽 피연산자가 null이거나 undefined일때만 오른쪽 피연산자를 반환하는 연산자입니다. 아래 코드에 따르면, comparer가 제공되지 않으면 기존에 구현해둔 DefaultGenericComparer를 사용하기 위해 널 병합 연산자를 사용하였습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1734845262597&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public constructor(comparer: IEqualityComparer&amp;lt;TValue&amp;gt; | null = null) {
    this._count = 0;
    this._head = null;
    this._tail = null;
    this._comparer = comparer ?? DefaultGenericComparer;
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Backend/TypeScript</category>
      <author>kimdozzi</author>
      <guid isPermaLink="true">https://kimdozzi.tistory.com/301</guid>
      <comments>https://kimdozzi.tistory.com/301#entry301comment</comments>
      <pubDate>Sun, 22 Dec 2024 14:29:16 +0900</pubDate>
    </item>
    <item>
      <title>가비지 컬렉션과 Minor/Major GC 동작 과정</title>
      <link>https://kimdozzi.tistory.com/299</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;가비지 컬렉션은 더 이상 사용되지 않는 메모리를 자동으로 찾아내어 해제하는 메모리 관리 메커니즘이다. Java 개발에서 가장 중요한 특징 중 하나로, 개발자가 메모리 관리에 대한 부담을 덜 수 있게 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;가비지(Garbage)란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램을 개발하고 실행하는 과정에서는 더 이상 사용되지 않는 메모리인 '가비지(Garbage)'가 발생한다. 이는 다음과 같은 경우에 발생할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1731560094263&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. 객체의 참조가 null이 되는 경우
String text = &quot;Hello&quot;;
text = null;  // 이전의 &quot;Hello&quot; 객체는 가비지가 됨

// 2. 객체의 참조 범위를 벗어나는 경우
public void createObject() {
    String localVar = &quot;Hello&quot;;  // 메서드 종료 시 접근 불가능
}  // localVar는 메서드 종료와 함께 가비지가 됨

// 3. 객체의 참조가 다른 객체를 가리키는 경우
String a = &quot;Hello&quot;;
a = &quot;World&quot;;  // &quot;Hello&quot; 객체는 가비지가 됨&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;가비지 컬렉션 대상&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;가비지 컬렉션은 특정 객체가 garbage인지 아닌지 판단하기 위해서 도달성, 도달능력(Reachability) 이라는 개념을 적용한다. &lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;객체에 레퍼런스가 있다면&amp;nbsp;Reachable로 구분되고, 객체에 유효한 레퍼런스가 없다면&amp;nbsp;Unreachable로 구분해버리고 수거해버린다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Reachable : 객체가 참조되고 있는 상태&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Unreachable&amp;nbsp; : 객체가 참조되고 있지 않은 상태 (GC의 대상이 됨)&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;753&quot; data-origin-height=&quot;327&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wpWo9/btsKIUggsNn/vpkE8GAhp6GMGaxi5ies30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wpWo9/btsKIUggsNn/vpkE8GAhp6GMGaxi5ies30/img.png&quot; data-alt=&quot;인파님 블로그 이미지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wpWo9/btsKIUggsNn/vpkE8GAhp6GMGaxi5ies30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwpWo9%2FbtsKIUggsNn%2FvpkE8GAhp6GMGaxi5ies30%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;610&quot; height=&quot;265&quot; data-origin-width=&quot;753&quot; data-origin-height=&quot;327&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;인파님 블로그 이미지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어 JVM 메모리에서는 객체들은 실질적으로 Heap영역에서 생성되고 Method Area이나 Stack Area 에서는 Heap Area에 생성된 객체의 주소만 참조하는 형식으로 구성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이렇게 생성된 Heap Area의 객체들이 메서드가 끝나는 등의 특정 이벤트들로 인하여 Heap Area 객체의 메모리 주소를 가지고 있는 참조 변수가 삭제되는 현상이 발생하면, 위의 그림에서의&lt;span style=&quot;color: #ee2323;&quot;&gt; 빨간색 객체&lt;/span&gt;와 같이 Heap영역에서 어디서든 &lt;span style=&quot;color: #ee2323;&quot;&gt;참조하고 있지 않은 객체(Unreachable)&lt;/span&gt;들이 발생하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 객체들을 주기적으로 가비지 컬렉터가 제거해주는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Minor GC와 Major GC&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;878&quot; data-origin-height=&quot;570&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cPNRWg/btsKH6IuvZa/6ZTOmjNVoY60j5NkrDrIHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cPNRWg/btsKH6IuvZa/6ZTOmjNVoY60j5NkrDrIHk/img.png&quot; data-alt=&quot;인파님 블로그 이미지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cPNRWg/btsKH6IuvZa/6ZTOmjNVoY60j5NkrDrIHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcPNRWg%2FbtsKH6IuvZa%2F6ZTOmjNVoY60j5NkrDrIHk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;536&quot; height=&quot;348&quot; data-origin-width=&quot;878&quot; data-origin-height=&quot;570&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;인파님 블로그 이미지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;jvm의 힙 영역은 처음 설계될 때 다음의 2가지를 전제로 설계되었다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;대부분의 객체는 금방 접근 불가능한 상태(unreachable)가 된다.&lt;/li&gt;
&lt;li&gt;오래된 객체에서 새로운 객체로의 참조는 아주 적게 존재한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 객체는 대부분 일회성이 되며 메모리에 오랫동안 남아있는 경우는 드물다는 것. 그렇기 때문에 객체의 생존 기간에 따라 물리적인 힙 영역을 나누게 되었고 yong, old 2가지 영역으로 설계되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;264&quot; data-origin-height=&quot;178&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/X3s6O/btsKJH1trq4/D8DuWW9RbQH11JnXVMc1P1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/X3s6O/btsKJH1trq4/D8DuWW9RbQH11JnXVMc1P1/img.png&quot; data-alt=&quot;https://mangkyu.tistory.com/118&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/X3s6O/btsKJH1trq4/D8DuWW9RbQH11JnXVMc1P1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FX3s6O%2FbtsKJH1trq4%2FD8DuWW9RbQH11JnXVMc1P1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;264&quot; height=&quot;178&quot; data-origin-width=&quot;264&quot; data-origin-height=&quot;178&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://mangkyu.tistory.com/118&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;young
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;새롭게 생성된 객체가 allocation되는 영역&lt;/li&gt;
&lt;li&gt;대부분의 객체가 금방 unreachable 상태가 되기 때문에, 많은 객체가 young 영역에 생성되었다가 사라진다.&lt;/li&gt;
&lt;li&gt;young 영역에 대한 가비지 컬렉션을 minor gc라고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;old
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;young 영역에서 reachable 상태를 유지하여 살아남은 객체가 복사되는 영역&lt;/li&gt;
&lt;li&gt;young 영역보다 크게 할당되며, 영역의 크기가 큰 만큼 가비지는 적게 발생한다.&lt;/li&gt;
&lt;li&gt;old 영역에 대한 가비지 컬렉션을 major gc라고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;old 영역이 young 영역보다 크게 할당되는 이유는 young 영역의 수명이 짧은 객체들은 큰 공간을 필요로 하지 않으며 큰객체들은 young 영역이 아니라 바로 old 영역으로 할당되기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예외적으로 old 영역에 있는 객체가 young 영역의 객체를 참조하는 경우도 존재한다. 이러한 경우를 대비하여 old 영역에는 512 bytes의 덩어리(chunk)로 되어있는 카드 테이블이 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;366&quot; data-origin-height=&quot;226&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCuiUN/btsKH0BrQIG/DYXBgctAen2Hz1fyovwfD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCuiUN/btsKH0BrQIG/DYXBgctAen2Hz1fyovwfD0/img.png&quot; data-alt=&quot;https://mangkyu.tistory.com/118&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCuiUN/btsKH0BrQIG/DYXBgctAen2Hz1fyovwfD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCuiUN%2FbtsKH0BrQIG%2FDYXBgctAen2Hz1fyovwfD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;366&quot; height=&quot;226&quot; data-origin-width=&quot;366&quot; data-origin-height=&quot;226&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://mangkyu.tistory.com/118&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카드 테이블에는 old 영역에 있는 객체가 young 영역의 객체를 참조할 때 마다 그에 대한 정보가 표시된다. 카드 테이블이 도입된 이유는 간단하다. young 영역에서 가비지 컬렉션이 실행될 때 모든 old 영역에 존재하는 객체를 검사하여 참조되지 않는 young 영역의 객체를 식별하는 것이 비효율적이기 때문이다. 그렇기 때문에 young 영역에서 GC가 진행될 때 카드 테이블만 조회하여 GC의 대상인지 식별할 수 있도록 하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;GC의 동작 방식&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;young과 old는 서로 다른 메모리 구조이므로 세부적인 동작 방식을 다르다. 하지만 기본적으로 GC가 실행된다고 하면 다음의 2가지 공통적인 단계를 따른다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;stop the world&lt;/li&gt;
&lt;li&gt;mark and sweep&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;stop the world&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stop the world는 가비지 컬렉션을 실행하기 위해 jvm이 애플리케이션의 실행을 멈추는 작업이다. gc가 실행될 때는 gc를 실행하는 스레드를 제외한 모든 스레드들의 작업이 중단되고, gc가 완료되면 작업이 재개된다. 당연히 모든 스레드들의 작업이 중단되면 애플리케이션이 멈추기 때문에, gc의 성능 개선을 위해 튜닝을 한다고 하면 stop-the-world의 시간을 줄이는 작업을 한다. 또한 jvm에서도 이러한 문제를 해결하기 위해 다양한 실행 옵션을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;mark and sweep&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;mark: 사용되는 메모리와 사용되지 않는 메모리를 식별하는 작업&lt;/li&gt;
&lt;li&gt;sweep: mark 단계에서 사용되지 않음으로 식별된 메모리를 해제하는 작업&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stop the world를 통해 모든 작업을 중단시키면, gc는 스택의 모든 변수 또는 reachable 객체를 스캔하면서 각각이 어떤 객체를 참고하고 있는지 탐색하게 된다. 그리고 사용되고 있는 메모리를 식별하는데 이러한 과정을 mark라고 한다. 이후 mark가 되지 않는 객체들을 메모리에서 제거하는데 이러한 과정을 sweep이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Minor GC의 동작 과정&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;마이너 GC를 이해하기 위해 young 영역의 구조에 대해 이해해야 한다. young 영역은 1개의 eden 영역과 2개의 survivor 영역. 총 3가지로 나뉜다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;eden : 새로 생성된 객체가 할당되는 지역&lt;/li&gt;
&lt;li&gt;survivor: 최소 1번 이상의 GC로부터 살아남은 객체가 존재하는 영역&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;객체가 새롭게 생성되면 young 영역 중에서도 eden에 할당이 되고, eden이 꽉 차면 마이너 GC가 발생한다. 사용되지 앟는 메모리는 해제되고 eden 영역에 존재하는 (사용중인) 객체는 survivor 영역으로 옮겨지게 된다. survivor 영역은 2개이지만 반드시 1개의 영역에만 존재하고, 1개는 비어있어야 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;870&quot; data-origin-height=&quot;551&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GwaDo/btsKH2MRKWD/I6KtrHrIfWfZIBe42ks8Vk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GwaDo/btsKH2MRKWD/I6KtrHrIfWfZIBe42ks8Vk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GwaDo/btsKH2MRKWD/I6KtrHrIfWfZIBe42ks8Vk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGwaDo%2FbtsKH2MRKWD%2FI6KtrHrIfWfZIBe42ks8Vk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;654&quot; height=&quot;414&quot; data-origin-width=&quot;870&quot; data-origin-height=&quot;551&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;대상 영역
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;eden 영역과 from survivor 영역이 대상이다.&lt;/li&gt;
&lt;li&gt;새로 생성된 대부분의 객체는 eden에 할당된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Minor GC 발생 조건
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;eden 영역이 가득 차면 마이너 GC가 트리거된다.&lt;/li&gt;
&lt;li&gt;새로운 객체를 할당하려 할 때 eden 영역의 공간이 부족한 경우 발생한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Mark 단계
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GC Root(스택 변수, 정적 변수 등)부터 시작하여 접근 가능한 객체를 식별한다.&lt;/li&gt;
&lt;li&gt;eden 영역과 from survivor 영역의 살아있는 객체를 마킹한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;copy/sweep 단계
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Eden 영역의 살아있는 객체를 To Survivor 영역으로 복사된다.&lt;/li&gt;
&lt;li&gt;From Survivor 영역의 살아있는 객체도 To Survivor 영역으로 복사된다.&lt;/li&gt;
&lt;li&gt;복사될 때마다 객체의 age가 증가, 마킹되지 않은 객체는 자동으로 메모리가 해제된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;survivor 영역 교체
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;to survivor가 from survivor가 되고, 이전의 from survivor사 to survivor가 된다.&lt;/li&gt;
&lt;li&gt;다음 마이너 GC때는 새로운 from survivor 영역에서 to survivor 영역으로 복사가 진행된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;promotion 처리
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체의 age가 임게값에 도달하면 old 영역으로 promotion된다.&lt;/li&gt;
&lt;li&gt;survivor 영역의 크기를 초과하는 객체들도 바로 old 영역으로 promotion된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;메모리 정리
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;eden 영역과 from survivor 영역은 완전히 비워진다.&lt;/li&gt;
&lt;li&gt;to survivor영역에는 살아남은 객체들만 존재한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;GC 완료 후&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;eden 영역은 비워지고, 살아남은 객체들은 to survivor로 이동했다. 각 객체의 age가 증가했으며, age가 임계값(기본값은 15정도)에 도달하면 old generation으로 promotion된다. 그리고 survivor의 from과 to의 역할이 교체된다. 이러한 과정이 반복되면서 Young Generation의 메모리가 관리되며, 오래 살아남은 객체들은 점진적으로 Old Generation으로 이동하게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;870&quot; data-origin-height=&quot;357&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BLBSw/btsKJPFb8yW/0TG8VHzrPrgTCEjL0cBt2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BLBSw/btsKJPFb8yW/0TG8VHzrPrgTCEjL0cBt2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BLBSw/btsKJPFb8yW/0TG8VHzrPrgTCEjL0cBt2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBLBSw%2FbtsKJPFb8yW%2F0TG8VHzrPrgTCEjL0cBt2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;646&quot; height=&quot;265&quot; data-origin-width=&quot;870&quot; data-origin-height=&quot;357&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Major GC 동작 과정&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;636&quot; data-origin-height=&quot;272&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Nd9Cl/btsKHXkJSzv/aUL8ox7YYKVjzzCfwNAYpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Nd9Cl/btsKHXkJSzv/aUL8ox7YYKVjzzCfwNAYpK/img.png&quot; data-alt=&quot;인파님 블로그 이미지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Nd9Cl/btsKHXkJSzv/aUL8ox7YYKVjzzCfwNAYpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNd9Cl%2FbtsKHXkJSzv%2FaUL8ox7YYKVjzzCfwNAYpK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;522&quot; height=&quot;223&quot; data-origin-width=&quot;636&quot; data-origin-height=&quot;272&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;인파님 블로그 이미지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Young 영역에서 오래 살아남은 객체는 Old 영역으로 Promotion됨을 확인할 수 있었다. 그리고 Major GC는&amp;nbsp;객체들이 계속 Promotion되어 Old 영역의 메모리가 부족해지면 발생하게 된다. Young 영역은 일반적으로 Old 영역보다 크키가 작기 때문에 GC가 보통 0.5초에서 1초 사이에 끝난다. 그렇기 때문에 Minor GC는 애플리케이션에 크게 영향을 주지 않는다. 하지만 Old 영역은 Young 영역보다 크며 Young 영역을 참조할 수도 있다. 그렇기 때문에 Major GC는 일반적으로 Minor GC보다 시간이 오래걸리며, 10배 이상의 시간을 사용한다. 참고로 Young 영역과 Old 영역을 동시하 처리하는 GC는&amp;nbsp;Full GC라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;요약&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;816&quot; data-origin-height=&quot;182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEdAUS/btsKH0n04A1/MT1L6vx1CI5WLzkO7K10e0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEdAUS/btsKH0n04A1/MT1L6vx1CI5WLzkO7K10e0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEdAUS/btsKH0n04A1/MT1L6vx1CI5WLzkO7K10e0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEdAUS%2FbtsKH0n04A1%2FMT1L6vx1CI5WLzkO7K10e0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;592&quot; height=&quot;132&quot; data-origin-width=&quot;816&quot; data-origin-height=&quot;182&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 자료&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://mangkyu.tistory.com/118&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://mangkyu.tistory.com/118&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EA%B0%80%EB%B9%84%EC%A7%80-%EC%BB%AC%EB%A0%89%EC%85%98GC-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%F0%9F%92%AF-%EC%B4%9D%EC%A0%95%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EA%B0%80%EB%B9%84%EC%A7%80-%EC%BB%AC%EB%A0%89%EC%85%98GC-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%F0%9F%92%AF-%EC%B4%9D%EC%A0%95%EB%A6%AC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://d2.naver.com/helloworld/1329&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://d2.naver.com/helloworld/1329&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Backend/Java</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>kimdozzi</author>
      <guid isPermaLink="true">https://kimdozzi.tistory.com/299</guid>
      <comments>https://kimdozzi.tistory.com/299#entry299comment</comments>
      <pubDate>Thu, 14 Nov 2024 14:22:43 +0900</pubDate>
    </item>
    <item>
      <title>volatile 이란?</title>
      <link>https://kimdozzi.tistory.com/298</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Volatile&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;'Java 변수를 main memory에 저장하겠다' 라는 것을 명시하는 것&lt;/li&gt;
&lt;li&gt;매번 변수의 값을 read할 때 마다 cpu cache에 저장된 값이 아닌 메인 메모리에서 읽는 것&lt;/li&gt;
&lt;li&gt;변수의 값을 write할 때 마다 main memory에 까지 작성하는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;volatile 변수를 사용하고 있지 않는 멀티스레드 어플리케이션에서는 task를 수행하는 동안 성능 향상을 위해 메인 메모리에서 읽은 변수 값을 cpu cache에 저장하게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;602&quot; data-origin-height=&quot;515&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCMDIz/btsKGkNkDGc/kPV0nwDtizhDw1szY6eAE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCMDIz/btsKGkNkDGc/kPV0nwDtizhDw1szY6eAE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCMDIz/btsKGkNkDGc/kPV0nwDtizhDw1szY6eAE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCMDIz%2FbtsKGkNkDGc%2FkPV0nwDtizhDw1szY6eAE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;444&quot; height=&quot;380&quot; data-origin-width=&quot;602&quot; data-origin-height=&quot;515&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Example&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sharedObject를 공유하는 두 개의 스레드가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스레드 1은 카운터 값을 더하고 읽는 연산을 한다. (read &amp;amp; write)&lt;/li&gt;
&lt;li&gt;스레드 2는 카운터 값을 읽기만 한다. (only read)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1731424239728&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class SharedObject {
	public int counter = 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스레드 1은 카운터 값을 증가시키고 있지만, cpu cache에 반영되어 있고 실제로 메인 메모리에는 반영이 되지 않는다. 그렇기 때문에 스레드2는 카운터 값을 계속 읽어오지만 0을 가져오게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;550&quot; data-origin-height=&quot;466&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZBGlf/btsKFj2E9KB/gHyreti8MVOH8XuU4VHuQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZBGlf/btsKFj2E9KB/gHyreti8MVOH8XuU4VHuQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZBGlf/btsKFj2E9KB/gHyreti8MVOH8XuU4VHuQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZBGlf%2FbtsKFj2E9KB%2FgHyreti8MVOH8XuU4VHuQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;415&quot; height=&quot;352&quot; data-origin-width=&quot;550&quot; data-origin-height=&quot;466&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그러면 어떻게 해결할 수 있을까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Volidate 키워드를 추가하게 되면 main memory에 저장하고 읽어오기 때문에 변수 값 불일치 문제를 해결할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1731424319494&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class SharedObject {
    public volatile int counter = 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;언제 volatile이 적합할까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀티 스레드 환경에서 하나의 스레드만 read &amp;amp; write하고 나머지 스레드가 read하는 상황에서 가장 최신의 값을 보장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;항상 volatile이 옳을까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스레드-1이 값을 읽어 1을 추가하는 연산을 진행한다. (아직 메인 메모리 반영 전)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스레드-2이 값을 읽어 1을 추가하는 연산을 진행한다. (아직 메인 메모리 반영 전)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두개의 스레드가 1을 추가하는 연산을 하여 최종결과가 2여야하지만 각각 결과를 메인 메모리에 반영하게 된다면 1만 남은 상황이 발생한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;855&quot; data-origin-height=&quot;723&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blExxv/btsKFg5TeBz/8KBVoul3k5dH0UkNLxRCj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blExxv/btsKFg5TeBz/8KBVoul3k5dH0UkNLxRCj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blExxv/btsKFg5TeBz/8KBVoul3k5dH0UkNLxRCj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblExxv%2FbtsKFg5TeBz%2F8KBVoul3k5dH0UkNLxRCj0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;514&quot; height=&quot;435&quot; data-origin-width=&quot;855&quot; data-origin-height=&quot;723&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 스레드가 아닌 여러 스레드가 write하는 상황에선 적합하지 않다. 여러 스레드가 write하는 상황이라면 synchronized를 통해 변수 read &amp;amp; write의 원자성을 보장해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;volatile 성능에 어떤 영향이 있나요?&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;volatile는 변수의 read와 write를 메인 메모리에서 진행하게 된다. cpu cache보다 메인 메모리가 비용이 더 크기 때문에 변수 값 일치를 보장해야 하는 경우에만 사용하는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실습&lt;/h2&gt;
&lt;pre id=&quot;code_1733229800307&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Main {
	private static volatile boolean flag = false;
	private static int counter = 0;

	public static void main(String[] args) {
		// 데이터를 쓰는 스레드
		Thread writerThread = new Thread(() -&amp;gt; {
			try {
				Thread.sleep(1000); // 1초 대기
				System.out.println(&quot;Writer thread is setting flag to true&quot;);
				flag = true;
				System.out.println(&quot;Flag has been set to true&quot;);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		});

		// 데이터를 읽는 스레드
		Thread readerThread = new Thread(() -&amp;gt; {
			System.out.println(&quot;Reader thread is waiting for flag&quot;);
			while (!flag) {
				counter++; // flag가 false인 동안 계속 반복
			}
			System.out.println(&quot;Reader thread detected flag change!&quot;);
			System.out.println(&quot;Loop executed &quot; + counter + &quot; times&quot;);
		});

		// 스레드 시작
		readerThread.start();
		writerThread.start();

		// 메인 스레드에서 두 스레드가 종료될 때까지 대기
		try {
			readerThread.join();
			writerThread.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과&lt;/p&gt;
&lt;pre id=&quot;code_1733229824554&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Reader thread is waiting for flag
Writer thread is setting flag to true
Flag has been set to true
Reader thread detected flag change!
Loop executed 556426940 times&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 volatile을 사용하지 않았다면 readerThread가 flag의 변경을 감지하지 못했을 것이며, 루프가 무한히 계속되었을 것이다. 이는 각 스레드가 CPU 캐시에서 값을 읽기 때문이다. 위와 같은 결과는 volatile이 멀티스레드 환경에서 변수의 가시성을 보장한다는 것을 보여주는 예시다. 하지만, 위에서 언급했듯이 멀티 스레드 환경에서 항상 정답은 아니다. 원자성을 보장하지 못하므로 AtomicInteger나 synchronized 키워드 등 다른 대안을 선택해야 한다. Spring Boot에서 동기화 메커니즘이 다양한 것으로 알고 있다. 앞으로 다양한 동기화 방식을 알아보겠다 !!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;정리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;volatile&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Main Memory에 read &amp;amp; write를 보장하는 키워드&lt;/li&gt;
&lt;li&gt;하나의 Thread가 write하고 나머지 Thread가 읽는 상황인 경우&lt;/li&gt;
&lt;li&gt;변수의 값이 최신의 값으로 읽어와야 하는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의할 점?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;성능에 영향이 어느 정도 영향을 줄 수 있는 Point라는 점&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 자료&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://nesoy.github.io/articles/2018-06/Java-volatile&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://nesoy.github.io/articles/2018-06/Java-volatile&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1731424516578&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Java volatile이란?&quot; data-og-description=&quot; &quot; data-og-host=&quot;nesoy.github.io&quot; data-og-source-url=&quot;https://nesoy.github.io/articles/2018-06/Java-volatile&quot; data-og-url=&quot;https://nesoy.github.io/articles/2018-06/Java-volatile&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/chryye/hyXwv8QaW1/2kVgXM3Jjo2TpQBWkjw7Fk/img.jpg?width=1900&amp;amp;height=800&amp;amp;face=0_0_1900_800,https://scrap.kakaocdn.net/dn/3djfg/hyXwpVCQar/QKfKKL0FYUNT7UVIQLBHvK/img.png?width=507&amp;amp;height=430&amp;amp;face=0_0_507_430,https://scrap.kakaocdn.net/dn/jIFkU/hyXwh4lS1M/Kzm8WOiTd85rreqbSo4gAk/img.png?width=454&amp;amp;height=386&amp;amp;face=0_0_454_386&quot;&gt;&lt;a href=&quot;https://nesoy.github.io/articles/2018-06/Java-volatile&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://nesoy.github.io/articles/2018-06/Java-volatile&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/chryye/hyXwv8QaW1/2kVgXM3Jjo2TpQBWkjw7Fk/img.jpg?width=1900&amp;amp;height=800&amp;amp;face=0_0_1900_800,https://scrap.kakaocdn.net/dn/3djfg/hyXwpVCQar/QKfKKL0FYUNT7UVIQLBHvK/img.png?width=507&amp;amp;height=430&amp;amp;face=0_0_507_430,https://scrap.kakaocdn.net/dn/jIFkU/hyXwh4lS1M/Kzm8WOiTd85rreqbSo4gAk/img.png?width=454&amp;amp;height=386&amp;amp;face=0_0_454_386');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Java volatile이란?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;nesoy.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Backend/Java</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>kimdozzi</author>
      <guid isPermaLink="true">https://kimdozzi.tistory.com/298</guid>
      <comments>https://kimdozzi.tistory.com/298#entry298comment</comments>
      <pubDate>Wed, 13 Nov 2024 00:15:27 +0900</pubDate>
    </item>
    <item>
      <title>폴링과 인터럽트</title>
      <link>https://kimdozzi.tistory.com/297</link>
      <description>&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;폴링&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 주기마다 스레드를 돌면서 시그널이 들어왔는지 확인하는 방식이다. (소프트웨어적으로 시그널을 확인하는 것) 이렇게 특정 주기마다 계속 확인해야 하기 때문에 시스템의 리소스를 많이 먹는다는 단점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;인터럽트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램 실행 도중에 예기치 않은 상황이 발생할 경우 현재 실행 중인 작업을 즉시 중단하고, 발생된 상황에 대한 우선 처리가 필요함을 CPU에게 알리는 것 (하드웨어적으로 시그널을 확인하는 것) 시그널이 들어온 정확한 타이밍을 알 수 있고, 반응 시간이 빠르며 시스템 부하가 적다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;인터럽트 처리 과정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램 실행 -&amp;gt; 인터럽트 요청 -&amp;gt; 프로그램 중단(수행중인 명령을 완료하고 PC, SR 저장) -&amp;gt; ISR 처리(인터럽트 벡터를 통하여 주소 접근) -&amp;gt; 프로그램 재시작(PC를 통해 접근)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면, 인터럽트가 발생했을 때 수행중이던 프로세스의 정보는 어디로 가는걸까? 진행 중이던 작업의 내용은 커널(운영체제 중 메모리에 상주하여 일을 처리하는 역할)의 자료 구조에 저장된다. 이 자료 구조를 우리는 PCB라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;동시에 발생한다면?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선순위 기반 처리 =&amp;gt; 인터럽트 우선순위가 따라 처리된다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;우선순위 결정 요소
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인터럽트의 중요도&lt;/li&gt;
&lt;li&gt;처리의 긴급선&lt;/li&gt;
&lt;li&gt;요구되는 응답 시간&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;처리 방식
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;중첩 허용: 처리 중인 인터럽트보다 우선순위가 높은 인터럽트 발생 시 현재 처리 중단하고 새로운 인터럽트를 먼저 처리한다. (중첩 금지: 한 번에 하나의 인터럽트만 처리)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;H/W 인터럽트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 장치에서 발생하는 인터럽트.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) 키보드/마우스 입력, 타이머 인터럽트, 전원 이상&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;S/W 인터럽트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램 내부에서 발생하는 인터럽트.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) 시스템 콜, 프로그램 오류, 예외 상황(0으로 나누기, 오버플로우)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영체제(공룡책)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/gyoogle/tech-interview-for-developer/blob/master/Computer%20Science/Operating%20System/Interrupt.md&quot;&gt;https://github.com/gyoogle/tech-interview-for-developer/blob/master/Computer%20Science/Operating%20System/Interrupt.md&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@pppp0722/%EC%9D%B8%ED%84%B0%EB%9F%BD%ED%8A%B8Interrupt%EB%9E%80&quot;&gt;https://velog.io/@pppp0722/%EC%9D%B8%ED%84%B0%EB%9F%BD%ED%8A%B8Interrupt%EB%9E%80&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://itwiki.kr/w/%EC%9D%B8%ED%84%B0%EB%9F%BD%ED%8A%B8&quot;&gt;https://itwiki.kr/w/%EC%9D%B8%ED%84%B0%EB%9F%BD%ED%8A%B8&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Computer Science/Operating System</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>kimdozzi</author>
      <guid isPermaLink="true">https://kimdozzi.tistory.com/297</guid>
      <comments>https://kimdozzi.tistory.com/297#entry297comment</comments>
      <pubDate>Sat, 9 Nov 2024 23:11:53 +0900</pubDate>
    </item>
    <item>
      <title>시스템 콜</title>
      <link>https://kimdozzi.tistory.com/296</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;시스템 콜이 무엇인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템 콜은 응용 프로그램이 직접적으로접근하지 못하는 하드웨어 자원에 접근하기 위해 사용되는 인터페이스이다. 즉, 시스템 콜을 호출함으로서 해당 자원에 간접적으로 접근하게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;우리가 사용하는 시스템 콜 예시와 동작 과정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;python의 import sys, time도 시스템 콜이고, 대표적으로 file I/O의 open, read, write 등이 있다. 한 파일에서 다른 파일로 데이터를 복사하는 프로그램을 예로 들어서 시스템 콜이 동작하는 과정을 살펴 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시스템 콜 예시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 아래 명령어와 같이, 복사할 input 파일과 output 파일의 이름을 작성한다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;$ cp input.txt output.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 프로그램은 input 파일을 open(system call)하고, output 파일을 create(system call), open(system call)한다. 이때, 읽을 파일이 존재하지 않거나, 이미 output 파일이 존재한다면 에러를 반환하고 종료한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3. 프로그램은 input 파일에서 데이터를 read(system call)하고, output 파일에 write(system call)한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;4. 프로그램은 input 파일과 output 파일을 close(system call)한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;시스템 콜 동작 과정&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;1. 사용자 프로그램 실행: 먼저, 사용자가 작성한 프로그램이 사용자 모드에서 실행된다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;233&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cRTbck/btsKzI8245y/uDSEfxz5hwzLjGHrnE6P00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cRTbck/btsKzI8245y/uDSEfxz5hwzLjGHrnE6P00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cRTbck/btsKzI8245y/uDSEfxz5hwzLjGHrnE6P00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcRTbck%2FbtsKzI8245y%2FuDSEfxz5hwzLjGHrnE6P00%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;537&quot; height=&quot;182&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;233&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;2. 시스템 콜 호출&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 사용자 모드에서 커널 모드 전환: 아래 그림과 같이, 시스템 콜이 호출되면 프로그램은 현재 실행 중인 사용자 모드에서 커널 모드로 전환된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 요청 처리 및 수행, 결과 반환: 내부적으로 요청 분석 및 처리 작업을 수행하고, 결과를 메모리에 저장하거나 레지스터에 반환한다. (예를 들어, 파일을 읽는 작업의 경우 파일 내용을 메모리에 읽어온다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 커널 모드에서 사용자 모드로 전환: 요청된 작업이 완료되면, 운영체제는 다시 사용자 모드로 전환된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;운영체제의 Dual Mode와 구분하는 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영체제는 사용자 모드와 커널 모드로 나뉘게 된다. 사용자 모드에서는 커널 모드에 직접적으로 접근하지 못하고, 시스템 콜을 통해 사용자 모드에서 요청하면, 커널 모드에서 요청된 작업을 처리 후 결과를 반환하게 된다. 핵심적인 기능은 커널모드에서 모두 제어하며, 사용자 모드에서 발생한 문제는 커널 모드에 영향을 끼치지 않게 되면서 시스템 자원을 안전하게 관리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서로 다른 시스템 콜을 구분할 수 있을까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 시스템 콜에는 번호가&amp;nbsp; 할당되어 있어서 서로 다른 시스템 콜을 구별할 수 있다. 시스템 콜 인터페이스는 이러한 번호에 따라 인덱스 테이블을 관리하고 서로 다른 시스템 콜을 구분한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 자료&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://suminii.tistory.com/entry/System-Call&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://suminii.tistory.com/entry/System-Call&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.omin.dev/posts/system-call&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.omin.dev/posts/system-call&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%8B%9C%EC%8A%A4%ED%85%9C_%ED%98%B8%EC%B6%9C&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ko.wikipedia.org/wiki/%EC%8B%9C%EC%8A%A4%ED%85%9C_%ED%98%B8%EC%B6%9C&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://namu.wiki/w/%EC%8B%9C%EC%8A%A4%ED%85%9C%20%EC%BD%9C&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://namu.wiki/w/%EC%8B%9C%EC%8A%A4%ED%85%9C%20%EC%BD%9C&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://brightstarit.tistory.com/13&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://brightstarit.tistory.com/13&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Computer Science/Operating System</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>kimdozzi</author>
      <guid isPermaLink="true">https://kimdozzi.tistory.com/296</guid>
      <comments>https://kimdozzi.tistory.com/296#entry296comment</comments>
      <pubDate>Thu, 7 Nov 2024 23:06:24 +0900</pubDate>
    </item>
  </channel>
</rss>