<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>안드로이드 개발자의 창고</title>
    <link>https://wise-99.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Fri, 24 Jul 2026 03:17:18 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Wise-99</managingEditor>
    <image>
      <title>안드로이드 개발자의 창고</title>
      <url>https://tistory1.daumcdn.net/tistory/6320473/attach/97afec2b79034d138b194b76df80f3c5</url>
      <link>https://wise-99.tistory.com</link>
    </image>
    <item>
      <title>NFC와 NFD, 자소분리 문제</title>
      <link>https://wise-99.tistory.com/209</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;자소분리란?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;macOS에서는 유니코드 인코딩 방식을 NFD(조합형)로 채택하고, Windows에서는 NFC(완성형)로 채택하여 서로 다른 인코딩 방식을 사용 중이다. macOS에서 NFD로 작성된 파일을 Windows에서 확인할 경우 한글이 깨지는 문제가 발생한다.&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;macOS에서 채택한 NFD(조합형)&lt;/h3&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;안녕하세요.txt &amp;rarr; ㅇㅏㄴㄴㅕㅇㅎㅏㅅㅔㅇㅛ.txt&lt;/li&gt;
&lt;li&gt;위&amp;nbsp;(U+C704) &amp;rarr;&amp;nbsp;ᄋ&amp;nbsp;(U+110B) +&amp;nbsp;ᅱ&amp;nbsp;(U+1171)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Windows에서 채택한 NFC(완성형)&lt;/h3&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;안녕하세요.txt &amp;rarr; 안녕하세요.txt&lt;/li&gt;
&lt;li&gt;ᄋ&amp;nbsp;(U+110B) +&amp;nbsp;ᅱ&amp;nbsp;(U+1171) &amp;rarr;&amp;nbsp;위&amp;nbsp;(U+C704)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;회사에서 일을 하면서 Windows를 사용하는 분과 문서를 주고받는 일이 생겼는데 나는 Mac을 쓰고 있고, 내가 작성한 문서를 확인하는 분들은 대부분 윈도우를 쓰고 계셨다.&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;자소분리 문제가 발생해서 내가 항상 했던 일은 내 Mac에서 작성한 파일을 임시 Windows PC로 확인 후 다른 분에게 보냈었다.&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;매번 이 과정을 반복하다보니 간단하게 자소분리를 해결하는 프로그램을 만들기로 했었다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;실제로는 방법이 없었지만 시도할 때만 해도 방법이 있는 줄 알았다.&lt;/s&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;스크립트 또는 각종 도구를 이용하여 NFD &amp;rarr; NFC 시도&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;github에 공개 되어있는 프로젝트 또는 GoLang 스크립트를 이용하여 NFD 형식의 파일을 NFC로 변환
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;변환 후 재검사 시 NFC로 변환되어 저장되어있음&lt;/li&gt;
&lt;li&gt;Notion 또는 Gmail 전송 후 Windows에서 확인하였으나 NFD로 저장되어 변환의 의미가 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;GoLang으로 작성한 스크립트
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 코드는 정해진 디렉토리의 파일 이름을 읽어 NFD인지 NFC인지 확인하여 NFD일 경우 NFC로 변환하는 기능을 가지고 있음&lt;/li&gt;
&lt;li&gt;해당 코드로 변환 후 Notion 또는 Gmail 전송해도 동일하게 NFD로 자동 변환됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;go&quot; data-ke-language=&quot;go&quot;&gt;&lt;code&gt;package main

import (
	&quot;fmt&quot;
	&quot;golang.org/x/text/unicode/norm&quot; // NFC와 NFD에 관련된 golang 확장 라이브러리
	&quot;os&quot;
)

func main() {
	// 테스트 문자열
	// macString := &quot;자소분리&quot;      // NFD: mac
	// windowString := &quot;자소분리&quot;   // NFC: window

	entries, _ := os.ReadDir(&quot;/test&quot;)

	for _, entry := range entries {
		// 읽어온 파일 이름이 NFD 형식일 경우
		if norm.NFD.IsNormalString(entry.Name()) {
			fmt.Println(entry.Name() + &quot; 은 NFD 형식임&quot;)

			// NFD -&amp;gt; NFC 변환
			newString := norm.NFC.String(entry.Name())

			// 변환된 것을 확인 후 파일 이름 변경
			if norm.NFC.IsNormalString(newString) {
				err := os.Rename(&quot;/test&quot;+entry.Name(), &quot;/test/&quot; + newString)
				
                // 오류 발생 시
                if err != nil {
					fmt.Println(&quot;파일 이름 변경 실패:&quot;, err)
					return
				}
			}
			fmt.Println(&quot;변경 완료&quot;)
            
		// 읽어온 파일 이름이 NFC일 경우
		} else if norm.NFC.IsNormalString(entry.Name()) {
			fmt.Println(entry.Name() + &quot; 은 NFC 형식임&quot;)
		}
		fmt.Println(&quot;--------------------------------------------------------------------------------------------&quot;)
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드에도 문제가 없고 다른 사람이 만든 프로그램에도 문제가 없다는 것을 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 내 코드의 문제인가 싶어서 다른 사람이 깃허브에 공개한 프로그램을 사용했었는데 결과는 똑같았다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구글링 해보니 Safari에서는 정상적으로 보내진다는 말이 있었고, 내가 만든 goLang 스크립트나 다른 사람의 프로그램을 이용하여 변환 후 Safari를 사용했더니 자소분리 문제가 일어나지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 두 가지의 가설을 만들어 테스트해봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. Gmail 서버의 문제?&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Notion에 NFC로 변환된 파일을 업로드&lt;/li&gt;
&lt;li&gt;Windows에서 다운로드 후 확인해봤으나 한글이 깨져있는 것은 동일함&lt;/li&gt;
&lt;li&gt;Notion을 자체 앱이 아니라 Safari 브라우저를 이용해 페이지를 띄우고 해당 페이지에서 전송 -&amp;gt; 자소분리 일어나지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;rArr; Gmail만의 문제가 아니라고 판단&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. Chrome 브라우저의 문제? 아니면 Chorme 서버의 문제?&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문제를 확인하기 위해 html 파일 작성. 해당 html은 2번의 console.log() 출력
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;lt;input&amp;gt; 태그를 통해 파일 선택 시&lt;/li&gt;
&lt;li&gt;&amp;lt;form&amp;gt; 태그를 통해 파일 업로드 시&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;ko&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&amp;gt;
    &amp;lt;title&amp;gt;파일 업로드 및 다운로드&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;파일 업로드 및 다운로드&amp;lt;/h1&amp;gt;

    &amp;lt;!-- 파일 업로드 폼 --&amp;gt;
    &amp;lt;form id=&quot;uploadForm&quot; method=&quot;post&quot; enctype=&quot;multipart/form-data&quot;&amp;gt;
        &amp;lt;label for=&quot;fileUpload&quot;&amp;gt;파일 선택:&amp;lt;/label&amp;gt;
        &amp;lt;input type=&quot;file&quot; id=&quot;fileUpload&quot; name=&quot;fileUpload&quot;&amp;gt;
        &amp;lt;button type=&quot;button&quot; onclick=&quot;uploadFile()&quot;&amp;gt;업로드&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;

    &amp;lt;hr&amp;gt;

    &amp;lt;!-- 다운로드 링크 --&amp;gt;
    &amp;lt;h2&amp;gt;다운로드 가능한 파일&amp;lt;/h2&amp;gt;
    &amp;lt;div id=&quot;downloadSection&quot;&amp;gt;
        &amp;lt;!-- 업로드된 파일 목록 표시 --&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;script&amp;gt;
        // 파일 업로드 처리 (브라우저 내에서)
        function uploadFile() {
            const fileInput = document.getElementById('fileUpload');
            const files = fileInput.files;

            if (files.length === 0) {
                alert('업로드할 파일을 선택하세요.');
                return;
            }

            const file = files[0];
            const downloadSection = document.getElementById('downloadSection');

            // URL.createObjectURL로 파일 객체를 Blob URL로 변환
            const fileURL = URL.createObjectURL(file);

            // 다운로드 링크 생성
            const link = document.createElement('a');
            link.href = fileURL;
            link.download = file.name; // 원본 파일 이름 유지
            link.textContent = `다운로드: ${file.name}`;
            link.style.display = 'block';

            // 다운로드 섹션에 추가
            downloadSection.appendChild(link);
            isNormalizedTo(&quot;파일 업로드&quot;, file.name);
            alert('파일이 업로드되었습니다. 아래에서 다운로드할 수 있습니다.');
        }

        function isNormalizedTo(funName, str) {
            // 주어진 문자열이 특정 정규화 형식인지 확인
            if(str === str.normalize(&quot;NFC&quot;)) {
                console.log(`${funName} / ${str} : NFC 형식`)
            } else if(str === str.normalize(&quot;NFD&quot;)) {
                console.log(`${funName} / ${str} : NFD 형식`)
            }
        }

        // 파일 선택 이벤트 핸들러
        document.getElementById('fileUpload').addEventListener('change', function (event) {
            const files = event.target.files; // 업로드된 파일 리스트
            const fileInfoDiv = document.getElementById('fileInfo');

            for (const file of files) {
                isNormalizedTo('파일 선택', file.name);
            }
        });
    &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Chrome에서의 log 확인&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image1.png&quot; data-origin-width=&quot;3456&quot; data-origin-height=&quot;1496&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmMlG7/btsL4NzNZ2P/PJZu9fzEDfnhp90GMNYYn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmMlG7/btsL4NzNZ2P/PJZu9fzEDfnhp90GMNYYn1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmMlG7/btsL4NzNZ2P/PJZu9fzEDfnhp90GMNYYn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmMlG7%2FbtsL4NzNZ2P%2FPJZu9fzEDfnhp90GMNYYn1%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;3456&quot; height=&quot;1496&quot; data-filename=&quot;image1.png&quot; data-origin-width=&quot;3456&quot; data-origin-height=&quot;1496&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Safari에서의 log 확인&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;3450&quot; data-origin-height=&quot;1902&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bozSpW/btsL2BA0qc2/ZnQVJhUIJziDXaKcn017tk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bozSpW/btsL2BA0qc2/ZnQVJhUIJziDXaKcn017tk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bozSpW/btsL2BA0qc2/ZnQVJhUIJziDXaKcn017tk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbozSpW%2FbtsL2BA0qc2%2FZnQVJhUIJziDXaKcn017tk%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;3450&quot; height=&quot;1902&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;3450&quot; data-origin-height=&quot;1902&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;style6&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;동일한 파일을 각각 Chrome과 Safari에서 처리했을 때&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Chrome&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파일 형식에 상관없이 Chrome을 이용하는 순간 NFD로 변환됨&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Safari&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;NFC &amp;rarr; NFC, NFD &amp;rarr; NFD&lt;/li&gt;
&lt;li&gt;파일의 형식을 건드리지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;rArr; Chrome의 문제가 맞다면 Chromium 기반인 Notion도 운영체제가 채택한 유니코드 인코딩 방식을 따라 자체적으로 변환하는 것&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;Chrome 55 버전부터는 인코딩 설정 메뉴가 제거되고 자동 인코딩 감지를 수행&lt;/h3&gt;
&lt;figure id=&quot;og_1738418109363&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;How do I change the character encoding for a webpage in Chrome?&quot; data-og-description=&quot;Google Chrome, like almost every web browser in recent memory, used to have an option to change the character encoding for the webpage being viewed by going to Menu &amp;rsaquo; Tools &amp;rsaquo; Encoding (or some simi...&quot; data-og-host=&quot;superuser.com&quot; data-og-source-url=&quot;https://superuser.com/questions/1160003/how-do-i-change-the-character-encoding-for-a-webpage-in-chrome&quot; data-og-url=&quot;https://superuser.com/questions/1160003/how-do-i-change-the-character-encoding-for-a-webpage-in-chrome&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/lDGOh/hyX71Ubyez/8H1ZsORlb8pLMz9HTEOZ01/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316,https://scrap.kakaocdn.net/dn/cWMhzc/hyX717IrKP/6q6CgnhKLplaZsawgkK971/img.png?width=563&amp;amp;height=293&amp;amp;face=0_0_563_293&quot;&gt;&lt;a href=&quot;https://superuser.com/questions/1160003/how-do-i-change-the-character-encoding-for-a-webpage-in-chrome&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://superuser.com/questions/1160003/how-do-i-change-the-character-encoding-for-a-webpage-in-chrome&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/lDGOh/hyX71Ubyez/8H1ZsORlb8pLMz9HTEOZ01/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316,https://scrap.kakaocdn.net/dn/cWMhzc/hyX717IrKP/6q6CgnhKLplaZsawgkK971/img.png?width=563&amp;amp;height=293&amp;amp;face=0_0_563_293');&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;How do I change the character encoding for a webpage in Chrome?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Google Chrome, like almost every web browser in recent memory, used to have an option to change the character encoding for the webpage being viewed by going to Menu &amp;rsaquo; Tools &amp;rsaquo; Encoding (or some simi...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;superuser.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해당 문제와 관련한 Chromium Issue Tracker&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 외에도 NFD와 관련한 이슈가 몇개 있지만 해결되어 종료된 이슈는 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1738418149265&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;Chromium&quot; data-og-description=&quot;&quot; data-og-host=&quot;issues.chromium.org&quot; data-og-source-url=&quot;https://issues.chromium.org/issues/41291096#comment3&quot; data-og-url=&quot;https://issues.chromium.org/issues/41291096#comment3&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://issues.chromium.org/issues/41291096#comment3&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://issues.chromium.org/issues/41291096#comment3&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;Chromium&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;issues.chromium.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&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;Chrome에서는 어떤 형식의 문서를 전달하든 NFD로 변환하여 전달됨&lt;/li&gt;
&lt;li&gt;Safari에서는 형식을 변환하지 않고 그대로 전달함&lt;/li&gt;
&lt;li&gt;Firefox도 확인했으나 Chrome과 마찬가지로 NFD로 변환됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;rArr; NFC로 변환하여 Safari를 이용하여야 함...&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;NFC로 변환하는 workflow 작성법&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;macOS에서 Automator.app 실행&lt;/li&gt;
&lt;li&gt;문서 유형 &amp;gt; 빠른 동작 선택&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1760&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tCRY8/btsL4zJPYfK/VnOkTBwwQ2sLs47Kq1Ddtk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tCRY8/btsL4zJPYfK/VnOkTBwwQ2sLs47Kq1Ddtk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tCRY8/btsL4zJPYfK/VnOkTBwwQ2sLs47Kq1Ddtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtCRY8%2FbtsL4zJPYfK%2FVnOkTBwwQ2sLs47Kq1Ddtk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;1760&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1760&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;3. 현재 수신하는 작업흐름 &amp;gt; 파일 또는 폴더 / 선택 항목 위치 &amp;gt; Finder.app 선택&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 왼쪽에서 &amp;lsquo;셸 스크립트 실행&amp;rsquo; 검색하여 오른쪽으로 끌어당기기&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image2.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;982&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xCa7L/btsL6hATSlL/BAJCY98D58bcoSFvDvDxWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xCa7L/btsL6hATSlL/BAJCY98D58bcoSFvDvDxWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xCa7L/btsL6hATSlL/BAJCY98D58bcoSFvDvDxWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxCa7L%2FbtsL6hATSlL%2FBAJCY98D58bcoSFvDvDxWK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;982&quot; data-filename=&quot;image2.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;982&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;5. 셸 스크립트 &amp;gt; 통과 입력 &amp;gt; &amp;lsquo;인수&amp;rsquo;로 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. 아래 스크립트 작성&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;for i in &quot;$@&quot;; do
	/opt/homebrew/bin/convmv -f utf-8 -t utf-8 --nfc --notest &quot;$i&quot;
done&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image3.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;820&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cDEiEW/btsL6p6xBt7/mo9lg4YODUI1qellIsDpSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cDEiEW/btsL6p6xBt7/mo9lg4YODUI1qellIsDpSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cDEiEW/btsL6p6xBt7/mo9lg4YODUI1qellIsDpSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcDEiEW%2FbtsL6p6xBt7%2Fmo9lg4YODUI1qellIsDpSK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;820&quot; data-filename=&quot;image3.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;820&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;7. 아래부터는 선택사항&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 스크립트가 제대로 실행되었을 때 알림이 뜨도록 하고 싶다면
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;왼쪽 검색 &amp;gt; 변수 값 설정 검색하여 오른쪽에 끌어다 놓기&lt;/li&gt;
&lt;li&gt;변수 클릭하여 새로운 변수(filePath) 이름 등록&lt;/li&gt;
&lt;li&gt;셸 스크립트 추가하여 아래 내용 추가&lt;/li&gt;
&lt;li&gt;다시 변수 설정(fileName)&lt;/li&gt;
&lt;li&gt;제목과 메시지 입력
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메시지에는 fileName을 추가하여 자소분리 변환한 파일명 기재되도록 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1738574295524&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for i in &quot;$@&quot;; do
	cleanedPath=${i%\&quot;}
	basename &quot;$cleanedPath&quot;
done&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;880&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cftJew/btsL50MWUCc/HNsXaTWaOHw7rx9PN90qD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cftJew/btsL50MWUCc/HNsXaTWaOHw7rx9PN90qD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cftJew/btsL50MWUCc/HNsXaTWaOHw7rx9PN90qD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcftJew%2FbtsL50MWUCc%2FHNsXaTWaOHw7rx9PN90qD1%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;1000&quot; height=&quot;880&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;880&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MegUS/btsL4YoKK4P/EaIT5MUBEZkqkpFEKQc0A0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MegUS/btsL4YoKK4P/EaIT5MUBEZkqkpFEKQc0A0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MegUS/btsL4YoKK4P/EaIT5MUBEZkqkpFEKQc0A0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMegUS%2FbtsL4YoKK4P%2FEaIT5MUBEZkqkpFEKQc0A0%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;748&quot; height=&quot;200&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;200&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;8. 저장 후 finder에서 아무 파일 선택하여 기타 &amp;gt; 사용자화 클릭&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;1356&quot; data-origin-height=&quot;630&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1uyvD/btsL6Hskr8J/ed2WblhanIBi9T1Ri3THBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1uyvD/btsL6Hskr8J/ed2WblhanIBi9T1Ri3THBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1uyvD/btsL6Hskr8J/ed2WblhanIBi9T1Ri3THBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1uyvD%2FbtsL6Hskr8J%2Fed2WblhanIBi9T1Ri3THBK%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;1356&quot; height=&quot;630&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;1356&quot; data-origin-height=&quot;630&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;9. automator로 만든 빠른 동작 체크&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;625&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cP2v9B/btsL4Y3nLap/hNKO5lfPpxMsM1NMa38Y2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cP2v9B/btsL4Y3nLap/hNKO5lfPpxMsM1NMa38Y2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cP2v9B/btsL4Y3nLap/hNKO5lfPpxMsM1NMa38Y2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcP2v9B%2FbtsL4Y3nLap%2FhNKO5lfPpxMsM1NMa38Y2k%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;715&quot; height=&quot;625&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;625&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;10. Finder에서 표시되는지 확인&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;1356&quot; data-origin-height=&quot;630&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kzXJ7/btsL6AtgiU8/al3CKaEC3YWjwm2OGimYW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kzXJ7/btsL6AtgiU8/al3CKaEC3YWjwm2OGimYW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kzXJ7/btsL6AtgiU8/al3CKaEC3YWjwm2OGimYW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkzXJ7%2FbtsL6AtgiU8%2Fal3CKaEC3YWjwm2OGimYW1%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;1356&quot; height=&quot;630&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;1356&quot; data-origin-height=&quot;630&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;1228&quot; data-origin-height=&quot;537&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIF6Bp/btsL5QcxW4L/EyIevJD8TXm0dusMlnpQm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIF6Bp/btsL5QcxW4L/EyIevJD8TXm0dusMlnpQm1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIF6Bp/btsL5QcxW4L/EyIevJD8TXm0dusMlnpQm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIF6Bp%2FbtsL5QcxW4L%2FEyIevJD8TXm0dusMlnpQm1%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;1228&quot; height=&quot;537&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;1228&quot; data-origin-height=&quot;537&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;style6&quot; /&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;Asana, Slack은 NFC와 NFD에 상관없이 깨지지 않음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Notion, Gmail, Chrome&lt;/b&gt;을 이용할 경우 NFC로 변환해야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Computer/ETC.</category>
      <category>Automator</category>
      <category>Chrome</category>
      <category>Mac</category>
      <category>NFC</category>
      <category>NFD</category>
      <category>Notion</category>
      <category>windows</category>
      <category>Workflow</category>
      <category>자소분리</category>
      <category>자소분리 원인</category>
      <author>Wise-99</author>
      <guid isPermaLink="true">https://wise-99.tistory.com/209</guid>
      <comments>https://wise-99.tistory.com/209#entry209comment</comments>
      <pubDate>Sat, 1 Feb 2025 23:10:15 +0900</pubDate>
    </item>
    <item>
      <title>[Android] Coroutine과 Thread의 차이</title>
      <link>https://wise-99.tistory.com/208</link>
      <description>&lt;figure id=&quot;og_1693815636427&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;blog&quot; data-og-title=&quot;Coroutine, Thread 와의 차이와 그 특징&quot; data-og-description=&quot;처음 Kotlin 를 사용하던 중에 비동기 처리를 위해 Coroutine 개념을 마주했었습니다. 동기란 요청을 보낸 후 요청에 대한 반환값을 얻기 이전까지 대기하는걸 의미하고, 비동기는 그 대기시간동안 &quot; data-og-host=&quot;aaronryu.github.io&quot; data-og-source-url=&quot;https://aaronryu.github.io/2019/05/27/coroutine-and-thread/&quot; data-og-url=&quot;https://aaronryu.github.io/2019/05/27/coroutine-and-thread/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/3gmYF/hyTPy9qwOe/PTU2eO06nBtpoXpBNKOfi0/img.png?width=1648&amp;amp;height=356&amp;amp;face=0_0_1648_356,https://scrap.kakaocdn.net/dn/fNxfO/hyTPyVRPQS/HYaP6pOrjYFwV30BwbuXW0/img.png?width=1778&amp;amp;height=160&amp;amp;face=0_0_1778_160,https://scrap.kakaocdn.net/dn/bPWdlH/hyTPIjTIvr/AGQgIWfPP6gcuOKEtOjzDK/img.png?width=1778&amp;amp;height=165&amp;amp;face=0_0_1778_165&quot;&gt;&lt;a href=&quot;https://aaronryu.github.io/2019/05/27/coroutine-and-thread/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://aaronryu.github.io/2019/05/27/coroutine-and-thread/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/3gmYF/hyTPy9qwOe/PTU2eO06nBtpoXpBNKOfi0/img.png?width=1648&amp;amp;height=356&amp;amp;face=0_0_1648_356,https://scrap.kakaocdn.net/dn/fNxfO/hyTPyVRPQS/HYaP6pOrjYFwV30BwbuXW0/img.png?width=1778&amp;amp;height=160&amp;amp;face=0_0_1778_160,https://scrap.kakaocdn.net/dn/bPWdlH/hyTPIjTIvr/AGQgIWfPP6gcuOKEtOjzDK/img.png?width=1778&amp;amp;height=165&amp;amp;face=0_0_1778_165');&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;Coroutine, Thread 와의 차이와 그 특징&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;처음 Kotlin 를 사용하던 중에 비동기 처리를 위해 Coroutine 개념을 마주했었습니다. 동기란 요청을 보낸 후 요청에 대한 반환값을 얻기 이전까지 대기하는걸 의미하고, 비동기는 그 대기시간동안&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;aaronryu.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;  Process &amp;amp; Thread&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1648&quot; data-origin-height=&quot;356&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/elnCug/btss74GPKEG/mFMbF932gC58jC38BAt7Vk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/elnCug/btss74GPKEG/mFMbF932gC58jC38BAt7Vk/img.png&quot; data-alt=&quot;출처 :&amp;amp;nbsp;https://aaronryu.github.io/2019/05/27/coroutine-and-thread/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/elnCug/btss74GPKEG/mFMbF932gC58jC38BAt7Vk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FelnCug%2Fbtss74GPKEG%2FmFMbF932gC58jC38BAt7Vk%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;1648&quot; height=&quot;356&quot; data-origin-width=&quot;1648&quot; data-origin-height=&quot;356&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 :&amp;nbsp;https://aaronryu.github.io/2019/05/27/coroutine-and-thread/&lt;/figcaption&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;span style=&quot;color: #333333;&quot;&gt;Process : 프로그램이 메모리에 적재되어 실행되는 인스턴스(객체)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Thread : Process 내에서 실행되는 실행 단위&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #4a4a4a; text-align: start;&quot;&gt;각 작업에 해당하는 메모리 영역을 할당&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #4a4a4a; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #4a4a4a; text-align: start;&quot;&gt;OS 레벨에서 각 작업들을 얼만큼씩 분배하여 수행해야지 효율적일지 선점 스케줄링(Preempting Scheduling)을 필요로 함&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;2684&quot; data-origin-height=&quot;702&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIl7ec/btss3VpQYvi/sIBOE3H1nNO7Vbx8wIDElK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIl7ec/btss3VpQYvi/sIBOE3H1nNO7Vbx8wIDElK/img.png&quot; data-alt=&quot;출처 :&amp;amp;nbsp;https://aaronryu.github.io/2019/05/27/coroutine-and-thread/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIl7ec/btss3VpQYvi/sIBOE3H1nNO7Vbx8wIDElK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIl7ec%2Fbtss3VpQYvi%2FsIBOE3H1nNO7Vbx8wIDElK%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;2684&quot; height=&quot;702&quot; data-origin-width=&quot;2684&quot; data-origin-height=&quot;702&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 :&amp;nbsp;https://aaronryu.github.io/2019/05/27/coroutine-and-thread/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #4a4a4a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Task 단위 =&amp;nbsp;&lt;b&gt;Thread&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;다수의 작업 각각에&amp;nbsp;&lt;b&gt;Thread&lt;/b&gt;를 할당한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;각 Thread는 자체 Stack 메모리 영역을 가지며&amp;nbsp;&lt;b&gt;JVM Stack 영역을 차지&lt;/b&gt;한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #4a4a4a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;Context Switching&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;OS Kernel&lt;/b&gt;에 의한&amp;nbsp;&lt;b&gt;Context Switching&lt;/b&gt;을 통해 동시성을 보장한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;Blocking&lt;/b&gt;: &lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Thread A가 Thread B의 결과가 나오기까지 기다려야한다면 &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; text-align: left;&quot;&gt;Thread A&lt;/span&gt;는 Blocking 되어 그 시간동안 해당 자원을 사용하지 못한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;  Coroutine&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;비동기적으로 실행되는 코드를 간소화하기 위해 Android에서 사용할 수 있는 동시성 디자인 패턴&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #4a4a4a; text-align: start;&quot;&gt;Lightweight Thread 라고 불림&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #4a4a4a; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #4a4a4a; text-align: start;&quot;&gt;작업을 효율적으로 분배하여 조금씩 수행하여 완수하는 동시성(Concurrency)을 목표로 함&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #4a4a4a; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #4a4a4a; text-align: start;&quot;&gt;각 작업에 대해 Thread를 할당하는 것이 아니라 작은 Object만을 할당해주고 이 Object들을 자유자재로 스위칭함으로써 Switching 비용을 최대한 줄임&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2693&quot; data-origin-height=&quot;702&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpQh0E/btss3Ff9AW6/tQenfWAPTZxNnRtkJmOqg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpQh0E/btss3Ff9AW6/tQenfWAPTZxNnRtkJmOqg0/img.png&quot; data-alt=&quot;출처 :&amp;amp;nbsp;https://aaronryu.github.io/2019/05/27/coroutine-and-thread/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpQh0E/btss3Ff9AW6/tQenfWAPTZxNnRtkJmOqg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpQh0E%2Fbtss3Ff9AW6%2FtQenfWAPTZxNnRtkJmOqg0%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;2693&quot; height=&quot;702&quot; data-origin-width=&quot;2693&quot; data-origin-height=&quot;702&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 :&amp;nbsp;https://aaronryu.github.io/2019/05/27/coroutine-and-thread/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #4a4a4a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Task 단위 =&amp;nbsp;&lt;b&gt;Object (Coroutine)&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;다수의 작업 각각에&amp;nbsp;&lt;b&gt;Object&lt;/b&gt;를 할당한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이 Coroutine Object는 객체를 담는&amp;nbsp;&lt;b&gt;JVM Heap에 적재&lt;/b&gt;된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #4a4a4a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;Programmer Switching&lt;/b&gt;&amp;nbsp;= No Context Switching&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;프로그래머의 코딩&lt;/b&gt;을 통해&amp;nbsp;&lt;b&gt;Switching 시점을 마음대로 정함으로&lt;/b&gt;써 동시성을 보장한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;Suspend (Non-Blocking)&lt;/b&gt;: &lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Task 1(Object)이 &lt;span style=&quot;background-color: #ffffff; text-align: left;&quot;&gt;Task&lt;/span&gt; 2(Object)의 결과가 나오기까지 기다려야한다면&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; text-align: left;&quot;&gt;Task&lt;/span&gt; 1은 일시 중지(Suspend) 되지만 &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; text-align: left;&quot;&gt;Task&lt;/span&gt; 1을 수행하던 Thread는 그대로 유효하기 때문에 &lt;span style=&quot;background-color: #ffffff; text-align: left;&quot;&gt;Task&lt;/span&gt; 2도 &lt;span style=&quot;background-color: #ffffff; text-align: left;&quot;&gt;Task&lt;/span&gt; 1과 동일한 Thread에서 실행될 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;  동시성(&lt;/span&gt;Concurrency&lt;span style=&quot;color: #333333;&quot;&gt;)이란 뭘까?&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;동시성과 같이 따라오는 단어는 병렬성(&lt;/span&gt;Parallelism&lt;span style=&quot;color: #333333;&quot;&gt;)이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;동시성(Concurrency) = Interleaving, 시분할&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1778&quot; data-origin-height=&quot;160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLdCD1/btssVKI8O1X/9qfVkQQkTMiWWx6rykEqzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLdCD1/btssVKI8O1X/9qfVkQQkTMiWWx6rykEqzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLdCD1/btssVKI8O1X/9qfVkQQkTMiWWx6rykEqzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLdCD1%2FbtssVKI8O1X%2F9qfVkQQkTMiWWx6rykEqzk%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;1778&quot; height=&quot;160&quot; data-origin-width=&quot;1778&quot; data-origin-height=&quot;160&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;1778&quot; data-origin-height=&quot;165&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cWaBJ6/btss9BD81Gq/zMoNGs81yiuBvaFkZ2K0Kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cWaBJ6/btss9BD81Gq/zMoNGs81yiuBvaFkZ2K0Kk/img.png&quot; data-alt=&quot;출처 :&amp;amp;nbsp;https://aaronryu.github.io/2019/05/27/coroutine-and-thread/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cWaBJ6/btss9BD81Gq/zMoNGs81yiuBvaFkZ2K0Kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcWaBJ6%2Fbtss9BD81Gq%2FzMoNGs81yiuBvaFkZ2K0Kk%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;1778&quot; height=&quot;165&quot; data-origin-width=&quot;1778&quot; data-origin-height=&quot;165&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 :&amp;nbsp;https://aaronryu.github.io/2019/05/27/coroutine-and-thread/&lt;/figcaption&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;다수의 Task가 있는데, 각 Task들을 평등하게 조금씩 나누어 실행하는 것&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #4a4a4a; text-align: start;&quot;&gt;총 실행시간은 Context Switching에 대한 비용을 제외하곤 각 Task 수행 시간을 합친 것과 동일하다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #4a4a4a; text-align: start;&quot;&gt;위의 사진에서는 Task가 각각 10분씩 걸리고 이를 동시성으로 처리하면 총 20분이 걸리는 것이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;병렬성(Parallelism) = Parallelizing, 병렬 수행&lt;/h4&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;115&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/M1zuv/btss3VDqgOb/R1MKBcJLZplk8W76ylUZ41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/M1zuv/btss3VDqgOb/R1MKBcJLZplk8W76ylUZ41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/M1zuv/btss3VDqgOb/R1MKBcJLZplk8W76ylUZ41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FM1zuv%2Fbtss3VDqgOb%2FR1MKBcJLZplk8W76ylUZ41%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;115&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;115&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;1778&quot; data-origin-height=&quot;255&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJJtxA/btss84T7wRW/iYHMLd1AGGV2QQm1K8VKf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJJtxA/btss84T7wRW/iYHMLd1AGGV2QQm1K8VKf1/img.png&quot; data-alt=&quot;출처 :&amp;amp;nbsp;https://aaronryu.github.io/2019/05/27/coroutine-and-thread/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJJtxA/btss84T7wRW/iYHMLd1AGGV2QQm1K8VKf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJJtxA%2Fbtss84T7wRW%2FiYHMLd1AGGV2QQm1K8VKf1%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;1778&quot; height=&quot;255&quot; data-origin-width=&quot;1778&quot; data-origin-height=&quot;255&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 :&amp;nbsp;https://aaronryu.github.io/2019/05/27/coroutine-and-thread/&lt;/figcaption&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;span style=&quot;background-color: #ffffff; color: #4a4a4a; text-align: start;&quot;&gt;Task 수 만큼 자원이 필요하며, Context Switching은 필요없다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #4a4a4a; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #4a4a4a; text-align: start;&quot;&gt;총 실행 시간은 다수의 Task 중 가장 소요 시간이 긴 Task만큼 걸린다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #4a4a4a; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #4a4a4a; text-align: start;&quot;&gt;위 사진에서는 각각의 Task가 10분 씩 소요되므로 총 소요 시간은 10분이다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #4a4a4a; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #4a4a4a; text-align: start;&quot;&gt;  Thread와 Coroutine&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2822&quot; data-origin-height=&quot;454&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lCXGE/btss92PcRVe/xvUbdNFGlYXZHhuZCDdWoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lCXGE/btss92PcRVe/xvUbdNFGlYXZHhuZCDdWoK/img.png&quot; data-alt=&quot;출처 :&amp;amp;nbsp;https://aaronryu.github.io/2019/05/27/coroutine-and-thread/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lCXGE/btss92PcRVe/xvUbdNFGlYXZHhuZCDdWoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlCXGE%2Fbtss92PcRVe%2FxvUbdNFGlYXZHhuZCDdWoK%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;2822&quot; height=&quot;454&quot; data-origin-width=&quot;2822&quot; data-origin-height=&quot;454&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 :&amp;nbsp;https://aaronryu.github.io/2019/05/27/coroutine-and-thread/&lt;/figcaption&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;Coroutine으로 &amp;lsquo;작업(Task)&amp;rsquo;의 단위를 Thread가 아닌 Object로 축소하면서, 작업의 전환 및 다수 작업 수행에 굳이 다수의 Thread 를 필요로 하지 않게 된다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #4a4a4a; text-align: start;&quot;&gt;하나의 Thread가 다수의 Coroutine을 수행할 수 있기 때문에 더 이상 작업의 수만큼 Thread를 양산하며 메모리를 소비할 필요가 없다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;각 &lt;span style=&quot;color: #4a4a4a; text-align: start;&quot;&gt;Thread&lt;/span&gt;마다 갖는 Stack 메모리 영역을 갖지 않기 때문에, &lt;span style=&quot;color: #4a4a4a; text-align: start;&quot;&gt;Thread&lt;/span&gt; 사용 시 &lt;span style=&quot;color: #4a4a4a; text-align: start;&quot;&gt;Thread&lt;/span&gt; 개수만큼 Stack 메모리에 따른 메모리 사용 공간이 증가하지 않아도 된다.&lt;/li&gt;
&lt;li&gt;같은 프로세스내에 &amp;lsquo;공유 데이터 구조&amp;rsquo;(Heap)에 대한 locking 걱정도 없다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #4a4a4a; text-align: start;&quot;&gt;즉, Coroutine 이 수행될 Thread 도 프로그래머가 Shared Thread Pool 을 지정하여 결정한다는 의미이며, Coroutine을 활용한 효율성은 오로지 프로그래머의 몫이라는 의미이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Computer/Android</category>
      <author>Wise-99</author>
      <guid isPermaLink="true">https://wise-99.tistory.com/208</guid>
      <comments>https://wise-99.tistory.com/208#entry208comment</comments>
      <pubDate>Mon, 4 Sep 2023 18:30:51 +0900</pubDate>
    </item>
    <item>
      <title>[Android] MVC / MVP / MVVM 차이</title>
      <link>https://wise-99.tistory.com/207</link>
      <description>&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  MVC&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Model - View - Controller로 이루어진 패턴&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;960&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUQGvO/btssUaAuWfW/RSznCJqPbkD1cOhSk6ctDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUQGvO/btssUaAuWfW/RSznCJqPbkD1cOhSk6ctDk/img.png&quot; data-alt=&quot;출처 :&amp;amp;amp;nbsp;https://developer.mozilla.org/ko/docs/Glossary/MVC&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUQGvO/btssUaAuWfW/RSznCJqPbkD1cOhSk6ctDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUQGvO%2FbtssUaAuWfW%2FRSznCJqPbkD1cOhSk6ctDk%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;551&quot; height=&quot;413&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;960&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 :&amp;amp;nbsp;https://developer.mozilla.org/ko/docs/Glossary/MVC&lt;/figcaption&gt;
&lt;/figure&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;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Model&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;앱이 포함해야할 데이터가 무엇인지를 정의&lt;/li&gt;
&lt;li&gt;데이터의 상태가 변경되면 일반적으로 View에게 알려주지만 가끔은 Controller에도 알려준다.&lt;/li&gt;
&lt;li&gt;가끔 Controller에게 알려주는 경우는 업데이트 된 View를 제어하기 위해 다른 로직이 필요한 경우이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;View&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터를 보여주는 방식을 정의한다.&lt;/li&gt;
&lt;li&gt;표시할 데이터는 Model로 부터 받게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Cotroller&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자로부터 입력에 대한 응답으로 Model 또는 View를 업데이트하는 로직을 포함한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;동작 과정 예시&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 to-do 리스트 목록에 있다고 가정하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;이러한 이벤트는 Controller에 전달된다.&lt;/li&gt;
&lt;li&gt;Controller는 Model이 수행할 메서드와 파라미터(사용자가 입력한 내용)를 전달한다.&lt;/li&gt;
&lt;li&gt;Model은 전달 받은 데이터로 메서드를 수행하고 데이터를 업데이트 한다.&lt;/li&gt;
&lt;li&gt;&amp;nbsp;Controller는 Model을 나타내 줄 View를 선택한다.&lt;/li&gt;
&lt;li&gt;&amp;nbsp;View는 Model을 이용하여 작성한 내용이 추가된 to-do 리스트를 표시한다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1693637731742&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;MVC - MDN Web Docs 용어 사전: 웹 용어 정의 | MDN&quot; data-og-description=&quot;MVC (모델-뷰-컨트롤러) 는 사용자 인터페이스, 데이터 및 논리 제어를 구현하는데 널리 사용되는 소프트웨어 디자인 패턴입니다. 소프트웨어의 비즈니스 로직과 화면을 구분하는데 중점을 두고 &quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/ko/docs/Glossary/MVC&quot; data-og-url=&quot;https://developer.mozilla.org/ko/docs/Glossary/MVC&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/O98zh/hyTPIcrYHh/9K3sIdzAog6P4i2DTkRIJK/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/bABcto/hyTMiNd1pP/aKWvrhKXsnzHgZ7GXKpzQK/img.png?width=2000&amp;amp;height=1500&amp;amp;face=0_0_2000_1500&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Glossary/MVC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/ko/docs/Glossary/MVC&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/O98zh/hyTPIcrYHh/9K3sIdzAog6P4i2DTkRIJK/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/bABcto/hyTMiNd1pP/aKWvrhKXsnzHgZ7GXKpzQK/img.png?width=2000&amp;amp;height=1500&amp;amp;face=0_0_2000_1500');&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;MVC - MDN Web Docs 용어 사전: 웹 용어 정의 | MDN&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;MVC (모델-뷰-컨트롤러) 는 사용자 인터페이스, 데이터 및 논리 제어를 구현하는데 널리 사용되는 소프트웨어 디자인 패턴입니다. 소프트웨어의 비즈니스 로직과 화면을 구분하는데 중점을 두고&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.mozilla.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  MVP&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Model - View - Presenter로 이루어진 패턴&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;312&quot; data-origin-height=&quot;214&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dzxuXT/btssUIqaPfA/ROgJS0zpRUvDhIGKSpMGn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dzxuXT/btssUIqaPfA/ROgJS0zpRUvDhIGKSpMGn0/img.png&quot; data-alt=&quot;출처 :&amp;amp;amp;nbsp;https://ko.wikipedia.org/wiki/%EB%AA%A8%EB%8D%B8-%EB%B7%B0-%ED%94%84%EB%A6%AC%EC%A0%A0%ED%84%B0#cite_note-1&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dzxuXT/btssUIqaPfA/ROgJS0zpRUvDhIGKSpMGn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdzxuXT%2FbtssUIqaPfA%2FROgJS0zpRUvDhIGKSpMGn0%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;538&quot; height=&quot;369&quot; data-origin-width=&quot;312&quot; data-origin-height=&quot;214&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 :&amp;amp;nbsp;https://ko.wikipedia.org/wiki/%EB%AA%A8%EB%8D%B8-%EB%B7%B0-%ED%94%84%EB%A6%AC%EC%A0%A0%ED%84%B0#cite_note-1&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Model&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;모델은 데이터와 비즈니스 로직을 나타냄&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;데이터를 저장하고 관리하며, 데이터를 변경하거나 가져오는 작업을 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;View&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자 인터페이스를 나타내며, 사용자와 상호작용을 하는 구성 요소&lt;/li&gt;
&lt;li&gt;수동적이며 비즈니스 로직을 포함하지 않음&lt;/li&gt;
&lt;li&gt;Presenter에 의존하여 사용자 입력과 데이터 표시를 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Presenter&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Model과 View의 중개자 역할&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000; text-align: start;&quot;&gt;View에서 사용자 입력을 받아 처리&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000; text-align: start;&quot;&gt;필요한 데이터 작업을 수행하기 위해 Model과 상호 작용하며 변경 사항을 반영하도록 &lt;span style=&quot;background-color: #fdfdfd; color: #000000; text-align: start;&quot;&gt;View&lt;/span&gt;를 업데이트&lt;/span&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;동작 과정 예시&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 to-do 리스트 목록에 있다고 가정하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;View는 사용자의 입력과 &quot;추가&quot; 버튼 클릭 이벤트를 Presenter에 전달한다.&lt;/li&gt;
&lt;li&gt;Presenter는 입력된 내용을 Model에 전달하여 목록에 추가한다.&lt;/li&gt;
&lt;li&gt;Model은 업데이트된 리스트를 반환한다.&lt;/li&gt;
&lt;li&gt;Presenter는 새로 업데이트된 리스트를 View에 전달하여 화면을 업데이트 한다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  MVVM&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Model - View - ViewModel로 이루어진 패턴&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;150&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQELAy/btssVMy8WU3/EeuUrVn2Yxe3PQLk13mDf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQELAy/btssVMy8WU3/EeuUrVn2Yxe3PQLk13mDf0/img.png&quot; data-alt=&quot;출처 :&amp;amp;amp;nbsp;https://ko.wikipedia.org/wiki/%EB%AA%A8%EB%8D%B8-%EB%B7%B0-%EB%B7%B0%EB%AA%A8%EB%8D%B8&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQELAy/btssVMy8WU3/EeuUrVn2Yxe3PQLk13mDf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQELAy%2FbtssVMy8WU3%2FEeuUrVn2Yxe3PQLk13mDf0%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;653&quot; height=&quot;196&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;150&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 :&amp;amp;nbsp;https://ko.wikipedia.org/wiki/%EB%AA%A8%EB%8D%B8-%EB%B7%B0-%EB%B7%B0%EB%AA%A8%EB%8D%B8&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Model&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;데이터와 비즈니스 로직을 관리&lt;/span&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;데이터 소스와 상호작용하여 데이터를 가져오고 저장&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;UI와 직접적으로 연결되지 않음&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;View&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자 인터페이스를 나타내며,&lt;span&gt;&amp;nbsp;&lt;/span&gt;사용자와 상호작용을 하는 구성 요소&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;데이터를 표시하고 사용자 입력을 ViewModel로 전달하는 역할&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;ViewModel&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;View와 Model의 중개자 역할&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;뷰와 모델 사이의 통신을 중재하며 데이터 변환 및 가공을 담당&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;동작 과정 예시&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 to-do 리스트 목록에 있다고 가정하면&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;사용자가 새로운 내용을 입력하고 추가 버튼을 클릭한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;View는 추가 버튼 클릭 이벤트를 ViewModel로 전달한다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;새로운 내용을 &lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;Model&lt;/span&gt;에 추가하는 로직을 실행한다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;Model&lt;/span&gt;은 새로운 내용을 추가하고, &lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;Model&lt;/span&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;의 변경 사항은 &lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;ViewModel&lt;/span&gt;에 알려진다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;뷰모델은 새로운 내용이 추가된 리스트를 다시 View에게 제공한다.&lt;/li&gt;
&lt;li&gt;View는 업데이트된 리스트를 화면에 표시한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;그래서 대체 차이점이 무엇인가?&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MVC에서는 Controller가 Model과 View를 관리하며 Model과 View 사이의 직접적인 연결이 &lt;b&gt;가능&lt;/b&gt;하고, Controller가 일부 비즈니스 로직을 처리한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Model과 View 사이의 직접적인 연결이 가능하므로 단위테스트가 제한된다.&lt;/li&gt;
&lt;li&gt;Controller가 처리하는 비즈니스 로직 : 데이터가 변경되지 않는 선에서 정렬 및 필터링(정렬이나 검색), 예외 처리나 데이터 유효성 검사 등등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;MVP에서는 Presenter가 Model과 View를 관리하며 &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;Model과&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;View 사이의 직접적인 연결을 &lt;b&gt;방지&lt;/b&gt;하고, Model은 비즈니스 로직을 처리한다.&lt;/li&gt;
&lt;li&gt;MVVM에서는 ViewModel이 View와 Model을 관리하며 &lt;b&gt;Data Binding&lt;/b&gt;을 사용하여 View와 ViewModel 간의 동기화를 &lt;b&gt;자동화&lt;/b&gt;하며, ViewModel은 UI 관련 로직을 담당한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Data Binding : 데이터와 View를 연결하는 작업을 Layout에서 처리하는 기술&lt;/li&gt;
&lt;/ul&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;&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;Data Binding에 대한 설명은 이 블로그에서 설명을 진짜 잘해주셨다.&lt;/p&gt;
&lt;figure id=&quot;og_1693642371197&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;코틀린 데이터 바인딩(Data binding)&quot; data-og-description=&quot;1. 데이터 바인딩 1-1. 데이터 바인딩이란? 1-2. 데이터 바인딩 vs 뷰 바인딩 2. 사용법 2-1. gradle 추가 2-2. 액티비티 2-3. 레이아웃 파일 1. 데이터 바인딩 1-1. 데이터 바인딩이란? // findViewById 쓸때는 이&quot; data-og-host=&quot;todaycode.tistory.com&quot; data-og-source-url=&quot;https://todaycode.tistory.com/31&quot; data-og-url=&quot;https://todaycode.tistory.com/31&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bnVbCI/hyTPJvF2sh/ialJr9oWAz1ZRGoGYwNn70/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/kGaHU/hyTMcGiWJo/ql9Ol6dTQLKpwM5ci5zjL1/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/bGmpaH/hyTPuL3qzf/74z2k5vl2KpPVeLBb30x21/img.jpg?width=880&amp;amp;height=456&amp;amp;face=0_0_880_456&quot;&gt;&lt;a href=&quot;https://todaycode.tistory.com/31&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://todaycode.tistory.com/31&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bnVbCI/hyTPJvF2sh/ialJr9oWAz1ZRGoGYwNn70/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/kGaHU/hyTMcGiWJo/ql9Ol6dTQLKpwM5ci5zjL1/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/bGmpaH/hyTPuL3qzf/74z2k5vl2KpPVeLBb30x21/img.jpg?width=880&amp;amp;height=456&amp;amp;face=0_0_880_456');&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;코틀린 데이터 바인딩(Data binding)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;1. 데이터 바인딩 1-1. 데이터 바인딩이란? 1-2. 데이터 바인딩 vs 뷰 바인딩 2. 사용법 2-1. gradle 추가 2-2. 액티비티 2-3. 레이아웃 파일 1. 데이터 바인딩 1-1. 데이터 바인딩이란? // findViewById 쓸때는 이&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;todaycode.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 안드로이드에 적용시킨 MVC, MVP, MVVM 에 대한 설명은 이 블로그에 코드까지 적혀있다.&lt;/p&gt;
&lt;figure id=&quot;og_1693644929140&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;안드로이드 [Kotlin] - 아키텍처 패턴 with MVC, MVP, MVVM (feat 코드 예제)&quot; data-og-description=&quot;MVP/MVVM/Clean Architecture 등 아키텍처 설계 혹은 적용 경험이 있으신 분 안드로이드 채용 공고를 보다 보면 어렵지 않게 볼 수 있는 글들이다. 오늘은 안드로이드 아키텍처패턴으로 많이 언급되는 MV&quot; data-og-host=&quot;jminie.tistory.com&quot; data-og-source-url=&quot;https://jminie.tistory.com/168#7.1.%20MVC%20%ED%8C%A8%ED%84%B4%20%EC%98%88%EC%A0%9C&quot; data-og-url=&quot;https://jminie.tistory.com/168&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/MsPRb/hyTPArZs71/wghZ92U7ZCS0o2WFtLWQn0/img.jpg?width=512&amp;amp;height=269&amp;amp;face=0_0_512_269,https://scrap.kakaocdn.net/dn/nqpTF/hyTPuL32l6/sdaizAm8JieB8jlgIvpRKK/img.jpg?width=512&amp;amp;height=269&amp;amp;face=0_0_512_269,https://scrap.kakaocdn.net/dn/iHJ52/hyTL7SvRup/ZJ6Ne6Dy92Hu0M1XZYKbJk/img.png?width=633&amp;amp;height=440&amp;amp;face=0_0_633_440&quot;&gt;&lt;a href=&quot;https://jminie.tistory.com/168#7.1.%20MVC%20%ED%8C%A8%ED%84%B4%20%EC%98%88%EC%A0%9C&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jminie.tistory.com/168#7.1.%20MVC%20%ED%8C%A8%ED%84%B4%20%EC%98%88%EC%A0%9C&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/MsPRb/hyTPArZs71/wghZ92U7ZCS0o2WFtLWQn0/img.jpg?width=512&amp;amp;height=269&amp;amp;face=0_0_512_269,https://scrap.kakaocdn.net/dn/nqpTF/hyTPuL32l6/sdaizAm8JieB8jlgIvpRKK/img.jpg?width=512&amp;amp;height=269&amp;amp;face=0_0_512_269,https://scrap.kakaocdn.net/dn/iHJ52/hyTL7SvRup/ZJ6Ne6Dy92Hu0M1XZYKbJk/img.png?width=633&amp;amp;height=440&amp;amp;face=0_0_633_440');&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;안드로이드 [Kotlin] - 아키텍처 패턴 with MVC, MVP, MVVM (feat 코드 예제)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;MVP/MVVM/Clean Architecture 등 아키텍처 설계 혹은 적용 경험이 있으신 분 안드로이드 채용 공고를 보다 보면 어렵지 않게 볼 수 있는 글들이다. 오늘은 안드로이드 아키텍처패턴으로 많이 언급되는 MV&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;jminie.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Computer/Android</category>
      <author>Wise-99</author>
      <guid isPermaLink="true">https://wise-99.tistory.com/207</guid>
      <comments>https://wise-99.tistory.com/207#entry207comment</comments>
      <pubDate>Sat, 2 Sep 2023 17:57:03 +0900</pubDate>
    </item>
    <item>
      <title>[Android] MVVM</title>
      <link>https://wise-99.tistory.com/206</link>
      <description>&lt;figure id=&quot;og_1692142769235&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;데이터 바인딩 및 MVVM - UWP applications&quot; data-og-description=&quot;데이터 바인딩은 MVVM(Model-View-ViewModel) UI 아키텍처 디자인 패턴의 핵심이며 UI 및 비 UI 코드 간의 느슨한 결합을 가능하게 합니다.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/ko-kr/windows/uwp/data-binding/data-binding-and-mvvm&quot; data-og-url=&quot;https://learn.microsoft.com/ko-kr/windows/uwp/data-binding/data-binding-and-mvvm&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/GpM2U/hyTCJRGFa0/xbwBuvkHfDrP9Ucq2XszH1/img.png?width=1201&amp;amp;height=630&amp;amp;face=0_0_1201_630&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/ko-kr/windows/uwp/data-binding/data-binding-and-mvvm&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/ko-kr/windows/uwp/data-binding/data-binding-and-mvvm&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/GpM2U/hyTCJRGFa0/xbwBuvkHfDrP9Ucq2XszH1/img.png?width=1201&amp;amp;height=630&amp;amp;face=0_0_1201_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;데이터 바인딩 및 MVVM - UWP applications&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;데이터 바인딩은 MVVM(Model-View-ViewModel) UI 아키텍처 디자인 패턴의 핵심이며 UI 및 비 UI 코드 간의 느슨한 결합을 가능하게 합니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;다른 블로그 글 보다는 공식 문서가 좋을 것 같아 microsoft 공식 문서를 가져왔다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;  MVVM이란?&lt;/span&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;span style=&quot;color: #333333;&quot;&gt;UI 및 비 UI 코드를 분리하기 위한 UI 아키텍처 디자인 패턴&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Model, View, ViewModel로 분리&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Model : 데이터를 관리하는 클래스&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;View : 화면 UI&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;ViewModel : 화면과 관련된 데이터를 관리하는 클래스&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;repository : 데이터를 구하는 클래스&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;MVVM의 장점&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;단위 테스트 간소화&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: left;&quot;&gt;유지 관리 효율성 향상&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: left;&quot;&gt;각 컴포넌트들의 분리로 &lt;span style=&quot;background-color: #f7f7f8; text-align: left;&quot;&gt;디자이너와 개발자가&amp;nbsp;&lt;/span&gt;병렬 작업 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: left;&quot;&gt;LifeCycle 관리&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;ViewModel은 View의 라이프사이클을 이해하고 관리할 수 있음&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;이로 인해 메모리 누수와 같은 문제를 방지하고, 화면 회전과 같은 &lt;span style=&quot;text-align: left;&quot;&gt;View&lt;/span&gt;의 상태 변화에 유연하게 대응 가능&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;MVVM의 단점&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;코드가 분리되면서 오히려 복잡해질 수 있음&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;오히려 작은 프로젝트에는 적합하지 않을 수 있음&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;높은 학습 곡선&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;추가적인 오버헤드의 발생&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;ViewModel&lt;/span&gt;은 &lt;span style=&quot;text-align: left;&quot;&gt;View&lt;/span&gt;와 Data 사이의 중간 역할&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;데이터 변환이나 처리 과정이 늘어나면서 약간의 성능 저하가 발생할 수 있음&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Computer/Android</category>
      <category>Android</category>
      <category>Android Studio</category>
      <category>MVVM</category>
      <category>안드로이드</category>
      <category>안드로이드 스튜디오</category>
      <author>Wise-99</author>
      <guid isPermaLink="true">https://wise-99.tistory.com/206</guid>
      <comments>https://wise-99.tistory.com/206#entry206comment</comments>
      <pubDate>Thu, 24 Aug 2023 19:31:06 +0900</pubDate>
    </item>
    <item>
      <title>[개인 회고] 13주차 회고(7/28 - 8/3)</title>
      <link>https://wise-99.tistory.com/205</link>
      <description>&lt;h2 style=&quot;background-color: #ffffff; color: #5c5c5c; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;&amp;nbsp;이번 주 목표&amp;nbsp;&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;[ V ] 코딩 테스트 일주일 3문제&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;[ X ] 이번 주 강의 정리하여 블로그 작성&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;SE-3cadc2f2-fc0f-4209-884b-0ed59bb8dad9&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;SE-68673338-fac5-43eb-bb44-df1340e9968e&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;SE-64d04444-0684-40d4-a963-b406c6595dd3&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;SE-dfb7bbe7-96d0-4ea1-9417-01e9412ed1cd&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;&amp;nbsp;Liked(좋았던 점)&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h4 id=&quot;SE-64e5db2a-2a35-40ed-a3ae-579ea3301ee8&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;MVVM 이해 및 적용&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;MVVM에 대해서 이론으로만 알고 있었는데 직접 실습해보기에는 어려웠다. Kotlin으로 작성된 코드를 찾아봐도 이해하기 어렵거나 어떤 분이 작성했냐에 따라 많이 달랐기 때문이다.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;강사님의 설명을 토대로 LiveData와 ViewModel을 이용해서 MVVM을 사용해볼 수 있어서 좋았다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;SE-348a1c3a-13a0-42c1-84ee-0e513b29a1b9&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;SE-93a7016c-d125-40c4-b4d9-dc46562370ad&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;SE-0a50bdd1-014e-4e41-84f9-6c166aed0e57&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;SE-45b49952-ab1a-4f32-84cb-5c86aa2e3972&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;✔️&lt;/b&gt;&lt;b&gt;&amp;nbsp;Lacked (아쉬웠던 점)&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h4 id=&quot;SE-2fcee1bf-70c7-4546-9377-8994a820f336&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;강의 내용 정리 미흡&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;MVVM에 대해 이해는 했으나 어떻게 정리를 해야할지 감이 잡히지 않아 미루고 있었다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여러 기업에서 실제로 사용하거나 면접에서도 자주 물어보는 주제인 만큼 확실하게 정리를 해놔야겠다. 개발자 공식 문서도 많이 참고를 해야할 것 같다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;SE-94b01dd8-6ae0-4b6e-b051-3fabe5143419&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;SE-ebf70da8-73b4-460e-b1a4-4f387f3e100c&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;SE-7507d6f7-da1b-4151-a90d-d30e63f1caa2&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;SE-dd5110b9-2937-4c83-a27c-f3ce29be46cc&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;&amp;nbsp;Learned (배운 점)&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;MVVM&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Model, View, ViewModel로 이루어진 소프트웨어 아키텍처 패턴&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;Model : 데이터 처리를 담당&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;View : 사용자에게 보여지는 화면(UI)으로 사용자와 상호작용이 가능&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;ViewModel : View와 ViewModel 사이를 연결해주는 역할&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;장점&lt;/span&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;데이터 처리 로직과 UI 디자인 간 상호 영향이 적어 개발에 용이&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;독립성을 유지하기 때문에 효율적인 유닛테스트가 가능&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;단점&lt;/span&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;간단한 UI에서 오히려 ViewModel을 설계하는 어려움이 있을 수 있음&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc; color: #000000;&quot;&gt;복잡해질수록 ViewModel이 빠르게 비대해질 수 있음&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc; color: #000000;&quot;&gt;표준화된 틀이 존재하지 않아 사람마다 이해가 다름&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;SE-94b01dd8-6ae0-4b6e-b051-3fabe5143419&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;SE-1727ac28-ddd1-42a7-9653-6e724186d7cc&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;SE-348fe562-9849-4d32-88ac-b8eb49347503&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;SE-c14b3ec2-1de2-42a6-a2a4-ef2b72dd2e1c&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;SE-81bb6cf1-fffb-4bed-ae6c-bbd6e4dfc4f3&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;zwj; &lt;/b&gt;&lt;b&gt;&amp;nbsp;Longed for (앞으로 바라는 점)&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h4 id=&quot;SE-2fcee1bf-70c7-4546-9377-8994a820f336&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;MVVM 패턴 관련 정리&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;블로그에 MVVM 코드와 이론 부분을 정리하여 작성해놔야겠다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;요즘 신입 모집 공고에서도 보이는 만큼 자세하게 공부해볼 필요가 있는 것 같다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>개인 회고</category>
      <author>Wise-99</author>
      <guid isPermaLink="true">https://wise-99.tistory.com/205</guid>
      <comments>https://wise-99.tistory.com/205#entry205comment</comments>
      <pubDate>Mon, 7 Aug 2023 10:21:19 +0900</pubDate>
    </item>
    <item>
      <title>[52일차 Android] XML/JSON Parsing</title>
      <link>https://wise-99.tistory.com/204</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;출처 : 안드로이드 앱스쿨 2기 윤재성 강사님 수업 PPT&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  XML&amp;nbsp;Parsing&lt;/span&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;div style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;eXtensible Markup Language&lt;/span&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;데이터를 태그라는 것으로 묶어 관리하는 기법&lt;/span&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;XML 문서에서 추출한 모든 데이터는 문자열이므로 적당한 타입으로 변환하여 사용해야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;예제 코드&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;파싱에 사용할 XML 문서&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1964&quot; data-origin-height=&quot;1400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b60cp6/btso11V9SSg/omTOO0D8dtFHb4td4Sbn21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b60cp6/btso11V9SSg/omTOO0D8dtFHb4td4Sbn21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b60cp6/btso11V9SSg/omTOO0D8dtFHb4td4Sbn21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb60cp6%2Fbtso11V9SSg%2FomTOO0D8dtFHb4td4Sbn21%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;1964&quot; height=&quot;1400&quot; data-origin-width=&quot;1964&quot; data-origin-height=&quot;1400&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;MainActivity.kt&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1690418129141&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MainActivity : AppCompatActivity() {

    lateinit var activityMainBinding: ActivityMainBinding
    
    // XML 파싱을 할 주소
    val serverAddress = &quot;https://www.aviationweather.gov/adds/dataserver_current/httpparam?datasource=metars&amp;amp;requestType=retrieve&amp;amp;format=xml&amp;amp;mostRecentForEachStation=constraint&amp;amp;hoursBeforeNow=1.25&amp;amp;stationString=KDE&quot;

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(activityMainBinding.root)

        activityMainBinding.run {

            button.setOnClickListener {
                thread {
                    // 접속 주소를 관리하는 객체 생성
                    val url = URL(serverAddress)
                    
                    // 접속
                    val httpUrlConnection = url.openConnection() as HttpURLConnection
                    // 웹 브라우저 종류를 확인할 수도 있기 때문에
                    httpUrlConnection.addRequestProperty(&quot;User-Agent&quot;, &quot;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36&quot;)

                    // DOM 방식으로 XML 문서를 분석할 수 있는 도구 생성
                    val documentBuilderFactory = DocumentBuilderFactory.newInstance()
                    val documentBuilder = documentBuilderFactory.newDocumentBuilder()
                    // 분석 도구를 이용해 XML 문서를 분석하여 각 태그들을 모두 객체로 생성한다.
                    // 태그들을 관리하는 객체를 반환한다.
                    val document = documentBuilder.parse(httpUrlConnection.inputStream)

                    // 최상위 태그를 가져온다.
                    val root = document.documentElement
                    // data tag를 가져온다.
                    val dataTag = root.getElementsByTagName(&quot;data&quot;)
                    // METAR tag를 가져온다.
                    val dataElement = dataTag.item(0) as Element
                    val METATag = dataElement.getElementsByTagName(&quot;METAR&quot;)

                    runOnUiThread {
                        textView.text = &quot;&quot;
                    }

                    // 태그의 수 만큼 반복한다.
                    for (idx in 0 until METATag.length){
                        // idx번째 태그 객체를 가져온다.
                        val METAElement = METATag.item(idx) as Element

                        // METAR 태그 내에서 필요한 태그들을 가져온다.
                        val rawTextList = METAElement.getElementsByTagName(&quot;raw_text&quot;)
                        val stationIdList = METAElement.getElementsByTagName(&quot;station_id&quot;)
                        val latitudeList = METAElement.getElementsByTagName(&quot;latitude&quot;)
                        val longitudeList = METAElement.getElementsByTagName(&quot;longitude&quot;)

                        val rowTextTag = rawTextList.item(0) as Element
                        val stationIdTag = stationIdList.item(0) as Element
                        val latitudeTag = latitudeList.item(0) as Element
                        val longitudeTag = longitudeList.item(0) as Element

                        // 질문 내의 데이터를 가져온다.
                        val rowText = rowTextTag.textContent
                        val stationId = stationIdTag.textContent
                        val latitude = latitudeTag.textContent.toDouble()
                        val longitude = longitudeTag.textContent.toDouble()

                        runOnUiThread {
                            textView.append(&quot;rowText : ${rowText}\n&quot;)
                            textView.append(&quot;stationId : ${stationId}\n&quot;)
                            textView.append(&quot;latitude : ${latitude}\n&quot;)
                            textView.append(&quot;longitude : ${longitude}\n\n&quot;)
                        }
                    }
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  JSON Parsing&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JavaScript Object Notation의 약자&lt;/li&gt;
&lt;li&gt;JavaScript 언어에서 객체를 표현하는 문법을 사용해 데이터를 표현하는 비표준 문서&lt;/li&gt;
&lt;li&gt;다양한 데이터 타입 표현 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;JSON 데이터 분석&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;{ } : JSONObject, 이름 - 값 형태&lt;/li&gt;
&lt;li&gt;[ ] : JSONArray, 0부터 1씩 증가하는 순서값을 가지고 관리&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예제 코드&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;파싱할 JSON 문서&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;1034&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0vymF/btso8zXYuqp/TGBISFAyDbq7cr5L5yHqFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0vymF/btso8zXYuqp/TGBISFAyDbq7cr5L5yHqFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0vymF/btso8zXYuqp/TGBISFAyDbq7cr5L5yHqFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0vymF%2Fbtso8zXYuqp%2FTGBISFAyDbq7cr5L5yHqFk%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;316&quot; height=&quot;451&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;1034&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;MainActivity.kt&lt;/h4&gt;
&lt;pre id=&quot;code_1690419863978&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MainActivity : AppCompatActivity() {

    lateinit var activityMainBinding: ActivityMainBinding

    // 사용할 JSON 문서 주소
    val serverAddress = &quot;https://api.nationalize.io/?name=michael&quot;

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(activityMainBinding.root)

        activityMainBinding.run{
            button.setOnClickListener {
                thread {
                    // URL 객체 생성
                    val url = URL(serverAddress)
                    // 접속 후 스트림 추출
                    val httpURLConnection = url.openConnection() as HttpURLConnection

                    val inputStreamReader = InputStreamReader(httpURLConnection.inputStream, &quot;UTF-8&quot;)
                    val bufferedReader = BufferedReader(inputStreamReader)

                    var str:String? = null
                    val stringBuffer = StringBuffer()
                    
                    // 문서의 마지막까지 읽어온다.
                    do{
                        str = bufferedReader.readLine()
                        if(str != null){
                            stringBuffer.append(str)
                        }
                    }while(str != null)

                    val data = stringBuffer.toString()

                    runOnUiThread {
                        textView.text = &quot;&quot;
                    }

                    // 최상위가 { }이므로 JSONObject를 생성한다.
                    val root = JSONObject(data)

                    // count
                    val count = root.getInt(&quot;count&quot;)
                    // name
                    val name = root.getString(&quot;name&quot;)

                    runOnUiThread {
                        textView.text = &quot;count : ${count}\n&quot;
                        textView.append(&quot;name : ${name}\n&quot;)
                    }

                    // country
                    val countryArray = root.getJSONArray(&quot;country&quot;)

                    for(idx in 0 until countryArray.length()){
                        // idx번째 JSONObject 추출
                        val countryObject = countryArray.getJSONObject(idx)
                        
                        // country_id 추출
                        val country_id = countryObject.getString(&quot;country_id&quot;)
                        
                        // probability 추출
                        val probability = countryObject.getDouble(&quot;probability&quot;)

                        runOnUiThread {
                            textView.append(&quot;country_id : ${country_id}\n&quot;)
                            textView.append(&quot;probability : ${probability}\n\n&quot;)
                        }
                    }
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Android Studio</category>
      <category>json</category>
      <category>parsing</category>
      <category>xml</category>
      <category>안드로이드</category>
      <category>안드로이드 스튜디오</category>
      <author>Wise-99</author>
      <guid isPermaLink="true">https://wise-99.tistory.com/204</guid>
      <comments>https://wise-99.tistory.com/204#entry204comment</comments>
      <pubDate>Thu, 27 Jul 2023 18:42:21 +0900</pubDate>
    </item>
    <item>
      <title>[52일차 Android] Socket 통신</title>
      <link>https://wise-99.tistory.com/203</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;출처 : 안드로이드 앱스쿨 2기 윤재성 강사님 수업 PPT&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  Socket&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버와 클라이언트 간에 연결된 Socket을 통해 통신하는 방법으로 프로그램 종료 시 까지 온라인으로 유지할 수 있는 특징을 가지고 있다.&lt;/li&gt;
&lt;li&gt;안드로이드 Socket 네트워크 구현 방법은 일반 자바 프로그램에서 구현하는 방법과 동일하다.&lt;/li&gt;
&lt;li&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예제 코드&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Server.kt&lt;/h4&gt;
&lt;pre id=&quot;code_1690416895487&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fun main() {
    // 사용할 포트 번호
    val portNumber = 50000
    
    // 서버 객체 생성
    val serverSocket = ServerSocket(portNumber)
    
    // 사용자 접속 대기
    println(&quot;사용자 접속 대기&quot;)
    val socket = serverSocket.accept()
    println(socket)

    // 스트림 추출
    val inputStream = socket.getInputStream()
    val dataInputStream = DataInputStream(inputStream)

    val outputStream = socket.getOutputStream()
    val dataOutputStream = DataOutputStream(outputStream)

    // 클라이언트에게 데이터 전달
    dataOutputStream.writeInt(100)
    dataOutputStream.writeDouble(11.11)
    dataOutputStream.writeBoolean(true)
    dataOutputStream.writeUTF(&quot;서버가 보낸 문자열&quot;)

    // 클라이언트에게 데이터 수신
    val data1 = dataInputStream.readInt()
    val data2 = dataInputStream.readDouble()
    val data3 = dataInputStream.readBoolean()
    val data4 = dataInputStream.readUTF()

    println(&quot;data1 : $data1&quot;)
    println(&quot;data2 : $data2&quot;)
    println(&quot;data3 : $data3&quot;)
    println(&quot;data4 : $data4&quot;)

    socket.close()
    serverSocket.close()
}&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;MainActivity.kt(Client)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;안드로이드는 모든 네트워크 코드는 쓰래드로 운영해야 한다.&lt;/li&gt;
&lt;li&gt;네트워크 특히 모바일은 통신에 문제가 발생할 가능성이 높기 때문에 오류 발생시 안드로이드 애플리케이션 자체가 종료되는 것을 예방하기 위함이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1690416967279&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MainActivity : AppCompatActivity() {

    lateinit var activityMainBinding: ActivityMainBinding

    // server와 동일한 IP, Port 번호 지정
    val SERVER_IP = &quot;192.168.0.24&quot;
    val SERVER_PORT = 50000

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(activityMainBinding.root)

        activityMainBinding.run {
            button.setOnClickListener {
                
                thread {
                    val socket = Socket(SERVER_IP, SERVER_PORT)

                    // 스트림 추출
                    val inputStream = socket.getInputStream()
                    val outputStream = socket.getOutputStream()

                    val dataInputStream = DataInputStream(inputStream)
                    val dataOutputStream = DataOutputStream(outputStream)

                    // 서버로부터 데이터 수신
                    val data1 = dataInputStream.readInt()
                    val data2 = dataInputStream.readDouble()
                    val data3 = dataInputStream.readBoolean()
                    val data4 = dataInputStream.readUTF()

                    // 서버에게 데이터 전달
                    dataOutputStream.writeInt(200)
                    dataOutputStream.writeDouble(22.22)
                    dataOutputStream.writeBoolean(false)
                    dataOutputStream.writeUTF(&quot;클라이언트가 보낸 문자열&quot;)

					// 서버로부터 받은 데이터로 화면 구성
                    runOnUiThread {
                        // textView.text = socket.toString()
                        textView.text = &quot;data1 : ${data1}\n&quot;
                        textView.append(&quot;data2 : ${data2}\n&quot;)
                        textView.append(&quot;data3 : ${data3}\n&quot;)
                        textView.append(&quot;data4 : $data4&quot;)
                    }

                    socket.close()
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Computer/Android</category>
      <category>Android</category>
      <category>Android Studio</category>
      <category>Client</category>
      <category>server</category>
      <category>socket</category>
      <category>안드로이드</category>
      <category>안드로이드 스튜디오</category>
      <author>Wise-99</author>
      <guid isPermaLink="true">https://wise-99.tistory.com/203</guid>
      <comments>https://wise-99.tistory.com/203#entry203comment</comments>
      <pubDate>Thu, 27 Jul 2023 18:21:38 +0900</pubDate>
    </item>
    <item>
      <title>[52일차 Android] 앨범에서 사진 가져오기</title>
      <link>https://wise-99.tistory.com/202</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;출처 : 안드로이드 앱스쿨 2기 윤재성 강사님 수업 PPT&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  앨범에서&amp;nbsp;사진&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;앨범에서 사진을 가져올 때 다른 Activity를 실행시켜서 사진을 가져올 수 있다.&lt;/li&gt;
&lt;li&gt;Intent.ACTION_PICK&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예제 코드&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;AndroidManifest.xml&lt;/h4&gt;
&lt;pre id=&quot;code_1690188613737&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    ...
    
    &amp;lt;/application&amp;gt;
    
    &amp;lt;uses-permission android:name=&quot;android.permission.READ_EXTERNAL_STORAGE&quot;/&amp;gt;
    &amp;lt;uses-permission android:name=&quot;android.permission.ACCESS_MEDIA_LOCATION&quot;/&amp;gt;

&amp;lt;/manifest&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;MainActivity.kt&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;427&quot; data-origin-height=&quot;470&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVspWw/btsoTmxtbde/3hGDuNM3tci2CkN9BBjOJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVspWw/btsoTmxtbde/3hGDuNM3tci2CkN9BBjOJk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVspWw/btsoTmxtbde/3hGDuNM3tci2CkN9BBjOJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVspWw%2FbtsoTmxtbde%2F3hGDuNM3tci2CkN9BBjOJk%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;320&quot; height=&quot;352&quot; data-origin-width=&quot;427&quot; data-origin-height=&quot;470&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;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;미디어 스캐너의 역할은 영상, 음원, 사진 등에 관련된 데이터를 추출하여 SQLiteDatabase에 저장한다.&lt;/li&gt;
&lt;li&gt;이 정보를 가져다 사용할 수 있는 Content Provider를 이용해 정보를 가져온다.&lt;/li&gt;
&lt;/ul&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;안드로이드 버전이 10(Q) 이상이면 Content Resolver로 바로 접근 가능하다.&lt;/li&gt;
&lt;li&gt;10 버전 이하이면&amp;nbsp; Contetnt Provider를 통해 이미지 경로를 가져와 접근해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1690189411052&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;nbsp;|&amp;nbsp; Android 개발자 &amp;nbsp;|&amp;nbsp; Android Developers&quot; data-og-description=&quot;콘텐츠 제공자 기본 사항 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 콘텐츠 제공자는 중앙 저장소로의 데이터 액세스를 관리합니다. 제공자는 Android 애&quot; data-og-host=&quot;developer.android.com&quot; data-og-source-url=&quot;https://developer.android.com/guide/topics/providers/content-provider-basics?hl=ko&quot; data-og-url=&quot;https://developer.android.com/guide/topics/providers/content-provider-basics?hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/qaSdG/hyTpdrS2Be/WaaH0r2j5QA2i3E9NusZmK/img.png?width=1201&amp;amp;height=676&amp;amp;face=0_0_1201_676,https://scrap.kakaocdn.net/dn/ljg6R/hyTqBxUntF/akKfn0J0WHw9YRiUPv0Zk0/img.png?width=644&amp;amp;height=450&amp;amp;face=0_0_644_450,https://scrap.kakaocdn.net/dn/egZFbi/hyTo9XjVNC/PD6dC4b9BQyFARIgQ79q3k/img.png?width=427&amp;amp;height=470&amp;amp;face=0_0_427_470&quot;&gt;&lt;a href=&quot;https://developer.android.com/guide/topics/providers/content-provider-basics?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.android.com/guide/topics/providers/content-provider-basics?hl=ko&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/qaSdG/hyTpdrS2Be/WaaH0r2j5QA2i3E9NusZmK/img.png?width=1201&amp;amp;height=676&amp;amp;face=0_0_1201_676,https://scrap.kakaocdn.net/dn/ljg6R/hyTqBxUntF/akKfn0J0WHw9YRiUPv0Zk0/img.png?width=644&amp;amp;height=450&amp;amp;face=0_0_644_450,https://scrap.kakaocdn.net/dn/egZFbi/hyTo9XjVNC/PD6dC4b9BQyFARIgQ79q3k/img.png?width=427&amp;amp;height=470&amp;amp;face=0_0_427_470');&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;nbsp;|&amp;nbsp; Android 개발자 &amp;nbsp;|&amp;nbsp; Android Developers&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;콘텐츠 제공자 기본 사항 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 콘텐츠 제공자는 중앙 저장소로의 데이터 액세스를 관리합니다. 제공자는 Android 애&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.android.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1690188220967&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MainActivity : AppCompatActivity() {

    lateinit var activityMainBinding: ActivityMainBinding

    // 확인할 권한 목록
    val permissionList = arrayOf(
        android.Manifest.permission.READ_EXTERNAL_STORAGE,
        android.Manifest.permission.ACCESS_MEDIA_LOCATION
    )

    lateinit var albumActivityLauncher : ActivityResultLauncher&amp;lt;Intent&amp;gt;

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(activityMainBinding.root)

        // 권한 확인
        requestPermissions(permissionList, 0)

        val contract1 = ActivityResultContracts.StartActivityForResult()
        albumActivityLauncher = registerForActivityResult(contract1){
           
           if(it?.resultCode == RESULT_OK){
                
                // 선택한 이미지에 접근할 수 있는 Uri 객체를 추출한다.
                val uri = it.data?.data

                if (uri != null){
                    // 안드로이드 10(Q) 이상이라면
                    if (Build.VERSION.SDK_INT &amp;gt;= Build.VERSION_CODES.Q){
                        
                        // 이미지를 생성할 수 있는 디코더를 생성한다.
                        val source = ImageDecoder.createSource(contentResolver, uri)
                        
                        // Bitmap객체를 생성한다.
                        val bitmap = ImageDecoder.decodeBitmap(source)
						
                        // 메인 화면의 imageView에 해당 이미지를 설정한다.
                        activityMainBinding.imageView.setImageBitmap(bitmap)
                        
                    } else {
                        // Content Provider를 통해 이미지 데이터 정보를 가져온다.
                        val cursor = contentResolver.query(uri,null,null,null,null)

                        if(cursor != null) {
                            cursor.moveToNext()

                            // 이미지의 경로를 가져온다.
                            val idx = cursor.getColumnIndex(MediaStore.Images.Media.DATA)
                            val source = cursor.getString(idx)

                            // 이미지를 생성하여 보여준다.
                            val bitmap = BitmapFactory.decodeFile(source)
                            activityMainBinding.imageView.setImageBitmap(bitmap)
                        }
                    }
                }
            }
        }

        activityMainBinding.run {
            button.setOnClickListener {
                // 앨범에서 사진을 선택할 수 있는 Activity를 실행한다.
                val newIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
                
                // 실행할 액티비티의 MIME 타입 설정(이미지로 설정)
                newIntent.setType(&quot;image/*&quot;)
                
                // 선택할 파일의 타입을 지정(안드로이드 os가 이미지에 대한 사진 작업을 할 수 있도록)
                val mineType = arrayOf(&quot;image/*&quot;)
                newIntent.putExtra(Intent.EXTRA_MIME_TYPES, mineType)
                
                // 액티비티 실행
                albumActivityLauncher.launch(newIntent)
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Computer/Android</category>
      <category>Android</category>
      <category>Android Studio</category>
      <category>Content Provider</category>
      <category>Content Resolver</category>
      <category>안드로이드</category>
      <category>안드로이드 스튜디오</category>
      <category>앨범 접근</category>
      <category>이미지 가져오기</category>
      <author>Wise-99</author>
      <guid isPermaLink="true">https://wise-99.tistory.com/202</guid>
      <comments>https://wise-99.tistory.com/202#entry202comment</comments>
      <pubDate>Mon, 24 Jul 2023 19:09:22 +0900</pubDate>
    </item>
    <item>
      <title>[52일차 Android] 사진 촬영하기</title>
      <link>https://wise-99.tistory.com/201</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;출처 : 안드로이드 앱스쿨 2기 윤재성 강사님 수업 PPT&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  사진 촬영하기&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;카메라 기능이 구현되어 있는 Activity를 실행하여 사진을 촬영할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;만약 카메라 기능이 구현되어 있는 애플리케이션이 다수 설치되어 있다면 앱을 선택하면 된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;기본 카메라 사용하는 방법을 사용하면 사진 원본이 아닌 썸네일 이미지를 가져오게 된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;촬영된 사진의 원본을 가져오려면 촬영된 사진을 파일로 저장한 다음 파일로부터 이미지 데이터를 가져오는 방식으로 개발해야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;예제 코드&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;AndroidManifest.xml&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1690167395249&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    &amp;lt;application
    
        ...
        
        &amp;lt;!-- 촬영된 사진을 저장하는 프로바이더 --&amp;gt;
        &amp;lt;provider
            android:authorities=&quot;com.test.getpicture.file_provider&quot;
            android:name=&quot;androidx.core.content.FileProvider&quot;
            android:exported=&quot;false&quot;
            android:grantUriPermissions=&quot;true&quot;&amp;gt;
            &amp;lt;meta-data
                android:name=&quot;android.support.FILE_PROVIDER_PATHS&quot;
                android:resource=&quot;@xml/file_path&quot;/&amp;gt;
        &amp;lt;/provider&amp;gt;
            
        ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;xml / file_path.xml&lt;/h4&gt;
&lt;pre class=&quot;kotlin&quot; style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
&amp;lt;paths xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;&amp;gt;
    &amp;lt;external-path
        name=&quot;storage/emulated/0&quot;
        path=&quot;.&quot;/&amp;gt;
&amp;lt;/paths&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;MainActivity.kt&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1690166736460&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MainActivity : AppCompatActivity() {

    lateinit var activityMainBinding: ActivityMainBinding
    lateinit var cameraLauncher: ActivityResultLauncher&amp;lt;Intent&amp;gt;

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)

        // 사진 활영을 위한 런처
        val contract1 = ActivityResultContracts.StartActivityForResult()

        // 아래의 코드는 썸네일을 가져온다.(사진이 흐릿함)
        cameraLauncher = registerForActivityResult(contract1){
        
            // 사진을 촬영하고 촬영한 사진을 선택하고 돌아왔을 경우
            if(it?.resultCode == RESULT_OK){
            
                // Intent로부터 사진 데이터를 가져온다.
                // 안드로이드 버전 별로 분기
                if(Build.VERSION.SDK_INT &amp;gt;= Build.VERSION_CODES.TIRAMISU) {
                
                    val bitmap = it.data?.getParcelableExtra(&quot;data&quot;, Bitmap::class.java)!!
                    activityMainBinding.imageView.setImageBitmap(bitmap)
                    
                } else {
                    val bitmap = it.data?.getParcelableExtra&amp;lt;Bitmap&amp;gt;(&quot;data&quot;)!!
                    activityMainBinding.imageView.setImageBitmap(bitmap)
                }
            }
        }

        activityMainBinding.run {
            // 아래의 코드는 썸네일을 가져온다.(사진이 흐릿함)
            button.setOnClickListener {
                // 사진 활영을 위한 Activity를 실행한다.
                val newIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
                cameraLauncher.launch(newIntent)
            }
        }

        setContentView(activityMainBinding.root)
    }
}&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;결과&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;사진이 흐릿하며 실제 기기에서 테스트하면 사진이 회전되어 나타난다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;642&quot; data-origin-height=&quot;1050&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qkcvR/btsoyrUYKiX/BjKaY3AcjxfrL6B1ssXFcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qkcvR/btsoyrUYKiX/BjKaY3AcjxfrL6B1ssXFcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qkcvR/btsoyrUYKiX/BjKaY3AcjxfrL6B1ssXFcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqkcvR%2FbtsoyrUYKiX%2FBjKaY3AcjxfrL6B1ssXFcK%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;302&quot; height=&quot;494&quot; data-origin-width=&quot;642&quot; data-origin-height=&quot;1050&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  사진 보정하기&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;ImageView는 특정 크기보다 큰 이미지는 표현할 수 없으며&amp;nbsp;이 크기는 단말기마다 다르다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이미지의 용량이 너무 크면 서버와 송수신할 때 데이터를 너무 많이 사용하게 된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;스마트폰을 세워서 촬영할 경우 사진이 90도로 회전되어 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이와 같은 이유로 카메라로 촬영된 사진은 보정해야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;예제 코드&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;xml/file_path.xml&lt;/h4&gt;
&lt;pre id=&quot;code_1690167585752&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
&amp;lt;paths xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;&amp;gt;
    &amp;lt;!-- 실제 단말기에서도 동일한 경로 --&amp;gt;
    &amp;lt;external-path
        name=&quot;storage/emulated/0&quot;
        path=&quot;.&quot;/&amp;gt;
&amp;lt;/paths&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;MainActivity.kt&lt;/h4&gt;
&lt;pre id=&quot;code_1690167736606&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MainActivity : AppCompatActivity() {

    lateinit var activityMainBinding: ActivityMainBinding
    lateinit var cameraLauncher: ActivityResultLauncher&amp;lt;Intent&amp;gt;

    // 이미지가 저장될 위치
    lateinit var filePath : String
    // 저장된 파일에 접근하기 위한 Uri(import android.uri)
    lateinit var contentUri:Uri

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)

        // 애플리케이션을 위한 외부 저장소 경로를 가져온다.
        // xml/file_path.xml에 등록한 경로를 가져온다.
        filePath = getExternalFilesDir(null).toString()

        // 사진 촬영을 위한 런처
        val contract1 = ActivityResultContracts.StartActivityForResult()

        cameraLauncher = registerForActivityResult(contract1){
            if (it?.resultCode == RESULT_OK){
                // uri를 이용해 이미지에 접근하여 Bitmap 객체로 생성한다.
                val bitmap = BitmapFactory.decodeFile(contentUri.path)

                // 이미지의 크기롤 조정한다.
                // 이미지의 축소/확대 비율을 구한다.
                val ratio = 1024.0 / bitmap.width
                // 세로 길이를 구한다.
                val targetHeight = (bitmap.height * ratio).toInt()

                // 크기를 조정한 Bitmap을 생성한다.
                val bitmap2 = Bitmap.createScaledBitmap(bitmap, 1024, targetHeight, false)

                // 회전 각도를 가져온다.
                val degree = getDegree(contentUri)

                // 회전 이미지를 생성하기 위한 변환 행렬
                val matrix = Matrix()
                matrix.postRotate(degree.toFloat())

                // 회전 행렬을 적용하여 회전된 이미지를 생성한다.
                // 원본 이미지, 원본 이미지에서의  X좌표, 원본 이미지에서의 Y좌표, 원본 가로 길이, 원본 세로 길이, 변환행렬, 필터 정보
                // 원본 이미지에서 지정된 x, y 좌표를 찍고 지정된 가로 세로 길이만큼의 이미지 데이터를 가져와 변환 행렬을 적용하여 이미지를 변환한다.
                val bitmap3 = Bitmap.createBitmap(bitmap2, 0,0, bitmap2.width, bitmap2.height, matrix, false)

                activityMainBinding.imageView.setImageBitmap(bitmap3)

                // 이미지 파일은 삭제한다.
                val file = File(contentUri.path)
                file.delete()
            }
        }


        activityMainBinding.run {

            button.setOnClickListener {
                val newIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)

                // 촬영될 사진이 저장될 파일 이름
                val now = System.currentTimeMillis()
                val fileName = &quot;/temp_${now}.jpg&quot;
                // 경로 + 파일 이름
                val picPath = &quot;${filePath}/${fileName}&quot;

                val file = File(picPath)

                // 사진이 저장될 경로를 관리할 uri 객체를 생성한다.
                contentUri = FileProvider.getUriForFile(this@MainActivity, &quot;com.test.getpicture.file_provider&quot;, file)

                newIntent.putExtra(MediaStore.EXTRA_OUTPUT, contentUri)
                cameraLauncher.launch(newIntent)
            }
        }

        setContentView(activityMainBinding.root)
    }

    // 이미지 파일에 기록되어 있는 회전 정보를 가져온다.
    fun getDegree(uri:Uri) : Int{
        var exifInterface:ExifInterface? = null

        // 사진 파일로부터 tag 정보를 관리하는 객체를 추출한다.
        // 안드로이드 버전별 분기
        if (Build.VERSION.SDK_INT &amp;gt;= Build.VERSION_CODES.Q){
            val photoUri = MediaStore.setRequireOriginal(uri)
            // 스트림 추출
            val inputStream = contentResolver.openInputStream(photoUri)
            // ExifInterface 정보를 읽어 온다.
            exifInterface = ExifInterface(inputStream!!)
        } else {
            exifInterface = ExifInterface(uri.path!!)
        }

        var degree = 0

        if (exifInterface != null){
            // 각도 값을 가지고 온다.
            val orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1)

            when(orientation){
                ExifInterface.ORIENTATION_ROTATE_90 -&amp;gt; degree = 90
                ExifInterface.ORIENTATION_ROTATE_180 -&amp;gt; degree = 180
                ExifInterface.ORIENTATION_ROTATE_270 -&amp;gt; degree = 270
            }
        }

        return degree
    }
}&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;h3 data-ke-size=&quot;size23&quot;&gt;결과&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;662&quot; data-origin-height=&quot;1064&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqEBAL/btsoNWTjWkX/l7SB2aQRKohuc0dtIkQVQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqEBAL/btsoNWTjWkX/l7SB2aQRKohuc0dtIkQVQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqEBAL/btsoNWTjWkX/l7SB2aQRKohuc0dtIkQVQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqEBAL%2FbtsoNWTjWkX%2Fl7SB2aQRKohuc0dtIkQVQ1%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;269&quot; height=&quot;432&quot; data-origin-width=&quot;662&quot; data-origin-height=&quot;1064&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Computer/Android</category>
      <category>Android</category>
      <category>Android Studio</category>
      <category>사진 보정</category>
      <category>사진 촬영</category>
      <category>사진 회전</category>
      <category>안드로이드</category>
      <category>안드로이드 스튜디오</category>
      <author>Wise-99</author>
      <guid isPermaLink="true">https://wise-99.tistory.com/201</guid>
      <comments>https://wise-99.tistory.com/201#entry201comment</comments>
      <pubDate>Mon, 24 Jul 2023 18:36:48 +0900</pubDate>
    </item>
    <item>
      <title>[개인 회고] 11주차 회고(7/14 - 7/20)</title>
      <link>https://wise-99.tistory.com/200</link>
      <description>&lt;h2 style=&quot;background-color: #ffffff; color: #5c5c5c; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;&amp;nbsp;이번 주 목표&amp;nbsp;&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;[ V ] 코딩 테스트 일주일 3문제&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;[ V ] 강의 정리하여 블로그 작성&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;[ V ] 오류 해결 과정 글쓰기&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;SE-3cadc2f2-fc0f-4209-884b-0ed59bb8dad9&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;SE-68673338-fac5-43eb-bb44-df1340e9968e&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;SE-64d04444-0684-40d4-a963-b406c6595dd3&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;SE-dfb7bbe7-96d0-4ea1-9417-01e9412ed1cd&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;&amp;nbsp;Liked(좋았던 점)&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h4 id=&quot;SE-64e5db2a-2a35-40ed-a3ae-579ea3301ee8&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Kotlin으로 Places API 연동하는 방법을 배웠다&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Java로 하는 방법은 알고 있었는데 Kotlin, JSON을 이용하여 연동하는 방법은 생각하지 못했었다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;원래 내가 했던 방식은 Places API를 Android용으로 적용했었는데 이번에는 웹 서비스용으로 적용하여 JSON으로 데이터를 받아 사용했다.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;원래 적용했던 방식보다 웹 서비스용 방식이 코드의 가독성이 좀 더 높은 것 같다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;개인 프로젝트에 WebView 적용&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;원래는 링크를 주고 브라우저 앱에서 해당 주소를 띄우도록 했었는데 WebView를 적용해보는 것이 좋겠다고 생각하게 되었다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;코드를 조금 수정하여 WebView를 적용, intent가 호출되어 발생하는 오류도 알게 되어 수정하는데 시간이 좀 걸렸다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;SE-348a1c3a-13a0-42c1-84ee-0e513b29a1b9&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;SE-93a7016c-d125-40c4-b4d9-dc46562370ad&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;SE-0a50bdd1-014e-4e41-84f9-6c166aed0e57&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;SE-45b49952-ab1a-4f32-84cb-5c86aa2e3972&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;✔️&lt;/b&gt;&lt;b&gt;&amp;nbsp;Lacked (아쉬웠던 점)&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h4 id=&quot;SE-2fcee1bf-70c7-4546-9377-8994a820f336&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;다시 생긴 조바심&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;수업을 들으면서 약간 현타가 온 것 같다. 취업할 수 있을까라는 생각이 든다. 생각해보니 면접을 본지도 오래되었고 제일 두려운 면접을 볼 생각에 어지러워진다..&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;슬슬 면접 준비를 해야할 것 같다. 더불어 이번주부터 이력서를 넣어야겠다고 생각하게 되었다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;SE-94b01dd8-6ae0-4b6e-b051-3fabe5143419&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;SE-ebf70da8-73b4-460e-b1a4-4f387f3e100c&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;SE-7507d6f7-da1b-4151-a90d-d30e63f1caa2&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;SE-dd5110b9-2937-4c83-a27c-f3ce29be46cc&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;&amp;nbsp;Learned (배운 점)&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;XML Parsing&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;XML 문서에서 추출한 모든 데이터는 문자열이므로 적당한 타입으로 변환하여 사용해야 한다.&lt;/span&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;JSON Parsing&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;JavaScript 언어에서 객체를 표현하는 문법을 사용해 데이터를 표현하는 비표준 문서&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;다양한 데이터 타입을 표현할 수 있어 많이 사용하는 문서 양식&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;SE-1727ac28-ddd1-42a7-9653-6e724186d7cc&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;SE-348fe562-9849-4d32-88ac-b8eb49347503&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;SE-c14b3ec2-1de2-42a6-a2a4-ef2b72dd2e1c&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;SE-81bb6cf1-fffb-4bed-ae6c-bbd6e4dfc4f3&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;zwj; &lt;/b&gt;&lt;b&gt;&amp;nbsp;Longed for (앞으로 바라는 점)&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h4 id=&quot;SE-2fcee1bf-70c7-4546-9377-8994a820f336&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;올해 안에 취업하기 위한 준비하기&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하반기 채용 공고도 나오고 있고, 면접 준비나 이력서를 다듬어보면서 취업에 대한 준비를 해야할 것 같다.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이력서 제출을 해서 면접도 봐보고 감을 다시 살려야 할 것 같다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>개인 회고</category>
      <author>Wise-99</author>
      <guid isPermaLink="true">https://wise-99.tistory.com/200</guid>
      <comments>https://wise-99.tistory.com/200#entry200comment</comments>
      <pubDate>Sat, 22 Jul 2023 17:00:19 +0900</pubDate>
    </item>
  </channel>
</rss>