<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>개발자노트 조현진</title>
    <link>https://chohyeonjin.tistory.com/</link>
    <description>chohyeonjin 님의 블로그 입니다.</description>
    <language>ko</language>
    <pubDate>Tue, 14 Apr 2026 04:40:30 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>개발자노트 조현진</managingEditor>
    <item>
      <title>SSE(Server-Sent Events) 채팅 시스템</title>
      <link>https://chohyeonjin.tistory.com/145</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;사전 기반 지식&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;1단계 폴링 시스템&lt;/b&gt; 완전 이해 (MVC 패턴, JPA, Mustache)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;HTTP 통신의 한계:&lt;/b&gt; 클라이언트가 먼저 요청해야만 응답 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;JavaScript 기초:&lt;/b&gt; EventSource API와 이벤트 처리&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;b&gt; 폴링 vs SSE 비교&lt;/b&gt;&lt;/p&gt;
&lt;table id=&quot;2629fed1-6934-803f-a122-c1cf76bc6805&quot; style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr id=&quot;2629fed1-6934-8023-b53d-d3b331ea9b00&quot;&gt;
&lt;td id=&quot;TmzI&quot;&gt;&lt;b&gt;통신 방향&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;IYA?&quot;&gt;클라이언트 &amp;rarr; 서버 (요청/응답)&lt;/td&gt;
&lt;td id=&quot;_@Px&quot;&gt;서버 &amp;rarr; 클라이언트 (푸시)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2629fed1-6934-801c-a1ab-ea968e15e9a0&quot;&gt;
&lt;td id=&quot;TmzI&quot;&gt;&lt;b&gt;연결 방식&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;IYA?&quot;&gt;매번 새로운 HTTP 연결&lt;/td&gt;
&lt;td id=&quot;_@Px&quot;&gt;하나의 지속적인 HTTP 연결&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2629fed1-6934-801c-99ec-f646b2bf940f&quot;&gt;
&lt;td id=&quot;TmzI&quot;&gt;&lt;b&gt;실시간성&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;IYA?&quot;&gt;폴링 주기에 따른 지연&lt;/td&gt;
&lt;td id=&quot;_@Px&quot;&gt;즉시 전달 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2629fed1-6934-8057-860d-e8efdabf7b47&quot;&gt;
&lt;td id=&quot;TmzI&quot;&gt;&lt;b&gt;서버 부하&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;IYA?&quot;&gt;높음 (불필요한 요청)&lt;/td&gt;
&lt;td id=&quot;_@Px&quot;&gt;낮음 (필요시에만 전송)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2629fed1-6934-8050-a5e2-c3b088397e4f&quot;&gt;
&lt;td id=&quot;TmzI&quot;&gt;&lt;b&gt;네트워크 효율&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;IYA?&quot;&gt;비효율적&lt;/td&gt;
&lt;td id=&quot;_@Px&quot;&gt;효율적&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2629fed1-6934-8039-b7ae-c5347a5c3d22&quot;&gt;
&lt;td id=&quot;TmzI&quot;&gt;&lt;b&gt;구현 복잡도&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;IYA?&quot;&gt;간단&lt;/td&gt;
&lt;td id=&quot;_@Px&quot;&gt;중간&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SSE의 주요 활용 사례&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;: 새로운 메시지, 친구 요청, 좋아요 등 서버에서 발생하는 이벤트들을 사용자에게 즉시 알릴 때 사용됩니다.&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;: 배달 앱에서 주문 상태가 '접수 완료' &amp;rarr; '배달 중' &amp;rarr; '배달 완료' 등으로 변경될 때, 클라이언트 화면을 실시간으로 업데이트하는 데 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 기능들은 모두 클라이언트가 서버에 데이터를 보낼 필요 없이, 오직 서버로부터 데이터를 받기만 하면 되기 때문에 &lt;b&gt;단방향 통신&lt;/b&gt;에 최적화된 &lt;b&gt;SSE&lt;/b&gt;가 효율적이고 정석적인 방법이 될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SSE 통신 흐름&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;클라이언트:&lt;/b&gt; new EventSource('/sse/connect') &amp;rarr; 서버에 SSE 연결 요청&lt;/li&gt;
&lt;li&gt;&lt;b&gt;서버:&lt;/b&gt; SseEmitter 생성 &amp;rarr; 클라이언트와 지속적 연결 유지&lt;/li&gt;
&lt;li&gt;&lt;b&gt;메시지 작성:&lt;/b&gt; 사용자가 새 메시지 POST &amp;rarr; 서버에 저장&lt;/li&gt;
&lt;li&gt;&lt;b&gt;브로드캐스트:&lt;/b&gt; sseService.broadcastMessage() &amp;rarr; 모든 연결된 클라이언트에 즉시 전송&lt;/li&gt;
&lt;li&gt;&lt;b&gt;클라이언트:&lt;/b&gt; eventSource.addEventListener('newMessage') &amp;rarr; 새 메시지 실시간 수신&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SSE의 한계와 WebSocket의 필요성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSE는 폴링보다 효율적이지만 여전히 &lt;b&gt;단방향 통신&lt;/b&gt;의 한계가 있습니다. 채팅에서 사용자가 &quot;입력 중...&quot; 상태를 보여주거나, 실시간 게임과 같은 양방향 상호작용이 필요한 경우에는 &lt;b&gt;WebSocket&lt;/b&gt;이 더 적합합니다.&lt;/p&gt;</description>
      <category>SpringBoot</category>
      <author>개발자노트 조현진</author>
      <guid isPermaLink="true">https://chohyeonjin.tistory.com/145</guid>
      <comments>https://chohyeonjin.tistory.com/145#entry145comment</comments>
      <pubDate>Tue, 2 Sep 2025 15:48:54 +0900</pubDate>
    </item>
    <item>
      <title>폴링 채팅 시스템</title>
      <link>https://chohyeonjin.tistory.com/144</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;사전 기반 지식&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Spring Boot 기본:&lt;/b&gt; @Controller, @Service, @Repository 어노테이션 이해&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Spring Data JPA:&lt;/b&gt; Entity, Repository 패턴 숙지&lt;/li&gt;
&lt;li&gt;&lt;b&gt;HTTP 기본:&lt;/b&gt; GET, POST 요청/응답 이해&lt;/li&gt;
&lt;li&gt;&lt;b&gt;HTML/JavaScript 기초:&lt;/b&gt; 기본 DOM 조작과 이벤트 처리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Mustache 템플릿 엔진:&lt;/b&gt; 기본 문법 ({{}}, {{#}})&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; 폴링(Polling)이란?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;폴링은 클라이언트가 서버에 &lt;span data-token-index=&quot;1&quot;&gt;주기적으로 요청&lt;/span&gt;을 보내 새로운 데이터가 있는지 확인하는 통신 방식입니다. &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;폴링(Polling)이라는 용어는 클라이언트가 서버로부터 데이터를 &lt;b&gt;계속해서 당겨오는(pulling)&lt;/b&gt; 행위에서 유래했습니다. 즉, 클라이언트가 서버에게 &quot;새로운 데이터가 있나요?&quot;라고 &lt;b&gt;지속적으로 질문을 던져 응답을 수집하는&lt;/b&gt; 행위를 말합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;장점:&lt;/b&gt; 구현이 간단하고, HTTP 기반이므로 방화벽 문제에서 자유롭습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;단점:&lt;/b&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; 동시 접속자가 많을수록 서버에 과도한 부하를 줄 수 있습니다. 예를 들어, 1,000명의 사용자가 2초마다 요청하면 서버는 2초마다 1,000개의 요청을 처리해야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;실시간성 제한:&lt;/b&gt; 데이터가 즉시 전달되지 않고, 폴링 주기에 따라 지연이 발생합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;시스템의 주요 기능&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;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; 웹 페이지는 2초마다 자동으로 새로고침되어 새로운 메시지가 있는지 확인하고 화면에 표시합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>SpringBoot</category>
      <author>개발자노트 조현진</author>
      <guid isPermaLink="true">https://chohyeonjin.tistory.com/144</guid>
      <comments>https://chohyeonjin.tistory.com/144#entry144comment</comments>
      <pubDate>Tue, 2 Sep 2025 15:45:33 +0900</pubDate>
    </item>
    <item>
      <title>상태 관리란 뭘까?</title>
      <link>https://chohyeonjin.tistory.com/143</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #cd3c3a;&quot; data-token-index=&quot;0&quot;&gt;공유 상태가 변경될 때,&lt;/span&gt; &lt;span style=&quot;color: #cd3c3a;&quot; data-token-index=&quot;2&quot;&gt;동기화 하는 것&lt;/span&gt;을 &lt;span style=&quot;color: #9065b0;&quot; data-token-index=&quot;4&quot;&gt;상태 관리(State Management)&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;데이터 일관성을 잃게 됩니다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;유저가 프로필을 변경해도 다른 화면에서 이전 프로필이 보이는 문제&lt;/li&gt;
&lt;li&gt;쇼핑 바구니에 표시된 상품의 개수와 실제 담긴 상품 개수가 다른 문제&lt;/li&gt;
&lt;li&gt;글을 작성했는데, 게시판에 반영되지 않는 문제(Create)&lt;/li&gt;
&lt;li&gt;글을 수정했는데, 게시판에 반영되지 않는 문제(Update)&lt;/li&gt;
&lt;li&gt;글을 삭제했는데, 게시판에 반영되지 않는 문제(Delete)&lt;/li&gt;
&lt;/ul&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;496&quot; data-origin-height=&quot;179&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkD7Qa/btsP0QUwpad/8PvJL3KArrV0oDmM3xeRn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkD7Qa/btsP0QUwpad/8PvJL3KArrV0oDmM3xeRn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkD7Qa/btsP0QUwpad/8PvJL3KArrV0oDmM3xeRn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkD7Qa%2FbtsP0QUwpad%2F8PvJL3KArrV0oDmM3xeRn0%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;496&quot; height=&quot;179&quot; data-origin-width=&quot;496&quot; data-origin-height=&quot;179&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;650&quot; data-origin-height=&quot;374&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cOgPGv/btsP1YqJw7x/t71DRzQhwORtb59lbOgjs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cOgPGv/btsP1YqJw7x/t71DRzQhwORtb59lbOgjs0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cOgPGv/btsP1YqJw7x/t71DRzQhwORtb59lbOgjs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcOgPGv%2FbtsP1YqJw7x%2Ft71DRzQhwORtb59lbOgjs0%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;650&quot; height=&quot;374&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;374&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Flutter</category>
      <author>개발자노트 조현진</author>
      <guid isPermaLink="true">https://chohyeonjin.tistory.com/143</guid>
      <comments>https://chohyeonjin.tistory.com/143#entry143comment</comments>
      <pubDate>Thu, 21 Aug 2025 17:31:04 +0900</pubDate>
    </item>
    <item>
      <title>MVVM 패턴과 상태 관리</title>
      <link>https://chohyeonjin.tistory.com/142</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; Monolithic (모노리스) 구조의 특징&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 코드 파일에 &lt;span data-token-index=&quot;1&quot;&gt;UI, 비즈니스 로직, 프레젠테이션 로직&lt;/span&gt;을 모두 넣는 형식을 흔히 &lt;span data-token-index=&quot;3&quot;&gt;Monolithic Architecture&lt;/span&gt; 또는 간단히 &lt;span data-token-index=&quot;5&quot;&gt;Monolith&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;먼저 MVVM 패턴 없이 &lt;span data-token-index=&quot;1&quot;&gt;간단한 구조&lt;/span&gt;로 코드를 작성해보자. 모든 로직과 상태 관리를 하나의 파일에 통합하여, &lt;span data-token-index=&quot;3&quot;&gt;UI와 데이터 처리가 한 클래스에서 이루어지는 방식&lt;/span&gt;으로 코드를 작성할 수 있다. 이 방식은 MVVM과 같은 디자인 패턴이 없어도 간단한 앱에서는 빠르게 개발할 수 있는 장점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;MVVM에 대한 기본 개념 잡기 &lt;/b&gt;&lt;/li&gt;
&lt;li&gt;MVVM(Model-View-ViewModel)은 소프트웨어 개발에서 널리 사용되는 아키텍처 패턴 중 하나입니다. 플러터에서 MVVM을 적용하는 것은 앱의 구조를 명확하게 하고, 코드의 재사용성과 테스트 용이성을 높여줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;MVVM은 세 가지 주요 구성 요소&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVVM은 소프트웨어를 Model / View / ViewModel로 관심사 분리하여 &lt;b&gt;View를 쉽게 변경할 수 있도록 만들어줍니다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;View :&lt;/b&gt; UI 담당&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ViewModel :&lt;/b&gt; View 상태 및 로직 담당&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Model :&lt;/b&gt; 비즈니스 로직 &amp;amp; 데이터 입출력 담당&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bF2ntE/btsPXIaSjX6/ldqyhXeKPL9SEPeoYcRyWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bF2ntE/btsPXIaSjX6/ldqyhXeKPL9SEPeoYcRyWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bF2ntE/btsPXIaSjX6/ldqyhXeKPL9SEPeoYcRyWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbF2ntE%2FbtsPXIaSjX6%2FldqyhXeKPL9SEPeoYcRyWk%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;730&quot; height=&quot;240&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Flutter</category>
      <author>개발자노트 조현진</author>
      <guid isPermaLink="true">https://chohyeonjin.tistory.com/142</guid>
      <comments>https://chohyeonjin.tistory.com/142#entry142comment</comments>
      <pubDate>Tue, 19 Aug 2025 16:35:56 +0900</pubDate>
    </item>
    <item>
      <title>SpringBoot Base64</title>
      <link>https://chohyeonjin.tistory.com/141</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Base64란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Base64는 바이너리 데이터(이미지, 파일 등)를 텍스트로 변환하는 인코딩 방식입니다. 64개의 ASCII 문자(A-Z, a-z, 0-9, +, /)와 패딩 문자(=)를 사용해 안전하게 전송할 수 있게 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 왜 사용하나요?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;텍스트만 허용하는 시스템(예: JSON, 이메일, HTTP)에서 바이너리 데이터를 보낼 때.&lt;/li&gt;
&lt;li&gt;데이터 손상 방지: 특수 문자가 바이너리에 섞이지 않음.&lt;/li&gt;
&lt;li&gt;예: 플러터 앱에서 파일을 Base64로 변환해 서버로 업로드.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 어떻게 작동하나요?&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;바이너리 데이터를 8비트 바이트로 봅니다.&lt;/li&gt;
&lt;li&gt;6비트 단위로 나눕니다 (3바이트 = 24비트 = 4개의 6비트).&lt;/li&gt;
&lt;li&gt;각 6비트를 0-63 숫자로 변환 후, Base64 테이블에서 문자로 매핑.&lt;/li&gt;
&lt;li&gt;나머지 비트가 부족하면 =로 패딩.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;도식화 (간단 예: &quot;Man&quot; 문자열)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원본: M a n (ASCII: 77, 97, 110 &amp;rarr; 바이너리: 01001101 01100001 01101110)&lt;/li&gt;
&lt;li&gt;6비트 나누기: 010011 010110 000101 101110&lt;/li&gt;
&lt;li&gt;10진수: 19, 22, 5, 46&lt;/li&gt;
&lt;li&gt;Base64 매핑: T W F u&lt;/li&gt;
&lt;li&gt;결과: TWFu&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Base64 역직렬화(디코딩)는 Base64 문자열을 원본 바이너리로 복원합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Base64 문자열을 4문자(24비트) 단위로 나눕니다.&lt;/li&gt;
&lt;li&gt;각 문자를 6비트(0-63)로 매핑 후 결합해 8비트 바이트로 재구성.&lt;/li&gt;
&lt;li&gt;패딩(=) 무시.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;도식화 (&quot;TWFu&quot; 디코딩 예):&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Base64: T W F u (인덱스: 19, 22, 5, 46 &amp;rarr; 6비트: 010011 010110 000101 101110)&lt;/li&gt;
&lt;li&gt;8비트 재구성: 01001101 01100001 01101110 (ASCII: 77, 97, 110 &amp;rarr; &quot;Man&quot;)&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>SpringBoot</category>
      <author>개발자노트 조현진</author>
      <guid isPermaLink="true">https://chohyeonjin.tistory.com/141</guid>
      <comments>https://chohyeonjin.tistory.com/141#entry141comment</comments>
      <pubDate>Fri, 8 Aug 2025 14:24:36 +0900</pubDate>
    </item>
    <item>
      <title>Flutter Key 및 상태 관리에 기본 개념</title>
      <link>https://chohyeonjin.tistory.com/140</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Flutter의 세 가지 트리 구조&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개요&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter는 UI를 효율적으로 렌더링하기 위해 세 가지 트리 구조를 사용합니다. 이 구조들은 성능 최적화와 재사용성을 위해 설계되었으며, 개발자는 주로 Widget Tree만 직접 다룹니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;세 가지 트리의 역할&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Widget Tree&lt;/b&gt;: 개발자가 작성한 UI 구조 (불변객체)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Element Tree&lt;/b&gt;: Widget과 RenderObject를 연결하는 중간 계층 (상태 유지)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;RenderObject Tree&lt;/b&gt;: 실제 화면에 그려지는 객체들 (렌더링 담당)&lt;br /&gt;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;659&quot; data-origin-height=&quot;404&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kQzES/btsPJ2ImIEQ/Alfu2QKVEW7dLexMIZrIO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kQzES/btsPJ2ImIEQ/Alfu2QKVEW7dLexMIZrIO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kQzES/btsPJ2ImIEQ/Alfu2QKVEW7dLexMIZrIO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkQzES%2FbtsPJ2ImIEQ%2FAlfu2QKVEW7dLexMIZrIO1%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;659&quot; height=&quot;404&quot; data-origin-width=&quot;659&quot; data-origin-height=&quot;404&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;664&quot; data-origin-height=&quot;585&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjJGVn/btsPMYEaG9n/JV07UXayKK2hwexXjFRtT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjJGVn/btsPMYEaG9n/JV07UXayKK2hwexXjFRtT1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjJGVn/btsPMYEaG9n/JV07UXayKK2hwexXjFRtT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjJGVn%2FbtsPMYEaG9n%2FJV07UXayKK2hwexXjFRtT1%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;664&quot; height=&quot;585&quot; data-origin-width=&quot;664&quot; data-origin-height=&quot;585&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;618&quot; data-origin-height=&quot;497&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FoHlD/btsPNh4wUK4/0dWp1mSMOHVc8BqLhhJWk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FoHlD/btsPNh4wUK4/0dWp1mSMOHVc8BqLhhJWk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FoHlD/btsPNh4wUK4/0dWp1mSMOHVc8BqLhhJWk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFoHlD%2FbtsPNh4wUK4%2F0dWp1mSMOHVc8BqLhhJWk1%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;618&quot; height=&quot;497&quot; data-origin-width=&quot;618&quot; data-origin-height=&quot;497&quot;/&gt;&lt;/span&gt;&lt;/figure&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;Widget&lt;/b&gt;: UI 설계도 (불변 청사진)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Element&lt;/b&gt;: 런타임에서 생성된 살아있는 구조물 (가변 인스턴스)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;역할&lt;/b&gt;: &lt;b&gt;위치, 타입, 상태를 저장&lt;/b&gt;하고 RenderObject와 연결&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;중요한 특징:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;State 객체는 Element에 의해 관리됩니다&lt;/li&gt;
&lt;li&gt;StatefulWidget은 StatefulElement를, StatelessWidget은 StatelessElement를 가집니다&lt;/li&gt;
&lt;li&gt;Flutter는 성능을 위해 Element와 RenderObject를 재활용합니다&lt;/li&gt;
&lt;/ul&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;Widget&lt;/td&gt;
&lt;td&gt;저렴&lt;/td&gt;
&lt;td&gt;자주 발생&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Element&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;td&gt;최소화됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RenderObject&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;td&gt;최소화됨&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;canUpdate() 메서드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter는 Widget.canUpdate() 메서드를 사용하여 Element 재사용 여부를 결정합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;재사용 판단 과정&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;프레임 빌드 시&lt;/b&gt;: Element Tree의 각 노드를 검사&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;타입과 키가 같으면 &amp;rarr; Element 재사용, 참조만 업데이트&lt;/li&gt;
&lt;li&gt;다르면 &amp;rarr; 기존 Element 제거 후 새로 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&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; Key를 사용한 문제 해결&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;p data-ke-size=&quot;size16&quot;&gt;플러터는 프레임을 빌드할 때 각 요소와 해당 위젯을 확인하여 해당 요소를 재사용할 수 있는지 판단합니다. 목록의 경우, 플러터는 자식 요소 내에서 일치하는 위젯 유형과 키를 확인합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; Key의 작동 원리&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;runtimeType&lt;/b&gt; (클래스 타입)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;key&lt;/b&gt; (고유 식별자)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Key의 작동 방식&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter는 Key를 통해 위젯을 고유하게 식별합니다. &lt;b&gt;같은 타입이라도 Key가 다르면 Flutter는 이를 별개의 위젯으로 인식&lt;/b&gt;하고, 다른 상태를 유지하도록 처리합니다. 따라서 Key가 있는 위젯의 순서가 변경되면 Flutter는 새로운 위치에 있는 위젯의 상태를 유지하도록 관리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;핵심 정리&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Key 사용의 이점&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;성능 최적화&lt;/b&gt;: 불필요한 Element 재생성 방지&lt;/li&gt;
&lt;li&gt;&lt;b&gt;상태 보존&lt;/b&gt;: 위젯 순서 변경 시에도 올바른 상태 유지&lt;/li&gt;
&lt;li&gt;&lt;b&gt;메모리 효율성&lt;/b&gt;: Element와 RenderObject 재사용으로 메모리 절약&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주요 개념 복습&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Widget Tree&lt;/b&gt;: 개발자가 작성하는 UI 설계도 (불변)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Element Tree&lt;/b&gt;: Widget의 실제 구현체 (상태 유지)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;RenderObject Tree&lt;/b&gt;: 실제 렌더링 담당&lt;/li&gt;
&lt;li&gt;&lt;b&gt;canUpdate()&lt;/b&gt;: 타입과 키를 비교하여 Element 재사용 결정&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Key의 역할&lt;/b&gt;: 위젯을 고유하게 식별하여 올바른 Element 매칭&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter의 Key는 단순해 보이지만 성능과 사용자 경험에 큰 영향을 미치는 중요한 개념입니다. 올바른 Key 사용을 통해 효율적이고 안정적인 Flutter 앱을 개발할 수 있습니다.&lt;/p&gt;</description>
      <category>Flutter</category>
      <author>개발자노트 조현진</author>
      <guid isPermaLink="true">https://chohyeonjin.tistory.com/140</guid>
      <comments>https://chohyeonjin.tistory.com/140#entry140comment</comments>
      <pubDate>Fri, 8 Aug 2025 14:23:03 +0900</pubDate>
    </item>
    <item>
      <title>GestureDetector 위젯</title>
      <link>https://chohyeonjin.tistory.com/139</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;개요&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GestureDetector는 사용자가 화면에서 수행하는 다양한 터치 이벤트를 감지하고 처리하는 기본적인 Flutter 위젯입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;특징&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GestureDetector 자체는 화면에 표시되지 않으며, child에 지정된 위젯에 사용자 이벤트가 발생할 때 이벤트를 처리할 수 있습니다&lt;/li&gt;
&lt;li&gt;다양한 사용자 이벤트에 대한 콜백 함수들로 구성되어 있어, 각 이벤트에 맞는 동작을 정의할 수 있습니다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주요 콜백 함수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;onTap&lt;/td&gt;
&lt;td&gt;사용자가 화면을 가볍게 탭할 때 호출&lt;/td&gt;
&lt;td&gt;버튼 클릭 효과&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;onDoubleTap&lt;/td&gt;
&lt;td&gt;사용자가 화면을 빠르게 두 번 탭할 때 호출&lt;/td&gt;
&lt;td&gt;이미지 확대&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;onLongPress&lt;/td&gt;
&lt;td&gt;사용자가 화면을 오래 누르고 있을 때 호출&lt;/td&gt;
&lt;td&gt;아이템 삭제 옵션 표시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;onTapDown&lt;/td&gt;
&lt;td&gt;사용자가 화면을 터치하기 시작할 때 호출&lt;/td&gt;
&lt;td&gt;버튼을 누르는 즉시 효과 적용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;onTapUp&lt;/td&gt;
&lt;td&gt;사용자가 터치한 손가락을 화면에서 떼었을 때 호출&lt;/td&gt;
&lt;td&gt;클릭 완료&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;onVerticalDragStart&lt;/td&gt;
&lt;td&gt;사용자가 화면을 위아래로 드래그하기 시작할 때 호출&lt;/td&gt;
&lt;td&gt;목록 스크롤 시작 감지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;onHorizontalDragStart&lt;/td&gt;
&lt;td&gt;사용자가 화면을 좌우로 드래그하기 시작할 때 호출&lt;/td&gt;
&lt;td&gt;슬라이드 메뉴 호출&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description>
      <category>Flutter</category>
      <author>개발자노트 조현진</author>
      <guid isPermaLink="true">https://chohyeonjin.tistory.com/139</guid>
      <comments>https://chohyeonjin.tistory.com/139#entry139comment</comments>
      <pubDate>Fri, 8 Aug 2025 12:52:07 +0900</pubDate>
    </item>
    <item>
      <title>dart 비동기 프로그래밍</title>
      <link>https://chohyeonjin.tistory.com/138</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; dart 비동기 프로그래밍에 대한 개념을 이해하자.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동기성 - 모든 코드가 순차적으로 진행되는 형태&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 - 코드가 동시다발적으로 실행, 순착적으로 보장을 할 수 없는 형태&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dart 비동기 프로그래밍은 프로그램의 흐름을 중단시키지 않고, 무언가의 완료를 기다리는 동안 다른 작업을 수행할 수 있게 해줍니다. Dart에서는 이를 위해 Future와 Stream이라는 두 가지 주요 개념을 제공합니다.&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;Future 타입이 뭘까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Future 객체는 미래에 어떤 값이나 오류를 반환할 것이라는 약속을 나타냅니다. async 키워드가 붙은 함수 내부에서 await 키워드를 사용하면, Future가 완료될 때까지 실행을 잠시 멈추고 결과가 준비되면 다시 실행을 계속합니다&lt;/p&gt;</description>
      <category>Flutter</category>
      <author>개발자노트 조현진</author>
      <guid isPermaLink="true">https://chohyeonjin.tistory.com/138</guid>
      <comments>https://chohyeonjin.tistory.com/138#entry138comment</comments>
      <pubDate>Fri, 8 Aug 2025 12:50:05 +0900</pubDate>
    </item>
    <item>
      <title>Flutter에서 콜백함수</title>
      <link>https://chohyeonjin.tistory.com/137</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; 콜백 함수란?&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;208&quot; data-start=&quot;85&quot; data-ke-size=&quot;size16&quot;&gt;콜백 함수(callback function)는 말 그대로 나중에 불려지는 함수입니다.&lt;br /&gt;보통 다른 함수(또는 위젯)에 인자로 넘겨서, 그 함수가 필요한 시점에 대신 호출해주는 방식이에요.&lt;/p&gt;
&lt;p data-end=&quot;208&quot; data-start=&quot;85&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;208&quot; data-start=&quot;85&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그럼 왜 콜백함수를 쓸까?&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;310&quot; data-start=&quot;235&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;251&quot; data-start=&quot;235&quot;&gt;버튼을 눌렀을 때&lt;/li&gt;
&lt;li data-end=&quot;268&quot; data-start=&quot;252&quot;&gt;값이 바뀌었을 때&lt;/li&gt;
&lt;li data-end=&quot;286&quot; data-start=&quot;269&quot;&gt;무언가가 끝났을 때&lt;/li&gt;
&lt;li data-end=&quot;310&quot; data-start=&quot;287&quot;&gt;자식 위젯이 부모에게 알릴 때&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-end=&quot;365&quot; data-start=&quot;312&quot; data-ke-size=&quot;size16&quot;&gt;이처럼 특정 상황이 생기면 실행되어야 하는 작업을 미리 정해두기 위해 콜백 함수를 씁니다.&lt;/p&gt;
&lt;p data-end=&quot;365&quot; data-start=&quot;312&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;365&quot; data-start=&quot;312&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; Flutter에서는 어떻게 쓰일까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;365&quot; data-start=&quot;312&quot; data-ke-size=&quot;size16&quot;&gt;Flutter는 위젯 중심의 프레임워크라서,&lt;br /&gt;콜백 함수는 보통 이벤트 처리에 많이 사용됩니다.&lt;/p&gt;
&lt;p data-end=&quot;365&quot; data-start=&quot;312&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;365&quot; data-start=&quot;312&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;한줄로 정리하자면&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;365&quot; data-start=&quot;312&quot; data-ke-size=&quot;size16&quot;&gt;콜백 함수는 나중에 실행될 함수를 다른 함수나 위젯에 넘겨주는 것입니다.&lt;/p&gt;
&lt;p data-end=&quot;365&quot; data-start=&quot;312&quot; data-ke-size=&quot;size16&quot;&gt;Flutter에서는 이걸로 버튼 누르기, 값 바꾸기, 사용자 반응 처리를 합니다.&lt;/p&gt;</description>
      <category>Flutter</category>
      <author>개발자노트 조현진</author>
      <guid isPermaLink="true">https://chohyeonjin.tistory.com/137</guid>
      <comments>https://chohyeonjin.tistory.com/137#entry137comment</comments>
      <pubDate>Mon, 28 Jul 2025 15:46:39 +0900</pubDate>
    </item>
    <item>
      <title>플러터로 스토어 앱 만들기</title>
      <link>https://chohyeonjin.tistory.com/136</link>
      <description>&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1.안드로이드 스튜디오 실행&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2.왼쪽 상단에서 New Flutter Project 생성&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3.Flutter를 선택&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;4.Flutter경로 선택후 알맞은 설정후 Finish버튼 클릭&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;2.1.asset 디렉토리 생성후 자신이 원하는 이미지 넣기&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2.2.yaml에 이미지 파일 인식을 위해 자원 폴더 위치 설정 (아래 코드 참조)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2.3.pub get 버튼을 클릭하여 설정 적용&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;3.1.나머지는 아래 코드처럼 main을 작성&lt;/p&gt;
&lt;pre id=&quot;code_1753433088554&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: StorePage(),
    );
  }
}

class StorePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Column(
          children: [
            Padding(
              padding: const EdgeInsets.all(25.0),
              child: Row(
                children: [
                  Text(&quot;Woman&quot;, style: TextStyle(fontWeight: FontWeight.bold)),
                  Spacer(),
                  Text(&quot;Kids&quot;, style: TextStyle(fontWeight: FontWeight.bold)),
                  Spacer(),
                  Text(&quot;Shoes&quot;, style: TextStyle(fontWeight: FontWeight.bold)),
                  Spacer(),
                  Text(&quot;Bags&quot;, style: TextStyle(fontWeight: FontWeight.bold)),
                ],
              ),
            ),
            //Image.asset(&quot;assets/bag.jpeg&quot;, fit: BoxFit.cover),
            //Image.asset(&quot;assets/cloth.jpeg&quot;, fit: BoxFit.cover),
            Expanded(child: Image.asset(&quot;assets/bag.jpeg&quot;, fit: BoxFit.cover)),
            SizedBox(height: 2),
            Expanded(child: Image.asset(&quot;assets/cloth.jpeg&quot;, fit: BoxFit.cover)),
          ],
        ),
      ),
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1753433107842&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;name: flutterstore
description: &quot;A new Flutter project.&quot;
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev

# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.0+1

environment:
  sdk: ^3.6.2

# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
  flutter:
    sdk: flutter

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.8

dev_dependencies:
  flutter_test:
    sdk: flutter

  # The &quot;flutter_lints&quot; package below contains a set of recommended lints to
  # encourage good coding practices. The lint set provided by the package is
  # activated in the `analysis_options.yaml` file located at the root of your
  # package. See that file for information about deactivating specific lint
  # rules and activating additional ones.
  flutter_lints: ^5.0.0

# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

# The following section is specific to Flutter packages.
flutter:

  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true

  # To add assets to your application, add an assets section, like this:
  assets:
    - assets/
  #   - images/a_dot_ham.jpeg

  # An image asset can refer to one or more resolution-specific &quot;variants&quot;, see
  # https://flutter.dev/to/resolution-aware-images

  # For details regarding adding assets from package dependencies, see
  # https://flutter.dev/to/asset-from-package

  # To add custom fonts to your application, add a fonts section here,
  # in this &quot;flutter&quot; section. Each entry in this list should have a
  # &quot;family&quot; key with the font family name, and a &quot;fonts&quot; key with a
  # list giving the asset and other descriptors for the font. For
  # example:
  # fonts:
  #   - family: Schyler
  #     fonts:
  #       - asset: fonts/Schyler-Regular.ttf
  #       - asset: fonts/Schyler-Italic.ttf
  #         style: italic
  #   - family: Trajan Pro
  #     fonts:
  #       - asset: fonts/TrajanPro.ttf
  #       - asset: fonts/TrajanPro_Bold.ttf
  #         weight: 700
  #
  # For details regarding fonts from package dependencies,
  # see https://flutter.dev/to/font-from-package&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Flutter</category>
      <author>개발자노트 조현진</author>
      <guid isPermaLink="true">https://chohyeonjin.tistory.com/136</guid>
      <comments>https://chohyeonjin.tistory.com/136#entry136comment</comments>
      <pubDate>Fri, 25 Jul 2025 17:51:04 +0900</pubDate>
    </item>
  </channel>
</rss>