이번 주차에서 주어진 과제는 이미 추가된 메뉴 추가 코드에 메뉴 삭제 API를 하나 더 만드는 것이다.
@/models/events.model.ts
class EventType {
// ... (생략) ...
async removeOrderByTransaction(args: IRemoveOrderReqParams): Promise<void> {
try {
await FirebaseAdmin.getInstance().Firestore.runTransaction(async (transaction) => {
const doc = await transaction.get(this.OrdersCollection(args.eventId).doc(args.guestId));
if (doc.exists === false) {
return Promise.reject(new DeleteOrderError(args.eventId, args.guestId));
}
transaction.delete(this.OrdersCollection(args.eventId).doc(args.guestId));
});
await this.updateCache({ eventId: args.eventId });
} catch (err) {
return Promise.reject(err);
}
}
}
@/models/errors/DeleteOrderError.ts
export class DeleteOrderError extends Error {
eventId: string;
docId: string;
constructor(eventId: string, docId: string) {
super(`Failed to delete doc of guest ${docId} in ${eventId}`);
this.eventId = eventId;
this.docId = docId;
}
}
과제에서 트랜잭션을 사용해보는 것을 추천했기 때문에 이를 사용하는 코드를 작성했다. 그리고 과제에서는 주어지지 않았지만 트랜잭션 중 실패했을 때, 이러한 상황을 알려주는 무언가가 필요하다고 생각해서 코드를 짜다보니 자연스럽게 예외도 별도로 정의해 주었다. 그리고 Promise의 reject 함수를 사용하여 에러를 반환했다.
지금 코드를 보니 reject가 중복으로 적용된 것 같다. try-catch문은 삭제해도 될 것으로 보인다.
@/controllers/event/event.controller.ts
// ...
import { Events } from '@/models/events.model';
export default class EventController {
// ...(생략)...
static async deleteOrder(req: Request, res: Response): Promise<void> {
const token = req.headers.authorization;
if (token === undefined) {
log('No auth');
return res.status(401).end();
}
try {
await FirebaseAdmin.getInstance().Auth.verifyIdToken(token);
} catch (err) {
log('Not valid auth');
return res.status(400).end();
}
const validateReq = validateParamWithData<IRemoveOrderReq>(
{
params: req.query,
},
JSCRemoveOrder,
);
if (validateReq.result === false) {
log('Invalid Request');
return res.status(400).send({
text: validateReq.errorMessage,
});
}
try {
const result = await Events.removeOrderByTransaction(validateReq.data.params);
return res.json(result);
} catch (err) {
log(err);
if (err instanceof DeleteOrderError) {
return res.status(404).send(err.message);
}
return res.status(500).send(err.toString());
}
}
}
기본적인 구조는 지난주와 했던 것에서 크게 차이는 없다. 앞서 MVC 구조에서 설명한 봐와 같이 모델 부분인 Events에서 작성한 함수를 호출하여 결과를 얻는다.
@/pages/api/events/[eventId]/orders/[guestId].ts
// ...(생략)...
export default async function handle(req: NextApiRequest, res: NextApiResponse): Promise<void> {
// eslint-disable-next-line no-console
const { method } = req;
log(method);
const supportMethod = ['DELETE'];
if (supportMethod.indexOf(method!) === -1) {
return res.status(400).end();
}
if (method === 'DELETE') {
await eventController.deleteOrder(req, res);
}
}
또한, 과제에서 주어진대로 Delete함수만 호출하도록 라우터를 작성하였다.