<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>E-room Achievement Logs</title>
    <link>https://e-room.tistory.com/</link>
    <description>나의 성취 기록들</description>
    <language>ko</language>
    <pubDate>Fri, 10 Apr 2026 02:23:24 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>E-room</managingEditor>
    <image>
      <title>E-room Achievement Logs</title>
      <url>https://tistory1.daumcdn.net/tistory/5435075/attach/4018a86c901944c1b12dee6e19efa3f9</url>
      <link>https://e-room.tistory.com</link>
    </image>
    <item>
      <title>MySQL Null 과 함께 Unique</title>
      <link>https://e-room.tistory.com/226</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;특정 데이터가 삭제되지 않았을 때는 중복을 허용하지 않고, 삭제(논리)된 경우에는 중복이 가능하게 요청을 받았다.&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 email이라는 컬럼이 위와 같이 되어야 한다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'test@test.com'이 이미 사용되고 있다. 그렇다면 더 이상 다른 사용자는 해당 이메일을 사용할 수 없다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wHuUZ/btsHbeiUg6k/GAVvTUvFKQ4mgANobjF921/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wHuUZ/btsHbeiUg6k/GAVvTUvFKQ4mgANobjF921/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wHuUZ/btsHbeiUg6k/GAVvTUvFKQ4mgANobjF921/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwHuUZ%2FbtsHbeiUg6k%2FGAVvTUvFKQ4mgANobjF921%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;597&quot; height=&quot;229&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;300&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 해당 로우가 삭제되었다면, 즉, deleted_at이 기록된 경우에는 더 이상 email은 존재하지 않는 것으로 간주되어 중복이 가능하도록 설정해야 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;758&quot; data-origin-height=&quot;280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mf5yp/btsHb98SU74/EkIVzNKnnGVKUHrsiN8Nsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mf5yp/btsHb98SU74/EkIVzNKnnGVKUHrsiN8Nsk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mf5yp/btsHb98SU74/EkIVzNKnnGVKUHrsiN8Nsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmf5yp%2FbtsHb98SU74%2FEkIVzNKnnGVKUHrsiN8Nsk%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;582&quot; height=&quot;215&quot; data-origin-width=&quot;758&quot; data-origin-height=&quot;280&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;그냥 deleted_at과 원하는 컬럼을 묶어서 UNIQUE로 지정하면 되지 않을까?&lt;/h2&gt;
&lt;pre id=&quot;code_1714831842536&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- email과 deleted_at을 묶어서 유니크로 지정
ALTER TABLE users
ADD CONSTRAINT UK_email_deleted_at UNIQUE(email, deleted_at);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;648&quot; data-origin-height=&quot;368&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vXv25/btsHcSyQGsr/v9jERetnq8izIkS7j09ekK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vXv25/btsHcSyQGsr/v9jERetnq8izIkS7j09ekK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vXv25/btsHcSyQGsr/v9jERetnq8izIkS7j09ekK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvXv25%2FbtsHcSyQGsr%2Fv9jERetnq8izIkS7j09ekK%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;491&quot; height=&quot;279&quot; data-origin-width=&quot;648&quot; data-origin-height=&quot;368&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안타깝게도 MySQL에서는 NULL은 무시해 버리게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 어떻게 해야 할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;가상컬럼을 이용해 보자&lt;/h2&gt;
&lt;pre id=&quot;code_1714832298035&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- deleted_at을 대신할 컬럼 생성
ALTER TABLE users
ADD not_archived BOOLEAN
GENERATED ALWAYS AS (IF(deleted_at IS NULL, TRUE, NULL)) VIRTUAL;

-- email과 함께 UNIQUE로 묶기
ALTER TABLE users
ADD CONSTRAINT UK_email_not_archived UNIQUE(email, not_archived);&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;쿼리 실행 중에 자동으로 deleted_at의 값을 기반으로 not_archived라는 컬럼을 임시로 생성하고 해당 컬럼을 가상컬럼으로 지정해 주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 해당 컬럼을 email과 함께 UNIQUE로 묶어 주었다.&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;그냥 쉽게 말해 deleted_at이라는 컬럼이 NULL일 때를 대신해서 계산될 컬럼을 하나 추가했다고 보면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 다시 중복된 email 삽입을 시도해 보면 에러가 발생한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;416&quot; data-origin-height=&quot;152&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dJvNL3/btsHbo6Qzex/5KLsZI3yZc6XLPDOItMxl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dJvNL3/btsHbo6Qzex/5KLsZI3yZc6XLPDOItMxl0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dJvNL3/btsHbo6Qzex/5KLsZI3yZc6XLPDOItMxl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdJvNL3%2FbtsHbo6Qzex%2F5KLsZI3yZc6XLPDOItMxl0%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;416&quot; height=&quot;152&quot; data-origin-width=&quot;416&quot; data-origin-height=&quot;152&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중복된 email을 논리삭제 후 다시 시도해보면 정상적으로 들어가는 것을 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;148&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdNBYo/btsHbrI1Hdd/SawhaTBdIxNY1SVP24QgMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdNBYo/btsHbrI1Hdd/SawhaTBdIxNY1SVP24QgMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdNBYo/btsHbrI1Hdd/SawhaTBdIxNY1SVP24QgMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdNBYo%2FbtsHbrI1Hdd%2FSawhaTBdIxNY1SVP24QgMK%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;966&quot; height=&quot;148&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;148&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;가성컬럼을 deleted_at이 NULL이 아닐 때 NULL이 되게 한 이유?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 개의 컬럼을 유니크로 묶을 경우 NULL을 무시하기 때문에 아래와 같이 삭제되었을 때 중복이 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;968&quot; data-origin-height=&quot;238&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRrFSx/btsHbNSAsOt/G9BGAT7nEDS5bQXJSWIvD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRrFSx/btsHbNSAsOt/G9BGAT7nEDS5bQXJSWIvD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRrFSx/btsHbNSAsOt/G9BGAT7nEDS5bQXJSWIvD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRrFSx%2FbtsHbNSAsOt%2FG9BGAT7nEDS5bQXJSWIvD1%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;968&quot; height=&quot;238&quot; data-origin-width=&quot;968&quot; data-origin-height=&quot;238&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ps) 기존 deleted_at과 함께 지정한 유니크키 삭제를 잊지 말자&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_1714834058964&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;Soft Delete and Unique Constraint&quot; data-og-description=&quot;When using the soft delete mechanism on the database, you might run into a situation where a record with a unique constraint was deleted&amp;hellip;&quot; data-og-host=&quot;gusiol.medium.com&quot; data-og-source-url=&quot;https://gusiol.medium.com/soft-delete-and-unique-constraint-da94b41cff62&quot; data-og-url=&quot;https://gusiol.medium.com/soft-delete-and-unique-constraint-da94b41cff62&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kv2lM/hyVZrf88iN/HDFJkb1P4BBZunrzdAnoHk/img.png?width=1200&amp;amp;height=327&amp;amp;face=0_0_1200_327,https://scrap.kakaocdn.net/dn/eilQ4n/hyVZs0oi5F/AIkidc9oqk0Edbklm28IP0/img.png?width=1358&amp;amp;height=725&amp;amp;face=0_0_1358_725,https://scrap.kakaocdn.net/dn/JyZRT/hyVZqBvuq0/wxmoJZgE9U8YkF1OJo8Mi1/img.png?width=1358&amp;amp;height=720&amp;amp;face=0_0_1358_720&quot;&gt;&lt;a href=&quot;https://gusiol.medium.com/soft-delete-and-unique-constraint-da94b41cff62&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://gusiol.medium.com/soft-delete-and-unique-constraint-da94b41cff62&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kv2lM/hyVZrf88iN/HDFJkb1P4BBZunrzdAnoHk/img.png?width=1200&amp;amp;height=327&amp;amp;face=0_0_1200_327,https://scrap.kakaocdn.net/dn/eilQ4n/hyVZs0oi5F/AIkidc9oqk0Edbklm28IP0/img.png?width=1358&amp;amp;height=725&amp;amp;face=0_0_1358_725,https://scrap.kakaocdn.net/dn/JyZRT/hyVZqBvuq0/wxmoJZgE9U8YkF1OJo8Mi1/img.png?width=1358&amp;amp;height=720&amp;amp;face=0_0_1358_720');&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;Soft Delete and Unique Constraint&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;When using the soft delete mechanism on the database, you might run into a situation where a record with a unique constraint was deleted&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;gusiol.medium.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;</description>
      <category>개발일지/DataBase</category>
      <category>column</category>
      <category>constraint</category>
      <category>mysql</category>
      <category>null</category>
      <category>unique</category>
      <category>virtual</category>
      <category>가상컬럼</category>
      <category>복합유니크키</category>
      <author>E-room</author>
      <guid isPermaLink="true">https://e-room.tistory.com/226</guid>
      <comments>https://e-room.tistory.com/226#entry226comment</comments>
      <pubDate>Sat, 4 May 2024 23:49:40 +0900</pubDate>
    </item>
    <item>
      <title>Prisma vs TypeORM 벤치마크</title>
      <link>https://e-room.tistory.com/225</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;새로운 프로젝트를 시작하기에 앞서 Nest.js, MySQL과 함께 사용할 ORM을 선택해야 하는 상황이 왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;다양한 ORM이 존재하지만 Prisma, TypeORM 둘 중 하나를 선택하기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;많은 글에서 두 ORM의 장단점을 비교하고 있지만, 성능 비교는 많지 않았고, 그마저도 최신자료는 없었다.&lt;/p&gt;
&lt;figure id=&quot;og_1712641894236&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;Benchmark: Prisma VS TypeORM&quot; data-og-description=&quot;Prisma and TypeORM are possibly the main ORM choices for the JavaScript ecosystem in 2022, so I have...&quot; data-og-host=&quot;dev.to&quot; data-og-source-url=&quot;https://dev.to/josethz00/benchmark-prisma-vs-typeorm-3873&quot; data-og-url=&quot;https://dev.to/josethz00/benchmark-prisma-vs-typeorm-3873&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b2H2Bt/hyVMWmDbrz/zsMpLTK5DGTBkRjyvcrpKK/img.png?width=1000&amp;amp;height=500&amp;amp;face=0_0_1000_500,https://scrap.kakaocdn.net/dn/umu9c/hyVMTKdwqy/dik4R2pBkkZTUP4kaLwnh1/img.png?width=1000&amp;amp;height=420&amp;amp;face=0_0_1000_420,https://scrap.kakaocdn.net/dn/vKGs2/hyVMXTmRic/pMCgXC1AuiLJPJccDwLj8K/img.png?width=505&amp;amp;height=289&amp;amp;face=0_0_505_289&quot;&gt;&lt;a href=&quot;https://dev.to/josethz00/benchmark-prisma-vs-typeorm-3873&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dev.to/josethz00/benchmark-prisma-vs-typeorm-3873&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b2H2Bt/hyVMWmDbrz/zsMpLTK5DGTBkRjyvcrpKK/img.png?width=1000&amp;amp;height=500&amp;amp;face=0_0_1000_500,https://scrap.kakaocdn.net/dn/umu9c/hyVMTKdwqy/dik4R2pBkkZTUP4kaLwnh1/img.png?width=1000&amp;amp;height=420&amp;amp;face=0_0_1000_420,https://scrap.kakaocdn.net/dn/vKGs2/hyVMXTmRic/pMCgXC1AuiLJPJccDwLj8K/img.png?width=505&amp;amp;height=289&amp;amp;face=0_0_505_289');&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;Benchmark: Prisma VS TypeORM&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Prisma and TypeORM are possibly the main ORM choices for the JavaScript ecosystem in 2022, so I have...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dev.to&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭐 어쨋든 참고해 보자면 위 글은 2년 전 자료이고, PostgreSQL을 사용했다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대략 요약하자면, 전반적으로 거의 비슷하나 Prisma가 근소하게 앞서고 스트레스 시나리오에서는 TypeORM이 압도적이라고 한다.&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;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;어느 진영이 더 좋다고 평소에 생각한바는 전혀 없으며, 최대한 공정하게 진행하려 노력했습니다.&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;테스트 환경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;PC - Macbook Air M2 RAM8GB&amp;nbsp; SSD512GB&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Prisma - 5.12.1&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;TypeORM - 0.3.20&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;MySQL - 8.3&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;진행 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Prisma와 TypeORM 각각의 데이터베이스를 생성하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;한쪽 먼저&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;CREATE * 5000&lt;/li&gt;
&lt;li&gt;FIND(단건) * 5000&lt;/li&gt;
&lt;li&gt;FIND(다건) * 5000&lt;/li&gt;
&lt;li&gt;UPDATE * 5000&lt;/li&gt;
&lt;li&gt;DELETE * 5000&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;모두 진행한 뒤,&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;다른 한쪽을 진행했다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;참고&lt;br /&gt;- 모든 테스트들은 값이 작을수록 좋음&lt;br /&gt;- 아래 테스트들에 등장하는 getFake~ 로 시작하는 함수들은 @faker-js/faker 패키지를 사용하여 만든 함수입니다.&lt;br /&gt;- 각각 환경 구성은 Prisma와 TypeORM의 공식문서를 참조하여 구성하였습니다.&lt;br /&gt;- 쿼리빌더와 같은 메서드는 사용하지 않았으며, 각각의 진영에서 제공하는 기본 함수들을 이용했습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;성능 테스트&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터 생성&lt;/h3&gt;
&lt;pre id=&quot;code_1712643820864&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Prisma
console.time('Create User - Prisma');
for (let i = 0; i &amp;lt; 5000; i++) {
    await this.user.create({
        data: getFakeUser()
    });
}
console.timeEnd('Create User - Prisma');&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1712644093201&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// TypeORM
console.time('Create User - TypeORM');
for (let i = 0; i &amp;lt; 5000; i++) {
    await this.userRepository.save(getFakeUser());
}
console.timeEnd('Create User - TypeORM');&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;create.png&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;1078&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bw3I3Q/btsGuLOQIV0/VHP9MADYp1dOhui0YfQ6N0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bw3I3Q/btsGuLOQIV0/VHP9MADYp1dOhui0YfQ6N0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bw3I3Q/btsGuLOQIV0/VHP9MADYp1dOhui0YfQ6N0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbw3I3Q%2FbtsGuLOQIV0%2FVHP9MADYp1dOhui0YfQ6N0%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;1448&quot; height=&quot;1078&quot; data-filename=&quot;create.png&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;1078&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터 단건 조회 by id&lt;/h3&gt;
&lt;pre id=&quot;code_1712644364239&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Prisma
console.time('Find User - Prisma');
for (let i = 0; i &amp;lt; 5000; i++) {
    await this.user.findUnique({
        where: { id: getRandomId(1, 5000) }
    });
}
console.timeEnd('Find User - Prisma');&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1712644429757&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// TypeORM
console.time('Find User - TypeORM');
for (let i = 0; i &amp;lt; 5000; i++) {
    await this.userRepository.findOneBy({ id: getRandomId(1, 5000) });
}
console.timeEnd('Find User - TypeORM');&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;getOne.png&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;1078&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HUqEt/btsGvWPUSCB/HidpXXhIKUjy3M73oTaaJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HUqEt/btsGvWPUSCB/HidpXXhIKUjy3M73oTaaJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HUqEt/btsGvWPUSCB/HidpXXhIKUjy3M73oTaaJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHUqEt%2FbtsGvWPUSCB%2FHidpXXhIKUjy3M73oTaaJ0%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;1448&quot; height=&quot;1078&quot; data-filename=&quot;getOne.png&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;1078&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;단건 조회의 경우에는 TypeORM이 압도적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터 다건 조회 by 조건&lt;/h3&gt;
&lt;pre id=&quot;code_1712644880033&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Prisma
console.time('Find Users - Prisma');
for (let i = 0; i &amp;lt; 5000; i++) {
    const query: Prisma.UserFindManyArgs = {
        where: {
            job: {
                contains: getRandomAlpha()
            }
        },
        take: 10,
        skip: 0
    }
    await this.$transaction([
        this.user.findMany(query),
        this.user.count({ where: query.where })
    ]);
}
console.timeEnd('Find Users - Prisma');&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1712644962831&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// TypeORM
console.time('Find Users - TypeORM');
for (let i = 0; i &amp;lt; 5000; i++) {
    await this.userRepository.findAndCount({
        where: {
            job: ILike(`%${getRandomAlpha()}%`)
        },
        take: 10,
        skip: 0
    });
}
console.timeEnd('Find Users - TypeORM');&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;getMany.png&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;1078&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BZ8j6/btsGwiFaxMU/19a2JKT8IsrlHECq7u5Ji1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BZ8j6/btsGwiFaxMU/19a2JKT8IsrlHECq7u5Ji1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BZ8j6/btsGwiFaxMU/19a2JKT8IsrlHECq7u5Ji1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBZ8j6%2FbtsGwiFaxMU%2F19a2JKT8IsrlHECq7u5Ji1%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;1448&quot; height=&quot;1078&quot; data-filename=&quot;getMany.png&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;1078&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;해당 테스트의 경우에는 Prisma에는 TypeORM의 findAndCount와 같은 Count쿼리를 함께 보내는 기능이 기본 메서드에 없어서 트랜잭션을 통해 진행했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그래서 결과가 차이가 심한 건가 싶어서 find만 진행하도록 변경해서 다시 요청해 보았다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;getmanyonly.png&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;1078&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1LsbA/btsGwfIsUiA/hfBxegZtVCPA0ESWTaFf7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1LsbA/btsGwfIsUiA/hfBxegZtVCPA0ESWTaFf7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1LsbA/btsGwfIsUiA/hfBxegZtVCPA0ESWTaFf7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1LsbA%2FbtsGwfIsUiA%2FhfBxegZtVCPA0ESWTaFf7K%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;1448&quot; height=&quot;1078&quot; data-filename=&quot;getmanyonly.png&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;1078&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;흠 ...&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&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;pre id=&quot;code_1712645911854&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Prisma
console.time('Update User - Prisma');
for (let i = 0; i &amp;lt; 5000; i++) {
    await this.user.update({
        select: { id: true },
        where: { id: getRandomId(1, 5000) },
        data: { name: getRandomName() }
    });
}
console.timeEnd('Update User - Prisma');&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1712646000039&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// TypeORM
console.time('Update User - TypeORM');
for (let i = 0; i &amp;lt; 5000; i++) {
    await this.userRepository.update(
        { id: getRandomId(1, 5000) },
        { name: getRandomName() }
    );
}
console.timeEnd('Update User - TypeORM');&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;update.png&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;1078&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dEn0fm/btsGu057Sow/sqW1KE8SGkWbj3Y6HEl1g1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dEn0fm/btsGu057Sow/sqW1KE8SGkWbj3Y6HEl1g1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dEn0fm/btsGu057Sow/sqW1KE8SGkWbj3Y6HEl1g1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdEn0fm%2FbtsGu057Sow%2FsqW1KE8SGkWbj3Y6HEl1g1%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;1448&quot; height=&quot;1078&quot; data-filename=&quot;update.png&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;1078&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;해당 테스트의 경우에는&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;TypeORM의 경우 UPDATE 쿼리만 보낸 후 영향을 받은 row들의 숫자만 리턴하는 식이고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Prisma는 Update 쿼리를 보낼 때, 전후로 Select 쿼리를 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그래서 차이가 심한 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;TypeORM은 데이터가 존재하지 않아도 에러를 발생시키지 않고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Prisma는 데이터가 없으면 에러를 발생시킨다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-04-09 오후 4.04.46.png&quot; data-origin-width=&quot;2458&quot; data-origin-height=&quot;192&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6J53V/btsGwfItKdx/4SWNLk7AIrPEcGXkfLKg6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6J53V/btsGwfItKdx/4SWNLk7AIrPEcGXkfLKg6k/img.png&quot; data-alt=&quot;Prisma의 로그&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6J53V/btsGwfItKdx/4SWNLk7AIrPEcGXkfLKg6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6J53V%2FbtsGwfItKdx%2F4SWNLk7AIrPEcGXkfLKg6k%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;2458&quot; height=&quot;192&quot; data-filename=&quot;스크린샷 2024-04-09 오후 4.04.46.png&quot; data-origin-width=&quot;2458&quot; data-origin-height=&quot;192&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Prisma의 로그&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터 삭제&lt;/h3&gt;
&lt;pre id=&quot;code_1712646522996&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Prisma
console.time('Delete User - Prisma');
for (let i = 1; i &amp;lt;= 5000; i++) {
    await this.user.delete({
        where: { id: i }
    });
}
console.timeEnd('Delete User - Prisma');&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1712646562092&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// TypeORM
console.time('Delete User - TypeORM');
for (let i = 1; i &amp;lt;= 5000; i++) {
    await this.userRepository.delete({ id: i });
}
console.timeEnd('Delete User - TypeORM');&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;delete.png&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;1078&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNlKEP/btsGvT6MsVt/Sf5pxqIxtyI7WStBkuBT2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNlKEP/btsGvT6MsVt/Sf5pxqIxtyI7WStBkuBT2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNlKEP/btsGvT6MsVt/Sf5pxqIxtyI7WStBkuBT2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNlKEP%2FbtsGvT6MsVt%2FSf5pxqIxtyI7WStBkuBT2k%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;1448&quot; height=&quot;1078&quot; data-filename=&quot;delete.png&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;1078&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Delete의 경우에도 Update와 비슷한 경우라고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;그 외&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;성능 외의 비교는 많은 글이 존재하기에 딱히 의미는 없으나 간략하게 적어보자면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;다운로드 추세를 보면 기존에는 TypeORM이 더 많다고 알고 있었는데, Prisma가 넘어섰다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-04-09 오후 4.21.34.png&quot; data-origin-width=&quot;1712&quot; data-origin-height=&quot;182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c2v9e6/btsGtOeAlPo/zaHvtx6wsx5fhJq2xkIROK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2v9e6/btsGtOeAlPo/zaHvtx6wsx5fhJq2xkIROK/img.png&quot; data-alt=&quot;npm 다운로드 수 (좌 Prisma 우 TypeORM)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2v9e6/btsGtOeAlPo/zaHvtx6wsx5fhJq2xkIROK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2v9e6%2FbtsGtOeAlPo%2FzaHvtx6wsx5fhJq2xkIROK%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;1712&quot; height=&quot;182&quot; data-filename=&quot;스크린샷 2024-04-09 오후 4.21.34.png&quot; data-origin-width=&quot;1712&quot; data-origin-height=&quot;182&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;npm 다운로드 수 (좌 Prisma 우 TypeORM)&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;Prisma의 경우에는 type-safety를 지원해서 잠재적인 휴먼에러를 막아준다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그리고 이번에 테스트를 진행하면서 Prisma를 처음 사용해 보았는데, 굉장히 쉽고 편리하다고 느꼈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;특히 model 정의할 때, 굉장히 깔끔했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;개인적으로 사용성 측면에서는 Prisma가 우세하다고 생각된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;복잡한 쿼리는 TypeORM이 좀 더 유연하게 작성할 수 있다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;하지만 최근 TypeORM이 0.3으로 넘어오면서 다양한 이슈가 존재하기에 잘 고려해보아야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&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;복잡한 쿼리를 많이 사용하며 성능이 중요하다 - TypeORM&lt;/li&gt;
&lt;li&gt;편리한 사용성과 낮은 진입장벽, 휴먼에러를 막아주는 게 중요하다 - Prisma&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>개발일지/Node.js</category>
      <category>Benchmark</category>
      <category>mysql</category>
      <category>nestjs</category>
      <category>ORM</category>
      <category>Prisma</category>
      <category>typeorm</category>
      <author>E-room</author>
      <guid isPermaLink="true">https://e-room.tistory.com/225</guid>
      <comments>https://e-room.tistory.com/225#entry225comment</comments>
      <pubDate>Tue, 9 Apr 2024 16:34:05 +0900</pubDate>
    </item>
    <item>
      <title>TypeORM + MySQL8 AsText does not exist</title>
      <link>https://e-room.tistory.com/224</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;개발환경&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;node: v20.10.0
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;nestjs: v10.0.0&lt;/li&gt;
&lt;li&gt;typeorm: v0.3.20&lt;/li&gt;
&lt;li&gt;mysql2: v3.9.2&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;mysql: v8.0.0&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제. AsText does not exist&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mysql의 Point 타입을 사용해서 좌표 관련 기능을 구현하던 중, 문제가 발생했다.&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;typeorm의 조회관련 메서드를 사용해 DB에 SELECT 쿼리를 보냈더니 다음과 같은 에러가 발생했다.&lt;/p&gt;
&lt;pre id=&quot;code_1710229620423&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;QueryFailedError: FUNCTION db-name.AsText does not exist&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;원인.&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대략 찾아보니 타 DB에는 공간(Spatial) 관련 타입을 처리할 때, AsText와 같은 함수가 존재해 이를 이용해서 조회를 할 수 있다고 한다.&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;그래서 Typeorm이 DB에서 해당 타입을 조회할 때, AsText를 사용하여 데이터를 가져온다.&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;하지만, Mysql 8버전에는 해당 함수가 없어서 위와 같은 오류가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결.&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색을 해보니 스택오버플로우에서 나와 같은 문제를 겪은 사람이 있었다.&lt;/p&gt;
&lt;figure id=&quot;og_1710230044820&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;Type geometry in mysql -&amp;gt; AsText doesn't exist&quot; data-og-description=&quot;I altered my db with a new column with type geometry. And then I am getting this error when I am trying to select something from my db. With select all and also with select of only one specific col...&quot; data-og-host=&quot;stackoverflow.com&quot; data-og-source-url=&quot;https://stackoverflow.com/questions/76827132/type-geometry-in-mysql-astext-doesnt-exist&quot; data-og-url=&quot;https://stackoverflow.com/questions/76827132/type-geometry-in-mysql-astext-doesnt-exist&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/le9tT/hyVxwJhQUN/pbI9TYjNkmNqkJebNyIumk/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/76827132/type-geometry-in-mysql-astext-doesnt-exist&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://stackoverflow.com/questions/76827132/type-geometry-in-mysql-astext-doesnt-exist&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/le9tT/hyVxwJhQUN/pbI9TYjNkmNqkJebNyIumk/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316');&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;Type geometry in mysql -&amp;gt; AsText doesn't exist&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;I altered my db with a new column with type geometry. And then I am getting this error when I am trying to select something from my db. With select all and also with select of only one specific col...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;stackoverflow.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;TypeOrm 모듈을 설정할 때, AsText 함수를 사용하여 조회하도록 하는 옵션을 끄면 간단히 해결된다.&lt;/p&gt;
&lt;pre id=&quot;code_1710230150511&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;TypeOrmModule.forRoot({
  legacySpatialSupport: 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;그리고 알고보니 설명서에도 친절하게 나와있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1466&quot; data-origin-height=&quot;214&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IiTyJ/btsFIkSr61P/OwAU5EdrwnAXcaBq8rgvhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IiTyJ/btsFIkSr61P/OwAU5EdrwnAXcaBq8rgvhk/img.png&quot; data-alt=&quot;MysqlConnectionOptions&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IiTyJ/btsFIkSr61P/OwAU5EdrwnAXcaBq8rgvhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIiTyJ%2FbtsFIkSr61P%2FOwAU5EdrwnAXcaBq8rgvhk%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;1466&quot; height=&quot;214&quot; data-origin-width=&quot;1466&quot; data-origin-height=&quot;214&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;MysqlConnectionOptions&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발일지/Node.js</category>
      <category>astext</category>
      <category>js</category>
      <category>legacyspatialsupport</category>
      <category>mysql</category>
      <category>nest</category>
      <category>Node</category>
      <category>typeorm</category>
      <category>문제</category>
      <category>에러</category>
      <author>E-room</author>
      <guid isPermaLink="true">https://e-room.tistory.com/224</guid>
      <comments>https://e-room.tistory.com/224#entry224comment</comments>
      <pubDate>Tue, 12 Mar 2024 17:00:09 +0900</pubDate>
    </item>
    <item>
      <title>Nestjs - Shared modules : 의존성 주입을 통하여 모듈 공유하기</title>
      <link>https://e-room.tistory.com/223</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;966&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rC2r1/btsFlSCdfv3/Pu9rVVD5Xy8hKN4xgnYDC1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rC2r1/btsFlSCdfv3/Pu9rVVD5Xy8hKN4xgnYDC1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rC2r1/btsFlSCdfv3/Pu9rVVD5Xy8hKN4xgnYDC1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrC2r1%2FbtsFlSCdfv3%2FPu9rVVD5Xy8hKN4xgnYDC1%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;1000&quot; height=&quot;966&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;966&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NestJS는 모듈과 서비스를 효과적으로 구성하고 관리할 수 있는 강력한 기능을 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 NestJS에서 서비스를 공유하는 두 가지 방법인 &lt;b&gt;의존성 주입(Dependency Injection)&lt;/b&gt;과 &lt;b&gt;모듈 임포트&lt;/b&gt;에 대해 알아보겠습니다.&lt;br /&gt;&lt;br /&gt;NestJS는 모듈과 서비스를 사용하여 애플리케이션을 구성합니다.&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;상황 가정&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Flowers와 Places라는 기능이 있다고 가정 (nest g resources flowers, nest g resources places)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. FlowersController에서 PlacesService를 사용하고 싶음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 단, PlacesService를 새로 생성하는 것이 아닌 하나의 PlacesService로 공유해서 사용해야 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;의존성 주입(Dependency Injection)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의존성 주입을 사용하면 서비스를 다른 컴포넌트로 주입할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 서비스를 공유할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1709202543880&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Controller('flowers')
export class FlowersController {
  constructor(
    private readonly flowersService: FlowersService,
    private readonly placesService: PlacesService // 의존성 주입
  ) { }
  
  ...
  
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;모듈 exports&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;PlacesModule에서 사용 중인 PlacesService를 다른 모듈에서도 사용할 수 있도록&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;exports&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;해줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1709203892451&quot; class=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;@Module({
  controllers: [PlacesController],
  providers: [PlacesService],
  exports: [PlacesService] // exports
})
export class PlacesModule {}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;모듈 imports&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PlacesService를 사용하고 싶은 다른 모듈에서 &lt;b&gt;PlacesService를 exports 한 Module인 PlacesModule을 import 해줍니다.&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;‼️ PlacesService를 사용하고 싶다고 PlacesService를 imports 하거나 providers로 등록하는 것이 아닙니다!&lt;/blockquote&gt;
&lt;pre id=&quot;code_1709202599593&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Module({
  imports: [PlacesModule], // 주의 !!
  controllers: [FlowersController],
  providers: [FlowersService],
})
export class FlowersModule {}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;사용해보기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FlowersController의 findAll을 호출 시, FlowersService.findAll과 PlacesService.findAll이 둘 다 호출되도록 하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PlacesController의 findAll을 호출 시, PlacesService.findAll만 호출되도록 구성하겠습니다.&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;Service&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1709204294482&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Injectable()
export class FlowersService {
  private count = 0; // 카운트

  findAll() {
    return `This action returns all flowers : ${++this.count}`;
  }
  
  ...
  
}

@Injectable()
export class PlacesService {
  private count = 0; // 카운트
  
  findAll() {
    return `This action returns all places : ${++this.count}`;
  }
  
  ...
  
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Controller&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1709204452271&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Controller('flowers')
export class FlowersController {
  constructor(
    private readonly flowersService: FlowersService,
    private readonly placesService: PlacesService
  ) { }

  @Get()
  findAll() {
    const flowers = this.flowersService.findAll(); // flowers의 findAll 호출
    const places = this.placesService.findAll() // places의 findAll 호출
    return `${flowers}&amp;lt;/br&amp;gt;${places}`;
  }
  ...
}

@Controller('places')
export class PlacesController {
  constructor(private readonly placesService: PlacesService) {}

  @Get()
  findAll() {
    return this.placesService.findAll(); // places의 findAll만 호출
  }
...
}&lt;/code&gt;&lt;/pre&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;테스트&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GET localhost:3000/places 두 번 호출 후,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GET localhost:3000/flowers 호출했을 때의 결과는?&lt;/p&gt;
&lt;pre id=&quot;code_1709205025811&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;This action returns all flowers : 1
This action returns all places : 3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 인스턴스를 의존성 주입을 통해 서로 공유하여 사용할 수 있으며, 이를 통해 자원 절약과 일관된 상태를 유지할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;번외&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'모듈 imports' 단계에서 imports가 아닌 providers에 등록한다면?&lt;/p&gt;
&lt;pre id=&quot;code_1709205469011&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Module({
  // imports: [PlacesModule], // 해당 코드 주석 처리
  controllers: [FlowersController],
  providers: [FlowersService, PlacesService], // PlacesService 등록
})
export class FlowersModule {}&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;p data-ke-size=&quot;size16&quot;&gt;GET localhost:3000/places&lt;span&gt;&amp;nbsp;&lt;/span&gt;두 번&lt;span&gt;&amp;nbsp;&lt;/span&gt;호출 후,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;GET localhost:3000/flowers&lt;span&gt;&amp;nbsp;&lt;/span&gt;호출했을&lt;span&gt;&amp;nbsp;&lt;/span&gt;때의&lt;span&gt;&amp;nbsp;&lt;/span&gt;결과는?&lt;/p&gt;
&lt;pre id=&quot;code_1709205610529&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;This action returns all flowers : 1
This action returns all places : 1&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;하나의 PlacesService를 공유하는 것이 아닌, 각각의 모듈에서 PlacesService를 생성하여 사용하게 된다.&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_1709217862328&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;Documentation | NestJS - A progressive Node.js framework&quot; data-og-description=&quot;Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Rea&quot; data-og-host=&quot;docs.nestjs.com&quot; data-og-source-url=&quot;https://docs.nestjs.com/modules#shared-modules&quot; data-og-url=&quot;https://docs.nestjs.com&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/8u1Eq/hyVqkbpf6A/wMBN6AQPLS6dxSbRkg9h6k/img.png?width=820&amp;amp;height=429&amp;amp;face=0_0_820_429&quot;&gt;&lt;a href=&quot;https://docs.nestjs.com/modules#shared-modules&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.nestjs.com/modules#shared-modules&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/8u1Eq/hyVqkbpf6A/wMBN6AQPLS6dxSbRkg9h6k/img.png?width=820&amp;amp;height=429&amp;amp;face=0_0_820_429');&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;Documentation | NestJS - A progressive Node.js framework&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Rea&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.nestjs.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;</description>
      <category>개발일지/Node.js</category>
      <category>controller</category>
      <category>dependency</category>
      <category>di</category>
      <category>export</category>
      <category>import</category>
      <category>Injection</category>
      <category>module</category>
      <category>nestjs</category>
      <category>nodejs</category>
      <category>Service</category>
      <author>E-room</author>
      <guid isPermaLink="true">https://e-room.tistory.com/223</guid>
      <comments>https://e-room.tistory.com/223#entry223comment</comments>
      <pubDate>Thu, 29 Feb 2024 20:22:47 +0900</pubDate>
    </item>
    <item>
      <title>TypeORM - @Column({ unique : true }) vs @Unique()</title>
      <link>https://e-room.tistory.com/222</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;nestjs + typeORM을 공부하던 중 특정 컬럼에 대해 유니크 속성을 주어야 하는데, 두 가지 방법이 보였다.&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;첫 번째는 변수에 @Column 데코레이터를 사용하는 것이고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째는 클래스에 @Unique 데코레이터를 사용하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 방법 모두 유니크 속성을 부여한다는 것은 동일하지만 약간의 차이가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유니크 속성을 &lt;b&gt;각각 부여하느냐,&lt;/b&gt; &lt;b&gt;합쳐서 부여하느냐&lt;/b&gt;의 차이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;@Column({ unique : true })&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 원하는 특정 컬럼에 유니크 속성을 주기 위해 사용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1708676602954&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    @Column({ unique: true })
    first: string;

    @Column({ unique: true })
    second: string;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1146&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PXr60/btsFeT1Nqqu/ntJOEAOnqdgATr5C1CEy50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PXr60/btsFeT1Nqqu/ntJOEAOnqdgATr5C1CEy50/img.png&quot; data-alt=&quot;@Column({ unique : true })&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PXr60/btsFeT1Nqqu/ntJOEAOnqdgATr5C1CEy50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPXr60%2FbtsFeT1Nqqu%2FntJOEAOnqdgATr5C1CEy50%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;1146&quot; height=&quot;210&quot; data-origin-width=&quot;1146&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;@Column({ unique : true })&lt;/figcaption&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;h2 data-ke-size=&quot;size26&quot;&gt;@Unique(['first', 'second'])&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두가지 컬럼에 하나의 유니크 제약조건을 부여한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 'first' 및 'second' 열의 값이 모두 동일한 행이 두 번 이상 삽입되는 것을 방지한다.(복합키)&lt;/p&gt;
&lt;pre id=&quot;code_1708678484601&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Unique(['first', 'second'])
export class User extends BaseEntity {
 ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1154&quot; data-origin-height=&quot;160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8QLa5/btsFhT0remB/WuDg7tDhh7wmKQvmGzdUkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8QLa5/btsFhT0remB/WuDg7tDhh7wmKQvmGzdUkk/img.png&quot; data-alt=&quot;@Unique&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8QLa5/btsFhT0remB/WuDg7tDhh7wmKQvmGzdUkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8QLa5%2FbtsFhT0remB%2FWuDg7tDhh7wmKQvmGzdUkk%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;1154&quot; height=&quot;160&quot; data-origin-width=&quot;1154&quot; data-origin-height=&quot;160&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;@Unique&lt;/figcaption&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;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;408&quot; data-origin-height=&quot;192&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/db3sbl/btsFdGWjVCD/ynd2gM0JTZqHSg8JlrQ5h0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/db3sbl/btsFdGWjVCD/ynd2gM0JTZqHSg8JlrQ5h0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/db3sbl/btsFdGWjVCD/ynd2gM0JTZqHSg8JlrQ5h0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdb3sbl%2FbtsFdGWjVCD%2Fynd2gM0JTZqHSg8JlrQ5h0%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;408&quot; height=&quot;192&quot; data-origin-width=&quot;408&quot; data-origin-height=&quot;192&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 사진처럼 first의 중복과 second의 각각 중복은 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 이 상태에서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;INSERT INTO users (first, second) VALUES ('a', 'b');&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿼리를 실행하게 되면 어떻게 될까?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1016&quot; data-origin-height=&quot;160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvd5Wd/btsFeqFDoXr/UL1EvZi415BGO9nFtcwnj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvd5Wd/btsFeqFDoXr/UL1EvZi415BGO9nFtcwnj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvd5Wd/btsFeqFDoXr/UL1EvZi415BGO9nFtcwnj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbvd5Wd%2FbtsFeqFDoXr%2FUL1EvZi415BGO9nFtcwnj0%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;1016&quot; height=&quot;160&quot; data-origin-width=&quot;1016&quot; data-origin-height=&quot;160&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;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Column 데코레이터에 unique: true 옵션을 주는 것은 일반적으로 유니크 속성을 부여하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Unique 데코레이터는 복합키를 생성할 때 사용한다.&lt;/p&gt;</description>
      <category>개발일지/Node.js</category>
      <category>@Column</category>
      <category>@Unique</category>
      <category>nestjs</category>
      <category>nodejs</category>
      <category>typeorm</category>
      <category>TypeScript</category>
      <category>복합키</category>
      <author>E-room</author>
      <guid isPermaLink="true">https://e-room.tistory.com/222</guid>
      <comments>https://e-room.tistory.com/222#entry222comment</comments>
      <pubDate>Fri, 23 Feb 2024 18:18:00 +0900</pubDate>
    </item>
    <item>
      <title>SQL Injection 방지 - 권장방식에는 다 이유가 있지..</title>
      <link>https://e-room.tistory.com/221</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;nodejs express 공부를 하고 있는데, 쿼리를 작성하던 중 문득 생각이 들었다.&lt;/h2&gt;
&lt;pre id=&quot;code_1706670754015&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;`
SELECT * FROM users
WHERE user_id = ?
`&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;mariadb 모듈에는 query 함수가 있는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 인자로 쿼리 문자열 받고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 인자로 쿼리의 플레이스홀더(? 부분)에 해당하는 부분을 배열 형태로 받는다.&lt;/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;/p&gt;
&lt;pre id=&quot;code_1706671265474&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;`
SELECT * FROM users
WHERE user_id = ${userId}
`&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 작성하고 두 번째 인자를 생략하면 정상적으로 작동된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 또 한편으로 드는 생각이 굳이 첫 번째 방식으로 작성하고 두 번째 인자로 배열을 받는 이유가 있지 않을까? 라는 생각이 들었다.&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;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;u&gt;그래서 검색을 좀 해보니 두번째 방식으로 작성을 하게 되면 &lt;b&gt;SQL Injection&amp;nbsp;&lt;/b&gt;공격에 취약해진다고 한다.&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역시 개발자들은 항상 코드에 이유가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;그래서 테스트를 해보았다.&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. ${변수}&lt;/h3&gt;
&lt;pre id=&quot;code_1706671918761&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 소스코드
login1 = (email, password) =&amp;gt; {
  return new Promise((resolve, reject) =&amp;gt; {
    const query = `
      SELECT * FROM users u
      WHERE email = '${email}' AND password = '${password}';
      `;
    db.query(query, (error, data) =&amp;gt; {
      if (error) reject(error);
      else resolve(data[0]);
    });
  });
}

// email과 password에 아래와 같이 입력
login1(&quot;test1&quot;, &quot;' OR '1' = '1&quot;)
    .then(result =&amp;gt; console.log(result));
    
/* 결과
{
    &quot;user_id&quot;: 1,
    &quot;email&quot;: &quot;test1&quot;,
    &quot;name&quot;: &quot;네임&quot;,
    &quot;password&quot;: &quot;$2b$10$qJ3/h/W4yiLLygW6.1xn8uI6xpbLY1/h6EpX./PkydaZ6tBd3AakC&quot;,
    &quot;created_at&quot;: &quot;2024-01-21T13:04:43.000Z&quot;,
    &quot;modified_at&quot;: &quot;2024-01-31T02:56:35.000Z&quot;
}
*/&lt;/code&gt;&lt;/pre&gt;
&lt;div style=&quot;background-color: #ffffff; color: #000000;&quot;&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;div&gt;
&lt;pre id=&quot;code_1706674171677&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 실제 데이터베이스에서 실행되는 쿼리
SELECT * FROM users u
WHERE email = 'test1' AND password = '' OR '1'  = '1';

-- 위 쿼리는 결국 아래 쿼리와 같다
SELECT * FROM users u&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2252&quot; data-origin-height=&quot;372&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uooqc/btsEa5iiM9C/fRN9APdSOqnUcf0dBZZznk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uooqc/btsEa5iiM9C/fRN9APdSOqnUcf0dBZZznk/img.png&quot; data-alt=&quot;DB에서 해당 쿼리를 실행하였을 경우 users테이블의 모든 데이터가 조회된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uooqc/btsEa5iiM9C/fRN9APdSOqnUcf0dBZZznk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fuooqc%2FbtsEa5iiM9C%2FfRN9APdSOqnUcf0dBZZznk%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;2252&quot; height=&quot;372&quot; data-origin-width=&quot;2252&quot; data-origin-height=&quot;372&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;DB에서 해당 쿼리를 실행하였을 경우 users테이블의 모든 데이터가 조회된다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;코드에서 data[0]를 통해 첫번째 데이터만 응답하도록 했기 때문에 한건만 조회가 되었지만,&amp;nbsp;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;모든 데이터에서 첫번째 데이터가 반환되게 된다.&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;2. 플레이스 홀더(공식 권장 방식)&lt;/h3&gt;
&lt;pre id=&quot;code_1706674488108&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 소스코드
const login2 = (email, password) =&amp;gt; {
    return new Promise((resolve, reject) =&amp;gt; {
        const query = `
        SELECT * FROM users u
        WHERE email = ? AND password = ?;
        `;
        db.query(query, [email, password], (error, data) =&amp;gt; {
            if (error) reject(error);
            else resolve(data[0]);
        });
    });
}

// email과 password에 아래와 같이 입력
login2(&quot;test1&quot;, &quot;' OR '1' = '1&quot;)
    .then(result =&amp;gt; console.log(result));
    
/* 결과
undefined
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드를 실행하면 데이터베이스에서는 아래와 같은 쿼리가 실행된다.&lt;/p&gt;
&lt;pre id=&quot;code_1707316787567&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 실제 데이터베이스에서 실행되는 쿼리
SELECT * FROM users u
WHERE email = 'test1' AND password = &quot;'' OR '1'  = '1'&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQL 인젝션을 위해 입력한 password부분이 하나의 문자열로 데이터베이스에서 실행이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 조건에 맞는 데이터가 없기 때문에 빈 배열을 가져오게 되고, 빈 배열의 첫 번째 데이터를 리턴하기 때문에 undefined가 반환된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;mariadb의 공식문서를 살펴보면&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1732&quot; data-origin-height=&quot;1114&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0jLkU/btsEClDs8Z0/EoS6Zaujl7KgiK9BpeXYp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0jLkU/btsEClDs8Z0/EoS6Zaujl7KgiK9BpeXYp1/img.png&quot; data-alt=&quot;https://github.com/mariadb-corporation/mariadb-connector-nodejs/blob/master/documentation/callback-api.md&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0jLkU/btsEClDs8Z0/EoS6Zaujl7KgiK9BpeXYp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0jLkU%2FbtsEClDs8Z0%2FEoS6Zaujl7KgiK9BpeXYp1%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;1732&quot; height=&quot;1114&quot; data-origin-width=&quot;1732&quot; data-origin-height=&quot;1114&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://github.com/mariadb-corporation/mariadb-connector-nodejs/blob/master/documentation/callback-api.md&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째 줄을 살펴보면 &lt;span style=&quot;background-color: #f3c000;&quot;&gt;&lt;b&gt;To avoid SQL Injection attacks,&lt;/b&gt;&lt;/span&gt; 라고 되어 있는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQL Injection attacks를 피하기 위해 라고 친절하게 적어놓았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론, 비밀번호의 경우 데이터베이스에 암호화해서 저장을 하고, 암호화 모듈에서 제공하는 함수를 이용해서 비교를 하기 때문에&amp;nbsp;위 쿼리처럼 WHERE절에 email AND password를 사용해서 로그인을 진행하는 경우는 드물다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 첫번째 방법을 사용해도 되는 거냐?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연히 절대 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서는 password에 조건문만 집어넣었지만 DELETE, DROP TABLE, UPDATE 등 다양한 쿼리를 사용해서 공격을 시도할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(이 경우에도 생각해야 할 것들이 있지만 주제와 맞지 않으므로 pass)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어쨌든 해커가 공격할 여지를 남겨주는 것은 좋은 방법이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;권장하는 방법을 쓰자.&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;전체 코드&lt;/h2&gt;
&lt;pre id=&quot;code_1707318397593&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const mariadb = require('mariadb/callback');

const DB_HOST = '호스트';
const DB_PORT = '포트';
const DB_USER = '유저';
const DB_PASSWORD = '비밀번호';
const DB_DATABASE = '데이터베이스명';

const db = mariadb.createConnection({
    host: DB_HOST,
    port: DB_PORT,
    user: DB_USER,
    password: DB_PASSWORD,
    database: DB_DATABASE,
});

// ${} 방식
const login1 = (email, password) =&amp;gt; {
    return new Promise((resolve, reject) =&amp;gt; {
        const query = `
        SELECT * FROM users u
        WHERE email = '${email}' AND password = '${password}';
        `;
        db.query(query, (error, data) =&amp;gt; {
            if (error) reject(error);
            else resolve(data[0]);
        });
    });
}
login1(&quot;test1&quot;, &quot;' OR '1' = '1&quot;)
    .then(result =&amp;gt; console.log(result));

// ? 방식
const login2 = (email, password) =&amp;gt; {
    return new Promise((resolve, reject) =&amp;gt; {
        const query = `
            SELECT * FROM users u
            WHERE email = ? AND password = ?;
            `;
        db.query(query, [email, password], (error, data) =&amp;gt; {
            if (error) reject(error);
            else resolve(data[0]);
        });
    });
}
login2(&quot;test1&quot;, &quot;' OR '1' = '1&quot;)
    .then(result =&amp;gt; console.log(result));&lt;/code&gt;&lt;/pre&gt;</description>
      <category>개발일지/Node.js</category>
      <category>Attacks</category>
      <category>Express</category>
      <category>Injection</category>
      <category>javascript</category>
      <category>mariadb</category>
      <category>nodejs</category>
      <category>SQL</category>
      <category>인젝션</category>
      <author>E-room</author>
      <guid isPermaLink="true">https://e-room.tistory.com/221</guid>
      <comments>https://e-room.tistory.com/221#entry221comment</comments>
      <pubDate>Wed, 31 Jan 2024 13:14:56 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 - 뉴스 클러스터링 [자바]</title>
      <link>https://e-room.tistory.com/219</link>
      <description>&lt;figure id=&quot;og_1701153537521&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;프로그래머스&quot; data-og-description=&quot;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/17677?language=java&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cLIKjW/hyUCaAPfey/orDRy6ikT0lSkInXP6wpK1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/hrKBh/hyUFcRik9z/s7SNWJromMcfyK5gv9svN0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/17677?language=java&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/17677?language=java&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cLIKjW/hyUCaAPfey/orDRy6ikT0lSkInXP6wpK1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/hrKBh/hyUFcRik9z/s7SNWJromMcfyK5gv9svN0/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;프로그래머스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&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;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;1. 문제 요약&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;두 문자열의 자카드 유사도를 구하시오.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;2. 접근 방법&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문자열이 주어질 때, 알파벳의 경우에만 원소로 사용한다는 점을 이용합니다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;2글자까지만 사용하기 때문에 경우의 수는 26 * 26이 됩니다.&lt;/li&gt;
&lt;li&gt;각각의 문자열을 검사하여 26 * 26의 int[][] 배열에 담아줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;문제를 읽어보면 &lt;span style=&quot;color: #000000; background-color: #f3c000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;text-align: left;&quot;&gt;교집합&amp;nbsp;&lt;/span&gt;A &amp;cap; B&lt;span style=&quot;text-align: left;&quot;&gt;는 원소 &quot;1&quot;을 min(3, 5)인 3개, 합집합&amp;nbsp;&lt;/span&gt;A &amp;cup; B&lt;span style=&quot;text-align: left;&quot;&gt;는 원소 &quot;1&quot;을 max(3, 5)인 5개 가지게 된다.&lt;/span&gt;&lt;/b&gt;&lt;/span&gt; 라고 힌트를 주고 있습니다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;이전 단계에서 생성한 배열에 힌트를 그대로 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;시간 복잡도는 문자열을 배열로 변환하는 과정에서 문자열이 한글자 늘어날때마다 1번의 연산이 추가됩니다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;즉, 문자열의 길이에 비례하므로 &lt;b&gt;O(N)&lt;/b&gt;입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;3. 자바&lt;/h2&gt;
&lt;pre id=&quot;code_1695394432492&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution {
    public int solution(String str1, String str2) {
        float intersection = 0;
        float union = 0;

		// 사용할 수 있는 모든 원소를 26 * 26 이중배열에 모두 담아준다.
        int[][] multiset1 = toMultiset(str1);
        int[][] multiset2 = toMultiset(str2);
        
        // 두 이중배열을 순회하면서 힌트를 그대로 사용해준다.
        for (int i = 0; i &amp;lt; 26; i++) {
            for (int j = 0; j &amp;lt; 26; j++) {
                intersection += Math.min(multiset1[i][j], multiset2[i][j]);
                union += Math.max(multiset1[i][j], multiset2[i][j]);
            }
        }

		// 문제에서 제시한 조건대로 정답 가공
        return union == 0
                ? 65536
                : (int) (intersection / union * 65536);
    }

    private int[][] toMultiset(String str) {
        int[][] multiset = new int[26][26];

		// 소대문자 구분하지 않으므로 하나로 통일.
        char[] arr = str.toUpperCase().toCharArray();
        for (int i = 0; i &amp;lt; arr.length - 1; i++) {
            char c1 = arr[i];
            char c2 = arr[i + 1];
            // 원소로 활용할 수 있는지 검사하여 이중배열에 담아준다.
            if (Character.isAlphabetic(c1) &amp;amp;&amp;amp; Character.isAlphabetic(c2)) {
                multiset[c1 - 65][c2 - 65]++;
            }
        }

        return multiset;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;5. 전체 코드&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/Ksiyeong/Algorithm/blob/main/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/2/17677.%E2%80%85%EF%BC%BB1%EC%B0%A8%EF%BC%BD%E2%80%85%EB%89%B4%EC%8A%A4%E2%80%85%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81/%EF%BC%BB1%EC%B0%A8%EF%BC%BD%E2%80%85%EB%89%B4%EC%8A%A4%E2%80%85%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81.java&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/Ksiyeong/Algorithm/blob/main/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/2/17677.%E2%80%85%EF%BC%BB1%EC%B0%A8%EF%BC%BD%E2%80%85%EB%89%B4%EC%8A%A4%E2%80%85%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81/%EF%BC%BB1%EC%B0%A8%EF%BC%BD%E2%80%85%EB%89%B4%EC%8A%A4%E2%80%85%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81.java&lt;/a&gt;&lt;/p&gt;</description>
      <category>개발일지/Algorithm</category>
      <category>뉴스</category>
      <category>알고리즘</category>
      <category>유사도</category>
      <category>자카드</category>
      <category>집합</category>
      <category>카카오</category>
      <category>클러스터링</category>
      <category>프로그래머스</category>
      <author>E-room</author>
      <guid isPermaLink="true">https://e-room.tistory.com/219</guid>
      <comments>https://e-room.tistory.com/219#entry219comment</comments>
      <pubDate>Tue, 28 Nov 2023 15:59:14 +0900</pubDate>
    </item>
    <item>
      <title>백준 - 11725 트리의 부모 찾기 [DFS][BFS]</title>
      <link>https://e-room.tistory.com/218</link>
      <description>&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1699154013351&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;11725번: 트리의 부모 찾기&quot; data-og-description=&quot;루트 없는 트리가 주어진다. 이때, 트리의 루트를 1이라고 정했을 때, 각 노드의 부모를 구하는 프로그램을 작성하시오.&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/11725&quot; data-og-url=&quot;https://www.acmicpc.net/problem/11725&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/SBQO8/hyUnJcopeY/uRZBqVOlpUCK1Svt26DYK0/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/11725&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/11725&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/SBQO8/hyUnJcopeY/uRZBqVOlpUCK1Svt26DYK0/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&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;11725번: 트리의 부모 찾기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;루트 없는 트리가 주어진다. 이때, 트리의 루트를 1이라고 정했을 때, 각 노드의 부모를 구하는 프로그램을 작성하시오.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&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;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;1. 문제 요약&lt;/h2&gt;
&lt;div style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;
&lt;div id=&quot;problem_description&quot;&gt;
&lt;p style=&quot;color: #555555;&quot; data-ke-size=&quot;size16&quot;&gt;루트 없는 트리가 주어진다. 이때, 트리의 루트를 1이라고 정했을 때, 각 노드의 부모를 구하는 프로그램을 작성하시오.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;2. 파이썬&lt;/h2&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;2 - 1. BFS&lt;/h3&gt;
&lt;figure id=&quot;og_1699154072842&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;백준 - 2606 바이러스 [BFS]&quot; data-og-description=&quot;2606번: 바이러스 첫째 줄에는 컴퓨터의 수가 주어진다. 컴퓨터의 수는 100 이하인 양의 정수이고 각 컴퓨터에는 1번 부터 차례대로 번호가 매겨진다. 둘째 줄에는 네트워크 상에서 직접 연결되어 &quot; data-og-host=&quot;e-room.tistory.com&quot; data-og-source-url=&quot;https://e-room.tistory.com/215&quot; data-og-url=&quot;https://e-room.tistory.com/215&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bCs6il/hyUnVKFMwt/Q6RJ1qjiMUCYpLkJv8RVfK/img.jpg?width=800&amp;amp;height=899&amp;amp;face=0_0_800_899,https://scrap.kakaocdn.net/dn/b301Vj/hyUrEUNbhC/x0yVhYe0IzQ7i5L8KlSti0/img.jpg?width=800&amp;amp;height=899&amp;amp;face=0_0_800_899,https://scrap.kakaocdn.net/dn/bIgnJC/hyUrBDLLHy/Gfv558Z5B5kfuyki4NCFF0/img.jpg?width=750&amp;amp;height=843&amp;amp;face=0_0_750_843&quot;&gt;&lt;a href=&quot;https://e-room.tistory.com/215&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://e-room.tistory.com/215&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bCs6il/hyUnVKFMwt/Q6RJ1qjiMUCYpLkJv8RVfK/img.jpg?width=800&amp;amp;height=899&amp;amp;face=0_0_800_899,https://scrap.kakaocdn.net/dn/b301Vj/hyUrEUNbhC/x0yVhYe0IzQ7i5L8KlSti0/img.jpg?width=800&amp;amp;height=899&amp;amp;face=0_0_800_899,https://scrap.kakaocdn.net/dn/bIgnJC/hyUrBDLLHy/Gfv558Z5B5kfuyki4NCFF0/img.jpg?width=750&amp;amp;height=843&amp;amp;face=0_0_750_843');&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;백준 - 2606 바이러스 [BFS]&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;2606번: 바이러스 첫째 줄에는 컴퓨터의 수가 주어진다. 컴퓨터의 수는 100 이하인 양의 정수이고 각 컴퓨터에는 1번 부터 차례대로 번호가 매겨진다. 둘째 줄에는 네트워크 상에서 직접 연결되어&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;e-room.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;2 - 2. 코드&lt;/h3&gt;
&lt;pre id=&quot;code_1695394332945&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;N = int(input())
tree = [[] for _ in range(N+1)]
for _ in range(N-1):
    a, b = map(int, input().split())
    tree[a].append(b)
    tree[b].append(a)

parents = [0] * (N+1)
parents[1] = 1

q = [1]
while q:
    node = q.pop()
    for next in tree[node]:
        if not parents[next]:
            parents[next] = node
            q.append(next)

print(*parents[2:], sep='\n')&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;3. 자바&lt;/h2&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;3 - 1. DFS&lt;/h3&gt;
&lt;figure id=&quot;og_1699154228096&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;백준 - 2606 바이러스 [DFS] 자바 백준 1위&quot; data-og-description=&quot;2606번: 바이러스 첫째 줄에는 컴퓨터의 수가 주어진다. 컴퓨터의 수는 100 이하인 양의 정수이고 각 컴퓨터에는 1번 부터 차례대로 번호가 매겨진다. 둘째 줄에는 네트워크 상에서 직접 연결되어 &quot; data-og-host=&quot;e-room.tistory.com&quot; data-og-source-url=&quot;https://e-room.tistory.com/214&quot; data-og-url=&quot;https://e-room.tistory.com/214&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/42naQ/hyUnOLwWqn/7ksKGEfys7XI9OK1R0SgV1/img.jpg?width=800&amp;amp;height=888&amp;amp;face=0_0_800_888,https://scrap.kakaocdn.net/dn/TL0CC/hyUnWph16k/dkOYlLwDUhUkok9Sh31Wc0/img.jpg?width=800&amp;amp;height=888&amp;amp;face=0_0_800_888,https://scrap.kakaocdn.net/dn/lwpkS/hyUnPRd61g/oRqnKGurnK0UTkTeEt8DKk/img.jpg?width=750&amp;amp;height=833&amp;amp;face=0_0_750_833&quot;&gt;&lt;a href=&quot;https://e-room.tistory.com/214&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://e-room.tistory.com/214&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/42naQ/hyUnOLwWqn/7ksKGEfys7XI9OK1R0SgV1/img.jpg?width=800&amp;amp;height=888&amp;amp;face=0_0_800_888,https://scrap.kakaocdn.net/dn/TL0CC/hyUnWph16k/dkOYlLwDUhUkok9Sh31Wc0/img.jpg?width=800&amp;amp;height=888&amp;amp;face=0_0_800_888,https://scrap.kakaocdn.net/dn/lwpkS/hyUnPRd61g/oRqnKGurnK0UTkTeEt8DKk/img.jpg?width=750&amp;amp;height=833&amp;amp;face=0_0_750_833');&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;백준 - 2606 바이러스 [DFS] 자바 백준 1위&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;2606번: 바이러스 첫째 줄에는 컴퓨터의 수가 주어진다. 컴퓨터의 수는 100 이하인 양의 정수이고 각 컴퓨터에는 1번 부터 차례대로 번호가 매겨진다. 둘째 줄에는 네트워크 상에서 직접 연결되어&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;e-room.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;3 - 2. 코드&lt;/h3&gt;
&lt;pre id=&quot;code_1695394332947&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static List&amp;lt;Integer&amp;gt;[] tree;
static int[] parents;

private static void solution(int node) {
    for (int next : tree[node]) {
        if (parents[next] == 0) {
            parents[next] = node;
            solution(next);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;4. 전체 코드&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/Ksiyeong/Algorithm/tree/main/%EB%B0%B1%EC%A4%80/Silver/11725.%E2%80%85%ED%8A%B8%EB%A6%AC%EC%9D%98%E2%80%85%EB%B6%80%EB%AA%A8%E2%80%85%EC%B0%BE%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/Ksiyeong/Algorithm/tree/main/%EB%B0%B1%EC%A4%80/Silver/11725.%E2%80%85%ED%8A%B8%EB%A6%AC%EC%9D%98%E2%80%85%EB%B6%80%EB%AA%A8%E2%80%85%EC%B0%BE%EA%B8%B0&lt;/a&gt;&lt;/p&gt;</description>
      <category>개발일지/Algorithm</category>
      <category>BFS</category>
      <category>dfs</category>
      <category>Java</category>
      <category>python</category>
      <category>tree</category>
      <category>그래프</category>
      <category>백준</category>
      <category>탐색</category>
      <author>E-room</author>
      <guid isPermaLink="true">https://e-room.tistory.com/218</guid>
      <comments>https://e-room.tistory.com/218#entry218comment</comments>
      <pubDate>Sun, 5 Nov 2023 12:20:03 +0900</pubDate>
    </item>
    <item>
      <title>백준 - 1991 트리 순회 [Tree]</title>
      <link>https://e-room.tistory.com/217</link>
      <description>&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1698824567983&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;1991번: 트리 순회&quot; data-og-description=&quot;첫째 줄에는 이진 트리의 노드의 개수 N(1 &amp;le; N &amp;le; 26)이 주어진다. 둘째 줄부터 N개의 줄에 걸쳐 각 노드와 그의 왼쪽 자식 노드, 오른쪽 자식 노드가 주어진다. 노드의 이름은 A부터 차례대로 알파&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/1991&quot; data-og-url=&quot;https://www.acmicpc.net/problem/1991&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kbSnp/hyUnQVMKdz/344kegSBZTqu95s77q5Kp1/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1991&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/1991&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kbSnp/hyUnQVMKdz/344kegSBZTqu95s77q5Kp1/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&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;1991번: 트리 순회&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;첫째 줄에는 이진 트리의 노드의 개수 N(1 &amp;le; N &amp;le; 26)이 주어진다. 둘째 줄부터 N개의 줄에 걸쳐 각 노드와 그의 왼쪽 자식 노드, 오른쪽 자식 노드가 주어진다. 노드의 이름은 A부터 차례대로 알파&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&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;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;1. 문제 요약&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이진 트리를 입력받아 &lt;b&gt;전위 순회(preorder traversal)&lt;/b&gt;, &lt;b&gt;중위 순회(inorder traversal)&lt;/b&gt;, &lt;b&gt;후위 순회(postorder traversal)&lt;/b&gt;한 결과를 출력하라.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;2. 접근 방법&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_EAC5D4C92C74-1.jpeg&quot; data-origin-width=&quot;1504&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pXjGN/btszy3QkoL1/U17DjK1CjqLiajKnBcjPB1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pXjGN/btszy3QkoL1/U17DjK1CjqLiajKnBcjPB1/img.jpg&quot; data-alt=&quot;트리 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pXjGN/btszy3QkoL1/U17DjK1CjqLiajKnBcjPB1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpXjGN%2Fbtszy3QkoL1%2FU17DjK1CjqLiajKnBcjPB1%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;460&quot; height=&quot;306&quot; data-filename=&quot;IMG_EAC5D4C92C74-1.jpeg&quot; data-origin-width=&quot;1504&quot; data-origin-height=&quot;1000&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;새로운 노드로 이동할 때마다&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp;&lt;b&gt;순회별 방문 순서&lt;/b&gt;를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&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;예시) 전위 순회의 경우&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;왼쪽 이동&lt;/li&gt;
&lt;li&gt;오른쪽 이동&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2 - 1. 전위 순회&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_605B32A51E04-1.jpeg&quot; data-origin-width=&quot;1467&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bno9rZ/btszCFHdrTh/ByesHufqSMKkHsorjVm7hk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bno9rZ/btszCFHdrTh/ByesHufqSMKkHsorjVm7hk/img.jpg&quot; data-alt=&quot;전위 순회&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bno9rZ/btszCFHdrTh/ByesHufqSMKkHsorjVm7hk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbno9rZ%2FbtszCFHdrTh%2FByesHufqSMKkHsorjVm7hk%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;465&quot; height=&quot;317&quot; data-filename=&quot;IMG_605B32A51E04-1.jpeg&quot; data-origin-width=&quot;1467&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;전위 순회&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;ABDCEFG // (루트) (왼쪽 자식) (오른쪽 자식)&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;전위 순회의 경우 루트가 가장 먼저 나오기 때문에, 노드를 방문하자마자 A(1)를 출력합니다.&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;왼쪽 노드(B)로 이동합니다.&lt;br /&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;루트가 나왔으므로 B&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;(2)&lt;/span&gt;를 출력합니다.(다시 루트-왼쪽-오른쪽 순서를 반복)&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;왼쪽 노드(D)로 이동합니다.&lt;br /&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;루트가 나왔으므로 D&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;(3)&lt;/span&gt;를 출력합니다.(다시 루트-왼쪽-오른쪽 순서를 반복)&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;왼쪽 노드를 방문할 차례인데, 방문할 곳이 없습니다.&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;오른쪽 노드를 방문할 차례인데, 방문할 곳이 없습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;오른쪽 노드를 방문할 차례인데, 방문할 곳이 없습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;&amp;nbsp;오른쪽 노드(C)로 이동합니다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;루트가 나왔으므로 C&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;(4)&lt;/span&gt;를 출력합니다.&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;(다시 루트-왼쪽-오른쪽 순서를 반복)&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;왼쪽 노드(E)로 이동합니다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;루트가 나왔으므로 E&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;(5)&lt;/span&gt;를 출력합니다.&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;(다시 루트-왼쪽-오른쪽 순서를 반복)&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;왼쪽 노드를 방문할 차례인데, 방문할 곳이 없습니다.&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;오른쪽 노드를 방문할 차례인데, 방문할 곳이 없습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;오른쪽 노드(F)로 이동합니다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;루트가 나왔으므로 F&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;(6)&lt;/span&gt;를 출력합니다.&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;(다시 루트-왼쪽-오른쪽 순서를 반복)&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;왼쪽 노드를 방문할 차례인데, 방문할 곳이 없습니다.&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;오른쪽 노드(G)로 이동합니다.&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;루트가 나왔으므로 G&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;(7)&lt;/span&gt;를 출력합니다.&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;(다시 루트-왼쪽-오른쪽 순서를 반복)&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;왼쪽 노드를 방문할 차례인데, 방문할 곳이 없습니다.&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;오른쪽 노드를 방문할 차례인데, 방문할 곳이 없습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2 - 2. 중위 순회&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1500&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cf57Lg/btszDAljsPU/XHYt4J63foIrrL4hMIBluK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cf57Lg/btszDAljsPU/XHYt4J63foIrrL4hMIBluK/img.jpg&quot; data-alt=&quot;중위 순회&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cf57Lg/btszDAljsPU/XHYt4J63foIrrL4hMIBluK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcf57Lg%2FbtszDAljsPU%2FXHYt4J63foIrrL4hMIBluK%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;471&quot; height=&quot;314&quot; data-origin-width=&quot;1500&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;중위 순회&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;DBAECFG // (왼쪽 자식) (루트) (오른쪽 자식)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중위 순회의 경우 각각의 노드의 자식이 &lt;b&gt;두 개 이하일 때만&lt;/b&gt; 사용할 수 있습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;중위 순회의 경우 왼쪽 노드가 가장 먼저 나오기 때문에 왼쪽 노드(B)로 이동합니다.&lt;br /&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;왼쪽 노드(D)로 이동합니다.&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;(다시 왼쪽-루트-오른쪽 순서를 반복)&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;왼쪽 노드를 방문할 차례인데, 방문할 곳이 없습니다.&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;(다시 왼쪽-루트-오른쪽 순서를 반복)&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;루트가 나왔으므로 D(1)를 출력합니다.&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;오른쪽 노드를 방문할 차례인데, 방문할 곳이 없습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;루트가 나왔으므로 B(2)를 출력합니다.&lt;/li&gt;
&lt;li&gt;오른쪽 노드를 방문할 차례인데, 방문할 곳이 없습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;루트가 나왔으므로 A(3)를 출력합니다.&lt;/li&gt;
&lt;li&gt;오른쪽 노드(C)로 이동합니다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;왼쪽 노드(E)로 이동합니다.&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;(다시 왼쪽-루트-오른쪽 순서를 반복)&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;왼쪽 노드를 방문할 차례인데, 방문할 곳이 없습니다.&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;(다시 왼쪽-루트-오른쪽 순서를 반복)&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;루트가 나왔으므로 E(4)를 출력합니다.&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;오른쪽 노드를 방문할 차례인데, 방문할 곳이 없습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;루트가 나왔으므로 C(5)를 출력합니다.&lt;/li&gt;
&lt;li&gt;오른쪽 노드(F)로 이동합니다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;왼쪽 노드를 방문할 차례인데, 방문할 곳이 없습니다.&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;(다시 왼쪽-루트-오른쪽 순서를 반복)&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;루트가 나왔으므로 F(6)를 출력합니다.&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;오른쪽 노드(G)로 이동합니다.&amp;nbsp;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;왼쪽 노드를 방문할 차례인데, 방문할 곳이 없습니다.&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;(다시 왼쪽-루트-오른쪽 순서를 반복)&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;&amp;nbsp;루트가 나왔으므로 G(7)를 출력합니다.&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;오른쪽 노드를 방문할 차례인데, 방문할 곳이 없습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2 - 3. 후위 순회&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;DBEGFCA // (왼쪽 자식) (오른쪽 자식) (루트)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1513&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cw1u1c/btszDTdTMR2/pw6937pWaA7Svf7mob1SXK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cw1u1c/btszDTdTMR2/pw6937pWaA7Svf7mob1SXK/img.jpg&quot; data-alt=&quot;후위 순회&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cw1u1c/btszDTdTMR2/pw6937pWaA7Svf7mob1SXK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcw1u1c%2FbtszDTdTMR2%2Fpw6937pWaA7Svf7mob1SXK%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;453&quot; height=&quot;299&quot; data-origin-width=&quot;1513&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;후위 순회&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;중위 순회의 경우 왼쪽 노드가 가장 먼저 나오기 때문에 왼쪽 노드(B)로 이동합니다.&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;왼쪽 노드(D)로 이동합니다.&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;(다시 왼쪽-오른쪽-루트 순서를 반복)&lt;/span&gt;&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;왼쪽 노드를 방문할 차례인데, 방문할 곳이 없습니다.&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;(다시 왼쪽-오른쪽-루트 순서를 반복)&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;오른쪽 노드를 방문할 차례인데, 방문할 곳이 없습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;루트가 나왔으므로 D(1)를 출력합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;오른쪽 노드를 방문할 차례인데, 방문할 곳이 없습니다.&lt;/li&gt;
&lt;li&gt;루트가 나왔으므로 B(2)를 출력합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;오른쪽 노드(C)로 이동합니다.&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;왼쪽 노드(E)로 이동합니다.&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;(다시 왼쪽-오른쪽-루트 순서를 반복)&lt;/span&gt;&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;왼쪽 노드를 방문할 차례인데, 방문할 곳이 없습니다.&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;(다시 왼쪽-오른쪽-루트 순서를 반복)&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;오른쪽 노드를 방문할 차례인데, 방문할 곳이 없습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;루트가 나왔으므로 E(3)를 출력합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;오른쪽 노드(F)로 이동합니다.&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;왼쪽 노드를 방문할 차례인데, 방문할 곳이 없습니다.&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;(다시 왼쪽-오른쪽-루트 순서를 반복)&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;오른쪽 노드(G)로 이동합니다.&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;왼쪽 노드를 방문할 차례인데, 방문할 곳이 없습니다.&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;(다시 왼쪽-오른쪽-루트 순서를 반복)&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;오른쪽 노드를 방문할 차례인데, 방문할 곳이 없습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;루트가 나왔으므로 G(4)를 출력합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;루트가 나왔으므로 F(5)를 출력합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&amp;nbsp;루트가 나왔으므로 C(6)를 출력합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;루트가 나왔으므로 A(7)를 출력합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;3. 파이썬&lt;/h2&gt;
&lt;pre id=&quot;code_1695394432490&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 전위 순회
def preorder(node):
    if node != '.':
        print(node, end='') # 현재 노드 출력
        preorder(tree[node][0]) # 왼쪽 노드
        preorder(tree[node][1]) # 오른쪽 노드
        
# 중위 순회
def inorder(node):
    if node != '.':
        inorder(tree[node][0]) # 왼쪽 노드
        print(node, end='') # 현재 노드 출력
        inorder(tree[node][1]) # 오른쪽 노드
        
# 후위 순회
def postorder(node):
    if node != '.':
        postorder(tree[node][0]) # 왼쪽 노드
        postorder(tree[node][1]) # 오른쪽 노드
        print(node, end='') # 현재 노드 출력&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;4. 자바&lt;/h2&gt;
&lt;pre id=&quot;code_1695394432492&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 전위 순회
private static void preorder(char node) {
    if (node != '.') {
        System.out.print(node); // 현재 노드 출력
        preorder(tree.get(node).left); // 왼쪽 노드
        preorder(tree.get(node).right); // 오른쪽 노드
    }
}

// 중위 순회
private static void inorder(char node) {
    if (node != '.') {
        inorder(tree.get(node).left); // 왼쪽 노드
        System.out.print(node); // 현재 노드 출력
        inorder(tree.get(node).right); // 오른쪽 노드
    }
}

// 후위 순회
private static void postorder(char node) {
    if (node != '.') {
        postorder(tree.get(node).left); // 왼쪽 노드
        postorder(tree.get(node).right); // 오른쪽 노드
        System.out.print(node); // 현재 노드 출력
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;5. 전체 코드&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/Ksiyeong/Algorithm/tree/main/%EB%B0%B1%EC%A4%80/Silver/1991.%E2%80%85%ED%8A%B8%EB%A6%AC%E2%80%85%EC%88%9C%ED%9A%8C&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/Ksiyeong/Algorithm/tree/main/%EB%B0%B1%EC%A4%80/Silver/1991.%E2%80%85%ED%8A%B8%EB%A6%AC%E2%80%85%EC%88%9C%ED%9A%8C&lt;/a&gt;&lt;/p&gt;</description>
      <category>개발일지/Algorithm</category>
      <category>binary</category>
      <category>inorder</category>
      <category>postorder</category>
      <category>preorder</category>
      <category>Traversal</category>
      <category>tree</category>
      <category>순회</category>
      <category>전위</category>
      <category>중위</category>
      <category>후위</category>
      <author>E-room</author>
      <guid isPermaLink="true">https://e-room.tistory.com/217</guid>
      <comments>https://e-room.tistory.com/217#entry217comment</comments>
      <pubDate>Wed, 1 Nov 2023 16:47:23 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 - 짝지어 제거하기 [Stack]</title>
      <link>https://e-room.tistory.com/216</link>
      <description>&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1698674411361&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;프로그래머스&quot; data-og-description=&quot;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/12973?language=python3&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/9f5pP/hyUkmO7VB9/R583FzPWxqdQUePfmMym8K/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/buKEZN/hyUnL7vB6e/ANhL3vUqqxzhfZJOMQNZEk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/12973?language=python3&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/12973?language=python3&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/9f5pP/hyUkmO7VB9/R583FzPWxqdQUePfmMym8K/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/buKEZN/hyUnL7vB6e/ANhL3vUqqxzhfZJOMQNZEk/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;프로그래머스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;1. 문제 요약&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;문자열 s가 주어집니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;문자열 s안에 연속으로 같은 알파벳 2개가 있으면 제거하고 앞뒤로 이어 붙입니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;해당 과정을 계속 반복하여 모든 알파벳을 제거할 수 있으면 1을 리턴하고, 없으면 0을 리턴합니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;2. 접근 방법&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;제거할 수 없는 알파벳을 담을 Stack을 초기화합니다.&lt;/li&gt;
&lt;li&gt;문자열을 순차적으로 방문합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Stack이 비어있으면 현재 알파벳을 넣습니다.&lt;/li&gt;
&lt;li&gt;Stack에 최근에 넣은 알파벳과 현재 알파벳이 다를 경우 제거할 수 없으므로 현재 알파벳을 넣습니다.&lt;/li&gt;
&lt;li&gt;Stack에 최근에 넣은 알파벳과 현재 알파벳이 같을 경우 제거할 수 있으므로 Stack의 해당 알파벳을 제거합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;과정을 마친 후 Stack이 비어있다면 1, 아니면 0을 리턴합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;3. 파이썬&lt;/h2&gt;
&lt;pre id=&quot;code_1695394432490&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(s: str):
    if len(s) % 2 == 1:
        return 0
    
    stack = []
    for char in s:
        if not(stack) or stack[-1] != char:
            stack.append(char)
        else: # stack[-1] == char:
            stack.pop()
    
    return 1 if not(stack) else 0&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;4. 자바&lt;/h2&gt;
&lt;pre id=&quot;code_1695394432492&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public int solution(String s) {
    if (s.length() % 2 == 1) {
        return 0;
    }

    Stack&amp;lt;Character&amp;gt; stack = new Stack&amp;lt;&amp;gt;();
    for (char c : s.toCharArray()) {
        if (stack.isEmpty() || stack.peek() != c) {
            stack.push(c);
        } else {
            stack.pop();
        }
    }

    return stack.isEmpty() ? 1 : 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;</description>
      <category>개발일지/Algorithm</category>
      <category>algorithm</category>
      <category>Java</category>
      <category>programmers</category>
      <category>python</category>
      <category>Stack</category>
      <category>스택</category>
      <category>자료구조</category>
      <author>E-room</author>
      <guid isPermaLink="true">https://e-room.tistory.com/216</guid>
      <comments>https://e-room.tistory.com/216#entry216comment</comments>
      <pubDate>Mon, 30 Oct 2023 23:08:55 +0900</pubDate>
    </item>
  </channel>
</rss>