# Site11 프로젝트 기술 면접 가이드 ## 프로젝트 개요 - **아키텍처**: API Gateway 패턴 기반 마이크로서비스 - **기술 스택**: FastAPI, React 18, TypeScript, MongoDB, Redis, Docker, Kubernetes - **도메인**: 뉴스/미디어 플랫폼 (다국가/다언어 지원) --- ## 1. 백엔드 아키텍처 (5문항) ### Q1. API Gateway vs Service Mesh **질문**: Console이 API Gateway 역할을 합니다. Service Mesh(Istio)와 비교했을 때 장단점은? > [!success]- 모범 답안 > > **API Gateway 패턴 (현재)**: > - ✅ 중앙화된 인증/라우팅, 구축 간단, 단일 진입점 > - ❌ SPOF 가능성, 병목 위험, Gateway 변경 시 전체 영향 > > **Service Mesh (Istio)**: > - ✅ 서비스 간 직접 통신(낮은 지연), mTLS 자동, 세밀한 트래픽 제어 > - ❌ 학습 곡선, 리소스 오버헤드(Sidecar), 복잡한 디버깅 > > **선택 기준**: > - 30개 이하 서비스 → API Gateway > - 50개 이상, 복잡한 통신 패턴 → Service Mesh --- ### Q2. FastAPI 비동기 처리 **질문**: `async/await` 사용 시기와 Motor vs PyMongo 선택 이유는? > [!success]- 모범 답안 > > **동작 차이**: > ```python > # Sync: 요청 1(50ms) → 요청 2(50ms) = 총 100ms > # Async: 요청 1 & 요청 2 병행 처리 = 총 ~50ms > ``` > > **Motor (Async) 추천**: > - I/O bound 작업(DB, API 호출)에 적합 > - 동시 요청 시 처리량 증가 > - FastAPI의 비동기 특성과 완벽 호환 > > **PyMongo (Sync) 사용**: > - CPU bound 작업(이미지 처리, 데이터 분석) > - Sync 전용 라이브러리 사용 시 > > **주의**: `time.sleep()`은 전체 이벤트 루프 블로킹 → `asyncio.sleep()` 사용 --- ### Q3. 마이크로서비스 간 통신 **질문**: REST API, Redis Pub/Sub, gRPC 각각 언제 사용? > [!success]- 모범 답안 > > | 방식 | 사용 시기 | 특징 | > |------|----------|------| > | **REST** | 즉시 응답 필요, 데이터 조회 | Synchronous, 구현 간단 | > | **Pub/Sub** | 이벤트 알림, 여러 서비스 반응 | Asynchronous, Loose coupling | > | **gRPC** | 내부 서비스 통신, 고성능 | HTTP/2, Protobuf, 타입 안정성 | > > **예시**: > - 사용자 조회 → REST (즉시 응답) > - 사용자 생성 알림 → Pub/Sub (비동기 처리) > - 마이크로서비스 간 내부 호출 → gRPC (성능) --- ### Q4. 데이터베이스 전략 **질문**: Shared MongoDB Instance vs Separate Instances 장단점? > [!success]- 모범 답안 > > **현재 전략 (Shared Instance, Separate DBs)**: > ``` > MongoDB (site11-mongodb:27017) > ├── console_db > ├── users_db > └── news_api_db > ``` > > **장점**: 운영 단순, 리소스 효율, 백업 간편, 비용 절감 > **단점**: 격리 부족, 확장성 제한, 장애 전파, 리소스 경합 > > **Separate Instances**: > - 장점: 완전 격리, 독립 확장, 장애 격리 > - 단점: 운영 복잡, 비용 증가, 트랜잭션 불가 > > **서비스 간 데이터 접근**: > - ❌ 직접 DB 접근 금지 > - ✅ API 호출 또는 Data Duplication (비정규화) > - ✅ Event-driven 동기화 --- ### Q5. JWT 인증 및 보안 **질문**: Access Token vs Refresh Token 차이와 탈취 대응 방안? > [!success]- 모범 답안 > > | 구분 | Access Token | Refresh Token | > |------|--------------|---------------| > | 목적 | API 접근 권한 | Access Token 재발급 | > | 만료 | 짧음 (15분-1시간) | 길음 (7일-30일) | > | 저장 | 메모리 | HttpOnly Cookie | > | 탈취 시 | 제한적 피해 | 심각한 피해 | > > **탈취 대응**: > 1. **Refresh Token Rotation**: 재발급 시 새로운 토큰 쌍 생성 > 2. **Blacklist**: Redis에 로그아웃된 토큰 저장 > 3. **Device Binding**: 디바이스 ID로 제한 > 4. **IP/User-Agent 검증**: 비정상 접근 탐지 > > **서비스 간 통신 보안**: > - Service Token (API Key) > - mTLS (Production) > - Network Policy (Kubernetes) --- ## 2. 프론트엔드 (4문항) ### Q6. React 18 주요 변화 **질문**: Concurrent Rendering과 Automatic Batching 설명? > [!success]- 모범 답안 > > **1. Concurrent Rendering**: > ```tsx > const [query, setQuery] = useState(''); > const [isPending, startTransition] = useTransition(); > > // 긴급 업데이트 (사용자 입력) > setQuery(e.target.value); > > // 비긴급 업데이트 (검색 결과) - 중단 가능 > startTransition(() => { > fetchSearchResults(e.target.value); > }); > ``` > → 사용자 입력이 항상 부드럽게 유지 > > **2. Automatic Batching**: > ```tsx > // React 17: fetch 콜백에서 2번 리렌더링 > fetch('/api').then(() => { > setCount(c => c + 1); // 리렌더링 1 > setFlag(f => !f); // 리렌더링 2 > }); > > // React 18: 자동 배칭으로 1번만 리렌더링 > ``` > > **기타**: `Suspense`, `useDeferredValue`, `useId` --- ### Q7. TypeScript 활용 **질문**: Backend API 타입을 Frontend에서 안전하게 사용하는 방법? > [!success]- 모범 답안 > > **방법 1: OpenAPI 코드 생성** (추천) > ```bash > npm install openapi-typescript-codegen > openapi --input http://localhost:8000/openapi.json --output ./src/api/generated > ``` > > ```typescript > // 자동 생성된 타입 사용 > import { ArticlesService, Article } from '@/api/generated'; > > const articles = await ArticlesService.getArticles({ > category: 'tech', // ✅ 타입 체크 > limit: 10 > }); > ``` > > **방법 2: tRPC** (TypeScript 풀스택) > ```typescript > // Backend > export const appRouter = t.router({ > articles: { > list: t.procedure.input(z.object({...})).query(...) > } > }); > > // Frontend - End-to-end 타입 안정성 > const { data } = trpc.articles.list.useQuery({ category: 'tech' }); > ``` > > **방법 3: 수동 타입 정의** (작은 프로젝트) --- ### Q8. 상태 관리 **질문**: Context API, Redux, Zustand, React Query 각각 언제 사용? > [!success]- 모범 답안 > > | 도구 | 사용 시기 | 특징 | > |------|----------|------| > | **Context API** | 전역 테마, 인증 상태 | 내장, 리렌더링 주의 | > | **Redux** | 복잡한 상태, Time-travel | Boilerplate 많음, DevTools | > | **Zustand** | 간단한 전역 상태 | 경량, 간결, 리렌더링 최적화 | > | **React Query** | 서버 상태 | 캐싱, 리페칭, 낙관적 업데이트 | > > **핵심**: 전역 상태 vs 서버 상태 구분 > - 전역 UI 상태 → Zustand/Redux > - 서버 데이터 → React Query --- ### Q9. Material-UI 최적화 **질문**: 번들 사이즈 최적화와 테마 커스터마이징 방법? > [!success]- 모범 답안 > > **번들 최적화**: > ```tsx > // ❌ 전체 import > import { Button, TextField } from '@mui/material'; > > // ✅ Tree shaking > import Button from '@mui/material/Button'; > import TextField from '@mui/material/TextField'; > ``` > > **Code Splitting**: > ```tsx > const Dashboard = lazy(() => import('./pages/Dashboard')); > > }> > > > ``` > > **테마 커스터마이징**: > ```tsx > import { createTheme, ThemeProvider } from '@mui/material/styles'; > > const theme = createTheme({ > palette: { > mode: 'dark', > primary: { main: '#1976d2' }, > }, > }); > > > > > ``` --- ## 3. DevOps & 인프라 (6문항) ### Q10. Docker Multi-stage Build **질문**: Multi-stage build의 장점과 각 stage 역할은? > [!success]- 모범 답안 > > ```dockerfile > # Stage 1: Builder (빌드 환경) > FROM node:18-alpine AS builder > WORKDIR /app > COPY package.json ./ > RUN npm install > COPY . . > RUN npm run build > > # Stage 2: Production (런타임) > FROM nginx:alpine > COPY --from=builder /app/dist /usr/share/nginx/html > ``` > > **장점**: > - 빌드 도구 제외 → 이미지 크기 90% 감소 > - Layer caching → 빌드 속도 향상 > - 보안 강화 → 소스코드 미포함 --- ### Q11. Kubernetes 배포 전략 **질문**: Rolling Update, Blue/Green, Canary 차이와 선택 기준? > [!success]- 모범 답안 > > | 전략 | 특징 | 적합한 경우 | > |------|------|------------| > | **Rolling Update** | 점진적 교체 | 일반 배포, Zero-downtime | > | **Blue/Green** | 전체 전환 후 스위칭 | 빠른 롤백 필요 | > | **Canary** | 일부 트래픽 테스트 | 위험한 변경, A/B 테스트 | > > **News API 같은 중요 서비스**: Canary (10% → 50% → 100%) > > **Probe 설정**: > ```yaml > livenessProbe: # 재시작 판단 > httpGet: > path: /health > readinessProbe: # 트래픽 차단 판단 > httpGet: > path: /ready > ``` --- ### Q12. 서비스 헬스체크 **질문**: Liveness Probe vs Readiness Probe 차이? > [!success]- 모범 답안 > > | Probe | 실패 시 동작 | 실패 조건 예시 | > |-------|-------------|---------------| > | **Liveness** | Pod 재시작 | 데드락, 메모리 누수 | > | **Readiness** | 트래픽 차단 | DB 연결 실패, 초기화 중 | > > **구현**: > ```python > @app.get("/health") # Liveness > async def health(): > return {"status": "ok"} > > @app.get("/ready") # Readiness > async def ready(): > # DB 연결 확인 > if not await db.ping(): > raise HTTPException(503) > return {"status": "ready"} > ``` > > **Startup Probe**: 초기 구동이 느린 앱 (DB 마이그레이션 등) --- ### Q13. 외부 DB 연결 **질문**: MongoDB/Redis를 클러스터 외부에서 운영하는 이유? > [!success]- 모범 답안 > > **현재 전략 (외부 운영)**: > - ✅ 데이터 영속성 (클러스터 재생성 시 보존) > - ✅ 관리 용이 (단일 인스턴스) > - ✅ 개발 환경 공유 > > **StatefulSet (내부 운영)**: > - ✅ Kubernetes 통합 관리 > - ✅ 자동 스케일링 > - ❌ PV 관리 복잡 > - ❌ 백업/복구 부담 > > **선택 기준**: > - 개발/스테이징 → 외부 (간편) > - 프로덕션 → Managed Service (RDS, Atlas) 추천 --- ### Q14. Docker Compose vs Kubernetes **질문**: 언제 Docker Compose만으로 충분하고 언제 Kubernetes 필요? > [!success]- 모범 답안 > > | 기능 | Docker Compose | Kubernetes | > |------|---------------|-----------| > | 컨테이너 실행 | ✅ | ✅ | > | Auto-scaling | ❌ | ✅ | > | Self-healing | ❌ | ✅ | > | Load Balancing | 기본적 | 고급 | > | 배포 전략 | 단순 | 다양 (Rolling, Canary) | > | 멀티 호스트 | ❌ | ✅ | > > **Docker Compose 충분**: > - 단일 서버 > - 소규모 서비스 (< 10개) > - 개발/테스트 환경 > > **Kubernetes 필요**: > - 고가용성 (HA) > - 자동 확장 > - 수십~수백 개 서비스 --- ### Q15. 모니터링 및 로깅 **질문**: 마이크로서비스 환경에서 로그 수집 및 모니터링 방법? > [!success]- 모범 답안 > > **로깅 스택**: > - **ELK**: Elasticsearch + Logstash + Kibana > - **EFK**: Elasticsearch + Fluentd + Kibana > - **Loki**: Grafana Loki (경량) > > **모니터링**: > - **Prometheus**: 메트릭 수집 > - **Grafana**: 대시보드 > - **Jaeger/Zipkin**: Distributed Tracing > > **Correlation ID**: > ```python > @app.middleware("http") > async def add_correlation_id(request: Request, call_next): > correlation_id = request.headers.get("X-Correlation-ID") or str(uuid.uuid4()) > request.state.correlation_id = correlation_id > > # 모든 로그에 포함 > logger.info(f"Request {correlation_id}: {request.url}") > > response = await call_next(request) > response.headers["X-Correlation-ID"] = correlation_id > return response > ``` > > **3가지 관찰성**: > - Metrics (숫자): CPU, 메모리, 요청 수 > - Logs (텍스트): 이벤트, 에러 > - Traces (흐름): 요청 경로 추적 --- ## 4. 데이터 및 API 설계 (3문항) ### Q16. RESTful API 설계 **질문**: News API 엔드포인트를 RESTful하게 설계하면? > [!success]- 모범 답안 > > ``` > GET /api/v1/outlets # 언론사 목록 > GET /api/v1/outlets/{outlet_id} # 언론사 상세 > GET /api/v1/outlets/{outlet_id}/articles # 특정 언론사 기사 > > GET /api/v1/articles # 기사 목록 > GET /api/v1/articles/{article_id} # 기사 상세 > POST /api/v1/articles # 기사 생성 > PUT /api/v1/articles/{article_id} # 기사 수정 > DELETE /api/v1/articles/{article_id} # 기사 삭제 > > # 쿼리 파라미터 > GET /api/v1/articles?category=tech&limit=10&offset=0 > > # 다국어 지원 > GET /api/v1/ko/articles # URL prefix > GET /api/v1/articles (Accept-Language: ko-KR) # Header > ``` > > **RESTful 원칙**: > 1. 리소스 중심 (명사 사용) > 2. HTTP 메소드 의미 준수 > 3. Stateless > 4. 계층적 구조 > 5. HATEOAS (선택) --- ### Q17. MongoDB 스키마 설계 **질문**: Outlets-Articles-Keywords 관계를 MongoDB에서 모델링? > [!success]- 모범 답안 > > **방법 1: Embedding** (Read 최적화) > ```json > { > "_id": "article123", > "title": "Breaking News", > "outlet": { > "id": "outlet456", > "name": "TechCrunch", > "logo": "url" > }, > "keywords": ["AI", "Machine Learning"] > } > ``` > - ✅ 1번의 쿼리로 모든 데이터 > - ❌ Outlet 정보 변경 시 모든 Article 업데이트 > > **방법 2: Referencing** (Write 최적화) > ```json > { > "_id": "article123", > "title": "Breaking News", > "outlet_id": "outlet456", > "keyword_ids": ["kw1", "kw2"] > } > ``` > - ✅ 데이터 일관성 > - ❌ 조회 시 여러 쿼리 필요 (JOIN) > > **하이브리드** (추천): > ```json > { > "_id": "article123", > "title": "Breaking News", > "outlet_id": "outlet456", > "outlet_name": "TechCrunch", // 자주 조회되는 필드만 복제 > "keywords": ["AI", "ML"] // 배열 embedding > } > ``` > > **인덱싱**: > ```python > db.articles.create_index([("outlet_id", 1), ("published_at", -1)]) > db.articles.create_index([("keywords", 1)]) > ``` --- ### Q18. 페이지네이션 전략 **질문**: Offset-based vs Cursor-based Pagination 차이? > [!success]- 모범 답안 > > **Offset-based** (전통적): > ```python > # GET /api/articles?page=2&page_size=10 > skip = (page - 1) * page_size > articles = db.articles.find().skip(skip).limit(page_size) > ``` > > - ✅ 구현 간단, 페이지 번호 표시 > - ❌ 대량 데이터에서 느림 (SKIP 연산) > - ❌ 실시간 데이터 변경 시 중복/누락 > > **Cursor-based** (무한 스크롤): > ```python > # GET /api/articles?cursor=article123&limit=10 > articles = db.articles.find({ > "_id": {"$lt": ObjectId(cursor)} > }).sort("_id", -1).limit(10) > > # Response > { > "items": [...], > "next_cursor": "article110" > } > ``` > > - ✅ 빠른 성능 (인덱스 활용) > - ✅ 실시간 데이터 일관성 > - ❌ 특정 페이지 이동 불가 > > **선택 기준**: > - 페이지 번호 필요 → Offset > - 무한 스크롤, 대량 데이터 → Cursor --- ## 5. 문제 해결 및 확장성 (2문항) ### Q19. 대규모 트래픽 처리 **질문**: 순간 트래픽 10배 증가 시 대응 방안? > [!success]- 모범 답안 > > **1. 캐싱 (Redis)**: > ```python > @app.get("/api/articles/{article_id}") > async def get_article(article_id: str): > # Cache-aside 패턴 > cached = await redis.get(f"article:{article_id}") > if cached: > return json.loads(cached) > > article = await db.articles.find_one({"_id": article_id}) > await redis.setex(f"article:{article_id}", 3600, json.dumps(article)) > return article > ``` > > **2. Auto-scaling (HPA)**: > ```yaml > apiVersion: autoscaling/v2 > kind: HorizontalPodAutoscaler > metadata: > name: news-api-hpa > spec: > scaleTargetRef: > apiVersion: apps/v1 > kind: Deployment > name: news-api > minReplicas: 2 > maxReplicas: 10 > metrics: > - type: Resource > resource: > name: cpu > target: > type: Utilization > averageUtilization: 70 > ``` > > **3. Rate Limiting**: > ```python > from slowapi import Limiter > > limiter = Limiter(key_func=get_remote_address) > > @app.get("/api/articles") > @limiter.limit("100/minute") > async def list_articles(): > ... > ``` > > **4. Circuit Breaker** (장애 전파 방지): > ```python > from circuitbreaker import circuit > > @circuit(failure_threshold=5, recovery_timeout=60) > async def call_external_service(): > ... > ``` > > **5. CDN**: 정적 리소스 (이미지, CSS, JS) --- ### Q20. 장애 시나리오 대응 **질문**: MongoDB 다운/서비스 무응답/Redis 메모리 가득 시 대응? > [!success]- 모범 답안 > > **1. MongoDB 다운**: > ```python > @app.get("/api/articles") > async def list_articles(): > try: > articles = await db.articles.find().to_list(10) > return articles > except Exception as e: > # Graceful degradation > logger.error(f"DB error: {e}") > > # Fallback: 캐시에서 반환 > cached = await redis.get("articles:fallback") > if cached: > return {"data": json.loads(cached), "source": "cache"} > > # 최후: 기본 메시지 > raise HTTPException(503, "Service temporarily unavailable") > ``` > > **2. 마이크로서비스 무응답**: > ```python > from circuitbreaker import circuit > > @circuit(failure_threshold=3, recovery_timeout=30) > async def call_user_service(user_id): > async with httpx.AsyncClient(timeout=5.0) as client: > response = await client.get(f"http://users-service/users/{user_id}") > return response.json() > > # Circuit Open 시 Fallback > try: > user = await call_user_service(user_id) > except CircuitBreakerError: > # 기본 사용자 정보 반환 > user = {"id": user_id, "name": "Unknown"} > ``` > > **3. Redis 메모리 가득**: > ```conf > # redis.conf > maxmemory 2gb > maxmemory-policy allkeys-lru # LRU eviction > ``` > > ```python > # 중요도 기반 TTL > await redis.setex("hot_article:123", 3600, data) # 1시간 > await redis.setex("old_article:456", 300, data) # 5분 > ``` > > **Health Check 자동 재시작**: > ```yaml > livenessProbe: > httpGet: > path: /health > failureThreshold: 3 > periodSeconds: 10 > ``` --- ## 평가 기준 ### 초급 (Junior) - 5-8개 정답 - 기본 개념 이해 - 공식 문서 참고하여 구현 가능 - 가이드 있으면 개발 가능 ### 중급 (Mid-level) - 9-14개 정답 - 아키텍처 패턴 이해 - 트레이드오프 판단 가능 - 독립적으로 서비스 설계 및 구현 - 기본 DevOps 작업 가능 ### 고급 (Senior) - 15-20개 정답 - 시스템 전체 설계 가능 - 성능/확장성/보안 고려한 의사결정 - 장애 대응 및 모니터링 전략 - 팀 리딩 및 기술 멘토링 --- ## 실무 과제 (선택) ### 과제: Comments 서비스 추가 기사에 댓글 기능을 추가하는 마이크로서비스 구현 **요구사항**: 1. Backend API (FastAPI) - CRUD 엔드포인트 - 대댓글(nested comments) 지원 - 페이지네이션 2. Frontend UI (React + TypeScript) - 댓글 목록/작성/수정/삭제 - Material-UI 사용 3. DevOps - Dockerfile 작성 - Kubernetes 배포 - Console과 연동 **평가 요소**: - 코드 품질 (타입 안정성, 에러 핸들링) - API 설계 (RESTful 원칙) - 성능 고려 (인덱싱, 캐싱) - Git 커밋 메시지 **소요 시간**: 4-6시간 --- ## 면접 진행 Tips 1. **깊이 있는 질문**: "이전 프로젝트에서는 어떻게 해결했나요?" 2. **화이트보드 세션**: 아키텍처 다이어그램 그리기 3. **코드 리뷰**: 기존 코드 개선점 찾기 4. **시나리오 기반**: "만약 ~한 상황이라면?" 5. **후속 질문**: 답변에 따라 심화 질문 --- **작성일**: 2025-10-28 **프로젝트**: Site11 Microservices Platform **대상**: Full-stack Developer