<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>스파킷 - SparkIT</title>
    <link>https://sparkit.tistory.com/</link>
    <description>IT 블로그</description>
    <language>ko</language>
    <pubDate>Fri, 15 May 2026 02:19:20 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>SparkIT</managingEditor>
    <image>
      <title>스파킷 - SparkIT</title>
      <url>https://tistory1.daumcdn.net/tistory/7406864/attach/5ff24f01b9fb4c91bf484a7611be27d7</url>
      <link>https://sparkit.tistory.com</link>
    </image>
    <item>
      <title>[MYSQL] Like 연산자와 %,_ 와일드카드를 이용한 검색 성능 개선하기(InnoDB)</title>
      <link>https://sparkit.tistory.com/95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스를 다루다 보면 &amp;ldquo;부분 검색&amp;rdquo;이 꼭 필요할 때가 있습니다.&lt;br /&gt;예를 들어 이름이 '김'으로 &lt;b&gt;시작하거나 포함된 사용자&lt;/b&gt;를 찾고 싶을 때 다음과 같은 쿼리를 자주 작성하죠.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;SELECT * FROM users WHERE name LIKE '%김%';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이런 쿼리는 굉장히 느리게 동작하여 시스템에 무리를 일으키기도 합니다. 인덱스도 걸어놨는데, 왜 느릴까요?&lt;br /&gt;이번 글에서는 MySQL의 LIKE 연산자와 와일들카드 그리고 인덱스가 어떻게 동작하는지 이해하고 이를 통해 어떻게 검색 성능을 개선할 수 있는지 여러 방법에 대해 고민해보겠습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기본 용어 설명&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 기본부터 짚고 갑시다.&lt;br /&gt;LIKE는 MySQL에서 &lt;b&gt;비교 연산자(Comparison Operator)&lt;/b&gt; 중 하나입니다.&lt;br /&gt;'=' 혹은 '&amp;gt;' 같은 비교 연사자처럼 &lt;b&gt;두 값을 비교해서 TRUE/FALSE를 반환&lt;/b&gt;합니다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;SELECT * FROM users WHERE name LIKE '김%';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 쿼리는 &quot;김&quot;으로 시작하는 문자열을 찾는 연산입니다.&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;LIKE와 함께 자주 쓰이는 특수 문자가 있습니다. 바로 &lt;b&gt;와일드 카드 문자&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;와일드카드 예시&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;%&lt;/td&gt;
&lt;td&gt;0개 이상의 임의의 문자&lt;/td&gt;
&lt;td&gt;'김%' &amp;rarr; &quot;김&quot;, &quot;김준&quot;, &quot;김민수&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;_&lt;/td&gt;
&lt;td&gt;정확히 1개의 임의의 문자&lt;/td&gt;
&lt;td&gt;'김_수' &amp;rarr; &quot;김민수&quot;, &quot;김진수&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열 패턴을 만들 때 이 둘을 조합해 다양한 조건을 걸 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;B+Tree 구조는 문자열에 대한 인덱스를 생성할때 문자열의 가장 왼쪽부터 이용해 정렬한다&lt;/h2&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;/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;MySQL&lt;/b&gt;의&lt;b&gt; InnoDB 엔진&lt;/b&gt;을 사용합니다. 해당 엔진에서는 &lt;b&gt;B+Tree 기반 인덱스 구조&lt;/b&gt;를 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B+Tree 인덱스는 문자열을 정렬 가능한 값으로 바꾼 뒤 트리 형태로 만듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B+Tree 구조에 대한 간단한 예시입니다. 다음과 같은 이름 데이터가 5개 존재한다고 가정합시다.&lt;/p&gt;
&lt;pre id=&quot;code_1761040322032&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;김강민
김정구
이민혁
백호민
최호찬&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이 데이터들은 'utf8mb4_general_ci' 으로 저장되어있다고 가정합니다. (utf8mb4_general_ci는 유니코드값을 기준으로 비교됩니다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 각 이름에 대한 유니코드값 정보입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1761040527832&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;김강민 --&amp;gt; [44608, 44053, 48188]
김정구 --&amp;gt; [44608, 51221, 44428]
이민혁 --&amp;gt; [51060, 48188, 54849]
백호민 --&amp;gt; [48353, 54840, 48188]
최호찬 --&amp;gt; [52572, 54840, 52268]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 이름을 이루고 있는 각각의 문자에 대한 유니코드값을 배열로 표현한 것입니다. 예로 '김강민'의 '김'이 44608, '강'이 44053, '민'이 48188이라는 유니코드값을 가진 것이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분이 중요합니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B+Tree 구조에서는 문자열 정보를 저장할 때 &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;즉, 5개의 이름 데이터가 아래와 같이 정렬된다는 것이죠.&lt;/p&gt;
&lt;pre id=&quot;code_1761367862820&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;                          [EA B9 80 ... 김*] ──┬── [EB B0 ... 백*] ──┬── [EC 9D ... 이*]
                                              │                     │
                                              ▼                     ▼
                              [김강민, 김정구]          [백호민]          [이민혁, 최호찬]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 두 가지 경우를 비교해봅시다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;경우1)&lt;/b&gt; '김'으로 시작하는 이름 찾기&lt;/li&gt;
&lt;li&gt;&lt;b&gt;경우2)&lt;/b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt; 중간에 '호'가 들어가는 이름 찾기&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;첫번째 경우에서는 루트에서 '김'으로 시작하는 브랜치로 바로 이동할 수 있습니다. 이를 통해 '김강민'을 알아낼 수 있죠. 그리고 리프 노드들은 모두 양방향 연결되어있기 때문에 '김강민' --&amp;gt; '김정구' --&amp;gt; '백호민' 순서로 이동하면서 검사합니다. 이때 '백호민'이라는 데이터는 조건을 만족하지 않기에 '김강민', '김정구' 데이터만 반환됩니다. 이 과정에서는 조건에 맞는 첫번째 데이터를 찾기 굉장히 쉽고, 조건을 만족하지 않을 때까지 오른쪽으로 계속 이동하기만 되기 때문에 빠르게 처리됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두번째 경우에서는 루트에서 특정 브랜치로 이동 자체가 불가합니다. 이름 중간에 '호'가 있는 데이터는 여러 브랜치에 있을 수 있기 때문이죠. 그래서 결국 모든 데이터를 순환해야합니다. 이 과정은 모든 데이터를 탐색하게 되므로 느릴 수 밖에 없죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;그럼 어떻게 조회(검색) 성능을 높일까?&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;상황에 따라 여러가지 방식을 활용할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;방법 1 ) LIKE 연산자 + '검색어%' 형식으로 제한하기&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;위에서 설명했듯이 '검색어%' 형식처럼 접두사 검색은 인덱스를 활용할 수 있기에 성능상 이점이 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;그러므로 시작하는 문자열을 모두 적어야 제대로 된 검색이 가능하도록 UX를 설계하는 것이 첫번째 방법입니다.&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;방법 2 ) FULLTEXT INDEX 활용하기&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;InnoDB에서는 FULLTEXT 인덱스를 지원하는데요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;이는 &lt;b&gt;단어&lt;/b&gt; 단위로 토큰화하여 검색할 수 있는 인덱스 구조입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;예를 들어 '안녕하세요. 저는 홍길동이라고 합니다' 라는 문자열을 저장한다면 '안녕하세요', '저는', '홍길동이라고', '합니다' 로 나누어 검색할 수 있다는 것입니다. '저는'이라는 단어를 검색하면 해당 문자열을 빠르게 조회할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;하지만 이런 인덱스는 여러 단어를 구성된 문장을 저장할 때 사용되면 좋은 구조입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;위에서 예시로 들었던 이름 검색같은 경우에서는 이점이 없습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;방법 3 ) REVERSE INDEX 활용하기&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;문자열을 거꾸로 뒤집고, 이에 대한 인덱스를 생성하는 것도 생각해볼 수 있는 방법 중 하나입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;예를 들어 한국어로 된 이름을 검색할 때, 성을 빼고 검색하는 경우가 많을 수 있습니다.('김정구' 검색 시 '정구'라는 이름만 가지고 검색)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;이런 경우에는 가장 마지막 문자열로부터 역으로 인덱스를 사용하면 위에서 설명했던 B+Tree 구조의 이점을 그대로 이용할 수 있어 성능 향상에 도움이 될 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;마무리&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 인덱스를 활용한 조회의 핵심은 &amp;ldquo;&lt;b&gt;정렬된 순서를 얼마나 잘 활용할 수 있느냐&lt;/b&gt;&amp;rdquo;에 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LIKE, FULLTEXT, REVERSE INDEX 모두 이 정렬 특성을 어떤 방향으로 사용할지를 달리한 전략일 뿐이죠.&lt;/p&gt;
&lt;p data-end=&quot;999&quot; data-start=&quot;932&quot; data-ke-size=&quot;size16&quot;&gt;검색 성능을 높이는 일은 결국 기술의 문제가 아니라, &lt;b&gt;데이터와 검색 의도를 정확히 이해하는 일&lt;/b&gt;에서 출발합니다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>데이터베이스, ORM</category>
      <category>%</category>
      <category>B+Tree</category>
      <category>InnoDB</category>
      <category>like</category>
      <category>MySQL</category>
      <category>검색</category>
      <author>SparkIT</author>
      <guid isPermaLink="true">https://sparkit.tistory.com/95</guid>
      <comments>https://sparkit.tistory.com/95#entry95comment</comments>
      <pubDate>Sat, 25 Oct 2025 14:24:59 +0900</pubDate>
    </item>
    <item>
      <title>[TypeORM] 데이터베이스 마이그레이션</title>
      <link>https://sparkit.tistory.com/94</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트 사용 시 TypeORM 공식 문서에 따른 명령어는 에러가 발생하여 사용하지 못한다.&lt;/p&gt;
&lt;figure id=&quot;og_1746715264601&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;Migrations | typeorm&quot; data-og-description=&quot;Once you get into production you'll need to synchronize model changes into the database. Typically, it is unsafe to use synchronize: true for schema synchronization on production once you get data in your database. Here is where migrations come to help. A &quot; data-og-host=&quot;orkhan.gitbook.io&quot; data-og-source-url=&quot;https://orkhan.gitbook.io/typeorm/docs/migrations&quot; data-og-url=&quot;https://orkhan.gitbook.io/typeorm/docs/migrations&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/KIpd4/hyYPpNxdMF/m64ICurz3Nv3V4OPVvo6qK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/ezswhZ/hyYM2lf9Yc/e2qwXgQMZsD4502fC1WKA0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://orkhan.gitbook.io/typeorm/docs/migrations&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://orkhan.gitbook.io/typeorm/docs/migrations&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/KIpd4/hyYPpNxdMF/m64ICurz3Nv3V4OPVvo6qK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/ezswhZ/hyYM2lf9Yc/e2qwXgQMZsD4502fC1WKA0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&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;Migrations | typeorm&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Once you get into production you'll need to synchronize model changes into the database. Typically, it is unsafe to use synchronize: true for schema synchronization on production once you get data in your database. Here is where migrations come to help. A&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;orkhan.gitbook.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식문서 상 명령어&lt;/p&gt;
&lt;pre id=&quot;code_1746715255582&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npx typeorm-ts-node-esm migration:generate ./src/migrations/update-post-table -d ./src/data-source.ts&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정한 명령어&lt;/p&gt;
&lt;pre id=&quot;code_1746716719443&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install --save-dev ts-node

npm install --save-dev typescript @types/node&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1746715289958&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npx ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:generate src/migrations/migration -d src/data-source.ts&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 migration:generate 뒤에 오는 파라미터는 경로라고 나오는데 이상한게 마지막 경로는 이름에 넣어짐.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) src/migrations/migration&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--&amp;gt; 마이그레이션된 파일이 src/migrations/ 하위에 생기고 '숫자-migration'이런 식으로 이름 뒤에 하이픈 migration이렇게 들어감. 즉 마지막 경로는 파일명 뒤에 하이픈과 추가되는 것임.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반영&lt;/p&gt;
&lt;pre id=&quot;code_1746716675798&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; npx ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:run -d ./src/data-source.ts&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터소스 예시&lt;/p&gt;
&lt;pre id=&quot;code_1746716620815&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src/data-source.ts

import { DataSource } from 'typeorm';
import * as dotenv from 'dotenv';
import 'reflect-metadata';

dotenv.config({ path: `.env.${process.env.NODE_ENV}` });

export default new DataSource({
	type: 'mysql',
	host: process.env.MYSQL_HOST,
	port: Number(process.env.MYSQL_PORT),
	username: process.env.MYSQL_USERNAME,
	password: process.env.MYSQL_PASSWORD,
	database: process.env.MYSQL_DATABASE,
	entities: ['src/**/*.entity.ts'],
	migrations: ['src/migrations/*.ts'],
	logging: true,
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>데이터베이스, ORM/TypeORM</category>
      <author>SparkIT</author>
      <guid isPermaLink="true">https://sparkit.tistory.com/94</guid>
      <comments>https://sparkit.tistory.com/94#entry94comment</comments>
      <pubDate>Sun, 25 May 2025 12:56:49 +0900</pubDate>
    </item>
    <item>
      <title>[Prisma] TypeORM vs Prisma 차이와 Prisma를 선택한 이유</title>
      <link>https://sparkit.tistory.com/93</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트를 이용해 개발하는 과정에서 사용할 수 있는 대표적인 ORM은 TypeORM과 Prisma입니다. 많은 사람들이 두 ORM 중 하나를 선택하는데요. 이때 두 ORM 중 어떤 것을 채택하는 것이 더 좋을지에 대해 많이 궁금하실 것입니다. 저도 마찬가지였습니다. 저는 타입스크립트 초기부터 현재까지 TypeORM을 잘 활용해오고 있었는데요. 이번에 새롭게 시작하는 프로젝트에는 TypeORM 대신 Prisma를 도입하기로 결정했습니다. 오늘은 그 이유를 TypeORM과 Prisma를 비교하며 공유해보려 합니다.&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;figure id=&quot;og_1746706254094&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;Prisma ORM vs TypeORM | Prisma Documentation&quot; data-og-description=&quot;Learn how Prisma compares to TypeORM.&quot; data-og-host=&quot;www.prisma.io&quot; data-og-source-url=&quot;https://www.prisma.io/docs/orm/more/comparisons/prisma-and-typeorm&quot; data-og-url=&quot;https://www.prisma.io/docs/orm/more/comparisons/prisma-and-typeorm&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ehx5iG/hyYMYC98mF/uCBD6iyWVAFP352K2BNTl1/img.png?width=1200&amp;amp;height=640&amp;amp;face=0_0_1200_640,https://scrap.kakaocdn.net/dn/rhBdI/hyYRtnYa1E/D9gIWTWvZkOwplyHaSALFk/img.png?width=1200&amp;amp;height=640&amp;amp;face=0_0_1200_640&quot;&gt;&lt;a href=&quot;https://www.prisma.io/docs/orm/more/comparisons/prisma-and-typeorm&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.prisma.io/docs/orm/more/comparisons/prisma-and-typeorm&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ehx5iG/hyYMYC98mF/uCBD6iyWVAFP352K2BNTl1/img.png?width=1200&amp;amp;height=640&amp;amp;face=0_0_1200_640,https://scrap.kakaocdn.net/dn/rhBdI/hyYRtnYa1E/D9gIWTWvZkOwplyHaSALFk/img.png?width=1200&amp;amp;height=640&amp;amp;face=0_0_1200_640');&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;Prisma ORM vs TypeORM | Prisma Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn how Prisma compares to TypeORM.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.prisma.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;TypeORM vs Prisma&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TypeORM과 Prisma를 비교한 간단한 표입니다.&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 158px;&quot; border=&quot;1&quot; data-end=&quot;874&quot; data-start=&quot;153&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 18px;&quot;&gt;&lt;b&gt;TypeORM&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 18px;&quot;&gt;&lt;b&gt;Prisma&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot; data-end=&quot;312&quot; data-start=&quot;223&quot;&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;234&quot; data-start=&quot;223&quot;&gt;&lt;b&gt;패러다임&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-end=&quot;280&quot; data-start=&quot;234&quot; data-col-size=&quot;md&quot;&gt;Code-First ORM&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-end=&quot;312&quot; data-start=&quot;280&quot; data-col-size=&quot;sm&quot;&gt;Schema-First ORM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot; data-end=&quot;386&quot; data-start=&quot;313&quot;&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;325&quot; data-start=&quot;313&quot;&gt;&lt;b&gt;모델 정의&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-end=&quot;353&quot; data-start=&quot;325&quot; data-col-size=&quot;md&quot;&gt;TypeScript 클래스 + 데코레이터 사용&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-end=&quot;386&quot; data-start=&quot;353&quot; data-col-size=&quot;sm&quot;&gt;schema.prisma 파일로 정의 (선언적 방식)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot; data-end=&quot;457&quot; data-start=&quot;387&quot;&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;399&quot; data-start=&quot;387&quot;&gt;&lt;b&gt;쿼리 작성&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-end=&quot;426&quot; data-start=&quot;399&quot; data-col-size=&quot;md&quot;&gt;엔티티 메서드 또는 Repository 사용&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-end=&quot;457&quot; data-start=&quot;426&quot; data-col-size=&quot;sm&quot;&gt;Prisma Client를 통한 타입 안전한 쿼리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot; data-end=&quot;516&quot; data-start=&quot;458&quot;&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;471&quot; data-start=&quot;458&quot;&gt;&lt;b&gt;타입 안전성&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-end=&quot;487&quot; data-start=&quot;471&quot; data-col-size=&quot;md&quot;&gt;제한적 (특히 쿼리에서)&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-end=&quot;516&quot; data-start=&quot;487&quot; data-col-size=&quot;sm&quot;&gt;매우 강력 (컴파일 타임에서 오류 탐지 가능)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot; data-end=&quot;599&quot; data-start=&quot;517&quot;&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;530&quot; data-start=&quot;517&quot;&gt;&lt;b&gt;마이그레이션&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-end=&quot;562&quot; data-start=&quot;530&quot; data-col-size=&quot;md&quot;&gt;수동으로 SQL 작성 또는 TypeORM CLI 사용&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-end=&quot;599&quot; data-start=&quot;562&quot; data-col-size=&quot;sm&quot;&gt;Prisma Migrate로 자동 마이그레이션 생성 및 적용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot; data-end=&quot;668&quot; data-start=&quot;600&quot;&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;612&quot; data-start=&quot;600&quot;&gt;&lt;b&gt;관계 처리&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-end=&quot;640&quot; data-start=&quot;612&quot; data-col-size=&quot;md&quot;&gt;복잡한 관계 설정 시 코드가 난해해질 수 있음&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-end=&quot;668&quot; data-start=&quot;640&quot; data-col-size=&quot;sm&quot;&gt;관계 정의가 명확하고, 연결된 쿼리도 직관적&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot; data-end=&quot;807&quot; data-start=&quot;743&quot;&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;758&quot; data-start=&quot;743&quot;&gt;&lt;b&gt;성능 및 최적화&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-end=&quot;782&quot; data-start=&quot;758&quot; data-col-size=&quot;md&quot;&gt;중간 수준, 일부 성능 문제 보고 있음&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-end=&quot;807&quot; data-start=&quot;782&quot; data-col-size=&quot;sm&quot;&gt;최적화된 쿼리 및 요청 배치 기능 제공&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TypeORM은 사실 JAVA Spring 진영의 JPA와 상당히 닮은 ORM입니다. OOP 스타일 패러다임을 사용해 데이터베이스 내 테이블과 1:1 매핑되는 엔티티 클래스를 만드는 방식을 사용합니다. 반면에 Prisma는 스키마 기반의 ORM으로 데이터베이스 모델을 schema.prisma 파일 하나에 모두 정의합니다. 이 파일을 활용해 타입 안정성을 제공합니다. 또한 Prisma는 마이그레이션 관리와 최적화된 쿼리를 제공합니다. 아래는 Prisma를 선택하면서 느낀 장점과 단점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Prisma 장점&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 타입 안정성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TypeORM 쿼리에서 잘못된 속성을 사용하는 경우를 컴파일 단계에서 잡지 못합니다. 즉, 다음과 같은 예시에서 에러가 발생하지 않습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1746707592214&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const user = await userRepository.findOne({
  where: { wrongField: 'test'} //   문제없는거 아닌가?
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Prisma에서는 잘못된 속성을 사용하면 이를 컴파일 단계에서 잡아줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746707654617&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const user = await prisma.user.findUnique({
  where: { wrongField: 'test' }, // ❗️ 타입 오류 발생
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Prisma 단점&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. enum&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스 테이블 컬럼 중 enum 타입을 사용해야 하는 경우가 있습니다. TypeORM을 사용하던 때에는 애플리케이션 레벨에서 먼저 enum을 정의하고 이를 DB에 반영했습니다. 즉, enum이라는 코드 파일을 먼저 작성하고 @Entity 코드에서 이를 import 해 사용하게 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 prisma의 경우 prisma.schema라는 스키마 설정 파일에 모든 enum을 설정해야 합니다.(제가 알기로는 외부 enum 파일 import 불가합니다) 이때 저는 도메인 모델을 만들어 사용 중이었는데요. 이 과정에 도메인 모델에 사용할 enum 파일을 따로 만들어야 한다는 문제점이 발생했습니다. 물론 prisma.schema에 작성한 enum값은 prisma client를 생성할 때 자동으로 @prisma/client 경로에 타입으로 생성됩니다. 이를 그대로 사용하면 prisma.schema에 설정된 enum을 그대로 외부에서 사용할 수 있죠. 하지만 이 경우 도메인 모델과 prisma라는 ORM 간 높은 결합도가 생깁니다. 중간에 prisma typeORM으로 교체하게 되면 도메인 모델 부분도 수정해야 한다는 뜻이죠. 저는 이런 부분이 prisma의 단점이라고 느꼈습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>데이터베이스, ORM/Prisma</category>
      <category>Prisma</category>
      <category>typeorm</category>
      <author>SparkIT</author>
      <guid isPermaLink="true">https://sparkit.tistory.com/93</guid>
      <comments>https://sparkit.tistory.com/93#entry93comment</comments>
      <pubDate>Sun, 25 May 2025 12:55:43 +0900</pubDate>
    </item>
    <item>
      <title>[Github] private 레포지토리 clone 에러 - remote: Repository not found</title>
      <link>https://sparkit.tistory.com/92</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;에러 상황&lt;/h3&gt;
&lt;pre id=&quot;code_1746861726761&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git clone {레포지토리 URL}&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;github에서 운영중인 private 레포지토리 코드를 clone 받으려는 상황. 아래와 같은 에러 발생&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1746861739671&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Cloning into '{레포지토리명}'...
remote: Repository not found.
fatal: repository '{레포지토리 URL}' not found&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;에러 코드를 읽어보면 해당 레포지토리를 찾지 못한다는 내용입니다. 하지만 보통 이런 경우 레포지토리 URL이 잘못된 경우는 많지 않습니다. 다른 이유가 더 일반적입니다. 그 이유는 바로 해당 &lt;b&gt;레포지토리가 private&lt;/b&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;h3 data-ke-size=&quot;size23&quot;&gt;해결 방법&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. Github 퍼스널 토큰 발급&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-05-10 오후 4.25.54.png&quot; data-origin-width=&quot;1233&quot; data-origin-height=&quot;1009&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhYUQA/btsNRhHpngY/AmeOevrA5QFsKeju9rTRg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhYUQA/btsNRhHpngY/AmeOevrA5QFsKeju9rTRg1/img.png&quot; data-alt=&quot;Github 홈페이지에서 Settings 클릭&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhYUQA/btsNRhHpngY/AmeOevrA5QFsKeju9rTRg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhYUQA%2FbtsNRhHpngY%2FAmeOevrA5QFsKeju9rTRg1%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;1233&quot; height=&quot;1009&quot; data-filename=&quot;스크린샷 2025-05-10 오후 4.25.54.png&quot; data-origin-width=&quot;1233&quot; data-origin-height=&quot;1009&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Github 홈페이지에서 Settings 클릭&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;github 퍼스널 토근을 발급하기 위해서는 github 접속 시&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;우측 상단에 위치한 자신의 프로필 이미지&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;를 클릭해 사이드 메뉴를 확인합니다. 사이드 메뉴에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;'&lt;span style=&quot;color: #006dd7;&quot;&gt;Settings&lt;/span&gt;'&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;탭을 클릭합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-05-10 오후 4.27.47.png&quot; data-origin-width=&quot;1241&quot; data-origin-height=&quot;958&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJZLo5/btsNR9aKWZP/0SJsze0dpHJRy0i6Wg1jwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJZLo5/btsNR9aKWZP/0SJsze0dpHJRy0i6Wg1jwK/img.png&quot; data-alt=&quot;Developer settings 클릭&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJZLo5/btsNR9aKWZP/0SJsze0dpHJRy0i6Wg1jwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJZLo5%2FbtsNR9aKWZP%2F0SJsze0dpHJRy0i6Wg1jwK%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;1241&quot; height=&quot;958&quot; data-filename=&quot;스크린샷 2025-05-10 오후 4.27.47.png&quot; data-origin-width=&quot;1241&quot; data-origin-height=&quot;958&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Developer settings 클릭&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Settings 탭에서 왼쪽 최하단에 있는 '&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Developer settings&lt;/b&gt;&lt;/span&gt;' 탭을 클릭합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-05-10 오후 4.28.29.png&quot; data-origin-width=&quot;1252&quot; data-origin-height=&quot;689&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NONtR/btsNS73Y9sO/knP0bwI8G4lu8TljLk0oGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NONtR/btsNS73Y9sO/knP0bwI8G4lu8TljLk0oGK/img.png&quot; data-alt=&quot;Personal access tokens 하위 토큰 종류 중 원하는 토큰 클릭&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NONtR/btsNS73Y9sO/knP0bwI8G4lu8TljLk0oGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNONtR%2FbtsNS73Y9sO%2FknP0bwI8G4lu8TljLk0oGK%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;1252&quot; height=&quot;689&quot; data-filename=&quot;스크린샷 2025-05-10 오후 4.28.29.png&quot; data-origin-width=&quot;1252&quot; data-origin-height=&quot;689&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Personal access tokens 하위 토큰 종류 중 원하는 토큰 클릭&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Developer settings 탭에서 Personal access tokens 탭을 통해 private 레포지토리에 접근할 수 있게 도와주는 퍼스널 토큰 발급이 가능합니다. 위 사진과 같이 현재 토큰 방식에는 2 가지 방식이 존재합니다. 두 종류에 큰 차이는 없습니다. 원하는 방식을 선택해 안내를 따라 퍼스널 토큰 발급을 마무리 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;⚠️ &lt;b&gt;퍼스널 토큰 저장해놓기&lt;/b&gt;&lt;br /&gt;퍼스널 토큰은 발급 이후 다시 조회할 수 없습니다. 만약 퍼스널 토큰을 분실하셨을 시 재발급 밖에 방법이 없습니다. 이 때문에 퍼스널 토큰을 발급받은 뒤 계속 재사용하기 위해서는 해당 토큰을 개인적으로 보관하셔야 합니다.&lt;/blockquote&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;h4 data-ke-size=&quot;size20&quot;&gt;2. git clone 시 유저명과 퍼스널 토큰 입력&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;public 레포지토리는 다음과 같은 방식으로 단순하게 클론 가능합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746862717811&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git clone {레포지토리 URL}&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;하지만 private 레포지토리는 다음과 같이 clone하셔야합니다. 더 자세히 설명드리자면 기존 레포지토리 URL에서 'https://' 와 'github.com' 사이에 방금 퍼스널 토큰을 발급받은 github 계정의 유저명과 '@' 문자를 넣어주는 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746862765207&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;❗️public repository
git clone https://github.com/SHINDongHyeo/test.git

✅ private repository
git clone https://SHINDongHyeo@github.com/SHINDongHyeo/test.git&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 아래와 같이 퍼스널 토큰을 입력하라는 안내가 표시됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746863129502&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Cloning into 'test'...
Password for 'https://SHINDongHyeo@github.com':   여기에 퍼스널 토큰값 입력&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;퍼스널 토큰까지 입력해주면 성공적으로 레포지토리가 clone 되는 모습을 확인할 수 있습니다.&lt;/p&gt;</description>
      <category>git clone</category>
      <category>github</category>
      <category>personal access token</category>
      <category>private</category>
      <category>Repository</category>
      <author>SparkIT</author>
      <guid isPermaLink="true">https://sparkit.tistory.com/92</guid>
      <comments>https://sparkit.tistory.com/92#entry92comment</comments>
      <pubDate>Sat, 10 May 2025 16:46:37 +0900</pubDate>
    </item>
    <item>
      <title>[MySQL] 쿼리 실행 계획(Query Execution Plan)을 통해 쿼리 최적화하기</title>
      <link>https://sparkit.tistory.com/91</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;쿼리 실행 계획은 데이터베이스에서 특정 SQL 쿼리문을 실행할 때, DB 옵티마이저가 해당 쿼리를 어떻게 실행할 지에 대한 계획을 의미합니다. 그래서 효율적인 데이터베이스 활용을 위해서는 쿼리 실행 계획에 대해 파악하는 것은 필수입니다. 저는 평소에 MySQL 데이터베이스를 많이 사용합니다. 그래서 오늘은 MySQL에서 쿼리 실행 계획을 확인하고 이를 통해 더 좋은 쿼리를 작성하는 방법에 대해 공유해보려합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;쿼리 실행 계획이란?&lt;/h3&gt;
&lt;figure id=&quot;og_1746374000755&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;MySQL :: MySQL 8.4 Reference Manual :: 10.8 Understanding the Query Execution Plan&quot; data-og-description=&quot;10.8&amp;nbsp;Understanding the Query Execution Plan Depending on the details of your tables, columns, indexes, and the conditions in your WHERE clause, the MySQL optimizer considers many techniques to efficiently perform the lookups involved in an SQL query. A qu&quot; data-og-host=&quot;dev.mysql.com&quot; data-og-source-url=&quot;https://dev.mysql.com/doc/refman/8.4/en/execution-plan-information.html&quot; data-og-url=&quot;https://dev.mysql.com/doc/refman/8.4/en/execution-plan-information.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://dev.mysql.com/doc/refman/8.4/en/execution-plan-information.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dev.mysql.com/doc/refman/8.4/en/execution-plan-information.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;MySQL :: MySQL 8.4 Reference Manual :: 10.8 Understanding the Query Execution Plan&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;10.8&amp;nbsp;Understanding the Query Execution Plan Depending on the details of your tables, columns, indexes, and the conditions in your WHERE clause, the MySQL optimizer considers many techniques to efficiently perform the lookups involved in an SQL query. A qu&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dev.mysql.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DBMS(MySQL, PostgreSQL 등)가 &lt;b&gt;SQL 쿼리를 실제로 어떻게 처리할지에 대한 단계별 계획&lt;/b&gt;을 말합니다. 즉, 쿼리를 실행하기 전에 DB가 스스로 세우는 가장 빠른 처리 방법에 대한 절략이라고 생각할 수 있습니다. 이런 쿼리 실행 계획은 유동적입니다. 같은 쿼리문일지라도 상황에 따라 다른 쿼리 실행 계획을 가지며 다르게 실행될 수 있다는 뜻입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mysql에서 쿼리 실행 계획을 확인하기 위해서는 &lt;b&gt;EXPLAIN 명령어&lt;/b&gt;를 사용합니다. 예를 들어 SELECT * FROM users; 라는 쿼리가 있다고 가정하면 EXPLAIN SELECT * FROM users;처럼 쿼리문 앞에 EXPLAIN 명령어를 붙이기만 하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;쿼리 실행 계획 구조&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mysql에서 EXPLAIN 명령어 사용 후 반환되는 값을 구성하는 컬럼 종류는 다음과 같습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;✅ &lt;/b&gt;id&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 계획 쿼리별 식별자값입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시)&lt;br /&gt;하나의 select 문에 대한 실행 계획을 확인한다면 id값이 1인 실행 계획 하나만 반환됩니다. 하지만 서브 쿼리로 select 문이 하나 더 있다면 id값이 1,2로 총 2개의 실행 계획이 반환됩니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;331&quot; data-origin-height=&quot;171&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5h3fv/btsNJEijWNI/jQfkeTjYBLBxKL6U6x0e81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5h3fv/btsNJEijWNI/jQfkeTjYBLBxKL6U6x0e81/img.png&quot; data-alt=&quot;서브 쿼리 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5h3fv/btsNJEijWNI/jQfkeTjYBLBxKL6U6x0e81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5h3fv%2FbtsNJEijWNI%2FjQfkeTjYBLBxKL6U6x0e81%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;331&quot; height=&quot;171&quot; data-origin-width=&quot;331&quot; data-origin-height=&quot;171&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;서브 쿼리 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;422&quot; data-origin-height=&quot;109&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/370Mc/btsNJ6ljpT1/kLjSkQKeFJv9qTJBv0JND1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/370Mc/btsNJ6ljpT1/kLjSkQKeFJv9qTJBv0JND1/img.png&quot; data-alt=&quot;서브 쿼리 실행 계과 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/370Mc/btsNJ6ljpT1/kLjSkQKeFJv9qTJBv0JND1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F370Mc%2FbtsNJ6ljpT1%2FkLjSkQKeFJv9qTJBv0JND1%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;422&quot; height=&quot;109&quot; data-origin-width=&quot;422&quot; data-origin-height=&quot;109&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;서브 쿼리 실행 계과 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;✅ select_type&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 계획 쿼리 타입입니다. insert문이라면 insert, delete라면 delete라고 표시됩니다. select문에 대해서는 여러 타입이 존재합니다. 아래 몇 가지 select 타입을 정리해보았습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SIMPLE&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서브 쿼리도 없고 UNION도 없는 가장 단순한 형태의 쿼리문&lt;/p&gt;
&lt;pre id=&quot;code_1746376214871&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 예시
EXPLAIN
SELECT
  *
FROM
  users u
;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PRIMARY&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아우터 쿼리문 ( 예를 들면 서브쿼리가 있을 때 서브 쿼리는 SUBQUERY, 메인 쿼리 혹은 아우터 쿼리는 PRIMARY&amp;nbsp; )&lt;/p&gt;
&lt;pre id=&quot;code_1746376604941&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 예시
EXPLAIN
SELECT
  name 
FROM
  users 
WHERE
  id
IN (
  SELECT user_id FROM orders WHERE amount &amp;gt; 100
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SUBQUERY&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서브 쿼리문 ( 예를 들면 서브쿼리가 있을 때 서브 쿼리는 SUBQUERY, 메인 쿼리 혹은 아우터 쿼리는 PRIMARY&amp;nbsp; )&lt;/p&gt;
&lt;pre id=&quot;code_1746376677246&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 예시
EXPLAIN
SELECT
  name 
FROM
  users 
WHERE
  id
IN (
  SELECT user_id FROM orders WHERE amount &amp;gt; 100
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;✅ table&lt;/h4&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;h4 data-ke-size=&quot;size20&quot;&gt;✅ partitions&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파티셔닝이 되어있을 경우 사용될 파티션 표시 ( 만약 파티션되지 않은 테이블일 경우 null로 표시됩니다 )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;✅ type&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;( 더 자세한 설명은 아래 '&lt;a href=&quot;https://sparkit.tistory.com/91#heading-14&quot;&gt;쿼리 실행 계획 예제&lt;/a&gt;' 참조 )&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ALL&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;u&gt;풀 스캔&lt;/u&gt;하는 경우&lt;/li&gt;
&lt;li&gt;index&lt;b&gt;&lt;br /&gt;&lt;/b&gt;전체 &lt;u&gt;인덱스 스캔&lt;/u&gt;하는 경우&lt;/li&gt;
&lt;li&gt;range&lt;b&gt;&lt;br /&gt;&lt;/b&gt;인덱스를 이용한 &lt;u&gt;범위 검색&lt;/u&gt;의 경우&lt;/li&gt;
&lt;li&gt;ref&lt;br /&gt;&lt;u&gt;조인 시&lt;/u&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;사용되며, 해당 조인에&amp;nbsp;&lt;/span&gt;&lt;u&gt;여러 행이 매칭&lt;/u&gt;되는 경우&lt;/li&gt;
&lt;li&gt;eq_ref&lt;br /&gt;&lt;u&gt;조인 시&lt;/u&gt;&lt;span&gt; 사용되며, 해당 조인에&amp;nbsp;&lt;/span&gt;&lt;u&gt;단 하나의 행만 매칭&lt;/u&gt;되는 경우&lt;/li&gt;
&lt;li&gt;const&lt;br /&gt;테이블에 &lt;u&gt;&lt;span&gt;단 하나의 행&lt;/span&gt;만 매칭&lt;/u&gt;되는 경우 (ex. &lt;span&gt;PRIMARY KEY&lt;/span&gt;나 &lt;span&gt;UNIQUE&lt;/span&gt; 인덱스를 통한 조회)&lt;/li&gt;
&lt;li&gt;system&lt;br /&gt;테이블에 &lt;u&gt;단 하나의 행만 존재&lt;/u&gt;하는&amp;nbsp;테이블&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;✅ possible_keys&lt;/h4&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;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;✅ key&lt;/h4&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;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;✅ key_len&lt;/h4&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;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;✅ ref&lt;/h4&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;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;✅ rows&lt;/h4&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;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;✅ filtered&lt;/h4&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;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;✅ Extra&lt;/h4&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;h3 data-ke-size=&quot;size23&quot;&gt;쿼리 실행 계획 예제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿼리를 최적화하기 위해 쿼리 실행 계획에서 집중할 부분은 &lt;b&gt;type 컬럼&lt;/b&gt;입니다. 여러 SQL 쿼리 예시를 통해 type 컬럼에 대해 더 자세히 알아봅시다. 먼저 다음과 같은 테이블 구조가 있다고 가정합시다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;692&quot; data-origin-height=&quot;464&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcZUii/btsNKuNdzjR/k1Cp1tw3FgYrzKSxDkzY3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcZUii/btsNKuNdzjR/k1Cp1tw3FgYrzKSxDkzY3k/img.png&quot; data-alt=&quot;예시 다이어그램(users, posts 테이블 구조)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcZUii/btsNKuNdzjR/k1Cp1tw3FgYrzKSxDkzY3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcZUii%2FbtsNKuNdzjR%2Fk1Cp1tw3FgYrzKSxDkzY3k%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;692&quot; height=&quot;464&quot; data-origin-width=&quot;692&quot; data-origin-height=&quot;464&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;예시 다이어그램(users, posts 테이블 구조)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블은 총 2가지로 users 테이블과 posts 테이블이 존재합니다. 그리고 각 테이블은 id라는 컬럼명으로 PK값을 사용합니다. 이때 posts 테이블은 user_id 컬럼을 가지고 있고, 이는 users 테이블 id에 대한 FK입니다. 이 상황에서 여러 SQL을 사용하고 각 SQL 쿼리문에 대한 EXPLAIN 결과를 확인해봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전체 행 조회&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 users 테이블 데이터를 조회합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746537319751&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;EXPLAIN
SELECT
  *
FROM
  users u
;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1746537403486&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Name         |Value |
-------------+------+
id           |1     |
select_type  |SIMPLE|
table        |u     |
partitions   |      |
type         |ALL   |
possible_keys|      |
key          |      |
key_len      |      |
ref          |      |
rows         |4     |
filtered     |100.0 |
Extra        |      |&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;type이 ALL로 표시됩니다. 즉, 모든 데이터를 풀 스캔으로 조회 중임을 알 수 있습니다. 또한 rows, filtered 결과를 통해 현재 users 테이블에는 총 4개의 데이터가 존재하여 이 행의 모두 조회했음을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PK로 조회&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;users 테이블의 PK 컬럼 id를 활용하여 id가 1인 단 하나의 데이터만 조회합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1746537614350&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;EXPLAIN
SELECT
  *
FROM
  users u
WHERE
  u.id=1
;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1746537625369&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Name         |Value  |
-------------+-------+
id           |1      |
select_type  |SIMPLE |
table        |u      |
partitions   |       |
type         |const  |
possible_keys|PRIMARY|
key          |PRIMARY|
key_len      |4      |
ref          |const  |
rows         |1      |
filtered     |100.0  |
Extra        |       |&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;type이 const로 표시됩니다. 즉, 특정 PK값을 만족하는 데이터는 테이블에서 단 한개만 존재하기 때문에 const로 표시됨을 알 수 있습니다. possible_keys에는 PRIMARY값만을 해당 쿼리에 사용할 수 있는 인덱스 목록으로 선정했고, key에서도 PRIMARY값을 인덱스로 사용하기로 했음을 알 수 있습니다.&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서브 쿼리 조회&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;users 테이블의 age 컬럼을 통해 조회합니다. 이때 서브 쿼리문을 추가해봅니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746538186232&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;EXPLAIN
SELECT
  *
FROM
  users u
WHERE
  u.age = (
	SELECT age FROM users WHERE id=1
  )
;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1746538218818&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Name         |Value      |
-------------+-----------+
id           |1          |
select_type  |PRIMARY    |
table        |u          |
partitions   |           |
type         |ALL        |
possible_keys|           |
key          |           |
key_len      |           |
ref          |           |
rows         |4          |
filtered     |25.0       |
Extra        |Using where|




Name         |Value   |
-------------+--------+
id           |2       |
select_type  |SUBQUERY|
table        |users   |
partitions   |        |
type         |const   |
possible_keys|PRIMARY |
key          |PRIMARY |
key_len      |4       |
ref          |const   |
rows         |1       |
filtered     |100.0   |
Extra        |        |&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아우터 쿼리와 서브 쿼리에 대한 실행 계획 결과값이 2개 출력됩니다.&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>데이터베이스, ORM</category>
      <category>Database</category>
      <category>db</category>
      <category>explain</category>
      <category>MySQL</category>
      <category>SQL</category>
      <category>실행 계획</category>
      <category>옵티마이저</category>
      <category>쿼리 실행 계획</category>
      <author>SparkIT</author>
      <guid isPermaLink="true">https://sparkit.tistory.com/91</guid>
      <comments>https://sparkit.tistory.com/91#entry91comment</comments>
      <pubDate>Tue, 6 May 2025 21:57:31 +0900</pubDate>
    </item>
    <item>
      <title>[NodeJS] 싱글 스레드도 경쟁 상태(Race Condition)는 발생합니다</title>
      <link>https://sparkit.tistory.com/90</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Node.js는 싱글 스레드로 동작하는 서버 환경으로, 일반적으로 멀티스레드 환경에서 발생하는 Race Condition 문제와는 거리가 멀다고 여겨집니다. 그러나 비동기 코드가 사용되면 Node.js에서도 Race Condition 문제가 발생할 수 있습니다. 이번 글에서는 Race Condition의 개념을 짚어보고, Node.js에서 이를 어떻게 실험할 수 있는지에 대한 예제를 소개하려 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;경쟁 상태(Race Condition)란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Race Condition은 &lt;u&gt;여러 프로세스나 스레드가 동시에 동일한 자원에 접근&lt;/u&gt;하면서 발생하는 문제입니다. 이는 공유 자원을 동시에 변경하려 할 때, 각 작업의 실행 순서에 따라 예상치 못한 결과가 발생하는 상황을 말합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 CounterService라는 클래스가 존재하고 해당 클래스에 counter라는 멤버 변수가 있다고 가정합니다. 그리고 이 멤버 변수 counter 값을 +1 하는 메서드가 존재합니다. 이때 해당 메서드에 여러 요청이 동시에 도착하면 어떻게 될까요? 순서대로 counter 값이 +1 된다면 race condition 문제가 발생하지 않는 경우고, 단순히 counter값이 +1 한 번 실행되면 race condition 문제가 발생된 경우입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Node.js는 싱글스레드니까 Race Condition 자체가 성립할 수 없지 않나요?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 race condition은 멀티 스레드 환경으로 실행되는 Java Spring 프레임워크에서 많이 거론됩니다. 반면 Node.js는 애초에 싱글 스레드 환경이기 때문에 race condition이 발생하지 않는다는 오해를 사기도 합니다. 하지만 이는 정확하지 않은 정보입니다. 더 정확하게 표현하자면 여러 스레드가 하나의 자원에 접근하면서 발생하는 race condition은 없습니다. 하지만 &lt;u&gt;서로 다른 논리적 트랜잭션 간 race condition을 발생 가능&lt;/u&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node.js에서 개발된 코드가 모두 동기적인 코드로만 구성된다면 race condition이 발생하지 않을 수 있습니다. Node.js는 이벤트 루프라는 싱글 스레드로 동작하기 때문에 동기적인 코드는 모두 순서대로 작동하기 때문입니다. 하지만 비동기 코드가 포함되면 이야기가&amp;nbsp; 달라집니다. 비동기 코드는 먼저 실행된 코드가 먼저 반환됨을 보장하지 않습니다. 여기서 순서가 꼬이게 됩니다. (Node.js의 이벤트 루프에 대한 내용은 아래 글에 더 자세하기 작성했습니다)&lt;/p&gt;
&lt;figure id=&quot;og_1746298028027&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[NodeJS]이벤트 루프 동작 이해하기&quot; data-og-description=&quot;용어 정리콜 스택(Call stack)수행해야 하는 함수들 대기 장소(To Do List 같은 역할). 실제로 노드가 함수들을 실행하기 위해 확인하는 곳이라고 생각하면 됩니다. 노드 엔진은 콜 스택에 함수들이 대&quot; data-og-host=&quot;sparkit.tistory.com&quot; data-og-source-url=&quot;https://sparkit.tistory.com/64&quot; data-og-url=&quot;https://sparkit.tistory.com/64&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/qw5L5/hyYMTHDrx6/eyRxs2rtkbQ93ExrCR8gWK/img.png?width=800&amp;amp;height=483&amp;amp;face=0_0_800_483,https://scrap.kakaocdn.net/dn/QtyPF/hyYMWqQofY/Lynfo0RXchhH2KL6P4nf90/img.png?width=800&amp;amp;height=483&amp;amp;face=0_0_800_483,https://scrap.kakaocdn.net/dn/Oi5Vo/hyYMWdifGG/SutkbdKNq506qs5uwIrOnk/img.png?width=1931&amp;amp;height=1167&amp;amp;face=0_0_1931_1167&quot;&gt;&lt;a href=&quot;https://sparkit.tistory.com/64&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://sparkit.tistory.com/64&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/qw5L5/hyYMTHDrx6/eyRxs2rtkbQ93ExrCR8gWK/img.png?width=800&amp;amp;height=483&amp;amp;face=0_0_800_483,https://scrap.kakaocdn.net/dn/QtyPF/hyYMWqQofY/Lynfo0RXchhH2KL6P4nf90/img.png?width=800&amp;amp;height=483&amp;amp;face=0_0_800_483,https://scrap.kakaocdn.net/dn/Oi5Vo/hyYMWdifGG/SutkbdKNq506qs5uwIrOnk/img.png?width=1931&amp;amp;height=1167&amp;amp;face=0_0_1931_1167');&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;[NodeJS]이벤트 루프 동작 이해하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;용어 정리콜 스택(Call stack)수행해야 하는 함수들 대기 장소(To Do List 같은 역할). 실제로 노드가 함수들을 실행하기 위해 확인하는 곳이라고 생각하면 됩니다. 노드 엔진은 콜 스택에 함수들이 대&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;sparkit.tistory.com&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;그럼 비동기 코드를 넣어서 Node.js에서 강제로 race condition을 발생시켜 보겠습니다. 아래 예제를 통해 쉽게 이해해봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Node.js 환경 race condition 예제&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Typescript와 Nest JS 프레임워크를 활용한 예제입니다. 서비스 코드 일부로 testRaceCondition 메서드를 실행하면 동시에 increment 메서드를 5번 실행시킵니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746298812027&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { Injectable } from '@nestjs/common';

@Injectable()
export class CounterService {
	private counter = 0;

	async testRaceCondition() { // ❗️ 이 메서들 통해 5번의 increment 메서드 동시에 요청
		return Promise.all([
			this.increment(),
			this.increment(),
			this.increment(),
			this.increment(),
			this.increment(),
		]);
	}

	async increment() {
		const currentValue = this.counter;
		this.counter = currentValue + 1;     // ✅ 위 아래 코드 순서 변경하면 결과 달라짐
		await this.simulateAsyncOperation(); // ✅ 위 아래 코드 순서 변경하면 결과 달라짐
	}

	private async simulateAsyncOperation() {
		return new Promise((resolve) =&amp;gt; setTimeout(resolve, 50));
	}

	getCounter() {
		return this.counter;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제 코드에서 testRaceCondition 실행 후 getCounter 실행하여 this.counter 값을 확인하면 어떤 값이 나올까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 &lt;b&gt;5&lt;/b&gt;입니다. 5개의 요청(increment 메서드에 대한)이 동시에 실행되어도 &lt;u&gt;콜 스택에서 동기적인 코드는 모두 순서를 보장받으며 실행되기 때문&lt;/u&gt;입니다. 즉, increment 메서드의 const currentValue = this.counter; 와 this.counter = currentValue = 1;는 5개의 요청 순서대로 실행되어 결과적으로 this.counter는 5라는 값을 가지게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 increment 메서드를 다음과 같이 수정하면 어떻게 될까요?&lt;/p&gt;
&lt;pre id=&quot;code_1746299174553&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;	async increment() {
    		const currentValue = this.counter;
        	await this.simulateAsyncOperation(); // ✅ 위 아래 코드 순서 변경하면 결과 달라짐
        	this.counter = currentValue + 1;     // ✅ 위 아래 코드 순서 변경하면 결과 달라짐
	}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이 increment 메서드를 수정하게 되면 this.counter값은 &lt;b&gt;5가 아니라 1이 반환&lt;/b&gt;됩니다. 왜 그럴까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 콜 스택에서 동기적인 코드 const currentValue = this.counter;만 먼저 순서대로 실행되기 때문입니다. 이를 통해 5개의 요청이 아무런 변환 없는 this.counter값을 그저 요청 간 순서를 지키며 읽기만 합니다. 그럼 모든 요청의 currentValue값은 0이 됩니다. 여기서 밑에 await 코드가 반환되길 기다리죠. 그리고 해당 비동기 코드의 콜백 함수가 반환되면 그제야 this.counter = currentValue + 1;이 실행되어서 this.counter 값이 0에서 1이 되는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;마무리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위처럼 Node.js 환경에서도 상황에 따라 race condition 문제가 발생할 수 있음을 확인했습니다. 이를 통해 코드를 설계하는 과정에서 이런 race condition 문제를 미리 예측하여 문제가 발생하지 않는 구조를 사용하도록 해야 합니다.&lt;/p&gt;</description>
      <category>백엔드/Node.js</category>
      <category>Node js</category>
      <category>race condition</category>
      <author>SparkIT</author>
      <guid isPermaLink="true">https://sparkit.tistory.com/90</guid>
      <comments>https://sparkit.tistory.com/90#entry90comment</comments>
      <pubDate>Sun, 4 May 2025 05:05:14 +0900</pubDate>
    </item>
    <item>
      <title>함수형 프로그래밍에 대한 이해와 오해 (feat.절차지향, 객체지향)</title>
      <link>https://sparkit.tistory.com/89</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;'&lt;b&gt;함수형 프로그래밍&lt;/b&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;h3 data-ke-size=&quot;size23&quot;&gt;절차 지향 vs 함수형 vs 객체 지향&lt;/h3&gt;
&lt;h4 data-end=&quot;517&quot; data-start=&quot;475&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;✅ 절차 지향 프로그래밍 (Procedural Programming)&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;456&quot; data-origin-height=&quot;483&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qsn8S/btsNLte4Iff/fAVRPpSZT3iWVu9V4N2tB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qsn8S/btsNLte4Iff/fAVRPpSZT3iWVu9V4N2tB1/img.png&quot; data-alt=&quot;절차 지향 프로그래밍 일러스트레이션&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qsn8S/btsNLte4Iff/fAVRPpSZT3iWVu9V4N2tB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fqsn8S%2FbtsNLte4Iff%2FfAVRPpSZT3iWVu9V4N2tB1%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;456&quot; height=&quot;483&quot; data-origin-width=&quot;456&quot; data-origin-height=&quot;483&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;절차 지향 프로그래밍 일러스트레이션&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;356&quot; data-start=&quot;195&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;248&quot; data-start=&quot;195&quot;&gt;&lt;b&gt;핵심 개념&lt;/b&gt;: 프로그램을 순차적인 절차(명령의 흐름)로 구성하여 문제를 해결함.&lt;/li&gt;
&lt;li data-end=&quot;322&quot; data-start=&quot;249&quot;&gt;&lt;b&gt;기반 구조&lt;/b&gt;: 함수(또는 프로시저)를 사용하여 코드를 구조화하되, 주로 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;전역 상태와 공유된 데이터&lt;/b&gt;&lt;/span&gt;를 중심으로 동작.&lt;/li&gt;
&lt;li data-end=&quot;356&quot; data-start=&quot;323&quot;&gt;&lt;b&gt;대표 언어&lt;/b&gt;: C, Pascal, Fortran 등&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;366&quot; data-start=&quot;358&quot; data-ke-size=&quot;size18&quot;&gt;특징:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;468&quot; data-start=&quot;367&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;395&quot; data-start=&quot;367&quot;&gt;명령어들이 순서대로 실행됨 (절차적 흐름 중요)&lt;/li&gt;
&lt;li data-end=&quot;418&quot; data-start=&quot;396&quot;&gt;상태 변화(변수 변경)를 자유롭게 함&lt;/li&gt;
&lt;li data-end=&quot;434&quot; data-start=&quot;419&quot;&gt;가변 데이터를 많이 사용&lt;/li&gt;
&lt;li data-end=&quot;468&quot; data-start=&quot;435&quot;&gt;함수는 보조 도구이며, 상태를 변경하거나 입출력을 수행함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;예시:&lt;/p&gt;
&lt;pre id=&quot;code_1746172107925&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let total = 0;
function addToTotal(num) {
  total += num;
}
addToTotal(5);
console.log(total); // 5 (상태 변화)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시에서 단순히 함수 addToTotal을 만들고 이를 사용했으니 함수형 프로그래밍이 아니냐?라고 생각할 수 있습니다. 하지만 이는 함수형 프로그래밍이 아닙니다. 그 이유는 다음과 같습니다.&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;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;외부 상태 변경&lt;br /&gt;전역 변수인 total을 함수 내부에서 변경하고 있습니다. 이렇게 &lt;u&gt;외부 상태를 변경하는 것은 함수형 프로그래밍 철칙에 위배&lt;/u&gt;됩니다.&lt;/li&gt;
&lt;li&gt;새로운 값 반환 안함&lt;br /&gt;함수형 프로그래밍에서는 새로운 값을 만들고 이를 반환해야합니다. 하지만 위 addToTotal은 단순히 외부 상태인 total 변수만 수정하고 &lt;u&gt;새로운 값을 만들거나 반환하지 않고 있습니다&lt;/u&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-end=&quot;517&quot; data-start=&quot;475&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;✅ 함수형 프로그래밍 (Functional Programming)&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Hd0JX/btsNJKCCqWX/AbM85IYvnGn4U1uM6w66g1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Hd0JX/btsNJKCCqWX/AbM85IYvnGn4U1uM6w66g1/img.png&quot; data-alt=&quot;순수함수 일러스트레이션&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Hd0JX/btsNJKCCqWX/AbM85IYvnGn4U1uM6w66g1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHd0JX%2FbtsNJKCCqWX%2FAbM85IYvnGn4U1uM6w66g1%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;880&quot; height=&quot;495&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;순수함수 일러스트레이션&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;696&quot; data-start=&quot;518&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;570&quot; data-start=&quot;518&quot;&gt;&lt;b&gt;핵심 개념&lt;/b&gt;: 프로그램을 수학적 함수처럼 구성하며, 상태와 부작용을 최소화함.&lt;/li&gt;
&lt;li data-end=&quot;619&quot; data-start=&quot;571&quot;&gt;&lt;b&gt;기반 구조&lt;/b&gt;: &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;순수 함수&lt;/b&gt;&lt;/span&gt;(Pure Function)를 중심으로 프로그램을 구성&lt;/li&gt;
&lt;li data-end=&quot;696&quot; data-start=&quot;620&quot;&gt;&lt;b&gt;대표 언어&lt;/b&gt;: Haskell, Clojure, Elm, (함수형 기능이 있는 JavaScript, Python, Scala 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;706&quot; data-start=&quot;698&quot; data-ke-size=&quot;size18&quot;&gt;특징:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;865&quot; data-start=&quot;707&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;763&quot; data-start=&quot;707&quot;&gt;순수 함수(Pure function)&lt;br /&gt;순수 함수 사용하여 같은 입력에 대해 항상 같은 출력을 보장함. 외부 상태를 변경하지 않음&lt;/li&gt;
&lt;li data-end=&quot;798&quot; data-start=&quot;764&quot;&gt;불변성(immutability)&lt;br /&gt;외부 상태를 변경하지 않고, 새로운 값을 생성함&lt;/li&gt;
&lt;li data-end=&quot;834&quot; data-start=&quot;799&quot;&gt;고차 함수 사용&lt;br /&gt;함수를 인자로 받거나 반환하는 함수&lt;/li&gt;
&lt;li data-end=&quot;865&quot; data-start=&quot;835&quot;&gt;상태와 부작용을 최소화하여 디버깅과 테스트가 용이함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;예시:&lt;/p&gt;
&lt;pre id=&quot;code_1746172126686&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function add(a, b) {
  return a + b;
}
const result = add(3, 5); // 항상 8
console.log(result); // 8 (불변, 순수 함수)&lt;/code&gt;&lt;/pre&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;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;외부 상태 변경&lt;br /&gt;&lt;u&gt;외부 상태를 변경하지 않습니다&lt;/u&gt;. 만약 add 함수 내부에서 외부에서 전달된 a, b값을 수정했다면 함수형 프로그래밍 철칙을 위배합니다.&lt;/li&gt;
&lt;li&gt;새로운 값 반환&lt;br /&gt;a와 b를 더한 &lt;u&gt;새로운 값을 반환&lt;/u&gt;하고 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-start=&quot;475&quot; data-end=&quot;517&quot;&gt;&lt;b&gt;✅ 객체 지향 프로그래밍 (Object-Oriented Programming)&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;625&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kzNb1/btsNJpr68Mx/IuDqJAw1b4rDpY0ljTmpck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kzNb1/btsNJpr68Mx/IuDqJAw1b4rDpY0ljTmpck/img.png&quot; data-alt=&quot;객체 지향 프로그래밍 일러스트레이션&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kzNb1/btsNJpr68Mx/IuDqJAw1b4rDpY0ljTmpck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkzNb1%2FbtsNJpr68Mx%2FIuDqJAw1b4rDpY0ljTmpck%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;804&quot; height=&quot;625&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;625&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;객체 지향 프로그래밍 일러스트레이션&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-start=&quot;518&quot; data-end=&quot;696&quot;&gt;
&lt;li data-start=&quot;518&quot; data-end=&quot;570&quot;&gt;&lt;b&gt;핵심 개념&lt;/b&gt;: 현실 세계의 개념을 객체(object)로 모델링하여, 객체 간의 상호작용으로 프로그램을 구성함&lt;/li&gt;
&lt;li data-start=&quot;571&quot; data-end=&quot;619&quot;&gt;&lt;b&gt;기반 구조&lt;/b&gt;:&lt;span&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt; 클래스(class)와 객체(instance)&lt;/span&gt;&lt;/b&gt;를 중심으로 프로그램을 구성&lt;/span&gt;&lt;/li&gt;
&lt;li data-start=&quot;620&quot; data-end=&quot;696&quot;&gt;&lt;b&gt;대표 언어&lt;/b&gt;: Java, C++, C#, Python, Ruby, Kotlin, Swift 등&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot; data-start=&quot;698&quot; data-end=&quot;706&quot;&gt;특징:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-start=&quot;707&quot; data-end=&quot;865&quot;&gt;
&lt;li data-end=&quot;441&quot; data-start=&quot;363&quot;&gt;캡슐화(Encapsulation)&lt;br /&gt;데이터(속성)와 동작(메서드)을 하나의 객체 안에 묶어 외부에 불필요한 내부 구현을 숨김&lt;/li&gt;
&lt;li data-end=&quot;511&quot; data-start=&quot;443&quot;&gt;상속(Inheritance)&lt;br /&gt;상위 클래스의 속성과 동작을 하위 클래스가 물려받아 재사용성과 확장성을 높임&lt;/li&gt;
&lt;li data-end=&quot;598&quot; data-start=&quot;513&quot;&gt;다형성(Polymorphism)&lt;br /&gt;동일한 인터페이스나 메서드가 여러 클래스에서 &lt;b&gt;다르게 동작&lt;/b&gt;할 수 있게 함 (오버라이딩, 오버로딩)&lt;/li&gt;
&lt;li data-end=&quot;665&quot; data-start=&quot;600&quot;&gt;추상화(Abstraction)&lt;br /&gt;복잡한 내용을 숨기고, 필수적인 요소만 외부에 제공하여 복잡도를 줄임&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;예시:&lt;/p&gt;
&lt;pre id=&quot;code_1746172889480&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Adder {
  constructor(a, b) {
    this.a = a;
    this.b = b;
  }

  add() {
    return this.a + this.b;
  }
}

// 객체 생성
const myAdder = new Adder(3, 5);

// 메서드 호출
const result = myAdder.add(); // 8
console.log(result);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 설명한 함수형 프로그래밍과 다른 점이 확실히 보일 것입니다. 객체 지향 프로그래밍에서는 하나의 역할과 책임을 담당하는 class를 만들기 때문에 위와 같이 Adder라는 클래스를 만들고 해당 클래스 내부에 add라는 메서드를 만드는 방식을 이용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;함수형 프로그래밍에 대한 오해&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 함수형(or 절차지향) 프로그래밍의 반대는 객체지향 프로그래밍이다?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수형 프로그래밍과 객체 지향 프로그래밍은 정반대의 개념이라고 생각하시는 분들이 많습니다. 혹은 절차 지향 프로그래밍과 객체 지향 프로그래밍이 정반대 개념이라고 생각하시는 분들도 있습니다. 각 프로그래밍 철학이 서로 다른 관점을 가지고 있는 것은 맞지만,&lt;u&gt; 정확히 정반대의 개념을 가지고 있어 혼용될 수 없다는 이야기는 틀렸다&lt;/u&gt;고 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 객체 지향 프로그래밍은 객체(클래스)라는 존재를 이용해 객체 간 관계를 중심으로 코드를 작성하겠다는 뜻입니다. 이때 전체적인 도메인 모델링만 객체 지향 프로그래밍 관점을 활용하고, 객체에 대한 데이터 처리 로직 과정에는 함수형 프로그래밍을 적극 활용하면 객체 지향 프로그래밍과 함수형 프로그래밍을 동시에 사용하는 것이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. map, filter 같은 것만 사용하면 함수형 프로그래밍인가요?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;map이나 filter와 같은 함수를 사용해도 내부에서 객체의 프로퍼티를 직접 수정하는 경우는 이를 함수형 프로그래밍이라고 할 수 없습니다. 간단한 예시입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746264885742&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ❌ 불변성 위반 예시
const users = [{ name: &quot;Alice&quot;, active: true }];
const deactivated = users.map(u =&amp;gt; {
  u.active = false; // 원본 변경!
  return u;
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시에서는 users라는 배열을 순회하면서 active 프로퍼티 값을 바꾸도록 설정되어 있습니다. 이는 함수형 프로그래밍의 철칙인 불변성(immutability)를 위배하므로 함수형 프로그래밍이라고 볼 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. Java, Python, Javascript로 함수형 프로그래밍이 가능한가요?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 언어로도 함수형 프로그래밍이 가능은 합니다. 하지만 여기서 조심해야할 것은 위 언어는 &lt;b&gt;함수형 언어&lt;/b&gt;는 아니라는 점입니다. 즉, 함수형 프로그래밍을 위한 기능을 지원하지만 제한적이라는 것입니다. 더 확실한 함수형 프로그래밍을 위해서는 Haskell, Elm 같은 함수형 프로그래밍에 최적화된 언어를 사용하는 것이 좋습니다.&lt;/p&gt;</description>
      <category>CS 상식/소프트웨어 공학</category>
      <category>FP</category>
      <category>OOP</category>
      <category>객체 지향 프로그래밍</category>
      <category>절차 지향 프로그래밍</category>
      <category>함수형 프로그래밍</category>
      <author>SparkIT</author>
      <guid isPermaLink="true">https://sparkit.tistory.com/89</guid>
      <comments>https://sparkit.tistory.com/89#entry89comment</comments>
      <pubDate>Sat, 3 May 2025 18:41:53 +0900</pubDate>
    </item>
    <item>
      <title>[JS] 자바스크립트 호이스팅 쉽게 이해하기</title>
      <link>https://sparkit.tistory.com/88</link>
      <description>&lt;p data-end=&quot;156&quot; data-start=&quot;110&quot; data-ke-size=&quot;size16&quot;&gt;자바스크립트를 공부하는 과정에서 자주 등장하는 개념 중 하나가 바로 &lt;b&gt;호이스팅(Hoisting)&lt;/b&gt;입니다. 호이스팅은 자바스크립트의 &lt;u&gt;변수 선언과 함수 선언이 코드 실행 전에 &quot;끌어 올려진다&quot;는 특성&lt;/u&gt;을 의미합니다. 이 글에서 호이스팅이 무엇인지, 어떻게 동작하는지, 그리고 이를 어떻게 다뤄야 하는지에 대해 설명하겠습니다.&lt;/p&gt;
&lt;p data-end=&quot;156&quot; data-start=&quot;110&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;156&quot; data-start=&quot;110&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;156&quot; data-start=&quot;110&quot; data-ke-size=&quot;size23&quot;&gt;  호이스팅이란?&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfLll9/btsNLstIVD7/0iWXXOilj9IVfFlCrbbHlk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfLll9/btsNLstIVD7/0iWXXOilj9IVfFlCrbbHlk/img.jpg&quot; data-alt=&quot;Hoisting 이미지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfLll9/btsNLstIVD7/0iWXXOilj9IVfFlCrbbHlk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfLll9%2FbtsNLstIVD7%2F0iWXXOilj9IVfFlCrbbHlk%2Fimg.jpg&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;1280&quot; height=&quot;720&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Hoisting 이미지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호이스팅(Hoisting)은 자바스크립트에서 &lt;u&gt;변수 선언과 함수 선언이 실행되기 전에 스코프(범위)의 최상단으로 끌어올려지는 현상&lt;/u&gt;을 말합니다. 이때 사용된 단언 hoist는 hoist crane에서 따온 것으로 hoist crane은 위 이미지에서 볼 수 있는 공사현장의 크레인을 의미합니다. 즉, 자바스크립트 엔진은 코드 실행 전에 선언된 변수와 함수들을 미리 최상단으로 끌어올려 기억해두고, 이후에 차례대로 코드를 실행하는 것입니다.&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;h3 data-end=&quot;156&quot; data-start=&quot;110&quot; data-ke-size=&quot;size23&quot;&gt;⚙️ 호이스팅 원리&lt;/h3&gt;
&lt;p data-end=&quot;223&quot; data-start=&quot;158&quot; data-ke-size=&quot;size16&quot;&gt;자바스크립트는 코드를 실행할 때, &lt;b&gt;Execution Context&lt;/b&gt;라는 것을 생성하고, 아래 2단계를 거칩니다:&lt;/p&gt;
&lt;h4 data-end=&quot;260&quot; data-start=&quot;225&quot; data-ke-size=&quot;size20&quot;&gt;1. Creation Phase (생성 단계)&lt;/h4&gt;
&lt;p data-end=&quot;319&quot; data-start=&quot;264&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 변수, 함수, 스코프 등을 메모리에 &lt;b&gt;미리 할당&lt;/b&gt;&lt;br /&gt;&amp;rarr; 이 때 &lt;b&gt;호이스팅이 발생&lt;/b&gt;&lt;/p&gt;
&lt;h4 data-end=&quot;357&quot; data-start=&quot;321&quot; data-ke-size=&quot;size20&quot;&gt;2. Execution Phase (실행 단계)&lt;/h4&gt;
&lt;p data-end=&quot;413&quot; data-start=&quot;361&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 실제 코드가 한 줄씩 실행됨&lt;br /&gt;&amp;rarr; 할당, 연산, 함수 호출 등이 이 단계에서 일어남&lt;/p&gt;
&lt;p data-end=&quot;413&quot; data-start=&quot;361&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;413&quot; data-start=&quot;361&quot; data-ke-size=&quot;size16&quot;&gt;즉, 2번 실행 단계 전에 1번 생성 단계에서 &lt;u&gt;인터프리터 형식으로 코드를 한줄한줄 실행하기 전에 변수, 함수, 스코프 등을 메모리에 미리 할당하기 때문&lt;/u&gt;에 호이스팅이라는 현상이 발생하게 됩니다.&lt;/p&gt;
&lt;p data-end=&quot;413&quot; data-start=&quot;361&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;413&quot; data-start=&quot;361&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;413&quot; data-start=&quot;361&quot; data-ke-size=&quot;size23&quot;&gt;  상세 설명&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호이스팅은 여러 상황에 따라 다르게 작동합니다. 각 상황에 따른 상세 설명은 다음과 같습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 변수&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;✅ var 변수&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;b&gt;선언&lt;/b&gt; : 호이스팅 ⭕️&lt;/li&gt;
&lt;li&gt;&lt;b&gt;초기화&lt;/b&gt; : 호이스팅 ❌&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1746112466764&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log(x); // undefined
var x = 5;
console.log(x); // 5&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 x는 변수 선언이 호이스팅 되어 코드 상단으로 끌어올려집니다. 그러나 x = 5는 호이스팅되지 않아서, x의 값은 undefined로 출력됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;✅ let, const 변수&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;b&gt;선언&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 호이스팅 ⭕️&lt;/li&gt;
&lt;li&gt;&lt;b&gt;초기화&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 호이스팅&lt;span&gt;&amp;nbsp;&lt;/span&gt;❌&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1746112505155&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log(x); // ReferenceError: Cannot access 'x' before initialization
let x = 5;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;let과 const는 var와 달리 선언된 위치로 호이스팅되지만, 초기화 전에 접근하려고 하면 ReferenceError가 발생합니다. 이를 &lt;b&gt;일시적 사각지대(Temporal Dead Zone, TDZ)&lt;/b&gt;라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 함수&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;✅ 선언식 함수&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 선언식은 전체 코드 블록이 실행되기 전에 선언부가 끌어올려지고 초기화됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746112814981&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;hello(); // &quot;Hello, World!&quot;
function hello() {
  console.log(&quot;Hello, World!&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;✅ 표현식 함수&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;함수 표현식은 변수에 함수가 할당되는 방식이기 때문에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;함수 표현식이 호이스팅되지 않습니다. 정확히 말하면 함수가 할당된 변수는 호이스팅되지만 함수 자체는 할당되지 않습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746112821928&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;hello(); // TypeError: hello is not a function
var hello = function() {
  console.log(&quot;Hello, World!&quot;);
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>백엔드/Node.js</category>
      <category>const</category>
      <category>Let</category>
      <category>var</category>
      <category>선언식</category>
      <category>자바스크립트</category>
      <category>표현식</category>
      <category>호이스팅</category>
      <author>SparkIT</author>
      <guid isPermaLink="true">https://sparkit.tistory.com/88</guid>
      <comments>https://sparkit.tistory.com/88#entry88comment</comments>
      <pubDate>Fri, 2 May 2025 00:23:58 +0900</pubDate>
    </item>
    <item>
      <title>ESLint, Prettier 기초부터 실전 적용까지 - Node 개발자 필수 상식</title>
      <link>https://sparkit.tistory.com/87</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;개요&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;353&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dnXRhs/btsNJt8YoTQ/KaF6GBxtwwtWlsfCZhKAQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dnXRhs/btsNJt8YoTQ/KaF6GBxtwwtWlsfCZhKAQ1/img.png&quot; data-alt=&quot;prettier, eslint 로고&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dnXRhs/btsNJt8YoTQ/KaF6GBxtwwtWlsfCZhKAQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdnXRhs%2FbtsNJt8YoTQ%2FKaF6GBxtwwtWlsfCZhKAQ1%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;750&quot; height=&quot;353&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;353&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;prettier, eslint 로고&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ESLint는 정적 분석을 통해 코드 품질을 체크하는 도구로, 주로 코드의 문법적 오류나 불필요한 코드를 찾아내는 데 집중합니다. 반면, Prettier는 코드를 자동으로 포맷해서 일관된 스타일을 유지하는 데 도움을 줍니다. 그래서 &lt;u&gt;Node 개발자들이 협업할 때는 이 둘을 같이 사용해 코드 품질과 스타일을 일관성있게 유지하는 것이 일반적&lt;/u&gt;입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.3566%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 38.4495%; text-align: center;&quot;&gt;&lt;b&gt;&lt;span&gt;ESLint&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.1938%; text-align: center;&quot;&gt;&lt;b&gt;&lt;span&gt;Prettier&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.3566%; text-align: center;&quot;&gt;설명&lt;/td&gt;
&lt;td style=&quot;width: 38.4495%; text-align: center;&quot;&gt;코드 문법 오류, 버그 가능성 탐지 목적&lt;/td&gt;
&lt;td style=&quot;width: 40.1938%; text-align: center;&quot;&gt;코드 스타일 일관성 유지 목적&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.3566%; text-align: center;&quot;&gt;목적&lt;/td&gt;
&lt;td style=&quot;width: 38.4495%; text-align: center;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;코드 안정성&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.1938%; text-align: center;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;코드 가독성&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 예시를 통해 ESLint와 Prettier를 사용하는 이유를 알아보겠습니다.&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;ESLint&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용되지 않는 변수 찾기&lt;br /&gt;: 쓸데없는 변수 선언 피할 수 있음&lt;/li&gt;
&lt;li&gt;잘못된 함수 호출&lt;br /&gt;: 존재하지 않는 함수. 혹은 함수 호출 시 함수명을 잘못 사용한 부분 캐치 가능&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Prettier&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;들여쓰기 일관성 유지&lt;br /&gt;: 누구는 들여쓰기 2칸 쓰고, 누구는 들여쓰기 4칸 사용하면 코드 가독성이 너무 안 좋아짐&lt;/li&gt;
&lt;li&gt;문자열 쌍따움표 vs 홑따움표&lt;br /&gt;: 누구는 쌍따움표 사용하고, 누구는 홑따움표 사용해 문자열 표시하면 코드 가독성이 너무 안 좋아짐&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 도구에 대한 더 상세한 내용은 아래와 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ESLint&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;설치&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자바스크립트 사용 시&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1746084678777&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install --save-dev eslint&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;타입스크립트 사용 시&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1746084765954&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;설정파일&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용 가능한 eslint v9 이전 eslint 설정파일명
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;.eslintrc.js&lt;/li&gt;
&lt;li&gt;.eslintrc.json&lt;/li&gt;
&lt;li&gt;.eslintrc.yaml&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;사용 가능한&lt;span&gt; &lt;/span&gt;&lt;/span&gt;eslint v9 이후 eslint 설정파일명
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;eslint.config.js&lt;/li&gt;
&lt;li&gt;eslint.config.mjs&lt;/li&gt;
&lt;li&gt;eslint.config.cjs&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 eslint 설정 파일은 2가지 형식으로 나뉩니다. &lt;u&gt;eslint 9 버전 이전에는 .eslintrc.js/json/yaml 파일&lt;/u&gt;로 설정을 관리했지만, &lt;u&gt;eslint 9 버전 이후에는 eslint.config.js/mjs/cjs 파일&lt;/u&gt;로 설정을 관리합니다. 하지만 두 설정 파일은 사실상 같은 파일이라고 생각해도 됩니다. 약간의 구조적 차이만 존재하기 때문입니다. 아래는 이런 설정 파일에 공통적으로 들어가야할 핵심 정보 5가지 정도를 소개합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. env&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;env는 코드가 실행될 환경을 명시하는 부분입니다. 예를 들어 browser 환경에서 사용된다면 window, document 등의 전역 객체를 사용할 수 있습니다. 하지만 env에 browser 옵션이 없는데 window, document 등의 전역 객체를 사용한다면 에러로 파악될 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746080606367&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;env: {
  browser: true,
  node: true,
  es2021: true,
  jest: true
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2. extends&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;extends는 상속할 외부 규칙을 명시하는 부분입니다. 보통 다음과 같이 인기 있는 airbnb, standard 등을 활용합니다. 여러 규칙을 명시할 경우 앞에서부터 뒤로 이동하며 덮어쓰여지기 때문에 후순위 설정이 우선 적용되는 방식이 적용됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746080674202&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;extends: [
  'eslint:recommended',
  'plugin:react/recommended',
  'airbnb',
  'prettier'  // prettier와 충돌 방지용
]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3. parserOptions&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;parserOptions는 ES 버전, 모듈 시스템 등의 최신 문법에 대해 명시하는 부분입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746081476491&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;parserOptions: {
  ecmaVersion: 2021,
  sourceType: 'module',
  ecmaFeatures: {
    jsx: true
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;4. plugins&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;plugins는 플러그인을 명시하는 부분입니다. 보통 다음과 같이 react, typescript 등을 활용합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746081466933&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;plugins: ['react', '@typescript-eslint']&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;5. rules&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;rules는 코드 규칙을 명시하는 부분입니다. 만약 extends에서 명시한 외부 규칙과 다른 부분이 존재하면 rules가 우선 적용됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746081539163&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;rules: {
  'semi': ['error', 'always'],          // 세미콜론 필수
  'quotes': ['error', 'single'],        // 작은 따옴표 사용
  'no-console': 'warn',                 // console.log 경고
  'react/react-in-jsx-scope': 'off'     // React 17+ JSX에서 필요 없음
}&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;size18&quot;&gt;&lt;b&gt;자바스크립트 vs 타입스크립트 - 예시&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;eslint에서 자바스크립트 파일을 이용할 때와 타입스크립트 파일을 이용할 때 설정을 다르게 해야합니다. 자세한 내용은 다음과 같습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 96px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;text-align: center; height: 18px;&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 18px;&quot;&gt;&lt;b&gt;Javascript&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 18px;&quot;&gt;&lt;b&gt;Typescript&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;&lt;b&gt;Parser&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: left;&quot;&gt;기본 제공 (espree) 사용&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: left;&quot;&gt;@typescript-eslint/parser 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;&lt;b&gt;플러그인&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: left;&quot;&gt;필요 없음 또는 JS 전용&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: left;&quot;&gt;@typescript-eslint/eslint-plugin 필수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;&lt;b&gt;tsconfig.json&lt;/b&gt; 사용&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: left;&quot;&gt;사용 안 함&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: left;&quot;&gt;사용함 (parserOptions.project) 설정 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; text-align: center;&quot;&gt;&lt;b&gt;확장 규칙&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; text-align: left;&quot;&gt;eslint:recommended&lt;/td&gt;
&lt;td style=&quot;height: 18px; text-align: left;&quot;&gt;plugin:@typescript-eslint/recommended 등 추가 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트 .eslintrc.js 예시&lt;/p&gt;
&lt;pre id=&quot;code_1746084147246&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;module.exports = {
  env: {
    browser: true,
    es2021: true,
  },
  extends: ['eslint:recommended'],
  rules: {
    'no-console': 'warn',
  },
};&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;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;.eslintrc.js 예시&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746084164754&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;module.exports = {
  root: true,
  parser: '@typescript-eslint/parser',
  parserOptions: {
    project: './tsconfig.json',
  },
  plugins: ['@typescript-eslint'],
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended', // 기본 TS 규칙
    'plugin:@typescript-eslint/recommended-requiring-type-checking', // 타입 체크 기반 규칙
    'prettier', // Prettier와 충돌 방지
  ],
  rules: {
    '@typescript-eslint/no-floating-promises': 'error',
    '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
  },
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;명령어&lt;/h4&gt;
&lt;pre id=&quot;code_1746082264486&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npx eslint src --ext .ts,.js --fix&lt;/code&gt;&lt;/pre&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;npx eslint&lt;br /&gt;eslint 실행&lt;/li&gt;
&lt;li&gt;src&lt;br /&gt;eslint 적용할 디렉토리 경로&lt;/li&gt;
&lt;li&gt;--ext .ts,.js&lt;br /&gt;디폴트는 .js 파일에만 eslint가 적용됨. 하지만 직접 명시하여 .ts 파일만 eslint 적용하거나 .ts, .js 둘 다 eslint 적용할 수 있음&lt;/li&gt;
&lt;li&gt;--fix&lt;br /&gt;eslint 규칙에 따라 자동 수정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 위 명령어를 사용하면 &lt;u&gt;src 디렉토리 하위에 있는 .ts, .js 확장자 파일이 모두 eslint 설정에 맞게 수정&lt;/u&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;h4 data-ke-size=&quot;size20&quot;&gt;자동 적용&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 VSCode를 이용해 작업을 주로 합니다. VSCode에서 &lt;u&gt;ESLint를 손쉽게 사용하는 방법은 extension을 설치하는 것&lt;/u&gt;입니다. ESLint Extension은 VSCode에서 작업 시 파일을 저장하면 자동으로 ESLint 검가 작동하게 하거나, 에러에 대한 하이라이팅 표시를 해주는 등 ESLint 활용한 개발을 도와줍니다. 해당 익스텐션을 활용해 파일 저장할 때마다 자동으로 ESLint 검사가 실행되게 하는 방법은 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. ESLint Extension 설치&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1183&quot; data-origin-height=&quot;633&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dJYlpd/btsNHvYNPWj/p3EVg6JAp1T8evAvIHqqQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dJYlpd/btsNHvYNPWj/p3EVg6JAp1T8evAvIHqqQk/img.png&quot; data-alt=&quot;vscode eslint extension 설치&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dJYlpd/btsNHvYNPWj/p3EVg6JAp1T8evAvIHqqQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdJYlpd%2FbtsNHvYNPWj%2Fp3EVg6JAp1T8evAvIHqqQk%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;1183&quot; height=&quot;633&quot; data-origin-width=&quot;1183&quot; data-origin-height=&quot;633&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;vscode eslint extension 설치&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2. vscode 설정 추가&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;해당 Extension을 활용하기 위해서는 설정 파일을 수정해야하는데요. 이때 파일 저장 시 자동적으로 eslint 검사가 적용되도록하는 방법은 두 가지 존재합니다.&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;방법1)&lt;/b&gt; VSCode 자체 Preferences: Open User Settings (JSON) 설정&lt;br /&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;'cmd + shift + P' 입력&lt;/li&gt;
&lt;li&gt;'Preferences: Open User Settings (JSON)' 검색&lt;/li&gt;
&lt;li&gt;아래 코드 추가&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1746092654928&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  &quot;editor.codeActionsOnSave&quot;: {
    &quot;source.fixAll&quot;: &quot;explicit&quot;,
    &quot;source.fixAll.eslint&quot;: &quot;explicit&quot;
  },
  &quot;eslint.validate&quot;: [&quot;javascript&quot;, &quot;typescript&quot;],
  &quot;editor.formatOnSave&quot;: false&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;방법2) &lt;/b&gt;프로젝트 하위에 .vscode/settings.json 설정
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;프로젝트 루트 경로에 .vscode 디렉토리 추가&lt;/li&gt;
&lt;li&gt;.vscode/settings.json 추가&lt;/li&gt;
&lt;li&gt;아래 코드 settings.json에 추가&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1746092979566&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  &quot;editor.codeActionsOnSave&quot;: {
    &quot;source.fixAll&quot;: &quot;explicit&quot;,
    &quot;source.fixAll.eslint&quot;: &quot;explicit&quot;
  },
  &quot;eslint.validate&quot;: [&quot;javascript&quot;, &quot;typescript&quot;],
  &quot;editor.formatOnSave&quot;: false&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Prettier&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;설치&lt;/h4&gt;
&lt;pre id=&quot;code_1746096641822&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install --save-dev prettier&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;설정파일&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용 가능한 prettier 설정파일명
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;.prettierrc&lt;/li&gt;
&lt;li&gt;.prettierrc.json&lt;/li&gt;
&lt;li&gt;.prettierrc.yaml&lt;/li&gt;
&lt;li&gt;.prettierrc.js&lt;/li&gt;
&lt;li&gt;.prettierrc.yaml&lt;/li&gt;
&lt;li&gt;.prettierrc.config.js&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prettier 설정파일에서 설정할 수 있는 옵션들은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;printWidth&lt;br /&gt;- 설명 : 한 줄 최대 문자 수. 이를 넘기면 자동으로 개행&lt;br /&gt;- 디폴트 : 80&lt;/li&gt;
&lt;li&gt;tabWidth&lt;br /&gt;- 설명 : 한 탭 너비(공백 수)&lt;br /&gt;- 디폴트 : 2&lt;/li&gt;
&lt;li&gt;useTabs&lt;br /&gt;- 설명 : 탭을 사용 여부(or 공백을 사용)&lt;br /&gt;- 디폴트 : false&lt;/li&gt;
&lt;li&gt;semi&lt;br /&gt;- 설명 : 세미콜론 자동 추가 여부&lt;br /&gt;- 디폴트 : true&lt;/li&gt;
&lt;li&gt;singleQuote&lt;br /&gt;- 설명 : 홑따움표 사용 여부(or 쌍따움표 사용)&lt;br /&gt;- 디폴트 : false&lt;/li&gt;
&lt;li&gt;quoteProps&lt;br /&gt;- 설명 : 객체 속성 이름에 대한 따움표 적용 방식&lt;br /&gt;- 디폴트 : 'as-needed'&lt;br /&gt;- 종류&lt;br /&gt;&amp;nbsp; &amp;nbsp; &quot;as-needed&quot;: 필요한 경우에만 따옴표 사용&lt;br /&gt;&amp;nbsp; &amp;nbsp; &quot;consistent&quot;: 모든 속성에 따옴표를 일관되게 사용&lt;br /&gt;&amp;nbsp; &amp;nbsp; &quot;always&quot;: 모든 속성에 따옴표 사용&lt;/li&gt;
&lt;li&gt;jsxSingleQuote&lt;br /&gt;- 설명 : JSX 속성에 홑따움표 사용 여부(or 쌍따움표 사용)&lt;br /&gt;- 디폴트 : false&lt;/li&gt;
&lt;li&gt;trailingComma&lt;br /&gt;- 설명 : 콤마를 통해 연속되는 값 마지막에 콤마 추가 여부&lt;br /&gt;- 디폴트 : &quot;none&quot;&lt;br /&gt;- 종류&lt;br /&gt;&amp;nbsp; &amp;nbsp; &quot;none&quot;: 추가하지 않음&lt;br /&gt;&amp;nbsp; &amp;nbsp; &quot;es5&quot;: ES5 구문에서만 끝에 콤마 추가&lt;br /&gt;&amp;nbsp; &amp;nbsp; &quot;all&quot;: 모든 곳 끝에 콤마 추가&lt;/li&gt;
&lt;li&gt;bracketSpacing&lt;br /&gt;- 설명 : 중괄호와 속성 사이 공백 추가 여부&lt;br /&gt;- 디폴트 : true&lt;/li&gt;
&lt;li&gt;endOfLine&lt;br /&gt;- 설명 : 파일 줄 바꿈 문자 처리 설정&lt;br /&gt;- 디폴트 : &quot;auto&quot;&lt;br /&gt;- 종류&lt;br /&gt;&amp;nbsp; &amp;nbsp; &quot;auto&quot;: 운영체제애 맞게 변환&lt;br /&gt;&amp;nbsp; &amp;nbsp; &quot;lf&quot;: \n 사용 ( 리눅스, 맥OS )&lt;br /&gt;&amp;nbsp; &amp;nbsp; &quot;crlf&quot;: \r\n 사용 ( 윈도우 )&lt;br /&gt;&amp;nbsp; &amp;nbsp; &quot;cr&quot;: \r 사용 (예전 맥OS)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1746096676981&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// .prettierrc 예시

{
  &quot;semi&quot;: true,
  &quot;singleQuote&quot;: true,
  &quot;tabWidth&quot;: 2,
  &quot;trailingComma&quot;: &quot;all&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;명령어&lt;/h4&gt;
&lt;pre id=&quot;code_1746096732386&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npx prettier &quot;src/**/*&quot; --write&lt;/code&gt;&lt;/pre&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;npx prettier&lt;br /&gt;prettier 실행&lt;/li&gt;
&lt;li&gt;&quot;src/**/*&quot;&lt;br /&gt;prettier 적용할 디렉토리 경로. src 하위 모든 파일을 타겟으로 지정한다는 의미&lt;/li&gt;
&lt;li&gt;--write&lt;br /&gt;prettier 규칙에 따라 자동 수정&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;즉, 위 명령어를 사용하면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;src 디렉토리 하위에 있는 파일이 모두 prettier 설정에 맞게 포맷팅&lt;/u&gt;됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;자동 적용&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. Prettier Extension 설치&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1255&quot; data-origin-height=&quot;635&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/700Ms/btsNIjcAsR3/9qYH8vUf18jz7dt6H7RfA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/700Ms/btsNIjcAsR3/9qYH8vUf18jz7dt6H7RfA0/img.png&quot; data-alt=&quot;vscode prettier extension 설치&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/700Ms/btsNIjcAsR3/9qYH8vUf18jz7dt6H7RfA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F700Ms%2FbtsNIjcAsR3%2F9qYH8vUf18jz7dt6H7RfA0%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;1255&quot; height=&quot;635&quot; data-origin-width=&quot;1255&quot; data-origin-height=&quot;635&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;vscode prettier extension 설치&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. &lt;b&gt;vscode 설정 추가&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 ESLint와 동일하게 해당 설정 파일은 프로젝트별 .vscode/settings.json 에 넣을 수도 있고 VSCode 자체 settings.json에 넣을 수도 있습니다. 각자 상황에 맞게 아래 내용을 추가해줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746096572644&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;editor.defaultFormatter&quot;: &quot;esbenp.prettier-vscode&quot;,
  &quot;editor.formatOnSave&quot;: true
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ESLint, Prettier 충돌 해결하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;eslint와 prettier에는 중첩되는 설정이 몇 가지 존재합니다. 이로 인해서 두 설정 간 충돌이 발생하기도 합니다. 충돌이 발생할 수 있는 부분은 다음과 같습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;ESLint&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;Prettier&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;세미콜론 사용 여부&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;&lt;b&gt;✅ (semi)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;&lt;b&gt;✅&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;&lt;b&gt;둘 다 설정 가능하지만 충돌 위험&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;문자열 따옴표 (single/double)&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;&lt;b&gt;✅ (quotes)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;&lt;b&gt;✅&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;&lt;b&gt;둘 다 강제할 수 있음&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;들여쓰기&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;&lt;b&gt;✅ (indent)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;&lt;b&gt;✅&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;&lt;b&gt;기본 설정이 다르면 충돌 가능&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;객체 속성 끝에 콤마 사용 (trailingComma)&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;&lt;b&gt;✅&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;&lt;b&gt;✅&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;&lt;b&gt;스타일 충돌 가능&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;max line length&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;&lt;b&gt;✅ (max-len)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;&lt;b&gt;✅ (printWidth)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;&lt;b&gt;출력 위치가 다를 수 있음&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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;u&gt;Prettier가 담당하는 영역에 대해 ESLint 규칙을 끄는 것&lt;/u&gt;입니다. 이를 위해 보통 &lt;b&gt;eslint-config-prettier&lt;/b&gt;를 많이 사용합니다. 충돌 해결 과정에 대해 간략하게 소개합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. eslint-config-prettier 설치&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746093565740&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install --save-dev eslint-config-prettier&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;size18&quot;&gt;&lt;b&gt;2. eslint 설정 파일 내용 추가&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746093665305&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    plugins: ['@typescript-eslint/eslint-plugin'], // ✅ 이를 통해 충돌 해결
    extends: [
        'plugin:@typescript-eslint/recommended',
        'plugin:prettier/recommended', // ✅ 이를 통해 충돌 해결
    ],&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS 상식/소프트웨어 공학</category>
      <category>eslint</category>
      <category>JavaScript</category>
      <category>prettier</category>
      <category>TypeScript</category>
      <category>협업</category>
      <author>SparkIT</author>
      <guid isPermaLink="true">https://sparkit.tistory.com/87</guid>
      <comments>https://sparkit.tistory.com/87#entry87comment</comments>
      <pubDate>Thu, 1 May 2025 16:34:57 +0900</pubDate>
    </item>
    <item>
      <title>[TypeORM] connection pool size 조정해서 데이터베이스 작업 효율 높이기</title>
      <link>https://sparkit.tistory.com/85</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;커넥션 테스트 준비&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(현재 글은 typeorm 0.3.21 버전, mysql 8.0 버전을 토대로 작성하였습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 NestJS 프레임워크에서 typeorm을 활용하여 데이터베이스 작업을 진행하고 있습니다. 그리고 typeorm과 관련된 설정에서 connection pool과 관련된 설정은 어떠한 것도 수정하지 않았습니다. 이때 &lt;u&gt;typeorm에서 데이터베이스와 연결되는 커넥션 수가 궁금&lt;/u&gt;하여 다음과 같은 테스트를 진행하였습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;커넥션 수 확인&lt;/li&gt;
&lt;li&gt;nest js &lt;b&gt;어플리케이션 실행&lt;/b&gt; 후 커넥션 수 확인&lt;/li&gt;
&lt;li&gt;nest js 서비스 코드 중 데이터베이스에 &lt;b&gt;한 번 CRUD&lt;/b&gt; 작업을 진행하는 메서드 실행 후 커넥션 확인&lt;/li&gt;
&lt;li&gt;nest js 서비스 코드 중 데이터베이스에 &lt;b&gt;여러 번 CRUD&lt;/b&gt; 작업을 진행하는 메서드 실행 후 커넥션 확인&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 사용한 서비스 코드는 다음과 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1745136501687&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 서비스 코드 중 일부

async updateTest() {
	// 데이터베이스에 데이터 수정하는 로직
}
    
async concurrencyProblem() {
    await Promise.all([
    	this.updateTest(),
        this.updateTest(),
        	... // 20번 반복
    ]);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 코드에서 concurrencyProblem 메서드를 실행해서 데이터베이스 작업인 updateTest 메서드를 여러번 실행시켰습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;커넥션 테스트 결과&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 결과는 다음과 같습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 커넥션 수 확인&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커넥션 수는 아래 쿼리를 통해 확인했습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1745136848535&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SHOW PROCESSLIST;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-04-20 오후 5.04.58.png&quot; data-origin-width=&quot;911&quot; data-origin-height=&quot;181&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dS0Ya2/btsNrwydRdp/Zcjcs53TNjkCkL1d4zKQ50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dS0Ya2/btsNrwydRdp/Zcjcs53TNjkCkL1d4zKQ50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dS0Ya2/btsNrwydRdp/Zcjcs53TNjkCkL1d4zKQ50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdS0Ya2%2FbtsNrwydRdp%2FZcjcs53TNjkCkL1d4zKQ50%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;911&quot; height=&quot;181&quot; data-filename=&quot;스크린샷 2025-04-20 오후 5.04.58.png&quot; data-origin-width=&quot;911&quot; data-origin-height=&quot;181&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 쿼리로 실행된 프로세스 하나만 존재하는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. nest js&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;어플리케이션 실행&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;후 커넥션 수 확인&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;912&quot; data-origin-height=&quot;170&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dmFs76/btsNrxqjpT0/SxIGlmHOVl4DnbcSPAM1n0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dmFs76/btsNrxqjpT0/SxIGlmHOVl4DnbcSPAM1n0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dmFs76/btsNrxqjpT0/SxIGlmHOVl4DnbcSPAM1n0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdmFs76%2FbtsNrxqjpT0%2FSxIGlmHOVl4DnbcSPAM1n0%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;912&quot; height=&quot;170&quot; data-origin-width=&quot;912&quot; data-origin-height=&quot;170&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nest js 어플리케이션 실행 후에는 typeorm으로 인해 커넥션이 하나 생성되면서 프로세스도 하나 추가된 모습을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. nest js 서비스 코드 중 데이터베이스에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;한 번 CRUD&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;작업을 진행하는 메서드 실행 후 커넥션 확인&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;911&quot; data-origin-height=&quot;161&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7AGHu/btsNscTyEFN/j4Abf2aB41m8Y8zkvqHJ40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7AGHu/btsNscTyEFN/j4Abf2aB41m8Y8zkvqHJ40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7AGHu/btsNscTyEFN/j4Abf2aB41m8Y8zkvqHJ40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7AGHu%2FbtsNscTyEFN%2Fj4Abf2aB41m8Y8zkvqHJ40%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;911&quot; height=&quot;161&quot; data-origin-width=&quot;911&quot; data-origin-height=&quot;161&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nest js에서 특정 API를 호출해 서비스 코드에 설정한 데이터베이스 관련 CRUD 작업을 한 번 실행합니다. 그럼 위와 같이 이미 nest js 어플리케이션 실행 시 생성된 커넥션을 그대로 활용하기 때문에 추가적인 프로세스는 생성되지 않은 모습을 확인할 수 있습니다. 단순히 해당 커넥션의 Time 부분이 다시 0초로 바뀌었다가 카운트되는 모습을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4. nest js 서비스 코드 중 데이터베이스에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;여러 번 CRUD&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;작업을 진행하는 메서드 실행 후 커넥션 확인&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;918&quot; data-origin-height=&quot;517&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckhjCz/btsNtdwU4on/iMTvg2zU2626NL2nqfq6K0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckhjCz/btsNtdwU4on/iMTvg2zU2626NL2nqfq6K0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckhjCz/btsNtdwU4on/iMTvg2zU2626NL2nqfq6K0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckhjCz%2FbtsNtdwU4on%2FiMTvg2zU2626NL2nqfq6K0%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;918&quot; height=&quot;517&quot; data-origin-width=&quot;918&quot; data-origin-height=&quot;517&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번엔 nest js에서 데이터베이스 작업을 20번 연속해서 실행하는 메서드에 요청을 보냅니다. 그럼 위와 같이 &lt;b&gt;총 10개&lt;/b&gt;의 데이터베이스 커넥션이 생성된 모습을 확인할 수 있습니다. 이때 궁금한 점이 생깁니다. &lt;u&gt;왜 20번 요청을 보냈는데 커넥션 수는 10개밖에 생기지 않을까요?&lt;/u&gt; 이는 typeorm이 사용하는 mysql2 라이브러리의 디폴트 설정인 것으로 추정됩니다. 이때 더 많은 커넥션이 필요하다면 어떻게 해야할까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;커넥션 관리하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;typeorm에서는 &lt;b&gt;기본적으로 1개의 커넥션만 생성&lt;/b&gt;한 채로 이후 추가 커넥션이 필요할 때마다 커넥션을 추가하는 것으로 보입니다. 그리고 이때 &lt;b&gt;최대로 늘어날 수 있는 커넥션 수는 10개&lt;/b&gt;로 보입니다.( typeorm 0.3.21, mysql 8.0 사용 시 ) 이때 최대 커넥션 수를 수정하고 싶다면 아래와 같은 옵션을 사용하면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1745137783926&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;TypeOrmModule.forRootAsync({
    imports: [ConfigModule],
    inject: [ConfigService],
    useFactory: async (configService: ConfigService) =&amp;gt; ({
        type: 'mysql',
        host: configService.get&amp;lt;string&amp;gt;('MYSQL_HOST'),
        port: configService.get&amp;lt;number&amp;gt;('MYSQL_PORT'),
        username: configService.get&amp;lt;string&amp;gt;('MYSQL_USERNAME'),
        password: configService.get&amp;lt;string&amp;gt;('MYSQL_PASSWORD'),
        database: configService.get&amp;lt;string&amp;gt;('MYSQL_DATABASE'),
			
            ...
            
        // ✅ 커넥션 수 설정하기!    
        extra: {
            connectionLimit: 20,
        },
    }),
}),&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위처럼 `extra` 옵션을 통해 최대 커넥션 수를 설정할 수 있습니다.&lt;/p&gt;</description>
      <category>데이터베이스, ORM/TypeORM</category>
      <category>connection</category>
      <category>Connection Pool</category>
      <category>MySQL</category>
      <category>typeorm</category>
      <category>커넥션</category>
      <author>SparkIT</author>
      <guid isPermaLink="true">https://sparkit.tistory.com/85</guid>
      <comments>https://sparkit.tistory.com/85#entry85comment</comments>
      <pubDate>Sun, 20 Apr 2025 17:31:03 +0900</pubDate>
    </item>
  </channel>
</rss>