React 앱 컴포넌트화 - Web 개발 학습하기

React 앱 컴포넌트화
=============

* 이전
* Overview: Client-side JavaScript frameworks
* 다음

이 시점에서 우리의 앱은 모놀리식입니다. 우리는 이를 작동시키기 전에, 관리 가능하고, 기술적인 컴포넌트로 나눌 필요가 있습니다. React는 컴포넌트로 무엇이고 무엇이 아닌지에 대한 엄격한 규칙은 없으며, 당신에게 달려 있습니다! 이 글에서는 우리의 앱을 컴포넌트로 나누는 합리적인 방법을 보여줄 것입니다.

|  |  |
| --- | --- |
| 필요한 사전 지식: | 코어 HTML, CSS, 및 JavaScript 언어에 익숙할 것. 터미널/커맨드라인 (en-US)에 대한 지식. |
| 목표: | 할 일 목록 앱을 컴포넌트로 나누는 합리적인 방법을 보여주고자 합니다. |

첫 번째 컴포넌트 정의하기
--------------

컴포넌트를 정의하는 것은 연습이 필요할 때까지는 까다로워 보일 수 있지만, 요점은 다음과 같습니다.

* 만약 당신의 앱의 명확한 "조각"을 나타낸다면, 아마도 컴포넌트일 것입니다.
* 만약 자주 재사용된다면, 아마도 컴포넌트일 것입니다.

특히 두번째 항목은 중요합니다. 자주 재사용되는 UI 요소를 컴포넌트로 만들면, 코드를 한 곳에서 변경하고 그 컴포넌트가 사용되는 모든 곳에서 변경 사항을 볼 수 있습니다. 모든 것을 컴포넌트로 분리할 필요는 없습니다. 두번째 항목을 영감으로 삼아, 가장 재사용되고 중요한 UI 조각인 todo list의 항목을 컴포넌트로 만들어 보겠습니다.

`<Todo />` 만들기
--------------

컴포넌트를 만들기 전에, 새로운 파일을 만들어야 합니다. 사실, 우리는 컴포넌트를 위한 디렉토리를 만들어야 합니다. 다음 명령어는 `components` 디렉토리를 만들고, 그 안에 `Todo.js`라는 파일을 만듭니다. 이 명령어를 실행하기 전에, 당신이 앱의 루트에 있는지 확인하세요!

```bash
mkdir src/components
touch src/components/Todo.js
```

`Todo.js` 파일은 현재 비어있습니다! 열어서 다음의 첫 번째 줄을 추가하세요.

```jsx
import React from "react";
```

`Todo`라는 컴포넌트를 만들 것이므로, 다음과 같이 `Todo.js`에 코드를 추가할 수 있습니다. 이 코드에서, 우리는 함수를 다음과 같이 정의하고 내보냅니다.

```jsx
function Todo() {
  return (
    // …
  );
}

export default Todo;
```

지금까지는 괜찮지만, 우리의 컴포넌트는 무언가를 반환해야 합니다! `src/App.js`로 돌아가서, 정렬되지 않은 목록 안에 있는 첫 번째 `<li>`를 복사하고, `Todo.js`에 붙여넣어서 다음과 같이 만드세요.

```jsx
export default function Todo() {
  return (
    <li className="todo stack-small">
 <div className="c-cb">
 <input id="todo-0" type="checkbox" defaultChecked={true} />
 <label className="todo-label" htmlFor="todo-0">
 Eat
 </label>
 </div>
 <div className="btn-group">
 <button type="button" className="btn">
 Edit <span className="visually-hidden">Eat</span>
 </button>
 <button type="button" className="btn btn\_\_danger">
 Delete <span className="visually-hidden">Eat</span>
 </button>
 </div>
 </li>
  );
}
```

**참고:** 컴포넌트는 항상 무언가를 반환해야 합니다. 만약 앞으로 컴포넌트를 렌더링하려고 할 때, 아무것도 반환하지 않는 컴포넌트를 만든다면, React는 브라우저에 에러를 표시할 것입니다.

우리가 만든 `Todo` 컴포넌트가 완성되어 사용할 수 있습니다. `App.js`에 다음과 같이 `Todo`를 가져오는 코드를 파일의 상단에 추가하세요.

```jsx
import Todo from "./components/Todo";
```

해당 컴포넌트를 가져왔다면, `App.js`의 모든 `<li>` 요소를 `<Todo />` 컴포넌트로 바꿀 수 있습니다. `<ul>`는 다음과 같이 읽을 수 있습니다.

```jsx
<ul
 role="list"
 className="todo-list stack-large stack-exception"
 aria-labelledby="list-heading">
 <Todo />
 <Todo />
 <Todo />
</ul>
```

브라우저를 새로고침하면, 불행히도 첫 번째 할 일이 세 번 반복되는 것을 알 수 있습니다.

![할 일 목록 앱, 라벨이 컴포넌트에 하드코딩되어 반복되는 컴포넌트](/ko/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_components/todo-list-repeating-todos.png)

우리는 먹는 것만 하고 싶지 않습니다. 다른 할 일도 있습니다. 다음에는 컴포넌트를 호출할 때 고유한 내용을 렌더링하는 방법을 살펴보겠습니다.

고유한 `<Todo />` 만들기
------------------

컴포넌트는 UI의 일부를 재사용하고, UI의 소스를 하나의 장소로 참조할 수 있기 때문에 강력합니다. 문제는, 우리는 일반적으로 모든 컴포넌트를 재사용하고 싶지 않습니다. 대부분의 부분을 재사용하고, 작은 부분을 변경하고 싶습니다. 이것이 props가 필요한 이유입니다.

### `name`에는 무엇이 들어갈까요?

할 일의 이름을 추적하기 위해서, 각 `<Todo />` 컴포넌트가 고유한 이름을 렌더링하도록 해야 합니다.

`App.js`에서 각 `<Todo />`에 `name` prop을 추가하세요. 이전에 사용했던 할 일의 이름을 사용해보겠습니다.

```jsx
<Todo name="Eat" />
<Todo name="Sleep" />
<Todo name="Repeat" />
```

브라우저를 새로고침하면, 이전과 똑같은 것을 볼 수 있습니다. `<Todo />`에 props를 추가했지만, 아직 사용하지 않았습니다. `Todo.js`로 돌아가서 이 문제를 해결해보겠습니다.

먼저, `Todo()` 함수 정의를 수정해서 `props`를 매개변수로 받도록 하세요. 이전에 했던 것처럼 `props`를 `console.log()`로 출력해보면, 컴포넌트가 `props`를 올바르게 받고 있는지 확인할 수 있습니다.

컴포넌트가 `props`를 받고 있다고 확신이 들면, `Eat`을 `name` prop으로 바꿀 수 있습니다. JSX 표현식 안에서 변수의 값을 주입할 때는 중괄호를 사용한다는 것을 기억하세요.

위의 내용을 모두 합치면, `Todo()` 함수는 다음과 같이 읽을 수 있습니다.

```jsx
function Todo(props) {
  return (
    <li className="todo stack-small">
 <div className="c-cb">
 <input id="todo-0" type="checkbox" defaultChecked={true} />
 <label className="todo-label" htmlFor="todo-0">
 {props.name}
 </label>
 </div>
 <div className="btn-group">
 <button type="button" className="btn">
 Edit <span className="visually-hidden">{props.name}</span>
 </button>
 <button type="button" className="btn btn\_\_danger">
 Delete <span className="visually-hidden">{props.name}</span>
 </button>
 </div>
 </li>
  );
}

export default Todo;
```

"이제" 브라우저에서 세 가지 고유한 할 일을 볼 수 있습니다. 하지만, 여전히 모두 기본적으로 체크되어 있습니다.

![할 일 목록 앱, 이제 컴포넌트에 고유한 할 일 레이블이 전달되어 렌더링됩니다.](/ko/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_components/todo-list-unique-todos.png)

### `completed`인가요?

기존의 정적인 목록에서는 `Eat`만 체크되어 있었습니다. 다시 말하지만, `<Todo />` 컴포넌트를 구성하는 UI의 대부분을 재사용하고 싶지만, 한 가지를 바꾸고 싶습니다. 그것은 또 다른 prop을 위한 좋은 작업입니다! `App.js`에서 각 `<Todo />` 호출에 `completed`라는 새로운 prop을 추가하세요. 첫 번째(`Eat`)는 `true`의 값을 가져야 하고, 나머지는 `false`여야 합니다.

```jsx
<Todo name="Eat" completed={true} />
<Todo name="Sleep" completed={false} />
<Todo name="Repeat" completed={false} />
```

이전과 마찬가지로, 이제 `Todo.js`로 돌아가서 이 props를 사용해야 합니다. `<input />`의 `defaultChecked` 속성을 `completed` prop과 동일한 값으로 변경하세요. 변경이 완료되면, Todo 컴포넌트의 `<input />` 요소는 다음과 같이 읽을 수 있습니다.

```jsx
<input id="todo-0" type="checkbox" defaultChecked={props.completed} />
```

그러면 다음과 같이 브라우저에서 `Eat`만 체크된 것을 볼 수 있습니다.

![할 일 목록 앱, 이제 체크된 상태가 다릅니다. 일부 체크박스는 체크되어 있고, 다른 것은 그렇지 않습니다.](/ko/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_components/todo-list-differing-checked-states.png)

`<Todo />` 컴포넌트의 `completed` prop을 변경하면, 브라우저에서 렌더링된 체크박스가 그에 상응하는 체크 또는 체크 해제됩니다.

### `id`를 부여해주세요

지금은 `<Todo />` 컴포넌트가 모든 할 일에 `id` 속성을 `todo-0`으로 부여합니다. 이것은 `id` 속성은 고유해야 한다는 점에서 나쁜 HTML입니다. (CSS, JavaScript 등에서 문서 조각을 식별하는 데 사용됩니다.) 이것은 각 `Todo`에 대해 고유한 값을 취하는 `id` prop을 컴포넌트에 부여해야 한다는 것을 의미합니다.

우리가 처음에 사용한 패턴을 따라서, `<Todo />` 컴포넌트의 각 인스턴스에 `todo-i` 형식의 ID를 부여해보겠습니다. 여기서 `i`는 매번 1씩 증가합니다.

```jsx
<Todo name="Eat" completed={true} id="todo-0" />
<Todo name="Sleep" completed={false} id="todo-1" />
<Todo name="Repeat" completed={false} id="todo-2" />
```

이제 `Todo.js`로 돌아가서 `id` prop을 사용해보겠습니다. `<input />` 요소의 `id` 속성의 값을, 그리고 레이블의 `htmlFor` 속성의 값을 바꿔야 합니다.

```jsx
<div className="c-cb">
 <input id={props.id} type="checkbox" defaultChecked={props.completed} />
 <label className="todo-label" htmlFor={props.id}>
 {props.name}
 </label>
</div>
```

지금까지는 잘 되고 있나요?
---------------

React를 잘 사용하고 있지만, 더 나은 방법이 있습니다! 우리의 코드는 반복적입니다. `<Todo />` 컴포넌트를 렌더링하는 세 줄은 거의 동일합니다. 하나의 차이점만 있습니다: 각 prop의 값입니다.

JavaScript의 핵심 기능 중 하나인 반복을 사용하여 코드를 정리할 수 있습니다. 반복을 사용하기 위해서는 먼저 할 일을 다시 생각해야 합니다.

할 일을 데이터로
---------

현재 각 할 일은 이름, 체크 여부, 고유 ID라는 세 가지 정보를 포함하고 있습니다. 이 데이터는 객체로 잘 표현됩니다. 여러 개의 할 일이 있으므로, 객체의 배열이 이 데이터를 잘 표현할 수 있습니다.

`src/index.js`에서, 마지막 import 아래에 새로운 `const`를 만드세요. 하지만 `ReactDOM.render()` 위에 위치해야 합니다.

```jsx
const DATA = [
  { id: "todo-0", name: "Eat", completed: true },
  { id: "todo-1", name: "Sleep", completed: false },
  { id: "todo-2", name: "Repeat", completed: false },
];
```

다음으로, `DATA`를 `tasks`라는 prop으로 `<App />`에 전달합니다. `src/index.js`의 마지막 줄은 다음과 같아야 합니다.

```jsx
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
 <App tasks={DATA} />
 </React.StrictMode>,
);
```

이 배열은 이제 `props.tasks`로 `<App />` 컴포넌트에서 사용할 수 있습니다. 원한다면 `console.log()`를 사용하여 확인할 수 있습니다.

**참고:** `ALL_CAPS` (모두 대문자인) 상수 이름은 JavaScript에서 특별한 의미가 없습니다. 이것은 다른 개발자들에게 "이 데이터는 여기에서 정의된 후에는 절대로 변경되지 않을 것이다"라고 알려주는 관습입니다.

반복을 사용하여 렌더링하기
--------------

객체 배열을 렌더링하려면, 각 객체를 `<Todo />` 컴포넌트로 변환해야 합니다. JavaScript는 항목을 다른 것으로 변환하는 `Array.prototype.map()` 배열 메서드를 제공합니다.

`App()`의 return 문 위에 `taskList`라는 새 `const`를 만드세요. `props.tasks` 배열의 각 할 일을 `name`으로 변환하는 것부터 시작해보겠습니다.

```jsx
const taskList = props.tasks?.map((task) => task.name);
```

`<ul>`의 모든 자식을 `taskList`로 바꿔보겠습니다.

```jsx
<ul
 role="list"
 className="todo-list stack-large stack-exception"
 aria-labelledby="list-heading">
 {taskList}
</ul>
```

이렇게 하면 컴포넌트를 다시 표시하는 데 일부 도움이 되지만, 더 많은 작업이 필요합니다. 브라우저는 현재 각 할 일의 이름을 구조화되지 않은 텍스트로 렌더링합니다. HTML 구조인 `<li>`와 해당 체크박스와 버튼이 누락되었습니다!

![할 일 목록 앱에서 할 일 항목 레이블이 한 줄에 뭉쳐져 표시됩니다.](/ko/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_components/todo-list-unstructured-names.png)

이를 해결하려면 `map()` 함수에서 `<Todo />` 컴포넌트를 반환해야 합니다. JSX를 사용하면 JavaScript와 마크업 구조를 혼합할 수 있습니다! 이전에 작성한 코드 대신 다음을 시도해 보겠습니다.

```jsx
const taskList = props.tasks.map((task) => <Todo />);
```

앱을 다시 확인하세요. 이제 할 일이 이전과 비슷해 보이지만, 할 일 이름이 누락되었습니다. 매핑하는 각 할 일에는 `<Todo />` 컴포넌트에 전달할 `id`, `name`, `completed` 속성이 있습니다. 이를 함께 사용하면 다음과 같은 코드가 됩니다.

```jsx
const taskList = props.tasks.map((task) => (
  <Todo id={task.id} name={task.name} completed={task.completed} />
));
```

이제 앱은 이전과 같이 보이며, 코드가 덜 반복됩니다.

고유 키
----

이제 React는 배열에서 할 일을 렌더링하므로, 올바르게 렌더링하려면 어떤 것이 어떤 것인지 추적해야 합니다. React는 추적을 위해 자체적으로 추측을 시도하지만, `<Todo />` 컴포넌트에 `key` prop을 전달하여 도움을 줄 수 있습니다. `key`는 React가 관리하는 특별한 prop입니다. 다른 용도로 `key`라는 단어를 사용할 수 없습니다.

key는 고유해야 하므로, 각 task 객체의 `id`를 키로 재사용할 것입니다. `taskList` 상수를 다음과 같이 업데이트하세요.

```jsx
const taskList = props.tasks.map((task) => (
  <Todo
 id={task.id}
 name={task.name}
 completed={task.completed}
 key={task.id}
 />
));
```

**반복문으로 렌더링하는 모든 것에 고유한 키를 전달해야 합니다.** 브라우저에서 명확한 변경 사항은 없지만, 고유한 키를 사용하지 않으면 React가 콘솔에 경고를 기록하고 앱이 이상하게 동작할 수 있습니다!

앱의 나머지 부분도 컴포넌트화하기
------------------

이제 가장 중요한 컴포넌트를 정리했으므로, 앱의 나머지 부분을 컴포넌트로 바꿀 수 있습니다. 컴포넌트는 UI의 명확한 부분, 재사용되는 UI의 부분 또는 둘 다일 수 있다는 것을 기억하세요. 두 개의 컴포넌트를 더 만들 수 있습니다.

* `<Form/>`
* `<FilterButton/>`

두 컴포넌트가 필요하다는 것을 알기 때문에, 터미널 명령으로 일부 파일 생성 작업을 함께 일괄 처리할 수 있습니다. 앱의 루트 디렉터리에 있는지 확인하고 다음 명령을 터미널에서 실행하세요.

```bash
touch src/components/Form.js src/components/FilterButton.js
```

### `<Form />`

`components/Form.js`를 열고 다음을 수행하세요.

* `Todo.js`에서 수행한 것처럼 파일 상단에서 `React`를 가져옵니다.
* `Todo()`와 동일한 기본 구조를 가진 새 `Form()` 컴포넌트를 만듭니다.
* `App.js` 내부에서 `<form>` 태그와 그 사이의 모든 내용을 복사하여 `Form()`의 `return` 문 내부에 붙여넣습니다.
* 파일 끝에서 `Form`을 내보냅니다.

`Form.js` 파일은 다음과 같아야 합니다.

```jsx
import React from "react";

function Form(props) {
  return (
    <form>
 <h2 className="label-wrapper">
 <label htmlFor="new-todo-input" className="label\_\_lg">
 무엇을 해야 합니까?
 </label>
 </h2>
 <input
 type="text"
 id="new-todo-input"
 className="input input\_\_lg"
 name="text"
 autoComplete="off"
 />
 <button type="submit" className="btn btn\_\_primary btn\_\_lg">
 추가
 </button>
 </form>
  );
}

export default Form;
```

### `<FilterButton />`

`FilterButton.js` 내부에서 `Form.js`를 만드는 데 사용한 것과 동일한 작업을 수행합니다. 하지만, 컴포넌트를 `FilterButton()`으로 호출하고 `App.js`의 `class`가 `filters`인 `<div>` 요소 내부의 첫 번째 버튼에 대한 HTML을 `return` 문으로 복사합니다.

파일은 다음과 같이 보여야 합니다.

```jsx
import React from "react";

function FilterButton(props) {
  return (
    <button type="button" className="btn toggle-btn" aria-pressed="true">
 <span className="visually-hidden">Show </span>
 <span>all </span>
 <span className="visually-hidden"> tasks</span>
 </button>
  );
}

export default FilterButton;
```

**참고:** 여기서 `<Todo />` 컴포넌트를 처음 만들 때와 동일한 실수를 하고 있다는 것을 알 수 있습니다. 각 버튼이 동일할 것입니다. 괜찮습니다! 필터 버튼으로 돌아가기에서 이 컴포넌트를 나중에 수정할 것입니다.

모든 컴포넌트를 가져오기
-------------

그럼 지금까지 만든 새 컴포넌트들을 사용해 보겠습니다.

`App.js`의 맨 위에 `import` 문을 몇 개 더 추가하여 가져옵니다.

그런 다음 `App()`의 `return` 문을 업데이트하여 컴포넌트를 렌더링합니다. 완료하면 `App.js`는 다음과 같이 보일 것입니다.

```jsx
import React from "react";
import Form from "./components/Form";
import FilterButton from "./components/FilterButton";
import Todo from "./components/Todo";

function App(props) {
  const taskList = props.tasks.map((task) => (
    <Todo
 id={task.id}
 name={task.name}
 completed={task.completed}
 key={task.id}
 />
  ));
  return (
    <div className="todoapp stack-large">
 <h1>TodoMatic</h1>
 <Form />
 <div className="filters btn-group stack-exception">
 <FilterButton />
 <FilterButton />
 <FilterButton />
 </div>
 <h2 id="list-heading">3개의 작업이 남음</h2>
 <ul
 role="list"
 className="todo-list stack-large stack-exception"
 aria-labelledby="list-heading">
 {taskList}
 </ul>
 </div>
  );
}

export default App;
```

이것으로, 우리는 React 앱에서 일부 상호작용을 다룰 준비가 거의 완료되었습니다!

요약
--

이 문서는 여기까지입니다. 앱을 컴포넌트로 잘 나누고 효율적으로 렌더링하는 방법에 대해 자세히 알아보았습니다. 이제 React에서 이벤트를 처리하는 방법을 살펴보고 상호 작용을 추가해 보겠습니다.

* 이전
* Overview: Client-side JavaScript frameworks
* 다음


Svelte 시작하기 - Web 개발 학습하기

Svelte 시작하기
===========

* 이전
* Overview: Client-side JavaScript frameworks
* 다음

 이 문서에는 Svelte 프레임워크에 대한 빠른 소개를 제공합니다.
 Svelte가 어떻게 작동하는지, 지금까지 본 나머지 프레임워크 및 도구와 Svelte가 어떻게 다른지 살펴보겠습니다. 그런 다음 개발 환경을 설정하고, 샘플 앱을 만들고, 프로젝트 구조를 이해하고, 로컬에서 실행하고 프로덕션용으로 빌드하는 방법을 배웁니다.

|  |  |
| --- | --- |
| 전제사항: |
 적어도
 HTML,
 CSS, and
 JavaScript
 언어의 사용에 익숙하기를 권장합니다. 그리고
 터미널/커맨트 라인 (en-US)의 사용법에 대해서도 지식이 있어야합니다.

 Svelte 컴파일러는 우리의 소스로 부터 최소화 및 최적화된 JavaScript 코드를 생성합니다.
 당신은 앱을 컴파일하고 빌드하기 위한 노드와 npm이 설치된 터미널이 필요합니다.
  |
| 목표: | 로컬 Svelte 개발 환경을 설정하기 위해, 시작하는 앱을 생성 및 빌드하고 Svelte가 어떻게 동작하는지에 대해서 이해합니다. |

Svelte: 풍부한 사용자 인터페이스를 구축하기 위한 새로운 접근
-------------------------------------

Svelte는 모듈에서 다루는 다른 프레임워크와는 다른 웹앱 구축 방식을 제공합니다. React와 Vue와 같은 프레임 워크는 앱이 실행되는 동안 사용자의 브라우저에서 대부분의 작업을 수행하지만 Svelte는 해당 작업을 앱을 빌드하는 컴파일 단계로 전환하여 고도로 최적화된 바닐라 JavaScript를 생성합니다.

이 접근 방식의 결과는 더 작은 어플리케이션 번들과 더 좋은 성능을 낼 뿐만 아니라 개발자들이 최신 도구적 생태계에 제한된 경험을 가진 사람들에게 더욱 다가가는 경험을 합니다.

Svelte는 HTML, CSS 및 JavaScript의 고전적인 웹 개발 모델을 고수하면서 HTML과 JavaScript에 몇 가지 확장을 추가합니다. 스벨트는 다른 프레임워크 선택지보다 배워야 할 개념과 도구가 적습니다.

현재 주요한 단점은 신생 프레임워크라는 것입니다. 따라서 생태계는 성숙한 프레임워크보다 도구, 지원, 플러그인, 명확한 사용 패턴 등의 측면에서 더 제한적이며 일자리도 적습니다. 그러나 Svelte의 장점은 당신이 그것에 대해 관심을 갖도록 만들기에 충분할 것입니다.

**참고:** 최근 Svelte는 가장 요청이 많았던 기능 중 하나인 공식 타입스크립트 지원을 추가했습니다. 나중에 이 자습서 시리즈에서 살펴보겠습니다.

Svelte 공식 튜토리얼을 통해 기본 개념을 빠르게 숙지한 후 이 튜토리얼 시리즈로 돌아와서 좀 더 심도 있게 빌드하는 방법을 알아보시기 바랍니다.

사용 사례
-----

Svelte는 인터페이스 또는 전체 애플리케이션의 작은 부분을 개발하는 데 사용할 수 있습니다. Svelte가 UI를 구동하도록 처음부터 시작하거나 기존 애플리케이션에 점진적으로 통합할 수 있습니다.

Svelte는 특히 다음 상황을 해결하는 데 적합합니다.

* 저전력 장치용 웹 애플리케이션: Svelte로 구축된 애플리케이션은 번들 크기가 더 작아서 네트워크 연결이 느리고 처리 능력이 제한된 장치에 이상적입니다. 코드가 적다는 것은 다운로드, 구문 분석, 실행 및 메모리에 머무는 데 필요한 KB가 적다는 것을 의미합니다.
* 고도의 대화형 페이지 또는 복잡한 시각화: 많은 수의 DOM 요소를 표시해야 하는 데이터 시각화를 구축하는 경우 런타임 오버헤드가 없는 프레임워크에서 제공되는 성능 향상을 통해 사용자 상호 작용이 빠르고 반응이 빠릅니다.
* 기본 웹 개발 지식이 있는 사람의 온보딩: Svelte는 학습 곡선이 얕습니다. 기본 HTML, CSS 및 JavaScript 지식이 있는 웹 개발자는 짧은 시간에 Svelte 세부 사항을 쉽게 파악하고 웹 애플리케이션 구축을 시작할 수 있습니다.

또한 Sapper(스벨트 기반 프레임워크)의 도움으로 서버 측 렌더링, 코드 분할, 파일 기반 라우팅 및 오프라인 지원과 같은 고급 기능을 갖춘 애플리케이션을 개발할 수도 있습니다. 그리고 네이티브 모바일 애플리케이션을 구축할 수 있는 스벨트 네이티브도 있습니다.

Svelte는 어떻게 작동하나요?
------------------

컴파일러이기 때문에 Svelte는 HTML, CSS 및 JavaScript를 확장하여 런타임 오버헤드 없이 최적의 JavaScript 코드를 생성할 수 있습니다. 이를 달성하기 위해 Svelte는 다음과 같은 방식으로 바닐라 웹 기술을 확장합니다.

* 마크업에서 JavaScript 표현식을 허용하고 핸들바와 유사한 방식으로 조건 및 루프를 사용하는 지시문을 제공하여 HTML을 확장합니다.
* 범위 지정 메커니즘을 추가해 CSS를 확장함으로 각 컴포넌트가 다른 컴포넌트의 스타일과 충돌할 위험 없이 자체 스타일을 정의할 수 있도록 합니다.
* 진정한 반응성을 달성하고 컴포넌트 상태 관리를 용이하게 하기 위해 언어의 특정 지시문을 재해석하여 JavaScript를 확장합니다.

컴파일러는 매우 특정한 상황과 Svelte 컴포넌트의 컨텍스트에서만 개입합니다. JavaScript 언어에 대한 확장은 최소한이며 JavaScript 구문을 위반하거나 개발자를 소외시키지 않도록 신중하게 선택됩니다. 사실, 대부분 바닐라 JavaScript로 작업하게 될 것입니다.

Svelte의 첫 단계
------------

Svelte는 컴파일러이므로 `<script src="svelte.js">` 태그를 페이지에 추가하고 앱으로 가져올 수 없습니다. 컴파일러가 작업을 수행하도록 하려면 개발 환경을 설정해야 합니다.

### 요구 사항

Svelte를 사용하려면 Node.js가 설치되어 있어야 합니다. 장기 지원(LTS) 버전을 사용하는 것이 좋습니다. 노드에는 npm(노드 패키지 관리자) 및 npx(노드 패키지 실행기)가 포함됩니다. npm 대신 Yarn 패키지 관리자를 사용할 수도 있지만 이 자습서 세트에서는 npm을 사용한다고 가정합니다. npm 및 yarn에 대한 자세한 내용은 패키지 관리 기본 사항 (en-US)을 참조하세요.

Windows를 사용하는 경우 이 튜토리얼에서 언급한 터미널 명령을 사용하려면 Unix/macOS 터미널과 패리티를 제공하는 일부 소프트웨어를 설치해야 합니다. Gitbash(Windows용 git 도구 집합) 또는 Linux용 Windows 하위 시스템(WSL) 둘 다 적합합니다. 이에 대한 자세한 내용과 일반적인 터미널 명령은 명령줄 단기 특강 (en-US)을 참조하세요.

또한 자세한 내용은 다음을 참조하십시오.

* nodejs.org의 "npm이란?"
* npm 블로그의 "npx 소개"
* Svelte 블로그의 "Svelte를 시작하는 가장 쉬운 방법"
### 첫 번째 Svelte 앱 만들기

스타터 앱 템플릿을 만드는 가장 쉬운 방법은 스타터 템플릿 애플리케이션을 다운로드하는 것입니다. GitHub의 sveltejs/template을 방문하거나 다운로드하고 압축을 푸는 과정을 피하고 싶다면 degit을 사용하면 됩니다.

시작 앱 템플릿을 만들려면 다음 터미널을 실행하세요.

```bash
npx degit sveltejs/template moz-todo-svelte
cd moz-todo-svelte
npm install
npm run dev
```

**참고:** degit은 마술 같은 것을 하지 않습니다. 단지 최신 버전의 git repo 콘텐츠를 다운로드하고 압축을 풀 수 있게 해줍니다. 리포지토리의 모든 기록을 다운로드하거나 완전한 로컬 복제본을 생성하지 않기 때문에 `git clone`을 사용하는 것보다 훨씬 빠릅니다.

`npm run dev`를 실행한 후 Svelte는 애플리케이션을 컴파일하고 빌드합니다. `localhost:8080`에서 로컬 서버를 시작합니다. Svelte는 파일 업데이트를 감시하고 소스 파일이 변경되면 자동으로 앱을 다시 컴파일하고 새로 고칩니다. 브라우저에 다음과 같이 표시됩니다.

![A simple start page that says hello world, and gives a link to the official svelte tutorials](/ko/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_getting_started/01-svelte-starter-app.png)

### 애플리케이션 구조

시작 템플릿은 다음 구조로 제공됩니다.
```

moz-todo-svelte
├── README.md
├── package.json
├── package-lock.json
├── rollup.config.js
├── .gitignore
├── node_modules
├── public
│   ├── favicon.png
│   ├── index.html
│   ├── global.css
│   └── build
│       ├── bundle.css
│       ├── bundle.js
│       └── bundle.js.map
├── scripts
│   └── setupTypeScript.js
└── src
    ├── App.svelte
    └── main.js
```

구성은 다음과 같습니다.

* `package.json`과 `package-lock.json`: Node.js/npm이 구성을 유지하기 위해 사용하는 프로젝트에 대한 정보를 포함합니다. 이 튜토리얼을 완료하기 위해 이 파일을 전혀 이해할 필요는 없지만, 이에 대해 더 알고 싶다면 NodeJS.org의 `package.json`은 무엇인가요??를 읽어보시면 됩니다. 또한 저희의 패키지 관리 기본 자습서 (en-US)에서도 이에 대해 다루고 있습니다.
* `node_modules`: 노드가 프로젝트 종속성을 저장하는 곳입니다. 이러한 종속성은 프로덕션으로 전송되지 않고 개발 목적으로만 사용됩니다.
* `.gitignore`: 프로젝트에서 무시할 파일 또는 폴더를 git에 알려줍니다. git repo에 앱을 포함하기로 결정한 경우에 유용합니다.
* `rollup.config.js`: Svelte는 롤업을 모듈 번들러로 사용합니다. 이 구성 파일은 앱을 컴파일하고 빌드하는 방법을 롤업에 알려줍니다. 웹팩을 선호하는 경우 대신 `npx degit sveltejs/template-webpack svelte-app`을 사용하여 시작 프로젝트를 만들 수 있습니다.
* `scripts`: 필요에 따라 설정 스크립트를 포함합니다. 현재는 `setupTypeScript.js`만 포함해야 합니다.
    + `setupTypeScript.js`: 이 스크립트는 Svelte에서 타입스크립트 지원을 설정합니다. 마지막 글에 이에 대해 더 이야기하겠습니다.
* `src`: 이 디렉토리는 애플리케이션의 소스 코드가 있는 곳으로 앱의 코드를 생성하게 됩니다.
    + `App.svelte`: 앱의 최상위 컴포넌트입니다. 'Hello World!' 메시지까지만 렌더링합니다.
    + `main.js`: 애플리케이션의 진입점입니다. 단지 `App` 컴포넌트를 인스턴스화하고 HTML 페이지의 본문에 바인딩합니다.
* `public`: 이 디렉토리에는 생산에 게시될 모든 파일이 포함됩니다.
    + `favicon.png`: 앱의 파비콘입니다. 현재 Svelte 로고입니다.
    + `index.html`: 앱의 기본 페이지입니다. 처음에는 Svelte가 생성한 CSS 파일과 js 번들을 로드하는 빈 HTML 페이지일 뿐입니다.
    + `global.css`: 이 파일에는 범위가 지정되지 않은 스타일이 포함되어 있습니다. 전체 애플리케이션에 적용되는 일반 CSS 파일입니다.
    + `build`: 이 폴더에는 생성된 CSS 및 JavaScript 소스 코드가 포함되어 있습니다.
        - `bundle.css`: 각 컴포넌트에 대해 정의된 스타일에서 Svelte가 생성한 CSS 파일입니다.
        - `bundle.js`: 모든 JavaScript 소스 코드에서 컴파일된 JavaScript 파일입니다.
첫 번째 Svelte 컴포넌트 살펴보기
---------------------

컴포넌트들은 Svelte 애플리케이션의 구축을 위한 블록들입니다. 컴포넌트들은 HTML의 상위 집합을 사용하여 `.svelte` 파일에 기록됩니다.

세 섹션(`<script>`, `<style>` 및 마크업)은 모두 선택 사항이며 원하는 순서로 표시될 수 있습니다.

```html
<script>
 // 로직이 들어갑니다.
</script>

<style>
 /\* 스타일이 들어갑니다. \*/
</style>

<!-- 마크업(0 또는 다른 HTML 엘리먼트들)이 들어갑니다. -->
```

**참고:** 컴포넌트 형식에 대한 자세한 내용은 Svelte 설명서를 참조하세요.

이를 염두에 두고 스타터 템플릿과 함께 제공되는 `src/App.svelte` 파일을 살펴보겠습니다. 다음과 같은 내용이 표시되어야 합니다.

```html
<script>
 export let name;
</script>

<main>
  <h1>안녕! {name}!</h1>
  <p>
    <a href="https://svelte.dev/tutorial">Svelte 튜토리얼</a>에 방문해서 Svelte
    앱을 반드는 방법을 배워보세요.
  </p>
</main>

<style>
 main {
 text-align: center;
 padding: 1em;
 max-width: 240px;
 margin: 0 auto;
 }

 h1 {
 color: #ff3e00;
 text-transform: uppercase;
 font-size: 4em;
 font-weight: 100;
 }

 @media (min-width: 640px) {
 main {
 max-width: none;
 }
 }
</style>
```

### `<script>` 섹션

`<script>` 블록에는 컴포넌트 인스턴스가 생성될 때 실행되는 JavaScript가 포함되어 있습니다. 최상위 수준에서 선언된(또는 가져온) 변수는 컴포넌트의 마크업에서 '볼 수' 있습니다. 최상위 변수는 Svelte가 컴포넌트 상태를 처리하는 방식이며 기본적으로 반응적입니다. 이것이 무엇을 의미하는지 나중에 자세히 설명하겠습니다.

```html
<script>
 export let name;
</script>
```

Svelte는 `export` 키워드를 사용하여 변수 선언을 속성으로 표시합니다. 이는 컴포넌트의 사용자가 액세스할 수 있음을 의미합니다. (예: 다른 컴포넌트). 이것은 Svelte가 JavaScript 구문을 확장하여 친숙함을 유지하면서 더 유용하게 만드는 한 예입니다.

### 마크업 부분

마크업 섹션에서 원하는 HTML을 삽입할 수 있으며 추가로 단일 중괄호(`{}`) 안에 유효한 JavaScript 표현식을 삽입할 수 있습니다. 이 경우 `안녕` 텍스트 바로 뒤에 `name` 속성의 값을 삽입합니다.

```html
<main>
  <h1>안녕 {name}!</h1>
  <p>
    <a href="https://svelte.dev/tutorial">Svelte 튜토리얼</a>에 방문해서 Svelte
    앱을 반드는 방법을 배워보세요.
  </p>
</main>
```

Svelte는 `{#if}`, `{#each}` 및 `{#await}`와 같은 태그도 지원합니다. 이러한 예제를 사용하면 마크업의 일부를 조건부로 렌더링하고, 요소 목록을 반복하고, 비동기 값들에 각각 작용합니다.

### `<style>` 섹션

CSS 작업 경험이 있는 경우 다음 스니펫을 쉽게 이해할 수 있습니다.

```html
<style>
 main {
 text-align: center;
 padding: 1em;
 max-width: 240px;
 margin: 0 auto;
 }

 h1 {
 color: #ff3e00;
 text-transform: uppercase;
 font-size: 4em;
 font-weight: 100;
 }

 @media (min-width: 640px) {
 main {
 max-width: none;
 }
 }
</style>
```

`<h1>` 요소에 스타일을 적용하고 있습니다. `<h1>` 요소가 포함된 다른 컴포넌트는 어떻게 될까요?

Svelte에서 컴포넌트의 `<style>` 블록 내부 CSS는 해당 컴포넌트로만 범위가 지정됩니다. 이는 컴포넌트 스타일의 해시를 기반으로 하는 선택한 요소에 클래스를 추가하여 작동합니다.

새 브라우저 탭에서 `localhost:8080`을 열고 *HELLO WORLD!* 레이블을 마우스 오른쪽/`Ctrl`-클릭하고 *Inspect*를 선택하여 작동 중인 것을 확인할 수 있습니다.

![Svelte starter app with devtools open, showing classes for scoped styles](/ko/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_getting_started/02-svelte-component-scoped-styles.png)

앱을 컴파일할 때 Svelte는 `h1` 스타일 정의를 `h1.svelte-1tky8bj`로 변경한 다음 컴포넌트의 모든 `<h1>` 요소를 `<h1 class="svelte-1tky8bj">`로 수정합니다. 필요에 따라 스타일을 선택합니다.

**참고:** 이 동작을 재정의하고 `:global()` 한정자를 사용하여 전역적으로 선택기에 스타일을 적용할 수 있습니다(Svelte `<style>` 문서를 보시면 더 많은 정보가 있습니다).

몇 가지 변경
-------

 이제 모든 것이 어떻게 조화를 이루는지에 대한 기본적인 아이디어를 얻었으므로 몇 가지 변경 작업을 시작할 수 있습니다.
 이 시점에서 `App.svelte` 컴포넌트를 업데이트할 수 있습니다. 예를 들어 `App.svelte`의 6번째 줄에 있는 `<h1>` 요소를 다음과 같이 변경합니다.

```html
<h1>Hello {name} from MDN!</h1>
```

변경 사항을 저장하면 `localhost:8080`에서 실행 중인 앱이 자동으로 업데이트됩니다.

### Svelte 반응성 살펴보기

UI 프레임워크의 컨텍스트에서 반응성은 컴포넌트의 상태가 변경될 때 프레임워크가 DOM을 자동으로 업데이트할 수 있음을 의미합니다.

Svelte에서 반응성은 컴포넌트의 최상위 변수에 새 값을 할당하여 트리거됩니다. 예를 들어 `App` 컴포넌트에 `toggleName()` 함수를 포함하고 이를 실행하는 버튼을 포함할 수 있습니다.

다음과 같이 `<script>` 및 마크업 섹션을 업데이트 해보세요.

```html
<script>
 export let name;

 function toggleName() {
 if (name === "world") {
 name = "svelte";
 } else {
 name = "world";
 }
 }
</script>

<main>
  <h1>안녕! {name}!</h1>
  <button on:click="{toggleName}">토글 name</button>
  <p>
    <a href="https://svelte.dev/tutorial">Svelte 튜토리얼</a>에 방문해서 Svelte
    앱을 반드는 방법을 배워보세요.
  </p>
</main>
```

버튼을 클릭할 때마다 Svelte는 `toggleName()` 함수를 실행하여 `name` 변수의 값을 업데이트합니다.

보시다시피 `<h1>` 레이블이 자동으로 업데이트됩니다. 뒤의 내용에서 Svelte는 가상 DOM이나 기타 복잡한 조정 메커니즘을 사용하지 않고 name 변수의 값이 변경될 때마다 DOM을 업데이트하는 JavaScript 코드를 만들었습니다.

`on:click`에서 `:`의 사용에 유의하십시오. 이것이 DOM 이벤트를 수신하기 위한 Svelte의 구문입니다.

main.js 검사: 앱의 진입점
------------------

`App` 컴포넌트를 가져와서 사용하고 있는 `src/main.js`를 열어봅시다. 이 파일은 우리 앱의 진입점이며 처음에는 다음과 같이 보입니다.

```js
import App from "./App.svelte";

const app = new App({
  target: document.body,
  props: {
    name: "world",
  },
});

export default app;
```

`main.js`는 사용할 Svelte 컴포넌트를 가져오는 것으로 시작합니다. 그런 다음 3행에서 인스턴스화하여 다음 속성을 가진 옵션 개체를 전달합니다.

* `target`: 컴포넌트를 렌더링하려는 DOM 요소(이 경우 `<body>` 요소)입니다.
* `props`: `App` 컴포넌트의 각 prop에 할당할 값입니다.
숨겨진 아래 살펴보기
-----------

어떻게 Svelte는 이 모든 파일이 함께 잘 작동하도록 관리하나요?

Svelte 컴파일러는 모든 컴포넌트의 `<style>` 섹션을 처리하고 `public/build/bundle.css` 파일로 컴파일합니다.

또한 모든 컴포넌트의 마크업 및 `<script>` 섹션을 컴파일하고 결과를 `public/build/bundle.js`에 저장합니다. 또한 `src/main.js`에 코드를 추가하여 각 컴포넌트의 기능을 참조합니다.

마지막으로 `public/index.html` 파일에는 생성된 `bundle.css` 및 `bundle.js` 파일이 포함됩니다.

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />

    <title>Svelte app</title>

    <link rel="icon" type="image/png" href="/favicon.png" />
    <link rel="stylesheet" href="/global.css" />
    <link rel="stylesheet" href="/build/bundle.css" />

    <script defer src="/build/bundle.js"></script>
  </head>

  <body></body>
</html>
```

`bundle.js`의 축소된 버전은 "Svelte 런타임"(단지 300줄의 JavaScript 코드) 및 `App.svelte` 컴파일 컴포넌트를 포함하여 무게가 3KB를 약간 넘습니다. 보시다시피 `bundle.js`는 `index.html`이 참조하는 유일한 JavaScript 파일입니다. 웹 페이지에 로드된 다른 라이브러리가 없습니다.

이것은 다른 프레임워크의 컴파일된 번들보다 훨씬 작은 공간입니다. 코드 번들의 경우 중요한 것은 다운로드해야 하는 파일의 크기만이 아니라는 점을 고려하십시오. 이것은 구문 분석, 실행 및 메모리에 보관해야 하는 실행 가능한 코드입니다. 따라서 이것은 특히 저전력 장치나 CPU를 많이 사용하는 응용 프로그램에서 차이를 만듭니다.

튜토리얼 이후에
--------

이 자습서 시리즈에서는 완전한 웹 애플리케이션을 구축하게 됩니다. Svelte에 대한 모든 기본 사항과 몇 가지 고급 주제에 대해 알아봅니다.

콘텐츠를 읽으면 Svelte 기능을 잘 이해할 수 있지만 진행하면서 앱 코딩을 따라가면 이 자습서를 최대한 활용할 수 있습니다. 각 문서를 더 쉽게 따라갈 수 있도록 각 자습서의 시작 부분에 있는 앱의 소스가 포함된 폴더가 있는 GitHub 리포지토리를 제공합니다.

Svelte는 컴퓨터에 아무것도 설치하지 않고도 웹에서 Svelte 앱을 라이브 코딩할 수 있는 놀이터인 온라인 REPL도 제공합니다. 바로 코딩을 시작할 수 있도록 각 기사에 대한 REPL을 제공합니다. 이러한 도구를 사용하는 방법에 대해 좀 더 이야기해 봅시다.

### Git 사용법

가장 널리 사용되는 버전 제어 시스템은 리포지토리에 대한 호스팅과 리포지토리 작업을 위한 여러 도구를 제공하는 사이트인 GitHub와 함께 Git입니다.

각 기사의 소스 코드를 쉽게 다운로드할 수 있도록 GitHub를 사용할 것입니다. 길을 잃을 경우를 대비하여 기사를 완료한 후 코드를 원래대로 얻을 수도 있습니다.

git 설치 후 레포지토리를 복제하려면 다음을 실행해야 합니다.

```bash
git clone https://github.com/opensas/mdn-svelte-tutorial.git
```

그런 다음 각 문서의 시작 부분에서 해당 폴더로 `cd`하고 개발 모드에서 앱을 시작하여 다음과 같이 현재 상태를 확인할 수 있습니다.

```bash
cd 02-starting-our-todo-app
npm install
npm run dev
```

git 및 GitHub에 대해 자세히 알아보려면 저희가 정리한 유용한 가이드 링크 목록을 보세요. Git 및 GitHub

**참고:** git repo를 복제하지 않고 파일을 다운로드하기만 하려면 `npx degit opensas/mdn-svelte-tutorial`과 같은 degit 도구를 사용할 수 있습니다. `npx degit opensas/mdn-svelte-tutorial/01-getting-started`를 사용하여 특정 폴더를 다운로드할 수도 있습니다. Degit은 로컬 git repo를 만들지 않고 지정된 폴더의 파일만 다운로드합니다.

### Svelte REPL 사용

REPL(read–eval–print loop)은 명령을 입력하고 결과를 즉시 확인하십시오. 많은 프로그래밍 언어가 REPL을 제공합니다.

Svelte의 REPL은 그 이상입니다. 완전한 앱을 만들고 온라인에 저장하고 다른 사람과 공유할 수 있는 온라인 도구입니다.

아무 것도 설치하지 않고 모든 컴퓨터에서 Svelte로 게임을 시작하는 가장 쉬운 방법입니다. Svelte 커뮤니티에서도 널리 사용됩니다. 아이디어를 공유하거나 도움을 요청하거나 문제를 보고하려는 경우 문제를 시연하는 REPL 인스턴스를 생성하는 것이 항상 매우 유용합니다.

Svelte REPL과 사용 방법을 간단히 살펴보겠습니다. 다음과 같이 보입니다.

![the svelte REPL in action, showing component code on the left, and output on the right](/ko/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_getting_started/03-svelte-repl-in-action.png)

REPL을 시작하려면 브라우저를 열고 https://svelte.dev/repl로 이동합니다.

* 화면 왼쪽에는 컴포넌트의 코드가 표시되고 오른쪽에는 실행 중인 앱의 출력이 표시됩니다.
* 코드 위의 막대를 사용하면 `.svelte` 및 `.js` 파일을 만들고 재배열할 수 있습니다. 폴더 안에 파일을 만들려면 `components/MyComponent.svelte`와 같이 전체 경로 이름을 지정하기만 하면 됩니다. 폴더가 자동으로 생성됩니다.
* 해당 막대 위에는 REPL의 제목이 있습니다. 그것을 클릭하여 편집하세요.
* 오른쪽에는 3개의 탭이 있습니다.
    + *Result* 탭에는 앱 출력이 표시되며 하단에 콘솔이 제공됩니다.
    + *JS output* 탭에서는 Svelte에서 생성된 JavaScript 코드를 검사하고 컴파일러 옵션을 설정할 수 있습니다.
    + *CSS output* 탭에는 Svelte에서 생성한 CSS가 표시됩니다.
* 탭 위에는 전체 화면 모드로 전환하고 앱을 다운로드할 수 있는 도구 모음이 있습니다. GitHub 계정으로 로그인하면 앱을 포크하고 저장할 수도 있습니다. 또한 GitHub 사용자 이름 프로필을 클릭하고 *Your saved apps* 을 선택하면 저장된 모든 REPL을 볼 수 있습니다.

REPL에서 파일을 변경할 때마다 Svelte는 앱을 다시 컴파일하고 결과 탭을 업데이트합니다. 앱을 공유하려면 URL을 공유하세요. 예를 들어 전체 앱을 실행하는 REPL 링크는 다음과 같습니다. https://svelte.dev/repl/378dd79e0dfe4486a8f10823f3813190?version=3.23.2.

**참고:** URL에서 Svelte의 버전을 지정하는 방법에 유의하십시오. 이는 Svelte의 특정 버전과 관련된 문제를 보고할 때 유용합니다.

바로 코딩을 시작할 수 있도록 각 기사의 시작과 끝에 REPL을 제공합니다.

**참고:** 현재 REPL은 폴더 이름을 제대로 처리할 수 없습니다. REPL에 대한 자습서를 따르는 경우 루트 폴더 내에 모든 컴포넌트를 생성하십시오. 그런 다음 코드에 다음과 같은 경로를 본다면 (예: `import Todos from './components/Todos.svelte'`),곧장 일반 URL로 대체하세요 (예: `import Todos from './Todos.svelte'`).

지금까지의 코드
--------

### Git

레포지토리 클론을 하세요(당신이 이미 클론을 완료하지 않았다면).

```bash
git clone https://github.com/opensas/mdn-svelte-tutorial.git
```

그런 다음 현재 앱으로 상태를 이동하고, 실행하세요.

```bash
cd mdn-svelte-tutorial/01-getting-started
```

또는 폴더의 내용을 직접 다운로드하세요.

```bash
npx degit opensas/mdn-svelte-tutorial/01-getting-started
```

개발 모드로 당신의 앱을 실행하기 위한 명령어(`npm install && npm run dev`)를 기억하세요.

### REPL

REPL을 사용하여 우리와 함께 코딩하려면 https://svelte.dev/repl/fc68b4f059d34b9c84fa042d1cce586c?version=3.23.2에서 시작하세요.

요약
--

이것으로 Svelte를 로컬로 설치하는 방법, 스타터 앱을 만드는 방법 및 기본 작동 방법을 포함하여 Svelte에 대한 초기 살펴보기를 마칩니다. 다음 글에서 우리는 첫 번째로 초심자에게 적절한 애플리케이션인 할 일 목록을 만들기 시작할 것입니다. 하지만 그 전에 배운 내용 중 일부를 요약해 보겠습니다.

Svelte에서.

* 단일 `.svelte` 파일에서 각 컴포넌트의 스크립트, 스타일 및 마크업을 정의합니다.
* 컴포넌트 속성은 `export` 키워드로 선언됩니다.
* 해당 `.svelte` 파일을 가져오기만 하면 Svelte 컴포넌트를 사용할 수 있습니다.
* 컴포넌트 스타일은 서로 충돌하지 않도록 범위가 지정됩니다.
* 마크업 섹션에서 중괄호 사이에 넣어 JavaScript 표현식을 포함할 수 있습니다.
* 컴포넌트의 최상위 변수는 컴포넌트의 상태를 구성합니다.
* 최상위 변수에 새 값을 할당하는 것만으로 반응성이 발동됩니다.
* 이전
* Overview: Client-side JavaScript frameworks
* 다음


Svelte의 동적인 동작: 변수와 props 작용 - Web 개발 학습하기

Svelte의 동적인 동작: 변수와 props 작용
============================

* 이전
* Overview: Client-side JavaScript frameworks
* 다음  (en-US)

이제 마크업과 스타일이 준비되었으므로 Svelte 할 일 목록 앱에 필요한 기능 개발을 시작할 수 있습니다. 이 문서에서는 변수와 props를 사용한 동적인 앱을 만들어 할 일을 추가 및 삭제하고, 완료로 표시하거나 상태별로 필터링할 수 있습니다.

|  |  |
| --- | --- |
| 전제사항: |
 적어도
 HTML,
 CSS, and
 JavaScript
 언어의 사용에 익숙하기를 권장합니다. 그리고
 터미널/커맨트 라인 (en-US)의 사용법에 대해서도 지식이 있어야합니다.

당신은 앱을 컴파일하고 빌드하기 위한 노드와 npm이 설치된 터미널이 필요합니다. |
| 목표: | 컴포넌트 생성, props를 활용한 데이터 전달, 마크업에 JavaScript 표현식 렌더링, 컴포넌트 상태 수정 및 전체 목록 순회 같은 몇 가지 기본 Svelte 개념을 배우고 실습합니다. |

함께 코드를 작성해봅시다
-------------

### Git

Git 레포지토리를 복제하세요(당신이 이미 복제를 한게 아니라면).

```bash
git clone https://github.com/opensas/mdn-svelte-tutorial.git
```

그런 다음 현재 앱의 위치로 이동하기 위해 다음을 실행하세요.

```bash
cd mdn-svelte-tutorial/03-adding-dynamic-behavior
```

폴더의 내용을 직접 다운로드 할 수도 있습니다.

```bash
npx degit opensas/mdn-svelte-tutorial/03-adding-dynamic-behavior
```

`npm install && npm run dev` 명령어로 실행하여 개발 모드에서 앱을 실행해주세요.

### REPL

REPL을 사용하여 우리와 함께 코딩하려면 https://svelte.dev/repl/c862d964d48d473ca63ab91709a0a5a0?version=3.23.2에서 시작하세요.

할 일 작업
------

우리의 `Todos.svelte` 컴포넌트는 현재 정적 마크업만 표시하고 있습니다. 좀 더 역동적으로 만들어 봅시다. 마크업에서 작업 정보를 가져와 `todos` 배열에 저장할 것입니다. 또한 총 작업 수와 완료된 작업을 추적하기 위해 두 개의 변수를 생성합니다.

컴포넌트의 상태는 이 세 가지 최상위 변수로 표시됩니다.

1. `src/components/Todos.svelte` 상단에 `<script>` 섹션을 생성하고 다음과 같이 콘텐츠 일부를 제공합니다.

```html
<script>
 let todos = [
 { id: 1, name: "Create a Svelte starter app", completed: true },
 { id: 2, name: "Create your first component", completed: true },
 { id: 3, name: "Complete the rest of the tutorial", completed: false },
 ];
 let totalTodos = todos.length;
 let completedTodos = todos.filter((todo) => todo.completed).length;
</script>
```

이제 정보를 활용해 무언가를 해봅시다.
2. 상태 메시지를 표시하는 것으로 시작하겠습니다. `id`가 `list-heading`인 `<h2>` 제목을 찾아 하드코딩된 활성 및 완료된 작업 수를 동적 표현식으로 바꿉니다.

```html
<h2 id="list-heading">
  {completedTodos} out of {totalTodos} items completed
</h2>
```

3. 앱으로 이동하면 이전과 같이 "3개 항목 중 2개 완료됨" 메시지를 볼 수 있지만, 이번에는 `todos` 배열에서 정보가 제공된 것입니다.
4. 이를 증명하기 위해 해당 배열로 이동하여 to-do 개체의 완료된 속성 값 중 일부를 변경하고 새로운 to-do 개체를 추가합니다. 메시지의 숫자가 어떻게 적절하게 업데이트되는지 확인하세요.
데이터에서 할 일을 동적으로 생성
------------------

현재 표시되는 할 일 항목은 모두 정적입니다. 우리는 `todos` 배열의 각 항목을 순회하고 각 작업에 대한 마크업을 렌더링하려고 하므로 지금 실행해 보겠습니다.

HTML에는 조건문 및 루프와 같은 논리를 표현하는 방법이 없습니다. 하지만 Svelte에서는 가능합니다. 이 경우에는 `{#each}` 지시문을 사용하여 `todos` 배열을 순회합니다. 두 번째 매개변수가 제공되면 현재 항목의 index가 포함됩니다. 또한 각 항목을 고유하게 식별하는 키 표현식을 제공할 수 있습니다. Svelte는 마지막에 항목을 추가하거나 제거하는 대신 데이터가 변경될 때 목록을 비교하는 데 표현식을 사용하며, 항상 키 표현식을 기입하는 것이 좋습니다. 마지막으로 `:else` 블록을 제공할 수 있으며 목록이 비어 있을 때 렌더링됩니다.

한 번 해봅시다.

1. 기존 `<ul>` 요소를 다음과 같은 단순화된 버전으로 대체하여 작동 방식을 파악합니다.

```html
<ul>
  {#each todos as todo, index (todo.id)}
  <li>
    <input type="checkbox" checked="{todo.completed}" /> {index}. {todo.name}
    (id: {todo.id})
  </li>
  {:else} Nothing to do here! {/each}
</ul>
```

2. 앱으로 돌아갑니다. 다음과 같은 내용이 표시됩니다.

 ![very simple to-do list output created using an each block](/ko/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_variables_props/01-each-block.png)
3. 이제 이것이 작동하는 것을 확인했으므로 `{#each}` 지시문의 각 루프로 완전한 할 일 항목을 생성하고 내부에 `todos` 배열의 정보를 임베드합니다: `id`, `name` 및 `completed`. 기존 `<ul>` 블록을 다음으로 바꿉니다.

```html
<!-- To-dos -->
<ul role="list" class="todo-list stack-large" aria-labelledby="list-heading">
  {#each todos as todo (todo.id)}
  <li class="todo">
    <div class="stack-small">
      <div class="c-cb">
        <input
 type="checkbox"
 id="todo-{todo.id}"
 checked="{todo.completed}" />
        <label for="todo-{todo.id}" class="todo-label"> {todo.name} </label>
      </div>
      <div class="btn-group">
        <button type="button" class="btn">
          Edit <span class="visually-hidden">{todo.name}</span>
        </button>
        <button type="button" class="btn btn\_\_danger">
          Delete <span class="visually-hidden">{todo.name}</span>
        </button>
      </div>
    </div>
  </li>
  {:else}
  <li>Nothing to do here!</li>
  {/each}
</ul>
```

체크박스의 `checked` 및 `id` 속성과 마찬가지로 중괄호를 사용하여 HTML 속성에 JavaScript 표현식을 삽입하는 방법에 주목하세요.

정적 마크업을 컴포넌트의 상태에서 작업을 표시할 준비가 된 동적 템플릿으로 전환했습니다. 멋집니다! 우리는 거의 다 왔습니다.

props 작업
--------

할 일 목록이 하드코딩되어 있는 `Todos` 컴포넌트는 그다지 유용하지 않습니다. 컴포넌트를 범용 할 일 편집기로 바꾸려면 이 컴포넌트의 부모가 편집할 할 일 목록을 전달하도록 허용해야 합니다. 이렇게 하면 웹 서비스나 로컬 저장소에 저장하고 나중에 업데이트를 위해 검색할 수 있습니다. 배열을 `prop`로 바꾸겠습니다.

1. `Todos.svelte`에서 기존 `let todos = …` 블록을 `export let todos = []`로 대체합니다.

```js
export let todos = [];
```

처음에는 조금 이상하게 느껴질 수 있습니다. 그것은 JavaScript 모듈에서 `export`가 일반적으로 작동하는 방식이 아닙니다! 이것은 Svelte가 유효한 구문을 사용하고 새로운 목적을 부여하여 JavaScript를 '확장'하는 방법입니다. 이 경우 Svelte는 'export' 키워드를 사용하여 변수 선언을 속성 또는 prop으로 표시합니다. 이는 컴포넌트 사용자가 접근 할 수 있음을 의미합니다.
 prop에 대한 기본 초기 값을 지정할 수도 있습니다. 이것은 컴포넌트를 인스턴스화할 때 컴포넌트의 사용자가 컴포넌트에 prop을 지정하지 않거나 초기 값이 정의되지 않은 경우에 사용됩니다.
 따라서 `export let todos = []`를 사용하여 `Todos.svelte` 컴포넌트가 `todos` 속성을 받고, 생략한다면 빈 배열로 초기화된다는 것을 Svelte에 알립니다.
2. 앱을 보면 "여기서 할 일이 없습니다!"라는 메시지가 표시됩니다. 메시지. 이는 현재 `App.svelte`에서 어떤 값도 전달하지 않아 기본값을 사용하고 있기 때문입니다.
3. 이제 해야 할 일을 `App.svelte`로 이동하고 `Todos.svelte` 컴포넌트에 prop으로 전달합니다. 다음과 같이 `src/App.svelte`를 업데이트합니다.

```svelte
<script>
 import Todos from "./components/Todos.svelte";

 let todos = [
 { id: 1, name: "Create a Svelte starter app", completed: true },
 { id: 2, name: "Create your first component", completed: true },
 { id: 3, name: "Complete the rest of the tutorial", completed: false },
 ];
</script>

<Todos todos={todos} />
```

4. 속성과 변수의 이름이 같은 경우, Svelte를 사용하면 변수를 편리한 바로 가기로 지정할 수 있으므로 마지막 줄을 이와 같이 다시 작성할 수 있습니다. 바로 시도해 보세요.

```svelte
<Todos {todos} />
```

이 시점에서 해야 할 일은 `App.svelte` 컴포넌트에서 전달한다는 점을 제외하면 이전과 동일하게 렌더링되어야 합니다.

할 일 토글링과 제거
-----------

작업 상태를 전환하는 몇 가지 기능을 추가해 보겠습니다. Svelte에는 DOM 이벤트를 수신하기 위한 `on:eventname` 지시어가 있습니다. 체크박스 입력의 `on:click` 이벤트에 핸들러를 추가하여 완성된 값을 토글하도록 합시다.

1. 다음과 같이 `src/components/Todos.svelte` 내부의 `<input type="checkbox">` 요소를 업데이트합니다.

```svelte
<input type="checkbox" id="todo-{todo.id}"
 on:click={() => todo.completed = !todo.completed}
 checked={todo.completed}
/>
```

2. 다음으로 `todos` 배열에서 할 일을 제거하는 함수를 추가합니다. `Todos.svelte`의 `<script>` 섹션 하단에 다음과 같이 `removeTodo()` 함수를 추가합니다.

```js
function removeTodo(todo) {
  todos = todos.filter((t) => t.id !== todo.id);
}
```

3. *Delete* 버튼을 통해 호출하겠습니다. 다음과 같이 `click` 이벤트로 업데이트하세요.

```svelte
<button type="button" class="btn btn\_\_danger"
 on:click={() => removeTodo(todo)}
>
  Delete <span class="visually-hidden">{todo.name}</span>
</button>
```

Svelte의 핸들러에서 매우 흔한 실수는 함수를 전달하는 대신 함수 실행 결과를 핸들러로 전달하는 것입니다. 예를 들어 `on:click={removeTodo(todo)}`를 지정하면 `removeTodo(todo)`가 실행되고 그 결과가 핸들러로 전달되는데 이는 우리가 생각한 것과는 다릅니다.
 이 경우 `on:click={() => removeTodo(todo)}`를 핸들러로 지정해야 합니다. `removeTodo()`가 매개변수를 받지 못한 경우 `on:event={removeTodo}`를 사용할 수 있지만 `on:event={removeTodo()}`는 사용할 수 없습니다. 이것은 특별한 Svelte 구문이 아닙니다. 여기서는 일반 JavaScript 화살표 함수를 사용하고 있습니다.

다시 말하지만 이것은 좋은 진전입니다. 이 시점에서 이제 작업을 삭제할 수 있습니다. 할 일 항목의 *Delete* 버튼을 누르면 관련 할 일이 `todos` 배열에서 제거되고 더 이상 표시되지 않도록 UI가 업데이트됩니다. 또한 이제 확인란을 선택할 수 있으며 관련 할 일의 완료된 상태가 이제 `todos` 배열에서 업데이트됩니다.

그러나 "y개 항목 중 x개 완료" 제목이 업데이트되지 않습니다. 왜 이런 일이 발생하고 어떻게 해결할 수 있는지 알아보려면 계속 읽어보세요.

반응형 할 일 목록
----------

이미 본 것처럼 컴포넌트 최상위 변수의 값이 수정될 때마다 Svelte는 UI를 업데이트하는 방법을 알고 있습니다. 우리 앱에서 `todos` 배열 값은 할 일이 토글되거나 삭제될 때마다 직접 업데이트되므로 Svelte는 자동으로 DOM을 업데이트합니다.

그러나 `totalTodos` 및 `completedTodos`의 경우에는 그렇지 않습니다. 다음 코드에서는 컴포넌트가 인스턴스화되고 스크립트가 실행될 때 값이 할당되지만 그 후에는 해당 값이 수정되지 않습니다.

```js
let totalTodos = todos.length;
let completedTodos = todos.filter((todo) => todo.completed).length;
```

할 일을 토글링하고 제거한 후에 다시 계산할 수 있지만 더 쉬운 방법이 있습니다.

Svelte에게 `totalTodos` 및 `completedTodos` 변수에 `$:` 접두사를 붙여서 반응형이 되기를 원한다고 말할 수 있습니다. Svelte는 의존하는 데이터가 변경될 때마다 자동으로 업데이트하는 코드를 생성합니다.

**참고:** Svelte는 `$:` JavaScript 레이블 문 구문을 사용하여 반응형 구문을 표시합니다. props를 선언하는 데 사용되는 `export` 키워드처럼, 이것은 약간 낯설게 보일 수 있습니다. 이것은 Svelte가 유효한 JavaScript 구문을 활용하고 새로운 목적을 부여하는 또 다른 예입니다. 이 경우 "참조된 값이 변경될 때마다 이 코드를 다시 실행"하는 것을 의미합니다. 한 번 익숙해지면, 모르던 때로 돌아갈 수 없을 것입니다.

`src/components/Todos.svelte` 내부의 `totalTodos` 및 `completedTodos` 변수 정의를 다음과 같이 업데이트하세요.

```js
$: totalTodos = todos.length;
$: completedTodos = todos.filter((todo) => todo.completed).length;
```

이제 앱을 확인하면 할 일이 완료되거나 삭제될 때 제목의 숫자가 업데이트되는 것을 볼 수 있습니다. 멋지네요!

Svelte 컴파일러는 배후에서 종속성 트리를 만들기 위해 코드를 파싱하고 분석한 다음 종속성 중 하나가 업데이트될 때마다 각 반응 상태을 재평가하는 JavaScript 코드를 생성합니다. Svelte의 반응성은 리스너(listeners), 세터(setters), 게터(getters) 또는 기타 복잡한 메커니즘을 사용하지 않고 매우 가볍고 성능이 뛰어난 방식으로 구현됩니다.

새로운 할 일 추가
----------

이제 문서의 다음 주요 작업입니다. 새 할 일을 추가하는 몇 가지 기능을 추가해 보겠습니다.

1. 먼저 새 할 일의 텍스트를 저장할 변수를 만듭니다. `Todos.svelte` 파일의 `<script>` 섹션에 다음 선언을 추가합니다.

```js
let newTodoName = "";
```

2. 이제 `<input>`에서 이 값을 사용하여 새 작업을 추가합니다. 그렇게 하려면 `newTodoName` 변수를 `todo-0` 입력에 바인딩해야 `newTodoName` 변수 값이 입력의 `value` 속성과 동기화 상태를 유지합니다. 다음과 같이 할 수 있습니다.

```svelte
<input value={newTodoName} on:keydown={(e) => newTodoName = e.target.value} />
```

변수 `newTodoName`의 값이 변경될 때마다 입력의 `value` 속성에 반영되며 입력에서 키를 누를 때마다 변수 `newTodoName`의 내용을 업데이트합니다.
 이는 입력에 대한 양방향 데이터 바인딩을 수동으로 구현한 것입니다. 하지만 이렇게 할 필요는 없습니다 왜냐하면 Svelte는 `bind:property` 지시문을 사용하여 모든 속성을 변수에 바인딩하는 더 쉬운 방법을 제공합니다.

```svelte
<input bind:value={newTodoName} />
```

자, 이것을 구현해 봅시다. 다음과 같이 `todo-0` 입력을 변경하세요.

```svelte
<input
 bind:value={newTodoName}
 type="text"
 id="todo-0"
 autocomplete="off"
 class="input input\_\_lg" />
```

3. 이것이 동작하는지 테스트하는 쉬운 방법은 `newTodoName`의 내용을 기록하는 반응형 구문을 추가하는 것입니다. `<script>` 섹션 끝에 이 스니펫을 추가합니다.

```js
$: console.log("newTodoName: ", newTodoName);
```

**참고:** 아시다시피 반응형 구문은 변수 선언으로 제한되지 않습니다. `$:` 기호 뒤에 *any* JavaScript 문을 넣을 수 있습니다.
4. 이제 `localhost:5042`로 돌아가서 `Ctrl` + `Shift` + `K`를 눌러 브라우저 콘솔을 열고 무언가를 입력하세요. 입력 필드. 항목이 기록된 것을 볼 수 있습니다. 이 시점에서 원하는 경우 반응형 `console.log()`를 삭제할 수 있습니다.
5. 다음으로 새로운 할 일을 추가하는 함수인 `addTodo()`를 만들겠습니다. 이 함수는 새로운 `todo` 객체를 `todos` 배열로 푸시합니다. `src/components/Todos.svelte` 내부의 `<script>` 블록 맨 아래에 다음을 추가합니다.

```js
function addTodo() {
  todos.push({ id: 999, name: newTodoName, completed: false });
  newTodoName = "";
}
```

**참고:** 지금은 모든 할 일에 동일한 `id`를 할당하지만 걱정하지 마세요. 곧 수정할 것입니다.
6. 이제 폼이 제출(submit)될 때마다 `addTodo()`를 호출하도록 HTML을 업데이트하려고 합니다. NewTodo 폼의 여는 태그를 다음과 같이 업데이트하세요.

```svelte
<form on:submit|preventDefault={addTodo}>
```

`on:eventname` 지시문은 `|` 문자를 사용하여 DOM 이벤트에 수식어를 추가하는 것을 지원합니다. 이 경우 `preventDefault` 수식어는 핸들러를 실행하기 전에 `event.preventDefault()`를 호출하는 코드를 생성하도록 Svelte에 지시합니다. 사용 가능한 다른 수식어를 보려면 이전 링크를 살펴보세요.
7. 이 시점에서 새 할 일을 추가하려고 하면 새 할 일은 할 일 배열에 추가되지만 UI는 업데이트되지 않습니다. Svelte에서는 반응성은 할당으로 트리거됨을 기억하세요. 즉, `addTodo()` 함수가 실행되고 요소가 `todos` 배열에 추가되지만 Svelte는 push 메서드가 배열을 수정했음을 감지하지 못하므로 작업 `<ul>`을 새로 고치지 않습니다.
 `addTodo()` 함수의 끝에 `todos = todos`를 추가하면 문제가 해결되지만, 함수의 끝에 포함시켜야 하는 것이 이상해 보입니다. 대신 `push()` 메서드를 제거하고 확산 구문을 사용하여 동일한 결과를 얻습니다. `todos` 배열에 새로운 개체를 추가한 것과 동일한 `todos` 배열을 할당합니다.

**참고:** `Array`에는 여러 가지 변경 연산자가 있습니다. `push()`, `pop()`, `splice()`, `shift()`, `unshift()`, `reverse()` 및 `sort()`. 연산자를 사용하면 종종 추적하기 어려운 부작용과 버그가 발생합니다. 확산 구문을 사용하여 `push()`로 배열 변경을 피하는 것은 좋은 사례로 간주됩니다.

`addTodo()`를 다음과 같이 업데이트합니다.

```js
function addTodo() {
  todos = [...todos, { id: 999, name: newTodoName, completed: false }];
  newTodoName = "";
}
```

각 할 일에 고유한 ID 부여
----------------

지금 앱에 새 할 일을 추가하려고 하면 새 할 일을 추가하고 UI에 한 번 표시할 수 있습니다. 두 번째 시도하면 작동하지 않고 "오류: 키는 중복 될 수 없습니다"라는 콘솔 메시지가 표시됩니다. 할 일에 대한 고유한 ID가 필요합니다.

1. 할 일의 수에 1을 더한 값으로 계산된 `newTodoId` 변수를 선언하고 반응형으로 만들어 봅시다. `<script>` 섹션에 다음 스니펫을 추가합니다.

```js
let newTodoId;
$: {
  if (totalTodos === 0) {
    newTodoId = 1;
  } else {
    newTodoId = Math.max(...todos.map((t) => t.id)) + 1;
  }
}
```

**참고:** 보시다시피 반응형 구문은 한 줄로 제한되지 않습니다. 다음 구문은 정상 작동하지만 가독성이 약간 떨어집니다. `$: newTodoId = totalTodos ? Math.max(...todos.map((t) => t.id)) + 1 : 1`
2. Svelte는 어떻게 이것이 가능할까요? 컴파일러는 전체 반응형 구문을 파싱하고 `totalTodos` 변수와 `todos` 배열에 의존하는 것을 감지합니다. 따라서 둘 중 하나가 수정될 때마다 이 코드가 재평가되고 그에 따라 `newTodoId`가 업데이트됩니다.
 이것을 `addTodo()` 함수에서 사용해봅시다. 다음과 같이 업데이트하세요.

```js
function addTodo() {
  todos = [...todos, { id: newTodoId, name: newTodoName, completed: false }];
  newTodoName = "";
}
```

상태별로 할 일 필터링
------------

이 문서의 마지막은 할 일을 상태별로 필터링하는 기능을 구현해 보겠습니다. 현재 필터를 저장할 변수와 필터링된 할 일을 반환하는 도우미 함수를 만듭니다.

1. `<script>` 섹션 하단에 다음을 추가합니다.

```js
let filter = "all";
const filterTodos = (filter, todos) =>
  filter === "active"
    ? todos.filter((t) => !t.completed)
    : filter === "completed"
      ? todos.filter((t) => t.completed)
      : todos;
```

활성 필터(*all*, *active* 또는 *completed*)를 제어하기 위해 `filter` 변수를 사용합니다. 이 값 중 하나를 필터 변수에 할당하기만 하면 필터가 활성화되고 할 일 목록이 업데이트됩니다. 이를 달성하는 방법을 살펴보겠습니다.
 'filterTodos()' 함수는 현재 필터와 할 일 목록을 수신하고 그에 따라 필터링된 할 일의 새 배열을 반환합니다.
2. 필터 버튼 마크업을 변경하여 동적으로 만들고 사용자가 필터 버튼 중 하나를 누를 때 필터 상태를 변경하겠습니다. 다음과 같이 변경하세요.

```svelte
<div class="filters btn-group stack-exception">
  <button class="btn toggle-btn" class:btn\_\_primary={filter === 'all'} aria-pressed={filter === 'all'} on:click={() => filter = 'all'} >
    <span class="visually-hidden">Show</span>
    <span>All</span>
    <span class="visually-hidden">tasks</span>
  </button>
  <button class="btn toggle-btn" class:btn\_\_primary={filter === 'active'} aria-pressed={filter === 'active'} on:click={() => filter = 'active'} >
    <span class="visually-hidden">Show</span>
    <span>Active</span>
    <span class="visually-hidden">tasks</span>
  </button>
  <button class="btn toggle-btn" class:btn\_\_primary={filter === 'completed'} aria-pressed={filter === 'completed'} on:click={() => filter = 'completed'} >
    <span class="visually-hidden">Show</span>
    <span>Completed</span>
    <span class="visually-hidden">tasks</span>
  </button>
</div>
```

이 마크업에는 몇 가지 진행 중인 것이 있습니다.
 활성 필터 버튼에 `btn__primary` 클래스를 적용하여 현재 필터를 표시합니다. 스타일 클래스를 요소에 조건부로 적용하려면 `class:name={value}` 지시문을 사용합니다. 값 표현식이 참으로 평가되면 클래스 이름이 적용됩니다. 조건이 다른 여러 지시문을 동일한 요소에 추가할 수 있습니다. 따라서 `class:btn__primary={filter === 'all'}`을 실행할 때 Svelte는 filter가 all인 경우 `btn__primary` 클래스를 적용합니다.

**참고:** Svelte는 클래스가 변수 이름과 일치할 때 `<div class:active={active}>`를 `<div class:active>`로 축소할 수 있는 바로 가기를 제공합니다.

`aria-pressed={filter === 'all'}`에서도 비슷한 일이 발생합니다. 중괄호 사이에 전달된 JavaScript 표현식이 참으로 값으로 평가되면 `aria-pressed` 속성이 버튼에 추가됩니다.
 버튼을 클릭할 때마다 `on:click={() => filter = 'all'}`을 실행하여 필터 변수를 업데이트합니다. Svelte 반응성이 이 외의 나머지를 처리하는 방법을 알아 보려면 계속 읽어보세요.
3. 이제 `{#each}` 루프에서 도우미 함수를 사용해야 합니다. 다음과 같이 업데이트하세요.

```svelte
…
  <ul role="list" class="todo-list stack-large" aria-labelledby="list-heading">
  {#each filterTodos(filter, todos) as todo (todo.id)}
…
```

코드를 분석한 후 Svelte는 `filterTodos()` 함수가 `filter` 및 `todos` 변수에 의존한다는 것을 감지합니다. 그리고 마크업에 포함된 다른 동적 표현식과 마찬가지로 이러한 종속성이 변경될 때마다 그에 따라 DOM이 업데이트됩니다. 따라서 `filter` 또는 `todos`가 변경될 때마다 `filterTodos()` 함수가 재평가되고 루프 내부의 항목이 업데이트됩니다.

**참고:** 때때로 반응성이 까다로울 수 있습니다. Svelte는 `filterTodos(filter, todo)` 표현식에서 참조하기 때문에 `filter`를 종속성으로 인식합니다. `filter`는 최상위 변수이므로 도우미 함수 params에서 제거하고 `filterTodos(todo)`와 같이 호출하고 싶을 수도 있습니다. 이것은 작동하지만 이제 Svelte는 `{#each filterTodos(todos) }`가 `filter`에 의존하고 필터가 변경될 때 필터링된 할 일 목록이 업데이트되지 않는다는 것을 알아낼 방법이 없습니다. Svelte는 종속성을 찾기 위해 코드를 분석하므로 이에 대해 명시하고 최상위 변수의 가시성에 의존하지 않는 것이 좋습니다. 게다가 어떤 정보를 사용하고 있는지에 대해 코드를 명확하고 명시적으로 만드는 것이 좋습니다.

지금까지의 코드
--------

### Git

이 문서의 끝에서 코드의 상태를 확인하려면 다음과 같이 저장소 사본에 접근하세요.

```bash
cd mdn-svelte-tutorial/04-componentizing-our-app
```

또는 폴더의 콘텐츠를 직접 다운로드합니다.

```bash
npx degit opensas/mdn-svelte-tutorial/04-componentizing-our-app
```

`npm install && npm run dev` 명령어로 실행하여 개발 모드에서 앱을 실행해주세요.

### REPL

REPL에서 코드의 현재 상태를 보려면 다음에 방문하십시오.

https://svelte.dev/repl/99b9eb228b404a2f8c8959b22c0a40d3?version=3.23.2

Summary
-------

이 정도면 충분합니다! 이 문서에서 우리는 이미 우리가 원하는 대부분의 기능을 구현했습니다. 우리의 앱은 할 일을 표시, 추가 및 삭제하고, 완료된 상태를 전환하고, 완료한 작업 수를 표시하고, 필터를 적용할 수 있습니다.

요약하자면 다음 주제를 다뤘습니다.

* 컴포넌트 생성 및 사용
* 정적 마크업을 라이브 템플릿으로 전환
* 마크업에 JavaScript 표현식 포함
* `{#each}` 지시문을 사용하여 목록 순회
* props로 컴포넌트 간 정보 전달
* DOM 이벤트 처리
* 반응형 구문 선언
* `console.log()` 및 반응형 구문을 사용한 기본 디버깅
* `bind:property` 지시어로 HTML 속성 바인딩
* 과제에 대한 반응성 유발
* 반응식을 사용하여 데이터 필터링
* 반응형 종속성을 명시적으로 정의

우리는 사용자가 할 일을 편집할 수 있는 기능을 더 추가할 것입니다.

* 이전
* Overview: Client-side JavaScript frameworks
* 다음  (en-US)


JavaScript 첫걸음 - Web 개발 학습하기

JavaScript 첫걸음
==============

우리의 첫 번째 JavaScript 과정에서는, 현실적인 첫 JavaScript 예제 작성에 앞서 "JavaScript란 뭘까", "JavaScript는 어떻게 생겼을까", "JavaScript는 뭘 할 수 있을까"와 같은 핵심적인 질문에 답을 하겠습니다. 그다음으로는 변수, 문자열, 숫자, 배열과 같은 핵심 개념을 자세히 논의해 보겠습니다.

#### 프론트엔드 웹 개발자가 되고자 하시나요?

목표를 달성하기 위한 모든 필수 정보가 들어있는 교육 과정을 준비했습니다.

**시작하기**

필요한 사전 지식
---------

JavaScript를 몰라도 이번 과정 시작에는 문제가 없지만, HTML과 CSS는 어느 정도 친숙해야 합니다. JavaScript를 시작하기 이전에 우선 다음 과정부터 진행하시는 게 좋습니다.

* Web과 함께 시작하기 (아주 기본적인 JavaScript 소개도 포함)
* HTML 소개
* CSS 소개

**참고:** 파일을 만들 수 없는 컴퓨터나 태블릿, 기타 장치에서 작업을 하신다면 예제 코드의 대부분을 JSBin이나 Glitch 같은 온라인 코딩 프로그램에서도 시험할 수 있습니다.

구성
--

JavaScript가 뭔가요?

MDN의 JavaScript 초급자 과정에 오신 걸 환영합니다! 첫 글은 JavaScript를 넓게 보면서 "뭔가요?", "뭘 하나요?"와 같은 질문을 답변하고, 여러분이 JavaScript에 친숙해지도록 도와드립니다.

JavaScript에 발 담그기

JavaScript에 관한 이론과 용도를 약간 배우셨으니, 실용적인 따라해보기와 함께 하는 기초과정 단기 특강을 제공해드리겠습니다. 여기서는 간단한 "숫자 알아맞히기" 게임을 단계별로 차근차근 만들어봅니다.

뭐가 잘못됐을까요? JavaScript 문제 해결

이전 글에서 "숫자 알아맞히기" 게임을 만든 뒤에, 프로그램이 돌아가지 않는다는 것을 발견하실 겁니다. 두려워하지 마세요. 세 번째 글에서 JavaScript 프로그램의 오류를 찾고 고치는 팁을 배울 수 있습니다.

필요한 정보 저장하기 — 변수

앞선 글을 모두 읽으셨으면 이제 JavaScript가 뭔지, 뭘 할 수 있는지, 다른 웹 기술과 함께 어떻게 사용하는지, 주요 기능이 어떻게 생겼는지 등 넓은 윤곽을 그리실 수 있을겁니다. 이제 진짜 기초로 돌아가, JavaScript의 제일 기본적인 구성 요소를 어떻게 사용하는지 알아보겠습니다. 바로 변수입니다.

JavaScript의 기본 수학 — 숫자와 연산자

여기서는 JavaScript에서의 수학을 이야기하면서, 연산자와 다른 기능을 이리 저리 조합하여 숫자를 여러분의 뜻에 맞게 바꾸는 법을 배웁니다.

텍스트 다루기 — 문자열

우리의 시선을 이제 문자열로 돌릴 차례입니다. 프로그래밍에서 텍스트를 일컫는 단어죠. 이번 글에서는 JavaScript를 배울 때 문자열에 대해 알아둬야 하는 것, 예를 들어 문자열 생성, 문자열 안의 따옴표 처리, 문자열 합치기 등을 알아봅니다.

유용한 문자열 메서드

문자열의 기초를 탐구했으니, 한 단계 더 나아가 문자열의 길이 찾기, 문자열 합치고 나누기, 문자를 다른 문자로 치환하기 등 내장 메서드로 할 수 있는 유용한 작업에 대해 생각해봅니다.

배열

본 과정의 마지막 글에서는 배열에 대해 알아봅니다. 배열은 하나의 변수 이름 아래에 여러 데이터 목록을 저장하는 깔끔한 방법입니다. 배열이 왜 유용한지, 배열을 어떻게 생성하는지, 저장한 데이터를 회수하고, 추가하고, 제거하는 법 등을 배웁니다.

평가
--

다음 평가를 통해 JavaScript의 기초를 얼마나 이해했는지 시험할 수 있습니다.

바보같은 이야기 생성기

각자 배운 내용을 돌이켜보면서, 바보같은 이야기를 무작위로 만들어내는 앱을 제작해야 합니다. 행운을 빌어요!

같이 보기
-----

Learn JavaScript

성장하려는 웹 개발자에게 완벽한 자료입니다. 상호작용형 환경에서 자동화 평가의 안내를 받으며 짧은 강의들과 시험을 통해 JavaScript를 배워보세요. 처음 40개의 강의는 무료고, 전체 코스는 저렴한 1회 결제로 볼 수 있습니다.


JavaScript 구성요소 - Web 개발 학습하기

JavaScript 구성요소
===============

이번 과정에서는 조건문, 반복문, 함수, 이벤트 등 일반적으로 발생하는 코드 종류를 중심으로 JavaScript의 중요한 기본 기능에 대해 설명합니다. 지금까지의 과정을 지나면서 여기서 다룰 내용을 살짝 보셨겠지만 좀 더 심도있게 다룰 것입니다.

#### 프론트엔드 웹 개발자가 되기를 생각해보고 계신가요?저희는 여러분의 목표를 향해 공부할 필요가 있는 모든 필수적인 정보를 담고 있는 코스를 준비해 놓았습니다.**시작하기**

필요한 사전 지식
---------

시작하기 전에, 기본적인 HTML과 CSS 지식을 알고 계셔야 하고, 또한 지난 모듈인 JavaScript 첫걸음을 학습하셔야 합니다.

**참고:** 여기 나온 코드를 작성하고 실행해 볼 수 없는 환경이라면 (태블릿, 스마트폰, 기타 장치), JSBin이나 Glitch에서 대부분의 예제를 시험해 볼 수 있습니다.

가이드
---

판단 내리기 — 조건문

어떤 프로그래밍 언어든 코드는 의사 결정을 내리고 입력 내용에 따라 작업을 수행해야 합니다. 예를 들어 게임에서 플레이어의 생명 수치가 0이면 게임이 종료됩니다. 날씨 앱에서는 아침에 해가 뜬 그림을 보여주고 밤에는 달과 별을 보여줍니다. 이 문서에서는 JavaScript에서 조건문이 작동하는 방법을 살펴 보겠습니다.

반복문

때때로 한 번보다 많이 연이어 작업을 완료해야 할 필요가 있습니다. 예를 들자면, 이름 목록을 순회하는 것입니다. 프로그래밍에서, 반복문은 이 작업을 대단히 잘 수행합니다. 여기서 우리는 JavaScript에서의 반복문 구조를 살펴볼 것입니다.

함수 — 코드 재사용

코딩에서의 또 다른 필수적인 개념은 **함수**입니다. **함수**를 사용하면 정의된 블록 안에 하나의 작업을 수행하는 코드 조각을 저장할 수 있고, 언제든지 그 코드 조각이 필요할 때 같은 코드를 여러번 입력하지 않고도 짧은 명령을 사용해 그 코드를 호출할 수 있습니다. 이 문서에서는 기본 문법, 어떻게 함수를 호출하고 정의하는지, 스코프, 그리고 매개변수와 같은 함수의 기본 개념을 탐사할 것입니다.

함수 만들기

그동안 배운 이론을 활용해, 이 문서에서는 실용적인 경험을 제공합니다. 여기서 여러분은 사용자 정의 함수를 만드는 연습을 할 것입니다. 그 과정에서, 우리는 또한 함수를 다루는 데 있어 유용한 추가적인 세부 사항을 알아볼 것입니다.

함수 반환 값

함수에 대해 알아야 할 마지막 필수 개념은 반환 값입니다. 어떤 함수는 완료 후에 중요한 값을 반환하지 않지만, 반환하는 함수도 있습니다. 그 값이 무엇인지, 어떻게 사용하는지, 그리고 어떻게 유용한 값들을 반환하는 사용자 정의 함수를 만드는지를 이해하는 것은 중요합니다.

이벤트 입문

이벤트란 여러분이 프로그래밍하고 있는 시스템에서 발생하는 동작이나 발생인데, 이는 시스템이 여러분에게 알려주므로 만약 원한다면 어떠한 방식으로 이에 응답할 수 있습니다. 예를 들어 만약 사용자가 웹페이지의 버튼을 클릭한다면, 여러분은 그 동작에 응답하여 인포메이션 박스를 표시함으로써 응답하기를 원할지도 모릅니다. 이 자미가 문서에서 우리는 이벤트에 대한 중요한 개념을 논하고, 이벤트가 브라우저에서 어떻게 동작하는지 살펴볼 것입니다.

평가
--

다음의 평가는 위의 가이드에서 다뤄진 기본 JavaScript에 대한 여러분의 이해를 테스트할 것입니다.

이미지 갤러리

이제 JavaScript의 기본 구성 요소를 살펴 보았으므로, 많은 웹 사이트에서 볼 수 있는 상당히 공통적인 항목인 JavaScript 기반 이미지 갤러리를 구현하며 반복문, 함수, 조건문, 그리고 이벤트에 대한 여러분의 지식을 테스트할 것입니다.

같이 보기
-----

Learn JavaScript

웹 개발자가 되려는 분들에게 훌륭한 자원입니다 — Learn JavaScript는 자동화된 평가에 의해 가이드되며, 짧은 강의와 상호작용을 하는 테스트를 가진 대화형 환경입니다. 처음 40개의 강의는 무료이며, 전체 코스는 한 번의 작은 지불로 이용 가능합니다.


JavaScript 객체 소개 - Web 개발 학습하기

JavaScript 객체 소개
================

JavaScript에서는 배열과같은 기능부터 JavaScript 위에 구축된 브라우저 APIs에 이르기까지 대부분의 것들이 객체입니다. 사용자는 관련된 함수들과 변수들을 효율적인 패키지로 추상화하거나 편리한 데이터 컨테이너로 작동하는 객체를 만들 수 있습니다. 언어에 대한 지식을 가지고 더 멀리 나아고자 한다면 JavaScript의 객체 기반의 본질을 이해하는 것이 중요하므로, 여러분을 돕기위해 이 모듈을 제공하였습니다. 여기에서는 객체 이론과 구문을 자세히 가르쳐드리며, 사용자 정의 객체를 만드는 방법도 알아볼 것입니다.

#### 프론트엔드 웹 개발자가 되고자 하시나요?목표를 달성하기 위한 모든 필수 정보가 들어있는 교육 과정을 준비했습니다.**시작하기**

사전 지식
-----

이 장을 읽기 전에, 여러분은 HTML 과 CSS 를 어느 정도 알고 있어야 합니다. 그렇지 않다면 HTML 소개 (en-US)와 CSS 소개를 먼저 읽고 오시기 바랍니다.

JavaScript 객체에 대해 자세히 알아보려면, 기본 문법에 대해 어느 정도 능숙해야 합니다. 이 장을 읽기 전에 JavaScript 첫걸음과 JavaScript 구성 요소를 먼저 읽어보시기를 바랍니다.

**참고:** 컴퓨터/태블릿/혹은 다른 디바이스 상에서 스스로 파일을 만들수 없는 환경이라면, JSBin 또는 Thimble 과 같은 온라인 코딩 프로그램을 이용하여 (거의 모든) 예제 코드를 테스트해 보실 수 있습니다.

가이드
---

객체 기본

JavaScript 객체 소개의 첫 장에서는, 기본적인 JavaScript 객체 문법을 볼 것이며, 이전 과정에서 이미 봐왔던 몇몇 JavaScript 특징들을 다시 들여다 볼 것입니다. 이를 통해 우리는 이전에 다루었던 많은 특징들이 사실상 객체였다는 사실을 알게 될 것입니다.

입문자를 위한 객체지향 JavaScript

가장 기본적인 것으로, 우리는 객체 지향 JavaScript(OOJS) 에 초점을 둘 것입니다. 이 글에서는 객체 지향 프로그래밍(OOP) 이론의 기본 관점을 보여주고, JavaScript 가 생성자함수를 이용하여 객체 클래스를 에뮬레이트 하는 방법과 객체 인스턴스를 생성하는 방법에 대해 탐구합니다.

객체 프로토타입

프로토타입은 JavaScript 객체가 또다른 객체로 특성을 상속시키기 위한 메커니즘이고, 그것들은 고전적인 객체 지향 프로그래밍 언어들의 상속 메커니즘과 다르게 작동합니다. 이 글에서는 그 다름을 탐구하고, 프로토타입 체인이 동작하는 방식을 설명하며 프로토타입의 속성이 기본 생성자들로 메소드를 추가하기 위해 사용되는 방법을 보여줍니다.

JavaScript에서의 상속

이제 OOJS 에 대한 대부분의 세부사항들에 대해서 설명이 되었으므로, 본 장에서는 "부모" 클래스로부터 특성을 상속받은 "자식" 오브젝트 클래스를 생성하는 방법을 보여줍니다. 뿐만 아니라, OOJS 를 언제 또는 어디서 사용하면 좋을 지에 대한 몇몇 조언도 기술되어있습니다.

JSON 데이터와 작업

JSON(JavaScript Object Notation) 은 JavaScript 객체 문법상의 구조화된 데이터를 표현하기 위한 표준 텍스트기반 포맷입니다. 그래서 주로 웹사이트 상에 데이터를 표현하고 전송하는 데 사용되고 있습니다(예를 들면, 웹페이지 상에 데이터를 디스플레이할 수 있도록 서버에서 클라이언트로의 데이터 전송). 이 글에서는 데이터에 접근하기 위해 JSON 을 파싱하고, 또 자신만의 JSON 을 작성하는 것처럼 JavaScript를 사용하여 JSON 과 연동할 필요가 있는 모든 것들을 보여드릴 것입니다.

객체 생성 실습 (en-US)

이전 글들에서 우리는 JavaScript 객체 이론과 문법 세부사항의 중요한 것들을 보았습니다. 본 장에서는 커스텀 JavaScript 객체를 생성하는 다양한 연습을 하면서 실용적인 예들을 깊이 다룹니다.

평가
--

Bouncing balls demo 에 기능들 추가 (en-US)

이 평가에서는 여러분이 이전 글의 bouncing balls demo 를 시작점으로 하여, 몇 가지 새롭고 재미있는 기능들을 추가하길 기대합니다.

같이 보기
-----

JavaScript 배우기

야심찬 웹 개발자를 위한 훌륭한 리소스 — 간단한 강의와 대화형 테스트, 자동화된 평가를 통해 대화형 환경에서 JavaScript를 배우십시오. 처음 40 개의 강의는 무료이며 전체 과정은 소액의 일회성 지불로 이용할 수 있습니다.


Client-side web APIs - Web 개발 학습하기

Client-side web APIs
====================

웹 사이트 또는 응용 프로그램 용 client-side JavaScript를 작성할때 API (응용 프로그램 프로그래밍 인터페이스)를 사용하지 않으면 많은것을 만들기 어렵습니다. API는 사이트가 실행되고 있는 브라우저와 운영 체제의 다양한 부분 그리고 웹 사이트 혹은 서비스의 데이터를 다룰 수 있는 인터페이스입니다. 이번장에서 우리는 API가 무엇인지, 그리고 개발할 때 자주 접하게 될 보편적인 API를 사용하는 방법에 대해 살펴볼 것입니다.

선행사항
----

다음의 문서를 미리 보고 오시는것을 권장합니다. (First steps, Building blocks, and JavaScript objects). 이 모듈은 클라이언트 측 JavaScript 예제를 사용하지 않고 유용하게 사용할 수 있기 때문에 간단한 API 사용법을 상당히 많이 사용합니다. 여기에서는 핵심 자바 스크립트 언어에 대한 지식이 있다고 가정하고 일반적인 웹 API를 좀 더 자세하게 살펴보면서 한 단계 올라갑니다. 이번장에서는 심플한 API사용방법을 여럿 보도록 하겠습니다. 이 API들은 client-side JavaScript를 만드는데 도움이 많이 될 것입니다.

HTM과CSS에 관한 기본지식이 있으면 좋습니다!

**참고:** 코드를 작성 할 수 없는 디바이스에서 작업하는 경우 JSBin 또는 Thimble.과 같은 온라인 코딩 프로그램에서 코드 예제를 시험해 볼 수 있습니다.

Guides
------

Introduction to web APIs (en-US)

우선, 높은 수준의 API를 먼저 살펴볼 것입니다. API는 무엇이며, 어떻게 작동하며, 코드에서 어떻게 사용하고 구조화되어 있는지 확인합니다. 또한 API의 다른 주요 클래스가 무엇인지, 그리고 용도가 무엇인지 살펴볼 것입니다.

Manipulating documents (en-US)

웹 페이지와 앱을 작성할 때 많이 하는 것중 하나는 웹 문서를 다루는 것입니다. 보통 `Document` 객체를 많이 사용하는 HTML 및 스타일 정보를 제어하기위한 API 집합 인 DOM (Document Object Model)을 사용합니다. 여기서는 흥미로운 방식으로 환경을 변경할 수 있는 API와 함께 DOM을 사용하는 방법에 대해 자세히 설명합니다.

Fetching data from the server (en-US)

최신 웹 사이트 및 응용 프로그램에서 필요한 작업중 하나는 완전히 새로운 페이지를 로드 할 필요없이 서버에서 개별 데이터를 가져와 웹 페이지의 일부분만 업데이트하는 것입니다. 이렇게 하면 사이트의 성능과 동작에 큰 영향을 줍니다. 여기서는 이 개념을 설명하고 `XMLHttpRequest`나 Fetch API와 같은 기술을 살펴 보겠습니다

Third party APIs (en-US)

지금까지 살펴본 API는 브라우저에 내장되어 있지만 모든 API가 지원되지는 않습니다. 예를들면 Google Maps, Twitter, Facebook, PayPal 등과 같은 많은 대형 웹 사이트와 서비스가 있고 여기에는 개발자 자신의 데이터 (예 : 블로그에 트위터 스트림 표시) 또는 서비스 (예 : 사이트에 맞춤 Google지도 표시, Facebook 로그인을 사용하여 사용자 로그인)가 있습니다.여기서는 브라우저 API와 타사 API의 차이점을 살펴본 후 일반적인 API 사용법을 보겠습니다.

Drawing graphics (en-US)

브라우저에는 SVG (Scalable Vector Graphics) 언어부터 HTML `<canvas>` 요소 (The Canvas API 및 WebGL 참조)를 그리는 데 필요한 API에 이르기까지 매우 강력한 그래픽 프로그래밍 도구가 포함되어 있습니다. 여기서는 Canvas API에 대한 소개와 더 많은 정보를 얻을 수있는 추가 자료를 제공합니다.

Video and audio APIs (en-US)

HTML5에는 문서 `<video>` 및 `<audio>` (en-US)에 미디어를 포함하기위한 요소가 포함되어 있으며 재생, 탐색 등을 제어하는 자체 API가 제공됩니다. 이문서에서는 사용자 커스텀 재생 컨트롤 만들기와 같은 일반적인 작업을 수행하는 방법을 보여줍니다.

Client-side storage (en-US)

최신 웹 브라우저에는 웹 사이트와 관련된 데이터를 저장하고 필요할 때 검색하여 장기간 데이터를 유지하고 오프라인으로 사이트를 저장하는 등 다양한 기술을 사용할 수 있습니다. 이 기사에서는 이러한 작업 방식에 대한 기본적인 내용을 설명합니다.


Solve common problems in your JavaScript code - Web 개발 학습하기

Solve common problems in your JavaScript code
=============================================

다음의 링크들은 당신의 JavaScript 코드가 정상적으로 작동할 수 있게 고쳐야되는 흔한 문제들의 해결책을 제시한다.

초보자들의 흔한 실수들
------------

### 스펠링과 대소문자를 제대로 해라

코드가 작동하지 않거나 브라우저가 무언가가 undefined라고 불평하면 모든 변수 이름, 함수 이름 등을 정확하게 입력했는지 확인하십시오.

문제를 일으키는 몇 가지 일반적인 내장 브라우저 함수는 다음과 같습니다.

| 올바른 | 잘못된 |
| --- | --- |
| `getElementsByTagName()` | `getElementbyTagName()` |
| `getElementsByName()` | `getElementByName()` |
| `getElementsByClassName()` | `getElementByClassName()` |
| `getElementById()` | `getElementsById()` |

### 세미콜론의 위치

세미콜론의 위치가 잘못 되지 않았는지 확인하세요. 예를들어:

| 올바른 | 잘못된 |
| --- | --- |
| `elem.style.color = 'red';` | `elem.style.color = 'red;'` |

### 함수

함수와 관련하여 실수할 수 있는 것들이 많이 있습니다.

가장 흔한 실수는 함수를 선언하고 어디서도 호출하지 않는 것입니다. 예를 들어:

```js
function myFunction() {
  alert("This is my function.");
}
```

위 코드는 호출하지 않는 이상 아무것도 하지 않습니다.

```js
myFunction();
```

#### 함수 스코프

함수는 각자의 스코프가 있음을 명심하세요 — 변수를 전역으로 선언하거나 함수에서 값을 리턴하지 않는한 함수 밖에서 함수 내부에 설정된 변수 값에 접근 할 수 없습니다. (즉, 어떠한 함수 내에서도 접근 안됨)

#### 리턴 문 이후 코드 실행

함수 밖으로 값을 리턴할 때, JavaScript 인터프리터가 함수를 완전히 빠져나감을 명심하세요. — 리턴 문 이후에 선언된 코드는 절대로 실행되지 않습니다.

사실, 몇몇 브라우저들은 (Firefox 처럼) 리턴 문 이후에 코드가 있다면 개발자 콘솔에 에러 메세지를 줍니다. Firefox는 "unreachable code after return statement" 라고 알려줍니다.

### 오브젝트 표기 vs 일반적 할당

보통 자바스크립테어서 무언가를 할당하려고 할때, 단일 등호 기호를 사용합니다. 예:

```js
const myNumber = 0;
```

하지만, 이 방법은 오브젝트에선 동작하지 않습니다. — 오브젝트에선 멤버와 값 사이를 콜론으로 구분하고, 각 멤버들은 콤마로 구분합니다, 예를 들어:

```js
const myObject = {
  name: "Chris",
  age: 38,
};
```

기본 정의
-----

* JavaScript란 무엇인가?
* 변수는 무엇인가?
* 문자열은 무엇인가?
* 배열은 무엇인가?
* 루프는 무엇인가?
* 함수는 무엇인가?
* 이벤트는 무엇인가?
* 오브젝트는 무엇인가?
* JSON은 무엇인가?
* web API는 무엇인가?(en-US) (en-US)
* DOM은 무엇인가? (en-US)
Basic use cases
---------------

### General

* How do you add JavaScript to your page?
* How do you add comments to JavaScript code?
### Variables

* How do you declare a variable?
* How do you initialize a variable with a value?
* How do you update a variable's value? (also see Assignment operators)
* What data types can values have in JavaScript?
* What does 'loosely typed' mean?
### Math

* What types of number do you have to deal with in web development?
* How do you do basic math in JavaScript?
* What is operator precedence, and how is it handled in JavaScript?
* How do you increment and decrement values in JavaScript?
* How do you compare values in JavaScript? (e.g. to see which one is bigger, or to see if one value is equal to another).
### Strings

* How do you create a string in JavaScript?
* Do you have to use single quotes or double quotes?
* How do you escape characters in strings?
* How do you join strings together?
* Can you join strings and numbers together?
* How do you find the length of a string?
* How you find what character is at a certain position in a string?
* How do you find and extract a specific substring from a string?
* How do you change the case of a string?
* How do you replace one specific substring with another?
### Arrays

* How do you create an array?
* How do you access and modify the items in an array? (this includes multidimensional arrays)
* How do you find the length of an array?
* How you add and remove array items?
* How do you split a string into array items, or join array items into a string?
### Debugging JavaScript

* What are the basic types of error?
* What are browser developer tools, and how do you access them?(en-US) (en-US)
* How do you log a value to the JavaScript console?(en-US) (en-US)
* How do you use breakpoints, and other JavaScript debugging features?(en-US) (en-US)

For more information on JavaScript debugging, see Handling common JavaScript problems(en-US) (en-US); also see Other common errors for a description of common errors.

### Making decisions in code

* How do you execute different blocks of code, depending on a variable's value or other condition?
* How do you use if ...else statements?
* How do nest one decision block inside another?
* How do you use AND, OR, and NOT operators in JavaScript?
* How do you conveniently handle a large number of choices for one condition?
* How do you use a ternary operator to make a quick choice between two options based on a true or false test?
### Looping/iteration

* How do you run the same bit of code over and over again?
* How do you exit a loop before the end if a certain condition is met?
* How do you skip to the next iteration of a loop if a certain condition is met?
* How do you use while and do ... while loops?
* How to iterate over the elements in an array
* How to iterate over the elements in a multidimensional array
* How to iterate over the members in an object
* How to iterate over the members of an object nested inside an array
Intermediate use cases
----------------------

### Functions

* How do you find functions in the browser?
* What is the difference between a function and a method?
* How do you create your own functions?
* How do you run (call, or invoke) a function?
* What is an anonymous function?
* How do you specify parameters (or arguments) when invoking a function?
* What is function scope?
* What are return values, and how do you use them?
### Objects

* How do you create an object?
* What is dot notation?
* What is bracket notation?
* How do you get and set the methods and properties of an object?
* What is `this`, in the context of an object?
* What is object-oriented programming?
* What are constructors and instances, and how do you create them?
* What different ways are there to create objects in JavaScript?
### JSON

* How do you structure JSON data, and read it from JavaScript?
* How can you load a JSON file into a page?
* How do you convert a JSON object to a text string, and back again?
### Events

* What are event handlers and how do you use them?
* What are inline event handlers?
* What does the `addEventListener()` function do, and how do you use it?
* Which mechanism should I use to add event code to my web pages?
* What are event objects, and how do you use them?
* How do you prevent default event behaviour?
* How do events fire on nested elements? (event propagation, also related — event bubbling and capturing)
* What is event delegation, and how does it work?
### Object-oriented JavaScript

* What are object prototypes?
* What is the constructor property, and how can you use it?
* How do you add methods to the constructor?
* How do you create a new constructor that inherits its members from a parent constructor?
* When should you use inheritance in JavaScript?
### Web APIs

* How do you manipulate the DOM (e.g. adding or removing elements) using JavaScript? (en-US)


비동기 JavaScript - Web 개발 학습하기

비동기 JavaScript
==============

이 과정에서 우리는 asynchronous JavaScript와 이것이 중요한 이유, 서버에서 리소스를 가져오는 것과 같은 잠재적 블로킹 연산을 어떻게 효과적으로 다룰 수 있을지에 대해 살펴봅니다.

>
> **Callout:**
>
>
> #### 프론트엔드 웹 개발자가 되고싶으신가요?
>
>
> 목표를 향해 나아가기 위해 필요한 모든 필수 정보를 포함한 교육과정을 준비했습니다.
>
>
> **시작하기**
>
>
>

필요한 사전 지식
---------

Asynchronous Javascript는 상당히 고급 주제에 속하고, 시도해보기 전에 JavaScript 첫걸음과 JavaScript 구성요소 과정을 거치는것이 좋습니다.

**참고:** 만약 파일을 만들 수 없는 컴퓨터/테블릿/기타 장치를 사용하고 있다면, JSBin이나 Glitch와 같은 온라인 코딩 프로그램에서 (대부분의) 예제 코드를 사용해볼 수 있습니다.

가이드
---

Introducing asynchronous JavaScript

**동기**와 **비동기** 프로그램이 무엇인지, 비동기 기술을 자주 사용해야하는 이유, 역사적으로 Javascript에서 비동기 함수가 구현된 방식 및 문제를 살펴봅니다.

How to use promises

프로미스와 프로미스 기반 API를 어떻게 사용하는지 알아봅니다. 또한 `async` 와 `await` 키워드를 소개합니다.

Implementing a promise-based API

자신만의 프로미스 기반 API를 구현하는 방법을 간략하게 설명합니다.

Introducing workers

workers를 사용하면 별도의 스레드에서 특정 작업을 실행하여 메인 코드의 응답성을 유지할 수 있습니다. 이 글에서는 workers를 사용하도록 장기 실행 동기 함수를 다시 작성합니다.

평가
--

Sequencing animations

프로미스를 사용하여 특정 시퀀스에서 애니메이션 세트를 재생하는 평가입니다.

같이 보기
-----

* Marijn Haverbeke의 환상적인 Eloquent JavaScript 온라인 책 Asynchronous Programming.


Sequencing animations - Web 개발 학습하기

Sequencing animations
=====================

* 이전
* Overview: Asynchronous

이 평가에서는 일련의 애니메이션을 순서대로 재생하도록 페이지를 업데이트할 것입니다. 이를 진행하기 위해 프로미스 사용 방법에서 배운 몇 가지 기술을 사용할 예정입니다.

|  |  |
| --- | --- |
| 필요한 사전 지식 | 기본적인 컴퓨터 사용능력, JavaScript 기초와 프로미스 기반 API사용 방법에 대한 이해 |
| 목적 | 프로미스기반 API 사용 방법에 대한 이해도를 테스트하기 위함 |

시작
--

https://github.com/mdn/learning-area/tree/main/javascript/asynchronous/sequencing-animations/start에서 로컬로 파일을 복사합니다. 여기에는 4개의 파일이 있습니다.

* alice.svg
* index.html
* main.js
* style.css

앞으로 편집할 파일은 "main.js" 뿐입니다.

브라우저에서 "index.html"을 열면 세 개의 이미지가 대각선으로 배열되어 있는것을 볼 수 있을것입니다.

![Screenshot of sequencing-animations assessment page](/ko/docs/Learn/JavaScript/Asynchronous/Sequencing_animations/sequencing-animations.png)

이미지는 Using the Web Animations API (en-US) 가이드에서 가져온 것입니다.

프로젝트 설명
-------

우리는 이 페이지를 업데이트하여 세 개의 이미지 모두에 하나씩 애니메이션을 적용할 것입니다. 첫 번째 애니메이션이 끝나면 두 번째 애니메이션이 시작되고, 두 번째가 끝나면 세 번째를 시작합니다.

애니메이션은 "main.js"에 정의되어 있습니다. 이 애니메이션은 이미지를 회전하고 이미지가 사라질 때까지 축소시킵니다.

페이지의 작동 방식에 대한 자세한 내용을 보려면 완료된 예제를 살펴보세요. 애니메이션은 한 번만 실행되니 다시 실행하려면 페이지를 다시 로드해야합니다.

단계별로 완성해보기
----------

### 첫 번째 이미지 애니메이션

우리는 Web Animations API의 `element.animate()` (en-US) 메서드를 사용하여 애니메이션을 적용할 것입니다.

"main.js"를 업데이트하여 다음과 같이 `alice1.animate()` 호출을 추가해줍니다.

```js
const aliceTumbling = [
  { transform: "rotate(0) scale(1)" },
  { transform: "rotate(360deg) scale(0)" },
];

const aliceTiming = {
  duration: 2000,
  iterations: 1,
  fill: "forwards",
};

const alice1 = document.querySelector("#alice1");
const alice2 = document.querySelector("#alice2");
const alice3 = document.querySelector("#alice3");

alice1.animate(aliceTumbling, aliceTiming);
```

페이지를 다시 로드해보면 첫 번째 이미지가 회전하고 축소되는것을 볼 수 있을겁니다.

### 모든 이미지 애니메이션

다음으로 `alice1`이 끝나면 `alice2`를, `alice2`가 끝나면 `alice3`가 시작되도록 하겠습니다.

`animate()` 메서드는 `Animation` (en-US) 객체를 반환합니다. 이 객체는 애니메이션 재생이 완료되면 이행되는 `Promise`로 `finished` 속성을 가지고 있습니다. 그래서 우리는 이 프로미스로 언제 다음 애니메이션을 시작해야할지 알 수 있습니다.

여러분이 이를 구현하기 위한 몇 가지 다른 방법들을 시도해 보시고, 프로미스를 사용하는 다른 방법들을 보강해주셨으면 합니다.

1. 먼저, 콜백 사용에 대한 논의에서 보았던 "콜백 지옥(callback hell)"으로 프로미스를 구현합니다.
2. 다음으로 프로미스 체인으로 구현합니다. 화살표 함수 대신 다른 것을 사용할 수 있기 때문에 코드는 여러 가지 방법으로 작성할 수 있음을 알아두세요. 다양한 방법을 시도해보세요. 어느 것이 가장 간결하고 읽기 쉬운가요?
3. 마지막으로 `async`와 `await`로 구현해보세요.

`element.animate()`는 프로미스를 반환하지 *않는다* 는것을 기억하세요. 프로미스인 `finished` 속성을 가진 `Animation` 객체를 반환합니다.

평가 또는 추가적인 도움
-------------

작업에 대한 평가를 원하거나 수행 중 막히는 부분이 있다면 다음 방법을 통해 도움을 요청할 수 있습니다.

1. CodePen, jsFiddle, 또는 Glitch와 같이 코드를 공유할 수 있는 온라인 에디터에 여러분이 작성한 코드를 올립니다.
2. MDN Discourse forum Learning category에 평가 혹은 도움을 요청하는 게시물을 작성합니다. 게시물에는 다음이 포함되어야 합니다.
    * "애니메이션 시퀀싱 평가 질문"과 같이 설명적인 제목
    * 이미 시도한 내용과 원하는 작업에 대한 세부 정보 (예: 막히는 부분이 있어서 도움이 필요하거나 평가가 필요)
    * 온라인 편집기에서 도움이나 평가가 필요한 코드 링크(1단계에서 언급됨). 이는 답변에 매우 도움이 됩니다. 만약 코드를 볼 수 없다면 도움을 주기 상당히 어렵습니다.
    * 도움이 필요한 질문을 찾을 수 있도록 실제 작업 또는 평가 페이지에 대한 링크
* 이전
* Overview: Asynchronous


How to implement a promise-based API - Web 개발 학습하기

How to implement a promise-based API
====================================

* 이전
* Overview: Asynchronous
* 다음

마지막 글에서는 프로미스를 반환하는 API를 사용하는 방법에 대해 알아봤습니다. 이 글에서는 프로미스를 반환하는 API를 구현하는 방법에 대해 살펴보겠습니다. 이것은 프로미스 기반 API를 사용하는 것보다 훨씬 덜 일반적인 작업이지만 그래도 알 가치가 있습니다.

|  |  |
| --- | --- |
| 필요한 사전 지식 | 기본적인 컴퓨터 사용능력, 함수와 이벤트 처리기, 프로미스의 기본을 포함한 JavaScript 기초에 대한 이해 |
| 목적 | 프로미스기반 API를 구현하는 방법을 이해하기 위함 |

일반적으로 프로미스 기반 API를 구현할 때 이벤트, 일반 콜백 또는 메시지 전달 모델을 사용할 수 있는 비동기 작업을 래핑합니다. 여러분은 해당 작업의 성공 또는 실패를 적절히 처리하기 위해 `Promise` 객체를 준비합니다.

alarm() API 구현하기
----------------

이 예제에서는 `alarm()`이라는 프로미스 기반 알람 API를 구현할것입니다. 깨울 사람의 이름과 깨울 때까지 기다리는 지연 시간(밀리초)이 인수로 사용됩니다. 기다림이 끝나면 함수는 깨워야 할 사람의 이름과 "Wake up!" 이라는 문구를 메시지로 보냅니다.

### setTimeout() 래핑

`setTimeout()` API를 사용하여 `alarm()` 함수를 구현합니다. `setTimeout()` API는 콜백 함수와 밀리초 단위로 주어진 지연 시간을 인수로 사용합니다. `setTimeout()`이 호출되면 지정된 지연으로 설정된 타이머를 시작하고, 시간이 만료되면 주어진 함수를 호출합니다.

아래 예제에서는 콜백 함수와 1000밀리초의 지연을 사용하여 `setTimeout()`을 호출합니다.

```html
<button id="set-alarm">Set alarm</button>
<div id="output"></div>
```

```
div {
  margin: 0.5rem 0;
}
```

```js
const output = document.querySelector("#output");
const button = document.querySelector("#set-alarm");

function setAlarm() {
  window.setTimeout(() => {
    output.textContent = "Wake up!";
  }, 1000);
}

button.addEventListener("click", setAlarm);
```

### Promise() 생성자

`alarm()` 함수는 타이머가 만료되었을 때 이행되는 `Promise`를 반환합니다. 그러면 "Wake up!" 메시지를 `then()` 처리기로 전달하며, 발신자가 음의 지연 값을 제공할 경우 프로미스를 거부합니다.

여기서 핵심 구성 요소는 `Promise()` 생성자입니다. `Promise()` 생성자는 하나의 함수를 인수로 사용합니다. 우리는 이 기능을 `executor(실행자)`라고 부릅니다. 새 프로미스를 만들 때 실행자의 구현을 제공합니다.

이 실행자 함수 자체에는 두 개의 인수가 필요한데, 이 인수는 함수이기도 하고, 관습적으로 `resolve`와 `reject`라고 불립니다. 실행자 구현에서는 기본 비동기 함수를 호출합니다. 비동기 함수가 성공하면 `resolve`를 호출하고 실패하면 `reject`를 호출합니다. 실행자 함수가 오류를 발생시키면 `reject`가 자동으로 호출됩니다. 모든 유형의 단일 매개 변수를 `resolve` 및 `reject`로 전달할 수 있습니다.

따라서 `alarm()`을 이렇게 구현할 수 있습니다.

```js
function alarm(person, delay) {
  return new Promise((resolve, reject) => {
    if (delay < 0) {
      throw new Error("Alarm delay must not be negative");
    }
    window.setTimeout(() => {
      resolve(`Wake up, ${person}!`);
    }, delay);
  });
}
```

이 함수는 새로운 `Promise`를 만들어 반환합니다. 프로미스 실행자 안에서 우리는 다음과 같이 합니다.

* `delay`가 음수인지 확인하고, 맞다면 오류를 발생시킨다.
* 콜백과 `delay`를 전달하며 `window.setTimeout()`을 호출한다. 콜백은 타이머가 만료되면 호출되며 `resolve`라고 불리는 콜백을 "Wake up!" 메시지와 함께 전달한다.
alarm() API 사용하기
----------------

이 부분은 지난 글부터 꽤 익숙할 것입니다. `alarm()`을 호출하고 반환된 프로미스에 대해 `then()`과 `catch()`를 호출하여 프로미스 이행 및 거부에 대한 처리기를 설정할 수 있습니다.
```

<div>
  <label for="name">Name:</label>
  <input type="text" id="name" name="name" size="4" value="Matilda" />
</div>

<div>
  <label for="delay">Delay:</label>
  <input type="text" id="delay" name="delay" size="4" value="1000" />
</div>

<button id="set-alarm">Set alarm</button>
<div id="output"></div>
```

```
button {
  display: block;
}

div,
button {
  margin: 0.5rem 0;
}
```

```js
const name = document.querySelector("#name");
const delay = document.querySelector("#delay");
const button = document.querySelector("#set-alarm");
const output = document.querySelector("#output");

function alarm(person, delay) {
  return new Promise((resolve, reject) => {
    if (delay < 0) {
      throw new Error("Alarm delay must not be negative");
    }
    window.setTimeout(() => {
      resolve(`Wake up, ${person}!`);
    }, delay);
  });
}

button.addEventListener("click", () => {
  alarm(name.value, delay.value)
    .then((message) => (output.textContent = message))
    .catch((error) => (output.textContent = `Couldn't set alarm: ${error}`));
});
```

이름과 `delay`에 다른 값을 설정해 보세요. `delay`에 음수도 설정해 보세요.

alarm()을 async와 await와 함께 사용하기
------------------------------

`alarm()`은 `Promise`를 반환하기 때문에 `promise.all()`과 `async` / `await`, 프로미스 체이닝 등 프로미스로 할 수 있는 모든 것을 할 수 있습니다.
```

<div>
  <label for="name">Name:</label>
  <input type="text" id="name" name="name" size="4" value="Matilda" />
</div>

<div>
  <label for="delay">Delay:</label>
  <input type="text" id="delay" name="delay" size="4" value="1000" />
</div>

<button id="set-alarm">Set alarm</button>
<div id="output"></div>
```

```
button {
  display: block;
}

div,
button {
  margin: 0.5rem 0;
}
```

```js
const name = document.querySelector("#name");
const delay = document.querySelector("#delay");
const button = document.querySelector("#set-alarm");
const output = document.querySelector("#output");

function alarm(person, delay) {
  return new Promise((resolve, reject) => {
    if (delay < 0) {
      throw new Error("Alarm delay must not be negative");
    }
    window.setTimeout(() => {
      resolve(`Wake up, ${person}!`);
    }, delay);
  });
}

button.addEventListener("click", async () => {
  try {
    const message = await alarm(name.value, delay.value);
    output.textContent = message;
  } catch (error) {
    output.textContent = `Couldn't set alarm: ${error}`;
  }
});
```

같이 보기
-----

* `Promise()` constructor
* Using promises
* 이전
* Overview: Asynchronous
* 다음


Introducing asynchronous JavaScript - Web 개발 학습하기

Introducing asynchronous JavaScript
===================================

* Overview: Asynchronous
* 다음

이 글에선 비동기 프로그래밍이 무엇인지, 왜 필요한지 설명하고 역사상 JavaScript에서 비동기 함수가 구현된 몇 가지 방법에 대해 간략하게 설명 할 것입니다.

|  |  |
| --- | --- |
| 필요한 사전 지식 | 기본적인 컴퓨터 사용능력, 함수와 이벤트 처리기를 포함한 JavaScript 기초에 대한 이해 |
| 목적 | 비동기 JavaScript가 무엇인지, 동기 JavaScript와 어떻게 다른지, 그리고 왜 필요한지에 대한 익숙함을 얻기 위함 |

비동기 프로그래밍은 작업이 완료될 때까지 기다리지 않고 잠재적으로 오래 실행되는 작업을 시작하여 해당 작업이 실행되는 동안에도 다른 이벤트에 응답할 수 있게 하는 기술입니다. 작업이 완료되면 프로그램이 결과를 제공합니다.

브라우저가 제공하는 많은 기능, 특히 가장 흥미로운 것들은 시간이 오래 걸릴 가능성이 있으므로 비동기적입니다. 예를 들어 다음과 같습니다.

* `fetch()`를 이용해 HTTP 요청 만들기
* `getUserMedia()` (en-US)를 사용해 사용자의 카메라 또는 마이크에 접근하기
* `showOpenFilePicker()` (en-US)를 통해 사용자에게 파일 선택을 요청

따라서 여러분만의 비동기 함수를 자주 *구현* 할 필요는 없지만 올바르게 *사용* 해야 할 경우가 많습니다.

이 글에서는 비동기 함수를 필요하게 하는 장기 실행 동기 함수의 문제점을 살펴보는 것으로 시작하겠습니다.

동기 프로그래밍
--------

아래 코드를 한번 보세요.

```js
const name = "Miriam";
const greeting = `Hello, my name is ${name}!`;
console.log(greeting);
// "Hello, my name is Miriam!"
```

이 코드는

1. `name`이라는 문자열을 선언합니다.
2. `name`을 사용하여 `greeting`이란 또다른 문자열을 선언합니다.
3. greeting을 JavaScript 콘솔에 출력합니다.

여기서 브라우저는 실질적으로 프로그램을 작성한 순서대로 한 줄씩 실행한다는 점에 주목해야 합니다. 브라우저는 각 지점에서 다음 줄로 넘어가기 전까지 현재 라인의 작업이 끝날 때까지 기다립니다. 각 라인들은 이전 라인에 의존하고 있으니 이렇게 해야 하죠.

따라서 **동기 프로그래밍**이 됩니다. 이렇게 별도의 함수로 호출해도 동기입니다.

```js
function makeGreeting(name) {
  return `Hello, my name is ${name}!`;
}

const name = "Miriam";
const greeting = makeGreeting(name);
console.log(greeting);
// "Hello, my name is Miriam!"
```

`makeGreeting()`은 **동기 함수**입니다. 왜냐하면 호출자는 함수의 작업이 완료될 때까지 기다렸다가 값을 반환해야 계속 진행할 수 있기 때문입니다.

장기 실행 동기 함수
-----------

동기 함수가 오랜 시간에 걸쳐 실행되면 어떨까요?

아래 프로그램은 매우 비효율적인 알고리즘을 사용하여 "Generate primes" 버튼을 클릭할 때 여러 개의 큰 소수를 생성합니다. 사용자가 입력하는 숫자가 커질수록 작업 시간도 더 오래 걸립니다.

```html
<label for="quota">Number of primes:</label>
<input type="text" id="quota" name="quota" value="1000000" />

<button id="generate">Generate primes</button>
<button id="reload">Reload</button>

<div id="output"></div>
```

```js
function generatePrimes(quota) {
  function isPrime(n) {
    for (let c = 2; c <= Math.sqrt(n); ++c) {
      if (n % c === 0) {
        return false;
      }
    }
    return true;
  }

  const primes = [];
  const maximum = 1000000;

  while (primes.length < quota) {
    const candidate = Math.floor(Math.random() \* (maximum + 1));
    if (isPrime(candidate)) {
      primes.push(candidate);
    }
  }

  return primes;
}

document.querySelector("#generate").addEventListener("click", () => {
  const quota = document.querySelector("#quota").value;
  const primes = generatePrimes(quota);
  document.querySelector("#output").textContent =
    `Finished generating ${quota} primes!`;
});

document.querySelector("#reload").addEventListener("click", () => {
  document.location.reload();
});
```

"Generate primes"를 클릭해보세요. 여러분의 컴퓨터가 얼마나 빠른지에 따라 달라지겠지만, 프로그램이 "Finished!" 메시지를 출력하기 전 아마 몇 초가 걸릴 것입니다.

장기 실행 동기 함수의 문제점
----------------

다음 예제는 입력할 수 있는 텍스트 상자를 추가한 것을 제외하고는 이전 예제와 같습니다. 이번에는 "Generate primes"를 누른 후 바로 텍스트 상자에 아무 글이나 입력해보세요.

`generatePrimes()` 함수가 실행 중인 동안 프로그램이 완전히 응답하지 않는 것을 확인할 수 있을 겁니다. 아무것도 입력할 수 없고 클릭도 안될 것이며 그 외 다른 것도 할 수 없습니다.
```

<label for="quota">Number of primes:</label>
<input type="text" id="quota" name="quota" value="1000000" />

<button id="generate">Generate primes</button>
<button id="reload">Reload</button>

<textarea id="user-input" rows="5" cols="62">
Try typing in here immediately after pressing "Generate primes"
</textarea>

<div id="output"></div>
```

```
textarea {
  display: block;
  margin: 1rem 0;
}
```

```
function generatePrimes(quota) {
  function isPrime(n) {
    for (let c = 2; c <= Math.sqrt(n); ++c) {
      if (n % c === 0) {
        return false;
      }
    }
    return true;
  }

  const primes = [];
  const maximum = 1000000;

  while (primes.length < quota) {
    const candidate = Math.floor(Math.random() \* (maximum + 1));
    if (isPrime(candidate)) {
      primes.push(candidate);
    }
  }

  return primes;
}

document.querySelector("#generate").addEventListener("click", () => {
  const quota = document.querySelector("#quota").value;
  const primes = generatePrimes(quota);
  document.querySelector("#output").textContent =
    `Finished generating ${quota} primes!`;
});

document.querySelector("#reload").addEventListener("click", () => {
  document.querySelector("#user-input").value =
    'Try typing in here immediately after pressing "Generate primes"';
  document.location.reload();
});
```

이는 장기 실행 동기 함수의 기본적인 문제입니다. 우리의 프로그램이 필요로 하는 것은 다음과 같습니다.

1. 함수를 호출함으로써 장기적으로 실행되는 작업을 시작한다.
2. 이 함수로 작업을 시작하고 즉시 복귀하여 다른 이벤트에 계속 응답할 수 있게 한다.
3. 작업이 완료되면 결과를 알려준다.

이것이 바로 비동기 함수가 할 수 있는 일입니다. 이 글의 나머지 부분에서는 비동기 함수가 JavaScript에서 구현되는 방법에 관해 설명합니다.

이벤트 처리기
-------

방금 들은 비동기 함수에 대한 설명으로 여러분은 이벤트 처리기를 떠올릴 수 있습니다. 만약 떠올랐다면 맞습니다. 이벤트 처리기는 실제로 비동기 프로그래밍의 한 형태입니다. 이벤트가 발생할 때마다 호출되는 함수(이벤트 처리기)를 제공하는 것으로 말이죠. "이벤트"가 "비동기 작업 완료"인 경우, 이 이벤트를 사용하여 호출자에게 비동기 함수 호출의 결과를 알릴 수 있습니다.

일부 초기 비동기 API는 이러한 이벤트 방식을 사용했습니다. `XMLHttpRequest` 는 JavaScript를 사용하여 원격 서버에 HTTP 요청을 할 수 있는 API입니다. HTTP 요청은 시간이 걸릴 수 있는 작업이라 비동기 API이며 이벤트 수신기를 `XMLHttpRequest` 객체에 연결해 요청의 진행 상태 및 최종 완료에 대한 알림을 받습니다.

다음 예제에서는 이를 실제로 보여 줍니다. "Click to start request"를 클릭하여 요청을 보내보세요. 새로운 `XMLHttpRequest`를 생성하고 이것의 `loadend` (en-US) 이벤트를 수신합니다. 처리기는 상태 코드와 함께 "Finished!" 메시지를 기록합니다.

이벤트 수신기를 추가한 후 요청을 보냅니다. 이때 요청이 시작된 이후 "Started XHR request"를 기록할 수 있는데, 이는 요청이 진행되는 동안 프로그램이 계속 실행되고 있음을 뜻하며 요청이 완료될 때 이벤트 처리기가 호출됩니다.

```html
<button id="xhr">Click to start request</button>
<button id="reload">Reload</button>

<pre readonly class="event-log"></pre>
```

```
pre {
  display: block;
  margin: 1rem 0;
}
```

```js
const log = document.querySelector(".event-log");

document.querySelector("#xhr").addEventListener("click", () => {
  log.textContent = "";

  const xhr = new XMLHttpRequest();

  xhr.addEventListener("loadend", () => {
    log.textContent = `${log.textContent}Finished with status: ${xhr.status}`;
  });

  xhr.open(
    "GET",
    "https://raw.githubusercontent.com/mdn/content/main/files/en-us/\_wikihistory.json",
  );
  xhr.send();
  log.textContent = `${log.textContent}Started XHR request\n`;
});

document.querySelector("#reload").addEventListener("click", () => {
  log.textContent = "";
  document.location.reload();
});
```

이전에 접한 이벤트 처리기와 유사하지만, 이벤트가 버튼 클릭과 같은 사용자 행동이 아닌 어떤 객체의 상태 변화라는 점이 다릅니다.

콜백
--

이벤트 핸들러는 콜백의 특정 유형입니다. 콜백은 그냥 적절한 시점에 호출될 것으로 예상하여 다른 함수로 전달되는 함수입니다. 방금 살펴본 것처럼 콜백은 JavaScript에서 비동기 함수를 구현하는 주요 방식이었습니다.

그러나 콜백 기반 코드는 콜백이 콜백을 가지는 함수를 호출하는 경우 이해하기 어려울 수 있습니다. 이것은 일련의 비동기 함수로 분해되는 작업을 수행해야 하는 일반적인 상황입니다. 예를 들어, 다음을 한번 보겠습니다.

```js
function doStep1(init) {
  return init + 1;
}

function doStep2(init) {
  return init + 2;
}

function doStep3(init) {
  return init + 3;
}

function doOperation() {
  let result = 0;
  result = doStep1(result);
  result = doStep2(result);
  result = doStep3(result);
  console.log(`result: ${result}`);
}

doOperation();
```

여기서는 세 단계로 나뉘는 단일 작업이 있습니다. 각 단계는 이전 단계에 의존적입니다. 이 예제에서 첫 번째 단계는 입력값에 1을 추가하고, 두 번째 단계는 2를 추가하고, 세 번째 단계는 3을 추가합니다. 0의 입력부터 시작하여 최종 결과는 6(0 + 1 + 2 + 3)입니다. 동기식 프로그램으로서, 이것은 매우 간단합니다. 하지만 콜백을 사용하여 단계를 구현하면 어떨까요?

```js
function doStep1(init, callback) {
  const result = init + 1;
  callback(result);
}

function doStep2(init, callback) {
  const result = init + 2;
  callback(result);
}

function doStep3(init, callback) {
  const result = init + 3;
  callback(result);
}

function doOperation() {
  doStep1(0, (result1) => {
    doStep2(result1, (result2) => {
      doStep3(result2, (result3) => {
        console.log(`result: ${result3}`);
      });
    });
  });
}

doOperation();
```

콜백 내부에서 콜백을 호출하기 때문에 깊게 중첩된 `doOperation()` 함수가 생깁니다. 이 함수는 읽고 디버깅하기 훨씬 어렵습니다. 이것은 때때로 "콜백 지옥(callback hell)" 또는 "파라미드 오브 둠(pyramid of doom)"이라고 불립니다. (왜냐하면 들여 써진 부분이 옆에서 봤을 때 피라미드처럼 보이거든요.)

이렇게 콜백을 중첩하면 오류 처리도 매우 어려워질 수 있습니다. 상위 레벨에서 한 번만 오류를 처리하는 대신 "피라미드"의 각 레벨에서 오류를 처리해야 하는 경우가 많습니다.

이러한 이유로 대부분의 최신 비동기 API는 콜백을 사용하지 않습니다. 대신 JavaScript에서 비동기 프로그래밍의 토대는 다음에 소개할 `Promise` 입니다.

* Overview: Asynchronous
* 다음