<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>절취선 블로그</title>
    <link>https://languagefight.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sat, 11 Jul 2026 18:19:03 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>절취선</managingEditor>
    <image>
      <title>절취선 블로그</title>
      <url>https://tistory1.daumcdn.net/tistory/2796491/attach/30a23eb050a74dddb399ef1b2ad22a87</url>
      <link>https://languagefight.tistory.com</link>
    </image>
    <item>
      <title>비행일정관리앱 개발 후기</title>
      <link>https://languagefight.tistory.com/213</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;2250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xLdTl/btsIZLT9uW5/dfPmjpffFWt2mF9itMtwKk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xLdTl/btsIZLT9uW5/dfPmjpffFWt2mF9itMtwKk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xLdTl/btsIZLT9uW5/dfPmjpffFWt2mF9itMtwKk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxLdTl%2FbtsIZLT9uW5%2FdfPmjpffFWt2mF9itMtwKk%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;4000&quot; height=&quot;2250&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;2250&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;왜 이런 앱을 배포했는가?&lt;/h1&gt;
&lt;p&gt;일반 직작 9-6 근무로 살아가는 일반 직장인의 일과를 쫓아가는 일은 가족으로서 크게 신경을 쓸 일은 아니지만&lt;/p&gt;
&lt;p&gt;비행 기장으로 지내는 가족의 일정을 확인하는 것은 꼭 필요한 일이다.&lt;/p&gt;
&lt;p&gt;옷을 준비하고 오붓한 시간을 보내는 등 가족으로서 알아야할 그리고 기장이 다음 비행을 준비하는 가장 기본적인 일이기 때문인데&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;내가 받아본 일정표는 마치 엑셀로 뽑아놓은 것처럼 보기에 불편했으며 항공사에서는 이를 관리할 수 있는 서비스가 전혀 없었다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1812&quot; data-origin-height=&quot;2176&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zB3eH/btsI0Iu2C6L/aYkvHJhHk1PTsuzz1jVScK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zB3eH/btsI0Iu2C6L/aYkvHJhHk1PTsuzz1jVScK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zB3eH/btsI0Iu2C6L/aYkvHJhHk1PTsuzz1jVScK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzB3eH%2FbtsI0Iu2C6L%2FaYkvHJhHk1PTsuzz1jVScK%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;1812&quot; height=&quot;2176&quot; data-origin-width=&quot;1812&quot; data-origin-height=&quot;2176&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;내가 해야할 것은 무엇인가?&lt;/h2&gt;
&lt;p&gt;개발자의 입장에서 생각해보면 이런 문제를 두고 어떤 서비스를 만들 수 있을까?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;비행 일정을 보기좋게 앱으로 만들자&lt;/li&gt;
&lt;li&gt;날씨 정보도 가져와 볼까?&lt;/li&gt;
&lt;li&gt;돈도 벌 수 있도록 광고도 달아야지!&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;문제 발생&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;비행 일정을 관리하는 기업에서 API를 가져오면되는거 아니야?&lt;/code&gt; 라고 생각한다면 나도 가장먼저 생각한 부분이다.&lt;/p&gt;
&lt;p&gt;하지만 &lt;code&gt;CrewConnex&lt;/code&gt;에서 돌아온 대답은 개인이 서비스 개발을 위해 API를 제공할 수 없으며 항공 데이터는 보여줄 수 없다는 대답이 돌아왔다.&lt;/p&gt;
&lt;p&gt;여기서 발생하는 문제점은 2가지가 발생한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;이미지 한장의 데이터를 어떻게 DB에 입력을 해야할까?&lt;/li&gt;
&lt;li&gt;데이터 노출 위험&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h1&gt;극복 계획&lt;/h1&gt;
&lt;h2&gt;OCR&lt;/h2&gt;
&lt;p&gt;가장 먼저 생각한 아이디어는 이미지 내부의 글자를 데이터화 시키는 OCR 기술을 사용해 DB에 저장시키는 것이었다.&lt;/p&gt;
&lt;p&gt;AWS EC2를 사용할 계획이었기 때문에 &lt;a href=&quot;https://aws.amazon.com/ko/textract/&quot;&gt;Amazon Textract&lt;/a&gt;으로 사용하기로 했다.&lt;/p&gt;
&lt;h3&gt;Amazon Textract?&lt;/h3&gt;
&lt;p&gt;사실 OCR 기술이지만 AWS에서 사용하는 기술이며 솔직히 개발에 대한 사용 후기 혹은 블로그 정보가 너무나도 적었으며 Document를 봐도 이해하기는 어려워 사용하기 까다로웠다.&lt;/p&gt;
&lt;h2&gt;데이터 노출&lt;/h2&gt;
&lt;p&gt;마지막까지 너무 어려운 문제였다.&lt;/p&gt;
&lt;p&gt;서비스는 &lt;code&gt;항공사 기장과 가족&lt;/code&gt; 만 가입할 수 있도록 해야하며 공개되어서는 안되었는데 방법을 찾아야 했다.&lt;/p&gt;
&lt;p&gt;때문에 다른 유명 서비스들은 어떤 방식을 고수하고 있는지 확인했고 마침 &amp;#39;&lt;a href=&quot;https://www.teamblind.com/kr/&quot;&gt;블라인드&lt;/a&gt;&amp;#39; 였다.&lt;/p&gt;
&lt;p&gt;블라인드는 가입시 기업 이메일을 사용하기 때문에 재직을 증명할 수 있었고 해당 메일을 사용한다면 &lt;code&gt;가족 인증 정보 전달&lt;/code&gt;, &lt;code&gt;가입 이메일 전달&lt;/code&gt; 등 다양한 정보 전달을 안전하게 전달 할 수 있게된 것이다.&lt;/p&gt;
&lt;h1&gt;배포하는 데 뭐가 어려웠는지?&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;비용&lt;/code&gt;과 &lt;code&gt;EC2의 메모리&lt;/code&gt; 그리고 &lt;code&gt;용량&lt;/code&gt;을 최적화 할 수 있는 &lt;strong&gt;인스턴스 합의점을 찾는 것이 가장 어려웠다.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;용량관리는 (Docker의 도입)?&lt;/h2&gt;
&lt;p&gt;Back-End를 감당 가능한 최소한의 비용이 가능한 t2.small 인스턴스를 선택했고 인스턴스 내부에 코드를 두는 것이 아닌 Docker를 배치에 이미지 및 컨테이너로 관리한다면 용량을 절반은 줄일 수 있을 것이라 생각했다.&lt;/p&gt;
&lt;h2&gt;메모리는(swap-file 도입)?&lt;/h2&gt;
&lt;p&gt;Docker는 용량의 개선을 도와주지만 메모리 관리에서는 한계점이 존재한다. &lt;code&gt;t2.small&lt;/code&gt;으로는 부족한 메모리를&lt;br&gt;&lt;code&gt;swap-file&lt;/code&gt;을 이용하여 2G 정도 늘려주는 방법을 선택했다.&lt;/p&gt;
&lt;hr&gt;
&lt;h1&gt;배포하면서 배운 점이 무엇인지?&lt;/h1&gt;
&lt;h2&gt;사용자 친화적&lt;/h2&gt;
&lt;p&gt;배포하는 기술보다는 서비스 차원에서 사용자가 어떤것을 원하는지 고민하는 과정에서 배울것이 더 많았다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;사용자가 안심하고 사용할 수 있는&lt;/li&gt;
&lt;li&gt;정확한 데이터를 관리할 수 있는가&lt;/li&gt;
&lt;li&gt;UI는 사용자 친화적인가&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;비행 기장이 필요한 정보가 무엇일까를 조사한 결과 일정에 이어서 &lt;code&gt;날씨&lt;/code&gt; 정보였다.&lt;/p&gt;
&lt;p&gt;도착지 날씨를 알아야 조종 방식이 바뀔 수 있다고 하니 DB에 국가별 위도 / 경도를 추가하여 &lt;a href=&quot;https://home.openweathermap.org/users/sign_in&quot;&gt;Open-Weather&lt;/a&gt;에 전달하여 데이터를 가져왔다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;540&quot; data-origin-height=&quot;1320&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpqYMI/btsI07VGJTt/UscBSrkN4LTvk8DZoGhGfK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpqYMI/btsI07VGJTt/UscBSrkN4LTvk8DZoGhGfK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpqYMI/btsI07VGJTt/UscBSrkN4LTvk8DZoGhGfK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpqYMI%2FbtsI07VGJTt%2FUscBSrkN4LTvk8DZoGhGfK%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;540&quot; height=&quot;1320&quot; data-origin-width=&quot;540&quot; data-origin-height=&quot;1320&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;Flutter의 도입&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Flutter&lt;/code&gt; 도입으로 새로운 프레임워크를 접할 수 있었다.&lt;/p&gt;
&lt;p&gt;빠른 개발과 완만한 러닝곡선을 가지는 모바일 프레임워크를 찾던중에 &lt;code&gt;Flutter&lt;/code&gt;를 도입한다면 좋겠다 생각했다.&lt;/p&gt;
&lt;p&gt;솔직하게 개발기간 지연을 막기위해 좋게 말한다면 &lt;code&gt;GPT주도개발&lt;/code&gt;로 진행했지만 아무 지식도 없이 Flutter프레임워크를 사용하는것은 온전히 기술을 사용할 수 없을 것으로 생각하여 이론적 배경부터 학습하여 장점을 최대화 하려 노력했다.&lt;/p&gt;
&lt;h1&gt;마치며&lt;/h1&gt;
&lt;p&gt;1년 6개월 계획 6개월 개발 의 기간을 거쳐 2년의 기간동안 서비스 개발의 쉼표를 찍었다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;마침표&lt;/code&gt;가 아닌 &lt;code&gt;쉼표&lt;/code&gt;인 이유는 &lt;strong&gt;앞으로도 지속적으로 개발을 진행해야하기 때문이며, 이직할때 써먹어야하니까...&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;앞으로도 활발하게 사용할 것이며 몇몇의 유저들이 도입될지 모르겠지만 SQS 유지를 위해 지속적으로 관심을 가지고 개발을 할 예정이다.&lt;/p&gt;</description>
      <category>후기</category>
      <author>절취선</author>
      <guid isPermaLink="true">https://languagefight.tistory.com/213</guid>
      <comments>https://languagefight.tistory.com/213#entry213comment</comments>
      <pubDate>Sat, 10 Aug 2024 23:14:38 +0900</pubDate>
    </item>
    <item>
      <title>SphinxSE는 Join 쿼리를 지원하는가?</title>
      <link>https://languagefight.tistory.com/212</link>
      <description>&lt;h1&gt;Sphinx 엔진의 한계&lt;/h1&gt;
&lt;p&gt;스핑크스를 사용하면서 가장 중요한 한계점을 발견하였는데&lt;br&gt;이미 인덱싱 처리가 되어있는 Sphinx 테이블간의 데이터 활용을 위한&lt;br&gt;Join / Subquery / Union 쿼리가 지원되지 않는다는 점이다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://sphinxsearch.com/forum/view.html?id=12237&quot;&gt;SphinxQL support for inner join on the same index with subquery&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;[일부 쿼리를 지원하지 않는 근본적 이유]&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;검색 엔진 최적화:&lt;br&gt;Sphinx는 주로 텍스트 검색을 위해 최적화된 검색 엔진이며 복잡한 SQL 기능보다는 고속의 텍스트 검색 및 인덱싱이 목적이기 때문이다.&lt;/li&gt;
&lt;li&gt;데이터 저장 및 인덱싱&lt;br&gt;Sphinx는 데이터를 &lt;code&gt;자체적인 인덱스 파일 형식&lt;/code&gt;으로 저장하고 관리하며 인덱스 파일은 MySQL의 테이블 구조와 다르다.&lt;/li&gt;
&lt;li&gt;단일 테이블 기반 검색&lt;br&gt;Sphinx는 &lt;code&gt;단일 테이블 기반의 검색을 주로 지원&lt;/code&gt;하며, 이는 빠른 검색 속도를 유지하기 위한 것입니다.&lt;/li&gt;
&lt;li&gt;복잡성 감소&lt;br&gt;데이터베이스 엔진의 복잡성이 크게 증가를 막기 위해 다중 테이블 쿼리는 지원하지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;정리하면 빠르게 값을 찾기위한 색인(Indexing) 방식을 통해 복잡했던 다중 테이블 연산 방식을 포기하고&lt;br&gt;속도 개선을 위한 인덱싱 처리에만 집중한 것으로 보인다.&lt;/p&gt;
&lt;h2&gt;여러 테이블을 다루어야하는 경우에는?&lt;/h2&gt;
&lt;p&gt;Sphinx 테이블이 생성되기 위해서는 대상 테이블을 인덱싱하는 과정을 지나야한다.&lt;br&gt;&lt;code&gt;sphinx.conf&lt;/code&gt; 파일에서 sph_{TableName}을 선언하고 원하는 테이블의 형태를 구현할 수 있는데&lt;br&gt;인덱싱을 위해서는 테이블을 호출해야하는데 해당 단계 까지는 Mysql | MariaDB | Nosql 등 DB에서 사용해왔던 쿼리는 모두 사용가능하다.&lt;/p&gt;
&lt;p&gt;즉, A | B 테이블을 결합을 원한다면 인덱싱 과정에서 Query를 실행시켜 sphinx테이블을 생성해야 한다는 것이다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;source sph_test{
        type = mysql

    ... 생략

        sql_query = 
        SELECT b.idx, a.join_key, a.n_date, a.n_time, a.m_cd, a.h_cd, a.img_chk, a.vod_chk, a.r_date, b.title, b.content 
        FROM SEARCH_table1 as a 
        JOIN SEARCH_YTN_table2 as b ON a.join_key = b.join_key

    ... 생략
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>DataBase</category>
      <category>DATABASE</category>
      <category>Join</category>
      <category>MySQL</category>
      <category>Sphinx</category>
      <author>절취선</author>
      <guid isPermaLink="true">https://languagefight.tistory.com/212</guid>
      <comments>https://languagefight.tistory.com/212#entry212comment</comments>
      <pubDate>Fri, 5 Jul 2024 10:11:01 +0900</pubDate>
    </item>
    <item>
      <title>서비스별 STT 개요 및 예시 코드</title>
      <link>https://languagefight.tistory.com/211</link>
      <description>&lt;h1&gt;서비스별 STT 개발 요구사항&lt;/h1&gt;
&lt;h2&gt;개요&lt;/h2&gt;
&lt;p&gt;정부 주관으로 청각 장애인 복지를 위해 영상 미디어 자료에 자막을 추가하는 사업을 수행하게 한다. &lt;/p&gt;
&lt;p&gt;자막은 VTT 파일로 관리하며 아래 4가지 서비스 중에서 하나를 선택하여 개발한다.&lt;/p&gt;
&lt;p&gt;서비스 선택은 API 지원 방식 / 문서화 / 비용 3가지를 중점으로 확인하며 확장자(.mp4)를 지원하는 서비스 위주로 선택된다. 결과 파일은 현재 사용중인 GS CDN에 저장하도록 하며 이후 파일을 관리할 수 있는 DB Table을 생성한다.&lt;/p&gt;
&lt;h2&gt;서비스별 요구사항&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;서비스&lt;/th&gt;
&lt;th&gt;AWS&lt;/th&gt;
&lt;th&gt;Naver Clova&lt;/th&gt;
&lt;th&gt;AssemblyAI&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;PHP 지원 방식&lt;/td&gt;
&lt;td&gt;SDK&lt;/td&gt;
&lt;td&gt;cURL&lt;/td&gt;
&lt;td&gt;cURL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;기타 언어&lt;/td&gt;
&lt;td&gt;PHP / JAVA / Python&lt;/td&gt;
&lt;td&gt;PHP / JAVA / Python&lt;/td&gt;
&lt;td&gt;PHP / JAVA / Python&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;미디어 형식 지원&lt;/td&gt;
&lt;td&gt;Mp4 / mp3&lt;/td&gt;
&lt;td&gt;Mp4 / mp3&lt;/td&gt;
&lt;td&gt;Mp4 / mp3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;화자분할&lt;/td&gt;
&lt;td&gt;O&lt;/td&gt;
&lt;td&gt;O&lt;/td&gt;
&lt;td&gt;O&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Input 링크&lt;/td&gt;
&lt;td&gt;S3 URI&lt;/td&gt;
&lt;td&gt;접근가능 링크&lt;/td&gt;
&lt;td&gt;접근가능 링크&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;출력&lt;/td&gt;
&lt;td&gt;VTT / SRT / JSON&lt;/td&gt;
&lt;td&gt;SRT / JSON&lt;/td&gt;
&lt;td&gt;VTT / SRT / JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;최대 용량&lt;/td&gt;
&lt;td&gt;2GB / 4H&lt;/td&gt;
&lt;td&gt;2GB / 2H&lt;/td&gt;
&lt;td&gt;5GB /10H&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;비용(1분당)&lt;/td&gt;
&lt;td&gt;0.024USD(32원)&lt;/td&gt;
&lt;td&gt;20원&lt;/td&gt;
&lt;td&gt;0.006USD(8원)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;추가작업&lt;/td&gt;
&lt;td&gt;SRT -&amp;gt; VTT&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;FFMPEG 오디오 작업&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;동기&lt;/td&gt;
&lt;td&gt;비동기&lt;/td&gt;
&lt;td&gt;동기 / 비동기&lt;/td&gt;
&lt;td&gt;비동기&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;pre&gt;&lt;code&gt;S3 URI: `s3://{bucket}/{file_name.mp4}`
sync: 인식 완료되면 response 결과(json)을 받을 수 있음
async: 입력한 Callback url 또는 ObjectStorage에 인식 결과를 리턴
60초보다 긴 오디오를 텍스트로 변환하려면 데이터를 Google Cloud Storage 버킷에 저장&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;서비스별 처리시간&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;30초&lt;/th&gt;
&lt;th&gt;1분25초&lt;/th&gt;
&lt;th&gt;5분&lt;/th&gt;
&lt;th&gt;12분&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;AWS&lt;/td&gt;
&lt;td&gt;20s&lt;/td&gt;
&lt;td&gt;31s&lt;/td&gt;
&lt;td&gt;30s&lt;/td&gt;
&lt;td&gt;61s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Naver Clova&lt;/td&gt;
&lt;td&gt;4.7s&lt;/td&gt;
&lt;td&gt;5.9s&lt;/td&gt;
&lt;td&gt;9.0s&lt;/td&gt;
&lt;td&gt;측정불가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AssemblyAI&lt;/td&gt;
&lt;td&gt;10.8s&lt;/td&gt;
&lt;td&gt;17.5s&lt;/td&gt;
&lt;td&gt;31.5s&lt;/td&gt;
&lt;td&gt;75.4s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;요금&lt;/h2&gt;
&lt;h3&gt;2024.03 등록 영상 기준 (총 13,293분)&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;서비스&lt;/th&gt;
&lt;th&gt;비용(분)&lt;/th&gt;
&lt;th&gt;총 비용&lt;/th&gt;
&lt;th&gt;환산(24.03.22 기준)&lt;/th&gt;
&lt;th&gt;일평균(663분)&lt;/th&gt;
&lt;th&gt;USD 환산&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;AWS&lt;/td&gt;
&lt;td&gt;0.024USD&lt;/td&gt;
&lt;td&gt;319.032USD&lt;/td&gt;
&lt;td&gt;425,931원&lt;/td&gt;
&lt;td&gt;21,277원&lt;/td&gt;
&lt;td&gt;15.912USD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AssemblyAI&lt;/td&gt;
&lt;td&gt;0.006USD&lt;/td&gt;
&lt;td&gt;79.758USD&lt;/td&gt;
&lt;td&gt;106,482원&lt;/td&gt;
&lt;td&gt;5,316원&lt;/td&gt;
&lt;td&gt;3.978USD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CLOVA&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;372,200원&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;₩18,560원&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GOOGLE&lt;/td&gt;
&lt;td&gt;0.024USD&lt;/td&gt;
&lt;td&gt;319.032USD&lt;/td&gt;
&lt;td&gt;425,931원&lt;/td&gt;
&lt;td&gt;21,277원&lt;/td&gt;
&lt;td&gt;15.912USD&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;2023.12 등록 영상 기준 (총 18409분)&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;서비스&lt;/th&gt;
&lt;th&gt;비용(분)&lt;/th&gt;
&lt;th&gt;총 비용&lt;/th&gt;
&lt;th&gt;환산(24.03.22 기준)&lt;/th&gt;
&lt;th&gt;일평균(594분)&lt;/th&gt;
&lt;th&gt;USD 환산&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;AWS&lt;/td&gt;
&lt;td&gt;0.024USD&lt;/td&gt;
&lt;td&gt;441.816USD&lt;/td&gt;
&lt;td&gt;592,070원&lt;/td&gt;
&lt;td&gt;19,059원&lt;/td&gt;
&lt;td&gt;14.256USD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AssemblyAI&lt;/td&gt;
&lt;td&gt;0.006USD&lt;/td&gt;
&lt;td&gt;110.454USD&lt;/td&gt;
&lt;td&gt;148,017원&lt;/td&gt;
&lt;td&gt;4,764원&lt;/td&gt;
&lt;td&gt;3.564USD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CLOVA&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;515,450원&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;₩16,630원&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GOOGLE&lt;/td&gt;
&lt;td&gt;0.024USD&lt;/td&gt;
&lt;td&gt;441.816USD&lt;/td&gt;
&lt;td&gt;592,070원&lt;/td&gt;
&lt;td&gt;19,059원&lt;/td&gt;
&lt;td&gt;14.256USD&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h1&gt;6. 개발&lt;/h1&gt;
&lt;h2&gt;AWS&lt;/h2&gt;
&lt;h3&gt;개발 환경&lt;/h3&gt;
&lt;p&gt;AWS는 API 서비스 간에 높은 결합도를 보이는데, 이는 폐쇄성이 강하다고 말할 수 있다.&lt;/p&gt;
&lt;p&gt;특히나 AWS S3를 사용하지 않고 기업에서 외부 CDN 서버를 사용한다면 해당 서비스 이용에 있어 API 요청과 응답을 문제에 대해 생각 해야한다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;AWS Transcribe&lt;/code&gt;에 미디어 데이터 요청 시 S3 URI 값을 요구하는데, S3 bucket 생성과 데이터가 해당 Bucket에 저장되어 있어야 한다는 전제가 요구된다. 또한 API 요청에 대한 결과 파일(VTT / SRT / JSON)이 S3 Bucket으로 떨어지기 때문에 해당 서비스가 AWS의 API 개발 환경이 폐쇄적이라고 느껴진 원인이 이것이다.&lt;/p&gt;
&lt;p&gt;정리하면 API 요청 시 유저의 Access / Secret key를 요구하는데, 이는 IAM에 대한 정보이며 해당 IAM 권한에 S3 Object Storage / AWS Transcribe가 추가가 되어 있어야 사용할 수 있다.&lt;/p&gt;
&lt;p&gt;처리 방식은 sync / async 방식을 제공하며 요청 결과는 S3에 저장된다. 만일 외부 CDN을 사용하는 경우 결과 파일을 S3가 아닌 사용 중인 스토리지로 전달하는 작업이 필요하지만 언제 작업이 완료될지 모르는 상황에서 지속적으로 작업 상태를 확인할 여유는 없을 것이고 cron_tab을 설정하자니 기업 입장에서는 등록 후 cron_tab이 동작하기를 마냥 기다릴 수는 없을 것이다.&lt;/p&gt;
&lt;p&gt;API 성능은 다른 서비스와 비교할 수 없을 정도로 양호하지만 S3를 통한 데이터 교환만 가능하다는 점에서 기업에 적용하기란 쉽지 않을 것이다.&lt;/p&gt;
&lt;h2&gt;Naver Clova&lt;/h2&gt;
&lt;p&gt;AWS와 다르게 네이버는 미디어 파일에 접근 가능 주소라면 어떤 방식이든 요청이 가능하며 응답으로 Naver Object Storage와 CallBack주소를 사용할 수 있는데 외부CDN을 사용하는 기업 입장에서는 AWS를 대체할 수 있는 좋은 대안이 될 것이다.&lt;/p&gt;
&lt;p&gt;Request.php 파일에서 미디어 주소와 callback 주소를 기입하여 API를 요청하면 Response.php으로 요청시 기입한 파일 형태(JSON / SRT)로 반환해 주기 때문에 이후 작업은 Response.php에서 처리해 줄 수 있다.&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/KIM-JS-95/175f62d2b72fabf786f42c02b5955503.js&quot;&gt;&lt;/script&gt;&lt;script src=&quot;https://gist.github.com/KIM-JS-95/ da74938d985bebc40bbeb9f4b86c68da.js&quot;&gt;&lt;/script&gt;

&lt;h2&gt;AssemblyAI&lt;/h2&gt;
&lt;p&gt;STT(Speech to Text)에 집중하여 개발하는 AI스타트업으로 높은 정확도와 저렴한 비용을 자랑한다.&lt;br&gt;webhooks을 지원하여 외부CDN을 사용하는 기업 입장에서는 Naver_Clova다음으로 좋은 대안이 될 것이며 JAVA, Python, Ruby, GO, PHP 코드를 사용할 수 있는 자체 API를 운용과 문서도 제공하고 있어 어렵지 않게 코드작성을 할 수 있다.&lt;/p&gt;
&lt;p&gt;PHP의 경우 Webhooks 요청은 body에 적으면 요청할 수 있다.&lt;br&gt;&lt;strong&gt;(API 문서에는 찾을 수 없으니 참고)&lt;/strong&gt;&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/KIM-JS-95/41c440181cc566d48d412ddadeb85f1e.js&quot;&gt;&lt;/script&gt;&lt;script src=&quot;https://gist.github.com/KIM-JS-95/5a037ea401bcb4b95764d706db7d8c0b.js&quot;&gt;&lt;/script&gt;</description>
      <category>AssemblyAI</category>
      <category>Clova_specch</category>
      <category>naver</category>
      <category>STT</category>
      <author>절취선</author>
      <guid isPermaLink="true">https://languagefight.tistory.com/211</guid>
      <comments>https://languagefight.tistory.com/211#entry211comment</comments>
      <pubDate>Thu, 4 Apr 2024 08:57:34 +0900</pubDate>
    </item>
    <item>
      <title>AWS textreck 테스트 전용 Entity</title>
      <link>https://languagefight.tistory.com/210</link>
      <description>&lt;p&gt;AWS 에서 제공하는 텍스트 감지 서비스 &lt;strong&gt;Textreck&lt;/strong&gt; 을 사용하면 한번 테스트하면 약 0.1달러씩 지불해야하기 때문에&lt;/p&gt;
&lt;p&gt;개발을 진행하는 과정에서는 너무 부담되는 가격이다.&lt;/p&gt;
&lt;p&gt;그런 개발자분을 위해 내가 테스트 전용으로 만든 엔티티를 공유하고자 한다.&lt;/p&gt;
&lt;p&gt;사용방법은 AWS에서 제공하는 Code_Sample를 그대로 사용하고 엔티티만 교체하여 사용하면된다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://aws.amazon.com/ko/textract/&quot;&gt;AWS Textreck Page&lt;/a&gt;&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/KIM-JS-95/007f6ac4a25c4080f9f4973351b23f4a.js&quot;&gt;&lt;/script&gt;</description>
      <category>프로그래밍 언어/JAVA</category>
      <category>AWS</category>
      <category>textreck</category>
      <author>절취선</author>
      <guid isPermaLink="true">https://languagefight.tistory.com/210</guid>
      <comments>https://languagefight.tistory.com/210#entry210comment</comments>
      <pubDate>Sun, 25 Jun 2023 11:51:49 +0900</pubDate>
    </item>
    <item>
      <title>[work 기록장] 반복되는 컬럼 개선</title>
      <link>https://languagefight.tistory.com/209</link>
      <description>&lt;h1&gt;반복되는 컬럼을 한줄로 표현하여 개선&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;다수의 테이블은 어떻게 조합할 것인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL에서 테이블을 조합하는 과정은 크게 2가지 방법(UNION / JOIN)이 있다.&lt;br /&gt;어느 함수가 더 뛰어나다 라는 것은 없으며 상황에 적합하도록 사용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나의 경우 3개의 테이블(PC / MA / MW)에서 동일 데이터 컬럼을 가져왔으며&lt;br /&gt;UNION을 사용하는 것이 적합했기에 최종 select 에서는 UNION을 사용했으며&lt;br /&gt;부가적으로 세부 컬럼을 가져와야 할 경우는 JOIN으로 획득하였다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;최초 작성 쿼리&lt;/h3&gt;
&lt;script src=&quot;https://gist.github.com/KIM-JS-95/4a80647e1bc0be64dd9cb4bb4b7c835c.js&quot;&gt;&lt;/script&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 쿼리가 작동하는데 있어서 문제는 없지만&lt;br /&gt;해당 결과를 화면에 보여준다면 문제가 생기게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만일'집계'가 Daily, 하루 단위로 집계가 된다면 최대 12개월의 31일 그리고 해당 테이블의 컬럼 갯수&lt;br /&gt;즉, 최소 372n 의 결과가 나타나는 것이다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;center&quot;&gt;cnt&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;tbname&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;n_date&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;41&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;A*&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;2023-04-02*&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;10&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;B*&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;2023-04-02*&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;6&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;C&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;2023-04-03&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;6&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;A&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;2023-04-04&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;750&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;C*&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;2023-04-02*&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;...&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;...&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;56&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;C&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;2023-04-30&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 결과를 다음과 같은 형식으로 개발한다면 어떨까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;```xml &lt;br /&gt;&amp;lt;pv_date&amp;gt;20230402&amp;lt;/pv_date&amp;gt; &lt;br /&gt;&amp;lt;pvs&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;A&amp;gt;41&amp;lt;/A&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;B&amp;gt;10&amp;lt;/B&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;C&amp;gt;750&amp;lt;/C&amp;gt; &lt;br /&gt;&amp;lt;/pvs&amp;gt; &lt;br /&gt;... &lt;br /&gt;```&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개선&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;월별, 작성일, 고유 코드를 기준으로 3번 순회해야 하지만&lt;br /&gt;&amp;gt; 컬럼의 수[별도의 테이블의 갯수(3개) * 1달(31)] * 3번 순회를 생각한다면&lt;br /&gt;위 xml을 만들기 위해 270번의 작업 처리를 반복해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 php 코드 개선 이전에 테이블에서 낭비되는 컬럼 데이터 즉 (PC: 41 / MW: null / MA: null)&lt;br /&gt;에 집중하였고 `tbname` 컬럼을 기준으로 만일 한줄로 집계할 수 있다면&lt;br /&gt;2번의 순회로 정리할 수 있다 생각했다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;테이블 개선&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과 테이블을 확인해보자 컬럼`tbname`이 중복되는 것을 볼 수 있는데&lt;br /&gt;하나의 컬럼에 동일한 `tbname`에 해당하는 `cnt` 값을 몰아준다면 어떨까?&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;전&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;center&quot;&gt;cnt&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;tbname&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;n_date&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;41&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;A*&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;2023-04-02*&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;10&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;B*&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;2023-04-02*&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;6&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;C&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;2023-04-03&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;6&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;A&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;2023-04-04&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;750&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;C*&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;2023-04-02*&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;...&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;...&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;56&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;C&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;2023-04-30&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;후&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;center&quot;&gt;A&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;B&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;C&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;n_date&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;41&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;6&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;750&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;2023-04-02&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;6&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;0&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;0&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;2023-04-03&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;6&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;0&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;0&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;2023-04-04&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;...&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;0&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;0&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;56&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;2023-04-30&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;쿼리 수정&lt;/h3&gt;
&lt;script src=&quot;https://gist.github.com/KIM-JS-95/af85dc10138b681289b810ee2d185680.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개선된 테이블을 의 컬럼수는 최대 해당 월의 일수로만큼 줄일 수 있었다.&lt;br /&gt;&amp;gt; 컬럼의 수[통합된 테이블의 갯수(1개) * 1달(31)] * 2번 순회&lt;/p&gt;</description>
      <category>DataBase</category>
      <category>MySQL</category>
      <author>절취선</author>
      <guid isPermaLink="true">https://languagefight.tistory.com/209</guid>
      <comments>https://languagefight.tistory.com/209#entry209comment</comments>
      <pubDate>Fri, 16 Jun 2023 09:06:54 +0900</pubDate>
    </item>
    <item>
      <title>파일 저장소 마이그레이션 후기</title>
      <link>https://languagefight.tistory.com/208</link>
      <description>&lt;h1&gt;파일 저장소 마이그레이션 후기&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;사연&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 이 이야기는 내가 입사하기전 22년 초부터 나온 이야기 일듯하다.&lt;br /&gt;본래 우리 회사의 CDN관리를 한 기업에서 전담하여 관리해 왔고 해당 기업은 주로 &lt;code&gt;FTP 방식&lt;/code&gt;으로 관리해온듯 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 뉴스회사가 그렇듯 방대한 이미지 데이터와 영상관리에 우리가 가용할 수 있는용량이 부족해지자 해당 기업은 2가지 선택지는 주었는데&lt;br /&gt;하나는 비용 업그레이드이며 다른 하나는 &lt;code&gt;Object Storage S3&lt;/code&gt;로 변경하여 관리하는 것이였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 이 두가지는 모두 사용해본 경험으로서는 S3가 다양한 이점을 가져다 주고&lt;br /&gt;이러한 이점 때문인지 회사 또한 선택지에 대한 고민은 없었다고 했다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;변경 목적&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;첫 번째&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 우선 순위는 Object storage가 가진 저장소의 특성일 것이다.&lt;br /&gt;데이터를 &lt;code&gt;객체 단위&lt;/code&gt;로 저장하기 때문에 기존 블록 단위 저장방식과 달리&lt;br /&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;개발 과정에서 느낀 이 둘의 차이점은 단순함 일듯 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FTP는 TCP/IP 프로토콜을 바탕으로 세션을 다루어 데이터를 옯기는데&lt;br /&gt;** 세션연결 -&amp;gt; 요청 및 결과 반환 -&amp;gt; 세션 해제** 의 과정으로 데이터가 저장되며&lt;br /&gt;매 요청마다 연결과 해제를 반복해 주어야한다는 것이 단점이였다.&lt;br /&gt;또한 ASCII 파일과 Binary 파일을 구분하기 때문에 저장 방식이 달라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 Object Storage는 API 방식을 사용하여&lt;br /&gt;계정정보과 요청 사항을 함수로 전달하면 되기 때문에 전 과정보다 훨씬 단순해 진다.&lt;br /&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;FTP 보안에 대해 많은 것을 신경써야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FTP로 전송되는 데이터는 암호화가 되지 않아 MITM 공격에 당할 경우 그래도 노출될 수 있으며 계정정보에 사용될 패스워드의 경우 평문 암호화를 사용하기 때문에 쉽게 노출될 가능성이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 보안이슈로 FTP에 SSL 혹은 SSH를 합하여 사용하는 &lt;code&gt;암호화 프로토콜 방식&lt;/code&gt;을 사용하거나 VPN을 사용하는 등 많은 것을 고려해야하는 번거로움이 따른다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Object Storage&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Object Storage는 대용량의 비정형 데이터를 저장하는 데 사용되는 데이터 스토리지 방식 중 하나입니다. 객체는 고유한 식별자와 데이터로 구성되며, 전체 객체 단위로 저장됩니다. 객체는 파일이나 블록 형식이 아니라 데이터 자체를 포함하고 있기 때문에 검색 및 관리가 용이하며, 대규모 데이터를 더 효율적으로 관리할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;FTP(File Transfer Protocol)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP/IP 프로토콜을 기반으로 하는 파일 전송 프로토콜이며 응용계층에서 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FTP는 클라이언트-서버 모델을 기반으로 동작하며, 클라이언트와 서버 간에 제어 및 데이터 연결을 설정후 파일 전송을 수행한다.&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;code&gt;Object Storage&lt;/code&gt; 방식으로 변경하는데에는 2달 정도 소요된듯 하다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 변경 및 테스트 -&amp;gt; CND 데이터 이전 -&amp;gt; 최종 적으로 서비스 적용&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3단계에 이르는 과정이지만 CDN기업과 협업으로 작업이 이루어지기 때문에 일처리가 쉽지많은 않았다.&lt;br /&gt;또한 메인 서비스와 계열사 까지 총 6가지 정도의 서버를 변경하고 서비스 도메인의 코드 수정까지 많은 번거로움까지 뒤따랐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;덕분에 작업을 거치며 우리 회사의 서비스 과정과 CDN 처리 방식 그리고 서비스와 서버 연결 상태 등 전체적인 관계를 이해할 수 있었던 좋은 기회이며 PHP로 &lt;code&gt;S3 API&lt;/code&gt;를 구현하는 작업이 있어 실력 향상에 도움이 되었다.&lt;/p&gt;</description>
      <author>절취선</author>
      <guid isPermaLink="true">https://languagefight.tistory.com/208</guid>
      <comments>https://languagefight.tistory.com/208#entry208comment</comments>
      <pubDate>Tue, 11 Apr 2023 10:23:08 +0900</pubDate>
    </item>
    <item>
      <title>@Configuration의 어노테이션은 SingleTon 이다.</title>
      <link>https://languagefight.tistory.com/207</link>
      <description>&lt;h1&gt;@Configuration의 어노테이션은 SingleTon 이다.&lt;/h1&gt;
&lt;h2&gt;@Component 와 @Configuration 의 차이부터 알고 시작하자.&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;@Component&lt;/code&gt;는 스프링이 가지는 특징인 &lt;code&gt;DI&lt;/code&gt;의 특성을 보여주는 어노테이션 일것이다.&lt;/p&gt;
&lt;p&gt;이전까지 XML 파일에 개발자가 클래스 혹은 메소드를 &lt;code&gt;Bean&lt;/code&gt;으로서 직접 주입해 주었다면 &lt;code&gt;@Component&lt;/code&gt; 어노테이션의 등장으로&lt;br&gt;개발자가 직접 XML파일에 직접 작성하는 과정을 생략할 수 있었다.&lt;/p&gt;
&lt;p&gt;스프링2.5 버전에서 추가된 이 버전은 @Controller, @Service, @Repository 어노테이션에 기본 내장되어있어&lt;br&gt;우리가 위 3가지를 클래스 상단에 추가해주기 때문에 &lt;code&gt;@Component&lt;/code&gt; 를 선언 없이도 Bean으로서 사용할 수 있었던 것이다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;code&gt;@Configuration&lt;/code&gt;은 어떨까?&lt;br&gt;스프링 3.0 에서 추가된 이 어노테이션 또한 메소드를 Bean으로서 자동 등록시켜주는 편리한 기능을 가지고 있다.&lt;/p&gt;
&lt;p&gt;동일한 기능을 보이는 &lt;code&gt;@Component&lt;/code&gt; 와 &lt;code&gt;@Configuration&lt;/code&gt; 의 차이는 기반에서부터 차이를 보인다.&lt;br&gt;&lt;code&gt;@Component&lt;/code&gt; 는 일반적인 스프링 빈을 나타내고, &lt;code&gt;@Configuration&lt;/code&gt; 은 자바 기반의 스프링 구성 클래스를 나타낸다.&lt;br&gt;즉, 자바 기반으로 작성된 클래스의 경우 &lt;code&gt;@Configuration&lt;/code&gt;를 설정하고 빈으로서 사용될 메소드에 &lt;code&gt;@Bean&lt;/code&gt;을 추가하면&lt;br&gt;컨테이너에 주입하여 관리할 수 있는 것이다. &lt;/p&gt;
&lt;p&gt;또한 &lt;code&gt;@Configuration&lt;/code&gt;은 &lt;code&gt;@Component&lt;/code&gt;을 내장하고 있기 때문에 성격이 비슷한 것이다.&lt;/p&gt;
&lt;h2&gt;@Configuration을 사용 해보자&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Configuration
public class AnnotatedHelloConfig(){
    @Bean
    public AnnotatedHello annotatedHello(){
        return new AnnotatedHello();
    }
}

@Test
public void 테스트1(){
    // 컨텍스트 등록
    ApplicationContext ctx = 
            new AnnotationConfigApplicationContext(AnnotatedHelloConfig.class);
    // 등록된 컨텍스트 호출 
    AnnotationHelloConfig config 
            = ctx.getBean(&amp;quot;annotationHelloConfig&amp;quot;, AnnotatedHelloConfig.class);
    // 결과 1
    assertThat(config, is(notNullValue())); // True
    // 결과2
    assertThat(config.annotatedHello(), is(not(sameInstance(hello)))); // False
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;결과 1 확인&lt;/h3&gt;
&lt;p&gt;당연하지만 @Configuration을 통한 빈 등록은 성공적이다.&lt;/p&gt;
&lt;h3&gt;결과 2 확인&lt;/h3&gt;
&lt;p&gt;JAVA를 공부했다면 &lt;code&gt;new&lt;/code&gt;를 통해 획득한 인스턴스 는 동일한 클래스라도 서로 다를 것이다.&lt;br&gt;그러나 결과 2에서 보여주듯 두 인스턴스는 동일함을 보여주는 테스트 결과를 보여준다.&lt;/p&gt;
&lt;h2&gt;호출된 메소드는 왜 동일한 인스턴스일까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;@Bean&lt;/code&gt;이 붙은 어노테이션이 annotatedHello() 메소드를 빈으로 생성할 때 가져갈 수 있는 정보는 무엇일까?&lt;/p&gt;
&lt;p&gt;우리도 확인 할 수 있듯이 &lt;code&gt;클래스의 종류&lt;/code&gt;, &lt;code&gt;메소드 이름&lt;/code&gt; 이다.&lt;br&gt;그리고 빈으로서 등록되기 위해서는 &lt;code&gt;메타 정보&lt;/code&gt;가 필요하지만 정보가 없을 경우 디폴트 값으로 입력된다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;@Configuration&lt;/code&gt;으로 호출된 메소드가 동일한 인스턴스인 이유를 여기서 확인 할 수 있다.&lt;br&gt;스프링은 @Bean 애노테이션을 이용해 빈을 등록할 때 빈 메타정보를 &lt;code&gt;싱글톤(Singleton) 스코프&lt;/code&gt;로 등록한다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Singleton&lt;/code&gt;은 객체 지향 프로그래밍에서 디자인 패턴 중 하나로, 해당 클래스의 인스턴스가 오직 하나만 존재하도록 보장하는 패턴이다.&lt;br&gt;다시 말해, 2개 이상의 인스턴스의 생성이 불가능하며 한번 호출하면 동일한 인스턴스를 사용해야 하는 것이다.&lt;/p&gt;
&lt;h2&gt;어떻게 구분해서 사용할까?&lt;/h2&gt;
&lt;p&gt;JAVA 기반의 &lt;code&gt;@Configuration&lt;/code&gt;은 Bean 스캐닝을 통한 인식으로는 등록하기 힘든 서비스 기능 혹은 컨테이너 XML 없이 등록하려고 할때&lt;br&gt;유용하게 쓰일 수 있다.&lt;br&gt;또한 싱글톤의 성격을 보여주기 때문에 역으로 생각한다면 &lt;strong&gt;한번의 호출로 서비스 전역에서 사용할 수 있는 장점을 가진다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;ChatGPT를 통해 해당 어노테이션의 사용 예시를 질문한 결과는 다음과 같다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;. 데이터베이스 연결 설정
데이터베이스 연결에 필요한 DataSource, EntityManagerFactory 등의 빈을 설정할 수 있습니다.

. 스프링 시큐리티 설정
스프링 시큐리티에서 필요한 빈들을 설정할 수 있습니다. 예를 들어, AuthenticationManager, UserDetailsService 등을 설정할 수 있습니다.

. 캐시 설정
스프링 캐시 추상화를 이용하여 캐시 설정을 할 수 있습니다. 예를 들어, EhCache, Redis 등의 캐시 구현체를 설정할 수 있습니다.

. 웹 애플리케이션 설정
스프링 웹 애플리케이션에서 필요한 빈들을 설정할 수 있습니다. 예를 들어, DispatcherServlet, HandlerMapping, ViewResolver 등을 설정할 수 있습니다.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;그리고 &lt;code&gt;@Component&lt;/code&gt;의 경우에는 다음과 같다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;. 서비스(Service) 클래스

. 리포지토리(Repository) 클래스

. 컨트롤러(Controller) 클래스&lt;/code&gt;&lt;/pre&gt;</description>
      <category>SPRING  /Basic</category>
      <category>@configuration</category>
      <category>singleton</category>
      <category>Spring</category>
      <author>절취선</author>
      <guid isPermaLink="true">https://languagefight.tistory.com/207</guid>
      <comments>https://languagefight.tistory.com/207#entry207comment</comments>
      <pubDate>Sat, 1 Apr 2023 20:13:55 +0900</pubDate>
    </item>
    <item>
      <title>SUBQUERY와 JOIN 무엇을 선택해야할까?</title>
      <link>https://languagefight.tistory.com/206</link>
      <description>&lt;h1&gt;database 에서 두개의 테이블 사이에 데이터 검색 방법&lt;/h1&gt;
&lt;p&gt;쿼리를 다루다보면 두가지 테이블을 연관지어 데이터를 조회할 때가 있는데&lt;br&gt;PROGRAM_INFO 와 NEWS_01 테이블의 연관관계가 외래키 &lt;code&gt;idx&lt;/code&gt;로 묶여있다면 프로그램이 가진 뉴스 콘텐츠를&lt;br&gt;쉽게 확인할 수 있을것이다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-mysql-sql&quot;&gt;-- SUBQUERY 방식
select *
from NEWS_01 a, PROGRAM_INFO b 
where (select count(join_key) from ATTACH where join_key=a.join_key)=0

-- JOIN 방식
select *
from NEWS_01 a join PROGRAM_INFO b ON a.m_cd=b.m_cd
where (SELECT COUNT(*) from NEWS_01 a right OUTER JOIN ATTACH b ON a.join_key=b.join_key WHERE b.join_key IS NULL)=0;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;두 쿼리는 동일한 테이블을 생성해 내지만 우리는 쿼리의 특성을 확인해야한다.&lt;/p&gt;
&lt;h2&gt;SUBQUERY : 하위질의 / 중첩질문&lt;/h2&gt;
&lt;p&gt;본래 쿼리는 &lt;code&gt;질문&lt;/code&gt;의 의미를 가지고 있다.&lt;br&gt;그런데 &lt;code&gt;SUB&lt;/code&gt;이라는 접두사가 붙는다면 &lt;strong&gt;중첩 질문&lt;/strong&gt; 으로 봐야한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;즉, 질문안에 질문인데 사람이 연속적인 질문에 머리를 써야하듯 컴퓨터도 연산처리가 필요하다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;위 쿼리를 말로 옮긴다면 다음과 같이 되지 않을까?&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;PROGRAM &lt;strong&gt;중에&lt;/strong&gt; NEWS 콘텐츠 &lt;strong&gt;중에&lt;/strong&gt; 첨부파일이 없는 것을 보여줘 &lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;JOIN : 집합&lt;/h2&gt;
&lt;p&gt;우리가 join은 배울때 집합을 빼 놓을 수 없는데 실제 작동방식 또한 이와 비슷하기 때문이다.&lt;/p&gt;
&lt;p&gt;위 쿼리를 말로 옮긴다면 다음과 같이 될 것이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;PROGRAM과 NEWS의 데이터의 m_cd 교집합에서 NEWS과 ATTACH의 차집합인 것&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;1만 개의 데이터를 대상으로 두 방식을 비교하면 &lt;code&gt;SUBQUERY&lt;/code&gt; 는 1초 &lt;code&gt;JOIN&lt;/code&gt;은 0.5초의 작업시간을 보여주었다.&lt;/p&gt;
&lt;p&gt;정리하면 두 쿼리 모두 같은 목적을 가지지만 작동 방식에서 차이를 보이며 처리시간또한 큰 차이를 보여주었다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;SUBQUERY&lt;/code&gt;의 경우 적은 데이터 테이블에서 우수한 쿼리 속도를 보여주며&lt;br&gt;&lt;code&gt;JOIN&lt;/code&gt;은 상대적으로 대량의 데이터에서 빠르게 작동했다.&lt;/p&gt;
&lt;p&gt;만약 실무에서 게시판 혹은 외래키로 구성되어 있는 여러 테이블을 사용한다면 두 쿼리로 모두 작성해보고&lt;br&gt;좋은 결과를 보여주는 쿼리문으로 선택해야할 것이다.&lt;/p&gt;
&lt;h3&gt;CHAT_GPT의 의견&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;join문은 두 개 이상의 테이블을 연결하여 하나의 결과 집합을 생성합니다. 
이 방법은 데이터 양이 많고 인덱스가 적절하게 구성되어 있는 경우 매우 빠를 수 있습니다. 
join문은 일반적으로 데이터를 처리하는 데 필요한 시간이 적으므로 대규모 데이터베이스에서 자주 사용됩니다.

반면에 subquery는 하위 질의라고도 불리며, 다른 질의 결과를 필터링하거나 참조하는 방법입니다. 
subquery를 사용하면 중첩된 질의를 작성할 수 있으므로 데이터 양이 적은 경우 유용합니다. 
그러나 subquery는 데이터 양이 많아질수록 성능이 저하될 가능성이 있습니다. 
특히 subquery가 매우 복잡하거나 인덱스가 부적절하게 구성되어 있는 경우 이러한 문제가 발생할 수 있습니다.&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;서브 쿼리를 조인으로 대체할 수 없는 경우&lt;/h2&gt;
&lt;p&gt;1) GROUP BY 를 사용한 서브 쿼리가 FROM 절에 있을 때&lt;br&gt;2) 집계된 값을 반환하는 서브 쿼리가 WHERE 절에 있을 때&lt;br&gt;3) 서브 쿼리가 ALL 연산자에 있을 때&lt;/p&gt;
&lt;p&gt;대체할 수 없는 위 3가지 공통점은 테이블 형식이 아닌 정수 혹은 참/거짓 처럼 단순 데이터라는 것이다.&lt;/p&gt;
&lt;p&gt;join은 두 테이블 사이의 연관관계에서 집합을 찾아내는 구조이므로 테이블 형식이 아닌 위 방식들은 사용할 수 없는 것이다. &lt;/p&gt;</description>
      <category>DataBase</category>
      <category>DATABASE</category>
      <author>절취선</author>
      <guid isPermaLink="true">https://languagefight.tistory.com/206</guid>
      <comments>https://languagefight.tistory.com/206#entry206comment</comments>
      <pubDate>Wed, 15 Mar 2023 21:51:08 +0900</pubDate>
    </item>
    <item>
      <title>AJAX vs CURL</title>
      <link>https://languagefight.tistory.com/205</link>
      <description>&lt;h1&gt;AJAX vs CURL&lt;/h1&gt;
&lt;h2&gt;원문&lt;/h2&gt;
&lt;p&gt;cURL is a server-side process.&lt;br&gt;This means that it will be called before the page is rendered and has nothing to do with the client&amp;#39;s capabilities.&lt;/p&gt;
&lt;p&gt;AJAX, however, is a client-side call.&lt;br&gt;This means that it will not be executed until the client loads the page&lt;br&gt;(or at least that piece of code is seen and executed, but this typically works on document.ready).&lt;/p&gt;
&lt;p&gt;If you&amp;#39;re looking to retrieve the information and dump it to the user immediately then cURL is your best bet.&lt;br&gt;If you&amp;#39;d like to do a progressive load (dump the page, then retrieve the content for a &amp;quot;seamless&amp;quot; load to the user)&lt;br&gt;then AJAX is the best bet. All th while keep in mind, though in today&amp;#39;s day and age it&amp;#39;s semi trivial,&lt;br&gt;AJAX may be disabled in cases of FireFox&amp;#39;s NoScript extension.&lt;/p&gt;
&lt;p&gt;That being said, the source of the cURL execution will be on the server.&lt;br&gt;The source of the AJAX request will be on a per-client basis. Neither of which provide a secure means of detection&lt;br&gt;(server-side) to know who sent what (as headers can be altered).&lt;/p&gt;
&lt;h2&gt;기본부터 알고 가자&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDklcb%2FbtrmALodDSf%2F1sFiV7OwIjU4207SKia3Q0%2Fimg.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;AJAX는 &lt;code&gt;클라이언트 사이드&lt;/code&gt;의 호출 방식이며 자바스크립트 기반으로 서버와 통신을 하기 위한 비동기 통신방법이다.&lt;/p&gt;
&lt;p&gt;cURL(client-URL)은 C언어 기반의 &lt;code&gt;data transfer tool&lt;/code&gt; 로서 라이브러리 형태로 제공된다.&lt;/p&gt;
&lt;p&gt;또한 &lt;code&gt;서버 사이드&lt;/code&gt; 호출을 기반으로 &lt;code&gt;커맨드 라인환경(SHELL)&lt;/code&gt;에서 또한 사용할 수 있어 환경에 상관없이 사용이 가능하다.&lt;/p&gt;
&lt;h2&gt;cURL 사용가능 언어&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;서버 사이드&lt;/code&gt; 통신 방식의 cURL은 이에 대응하여 서버 사이드 스크립트 언어(PHP, JAVA, Python 등)에서 사용 가능하다.&lt;/p&gt;
&lt;p&gt;순수 JS는 클라이언스 스크립트언어도 AJAX 통신 방법을 가지고 있지만 &lt;/p&gt;
&lt;p&gt;Node.js 런타임 엔진을 사용하여 서버 사이드를 구축한다면 cURL을 사용할 수도 있을 것이다.&lt;/p&gt;
&lt;h3&gt;cURL은 비동기 통신이 가능할까?&lt;/h3&gt;
&lt;p&gt;AJAX는 이름부터 비동기의 성격을 지니고 있지만&lt;br&gt;공식 페이지에도 cURL 비동기에 대한 정보가 없으며 &lt;code&gt;cURL async&lt;/code&gt;라고 검색하면 프로세스를 병렬처리하는 &lt;code&gt;parallel(php 에서는 curl_multi)&lt;/code&gt; 함수만 있을 뿐&lt;br&gt;이것이 정말 비동기 통신이라고 볼 수는 없을 것이다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;mycurl() {
    START=$(date +%s)
    curl -s &amp;quot;http://some_url_here/&amp;quot;$1  &amp;gt; $1.txt
    END=$(date +%s)
    DIFF=$(( $END - $START ))
    echo &amp;quot;It took $DIFF seconds&amp;quot;
}
export -f mycurl

seq 100000 | parallel -j0 mycurl&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;PHP 에서 통신을 위해서는 cURL 사용이 일반적이지만 이 또한 비동기 통신은 아니다.&lt;br&gt;이를 극복하기 위해 스레드를 늘리거나 소켓을 개방하는 다양한 방법을 사용하고있다.&lt;/p&gt;
&lt;p&gt;물론 php 프레임워크(codeIgniter, Laravel)에서는 비동기를 지원하는 듯 하다.&lt;/p&gt;
&lt;h1&gt;reference&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/4775592/ajax-versus-curl&quot;&gt;AJAX vs CURL stackoverflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>ajax</category>
      <category>curl</category>
      <author>절취선</author>
      <guid isPermaLink="true">https://languagefight.tistory.com/205</guid>
      <comments>https://languagefight.tistory.com/205#entry205comment</comments>
      <pubDate>Thu, 16 Feb 2023 02:14:23 +0900</pubDate>
    </item>
    <item>
      <title>검색엔진 변경 후기</title>
      <link>https://languagefight.tistory.com/204</link>
      <description>&lt;h1&gt;검색엔진 변경 후기&lt;/h1&gt;
&lt;p&gt;ZOOM 검색 엔진의 계약이 종료되면서 자체 서버를 통해 검색엔진을 만들어낼 필요성이 생겼다.&lt;/p&gt;
&lt;h2&gt;목표&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;DataBase 프레임워크는 Mysql을 사용하며 서브 엔진으로는 SPINX 를 사용한다.&lt;/li&gt;
&lt;li&gt;스핑크스 검색엔진에 적합하도록 쿼리를 변경하여 사용한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;개선점&lt;/h2&gt;
&lt;h3&gt;DB 접근 횟수 최소화&lt;/h3&gt;
&lt;p&gt;기존 검색 방식에서는 SQL에 접근횟수가 너무 많았다.&lt;/p&gt;
&lt;p&gt;회면에는 5개의 테마가 있으며 각 테마별로 20개의 ROW를 가져와야했는데 사용자가 메인화면에 접속하면 6번의 DB접근을 필요로 한다는 것이다.&lt;/p&gt;
&lt;h3&gt;스핑크스 노이즈 문제 해결&lt;/h3&gt;
&lt;p&gt;스핑크스 엔진의 문제점은 검색 결과의 노이즈 문제이며 이를 해결하기 위해서는 2가지 방식이 존재했다.&lt;/p&gt;
&lt;p&gt;1.N 의 수를 늘리자&lt;br&gt;2.TOKEN을 많이 가져가자 (검색 단어를 많이 입력하자)&lt;/p&gt;
&lt;p&gt;여기서 내가 할 수 있었던 방식은 2번 방식이였다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;검색 LOG DATA&lt;/code&gt;를 통해 UX 를 분석했고 많은 사용자들이 문장보다는 단어 검색을 선호했고&lt;br&gt;90%가 하나의 단어 10% 이상이 두개의 단어를 검색하여 사용한다는 것을 힌트로&lt;/p&gt;
&lt;p&gt;검색 결과를 공백을 기준으로 구분하여 &lt;code&gt;TOKEN&lt;/code&gt;을 늘려 검색 속도와 검색 결과의 정확도가 향상시켜 퍼포먼스를 향상시킬 수 있었다.&lt;/p&gt;
&lt;p&gt;앞으로도 개선해 나갈 문제지만 아쉬웠던 점은 &lt;code&gt;Display Load 속도&lt;/code&gt;가 매우 느리다는 점이였다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;비동기 방식이 아니었고 &lt;/li&gt;
&lt;li&gt;광고를 위한 데이터 소스 로드 속도 문제&lt;/li&gt;
&lt;li&gt;각 계열사는 하나의 검색 페이지를 &lt;code&gt;iframe&lt;/code&gt; 태그로 사용함&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;배운 것&lt;/h2&gt;
&lt;p&gt;다시 초심으로 돌아가 기본부터 다시 시작한 기분이었다.&lt;/p&gt;
&lt;p&gt;어려운거라면 스핑크스 쿼리를 사용해야했다는 점과 처음으로 PHP 코드를 사용하여 작성해야 한다는 것이였지만&lt;/p&gt;
&lt;p&gt;다행이 쿼리는 &lt;code&gt;Mapper 방식&lt;/code&gt;, Viwq는 &lt;code&gt;템플릿 방식&lt;/code&gt;의 개발이어서 내가 할 일은 &lt;code&gt;Service Layer&lt;/code&gt;의  DB 환경을&lt;br&gt;스핑크스 엔진에 적합하도록 변경해주기만 하면되었고 추가적으로 기존 ZOOM API에서 PHP 템플릿만 적용하여 View에 적용하면 되었다.&lt;/p&gt;
&lt;p&gt;검색엔진 프로젝트 하나로 기업이 사용하는 PHP의 코드 스타일과 나의 기본기를 사용할 수 있었던 좋은 기회였다.&lt;/p&gt;</description>
      <author>절취선</author>
      <guid isPermaLink="true">https://languagefight.tistory.com/204</guid>
      <comments>https://languagefight.tistory.com/204#entry204comment</comments>
      <pubDate>Thu, 5 Jan 2023 21:56:17 +0900</pubDate>
    </item>
  </channel>
</rss>