<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>SH's Devlog</title>
    <link>https://seosh817.tistory.com/</link>
    <description>공부한 내용을 정리하는 개발 기록 블로그</description>
    <language>ko</language>
    <pubDate>Thu, 16 Apr 2026 18:47:32 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>seunghwaan</managingEditor>
    <item>
      <title>[React] React 사용을 위한 Node.js 설정 및 사용법</title>
      <link>https://seosh817.tistory.com/712</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Node.js 설치&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node.js 공식 사이트에서 LTS 버전으로 설치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nodejs.org/ko/download&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://nodejs.org/ko/download&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1772544780219&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;Node.js &amp;mdash; Node.js&amp;reg; 다운로드&quot; data-og-description=&quot;Node.js&amp;reg; is a free, open-source, cross-platform JavaScript runtime environment that lets developers create servers, web apps, command line tools and scripts.&quot; data-og-host=&quot;nodejs.org&quot; data-og-source-url=&quot;https://nodejs.org/ko/download&quot; data-og-url=&quot;https://nodejs.org/ko/download&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bbkO03/dJMb9efbv2T/4zdSqWvgh82ktdShL6qK40/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/gm8nQ/dJMb9aKCGc2/Q30Y9sVT3wGQn9nM5CqQqK/img.png?width=224&amp;amp;height=256&amp;amp;face=0_0_224_256&quot;&gt;&lt;a href=&quot;https://nodejs.org/ko/download&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://nodejs.org/ko/download&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bbkO03/dJMb9efbv2T/4zdSqWvgh82ktdShL6qK40/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/gm8nQ/dJMb9aKCGc2/Q30Y9sVT3wGQn9nM5CqQqK/img.png?width=224&amp;amp;height=256&amp;amp;face=0_0_224_256');&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;Node.js &amp;mdash; Node.js&amp;reg; 다운로드&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Node.js&amp;reg; is a free, open-source, cross-platform JavaScript runtime environment that lets developers create servers, web apps, command line tools and scripts.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;nodejs.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치 후 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;node -v&lt;/span&gt;&lt;/b&gt;, &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;npm -v&lt;/span&gt;&lt;/b&gt; 명령어로 설치 여부 확인.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;318&quot; data-origin-height=&quot;56&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8RmO3/dJMcaiCqk0x/9dedGXQnkvI3Ray3NwP6vK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8RmO3/dJMcaiCqk0x/9dedGXQnkvI3Ray3NwP6vK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8RmO3/dJMcaiCqk0x/9dedGXQnkvI3Ray3NwP6vK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8RmO3%2FdJMcaiCqk0x%2F9dedGXQnkvI3Ray3NwP6vK%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;500&quot; height=&quot;88&quot; data-origin-width=&quot;318&quot; data-origin-height=&quot;56&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;패키지 설정&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;패키지&lt;/span&gt;&lt;/b&gt; -&amp;gt; Node.js에서 사용하는 프로그램의 단위&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;npm init&lt;/span&gt;&lt;/b&gt; 명령어를 이용해 패키지를 생성할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;634&quot; data-origin-height=&quot;621&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l9wpk/dJMcaioVt8S/V5ngk5eNr6WHLUGcDPg4DK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l9wpk/dJMcaioVt8S/V5ngk5eNr6WHLUGcDPg4DK/img.png&quot; data-alt=&quot;npm init&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l9wpk/dJMcaioVt8S/V5ngk5eNr6WHLUGcDPg4DK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl9wpk%2FdJMcaioVt8S%2FV5ngk5eNr6WHLUGcDPg4DK%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;634&quot; height=&quot;621&quot; data-origin-width=&quot;634&quot; data-origin-height=&quot;621&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;npm init&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패키지 생성이 완료되면 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;package.json&lt;/span&gt;&lt;/b&gt; 이라는 설정 파일이 생성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pacakage.json을 열어보면 아래와 같이 패키지를 생성할 때 설정한 기본적인 내용들이 들어있다.&lt;/p&gt;
&lt;pre id=&quot;code_1772546058914&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;section03&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;description&quot;: &quot;&quot;,
  &quot;license&quot;: &quot;ISC&quot;,
  &quot;author&quot;: &quot;&quot;,
  &quot;main&quot;: &quot;index.js&quot;,
  &quot;scripts&quot;: {
    &quot;test&quot;: &quot;echo \&quot;Error: no test specified\&quot; &amp;amp;&amp;amp; exit 1&quot;,
   },
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태에서 새로운 자바스크립트 파일을 만들고 실행시킬 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 폴더 내에 index.js 파일을 생성하고 아래와 같은 내용을 입력한다.&lt;/p&gt;
&lt;pre id=&quot;code_1772546152962&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log(&quot;안녕 Node.js&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 node 명령어로 index.js를 실행시킬 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;node src/index.js&lt;/span&gt;&lt;/b&gt; 명령어로 노드를 실행할 수 있으나, package.json의 scripts에 명령어를 입력해주면 start에 node src/index.js로 값을 입력해주면 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;npm run start&lt;/span&gt;&lt;/b&gt; 명령어를 입력하여 실행 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1772544506272&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;section03&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;description&quot;: &quot;&quot;,
  &quot;license&quot;: &quot;ISC&quot;,
  &quot;author&quot;: &quot;&quot;,
  &quot;type&quot;: &quot;module&quot;,
  &quot;main&quot;: &quot;index.js&quot;,
  &quot;scripts&quot;: {
    &quot;test&quot;: &quot;echo \&quot;Error: no test specified\&quot; &amp;amp;&amp;amp; exit 1&quot;,
    &quot;start&quot;: &quot;node src/index.js&quot;
  },
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;430&quot; data-origin-height=&quot;114&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPMzeE/dJMcaioVugg/6gZwTmL53FYEf87Twt6RWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPMzeE/dJMcaioVugg/6gZwTmL53FYEf87Twt6RWk/img.png&quot; data-alt=&quot;node index.js, npm run start&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPMzeE/dJMcaioVugg/6gZwTmL53FYEf87Twt6RWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPMzeE%2FdJMcaioVugg%2F6gZwTmL53FYEf87Twt6RWk%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;430&quot; height=&quot;114&quot; data-origin-width=&quot;430&quot; data-origin-height=&quot;114&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;node index.js, npm run start&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;모듈 시스템 이해하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능별로 모듈을 생성하고, 불러오고, 사용하는 방법을 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JavaScript의 모듈 시스템에는 아래와 같은 것들이 존재하지만, 가장 많이 사용하는 CJS와 EMS에 대해서만 알아본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;Common JS(CJS)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;ES Module(ESM)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AMD&lt;br /&gt;UMD&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;...&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Common JS(CJS)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;module.exports&lt;/span&gt;&lt;/b&gt;에 메소드를 등록해서 외부에서 불러올 수 있게 하고, 가져올 때는 &lt;b&gt;&lt;/b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;require&lt;/b&gt;&lt;/span&gt; 메소드를 이용해서 모듈의 메소드를 불러올 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;math.js&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1772548004200&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// math 모듈
function add(a, b) {
  return a + b;
}

function sub(a, b) {
  return a - b;
}

// CJS (Common JS 모듈 시스템)

module.exports = {
  add, // 이렇게 key값과 value 값이 같을 경우에는 생략 가능.
  // add : add,
  sub: sub,
};&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;index.js&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1772548067108&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// CJS (Common JS 모듈 시스템)

// const moduleData = require(&quot;./math&quot;);
// console.log(moduleData.add(1, 2));
// console.log(moduleData.sub(1, 2));

const { add, sub } = require(&quot;./math&quot;);

console.log(add(1, 2));
console.log(sub(1, 2));&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;&lt;b&gt;EMS(ES Module 시스템)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;export 키워드&lt;/span&gt;&lt;/b&gt;를 붙여 외부에서 불러올 수 있게 하였고, 가져오는 곳에서 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;import 키워드&lt;/span&gt;&lt;/b&gt;를 이용하여 불러올 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1772548178504&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// math 모듈
export function add(a, b) {
  return a + b;
}

export function sub(a, b) {
  return a - b;
}

// default 불러올 때 이름을 마음대로 정해서 불러올 수 있다.
export default function multiply(a, b) {

  return a * b;
}

// ESM (ES Module 시스템)
// 이렇게 안하고 function 앞에 export 키워드를 붙여줘도 된다!
// export { add, sub }; // export 키워드를 이용해서 객체를 리터럴로 생성해서 넘겨주기만 하면 된다.&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;index.js&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1772548298459&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ESM (ES Module 시스템)
import mul, { add, sub } from &quot;./math.js&quot;;
// import mul from &quot;./math.js&quot;;
// import { add, sub } from &quot;./math.js&quot;;

console.log(add(1, 2));
console.log(sub(1, 2));&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;외부 패키지 설치하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;npmjs 사이트&lt;/b&gt;&lt;/span&gt; -&amp;gt;&amp;nbsp; node.js 패키지 모음 사이트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.npmjs.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.npmjs.com/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;npm i [패키지명]&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;npm i 명령어를 입력하여 패키지를 설치하면 node_modules 폴더와 package-lock.json 파일이 생성된다.&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: #ef5369;&quot;&gt;&lt;b&gt;node_modules&lt;/b&gt;&lt;/span&gt; -&amp;gt; 설치한 라이브러리가 저장되는 곳&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(node_modules가 지워졌다면 npm i 명령어를 입력하면 다시 설치됨.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;package-lock.json&lt;/span&gt;&lt;/b&gt; -&amp;gt; 패키지가 사용하고 있는 라이브러리의 정확한 버전이나 정보를 저장하는 파일&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(package.json에는 대략적인 버전 정보만 저장됨.)&lt;/p&gt;</description>
      <category>Web Frontend/React</category>
      <category>Node.js</category>
      <category>React</category>
      <author>seunghwaan</author>
      <guid isPermaLink="true">https://seosh817.tistory.com/712</guid>
      <comments>https://seosh817.tistory.com/712#entry712comment</comments>
      <pubDate>Tue, 3 Mar 2026 22:23:11 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] JS의 비동기 처리 (콜백 함수, Promise, Async &amp;amp; Await)</title>
      <link>https://seosh817.tistory.com/710</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;동기와 비동기&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;동기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;-&amp;gt; 여러개의 작업을 순서대로, 하나씩 표현하는 방식&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 개의 작업이 있을 때 각 작업을 순서대로 처리하는 것을 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;동기적으로 처리한다&lt;/span&gt;&lt;/b&gt;라고 표현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, Task A, Task B, Task C가 있다고 가정한다면, Task A가 종료되면 Task B가 실행되고, Task B가 종료되면 Task C가 실행되고 최종적으로는 순서대로 완료하는 흐름으로 표현할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1803&quot; data-origin-height=&quot;290&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXp0Vc/dJMcahXONw2/Ku9saD5oXs8S2o9UA3znB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXp0Vc/dJMcahXONw2/Ku9saD5oXs8S2o9UA3znB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXp0Vc/dJMcahXONw2/Ku9saD5oXs8S2o9UA3znB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXp0Vc%2FdJMcahXONw2%2FKu9saD5oXs8S2o9UA3znB1%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;600&quot; height=&quot;97&quot; data-origin-width=&quot;1803&quot; data-origin-height=&quot;290&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그래밍에서 이렇게 작업을 실행하고 처리해주는 단위를 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;쓰레드&lt;/span&gt;&lt;/b&gt;라고 부른다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;동기적인 방식의 단점&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;but, 동기적인 방식에는 치명적인 단점이 존재한다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; Task가 오래 걸리는 작업이라면 작업을 처리하기 전 까지는 다음 Task를 실행할 수 없게되어 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;전체 프로그램의 성능이 악화&lt;/span&gt;&lt;/b&gt;되어 버리는 치명적인 단점이 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1887&quot; data-origin-height=&quot;290&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baJ98j/dJMcaih7bOr/Rp5N3DGatWQ6AgErwkmMfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baJ98j/dJMcaih7bOr/Rp5N3DGatWQ6AgErwkmMfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baJ98j/dJMcaih7bOr/Rp5N3DGatWQ6AgErwkmMfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaJ98j%2FdJMcaih7bOr%2FRp5N3DGatWQ6AgErwkmMfk%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;600&quot; height=&quot;92&quot; data-origin-width=&quot;1887&quot; data-origin-height=&quot;290&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;이러한 단점을 해결하기 위해, Java나 C# 같은 경우에는 여러개의 쓰레드를 동시에 사용하는 멀티 쓰레드 방법을 활용한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1823&quot; data-origin-height=&quot;803&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/diow6C/dJMcaioT5ii/iGH7T7rPCMJZToIfgKN8k0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/diow6C/dJMcaioT5ii/iGH7T7rPCMJZToIfgKN8k0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/diow6C/dJMcaioT5ii/iGH7T7rPCMJZToIfgKN8k0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdiow6C%2FdJMcaioT5ii%2FiGH7T7rPCMJZToIfgKN8k0%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;600&quot; height=&quot;264&quot; data-origin-width=&quot;1823&quot; data-origin-height=&quot;803&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러개의 쓰레드를 사용하면 오래 걸리는 작업이 중간에 포함되어 있다고 하더라도 전체 프로그램의 성능을 악화시키는 데에는 큰 영향을 주지 못하기 때문에 동기적인 방식의 문제점을 어느정도 보완할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;but, 아쉽게도 JavaScript 엔진에는 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;쓰레드가 1개 밖에 없기 때문&lt;/span&gt;&lt;/b&gt;에 멀티 쓰레드 방식으로는 해결할 수 없으므로, 비동기 방식으로 해결해야 한다.&lt;/p&gt;
&lt;h3 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;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;-&amp;gt; 작업을 순서대로 처리하지 않는 방식&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1037&quot; data-origin-height=&quot;821&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KSiVJ/dJMcaf6Lj1B/2ckDIamFN4wqt9PtO66yCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KSiVJ/dJMcaf6Lj1B/2ckDIamFN4wqt9PtO66yCk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KSiVJ/dJMcaf6Lj1B/2ckDIamFN4wqt9PtO66yCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKSiVJ%2FdJMcaf6Lj1B%2F2ckDIamFN4wqt9PtO66yCk%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;600&quot; height=&quot;475&quot; data-origin-width=&quot;1037&quot; data-origin-height=&quot;821&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 방식에서는 여러개의 작업이 주어졌을 때 앞의 작업이 종료되지 않아도, 기다릴 필요 없이 다른 작업을 동시에 실행 시키는 것이 가능하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1844&quot; data-origin-height=&quot;517&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7K69h/dJMcagq4htH/U6j3KJknD5o19LfBku3tIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7K69h/dJMcagq4htH/U6j3KJknD5o19LfBku3tIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7K69h/dJMcagq4htH/U6j3KJknD5o19LfBku3tIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7K69h%2FdJMcagq4htH%2FU6j3KJknD5o19LfBku3tIk%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;600&quot; height=&quot;168&quot; data-origin-width=&quot;1844&quot; data-origin-height=&quot;517&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;그래서 앞선 작업의 결과 값을 받아서 사용하고 싶다면 각각의 작업에 Callback을 붙여서 사용할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;JavaScript는 비동기 작업들을 어떻게 동시에 처리할 수 있는가?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 작업들은 쓰레드에서 실행되는 것이 아닌, 자바스크립트 엔진이 아닌 Web APIs 에서 실행된다.&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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;1. JavaScript Engine은 비동기 함수를 만나게 되면, 이 비동기 함수를 Web Browser의 Web APIs에게 실행해달라고 부탁한다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;2. 이때, 해당 비동기 함수가 끝나면 실행할 콜백 함수까지 넘겨준다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;3. 비동기 함수가 끝나면 콜백 함수를 JavaScript Engine에게 돌려준다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1362&quot; data-origin-height=&quot;916&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLiJmY/dJMcajnJjLm/kLUSFxltuygW2JG8trvKak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLiJmY/dJMcajnJjLm/kLUSFxltuygW2JG8trvKak/img.png&quot; data-alt=&quot;JavaScript의 비동기 작업 처리 방법&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLiJmY/dJMcajnJjLm/kLUSFxltuygW2JG8trvKak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLiJmY%2FdJMcajnJjLm%2FkLUSFxltuygW2JG8trvKak%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;600&quot; height=&quot;404&quot; data-origin-width=&quot;1362&quot; data-origin-height=&quot;916&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;JavaScript의 비동기 작업 처리 방법&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;비동기&amp;nbsp; 처리 방법&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 콜백 함수&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 비동기 처리 작업의 결과로 콜백 함수를 달아줄 수는 있지만 아래의 orderFood() 함수처럼 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;콜백 지옥&lt;/span&gt;&lt;/b&gt;이 생길 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1772375847964&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 콜백 함수

function add(a, b) {
  setTimeout(() =&amp;gt; {
    const sum = a + b; // 3
    callback(sum);
  }, 3000);
}

add(1, 2, (value) =&amp;gt; {
  console.log(value); // 3 출력
});

// 음식을 주문하는 상황
function orderFood(callback) {
  setTimeout(() =&amp;gt; {
    const food = &quot;떡볶이&quot;;
    callback(food);
  }, 3000);
}

function cooldownFood(food, callback) {
  setTimeout(() =&amp;gt; {
    const cooldowndedFood = `식은 ${food}`;
    callback(cooldownFood);
  }, 2000);
}

function freezFood(food) {
  setTimeout(() =&amp;gt; {
    const freezedFood = `냉동된 ${food}`;
    callback(freezedFood);
  }, 1500);
}

orderFood((food) =&amp;gt; {
  console.log(food);

  cooldowndedFood(food, (cooldownFood) =&amp;gt; {
    console.log(cooldownFood);

    freezFood(cooldownFood, (freezedFood) =&amp;gt; {
      console.log(freezedFood);
    });
  });
});

// 3초 뒤 떡볶이 출력
// 2초 뒤 식은 떡볶이 출력
// 1.5초 뒤 냉동된 식은 떡볶이 출력&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. Promise&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 비동기 작업을 효율적으로 처리할 수 있도록 도와주는 자바스크립트 내장 객체&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; Promise는 setTimeout 함수와 같은 비동기 작업들을 래핑하는 객체&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 감싸고 있는 비동기 작업을 실행, 관리, 결과 저장, 병렬 실행, 다시 실행 등의 비동기 작업들을 처리하는데에 필요한 모든 기능을 제공해주는 객체&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Promise의 3가지 상태&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1697&quot; data-origin-height=&quot;616&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbmz1w/dJMcajnJEDk/qDxuiPKJmSKEAUsPKMBgsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbmz1w/dJMcajnJEDk/qDxuiPKJmSKEAUsPKMBgsk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbmz1w/dJMcajnJEDk/qDxuiPKJmSKEAUsPKMBgsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbbmz1w%2FdJMcajnJEDk%2FqDxuiPKJmSKEAUsPKMBgsk%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;600&quot; height=&quot;218&quot; data-origin-width=&quot;1697&quot; data-origin-height=&quot;616&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;대기(Pending)&lt;/b&gt;&lt;/span&gt; -&amp;gt; 비동기 작업이 진행중인, 아직 작업이 완료되지 않은 상태&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;성공(Fulfilled)&lt;/b&gt;&lt;/span&gt; -&amp;gt; 비동기 작업이 성공적으로 마무리 된 상태&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;실패(Rejected)&lt;/span&gt;&lt;/b&gt; -&amp;gt; 비동기 작업이 실패한 상태&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1649&quot; data-origin-height=&quot;613&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGYsRl/dJMcaaqR4ic/9IjClbdirDmtxNFp4KvYCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGYsRl/dJMcaaqR4ic/9IjClbdirDmtxNFp4KvYCk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGYsRl/dJMcaaqR4ic/9IjClbdirDmtxNFp4KvYCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGYsRl%2FdJMcaaqR4ic%2F9IjClbdirDmtxNFp4KvYCk%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;600&quot; height=&quot;223&quot; data-origin-width=&quot;1649&quot; data-origin-height=&quot;613&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;해결(resolve)&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;/span&gt; -&amp;gt;&amp;nbsp;비동기 작업이 성공해서 대기 -&amp;gt; 성공으로 바뀜&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;거부(reject) &lt;/span&gt;&lt;/b&gt;-&amp;gt;&amp;nbsp;비동기 작업이 어떠한 이유로 대기 -&amp;gt; 실패로 바뀜&lt;/p&gt;
&lt;pre id=&quot;code_1772420648108&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 비동기 작업 처리하기 Promise

// resolve -&amp;gt; Promise가 관리하는 비동기 작업을 성공 상태로 바꾸는 함수가 들어있음.
// reject -&amp;gt; Promise가 관리하는 비동기 작업을 실패 상태로 바꾸는 함수가 들어있음.
const promise = new Promise((resolve, reject) =&amp;gt; {
  // 비동기 작업
  // executor

  setTimeout(() =&amp;gt; {
    console.log(&quot;안녕&quot;);
  }, 2000);
});

console.log(promise);&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;promise의 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;PromiseState가&lt;/span&gt;&amp;nbsp;pending 상태&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;475&quot; data-origin-height=&quot;187&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AQlnG/dJMcajuxHmS/k8RL9vKGBxhKcktQYWfc6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AQlnG/dJMcajuxHmS/k8RL9vKGBxhKcktQYWfc6k/img.png&quot; data-alt=&quot;실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AQlnG/dJMcajuxHmS/k8RL9vKGBxhKcktQYWfc6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAQlnG%2FdJMcajuxHmS%2Fk8RL9vKGBxhKcktQYWfc6k%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;500&quot; height=&quot;197&quot; data-origin-width=&quot;475&quot; data-origin-height=&quot;187&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1772420667068&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 비동기 작업 처리하기 Promise

// resolve -&amp;gt; Promise가 관리하는 비동기 작업을 성공 상태로 바꾸는 함수가 들어있음.
// reject -&amp;gt; Promise가 관리하는 비동기 작업을 실패 상태로 바꾸는 함수가 들어있음.
const promise = new Promise((resolve, reject) =&amp;gt; {
  // 비동기 작업
  // executor

  setTimeout(() =&amp;gt; {
    console.log(&quot;안녕&quot;);
    resolve();
  }, 2000);
});

console.log(promise);&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;resolve() 추가 후, 2초 뒤 promise의 PromiseState가 pending -&amp;gt; resolved 상태&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;409&quot; data-origin-height=&quot;180&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btvkCI/dJMcahwL0aY/kJtOyFM1AoL3rEPkM59dyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btvkCI/dJMcahwL0aY/kJtOyFM1AoL3rEPkM59dyk/img.png&quot; data-alt=&quot;실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btvkCI/dJMcahwL0aY/kJtOyFM1AoL3rEPkM59dyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtvkCI%2FdJMcahwL0aY%2FkJtOyFM1AoL3rEPkM59dyk%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;500&quot; height=&quot;220&quot; data-origin-width=&quot;409&quot; data-origin-height=&quot;180&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1772420675909&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 비동기 작업 처리하기 Promise

// resolve -&amp;gt; Promise가 관리하는 비동기 작업을 성공 상태로 바꾸는 함수가 들어있음.
// reject -&amp;gt; Promise가 관리하는 비동기 작업을 실패 상태로 바꾸는 함수가 들어있음.
const promise = new Promise((resolve, reject) =&amp;gt; {
  // 비동기 작업
  // executor

  setTimeout(() =&amp;gt; {
    console.log(&quot;안녕&quot;);
    resolve(&quot;안녕&quot;);
  }, 2000);
});

console.log(promise);&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;이때, PromiseResult가 undefined로 결과값이 저장되어있지 않은 것을 확인할 수 있는데, resolve() 함수 안에 값을 넣어주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;336&quot; data-origin-height=&quot;205&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lhcu9/dJMcafsagz7/rafuanOuy8Jrfes7s9RnxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lhcu9/dJMcafsagz7/rafuanOuy8Jrfes7s9RnxK/img.png&quot; data-alt=&quot;실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lhcu9/dJMcafsagz7/rafuanOuy8Jrfes7s9RnxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flhcu9%2FdJMcafsagz7%2FrafuanOuy8Jrfes7s9RnxK%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;500&quot; height=&quot;305&quot; data-origin-width=&quot;336&quot; data-origin-height=&quot;205&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1772420878348&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 비동기 작업 처리하기 Promise

// resolve -&amp;gt; Promise가 관리하는 비동기 작업을 성공 상태로 바꾸는 함수가 들어있음.
// reject -&amp;gt; Promise가 관리하는 비동기 작업을 실패 상태로 바꾸는 함수가 들어있음.
const promise = new Promise((resolve, reject) =&amp;gt; {
  // 비동기 작업
  // executor

  setTimeout(() =&amp;gt; {
    console.log(&quot;안녕&quot;);
    // resolve(&quot;안녕&quot;);
    reject(&quot;왜 실패했는지 이유....&quot;);
  }, 2000);
});

console.log(promise);&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;반면, reject()를 사용하면 콘솔에 아래와 같이 PromiseState와 PromiseReulst가 출력된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;386&quot; data-origin-height=&quot;248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBcwSS/dJMcajnJNAS/wph7873DKfnApmAYvj6K20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBcwSS/dJMcajnJNAS/wph7873DKfnApmAYvj6K20/img.png&quot; data-alt=&quot;실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBcwSS/dJMcajnJNAS/wph7873DKfnApmAYvj6K20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBcwSS%2FdJMcajnJNAS%2Fwph7873DKfnApmAYvj6K20%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;500&quot; height=&quot;321&quot; data-origin-width=&quot;386&quot; data-origin-height=&quot;248&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;then 메소드&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;-&amp;gt; promise가 성공하면, then 메소드에 전달한 callback 함수를 실행시킴.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1772424450517&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// then 메소드
// -&amp;gt; promise가 성공하면, then 메소드에 전달한 callback 함수를 실행시킴.
promise.then((value) =&amp;gt; {
  console.log(value);
});&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;catch 메소드&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;-&amp;gt; promise가 실패하면, catch 메소드에 전달한 callback 함수를 실행시킴.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1772424494869&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// catch 메소드
// -&amp;gt; promise가 실패하면, catch 메소드에 전달한 callback 함수를 실행시킴.
promise.catch((error) =&amp;gt; {
  console.log(value);
});&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Promise Chaining&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;-&amp;gt; promise의 then과 catch는 promise 자기 자신을 반환하므로 연결해서 사용하는 것도 가능하다.&lt;/span&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1772424521702&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// promise의 then과 catch는 promise 자기 자신을 반환하므로 연결해서 사용하는 것도 가능함. (promise chaining)
promise
  .then((value) =&amp;gt; {
    console.log(value);
  })
  .catch((error) =&amp;gt; {
    console.log(&quot;error&quot;);
  });&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. Async &amp;amp; Await&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Async&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;-&amp;gt; 어떤 함수를 비동기 함수로 만들어지는 키워드&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;-&amp;gt; 즉, 함수가 Promise를 반환하도록 변환해주는 키워드&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1772427213772&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// async
// 어떤 함수를 비동기 함수로 만들어주는 키워드
// 함수가 프로미스를 반환하도록 변환해주는 키워드

async function getData() {
  return {
    name: &quot;서승환&quot;,
    id: &quot;seosh817&quot;,
  };
}

console.log(getData());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;395&quot; data-origin-height=&quot;206&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8Gi18/dJMcadA3tFm/TDFuhfyUYC9O2t0ITfvHIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8Gi18/dJMcadA3tFm/TDFuhfyUYC9O2t0ITfvHIk/img.png&quot; data-alt=&quot;실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8Gi18/dJMcadA3tFm/TDFuhfyUYC9O2t0ITfvHIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8Gi18%2FdJMcadA3tFm%2FTDFuhfyUYC9O2t0ITfvHIk%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;500&quot; height=&quot;261&quot; data-origin-width=&quot;395&quot; data-origin-height=&quot;206&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Await&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; async 함수 내부에서만 사용이 가능한 키워드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 비동기 함수가 다 처리되기를 기다리고 결과를 반환함.&lt;/p&gt;
&lt;pre id=&quot;code_1772427253544&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async function getData() {
  return new Promise((resolve, reject) =&amp;gt; {
    setTimeout(() =&amp;gt; {
      resolve({
        name: &quot;서승환&quot;,
        id: &quot;seosh817&quot;,
      });
    });
  });
}

// await
// async 함수 내부에서만 사용이 가능한 키워드
// 비동기 함수가 다 처리되기를 기다리고 결과를 반환함.

async function printData() {
    const data = await getData();
    console.log(data);
    // getData().then((result) =&amp;gt; {
    //     console.log(result);
    // });
}

printData();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;397&quot; data-origin-height=&quot;158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bawuPy/dJMcabpNPlq/qLwNrF5SNyKAISNcjliT0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bawuPy/dJMcabpNPlq/qLwNrF5SNyKAISNcjliT0k/img.png&quot; data-alt=&quot;실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bawuPy/dJMcabpNPlq/qLwNrF5SNyKAISNcjliT0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbawuPy%2FdJMcabpNPlq%2FqLwNrF5SNyKAISNcjliT0k%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;500&quot; height=&quot;199&quot; data-origin-width=&quot;397&quot; data-origin-height=&quot;158&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;b&gt;References&lt;/b&gt;&lt;/h2&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;b&gt;한 입 크기로 잘라 먹는 리액트(React.js)(이정환)&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web Frontend/JavaScript</category>
      <category>async</category>
      <category>Await</category>
      <category>PROMISE</category>
      <category>React</category>
      <category>비동기 처리</category>
      <author>seunghwaan</author>
      <guid isPermaLink="true">https://seosh817.tistory.com/710</guid>
      <comments>https://seosh817.tistory.com/710#entry710comment</comments>
      <pubDate>Fri, 20 Feb 2026 23:41:23 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] 함수 표현식과 화살표 함수 &amp;amp; 콜백 함수(Callback)</title>
      <link>https://seosh817.tistory.com/708</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;함수 표현식과 화살표 함수&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 함수 표현식&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;-&amp;gt; &lt;b&gt;익명함수는&amp;nbsp;&lt;/b&gt;function () 형태로 만들 수 있다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1771414679800&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. 함수 표현식
function funcA() {
  console.log(&quot;funcA&quot;);
}

let varA = funcA; // 함수가 아니라 함수 자체를 넣어줌.
console.log(varA);

// f funcA() {
//    console.log(&quot;funcA&quot;);
//}
// 출력

varA(); // 함수를 실행

let varB = function funcB() {
  console.log(&quot;funcB&quot;);
};

varB();
// funcB(); -&amp;gt; 이건 오류, funcB는 사용할 수 없음.

let varC = function () {
  // 익명함수
  console.log(&quot;funcB&quot;);
};

console.log(varC()); // funcB 출력&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 화살표 함수&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;-&amp;gt; 화살표 함수를 이용해서 익명함수를 function()이 아닌 () =&amp;gt; 형태로도 만들 수 있다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1771414708935&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 2. 화살표 함수
let varD = () =&amp;gt; {
  return 1;
};

console.log(varD()); // 1 출력

let varE = () =&amp;gt; 1;

console.log(varE()); // 1 출력

let varF = (value) =&amp;gt; value + 1;

console.log(varF(10)); // 11 출력

let varG = (value) =&amp;gt; {
  console.log(value);
  return value + 1;
};

console.log(varG(10)); // 10 11 출력&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;콜백 함수&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;-&amp;gt; 자신이 아닌 다른 함수에, 인수로써 전달된 함수를 의미 함.&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;-&amp;gt; 매개 변수에 변수가 아닌 함수를 넘겨서 사용.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1771422941273&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 콜백 함수

// 1. 콜백함수
// 자신이 아닌 다른 함수에, 인수로써 전달된 함수를 의미 함

function main(value) {
  value();
}

function sub() {
  console.log(&quot;i am sub&quot;);
}

main(sub); // &quot;i am sub 출력&quot;

main(function sub() {
  // 이렇게도 가능.
  console.log(&quot;i am sub&quot;);
});

main(() =&amp;gt; {
  // 이렇게도 가능.
  console.log(&quot;i am sub&quot;);
});

// 2. 콜백함수의 활용.
function repeat(count) {
  for (let idx = 1; idx &amp;lt;= count; idx++) {
    console.log(idx);
  }
}

function repeatDouble(count) {
  for (let idx = 1; idx &amp;lt;= count; idx++) {
    console.log(idx * 2);
  }
}

// repeat(5);
// repeatDouble(5);

// 위 처럼 구조가 흡사한 함수를 만들 일이 있음.
// 이럴 경우 아래와 같이 콜백함수로 만들 수 있음.

function repeatCallback(count, callback) {
  for (let idx = 1; idx &amp;lt;= count; idx++) {
    callback(idx);
  }
}

repeatCallback(5, function (idx) {
  console.log(idx);
});

repeatCallback(5, (idx) =&amp;gt; {
  // function 제거하고 =&amp;gt; 붙여도 동일하게 사용 가능.
  console.log(idx * 2);
});

repeatCallback(5, (idx) =&amp;gt; {
  console.log(idx * 3);
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web Frontend/JavaScript</category>
      <category>JavaScript</category>
      <category>함수 표현식</category>
      <category>화살표 함수</category>
      <author>seunghwaan</author>
      <guid isPermaLink="true">https://seosh817.tistory.com/708</guid>
      <comments>https://seosh817.tistory.com/708#entry708comment</comments>
      <pubDate>Wed, 18 Feb 2026 20:37:33 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] 연산자</title>
      <link>https://seosh817.tistory.com/707</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;연산자&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 대입 연산자&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;-&amp;gt; = 연산자&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1771411624276&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. 대입 연산자 (=)
let var1 = 1;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 산술 연산자&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;-&amp;gt; +, -, *, /, % 연산자&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1771411672600&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 2. 산술 연산자
let num1 = 3 + 2;
let num2 = 3 - 2;
let num3 = 3 * 2;
let num4 = 3 / 2;
let num5 = 3 % 2;

let num6 = 1 + 2 * 10;
console.log(num6); // *가 우선순위가 높으므로 21 출력.&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 복합 대입 연산자&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;-&amp;gt; 산술 + 대입 연산자&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1771411710443&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 3. 복합 대입 연산자 (산술 + 대입)
let num7 = 10;
num7 += 20; // num7 = num7 + 20;
num7 -= 20;
num7 *= 20;
num7 /= 20;
num7 %= 10;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. 증감 연산자&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;-&amp;gt; 후위 연산, 전위 연산&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1771411742567&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 4. 증감 연산자
let num8 = 10;
num8++; // 후위 연산. 이 줄이 끝나고 나서야 +1이 증가된다.
console.log(num8);
console.log(++num8); // 전위 연산. +1이 증가된 이후에 줄이 실행된다.&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5. 논리 연산자&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;-&amp;gt; ||(or), &amp;amp;&amp;amp;(and), !(not)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1771411813532&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 5. 논리 연산자
let or = true || false;
let and = true &amp;amp;&amp;amp; false;
let not = !true;
console.log(or, and, not); // true false false 출력&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;6. 비교 연산자&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;-&amp;gt; ===는 자료형까지 같은지 비교한다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;-&amp;gt; ==는 자료형이 같은지는 비교하지 않는다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1771411888223&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 6. 비교 연산자 (==을 사용하게 되면 자료형이 같은지 까지는 비교하지 않는다!)
let comp1 = 1 === 2; // 같은 값이 아니기 때문에 false
let comp2 = 1 !== 2; // 같은 값이 아니기 때문에 true
console.log(comp1, comp2);

let comp3 = 2 &amp;gt; 1; // true
let comp4 = 2 &amp;lt; 1; // false
console.log(comp3, comp4);

let comp5 = 2 &amp;gt;= 2; // true
let comp6 = 2 &amp;lt;= 2; // true
console.log(comp5, comp6);&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;7. null 병합 연산자&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;-&amp;gt; null, undefined가 아닌 값을 찾아내는 연산자&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;-&amp;gt; 존재하는 값을 추려내는 기능&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1771411971223&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 7. null 병합 연산자
// -&amp;gt; null, undefined가 아닌 값을 찾아내는 연산자
// -&amp;gt; 존재하는 값을 추려내는 기능
let var1; // undefined
let var2 = 10;
let var3 = 20;

let var4 = var1 ?? var2; // 10 var4에 undefined가 아닌 var2의 값을 저장한다.
let var5 = var1 ?? var3; // 20 var4에 undefined가 아닌 var3의 값을 저장한다.
let var6 = var3 ?? var2;

let userName;
let userNickName = &quot;seosh817&quot;;

let displayName = userName ?? userNickName; // userName이 undefined라면 userNickName으로 저장한다.&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;8. typeof 연산자&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;-&amp;gt; 값의 타입을 문자열로 반환하는 기능을 하는 연산자.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1771412143528&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 8. typeof 연산자
// -&amp;gt; 값의 타입을 문자열로 반환하는 기능을 하는 연산자.
let var7 = &quot;hello&quot;;

let t1 = typeof var7;
console.log(t1); // string&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;9. 삼항 연산자&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;-&amp;gt; 항을 3개 사용하는 연산자.&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;-&amp;gt; 조건식을 이용해서 참, 거짓일 때의 값을 다르게 반환&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1771412191183&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 9. 삼항 연산자
// -&amp;gt; 항을 3개 사용하는 연산자.
// -&amp;gt; 조건식을 이용해서 참, 거짓일 때의 값을 다르게 반환
let var8 = 10;

// 요구사항 : 변수 res에 var8의 값이 짝수 -&amp;gt; &quot;짝&quot;, 홀수 -&amp;gt; &quot;&quot;
let res = var8 % 2 === 0 ? &quot;짝수&quot; : &quot;홀수&quot;;
console.log(res); // 짝수 출력.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web Frontend/JavaScript</category>
      <category>JavaScript</category>
      <category>연산자</category>
      <author>seunghwaan</author>
      <guid isPermaLink="true">https://seosh817.tistory.com/707</guid>
      <comments>https://seosh817.tistory.com/707#entry707comment</comments>
      <pubDate>Wed, 18 Feb 2026 19:48:50 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] 형 변환</title>
      <link>https://seosh817.tistory.com/706</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;형 변환&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 묵시적 형 변환&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;-&amp;gt;&amp;nbsp;자바스크립트&amp;nbsp;엔진이&amp;nbsp;오류를&amp;nbsp;발생시키지&amp;nbsp;않기&amp;nbsp;위해서&amp;nbsp;알아서&amp;nbsp;형&amp;nbsp;변환&amp;nbsp;하는&amp;nbsp;것.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1771411177340&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. 묵시적 형 변환
// -&amp;gt; 자바스크립트 엔진이 오류를 발생시키지 않기 위해서 알아서 형 변환 하는 것.

let num = 10;
let str = &quot;20&quot;;

const result = num + str; // 묵시적으로 String으로 형 변환. &quot;10&quot; + &quot;20&quot; = &quot;1020&quot;
console.log(result); // &quot;1020&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 명시적 형 변환&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;-&amp;gt; 프로그래머 내장함수 등을 이용해서 직접 형 변환을 명시&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1771411115968&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 2. 명시적 형 변환
// -&amp;gt; 프로그래머 내장함수 등을 이용해서 직접 형 변환을 명시
// -&amp;gt; 문자열 -&amp;gt; 숫자

let str1 = &quot;10&quot;;
let strToNum1 = Number(str1);
console.log(10 + strToNum1); // 20 출력. 10 + 10 = 20

let str2 = &quot;10개&quot;;
let strToNum2 = parseInt(str2); // 숫자가 앞쪽에 있으면 뒤에 문자가 있더라도 형 변환 해준다.

console.log(strToNum2); // 10 출력.

// -&amp;gt; 숫자 -&amp;gt; 문자열
let num1 = 20;
let numToStr1 = String(num1);

console.log(numToStr1 + &quot;입니다&quot;); // 20입니다 출력.&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Web Frontend/JavaScript</category>
      <category>JavaScript</category>
      <category>형 변환</category>
      <author>seunghwaan</author>
      <guid isPermaLink="true">https://seosh817.tistory.com/706</guid>
      <comments>https://seosh817.tistory.com/706#entry706comment</comments>
      <pubDate>Wed, 18 Feb 2026 19:38:23 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] 변수와 상수 &amp;amp; 자료형</title>
      <link>https://seosh817.tistory.com/705</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;변수와 상수&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 변수 선언&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1771396076593&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. 변수

let age;
console.log(age);

age = 30;
console.log(age);

// let age = 40; // Cannot redeclare block-scoped variable 'age'.&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 상수 선언&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1771396106473&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 2. 상수
const birth = &quot;1997.01.07&quot; // 선언과 동시에 초기값을 할당해주어야 함.
// birth = &quot;123&quot;; // 재선언 할 수 없다.
console.log(birth);&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 변수 명명규칙 (네이밍 규칙)&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3-1. $, _ 제외한 기호는 변수명에 사용할 수 없다.&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1771396152313&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let $_name;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3-2. 변수명에 숫자로 시작할 수 없다.&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1771396207881&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let 2name; // 사용 불가
// but, 정 사용하겠다면 아래와 같이 사용
let _2name; let $2name;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3-3. 예약어를 변수명에 사용할 수 없다.&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1771396226223&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let let; // 사용 불가&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;자료형&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.&amp;nbsp; Number Type&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1771396409576&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. Number Type
let num1 = 27;
let num2 = 1.5;
let num3 = -20;

let inf = Infinity;
let mInf = -Infinity;

let nan = NaN;

console.log(1 * &quot;hello&quot;); // JS는 죽지 않고 NaN을 반환, 다른 언어에 비해서 수학 연산에 안전하다.&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. String Type&lt;br /&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1771396459348&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 2. String Type
let myName = &quot;서승환&quot;;
let myLocation = &quot;역삼동&quot;;
let introduce = myName + myLocation;

let introduceText = `${myName}은 ${myLocation}에 거주합니다`; // 백틱으로 문자열을 만들면 다른 변수를 동적으로 포함시킬 수 있다.&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. Boolean Type&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1771396506434&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 3. Boolean Type
let isSwitchOn = true;
let isEmpty = false;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. Null Type&lt;br /&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1771396531853&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 4. Null Type (아무것도 없다) 명시적으로 넣어주어야 한다. 
// JS는 변수를 초기화하지 않으면 null이 아닌 undefined로 선언이 된다.
let empty = null;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5. Undefined Type&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1771396564067&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 5. Undefined Type
undefined; // 변수를 선언하고 아무런 값도 초기화 하지 않았을 때 자동으로 할당되는 값.
let none;
console.log(none);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;References&lt;/b&gt;&lt;/h2&gt;
&lt;div style=&quot;color: #333333; text-align: start;&quot;&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;b&gt;한 입 크기로 잘라 먹는 리액트(React.js)(이정환)&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;</description>
      <category>Web Frontend/JavaScript</category>
      <category>JavaScript</category>
      <category>변수와 상수</category>
      <category>자료형</category>
      <author>seunghwaan</author>
      <guid isPermaLink="true">https://seosh817.tistory.com/705</guid>
      <comments>https://seosh817.tistory.com/705#entry705comment</comments>
      <pubDate>Wed, 18 Feb 2026 15:28:35 +0900</pubDate>
    </item>
    <item>
      <title>[Swift] 동시성 (Concurrency)</title>
      <link>https://seosh817.tistory.com/698</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[ 이 게시물은 Swift.org를 참고하며 Swift를 공부하기 위해 작성하는 글 입니다. ]&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;Swift 는 구조화 된 방식으로 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;비동기 (asynchronous)&lt;/span&gt;&lt;/b&gt; 와 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;병렬 (parallel)&lt;/span&gt;&lt;/b&gt; 코드 작성을 지원합니다. &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;비동기 코드 (Aynschronous code)&lt;/span&gt;&lt;/b&gt; 는 일시적으로 중단했다가 다시 실행할 수 있지만 한번에 프로그램의 한 부분만 실행합니다. 프로그램에서 코드를 일시 중단하고 다시 실행하면 UI 업데이트와 같은 짧은 작업을 계속 진행하면서 네트워크를 통해 데이터를 가져오거나 파일을 분석하는 것과 같은 긴 실행작업을 계속할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;병렬 코드 (Parallel code)&lt;/span&gt;&lt;/b&gt; 는 동시에 코드의 여러부분이 실행됨을 의미합니다. 예를 들어 4코어 프로세서의 컴퓨터는 각 코어가 하나의 작업을 수행하므로 코도의 4부분을 동시에 실행할 수 있습니다. 병렬과 비동기 코드를 사용하는 프로그램은 한 번에 여러 작업을 수행하고, 이 코드를 메모리 안전 방식으로 더 쉽게 작성할 수 있도록 합니다.&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;Swift 언어 지원을 사용하지 않고 동시성 코드를 작성할 수 있지만 해당 코드는 읽기가 더 어려운 경우가 많습니다. 예를 들어, 아래 코드는 사진을 리스트화 하고 해당 리스트의 첫번째 사진을 다운로드하고 해당 사진을 사용자에게 보여줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1747573601373&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;listPhotos(inGallery: &quot;Summer Vacation&quot;) { photoNames in
    let sortedNames = photoNames.sorted()
    let name = sortedNames[0]
    downloadPhoto(named: name) { photo in
        show(photo)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 간단한 경우에도 완료 핸들러가 연속해서 작성되어야 하므로, 중첩 클로저를 작성하게 됩니다. 이 스타일에서 더 많이 중첩된 복잡한 코드는 다루기가 어려울 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;비동기 함수 정의와 호출 (Defining and Calling Asynchronous Functions)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;비동기 함수 (asynchronous function)&lt;/span&gt;&lt;/b&gt; 또는 비동기 메소드 (asynchronous method) 는 실행 도중에 일시적으로 중단될 수 있는 특수한 함수 또는 메소드 입니다. 이는 완료될 때까지 실행되거나 오류가 발생하거나 반환되지 않는 일반적인 동기 함수 또는 메소드 (asynchronous functions and methods) 와 대조됩니다. 비동기 함수 또는 메소드는 이 세가지 중 하나를 수행하지만 무언가를 기다리고 있을 때 중간에 일시 중지될 수도 있습니다. 비동기 함수 또는 메소드의 본문 내에서 실행을 일시 중지 할 수 있는 부분을 표시합니다.&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;함수 또는 메소드가 비동기 임을 나타내려면 던지는 함수 (throwing function) 를 나타내기 위해 throws를 사용하는 것과 유사하게 파라미터 뒤의 선언에 async 키워드를 작성합니다. 함수 또는 메소드가 값을 반환한다면 반환 화살표 (-&amp;gt;) 전에 async 를 작성합니다. 예를 들어, 갤러리에 사진의 이름을 가져오는 방법은 아래와 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1747576624758&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func listPhotos(inGallery name: String) async -&amp;gt; [String] {
    let result = // ... some asynchronous networking code ...
    return result
}&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;async 와 throws 둘 다인 함수 또는 메소드는 throws 전에 async 를 작성합니다.&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;비동기 메소드를 호출할 때, 해당 메소드가 반환될 때까지 실행이 일시 중단됩니다. 중단될 가능성이 있는 지점을 표시하기 위해 호출 앞에 await을 작성합니다. 이는 에러가 있는 경우 프로그램의 흐름을 변경 가능함을 나타내기 위해 던지는 함수를 호출할 때 try 를 작성하는 것과 같습니다. 비동기 메소드 내에서 실행 흐름은 다른 비동기 메소드를 호출할 때만 일시 중단됩니다. 이는 가능한 모든 중단 지점이 await으로 표시된다는 의미입니다. 코드에서 중단 간으한 모든 지점을 표시하면 동시성 코드를 더 읽고 이해하기 쉽게 만들어 줍니다.&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;pre id=&quot;code_1747577482571&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let photoNames = await listPhotos(inGallery: &quot;Summer Vacation&quot;)
let sortedNames = photoNames.sorted()
let name = sortedNames[0]
let photo = await downloadPhoto(named: name)
show(photo)&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;listphotos(inGallery:) 와 downloadPhoto(named:) 함수 모두 네트워크 요청을 필요로 하기 때문에 완료하는데 비교적 오랜시간이 걸릴 수 있습니다. 반환 화살표 전에 async 를 작성하여 둘 다 비동기로 만들면 이 코드는 그림이 준비될 때까지 기다리는 동안 앱의 나머지 코드가 계속 실행될 수 있습니다.&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;p data-ke-size=&quot;size16&quot;&gt;1. 코드는 첫번째 줄에서 실행을 시작하고 첫번째 await 까지 실행됩니다. listPhotos(inGallery:) 함수를 호출하고 해당 함수가 반환될 때까지 실행을 일시 중단합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 이 코드의 실행이 일시 중단되는 동안 같은 프로그램의 다른 동시 코드가 실행됩니다. 예를 들어 오랜시간 실행되는 백그라운드 작업이 새 사진의 리스트를 업데이트할 수 있습니다. 이 코드는 await 로 표시된 다음 중단 지점 또는 완료될 때까지 실행됩니다.&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;3. listPhotos(inGallery:) 가 반환된 후에 이 코드는 해당 지점에서 시작하여 계속 실행됩니다. 반환된 값을 photoNames 에 할당됩니다.&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;4. sortedNames 와 name 을 정의하는 라인은 일반적인 동기 코드입니다. 이 라인은 await 이 표시되지 않았으므로 중단 지점이 없습니다.&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. 다음 await 은 downloadPhoto(named:) 함수에 대한 호출입니다. 이 코드는 해당 함수가 반환될 때까지 실행을 다시 일시 중단하여 다른 코드에 실행할 기회를 제공합니다.&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;6. downloadPhoto(named:) 가 반환된 후에 반환값은 photo 에 할당된 다음에 show(_:)를 호출할 때 인수로 전달됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;await&lt;/span&gt;&lt;/b&gt;으로 표시된 코드의 중단 가능한 지점은 비동기 함수 또는 메소드가 반환되기 기다리는 동안 현재 코드 부분의 실행을 일시적으로 중단될 수 있음을 나타냅니다. Swift가 현재 스레드에서 코드의 실행을 일시 중단하고 대신 해당 쓰레드에서 다른 코드를 실행하기 때문에 이것을 쓰레드 양보 (yielding the thread) 라고도 부릅니다. await가 있는 코드는 실행을 일시 중단할 수 있어야 하므로 프로그램의 특정 위치에서만 비동기 함수 또는 메소드를 호출할 수 있습니다.&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;@main 으로 표시된 구조체, 클래스, 또는 열거형의 정적 (static) main() 메소드에 있는 코드&lt;/li&gt;
&lt;li&gt;아래의 구조화되지 않은 동시성 (Unstructured Concurrnecy)에 보이는 것처럼 구조화되지 않은 하위 작업의 코드&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Task.yield() 메소드를 호출해서 명시적으로 중단 지점을 추가할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1748785137522&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func generateSlideshow(forGallery gallery: String) async {
    let photos = await listPhotos(inGallery: gallery)
    for photo in photos {
        // ... render a few seconds of video for this photo ...
        await Task.yield()
    }
}&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;영상 렌더링을 동기화하는 코드가 있다고 가정해보면, 이것은 어떠한 중단 지점을 포함하지 않습니다. 영상 렌더링 작업은 오랜 시간이 걸립니다. 그러나, Task.yield()를 주기적으로 호출하여 명시적으로 중단 지점을 추가할 수 있습니다. 이러한 방법으로 코드를 구성하면 Swift는 이 작업과 다른 작업의 진행을 균형적으로 맞출 수 있습니다.&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;Task.sleep(for:tolerance:clock:) 메소드는 동시성 동작이 어떻게 동작하는지 알기 위한 간단한 코드를 작성할 때 유용합니다. 이 메소드는 주어진 시간만큼 현재 작업을 중단합니다. 다음은 네트워크 동작을 지연시키기 위해 sleep(for: tolerance:clock:) 을 사용하는 listPhotos(inGallery:) 함수 입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1748785449729&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func listPhotos(inGallery name: String) async throws -&amp;gt; [String] {
    try await Task.sleep(for: .seconds(2))
    return [&quot;IMG001&quot;, &quot;IMG99&quot;, &quot;IMG0404&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;위 코드에서 listPhotos(inGallery:) 은 Task:sleep(until:tolerance:clock:) 호출이 에러를 발생할 수 있으므로 async와 throws를 모두 작성했습니다. 이 listPhotos(inGallery:)를 호출하려면, try 와 await을 모두 작성해야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1748785689968&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let photos = try await listPhotos(inGallery: &quot;A Rainy Weekend&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;async 함수는 throws 함수와 유사한 점이 있습니다. async 또는 throws 함수를 정의할 때, async 또는 throws 를 표시하고, 이 함수를 호출할 때 await 또는 try 를 작성합니다. async 함수는 throws 함수가 다른 throws 함수를 호출할 수 있듯이 다른 async 함수를 호출할 수 있습니다.&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;그러나, 매우 중요한 다른 점이 있습니다. 에러를 처리하기 위해 do-catch 블럭에 코드를 래핑하거나, 에러를 다른 곳에서 처리하기 위해 에러를 저장하는 Result 를 사용할 수 있습니다. 이러한 접근방식은 throws를 호출하지 않는 코드에서 throws 함수를 호출할 수 있습니다. 예를 들어 아래와 같이 호출할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1748786079814&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func availableRainyWeekendPhotos() -&amp;gt; Result&amp;lt;[String], Error&amp;gt; {
    return Result {
        try listDownloadedPhotos(inGallery: &quot;A Rainy Weekend&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;반면에, 동기 코드를 호출하고 결과를 기다리기 위해 비동기 코드를 래핑하는 방법은 없습니다. 이를 구현하려고 하면 미묘한 경합, 쓰레드 이슈, 데드락과 같은 문제가 발생할 수 있습니다. 기존 프로젝트에 비동기 코드를 추가할 때, 위에서 아래로 작업해야 합니다. 특히, async를 사용할 코드의 최상위 레이어부터 변환한 다음에, 호출하는 함수와 메소드를 변환하여, 한번에 하나의 레이어씩 작업합니다. 동기 코드는 비동기 코드를 호출할 수 있는 방법이 없으므로, 상향식 (bottom-up) 접근 방식을 사용할 수 없습니다.&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;b&gt;비동기 시퀀스 (Asynchronous Sequences)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 섹션에서 listPhotos (inGallery:) 함수는 비동기적으로 배열의 모든 요소가 준비된 후에 전체 배열을 한번에 반환합니다. 또 다른 접근 방식은 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;비동기 시퀀스 (asynchronous sequence)&lt;/span&gt;&lt;/b&gt; 를 사용하여 한번에 콜렉션의 한 요소를 기다리는 것입니다. 비동기 시퀀스에 대한 조회 동작은 아래와 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1748935219550&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import Foundation


let handle = FileHandle.standardInput
for try await line in handle.bytes.lines {
    print(line)
}&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;일반적인 for-in 루프 대신에 위의 예제는 for 다음에 await을 작성했습니다. 비동기 함수 또는 메소드를 호출할 때와 마찬가지로 await의 작성은 가능한 중단 지점을 나타냅니다. for-await-in 루프는 다음 요소를 사용할 수 있을 때까지 기다리고 각 반복이 시작될 때 잠재적으로 실행을 일시 중단합니다.&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;b&gt;비동기 함수 병렬로 호출 (Calling Asynchronous Functions in Parallel)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;await 을 사용하여 비동기 함수를 호출하면 한번에 한 코드만 실행됩니다. 비동기 코드가 실행되는 동안 호출자는 코드의 다음 라인을 실행하기 위해 이동하기 전에 해당 코드가 완료될 때 까지 기다립니다. 예를 들어, 갤러리에서 처음 세 장의 사진을 가져오려면 아래와 같이 downloadPhoto (named:) 함수에 대한 세 번의 호출을 기다릴 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1748935544778&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let firstPhoto = await downloadPhoto(named: photoNames[0])
let secondPhoto = await downloadPhoto(named: photoNames[1])
let thirdPhoto = await downloadPhoto(named: photoNames[2])


let photos = [firstPhoto, secondPhoto, thirdPhoto]
show(photos)&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;이 방식에는 중요한 단점이 있습니다. 다운로드가 비동기이고 진행되는 동안에는 다른 작업을 수행할 수 있지만 download Photo (named:) 에 대한 호출은 한 번에 하나만 실행됩니다. 각 사진은 다음 사진이 다운로드를 시작하기 전에 완료됩니다. 하지만, 이러한 작업들은 다른 작업의 완료를 기다릴 필요가 없고, 각 사진은 동시에 다운로드 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 함수를 호출하고 코드를 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;병렬로 실행&lt;/span&gt;&lt;/b&gt;하려면 상수를 정의할 때 let 앞에 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;async 를 작성&lt;/span&gt;&lt;/b&gt;하고 상수를 사용할 때마다 await을 작성하면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1748936231686&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async let firstPhoto = downloadPhoto(named: photoNames[0])
async let secondPhoto = downloadPhoto(named: photoNames[1])
async let thirdPhoto = downloadPhoto(named: photoNames[2])


let photos = await [firstPhoto, secondPhoto, thirdPhoto]
show(photos)&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;이 예제에서 downloadPhoto (named:) 을 호출하는 세가지는 모두 이전 호출이 완료되길 기다리지 않고 시작됩니다. 사용할 수 있는 시스템 자원이 충분하다면 동시에 실행할 수 있습니다. 코드가 함수의 결과를 기다리기 위해 일시 중단되지 않기 때문에 이러한 함수 호출 중 어느 것도 await 으로 표시하지 않습니다. 대신 photos 가 정의된 라인까지 중단되지 않고 실행이 계속 됩니다. let photos를 선언한 시점에서는 이러한 비동기 호출의 결과를 필요로 하므로 세 장의 사진이 모두 다운로드 될 때까지 실행을 일시 중단하기 위해 await 을 작성합니다.&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다음 줄의 코드가 해당 함수의 결과에 따라 달라지면 await 를 사용하여 비동기 함수를 호출합니다. 이것은 순차적으로 실행되는 작업을 생성합니다.&lt;/li&gt;
&lt;li&gt;나중에 코드에서 결과가 필요하지 않을 때 async-let 을 사용하여 비동기 함수를 호출합니다. 이렇게 하면 병렬로 수행할 수 있는 작업이 생성됩니다.&lt;/li&gt;
&lt;li&gt;await 와 async-let 은 모두 일시 중단되는 동안 다른 코드를 실행할 수 있도록 합니다.&lt;/li&gt;
&lt;li&gt;두 경우 모두 비동기 함수가 반환될 때까지 필요한 경우 실행이 일시 중단됨을 나타내기 위해 가능한 일시 중단 지점을 await 로 표시합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 코드에서 이 두 가지 접근 방식을 혼합할 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;작업과 작업 그룹 (Tasks and Task Groups)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;작업 (task)&lt;/span&gt;&lt;/b&gt; 은 프로그램의 일부로 비동기적으로 실행할 수 있는 작업 단위입니다. 모든 비동기 코드는 어떠한 작업의 일부로 실행됩니다. 작업은 한번에 하나의 작업만 수행하지만, 여러 작업을 생성하면, Swift 는 동시에 수행하기 위해 작업을 스케줄링 할 수 있습니다.&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;이전에 설명한 async-let 구문은 암시적으로 하위 작업을 생성합니다. 이 구문은 프로그램에서 무슨 작업을 수행해야 될지 이미 알고 있는 상태에서 잘 동작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;작업 그룹 (task group)&lt;/span&gt;&lt;/b&gt;(TaskGroup 의 인스턴스) 을 생성하고 우선순위와 취소를 더 잘 제어할 수 있으며 동적으로 작업의 수를 생성할 수 있도록 해당 그룹에 하위 작업을 명시적으로 추가할 수도 있습니다.&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;작업은 계층 구조로 정렬됩니다. 주어진 작업 그룹의 각 작업에는 동일한 상위 작업이 있으며 각 작업에는 하위 작업이 있을수도 있습니다. 작업과 작업 그룹 간의 명시적 관계 때문에 이 접근 방식을 구조적 동시성 (structured concurrency) 이라고 합니다. 명시적 부모 (parent)-자식 (child) 관계는 작업간의 여러 이점이 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;부모 작업에서, 하위 작업이 완료될 때까지 기다릴 수 있습니다.&lt;/li&gt;
&lt;li&gt;하위 작업에서 더 높은 우선순위로 설정되면, 상위 작업의 우선순위는 자동으로 높아집니다.&lt;/li&gt;
&lt;li&gt;상위 작업이 취소되면, 각 하위 작업들은 자동으로 취소됩니다.&lt;/li&gt;
&lt;li&gt;작업-로컬 값 (Task-local value) 는 하위 작업에 자동으로 전파됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 여러 사진을 다운로드하는 코드입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1749218708274&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;await withTaskGroup(of: Data.self) { group in
    let photoNames = await listPhotos(inGallery: &quot;Summer Vacation&quot;)
    for name in photoNames {
        group.addTask {
            return await downloadPhoto(named: name)
        }
    }


    for await photo in group {
        show(photo)
    }
}&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;위 코드는 새로운 작업 그룹을 생성하고, 갤러리에 사진을 다운로드하는 하위 작업을 생성합니다. 갤러리에 사진을 다운로드하는 하위 작업을 생성합니다. Swift는 조건이 되는 만큼 비동기적으로 여러 작업을 수행할 수 있습니다. 하위 작업에서 사진 다운로드가 끝나자마자 해당 사진은 보여집니다. 하위 작업 완료에 대한 순서를 보장하지 않으므로, 갤러리의 사진은 무작위로 보여질 수 있습니다.&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;위 코드에서 각 사진은 다운로드되고 보여지므로, 작업 그룹은 결과를 반환하지 않습니다. 결과를 반환하는 작업 그룹의 경우, withTaskGroup(of:returning:body:)에 전달하는 클로저에 결과를 누적하는 코드를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1749220442500&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let photos = await withTaskGroup(of: Data.self) { group in
    let photoNames = await listPhotos(inGallery: &quot;Summer Vacation&quot;)
    for name in photoNames {
        group.addTask {
            return await downloadPhoto(named: name)
        }
    }


    var results: [Data] = []
    for await photo in group {
        results.append(photo)
    }


    return results
}&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;이전 예제와 같이, 이 예제는 각 사진을 다운로드하는 하위 작업을 생성합니다. 이전 예제와 다르게, for-await-in 루프는 다음 하위 작업이 완료될 때까지 기다리고, 해당 작업의 결과를 결과 배열에 추가한 다음에, 모든 하위 작업이 완료될 때까지 기다립니다. 마지막으로, 작업 그룹은 다운로드한 사진의 배열을 전체 결과로 반환합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;작업 취소 (Task Cancellation)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift 동시성은 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;협동 취소 모델 (cooperative cancellation model)&lt;/span&gt;&lt;/b&gt; 을 상용합니다. 각 작업은 실행 중에 적절할 때 취소 여부를 확인하고, 적절하게 취소에 응답합니다. 작업의 종류에 따라 취소에 대한 응답은 다음 중 하나에 해당합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CancellationError 와 같은 에러 발생&lt;/li&gt;
&lt;li&gt;nil 또는 빈 콜렉션 반환&lt;/li&gt;
&lt;li&gt;부분적으로 완료된 작업 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사진이 크거나 네트워크가 느려서 사진을 다운로드하는데 오랜 시간이 걸릴 수 있습니다. 모든 작업이 완료되기를 기다리지 않고 작업을 멈추려면, 작업이 취소되었는지 확인하고 취소된 경우에 실행을 중지합니다. 작업이 취소되었는지 확인하는 방법은 두 가지가 있습니다. Task.checkCancellation() 타입 메소드를 호출하거나, Task.isCancelled 타입 프로퍼티를 읽어서 확인할 수 있습니다. checkCancellation() 을 호출하는 것은 작업이 취소되었으면 에러를 발생시킵니다. 던지는 작업은 작업 외부로 에러를 전파하여 모든 작업을 중지시킬 수 있습니다. 이는 구현과 이해가 간단하다는 이점이 있습니다. 더 유연한 방법으로는, 네트워크 연결을 닫고 임시 파일을 지우는 것과 같은 작업 중지의 부분으로 정리 작업을 수행할 수 있는 isCancelled 프로퍼티를 사용합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1749391828381&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let photos = await withTaskGroup(of: Optional&amp;lt;Data&amp;gt;.self) { group in
    let photoNames = await listPhotos(inGallery: &quot;Summer Vacation&quot;)
    for name in photoNames {
        let added = group.addTaskUnlessCancelled {
            guard !Task.isCancelled else { return nil }
            return await downloadPhoto(named: name)
        }
        guard added else { break }
    }


    var results: [Data] = []
    for await photo in group {
        if let photo { results.append(photo) }
    }
    return results
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 이전 버전과 몇 가지 다른 점이 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;취소 후에 새로운 작업이 시작되는 것을 피하기 위해 각 작업은 TaskGroup.addTaskUnlessCancelled(priority:operation:) 메소드를 사용해서 추가합니다.&lt;/li&gt;
&lt;li&gt;addTaskUnlessCancelled(priority:operation:) 을 호출할 때마다, 코드는 하위 작업이 새로 추가되었음을 확인합니다. 그룹이 취소되면 added 의 값은 false 이고, 코드는 추가적인 사진 다운로드를 중지합니다&lt;/li&gt;
&lt;li&gt;각 작업은 사진을 다운로드 하기 전에 취소 여부를 검사합니다. 작업이 취소 되었으면, nil 을 반환합니다.&lt;/li&gt;
&lt;li&gt;결국, 작업 그룹은 결과를 수집할 때 nil 값이면 건너뜁니다. nil을 반환해서 취소를 처리한다는 것은 작업 그룹에서 부분적인 결과를 반활할 수 있다는 의미입니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉시 취소에 대한 알림이 필요한 경우에 Task.withTaskCacnellationHandler(operation:onCancel:) 메소드를 사용하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면,&lt;/p&gt;
&lt;pre id=&quot;code_1749562128426&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let task = await Task.withTaskCancellationHandler {
    // ...
} onCancel: {
    print(&quot;Canceled!&quot;)
}


// ... some time later...
task.cancel()  // Prints &quot;Canceled!&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;작업은 완료될 때까지 수행하거나 취소를 확인하고 조기 중지합니다. 취소 처리가 시작될 때 작업은 여전히 수행 중이므로, 경쟁 조건이 생성될 수 있는 작업과 취소 처리간의 상태 공유를 피해야 합니다.&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;b&gt;구조화되지 않은 동시성 (Unstrcutured Concurrency)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 설명한 동시성에 대한 구조화된 접근 방식 외에도 Swift는 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;구조화되지 않은 동시성 (unstructured concurrency)&lt;/span&gt;&lt;/b&gt; 을 지원합니다. 작업 그룹의 일부인 작업과 달리 구조화되지 않은 작업 (unstructured task) 에는 상위 작업이 없습니다. 프로그램이 필요로 하는 방식으로 구조화되지 않은 작업을 관리할 수 있는 유연성이 있지만 정확성에 대한 책임도 있습니다. 현재 액터(actor) 에서 실행되는 구조화되지 않은 작업을 생성하려면 Task.init(priority:operation:) 초기화 구문을 호출해야 합니다. 더 구체적으로 분리된 작업으로 알려진 현재 액터의 일부가 아닌 구조화되지 않은 작업을 생성하려면 Task.detached(priority:operation:) 클래스 메소드를 호출하면 됩니다. 이 모든 ㅈ동작은 서로 상호작용 할 수 있는 작업(task)을 반환합니다. 예를 들면, 결과를 기다리거나 취소하는 경우에 해당됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1749562799623&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let newPhoto = // ... some photo data ...
let handle = Task {
    return await add(newPhoto, toGalleryNamed: &quot;Spring Adventures&quot;)
}
let result = await handle.value&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;액터 (Actors)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램을 동시성 조각으로 분리하기위해 작업 (task) 을 사용할 수 있습니다. 작업은 서로 분리되어 있어 같은 시간에 안전하게 실행될 수 있지만 작업 간에 일부 정보를 공유해야 할 수도 있습니다. 액터 (Actors) 는 동시성 코드간에 정보를 안전하게 공유할 수 있게 해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스와 마찬가지로 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;액터 (actors)&lt;/span&gt;&lt;/b&gt; 는 참조 타입이므로 클래스는 참조 타입 (Classes Are Refrence Types) 에서 값 타입과 참조 타입의 비교는 클래스 뿐만 아니라 액터에도 적용됩니다. 클래스와 다르게 액터는 한 번에 하나의 작업만 변경 가능한 상태에 접근할 수 있도록 허용하므로 여러 작업의 코드가 액터의 동일한 인스턴스와 상호작용 하는 것은 안전합니다. 예를 들어 아래는 온도를 기록하는 액터 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1749563473822&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;actor TemperatureLogger {
    let label: String
    var measurements: [Int]
    private(set) var max: Int


    init(label: String, measurement: Int) {
        self.label = label
        self.measurements = [measurement]
        self.max = measurement
    }
}&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;actor 키워드를 사용하여 액터를 도입하고 중괄호로 정의합니다. TemperatureLogger 액터는 액터 외부의 다른 코드가 접근할 수 있는 프로퍼티가 있으며, 액터 내부의 코드만 최대값을 업데이트 할 수 있게 max 프로퍼티를 제한합니다.&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;구조체와 클래스와 같은 초기화 구문으로 액터와 인스턴스를 생성합니다. 액터의 프로퍼티 또는 메소드에 접근할 때 일시 중단 지점을 나타내기 위해 await을 사용합니다. 예를 들면 아래와 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1749910614735&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let logger = TemperatureLogger(label: &quot;Outdoors&quot;, measurement: 25)
print(await logger.max)
// Prints &quot;25&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;이 예제에서 logger.max 에 접근하는 것은 일시 중단 지점으로 가능합니다. 액터는 한 번에 하나의 작업만 변경 가능한 상태에 접근할 수 있도록 허용하므로 다른 작업의 코드가 이미 로거와 상호 작용하고 있는 경우 이 코드는 프로퍼티 접근을 기다리는 동안 일시 중단됩니다.&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;대조적으로 액터의 부분 코드는 행위자의 프로퍼티에 접근할 때 await 을 작성하지 않습니다. 예를 들어 새로운 온도로 TemperatureLogger 를 업데이트 하는 메소드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1749912781986&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;extension TemperatureLogger {
    func update(with measurement: Int) {
        measurements.append(measurement)
        if measurement &amp;gt; max {
            max = measurement
        }
    }
}&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;update(with:) 메소드는 액터에서 이미 실행 중이므로 max와 같은 프로퍼티에 대한 접근을 await 으로 표시하지 않습니다. 이 메소드는 액터가 변경 가능한 상태와 상호 작용하기 위해 한 번에 하나의 작업만 허용하는 이유 중 하나를 보여줍니다. 액터의 상태에 대한 일부 업데이트는 일시적으로 불변성을 깨뜨리빈다. TemperatureLogger 액터는 온도 리스트와 최대 온도를 추적하고 새로운 측정값을 기록할 때 최대 온도를 업데이트 합니다. 업데이트 도중에 새로운 측정값을 추가한 후 max 를 업데이트 하기 전에 온도 Logger는 일시적으로 일치하지 않는 상태가 됩니다. 여러 작업이 동일한 인스턴스에 상호 작용하는 것을 방지하면 다음 이벤트 시퀀스와 같은 문제를 방지할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 코드는 update (with:) 메소드를 호출합니다. 먼저 measurements 배열을 업데이트 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 코드에서 max를 업데이트 하기 전에 다른 코드에서 최대값과 온도 배열을 읽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 코드는 max 를 변경하여 업데이트를 완료합니다.&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;이러한 경우 다른 곳에서 실행 중인 코드는 데이터가 일시적으로 유효하지 않은 동안 update(with:) 호출 중간에 액터에 대한 접근이 인터리브 (interleaved) 되어 잘못된 정보를 읽습니다. Swift 액터는 한 번에 해당 상태에 대해 하나의 작업만 허용하고 해당 코드는 awiat가 일시 주단 지점으로 표시되는 위치에서만 중단될 수 있기 때문에 Swift 행위자를 사용하여 이 문제를 방지할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;update(:with) 는 일시 중단 지점을 포함하지 않으므로 다른 코드는 업데이트 중간에 데이터에 접근할 수 없습니다.&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;pre id=&quot;code_1750514055433&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;print(logger.max)  // Error&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;await 작성 없이 logger.max 에 접근하는 것은 액터의 프로퍼티가 해당 액터의 분리된 로컬 상태의 부분이기 때문에 실패합니다. 이 프로퍼티에 접근하는 코드는 비동기 작업인 액터의 일부분으로 수행되어야 하고, await 을 작성해야 합니다. Swift는 액터에서 수행하는 코드만 액터의 로컬 상태에 접근할 수 있도록 보장합니다. 이 보장을 액터 분리 (actor isolation) 이라고 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;중단 가능 지점 사이의 코드는 다른 비동기 코드의 중단없이 순차적으로 수행됩니다.&lt;/li&gt;
&lt;li&gt;액터의 로컬 상태와 상호작용하는 코드는 해당 액터에서만 수행됩니다.&lt;/li&gt;
&lt;li&gt;액터는 한번에 하나의 코드만 수행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 보장 때문에, await 을 포함하고 액터 내부에 있는 코드는 프로그램의 다른 위치에서 일시적으로 유효하지 않은 상태를 관찰하지 않고 업데이트를 수행할 수 있습니다. 예를 들어, 아래의 코드는 측정된 온도를 화씨에서 섭씨로 변환합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1750514702161&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;extension TemperatureLogger {
    func convertFahrenheitToCelsius() {
        measurements = measurements.map { measurement in
            (measurement - 32) * 5 / 9
        }
    }
}&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;위 코드는 측정된 온도를 한번에 하나씩 변환합니다. map 작업이 진행되는 동안, 일부 온도는 화씨로 다른 온도는 섭씨로 표시됩니다. 그러나, 이 코드는 await 을 포함하지 않으므로, 메소드에서 중단 지점이 없습니다. 이 메소드가 수정하는 상태는 액터에 속하며, 해당 코드가 액터에서 수행될 때를 제외하고는 코드를 읽거나 수정하지 못하도록 합니다. 이것은 온도 변환이 진행되는 동안 다른 코드가 부분적으로 변환된 온도를 읽을 수 없다는 것을 의미합니다.&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;잠재적 중단 지점을 생략해서 임시적으로 유효하지 않은 상태를 보호하는 액터에 코드를 작성하는 것 외에도 해당 코드를 동기 메소드를 이동할 수 있습니다. 위에 convertFarenheitToCelsius() 메소드는 동기 메소드이므로, 절대 잠재적 중단 지점이 포함되지 않음을 보장합니다. 이 함수는 데이터 모델을 일시적으로 불일치하게 만드는 코드를 캡슐화하고, 작업이 완료되어 데이터 일관성을 복원하기 전까지 다른 코드를 수행할 수 없음을 읽기 쉽게 만들어 줍니다. 앞으로, 이 함수에 비동기 코드를 추가하여, 일시 중단 지점을 도입하면 버그가 발생하는 대신에 컴파일 에러가 발생합니다.&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;b&gt;전송 가능 타입 (Sendable Types)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업 (Tasks)와 액터 (actors)는 프로그램을 동시에 안전하게 실행할 수 있는 조각으로 나눌 수 있습니다. 작업 또는 액터의 인스턴스 내에서 변수와 프로퍼티와 같은 변경 가능한 상태를 포함하는 프로그램의 일부분을 동시에 도메인 (concurrency domain) 이라고 부릅니다. 어떤 데이터는 데이터가 변경 가능한 상태를 포함하지만 동시 접근에 대해 보호되지 않으므로 동시성 도메인 간에 공유될 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 동시성 도메인에서 다른 동시성 도메인으로 공유될 수 있는 타입을 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;전송 가능 타입 (sendable type)&lt;/span&gt;&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;Sendable 프로토콜을 선언하여 전송 가능한 타입으로 표시합니다. 이 프로토콜은 어떠한 코드 요구사항을 가지지 않지만 Swift 가 적용하는 의미론적 요구사항이 있습니다. 일반적으로 타입을 전송 가능한 것으로 나타내기 위한 세가지 방법이 있습니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;타입은 값 타입이고 변경 가능한 상태는 다른 전송 가능한 데이터로 구성됩니다 - 예를 들어, 전송 가능한 저장된 프로퍼티가 있는 구조체 또는 전송 가능한 연관된 값이 있는 열거형이 있습니다.&lt;/li&gt;
&lt;li&gt;타입은 변경 가능한 상태가 없으며 변경 불가능한 상태는 다른 전송 가능한 데이터로 구성됩니다. - 예를 들어, 읽기전용 프로퍼티만 있는 구조체 또는 클래스가 있습니다.&lt;/li&gt;
&lt;li&gt;타입은 @MainActor 로 표시된 클래스나 특정 쓰레드나 큐에서 프로퍼티에 순차적으로 접근하는 클래스와 같이 변경 가능한 상태의 안정성을 보장하는 코드를 가지고 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의미론적 요구사항의 자세한 리스트는 전송 가능 (Sendable) 프로토콜을 참고 바랍니다.&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;pre id=&quot;code_1750518329219&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct TemperatureReading: Sendable {
    var measurement: Int
}


extension TemperatureLogger {
    func addReading(from reading: TemperatureReading) {
        measurements.append(reading.measurement)
    }
}


let logger = TemperatureLogger(label: &quot;Tea kettle&quot;, measurement: 85)
let reading = TemperatureReading(measurement: 45)
await logger.addReading(from: reading)&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;TemperatureReading 은 전송 가능한 프로퍼티만 가지는 구조체이며 public 또는 @usableFromInline 으로 표시되지 않은 구조체이므로 암시적으로 전송 가능합니다. 다음은 Sendable 프로토콜 준수가 암시되는 구조체입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1750518522390&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct TemperatureReading {
    var measurement: Int
}&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;명시적으로 타입을 보낼 수 없는 것으로 나타내려면 Senable 프로토콜에 대한 암시적으로 준수를 재정의하고 확장을 사용합니다:&lt;/p&gt;
&lt;pre id=&quot;code_1750518622488&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct FileDescriptor {
    let rawValue: CInt
}




@available(*, unavailable)
extension FileDescriptor: Sendable { }&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;위의 코드는 POSIX 파일 디스크립터에 대한 래퍼의 일부분을 보여줍니다. 파일 디스크립터의 인터페이스는 징수를 사용하여 열린 파일에 대해 식별하고 상호작용 하고 정수값을 보낼 수 있지만, 비동기적 도메인을 통해 전송하는 것은 안전하지 않습니다.&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;위의 코드에서 FileDescriptor 는 암시적으로 보낼 수 있는 구조체입니다. 그러나 확장에 Sendable 에 대한 준수를 사용할 수 없게 만들어 타입 전송을 막을 수 있습니다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;References&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/&quot;&gt;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1751733103119&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;Documentation&quot; data-og-description=&quot;&quot; data-og-host=&quot;docs.swift.org&quot; data-og-source-url=&quot;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/&quot; data-og-url=&quot;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/&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;Documentation&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;docs.swift.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>iOS/Swift</category>
      <author>seunghwaan</author>
      <guid isPermaLink="true">https://seosh817.tistory.com/698</guid>
      <comments>https://seosh817.tistory.com/698#entry698comment</comments>
      <pubDate>Sun, 18 May 2025 22:10:16 +0900</pubDate>
    </item>
    <item>
      <title>[iOS/Swift] 열거형, 구조체, 클래스(Enum, Structure, Class)</title>
      <link>https://seosh817.tistory.com/697</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[ 이 게시물은 Swift.org를 참고하며 Swift를 공부하기 위해 작성하는 글 입니다. ]&lt;/b&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;열거형 (Enumeration)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;열거형 (Enumeration)&lt;/b&gt;&lt;/span&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;열거형 구문 (Enumeration Syntax)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;열거형&lt;/span&gt;&lt;/b&gt;은 enum 키워드로 선언하고 중괄호 안에 모든 정의를 선언합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746363107520&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;enum SomeEnumeration {
    // enumeration definition goes here
}&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;아래 예제는 나침반 4개의 주요 포인트를 나타냅니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746363134001&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;enum CompassPoint {
    case north
    case south
    case east
    case west
}&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;열거형 안에 정의된 값(north, south, east, wet)은 열거형 케이스이며 case 키워드를 사용하여 나타냅니다.&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;pre id=&quot;code_1746364195411&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;enum Planet {
    case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 열거형 정의는 새로운 타입으로 정의합니다. 열거형 타입에 단수 이름으로 지정할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746367585183&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var directionToHead = CompassPoint.west&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;directionToHead 는 초기화될 때 CompassPoint의 값 중 하나로 유추됩니다. 더 짧게 점 구문을 사용하여 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;CompassPoint 값을 설정할 수 있습니다.&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;directionToHead 의 타입은 이미 알고 있으므로 값을 설정할 때 타입 명시를 삭제할 수 있습니다. 따라서 명시적으로 타입화 된 열거형 값을 코드를 쉽게 읽을 수 있습니다.&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;스위치 구문의 열거형 값 일치(Matching Enumeration Values with a Switch Statement)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;switch 구문&lt;/span&gt;&lt;/b&gt;으로 각각의 열거형 값을 일치시킬 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746368536692&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;directionToHead = .south
switch directionToHead {
case .north:
    print(&quot;Lots of planets have a north&quot;)
case .south:
    print(&quot;Watch out for penguins&quot;)
case .east:
    print(&quot;Where the sun rises&quot;)
case .west:
    print(&quot;Where the skies are blue&quot;)
default:
    print(&quot;Not a safe place for humans&quot;)
}
// Prints &quot;Watch out for penguins&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;이 코드는 directonToHead의 값을 switch 문으로 돌리는데, .north일 경우 &quot;Lots of planets have a north&quot;, .south와 같은 케이스일 경우 &quot;Watch out for penguins&quot;를 출력합니다.&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;모든 열거형 케이스에 대해 case를 제공할 수 없는 경우 default 케이스를 통해 정의할 수 있습니다.&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;열거형 케이스 반복 (Iterating over Enumeration Cases)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;열거형 이름 뒤에 : CaseIterable 을 작성하여 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;열거형의 반복&lt;/span&gt;&lt;/b&gt;을 활성화시킬 수 있습니다. 이후, 열거형 타입에 allCases 프로퍼티를 통해 모든 케이스를 수집하고 방출할 수 있습니다. 아래는 예시입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746370528481&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;enum Beverage: CaseIterable {
    case coffee, tea, juice
}
let numberOfChoices = Beverage.allCases.count
print(&quot;\(numberOfChoices) beverages available&quot;)
// Prints &quot;3 beverages available&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;아래 예시에서 Beverage 열거형의 모든 케이스를 포함하는 콜렉션에 접근하기 위해 for-in 루프와 Beverage.allCases를 사용합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746374352753&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for beverage in Beverage.allCases {
    print(beverage)
}
// coffee
// tea
// juice&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;연관된 값 (Associated Values)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;연관된 값&lt;/span&gt;&lt;/b&gt;을 저장하기 위해 Enum 열거형을 정의할 수 있고 이 값 타입은 필요에 따라 열거형의 각 케이스에 따라 달라질 수 있습니다.&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;예를 들어, 4개의 정수로 이루어진 UPC 바코드와 문자열을 저장하는 QR 코드 바코드, 두 타입의 Barcode를 정의하는 열거형은 아래와 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746460158198&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;enum Barcode {
    case upc(Int, Int, Int, Int)
    case qrCode(String)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 이러한 타입을 이용하면 아래와 같이 새로운 바코드를 생성할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746460999199&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var productBarcode = Barcode.upc(8, 85909, 51226, 3)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 상품의 다른 바코드 타입은 아래와 같이 할당할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746461019569&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;productBarcode = .qrCode(&quot;ABCDEFGHIJKLMNOP&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;enum의 스위치 구문은 아래와 같이 사용할 수 있습니다. switch 케이스의 본문 내에서 사용하기 위해 상수 (let 접두사) 또는 변수 (var 접두사)로 값을 추출할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746462573400&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
    print(&quot;UPC: \(numberSystem), \(manufacturer), \(product), \(check).&quot;)
case .qrCode(let productCode):
    print(&quot;QR code: \(productCode).&quot;)
}
// Prints &quot;QR code: ABCDEFGHIJKLMNOP.&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;열거형 케이스를 위한 연관된 값 모두를 상수로 추출하거나 변수로 추출하려면 간겨랗게 케이스 이름 앞에 let 또는 var를 선언하면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746462607678&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;switch productBarcode {
case let .upc(numberSystem, manufacturer, product, check):
    print(&quot;UPC : \(numberSystem), \(manufacturer), \(product), \(check).&quot;)
case let .qrCode(productCode):
    print(&quot;QR code: \(productCode).&quot;)
}
// Prints &quot;QR code: ABCDEFGHIJKLMNOP.&quot;&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;&lt;b&gt;암시적으로 할당된 원시값 (Implicitly Assigned Raw Values)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정수 또는 문자열 원시값이 저장된 열거형일 경우 각 케이스에 명시적으로 원시값을 가질 필요가 없고, &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;원시값을 설정하지 않으면 Swift는 자동적으로 값을 할당&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;예를 들어, 정수를 원시값으로 사용하면 각 케이스의 암시적 값은 이전 케이스에서 하나씩 증가됩니다. 그리고, 첫번째 케이스에 값 설정이 안되어 있으면 0 으로 설정합니다.&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;아래 열거형은 태양으로부터 각 행성의 순서를 정수값으로 나타내는 Planet 열거형입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746535847231&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;enum Planet: Int {
    case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}&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;위의 예시에서 Planet.mercury 는 명시적을 원시값 1을 가지고 Planet.venus 는 암시적으로 원시값 2를 가지게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 열거형은 각 방향의 이름을 문자열 원시값으로 나타내는 CompassPoint 입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746535963350&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;enum CompassPoint: String {
    case north, south, east, west
}&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;위의 예시에서 CompassPoint.south는 &quot;south&quot;라는 암시적 원시값을 가지고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고, rawValue 프로퍼티를 사용하여 열거형 케이스의 원시값에 접근할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746536003846&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let earthsOrder = Planet.earth.rawValue
// earthsOrder is 3


let sunsetDirection = CompassPoint.west.rawValue
// sunsetDirection is &quot;west&quot;&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;&lt;b&gt;원시값으로 초기화 (Initializing from a Raw Value)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원시값 타입으로 열거형을 정의한다면 열거형은 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;원시값의 타입 (rawValue 라는 파라미터)을 사용하는 초기화를 자동으로 수신&lt;/span&gt;&lt;/b&gt;하고 열거형 케이스 또는 nil 을 반환합니다. 이 초기화를 이용하여 열거형의 새 인스턴스를 만들 수 있습니다.&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;pre id=&quot;code_1746536755772&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let possiblePlanet = Planet(rawValue: 7)
// possiblePlanet is of type Planet? and equals Planet.uranus&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;그러나 Int 값으로 모든 행성을 찾을 수는 없습니다. 이러한 점 때문에 원시값 초기화는 항상 옵셔널 열거형 케이스를 반환합니다. 위의 예시에서 possiblePlanet은 Planet? 타입입니다.&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;11 으로 행성을 찾는다면 원시값 초기화로부터 반환된 옵셔널 Planet 값은 nil 입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746537023112&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let positionToFind = 11
if let somePlanet = Planet(rawValue: positionToFind) {
    switch somePlanet {
    case .earth:
        print(&quot;Mostly harmless&quot;)
    default:
        print(&quot;Not a safe place for humans&quot;)
    }
} else {
    print(&quot;There isn't a planet at position \(positionToFind)&quot;)
}
// Prints &quot;There isn't a planet at position 11&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;이 예시는 11 의 원시값으로 행성을 찾기위해 옵셔널 바인딩을 사용합니다. if let somePlanet = Planet(rawValue: 11) 구문은 옵셔널 Planet을 생성하고 가져올 수 있다면 somePlanet에 값이 설정되고, 가져올 수 없다면 nil이 설정됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;재귀 열거형 (Recursive Enumerations)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;재귀 열거형 (recursive enumeration)&lt;/span&gt;&lt;/b&gt;은 열거형 케이스에 하나 이상의 연관된 열거형의 다른 인스턴스를 가지고 있는 열거형입니다. 열거형 케이스가 재귀적임을 나타내기 위해 케이스 작성 전에 indirect 를 작성하여 컴파일러에게 필요한 간접 (indirection) 계층을 삽입하도록 지시합니다.&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;pre id=&quot;code_1746538363862&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;enum ArithmeticExpression {
    case number(Int)
    indirect case addition(ArithmeticExpression, ArithmeticExpression)
    indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}&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;열거형의 시작 전에 indirect 를 작성하여 연관된 값을 가진 모든 열거형 케이스에 간접을 활성화할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746538614049&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;indirect enum ArithmeticExpression {
    case number(Int)
    case addition(ArithmeticExpression, ArithmeticExpression)
    case multiplication(ArithmeticExpression, ArithmeticExpression)
}&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;이 열거형은 숫자, 2개의 표현식의 덧셈, 2개의 표현식의 곱셉의 3가지의 산술 표현식을 저장할 수 있습니다. addition과 multiplication 케이스는 산술 표현식과 연관된 값을 가지고 있고 이 연관된 값은 중첩 표현식을 가능하게 해줍니다.&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;예를 들어 (5 + 4) * 2 표현식은 곱셈의 우항은 하나의 숫자를 가지고 있고 좌항은 다른 표현식을 가지고 있습니다. 데이터는 중첩되기 때문에 데이터를 저장하는 열거형도 중첩을 지원해야 합니다. 이것은 열거형은 재귀적이어야 한다는 의미입니다. 아래의 코드는 (5 + 4) * 2에 대해 생성되는 ArithmeticExpression 재귀 열거형을 나타냅니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746542188345&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재귀 함수는 재귀 구조를 가진 데이터를 작업하는 간단한 방법입니다. 예를 들어 아래는 산술 표현식을 판단하는 함수입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746542445458&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func evaluate(_ expression: ArithmeticExpression) -&amp;gt; Int {
    switch expression {
    case let .number(value):
        return value
    case let .addition(left, right):
        return evaluate(left) + evaluate(right)
    case let .multiplication(left, right):
        return evaluate(left) * evaluate(right)
    }
}


print(evaluate(product))
// Prints &quot;18&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;구조체와 클래스 (Structure and Classes)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;구조체&lt;/span&gt;&lt;/b&gt;와 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;클래스&lt;/span&gt;&lt;/b&gt;에 대해 알아보겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;구조체와 클래스 비교&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;공통점&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값을 저장하는 프로퍼티 정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능 제공을 위한 메소드 정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서브 스크립트 구문을 사용하여 값에 접근을 제공하는 서브 스크립트 정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기화 상태를 설정하기 위한 초기화 정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 구현을 넘어 기능적 확장을 위한 확장&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 종류의 표준 기능을 제공하는 프로토콜 준수&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;클래스에는 존재하고 구조체에는 없는 기능&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상속 사용 가능.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입 캐스팅을 통해 런타임에 클래스 인스턴스의 타입을 확인 가능.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기화 해제 구문 (Deinitializers)을 통한 클래스 인스턴스가 할당된 리소스 해제.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참조 카운팅은 하나 이상의 클래스 인스턴스 참조를 허락.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;정의 구문 (Definition Syntax)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조체와 클래스는 유사한 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;정의 구문&lt;/span&gt;&lt;/b&gt;을 가지고 있습니다. 구조체는 struct 키워드, 클래스는 class 키워드로 시작합니다. 둘다 중괄호 안에 정의가 위치합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746873123642&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct SomeStructure {
    // structure definition goes here
}
class SomeClass {
    // class definition goes here
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 구조체 정의와 클래스 정의의 예시입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746873255162&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct Resolution {
    var width = 0
    var height = 0
}
class VideoMode {
    var resolution = Resolution()
    var interlaced = false
    var frameRate = 0.0
    var name: String?
}&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;위의 예제는 픽셀기반의 화면 해상도를 설명하는 Resolution 이라는 새로운 구조체를 정의합니다. 이 구조체는 width와 height 두 개의 프로퍼티를 가지고 있습니다.&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;위 예시는 비디오 화면을 위한 특정 비디오 모드를 설명하는 VideoMode 라는 새로운 클래스를 정의합니다. 이 클래스는 4개의 프로퍼티를 가지고 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;구조체와 클래스 인스턴스 (Structure and Class Instances)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Resolution 구조체 정의와 VideoMode 클래스 정의는 오직 Resolution 또는 VideoMode 의 모양만 설명하고, 자체적인 해상도 또는 비디오 모드에 대해서는 설명하지 않습니다. 그렇게 하려면 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;구조체 또는 클래스의 인스턴스 생성&lt;/span&gt;&lt;/b&gt;이 필요합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746877674890&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let someResolution = Resolution()
let someVideoMode = VideoMode()&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;&lt;b&gt;구조체 타입에 대한 멤버별 초기화 구문 (Memberwise Initializers for Structure Types)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 구조체는 새로운 구조체 인스턴스의 멤버 프로퍼티를 초기화 할 때 사용할 수 있는 자동적으로 생성되는 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;멤버별 초기화 구문 (meberwise initializer)&lt;/span&gt;&lt;/b&gt; 을 가지고 있습니다. 새로운 인스턴스에 프로퍼티 초기값은 이름으로 멤버별 초기화에 전달 될 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1747491570837&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let vga = Resolution(width: 640, height: 480)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조체와는 반대로 클래스 인스턴스는 멤버별 초기화를 가지지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;구조체와 열거형은 값 타입 (Structures and Enumerations are value types)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값 타입 (value type) 은 변수 또는 상수에 할당될 때나 함수에 전달될 때 복사 되는 값인 타입입니다. 실제로 Swift에서 정수, 부동 소수점, 부울, 문자열, 배열 그리고 딕셔너리와 같은 기본 타입은 모두 값 타입이고 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;구조체로 구현&lt;/span&gt;&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;Swift에서 모든 구조체와 열거형은 값 타입입니다. 이는 생성한 구조체와 열거형 인스턴스와 프로퍼티로 포함된 모든 값 타입은 코드에서 전달될 때 복사된다는 의미입니다.&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;Resolution 구조체를 사용하는 아래 예제를 살펴봅시다.&lt;/p&gt;
&lt;pre id=&quot;code_1747495785129&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let hd = Resolution(width: 1920, height: 1080)
var cinema = hd&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;이 예제는 hd 라는 상수를 선언하고 풀 HD 비디오 (1920 픽셀 너비와 1080 픽셀 높이)의 너비와 높이를 초기화하는 Resolution 인스턴스를 설정합니다.&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;그리고 Cinema 라는 변수를 선언하고 hd의 값을 설정하면, Resolution은 구조체이므로 기존 인스턴스의 복사본이 만들어지고 이 새 복사본에 cinema 가 할당됩니다. hd 와 cinema 는 같은 너비와 높이를 가지지만 2개는 완벽하게 다른 인스턴스 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로 cinema 의 width 프로퍼티를 디지털 시네마 프로젝션에 사용되는 2K 표준 (2048 픽셀 너비)로 수정하면 cinema의 width 프로퍼티는 2048로 바뀌지만, 기존 hd 인스턴스의 width 프로퍼티는 1920 의 기존값을 그대로 가지고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1747495988098&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;print(&quot;cinema is now \(cinema.width) pixels wide&quot;)
// Prints &quot;cinema is now 2048 pixels wide&quot;

print(&quot;hd is still \(hd.width) pixels wide&quot;)
// Prints &quot;hd is still 1920 pixels wide&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1294&quot; data-origin-height=&quot;460&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nlYCr/btsN1t1mmuV/NL0lyqWvbmRiQAETe53Chk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nlYCr/btsN1t1mmuV/NL0lyqWvbmRiQAETe53Chk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nlYCr/btsN1t1mmuV/NL0lyqWvbmRiQAETe53Chk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnlYCr%2FbtsN1t1mmuV%2FNL0lyqWvbmRiQAETe53Chk%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;600&quot; height=&quot;213&quot; data-origin-width=&quot;1294&quot; data-origin-height=&quot;460&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;열거형에서도 같은 동작이 이루어집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;remeberedDirection 은 currentDirection 에 값이 할당될 때 실질적으로 복사본이 설정됩니다. 이후에 currentDirection 에 값을 변경해도 rememberedDirection 에 저장된 원래 값의 복사본에는 영향을 주지 않습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1747496442146&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;enum CompassPoint {
    case north, south, east, west
    mutating func turnNorth() {
        self = .north
    }
}
var currentDirection = CompassPoint.west
let rememberedDirection = currentDirection
currentDirection.turnNorth()


print(&quot;The current direction is \(currentDirection)&quot;)
print(&quot;The remembered direction is \(rememberedDirection)&quot;)
// Prints &quot;The current direction is north&quot;
// Prints &quot;The remembered direction is west&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;클래스는 참조 타입 (Classes Are Reference Types)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값 타입과 반대로 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;참조 타입 (reference types)&lt;/span&gt;&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;아래는 위에 정의된 VideoMode 를 사용하는 예시입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1747568298384&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = &quot;1080i&quot;
tenEighty.frameRate = 25.0&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;이 예시는 tenEighty 라는 새로운 상수를 선언하고 VideoMode 클래스의 새로운 인스턴스를 참조하도록 설정합니다.&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;아래는 tenEighty 에 alsoTenEighty 라는 새로운 상수를 할당하고 alsoTenEighty 의 프레임 속도로 수정합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1747568880884&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0&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;클래스는 참조 타입이므로 TenEighty 와 alsoTenEighty 는 실질적으로 같은 VideoMode 인스턴스를 참조합니다. 그래서 아래 그림과 같이 하나의 인스턴스에 다른 2개의 이름을 가지고 있게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1474&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIwkfn/btsN1t1B0Eu/BcWCZSk7QLGzaBslIhZ7G1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIwkfn/btsN1t1B0Eu/BcWCZSk7QLGzaBslIhZ7G1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIwkfn/btsN1t1B0Eu/BcWCZSk7QLGzaBslIhZ7G1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIwkfn%2FbtsN1t1B0Eu%2FBcWCZSk7QLGzaBslIhZ7G1%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;600&quot; height=&quot;122&quot; data-origin-width=&quot;1474&quot; data-origin-height=&quot;300&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;pre id=&quot;code_1747568954733&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;print(&quot;The frameRate property of tenEighty is now \(tenEighty.frameRate)&quot;)
// Prints &quot;The frameRate property of tenEighty is now 30.0&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;References&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/&quot;&gt;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1751731592067&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;Documentation&quot; data-og-description=&quot;&quot; data-og-host=&quot;docs.swift.org&quot; data-og-source-url=&quot;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/&quot; data-og-url=&quot;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/&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;Documentation&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;docs.swift.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>iOS/Swift</category>
      <category>Class</category>
      <category>enum</category>
      <category>IOS</category>
      <category>structure</category>
      <category>Swift</category>
      <author>seunghwaan</author>
      <guid isPermaLink="true">https://seosh817.tistory.com/697</guid>
      <comments>https://seosh817.tistory.com/697#entry697comment</comments>
      <pubDate>Sun, 4 May 2025 21:30:31 +0900</pubDate>
    </item>
    <item>
      <title>[iOS/Swift] 클로저 (Closures)</title>
      <link>https://seosh817.tistory.com/692</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[ 이 게시물은 Swift.org를 참고하며 Swift를 공부하기 위해 작성하는 글 입니다. ]&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;Swift의 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;클로저 (Closures)&lt;/span&gt;&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;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;클로저&lt;/b&gt;&lt;/span&gt;는 정의된 컨텍스트에서 모든 상수와 변수에 대한 참조를 캡처하고 저장할 수 있습니다. 이러한 상수와 변수를 폐쇄 (closing over)라고 합니다. Swift는 캡처의 모든 메모리 관리를 처리합니다.&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: #ef5369;&quot;&gt;&lt;b&gt;클로저&lt;/b&gt;&lt;/span&gt;는 3가지 형태 중 하나를 취합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;전역 함수&lt;/span&gt;&lt;/b&gt;는 이름을 가지고 어떠한 값도 캡처하지 않는 클로저입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;중첩 함수&lt;/span&gt;&lt;/b&gt;는 이름을 가지고 둘러싼 함수로 부터 값을 캡처할 수 있는 클로저입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;클로저 표현식&lt;/span&gt;&lt;/b&gt;은 주변 컨텍스트에서 값을 캡처할 수 있는 경량 구문으로 작성된 이름이 없는 클로저입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift의 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;클로저 표현식&lt;/span&gt;&lt;/b&gt;은 일반 시나리오에서 간단하고 깔끔한 구문을 위한 최적화를 통해 깔끔하고 명확한 스타일을 가지고 있습니다. 이러한 최적화는 아래와 같은 것들이 포함됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컨텍스트에서 파라미터와 반환값 타입 유추&lt;/li&gt;
&lt;li&gt;단일 표현식 클로저의 암시적 반환&lt;/li&gt;
&lt;li&gt;약식 인수 이름&lt;/li&gt;
&lt;li&gt;후행 클로저 구문&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;클로저 표현식 (Closure Expressions)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;클로저 표현식 (Closure expressions)&lt;/span&gt;&lt;/b&gt;은 짧은 형태로 클로저를 작성하기 위한 몇개의 구문 최적화를 제공합니다. 아래의 클로저 표현식 예제는 여러 반복에 걸쳐 sorted(by:) 메소드의 단일 예제를 구체화하는 최적화 예시입니다. 각 예시는 동일한 기능을 보다 간결하게 표현합니다.&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;정렬 메소드 (The Sorted Method)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift의 표준 라이브러리는 사용자가 제공하는 정렬 클로저의 출력을 기반으로 타입의 값 배열을 정렬하는 sorted(by:)라는 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;정렬 메소드&lt;/span&gt;&lt;/b&gt;를 제공합니다. 정렬 프로세스가 완료되면 sorted(by:) 메소드는 원본 배열과 같은 타입과 같은 크기의 올바르게 정렬된 새로운 배열을 반환합니다. 기존 배열은 sorted(by:) 메소드로 수정되지 않습니다.&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;아래 예시들의 클로저 표현식은 알파벳 역순으로 String 값의 배열을 정렬하기 위해 sorted(by:) 메소드를 사용합니다. 아래 배열은 정렬하기 전의 배열입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744029446062&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let names = [&quot;Chris&quot;, &quot;Alex&quot;, &quot;Ewa&quot;, &quot;Barry&quot;, &quot;Daniella&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;sorted(by:) 메소드는 값이 정렬된 후 첫번째 값이 두번째 값의 앞 또는 뒤에 표시되어야 하는지 여부를 나타내는 Bool 값을 반환합니다. 정렬 클로저는 첫번째 값이 두번째 값 앞에 나타나야 하는 경우 true를 반환하고 그렇지 않으면 false를 반환합니다.&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;아래 예시는 String 값의 배열을 정렬하고 정렬 클로저는 (String, String) -&amp;gt; Bool 타입의 함수를 필요로 합니다.&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;정렬 클로저를 제공하는 한가지 방법은 올바른 타입의 일반 함수를 작성하고 sorted(by:) 메소드에 인수로 전달하는 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744029759032&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func backward(_ s1: String, _ s2: String) -&amp;gt; Bool {
    return s1 &amp;gt; s2
}
var reversedNames = names.sorted(by: backward)
// reversedNames is equal to [&quot;Ewa&quot;, &quot;Daniella&quot;, &quot;Chris&quot;, &quot;Barry&quot;, &quot;Alex&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;위 예시에서 backward(_:_:) 함수는 첫번째 문자열 (s1)이 두번째 문자열(s2)보다 크다면 true를 반환합니다. 문자열의 문자가 &quot;더 크다&quot;는 &quot;알파벳 순으로 더 뒤에 나타난다&quot;는 의미입니다. 이것은 문자 B는 문자 A보다 &quot;더 크다&quot;이고 문자열 &quot;Tom&quot;은 문자열 &quot;Tim&quot;보다 더 큽니다.&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;그러나 위 예시는 단일 표현식 함수 (a &amp;gt; b)를 작성하는 방법 중 다소 긴 방식입니다. 이 예시에서는 클로저 표현식 구문을 사용하여 정렬 클로저를 인라인으로 작성하는 것이 좋습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;클로저 표현구 (Closure Expresssion Syntax)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;클로저 표현구&lt;/span&gt;&lt;/b&gt;는 아래와 같은 형태를 가지고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744458263091&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{ (&amp;lt;#parameters#&amp;gt;) -&amp;gt; &amp;lt;#return type#&amp;gt; in
   &amp;lt;#statements#&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;클로저 표현구의 파라미터는 in-out 파라미터일 수도 있지만, 기본값을 가질 수 없습니다. 가변 파라미터의 이름을 지정하면 가변 파라미터를 사용할 수 있습니다. 튜플은 파라미터 타입과 반환 타입으로 사용될 수도 있습니다.&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;아래 예시는 위에서 backward(_:_:) 함수의 클로저 표현 버전입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744458359847&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;reversedNames = names.sorted(by: { (s1: String, s2: String) -&amp;gt; Bool in
    return s1 &amp;gt; s2
})&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;이 인라인 클로저의 파라미터와 반환 타입은 위의 backward(_:_:) 함수에서 선언한 것과 동일합니다. 두 경우는 모두 (s1: String, s2: String) -&amp;gt; Bool로 작성합니다. 그러나 인라인 클로저 표현식을 위한 파라미터와 반환 타입은 중괄호 바깥이 아닌 안에 작성합니다.&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;클로저 본문의 시작은 in 키워드로 시작합니다. 이 키워드는 클로저의 파라미터와 리턴 타입 정의가 끝났음을 나타내며 클로저의 본문이 시작함을 나타냅니다.&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;pre id=&quot;code_1744458808929&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;reversedNames = names.sorted(by: { (s1: String, s2: String) -&amp;gt; Bool in return s1 &amp;gt; s2 } )&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;이는 sorted(by:) 메소드에 대한 전체 호출이 동일하게 유지되었음을 보여줍니다. 소괄호는 여전히 메소드의 전체 인자를 둘러싸고 있지만, 인자는 인라인 클로저입니다.&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;b&gt;컨텍스트로 타입 추론 (Inferring Type From Context)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬 클로저는 메소드에 인수로 전달되기 때문에 Swift는 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;파라미터 타입과 반환되는 값의 타입을 추론&lt;/span&gt;&lt;/b&gt;할 수 있습니다. sorted(by:) 메소드는 문자열 배열에서 호출되므로 인수는 (String, String) -&amp;gt; Bool 타입의 함수여야 합니다. 이는 (String, String)과 Bool 타입을 클로저 표현식 정의에 일부러 작성할 필요가 없음을 의미합니다. 모든 타입은 유추할 수 있기 때문에 반환 화살표 (-&amp;gt;)와 파라미터의 이름을 둘러싼 소괄호를 생략할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744459317823&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;reversedNames = names.sorted(by: { s1, s2 in return s1 &amp;gt; s2 } )&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수나 메서드에 클로저를 인라인 클로저 표현식으로 전달할 때 항상 파라미터 타입과 반환 타입을 유추할 수 있으므로, 결과적으로 클로저가 함수 또는 메소드를 인자로 사용될 때 완전한 형태의 인라인 클로저를 작성할 필요가 없습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;후행 클로저 (Trailing Closures)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수의 마지막 인수로 함수에 클로저 표현식을 전달해야하고 클로저 표현식이 긴 경우 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;후행 클로저 (trailing closure)&lt;/span&gt;&lt;/b&gt;로 작성하는 것이 유용할 수 있습니다. 후행 클로저는 함수의 인수이지만 함수 호출의 소괄호 다음에 작성합니다. 후행 클로저 구문을 사용할 때 함수 호출 시 클로저 인수 라벨을 작성하지 않아도 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744460597040&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func someFunctionThatTakesAClosure(closure: () -&amp;gt; Void) {
    // function body goes here
}


// Here's how you call this function without using a trailing closure:


someFunctionThatTakesAClosure(closure: {
    // closure's body goes here
})


// Here's how you call this function with a trailing closure instead:


someFunctionThatTakesAClosure() {
    // trailing closure's body goes here
}&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;위의 클로저 표현구 섹션의 문자열 정렬 클로저는 후행 클로저로 sorted(by:) 메소드의 소괄호 바깥에 작성될 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744460752420&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;reversedNames = names.sorted() { $0 &amp;gt; $1 }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;후행 클로저로 표현식이 함수와 메소드의 유일한 인수일 경우 함수를 호출할 때 함수 또는 메소드 이름 뒤에 소괄호 ()를 작성하지 안하도 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744462148525&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;reversedNames = names.sorted { $0 &amp;gt; $1 }&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;후행 클로저는 클로저가 길어서 한줄로 인라인으로 작성이 불가능할 때 유용합니다. 예를 들어 Swift의 Array 타입은 단일 인수로 클로저 표현식을 가지는 map(_:) 메소드가 있습니다. 이 클로저는 배열의 각 아이템에 대해 한번 호출되고 아이템에 대해 매핑된 대체값 (다른 타입일 수 있음)이 반환됩니다. map(_:) 에 전달한 클로저에 작성된 코드에 따라 매핑 특성과 반환된 값의 타입을 지정합니다.&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;제공된 클로저에 각 배열의 요소를 적용한 후에 map(_:) 메소드는 기존 배열에 해당값과 같은 순서로 새로 매핑된 값의 새로운 배열을 반환합니다.&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;아래 예시는 Int 값의 배열을 String 값의 배열로 변환하기 위해 후행 클로저와 map(_:) 메소드를 어떻게 사용하는지 보여줍니다. 배열 [16, 58, 510]은 새로운 배열 [&quot;OneSix&quot;, &quot;FiveEight&quot;, &quot;FiveOneZero&quot;]을 생성하는데 사용됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744462491014&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let digitNames = [
    0: &quot;Zero&quot;, 1: &quot;One&quot;, 2: &quot;Two&quot;,   3: &quot;Three&quot;, 4: &quot;Four&quot;,
    5: &quot;Five&quot;, 6: &quot;Six&quot;, 7: &quot;Seven&quot;, 8: &quot;Eight&quot;, 9: &quot;Nine&quot;
]
let numbers = [16, 58, 510]&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;위 코드는 정수와 그 정수에 맞는 영어 표기를 매핑하는 딕셔너리를 생성합니다. 문자열로 변환하기 위한 정수으 배열을 의미합니다. numbers 배열을 사용하여 후행 클로저로 map(_:) 메소드로 클로저 표현식을 전달하여 String 값의 배열을 생성할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744462580335&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let strings = numbers.map { (number) -&amp;gt; String in
    var number = number
    var output = &quot;&quot;
    repeat {
        output = digitNames[number % 10]! + output
        number /= 10
    } while number &amp;gt; 0
    return output
}
// strings is inferred to be of type [String]
// its value is [&quot;OneSix&quot;, &quot;FiveEight&quot;, &quot;FiveOneZero&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;map(_:) 메소드는 배열에 각 아이템을 위한 클로저 표현식을 호출합니다. 매핑할 배열의 값에서 유추할 수 있으므로 클로저의 입력 파라미터인 number 타입을 지정할 필요가 없습니다.&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;이 예시에서 변수 number는 클로저의 number 파라미터의 값으로 초기화되기 때문에 값은 클로저 본문 내에서 수정될 수 있습니다. 클로저 표현식은 출력 매핑된 출력 배열에 저장될 타입을 나타내기 위해 String 타입도 변환 타입을 지정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클로저 표현식은 호출될 때마다 output 이라는 문자열을 만듭니다. 나머지 연산자(number % 10)를 이용하여 number의 마지막 숫자를 계산하고 digitNames 딕셔너리에 적절한 숫자 문자열을 찾습니다. 클로저는 0보다 큰 정수에 대한 문자열 표현을 생성하는데 사용할 수 있습니다.&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;위의 예시처럼 후행 클로저 구문을 사용하면 클로저가 지원하는 함수 바로 뒤에 있는 클로저의 기능을 깔끔하게 캡슐화 합니다. 전체 클로저를 map(_:) 메소드의 바깥 소괄호로 감쌀 필요가 없습니다.&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;pre id=&quot;code_1744463038682&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func loadPicture(from server: Server, completion: (Picture) -&amp;gt; Void, onFailure: () -&amp;gt; Void) {
    if let picture = download(&quot;photo.jpg&quot;, from: server) {
        completion(picture)
    } else {
        onFailure()
    }
}&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;하나의 사진을 불러오기 위해 이 함수를 호출할 때 2개의 클로저를 제공합니다. 첫번째 클로저는 사진 다운로드 완료후에 사진을 보여주기 위한 완료 처리입니다. 두번째 클로저는 오류를 표시하는 오류 처리기 입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744463111492&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;loadPicture(from: someServer) { picture in
    someView.currentPicture = picture
} onFailure: {
    print(&quot;Couldn't download the next picture.&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;이러한 방식으로 함수를 작성하면 두 상황을 모두 처리하는 하나의 클로저를 사용하는 대신에, 다운로드 성공 후 사용자 인터페이스를 업데이트 하는 코드와 네트워크 오류를 처리하는 코드를 명확하게 분리할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;캡처값 (Capturing Values)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클로저는 정의된 둘러싸인 컨텍스트에서 상수와 변수를 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;캡처 (capture)&lt;/span&gt;&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;Swift에서 값을 캡쳐할 수 있는 가장 간단한 클로저 형태는 다른 함수의 본문 내에 작성하는 중첩 함수입니다. 중첩 함수는 바깥 함수의 어떤 인수든 캡처할 수 있고 바깥 함수에서 정의된 상수와 변수를 캡쳐할 수도 있습니다.&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;아래는 incrementer 라는 중첩 함수가 포함된 makeIncrementer 라는 함수의 예시입니다. incrementer() 중첩 함수는 둘러싸인 컨텍스트에 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;runningTotal과 amount 2개의 값을 캡처&lt;/span&gt;&lt;/b&gt;합니다. 이 값을 캡처한 후에 incrementer는 호출될 때마다 amount로 runningTotal을 증가시키는 클로저로 makeIncrementer에 의해 반환됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744463972366&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func makeIncrementer(forIncrement amount: Int) -&amp;gt; () -&amp;gt; Int {
    var runningTotal = 0
    func incrementer() -&amp;gt; Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementer
}&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;makeIncrementer의 반환 타입은 () -&amp;gt; Int 입니다. 이는 값이 아닌 함수를 반환한다는 의미입니다. 반환하는 함수에는 파라미터가 없으며 호출될 때마다 Int 값을 반환합니다.&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;makeIncrementer(forIncrement:) 함수는 반환될 현재 증가분을 저장하기 위해 runningTotal 이라는 정수 변수를 정의합니다. 이 변수는 0으로 초기화합니다.&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;makeIncrementer(forIncrement:) 함수는 forIncrement 인수 라벨의 하나의 Int 파라미터 amonut 라는 이름의 파라미터를 가지고 있습니다. 이 파라미터에 전달된 인수값은 반환된 증가 함수가 호출 될 때마다 runningTotal을 얼마나 증가시켜야 하는지를 의미합니다. makeIncrementer 함수는 실제 증가를 수행하는 incrementer 라는 중첩 함수를 정의합니다. 이 함수는 간단하게 amount 를 runningTotal에 더하고 그 결과를 반환합니다.&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;incrementer() 함수는 단독으로 사용된다고 생각하면 비정상적으로 보일 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744464052495&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func incrementer() -&amp;gt; Int {
    runningTotal += amount
    return runningTotal
}&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;왜냐하면, incrementer() 함수는 함수 본문 내의 runningTotal과 amount를 참조하고 있기 때문에 파라미터가 없습니다. 둘러싸인 함수에 runningTotal과 ammount에 대한 참조(reference)를 캡처하고 함수 내에서 사용합니다. 참조를 캡처하는 것 덕분에 makeIncrementer 호출이 종료될 때 runningTotal과 amount가 사라지지 않고 다음에 incrementer 함수가 호출될 때 runningTotal을 사용할 수 있습니다.&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;아래는 makeIncrementer 동작의 예시입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744505825642&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let incrementByTen = makeIncrementer(forIncrement: 10)&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;이 예시에서는 호출될 때마다 runningTotal 변수에 10을 더하는 증가 함수를 참조하도록 incrementByTen 이라는 상수를 설정했습니다. 함수를 여러번 호출될 때 마다 runningTotal이 증가되는 동작을 볼 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744506020215&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;incrementByTen()
// returns a value of 10
incrementByTen()
// returns a value of 20
incrementByTen()
// returns a value of 30&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;&lt;b&gt;클로저는 참조 타입이다 (Closures Are Reference Types)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서 incrementByTen은 상수이지만 이러한 상수가 참조하는 클로저는 캡처한 runningTotal 변수를 계속 증가시킬 수 있습니다. 이는 함수와 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;클로저가 참조 타입 (reference types)&lt;/span&gt;&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;함수 또는 클로저를 상수 또는 변수에 할당할 때마다 실제로 해당 상수 또는 변수를 함수 또는 클로저에 대한 참조로 설정합니다. 위의 예시에서 incrementByTen은 클로저 자체의 내용이 아니라 상수를 가리키는 클로저입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 또한 서로 다른 2개의 상수 또는 변수에 클로저를 할당한다면 2개의 상수 또는 변수는 모두 같은 클로저를 참조한다는 의미입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1744514269349&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let alsoIncrementByTen = incrementByTen
alsoIncrementByTen()
// returns a value of 50


incrementByTen()
// returns a value of 60&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;위의 예제는 alsoIncrementByTen 호출은 incrementByTen 호출과 같음을 의미합니다. 2개 모두 같은 클로저를 참조하기 때문에 둘다 똑같이 runningTotal을 증가시키고 반환합니다.&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;탈출 클로저 (Escaping Closures)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수의 인수로 클로저를 전달하지만 함수가 반환된 후 호출되는 클로저를 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;함수 탈출 (escape)&lt;/b&gt;&lt;/span&gt; 이라고 말합니다. 클로저를 파라미터로 갖는 함수를 선언할 때 이 클로저는 탈출을 허락한다는 의미로 파라미터의 타입 전에 @escaping 을 작성할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클로저가 탈출할 수 있는 한가지 방법은 함수 바깥에 정의된 변수에 저장하는 것입니다. 예를 들어, 비동기 작업을 시작하는 대분의 함수는 완료 핸들러로 클로저를 사용합니다. 이 함수는 작업을 시작한 후에 반환되지만, 작업이 완료될때까지 클로저가 호출되지 않습니다. 클로저가 호출되기 위해서는 먼저 탈출해야 합니다. 예를 들면 아래와 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744517357447&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var completionHandlers: [() -&amp;gt; Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -&amp;gt; Void) {
    completionHandlers.append(completionHandler)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;someFunctionWithEscapingClosure(_:) 함수는 매개변수로 클로저를 가지고 있고 함수 바깥에 선언된 배열을 추가합니다. 함수의 파라미터에 @escaping 을 표시하지 않으면 컴파일 시 에러가 발생합니다.&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;self 를 참조하는 이스케이프 클로저는 self 가 클래스의 인스턴스를 참조하는 경우 구현에 대한 추가적인 고민이 필요합니다. 이스케이프 클로저에 self&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;일반적으로 클로저는 클로저 내부에서 변수를 사용하여 암시적으로 변수를 캡처하지만 이 경우에는 명시적으로 캡처해야 합니다. self를 캡처하려면 사용할 때 명시적으로 self를 작성하거나 클로저의 캡처 리스트에 self를 포함해야 합니다. self를 명시적으로 작성해서 의도를 표현하고 참조 사이클이 없음을 확인시켜주어야 합니다.&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;예를 들어, 아래 코드에서 someFunctionWithEscapingClosure(_:)에 전달된 클로저는 명시적으로 self를 참조합니다. 반대로 someFunctionWithNonescapingClosure(_:)에 전달된 클로저는 바이스케이프 클로저입니다. 즉, 암시적으로 self를 참조할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744520395350&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func someFunctionWithEscapingClosure(completionHandler: @escaping () -&amp;gt; Void) {
    completionHandlers.append(completionHandler)
}

func someFunctionWithNonescapingClosure(closure: () -&amp;gt; Void) {
    closure()
}


class SomeClass {
    var x = 10
    func doSomething() {
        someFunctionWithEscapingClosure { self.x = 100 }
        someFunctionWithNonescapingClosure { x = 200 }
    }
}


let instance = SomeClass()
instance.doSomething()
print(instance.x)
// Prints &quot;200&quot;


completionHandlers.first?()
print(instance.x)
// Prints &quot;100&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;아래의 doSomething() 메소드는 클로저의 캡처 리스트에 self를 캡처하고 암시적으로 self를 참조합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744520559965&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class SomeOtherClass {
    var x = 10
    func doSomething() {
        someFunctionWithEscapingClosure { [self] in x = 100 }
        someFunctionWithNonescapingClosure { x = 200 }
    }
}&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;self가 구조체 또는 열거형 인스턴스이면 항상 암시적으로 self를 참조할 수 있습니다. 하지만, 이스케이프 클로저는 구조체 또는 열거형 인스턴스이면 self에 대한 변경 가능한 참조를 캡처할 수 없습니다. 구조체와 열거형은 공유 변경을 허용하지 않습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744520939932&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct SomeStruct {
    var x = 10
    mutating func doSomething() {
        someFunctionWithNonescapingClosure { x = 200 }  // Ok
        someFunctionWithEscapingClosure { x = 100 }     // Error
    }
}&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;위의 예제에서 someFunctionWithEscapingClosure 함수 호출은 변경 가능한 메소드 내부에 있기 때문에 에러이고, self는 변경 가능합니다. 이는 이스케이프 클로저는 구조체인 self를 변경가능한 참조로 캡처할 수 없다는 규칙을 위반합니다.&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;자동 클로저 (Autoclosures)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;자동 클로저 (autoclosure)&lt;/span&gt;&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;자동 클로저를 가지는 함수를 호출하는 것은 일반적이지만 이러한 함수를 구현하는 것은 일반적이지 않습니다. 예를 들어, assert(condition:message:file:line:) 함수는 condition과 message 파라미터에 대한 자동 클로저를 가집니다. condition 파라미터는 오직 디버그 빌드인지 판단하고 message 파라미터는 condition이 false 인지만 판단합니다.&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;pre id=&quot;code_1744522959937&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var customersInLine = [&quot;Chris&quot;, &quot;Alex&quot;, &quot;Ewa&quot;, &quot;Barry&quot;, &quot;Daniella&quot;]
print(customersInLine.count)
// Prints &quot;5&quot;


let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)
// Prints &quot;5&quot;


print(&quot;Now serving \(customerProvider())!&quot;)
// Prints &quot;Now serving Chris!&quot;
print(customersInLine.count)
// Prints &quot;4&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;클로저 내부의 코드에 의해 customersIsLine 배열의 첫번째 요소는 삭제되지만 클로저가 실제로 호출되기 전까지 삭제되지 않습니다. 클로저가 호출되지 않으면 클로저 내부의 표현식은 판단되지 않습니다. 이것은 배열의 요소가 삭제되지 않는다는 의미입니다. customerProvider 타입은 String이 아니고 파라미터가 없고 문자열을 반환하는 () -&amp;gt; String 입니다.&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;pre id=&quot;code_1744523269157&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// customersInLine is [&quot;Alex&quot;, &quot;Ewa&quot;, &quot;Barry&quot;, &quot;Daniella&quot;]
func serve(customer customerProvider: () -&amp;gt; String) {
    print(&quot;Now serving \(customerProvider())!&quot;)
}
serve(customer: { customersInLine.remove(at: 0) } )
// Prints &quot;Now serving Alex!&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;serve(customer:) 함수는 소비자의 이름을 반환하는 명시적 클로저를 가집니다. 아래 serve(customer:)의 버전은 같은 동작을 수행하지만 명시적 클로저를 가지는 대신에 파라미터 타입 @autoclosure 속성을 표기하여 자동 클로저를 가집니다. 이제 클로저 대신 String 인수를 받는 것처럼 함수를 호출할 수 있습니다. customerProvider 파라미터 타입은 @autoclosure 속성으로 표시되는 인수는 자동으로 클로저로 변환됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744523436442&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// customersInLine is [&quot;Ewa&quot;, &quot;Barry&quot;, &quot;Daniella&quot;]
func serve(customer customerProvider: @autoclosure () -&amp;gt; String) {
    print(&quot;Now serving \(customerProvider())!&quot;)
}
serve(customer: customersInLine.remove(at: 0))
// Prints &quot;Now serving Ewa!&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;자동 클로저가 이스케이프를 허용하길 원한다면 @autoclosure와 @escaping 속성을 둘 다 사용하면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744523558169&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// customersInLine is [&quot;Barry&quot;, &quot;Daniella&quot;]
var customerProviders: [() -&amp;gt; String] = []
func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -&amp;gt; String) {
    customerProviders.append(customerProvider)
}
collectCustomerProviders(customersInLine.remove(at: 0))
collectCustomerProviders(customersInLine.remove(at: 0))


print(&quot;Collected \(customerProviders.count) closures.&quot;)
// Prints &quot;Collected 2 closures.&quot;
for customerProvider in customerProviders {
    print(&quot;Now serving \(customerProvider())!&quot;)
}
// Prints &quot;Now serving Barry!&quot;
// Prints &quot;Now serving Daniella!&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;위의 코드에서 customerProvider 인수로 전달된 클로저를 호출하는 대신에 collectCustomerProviders(_:) 함수는 클로저를 customerProviders 배열에 추가합니다. 이 배열은 함수의 범위 밖에 선언됩니다. 이는 배열 클로저는 함수가 반환된 후에야 실행될 수 있다는 의미입니다. 그 결과 customerProvider 인수의 값은 함수의 범위를 벗어날 수 있어야 합니다.&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;b&gt;References&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1742730910687&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;Documentation&quot; data-og-description=&quot;&quot; data-og-host=&quot;docs.swift.org&quot; data-og-source-url=&quot;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/&quot; data-og-url=&quot;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/&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;Documentation&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;docs.swift.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>iOS/Swift</category>
      <category>Swift</category>
      <author>seunghwaan</author>
      <guid isPermaLink="true">https://seosh817.tistory.com/692</guid>
      <comments>https://seosh817.tistory.com/692#entry692comment</comments>
      <pubDate>Mon, 7 Apr 2025 20:20:36 +0900</pubDate>
    </item>
    <item>
      <title>[iOS/Swift] 함수 (Functions)</title>
      <link>https://seosh817.tistory.com/691</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;[ 이 게시물은 Swift.org를 참고하며 Swift를 공부하기 위해 작성하는 글 입니다. ]&lt;/b&gt;&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;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;함수 (Functions)&lt;/b&gt;&lt;/span&gt;는 특정 작업을 수행하는 코드 모음입니다. Swift의 통합 함수 구문은 파라미터 이름이 없는 단순한 C 스타일 함수에서 각 파라미터에 대한 이름과 인수가 있는 복잡한 Objective-C 스타일까지 모든 것을 표현할 수 있을 만큼 유연합니다. 파라미터는 함수 호출을 단순화하기 위해 기본 값을 제공하며, 함수가 실행을 완료하면 전달된 변수를 수정할 수 있는 in-out 파라미터를 전달할 수 있습니다.&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;Swift의 함수에는 함수 파라미터 타입과 반환 타입으로 구성된 타입이 있습니다. Swift의 다른 타입과 마찬가지로 이 타입을 사용할 수 있으므로 함수를 파라미터로 다른 함수에 전달하고 함수에서 함수를 반환할 수 있습니다. 함수는 중첩된 함수 범위내에서 유용한 기능을 캡슐화하기 위해 다른 함수 내에 작성될 수 도 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;함수 정의와 호출 (Defining and Calling Functions)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수를 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;정의&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;모든 함수는 함수가 수행하는 작업을 설명하는 함수 이름을 가지고 있습니다. 함수를 사용하려면 함수의 이름으로 호출(Call)할 때, 함수의 파라미터와 일치하는 인수를 입력 값을 전달해야 합니다. 함수의 인수는 항상 함수의 파라미터 순서와 동일하도록 정의해야 합니다.&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;아래 예시의 greet(person:) 함수는 사람의 이름을 입력으로 받아 그 사람의 인사말을 반환합니다. 이 함수는 String 타입인 person 값과 String 타입의 인사말에 대한 반환 타입을 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743929782303&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func greet(person: String) -&amp;gt; String {
    let greeting = &quot;Hello, &quot; + person + &quot;!&quot;
    return greeting
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수를 정의할 때는 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;func 키워드&lt;/b&gt;&lt;/span&gt;를 앞에 붙여 함수를 정의합니다. 또한, 반환 화살표 -&amp;gt; 뒤에 반환 타입을 붙여 함수의 반환 타입을 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743929928850&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;print(greet(person: &quot;Anna&quot;))
// Prints &quot;Hello, Anna!&quot;
print(greet(person: &quot;Brian&quot;))
// Prints &quot;Hello, Brian!&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;함수 파라미터와 반환 값 (Function Parameters and Return Values)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift에서는 함수 파라미터와 반환 값을 유연하게 사용할 수 있습니다. 이름이 지정되지 않은 단일 파라미터가 있는 간단한 유틸리티 함수에서 파라미터 이름과 다른 파라미터 옵션이 있는 복잡한 함수에 이르기까지 모든 것을 정의할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;파라미터 없는 함수 (Functions Without Parameters)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드처럼 파라미터가 없는 함수를 정의할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743935861652&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func sayHelloWorld() -&amp;gt; String {
    return &quot;hello, world&quot;
}
print(sayHelloWorld())
// Prints &quot;hello, world&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;여러개 파라미터가 있는 함수 (Functions With Multiple Parameters)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드처럼 여러개의 파라미터가 있는 함수를 정의할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743935954018&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func greet(person: String, alreadyGreeted: Bool) -&amp;gt; String {
    if alreadyGreeted {
        return greetAgain(person: person)
    } else {
        return greet(person: person)
    }
}
print(greet(person: &quot;Tim&quot;, alreadyGreeted: true))
// Prints &quot;Hello again, Tim!&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;위 함수는 person, alreadyGreeted라는 두개의 파라미터를 가집니다.&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;반환 값 없는 함수 (Functions Without Returns Values)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드처럼 반환 타입이 없는 함수를 정의할 수 있습니다. 반환값이 필요없기 때문에 반환 화살표 (-&amp;gt;)와 반환 타입을 정의할 필요가 없습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743936207244&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func greet(person: String) {
    print(&quot;Hello, \(person)!&quot;)
}
greet(person: &quot;Dave&quot;)
// Prints &quot;Hello, Dave!&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;아래 코드에서 첫번째 함수 printAndCount(String:)은 문자열을 출력하고 문자 갯수를 Int로 반환합니다. 두번째 함수 printWithoutCount(string:)은 첫번째 함수를 호출하지만 반환값을 무시합니다. 두번째 함수가 호출될 때 첫번째 함수에 의해 메세지는 출력되지만 반환된 값은 사용하지 않습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743936518794&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func printAndCount(string: String) -&amp;gt; Int {
    print(string)
    return string.count
}
func printWithoutCounting(string: String) {
    let _ = printAndCount(string: string)
}
printAndCount(string: &quot;hello, world&quot;)
// prints &quot;hello, world&quot; and returns a value of 12
printWithoutCounting(string: &quot;hello, world&quot;)
// prints &quot;hello, world&quot; but does not return a value&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;&lt;b&gt;여러개의 반환값이 있는 함수 (Functions with Multiple Return Values)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수가 여러개의 값을 반환하기 위해 함수의 반환 타입으로 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;튜플 타입&lt;/span&gt;&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;아래 예시는 Int 타입의 배열에서 가장 작은값과 가장 큰 값을 찾는 minMax(array:) 함수를 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743937077628&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func minMax(array: [Int]) -&amp;gt; (min: Int, max: Int) {
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1..&amp;lt;array.count] {
        if value &amp;lt; currentMin {
            currentMin = value
        } else if value &amp;gt; currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}&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;minMax(array:) 함수는 2개의 Int 값을 가진 튜플을 반환합니다. 이 값들은 함수의 반환 값을 조회할 때 이름으로 접근할 수 있도록 min과 Max로 라벨되어 있습니다.&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;pre id=&quot;code_1743944743985&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let bounds = minMax(array: [8, -6, 2, 109, 3, 71])
print(&quot;min is \(bounds.min) and max is \(bounds.max)&quot;)
// Prints &quot;min is -6 and max is 109&quot;&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;&lt;b&gt;옵셔널 튜플 반환 타입 (Optional Tuple Return Types)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수에서 반환되는 튜플 타입이 전체 튜플에 대해 &quot;값이 없을&quot; 가능성이 있는 경우 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;옵셔널 튜플 반환 타입&lt;/b&gt;&lt;/span&gt;을 사용하여 전체 튜플이 nil 일 수 있다는 사실을 반영할 수 있습니다. (Int, Int)? 또는 (String, Int, Bool)?와 같이 튜플 타입의 닫는 소괄호 다음에 물음표를 붙여 옵셔널 튜플 반환 타입을 정의합니다.&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;위에서 minMax(array:) 함수는 2개의 Int 값을 포함하는 튜플을 반환합니다. 그러나 이 함수는 배열이 전달될 때 아무런 안정성을 확인하지 않습니다. array 인수가 빈 배열을 포함하면 위에서 정의된 minMax(array:) 함수는 array[0]을 접근할 때 런타임 에러가 발생합니다.&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;빈 배열을 안전하게 처리하기 위해선 minMax(array:) 함수는 옵셔널 튜플 반환 타입을 가지고 배열이 비어있을 때 nil을 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1743945235834&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func minMax(array: [Int]) -&amp;gt; (min: Int, max: Int)? {
    if array.isEmpty { return nil }
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1..&amp;lt;array.count] {
        if value &amp;lt; currentMin {
            currentMin = value
        } else if value &amp;gt; currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}&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;옵셔널 바인딩을 통해 minMax(array:) 함수가 실제 튜플 값을 반환하는지 nil을 반환하는지 확인할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743945705016&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) {
    print(&quot;min is \(bounds.min) and max is \(bounds.max)&quot;)
}
// Prints &quot;min is -6 and max is 109&quot;&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;&lt;b&gt;암시적 반환을 가진 함수 (Functions With an Implicit Return)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수의 전체 본문이 한줄로 표현이 된다면 이 함수는 맹목적으로 해당 표현식을 반환합니다. 예를 들면 아래의 두 함수는 모두 같은 동작을 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743945922537&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func greeting(for person: String) -&amp;gt; String {
    &quot;Hello, &quot; + person + &quot;!&quot;
}
print(greeting(for: &quot;Dave&quot;))
// Prints &quot;Hello, Dave!&quot;


func anotherGreeting(for person: String) -&amp;gt; String {
    return &quot;Hello, &quot; + person + &quot;!&quot;
}
print(anotherGreeting(for: &quot;Dave&quot;))
// Prints &quot;Hello, Dave!&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;위의 greeting(for:) 함수는 anotherGreeting(for:) 함수와 동일한 동작을 하지만, return 키워드를 생략할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;함수 인수 라벨과 파라미터 이름 (Function Argument Labels and Parameter Names)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 함수 파라미터는 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;인수 라벨(argument label)&lt;/span&gt;&lt;/b&gt;과 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;파라미터 이름(parameter name)&lt;/span&gt;&lt;/b&gt;을 가지고 있습니다. 인수 라벨은 함수가 호출될 때 사용되고 각 인수는 함수 호출 시 인수 라벨 다음에 작성합니다. 파라미터 이름은 함수를 구현할 때 사용됩니다. 기본적으로 파라미터는 인수 라벨로 파라미터 이름을 사용합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743946465450&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func someFunction(firstParameterName: Int, secondParameterName: Int) {
    // In the function body, firstParameterName and secondParameterName
    // refer to the argument values for the first and second parameters.
}
someFunction(firstParameterName: 1, secondParameterName: 2)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 파라미터는 유니크한 이름을 가져야 합니다. 여러 파라미터에 동일한 인수 라벨을 가질수도 있지만 유니크한 인수 라벨을 붙여야 더 읽기 편한 코드로 작성할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;인수 라벨 지정 (Specifying Argument Labels)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공백으로 구분하여 파라미터 이름 앞에 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;인수 라벨&lt;/span&gt;&lt;/b&gt;을 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743946590347&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func someFunction(argumentLabel parameterName: Int) {
    // In the function body, parameterName refers to the argument value
    // for that parameter.
}&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;다음은 사람의 이름과 고향을 가져와 인사말을 반환하는 greet(person:) 함수의 변형입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743946623832&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func greet(person: String, from hometown: String) -&amp;gt; String {
    return &quot;Hello \(person)!  Glad you could visit from \(hometown).&quot;
}
print(greet(person: &quot;Bill&quot;, from: &quot;Cupertino&quot;))
// Prints &quot;Hello Bill!  Glad you could visit from Cupertino.&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;인수 라벨을 사용하면 문장과 같은 표현방식으로 함수를 호출할 수 있는 동시에 읽기 쉽고 의도가 명확한 함수 본문을 제공할 수 있습니다.&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;인수 라벨 생략 (Omitting Argument Labels)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파라미터에 인수 라벨을 원치 않으면 명시적인 인수 라벨 대신에 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;언더바(_)&lt;/span&gt;&lt;/b&gt;를 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743946730896&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func someFunction(_ firstParameterName: Int, secondParameterName: Int) {
    // In the function body, firstParameterName and secondParameterName
    // refer to the argument values for the first and second parameters.
}
someFunction(1, secondParameterName: 2)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파라미터가 인수 라벨을 가지고 있다면 함수를 호출할 때 인수는 반드시 라벨을 지정해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;파라미터 기본값 (Default Parameter Values)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파라미터의 타입 뒤에 파라미터 값을 할당하여 함수의 파라미터에 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;기본값(default value)&lt;/span&gt;&lt;/b&gt;을 정의할 수 있습니다. 기본값이 정의되어 있다면 함수를 호출할 때 파라미터를 생략할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743946818204&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) {
    // If you omit the second argument when calling this function, then
    // the value of parameterWithDefault is 12 inside the function body.
}
someFunction(parameterWithoutDefault: 3, parameterWithDefault: 6) // parameterWithDefault is 6
someFunction(parameterWithoutDefault: 4) // parameterWithDefault is 12&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본값이 없는 파라미터는 함수의 파라미터 리스트 시작부분에 위치하고 기본값이 있는 파라미터 전에 위치합니다. 기본 값이 없는 파라미터는 일반적으로 함수의 의미적으로 더 중요합니다. 이를 먼저 작성하면 기본 파라미터가 생략되었는지 여부에 관계없이 동일한 함수가 호출되고 있음을 쉽게 인식할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;가변 파라미터 (Variadic Parameter)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;가변 파라미터(variadic parameter)&lt;/span&gt;&lt;/b&gt;는 0개 이상의 특정 타입의 값을 허용합니다. 함수가 호출될 때 여러개의 입력값이 전달될 수 있는 특정 파라미터는 가변 파라미터를 사용합니다. 가변 파라미터는 파라미터의 타입 이름 뒤에 세개의 간격 문자(...)를 추가하여 작성합니다.&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;가변 파라미터에 전달된 값은 함수 본문 내에서 적절한 타입의 배열로 사용할 수 있습니다. 예를 들어 numbers 라는 이름과 Double... 타입을 가진 가변 파라미터는 함수 본문 내에서 [Double] 타입의 numbers라 불리는 상수 배열로 사용할 수 있습니다.&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;pre id=&quot;code_1743947018263&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func arithmeticMean(_ numbers: Double...) -&amp;gt; Double {
    var total: Double = 0
    for number in numbers {
        total += number
    }
    return total / Double(numbers.count)
}
arithmeticMean(1, 2, 3, 4, 5)
// returns 3.0, which is the arithmetic mean of these five numbers
arithmeticMean(3, 8.25, 18.75)
// returns 10.0, which is the arithmetic mean of these three numbers&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수는 여러개의 가변 파라미터를 가질 수 있습니다. 가변 파라미터 뒤에 오는 첫번째 파라미터는 인수 라벨을 가지고 있어야 합니다. 인수 라벨은 가변 파라미터에 전달되는 인수와 가변 파라미터 뒤에 오는 파라미터에 전달되는 인수를 명확하게 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;In-Out 파라미터 (In-Out Parameters)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 파라미터는 기본적으로 상수입니다. 해당 함수의 본문 내에서 함수 파라미터 값을 변경하려고 하면 컴파일 타입 에러 발생합니다. 이것은 실수로 파라미터의 값을 변경할 수 없다는 것을 의미합니다. 함수의 파라미터 값을 변경하고 함수 호출이 종료된 후에도 이러한 변경된 값을 유지하고 싶다면 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;in-out 파라미터 (in-out paremeter)&lt;/span&gt;&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;in-out 파라미터를 사용하려면 파라미터의 타입 바로 전에 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;inout 키워드&lt;/span&gt;&lt;/b&gt;를 작성하면 됩니다. in-out 파라미터는 함수로 전달하는 값을 가지고 있고 함수로 부터 이 값을 수정하고 원래 값을 대체하기 위해 함수 밖으로 다시 되돌려 줍니다.&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;in-out 파라미터의 인수로 변수만 전달할 수 있습니다. 상수와 반복은 수정할 수 없기 때문에 인수로 상수 또는 반복 값은 전달할 수 없습니다. 함수에 수정가능함을 알리기 위해 in-out 파라미터에 인수로 전달할 때 변수의 이름 앞에 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;앰퍼샌드(&amp;amp;)&lt;/span&gt;&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;아래 예시의 함수는 a와 b라는 2개의 in-out 정수 파라미터를 가지는 swapTwoInts(_:_:) 입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743947504355&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}&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;swapTwoInts(_:_:) 함수는 간단하게 b의 값을 a로는 a의 값을 b로 바꿉니다. 이 함수는 a의 값을 temporaryA라 하는 임시 상수에 저장하고 b의 값을 a에 할당하고 temporaryA의 값을 b에 할당합니다.&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;Int 타입의 바꿀 2개의 변수를 이용하여 swapTwoInts(_:_:) 함수를 호출할 수 있습니다. swapTwoInts(_:_:) 함수에 전달할 때 someInt 와 anotherInt 의 이름 앞에 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;앰퍼샌드(&amp;amp;)&lt;/span&gt;&lt;/b&gt;를 붙여야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743949747494&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var someInt = 3
var anotherInt = 107
swapTwoInts(&amp;amp;someInt, &amp;amp;anotherInt)
print(&quot;someInt is now \(someInt), and anotherInt is now \(anotherInt)&quot;)
// Prints &quot;someInt is now 107, and anotherInt is now 3&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;위 예시는 someInt와 anotherInt의 기본값이 함수의 바깥에서 정의되었지만 swapTwoInts(_:_:) 함수로 인해 원래값이 수정되는 것을 보여줍니다.&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;b&gt;함수 타입 (Function Types)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 함수는 파라미터 타입과 반환 타입으로 구성된 특정 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;함수 타입 (fuinction type)&lt;/span&gt;&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;아래 예시는 2개의 Int 파라미터를 받고, 적ㄹ절한 수학 연산을 수행하여 Int 값을 반환합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743950118382&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func addTwoInts(_ a: Int, _ b: Int) -&amp;gt; Int {
    return a + b
}
func multiplyTwoInts(_ a: Int, _ b: Int) -&amp;gt; Int {
    return a * b
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예시는 파라미터 또는 반환 값이 없는 함수입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743950142339&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func printHelloWorld() {
    print(&quot;hello, world&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 이 함수의 타입은 () -&amp;gt; Void와 같습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;함수 타입 사용 (Using Function Types)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift에서 다른 타입처럼 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;함수 타입&lt;/span&gt;&lt;/b&gt;을 사용합니다. 예를 들어 함수 타입에 대해 상수 또는 변수로 정의할 수 있고 변수에 적절한 함수 타입을 할당할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743950341447&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var mathFunction: (Int, Int) -&amp;gt; Int = addTwoInts&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;위 함수는 &quot;2개의 Int값을 파라미터로 가지고, Int 타입의 반환값을 가지는 mathFunction 이라는 변수&quot;를 정의한 것입니다.&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;즉, 아래와 같이 mathFunction 이라는 이름으로 할당된 함수를 호출할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743950463760&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;print(&quot;Result: \(mathFunction(2, 3))&quot;)
// Prints &quot;Result: 5&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;비함수 타입과 동일한 방식으로 같은 타입으로 일치하는 다른 함수를 같은 변수에 할당할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743950486611&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mathFunction = multiplyTwoInts
print(&quot;Result: \(mathFunction(2, 3))&quot;)
// Prints &quot;Result: 6&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;다른 타입과 마찬가지로 상수 또는 변수에 함수를 할당할 때 함수 타입을 추론하기 위해 Swift에 맡길 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743950508954&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let anotherMathFunction = addTwoInts
// anotherMathFunction is inferred to be of type (Int, Int) -&amp;gt; Int&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;&lt;b&gt;파라미터에 함수 타입을 사용 (Function Types as Parameter Types)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Int, Int) -&amp;gt; Int 와 같은 함수 타입을 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;다른 함수의 파라미터&lt;/span&gt;&lt;/b&gt;로 사용할 수 있습니다. 이렇게 하면 함수 호출자가 함수가 호출될 때 제공할 함수 구현의 일부를 남겨둘 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743952132755&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func printMathResult(_ mathFunction: (Int, Int) -&amp;gt; Int, _ a: Int, _ b: Int) {
    print(&quot;Result: \(mathFunction(a, b))&quot;)
}
printMathResult(addTwoInts, 3, 5)
// Prints &quot;Result: 8&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;위의 printMathResult(_:_:_:)의 역할은 적절한 타입의 수학 함수를 호출하고 값을 출력합니다. 이 함수는 함수의 실질 동작 구현이 어떻게 되는지는 없습니다.&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;반환 타입의 함수 타입 (Function Types as Return Types)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 함수의 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;반환 타입으로 함수 타입&lt;/span&gt;&lt;/b&gt;을 사용할 수 있습니다. 반환하는 함수의 반환 화살표 (-&amp;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;다음 예제는 stepForward(_:)와 stepBackward(_:)라 불리는 간단한 2개의 함수를 정의합니다. stepForward(_:) 함수는 입력값에 1을 더해 반환하고 stepBackward(_:) 함수는 입력값에 1을 빼고 반환합니다. 두 함수 모두 (Int) -&amp;gt; Int 타입 입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743952362229&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func stepForward(_ input: Int) -&amp;gt; Int {
    return input + 1
}
func stepBackward(_ input: Int) -&amp;gt; Int {
    return input - 1
}&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;아래 함수 chooseStepFunction(backward:)는 반환 타입이 (Int) -&amp;gt; Int 입니다. chooseStepFunction(backward:) 함수는 backward 부울 파라미터를 토대로 stepForward(_:) 함수 또는 stepBackward(_:) 함수를 반환합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743952451401&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func chooseStepFunction(backward: Bool) -&amp;gt; (Int) -&amp;gt; Int {
    return backward ? stepBackward : stepForward
}&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;위의 예시에서 currentValue &amp;gt; 0 이 true를 반환한다는 의미이고, chooseStepFunction(backward:)가 stepBackward(_:) 함수를 반환하도록 합니다. 반환된 함수의 참조는 moveNearerToZero 라는 상수에 저장됩니다.&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;moveNearerToZero 가 올바른 함수를 참조하므로 0으로 계산하는데 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743952553737&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;print(&quot;Counting to zero:&quot;)
// Counting to zero:
while currentValue != 0 {
    print(&quot;\(currentValue)... &quot;)
    currentValue = moveNearerToZero(currentValue)
}
print(&quot;zero!&quot;)
// 3...
// 2...
// 1...
// zero!&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;중첩 함수 (Nested Functions)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 다룬 모든 함수는 전역 범위에서 정의된 전역 함수 (global functions) 였습니다. 반면, &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;중첩 함수(nested functions)&lt;/span&gt;&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;중첩 함수는 기본적으로 바깥에서 보이지 않지만 중첩 함수를 둘러싼 함수를 통해 호출될 수 있고 사용할 수 있습니다. 중첩 함수를 둘러싼 함수는 중첩 함수 중 하나를 반환하여 중첩 함수를 다른 범위에서 사용할 수도 있습니다.&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;위의 예시 chooseStepFunction(backward:)를 중첩 함수를 사용하고 반환하도록 아래처럼 다시 작성할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743953671584&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func chooseStepFunction(backward: Bool) -&amp;gt; (Int) -&amp;gt; Int {
    func stepForward(input: Int) -&amp;gt; Int { return input + 1 }
    func stepBackward(input: Int) -&amp;gt; Int { return input - 1 }
    return backward ? stepBackward : stepForward
}
var currentValue = -4
let moveNearerToZero = chooseStepFunction(backward: currentValue &amp;gt; 0)
// moveNearerToZero now refers to the nested stepForward() function
while currentValue != 0 {
    print(&quot;\(currentValue)... &quot;)
    currentValue = moveNearerToZero(currentValue)
}
print(&quot;zero!&quot;)
// -4...
// -3...
// -2...
// -1...
// zero!&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1742730910687&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;Documentation&quot; data-og-description=&quot;&quot; data-og-host=&quot;docs.swift.org&quot; data-og-source-url=&quot;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/&quot; data-og-url=&quot;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/&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;Documentation&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;docs.swift.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>iOS/Swift</category>
      <category>IOS</category>
      <category>Swift</category>
      <author>seunghwaan</author>
      <guid isPermaLink="true">https://seosh817.tistory.com/691</guid>
      <comments>https://seosh817.tistory.com/691#entry691comment</comments>
      <pubDate>Sun, 6 Apr 2025 17:52:33 +0900</pubDate>
    </item>
  </channel>
</rss>