PHP 시니어 개발자 기술면접

PHP 시니어 (7년+) 종합 5문항 조회수 134 · 2026-05-28 (목) 11:11:08
1 동시성 제어
Hard

Q. Redis를 활용한 분산 락(Distributed Lock) 구현 시 Redlock 알고리즘의 동작 원리를 설명하고, 단일 Redis 인스턴스 기반 락과의 차이점을 설명해주세요. 또한 PHP 애플리케이션에서 분산 락을 사용할 때 발생할 수 있는 Lock Timeout과 TTL 설정 문제, 그리고 락 해제 시 원자성을 보장하는 방법을 실무 관점에서 설명해주세요.

A. 모범답안

Redlock 알고리즘은 N개의 독립적인 Redis 인스턴스에서 과반수 이상의 락 획득을 요구하여 단일 장애점을 제거합니다. 단일 인스턴스 락은 Redis 장애 시 전체 락 시스템이 무너지지만, Redlock은 과반수 노드가 살아있으면 동작합니다. Lock Timeout은 작업 시간보다 충분히 길게 설정하되, Clock Drift를 고려해 Safety Margin을 두어야 합니다. TTL은 데드락 방지를 위해 필수이며, 작업이 TTL보다 길어질 경우 Lock Extension(갱신) 메커니즘을 구현해야 합니다. 락 해제 시 원자성은 Lua 스크립트를 사용해 소유권 확인과 삭제를 한 번에 처리하여 보장하며, 이때 고유한 Lock Identifier를 사용해 다른 프로세스의 락을 실수로 해제하는 것을 방지합니다.

핵심 포인트
  • • Redlock은 과반수 노드에서 락 획득으로 단일 장애점 제거
  • • TTL 설정 시 Clock Drift와 작업 시간을 고려한 Safety Margin 필요
  • • Lua 스크립트로 락 소유권 확인 및 해제의 원자성 보장
  • • Lock Identifier를 통한 락 소유권 검증으로 잘못된 해제 방지
Follow-up 질문

Redlock 알고리즘의 한계점과 Martin Kleppmann이 제기한 비판, 그리고 이에 대한 대안으로 Fencing Token 방식을 설명해주세요.

2 데이터베이스 최적화
Hard

Q. MySQL에서 커버링 인덱스(Covering Index)의 개념과 동작 원리를 설명하고, 실행 계획에서 'Using index'와 'Using where; Using index'의 차이점을 설명해주세요. 또한 대용량 테이블에서 페이지네이션 쿼리 최적화를 위해 커버링 인덱스를 활용하는 구체적인 방법과 Offset 기반 페이지네이션의 문제점 및 대안을 제시해주세요.

A. 모범답안

커버링 인덱스는 쿼리에 필요한 모든 컬럼이 인덱스에 포함되어 테이블 접근 없이 인덱스만으로 결과를 반환하는 최적화 기법입니다. 'Using index'는 인덱스만으로 모든 데이터를 조회한 것이고, 'Using where; Using index'는 인덱스에서 WHERE 조건 필터링까지 수행한 것입니다. 대용량 페이지네이션에서는 서브쿼리로 커버링 인덱스를 통해 PK만 먼저 조회한 후 메인 쿼리에서 JOIN하여 전체 컬럼을 가져오는 Deferred Join 기법을 사용합니다. Offset 기반 페이지네이션은 뒤 페이지로 갈수록 앞의 모든 행을 스캔해야 하므로 성능이 급격히 저하되며, 대안으로 Cursor 기반(Keyset Pagination) 방식을 사용해 마지막 조회 값을 WHERE 조건으로 활용하면 일정한 성능을 유지할 수 있습니다.

핵심 포인트
  • • 커버링 인덱스는 테이블 접근 없이 인덱스만으로 쿼리 처리
  • • Deferred Join으로 커버링 인덱스 활용한 페이지네이션 최적화
  • • Offset 페이지네이션은 깊은 페이지에서 성능 저하 발생
  • • Cursor 기반 페이지네이션으로 일정한 성능 유지 가능
Follow-up 질문

인덱스 컬럼 순서가 쿼리 성능에 미치는 영향과 카디널리티, 선택도를 고려한 복합 인덱스 설계 원칙을 설명해주세요.

3 아키텍처 패턴
Hard

Q. CQRS(Command Query Responsibility Segregation) 패턴의 핵심 개념과 Event Sourcing과의 관계를 설명하고, PHP 애플리케이션에서 CQRS를 도입할 때의 장단점을 설명해주세요. 또한 Read Model과 Write Model 간의 데이터 동기화 전략과 Eventual Consistency 문제를 어떻게 처리할 수 있는지 실무 관점에서 설명해주세요.

A. 모범답안

CQRS는 Command(쓰기)와 Query(읽기) 모델을 분리하여 각각 최적화하는 패턴으로, 복잡한 도메인 로직과 조회 성능을 독립적으로 개선할 수 있습니다. Event Sourcing은 상태 변경을 이벤트로 저장하는 패턴으로 CQRS와 함께 사용되며, Write Model의 이벤트를 기반으로 Read Model을 구성합니다. 장점으로는 읽기/쓰기 독립적 확장, 복잡한 조회 쿼리 최적화, 도메인 로직 단순화가 있으며, 단점으로는 시스템 복잡도 증가, 데이터 동기화 부담, 개발/운영 비용 증가가 있습니다. Read/Write Model 동기화는 Message Queue(RabbitMQ, Kafka)를 통한 비동기 이벤트 전파로 처리하며, Eventual Consistency는 UI에서 낙관적 업데이트 표시, Polling/WebSocket을 통한 상태 확인, 버전 정보를 포함한 응답으로 처리합니다.

핵심 포인트
  • • CQRS는 Command와 Query 모델 분리로 각각 최적화
  • • Event Sourcing과 결합하여 이벤트 기반 Read Model 구성
  • • Message Queue 통한 비동기 동기화로 모델 간 데이터 전파
  • • Eventual Consistency는 UI 피드백과 버전 관리로 처리
Follow-up 질문

CQRS를 도입하기에 적합한 도메인의 특성과 과도한 적용을 피해야 하는 경우를 구분하여 설명해주세요.

4 메모리 관리
Hard

Q. PHP-FPM의 프로세스 관리 방식(static, dynamic, ondemand)의 차이점과 각각의 장단점을 설명하고, pm.max_children, pm.start_servers, pm.max_requests 등 주요 설정값이 메모리 사용량과 성능에 미치는 영향을 설명해주세요. 또한 대규모 트래픽 환경에서 메모리 누수를 모니터링하고 대응하는 전략을 실무 관점에서 설명해주세요.

A. 모범답안

static 모드는 고정된 수의 워커를 항상 유지하여 예측 가능한 메모리 사용과 빠른 응답을 제공하지만 유휴 시에도 메모리를 점유합니다. dynamic 모드는 부하에 따라 워커를 증감하여 리소스를 효율적으로 사용하지만 프로세스 생성 오버헤드가 있으며, ondemand는 요청이 있을 때만 생성하여 최소 메모리를 사용하지만 초기 응답 지연이 발생합니다. pm.max_children은 최대 동시 처리 수를 결정하며 서버 메모리를 고려해 설정해야 하고, pm.max_requests는 워커 재시작 주기를 정하여 메모리 누수를 완화합니다. 대규모 환경에서는 APM 도구로 프로세스별 메모리 사용량을 모니터링하고, pm.max_requests를 적절히 설정해 주기적 재시작을 유도하며, Slow Log와 메모리 프로파일링으로 누수 원인을 파악하고, 필요 시 객체 풀링이나 메모리 해제 로직을 추가합니다.

핵심 포인트
  • • static은 예측 가능, dynamic은 효율적, ondemand는 최소 메모리 사용
  • • pm.max_children은 서버 메모리 고려한 동시 처리 수 결정
  • • pm.max_requests로 주기적 워커 재시작하여 메모리 누수 완화
  • • APM 모니터링과 프로파일링으로 메모리 누수 원인 파악 및 대응
Follow-up 질문

PHP의 메모리 할당 방식과 Zend Memory Manager의 역할, 그리고 memory_limit 설정이 실제 시스템 메모리 사용에 미치는 영향을 설명해주세요.

5 성능 최적화
Hard

Q. N+1 쿼리 문제의 발생 원인과 성능에 미치는 영향을 설명하고, PHP ORM(Eloquent, Doctrine)에서 이를 해결하기 위한 Eager Loading, Lazy Loading, Batch Loading의 차이점과 각각의 적용 시나리오를 설명해주세요. 또한 복잡한 관계형 데이터를 조회할 때 DataLoader 패턴을 활용하는 방법과 캐싱 전략을 포함한 종합적인 최적화 방안을 제시해주세요.

A. 모범답안

N+1 쿼리는 메인 쿼리 1회와 각 결과당 추가 쿼리 N회가 발생하여 총 N+1회 쿼리가 실행되는 문제로, 데이터베이스 부하와 응답 시간을 급격히 증가시킵니다. Eager Loading은 JOIN이나 서브쿼리로 사전에 관련 데이터를 모두 가져와 쿼리 수를 최소화하며, Lazy Loading은 실제 접근 시 로드하여 불필요한 데이터 로드를 방지하지만 N+1 문제를 유발할 수 있고, Batch Loading은 필요한 ID를 모아 IN 절로 한 번에 조회하여 중간 방식을 제공합니다. DataLoader 패턴은 단일 요청 내에서 동일 리소스 조회를 배치 처리하고 결과를 캐싱하여 중복 쿼리를 제거합니다. 종합적으로는 Eager Loading으로 기본 관계 로드, 조건부 관계는 Lazy Loading, 루프 내 조회는 Batch Loading을 사용하고, Redis 등으로 자주 조회되는 관계 데이터를 캐싱하며, Query Result Cache와 Identity Map을 활용해 동일 요청 내 중복 쿼리를 방지합니다.

핵심 포인트
  • • N+1 문제는 메인 쿼리 후 각 결과당 추가 쿼리 발생으로 성능 저하
  • • Eager Loading은 사전 로드, Lazy는 지연 로드, Batch는 일괄 조회
  • • DataLoader 패턴으로 요청 내 배치 처리 및 캐싱으로 중복 제거
  • • 관계 데이터 캐싱과 Identity Map으로 종합적 쿼리 최적화
Follow-up 질문

GraphQL API에서 발생하는 N+1 문제의 특수성과 DataLoader 라이브러리를 활용한 해결 방법, 그리고 PHP에서의 구현 전략을 설명해주세요.

댓글 0

로그인 후 댓글을 작성할 수 있습니다.

아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!