728x90
기본적인 crud 예제 프로그램 만들기
//프로젝트 초기화
npm init -y
tsc -init
tsconfig.json
{
// include : 컴파일할 파일 경로를 설정합니다. [ src폴더 하위의 모든 .ts 확장자를 가진 파일 ]
"include": ["src/**/*.ts"],
"compilerOptions": {
"lib": ["ES2021","DOM"],
// esModuleInterop 속성이 위의 코드 처럼 true로 설정될 경우, ES6 모듈 사양을 준수하여 CommonJS 모듈을 가져올 수 있게 됩니다.
// e.g. typescript-test.ts => import express from "express"
"esModuleInterop": true,
// 타입스크립트가 모듈을 찾는방법을 지정
"moduleResolution":"NodeNext",
"target": "ES2021",
// rootDir : 컴파일될 타입스크립트 코드들의 위치를 명시합니다.
// "rootDir": "src",
// outDir : 컴파일 후 생성되는 js파일이 생성될 폴더명
"outDir": "dist",
// strictNullChecks
"strictNullChecks": true,
// 암시적 any 타입을 허용하지 않는다
"noImplicitAny": true
}
}
필요 패키지 설치
npm i express dotenv
npm i -D typescript @types/node @types/express @types/dotenv
.env
PORT=6600
src 폴더 안에 index.ts
/**
* Required External Modules
*/
import dotenv from "dotenv";
import express, { Request, Response } from "express";
dotenv.config();
/**
* App Variables
*/
if (!process.env.PORT) {
process.exit(1);
}
const PORT: number = parseInt(process.env.PORT as string, 10);
const app = express();
/**
* App Configuration
*/
app.use(express.json());
/**
* Server Activation
*/
app.get("/", (req: Request, res: Response) => {
res.send("Hi");
});
app.listen(PORT, () => {
console.log(`Listening on port http://localhost:${PORT}`);
});
ts-node-dev 패키지 설치
// 파일 변경되면 자동으로 변경된 ts파일을 실행시켜줌
npm i -D ts-node-dev
package.json 수정
- --transpile-only : 타입스크립트의 빠른 트랜스파일 모듈 사용
- src/index.ts : 엔트리파일 지정
7번째줄
"test": "echo \"Error: no test specified\" && exit 1"
를
"dev": "ts-node-dev --transpile-only src/index.ts"
npm run dev 로 서버 실행
폴더 생성 - src/items
파일 생성 - src/items/item.interface.ts
item.interface.ts
기본이 되는 BaseItem 인터페이스와 BaseItem을 상속받을 Item 인터페이스를 만든다.
Item ⇒ 특정한 값을 검색하거나 삭제할때 사용
BaseItem ⇒ 아이템을 수정, 등록 할때 사용
export interface BaseItem {
name: string;
price: number;
description: string;
image: string;
}
export interface Item extends BaseItem {
id: number;
}
items.interface.ts
Items 인터페이스는 DB를 사용하지 않아서 메모리에 저장하는 배열의 타입을 지정하기 위해 만들었습니다.
import { Item } from "./item.interface";
export interface Items {
[key: number]: Item;
}
item.interface 로부터 Item을 가져고
Items 인터페이스 생성 후 내보냄
[key: number]: Item; 부분 해석은 아래 코드를 보면서 이해하면 쉽습니다.
key 값으로 넘버가 여러개 들어가고 값은 Item을 받는다 라고 생각 하시면 됩니다.
[key: number]: Item; 이러한 부분을 타입스크립트에서 인덱스 시그니처 라고 합니다.
[key: number]: Item; 부분 해석
const items: Items = {
1: { id: 1, name: "item1", price: 100, description: "This is item1", image: "item1.jpg" },
2: { id: 2, name: "item2", price: 200, description: "This is item2", image: "item2.jpg" },
3: { id: 3, name: "item3", price: 300, description: "This is item3", image: "item3.jpg" },
};
파일 생성 - src/items/items.service.ts
items.service.ts
/**
* Data Model Interfaces
*/
import { BaseItem, Item } from "./item.interface";
import { Items } from "./items.interface";
/**
* In-Memory Store
*/
let items: Items = {
1: {
id: 1,
name: "Burger",
price: 599,
description: "Tasty",
image: "https://cdn.auth0.com/blog/whatabyte/burger-sm.png",
},
2: {
id: 2,
name: "Pizza",
price: 299,
description: "Cheesy",
image: "https://cdn.auth0.com/blog/whatabyte/pizza-sm.png",
},
3: {
id: 3,
name: "Tea",
price: 199,
description: "Informative",
image: "https://cdn.auth0.com/blog/whatabyte/tea-sm.png",
},
};
/**
* Service Methods
*/
export const findAll = async (): Promise<Item[]> => Object.values(items);
export const find = async (id: number): Promise<Item> => items[id];
//매개변수로 {name, price, description, image} 객체를 받고
//생성된 아이템을 반환 합니다.
//new Date().valueOf(); ⇒ 현재시간을 유닉스 타임으로 반환합니다.
export const create = async (newItem: BaseItem): Promise<Item> => {
const id = new Date().valueOf();
items[id] = {
id,
...newItem,
};
return items[id];
};
//아이템을 업데이트할 update 함수를 만듭니다.
//update 함수는 id와 업데이트할 값 을 받고 업데이트 후
//업데이트된 값 또는 null을 반환합니다.
//아이템이 없을때 ⇒ null
//아이템이 있으면 ⇒ 업데이트된 아이템
export const update = async ( id: number, itemUpdate: BaseItem ): Promise<Item | null> => {
const item = await find(id);
if (!item) {
return null;
}
items[id] = { id, ...itemUpdate };
return items[id];
};
//아이템을 삭제할 remove 함수를 만듭니다.
//이 함수에서도 삭제할 아이템이 없으면 null 반환 하고
//삭제가되면 아무것도 반환하지 않습니다(void).
export const remove = async (id: number): Promise<null | void> => {
const item = await find(id);
if (!item) {
return null;
}
delete items[id];
};
파일 생성 - src/items/items.router.ts
items.router.ts
/**
* Required External Modules and Interfaces
*/
import express, { Request, Response } from "express";
// * 로 해서 ./items.service 에 있는 export를 모두 불러온다.
import * as ItemService from "./items.service";
import { BaseItem, Item } from "./item.interface";
/**
* Router Definition
*/
export const itemsRouter = express.Router();
/**
* Controller Definitions
*/
// GET items
itemsRouter.get("/", async (req: Request, res: Response) => {
try {
const items: Item[] = await ItemService.findAll();
res.status(200).send(items);
} catch (e) {
res.status(500).send(e.message);
}
});
// GET items/:id
itemsRouter.get("/:id", async (req: Request, res: Response) => {
try {
const id: number = parseInt(req.params.id, 10);
const item: Item = await ItemService.find(id);
if (item) {
return res.status(200).send(item);
}
res.status(404).send("item not found");
} catch (e) {
res.status(500).send(e.message);
}
});
// POST items
itemsRouter.post("/", async (req: Request, res: Response) => {
try {
const item: BaseItem = req.body;
const newItem: Item = await ItemService.create(item);
res.status(201).json(newItem);
} catch (e) {
res.status(500).send(e.message);
}
});
// PUT items/:id
itemsRouter.put("/:id", async (req: Request, res: Response) => {
try {
const id: number = parseInt(req.params.id, 10);
const itemUpdate: Item = req.body;
const existingItem: Item = await ItemService.find(id);
if (existingItem) {
const updatedItem = await ItemService.update(id, itemUpdate);
return res.status(200).json(updatedItem);
}
const newItem = await ItemService.create(itemUpdate);
res.status(201).json(newItem);
} catch (e) {
res.status(500).send(e.message);
}
});
// DELETE items/:id
itemsRouter.delete("/:id", async (req: Request, res: Response) => {
try {
const id: number = parseInt(req.params.id, 10);
const result: void | null = await ItemService.remove(id);
if (result === null) {
return res.status(404).send("item not found");
}
res.sendStatus(204);
} catch (e) {
res.status(500).send(e.message);
}
});
index.ts 수정
// 라우터 추가
import { itemsRouter } from "./items/items.router";
//미들웨어 추가
app.use("/api/menu/items", itemsRouter);
서버 실행 후 api 테스트
# get all items
GET /api/menu/items
# get a single item using an id parameter
GET /api/menu/items/:id
# create an item
POST /api/menu/items
{
"name": "Salad",
"price": 499,
"description": "Fresh",
"image": "https://images.ctfassets.net/23aumh6u8s0i/5pnNAeu0kev0P5Neh9W0jj/5b62440be149d0c1a9cb84a255662205/whatabyte_salad-sm.png"
}
# update an item using an id parameter
PUT /api/menu/items/:id
{
"name": "Salad",
"price": 500,
"description": "Fresh",
"image": "https://images.ctfassets.net/23aumh6u8s0i/5pnNAeu0kev0P5Neh9W0jj/5b62440be149d0c1a9cb84a255662205/whatabyte_salad-sm.png"
}
# remove an item using an id parameter
DELETE /api/menu/items/:id
'코딩캠프 > 내일배움캠프' 카테고리의 다른 글
[ TIL ] 01.25(수) 51일차 (0) | 2023.01.25 |
---|---|
[ WIL ] 01.16~20 10주차 (0) | 2023.01.22 |
[ TIL ] 01.19(목) 49일차 (0) | 2023.01.19 |
[ TIL ] 01.18(수) 48일차 (0) | 2023.01.18 |
[ TIL ] 01.17(화) 47일차 (0) | 2023.01.17 |