FIX : fix url for translations folder

This commit is contained in:
minwook-shin
2020-11-30 18:39:54 +09:00
parent 3e0e431d7e
commit 97de424e91
32 changed files with 152 additions and 152 deletions

View File

@@ -0,0 +1,291 @@
# Cree un formulario de inicio de sesión y registro
## [Prueba previa a la conferencia](.github/prelecture-quiz.md)
### Introducción
En casi todas las aplicaciones web modernas, puede crear una cuenta para tener su propio espacio privado. Como varios usuarios pueden acceder a una aplicación web al mismo tiempo, necesita un mecanismo para almacenar los datos personales de cada usuario por separado y seleccionar qué información mostrar. No cubriremos cómo administrar [la identidad del usuario de forma segura](https://en.wikipedia.org/wiki/Authentication) ya que es un tema extenso en sí mismo, pero nos aseguraremos de que cada usuario pueda crear uno. O más cuenta bancaria en nuestra aplicación.
En esta parte usaremos formularios HTML para agregar inicio de sesión y registro a nuestra aplicación web. Veremos cómo enviar los datos a una API de servidor de forma programática y, en última instancia, cómo definir reglas de validación básicas para las entradas del usuario.
### Requisito previo
Debe haber completado las [plantillas HTML y enrutamiento](../1-template-route/README.md) de la aplicación web para esta lección. También necesita instalar [Node.js](https://nodejs.org) y [ejecutar la API del servidor](../api/README.md) localmente para poder enviar datos para crear cuentas.
Puede probar que el servidor está funcionando correctamente ejecutando este comando en una terminal:
```sh
curl http://localhost:5000/api
# -> should return "Bank API v1.0.0" as a result
```
---
## Forma y controles
El elemento `<form>` encapsula una sección de un documento HTML donde el usuario puede ingresar y enviar datos con controles interactivos. Hay todo tipo de controles de interfaz de usuario (UI) que se pueden usar dentro de un formulario, siendo el más común los elementos `<input>` y `<button>`.
Hay muchos [types diferentes](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) de `<input>`, por ejemplo, para crear un campo donde el usuario puede ingresar su nombre de usuario que puede usar:
```html
<input name="username" type="text">
```
El atributo `name` se usa para identificar el control y se usará como el nombre de la propiedad cuando se envíen los datos del formulario.
> Eche un vistazo a la lista completa de [`<input>` tipos](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) y [otros controles de formulario](https://developer.mozilla.org/en-US/docs/Learn/Forms/Other_form_controls) para tener una idea de todos los elementos nativos de la interfaz de usuario que puede utilizar al crear su interfaz de usuario.
✅ Tenga en cuenta que `<input>` es un [elemento vacío](https://developer.mozilla.org/en-US/docs/Glossary/Empty_element) en el que *no* debe agregar una etiqueta de cierre coincidente. Sin embargo, puede usar la notación de cierre automático `<input/>`, pero no es necesaria.
El elemento `<button>` dentro de un formulario es un poco especial. Si no especifica su atributo `type`, automáticamente enviará los datos del formulario al servidor cuando se presione. Estos son los posibles valores de tipo:
- `enviar`: el valor predeterminado dentro de un` <formulario> `, el botón activa la acción de envío del formulario.
- `reset`: El botón restablece todos los controles de formulario a sus valores iniciales.
- `button`: No asigna un comportamiento predeterminado cuando se presiona el botón. A continuación, puede asignarle acciones personalizadas mediante JavaScript.
### Tarea:
Comencemos agregando un formulario a la plantilla de inicio de sesión. Necesitaremos un campo *nombre de usuario* y un botón *Iniciar sesión*.
```html
<template id="login">
<h1>Bank App</h1>
<section>
<h2>Login</h2>
<form id="loginForm">
<label for="user">Username</label>
<input name="user" type="text">
<button>Login</button>
</form>
</section>
</template>
```
Si observa más de cerca, puede notar que también agregamos un elemento `<label>` aquí. Las `<label>` se utilizan para agregar un título para los controles de la IU, como nuestro campo de nombre de usuario. Las etiquetas son importantes para la legibilidad de sus formularios, pero también tienen beneficios adicionales:
- Al asociar una etiqueta a un control de formulario, ayuda a los usuarios que utilizan tecnologías de asistencia (como un lector de pantalla) a comprender qué datos se espera que proporcionen.
- Puede hacer clic en la etiqueta para centrarse directamente en la entrada asociada, lo que facilita el acceso a los dispositivos con pantalla táctil.
> [Accesibilidad](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/What_is_accessibility) en la web es un tema muy importante que a menudo se pasa por alto. Gracias a los [elementos semánticos HTML5](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/HTML) no es difícil crear contenido accesible si los usas correctamente. Puede [leer más sobre accesibilidad](https://developer.mozilla.org/en-US/docs/Web/Accessibility) para evitar errores comunes y convertirse en un desarrollador responsable.
Ahora agregaremos un segundo formulario para el registro, justo debajo del anterior:
```html
<hr/>
<h2>Register</h2>
<form id="registerForm">
<label for="user">Username</label>
<input name="user" type="text">
<label for="currency">Currency</label>
<input name="currency" type="text" value="$">
<label for="description">Description</label>
<input name="description" type="text">
<label for="balance">Current balance</label>
<input name="balance" type="number" value="0">
<button>Register</button>
</form>
```
Usando el atributo `value` podemos definir un valor predeterminado para una entrada dada.
Observe también que la entrada para `balance` tiene el tipo `number`. ¿Se ve diferente a las otras entradas? Intenta interactuar con él.
✅ ¿Puede navegar e interactuar con los formularios usando solo un teclado? ¿Cómo lo harías tú?
## Envío de datos al servidor
Ahora que tenemos una interfaz de usuario funcional, el siguiente paso es enviar los datos a nuestro servidor. Hagamos una prueba rápida con nuestro código actual: ¿qué sucede si hace clic en el botón *Iniciar sesión* o *Registrarse*?
¿Notó el cambio en la sección de URL de su navegador?
! [Captura de pantalla del cambio de URL del navegador después de hacer clic en el botón Registrar](./images/click-register.png)
La acción predeterminada para un `<form>` es enviar el formulario a la URL del servidor actual utilizando el [método GET](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.3 ), agregando los datos del formulario directamente a la URL. Sin embargo, este método tiene algunas deficiencias:
- Los datos enviados son de tamaño muy limitado (unos 2000 caracteres)
- Los datos son directamente visibles en la URL (no es ideal para contraseñas)
- No funciona con cargas de archivos.
Es por eso que puede cambiarlo para usar el [método POST](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5) que envía los datos del formulario al servidor en el cuerpo de la solicitud HTTP, sin ninguna de las limitaciones anteriores.
> Si bien POST es el método más utilizado para enviar datos, [en algunos escenarios específicos](https://www.w3.org/2001/tag/doc/whenToUseGet.html) es preferible utilizar el método GET, al implementar un campo de búsqueda, por ejemplo.
### Tarea
Agregue las propiedades `action` y `method` al formulario de registro:
```html
<form id="registerForm" action="//localhost:5000/api/accounts" method="POST">
```
Ahora intente registrar una nueva cuenta con su nombre. Después de hacer clic en el botón * Registrarse *, debería ver algo como esto:
![](./images/form-post.png)
Si todo va bien, el servidor debe responder a su solicitud con una respuesta [JSON](https://www.json.org/json-en.html) que contenga los datos de la cuenta que se creó.
✅ Intente registrarse nuevamente con el mismo nombre. ¿Lo que pasa?
## Envío de datos sin recargar la página
Como probablemente haya notado, hay un pequeño problema con el enfoque que acabamos de usar: al enviar el formulario, salimos de nuestra aplicación y el navegador redirige a la URL del servidor. Estamos tratando de evitar todas las recargas de páginas con nuestra aplicación web, ya que estamos creando una [Aplicación de una sola página (SPA)](https://en.wikipedia.org/wiki/Single-page_application).
Para enviar los datos del formulario al servidor sin forzar la recarga de una página, tenemos que usar código JavaScript. En lugar de poner una URL en la propiedad `action` de un elemento `<form>`, puede usar cualquier código JavaScript precedido por la cadena `javascript:` para realizar una acción personalizada. Usar esto también significa que tendrá que implementar algunas tareas que anteriormente el navegador realizaba automáticamente:
- Recuperar los datos del formulario
- Convierta y codifique los datos del formulario a un formato adecuado
- Crea la solicitud HTTP y envíala al servidor
### Tarea
Reemplace el formulario de registro `acción` con:
```html
<form id="registerForm" action="javascript:register()">
```
Abra `app.js` agregue una nueva función llamada `registro`:
```js
function register() {
const registerForm = document.getElementById('registerForm');
const formData = new FormData(registerForm);
const data = Object.fromEntries(formData);
const jsonData = JSON.stringify(data);
}
```
Aquí recuperamos el elemento del formulario usando `getElementById()` y usamos el ayudante [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) para extraer los valores del formulario controles como un conjunto de pares clave/valor. Luego convertimos los datos a un objeto regular usando [`Object.fromEntries()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries) y finalmente serialice los datos en [JSON](https://www.json.org/json-en.html), un formato que se utiliza comúnmente para intercambiar datos en la web.
Los datos ahora están listos para enviarse al servidor. Cree una nueva función llamada `createAccount`:
```js
async function createAccount(account) {
try {
const response = await fetch('//localhost:5000/api/accounts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: account
});
return await response.json();
} catch (error) {
return { error: error.message || 'Unknown error' };
}
}
```
¿Qué hace esta función? Primero, observe la palabra clave `async` aquí. Esto significa que la función contiene código que se ejecutará [** asincrónicamente**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function). Cuando se usa junto con la palabra clave `await`, permite esperar a que se ejecute el código asincrónico, como esperar la respuesta del servidor aquí, antes de continuar.
Aquí hay un video rápido sobre el uso de `async/await`:
[![Async y Await para administrar promesas](https://img.youtube.com/vi/YwmlRkrxvkk/0.jpg)] (https://youtube.com/watch?v=YwmlRkrxvkk "Async y Await para administrar promesas")
Usamos la API `fetch()` para enviar datos JSON al servidor. Este método toma 2 parámetros:
- La URL del servidor, por lo que volvemos a colocar `//localhost:5000/api/accounts` aquí.
- La configuración de la solicitud. Ahí es donde establecemos el método en `POST` y proporcionamos el `body` de la solicitud. Como estamos enviando datos JSON al servidor, también necesitamos establecer el encabezado `Content-Type` en `application/json` para que el servidor sepa cómo interpretar el contenido.
Como el servidor responderá a la solicitud con JSON, podemos usar `await response.json()` para analizar el contenido JSON y devolver el objeto resultante. Tenga en cuenta que este método es asíncrono, por lo que usamos la palabra clave `await` aquí antes de regresar para asegurarnos de que también se detecta cualquier error durante el análisis.
Ahora agregue un código a la función `register` para llamar a `createAccount()`:
```js
const result = await createAccount(jsonData);
```
Debido a que usamos la palabra clave `await` aquí, necesitamos agregar la palabra clave `async` antes de la función de registro:
```js
async function register() {
```
Finalmente, agreguemos algunos registros para verificar el resultado. La función final debería verse así:
```js
async function register() {
const registerForm = document.getElementById('registerForm');
const formData = new FormData(registerForm);
const jsonData = JSON.stringify(Object.fromEntries(formData));
const result = await createAccount(jsonData);
if (result.error) {
return console.log('An error occured:', result.error);
}
console.log('Account created!', result);
}
```
¡Eso fue un poco largo pero llegamos allí! Si abre sus [herramientas de desarrollo del navegador](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_are_browser_developer_tools) e intenta registrar una nueva cuenta, no debería ver ningún cambio en la página web pero aparecerá un mensaje en la consola confirmando que todo funciona.
![Captura de pantalla que muestra el mensaje de registro en la consola del navegador](./images/browser-console.png)
✅ ¿Crees que los datos se envían al servidor de forma segura? ¿Y si alguien pudiera interceptar la solicitud? Puede leer sobre [HTTPS](https://en.wikipedia.org/wiki/HTTPS) para saber más sobre la comunicación segura de datos.
## Validación de datos
Si intenta registrar una nueva cuenta sin establecer un nombre de usuario primero, puede ver que el servidor devuelve un error con el código de estado [400 (Solicitud incorrecta)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400#:~:text=The%20HyperText%20Transfer%20Protocol%20(HTTP,%2C%20or%20deceptive%20request%20routing).).
Antes de enviar datos a un servidor, es una buena práctica [validar los datos del formulario](https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation) de antemano cuando sea posible, para asegurarse de enviar un solicitud válida. Los controles de formularios HTML5 proporcionan una validación incorporada utilizando varios atributos:
- `required`: el campo debe completarse; de lo contrario, el formulario no se podrá enviar
- `minlength` y `maxlength`: define el número mínimo y máximo de caracteres en los campos de texto.
- `min` y `max`: define el valor mínimo y máximo de un campo numérico.
- `type`: define el tipo de datos esperados, como `number`, `email`, `file` u [otros tipos integrados](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). Este atributo también puede cambiar la representación visual del control de formulario.
- `patrón`: permite definir un patrón [expresión regular](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) para probar si los datos ingresados son válidos o no.
> Consejo: puedes personalizar el aspecto de los controles de tu formulario dependiendo de si son válidos o no usando las pseudoclases CSS `:valid` y `:invalid`.
### Tarea
Hay 2 campos obligatorios para crear una nueva cuenta válida, el nombre de usuario y la moneda, los otros campos son opcionales. Actualice el formulario en HTML para reflejar que:
```html
<input name="user" type="text" required>
...
<input name="currency" type="text" value="$" required>
```
Si bien esta implementación de servidor en particular no impone límites específicos en la longitud máxima de los campos, siempre es una buena práctica definir límites razonables para cualquier entrada de texto del usuario.
Agrega un atributo `maxlength` a los campos de texto:
```html
<input name="user" type="text" maxlength="20" required>
...
<input name="currency" type="text" value="$" maxlength="5" required>
...
<input name="description" type="text" maxlength="100">
```
Ahora, si presiona el botón *Registrar* y un campo no respeta una regla de validación que definimos, debería ver algo como esto:
![Captura de pantalla que muestra el error de validación al intentar enviar el formulario](./images/validation-error.png)
Una validación como esta realizada *antes* de enviar cualquier dato al servidor se llama validación **del lado del cliente**. Pero tenga en cuenta que no siempre es posible realizar todas las comprobaciones sin enviar los datos. Por ejemplo, no podemos comprobar aquí si ya existe una cuenta con el mismo nombre de usuario sin enviar una solicitud al servidor. La validación adicional realizada en el servidor se denomina validación **del lado del servidor**.
Por lo general, ambos deben implementarse y, si bien el uso de la validación del lado del cliente mejora la experiencia del usuario al proporcionar comentarios instantáneos al usuario, la validación del lado del servidor es crucial para garantizar que los datos del usuario que manipula sean sólidos y seguros.
---
## 🚀 Desafío
Muestra un mensaje de error en el HTML si el usuario ya existe.
Aquí hay un ejemplo de cómo puede verse la página de inicio de sesión final después de un poco de estilo:
![Captura de pantalla de la página de inicio de sesión después de agregar estilos CSS](./images/result.png)
## [Prueba posterior a la conferencia](.github/post-lecture-quiz.md)
## Revisión y autoestudio
Los desarrolladores se han vuelto muy creativos en sus esfuerzos de creación de formularios, especialmente en lo que respecta a las estrategias de validación. Obtenga información sobre los diferentes flujos de formularios consultando [CodePen](https://codepen.com); ¿Puedes encontrar algunas formas interesantes e inspiradoras?
## Asignación
[Diseña tu aplicación bancaria](assignment.md)

View File

@@ -0,0 +1,290 @@
# 은행 앱 제작하기 파트 2: 로그인과 가입 폼 작성하기
## 강의 전 퀴즈
[Pre-lecture quiz](../.github/pre-lecture-quiz.md)
### 소개
모든 모던 웹 앱에서 대부분은, 자신의 개인 공간을 가질 계정을 만들 수 있습니다. 여러 사용자가 동시에 웹 앱에 접근할 수 있으므로, 각자 사용자의 개인 데이터를 별도로 저장하고 어느 정보를 보여줄 지에 대하여 선택하는 메커니즘이 필요합니다. 자체적으로 광범위한 주제이므로 [user identity securely](https://en.wikipedia.org/wiki/Authentication) 관리하는 방법은 다루지 않지만, 각자가 앱에서 하나 (이상)의 은행 계좌를 만들 수 있는지 확인합니다.
이 파트에서는 HTML 폼으로 웹 앱에 로그인과 가입을 추가합니다. 프로그래밍 방식으로 데이터를 서버 API에 보내는 방법과, 최종적으로 사용자 입력에 대한 기본 유효성 검사 규칙을 정의하는 방법에 대해 보겠습니다.
### 준비물
이 강의를 위해 웹 앱의 [HTML templates and routing](../../1-template-route/translations/README.ko.md)을 완료해야합니다. 또한 [Node.js](https://nodejs.org)와 [run the server API](../../api/README.md)를 로컬에 설치해야 계정을 만들 데이터를 보낼 수 있습니다.
터미널에서 다음 명령을 실행하여 서버가 잘 실행되고 있는지 테스트할 수 있습니다:
```sh
curl http://localhost:5000/api
# -> should return "Bank API v1.0.0" as a result
```
---
## 폼과 컨트롤
`<form>` 요소는 사용자가 대화형 컨트롤을 사용하여 데이터를 입력하고 제출할 수 있는 HTML 문서의 섹션을 캡슐화합니다. 폼 내에서 쓸 수 있는 모든 종류의 사용자 인터페이스(UI) 컨트롤이 있으며, 가장 일반적인 컨트롤은 `<input>``<button>` 요소입니다.
`<input>`에는 다양한 [types](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input)이 많이 있습니다, 예를 들어 사용자 이름으로 입력 가능한 필드를 만들려면 다음과 같이 사용할 수 있습니다:
```html
<input name="username" type="text">
```
`name` 속성은 컨트롤을 식별하는 데 사용되고 폼 데이터를 전송할 때 속성 이름으로 사용됩니다.
> UI를 작성할 때 쓸 수 있는 모든 네이티브 UI 요소에 대한 아이디어를 얻으려면 [`<input>` types](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input)과 [other form controls](https://developer.mozilla.org/en-US/docs/Learn/Forms/Other_form_controls)의 전체 목록을 찾아봅시다.
`<input>`은 닫는 태그를 맞추지 *않는* [empty element](https://developer.mozilla.org/en-US/docs/Glossary/Empty_element)입니다. 자동으로-닫는 `<input/>` 표기법을 사용할 수 있지만, 필수는 아닙니다.
폼 내의 `<button>` 요소는 약간 특별합니다. `type` 속성을 지정하지 않으면, 눌렀을 때 폼 데이터가 자동으로 서버에 제출됩니다. 가능한 `type` 값은 다음과 같습니다:
- `submit`: `<form>`내의 기본값이며, 버튼은 폼 제출 작업으로 연결합니다.
- `reset`: 버튼은 모든 폼 컨트롤을 초기 값으로 다시 설정합니다.
- `button`: 버튼을 눌렀을 때 기본 동작을 지정하지 않습니다. JavaScript를 사용하여 커스텀 작업을 할당할 수 있습니다.
### 작업
`login` 템플릿에 폼을 추가하는 것으로 시작하겠습니다. *username* 필드와 *Login* 버튼이 필요합니다.
```html
<template id="login">
<h1>Bank App</h1>
<section>
<h2>Login</h2>
<form id="loginForm">
<label for="user">Username</label>
<input name="user" type="text">
<button>Login</button>
</form>
</section>
</template>
```
자세히 살펴보면, 여기에 `<label>` 요소도 추가된 것을 알 수 있습니다. `<label>`은 username 필드와 같은, UI 컨트롤의 캡션을 추가하는 데 사용됩니다. 라벨은 폼의 가독성을 위해서 중요하지만, 추가적인 장점도 제공합니다:
- 라벨을 폼 컨트롤에 연결하면, (화면 판독기와 같은) 보조 기술을 사용하는 사용자가 받는 것으로 예상되는 데이터를 이해하는 데 도움이 됩니다.
- 라벨을 클릭하여 연결된 입력에 직접 맞출 수 있으므로, 터치-스크린 기반 장치에서 더 쉽게 접근할 수 있습니다.
> 웹에서의 [Accessibility](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/What_is_accessibility)은 종종 간과되는 매우 중요한 주제입니다. [HTML5 semantic elements](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/HTML) 덕분에 이를 적절하게 사용한다면 접근성 콘텐츠로 만드는 것은 어렵지 않습니다. 일반적인 실수를 피하고 책임있는 개발자가 되기 위해 [accessibility에 대하여 읽을 수](https://developer.mozilla.org/en-US/docs/Web/Accessibility) 있습니다.
이제 이전 항목의 바로 아래에, 가입을 위한 두번째 폼을 추가합니다:
```html
<hr/>
<h2>Register</h2>
<form id="registerForm">
<label for="user">Username</label>
<input name="user" type="text">
<label for="currency">Currency</label>
<input name="currency" type="text" value="$">
<label for="description">Description</label>
<input name="description" type="text">
<label for="balance">Current balance</label>
<input name="balance" type="number" value="0">
<button>Register</button>
</form>
```
`value` 속성을 사용하여 주어진 입력에 대한 기본값을 정의할 수 있습니다.
`balance`에 대한 입력에는 `number` 타입이 존재 합니다. 다른 입력과 다르게 보이나요? 상호작용 해보세요.
✅ 키보드만 사용하여 폼을 탐색하고 상호 작용할 수 있나요? 어떻게 하나요?
## 서버에 데이터 제출하기
이제 기능 UI가 있으므로, 다음 단계는 데이터를 서버로 보내는 것입니다. 현재 코드를 사용하여 간단한 테스트를 해봅시다. *Login* 혹은 *Register* 버튼을 클릭하면 어떻게 되나요?
브라우저의 URL 섹션에서 변경된 것을 알고 있나요?
![Screenshot of the browser's URL change after clicking the Register button](.././images/click-register.png)
`<form>`의 기본 작업은 [GET method](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.3)를 사용하여 현재 서버 URL에 폼을 제출하고, 폼 데이터를 URL에 직접 추가하는 것입니다. 이 방식에는 몇 가지 단점이 있습니다:
- 전송되는 데이터는 크기가 매우 제한적입니다 (2000 자)
- 데이터가 URL에 직접 보입니다 (비밀번호에 적절하지 않습니다)
- 파일 업로드는 작동하지 않습니다
그러므로 아무런 제한없이 하려면, HTTP 요청 본문에서 폼 데이터를 서버로 보내는 [POST method](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5)를 사용하게 변경할 수 있습니다.
> POST는 데이터를 보낼 때 가장 일반적인 방식이지만, [some specific scenarios](https://www.w3.org/2001/tag/doc/whenToUseGet.html)에서 검색 필드를 구현할 때는, GET 방법을 사용하는 것이 더 좋습니다.
### 작업
가입 폼에 `action``method` 속성을 추가합니다:
```html
<form id="registerForm" action="//localhost:5000/api/accounts" method="POST">
```
이제 이름으로 새로운 계정을 가입합니다. *Register* 버튼을 클릭하면 다음과 같은 내용이 표시됩니다:
![](.././images/form-post.png)
모든 것이 잘 되면, 서버에 생성된 계정 데이터가 포함되어 [JSON](https://www.json.org/json-en.html)으로 응답해야 합니다.
✅ 같은 이름으로 다시 가입해보세요. 무슨 일이 일어났나요?
## 페이지를 다시 불러오지 않고 데이터 제출하기
알다시피, 사용한 접근 방식에는 약간 이슈가 있습니다: 폼을 제출할 때, 앱에서 나가면서 브라우저가 서버 URL로 리디렉션됩니다. [Single-page application (SPA)](https://en.wikipedia.org/wiki/Single-page_application)을 만들고 있으므로, 웹 앱으로 모든 페이지를 다시 불러오지 않으려 합니다.
페이지를 강제로 다시 불러오지 않고 폼 데이터를 서버로 보내려면, JavaScript 코드를 사용해야 합니다. `<form>` 요소의 `action` 속성에 URL을 넣는 대신, `javascript:` 문자열이 앞에 붙은 JavaScript 코드를 사용하여 커스텀 작업을 할 수 있습니다. 이를 사용하면 이전에 끝낸 브라우저로 자동 수행한 일부 작업을 구현해야 합니다:
- 폼 데이터 검색하기
- 폼 데이터를 알맞은 포맷으로 변환하고 인코딩하기
- HTTP 요청을 생성하고 서버로 전송하기
### 작업
가입 폼 `action`을 다음으로 바꿉니다:
```html
<form id="registerForm" action="javascript:register()">
```
`app.js` 열어서 `register`라고 지어진 새로운 함수를 추가합니다:
```js
function register() {
const registerForm = document.getElementById('registerForm');
const formData = new FormData(registerForm);
const data = Object.fromEntries(formData);
const jsonData = JSON.stringify(data);
}
```
여기서는 `getElementById()`를 사용하여 폼 요소를 검색하고, [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) 헬퍼를 사용하여 키/값 쌍 집합으로 폼 컨트롤에서 값을 추출합니다. 그러고 [`Object.fromEntries()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries)를 사용하여 데이터를 일반 객체로 변환하여 최종적으로 웹에서 데이터를 교환할 때, 일반적으로 사용되는 포맷인 [JSON](https://www.json.org/json-en.html)으로 데이터를 직렬화합니다.
데이터는 이제 서버에 보낼 준비가 되었습니다. `createAccount`라고 지은 새로운 함수를 만듭니다:
```js
async function createAccount(account) {
try {
const response = await fetch('//localhost:5000/api/accounts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: account
});
return await response.json();
} catch (error) {
return { error: error.message || 'Unknown error' };
}
}
```
이 함수는 어떤 일을 할까요? 먼저, 여기있는 `async` 키워드를 확인하세요. 이 함수는 [**asynchronously**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)하게 실행하는 코드가 포함되어 있다는 것을 의미합니다. `await` 키워드와 함께 사용하면, 비동기 코드가 실행될 때까지 기다릴 수 있습니다 - 여기에서 서버의 응답을 기다리는 것과 같습니다 - 계속하기 전에.
여기는 ``async/await` 사용 방식에 대한 간단한 영상입니다:
[![Async and Await for managing promises](https://img.youtube.com/vi/YwmlRkrxvkk/0.jpg)](https://youtube.com/watch?v=YwmlRkrxvkk "Async and Await for managing promises")
`fetch()` API를 사용하여 JSON 데이터를 서버로 보냅니다. 이 메소드는 2개의 파라미터가 사용됩니다:
- 서버의 URL이므로, 여기에 `//localhost:5000/api/accounts`를 다시 넣습니다.
- 요청의 설정입니다. 여기서 메소드를 `POST`로 설정하고 요청한 `body`를 줍니다. JSON 데이터를 서버로 보낼 때, `Content-Type` 헤더를 `application/json`으로 설정하여 서버가 인터프리터하는 방식을 알 수 있도록 합니다.
서버가 JSON으로 응답하므로, `await response.json()`을 사용하여 JSON 콘텐츠를 파싱하고 결과 객체를 반환할 수 있습니다. 이 메소드는 비동기이므로, 반환하기 전 여기에서 `await` 키워드를 사용하여 파싱하는 도중에도 오류가 발생하는지 확인합니다.
이제 `register` 함수에 코드를 추가하여 `createAccount()`를 호출합니다:
```js
const result = await createAccount(jsonData);
```
`await` 함수를 여기에서 사용하기 때문에, 가입 함수 전에 `async` 키워드를 추가해야 합니다:
```js
async function register() {
```
마지막으로, 결과를 보기 위해서 로그를 추가하겠습니다. 최종 함수은 다음과 같습니다:
```js
async function register() {
const registerForm = document.getElementById('registerForm');
const formData = new FormData(registerForm);
const jsonData = JSON.stringify(Object.fromEntries(formData));
const result = await createAccount(jsonData);
if (result.error) {
return console.log('An error occured:', result.error);
}
console.log('Account created!', result);
}
```
조금 길지만 도착했습니다! [browser developer tools](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_are_browser_developer_tools)를 열고, 새 계정을 가입하면, 웹 페이지에 변경 사항이 표시되지 않으면서 콘솔에 작동을 확인할 메시지가 나타납니다.
![Screenshot showing log message in the browser console](.././images/browser-console.png)
✅ 데이터가 안전하게 서버로 보내졌다고 생각하나요? 누군가 요청을 가져갈 수 있다면 어떤가요? 보안 데이터 통신에 대해 자세히 보려면 [HTTPS](https://en.wikipedia.org/wiki/HTTPS)를 읽어보세요.
## Data 검증하기
사용자 이름을 먼저 설정하지 않고 새 계정을 가입하려하면, 서버에서 상태 코드 [400 (Bad Request)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400#:~:text=The%20HyperText%20Transfer%20Protocol%20(HTTP,%2C%20or%20deceptive%20request%20routing).) 오류를 반환하는 것으로 볼 수 있습니다.
데이터를 서버로 보내기 전에 할 수 있다면, 유요한 요청을 보낼 수 있도록, 미리 [validate the form data](https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation)를 실습하는 것이 좋습니다. HTML5 포맷 컨트롤은 다양한 속성을 사용하여 빌트인 유효성 검사를 제공합니다:
- `required`: 필드를 채워야하며 안 채운다면 폼을 제출할 수 없습니다.
- `minlength``maxlength`: 텍스트 입력의 최소 및 최대 문자 수를 정의합니다.
- `min``max`: 숫자 필드의 최소값과 최대값을 정의합니다.
- `type`: `number`, `email`, `file` 또는 [other built-in types](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input)처럼, 예상되는 데이터의 종류를 정의합니다. 이 속성은 폼 컨트롤의 비주얼 렌더링을 바꿀 수도 있습니다.
- `pattern`: 입력된 데이터가 유효한지 테스트하기 위해 [regular expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) 패턴을 정의할 수 있습니다.
> Tip: 유효하거나 `:valid`와 `:invalid` CSS pseudo-classes를 사용하지 않는 여부에 따라 폼 컨트롤의 모양을 커스텀할 수 있습니다.
### 작업
유효한 새로운 계정을 만들 때에 username과 currency라는 2개의 필수 필드가 필요하며, 다른 필드는 옵션입니다. HTML에서 폼을 갱신하여 다음 사항을 반영합니다:
```html
<input name="user" type="text" required>
...
<input name="currency" type="text" value="$" required>
```
이 특정 서버을 구현하는 것은 필드의 최대 길이에 제한을 걸진 않지만, 항상 사용자 텍스트 항목에 대하여 적당한 제한을 두는 것이 좋습니다.
`maxlength` 속성을 이 텍스트 필드에 추가합니다:
```html
<input name="user" type="text" maxlength="20" required>
...
<input name="currency" type="text" value="$" maxlength="5" required>
...
<input name="description" type="text" maxlength="100">
```
이제 *Register* 버튼을 누르고 정의한 유효성 검사 규칙을 필드가 따르지 않는 경우에는, 다음과 같이 보입니다:
![Screenshot showing the validation error when trying to submit the form](./images/validation-error.png)
서버에 데이터를 보내기 *전에 하는* 유효성 검사를 **client-side** 유효성 검사라고 합니다. 그러나 데이터를 보내지 않고 모든 검사를 하는 것은 항상 가능하지 않습니다. 예를 들면, 서버에 요청을 보내지 않고 동일한 사용자 이름을 가진 계정이 이미 존재하는지 확인할 수 있는 방식은 없습니다. 서버에서 수행되는 추가적인 유효성 검사를 **server-side** 유효성 검사라고합니다.
일반적으로 모두 구현해야하며, 클라이언트-측 유효성 검사를 사용하면 사용자에게 즉시 피드백을 주고 사용자 경험도 향상되지만, 서버-측 유효성 검사도 바뀌는 사용자 데이터가 건전하고 안전한지 확인하려면 중요합니다.
---
## 🚀 도전
이미 사용자가 존재한다면 HTML 오류 메시지가 나옵니다.
다음은 살짝 스타일을 적용한 뒤에 최종 로그인 페이지를 보여주는 예시입니다:
![Screenshot of the login page after adding CSS styles](.././images/result.png)
## 강의 후 퀴즈
[Post-lecture quiz](../.github/post-lecture-quiz.md)
## 리뷰 & 자기주도 학습
개발자는 특히 유효성 검사 전략과 관련하여, 폼을 작성하는 노력에 대해 매우 창의적으로 생각했습니다. [CodePen](https://codepen.com)으로 다양한 폼 흐름에 대해 알아보세요; 흥미롭고 영감이 생기는 폼을 찾을 수 있나요?
## 과제
[Style your bank app](../assignment.md)

View File

@@ -0,0 +1,13 @@
# Diseña tu aplicación bancaria
## Instrucciones
Cree un nuevo archivo `styles.css` y agréguele un enlace en su archivo `index.html` actual. En el archivo CSS que acaba de crear, agregue algunos estilos para que la página *Inicio de sesión* y *Panel de control* se vea bien y ordenada. Intente crear un tema de color para darle a su aplicación su propia marca.
> Consejo: puede modificar el HTML y agregar nuevos elementos y clases si es necesario.
## Rúbrica
| Criterios | Ejemplar | Adecuado | Necesita mejorar |
| -------- | -------------------------------------------------- -------------------------------------------------- ------------------- | -------------------------------------------------- ---------------------------- | -------------------------------------------------- ------------------------------------------- |
| | Todas las páginas se ven limpias y legibles, con un tema de color consistente y las diferentes secciones destacando correctamente. | Las páginas tienen un estilo pero sin un tema o con secciones no claramente delimitadas. | Las páginas carecen de estilo, las secciones se ven desorganizadas y la información es difícil de leer. |