Svelte 전면을 AWS 후면과 연결해 보십시오
개시하다
이전에는 Svelte를 할 때 ① TODO 애플리케이션(프런트만 있음)을 만들었고, 이후 AWS Amplify에서 시작한 뒤 ② Amplify로 TODO 백엔드를 만들었는데, 여기에 도착하면 ①와 ②를 스트리밍으로 연결해보자.그런 거야.
①와 ②의 기사는 다음과 같다.
완성하다
죄송합니다. 완성된 프레젠테이션 화면입니다.
구성은 이런 느낌.
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Frontend │ │ APIGW │ │ Lambda │ │ │
│ (Svelte) ├─────┤► (REST) ├──────► with ├─────► DynamoDB │
│ │ │ │ │ RubySDK │ │ │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
코드
Svelte
마지막으로 프런트엔드에서만 이동한 버전으로, API 호출로 변경됨
Todo.svelte
<script>
const baseURL = 'https://<api-id>.execute-api.<region>.amazonaws.com/dev/tasks'
let taskName = ""
let checked = false
let modalOn = false
// sort
function compare( a, b ) {
console.log('compare called')
if ( a['updated-at'] > b['updated-at'] ){ return -1; }
if ( a['updated-at'] < b['updated-at'] ){ return 1; }
return 0;
}
function sortByCreatedAt(todos){
console.log('sortBy called')
todos.then(data => {
data = data.sort(compare);
});
return todos
}
function statusText(taskStatus){ return taskStatus ? "Active" : "Done" }
const listTasks = async () => {
console.log('listTasks called')
const res = await fetch(baseURL);
return await sortByCreatedAt(res.json())
}
const getTask = async (taskId) => {
console.log('getTask called')
const task = await fetch(baseURL + '/' +taskId);
return await task.json()
}
const addTask = async (taskName) => {
console.log('addTask called!')
let taskData = {
"task-id": Date.now().toString(36),
"is-active": true,
"task-name": "",
"updated-at": Date.now().toString(),
"created-at": Date.now().toString(),
"user-id": "100"
}
taskData['task-name'] = taskName;
const res = await fetch(baseURL, {
method: 'POST',
body: JSON.stringify(taskData)
});
todos = listTasks()
}
const deleteTask = async (taskId) => {
console.log('deleteTask called!')
const res = await fetch(baseURL + '/' + taskId, {
method: 'DELETE',
});
todos = listTasks() // assignment to ignite the reactivity
}
const completeTask = async (taskId, status) => {
const res = await fetch(baseURL + '/' + taskId, {
method: 'PUT',
body: JSON.stringify({ 'task-id': taskId, 'is-active': !status })
});
todos = listTasks()
}
let taskDetail = ''
function showDetail(taskId) {
console.log('showDetail called')
modalOn = true
//return getTask(taskId)
taskDetail = getTask(taskId)
}
// format date
function fd(date) {
console.log('fd called')
let d = new Date(parseInt(date))
return d.getFullYear() + '/' + d.getMonth() + '/' + d.getDate() + ' ' + d.getHours() + ':' + d.getMinutes()
}
// todos
let todos = listTasks(); // Initialized as Promise
$: activeTodos = checked ? todos : todos.then(data => data.filter(todo => todo['is-active']))
</script>
<main>
<h1>HELLO TODO!</h1>
<div class="container is-fluid">
<div class="columns is-centered">
<div class="field has-addons">
<div class="control" style="margin-bottom: 5em;">
<input class="input" type="text" placeholder="Add a task" bind:value={taskName}>
</div>
<div class="control">
<a class="button is-danger" on:click={() => addTask(taskName)}><i class="fas fa-plus"></i></a>
</div>
</div>
</div>
<div class="columns" style="width: 200px; margin-left: 60%; margin-bottom: 3em">
<div class="field">
<input id="switchRoundedDanger" type="checkbox" name="switchRoundedDanger" class="switch is-rounded is-danger is-rtl" bind:checked={checked}>
<label for="switchRoundedDanger">Show completed</label>
</div>
</div>
<div class="columns is-centered">
{#await activeTodos}
<span class="spinner-loader" style="margin-top: 10em">Loading…</span>
{:then tasks}
<table class="table is-hoverable">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Created at</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{#each tasks as task}
<tr>
<td>{task['task-id']}</td>
<td>{task['task-name']}</td>
<td>{fd(task['created-at'])}</td>
<td>{statusText(task['is-active'])}</td>
<td>
<i class="fas fa-check" on:click={() => completeTask(task['task-id'], task['is-active'])}></i>
<i class="fas fa-info" on:click={() => showDetail(task['task-id'])}></i>
<i class="far fa-trash-alt" on:click={() => deleteTask(task['task-id'])}></i>
</td>
</tr>
{/each}
</tbody>
</table>
{/await}
</div>
<div class="modal { modalOn ? 'is-active' : ''}">
<div class="modal-background"></div>
<div class="modal-content">
<dev class="box">
{#await taskDetail}
<span class="spinner-loader" style="margin-top: 10em">Loading…</span>
{:then td}
<table class="table is-fullwidth">
<thead>
<tr><td>key</td><td>value</td></tr>
</thead>
<tbody>
<tr><td>task-id</td><td><span class="tag">{td['task-id']}</span></td></tr>
<tr><td>task-name</td><td><span class="tag">{td['task-name']}</span></td></tr>
<tr><td>created-at</td><td><span class="tag">{fd(td['created-at'])}</span></td></tr>
<tr><td>updated-at</td><td><span class="tag">{fd(td['updated-at'])}</span></td></tr>
<tr><td>is-active</td><td><span class="tag">{td['is-active']}</span></td></tr>
<tr><td>user-id</td><td><span class="tag">{td['user-id']}</span></td></tr>
</tbody>
</table>
{/await}
</dev>
</div>
<button class="modal-close is-large" aria-label="close" on:click="{() => modalOn = false}"></button>
</div>
</div>
</main>
<style>
main {
text-align: center;
padding: 1em;
max-width: 240px;
}
h1 {
color: #ff3e00;
text-transform: uppercase;
font-size: 4em;
font-weight: 100;
margin-bottom: 1.5em;
}
td i {
padding: 5px;
cursor: pointer;
color: gray;
}
label { font-family: monospace; }
table { font-family: monospace; }
@media (min-width: 640px) {
main {
max-width: none;
}
}
</style>
Lambda
이전 ②의 메일박스 내용과 거의 같다...
lambda_handler.rb
require 'json'
require 'json/add/exception'
require 'aws-sdk-dynamodb'
CORS_HEADER = {
"Access-Control-Allow-Headers": "Content-Type",
"Access-Control-Allow-Origin": '*',
"Access-Control-Allow-Methods": "OPTIONS,POST,PUT,GET"
}
def add_task(table, body)
begin
table.put_item({ item: body })
{ statusCode: 200, headers: CORS_HEADER, body: JSON.generate(body) }
rescue => e
{ statusCode: 500, body: e.to_json }
end
end
def delete_task(table, task_id)
begin
params = { table_name: table, key: { 'task-id': task_id } }
table.delete_item(params)
list_task(table)
rescue => e
{ statusCode: 500, body: e.to_json }
end
end
def update_task(table, body)
begin
params = {
table_name: table,
key: { 'task-id': body['task-id'] },
attribute_updates: {
'is-active': { value: body['is-active'], action: "PUT" },
}
}
table.update_item(params)
{ statusCode: 200, headers: CORS_HEADER, body: JSON.generate(body) }
rescue => e
{ statusCode: 500, headers: CORS_HEADER, body: e.to_json }
end
end
def list_task(table)
begin
scan_output = table.scan({ limit: 50, select: "ALL_ATTRIBUTES" })
{ statusCode: 200, headers: CORS_HEADER, body: JSON.generate(scan_output['items']) }
rescue => e
{ statusCode: 500, body: e.to_json }
end
end
def get_task(table, task_id)
begin
params = { key: { 'task-id': task_id } }
task = table.get_item(params)
{ statusCode: 200, headers: CORS_HEADER, body: JSON.generate(task['item']) }
rescue => e
{ statusCode: 500, body: e.to_json }
end
end
def lambda_handler(event:, context:)
begin
http_method = event['httpMethod']
dynamodb = Aws::DynamoDB::Resource.new(region: 'us-east-2')
table = dynamodb.table('todoTable-dev')
case http_method
when 'GET'
path_param = event.dig('pathParameters', 'proxy')
if path_param.nil?
list_task(table)
else
get_task(table, path_param)
end
when 'PUT' then update_task(table, JSON.parse(event['body']))
when 'POST' then result = add_task(table, JSON.parse(event['body']))
when 'DELETE' then delete_task(table, event['pathParameters']['proxy'])
else 0
end
rescue => e
{ statusCode: 500, body: e.to_json }
end
end
학습적인 것, 혹은 단지 감상일 뿐이다
토도 앱이라고 만만치 않게 볼 수는 없다.특히 새로운 틀의 작업을 배워야 한다
Svelte 편
1. Await Block은 변태지만 느낌이 좋아요.
처음 보시면 오, 거푸집
await
으로 만드셨나요!이렇게 생각하는 작법은 괜찮다. <h1>Task list</h1>
{#await todos}
<p>Loading...</p>
{:then tasks}
{#each tasks as task}
<p>{task['task-id']}, {task['task-name']}</p>
{/each}
{/await}
2.on: 클릭을 통해 발화 함수에 매개 변수를 전달하는 방법
매개 변수 함수 없이 호출하면 돼요.
<button on:click="{listTask}"></button>
매개 변수가 있다면 이렇게 쓰면 클릭할 때가 아니라 바로 실행합니다대신
<button on:click="{getTask(taskId)}"></button>
3. 한 동작에 두 함수를 호출할 수 없다
베이처럼 흠뻑 젖을 줄 알았는데 안 될 줄 몰랐어요.응, 앞에 있는 함수에서 뒤에 있는 함수라고 부르면 돼.
<button on:click="{() => getTask(taskId)}"></button>
4. selected 등 단축키 사용
상자와 모형을 열 때
selected
등class
. <button on:click="{listTask; getTask}"></button>
이렇게 쓰여있어요.<style>
/* ...other CSS... */
span.cell.selected {
outline-color: lightblue;
outline-style: dotted;
}
</style>
<span class="cell {selected === true ? 'selected' : ''}">
{value}
</span>
더 나아가 단락수로 이렇게 한다<span class="cell" class:selected="{selected}">
{value}
</span>
너무 좋아요.하지만 클라스 이름에 하이픈을 사용하면 움직이지 않아 곤란하다.출처는 다음과 같다.
CORS 편
1.fetch의 no-cors는 만능이 아니다.PUT이면 가만히 있어요.
CORS 주변 사람들은 한번 빠지면 속상해해요.이럴 때
no-cors
꼭 도망갈 수 있는 건 아니다.2. 오류 메시지에 CORS 헤드를 추가하면 디버깅이 용이합니다.
APIGW
500
의 경우 동일하게 사용 가능JavaScript 편
1. JS의 데이트는 여전히 고통스럽다
쓰기 싫다
2. Promise 탈출 불가
안에 있는 것만 원하고, 안에 있는 것만 말하려고 아무리 노력해도 보답이 있을 거야
Promise
.반드시 관계를 잘 해야 한다Loading CSS에서 이 편집 사용
이런 작은 곳에서는 분위기가 변하기 때문에 조금만 만들어도 동력이 오래 간다.
DynamoDB 편
1. 정렬은 검색에서 하는 것이 아니라 책상에 정렬 키를 추가합니다...
Firebase였으면 좋겠는데...
2. 정렬 키를 잊어버리면 테이블 다시 만들기
DynamoDB doesn't allow to add a sort key for an already existing table. Your only option is to create a new table with redefined key attributes and then copy the data from the existing table to the newly created table.
정말?무대 위에서 순서를 정했다.
3. :return_values 조금 더
업데이트 시스템을 처리할 때 수치는 돌려주지만 업데이트 전 값
ALL_OLD
같은 거라서 잘 안 돼요.당신은 어떤 응용 프로그램을 구상하고 있습니까?람바다 편
처음에는 고객이 편하다고 생각했는데 람바다 쪽에서 포스터와 퓨트를 받으면 업데이트된 미션인 크리스토퍼 GET를 돌려보내지만 포기하는 게 좋을 것 같다.각 요구 사항은 모두 Atomic이므로 프런트부터 조작하는 것이 그리 번거롭지 않다
수고하셨습니다.
Reference
이 문제에 관하여(Svelte 전면을 AWS 후면과 연결해 보십시오), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://zenn.dev/masaino/articles/a3b0eb46fd54f0텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)