본문 바로가기
Backend2024년 4월 12일6분 읽기

12 Factor App — 클라우드 네이티브 앱 설계 원칙

YS
김영삼
조회 449

12 Factor App이란?

12 Factor App은 Heroku의 공동 창립자 Adam Wiggins가 제안한 SaaS 앱 개발 방법론입니다. 클라우드 환경에서 이식성, 확장성, 유지보수성을 극대화하기 위한 12가지 원칙을 정의합니다. 마이크로서비스와 컨테이너 기반 배포가 표준이 된 현재, 이 원칙의 중요성은 더욱 커지고 있습니다.

12가지 원칙 요약

#원칙핵심 내용
1Codebase하나의 코드베이스를 버전 관리, 여러 배포
2Dependencies명시적 의존성 선언 및 격리
3Config환경 변수에 설정 저장
4Backing Services연결된 리소스를 교체 가능하게
5Build, Release, Run빌드와 실행 단계 엄격히 분리
6Processes무상태 프로세스로 실행
7Port Binding포트 바인딩으로 서비스 노출
8Concurrency프로세스 모델로 수평 확장
9Disposability빠른 시작과 그레이스풀 셧다운
10Dev/Prod Parity개발, 스테이징, 프로덕션 최대한 동일
11Logs로그를 이벤트 스트림으로 처리
12Admin Processes관리 작업을 일회성 프로세스로

핵심 원칙 상세 분석

3. Config — 환경 변수 활용

설정은 코드에 하드코딩하지 않고, 반드시 환경 변수로 분리해야 합니다. 데이터베이스 URL, API 키, 외부 서비스 엔드포인트 등이 해당됩니다.

// BAD: 설정을 코드에 하드코딩
const dbUrl = 'postgres://user:pass@prod-db:5432/myapp';

// GOOD: 환경 변수에서 읽기
const dbUrl = process.env.DATABASE_URL;
if (!dbUrl) throw new Error('DATABASE_URL is required');

// docker-compose.yml에서 환경 변수 주입
// services:
//   app:
//     environment:
//       - DATABASE_URL=postgres://user:pass@db:5432/myapp
//       - REDIS_URL=redis://cache:6379
//       - NODE_ENV=production

6. Processes — 무상태 프로세스

앱 프로세스는 무상태(stateless)여야 합니다. 세션 데이터, 파일 캐시 등을 로컬에 저장하면 수평 확장 시 문제가 생깁니다.

// BAD: 메모리에 세션 저장 (스케일아웃 불가)
const sessions = {};
app.use((req, res, next) => {
  sessions[req.sessionId] = req.user;
  next();
});

// GOOD: Redis 같은 외부 스토어 사용
const RedisStore = require('connect-redis').default;
const session = require('express-session');
app.use(session({
  store: new RedisStore({ client: redisClient }),
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  cookie: { secure: true, maxAge: 86400000 }
}));

9. Disposability — 빠른 시작과 그레이스풀 셧다운

프로세스는 빠르게 시작하고, SIGTERM을 받으면 진행 중인 요청을 완료한 후 정상 종료해야 합니다.

const server = app.listen(PORT, () => {
  console.log(\`Server ready on port \${PORT}\`);
});

process.on('SIGTERM', () => {
  console.log('SIGTERM received, graceful shutdown...');
  server.close(() => {
    prisma.$disconnect();
    console.log('Process terminated');
    process.exit(0);
  });
  setTimeout(() => process.exit(1), 30000);
});

11. Logs — 이벤트 스트림

앱은 로그 파일의 라우팅이나 저장에 관여하지 않고, stdout으로 출력만 합니다. 로그 수집은 실행 환경의 책임입니다.

// GOOD: 구조화된 로그를 stdout으로 출력
const pino = require('pino');
const logger = pino({
  level: process.env.LOG_LEVEL || 'info',
  formatters: {
    level: (label) => ({ level: label })
  }
});
logger.info({ userId: user.id, action: 'login' }, 'User logged in');

실전 적용 체크리스트

  • 모든 설정이 환경 변수로 분리되어 있는가?
  • 의존성이 package.json, requirements.txt 등에 명시적으로 선언되어 있는가?
  • 프로세스가 무상태이며, 세션/캐시가 외부 저장소에 있는가?
  • SIGTERM 핸들러가 구현되어 있는가?
  • 로그가 stdout/stderr로만 출력되는가?
  • 개발 환경과 프로덕션 환경이 Docker 등으로 동일하게 구성되어 있는가?

마치며

12 Factor App의 원칙은 Kubernetes, Docker, 서버리스 환경에서 거의 필수입니다. 처음부터 완벽하게 적용하기보다는 가장 위반이 많은 Config, Processes, Logs부터 개선해 나가는 것을 권장합니다.

댓글 0

아직 댓글이 없습니다.
Ctrl+Enter로 등록