<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>공부한거 기록하는 블로그</title>
    <link>https://whyhard.tistory.com/</link>
    <description>공부한거 기록하는 블로그입니다!!</description>
    <language>ko</language>
    <pubDate>Tue, 11 Aug 2026 10:11:59 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>SpaceCowboy</managingEditor>
    <image>
      <title>공부한거 기록하는 블로그</title>
      <url>https://tistory1.daumcdn.net/tistory/4708778/attach/c19ec585b46e491d85613a63d464374e</url>
      <link>https://whyhard.tistory.com</link>
    </image>
    <item>
      <title>객체지향 기초 개념 정리</title>
      <link>https://whyhard.tistory.com/63</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;article id=&quot;98b8fd9c-4730-4c6d-a4e6-2efc39047381&quot; class=&quot;page sans&quot;&gt;&lt;header&gt;
&lt;p class=&quot;page-title&quot; data-ke-size=&quot;size14&quot;&gt;참고&lt;/p&gt;
&lt;/header&gt;
&lt;div class=&quot;page-body&quot;&gt;
&lt;ul id=&quot;de773aaf-5420-4e73-b3d0-c821f706081e&quot; class=&quot;bulleted-list&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Object_(computer_science)&quot;&gt;https://en.wikipedia.org/wiki/Object_(computer_science)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;596b5a5f-aedc-4217-947f-75d0acb6d6f8&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;7b0f2fb2-5b4a-4711-bcbe-d0faf686f365&quot; class=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;객체란?&lt;/h3&gt;
&lt;p id=&quot;5f8d3462-cee9-4ec8-b928-ab628afc7fe0&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;객체 지향 프로그래밍에서 객체가 뜻하는건 무엇일까요?&lt;/p&gt;
&lt;ul id=&quot;105369a3-7934-4cd0-b948-bec260b0001f&quot; class=&quot;bulleted-list&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;보통 객체지향에서 객체는 변수, 함수, 데이터 구조의 혼합을 뜻합니다. C언어의 Structure에서 메서드가 추가되었다고 생각하면 편합니다. (엄밀히 따지면 더 복잡하지만)&lt;/li&gt;
&lt;/ul&gt;
&lt;ul id=&quot;042b687d-8dfa-4b1a-824c-4234134fc184&quot; class=&quot;bulleted-list&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;특히 Class 기반의 객체 지향 프로그래밍 언어에서 Class에 의해 생성된 Instance를 나타냅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;67011f0a-c255-49da-bdcd-81cb7fd6ccb4&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;85046677-07da-47af-9669-89643bc32024&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;b2e2648e-fbe1-43ce-9893-998a13dedbfa&quot; class=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;객체지향프로그래밍이란?&lt;/h3&gt;
&lt;p id=&quot;3100ff1d-0781-46ae-96d5-517118a7d37d&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;그러면 객체 지향 프로그래밍은 또 뭘까요?&lt;/p&gt;
&lt;ul id=&quot;81258499-d4ab-4000-a636-481678a4fa1d&quot; class=&quot;bulleted-list&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;OOP는 현실세계의 객체들을 모델링하고 그런 객체들끼리 상호작용하는 프로그램을 개발하는 패러다임입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul id=&quot;6d64c8ac-9a19-4ff4-821c-c7fd7cb39ac6&quot; class=&quot;bulleted-list&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;객체들의 상태와 행동을 캡슐화해서 코드의 재사용성, 유지보수성, 확장성을 높이는 걸 목적으로 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul id=&quot;85f1bfad-124d-4af7-b28c-dd162279dcbe&quot; class=&quot;bulleted-list&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;따라서, 객체 지향 프로그래밍은 객체들을 중심으로 코드를 구성하여 높은 추상화 수준을 제공하고 코드의 구조화와 관리를 용이하게 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;b78c4557-6aee-40db-ac98-3baefa836ab1&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;83ae90bf-8461-4626-93c9-02eec276e496&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;cbaf7336-2eb2-41d7-a77f-1f110bcf0055&quot; class=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;객체지향의 특징&lt;/h3&gt;
&lt;ul id=&quot;e24f01d3-a4c7-4af6-95a8-9fe084a59464&quot; class=&quot;toggle&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;details open=&quot;&quot;&gt;
&lt;summary&gt;&lt;b&gt;추상화 (Abstraction)&lt;/b&gt;&lt;/summary&gt;
&lt;ul id=&quot;43d6853a-6fe4-4ec3-bcea-00180ae52ea3&quot; class=&quot;bulleted-list&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;네이버 지식백과에서 설명하는 추상화 - &lt;a href=&quot;https://terms.naver.com/entry.naver?docId=3607505&amp;amp;cid=58598&amp;amp;categoryId=59316&quot;&gt;https://terms.naver.com/entry.naver?docId=3607505&amp;amp;cid=58598&amp;amp;categoryId=59316&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul id=&quot;52b9e673-f80e-48b7-bd97-0d953437bee3&quot; class=&quot;bulleted-list&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;객체지향프로그래밍에서 추상화는 복잡한 현실 세계의 개념을 단순화하고 모델링하는 것을 의미합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul id=&quot;07021e38-9b28-40bf-8e68-44ef3d390191&quot; class=&quot;bulleted-list&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;즉, 객체들의 공통된 속성과 행동을 추출해 상위 개념을 정의하고, 이를 기반으로 하위 개념들을 생성하는 과정입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;bd3a31f4-65a0-4c03-8000-caa94913acc2&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;다음은 TypeScript에서 추상화를 구현한 예제입니다.&lt;/p&gt;
&lt;pre id=&quot;37a22d8a-abe5-471f-b682-5ccaba861a2e&quot; class=&quot;code typescript&quot;&gt;&lt;code&gt;interface Vehicle {
  speed: number;
  move(): void;
}

class Car implements Vehicle {
  speed: number;
  constructor(speed: number) {
    this.speed = speed;
  }
  move(): void {
    console.log(&quot;The car is moving at &quot; + this.speed + &quot; km/h.&quot;);
  }
}

class Bicycle implements Vehicle {
  speed: number;
  constructor(speed: number) {
    this.speed = speed;
  }
  move(): void {
    console.log(&quot;The bicycle is moving at &quot; + this.speed + &quot; km/h.&quot;);
  }
}

const car = new Car(100);
const bicycle = new Bicycle(20);

car.move(); // The car is moving at 100 km/h.
bicycle.move(); // The bicycle is moving at 20 km/h.&lt;/code&gt;&lt;/pre&gt;
&lt;p id=&quot;51f5da1c-2dcf-490e-8351-5bb94cb1cbad&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;위 예시에서 &lt;code&gt;Vehicle&lt;/code&gt;이라는 인터페이스를 선언하고, &lt;code&gt;Car&lt;/code&gt; 클래스와 &lt;code&gt;Bicycle&lt;/code&gt; 클래스가 이를 구현하고 있습니다. &lt;code&gt;Vehicle&lt;/code&gt; 인터페이스는 &lt;code&gt;speed&lt;/code&gt; 속성과 &lt;code&gt;move&lt;/code&gt; 메서드를 정의하고 있으며, 이 두 속성과 메서드는 &lt;code&gt;Car&lt;/code&gt; 클래스와 &lt;code&gt;Bicycle&lt;/code&gt; 클래스에서 모두 구현되고 있습니다.&lt;/p&gt;
&lt;p id=&quot;238280af-654f-4bc9-9894-711da560c69e&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;이처럼 인터페이스를 통해 공통된 속성과 행동을 추상화하고, 이를 구현한 클래스들에서 공통된 코드를 재사용함으로써, 소프트웨어의 유지보수성과 확장성을 높일 수 있습니다.&lt;/p&gt;
&lt;/details&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul id=&quot;2537137f-3c95-4424-89db-7ac16cacee4d&quot; class=&quot;toggle&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;details open=&quot;&quot;&gt;
&lt;summary&gt;&lt;b&gt;상속 (Inheritance)&lt;/b&gt;&lt;/summary&gt;
&lt;ul id=&quot;370e49e6-8aa6-47db-a450-f87ab4180235&quot; class=&quot;bulleted-list&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;객체 지향 프로그래밍에서 상속은 부모 클래스가 가지고 있는 속성과 메서드를 자식 클래스에서 물려받아 사용할 수 있는 개념입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul id=&quot;9aec928e-05e2-4819-b7f0-b675144049de&quot; class=&quot;bulleted-list&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;자식 클래스는 부모 클래스의 기능을 그대로 상속받을 뿐 아니라, 필요에 따라 새로운 속성과 메서드를 추가하거나, 부모 클래스의 기능을 재정의할 수도 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul id=&quot;89ffd59f-17a9-48da-9f3e-5d6dbe2e8540&quot; class=&quot;bulleted-list&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;상속은 클래스 간의 계층 구조를 만들어 코드의 재사용성을 높이고, 클래스 간의 관계를 명확하게 해줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;c0f39b2b-f628-4d3d-bc71-0d6d6af13e27&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;82eac592-6fcf-4ce3-8518-3c060beb75eb&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;TypeScript에서 상속을 사용한 예시 코드는 다음과 같습니다.&lt;/p&gt;
&lt;pre id=&quot;6dca02d5-7270-42e1-81d9-4d5ff39d4150&quot; class=&quot;code scala&quot;&gt;&lt;code&gt;class Vehicle {
  speed: number;
  constructor(speed: number) {
    this.speed = speed;
  }
  move(): void {
    console.log(&quot;The vehicle is moving at &quot; + this.speed + &quot; km/h.&quot;);
  }
}

class Car extends Vehicle {
  constructor(speed: number) {
    super(speed);
  }
}

class Bicycle extends Vehicle {
  constructor(speed: number) {
    super(speed);
  }
  move(): void {
    console.log(&quot;The bicycle is moving at &quot; + this.speed + &quot; km/h.&quot;);
  }
}

const car = new Car(100);
const bicycle = new Bicycle(20);

car.move(); // The vehicle is moving at 100 km/h.
bicycle.move(); // The bicycle is moving at 20 km/h.&lt;/code&gt;&lt;/pre&gt;
&lt;p id=&quot;1357b2c9-9070-4fe0-8680-635a7d843438&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Car&lt;/code&gt; 클래스와 &lt;code&gt;Bicycle&lt;/code&gt; 클래스 모두 공통적인 특성을 가지고 있습니다. 속도나 움직이는 행동이 있습니다. 따라서 &lt;code&gt;Vehicle&lt;/code&gt; 클래스를 정의해서 &lt;code&gt;speed&lt;/code&gt;라는 속성을 정의하고 &lt;code&gt;move&lt;/code&gt;라는 메서드를 정의했습니다. 그리고 &lt;code&gt;Car&lt;/code&gt;와 &lt;code&gt;Bicycle&lt;/code&gt; 클래스가 상속을 받도록 해서 중복되는 특성을 편하게 받을 수 있도록 했습니다.&lt;/p&gt;
&lt;/details&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul id=&quot;b3e61c15-182a-43f5-bc4e-2cad493f0238&quot; class=&quot;toggle&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;details open=&quot;&quot;&gt;
&lt;summary&gt;&lt;b&gt;다형성 (Polymorphism)&lt;/b&gt;&lt;/summary&gt;
&lt;ul id=&quot;c275b5d4-deb2-49c9-ab80-e89ee191cb56&quot; class=&quot;bulleted-list&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;객체 지향 프로그래밍에서 다형성(Polymorphism)은 같은 이름의 메서드나 연산자가 다른 클래스의 인스턴스에서 다르게 동작하는 능력을 말합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul id=&quot;b4925cae-33b0-4fc5-b516-e0b195e7a6f3&quot; class=&quot;bulleted-list&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;즉, 하나의 인터페이스나 추상 클래스를 여러 클래스가 구현하고, 이를 사용하는 코드에서는 여러 클래스의 객체를 동일한 타입으로 처리할 수 있는 능력입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;fb8e1a52-8ce7-4235-b672-6cd622ab4af4&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;다형성을 이용하면, 코드의 재사용성과 확장성을 높일 수 있습니다. 예를 들어, 다양한 도형(원, 삼각형, 사각형 등)이 있을 때, 각 도형의 면적을 계산하는 메서드를 구현할 때, 다형성을 이용하면 여러 도형 클래스에서 이 메서드를 구현할 수 있습니다. 이렇게 하면, 코드의 재사용성이 높아지며, 새로운 도형 클래스를 추가해도 기존 코드의 수정이 필요 없습니다.&lt;/p&gt;
&lt;p id=&quot;d8025b07-40fa-4c19-b98c-14d2c2bc606b&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;TypeScript에서는 인터페이스나 추상 클래스를 이용하여 다형성을 구현할 수 있습니다. 다음은 TypeScript에서 다형성을 구현한 예시 코드입니다.&lt;/p&gt;
&lt;pre id=&quot;3b996459-fcce-42ed-b161-e61243d3f8a9&quot; class=&quot;code typescript&quot;&gt;&lt;code&gt;interface Shape {
  getArea(): number;
}

class Rectangle implements Shape {
  width: number;
  height: number;
  constructor(width: number, height: number) {
    this.width = width;
    this.height = height;
  }
  getArea(): number {
    return this.width * this.height;
  }
}

class Circle implements Shape {
  radius: number;

  constructor(radius: number) {
    this.radius = radius;
  }

  getArea(): number {
    return Math.PI * this.radius * this.radius;
  }
}

function printArea(shape: Shape): void {
  console.log(&quot;The area is &quot; + shape.getArea());
}

const rectangle = new Rectangle(10, 20);
const circle = new Circle(5);

printArea(rectangle); // The area is 200
printArea(circle); // The area is 78.53981633974483&lt;/code&gt;&lt;/pre&gt;
&lt;p id=&quot;88f5a141-768f-41c6-89f6-406aebd6c1c0&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;위 코드에서는 &lt;code&gt;Shape&lt;/code&gt; 인터페이스를 정의하고, &lt;code&gt;Rectangle&lt;/code&gt; 클래스와 &lt;code&gt;Circle&lt;/code&gt; 클래스가 이를 구현하고 있습니다. 이 때, 각 클래스에서는 &lt;code&gt;getArea&lt;/code&gt; 메서드를 구현하고 있습니다. &lt;code&gt;printArea&lt;/code&gt; 함수는 &lt;code&gt;Shape&lt;/code&gt; 타입을 매개변수로 받으며, 이를 구현한 &lt;code&gt;Rectangle&lt;/code&gt; 클래스와 &lt;code&gt;Circle&lt;/code&gt; 클래스의 인스턴스를 모두 전달할 수 있습니다. 이렇게 하면, &lt;code&gt;printArea&lt;/code&gt; 함수에서는 각 도형 클래스에서 구현한 &lt;code&gt;getArea&lt;/code&gt; 메서드가 동일한 이름으로 호출되며, 다형성이 구현됩니다.&lt;/p&gt;
&lt;/details&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul id=&quot;3e224526-802d-44ff-9283-1a44f9b890ba&quot; class=&quot;toggle&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;details open=&quot;&quot;&gt;
&lt;summary&gt;&lt;b&gt;캡슐화 (Encapsulation)&lt;/b&gt;&lt;/summary&gt;
&lt;ul id=&quot;ad77cc1e-7ab6-4bf1-9301-9e4e67b7955e&quot; class=&quot;bulleted-list&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;객체 지향 프로그래밍에서 캡슐화(Encapsulation)란, 데이터와 데이터를 처리하는 메서드를 하나의 묶음으로 묶는 것을 말합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul id=&quot;e7156a8a-7fb1-49e3-b9f1-21537f229408&quot; class=&quot;bulleted-list&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;이를 통해, 데이터에 직접 접근하는 것을 제한하고, 데이터와 메서드를 함께 캡슐화된 객체의 인터페이스를 통해 사용하도록 하는 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;347e148b-b5b5-4185-a8f5-8424eb9d8787&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;캡슐화를 이용하면, 객체의 내부 구현을 외부로부터 숨길 수 있어 정보은닉(Information Hiding)이 가능해집니다. 이를 통해, 객체 간의 상호작용이 명확하게 정의될 수 있으며, 객체의 내부 구현이 변경되더라도 외부에서 영향을 받지 않도록 할 수 있습니다.&lt;/p&gt;
&lt;p id=&quot;010c4a2e-d175-408a-941d-e59f377b7667&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;TypeScript에서는 클래스를 이용하여 캡슐화를 구현할 수 있습니다. 클래스의 속성에는 접근 제어자(Access Modifier)를 이용하여 외부에서의 접근을 제어할 수 있습니다. 다음은 TypeScript에서 캡슐화를 구현한 예시 코드입니다.&lt;/p&gt;
&lt;pre id=&quot;975b57a6-fecd-4a31-93fb-e21a49ac6fef&quot; class=&quot;code typescript&quot;&gt;&lt;code&gt;class BankAccount {
  private balance: number;

  constructor(initialBalance: number) {
    this.balance = initialBalance;
  }

  public deposit(amount: number): void {
    this.balance += amount;
  }

  public withdraw(amount: number): void {
    if (amount &amp;lt;= this.balance) {
      this.balance -= amount;
    } else {
      console.log(&quot;Insufficient balance&quot;);
    }
  }

  public getBalance(): number {
    return this.balance;
  }
}

const account = new BankAccount(1000);
console.log(account.getBalance()); // 1000

account.deposit(500);
console.log(account.getBalance()); // 1500

account.withdraw(1000);
console.log(account.getBalance()); // 500

// Cannot access 'balance' because it is private.
// account.balance = 2000;&lt;/code&gt;&lt;/pre&gt;
&lt;p id=&quot;f2dda997-6b1d-436f-82a1-c35b3639e831&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;위 코드에서는 &lt;code&gt;BankAccount&lt;/code&gt; 클래스가 정의되어 있습니다. 이 클래스에는 &lt;code&gt;balance&lt;/code&gt; 속성이 private 접근 제어자로 선언되어 있습니다. 이렇게 하면, &lt;code&gt;balance&lt;/code&gt; 속성에는 클래스 외부에서 직접 접근할 수 없습니다. 대신에, &lt;code&gt;deposit&lt;/code&gt;, &lt;code&gt;withdraw&lt;/code&gt;, &lt;code&gt;getBalance&lt;/code&gt; 메서드를 이용하여 &lt;code&gt;balance&lt;/code&gt; 속성에 간접적으로 접근할 수 있습니다. 이렇게 하면, &lt;code&gt;balance&lt;/code&gt; 속성의 값을 클래스 외부에서 보호할 수 있습니다.&lt;/p&gt;
&lt;/details&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;53e20d0c-17dc-407b-9839-3e9e892d1a4e&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/article&gt;</description>
      <category>Computer Science/객체지향</category>
      <category>OOP</category>
      <category>객체지향</category>
      <author>SpaceCowboy</author>
      <guid isPermaLink="true">https://whyhard.tistory.com/63</guid>
      <comments>https://whyhard.tistory.com/63#entry63comment</comments>
      <pubDate>Thu, 2 Mar 2023 21:56:18 +0900</pubDate>
    </item>
    <item>
      <title>자바스크립트 자주 쓰는 메서드 정리</title>
      <link>https://whyhard.tistory.com/62</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 일 할 때 자주 쓰던 메서드들을 정리해봤습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Notion Export 기능이 안먹혀서 일단 링크로 대신 올립니다.&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://shrouded-laugh-949.notion.site/2cee52f117584d78b57e6c43c4a4449d&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;자바스크립트 자주 쓰는 메서드 정리&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>프로그래밍 언어/JavaScript</category>
      <category>메서드</category>
      <category>자바스크립트</category>
      <author>SpaceCowboy</author>
      <guid isPermaLink="true">https://whyhard.tistory.com/62</guid>
      <comments>https://whyhard.tistory.com/62#entry62comment</comments>
      <pubDate>Thu, 2 Mar 2023 21:43:58 +0900</pubDate>
    </item>
    <item>
      <title>Nest.js에서 TypeORM Configuration 하는 방법</title>
      <link>https://whyhard.tistory.com/61</link>
      <description>&lt;h3&gt;참고&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.nestjs.com/techniques/database#async-configuration&quot;&gt;https://docs.nestjs.com/techniques/database#async-configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/myvoltron/typeorm-configuration/tree/master/src&quot;&gt;https://github.com/myvoltron/typeorm-configuration/tree/master/src&lt;/a&gt;&lt;br/&gt;
&lt;br/&gt;

&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;개요&lt;/h3&gt;
&lt;p&gt;Nest.js 환경에서 환경변수를 로드할 때 &lt;code&gt;@nestjs/config&lt;/code&gt; 패키지를 사용합니다. 해당 패키지와 함께 TypeORM configuration 하는 방법을 알아봅니다.&lt;/p&gt;
&lt;p&gt;참고로 여기서 쓰이는 방법은 다른 패키지 쓸 때도 적용할 수 있습니다.&lt;br&gt;&lt;br/&gt;&lt;br&gt;&lt;br/&gt;&lt;/p&gt;
&lt;h3&gt;일단 환경변수 설정하기 feat. &lt;code&gt;ConfigModule&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;우선 환경변수 로드를 위해서 &lt;code&gt;ConfigModule&lt;/code&gt; 설정을 해야합니다. 저는 해당 모듈의 설정 파일을 &lt;code&gt;src/config/config.validator.ts&lt;/code&gt; 에 두었습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { ConfigModuleOptions } from &amp;#39;@nestjs/config/dist&amp;#39;;
import * as Joi from &amp;#39;joi&amp;#39;;

export const CONFIG_VALIDATOR: ConfigModuleOptions = {
  validationSchema: Joi.object({
    DB_HOST: Joi.string().required(),
    DB_PORT: Joi.number().required(),
    DB_USERNAME: Joi.string().required(),
    DB_PASSWORD: Joi.string().required(),
    DB_DATABASE: Joi.string().required(),
  }),
  isGlobal: true,
};&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;Joi&lt;/code&gt; 패키지를 사용해서 환경변수 검증을 할 수 있습니다. 그리고 &lt;code&gt;isGlobal&lt;/code&gt; 옵션을 &lt;code&gt;true&lt;/code&gt;로 설정해서 전역적으로 쓰일 수 있게 했습니다.&lt;/p&gt;
&lt;p&gt;그리고 &lt;code&gt;AppModule&lt;/code&gt;에 &lt;code&gt;ConfigModule&lt;/code&gt;을 imports 해줍니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { Module } from &amp;#39;@nestjs/common&amp;#39;;
import { ConfigModule } from &amp;#39;@nestjs/config&amp;#39;;
import { AppController } from &amp;#39;./app.controller&amp;#39;;
import { AppService } from &amp;#39;./app.service&amp;#39;;
import { CONFIG_VALIDATOR } from &amp;#39;./app/config/config.validator&amp;#39;;
import { CatsModule } from &amp;#39;./cats/cats.module&amp;#39;;

@Module({
  imports: [CatsModule, ConfigModule.forRoot(CONFIG_VALIDATOR)],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}&lt;/code&gt;&lt;/pre&gt;&lt;br/&gt;
&lt;br/&gt;

&lt;h3&gt;ConfigService를 통해 TypeORM Configuration 하기&lt;/h3&gt;
&lt;p&gt;이제 ConfigService를 주입받아서 TypeORM 설정을 해야합니다. 우선 저같은 경우에는 TypeORM 설정 파일을 &lt;code&gt;src\app\mysql\mysql-config.provider.ts&lt;/code&gt; 에 두었습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { Injectable } from &amp;#39;@nestjs/common&amp;#39;;
import { ConfigService } from &amp;#39;@nestjs/config&amp;#39;;
import { TypeOrmModuleOptions, TypeOrmOptionsFactory } from &amp;#39;@nestjs/typeorm&amp;#39;;

@Injectable()
export class MysqlConfigProvider implements TypeOrmOptionsFactory {
  constructor(private configService: ConfigService) {}

  createTypeOrmOptions(): TypeOrmModuleOptions {
    return {
      type: &amp;#39;mysql&amp;#39;,
      host: this.configService.get(&amp;#39;DB_HOST&amp;#39;),
      port: this.configService.get&amp;lt;number&amp;gt;(&amp;#39;DB_PORT&amp;#39;),
      username: this.configService.get(&amp;#39;DB_USERNAME&amp;#39;),
      password: this.configService.get(&amp;#39;DB_PASSWORD&amp;#39;),
      database: this.configService.get(&amp;#39;DB_DATABASE&amp;#39;),
      autoLoadEntities: true,
      synchronize: true,
      logging: true,
    };
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이제 마지막으로 AppModule에서 TypeOrmModule을 imports 하면됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { Module } from &amp;#39;@nestjs/common&amp;#39;;
import { ConfigModule } from &amp;#39;@nestjs/config&amp;#39;;
import { TypeOrmModule } from &amp;#39;@nestjs/typeorm&amp;#39;;
import { AppController } from &amp;#39;./app.controller&amp;#39;;
import { AppService } from &amp;#39;./app.service&amp;#39;;
import { CONFIG_VALIDATOR } from &amp;#39;./app/config/config.validator&amp;#39;;
import { MysqlConfigProvider } from &amp;#39;./app/mysql/mysql-config.provider&amp;#39;;
import { CatsModule } from &amp;#39;./cats/cats.module&amp;#39;;

@Module({
  imports: [
    CatsModule,
    ConfigModule.forRoot(CONFIG_VALIDATOR),
    TypeOrmModule.forRootAsync({
      useClass: MysqlConfigProvider,
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>프레임워크/TypeORM</category>
      <category>configuration</category>
      <category>nestjs</category>
      <category>TypeORM</category>
      <author>SpaceCowboy</author>
      <guid isPermaLink="true">https://whyhard.tistory.com/61</guid>
      <comments>https://whyhard.tistory.com/61#entry61comment</comments>
      <pubDate>Tue, 14 Feb 2023 23:40:18 +0900</pubDate>
    </item>
    <item>
      <title>TypeORM 0.3에서 Seeding 하기</title>
      <link>https://whyhard.tistory.com/60</link>
      <description>&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://typeorm-extension.tada5hi.net/guide/&quot;&gt;typeorm-extension docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://min-nine.tistory.com/80&quot;&gt;https://min-nine.tistory.com/80&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/tada5hi/typeorm-extension&quot;&gt;https://github.com/tada5hi/typeorm-extension&lt;/a&gt; &amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/myvoltron/typeorm-seeding&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/myvoltron/typeorm-seeding&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Seeder란?&lt;/h3&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;b&gt;권한 정보나 관리자 계정같은 데이터&lt;/b&gt;는 초기에 필요한 정보인데 이를 Seeder를 사용해서 미리 입력할 수 있습니다.&lt;/li&gt;
&lt;li&gt;테스트할 때 필요한 데이터들을 편리하게 채워넣을 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 셋업을 설명하기 전에, 보통 TypeORM Seeding을 위해서 구글링을 해보면 &lt;code&gt;typeorm-seeding&lt;/code&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://www.npmjs.com/package/typeorm-seeding&quot;&gt;여기&lt;/a&gt;에서 확인해보시면 마지막 업데이트가 3년전입니다.&lt;/li&gt;
&lt;li&gt;따라서 여전히 ormconfig를 사용해야함: &lt;b&gt;dataSource&lt;/b&gt; 사용이 좀 더 권장됩니다. 왜 ormconfig를 쓰면 안되는지는 &lt;a href=&quot;https://typeorm.io/changelog#breaking-changes-1&quot;&gt;여기&lt;/a&gt;에서 확인하실 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 제가 찾아본 패키지는 &lt;code&gt;typeorm-extension&lt;/code&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://www.npmjs.com/package/typeorm-extension&quot;&gt;https://www.npmjs.com/package/typeorm-extension&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;지속적으로 관리되고 있습니다. - 2023년 2월 12일 기준으로 한달 전까지 업데이트가 있었군요!&lt;/li&gt;
&lt;li&gt;TypeORM 공식문서에도 소개되어있습니다! - &lt;a href=&quot;https://typeorm.io/#extensions&quot;&gt;여기&lt;/a&gt;를 보시면 확인하실 수 있습니다. 신뢰도가 상승하는군요&amp;hellip;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;dataSource&lt;/b&gt;를 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;TypeORM Seeding 셋업 및 실행&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TypeORM &lt;a href=&quot;https://whyhard.tistory.com/59&quot;&gt;Migration&lt;/a&gt; 을 읽고 오셨다는 가정하에 진행됩니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;typeorm-extension&lt;/code&gt; 패키지를 통해 seeding 셋업을 진행해봅시다.&lt;/p&gt;
&lt;pre class=&quot;elm&quot;&gt;&lt;code&gt;npm install typeorm-extension --save&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;package.json&lt;/code&gt;의 &lt;code&gt;scripts&lt;/code&gt; 섹션에 다음 스크립트를 넣습니다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;seed&quot;: &quot;ts-node -r tsconfig-paths/register ./node_modules/typeorm-extension/dist/cli/index.js seed&quot;,&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;typeorm-extension&lt;/code&gt; cli의 경우에도 절대경로를 이해하지 못해서 &lt;code&gt;tsconfig-paths&lt;/code&gt; 패키지를 활용한 모습입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- typeorm-extension은 &lt;code&gt;DataSource&lt;/code&gt; 파일의 디폴트 경로로 루트 디렉토리의 &lt;code&gt;data-source.ts&lt;/code&gt; 파일을 찾습니다. 따로 지정할 수 있습니다.&lt;br /&gt;- typeorm-extension의 seed 명령어는 seeder 파일의 디폴트 경로를 &lt;code&gt;src/database/seeds/**/*{.ts,.js}&lt;/code&gt; 로 간주합니다. 따로 지정할 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;typeorm-extension 패키지도 typeorm의 dataSource를 사용하기 때문에 &lt;code&gt;data-source.ts&lt;/code&gt; 가 필요할거같네요. 루트 디렉토리에 추가했습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import { ConfigService } from '@nestjs/config';
import { config } from 'dotenv';
import { DataSource } from 'typeorm';

config();

const configService = new ConfigService();

export default new DataSource({
  type: 'mysql',
  host: configService.get('DB_HOST'),
  port: configService.get&amp;lt;number&amp;gt;('DB_PORT'),
  username: configService.get('DB_USERNAME'),
  password: configService.get('DB_PASSWORD'),
  database: configService.get('DB_DATABASE'),
  synchronize: false,
  entities: ['src/**/*.entity.ts'],
  migrations: ['src/database/migrations/*.ts'],
  migrationsTableName: 'migrations',
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 우리의 Cat Entity에 대한 초기 데이터 삽입을 위해서 &lt;code&gt;src/database/seeds&lt;/code&gt; 경로에 &lt;code&gt;cats.seeder.ts&lt;/code&gt; 파일을 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 파일에서 어떤 데이터를 넣을지 정의하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import { Cat } from 'src/cats/entities/cat.entity';
import { DataSource } from 'typeorm';
import { Seeder } from 'typeorm-extension';

export default class CatsSeeder implements Seeder {
  async run(dataSource: DataSource): Promise&amp;lt;any&amp;gt; {
    const repository = dataSource.getRepository(Cat);
    await repository.insert([
      {
        name: 'cat1',
        kind: 'cat',
      },
    ]);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보시다시피, typeorm-extension의 Seeder를 구현했습니다. 그리고 내부적으로 &lt;code&gt;DataSource&lt;/code&gt;를 주입받고 repository를 사용해서 실제 데이터를 입력하는 방식으로 진행된다는걸 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 터미널에서 다음 명령어를 실행합니다.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;npm run seed&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 비슷한 화면이 나오면 성공한겁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;Seeder%20d504cec83d7e465cab9196fbf7886841/1.png&quot;&gt;&lt;img src=&quot;Seeder%20d504cec83d7e465cab9196fbf7886841/1.png&quot; alt=&quot;&quot; /&gt;&lt;/a&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;실제로 데이터가 잘 들어갔는지 확인하기 위해, &lt;b&gt;MySQL Workbench&lt;/b&gt;로 확인해봤습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;Seeder%20d504cec83d7e465cab9196fbf7886841/2.png&quot;&gt;&lt;img src=&quot;Seeder%20d504cec83d7e465cab9196fbf7886841/2.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 들어갔네요.&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;85&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4uxkc/btrZboalDDs/LLVSkJwU80CwiWBKJnh1r1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4uxkc/btrZboalDDs/LLVSkJwU80CwiWBKJnh1r1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4uxkc/btrZboalDDs/LLVSkJwU80CwiWBKJnh1r1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4uxkc%2FbtrZboalDDs%2FLLVSkJwU80CwiWBKJnh1r1%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;688&quot; height=&quot;85&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;85&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;101&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dtFZ7X/btrY7MbXlub/8L2fEcQvx8ISb7xoDOxz01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dtFZ7X/btrY7MbXlub/8L2fEcQvx8ISb7xoDOxz01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dtFZ7X/btrY7MbXlub/8L2fEcQvx8ISb7xoDOxz01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdtFZ7X%2FbtrY7MbXlub%2F8L2fEcQvx8ISb7xoDOxz01%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;667&quot; height=&quot;101&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;101&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;747&quot; data-origin-height=&quot;178&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nhIl2/btrZbPZPyqs/O77HWjgKhXAxdU99YKIQgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nhIl2/btrZbPZPyqs/O77HWjgKhXAxdU99YKIQgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nhIl2/btrZbPZPyqs/O77HWjgKhXAxdU99YKIQgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnhIl2%2FbtrZbPZPyqs%2FO77HWjgKhXAxdU99YKIQgk%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;747&quot; height=&quot;178&quot; data-origin-width=&quot;747&quot; data-origin-height=&quot;178&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;랜덤 데이터 삽입하기 feat. Faker, Factory&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 과정에서는 우리가 임의로 정한 데이터만 삽입하는 과정이였고 랜덤한 데이터가 입력되게 하고 싶을 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;typeorm-extension에서는 factory라는 기능을 통해서 랜덤한 데이터를 입력할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;src/database/factories&lt;/code&gt; 디렉토리에 &lt;code&gt;cats.factory.ts&lt;/code&gt; 파일을 추가합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;typeorm-extension은 factory 파일의 디폴트 디렉토리를 &lt;code&gt;src/database/factories&lt;/code&gt;로 간주합니다. 따로 지정할 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import { Cat } from 'src/cats/entities/cat.entity';
import { setSeederFactory } from 'typeorm-extension';

export default setSeederFactory(Cat, (faker) =&amp;gt; {
  const cat = new Cat();
  cat.name = faker.name.fullName();
  cat.kind = faker.animal.cat();

  return cat;
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;cats.seeder.ts&lt;/code&gt; 의 코드를 수정합니다.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;import { Cat } from 'src/cats/entities/cat.entity';
import { DataSource } from 'typeorm';
import { Seeder, SeederFactoryManager } from 'typeorm-extension';

export default class CatsSeeder implements Seeder {
  async run(
    dataSource: DataSource,
    factoryManager: SeederFactoryManager,
  ): Promise&amp;lt;any&amp;gt; {
    const repository = dataSource.getRepository(Cat);
    // await repository.insert([
    //   {
    //     name: 'cat1',
    //     kind: 'cat',
    //   },
    // ]);

    const catsFactory = factoryManager.get(Cat);
    await catsFactory.saveMany(5);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;SeederFactoryManager&lt;/code&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;code&gt;get&lt;/code&gt; 메서드를 통해서 Entity에 관련된 factory를 얻습니다. 해당 Entity와 관련된 factory가 &lt;code&gt;src/database/factories&lt;/code&gt;에 존재해야합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;saveMany&lt;/code&gt; 메서드를 통해 원하는 개수만큼 랜덤 데이터를 입력할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 &lt;code&gt;npm run seed&lt;/code&gt; 명령어를 실행하고나서 DB를 보면 랜덤한 데이터가 생성되어 있는 것을 보실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;Seeder%20d504cec83d7e465cab9196fbf7886841/3.png&quot;&gt;&lt;img src=&quot;Seeder%20d504cec83d7e465cab9196fbf7886841/3.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5번부터 9번까지 랜덤하게 생성된것을 느낄 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그 외&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;typeorm-extension 공식문서를 보시면 아시겠지만 굳이 Seeding 뿐만 아니라 다양한 용도로 쓸 수 있습니다. 직접 확인해보세요&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프레임워크/TypeORM</category>
      <category>seeder</category>
      <category>seeding</category>
      <category>TypeORM</category>
      <category>typeorm0.3</category>
      <category>typeormseeding</category>
      <author>SpaceCowboy</author>
      <guid isPermaLink="true">https://whyhard.tistory.com/60</guid>
      <comments>https://whyhard.tistory.com/60#entry60comment</comments>
      <pubDate>Mon, 13 Feb 2023 23:47:30 +0900</pubDate>
    </item>
    <item>
      <title>TypeORM 0.3에서 migration 해보기</title>
      <link>https://whyhard.tistory.com/59</link>
      <description>&lt;h1&gt;Migration&lt;/h1&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://typeorm.io/using-cli&quot;&gt;https://typeorm.io/using-cli&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://typeorm.io/migrations#using-migration-api-to-write-migrations&quot;&gt;https://typeorm.io/migrations#using-migration-api-to-write-migrations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/typeorm/typeorm/blob/master/src/commands/MigrationRunCommand.ts&quot;&gt;https://github.com/typeorm/typeorm/blob/master/src/commands/MigrationRunCommand.ts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://chanyeong.com/blog/post/35&quot;&gt;https://chanyeong.com/blog/post/35&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@dev_leewoooo/TypeORM%EC%9D%98-built-in-migration-%EC%9D%B4%EC%9A%A9%ED%95%98%EA%B8%B0&quot;&gt;https://velog.io/@dev_leewoooo/TypeORM의-built-in-migration-이용하기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://wanago.io/2022/07/25/api-nestjs-database-migrations-typeorm/&quot;&gt;https://wanago.io/2022/07/25/api-nestjs-database-migrations-typeorm/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/myvoltron/typeorm-migration&quot;&gt;https://github.com/myvoltron/typeorm-migration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;migration이란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TypeORM에서 &lt;code&gt;synchronize: true&lt;/code&gt; 기능을 사용하게 되면 Entity의 수정사항을 자동으로 DB에 갱신시켜줍니다. 이 기능이 편한건 맞지만 간혹 특정 데이터를 아예 밀어버릴 수 있기 때문에 &lt;b&gt;production 레벨에서는 안전하지 않습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 보통 백엔드 관련 프레임워크에서는 Migration이라는 기능을 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TypeORM에서는 데이터베이스 스키마를 업데이트하는 sql 쿼리를 모은 하나의 파일을 migration이라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 섹션에서는 Nest.js 환경에서 typeorm을 쓸 때 migration을 어떻게 진행할 수 있는지 서술합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;migration을 위한 셋업&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 아래의 의존성들을 설치합니다. &lt;b&gt;ts-node&lt;/b&gt;와 &lt;b&gt;tsconfig-paths&lt;/b&gt;입니다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;npm install -D ts-node tsconfig-paths&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 루트 디렉토리에 &lt;code&gt;data-source.ts&lt;/code&gt; 라는 파일을 만들고 다음 코드를 작성합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import { ConfigService } from '@nestjs/config';
import { config } from 'dotenv';
import { DataSource } from 'typeorm';

config();

const configService = new ConfigService();

export default new DataSource({
  type: 'mysql',
  host: configService.get('DB_HOST'),
  port: configService.get&amp;lt;number&amp;gt;('DB_PORT'),
  username: configService.get('DB_USERNAME'),
  password: configService.get('DB_PASSWORD'),
  database: configService.get('DB_DATABASE'),
  synchronize: false,
  entities: ['src/**/*.entity.ts'],
  migrations: ['src/database/migrations/*.ts'],
  migrationsTableName: 'migrations',
});&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 이름이나 코드 내용은 어느 정도 커스텀하셔도 상관없습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 &lt;code&gt;package.json&lt;/code&gt;의 &lt;code&gt;scripts&lt;/code&gt;에 &lt;b&gt;TypeORM migration&lt;/b&gt; 관련 스크립트를 넣습니다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;&quot;scripts&quot;: {
    &quot;prebuild&quot;: &quot;rimraf dist&quot;,
    &quot;build&quot;: &quot;nest build&quot;,
    &quot;format&quot;: &quot;prettier --write \&quot;src/**/*.ts\&quot; \&quot;test/**/*.ts\&quot;&quot;,
    &quot;start&quot;: &quot;nest start&quot;,
    &quot;start:dev&quot;: &quot;nest start --watch&quot;,
    &quot;start:debug&quot;: &quot;nest start --debug --watch&quot;,
    &quot;start:prod&quot;: &quot;node dist/main&quot;,
        // typeorm 스크립트
    &quot;typeorm&quot;: &quot;ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js --dataSource ./data-source.ts&quot;,
    &quot;migration:create&quot;: &quot;ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:create ./src/database/migrations/Migration&quot;,
    &quot;migration:generate&quot;: &quot;npm run typeorm migration:generate ./src/database/migrations/Migration&quot;,
    &quot;migration:run&quot;: &quot;npm run typeorm  migration:run&quot;,
    &quot;migration:revert&quot;: &quot;npm run typeorm migration:revert&quot;,
    &quot;lint&quot;: &quot;eslint \&quot;{src,apps,libs,test}/**/*.ts\&quot; --fix&quot;,
    &quot;test&quot;: &quot;jest&quot;,
    &quot;test:watch&quot;: &quot;jest --watch&quot;,
    &quot;test:cov&quot;: &quot;jest --coverage&quot;,
    &quot;test:debug&quot;: &quot;node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand&quot;,
    &quot;test:e2e&quot;: &quot;jest --config ./test/jest-e2e.json&quot;
  },&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TypeORM의 &lt;a href=&quot;https://typeorm.io/migrations#running-and-reverting-migrations&quot;&gt;공식문서 설명&lt;/a&gt;과는 좀 다른 스크립트입니다. TypeORM CLI가 절대경로를 이해하지 못해서 공식문서대로 따라하다가는 can not found module 같은 에러를 맞닥뜨리게 될겁니다. 그래서 임의로 절대경로를 이해시키기 위해 &lt;code&gt;ts-node -r tsconfig-paths/register&lt;/code&gt; 가 들어간 것을 볼 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ts-config-paths 모듈에 관한 내용은 &lt;a href=&quot;https://www.npmjs.com/package/tsconfig-paths&quot;&gt;여기&lt;/a&gt;에서 확인하실 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼 여기서 &lt;code&gt;npm migration:generate&lt;/code&gt; 명령어를 실행해보시고 대충 아래 화면처럼 뜨면 성공한겁니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;1062&quot; data-origin-height=&quot;170&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mPtHN/btrYIcB9dkb/K7aukz9POkgHWRFBtqS3tk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mPtHN/btrYIcB9dkb/K7aukz9POkgHWRFBtqS3tk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mPtHN/btrYIcB9dkb/K7aukz9POkgHWRFBtqS3tk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmPtHN%2FbtrYIcB9dkb%2FK7aukz9POkgHWRFBtqS3tk%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;1062&quot; height=&quot;170&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;1062&quot; data-origin-height=&quot;170&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;1030&quot; data-origin-height=&quot;174&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oYAmN/btrYKvHaQmt/ea4VFC9M87emRHoCqM4qK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oYAmN/btrYKvHaQmt/ea4VFC9M87emRHoCqM4qK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oYAmN/btrYKvHaQmt/ea4VFC9M87emRHoCqM4qK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoYAmN%2FbtrYKvHaQmt%2Fea4VFC9M87emRHoCqM4qK0%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;1030&quot; height=&quot;174&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;1030&quot; data-origin-height=&quot;174&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;migration 해보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 처음에 다음 entity가 존재하고 DB에도 적용되어있다고 가정합니다.&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;import { BaseEntity } from 'src/app/base/base.entity';
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class Cat extends BaseEntity {
  @PrimaryGeneratedColumn()
  idx: number;

  @Column()
  name: string;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 고객의 요구로 (가정) &lt;code&gt;Cat&lt;/code&gt; 에 &lt;code&gt;kind&lt;/code&gt; 컬럼을 추가합니다.&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;import { BaseEntity } from 'src/app/base/base.entity';
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class Cat extends BaseEntity {
  @PrimaryGeneratedColumn()
  idx: number;

  @Column()
  name: string;

  @Column()
  kind: string;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에는 typeorm의 synchronize 기능을 통해 자동으로 변경되었겠지만 이번에는 synchronize 기능을 끄고 migration 방식으로 진행해봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 &lt;a href=&quot;https://www.notion.so/migration-eab9396bf04c48069a314255f212d611&quot;&gt;migration을 위한 셋업&lt;/a&gt; 과정을 잘 진행했다면 &lt;code&gt;npm run migration:generate&lt;/code&gt; 명령어를 터미널에서 실행했을 때 &lt;code&gt;src/database/migartions&lt;/code&gt; 디렉토리에 &lt;code&gt;{timestamp}-Migration.ts&lt;/code&gt;파일이 생성된 것을 보실 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;import { MigrationInterface, QueryRunner } from 'typeorm';

export class Migration1675868794851 implements MigrationInterface {
  name = 'Migration1675868794851';

  public async up(queryRunner: QueryRunner): Promise&amp;lt;void&amp;gt; {
    await queryRunner.query(
      `ALTER TABLE \`cat\` ADD \`kind\` varchar(255) NOT NULL`,
    );
  }

  public async down(queryRunner: QueryRunner): Promise&amp;lt;void&amp;gt; {
    await queryRunner.query(`ALTER TABLE \`cat\` DROP COLUMN \`kind\``);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;up은 &lt;code&gt;npm run migration:run&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;down은 &lt;code&gt;npm run migration:revert&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 &lt;code&gt;npm run migration:run&lt;/code&gt;을 실행하면 터미널에서 다음과 같이 진행될겁니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;1141&quot; data-origin-height=&quot;258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lcw1y/btrYJ0tTDzp/Okq4qEYbQ0jPwL71JKVKw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lcw1y/btrYJ0tTDzp/Okq4qEYbQ0jPwL71JKVKw1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lcw1y/btrYJ0tTDzp/Okq4qEYbQ0jPwL71JKVKw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flcw1y%2FbtrYJ0tTDzp%2FOkq4qEYbQ0jPwL71JKVKw1%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;1141&quot; height=&quot;258&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;1141&quot; data-origin-height=&quot;258&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 성공했다고 뜨면 migration을 완료한겁니다!&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;migration 파일 생성할 때 create vs generate&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아마 공식문서를 보면 migration 파일을 만드는 방법이 generate 말고도 create 방식이 있다는걸 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;npm run migration:create&lt;/code&gt; 명령어를 실행하면 다음과 같은 파일이 생성됩니다.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;// src/database/migrations/1675869805076-Migration.ts

import { MigrationInterface, QueryRunner } from 'typeorm';

export class Migration1675869805076 implements MigrationInterface {
  public async up(queryRunner: QueryRunner): Promise&amp;lt;void&amp;gt; {}

  public async down(queryRunner: QueryRunner): Promise&amp;lt;void&amp;gt; {}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;generate와는 달리 up과 down 메서드 부분에 내용이 아무것도 없습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;generate: 읽어들인 모든 entity 파일과 실제 데이터베이스를 비교해서 테이블을 추가 및 수정하는 migration 파일을 생성합니다.&lt;/li&gt;
&lt;li&gt;create: 빈 migration 파일을 생성합니다. 따라서 스스로 테이블을 수정시키는 sql 쿼리문을 작성해야합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;generate는 컬럼을 아예 &lt;a href=&quot;https://velog.io/@heumheum2/typeORM-Migration-%EC%9D%B4%EC%8A%88&quot;&gt;DROP 시키고 나서 재생성하는 쿼리문을 작성&lt;/a&gt;해줄 때가 종종 있습니다. 따라서 generate 방식을 쓸 땐 쿼리문을 확인해서 DROP하는 쿼리문이 있다면, 직접 모두 수동으로 작성하는 create 방식으로 진행하는게 좋습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;migration 파일 위치 src vs dist 비교&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import { ConfigService } from '@nestjs/config';
import { config } from 'dotenv';
import { DataSource } from 'typeorm';

config();

const configService = new ConfigService();

export default new DataSource({
  type: 'mysql',
  host: configService.get('DB_HOST'),
  port: configService.get&amp;lt;number&amp;gt;('DB_PORT'),
  username: configService.get('DB_USERNAME'),
  password: configService.get('DB_PASSWORD'),
  database: configService.get('DB_DATABASE'),
  synchronize: false,
  entities: ['src/**/*.entity.ts'],
  migrations: ['src/database/migrations/*.ts'],
  migrationsTableName: 'migrations',
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;migrations: ['src/database/migrations/*.ts']&lt;/code&gt; 부분에 의문이 갔던게, 저기를 dist 폴더 기준으로 하시는 분들도 좀 있던거 같습니다. (정확히는 기억 안남)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;src 폴더 기준으로 넣어야할지 dist 폴더 기준으로 넣을지 고민이 되어서 뭐가 좋을지 고민해봤는데 그냥 src 폴더 기준으로 넣는게 좋을거 같습니다. 다음은 chatGPT 답변을 기용한겁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 TypeORM이 개발 단계에서 사용하는 소스 코드를 기준으로 마이그레이션을 수행할 수 있기 때문입니다. dist 기준으로 설정하면 빌드 과정에서 생성된 파일을 기준으로 마이그레이션을 수행하기 때문에, 소스 코드와 빌드 결과가 맞지 않을 경우 문제가 발생할 수 있습니다.&lt;/p&gt;</description>
      <category>프레임워크/TypeORM</category>
      <category>Migration</category>
      <category>TypeORM</category>
      <author>SpaceCowboy</author>
      <guid isPermaLink="true">https://whyhard.tistory.com/59</guid>
      <comments>https://whyhard.tistory.com/59#entry59comment</comments>
      <pubDate>Thu, 9 Feb 2023 22:41:44 +0900</pubDate>
    </item>
    <item>
      <title>TypeORM에서 Custom Repository 쓰기</title>
      <link>https://whyhard.tistory.com/58</link>
      <description>&lt;p&gt;참고&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/74542474/how-to-create-custom-separate-file-repository-in-nestjs-9-with-typeorm-0-3-x&quot;&gt;https://stackoverflow.com/questions/74542474/how-to-create-custom-separate-file-repository-in-nestjs-9-with-typeorm-0-3-x&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;우선 기본적으로 다음과 같은 entity가 있다고 가정하고 진행하겠다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;import { Entity, Column, PrimaryGeneratedColumn } from &amp;#39;typeorm&amp;#39;;

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string; 
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;TypeORM 0.2.x 버전에서의 custom repository&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;import { EntityRepository, Repository } from &amp;quot;typeorm&amp;quot;;
import { User } from &amp;quot;./entity/user.entity&amp;quot;;

@EntityRepository(User)
export class UserRepository extends Repository&amp;lt;User&amp;gt;{}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;TypeORM에서 제공되는 &lt;code&gt;@EntityRepository&lt;/code&gt; 데코레이터로 간단하게 구현할 수 있다. &lt;/p&gt;
&lt;p&gt;다만, TypeORM 0.3.x 버전부터는 &lt;strong&gt;deprecated&lt;/strong&gt; 되었고 사용이 불가능하다. 따라서 위와 비슷하게 구현하기 위해서 여러가지 방법이 나오고 있다. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;TypeORM 0.3.x 버전에서의 custom repository&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;여러가지 방법으로 구현할 수 있겠지만 내가 본 것 중에서 그나마 간단하면서 기존의 &lt;code&gt;@EntityRepository&lt;/code&gt;를 쓴 custom repository와 비슷하게 구현할 수 있는 방법을 설명하겠다. &lt;/p&gt;
&lt;p&gt;우선 결론적으로 repository class는 다음과 같이 구현하면 된다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;import { Injectable } from &amp;#39;@nestjs/common&amp;#39;;
import { InjectRepository } from &amp;#39;@nestjs/typeorm&amp;#39;;
import { Repository } from &amp;#39;typeorm&amp;#39;;
import { UserEntity } from &amp;#39;./user.entity&amp;#39;;

@Injectable()
export class UserRepository extends Repository&amp;lt;User&amp;gt; {
    constructor(
        @InjectRepository(UserEntity)
        private readonly repository: Repository&amp;lt;User&amp;gt;
    ) {
        super(repository.target, repository.manager, repository.queryRunner);
    }

   // 원하는 메서드 알아서 작성
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이제 &lt;code&gt;UserRepository&lt;/code&gt;를 다른 Service class에서 쓰기 위해서는 Module에 등록을 해야한다. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;import { Module } from &amp;#39;@nestjs/common&amp;#39;;
import { TypeOrmModule } from &amp;#39;@nestjs/typeorm&amp;#39;;
import { User } from &amp;#39;./user.entity&amp;#39;;

@Module({
  imports: [TypeOrmModule.forFeature([User])], // here we provide the TypeOrm support as usual, specifically for our UserEntity in this case
  providers: [UserRepository], // 모듈의 providers 배열에 UserRepository class 등록 
  exports: [UserRepository] // 다른 모듈에서 쓸 수 있도록 exports 배열에 UserRepository class를 등록
})
export class UserModule {}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>프레임워크/TypeORM</category>
      <category>customrepository</category>
      <category>TypeORM</category>
      <category>typeorm3.0</category>
      <author>SpaceCowboy</author>
      <guid isPermaLink="true">https://whyhard.tistory.com/58</guid>
      <comments>https://whyhard.tistory.com/58#entry58comment</comments>
      <pubDate>Wed, 1 Feb 2023 22:03:01 +0900</pubDate>
    </item>
    <item>
      <title>TypeORM에서 Soft Delete 써보기</title>
      <link>https://whyhard.tistory.com/57</link>
      <description>&lt;article id=&quot;581845d4-00ed-4b55-b2e2-336d21ec4deb&quot; class=&quot;page sans&quot;&gt;
&lt;div class=&quot;page-body&quot;&gt;
&lt;p id=&quot;e7bcd928-fbac-4f4c-8f41-c2707f8537ed&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고&lt;/b&gt;&lt;/p&gt;
&lt;ul id=&quot;413452e2-6eac-45bc-93ba-ed200cf42e67&quot; class=&quot;bulleted-list&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;a href=&quot;https://typeorm.io/delete-query-builder#soft-delete&quot;&gt;https://typeorm.io/delete-query-builder#soft-delete&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul id=&quot;9e85c208-e09f-4a06-bbbb-62907e65479a&quot; class=&quot;bulleted-list&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;a href=&quot;https://github.com/myvoltron/typeorm-soft-delete&quot;&gt;https://github.com/myvoltron/typeorm-soft-delete&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;a4eb5b9c-0122-4504-8b5d-d63b0e59b85e&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;7d279675-3e49-47f0-bff9-2c1794bde85a&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Soft Delete란?&lt;/b&gt;&lt;/p&gt;
&lt;ul id=&quot;b2399fe5-c25f-4a94-ae4b-aa14528bd6ac&quot; class=&quot;bulleted-list&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;실제로 데이터를 삭제시키는 방법이 &lt;b&gt;Hard Delete&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul id=&quot;dece6966-023d-403a-bafb-3c9525f9acc2&quot; class=&quot;bulleted-list&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;삭제여부&lt;/b&gt;를 알 수 있는 컬럼에 삭제를 표현하는 방법이 &lt;b&gt;Soft Delete&lt;/b&gt;, 즉 실제로 삭제하지는 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;6b8163ac-170b-44e2-a1cb-0fcce4501d43&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;따라서 실제로 삭제시키지 않고 삭제된 것처럼 표현이 가능해서 Soft Delete된 &lt;b&gt;데이터가 남아있으니&lt;/b&gt; 이를 &lt;b&gt;활용&lt;/b&gt;할 수 있어서 &lt;b&gt;현업에서는 Soft Delete가 권장&lt;/b&gt;되는 분위기입니다.&lt;/p&gt;
&lt;p id=&quot;39e87a05-5330-4e12-822c-114e8a8d044d&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;7bd3abd0-a967-480f-ae79-69e49c5b4bfc&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;7c5f1964-9bfd-46aa-864e-adf759d7a712&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;TypeORM&lt;/b&gt;에서 &lt;b&gt;Soft Delete&lt;/b&gt;를 하는 방법&lt;/p&gt;
&lt;table id=&quot;82f4e1a2-d1e2-4726-afbf-a352c6590fca&quot; class=&quot;simple-table&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead class=&quot;simple-table-header&quot;&gt;
&lt;tr id=&quot;2739c175-cf76-4496-b7cc-9e2a539c7faf&quot;&gt;
&lt;th id=&quot;WIcJ&quot; class=&quot;simple-table-header-color simple-table-header&quot; style=&quot;width: 171.65625px;&quot;&gt;방법&lt;/th&gt;
&lt;th id=&quot;;H?D&quot; class=&quot;simple-table-header-color simple-table-header&quot; style=&quot;width: 365.65625px;&quot;&gt;장점&lt;/th&gt;
&lt;th id=&quot;vmz&amp;gt;&quot; class=&quot;simple-table-header-color simple-table-header&quot; style=&quot;width: 378.65625px;&quot;&gt;단점&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr id=&quot;e4134cb5-5c0d-4976-8881-497c0ef028a8&quot;&gt;
&lt;th id=&quot;WIcJ&quot; class=&quot;simple-table-header-color simple-table-header&quot; style=&quot;width: 171.65625px;&quot;&gt;직접 구현&lt;/th&gt;
&lt;td id=&quot;;H?D&quot; class=&quot;&quot; style=&quot;width: 365.65625px;&quot;&gt;1. 자기가 원하는 방식으로 soft delete를 구현할 수 있다.&lt;/td&gt;
&lt;td id=&quot;vmz&amp;gt;&quot; class=&quot;&quot; style=&quot;width: 378.65625px;&quot;&gt;1. 조회나 업데이트 같은 메서드를 쓸 때마다 soft delete 처리되었는지 확인하는 where절을 넣어야합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;92fd5fb7-0f35-4bc6-9774-553c78f8823f&quot;&gt;
&lt;th id=&quot;WIcJ&quot; class=&quot;simple-table-header-color simple-table-header&quot; style=&quot;width: 171.65625px;&quot;&gt;TypeORM의 Soft Delete&lt;/th&gt;
&lt;td id=&quot;;H?D&quot; class=&quot;&quot; style=&quot;width: 365.65625px;&quot;&gt;1. soft delete 처리되었는지 확인하는 where절을 자동으로 넣어줍니다. 2. soft delete된 데이터들을 복구시키는 메서드도 제공합니다. 3. soft delete된 데이터들도 같이 조회하는 기능도 제공합니다.&lt;/td&gt;
&lt;td id=&quot;vmz&amp;gt;&quot; class=&quot;&quot; style=&quot;width: 378.65625px;&quot;&gt;1. 무조건 &lt;code&gt;@DeleteDateColumn()&lt;/code&gt;방식으로만 soft delete가 구현됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p id=&quot;bca9e044-c4a0-4172-897e-18ece8889728&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;75e7a55f-011a-4bb7-86a0-a226599ff5cd&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;2633199b-756f-4026-ace4-3b8ce2d89a38&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;TypeORM의 Soft Delete 써보기&lt;/b&gt;&lt;/p&gt;
&lt;ul id=&quot;a996ea65-7fb5-4ab3-a02c-f9be88c7bd65&quot; class=&quot;toggle&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;details open=&quot;&quot;&gt;
&lt;summary&gt;빠르게 soft delete 써보기&lt;/summary&gt;
&lt;p id=&quot;25abe0b4-31f3-4fa6-8ba7-2c4b1f4a8e40&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;우선 entity에 &lt;code&gt;@DeleteDateColumn()&lt;/code&gt; 처리된 컬럼이 필요합니다.&lt;/p&gt;
&lt;pre id=&quot;071b6fcb-793a-4d25-9cf5-618474c20d14&quot; class=&quot;code typescript&quot;&gt;&lt;code&gt;import { CreateDateColumn, DeleteDateColumn, UpdateDateColumn } from 'typeorm';

export class BaseEntity {
  @CreateDateColumn()
  createdDate: Date;

  @UpdateDateColumn()
  modifiedDate: Date;

  @DeleteDateColumn() // soft delete를 하려면 필수
  deletedDate: Date;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;8893a1cf-77b4-40b7-8037-15268c6976d4&quot; class=&quot;code scala&quot;&gt;&lt;code&gt;import { BaseEntity } from 'src/app/base/base.entity';
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class Cat extends BaseEntity {
  @PrimaryGeneratedColumn()
  idx: number;

  @Column()
  name: string;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p id=&quot;2e85ed02-9de0-4ca1-94de-f178bfc711ff&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;BaseEntity&lt;/code&gt;를 정의해서 Cat Entity에서 extends했습니다.&lt;/p&gt;
&lt;p id=&quot;d1aa3470-e429-431a-b0ef-445e46296043&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;116777a8-a0d4-4e32-b29b-7f3095a7fa53&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;이제 &lt;code&gt;CatsService&lt;/code&gt;에서 &lt;code&gt;CatsRepository&lt;/code&gt;를 사용해서 soft delete를 할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;122f8c12-87ec-425d-903e-5ac6557e4729&quot; class=&quot;code reasonml&quot;&gt;&lt;code&gt;async softDelete(idx: number) {
    return this.catsRepository.softDelete(idx);
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul id=&quot;dc540d80-212c-47bf-b028-a4d65e727eaf&quot; class=&quot;toggle&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;details open=&quot;&quot;&gt;
&lt;summary&gt;내부 원리&lt;/summary&gt;
&lt;p id=&quot;9b04ab7c-754e-4d88-a77d-75a59d53acd5&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;TypeORM logging 기능을 켜서 실제로 어떤 쿼리문이 실행되는지 알아봅시다.&lt;/p&gt;
&lt;pre id=&quot;f6a476a6-db9a-4fea-b967-123979af71c1&quot; class=&quot;code n1ql&quot;&gt;&lt;code&gt;query: SELECT `Cat`.`createdDate` AS `Cat_createdDate`, `Cat`.`modifiedDate` AS `Cat_modifiedDate`, `Cat`.`deletedDate` AS `Cat_deletedDate`, `Cat`.`idx` AS `Cat_idx`, `Cat`.`name` AS `Cat_name` FROM `cat` `Cat` WHERE `Cat`.`deletedDate` IS NULL&lt;/code&gt;&lt;/pre&gt;
&lt;p id=&quot;674d965a-3561-45fa-bd13-7e0220484234&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;이게 조회를 했을 때 쿼리문입니다. 따로 Where절을 넣지않았음에도 deletedDate를 체크하는 모습입니다.&lt;/p&gt;
&lt;p id=&quot;2eaef098-3a7b-4e9d-a648-d5909845cc0a&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;6f48fd2d-ff82-4ad0-877f-085dcf1d2c22&quot; class=&quot;code n1ql&quot;&gt;&lt;code&gt;query: UPDATE `cat` SET `deletedDate` = CURRENT_TIMESTAMP, `modifiedDate` = CURRENT_TIMESTAMP WHERE `idx` IN (?) -- PARAMETERS: [3]&lt;/code&gt;&lt;/pre&gt;
&lt;p id=&quot;51c7ae85-e0da-46d1-aafb-a40f58dcda37&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;이게 soft delete를 했을 때 쿼리문입니다. &lt;code&gt;deleteDate&lt;/code&gt; 컬럼에 현재 TimeStamp를 넣는것을 볼 수 있습니다.&lt;/p&gt;
&lt;/details&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul id=&quot;f7b5c0d0-4c12-43e6-85b3-02ed03b34261&quot; class=&quot;toggle&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;details open=&quot;&quot;&gt;
&lt;summary&gt;데이터 복구&lt;/summary&gt;
&lt;p id=&quot;a60253bb-1987-4092-b872-616791a91bc4&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;soft delete 처리된 데이터들을 쉽게 복구시킬 수 있는 메서드가 제공됩니다.&lt;/p&gt;
&lt;pre id=&quot;2d7c14a4-a198-4418-a5a5-35388669fa34&quot; class=&quot;code cs&quot;&gt;&lt;code&gt;async restore(idx: number) {
    return this.catsRepository.restore({ idx });
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p id=&quot;e27aa07d-92b8-4402-8e7d-0134905e3ae0&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;restore 메서드를 통해서 soft delete 처리된 row 데이터를 복구시킬 수 있습니다.&lt;/p&gt;
&lt;p id=&quot;a1b026e7-60ea-4d2e-98d2-0e0a200f6ac1&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;62949d4b-c7fc-4e20-8187-9054cff39188&quot; class=&quot;code n1ql&quot;&gt;&lt;code&gt;query: UPDATE `cat` SET `deletedDate` = NULL, `modifiedDate` = CURRENT_TIMESTAMP WHERE `idx` = ? -- PARAMETERS: [3]&lt;/code&gt;&lt;/pre&gt;
&lt;p id=&quot;b00788bd-f8a6-4456-a2de-b4caae74c6f2&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;restore 메서드를 실행했을 때의 쿼리문입니다. &lt;code&gt;deletedDate&lt;/code&gt;를 &lt;code&gt;null&lt;/code&gt;처리 하는걸 볼 수 있습니다.&lt;/p&gt;
&lt;/details&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul id=&quot;4becfe62-99e2-405e-b51f-5b91caf94e67&quot; class=&quot;toggle&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;details open=&quot;&quot;&gt;
&lt;summary&gt;Soft Delete 처리된 데이터 함께 조회하기&lt;/summary&gt;
&lt;p id=&quot;f2a99a0a-e3b8-422f-9467-da77523ca965&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;find 옵션에서 &lt;code&gt;withDeleted&lt;/code&gt; 옵션 &lt;code&gt;true&lt;/code&gt;로 세팅하면 TypeORM의 soft delete 기능을 써서 처리한 row 데이터들도 함께 조회합니다.&lt;/p&gt;
&lt;pre id=&quot;462e2fc3-b46f-4a94-b1f5-71bcb8771f76&quot; class=&quot;code arduino&quot;&gt;&lt;code&gt;async findAllWithDeleted() {
    return this.catsRepository.find({ withDeleted: true });
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p id=&quot;4a5aed54-a35c-4ad8-9926-9d74101c13c0&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;8a67a218-7129-4900-aad3-0c326d17ddc1&quot; class=&quot;code n1ql&quot;&gt;&lt;code&gt;query: SELECT `Cat`.`createdDate` AS `Cat_createdDate`, `Cat`.`modifiedDate` AS `Cat_modifiedDate`, `Cat`.`deletedDate` AS `Cat_deletedDate`, `Cat`.`idx` AS `Cat_idx`, `Cat`.`name` AS `Cat_name` FROM `cat` `Cat`&lt;/code&gt;&lt;/pre&gt;
&lt;p id=&quot;e88b6c63-902f-433a-87c3-7d326adad665&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;withDeleted&lt;/code&gt;옵션을 썼을 때 &lt;code&gt;deletedDate&lt;/code&gt;컬럼을 체크하는 where 절이 사라진 것을 볼 수 있습니다.&lt;/p&gt;
&lt;p id=&quot;b6114dd9-eebf-4a5e-b724-05657bab884a&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/details&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;f5f92ccc-fc48-4528-a7fe-4b13f81273dd&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/article&gt;</description>
      <category>프레임워크/TypeORM</category>
      <category>nestjs</category>
      <category>softDelete</category>
      <category>TypeORM</category>
      <category>논리삭제</category>
      <author>SpaceCowboy</author>
      <guid isPermaLink="true">https://whyhard.tistory.com/57</guid>
      <comments>https://whyhard.tistory.com/57#entry57comment</comments>
      <pubDate>Sun, 29 Jan 2023 22:05:33 +0900</pubDate>
    </item>
    <item>
      <title>[자바스크립트 필기] #24 - 비동기 프로그래밍이란?</title>
      <link>https://whyhard.tistory.com/56</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;200&quot; data-origin-height=&quot;200&quot; data-filename=&quot;200px-Unofficial_JavaScript_logo_2.svg.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btCOn7/btrcONBcukI/cqRIpQpDrla82LWtaIxSX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btCOn7/btrcONBcukI/cqRIpQpDrla82LWtaIxSX1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btCOn7/btrcONBcukI/cqRIpQpDrla82LWtaIxSX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtCOn7%2FbtrcONBcukI%2FcqRIpQpDrla82LWtaIxSX1%2Fimg.png&quot; data-origin-width=&quot;200&quot; data-origin-height=&quot;200&quot; data-filename=&quot;200px-Unofficial_JavaScript_logo_2.svg.png&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&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;*이 필기는 &lt;a href=&quot;https://poiemaweb.com/&quot;&gt;웹 프로그래밍 튜토리얼&lt;/a&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;1. 동기 처리와 비동기 처리&amp;nbsp;&lt;/b&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;이 처럼 실행 컨텍스트 스택에 함수 실행 컨텍스트가 푸시되는 것은 함수 실행의 시작을 알리는 것이다. 그래서 함수의 실행 순서는 스택을 통해 관리되는데 문제는 이 스택이 하나밖에 없어서 2개 이상의 함수를 동시에 실행할 수 없다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 예제를 보자&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1629566472707&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// sleep 함수는 일정 시간 뒤에 func 함수를 실행한다. 
function sleep(func, delay) {
    const delayUntil = Date.now() + delay;
    while (Date.now() &amp;lt; delayUntil);

    // 일정 시간이 지나고 나서 콜백 함수를 호출한다. 
    func();
}

function foo() {
    console.log('foo');
}

function bar() {
    console.log('bar');
}

// sleep 함수는 3초 이상 실행된다. 
sleep(foo, 3 * 1000);
// sleep 함수가 스택에서 팝되어야 하므로 3초 이상 블록킹 된다. 
bar();
// (3초 경과 후) foo -&amp;gt; bar 호출&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;setTimeout 함수와 비슷하게 일정 시간이 지나면 함수를 호출하는 sleep 함수이다. 여기에서 3초가 지나면 foo 함수를 호출한다. foo함수와 sleep 함수가 스택에서 팝될때까지 bar함수는 3초 이상 호출되지 못하고 블로킹된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 현재 실행 중인 태스크가 종료할 때까지 다음에 실행될 태스크가 대기하는 방식을&amp;nbsp;&lt;b&gt;동기 처리&lt;/b&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;다음은 위 예제를 setTimeout으로 변형한 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1629566985798&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function foo() {
    console.log('foo');
}

function bar() {
    console.log('bar');
}

// 타이머 함수 setTimeout은 bar함수를 블로킹하지 않는다. 
setTimeout(foo, 3 * 1000);
bar();
// bar호출 -&amp;gt; (3초 후에)foo호출&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;setTimeout 함수는 앞의 sleep 함수와 비슷하게 일정 시간 뒤에 콜백 함수를 호출하지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;setTimeout 함수 이후의 태스크들은 블로킹하지 않고 곧바로 실행한다. 이처럼 현재 실행 중인 태스크가 종료되지 않은 상태라 해도 다음 태스크를 곧바로 실행하는 방식을&amp;nbsp;&lt;b&gt;비동기 처리&lt;/b&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;비동기 처리를 하는 함수의 예로는, 타이머 함수인 setTimeout / setInterval 함수 | HTTP 요청 | 이벤트 핸들러 등이 있다.&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;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>프로그래밍 언어/JavaScript</category>
      <category>자바스크립트필기</category>
      <author>SpaceCowboy</author>
      <guid isPermaLink="true">https://whyhard.tistory.com/56</guid>
      <comments>https://whyhard.tistory.com/56#entry56comment</comments>
      <pubDate>Sun, 22 Aug 2021 02:32:55 +0900</pubDate>
    </item>
    <item>
      <title>[자바스크립트 필기] #23 - 타이머</title>
      <link>https://whyhard.tistory.com/55</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;200&quot; data-origin-height=&quot;200&quot; data-filename=&quot;200px-Unofficial_JavaScript_logo_2.svg.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bS2EMX/btrcMVz3wjh/AriFhOrY8tgpvQkiWTDKk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bS2EMX/btrcMVz3wjh/AriFhOrY8tgpvQkiWTDKk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bS2EMX/btrcMVz3wjh/AriFhOrY8tgpvQkiWTDKk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbS2EMX%2FbtrcMVz3wjh%2FAriFhOrY8tgpvQkiWTDKk1%2Fimg.png&quot; data-origin-width=&quot;200&quot; data-origin-height=&quot;200&quot; data-filename=&quot;200px-Unofficial_JavaScript_logo_2.svg.png&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&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;*이 필기는 &lt;a href=&quot;https://poiemaweb.com/&quot;&gt;웹 프로그래밍 튜토리얼&lt;/a&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;1. 호출 스케줄링&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;자바스크립트에서는 타이머를 생성할 수 있는 setTimeout과 setInterval, 타이머를 제거할 수 있는 clearTimeout과 clearInterval을 제공한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 setTimeout과 setInterval은 비동기 처리방식으로 동작한다.&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;2. 타이머 함수&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;setTimeout / clearTimeout &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;setTimeout 함수는 ms단위로 받은 두번째 인수의 시간으로 첫번째 함수를 단 한번 실행하는 타이머를 생성한다.&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;첫 번째 매개변수 : 타이머가 만료된 뒤 호출될 콜백 함수&amp;nbsp;&lt;/li&gt;
&lt;li&gt;두 번째 매개변수 : 타이머 만료 시간(밀리초(ms) 단위)&lt;/li&gt;
&lt;li&gt;세 번째 매개변수 : 콜백 함수에 전달해야 할 인수가 있으면 세 번째 인수를 통해 전달할 수 있다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1629563779201&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1초(1000ms) 후 타이머가 만료되면 콜백 함수가 호출된다. 
setTimeout(() =&amp;gt; console.log('Hi!'), 1000);

// 1초(1000ms) 후 타이머가 만료되면 콜백 함수가 호출된다. 
setTimeout(name =&amp;gt; console.log(`Hi! ${name}`), 1000, 'Lee');

// 두 번째 인수를 생략하면 기본값 0이 지정된다. 
setTimeout(() =&amp;gt; console.log('Hello!'));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;setTimeout 함수는 생성된 타이머를 식별할 수 있는 고유한 타이머 id를 반환한다. 이것을 clearTimeout 함수에 전달해서 타이머를 취소할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1629563891227&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const timerId = setTimeout(() =&amp;gt; console.log('Hi!'), 1000);

clearTimeout(timerId);&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;&lt;b&gt;setInterval / clearInterval&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;setInterval 함수도 setTimeout 함수와 거의 비슷하다. 다만 setInterval 함수는 계속해서 반복 동작하는 타이머를 생성한다. 반환된 타이머 id를 통해 clearInterval 함수로 타이머를 취소시킬 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1629564102198&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let count = 1;

// 1초 후 타이머가 만료될 때마다 콜백 함수가 호출된다. 
// setInterval 함수는 생성된 타이머를 식별할 수 있는 고유한 타이머 id를 반환한다. 
const timeoutId = setInterval(() =&amp;gt; {
    console.log(count); // 1 2 3 4 5
    // count가 5이면 setInterval 함수가 반환한 타이머 id를 clearInterval 함수에 전달해서 
    // 타이머를 취소할 수 있다. 
    if(count++ === 5) clearInterval(timeoutId);
}, 1000);&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;여기까지 입니다 - 틀린 점이 있다면 꼭 지적해주세요!&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;</description>
      <category>프로그래밍 언어/JavaScript</category>
      <category>자바스크립트필기</category>
      <author>SpaceCowboy</author>
      <guid isPermaLink="true">https://whyhard.tistory.com/55</guid>
      <comments>https://whyhard.tistory.com/55#entry55comment</comments>
      <pubDate>Sun, 22 Aug 2021 01:42:05 +0900</pubDate>
    </item>
    <item>
      <title>[자바스크립트 필기] #22 - 이벤트</title>
      <link>https://whyhard.tistory.com/54</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;200&quot; data-origin-height=&quot;200&quot; data-filename=&quot;200px-Unofficial_JavaScript_logo_2.svg.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BI9ck/btrcvxffKIJ/iQm1kB7r8jrMkCyCNhAyLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BI9ck/btrcvxffKIJ/iQm1kB7r8jrMkCyCNhAyLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BI9ck/btrcvxffKIJ/iQm1kB7r8jrMkCyCNhAyLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBI9ck%2FbtrcvxffKIJ%2FiQm1kB7r8jrMkCyCNhAyLK%2Fimg.png&quot; data-origin-width=&quot;200&quot; data-origin-height=&quot;200&quot; data-filename=&quot;200px-Unofficial_JavaScript_logo_2.svg.png&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&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;*이 필기는 &lt;a href=&quot;https://poiemaweb.com/&quot;&gt;웹 프로그래밍 튜토리얼&lt;/a&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;1. 이벤트 드리븐 프로그래밍&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저는 처리해야할 특정 사건(클릭, 스크롤 등)이 발생했을 때 이를 감지해&amp;nbsp;&lt;b&gt;이벤트&lt;/b&gt;를 발생시킨다. 이러한 이벤트에 대응해서 호출하고 싶은 함수가 있을 것이다. 이런 이벤트가 발생했을 때 호출될 함수를 &lt;b&gt;이벤트 핸들러&lt;/b&gt;라고 한다. 그리고 브라우저에게 이벤트 핸들러가 호출될 수 있게 하는것을 이벤트 핸들러 등록이라고 한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1629289587982&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;button&amp;gt;Click me!&amp;lt;/button&amp;gt;
    &amp;lt;script&amp;gt;
        const $button = document.querySelector('button');

        // 사용자가 버튼을 클릭하면 함수를 호출하도록 요청 
        $button.onclick = () =&amp;gt; { alert('button click'); };
    &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&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;b&gt;이벤트 드리븐 프로그래밍&lt;/b&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;2. 이벤트 타입&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 타입은 이벤트의 종류를 나타내는 문자열이다. 에를 들어, 이벤트 타입 'click'은 사용자가 마우스 버튼을 클릭했을 때 발생하는 이벤트를 나타낸다.&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://developer.mozilla.org/ko/docs/Web/Events&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.mozilla.org/ko/docs/Web/Events&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1629290019519&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;이벤트 참조 | MDN&quot; data-og-description=&quot;DOM 이벤트는 발생한 흥미로운 것을 코드에 알리기 위해 전달됩니다. 각 이벤트는&amp;nbsp;Event 인터페이스를 기반으로한 객체에 의해 표현되며 발생한 것에 대한 부가적인 정보를 얻는데 사용되는 추가&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/Events&quot; data-og-url=&quot;https://developer.mozilla.org/ko/docs/Web/Events&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/61Ibq/hyLimWGdKY/4sVOqNeyJF0asDaZthl6pK/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/Events&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/Events&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/61Ibq/hyLimWGdKY/4sVOqNeyJF0asDaZthl6pK/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080');&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;이벤트 참조 | MDN&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;DOM 이벤트는 발생한 흥미로운 것을 코드에 알리기 위해 전달됩니다. 각 이벤트는&amp;nbsp;Event 인터페이스를 기반으로한 객체에 의해 표현되며 발생한 것에 대한 부가적인 정보를 얻는데 사용되는 추가&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.mozilla.org&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;&lt;b&gt;3. 이벤트 핸들러 등록&amp;nbsp;&lt;/b&gt;&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;&lt;b&gt;이벤트 핸들러 어트리뷰트 방식&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1629292034574&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;button onclick=&quot;sayHi('Lee')&quot;&amp;gt;Click me!&amp;lt;/button&amp;gt;
    &amp;lt;script&amp;gt;
        function sayHi(name) {
            console.log(`Hi! ${name}`);
        }
    &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML과 자바스크립트는 분리되어 있는게 좋기 때문에 권장되는 방법은 아니다.&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;이벤트 핸들러 프로퍼티 방식&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1629292646662&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;button&amp;gt;Click me!&amp;lt;/button&amp;gt;
    &amp;lt;script&amp;gt;
        const $button = document.querySelector('button');

        // 이벤트 핸들러 프로퍼티에 이벤트 핸들러를 바인딩 
        $button.onclick = function () {
            console.log('button click');
        };
    &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&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;이벤트의 종류를 나타내는 문자열인&amp;nbsp;&lt;b&gt;이벤트 타입&amp;nbsp;&lt;/b&gt;- 'click'&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이벤트 핸들러&amp;nbsp;&lt;/b&gt;- handleClick 함수&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;b&gt;addEventListenter 메서드 방식&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EventTarget.prototype.addEventListener 메서드를 사용하여 이벤트 핸들러를 등록할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 매개변수에는 이벤트의 종류를 나타내는 이벤트 타입을 전달한다. 위와는 다르게 접두사 'on'을 붙이지 않는다.&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;마지막 매개변수에는 이벤트 전파 단계를 지정한다. 생략하거나 false의 경우에는 버블링 단계에서 캐치하고 true의 경우에는 캡처링 단계에서 캐치한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1629293039018&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;button&amp;gt;Click me!&amp;lt;/button&amp;gt;
    &amp;lt;script&amp;gt;
        const $button = document.querySelector('button');

        $button.addEventListener('click', function () {
            console.log('button click');
        });
    &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&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;&lt;b&gt;4. 이벤트 핸들러 제거&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;addEventListener 메서드로 등록한 이벤트 핸들러를 제거하려면 EventTarget.prototype.removeEventListener 메서드를 사용한다. 전달한 인수가 동일해야 제거된다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1629294537221&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;button&amp;gt;Click me!&amp;lt;/button&amp;gt;
    &amp;lt;script&amp;gt;
        const $button = document.querySelector('button');

        const handleClick = () =&amp;gt; console.log('button click');

        // 이벤트 핸들러 등록 
        $button.addEventListener('click', handleClick);

        // 이벤트 핸들러 제거 
        // addEventListener 메서드에 전달한 인수와 removeEventListener 메서드에 
        // 전달한 인수가 일치하지 않으면 이벤트 핸들러가 제거되지 않는다. 
        $button.removeEventListener('click', handleClick, true); // 실패 
        $button.removeEventListener('click', handleClick); // 성공 
    &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&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;&lt;b&gt;5. 이벤트 객체&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트가 발생하면 이벤트에 관련된 다양한 정보를 담고 있는 이벤트 객체가 동적으로 생성된다. 생성된 이벤트 객체는 이벤트 핸들러의 첫 번째 인수로 전달된다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1629295543452&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;p&amp;gt;클릭하시오. 클릭한 곳의 좌표가 표시됩니다.&amp;lt;/p&amp;gt;
    &amp;lt;em class=&quot;message&quot;&amp;gt;&amp;lt;/em&amp;gt;
    &amp;lt;script&amp;gt;
        const $msg = document.querySelector('.message');

        // 클릭 이벤트에 의해 생성된 이벤트 객체는 이벤트 핸들러의 첫 번째 인수로 전달한다. 
        function showCoords(e) {
            $msg.textContent = `X: ${e.clientX}, Y: ${e.clientY}`;
        }

        document.onclick = showCoords;
    &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클릭 이벤트에 의해 생성된 이벤트 객체는 이벤트 핸들러의 첫 번째 인수로 전달되어 매개변수 e에 암묵적으로 할당된다. 브라우저가 이벤트 핸들러를 호출할 때 이벤트 객체를 인수로 전달하기 때문이다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;따라서 이벤트 객체를 받을 매개변수를 따로 선언해줘야한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;이름은 다른거로 사용해도 상관없다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;이벤트 객체의 공통 프로퍼티&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/Event&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.mozilla.org/ko/docs/Web/API/Event&lt;/a&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1629296995314&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Event - Web API | MDN&quot; data-og-description=&quot;Event 인터페이스는 DOM 내에 위치한 이벤트를 나타냅니다.&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/API/Event&quot; data-og-url=&quot;https://developer.mozilla.org/ko/docs/Web/API/Event&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bTuobg/hyLihaBIuK/XH9c5qgfyjvz3dWknAtlok/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/Event&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/API/Event&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bTuobg/hyLihaBIuK/XH9c5qgfyjvz3dWknAtlok/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080');&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;Event - Web API | MDN&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Event 인터페이스는 DOM 내에 위치한 이벤트를 나타냅니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.mozilla.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;마우스 정보 취득&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/MouseEvent&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.mozilla.org/ko/docs/Web/API/MouseEvent&lt;/a&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1629296537105&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;MouseEvent - Web API | MDN&quot; data-og-description=&quot;MouseEvent&amp;nbsp;인터페이스는 특정 지점을&amp;nbsp;가리키는 장치를 통해 발생하는 이벤트를 의미한다 (마우스 등). 주로 사용되는 이벤트로는 click, dblclick (en-US), mouseup (en-US), mousedown (en-US)가 있다.&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/API/MouseEvent&quot; data-og-url=&quot;https://developer.mozilla.org/ko/docs/Web/API/MouseEvent&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/4ifWa/hyLij0x3nq/VgmytwF4R98aQ2BTkj8CyK/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/MouseEvent&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/API/MouseEvent&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/4ifWa/hyLij0x3nq/VgmytwF4R98aQ2BTkj8CyK/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080');&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;MouseEvent - Web API | MDN&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;MouseEvent&amp;nbsp;인터페이스는 특정 지점을&amp;nbsp;가리키는 장치를 통해 발생하는 이벤트를 의미한다 (마우스 등). 주로 사용되는 이벤트로는 click, dblclick (en-US), mouseup (en-US), mousedown (en-US)가 있다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.mozilla.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&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;&lt;b&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1629296672559&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;KeyboardEvent - Web APIs | MDN&quot; data-og-description=&quot;KeyboardEvent objects describe a user interaction with the keyboard; each event describes a single interaction between the user and a key (or combination of a key with modifier keys) on the keyboard.&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent&quot; data-og-url=&quot;https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/buyps8/hyLidlIPBd/VsiBAskuXwOmSu4RKRrHDk/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/buyps8/hyLidlIPBd/VsiBAskuXwOmSu4RKRrHDk/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080');&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;KeyboardEvent - Web APIs | MDN&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;KeyboardEvent objects describe a user interaction with the keyboard; each event describes a single interaction between the user and a key (or combination of a key with modifier keys) on the keyboard.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.mozilla.org&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;&lt;b&gt;6. DOM 요소의 기본 동작 조작&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;DOM 요소의 기본 동작 중단&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;a요소를 클릭하면 href에 지정된 링크로 이동하고, checkbox 또는 radio 요소를 클릭하면 체크 또는 해제된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 객체의 preventDefault 메서드는 이러한 DOM 요소의 기본 동작을 중단시킨다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1629297275869&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;a href=&quot;https://www.google.com/&quot;&amp;gt;&amp;lt;/a&amp;gt;
    &amp;lt;script&amp;gt;
        document.querySelector('a').onclick = e =&amp;gt; {
            // a 요소의 기본 동작을 중단한다. 
            e.preventDefault();
        };
    &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&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;이벤트 객체의 stopPropagation 메서드는 이벤트 전파를 중단시킨다.&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;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>프로그래밍 언어/JavaScript</category>
      <category>자바스크립트필기</category>
      <author>SpaceCowboy</author>
      <guid isPermaLink="true">https://whyhard.tistory.com/54</guid>
      <comments>https://whyhard.tistory.com/54#entry54comment</comments>
      <pubDate>Wed, 18 Aug 2021 23:42:26 +0900</pubDate>
    </item>
  </channel>
</rss>