10. 백엔드와 데이터
백엔드의 일은 결국 셋이다 — 데이터를 지키고, 규칙을 강제하고, 형태를 맞춰 내준다
이 장에서 처음 나오는 말4개
RESTRepresentational State Transfer- 리소스 중심 URL(
/memos/3)과 HTTP 메서드(GET/POST/PATCH/DELETE)로 API를 설계하는 관례. 여전히 기본값이다. ORMObject-Relational Mapping- SQL 대신 코드의 객체로 DB를 다루게 해 주는 계층. Prisma, Drizzle. 타입 안전이 최대 장점, 숨은 쿼리가 최대 함정.
인증 / 인가Authentication / Authorization- "누구인가"의 확인 vs "무엇을 해도 되는가"의 판단. 붙여 쓰지만 다른 문제고, 사고는 주로 인가 쪽에서 난다.
JWTJSON Web Token- 서명된 claim을 담는 토큰 형식. 서명과 만료는 로컬에서 확인할 수 있지만, 철회·현재 권한 같은 실시간 상태는 별도 확인이 필요할 수 있다.
API — 프론트와 백의 계약
섹션 제목: “API — 프론트와 백의 계약”3장에서 봤듯 요즘은 백엔드가 메타 프레임워크 안(Route Handler)일 수도, 별도 서버일 수도, BaaS일 수도 있다. 어디 있든 프론트와 주고받는 계약의 스타일은 크게 셋이다.
GET /memos 목록POST /memos 생성GET /memos/3 하나 조회PATCH /memos/3 수정DELETE /memos/3 삭제리소스(명사)에 메서드(동사)를 조합한다. 도구·캐시·문서화 생태계가 넓어 외부와 오래 유지할 API의 무난한 출발점이다.
query { memo(id: 3) { title author { name } # 필요한 필드만 골라서 한 번에 }}화면마다 필요한 데이터 조합이 다른 대형 앱(모바일 + 웹 + 파트너)에서 “엔드포인트 폭발”과 “과잉 전송”을 푼다. 대신 서버 쪽 복잡도(스키마, 캐싱, N+1)를 산다. 조직이 커서 아픈 문제를 푸는 도구지 기본값이 아니다.
// tRPC — 프론트에서 백엔드 함수를 그냥 호출하는 것처럼 보인다const memo = await trpc.memo.byId.query(3);// ^ 반환 타입이 백엔드 코드에서 자동 추론된다“어차피 양쪽 다 TypeScript인데 URL 설계가 왜 필요하지?”에서 나온 스타일. 타입이 끝에서 끝까지 이어지는 게 강점. 같은 팀이 양쪽을 다 쥔 모노레포(9장)에서 빛나고, 같은 팀의 내부 API에 특히 잘 맞는다. 외부 공개 API로 쓰려면 생성 클라이언트·버전 정책·언어 간 호환을 별도로 설계한다.
인증 — 누구인지 어떻게 기억하는가
섹션 제목: “인증 — 누구인지 어떻게 기억하는가”HTTP는 무상태다 — 서버는 직전 요청을 기억하지 않는다. 로그인 상태는 두 방식으로 유지한다.
| 세션 | JWT (토큰) | |
|---|---|---|
| 원리 | 서버가 세션 저장소에 기록, 브라우저엔 ID만 쿠키로 | 서명된 토큰을 통째로 클라이언트에 |
| 기본 확인 | 세션 저장소 조회 | 서명·만료를 로컬 검증 가능 |
| 즉시 로그아웃 | 저장소에서 지우면 끝 | 어렵다 — 만료 전까지 유효 (짧은 만료 + 갱신으로 보완) |
| 주의점 | 저장소 가용성·확장 | 철회·권한 변경·토큰 보관과 갱신 |
요즘 스택에서 JWT가 많이 보이는 이유는 여러 서비스가 발급자의 공개 키로 토큰의 서명·만료를 검증할 수 있기 때문이다. 다만 토큰이 유효하다는 것과 지금도 그 작업을 허용해야 한다는 것은 다르다. 민감한 인가는 DB·정책 서버의 현재 상태를 다시 확인할 수 있다.
직접 구현하지 않는 것도 요령이다 — Supabase Auth, Auth.js, Clerk 같은 완성품이 있고, 비밀번호 저장·재설정·소셜 로그인은 틀리면 치명적인데 차별화는 안 되는 전형적인 영역이다.
DB — 기본값은 Postgres
섹션 제목: “DB — 기본값은 Postgres”| 선택지 | 무엇 | 언제 |
|---|---|---|
| Postgres | 관계형의 표준 | 기본값. 고민은 “왜 Postgres가 아닌가”부터 시작한다 |
| SQLite | 파일 하나짜리 관계형 | 로컬 도구, 소규모 서비스. 최근 서버용으로도 재조명 |
| MongoDB | 문서(JSON) 저장 | 스키마가 정말로 유동적일 때 — 생각보다 드물다 |
| Redis | 인메모리 키-값 | DB가 아니라 보조 — 캐시, 큐, 세션 저장소 |
관계형이 기본값인 이유 — 데이터의 관계(사용자-글-댓글)는 어떤 서비스에나 있고, JOIN·트랜잭션·제약 조건은 그 관계를 DB가 강제로 지켜 주는 장치다. 애플리케이션 코드가 실수하더라도 DB 제약은 마지막 방어선이 된다.
코드에서 DB를 다루는 층이 ORM이다(Prisma·Drizzle). 타입 안전한 쿼리가 장점이지만, 편해 보이는 코드가 뒤에서 쿼리를 수백 번 날리는 함정(N+1)이 고전적이라 내가 쓴 코드가 어떤 SQL이 되는지는 볼 줄 알아야 한다.
다 조립하면 — 데이터 경로 두 개
섹션 제목: “다 조립하면 — 데이터 경로 두 개”3장의 “경계가 흐린” 스택을 데이터 관점에서 다시 그리면 이렇게 된다.
단순한 읽기·쓰기는 브라우저가 BaaS에 직접(경로 ①, DB의 행 단위 보안 규칙 RLS가 지킨다), 복잡한 로직은 API 서버를 거쳐(경로 ②, 코드가 지킨다). 어느 요청을 어느 경로에 태울지가 이 아키텍처의 반복되는 설계 판단이다. 경로 ①의 자세한 이야기 — RLS가 실제로 어떻게 지켜 주는가 — 는 Supabase 덱이 통째로 다룬다.
10장 요약
섹션 제목: “10장 요약”- API 스타일 셋 — REST(기본값) / GraphQL(대형 조직의 문제 해결) / RPC(모노레포 풀스택)
- 프론트 검증은 UX, 보안 규칙은 서버·DB의 신뢰 경계에서 강제한다
- 인증은 세션 vs JWT — JWT도 철회·현재 권한 확인이 필요할 수 있다. 인증 프로토콜은 직접 만들지 말 것
- DB 기본값은 Postgres — 관계와 제약을 DB가 강제한다. ORM 뒤의 SQL은 볼 줄 알아야 한다
- 데이터 경로는 둘 — 단순은 BaaS 직행(RLS), 복잡은 API 서버 경유. 그 판단이 설계의 반복 지점