<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>SomeThing, Sum Thing</title>
    <link>https://pcseob.tistory.com/</link>
    <description>코딩 그리고</description>
    <language>ko</language>
    <pubDate>Tue, 30 Jun 2026 11:16:42 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>코딩 잘 할거얌:)</managingEditor>
    <image>
      <title>SomeThing, Sum Thing</title>
      <url>https://tistory1.daumcdn.net/tistory/4729008/attach/4fbfa760502d4dc0a0ee7969466b7f9a</url>
      <link>https://pcseob.tistory.com</link>
    </image>
    <item>
      <title>RAG에 대하여, 벡터 데이터베이스</title>
      <link>https://pcseob.tistory.com/89</link>
      <description>&lt;h1&gt;  벡터 데이터베이스(Vector Database) 쉽게 이해하기&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  벡터? 스칼라? 벡터 데이터베이스?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 &quot;벡터&quot;라는 단어를 들으면 물리에서 배운 &lt;b&gt;크기와 방향을 가진 물리량&lt;/b&gt;이 떠오를 수도 있다. 그렇다면 우리가 다루는 &quot;벡터 데이터베이스&quot;도 그런 개념일까? 처음엔 나도 그렇게 생각했다. 벡터와 스칼라를 떠올리며, 데이터에도 방향성이 존재하는 건가? 라는 생각을 했지만, 여기서 말하는 벡터는 그런 의미가 아니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsPKKr/btsL5BrYqEn/7QNzvTwGKkMY2zBMjIpu50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsPKKr/btsL5BrYqEn/7QNzvTwGKkMY2zBMjIpu50/img.png&quot; data-alt=&quot;개인적으론 이런건줄 알았다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsPKKr/btsL5BrYqEn/7QNzvTwGKkMY2zBMjIpu50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsPKKr%2FbtsL5BrYqEn%2F7QNzvTwGKkMY2zBMjIpu50%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;590&quot; height=&quot;328&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;개인적으론 이런건줄 알았다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;벡터 데이터베이스(Vector Database)&lt;/b&gt;는 데이터를 벡터(숫자의 집합)로 변환하여 저장하고 검색하는 새로운 방식의 데이터베이스다. 기존의 관계형 데이터베이스(RDB)나 NoSQL과는 다르게, &lt;b&gt;키워드가 아닌 의미 기반 검색(Semantic Search)을 가능하게 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이렇게 말하면 여전히 와닿지 않을 수도 있다. 그래서 &lt;b&gt;기존 데이터베이스와 비교해 보면 훨씬 쉽게 이해보도록 하자.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  1. 기존 데이터베이스 vs 벡터 데이터베이스 비교&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  기존 데이터베이스 (RDB, NoSQL)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정보를 &lt;b&gt;문자나 숫자로 저장&lt;/b&gt; &amp;rarr; 예: &lt;b&gt;&quot;사과&quot;&lt;/b&gt;, &lt;b&gt;&quot;가격: 1000원&quot;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;SQL 같은 명령어로 &lt;b&gt;정확하게 일치하는 값&lt;/b&gt;을 찾아야 함 &amp;rarr; 예: &quot;사과&quot;를 검색하면 사과만 나옴&lt;/li&gt;
&lt;li&gt;&lt;b&gt;키워드 기반 검색&lt;/b&gt; &amp;rarr; &quot;사과&quot;라고 검색하면 '사과'가 포함된 결과만 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  벡터 데이터베이스 (Vector DB)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정보를 **벡터(숫자의 집합)**로 변환하여 저장 &amp;rarr; 예: &lt;b&gt;&quot;사과 &amp;rarr; (0.8, 1.2, 0.6, ...)&quot;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;의미 기반 검색 가능&lt;/b&gt; &amp;rarr; 예: &quot;사과&quot;를 검색하면 배, 귤, 포도도 추천!&lt;/li&gt;
&lt;li&gt;키워드가 아니라 &lt;b&gt;문맥과 의미를 기반으로 검색 (Semantic Search)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;즉, 벡터DB는 정확한 키워드를 몰라도 관련된 정보를 찾아준다!&lt;/b&gt;   &lt;b&gt;일반 검색 엔진(네이버, 구글)과 추천 시스템(넷플릭스, 유튜브)의 차이와 비슷함&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;rarr; &lt;b&gt;기존 검색 엔진&lt;/b&gt;: &quot;아이언맨&quot;을 검색하면 영화 아이언맨만 나옴&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;rarr; &lt;b&gt;추천 시스템 (벡터DB 기반)&lt;/b&gt;: &quot;아이언맨&quot;을 검색하면 마블 영화 추천&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;1194&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/94uf2/btsL2RXUnue/4tpkDOgD5BWK5WlHEkXby0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/94uf2/btsL2RXUnue/4tpkDOgD5BWK5WlHEkXby0/img.png&quot; data-alt=&quot;벡터 데이터베이스에서 '이념', '정당' 에 대해 검색하면 이렇게 나오지 않을까..?&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/94uf2/btsL2RXUnue/4tpkDOgD5BWK5WlHEkXby0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F94uf2%2FbtsL2RXUnue%2F4tpkDOgD5BWK5WlHEkXby0%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;718&quot; height=&quot;1194&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;1194&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;벡터 데이터베이스에서 '이념', '정당' 에 대해 검색하면 이렇게 나오지 않을까..?&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  2. 벡터 데이터베이스 활용 예시&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️&amp;zwj;♂️ 예제 1: 네이버/구글 검색 vs AI 검색 차이&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;질문:&lt;/b&gt; &quot;한국에서 가장 높은 산은?&quot;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;일반 DB&lt;/b&gt; &amp;rarr; &quot;한라산&quot;, &quot;백두산&quot; 키워드가 들어간 웹페이지 목록&lt;/li&gt;
&lt;li&gt;&lt;b&gt;벡터 DB&lt;/b&gt; &amp;rarr; &quot;한국에서 제일 높은 산은 백두산이며, 남한에서 제일 높은 산은 한라산입니다.&quot;&lt;/li&gt;
&lt;li&gt;  AI가 문맥을 이해하고 &lt;b&gt;최적의 답변을 제공!&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  예제 2: 넷플릭스/유튜브 추천 시스템&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;검색어:&lt;/b&gt; &quot;액션 영화&quot;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;일반 DB&lt;/b&gt; &amp;rarr; &quot;액션 영화&quot; 키워드가 포함된 동영상 목록&lt;/li&gt;
&lt;li&gt;&lt;b&gt;벡터 DB&lt;/b&gt; &amp;rarr; &quot;빠른 전개&quot;, &quot;격투 장면이 많은 영화&quot; 등 비슷한 특징의 영상 추천&lt;/li&gt;
&lt;li&gt;  AI가 &lt;b&gt;영상의 특징을 분석해서 맞춤 추천&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  예제 3: 온라인 쇼핑몰 상품 추천&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;검색어:&lt;/b&gt; &quot;흰색 운동화&quot;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;일반 DB&lt;/b&gt; &amp;rarr; &quot;흰색 운동화&quot;만 검색됨&lt;/li&gt;
&lt;li&gt;&lt;b&gt;벡터 DB&lt;/b&gt; &amp;rarr; &quot;흰색 스니커즈&quot;, &quot;흰색 런닝화&quot;, &quot;비슷한 스타일의 신발&quot;까지 추천&lt;/li&gt;
&lt;li&gt;  AI가 &lt;b&gt;제품 디자인, 색상, 브랜드까지 고려해서 추천&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  예제 4: AI 챗봇 (RAG)에서 활용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;질문:&lt;/b&gt; &quot;챗봇 만들려면 어떻게 해야 돼?&quot;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;일반 DB&lt;/b&gt; &amp;rarr; &quot;챗봇&quot; 키워드가 포함된 문서 검색&lt;/li&gt;
&lt;li&gt;&lt;b&gt;벡터 DB&lt;/b&gt; &amp;rarr; &quot;FastAPI + GPT-4o + LangChain을 활용한 챗봇 구축법&quot;과 관련된 문서 추천&lt;/li&gt;
&lt;li&gt;  AI가 &lt;b&gt;맥락을 이해하고 최적의 문서 검색&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;❓ 3. 왜 벡터DB가 필요할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;기존 데이터베이스는 정확한 키워드가 없으면 검색이 어렵다!&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;벡터DB는 의미를 이해하고 연관된 정보를 제공한다!&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;LLM과 결합하면 질문에 맞는 데이터를 찾아 AI가 더 정확한 답변을 제공할 수 있다!&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  4. 대표적인 벡터 데이터베이스&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름 특징&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;FAISS (Meta)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;GPU 가속 지원, 빠른 검색 속도&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;ChromaDB&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;LangChain과 호환, Python 친화적&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Pinecone&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;클라우드 기반, 대규모 검색 최적화&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;ChromaDB는 FastAPI와 함께 RAG 시스템 구축에 많이 사용됨&lt;/b&gt;&lt;br /&gt;✅ &lt;b&gt;FAISS는 GPU 지원이 가능해서 대량 데이터 검색에 유리함&lt;/b&gt;&lt;br /&gt;✅ &lt;b&gt;Pinecone은 AWS, GCP와 잘 연동되어 클라우드 기반 벡터 검색에 최적화됨&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  5. 결론&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;벡터 데이터베이스는 데이터를 벡터(숫자 좌표)로 변환하여 저장하는 DB&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;기존 키워드 검색과 달리 의미를 기반으로 유사한 데이터를 찾아준다&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;AI 챗봇, 검색 엔진, 추천 시스템, 문서 검색 등에 활용&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;RAG 시스템에서 벡터DB를 사용하면 더 정확한 검색과 응답이 가능하다&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;쉽게 말하면, 벡터DB는 &quot;검색을 유연하게 만들어 주는 AI&quot; 역할이라고 생각하면 좋다.&lt;/b&gt;&lt;/p&gt;</description>
      <category>Study/기타</category>
      <category>Ai</category>
      <category>Database</category>
      <category>rag</category>
      <category>vector</category>
      <category>벡터데이터베이스</category>
      <author>코딩 잘 할거얌:)</author>
      <guid isPermaLink="true">https://pcseob.tistory.com/89</guid>
      <comments>https://pcseob.tistory.com/89#entry89comment</comments>
      <pubDate>Sat, 1 Feb 2025 20:14:29 +0900</pubDate>
    </item>
    <item>
      <title>RAG 기반 AI 서버 구축: 기초지식 다지기</title>
      <link>https://pcseob.tistory.com/88</link>
      <description>&lt;h1&gt;RAG 기반 AI 서버 구축: 도구들과 기초지식 다지기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 포스팅한 내용을 생각하며 기초지식을 다져보도록 하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;225&quot; data-origin-height=&quot;225&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/B0SE1/btsL5zAUTf2/roGJx4pvjmOG5V8ejTpVw1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/B0SE1/btsL5zAUTf2/roGJx4pvjmOG5V8ejTpVw1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/B0SE1/btsL5zAUTf2/roGJx4pvjmOG5V8ejTpVw1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FB0SE1%2FbtsL5zAUTf2%2FroGJx4pvjmOG5V8ejTpVw1%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;225&quot; height=&quot;225&quot; data-origin-width=&quot;225&quot; data-origin-height=&quot;225&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  1. 목표&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 이야기했듯이 &lt;b&gt;AI 서버 구축&lt;/b&gt;을 진행할 것이다. 일반 서버 내부에 AI 서버를 함께 구축하면 &lt;b&gt;의존성이 증가&lt;/b&gt;하고, &lt;b&gt;모듈화가 어렵고&lt;/b&gt;, 하나의 서버 장애가 모든 시스템에 영향을 미치는&amp;nbsp;문제가 발생할 가능성이 크다. 이를 방지하고 &lt;b&gt;책임 분리&lt;/b&gt;를 명확히 하기 위해 별도의 AI 서버를 구축하는 방향을 선택했다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ 2. 구축할 아키텍처&amp;nbsp;&lt;/h2&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;(일반 서버: FastAPI) ───&amp;gt; (AI 서버: FastAPI + vLLM + RAG)
                        ├── LLM
                        ├── 벡터DB
                        ├── 임베딩 모델&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 아키텍처로 구성할 예정이다. 이는 &lt;b&gt;AI 서버를 독립적으로 운영&lt;/b&gt;하면서도 일반 서버와의 원활한 통신을 유지할 수 있도록 설계하였다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;⚙️ 3. 사용할 개발 환경&amp;nbsp;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;RunPod&lt;/b&gt; &amp;rarr; GPU 서버 제공&lt;/li&gt;
&lt;li&gt;&lt;b&gt;vLLM&lt;/b&gt; &amp;rarr; LLM 서빙 최적화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DeepSeek LLM&lt;/b&gt; &amp;rarr; 실제 추론을 담당할 모델&lt;/li&gt;
&lt;li&gt;&lt;b&gt;RAG (Retrieval-Augmented Generation)&lt;/b&gt; &amp;rarr; AI 답변의 정확도를 향상시키는 검색 기반 기술&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ChromaDB (or FAISS)&lt;/b&gt; &amp;rarr; 벡터 검색을 위한 데이터베이스&lt;/li&gt;
&lt;li&gt;&lt;b&gt;FastAPI&lt;/b&gt; &amp;rarr; REST API로 AI 응답을 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  4. Wls(찐) 기초지식들  &lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  LLM이란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM(Large Language Model)은 대량의 데이터를 학습한 대규모 언어 모델로, 텍스트 기반의 다양한 작업을 수행할 수 있도록 설계된 AI 모델이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  RAG이란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAG(Retrieval-Augmented Generation)는 검색 기반의 문서 검색 시스템과 LLM을 결합하여 보다 정확하고 신뢰할 수 있는 AI 응답을 생성하는 기술이다. 기존의 LLM 단독 사용과 달리, 외부 데이터를 검색하여 활용하므로 최신 정보에 대응할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ 파인튜닝이란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파인튜닝(Fine-Tuning)은 이미 학습된 모델을 특정 도메인이나 데이터셋에 맞춰 추가 학습하는 과정이다. 이를 통해 특정한 업무나 산업에 맞는 최적화된 모델을 만들 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;⚔️ RAG vs 파인튜닝&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  RAG (Retrieval-Augmented Generation)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;외부 지식 활용&lt;/b&gt;: 모델이 자체적으로 알고 있는 정보를 넘어서, 외부 데이터베이스에서 실시간 정보를 검색하여 활용.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;도메인별 학습 불필요&lt;/b&gt;: 새로운 정보가 필요할 때마다 모델을 다시 학습시킬 필요 없이, 데이터를 업데이트하면 됨.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;빠른 응답 가능&lt;/b&gt;: 모델이 직접 모든 정보를 학습하는 것이 아니라 필요한 순간에 검색하여 응답 생성.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;비용 절감&lt;/b&gt;: 모델을 반복적으로 재학습할 필요가 없으므로, 유지보수 비용이 낮음.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예제&lt;/b&gt;: 고객 지원 챗봇이 최신 정책을 실시간으로 검색하여 제공하는 경우.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  파인튜닝 (Fine-Tuning)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;내부 지식 강화&lt;/b&gt;: 특정 데이터셋으로 모델을 추가 학습하여 특정 도메인에 최적화된 모델 생성.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;재학습 필요&lt;/b&gt;: 새로운 정보를 학습하려면 기존 모델을 다시 학습시켜야 함. &lt;b&gt;(많은 시간과 비용 발생)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;빠른 추론 가능&lt;/b&gt;: 검색 단계를 거치지 않고 모델 자체가 모든 지식을 포함하고 있으므로 응답 속도가 빠름.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;고정된 데이터 의존&lt;/b&gt;: 학습 이후 변경된 정보는 반영되지 않기 때문에 정기적인 재학습이 필요함.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예제&lt;/b&gt;: 의료 데이터로 학습된 모델이 특정 질병에 대한 정확한 정보를 제공하는 경우.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  RAG를 선택한 이유&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;RAG를 선택하기로 했다&lt;/b&gt;&amp;nbsp;왜냐하면&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;외부 지식 활용&lt;/b&gt;이 가능하기 때문에, 최신 정보를 즉시 반영할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;비용 절감&lt;/b&gt;이 된다. 파인튜닝처럼 모델을 계속 재학습할 필요가 없어 유지보수 비용이 적게 든다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;파인튜닝의 가장 큰 단점은 재학습 필요&lt;/b&gt;인데, 이 과정에서 &lt;b&gt;많은 시간과 비용이 발생&lt;/b&gt;한다. 모델을 업데이트하려면 새로운 데이터로 다시 학습해야 하고, 이 과정에서 높은 GPU 자원이 소모된다. RAG는 단순히 데이터베이스를 업데이트하는 방식으로 새로운 정보를 반영할 수 있어 훨씬 효율적이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로,&amp;nbsp;지속적으로&amp;nbsp;변화하는&amp;nbsp;데이터를&amp;nbsp;다뤄야&amp;nbsp;하는&amp;nbsp;환경에서는&amp;nbsp;RAG가&amp;nbsp;가장&amp;nbsp;효과적인&amp;nbsp;선택이다.&amp;nbsp;특히&amp;nbsp;혼자&amp;nbsp;개발을&amp;nbsp;진행하는&amp;nbsp;상황에서&amp;nbsp;비용과&amp;nbsp;유지보수&amp;nbsp;부담을&amp;nbsp;최소화하는&amp;nbsp;것이&amp;nbsp;중요하기&amp;nbsp;때문에,&amp;nbsp;파인튜닝보다&amp;nbsp;효율적인&amp;nbsp;RAG를&amp;nbsp;선택하여&amp;nbsp;구현해보도록&amp;nbsp;하겠다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;974&quot; data-origin-height=&quot;1088&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBDZsg/btsL3jzYuvy/lXjUF48IJLSKt0KxZNl9dk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBDZsg/btsL3jzYuvy/lXjUF48IJLSKt0KxZNl9dk/img.png&quot; data-alt=&quot;굳이 어렵겐...가지말자&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBDZsg/btsL3jzYuvy/lXjUF48IJLSKt0KxZNl9dk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBDZsg%2FbtsL3jzYuvy%2FlXjUF48IJLSKt0KxZNl9dk%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;587&quot; height=&quot;656&quot; data-origin-width=&quot;974&quot; data-origin-height=&quot;1088&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;굳이 어렵겐...가지말자&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  5. 요구되는 기술 스택 및 예상 기술 요소  &lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;백엔드&lt;/b&gt;: Python, FastAPI, vLLM&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터베이스&lt;/b&gt;: ChromaDB, FAISS (벡터 DB)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;AI 모델&lt;/b&gt;: DeepSeek LLM, all-MiniLM-L6-v2 (임베딩 모델)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;서버 환경&lt;/b&gt;: RunPod (GPU 인프라), Docker (컨테이너화)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;검색 최적화&lt;/b&gt;: RAG 기반 검색 시스템&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 기술 스택을 활용하여 AI 서버 구축을 진행할 예정이며, 이후 상세한 구현 방법을 다룰 계획이다.&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/기타</category>
      <category>Ai</category>
      <category>AI서버</category>
      <category>llm</category>
      <category>rag</category>
      <category>파인튜닝</category>
      <author>코딩 잘 할거얌:)</author>
      <guid isPermaLink="true">https://pcseob.tistory.com/88</guid>
      <comments>https://pcseob.tistory.com/88#entry88comment</comments>
      <pubDate>Sat, 1 Feb 2025 15:43:50 +0900</pubDate>
    </item>
    <item>
      <title>App 개발자의 우당탕탕 AI서버 만들기</title>
      <link>https://pcseob.tistory.com/87</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;앱 개발자에서 AI서버를 만들려고 마음먹은 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 기술이 너무나도 빠르게 발전하면서, 내가 현실 세계에서 어떤 자세를 취해야 하는지 고민하게 되었다. 평생 Flutter 앱 개발만으로 먹고살기는 어렵다는 걸 진작에 알고 있었다. 실제로 나보다 10~20살 많은 개발자들을 보면, 하나의 프레임워크로만 일을 지속한 경우는 거의 없었다. 그렇다면 나는 어떤 기술을 함께 익혀야 할까? 그 고민이 시작이었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;620&quot; data-origin-height=&quot;399&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgOvWb/btsL3008BW9/S16khBaSZkjGmHZHYk72jK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgOvWb/btsL3008BW9/S16khBaSZkjGmHZHYk72jK/img.jpg&quot; data-alt=&quot;세상의 발전이 너무 빨라요&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgOvWb/btsL3008BW9/S16khBaSZkjGmHZHYk72jK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgOvWb%2FbtsL3008BW9%2FS16khBaSZkjGmHZHYk72jK%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;463&quot; height=&quot;298&quot; data-origin-width=&quot;620&quot; data-origin-height=&quot;399&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;세상의 발전이 너무 빨라요&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국, 이 세상에서 개발자로 살아남기 위해 가장 중요한 것은 실력뿐만 아니라 &lt;b&gt;그 기술의 수요&lt;/b&gt;라고 생각했다. 앞으로 AI 기술의 수요는 꾸준할 것이고, 앱 개발은 특정 플랫폼의 프로그램을 만드는 데 필수적인 기반 기술이 될 것이라 판단했다. 그렇다면 AI를 활용하는 방법을 익혀야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러던 와중 &lt;b&gt;DeepSeek&lt;/b&gt;이 2025년 1월 20일에 발표되면서 엄청난 파장을 일으켰다. 특히 오픈소스로 사용할 수 있다는 점이 큰 매력으로 다가왔다. 이 기회를 활용해 &lt;b&gt;내가 직접 AI서버를 구축해 보고, 내가 원하는 기능을 구현해 보자&lt;/b&gt;는 결심을 하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;나의 목표는 AI 서버를 구축하여 API로 Flutter와 연동하는 것이다. 단순한 AI 응답을 받는 것이 아니라, 실제로 앱 내부의 데이터와 연결하여 사용자에게 더욱 맞춤형 AI 경험을 제공하는 것이 궁극적인 목표다. 이를 위해 AI의 다양한 기능을 학습하고, 실제 애플리케이션에 적용해 나갈 예정이다.&lt;/p&gt;</description>
      <category>Study/기타</category>
      <category>deepseek</category>
      <category>Flutter</category>
      <category>앱개발</category>
      <author>코딩 잘 할거얌:)</author>
      <guid isPermaLink="true">https://pcseob.tistory.com/87</guid>
      <comments>https://pcseob.tistory.com/87#entry87comment</comments>
      <pubDate>Sat, 1 Feb 2025 13:51:30 +0900</pubDate>
    </item>
    <item>
      <title>새로운 AI DeepSeek에 대하여</title>
      <link>https://pcseob.tistory.com/86</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요! 설 연휴 잘 보내고 계시나요?  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설 연휴 동안 엄청난 핫 이슈, &lt;b&gt;DeepSeek R1&lt;/b&gt;의 등장으로 꽤나 시끌시끌했는데요, 그 내용을 바탕으로 제가 알기 쉽게 정리해봤습니다!&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;DeepSeek란? 새로운 AI 모델의 등장&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 챗봇 하면 가장 먼저 떠오르는 것은 아마도 OpenAI의 ChatGPT일 것입니다. 하지만 최근 &lt;b&gt;DeepSeek&lt;/b&gt;이라는 새로운 AI 모델이 등장하며 많은 주목을 받고 있습니다. DeepSeek은 ChatGPT와 마찬가지로 대화를 할 수 있는 AI이지만, 학습 방식과 활용 방식에서 차이를 보입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DeepSeek이 화제가 된 이유 중 하나는 &lt;b&gt;미국의 대중국 AI 제재에도 불구하고 높은 성능을 보여주었다는 점&lt;/b&gt;입니다. 미국은 AI 러닝에 필수적인 &lt;b&gt;엔비디아(NVIDIA) GPU&lt;/b&gt;의 중국 수출을 제한했음에도 불구하고, 이를 극복하며 DeepSeek AI 모델을 탄생시켰습니다. DeepSeek 발표된 후, &quot;AI 모델을 학습하는 데 엔비디아의 칩셋이 반드시 필요하지 않을 수도 있다.&quot;는 논의가 이어졌고(사실 엔비디아의 CUDA로 효율적이게 개발을 하는 것이지 아예 개발을 못하는건 아닙니다.), 실제로 이 소식이 전해진 후 &lt;b&gt;엔비디아의 주가가 급락&lt;/b&gt;하는 일까지 벌어졌습니다.&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;(현재에는 엔비디아 칩셋을 쓰고있다. 가 정설입니다.)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;1581&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A4i4T/btsL170vjrb/KrvmqrXovN3jUbf3jRSOvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A4i4T/btsL170vjrb/KrvmqrXovN3jUbf3jRSOvk/img.png&quot; data-alt=&quot;https://finance.yahoo.com/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A4i4T/btsL170vjrb/KrvmqrXovN3jUbf3jRSOvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA4i4T%2FbtsL170vjrb%2FKrvmqrXovN3jUbf3jRSOvk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1170&quot; height=&quot;1581&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;1581&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://finance.yahoo.com/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면, 이런 환경에서도 DeepSeek은 어떻게 학습되었을까요?&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. DeepSeek과 ChatGPT의 학습 방식 차이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DeepSeek과 ChatGPT는 모두 대화형 AI이지만, 학습 방식에서 큰 차이가 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  ChatGPT: 지도 학습 + 강화 학습 (RLHF)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChatGPT는 &lt;b&gt;지도 학습(Supervised Learning)&lt;/b&gt;과 &lt;b&gt;강화 학습(RLHF, Reinforcement Learning from Human Feedback)&lt;/b&gt;을 결합한 방식으로 학습됩니다. 쉽게 이해할 수 있도록 예를 들어보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;gcode&quot;&gt;&lt;code&gt;✅ 한국의 수도는? &amp;rarr; 서울입니다. (정답) &amp;rarr; 학습 완료
❌ 한국의 수도는? &amp;rarr; 워싱턴입니다. (오답) &amp;rarr; 수정 및 재학습
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, ChatGPT는 &lt;b&gt;기존 데이터 학습 + 인간의 피드백을 통한 수정 과정&lt;/b&gt;을 반복하며 답변의 정확도를 높여갑니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pahNC/btsL3nabCQG/XvnCCzPsZA5VgQ5Ckzt1tk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pahNC/btsL3nabCQG/XvnCCzPsZA5VgQ5Ckzt1tk/img.webp&quot; data-alt=&quot;출처 : 무한도전&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pahNC/btsL3nabCQG/XvnCCzPsZA5VgQ5Ckzt1tk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpahNC%2FbtsL3nabCQG%2FXvnCCzPsZA5VgQ5Ckzt1tk%2Fimg.webp&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;483&quot; height=&quot;269&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : 무한도전&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  DeepSeek: 순수 강화 학습 (RL)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 DeepSeek은 &lt;b&gt;순수한 강화 학습&lt;/b&gt; 방식을 채택합니다. 예를 들어, AI에게 다음과 같은 수학 문제를 주었다고 가정해 봅시다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;3x^2 + 100 = 127일 때, x는 얼마인가?
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DeepSeek은 처음에는 무작위로 답변을 내놓습니다. 하지만 정답인 x = 3을 도출할 때 보상을 주는 방식을 통해, 시행착오를 거치면서 점점 더 정확한 답을 찾게 됩니다. 즉, DeepSeek은&lt;b&gt;&amp;nbsp;스스로 문제 해결 능력을 발전시키는 AI 모델&lt;/b&gt;이라고 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;651&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PQaOL/btsL2KDDStJ/emfHRHWf0IdDfK2esnXfqk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PQaOL/btsL2KDDStJ/emfHRHWf0IdDfK2esnXfqk/img.jpg&quot; data-alt=&quot;당근으로 채찍을&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PQaOL/btsL2KDDStJ/emfHRHWf0IdDfK2esnXfqk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPQaOL%2FbtsL2KDDStJ%2FemfHRHWf0IdDfK2esnXfqk%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;476&quot; height=&quot;387&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;651&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;당근으로 채찍을&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 특히 논리적 사고와 문제 해결 능력을 요구하는 작업에서 강력한 성능을 발휘할 가능성이 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. DeepSeek의 강점&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 1) 오픈 소스 기반의 자유로운 활용 (MIT 라이선스)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DeepSeek은 &lt;b&gt;MIT 라이선스&lt;/b&gt;로 제공되며, 누구나 무료로 사용할 수 있습니다. MIT 라이선스는 가장 자유로운 오픈 소스 라이선스 중 하나로, 사용자가 코드를 수정하고, 재배포하며, 상업적 목적으로도 활용할 수 있도록 허용합니다. 반면 ChatGPT는 OpenAI의 관리 하에 제한적으로 무료가 되며, 대부분 유료로 서비스가 제공됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오픈 소스 기반인 DeepSeek은 기업과 개발자들이 자신의 필요에 맞게 AI 모델을 커스터마이징할 수 있다는 점에서 큰 장점이 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2730&quot; data-origin-height=&quot;1058&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfRJ5I/btsL2GIaKBZ/PCWtR7k4ojE3kjnV1eWlmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfRJ5I/btsL2GIaKBZ/PCWtR7k4ojE3kjnV1eWlmk/img.png&quot; data-alt=&quot;실제 Github에 MIT License로 등록이 되어있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfRJ5I/btsL2GIaKBZ/PCWtR7k4ojE3kjnV1eWlmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfRJ5I%2FbtsL2GIaKBZ%2FPCWtR7k4ojE3kjnV1eWlmk%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;2730&quot; height=&quot;1058&quot; data-origin-width=&quot;2730&quot; data-origin-height=&quot;1058&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실제 Github에 MIT License로 등록이 되어있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 2) 저렴한 추론 비용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DeepSeek은 &lt;b&gt;추론 비용(Inference Cost)&lt;/b&gt; 측면에서도 큰 강점을 가지고 있습니다. ChatGPT API와 비교했을 때 DeepSeek API의 비용이 훨씬 저렴하며, 대량의 요청을 처리하는 기업과 개발자들에게 유리한 선택지가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ChatGPT API 비용 예시:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;- GPT-4 (입력): $0.03 / 1,000 tokens
- GPT-4 (출력): $0.06 / 1,000 tokens
- 500단어 (750 tokens) 응답 시 비용: $0.0675 / 응답
- 100M (1억) 토큰 사용 시: $9,000 / 월
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;DeepSeek API 비용 예시 (예측치):&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;- 입력: $0.01 / 1,000 tokens
- 출력: $0.01 / 1,000 tokens
- 500단어 (750 tokens) 응답 시 비용: $0.015 / 응답
- 100M (1억) 토큰 사용 시: $2,000 / 월
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 차이로 인해 &lt;b&gt;DeepSeek을 사용하면 연간 최대 $84,000 이상을 절약할 수 있습니다&lt;/b&gt;. 특히 &lt;b&gt;오픈 소스로 제공되기 때문에 자체 서버에 배포하면 API 비용 자체를 없애고 추론비용만 발생할 수 있습니다&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DeepSeek은 &lt;b&gt;저렴한 비용과 오픈 소스 라이선스를 통한 자유로운 활용&lt;/b&gt;이 가능하다는 점에서, AI 모델을 활용하려는 기업과 개발자들에게 매우 매력적인 선택이 될 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. DeepSeek의 활용 방안&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DeepSeek은 MIT 라이선스를 기반으로 무료로 사용할 수 있기 때문에, 다양한 시스템에 적용할 수 있습니다. 예를 들어:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;자체 AI 시스템 구축&lt;/b&gt; &amp;rarr; DeepSeek의 오픈 소스 모델을 활용해 기업이나 연구 기관에서 맞춤형 AI 시스템을 개발할 수 있음.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;회사 내부 로컬 서버에 설치하여 운영&lt;/b&gt; &amp;rarr; DeepSeek을 자체 서버에 배포하여 맞춤형 AI를 튜닝하고 사용할 수 있음.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;성능 테스트 후 다양한 곳에 활용 가능&lt;/b&gt; &amp;rarr; AI의 성능을 비교 분석하고, 특정 작업에 대한 최적의 활용 방안을 찾는 데 도움.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DeepSeek은 아직 발전 중인 모델이지만, 강화 학습 기반의 접근 방식과 오픈 소스 라이선스 덕분에 앞으로 다양한 활용이 가능할 것으로 보입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;로컬에서 DeepSeek 실행해보기.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 내용은 m1 macbook air 에서 실제로 로컬에서 Ollama를 이용하여 실행을 한 결과입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;318&quot; data-origin-height=&quot;230&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NIAmD/btsL2bIxUZR/lIP9FYv8SyAkqLc8DeVZ6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NIAmD/btsL2bIxUZR/lIP9FYv8SyAkqLc8DeVZ6K/img.png&quot; data-alt=&quot;글쓴이의 노트북 스펙&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NIAmD/btsL2bIxUZR/lIP9FYv8SyAkqLc8DeVZ6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNIAmD%2FbtsL2bIxUZR%2FlIP9FYv8SyAkqLc8DeVZ6K%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;318&quot; height=&quot;230&quot; data-origin-width=&quot;318&quot; data-origin-height=&quot;230&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;글쓴이의 노트북 스펙&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ollama.com/library/deepseek-r1:7b&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ollama.com/library/deepseek-r1:7b&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1738225618807&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;deepseek-r1:7b&quot; data-og-description=&quot;DeepSeek's first-generation of reasoning models with comparable performance to OpenAI-o1, including six dense models distilled from DeepSeek-R1 based on Llama and Qwen.&quot; data-og-host=&quot;ollama.com&quot; data-og-source-url=&quot;https://ollama.com/library/deepseek-r1:7b&quot; data-og-url=&quot;https://ollama.com/deepseek-r1:7b&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/FmwRl/hyX7RDoxjf/LbS8Esyj22vZeGLJNvxFm0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/c1uZ62/hyX73DO1IE/mQLQNtVFyhzymOjb6zk6yk/img.png?width=4702&amp;amp;height=2787&amp;amp;face=0_0_4702_2787&quot;&gt;&lt;a href=&quot;https://ollama.com/library/deepseek-r1:7b&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ollama.com/library/deepseek-r1:7b&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/FmwRl/hyX7RDoxjf/LbS8Esyj22vZeGLJNvxFm0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/c1uZ62/hyX73DO1IE/mQLQNtVFyhzymOjb6zk6yk/img.png?width=4702&amp;amp;height=2787&amp;amp;face=0_0_4702_2787');&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;deepseek-r1:7b&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;DeepSeek's first-generation of reasoning models with comparable performance to OpenAI-o1, including six dense models distilled from DeepSeek-R1 based on Llama and Qwen.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ollama.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 링크에서 공개된 Deepseek의 모델을 로컬환경에서 사용할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;948&quot; data-origin-height=&quot;180&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zvAOD/btsL2mbZoLl/SFkN9Mh5vCX0jWwFCKIiz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zvAOD/btsL2mbZoLl/SFkN9Mh5vCX0jWwFCKIiz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zvAOD/btsL2mbZoLl/SFkN9Mh5vCX0jWwFCKIiz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzvAOD%2FbtsL2mbZoLl%2FSFkN9Mh5vCX0jWwFCKIiz0%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;739&quot; height=&quot;140&quot; data-origin-width=&quot;948&quot; data-origin-height=&quot;180&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플러터에 대해서 물어봤을 때 70b(42GB)는 추론이 되지 않았으며(스펙미달로 추정), 14b(9.0GB)는 추론이 가능하였습니다. 아래 영상은 추론 결과입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1392&quot; data-origin-height=&quot;1550&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uSMOk/btsL3XWrScU/CN0tLS4Mk2pt57ompNWAhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uSMOk/btsL3XWrScU/CN0tLS4Mk2pt57ompNWAhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uSMOk/btsL3XWrScU/CN0tLS4Mk2pt57ompNWAhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuSMOk%2FbtsL3XWrScU%2FCN0tLS4Mk2pt57ompNWAhk%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;1392&quot; height=&quot;1550&quot; data-origin-width=&quot;1392&quot; data-origin-height=&quot;1550&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1412&quot; data-origin-height=&quot;1178&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3Obr5/btsL2dFZFGx/o3Bgec0xpYaVT99mYuLu90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3Obr5/btsL2dFZFGx/o3Bgec0xpYaVT99mYuLu90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3Obr5/btsL2dFZFGx/o3Bgec0xpYaVT99mYuLu90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3Obr5%2FbtsL2dFZFGx%2Fo3Bgec0xpYaVT99mYuLu90%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;1412&quot; height=&quot;1178&quot; data-origin-width=&quot;1412&quot; data-origin-height=&quot;1178&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고링크&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Teachfloor&lt;/b&gt; &amp;ndash; &lt;a href=&quot;https://www.teachfloor.com/blog/deepseek-vs-chatgpt&quot;&gt;DeepSeek vs ChatGPT 비교&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DeepSeek과 ChatGPT의 기술적 차이, 성능 및 윤리적 측면 비교&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Creole Studios&lt;/b&gt; &amp;ndash; &lt;a href=&quot;https://www.creolestudios.com/deepseek-vs-chatgpt-cost-comparison/&quot;&gt;DeepSeek vs ChatGPT 비용 비교&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DeepSeek과 ChatGPT의 API 비용 비교 및 확장성 분석&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/deepseek-ai/DeepSeek-R1&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/deepseek-ai/DeepSeek-R1&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>Study/기타</category>
      <category>Ai</category>
      <category>deepseek</category>
      <category>GPT</category>
      <category>llm</category>
      <category>딥시크</category>
      <category>엔비디아</category>
      <category>인공지능</category>
      <category>지피티</category>
      <author>코딩 잘 할거얌:)</author>
      <guid isPermaLink="true">https://pcseob.tistory.com/86</guid>
      <comments>https://pcseob.tistory.com/86#entry86comment</comments>
      <pubDate>Thu, 30 Jan 2025 17:59:49 +0900</pubDate>
    </item>
    <item>
      <title>Flutter의 Clean Architecture 클린아키텍처, 각 Layer에 대하여</title>
      <link>https://pcseob.tistory.com/85</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 배웠던 내용으로 Flutter에 Clean architecture를 적용해 보도록 하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1185&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvT5vg/btsH7l7TlbQ/aTM9a8gW9jYXEqAvFheOX1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvT5vg/btsH7l7TlbQ/aTM9a8gW9jYXEqAvFheOX1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvT5vg/btsH7l7TlbQ/aTM9a8gW9jYXEqAvFheOX1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcvT5vg%2FbtsH7l7TlbQ%2FaTM9a8gW9jYXEqAvFheOX1%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;1185&quot; height=&quot;500&quot; data-origin-width=&quot;1185&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드디어 Layer별 어떤 폴더들을 가지게되는지 설명하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 설명한 &lt;b&gt;OOP(객체 지향 프로그래밍)과 SOLID원칙을 기초로 하여 구현을 하는 게 목표&lt;/b&gt;이다. 하나하나씩 살펴보자.&lt;/p&gt;
&lt;p style=&quot;box-sizing: border-box; border-width: 9px; border-left-style: solid; border-left-color: #000044; padding: 1px 15px; letter-spacing: 1px; margin: 0px 0px; line-height: 1.2;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-size: 25px; font-family: 'Cafe24Ssurround'; font-weight: normal; color: #000044;&quot;&gt;Domain Layer&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Domain Layer는 애플리케이션의 비즈니스 로직을 담고 있는 계층이다. 이 계층은 프레임워크에 독립적이며, 순수한 Dart 코드로 작성되며, 테스트가 용이하고, 다른 기술 스택으로 교체할 때도 영향을 최소화하도록 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Entities&lt;/li&gt;
&lt;li&gt;Use Cases&lt;/li&gt;
&lt;li&gt;Repositories(Interfaces)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Domain Layer에서는 위 3가지 요소들이 포함이 되어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나하나씩 살펴보도록 하자.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Entities&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Entities는 애플리케이션의 주요 비즈니스 객체를 표현한다.&lt;/p&gt;
&lt;pre id=&quot;code_1718880754111&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class User {
  final int id;
  final String name;
  final String email;

  User({required this.id, required this.name, required this.email});
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Use Cases&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;유즈 케이스는 특정 비즈니스 요구사항을 수행하는 단위 작업을 정의하며, 각 유즈 케이스는 하나의 메서드만을 가져야 하며, 비즈니스 로직을 수행한다.&lt;/p&gt;
&lt;pre id=&quot;code_1718880823054&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class GetUserDetails {
  final UserRepository repository;

  GetUserDetails(this.repository);

  Future&amp;lt;User&amp;gt; excute(int userId) {
    return repository.getUserById(userId);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Repositories(Interfaces)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Repositories는 데이터 소스와 상호작용하는 인터페이스를 정의한다. 리포지토리 구현체가 Domain Layer에 종속되지 않도록 해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1718880907625&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;abstract class UserRepository {
  Future&amp;lt;User&amp;gt; getUserById(int id);
  Future&amp;lt;List&amp;lt;User&amp;gt;&amp;gt; getAllUsers();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;box-sizing: border-box; border-width: 9px; border-left-style: solid; border-left-color: #000044; padding: 1px 15px; letter-spacing: 1px; margin: 0px 0px; line-height: 1.2;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-size: 25px; font-family: 'Cafe24Ssurround'; font-weight: normal; color: #000044;&quot;&gt;Data Layer&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;데이터 소스와 레포지토리 구현체를 포함한다. 예를 들어, API 호출이나 로컬 데이터베이스와의 상호작용을 담당하게 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Models&lt;/li&gt;
&lt;li&gt;Repositories&lt;/li&gt;
&lt;li&gt;Data Sources&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Data Layer에서는 위 3가지로 이루어져 있다. 여기서 Domain Layer에서 Repositories가 중복된다고 생각할 수 있지만, Data Layer에서 Repositories는 Domain Layer의 Repositories에서 정의된 것을 구현하고 실제 데이터 소스와 상호작용하는 곳이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Models&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;엔터티를 표현하는 데이터 구조체로, 일반적으로 JSON 등의 데이터를 파싱하고 변환하는 역할을 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1718883710931&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class UserModel {
  final int id;
  final String name;
  final String email;

  UserModel({required this.id, required this.name, required this.email});

  factory UserModel.fromJson(Map&amp;lt;String, dynamic&amp;gt; json) {
    return UserModel(
      id: json['id'],
      name: json['name'],
      email: json['email'],
    );
  }

  Map&amp;lt;String, dynamic&amp;gt; toJson() {
    return {
      'id': id,
      'name': name,
      'email': email,
    };
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Repositories&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Domain Layer에서 정의된 Repositories interface를 구현하여 실제 데이터소스와 상호작용한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718883938633&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mport 'package:dartz/dartz.dart';
import 'package:flutter_clean_architecture/core/error/failures.dart';
import 'package:flutter_clean_architecture/features/user/domain/entities/user.dart';
import 'package:flutter_clean_architecture/features/user/domain/repositories/user_repository.dart';
import 'package:flutter_clean_architecture/features/user/data/datasources/user_remote_datasource.dart';
import 'package:flutter_clean_architecture/features/user/data/datasources/user_local_datasource.dart';
import 'package:flutter_clean_architecture/features/user/data/models/user_model.dart';

class UserRepositoryImpl implements UserRepository {
  final UserRemoteDataSource remoteDataSource;
  final UserLocalDataSource localDataSource;

  UserRepositoryImpl({
    required this.remoteDataSource,
    required this.localDataSource,
  });

  @override
  Future&amp;lt;Either&amp;lt;Failure, User&amp;gt;&amp;gt; getUserById(int id) async {
    try {
      final remoteUser = await remoteDataSource.getUserById(id);
      localDataSource.cacheUser(remoteUser);
      return Right(remoteUser);
    } on Exception {
      try {
        final localUser = await localDataSource.getUserById(id);
        return Right(localUser);
      } on Exception {
        return Left(ServerFailure());
      }
    }
  }

  @override
  Future&amp;lt;Either&amp;lt;Failure, List&amp;lt;User&amp;gt;&amp;gt;&amp;gt; getAllUsers() async {
    try {
      final remoteUsers = await remoteDataSource.getAllUsers();
      localDataSource.cacheUsers(remoteUsers);
      return Right(remoteUsers);
    } on Exception {
      try {
        final localUsers = await localDataSource.getAllUsers();
        return Right(localUsers);
      } on Exception {
        return Left(ServerFailure());
      }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Data Sources&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;실제 데이터소스와 상호작용을 담당하며, API호출하는 Remote부분과 Local부분이 따로 정의된다.&lt;/p&gt;
&lt;pre id=&quot;code_1718884011628&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;abstract class UserRemoteDataSource {
  Future&amp;lt;UserModel&amp;gt; getUserById(int id);
  Future&amp;lt;List&amp;lt;UserModel&amp;gt;&amp;gt; getAllUsers();
}

class UserRemoteDataSourceImpl implements UserRemoteDataSource {
  final http.Client client;

  UserRemoteDataSourceImpl({required this.client});

  @override
  Future&amp;lt;UserModel&amp;gt; getUserById(int id) async {
    final response = await client.get(Uri.parse('https://api.example.com/users/$id'));

    if (response.statusCode == 200) {
      return UserModel.fromJson(json.decode(response.body));
    } else {
      throw Exception('Failed to load user');
    }
  }

  @override
  Future&amp;lt;List&amp;lt;UserModel&amp;gt;&amp;gt; getAllUsers() async {
    final response = await client.get(Uri.parse('https://api.example.com/users'));

    if (response.statusCode == 200) {
      Iterable jsonResponse = json.decode(response.body);
      return List&amp;lt;UserModel&amp;gt;.from(jsonResponse.map((model) =&amp;gt; UserModel.fromJson(model)));
    } else {
      throw Exception('Failed to load users');
    }
  }
}

------------------------------

abstract class UserLocalDataSource {
  Future&amp;lt;UserModel&amp;gt; getUserById(int id);
  Future&amp;lt;List&amp;lt;UserModel&amp;gt;&amp;gt; getAllUsers();
  Future&amp;lt;void&amp;gt; cacheUser(UserModel user);
  Future&amp;lt;void&amp;gt; cacheUsers(List&amp;lt;UserModel&amp;gt; users);
}

class UserLocalDataSourceImpl implements UserLocalDataSource {
  final Database database;

  UserLocalDataSourceImpl({required this.database});

  @override
  Future&amp;lt;UserModel&amp;gt; getUserById(int id) async {
    final List&amp;lt;Map&amp;lt;String, dynamic&amp;gt;&amp;gt; maps = await database.query(
      'users',
      where: 'id = ?',
      whereArgs: [id],
    );

    if (maps.isNotEmpty) {
      return UserModel.fromJson(maps.first);
    } else {
      throw Exception('No user found');
    }
  }

  @override
  Future&amp;lt;List&amp;lt;UserModel&amp;gt;&amp;gt; getAllUsers() async {
    final List&amp;lt;Map&amp;lt;String, dynamic&amp;gt;&amp;gt; maps = await database.query('users');

    return List&amp;lt;UserModel&amp;gt;.from(maps.map((map) =&amp;gt; UserModel.fromJson(map)));
  }

  @override
  Future&amp;lt;void&amp;gt; cacheUser(UserModel user) async {
    await database.insert(
      'users',
      user.toJson(),
      conflictAlgorithm: ConflictAlgorithm.replace,
    );
  }

  @override
  Future&amp;lt;void&amp;gt; cacheUsers(List&amp;lt;UserModel&amp;gt; users) async {
    for (var user in users) {
      await cacheUser(user);
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;box-sizing: border-box; border-width: 9px; border-left-style: solid; border-left-color: #000044; padding: 1px 15px; letter-spacing: 1px; margin: 0px 0px; line-height: 1.2;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-size: 25px; font-family: 'Cafe24Ssurround'; font-weight: normal; color: #000044;&quot;&gt;Presentation Layer&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;UI 관련 코드와 상태 관리를 포함한다. Bloc, Riverpod, Provider, GetX 등 상태 관리 라이브러리를 사용하여 UI와 비즈니스 로직을 연결한다. 여기서 MVC, MVVM, MVP 등 디자인패턴을 적용하는 곳이다.&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Providers&lt;/li&gt;
&lt;li&gt;Pages&lt;/li&gt;
&lt;li&gt;Widgets&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Presentation Layer에서는 위 세 가지로 나눌 수 있다. 물론 MVC, MVVM 등 다른 디자인패턴을 적용하거나 어떤 상태관리를 쓰냐에 따라 구조가 달라질 수 있다. 그래서 여기서는 riverpod을 적용했다는 가정하에 작성하도록 하겠다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Providers&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;상태 관리 및 의존성 주입을 위한 프로바이더들을 정의한다. 어떤 상태관리 및 디자인패턴을 적용했냐에 따라 폴더명이 달라질 수 있고 코드도 달라질 수 있다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1718885320582&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// lib/features/user/presentation/providers/user_providers.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:dartz/dartz.dart';
import 'package:flutter_clean_architecture/features/user/domain/entities/user.dart';
import 'package:flutter_clean_architecture/features/user/domain/usecases/get_user_details.dart';
import 'package:flutter_clean_architecture/core/error/failures.dart';
import 'package:flutter_clean_architecture/features/user/presentation/state/user_state.dart';
import 'package:flutter_clean_architecture/features/user/data/models/user_model.dart';

part 'user_providers.g.dart';

@riverpod
class UserProvider extends _$UserProvider {
  @override
  AsyncValue&amp;lt;User&amp;gt; build() =&amp;gt; const AsyncData(User(id: 0, name: '', email: ''));

  Future&amp;lt;void&amp;gt; loadUser(int userId) async {
    if (state.isLoading) return;

    state = const AsyncLoading();

    try {
      final Either&amp;lt;Failure, User&amp;gt; result = await GetUserDetails().execute(userId);

      result.fold(
        (failure) =&amp;gt; state = AsyncError(failure, StackTrace.current),
        (user) =&amp;gt; state = AsyncData(user),
      );
    } catch (error) {
      state = AsyncError(
        CustomErrorModel.errorModel(
          errorCode: '700',
          errorDescription: 'User provider error $error',
          errorMessage: '사용자 정보를 불러오는데 실패했습니다.',
        ),
        StackTrace.current,
      );
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Pages&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;주요 화면(페이지) 컴포넌트들을 정의하며, 각 페이지는 일반적으로 앱 내의 주요 내비게이션 단위이다.&lt;/p&gt;
&lt;pre id=&quot;code_1718885372120&quot; class=&quot;scala&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// lib/features/user/presentation/pages/user_page.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_clean_architecture/features/user/presentation/providers/user_providers.dart';
import 'package:flutter_clean_architecture/features/user/presentation/widgets/user_details.dart';

class UserPage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, ScopedReader watch) {
    final userState = watch(userStateNotifierProvider);

    return Scaffold(
      appBar: AppBar(
        title: Text('User Details'),
      ),
      body: userState.isLoading
          ? Center(child: CircularProgressIndicator())
          : userState.error != null
              ? Center(child: Text(userState.error!))
              : userState.user != null
                  ? UserDetails(user: userState.user!)
                  : Center(child: Text('No user loaded')),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          context.read(userStateNotifierProvider.notifier).loadUser(1);
        },
        child: Icon(Icons.refresh),
      ),
    );
  }
}

// lib/features/user/presentation/widgets/user_details.dart
import 'package:flutter/material.dart';
import 'package:flutter_clean_architecture/features/user/domain/entities/user.dart';

class UserDetails extends StatelessWidget {
  final User user;

  UserDetails({required this.user});

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        children: [
          Text('ID: ${user.id}'),
          Text('Name: ${user.name}'),
          Text('Email: ${user.email}'),
        ],
      ),
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Widgets&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재사용 가능한 UI 위젯들을 정의한다. 이러한 위젯들은 여러 페이지에서 재사용될 수 있고, UI 구성 요소를 모듈화 하는데 용이하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718885414582&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// lib/features/user/presentation/widgets/user_details.dart
import 'package:flutter/material.dart';
import 'package:flutter_clean_architecture/features/user/domain/entities/user.dart';

class UserDetails extends StatelessWidget {
  final User user;

  UserDetails({required this.user});

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        children: [
          Text('ID: ${user.id}'),
          Text('Name: ${user.name}'),
          Text('Email: ${user.email}'),
        ],
      ),
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;폴더구조&lt;/h4&gt;
&lt;pre id=&quot;code_1718885520257&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;lib/
├── features/
│   ├── user/
│   │   ├── domain/
│   │   │   ├── entities/
│   │   │   │   └── user.dart
│   │   │   ├── usecases/
│   │   │   │   └── get_user_details.dart
│   │   │   └── repositories/
│   │   │       └── user_repository.dart
│   │   ├── data/
│   │   │   ├── models/
│   │   │   │   └── user_model.dart
│   │   │   ├── repositories/
│   │   │   │   └── user_repository_impl.dart
│   │   │   └── datasources/
│   │   │       ├── user_remote_datasource.dart
│   │   │       └── user_local_datasource.dart
│   │   └── presentation/
│   │       ├── providers/
│   │       │   └── user_providers.dart
│   │       ├── pages/
│   │       │   └── user_page.dart
│   │       ├── widgets/
│   │       │   └── user_details.dart
└── core/
    ├── error/
    ├── network/
    └── util/&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;이렇게 폴더구조까지 작성하면 Clean Architecture를 적용할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개인적인 견해&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로 클린아키텍처는 무조건 좋다. 100% 좋다.라고 생각하지는 않는다. 우리가 클린아키텍처를 적용하는 이유는 &lt;b&gt;&quot;장기적인 유지보수 및 확장성&quot;&lt;/b&gt; 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 우리는 개발을 하면 &quot;장기적인 유지보수 및 확장성&quot; 보다 더 우선시 되는 것들이 많다. 예를 들면 단기적인 효율성 및 생산성을 중시하여 빠른 개발을 원하는 경우에는 반복되는 코드들과 boiler plate들이 생성이 되어 오히려 걸림돌이 되는 경우가 발생할 수 있다. 그래서 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;클린아키텍처를 무조건 적용하는 것보다 그에 맞는 상황에 따라 적용하는 것이 가장 중요할 것이다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>Study/기타</category>
      <category>clean architecture</category>
      <category>Flutter</category>
      <category>riverpod</category>
      <category>클린아키텍처</category>
      <category>폴더구조</category>
      <category>플러터</category>
      <author>코딩 잘 할거얌:)</author>
      <guid isPermaLink="true">https://pcseob.tistory.com/85</guid>
      <comments>https://pcseob.tistory.com/85#entry85comment</comments>
      <pubDate>Thu, 20 Jun 2024 21:17:58 +0900</pubDate>
    </item>
    <item>
      <title>22. [Flutter] 상태 관리(Riverpod)를 이용하여 비동기 관리하기</title>
      <link>https://pcseob.tistory.com/84</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 Flutter의 riverpod을 이용하여 비동기관리에 대해서 설명하도록 하겠다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FCvux/btsH64ys01f/JFgvPbpWltOOPbxULRJZ80/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FCvux/btsH64ys01f/JFgvPbpWltOOPbxULRJZ80/img.jpg&quot; data-alt=&quot;내 상태&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FCvux/btsH64ys01f/JFgvPbpWltOOPbxULRJZ80/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFCvux%2FbtsH64ys01f%2FJFgvPbpWltOOPbxULRJZ80%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;800&quot; height=&quot;450&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;450&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;내 상태&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://riverpod.dev/ko/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://riverpod.dev/ko/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1718870024033&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;Riverpod&quot; data-og-description=&quot;어디서나 공유 상태 선언하기 더 이상 main.dart과 UI 파일 사이를 오갈 필요가 없습니다. 공유 상태의 코드를 별도의 패키지에 넣든, 필요한 위젯 바로 옆에 넣든, 테스트 가능성을 잃지 않고 적절&quot; data-og-host=&quot;riverpod.dev&quot; data-og-source-url=&quot;https://riverpod.dev/ko/&quot; data-og-url=&quot;https://riverpod.dev/ko/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/pbZwW/hyWoLSu6JA/6XjL6B4Ck0guqlxpfk48S1/img.png?width=851&amp;amp;height=315&amp;amp;face=0_0_851_315,https://scrap.kakaocdn.net/dn/iuzhs/hyWoD1d2rm/r0UIKxkGuKb6hNO9cxT3rK/img.png?width=851&amp;amp;height=315&amp;amp;face=0_0_851_315,https://scrap.kakaocdn.net/dn/cER8o0/hyWoC85FLG/mVSAzas2TKfwW6hmDgrcMK/img.png?width=2000&amp;amp;height=2000&amp;amp;face=0_0_2000_2000&quot;&gt;&lt;a href=&quot;https://riverpod.dev/ko/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://riverpod.dev/ko/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/pbZwW/hyWoLSu6JA/6XjL6B4Ck0guqlxpfk48S1/img.png?width=851&amp;amp;height=315&amp;amp;face=0_0_851_315,https://scrap.kakaocdn.net/dn/iuzhs/hyWoD1d2rm/r0UIKxkGuKb6hNO9cxT3rK/img.png?width=851&amp;amp;height=315&amp;amp;face=0_0_851_315,https://scrap.kakaocdn.net/dn/cER8o0/hyWoC85FLG/mVSAzas2TKfwW6hmDgrcMK/img.png?width=2000&amp;amp;height=2000&amp;amp;face=0_0_2000_2000');&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;Riverpod&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;어디서나 공유 상태 선언하기 더 이상 main.dart과 UI 파일 사이를 오갈 필요가 없습니다. 공유 상태의 코드를 별도의 패키지에 넣든, 필요한 위젯 바로 옆에 넣든, 테스트 가능성을 잃지 않고 적절&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;riverpod.dev&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;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;box-sizing: border-box; border-width: 9px; border-left-style: solid; border-left-color: #000044; padding: 1px 15px; letter-spacing: 1px; margin: 0px 0px; line-height: 1.2;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-size: 25px; font-family: 'Cafe24Ssurround'; font-weight: normal; color: #000044;&quot;&gt;Riverpod이란?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 들어가기에 앞서 riverpod이 무엇인지 알아보도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/rrousselGit&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/rrousselGit&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1718870135136&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;profile&quot; data-og-title=&quot;rrousselGit - Overview&quot; data-og-description=&quot;Flutter enthusiast. You'll find me on stackoverflow. Or as a speaker in Flutter meetups - rrousselGit&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/rrousselGit&quot; data-og-url=&quot;https://github.com/rrousselGit&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ijRDM/hyWoNCLrny/kQMrLo7yJ1Ee0NGFkxhGJ1/img.jpg?width=200&amp;amp;height=200&amp;amp;face=61_76_145_168&quot;&gt;&lt;a href=&quot;https://github.com/rrousselGit&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/rrousselGit&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ijRDM/hyWoNCLrny/kQMrLo7yJ1Ee0NGFkxhGJ1/img.jpg?width=200&amp;amp;height=200&amp;amp;face=61_76_145_168');&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;rrousselGit - Overview&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Flutter enthusiast. You'll find me on stackoverflow. Or as a speaker in Flutter meetups - rrousselGit&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter의 Provider과 Rivepod의 창시자이다. 우선 이전에 포스팅한 내용이 있지만 다시 설명하도록 하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://pcseob.tistory.com/32&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://pcseob.tistory.com/32&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1718870217702&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;8. Dart, Flutter 상태관리 그리고 Riverpod (1)&quot; data-og-description=&quot;이번 포스팅은 상태 관리법 중 하나인 Riverpod 알아보도록 하자. 이론적인 내용이므로 만약 코드에 바로 사용할 방법을 찾는다면 다음 포스팅을 읽으면 된다. 목차 Flutter의 상태관리 Flutter의 상태&quot; data-og-host=&quot;monocsp.dev&quot; data-og-source-url=&quot;https://pcseob.tistory.com/32&quot; data-og-url=&quot;https://monocsp.dev/32&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/T5IZs/hyWoOIsQN8/vTtKUQF5phd6aoLWGrtCG1/img.jpg?width=640&amp;amp;height=755&amp;amp;face=0_0_640_755,https://scrap.kakaocdn.net/dn/GystA/hyWoH3Cphx/QbxMsDQGysZwy9nipzjyP1/img.jpg?width=640&amp;amp;height=755&amp;amp;face=0_0_640_755,https://scrap.kakaocdn.net/dn/edXBNE/hyWoFSihbo/5v9xTwAFGD6hQT6UYXVJyk/img.jpg?width=3024&amp;amp;height=3024&amp;amp;face=0_0_3024_3024&quot;&gt;&lt;a href=&quot;https://pcseob.tistory.com/32&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://pcseob.tistory.com/32&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/T5IZs/hyWoOIsQN8/vTtKUQF5phd6aoLWGrtCG1/img.jpg?width=640&amp;amp;height=755&amp;amp;face=0_0_640_755,https://scrap.kakaocdn.net/dn/GystA/hyWoH3Cphx/QbxMsDQGysZwy9nipzjyP1/img.jpg?width=640&amp;amp;height=755&amp;amp;face=0_0_640_755,https://scrap.kakaocdn.net/dn/edXBNE/hyWoFSihbo/5v9xTwAFGD6hQT6UYXVJyk/img.jpg?width=3024&amp;amp;height=3024&amp;amp;face=0_0_3024_3024');&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;8. Dart, Flutter 상태관리 그리고 Riverpod (1)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이번 포스팅은 상태 관리법 중 하나인 Riverpod 알아보도록 하자. 이론적인 내용이므로 만약 코드에 바로 사용할 방법을 찾는다면 다음 포스팅을 읽으면 된다. 목차 Flutter의 상태관리 Flutter의 상태&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;monocsp.dev&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 Riverpod의 큰 특징을 설명하도록 하겠다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc; color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Provider의 제한사항이 없다.&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc; color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;어디서든 명시된 상태를 공유한다.&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc; color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Flutter에 종속적이지 않다.&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;크게 이렇게 3가지의 특징을 가지고 있다. 간단하게 설명하자면,&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Provider의 제한사항이 없다.&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;개인적으로 provider로 개발했을 때 가장 불편했던점은 다른 provider에서 또 다른 provider의 종속이 생길 때 가장 불편했다. provider의 경우에는 Widget Tree를 기준으로 상태관리가 된다. 그래서 상위 widget을 업데이트하는 경우에는 Proxy Provider를 사용하여 꽤나 불편했다. 하지만 riverpod의 경우에는 상태관리가 전역으로 이루어지므로 Widget tree의 영향을 받지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;어디서든 명시된 상태를 공유한다.&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 이어지는 내용으로 provider의 경우에는 Widget tree의 순서를 따르게 되므로 Flutter의 Widget tree에 익숙하다면 편리할 수 있겠지만, 대부분의 경우는 불편할 때가 많다. 그래서 상태가 전역적으로 관리되는 riverpod이 조금 더 편리한 경우가 많다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Flutter에 종속적이지 않다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Provider는 내부적으로 &lt;span style=&quot;color: #0c0d0e; text-align: left;&quot;&gt;InheritedWidget을 베이스로 두고 있다. 그래서 반드시 Flutter를 사용해야만 사용할 수 있다. dart로만 쓸 수 없다는 뜻이다. riverpod은 flutter 없이 순수 dart언어에서도 사용할 수 있는 특징이 있다. 그래서 Widget tree에 관계없이 전역적으로 처리할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;box-sizing: border-box; border-width: 9px; border-left-style: solid; border-left-color: #000044; padding: 1px 15px; letter-spacing: 1px; margin: 0px 0px; line-height: 1.2;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-size: 25px; font-family: 'Cafe24Ssurround'; font-weight: normal; color: #000044;&quot;&gt;Riverpod으로 비동기 처리하기&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 본격적으로 riverpod으로 비동기 처리하는 법을 알아보도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AsyncValue를 이용하여 비동기 데이터를 처리하였다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;AsyncValue&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://pub.dev/documentation/riverpod/latest/riverpod/AsyncValue-class.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://pub.dev/documentation/riverpod/latest/riverpod/AsyncValue-class.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1718871730201&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;AsyncValue class - riverpod library - Dart API&quot; data-og-description=&quot;A utility for safely manipulating asynchronous data. By using AsyncValue, you are guaranteed that you cannot forget to handle the loading/error state of an asynchronous operation. It also exposes some utilities to nicely convert an AsyncValue to a differen&quot; data-og-host=&quot;pub.dev&quot; data-og-source-url=&quot;https://pub.dev/documentation/riverpod/latest/riverpod/AsyncValue-class.html&quot; data-og-url=&quot;https://pub.dev/documentation/riverpod/latest/riverpod/AsyncValue-class.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://pub.dev/documentation/riverpod/latest/riverpod/AsyncValue-class.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://pub.dev/documentation/riverpod/latest/riverpod/AsyncValue-class.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;AsyncValue class - riverpod library - Dart API&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A utility for safely manipulating asynchronous data. By using AsyncValue, you are guaranteed that you cannot forget to handle the loading/error state of an asynchronous operation. It also exposes some utilities to nicely convert an AsyncValue to a differen&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;pub.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AsyncValue를 이용하여 data, loading, error를 잡을 수 있다. 이거를 riverpod과 같이 사용하면 조금 더 편리하게 작업을 진행할 수 있어서 개인적으로 선호하는 편이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 riverpod과 AsyncValue를 사용한 코드이다.&lt;/p&gt;
&lt;pre id=&quot;code_1718871672352&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 이 파일의 이름을 'upload_provider.g.dart'로 지정합니다.
// 이 파일은 `build_runner`를 통해 생성될 것입니다.
part 'upload_provider.g.dart';

@riverpod
class UploadProvider extends _$UploadProvider {
  // 초기 상태로 빈 UploadFileEntity를 담은 AsyncData를 반환합니다.
  @override
  AsyncValue&amp;lt;UploadFileEntity&amp;gt; build() =&amp;gt; AsyncData(UploadFileEntity.empty());

  // 파일 업로드를 처리하는 비동기 메서드입니다.
  Future uploadFile({
    required String filePath, // 업로드할 파일의 경로
    required UploadType type, // 업로드 타입 (파일 또는 이미지)
    required UploadPartType uploadPartType, // 업로드 파트 타입
  }) async {
    // 현재 상태가 로딩 중인 경우, 업로드를 중단합니다.
    if (state.isLoading) return;

    // 상태를 로딩 중으로 설정합니다.
    state = const AsyncLoading();

    try {
      // 업로드 결과를 담을 변수입니다.
      Either&amp;lt;ErrorModel, UploadFileEntity&amp;gt; result;
      
      // 업로드 타입에 따라 다른 업로드 유즈케이스를 실행합니다.
      if (type == UploadType.file) {
        result = await UploadFileUsecase()
            .excute(filePath: filePath, uploadPartType: uploadPartType);
      } else {
        result = await UploadImageUsecase()
            .excute(filePath: filePath, uploadPartType: uploadPartType);
      }

      // 업로드가 성공한 경우, 상태를 성공 데이터로 설정합니다.
      if (result.isRight()) {
        state = AsyncData(result.asRight());
      } else {
        // 업로드가 실패한 경우, 상태를 에러로 설정합니다.
        state = AsyncError(result.asLeft(), StackTrace.current);
      }
    } catch (error) {
      // 예외가 발생한 경우, 상태를 커스텀 에러 모델을 담은 에러로 설정합니다.
      state = AsyncError(
        left(
          CustomErrorModel.errorModel(
            errorCode: '700',
            errorDescription: 'Upload provider error $error',
            errorMessage: '업로드에 실패했습니다.',
          ),
        ),
        StackTrace.current,
      );
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로 Either를 사용하여 두 가지 타입반환까지 하였다. 이 부분까지 따라 할 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://pub.dev/packages/either_dart&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://pub.dev/packages/either_dart&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1718871955558&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;either_dart | Dart package&quot; data-og-description=&quot;Error handler library for type-safe and easy work with errors on Dart and Flutter. Either is an alternative to Nullable value and Exceptions.&quot; data-og-host=&quot;pub.dev&quot; data-og-source-url=&quot;https://pub.dev/packages/either_dart&quot; data-og-url=&quot;https://pub.dev/packages/either_dart&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/by4Rrg/hyWoEy42UC/m3MqNGMIhPQd6pczAhydnk/img.png?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640,https://scrap.kakaocdn.net/dn/79PfJ/hyWoLycz0E/qrkMwysltjcPU3qg3HQhW1/img.png?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640&quot;&gt;&lt;a href=&quot;https://pub.dev/packages/either_dart&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://pub.dev/packages/either_dart&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/by4Rrg/hyWoEy42UC/m3MqNGMIhPQd6pczAhydnk/img.png?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640,https://scrap.kakaocdn.net/dn/79PfJ/hyWoLycz0E/qrkMwysltjcPU3qg3HQhW1/img.png?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;either_dart | Dart package&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Error handler library for type-safe and easy work with errors on Dart and Flutter. Either is an alternative to Nullable value and Exceptions.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;pub.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 사용하고 riverpod generate를 실행하면. g파일이 생성이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 실행부.&lt;/p&gt;
&lt;pre id=&quot;code_1718872161115&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// `uploadProviderProvider`의 상태 변경을 구독하고, 업로드 작업을 실행합니다.
ref.watch(uploadProviderProvider.notifier).uploadFile(
    // 업로드할 파일의 경로를 설정합니다.
    filePath: (result).path,
    // 업로드 타입을 이미지로 설정합니다.
    type: UploadType.image,
    // 업로드 파트 타입을 사용자 사진으로 설정합니다.
    uploadPartType: UploadPartType.userPicture,
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선언한 provider를 다음과 같이 ref.watch로 사용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UI 부&lt;/p&gt;
&lt;pre id=&quot;code_1718872258239&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 업로드 타입을 정의하는 열거형입니다.
enum UploadType { image, file }

// 업로드 위젯을 정의하는 클래스입니다.
class UploadWidget extends ConsumerWidget {
  // 업로드 타입 (image 또는 file)을 나타내는 필드입니다.
  final UploadType type;

  // 업로드가 성공했을 때 표시할 위젯을 반환하는 함수입니다.
  final Widget Function(UploadFileEntity) child;

  // 업로드 중 에러가 발생했을 때 표시할 위젯을 반환하는 함수입니다.
  final Widget Function(Object, StackTrace) errorWidget;

  // 업로드가 진행 중일 때 표시할 위젯을 반환하는 함수입니다.
  final Widget Function() loadingWidget;

  // 생성자입니다. 필요한 매개변수들을 받아 초기화합니다.
  const UploadWidget({
    super.key,
    required this.type,
    required this.child,
    required this.errorWidget,
    required this.loadingWidget,
  });

  // 위젯 빌드 메서드입니다.
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // uploadProviderProvider를 구독하고 상태에 따라 다른 위젯을 표시합니다.
    return ref
        .watch(uploadProviderProvider) // 상태를 구독합니다.
        .when(
          // 데이터가 로드된 경우 child 함수를 호출하여 위젯을 반환합니다.
          data: child,
          // 에러가 발생한 경우 errorWidget 함수를 호출하여 위젯을 반환합니다.
          error: errorWidget,
          // 로딩 중인 경우 loadingWidget 함수를 호출하여 위젯을 반환합니다.
          loading: loadingWidget,
        );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ConsumerWidget을 받아와서 ref.watch.when을 통해 data, error, loading을 처리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로 error의 경우에는 errorWidget을 따로 페이지를 만들어 통일감 있게 해 주는걸 조금 더 선호하지만, 이건 기획마다 다르므로 알맞게 적용하면 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;궁금한 게 있거나 수정사항 있다면 댓글 부탁드립니다.&lt;/p&gt;</description>
      <category>Study/Dart,Flutter</category>
      <category>asyncdata</category>
      <category>ConsumerWidget</category>
      <category>Flutter</category>
      <category>Future</category>
      <category>riverpod</category>
      <category>상태관리</category>
      <author>코딩 잘 할거얌:)</author>
      <guid isPermaLink="true">https://pcseob.tistory.com/84</guid>
      <comments>https://pcseob.tistory.com/84#entry84comment</comments>
      <pubDate>Thu, 20 Jun 2024 17:33:07 +0900</pubDate>
    </item>
    <item>
      <title>제주도 SK렌터카 인도 연석에 휠 부딪침 사고 후기</title>
      <link>https://pcseob.tistory.com/83</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 시간이 생겨서 제주도로 여행 갔다가 도로 연석에 휠을 갈아먹은 사고가 났고, 나랑 같은 처지에 있었던 사람들에게 도움이 되고자 작성하게 되었다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;225&quot; data-origin-height=&quot;225&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgQaBZ/btsHTUie8Nj/ZwvEFfzub0Lfenqd39qzj1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgQaBZ/btsHTUie8Nj/ZwvEFfzub0Lfenqd39qzj1/img.jpg&quot; data-alt=&quot;연석에 갈은 직후 내 마음&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgQaBZ/btsHTUie8Nj/ZwvEFfzub0Lfenqd39qzj1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgQaBZ%2FbtsHTUie8Nj%2FZwvEFfzub0Lfenqd39qzj1%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;455&quot; height=&quot;455&quot; data-origin-width=&quot;225&quot; data-origin-height=&quot;225&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;연석에 갈은 직후 내 마음&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;불안한 마음에 이 글을 보시는 분들을 위해 결론부터 말씀드리자면, &lt;/p&gt;&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;SK제주렌터카 완전자차로 사고났을때 주행에 문제가 되지 않는다면 괜찮다.&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;로 생각이 된다.&lt;br&gt;&lt;span style=&quot;color: #000044;&quot;&gt;제주렌터카 예약전&lt;/span&gt;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;뚜벅이로 다니기는 조금 힘들겠다는 생각이 들어서 렌터카에서 대여해서 다니기로 했다.&lt;br&gt;렌터카를 선택하는 데에 있어 선택지가 참 많았다.&lt;/p&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;대기업 렌터카 + 완전자차&lt;/li&gt;&lt;li&gt;대기업 렌터카 + 일반&lt;/li&gt;&lt;li&gt;중소기업 렌터카 + 슈퍼자차&lt;/li&gt;&lt;li&gt;중소기업 렌터카 + 완전자차&lt;/li&gt;&lt;li&gt;중소기업 렌터카 + 일반&lt;/li&gt;&lt;/ol&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 5가지 선택지가 있었다. 보통 제주 중소기업 업체에서 저렴하다고 광고하고 후기도 많이 작성되어 있어서 조금 혹 했었다. 하지만 완전자차로 무조건 진행하기로 했다.&lt;br&gt;우선 왜 완전자차를 고집했냐면 여러 가지 이유가 있었다.&lt;/p&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;내가 운전에 자신이 없었다.&lt;/li&gt;&lt;li&gt;보장범위가 꽤 넓다. (타이어 휠, 사이드미러 등 소모품도 전부 보장이 되었다.)&lt;/li&gt;&lt;li&gt;완전자차하면 반납시간이 획기적으로 줄어든다. (사고유무 체크를 거의 안 함)&lt;/li&gt;&lt;/ol&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;게다가 추석에 롯데렌터카를 빌리고 반납할 때, 직원분이&amp;nbsp;&lt;b&gt;'차량 P로 두시고 가시면 됩니다.'라는&lt;/b&gt; 말에 아주 감동을 받았어서 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;완전자차를 무조건 하기로 했다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;477&quot; data-origin-height=&quot;458&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cE5uKo/btsHTgeZa8K/Xk8KgNl1WnQti8RMHRzjSK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cE5uKo/btsHTgeZa8K/Xk8KgNl1WnQti8RMHRzjSK/img.webp&quot; data-alt=&quot;대기업 + 완전자차로 진행시켜&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cE5uKo/btsHTgeZa8K/Xk8KgNl1WnQti8RMHRzjSK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcE5uKo%2FbtsHTgeZa8K%2FXk8KgNl1WnQti8RMHRzjSK%2Fimg.webp&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;335&quot; height=&quot;322&quot; data-origin-width=&quot;477&quot; data-origin-height=&quot;458&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;대기업 + 완전자차로 진행시켜&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추석에 가격을 알아봤을 때에는 SK렌터카보다 롯데렌터카가 더 저렴했었는데, 이번에는 롯데렌터카보다 SK렌터카가 조금 더 저렴했어서 SK렌터카로 진행했다.&lt;/p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bv3OJg/btsHSx2CpH2/r7zOVij9mKYdjDDzfwQRxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bv3OJg/btsHSx2CpH2/r7zOVij9mKYdjDDzfwQRxk/img.png&quot; data-origin-width=&quot;1480&quot; data-origin-height=&quot;1975&quot; style=&quot;width: 49.3977%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bv3OJg/btsHSx2CpH2/r7zOVij9mKYdjDDzfwQRxk/img.png&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbv3OJg%2FbtsHSx2CpH2%2Fr7zOVij9mKYdjDDzfwQRxk%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;1480&quot; height=&quot;1975&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bolyjv/btsHSygeHmr/1GVKcSbrqjLYqFrZUXwoO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bolyjv/btsHSygeHmr/1GVKcSbrqjLYqFrZUXwoO0/img.png&quot; data-origin-width=&quot;2250&quot; data-origin-height=&quot;3000&quot; style=&quot;width: 49.4395%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bolyjv/btsHSygeHmr/1GVKcSbrqjLYqFrZUXwoO0/img.png&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbolyjv%2FbtsHSygeHmr%2F1GVKcSbrqjLYqFrZUXwoO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2250&quot; height=&quot;3000&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 대여하고 열심히 돌아다니다가 반납하기 이틀 전 사고가 발생했다...&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #000044;&quot;&gt;사고..&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;한림항 근처에 에어비앤비로 대여한 후 렌터카로 열심히 돌아다니다가 근처 '명랑스낵'이라는 맛있는 떡볶이집이 있다고 해서 얼른 가게 되었다.&lt;br&gt;18시까지 운영한다고 적혀있어서 17시40분에 급히 연락해서 포장해 달라고 말씀드렸고, 차량을 몰고 빠르게 가기 시작했다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mjCRf/btsHR4GQYmx/VY6YEoy89NKI3xPAzEbC7K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mjCRf/btsHR4GQYmx/VY6YEoy89NKI3xPAzEbC7K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mjCRf/btsHR4GQYmx/VY6YEoy89NKI3xPAzEbC7K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmjCRf%2FbtsHR4GQYmx%2FVY6YEoy89NKI3xPAzEbC7K%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;3024&quot; height=&quot;4032&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 위에 사진처럼 '명량스낵'이라는 커다란 간판이 없어서 어딘지 헤매다가 잠시 인도옆에 정차하고 찾아봐야겠다는 생각에 인도 쪽으로 차량을 틀면서 반대편 건물들을 살펴보던중에..&lt;br&gt;'덜커커커커컥'&lt;br&gt;인도 연석에 타이어가 갈리는 소리가 들렸고 차량이 연석에 부딪쳐 살짝 튕겨져 나왔다.&lt;/p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfUxne/btsHS0pVou9/m9t1vXn3gxQHA7t604xtF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfUxne/btsHS0pVou9/m9t1vXn3gxQHA7t604xtF1/img.png&quot; data-origin-width=&quot;2780&quot; data-origin-height=&quot;2586&quot; style=&quot;width: 58.2196%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfUxne/btsHS0pVou9/m9t1vXn3gxQHA7t604xtF1/img.png&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfUxne%2FbtsHS0pVou9%2Fm9t1vXn3gxQHA7t604xtF1%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;2780&quot; height=&quot;2586&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KTymk/btsHRLU99lK/40CIyNvcQ7v0P5Lijfzwb0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KTymk/btsHRLU99lK/40CIyNvcQ7v0P5Lijfzwb0/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; style=&quot;width: 40.6176%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KTymk/btsHRLU99lK/40CIyNvcQ7v0P5Lijfzwb0/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKTymk%2FbtsHRLU99lK%2F40CIyNvcQ7v0P5Lijfzwb0%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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;사고가 난 자리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;휠 스크래치뿐만 아니라 휠이 약간 휘어서 울퉁불퉁하게 되었다. 진짜 머릿속이 하얗게 되어서 와 어쩌지..라는 생각을 하다가 우선 음식부터 픽업하고 숙소 가서 생각하잔 생각이 들어서 픽업부터 했다.&lt;/p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dQngtw/btsHScEGr3Y/6l0HzBY0e2BgWZNrCUSDFK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dQngtw/btsHScEGr3Y/6l0HzBY0e2BgWZNrCUSDFK/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; style=&quot;width: 49.4186%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dQngtw/btsHScEGr3Y/6l0HzBY0e2BgWZNrCUSDFK/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdQngtw%2FbtsHScEGr3Y%2F6l0HzBY0e2BgWZNrCUSDFK%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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKe9po/btsHSVPNDfs/iSrY5DrkJsK5pSt0WOtgc1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKe9po/btsHSVPNDfs/iSrY5DrkJsK5pSt0WOtgc1/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; style=&quot;width: 49.4186%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKe9po/btsHSVPNDfs/iSrY5DrkJsK5pSt0WOtgc1/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKe9po%2FbtsHSVPNDfs%2FiSrY5DrkJsK5pSt0WOtgc1%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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;픽업. 그와중에 날씨는 좋음..&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;픽업하고 숙소에 도착해서 차량을 더 살펴봤다.&lt;/p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKfwQJ/btsHTm0rV4P/zAAOEohvjEpkBF7E2JKpAK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKfwQJ/btsHTm0rV4P/zAAOEohvjEpkBF7E2JKpAK/img.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; style=&quot;width: 49.4186%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKfwQJ/btsHTm0rV4P/zAAOEohvjEpkBF7E2JKpAK/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKfwQJ%2FbtsHTm0rV4P%2FzAAOEohvjEpkBF7E2JKpAK%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;4032&quot; height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czTO4e/btsHRZ6IRXl/G3JKLqtKXlKo3tbmaje3C1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czTO4e/btsHRZ6IRXl/G3JKLqtKXlKo3tbmaje3C1/img.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; style=&quot;width: 49.4186%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czTO4e/btsHRZ6IRXl/G3JKLqtKXlKo3tbmaje3C1/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczTO4e%2FbtsHRZ6IRXl%2FG3JKLqtKXlKo3tbmaje3C1%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;4032&quot; height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 주행 중에 떨림이나 흔들림은 없는데 그래도 불안한 거는 어쩔 수가 없었다. &lt;b&gt;찾아보니 중소렌터카에는 완전자차에도 타이어 휠 보장이 없어 30만 원 정도 내야 한다는 이야기가 꽤나 적혀있었기 때문이다.&lt;/b&gt;&lt;br&gt;그래서 일단 SK제주렌터카에 전화를 걸었다.&lt;br&gt;나 : 안녕하세요. 혹시 SK렌터카 완전자차는 타이어 휠도 보장범위에 들어가나요?&lt;br&gt;직 : 네네 저희는 다 보장됩니다.&lt;br&gt;나 : 아 그래요? 제가 완전자차로 빌리는 중인데, 인도 연석에 타이어 휠을 부딪쳐 갈리고 조금 휘었는데 괜찮나요?&lt;br&gt;직 : 혹시 차량 운행이 안 되거나 주행 중에 불편함이 있으신가요?&lt;br&gt;나 : 아뇨. 그래도 이거 신고를 해야 하나 해서요.&lt;br&gt;직 : 주행이 되면 괜찮으시고, 만약에 주행도 못하실 만큼 되시면 저희에게 연락 주시면 차량 반납 도와드리고 있습니다.&lt;br&gt;나 : 그래요? 주행이 정상적으로 잘 되는데, 반납할 때는 따로 말씀 안 드려도 될까요?&lt;br&gt;직 : 반납할때 직원분께 말씀드리고 사고경위서만 작성하시면 될 거 같네요.&lt;br&gt;나 : 감사합니다.&lt;br&gt;일단 전화로 한숨 돌렸다. 그래서 이다음부터는 최대한 안전 운전하고 다녔다..&lt;br&gt;&lt;span style=&quot;color: #000044;&quot;&gt;렌터카 반납&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;반납일이 되어서 반납하러 가게 되었다. 타이어사진도 한번 더 찍고 이전사진이랑 비교해도 휠이 확연히 휘어있는 게 분명했다.&lt;/p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNEwJZ/btsHR3OLgzh/Kd5ATkIAfHKTqVo8kO22h0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNEwJZ/btsHR3OLgzh/Kd5ATkIAfHKTqVo8kO22h0/img.png&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; style=&quot;width: 49.4186%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNEwJZ/btsHR3OLgzh/Kd5ATkIAfHKTqVo8kO22h0/img.png&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNEwJZ%2FbtsHR3OLgzh%2FKd5ATkIAfHKTqVo8kO22h0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KTymk/btsHRLU99lK/40CIyNvcQ7v0P5Lijfzwb0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KTymk/btsHRLU99lK/40CIyNvcQ7v0P5Lijfzwb0/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; style=&quot;width: 49.4186%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KTymk/btsHRLU99lK/40CIyNvcQ7v0P5Lijfzwb0/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKTymk%2FbtsHRLU99lK%2F40CIyNvcQ7v0P5Lijfzwb0%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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;빌린 직후(좌), 사고난 직후(우)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;떨리는 마음에 SK렌터카에 입장하고 직원분이 반납을 도와주시면서 롯데렌터카와 똑같이 말씀해 주셨다.&lt;br&gt;&lt;b&gt;'기름 더 많이 넣으신 건 계좌로 환불되실 거고 차량 P로 두시고 가시면 됩니다.'&lt;/b&gt;&lt;br&gt;라고 해서 그냥 '가버릴까?'라는 마음이 들었지만, 그래도 걱정되어서 직원분에게 연석에 타이어 부딪쳐서 휠이 휘었다고 말씀드렸다.&lt;br&gt;직 : 주행에 문제 있으셨나요?&lt;br&gt;나 : 아뇨&lt;br&gt;직 : 그러면 괜찮아요 내리시면 됩니다~&lt;br&gt;그래도 괜찮은 게 맞는지 걱정이 되었는데 일주일이 지난 지금은 기름 환급금도 들어오고 연락도 없다. &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;정말 끝났나 보다.&lt;/b&gt;&lt;/span&gt;&lt;br&gt;진짜 다음에 제주도가도 SK렌터카에 완전자차로 빌릴 예정이다.&amp;nbsp;&lt;/p&gt;</description>
      <category>Daily/여행</category>
      <category>sk렌터카</category>
      <category>sk제주렌터카</category>
      <category>사고</category>
      <category>연석</category>
      <category>연석부딪침</category>
      <category>제주도</category>
      <category>제주렌터카</category>
      <category>타이어</category>
      <category>휠</category>
      <category>휠 휘어짐</category>
      <author>코딩 잘 할거얌:)</author>
      <guid isPermaLink="true">https://pcseob.tistory.com/83</guid>
      <comments>https://pcseob.tistory.com/83#entry83comment</comments>
      <pubDate>Sun, 9 Jun 2024 22:53:27 +0900</pubDate>
    </item>
    <item>
      <title>머신러닝에 대한 배경지식 공부[1부]</title>
      <link>https://pcseob.tistory.com/82</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 머신러닝에 대해서 프로젝트를 진행하고있다. 정확하게는 Scen Text Recognition을 진행하고있는데, 그 단계 이전에 Object Detection이 필요해서 먼저 Object Detection에 대해서 알아보도록 하자. 라고 하기전에 우선 머신러닝에 대한 모든 기본지식부터 알아본내용들을 아주 간단하게 정리했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;628&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BVvqw/btsF0siD4z4/vOUMoCdOsg581yKdxmmF6K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BVvqw/btsF0siD4z4/vOUMoCdOsg581yKdxmmF6K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BVvqw/btsF0siD4z4/vOUMoCdOsg581yKdxmmF6K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBVvqw%2FbtsF0siD4z4%2FvOUMoCdOsg581yKdxmmF6K%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;1200&quot; height=&quot;628&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;628&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p style=&quot;box-sizing: border-box; border-width: 9px; border-left-style: solid; border-left-color: #000044; padding: 1px 15px; letter-spacing: 1px; margin: 0px 0px; line-height: 1.2;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-size: 25px; font-family: 'Cafe24Ssurround'; font-weight: normal; color: #000044;&quot;&gt;머신러닝이 뭘까&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;625&quot; data-origin-height=&quot;380&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tESk6/btsF0tWpIo7/8RkmS0DwpzmcziWci1tTA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tESk6/btsF0tWpIo7/8RkmS0DwpzmcziWci1tTA0/img.png&quot; data-alt=&quot;https://www.lgcns.com/blog/cns-tech/ai-data/8864/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tESk6/btsF0tWpIo7/8RkmS0DwpzmcziWci1tTA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtESk6%2FbtsF0tWpIo7%2F8RkmS0DwpzmcziWci1tTA0%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;625&quot; height=&quot;380&quot; data-origin-width=&quot;625&quot; data-origin-height=&quot;380&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.lgcns.com/blog/cns-tech/ai-data/8864/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 나는 시작부터 &quot;머신러닝&quot;이라는 것에 대한 정의부터 시작했다. 무엇이 머신러닝일까 흔히 사용하는 &quot;딥러닝&quot; 그리고 &quot;AI&quot;는 어떤 차이점이 있는지 찾아보았다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 위의 이미지로 한번에 정리되었다. 그렇지만 조금 더 찾아보았다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1026&quot; data-origin-height=&quot;626&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvv8OA/btsF3lISmjH/g15NkdQdTnvqs1Bc8K39I0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvv8OA/btsF3lISmjH/g15NkdQdTnvqs1Bc8K39I0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvv8OA/btsF3lISmjH/g15NkdQdTnvqs1Bc8K39I0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbvv8OA%2FbtsF3lISmjH%2Fg15NkdQdTnvqs1Bc8K39I0%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;1026&quot; height=&quot;626&quot; data-origin-width=&quot;1026&quot; data-origin-height=&quot;626&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 머신러닝은 사람이 특징점 추출(Feature Extraction)과 분류(Classification)을 직접해야 모델을 만들 수 있지만, 딥러닝의 경우는 자동으로 특징점 추출 및 분류를 진행한다는 것에 차이점이 있다.&lt;/p&gt;
&lt;p style=&quot;box-sizing: border-box; border-width: 9px; border-left-style: solid; border-left-color: #000044; padding: 1px 15px; letter-spacing: 1px; margin: 0px 0px; line-height: 1.2;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-size: 25px; font-family: 'Cafe24Ssurround'; font-weight: normal; color: #000044;&quot;&gt;퍼셉트론이란?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퍼셉트론이란 내용을 많이 접하게되었다. 사실 접하게 된 계기는, 어떻게 input값을 넣어서 어떤 판단을 해서 어떻게 output이 나오게 되는가. 즉 원론부터 궁금해서 찾아보게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터의 경우에는 01. 이진수를 가지고 아주 많은 AND, OR, XOR, NOT 등등 연산자들로 계산을 해서 결과를 낸다. 그렇다면 머신러닝은 어떻게 결과를 내는걸까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 퍼셉트론이라는 것에서 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;퍼셉트론(Perceptron)은 인공 신경망의 한 종류로, 이진 분류를 위한 간단한 형태의 인공 뉴런이다. 퍼셉트론은 입력값과 가중치를 곱하여 합산하고, 이 합산값에 활성화 함수를 적용하여 출력값을 계산한다. 이 출력값은 임계치와 비교되어 결정을 내리게 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;544&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuhb1i/btsF1t2w9wQ/T2VWCrehl2PNmI8lgGiZO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuhb1i/btsF1t2w9wQ/T2VWCrehl2PNmI8lgGiZO0/img.png&quot; data-alt=&quot;https://compmath.korea.ac.kr/deeplearning/Perceptron.html&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuhb1i/btsF1t2w9wQ/T2VWCrehl2PNmI8lgGiZO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcuhb1i%2FbtsF1t2w9wQ%2FT2VWCrehl2PNmI8lgGiZO0%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;1280&quot; height=&quot;544&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;544&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://compmath.korea.ac.kr/deeplearning/Perceptron.html&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퍼셉트론은 우리의 뇌에 들어있는 뉴런과 굉장히 비슷하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;472&quot; data-origin-height=&quot;430&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bI07dp/btsF3f21OlP/Vhi95G5v6kvhdfdrYHKELk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bI07dp/btsF3f21OlP/Vhi95G5v6kvhdfdrYHKELk/img.png&quot; data-alt=&quot;입력이 2개인 퍼셉트론&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bI07dp/btsF3f21OlP/Vhi95G5v6kvhdfdrYHKELk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbI07dp%2FbtsF3f21OlP%2FVhi95G5v6kvhdfdrYHKELk%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;472&quot; height=&quot;430&quot; data-origin-width=&quot;472&quot; data-origin-height=&quot;430&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;입력이 2개인 퍼셉트론&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;box-sizing: border-box; border-width: 9px; border-left-style: solid; border-left-color: #000044; padding: 1px 15px; letter-spacing: 1px; margin: 0px 0px; line-height: 1.2;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-size: 25px; font-family: 'Cafe24Ssurround'; font-weight: normal; color: #000044;&quot;&gt;합성곱 신경망 (Convolutional Neural Network CNN)&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-origin-width=&quot;1280&quot; data-origin-height=&quot;685&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQ6DOO/btsF1rXWaJG/vKtt4QfWk1h7fkDGM1f0YK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQ6DOO/btsF1rXWaJG/vKtt4QfWk1h7fkDGM1f0YK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQ6DOO/btsF1rXWaJG/vKtt4QfWk1h7fkDGM1f0YK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQ6DOO%2FbtsF1rXWaJG%2FvKtt4QfWk1h7fkDGM1f0YK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;685&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;685&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;합성곱 신경망은 이름이 앞에 Convolutional이 붙어있다. 실제로 전통적인 신경망에서 Convolutional Layer가 여러개 붙인 모양인데, Convolutional Layer를 통해 입력받은 이미지에 대한 특징을 추출하게 되기 때문이다.&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그래서 Convolutional Layer가 뭘까&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;364&quot; data-origin-height=&quot;219&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1c2sX/btsF0sb9TjK/yI3vPL5SocXOMamKhCNeL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1c2sX/btsF0sb9TjK/yI3vPL5SocXOMamKhCNeL0/img.png&quot; data-alt=&quot;https://intellabs.github.io/RiverTrail/tutorial/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1c2sX/btsF0sb9TjK/yI3vPL5SocXOMamKhCNeL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1c2sX%2FbtsF0sb9TjK%2FyI3vPL5SocXOMamKhCNeL0%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;364&quot; height=&quot;219&quot; data-origin-width=&quot;364&quot; data-origin-height=&quot;219&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://intellabs.github.io/RiverTrail/tutorial/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;입력이미지에서 들어온 것을 Kernel(필터)를 통해서 특징을 추출하는것이 주요 역할이다.&lt;/b&gt; 그래서 그림에서 보면 Kernel이라는 것으로 Input의 이미지를 하나하나 특징을 추출해서 output으로 바꾸는 그림이다. 이게 너무 간략화되어있어서 다음 gif로 확실히 이해할 수 있을 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;384&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEbdE0/btsF1Z7Fvyw/U66gz83MNRNWsiRwCjq73K/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEbdE0/btsF1Z7Fvyw/U66gz83MNRNWsiRwCjq73K/img.gif&quot; data-alt=&quot;https://towardsdatascience.com/beginners-guide-to-understanding-convolutional-neural-networks-ae9ed58bb17d&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEbdE0/btsF1Z7Fvyw/U66gz83MNRNWsiRwCjq73K/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cEbdE0/btsF1Z7Fvyw/U66gz83MNRNWsiRwCjq73K/img.gif&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;526&quot; height=&quot;384&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;384&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://towardsdatascience.com/beginners-guide-to-understanding-convolutional-neural-networks-ae9ed58bb17d&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1120&quot; data-origin-height=&quot;631&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zL1jM/btsF2uzABrf/wZ17ZGJc1qu6ZJK5KWQPKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zL1jM/btsF2uzABrf/wZ17ZGJc1qu6ZJK5KWQPKk/img.png&quot; data-alt=&quot;https://www.ibm.com/topics/convolutional-neural-networks&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zL1jM/btsF2uzABrf/wZ17ZGJc1qu6ZJK5KWQPKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzL1jM%2FbtsF2uzABrf%2FwZ17ZGJc1qu6ZJK5KWQPKk%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;1120&quot; height=&quot;631&quot; data-origin-width=&quot;1120&quot; data-origin-height=&quot;631&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.ibm.com/topics/convolutional-neural-networks&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cw9y9J/btsF3Eaq7NB/X5Ari6PPge7FjpyNJdAurk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cw9y9J/btsF3Eaq7NB/X5Ari6PPge7FjpyNJdAurk/img.png&quot; data-origin-width=&quot;1222&quot; data-origin-height=&quot;568&quot; data-is-animation=&quot;false&quot; style=&quot;width: 19.0915%; margin-right: 10px;&quot; data-widthpercent=&quot;19.55&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cw9y9J/btsF3Eaq7NB/X5Ari6PPge7FjpyNJdAurk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcw9y9J%2FbtsF3Eaq7NB%2FX5Ari6PPge7FjpyNJdAurk%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;1222&quot; height=&quot;568&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0aM9K/btsF2O5FAms/pP8JoVFt81izNYLVSkyg31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0aM9K/btsF2O5FAms/pP8JoVFt81izNYLVSkyg31/img.png&quot; data-origin-width=&quot;1266&quot; data-origin-height=&quot;246&quot; data-is-animation=&quot;false&quot; style=&quot;width: 45.6685%; margin-right: 10px;&quot; data-widthpercent=&quot;46.76&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0aM9K/btsF2O5FAms/pP8JoVFt81izNYLVSkyg31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0aM9K%2FbtsF2O5FAms%2FpP8JoVFt81izNYLVSkyg31%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;1266&quot; height=&quot;246&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2OSF6/btsF148V1GN/elAsxr93YONUdekdeIK8s1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2OSF6/btsF148V1GN/elAsxr93YONUdekdeIK8s1/img.png&quot; data-origin-width=&quot;1224&quot; data-origin-height=&quot;330&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.9144%;&quot; data-widthpercent=&quot;33.69&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2OSF6/btsF148V1GN/elAsxr93YONUdekdeIK8s1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2OSF6%2FbtsF148V1GN%2FelAsxr93YONUdekdeIK8s1%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;1224&quot; height=&quot;330&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;Convolutinal Layer에 대하여(https://joanne.tistory.com/5)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 만들고나면, Feature/Activation Map이라는 결과물이 나오게 된다. 그리고 Padding이라는 것을 통해 결과값의 소실을 막기 위해 입력값 주위로 0을 넣어서 결과값이 작아지는것을 방지하게 된다.&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;그렇다면 Max-Pooling은 뭘까?&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;377&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bH6vSJ/btsF2wc70Ih/ZFTKGNPyNXWl2BpXKc96A0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bH6vSJ/btsF2wc70Ih/ZFTKGNPyNXWl2BpXKc96A0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bH6vSJ/btsF2wc70Ih/ZFTKGNPyNXWl2BpXKc96A0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbH6vSJ%2FbtsF2wc70Ih%2FZFTKGNPyNXWl2BpXKc96A0%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;652&quot; height=&quot;377&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;377&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;max pool, 즉 큰 값이 다른 특징들을 대표한다는 개념이다. 그래서 위의 이미지에서 2X2 필터내의 값들 중 가장 큰 값을 추출하는 것이다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;461&quot; data-origin-height=&quot;302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CYtUl/btsF04B6g61/q95vHKYqqh4L6FvKtsD3dK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CYtUl/btsF04B6g61/q95vHKYqqh4L6FvKtsD3dK/img.png&quot; data-alt=&quot;가장 중요한 정보를 유지하며 정보량을 줄인다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CYtUl/btsF04B6g61/q95vHKYqqh4L6FvKtsD3dK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCYtUl%2FbtsF04B6g61%2Fq95vHKYqqh4L6FvKtsD3dK%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;461&quot; height=&quot;302&quot; data-origin-width=&quot;461&quot; data-origin-height=&quot;302&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;가장 중요한 정보를 유지하며 정보량을 줄인다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;마지막으로 Fully-Connected Layer는 무엇일까?&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;441&quot; data-origin-height=&quot;538&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yyJ0v/btsF09iYrQH/61zEm2uLyrD2YxwRvjTbZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yyJ0v/btsF09iYrQH/61zEm2uLyrD2YxwRvjTbZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yyJ0v/btsF09iYrQH/61zEm2uLyrD2YxwRvjTbZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyyJ0v%2FbtsF09iYrQH%2F61zEm2uLyrD2YxwRvjTbZk%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;441&quot; height=&quot;538&quot; data-origin-width=&quot;441&quot; data-origin-height=&quot;538&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;레이어의 출력을 평탄화(flattening) 하여 다음 스테이지에 입력될 수 있는 단일 벡터로 변환한다. 즉 앞에서 결과된 처리들이 2차원 배열일 것이다. (이미지는 2차원) 그래서 이 2차원배열을 1차원 배열로 풀어 표시한 후 분류가 수행이 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 이미지를 잘 보면 with drop out이 적혀있는데, Drop out은 신경망의 과적합(Overfitting)을 방지를 위한 정규화 기법 중 하나로, 학습과정중에 랜덤하게 일부 뉴런을 비활성화 시켜 과적합을 방지한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 왜 과적합이 문제가 되느냐, &lt;b&gt;모델의 훈련에만 너무 맞춰져있어서 새로운 데이터에 대한 일반화 성능이 저하될 수 있다. 즉 훈련데이터는 아주 100점이 나올 수 있지만, 새로운 데이터에 대해서는 성능이 하락할 수 있다는 뜻이다.&lt;/b&gt; 또한 모델의 복잡성이 증가하기 때문에 랜덤으로 뉴런신경망을 비활성화 시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 CNN이 탄생하게되고 이미지 머신러닝에 사용하게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;CNN은 이미지 머신러닝에 특화되어있는 여러 인공신경망 구조이다. 그리고 CNN을 기반으로 객체검출(Object Detection) 알고리즘, R-CNN과 YOLO라는게 등장했다.&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;참고자료&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://joanne.tistory.com/5&quot;&gt;https://joanne.tistory.com/5&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1711329408373&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;[머신러닝 기초] 퍼셉트론부터 CNN 구조까지&quot; data-og-description=&quot;About Perceptron 퍼셉트론이란? 다수의 신호를 입력으로 받아 하나의 결과를 내보내는 알고리즘이다. 퍼셉트론 신호는 1 또는 0의 두가지 값을 가질 수 있다. 즉, 어떠한 임계값을 기준으로 하여 신&quot; data-og-host=&quot;joanne.tistory.com&quot; data-og-source-url=&quot;https://joanne.tistory.com/5&quot; data-og-url=&quot;https://joanne.tistory.com/5&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/gPFxZ/hyVDGyaanL/YF8ySWLiYQbM2GGrCHmyIK/img.jpg?width=800&amp;amp;height=336&amp;amp;face=0_0_800_336,https://scrap.kakaocdn.net/dn/fNmz0/hyVDy70urs/a5kocfqJpn2cqKzpO9RNAK/img.jpg?width=800&amp;amp;height=336&amp;amp;face=0_0_800_336,https://scrap.kakaocdn.net/dn/bvPMon/hyVDHw58mz/7AOvbDkJ4LCBoM4AG7CuR0/img.jpg?width=1644&amp;amp;height=880&amp;amp;face=0_0_1644_880&quot;&gt;&lt;a href=&quot;https://joanne.tistory.com/5&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://joanne.tistory.com/5&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/gPFxZ/hyVDGyaanL/YF8ySWLiYQbM2GGrCHmyIK/img.jpg?width=800&amp;amp;height=336&amp;amp;face=0_0_800_336,https://scrap.kakaocdn.net/dn/fNmz0/hyVDy70urs/a5kocfqJpn2cqKzpO9RNAK/img.jpg?width=800&amp;amp;height=336&amp;amp;face=0_0_800_336,https://scrap.kakaocdn.net/dn/bvPMon/hyVDHw58mz/7AOvbDkJ4LCBoM4AG7CuR0/img.jpg?width=1644&amp;amp;height=880&amp;amp;face=0_0_1644_880');&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;[머신러닝 기초] 퍼셉트론부터 CNN 구조까지&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;About Perceptron 퍼셉트론이란? 다수의 신호를 입력으로 받아 하나의 결과를 내보내는 알고리즘이다. 퍼셉트론 신호는 1 또는 0의 두가지 값을 가질 수 있다. 즉, 어떠한 임계값을 기준으로 하여 신&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;joanne.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://excelsior-cjh.tistory.com/169&quot;&gt;https://excelsior-cjh.tistory.com/169&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1711329425037&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;01. 퍼셉트론 - Perceptron&quot; data-og-description=&quot;이번 포스팅은 '밑바닥 부터 시작하는 딥러닝'교재로 공부한 것을 정리했습니다. 퍼셉트론 - Perceptron 1. 퍼셉트론이란?퍼셉트론(perceptron)은 프랑크 로젠블라트(Fank Rosenblatt)가 1957년에 고안안 알&quot; data-og-host=&quot;excelsior-cjh.tistory.com&quot; data-og-source-url=&quot;https://excelsior-cjh.tistory.com/169&quot; data-og-url=&quot;https://excelsior-cjh.tistory.com/169&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dfRXhN/hyVDvQYylD/5tFf5KsBqENkw126PSDhx0/img.png?width=210&amp;amp;height=180&amp;amp;face=0_0_210_180,https://scrap.kakaocdn.net/dn/bgc6md/hyVDuEw2bl/QZyzdamvLCDrIW0VC1gLL0/img.png?width=210&amp;amp;height=180&amp;amp;face=0_0_210_180,https://scrap.kakaocdn.net/dn/u6jU6/hyVDt6FDfs/astVu62IoavHmACSCGdxQ0/img.png?width=1122&amp;amp;height=1002&amp;amp;face=0_0_1122_1002&quot;&gt;&lt;a href=&quot;https://excelsior-cjh.tistory.com/169&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://excelsior-cjh.tistory.com/169&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dfRXhN/hyVDvQYylD/5tFf5KsBqENkw126PSDhx0/img.png?width=210&amp;amp;height=180&amp;amp;face=0_0_210_180,https://scrap.kakaocdn.net/dn/bgc6md/hyVDuEw2bl/QZyzdamvLCDrIW0VC1gLL0/img.png?width=210&amp;amp;height=180&amp;amp;face=0_0_210_180,https://scrap.kakaocdn.net/dn/u6jU6/hyVDt6FDfs/astVu62IoavHmACSCGdxQ0/img.png?width=1122&amp;amp;height=1002&amp;amp;face=0_0_1122_1002');&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;01. 퍼셉트론 - Perceptron&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이번 포스팅은 '밑바닥 부터 시작하는 딥러닝'교재로 공부한 것을 정리했습니다. 퍼셉트론 - Perceptron 1. 퍼셉트론이란?퍼셉트론(perceptron)은 프랑크 로젠블라트(Fank Rosenblatt)가 1957년에 고안안 알&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;excelsior-cjh.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://compmath.korea.ac.kr/deeplearning/Perceptron.html&quot;&gt;https://compmath.korea.ac.kr/deeplearning/Perceptron.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1711329429221&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;퍼셉트론 &amp;mdash; 딥러닝 개론 및 연습  documentation&quot; data-og-description=&quot;퍼셉트론을 활용한 간단한 문제를 살펴봅니다. 첫번째로 논리곱(AND) 진리표를 살펴봅니다. 두 입력이 모두 1일 때만 1을 출력하고 그 외에는 0을 출력합니다. 논리곱(AND 게이트) 논리곱 \(x_1\) \(x_2&quot; data-og-host=&quot;compmath.korea.ac.kr&quot; data-og-source-url=&quot;https://compmath.korea.ac.kr/deeplearning/Perceptron.html&quot; data-og-url=&quot;https://compmath.korea.ac.kr/deeplearning/Perceptron.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/N2rzr/hyVDGSvPSe/YHB0ssfJdxfjUgj8jif8b1/img.png?width=2046&amp;amp;height=870&amp;amp;face=0_0_2046_870,https://scrap.kakaocdn.net/dn/ceVuD1/hyVDwPVlLv/eoKgnkK9LFcZZI5kKWwKa0/img.png?width=1235&amp;amp;height=421&amp;amp;face=0_0_1235_421,https://scrap.kakaocdn.net/dn/cNxfVP/hyVDBDFUwJ/sKyRqy6HXTHSVf75N8HJwk/img.png?width=900&amp;amp;height=493&amp;amp;face=0_0_900_493&quot;&gt;&lt;a href=&quot;https://compmath.korea.ac.kr/deeplearning/Perceptron.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://compmath.korea.ac.kr/deeplearning/Perceptron.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/N2rzr/hyVDGSvPSe/YHB0ssfJdxfjUgj8jif8b1/img.png?width=2046&amp;amp;height=870&amp;amp;face=0_0_2046_870,https://scrap.kakaocdn.net/dn/ceVuD1/hyVDwPVlLv/eoKgnkK9LFcZZI5kKWwKa0/img.png?width=1235&amp;amp;height=421&amp;amp;face=0_0_1235_421,https://scrap.kakaocdn.net/dn/cNxfVP/hyVDBDFUwJ/sKyRqy6HXTHSVf75N8HJwk/img.png?width=900&amp;amp;height=493&amp;amp;face=0_0_900_493');&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;퍼셉트론 &amp;mdash; 딥러닝 개론 및 연습 documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;퍼셉트론을 활용한 간단한 문제를 살펴봅니다. 첫번째로 논리곱(AND) 진리표를 살펴봅니다. 두 입력이 모두 1일 때만 1을 출력하고 그 외에는 0을 출력합니다. 논리곱(AND 게이트) 논리곱 \(x_1\) \(x_2&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;compmath.korea.ac.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/ML 머신러닝(Yolo)</category>
      <category>cnn</category>
      <category>머신러닝</category>
      <category>머신러닝 배경지식</category>
      <category>퍼셉트론</category>
      <author>코딩 잘 할거얌:)</author>
      <guid isPermaLink="true">https://pcseob.tistory.com/82</guid>
      <comments>https://pcseob.tistory.com/82#entry82comment</comments>
      <pubDate>Sun, 24 Mar 2024 01:23:55 +0900</pubDate>
    </item>
    <item>
      <title>Clean Architecture 클린아키텍처,  객체지향 디자인 5원칙 SOLID[5부]</title>
      <link>https://pcseob.tistory.com/81</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 객체지향 디자인 5원칙에 대해서 알아보도록 하자.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;원래 포스팅의 순서는 Folder구조를 잡고, 각 폴더 안에 들어가는 파일 class들의 예시를 보여주려고 했다. 하지만 객체지향 디자인 5원칙을 설명하지 않고는 작성하기가 힘들다 생각이 되어 이것부터 먼저 포스팅을 하도록 하겠다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;937&quot; data-origin-height=&quot;1171&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mwgJt/btsFUECSI2p/WD8jnDrbALdCKKXFBaHIs0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mwgJt/btsFUECSI2p/WD8jnDrbALdCKKXFBaHIs0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mwgJt/btsFUECSI2p/WD8jnDrbALdCKKXFBaHIs0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmwgJt%2FbtsFUECSI2p%2FWD8jnDrbALdCKKXFBaHIs0%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;937&quot; height=&quot;1171&quot; data-origin-width=&quot;937&quot; data-origin-height=&quot;1171&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;1002&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/R4vRq/btsFU0eQLA0/ngJ9fXSkt930HdTighXvX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/R4vRq/btsFU0eQLA0/ngJ9fXSkt930HdTighXvX0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/R4vRq/btsFU0eQLA0/ngJ9fXSkt930HdTighXvX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FR4vRq%2FbtsFU0eQLA0%2FngJ9fXSkt930HdTighXvX0%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;1400&quot; height=&quot;1002&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;1002&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;box-sizing: border-box; border-width: 9px; border-left-style: solid; border-left-color: #000044; padding: 1px 15px; letter-spacing: 1px; margin: 0px 0px; line-height: 1.2;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-size: 25px; font-family: 'Cafe24Ssurround'; font-weight: normal; color: #000044;&quot;&gt;SOLID란 무엇인가.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;SOLID는 위의 이미지로 유추할 수 있듯이, 5개의 원칙에서 앞에 한 글자씩 따온 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; Robert C Martin(Uncle Bob)이 제시한 객체지향설계에 대한 원칙을 5원칙으로 정리를 한 것이다&lt;/b&gt;. Uncle Bob, 익숙한 이름일 것이다. Clean Architecture를 제안한 아키텍처이다. 클린아키텍처는 헥사고날(hexagonal) 아키텍처라고 하기도하고 포트와 어댑터(Ports and Adapters) 패턴이라고도 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://sites.google.com/site/unclebobconsultingllc/getting-a-solid-start&quot;&gt;https://sites.google.com/site/unclebobconsultingllc/getting-a-solid-start&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1710943643561&quot; style=&quot;color: #333333; text-align: start;&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/hAiJj/hyVAGL6dXy/DtJgIm4YkfgskgDzA8hSC0/img.png?width=200&amp;amp;height=200&amp;amp;face=0_0_200_200&quot; data-og-url=&quot;https://sites.google.com/site/unclebobconsultingllc/getting-a-solid-start&quot; data-og-source-url=&quot;https://sites.google.com/site/unclebobconsultingllc/getting-a-solid-start&quot; data-og-host=&quot;sites.google.com&quot; data-og-description=&quot;Posted by Uncle Bob on 02/12/2009 I am often asked: &amp;ldquo;How should I get started with SOLID principles?&amp;rdquo; Given the recent interest and controversy about the issue, it&amp;rsquo;s probably time I gave a written answer. First things first. You can read about the So&quot; data-og-title=&quot;Clean Coder - Getting a SOLID start.&quot; data-og-type=&quot;website&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://sites.google.com/site/unclebobconsultingllc/getting-a-solid-start&quot; data-source-url=&quot;https://sites.google.com/site/unclebobconsultingllc/getting-a-solid-start&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/hAiJj/hyVAGL6dXy/DtJgIm4YkfgskgDzA8hSC0/img.png?width=200&amp;amp;height=200&amp;amp;face=0_0_200_200');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;Clean Coder - Getting a SOLID start.&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;Posted by Uncle Bob on 02/12/2009 I am often asked: &amp;ldquo;How should I get started with SOLID principles?&amp;rdquo; Given the recent interest and controversy about the issue, it&amp;rsquo;s probably time I gave a written answer. First things first. You can read about the So&lt;/p&gt;
&lt;p class=&quot;og-host&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;sites.google.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;SOLID라는 것은 객체지향설계를 위한 5원칙을 앞의 문자만 따서 온 것이다.&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;1. &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;Single Responsibility Principle (SRP) 단일 책임의 원칙&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;&lt;b&gt;소프트웨어 구성요소(클래스, 모듈, 함수 등)가 하나의 기능 또는 책임만을 가져야 한다는 내용이다.&lt;/b&gt; 즉 클래스나 모듈이 여러가지 목적을 수행하거나 다양한 책임을 지니지 않도록 하는 것을 의미한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;코드로 우선 살펴보도록 하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;우리가 앱을 개발할때, 로그인을 필수적으로 하는 경우가 많다. 로그인을 하면 서버에서는 액세스 토큰(Access Token)과 리프레시 토큰(Refresh Token)을 주는데, 액세스 토큰을 api 헤더에다가 넣어서 통신을 하다가 만료가 되면, 리프레시 토큰으로 액세스토큰을 새로 발급받는 형식으로 진행이 된다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;그래서 로그인하는 기능과 액세스 토큰을 새로 발급받는 메소드는 서로 다른 역할이기 때문에 다른 메서드를 이용해야 한다. 이 개념을 가지고 예시코드를 살펴보자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1711076316019&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Single Responsibility Principle를 준수하지 않는 클래스

// 사용자 로그인과 액세스 토큰을 모두 관리하는 클래스
class User {
  void login(String username, String password) {
    print('로그인: $username');
    // 로그인 로직
  }

  void refreshAccessToken(String refreshToken) {
    print('액세스 토큰 재발급');
    // 액세스 토큰 재발급 로직
  }
}


// Single Responsibility Principle를 준수하는 클래스

// 로그인을 관리하는 클래스
class LoginManager {
  void login(String username, String password) {
    print('로그인: $username');
    // 로그인 로직
  }
}

// 액세스 토큰을 관리하는 클래스
class AccessTokenManager {
  void refreshAcessToken(String refreshToken) {
    print('액세스 토큰 재발급');
    // 액세스 토큰 재발급 로직
  }
}

void main() {
  var loginManager = LoginManager();
  var accessTokenManager = AccessTokenManager();

  loginManager.login('john_doe', 'password123');
  accessTokenManager.refreshToken('refresh_token_value');
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;위 예시에서 단일 책임의 원칙에서 위배되는 코드를 먼저 보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;우선 UserManager Class에서 login과 refreshAccessToken이라는 두 메소드가 있다. 이 두 메서드는 하나의 클래스에서 로그인과 액세스 토큰을 새로 발급받는 두 가지의 기능이 존재한다. 즉 유저 클래스에서 &quot;로그인&quot; 기능과 &quot;액세스 토큰 업데이트&quot; 하는 두 가지 책임이 &quot;유저&quot; 클래스에서 존재한다. 따라서 좋은 코드라고 할 수 없다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;반면에 아래쪽에 있는 단일 책임의 원칙을 지키는 코드를 보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;LoginManger 클래스와 AccessTokenManger클래스가 각각 존재하고, 각각 클래스안에 login메서드와 refreshAcessToken이라는 메서드가 존재한다. 즉 각 클래스가 하나의 책임만을 가지는 내용이다. 따라서 단일 책임 원칙을 준수한다고 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Open/Closed Principle 개방-폐쇄 원칙&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;개방-폐쇄 원칙은 소프트웨어 개체(클래스, 모듈, 함수 등)는 확장에는 열려있어야 하지만 수정이는 닫혀있어야 한다는 원칙이다.&lt;/b&gt; 즉, 기존의 코드를 변경하지 않고도 새로운 기능을 추가할 수 있어야 한다는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어의 유지 보수를 향상시키고 코드의 재사용을 높이며, 새로운 요구 사항이나 변경이 발생하면 기존 코드를 변경하지 않고 새로운 기능을 추가하여 확장할 수 있게 한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;추상화와 다형성 활용&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인터페이스와 추상 클래스 사용&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;패턴 사용 (Strategy Pattern, Observer Pattern, Decorator Pattern 등)&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 세가지를 생각하며 코드를 작성하면 아주 지키기 쉬운 원칙이다. 바로 예시코드를 살펴보자&lt;/p&gt;
&lt;pre id=&quot;code_1711085155079&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Open/Closed Principle를 준수하지 않는 예제

// 정사각형 클래스
class Square {
  final double side;
  final double totalDegree;
  final int cornerCount;

  Square(this.side, this.totalDegree, this.conerCount);

  double area() {
    return side * side;
  }
  
  int totalDegree() {
  	return 180 * (conerCount-2);
  }
}


// Open/Closed Principle를 준수하는 예제

// 도형 추상 클래스
abstract class Shape {
  double area();
  
  int totalDegree();
}

// 원 클래스
class Circle implements Shape {
  final double radius;

  Circle(this.radius);

  @override
  double area() {
    return 3.14 * radius * radius;
  }
  
  @override
  int totalDegree() {
    return 360; // 원의 경우 내각의 합은 360도
  }
}

// 직사각형 클래스
class Rectangle implements Shape {
  final double width;
  final double height;

  Rectangle(this.width, this.height);

  @override
  double area() {
    return width * height;
  }

  @override
  int totalDegree() {
    return 360; // 직사각형의 경우 내각의 합은 360도
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;개방-폐쇄 원칙을 준수하지 않는 경우의 코드를 보자. Square의 클래스를 정의하지만, 새로운 도형을 추가할 때에는 Square코드를 상속받기가 쉽지 않다. 또한 새로운 기능이 추가될 때 마다도 코드를 변경해야 한다. 따라서 개방-폐쇄 원칙을 준수하지 않는 코드는 (다른 클래스를) 확장에는 닫혀있고, (다른 클래스의) 수정에 열려있다고 할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아래에 개방-폐쇄 원칙을 준수하는 코드를 보자. Shape라는 추상클래스를 정의하고, Circle과 Rectangle은 Shape에 상속을 받아서 정의를 하게되었다. 이런 구조에서는 새로운 도형, 삼각형을 추가한다 하더라도 기존의 코드를 변경하지 않고 triangle 클래스를 정의할 수 있고, 메서드들을 override로 새로 정의할 수 있다. 이렇게 (추가적인 클래스) 확장에는 열려있고 (다른 클래스의) 수정에는 닫혀있다고 할 수 있다.&lt;/p&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;3. Liskov Substitution Principle(LSP) 리스코프 치환 원칙&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;상위 클래스의 객체를 하위 클래스의 객체로 대체하여도 프로그램의 기능이 변경되지 않아야 한다. 즉 상속 관계에서 하위클래스는 상위클래스를 완전히 대체할 수 있어야 한다는 뜻이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 말하자면, 상위클래스를 정의하면 모든 하위클래스들은 정의된 상위클래스에 선언이 될 수 있어야한다는 뜻이다. 좀 더 풀어말하자면,&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt; 상위 클래스의 인스턴스를 사용하는 코드에서는 하위 클래스의 인스턴스로 대체해도 프로그램의 동작이 변하지 않아야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1711086239609&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  List&amp;lt;Shape&amp;gt; shapes = [
    Rectangle(5, 10),
    Square(4),
  ];&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위의 예시코드에서 이렇게 정의할 수 있어야한다는 뜻이다. shapes에 Shape를 상속받은 Rectangle과 Square가 들어갈 수 있도록 한다는 뜻이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;사실 dart언어에서는 추상화를 정상적으로 하였다면 리스코프 치환 원칙을 위반하는 경우는 거의 없다. 왜냐하면 오버로딩이 없기 때문이다. 하지만 Java언어는 오버로딩이 가능하기 때문에, 오버로딩하여 매개변수를 변경하거나 리턴타입을 바꾼다면, 리스코프 치환 원칙을 위반할 수 있다.&lt;/p&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;4. &lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;Interface Segregation Principle(ISP) 인터페이스 분리 원칙&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;&lt;b&gt;인터페이스 분리 원칙은 클라이언트가 사용하지 않는 메소드에 의존하지 않아야 한다.라는 원칙으로, 거대한 인터페이스를 작은 인터페이스들로 분리하는 것을 의미한다.&lt;/b&gt; 즉 인터페이스가 클라이언트에게 필요한 기능만 제공하도록 하는 것을 의미한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어 파일관리 인터페이스가 있다고 가정하자. 그러면 파일관리 인터페이스 안에는 &quot;파일관리&quot;에 관련된 것만 정의가 되어있어야 한다. 혹시나 폴더에 관련된 내용이 있다면 인터페이스 분리 원칙을 위배한 것이라고 할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1711118232647&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ISP 위배 예시: 하나의 인터페이스에 파일과 폴더 관리에 대한 메서드가 함께 정의되어 있음

abstract class FileManager {
  void createFile(String fileName);
  void deleteFile(String fileName);
  void createFolder(String folderName);
  void deleteFolder(String folderName);
}

class FileSystem implements FileManager {
  @override
  void createFile(String fileName) {
    print('Creating file: $fileName');
  }

  @override
  void deleteFile(String fileName) {
    print('Deleting file: $fileName');
  }

  @override
  void createFolder(String folderName) {
    print('Creating folder: $folderName');
  }

  @override
  void deleteFolder(String folderName) {
    print('Deleting folder: $folderName');
  }
}


// ISP 준수 예시: 파일과 폴더 관리에 대한 인터페이스들이 분리됨

abstract class FileManager {
  void createFile(String fileName);
  void deleteFile(String fileName);
}

abstract class FolderManager {
  void createFolder(String folderName);
  void deleteFolder(String folderName);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. Dependency Inversion 의존성 역전 원칙&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;생성자 주입(Constructor Injection)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1711122343645&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class UserManager {
  final AuthService authService;

  UserManager(this.authService);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;세터 주입(Setter Injection)&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인터페이스 주입(Interface Injection)&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;변수 주입(Variable Injection)&lt;/li&gt;
&lt;li&gt;환경 변수 주입(Environment Variable Injection)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 있다. 이중 가장 많이 사용하는건 생성자 주입법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;고수준모듈은 저수준모듈에 의존하면 안되며, 둘 다 추상화에 의존해야 한다는 원칙이다.&lt;/b&gt; 저수준 모듈이라고 하면 데이터베이스와 연결되거나 api통신하는 부분을 의미하며, 고수준 모듈은 애플리케이션의 핵심 비즈니스 로직을 다루는 부분이라고 생각하면 좋다. 즉 고수준의 모듈, 애플리케이션의 핵심 비즈니스 로직을 다루는 모듈은 저수준모듈, 데이터베이스와 연결되는 모듈에게 의존하면 안 된다는 뜻이다.&amp;nbsp;&lt;br /&gt;그러면 어떻게 의존하냐, 추상 클래스에 의존할 수 있도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;고수준 모듈이 추상화에 의존하도록  구현한다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;저수준 모듈을 추상화하고, 고수준 모듈에 주입한다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인터페이스를 통해 의존성을 주입하고, 실제 구현은 이를 구현하는 클래스에서 한다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시를 들어보자&lt;/p&gt;
&lt;pre id=&quot;code_1711121828520&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 고수준 모듈이 의존하는 인터페이스
abstract class MessageSender {
  void sendMessage(String message);
}

// 저수준 모듈이 구현하는 인터페이스
class SendService implements MessageSender {
  @override
  void sendMessage(String message) {
    // 메시지를 전송하는 로직
    print('Message sent: $message');
  }
}

// 고수준 모듈이 의존하는 저수준 모듈의 인터페이스를 주입
class ChatService {
  final MessageSender messageSender;

  ChatService(this.messageSender);

  void sendMessage(String message) {
    // 채팅 메시지를 전송하는 로직
    print('Chat message sent: $message');
    // 전송된 메시지를 전송 서비스를 통해 전송
    messageSender.sendMessage(message);
  }
}

void main() {
  // SendService를 주입하여 ChatService 인스턴스 생성
  var sendService = SendService();
  var chatService = ChatService(sendService);

  // 메시지 전송
  chatService.sendMessage('Hello, world!');
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChatService 클래스는 MessageSender 인터페이스에만 의존하고있다. 그리고 SendService의 클래스의 인스턴스를 주입받아서 사용하게 되어 의존성 역전 원칙을 준수하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 진짜로 이 모든것들을 적용해서 Flutter에서 Clean architecture를 기준으로 어떻게 구현을 하면 되는지 살펴보도록 하자.&lt;/p&gt;</description>
      <category>Study/기타</category>
      <category>clean architecture</category>
      <category>DART</category>
      <category>Flutter</category>
      <category>SOLID</category>
      <category>객체지향 프로그래밍</category>
      <category>객체지향 프로그래밍 5원칙</category>
      <category>클린 아키텍처</category>
      <category>플러터</category>
      <author>코딩 잘 할거얌:)</author>
      <guid isPermaLink="true">https://pcseob.tistory.com/81</guid>
      <comments>https://pcseob.tistory.com/81#entry81comment</comments>
      <pubDate>Sat, 23 Mar 2024 00:51:00 +0900</pubDate>
    </item>
    <item>
      <title>Clean Architecture 클린아키텍처, 객체와 객체지향 프로그래밍[4부]</title>
      <link>https://pcseob.tistory.com/80</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 객체와 객체지향 프로그래밍에 대해 알아보도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 포스팅의 순서는 Folder구조를 잡고, 각 폴더안에 들어가는 파일 class들의 예시를 보여주려고 했다. 하지만 객체지향 디자인 5원칙을 설명하지 않고는 작성하기가 힘들고, 객체지향프로그래밍에 대해서 설명이 부족하면 객체지향 디자인 5원칙이 작성하기 힘들다. 우선 객체와 객체지향 프로그래밍에 대해서 알아보도록 하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/diknuk/btsFX2XjCF2/lZ9Z1qtXiTCOZWkk1Eku2k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/diknuk/btsFX2XjCF2/lZ9Z1qtXiTCOZWkk1Eku2k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/diknuk/btsFX2XjCF2/lZ9Z1qtXiTCOZWkk1Eku2k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdiknuk%2FbtsFX2XjCF2%2FlZ9Z1qtXiTCOZWkk1Eku2k%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;600&quot; height=&quot;600&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;557&quot; data-origin-height=&quot;429&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzJMtV/btsFYpLrCeh/Qy0xbe6Y2pjIkscGTuGV81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzJMtV/btsFYpLrCeh/Qy0xbe6Y2pjIkscGTuGV81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzJMtV/btsFYpLrCeh/Qy0xbe6Y2pjIkscGTuGV81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzJMtV%2FbtsFYpLrCeh%2FQy0xbe6Y2pjIkscGTuGV81%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;557&quot; height=&quot;429&quot; data-origin-width=&quot;557&quot; data-origin-height=&quot;429&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;box-sizing: border-box; border-width: 9px; border-left-style: solid; border-left-color: #000044; padding: 1px 15px; letter-spacing: 1px; margin: 0px 0px; line-height: 1.2;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-size: 25px; font-family: 'Cafe24Ssurround'; font-weight: normal; color: #000044;&quot;&gt;객체, 객체지향 프로그래밍이란&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;객체(Object)라는 것은 클래스(Class)에서 정의한 것을 토대로 메모리에 할당된 것으로 프로그램에서 사용되는 데이터 또는 식별자에 의해 참조되는 공간을 의미하며, 변수 자료 구조, 함수 또는 메소드가 될 수 있다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좀더 쉽게 설명을 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체지향 프로그레밍에서 객체(Object)는 데이터와 해당 데이터를 처리하는 메소드(Method)의 결합체이다. 객체라는것은 현실세계에서의 실체나 개념을 모델링하는 프로그래밍 구성요소로 작용한다고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어 자동차의 객체라는게 있다고 가정해보자. 자동차의 객체의 속성으로는 브랜드, 색상, 마력 등을 가지고있고, 메소드로는 가속, 감속, 정지 등이 있을 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1710923081831&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 자동차 클래스 정의
class Car {
  // 속성(멤버 변수)
  String brand;
  String color;
  int speed;

  // 생성자
  Car(String brand, String color) {
    this.brand = brand;
    this.color = color;
    this.speed = 0; // 초기 속도는 0으로 설정
  }

  // 메서드
  void accelerate(int increment) {
    speed += increment;
    print('$brand 자동차가 $increment 만큼 가속하여 현재 속도는 $speed 입니다.');
  }

  void brake(int decrement) {
    speed -= decrement;
    print('$brand 자동차가 $decrement 만큼 감속하여 현재 속도는 $speed 입니다.');
  }
}

void main() {
  // Car 클래스의 인스턴스 생성
  Car myCar = Car('BMW', 'Blue');

  // 자동차 동작
  myCar.accelerate(50); // 가속
  myCar.brake(20);      // 감속
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면, 우리가 종종 혼동하는 클래스(class), 객체(Object)그리고 인스턴스(Instance)차이점이 뭘까?&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;클래스(Class):
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클래스는 객체를 만들기 위한 설계도 또는 틀이다&lt;/li&gt;
&lt;li&gt;객체들이 가져야 할 속성(데이터)과 행동(메서드)을 정의한다.&lt;/li&gt;
&lt;li&gt;클래스는 추상적인 개념으로, 실제로 메모리에 할당되지 않는다.&lt;/li&gt;
&lt;li&gt;예를 들어, 자동차 클래스는 자동차의 속성(브랜드, 색상 등)과 메서드(가속, 감속 등)를 정의한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;객체(Object):
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체는 클래스의 인스턴스(instance)이다.&lt;/li&gt;
&lt;li&gt;클래스를 바탕으로 실제로 메모리에 할당된 것으로, 데이터와 메서드를 포함한다.&lt;/li&gt;
&lt;li&gt;객체는 클래스의 속성과 메서드에 대한 실제 값이나 동작을 갖는다.&lt;/li&gt;
&lt;li&gt;예를 들어, 자동차 클래스를 기반으로 생성된 특정 자동차 객체는 실제 자동차라고 할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;인스턴스(Instance):
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인스턴스는 클래스를 기반으로 생성된 개별 객체를 가리킨다.&lt;/li&gt;
&lt;li&gt;클래스의 구조를 가지고 있지만 고유한 데이터를 가질 수 있다.&lt;/li&gt;
&lt;li&gt;한 클래스로부터 여러 개의 인스턴스를 생성할 수 있다.&lt;/li&gt;
&lt;li&gt;예를 들어, &quot;bmw&quot;와 &quot;audi&quot;는 자동차 클래스의 인스턴스. &quot;bmw&quot;와 &quot;audi&quot;는 모두 자동차 클래스의 특정 객체들이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;간단히 말하면, 클래스는 객체를 만들기 위한 설계도이고, 객체는 실제로 클래스에 의해 만들어진 것이며, 인스턴스는 클래스로부터 생성된 개별 객체를 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;b&gt;객체지향프로그래밍(Object-Oriented Programming, OOP)은 소프트웨어 개발에서 사용되는 중요한 프로그래밍 패러다임 중 하나로, 이 접근 방식은 현실 세계의 객체들을 소프트웨어적으로 모델링하고, 이러한 객체들 간의 상호 작용을 기반으로 프로그램을 구성한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체지향 프로그래밍에서 사용되는 주요개념들을 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 객체(Object): 현실 세계에서 식별 가능한 것. 예를 들어, 자동차, 사람, 주문 등이 객체가 될 수 있습니다. 각 객체는 특징과 동작을 가지고 있다.&lt;br /&gt;&lt;br /&gt;2. 클래스(Class): 객체를 생성하기 위한 설계 도면이며, 객체의 특징과 동작을 정의한다. 클래스는 객체들이 공통으로 가지는 속성과 메서드를 정의한다.&lt;br /&gt;&lt;br /&gt;3. 상속(Inheritance): 클래스들 간의 관계를 정의할 때 사용된다. 한 클래스가 다른 클래스의 특성과 동작을 상속받아 확장할 수 있다. 이를 통해 코드 재사용성과 계층적 구조를 구현할 수  있다.&lt;br /&gt;&lt;br /&gt;4. 다형성(Polymorphism): 다형성은 같은 이름의 메서드나 함수가 서로 다른 동작을 할 수 있는 능력을 의미한다. 상속과 밀접하게 연관이 되어있고, 같은 인터페이스를 가진 다양한 구현을 한다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;5. 캡슐화(Encapsulation): 객체의 상태와 동작을 외부로부터 감추는 것을 의미하며. 이는 정보 은닉의 개념과도 관련이 있으며, 객체의 내부 상태는 외부에서 직접 접근할 수 없도록 보호가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 다섯가지를 dart언어로 구현해보았다.&lt;/p&gt;
&lt;pre id=&quot;code_1710944468950&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 다형성을 위한 추상 클래스 (Abstract Class)

abstract class Product {
  // 추상 메서드 (Abstract Method)
  void displayInfo(); // 제품 정보를 출력하는 메서드
}

// 부모 클래스 (Parent Class)
class Car implements Product {
  // 속성(멤버 변수)
  String brand; // 객체 속성: 자동차의 브랜드
  String color; // 객체 속성: 자동차의 색상
  late int speed; // 객체 속성: 자동차의 속도

  // 생성자
  Car(this.brand, this.color) {
    // 클래스의 생성자

    speed = 0; // 초기 속도는 0으로 설정
  }

  // 메서드
  void accelerate(int increment) {
    // 메서드: 자동차를 가속시키는 행동
    speed += increment;
    print('$brand 자동차가 $increment 만큼 가속하여 현재 속도는 $speed 입니다.');
  }

  void brake(int decrement) {
    // 메서드: 자동차를 감속시키는 행동
    speed -= decrement;
    if (speed &amp;lt; 0) {
      speed = 0; // 음수 속도를 허용하지 않음
    }
    print('$brand 자동차가 $decrement 만큼 감속하여 현재 속도는 $speed 입니다.');
  }

  // Product 클래스의 메서드 구현
  @override
  void displayInfo() {
    print('제품 정보: $brand 자동차, 색상: $color');
  }
}

// 상속을 받기 위한 자식 클래스 (Child Class)
class ElectricCar extends Car {
  // 생성자
  ElectricCar(super.brand, super.color); // 생성자: 부모 클래스의 생성자를 호출하여 상속받은 속성 초기화

  // 추가적인 동작
  void charge() {
    // 메서드: 전기차를 충전하는 행동
    print('$brand 전기차가 충전 중입니다.');
  }
}

void main() {
  // Car 클래스의 인스턴스 생성
  Car myCar = Car('BMW', 'Blue');

  myCar.accelerate(50); // 가속
  myCar.brake(20); // 감속

  // Product 클래스의 메서드 호출
  myCar.displayInfo(); // 제품 정보 출력

  // ElectricCar 클래스의 인스턴스 생성
  ElectricCar myElectricCar = ElectricCar('Tesla', 'Red');

  // 부모 클래스의 메서드 호출
  myElectricCar.accelerate(50); // 가속
  myElectricCar.brake(20); // 감속

  // Product 클래스의 메서드 호출
  myElectricCar.displayInfo(); // 제품 정보 출력

  // 자식 클래스의 메서드 호출
  myElectricCar.charge(); // 객체의 메서드 호출
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체지향 프로그래밍을 최대한 dart언어로 구현해보았다. 너무 어려운건 없다. 익숙해지면 쉬운내용이므로 개발하면서 최대한 적용해보면서 코딩하도록 하자!&lt;/p&gt;</description>
      <category>Study/기타</category>
      <category>clean architecture</category>
      <category>Object</category>
      <category>Object-Oriented</category>
      <category>OOP</category>
      <category>객체</category>
      <category>객체지향</category>
      <category>객체지향 프로그래밍</category>
      <category>클린아키텍처</category>
      <author>코딩 잘 할거얌:)</author>
      <guid isPermaLink="true">https://pcseob.tistory.com/80</guid>
      <comments>https://pcseob.tistory.com/80#entry80comment</comments>
      <pubDate>Wed, 20 Mar 2024 23:30:23 +0900</pubDate>
    </item>
  </channel>
</rss>