이벤트 기능은 아파트 캘린더에 표시될 일정을 관리하는 기능이다. 공지사항이나 투표가 등록될 때 자동으로 이벤트가 생성되고, 관리자가 직접 생성/수정/삭제도 할 수 있다.
이벤트 API 중 PUT /api/event는 생성과 수정을 하나의 엔드포인트로 처리한다. 같은 boardId에 이벤트가 이미 있으면 업데이트하고, 없으면 새로 만드는 방식이다.
const existing = await eventRepository.findEventByBoardId(boardId);
if (existing) {
await eventRepository.updateEvent(existing.id, { title, start, end });
} else {
await eventRepository.createEvent({ ... });
}
이 패턴을 upsert라고 부른다. Prisma에는 upsert() 메서드가 있지만, 여기서는 boardId가 unique 제약이 없어서 직접 조회 후 분기하는 방식을 썼다.
PUT HTTP 메서드를 사용한 것도 이 이유였다. POST는 새로운 리소스를 만들 때, PUT은 리소스를 덮어쓰거나 없으면 만들 때 쓰는 게 REST 관례다. 생성과 수정을 하나로 처리하는 이 API의 성격에 PUT이 더 맞았다.
이벤트 목록 조회는 apartmentId, year, month 세 가지 파라미터를 받는다. 특정 달의 이벤트만 가져오기 위해 해당 달의 시작과 끝 날짜를 계산해서 범위 쿼리를 날렸다.
const startOfMonth = new Date(year, month - 1, 1);
const endOfMonth = new Date(year, month, 0, 23, 59, 59, 999);
return prisma.event.findMany({
where: {
apartmentId,
start: { gte: startOfMonth },
end: { lte: endOfMonth },
},
orderBy: { start: "asc" },
});
new Date(year, month, 0)은 JavaScript에서 해당 달의 마지막 날을 구하는 트릭이다. 월을 하나 더하고 day를 0으로 설정하면 이전 달의 마지막 날이 나온다.
upsertEvent 함수는 tx라는 optional 파라미터를 받는다.
export const upsertEvent = async (
query: UpsertEventQuery,
tx?: any,
): Promise<void> => {
const client = tx ?? prisma;
...
};
이 설계는 공지사항 등록 시 이벤트를 자동 생성하는 케이스를 위해서였다. 공지사항 등록과 이벤트 생성이 하나의 트랜잭션에서 일어나야 할 때, 외부에서 시작된 트랜잭션 객체를 주입받아서 같이 사용할 수 있도록 한 것이다.
트랜잭션이 없으면 기본 prisma 클라이언트를 쓰고, 있으면 주입받은 tx를 쓴다. 이런 방식을 의존성 주입이라고 부르기도 한다.