Foundations
Connecting Data
복사한 컴포넌트를 실제 DB·API 에 연결하는 권장 패턴입니다.
데이터가 인라인인 이유
Groudit 컴포넌트는 복사-붙여넣기 모델입니다. 한 파일만 가져가도 동작해야 하므로 데모 데이터를 각 파일 안에 인라인으로 둡니다. 공유 타입에 의존하지 않으려는 의도된 선택입니다.
Mapper 로 컬럼명 흡수
실무에서는 DB 컬럼명(예: cust_nm)이 코드 필드명(customer)과 다른 경우가 많습니다. DB row 를 앱 도메인 객체로 바꾸는 변환 함수(mapper)를 한 곳에 두면, DB 가 어떻게 생겼든 컴포넌트는 그대로 둘 수 있습니다.
1. 앱이 쓸 표준 도메인 타입을 정합니다.
// src/types/order.ts — 앱 표준 도메인 타입
export interface Order {
id: string;
number: string;
customer: string;
total: number;
status: "delivered" | "in_progress" | "overdue";
}2. DB row → 도메인 객체 변환을 한 파일에 모읍니다.
// src/lib/mappers/order.ts — DB row → 도메인 객체
import type { Order } from "@/types/order";
export function toOrder(row: Record<string, unknown>): Order {
return {
id: String(row.order_uuid), // DB: order_uuid
number: String(row.ord_no), // DB: ord_no
customer: String(row.cust_nm), // DB: cust_nm
total: Number(row.amt_total), // DB: amt_total
status: row.stat_cd as Order["status"],
};
}3. 화면은 도메인 객체만 사용합니다.
// 서버에서 조회 → 도메인 객체로 변환. 화면은 DB 를 모른다.
const rows = await db.query("select * from orders");
const orders = rows.map(toOrder);
// <OrderRow order={orders[0]} /> → order.customer, order.total연결 3단계
- 1
도메인 타입 정의
앱이 쓸 표준 필드명을 타입으로 정합니다.
- 2
Mapper 작성
DB row 를 도메인 객체로 바꾸는 함수를 한 파일에 둡니다.
- 3
객체 전달
컴포넌트의 인라인 데이터를 지우고 도메인 객체를 받게 합니다.
공통 타입은 어디에 두나
registry/
복사 대상 컴포넌트는 공통 타입을 import 하지 않습니다. 다른 파일 없이도 동작하도록 자급자족을 유지합니다.
src/
내 앱 코드에서는 공통 타입과 mapper 를 자유롭게 정의합니다. 복사 대상이 아니므로 공통화해도 됩니다.
보안
DB 자격증명·쿼리는 반드시 서버(서버 컴포넌트·API 라우트)에서만 실행하고, 브라우저로는 결과만 내려보내세요. 권한 검사는 서버와 DB 의 행 수준 보안(RLS)에서 강제해야 하며, 클라이언트가 보낸 값(가격·권한 등)은 절대 신뢰하지 않습니다. mapper 위치가 아니라 이 경계가 안전을 결정합니다.
용어 참고
같은 하나의 값이라도 부르는 이름은 레이어마다 다릅니다. DB 에서는 컬럼(column), 코드·타입·백엔드에서는 필드 또는 프로퍼티(field·property), 화면 폼에서는 입력 필드(input field)라고 부릅니다. 데이터는 모든 레이어에 존재하므로 '데이터'는 특정 레이어를 가리키는 용어가 아닙니다.