웹에 벡터 그래픽 추가하기 - Web 개발 학습하기

웹에 벡터 그래픽 추가하기
==============

* 이전
* Overview: Multimedia and embedding
* 다음

벡터 그래픽은 파일 크기가 작고 확장성이 뛰어나 확대하거나 큰 크기로 확대해도 픽셀이 깨지지 않기 때문에 여러 상황에서 매우 유용합니다. 이 글에서는 웹페이지에 벡터 그래픽을 삽입하는 방법을 보여드리겠습니다.

|  |  |
| --- | --- |
| 필요한 사전 지식: | HTML의 기본 사항
 과
 문서에 이미지를 삽입하는 방법
 을 알고 있어야 합니다.
  |
| 목표: | 웹 페이지에 SVG(벡터) 이미지를 삽입하는 방법을 알아보세요. |

**참고:** 이 글에서는 SVG가 무엇인지, 웹 페이지에 추가하는 방법만 설명합니다.

벡터 그래픽이란 무엇인가요?
---------------

웹에서는 **래스터 이미지**와 **벡터 이미지**의 두 가지 유형의 이미지로 작업하게 됩니다.

* **래스터 이미지**는 픽셀 그리드를 사용하여 정의되며, 래스터 이미지 파일에는 각 픽셀이 배치될 위치와 정확한 색상을 나타내는 정보가 포함되어 있습니다. 널리 사용되는 웹 래스터 형식에는 비트맵(`.bmp`), PNG(`.png`), JPEG(`.jpg`), GIF(`.gif`) 등이 있습니다.
* **벡터 이미지**는 알고리즘을 사용하여 정의되며, 벡터 이미지 파일에는 컴퓨터가 화면에 렌더링할 때 이미지가 어떻게 보일지 계산하는 데 사용할 수 있는 모양 및 경로 정의가 포함되어 있습니다. SVG 형식을 사용하면 웹에서 사용할 수 있는 강력한 벡터 그래픽을 만들 수 있습니다.

이 둘의 차이점을 알기 위해 예시를 살펴보겠습니다. 이 예제는 GitHub 리포지토리에서 벡터 대 래스터.html로 확인할 수 있으며, 검은색 그림자가 있는 빨간색 별의 겉보기에는 동일한 두 개의 이미지를 나란히 보여줍니다. 차이점은 왼쪽은 PNG이고 오른쪽은 SVG 이미지라는 점입니다.

페이지를 확대하면 그 차이가 분명해집니다. PNG 이미지에는 각 픽셀의 위치(및 색상)에 대한 정보가 포함되어 있기 때문에 확대할수록 픽셀화됩니다. 확대하면 각 픽셀의 크기가 커져 화면의 여러 픽셀을 채우므로 이미지가 뭉개져 보이기 시작합니다. 그러나 벡터 이미지는 이미지의 크기에 관계없이 알고리즘을 사용하여 이미지의 모양을 계산하고 크기가 커짐에 따라 값을 조정하기 때문에 계속 선명하고 멋지게 보입니다.

![별 이미지 2개](/ko/docs/Learn/HTML/Multimedia_and_embedding/Adding_vector_graphics_to_the_Web/raster-vector-default-size.png)

![두 개의 별 이미지 확대, 하나는 선명하고 다른 하나는 흐릿함](/ko/docs/Learn/HTML/Multimedia_and_embedding/Adding_vector_graphics_to_the_Web/raster-vector-zoomed.png)

**참고:** 위의 이미지는 모두 PNG이며, 각각의 경우 왼쪽 별은 래스터 이미지를 나타내고 오른쪽 별은 벡터 이미지를 나타냅니다. 다시 한 번 벡터 대 래스터.html 데모로 이동하여 실제 예시를 확인하세요!

또한 벡터 이미지 파일은 이미지의 모든 픽셀에 대한 정보를 개별적으로 저장하는 대신 몇 가지 알고리즘만 저장하면 되기 때문에 래스터 이미지 파일보다 훨씬 가볍습니다.

SVG란 무엇인가요?
-----------

SVG는 벡터 이미지를 설명하기 위한 XML 기반 언어입니다. 기본적으로 HTML과 같은 마크업이지만, 이미지에 표시할 모양과 해당 모양에 적용할 효과를 정의하는 다양한 요소가 있다는 점이 다릅니다. SVG는 콘텐츠가 아닌 그래픽을 마크업하기 위한 것입니다. 가장 간단하게는 `<circle>`, `<rect>`과 같은 간단한 도형을 만들기 위한 요소가 있습니다. 고급 SVG 기능에는 `<feColorMatrix>` (en-US)(변환 매트릭스를 사용하여 색상 변환), `<animate>` (en-US)(벡터 그래픽의 일부에 애니메이션 적용), `<mask>` (en-US)(이미지 위에 마스크 적용) 등이 있습니다.

간단한 예로 다음 코드는 원과 직사각형을 만듭니다.

```html
<svg
 version="1.1"
 baseProfile="full"
 width="300"
 height="200"
 xmlns="http://www.w3.org/2000/svg">
  <rect width="100%" height="100%" fill="black" />
  <circle cx="150" cy="100" r="90" fill="blue" />
</svg>
```

그러면 다음과 같은 출력이 생성됩니다.

위의 예시를 보면 SVG가 손으로 코딩하기 쉽다는 인상을 받을 수 있는데요. 이처럼, 텍스트 편집기에서 간단한 SVG를 직접 코딩할 수 있습니다. 그러나 복잡한 이미지의 경우 매우 어려워지기 시작합니다. SVG 이미지를 만들 때 대부분의 사람들은 Inkscape나 Illustrator와 같은 벡터 그래픽 편집기를 사용합니다. 이러한 패키지를 사용하면 다양한 그래픽 도구를 사용하여 다양한 일러스트레이션을 만들고 사진의 근사치를 만들 수 있습니다. 예를 들어, Inkscape의 트레이스 비트맵 기능이 있습니다.

SVG에는 지금까지 설명한 것 외에도 몇 가지 추가적인 이점이 있습니다.

* 벡터 이미지의 텍스트는 계속 액세스할 수 있습니다(SEO에도 도움이 됩니다).
* SVG는 이미지의 각 구성 요소가 CSS를 통해 스타일을 지정하거나 JavaScript를 통해 스크립트를 작성할 수 있는 요소이기 때문에 스타일링/스크립팅에 적합합니다.

그렇다면 왜 SVG 대신 래스터 그래픽을 사용해야 할까요? SVG에는 몇 가지 단점이 있습니다.

* SVG는 매우 빠르게 복잡해져 파일 크기가 커질 수 있으며, 복잡한 SVG는 브라우저에서 처리하는 데 상당한 시간이 걸릴 수 있습니다.
* SVG는 만들려는 이미지의 종류에 따라 래스터 이미지보다 만들기가 더 어려울 수 있습니다.

래스터 그래픽은 위에서 설명한 이유로 사진과 같은 복잡한 정밀 이미지에 더 적합할 수 있습니다.

**참고:** Inkscape에서 파일을 일반 SVG로 저장하면 공간을 절약할 수 있습니다. 또한 웹용 SVG를 준비하는 방법을 설명하는 이 문서를 참조하세요.

페이지에 SVG 추가하기
-------------

이 섹션에서는 웹 페이지에 SVG 벡터 그래픽을 추가할 수 있는 다양한 방법을 살펴봅니다.

### 빠른 방법: `img` 요소

`<img>` 요소를 통해 SVG를 임베드하려면 예상대로 src 속성에서 참조하기만 하면 됩니다. `height` 또는 `width` 속성(또는 SVG에 고유한 가로 세로 비율이 없는 경우 둘 다)이 필요합니다. 아직 읽어보지 않았다면 HTML로 이미지 만들기를 읽어보세요.

```html
<img
 src="equilateral.svg"
 alt="세 변이 모두 같은 삼각형"
 height="87"
 width="100" />
```

#### 장점

* `alt` 속성에 텍스트에 해당하는 기본 제공 이미지 구문을 빠르고 친숙하게 사용할 수 있습니다.
* `<a>` 요소 안에 `<img>`를 중첩하여 이미지를 하이퍼링크로 쉽게 만들 수 있습니다.
* SVG 파일은 브라우저에서 캐시할 수 있으므로 나중에 로드된 이미지를 사용하는 모든 페이지의 로딩 시간이 빨라집니다.

#### 단점

* Javascript로 이미지를 조작할 수 없습니다.
* CSS로 SVG 콘텐츠를 제어하려면 SVG 코드에 인라인 CSS 스타일을 포함해야 합니다. SVG 파일에서 호출된 외부 스타일시트는 적용되지 않습니다.
* CSS 의사 클래스(예: `:focus`)를 사용하여 이미지 스타일을 다시 지정할 수 없습니다.
### 문제 해결 및 브라우저 간 지원

SVG를 지원하지 않는 브라우저(IE 8 이하, Android 2.3 이하)의 경우, `src` 속성에서 PNG 또는 JPG를 참조하고 `srcset` 속성(최신 브라우저만 인식)을 사용하여 SVG를 참조할 수 있습니다. 이 경우 지원 브라우저에서만 SVG를 로드하며, 이전 브라우저에서는 대신 PNG를 로드합니다.

```html
<img src="equilateral.png" alt="변이 같은 삼각형" srcset="equilateral.svg" />
```

아래와 같이 SVG를 CSS 배경 이미지로 사용할 수도 있습니다. 아래 코드에서 구형 브라우저는 인식하는 PNG를 그대로 사용하지만 최신 브라우저는 SVG를 로드합니다:

```css
background: url("fallback.png") no-repeat center;
background-image: url("image.svg");
background-size: contain;
```

위에서 설명한 `<img>` 방법과 마찬가지로 CSS 배경 이미지를 사용하여 SVG를 삽입하면 Javascript로 SVG를 조작할 수 없으며 동일한 CSS 제한이 적용됩니다.

SVG가 전혀 표시되지 않는다면 서버가 제대로 설정되지 않았기 때문일 수 있습니다. 이 문서에서 올바른 방향을 안내해 드립니다.

### HTML에 SVG 코드를 포함하는 방법

텍스트 편집기에서 SVG 파일을 열고 SVG 코드를 복사한 후 HTML 문서에 붙여넣을 수도 있는데, 이를 **SVG 인라인 넣기** 또는 **인라이닝 SVG**라고도 합니다. SVG 코드 스니펫이 `<svg>` 시작 태그로 시작하고 `</svg>` 끝 태그로 끝나는지 확인하세요. 다음은 문서에 붙여넣을 수 있는 아주 간단한 예시입니다.

```html
<svg width="300" height="200">
  <rect width="100%" height="100%" fill="green" />
</svg>
```

#### 장점

* SVG를 인라인에 넣으면 HTTP 요청이 절약되므로 로딩 시간을 조금 줄일 수 있습니다.
* SVG 요소에 `클래스` 및 `ID`를 할당하고 SVG 내 또는 HTML 문서에 대한 CSS 스타일 규칙을 넣는 모든 위치에서 CSS로 스타일을 지정할 수 있습니다. 실제로 모든 SVG 프레젠테이션 속성 (en-US)을 CSS 속성으로 사용할 수 있습니다.
* SVG 인라인은 일반 스타일시트에서도 SVG 이미지에 CSS 상호 작용(예: `:focus`) 및 CSS 애니메이션을 사용할 수 있는 유일한 접근 방식입니다.
* SVG 마크업을 `<a>` 요소로 감싸서 하이퍼링크로 만들 수 있습니다.

#### 단점

* 이 방법은 SVG를 한 곳에서만 사용하는 경우에만 적합합니다. 복제로 인해 리소스 집약적인 유지 관리가 필요합니다.
* 추가 SVG 코드는 HTML 파일의 크기를 증가시킵니다.
* 브라우저는 일반 이미지 자산을 캐시하는 것처럼 인라인 SVG를 캐시할 수 없으므로 이미지가 포함된 첫 페이지가 로드된 후에는 이미지가 포함된 페이지가 더 빨리 로드되지 않습니다.
* `<foreignObject>` (en-US) 요소에 폴백을 포함할 수 있지만 SVG를 지원하는 브라우저는 여전히 폴백 이미지를 다운로드합니다. 구형 브라우저를 지원하기 위해 추가 오버헤드를 감수할 가치가 있는지를 잘 따져봐야 합니다.
### `iframe`을 사용하여 SVG를 삽입하는 방법

웹페이지처럼 브라우저에서 SVG 이미지를 열 수 있습니다. 따라서 `<iframe>`으로 SVG 문서를 임베드하는 것은 다른 임베딩 기술인 <object>에서 <iframe>으로에서 공부한 것처럼 수행됩니다.

간단히 살펴보세요.

```html
<iframe src="triangle.svg" width="500" height="500" sandbox>
  <img src="triangle.png" alt="세 변이 같지 않은 삼각형" />
</iframe>
```

이것은 확실히 최선의 방법은 아닙니다.

#### 단점

* 보시다시피 `iframe`에는 대체 메커니즘이 있지만 브라우저는 `iframe`을 전혀 지원하지 않는 경우에만 대체를 표시합니다.
* 또한 SVG와 현재 웹페이지의 origin가 같지 않으면 기본 웹페이지에서 JavaScript를 사용하여 SVG를 조작할 수 없습니다.
능동적 학습: SVG로 놀기
---------------

이 능동적 학습 섹션에서는 몇 가지 SVG를 재미있게 사용해 보시기 바랍니다. 아래의 입력 섹션에서 시작하는 데 도움이 되는 몇 가지 샘플을 이미 제공한 것을 확인할 수 있습니다. 또한 SVG 요소 참조로 이동하여 SVG에서 사용할 수 있는 다른 장난감에 대한 자세한 내용을 찾아서 사용해 볼 수도 있습니다. 이 섹션은 여러분의 리서치 기술을 연습하고 재미를 느끼기 위한 것입니다.

코드가 멈춰서 작동되지 않는 경우 언제든지 재설정 버튼을 사용하여 코드를 재설정할 수 있습니다.
```

<h2>Live output</h2>

<div class="output" style="min-height: 50px;"></div>

<h2>Editable code</h2>
<p class="a11y-label">
  Press Esc to move focus away from the code area (Tab inserts a tab character).
</p>

<textarea id="code" class="input" style="width: 95%;min-height: 200px;">
  <svg width="100%" height="100%">
    <rect width="100%" height="100%" fill="red" />
    <circle cx="100%" cy="100%" r="150" fill="blue" stroke="black" />
    <polygon points="120,0 240,225 0,225" fill="green"/>
    <text x="50" y="100" font-family="Verdana" font-size="55"
 fill="white" stroke="black" stroke-width="2">
            Hello!
    </text>
  </svg>
</textarea>

<div class="playable-buttons">
  <input id="reset" type="button" value="Reset" />
  <input id="solution" type="button" value="Show solution" disabled />
</div>
```

```
html {
  font-family: sans-serif;
}

h2 {
  font-size: 16px;
}

.a11y-label {
  margin: 0;
  text-align: right;
  font-size: 0.7rem;
  width: 98%;
}

body {
  margin: 10px;
  background: #f5f9fa;
}
```

```
const textarea = document.getElementById("code");
const reset = document.getElementById("reset");
const solution = document.getElementById("solution");
const output = document.querySelector(".output");
let code = textarea.value;
let userEntry = textarea.value;

function updateCode() {
  output.innerHTML = textarea.value;
}

reset.addEventListener("click", function () {
  textarea.value = code;
  userEntry = textarea.value;
  solutionEntry = htmlSolution;
  solution.value = "Show solution";
  updateCode();
});

solution.addEventListener("click", function () {
  if (solution.value === "Show solution") {
    textarea.value = solutionEntry;
    solution.value = "Hide solution";
  } else {
    textarea.value = userEntry;
    solution.value = "Show solution";
  }
  updateCode();
});

const htmlSolution = "";
let solutionEntry = htmlSolution;

textarea.addEventListener("input", updateCode);
window.addEventListener("load", updateCode);

// 텍스트 영역에서 탭 키 사용을 중지하고
// 대신 캐럿 위치에 탭을 작성하도록 합니다.

textarea.onkeydown = function (e) {
  if (e.keyCode === 9) {
    e.preventDefault();
    insertAtCaret("\t");
  }

  if (e.keyCode === 27) {
    textarea.blur();
  }
};

function insertAtCaret(text) {
  const scrollPos = textarea.scrollTop;
  let caretPos = textarea.selectionStart;
  const front = textarea.value.substring(0, caretPos);
  const back = textarea.value.substring(
    textarea.selectionEnd,
    textarea.value.length,
  );

  textarea.value = front + text + back;
  caretPos += text.length;
  textarea.selectionStart = caretPos;
  textarea.selectionEnd = caretPos;
  textarea.focus();
  textarea.scrollTop = scrollPos;
}

// 사용자가 텍스트 영역 코드를 업데이트할 때마다 저장된 사용자 코드를 업데이트합니다.

textarea.onkeyup = function () {
  // 솔루션이 아닌 사용자 코드가 표시될 때의 상태만 저장하여 사용자 코드 위에 솔루션이 저장되지 않도록 하려는 것입니다.
  if (solution.value === "Show solution") {
    userEntry = textarea.value;
  } else {
    solutionEntry = textarea.value;
  }

  updateCode();
};
```

요약
--

이 글에서는 벡터 그래픽과 SVG가 무엇인지, 왜 알아두면 유용한지, 웹페이지에 SVG를 포함하는 방법에 대해 간략하게 살펴보았습니다. 이 글은 SVG를 배우기 위한 완전한 가이드가 아니라 웹을 돌아다니다가 SVG를 만났을 때 어떤 것인지 알 수 있도록 포인터를 제공하기 위한 것입니다. 따라서 아직 SVG 전문가가 아니라고 생각되더라도 걱정하지 마세요. 작동 원리에 대해 자세히 알아보고 싶으시다면 아래에 도움이 될 만한 링크가 포함되어 있습니다.

이 모듈의 마지막 글에서는 반응형 이미지에 대해 자세히 살펴보면서 다양한 기기에서 이미지가 더 잘 작동하도록 하는 HTML의 도구를 살펴봅니다.

같이 보기
-----

* MDN의 SVG 튜토리얼
* 반응형 SVG를 위한 빠른 팁
* Sara Soueidan의 반응형 SVG 이미지 튜토리얼
* SVG의 접근성 이점
* SVG 속성 및 CSS
* SVG 크기를 조정하는 방법 (래스터 그래픽만큼 간단하지 않습니다!)
* 이전
* Overview: Multimedia and embedding
* 다음


HTML의 이미지 - Web 개발 학습하기

HTML의 이미지
=========

* Overview: Multimedia and embedding
* 다음  (en-US)

초창기 웹에는 텍스트만 있었고 꽤 지루했습니다. 다행히도 웹 페이지 안에 이미지 (및 보다 흥미로운 유형의 컨텐츠)를 삽입하는 기능이 추가되기까지는 오래 걸리지 않았습니다. 고려해 볼 수 있는 다른 유형의 멀티미디어가 있지만 단순한 이미지를 웹 페이지에 삽입하는 데 사용되는 `<img>` 요소로 쉽게 시작해 보겠습니다. 이 글에서는 기초 내용부터 심층적으로 사용하는 방법, `<figure>`를 사용하여 캡션을 주석으로 추가하는 방법, CSS 배경 이미지와 관련된 사용 방법을 자세히 설명합니다.

멀티미디어 및 이미지 삽입| 필요한 사전 지식: |
 기본 컴퓨터 활용 능력,
 기본 소프트웨어 설치,
 파일 작업에 대한 기본 지식, HTML 기초 지식 숙지 (HTML 시작하기에서 설명)
  |
| 목표: |
 간단한 이미지를 HTML에 삽입하고, 캡션을 사용하여 주석을 추가하는 방법과
 HTML 이미지가 CSS 배경 이미지와 관련되는 방법을 배웁니다.
  |

웹페이지에 어떻게 이미지를 넣을까?
-------------------

이미지를 웹사이트에 넣기 위해서 `<img>` 요소를 사용합니다. 이것은 텍스트 내용이나 클로징 태그를 갖지 않는 void element이며, `src` 와 `alt`가 있어야 유용합니다. `src` 속성은 페이지에 삽입하려는 이미지의 URL을 포함합니다. `<a>`요소의 href 속성과 마찬가지로 src 속성은 상대 URL 또는 절대 URL일 수 있습니다. `src`속성이 없으면 이미지 요소에 불러올 이미지가 없습니다.

`alt` 속성은 아래에 설명되어 있습니다.

**참고:** 계속하기 전에 절대 URL, 상대 URL에 대해 복습하기 위해 URL과 path에 대한 기본 지침을 읽어보세요.

예를 들어, 당신의 이미지 파일 이름이 `dinosaur.jpg`이고, 당신의 HTML 페이지와 같은 디렉토리 아래에 위치한다면 이런 식으로 이미지를 삽입할 수 있습니다.

```html
<img src="dinosaur.jpg" alt="Dinosaur" />
```

그 이미지가 HTML 페이지와 같은 디렉토리의 `images` 라는 하위 디렉토리에 있다면 아래 예제 등과 같이 삽입할 수 있습니다.

```html
<img src="images/dinosaur.jpg" alt="Dinosaur" />
```

기타 등등.

**참고:** 검색 엔진은 이미지 파일 이름을 읽고 SEO에 포함합니다. 따라서 그 내용을 설명하는 파일 이름을 사용하세요. `img835.png` 보다는 `dinosaur.jpg`가 낫습니다.

절대 URL을 사용해서 이미지를 삽입할 수도 있습니다. 아래의 예시를 확인하세요.

```html
<img src="https://www.example.com/images/dinosaur.jpg" alt="Dinosaur" />
```

하지만 절대 URL을 사용하는 것은 추천하지 않습니다. 사이트에서 사용할 이미지를 호스팅 해야 하는데, 이는 간단한 설정에서는 웹사이트의 이미지를 HTML과 동일한 서버에 보관하는 것을 의미합니다. 또한 유지보수 측면에서 절대 URL보다 상대 URL을 사용하는 것이 더 효율적입니다. (사이트를 다른 도메인으로 이전할 때 새 도메인을 포함하도록 모든 URL을 업데이트할 필요가 없습니다). 고급 설정에서는 CDN (Content Delivery Network)을 사용하여 이미지를 전송할 수 있습니다.

이미지를 직접 만들지 않은 경우, 해당 이미지가 게시된 라이선스 조건에 따라 사용할 수 있는 권한이 있는지 확인해야 합니다. (자세한 내용은 미디어 애셋 및 라이선스를 참고해 주세요).

**경고:** 다른 웹사이트에 호스팅 된 이미지를 허가 없이 `src`속성으로 가리키지 마세요. 이를 "핫링크"라고 합니다. 누군가 내 페이지를 방문할 때 이미지를 전송하는 대역폭 비용을 다른 사람이 지불하게 되므로 비윤리적인 행위로 간주됩니다. 또한 이미지가 삭제되거나 부끄러운 이미지로 변경되는 것을 제어할 수 없습니다.

절대 URL 또는 상대 URL을 사용한 이전 코드는 다음과 같은 결과를 보여줄 것입니다.

![브라우저에 임베드된 공룡의 기본 이미지와 그 위에 "HTML로 된 이미지"라고 쓰여 있습니다.](/ko/docs/Learn/HTML/Multimedia_and_embedding/Images_in_HTML/basic-image.png)

**참고:** `<img>` 와 `<video>` 와 같은 요소들을 대체 요소라고 하기도 합니다. 그 이유는 요소의 내용과 크기가 요스 그 자체가 아니라, 외부적인 요소(이미지나 비디오)에 의해 결정되기 때문입니다. 이에 대한 자세한 내용은 대체 요소에서 확인할 수 있습니다.

**참고:** 완성된 예제들을 Github에서 확인하실 수 있습니다. (source code도 참고하세요.)

### 대체 텍스트

다음으로 살펴볼 속성은 `alt`입니다. 이 값은 이미지에 대한 텍스트 설명으로, 인터넷 연결이 느려서 이미지를 보거나 표시할 수 없는 경우 또는 렌더링 하는데 시간이 오래 걸리는 상황에서 사용할 수 있습니다. 예를 들어 위의 코드를 다음과 같이 수정할 수 있습니다.

```html
<img
 src="images/dinosaur.jpg"
 alt="The head and torso of a dinosaur skeleton;
 it has a large head with long sharp teeth" />
```

`alt` 를 잘 작성하였는지 확인하기 위한 가장 쉬운 방법은 파일 이름을 고의로 틀리게 적는 것입니다. 예를 들어 이미지 파일의 이름을 `dinosooooor.jpg` 로 바꾼다면 브라우저는 이미지를 보여주는 대신, 대체 텍스트의 내용을 보여줄 것입니다.

![HTML 제목의 이미지이지만 이번에는 공룡 이미지가 표시되지 않고 그 자리에 대체 텍스트가 표시됩니다.](/ko/docs/Learn/HTML/Multimedia_and_embedding/Images_in_HTML/alt-text.png)

대체 텍스트가 필요한 이유는 무엇일까요? 여러 가지 이유로 대체 텍스트가 유용할 수 있습니다.

* 사용자가 시각적인 장애를 가지고 있는 경우 스크린 리더가 그 내용을 읽어줄 수 있습니다. 사실 alt를 가지고 있다는 것만으로 대부분의 사용자들에게 유용합니다.
* 위에서 살펴봤듯이, 파일명을 잘못 적거나 경로 이름을 잘못 적었을 수도 있습니다.
* 아직까지도 텍스트만 지원되는 브라우저(예를 들면 Lynx)를 사용하는 사용자들이 있기 때문에 이미지 지원이 안되는 사용자도 있습니다.
* 텍스트를 제공하여 검색 엔진이 이를 활용할 수 있도록 할 수 있습니다. 예를 들어, 검색 엔진이 대체 텍스트와 검색어를 일치시킬 수 있습니다.
* 사용자가 데이터 전송량과 방해 요소를 줄이기 위해 고의적으로 이미지를 꺼 놓았을 수도 있습니다. 이러한 현상은 휴대폰과 대역폭이 제한적이거나 비싼 국가에서 흔히 볼 수 있는 현상입니다.

'alt' 속성 안에 정확하게 무엇을 써야 할까요? 이것은 이미지가 처음에 "왜" 존재하는지에 따라 다릅니다. 다시 말해, 이미지가 보이지 않는다면 무엇을 잃게 될까요?

* **꾸밈 요소.** 꾸미는 이미지를 위해서 CSS 배경 이미지를 사용하는 것이 좋습니다. 하지만 HTML을 사용해야 한다면, 빈 `alt=""` 를 추가하세요. 만약 이미지가 콘텐츠의 일부가 아니라면, 스크린 리더는 그것을 읽는 데 시간을 낭비하지 않을 것입니다.
* **내용.** 이미지가 중요한 정보를 제공한다면, *간단한* `alt` 텍스트나 더 좋은 방법으로, 모든 사람들이 볼 수 있는 메인 텍스트에 동일한 정보를 제공하세요. 중복된 `alt` 텍스트를 작성하지 마세요. 만약 모든 문단이 메인 콘텐츠에 두 번 쓰여진다면 시각 장애인 사용자에게 얼마나 귀찮은 일일까요? 이미지가 메인 텍스트에 의해 충분히 설명되고 있다면, `alt=""` 를 사용할 수 있습니다.
* **링크.** 만약 이미지를 `<a>` 태그 안에 넣는다면, 이미지를 링크로 만들기 위해서, 여전히 접근 가능한 링크 텍스트를 제공해야 합니다. 이러한 경우에는, `<a>` 요소 안에 작성하거나, 이미지의 `alt` 속성 안에 작성할 수 있습니다. 어떤 것이 더 좋은지는 당신의 상황에 따라 다릅니다.
* **텍스트.** 텍스트를 이미지 안에 넣어서는 안됩니다. 예를 들어, 메인 헤딩이 드롭 쉐도우가 필요하다면, 텍스트를 이미지 안에 넣는 대신에 CSS (en-US)를 사용하세요. 하지만, 만약 이것을 피할 수 없다면, `alt` 속성 안에 텍스트를 제공해야 합니다.

본질적으로 핵심은 이미지를 볼 수 없는 상황에서도 사용 가능한 경험을 제공하는 것입니다. 이것은 모든 사용자들이 콘텐츠를 놓치지 않도록 합니다. 브라우저에서 이미지를 끄고 어떻게 보이는지 확인해 보세요. 이미지가 보이지 않는다면 대체 텍스트가 얼마나 유용한지 빨리 깨닫게 될 것입니다.

**참고:** 더 많은 정보를 원한다면, 대체 텍스트 가이드를 참고하세요.

### 너비와 높이

`width` 및 `height` 속성을 사용하여 이미지의 너비와 높이를 지정할 수 있습니다. 이들은 단위 없이 정수로 제공되며, 픽셀 단위로 이미지의 너비와 높이를 나타냅니다.

여러 방법으로 이미지의 너비와 높이를 찾을 수 있습니다. 예를 들어, Mac에서는 이미지 파일의 표시 정보를 얻기 위해 `Cmd` + `I`를 사용할 수 있습니다. 우리의 예제로 돌아가서, 우리는 다음과 같이 할 수 있습니다.

```html
<img
 src="images/dinosaur.jpg"
 alt="The head and torso of a dinosaur skeleton;
 it has a large head with long sharp teeth"
 width="400"
 height="341" />
```

너비와 높이를 사용하는 좋은 이유가 있습니다. 페이지의 HTML과 이미지는 별도의 HTTP(S) 요청으로 브라우저에 의해 가져온 별개의 리소스입니다. 브라우저가 HTML을 받자마자, 그것은 사용자에게 표시하기 시작합니다. 이미지가 아직 받아지지 않았다면(이것은 자주 발생할 것입니다. 이미지 파일의 크기는 HTML 파일보다 훨씬 크기 때문입니다.), 브라우저는 HTML만 렌더링하고 이미지가 받아지면 페이지를 업데이트할 것입니다.

예를 들어, 이미지 뒤에 텍스트가 있다고 가정해 봅시다.

```html
<h1>Images in HTML</h1>

<img
 src="dinosaur.jpg"
 alt="The head and torso of a dinosaur skeleton; it has a large head with long sharp teeth"
 title="A T-Rex on display in the Manchester University Museum" />
<blockquote>
  <p>
    But down there it would be dark now, and not the lovely lighted aquarium she
    imagined it to be during the daylight hours, eddying with schools of tiny,
    delicate animals floating and dancing slowly to their own serene currents
    and creating the look of a living painting. That was wrong, in any case. The
    ocean was different from an aquarium, which was an artificial environment.
    The ocean was a world. And a world is not art. Dorothy thought about the
    living things that moved in that world: large, ruthless and hungry. Like us
    up here.
  </p>
  <footer>- Rachel Ingalls, <cite>Mrs. Caliban</cite></footer>
</blockquote>
```

브라우저가 HTML을 다운로드 받자마자, 브라우저는 페이지를 보여주기 시작할 것입니다.

이미지가 불러와지면, 브라우저는 이미지를 페이지에 추가할 것입니다. 이미지가 공간을 차지하기 때문에, 브라우저는 텍스트를 아래로 내려서 이미지를 위에 맞추어야 합니다.

![브라우저가 페이지를 로드하는 동안과 페이지가 완료된 후 이미지 크기를 지정하지 않은 경우 페이지 레이아웃을 비교합니다.](/ko/docs/Learn/HTML/Multimedia_and_embedding/Images_in_HTML/no-size.png)

텍스트가 이렇게 옮겨지는 것은 사용자를 매우 불편하게 합니다. 특히 이미 읽기 시작했다면 더욱 그렇습니다.

HTML에 이미지의 실제 사이즈를 명시했다면, `width`와 `height` 속성을 사용하여, 브라우저는 이미지를 다운로드하기 전에 이미지를 위한 공간이 얼마나 필요한지 알 수 있습니다.

이것은 이미지가 다운로드 되었을 때, 브라우저가 주변 콘텐츠를 옮길 필요가 없다는 것을 의미합니다.

![브라우저가 페이지를 로드하는 동안과 페이지가 완료된 후 이미지 크기를 지정했을 때의 페이지 레이아웃을 비교합니다.](/ko/docs/Learn/HTML/Multimedia_and_embedding/Images_in_HTML/size.png)

이 기능에 대한 훌륭한 기사는 이미지에 높이와 너비를 설정하는 것은 정말 중요합니다를 참고하세요.

>
> **Note:** 그럼에도 불구하고, 우리가 말했듯이, HTML 속성을 사용하여 이미지의 실제 크기를 지정하는 것이 좋은 습관이지만, 이미지의 크기를 *조정*하는 데 사용해서는 안 됩니다.
>
>
> 만약 이미지 크기를 너무 크게 설정한다면, 이미지가 거칠거나, 흐릿하거나 너무 작아 보이고, 사용자의 요구에 맞지 않는 이미지를 다운로드 하는데 많은 대역폭을 사용하게 될 것입니다. 이미지는 또한 왜곡되어 보일 수 있습니다. 만약 올바른 종횡비를 유지하지 않는다면. 웹페이지에 올리기 전에 이미지 편집기를 사용하여 이미지를 올바른 크기로 조정해야 합니다.
>
>
> 만약 이미지의 크기를 조정해야 한다면, CSS를 사용하세요.
>
>
>

### 이미지 제목

링크와 마찬가지로, 이미지에 `title` 속성을 추가하여, 필요한 경우 추가적인 정보를 제공할 수 있습니다. 우리의 예제에서, 우리는 다음과 같이 할 수 있습니다.

```html
<img
 src="images/dinosaur.jpg"
 alt="The head and torso of a dinosaur skeleton;
 it has a large head with long sharp teeth"
 width="400"
 height="341"
 title="A T-Rex on display in the Manchester University Museum" />
```

이것은 마우스를 올렸을때 툴팁을 제공합니다. 링크의 title과 같습니다.

![맨체스터 대학교 박물관에 전시된 티라노사우루스라는 툴팁 제목과 함께 공룡 이미지가 표시됩니다.](/ko/docs/Learn/HTML/Multimedia_and_embedding/Images_in_HTML/image-with-title.png)

그러나 이 방법은 추천되지 않습니다. `title`에는 접근성 문제가 많습니다. 주로 스크린 리더 지원을 예측할 수 없고, 대부분의 브라우저는 마우스를 올려야만 표시됩니다. (키보드 사용자는 접근할 수 없습니다). 이에 대한 자세한 내용은 title 속성의 시련과 고난을 참고하세요.

지원하는 정보 같은 경우 이미지에 첨부하는 것보다는, 본문에 포함하는 것이 좋습니다.

### 학습하기: 이미지 삽입하기

이제 당신의 차례입니다! 이 활동적인 학습 섹션에서는 간단한 이미지 첨부 연습을 할 수 있습니다. 당신은 기본적인 `<img>` 태그를 제공받았습니다. 우리는 다음 URL에 위치한 이미지를 삽입하길 원합니다.

```url
https://raw.githubusercontent.com/mdn/learning-area/master/html/multimedia-and-embedding/images-in-html/dinosaur\_small.jpg
```

전에 이미지에 핫링크를 넣지 말라고 했지만 이것은 학습 목적이기 때문에, 이번 한 번은 괜찮습니다.

다음 내용을 수행해 주세요.

* 대체 텍스트를 추가하세요. 그리고 이미지 URL을 잘못 입력하여 작동하는지 확인하세요.
* 이미지의 올바른 `너비`와 `높이`를 설정하세요. (힌트: 200px 너비와 171px 높이입니다.) 그리고 다른 값들을 실험하여 어떤 효과가 있는지 확인하세요.
* 이미지에 `title`을 설정하세요.

만약 실수했다면, 당신은 항상 Reset 버튼을 눌러서 초기화할 수 있습니다. 만약 당신이 정말로 막혔다면, Show solution 버튼을 눌러서 답을 확인하세요.
```

<h2>Live output</h2>

<div class="output" style="min-height: 50px;"></div>

<h2>Editable code</h2>
<p class="a11y-label">
  Press Esc to move focus away from the code area (Tab inserts a tab character).
</p>

<textarea id="code" class="input" style="min-height: 100px; width: 95%">
<img>
</textarea>

<div class="playable-buttons">
  <input id="reset" type="button" value="Reset" />
  <input id="solution" type="button" value="Show solution" />
</div>
```

```
html {
  font-family: sans-serif;
}

h2 {
  font-size: 16px;
}

.a11y-label {
  margin: 0;
  text-align: right;
  font-size: 0.7rem;
  width: 98%;
}

body {
  margin: 10px;
  background: #f5f9fa;
}
```

```
const textarea = document.getElementById("code");
const reset = document.getElementById("reset");
const solution = document.getElementById("solution");
const output = document.querySelector(".output");
const code = textarea.value;
let userEntry = textarea.value;

function updateCode() {
  output.innerHTML = textarea.value;
}

const htmlSolution =
  '<img src="https://raw.githubusercontent.com/mdn/learning-area/master/html/multimedia-and-embedding/images-in-html/dinosaur\_small.jpg"\n alt="The head and torso of a dinosaur skeleton; it has a large head with long sharp teeth"\n width="200"\n height="171"\n title="A T-Rex on display in the Manchester University Museum">';
let solutionEntry = htmlSolution;

reset.addEventListener("click", () => {
  textarea.value = code;
  userEntry = textarea.value;
  solutionEntry = htmlSolution;
  solution.value = "Show solution";
  updateCode();
});

solution.addEventListener("click", () => {
  if (solution.value === "Show solution") {
    textarea.value = solutionEntry;
    solution.value = "Hide solution";
  } else {
    textarea.value = userEntry;
    solution.value = "Show solution";
  }
  updateCode();
});

textarea.addEventListener("input", updateCode);
window.addEventListener("load", updateCode);

// textarea 외부에서 탭 키 탭을 중지하고
// 대신 캐럿 위치에 탭을 작성하도록 합니다.

textarea.onkeydown = (e) => {
  if (e.keyCode === 9) {
    e.preventDefault();
    insertAtCaret("\t");
  }

  if (e.keyCode === 27) {
    textarea.blur();
  }
};

function insertAtCaret(text) {
  const scrollPos = textarea.scrollTop;
  let caretPos = textarea.selectionStart;

  const front = textarea.value.substring(0, caretPos);
  const back = textarea.value.substring(
    textarea.selectionEnd,
    textarea.value.length,
  );
  textarea.value = front + text + back;
  caretPos += text.length;
  textarea.selectionStart = caretPos;
  textarea.selectionEnd = caretPos;
  textarea.focus();
  textarea.scrollTop = scrollPos;
}

// 사용자가 텍스트 영역 코드를 업데이트할 때마다 저장된 사용자 코드를 업데이트합니다.

textarea.onkeyup = function () {
  // 사용자 코드가 표시될 때만 상태를 저장하려고 합니다,
  // 솔루션이 아닌 사용자 코드를 통해 솔루션이 저장되지 않도록 합니다.
  if (solution.value === "Show solution") {
    userEntry = textarea.value;
  } else {
    solutionEntry = textarea.value;
  }

  updateCode();
};
```

미디어 애셋 및 라이선스
-------------

이미지들(그리고 다른 미디어 자산들)은 여러 라이선스 타입들 아래에서 찾을 수 있습니다. 당신이 만드는 사이트에 이미지를 사용하기 전에 당신이 그것을 소유하고 있거나, 사용할 수 있는 권한이 있거나, 또는 소유자의 라이선스 조건을 준수하는지 확인하세요.

### 라이선스 타입 이해하기

웹에서 찾을 수 있는 일반적인 라이선스 카테고리들을 살펴봅시다.

#### 모든 권리 보유

음악, 책 또는 소프트웨어와 같은 원본 작업물의 창작자들은 종종 그들의 작업물을 닫힌 저작권 보호 아래에서 발표합니다. 이것은 기본적으로 그들 (또는 그들의 출판사)이 그들의 작업물을 사용하는 (예를 들어, 표시하거나 배포하는) 독점적인 권리를 가지고 있다는 것을 의미합니다. 당신이 저작권 보호 이미지를 사용하고 싶다면, 당신은 다음 중 하나를 해야 합니다.

* 명시적으로 작성된 허가를 저작권 보유자로부터 얻으세요.
* 사용하기 위해 라이선스 비용을 지불하세요. 이것은 무제한 사용을 위한 일회성 비용("로열티 프리")이거나, "권리 관리"일 수 있습니다. 이 경우, 당신은 시간대, 지리적 영역, 산업 또는 미디어 유형 등에 따라 사용당 특정 비용을 지불해야 할 수 있습니다.
* 관할 지역에서 공정 사용 또는 공정 거래로 간주되는 경우에만 사용하세요.

저작권자들은 저작물에 저작권 표시 또는 라이선스 조건을 포함시키는 것이 필수적이지 않습니다. 저작권은 저작물이 유형화된 매체에 창작되면 자동으로 발생합니다. 그래서 당신이 온라인에서 이미지를 찾고, 저작권 표시나 라이선스 조건이 없다면, 가장 안전한 방법은 모든 권리가 보호되는 저작권에 의해 보호된다고 가정하는 것입니다.

#### 허용

MIT, BSD, 또는 적절한 크리에이티브 커먼즈(CC) 라이선스와 같은 허용 라이센스에 따라 이미지를 공개하는 경우, 당신은 라이선스 비용을 지불하거나 사용을 위한 허가를 요청할 필요가 없습니다. 그럼에도 불구하고, 당신은 라이선스에 따라 다양한 라이선스 조건을 충족해야 합니다.

예를 들어 당신은 다음 내용들을 해야 할 수 있습니다.

* 이미지의 원본 소스에 대한 링크를 제공하고, 그것의 창작자에게 크레딧을 제공합니다.
* 변경사항이 있는지 표시합니다.
* 이미지를 사용하여 만든 모든 2차 작업물을 원본과 동일한 라이선스 하에 공유합니다.
* 2차 작업물을 공유하지 않습니다.
* 이미지를 상업적인 작업물에 사용하지 않습니다.
* 이미지를 사용하는 모든 릴리즈와 함께 라이선스의 사본을 포함합니다.

적용 가능한 라이선스를 확인하여 따라야 할 구체적인 조건을 확인해야 합니다.

**참고:** "copyleft"라는 용어를 허용 라이선스의 문맥에서 만날 수 있습니다. Copyleft 라이선스 (예를 들어, GNU General Public License (GPL) 또는 "Share Alike" Creative Commons 라이선스)는 2차 작업물이 원본과 동일한 라이선스 하에 공개되어야 한다고 규정합니다.

Copyleft 라이선스들은 소프트웨어 세계에서 두드러집니다. 기본적인 아이디어는 Copyleft 라이선스를 받은 프로젝트의 코드로 만들어진 새로운 프로젝트(이것은 원본 소프트웨어의 "fork"라고 불립니다)도 동일한 Copyleft 라이선스 하에 라이선스 되어야 한다는 것입니다. 이것은 새로운 프로젝트의 소스 코드가 다른 사람들이 공부하고 수정할 수 있도록 공개되어야 한다는 것을 보장합니다. 일반적으로, 소프트웨어를 위해 작성된 라이선스들은 비소프트웨어 작업물을 위해 작성되지 않았기 때문에, GPL과 같은 라이선스들은 비-소프트웨어 작업물을 위한 좋은 라이선스로 간주되지 않습니다.

이전에 제공된 링크를 확인해서 다른 라이선스 유형과 그들이 지정하는 조건들을 읽어보세요.

#### 공개 도메인/CC0

공개된 도메인은 때때로 "권리가 보호되지 않음"이라고 불립니다. 저작권이 적용되지 않으며 허가 없이 사용할 수 있고 라이선스 조건을 충족할 필요가 없습니다. 저작물은 저작권의 만료 또는 특정 권리 포기와 같은 다양한 방법으로 공개 도메인에 들어갈 수 있습니다.

공개된 도메인에 작업물을 놓는 가장 효과적인 방법 중 하나는 CC0라는 특정 크리에이티브 커먼즈 라이선스를 사용하는 것입니다. 이것은 명확하고 모호하지 않은 법적 도구를 제공합니다.

공개된 도메인을 사용할 때, 이미지가 공개된 도메인에 있는 것임을 증명하고 증명을 기록해 두세요. 예를 들어, 라이선스 상태가 명확하게 표시된 원본 소스의 스크린샷을 찍고, 라이선스 요구사항과 함께 이미지를 획득한 웹사이트에 페이지를 추가하는 것을 고려해 보세요.

### 허가된 라이선스 이미지 검색하기

당신은 당신의 프로젝트를 위해서 허가된 라이선스 이미지를 찾을 수 있습니다. 이미지 검색 엔진이나 이미지 저장소에서 직접 찾을 수 있습니다.

당신이 찾고 있는 이미지에 대한 설명과 관련된 라이선스 조건을 포함해서 이미지를 검색하세요. 예를 들어, "노란 공룡"을 검색할 때 "공개 도메인 이미지", "공개 도메인 이미지 라이브러리", "오픈 라이선스 이미지" 또는 비슷한 용어를 검색어에 추가하세요.

어떤 검색 엔진은 허가된 라이선스 이미지를 찾는 데 도움을 주는 도구를 가지고 있습니다. 예를 들어, Google을 사용할 때, 이미지를 검색하기 위해 "이미지" 탭을 클릭하고, "도구"를 클릭하세요. 그러면 툴바에 "사용 권한" 드롭다운이 나타납니다. 여기서 크리에이티브 커먼즈 라이선스 하에 있는 이미지를 검색할 수 있습니다.

이미지 레포지토리 사이트 (예: Flickr, ShutterStock, Pixabay)는 허가된 라이선스 이미지만 검색할 수 있도록 검색 옵션을 제공합니다. Picryl과 The Noun Project와 같은 사이트는 허가된 라이선스 이미지와 아이콘만 배포합니다.

이미지가 배포된 라이선스를 준수하려면 라이선스 세부 사항을 찾고 소스에서 제공하는 라이선스 또는 지시사항 페이지를 읽고 그 지시사항을 따르면 됩니다. 신뢰할 수 있는 이미지 레포지토리는 라이선스 조건을 명확하고 쉽게 찾을 수 있도록 합니다.

figures 및 figure captions으로 이미지에 주석 달기
--------------------------------------

캡션에 대해 말하자면, 이미지에 캡션을 추가하는 몇 가지 방법이 있습니다. 예를 들어, 다음과 같이 캡션을 추가하는 것을 막을 수는 없습니다.

```html
<div class="figure">
  <img
 src="images/dinosaur.jpg"
 alt="The head and torso of a dinosaur skeleton;
 it has a large head with long sharp teeth"
 width="400"
 height="341" />

  <p>A T-Rex on display in the Manchester University Museum.</p>
</div>
```

이것도 좋습니다. 당신이 필요한 컨텐츠를 포함하고 있습니다. CSS를 사용해서 멋지게 스타일링할 수 있습니다. 하지만 여기에는 문제가 있습니다. 이미지와 캡션을 의미론적으로 연결하는 것이 없습니다. 이것은 스크린 리더에 문제를 일으킬 수 있습니다. 예를 들어, 50개의 이미지와 캡션을 가지고 있을 때, 어떤 캡션이 어떤 이미지와 연결되는지 알 수 없습니다.

더 나은 방법은 HTML `<figure>`와 `<figcaption>` 요소를 사용하는 것입니다. 이것들은 정확히 이 목적을 위해 만들어졌습니다. 즉, figure에 대한 의미론적인 컨테이너를 제공하고, figure를 캡션과 명확하게 연결하는 것입니다. 위의 예제는 다음과 같이 다시 작성할 수 있습니다.

```html
<figure>
  <img
 src="images/dinosaur.jpg"
 alt="The head and torso of a dinosaur skeleton;
 it has a large head with long sharp teeth"
 width="400"
 height="341" />

  <figcaption>
    A T-Rex on display in the Manchester University Museum.
  </figcaption>
</figure>
```

`<figcaption>` 요소는 브라우저와 보조 기술에게 캡션이 `<figure>` 요소의 다른 컨텐츠를 설명한다고 알려줍니다.

**참고:** 접근성의 관점에서, 캡션과 `alt` 텍스트는 다른 역할을 합니다. 캡션은 이미지를 볼 수 있는 사람들에게도 도움이 되지만, `alt` 텍스트는 이미지가 없을 때와 같은 기능을 제공합니다. 따라서, 캡션과 `alt` 텍스트는 같은 것을 말해서는 안 됩니다. 왜냐하면 이미지가 없을 때 둘 다 나타나기 때문입니다. 브라우저에서 이미지를 끄고 어떻게 보이는지 확인해 보세요.

figure는 이미지일 필요가 없습니다. figure는 다음과 같은 독립적인 컨텐츠 단위입니다.

* 이미지의 의미를 간략하고 이해하기 쉬운 방식으로 표현합니다.
* 페이지의 선형 흐름에서 여러 곳에 배치될 수 있습니다.
* 본문을 지원하는 필수적인 정보를 제공합니다.

figure는 여러 이미지, 코드 스니펫, 오디오, 비디오, 방정식, 표 또는 다른 것들이 될 수 있습니다.

### 학습하기: figure 생성하기

이 학습하기 섹션에서, 우리는 이전 학습하기 섹션의 완성된 코드를 가져와서 figure로 바꾸길 원합니다.

1. `<figure>` 요소로 감싸세요.
2. `title` 속성의 텍스트를 복사하고, `title` 속성을 제거하고, 이미지 아래에 `<figcaption>` 요소 안에 텍스트를 넣으세요.

만약 실수했다면, Reset 버튼을 눌러서 다시 시작할 수 있습니다. 만약 정말 막혔다면, Show solution 버튼을 눌러서 답을 확인할 수 있습니다.
```

<h2>Live output</h2>

<div class="output" style="min-height: 50px;"></div>

<h2>Editable code</h2>
<p class="a11y-label">
  Press Esc to move focus away from the code area (Tab inserts a tab character).
</p>

<textarea
 id="code"
 class="input"
 style="min-height: 100px; width: 95%"></textarea>

<div class="playable-buttons">
  <input id="reset" type="button" value="Reset" />
  <input id="solution" type="button" value="Show solution" />
</div>
```

```
html {
  font-family: sans-serif;
}

h2 {
  font-size: 16px;
}

.a11y-label {
  margin: 0;
  text-align: right;
  font-size: 0.7rem;
  width: 98%;
}

body {
  margin: 10px;
  background: #f5f9fa;
}
```

```
const textarea = document.getElementById("code");
const reset = document.getElementById("reset");
const solution = document.getElementById("solution");
const output = document.querySelector(".output");
const code = textarea.value;
let userEntry = textarea.value;

function updateCode() {
  output.innerHTML = textarea.value;
}

const htmlSolution =
  '<figure>\n <img src="https://raw.githubusercontent.com/mdn/learning-area/master/html/multimedia-and-embedding/images-in-html/dinosaur\_small.jpg"\n alt="The head and torso of a dinosaur skeleton; it has a large head with long sharp teeth"\n width="200"\n height="171">\n <figcaption>A T-Rex on display in the Manchester University Museum</figcaption>\n</figure>';
let solutionEntry = htmlSolution;

reset.addEventListener("click", () => {
  textarea.value = code;
  userEntry = textarea.value;
  solutionEntry = htmlSolution;
  solution.value = "Show solution";
  updateCode();
});

solution.addEventListener("click", () => {
  if (solution.value === "Show solution") {
    textarea.value = solutionEntry;
    solution.value = "Hide solution";
  } else {
    textarea.value = userEntry;
    solution.value = "Show solution";
  }
  updateCode();
});

textarea.addEventListener("input", updateCode);
window.addEventListener("load", updateCode);

// textarea 외부에서 탭 키 탭을 중지하고
// 대신 캐럿 위치에 탭을 작성하도록 합니다.

textarea.onkeydown = (e) => {
  if (e.keyCode === 9) {
    e.preventDefault();
    insertAtCaret("\t");
  }

  if (e.keyCode === 27) {
    textarea.blur();
  }
};

function insertAtCaret(text) {
  const scrollPos = textarea.scrollTop;
  let caretPos = textarea.selectionStart;

  const front = textarea.value.substring(0, caretPos);
  const back = textarea.value.substring(
    textarea.selectionEnd,
    textarea.value.length,
  );
  textarea.value = front + text + back;
  caretPos += text.length;
  textarea.selectionStart = caretPos;
  textarea.selectionEnd = caretPos;
  textarea.focus();
  textarea.scrollTop = scrollPos;
}

// 사용자가 텍스트 영역 코드를 업데이트할 때마다 저장된 사용자 코드를 업데이트합니다.

textarea.onkeyup = () => {
  // 사용자 코드가 표시될 때만 상태를 저장하려고 합니다,
  // 솔루션이 아닌 사용자 코드를 통해 솔루션이 저장되지 않도록 합니다.

  if (solution.value === "Show solution") {
    userEntry = textarea.value;
  } else {
    solutionEntry = textarea.value;
  }

  updateCode();
};
```

CSS 배경 이미지
----------

이미지를 웹페이지에 삽입하기 위해 CSS도 사용할 수 있습니다. (JavaScript도 사용할 수 있지만, 이건 완전 다른 이야기 입니다). CSS `background-image` 속성과 다른 `background-*` 속성들은 배경 이미지의 위치를 제어하는 데 사용됩니다. 예를 들어, 페이지의 모든 문단에 배경 이미지를 놓기 위해, 다음과 같이 할 수 있습니다.

```css
p {
  background-image: url("images/dinosaur.jpg");
}
```

임베디드 이미지는 HTML 이미지보다 위치 지정과 제어가 훨씬 쉽습니다. 그렇다면 HTML 이미지를 사용해야 할 이유가 무엇일까요? 위에서 언급했듯이, CSS 배경 이미지는 장식용입니다. 페이지에 예쁜 것을 추가하여 시각적으로 더욱 향상시키고 싶다면, 이것은 괜찮습니다. 하지만, 이러한 이미지는 의미가 없습니다. 텍스트 대체가 없으며, 스크린 리더에서도 보이지 않습니다. 이 부분에서 HTML 이미지가 빛을 발합니다.

요약하기: 이미지가 의미가 있다면, 콘텐츠 관점에서, HTML 이미지를 사용해야 합니다. 이미지가 순전히 장식이라면, CSS 배경 이미지를 사용해야 합니다.

**참고:** CSS 배경 이미지에 대해서는 CSS 주제에서 더 자세히 배울 수 있습니다.

스킬을 테스트 하세요!
------------

기사의 마지막에 도달했지만, 가장 중요한 정보를 기억하고 있나요? 다음으로 넘어가기 전에 이 정보를 기억하고 있는지 확인할 수 있는 몇 가지 테스트를 찾을 수 있습니다. 스킬 테스트하기: HTML 이미지 (en-US)를 참고하세요.

요약
--

여기까지입니다. 이미지와 캡션에 대해 자세히 알아보았습니다. 다음 기사에서는 웹 페이지에 비디오와 오디오 콘텐츠 (en-US)를 삽입하는 방법에 대해 알아보겠습니다.

* Overview: Multimedia and embedding
* 다음  (en-US)


<object> 로부터 <iframe>까지 — 기타 임베딩 기술 - Web 개발 학습하기

<object> 로부터 <iframe>까지 — 기타 임베딩 기술
===================================

* 이전  (en-US)
* Overview: Multimedia and embedding
* 다음

이제 웹 페이지에 이미지와 비디오, 오디오를 추가하는 방법을 이해하셨을 겁니다. 이 시점에서 옆으로 한발 비켜서서, 웹 페이지에 다양한 형태의 콘텐츠를 넣을 수 있는 `<iframe>` (en-US) 요소와 `<embed>` 요소, `<object>` 요소를 살펴보겠습니다. `<iframe>` 요소는 다른 웹 페이지를 삽입하기 위해, 다른 두 요소는 PDF 파일과 같은 외부 자원을 웹 페이지에 추가하기 위해 사용합니다.

|  |  |
| --- | --- |
| 필요한 사전 지식: |
 기본적인 컴퓨터 활용 능력,
 기본적인 소프트웨어 설치,
 파일 작업에 대한 기본 지식
 , HTML 기초 지식(
 HTML 시작하기에서 다룸)과 이 과정에 포함된 이전 문서를 익힘
  |
| 목표: | `<object>` 요소, `<embed>` 요소, `<iframe>` (en-US) 요소를 사용하여 웹 페이지에 PDF 문서나 다른 웹 페이지를 삽입하는 방법을 배워봅니다. |

임베딩의 짧은 역사
----------

과거에는 개별 HTML 페이지에 저장된 웹사이트의 작은 부분을 일컫는 **frame**을 사용하여 웹사이트를 만드는 방식이 유행했습니다. 메인이 되는 문서인 **frameset**에 frame이 삽입되었습니다. 테이블의 행과 열의 크기를 조정하듯 화면에 각 frame이 채워질 영역을 설정할 수 있었습니다. 90년대 중후반에는 이와 같은 방식을 가장 근사하다고 여겼고 이를 증명하기라도 하듯 웹페이지는 더 작은 덩어리로 쪼개졌습니다. 당시 네트워크 속도를 고려했을 때 다운로드 속도가 더 빠르다는 이점이 있었습니다. 하지만 네트워크 속도가 빨라짐에 따라 장점보다 여러 문제점이 더 부각되어 이제는 보기 어려운 방식입니다.

얼마 후인 90년대 후반과 2000년대 초반에는 Java Applets와 Flash 같은 플러그인 기술이 유행하여 웹 개발자는 HTML만으로는 불가능했던 영상이나 애니메이션 같은 풍부한 콘텐츠를 웹페이지에 넣는 작업을 할 수 있게 되었습니다. 이런 작업은 `<object>` 요소나 그보다는 사용 빈도가 낮았던 `<embed>` 요소 등을 이용하면 가능했고 당시에는 매우 유용했습니다. 그러나 접근성이나 보안, 파일 크기 등 여러 문제로 인해 인기가 사그라들었습니다. 최근 주요 브라우저는 Flash 같은 플러그인에 대한 지원을 중단했습니다.

마지막으로 `<canvas>`나 `<video>` 등 콘텐츠를 삽입하는 다른 방식과 함께 `<iframe>` (en-US)요소가 등장했습니다. 이 요소를 사용하면 `<img>`요소를 웹 문서에 포함하듯이 전체 웹 문서를 다른 웹 문서에 넣을 수 있습니다. 요즘 주로 사용하는 방식입니다.

역사 이야기는 여기서 마치고 이러한 요소를 어떻게 사용하는지 알아보겠습니다.

능동적으로 학습하기: 대표적인 임베딩 사용
-----------------------

이번 글에서는 곧바로 능동적으로 학습하기 부분으로 바로 넘어가겠습니다. 임베딩 기술이 어떻게 유용한지 바로 알 수 있도록 말이죠. 온라인 세상에서 YouTube는 굉장히 익숙하지만, YouTube에서 제공하는 영상 공유 방법을 모두 알고 있는 사람은 많지 않습니다. `<iframe>` (en-US) 요소를 이용하여 우리가 원하는 웹 페이지에 YouTube 영상을 삽입하는 방법을 알아보겠습니다.

1. 우선 YouTube에 접속하여 원하는 영상을 찾습니다.
2. 영상 아래에 있는 "공유" 버튼을 선택하여 공유 옵션을 화면에 표시하세요.
3. "퍼가기" 버튼을 선택하면 `<iframe>` 코드가 나옵니다. 해당 코드를 복사하세요.
4. 아래에 있는 입력 상자에 복사한 코드를 붙여 넣은 후 결과 화면을 살펴보세요.

추가로 Google Map도 넣어볼 수 있습니다. 다음 예시를 참고해보세요.

1. Google Map에 접속하여 원하는 지도를 찾습니다.
2. UI 왼쪽 상단에 위치한 가로줄이 세 개 있는 "메뉴" 버튼을 클릭하세요.
3. "공유" 옵션을 선택합니다.
4. "지도 퍼가기" 옵션을 선택하면 `<iframe>` 코드가 나옵니다. 해당 코드를 복사하세요.
5. 아래에 있는 입력 상자에 복사한 코드를 붙여 넣은 후 결과 화면을 살펴보세요.

잘못 입력했다면 언제든 *Reset* 버튼을 눌러 초기화할 수 있습니다. 도저히 모르겠다면 *Show solution* 버튼을 눌러 답을 확인해보세요.
```

<h2>실시간 결과</h2>

<div class="output" style="min-height: 250px;"></div>

<h2>편집 가능한 코드</h2>
<p class="a11y-label">
  코드 영역에서 포커스를 움직이기 위해서는 ESC 키를 누르세요 (Tab 키를 누르면 탭
  문자가 입력됩니다).
</p>

<textarea
 id="code"
 class="input"
 style="width: 95%;min-height: 100px;"></textarea>

<div class="playable-buttons">
  <input id="reset" type="button" value="Reset" />
  <input id="solution" type="button" value="Show solution" />
</div>
```

```
html {
  font-family: sans-serif;
}

h2 {
  font-size: 16px;
}

.a11y-label {
  margin: 0;
  text-align: right;
  font-size: 0.7rem;
  width: 98%;
}

body {
  margin: 10px;
  background: #f5f9fa;
}
```

```
const textarea = document.getElementById("code");
const reset = document.getElementById("reset");
const solution = document.getElementById("solution");
const output = document.querySelector(".output");
let code = textarea.value;
let userEntry = textarea.value;

function updateCode() {
  output.innerHTML = textarea.value;
}

reset.addEventListener("click", function () {
  textarea.value = code;
  userEntry = textarea.value;
  solutionEntry = htmlSolution;
  solution.value = "Show solution";
  updateCode();
});

solution.addEventListener("click", function () {
  if (solution.value === "Show solution") {
    textarea.value = solutionEntry;
    solution.value = "Hide solution";
  } else {
    textarea.value = userEntry;
    solution.value = "Show solution";
  }
  updateCode();
});

const htmlSolution =
  '<iframe width="420" height="315" src="https://www.youtube.com/embed/QH2-TGUlwu4" frameborder="0" allowfullscreen>\n</iframe>\n\n<iframe src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d37995.65748333395!2d-2.273568166412784!3d53.473310471916975!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x487bae6c05743d3d%3A0xf82fddd1e49fc0a1!2sThe+Lowry!5e0!3m2!1sen!2suk!4v1518171785211" width="600" height="450" frameborder="0" style="border:0" allowfullscreen>\n</iframe>';
let solutionEntry = htmlSolution;

textarea.addEventListener("input", updateCode);
window.addEventListener("load", updateCode);

// stop tab key tabbing out of textarea and
// make it write a tab at the caret position instead

textarea.onkeydown = function (e) {
  if (e.keyCode === 9) {
    e.preventDefault();
    insertAtCaret("\t");
  }

  if (e.keyCode === 27) {
    textarea.blur();
  }
};

function insertAtCaret(text) {
  const scrollPos = textarea.scrollTop;
  let caretPos = textarea.selectionStart;

  const front = textarea.value.substring(0, caretPos);
  const back = textarea.value.substring(
    textarea.selectionEnd,
    textarea.value.length,
  );
  textarea.value = front + text + back;
  caretPos += text.length;
  textarea.selectionStart = caretPos;
  textarea.selectionEnd = caretPos;
  textarea.focus();
  textarea.scrollTop = scrollPos;
}

// Update the saved userCode every time the user updates the text area code

textarea.onkeyup = function () {
  // We only want to save the state when the user code is being shown,
  // not the solution, so that solution is not saved over the user code
  if (solution.value === "Show solution") {
    userEntry = textarea.value;
  } else {
    solutionEntry = textarea.value;
  }

  updateCode();
};
```

iframes 자세히 보기
--------------

간단하고 재밌었죠? `<iframe>` (en-US) 요소를 사용하면 현재 웹 문서에 다른 웹 문서를 삽입할 수 있습니다. 이는 직접 조작할 수 없거나 자체적인 버전을 구현하고 싶지 않은 외부 콘텐츠를 여러분의 웹사이트에 포함할 때 아주 유용합니다. 예를 들면 온라인 비디오 제공자의 영상이나 Disqus와 같은 댓글 시스템, 온라인 지도 제공자의 지도, 광고 배너 등이 있습니다. 이 과정에서 사용하고 있는 실시간 예제도 `<iframe>`을 사용하여 구현했습니다.

`<iframe>`를 사용하기 전에 알고 계셔야 할 보안 문제가 몇 가지 존재합니다. 웹 페이지에 MDN 용어사전을 넣으려고 한다면 아마 아래와 같은 형태로 코드를 작성하실 겁니다. 아래 코드를 웹 페이지에 추가하면 용어사전 페이지 대신 에러 메시지가 표시되는 걸 보고 놀랄 수 있습니다.

```html
<head>
  <style>
 iframe {
 border: none;
 }
 </style>
</head>
<body>
  <iframe
 src="https://developer.mozilla.org/ko/docs/Glossary"
 width="100%"
 height="500"
 allowfullscreen
 sandbox>
    <p>
      <a href="/ko/docs/Glossary"> iframes을 지원하지 않는 브라우저용 링크 </a>
    </p>
  </iframe>
</body>
```

브라우저 콘솔 창을 보면 아래와 같은 에러 메시지를 확인할 수 있습니다.
```

Refused to display 'https://developer.mozilla.org/' in a frame because it set 'X-Frame-Options' to 'deny'.
```

아래에 있는 보안 부분에서 왜 이런 에러가 표시되는지 좀 더 자세히 다룹니다. 우선 지금은 이 코드가 어떤 작업을 하는지 살펴봅시다.

이 예시는 `<iframe>`을 사용하기 위한 가장 기본적인 필수 요소를 포함하고 있습니다.

`border: none`

이를 적용하면, `<iframe>` 은 테두리 없이 표시됩니다. 적용하지 않으면 브라우저는 `<iframe>`을 기본적으로 테두리가 있는 상태로 표시하는데, 일반적으로 바람직하지는 않습니다.

`allowfullscreen` (en-US)

이를 설정하면, `<iframe>`에서 Fullscreen API를 통해 전제화면 모드를 실행할 수 있습니다. Fullscreen API는 본문에서 다루는 범위를 넘어선 내용입니다.

`src` (en-US)

`<video>`/`<img>`와 마찬가지로 삽입될 문서의 URL 경로를 저장합니다.

`width` (en-US) 와 `height` (en-US)

iframe 요소에 원하는 너비와 높이를 설정할 수 있습니다.

대체 콘텐츠

`<video>` 요소와 같이 유사한 다른 요소들과 마찬가지로, `<iframe></iframe>` 여닫는 태그 사이에 대체 콘텐츠를 추가하여 브라우저가 `<iframe>`을 지원하지 않는 경우 대체 콘텐츠를 표시할 수 있습니다. 위 예시에서는 해당 페이지로 이동하는 링크를 추가하였습니다. 요즘 `<iframe>`을 지원하지 않는 브라우저를 마주칠 일은 거의 없겠지만요.

`sandbox` (en-US)

IE 10 이상에서 지원되는`<iframe>`의 다른 기능보다 이 특성은 상대적으로 최신 브라우저에서 작동하며 높은 보안 설정을 요구합니다. 이에 대해서는 다음 내용에서 다룰 예정입니다.

**참고:** 속도를 향상하기 위해 메인 콘텐츠가 완전히 로딩된 이후에 JavaScript로 iframe의 src 특성을 설정하는 편이 좋습니다. 웹 페이지를 더 빠르게 이용할 수 있고 SEO 측정 시 중요한 지표인 웹 페이지 로딩 시간을 단축할 수 있습니다.

### 보안 문제

위에서 언급했던 보안 문제를 좀 더 자세히 살펴봅시다. 이 내용을 한 번에 완벽하게 이해하실 필요는 없습니다. 다만 이러한 문제가 있다는 점을 알고 계시길 바라며 참고 자료를 제공하오니 경험을 더 쌓고 `<iframe>`을 작업에 사용하려고 할 때 돌아와서 보시기를 바랍니다. 또한 겁먹고 `<iframe>` 사용을 피할 이유는 없습니다. 주의해서 사용하기만 한다면요. 계속 읽어보세요.

브라우저 제작사와 웹 개발자들이 얻은 값비싼 교훈이 있는데요, iframe이 웹상에서 악의를 품은 사람들의 일반적인 공격 목표(공식 용어로는 **공격 벡터**)가 된다는 점입니다. **해커**, 더 정확히는 **크래커**라 불리는 이 나쁜 사람들은 iframe을 악용하여 여러분의 웹페이지를 수정하거나, 사람들을 속여 사용자 명이나 비밀번호 등 민감한 정보를 유출하려 합니다. 이 때문에 명세 엔지니어와 브라우저 개발자는 `<iframe>`을 더 안전하게 만들기 위해 다양한 보안 장치를 개발했고, 고려할만한 모범 사례도 있습니다. 아래에서 다뤄보겠습니다.

**참고:** 클릭재킹은 iframe 공격 방식의 하나입니다. 눈에 보이지 않는 iframe을 여러분의 웹 문서에 삽입하거나, 해커들의 악성 웹사이트에 여러분의 웹페이지를 삽입하여 사용자들의 활동을 빼돌립니다. 이는 사용자들을 잘못된 행동으로 유도하거나 민감한 데이터를 훔쳐내는 일반적인 기술입니다.

우선 간단한 예시를 보겠습니다. 위에서 보여드린 예제를 브라우저에 표시해 보세요. GitHub에서 실시간 동작 확인도 가능합니다(소스 코드도 살펴보세요). 예상과 달리 "페이지를 열 수 없음"과 같은 문구가 표시될 겁니다. 브라우저 개발자 도구 (en-US)를 열어 '콘솔' 창을 보면 그 이유를 알려주는 문구를 확인할 수 있습니다. Firefox 브라우저의 경우 "The loading of 'https://developer.mozilla.org/ko/docs/Glossary' in a frame is denied by 'X-Frame-Options' directive set to 'DENY'"라는 문구가 적혀 있습니다. 이는 다른 사이트에서 MDN 웹페이지를 `<iframe>`안에 포함하지 못하도록 MDN 개발자들이 설정했기 때문입니다. (아래에 있는 CSP 지시어를 설정하세요 참고) 당연한 일입니다. 여러분의 웹사이트에 전체 MDN 페이지를 삽입해두고 자기 사이트라고 우기거나 클릭재킹으로 데이터를 훔치려 들면 안 되잖아요. 게다가 모두가 MDN 페이지를 삽입한다면 대역폭이 추가로 들어 Mozilla에서 큰 비용을 지불해야 할 겁니다.

#### 필요한 경우에만 삽입하세요

가끔 YouTube 영상이나 지도 같은 외부 콘텐츠를 삽입해야 하는 경우가 있습니다. 정말로 필요한 경우에만 외부 콘텐츠를 삽입한다면 골머리를 앓을 일은 별로 없을 겁니다. 웹 보안을 위해 이런 규칙을 따르면 좋습니다. "아무리 조심해도 지나치지 않습니다. 당신이 만들었다면 아무튼 다시 확인하세요. 다른 사람이 만들었다면 안전하다고 증명되기 전까지 위험 요소가 있다고 간주하세요."

보안 외에도 지적재산권 문제도 알고 있어야 합니다. 온라인, 오프라인을 막론하고 대부분의 콘텐츠는 저작권 보호를 받습니다. Wikimedia Commons에 있는 대다수의 이미지를 포함하여 생각지도 못한 콘텐츠에도 저작권이 있습니다. 본인 소유가 아니거나 저작권자에게 서면으로 명백한 허락을 받지 않았다면 자신의 웹페이지에 해당 콘텐츠를 절대로 게시해서는 안 됩니다. 저작권 위반에 대한 처벌은 엄중합니다. 다시 한번 말씀드리지만, 아무리 조심해도 지나치지 않습니다.

콘텐츠의 라이선스를 얻었다면, 라이선스 규정을 준수해야 합니다. 예를 들어 MDN에 있는 콘텐츠는 저작권표시-동일조건변경허락 (CC-BY-SA) 라이선스를 적용하고 있습니다. 즉 MDN 콘텐츠를 인용할 때 내용을 상당 부분 수정하더라도 올바른 저작권 표시를 해야 합니다.

#### HTTPS를 사용하세요

HTTPS는 HTTP가 암호화된 버전입니다. 불가피한 경우를 제외하고는 항상 HTTPS를 사용하여 웹사이트를 전송해야 합니다.

1. HTTPS는 콘텐츠가 전송 도중에 변조될 위험을 줄여줍니다.
2. HTTPS는 삽입된 콘텐츠와 이를 포함하고 있는 문서가 서로에게 접근하는 것을 방지합니다.

웹사이트에 HTTPS를 적용하려면 특별한 보안 인증서를 설치해야 합니다. 여러 호스팅 업체에서는 직접 인증서를 설치하지 않아도 HTTPS를 적용할 수 있는 호스팅을 제공합니다. 하지만 직접 HTTPS를 적용해야 한다면, Let's Encrypt에서 Apache 웹 서버나 Nginx 등 널리 사용되는 웹 서버를 지원할 뿐만 아니라 자동으로 필요한 인증서를 생성하고 설치할 수 있는 도구와 설명서를 제공하고 있습니다. Let's Encrypt는 이 과정을 최대한 쉽게 만들도록 설계되었습니다. 그러니 웹페이지에 HTTPS를 적용하기 위하여 Let's Encrypt를 사용하지 않을 이유가 전혀 없습니다.

**참고:** GitHub pages (en-US)는 기본적으로 HTTPS를 통해 콘텐츠 전송을 허용하므로 콘텐츠를 호스팅하기에 유용합니다. 만약 다른 호스팅 업체를 사용하는데 HTTPS 적용 여부가 확실하지 않다면 업체에 문의해보세요.

#### 항상 `sandbox` 특성을 사용하세요

웹사이트를 악용하려는 사람들이 공격할 여지를 최소화하기 위해 삽입된 콘텐츠에 대해서는 필요한 작업만 허용해야 합니다. 물론 여러분의 콘텐츠에도 해당합니다. 다른 코드베이스에 영향을 미치지 않으면서도 특정 코드를 테스트하거나 적절하게 사용할 수 있도록 코드를 감싸고 있는 영역을 sandbox라고 합니다.

샌드박스를 적용하지 않은 콘텐츠는 JavaScript를 실행하거나, 폼을 제출하거나, 새 창을 띄우는 등의 작업을 할 수 있습니다. 이전 예시에서 보았듯 기본적으로 `sandbox` 특성을 파라미터 없이 사용하여 가능한 모든 제약을 부과해야 합니다.

꼭 필요한 경우 `sandbox=""` 특성값 안에 하나씩 권한을 부여할 수 있습니다. `sandbox` (en-US) 항목을 참고하여 사용할 수 있는 옵션을 살펴보세요. 한 가지 명심하셔야 할 점은 절대로 `sandbox` 특성에 `allow-scripts`와 `allow-same-origin` 옵션을 동시에 적용하면 안 된다는 점입니다. 두 옵션을 동시에 적용하면 삽입된 콘텐츠는 스크립트 실행을 방지하는 동일 출처 정책을 우회하여 샌드박스 특성을 해제하는 JavaScript를 사용할 수 있게 됩니다.

**참고:** 공격자들이 사용자를 속여 `iframe` 외부에서 직접 악성 콘텐츠로 접근하게 한다면 샌드박스는 이를 보호할 수 없습니다. 만약 사용자가 생성한 콘텐츠 등 악의적인 콘텐츠일 가능성이 있는 경우, 여러분의 웹사이트와 다른 domain에서 메인 사이트로 제공하도록 하세요.

#### CSP 지시어를 설정하세요

CSP는 **콘텐츠 보안 정책** 을 나타내며 HTML 문서 보안을 개선하기 위해 고안된 일련의 HTTP 헤더를 제공합니다. HTTP 헤더란 웹 서버에서 웹페이지가 전송될 때 동반되는 메타데이터입니다. `<iframe>` 보안과 연관 지어 말씀드리자면, 적절한 `X-Frame-Options` 헤더를 전송하도록 설정할 수 있습니다. 이렇게 하면 다른 웹사이트에서 여러분의 웹페이지를 삽입하지 못하도록 만들어서 클릭재킹이나 다른 공격의 대상이 되는 일을 막을 수 있습니다. 이전 예시에서 보셨듯이 이것이 바로 MDN 개발자들이 설정해 둔 것입니다.

**참고:** 프레데리크 브라운의 게시글 On the X-Frame-Options Security Header에서 이 주제와 관련된 배경지식을 더 찾아볼 수 있습니다. 자세한 설명을 다루기에는 이 글의 범위를 벗어납니다.

<embed> 와 <object> 요소
---------------------

`<embed>`와 `<object>`요소는 `<iframe>` (en-US)와 다른 기능을 제공합니다. 이 요소들은 PDF 같은 외부 콘텐츠를 포함하기 위한 일반적인 임베딩 도구입니다.

하지만 이 요소를 사용하는 경우는 별로 없습니다. PDF를 표시하려면 웹페이지에 포함하기보다 보통 파일을 링크로 연결하는 편이 좋습니다.

역사적으로 이 요소들은 Adobe Flash처럼 브라우저 plugins이 제어하는 콘텐츠를 삽입하는 데도 사용되었습니다. 하지만 이 기술은 이제 최신 브라우저에서 지원하지 않습니다.

만약 플러그인 콘텐츠를 삽입하려면 적어도 아래와 같은 정보를 알고 있어야 합니다.

|  | `<embed>` | `<object>` |
| --- | --- | --- |
| 삽입된 콘텐츠의 URL | `src` | `data` |
| 삽입된 콘텐츠의 정확한 media type | `type` | `type` |
| 플러그인이 제어하는 박스의 높이와 너비(CSS 픽셀단위) | `height``width` | `height``width` |
| 플러그인으로 넘길 매개변수의 이름과 값 | 동일한 이름과 값을 가진 특성 | 단일 태그 `<param>` (en-US)요소, `<object>`태그 안에 포함 |
| 이용 불가 리소스에 대한 대체 HTML 콘텐츠 | 지원하지 않음 (`<noembed>`는 더 이상 사용되지 않음) | `<object>`태그 안에 포함,
 `<param>` 요소 다음
  |

PDF 파일을 페이지에 포함하는 `<object>` 사용 예제를 보겠습니다. (동작 예시와 소스 코드를 참고하세요)

```html
<object data="mypdf.pdf" type="application/pdf" width="800" height="1200">
  <p>
    You don't have a PDF plugin, but you can
    <a href="mypdf.pdf">download the PDF file. </a>
  </p>
</object>
```

PDF는 종이와 디지털을 연결하는 필수적인 징검다리 역할을 했습니다. 하지만 많은 접근성 문제가 있었고 작은 화면에서 읽기 어려웠습니다. 여전히 이를 선호하는 사람들도 있지만 웹페이지에 삽입하기보다 링크를 통해 내려받거나 별도의 페이지에서 읽을 수 있도록 하는 편이 더 낫습니다.

실력을 테스트해보세요!
------------

이 글의 마지막에 다다랐습니다. 가장 중요한 정보가 무엇인지 기억하시나요? 다른 글로 넘어가기 전에 이번에 배운 내용을 확인할 수 있는 테스트가 있습니다. Test your skills: Multimedia and embedding (en-US) 글을 참고하세요.

요약
--

웹 문서에 다른 콘텐츠를 삽입하는 주제는 복잡한 이야기로 빠지기 쉽습니다. 그래서 이번 글에서는 고급 기능에 대한 힌트도 있으면서 간단하고 친숙한 형태로 소개했습니다. 처음에는 지도나 영상 외에 외부 콘텐츠를 웹페이지에 삽입할 일은 많지 않을 겁니다. 하지만 점점 경험이 많아지면서 더 많은 용도를 알게 될 겁니다.

여기서 다룬 것 외에도 외부 콘텐츠를 삽입하는 기술은 여럿 존재합니다. 이전 글에서 다룬 `<video>`나 `<audio>` (en-US), `<img>` 등이 있으며, 아직 살펴보지 않은 기술로는 JavaScript로 생성한 2D, 3D 그래픽을 삽입하는 `<canvas>`, 벡터 그래픽을 삽입하는 `<svg>` (en-US) 등이 있습니다. 이 과정의 다음 글에서 SVG를 살펴보겠습니다.

* 이전  (en-US)
* Overview: Multimedia and embedding
* 다음


Git and GitHub - Web 개발 학습하기

Git and GitHub
==============

모든 개발자는 **버전 관리 시스템** (**VCS**)을 사용하거나 앞으로 그럴 것이다. VCS는 다른 개발자와의 협업 시 코드 중복작성으로 인한 손실을 방지해 주고, 문재가 생겼을 시 이전 버전으로의 롤백을 쉽게 해준다. 가장 유명한 VCS는 Git와 그와 관련된 소셜 코딩 사이트, GitHub이다. 이 글은 이 두가지에 대해 설명할 것이다.

개요
--

VCS는 소프트웨어 개발을 할 때 필수적이다.

* 프로젝트를 완전히 직접 수행하는 일은 드물며 다른 사람들과 작업을 시작하자마자 서로의 작업과 충돌할 위험이 있다. 대표적인 예시가 두 사람이 같은 부분을 업데이트하려고 할 때이다. 동시에 코드의. 발생을 관리하고 결과적으로 작업 손실을 피할 수있는 메커니즘이 필요하다.
* 혼자 또는 다른 사람들과 함께 프로젝트를 수행 할 때 중앙 위치에 코드를 백업 할 수 있기 때문에 컴퓨터가 고장 나더라도 코드가 손실되지 않는다.
* 추후에 문제가 발견될 경우, 이전 버전을 찾게 될 수 있다. 우리는 이미 같은 파일의 다른 버전들을 생성함으로써 우리는 버전 컨트롤을 하고 있었을지도 모른다. 예를 들어, `myCode.js`, `myCode_v2.js`, `myCode_v3.js`, `myCode_final.js`, `myCode_really_really_final.js`, 등 이런식으로 말이다. 그러나 이런 방식은 에러에 취약하며 신뢰성이 떨어진다.
* Different team members will commonly want to create their own separate versions of the code (called **branches** in Git), work on a new feature in that version, and then get it merged in a controlled manner (in GitHub we use **pull requests**) with the master version when they are done with it.

VCSes provide tools to meet the above needs. Git is an example of a VCS, and GitHub is a web site + infrastructure that provides a Git server plus a number of really useful tools for working with git repositories individually or in teams, such as reporting issues with the code, reviewing tools, project management features such as assigning tasks and task statuses, and more.

**참고:** Git is actually a *distributed* version control system, meaning that a complete copy of the repository containing the codebase is made on your computer (and everyone else's). You make changes to your own copy, and then push those changes back up to the server, where an administrator will decide whether to merge your changes with the master copy.

전제 조건
-----

Git와 GitHub를 사용하려면, 다음과 같은 사양이 요구된다:

* Git가 설치된 데스크탑 컴퓨터(Git 설치 페이지를 참고하라).
* Git을 사용할 도구. 당신이 어떻게 일하고 싶은지에 따라 취향에 맞게 Git GUI client (GitHub Desktop, SourceTree, Git Kraken을 추천합니다) 와 터미널 중에 고르실 수 있습니다. GUI를 사용하더라도 기본적인 git 터미널 명령어들을 숙지하고 있다면 더 큰 도움이 될 것입니다.
* GitHub 계정. 만일 계정이 없다면, 이 링크에서 계정을 만들 수 있다.

이 모듈에 필요한 전제 지식과 관련해서 웹개발, Git/Github, VCS에 관한 지식은 없어도 시작할 수 있다. 그러나 어느정도의 컴퓨터 소양과 코딩 능력이 뒷바침되면 repository에 코드를 저장해 놓을 수 있으므로 코딩 지식이 있으면 좋다.

코딩 지식과 더불어 기초적인 용어에 대한 지식이 있으면 좋다. 예를 들어, 디렉토리를 이동하거나 파일생성, 그리고 시스템 `PATH`를 변경시키는것과 같은 기본적인 용어에 대한 지식이 있는 것이 추천된다.

**참고:** Github is not the only site/toolset you can use with Git. There are other alternatives such as GitLab that you could try, and you could also try setting your own Git server up and using it instead of GitHub. We've only stuck with GitHub in this course to provide a single way that works.

가이드
---

아래 링크들은 모두 외부 사이트로 연결된다. Mozilla는 Mozilla만의 독자적인 Git/Github 과정을 만드는 것을 목표로 하고 있으나 당장은 아래에 있는 항목들이 Github을 직접 해볼 수 있도록 도와줄 것이다.

Hello World (from GitHub)

This is a good place to start — this practical guide gets you to jump right into using GitHub, learning the basics of Git such as creating repositories and branches, making commits, and opening and merging pull requests.

Git Handbook (from GitHub)

This Git Handbook goes into a little more depth, explaining what a VCS is, what a repository is, how the basic GitHub model works, Git commands and examples, and more.

Forking Projects (from GitHub)

Forking projects is essential when you want to contribute to someone else's code. This guide explains how.

About Pull Requests (from GitHub)

A useful guide to managing pull requests, the way that your suggested code changes are delivered to people's repositories for consideration.

Mastering issues (from GitHub)

Issues are like a forum for your GitHub project, where people can ask questions and report problems, and you can manage updates (for example assigning people to fix issues, clarifying the issue, letting people know things are fixed). This articles gives you what you need to know about issues.

**참고:** There is **a lot more** that you can do with Git and GitHub, but we feel that the above represents the minimum you need to know to start using Git effectively. As you get deeper into Git, you'll start to realise that it is easy to go wrong when you start using more complicated commands. Don't worry, even professional web developers find Git confusing sometimes, and often solve problems by searching for solutions on the web, or consulting sites like Flight rules for Git and Dangit, git!

바깥 고리
-----

* Understanding the GitHub flow
* Git command list
* Mastering markdown (the text format you write in on PR, issue comments, and `.md` files).
* Getting Started with GitHub Pages (how to publish demos and websites on GitHub).
* Learn Git branching
* Flight rules for Git (a very useful compendium of ways to achieve specific things in Git, including how to correct things when you went wrong).
* Dangit, git! (another useful compendium, specifically of ways to correct things when you went wrong).


Understanding client-side JavaScript frameworks - Web 개발 학습하기

Understanding client-side JavaScript frameworks
===============================================

JavaScript 프레임워크는 최신 프론트엔드 웹 개발의 필수 부분으로, 개발자에게 확장 가능한 대화형 웹 응용 프로그램을 구축하기 위한 검증된 도구를 제공합니다. 많은 현대 기업들은 프레임워크를 도구화(tooling)의 표준 부분으로 사용하기때문에, 프론트엔드 개발에는 이제 프레임워크 경험이 필요합니다.

프론트엔드 개발자 지망생으로서 프레임워크를 배울 때 시작해야 할 부분을 알기가 어려울 수 있습니다. 선택할 수 있는 프레임워크가 너무 많고, 새로운 프레임워크는 항상 나타나고,대부분 유사한 방식으로 작동하지만 다른 일을 합니다. 때문에 프레임워크를 사용할 때 주의해야 할 몇 가지 사항이 있습니다.

이 글에서는 프레임워크를 배우는데 도움이되는 편안한 출발점을 제공하고자 합니다. 우리는 React/ReactDOM, Vue, 또는 그외 특정 프레임워크에 대해 알아야 할 모든 것을 철저하게 가르치려는것이 아닙니다. 프레임워크 자체 문서들 및 다른 리소스들이 이미 철저하게 가르쳐주고 있습니다. 대신 우리는 다음과 같은, 보다 근본적인 질문에 답하고 지원하려고 합니다.

* 왜 프레임워크를 사용해야 하나요? 그들이 어떤 문제를 해결하나요?
* 프레임워크를 선택할 때 어떤 질문을 해야 하나요? 프레임워크를 사용해야 하나요?
* 프레임워크에는 어떤 기능이 있나요? 그것들은 일반적으로 어떻게 작동하고, 이러한 기능에 대한 프레임워크에 구현은 어떻게 다른가요?
* "vanilla" JavaScript 또는 HTML과 어떤 관련이 있나요?

그 후에, 우리는 몇 가지 다른 프레임워크 선택의 핵심을 다루는 튜토리얼을 제공할 것입니다. 이는 더 심도있게 학습하기에 충분한 맥락과 친숙함을 제공해줄겁니다. 우리는 여러분이 앞으로 나아가 접근성과 같은 웹 플랫폼의 근본적인 모범 사례를 잊지 않는 실용적인 방법으로 프레임워크에 대해 배우고 발전시키기를 바랍니다.

**"클라이언트 사이드 프레임워크 소개"문서로 지금 시작하세요!**

전제조건
----

클라이언트 사이드 프레임워크를 배우기 전에 HTML, CSS, JavaScript 와 같은 핵심 웹 언어의 기초를 익혀야 합니다.

결과적으로 코드는 더욱 풍부해지고 전문적이게 될 것이며, 프레임워크가 구축하고 있는 근본적인 웹 플랫폼 기능을 이해한다면 보다 확실하게 문제를 해결할 수 있을 겁니다.

### 프론트 개발자가 되고 싶으신가요?목표를 달성하는데 필요한 모든 필수 정보가 포함 된 과정을 구성했습니다.시작하기

입문 가이드
------

1. 클라이언트 사이드 프레임워크 소개

우리는 이 영역에 대한 일반적인 개요로 프레임워크를 살펴 볼겁니다. JavaScript와 프레임워크의 간략한 역사, 프레임워크가 왜 존재하는지와 그들이 우리에게 무엇을 제공하는지, 학습을 프레임워크를 어떻게 선택할지 생각하는 방법, 클라이언트 사이드 프레임워크에 대한 어떤 대안이 있는지 등이 해당합니다.

2. 프레임워크 주요 기능

각 주요 JavaScript 프레임워크는 DOM 업데이트, 브라우저 이벤트 처리, 즐거운 개발 경험에 대해 다양한 접근 방식을 갖고 있습니다. 이 글에서는 "4대" 프레임워크의 주요 기능을 살펴보고 프레임워크가 높은 수준에서 작동하는 방식과 프레임워크의 차이점을 살펴봅니다.

리액트 튜토리얼
--------

**참고:** **참조**: React/ReactDOM 16.13.1과 create-react-app 3.4.1. 을 사용하여 2020년 5월에 마지막으로 테스트된 튜토리얼입니다.

우리의 버전에 대해 여러분의 코드를 확인해야 하는 경우, 우리의 todo-react repository에서 샘플 React app 코드의 완성된 버전을 볼 수 있습니다. 라이브 버전을 실행하려면https://mdn.github.io/todo-react-build/을 참조하세요.

1. React 시작하기

이 글은 리액트에 대한 첫걸음입니다. 우리는 리액트의 배경과 사용 사례에 대한 약간의 세부 사항을 학습합니다. 우리 컴퓨터 로컬에서 기본적인 리액트 툴체인을 설정하고, 간단한 스타터 앱을 만들어 리액트가 어떻게 작동하는지 배웁니다.

2. React todo list 시작하기

우리가 React에서 개념 증명(proof-of-concept)을 작성하는 임무를 맡았다고 가정 해 봅시다. 사용자가 작업하려는 과제(task)를 추가, 수정, 삭제 혹은 그것들을 삭제하는 대신 완료된 것으로 표시할 수 있는 앱입니다. 이 글에서는 기본 앱 컴포넌트 구조와 스타일을 배치하는 과정, 각각의 컴포넌트를 정의하고 상호작용하는 방법을 설명합니다.

3. React app 컴포넌트 세분화

이 시점에서 우리의 앱은 단일체(monolith)입니다. 우리는 이것을 관리 가능하고 설명적인 컴포넌트들로 분리해야 합니다. React에는 컴포넌트와 컴포넌트가 아닌것에 대한 엄격한 규칙이 없습니다. 그것은 사용자에게 달려있습니다. 이 글에서는 앱을 컴포넌트로 분해하는 합리적인 방법을 보여줍니다.

4. React 상호작용(interactivity): Events 와 state

컴포넌트 구성이 완성되면 완전한 정적 UI 에서 실제로 상호작용하고 변화시킬 수 있는 UI로 앱을 업데이트할 차례입니다. 이 글에서는 이벤트와 state에 대해 자세히 알아봅니다.

5. React 상호작용(interactivity): 수정, 필터링, 조건부 렌더링

리액트 여정이 끝날 무렵(적어도 현재로서는) Todo list 앱의 주요 기능 영역에 마무리 작업을 추가합니다. 여기서는 기존 과제(task)를 편집할 수 있도록 하고, 완료된 과제와 완료되지 않은 과제 모두 필터링 하는 작업이 포함됩니다. 또한 도중에 조건부 UI 렌더링도 살펴보겠습니다.

6. React 접근성(Accessibility)

이 글에서는 React의 포커스 관리를 포함하여 접근성에 관점을 맞출것입니다. 키보드 전용 및 스크린 리더기 사용자 모두에게 유용성을 개선하고 혼동을 줄일 수 있습니다.

7. React 리소스(resources)

우리의 마지막 글은 학습을 계속하기 위해 사용할 수 있는 React 리소스 목록을 제공합니다.

Ember tutorials
---------------

**참고:** **참조**: Ember/Ember CLI 3.18.0을 사용하여 2020년 5월에 마지막으로 테스트된 튜토리얼입니다.

우리의 버전에 대해 여러분의 코드를 확인해야 하는 경우, 우리의 ember-todomvc-tutorial repository에서 샘플 Ember app 코드의 완성된 버전을 볼 수 있습니다. 라이브 버전을 실행하려면 https://nullvoxpopuli.github.io/ember-todomvc-tutorial/ 을 참조하세요. (여기에는 튜토리얼에서 다루지 않은 몇 가지 기능도 포함됩니다.)

1. Ember 시작하기 (en-US)

첫 번째 Ember 글에서는 Ember 의 작동방식과 유용한 기능에 대해 살펴봅니다. 로컬에 Ember 툴체인을 설치하고, 샘플 앱을 만들어 개발 준비를 위한 초기 설정을 수행합니다.

2. Ember app 구조와 컴포넌트화 (en-US)

이 글에서는 TodoMVC Ember app의 구조를 설계하는 방법에 대해 알아봅니다. HTML을 추가하고, HTML 구조를 컴포넌트로 나눕니다.

3. Ember 상호작용(interactivity): Events, classes, state (en-US)

이제 앱에 상호작용하는 기능을 추가하여 새로운 할일(todo) 항목을 추가하고 표시할 수 있습니다. 그 과정에서 Ember 이벤트를 사용하는 방법을 살펴보겠습니다. 상호작용하는 기능을 제어하기 위해 JavaScript 코드를 포함하는 컴포넌트 클래스를 만들고, 앱의 데이터 상태(state)를 추적하는 서비스를 설정합니다.

4. Ember 상호작용: 푸터 기능, 조건부 렌더링 (en-US)

이제 앱에서 푸터 기능을 시작할 차례입니다. 아직 완료하지 않은 할일(todo) 항목의 수를 카운트하고, 완료된 할일(체크표시한 체크박스)에 스타일을 올바르게 적용합니다. 또한 "Clear completed" 버튼을 연결합니다. 이 과정에서 템플릿에서 조건부 렌더링을 사용하는 방법을 배웁니다.

5. Ember 라우팅 (en-US)

이 글에서는 라우팅 또는 URL기반 필터링에 대해 설명합니다. 이를 사용하여 todo의 세 가지 뷰("All", "Active", "Completed")를 고유한 URL로 제공합니다.

6. Ember 리소스와 문제해결 (en-US)

마지막 Ember 글은 학습을 진행하는데 사용할 수 있는 리소스 목록, 유용한 문제 해결(troubleshooting)에 관한 내용, 그외 정보를 제공합니다.

Vue tutorials
-------------

**참고:** **참조**: Vue 2.6.11을 사용하여 2020년 5월에 마지막으로 테스트한 튜토리얼입니다.

우리의 버전에 대해 여러분의 코드를 확인해야 하는 경우, 우리의 todo-vue repository에서 샘플 Vue app 코드의 완성된 버전을 볼 수 있습니다. 라이브 버전을 실행하려면 https://mdn.github.io/todo-vue/dist/ 를 참조하세요.

1. Vue 시작하기

이제 세 번째 프레임워크 뷰를 소개합니다. 이 글에서는 Vue의 배경을 약간 살펴보고, 설치 방법과 새 프로젝트를 만드는 방법에 대해 알아봅니다. 전체 프로젝트의 HLS(high-level structure) 와 각각의 컴포넌트를 공부합니다. 또한, 프로젝트를 로컬에서 실행하는 방법과 예제 작성을 시작할 준비를 합니다.

2. 첫 번째 Vue 컴포넌트 만들기 (en-US)

이제 Vue에 대해 자세히 살펴보고 우리만의 커스텀 컴포넌트를 만들겠습니다. Todo list의 각 항목을 나타내는 컴포넌트를 만드는 것으로 시작합니다. 그 과정에서, 다른 컴포넌트 내에서 컴포넌트를 호출하고, props(properties)를 통해 데이터를 전달하고 데이터 상태를 저장하는 등 중요한 개념을 학습합니다.

3. Vue 컴포넌트 리스트 렌더링 (en-US)

이 시점에서 우리는 충분히 잘 작동하는 컴포넌트를 얻었습니다. 이제 앱에 여러 `ToDoItem` 컴포넌트를 추가할 준비가 되었습니다. 이 글에서는 `App.vue` 컴포넌트에 todo 항목 데이터 셋을 추가하는 방법을 살펴보고, `v-for` 지시문(directive)을 사용하여 `ToDoItem` 컴포넌트를 반복하여 출력합니다.

4. 새로운 todo form추가: Vue events, methods, models (en-US)

이제 샘플 데이터와 각 비트의 데이터를 가져와서 앱의 `ToDoItem` 내에 렌더링하는 루프가 준비되었습니다. 다음으로 필요한 것은 사용자가 todo 항목을 앱에 입력할 수 있게 하는 기능입니다. 이를 위해 text `<input>`, 데이터가 제출될 때 발생하는 이벤트, 데이터 제출 시 데이터를 추가하고 목록을 다시 렌더링하기 위한 방법, 데이터를 제어하는 모델이 필요합니다. 이것이 이 글에서 다룰 내용입니다.

5. Styling Vue components with CSS (en-US)

마침내 우리 앱이 좀 더 멋지게 보입니다. 이 글에서는 CSS를 사용하여 Vue 컴포넌트를 스타일링하는 다양한 방법을 살펴봅니다.

6. Vue computed properties 사용하기 (en-US)

이 글에서는 computed properties 라고 불리는 Vue 기능을 사용하여 완료된 todo 항목 수를 표시하는 카운터를 추가합니다. 이들은 메서드와 유사하게 작동하지만 종속성 중 하나가 변경될 때만 다시 실행됩니다.

7. Vue 조건부 렌더링: 이미 존재하는 todo 항목 편집 (en-US)

이제 우리가 아직 놓치고 있는 주요 기능 중 한 기능의 주요한 부분 중 하나를 추가 할 차례입니다. 이를 위해 Vue의 조건부 렌더링 기능 `v-if` 와 `v-else` 를 활용하여, 이미 존재하는 todo 항목 view 와 todo 항목 레이블을 업데이트 할 수 있는 편집 view 간에 전환할 수 있습니다. 또한 todo 항목들을 삭제하는 기능을 추가하는 방법도 살펴봅니다.

8. Vue refs를 통한 포커스 관리 (en-US)

이제 거의 다 만들었습니다. 마지막으로 살펴볼 기능은 포커스 관리와 앱의 키보드 접근성을 향상시키는 방법입니다. Vue refs를 사용하여 이를 처리하는 방법을 살펴보겠습니다. ref는 가상 DOM 아래의 기본 DOM 노드에 직접 접근하거나 한 컴포넌트에서 하위 컴포넌트의 내부 DOM 구조로 직접 접근할 수 있는 고급 기능입니다.

9. Vue resources (en-US)

이제 Vue에 대한 학습을 마무리 할 것입니다. 학습을 진행하는데 사용할 수 있는 리소스 목록과 유용한 팁이 있습니다.

Svelte tutorials
----------------

**참고:** **참조**: Svelte 3.24.1 을 사용하여 2020년 8월에 마지막으로 테스트된 튜토리얼입니다.

코드를 우리 버전과 비교하여 확인해야 하는 경우, mdn-svelte-tutorial repo에서 각각의 문서 뒤에 있어야 하는 샘플 Svelte 앱 코드의 완성된 버전을 볼 수 있습니다. 라이브 버전을 실행하려면 Svelte REPL 을 참조하세요 https://svelte.dev/repl/378dd79e0dfe4486a8f10823f3813190?version=3.23.2.

1. Svelte 시작하기

이 문서에서는 Svelte framework에 대한 간략한 소개를 제공합니다. Svelte의 작동 방식과 지금까지 살펴본 나머지 프레임워크 및 도구와 차별화되는 점을 살펴보겠습니다. 그 다음 개발 환경 구성하기, 샘플 앱 만들기, 프로젝트 구조 이해하기, 로컬에서 실행하기와 프로덕션 용으로 빌드하는 방법을 알아보겠습니다.

2. Svelte Todo list app 시작하기

이제 Svelte가 작동하는 방식에 대한 기본적인 이해를 마쳤으므로, 예제 앱(todo list) 빌드를 시작하겠습니다. 이 문서에서는 먼저 앱에서 구현해야할 기능을 살펴보고, 그 다음 `Todos.svelte` 컴포넌트를 만들어 정적 마크업과 스타일을 배치합니다. 그리고 To-Do list 앱 기능 개발을 시작할 준비를 합니다. 이 내용은 다음 문서에서 계속 설명하겠습니다.

3. Svelte의 동적 동작: 변수와 props 작업

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

4. Svelte app 컴포넌트화 (en-US)

이 문서의 핵심 목표는 앱을 관리 가능한 컴포넌트로 나누고, 이들간에 정보를 공유하는 방법을 살펴 보는 것입니다. 앱을 컴포넌트화 하고 기능을 추가하여 기존 컴포넌트를 업데이트 할 수 있도록 합니다.

5. 향상된 Svelte: 반응성, 생명주기, 접근성 (en-US)

이 문서에서는 앱의 최종 기능을 추가하고 앱을 더욱 컴포넌트화 합니다. 객체와 배열의 업데이트와 관련된 반응성(reactivity) 이슈를 처리하는 방법을 배웁니다. 일반적인 함정을 피하기 위해 우리는 Svelte의 반응성 시스템을 좀 더 깊이 공부해야 합니다. 또한 접근성(accessibility ) 관련 이슈를 비롯해 좀 더 많은 내용을 해결하는 방법도 살펴보겠습니다.

6. Svelte stores 사용하기 (en-US)

이 문서에서는 Svelte에서 상태 관리를 처리하는 또 다른 방법을 보여줍니다. 스토어(Stores). 스토어는 값을 갖고있는 글로벌 데이터 저장소입니다. 컴포넌트는 스토어를 구독하고 값이 변경되면 알림을 받을 수 있습니다.

7. Svelte의 타입스크립트 지원 (en-US)

이제 Svelte 애플리케이션에서 타입스크립트를 사용하는 방법을 배웁니다. 먼저 타입스크립트가 무엇이며 어떤 이점이 있는지 알아봅니다. 그 다음 타입스크립트 파일과 함께 작동하도록 프로젝트를 구성하는 방법을 살펴봅니다. 마지막으로 앱을 검토하여 타입스크립트 기능을 최대한 활용하기 위해 어떤 수정을 해야 하는지 살펴보겠습니다.

8. 배포(Deployment)와 다음단계 (en-US)

이 마지막 문서에서는 애플리케이션을 배포하고 온라인으로 가져오는 방법을 살펴봅니다. 또한 Svelte 학습 여정을 계속하기 위해 알아야 할 몇 가지 리소스를 공유합니다.

Which frameworks did we choose?
-------------------------------

우리는 React/ReactDOM, Ember, Vue의 세 가지 주요 프레임워크에 초점을 맞춘 가이드와 시작하는 글 모음을 함께 게시하고 있습니다. 여기에는 여러가지 이유가 있습니다.

* 이것들은 잠시동안 인기가 있습니다. 다른 소프트웨어 툴과 마찬가지로 다음주에도 중단되지 않고 적극적으로 개발 되는 도구를 선택하고 유지하는것이 좋습니다. 또한 이것은 구인할 때에도 바람직하게 작용할 겁니다.
* 이들은 강력한 커뮤니티와 좋은 문서를 갖고 있습니다. 복잡한 주제를 배우는 데 도움을 얻을 수 있다는 것이 특히 중요하고, 처음시작하는 경우라면 더욱 그렇습니다.
* 우리는 모든 최신 프레임 워크를 다룰 수 있는 리소스가 없습니다. 새로운 목록이 항상 표시되므로 이 목록을 최신 상태로 유지하기가 매우 어려울 수 있습니다.
* 초심자로서 매우 많은 선택지 중 집중할 것을 선택하는 것은 매우 현실적인 문제입니다. 따라서 목록을 짧게 유지하는것이 좋습니다.

우리는 이것을 미리 말하고 싶습니다. 위 프레임워크들이 최고라고 생각하거나 어떤 식으로든 보증되기 때문에 선택하지 않았습니다. 우리는 단지 이 프레임워크들이 위의 기준에서 높은 점수를 얻었다고 생각합니다.

이 글을 처음 게시할 때 더 많은 프레임워크가 포함되기를 원했지만, 더 오래 지연시키지 않고 내용을 릴리즈 한 다음에 나중에 프레임워크 가이드를 더 추가하기로 결정했습니다. 이 컨텐츠에 좋아하는 프레임워크가 표시되지 않았고, 이를 변경하고 싶다면 언제든지 저희와 논의하시기 바랍니다. Matrix나 Discourse를 통해 연락하거나 mdn-admins list로 메일을 보내셔도 됩니다.


React resources - Web 개발 학습하기

React resources
===============

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

마지막 글에서는 학습을 더 진행하는 데 사용할 수 있는 React 리소스 목록을 제공합니다.

|  |  |
| --- | --- |
| 필요한 사전 지식: |
HTML,
 CSS, 및
 JavaScript 언어 핵심에 대한 친숙도,
 터미널/커멘드 라인 (en-US)에 대한 지식.
  |
| 목표: | React에 대해 자세히 알아보기 위한 추가 리소스를 제공하기 위함입니다. |

컴포넌트 수준 스타일
-----------

이 자습서에서는 이 접근 방식을 사용하지 않지만, 많은 React 애플리케이션은 하나의 모놀리식 스타일시트가 아니라, 컴포넌트별로 스타일을 정의합니다.

`create-react-app`을 사용하면 CSS 파일을 JavaScript 모듈로 가져올 수 있으므로, 해당 컴포넌트가 렌더링될 때만 CSS가 사용자에게 전송됩니다. 이 애플리케이션의 경우, 예를 들어 전용 `Form.css` 파일을 작성하여 각 컴포넌트의 스타일을 저장한 다음, 다음과 같이 스타일을 각각의 해당 모듈로 가져올 수 있습니다.

```jsx
import Form from "./Form";
import "./Form.css";
```

이 접근 방식을 사용하면 특정 컴포넌트에 속하는 CSS를 쉽게 식별하고 관리할 수 있습니다. 그러나 코드베이스 전체에서 스타일시트를 산산조각 내기도 하며, 스타일시트 분할이 그닥 가치가 없을 수도 있습니다. 수백 개의 고유한 시각적인 요소와 많은 이동 요소가 있는 대규모 애플리케이션의 경우, 사용자에게 전송되는 관련없는 코드의 양을 제한하는 것이 좋습니다. 애플리케이션 전체 스타일이 있고 그 위에 특정 컴포넌트 스타일을 빌드하는 편이 좋을 수 있습니다.

create-react-app 문서에서 컴포넌트 스타일시트에 대해 자세히 알아보세요.

React DevTools
--------------

이 자습서에서는 `console.log()`를 사용하여 애플리케이션의 상태와 속성을 확인했으며 React가 CLI와 브라우저의 JavaScript 콘솔에서 모두 제공하는 유용한 경고 및 오류 메시지도 볼 수 있지만, 여기서 할 수 있는 일이 더 있습니다.

React DevTools 유틸리티를 사용하면 브라우저에서 직접 React 애플리케이션의 내부를 검사할 수 있습니다. 브라우저의 개발자 도구에 새 패널을 추가하고 이를 통해 다양한 컴포넌트의 상태와 속성을 검사하고, 상태와 속성을 편집하여 애플리케이션을 즉시 변경할 수 있습니다.

이 스크린샷은 React DevTools에 표시되는 완성된 애플리케이션을 보여줍니다.

![React devtools에 표시되는 프로젝트](/ko/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_resources/react-devtools.png)

왼쪽에는, 배열에서 렌더링되는 항목에 대한 일부 고유 키를 포함하여, 응용 프로그램을 구성하는 모든 컴포넌트가 표시됩니다. 오른쪽에는 애플리케이션 컴포넌트가 활용하는 props와 훅이 표시됩니다. 또한, `Form`, `FilterButton` 및 `Todo` 컴포넌트가 오른쪽으로 들여쓰기되어 있다는 점에 유의하세요. 이는 `App`이 해당 컴포넌트의 부모임을 나타냅니다. 더 복잡한 어플리케이션에서, 이 화면은 부모/자식 관계를 한눈에 이해하는 데 유용합니다.

React DevTools는 다양한 형태로 제공됩니다.

* A Chrome 브라우저 익스텐션.
* A Firefox 브라우저 익스텐션.
* A Microsoft Edge 브라우저 익스텐션.
* A npm 또는 Yarn으로 설치할 수 있는 독립 실행형 애플리케이션.

이 중에 하나를 설치한 다음, 이를 사용하여 방금 빌드한 애플리케이션을 검사하세요!

React 블로그에서 React DevTools에 대해 자세히 읽어보세요.

Context API
-----------

이 자습서에서 빌드한 애플리케이션은 컴포넌트 props를 활용하여 `App` 컴포넌트에서 필요한 하위 컴포넌트로 데이터를 전달했습니다. 대부분의 경우, props는 데이터 공유에 적합한 방법입니다. 그러나 복잡하고 깊이 중첩된 응용 프로그램의 경우 항상 최선은 아닙니다.

React는 Context API를 제공하여 컴포넌트 트리 아래로 props를 전달하지 않고 필요한 컴포넌트에 데이터를 제공합니다. 이를 용이하게 하는 useContext 훅도 있습니다.

이 API를 직접 사용해 보고 싶다면, Smashing Magazine에서 React 컨텍스트에 대한 소개 기사를 작성한 기사를 읽어보세요.

클래스 컴포넌트
--------

이 튜토리얼에서는 언급하지 않았지만, 클래스를 사용하여 React Component를 구축할 수 있습니다. 이를 클래스 컴포넌트라고 합니다. 훅이 등장하기 전까지 클래스는 상태를 컴포넌트로 가져오거나 렌더링 부수효과를 관리하는 유일한 방법이었습니다. 그들은 여전히 특정 다른 엣지 케이스 기능을 처리하는 유일한 방법이며, 레거시 React 프로젝트에서 매우 일반적입니다. 공식 React 문서는 이에 대해 배우기에 좋은 곳입니다.

* React 문서의 상태 및 수명 주기
* React 문서의 React 소개
* MDN에서 JavaScript 클래스에 대해 읽어보기
테스팅
---

`create-react-app`은 애플리케이션을 즉시 테스트할 수 있는 몇 가지 도구를 제공합니다. 자습서의 앞부분에서 관련 파일을 삭제했을 수 있습니다. `create-react-app` 문서 테스트를 위한 몇 가지 기본 사항을 다룹니다.

라우팅
---

라우팅은 일반적으로 사용자 컴퓨터의 애플리케이션이 아닌 서버에서 처리되지만, 브라우저의 위치를 읽고 업데이트하고, 특정 사용자 인터페이스를 렌더링하도록 웹 애플리케이션을 구성할 수 있습니다. 이를 클라이언트 측 라우팅이라고 합니다. 애플리케이션에 대한 고유 경로(`/home`, `/dashboard` 또는 `/login`)를 많이 만들 수 있습니다.

React 커뮤니티는 클라이언트 측 라우팅을 위한 두 가지 주요 라이브러리인 React Router 및 Reach Router를 만들었습니다.

* React Router는 복잡한 라우팅 요구 사항이 있는 애플리케이션에 적합하며, Reach Router보다 일부 엣지 케이스를 더 잘 충족합니다. 그러나 React Router는 더 큰 라이브러리입니다.
* Reach Router는 단순한 애플리케이션에 적합하며, 사용자가 페이지를 탐색할 때 포커스를 자동으로 관리합니다.

포커스 관리는 클라이언트 측 라우팅에서 필수적입니다. 포커스 관리가 없으면 키보드 사용자는 포커스 림보에 갇힐 수 있으며, 스크린 리더 사용자는 자신이 새 페이지로 이동했는지 모를 수 있습니다. Reach Router는 접근성이 더 좋기 때문에, 시작하기에 좋은 곳입니다.

그러나 한 가지 주의 사항이 있습니다. 이러한 프로젝트는 가까운 미래에 병합될 것입니다. 이 병합이 발생하면, React Router는 살아남은 프로젝트가 됩니다(Reach의 포커스 관리 기능 추가).

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


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

React 시작하기
==========

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

이 문서에서는 React를 소개합니다. 우리는 React에 대한 약간의 배경지식과 사용 예시, 로컬 컴퓨터에 React 툴체인을 설정하는 방법을 배우고 프로세스에서의 React 동작 원리를 배우기 위해서 간단한 입문 앱을 만들어 봅니다.

|  |  |
| --- | --- |
| 필요한 사전 지식: | 코어 HTML, CSS, 및 JavaScript 언어에 익숙할 것. 터미널/커맨드라인 (en-US)에 대한 지식.
React는 JSX(JavaScript와 XML)라는 HTML-in-JavaScript 문법을 사용합니다. HTML와 JavaScript에 익숙하다면 JSX를 배우는 것에 도움이 됩니다. 또한 애플리케이션에서 발생하는 버그가 JavaScript와 관련이 있는지 아니면 React의 구체적인 도메인과 관련이 있는지 수월하게 식별하는 것에 도움이 됩니다. |
| 목표: | React 로컬 개발 환경을 구성하고 입문 앱을 만들어서 기본적인 동작 원리를 이해한다. |

Hello React
-----------

공식적인 태그라인에 따르면, React는 사용자 인터페이스를 만들기 위한 라이브러리입니다. React는 프레임워크가 아니고, 심지어 웹에만 사용할 수 있는 것도 아닙니다. React는 다른 라이브러리들과 함께 특정한 환경을 렌더링하는 데 사용됩니다. 가령, React Native는 모바일 애플리케이션을 만드는 데 사용될 수 있고, React 360은 가상 현실 애플리케이션을 만드는 데 사용할 수 있습니다. 그 밖에도 많은 가능성이 존재합니다.

웹을 만들기 위해서 사용자는 React를 ReactDOM과 함께 사용해야 합니다. React와 ReactDOM은 다른 실제 웹 프레임워크들과 동일한 공간에서 논의되고 동일한 문제를 해결하기 위해 사용됩니다. React를 "프레임워크"로 지칭할 때는 통상적인 뜻으로 받아들이면 됩니다.

React의 주요한 목표는 개발자가 UI를 만들 때 일어나는 버그들을 최소화하는 것입니다. 이를 달성하기 위하여 React는 사용자 인터페이스의 일부를 나타내는 자족적이고, 논리적인 코드 조각인 컴포넌트들을 사용합니다. 이 컴포넌트들은 함께 전체 UI를 구성할 수 있고, React는 렌더링 작업의 대부분을 추상화하여 개발자가 UI 디자인에 집중할 수 있게 합니다.

Use cases
---------

이 모듈에서 다루고 있는 다른 프레임워크와 달리, React는 코드 컨벤션이나 파일 구조에 엄격한 규칙을 강제하지 않습니다. 이를 통해 팀은 그들에게 가장 적합한 컨벤션을 설정할 수 있고, 원하는 어떤 방식으로든 React를 쓸 수 있습니다. React는 하나의 버튼, 인터페이스의 일부분, 혹은 애플리케이션의 사용자 인터페이스 전체를 다룰 수 있습니다.

React는 인터페이스의 일부분을 위하여 사용할 수 있지만, jQuery와 같은 라이브러리 혹은 Vue와 같은 프레임워크처럼 애플리케이션 "안에 넣는" 것은 쉬운 일이 아닙니다. React는 애플리케이션 전체를 만들 때 사용하기가 보다 쉽습니다.

인터페이스를 JSX로 작성하는 것과 같이 React app의 많은 개발자-경험 혜택은 컴파일 과정이 필요합니다. Babel과 같은 컴파일러를 웹사이트에 추가하는 것은 코드를 느리게 만들기 때문에, 개발자들은 종종 빌드 과정에서 이러한 툴링을 설정합니다. React는 아주 무거운 툴링을 요구하지만 학습할 수 있습니다.

이 기사는 애플리케이션의 사용자 인터페이스 전체를 렌더링하기 위해 React를 사용하는 예시에 초점을 맞출 겁니다. 이 예시는 Facebook의 create-react-tool 툴이 제공하는 툴링을 사용합니다.

React는 JavaScript를 어떻게 사용할까?
----------------------------

React는 많은 패턴에 모던 JavaScript의 특징을 이용합니다. JavaScript와 가장 다른 부분은 JSX 구문의 사용입니다. JSX는 JavaScript의 구문을 확장하여 HTML과 유사한 코드를 함께 사용할 수 있도록 합니다. 다음은 JSX 구문의 예시입니다.

```js
const heading = <h1>Mozilla Developer Network</h1>;
```

이 heading 상수는 **JSX 표현식(JSX expression)** 으로 알려져 있습니다. React는 애플리케이션에 `<h1>` 태그를 렌더링할 때 이 표현식을 사용할 수 있습니다.

`<header>` 태그로 heading을 감싸고 싶다고 가정해보겠습니다. JSX는 HTML에서 하던 것처럼 요소를 다른 요소 안에 중첩할 수 있습니다.

```js
const header = (
  <header>
    <h1>Mozilla Developer Network</h1>
  </header>
);
```

**참고:** 위의 스니펫에 있는 괄호는 JSX에 유일하지도 않고, 여러분의 애플리케이션에 어떠한 영향을 주지도 않습니다. 이들은 여러 줄의 코드가 동일한 표현식의 부분이라는 것을 당신(과 컴퓨터)에게 알려줍니다. header 표현식은 다음과 같이도 쓸 수도 있습니다.

```js
const header = (
  <header>
    <h1>Mozilla Developer Network</h1>
  </header>
);
```

하지만, 표현식을 시작하는 `<header>` 태그가 클로징 태그와 상응하는 위치에 들여쓰기가 되어있지 않기 때문에 이 코드는 살짝 이상해 보입니다.

브라우저는 도움 없이는 JSX를 읽을 수 없습니다. (Babel이나 Parcel과 같은 툴을 사용하여 컴파일할 때 header 표현식은 다음과 같이 보일 것입니다.

```js
const header = React.createElement(
  "header",
  null,
  React.createElement("h1", null, "Mozilla Developer Network"),
);
```

`React.createElement()` 를 사용하여 UI를 직접 작성해서 컴파일 단계를 생략할 수도 있습니다. 하지만, 이렇게 하면 JSX의 선언적인 장점을 잃을 것이고 코드는 읽기 더욱 어려워질 것입니다. 컴파일은 개발 과정에 있어 추가 과정이지만 React 커뮤니티의 많은 개발자는 JSX의 가독성이 그럴만한 가치가 있다고 생각합니다. 그리고 널리 사용되는 툴링은 JSX를 JavaScript로 컴파일하는 부분을 설정 과정의 일부로 만듭니다. 원하지 않는다면, 컴파일을 직접 구성하지 않아도 됩니다.

JSX는 HTML과 JavaScript를 섞은 것이기 때문에 어떤 개발자들은 JSX를 직관적이라고 생각합니다. 다른 이들은 JSX의 혼합된 본성 때문에 혼란스럽다고 하기도 합니다. 하지만 한번 익숙해지면 사용자 인터페이스를 더 빠르고 직관적으로 만들 수 있고 다른 이들이 여러분의 코드베이스를 한눈에 더 잘 이해할 수 있게 합니다.

JSX에 대해 더 알고 싶다면, React 팀의 JSX in Depth 기사를 참고하세요.

첫번째 React 앱 준비하기
----------------

React를 사용하는 데에는 많은 방법이 있지만, 여기서는 커맨드-라인 인터페이스(command-line interface; CLI) 툴인 create-react-app을 사용할 겁니다. 이전에 말했듯이, 이 툴은 패키지를 설치하고 파일들을 생성하여 앞서 설명한 툴링들로 React 애플리케이션을 개발하는 과정을 신속히 처리합니다.

create-react-app 없이 웹 사이트에 React를 추가하는 것은 HTML 파일에서 `<script>` 요소들을 복사해오는 것으로 가능하지만, create-react-app CLI는 React 애플리케이션을 만드는 가장 흔한 시작점입니다. 애플리케이션을 빌드하는 데에는 더 많은 시간이 걸리나 설정에 걸리는 시간은 줄일 수 있습니다.

### 요구되는 것들

create-react-app을 사용하기 위해선 Node.js를 설치해야 합니다. LTS(오랜 기간 지원된; long-term support) 버전을 사용하는 것을 추천합니다. Node는 npm (the node package manager)와 npx (the node package runner)를 포함합니다.

Node 대신에 Yarn package manager를 사용할 수도 있습니다. 하지만 이 튜토리얼의 설정에서는 npm을 사용하는 것을 권고합니다. npm과 yarn에 대해 더 자세한 정보는 Package management basics (en-US)를 참고하세요.

Windows를 이용하고 있다면 이 튜토리얼에서 언급되는 터미널 커맨드를 사용하기 위해, Unix/MacOS 터미널과 같은 기능을 제공하는 소프트웨어를 설치해야 합니다. **Gitbash** (git for Windows toolset의 일부) 혹은 **Windows Subsystem for Linux** (**WSL**) 모두 적합합니다. 이 소프트웨어들이나 일반적인 터미널 커맨드에 대해서 더 자세히 알고 싶다면 Command line carsh course (en-US)를 참고하세요.

그리고 React와 ReactDOM이 꽤 최신 브라우저들에서만 작동한다는 것을 명심하세요. IE9+는 몇몇 폴리필을 통하여 가능합니다. 튜토리얼에서는 Firefox, Microsoft Edge, Safari, 혹은 Chrome과 같은 최신 브라우저를 사용하기를 권장합니다.

아래에서 더 자세한 정보를 찾아보세요.

* "What is npm" on nodejs.org
* "Introducing npx" on the npm blog
* The create-react-app documentation
### 앱 초기화

create-react-app은 애플리케이션의 이름을 인수로 가집니다. create-react-app은 이 이름을 새로운 디렉토리를 만들 때 사용한 후 그 안에 중요한 파일들을 생성합니다. `cd`로 하드 드라이브에 애플리케이션을 저장하고 싶은 장소까지 이동하고 다음 명령어를 터미널에 실행하세요.

```bash
npx create-react-app moz-todo-react
```

이 명령어는 `moz-todo-react` 디렉토리를 만들고 다음과 같은 일들을 합니다.

* 앱의 기능에 필수적인 npm 패키지들을 설치합니다.
* 애플리케이션을 시작하고 서비스하기 위한 스크립트를 작성합니다.
* 기본적인 앱 아키텍처를 정의하는 파일과 디렉토리의 구조를 만듭니다.
* 컴퓨터에 깃이 설치되어있다면, 디렉토리를 깃 레포지토리로 초기화합니다.

**참고:** yarn package manager를 설치했다면, create-react-app은 그것을 npm 대신 기본으로 사용할 것입니다. 두 패키지 매니저가 모두 설치되어있고 NPM을 명시적으로 사용하길 원한다면, create-react-app을 실행할 때 플래그 `--use-npm`을 사용하면 됩니다.

```bash
npx create-react-app moz-todo-react --use-npm
```

create-react-app은 실행되는 동안 터미널에 메시지들을 출력할 것입니다. 이건 매우 정상적인 일입니다! 몇 분 정도 걸리니, 차 한 잔 만들기에 좋은 시간이에요.

이 과정이 완료되면 `moz-todo-react` 디렉토리 안으로 `cd`를 실행한 후 `npm start` 커맨드를 실행하세요. create-react-app에 의해 설치된 스크립트는 localhost:3000에서 실행되고 있는 로컬 서버에서 실행을 시작할 것입니다. 새로운 브라우저 탭을 열고 앱을 열면, 브라우저는 다음과 같이 무언가를 표시하고 있을 겁니다.

![Screenshot of Firefox MacOS, open to localhost:3000, showing the default create-react-app application](/ko/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_getting_started/default-create-react-app.png)

### 애플리케이션 구조

create-react-app은 React 애플리케이션을 개발하기 위해 필요한 모든 것을 제공합니다. 초기 파일 구조는 다음과 같습니다:
```

moz-todo-react
├── README.md
├── node_modules
├── package.json
├── package-lock.json
├── .gitignore
├── public
│   ├── favicon.ico
│   ├── index.html
│   └── manifest.json
└── src
    ├── App.css
    ├── App.js
    ├── App.test.js
    ├── index.css
    ├── index.js
    ├── logo.svg
    └── serviceWorker.js
```

**`src`** 디렉토리는 애플리케이션의 소스 코드가 있는 곳으로서, 가장 많은 시간을 보낼 곳입니다.

**`public`** 디렉토리는 앱을 개발하는 동안 브라우저가 읽을 파일들을 가지고 있습니다; 이들 중 가장 중요한 것은 `index.html` 입니다. React는 코드를 이 html 파일에 주입하여 브라우저가 코드를 실행할 수 있게 합니다. create-react-app function에 도움이 되는 다른 마크업도 있기 때문에, 무엇을 하는지 모른다면 함부로 편집하지 않도록 주의하세요. 애플리케이션의 타이틀에 반영되도록 `index.html` 파일의 `<title>` 요소 안의 텍스트를 바꿔야 합니다. 정확한 페이지 타이틀은 접근성을 위해 중요하니까요!

`public` 디렉토리 또한 앱의 프로덕션 버전을 빌드하고 배포할 때 퍼블리시 됩니다. 이 튜토리얼에서는 배포에 대해 다루지 않지만, Deploying our app (en-US) 튜토리얼에 기술된 것과 비슷한 해결책을 사용할 수 있을 겁니다.

`package.json` 파일은 Node.js/npm가 프로젝트를 조직하기 위해 사용하는, 프로젝트에 관련한 정보를 포함하고 있습니다. 이 파일은 React 애플리케이션에 유일하지 않은데요; create-react-app이 이 파일을 수정합니다. 튜토리얼을 완성하기 위해 이 파일을 이해할 필요는 전혀 없지만, 더 자세히 알고 싶다면 What is the file `package.json`? on NodeJS.org 을 참고해주세요. Package management basics (en-US) 에서도 다루고 있답니다.

첫번째 React 컴포넌트 `</App>` 탐색하기
----------------------------

React에서, **컴포넌트**(**component**)는 앱의 일부를 렌더링하는 재사용가능한 모듈입니다. 이 부분들은 클 수도 작을 수도 있지만, 대개 정확하게 정의됩니다; 이들은 단일하고 명확한 목적만을 위해 일합니다.

`src/App.js`를 열어봅시다. 이 파일은 첫번째 컴포넌트 `App`과 몇 줄의 코드를 포함하고 있습니다.

```js
import React from "react";
import logo from "./logo.svg";
import "./App.css";

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="\_blank"
          rel="noopener noreferrer">
          Learn React
        </a>
      </header>
    </div>
  );
}
export default App;
```

`App.js` 파일은 세 개의 부분으로 나눌 수 있습니다: 상단의 `import` 문들, 중앙의 `App` 컴포넌트, 그리고 하단의 `export` 문들. 많은 React 컴포넌트들이 이 패턴을 따르고 있습니다.

### Import 문

상단의 `import` 문은 `App.js`가 다른 곳에 정의된 코드들을 사용할 수 있게 해줍니다. 이 명령문들을 자세히 들여다봅시다.

```js
import React from "react";
import logo from "./logo.svg";
import "./App.css";
```

첫 번째 명령문은 React 라이브러리를 불러옵니다. React가 우리가 작성한 JSX를 `React.createElement()`로 변환하기 때문에, 모든 React 컴포넌트들은 반드시 `React` 모듈을 불러와야 합니다. 이 단계를 건너뛰면 애플리케이션은 오류를 발생시킬 겁니다.

두 번째 명령문은 `./logo.svg`로부터 로고를 불러옵니다. `./`를 경로의 시작에, `.svg` 익스텐션을 마지막에 사용한다는 것은 해당 파일이 로컬에 위치하며 JavaScript 파일이 아니라는 것을 뜻합니다. 실제로, `logo.svg` 파일은 소스 디렉토리 안에 있습니다.

`React` 모듈을 불러올 때는 경로나 익스텐션을 쓰지 않습니다. 이것들은 로컬 파일이 아니라, `package.json`의 의존성 목록에 포함되어 있습니다. 로컬과 이들의 구분을 꼭 주의하세요!

세 번째 명령문은 `App` 컴포넌트에 관련된 CSS를 불러옵니다. 어떤 변수 이름이나 `from` 지시가 없다는 것을 주목해주세요. 이 특별한 import 구문은 JavaScript 모듈 구문이 아니라 웹팩(Webpack)으로부터 온 것입니다. 웹팩은 create-react-app이 우리의 모든 JavaScript 파일을 함께 번들하고 브라우저에 제공하기 위해 사용하는 도구입니다.

### `App` 컴포넌트

`import`문들 이후, `App`이라고 이름 지어진 함수가 있습니다. 많은 JavaScript 커뮤니티가 `helloWorld`와 같은 카멜-케이스(camel-case) 이름을 선호하는 반면에, React 컴포넌트는 파스칼-케이스(pascal-case) 변수 이름을 사용합니다. 이는 JSX 요소가 React 컴포넌트이지, 정규 HTML 태그가 아니라는 것을 분명히 하기 위해서입니다. `App` 함수를 `app`으로 이름 짓는다면 브라우저는 오류를 발생시킬 겁니다.

이제 `App`을 자세히 들여다보겠습니다.

```js
function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="\_blank"
          rel="noopener noreferrer">
          Learn React
        </a>
      </header>
    </div>
  );
}
```

`App` 함수는 JSX 표현식을 반환합니다. 이 표현식은 브라우저가 궁극적으로 DOM에 렌더링할 것을 정의하고 있습니다.

표현식의 어떤 요소들은 HTML에 쓰인 것과 같이 속성들을 가지고 `attribute="value"`와 같은 패턴을 따르고 있습니다. 세 번째 줄에서, 여는 `div` (en-US) 태그는 `className` 속성을 가지고 있습니다. 이건 HTML의 `class` 속성과 같지만, JSX는 JavaScript이므로 `class` 라는 단어를 쓸 수 없습니다. 거꾸로 말하자면, JavaScript는 이미 이 단어를 특별한 목적을 위해 사용하고 있어 코드 안에서 사용한다면 문제를 발생시킬 것입니다. 같은 이유로 몇 개의 다른 HTML 속성들이 HTML에서 그랬던 것과는 다르게 JSX에서 쓰이고 있습니다.

여섯 번째 줄의 `<p>` 태그 안의 텍스트를 "Hello, world!"로 바꾸고, 파일을 저장하세요. 브라우저에서 `http://localhost:3000`에 실행되고 있는 개발 서버에 즉시 변화가 렌더링 되는 것을 볼 수 있을 겁니다. 이제 `<a>` 태그를 지우고 저장해보세요. "Learn React" 링크가 사라질 겁니다.

이제 `App` 컴포넌트는 아래와 같을 겁니다.

```js
function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>Hello, World!</p>
      </header>
    </div>
  );
}
```

### Export 문

`App.js` 파일의 최하단에 있는 `export default App`라는 명령문은 `App` 컴포넌트들을 다른 모듈에서 사용할 수 있게 합니다.

Interrogating the index
-----------------------

`src/index.js`를 열어봅시다. `App` 컴포넌트가 사용된 곳이기 때문입니다. 이 파일은 앱의 진입점이고, 초기에는 이렇게 생겼습니다.

```js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import \* as serviceWorker from "./serviceWorker";

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById("root"),
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
```

`App.js`와 같이, 파일은 자신을 실행하기 위해 필요한 JS 모듈이나 다른 에셋들을 모두 임포트하며 시작합니다. `src/index.css`는 전체 앱에 적용되는 전역 스타일을 나타냅니다. `App` 컴포넌트가 임포트된 것도 볼 수 있는데요, 이것은 `App.js`의 하단에 있는 `export` 문 덕분에 가능한 것입니다.

일곱 번째 줄은 React의 `ReactDOM.render()` 함수를 호출하고 있습니다. 두 개의 인수는 다음과 같습니다:

* 렌더링하기 원하는 컴포넌트. 여기서는 `<App />`입니다.
* 렌더링 되길 원하는 컴포넌트들이 포함된 DOM 요소. 여기서는 `root`라는 ID를 가진 요소입니다. `public/index.html`을 본다면, 이것이 `<body>` 내부에 있는 `<div>` 요소라는 것을 알 수 있습니다.

이 모든 것이 React에게 우리가 루트로서, 혹은 첫 번째 컴포넌트로서 `App` 컴포넌트를 렌더링하길 원한다고 알려줍니다.

**참고:** JSX에서, React 컴포넌트와 HTML 요소는 반드시 닫는 태그가 있어야 합니다. `<App>` 혹은 `<img>`라고만 쓰는 것은 오류를 발생시킬 것입니다.

Service workers (en-US)는 앱 성능에 도움이 되고 오프라인에서 일할 수 있는 웹 애플리케이션의 기능을 제공하는 특별한 코드들이지만, 이 기사에서 다루는 범위는 아닙니다. 다섯 번째 줄과 코드 가장 아래에 있는 것은 지울 수 있습니다.

최종적인 `index.js`는 다음과 같을 것입니다:

```js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";

ReactDOM.render(<App />, document.getElementById("root"));
```

변수와 props
---------

다음으로, React에서 컴포넌트들을 편집하고 데이터와 작업하는 것을 조금 더 편하게 해주는 JavaScript 스킬들을 사용해보겠습니다. 변수가 어떻게 JSX 안에서 사용되는지에 대해, 그리고 props에 대해 소개합니다. props는 컴포넌트에 데이터를 전달하는 방법이며, 전달된 후에는 변수를 통하여 접근할 수 있습니다.

### JSX 안의 변수들

`App.js`으로 돌아가서, 아홉 번째 줄을 주목해봅시다:

```js
<img src={logo} className="App-logo" alt="logo" />
```

여기, `<img />` 태그의 `src` 속성값은 중괄호 안에 들어가 있습니다. 이것이 JSX가 변수를 인식하는 방법입니다. React는 `{logo}`를 보고 앱의 두번째 줄에 임포트된 로고를 가리키고 있음을 깨닫습니다. 그리고 로고 파일을 얻어와 그것을 렌더링합니다.

우리만의 변수를 만들어보도록 합시다. `App`의 반환문 이전에 `const subject = 'React';`를 추가합니다. `App` 컴포넌트는 이제 이런 모습을 가지게 됩니다:

```js
function App() {
  const subject = "React";
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>Hello, World!</p>
      </header>
    </div>
  );
}
```

여덞 번째 줄의 단어 "world" 대신 우리가 만든 `subject` 변수를 사용해보도록 하겠습니다. 다음과 같습니다.

```js
function App() {
  const subject = "React";
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>Hello, {subject}!</p>
      </header>
    </div>
  );
}
```

이제 저장하면 브라우저는 "Hello, world!" 대신에 "Hello, React!"를 표시할 것입니다.

변수는 편리하지만 방금 설정한 변수가 React의 기능들을 잘 활용한 것은 아닙니다. 이 변수는 나중에 props가 들어올 곳입니다.

### 컴포넌트 props

**prop**는 React 컴포넌트에 전달되는 모든 데이터를 뜻합니다. React props는 HTML 속성과 비교할 수 있습니다. HTML 요소는 속성을 가지고 있고 React 컴포넌트는 props를 가지고 있습니다. props는 컴포넌트 호출 안에 쓰이며 `prop="value"`와 같이 HTML 속성과 동일한 구문을 씁니다. React에서, 데이터의 흐름은 단방향(unidirectional)입니다: props는 오직 부모 컴포넌트에서 자식 컴포넌트로 내려갑니다. 그리고 props는 오로지 읽기 전용입니다.

`index.js`를 열고 `<App />` 호출에 첫 번째 prop를 전달해봅시다.

`<App />` 컴포넌트 호출에 `subject`의 prop를 `Clarice`라는 값으로 추가해봅시다. 이제 코드는 다음과 같을 것입니다:

```js
ReactDOM.render(<App subject="Clarice" />, document.getElementById("root"));
```

`App.js`로 돌아가서, App 함수를 다시 들여다봅시다. 간결함을 위해 `return` 문으로 요약하면 다음과 같이 읽어볼 수 있습니다:

```js
function App() {
  const subject = "React";
  return (
    // return statement
  );
}
```

`App` 함수의 시그네처(signature)를 바꾸어, `props`를 파라미터로 써 봅시다. 그리고 `subject` 상수를 지워 봅시다. 다른 함수 파라미터처럼, `props`를 브라우저 콘솔에 출력하도록 `console.log()`에 전달할 수 있습니다. `return` 문 이전에 다음과 같이 한 번 적어보세요.

```js
function App(props) {
  console.log(props);
  return (
    // return statement
  );
}
```

파일을 저장하고 브라우저의 JavaScript 콘솔을 검사해봅시다. 다음과 같이 출력된 것을 볼 수 있을 겁니다:

```js
Object { subject: "Clarice" }
```

객체 속성 `subject`는 `<App />` 컴포넌트 호출에 추가한 `subject` prop에 상응합니다. 그리고 문자열 `Clarice`는 prop의 값에 상응합니다. React에서 컴포넌트 props는 항상 이러한 방식으로 객체에 포함됩니다.

이제 `subject`가 props 중 하나이니, `App.js`에서 사용해봅시다. `subject` 상수를 바꾸어 문자열 `React`를 정의하는 대신에 `props.subject`의 값을 읽도록 합시다. 원한다면 `console.log()`를 지워도 좋습니다.

```js
function App(props) {
  const subject = props.subject;
  return (
    // return statement
  );
}
```

저장한다면 앱은 "Hello, Clarice!"로 당신을 환영해줄 것입니다. `index.js`로 돌아가 `subject`의 값을 수정하고, 저장한다면 텍스트 또한 바뀔 것입니다.

요약
--

이것으로 로컬에 React를 설치하는 방법과 입문 앱을 생성하는 방법, 그리고 기본적으로 어떻게 작동하는지를 포함하여 React에 대한 첫 탐색이 끝났습니다. 다음 기사에서는 첫 번째 애플리케이션으로 적절한 투두 리스트 만들기를 시작해 볼 것입니다. 하지만 그전에 우리가 배운 것들을 상기해봅시다.

React에서는:

* 컴포넌트는 필요한 모듈을 불러오고 그들 자신을 파일의 하단에서 내보낼 수 있습니다.
* 컴포넌트 함수는 `PascalCase`로 명명합니다.
* JSX 변수는 `{so}`와 같이 중괄호 사이에 넣어 읽을 수 있습니다.
* JSX 속성은 HTML 속성과 다르기 때문에 JavaScript의 예약어와 충돌하지 않습니다. 예를 들어 HTML의 `class`는 JSX에서는 `className`입니다. 여러 개의 단어로 된 속성을 `camelCase`로 명명한다는 것을 기억해주세요.
* props는 컴포넌트 호출에 속성처럼 쓰이고, 컴포넌트로 전달됩니다.

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


React 상호작용: 편집하기, 필터링, 조건부 렌더링 - Web 개발 학습하기

React 상호작용: 편집하기, 필터링, 조건부 렌더링
==============================

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

React 여정이 거의 끝나감에 따라(적어도 지금은), Todo 목록 애플리케이션의 주요 기능 영역에 마무리 작업을 추가할 것입니다. 여기에는 기존 할 일을 편집할 수 있도록 허용하고, 모든 할 일, 완료된 할 일 및 완료되지 않은 할 일 간의 할 일 목록을 필터링하는 것이 포함됩니다. 그 과정에서 조건부 UI 렌더링을 살펴보겠습니다.

|  |  |
| --- | --- |
| 필요한 사전 지식: |
HTML,
 CSS, 및
 JavaScript 언어 핵심에 대한 친숙도,
 터미널/커멘드 라인 (en-US)에 대한 지식.
  |
| 목표: | React의 조건부 렌더링에 대해 배우고 애플리케이션에서 목록 필터링 및 편집 UI를 구현합니다. |

할 일 이름 편집
---------

아직 할 일 이름을 편집하기 위한 사용자 인터페이스가 없습니다. 우리는 잠시 후에 그것을 얻을 것입니다. 우선 `App.js`에서 적어도 `editTask()` 함수를 구현할 수 있습니다. 대상 객체를 찾기 위해 `id`를 사용하기 때문에, `deleteTask()`와 비슷하지만 할 일을 업데이트할 이름이 포함된 `newName` 속성도 사용합니다. 배열에서 무언가를 삭제하는 대신, 약간의 변경사항이 있는 새 배열을 반환하고 싶기 때문에 `Array.prototype.filter()` 대신 `Array.prototype.map()`을 사용합니다.

`App` 컴포넌트 내부의 `editTask()` 함수를 다른 함수와 동일한 위치에 추가합니다.

```jsx
function editTask(id, newName) {
  const editedTaskList = tasks.map((task) => {
    // 이 할 일이 편집된 작업과 동일한 ID를 갖는 경우
    if (id === task.id) {
      //
      return { ...task, name: newName };
    }
    return task;
  });
  setTasks(editedTaskList);
}
```

`deleteTask`와 같은 방식으로 `editTask`를 `<Todo />` 컴포넌트에 속성으로 전달합니다.

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

이제 `Todo.js`를 엽니다. 우리는 약간의 리팩토링을 할 것입니다.

편집을 위한 UI
---------

사용자가 할 일을 편집할 수 있도록 하려면, 그렇게 할 수 있는 사용자 인터페이스를 제공해야 합니다. 먼저 `App` 컴포넌트에서 이전에 했던 것처럼 `useState`를 `Todo` 컴포넌트로 가져옵니다. 첫 번째 import 문을 다음과 같이 업데이트합니다.

```jsx
import React, { useState } from "react";
```

이제 이것을 사용하여 `isEditing` 상태를 설정하고, 기본 상태는 `false`여야 합니다. `Todo(props) {}` 컴포넌트 정의의 맨 위에 다음 행을 추가하십시오.

```jsx
const [isEditing, setEditing] = useState(false);
```

다음으로, `<Todo />` 컴포넌트를 다시 생각해 보겠습니다. 이제부터 지금까지 사용된 단일 템플릿이 아닌, 두 가지 가능한 "템플릿" 중 하나를 표시하려고 합니다.

* "보기" 템플릿, 단지 할 일을 볼 때, 이것이 지금까지 튜토리얼에서 사용한 것입니다.
* 할 일을 편집할 때, "편집" 템플릿입니다. 우리는 이것을 만들려고 합니다.

이 코드 블록을 `useState()` 훅 아래, `return`문 위에 있는 `Todo()` 함수에 복사합니다.

```jsx
const editingTemplate = (
  <form className="stack-small">
 <div className="form-group">
 <label className="todo-label" htmlFor={props.id}>
 {props.name}의 새로운 이름
 </label>
 <input id={props.id} className="todo-text" type="text" />
 </div>
 <div className="btn-group">
 <button type="button" className="btn todo-cancel">
 취소
 <span className="visually-hidden">{props.name} 이름 바꾸기</span>
 </button>
 <button type="submit" className="btn btn\_\_primary todo-edit">
 저장
 <span className="visually-hidden">{props.name}의 새로운 이름</span>
 </button>
 </div>
 </form>
);
const viewTemplate = (
  <div className="stack-small">
 <div className="c-cb">
 <input
 id={props.id}
 type="checkbox"
 defaultChecked={props.completed}
 onChange={() => props.toggleTaskCompleted(props.id)}
 />
 <label className="todo-label" htmlFor={props.id}>
 {props.name}
 </label>
 </div>
 <div className="btn-group">
 <button type="button" className="btn">
 편집 <span className="visually-hidden">{props.name}</span>
 </button>
 <button
 type="button"
 className="btn btn\_\_danger"
 onClick={() => props.deleteTask(props.id)}>
 삭제 <span className="visually-hidden">{props.name}</span>
 </button>
 </div>
 </div>
);
```

이제 두 개의 서로 다른 상수 내에 정의된, "편집" 및 "보기"라는 두 가지 템플릿 구조가 있습니다. 이는 `<Todo />`의 `return` 문이 이제 반복된다는 것을 의미합니다. 또한 "보기" 템플릿의 정의도 포함됩니다. 컴포넌트가 반환하는 템플릿을 결정하기 위해 **조건부 렌더링**을 사용하여 이를 정리할 수 있으므로, UI에서 렌더링됩니다.

조건부 렌더링
-------

JSX에서는, 조건을 사용하여 브라우저에서 렌더링되는 내용을 변경할 수 있습니다. JSX에서 조건을 작성하려면, 삼항 연산자를 사용할 수 있습니다.

`<Todo />` 컴포넌트의 경우, 우리의 조건은 "이 할 일이 편집되고 있는가?"입니다. `Todo()` 내부의 `return` 문을 다음과 같이 변경합니다.

```jsx
return <li className="todo">{isEditing ? editingTemplate : viewTemplate}</li>;
```

브라우저는 이전과 마찬가지로 모든 할 일들을 렌더링해야 합니다. 편집 템플릿을 보려면, 현재 코드에서 기본 `isEditing` 상태를 `false`에서 `true`로 변경해야 합니다. 다음 섹션에서 편집 버튼 토글을 만드는 방법을 살펴보겠습니다!

`<Todo />` 템플릿 토글
-----------------

마침내, 최종 핵심 기능을 대화형으로 만들 준비가 되었습니다. 우선, 사용자가 `viewTemplate`에서 "편집" 버튼을 누를 때 값이 `true`인 `setEditing()`을 호출하여 템플릿을 전환할 수 있도록 합니다.

다음과 같이 `viewTemplate`에서 "편집" 버튼을 업데이트합니다.

```jsx
<button type="button" className="btn" onClick={() => setEditing(true)}>
 편집 <span className="visually-hidden">{props.name}</span>
</button>
```

이제 동일한 `onClick` 핸들러를 `editingTemplate`의 "Cancel" 버튼에 추가하지만, 이번에는 `isEditing`을 `false`로 설정하여 "보기" 템플릿으로 다시 전환합니다.

다음과 같이 `editingTemplate`에서 "취소" 버튼을 업데이트합니다.

```jsx
<button
 type="button"
 className="btn todo-cancel"
 onClick={() => setEditing(false)}>
 취소
 <span className="visually-hidden">{props.name} 이름 바꾸기</span>
</button>
```

이 코드를 사용하면, 할 일 항목에서 "편집" 및 "취소" 버튼을 눌러 템플릿 간에 전환할 수 있어야 합니다.

![사용 가능한 편집 및 삭제 버튼이 있는 보기 템플릿을 보여주는 eat todo 항목](/ko/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_interactivity_filtering_conditional_rendering/view.png)

![새 이름을 입력할 수 있는 입력 필드와 사용 가능한 취소 및 저장 버튼이 있는 편집 템플릿을 보여주는 eat todo 항목](/ko/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_interactivity_filtering_conditional_rendering/edit.png)

다음 단계는 실제로 편집 기능을 작동시키는 것입니다.

UI에서 편집하기
---------

우리가 하려는 많은 작업은 `Form.js`에서 수행한 작업을 반영할 것입니다. 사용자가 새 입력 필드에 입력할 때 입력하는 텍스트를 추적해야 합니다. 양식을 제출하면 콜백 속성을 사용하여 할 일의 새 이름으로 상태를 업데이트해야 합니다.

새 이름을 저장하고 설정하기 위한 새 훅을 만드는 것으로 시작하겠습니다. 여전히 `Todo.js`에서 기존 훅 아래에 다음을 추가합니다.

```jsx
const [newName, setNewName] = useState("");
```

다음으로, 새 이름을 설정할 `handleChange()` 함수를 만듭니다. 이 함수를 훅과 템플릿의 사이에 넣으세요.

```jsx
function handleChange(e) {
  setNewName(e.target.value);
}
```

이제 `editingTemplate`의 `<input />` 필드를 업데이트하고 `value` 속성을 `newName`으로 설정하고, `handleChange()` 함수를 `onChange` 이벤트에 바인딩합니다.

```jsx
<input
 id={props.id}
 className="todo-text"
 type="text"
 value={newName}
 onChange={handleChange}
/>
```

마지막으로, 편집 양식의 `onSubmit` 이벤트를 처리하는 함수를 만들어야 합니다. 이전에 추가한 함수 바로 아래에 다음을 추가합니다.

```jsx
function handleSubmit(e) {
  e.preventDefault();
  props.editTask(props.id, newName);
  setNewName("");
  setEditing(false);
}
```

`editTask()` 콜백 속성에는 편집 중인 할 일의 ID와 새 이름이 필요하다는 점을 기억하세요.

다음 `onSubmit` 핸들러를 `editingTemplate`의 `<form>`에 추가하여 이 함수를 양식의 `submit` 이벤트에 바인딩합니다.

```jsx
<form className="stack-small" onSubmit={handleSubmit}>
```

이제 브라우저에서 할 일을 편집할 수 있습니다!

필터 버튼으로 돌아가기
------------

이제 주요 기능이 완성되었으니, 필터 버튼에 대해 생각해볼 수 있습니다. 현재, 필터 버튼은 "All" 레이블을 반복하며, 아무런 기능도 없습니다! 여기에는 `<Todo />` 컴포넌트에서 사용했던 몇 가지 기술을 다시 적용할 것입니다.

* 활성된 필터를 저장하기 위한 훅을 만듭니다.
* 사용자가 활성 필터를 모두, 완료됨, 완료되지 않음으로 변경할 수 있는 `<FilterButton />` 요소의 배열을 렌더링합니다.
### 필터 훅 추가하기

필터를 읽고 설정하는 `App()` 함수에 새 훅을 추가합니다. 모든 할 일이 처음에 표시되어야 하므로 기본 필터를 `All`로 지정합니다.

```jsx
const [filter, setFilter] = useState("All");
```

### 필터 정의

현재 우리의 목표는 두 가지입니다.

* 각 필터의 이름은 고유해야 합니다.
* 각 필터에는 고유한 행위가 있어야 합니다.

JavaScript 객체는 이름과 행위를 연결하는 좋은 방법입니다. 각 키는 필터의 이름입니다. 각 속성은 해당 이름과 관련된 행위입니다.

`App.js` 상단에, import 문들 아래에 있지만 `App()` 함수 위에, `FILTER_MAP` 이라는 객체를 추가해보겠습니다.

```jsx
const FILTER\_MAP = {
  All: () => true,
  Active: (task) => !task.completed,
  Completed: (task) => task.completed,
};
```

`FILTER_MAP`의 값은 `tasks` 데이터 배열을 필터링하는 데 사용할 함수입니다.

* `All` 필터는 모든 할 일을 표시하므로, 모든 할 일에 대해 `true`를 반환합니다.
* `Active` 필터는 `completed` 속성이 `false`인 할 일을 표시합니다.
* `Completed` 필터는 `completed` 속성이 `true`인 할 일을 표시합니다.

이전에 추가된 항목 아래에, 다음을 추가합니다. 여기서는 `Object.keys()`를 사용하여 `FILTER_NAMES` 배열을 수집합니다.

```jsx
const FILTER\_NAMES = Object.keys(FILTER\_MAP);
```

**참고:** 우리는 `App()` 함수 외부에서 이러한 상수들을 정의하고 있습니다. 내부에 정의된 경우, `<App />` 컴포넌트가 다시 렌더링될 때마다 다시 계산되기 때문인데, 우리는 그것을 원하지 않습니다. 이 정보는 애플리케이션이 무엇을 하든 절대 변경되지 않습니다.

### 필터 렌더링

이제 `FILTER_NAMES` 배열이 있으므로, 이를 사용하여 세 가지 필터를 모두 렌더링할 수 있습니다. `App()` 함수 내에서 `filterList`라는 상수를 만들 수 있습니다. 이 상수는 이름 배열을 매핑하고 `<FilterButton />` 컴포넌트를 반환하는 데 사용할 것입니다. 여기서도 키가 필요하다는 것을 기억하세요.

`taskList` 상수 선언 아래에 다음을 추가합니다.

```jsx
const filterList = FILTER\_NAMES.map((name) => (
  <FilterButton key={name} name={name} />
));
```

이제 `App.js`에서 반복되는 세 개의 `<FilterButton />`을 이 `filterList`로 바꾸어줍니다. 다음을 변경해주세요.

```jsx
<FilterButton />
<FilterButton />
<FilterButton />
```

이 코드로 변경해주세요.

```jsx
{filterList}
```

아직 작동하지 않습니다. 먼저 해야 할 일이 조금 더 있습니다.

### 상호 작용을 하는 필터

필터 버튼을 상호 작용할 수 있게 만들려면, 어떤 속성을 활용해야 하는지 고려해야 합니다.

* 우리는 `<FilterButton />`이 현재 눌렸는지 여부를 보고해야 하며, 이름이 필터 상태의 현재 값과 일치하면 눌러야 한다는 것을 알고 있습니다.
* 활성 필터를 설정하려면 `<FilterButton />`에 콜백이 필요하다는 것을 알고 있습니다. `setFilter` 훅을 직접 사용할 수 있습니다.

다음과 같이 `filterList` 상수를 업데이트합니다.

```jsx
const filterList = FILTER\_NAMES.map((name) => (
  <FilterButton
 key={name}
 name={name}
 isPressed={name === filter}
 setFilter={setFilter}
 />
));
```

이전에 `<Todo />` 컴포넌트에서 했던 것과 같은 방식으로, 이제 `FilterButton.js`를 업데이트하여 제공된 속성을 활용해야 합니다. 다음을 각각 수행하고, 중괄호를 사용하여 이러한 변수를 읽어오는 것을 잊지 마세요!

* `all`을 `{props.name}`으로 바꾸어줍니다.
* `aria-pressed` 값을 `{props.isPressed}`로 설정합니다.
* 필터 이름으로 `props.setFilter()`를 호출하는 `onClick` 핸들러를 추가합니다.

모든 할 일이 완료되면, `FilterButton()` 함수는 다음과 같아야 합니다.

```jsx
function FilterButton(props) {
  return (
    <button
 type="button"
 className="btn toggle-btn"
 aria-pressed={props.isPressed}
 onClick={() => props.setFilter(props.name)}>
 <span className="visually-hidden">보여주다</span>
 <span>{props.name}</span>
 <span className="visually-hidden">할 일들</span>
 </button>
  );
}
```

브라우저를 다시 열어보세요. 서로 다른 버튼에 각각의 이름이 부여된 것을 볼 수 있습니다. 필터 버튼을 누르면, 해당 텍스트가 새로운 윤곽선에 나타나는 것을 볼 수 있습니다. 이는 해당 버튼이 선택되었음을 알려줍니다. 버튼을 클릭하는 동안 DevTool의 Page Inspector를 보면, 그에 따라 `aria-pressed` 속성 값이 변경되는 것을 볼 수 있습니다.

![애플리케이션의 3개 필터 버튼 - 모두, 활성화 및 완료됨 - 완료 주위에 포커스 강조 표시](/ko/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_interactivity_filtering_conditional_rendering/filter-buttons.png)

그러나, 버튼은 여전히 UI에서 할 일을 실제로 필터링하지 않습니다! 이것으로 마무리하겠습니다.

### UI에서 할 일 필터링

지금은, `App()`의 `taskList` 상수가 할 일 상태를 매핑하고 모든 할 일에 대한 새로운 `<Todo />` 컴포넌트를 반환합니다. 이것은 우리가 원하는 것이 아닙니다! 할 일은 선택한 필터를 적용한 결과에 포함된 경우에만 렌더링되어야 합니다. 할 일 상태를 매핑하기 전에, 렌더링하지 않으려는 객체를 제거하기 위해 필터링(`Array.prototype.filter()`)해야 합니다.

다음과 같이 `taskList`를 업데이트하세요.

```jsx
const taskList = tasks
  .filter(FILTER\_MAP[filter])
  .map((task) => (
    <Todo
 id={task.id}
 name={task.name}
 completed={task.completed}
 key={task.id}
 toggleTaskCompleted={toggleTaskCompleted}
 deleteTask={deleteTask}
 editTask={editTask}
 />
  ));
```

`Array.prototype.filter()`에서 사용할 콜백 함수를 결정하기 위해, 필터 상태의 키에 해당하는 `FILTER_MAP`의 값에 접근합니다. 예를 들어, 필터가 `All`인 경우, `FILTER_MAP[filter]`는 `() => true`로 평가됩니다.

이제 브라우저에서 필터를 선택하면 기준에 맞지 않는 할 일들이 제거됩니다. 목록 위의 제목에 있는 개수도 목록을 반영하도록 변경됩니다.

![필터 버튼이 있는 앱. 활성화가 강조 표시되어, 활성화된 할 일 항목만 표시됩니다.](/ko/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_interactivity_filtering_conditional_rendering/filtered-todo-list.png)

요약
--

이제 애플리케이션이 기능적으로 완성되었습니다. 그러나, 이제 모든 기능을 구현했으므로, 더 많은 사용자가 애플리케이션을 사용할 수 있도록 몇 가지 개선 사항을 적용할 수 있습니다. 다음 글에서는 React에 포커스 관리를 포함하여, 사용성을 개선하고 키보드만 사용하는 사용자와 화면 판독기 사용자 모두의 혼란을 줄일 수 있는 방법을 살펴봄으로써, React 자습서의 내용을 마무리합니다.

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