"DB 테이블은 다 만들었어요. 이제 버튼 누르면 저장이 되게 하고 싶은데... API를 만들어야 한다고요?"
지난 시간에 우리는 튼튼한 **창고(DB 테이블)**를 지었습니다. 하지만 창고만 있으면 뭐하나요? 물건을 넣고 빼는 사람이 없는데요.
이제 이 창고를 관리할 4명의 일꾼을 고용해야 합니다. 개발자들은 이 일꾼들을 묶어서 CRUD라고 부릅니다.
- Create (넣기)
- Read (꺼내기)
- Update (고치기)
- Delete (버리기)
회사 업무로 치면 '휴가 신청'과 똑같아요.
- 휴가 신청서 제출 (Create)
- 남은 휴가 며칠인지 확인 (Read)
- 날짜 변경 (Update)
- 휴가 취소 (Delete)
이 4가지만 할 줄 알면, 인스타그램(사진 올리고, 보고, 수정하고, 삭제)부터 배달앱(주문하고, 조회하고, 변경하고, 취소)까지 세상 모든 서비스를 만들 수 있습니다.
이 글을 읽고 나면:
- CRUD가 왜 서비스의 기본인지 이해합니다.
- Next.js에서 API Route로 백엔드(일꾼)를 만드는 법을 배웁니다.
- **유효성 검사(Validation)**라는 '문지기'를 세우는 법을 익힙니다.
1. API Route: 일꾼들의 사무소
"프론트엔드랑 백엔드를 따로 만들어야 하나요?" 전통적으로는 그렇습니다. 하지만 Next.js를 쓰면 한 프로젝트 안에서 다 할 수 있습니다.
app/api/ 폴더 안에 파일을 만들면, 그게 바로 정보가 드나드는 **뒷문(Backend API)**이 됩니다.
우리는 app/api/expenses/route.ts라는 파일을 만들어서 일꾼들을 배치할 거예요.
잠깐, 13편에서 배운 내용 기억나시나요? 식당 주방(백엔드)에 손님(프론트)이 아무나 들어오면 안 되죠? 그래서 **점원(API)**에게 주문서를 줘야 한다고 했잖아요. 이 파일이 바로 그 점원 역할을 하는 거예요.
- 프론트엔드: "저장해줘!" (주문서 제출)
- API Route: "확인했습니다. 창고에 넣을게요." (실제 작업)
- DB: (데이터 저장됨)
2. 실전: 지출 추가하기 (Create)
첫 번째 일꾼, Create(생성) 담당을 만들어봅시다. 하지만 무턱대고 아무거나 저장해주면 안 됩니다.
상황: 누군가가 금액에
-5000원을 적거나, 카테고리도 없이 저장을 요청했습니다. 이걸 그대로 저장하면 나중에 정산할 때 대참사가 일어납니다.
그래서 우리는 **유효성 검사(Validation)**라는 문지기를 세워야 합니다.

AI에게 지시하기 (상황극)
Tech Lead(나): "지출 내역을 저장하는 API를 만들자. 근데 이상한 데이터는 절대 받으면 안 돼."
┌───────────────────────────────────────────────────────────────┐
│ 1단계: API 코드 작성 (문지기 포함) 요청 │
├───────────────────────────────────────────────────────────────┤
│ │
│ 나: "Next.js App Router로 POST API를 만들어줘. │
│ │
│ 요구사항: │
│ 1. supabase.from('expenses').insert() 사용 │
│ 2. 유효성 검사 (필수): │
│ - amount(금액)가 0보다 커야 함. │
│ - category_id가 반드시 있어야 함. │
│ - 조건 안 맞으면 400 Bad Request 에러 반환. │
│ │
│ 파일명: app/api/expenses/route.ts │
│ │
│ AI: "알겠습니다! request.json()으로 데이터를 까보고, │
│ 조건이 안 맞으면 바로 return Response.json(...)으로 │
│ 돌려보내는 코드를 짤게요." │
│ │
└───────────────────────────────────────────────────────────────┘
AI가 작성해준 코드는 대략 이런 모양일 겁니다. (주석을 잘 보세요!)
// app/api/expenses/route.ts
import { createClient } from '@/utils/supabase/server';
export async function POST(request: Request) {
// 1. 주문서(데이터) 읽기
const body = await request.json();
// 🛑 2. 문지기 (유효성 검사)
if (!body.amount || body.amount <= 0) {
return Response.json(
{ error: '금액은 0보다 커야 합니다.' },
{ status: 400 } // 탈락!
);
}
// 3. 통과하면 창고(DB)에 저장
const supabase = createClient();
const { data, error } = await supabase
.from('expenses')
.insert(body)
.select();
if (error) {
return Response.json({ error: error.message }, { status: 500 });
}
return Response.json(data);
}
이제 "마이너스 금액"이 들어오면, DB 근처에도 못 가고 문지기 선에서 컷(Cut) 당합니다.
3. 실전: 지출 목록 조회하기 (Read)
두 번째 일꾼, Read(조회) 담당입니다. 저장된 데이터를 화면에 보여주려면 꺼내와야겠죠?
여기서 중요한 건 **조인(Join)**입니다.
DB에는 category_id: 1처럼 숫자만 저장되어 있습니다.
이걸 그대로 보여주면 사용자는 "1번이 뭔데?"라고 하겠죠.
그래서 **"1번은 '식비'야"**라고 연결해서 가져와야 합니다. 이것이 지난 시간에 배운 1 관계의 핵심입니다.
AI에게 지시하기
┌───────────────────────────────────────────────────────────────┐
│ 2단계: 데이터 조회 API 요청 │
├───────────────────────────────────────────────────────────────┤
│ │
│ 나: "이번엔 GET 메서드로 목록 불러오는 로직 추가해줘. │
│ │
│ 요구사항: │
│ 1. expenses 테이블 데이터를 가져와줘. │
│ 2. 조인(Join): categories 테이블 정보도 같이 보고 싶어. │
│ (화면에 '식비', '교통비'라고 보여줘야 하니까) │
│ 3. 정렬: 최신 날짜순으로 정렬해줘. │
│ │
│ AI: "네! Supabase의 .select('*, categories(*)') 문법을 쓰면 │
│ 연결된 정보를 한 번에 가져올 수 있어요. VLOOKUP이랑 비슷하죠?" │
│ │
└───────────────────────────────────────────────────────────────┘
이 코드가 완성되면, 이제 프론트엔드에서 /api/expenses로 요청을 보낼 때마다 정리된 장부(JSON 데이터)를 받을 수 있게 됩니다.
오늘의 핵심 정리
✅ CRUD: 만들고(C), 보고(R), 고치고(U), 지우는(D) 기능은 서비스의 알파이자 오레가다.
✅ API Route: Next.js에서는 app/api/ 폴더에 파일을 만들면 그게 바로 백엔드다.
✅ 문지기(Validation): 이상한 데이터가 DB를 더럽히지 않도록 입구에서 막아야 한다.
셀프체크
이 글을 이해했다면 다음 질문에 답할 수 있어야 합니다.
- "지출 금액은 무조건 양수여야 해"라는 규칙을 코드로 구현하는 것을 무엇이라 하나요?
- (정답: 유효성 검사 / Validation)
- DB에 저장된
category_id(숫자)를 실제 이름(문자)과 연결해서 가져오는 기술은?- (정답: 조인 / Join)
- 프론트엔드에서 데이터를 서버로 보낼 때(저장), 봉투에 어떤 태그를 붙여서 보내야 할까요? (GET vs POST)
- (정답: POST)
다음 글 예고
일꾼(API)도 고용했고, 창고(DB)도 튼튼합니다. 그런데 우리 서비스엔 아직 없는 게 하나 있습니다.
"오늘 내가 쓴 10달러, 한국 돈으로 얼마지?"
달러 환율은 매일 변하잖아요. 이걸 우리가 직접 입력할 수는 없습니다. 남이 이미 만들어둔 정보(은행 환율)를 빌려와야 합니다.
다음 시간에는 내 서비스에 남의 서비스 기능을 갖다 붙이는 마법, **[외부 API 연결 (feat. 환율 계산)]**을 해보겠습니다.