React AWS Amplify DataStore Tutorial
๋ชฉ์ฐจ
๐ DataStore with Amplify
Amplify DataStore๋ ์คํ๋ผ์ธ ๋ฐ ์จ๋ผ์ธ ์๋๋ฆฌ์ค์ ๋ํ ์ถ๊ฐ ์ฝ๋๋ฅผ ์์ฑํ์ง ์๊ณ ๊ณต์ ๋ฐ ๋ถ์ฐ ๋ฐ์ดํฐ๋ฅผ ํ์ฉํ ์ ์๋ ํ๋ก๊ทธ๋๋ฐ ๋ชจ๋ธ์ ์ ๊ณตํ๋ฏ๋ก ๋ถ์ฐํ ๊ต์ฐจ ์ฌ์ฉ์ ๋ฐ์ดํฐ๋ฅผ ๋ก์ปฌ ์ ์ฉ ๋ฐ์ดํฐ๋ก ์์
ํ๋ ๊ฒ๋งํผ ๊ฐ๋จํ๋ค.
๐ AWS ๊ณ์ *
- ํ์ฌ๋ก๋ถํฐ ์ง๊ธ ๋ฐ์ AWS ๊ณ์ ์ฌ์ฉ
- two factor ์ค์
- https://www.notion.so/source-tree-2FA-0b00df2002794e5eb4a349855fa98397
- ์ญํ ์ ํ ํ๊ธฐ
- https://www.notion.so/AWS-5a41e697e30f4c53801d1b37ae633f82
๐ Amplify CLI ์ค์น ๋ฐ ๊ตฌ์ฑ*
Amplify CLI๋ ์ฑ์ฉ AWS ํด๋ผ์ฐ๋ ์๋น์ค๋ฅผ ๋ง๋ค๊ธฐ ์ํ ํตํฉ ํด์ฒด์ธ์ด๋ค.
ํ๋จ์ ๋ฐฉ๋ฒ์ ๊ฐ์ด๋ ์์์ ๋ณด๊ณ ์ง์ ์ค๋ช ์๋ฅผ ์์ฑํ๋ค.
๊ธ๋ก๋ฒ๋ก ์ด๊ฒ์ ์ค์นํ๋ค.
๊ฒฝ๋ก : ์๊ด ์์
๋ง์ฝ ์ค์น๊ฐ ์๋๋ฉด ์์ ‘sudo’๋ฅผ ๋ถ์ธ๋ค.
npm install -g @aws-amplify/cli
sudo npm install -g @aws-amplify/cli
๐ Amplify ํ๊ฒฝ์ค์
ํ๋ฒ๋ง ํ๋ฉด๋๊ธฐ ๋๋ฌธ์.. ๋ง์ฝ ์ด์ ์ ํ์ ์ด ์๋ค๋ฉด ๋์ด๊ฐ๋ค.
๊ฒฝ๋ก : ์๊ด ์์
amplify configure
1. ์ ๋ ฅํ๋ฉด aws console์ฐฝ์ด ์น ๋ธ๋ผ์ฐ์ ์ ๋ก๋๋๋๋ฐ ๋ก๊ทธ์ธ์ ํ๊ณ ์ญํ ์ ์ ํํ๋ค.
(ํ์ฌ ๊ณ์ ์ด ์๋๋ผ๋ฉด ์ญํ ์ ํํ ํ์๊ฐ ์๋ค)
2. ๋ก๊ทธ์ธ์ ํ๊ณ ํฐ๋ฏธ๋์ฐฝ์ผ๋ก ๋ค์ ๋์์์ Enter๋ฅผ ๋๋ฌ ์ด์ด๊ฐ๋ค.
3. ์ง์ญ์ ์ ํํ๋ผ๊ณ ๋์จ๋ค.
- AWS ์น ๋ธ๋ผ์ฐ์ ์์ ์ง์ญ์ ํ์ธํ๋ค.
- ์ ํํ๋ค
4. user name์ ์ ๋ ฅํ๋ ๋ฉ์ธ์ง๊ฐ ์ถ๋ ฅ๋๋๋ฐ ์ด๋ฆ์ ์ ๋ ฅํ๋ค.
- ์ ๋ ฅํ๊ฒ๋๋ฉด ๋ธ๋ผ์ฐ์ ์ ์ฌ์ฉ์๋ฅผ ์ถ๊ฐํ๋ ํ์ด์ง๊ฐ ์ด๋ฆฌ๊ฒ๋๋ค.
# example
dave
5. ์ด๋ฆฐ ํ์ด์ง์์ ‘๋ค์:๊ถํ’ ๋ฒํผ์ ํด๋ฆญํ๋ค.
6. ‘๋ค์:ํ๊ทธ’ ๋ฒํผ์ ํด๋ฆญํ๋ค.
7. ‘๋ค์:๊ฒํ ’ ๋ฒํผ์ ํด๋ฆญํ๋ค.
8. ‘์ฌ์ฉ์ ๋ง๋ค๊ธฐ’ ๋ฒํผ์ ํด๋ฆญํ๋ค.
9. ์ก์ธ์ค ํค ID๋ฅผ ๋ณต์ฌํ๋ค.
10. ํฐ๋ฏธ๋ ์ฐฝ์ผ๋ก ๋์์จ๋ค.
- Enterํค๋ฅผ ๋๋ฌ ์งํํ๋ค.
11. accessKeyId๋ฅผ ์ ๋ ฅํ๋ผ๊ณ ํ๋ค
- ๋ณต์ฌํ ID๋ฅผ ๋ถ์ฌ๋ฃ๊ณ Enter๋ฅผ ๋๋ฅธ๋ค.
12. secretAccessKey๋ฅผ ์ ๋ ฅํ๋ผ๊ณ ๋์จ๋ค.
- ๋ค์ ๋ธ๋ผ์ฐ์ ๋ก ๋์๊ฐ์ ๋ณต์ฌํด์ ๋์์จ๋ค.
- ํ์๋ฅผ ๋๋ฅด๋ฉด ๋น๋ฐ๋ฒํธ๊ฐ ํ์๋๋ค.
13. ๋ถ์ฌ๋ฃ๊ธฐ๋ฅผ ํ๊ณ Enter๋ฅผ ๋๋ฅธ๋ค.
14. profile Name๋ฅผ ์ ๋ ฅํ๊ณ enter๋ฅผ ๋๋ฅด๋ฉด ์๋ฃ
# example
dave
๐ ๋ก์ปฌ ๊ฐ๋ฐ ํ๊ฒฝ ์ค์
1. Amplify ์ด๋์ ๋ผ์ด์ฆ๋ฅผ ํ๋ค.
amplify init
? Enter a name for the project
The following configuration will be applied:
?Project information
| Name: OJTFrontend
| Environment: dev
| Default editor: Visual Studio Code
| App type: javascript
| Javascript framework: react
| Source Directory Path: src
| Distribution Directory Path: build
| Build Command: npm run-script build
| Start Command: npm run-script start
? Initialize the project with the above configuration? Yes
Using default provider awscloudformation
? Select the authentication method you want to use: AWS profile
...
? Please choose the profile you want to use default
2. Amplify ์ฑ์ ์์ฑํ๋ค.
- ๊ฒฝ๋ก : ํ๋ก์ ํธ ๋๋ ํ ๋ฆฌ
- ์๋ฌ๊ฐ ๋ฐ์ํ๋ค๋ฉด ํ๋จ ๊ธ์ ์ฐธ๊ณ ํ๋ค.
https://conqueror-g.tistory.com/238
npx amplify-app
3. ํด๋น ํจํค์ง๋ค์ ์ค์นํ๋ค.
npm i aws-amplify @aws-amplify/core @aws-amplify/datastore
4. api ๋ฅผ ์ถ๊ฐํด์ผํ๋ค.
- ์ต์ด 1๋ฒ๋ง ์ง์ ํ๋ฉด ์ดํ์ ํ ํ์๊ฐ ์๋ค.
amplify add api
? Please select from one of the below mentioned services:
`GraphQL`
? Here is the GraphQL API that we will create. Select a setting to edit or continue:
`Name`
? Provide API name:
`BlogAppApi`
? Here is the GraphQL API that we will create. Select a setting to edit or continue:
`Authorization modes: API key (default, expiration time: 7 days from now)`
? Choose the default authorization type for the API
`API key`
? Enter a description for the API key:
`BlogAPIKey`
? After how many days from now the API key should expire (1-365):
`365`
? Configure additional auth types?
`No`
? Here is the GraphQL API that we will create. Select a setting to edit or continue:
`Conflict detection (required for DataStore): Disabled`
? Enable conflict detection?
`Yes`
? Select the default resolution strategy
`Auto Merge`
? Here is the GraphQL API that we will create. Select a setting to edit or continue:
`Continue`
? Choose a schema template
`Single object with fields (e.g., “Todo” with ID, name, description)`
? Do you want to edit the schema now? (Y/n)
'Y'
5. ํด๋น ๊ฒฝ๋ก์ ํ์ผ์์ ์ธ์คํด์ค๋ก ์์ฑํ (ํ์ ? ํํ?)๋ฅผ ์์ฑํ๋ค.
- ๊ฒฝ๋ก : /amplify/backend/api/schema.graphql
type DiaryPost @model {
id: ID!
title: String!
content: String!
}
! : ํญ์ ๊ฐ์ ์ ๊ณตํ๋ค๋ ์๋ฏธ
6. ๋ฐฑ์๋๋ฅผ ๋ฐฐํฌํ๋ค.
amplify push
? Are you sure you want to continue? (Y/n)
$ Y
? Do you want to generate code for your newly created GraphQL API (Y/n)
(์๋ก ๋ง๋ GraphQL API์ ๋ํ ์ฝ๋๋ฅผ ์์ฑํ์๊ฒ ์ต๋๊น?)
$ n
7. ํ๊ธฐ ์ฝ๋๋ฅผ ํฐ๋ฏธ๋์ ์ ๋ ฅํ๋ค.
- graphQL schma.grapql ๋ด์ฉ์ ์์ ํ๋ฉด ๋ค์ ํด์ฃผ์ด์ผํ๋ค.
amplify codegen model
8. ํด๋ผ์ด์ธํธ์์ Amplify๋ฅผ ๊ตฌ์ฑํ์ฌ ๋ฐฑ์๋ ์๋น์ค์ ์ํธ ์์ฉํ ์ ์๋๋ก ํด์ผ ํ๋ค.
- ๊ฒฝ๋ก : src/index.js
import Amplify from '@aws-amplify/core'
import config from './aws-exports'
Amplify.configure(config)
9. ํ๊ธฐ ์์ ์ฝ๋๋ฅผ ์์ฑํด์ ๊ฒฐ๊ณผ๋ฅผ ํ์ธํ๋ค.
- ์์ธํ ์ค๋ช ์ ์์ ์ฝ๋ ํ๋จ์ ๋ง ๋ถ์ธ๋ค.
import React, { useState, useEffect } from 'react';
import logo from './logo.svg';
import './App.css';
import { DataStore } from '@aws-amplify/datastore/lib-esm/datastore/datastore';
import { DiaryPost } from './models';
function App() {
const [messages, updateMessages] = useState([]);
useEffect(() => {
fetchMessages();
const subscription = DataStore.observe(DiaryPost).subscribe(() => {
fetchMessages();
});
return () => subscription.unsubscribe();
}, []);
const fetchMessages = async () => {
const messages = await DataStore.query(DiaryPost);
updateMessages(messages);
};
const createMessage = async () => {
await DataStore.save(
new DiaryPost({
...{
title: 'hi there~',
content: 'nothing',
},
})
);
};
const updateMessage = async () => {
// ์์๋ก 0๋ฒ์จฐ ์ธ๋ฑ์ค์ ๋ฐ์ดํฐ๋ฅผ ์์ ํ๋๋ก ํด๋์ต๋๋ค.
const original = await DataStore.query(DiaryPost, messages[0].id);
await DataStore.save(
DiaryPost.copyOf(original, updated => {
// eslint-disable-next-line no-unused-expressions
(updated.title = 'good'), (updated.content = 'content');
})
);
};
const deleteMessage = async () => {
// ์์๋ก 0๋ฒ์จฐ ์ธ๋ฑ์ค์ ๋ฐ์ดํฐ๋ฅผ ์ญ์ ํ๋๋ก ํด๋์ต๋๋ค.
const deleteMessage = await DataStore.query(DiaryPost, messages[0].id);
await DataStore.delete(deleteMessage);
};
return (
<div className='App'>
<header className='App-header'>
<img src={logo} className='App-logo' alt='logo' />
<div>
<input type='button' value='NEW' onClick={createMessage} />
<input type='button' value='UPDATE' onClick={updateMessage} />
<input type='button' value='DELETE' onClick={deleteMessage} />
</div>
{messages.map(data => {
return (
<div key={data.id}>
<div>{data.title}</div>
<div>{data.content}</div>
</div>
);
})}
</header>
</div>
);
}
export default App;
์ถ๊ฐ ์ค๋ช
- ๋ฐ์ดํฐ์คํ ์ด์ ๋ฐ์ดํฐ๋ฅผ ๋ ๋๋ง ํด์ฃผ๋ ์ฝ๋ ์ค์ํ์ง ๋ชฐ๋์ผ๋,
์ด ์ฝ๋๊ฐ ์์ผ๋ฉด ๋ฐ์ดํฐ์คํ ์ด์ ๋ฐ์ดํฐ๋ฅผ ํญ์ ๋ฐ์ํ์ง ์๋๋ค.
๋ฆฌ์กํธ์ ๋ ๋๋ง๊ณผ๋ ๋ณ๊ฐ๋ผ๊ณ ๋ณด๋ฉด ๋๋ค.
๋ฐ์ดํฐ์คํ ์ด์ ๋ฐ์ดํฐ๊ฐ ๋ณ๊ฒฝ๋๊ฑฐ๋ ์ญ์ ๋ ๋๋ง๋ค fetch๋ฅผ ํ๋ค.
useEffect์ ์์กด์ฑ ๋ฐฐ์ด์ด ๋น์นธ์ธ ๊ฒ๊ณผ๋ ๊ด๊ณ๊ฐ ์๋ค.
useEffect(() => {
fetchMessages();
// fetchMessages๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ query์ด๋ค.
const subscription = DataStore.observe(DiaryPost).subscribe(() => {
fetchMessages();
});
return () => subscription.unsubscribe();
}, []);
const fetchMessages = async () => {
const messages = await DataStore.query(DiaryPost);
updateMessages(messages);
};
- ๋ฐ์ดํฐ์คํ ์ด์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ ์ฝ๋์ด๋ค.
์ด๊ฒ์ ๋ํด์๋ ํฌ๊ฒ ์ค๋ช
ํ ํ์๊ฐ ์๋ค๊ณ ์๊ฐํ๋ค.
’DiaryPost’๋ graphql ๋ชจ๋ธ ์ด๋ฆ์ด๋ค.
const createMessage = async () => {
await DataStore.save(
new DiaryPost({
...{
title: 'hi there~',
content: 'nothing',
},
})
);
};
- ๋ฐ์ดํฐ์คํ ์ด์ ๋ฐ์ดํฐ๋ฅผ ์
๋ฐ์ดํธ ํ๋ค.
๋ชจ๋ธ๊ณผ ๋ฐ๊พธ๊ณ ์ํ๋ ๋ฐ์ดํฐ์ id๋ฅผ original์ด๋ผ๋ ๋ณ์์ ๋ด๊ณ , ๋ฐ๊พธ๊ณ ์ ํ๋ ๋ด์ฉ์ ์ ์กํ๋ค.
const updateMessage = async () => {
// ์์๋ก 0๋ฒ์จฐ ์ธ๋ฑ์ค์ ๋ฐ์ดํฐ๋ฅผ ์์ ํ๋๋ก ํด๋๋ค.
const original = await DataStore.query(DiaryPost, messages[0].id);
await DataStore.save(
DiaryPost.copyOf(original, updated => {
// eslint-disable-next-line no-unused-expressions
(updated.title = 'good'), (updated.content = 'content');
})
);
};
- ๋ฐ์ดํฐ์คํ ์ด์ ๋ฐ์ดํฐ๋ฅผ ์ญ์ ํ๋ค. ์ญ์ ํ๊ณ ์ ํ๋ id๋ฅผ ๋ณด๋ด๋ฉด ์ญ์ ๊ฐ ์๋ฃ๋๋ค.
const deleteMessage = async () => {
// ์์๋ก 0๋ฒ์จฐ ์ธ๋ฑ์ค์ ๋ฐ์ดํฐ๋ฅผ ์ญ์ ํ๋๋ก ํด๋์ต๋๋ค.
const deleteMessage = await DataStore.query(DiaryPost, messages[0].id);
await DataStore.delete(deleteMessage);
};ใ
์ด๋ ๊ฒ ๋ฐฑ์ค๋ ๊ฐ๋ฐ์๋ผ๋ ๋๋ฃ ์์ด CRUD์ ๋ํ ๊ธฐ๋ฅ์ ์ํํ ์ ์๋ ์๋น์ค๋ฅผ ์ฐ๊ฒฐ์์ผ๋ณด์๋ค.
Amplify DataStore ๋.. ์ ๋ง ๊ฐ์ ์ด์์ด.. 3์ผ์ ํด๋งธ๋ค
