aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--files/ko/learn/tools_and_testing/client-side_javascript_frameworks/react_todo_list_beginning/index.md635
-rw-r--r--files/ko/learn/tools_and_testing/client-side_javascript_frameworks/react_todo_list_beginning/unstyled-app.pngbin0 -> 24907 bytes
2 files changed, 635 insertions, 0 deletions
diff --git a/files/ko/learn/tools_and_testing/client-side_javascript_frameworks/react_todo_list_beginning/index.md b/files/ko/learn/tools_and_testing/client-side_javascript_frameworks/react_todo_list_beginning/index.md
new file mode 100644
index 0000000000..eb0ea7fd63
--- /dev/null
+++ b/files/ko/learn/tools_and_testing/client-side_javascript_frameworks/react_todo_list_beginning/index.md
@@ -0,0 +1,635 @@
+---
+title: React todo list 시작하기
+slug: >-
+ Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_todo_list_beginning
+tags:
+ - App
+ - Beginner
+ - Frameworks
+ - JavaScript
+ - Learn
+ - React
+ - Style
+ - client-side
+---
+{{LearnSidebar}}
+
+{{PreviousMenuNext("Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_getting_started","Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_components", "Learn/Tools_and_testing/Client-side_JavaScript_frameworks")}}
+
+여러분이 React로 개념 증명을 만드는, 곧 사용자가 원하는 작업을 추가, 편집, 그리고 삭제할 수 있으며, 작업을 삭제하지 않고 완료로 표시할 수도 있는 앱을 만드는 작업을 맡게 되었다고 가정해 보겠습니다. 이 기사는 기본 `App` 컴포넌트 구조와 스타일링을 설정하는 방법에 대해서 다루며, 여러분은 개별 컴포넌트 정의와 상호작용성을 배울 준비를 갖추게 될 겁니다.
+
+<div class="notecard note">
+<p class="summary"><strong>참고</strong>: 여러분의 코드를 우리의 것과 비교하고 싶다면 <a href="https://github.com/mdn/todo-react">todo-react repository</a>에서 최종적인 버전의 샘플 React 앱 코드를 확인해보세요. 실행 중인 라이브 버전은 <a href="https://mdn.github.io/todo-react-build/">https://mdn.github.io/todo-react-build/</a>에서 볼 수 있습니다.</p></div>
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">필요한 사전 지식:</th>
+ <td>
+ 코어 <a href="/ko/docs/Learn/HTML">HTML</a>, <a href="/ko/docs/Learn/CSS">CSS</a>, 및 <a href="/ko/docs/Learn/JavaScript">JavaScript</a> 언어에 익숙할 것. <a href="/ko/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Command_line">터미널/커맨드라인</a>에 대한 지식.
+ </td>
+ </tr>
+ <tr>
+ <th scope="row">목표:</th>
+ <td>투두 리스트를 만들고 기본 <code>App</code> 구조와 스타일링을 설정한다.</td>
+ </tr>
+ </tbody>
+</table>
+
+
+
+
+## 우리가 만들 애플리케이션의 유저 스토리
+
+소프트웨어 개발에서 유저 스토리(user story)는 사용자의 관점에서 실행 가능한 목표를 뜻합니다. 개발을 시작하기 전에 유저 스토리를 정의하는 것은 매우 도움이 됩니다. 우리의 애플리케이션은 다음과 같은 스토리를 만족해야 합니다:
+
+나는 사용자로서, 다음의 것들을 할 수 있다:
+
+- 작업 목록 읽기
+- 마우스나 키보드로 작업 추가하기
+- 마우스나 키보드를 사용하여 작업을 완료로 표시하기
+- 마우스나 키보드를 사용하여 작업 삭제하기
+- 마우스나 키보드를 사용하여 작업 편집하기
+- 작업을 특정한 집합으로 나누어 보기: 전체 작업, 진행 중인 작업, 혹은 완료된 작업들.
+
+이 스토리들을 하나하나 다뤄보겠습니다.
+
+
+
+## 프로젝트를 실행하기 전 점검하기
+
+create-react-app은 우리의 프로젝트에서 전혀 사용하지 않을 파일을 몇 개 만듭니다.
+
+- 컴포넌트마다 스타일 시트를 만들지 않을 것이므로, 우선 `App.js`의 상단에 `App.css`를 임포트하고 있는 문을 지웁니다.
+- `logo.svg` 파일 역시 사용하지 않을 것이므로, 이것을 임포트하고 있는 문 역시 삭제합니다.
+
+그러고 나서 필요하지 않은 파일들을 삭제하기 위해 터미널에 아래의 커맨드들을 복사, 붙여넣기 합니다. 애플리케이션의 루트 디렉토리에서 터미널을 실행하고 있는지 확인하세요!
+
+```shell
+# 프로젝트의 src 디렉토리로 이동
+cd src
+# 파일들을 삭제
+rm -- App.test.js App.css logo.svg serviceWorker.js setupTests.js
+# 프로젝트의 루트 디렉토리로 다시 돌아오기
+cd ..
+```
+
+참고:
+
+- 삭제하려는 파일 중 두 개는 애플리케이션을 테스트하기 위해 사용됩니다. 이 기사에서는 테스팅에 대해 다루지 않습니다.
+- 위의 터미널 작업을 수행하기 위해 서버를 멈추었다면, `npm start`를 사용하여 다시 시작해야 합니다.
+
+
+
+## 프로젝트 시작 코드
+
+프로젝트를 시작하기 위해 여러분이 지금 가지고 있는 것을 대신할 `App()` 함수와 애플리케이션을 꾸밀 CSS를 제공하겠습니다.
+
+
+
+### JSX
+
+아래의 스니펫을 복사하고 기존의 `App()` 함수 대신 `App.js`에 붙여넣으세요.
+
+```js
+function App(props) {
+ return (
+ <div className="todoapp stack-large">
+ <h1>TodoMatic</h1>
+ <form>
+ <h2 className="label-wrapper">
+ <label htmlFor="new-todo-input" className="label__lg">
+ What needs to be done?
+ </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">
+ Add
+ </button>
+ </form>
+ <div className="filters btn-group stack-exception">
+ <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>
+ <button type="button" className="btn toggle-btn" aria-pressed="false">
+ <span className="visually-hidden">Show </span>
+ <span>Active</span>
+ <span className="visually-hidden"> tasks</span>
+ </button>
+ <button type="button" className="btn toggle-btn" aria-pressed="false">
+ <span className="visually-hidden">Show </span>
+ <span>Completed</span>
+ <span className="visually-hidden"> tasks</span>
+ </button>
+ </div>
+ <h2 id="list-heading">
+ 3 tasks remaining
+ </h2>
+ <ul
+ role="list"
+ className="todo-list stack-large stack-exception"
+ aria-labelledby="list-heading"
+ >
+ <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>
+ <li className="todo stack-small">
+ <div className="c-cb">
+ <input id="todo-1" type="checkbox" />
+ <label className="todo-label" htmlFor="todo-1">
+ Sleep
+ </label>
+ </div>
+ <div className="btn-group">
+ <button type="button" className="btn">
+ Edit <span className="visually-hidden">Sleep</span>
+ </button>
+ <button type="button" className="btn btn__danger">
+ Delete <span className="visually-hidden">Sleep</span>
+ </button>
+ </div>
+ </li>
+ <li className="todo stack-small">
+ <div className="c-cb">
+ <input id="todo-2" type="checkbox" />
+ <label className="todo-label" htmlFor="todo-2">
+ Repeat
+ </label>
+ </div>
+ <div className="btn-group">
+ <button type="button" className="btn">
+ Edit <span className="visually-hidden">Repeat</span>
+ </button>
+ <button type="button" className="btn btn__danger">
+ Delete <span className="visually-hidden">Repeat</span>
+ </button>
+ </div>
+ </li>
+ </ul>
+ </div>
+ );
+}
+```
+
+이제 `public/index.html`을 열고 [`<title>`](/en-US/docs/Web/HTML/Element/title) 요소의 텍스트를 `TodoMatic`으로 바꾸세요. 이렇게 하면 우리의 애플리케이션 상단의 [`<h1>`](/en-US/docs/Web/HTML/Element/Heading_Elements)과 일치할 겁니다.
+
+```js
+<title>TodoMatic</title>
+```
+
+브라우저를 새로고침하면, 아래 이미지처럼 보일 거예요.
+
+![todo-matic app, unstyled, showing a jumbled mess of labels, inputs, and buttons](./unstyled-app.png)
+
+예쁘지 않고 아무런 기능도 갖추지 않았지만, 괜찮습니다! 곧 꾸밀 거니까요. 우선, JSX가 유저 스토리에 어떻게 대응하는지 살펴봅시다:
+
+- 새로운 작업을 쓰기 위한 [`<input type="text">`](/en-US/docs/Web/HTML/Element/input/text)와 폼(form)을 제출하기 위한 버튼을 가진 [`<form>`](/en-US/docs/Web/HTML/Element/form) 요소가 있습니다.
+- 작업을 필터링하기 위한 여러 개의 버튼이 있습니다.
+- 작업이 몇 개 남아있는지 알려주는 헤딩(heading)이 있습니다.
+- 순서가 매겨지지 않은 세 개의 작업이 있습니다. 각각의 작업은 리스트 아이템([`<li>`](/en-US/docs/Web/HTML/Element/li))이며, 편집하거나 삭제하기 위한 버튼과 완료를 표시할 체크 박스를 가지고 있습니다.
+
+폼은 작업을 *만들* 수 있게 해줍니다; 버튼들은 작업들을 *필터링할* 수 있게 해줍니다; 헤딩과 리스트는 작업들을 *읽을* 수 있게 해줍니다. 작업을 *편집하는* UI는 현재는 없습니다. 이것도 괜찮습니다 – 나중에 만들 거니까요.
+
+
+
+### 접근성 기능
+
+익숙하지 않은 속성을 발견했을 겁니다. 예를 들어:
+
+```js
+<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>
+```
+
+`aria-pressed`은 보조 기술(예: 스크린 리더)에게 하나의 버튼이 두 개의 상태 `pressed`와 `unpressed`로 있을 수 있다는 것을 알려줍니다. `on`과 `off`라고 생각해보세요. 값을 `true`로 설정하는 것은 버튼을 기본적으로 눌린 상태라는 것을 의미합니다.
+
+클래스 `visually-hidden`는 아직 CSS를 연결하지 않았으므로 아직 아무런 이펙트도 없습니다. 제대로 스타일을 설정하면 이 클래스를 가진 모든 요소가 시력이 정상인 사용자에게는 숨겨지며 스크린 리더 사용자에게만 보입니다 — 이 단어들은 시력이 정상인 사용자들에게 필요하지 않기 때문입니다; 단어들은 추가적인 시각적 맥락(visual context)을 가지고 있지 않은 스크린 리더 사용자들에게 버튼이 무엇을 하는지 더 많은 정보를 제공합니다.
+
+아래에서 [`<ul>`](/en-US/docs/Web/HTML/Element/ul) 요소를 찾을 수 있을 겁니다.
+
+```js
+<ul
+ role="list"
+ className="todo-list stack-large stack-exception"
+ aria-labelledby="list-heading"
+>
+```
+
+`role` 속성은 보조 기술이 태그가 나타내고 있는 요소의 종류가 무엇인지 설명하는 데 도움이 됩니다. `<ul>`은 기본적으로 리스트처럼 취급되지만, 우리가 추가할 스타일은 리스트의 기능성을 해칩니다. `role="list"`는 `<ul>` 요소가 "리스트"의 의미를 회복하도록 합니다. 이것이 왜 필요한지 알고 싶다면, [Scott O'Hara’s article, “Fixing Lists”](https://www.scottohara.me/blog/2019/01/12/lists-and-safari.html)을 참고하세요.
+
+`aria-labelledby` 속성은 보조 기술에게 리스트 헤딩을 헤딩 아래의 리스트의 목적을 나타내는 라벨로 사용하고 있음을 알립니다. 이러한 연관을 만드는 것은 리스트가 더욱 정보가 많은 맥락을 가지게 하여, 스크린 리더 사용자가 리스트의 목적을 더 잘 이해할 수 있도록 합니다.
+
+마지막으로, 리스트 항목 안의 라벨과 인풋들은 JSX에 고유한 속성을 가지고 있습니다.
+
+```js
+<input id="todo-0" type="checkbox" defaultChecked={true} />
+<label className="todo-label" htmlFor="todo-0">
+ Eat
+</label>
+```
+
+`<input/ >`의 `defaultChecked` 속성은 React가 초기에 이 체크박스를 체크하도록 합니다. HTML에서처럼 `checked`를 사용한다면, React는 체크박스에 대한 이벤트를 다루는 것과 관련한 경고를 브라우저 콘솔에 출력할 것입니다. 지금은 크게 걱정하지 마세요 — 나중에 이벤트를 사용할 때 이야기할테니까요.
+
+`htmlFor` 속성은 HTML에서는 `for` 속성과 대응합니다. JSX에서는 `for`을 속성으로 사용하지 않는데, `for`이 예약어(reserved word)이기 때문입니다. 그래서 React는 `htmlFor`을 대신 사용합니다.
+
+참고:
+
+- JSX 속성에 불리언 값들(`true`와 `false`)를 사용하기 위해서는 이 값들을 중괄호로 감싸야 합니다. `defaultChecked="true"`라고 적는다면, `defaultChecked`의 값은 문자열 리터럴인 `"true"`가 될 겁니다. 꼭 기억하세요 — JSX는 JavaScript이지, HTML이 아닙니다!
+- 이전의 코드 스니펫에서 사용되었던 `aria-pressed` 속성은 `"true"`라는 값을 가지는데, 이것은 `aria-pressed`가 `checked`가 있는 방식에서 진짜 불리언 속성이 아니기 때문입니다.
+
+### 스타일 적용하기
+
+`src/index.css`에 기존에 있던 것 대신 아래의 CSS 코드를 붙여넣기하세요.
+
+```css
+/* RESETS */
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+}
+*:focus {
+ outline: 3px dashed #228bec;
+ outline-offset: 0;
+}
+html {
+ font: 62.5% / 1.15 sans-serif;
+}
+h1,
+h2 {
+ margin-bottom: 0;
+}
+ul {
+ list-style: none;
+ padding: 0;
+}
+button {
+ border: none;
+ margin: 0;
+ padding: 0;
+ width: auto;
+ overflow: visible;
+ background: transparent;
+ color: inherit;
+ font: inherit;
+ line-height: normal;
+ -webkit-font-smoothing: inherit;
+ -moz-osx-font-smoothing: inherit;
+ -webkit-appearance: none;
+}
+button::-moz-focus-inner {
+ border: 0;
+}
+button,
+input,
+optgroup,
+select,
+textarea {
+ font-family: inherit;
+ font-size: 100%;
+ line-height: 1.15;
+ margin: 0;
+}
+button,
+input {
+ overflow: visible;
+}
+input[type="text"] {
+ border-radius: 0;
+}
+body {
+ width: 100%;
+ max-width: 68rem;
+ margin: 0 auto;
+ font: 1.6rem/1.25 Arial, sans-serif;
+ background-color: #f5f5f5;
+ color: #4d4d4d;
+}
+@media screen and (min-width: 620px) {
+ body {
+ font-size: 1.9rem;
+ line-height: 1.31579;
+ }
+}
+/*END RESETS*/
+/* GLOBAL STYLES */
+.form-group > input[type="text"] {
+ display: inline-block;
+ margin-top: 0.4rem;
+}
+.btn {
+ padding: 0.8rem 1rem 0.7rem;
+ border: 0.2rem solid #4d4d4d;
+ cursor: pointer;
+ text-transform: capitalize;
+}
+.btn.toggle-btn {
+ border-width: 1px;
+ border-color: #d3d3d3;
+}
+.btn.toggle-btn[aria-pressed="true"] {
+ text-decoration: underline;
+ border-color: #4d4d4d;
+}
+.btn__danger {
+ color: #fff;
+ background-color: #ca3c3c;
+ border-color: #bd2130;
+}
+.btn__filter {
+ border-color: lightgrey;
+}
+.btn__primary {
+ color: #fff;
+ background-color: #000;
+}
+.btn-group {
+ display: flex;
+ justify-content: space-between;
+}
+.btn-group > * {
+ flex: 1 1 49%;
+}
+.btn-group > * + * {
+ margin-left: 0.8rem;
+}
+.label-wrapper {
+ margin: 0;
+ flex: 0 0 100%;
+ text-align: center;
+}
+.visually-hidden {
+ position: absolute !important;
+ height: 1px;
+ width: 1px;
+ overflow: hidden;
+ clip: rect(1px 1px 1px 1px);
+ clip: rect(1px, 1px, 1px, 1px);
+ white-space: nowrap;
+}
+[class*="stack"] > * {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+.stack-small > * + * {
+ margin-top: 1.25rem;
+}
+.stack-large > * + * {
+ margin-top: 2.5rem;
+}
+@media screen and (min-width: 550px) {
+ .stack-small > * + * {
+ margin-top: 1.4rem;
+ }
+ .stack-large > * + * {
+ margin-top: 2.8rem;
+ }
+}
+.stack-exception {
+ margin-top: 1.2rem;
+}
+/* END GLOBAL STYLES */
+.todoapp {
+ background: #fff;
+ margin: 2rem 0 4rem 0;
+ padding: 1rem;
+ position: relative;
+ box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 2.5rem 5rem 0 rgba(0, 0, 0, 0.1);
+}
+@media screen and (min-width: 550px) {
+ .todoapp {
+ padding: 4rem;
+ }
+}
+.todoapp > * {
+ max-width: 50rem;
+ margin-left: auto;
+ margin-right: auto;
+}
+.todoapp > form {
+ max-width: 100%;
+}
+.todoapp > h1 {
+ display: block;
+ max-width: 100%;
+ text-align: center;
+ margin: 0;
+ margin-bottom: 1rem;
+}
+.label__lg {
+ line-height: 1.01567;
+ font-weight: 300;
+ padding: 0.8rem;
+ margin-bottom: 1rem;
+ text-align: center;
+}
+.input__lg {
+ padding: 2rem;
+ border: 2px solid #000;
+}
+.input__lg:focus {
+ border-color: #4d4d4d;
+ box-shadow: inset 0 0 0 2px;
+}
+[class*="__lg"] {
+ display: inline-block;
+ width: 100%;
+ font-size: 1.9rem;
+}
+[class*="__lg"]:not(:last-child) {
+ margin-bottom: 1rem;
+}
+@media screen and (min-width: 620px) {
+ [class*="__lg"] {
+ font-size: 2.4rem;
+ }
+}
+.filters {
+ width: 100%;
+ margin: unset auto;
+}
+/* Todo item styles */
+.todo {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+}
+.todo > * {
+ flex: 0 0 100%;
+}
+.todo-text {
+ width: 100%;
+ min-height: 4.4rem;
+ padding: 0.4rem 0.8rem;
+ border: 2px solid #565656;
+}
+.todo-text:focus {
+ box-shadow: inset 0 0 0 2px;
+}
+/* CHECKBOX STYLES */
+.c-cb {
+ box-sizing: border-box;
+ font-family: Arial, sans-serif;
+ -webkit-font-smoothing: antialiased;
+ font-weight: 400;
+ font-size: 1.6rem;
+ line-height: 1.25;
+ display: block;
+ position: relative;
+ min-height: 44px;
+ padding-left: 40px;
+ clear: left;
+}
+.c-cb > label::before,
+.c-cb > input[type="checkbox"] {
+ box-sizing: border-box;
+ top: -2px;
+ left: -2px;
+ width: 44px;
+ height: 44px;
+}
+.c-cb > input[type="checkbox"] {
+ -webkit-font-smoothing: antialiased;
+ cursor: pointer;
+ position: absolute;
+ z-index: 1;
+ margin: 0;
+ opacity: 0;
+}
+.c-cb > label {
+ font-size: inherit;
+ font-family: inherit;
+ line-height: inherit;
+ display: inline-block;
+ margin-bottom: 0;
+ padding: 8px 15px 5px;
+ cursor: pointer;
+ touch-action: manipulation;
+}
+.c-cb > label::before {
+ content: "";
+ position: absolute;
+ border: 2px solid currentColor;
+ background: transparent;
+}
+.c-cb > input[type="checkbox"]:focus + label::before {
+ border-width: 4px;
+ outline: 3px dashed #228bec;
+}
+.c-cb > label::after {
+ box-sizing: content-box;
+ content: "";
+ position: absolute;
+ top: 11px;
+ left: 9px;
+ width: 18px;
+ height: 7px;
+ transform: rotate(-45deg);
+ border: solid;
+ border-width: 0 0 5px 5px;
+ border-top-color: transparent;
+ opacity: 0;
+ background: transparent;
+}
+.c-cb > input[type="checkbox"]:checked + label::after {
+ opacity: 1;
+}
+```
+
+코드를 저장하고 브라우저로 되돌아오면, 이제 애플리케이션은 적당한 스타일을 가지게 될 겁니다.
+
+
+
+## 요약
+
+이제 우리의 투두 리스트는 조금 더 진짜 애플리케이션 같아졌습니다! 문제는, 이 앱이 실제로는 아무것도 하지 않는다는 거죠. 다음 챕터에서 해결해봅시다!
+
+
+
+{{PreviousMenuNext("Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_getting_started","Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_components", "Learn/Tools_and_testing/Client-side_JavaScript_frameworks")}}
+
+<h2 id="In_this_module">In this module</h2>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Introduction">Introduction to client-side frameworks</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Main_features">Framework main features</a></li>
+ <li>React
+ <ul>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_getting_started">Getting started with React</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_todo_list_beginning">Beginning our React todo list</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_components">Componentizing our React app</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_interactivity_events_state">React interactivity: Events and state</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_interactivity_filtering_conditional_rendering">React interactivity: Editing, filtering, conditional rendering</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_accessibility">Accessibility in React</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_resources">React resources</a></li>
+ </ul>
+ </li>
+ <li>Ember
+ <ul>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_getting_started">Getting started with Ember</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_structure_componentization">Ember app structure and componentization</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_interactivity_events_state">Ember interactivity: Events, classes and state</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_conditional_footer">Ember Interactivity: Footer functionality, conditional rendering</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_routing">Routing in Ember</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_resources">Ember resources and troubleshooting</a></li>
+ </ul>
+ </li>
+ <li>Vue
+ <ul>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_getting_started">Getting started with Vue</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_first_component">Creating our first Vue component</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_rendering_lists">Rendering a list of Vue components</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_methods_events_models">Adding a new todo form: Vue events, methods, and models</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_styling">Styling Vue components with CSS</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_computed_properties">Using Vue computed properties</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_conditional_rendering">Vue conditional rendering: editing existing todos</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_refs_focus_management">Focus management with Vue refs</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_resources">Vue resources</a></li>
+ </ul>
+ </li>
+ <li>Svelte
+ <ul>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_getting_started">Getting started with Svelte</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_Todo_list_beginning">Starting our Svelte Todo list app</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_variables_props">Dynamic behavior in Svelte: working with variables and props</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_components">Componentizing our Svelte app</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_reactivity_lifecycle_accessibility">Advanced Svelte: Reactivity, lifecycle, accessibility</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_stores">Working with Svelte stores</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_TypeScript">TypeScript support in Svelte</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_deployment_next">Deployment and next steps</a></li>
+ </ul>
+ </li>
+ <li>Angular
+ <ul>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_getting_started">Getting started with Angular</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_todo_list_beginning">Beginning our Angular todo list app</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_styling">Styling our Angular app</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_item_component">Creating an item component</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_filtering">Filtering our to-do items</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_building">Building Angular applications and further resources</a></li>
+ </ul>
+ </li>
+</ul>
diff --git a/files/ko/learn/tools_and_testing/client-side_javascript_frameworks/react_todo_list_beginning/unstyled-app.png b/files/ko/learn/tools_and_testing/client-side_javascript_frameworks/react_todo_list_beginning/unstyled-app.png
new file mode 100644
index 0000000000..a25911baa1
--- /dev/null
+++ b/files/ko/learn/tools_and_testing/client-side_javascript_frameworks/react_todo_list_beginning/unstyled-app.png
Binary files differ