diff --git "a/keyword/chapter05/images/\354\213\244\354\212\265-1-\354\244\221\353\263\265\353\220\234_\355\232\214\354\233\220.png" "b/keyword/chapter05/images/\354\213\244\354\212\265-1-\354\244\221\353\263\265\353\220\234_\355\232\214\354\233\220.png"
new file mode 100644
index 0000000..501cb97
Binary files /dev/null and "b/keyword/chapter05/images/\354\213\244\354\212\265-1-\354\244\221\353\263\265\353\220\234_\355\232\214\354\233\220.png" differ
diff --git "a/keyword/chapter05/images/\354\213\244\354\212\265-1-\355\232\214\354\233\220_\353\223\261\353\241\235.png" "b/keyword/chapter05/images/\354\213\244\354\212\265-1-\355\232\214\354\233\220_\353\223\261\353\241\235.png"
new file mode 100644
index 0000000..98573b5
Binary files /dev/null and "b/keyword/chapter05/images/\354\213\244\354\212\265-1-\355\232\214\354\233\220_\353\223\261\353\241\235.png" differ
diff --git a/keyword/chapter05/keyword.md b/keyword/chapter05/keyword.md
new file mode 100644
index 0000000..0ed6af2
--- /dev/null
+++ b/keyword/chapter05/keyword.md
@@ -0,0 +1,383 @@
+### **๐ฆ ์ค์ต**
+---
+1. ํ์๊ฐ์
API
+ - Repository ํจ์
+ ```javascript
+ // ํ์ ๋ฐ์ดํฐ ์ฝ์
(ํ์ ๋ฑ๋ก) & ํ์ ID ๋ฐํ
+ export const addMember = async(data) => {
+ const conn = await pool.getConnection(); // ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ
+ try{
+ // ํด๋น ์ด๋ฉ์ผ(์ค๋ณต๋ ์ด๋ฉ์ผ)์ ์ฌ์ฉ์๊ฐ ์๋์ง ํ์ธ
+ const [confirm] = await pool.query( // ์ฟผ๋ฆฌ ์คํ
+ `SELECT EXISTS(SELECT 1 FROM member WHERE email = ?) as isExistEmail;`,
+ data.email
+ );
+ if (confirm[0].isExistEmail) { // ์ด๋ฏธ ๋ฑ๋ก๋ ์ด๋ฉ์ผ์ผ ๊ฒฝ์ฐ
+ return null;
+ }
+ // ์ค๋ณต๋ ์ด๋ฉ์ผ์ด ์๋ ๊ฒฝ์ฐ, ํ์ ์ ๋ณด member ํ
์ด๋ธ์ ์ฝ์
+ const [result] = await pool.query(
+ `INSERT INTO member (member_name, nickname, gender, birth, location_address, email, phone_number) VALUES (?, ?, ?, ?, ?, ?, ?);`,
+ [
+ data.name,
+ data.nickname,
+ data.gender,
+ data.birth,
+ data.location,
+ data.email,
+ data.phoneNumber,
+ ] // ์ ๋ณด ์ฝ์
, ๋ค๋ฅธ ์์ฑ๋ค์ default ๊ฐ์ ์ง์ ํด์ฃผ์๋ค.
+ );
+ return result.insertId; // ์์ฑ๋ ํ์์ ID ๋ฐํ
+ // insertId - DB์ ์๋ก์ด ๋ ์ฝ๋ ์ฝ์
+ // ์ ๋ ์ฝ๋๊ฐ ์ฝ์
๋ ๋ ํด๋น ๋ ์ฝ๋์ ์๋ ์ฆ๊ฐ ID ๊ฐ์ ๋ฐํํ๋ค.
+ } catch(err) { // ์ค๋ฅ ์ฒ๋ฆฌ
+ throw new Error(`
+ ๐ซ ์ค๋ฅ ๋ฐ์ ๐ซ
+ ์์ฒญ ํ๋ผ๋ฏธํฐ ํ์ธ ๋ฐ๋ (${err})
+ `);
+ } finally {
+ conn.release(); // DB ์ฐ๊ฒฐ ํด์ง
+ }
+ };
+
+ // ํ์ ์ ๋ณด ์กฐํ
+ export const getMember = async (memberId) => {
+ const conn = await pool.getConnection();
+ try{
+ // ํ์ ID๋ก ์กฐํํด์ ํด๋น ํ์ ์ ๋ณด๋ฅผ ๋ชจ๋ ์กฐํํ๋ค.
+ const [member] = await pool.query(
+ `SELECT * FROM member WHERE id = ?;`,
+ memberId
+ )
+ console.log(member);
+ if (member.length == 0){ // ์กฐํ๋ ํ์์ด ์์ ๊ฒฝ์ฐ
+ return null;
+ }
+ return member;
+ }catch (err){
+ throw new Error(`
+ ๐ซ ์ค๋ฅ ๋ฐ์ ๐ซ
+ ์์ฒญ ํ๋ผ๋ฏธํฐ ํ์ธ ๋ฐ๋ (${err})
+ `);
+ }finally{
+ conn.release();
+ }
+ };
+
+ // ์์ - ์ ํธ_์์_์ข
๋ฅ ๋งคํ
+ export const setFavoriteFoodKind = async(memberId, favoriteFoodKindId) => {
+ const conn = await pool.getConnection();
+ try{
+ await pool.query( // ํ์-์์_์ข
๋ฅ ๋งคํ ํ
์ด๋ธ์ ํ์ ID, ์์_์ข
๋ฅ ID๋ฅผ ์ฝ์
ํจ์ผ๋ก์จ ๋ ๊ด๊ณ๋ฅผ ์ด์ด์ค๋ค.
+ `INSERT INTO member_food_kind (food_kind_id, member_id) VALUES (?, ?);`,
+ [favoriteFoodKindId, memberId]
+ );
+ return;
+ } catch(err){
+ throw new Error(`
+ ๐ซ ์ค๋ฅ ๋ฐ์ ๐ซ
+ ์์ฒญ ํ๋ผ๋ฏธํฐ ํ์ธ ๋ฐ๋ (${err})
+ `);
+ } finally{
+ conn.release();
+ }
+ };
+
+ // ํ์ - ์ ํธ_์์_์ข
๋ฅ ๋ฐํ
+ export const getMemberFavoriteFoodKindByMemberId = async (memberId) => {
+ const conn = await pool.getConnection();
+ try{
+ const [favoriteFoodKinds] = await pool.query(`
+ SELECT mfk.id, mfk.food_kind_id, mfk.member_id, fk.kind
+ FROM member_food_kind mfk JOIN food_kind fk ON mfk.food_kind_id = fk.id
+ WHERE mfk.member_id = ?
+ ORDER BY mfk.food_kind_id ASC`,
+ memberId
+ ); // member ํ
์ด๋ธ๊ณผ food_kind ํ
์ด๋ธ์ joinํด ํด๋น ํ์์ ์ ํธ ์์ ์ข
๋ฅ์ ๋ํ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์จ๋ค.
+ return favoriteFoodKinds;
+ }catch(err){
+ throw new Error(`
+ ๐ซ ์ค๋ฅ ๋ฐ์ ๐ซ
+ ์์ฒญ ํ๋ผ๋ฏธํฐ ํ์ธ ๋ฐ๋ (${err})
+ `);
+ }finally{
+ conn.release();
+ }
+ }
+ ```
+ - ํ
์คํธ ๊ฒฐ๊ณผ
+ ![์ค์ต-1-ํ์๋ฑ๋ก](images/์ค์ต-1-ํ์_๋ฑ๋ก.png)
+ - ๋์ผํ ์ด๋ฉ์ผ๋ก ํ์๊ฐ์
ํ๋ ๊ฒฝ์ฐ
+ ![images/์ค์ต-1-์ค๋ณต๋_ํ์](images/์ค์ต-1-์ค๋ณต๋_ํ์.png)
+ - ํ์ ์ด๋ฉ์ผ๋ก ์กฐํํ์ ๋ ์ด๋ฏธ ๋ฐ์ดํฐ๊ฐ ์กด์ฌํ ๊ฒฝ์ฐ ์๋ฌ๋ฅผ ๋ฐ์์์ผฐ๋ค.
+### ๐ฏ ํต์ฌ ํค์๋
+---
+- ํ๊ฒฝ ๋ณ์ (Environment Variables)
+ - ์ด์์ฒด์ ๋ ์คํ ํ๊ฒฝ์์ ์ค์ ๋ ๋ณ์
+ - ์ํํธํค์ด ์ ํ๋ฆฌ์ผ์ด์
๋์์ ์ํฅ์ ๋ฏธ์น๋ ์ค์ํ ์ ๋ณด๋ฅผ ์ ์ฅํ๋ ๋ฐ ์ฌ์ฉ๋๋ค. ์ฆ, ์ค์ํ ์ ๋ณด๋ฅผ ์์ค์ฝ๋์ ์ง์ ํฌํจํ์ง ์์ ๋ณด์์ ๊ฐํํ๋ค.
+ - ๋ฏผ๊ฐํ๊ฑฐ๋ ์์ฃผ ๋ณ๊ฒฝ๋ ์ ์๋ ์ค์ ๊ฐ์ ์ฝ๋์ ํ๋์ฝ๋ฉํ์ง ์๊ณ ์ธ๋ถ์์ ๊ด๋ฆฌํ ์ ์๋๋ก ํ๋ค.
+ - ex. ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ์ ๋ณด, API ํค, ์๋ฒ ํฌํธ ๋ฒํธ ๋ฑ
+ - ex. Node.js ํ๋ก์ ํธ์์์ .env ํ์ผ
+ - dotenv ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด .env ํ์ผ์ ํ๊ฒฝ๋ณ์๋ฅผ ์ ์ํ๊ณ ๋ก๋ํ ์ ์๋ค.
+ ```javascript
+ DB_HOST=localhost
+ DB_POST=3306
+ DB_USER=root
+ DB_PASSWORD=mypassword
+ PORT=3000
+ ```
+ - Node.js์์ process.env๋ฅผ ํตํด ํ๊ฒฝ๋ณ์์ ์ ๊ทผํ ์ ์๋ค.
+ ```javascript
+ const dbHost = process.env.DB_HOST
+ const dbPort = process.env.DB_PORT
+ ```
+- CORS (Cross-Origin Resource Sharing)
+ - ์น ๋ธ๋ผ์ฐ์ ์์ ์คํ๋๋ ์น ์ ํ๋ฆฌ์ผ์ด์
์ด ๋ค๋ฅธ ๋๋ฉ์ธ, ํ๋กํ ์ฝ, ํฌํธ์์ ์คํ๋๋ ๋ฆฌ์์ค์ ์ ๊ทผํ ์ ์๋๋ก ํ์ฉํ๋ ๋ณด์ ๊ธฐ๋ฅ
+ - ๊ธฐ๋ณธ์ ์ผ๋ก ๋ธ๋ผ์ฐ์ ๋ ๋ณด์์์ ์ด์ ๋ก ๋์ผํ ์ถ์ฒ ์ ์ฑ
(Same-Origin Policy)์ ์ ์ฉํด ํ ์ถ์ฒ์์ ๋ก๋๋ ์น ํ์ด์ง๊ฐ ๋ค๋ฅธ ์ถ์ฒ์ ๋ฆฌ์์ค์ ์ ๊ทผํ๋ ๊ฒ์ ์ ํํ๋ค.
+ - ex. http://umc.com์์ ๋ก๋๋ ์น ์ ํ๋ฆฌ์ผ์ด์
์ ๊ธฐ๋ณธ์ ์ผ๋ก http://api.umc.com๊ณผ ๊ฐ์ ๋ค๋ฅธ ๋๋ฉ์ธ์ ์๋ API์ ์ ๊ทผํ ์ ์๋ค. โ Cors๋ ์ด๋ฌํ ์ ํ์ ์ํํ๋ค.
+ - ์น ์ ํ๋ฆฌ์ผ์ด์
์ด ์ธ๋ถ API๋ฅผ ํตํด ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ฑฐ๋ ๋ค๋ฅธ ๋๋ฉ์ธ์ ์๋ ์๋ฒ์ ์ํธ์์ฉํด์ผ ํ ๋ CORS ์ค์ ์ผ๋ก ์ด๋ฌํ ์์ฒญ์ ํ์ฉํ ์ ์๋ค.
+ 1. ๊ฐ๋จํ ์์ฒญ (Simple Request)
+ - ๋ธ๋ผ์ฐ์ ๋ GET, POST, HEAD ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ ๋จ์ํ HTTP ์์ฒญ์ ๋ํด origin ํค๋๋ฅผ ์ถ๊ฐํ๋ค. ์๋ฒ๋ ์ด ์์ฒญ์ ๋ฐ๊ณ ์๋ต ํค๋์ Access-Control-Allow-Origin์ ํฌํจ์์ผ ์์ฒญ์ ํ์ฉํ ์ง๋ฅผ ๊ฒฐ์ ํ๋ค.
+ - ex.
+ ์์ฒญ ํค๋: Origin: http://umc.com
+ ์๋ต ํค๋: Access-Control-Allow-Origin: http://umc.com
+ 2. ์๋น ์์ฒญ (Preflight Request)
+ - ๋ณด์์์ ์ด์ ๋ก ๋ธ๋ผ์ฐ์ ๋ ๋จ์ํ์ง ์์ ์์ฒญ(ex. PUT, DELETE ๋ฉ์๋, ์ฌ์ฉ์ ์ง์ ํค๋๊ฐ ํฌํจ๋ ์์ฒญ)์ ์คํํ๊ธฐ ์ ์ ์๋ฒ์ ์๋น ์์ฒญ(OPTIONS ๋ฉ์๋)์ ๋ณด๋ธ๋ค. ์ด๋ ์ค์ ์์ฒญ์ ๋ณด๋ด๊ธฐ ์ ์ ์๋ฒ๊ฐ ํด๋น ์์ฒญ์ ํ์ฉํ ์ง๋ฅผ ํ์ธํ๋ ๊ณผ์ ์ด๋ค.
+ - CORS ๊ด๋ จ ํค๋
+ 1. Access-Control-Allow-Origin
+ - ํด๋ผ์ด์ธํธ๊ฐ ๋ฆฌ์์ค์ ์ ๊ทผํ ์ ์๋๋ก ํ์ฉ๋ ์ถ์ฒ(Origin)๋ฅผ ์ ์ํ๋ค.
+ - ex. Access-Control-Allow-Origin: * โ ๋ชจ๋ ์ถ์ฒ๋ฅผ ํ์ฉํ๋ค.
+ 2. Access-Control-Allow-Methods
+ - ํ์ฉ๋ HTTP ๋ฉ์๋๋ฅผ ์ง์ ํ๋ค.
+ - ex. Access-Control-Allow-Methods: GET, POST, PUT, DELETE
+ 3. Access-Control-Allow-Headers
+ - ํด๋ผ์ด์ธํธ๊ฐ ์์ฒญํ ๋ ์ฌ์ฉํ ์ ์๋ ํค๋๋ฅผ ๋ช
์ํ๋ค.
+ - ex. Access-Control-Allow-Headers: Content-Type, Authorization
+ 4. Access-Control-Allow-Credentials
+ - ํด๋ผ์ด์ธํธ์์ ์ฟ ํค์ ๊ฐ์ ์๊ฒฉ ์ฆ๋ช
์ ํฌํจํ ์์ฒญ์ ํ ์ ์๋๋ก ํ์ฉํ ์ง์ ๋ํ ์ฌ๋ถ๋ฅผ ์ค์ ํ๋ค.
+ - ex. Access-Control-Allow-Credentials: true
+ - ex. CORS ์ค์ (Node.js/Express)
+ ```javascript
+ import express from 'express';
+ import cors from 'cors';;
+ const app = express();
+
+ app.use(cors()); // ๋ชจ๋ ์ถ์ฒ์ ๋ํด cors ํ์ฉ
+
+ // ํน์ ์ถ์ฒ๋ง ํ์ฉํ๋ ๊ฒฝ์ฐ
+ app.use(cors({ // cors ๋ฏธ๋ค์จ์ด ์ถ๊ฐ
+ origin: 'http://umc.com', // ํ์ฉ๋ ์ถ์ฒ(Cross-Origin) ์ง์
+ methods: 'GET, POST', // ํ์ฉ๋ HTTP ์ง์
+ credentials: true // ํด๋ผ์ด์ธํธ๊ฐ ์๋ฒ์ ์ฟ ํค, ์ธ์ฆ ํค๋ ๋ฑ ์๊ฒฉ ์ฆ๋ช
(credentials)์ ํฌํจํ ์ ์๋ค.
+ }));
+
+ app.listen(3000, () => {
+ console.log('Server is running on port 3000');
+ });
+ ```
+ - http://umc.com์์ ์ค๋ ์์ฒญ๋ง CORS ๊ท์น์ ์ํด ํ์ฉ๋๋ค.
+ - Cross-Origin: ๋์ผ ์ถ์ฒ ์ ์ฑ
์ ์ฐํํ ์ถ์ฒ๋ฅผ ์ง์ ํ๋ค.
+ - ์์ ์์์์ ์น ๋ธ๋ผ์ฐ์ ๊ฐ http://umc.com์์ ์คํ๋๋ ์ ํ๋ฆฌ์ผ์ด์
์ด ์๋ฒ์ ์์ฒญ์ ๋ณด๋ด๋ ๊ฒ์ ํ์ฉํ๋ค.
+ - ์๋ฒ๋ GET๊ณผ POST ๋ฉ์๋๋ก๋ง ์์ฒญ์ ๋ฐ์ ์ ์๋ค.
+ - Credential ์ต์
์ด true๋ก ์ค์ ๋ ๊ฒฝ์ฐ Access-Control-Allow-Origin์ *๋ก ์ค์ ํ ์ ์๊ณ ํน์ ์ถ์ฒ๋ฅผ ์ง์ ํด์ผ ํ๋ค.
+- DB Connection, DB Connection Pool
+ 1. DB Connection (๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ)
+ - ์ ํ๋ฆฌ์ผ์ด์
์ด ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ฑฐ๋ ์ฐ๊ธฐ ์ํด ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ๊ทผํ ๋ ํ์ํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ์ ํด์ค๋ค.
+ - ๊ฐ ์ฐ๊ฒฐ์ ๋คํธ์ํฌ๋ฅผ ํตํด ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ํธ์์ฉํ๊ณ ์ฟผ๋ฆฌ๋ฅผ ์คํํ๋ฉฐ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ ธ์ค๋ ๋ฐ ์ฌ์ฉ๋๋ค.
+ - ๊ธฐ๋ณธ ํ๋ฆ
+ 1. ์ ํ๋ฆฌ์ผ์ด์
์ด ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ์ ์ค์ ํ๋ค.
+ 2. ์ฟผ๋ฆฌ๋ฅผ ๋ณด๋ด๊ณ ์๋ต์ ๋ฐ๋๋ค.
+ 3. ์์
์ด ๋๋๋ฉด ์ฐ๊ฒฐ์ ์ข
๋ฃํ๋ค.
+ 2. DB Connection Pool (๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ํ)
+ - ์ฌ๋ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ์ ๋ฏธ๋ฆฌ ์์ฑํด๋๊ณ ์ด๋ฅผ ์ ํ๋ฆฌ์ผ์ด์
์์ ์ฌ์ฌ์ฉ(์ฑ๋ฅ, ํจ์จ โ)ํ ์ ์๋๋ก ๊ด๋ฆฌํ๋ ๋ฉํค๋์ฆ
+ - ์๋ ์๋ฆฌ
+ 1. ์ด๊ธฐํ: ์ ํ๋ฆฌ์ผ์ด์
์ด ์์๋ ๋ ์ผ์ ํ ์(๊ณผ๋ถํ ๋ฐฉ์ง)์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ์ ๋ฏธ๋ฆฌ ์์ฑํด๋๋๋ค.
+ 2. ์ฌ์ฌ์ฉ: ์ ํ๋ฆฌ์ผ์ด์
์ด ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์
์ ์์ฒญํ ๋ ์ฐ๊ฒฐ ํ์์ ์ฌ์ฉ ๊ฐ๋ฅํ ์ฐ๊ฒฐ์ ๊ฐ์ ธ๋ค ์ฌ์ฉํ๋ค. (์๊ฐ ๋ฐ ๋ฆฌ์์ค ์ ์ฝ)
+ 3. ๋ฐํ: ์์
์ด ๋๋๋ฉด ์ฐ๊ฒฐ์ด ๋ซํ๋ ๊ฒ์ด ์๋๋ผ ํ๋ก ๋ฐํ๋์ด ๋ค๋ฅธ ์์ฒญ์ด ์ฌ์ฌ์ฉํ ์ ์๋ค.
+ 4. ํ์ฅ/์ถ์: ์ฐ๊ฒฐ ํ์ด ํ์ํ ๊ฒฝ์ฐ ๋ ๋ง์ ์ฐ๊ฒฐ์ ์์ฑํ๊ฑฐ๋ ์ฌ์ฉ๋์ง ์๋ ์ฐ๊ฒฐ์ ์ข
๋ฃํ์ฌ ๋ฆฌ์์ค๋ฅผ ๊ด๋ฆฌํ ์ ์๋ค.
+ - Node.js์์ mysql2 ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ํ์ ์์ฑํ ์ ์๋ค.
+ ```javascript
+ import mysql from 'mysql2/promise'; // MySQL ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ฐ๊ฒฐ
+
+ export const pool = mysql.createPool({ // ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ํ(connection pool) ์์ฑ
+ host: process.env.DB_HOST || 'localhost', // mysql์ hostname
+ user: process.env.DB_USER || 'root', // ์ฌ์ฉ์ ์ด๋ฆ
+ port: process.env.DB_PORT || 3306, // ํฌํธ ๋ฒํธ
+ database: process.env.DB_TABLE || 'restaurant_service', // ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ด๋ฆ
+ password: process.env.DB_PASSWORD || 'mypassword', // ๋น๋ฐ๋ฒํธ
+ waitForConnections: true,
+ // Pool์ ํ๋ํ ์ ์๋ connection์ด ์์ ๋
+ // true๋ฉด ์์ฒญ์ queue์ ๋ฃ๊ณ connection์ ์ฌ์ฉํ ์ ์๊ฒ ๋๋ฉด ์์ฒญ์ ์คํํ๋ค.
+ // false๋ฉด ์ฆ์ ์ค๋ฅ๋ฅผ ๋ด๋ณด๋ด๊ณ ๋ค์ ์์ฒญํ๋ค.
+ connectionLimit: 10, // ๋ช ๊ฐ์ ์ปค๋ฅ์
์ ๊ฐ์ง๊ฒ๋ ํ ๊ฒ์ธ์ง
+ queueLimit: 0, // getConnection์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๊ธฐ ์ ์ Pool์ ๋๊ธฐํ ์์ฒญ์ ๊ฐ์ ํ๋
+ });
+
+ // ์ฐ๊ฒฐ ์ฌ์ฉ
+ pool.query('SELECT * FROM users', (error, results) => { // sql ์ฟผ๋ฆฌ ์คํ
+ if (error) throw error;
+ console.log(results);
+ });
+ ```
+
+- ๋น๋๊ธฐ (async, await)
+ - **๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ (Synchronous Programming)**
+ - ์์
๋ค์ด ์์๋๋ก ์คํ๋์ด ์ด์ ์์
์ด ๋๋์ผ๋ง ๋ค์ ์์
์ ์์ํ๋ค.
+ - ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆฌ๋ ์์
์ด ์์ผ๋ฉด ๊ทธ๋์ ํ๋ก๊ทธ๋จ์ด ๋ฉ์ถ ์ํ๋ก ๋๊ธฐํ๋ค.
+ ```javascript
+ console.log("Start");
+ const result = fetchData(); // ์ค๋ ๊ฑธ๋ฆฌ๋ ์์
+ console.log(result);
+ console.log("End");
+ ```
+ 1. fetchData()๊ฐ ์๋ฃ๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฐ ํ
+ 2. โEndโ๊ฐ ์ถ๋ ฅ๋๋ค.
+ - โStartโ โ fetchData() โ โEndโ
+ - **๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ (Asynchronous Programming)**
+ - ์์
์ ๋์์ ์คํ๋๋๋ก ํ์ฌ ํ๋์ ์์
์ด ๋๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ์ง ์๊ณ ๋ค์ ์์
์ ์ํํ๋ ๋ฐฉ์
+ - ์ค๋ ๊ฑธ๋ฆฌ๋ ์์
์ ์ฒ๋ฆฌํ๋ ๋์ ์ ํ๋ฆฌ์ผ์ด์
์ด ๋ค๋ฅธ ์์
์ ๊ณ์ ์ํํ ์ ์๋๋ก ๋์์ค๋ค. (๊ฐ ์์
์ด ๋ณ๋ ฌ์ ์ผ๋ก ์งํ๋๋ค)
+ - ์๋ฃ๋ ์์
์ ๋์ค์ ์ฝ๋ฐฑ, Promise ๋๋ async/await์ ํตํด ์ฒ๋ฆฌ๋๋ค.
+ ```javascript
+ console.log("Start");
+ setTimeout(() => {
+ console.log("Fetching data... (completed)");
+ }, 2000); // 2์ด ํ ์คํ
+ console.log("End");
+ ```
+ - setTimeout ํจ์์์ 2์ด ๊ธฐ๋ค๋ฆฌ๋ ๋์ ๋ค์ ์ฝ๋(โEndโ ์ถ๋ ฅ)๋ฅผ ๋จผ์ ์คํํ๋ค.
+ - โStartโ โ setTimeout() ์คํ โ โEndโ โ (2์ด ํ) โFetching data... (completed)"
+ - **๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ์ ์ฃผ์ ํจํด**
+ 1. **์ฝ๋ฐฑ ํจ์ (Callback Function)**
+ - ์์
์ด ์๋ฃ๋ ํ ํน์ ํจ์๋ฅผ ํธ์ถํ๋ค.
+ ```javascript
+ function fetchData(callback){
+ setTimeout(() => {
+ **callback("Data loaded")**;
+ }, 1000);
+ }
+ console.log("Start");
+ fetchData(**(data) => console.log(data)**);
+ console.log("End");
+ ```
+ - โStartโ โ setTimeout() ์คํ โ โEndโ โ (1์ด ํ) โData loadedโ
+ - ๋ฌธ์ ์ : ์ฝ๋ฐฑ ํจ์(Callback Hell)๊ฐ ์ค์ฒฉ๋๋ฉด ์ฝ๋ฐฑ ์ง์ฅ์ด๋ผ๋ ๋ณต์กํ ์ฝ๋๊ฐ ๋ฐ์ํ ์ ์๋ค.
+ 2. **Promise**
+ - ์์
์ ์ฑ๊ณต/์คํจ๋ฅผ ํํํ๋ฉฐ then๊ณผ catch๋ก ๊ฒฐ๊ณผ๋ฅผ ์ฒ๋ฆฌํ๋ค.
+ ```javascript
+ const fetchData = new **Promise**((resolve, reject) => {
+ setTimeout(() => **resolve**("Data loaded"), 1000);
+ });
+ console.log("Start");
+ fetchData.**then**((data) => console.log(data));
+ console.log("End");
+ ```
+ > resolve: ์์
์ด ์ฑ๊ณต์ ์ผ๋ก ์๋ฃ๋์์ ๋ ํธ์ถ๋๋ค.
+ > reject: ์์
์ด ์คํจํ์ ๋ ํธ์ถ๋๋ค.
+ - setTimeout์ผ๋ก 1์ด ํ์ resolve ํจ์๋ฅผ ํธ์ถํ๋ค. ์ด ์์
์ฑ๊ณต ์ Promise๊ฐ ํด๊ฒฐ(resolved)๋๋ค.
+ - fetchData๋ Promise์ด๋ฏ๋ก then ๋ฉ์๋๋ฅผ ์ฌ์ฉํด ๋น๋๊ธฐ ์์
์ด ์๋ฃ๋์์ ๋ ๊ฒฐ๊ณผ(data)๋ฅผ ์ฒ๋ฆฌํ๋ค.
+ - 1์ด ํ resolve ํจ์๊ฐ ํธ์ถ๋๋ฉด ๊ฐ("Data loaded")์ด then์ ์ ๋ฌ๋์ด data๊ฐ ์ถ๋ ฅ๋๋ค.
+ - โStartโ โ setTimeout() ์คํ โ โEndโ โ (1์ด ํ) โData loadedโ
+ 3. **async/await**
+ - ์๋ฐ์คํฌ๋ฆฝํธ์ ๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ์ ์ง๊ด์ ์ด๊ณ ๊ฐ๊ฒฐํ๊ฒ ์ฒ๋ฆฌํ ์ ์๋๋ก ๋์์ฃผ๋ ๋ฌธ๋ฒ
+ - ๋น๋๊ธฐ ์์
์์์ ํ๋ฆ์ ๋๊ธฐ์ ์ฝ๋์ฒ๋ผ ์์ฑํ ์ ์๋ค.
+ - async ํค์๋๋ฅผ ํจ์ ์์ ๋ถ์ฌ ํด๋น ํจ์๋ฅผ ๋น๋๊ธฐ ํจ์๋ก ๋ง๋ ๋ค.
+ ```javascript
+ async function example() {
+ return "Hello, Wenty!";
+ }
+ example().then((result) => console.log(result)); // ํธ์ถ ์ Promise๋ก ๋ฐํ๋๋ค.
+ ```
+ - ๋น๋๊ธฐ ํจ์๋ ํญ์ Promise ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ค.
+ - ๋ฐํ ๊ฐ์ด ์ผ๋ฐ ๊ฐ์ด๋ฉด ์๋์ผ๋ก Promise.resolve(โฆ)์ผ๋ก ๊ฐ์ธ์ ๋ฐํ๋๋ค.
+ - await๋ ๋น๋๊ธฐ ํจ์ ๋ด์์๋ง ์ฌ์ฉํ ์ ์๋ค.
+ ```javascript
+ async function fetchData() {
+ let data = await **new Promise**((resolve) =>
+ setTimeout(() => resolve("Data loaded"), 1000)
+ );
+ console.log(data); // 1์ด ํ ์ถ๋ ฅ
+ }
+ fetchData();
+ ```
+ - Promise๊ฐ ํด๊ฒฐ๋ ๋๊น์ง ๊ธฐ๋ค๋ ธ๋ค๊ฐ ํด๋น ๊ฒฐ๊ณผ๋ฅผ ๋ฐํ ๋ฐ์ ๋ค์ ์ฝ๋๋ฅผ ์คํํ๋ค.
+ - await ๋ค์๋ Promise๊ฐ ๋ฐํ๋๋ ๋น๋๊ธฐ ์์
์ด ์์ผ ํ๋ค.
+ - ex.
+ ```javascript
+ **async** function loadData(){
+ console.log("Start");
+ let data = **await** new **Promise**(
+ (resolve) => setTimeout(() => resolve("Data loaded"), 1000));
+ );
+ console.log(data);
+ condole.log("End");
+ }
+ loadData();
+ ```
+ - await๋ Promise๊ฐ ํด๊ฒฐ๋ ๋๊น์ง ๋๊ธฐํ๋ฏ๋ก ์ดํ์ ์ฝ๋๋ ์คํ๋์ง ์๋๋ค.
+ - 1์ด ํ Promise๊ฐ ํด๊ฒฐ๋ ํ์ resolve()๊ฐ ํธ์ถ๋๋ฉด์ await๊ฐ ํด์ ๋๊ณ โData loadedโ๊ฐ data ๋ณ์์ ์ ์ฅ๋๋ค.
+ - โStartโ โ (1์ด ํ) โData loadedโ โ โEndโ
+ 4. async/await + Promise.all
+ - ์ฌ๋ฌ ๊ฐ์ ๋น๋๊ธฐ ์์
์ ๋ณ๋ ฌ๋ก ์คํํ๋ ค๋ฉด Promise.all๊ณผ ํจ๊ป ์ฌ์ฉํ๋ค.
+ - async/await๋ง ์ฌ์ฉํ๋ฉด ๋น๋๊ธฐ ์์
๋ค์ด ์์ฐจ์ ์ผ๋ก ์คํ๋์ด ์๊ฐ์ด ๋ ์ค๋ ๊ฑธ๋ฆฐ๋ค. ๊ทธ๋ฌ๋ Promise.all๊ณผ async/await์ ํจ๊ป ์ฌ์ฉํ๋ฉด ์์
๋ค์ ๋์์ ์คํํ ์ ์์ด ๋ ํจ์จ์ ์ด๋ค.
+ - ์ด ๊ฒฝ์ฐ ์ ์ฒด ์คํ ์๊ฐ์ ๊ฐ์ฅ ์ค๋ ๊ฑธ๋ฆฌ๋ ์์
์ ์๊ฐ๋งํผ๋ง ํ์ํฉ๋๋ค.
+ - async/await๋ง ์ฌ์ฉํ ๊ฒฝ์ฐ
+ ```javascript
+ async function getStudent1() {
+ return new Promise((resolve) => setTimeout(() => resolve("Wenty", 1000));
+ }
+ async function getStudent2() {
+ return new Promise((resolve) => setTimeout(() => resolve("Bongoo"), 2000));
+ }
+ async function loadData() {
+ console.log("Start"); // 1
+ let student1 = await getStudent1();
+ console.log(`Student 1: ${student1}`); // 2
+ let student2 = await getStudent2();
+ console.log(`Student 2: ${student2}`); // 3
+ console.log("End"); // 4
+ }
+ loadData();
+ ```
+ - getStudent1()๋ฅผ ๊ธฐ๋ค๋ ธ๋ค๊ฐ getStudent2()๋ฅผ ๊ธฐ๋ค๋ฆฐ๋ค.
+ - ์ด 1 + 2 = 3์ด ์์๋๋ค.
+ - async/await + Promise.all์ ์ฌ์ฉํ ๊ฒฝ์ฐ
+ ```javascript
+ async function getStudent1() {
+ return new Promise((resolve) => setTimeout(() => resolve("Wenty", 1000));
+ }
+ async function getStudent2() {
+ return new Promise((resolve) => setTimeout(() => resolve("Bongoo"), 2000));
+ }
+ async function loadData() {
+ console.log("Start"); // 1
+ let [student1, student2] = await Promise.all([getStudent1(), getStuent2()]); // ๋ณ๋ ฌ๋ก ์คํ๋๋ฉฐ ๊ฐ์ฅ ์ค๋ ๊ฑธ๋ฆฌ๋ ์์
์๊ฐ(2์ด)๋ง ๊ธฐ๋ค๋ฆฐ๋ค.
+ console.log(`Student 1: ${student1}`); // 2
+ console.log(`Student 2: ${student2}`); // 3
+ console.log("End"); // 4
+ }
+ loadData();
+ ```
+ - Promise.all์ ์ฌ์ฉํด student1๊ณผ student2 ์ ๋ณด๋ฅผ ๋์์ ๊ฐ์ ธ์จ๋ค.
+ - ๋ ์์
์ด ๋ณ๋ ฌ๋ก ์งํ๋์ด ๊ฐ์ฅ ์ค๋ ๊ฑธ๋ฆฌ๋ ์์
์๊ฐ๋ง ์์๋๋ค.
+ - getStudent1()๊ณผ getStudent2() ์ค ์์
์๊ฐ์ด ๋ ๊ธด ์๊ฐ ์ฆ, 2์ด ์์๋๋ค.
+- try/catch/finally
+ - ์ฝ๋์์ ๋ฐ์ํ ์ ์๋ ์ค๋ฅ๋ฅผ ์ฒ๋ฆฌํ๊ณ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ ๋ ์ด๋ป๊ฒ ๋์ํ ์ง๋ฅผ ์ ์ดํ๋ ๋ฐ ์ฌ์ฉ๋๋ค.
+ - ์์ธ ์ฒ๋ฆฌ(Exception Handling)์ ํตํด ์ฝ๋ ์คํ ์ค ์ค๋ฅ๋ฅผ ํ์งํ๊ณ ์ด๋ฅผ ์ ์ ํ ์ฒ๋ฆฌํ ์ ์๋๋ก ํ๋ค.
+ ```javascript
+ function riskyFunction() { // ์ค๋ฅ๊ฐ ๋ ์๋ ์๋ ์ฝ๋
+ throw new Error("Error");
+ }
+
+ try { // try ๋ธ๋ก - ์คํํ ์ฝ๋
+ let result = riskyFunction(); // ์ค๋ฅ ๋ฐ์ ์
+ console.log("Result:", result); // ์ด ์ฝ๋๋ ์คํ๋์ง ์๋๋ค.
+ } catch (error) { // ์ค๋ฅ ๋ฐ์ ์ ์ด ์ฝ๋๋ก ์ด๋
+ console.error("Error:", error.message); // ์ค๋ฅ ์ฒ๋ฆฌ
+ } finally {
+ console.log("End"); // ํญ์ ์คํ
+ }
+ ```
+ - ์ค๋ฅ๊ฐ ๋ฐ์ํ ๊ฐ๋ฅ์ฑ์ด ์๋ ์ฝ๋๋ฅผ try์ ๋ฃ์ด ์์ธ ์ฒ๋ฆฌ์ ๋๋นํ๋ค.
+ - catch์ ๋งค๊ฐ๋ณ์: ์ผ๋ฐ์ ์ผ๋ก error๋ก ์ฌ์ฉ๋๋ฉฐ ๋ฐ์ํ ์ค๋ฅ์ ๋ํ ์ ๋ณด๊ฐ ํฌํจ๋๋ค.
+ - finally: ์ค๋ฅ ๋ฐ์ ์ฌ๋ถ์ ์๊ด์์ด ํญ์ ์คํ๋๋ค. try ๋ธ๋ก๊ณผ catch ๋ธ๋ก์ด ๋๋ ํ์ ์คํ๋๋ค.
\ No newline at end of file
diff --git "a/keyword/chapter06/images/\354\213\244\354\212\265-1-\354\244\221\353\263\265\353\220\234_\355\232\214\354\233\220.png" "b/keyword/chapter06/images/\354\213\244\354\212\265-1-\354\244\221\353\263\265\353\220\234_\355\232\214\354\233\220.png"
new file mode 100644
index 0000000..94164f6
Binary files /dev/null and "b/keyword/chapter06/images/\354\213\244\354\212\265-1-\354\244\221\353\263\265\353\220\234_\355\232\214\354\233\220.png" differ
diff --git "a/keyword/chapter06/images/\354\213\244\354\212\265-1-\355\232\214\354\233\220\353\223\261\353\241\235.png" "b/keyword/chapter06/images/\354\213\244\354\212\265-1-\355\232\214\354\233\220\353\223\261\353\241\235.png"
new file mode 100644
index 0000000..13aedcb
Binary files /dev/null and "b/keyword/chapter06/images/\354\213\244\354\212\265-1-\355\232\214\354\233\220\353\223\261\353\241\235.png" differ
diff --git "a/keyword/chapter06/images/\354\213\244\354\212\265-2-\355\212\271\354\240\225\352\260\200\352\262\214_\353\246\254\353\267\260\353\252\251\353\241\235.png" "b/keyword/chapter06/images/\354\213\244\354\212\265-2-\355\212\271\354\240\225\352\260\200\352\262\214_\353\246\254\353\267\260\353\252\251\353\241\235.png"
new file mode 100644
index 0000000..7d70930
Binary files /dev/null and "b/keyword/chapter06/images/\354\213\244\354\212\265-2-\355\212\271\354\240\225\352\260\200\352\262\214_\353\246\254\353\267\260\353\252\251\353\241\235.png" differ
diff --git a/keyword/chapter06/keyword.md b/keyword/chapter06/keyword.md
new file mode 100644
index 0000000..a37c2ae
--- /dev/null
+++ b/keyword/chapter06/keyword.md
@@ -0,0 +1,433 @@
+### **๐ฆ ์ค์ต**
+---
+1. ํ์๊ฐ์
API
+ - Repository ํจ์
+ ```javascript
+ // ํ์ ๋ฐ์ดํฐ ์ฝ์
(ํ์ ๋ฑ๋ก) & ํ์ ID ๋ฐํ
+ export const addMember = async(data) => {
+ const member = await prisma.member.findFirst({where: {email: data.email}}); // ํด๋น ์ด๋ฉ์ผ๋ก ๋ฑ๋ก๋ ํ์(์ค๋ณต ํ์)์ด ์กด์ฌํ๋์ง ํ์ธ
+ if (member){ // ํด๋น ์ด๋ฉ์ผ๋ก ๋ฑ๋ก๋ ํ์์ด ์์ ๊ฒฝ์ฐ
+ return null;
+ }
+ const created = await prisma.member.create({data: data}); // ํ์ ์์ฑ
+ return created.id; // ์์ฑ๋ ํ์ ID ๋ฐํ
+ };
+
+ // ํ์ ์ ๋ณด ์กฐํ
+ export const getMember = async (memberId) => {
+ const member = await prisma.member.findFirstOrThrow({ where: {id: memberId}});
+ // prisma์์ member ํ
์ด๋ธ์ ์ ๊ทผํ์ฌ ํด๋น memberId์ ์ผ์นํ๋, ์ฒซ ๋ฒ์งธ ๋ ์ฝ๋๋ฅผ ์กฐํํ๋ค.
+ // ํด๋น ๋ ์ฝ๋๊ฐ ์์ ์ ์์ธ๋ฅผ ๋์ง๋ค(์๋ฌ ๋ฐ์).
+ return member;
+ };
+
+ // ์์ - ์ ํธ_์์_์ข
๋ฅ ๋งคํ
+ export const setFavoriteFoodKind = async(memberId, favoriteFoodKindId) => {
+ await prisma.memberFavoriteFoodKind.create({
+ data:{ // ์ ๋ ์ฝ๋์ ํ๋์ ๊ฐ์ ์ง์ ํ๋ค.
+ memberId: memberId,
+ foodKindId: favoriteFoodKindId
+ },
+ });
+ };
+
+ // ํ์ - ์ ํธ_์์_์ข
๋ฅ ๋ฐํ
+ export const getMemberFavoriteFoodKindByMemberId = async (memberId) => {
+ const favoriteFoodKinds = await prisma.memberFavoriteFoodKind.findMany({ // ์ฌ๋ฌ ๋ ์ฝ๋ ์กฐํ, ์กฐ๊ฑด์ ๋ง๋ ๋ชจ๋ ๋ ์ฝ๋๋ฅผ ๋ฐฐ์ด ํํ๋ก ๋ฐํ
+ select: { // ๋ฐํํ ํ๋ ๋ช
์
+ id: true,
+ memberId: true,
+ foodKindId: true,
+ foodKind: true, // ์ฐธ์กฐํ๋ foodKind ํ
์ด๋ธ
+ },
+ where: { memberId: memberId },
+ orderBy: {foodKindId: "asc"}, // foodKindId ๊ธฐ์ค ์ค๋ฆ์ฐจ์ ์ ๋ ฌ
+ });
+ return favoriteFoodKinds;
+ }
+ ```
+ - ํ
์คํธ ๊ฒฐ๊ณผ
+ ![์ค์ต-1-ํ์๋ฑ๋ก](images/์ค์ต-1-ํ์๋ฑ๋ก.png)
+ - ๋์ผํ ์ด๋ฉ์ผ๋ก ํ์๊ฐ์
ํ๋ ๊ฒฝ์ฐ
+ ![images/์ค์ต-1-์ค๋ณต๋_ํ์](images/์ค์ต-1-์ค๋ณต๋_ํ์.png)
+ - ํ์ ์ด๋ฉ์ผ๋ก ์กฐํํ์ ๋ ์ด๋ฏธ ๋ฐ์ดํฐ๊ฐ ์กด์ฌํ ๊ฒฝ์ฐ ์๋ฌ๋ฅผ ๋ฐ์์์ผฐ๋ค.
+2. ํน์ ๊ฐ๊ฒ์ ๋ฆฌ๋ทฐ ๋ชฉ๋ก ์กฐํ API
+ - Repository ํจ์
+ ```javascript
+ // ํน์ ์๋น์ ๋ชจ๋ ๋ฆฌ๋ทฐ ์กฐํ
+ export const getAllRestaurantReviews = async (restaurantId, cursor) => {
+ const reviews = await prisma.review.findMany({ // Prisma ORM์ ์ฌ์ฉํ์ฌ review ํ
์ด๋ธ์์ ์ฌ๋ฌ ๊ฐ์ ๋ ์ฝ๋๋ฅผ ์กฐํํ๋ค.
+ select: {
+ id: true,
+ member: true,
+ restaurant: true,
+ rating: true,
+ createdAt: true,
+ content: true,
+ status: true
+ },
+ where: { restaurantId: restaurantId, id: { gt: cursor }},
+ orderBy: { id: "asc"},
+ take: 5,
+ })
+ const formattedReviews = reviews.map(review => ({
+ ...review,
+ id: review.id.toString(),
+ member: {
+ id: review.member.id.toString(),
+ name: review.member.name,
+ nickname: review.member.nickname,
+ birth: review.member.birth,
+ gender: review.member.gender,
+ location: review.member.location,
+ phoneNumber: review.member.phoneNumber
+ },
+ restaurant: {
+ id: review.restaurant.id.toString(),
+ name: review.restaurant.name
+ },
+ }));
+
+ return formattedReviews;
+ }
+ ```
+ - ํ
์คํธ ๊ฒฐ๊ณผ
+ ![์ค์ต-2-ํน์ ๊ฐ๊ฒ_๋ฆฌ๋ทฐ๋ชฉ๋ก](images/์ค์ต-2-ํน์ ๊ฐ๊ฒ_๋ฆฌ๋ทฐ๋ชฉ๋ก.png)
+### ๐ฏ ํต์ฌ ํค์๋
+---
+- ORM (Object-Relation Mapping)
+ - ๊ฐ์ฒด ์งํฅ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ํ ๊ธฐ์ ๋ก ๊ฐ์ฒด์ ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ฐ์ ๋ณํ์ ์๋์ผ๋ก ์ฒ๋ฆฌํ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
+ - ๊ฐ๋ฐ์๋ SQL์ ์ง์ ์์ฑํ์ง ์๊ณ (๊ฐ๋ฐ ์๋ โ) ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์
์ ๊ฐ์ฒด ์งํฅ ์ธ์ด์ ์ฝ๋๋ก ํ ์ ์๋ค.
+ - ํน์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ข
์๋์ง ์๊ณ ๋ค์ํ DBMS๋ฅผ ์ฝ๊ฒ ๊ต์ฒดํ ์ ์๋ค.
+ - ORM ๋๊ตฌ: Prisma
+ - Node.js ๋ฐ TypeScript๋ฅผ ์ํ ์ต์ ORM ๋๊ตฌ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ํธ์์ฉํ๋ ์์
์ ๋ ๊ฐํธํ๊ณ ํจ์จ์ ์ผ๋ก ์ํํ ์ ์๋๋ก ๋๋๋ค.
+ 1. **Prisma Client**
+ - ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ฟผ๋ฆฌ๋ฅผ ์คํํ๊ธฐ ์ํ ์๋ ์์ฑ๋ ํ์
์์ ํ ์ฝ๋
+ - ์ด๋ฅผ ํตํด TypeScript๋ฅผ ์ฌ์ฉํ ๋ ํ์
์ค๋ฅ๋ฅผ ๋ฐฉ์งํ๊ณ ์ฝ๋ ์๋ ์์ฑ ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์๋ค.
+ 2. **Prisma Migrate**
+ - ๋ฐ์ดํฐ๋ฒ ์ด์ค ์คํค๋ง๋ฅผ ๊ด๋ฆฌํ๋ ๊ธฐ๋ฅ
+ - Migration ํ์ผ์ ์์ฑํ๊ณ ๋ฒ์ ๊ด๋ฆฌ๋ฅผ ํตํด ์คํค๋ง ๋ณ๊ฒฝ์ ์ฝ๊ฒ ๊ด๋ฆฌํ ์ ์๋ค.
+ 3. **Prisma Studio**
+ - ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์๊ฐ์ ์ผ๋ก ํ์ํ๊ณ ๊ด๋ฆฌํ ์ ์๋ ์น ๊ธฐ๋ฐ UI
+ - ๋ฐ์ดํฐ๋ฅผ ์กฐํ, ํธ์ง, ์ญ์ ํ๋ ์์
์ ์ง๊ด์ ์ผ๋ก ์ํํ ์ ์๋ค.
+ - ex. prisma client๋ฅผ ์ฌ์ฉํด ๋ฆฌ๋ทฐ ๋ฐ์ดํฐ๋ฅผ ์์ฑ(๋ฆฌ๋ทฐ ๋ฑ๋ก)ํ๊ณ ์กฐํํ๋ ์ฝ๋
+ ```javascript
+ // prisma/schema.prisma
+
+ ...
+
+ // ๋ฆฌ๋ทฐ ๋ชจ๋ธ
+ model Review{
+ id BigInt @id @default(autoincrement())
+ member Member @relation(fields: [memberId], references: [id])
+ memberId BigInt @map("member_id")
+ restaurant Restaurant @relation(fields: [restaurantId], references: [id])
+ restaurantId BigInt @map("restaurant_id")
+ rating Decimal @db.Decimal(2, 1) @default(0.0)
+ content String
+ createdAt DateTime @map("created_at") @db.Timestamp(6) @default(now())
+ updatedAt DateTime @map("updated_at") @db.Timestamp(6) @default(now())
+ status Int @default(1)
+
+ replys Reply[]
+ images Image[]
+
+ @@index([memberId], map: "member_id")
+ @@index([restaurantId], map: "restaurant_id")
+ @@map("review")
+ }
+
+ ...
+ ```
+ ```javascript
+ // src/db.config.js
+
+ import { PrismaClient } from '@prisma/client';
+ // ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ํธ ์์ฉํ๊ธฐ ์ํ ๊ธฐ๋ณธ ํด๋ผ์ด์ธํธ(PrismaClient)๋ฅผ ๊ฐ์ ธ์จ๋ค.
+
+ export const prisma = new PrismaClient({ log: ["query"] });
+ // PrismaClient์ ์ ์ธ์คํด์ค ์์ฑ
+ // ์คํ๋๋ ๋ชจ๋ ์ฟผ๋ฆฌ๋ฅผ ์ฝ์์ ๋ก๊น
ํ๋๋ก ํ๋ค.
+ ```
+ ```javascript
+ // src/repositories/review.repository.js
+
+ import { prisma, pool } from "../db.config.js";
+
+ // ๋ฆฌ๋ทฐ ๋ฑ๋ก ๋ฐ ๋ฆฌ๋ทฐ ID ๋ฐํ
+ export const addReview = async(data) => {
+ // ๋ฆฌ๋ทฐ๋ฅผ ๋ฑ๋กํ๊ณ ์ ํ๋ ์๋น์ด ์กด์ฌํ๋์ง ์กฐ๊ฑด์ ๋ง์กฑํ๋ ์ฒซ ๋ฒ์ฌ ๋ฐ์ดํฐ๋ฅผ ์ถ์ถํ์ฌ ํ์ธ
+ const restaurant = await prisma.restaurant.findFirst( {
+ where: {
+ id: data.restaurant // ๋ฆฌ๋ทฐ๋ฅผ ๋ฑ๋กํ๊ณ ์ ํ๋ ์๋น์ ID๋ก ํ์ธํ๋ค.
+ }
+ })
+ if (restaurant == null){ // ์๋น์ด ์กด์ฌํ์ง ์์ ์
+ return null;
+ }
+ const created = await prisma.review.create({ // ๋ฆฌ๋ทฐ ์์ฑ
+ data: { // ์์ฑํ ๋ฐ์ดํฐ ๊ฐ์ฒด
+ ...data, // data ๊ฐ์ฒด์ ์์ฑ์ ํผ์น๋ค.
+ member: {
+ connect: { // member ํ
์ด๋ธ๊ณผ ๊ด๊ณ ์ฐ๊ฒฐ
+ id: data.member
+ }
+ },
+ restaurant:{
+ connect: { // restaurant ํ
์ด๋ธ๊ณผ ๊ด๊ณ ์ฐ๊ฒฐ
+ id: data.restaurant
+ }
+ }
+ }
+ });
+ return created.id; // ์์ฑ๋ ๋ฆฌ๋ทฐ์ ID ๋ฐํ
+ }
+
+ export const getReview = async(reviewId) => {
+ // ํด๋น ๋ฆฌ๋ทฐ ID์ ์ผ์นํ๋ ๋ฆฌ๋ทฐ ์กฐํ (๋ฐ์ดํฐ๊ฐ ์์ ์ ์์ธ ๋ฐ์)
+ const review = await prisma.review.findFirstOrThrow({
+ select: { // ๋ฐํํ ํ๋ ๋ช
์
+ id: true,
+ member: true, // ์ฐธ์กฐํ๋ memberํ
์ด๋ธ
+ restaurant: true, // ์ฐธ์กฐํ๋ restaurant ํ
์ด๋ธ
+ rating: true,
+ content: true,
+ createdAt: true,
+ status: true
+ },
+ where: {
+ id: reviewId // ํด๋น reviewId์ ์ผ์นํ๋ ๋ฆฌ๋ทฐ ์กฐํ
+ }
+ });
+ const formattedReview = { // ๋ฆฌ๋ทฐ ๋ฐ์ดํฐ ํ์ํ
+ ...review, // ์์์ selectํ ์์ฑ๋ค์ ํผ์น๋ค.
+ id: review.id.toString(),
+ // DB์ id ํ๋๊ฐ BigInt ํ์
์ผ๋ก ์ ์๋์ด ์๋๋ฐ
+ // javaScript์์ BigInt ํ์
์ JSON์ผ๋ก ๋ณํํ ์ ์์ด
+ // BigInt ํ์
์ id๋ฅผ ๋ฌธ์์ด๋ก ๋ณํํด์ฃผ์๋ค.
+ member: { // ์ฐธ์กฐํ๋ member ํ
์ด๋ธ์์ ์ถ์ถํ ์์ฑ
+ id: review.member.id.toString(),
+ name: review.member.name,
+ },
+ restaurant: {
+ id: review.restaurant.id.toString(),
+ name: review.restaurant.name,
+ },
+ }
+ return formattedReview;
+ }
+ ```
+ - Prisma ์ฌ์ฉ ํ๋ฆ
+ 1. prisma/schema.prisma ํ์ผ์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ชจ๋ธ์ ์ ์ํ๋ค.
+ 2. ์คํค๋ง ๋ณ๊ฒฝ ์ฌํญ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฉํ๊ธฐ ์ํด Prisma Migration์ ์คํํ๋ค.
+ 3. ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์
์ ์ํํ๊ธฐ ์ํด Prisma Client๋ฅผ ์์ฑํ๋ค.
+ 4. Prisma Client๋ฅผ ์ฌ์ฉํด ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ CRUD ์์
์ ์ํํ๋ค.
+ - ๊ธฐํ ORM ๋๊ตฌ
+ 1. Entity Framework: .NET ํ๊ฒฝ์์ ์ฌ์ฉ๋๋ ORM
+ 2. Hibernate: Java ์ํ๊ณ์์ ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉํ๋ ORM ํ๋ ์์ํฌ
+- Prisma ๋ฌธ์ ์ดํด๋ณด๊ธฐ
+ - ex. Prisma์ Connection Pool ๊ด๋ฆฌ ๋ฐฉ๋ฒ
+ - Prisma๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ํจ์จ์ ์ธ ์ฐ๊ฒฐ ๊ด๋ฆฌ๋ฅผ ์ํด ์์ฒด์ ์ธ Connection Pool์ ์ฌ์ฉํ๋ค. ์ด๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ์ ์ฌ์ฌ์ฉํ์ฌ ์ฑ๋ฅ์ ํฅ์์ํค๊ณ ํนํ ์๋ฒ๋ฆฌ์ค ํ๊ฒฝ์์์ ์ฐ๊ฒฐ ๋ฌธ์ ๋ฅผ ์ํํ๋ ๋ฐ ๋์์ ์ค๋ค.
+ - ๋์ ๋ฐฉ์
+ - Prisma์ ์ฟผ๋ฆฌ ์์ง์ ์ฒซ ๋ฒ์งธ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ์ ์ปค๋ฅ์
ํ์ ์์ฑํ๋ค.
+ - ์ดํ ์ฟผ๋ฆฌ๊ฐ ์คํ๋ ๋๋ง๋ค ๊ธฐ์กด ์ฐ๊ฒฐ์ ์ฌ์ฌ์ฉํ๊ฑฐ๋ ํ์์ ๋ฐ๋ผ ์๋ก์ด ์ฐ๊ฒฐ์ ์ถ๊ฐํ๋ค.
+ - ์ด๋ฌํ ๋ฐฉ์์ผ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ์ ํจ์จ์ฑ์ ๋์ด๊ณ ๋ถํ์ํ ์ฐ๊ฒฐ ์์ฑ์ ์ต์ํํ๋ค.
+ - ๊ธฐ๋ณธ์ ์ผ๋ก Prisma๋ ๋จธ์ ์ ๋ฌผ๋ฆฌ์ CPU ์๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ปค๋ฅ์
ํ์ ํฌ๊ธฐ๋ฅผ ๊ฒฐ์ ํ๋ค.
+ - ์ปค๋ฅ์
ํ ํฌ๊ธฐ๋ฅผ ์๋์ผ๋ก ์ค์ ํ๋ ค๋ฉด ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ URL์ connection_limit ํ๋ผ๋ฏธํฐ๋ฅผ ์ถ๊ฐํ๋ค.
+ ```javascript
+ datasource db {
+ provider = "mysql"
+ url = "mysql://root:mypassword@localhost:3306/restaurant_service?connection_limit=5"
+ }
+ ```
+ - ์ด ๊ฒฝ์ฐ ์ปค๋ฅ์
ํ์ ํฌ๊ธฐ๊ฐ 5๋ก ์ ํ๋๋ค.
+ - ex. Prisma์ Migration ๊ด๋ฆฌ ๋ฐฉ๋ฒ
+ - Prisma์ Migration ๊ด๋ฆฌ: ๋ฐ์ดํฐ๋ฒ ์ด์ค ์คํค๋ง์ ์ ํ๋ฆฌ์ผ์ด์
์ ๋ฐ์ดํฐ ๋ชจ๋ธ์ ์ผ๊ด๋๊ฒ ์ ์งํ๊ณ ์คํค๋ง ๋ณ๊ฒฝ์ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ๊ธฐ ์ํ ๋๊ตฌ
+ - Prisma ์คํค๋ง ํ์ผ(schema.prisma)์์ ๋ฐ์ดํฐ ๋ชจ๋ธ์ ์ ์ํ๊ณ ์ด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค ์คํค๋ง๋ฅผ ์์ฑํ๊ฑฐ๋ ์
๋ฐ์ดํธํ๋ค.
+ - ์คํค๋ง ๋ณ๊ฒฝ ์, ํด๋น ๋ณ๊ฒฝ ์ฌํญ์ ๋ฐ์ํ SQL ๋ง์ด๊ทธ๋ ์ด์
ํ์ผ์ ์๋์ผ๋ก ์์ฑํ์ฌ ๋ฒ์ ๊ด๋ฆฌ๊ฐ ๊ฐ๋ฅํ๋ค.
+ - ์์ฑ๋ ๋ง์ด๊ทธ๋ ์ด์
ํ์ผ์ ๊ฐ๋ฐ, ํ
์คํธ, ํ๋ก๋์
ํ๊ฒฝ์ ์ ์ฉํ์ฌ ์คํค๋ง ๋ณ๊ฒฝ์ ์ผ๊ด๋๊ฒ ์ ์งํ๋ค.
+ - ์ฃผ์ ๋ช
๋ น์ด
+ - **prisma migrate dev**
+ - ๊ฐ๋ฐ ํ๊ฒฝ์์ ์คํค๋ง ๋ณ๊ฒฝ ์ ์๋ก์ด Migration ํ์ผ์ ์์ฑํ๊ณ ์ด๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฉํ๋ค.
+ - Prisma Client๋ฅผ ์๋์ผ๋ก ์์ฑํ์ฌ ์ฝ๋์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ํธ์์ฉํ ์ ์๋๋ก ํ๋ค.
+ - **prisma migrate deploy**
+ - ํ๋ก๋์
ํ๊ฒฝ์์ ๋๊ธฐ ์ค์ธ Migration์ ์์ฐจ์ ์ผ๋ก ์ ์ฉํ๋ค.
+ - ์ด ๋ช
๋ น์ด๋ CI/CD ํ์ดํ๋ผ์ธ์์ ์ฌ์ฉ๋์ด ์คํค๋ง ๋ณ๊ฒฝ์ ์๋ํํ๋ค.
+ - **prisma migrate reset**
+ - ๊ฐ๋ฐ ํ๊ฒฝ์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ด๊ธฐํํ๊ณ ๋ชจ๋ Migration์ ๋ค์ ์ ์ฉํ๋ค.
+ - ํ
์คํธ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฌ์ค์ ํ๊ฑฐ๋ ์คํค๋ง๋ฅผ ์ด๊ธฐ ์ํ๋ก ๋๋๋ฆด ๋ ์ ์ฉํ๋ค.
+ - Migration ์ํฌํ๋ก์ฐ
+ 1. schema.prisma ํ์ผ์์ ๋ฐ์ดํฐ ๋ชจ๋ธ์ ์์ ํ๋ค.
+ 2. prisma migrate dev ๋ช
๋ น์ด๋ฅผ ์คํํ์ฌ ์๋ก์ด ๋ง์ด๊ทธ๋ ์ด์
ํ์ผ์ ์์ฑํ๊ณ ์ด๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฉํ๋ค.
+ 3. ์์ฑ๋ Migration ํ์ผ๊ณผ schema.prisma ํ์ผ์ ๋ฒ์ ๊ด๋ฆฌ ์์คํ
์ ์ปค๋ฐํ์ฌ ๋ณ๊ฒฝ ์ด๋ ฅ์ ์ถ์ ํ๋ค.
+ 4. ํ๋ก๋์
ํ๊ฒฝ์์๋ prisma migrate deploy ๋ช
๋ น์ด๋ฅผ ์ฌ์ฉํ์ฌ ๋๊ธฐ ์ค์ธ Migration์ ์ ์ฉํ๋ค.
+- ORM(Prisma)์ ์ฌ์ฉํ์ฌ ์ข์ ์ ๊ณผ ๋์ ์
+ 1. ์ข์ ์
+ - TypeScript์ ๊ธด๋ฐํ๊ฒ ํตํฉ๋์ด ์์ด ๋ชจ๋ ์ฟผ๋ฆฌ์ ๋ํด ์๋์ผ๋ก ํ์
์ ์์ฑํ๋ค. ์ด๋ฅผ ํตํด ์ฝ๋ ์์ฑ ์ ์ค์๋ฅผ ์ค์ด๊ณ ์์ ํ๊ฒ ๊ฐ๋ฐํ ์ ์๋ค.
+ - Prisma์ ์ง๊ด์ ์ธ API ๋๋ถ์ ๋ณต์กํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์
๋ ์ฝ๊ฒ ๊ตฌํํ ์ ์์ด ๊ฐ๋ฐ ์๋๊ฐ ๋นจ๋ผ์ง๋ค.
+ - Prisma Migrate๋ฅผ ํตํด ๋ฐ์ดํฐ๋ฒ ์ด์ค ์คํค๋ง๋ฅผ ์๋์ผ๋ก ๊ด๋ฆฌํ๊ณ ๋ฒ์ ๊ธฐ๋ก์ ๋จ๊ธธ ์ ์๋ค.
+ - MySQL, PostgreSQL, SQLite, SQL Server ๋ฑ ๋ค์ํ ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ํธํ๋๋ค.
+ 2. ๋์ ์
+ - ORM์ด ์๋์ผ๋ก ์์ฑํ ์ฟผ๋ฆฌ๋ ์์์
์ผ๋ก ์ต์ ํ๋ SQL๋ณด๋ค ๋๋ฆด ์ ์๋ค. ๋ณต์กํ ์ฟผ๋ฆฌ์ผ์๋ก ๋นํจ์จ์ ์ด ๋ ๊ฐ๋ฅ์ฑ์ด ํฌ๋ค.
+ - ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๊ณ ์ ๊ธฐ๋ฅ์ ํ์ฉํ๊ฑฐ๋ ์ธ๋ถ์ ์ธ ์ฟผ๋ฆฌ ์ต์ ํ๊ฐ ์ด๋ ค์ ์ฑ๋ฅ์ ์ธ๋ฐํ๊ฒ ์กฐ์ ํ๊ธฐ ํ๋ค ์ ์๋ค.
+ - ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ตฌ์กฐ๊ฐ ๋ณต์กํ ๊ฒฝ์ฐ ๋ชจ๋ ๊ด๊ณ๋ฅผ ์ ์ ํ ๋งคํํ๋ ๋ฐ ํ๊ณ๊ฐ ์๋ค.
+ - ์๋ ์์ฑ๋ ์ฟผ๋ฆฌ์ ์ฑ๋ฅ ๋ฌธ์ ๋ ์ค๋ฅ๋ฅผ ํด๊ฒฐํ๊ธฐ๊ฐ ์ด๋ ต๊ณ ๋ณต์กํ ์ ์๋ค.
+ - ํน์ ORM ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์์กดํ๊ฒ ๋๋ฉด,์ ์ง๋ณด์๋ ๋ณ๊ฒฝ ์ ์ด๋ ค์์ ๊ฒช์ ์ ์๋ค.
+- ๋ค์ํ ORM ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ดํด๋ณด๊ธฐ
+ - ex. Sequelize
+ - Node.js์์ ์ฌ์ฉํ๋ ORM ๋ผ์ด๋ธ๋ฌ๋ฆฌ
+ - ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์
์ ์ํํ ์ ์์ผ๋ฉฐ ๋ณต์กํ SQL ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ์ง ์๊ณ ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ํธ์์ฉํ ์ ์๋ค.
+ - ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ
์ด๋ธ์ ๋ชจ๋ธ๋ก ์ ์ํ์ฌ ๋ค๋ฃฌ๋ค. ๊ฐ ๋ชจ๋ธ์ ํ
์ด๋ธ์ ๊ตฌ์กฐ๋ฅผ ์ ์ํ๊ณ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ฒ CRUD ์์
ํ ์ ์๋๋ก ์ง์ํ๋ค.
+ - ๋ณต์กํ ์ฟผ๋ฆฌ๋ฅผ ์ฝ๊ฒ ์์ฑํ ์ ์๋ ๋ฉ์๋์ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค. findAll, create, update ๋ฑ์ ๋ฉ์๋๋ฅผ ํตํด SQL ์ฟผ๋ฆฌ๋ฅผ ๊ฐ๋จํ ์์ฑํ ์ ์๋ค.
+ ```javascript
+ // ํ์ ๋ฐ์ดํฐ ์์ฑ
+ async function createMember() {
+ const member = await Member.create({
+ locationAddress: '์์ธ์ ๊ด์
๊ตฌ',
+ email: 'ahnnn000@gmail.com',
+ phoneNumber: '010-9836-3964',
+ memberName: '์์ฑ์ง',
+ nickname: '์ฌํฐ',
+ gender: 1,
+ birth: 2000-04-24
+ });
+ }
+
+ // ๋ชจ๋ ํ์ ๋ฐ์ดํฐ ์กฐํ
+ async function getMembers() {
+ const members = await Member.findAll();
+ }
+ ```
+ - Sequelize ๋ชจ๋ธ
+ - Sequelize ๋ชจ๋ธ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ํ
์ด๋ธ์ ์ฝ๋ ์์์ ๊ฐ์ฒด๋ก ํํํ ๊ฒ์ด๋ค. (๋ฐ์ดํฐ๋ฒ ์ด์ค์ ํ
์ด๋ธ๊ณผ 1:1๋ก ๋งคํ๋๋ค)
+ - ์ด ๋ชจ๋ธ์ ํตํด SQL ์ฟผ๋ฆฌ๋ฅผ ์ง์ ์์ฑํ์ง ์๊ณ ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ํธ์์ฉํ ์ ์๋ค. CRUD ์์
์ JavaScript ์ฝ๋๋ก ์ฝ๊ฒ ์ํํ ์ ์๋๋ก ํด์ค๋ค.
+ - ex. Member ๋ชจ๋ธ ์ ์
+ ```javascript
+ const { DataTypes, Model } = require('sequelize');
+ // DataTypes: ๋ฐ์ดํฐ ํ์
์ ์ ๋ชจ๋(string, integer ๋ฑ)
+ // Model: ๋ชจ๋ธ ํด๋์ค๋ฅผ ํ์ฅํ๋ ๊ธฐ๋ณธ ํด๋์ค. ์ด๋ฅผ ์์๋ฐ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ
์ด๋ธ๊ณผ ๋งคํ๋๋ ๊ฐ์ฒด๊ฐ ๋๋ค.
+ // Sequelize ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ
+ const sequelize = require('../config/database');
+ // ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ค์ ํ์ผ(database.js)์์ ์ค์ ํ Sequelize ์ธ์คํด์ค๋ฅผ ๊ฐ์ ธ์จ๋ค.
+ class Member extends Model {} // Model ํด๋์ค๋ฅผ ์์๋ฐ๋๋ค.
+ Member.init( // ๋ชจ๋ธ์ ์ด๊ธฐํํ๊ณ ํ
์ด๋ธ์ ์ปฌ๋ผ์ ์ ์ํ๋ค.
+ {
+ id: {
+ **type**: DataTypes.BIGINT,
+ **primaryKey**: true,
+ **autoIncrement**: true,
+ },
+ locationAddress: {
+ type: DataTypes.TEXT,
+ **allowNull**: false,
+ },
+ email: {
+ type: DataTypes.STRING(50),
+ allowNull: false,
+ unique: true,
+ },
+ phoneNumber: {
+ type: DataTypes.STRING(15),
+ allowNull: true,
+ },
+ memberName: {
+ type: DataTypes.STRING(30),
+ allowNull: false,
+ },
+ nickname: {
+ type: DataTypes.STRING(30),
+ allowNull: false,
+ },
+ gender: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ },
+ birth: {
+ type: DataTypes.STRING(10),
+ allowNull: false,
+ },
+ points: {
+ type: DataTypes.BIGINT,
+ defaultValue: 0,
+ allowNull: false,
+ },
+ createdAt: {
+ type: DataTypes.DATE(6),
+ allowNull: false,
+ },
+ updatedAt: {
+ type: DataTypes.DATE(6),
+ allowNull: false,
+ },
+ status: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ },
+ inactiveAt: {
+ type: DataTypes.DATE(6),
+ allowNull: true,
+ },
+ },
+ {
+ sequelize, // Sequelize ์ธ์คํด์ค
+ modelName: 'Member', // ๋ชจ๋ธ ์ด๋ฆ ์ค์
+ tableName: 'member', // ๋งคํ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ํ
์ด๋ธ ์ด๋ฆ
+ timestamps: true, // createdAt, updatedAt ์๋ ๊ด๋ฆฌ ํ์ฑํ
+ }
+ );
+ module.exports = Member; // Member ๋ชจ๋ธ์ ์ธ๋ถ์์ ์ฌ์ฉํ๋๋ก ๋ด๋ณด๋ธ๋ค.
+ ```
+ - ex. TypeORM
+ - **TypeScript**์ **JavaScript**๋ฅผ ์ํ ORM ๋ผ์ด๋ธ๋ฌ๋ฆฌ
+ - ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ฌ์ฉํ์ฌ ์ํฐํฐ์ ํ
์ด๋ธ, ์ปฌ๋ผ ๋ฑ์ ์ ์ํ๋ค. ์ด๋ฅผ ํตํด ๊ฐ์ฒด์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ
์ด๋ธ ๊ฐ์ ๋งคํ์ ๊ฐ๋จํ๊ฒ ์ค์ ํ ์ ์๋ค.
+ - ์ํฐํฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ
์ด๋ธ์ ์๋ ์์ฑํ๊ณ ์คํค๋ง๋ฅผ ๋๊ธฐํํ ์ ์๋ค.
+ - ๋ณต์กํ SQL ์ฟผ๋ฆฌ๋ฅผ TypeORM์ ์ฟผ๋ฆฌ ๋น๋๋ฅผ ์ฌ์ฉํ์ฌ ์ฝ๊ฒ ์์ฑํ ์ ์๋ค.
+ - ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ฌ์ฉํ์ฌ ์ปฌ๋ผ์ ๋ํ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ์ค์ ํ ์ ์๋ค.
+ - ๊ด๊ณ๋ ์ํฐํฐ๋ฅผ ์ง์ฐ ๋ก๋ฉ(Lazy Loading)ํ ์ ์๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
+- ํ์ด์ง๋ค์ด์
์ ์ฌ์ฉํ๋ ๋ค๋ฅธ API ์ฐพ์๋ณด๊ธฐ
+ - ex. https://docs.github.com/en/rest/using-the-rest-api/using-pagination-in-the-rest-api?apiVersion=2022-11-28
+ - **GitHub REST API**
+ - ํ์ด์ง๋ค์ด์
์ ์ฌ์ฉํด ๋๊ท๋ชจ ๋ฐ์ดํฐ๋ฅผ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ๋ค.
+ - ๊ธฐ๋ณธ์ ์ผ๋ก ํ ํ์ด์ง์ ์ผ์ ์์ ํญ๋ชฉ๋ง ๋ฐํํ๋ฉฐ link ํค๋๋ฅผ ํตํด ๋ค์ ํ์ด์ง, ์ด์ ํ์ด์ง ๋ฑ์ผ๋ก ์ด๋ํ ์ ์๋ URL์ ์ ๊ณตํ๋ค.
+ - per_page ๋งค๊ฐ๋ณ์๋ฅผ ์ฌ์ฉํด ํ์ด์ง๋น ํญ๋ชฉ ์๋ฅผ ์กฐ์ ํ ์ ์๋ค.
+ - ex. ์ด์ ๋ชฉ๋ก์ ์์ฒญํ ๊ฒฝ์ฐ (๊ธฐ๋ณธ์ ์ผ๋ก ํ ํ์ด์ง์ 30๊ฐ ๋ฐํ)
+ ```javascript
+ GET /repos/{owner}/{repo}/issues?per_page=10&page=2
+ ```
+ - per_page=10: ํ์ด์ง๋น 10๊ฐ์ ์ด์๋ฅผ ์์ฒญํ๋ค.
+ - page=2: ๋ ๋ฒ์งธ ํ์ด์ง๋ฅผ ์์ฒญํ๋ค.
+ - ex. https://developers.notion.com/reference/intro#pagination
+ - **Notion์ REST API**
+ - ๋๋์ ๋ฐ์ดํฐ๋ฅผ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ๊ธฐ ์ํด ํ์ด์ง๋ค์ด์
์ ์ฌ์ฉํ๋ค.
+ - ์ด๋ ํ ๋ฒ์ ์์ฒญ์ผ๋ก ๋ฐํ๋๋ ๋ฐ์ดํฐ ์์ ์ ํํ๊ณ ์ถ๊ฐ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํด ํ์ ์์ฒญ์ ์ํํ๋ ๋ฐฉ์
+ - ์ฃผ์ ์์
+ 1. **page_size**
+ - ํ ๋ฒ์ API ์์ฒญ์ผ๋ก ๋ฐํ๋๋ ํญ๋ชฉ์ ์ต๋ ์๋ฅผ ์ง์ ํ๋ค.
+ - ๊ธฐ๋ณธ๊ฐ์ 100์ด๋ฉฐ ์ต๋ 100๊น์ง ์ค์ ํ ์ ์์ต๋๋ค.
+ - ex. page_size=50 โ ํ ๋ฒ์ ์์ฒญ์ผ๋ก ์ต๋ 50๊ฐ์ ํญ๋ชฉ ๋ฐํ
+ 2. **start_cursor**
+ - ๋ฐ์ดํฐ์ ํน์ ์ง์ ๋ถํฐ ๋ฐํ์ ์์ํ๊ธฐ ์ํด ์ฌ์ฉ๋๋ ์ปค์
+ - ์ด์ ์๋ต์ next_cursor ํ๋์์ ๊ฐ์ ธ์จ๋ค. ์ด๋ฅผ ํตํด ๋ค์ ํ์ด์ง์ ๋ฐ์ดํฐ๋ฅผ ์์ฒญํ ์ ์๋ค.
+ - ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ํญ๋ชฉ์ ์กฐํํ ๊ฒฝ์ฐ์ ํ์ด์ง๋ค์ด์
๊ตฌํ
+ 1. **์ฒซ ๋ฒ์งธ ์์ฒญ**
+ ```javascript
+ POST https://api.notion.com/v1/databases/{database_id}/query
+ Content-Type: application/json
+ Notion-Version: 2022-06-28
+ {
+ "page_size": 100
+ }
+ ```
+ - ์ต๋ 100๊ฐ์ ํญ๋ชฉ์ ๋ฐํํ๋ฉฐ ์๋ต์๋ next_cursor์ has_more ํ๋๊ฐ ํฌํจ๋๋ค.
+ 2. **ํ์ ์์ฒญ**
+ ```javascript
+ POST https://api.notion.com/v1/databases/{database_id}/query
+ Content-Type: application/json
+ Notion-Version: 2022-06-28
+ {
+ "page_size": 100,
+ "start_cursor": "{next_cursor}"
+ }
+ ```
+ - ์ฒซ ๋ฒ์งธ ์๋ต์์ has_more๊ฐ true์ด๊ณ next_cursor๊ฐ ์กด์ฌํ๋ฉด ๋ค์๊ณผ ๊ฐ์ด ํ์ ์์ฒญ์ ๋ณด๋ธ๋ค.
+ - ์ฌ๊ธฐ์ {next_cursor}๋ ์ด์ ์๋ต์ next_cursor ๊ฐ์ ์ฌ์ฉํ๋ค. ์ด๋ฌํ ๋ฐฉ์์ผ๋ก ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ์์ฐจ์ ์ผ๋ก ๊ฐ์ ธ์ฌ ์ ์๋ค.
\ No newline at end of file
diff --git "a/mission/chapter05/images/\353\257\270\354\205\230-1-\354\213\235\353\213\271_\353\223\261\353\241\235.png" "b/mission/chapter05/images/\353\257\270\354\205\230-1-\354\213\235\353\213\271_\353\223\261\353\241\235.png"
new file mode 100644
index 0000000..5b2a71f
Binary files /dev/null and "b/mission/chapter05/images/\353\257\270\354\205\230-1-\354\213\235\353\213\271_\353\223\261\353\241\235.png" differ
diff --git "a/mission/chapter05/images/\353\257\270\354\205\230-1-\354\244\221\353\263\265\353\220\234_\354\213\235\353\213\271.png" "b/mission/chapter05/images/\353\257\270\354\205\230-1-\354\244\221\353\263\265\353\220\234_\354\213\235\353\213\271.png"
new file mode 100644
index 0000000..8ba6107
Binary files /dev/null and "b/mission/chapter05/images/\353\257\270\354\205\230-1-\354\244\221\353\263\265\353\220\234_\354\213\235\353\213\271.png" differ
diff --git "a/mission/chapter05/images/\353\257\270\354\205\230-2-\353\246\254\353\267\260_\353\223\261\353\241\235.png" "b/mission/chapter05/images/\353\257\270\354\205\230-2-\353\246\254\353\267\260_\353\223\261\353\241\235.png"
new file mode 100644
index 0000000..fa3485a
Binary files /dev/null and "b/mission/chapter05/images/\353\257\270\354\205\230-2-\353\246\254\353\267\260_\353\223\261\353\241\235.png" differ
diff --git "a/mission/chapter05/images/\353\257\270\354\205\230-2-\354\241\264\354\236\254\355\225\230\354\247\200\354\225\212\353\212\224_\354\213\235\353\213\271.png" "b/mission/chapter05/images/\353\257\270\354\205\230-2-\354\241\264\354\236\254\355\225\230\354\247\200\354\225\212\353\212\224_\354\213\235\353\213\271.png"
new file mode 100644
index 0000000..3c0897f
Binary files /dev/null and "b/mission/chapter05/images/\353\257\270\354\205\230-2-\354\241\264\354\236\254\355\225\230\354\247\200\354\225\212\353\212\224_\354\213\235\353\213\271.png" differ
diff --git "a/mission/chapter05/images/\353\257\270\354\205\230-3-\353\257\270\354\205\230_\353\223\261\353\241\235.png" "b/mission/chapter05/images/\353\257\270\354\205\230-3-\353\257\270\354\205\230_\353\223\261\353\241\235.png"
new file mode 100644
index 0000000..d3293bf
Binary files /dev/null and "b/mission/chapter05/images/\353\257\270\354\205\230-3-\353\257\270\354\205\230_\353\223\261\353\241\235.png" differ
diff --git "a/mission/chapter05/images/\353\257\270\354\205\230-3-\354\241\264\354\236\254\355\225\230\354\247\200\354\225\212\353\212\224_\354\213\235\353\213\271.png" "b/mission/chapter05/images/\353\257\270\354\205\230-3-\354\241\264\354\236\254\355\225\230\354\247\200\354\225\212\353\212\224_\354\213\235\353\213\271.png"
new file mode 100644
index 0000000..8988db0
Binary files /dev/null and "b/mission/chapter05/images/\353\257\270\354\205\230-3-\354\241\264\354\236\254\355\225\230\354\247\200\354\225\212\353\212\224_\354\213\235\353\213\271.png" differ
diff --git "a/mission/chapter05/images/\353\257\270\354\205\230-3-\354\244\221\353\263\265\353\220\234_\353\257\270\354\205\230.png" "b/mission/chapter05/images/\353\257\270\354\205\230-3-\354\244\221\353\263\265\353\220\234_\353\257\270\354\205\230.png"
new file mode 100644
index 0000000..3d9e30a
Binary files /dev/null and "b/mission/chapter05/images/\353\257\270\354\205\230-3-\354\244\221\353\263\265\353\220\234_\353\257\270\354\205\230.png" differ
diff --git "a/mission/chapter05/images/\353\257\270\354\205\230-4-\353\217\204\354\240\204\355\225\240\354\210\230\354\227\206\353\212\224_\353\257\270\354\205\230.png" "b/mission/chapter05/images/\353\257\270\354\205\230-4-\353\217\204\354\240\204\355\225\240\354\210\230\354\227\206\353\212\224_\353\257\270\354\205\230.png"
new file mode 100644
index 0000000..dbd6bae
Binary files /dev/null and "b/mission/chapter05/images/\353\257\270\354\205\230-4-\353\217\204\354\240\204\355\225\240\354\210\230\354\227\206\353\212\224_\353\257\270\354\205\230.png" differ
diff --git "a/mission/chapter05/images/\353\257\270\354\205\230-4-\354\241\264\354\236\254\355\225\230\354\247\200\354\225\212\353\212\224_\353\257\270\354\205\230.png" "b/mission/chapter05/images/\353\257\270\354\205\230-4-\354\241\264\354\236\254\355\225\230\354\247\200\354\225\212\353\212\224_\353\257\270\354\205\230.png"
new file mode 100644
index 0000000..6fd7460
Binary files /dev/null and "b/mission/chapter05/images/\353\257\270\354\205\230-4-\354\241\264\354\236\254\355\225\230\354\247\200\354\225\212\353\212\224_\353\257\270\354\205\230.png" differ
diff --git "a/mission/chapter05/images/\353\257\270\354\205\230-4-\354\247\204\355\226\211\354\244\221_\354\227\205\353\215\260\354\235\264\355\212\270.png" "b/mission/chapter05/images/\353\257\270\354\205\230-4-\354\247\204\355\226\211\354\244\221_\354\227\205\353\215\260\354\235\264\355\212\270.png"
new file mode 100644
index 0000000..70e6b46
Binary files /dev/null and "b/mission/chapter05/images/\353\257\270\354\205\230-4-\354\247\204\355\226\211\354\244\221_\354\227\205\353\215\260\354\235\264\355\212\270.png" differ
diff --git a/mission/chapter05/mission.md b/mission/chapter05/mission.md
new file mode 100644
index 0000000..21d6a8b
--- /dev/null
+++ b/mission/chapter05/mission.md
@@ -0,0 +1,364 @@
+### ๐ฅ ๋ฏธ์
+---
+> GitHub ์ ์ฅ์ ์ฃผ์
+> [https://github.com/asjasj3964/UMC-7th-Node.js-Workbook](https://github.com/asjasj3964/UMC-7th-Node.js-Workbook)
+
+
๋ชจ๋ ์ฝ๋์ ๋ํด ์ค๋ช
์ ํ๋ฉด ๋๋ฌด ๊ธธ์ด์ง ๊ฒ ๊ฐ์ ๋ฐ์ดํฐ ์ฝ์
๋ฐ ์กฐํ ๊ณผ์ ์ด ์๋ Repository ํจ์๋ง ๊ฐ์ ธ์์ต๋๋ค.
+
+1. ํน์ ์ง์ญ์ ์๋น ์ถ๊ฐํ๊ธฐ API
+ - Repository ํจ์
+ ```javascript
+ // ์๋น ๋ฐ์ดํฐ ์ฝ์
(์๋น ๋ฑ๋ก) & ์๋น ID ๋ฐํ
+ export const addRestaurant = async(data) => {
+ const conn = await pool.getConnection();
+ try{
+ // ํด๋น ์์น์ ์๋น(์ค๋ณต๋ ์๋น)์ ์ฌ์ฉ์๊ฐ ์๋์ง ํ์ธ
+ const [confirm] = await pool.query(
+ `SELECT EXISTS(SELECT 1 FROM restaurant WHERE region_id = ? and restaurant_name = ?) as isExistRestaurant`,
+ [data.region, data.name]
+ );
+ if (confirm[0].isExistRestaurant) { // ์ค๋ณต๋ ์๋น์ผ ๊ฒฝ์ฐ
+ return null;
+ }
+ // ์๋น ์์ฑ
+ const [result] = await pool.query(
+ `INSERT INTO restaurant (ceo_id, region_id, restaurant_name, introduction, start_time, end_time) VALUES (?, ?, ?, ?, ?, ?);`,
+ [
+ data.ceoId,
+ data.region,
+ data.name,
+ data.introduction,
+ data.startTime,
+ data.endTime,
+ ]
+ ); // ์๋น ๋ฐ์ดํฐ ์ฝ์
+ return result.insertId;
+ }catch(err){
+ throw new Error(`
+ ๐ซ ์ค๋ฅ ๋ฐ์ ๐ซ
+ ์์ฒญ ํ๋ผ๋ฏธํฐ ํ์ธ ๋ฐ๋ (${err})
+ `);
+ }finally{
+ conn.release();
+ }
+ }
+
+ // ์๋น ID๋ก ์๋น ์กฐํ
+ export const getRestaurant = async(restaurantId) => {
+ const conn = await pool.getConnection();
+ try{
+ const [restaurant] = await pool.query(
+ `SELECT * FROM restaurant WHERE id = ?`,
+ restaurantId
+ )
+ console.log(restaurant);
+ if (restaurant.length == 0){
+ return null;
+ }
+ return restaurant;
+ }catch (err){
+ throw new Error(`
+ ๐ซ ์ค๋ฅ ๋ฐ์ ๐ซ
+ ์์ฒญ ํ๋ผ๋ฏธํฐ ํ์ธ ๋ฐ๋ (${err})
+ `);
+ }finally{
+ conn.release();
+ }
+ }
+
+ // ์๋น - ์ง์ญ ๋ฐํ
+ export const getrestaurantRegionByRestaurantId = async (restaurantId) => {
+ const conn = await pool.getConnection();
+ try{
+ const [region] = await pool.query(`
+ SELECT rest.id, rest.region_id, re.address
+ FROM restaurant rest JOIN region re ON rest.region_id = re.id
+ WHERE rest.id = ?`,
+ restaurantId
+ ); // restaurant ํ
์ด๋ธ๊ณผ region ํ
์ด๋ธ์ joinํด ํด๋น ์๋น์ ์์น ์ ๋ณด๋ฅผ ์กฐํํ๋ค.
+ return region;
+ }catch(err){
+ throw new Error(`
+ ๐ซ ์ค๋ฅ ๋ฐ์ ๐ซ
+ ์์ฒญ ํ๋ผ๋ฏธํฐ ํ์ธ ๋ฐ๋ (${err})
+ `);
+ }finally{
+ conn.release();
+ }
+ }
+
+ // ์๋น ceo ๋ฐํ
+ export const getrestaurantCeoByCeoId = async (restaurantCeoId) => {
+ const conn = await pool.getConnection();
+ try{
+ const [restaurantCeo] = await pool.query(`
+ SELECT rest.id, rest.ceo_id, mem.member_name
+ FROM restaurant rest JOIN member mem ON rest.ceo_id = mem.id
+ WHERE rest.ceo_id = ?`,
+ restaurantCeoId
+ ); // restaurant ํ
์ด๋ธ๊ณผ member ํ
์ด๋ธ์ joinํด ํด๋น ์๋น์ CEO(ํ์) ์ ๋ณด๋ฅผ ์กฐํํ๋ค.
+ return restaurantCeo;
+ }catch(err){
+ throw new Error(`
+ ๐ซ ์ค๋ฅ ๋ฐ์ ๐ซ
+ ์์ฒญ ํ๋ผ๋ฏธํฐ ํ์ธ ๋ฐ๋ (${err})
+ `);
+ }finally{
+ conn.release();
+ }
+ }
+ ```
+ - ํ
์คํธ ๊ฒฐ๊ณผ
+ ![์๋น ๋ฑ๋ก](images/๋ฏธ์
-1-์๋น_๋ฑ๋ก.png)
+ - ์ด๋ฏธ ๋ฑ๋ก๋ ์๋น์ ๋ฑ๋กํ ๊ฒฝ์ฐ
+ ![์ค๋ณต๋ ์๋น](images/๋ฏธ์
-1-์ค๋ณต๋_์๋น.png
+ )
+ - ๊ฐ์ ์์น์ ์ด๋ฆ์ ์๋น์ ์ค๋ณต ๋ฑ๋กํ ์ ์๊ฒ ํ์๋ค.
+
+2. ์๋น์ ๋ฆฌ๋ทฐ ์ถ๊ฐํ๊ธฐ API
+ - Repository ํจ์
+ ```javascript
+ // ๋ฆฌ๋ทฐ ๋ฐ์ดํฐ ์ฝ์
(๋ฆฌ๋ทฐ ๋ฑ๋ก) & ๋ฆฌ๋ทฐ ID ๋ฐํ
+ export const addReview = async(data) => {
+ const conn = await pool.getConnection();
+ try{
+ // ๋ฆฌ๋ทฐ๋ฅผ ์ถ๊ฐํ๋ ค๋ ๊ฐ๊ฒ๊ฐ ์กด์ฌํ๋์ง ๊ฒ์ฆ
+ const [confirm] = await pool.query(
+ `SELECT EXISTS(SELECT 1 FROM restaurant WHERE id = ?)as isExistRestaurant;`,
+ data.restaurant
+ );
+ if (!confirm[0].isExistRestaurant){ // ๋ฑ๋กํ๋ ค๋ ์๋น์ด ์กด์ฌํ์ง ์์ ๊ฒฝ์ฐ
+ return null;
+ }
+ // ๋ฆฌ๋ทฐ ์์ฑ
+ const [result] = await pool.query(
+ `INSERT INTO review (member_id, restaurant_id, rating, content) VALUES (?, ?, ?, ?);`,
+ [
+ data.member,
+ data.restaurant,
+ data.rating,
+ data.content
+ ]
+ ); // ๋ฆฌ๋ทฐ ๋ฐ์ดํฐ ์ฝ์
+ return result.insertId;
+ }catch(err){
+ throw new Error(`
+ ๐ซ ์ค๋ฅ ๋ฐ์ ๐ซ
+ ์์ฒญ ํ๋ผ๋ฏธํฐ ํ์ธ ๋ฐ๋ (${err})
+ `);
+ }finally{
+ conn.release();
+ }
+ }
+
+ // ๋ฆฌ๋ทฐ ID๋ก ๋ฆฌ๋ทฐ ์กฐํ
+ export const getReview = async(reviewId) => {
+ const conn = await pool.getConnection();
+ try{
+ const[review] = await pool.query(
+ `SELECT * FROM review WHERE id = ?`,
+ reviewId
+ )
+ console.log(review);
+ if (review.length == 0){
+ return null;
+ }
+ return review;
+ }catch (err){
+ throw new Error(`
+ ๐ซ ์ค๋ฅ ๋ฐ์ ๐ซ
+ ์์ฒญ ํ๋ผ๋ฏธํฐ ํ์ธ ๋ฐ๋ (${err})
+ `);
+ }finally{
+ conn.release();
+ }
+ }
+
+ // ๋ฆฌ๋ทฐ ID๋ก ๋ฆฌ๋ทฐ ๋ฑ๋กํ ์๋น์ ์ด๋ฆ ์์๋ด๊ธฐ
+ export const getReviewRestaurantByReviewId = async(reviewId) => {
+ const conn = await pool.getConnection();
+ try{
+ const [review] = await pool.query(`
+ SELECT re.id, re.restaurant_id, rest.restaurant_name
+ FROM review as re JOIN restaurant rest ON re.restaurant_id = rest.id
+ WHERE re.id = ?`,
+ reviewId
+ ); // review ํ
์ด๋ธ๊ณผ restaurant ํ
์ด๋ธ์ joinํด ํด๋น ๋ฆฌ๋ทฐ์ ์๋น ์ ๋ณด๋ฅผ ์กฐํํ๋ค.
+ return review;
+ }catch(err){
+ throw new Error(`
+ ๐ซ ์ค๋ฅ ๋ฐ์ ๐ซ
+ ์์ฒญ ํ๋ผ๋ฏธํฐ ํ์ธ ๋ฐ๋ (${err})
+ `);
+ }finally{
+ conn.release();
+ }
+ }
+
+ // ๋ฆฌ๋ทฐ ์์ฑ์ ID๋ก ์์ฑ์์ ์ด๋ฆ ์์๋ด๊ธฐ
+ export const getReviewWriterByWriterId = async(reviewWriterId) => {
+ const conn = await pool.getConnection();
+ try{
+ const [reviewWriter] = await pool.query(`
+ SELECT re.id, re.member_id, mem.member_name
+ FROM review as re JOIN member mem ON re.member_id = mem.id
+ WHERE re.member_id = ?`,
+ reviewWriterId
+ ); // review ํ
์ด๋ธ๊ณผ member ํ
์ด๋ธ์ joinํด ํด๋น ๋ฆฌ๋ทฐ์ ์์ฑ์(ํ์) ์ ๋ณด๋ฅผ ์กฐํํ๋ค.
+ return reviewWriter;
+ }catch(err){
+ throw new Error(`
+ ๐ซ ์ค๋ฅ ๋ฐ์ ๐ซ
+ ์์ฒญ ํ๋ผ๋ฏธํฐ ํ์ธ ๋ฐ๋ (${err})
+ `);
+ }finally{
+ conn.release();
+ }
+ }
+ ```
+ - ํ
์คํธ ๊ฒฐ๊ณผ
+ ![๋ฆฌ๋ทฐ ๋ฑ๋ก](images/๋ฏธ์
-2-๋ฆฌ๋ทฐ_๋ฑ๋ก.png)
+ - ์กด์ฌํ์ง ์๋ ์๋น์ ๋ฑ๋กํ ๊ฒฝ์ฐ
+ ![์กด์ฌํ์ง ์๋ ์๋น](images/๋ฏธ์
-2-์กด์ฌํ์ง์๋_์๋น.png)
+ - ์กด์ฌํ์ง ์๋ ID(150)์ ์๋น์ ๋ฆฌ๋ทฐ๋ฅผ ๋ฑ๋กํ ๊ฒฝ์ฐ ์๋ฌ๋ฅผ ๋ฐ์์์ผฐ๋ค.
+3. ์๋น์ ๋ฏธ์
์ถ๊ฐํ๊ธฐ API
+ - Repository ํจ์
+ ```javascript
+ // ๋ฏธ์
๋ฐ์ดํฐ ์ฝ์
(๋ฏธ์
๋ฑ๋ก) & ๋ฏธ์
ID ๋ฐํ
+ export const addMission = async(data) => {
+ const conn = await pool.getConnection();
+ try{
+ // ๋ฑ๋กํ๋ ค๋ ์๋น ID, ๋ฏธ์
์ด๋ฆ, ๋ฏธ์
๋ด์ฉ๊ณผ ๋ชจ๋ ์ผ์นํ๋ ์ค๋ณต ๋ฏธ์
์ด ์กด์ฌํ๋์ง ํ์ธ
+ const [confirm1] = await pool.query(
+ `SELECT EXISTS(SELECT 1 FROM mission WHERE restaurant_id = ? and mission_name = ? and introduction = ?) as isExistMission;`,
+ [data.restaurant, data.name, data.introduction]
+ );
+ // ๋ฑ๋กํ๋ ค๋ ์๋น์ด ์กด์ฌํ๋์ง ํ์ธ
+ const [confirm2] = await pool.query(
+ `SELECT EXISTS(SELECT 1 FROM restaurant WHERE id = ?) as isExistRestaurant;`,
+ data.restaurant
+ );
+ // ์ค๋ณต ๋ฏธ์
์ด ์๊ฑฐ๋ ์๋น์ด ์กด์ฌํ์ง ์์ ์
+ if (confirm1[0].isExistMission || (!confirm2[0].isExistRestaurant)){
+ return null;
+ }
+ // ๋ฏธ์
์์ฑ
+ const [result] = await pool.query(
+ `INSERT INTO mission (restaurant_id, mission_name, introduction, deadline, points) VALUES (?, ?, ?, ?, ?);`,
+ [data.restaurant, data.name, data.introduction, data.deadline, data.points]
+ ); // ๋ฏธ์
๋ฐ์ดํฐ ์ฝ์
+ return result.insertId;
+ }catch(err){
+ throw new Error(`
+ ๐ซ ์ค๋ฅ ๋ฐ์ ๐ซ
+ ์์ฒญ ํ๋ผ๋ฏธํฐ ํ์ธ ๋ฐ๋ (${err})
+ `);
+ }finally{
+ conn.release();
+ }
+ }
+
+ // ๋ฏธ์
ID๋ก ๋ฏธ์
์กฐํ
+ export const getMission = async(missionId) => {
+ const conn = await pool.getConnection();
+ try{
+ const[mission] = await pool.query(
+ `SELECT * FROM mission WHERE id = ?`,
+ missionId
+ );
+ console.log(mission);
+ if (mission.length == 0){
+ return null;
+ }
+ return mission;
+ }catch (err){
+ throw new Error(`
+ ๐ซ ์ค๋ฅ ๋ฐ์ ๐ซ
+ ์์ฒญ ํ๋ผ๋ฏธํฐ ํ์ธ ๋ฐ๋ (${err})
+ `);
+ }finally{
+ conn.release();
+ }
+ }
+
+ // ๋ฏธ์
ID๋ก ์๋น ์กฐํ
+ export const getRestaurantByMissionId = async(missionId) => {
+ const conn = await pool.getConnection();
+ try{
+ const [restaurant] = await pool.query(`
+ SELECT mi.id, mi.restaurant_id, rest.restaurant_name
+ FROM mission mi JOIN restaurant rest ON mi.restaurant_id = rest.id
+ WHERE mi.id = ?`,
+ missionId
+ ); // mission ํ
์ด๋ธ๊ณผ restaurant ํ
์ด๋ธ์ joinํด ํด๋น ํ์์ ์๋น ์ ๋ณด๋ฅผ ์กฐํํ๋ค.
+ return restaurant;
+ }catch(err){
+ throw new Error(`
+ ๐ซ ์ค๋ฅ ๋ฐ์ ๐ซ
+ ์์ฒญ ํ๋ผ๋ฏธํฐ ํ์ธ ๋ฐ๋ (${err})
+ `);
+ }finally{
+ conn.release();
+ }
+ }
+ ```
+ - ํ
์คํธ ๊ฒฐ๊ณผ
+ ![๋ฏธ์
๋ฑ๋ก](images/๋ฏธ์
-3-๋ฏธ์
_๋ฑ๋ก.png)
+ - ์กด์ฌํ์ง ์๋ ์๋น์ ๋ฏธ์
์ ๋ฑ๋กํ ๊ฒฝ์ฐ
+ ![์กด์ฌํ์ง ์๋ ์๋น](images/๋ฏธ์
-3-์กด์ฌํ์ง์๋_์๋น.png)
+ - ์กด์ฌํ์ง ์๋ ID(140)์ ์๋น์ ๋ฏธ์
์ ๋ฑ๋กํ ๊ฒฝ์ฐ ์๋ฌ๋ฅผ ๋ฐ์์์ผฐ๋ค.
+ - ์ด๋ฏธ ๋ฑ๋ก๋ ๋ฏธ์
์ ๋ฑ๋กํ ๊ฒฝ์ฐ
+ ![์ค๋ณต๋ ๋ฏธ์
](images/๋ฏธ์
-3-์ค๋ณต๋_๋ฏธ์
.png)
+ - ๊ฐ์ ์๋น, ๋ฏธ์
๋ช
, ๋ฏธ์
์ค๋ช
์ ๋ฏธ์
์ ์ค๋ณต ๋ฑ๋กํ ์ ์๊ฒ ํ์๋ค.
+4. ๊ฐ๊ฒ์ ๋ฏธ์
์ ๋์ ์ค์ธ ๋ฏธ์
์ ์ถ๊ฐํ๊ธฐ API
+ - Repository ํจ์
+ ```javascript
+ // ํน์ ๋ฏธ์
์ํ ์
๋ฐ์ดํธ(์งํ X -> ์งํ ์ค)
+ export const updateMissionStatus = async(missionId) => {
+ const conn = await pool.getConnection();
+ try{
+ // ์
๋ฐ์ดํธํ ๋ฏธ์
์ด ์กด์ฌํ๋์ง ๋ฏธ์
ID๋ก ์กฐํํ์ฌ ํ์ธ
+ const [confirm1] = await pool.query(
+ `SELECT EXISTS(SELECT 1 FROM mission WHERE id = ?) as isExistMission;`,
+ missionId
+ );
+ // ํด๋น ๋ฏธ์
์ ์ํ๋ฅผ ํ์ธํ๊ธฐ ์ํด status ์ ํ
+ const [confirm2] = await pool.query(
+ `SELECT status FROM mission WHERE id = ?`,
+ missionId
+ );
+ // ํด๋น ๋ฏธ์
์ด ์กด์ฌํ์ง ์๊ฑฐ๋ ์ํ๊ฐ ์งํ X๊ฐ ์๋ ๊ฒฝ์ฐ
+ if((!confirm1[0].isExistMission) || (confirm2[0].status != 0)){
+ return null;
+ }
+ // ํด๋น ๋ฏธ์
์ status ๊ฐ์ 1(์งํ ์ค)๋ก ๋ณ๊ฒฝ
+ await pool.query(`
+ UPDATE mission SET status = 1 WHERE id = ?;`,
+ missionId
+ );
+ // ๋ฏธ์
ID๋ก ์กฐํํด์ ํด๋น ๋ฏธ์
์ ๋ณด๋ฅผ ๋ชจ๋ ์กฐํํ๋ค.
+ const [mission] = await pool.query(`
+ SELECT * FROM mission WHERE id = ?;`,
+ missionId
+ )
+ return mission;
+ }catch(err){
+ throw new Error(`
+ ๐ซ ์ค๋ฅ ๋ฐ์ ๐ซ
+ ์์ฒญ ํ๋ผ๋ฏธํฐ ํ์ธ ๋ฐ๋ (${err})
+ `);
+ }finally{
+ conn.release();
+ }
+ }
+ ```
+ - ํ
์คํธ ๊ฒฐ๊ณผ
+ ![๋ฏธ์
์งํ ์ค์ผ๋ก ์
๋ฐ์ดํธ](images/๋ฏธ์
-4-์งํ์ค_์
๋ฐ์ดํธ.png)
+ - ๋ฏธ์
3์์ ๋ฑ๋กํ ๋ฏธ์
(๋์ ํ๊ธฐ ์ ์ ์ํ(0))์ ์งํ ์ค์ผ๋ก ์
๋ฐ์ดํธ ํ์๋ค.
+ - ๋์ ํ ์ ์๋ ๋ฏธ์
(์ด๋ฏธ ์งํ ์ค์ด๊ฑฐ๋ ์๋ฃํ ๋ฏธ์
)์ ๊ฒฝ์ฐ
+ ![๋์ ํ ์ ์๋ ๋ฏธ์
](images/๋ฏธ์
-4-๋์ ํ ์์๋_๋ฏธ์
.png)
+ - ๋ฐฉ๊ธ ์ ์ ์
๋ฐ์ดํธ ์ํจ ๋ฏธ์
(์ด๋ฏธ ์งํ ์ค์ธ ์ํ)์ ๋ ํ
์คํธ ํด๋ณด์๋ค.
+ - ๋ฏธ์
์ ์ํ(status)๊ฐ 0(์งํํ๊ธฐ ์ )์ด ์๋ ๊ฒฝ์ฐ์ ์๋ฌ๋ฅผ ๋ฐ์์์ผฐ๋ค.
+ - ์กด์ฌํ์ง ์๋ ๋ฏธ์
์ ์
๋ฐ์ดํธํ ๊ฒฝ์ฐ
+ ![์กด์ฌํ์ง ์๋ ๋ฏธ์
](images/๋ฏธ์
-4-์กด์ฌํ์ง์๋_๋ฏธ์
.png)
+ - ์
๋ฐ์ดํธ ํ๋ ค๋ ๋ฏธ์
์ ID๋ก ์กฐํํ์ ๋ ์กด์ฌํ์ง ์์ ๊ฒฝ์ฐ์ ์๋ฌ๋ฅผ ๋ฐ์์์ผฐ๋ค.
\ No newline at end of file
diff --git "a/mission/chapter06/images/\353\257\270\354\205\230-1-1-\354\213\235\353\213\271_\353\223\261\353\241\235.png" "b/mission/chapter06/images/\353\257\270\354\205\230-1-1-\354\213\235\353\213\271_\353\223\261\353\241\235.png"
new file mode 100644
index 0000000..70d6e7a
Binary files /dev/null and "b/mission/chapter06/images/\353\257\270\354\205\230-1-1-\354\213\235\353\213\271_\353\223\261\353\241\235.png" differ
diff --git "a/mission/chapter06/images/\353\257\270\354\205\230-1-1-\354\244\221\353\263\265\353\220\234_\354\213\235\353\213\271.png" "b/mission/chapter06/images/\353\257\270\354\205\230-1-1-\354\244\221\353\263\265\353\220\234_\354\213\235\353\213\271.png"
new file mode 100644
index 0000000..40eb013
Binary files /dev/null and "b/mission/chapter06/images/\353\257\270\354\205\230-1-1-\354\244\221\353\263\265\353\220\234_\354\213\235\353\213\271.png" differ
diff --git "a/mission/chapter06/images/\353\257\270\354\205\230-1-2-\353\246\254\353\267\260_\353\223\261\353\241\235.png" "b/mission/chapter06/images/\353\257\270\354\205\230-1-2-\353\246\254\353\267\260_\353\223\261\353\241\235.png"
new file mode 100644
index 0000000..207bc23
Binary files /dev/null and "b/mission/chapter06/images/\353\257\270\354\205\230-1-2-\353\246\254\353\267\260_\353\223\261\353\241\235.png" differ
diff --git "a/mission/chapter06/images/\353\257\270\354\205\230-1-2-\354\241\264\354\236\254\355\225\230\354\247\200\354\225\212\353\212\224_\354\213\235\353\213\271.png" "b/mission/chapter06/images/\353\257\270\354\205\230-1-2-\354\241\264\354\236\254\355\225\230\354\247\200\354\225\212\353\212\224_\354\213\235\353\213\271.png"
new file mode 100644
index 0000000..382bb74
Binary files /dev/null and "b/mission/chapter06/images/\353\257\270\354\205\230-1-2-\354\241\264\354\236\254\355\225\230\354\247\200\354\225\212\353\212\224_\354\213\235\353\213\271.png" differ
diff --git "a/mission/chapter06/images/\353\257\270\354\205\230-1-3-\353\257\270\354\205\230_\353\223\261\353\241\235.png" "b/mission/chapter06/images/\353\257\270\354\205\230-1-3-\353\257\270\354\205\230_\353\223\261\353\241\235.png"
new file mode 100644
index 0000000..463f33d
Binary files /dev/null and "b/mission/chapter06/images/\353\257\270\354\205\230-1-3-\353\257\270\354\205\230_\353\223\261\353\241\235.png" differ
diff --git "a/mission/chapter06/images/\353\257\270\354\205\230-1-3-\354\241\264\354\236\254\355\225\230\354\247\200\354\225\212\353\212\224_\354\213\235\353\213\271.png" "b/mission/chapter06/images/\353\257\270\354\205\230-1-3-\354\241\264\354\236\254\355\225\230\354\247\200\354\225\212\353\212\224_\354\213\235\353\213\271.png"
new file mode 100644
index 0000000..5e53076
Binary files /dev/null and "b/mission/chapter06/images/\353\257\270\354\205\230-1-3-\354\241\264\354\236\254\355\225\230\354\247\200\354\225\212\353\212\224_\354\213\235\353\213\271.png" differ
diff --git "a/mission/chapter06/images/\353\257\270\354\205\230-1-3-\354\244\221\353\263\265\353\220\234_\353\257\270\354\205\230.png" "b/mission/chapter06/images/\353\257\270\354\205\230-1-3-\354\244\221\353\263\265\353\220\234_\353\257\270\354\205\230.png"
new file mode 100644
index 0000000..75700a7
Binary files /dev/null and "b/mission/chapter06/images/\353\257\270\354\205\230-1-3-\354\244\221\353\263\265\353\220\234_\353\257\270\354\205\230.png" differ
diff --git "a/mission/chapter06/images/\353\257\270\354\205\230-1-4-\353\217\204\354\240\204\355\225\240\354\210\230\354\227\206\353\212\224_\353\257\270\354\205\230.png" "b/mission/chapter06/images/\353\257\270\354\205\230-1-4-\353\217\204\354\240\204\355\225\240\354\210\230\354\227\206\353\212\224_\353\257\270\354\205\230.png"
new file mode 100644
index 0000000..bce3a0c
Binary files /dev/null and "b/mission/chapter06/images/\353\257\270\354\205\230-1-4-\353\217\204\354\240\204\355\225\240\354\210\230\354\227\206\353\212\224_\353\257\270\354\205\230.png" differ
diff --git "a/mission/chapter06/images/\353\257\270\354\205\230-1-4-\353\257\270\354\205\230_\354\247\204\355\226\211\354\244\221_\354\227\205\353\215\260\354\235\264\355\212\270.png" "b/mission/chapter06/images/\353\257\270\354\205\230-1-4-\353\257\270\354\205\230_\354\247\204\355\226\211\354\244\221_\354\227\205\353\215\260\354\235\264\355\212\270.png"
new file mode 100644
index 0000000..0f3671e
Binary files /dev/null and "b/mission/chapter06/images/\353\257\270\354\205\230-1-4-\353\257\270\354\205\230_\354\247\204\355\226\211\354\244\221_\354\227\205\353\215\260\354\235\264\355\212\270.png" differ
diff --git "a/mission/chapter06/images/\353\257\270\354\205\230-1-4-\354\241\264\354\236\254\355\225\230\354\247\200\354\225\212\353\212\224_\353\257\270\354\205\230.png" "b/mission/chapter06/images/\353\257\270\354\205\230-1-4-\354\241\264\354\236\254\355\225\230\354\247\200\354\225\212\353\212\224_\353\257\270\354\205\230.png"
new file mode 100644
index 0000000..632c6c8
Binary files /dev/null and "b/mission/chapter06/images/\353\257\270\354\205\230-1-4-\354\241\264\354\236\254\355\225\230\354\247\200\354\225\212\353\212\224_\353\257\270\354\205\230.png" differ
diff --git "a/mission/chapter06/images/\353\257\270\354\205\230-1-4-\354\247\204\355\226\211\354\244\221_\354\227\205\353\215\260\354\235\264\355\212\270\354\240\204\354\235\230_\353\257\270\354\205\230.png" "b/mission/chapter06/images/\353\257\270\354\205\230-1-4-\354\247\204\355\226\211\354\244\221_\354\227\205\353\215\260\354\235\264\355\212\270\354\240\204\354\235\230_\353\257\270\354\205\230.png"
new file mode 100644
index 0000000..3cdad9f
Binary files /dev/null and "b/mission/chapter06/images/\353\257\270\354\205\230-1-4-\354\247\204\355\226\211\354\244\221_\354\227\205\353\215\260\354\235\264\355\212\270\354\240\204\354\235\230_\353\257\270\354\205\230.png" differ
diff --git "a/mission/chapter06/images/\353\257\270\354\205\230-2-\355\212\271\354\240\225\355\232\214\354\233\220-\353\246\254\353\267\260\353\252\251\353\241\235.png" "b/mission/chapter06/images/\353\257\270\354\205\230-2-\355\212\271\354\240\225\355\232\214\354\233\220-\353\246\254\353\267\260\353\252\251\353\241\235.png"
new file mode 100644
index 0000000..30d789f
Binary files /dev/null and "b/mission/chapter06/images/\353\257\270\354\205\230-2-\355\212\271\354\240\225\355\232\214\354\233\220-\353\246\254\353\267\260\353\252\251\353\241\235.png" differ
diff --git "a/mission/chapter06/images/\353\257\270\354\205\230-3-\355\212\271\354\240\225\352\260\200\352\262\214_\353\257\270\354\205\230\353\252\251\353\241\235.png" "b/mission/chapter06/images/\353\257\270\354\205\230-3-\355\212\271\354\240\225\352\260\200\352\262\214_\353\257\270\354\205\230\353\252\251\353\241\235.png"
new file mode 100644
index 0000000..fce1ce0
Binary files /dev/null and "b/mission/chapter06/images/\353\257\270\354\205\230-3-\355\212\271\354\240\225\352\260\200\352\262\214_\353\257\270\354\205\230\353\252\251\353\241\235.png" differ
diff --git "a/mission/chapter06/images/\353\257\270\354\205\230-4-\355\212\271\354\240\225\355\232\214\354\233\220_\354\247\204\355\226\211\354\244\221\354\235\270_\353\257\270\354\205\230\353\252\251\353\241\235.png" "b/mission/chapter06/images/\353\257\270\354\205\230-4-\355\212\271\354\240\225\355\232\214\354\233\220_\354\247\204\355\226\211\354\244\221\354\235\270_\353\257\270\354\205\230\353\252\251\353\241\235.png"
new file mode 100644
index 0000000..8ef5fa3
Binary files /dev/null and "b/mission/chapter06/images/\353\257\270\354\205\230-4-\355\212\271\354\240\225\355\232\214\354\233\220_\354\247\204\355\226\211\354\244\221\354\235\270_\353\257\270\354\205\230\353\252\251\353\241\235.png" differ
diff --git "a/mission/chapter06/images/\353\257\270\354\205\230-5-\353\257\270\354\205\230_\354\247\204\355\226\211\354\231\204\353\243\214_\354\227\205\353\215\260\354\235\264\355\212\270.png" "b/mission/chapter06/images/\353\257\270\354\205\230-5-\353\257\270\354\205\230_\354\247\204\355\226\211\354\231\204\353\243\214_\354\227\205\353\215\260\354\235\264\355\212\270.png"
new file mode 100644
index 0000000..b411f4c
Binary files /dev/null and "b/mission/chapter06/images/\353\257\270\354\205\230-5-\353\257\270\354\205\230_\354\247\204\355\226\211\354\231\204\353\243\214_\354\227\205\353\215\260\354\235\264\355\212\270.png" differ
diff --git "a/mission/chapter06/images/\353\257\270\354\205\230-5-\354\231\204\353\243\214\355\225\240\354\210\230\354\227\206\353\212\224_\353\257\270\354\205\230.png" "b/mission/chapter06/images/\353\257\270\354\205\230-5-\354\231\204\353\243\214\355\225\240\354\210\230\354\227\206\353\212\224_\353\257\270\354\205\230.png"
new file mode 100644
index 0000000..7b8e17a
Binary files /dev/null and "b/mission/chapter06/images/\353\257\270\354\205\230-5-\354\231\204\353\243\214\355\225\240\354\210\230\354\227\206\353\212\224_\353\257\270\354\205\230.png" differ
diff --git "a/mission/chapter06/images/\353\257\270\354\205\230-5-\354\241\264\354\236\254\355\225\230\354\247\200\354\225\212\353\212\224_\353\257\270\354\205\230.png" "b/mission/chapter06/images/\353\257\270\354\205\230-5-\354\241\264\354\236\254\355\225\230\354\247\200\354\225\212\353\212\224_\353\257\270\354\205\230.png"
new file mode 100644
index 0000000..fb4713e
Binary files /dev/null and "b/mission/chapter06/images/\353\257\270\354\205\230-5-\354\241\264\354\236\254\355\225\230\354\247\200\354\225\212\353\212\224_\353\257\270\354\205\230.png" differ
diff --git "a/mission/chapter06/images/\353\257\270\354\205\230-5-\354\247\204\355\226\211\354\231\204\353\243\214_\354\227\205\353\215\260\354\235\264\355\212\270\354\240\204\354\235\230_\353\257\270\354\205\230.png" "b/mission/chapter06/images/\353\257\270\354\205\230-5-\354\247\204\355\226\211\354\231\204\353\243\214_\354\227\205\353\215\260\354\235\264\355\212\270\354\240\204\354\235\230_\353\257\270\354\205\230.png"
new file mode 100644
index 0000000..9a33115
Binary files /dev/null and "b/mission/chapter06/images/\353\257\270\354\205\230-5-\354\247\204\355\226\211\354\231\204\353\243\214_\354\227\205\353\215\260\354\235\264\355\212\270\354\240\204\354\235\230_\353\257\270\354\205\230.png" differ
diff --git a/mission/chapter06/mission.md b/mission/chapter06/mission.md
new file mode 100644
index 0000000..9cc93e6
--- /dev/null
+++ b/mission/chapter06/mission.md
@@ -0,0 +1,483 @@
+### ๐ฅ ๋ฏธ์
+---
+> GitHub ์ ์ฅ์ ์ฃผ์
+> [https://github.com/asjasj3964/UMC-7th-Node.js-Workbook](https://github.com/asjasj3964/UMC-7th-Node.js-Workbook)
+
+1. 5์ฃผ์ฐจ ๋ ์์ฑํ๋ API๋ฅผ Prisma ORM์ ์ฌ์ฉํ์ฌ ๊ตฌํํ๊ธฐ
+ 1. ํน์ ์ง์ญ์ ์๋น ์ถ๊ฐํ๊ธฐ API
+ - Repository ํจ์ ์์
+ ```javascript
+ // ์๋น ๋ฐ์ดํฐ ์ฝ์
(์๋น ๋ฑ๋ก) & ์๋น ID ๋ฐํ
+ export const addRestaurant = async(data) => {
+ // ๋ฑ๋กํ๊ณ ์ ํ๋ ์๋น์ ์ด๋ฆ๊ณผ ์์น๊ฐ ๊ฐ์ ์ค๋ณต ์๋น์ด ์กด์ฌํ๋์ง ํ์ธ
+ const restaurant = await prisma.restaurant.findFirst({
+ where: {
+ name: data.name,
+ regionId: data.region
+ }
+ });
+ if (restaurant){ // ์ค๋ณต ์๋น์ผ ๊ฒฝ์ฐ
+ return null;
+ }
+ const created = await prisma.restaurant.create({
+ data: {
+ ...data,
+ region: {
+ connect: { id: data.region } // region ํ
์ด๋ธ ๊ด๊ณ ์ฐ๊ฒฐ
+ },
+ ceo: {
+ connect: { id: data.ceo } // ceo(member) ํ
์ด๋ธ ๊ด๊ณ ์ฐ๊ฒฐ
+ },
+ }
+ });
+ return created.id;
+ }
+
+ // ์๋น ID๋ก ์๋น ์กฐํ
+ export const getRestaurant = async(restaurantId) => {
+ const restaurant = await prisma.restaurant.findFirstOrThrow({
+ select: {
+ id: true,
+ ceo: true,
+ region: true,
+ name: true,
+ introduction: true,
+ startTime: true,
+ endTime: true,
+ totalRating: true,
+ },
+ where: { id: restaurantId }
+ });
+ const formattedRestaurant = {
+ ...restaurant,
+ id: restaurant.id.toString(),
+ region: {
+ id: restaurant.region.id.toString(),
+ address: restaurant.region.address,
+ },
+ ceo: {
+ id: restaurant.ceo.id.toString(),
+ name: restaurant.ceo.name.toString()
+ }
+ };
+ return formattedRestaurant;
+ }
+ ```
+ - ํ
์คํธ ๊ฒฐ๊ณผ
+ ![์๋น ๋ฑ๋ก](images/๋ฏธ์
-1-1-์๋น_๋ฑ๋ก.png)
+ - ์ด๋ฏธ ๋ฑ๋ก๋ ์๋น์ ๋ฑ๋กํ ๊ฒฝ์ฐ
+ ![์ค๋ณต๋ ์๋น](images/๋ฏธ์
-1-1-์ค๋ณต๋_์๋น.png
+ )
+ - ๊ฐ์ ์์น์ ์ด๋ฆ์ ์๋น์ ์ค๋ณต ๋ฑ๋กํ ์ ์๊ฒ ํ์๋ค.
+
+ 2. ์๋น์ ๋ฆฌ๋ทฐ ์ถ๊ฐํ๊ธฐ API
+ - Repository ํจ์ ์์
+ ```javascript
+ // ๋ฆฌ๋ทฐ ๋ฐ์ดํฐ ์ฝ์
(๋ฆฌ๋ทฐ ๋ฑ๋ก) & ๋ฆฌ๋ทฐ ID ๋ฐํ
+ export const addReview = async(data) => {
+ // ๋ฆฌ๋ทฐ๋ฅผ ์ถ๊ฐํ๋ ค๋ ์๋น์ด ์กด์ฌํ๋์ง ๊ฒ์ฆ
+ const restaurant = await prisma.restaurant.findFirst( {
+ where: {
+ id: data.restaurant // ๋ฑ๋กํ ์๋น์ ID๋ฅผ ๊ฐ์ง ๊ฐ๊ฒ๊ฐ ์๋์ง ํ์ธ
+ }
+ })
+ if (restaurant == null){ // ํด๋น ์๋น์ด ์กด์ฌํ์ง ์๋ค๋ฉด
+ return null;
+ }
+ const created = await prisma.review.create({
+ data: {
+ ...data,
+ member: {
+ connect: { // member ํ
์ด๋ธ๊ณผ ๊ด๊ณ ์ฐ๊ฒฐ
+ id: data.member
+ }
+ },
+ restaurant:{
+ connect: { // restaurant ํ
์ด๋ธ๊ณผ ๊ด๊ณ ์ฐ๊ฒฐ
+ id: data.restaurant
+ }
+ }
+ }
+ });
+ return created.id;
+ }
+
+ // ๋ฆฌ๋ทฐ ID๋ก ๋ฆฌ๋ทฐ ์กฐํ
+ export const getReview = async(reviewId) => {
+ const review = await prisma.review.findFirstOrThrow({
+ select: {
+ id: true,
+ member: true,
+ restaurant: true,
+ rating: true,
+ content: true,
+ createdAt: true,
+ status: true
+ },
+ where: {
+ id: reviewId
+ }
+ });
+ const formattedReview = {
+ ...review,
+ id: review.id.toString(),
+ member: {
+ id: review.member.id.toString(),
+ name: review.member.name,
+ },
+ restaurant: {
+ id: review.restaurant.id.toString(),
+ name: review.restaurant.name,
+ },
+ }
+ return formattedReview;
+ }
+ ```
+ - ํ
์คํธ ๊ฒฐ๊ณผ
+ ![๋ฆฌ๋ทฐ ๋ฑ๋ก](images/๋ฏธ์
-1-2-๋ฆฌ๋ทฐ_๋ฑ๋ก.png)
+ - ์กด์ฌํ์ง ์๋ ์๋น์ ๋ฑ๋กํ ๊ฒฝ์ฐ
+ ![์กด์ฌํ์ง ์๋ ์๋น](images/๋ฏธ์
-1-2-์กด์ฌํ์ง์๋_์๋น.png)
+ - ์กด์ฌํ์ง ์๋ ID(100)์ ์๋น์ ๋ฆฌ๋ทฐ๋ฅผ ๋ฑ๋กํ ๊ฒฝ์ฐ ์๋ฌ๋ฅผ ๋ฐ์์์ผฐ๋ค.
+ 3. ์๋น์ ๋ฏธ์
์ถ๊ฐํ๊ธฐ API
+ - Repository ํจ์ ์์
+ ```javascript
+ // ๋ฏธ์
๋ฐ์ดํฐ ์ฝ์
(๋ฏธ์
๋ฑ๋ก) & ๋ฏธ์
ID ๋ฐํ
+ export const addMission = async(data) => {
+ // ๋ฑ๋กํ๋ ค๋ ์๋น ID, ๋ฏธ์
์ด๋ฆ, ๋ฏธ์
๋ด์ฉ๊ณผ ๋ชจ๋ ์ผ์นํ๋ ์ค๋ณต ๋ฏธ์
์ด ์กด์ฌํ๋์ง ํ์ธ
+ const mission = await prisma.mission.findFirst({
+ where: {
+ restaurantId: data.restaurant,
+ name: data.name,
+ introduction: data.introduction
+ }
+ });
+ // ๋ฑ๋กํ๋ ค๋ ์๋น์ด ์กด์ฌํ๋์ง ํ์ธ
+ const restaurant = await prisma.restaurant.findFirst({
+ where: {
+ id: data.restaurant
+ }
+ });
+ if (mission != null || restaurant == null){ // ์ค๋ณต ๋ฏธ์
์ด ์๊ฑฐ๋ ์๋น์ด ์กด์ฌํ์ง ์์ ์
+ return null;
+ }
+ const created = await prisma.mission.create({ // ๋ฏธ์
์์ฑ
+ data: { // ์์ฑํ ๋ฐ์ดํฐ ๊ฐ์ฒด
+ ...data, // ๋งค๊ฐ๋ณ์๋ก ์ ๋ฌ ๋ฐ์ data ๊ฐ์ฒด์ ๋ชจ๋ ์์ฑ์ ๋ณต์ฌํ๋ค.
+ restaurant: {
+ connect: { id: data.restaurant } // restaurant ํ
์ด๋ธ๊ณผ ๊ด๊ณ ์ฐ๊ฒฐ
+ }
+ }
+ });
+ return created.id; // ์์ฑ๋ ๋ฏธ์
ID ๋ฐํ
+ }
+
+ // ๋ฏธ์
ID๋ก ๋ฏธ์
์กฐํ
+ export const getMission = async(missionId) => {
+ const mission = await prisma.mission.findFirstOrThrow({
+ select: {
+ id: true,
+ restaurant: true,
+ name: true,
+ introduction: true,
+ deadline: true,
+ points: true,
+ status: true
+ },
+ where: { id: missionId }});
+
+ const formattedMission = {
+ ...mission,
+ id: mission.id.toString(),
+ points: mission.points.toString(),
+ restaurant: {
+ id: mission.restaurant.id.toString(),
+ name: mission.restaurant.name,
+ },
+ };
+ console.log(formattedMission);
+ return formattedMission;
+ }
+ ```
+ - ํ
์คํธ ๊ฒฐ๊ณผ
+ ![๋ฏธ์
๋ฑ๋ก](images/๋ฏธ์
-1-3-๋ฏธ์
_๋ฑ๋ก.png)
+ - ์กด์ฌํ์ง ์๋ ์๋น์ ๋ฏธ์
์ ๋ฑ๋กํ ๊ฒฝ์ฐ
+ ![์กด์ฌํ์ง ์๋ ์๋น](images/๋ฏธ์
-1-3-์กด์ฌํ์ง์๋_์๋น.png)
+ - ์กด์ฌํ์ง ์๋ ID(100)์ ์๋น์ ๋ฏธ์
์ ๋ฑ๋กํ ๊ฒฝ์ฐ ์๋ฌ๋ฅผ ๋ฐ์์์ผฐ๋ค.
+ - ์ด๋ฏธ ๋ฑ๋ก๋ ๋ฏธ์
์ ๋ฑ๋กํ ๊ฒฝ์ฐ
+ ![์ค๋ณต๋ ๋ฏธ์
](images/๋ฏธ์
-1-3-์ค๋ณต๋_๋ฏธ์
.png)
+ - ๊ฐ์ ์๋น, ๋ฏธ์
๋ช
, ๋ฏธ์
์ค๋ช
์ ๋ฏธ์
์ ์ค๋ณต ๋ฑ๋กํ ์ ์๊ฒ ํ์๋ค.
+ 4. ๊ฐ๊ฒ์ ๋ฏธ์
์ ๋์ ์ค์ธ ๋ฏธ์
์ ์ถ๊ฐํ๊ธฐ API
+ - Repository ํจ์ ์์
+ ```javascript
+ // ํน์ ๋ฏธ์
์ํ ์
๋ฐ์ดํธ(์งํ X -> ์งํ ์ค)
+ export const updateMissionStatus = async(missionId) => {
+ // ์
๋ฐ์ดํธํ ๋ฏธ์
์ด ์กด์ฌํ๋์ง ํ์ธ
+ const mission = await prisma.mission.findFirst({
+ where: {
+ id: missionId
+ }
+ });
+ // ํด๋น ๋ฏธ์
์ ์ํ๋ฅผ ํ์ธํ๊ธฐ ์ํด status ์ ํ
+ const missionStatus = await prisma.mission.findFirst({
+ select: {
+ status: true
+ },
+ where: {
+ id: missionId
+ }
+ });
+ // ํด๋น ๋ฏธ์
์ด ์กด์ฌํ์ง ์๊ฑฐ๋ ์ํ๊ฐ ์งํ X๊ฐ ์๋ ๊ฒฝ์ฐ
+ if (mission == null || missionStatus.status != 0){
+ return null;
+ }
+ const missionUpdated = await prisma.mission.update({
+ where: {
+ id: missionId
+ },
+ data: {
+ status: 1 // status ๊ฐ์ 1(์งํ ์ค)๋ก ๋ณ๊ฒฝ
+ },
+ select: {
+ id: true,
+ restaurant: true,
+ name: true,
+ introduction: true,
+ deadline: true,
+ points: true,
+ status: true,
+ }
+ });
+ const formattedMission = {
+ ...missionUpdated,
+ id: missionUpdated.id.toString(),
+ points: missionUpdated.points.toString(),
+ restaurant: {
+ id: missionUpdated.restaurant.id.toString(),
+ name: missionUpdated.restaurant.name
+ },
+ };
+
+ return formattedMission;
+ }
+ ```
+ - ์
๋ฐ์ดํธํ๊ธฐ ์ (์งํ X)์ ๋ฐ์ดํฐ
+ ![์งํ X์ธ ๋ฏธ์
](images/๋ฏธ์
-1-4-์งํ์ค_์
๋ฐ์ดํธ์ ์_๋ฏธ์
.png)
+ - ํ
์คํธ ๊ฒฐ๊ณผ
+ ![๋ฏธ์
์งํ ์ค์ผ๋ก ์
๋ฐ์ดํธ](images/๋ฏธ์
-1-4-๋ฏธ์
_์งํ์ค_์
๋ฐ์ดํธ.png)
+ - ๋์ ํ ์ ์๋ ๋ฏธ์
(์ด๋ฏธ ์งํ ์ค์ด๊ฑฐ๋ ์๋ฃํ ๋ฏธ์
)์ ๊ฒฝ์ฐ
+ ![๋์ ํ ์ ์๋ ๋ฏธ์
](images/๋ฏธ์
-1-4-๋์ ํ ์์๋_๋ฏธ์
.png)
+ - ๋ฐฉ๊ธ ์ ์ ์
๋ฐ์ดํธ ์ํจ ๋ฏธ์
(์ด๋ฏธ ์งํ ์ค์ธ ์ํ)์ ๋ ํ
์คํธ ํด๋ณด์๋ค.
+ - ๋ฏธ์
์ ์ํ(status)๊ฐ 0(์งํํ๊ธฐ ์ )์ด ์๋ ๊ฒฝ์ฐ์ ์๋ฌ๋ฅผ ๋ฐ์์์ผฐ๋ค.
+ - ์กด์ฌํ์ง ์๋ ๋ฏธ์
์ ์
๋ฐ์ดํธํ ๊ฒฝ์ฐ
+ ![์กด์ฌํ์ง ์๋ ๋ฏธ์
](images/๋ฏธ์
-1-4-์กด์ฌํ์ง์๋_๋ฏธ์
.png)
+ - ์
๋ฐ์ดํธ ํ๋ ค๋ ๋ฏธ์
์ ID๋ก ์กฐํํ์ ๋ ์กด์ฌํ์ง ์์ ๊ฒฝ์ฐ์ ์๋ฌ๋ฅผ ๋ฐ์์์ผฐ๋ค.
+2. ํน์ ํ์์ด ์์ฑํ ๋ฆฌ๋ทฐ ๋ชฉ๋ก ์กฐํ API
+ - Repository ํจ์
+ ```javascript
+ // ํน์ ํ์์ ๋ชจ๋ ๋ฆฌ๋ทฐ ์กฐํ
+ export const getAllMemberReviews = async(memberId, cursor) => {
+ const reviews = await prisma.review.findMany({
+ select: {
+ id: true,
+ member: true, // ์ฐธ์กฐํ๋ member ํ
์ด๋ธ
+ restaurant: true, // ์ฐธ์กฐํ๋ restaurantn ํ
์ด๋ธ
+ rating: true,
+ content: true,
+ status: true
+ },
+ where: { memberId: memberId, id: { gt: cursor }},
+ // ๋ฆฌ๋ทฐ์ ID๊ฐ cursor๋ณด๋ค ํฐ ๋ ์ฝ๋๋ง ๊ฐ์ ธ์จ๋ค.
+ // gt: "greater than", ๊ฐ์ด cursor๋ณด๋ค ํฐ ๋ฐ์ดํฐ๋ฅผ ํํฐ๋งํ๋ค. (ํ์ด์ง ๊ตฌํ)
+ orderBy: { id: "asc"}, // ID ๊ธฐ์ค ์ค๋ฆ์ฐจ์ ์ ๋ ฌ
+ take: 5, // 5๊ฐ์ ๋ ์ฝ๋๋ง ์กฐํ
+ })
+
+ // review ๊ฐ์ฒด์ ํ๋ณํ (BigInt ์ฒ๋ฆฌ๋ฅผ ์ํจ)
+ const formattedReviews = reviews.map(review => ({ // reviews(DB์์ ์ถ์ถํ ๋ฆฌ๋ทฐ ๋ฐ์ดํฐ) ๋ฐฐ์ด์ map() ๋ฉ์๋๋ก ๊ฐ review ๊ฐ์ฒด ๋ณํ
+ ...review, // review ๊ฐ์ฒด์ ๋ชจ๋ ์์ฑ ๋ณต์ฌ
+ id: review.id.toString(),
+ // DB์ id ํ๋๊ฐ BigInt ํ์
์ผ๋ก ์ ์๋์ด ์๋๋ฐ
+ // javaScript์์ BigInt ํ์
์ JSON์ผ๋ก ๋ณํํ ์ ์์ด
+ // BigInt ํ์
์ id๋ฅผ ๋ฌธ์์ด๋ก ๋ณํํด์ฃผ์๋ค.
+ member: { // ์ฐธ์กฐํ๋ member ํ
์ด๋ธ์์ ์ถ์ถํ ์์ฑ
+ id: review.member.id.toString(),
+ name: review.member.name,
+ nickname: review.member.nickname,
+ birth: review.member.birth,
+ gender: review.member.gender,
+ location: review.member.location,
+ phoneNumber: review.member.phoneNumber
+ },
+ restaurant: { // ์ฐธ์กฐํ๋ restaurant ํ
์ด๋ธ์์ ์ถ์ถํ ์์ฑ
+ id: review.restaurant.id.toString(),
+ name: review.restaurant.name
+ },
+ }));
+
+ return formattedReviews;
+ }
+ ```
+ - ํ
์คํธ ๊ฒฐ๊ณผ
+ ![ํน์ ํ์์ ๋ฆฌ๋ทฐ ๋ชฉ๋ก](images/๋ฏธ์
-2-ํน์ ํ์-๋ฆฌ๋ทฐ๋ชฉ๋ก.png)
+3. ํน์ ์๋น์ ๋ฏธ์
๋ชฉ๋ก ์กฐํ API
+ - Repository ํจ์
+ ```javascript
+ // ํน์ ์๋น์ ๋ชจ๋ ๋ฏธ์
์กฐํ
+ export const getAllRestaurantMissions = async(restaurantId, cursor) => {
+ const missions = await prisma.mission.findMany({
+ select: {
+ id: true,
+ restaurant: true,
+ points: true,
+ name: true,
+ introduction: true,
+ status: true
+ },
+ where: { restaurantId: restaurantId, id: { gt: cursor }},
+ orderBy: { id: "asc" },
+ take: 5
+ })
+ const formattedMissions = missions.map(mission => ({
+ ...mission,
+ id: mission.id.toString(),
+ points: mission.points.toString(),
+ restaurant: {
+ id: mission.restaurant.id.toString(),
+ name: mission.restaurant.name
+ },
+ }));
+
+ return formattedMissions;
+ }
+ ```
+ - ํ
์คํธ ๊ฒฐ๊ณผ
+ ![ํน์ ์๋น์ ๋ฏธ์
๋ชฉ๋ก](images/๋ฏธ์
-3-ํน์ ๊ฐ๊ฒ_๋ฏธ์
๋ชฉ๋ก.png)
+4. ํน์ ํ์์ ์งํ ์ค์ธ ๋ฏธ์
๋ชฉ๋ก ์กฐํ API
+ - Repository ํจ์
+ ```javascript
+ // ํน์ ํ์์ ๋ชจ๋ ์งํ ์ค์ธ ๋ฏธ์
์กฐํ
+ export const getAllMemberMissions = async(memberId, cursor) => {
+ const memberMissions = await prisma.memberMission.findMany({
+ select: {
+ id: true,
+ member: true,
+ mission: true,
+ },
+ where: {
+ memberId: memberId,
+ mission: {
+ status: 1 // mission ๊ฐ์ฒด์ status๊ฐ 1(์งํ ์ค)์ธ ๋ฏธ์
๋ค๋ง ์กฐํ์จ๋ค.
+ },
+ id: {gt: cursor}
+ },
+ orderBy: {id: "asc"},
+ take: 5
+ })
+
+ const formattedMemberMissions = memberMissions.map(memberMission => ({
+ ...memberMission,
+ id: memberMission.id.toString(),
+ member: {
+ id: memberMission.member.id.toString(),
+ nickname: memberMission.member.nickname,
+ },
+ mission: {
+ id: memberMission.mission.id.toString(),
+ restaurantId: memberMission.mission.restaurantId.toString(),
+ name: memberMission.mission.name,
+ introduction: memberMission.mission.introduction,
+ points: memberMission.mission.points.toString(),
+ deadline: memberMission.mission.deadline,
+ status: memberMission.mission.status,
+
+ },
+ }));
+
+ return formattedMemberMissions;
+ }
+ ```
+ - ํ
์คํธ ๊ฒฐ๊ณผ
+ ![ํน์ ํ์์ ์งํ ์ค์ธ ๋ฏธ์
๋ชฉ๋ก](images/๋ฏธ์
-4-ํน์ ํ์_์งํ์ค์ธ_๋ฏธ์
๋ชฉ๋ก.png)
+5. ํน์ ํ์์ ์งํ ์ค์ธ ๋ฏธ์
์ ์งํ ์๋ฃ๋ก ๋ฐ๊พธ๊ธฐ API
+ - Repository ํจ์
+ ```javascript
+ // ํน์ ํ์์ ํน์ ๋ฏธ์
์ ์ํ ์
๋ฐ์ดํธ(์งํ ์ค -> ์งํ ์๋ฃ)
+ export const updateMissionCompleted = async(memberId, missionId) => {
+ // ํน์ ํ์์ ์ฃผ์ด์ง ํน์ ๋ฏธ์
์ด ์กด์ฌํ๋์ง ํ์ธ
+ const confirmMemberMission = await prisma.memberMission.findFirst({
+ where: {
+ missionId: missionId,
+ memberId: memberId
+ }
+ })
+
+ // ํด๋น ๋ฏธ์
์ ์ํ๋ฅผ ํ์ธํ๊ธฐ ์ํด status ์ ํ
+ const missionStatus = await prisma.mission.findFirst({
+ select: {
+ status: true
+ },
+ where: {
+ id: missionId
+ }
+ })
+
+ // ํด๋น ๋ฏธ์
์ด ์กด์ฌํ์ง ์๊ฑฐ๋ ๋ฏธ์
์ ์ํ๊ฐ 1(์งํ ์ค)์ด ์๋ ๊ฒฝ์ฐ ์๋ฌ ์ฒ๋ฆฌ
+ if (confirmMemberMission == null || missionStatus.status != 1){
+ return null;
+ }
+
+ const memberMission = await prisma.memberMission.update({
+ where: {
+ // update ๋ฉ์๋๋ ์ง์ ํ Unique key๋ฅผ ์ฌ์ฉํ์ฌ ๋ ์ฝ๋๋ฅผ ์ฐพ๊ธฐ ๋๋ฌธ์
+ // memberId, missionId๋ก ์ด๋ฃจ์ด์ง ๋ณตํฉ ๊ณ ์ ํค์ธ memberId_missionId_unique์ ๋ง๋ค์ด์ฃผ์๋ค.
+ memberId_missionId_unique: {
+ memberId: memberId,
+ missionId: missionId
+ }
+ },
+ data: { // ์์ ํ ๋ด์ฉ ์ ์
+ mission:{
+ update:{ // mission์ ํน์ ์์ฑ ์
๋ฐ์ดํธ
+ status: 2 // ์งํ ์๋ฃ๋ก ์
๋ฐ์ดํธ
+ }
+ }},
+ select: { // ๋ฐํํ ํน์ ์์ฑ ์ง์
+ id: true,
+ member: true,
+ mission: true,
+ }
+ })
+
+ const formattedMemberMissions = {
+ ...memberMission,
+ id: memberMission.id.toString(),
+ member: {
+ id: memberMission.member.id.toString(),
+ nickname: memberMission.member.nickname,
+ },
+ mission: {
+ id: memberMission.mission.id.toString(),
+ restaurantId: memberMission.mission.restaurantId.toString(),
+ name: memberMission.mission.name,
+ introduction: memberMission.mission.introduction,
+ points: memberMission.mission.points.toString(),
+ deadline: memberMission.mission.deadline,
+ status: memberMission.mission.status,
+ },
+ };
+
+ return formattedMemberMissions;
+ }
+ ```
+ - ์
๋ฐ์ดํธํ๊ธฐ ์ (์งํ ์ค)์ ๋ฐ์ดํฐ
+ ![์งํ ์ค์ธ ๋ฏธ์
](images/๋ฏธ์
-5-์งํ์๋ฃ_์
๋ฐ์ดํธ์ ์_๋ฏธ์
.png)
+ - ๋ฏธ์
1-4์์ ์งํ ์ค์ผ๋ก ๋ฐ๊พผ ๋ฏธ์
์ ์งํ ์๋ฃ๋ก ์
๋ฐ์ดํธ ํด๋ณด๊ฒ ๋ค.
+ - ํ
์คํธ ๊ฒฐ๊ณผ
+ ![๋ฏธ์
์๋ฃ๋ก ์
๋ฐ์ดํธ](images/๋ฏธ์
-5-๋ฏธ์
_์งํ์๋ฃ_์
๋ฐ์ดํธ.png)
+ - ์๋ฃํ ์ ์๋ ๋ฏธ์
(์งํํ์ง๋ ์์๊ฑฐ๋ ์ด๋ฏธ ์๋ฃํ ๋ฏธ์
)์ ๊ฒฝ์ฐ
+ ![์๋ฃํ ์ ์๋ ๋ฏธ์
](images/๋ฏธ์
-5-์๋ฃํ ์์๋_๋ฏธ์
.png)
+ - ๋ฐฉ๊ธ ์ ์ ์
๋ฐ์ดํธ ์ํจ ๋ฏธ์
(์ด๋ฏธ ์งํ์๋ฃ์ธ ์ํ)์ ๋ ํ
์คํธ ํด๋ณด์๋ค.
+ - ๋ฏธ์
์ ์ํ(status)๊ฐ 1(์งํ ์ค)์ด ์๋ ๊ฒฝ์ฐ์ ์๋ฌ๋ฅผ ๋ฐ์์์ผฐ๋ค.
+ - ํน์ ํ์์๊ฒ ํ ๋น๋์ง ์์ ๋ฏธ์
์ ์
๋ฐ์ดํธํ ๊ฒฝ์ฐ
+ ![์กด์ฌํ์ง ์๋ ๋ฏธ์
](images/๋ฏธ์
-5-์กด์ฌํ์ง์๋_๋ฏธ์
.png)
+ - ํ์์ ID ๋ฐ ๋ฏธ์
์ ID๋ก ์กฐํํ์ ๋ ์กด์ฌํ์ง ์์ ๊ฒฝ์ฐ์ ์๋ฌ๋ฅผ ๋ฐ์์์ผฐ๋ค.
\ No newline at end of file