<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>jnheo : medical studio</title>
    <link>https://jnheo.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Thu, 23 Apr 2026 02:35:46 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>JNHEO</managingEditor>
    <image>
      <title>jnheo : medical studio</title>
      <url>https://tistory1.daumcdn.net/tistory/832181/attach/98ec555be9fc41d7b75f5ffc0bba09ac</url>
      <link>https://jnheo.tistory.com</link>
    </image>
    <item>
      <title>Xelento Remote 2nd : 고민이 필요없는 독일 명품</title>
      <link>https://jnheo.tistory.com/42</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;DSC09521.JPG&quot; data-origin-width=&quot;6000&quot; data-origin-height=&quot;4000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cy1nKG/btrXlhER304/iQgug7GJJ1D5QlBg15qca1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cy1nKG/btrXlhER304/iQgug7GJJ1D5QlBg15qca1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cy1nKG/btrXlhER304/iQgug7GJJ1D5QlBg15qca1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcy1nKG%2FbtrXlhER304%2FiQgug7GJJ1D5QlBg15qca1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;6000&quot; height=&quot;4000&quot; data-filename=&quot;DSC09521.JPG&quot; data-origin-width=&quot;6000&quot; data-origin-height=&quot;4000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 이런 오디오 기계에 관심을 갖게 된 것은 20여년전 B&amp;amp;O A8을 선물로 받고 사용하게 되면서 부터였습니다. 골판지 같은 포장에 씌여진 처음보는 비대칭적인 B와O가 그려진 브랜드의 밋밋하기만 했던 A8은 별 생각없이 6개월 쓰고나니 다시는 돌아올 수 없는 강을 건넌것 같이 귀가 변해버렸습니다. 그 뒤로는 어떤 리시버를 사용해도 언제나 무언가 1% 부족한 느낌을 받으며 20년이 넘도록 다양한 기기를 사고 팔며 나름의 여정을 걸어왔습니다. 아마 다들 이렇게 무언가 부족한 느낌으로 항상 조금 더 나은 디바이스와 조합을 찾으며 다양한 즐거움을 경험하시리라 믿습니다.&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;아직도 20대인줄 알았는데 어느새 40이 다 되어가다보니 경제적 여유는 좀 더 생겼으나 젊었을때 만큼의 시간적 여유는 없고 직업에 치이고 가족에 치이며 살게 되었습니다. 가끔 일이 끝나고 유투브에서 좋아하는 오디오 관련 채널들의 동영상을 보다가, 리뷰가 마음에 드는것이 있으면 며칠동안 행복한 고민끝에 와이프 눈치를 보며 구매하고는 합니다. 그러다가 우연히 셀렌토 2세대 출시에 대한 이야기를 듣고 마법같이 끌려서 허락도없이 어느새 구매를 해버렸습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;셀렌토 1세대에 대해서 저는 아주 강렬한 추억이 있습니다. 베이어다이나믹의 테슬라 드라이버의 명성은 워낙 훌륭했지만 긴가민가했으나, 귀에 꽂는 순간 매우 충격적이었습니다. 처음 들어보는 쫄깃하면서도 절제된 사운드는 너무 감동적이었습니다. 당시 개인적인 사정으로 방출해야만 했는데, 아쉬운 마음이 항상 있었습니다. 그런 셀렌토의 2세대가 나왔다고 하면 분명 후회없는 구매가 될 것이라고 확신했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;DSC09530.JPG&quot; data-origin-width=&quot;6000&quot; data-origin-height=&quot;4000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZsT8t/btrXm6IVQ0l/BlSbqKIQ9N9cJGuZJiEqMk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZsT8t/btrXm6IVQ0l/BlSbqKIQ9N9cJGuZJiEqMk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZsT8t/btrXm6IVQ0l/BlSbqKIQ9N9cJGuZJiEqMk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZsT8t%2FbtrXm6IVQ0l%2FBlSbqKIQ9N9cJGuZJiEqMk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;6000&quot; height=&quot;4000&quot; data-filename=&quot;DSC09530.JPG&quot; data-origin-width=&quot;6000&quot; data-origin-height=&quot;4000&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;150만원이 넘는 고가 명품 이어폰인 만큼, 패키징부터 내용물 모두 완벽합니다. 제일 처음 보이는 문구가 Audible piece of jewellery 인것과 일맥상통하게, 이런 명품은 사용자가 경험하는 모든 과정이 명품에 걸맞아야 한다고 생각합니다. 제공되는 케이블 2개와 가죽 케이스, 다양한 사이즈의 이어팁은 당연하다고 느껴지는 고급스러운 패키징입니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;DSC09523.JPG&quot; data-origin-width=&quot;6000&quot; data-origin-height=&quot;4000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBPgp3/btrXmx76l4G/T1SJCAYwkYj3e5RISPe5Q1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBPgp3/btrXmx76l4G/T1SJCAYwkYj3e5RISPe5Q1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBPgp3/btrXmx76l4G/T1SJCAYwkYj3e5RISPe5Q1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBPgp3%2FbtrXmx76l4G%2FT1SJCAYwkYj3e5RISPe5Q1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;6000&quot; height=&quot;4000&quot; data-filename=&quot;DSC09523.JPG&quot; data-origin-width=&quot;6000&quot; data-origin-height=&quot;4000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 이어피스를 보았을때, 동영상으로 보던것보다 훨씬 더 작게 느껴졌습니다. 셀렌토 1세대의 경우 납작해서인지 조금 크기가 있다고는 느껴졌는데 (또는 그사이에 워낙 큰 이어피스를 많이 경험해서인지) 이번에는 매우 작게 느껴졌습니다. 케이싱 자체가 둥글게 처리되어서 그렇게 느껴질 수도 있겠습니다. 소재들이 금을 포함하여 온갖 귀한 소재를 사용했다고 하는데, 자세한건 잘 모르겠지만 촉감은 최상급입니다. 저는 피부가 예민한 편이라서 이런 금속 소재에 민감한데, 100% 만족스러우며 내가 명품을 샀다는 만족감을 확실하게 줍니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;DSC09531.JPG&quot; data-origin-width=&quot;6000&quot; data-origin-height=&quot;4000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xbGVV/btrXl1aPdRX/o2e6qWye2X8EQ4FCbxKka0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xbGVV/btrXl1aPdRX/o2e6qWye2X8EQ4FCbxKka0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xbGVV/btrXl1aPdRX/o2e6qWye2X8EQ4FCbxKka0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxbGVV%2FbtrXl1aPdRX%2Fo2e6qWye2X8EQ4FCbxKka0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;6000&quot; height=&quot;4000&quot; data-filename=&quot;DSC09531.JPG&quot; data-origin-width=&quot;6000&quot; data-origin-height=&quot;4000&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;예전 셀렌토 1세대때도 그랬듯이, 착용감 또한 매우 만족스럽습니다. (처음 꽂을때 짤깍 하는 진동판이 압력에 눌리는 소리는 여전히 납니다) 저는 일할 때 귀가 불편한 것에 매우 예민하여서 B&amp;amp;O EQ를 소유하고 있지만 에어팟프로을 제일 많이 쓰다가 갑자기 음질이 아쉬울때만 EQ를 가끔 꺼내서 씁니다. 하지만 셀렌토는 귀에 꽂아도 전혀 불편감이 없고, 고급스러운 가죽 시트의 독일차의 시트와 같은 느낌이라는 생각이 들었습니다. 꼈을때 부드럽지만 어느정도 위치를 고정해주며 장시간 착용하여도 부담이 없고 워낙 가벼워서 귀가 잘 아프지 않습니다. (실리콘팁)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;DSC09528.JPG&quot; data-origin-width=&quot;6000&quot; data-origin-height=&quot;4000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNV2ey/btrXmCuLkm7/wbixo7UCjm57PTcXZWwNv1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNV2ey/btrXmCuLkm7/wbixo7UCjm57PTcXZWwNv1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNV2ey/btrXmCuLkm7/wbixo7UCjm57PTcXZWwNv1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNV2ey%2FbtrXmCuLkm7%2Fwbixo7UCjm57PTcXZWwNv1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;6000&quot; height=&quot;4000&quot; data-filename=&quot;DSC09528.JPG&quot; data-origin-width=&quot;6000&quot; data-origin-height=&quot;4000&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;가장 중요한 소리를 떨리는 마음으로 들어봅니다. 저는 리시버 외에는 크게 신경쓰지 않는 라이트 유저라서 오래전에 역시나 충동적으로 구매한 SR15를 line out으로 컴퓨터에 연결해서 사용중입니다. 여기에 apple music lossless로 듣고 있습니다. 처음에는 제가 쓰는 현재 기기들 (B&amp;amp;O H95, B&amp;amp;O EQ, Etymotic EVO)에 비해서 매우 강력한 저음에 놀랐습니다. 하지만 부담스러울 정도는 아니라는 느낌과 함께 어느새 자연스럽게 음며들었습니다(?). 그렇게 이런 저런 노래를 들을때 가장 인상적이었던 것은 거슬리는것이 없다는 점이었습니다. 음악감상을 한창 할 때에는 해상도 공간감 정위감 등등 다양한 고민을 했으나 이제는 자세한건 잘 모르겠습니다. 하지만 어느 부분에서도 빠지는 것이 없었습니다. 요즘 제가 리시버 비교할 때 즐겨 듣는 이진아의 &amp;lt;계단&amp;gt;을 들었을때에 고급기기 답게 모든 악기들이 잘 분리가되어서 들리고 빠르게 다양한 악기가 겹쳐도 전혀 간섭없이 확실하게 구분되어 모든 음이 정확하게, 그리고 매우 만족스럽게 잘 들렸습니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;DSC09533.JPG&quot; data-origin-width=&quot;6000&quot; data-origin-height=&quot;4000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bs7Vp6/btrXkYZEda7/ZqfFIdgJXJTVaB3RXwUPaK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bs7Vp6/btrXkYZEda7/ZqfFIdgJXJTVaB3RXwUPaK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bs7Vp6/btrXkYZEda7/ZqfFIdgJXJTVaB3RXwUPaK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbs7Vp6%2FbtrXkYZEda7%2FZqfFIdgJXJTVaB3RXwUPaK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;6000&quot; height=&quot;4000&quot; data-filename=&quot;DSC09533.JPG&quot; data-origin-width=&quot;6000&quot; data-origin-height=&quot;4000&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;lsquo;크게 인상적인 차이는 없는것일까&amp;lsquo; 싶은 생각이 들다가, 다른 리시버들과 비교 청음을 하는 순간 역체감이 엄청나게 느껴졌습니다. 수월우 스타필드 같은 그냥 서랍속에 있던 리시버부터 H95까지 다양하게 비교해보니 테슬라 드라이버가 얼마나 위대한지 새삼 느껴졌습니다. 정말 펀(fun)하게 저음을 잘 울려주면서도 모든 음들이 아주 섬세하고 정밀하게 들렸습니다. 펀(fun)한 리시버들이 과한 양념같은 느낌이 들어서 항상 싫어하는데, 이건 정말 최고급 재료들을 엄선해서 조미료 보다 훨씬 더 맛있는 양념을 만든 느낌이었습니다. 어떻게 이렇게 작은 기기에서 이런 소리를 내지? 라는 생각과 함께 &amp;rsquo;아 이제 정착해도 되겠다&amp;lsquo; 싶었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;DSC09540.JPG&quot; data-origin-width=&quot;6000&quot; data-origin-height=&quot;4000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgIliA/btrXm9FB609/PdnC52ffX9ALbAT8gspl9k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgIliA/btrXm9FB609/PdnC52ffX9ALbAT8gspl9k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgIliA/btrXm9FB609/PdnC52ffX9ALbAT8gspl9k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcgIliA%2FbtrXm9FB609%2FPdnC52ffX9ALbAT8gspl9k%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;6000&quot; height=&quot;4000&quot; data-filename=&quot;DSC09540.JPG&quot; data-origin-width=&quot;6000&quot; data-origin-height=&quot;4000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학생때 밴드부에서 베이스 기타를 치면서 레드 핫 칠리 페퍼스에 한창 빠져있었는데 0db님이 이번에 잘 어울리는 곡으로 레드핫 칠리 페퍼스를 추천해주셔서 매우 반가웠습니다. 추가로 저는 Fun한 리시버인 만큼 (좀 클리쉐인것 같기도하지만) Earth Wind and Fire의 September를 들을때 만족도가 컸습니다. 다른 리시버로 들을때에는 동네 파티에 참석해서 춤을 추는 느낌이었다면, 셀렌토로 들으니 최고급 호텔 바를 통채로 빌려서 샴페인과 함께 춤을 추는 기분입니다 (제 현실과는 매우 동떨어진 이야기이며 실제로 춤은 추지 않았음을 밝힙니다).&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;DSC09544.JPG&quot; data-origin-width=&quot;6000&quot; data-origin-height=&quot;4000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhXJxl/btrXmoKjXuH/YBxWrUfZKAXkD8G5U1EWH1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhXJxl/btrXmoKjXuH/YBxWrUfZKAXkD8G5U1EWH1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhXJxl/btrXmoKjXuH/YBxWrUfZKAXkD8G5U1EWH1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhXJxl%2FbtrXmoKjXuH%2FYBxWrUfZKAXkD8G5U1EWH1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;6000&quot; height=&quot;4000&quot; data-filename=&quot;DSC09544.JPG&quot; data-origin-width=&quot;6000&quot; data-origin-height=&quot;4000&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;이정도 가격대에서 상상되는 용도는, 완전 전문가가 업무를 위해서 사용하는 경우이거나 음악을 사랑하는 경제적 여유가 되시는 분이 순수한 행복을 위해서 사는 두 가지일 것 같습니다. 이 기기는 경제적인 여유가 되시는 분께서 스펙같은거 신경안쓰시고 만족감을 원하시는 경우 구매하시면 맞는 것 아닌가 싶었습니다. 셀렌토 2세대는 명성에 맞게 정말 너무 잘 만들어졌고 flat하지는 않지만 만져지는 촉감부터 크기, 외형, 패키징, 그 어떤 면에서도 빠지지 않습니다. 테슬라 드라이버를 주축으로 하여 온갖 고급스러운 재료들과 최고급 기술들이 적절하게 조합되어 최상의 만족감을 선사합니다. 주의 사항은 역체감이 심하니 &amp;lsquo;나중에 팔아야지&amp;rsquo;라는 생각으로 사셨다가는 평생 돌려받지 못하는 돈이 될 수 있습니다.&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&gt;** &lt;/span&gt;이&lt;span&gt; &lt;/span&gt;글은&lt;span&gt; 0db&lt;/span&gt;샵에서&lt;span&gt; &lt;/span&gt;진행한&lt;span&gt; &lt;/span&gt;셀렌토&lt;span&gt; &lt;/span&gt;구매&lt;span&gt; &lt;/span&gt;리뷰&lt;span&gt; &lt;/span&gt;이벤트를&lt;span&gt; &lt;/span&gt;위해&lt;span&gt; &lt;/span&gt;작성되었습니다&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>잡설</category>
      <category>0db</category>
      <category>beyer dynamic</category>
      <category>xelento</category>
      <category>셀렌토</category>
      <author>JNHEO</author>
      <guid isPermaLink="true">https://jnheo.tistory.com/42</guid>
      <comments>https://jnheo.tistory.com/42#entry42comment</comments>
      <pubDate>Fri, 27 Jan 2023 17:08:46 +0900</pubDate>
    </item>
    <item>
      <title>FrIn : french to inch converter</title>
      <link>https://jnheo.tistory.com/41</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;인터벤션하다보면 french라는 단위와 inch단위를 혼용하는 일이 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-11-13 오후 6.50.31.png&quot; data-origin-width=&quot;1962&quot; data-origin-height=&quot;604&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DQlVE/btrQ5ksRgiZ/MMgNKtc9HkJwtPwNCYYsP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DQlVE/btrQ5ksRgiZ/MMgNKtc9HkJwtPwNCYYsP1/img.png&quot; data-alt=&quot;French와 inch를 혼용하는 경우가 많다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DQlVE/btrQ5ksRgiZ/MMgNKtc9HkJwtPwNCYYsP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDQlVE%2FbtrQ5ksRgiZ%2FMMgNKtc9HkJwtPwNCYYsP1%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;1962&quot; height=&quot;604&quot; data-filename=&quot;스크린샷 2022-11-13 오후 6.50.31.png&quot; data-origin-width=&quot;1962&quot; data-origin-height=&quot;604&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;French와 inch를 혼용하는 경우가 많다&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;이런 french는&amp;nbsp;1/3 mm를 표현하는 단위로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1 F = 1/3mm = 1/30 cm = 1 / (30 * 2.54) inch&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;하지만 이상하게도 converter가 썩 좋은게 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1자리수만 보여줘서 다시 집어넣으면 숫자가 오히려 틀리게 나오는경우까지도..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매번 계산기 켜서 저거 계산하다가 이럴시간에 converter하나 만들겠다 싶어서 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://frin.brainpill.io&quot;&gt;https://frin.brainpill.io&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1668333199633&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;FrIn : French to Inch converter&quot; data-og-description=&quot;&quot; data-og-host=&quot;frin.brainpill.io&quot; data-og-source-url=&quot;https://frin.brainpill.io&quot; data-og-url=&quot;https://frin.brainpill.io&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://frin.brainpill.io&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://frin.brainpill.io&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;FrIn : French to Inch converter&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;frin.brainpill.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-11-13 오후 5.59.21.png&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;1396&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJ6spg/btrQ2eUtdHb/uebeOxg67996w9JEgKI851/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJ6spg/btrQ2eUtdHb/uebeOxg67996w9JEgKI851/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJ6spg/btrQ2eUtdHb/uebeOxg67996w9JEgKI851/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJ6spg%2FbtrQ2eUtdHb%2FuebeOxg67996w9JEgKI851%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;1548&quot; height=&quot;1396&quot; data-filename=&quot;스크린샷 2022-11-13 오후 5.59.21.png&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;1396&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Projects</category>
      <author>JNHEO</author>
      <guid isPermaLink="true">https://jnheo.tistory.com/41</guid>
      <comments>https://jnheo.tistory.com/41#entry41comment</comments>
      <pubDate>Sun, 13 Nov 2022 18:53:41 +0900</pubDate>
    </item>
    <item>
      <title>[메디스태프 강의] ML 모델을 (웹)앱으로 만들기</title>
      <link>https://jnheo.tistory.com/40</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이 강의는 국내 최고 의사 커뮤니티인 &amp;lt;메디스태프 - 젊은 의사커뮤니티&amp;gt;에서 강의를 했던 내용으로, 강의를 들으신 분들이 자세한 내용을 다시 찾아보시는데에 도움이 되시라고 전체 과정을 정리한 내용입니다.&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;들어가기에 앞서 : 왜?!!!!&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 AI관련 의학 논문들이 출판되면서 단순히 'ML을 사용했더니 예측력이 좋았다'라는것의 학문적 의미의 한계를 많은 사람들이 느끼고 있습니다. 이에 따라 &amp;lt;실제 임상에서 사용되는 상황&amp;gt;에 대한 이야기가 포함된 논문들을 요구하는 경우가 많습니다. 또는 적어도 reporting guideline에서 이런 ML모델을 사용할 수 있는 방법을 (또는 자세한 weight가 포함된 model의 description을) 제시하도록 강제하는것이 앞으로 미래일 가능성이 높습니다. 이에 따라 단순히 ML모델을 만들고 결과를 도출한것이 끝이 아니고, 이 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;모델을 어떤 방식으로든 독자가 직접 테스트해볼 수 있도록 하는것이 중요&lt;/b&gt;&lt;/span&gt;해졌습니다. 그리고, 실제로 사용해보면 독자들이 이 기술이 얼마나 정확하고 훌륭한지 더욱 체감하는 효과도 있겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1370&quot; data-origin-height=&quot;806&quot; data-filename=&quot;Screen Shot 2021-10-31 at 2.52.20 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nH9Qu/btrjsrkCAcK/hXsmIXZB9teRrKtl1IayVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nH9Qu/btrjsrkCAcK/hXsmIXZB9teRrKtl1IayVk/img.png&quot; data-alt=&quot;예측 모델의 대표적인 Reporting guideline인 TRIPOD는 AI전용 모델이 개발중이며, 현재 일반 예측모델에서도 위와같이 사용할 수 있는 자세한 방법에 대해 reporting하도록 하고있습니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nH9Qu/btrjsrkCAcK/hXsmIXZB9teRrKtl1IayVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnH9Qu%2FbtrjsrkCAcK%2FhXsmIXZB9teRrKtl1IayVk%2Fimg.png&quot; data-origin-width=&quot;1370&quot; data-origin-height=&quot;806&quot; data-filename=&quot;Screen Shot 2021-10-31 at 2.52.20 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;예측 모델의 대표적인 Reporting guideline인 TRIPOD는 AI전용 모델이 개발중이며, 현재 일반 예측모델에서도 위와같이 사용할 수 있는 자세한 방법에 대해 reporting하도록 하고있습니다.&lt;/figcaption&gt;
&lt;/figure&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;온디바이스 or 서버에서 돌리기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ML 모델을 사용자가 사용할 수 있도록 하는 방법은 실제로 여러가지가 있겠습니다. 모델 자체를 디바이스 안에다가 집어넣어서 (스마트폰 등) 디바이스 안에서 돌아가도록 하는 방법도 있고, 서버에서 돌리는 방법도 있겠습니다. 두 방법이 장단점이 있겠으며 (아래) 이 중 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;서버에서 모델을 돌리는 방법&lt;/b&gt;&lt;/span&gt;을 이 강의에서는 다룹니다. 이전 시간에 김병훈 선생님이 제작한 모델(Brain tumor segmentation model)을 구현하는것을 목표로 하며, 이 모델이 pytorch기반 모델이므로 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;python기반 FastAPI상에서 해당 모델을 구현&lt;/b&gt;&lt;/span&gt;하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1422&quot; data-origin-height=&quot;792&quot; data-filename=&quot;Screen Shot 2021-10-31 at 3.05.59 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7eRPS/btrjudUbzRK/8WKr2ijDGNZjXtaf2YRWM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7eRPS/btrjudUbzRK/8WKr2ijDGNZjXtaf2YRWM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7eRPS/btrjudUbzRK/8WKr2ijDGNZjXtaf2YRWM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7eRPS%2FbtrjudUbzRK%2F8WKr2ijDGNZjXtaf2YRWM1%2Fimg.png&quot; data-origin-width=&quot;1422&quot; data-origin-height=&quot;792&quot; data-filename=&quot;Screen Shot 2021-10-31 at 3.05.59 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1434&quot; data-origin-height=&quot;804&quot; data-filename=&quot;Screen Shot 2021-10-31 at 3.06.11 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XLe7m/btrjl9rYTFS/lKZXheb28uoSK812ZDiYwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XLe7m/btrjl9rYTFS/lKZXheb28uoSK812ZDiYwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XLe7m/btrjl9rYTFS/lKZXheb28uoSK812ZDiYwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXLe7m%2Fbtrjl9rYTFS%2FlKZXheb28uoSK812ZDiYwk%2Fimg.png&quot; data-origin-width=&quot;1434&quot; data-origin-height=&quot;804&quot; data-filename=&quot;Screen Shot 2021-10-31 at 3.06.11 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;클라이언트 고르기 (스마트폰 or 웹?)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 이 모델에 값을 입력하고 결과값을 볼 수 있는 클라이언트 (예. 스마트폰앱 또는 웹사이트)를 정하는 것도 필요합니다. 이 또한 장단점이 명확하며, &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;이 강좌에서는 웹사이트를 만드는것&lt;/b&gt;&lt;/span&gt;을 목표로 합니다. &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;React.js framework를 이용&lt;/b&gt;&lt;/span&gt;할 예정입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2444&quot; data-origin-height=&quot;1374&quot; data-filename=&quot;Screen Shot 2021-10-31 at 3.09.28 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCTYES/btrjmV76NX5/V8xVd5YLO0iYo2eYnIww1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCTYES/btrjmV76NX5/V8xVd5YLO0iYo2eYnIww1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCTYES/btrjmV76NX5/V8xVd5YLO0iYo2eYnIww1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCTYES%2FbtrjmV76NX5%2FV8xVd5YLO0iYo2eYnIww1k%2Fimg.png&quot; data-origin-width=&quot;2444&quot; data-origin-height=&quot;1374&quot; data-filename=&quot;Screen Shot 2021-10-31 at 3.09.28 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;최종 목표&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와같이 웹상에서 nii파일을 업로드하면, 이 파일을 서버로 보내서 결과물이 .gif형태로 아래 표시되는 웹사이트가 최종 구축 목표입니다.&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;kakaotv&quot; data-video-url=&quot;https://tv.kakao.com/v/423528860&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/FWYfQ/hyMa3VM9ZV/YzYz7D8jB72sDtEMCgnBh0/img.jpg?width=2808&amp;amp;height=1552&amp;amp;face=0_0_2808_1552&quot; data-video-width=&quot;860&quot; data-video-height=&quot;475&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;475&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-play-service=&quot;daum_tistory&quot;&gt;&lt;iframe src=&quot;https://play-tv.kakao.com/embed/player/cliplink/423528860?service=daum_tistory&quot; width=&quot;860&quot; height=&quot;475&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption&gt;최종 결과물 영상&lt;/figcaption&gt;
&lt;/figure&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;h3 data-ke-size=&quot;size23&quot;&gt;서버 만들기 (아마존 AWS)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 강의에서는 amazon AWS의 EC2를 이용하여 서버를 구축할예정입니다. t2.large (2vCPU, 8GB ram)인스턴스에 15Gb SSD를 사용하는 인스턴스입니다. (Oregon region에서 한달에 약 $70 정도 과금된다고 합니다)&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;아무 linux based OS나 상관없을것 같아서 Amazon Linux 2를 선택했습니다. (평소에는 ubuntu를 많이 사용합니다)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 3.22.17 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BohWl/btrjr10NKCL/CyQp1k3FHhzF1KRTdjchN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BohWl/btrjr10NKCL/CyQp1k3FHhzF1KRTdjchN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BohWl/btrjr10NKCL/CyQp1k3FHhzF1KRTdjchN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBohWl%2Fbtrjr10NKCL%2FCyQp1k3FHhzF1KRTdjchN0%2Fimg.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 3.22.17 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;말씀드린대로 t2.large이며 바로 review &amp;amp; launch하고 싶지만 SSD 8Gb셋팅은 너무 부족하므로 15기가로 올리기위해 next: configure instance details로 넘어갑니다..&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 3.23.30 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/29kMu/btrjoPzMPJI/Y8lKdqK5HqvwAjZmpNsmV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/29kMu/btrjoPzMPJI/Y8lKdqK5HqvwAjZmpNsmV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/29kMu/btrjoPzMPJI/Y8lKdqK5HqvwAjZmpNsmV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F29kMu%2FbtrjoPzMPJI%2FY8lKdqK5HqvwAjZmpNsmV1%2Fimg.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 3.23.30 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음페이지는 그냥 넘어가고.. 드디어 add storage.. 15Gb SSD로 셋팅합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 3.24.45 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXcaMo/btrjqnCLor0/iPVAcZzXB5NMn2a5AsVUI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXcaMo/btrjqnCLor0/iPVAcZzXB5NMn2a5AsVUI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXcaMo/btrjqnCLor0/iPVAcZzXB5NMn2a5AsVUI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXcaMo%2FbtrjqnCLor0%2FiPVAcZzXB5NMn2a5AsVUI1%2Fimg.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 3.24.45 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후에는 그냥 넘어가다가 ssh key는 원래 있던 key를 사용하시거나 본인의 상황에 맞게 새로운 key를 생성해서 설정하시면됩니다.&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-origin-width=&quot;2784&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 3.25.34 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpJ6U0/btrjr2Fo9l0/ss6xOCtyDwOEK8MQwmtO0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpJ6U0/btrjr2Fo9l0/ss6xOCtyDwOEK8MQwmtO0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpJ6U0/btrjr2Fo9l0/ss6xOCtyDwOEK8MQwmtO0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpJ6U0%2Fbtrjr2Fo9l0%2Fss6xOCtyDwOEK8MQwmtO0k%2Fimg.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 3.25.34 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 EC2 instances menu에서 새로 생성된 인스턴스의&amp;nbsp; &lt;u&gt;&lt;b&gt;IP주소를 확인&lt;/b&gt;&lt;/u&gt;합니다. 참고로 elastic IP를 설정하지 않으시면 이 인스턴스를 껐다 켤때마다 IP주소가바뀌니 이를 유념하셔서 만약 여유가되신다면 elastic IP주소를 할당하시는것을 추천드립니다. (&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.aws.amazon.com/ko_kr/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 3.27.26 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cccObp/btrjoPT7UP1/vkAYoBskg2FNP3syEaXo2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cccObp/btrjoPT7UP1/vkAYoBskg2FNP3syEaXo2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cccObp/btrjoPT7UP1/vkAYoBskg2FNP3syEaXo2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcccObp%2FbtrjoPT7UP1%2FvkAYoBskg2FNP3syEaXo2K%2Fimg.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 3.27.26 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 해당 인스턴스를 클릭해서 들어간 뒤, security 탭에서 security group설정을 들어갑니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 3.30.52 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IN2lL/btrjr2rQ7sj/ilLXmnk8J4yD6POgwPUkr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IN2lL/btrjr2rQ7sj/ilLXmnk8J4yD6POgwPUkr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IN2lL/btrjr2rQ7sj/ilLXmnk8J4yD6POgwPUkr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIN2lL%2Fbtrjr2rQ7sj%2FilLXmnk8J4yD6POgwPUkr1%2Fimg.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 3.30.52 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 edit inbound rule를 선택해주시고&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 3.31.35 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKhIjf/btrjnGJnX4l/nyDSXlHCNamZjktZ36pnE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKhIjf/btrjnGJnX4l/nyDSXlHCNamZjktZ36pnE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKhIjf/btrjnGJnX4l/nyDSXlHCNamZjktZ36pnE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKhIjf%2FbtrjnGJnX4l%2FnyDSXlHCNamZjktZ36pnE0%2Fimg.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 3.31.35 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와같이 22 port, 80 port를 열어주십시요. (제대로 https로 하시려면 443 등 추가 포트를 열어주시면 좋습니다) 참고로 저는 letsencrypt를 애용하고 있습니다. (&lt;a href=&quot;https://letsencrypt.org/ko/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://letsencrypt.org/ko/&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 3.32.37 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5G1Up/btrjr1zMobF/QteKkGYGH9uhLrMsUBQlJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5G1Up/btrjr1zMobF/QteKkGYGH9uhLrMsUBQlJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5G1Up/btrjr1zMobF/QteKkGYGH9uhLrMsUBQlJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5G1Up%2Fbtrjr1zMobF%2FQteKkGYGH9uhLrMsUBQlJK%2Fimg.png&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 3.32.37 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 ssh로 잘 접속이 되는지 확인해봅니다. 맥이라면 터미널프로그램이 자동으로 깔려있고, 윈도우즈이시거나 다른상황에서는 ssh client를 다운받아 설치하시면됩니다. (PuTTY나 iTerm 등) 이후에 아래와같이 ssh를 접속해주시면됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;ssh -i [키_위치.pem] ec2-user@[IP주소]&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;966&quot; data-filename=&quot;Screen Shot 2021-10-31 at 3.36.11 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/J7FzX/btrjmV77Z55/WzJWxyjUz02KaQi9G3AoQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/J7FzX/btrjmV77Z55/WzJWxyjUz02KaQi9G3AoQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/J7FzX/btrjmV77Z55/WzJWxyjUz02KaQi9G3AoQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJ7FzX%2FbtrjmV77Z55%2FWzJWxyjUz02KaQi9G3AoQK%2Fimg.png&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;966&quot; data-filename=&quot;Screen Shot 2021-10-31 at 3.36.11 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 root권한 설정을 해주셔야하는데, 이는 추후에 1024이하 port에서 (이 강의에서는 80port) 서비스를 제공하기 위함입니다.&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;span&gt;&lt;/span&gt;&lt;a href=&quot;https://goddaehee.tistory.com/193&quot;&gt;https://goddaehee.tistory.com/193&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1635662312537&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[AWS] 8.AWS EC2 root 계정 활성화 시키기&quot; data-og-description=&quot;[AWS] 8.AWS&amp;nbsp;EC2&amp;nbsp;root&amp;nbsp;계정&amp;nbsp;활성화&amp;nbsp;시키기 안녕하세요. 갓대희 입니다. 이번 포스팅은&amp;nbsp;[ AWS EC2 (리눅스) root 계정 사용하기&amp;nbsp;]&amp;nbsp;입니다.&amp;nbsp;: ) 이전 포스팅을 통해 EC2 리눅스를 설치 해보았다. 다만 root.&quot; data-og-host=&quot;goddaehee.tistory.com&quot; data-og-source-url=&quot;https://goddaehee.tistory.com/193&quot; data-og-url=&quot;https://goddaehee.tistory.com/193&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/EU9SW/hyL9LJfcfK/Kc7mU1OD8sSRJBRgjyQRDk/img.png?width=509&amp;amp;height=375&amp;amp;face=0_0_509_375,https://scrap.kakaocdn.net/dn/bKsgtp/hyL9z9Uorp/hHzEkJv4mbHi6nQvwBlB3k/img.png?width=509&amp;amp;height=375&amp;amp;face=0_0_509_375,https://scrap.kakaocdn.net/dn/u4HEk/hyL9zPAgWO/paEdpUUgtHISAHkwKxKPJ1/img.png?width=591&amp;amp;height=544&amp;amp;face=0_0_591_544&quot;&gt;&lt;a href=&quot;https://goddaehee.tistory.com/193&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://goddaehee.tistory.com/193&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/EU9SW/hyL9LJfcfK/Kc7mU1OD8sSRJBRgjyQRDk/img.png?width=509&amp;amp;height=375&amp;amp;face=0_0_509_375,https://scrap.kakaocdn.net/dn/bKsgtp/hyL9z9Uorp/hHzEkJv4mbHi6nQvwBlB3k/img.png?width=509&amp;amp;height=375&amp;amp;face=0_0_509_375,https://scrap.kakaocdn.net/dn/u4HEk/hyL9zPAgWO/paEdpUUgtHISAHkwKxKPJ1/img.png?width=591&amp;amp;height=544&amp;amp;face=0_0_591_544');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[AWS] 8.AWS EC2 root 계정 활성화 시키기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;[AWS] 8.AWS&amp;nbsp;EC2&amp;nbsp;root&amp;nbsp;계정&amp;nbsp;활성화&amp;nbsp;시키기 안녕하세요. 갓대희 입니다. 이번 포스팅은&amp;nbsp;[ AWS EC2 (리눅스) root 계정 사용하기&amp;nbsp;]&amp;nbsp;입니다.&amp;nbsp;: ) 이전 포스팅을 통해 EC2 리눅스를 설치 해보았다. 다만 root.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;goddaehee.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 같은 서버에 root로 접속해주시면됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;ssh -i [키_위치.pem] &lt;u&gt;&lt;b&gt;root&lt;/b&gt;&lt;/u&gt;@[IP주소]&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추후에 파일을 쉽게 업로드하고 수정하기위해 sftp client에도 설정을 해줍니다. 저는 Cyberduck을 좋아하기 때문에 이를 통해 설정하였습니다.&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-origin-width=&quot;1044&quot; data-origin-height=&quot;798&quot; data-filename=&quot;Screen Shot 2021-10-31 at 3.49.44 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LEAPQ/btrjlpWpMES/SKw5ei333VgrQ8DLnpfyo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LEAPQ/btrjlpWpMES/SKw5ei333VgrQ8DLnpfyo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LEAPQ/btrjlpWpMES/SKw5ei333VgrQ8DLnpfyo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLEAPQ%2FbtrjlpWpMES%2FSKw5ei333VgrQ8DLnpfyo1%2Fimg.png&quot; data-origin-width=&quot;1044&quot; data-origin-height=&quot;798&quot; data-filename=&quot;Screen Shot 2021-10-31 at 3.49.44 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;필요한 패키지들 설치하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pip를 이용하여 아래의 패키지들을 설치해주시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 하려고했으나 pip조차없으니 이부터 설치해야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Amazon의 공식문서대로 하면되며, eb cli까지는 필요없으니 pip설치하기 섹션의 4번까지 하시면됩니다.(또한 위 셋팅대로 하시면 python3도 기본으로 설치되어있습니다.&amp;nbsp; python3 --version으로 확인가능합니다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/elasticbeanstalk/latest/dg/eb-cli3-install-linux.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.aws.amazon.com/ko_kr/elasticbeanstalk/latest/dg/eb-cli3-install-linux.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1635662701963&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Linux에 Python, pip 및 EB CLI 설치 - AWS Elastic Beanstalk&quot; data-og-description=&quot;Linux에 Python, pip 및 EB CLI 설치 EB CLI에는 Python 2.7, 3.4 또는 그 이상이 필요합니다. 배포가 Python과 함께 제공되지 않았거나 이전 버전과 함께 제공된 경우 pip 및 EB CLI를 설치하기 전에 Python을 설치&quot; data-og-host=&quot;docs.aws.amazon.com&quot; data-og-source-url=&quot;https://docs.aws.amazon.com/ko_kr/elasticbeanstalk/latest/dg/eb-cli3-install-linux.html&quot; data-og-url=&quot;https://docs.aws.amazon.com/ko_kr/elasticbeanstalk/latest/dg/eb-cli3-install-linux.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/elasticbeanstalk/latest/dg/eb-cli3-install-linux.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.aws.amazon.com/ko_kr/elasticbeanstalk/latest/dg/eb-cli3-install-linux.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Linux에 Python, pip 및 EB CLI 설치 - AWS Elastic Beanstalk&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Linux에 Python, pip 및 EB CLI 설치 EB CLI에는 Python 2.7, 3.4 또는 그 이상이 필요합니다. 배포가 Python과 함께 제공되지 않았거나 이전 버전과 함께 제공된 경우 pip 및 EB CLI를 설치하기 전에 Python을 설치&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.aws.amazon.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 필요한 패키지를 pip로 설정하시면됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1635663646580&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pip install torch
pip install torchio
pip install monai
pip install fastapi
pip install uvicorn
pip install python-multipart
pip install aiofiles&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;FastAPI 로 서버 구성하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제부터 본론입니다. Python 기반 백엔드로는 대표적인것이 매우 가볍고 간단한 Flask, Django등이 있습니다. 이 중에서 저는 FastAPI를 사용하였는데, django의 'batteries-included' 방식과 Flask의 가볍고 심플함의 중간이라는 누군가의 썰을듣고 사용하고 있기 때문입니다. 최근 굉장히 popular해지고 있다고 알고 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아주 쉽게 getting started문서가 잘 작성되어있으므로 조금이라도 궁금하시면 잠깐만 들어가보시면 매우 쉽게 배울 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://fastapi.tiangolo.com/ko/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://fastapi.tiangolo.com/ko/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1635663352805&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;FastAPI&quot; data-og-description=&quot;FastAPI FastAPI 프레임워크, 고성능, 간편한 학습, 빠른 코드 작성, 준비된 프로덕션 문서: https://fastapi.tiangolo.com 소스 코드: https://github.com/tiangolo/fastapi FastAPI는 현대적이고, 빠르며(고성능), 파이썬 &quot; data-og-host=&quot;fastapi.tiangolo.com&quot; data-og-source-url=&quot;https://fastapi.tiangolo.com/ko/&quot; data-og-url=&quot;https://fastapi.tiangolo.com/ko/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://fastapi.tiangolo.com/ko/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://fastapi.tiangolo.com/ko/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;FastAPI&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;FastAPI FastAPI 프레임워크, 고성능, 간편한 학습, 빠른 코드 작성, 준비된 프로덕션 문서: https://fastapi.tiangolo.com 소스 코드: https://github.com/tiangolo/fastapi FastAPI는 현대적이고, 빠르며(고성능), 파이썬&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;fastapi.tiangolo.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로 fastapi서버를 시작하려면 아래의 코드를 main.py에 넣고 (위 튜토리얼의 첫 코드입니다)&lt;/p&gt;
&lt;pre id=&quot;code_1635663630170&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from fastapi import FastAPI

app = FastAPI()


@app.get(&quot;/&quot;)
async def root():
    return {&quot;message&quot;: &quot;Hello World&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널에서 아래와같이 입력해주면됩니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1635663694191&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;uvicorn main:app --host 0.0.0.0 --port 80 --reload&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이러고나면 이전에 확인된 우리의 아이피주소로 브라우져에서 입력하면 벌써 서버가 셋팅된것을 확인할 수 있습니다. 또한,&amp;nbsp; /docs 디렉토리에 들어가면 자동으로 openAPI 기반 &lt;b&gt;swagger페이지까지(!!)&lt;/b&gt; 생성되어있는것을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1224&quot; data-origin-height=&quot;974&quot; data-filename=&quot;Screen Shot 2021-10-31 at 4.02.15 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1XSlC/btrjxdTJKNv/dKIhWAXD0huFQKnWeJaL4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1XSlC/btrjxdTJKNv/dKIhWAXD0huFQKnWeJaL4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1XSlC/btrjxdTJKNv/dKIhWAXD0huFQKnWeJaL4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1XSlC%2FbtrjxdTJKNv%2FdKIhWAXD0huFQKnWeJaL4k%2Fimg.png&quot; data-origin-width=&quot;1224&quot; data-origin-height=&quot;974&quot; data-filename=&quot;Screen Shot 2021-10-31 at 4.02.15 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2568&quot; data-origin-height=&quot;1760&quot; data-filename=&quot;Screen Shot 2021-10-31 at 4.03.20 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AXF4i/btrjkDtDm8e/QK501mzGqcuIz0DnD6p1i1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AXF4i/btrjkDtDm8e/QK501mzGqcuIz0DnD6p1i1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AXF4i/btrjkDtDm8e/QK501mzGqcuIz0DnD6p1i1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAXF4i%2FbtrjkDtDm8e%2FQK501mzGqcuIz0DnD6p1i1%2Fimg.png&quot; data-origin-width=&quot;2568&quot; data-origin-height=&quot;1760&quot; data-filename=&quot;Screen Shot 2021-10-31 at 4.03.20 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4개의 파일 입력받기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 /predict라는 URL로 파일 4개를 입력받고 (t1, t2, t1ce, flair) 이 파일을 기반으로 작동하도록 구성해보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://fastapi.tiangolo.com/tutorial/request-files/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://fastapi.tiangolo.com/tutorial/request-files/&lt;/a&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_1635664053575&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from fastapi import FastAPI, File, UploadFile

app = FastAPI()


@app.post(&quot;/predict&quot;)
async def getPrediction(t1_file: UploadFile = File(...),t2_file: UploadFile = File(...),t1ce_file: UploadFile = File(...),flair_file: UploadFile = File(...)):
    return {
        &quot;t1&quot; : t1_file.filename,
        &quot;t2&quot; : t2_file.filename,
        &quot;t1ce&quot; : t1ce_file.filename,
        &quot;flair&quot; : flair_file.filename,
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와같이 t1_file, t2_file.. 이런식으로 4개의 파일을 받겠다고 getPrediction 함수에서 설정했으며 (참고로 이 함수 이름은 아무거나 지어도 전혀 상관없습니다), 우선 잘 작동하는지 보기위해 해당 파일이름 자체를 다시 반환하도록 설정했습니다.&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;http://[IP_ADDR]/docs 에서 잘 작동하는지 테스트해봅니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2568&quot; data-origin-height=&quot;1760&quot; data-filename=&quot;Screen Shot 2021-10-31 at 4.12.15 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccbrsf/btrjlqA13t7/3EtucPJ3sZsvnDJUyu3qwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccbrsf/btrjlqA13t7/3EtucPJ3sZsvnDJUyu3qwK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccbrsf/btrjlqA13t7/3EtucPJ3sZsvnDJUyu3qwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fccbrsf%2FbtrjlqA13t7%2F3EtucPJ3sZsvnDJUyu3qwK%2Fimg.png&quot; data-origin-width=&quot;2568&quot; data-origin-height=&quot;1760&quot; data-filename=&quot;Screen Shot 2021-10-31 at 4.12.15 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 Try it out버튼을 클릭한 뒤 각 파일을 하나씩 설정해주신 뒤 execute 버튼을 누르면 아래와같이 서버가 해당 파일 이름들을 잘 출력해주는것을 확인할 수 있습니다.&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-origin-width=&quot;2568&quot; data-origin-height=&quot;1760&quot; data-filename=&quot;Screen Shot 2021-10-31 at 4.13.05 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p1bsr/btrjud7LEkx/tXk0aAu8KuozzkeZqceegk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p1bsr/btrjud7LEkx/tXk0aAu8KuozzkeZqceegk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p1bsr/btrjud7LEkx/tXk0aAu8KuozzkeZqceegk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp1bsr%2Fbtrjud7LEkx%2FtXk0aAu8KuozzkeZqceegk%2Fimg.png&quot; data-origin-width=&quot;2568&quot; data-origin-height=&quot;1760&quot; data-filename=&quot;Screen Shot 2021-10-31 at 4.13.05 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 여기까지 잠시 정리하고 넘어가겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 우리는 서버를 아마존에서 설정(구입)하고 거기에 fastapi라는 서버 프로그램을 깔았으며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 서버로 하여금 4개의 파일을 입력받도록 만들었고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 입력받은 파일의 이름을 요청한 사람에게 반환하도록 설정했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반환한 형태는 JSON형태로, JSON에 대해서 모르신다면 간단히 찾아보시기를 추천드립니다. (매우 쉽습니다 ^^)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4개의 파일 임시 경로에 저장하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;업로드된 파일은 가상의 공간에 존재하며, 이것을 디스크의 특정 위치로 옮긴뒤 작업하기를 강력히 권장하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 임시디렉토리를 서버상에 아무데나 만들고, 여기다가 해당 파일들을 저장하는 작업을 하겠습니다.&lt;/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;아래와같이 서버상에 저장할 특정 폴더를 만들어주시고 시작하시면됩니다 (저는 /home/ec2-user/tmp 로 설정했습니다)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1572&quot; data-origin-height=&quot;1152&quot; data-filename=&quot;Screen Shot 2021-10-31 at 4.18.12 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/W5PdL/btrjnGJpFmK/kCgZxvoSHSxT8xvSKtaPyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/W5PdL/btrjnGJpFmK/kCgZxvoSHSxT8xvSKtaPyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/W5PdL/btrjnGJpFmK/kCgZxvoSHSxT8xvSKtaPyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FW5PdL%2FbtrjnGJpFmK%2FkCgZxvoSHSxT8xvSKtaPyK%2Fimg.png&quot; data-origin-width=&quot;1572&quot; data-origin-height=&quot;1152&quot; data-filename=&quot;Screen Shot 2021-10-31 at 4.18.12 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 main.py를 아래와같이 작성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1635665364041&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from fastapi import FastAPI, File, UploadFile
import aiofiles
import time


app = FastAPI()

TMP_DIR = &quot;/home/ec2-user/tmp&quot;


async def saveFiles(file) :
    TIME = time.time() ### set current time as unique name for all file
    out_file_path =f&quot;{TMP_DIR}/{TIME}-{file.filename}&quot; 
    async with aiofiles.open(out_file_path, 'wb') as out_file:
        content = await file.read()  # async read
        await out_file.write(content)  # async write
    return out_file_path
    

@app.post(&quot;/predict&quot;)
async def getPrediction(t1_file: UploadFile = File(...),t2_file: UploadFile = File(...),t1ce_file: UploadFile = File(...),flair_file: UploadFile = File(...)):
    tmp_file = {}
    tmp_file[&quot;t1&quot;] = await saveFiles(t1_file)
    tmp_file[&quot;t2&quot;] = await saveFiles(t2_file)
    tmp_file[&quot;t1ce&quot;] = await saveFiles(t1ce_file)
    tmp_file[&quot;flair&quot;] = await saveFiles(flair_file)

    return tmp_file&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;/docs에서 또다시 테스트를 해보고나면 sftp client에서 확인해보면 아래와같이 파일이 잘 생성된것을 확인할 수 있습니다.&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-origin-width=&quot;1572&quot; data-origin-height=&quot;1152&quot; data-filename=&quot;Screen Shot 2021-10-31 at 4.31.27 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HDsPR/btrjlPghgcN/QmPkkknKh4meEa37byHKy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HDsPR/btrjlPghgcN/QmPkkknKh4meEa37byHKy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HDsPR/btrjlPghgcN/QmPkkknKh4meEa37byHKy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHDsPR%2FbtrjlPghgcN%2FQmPkkknKh4meEa37byHKy0%2Fimg.png&quot; data-origin-width=&quot;1572&quot; data-origin-height=&quot;1152&quot; data-filename=&quot;Screen Shot 2021-10-31 at 4.31.27 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;PyTorch로 모델 결과 구하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 업로드된 파일로 김병훈 선생님의 코드를 훔쳐와서 적용하여, segmentation image를 도출합니다.&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;시작하기 전에 적절히 모델 pth파일도 서버 경로상에 올려놔야겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 /home/ec2-user/server/model.pth에 올려놨습니다.&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;그리고 segmentation된 결과물이 저장되는 (.gif파일) 위치도 미리 지정해놓고, 이 위치에 폴더도 미리 만들어놔야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 /home/ec2-user/server/static/output 으로 해놨습니다.&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;아래와 같이 main.py를 수정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1635666201233&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from fastapi import FastAPI, File, UploadFile
import aiofiles
import time

import os
import torch
import torchio as tio
import monai

app = FastAPI()

TMP_DIR = &quot;/home/ec2-user/tmp&quot;


MODEL_PATH = '/home/ec2-user/server/model.pth'
SAVE_DIR = '/home/ec2-user/server/static/output'

async def saveFiles(file) :
    TIME = time.time() 
    out_file_path =f&quot;{TMP_DIR}/{TIME}-{file.filename}&quot; 
    async with aiofiles.open(out_file_path, 'wb') as out_file:
        content = await file.read()  # async read
        await out_file.write(content)  # async write
    return out_file_path
    

def get_segmentation(model, data, device):
    model.to(device)
    input = torch.cat([data[sequence]['data'].unsqueeze(0) for sequence in ['t1', 't2', 't1ce', 'flair']], dim=1).to(device) 
    output = model(input).cpu().detach()
    pred = torch.nn.functional.one_hot(output.argmax(dim=1).squeeze(0)).permute(3,0,1,2) 
    return tio.LabelMap(tensor=pred)

@app.post(&quot;/predict&quot;)
async def getPrediction(t1_file: UploadFile = File(...),t2_file: UploadFile = File(...),t1ce_file: UploadFile = File(...),flair_file: UploadFile = File(...)):
    tmp_file = {}
    tmp_file[&quot;t1&quot;] = await saveFiles(t1_file)
    tmp_file[&quot;t2&quot;] = await saveFiles(t2_file)
    tmp_file[&quot;t1ce&quot;] = await saveFiles(t1ce_file)
    tmp_file[&quot;flair&quot;] = await saveFiles(flair_file)

    tio_images = {}
    for sequence in tmp_file :
        tio_images[sequence] =tio.ScalarImage(tmp_file[sequence]) 

    model = monai.networks.nets.BasicUNet(spatial_dims=3, in_channels=4, out_channels=2)
    model.load_state_dict(torch.load(MODEL_PATH, map_location=torch.device('cpu')))
    subject = tio.Subject(tio_images)
    transforms = [
        tio.ToCanonical(),
        tio.Resample(3),
        tio.CropOrPad((64,64,48)),
        tio.RescaleIntensity(out_min_max=(0, 1)),
    ]
    transform = tio.Compose(transforms)
    dataset = tio.SubjectsDataset([subject], transform=transform)

    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    seg = get_segmentation(model, dataset[0], device)

    TIME = time.time()
    segmented_gif = f&quot;{SAVE_DIR}/{TIME}-segmented.gif&quot;
    seg.to_gif(axis=2, duration=10, output_path=segmented_gif, loop=0)

    return {
        &quot;success&quot; : True,
        &quot;segmented&quot; : segmented_gif
    }&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2568&quot; data-origin-height=&quot;1760&quot; data-filename=&quot;Screen Shot 2021-10-31 at 4.42.54 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PpfTi/btrjlyyzSDM/eQdFKmi9KNVABj2nb0cADK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PpfTi/btrjlyyzSDM/eQdFKmi9KNVABj2nb0cADK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PpfTi/btrjlyyzSDM/eQdFKmi9KNVABj2nb0cADK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPpfTi%2FbtrjlyyzSDM%2FeQdFKmi9KNVABj2nb0cADK%2Fimg.png&quot; data-origin-width=&quot;2568&quot; data-origin-height=&quot;1760&quot; data-filename=&quot;Screen Shot 2021-10-31 at 4.42.54 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sftp로 파일이 잘 생성되었는지 확인해봅니다 :&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1572&quot; data-origin-height=&quot;1152&quot; data-filename=&quot;Screen Shot 2021-10-31 at 4.44.27 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d67lY8/btrjr1zPQxM/8LFONDrAKjEAjdnJbDtP51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d67lY8/btrjr1zPQxM/8LFONDrAKjEAjdnJbDtP51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d67lY8/btrjr1zPQxM/8LFONDrAKjEAjdnJbDtP51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd67lY8%2Fbtrjr1zPQxM%2F8LFONDrAKjEAjdnJbDtP51%2Fimg.png&quot; data-origin-width=&quot;1572&quot; data-origin-height=&quot;1152&quot; data-filename=&quot;Screen Shot 2021-10-31 at 4.44.27 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;1230&quot; data-filename=&quot;Screen Shot 2021-10-31 at 4.44.44 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0AinN/btrjlVtxBDp/18b9uh42SNTb2VsEGj21p0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0AinN/btrjlVtxBDp/18b9uh42SNTb2VsEGj21p0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0AinN/btrjlVtxBDp/18b9uh42SNTb2VsEGj21p0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0AinN%2FbtrjlVtxBDp%2F18b9uh42SNTb2VsEGj21p0%2Fimg.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;1230&quot; data-filename=&quot;Screen Shot 2021-10-31 at 4.44.44 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확히 모르지만 어쨌든 잘 생성된것같습니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 모든 이미지 sequence에 대하여&amp;nbsp; gif를 만들어서 output directory에 잘 넣어주면 pytorch로 할것은 모두 마무리된것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비슷하게 아래와같이 수정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1635666701663&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from fastapi import FastAPI, File, UploadFile
import aiofiles
import time

import os
import torch
import torchio as tio
import monai

app = FastAPI()

TMP_DIR = &quot;/home/ec2-user/tmp&quot;


MODEL_PATH = '/home/ec2-user/server/model.pth'
SAVE_DIR = '/home/ec2-user/server/static/output'

async def saveFiles(file) :
    TIME = time.time() 
    out_file_path =f&quot;{TMP_DIR}/{TIME}-{file.filename}&quot; 
    async with aiofiles.open(out_file_path, 'wb') as out_file:
        content = await file.read()  # async read
        await out_file.write(content)  # async write
    return out_file_path
    

def get_segmentation(model, data, device):
    model.to(device)
    input = torch.cat([data[sequence]['data'].unsqueeze(0) for sequence in ['t1', 't2', 't1ce', 'flair']], dim=1).to(device) 
    output = model(input).cpu().detach()
    pred = torch.nn.functional.one_hot(output.argmax(dim=1).squeeze(0)).permute(3,0,1,2) 
    return tio.LabelMap(tensor=pred)

@app.post(&quot;/predict&quot;)
async def getPrediction(t1_file: UploadFile = File(...),t2_file: UploadFile = File(...),t1ce_file: UploadFile = File(...),flair_file: UploadFile = File(...)):
    tmp_file = {}
    tmp_file[&quot;t1&quot;] = await saveFiles(t1_file)
    tmp_file[&quot;t2&quot;] = await saveFiles(t2_file)
    tmp_file[&quot;t1ce&quot;] = await saveFiles(t1ce_file)
    tmp_file[&quot;flair&quot;] = await saveFiles(flair_file)

    tio_images = {}
    for sequence in tmp_file :
        tio_images[sequence] =tio.ScalarImage(tmp_file[sequence]) 

    model = monai.networks.nets.BasicUNet(spatial_dims=3, in_channels=4, out_channels=2)
    model.load_state_dict(torch.load(MODEL_PATH, map_location=torch.device('cpu')))
    subject = tio.Subject(tio_images)
    transforms = [
        tio.ToCanonical(),
        tio.Resample(3),
        tio.CropOrPad((64,64,48)),
        tio.RescaleIntensity(out_min_max=(0, 1)),
    ]
    transform = tio.Compose(transforms)
    dataset = tio.SubjectsDataset([subject], transform=transform)

    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    seg = get_segmentation(model, dataset[0], device)


    TIME = time.time() 

    seg.to_gif(axis=2, duration=10, output_path=f&quot;{SAVE_DIR}/{TIME}-segmented.gif&quot;, loop=0)
    paths['segmented'] = f&quot;output/{TIME}-segmented.gif&quot; 
    for sequence in dataset[0] :
        output_path = f&quot;{SAVE_DIR}/{TIME}-{sequence}.gif&quot; 
        dataset[0][sequence].to_gif(axis=2, duration=10, output_path=output_path, loop=0)
        paths[sequence] = f&quot;output/{TIME}-{sequence}.gif&quot;
    
    return {
        &quot;success&quot; : True
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;정리하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 client(웹페이지)에게 전달할 내용을 고민해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서 그랬던것 처럼 파일의 절대 경로를 알려주는것 (/home/ec2-user/...) 은 아무 도움이 되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 gif파일에 대한 인터넷상에서 접속가능한 URL을 줘야할텐데, 이건 static file serving이 필요한 부분입니다.&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;static file serving은 fastapi의 아래 문서에 잘 나와있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://fastapi.tiangolo.com/tutorial/static-files/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://fastapi.tiangolo.com/tutorial/static-files/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1635666937240&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Static Files - FastAPI&quot; data-og-description=&quot;Static Files You can serve static files automatically from a directory using StaticFiles. Use StaticFiles Import StaticFiles. &amp;quot;Mount&amp;quot; a StaticFiles() instance in a specific path. from fastapi import FastAPI from fastapi.staticfiles import StaticFiles app =&quot; data-og-host=&quot;fastapi.tiangolo.com&quot; data-og-source-url=&quot;https://fastapi.tiangolo.com/tutorial/static-files/&quot; data-og-url=&quot;https://fastapi.tiangolo.com/tutorial/static-files/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://fastapi.tiangolo.com/tutorial/static-files/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://fastapi.tiangolo.com/tutorial/static-files/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Static Files - FastAPI&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Static Files You can serve static files automatically from a directory using StaticFiles. Use StaticFiles Import StaticFiles. &quot;Mount&quot; a StaticFiles() instance in a specific path. from fastapi import FastAPI from fastapi.staticfiles import StaticFiles app =&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;fastapi.tiangolo.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 따라 아래 import를 추가해주고&lt;/p&gt;
&lt;pre id=&quot;code_1635666954700&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from fastapi.staticfiles import StaticFiles&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;아래의 기존 predict 를 정의해준 것이 모두 끝나고 나서 맨 뒤에다가 아래 항목을 작성해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1635667007089&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
...

@app.post(&quot;/predict&quot;)
async def getPrediction(t1_file: UploadFile = File(...),t2_file: UploadFile = File(...),t1ce_file: UploadFile = File(...),flair_file: UploadFile = File(...)):

...
...



app.mount(&quot;/&quot;, StaticFiles(directory=&quot;static&quot;, html=True), name=&quot;static&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;app.mount안의 코드를 잘 보면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째 &quot;/&quot;은 외부에서 접속할 주소를 나타내며 (결국 우리의 경우 http://[IP_ADDRESS]/ 이렇게 그냥 가장 root 주소로 접속하는 경우를 뜻함)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;directory=&quot;static&quot;은 실제 경로상에서 main.py기준으로 어느 폴더를 보여주기를 희망하는지를 물어보며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(저는 main.py가 /home/ec2-user/server/main.py이므로 /home/ec2-user/server/static/ 폴더를 지칭하게 됩니다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;html=True option으로 html파일을 서빙하는 목적이라고 명시해주며 (그렇게 하면 index.html이렇게 써주지 않아도 기본적으로 index.html을 보여줍니다... 잘 모르겠으면 걍 써주세요)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;name은 내부적으로 사용할 이름입니다.&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;이제 https://[IP_ADDRESS]/ 로 접속해보면 아래와 같이 보입니다.&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-origin-width=&quot;1224&quot; data-origin-height=&quot;974&quot; data-filename=&quot;Screen Shot 2021-10-31 at 4.59.44 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rIwi4/btrjlpIYKdy/Qnf6kpQoYIj2M2YvsWHZk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rIwi4/btrjlpIYKdy/Qnf6kpQoYIj2M2YvsWHZk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rIwi4/btrjlpIYKdy/Qnf6kpQoYIj2M2YvsWHZk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrIwi4%2FbtrjlpIYKdy%2FQnf6kpQoYIj2M2YvsWHZk1%2Fimg.png&quot; data-origin-width=&quot;1224&quot; data-origin-height=&quot;974&quot; data-filename=&quot;Screen Shot 2021-10-31 at 4.59.44 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐면 해당 폴더(/home/ec2-user/server/static/)에 아무것도 없기 때문입니다. 그래서 여기에 index.html을 샘플로 만들어서 잘 작동하는지 보겠습니다.&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;/home/ec2/user/server/static/index.html 에다가 아래 파일을 넣어줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1635667240637&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;b&amp;gt;hello&amp;lt;/b&amp;gt; world!&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1224&quot; data-origin-height=&quot;974&quot; data-filename=&quot;Screen Shot 2021-10-31 at 5.01.09 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/coqGAF/btrjlpvqJA5/FIGAo4jus3l0anEz4CgqL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/coqGAF/btrjlpvqJA5/FIGAo4jus3l0anEz4CgqL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/coqGAF/btrjlpvqJA5/FIGAo4jus3l0anEz4CgqL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcoqGAF%2FbtrjlpvqJA5%2FFIGAo4jus3l0anEz4CgqL0%2Fimg.png&quot; data-origin-width=&quot;1224&quot; data-origin-height=&quot;974&quot; data-filename=&quot;Screen Shot 2021-10-31 at 5.01.09 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 잘 작동하는것을 볼 수 있습니다. 그러면 우리가 실제로 원하는 gif파일은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;/home/ec2-user/server/static/output폴더에 있는데, 여기있는것은 잘 보일까요?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1224&quot; data-origin-height=&quot;974&quot; data-filename=&quot;Screen Shot 2021-10-31 at 5.02.18 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgnIxO/btrjqqNnkUQ/RzSYgUwKnjqBbZoFDKHVVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgnIxO/btrjqqNnkUQ/RzSYgUwKnjqBbZoFDKHVVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgnIxO/btrjqqNnkUQ/RzSYgUwKnjqBbZoFDKHVVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgnIxO%2FbtrjqqNnkUQ%2FRzSYgUwKnjqBbZoFDKHVVk%2Fimg.png&quot; data-origin-width=&quot;1224&quot; data-origin-height=&quot;974&quot; data-filename=&quot;Screen Shot 2021-10-31 at 5.02.18 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;넵! 위와같이 잘 보입니다.&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;제 생각에는 &quot;/output/1635666652.807742-flair.gif&quot; 이렇게 http://[IP_ADDRESS]/ 이후의 경로만 잘 전달해주면 될것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 따라 아래와같이 출력되도록 구성해보도록 하겠습니다 :&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1635667559944&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
	&quot;success&quot; : True,
    &quot;paths&quot; : {
    	&quot;t1&quot; : &quot;output/1635666652.807742-t1.gif&quot;,
        &quot;t2&quot; : &quot;output/1635666652.807742-t2.gif&quot;,
        &quot;t1ce&quot; : &quot;output/1635666652.807742-t1ce.gif&quot;,
        &quot;flair&quot; : &quot;output/1635666652.807742-flair.gif&quot;,
        &quot;segmented&quot; : &quot;output/1635666652.807742-segmented.gif&quot;,
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;main.py를 아래와같이 수정해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1635680040542&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from fastapi import FastAPI, File, UploadFile
import aiofiles
import time

import os
import torch
import torchio as tio
import monai

from fastapi.staticfiles import StaticFiles

app = FastAPI()



TMP_DIR = &quot;/home/ec2-user/tmp&quot;

MODEL_PATH = '/home/ec2-user/server/model.pth'
SAVE_DIR = '/home/ec2-user/server/static/output'

async def saveFiles(file) :
    TIME = time.time() 
    out_file_path =f&quot;{TMP_DIR}/{TIME}-{file.filename}&quot; 
    async with aiofiles.open(out_file_path, 'wb') as out_file:
        content = await file.read()  # async read
        await out_file.write(content)  # async write
    return out_file_path
    

def get_segmentation(model, data, device):
    model.eval()
    model.to(device)
    input = torch.cat([data[sequence]['data'].unsqueeze(0) for sequence in ['t1', 't2', 't1ce', 'flair']], dim=1).to(device) 
    output = model(input).cpu().detach()
    pred = torch.nn.functional.one_hot(output.argmax(dim=1).squeeze(0)).permute(3,0,1,2) 
    return tio.LabelMap(tensor=pred)

@app.post(&quot;/predict&quot;)
async def getPrediction(t1_file: UploadFile = File(...),t2_file: UploadFile = File(...),t1ce_file: UploadFile = File(...),flair_file: UploadFile = File(...)):
    tmp_file = {}
    tmp_file[&quot;t1&quot;] = await saveFiles(t1_file)
    tmp_file[&quot;t2&quot;] = await saveFiles(t2_file)
    tmp_file[&quot;t1ce&quot;] = await saveFiles(t1ce_file)
    tmp_file[&quot;flair&quot;] = await saveFiles(flair_file)

    tio_images = {}
    for sequence in tmp_file :
        tio_images[sequence] =tio.ScalarImage(tmp_file[sequence]) 

    model = monai.networks.nets.BasicUNet(spatial_dims=3, in_channels=4, out_channels=2)
    model.load_state_dict(torch.load(MODEL_PATH, map_location=torch.device('cpu')))
    subject = tio.Subject(tio_images)
    transforms = [
        tio.ToCanonical(),
        tio.Resample(3),
        tio.CropOrPad((64,64,48)),
        tio.RescaleIntensity(out_min_max=(0, 1)),
    ]
    transform = tio.Compose(transforms)
    dataset = tio.SubjectsDataset([subject], transform=transform)

    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    seg = get_segmentation(model, dataset[0], device)

    paths = {}

    TIME = time.time() 

    seg.to_gif(axis=2, duration=10, output_path=f&quot;{SAVE_DIR}/{TIME}-segmented.gif&quot;, loop=0)
    paths['segmented'] = f&quot;output/{TIME}-segmented.gif&quot; 
    for sequence in dataset[0] :
        output_path = f&quot;{SAVE_DIR}/{TIME}-{sequence}.gif&quot; 
        dataset[0][sequence].to_gif(axis=2, duration=10, output_path=output_path, loop=0)
        paths[sequence] = f&quot;output/{TIME}-{sequence}.gif&quot;
    

    return {
        &quot;success&quot; : True,
        &quot;paths&quot; : paths
    }


app.mount(&quot;/&quot;, StaticFiles(directory=&quot;static&quot;, html=True), name=&quot;static&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swagger에서 예상한대로 잘 출력이됩니다.&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-origin-width=&quot;2568&quot; data-origin-height=&quot;1760&quot; data-filename=&quot;Screen Shot 2021-10-31 at 5.07.35 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2xD0j/btrjvRDqiDV/ls23SrRDlMeX5Xub2LagMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2xD0j/btrjvRDqiDV/ls23SrRDlMeX5Xub2LagMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2xD0j/btrjvRDqiDV/ls23SrRDlMeX5Xub2LagMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2xD0j%2FbtrjvRDqiDV%2Fls23SrRDlMeX5Xub2LagMk%2Fimg.png&quot; data-origin-width=&quot;2568&quot; data-origin-height=&quot;1760&quot; data-filename=&quot;Screen Shot 2021-10-31 at 5.07.35 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;파일 처리하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 끝내고 싶지만, 생각해보면 사용자들이 업로드하는 파일들이 그대로 다 서버에 쌓이므로, 15기가가 금방 다 찰것으로 기대할 수 있습니다. 이에 따라 결과물은 바로 삭제하면 client에서 확인하기도 전에 삭제가 될 것이므로 조금 문제가 되겠지만, 용량이 큰 원본파일은 프로세스가 끝나고 나면 불필요하므로 삭제하는것이 좋을것 같습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 따라 마지막에 return하기 직전에 원본 파일을 아래와같이 지웁니다 :&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1635668231965&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
...

    for sequence in tmp_file :
        os.remove(tmp_file[sequence])

    return {
        &quot;success&quot; : True,
        &quot;paths&quot; : paths
    }


app.mount(&quot;/&quot;, StaticFiles(directory=&quot;static&quot;, html=True), name=&quot;static&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 파일을 지우고나면 이제는 tmp폴더에 더이상 파일이 쌓이지 않습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1572&quot; data-origin-height=&quot;1152&quot; data-filename=&quot;Screen Shot 2021-10-31 at 5.18.40 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3nFmP/btrjvSCmMvb/auO14gzaaQEgsTfkHEecrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3nFmP/btrjvSCmMvb/auO14gzaaQEgsTfkHEecrk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3nFmP/btrjvSCmMvb/auO14gzaaQEgsTfkHEecrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3nFmP%2FbtrjvSCmMvb%2FauO14gzaaQEgsTfkHEecrk%2Fimg.png&quot; data-origin-width=&quot;1572&quot; data-origin-height=&quot;1152&quot; data-filename=&quot;Screen Shot 2021-10-31 at 5.18.40 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;결과물 (.gif)파일 지우는것에 대하여&lt;/b&gt;&lt;br /&gt;파일을 바로 지우면 client에서 해당 이미지를 보기도 전에 지워져버리기 때문에 어떤 delay를 주고 지우는것이 좋겠습니다. 이에 대해서는 cron.daily등의 linux상에서 매일 특정시간에 실행되는 bash script를 통해 실현이 가능합니다.&lt;br /&gt;아래의 링크 또는 검색을 통해 (cron delete files after 1 day...) 추가로 공부해보실 수 있습니다.&lt;br /&gt;https://arstech.net/cron-job-for-linux-to-delete-files-older-than-x-days/&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;정말 마지막, CORS설정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CORS 설정이라는것이 필요합니다. 보안관련된 문제인데, API를 아무데서나 외부에서 막 리퀘스트하면 보안상 여러가지 문제가 발생한다는 점에서, 정해진 위치에서만 해당 서버에 api콜을 할 수 있다는 개념입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 위치라고 하면 실제 사용하는 사람의 위치가 아니라, 이 서버에 요청을 보내는 client 앱의 위치라는 뜻으로,&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;아래에서 프론트 개발중에는 localhost에서 돌아갈 예정이기때문에 이 내용을 추가해줘야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 공식 가이드라인에 따라 cors middleware를 설치해주신다음에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;main.py에는 아래와같이 추가해주시면됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://fastapi.tiangolo.com/tutorial/cors/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://fastapi.tiangolo.com/tutorial/cors/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1635674253703&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;CORS (Cross-Origin Resource Sharing) - FastAPI&quot; data-og-description=&quot;CORS (Cross-Origin Resource Sharing) CORS or &amp;quot;Cross-Origin Resource Sharing&amp;quot; refers to the situations when a frontend running in a browser has JavaScript code that communicates with a backend, and the backend is in a different &amp;quot;origin&amp;quot; than the frontend. O&quot; data-og-host=&quot;fastapi.tiangolo.com&quot; data-og-source-url=&quot;https://fastapi.tiangolo.com/tutorial/cors/&quot; data-og-url=&quot;https://fastapi.tiangolo.com/tutorial/cors/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://fastapi.tiangolo.com/tutorial/cors/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://fastapi.tiangolo.com/tutorial/cors/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;CORS (Cross-Origin Resource Sharing) - FastAPI&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;CORS (Cross-Origin Resource Sharing) CORS or &quot;Cross-Origin Resource Sharing&quot; refers to the situations when a frontend running in a browser has JavaScript code that communicates with a backend, and the backend is in a different &quot;origin&quot; than the frontend. O&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;fastapi.tiangolo.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 백엔드는 모두 완성되었습니다.............&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1635680101893&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from fastapi import FastAPI, File, UploadFile
import aiofiles
import time

import os
import torch
import torchio as tio
import monai

from fastapi.staticfiles import StaticFiles
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

origins = [
    &quot;http://localhost:3000&quot;,
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=[&quot;*&quot;],
    allow_headers=[&quot;*&quot;],
)



TMP_DIR = &quot;/home/ec2-user/tmp&quot;

MODEL_PATH = '/home/ec2-user/server/model.pth'
SAVE_DIR = '/home/ec2-user/server/static/output'

async def saveFiles(file) :
    TIME = time.time() 
    out_file_path =f&quot;{TMP_DIR}/{TIME}-{file.filename}&quot; 
    async with aiofiles.open(out_file_path, 'wb') as out_file:
        content = await file.read()  # async read
        await out_file.write(content)  # async write
    return out_file_path
    

def get_segmentation(model, data, device):
    model.eval()
    model.to(device)
    input = torch.cat([data[sequence]['data'].unsqueeze(0) for sequence in ['t1', 't2', 't1ce', 'flair']], dim=1).to(device) 
    output = model(input).cpu().detach()
    pred = torch.nn.functional.one_hot(output.argmax(dim=1).squeeze(0)).permute(3,0,1,2) 
    return tio.LabelMap(tensor=pred)

@app.post(&quot;/predict&quot;)
async def getPrediction(t1_file: UploadFile = File(...),t2_file: UploadFile = File(...),t1ce_file: UploadFile = File(...),flair_file: UploadFile = File(...)):
    tmp_file = {}
    tmp_file[&quot;t1&quot;] = await saveFiles(t1_file)
    tmp_file[&quot;t2&quot;] = await saveFiles(t2_file)
    tmp_file[&quot;t1ce&quot;] = await saveFiles(t1ce_file)
    tmp_file[&quot;flair&quot;] = await saveFiles(flair_file)

    tio_images = {}
    for sequence in tmp_file :
        tio_images[sequence] =tio.ScalarImage(tmp_file[sequence]) 

    model = monai.networks.nets.BasicUNet(spatial_dims=3, in_channels=4, out_channels=2)
    model.load_state_dict(torch.load(MODEL_PATH, map_location=torch.device('cpu')))
    subject = tio.Subject(tio_images)
    transforms = [
        tio.ToCanonical(),
        tio.Resample(3),
        tio.CropOrPad((64,64,48)),
        tio.RescaleIntensity(out_min_max=(0, 1)),
    ]
    transform = tio.Compose(transforms)
    dataset = tio.SubjectsDataset([subject], transform=transform)

    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    seg = get_segmentation(model, dataset[0], device)

    paths = {}

    TIME = time.time() 

    seg.to_gif(axis=2, duration=10, output_path=f&quot;{SAVE_DIR}/{TIME}-segmented.gif&quot;, loop=0)
    paths['segmented'] = f&quot;output/{TIME}-segmented.gif&quot; 
    for sequence in dataset[0] :
        output_path = f&quot;{SAVE_DIR}/{TIME}-{sequence}.gif&quot; 
        dataset[0][sequence].to_gif(axis=2, duration=10, output_path=output_path, loop=0)
        paths[sequence] = f&quot;output/{TIME}-{sequence}.gif&quot;
    

    for sequence in tmp_file :
        os.remove(tmp_file[sequence])

    return {
        &quot;success&quot; : True,
        &quot;paths&quot; : paths
    }


app.mount(&quot;/&quot;, StaticFiles(directory=&quot;static&quot;, html=True), name=&quot;static&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프론트엔드 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드는 리액트로 만들예정입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트에 대해서는 너무 강좌가 많을정도로 대중적으로 쓰이는 프레임워크라서 쉽게 검색해서 공부해보실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;리액트 등 프레임워크 구성하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 간단한 예제를 위하여 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;npx create-react-app&lt;/b&gt;&lt;/span&gt;을 통하여 새로운 프로젝트를 만드는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에서 바로 하지 않고 &lt;u&gt;&lt;b&gt;로컬에서 진행합니다&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널상에서 해당 프로젝트를 만들고 싶은 폴더에 가서 npx create-react-app을 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 프로젝트 이름을 tumor로 설정하겠으며, 그러면 tumor라는 폴더가 만들어집니다.&lt;/p&gt;
&lt;pre id=&quot;code_1635668920612&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npx create-react-app tumor&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;2310&quot; data-filename=&quot;Screen Shot 2021-10-31 at 5.28.13 PM.png&quot; width=&quot;549&quot; height=&quot;929&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cOISrU/btrjqqGCaU0/OEiROk88FiWqVK3r9RvwU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cOISrU/btrjqqGCaU0/OEiROk88FiWqVK3r9RvwU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cOISrU/btrjqqGCaU0/OEiROk88FiWqVK3r9RvwU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcOISrU%2FbtrjqqGCaU0%2FOEiROk88FiWqVK3r9RvwU0%2Fimg.png&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;2310&quot; data-filename=&quot;Screen Shot 2021-10-31 at 5.28.13 PM.png&quot; width=&quot;549&quot; height=&quot;929&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 해당 디렉토리에 가서 필요한 프레임워크 두가지를 더 추가해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1635669860478&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd tumor
npm install axios
npm install react-bootstrap bootstrap@5.1.3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;axios는 network request를 잘 해주는 프레임워크로, 많이들 사용하고 있으며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(&lt;a href=&quot;https://www.npmjs.com/package/axios&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.npmjs.com/package/axios)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1635669991320&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;axios&quot; data-og-description=&quot;Promise based HTTP client for the browser and node.js&quot; data-og-host=&quot;www.npmjs.com&quot; data-og-source-url=&quot;https://www.npmjs.com/package/axios&quot; data-og-url=&quot;https://www.npmjs.com/package/axios&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b71yyV/hyMaUECZsV/drp3aJPsmPgAu6IujscLLK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.npmjs.com/package/axios&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.npmjs.com/package/axios&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b71yyV/hyMaUECZsV/drp3aJPsmPgAu6IujscLLK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;axios&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Promise based HTTP client for the browser and node.js&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.npmjs.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react-bootstrap은 너무 허접해보이는 것을 방지하기 위해서 좀 이쁜 HTML +CSS&amp;nbsp; 컴포넌트를 제공해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(&lt;a href=&quot;https://react-bootstrap.github.io/getting-started/introduction&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://react-bootstrap.github.io/)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1635669983641&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;React-Bootstrap&quot; data-og-description=&quot;The most popular front-end framework, rebuilt for React.&quot; data-og-host=&quot;react-bootstrap.github.io&quot; data-og-source-url=&quot;https://react-bootstrap.github.io/getting-started/introduction&quot; data-og-url=&quot;https://react-bootstrap.github.io/getting-started/introduction/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://react-bootstrap.github.io/getting-started/introduction&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://react-bootstrap.github.io/getting-started/introduction&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;React-Bootstrap&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The most popular front-end framework, rebuilt for React.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;react-bootstrap.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본인이 좋아하는 editor에서 생성된 폴더를 열어줍니다. (저는 VSCode)&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-origin-width=&quot;2272&quot; data-origin-height=&quot;1760&quot; data-filename=&quot;Screen Shot 2021-10-31 at 5.30.55 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zscdy/btrjqqfxRQY/BjWKWAL3089yaIBKrwN7BK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zscdy/btrjqqfxRQY/BjWKWAL3089yaIBKrwN7BK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zscdy/btrjqqfxRQY/BjWKWAL3089yaIBKrwN7BK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fzscdy%2FbtrjqqfxRQY%2FBjWKWAL3089yaIBKrwN7BK%2Fimg.png&quot; data-origin-width=&quot;2272&quot; data-origin-height=&quot;1760&quot; data-filename=&quot;Screen Shot 2021-10-31 at 5.30.55 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;React-bootstrap관련 설정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bootstrap은 추가 설정이 조금 더 필요합니다. 공식 가이드에 따라 아래와같이 수정이 필요하며,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;/tumor/public/index.html의 &amp;lt;head&amp;gt; tag안에 추가해주시면됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://react-bootstrap.github.io/getting-started/introduction/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://react-bootstrap.github.io/getting-started/introduction/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1635670944021&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;React-Bootstrap&quot; data-og-description=&quot;The most popular front-end framework, rebuilt for React.&quot; data-og-host=&quot;react-bootstrap.github.io&quot; data-og-source-url=&quot;https://react-bootstrap.github.io/getting-started/introduction/&quot; data-og-url=&quot;https://react-bootstrap.github.io/getting-started/introduction/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://react-bootstrap.github.io/getting-started/introduction/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://react-bootstrap.github.io/getting-started/introduction/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;React-Bootstrap&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The most popular front-end framework, rebuilt for React.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;react-bootstrap.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;pre id=&quot;code_1635670984588&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
  &amp;lt;head&amp;gt;
  	&amp;lt;!--- 아래를 추가해주세요 ---&amp;gt;
    &amp;lt;script src=&quot;https://unpkg.com/react/umd/react.production.min.js&quot; crossorigin&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script
      src=&quot;https://unpkg.com/react-dom/umd/react-dom.production.min.js&quot;
      crossorigin&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script
      src=&quot;https://unpkg.com/react-bootstrap@next/dist/react-bootstrap.min.js&quot;
      crossorigin&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script&amp;gt;var Alert = ReactBootstrap.Alert;&amp;lt;/script&amp;gt;
    &amp;lt;link
      rel=&quot;stylesheet&quot;
      href=&quot;https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css&quot;
      integrity=&quot;sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3&quot;
      crossorigin=&quot;anonymous&quot;
    /&amp;gt;
    &amp;lt;!--- 위를 추가해주세요 ---&amp;gt;
    &amp;lt;meta charset=&quot;utf-8&quot; /&amp;gt;
    &amp;lt;link rel=&quot;icon&quot; href=&quot;%PUBLIC_URL%/favicon.ico&quot; /&amp;gt;
    &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot; /&amp;gt;
    &amp;lt;meta name=&quot;theme-color&quot; content=&quot;#000000&quot; /&amp;gt;
    &amp;lt;meta&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1844&quot; data-origin-height=&quot;1054&quot; data-filename=&quot;Screen Shot 2021-10-31 at 6.03.35 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5iqZs/btrjl8GMQt8/hLgDJBg0NiCpIg4uQdgMP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5iqZs/btrjl8GMQt8/hLgDJBg0NiCpIg4uQdgMP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5iqZs/btrjl8GMQt8/hLgDJBg0NiCpIg4uQdgMP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5iqZs%2Fbtrjl8GMQt8%2FhLgDJBg0NiCpIg4uQdgMP0%2Fimg.png&quot; data-origin-width=&quot;1844&quot; data-origin-height=&quot;1054&quot; data-filename=&quot;Screen Shot 2021-10-31 at 6.03.35 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;레이아웃 구성하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;&lt;br /&gt;우리는 이 중 src/App.js에서만 작업할 예정으로 이 파일을 띄워주시면 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 다 지우고 새로운 마음으로 시작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1635669775034&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import './App.css';

function App() {
  return (
    &amp;lt;div className=&quot;App&quot;&amp;gt;
   	  Hello world!
    &amp;lt;/div&amp;gt;
  );
}

export default App;&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;이제 해당 폴더 (/tumor)에서 npm start 을 입력하여 실행시켜봅니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1635670195556&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm start&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1366&quot; data-origin-height=&quot;974&quot; data-filename=&quot;Screen Shot 2021-10-31 at 5.51.23 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/be1Lu3/btrjlyFobQo/pNqx36IJ47CulAXhYukb5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/be1Lu3/btrjlyFobQo/pNqx36IJ47CulAXhYukb5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/be1Lu3/btrjlyFobQo/pNqx36IJ47CulAXhYukb5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbe1Lu3%2FbtrjlyFobQo%2FpNqx36IJ47CulAXhYukb5K%2Fimg.png&quot; data-origin-width=&quot;1366&quot; data-origin-height=&quot;974&quot; data-filename=&quot;Screen Shot 2021-10-31 at 5.51.23 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우져에서 해당 위치 (http://localhost:3000)에서 웹페이지가 잘 작동하시는것을 볼 수 있으며, 내용을 수정하고 저장하면 자동으로 리프레쉬되며 내용이 바뀝니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단히 아래와같이 제목을 바꿔보고 저장해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1635670393084&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import './App.css';

function App() {
  return (
    &amp;lt;div className=&quot;App&quot;&amp;gt;
      &amp;lt;h3&amp;gt;Tumor Detector&amp;lt;/h3&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1366&quot; data-origin-height=&quot;974&quot; data-filename=&quot;Screen Shot 2021-10-31 at 5.53.02 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mwolX/btrjqnQof5h/RNbF67vbKCuyK95oG0fKHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mwolX/btrjqnQof5h/RNbF67vbKCuyK95oG0fKHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mwolX/btrjqnQof5h/RNbF67vbKCuyK95oG0fKHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmwolX%2FbtrjqnQof5h%2FRNbF67vbKCuyK95oG0fKHK%2Fimg.png&quot; data-origin-width=&quot;1366&quot; data-origin-height=&quot;974&quot; data-filename=&quot;Screen Shot 2021-10-31 at 5.53.02 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레이아웃으로는 아래의 항목들이 필요합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;파일을 업로드할 수 있는 인풋 4개 (t1, t2, t1ce, flair)&lt;/li&gt;
&lt;li&gt;업로드 버튼&lt;/li&gt;
&lt;li&gt;받은 결과를 표시할 곳&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 레이아웃을 구성할 수 있는 방법은 여러가지가 있겠지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React-bootstrap의 &amp;lt;Row&amp;gt; &amp;lt;Col&amp;gt;을 이용하려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;Row&amp;gt; 는 한 가로줄을 구성하고, Col은 칸(?)을 생성해줍니다.&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;a href=&quot;https://react-bootstrap.github.io/layout/grid/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://react-bootstrap.github.io/layout/grid/&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1635670512147&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;React-Bootstrap&quot; data-og-description=&quot;The most popular front-end framework, rebuilt for React.&quot; data-og-host=&quot;react-bootstrap.github.io&quot; data-og-source-url=&quot;https://react-bootstrap.github.io/layout/grid/&quot; data-og-url=&quot;https://react-bootstrap.github.io/layout/grid/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://react-bootstrap.github.io/layout/grid/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://react-bootstrap.github.io/layout/grid/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;React-Bootstrap&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The most popular front-end framework, rebuilt for React.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;react-bootstrap.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;1114&quot; data-filename=&quot;Screen Shot 2021-10-31 at 5.54.38 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A6rEX/btrjxc8r8w1/mrhYWyRKxnd08xUfkBbR7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A6rEX/btrjxc8r8w1/mrhYWyRKxnd08xUfkBbR7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A6rEX/btrjxc8r8w1/mrhYWyRKxnd08xUfkBbR7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA6rEX%2Fbtrjxc8r8w1%2FmrhYWyRKxnd08xUfkBbR7K%2Fimg.png&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;1114&quot; data-filename=&quot;Screen Shot 2021-10-31 at 5.54.38 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 컴포넌트들을 import해주시고 아래와같이 레이아웃을 구성해 봅니다.&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_1635674448270&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import './App.css';
import {Row, Col, Container} from 'react-bootstrap'

function App() {
  return (
    &amp;lt;div className=&quot;App&quot;&amp;gt;
      &amp;lt;h3&amp;gt;Tumor Detector&amp;lt;/h3&amp;gt;
      &amp;lt;Container fluid&amp;gt;
        &amp;lt;Row&amp;gt;
          &amp;lt;Col&amp;gt;input&amp;lt;/Col&amp;gt;
          &amp;lt;Col&amp;gt;input&amp;lt;/Col&amp;gt;
          &amp;lt;Col&amp;gt;input&amp;lt;/Col&amp;gt;
          &amp;lt;Col&amp;gt;input&amp;lt;/Col&amp;gt;
        &amp;lt;/Row&amp;gt;
        &amp;lt;Row&amp;gt;
          Button
        &amp;lt;/Row&amp;gt;
        &amp;lt;Row&amp;gt;
          &amp;lt;Col&amp;gt;output&amp;lt;/Col&amp;gt;
          &amp;lt;Col&amp;gt;output&amp;lt;/Col&amp;gt;
          &amp;lt;Col&amp;gt;output&amp;lt;/Col&amp;gt;
          &amp;lt;Col&amp;gt;output&amp;lt;/Col&amp;gt;
          &amp;lt;Col&amp;gt;output&amp;lt;/Col&amp;gt;

        &amp;lt;/Row&amp;gt;
      &amp;lt;/Container&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1856&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 7.00.28 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAexdi/btrjl8NBxDd/KJi7stqhk2Q0T2exEBGEV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAexdi/btrjl8NBxDd/KJi7stqhk2Q0T2exEBGEV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAexdi/btrjl8NBxDd/KJi7stqhk2Q0T2exEBGEV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAexdi%2Fbtrjl8NBxDd%2FKJi7stqhk2Q0T2exEBGEV0%2Fimg.png&quot; data-origin-width=&quot;1856&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 7.00.28 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Input component만들어보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트는 기본적으로 컴포넌트들을 만들고, 이 컴포넌트들의 조합으로 이뤄지는 개념입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 따라 input 에 들어갈 컴포넌트들을 만들어보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Javascript function이며 아래와같이 문서 아래에 추가해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1635674784088&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function InputComponent(props) {
  return(

    &amp;lt;Col&amp;gt;
        &amp;lt;input type=&quot;file&quot; /&amp;gt;
    &amp;lt;/Col&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 이제 위 스크립트의 &amp;lt;Col&amp;gt;input&amp;lt;/Col&amp;gt;부분을 해당 InputComponent로 대체할 수 있습니다&lt;/p&gt;
&lt;pre id=&quot;code_1635674844703&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import './App.css';
import {Row, Col, Container, InputGroup} from 'react-bootstrap'

function App() {
  return (
    &amp;lt;div className=&quot;App&quot;&amp;gt;
      &amp;lt;h3&amp;gt;Tumor Detector&amp;lt;/h3&amp;gt;
      &amp;lt;Container fluid&amp;gt;
        &amp;lt;Row&amp;gt;
          &amp;lt;InputComponent /&amp;gt;
          &amp;lt;InputComponent /&amp;gt;
          &amp;lt;InputComponent /&amp;gt;
          &amp;lt;InputComponent /&amp;gt;
        &amp;lt;/Row&amp;gt;
        &amp;lt;Row&amp;gt;
          Button
        &amp;lt;/Row&amp;gt;
        &amp;lt;Row&amp;gt;
          &amp;lt;Col&amp;gt;output&amp;lt;/Col&amp;gt;
          &amp;lt;Col&amp;gt;output&amp;lt;/Col&amp;gt;
          &amp;lt;Col&amp;gt;output&amp;lt;/Col&amp;gt;
          &amp;lt;Col&amp;gt;output&amp;lt;/Col&amp;gt;
          &amp;lt;Col&amp;gt;output&amp;lt;/Col&amp;gt;

        &amp;lt;/Row&amp;gt;
      &amp;lt;/Container&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

function InputComponent(props) {
  return(

    &amp;lt;Col&amp;gt;
        &amp;lt;input type=&quot;file&quot; /&amp;gt;
    &amp;lt;/Col&amp;gt;
  )
}
export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;3610&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 7.07.13 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mtLia/btrjl87UKWC/xKTYtoUFIledSVkkKxhOE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mtLia/btrjl87UKWC/xKTYtoUFIledSVkkKxhOE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mtLia/btrjl87UKWC/xKTYtoUFIledSVkkKxhOE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmtLia%2Fbtrjl87UKWC%2FxKTYtoUFIledSVkkKxhOE0%2Fimg.png&quot; data-origin-width=&quot;3610&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 7.07.13 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 InputComponent function은 props라는 argument를 받게 되어있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 props는 다른곳에서 이 컴포넌트를 사용할때 attribute로 설정되는 값들을 받아오는 역할을 합니다.&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;즉, 아래와 같이 component에서 label (또는 어떤 이름이어도 상관없습니다) 이라는 attribute를 정의하고나면 아래와같이 해당 컴포넌트에서 갖다가 쓸수 있게됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1635674978303&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Component label=&quot;this is label&quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1635674926722&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Component(props) {
	return(&amp;lt;span&amp;gt;props.label&amp;lt;/span&amp;gt;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 개념을 그대로 이용하여 InputComponent를 아래와같이 수정합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1635675081974&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function InputComponent(props) {
  return(

    &amp;lt;Col&amp;gt;
        {props.label} image :
        &amp;lt;input type=&quot;file&quot; onChange={props.handleChange} /&amp;gt;
    &amp;lt;/Col&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 output component도 React-bootstrap의 Figure component를 활용하여 새롭게 구성해 봅니다 :&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1635675511439&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import './App.css';
import {Row, Col, Container, Figure} from 'react-bootstrap'

const SEQUENCES = [&quot;t1&quot;,&quot;t2&quot;,&quot;flair&quot;,&quot;t1ce&quot;];
const OUTPUT_SEQUENCES = [&quot;t1&quot;,&quot;t2&quot;,&quot;flair&quot;,&quot;t1ce&quot;,&quot;segmented&quot;];
const SERVER_URL = &quot;3.35.4.26&quot;;

function App() {
  
  return (
    &amp;lt;div className=&quot;App&quot;&amp;gt;
      &amp;lt;h3&amp;gt;Tumor Detector&amp;lt;/h3&amp;gt;
      &amp;lt;Container fluid&amp;gt;
        &amp;lt;Row&amp;gt;
          {SEQUENCES.map((element) =&amp;gt; &amp;lt;InputComponent key={element} label={element} handleChange={(e) =&amp;gt; {
            console.log(e.target.files[0])
          }} /&amp;gt;)}
        &amp;lt;/Row&amp;gt;
        &amp;lt;Row&amp;gt;
          Button
        &amp;lt;/Row&amp;gt;
        &amp;lt;Row&amp;gt;
          {OUTPUT_SEQUENCES.map((element) =&amp;gt; &amp;lt;OutputComponent key={element} label={element} /&amp;gt;)}


        &amp;lt;/Row&amp;gt;
      &amp;lt;/Container&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

function InputComponent(props) {
  return(
    &amp;lt;Col&amp;gt;
        {props.label} image :
        &amp;lt;input type=&quot;file&quot; onChange={props.handleChange} /&amp;gt;
    &amp;lt;/Col&amp;gt;
  )
}


function OutputComponent(props) {
  return (
    &amp;lt;Col&amp;gt;
      &amp;lt;Figure&amp;gt;
        &amp;lt;Figure.Image
          width={128}
          height={128}
          alt={props.label}
          src={&quot;http://&quot; + SERVER_URL + props.path}
        /&amp;gt;
        &amp;lt;Figure.Caption&amp;gt;
          {props.label}
        &amp;lt;/Figure.Caption&amp;gt;
      &amp;lt;/Figure&amp;gt;
    &amp;lt;/Col&amp;gt;
  )
}
export default App;&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;위 코드를 잘 살펴보면, InputComponent에 handleChange라는 attribute를 전달하며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 attribute에다가 함수를 전달하고 있습니다. 이 함수는 e 라는 instance를 받아서 e.target.files[0]을 console.log함수에 전달합니다. e.target.files[0] 은 사용자가 선택한 파일의 이름이라고 보시면되겠으며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;console.log()은 디버그를 위한 함수로, 브라우져에서 (사용자에게 보이지않게) 디버그 메세지를 출력할 수 있습니다.&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;브라우져에서 (저는 현재 chrome을 사용중이며) 우측 클릭 -&amp;gt;inspector와 같은 메뉴를 클릭하여 디버그 창을 같이 켜놓으면 아래와같이 보입니다.&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-origin-width=&quot;3378&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 7.22.08 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/duzZ68/btrjqniCIP8/HbGDkUm200kKWcps3h2KaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/duzZ68/btrjqniCIP8/HbGDkUm200kKWcps3h2KaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/duzZ68/btrjqniCIP8/HbGDkUm200kKWcps3h2KaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FduzZ68%2FbtrjqniCIP8%2FHbGDkUm200kKWcps3h2KaK%2Fimg.png&quot; data-origin-width=&quot;3378&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 7.22.08 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 파일을 선택해보면 여기에 해당 파일이 출력됩니다.&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-origin-width=&quot;3378&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 7.23.19 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZGYTb/btrjl8NCVkc/VEpidaPqCacKmoiCpLCYCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZGYTb/btrjl8NCVkc/VEpidaPqCacKmoiCpLCYCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZGYTb/btrjl8NCVkc/VEpidaPqCacKmoiCpLCYCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZGYTb%2Fbtrjl8NCVkc%2FVEpidaPqCacKmoiCpLCYCK%2Fimg.png&quot; data-origin-width=&quot;3378&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 7.23.19 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 이렇게 사용자가 선택한 파일을 이제 프로그램상으로 받을 수 있어졌습니다.&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;이렇게 받은 변수는 javascript object를 만들어서 저장해놓으면 나중에 서버로 request를 보낼때 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;files라는 object를 정의하고, 업데이트시 여기에 저장되도록 스크립트를 변경합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1635676498998&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import './App.css';
import {Row, Col, Container, Figure} from 'react-bootstrap'
import Button from '@restart/ui/esm/Button';

const SEQUENCES = [&quot;t1&quot;,&quot;t2&quot;,&quot;flair&quot;,&quot;t1ce&quot;];
const OUTPUT_SEQUENCES = [&quot;t1&quot;,&quot;t2&quot;,&quot;flair&quot;,&quot;t1ce&quot;,&quot;segmented&quot;];
const SERVER_URL = &quot;3.35.4.26&quot;;

var files = {
  &quot;t1&quot; : null,
  &quot;t2&quot; : null,
  &quot;t1ce&quot; : null,
  &quot;flair&quot; : null
};

function App() {
  return (
    &amp;lt;div className=&quot;App&quot;&amp;gt;
      &amp;lt;h3&amp;gt;Tumor Detector&amp;lt;/h3&amp;gt;
      &amp;lt;Container fluid&amp;gt;
        &amp;lt;Row&amp;gt;
          {SEQUENCES.map((element) =&amp;gt; &amp;lt;InputComponent key={element} label={element} handleChange={(e) =&amp;gt; {
            files[element] = e.target.files[0];
          }} /&amp;gt;)}
        &amp;lt;/Row&amp;gt;
        &amp;lt;Row&amp;gt;
          Button
          
        &amp;lt;/Row&amp;gt;
        &amp;lt;Row&amp;gt;
          {OUTPUT_SEQUENCES.map((element) =&amp;gt; &amp;lt;OutputComponent key={element} label={element} /&amp;gt;)}


        &amp;lt;/Row&amp;gt;
      &amp;lt;/Container&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

function InputComponent(props) {
  return(
    &amp;lt;Col&amp;gt;
        {props.label} image :
        &amp;lt;input type=&quot;file&quot; onChange={props.handleChange} /&amp;gt;
    &amp;lt;/Col&amp;gt;
  )
}


function OutputComponent(props) {
  return (
    &amp;lt;Col&amp;gt;
      &amp;lt;Figure&amp;gt;
        &amp;lt;Figure.Image
          width={128}
          height={128}
          alt={props.label}
          src={&quot;http://&quot; + SERVER_URL + props.path}
        /&amp;gt;
        &amp;lt;Figure.Caption&amp;gt;
          {props.label}
        &amp;lt;/Figure.Caption&amp;gt;
      &amp;lt;/Figure&amp;gt;
    &amp;lt;/Col&amp;gt;
  )
}
export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Axios로 request보내기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제는 파일이 준비가 되었으니 이 파일을 서버로 보내기만 하면될것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;axios라는 프레임워크를 이용하게 될텐데요,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 이전에 &quot;Button&quot;이라고 쓰여있던 부분을 실제로 React-bootstrap의 Button component로 바꾸고, onClick 이벤트에 axios 함수를 불러 처리하도록 합니다.&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;우선 axios를 import하고,&lt;/p&gt;
&lt;pre id=&quot;code_1635677242749&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import axios from 'axios';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Button부분에 아래 스크립트를 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1635677270266&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Button onClick={() =&amp;gt; {
  if(Object.values(files).every((element) =&amp;gt; element !== null)) {

    const formData = new FormData();

    for (const [key,value] of Object.entries(files)) {
      formData.append(key+&quot;_file&quot;, value);
    }

    axios.post('http://'+SERVER_URL+'/predict',formData,{
      headers: { &quot;Content-Type&quot;: &quot;multipart/form-data&quot;}
    }).then((response) =&amp;gt; {
      console.log(response);
    }).catch((e) =&amp;gt; {
      alert('error');
    }) ;
  } else {
    alert('please select files for all sequences');
  }
}}&amp;gt;Submit&amp;lt;/Button&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;익숙하지 않으시다면 좀 어렵게 느껴지실수 있고, 그냥 자세한 문법등은 무시하시고 대략 이해만 하시면 될것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Button이라는 컴포넌트를 만들고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;거기에서 onClick이벤트로 아래 내용을 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. files object를 모두 검사해서 빈값이 없는지 확인하고(null), 빈값이 있으면 'please select files for all sequences'라고 에러 메세지를 출력한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. FormData instance를 만들고, 여기에 각 시퀀스별로 _file을 붙여서 보낼 값을 정의한다 (ie. t1_file, t2_file...) &amp;lt;- 이 부분은 이전에 만든 backend에 맞춰서 폼을 구성하는것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 이후 axios.post를 통해 post method로 우리 서버/predict주소로 해당 리퀘스트를 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 성공적으로 response가 도착하면 console.log으로 디버그 메세지를 출력한다.&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;이렇게 해서 실행하면, 아래와같이 (우리가 백엔드 짤 때 의도한대로) 잘 response가 도착하는것을 볼 수 있습니다.&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-origin-width=&quot;3378&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 7.46.56 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NSZ5G/btrjlO2W7kI/RFeoNKXlRPKcehM15dankk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NSZ5G/btrjlO2W7kI/RFeoNKXlRPKcehM15dankk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NSZ5G/btrjlO2W7kI/RFeoNKXlRPKcehM15dankk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNSZ5G%2FbtrjlO2W7kI%2FRFeoNKXlRPKcehM15dankk%2Fimg.png&quot; data-origin-width=&quot;3378&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 7.46.56 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데, 처음에 버튼을 누르고나서 사실 아무반응이 없어서 '버그인가?'싶으셨을 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 로딩을 표시하는 방법이 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로딩을 표시하기위해서는 react state에 대한 이해가 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;React State에 대한 이해&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트는 state기반 프레임워크입니다. 이 컨셉을 처음 접하고는 정말 너무 편리해서 저는 충격적이었는데요, 이 state라는 개념을 이해하는게 아주 쉬운일은 아닙니다. 관련된 문서를 한번 읽어보시기를 권유드립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.reactjs.org/docs/state-and-lifecycle.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ko.reactjs.org/docs/state-and-lifecycle.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1635675942406&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;State and Lifecycle &amp;ndash; React&quot; data-og-description=&quot;A JavaScript library for building user interfaces&quot; data-og-host=&quot;ko.reactjs.org&quot; data-og-source-url=&quot;https://ko.reactjs.org/docs/state-and-lifecycle.html&quot; data-og-url=&quot;https://ko.reactjs.org/docs/state-and-lifecycle.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/gVnCl/hyL9LvPzqb/bLzS6NhqSWv1XDbfObRfe0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://ko.reactjs.org/docs/state-and-lifecycle.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ko.reactjs.org/docs/state-and-lifecycle.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/gVnCl/hyL9LvPzqb/bLzS6NhqSWv1XDbfObRfe0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;State and Lifecycle &amp;ndash; React&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A JavaScript library for building user interfaces&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ko.reactjs.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게만 말씀드리자면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react component에서 state란, 해당 컴포넌트의 출력과 관련된 여러가지 변수를 저장할 수 있는 창고로 생각하시면됩니다.&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;예를 들어 name이라는 변수가 state안에 저장되고 있다고 정의하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 name이라는 변수를 이용해서 본문에서 출력하고 있다고 정의하면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 name을 변경하므로 해서 본문의 출력이 변경되게 되는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 우리의 케이스를 고민해보면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;loading이라는 변수를 만들고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약에 현재 서버에 요청을 보내놓은 상태이면 이 loading state를 true로 변경해놓고,&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;loading state가 true라면 출력상에서 버튼에다가 돌아가는 loading그림을 보여주면 적절할것 같습니다.&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;useState hook 이라는 react 개념을 이용하게되며, 이를 이용하여 아래와같이 코드를 수정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.reactjs.org/docs/hooks-state.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ko.reactjs.org/docs/hooks-state.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1635677603316&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Using the State Hook &amp;ndash; React&quot; data-og-description=&quot;A JavaScript library for building user interfaces&quot; data-og-host=&quot;ko.reactjs.org&quot; data-og-source-url=&quot;https://ko.reactjs.org/docs/hooks-state.html&quot; data-og-url=&quot;https://ko.reactjs.org/docs/hooks-state.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/IuhCf/hyMaWbsGIN/q3uPeq9uR5Nk37yV9fl2Qk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://ko.reactjs.org/docs/hooks-state.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ko.reactjs.org/docs/hooks-state.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/IuhCf/hyMaWbsGIN/q3uPeq9uR5Nk37yV9fl2Qk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Using the State Hook &amp;ndash; React&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A JavaScript library for building user interfaces&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ko.reactjs.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1635677756349&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import './App.css';
import {Row, Col, Container, Figure, Button, Spinner} from 'react-bootstrap'
import axios from 'axios';
import { useState } from 'react';

const SEQUENCES = [&quot;t1&quot;,&quot;t2&quot;,&quot;flair&quot;,&quot;t1ce&quot;];
const OUTPUT_SEQUENCES = [&quot;t1&quot;,&quot;t2&quot;,&quot;flair&quot;,&quot;t1ce&quot;,&quot;segmented&quot;];
const SERVER_URL = &quot;3.35.4.26&quot;;

var files = {
  &quot;t1&quot; : null,
  &quot;t2&quot; : null,
  &quot;t1ce&quot; : null,
  &quot;flair&quot; : null
};

function App() {
  const [loading, setLoading] = useState(false);

  return (
    &amp;lt;div className=&quot;App&quot;&amp;gt;
      &amp;lt;h3&amp;gt;Tumor Detector&amp;lt;/h3&amp;gt;
      &amp;lt;Container fluid&amp;gt;
        &amp;lt;Row&amp;gt;
          {SEQUENCES.map((element) =&amp;gt; &amp;lt;InputComponent key={element} label={element} handleChange={(e) =&amp;gt; {
            files[element] = e.target.files[0];
          }} /&amp;gt;)}
        &amp;lt;/Row&amp;gt;
        &amp;lt;Row&amp;gt;
          &amp;lt;Button onClick={() =&amp;gt; {
            if(Object.values(files).every((element) =&amp;gt; element !== null)) {
              
              const formData = new FormData();

              for (const [key,value] of Object.entries(files)) {
                formData.append(key+&quot;_file&quot;, value);
              }
              setLoading(true);

              axios.post('http://'+SERVER_URL+'/predict',formData,{
                headers: { &quot;Content-Type&quot;: &quot;multipart/form-data&quot;}
              }).then((response) =&amp;gt; {
                console.log(response);
                setLoading(false);
              }).catch((e) =&amp;gt; {
                alert('error');
                setLoading(false);
              }) ;
            } else {
              alert('please select files for all sequences');
            }
          }}&amp;gt;{loading ? &amp;lt;span&amp;gt;&amp;lt;Spinner
            as=&quot;span&quot;
            animation=&quot;grow&quot;
            size=&quot;sm&quot;
            role=&quot;status&quot;
            aria-hidden=&quot;true&quot;
          /&amp;gt;&amp;lt;span&amp;gt; loading&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; : &amp;lt;span&amp;gt;Submit&amp;lt;/span&amp;gt;}&amp;lt;/Button&amp;gt;
          
        &amp;lt;/Row&amp;gt;
        &amp;lt;Row&amp;gt;
          {OUTPUT_SEQUENCES.map((element) =&amp;gt; &amp;lt;OutputComponent key={element} label={element} /&amp;gt;)}


        &amp;lt;/Row&amp;gt;
      &amp;lt;/Container&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

function InputComponent(props) {
  return(
    &amp;lt;Col&amp;gt;
        {props.label} image :
        &amp;lt;input type=&quot;file&quot; onChange={props.handleChange} /&amp;gt;
    &amp;lt;/Col&amp;gt;
  )
}


function OutputComponent(props) {
  return (
    &amp;lt;Col&amp;gt;
      &amp;lt;Figure&amp;gt;
        &amp;lt;Figure.Image
          width={128}
          height={128}
          alt={props.label}
          src={&quot;http://&quot; + SERVER_URL + props.path}
        /&amp;gt;
        &amp;lt;Figure.Caption&amp;gt;
          {props.label}
        &amp;lt;/Figure.Caption&amp;gt;
      &amp;lt;/Figure&amp;gt;
    &amp;lt;/Col&amp;gt;
  )
}
export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;3378&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 7.55.42 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ct88Vl/btrjr1zZKSD/ofpmPuso520jCoCkHVbEEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ct88Vl/btrjr1zZKSD/ofpmPuso520jCoCkHVbEEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ct88Vl/btrjr1zZKSD/ofpmPuso520jCoCkHVbEEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fct88Vl%2Fbtrjr1zZKSD%2FofpmPuso520jCoCkHVbEEK%2Fimg.png&quot; data-origin-width=&quot;3378&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 7.55.42 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와같이 버튼을 클릭하고나면 로딩 애니메이션이 보여서 사용자가 지금 작업중이라는것을 인지할 수 있도록 합니다.&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;또한, 아래 결과이미지들의 주소 또한 state에 저장하게된다면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;response가 오고나서 이 주소를 저장하는것만으로 아래 이미지도 표시할 수 있게 됩니다.&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;여기에 추가로 loaded 라는 변수까지 정의하여서, loading 이 끝난 시점에서만 이미지를 보여서 엑박이 출력되지 않도록 수정해보면 아래와 같습니다.&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_1635680156879&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import './App.css';
import {Row, Col, Container, Figure, Button, Spinner} from 'react-bootstrap'
import axios from 'axios';
import { useState } from 'react';

const SEQUENCES = [&quot;t1&quot;,&quot;t2&quot;,&quot;flair&quot;,&quot;t1ce&quot;];
const OUTPUT_SEQUENCES = [&quot;t1&quot;,&quot;t2&quot;,&quot;flair&quot;,&quot;t1ce&quot;,&quot;segmented&quot;];
const SERVER_URL = &quot;3.35.4.26&quot;;

var files = {
  &quot;t1&quot; : null,
  &quot;t2&quot; : null,
  &quot;t1ce&quot; : null,
  &quot;flair&quot; : null
};

function App() {
  const [loading, setLoading] = useState(false);
  const [loaded, setLoaded] = useState(false);
  const [urls, setUrls] = useState({
    &quot;t1&quot;: null,
    &quot;t2&quot; : null,
    &quot;t1ce&quot; : null,
    &quot;flair&quot; : null
  });

  return (
    &amp;lt;div className=&quot;App&quot;&amp;gt;
      &amp;lt;h3&amp;gt;Tumor Detector&amp;lt;/h3&amp;gt;
      &amp;lt;Container fluid&amp;gt;
        &amp;lt;Row&amp;gt;
          {SEQUENCES.map((element) =&amp;gt; &amp;lt;InputComponent key={element} label={element} handleChange={(e) =&amp;gt; {
            files[element] = e.target.files[0];
          }} /&amp;gt;)}
        &amp;lt;/Row&amp;gt;
        &amp;lt;Row&amp;gt;
          &amp;lt;Button onClick={() =&amp;gt; {
            if(Object.values(files).every((element) =&amp;gt; element !== null)) {
              
              const formData = new FormData();

              for (const [key,value] of Object.entries(files)) {
                formData.append(key+&quot;_file&quot;, value);
              }
              setLoading(true);
              setLoaded(false); // reset loaded state, so hide result images

              axios.post('http://'+SERVER_URL+'/predict',formData,{
                headers: { &quot;Content-Type&quot;: &quot;multipart/form-data&quot;}
              }).then((response) =&amp;gt; {
                console.log(response);
                setLoading(false);
                setLoaded(true); // set loaded ==true only when successful
                setUrls(response.data.paths);

              }).catch((e) =&amp;gt; {
                alert('error');
                setLoading(false);
              }) ;
            } else {
              alert('please select files for all sequences');
            }
          }}&amp;gt;{loading ? &amp;lt;span&amp;gt;&amp;lt;Spinner
            as=&quot;span&quot;
            animation=&quot;grow&quot;
            size=&quot;sm&quot;
            role=&quot;status&quot;
            aria-hidden=&quot;true&quot;
          /&amp;gt;&amp;lt;span&amp;gt; loading&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; : &amp;lt;span&amp;gt;Submit&amp;lt;/span&amp;gt;}&amp;lt;/Button&amp;gt;
          
        &amp;lt;/Row&amp;gt;
        {loading === false &amp;amp;&amp;amp; loaded === true &amp;amp;&amp;amp; &amp;lt;Row&amp;gt;
          {OUTPUT_SEQUENCES.map((element) =&amp;gt; &amp;lt;OutputComponent key={element} label={element} path={urls[element]} /&amp;gt;)}


        &amp;lt;/Row&amp;gt;}
      &amp;lt;/Container&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

function InputComponent(props) {
  return(
    &amp;lt;Col&amp;gt;
        {props.label} image :
        &amp;lt;input type=&quot;file&quot; onChange={props.handleChange} /&amp;gt;
    &amp;lt;/Col&amp;gt;
  )
}


function OutputComponent(props) {
  return (
    &amp;lt;Col&amp;gt;
      &amp;lt;Figure&amp;gt;
        &amp;lt;Figure.Image
          width={128}
          height={128}
          alt={props.label}
          src={&quot;http://&quot; + SERVER_URL + &quot;/&quot; + props.path}
        /&amp;gt;
        &amp;lt;Figure.Caption&amp;gt;
          {props.label}
        &amp;lt;/Figure.Caption&amp;gt;
      &amp;lt;/Figure&amp;gt;
    &amp;lt;/Col&amp;gt;
  )
}
export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와같이 결과를 받기전에는 최종 이미지 관련 섹션이 뜨지 않다가,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;3386&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 8.36.24 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A5C0H/btrjsrkPpce/tv80RtSfepZJkHpwxkKF11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A5C0H/btrjsrkPpce/tv80RtSfepZJkHpwxkKF11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A5C0H/btrjsrkPpce/tv80RtSfepZJkHpwxkKF11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA5C0H%2FbtrjsrkPpce%2Ftv80RtSfepZJkHpwxkKF11%2Fimg.png&quot; data-origin-width=&quot;3386&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 8.36.24 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과가 나오면 그때 내용이 뜹니다.&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-origin-width=&quot;3386&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 8.36.32 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cRjOvD/btrjnIgmis7/l8Fiq3jCPUzi3TJ3F9hRTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cRjOvD/btrjnIgmis7/l8Fiq3jCPUzi3TJ3F9hRTk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cRjOvD/btrjnIgmis7/l8Fiq3jCPUzi3TJ3F9hRTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcRjOvD%2FbtrjnIgmis7%2Fl8Fiq3jCPUzi3TJ3F9hRTk%2Fimg.png&quot; data-origin-width=&quot;3386&quot; data-origin-height=&quot;1692&quot; data-filename=&quot;Screen Shot 2021-10-31 at 8.36.32 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;리액트 사이트 웹에 올리기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재까지는 개발목적으로 로컬에서 돌리고 있었는데, 실제로 서버에 올리려면 build 과정을 거쳐야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;/tumor 폴더에서&lt;/p&gt;
&lt;pre id=&quot;code_1635680706836&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm run build&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 입력하면 /tumor/build 디렉토리가 생성되며 파일들이 만들어집니다.&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-origin-width=&quot;1464&quot; data-origin-height=&quot;866&quot; data-filename=&quot;Screen Shot 2021-10-31 at 8.45.57 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1TZdI/btrjyIMWMG7/iYqF5W8IsZ35UOzKbWD8y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1TZdI/btrjyIMWMG7/iYqF5W8IsZ35UOzKbWD8y1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1TZdI/btrjyIMWMG7/iYqF5W8IsZ35UOzKbWD8y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1TZdI%2FbtrjyIMWMG7%2FiYqF5W8IsZ35UOzKbWD8y1%2Fimg.png&quot; data-origin-width=&quot;1464&quot; data-origin-height=&quot;866&quot; data-filename=&quot;Screen Shot 2021-10-31 at 8.45.57 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일들을 sftp client를 이용하여 /home/ec-user/server/static/ 폴더안에 올려줍니다.&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;http://[IP_ADDRESS]/&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 접속하면 만들어진 웹사이트를 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;웹사이트 꾸미기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 아무래도 좀더 보기 좋게 꾸미는 일만 남았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React-bootstrap framework에서 다양한 component를 보면서 하나하나 추가해나가는 재미가 있습니다.&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;이 전체 소스코드는 github에서 확인하실 수 있습니다.&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-origin-width=&quot;2836&quot; data-origin-height=&quot;1902&quot; data-filename=&quot;Screen Shot 2021-10-31 at 8.44.00 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7uioS/btrjxdTXC9s/mGwgmfQK6Ydh6vwMpPPA30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7uioS/btrjxdTXC9s/mGwgmfQK6Ydh6vwMpPPA30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7uioS/btrjxdTXC9s/mGwgmfQK6Ydh6vwMpPPA30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7uioS%2FbtrjxdTXC9s%2FmGwgmfQK6Ydh6vwMpPPA30%2Fimg.png&quot; data-origin-width=&quot;2836&quot; data-origin-height=&quot;1902&quot; data-filename=&quot;Screen Shot 2021-10-31 at 8.44.00 PM.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상이었습니다. 감사합니다 ^^&lt;/p&gt;</description>
      <category>컴퓨터</category>
      <author>JNHEO</author>
      <guid isPermaLink="true">https://jnheo.tistory.com/40</guid>
      <comments>https://jnheo.tistory.com/40#entry40comment</comments>
      <pubDate>Sun, 31 Oct 2021 20:47:52 +0900</pubDate>
    </item>
    <item>
      <title>[업적] 전국에서 사용한 QR 체크인 개발</title>
      <link>https://jnheo.tistory.com/39</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;2021.08.22 페이스북 글&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;오늘도 아무생각 없이 머리를 자르러 가서 QR 체크인을 하다가 생각해보니, 예전에 보건복지부에서 처음 이런 QR 체크인 프로토타입을 구상하실때, 프로토타입을 만들어드렸던 기억이 났다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당시 보건복지부에 계시던&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;&lt;a href=&quot;https://www.facebook.com/junghwan.park?__cft__[0]=AZXtiiQQy_qhj8-9SIwof9E4lfo2SkWKm5gQSmYQk9Vy-Zf5LgXk0b74BOHfOTjr6V9ibiwjCdQNYlhBa9-BPQrqb7hM9pgvaClZoCdmhleQQzshXXC7QoDudj2MlGek5vs&amp;amp;__tn__=-]K-R&quot;&gt;&lt;span&gt;&lt;span&gt;Junghwan Park&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;선배님께서 구상하신 QR 체크인은 정말 엄청난 내공에서 나온 혁신적인 개념으로, 3자 (정부, 업소, 제3자업체) 가 각각 분산되어 개인정보를 갖고있어서, 실제로 코로나 확진자가 발생하여 조회가 필요한 상황이 되지 않고서는 절대 누가 어디를 갔는지 알 수 없게 만든, 개인정보보호측면에서 완벽하면서도 필요시에는 너무나도 빠르게 개인 식별이 가능한데다가. 사용자도 정말 번거롭지 않고 편한... 정말 완벽한 솔루션이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선배님께서 밤새서 이 구상을 하시고 높은분들께 보고하신 후 전국적으로 이런 서비스를 도입할 예정으로, 빠르게 프로토타입이 필요하신 상황이었다 (이런 복잡한 개념을 윗분들에게 효과적으로 전달하기위해)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 너무 신나서 당연히 기꺼이 만들어드렸고, 토요일 하루동안 백엔드 3포인트와 (정부, 업소, 그리고 프로그램 제공업체) front-end 세 개를 만들었었다 (정부용 조회 페이지, 업소용 스캔프로그램, 사용자 QR 생성앱)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 뭐 내가 한 일은 새발의 피정도 되는거라 대세에 큰 도움은 되지 않았겠지만... 이후에 이 QR체크인 시스템이 정말 전국적으로 도입되고, 내가 샘플로 개발했던 이런 솔루션이 전국민 모두가 당연하게 사용하는 시스템이 되는 것을 보며, 나는 어떤 사람이 되고 싶은가 많이 고민을 했었다.&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;(나중에 유명 학술지에서 나한테 리뷰를 요청한 논문을 보면서 그 잘난 미국에서도 이런 contact tracing이 얼마나 허접하게 이뤄졌는지 알 수 있었다... 우리 나라의 QR체크인이 정말 대단한거임..)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저 앱을 만들던 날, 주말인데 하루종일 컴터에서 꼼짝도 안하니 옆에 와서 우리 딸이 &quot;아빠 뭐해?&quot; 라면서 내 마우스 옆에 같이 놀아달라고 자기 장난감을 잔뜩 쌓아놓고 갔던 기억이 난다. 나중에 크면 아빠가 이런일도 도와드린적있어!! 라고 자랑해야지.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;239290155_10159706365954656_6922294531243421557_n.jpg&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1170&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAVhmm/btrdK59ETFC/eGoYOlv7XGMOxCPkkMD4c1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAVhmm/btrdK59ETFC/eGoYOlv7XGMOxCPkkMD4c1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAVhmm/btrdK59ETFC/eGoYOlv7XGMOxCPkkMD4c1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAVhmm%2FbtrdK59ETFC%2FeGoYOlv7XGMOxCPkkMD4c1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2048&quot; height=&quot;1170&quot; data-filename=&quot;239290155_10159706365954656_6922294531243421557_n.jpg&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1170&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;239956408_10159706365814656_685257581425343398_n.jpg&quot; data-origin-width=&quot;864&quot; data-origin-height=&quot;1764&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjPZkd/btrdKWkklFU/kKkeadRgWcsIaodD2Hbn90/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjPZkd/btrdKWkklFU/kKkeadRgWcsIaodD2Hbn90/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjPZkd/btrdKWkklFU/kKkeadRgWcsIaodD2Hbn90/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjPZkd%2FbtrdKWkklFU%2FkKkeadRgWcsIaodD2Hbn90%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;256&quot; height=&quot;523&quot; data-filename=&quot;239956408_10159706365814656_685257581425343398_n.jpg&quot; data-origin-width=&quot;864&quot; data-origin-height=&quot;1764&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;237921109_10159706365764656_4876565297123483256_n.jpg&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;1136&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dBBXuA/btrdJiVWgOE/hvmvV98Qqd8gWDCCglanq1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dBBXuA/btrdJiVWgOE/hvmvV98Qqd8gWDCCglanq1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dBBXuA/btrdJiVWgOE/hvmvV98Qqd8gWDCCglanq1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdBBXuA%2FbtrdJiVWgOE%2FhvmvV98Qqd8gWDCCglanq1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;471&quot; data-filename=&quot;237921109_10159706365764656_4876565297123483256_n.jpg&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;1136&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>Projects</category>
      <author>JNHEO</author>
      <guid isPermaLink="true">https://jnheo.tistory.com/39</guid>
      <comments>https://jnheo.tistory.com/39#entry39comment</comments>
      <pubDate>Wed, 1 Sep 2021 21:48:59 +0900</pubDate>
    </item>
    <item>
      <title>슬라이드 스캐너 파일 Crop &amp;amp; Export (tif)</title>
      <link>https://jnheo.tistory.com/38</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 제가 병리사진을 이용한 연구를 할 일이 있어서 슬라이드 스캐너로 스캔된 파일을 분석해야했는데요, .svs, .mrxs, .svslide등 슬라이드만을 위한 포맷을 사용하고 있어서 적잖이 당황했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 정확히 공부하지는 않았지만 병리 슬라이드에서 특별히 저장하는 다양한 정보(예를 들어 다양한 배율의 사진)를 담기 위함일텐데요, 그냥 단순히TIF, PNG, JPG등 일반적으로 사용가능한 포맷으로 변환하는 작업 자체도 쉽지 않게 느껴졌습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제 경우에는 스캐너 회사에서 윈도우즈에서만 돌아가는 viewer를 제공하는 바람에 맥기반인 제가 매우 난감했는데요, 그래서 OpenSlide라는 라이브러리를 이용하여 간단히 mouse drag으로 필요한 부분만 cropping하고 원하는 포맷으로 export해주는 스크립트를 제작했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 그냥 개인적으로 쓰고 버리려고 하다가, 혹시라도 누구에게 도움이 되실까 싶어서 코드 정리해서 DevDoctors에 프로젝트로 올려봤습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DevDoctors에서 혹시 병리사진 분석하시는 고수님들계시면 서로 노하우 공유했으면 좋겠습니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;a href=&quot;https://github.com/devdoctors/python-slide-crop&quot;&gt;https://github.com/devdoctors/python-slide-crop&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DevDoctors: &lt;span&gt;&lt;a href=&quot;https://bit.ly/devdoctors&quot;&gt;https://bit.ly/devdoctors&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>컴퓨터</category>
      <category>mrxs</category>
      <category>SVS</category>
      <category>svslide</category>
      <author>JNHEO</author>
      <guid isPermaLink="true">https://jnheo.tistory.com/38</guid>
      <comments>https://jnheo.tistory.com/38#entry38comment</comments>
      <pubDate>Wed, 1 Sep 2021 21:46:59 +0900</pubDate>
    </item>
    <item>
      <title>DevDoctors - 개발하는 의사들</title>
      <link>https://jnheo.tistory.com/37</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;DevDoctors와 함께해요!&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;저의 친구이자 KAIST에서 AI로 박사를 취득한 정신과&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;&lt;a href=&quot;https://www.facebook.com/byunghoon.kim.52?__cft__[0]=AZUhduzXwZiMMeFsFN9jHCxoq9VoFP1Sac7mz2hjHQTETjrXF-vNCniGQJ4RTnRC37EhGDBshz_CUzptHHgoFMUeKfvWzfFhI4jQIVBcmvJGUggBHagCoGjx_u2Bt_vPj5c&amp;amp;__tn__=-]K-R&quot;&gt;&lt;span&gt;&lt;span&gt;김병훈&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;선생님과 함께 디지털의료를 주제로 소통의 장을 마련하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소통을 통한 친목 도모에서부터 정보 공유, 강의, 세미나, 그리고 장기적으로는 각종 오픈소스 프로젝트 진행까지 계획하고 있으니&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;a href=&quot;http://bit.ly/devdoctors&quot;&gt;http://bit.ly/devdoctors&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에서 확인해 주세요&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의료인이 아니더라도 디지털의료 관련 분야에 종사중이신 분들도 모시고 있으니 많은 관심 부탁드립니다!&lt;/p&gt;</description>
      <category>Projects</category>
      <author>JNHEO</author>
      <guid isPermaLink="true">https://jnheo.tistory.com/37</guid>
      <comments>https://jnheo.tistory.com/37#entry37comment</comments>
      <pubDate>Wed, 1 Sep 2021 21:45:37 +0900</pubDate>
    </item>
    <item>
      <title>의료 데이터 관리 플랫폼 리디아</title>
      <link>https://jnheo.tistory.com/36</link>
      <description>&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-origin-width=&quot;2995&quot; data-origin-height=&quot;1453&quot; data-filename=&quot;img.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKVA1N/btrdJV0hJmr/TwkuqChu0SKrvVBusKlg3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKVA1N/btrdJV0hJmr/TwkuqChu0SKrvVBusKlg3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKVA1N/btrdJV0hJmr/TwkuqChu0SKrvVBusKlg3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKVA1N%2FbtrdJV0hJmr%2FTwkuqChu0SKrvVBusKlg3K%2Fimg.png&quot; data-origin-width=&quot;2995&quot; data-origin-height=&quot;1453&quot; data-filename=&quot;img.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의료 데이터를 엑셀에 보관하시는 선생님들이 많아서, 관리하시기 좋은 서비스를 런칭하였습니다 ^^&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;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://blog.redea.io&quot;&gt;https://blog.redea.io&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1630500010995&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;RE&amp;bull;DEA Blog&quot; data-og-description=&quot;의료데이터는 리디아에서! 무료로 편하게 관리하세요~ https://redea.io&quot; data-og-host=&quot;blog.redea.io&quot; data-og-source-url=&quot;https://blog.redea.io&quot; data-og-url=&quot;https://blog.redea.io&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/BnI9I/hyLspfp1pS/c0wxK6l6X7kFjuiqsZeG4k/img.jpg?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/cDWvsU/hyLsn2XO2w/Z31aUkHk5DkNyDzpBKXI40/img.jpg?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512&quot;&gt;&lt;a href=&quot;https://blog.redea.io&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://blog.redea.io&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/BnI9I/hyLspfp1pS/c0wxK6l6X7kFjuiqsZeG4k/img.jpg?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/cDWvsU/hyLsn2XO2w/Z31aUkHk5DkNyDzpBKXI40/img.jpg?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;RE&amp;bull;DEA Blog&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;의료데이터는 리디아에서! 무료로 편하게 관리하세요~ https://redea.io&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;blog.redea.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Projects</category>
      <category>eCRF</category>
      <category>REDEA</category>
      <category>리디아</category>
      <category>의료데이터</category>
      <author>JNHEO</author>
      <guid isPermaLink="true">https://jnheo.tistory.com/36</guid>
      <comments>https://jnheo.tistory.com/36#entry36comment</comments>
      <pubDate>Wed, 1 Sep 2021 21:36:18 +0900</pubDate>
    </item>
    <item>
      <title>디지털 헬스케어 인터뷰 출연</title>
      <link>https://jnheo.tistory.com/35</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Digital Healthcare Partners의 최윤섭 대표님께서 감사하게도 저를 인터뷰해주셔서&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2부에 걸친 유투브로 인터뷰 영상이 올라왔었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1편 : &lt;a href=&quot;https://youtu.be/gA_nSrUIaPA&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://youtu.be/gA_nSrUIaPA&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2편 : &lt;a href=&quot;https://youtu.be/z0EcxNGGRc0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://youtu.be/z0EcxNGGRc0&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Projects</category>
      <author>JNHEO</author>
      <guid isPermaLink="true">https://jnheo.tistory.com/35</guid>
      <comments>https://jnheo.tistory.com/35#entry35comment</comments>
      <pubDate>Wed, 1 Sep 2021 21:32:53 +0900</pubDate>
    </item>
    <item>
      <title>한국일보 기사</title>
      <link>https://jnheo.tistory.com/34</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;2021.02.14일, 발렌타인데이에 한국일보에 우리 프로젝트 관련 기사가 났었다.&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;a href=&quot;https://n.news.naver.com/article/469/0000579992?cds=news_edit&amp;amp;fbclid=IwAR1kfB59FqHLsWqNCEmxIrpzDQfRkey627CylzCgHowHc382uMUMi9Qqd7c&quot;&gt;https://n.news.naver.com/article/469/0000579992?cds=news_edit&amp;amp;fbclid=IwAR1kfB59FqHLsWqNCEmxIrpzDQfRkey627CylzCgHowHc382uMUMi9Qqd7c&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1630499397028&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[단독] 현역 군의관이 만든 세계 첫 AI 코로나 앱, 글로벌 학술지가 인정했다&quot; data-og-description=&quot;현역 군의관들이 세계 최초로 인공지능(AI)을 바탕으로 신종 코로나바이러스 감염증(코로나19) 중증 환자와 경증 환자를 빠르게 가려내는 애플리케이션(앱)을 개발한 데 이어 저명 해외 학회지에&quot; data-og-host=&quot;n.news.naver.com&quot; data-og-source-url=&quot;https://n.news.naver.com/article/469/0000579992?cds=news_edit&amp;amp;fbclid=IwAR1kfB59FqHLsWqNCEmxIrpzDQfRkey627CylzCgHowHc382uMUMi9Qqd7c&quot; data-og-url=&quot;https://n.news.naver.com/article/469/0000579992&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cTsXad/hyLsmXitYF/sLHMLsYqjTsgYBHHsd7NG1/img.jpg?width=640&amp;amp;height=427&amp;amp;face=184_134_233_187,https://scrap.kakaocdn.net/dn/dtBQI7/hyLsnhApEH/J69AcjR2FahPvymkElgIXK/img.jpg?width=640&amp;amp;height=427&amp;amp;face=184_134_233_187&quot;&gt;&lt;a href=&quot;https://n.news.naver.com/article/469/0000579992?cds=news_edit&amp;amp;fbclid=IwAR1kfB59FqHLsWqNCEmxIrpzDQfRkey627CylzCgHowHc382uMUMi9Qqd7c&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://n.news.naver.com/article/469/0000579992?cds=news_edit&amp;amp;fbclid=IwAR1kfB59FqHLsWqNCEmxIrpzDQfRkey627CylzCgHowHc382uMUMi9Qqd7c&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cTsXad/hyLsmXitYF/sLHMLsYqjTsgYBHHsd7NG1/img.jpg?width=640&amp;amp;height=427&amp;amp;face=184_134_233_187,https://scrap.kakaocdn.net/dn/dtBQI7/hyLsnhApEH/J69AcjR2FahPvymkElgIXK/img.jpg?width=640&amp;amp;height=427&amp;amp;face=184_134_233_187');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[단독] 현역 군의관이 만든 세계 첫 AI 코로나 앱, 글로벌 학술지가 인정했다&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;현역 군의관들이 세계 최초로 인공지능(AI)을 바탕으로 신종 코로나바이러스 감염증(코로나19) 중증 환자와 경증 환자를 빠르게 가려내는 애플리케이션(앱)을 개발한 데 이어 저명 해외 학회지에&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;n.news.naver.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Projects</category>
      <author>JNHEO</author>
      <guid isPermaLink="true">https://jnheo.tistory.com/34</guid>
      <comments>https://jnheo.tistory.com/34#entry34comment</comments>
      <pubDate>Wed, 1 Sep 2021 21:30:09 +0900</pubDate>
    </item>
    <item>
      <title>2021 Google COVID-19 Research and Action Workshop 참석 후기</title>
      <link>https://jnheo.tistory.com/33</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot; data-filename=&quot;141908221_10159230297174656_5960619122583540228_n.jpg&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cidk6Z/btrdK61J9ot/cRYNqoOgjnsqRkrxhTA7G1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cidk6Z/btrdK61J9ot/cRYNqoOgjnsqRkrxhTA7G1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cidk6Z/btrdK61J9ot/cRYNqoOgjnsqRkrxhTA7G1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcidk6Z%2FbtrdK61J9ot%2FcRYNqoOgjnsqRkrxhTA7G1%2Fimg.jpg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot; data-filename=&quot;141908221_10159230297174656_5960619122583540228_n.jpg&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2021 COVID-19 Research and Action Workshop&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pacific time이다보니 새벽 1시-5시에 2일간 진행되었던 workshop!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전세계 연구동향을 볼 수 있어서 너무 좋았고, 외국 사람들의 현재 코로나에 대응하는 분위기가 느껴져서 너무 좋았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 우리 결과를 공유할때 많은 사람들이 놀랐고, 심사에 참여했다는 Google Health에서 일하는 specialist가 나만큼 우리프로젝트를 잘 이해하고 소개해줘서 우리에게 관심이 많았다는 점, (패널 토크 중에 전체 앞에서 내이름을 언급하며 우리프로젝트가 의의가 크고 코로나 상황 이후에도 이런 프로젝트로 인하여 의료현장이 많이 변화할거라고 해줘서 더 뜻깊었다) 그리고 진심으로 우리를 바라봐줬다는 그 sincerity에 감사했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 내가 팔불출이라 그럴수 있지만 우리만큼 가시적인 성과가 있는 그룹이 전세계에 손에 꼽는다고 감히 말할 수 있어서 너무 좋았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 서비스가 해외에서 쓰이도록 앱도 제공하고 API도 제공한다고 여기저기 말하고 다니며 열심히 NETWORKING하였으니 뭐라도 우리 프로젝트에 도움이되었으면!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Many thanks to Google!&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;[2021.01.29 작성 글]&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>Projects</category>
      <category>Google AI for Social Good Grant</category>
      <category>닥클</category>
      <author>JNHEO</author>
      <guid isPermaLink="true">https://jnheo.tistory.com/33</guid>
      <comments>https://jnheo.tistory.com/33#entry33comment</comments>
      <pubDate>Wed, 1 Sep 2021 21:28:25 +0900</pubDate>
    </item>
  </channel>
</rss>