<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>코딩둘기</title>
    <link>https://100s.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Tue, 28 Jul 2026 06:08:00 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>딩둘기</managingEditor>
    <image>
      <title>코딩둘기</title>
      <url>https://tistory1.daumcdn.net/tistory/4905957/attach/476838d200414e8b93ed27228fd6d238</url>
      <link>https://100s.tistory.com</link>
    </image>
    <item>
      <title>PostgreSQL, MySQL, Oracle의 MVCC 구현 차이</title>
      <link>https://100s.tistory.com/72</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;MVCC란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVCC는 &lt;b&gt;Multi-Version Concurrency Control&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;PostgreSQL, MySQL InnoDB, Oracle은 모두 MVCC를 사용한다. MVCC의 목적은 데이터의 여러 버전을 관리해 읽기와 쓰기가 서로를 불필요하게 차단하지 않도록 하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차이는 과거 버전을 어디에 저장하고, 어떻게 조회하느냐에 있다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;DBMS&lt;/th&gt;
&lt;th&gt;과거 버전 관리 방식&lt;/th&gt;
&lt;th&gt;조회 방식&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PostgreSQL&lt;/td&gt;
&lt;td&gt;테이블 내부에 이전 튜플을 유지&lt;/td&gt;
&lt;td&gt;스냅샷에 보이는 튜플을 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MySQL InnoDB&lt;/td&gt;
&lt;td&gt;이전 값을 Undo Log에 기록&lt;/td&gt;
&lt;td&gt;Undo Chain을 따라 과거 행을 재구성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oracle&lt;/td&gt;
&lt;td&gt;이전 값을 Undo Segment에 기록&lt;/td&gt;
&lt;td&gt;Undo를 적용해 특정 SCN의 블록 이미지를 재구성&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단히 요약하면 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PostgreSQL: 여러 튜플 버전 중 하나를 선택한다.&lt;/li&gt;
&lt;li&gt;MySQL: Undo Chain을 따라 과거 행을 복원한다.&lt;/li&gt;
&lt;li&gt;Oracle: Undo를 적용해 과거 시점의 블록을 만든다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. PostgreSQL: 테이블에 여러 튜플 버전을 저장&lt;/h2&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;520&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3ofWg/dJMcad3TIFG/8h5whYGLrwCzIHkqlUPnkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3ofWg/dJMcad3TIFG/8h5whYGLrwCzIHkqlUPnkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3ofWg/dJMcad3TIFG/8h5whYGLrwCzIHkqlUPnkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3ofWg%2FdJMcad3TIFG%2F8h5whYGLrwCzIHkqlUPnkk%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;1500&quot; height=&quot;520&quot; data-origin-width=&quot;1500&quot; data-origin-height=&quot;520&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PostgreSQL의 &lt;code&gt;UPDATE&lt;/code&gt;는 기존 튜플을 제자리에서 덮어쓰기보다 새로운 튜플 버전을 생성하는 방식에 가깝다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;UPDATE member
SET name = 'Lee'
WHERE id = 1;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개념적으로 테이블에는 다음 두 버전이 함께 존재한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;이전 튜플: id=1, name='Kim'
새 튜플:   id=1, name='Lee'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 튜플에는 가시성 판단을 위한 트랜잭션 정보가 저장된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;xmin&lt;/code&gt;: 튜플을 생성한 트랜잭션 ID&lt;/li&gt;
&lt;li&gt;&lt;code&gt;xmax&lt;/code&gt;: 튜플을 삭제하거나 새 버전으로 대체한 트랜잭션 ID&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;이전 튜플
xmin = 100
xmax = 200
name = 'Kim'

새 튜플
xmin = 200
xmax = 0
name = 'Lee'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조회 시 PostgreSQL은 &lt;code&gt;xmin&lt;/code&gt;, &lt;code&gt;xmax&lt;/code&gt;와 현재 스냅샷을 비교해 보이는 튜플만 반환한다. 과거 데이터를 Undo Log에서 복원하는 것이 아니라, 이미 테이블에 존재하는 버전 중 하나를 선택하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 이상 어떤 트랜잭션에도 필요하지 않은 이전 튜플은 &lt;code&gt;VACUUM&lt;/code&gt;을 통해 정리된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. MySQL InnoDB: 현재 행과 Undo Chain 사용&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;434&quot;&gt;&lt;a href=&quot;https://lefred.be/content/a-graph-a-day-keeps-the-doctor-away-mysql-history-list-length/&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/njjCN/dJMcahSKeVP/IvCy0kCXJdCSCUiVIDyDIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnjjCN%2FdJMcahSKeVP%2FIvCy0kCXJdCSCUiVIDyDIK%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;600&quot; height=&quot;434&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;434&quot;/&gt;&lt;/a&gt;&lt;figcaption&gt;A graph a day, keeps the doctor away ! &amp;ndash; MySQL History List Length&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;InnoDB는 클러스터드 인덱스에 현재 행을 저장하고, 이전 상태를 복원하는 데 필요한 정보를 Undo Log에 기록한다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;현재 행
id = 1
name = 'Lee'
DB_TRX_ID = 200
DB_ROLL_PTR = Undo Record 주소

Undo Record
이전 name = 'Kim'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주요 시스템 컬럼은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;DB_TRX_ID&lt;/code&gt;: 현재 행 버전을 만든 트랜잭션&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DB_ROLL_PTR&lt;/code&gt;: 이전 버전의 Undo Record를 가리키는 포인터&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 포인터들은 다음과 같은 Version Chain을 구성한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;[현재 버전 : LEE] &amp;rarr; [이전 버전: Kim] &amp;rarr; [더 이전 버전]&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조회 시에는 행의 &lt;code&gt;DB_TRX_ID&lt;/code&gt;와 현재 &lt;code&gt;Read View&lt;/code&gt;를 비교한다. 현재 버전이 보이지 않으면 &lt;code&gt;DB_ROLL_PTR&lt;/code&gt;를 따라가면서 보이는 버전이 나올 때까지 이전 행을 재구성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, PostgreSQL과 달리 과거 버전 전체가 테이블의 독립된 튜플로 남아 있는 구조는 아니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Oracle: Undo를 적용해 CR 블록 생성&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1520&quot; data-origin-height=&quot;550&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mxY6m/dJMcafOkk8R/VPstewnk8M1nYscgLA1KM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mxY6m/dJMcafOkk8R/VPstewnk8M1nYscgLA1KM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mxY6m/dJMcafOkk8R/VPstewnk8M1nYscgLA1KM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmxY6m%2FdJMcafOkk8R%2FVPstewnk8M1nYscgLA1KM0%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;1520&quot; height=&quot;550&quot; data-origin-width=&quot;1520&quot; data-origin-height=&quot;550&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Oracle도 과거 값을 Undo Segment에 저장하지만, 일관된 읽기를 설명할 때는 블록 재구성이 핵심이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Oracle의 쿼리는 읽기 기준이 되는 SCN(System Change Number)을 가진다. SCN은 데이터베이스 변경 순서를 나타내는 논리적 시간 값이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿼리의 기준 SCN보다 최신 변경이 포함된 블록을 만나면 Oracle은 다음과 같이 처리한다.&lt;/p&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;현재 데이터 블록
        &amp;darr; 복사
블록 복사본
        &amp;darr; Undo 적용
쿼리 기준 SCN의 CR 블록&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 만든 블록을 &lt;code&gt;CR(Consistent Read) Clone&lt;/code&gt;이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 데이터 블록을 과거 상태로 되돌리는 것은 아니다. 버퍼 캐시에 복사본을 만들고 그 복사본에만 Undo를 적용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 쿼리가 SCN 1000에서 시작했는데 현재 블록에 SCN 1020의 변경이 포함되어 있다면, Undo를 이용해 SCN 1000 기준의 일관된 블록 이미지를 만든다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 같은 UPDATE가 처리되는 방식&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 값이 &lt;code&gt;Kim&lt;/code&gt;이고 이를 &lt;code&gt;Lee&lt;/code&gt;로 수정한다고 가정하자.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;PostgreSQL&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;테이블
├─ 이전 튜플: Kim, xmax=수정 트랜잭션 ID
└─ 새 튜플:   Lee, xmin=수정 트랜잭션 ID&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 튜플을 종료하고 새 튜플을 생성한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;MySQL InnoDB&lt;/h3&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;현재 행: Lee
    &amp;darr; DB_ROLL_PTR
Undo: Kim&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 레코드를 변경하고 이전 상태를 Undo Log에 기록한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Oracle&lt;/h3&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;현재 블록: Lee
Undo: Kim&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거 스냅샷을 가진 쿼리가 접근하면 현재 블록의 복사본에 Undo를 적용해 &lt;code&gt;Kim&lt;/code&gt;이 보이는 CR 블록을 만든다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 격리 수준에 따른 스냅샷&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;DBMS&amp;middot;격리 수준&lt;/th&gt;
&lt;th&gt;스냅샷 기준&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PostgreSQL &lt;code&gt;READ COMMITTED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SQL 문장마다 새 스냅샷&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PostgreSQL &lt;code&gt;REPEATABLE READ&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;트랜잭션 동안 같은 스냅샷&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MySQL &lt;code&gt;READ COMMITTED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;일관된 읽기마다 새 Read View&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MySQL &lt;code&gt;REPEATABLE READ&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;일반적으로 최초 일관된 읽기의 Read View를 재사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oracle &lt;code&gt;READ COMMITTED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SQL 문장 시작 SCN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oracle &lt;code&gt;SERIALIZABLE&lt;/code&gt;&amp;middot;읽기 전용 트랜잭션&lt;/td&gt;
&lt;td&gt;트랜잭션 기준 SCN&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 세 DBMS가 비슷한 조회 결과를 제공하더라도, 내부적으로 과거 버전을 얻는 방법은 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;최종 정리&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;PostgreSQL&lt;/th&gt;
&lt;th&gt;MySQL InnoDB&lt;/th&gt;
&lt;th&gt;Oracle&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;현재 버전&lt;/td&gt;
&lt;td&gt;새 튜플&lt;/td&gt;
&lt;td&gt;클러스터드 인덱스 레코드&lt;/td&gt;
&lt;td&gt;데이터 블록&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;이전 버전&lt;/td&gt;
&lt;td&gt;테이블의 이전 튜플&lt;/td&gt;
&lt;td&gt;Undo Log&lt;/td&gt;
&lt;td&gt;Undo Segment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;가시성 기준&lt;/td&gt;
&lt;td&gt;MVCC Snapshot&lt;/td&gt;
&lt;td&gt;Read View&lt;/td&gt;
&lt;td&gt;SCN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;과거 데이터 조회&lt;/td&gt;
&lt;td&gt;보이는 튜플 선택&lt;/td&gt;
&lt;td&gt;Undo Chain으로 행 재구성&lt;/td&gt;
&lt;td&gt;Undo로 CR 블록 재구성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UPDATE 관점&lt;/td&gt;
&lt;td&gt;기존 튜플 종료 + 새 튜플 생성&lt;/td&gt;
&lt;td&gt;현재 행 변경 + Undo 생성&lt;/td&gt;
&lt;td&gt;블록 변경 + Undo 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;정리 작업&lt;/td&gt;
&lt;td&gt;&lt;code&gt;VACUUM&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Purge&lt;/td&gt;
&lt;td&gt;Undo 재사용&amp;middot;관리&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고자료&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;PostgreSQL Vacuum에 대한 거의 모든 것&lt;/b&gt; &lt;a href=&quot;https://techblog.woowahan.com/9478/&quot;&gt;https://techblog.woowahan.com/9478/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Backend/Database</category>
      <author>딩둘기</author>
      <guid isPermaLink="true">https://100s.tistory.com/72</guid>
      <comments>https://100s.tistory.com/72#entry72comment</comments>
      <pubDate>Mon, 27 Jul 2026 22:00:09 +0900</pubDate>
    </item>
    <item>
      <title>Word2Vec 원리: CBOW와 Skip-gram으로 단어 임베딩을 학습하는 방법</title>
      <link>https://100s.tistory.com/71</link>
      <description>&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. 원-핫 벡터(One-hot Vector)만으로는 의미를 표현할 수 없다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터에서 단어를 표현하는 가장 단순한 방법은 One-hot Encoding이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어휘가 다음과 같을때,&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[강아지, 고양이, 자동차, 비행기]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 단어는 하나의 위치만 1이고 나머지는 모두 0인 벡터로 표현할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;강아지 &amp;rarr; [1, 0, 0, 0]
고양이 &amp;rarr; [0, 1, 0, 0]
자동차 &amp;rarr; [0, 0, 1, 0]
비행기 &amp;rarr; [0, 0, 0, 1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 대부분의 값이 0인 표현을 &lt;b&gt;희소 표현(Sparse Representation)&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원-핫 벡터(One-hot Vector)는 단어를 구분하는 데는 유용하지만 단어 사이의 의미 관계는 표현하지 못한다. 강아지와 고양이가 의미적으로 가깝더라도, 유의미한 유사성을 표현할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Word2Vec은 단어를 다음과 같은 저차원의 실수 벡터로 표현한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;강아지 &amp;rarr; [0.21, -0.35, 0.72, ...]
고양이 &amp;rarr; [0.19, -0.31, 0.68, ...]
자동차 &amp;rarr; [-0.44, 0.58, 0.04, ...]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단어의 의미가 여러 차원에 나누어 표현되기 때문에 이를 &lt;b&gt;분산 표현(Distributed Representation)&lt;/b&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;2. 분포 가설: 비슷한 문맥의 단어는 비슷한 의미를 가진다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Word2Vec은 &lt;b&gt;분포 가설(Distributional Hypothesis)&lt;/b&gt;을 기반으로 한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비슷한 문맥에서 등장하는 단어들은 비슷한 의미를 가진다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음 두 문장을 생각해 볼 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아침에 따뜻한 커피를 마셨다.&lt;/li&gt;
&lt;li&gt;아침에 따뜻한 차를 마셨다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커피와 차는 모두 아침, 따뜻한, 마시다와 같은 표현 주변에 등장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 문장이 말뭉치(Corpus)에서 반복되면 두 단어는 비슷한 문맥 분포를 갖게 된다. Word2Vec은 이러한 문맥 예측을 반복해서 학습한다. 그 결과 비슷한 문맥에서 사용되는 커피와 차는 유사한 벡터 표현을 가지게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, Word2Vec이 직접 학습하는 것은 단어의 사전적 정의가 아니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어떤 단어가&lt;/li&gt;
&lt;li&gt;어떤 단어 주변에&lt;/li&gt;
&lt;li&gt;얼마나 자주 등장하는가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 사용 패턴을 학습한 결과가 단어 벡터에 반영된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. 문장에서 학습 데이터 만들기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Word2Vec은 문장을 중심 단어와 주변 단어의 관계로 바꾸어 학습한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 문장이 있다고 하자.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;you say goodbye and I say hello&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중심 단어를 goodbye으로 정하고 윈도우 크기(Window Size)를 2로 설정하면, 앞뒤 두 단어가 주변 단어가 된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;you, say, and, I&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Window는 중심 단어를 기준으로 문맥을 어디까지 볼 것인지를 나타낸다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[you say] goodbye [and I]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중심 단어를 문장 안에서 한 칸씩 이동하며 학습 데이터를 만드는 방식을 &lt;b&gt;슬라이딩 윈도우&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Word2Vec에는 이 데이터를 사용하는 두 가지 대표적인 방식이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CBOW : 주변 단어로 중심 단어를 예측한다.&lt;/li&gt;
&lt;li&gt;Skip-Gram : 중심 단어로 주변 단어를 예측한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 방식은 예측 방향은 다르지만, 단어와 주변 문맥의 관계를 학습한다는 원리는 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. CBOW는 어떻게 단어 벡터를 학습할까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CBOW는 &lt;b&gt;주변 단어를 보고 가운데 단어를 맞히는 방식&lt;/b&gt;이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;you say goodbye and I say hello&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중심 단어를 goodbye로 두고, 양옆 한 단어씩 본다면 학습 문제는 이렇게 만들어진다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력 x: say, and&lt;/li&gt;
&lt;li&gt;정답 y: goodbye&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 문장 자체에서 입력과 정답을 자동으로 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) 단어를 원-핫 벡터(One-hot Vector)로 표현한다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;말뭉치(Corpus)에 등장한 단어들로 어휘 사전을 구성한다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[you, say, goodbye, and, I, hello]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어휘 크기가 6이라면 각 단어는 6차원의 원-핫 벡터(One-hot Vector)로 표현된다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;you      &amp;rarr; [1, 0, 0, 0, 0, 0]
say      &amp;rarr; [0, 1, 0, 0, 0, 0]
goodbye  &amp;rarr; [0, 0, 1, 0, 0, 0]
and      &amp;rarr; [0, 0, 0, 1, 0, 0]
I        &amp;rarr; [0, 0, 0, 0, 1, 0]
hello    &amp;rarr; [0, 0, 0, 0, 0, 1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원-핫 벡터(One-hot Vector)는 단어의 의미를 담는 것이 아니라, 어휘 사전에서 해당 단어를 구분하기 위한 표현이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) W_in에서 입력 단어의 벡터를 가져온다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;W_in은 각 단어의 입력 벡터를 저장하는 행렬이다. 처음에는 각 벡터가 임의의 값으로 초기화된다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;say &amp;rarr; [-0.3, 0.7, 0.1]
and &amp;rarr; [ 0.1, 0.3, 0.5]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원-핫 벡터(One-hot Vector)와 W_in을 곱하면 해당 단어의 벡터를 선택할 수 있다. 실제 구현에서는 보통 단어의 인덱스를 이용해 해당 행을 직접 조회하며, 이를 임베딩 룩업(Embedding Lookup)이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3) 주변 단어 벡터의 평균으로 문맥 벡터를 만든다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CBOW는 주변 단어들의 벡터를 평균내어 하나의 문맥 벡터로 만든다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;say 벡터 = [-0.3, 0.7, 0.1]
and 벡터 = [ 0.1, 0.3, 0.5]

문맥 벡터 = [-0.1, 0.5, 0.3]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문맥 벡터에는 say와 and의 정보가 함께 반영된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4) 모든 후보 단어의 점수를 계산한다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정답 후보는 어휘 사전에 포함된 모든 단어이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 W_out에 저장된 각 단어의 출력 벡터가 다음과 같다고 하자&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;you      &amp;rarr; [ 0.00, 0.00, 0.00]
say      &amp;rarr; [-0.13, 0.67, 0.40]
goodbye  &amp;rarr; [-0.68, 3.42, 2.05]
and      &amp;rarr; [-0.09, 0.48, 0.29]
I        &amp;rarr; [ 0.00, 0.01, 0.00]
hello    &amp;rarr; [-0.39, 1.98, 1.19]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델은 문맥 벡터를 W_out에 저장된 각 단어의 출력 벡터와 내적하여 점수를 계산한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;you      &amp;rarr; 0.000
say      &amp;rarr; 0.468
goodbye  &amp;rarr; 2.393
and      &amp;rarr; 0.336
I        &amp;rarr; 0.005
hello    &amp;rarr; 1.386
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점수가 높을수록 해당 단어가 중심 단어일 가능성이 높다고 판단한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5) 소프트맥스(Softmax)로 점수를 확률로 변환한다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단어별 점수에 소프트맥스(Softmax)를 적용하면 전체 합이 1인 확률 분포가 된다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;you      &amp;rarr; 0.05
say      &amp;rarr; 0.08
goodbye  &amp;rarr; 0.55
and      &amp;rarr; 0.07
I        &amp;rarr; 0.05
hello    &amp;rarr; 0.20
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 결과는 모델이 goodbye를 정답일 확률이 가장 높은 단어로 예측했다는 의미다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 정답은 goodbye이므로, 학습 과정에서는 goodbye에 부여한 확률을 높이고 다른 단어들의 확률을 낮추도록 가중치를 조정한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  여기서는 원리를 단순하게 설명하기 위해 모든 단어를 대상으로 소프트맥스를 계산하는 구조를 사용했다. 실제 Word2Vec 구현에서는 계산량을 줄이기 위해 네거티브 샘플링이나 계층적 소프트맥스를 사용하는 경우가 많다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6) 손실을 계산하고 가중치를 갱신한다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트맥스(Softmax)로 얻은 예측 확률과 실제 정답을 비교해 손실값을 계산한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예측: goodbye = 0.55&lt;/li&gt;
&lt;li&gt;정답: goodbye = 1&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 역전파를 통해 손실이 감소하도록 W_in과 W_out을 갱신한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 학습 샘플에서는 입력으로 사용된 say, and의 입력 벡터가 수정되며, 출력층에서는 정답인 goodbye의 점수는 높이고 다른 단어들의 점수는 낮추는 방향으로 가중치가 조정된다.&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;7) 전체 말뭉치(Corpus)에 대해 학습을 반복한다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CBOW는 중심 단어의 위치를 이동시키며 여러 학습 샘플을 생성한다.&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;학습이 끝난 뒤에는 일반적으로 W_in의 각 행을 해당 단어의 임베딩 벡터로 사용한다. 구현이나 목적에 따라 W_out을 사용하거나 두 벡터를 결합하기도 한다.&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;5. Skip-gram: 중심 단어로 주변 단어 예측하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Skip-gram은 CBOW와 반대로, 중심 단어 하나를 입력해 주변 단어를 예측한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중심 단어를 goodbye로 설정하고 윈도우 크기를 1로 두면 다음과 같은 학습 샘플이 생성된다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;goodbye &amp;rarr; say
goodbye &amp;rarr; and
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Skip-gram은 입력 단어가 하나이므로 CBOW처럼 주변 단어 벡터의 평균을 구하지 않는다. W_in에서 중심 단어인 goodbye의 입력 벡터를 가져와 그대로 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 입력 벡터를 W_out의 모든 단어 벡터와 비교해 점수를 계산하고, 소프트맥스(Softmax)를 통해 단어별 확률로 변환한다. 이후 실제 주변 단어와 비교해 손실을 계산하고, 손실이 감소하도록 W_in과 W_out을 갱신한다.&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;6. CBOW와 Skip-gram 중 어떤 방식이 더 좋을까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CBOW는 여러 주변 단어의 정보를 한 번에 사용하므로 학습 속도가 비교적 빠르고, 자주 등장하는 단어의 표현을 안정적으로 학습하는 데 유리하다. 따라서 학습 효율이 중요하거나 빠르게 기본 임베딩을 학습하려는 경우 적합하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Skip-gram은 하나의 중심 단어에서 여러 학습 쌍을 생성하므로 계산량은 더 많지만, 개별 단어와 문맥의 관계를 세밀하게 학습한다. 일반적으로 빈도가 낮은 단어의 표현을 학습하는 데 상대적으로 유리하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 실제 성능은 말뭉치(Corpus)의 크기, 단어 빈도, 윈도우 크기, 임베딩 차원, Negative Sampling 등의 설정에 따라 달라진다. 따라서 절대적으로 우수한 방식을 정하기보다는 목적과 데이터에 맞게 선택해야 한다.&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 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;7. Word2Vec의 한계&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Word2Vec은 단어를 의미 있는 벡터로 표현할 수 있다는 점에서 중요한 모델이지만, 하나의 단어에 하나의 고정된 벡터를 부여한다는 한계가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &amp;lsquo;사과를 먹다&amp;rsquo;의 &amp;lsquo;사과&amp;rsquo;와 &amp;lsquo;잘못을 사과하다&amp;rsquo;의 &amp;lsquo;사과&amp;rsquo;는 의미가 다르지만, Word2Vec에서는 동일한 하나의 벡터로 표현된다. 따라서 문맥에 따라 달라지는 단어의 의미를 충분히 구분하기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 등장한 ELMo와 BERT 같은 문맥 기반 언어 모델은 문장 속 문맥에 따라 같은 단어에도 서로 다른 벡터를 생성함으로써 이러한 한계를 보완한다.&lt;/p&gt;</description>
      <category>AI &amp;amp; ML/NLP &amp;amp; LLM</category>
      <author>딩둘기</author>
      <guid isPermaLink="true">https://100s.tistory.com/71</guid>
      <comments>https://100s.tistory.com/71#entry71comment</comments>
      <pubDate>Fri, 24 Jul 2026 22:00:26 +0900</pubDate>
    </item>
    <item>
      <title>WordNet은 단어의 의미를 어떻게 표현할까? Lemma, Sense, Synset과 의미 관계 그래프</title>
      <link>https://100s.tistory.com/70</link>
      <description>&lt;h2&gt;1. 시소러스와 WordNet&lt;/h2&gt;
&lt;p&gt;시소러스(Thesaurus)는 단어 또는 개념 사이의 의미 관계를 체계적으로 정리한 어휘 사전이다. 단순히 유의어만 나열하는 것이 아니라 동의어, 반의어, 상위어, 하위어, 관련어 등의 관계를 함께 표현한다.&lt;/p&gt;
&lt;p&gt;예를 들어 &lt;code&gt;car&lt;/code&gt;를 중심으로 다음과 같은 관계를 구성할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;car
├── 동의 표현: automobile
├── 상위 개념: motor vehicle
├── 하위 개념: ambulance, taxi
└── 관련 개념: driver, road, engine&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이러한 의미 관계를 체계적으로 분류하고 데이터베이스로 구축하면 정보검색이나 자연어 처리에서 단어의 의미를 분석하고 확장하는 데 활용할 수 있다.&lt;/p&gt;
&lt;p&gt;WordNet은 대표적인 어휘 의미 데이터베이스다. 일반적인 시소러스처럼 단어 사이의 관계를 제공하지만, 단어를 그대로 연결하기 전에 하나의 단어가 가진 여러 의미를 구분한다. 이후 같은 의미를 나타내는 단어들을 Synset으로 묶고, Synset 사이를 다양한 의미 관계로 연결한다.&lt;/p&gt;
&lt;p&gt;따라서 WordNet은 단순한 유의어 사전이라기보다, 구분된 의미를 중심으로 구성된 의미 관계망이라고 할 수 있다.&lt;/p&gt;
&lt;br/&gt;

&lt;h2&gt;2. Word Form, Lemma, Sense, Synset&lt;/h2&gt;
&lt;p&gt;WordNet의 구조를 이해하려면 Word Form, Lemma, Sense, Synset을 구분해야 한다.&lt;/p&gt;
&lt;h3&gt;1) Word Form&lt;/h3&gt;
&lt;p&gt;Word Form은 텍스트에 실제로 나타나는 단어 형태다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;run
runs
running
ran&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;형태는 다르지만 모두 기본형 &lt;code&gt;run&lt;/code&gt;으로 정규화할 수 있다.&lt;/p&gt;
&lt;h3&gt;2) Lemma&lt;/h3&gt;
&lt;p&gt;Lemma는 여러 형태로 변한 단어를 대표하는 기본형이다.&lt;/p&gt;
&lt;p&gt;예를 들어 &lt;code&gt;run&lt;/code&gt;은 문장에서 &lt;code&gt;runs&lt;/code&gt;, &lt;code&gt;running&lt;/code&gt;, &lt;code&gt;ran&lt;/code&gt;처럼 다양한 형태로 나타날 수 있다. WordNet에서는 이러한 단어들을 기본형인 &lt;code&gt;run&lt;/code&gt;을 기준으로 찾을 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;running → run
cars    → car&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;3) Synset&lt;/h3&gt;
&lt;p&gt;Synset은 같은 의미를 가진 여러 단어를 모아 만든 개념 묶음이다. 이름은 Synonym Set의 줄임말이다.&lt;/p&gt;
&lt;p&gt;예를 들어 &lt;code&gt;car&lt;/code&gt;, &lt;code&gt;automobile&lt;/code&gt;, &lt;code&gt;motorcar&lt;/code&gt;는 서로 다른 단어지만 모두 ‘자동차’라는 의미를 나타낸다. WordNet에서는 이 단어들을 하나의 Synset으로 묶는다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;car.n.01: Synset
├── Lemma: car
├── Lemma: automobile
└── Lemma: motorcar&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;한 Synset에 속한 단어들이 모든 문장에서 완벽하게 치환되는 것은 아니다. 같은 개념을 공유하며 적어도 일부 문맥에서 서로 바꿔 쓸 수 있는 표현들의 집합으로 이해하는 편이 정확하다.&lt;/p&gt;
&lt;h3&gt;4) Sense&lt;/h3&gt;
&lt;p&gt;Sense는 하나의 단어가 WordNet 안에서 갖는 개별 의미다. WordNet 데이터 구조에서는 &lt;strong&gt;특정 단어가 특정 Synset에 속한 경우&lt;/strong&gt;를 하나의 Sense로 볼 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Word Form: automobiles
        ↓ 기본형 변환
Lemma 이름: automobile
        ↓
        └── Sense: 자동차
               └── Synset: car.n.01
                      ├── car
                      ├── auto
                      ├── automobile
                      └── motorcar&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Sense는 별도의 단어나 묶음이 아니라, &lt;strong&gt;Lemma와 Synset 사이의 연결&lt;/strong&gt;을 의미한다.&lt;/p&gt;
&lt;p&gt;예를 들어 &lt;code&gt;car&lt;/code&gt;, &lt;code&gt;automobile&lt;/code&gt;, &lt;code&gt;auto&lt;/code&gt;, &lt;code&gt;motorcar&lt;/code&gt;는 각각 서로 다른 Lemma지만 모두 ‘자동차’라는 같은 개념을 나타낸다. 따라서 이 Lemma들은 &lt;code&gt;car.n.01&lt;/code&gt; Synset과 연결된다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Lemma: car ─────────┐
Lemma: automobile ──┼──▶ Synset: car.n.01
Lemma: auto ────────┤    의미: 자동차
Lemma: motorcar ────┘&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이때 각 연결은 해당 Lemma가 ‘자동차’라는 의미로 사용된다는 것을 나타낸다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;car        ──▶ car.n.01 = car의 ‘자동차’ Sense
automobile ──▶ car.n.01 = automobile의 ‘자동차’ Sense
motorcar   ──▶ car.n.01 = motorcar의 ‘자동차’ Sense&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;하나의 Lemma가 여러 의미를 가지면 여러 Synset과 연결될 수도 있다. 예를 들어 &lt;code&gt;car&lt;/code&gt;는 ‘자동차’뿐만 아니라 ‘기차 객차’라는 의미로도 사용된다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;                  ┌──▶ car.n.01: 자동차
Lemma: car ───────┤
                  └──▶ car.n.02: 기차 객차&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;따라서 Lemma &lt;code&gt;car&lt;/code&gt;는 &lt;code&gt;car.n.01&lt;/code&gt;과 &lt;code&gt;car.n.02&lt;/code&gt;라는 두 Synset에 연결되고, 여기서는 각각 ‘자동차’와 ‘기차 객차’라는 Sense를 갖는다.&lt;/p&gt;
&lt;p&gt;정리하면 하나의 Synset에는 같은 개념을 표현하는 여러 Lemma가 연결될 수 있고, 하나의 Lemma도 의미에 따라 여러 Synset과 연결될 수 있다. &lt;strong&gt;이때 Lemma와 Synset 사이의 연결 하나가 하나의 Sense에 해당한다.&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;

&lt;h2&gt;3. Synset에는 무엇이 들어 있을까?&lt;/h2&gt;
&lt;p&gt;하나의 Synset에는 동의 표현만 들어 있는 것이 아니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Synset
├── 품사
├── Lemma 목록
├── Gloss
│   ├── 의미 정의
│   └── 사용 예문
└── 다른 Synset과의 의미 관계&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;WordNet은 명사, 동사, 형용사, 부사를 구분한다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;표기&lt;/th&gt;
&lt;th&gt;품사&lt;/th&gt;
&lt;th&gt;예시&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;n&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;명사&lt;/td&gt;
&lt;td&gt;&lt;code&gt;car.n.01&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;v&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;동사&lt;/td&gt;
&lt;td&gt;&lt;code&gt;run.v.01&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;형용사&lt;/td&gt;
&lt;td&gt;&lt;code&gt;good.a.01&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;r&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;부사&lt;/td&gt;
&lt;td&gt;&lt;code&gt;quickly.r.01&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;code&gt;car.n.01&lt;/code&gt;은 NLTK에서 &lt;code&gt;car&lt;/code&gt;라는 이름으로 조회되는 첫 번째 명사 Synset을 뜻한다. 이때 이름 앞부분의 &lt;code&gt;car&lt;/code&gt;는 Synset에 등록된 첫 번째 Lemma를 사용한 식별자이지, 반드시 언어학적으로 가장 대표적인 단어라는 뜻은 아니다.&lt;/p&gt;
&lt;p&gt;NLTK에서는 다음과 같이 Synset의 내용을 확인할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import nltk
from nltk.corpus import wordnet as wn

nltk.download(&amp;quot;wordnet&amp;quot;)
car = wn.synset(&amp;quot;car.n.01&amp;quot;)

print(car.name())
print(car.lemma_names())
print(car.definition())
print(car.examples())&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;car.n.01
[&amp;#39;car&amp;#39;, &amp;#39;auto&amp;#39;, &amp;#39;automobile&amp;#39;, &amp;#39;machine&amp;#39;, &amp;#39;motorcar&amp;#39;]
a motor vehicle with four wheels; usually propelled by an internal combustion engine
[&amp;#39;he needs a car to get to work&amp;#39;]&lt;/code&gt;&lt;/pre&gt;&lt;br/&gt;


&lt;h2&gt;4. WordNet을 그래프로 보기&lt;/h2&gt;
&lt;p&gt;WordNet은 Synset을 의미 관계로 연결한 그래프 구조이다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;노드: Synset, 즉 구분된 개념
간선: Synset 사이의 의미 관계&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;예를 들어 자동차 개념은 다음처럼 다른 개념들과 연결될 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;40%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SaddP/dJMcaccQWPC/mNaEwN1kxeq1A1qczHXVX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SaddP/dJMcaccQWPC/mNaEwN1kxeq1A1qczHXVX1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SaddP/dJMcaccQWPC/mNaEwN1kxeq1A1qczHXVX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSaddP%2FdJMcaccQWPC%2FmNaEwN1kxeq1A1qczHXVX1%2Fimg.png&quot; width=&quot;40%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;code&gt;car&lt;/code&gt;는 &lt;code&gt;motor vehicle&lt;/code&gt;의 하위 개념이고, &lt;code&gt;ambulance&lt;/code&gt;는 &lt;code&gt;car&lt;/code&gt;의 하위 개념이다.&lt;/p&gt;
&lt;p&gt;상·하위 관계만 보면 트리처럼 보이지만, WordNet에는 부분·전체와 함의 등 여러 관계가 함께 존재한다. 따라서 전체 구조는 하나의 트리가 아니라 복합적인 의미 그래프에 가깝다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;80%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/liXpc/dJMcaccQWPe/pj9d3ukRXWKgbtxl6onk5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/liXpc/dJMcaccQWPe/pj9d3ukRXWKgbtxl6onk5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/liXpc/dJMcaccQWPe/pj9d3ukRXWKgbtxl6onk5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FliXpc%2FdJMcaccQWPe%2Fpj9d3ukRXWKgbtxl6onk5k%2Fimg.png&quot; width=&quot;80%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;br/&gt;

&lt;h2&gt;5. 대표적인 의미 관계&lt;/h2&gt;
&lt;h3&gt;1) Hypernym과 Hyponym&lt;/h3&gt;
&lt;p&gt;Hypernym은 더 넓은 범위의 상위 개념이다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;car → motor vehicle&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;자동차는 동력으로 움직이는 차량의 한 종류이므로 &lt;code&gt;motor vehicle&lt;/code&gt;은 &lt;code&gt;car&lt;/code&gt;의 Hypernym이다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;A car is a kind of motor vehicle.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Hyponym은 반대로 더 구체적인 하위 개념이다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;car
├── ambulance
├── taxi
└── sports car&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;ambulance&lt;/code&gt;가 &lt;code&gt;car&lt;/code&gt;의 Hyponym이라면, 반대 방향에서 &lt;code&gt;car&lt;/code&gt;는 &lt;code&gt;ambulance&lt;/code&gt;의 Hypernym이다. 이 관계를 흔히 IS-A 관계라고 부른다.&lt;/p&gt;
&lt;h3&gt;2) Meronym과 Holonym&lt;/h3&gt;
&lt;p&gt;Meronym은 전체를 구성하는 부분이며, Holonym은 그 부분을 포함하는 전체다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;wheel  ── PART-OF ──▶ car
wheel  ◀─ HAS-PART ── car&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;같은 사실을 어느 쪽에서 바라보느냐에 따라 용어가 달라진다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;wheel&lt;/code&gt;은 &lt;code&gt;car&lt;/code&gt;의 Meronym이다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;car&lt;/code&gt;는 &lt;code&gt;wheel&lt;/code&gt;의 Holonym이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;WordNet에서는 부분·전체 관계를 더 세분화해 부품, 구성 물질, 구성원 관계 등으로 구분한다.&lt;/p&gt;
&lt;h3&gt;관계는 품사마다 다르다&lt;/h3&gt;
&lt;p&gt;모든 관계를 모든 품사에 똑같이 적용할 수 있는 것은 아니다. 명사와 동사는 주로 Hypernym–Hyponym 계층으로 조직된다. 동사에는 어떤 행동이 다른 행동을 수반하는 Entailment 관계도 있으며, 반의 관계 등 일부 관계는 Synset 전체가 아니라 개별 Lemma 사이에 정의된다.&lt;/p&gt;
&lt;p&gt;이 차이는 NLTK를 사용할 때 중요하다. 예를 들어 반의어는 Synset이 아니라 Lemma에서 조회한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from nltk.corpus import wordnet as wn

good = wn.synset(&amp;quot;good.a.01&amp;quot;)
print(good.lemmas()[0].antonyms()) # [Lemma(&amp;#39;bad.a.01.bad&amp;#39;)]&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;


&lt;h2&gt;6. 그래프 경로로 의미 유사도 계산하기&lt;/h2&gt;
&lt;p&gt;Synset이 그래프로 연결되어 있다면 두 의미 사이의 거리를 계산할 수 있다.&lt;/p&gt;
&lt;p&gt;예를 들어 &lt;code&gt;car&lt;/code&gt;와 &lt;code&gt;motorcycle&lt;/code&gt;은 IS-A 분류 체계에서 비교적 가까운 상위 개념을 공유한다. 반면 &lt;code&gt;car&lt;/code&gt;와 &lt;code&gt;novel&lt;/code&gt;은 훨씬 먼 곳에 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;          motor vehicle
           /          \
         car       motorcycle

entity
  └── abstraction
       └── communication
            └── written work
                 └── novel&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;NLTK에서는 &lt;code&gt;path_similarity()&lt;/code&gt;로 두 Synset 사이의 경로 기반 유사도를 계산할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from nltk.corpus import wordnet as wn

car = wn.synset(&amp;quot;car.n.01&amp;quot;)
motorcycle = wn.synset(&amp;quot;motorcycle.n.01&amp;quot;)
novel = wn.synset(&amp;quot;novel.n.01&amp;quot;)

print(car.path_similarity(motorcycle)) # 0.3333333333333333
print(car.path_similarity(novel)) # 0.05555555555555555&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;path_similarity()&lt;/code&gt;는 Hypernym–Hyponym으로 구성된 IS-A 분류 체계에서 두 Sense를 연결하는 최단 경로를 바탕으로 0과 1 사이의 점수를 계산한다. 같은 Synset을 비교하면 1이 되며, 일반적으로 경로가 짧을수록 점수가 높다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;IS-A 분류 체계의 최단 경로가 짧다
→ 구조상 가까운 개념이다
→ 더 높은 유사도 점수를 얻는다&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;하지만 그래프에서 가깝다고 해서 모든 문맥에서 비슷한 표현이라는 뜻은 아니다. WordNet의 경로 유사도는 사람이 구축한 분류 관계를 사용하며, 문장 문맥을 직접 읽고 판단하지 않는다.&lt;/p&gt;
&lt;br/&gt;

&lt;h2&gt;7. WordNet과 임베딩은 무엇이 다를까?&lt;/h2&gt;
&lt;p&gt;WordNet과 임베딩은 모두 의미를 표현하지만 접근 방식이 다르다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;WordNet&lt;/th&gt;
&lt;th&gt;임베딩&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;기본 단위&lt;/td&gt;
&lt;td&gt;Synset과 Lemma&lt;/td&gt;
&lt;td&gt;벡터&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;의미 획득&lt;/td&gt;
&lt;td&gt;사람이 관계를 정의&lt;/td&gt;
&lt;td&gt;데이터에서 학습&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;관계 표현&lt;/td&gt;
&lt;td&gt;명시적&lt;/td&gt;
&lt;td&gt;벡터 공간에 암묵적&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;유사도 계산&lt;/td&gt;
&lt;td&gt;그래프 경로 등&lt;/td&gt;
&lt;td&gt;코사인 유사도 등&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;해석 가능성&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;td&gt;상대적으로 낮음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;문맥 반영&lt;/td&gt;
&lt;td&gt;제한적&lt;/td&gt;
&lt;td&gt;문맥 임베딩에서 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;새 표현 대응&lt;/td&gt;
&lt;td&gt;수작업 갱신 필요&lt;/td&gt;
&lt;td&gt;학습 데이터에 좌우됨&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;WordNet에서는 관계를 사람이 직접 읽을 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;car IS-A motor vehicle
wheel PART-OF car&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;임베딩에서는 단어나 문장이 고정된 크기의 벡터로 표현된다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;car → [0.17, -0.42, 0.08, ...]&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;벡터의 각 차원에 &lt;code&gt;상위어&lt;/code&gt;나 &lt;code&gt;부분 관계&lt;/code&gt; 같은 의미가 하나씩 붙어 있지는 않다. 의미 정보가 여러 차원에 분산되어 있기 때문에 표현 간 유사성은 잘 계산할 수 있지만, 왜 가까운지를 관계 이름으로 설명하기는 어렵다.&lt;/p&gt;
&lt;p&gt;둘 중 하나가 항상 더 우수한 것은 아니다. WordNet은 명시적인 관계와 설명 가능성이 필요한 작업에 유용하고, 임베딩은 대규모 데이터와 문맥을 활용해야 하는 작업에 강하다. 실제 자연어 처리 시스템에서는 두 방식을 함께 활용하기도 한다.&lt;/p&gt;
&lt;br/&gt;


&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://wordnet.princeton.edu&quot;&gt;https://wordnet.princeton.edu&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://wordnet.princeton.edu/documentation/wngloss7wn&quot;&gt;https://wordnet.princeton.edu/documentation/wngloss7wn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.nltk.org/howto/wordnet.html&quot;&gt;https://www.nltk.org/howto/wordnet.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>AI &amp;amp; ML/NLP &amp;amp; LLM</category>
      <category>LexicalSemantics</category>
      <category>NLP</category>
      <category>SemanticNetwork</category>
      <category>Synset</category>
      <category>thesaurus</category>
      <category>wordnet</category>
      <author>딩둘기</author>
      <guid isPermaLink="true">https://100s.tistory.com/70</guid>
      <comments>https://100s.tistory.com/70#entry70comment</comments>
      <pubDate>Thu, 23 Jul 2026 22:52:10 +0900</pubDate>
    </item>
    <item>
      <title>뉴스 피드 시스템 설계에서 Fanout이 중요한 이유</title>
      <link>https://100s.tistory.com/69</link>
      <description>&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&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;누군가 게시물을 작성했을 때 그 게시물을 팔로워들의 피드에 미리 넣어둘 것인지, 아니면 사용자가 피드를 열었을 때 게시물을 모아올 것인지 결정해야 하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;『가상 면접 사례로 배우는 대규모 시스템 설계 기초』&lt;/b&gt;의 뉴스 피드 시스템 설계 장을 읽으면서, 전체 구조보다 특히 흥미롭게 느껴졌던 부분은 게시물을 사용자 피드에 반영하는 방식이었다. 기존 프로젝트에서는 사용자가 피드를 요청할 때 팔로우 관계를 조회하고 게시물을 정렬해 반환했다. 소규모 환경에서는 단순하고 관리하기 쉬웠지만, 팔로우 수와 조회량이 증가할수록 읽기 비용이 커질 수 있다는 한계가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 게시물 작성 시점에 여러 사용자의 피드를 미리 구성하는 방식은 어떻게 동작하며, 두 방식은 각각 어떤 상황에서 적합한지 궁금해졌다. 이번 글에서는 뉴스 피드 시스템의 전체 흐름을 간단히 살펴본 뒤, 피드 설계의 핵심인 Fanout을 중심으로 조금 더 깊게 정리해 보았다.&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;뉴스 피드 시스템은 크게 게시물을 작성하는 과정과 피드를 읽는 과정으로 나눌 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 게시물을 작성하면 서버는 먼저 게시물 내용을 데이터베이스에 저장한다. 이미지나 동영상은 Object Storage에 저장하고, 게시물에는 해당 파일의 URL을 기록할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음에는 작성된 게시물이 친구나 팔로워의 피드에 나타나도록 처리해야 한다. 이 작업을 Fanout이라고 한다.&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;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOyORp/dJMcabdRvId/wsllVb3O53zcXX2HBy2tDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOyORp/dJMcabdRvId/wsllVb3O53zcXX2HBy2tDk/img.png&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;1108&quot; data-is-animation=&quot;false&quot; style=&quot;width: 51.5761%; margin-right: 10px;&quot; data-widthpercent=&quot;52.18&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOyORp/dJMcabdRvId/wsllVb3O53zcXX2HBy2tDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOyORp%2FdJMcabdRvId%2FwsllVb3O53zcXX2HBy2tDk%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;720&quot; height=&quot;1108&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4xW5b/dJMcahrK5vL/64FLCm5nOKoekN6w5DBv41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4xW5b/dJMcahrK5vL/64FLCm5nOKoekN6w5DBv41/img.png&quot; data-origin-width=&quot;524&quot; data-origin-height=&quot;880&quot; data-is-animation=&quot;false&quot; style=&quot;width: 47.2611%;&quot; data-widthpercent=&quot;47.82&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4xW5b/dJMcahrK5vL/64FLCm5nOKoekN6w5DBv41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4xW5b%2FdJMcahrK5vL%2F64FLCm5nOKoekN6w5DBv41%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;524&quot; height=&quot;880&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;게시물 작성 흐름 / 피드 조회 흐름&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;팔로워가 10명인 사용자와 100만 명인 사용자의 처리 시간이 완전히 다르기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 게시물 저장이 끝나면 사용자에게 먼저 성공 응답을 보내고, Fanout 작업은 메시지 큐와 Worker를 이용해 비동기로 처리하는 구조를 사용할 수 있다.&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;Fanout이란 무엇인가&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fanout은 하나의 이벤트를 여러 대상에게 전달하는 과정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뉴스 피드에서는 한 사용자가 작성한 게시물을 여러 친구나 팔로워의 피드에 반영하는 작업을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 A의 팔로워가 B, C, D라면 A가 작성한 게시물을 다음과 같이 전달할 수 있다.&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;1200&quot; data-origin-height=&quot;401&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Wc01m/dJMcadCHg70/k7UDqiTWKdYlgy2xERAkJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Wc01m/dJMcadCHg70/k7UDqiTWKdYlgy2xERAkJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Wc01m/dJMcadCHg70/k7UDqiTWKdYlgy2xERAkJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWc01m%2FdJMcadCHg70%2Fk7UDqiTWKdYlgy2xERAkJ1%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;641&quot; height=&quot;214&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;401&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;A의 팔로워가 세 명이면 세 번의 작업으로 끝난다. 하지만 A가 수백만 명의 팔로워를 가진 유명인이라면 게시물 하나를 작성할 때 수백만 건의 피드 갱신 작업이 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 어떻게 처리하는지에 따라 Fanout-on-Write와 Fanout-on-Read 두 가지 방식으로 나뉜다.&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;Fanout-on-Write: 게시물을 미리 피드에 넣어두는 방식&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fanout-on-Write는 게시물이 작성되는 시점에 팔로워들의 피드를 미리 갱신하는 방식이다. Push 모델이라고도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A가 게시물을 작성하면 Fanout Service는 A의 팔로워 목록을 가져오고, 각 팔로워의 Feed Cache에 A의 게시물 ID를 추가한다.&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;774&quot; data-origin-height=&quot;942&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p8SJF/dJMcahSG1GK/axQVSklBep4Bje6kU3rTO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p8SJF/dJMcahSG1GK/axQVSklBep4Bje6kU3rTO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p8SJF/dJMcahSG1GK/axQVSklBep4Bje6kU3rTO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp8SJF%2FdJMcahSG1GK%2FaxQVSklBep4Bje6kU3rTO0%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;605&quot; height=&quot;736&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;942&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;사용자가 피드를 조회할 때는 이미 피드가 만들어져 있다. 따라서 Feed Cache에 저장된 게시물 ID 목록을 읽기만 하면 된다.&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;하지만 모든 사용자에게 이 방식을 적용하면 유명인 계정에서 문제가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팔로워가 500만 명인 사용자가 게시물 하나를 작성했다고 가정하면 500만 명의 Feed Cache를 갱신해야 한다. Worker를 여러 대 사용해 병렬로 처리하더라도 상당한 작업량이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유명인이 짧은 시간에 여러 게시물을 작성하면 특정 사용자와 관련된 작업이 한꺼번에 몰릴 수도 있다. 이처럼 일부 인기 사용자나 데이터에 트래픽이 집중되는 현상을 Hot Key 문제라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 서비스를 거의 사용하지 않는 사용자의 피드까지 매번 갱신한다는 문제도 있다. 몇 달 동안 로그인하지 않은 사용자에게도 새로운 게시물을 계속 Push하면 실제로 조회되지 않을 데이터를 위해 저장 공간과 연산 자원을 사용하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fanout-on-Write는 읽기 성능을 높이는 대신, 쓰기 작업이 커지고 데이터가 중복 저장될 수 있는 방식이다.&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;Feed Cache에는 무엇을 저장할까&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fanout-on-Write를 사용할 때 각 사용자의 Feed Cache에 게시물 전체 내용을 저장할 필요는 없다.&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;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;postId&quot;: 100,
  &quot;authorId&quot;: 1,
  &quot;content&quot;: &quot;새로운 게시물&quot;,
  &quot;imageUrls&quot;: [&quot;...&quot;],
  &quot;createdAt&quot;: &quot;...&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 데이터를 팔로워 수만큼 저장하는 대신 Feed Cache에는 게시물 ID와 정렬에 필요한 최소 정보만 저장한다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;user:B &amp;rarr; [post:100, post:93, post:81]
user:C &amp;rarr; [post:100, post:88, post:74]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 게시물 내용은 Post Cache에서 조회하고, 작성자 정보는 User Cache에서 가져와 최종 응답을 구성한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Feed Cache
&amp;rarr; 사용자가 볼 게시물 ID 목록

Post Cache
&amp;rarr; 게시물 내용

User Cache
&amp;rarr; 작성자 정보&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;Feed Cache에 게시물 ID만 저장하면 중복을 줄일 수 있지만, 피드를 읽을 때 여러 캐시에서 데이터를 조합해야 한다. 이때 게시물마다 캐시를 한 번씩 조회하면 요청 횟수가 지나치게 많아질 수 있으므로 여러 게시물을 한 번에 가져오는 Batch 조회가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 피드에 게시물 20개가 있다면 게시물 캐시를 20번 호출하는 대신, 20개의 ID를 한 번에 전달해 조회하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Feed Cache 자체도 무한히 커질 필요는 없다. 대부분의 사용자는 최근 게시물 위주로 확인하므로 최근 수백 개 또는 수천 개의 게시물 ID만 캐시에 유지할 수 있다. 오래된 게시물이 필요한 경우에는 데이터베이스나 별도의 영속 저장소에서 가져온다.&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;Fanout-on-Read: 피드를 열 때 게시물을 모으는 방식&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fanout-on-Read는 사용자가 피드를 조회하는 시점에 친구나 팔로우한 사용자의 게시물을 모아 피드를 만드는 방식이다. Pull 모델이라고도 한다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;사용자가 피드 조회
      &amp;darr; 
팔로우한 사용자 목록 조회
            &amp;darr;
각 사용자의 최근 게시물 조회
            &amp;darr; 
게시물을 시간순으로 병합
            &amp;darr;
최종 피드 반환&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식에서는 게시물을 작성할 때 팔로워들의 피드를 갱신하지 않는다. 게시물 자체만 저장하면 되므로 쓰기 비용이 작다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 팔로워가 많은 유명인이 게시물을 작성하더라도 하나의 게시물을 저장하는 것으로 작업이 끝난다. 수백만 명의 Feed Cache를 갱신할 필요가 없다.&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;사용자가 1,000명을 팔로우하고 있다면 최대 1,000명의 최근 게시물을 조회한 뒤 하나의 목록으로 합쳐야 한다. 게시물을 단순히 모두 가져와 정렬하면 조회량과 정렬 비용이 커질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로는 각 사용자에게서 최신 게시물을 일부씩 가져온 뒤 여러 정렬된 목록을 병합하는 방식으로 처리할 수 있다. 각 목록이 이미 시간순으로 정렬되어 있다면 모든 게시물을 다시 전체 정렬하기보다 우선순위 큐를 이용해 필요한 개수만 추출할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 사용자에게 최근 게시물 20개만 보여주면 된다면, 수천 개의 게시물을 전부 정렬하지 않고 각 목록의 가장 최신 게시물부터 비교해 상위 20개만 선택하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 Fanout-on-Write처럼 이미 완성된 피드를 읽는 것보다는 처리 과정이 복잡하고 응답 시간이 길어질 가능성이 크다.&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;결국 Hybrid 방식이 필요하다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 방식을 비교하면 하나가 무조건 더 좋은 것은 아니다.&lt;/p&gt;
&lt;table style=&quot;height: 58px;&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;th style=&quot;height: 20px;&quot;&gt;방식&lt;/th&gt;
&lt;th style=&quot;height: 20px;&quot;&gt;게시물 작성&lt;/th&gt;
&lt;th style=&quot;height: 20px;&quot;&gt;피드 조회&lt;/th&gt;
&lt;th style=&quot;height: 20px;&quot;&gt;적합한 경우&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Fanout-on-Write&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;무거움&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;빠름&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;일반 사용자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Fanout-on-Read&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;가벼움&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;무거움&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;유명인, 비활성 사용자&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 사용자는 팔로워 수가 많지 않으므로 게시물을 작성할 때 미리 Feed Cache를 갱신해도 감당할 수 있다. 이렇게 하면 대부분의 사용자가 빠르게 피드를 조회할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 팔로워가 매우 많은 사용자는 Fanout-on-Write 대상에서 제외한다. 해당 사용자의 게시물은 사용자가 피드를 조회할 때 별도로 가져온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 피드는 다음 두 결과를 합쳐서 만든다.&lt;/p&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;일반 사용자의 게시물
&amp;rarr; Feed Cache에 미리 저장

유명인의 게시물
&amp;rarr; 조회 시 가져오기

두 목록을 시간순으로 병합
&amp;rarr; 최종 뉴스 피드&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 Hybrid Fanout 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 단순히 팔로워 수가 일정 기준을 넘으면 유명인으로 분류하면 된다고 생각했다. 하지만 실제로는 팔로워 수만으로 기준을 정하기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팔로워 수가 많아도 게시물을 거의 작성하지 않는 사용자가 있고, 팔로워 수는 상대적으로 적지만 매우 자주 게시물을 작성하는 사용자도 있기 때문이다. 또한 팔로워 중 실제 활성 사용자의 비율에 따라서도 필요한 Fanout 작업량이 달라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Push와 Pull을 나누는 기준은 단순한 팔로워 수보다 다음 요소를 함께 고려하는 편이 자연스럽다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;팔로워 수&lt;/li&gt;
&lt;li&gt;활성 팔로워 비율&lt;/li&gt;
&lt;li&gt;게시물 작성 빈도&lt;/li&gt;
&lt;li&gt;Fanout Worker의 현재 처리량&lt;/li&gt;
&lt;li&gt;허용 가능한 피드 반영 지연 시간&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 Celebrity 계정의 기준은 서비스 상황에 따라 달라지는 운영 정책에 가깝다.&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;Fanout 작업에는 왜 메시지 큐가 필요할까&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fanout-on-Write에서는 하나의 게시물이 수많은 피드 갱신 작업으로 확장된다. 이 작업을 게시물 작성 API에서 직접 처리하면 팔로워 수에 따라 응답 시간이 달라진다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;게시물 저장
       &amp;darr;
Fanout 작업을 메시지 큐에 등록
       &amp;darr;
사용자에게 작성 성공 응답
       &amp;darr;
Fanout Worker가 비동기로 처리&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메시지 큐를 사용하면 게시물 작성 요청과 피드 갱신 작업을 분리할 수 있다. 순간적으로 게시물이 많이 작성되더라도 큐가 작업을 임시로 보관하고 Worker가 처리 가능한 속도로 소비한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Worker 수도 트래픽에 따라 늘리거나 줄일 수 있다. 큐에 쌓인 메시지 수나 메시지 처리 지연 시간을 기준으로 Worker를 자동 확장하는 방식도 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 비동기로 처리하면 새로운 문제도 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메시지가 중복 전달되면 동일한 게시물 ID가 Feed Cache에 두 번 들어갈 수 있고, Worker가 처리 도중 실패하면 일부 사용자의 피드만 갱신될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Fanout Worker는 같은 작업을 여러 번 처리해도 결과가 달라지지 않도록 멱등성을 고려해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Feed Cache에 게시물을 추가할 때 단순 리스트 대신 게시물 ID를 기준으로 중복을 방지할 수 있는 자료구조를 사용하거나, &lt;code&gt;(userId, postId)&lt;/code&gt; 조합을 작업의 고유 키로 관리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재시도 횟수를 초과한 메시지는 별도의 Dead Letter Queue로 보내 원인을 확인할 수 있다. 피드 반영이 약간 지연되는 것은 허용할 수 있지만, 작업이 조용히 사라지는 것은 막아야 하기 때문이다.&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;게시물이 삭제되면 이미 Fanout된 데이터는 어떻게 할까&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fanout-on-Write에서는 게시물 ID가 여러 사용자의 Feed Cache에 들어간다. 그렇다면 원본 게시물이 삭제되었을 때 모든 사용자의 피드에서 해당 ID를 즉시 찾아 제거해야 할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팔로워가 많은 사용자의 게시물이라면 삭제 작업 역시 대규모 Fanout이 될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 가지 방법은 게시물 원본에 삭제 상태를 기록하고, 피드를 조합하는 과정에서 삭제된 게시물을 제외하는 것이다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;Feed Cache
&amp;rarr; postId 100 포함

Post Cache 또는 DB
&amp;rarr; postId 100은 삭제 상태

최종 피드 구성
&amp;rarr; 삭제된 게시물 제외&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 Feed Cache에서는 백그라운드 작업을 통해 해당 ID를 천천히 제거할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 즉시 모든 캐시를 수정하는 비용을 줄여주지만, 피드 목록에 유효하지 않은 ID가 남을 수 있다. 삭제 게시물이 많아지면 원하는 개수만큼 피드를 반환하지 못할 수 있으므로 부족한 게시물을 추가로 조회하는 처리도 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 지점에서도 캐시는 원본 데이터가 아니라는 점이 중요하다. Feed Cache에 게시물 ID가 존재하더라도 실제 게시물이 삭제되었거나 공개 범위가 변경되었다면 최종 응답에서는 제외해야 한다.&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;Fanout을 메시지 큐로 비동기 처리하면 게시물을 작성한 직후 모든 팔로워의 피드에 동시에 나타나는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 사용자의 Feed Cache는 바로 갱신되고, 다른 사용자의 피드는 몇 초 후에 갱신될 수 있다.&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;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;Fanout-on-Write는 게시물 작성 시점에 피드를 미리 만들어 읽기를 빠르게 한다. 하지만 팔로워가 많은 사용자는 하나의 게시물만으로도 대량의 갱신 작업을 발생시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fanout-on-Read는 게시물 작성 비용을 줄일 수 있지만, 사용자가 피드를 조회할 때 여러 사용자의 게시물을 모아 정렬해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 실제 시스템에서는 일반 사용자에게 Fanout-on-Write를 적용하고, 유명인처럼 Fanout 비용이 큰 사용자에게 Fanout-on-Read를 적용하는 Hybrid 방식을 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;책을 읽기 전에는 뉴스 피드를 단순히 친구 게시물을 시간순으로 조회하는 기능이라고 생각했다. 하지만 대규모 시스템에서는 같은 기능도 계산 시점을 어디에 둘 것인지에 따라 구조가 크게 달라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쓰기 시점에 미리 계산하면 읽기가 빨라지고, 계산을 미루면 쓰기는 가벼워지지만 읽기가 복잡해진다. 결국 뉴스 피드 설계의 핵심은 Push와 Pull 중 하나를 선택하는 것이 아니라, 사용자별 트래픽 특성을 기준으로 두 방식의 비용을 적절히 분배하는 데 있다고 이해했다.&lt;/p&gt;</description>
      <category>Study Notes</category>
      <category>fanout</category>
      <category>가상면접사례로배우는대규모시스템설계기초</category>
      <category>뉴스피드시스템</category>
      <author>딩둘기</author>
      <guid isPermaLink="true">https://100s.tistory.com/69</guid>
      <comments>https://100s.tistory.com/69#entry69comment</comments>
      <pubDate>Wed, 22 Jul 2026 22:14:53 +0900</pubDate>
    </item>
    <item>
      <title>[Flutter] Could not find an option named &amp;quot;no-sound-null-safety&amp;quot;.</title>
      <link>https://100s.tistory.com/68</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;문제 원인 : VsCode 내 다음과 같은 오류가 발생&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;오랜만에 플러터 프로젝트 맡게 되었는데 다음과 같은 오류가 날 맞이했다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1714460422383&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Could not find an option named &quot;no-sound-null-safety&quot;/
Run 'flutter-h' (or 'flutter &amp;lt;command&amp;gt; -h') for available flutter commands and options.

Exited (1)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;--no-sound-null-safety ?&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot; data-token-index=&quot;0&quot;&gt;--no-sound-null-safety 는 Flutter 프로젝트에서 널 포인터 예외를 방지하기 위한 기능이다. 이를 통해 프로그램의 안전성을 높이고 런타임 오류를 줄일 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;시도 1: 기본설정 Settings 변경&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot; data-token-index=&quot;0&quot;&gt;1. Code - 기본설정 - 설정&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xjuuw/btsG49Vh7M6/wN8ujnRQgIhhOeaLPPShm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xjuuw/btsG49Vh7M6/wN8ujnRQgIhhOeaLPPShm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xjuuw/btsG49Vh7M6/wN8ujnRQgIhhOeaLPPShm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fxjuuw%2FbtsG49Vh7M6%2FwN8ujnRQgIhhOeaLPPShm0%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;550&quot; height=&quot;359&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1306&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;2. flutter run additional 검색 - Flutter Run Additional Args에 Add Item - &amp;ldquo;&amp;mdash;no-sound-null-safety&amp;rdquo; 입력 &lt;s&gt;( 나 같은 경우에는 이 부분이 이미 추가되어있었다. )&lt;/s&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;760&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nXEKF/btsG4hzldNx/WK1PiPcoR3KTeK0ew6lsE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nXEKF/btsG4hzldNx/WK1PiPcoR3KTeK0ew6lsE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nXEKF/btsG4hzldNx/WK1PiPcoR3KTeK0ew6lsE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnXEKF%2FbtsG4hzldNx%2FWK1PiPcoR3KTeK0ew6lsE1%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;634&quot; height=&quot;760&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;760&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;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;3. .vscode - launch.json 파일 - args에 &amp;ldquo;&amp;mdash;no-sound-null-safety&amp;rdquo; 추가&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1106&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIv2cW/btsG3AMQxMw/pJ3JEL5UcyFDvVoHyd2j11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIv2cW/btsG3AMQxMw/pJ3JEL5UcyFDvVoHyd2j11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIv2cW/btsG3AMQxMw/pJ3JEL5UcyFDvVoHyd2j11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIv2cW%2FbtsG3AMQxMw%2FpJ3JEL5UcyFDvVoHyd2j11%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;562&quot; height=&quot;1106&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1106&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;여전히 &amp;ldquo;&amp;mdash;no-sound-null-safety&amp;rdquo; 문제가 발생하여 다른 방법을 찾아보다가 새로운 사실을 발견했다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Flutter 3.10.0 release notes에 따르면 해당 명령어가 3.10.0 부터 삭제되었다고 한다. Flutter 2.12 버전부터 null 안전성 기능이 기본으로 적용되었는데 그 이후로 옵션 자체가 더 이상 필요하지 않아서 삭제한 모양이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;367&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Xbs3N/btsG3AMQ8kb/Vkxx925GVcc5ngpK5jlnD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Xbs3N/btsG3AMQ8kb/Vkxx925GVcc5ngpK5jlnD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Xbs3N/btsG3AMQ8kb/Vkxx925GVcc5ngpK5jlnD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXbs3N%2FbtsG3AMQ8kb%2FVkxx925GVcc5ngpK5jlnD0%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;713&quot; height=&quot;367&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;367&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1714460948709&quot; style=&quot;color: #333333; text-align: start;&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bAO2He/hyVZfMtfj2/PW4W6sX3HuRt1KQPvhFoEK/img.png?width=937&amp;amp;height=461&amp;amp;face=0_0_937_461&quot; data-og-url=&quot;https://docs.flutter.dev/release/release-notes/release-notes-3.10.0&quot; data-og-source-url=&quot;https://docs.flutter.dev/release/release-notes/release-notes-3.10.0&quot; data-og-host=&quot;docs.flutter.dev&quot; data-og-description=&quot;Release notes for Flutter 3.10.0.&quot; data-og-title=&quot;Flutter 3.10.0 release notes&quot; data-og-type=&quot;website&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://docs.flutter.dev/release/release-notes/release-notes-3.10.0&quot; data-source-url=&quot;https://docs.flutter.dev/release/release-notes/release-notes-3.10.0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bAO2He/hyVZfMtfj2/PW4W6sX3HuRt1KQPvhFoEK/img.png?width=937&amp;amp;height=461&amp;amp;face=0_0_937_461');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;Flutter 3.10.0 release notes&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;Release notes for Flutter 3.10.0.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;docs.flutter.dev&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;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;해결 방법 : &amp;ldquo;&amp;mdash;no-sound-null-safety&amp;rdquo; 모두 제거&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vscode내부에 있는 &amp;ldquo;&amp;mdash;no-sound-null-safety&amp;rdquo; 항목을 모두 제거하면 정상적으로 작동이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나 같은 경우는 Flutter Run Additional Args에 &amp;mdash;no-sound-null-safety 아이템이 존재했던 것이 문제였던 것으로 보인다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;739&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIsfJp/btsG35y48f5/hrJkfsvKFzUsaX7AI84xeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIsfJp/btsG35y48f5/hrJkfsvKFzUsaX7AI84xeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIsfJp/btsG35y48f5/hrJkfsvKFzUsaX7AI84xeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIsfJp%2FbtsG35y48f5%2FhrJkfsvKFzUsaX7AI84xeK%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;638&quot; height=&quot;236&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;739&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;594&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJBIqB/btsG2osIfSe/eMpKjYbzqN0nYAzb6IFHY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJBIqB/btsG2osIfSe/eMpKjYbzqN0nYAzb6IFHY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJBIqB/btsG2osIfSe/eMpKjYbzqN0nYAzb6IFHY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJBIqB%2FbtsG2osIfSe%2FeMpKjYbzqN0nYAzb6IFHY0%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;641&quot; height=&quot;594&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;594&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;</description>
      <category>Study Notes</category>
      <category>Flutter</category>
      <author>딩둘기</author>
      <guid isPermaLink="true">https://100s.tistory.com/68</guid>
      <comments>https://100s.tistory.com/68#entry68comment</comments>
      <pubDate>Tue, 30 Apr 2024 16:06:20 +0900</pubDate>
    </item>
    <item>
      <title>Hack for Public Good in Korea Gen.AI 해커톤 2024 / KDI 국제정책대학원, 마이크로소프트</title>
      <link>https://100s.tistory.com/67</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-04-23 오후 2.22.30.png&quot; data-origin-width=&quot;1456&quot; data-origin-height=&quot;694&quot;&gt;&lt;a href=&quot;https://event-us.kr/aiinnovation/event/80730&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nlV9l/btsGSPoVyJx/F9ExZMcCgadLGHIyTptlU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnlV9l%2FbtsGSPoVyJx%2FF9ExZMcCgadLGHIyTptlU1%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;1456&quot; height=&quot;694&quot; data-filename=&quot;스크린샷 2024-04-23 오후 2.22.30.png&quot; data-origin-width=&quot;1456&quot; data-origin-height=&quot;694&quot;/&gt;&lt;/a&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;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;- Hack for Public Good in Korea Gen.AI 해커톤&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;싱가폴 Hack for Public Good에 영감을 받은 행사로 생성형 AI 기술로 공공부문 업무혁신을 실현할 수 있는 아이디어 창출 및 솔루션을 개발하는 해커톤 행사&lt;/blockquote&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;지난 4월 13일 KID국제정책대학원과 마이크로소프트가 함께하는 생성형 AI를 활용한 행사에 참여하였다  &lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;참가자들에게 OpenAI를 이용해 챗봇 개발을 할 수 있도록 교육해주며 토큰도 (거의) 무제한이라고 해서 좋은 기회라고 생각되었다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;i&gt; 음성이나 이미지 처리하시는 팀들은 토큰을 금방 다 쓰셔서 발표할 때 애를 먹으시기도 했다.&lt;/i&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cU2R9d/btsGPaax1En/3pwaTPiCvUKXaZYK2O2uF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cU2R9d/btsGPaax1En/3pwaTPiCvUKXaZYK2O2uF0/img.png&quot; width=&quot;456&quot; height=&quot;608&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;4032&quot; data-origin-width=&quot;3024&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cU2R9d/btsGPaax1En/3pwaTPiCvUKXaZYK2O2uF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcU2R9d%2FbtsGPaax1En%2F3pwaTPiCvUKXaZYK2O2uF0%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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NW2Cf/btsGQ3Pg7eL/xIV9vsiMpSK39wETQeRIkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NW2Cf/btsGQ3Pg7eL/xIV9vsiMpSK39wETQeRIkk/img.png&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; width=&quot;420&quot; height=&quot;560&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NW2Cf/btsGQ3Pg7eL/xIV9vsiMpSK39wETQeRIkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNW2Cf%2FbtsGQ3Pg7eL%2FxIV9vsiMpSK39wETQeRIkk%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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;마이크로소프트 건물은 경복궁 바로 앞에 있는데&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;경복궁에 놀러갈 때마다 참 전망이 좋아보여서 올라가보고 싶었었다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;2250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KkDed/btsGR318dTt/a3h0XDht95mRdZGIJ72XB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KkDed/btsGR318dTt/a3h0XDht95mRdZGIJ72XB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KkDed/btsGR318dTt/a3h0XDht95mRdZGIJ72XB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKkDed%2FbtsGR318dTt%2Fa3h0XDht95mRdZGIJ72XB1%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;3000&quot; height=&quot;2250&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;2250&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;말도 안되는 전경.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;외국인 친구들 데려오고 싶을 지경이였다..&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;이런데에서 근무한다면 야근 나쁘지 않을지도?&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mD3mP/btsGR23eh9l/8sWXmJTduMPZ62OI3lgn4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mD3mP/btsGR23eh9l/8sWXmJTduMPZ62OI3lgn4K/img.png&quot; data-origin-width=&quot;1899&quot; data-origin-height=&quot;2250&quot; data-is-animation=&quot;false&quot; data-filename=&quot;blob&quot; style=&quot;width: 52.3329%; margin-right: 10px;&quot; data-widthpercent=&quot;52.95&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mD3mP/btsGR23eh9l/8sWXmJTduMPZ62OI3lgn4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmD3mP%2FbtsGR23eh9l%2F8sWXmJTduMPZ62OI3lgn4K%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;1899&quot; height=&quot;2250&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7AeT7/btsGR3HOPHN/ZV8yBtxvzX8LazJ80AU8o0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7AeT7/btsGR3HOPHN/ZV8yBtxvzX8LazJ80AU8o0/img.png&quot; data-origin-width=&quot;2250&quot; data-origin-height=&quot;3000&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;47.05&quot; data-filename=&quot;blob&quot; style=&quot;width: 46.5043%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7AeT7/btsGR3HOPHN/ZV8yBtxvzX8LazJ80AU8o0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7AeT7%2FbtsGR3HOPHN%2FZV8yBtxvzX8LazJ80AU8o0%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;2250&quot; height=&quot;3000&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;OT는 1시부터, 교육은 2시부터였는데 1시부터 온 사람들은 거의 없었던 것 같다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;정시에 도착했는데 텅텅 비어있었던 !&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;701&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Qh73A/btsGQ5lZrYY/Xrm7T1HE3wGwTHlX2uZSDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Qh73A/btsGQ5lZrYY/Xrm7T1HE3wGwTHlX2uZSDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Qh73A/btsGQ5lZrYY/Xrm7T1HE3wGwTHlX2uZSDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQh73A%2FbtsGQ5lZrYY%2FXrm7T1HE3wGwTHlX2uZSDK%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;1170&quot; height=&quot;701&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;701&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;행사운영 패들렛에 들어가면 행사 일정과 어떤 사람들이 참여했는지 볼 수 있었다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;거의 34팀 정도였던 듯 하다!&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;패들렛은 팀원을 구하고, 중간 자료나 자기소개를 업로드 하는 등&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;해커톤 전반적으로 유용하게 사용했다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;- 해커톤 Azure OpenAI 교육과정 소개&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;교육은 총 4시간으로 진행되었고, 다음과 같은 교육을 받을 수 있었다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 생성형 AI의 기본 개념과 주요 개념을 소개 (0.5)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. 프롬프트 엔지니어링을 통해서 충분한 결과를 얻기위한 여러가지 기법들을 학습 &lt;span style=&quot;color: #000000; text-align: center;&quot;&gt;(0.5)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. OpenAI에서 제공하는 모델들을 설명하고 이를 Azure에서 제공하고 있는 Azure OpenAI Studio를 활용 &lt;span style=&quot;color: #000000; text-align: center;&quot;&gt;(1)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4. OpenAI 모델을 활용한 Chatbot을 개발해보고 각자 목적에 맞는 Chatbot 개발&lt;span style=&quot;color: #000000; text-align: center;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: center;&quot;&gt;(1)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;5. 이미지를 생성하기 위해서 OpenAI의 DALL-E3을 사용하는 서비스 개발&lt;span style=&quot;color: #000000; text-align: center;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: center;&quot;&gt;(1)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p 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 style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아래는 간단하게 Azure OpenAI를 이용해서 세종대왕이 누군지 물어볼 수 있는 간단한 코드 예시이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1713850189564&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import openai

openai.api_key = 'open_api_key'
openai.azure_endpoint = 'azure_endpoint'
openai.api_type = 'azure'
openai.api_version = '2023-05-15'

result = openai.chat.completions.create(
    model='dev-gpt-35-turbo-01',
    messages=[
        {&quot;role&quot;: &quot;system&quot;, &quot;content&quot;: &quot;You are a helpful assistant&quot;},
        {&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;세종대왕은 누구?&quot;},
    ]
)

print(result.choices[0].message.content)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;- 해커톤 진행&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;해커톤은 공공서비스의 혁신과 국민의 공공서비스 이용향상에 기여하는 아이디어 개발에 관한 네가지 주제로 진행이 되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;우리는 다음과 같은 주제를 선택하였다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;AI/Cloud Services for Goverment Productivity&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;: 정부 업무 효율과 생산성 향상을 위해 AI와 클라우드 사용, 문서처리부터 데이터 분석까지 최적화 목표&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;주제 선택 이후 팀원들은 공무원 지인들과 전화 인터뷰를 진행하여 업무 현실에 대해서 조사를 시작하였다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;공무원들 대다수는 온나라 플랫폼을 통해 업무를 진행하고 있으나, 온나라 플랫폼 2.0 개편 이후 불편을 호소하는 사람들이 많았다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;문제점 1. 검색 효율성이 낮음.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;: 응답자들은 온나라 문서시스템을 활용할 때, '키워드 선정 기준 부재'와 '키워드 개수 및 표현법 선택의 어려움' 때문에 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;키워드 작성에 어려움을 겪는다고 이야기하였다. 이렇게 전문 교육 없이 임의로 입력된 키워드는 검색 효율성을 매우 떨어뜨리는 결과를 야기한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;문제점 2. 검색 시스템의 한계&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;: 응답자들은 문서 검색 시, 제목과 특정 키워드로만 검색을 해야해서 원하는 자료를 찾기 어렵다는 불편을 이야기하였다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;추가적으로 1년치 자료만 검색이 가능해 기존 자료를 볼 수 있는 한계가 존재하다고 말했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;문제점 3: 인수인계 과정의 한계&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;: 공무원들이 자주 근무지를 옮겨야 하는 것에 비해 인수인계 과정이 미흡하며, &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;교육 자료가 매우 미흡하여 문서 작성에 어려움을 겪는다고 이야기하였다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;해결책 1. 과제 키워드 추천&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;: 첨부된 문서로부터 과제 키워드를 자동으로 생성해준다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 첨부된 pdf 로부터 요약본 자동 생성&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. 카테고리 키워드 후보 산출&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. 담당자 후보 산출&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4. 제목, 내용 후보 산출&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;해결책 2. RAG 기술을 활용한 의미기반 검색&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1년치 자료 뿐만 아니라 더 넓은 기간을 효율적으로 검색할 수 있도록 세팅한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;기존 제목, 특정 소수 키워드를 넘어 문서를 GPT로 분석하여 의미기반 검색이 가능하게 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;해결책 2. 기존 업무 프로세스 학습 후 실무자들에게 온보딩 프로그램 제공&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;실무자들이 업무를 진행할 수 있는 샘플을 모의고사처럼 제공한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;총 3시간의 긴 회의 이후&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;해커톤을 시작하였다. (12시..)&lt;/span&gt;&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;niniz&quot; data-emoticon-name=&quot;019&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/niniz/large/019.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/niniz/large/019.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/deLwfj/btsGSOp2yBD/HHc5npHNDRcn0jVMpKR831/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/deLwfj/btsGSOp2yBD/HHc5npHNDRcn0jVMpKR831/img.png&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; data-is-animation=&quot;false&quot; style=&quot;width: 68.6846%; margin-right: 10px;&quot; data-widthpercent=&quot;69.49&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/deLwfj/btsGSOp2yBD/HHc5npHNDRcn0jVMpKR831/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdeLwfj%2FbtsGSOp2yBD%2FHHc5npHNDRcn0jVMpKR831%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;4032&quot; height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bz2uss/btsGQ0L3ShQ/qFqbGP34vgzDkmpiZCbR1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bz2uss/btsGQ0L3ShQ/qFqbGP34vgzDkmpiZCbR1k/img.png&quot; data-origin-width=&quot;1756&quot; data-origin-height=&quot;3000&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;30.51&quot; data-filename=&quot;blob&quot; style=&quot;width: 30.1526%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bz2uss/btsGQ0L3ShQ/qFqbGP34vgzDkmpiZCbR1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbz2uss%2FbtsGQ0L3ShQ%2FqFqbGP34vgzDkmpiZCbR1k%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;1756&quot; height=&quot;3000&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;당연히 과자 무한리필이었던 해커톤&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;2250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clcqbw/btsGSj4ZqP6/X5KfzYOv3mBmPisiRRyEgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clcqbw/btsGSj4ZqP6/X5KfzYOv3mBmPisiRRyEgK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clcqbw/btsGSj4ZqP6/X5KfzYOv3mBmPisiRRyEgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fclcqbw%2FbtsGSj4ZqP6%2FX5KfzYOv3mBmPisiRRyEgK%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;3000&quot; height=&quot;2250&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;2250&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 style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;도시락 종류는 3가지였던 해커톤&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&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;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zQR1H/btsGQPcC3pE/BR5nJPcPCtkJRZyYgiX7oK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zQR1H/btsGQPcC3pE/BR5nJPcPCtkJRZyYgiX7oK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zQR1H/btsGQPcC3pE/BR5nJPcPCtkJRZyYgiX7oK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzQR1H%2FbtsGQPcC3pE%2FBR5nJPcPCtkJRZyYgiX7oK%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;3024&quot; height=&quot;4032&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;1인 1 닭강정이였지만 백엔드 mj님과 나눠먹었던 해커톤&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&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;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EzgPg/btsGQjrtzk2/3lSnSYJWpDKd5jdICd2AsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EzgPg/btsGQjrtzk2/3lSnSYJWpDKd5jdICd2AsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EzgPg/btsGQjrtzk2/3lSnSYJWpDKd5jdICd2AsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEzgPg%2FbtsGQjrtzk2%2F3lSnSYJWpDKd5jdICd2AsK%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;3024&quot; height=&quot;4032&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;개발해야 하는 양도 너무 많았고, 중간에 나 자신의 한계를 느껴버려서&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;마감시간인 오전 9시까지 못할 것 같다는 막연한 불안감이 들었다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;너무 불안해서 혼자 키다리책상 가서 개발 완료 할때까지 자리로 돌아가지 않았다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;i&gt;집중해서 오전 8시까지 기적적인 완료!&lt;/i&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;i&gt;개발하는 내내 심장이 너무 빨리 뛰었다..&lt;/i&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;- 해커톤 마무리&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;간단하게 공유해 보는 해커톤 결과!&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w1EZx/btsGPaV1G29/Rdft66t3ygfXrx46k6tsyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w1EZx/btsGPaV1G29/Rdft66t3ygfXrx46k6tsyK/img.png&quot; data-origin-width=&quot;1294&quot; data-origin-height=&quot;1562&quot; data-is-animation=&quot;false&quot; style=&quot;width: 45.2894%; margin-right: 10px;&quot; data-widthpercent=&quot;45.82&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w1EZx/btsGPaV1G29/Rdft66t3ygfXrx46k6tsyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw1EZx%2FbtsGPaV1G29%2FRdft66t3ygfXrx46k6tsyK%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;1294&quot; height=&quot;1562&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tbVCG/btsGS6c3FPU/wd40l3kAwhb4KHhfXMdk3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tbVCG/btsGS6c3FPU/wd40l3kAwhb4KHhfXMdk3k/img.png&quot; data-origin-width=&quot;1528&quot; data-origin-height=&quot;1560&quot; data-is-animation=&quot;false&quot; style=&quot;width: 53.5478%;&quot; data-widthpercent=&quot;54.18&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tbVCG/btsGS6c3FPU/wd40l3kAwhb4KHhfXMdk3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtbVCG%2FbtsGS6c3FPU%2Fwd40l3kAwhb4KHhfXMdk3k%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;1528&quot; height=&quot;1560&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000; text-align: center;&quot;&gt;가장 집중하였던, pdf 내용을 분석해 업무를 반자동으로 진행할 수 있도록 도와주는 서비스&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ciZyvu/btsGRqjeuq0/kwZQKxLSvFfbqkYcsUXKD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ciZyvu/btsGRqjeuq0/kwZQKxLSvFfbqkYcsUXKD1/img.png&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;2250&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;50&quot; data-filename=&quot;blob&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ciZyvu/btsGRqjeuq0/kwZQKxLSvFfbqkYcsUXKD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FciZyvu%2FbtsGRqjeuq0%2FkwZQKxLSvFfbqkYcsUXKD1%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;3000&quot; height=&quot;2250&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RKjn1/btsGS3ABUcw/H9ujIQjpEpO0KwBm3Zv9iK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RKjn1/btsGS3ABUcw/H9ujIQjpEpO0KwBm3Zv9iK/img.png&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;2250&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;50&quot; data-filename=&quot;blob&quot; style=&quot;width: 49.4186%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RKjn1/btsGS3ABUcw/H9ujIQjpEpO0KwBm3Zv9iK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRKjn1%2FbtsGS3ABUcw%2FH9ujIQjpEpO0KwBm3Zv9iK%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;3000&quot; height=&quot;2250&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;PPT에서 백소현 찾기.&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 style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;쟁쟁한 팀원들이 너무 많아서 아쉽게도 수상까지는 가지 못했다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;그래도 생성형 AI에 대해서 전반적인 생태계를 알게 되었으며 참가자들을 보며 많은 자극을 받았다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;또한 프론트엔드에 한정되지 않고 더 넓은 분야를 바라보아야 되겠다는 생각이 들었다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;마지막으로 최근 프론트엔드 개발에 대해서 많이 위축되어 있었던 것 같은데,&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;시간 내에 정해진 양을 성공적으로 끝마치면서&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;잠시나마 해커톤을 통해 자신감을 회복할 수 있는 계기가 되었던 것 같다!&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study Notes</category>
      <category>AI</category>
      <category>Azure</category>
      <category>GenAI</category>
      <category>hackertone</category>
      <category>HackForPublic</category>
      <category>KDI</category>
      <category>Microsoft</category>
      <category>OpenAI</category>
      <author>딩둘기</author>
      <guid isPermaLink="true">https://100s.tistory.com/67</guid>
      <comments>https://100s.tistory.com/67#entry67comment</comments>
      <pubDate>Tue, 23 Apr 2024 15:42:26 +0900</pubDate>
    </item>
    <item>
      <title>Dayjs vs. Moment.js: Dayjs를 사용해야하는 이유</title>
      <link>https://100s.tistory.com/66</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;801&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dGzXFc/btsEig4wX8b/XTGOu3Sq6ZugKBPGXwakAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dGzXFc/btsEig4wX8b/XTGOu3Sq6ZugKBPGXwakAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dGzXFc/btsEig4wX8b/XTGOu3Sq6ZugKBPGXwakAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdGzXFc%2FbtsEig4wX8b%2FXTGOu3Sq6ZugKBPGXwakAk%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;615&quot; height=&quot;246&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;801&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;프로젝트를 진행하다 보면 날짜와 관련된 여러가지 연산이 머리아프게 할 때가 많다. &lt;s&gt;물론 내가 아직 아기 개발자이기 때문이겠지만...&lt;/s&gt; 이런 상황에서 자주 사용되는 날짜 관련 라이브러리가 바로 dayjs와 momentjs인데, 각각의 프로젝트나 상황에 따라 선택되는 이유가 달라지게 된다. 이번 기회에 깔끔하게 정리해보려고 한다.&lt;/span&gt;&lt;span style=&quot;background-color: #343541; color: #d1d5db; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Dayjs vs Moment.js&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Moment.js와 Dayjs는 JavaScript 기반의 날짜 및 시간 라이브러리로, 특히 Moment.js는 과거에 많이 사용되었다. 현재는 Moment.js의 유지보수가 중단되어, 새로운 프로젝트에는 사용이 권장되지 않지만 레거시 프로젝트에서 여전히 많이 사용되고 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Dayjs는 Moment.js 중단 이후 많은 개발자들 사이에서 주목받게 되었으며, Moment.js와 유사한 API를 제공하면서도 크기가 작아 더 가벼운 라이브러리로 평가받고 있다. 현대적이고 경량화된 대안으로, 새로운 프로젝트에서는 Dayjs를 사용하는 것이 일반적이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;1. 크기와 성능 :&amp;nbsp; Moment.js의 부담과 Dayjs의 경량함&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Moment.js:&lt;/b&gt; Moment.js는 매우 풍부한 기능을 제공하는 대신 크기가 큰 라이브러리이다. Date 객체에 대한 다양한 연산을 지원하기에 앱에서 실제 사용되지 않는 기능이 번들에 포함될 가능성이 높다. 이는 번들 크기에 부담을 줄 수 있고, 성능 손실이 발생하게 된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Dayjs:&lt;/b&gt; Dayjs는 Moment.js의 경량화 버전으로, 주로 개발자들이 자주 사용하는 기본 기능만을 제공하여 작은 크기에 중점을 둔 라이브러리이다. Moment.js와 거의 유사한 API를 제공하지만 크기가 훨씬 작아 초기 로딩 속도 및 앱 크기에 미치는 영향이 적다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot; data-token-index=&quot;0&quot;&gt;2. 모듈성과 번들 최적화 :&amp;nbsp; 필요한 것만 가져오기&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Moment.js:&lt;/b&gt; Moment.js는 모든 기능을 기본적으로 포함하고 있어 번들 최적화가 어렵다. 사용하지 않는 기능까지 모두 번들에 포함되므로 앱의 크기가 크게 늘어나게 된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Dayjs:&lt;/b&gt; Dayjs는 경량하게 설계되어 필요한 기능만 제공한다. 또한, 필요에 따라 플러그인을 통해 추가 기능을 확장할 수 있다. 필요한 모듈만 선택하여 사용하므로 불필요한 부분을 제거하여 번들 크기를 최소화할 수 있다.&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. &lt;span data-token-index=&quot;1&quot;&gt;지속적인 개발과 업데이트&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Moment.js:&lt;/b&gt; Moment.js는 2020년 이후에는 더 이상 업데이트되지 않는다. 따라서 새로운 기능이나 보안 업데이트를 받을 수 없다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Dayjs:&lt;/b&gt; Dayjs는 여전히 활발하게 개발되어 새로운 기능 및 업데이트가 지속적으로 이루어진다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로 새로운 프로젝트에서는 Dayjs를 사용하는 것이 옳다. 레거시 프로젝트의 경우에는 의존성 여부와 프로젝트 요구 사항을 고려하여 이미그레이션 여부를 결정하는게 좋을 듯 하다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;참고자료&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.hoseung.me/2022-03-13-dayjs-instead-of-momentjs&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://blog.hoseung.me/2022-03-13-dayjs-instead-of-momentjs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Frontend/Javascript</category>
      <category>dayjs</category>
      <category>moment.js</category>
      <category>npm</category>
      <author>딩둘기</author>
      <guid isPermaLink="true">https://100s.tistory.com/66</guid>
      <comments>https://100s.tistory.com/66#entry66comment</comments>
      <pubDate>Fri, 2 Feb 2024 00:25:22 +0900</pubDate>
    </item>
    <item>
      <title>PostMan Mock Server 로 프론트엔드 개발속도 올리기</title>
      <link>https://100s.tistory.com/65</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1045&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zPdwf/btsD4VZWPhE/9uPkJbLIeAXVP36cPkdGTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zPdwf/btsD4VZWPhE/9uPkJbLIeAXVP36cPkdGTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zPdwf/btsD4VZWPhE/9uPkJbLIeAXVP36cPkdGTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzPdwf%2FbtsD4VZWPhE%2F9uPkJbLIeAXVP36cPkdGTK%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;490&quot; height=&quot;256&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1045&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Mock Server&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;Mock&quot;이란 가짜나 모조품을 의미한다. Mock Server는 실제 백엔드 서버가 아직 구축되지 않았을 때, 프론트엔드 개발자가 가상의 서버를 사용하여 개발 및 테스트를 진행할 수 있도록 도와줄 수 있다. 일반적으로 백엔드가 아직 완성되지 않은 초기 단계에서 프론트엔드 개발자는 실제 데이터를 기다리지 않고도 Mock Server를 활용하여 API 응답을 모방하고 프로젝트를 진행이 가능하다. Postman과 같은 도구를 이용하면 쉽게 Mock Server를 구성하여 협업과 개발 효율성을 높일 수 있다. (무료계정에서는 Mock Server Request 제한 횟수를 월 1000회로 제한하고 있어 지속적으로 사용하기에는 어려울 수 있다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;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;Postman으로 Mock Server 구성하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Postman을 실행하고 새로운 Collection을 만든 후, Sidebar에서 Mock Server를 활성화 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1180&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LdbHx/btsD09FzD1x/tLhxAVQvkeUZmu4GvdnxLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LdbHx/btsD09FzD1x/tLhxAVQvkeUZmu4GvdnxLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LdbHx/btsD09FzD1x/tLhxAVQvkeUZmu4GvdnxLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLdbHx%2FbtsD09FzD1x%2FtLhxAVQvkeUZmu4GvdnxLK%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;2000&quot; height=&quot;1180&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1180&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Create mock server를 선택한 후, 아까 생성한 Collection을 선택한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1177&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZIgC6/btsD3Mblrbz/pxxD0isK4R4DZmxibQoKfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZIgC6/btsD3Mblrbz/pxxD0isK4R4DZmxibQoKfK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZIgC6/btsD3Mblrbz/pxxD0isK4R4DZmxibQoKfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZIgC6%2FbtsD3Mblrbz%2FpxxD0isK4R4DZmxibQoKfK%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;2000&quot; height=&quot;1177&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1177&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. Mock Server Name을 지정해주고, Save the mock server URL as an new environment variables 체크 후 Create Mock Server 버튼을 눌러 생성한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1171&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cVC3XL/btsD3bvVwgV/gOKKKS9G6KfGeBMYXfpi1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cVC3XL/btsD3bvVwgV/gOKKKS9G6KfGeBMYXfpi1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cVC3XL/btsD3bvVwgV/gOKKKS9G6KfGeBMYXfpi1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcVC3XL%2FbtsD3bvVwgV%2FgOKKKS9G6KfGeBMYXfpi1K%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;2000&quot; height=&quot;1171&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1171&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 생성된 Mock Server의 주소를 복사해놓는다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1178&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Yv8Ty/btsD3JeNpiY/XredIaW5d4wcVR9eMjK24k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Yv8Ty/btsD3JeNpiY/XredIaW5d4wcVR9eMjK24k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Yv8Ty/btsD3JeNpiY/XredIaW5d4wcVR9eMjK24k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYv8Ty%2FbtsD3JeNpiY%2FXredIaW5d4wcVR9eMjK24k%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;2000&quot; height=&quot;1178&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1178&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Mock Server로 GET 요청 수행하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Collections 탭에서 Add request &amp;rarr; Get 요청을 생성한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1177&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beRIjT/btsD2ubDYnx/AobvZQvKctqpmMlsx6ZUU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beRIjT/btsD2ubDYnx/AobvZQvKctqpmMlsx6ZUU1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beRIjT/btsD2ubDYnx/AobvZQvKctqpmMlsx6ZUU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeRIjT%2FbtsD2ubDYnx%2FAobvZQvKctqpmMlsx6ZUU1%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;2000&quot; height=&quot;1177&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1177&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Add example에서 API 명세서를 기준으로 GET API를 제작한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;user를 조회하는 mock API를 생성한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1173&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCxTYz/btsD9Ddi4AT/RzSsrLm05z7KckrPLqBzb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCxTYz/btsD9Ddi4AT/RzSsrLm05z7KckrPLqBzb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCxTYz/btsD9Ddi4AT/RzSsrLm05z7KckrPLqBzb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCxTYz%2FbtsD9Ddi4AT%2FRzSsrLm05z7KckrPLqBzb1%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;2000&quot; height=&quot;1173&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1173&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. Mock Server에서 요청을 처리하여 응답 값이 오는 것을 확인할 수 있다. 또한 일반 브라우저에서도 Mock API가 동작하는 것을 확인가능하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3BrCv/btsD4HgsMRA/YjJ8YKs3ON0wkFsJksk9LK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3BrCv/btsD4HgsMRA/YjJ8YKs3ON0wkFsJksk9LK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3BrCv/btsD4HgsMRA/YjJ8YKs3ON0wkFsJksk9LK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3BrCv%2FbtsD4HgsMRA%2FYjJ8YKs3ON0wkFsJksk9LK%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;2000&quot; height=&quot;700&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Mock Server로 POST 요청 수행하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Add request &amp;rarr; POST 요청을 생성한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1176&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UzFFM/btsD6v0ZXj2/xY0WfHLy8nwEkiYnX5rVQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UzFFM/btsD6v0ZXj2/xY0WfHLy8nwEkiYnX5rVQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UzFFM/btsD6v0ZXj2/xY0WfHLy8nwEkiYnX5rVQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUzFFM%2FbtsD6v0ZXj2%2FxY0WfHLy8nwEkiYnX5rVQk%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;2000&quot; height=&quot;1176&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1176&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Add example에서 API 명세서를 기준으로 POST API를 제작하고, Body에 원하는 요청을 설정하며 응답 값을 정의한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Body &amp;gt; raw &amp;gt; JSON 요청을 통해 요청을 받도록 설정하고, 성공 시 응답을 Body에 포함시켰다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1172&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bd5IM4/btsD3JTodQO/NjXlXWucIShV0pJilpKw50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bd5IM4/btsD3JTodQO/NjXlXWucIShV0pJilpKw50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bd5IM4/btsD3JTodQO/NjXlXWucIShV0pJilpKw50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbd5IM4%2FbtsD3JTodQO%2FNjXlXWucIShV0pJilpKw50%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;2000&quot; height=&quot;1172&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1172&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;</description>
      <category>Frontend</category>
      <category>Postman</category>
      <author>딩둘기</author>
      <guid isPermaLink="true">https://100s.tistory.com/65</guid>
      <comments>https://100s.tistory.com/65#entry65comment</comments>
      <pubDate>Sun, 28 Jan 2024 22:50:06 +0900</pubDate>
    </item>
    <item>
      <title>자바스크립트 패키지 매니저 / npm 과 yarn 비교</title>
      <link>https://100s.tistory.com/64</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;628&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcXAwo/btsD0IgYxXl/ZtZkg510jiTkmdxftbEtjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcXAwo/btsD0IgYxXl/ZtZkg510jiTkmdxftbEtjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcXAwo/btsD0IgYxXl/ZtZkg510jiTkmdxftbEtjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcXAwo%2FbtsD0IgYxXl%2FZtZkg510jiTkmdxftbEtjk%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;541&quot; height=&quot;243&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;628&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node.js로 프로젝트를 진행할 때, 패키지 관리를 위해 주로 &lt;b&gt;npm&lt;/b&gt;과 &lt;b&gt;yarn&lt;/b&gt;을 선택한다. 프로젝트는 일관된 패키지 매니저를 사용하는 것이 중요하며 npm과 yarn은 패키지 관리 방식이 다르기 때문에 충돌이 날 수 있으므로 혼용하지 않는 것이 좋다. 따라서 프로젝트를 넘겨 받았을 때, yarn.lock 파일이 있다면 yarn을 사용하고, package-lock.json 파일이 있다면 npm을 사용하는 등 프로젝트 팀 간에 일관성을 유지하는 것이 중요하다. 만약 프로젝트에서&lt;span style=&quot;color: #456771;&quot;&gt;&lt;b&gt; yarn.lock&lt;/b&gt; &lt;/span&gt;과 &lt;span style=&quot;color: #456771;&quot;&gt;&lt;b&gt;package-lock.json&lt;/b&gt;&lt;/span&gt;이 동시에 존재한다면 프로젝트 개발환경을 정리하고 앞으로의 프로젝트에서는 선택된 패키지 매니저를 사용하여 일관성 있게 개발을 진행하는 것이 미래에 발생할 수 있는 의존성 충돌을 방지할 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;NPM (Node Package Manager)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;NPM(Node Package Manager)&lt;/b&gt;은 자바스크립트 언어를 위한 패키지 관리자로, Node.js의 기본 패키지 관리자이다. Node.js로 만들어진 모듈을 웹에서 받아서 쉽게 설치하고 관리해주는 프로그램으로 개발자 입장에서는 단 몇 줄의 command로 기존 공개된 모듈들을 설치하고 활용할 수 있다. &lt;b&gt;npm은 command CLI&lt;/b&gt; 와 &lt;b&gt;npm registry&lt;/b&gt; 로 구성되어있다. &lt;br /&gt;npm이 없었을 때에는 필요로 하는 기능을 추가하기 위해서 직접 작성하거나 github를 통해 다운로드하여 사용해야 했다. 이러한 불편을 해소하기 위해 나타났으며, Node.js를 설치하기만 하면(기본적으로 npm은 Node.js 내에 내장되어 있습니다) 명령어 한 줄로 기능의 추가가 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Yarn&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Yarn&lt;/b&gt;은 페이스북에서 만든 자바스크립트 패키지 매니저로 리액트(React)와 같은 프로젝트를 진행하며 npm의 단점을 느껴 개발되었다. npm과 같은 기능을 수행하나, npm 레지스트리와 호환하면서 속도나 안정성 측면에서 npm보다 향상되었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Command-line client :&lt;/b&gt; 커맨드 라인 인터페이스(CLI)를 사용하여 서버 또는 서비스와 상호 작용하는 프로그램. NPM의 경우, npm 패키지 매니저의 명령어를 실행하는 도구로 &quot;npm&amp;rdquo;, Yarn의 경우 Yarn 패키지 매니저의 명령어를 실행하는 도구로 &quot;yarn&quot;이라는 명령어를 사용하여 패키지를 설치하거나 관리할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1706447766315&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm init //package.json 생성
npm install //프로젝트의 의존성 패키지 설치
npm install package_name@version //특정 패키지를 지정된 버전으로 설치
npm install url // URL을 통해 패키지를 설치
npm install package_name -g // 특정 패키지 전역 설치
npm uninstall // 프로젝트에서 패키지를 제거
npm update // 패키지를 최신 버전으로 업데이트
npm dedupe // 중복된 패키지 정리&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1706447777097&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;yarn init //package.json 생성
yarn install //프로젝트의 의존성 패키지 설치
yarn add package_name@version //특정 패키지를 지정된 버전으로 설치
yarn add url // URL을 통해 패키지를 설치
yarn global add package_name // 특정 패키지 전역 설치
yarn remove // 프로젝트에서 패키지를 제거
yarn upgrade // 패키지를 최신 버전으로 업데이트
yarn dedupe // 중복된 패키지 정리&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;b&gt;npm Registry :&lt;/b&gt; Node.js 패키지들의 중앙 저장소로, 이곳에서 패키지들을 찾고 다운로드할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.npmjs.com/&quot;&gt;npm 공식 웹사이트&lt;/a&gt;에서 패키지 검색 및 정보 확인 가능.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;npm install&lt;/b&gt; 이나 &lt;b&gt;yarn add&lt;/b&gt; 명령을 통해 패키지를 다운로드할 때, npm registry에서 해당 패키지를 가져옴.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;npm registry에서 패키지 정보를 가져오는 동작은 npm과 Yarn에서 공통적으로 이루어지는 프로세스이다. yarn 또한 npm registry에서 해당 패키지를 찾아 다운로드하고, &lt;b&gt;yarn.lock&lt;/b&gt; 파일을 업데이트하여 패키지의 정확한 버전 및 의존성 정보를 기록한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;NPM vs Yarn : 성능과 보안 비교&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;속도(perfomance) : npm &amp;lt; yarn&lt;br /&gt;&lt;/b&gt;npm은 패키지를 한번에 하나씩 순차적으로 설치한다. 반면에 &lt;b&gt;yarn&lt;/b&gt;은 여러 개의 패키지를 병렬로 처리하여 설치하기 때문에 패키지 처리 속도에서 우세하다. 특히 대규모 프로젝트에서 많은 의존성을 가진 패키지들을 빠르게 설치할 수 있는 장점이 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;보안과 안정성 (Security / Stability) : npm &amp;lt; yarn&lt;br /&gt;&lt;/b&gt;npm은 패키지가 설치될 떄, 자동으로 패키지에 포함된 다른 패키지 코드를 실행할 수 있도록 허용하였다. 이는 편리하지만 동시에 안전성을 위협할 수 있다. 특히나 보장된 정책 없이 등록한 패키지가 존재할 수 있다는 점에서 더욱 위험도가 높다. 반면 &lt;b&gt;yarn&lt;/b&gt;은 패키지를 설치할 때 yarn.lock이나 package.json 내의 파일들만을 설치하는 것을 보장한다. 이로써 외부에서의 코드 실행을 최소화하여 패키지 설치의 보안성을 강화한다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;마무리&lt;br /&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;yarn이 처음에는 npm의 보안 이슈를 보완하고 속도와 안정성 면에서 우수하게 나왔지만, npm도 지속적으로 업데이트를 통해 단점을 보완해왔다. 그 결과, 현재는 npm과 yarn 간의 성능 및 안정성 차이가 크지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 언급한 것처럼 yarn은 병렬적인 패키지 설치로 인한 가벼움과 버전 간 차이로 인한 버그 방지 등의 기능에서 뛰어나다는 특징이 있다. 그러나 npm은 여전히 가장 많은 사용자를 보유하고 있다. 아마도 이는 npm의 편리한 접근성 때문일 것으로 생각된다. &lt;span style=&quot;color: #9d9d9d;&quot;&gt;(yarn의 경우는 brew나 npm을 통해 한번 더 설치를 해줘야하는 불편함이 존재)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 선택은 개발자의 선호도와 프로젝트의 특정 요구사항에 따라 다를 수 있다고 생각한다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;참고&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@kysung95/개발상식-npm과-yarn&quot;&gt;https://velog.io/@kysung95/개발상식-npm과-yarn&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;</description>
      <category>Frontend/Javascript</category>
      <category>npm</category>
      <category>Yarn</category>
      <author>딩둘기</author>
      <guid isPermaLink="true">https://100s.tistory.com/64</guid>
      <comments>https://100s.tistory.com/64#entry64comment</comments>
      <pubDate>Sun, 28 Jan 2024 22:31:37 +0900</pubDate>
    </item>
    <item>
      <title>npm install --force와 npm install --legacy-peer-deps의 차이 이해하기</title>
      <link>https://100s.tistory.com/63</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;i&gt;&lt;b&gt;npm ERR! Fix the upstream dependency conflict, or retry &lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;i&gt;&lt;b&gt;npm ERR! this command with --force, or --legacy-peer-deps&lt;/b&gt;&lt;/i&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;span style=&quot;color: #000000;&quot;&gt;&lt;i&gt;&lt;/i&gt;npm 패키지를 사용할 때 종종 다음과 같은 의존성 충돌에 부딪힐 수 있다. 이러한 충돌을 해결하기 위해 주로 사용되는 두 가지 옵션은 --force와 --legacy-peer-deps 이다. 이 두 플래그 간의 차이와 각각 언제 사용해야 하는지에 대해 알아보려고 한다.&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;npm install --force&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; --force 플래그를 사용하면 peerDependency가 맞지 않더라도 강제로 패키지를 설치한다. 이는 종종 의존성 버전이 호환되지 않을 때 사용된다. 주의가 필요하며, 이는 종속성 충돌을 일으킬 수 있으므로 조심해서 사용해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;npm install --legacy-peer-deps&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; --legacy-peer-deps 플래그를 사용하면 peerDependencies 충돌이 발생해도 패키지 설치를 무시한다. 이 옵션은 자동으로 peerDependencies를 설치하는 기능을 무시하므로, 레거시 프로젝트에서 유용하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 두 옵션을 사용할 때는 주의가 필요하며, 먼저 &lt;b&gt;`--legacy-peer-deps`&lt;/b&gt;로 실행하여 설치가 잘 되는지 확인한 후, 필요한 경우에는 &lt;b&gt;`--force`&lt;/b&gt;로 충돌 부분을 개선해나가는 것이 좋을 듯 하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend/Javascript</category>
      <author>딩둘기</author>
      <guid isPermaLink="true">https://100s.tistory.com/63</guid>
      <comments>https://100s.tistory.com/63#entry63comment</comments>
      <pubDate>Wed, 10 Jan 2024 12:32:36 +0900</pubDate>
    </item>
  </channel>
</rss>