NestJS-ClickUp-Notion CRM에서 사용하지 않는 라우트 정리, 개발 시트 수정 및 인시던트 연결하기
요약
NestJS 기반 CRM 시스템의 API 라우트 정리, 컨트롤러 참조 오류 수정 및 데이터 동기화 작업을 다룹니다. 사용하지 않는 엔드포인트를 제거하고, 개발 시트 생성 로직을 보완하며, ClickUp 인시던트 데이터를 연결하여 시스템 안정성을 높였습니다.
핵심 포인트
- 사용하지 않는 Dead routes 제거를 통한 API 간소화 및 404 오류 방지
- 컨트롤러 간 잘못된 메서드 참조 수정으로 런타임 TypeError 해결
- 개발 레코드 생성 시 Craft 시트가 자동 생성되도록 로직 보완
- ClickUp API 연결을 통해 실제 인시던트 데이터와 UI 동기화
NestJS-ClickUp-Notion CRM에서 사용하지 않는 라우트 정리, 개발 시트 수정 및 인시던트 연결하기
요약 (TL;DR):
DevelopmentsController에서 사용하지 않는 라우트를 제거하고, VentasController의 잘못된 컨트롤러 참조를 수정했으며, Craft의 개발 시트(development sheets)를 위한 누락된 create 흐름을 추가하고, ClickUp에서 데이터를 가져오도록 인시던트(incident) API를 연결했습니다. 이러한 변경 사항을 통해 런타임 오류를 제거하고, API 표면을 간소화하며, 인시던트 데이터를 개발 티켓과 동기화했습니다.
문제 상황
CRM API에서 통합 테스트 및 프로덕션 환경 중에 발생하는 여러 버그가 드러났습니다:
- 사용하지 않는 라우트 (Dead routes) –
DevelopmentsController가 데이터베이스 스키마에 존재하지 않는 엔드포인트를 여전히 내보내고 있어, 404 오류와 불필요한 검증 오버헤드를 유발했습니다. - 잘못된 컨트롤러 참조 (Wrong controller reference) –
VentasController가 이름이 변경된DevelopmentsController의 메서드를 호출하려고 시도하여TypeError: undefined is not a function오류가 발생했습니다. - 개발 시트 생성 누락 (Missing development sheet creation) – 새로운 개발 레코드가 삽입될 때, 그에 상응하는 Craft 시트가 생성되지 않아 후속 분석 프로세스가 중단되었습니다.
- 가짜 인시던트 카드 (Fake incident cards) – 프론트엔드에서 실제 데이터와 매핑되지 않는 7개의 플레이스홀더 인시던트 카드를 렌더링하여 사용자를 혼란스럽게 하고 UI를 복잡하게 만들었습니다.
- 동기화되지 않은 문서 (Docs out of sync) –
CLAUDE.md및CLAUDE_CODE_CONTEXT.md파일이 현재 커밋 해시(bad3d3b) 대신 이전 커밋 해시(08ed379)를 참조하고 있었습니다.
이러한 문제들이 런타임 오류로 나타나고 UI 상태를 혼란스럽게 만들었기 때문에, 깨끗하고 재현 가능한 해결책이 필요했습니다.
처음 시도한 것
실패하는 테스트의 스택 트레이스(stack traces)를 조사하는 것부터 시작했습니다. 첫 번째 오류는 개발 엔드포인트에서 발생하는 404 오류였으므로, 빠르게 grep을 실행했습니다:
grep -R "DevelopmentsController" -n apps/api/src/ventas
이를 통해 해당 컨트롤러(controller)에 데이터베이스 필드와 전혀 일치하지 않는 @Get('dead-route')가 있음을 확인했습니다. 처음에는 단순히 주석 처리할까 생각했지만, 그렇게 하면 Swagger 문서에 고립된(orphaned) 라우트가 남게 됩니다. 그 다음 수동으로 라우트를 삭제하려고 시도했으나, 컴파일러가 누락된 임포트(import)에 대해 오류를 발생시켰습니다.
다음으로, VentasController 오류를 살펴보았습니다. 스택 트레이스(stack trace)는 더 이상 존재하지 않는 this.developmentsService.create() 호출을 가리키고 있었습니다. 오류를 우회하기 위해 더미 메서드(dummy method)로 서비스를 패치했지만, 근본적인 불일치 문제는 해결되지 않았습니다.
누락된 시트 생성 건에 대해서는, 요청이 컨트롤러에 도달했는지 확인하기 위해 데이터베이스 삽입(insert) 이후에 임시로 콘솔 로그(console log)를 추가했습니다. 요청은 도달했지만, 함수가 호출되지 않았기 때문에 이후의 CraftService.createSheet() 호출은 실행되지 않았습니다.
마지막으로 프론트엔드(front-end)를 확인했습니다. page.tsx에는 7개의 카드가 하드코딩된 배열(hard-coded array)로 포함되어 있었습니다. 이를 수동으로 제거했지만, 상태(state)가 여전히 빈 배열을 반환하는 오래된 엔드포인트(endpoint)로부터 데이터를 가져오고 있었기 때문에 UI에는 여전히 플레이스홀더(placeholder)가 표시되었습니다.
구현 (The Implementation)
아래는 정확한 파일 차이(diff)와 아키텍처 결정 사항을 포함하여 제가 수행한 구체적인 변경 사항에 대한 설명입니다.
1. DevelopmentsController 내 불필요한 라우트 정리
- import { Body, Controller, Delete, Get, Param, Patch, Post, Query, UseGuards } from "@nestjs/common";
+ import { Body, Controller, Delete, Get, Param, Patch, UseGuards } from "@nestjs/common";
사용하지 않는 Post 임포트와 @Get('dead-route') 엔드포인트를 완전히 제거했습니다:
- @Get('dead-route')
- async getDeadRoute() {
- return { message: "This route is dead" };
...
이를 통해 Swagger 문서를 깔끔하게 유지하고 검증(validation) 오버헤드를 줄였습니다.
2. VentasController의 잘못된 컨트롤러 참조 수정
@@
import { RequirePerm } from "../auth/perm.decorator.js";
import { query as db } from "../db/db.js";
...
의존성 주입(injection)과 메서드 호출을 업데이트했습니다:
- @Post()
- async createVenta(@Body() body: any) {
- return this.developmentsController.create(body);
...
이제 VentasController가 서비스 레이어 (service layer)로 올바르게 위임합니다.
3. 개발 시트 생성 로직 추가
DevelopmentsController에서 새로운 개발 레코드 (development record)를 삽입한 후, 이제 CraftService.createSheet()를 호출합니다:
- .catch(()=>{});
- //
+ .catch(()=>{});
...
CraftService는 이제 외부 API 호출을 try/catch 블록으로 감싸고 실패 시 로그를 남깁니다:
async createSheet(data: { id: string; title: "string; status: string }) {"
try {
await this.httpService.post('/craft/sheets', data).toPromise();
...
4. 인시던트(Incidents)를 개발(Developments)에 연결
IncidentsController에 개발 ID (development ID)와 연결된 ClickUp 태스크 (tasks)를 가져오는 새로운 엔드포인트 (endpoint)를 추가했습니다:
@Get('development/:devId')
async getIncidentsByDev(@Param('devId') devId: string) {
const tasks = await this.clickUpService.getTasksByList(CU_LISTS.INCIDENTS, devId);
...
이는 기존의 하드코딩된 (hard-coded) 데이터를 대체하고 ClickUp에서 실제 태스크를 가져오므로, 인시던트가 항상 최신 상태로 유지되도록 보장합니다.
5. 프론트엔드(Front-End)에서 가짜 카드 제거
apps/web/src/app/craft/page.tsx에서 7개의 카드로 구성된 정적 배열 (static array)을 제거했습니다:
- const fakeCards = Array.from({ length: 7 }, (_, i) => ({
- id: `fake-${i}`,
- title: `"Fake Incident ${i + 1}"`,
...
대신, 이제 실제 인시던트를 가져옵니다:
const { data: incidents } = useSWR(`/api/incidents/development/${devId}`);
그리고 이를 렌더링합니다:
{incidents?.map(incident => (
<IncidentCard key={incident.id} {...incident} />
))}
6. 문서 업데이트
새로운 커밋 해시 (commit hash)를 참조하도록 CLAUDE.md 및 CLAUDE_CODE_CONTEXT.md를 패치했습니다:
- **Último commit:** `08ed379`
+ **Último commit:** `bad3d3b`
이를 통해 문서가 코드베이스 (codebase)와 동기화된 상태를 유지합니다.
핵심 요약 (Key Takeaway)
항상 API 표면 (API surface)을 데이터 모델 (data model) 및 서비스 레이어 (service layer)와 일치시켜 유지하세요.
사용하지 않는 라우트 (dead routes)와 일치하지 않는 컨트롤러 (controller) 참조는 부하가 걸리거나 통합 (integration) 과정 중에만 나타나는 잠재적인 오류 (silent failures)를 유발합니다. 사용하지 않는 엔드포인트 (endpoints)를 체계적으로 제거하고, 서비스 주입 (service injections)을 수정하며, UI 데이터를 실제 엔드포인트와 연결함으로써 공격 표면 (attack surface)을 줄이고 유지보수성 (maintainability)을 향상할 수 있습니다.
다음 단계 (What's Next)
- 자동화된 테스트 (automated tests) 추가: 새로운 인시던트 (incident) 엔드포인트에 대한 테스트를 추가하여 API 변경 시에도 ClickUp 통합이 안정적으로 유지되도록 합니다.
- 낙관적 UI 업데이트 (optimistic UI updates) 구현: 인시던트 생성 시 낙관적 UI 업데이트를 구현하여 프론트엔드 (front-end)에 변경 사항이 즉시 반영되도록 합니다.
CraftService리팩터링 (Refactor):CraftService를 범용 SDK 래퍼 (generic SDK wrapper)를 사용하도록 리팩터링하여 ClickUp 서비스와의 중복을 줄입니다.
이러한 단계들은 우리의 CRM, Notion, ClickUp, 그리고 프론트엔드 간의 통합 루프 (integration loop)를 더욱 견고하게 만들 것입니다.
vibecoding #buildinpublic #nestjs #typescript #clickup #notion #api #frontend #devops #docker #networking
제 Build in Public 시리즈의 일부입니다 — 멕시코 플라야 델 카르멘 (Playa del Carmen, México)에서 Building PlayaMXCRM을 구축하는 실제 과정을 공유합니다.
Repo: zaerohell/VS · 2026-08-03
#playadev #buildinpublic
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기