PHP 시니어 프레임워크 기술면접
새 면접Q. Laravel의 Middleware 파이프라인이 내부적으로 어떻게 구현되어 있는지 설명하고, Global Middleware, Route Middleware, Middleware Group의 실행 순서와 우선순위를 설명해주세요. 또한 Terminable Middleware의 동작 원리와 응답 후 로깅이나 분석 작업을 처리할 때 주의해야 할 사항을 실무 관점에서 설명해주세요.
Laravel의 Middleware는 Pipeline 패턴을 사용하여 구현되며, Illuminate\Pipeline\Pipeline 클래스를 통해 각 미들웨어를 순차적으로 통과시킵니다. 실행 순서는 Global Middleware -> Middleware Group -> Route Middleware 순이며, 각 미들웨어는 $next 클로저를 호출하여 다음 미들웨어로 제어를 전달합니다. Terminable Middleware는 terminate() 메서드를 구현하여 응답이 브라우저로 전송된 후에 실행되며, fastcgi_finish_request()를 통해 클라이언트 연결을 먼저 종료한 후 작업을 수행합니다. 주의사항으로는 terminate 메서드에서 예외가 발생해도 클라이언트에게 영향을 주지 않으므로 별도의 예외 처리와 로깅이 필요하며, DB 트랜잭션이 이미 커밋된 상태이므로 데이터 정합성 이슈가 없는 작업만 수행해야 합니다.
- • Pipeline 패턴을 사용한 미들웨어 체인 구현 방식
- • Global -> Group -> Route 순서의 실행 우선순위
- • Terminable Middleware의 fastcgi_finish_request() 활용
- • 응답 후 실행되는 작업의 예외 처리 및 트랜잭션 상태 고려
Middleware에서 발생한 예외를 전역적으로 처리하기 위해 Custom Exception Handler를 구현할 때 고려해야 할 사항과 특정 미들웨어에서만 예외 처리 로직을 다르게 가져가야 할 때의 설계 전략을 설명해주세요.
Q. Symfony의 Dependency Injection Container에서 서비스 컴파일 과정(Service Compilation)의 동작 원리를 설명하고, Compiler Pass의 역할과 실행 시점을 설명해주세요. 또한 대규모 애플리케이션에서 Container 빌드 시간을 최적화하기 위한 전략과 Lazy Service의 Proxy 패턴 구현 방식을 설명해주세요.
Symfony의 DI Container는 설정 파일을 읽어 서비스 정의를 수집한 후 컴파일 단계에서 최적화된 PHP 코드로 변환합니다. Compiler Pass는 컴파일 과정 중 특정 시점에 실행되어 서비스 정의를 동적으로 수정하거나 태그 기반 서비스를 자동으로 등록하는 역할을 하며, PassConfig의 TYPE_BEFORE_OPTIMIZATION, TYPE_OPTIMIZE, TYPE_AFTER_REMOVING 등의 단계로 나뉩니다. Container 빌드 시간 최적화를 위해서는 프로덕션 환경에서 Container Preloading을 활용하고, 불필요한 서비스는 autoconfigure를 통해 자동 등록을 제한하며, 컴파일된 Container를 캐싱합니다. Lazy Service는 ProxyManager 라이브러리를 사용하여 실제 서비스 대신 Proxy 객체를 주입하고, 메서드 호출 시점에 실제 서비스를 인스턴스화하여 초기 부트스트랩 시간을 단축합니다.
- • 서비스 정의 수집 후 최적화된 PHP 코드로 컴파일하는 과정
- • Compiler Pass의 단계별 실행 시점과 서비스 정의 수정 역할
- • Container Preloading과 autoconfigure를 통한 빌드 최적화
- • ProxyManager를 활용한 Lazy Service의 지연 인스턴스화
Symfony에서 서비스를 Singleton이 아닌 Prototype 스코프로 설정해야 하는 상황과 이때 발생할 수 있는 메모리 이슈, 그리고 Request Scope 서비스를 일반 서비스에 주입할 때 발생하는 Scope Widening 문제의 해결 방법을 설명해주세요.
Q. Doctrine ORM의 Unit of Work 패턴의 내부 동작 원리를 설명하고, Entity의 상태(Managed, Detached, Removed, New)가 변경되는 시점과 flush() 메서드 호출 시 실행되는 Change Tracking 알고리즘을 설명해주세요. 또한 대용량 배치 작업에서 메모리 누수를 방지하기 위한 clear()와 detach()의 차이점과 Identity Map의 역할을 실무 관점에서 설명해주세요.
Unit of Work는 트랜잭션 동안 변경된 모든 엔티티를 추적하고 flush() 호출 시 한 번에 데이터베이스에 반영하는 패턴입니다. Entity 상태는 persist() 호출 시 New에서 Managed로, flush() 후에도 Managed 유지, detach() 호출 시 Detached로, remove() 호출 시 Removed로 변경됩니다. Change Tracking은 기본적으로 Deferred Implicit 방식으로 동작하며, flush() 시점에 원본 스냅샷과 현재 엔티티를 비교하여 변경사항을 감지합니다. 대용량 배치 작업에서 clear()는 모든 엔티티를 Detached 상태로 만들고 Identity Map을 초기화하여 메모리를 해제하는 반면, detach()는 특정 엔티티만 분리합니다. Identity Map은 동일한 PK를 가진 엔티티의 중복 인스턴스 생성을 방지하고 객체 동일성을 보장하지만, 배치 작업에서는 주기적으로 clear()를 호출하여 메모리 누수를 방지해야 합니다.
- • Unit of Work의 변경 추적 및 일괄 커밋 메커니즘
- • Entity 상태 전이와 Deferred Implicit Change Tracking
- • clear()와 detach()의 메모리 관리 차이점
- • Identity Map의 객체 동일성 보장과 배치 작업에서의 주기적 초기화 필요성
Doctrine에서 Extra Lazy Collection과 일반 Collection의 차이점을 설명하고, 일대다 관계에서 count() 쿼리 최적화를 위한 전략과 Batch Size를 활용한 N+1 쿼리 완화 방법을 설명해주세요.
Q. Laravel Eloquent의 Global Scope와 Local Scope의 차이점을 설명하고, Global Scope가 성능에 미치는 영향과 Soft Delete 구현에 Global Scope가 사용되는 방식을 설명해주세요. 또한 다중 테넌시 시스템에서 Global Scope를 활용한 데이터 격리 전략과 특정 쿼리에서 Global Scope를 제외해야 할 때의 방법을 설명해주세요.
Global Scope는 모델의 모든 쿼리에 자동으로 적용되는 조건이며, Local Scope는 명시적으로 호출할 때만 적용되는 재사용 가능한 쿼리 조건입니다. Global Scope는 모든 쿼리에 WHERE 절이 추가되므로 인덱스 설계 시 고려해야 하며, Soft Delete는 SoftDeletingScope를 Global Scope로 등록하여 deleted_at IS NULL 조건을 자동 추가합니다. 다중 테넌시 시스템에서는 tenant_id를 기준으로 Global Scope를 등록하여 데이터를 자동으로 격리할 수 있으며, 인증된 사용자의 tenant_id를 스코프에 바인딩합니다. 특정 쿼리에서 Global Scope를 제외하려면 withoutGlobalScope() 또는 withoutGlobalScopes() 메서드를 사용하며, Soft Delete의 경우 withTrashed()나 onlyTrashed()를 사용합니다.
- • Global Scope의 자동 적용과 Local Scope의 명시적 호출 차이
- • SoftDeletingScope를 통한 Soft Delete 구현 메커니즘
- • 다중 테넌시에서 tenant_id 기반 데이터 격리
- • withoutGlobalScope()를 통한 선택적 스코프 제외
Eloquent의 Attribute Casting에서 Custom Cast Class를 구현할 때 Inbound와 Outbound 변환 과정을 설명하고, JSON 컬럼을 객체로 캐스팅할 때 발생할 수 있는 성능 이슈와 해결 방법을 설명해주세요.
Q. PHP 프레임워크에서 Route Caching과 Config Caching이 성능에 미치는 영향을 설명하고, 각 캐싱의 내부 동작 원리와 프로덕션 배포 시 캐시 워밍업(Cache Warming) 전략을 설명해주세요. 또한 View Caching과 OPcache의 차이점, 그리고 Swoole이나 RoadRunner 같은 Application Server를 사용할 때 기존 캐싱 전략에서 변경해야 할 사항을 설명해주세요.
Route Caching은 모든 라우트 정의를 직렬화하여 파일로 저장해 매 요청마다 라우트 파일을 파싱하는 오버헤드를 제거하며, Config Caching은 모든 설정 파일을 하나의 배열로 병합하여 저장합니다. 프로덕션 배포 시에는 php artisan route:cache, config:cache, view:cache를 순서대로 실행하여 캐시를 생성하고, 배포 스크립트에 포함시켜 자동화합니다. View Caching은 Blade 템플릿을 PHP 코드로 컴파일한 결과를 저장하는 반면, OPcache는 PHP 스크립트의 바이트코드를 메모리에 캐싱하여 파싱과 컴파일 단계를 생략합니다. Swoole이나 RoadRunner 환경에서는 애플리케이션이 메모리에 상주하므로 매 요청마다 부트스트랩이 발생하지 않아 Route/Config Caching의 효과가 상대적으로 감소하지만, 메모리 기반 캐싱과 상태 관리에 주의해야 하며 요청 간 상태 공유를 방지하기 위한 리셋 로직이 필요합니다.
- • Route/Config Caching의 파일 직렬화를 통한 부트스트랩 최적화
- • 배포 스크립트에 캐시 워밍업 명령 자동화
- • View Caching과 OPcache의 서로 다른 레벨의 캐싱
- • Application Server 환경에서 메모리 상주로 인한 캐싱 전략 변화와 상태 관리 필요성
PHP 프레임워크에서 Database Query Result Caching을 구현할 때 Cache Tagging을 활용한 무효화 전략과 Remember 패턴의 장단점, 그리고 분산 환경에서 Cache Invalidation의 일관성을 보장하는 방법을 설명해주세요.
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!