백엔드 개발
핵심 원칙
- 무상태 서비스 — 메모리에 세션 상태를 저장하지 말고, Redis 또는 DB를 사용합니다.
- 조기 실패(Fail Fast) — 경계에서 입력을 검증하고, 오류는 즉시 반환합니다.
- 멱등성(Idempotency) — POST/PUT 엔드포인트는 여러 번 호출되어도 안전해야 합니다.
- 관찰 가능성(Observability) — 모든 서비스에는 로그, 메트릭, 트레이스가 있어야 합니다.
API 엔드포인트 구조
요청 수명 주기
Request → Rate Limiting → Authentication → Authorization → Validation → Business Logic → Response
이 순서는 절대 건너뛰지 마세요. 인증은 비즈니스 로직 전에, 검증은 처리 전에 수행합니다.
응답 표준
// 성공
{ "data": { ... }, "meta": { "page": 1, "total": 100 } }
// 오류
{
"error": {
"code": "VALIDATION_ERROR",
"message": "사람이 읽을 수 있는 설명",
"details": [{ "field": "email", "issue": "잘못된 형식입니다" }]
}
}
인증 패턴
JWT 모범 사례
- 짧은 액세스 토큰 TTL(15분) + 긴 리프레시 토큰(7일)
- 리프레시 토큰은 localStorage가 아닌 httpOnly 쿠키에 저장합니다.
- 서명, 만료, 발급자, 수신자를 검증합니다.
- 리프레시 토큰은 사용 시 순환(rotate)시킵니다.
- 폐기 목록을 유지하거나, 짧은 수명의 토큰만 사용합니다.
세션 기반 인증
- 세션 ID를 httpOnly, Secure, SameSite 쿠키에 저장합니다.
- 세션 데이터는 TTL과 함께 Redis에 저장합니다.
- 로그인 후 세션 ID를 재생성합니다(세션 고정 방지).
권한 부여
// 라우트뿐만 아니라 서비스 계층에서 권한을 확인합니다.
async function getOrder(userId: string, orderId: string) {
const order = await db.orders.findById(orderId)
if (!order) throw new NotFoundError()
// 권한 확인 - 사용자는 자신의 주문만 볼 수 있습니다.
if (order.userId !== userId) throw new ForbiddenError()
return order
}
백그라운드 작업 및 큐
큐는 다음에 사용하세요:
- 이메일 발송
- 이미지/파일 처리
- 동기화될 필요가 없는 외부 API 호출
- 장기 실행 작업
작업 패턴
Fire-and-forget: 대기열에 추가하고 즉시 반환
Scheduled: 특정 시간에 실행 (cron)
Delayed: X분/시간 후 실행
Retry: 실패 시 백오프(backoff)를 적용하여 재시도
항상 구현해야 할 사항:
- 실패한 작업을 위한 데드 레터 큐(Dead Letter Queue)
- 중복 처리를 방지하는 멱등성 키(Idempotency key)
- 작업별 타임아웃
- 최대 재시도 횟수
오류 처리
// 구조화된 오류 계층 구조
class AppError extends Error {
constructor(
public message: string,
public statusCode: number,
public code: string
) { super(message) }
}
class ValidationError extends AppError {
constructor(details: ValidationDetail[]) {
super('유효성 검사 실패', 400, 'VALIDATION_ERROR')
this.details = details
}
}
// 전역 오류 처리기
app.use((err, req, res, next) => {
if (err instanceof AppError) {
return res.status(err.statusCode).json({ error: err })
}
// 예상치 못한 오류 로깅
logger.error('예상치 못한 오류', { err, req })
res.status(500).json({ error: { code: 'INTERNAL_ERROR' } })
})
미들웨어 체크리스트
모든 프로덕션 API에는 다음이 필요합니다:
- 요청 로깅 (메서드, 경로, 상태, 지속 시간)
- 속도 제한 (IP별, 사용자별, 엔드포인트별)
- CORS 설정 (명시적 허용 목록)
- Helmet / 보안 헤더
- 요청 ID 전파 (분산 추적용)
- 본문 크기 제한
- 타임아웃 처리
환경 설정
// 설정을 하드코딩하지 마세요 — 항상 환경 변수를 사용합니다.
const config = {
db: {
url: process.env.DATABASE_URL, // 필수
poolSize: parseInt(process.env.DB_POOL_SIZE ?? '10')
},
jwt: {
secret: process.env.JWT_SECRET, // 필수
expiresIn: process.env.JWT_TTL ?? '15m'
}
}
// 시작 시 필수 설정을 검증합니다 — 조기에 실패합니다.
const required = ['DATABASE_URL', 'JWT_SECRET']
for (const key of required) {
if (!process.env[key]) throw new Error(`필수 환경 변수 누락: ${key}`)
}