๐ง lib/books.ts ํ ์ค์ฉ ๋ง๋ค๊ธฐ โ CLI ๋ก ํ์ธํ๋ฉฐ ๋ฐฐ์ฐ๊ธฐ
๋ชฉ์ฐจ11
์ด ๊ธ์ ๋ชฉ์
book-api-tutorial ๋ณธ ํํ ๋ฆฌ์ผ(TUTORIAL.md)์ STEP 2 ์์ lib/books.ts ๋ฅผ ํ ๋ฒ์ ๋ณด์ฌ์คฌ์ด์. ์งง์ ํ์ผ์ด์ง๋ง ์ฒ์ ๋ณด๋ฉด ์ต์ํ์ง ์์ ๋ฌธ๋ฒ์ด ํ๊บผ๋ฒ์ ๋์ต๋๋ค.
Omit<Book, "id">๊ฐ์ ํ์- ๋ฐฐ์ด์
find,findIndex globalThis์บ์คํ!(non-null assertion)
์ด ๋ณด์กฐ ํํ ๋ฆฌ์ผ์์๋ ๊ฐ์ ํ์ผ์ 6 ๋จ๊ณ ๋ก ์๋ผ์, ๊ฐ ๋จ๊ณ๋ง๋ค ํฐ๋ฏธ๋ ์์ ๋์์ ์ง์ ํ์ธํ๋ฉฐ ๋ง๋ญ๋๋ค. ๋งค ๋จ๊ณ ๋์๋ ํญ์ ๊ฐ์ ๋ช ๋ น์ด ๋ฑ์ฅํฉ๋๋ค:
npx tsx scripts/test-books.ts
์ด ํ ์ค๋ก ์ฐ๋ฆฌ๊ฐ ๋ง๋ ์ฝ๋๋ฅผ ์ฆ์ ์คํํด ๊ฒฐ๊ณผ๋ฅผ ๋ด ๋๋ค. ํ์ด์ง๋ฅผ ์๋ก๊ณ ์นจํ๊ฑฐ๋ Next.js ์๋ฒ๋ฅผ ๋์ธ ํ์๊ฐ ์์ด์.
๐ ์์ ์์น:
book-api-tutorial/ํ๋ก์ ํธ ์์์ ๊ทธ๋๋ก ์์ ํฉ๋๋ค. ๋ณ๋ ํด๋๋ฅผ ๋ง๋ค์ง ์์์.
๋ง์ง๋ง์lib/books.ts๊ฐ ๋ณธ ํํ ๋ฆฌ์ผ๊ณผ ๋๊ฐ์ ํํ๊ฐ ๋ฉ๋๋ค.
์ฌ์ ์ค๋น
book-api-tutorial ํด๋๊ฐ ์ด๋ฏธ ์๋ค๋ ์ ์ ๋ก ์์ํฉ๋๋ค.
0-1. TypeScript ์คํ ๋๊ตฌ ์ค์น
.ts ํ์ผ์ ์ปดํ์ผ ์์ด ๋ฐ๋ก ์คํํ๋ ๋๊ตฌ tsx ๋ฅผ ์ถ๊ฐํฉ๋๋ค.
cd book-api-tutorial npm install -D tsx
-D ๋ "๊ฐ๋ฐ์ฉ ์์กด์ฑ" ์ด๋ผ๋ ํ์์์ (์ค์ ๋น๋์๋ ์ ๋ค์ด๊ฐ).
0-2. scripts ํด๋ ๋ง๋ค๊ธฐ
์ด๋ฒ ๋ณด์กฐ ํํ ๋ฆฌ์ผ์์๋ง ์ธ ํ ์คํธ ์คํฌ๋ฆฝํธ๋ฅผ ๋ ํด๋์ ๋๋ค.
mkdir scripts
ํด๋ ๊ตฌ์กฐ๋ ์ด๋ ๊ฒ ๋ฉ๋๋ค.
book-api-tutorial/ โโ app/ โ ๊ธฐ์กด โโ lib/ โ โโ books.ts โ ๋จ๊ณ๋ณ๋ก ๋ค์ ๋ง๋ค ํ์ผ (์ง๊ธ์ ์์ฑ๋ณธ ์ํ) โโ scripts/ โ ์๋ก ์ถ๊ฐ โ โโ test-books.ts โ CLI ์์ ์คํํ ํ ์คํธ โโ package.json โโ tsconfig.json โ @/* ๋ณ์นญ์ด ์ด๋ฏธ ์ค์ ๋ผ ์์ด์
tsx ๊ฐ ์ ๋์ํ๋์ง ํ ๋ฒ ํ์ธํด ๋ด
๋๋ค. ์์ ํ์ผ์ ํ๋ ๋ง๋ค๊ณ :
echo 'console.log("hello", { from: "tsx" });' > scripts/smoke.ts npx tsx scripts/smoke.ts # ์ถ๋ ฅ: hello { from: 'tsx' }
ํ์ธ์ด ๋์ผ๋ฉด ์์ ํ์ผ์ ์ง์๋๋ค.
rm scripts/smoke.ts
0-3. ๊ธฐ์กด lib/books.ts ๋ฐฑ์ (์ ํ)
์ด๋ฏธ ๋ณธ ํํ ๋ฆฌ์ผ์ ๋ฐ๋ผ ๋ง๋ lib/books.ts ๊ฐ ์๋ค๋ฉด, ๋จ๊ณ๋ณ๋ก ๋ฎ์ด์ฐ๊ฒ ๋๋ ๋ฏธ๋ฆฌ ์ด๋๊ฐ์ ๋ณต์ฌํด ๋๋ฉด ๋ง์์ด ํธํฉ๋๋ค.
cp lib/books.ts /tmp/books-original-backup.ts
์ด์ ๋น ๋ง์์ผ๋ก ๋ค์ ์์ํฉ๋๋ค.
STEP 1. ํ์ ๊ณผ ์ด๊ธฐ ๋ฐ์ดํฐ๋ง
๊ฐ์ฅ ๋จ์ํ ํํ๋ก ์์ํฉ๋๋ค. ํจ์๋ ์๊ณ , ๋ฐ์ดํฐ๋ง ์๋ ์ํ.
๐ lib/books.ts ์ ์ฒด๋ฅผ ๋ค์์ผ๋ก ๊ต์ฒด:
export type Book = { id: number; title: string; author: string; publishedYear: number; }; export const books: Book[] = [ { id: 1, title: "์ด๋ฆฐ ์์", author: "์ํ ์ฅํ๋ฆฌ", publishedYear: 1943 }, { id: 2, title: "๋ฐ๋ฏธ์", author: "ํค๋ฅด๋ง ํค์ธ", publishedYear: 1919 }, { id: 3, title: "1984", author: "์กฐ์ง ์ค์ฐ", publishedYear: 1949 }, ];
๐ scripts/test-books.ts ์๋ก ๋ง๋ค๊ธฐ:
import { books } from "@/lib/books"; console.log("=== ์ด๊ธฐ ๋ฐ์ดํฐ ==="); console.log(books); console.log("๊ฐ์:", books.length);
ํ์ด ๋ณด๊ธฐ
๐น export type Book
"Book ์ด๋ผ๋ ํ์
์ ๋ง๋ค์๊ณ , ๋ค๋ฅธ ํ์ผ์์๋ ์ธ ์ ์์ด" ๋ผ๋ ๋ป. ๊ฐ์ฒด ํ ๊ถ์ ๋ชจ์์ ๋ฏธ๋ฆฌ ์ ์ด ๋๋ฉด ์คํ๊ฐ ๋ฌ์ ๋ TypeScript ๊ฐ ๋นจ๊ฐ ์ค๋ก ์๋ ค์ค๋๋ค.
๐น export const books: Book[] = [...]
export: ๋ค๋ฅธ ํ์ผ์์ import ํด์ ์ธ ์ ์๋ค.const: ์ด ๋ณ์์ ์ด๋ฆ ์ ๋ค๋ฅธ ๋ฐฐ์ด๋ก ๋ชป ๋ฐ๊พผ๋ค. ๋จ, ๋ฐฐ์ด ์์ ๋ด์ฉ์ ๋ฐ๊ฟ ์ ์๋ค (books.push(...)๊ฐ์ ๊ฑด ๊ฐ๋ฅ).: Book[]: "Book ์ ๋ฐฐ์ด" ์ด๋ผ๋ ํ์ .
๐น import { books } from "@/lib/books"
@/ ๋ ํ๋ก์ ํธ ๋ฃจํธ๋ฅผ ๊ฐ๋ฆฌํค๋ ๋ณ์นญ์
๋๋ค. book-api-tutorial/tsconfig.json ์ "@/*": ["./*"] ๋ก ์ค์ ๋ผ ์์ด tsx ๋ ์ด๋ฅผ ์ธ์ํด์. ๊ทธ๋์ ์ด๋์ import ํ๋ @/lib/books ํ ๊ฐ์ง ํํ์ผ๋ก ํต์ผ๋ฉ๋๋ค.
โ ํ์ธํ๊ธฐ
npx tsx scripts/test-books.ts
์ถ๋ ฅ:
=== ์ด๊ธฐ ๋ฐ์ดํฐ === [ { id: 1, title: '์ด๋ฆฐ ์์', author: '์ํ ์ฅํ๋ฆฌ', publishedYear: 1943 }, { id: 2, title: '๋ฐ๋ฏธ์', author: 'ํค๋ฅด๋ง ํค์ธ', publishedYear: 1919 }, { id: 3, title: '1984', author: '์กฐ์ง ์ค์ฐ', publishedYear: 1949 } ] ๊ฐ์: 3
3๊ถ์ ์ฑ ์ด ๋ณด์ ๋๋ค. ๋ค์ ๋จ๊ณ์์ ํจ์๋ฅผ ์ถ๊ฐํฉ๋๋ค.
์ค์ 1:
export๋ฅผ ๋นผ๋จน๊ณconst books = [...]๋ง ์ฐ๋ฉด, ๋ค๋ฅธ ํ์ผ์์ ๋ชป ๊ฐ์ ธ์ต๋๋ค.์ค์ 2: ๊ฐ์ฒด์ ํ๋๋ฅผ ๋น ๋จ๋ฆฌ๋ฉด (์:
publishedYear์์ด) ๋นจ๊ฐ ์ค์ด ๊ทธ์ด์ง๋๋ค.
STEP 2. getAllBooks() ํจ์
๋ฐ์ดํฐ๋ฅผ ์ง์ export ํ์ง ์๊ณ , ํจ์๋ก ํ ๋ฒ ๊ฐ์ธ์ ๋๋ ค์ฃผ๋ ๋ฐฉ์์ผ๋ก ๋ฐ๊ฟ๋๋ค. ๋์ค์ ๋ฐ์ดํฐ ์ ์ฅ ๋ฐฉ์์ด ๋ฐ๋์ด๋(์: globalThis โ DB) ํจ์๋ง ๊ฐ์ ๋ชจ์์ด๋ฉด ๋ฐ๊นฅ ์ฝ๋๋ ์ ๋ฐ๋์ด์.
๐ lib/books.ts ๋ฅผ ํต์งธ๋ก ๊ต์ฒด:
export type Book = { id: number; title: string; author: string; publishedYear: number; }; const books: Book[] = [ { id: 1, title: "์ด๋ฆฐ ์์", author: "์ํ ์ฅํ๋ฆฌ", publishedYear: 1943 }, { id: 2, title: "๋ฐ๋ฏธ์", author: "ํค๋ฅด๋ง ํค์ธ", publishedYear: 1919 }, { id: 3, title: "1984", author: "์กฐ์ง ์ค์ฐ", publishedYear: 1949 }, ]; export function getAllBooks(): Book[] { return books; }
๋ฐ๋ ๊ณณ:
export const books์export๋ฅผ ๋ผ๊ณ , ๋์getAllBooks()ํจ์๋ฅผ export ํ์ต๋๋ค.
๐ scripts/test-books.ts:
import { getAllBooks } from "@/lib/books"; console.log("=== ๋ชจ๋ ์ฑ ==="); console.log(getAllBooks()); console.log("๊ฐ์:", getAllBooks().length);
ํ์ด ๋ณด๊ธฐ
๐น export function getAllBooks(): Book[] { ... }
- ํจ์ ์ด๋ฆ:
getAllBooks - ๋ฐํ ํ์
:
Book[]โ Book ๋ฐฐ์ด - ๋ชธ์ฒด:
return booksโ ๋ชจ๋ ์์ books ๋ฅผ ๊ทธ๋๋ก ๋๋ ค์ค๋ค.
ํ์ ์ ๋ช ์ํ๋ฉด IDE ๊ฐ ์๋์์ฑ์ ์ ํด ์ค๋๋ค. ์ ์ ์ด๋ ๋์์ ํ๋๋ฐ ๋ช ์ํ๋ ๊ฒ ์์ ํฉ๋๋ค.
โ ํ์ธํ๊ธฐ
npx tsx scripts/test-books.ts
=== ๋ชจ๋ ์ฑ === [ { id: 1, title: '์ด๋ฆฐ ์์', author: '์ํ ์ฅํ๋ฆฌ', publishedYear: 1943 }, ... ] ๊ฐ์: 3
Step 1 ๊ณผ ๊ฐ์ ๊ฒฐ๊ณผ์ธ๋ฐ, ์ด์ ๋ ํจ์๋ฅผ ํตํด์ ๊ฐ์ ธ์์ต๋๋ค.
STEP 3. getBookById() ํจ์
ID ํ ๊ฐ๋ฅผ ๋ฐ์ ๊ทธ ์ฑ
ํ ๊ถ๋ง ๋๋ ค์ค๋๋ค. ์๋ฐ์คํฌ๋ฆฝํธ ๋ฐฐ์ด์ find ๋ฉ์๋๋ฅผ ์ฒ์ ์ฐ๋ ์๋ฆฌ์์.
๐ lib/books.ts ์ ํจ์ ์ถ๊ฐ (์ ์ฝ๋๋ ๊ทธ๋๋ก ๋๊ณ , ๋ง์ง๋ง์):
export function getBookById(id: number): Book | undefined { return books.find((b) => b.id === id); }
๐ scripts/test-books.ts:
import { getAllBooks, getBookById } from "@/lib/books"; console.log("=== ์ ์ฒด ==="); console.log(getAllBooks().length, "๊ถ"); console.log("\n=== ID ๋ก ์ฐพ๊ธฐ ==="); console.log("ID 2:", getBookById(2)); console.log("ID 999 (์๋ ID):", getBookById(999));
ํ์ด ๋ณด๊ธฐ
๐น books.find((b) => b.id === id)
๋ฐฐ์ด์ ์ฒ์๋ถํฐ ํ์ผ๋ฉด์ ์กฐ๊ฑด์ ๋ง์กฑํ๋ ์ฒซ ๋ฒ์งธ ํญ๋ชฉ ์ ๋๋ ค์ค๋๋ค. ์กฐ๊ฑด์ ํ์ดํ ํจ์ (b) => b.id === id ์ธ๋ฐ, "๊ฐ ์ฑ
b ์ ๋ํด, ๊ทธ ์ฑ
์ id ๊ฐ ์ฐ๋ฆฌ๊ฐ ๋ฐ์ id ์ ๊ฐ์ผ๋ฉด ํต๊ณผ" ๋ผ๋ ๋ป์ด์์.
๐น Book | undefined
๋ฐํ ํ์
์ | undefined ๊ฐ ๋ถ์ด ์์ด์. find ๋ ๋ชป ์ฐพ์ผ๋ฉด undefined ๋ฅผ ๋๋ ค์ฃผ๊ฑฐ๋ ์. ์ด๊ฑธ ๋ช
์ํด์ผ ํธ์ถํ๋ ์ชฝ์์ "์์ ์๋ ์๊ณ ์์ ์๋ ์๋ค" ๋ ๊ฑธ ์์์ฑ๋๋ค.
โ ํ์ธํ๊ธฐ
npx tsx scripts/test-books.ts
=== ์ ์ฒด === 3 ๊ถ === ID ๋ก ์ฐพ๊ธฐ === ID 2: { id: 2, title: '๋ฐ๋ฏธ์', author: 'ํค๋ฅด๋ง ํค์ธ', publishedYear: 1919 } ID 999 (์๋ ID): undefined
- ์์ ๋: ์ฑ ๊ฐ์ฒด
- ์์ ๋:
undefined
๋ ๊ฒฝ์ฐ๋ฅผ ๋ชจ๋ ๋์ผ๋ก ๋ดค์ผ๋, ๋์ค์ ํ์ด์ง์์ "์ฑ ์ ์ฐพ์ ์ ์์ต๋๋ค" ๋ถ๊ธฐ๋ฅผ ๋ง๋ค ๋ ์์ ์๊ฒ ์ฒ๋ฆฌํ ์ ์์ด์.
STEP 4. createBook() ํจ์
์ด๋ฒ์ ๋ฐ์ดํฐ๊ฐ ๋ฐ๋๋๋ค. ์ ์ฑ ์ ๋ฑ๋กํ๋ฉด id ๊ฐ ์๋์ผ๋ก ๋งค๊ฒจ์ ธ์ผ ํด์.
๐ lib/books.ts. ๋ ๊ฐ์ง๊ฐ ๋ฐ๋๋๋ค:
let nextId = 4;๋ฅผ books ๋ฐฐ์ด ์๋์ ์ถ๊ฐcreateBookํจ์๋ฅผ ์ถ๊ฐ
export type Book = { id: number; title: string; author: string; publishedYear: number; }; const books: Book[] = [ { id: 1, title: "์ด๋ฆฐ ์์", author: "์ํ ์ฅํ๋ฆฌ", publishedYear: 1943 }, { id: 2, title: "๋ฐ๋ฏธ์", author: "ํค๋ฅด๋ง ํค์ธ", publishedYear: 1919 }, { id: 3, title: "1984", author: "์กฐ์ง ์ค์ฐ", publishedYear: 1949 }, ]; let nextId = 4; // โฌ ๏ธ ์ถ๊ฐ export function getAllBooks(): Book[] { return books; } export function getBookById(id: number): Book | undefined { return books.find((b) => b.id === id); } // โฌ๏ธ ์ถ๊ฐ export function createBook(data: Omit<Book, "id">): Book { const newBook: Book = { id: nextId, ...data }; nextId++; books.push(newBook); return newBook; }
๐ scripts/test-books.ts:
import { createBook, getAllBooks } from "@/lib/books"; console.log("=== ๋ฑ๋ก ์ ==="); console.log(getAllBooks().length, "๊ถ"); const created = createBook({ title: "๋ ธ๋ฅด์จ์ด์ ์ฒ", author: "๋ฌด๋ผ์นด๋ฏธ ํ๋ฃจํค", publishedYear: 1987, }); console.log("\n=== ์๋ก ๋ฑ๋กํ ์ฑ ==="); console.log(created); console.log("\n=== ๋ฑ๋ก ํ ==="); console.log(getAllBooks().length, "๊ถ");
ํ์ด ๋ณด๊ธฐ
๐น Omit<Book, "id">
"Book ํ์
์์ id ํ๋๋ง ๋นผ๋ธ ์ ํ์
" ์
๋๋ค. ํธ์ถํ๋ ์ชฝ์ id ๋ฅผ ๋ชจ๋ฅด๊ณ , ์ฐ๋ฆฌ๊ฐ ์๋์ผ๋ก ๋งค๊ฒจ ์ค ๊ฑฐ๋๊น์.
์ด๋ ๊ฒ ํธ์ถ ๊ฐ๋ฅ:
createBook({ title: "...", author: "...", publishedYear: 1987 }); // OK
์ด๋ ๊ฒ๋ ์๋ฌ:
createBook({ id: 99, title: "...", ... }); // โ id ๋ฅผ ๋ฃ์ผ๋ฉด ์ ๋จ
๐น const newBook: Book = { id: nextId, ...data };
id: nextIdโ ์ฐ๋ฆฌ๊ฐ ๊ฐ์ง ๋ค์ id...dataโ ๋งค๊ฐ๋ณ์๋ก ๋ฐ์ ๊ฐ์ฒด์ ํ๋๋ค์ ํผ์ณ์ ํฉ์ณ ๋ฃ๊ธฐ
๊ฒฐ๊ณผ: { id: 4, title: "...", author: "...", publishedYear: 1987 }
๐น nextId++
๋ค์ ๋ฑ๋ก์ ์ํด 1์ ๋ํด ๋ก๋๋ค. let ์ด๋ผ์ ๊ฐ์ ๋ฐ๊ฟ ์ ์์ด์.
๐น books.push(newBook)
books ๊ฐ const ์ธ๋ฐ ์ด๋ป๊ฒ ์ถ๊ฐ๊ฐ ๋ ๊น์? const ๋ ๋ณ์ ์ด๋ฆ์ ๋ค๋ฅธ ๋ฐฐ์ด๋ก ๋ชป ๋ฐ๊พผ๋ค๋ ๋ป์ผ ๋ฟ, ๋ฐฐ์ด ๋ด์ฉ ์ ๋ฐ๊ฟ ์ ์์ด์.
โ ํ์ธํ๊ธฐ
npx tsx scripts/test-books.ts
=== ๋ฑ๋ก ์ === 3 ๊ถ === ์๋ก ๋ฑ๋กํ ์ฑ === { id: 4, title: '๋ ธ๋ฅด์จ์ด์ ์ฒ', author: '๋ฌด๋ผ์นด๋ฏธ ํ๋ฃจํค', publishedYear: 1987 } === ๋ฑ๋ก ํ === 4 ๊ถ
- id ๊ฐ ์๋์ผ๋ก 4 ๊ฐ ๋๊ณ
- ์ ์ฒด ๊ฐ์๊ฐ 4๊ถ์ผ๋ก ๋์์ต๋๋ค.
๋งค๋ฒ
npx tsx๋ฅผ ์คํํ ๋ ๊ฒฐ๊ณผ๊ฐ ํญ์ ๋๊ฐ์ด 4๊ถ์ผ๋ก ๋์ค๋ ๊ฒ ์ด์ํด ๋ณด์ผ ์ ์์ด์. ์ด์ : ๊ฐ ์คํ์ ์๋ก์ด Node ํ๋ก์ธ์ค ๋ผ ๋งค๋ฒ ์ฒ์ ์ํ(3๊ถ)์์ ์์ํ๊ธฐ ๋๋ฌธ์ ๋๋ค. ์ด๊ฒ ์ฐ๋ฆฌํํ ๋ ์ข์ ๊ฑฐ์์ โ ๋งค๋ฒ ๊ฐ์ ์๋๋ฆฌ์ค๋ก ํ ์คํธํ ์ ์์ผ๋๊น.
STEP 5. updateBook() ํจ์
์ด๋ฏธ ์๋ ์ฑ ์ ๋ด์ฉ์ ํต์งธ๋ก ๋ฐ๊ฟ๋๋ค.
๐ lib/books.ts ๋ง์ง๋ง์ ํจ์ ์ถ๊ฐ:
export function updateBook( id: number, data: Omit<Book, "id">, ): Book | null { const idx = books.findIndex((b) => b.id === id); if (idx === -1) return null; books[idx] = { id, ...data }; return books[idx]; }
๐ scripts/test-books.ts:
import { getBookById, updateBook } from "@/lib/books"; console.log("=== ์์ ์ ID 2 ==="); console.log(getBookById(2)); console.log("\n=== updateBook(2, ...) ==="); const updated = updateBook(2, { title: "๋ฐ๋ฏธ์ (๊ฐ์ ํ)", author: "ํค๋ฅด๋ง ํค์ธ", publishedYear: 1919, }); console.log("๋ฐํ๊ฐ:", updated); console.log("\n=== ์์ ํ ๋ค์ ์กฐํ ==="); console.log(getBookById(2)); console.log("\n=== ์๋ ์ฑ ์์ ์๋ ==="); console.log(updateBook(999, { title: "x", author: "y", publishedYear: 0 }));
ํ์ด ๋ณด๊ธฐ
๐น findIndex vs find
find๋ ํญ๋ชฉ ์์ฒด ๋ฅผ ๋๋ ค์ค๋๋ค.findIndex๋ ๊ทธ ํญ๋ชฉ์ ์์น(์ธ๋ฑ์ค) ๋ฅผ ๋๋ ค์ค๋๋ค. ์์ผ๋ฉด-1.
์์ ํ๋ ค๋ฉด "๋ช ๋ฒ์งธ ์๋ฆฌ์ธ์ง" ๋ฅผ ์์์ผ ํ๋ฏ๋ก findIndex ๊ฐ ์ด์ธ๋ ค์.
๐น if (idx === -1) return null;
์๋ ์ฑ
์ ์์ ํ๋ผ๋ ์์ฒญ์ด ๋ค์ด์ค๋ฉด null ์ ๋๋ ค "๊ทธ๋ฐ ์ฑ
์์ด์" ์ ํธ๋ฅผ ๋ณด๋
๋๋ค. getBookById ๋ undefined ๋ฅผ ์ผ๋๋ฐ, updateBook ์ null ์
๋๋ค. ์ฝ๊ฐ ์ผ๊ด์ฑ์ด ๋ถ์กฑํด ๋ณด์ด์ง๋ง ์ค๋ฌด์์ ํํ ๋ณด๋ ์ฐจ์ด์์ (์กฐํ โ undefined / ๋ณ๊ฒฝ โ null ๊ฐ์ ๊ด๋ก).
๐น books[idx] = { id, ...data };
- ์ ๊ฐ์ฒด๋ฅผ ์ ๊ฐ์ฒด๋ก ํต์งธ๋ก ๊ต์ฒดํฉ๋๋ค.
{ id, ...data }๋{ id: id, ...data }์ ์ถ์ฝํ์ ๋๋ค.
โ ํ์ธํ๊ธฐ
npx tsx scripts/test-books.ts
=== ์์ ์ ID 2 === { id: 2, title: '๋ฐ๋ฏธ์', ... } === updateBook(2, ...) === ๋ฐํ๊ฐ: { id: 2, title: '๋ฐ๋ฏธ์ (๊ฐ์ ํ)', ... } === ์์ ํ ๋ค์ ์กฐํ === { id: 2, title: '๋ฐ๋ฏธ์ (๊ฐ์ ํ)', ... } === ์๋ ์ฑ ์์ ์๋ === null
์ธ ๊ฐ์ง ๋ชจ๋ ํ์ธ:
- ์์ ์ด ์ค์ ๋ก ๋ฐ์๋จ (
getBookById๋ก ์ฌ์กฐํํ๋๋ ์ ์ ๋ชฉ) - ์์ ํ ๊ฒฐ๊ณผ๊ฐ ๋ฐํ๊ฐ์ผ๋ก๋ ๋์ด
- ์๋ ์ฑ
์์ ์
null
STEP 6. globalThis ๋ก ์ ๊ทธ๋ ์ด๋
์ฌ๊ธฐ์ ๋๋ด๋ ์ฝ๋๋ ๋์ํฉ๋๋ค. CLI ํ๊ฒฝ ์์๋ ์ถฉ๋ถํด์.
๋ฌธ์ ๋ Next.js dev ๋ชจ๋ ์
๋๋ค. Next.js dev ์๋ฒ๋ ํ์ผ์ ์ ์ฅํ ๋๋ง๋ค ๋ณ๊ฒฝ๋ ๋ชจ๋์ ๋ค์ ๋ถ๋ฌ์์(hot-reload). ์ด๋ lib/books.ts ์์ books ์ nextId ๋ณ์๊ฐ ์ ๊ฐ์ผ๋ก ์ด๊ธฐํ ๋ฉ๋๋ค. ์ฌ์ฉ์๊ฐ ํผ์์ ์ฑ
์ ๋ฑ๋กํ๋๋ฐ ์ฝ๋๋ฅผ ํ ๋ฒ๋ง ์ ์ฅํด๋ ๊ทธ ์ฑ
์ด ์ฌ๋ผ์ง๋ ๊ฑฐ์์.
ํด๊ฒฐ๋ฒ: ๋ฐ์ดํฐ๋ฅผ ๋ชจ๋ ์์ ๋ณ์ ๊ฐ ์๋๋ผ ํ๋ก์ธ์ค ์ ์ญ(globalThis) ์ ๋๊ธฐ. globalThis ๋ hot-reload ๊ฐ ์ผ์ด๋๋ ์ด์๋จ์ต๋๋ค.
๐ lib/books.ts ๋ฅผ ํต์งธ๋ก ๋ค์์ผ๋ก ๋ฐ๊ฟ๋๋ค:
export type Book = { id: number; title: string; author: string; publishedYear: number; }; // Next.js dev ๋ชจ๋์ hot-reload ๊ฐ ์ผ์ด๋๋ ๋ฐ์ดํฐ๊ฐ ์ด์๋จ๋๋ก // ๋ชจ๋ ์์ ๋ณ์๊ฐ ์๋๋ผ "ํ๋ก์ธ์ค ์ ์ญ" ์ธ globalThis ์ ์ ์ฅํฉ๋๋ค. type GlobalStore = { __books?: Book[]; __nextId?: number; }; const store = globalThis as unknown as GlobalStore; if (!store.__books) { store.__books = [ { id: 1, title: "์ด๋ฆฐ ์์", author: "์ํ ์ฅํ๋ฆฌ", publishedYear: 1943 }, { id: 2, title: "๋ฐ๋ฏธ์", author: "ํค๋ฅด๋ง ํค์ธ", publishedYear: 1919 }, { id: 3, title: "1984", author: "์กฐ์ง ์ค์ฐ", publishedYear: 1949 }, ]; store.__nextId = 4; } export function getAllBooks(): Book[] { return store.__books!; } export function getBookById(id: number): Book | undefined { return store.__books!.find((b) => b.id === id); } export function createBook(data: Omit<Book, "id">): Book { const newBook: Book = { id: store.__nextId!, ...data }; store.__nextId!++; store.__books!.push(newBook); return newBook; } export function updateBook( id: number, data: Omit<Book, "id">, ): Book | null { const idx = store.__books!.findIndex((b) => b.id === id); if (idx === -1) return null; store.__books![idx] = { id, ...data }; return store.__books![idx]; }
๐ scripts/test-books.ts โ ํจ์ ์ฌ์ฉ์ ๊ทธ๋๋ก์ง๋ง ํตํฉ ํ๊ท ํ
์คํธ ํ ๋ฒ ๋:
import { createBook, getAllBooks, getBookById, updateBook, } from "@/lib/books"; console.log("=== 1. ์ด๊ธฐ ๋ฐ์ดํฐ ==="); console.log(getAllBooks().length, "๊ถ"); console.log("\n=== 2. ID ๋ก ์กฐํ ==="); console.log("ID 2:", getBookById(2)?.title); console.log("ID 999:", getBookById(999)); console.log("\n=== 3. ์ ์ฑ ๋ฑ๋ก ==="); const created = createBook({ title: "๋ ธ๋ฅด์จ์ด์ ์ฒ", author: "๋ฌด๋ผ์นด๋ฏธ ํ๋ฃจํค", publishedYear: 1987, }); console.log("์ ID:", created.id); console.log("๋ฑ๋ก ํ ์ ์ฒด:", getAllBooks().length, "๊ถ"); console.log("\n=== 4. ์์ ==="); const updated = updateBook(2, { title: "๋ฐ๋ฏธ์ (๊ฐ์ ํ)", author: "ํค๋ฅด๋ง ํค์ธ", publishedYear: 1919, }); console.log("์์ ๊ฒฐ๊ณผ:", updated?.title); console.log("์ฌ์กฐํ:", getBookById(2)?.title); console.log("\n=== 5. ์๋ ์ฑ ์์ ==="); console.log(updateBook(999, { title: "x", author: "y", publishedYear: 0 })); console.log("\n=== 6. ์ต์ข ==="); console.log(getAllBooks());
ํ์ด ๋ณด๊ธฐ
๐น type GlobalStore = { __books?: Book[]; __nextId?: number; }
globalThis ์ ์ฐ๋ฆฌ๊ฐ ์ธ ๋ ํ๋์ ๋ชจ์์ ์ ์ด ๋ ํ์
. ? ๋ "์์ ์๋ ์๊ณ ์์ ์๋ ์๋ค" ๋ ํ์.
๐น const store = globalThis as unknown as GlobalStore;
TypeScript ์๊ฒ "globalThis ๋ฅผ GlobalStore ํ์
์ฒ๋ผ ๋ค๋ฃฐ ๊ฑฐ์ผ" ๋ผ๊ณ ์๋ ค ์ค๋๋ค.
as unknown as ๋ผ๋ ๋ ๋จ๊ณ ์บ์คํ
์ด ๋ค์ด๊ฐ๋ ์ด์ : globalThis ์ ๊ธฐ๋ณธ ํ์
๊ณผ GlobalStore ๋ ์์ ํ ๋ฌ๋ผ์ ํ ๋ฒ์ ๋ณํํ๋ฉด TypeScript ๊ฐ "๋๋ฌด ์ํํ ๋ณํ์ธ๋ฐ?" ๋ผ๋ฉฐ ๊ฑฐ์ ํด์. unknown ์ผ๋ก ํ ๋ฒ ๊ฑฐ์น๋ฉด ํต๊ณผํฉ๋๋ค.
๐น if (!store.__books) { ... store.__books = [...]; }
"globalThis ์ __books ๊ฐ ์์ง ์์ผ๋ฉด ์ฒ์ ๋ฐ์ดํฐ๋ฅผ ์ฑ์๋ผ". ์ด๋ฏธ ์์ผ๋ฉด(=hot-reload ํ) ๊ทธ๋๋ก ๋ก๋๋ค. ์ด๊ฒ ์ด์๋จ๋ ํต์ฌ.
๐น store.__books!.find(...)
๋์ ! ๋ "์ด ๊ฐ์ ์ ๋ undefined ๊ฐ ์๋์ผ" ๋ผ๊ณ TypeScript ํํ
์ฝ์ํ๋ ํ์. ์์ if (!store.__books) ๋ก ์ฑ์ ์ผ๋ ์ด์ ๋ถํฐ๋ ๋ถ๋ช
ํ ์๋ค โ ๊ทธ๊ฑธ ์ปดํ์ผ๋ฌ๋ ๋ชจ๋ฅด๋๊น ์ฐ๋ฆฌ๊ฐ ์๋ ค ์ฃผ๋ ๊ฑฐ์์.
โ ํ์ธํ๊ธฐ
npx tsx scripts/test-books.ts
=== 1. ์ด๊ธฐ ๋ฐ์ดํฐ === 3 ๊ถ === 2. ID ๋ก ์กฐํ === ID 2: ๋ฐ๋ฏธ์ ID 999: undefined === 3. ์ ์ฑ ๋ฑ๋ก === ์ ID: 4 ๋ฑ๋ก ํ ์ ์ฒด: 4 ๊ถ === 4. ์์ === ์์ ๊ฒฐ๊ณผ: ๋ฐ๋ฏธ์ (๊ฐ์ ํ) ์ฌ์กฐํ: ๋ฐ๋ฏธ์ (๊ฐ์ ํ) === 5. ์๋ ์ฑ ์์ === null === 6. ์ต์ข === [ { id: 1, title: '์ด๋ฆฐ ์์', ... }, { id: 2, title: '๋ฐ๋ฏธ์ (๊ฐ์ ํ)', ... }, { id: 3, title: '1984', ... }, { id: 4, title: '๋ ธ๋ฅด์จ์ด์ ์ฒ', ... } ]
Step 5 ์ ๊ฐ์ ๊ฒฐ๊ณผ ๊ฐ ๋์ค๋ฉด ์ฑ๊ณต์ ๋๋ค. ๋ฐ์ดํฐ ์ ์ฅ์๋ง ๋ฐ๋์์ง ๋์์ ๊ทธ๋๋ก๋๊น์.
CLI ํ๊ฒฝ์์๋ globalThis ์ ์ด์ ์ด ์ ๋ณด์
๋๋ค(๋งค ์คํ์ด ์ ํ๋ก์ธ์ค). ํ์ง๋ง Next.js dev ์๋ฒ๋ฅผ ๋์ฐ๊ณ app/books/new ์์ ์ฑ
์ ๋ฑ๋กํ ๋ค lib/books.ts ์ ์ฃผ์ ํ ์ค์ ๋ฐ๊ฟ ์ ์ฅํด ๋ณด์ธ์. ์๋ก ๋ฑ๋กํ ์ฑ
์ด ์ฌ๋ผ์ง์ง ์์ต๋๋ค.
๋ณธ ํํ ๋ฆฌ์ผ๋ก ๋์๊ฐ๊ธฐ
์ด์ lib/books.ts ๊ฐ ๋ณธ ํํ ๋ฆฌ์ผ(TUTORIAL.md)์ STEP 2 ๊ฒฐ๊ณผ์ ๋์ผํฉ๋๋ค. Next.js ํ์ด์ง/๋ผ์ฐํธ๋ฅผ ํตํด์๋ ๊ฐ์ ํจ์๋ค์ ๋ถ๋ฅด๊ฒ ๋ฉ๋๋ค.
dev ์๋ฒ์์๋ ํ์ธ
npm run dev
http://localhost:3300/api/books ๋ฅผ ์ด๋ฉด ๊ฐ์ ์ฑ
3๊ถ์ด JSON ์ผ๋ก ๋์ต๋๋ค. CLI ์์ ํ์ธํ ํจ์๊ฐ ๊ทธ๋๋ก API ๋ผ์ฐํธ์์ ํธ์ถ๋๊ณ ์์ด์.
scripts ํด๋ ์ฒ๋ฆฌ
scripts/test-books.ts ๋ ํ์ต์ฉ ์ฐ์ถ๋ฌผ์ด๋ผ ๊ทธ๋๋ก ๋ฌ๋ ๋ณธ ํ๋ก์ ํธ์ ์ํฅ์ ์ฃผ์ง ์์ต๋๋ค. ๋น๋(npm run build) ์์๋ ๋ฌด์๋๊ณ , dev ์๋ฒ์์๋ ์ ๋ถ๋ฆฝ๋๋ค. ๊น๋ํ๊ฒ ์ ๋ฆฌํ๊ณ ์ถ์ผ๋ฉด scripts/ ํด๋ ์ ์ฒด๋ฅผ ์ง์ฐ์ธ์.
rm -rf scripts
tsx ๋ ๋ ์ด์ ์ ์ฐ๊ฒ ๋ค๋ฉด ์์กด์ฑ์์ ์ ๊ฑฐ:
npm uninstall tsx
์ฐ๋ฆฌ๊ฐ ๋ฐฐ์ด ๊ฒ
| ๋จ๊ณ | ์๋ก ๋ฐฐ์ด ๊ฒ |
|---|---|
| Step 1 | export, type, Book[] ๊ฐ์ ๊ธฐ์ด ํ์
|
| Step 2 | ํจ์๋ก ๊ฐ์ธ export ํ๋ ํจํด |
| Step 3 | Array#find, `T |
| Step 4 | Omit<T, K>, ์คํ๋ ๋ ์ฐ์ฐ์, const ๋ฐฐ์ด์ push |
| Step 5 | Array#findIndex, null ๋ก "์์" ํํ |
| Step 6 | globalThis ์บ์คํ
, ! non-null assertion |
๋ค์์ ํด ๋ณผ ๋งํ ๊ฒ
deleteBook(id)์ถ๊ฐ โsplice๋๋filter๋ก ํ ์ค ์ ๋.- ๊ฒ์ ํจ์
searchBooks(keyword)โtitle.includes(keyword)ํ์ฉ. - ์ ๋ ฌ
sortBooksByYear()โ[...books].sort(...)๋ก ์๋ณธ์ ์ ๊ฑด๋๋ฆฌ๊ณ ์ ๋ ฌ.
CLI ์์ ๋งค๋ฒ ๊ฒฐ๊ณผ๋ฅผ ํ์ธํ๋ ์ด ํจํด์ ์ด๋ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํจ์๋ฅผ ๋ง๋ค๋ ๋๊ฐ์ด ์ธ ์ ์์ด์. ์๊ฒ ๋ง๋ค๊ณ , ์คํํด์ ๋ณด๊ณ , ๊ทธ๋ค์ ํ ์ค์ ๋ํ๋ค โ ์ด๊ฒ ์ง์น์ง ์๊ณ ์ฝ๋๋ฅผ ๋๋ฆฌ๋ ๊ฐ์ฅ ์์ ํ ๋ฐฉ๋ฒ์ ๋๋ค.
๋๊ธ
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ์ด ํ์ํฉ๋๋ค.