Python 시니어 프레임워크 심화 면접
새 면접Q. Python의 __init_subclass__ 훅 메서드의 동작 원리와 활용 사례를 설명하고, Metaclass를 사용하는 방식과 비교했을 때의 장단점을 설명해주세요. 또한 플러그인 시스템이나 자동 등록 패턴을 구현할 때 __init_subclass__를 어떻게 활용할 수 있는지 실무 예시와 함께 설명해주세요.
__init_subclass__는 Python 3.6+에서 도입된 훅으로, 클래스가 상속될 때 자동으로 호출되어 서브클래스를 커스터마이징할 수 있습니다. Metaclass보다 간단하고 직관적이며, 단일 상속 계층에서 서브클래스 등록이나 검증에 적합합니다. 플러그인 시스템에서는 베이스 클래스에 __init_subclass__를 정의하여 모든 플러그인을 자동으로 레지스트리에 등록할 수 있으며, Metaclass 대비 다중상속 시 충돌 위험이 적고 코드 가독성이 높습니다. 다만 Metaclass는 인스턴스 생성 과정 전체를 제어할 수 있어 더 강력하지만, __init_subclass__는 상속 시점에만 개입 가능하다는 제약이 있습니다.
- • __init_subclass__는 클래스 상속 시 자동 호출되는 훅으로 서브클래스 커스터마이징 가능
- • Metaclass 대비 간단하고 직관적이며 다중상속 시 충돌 위험 감소
- • 플러그인 자동 등록, 서브클래스 검증, 설정 상속 등의 패턴 구현에 유용
- • Metaclass는 인스턴스 생성 전체 제어 가능하나 __init_subclass__는 상속 시점만 개입
대규모 프로젝트에서 여러 팀이 플러그인을 개발할 때, __init_subclass__를 사용한 자동 등록 시스템의 스레드 안전성과 초기화 순서를 어떻게 보장하시겠습니까?
Q. Python의 dataclasses, attrs, pydantic의 차이점과 각각의 사용 시나리오를 비교 설명해주세요. 특히 성능, 유효성 검증, 직렬화/역직렬화 측면에서 어떤 차이가 있으며, API 서버 개발 시 어떤 라이브러리를 선택하는 것이 적절한지 근거와 함께 설명해주세요.
dataclasses는 Python 표준 라이브러리로 보일러플레이트 코드 감소에 초점을 맞추며 타입 힌트 기반이지만 런타임 검증은 없습니다. attrs는 dataclasses보다 먼저 등장했으며 더 많은 기능(validators, converters)을 제공하고 성능도 우수합니다. pydantic은 런타임 데이터 검증과 파싱에 특화되어 있으며, FastAPI 등 API 개발에서 요청/응답 검증에 최적화되어 있습니다. API 서버에서는 외부 입력 검증이 필요하므로 pydantic이 적합하며, 내부 데이터 구조만 필요하다면 표준 라이브러리인 dataclasses나 성능이 중요하면 attrs를 선택하는 것이 좋습니다.
- • dataclasses는 표준 라이브러리로 간단하지만 런타임 검증 미지원
- • attrs는 풍부한 기능과 우수한 성능 제공, validators와 converters 지원
- • pydantic은 런타임 검증과 파싱에 특화, API 요청/응답 검증에 최적
- • 사용 시나리오에 따라 선택: 외부 입력 검증은 pydantic, 내부 구조는 dataclasses/attrs
pydantic v2에서 Rust 기반 코어로 성능이 대폭 향상되었는데, 기존 v1에서 마이그레이션할 때 주의해야 할 breaking changes와 성능 최적화 포인트는 무엇인가요?
Q. Python의 asyncio와 함께 사용되는 aiohttp, httpx 같은 비동기 HTTP 클라이언트에서 connection pooling과 keep-alive의 동작 원리를 설명하고, 대량의 외부 API 호출 시 최적의 성능을 내기 위한 설정 전략(pool size, timeout, retry)을 설명해주세요. 또한 rate limiting을 구현할 때 semaphore와 token bucket 알고리즘의 차이점도 함께 설명해주세요.
비동기 HTTP 클라이언트의 connection pooling은 TCP 연결을 재사용하여 handshake 오버헤드를 줄이며, keep-alive는 연결을 일정 시간 유지합니다. pool size는 동시 연결 수를 제한하며, 너무 크면 메모리 낭비, 너무 작으면 대기 시간 증가가 발생하므로 대상 서버의 동시 연결 제한과 네트워크 대역폭을 고려해 설정해야 합니다. timeout은 connect, read, total로 구분하여 설정하고, retry는 멱등성 있는 요청에만 적용하며 exponential backoff를 사용합니다. semaphore는 단순히 동시 실행 수를 제한하지만, token bucket은 버스트 트래픽을 허용하면서 평균 처리율을 제어할 수 있어 API rate limit 준수에 더 적합합니다.
- • connection pooling은 TCP 연결 재사용으로 성능 향상, pool size는 서버 제한과 리소스 고려
- • timeout은 connect/read/total로 구분 설정, retry는 멱등성 요청에만 exponential backoff 적용
- • semaphore는 동시 실행 수 제한, token bucket은 버스트 허용하며 평균 처리율 제어
- • 대량 API 호출 시 connection pool 재사용, 적절한 timeout/retry 설정으로 안정성 확보
외부 API가 간헐적으로 응답 지연을 보일 때, circuit breaker 패턴을 asyncio 환경에서 구현한다면 어떤 메트릭을 기준으로 상태 전환을 판단하시겠습니까?
Q. SQLAlchemy의 lazy loading, eager loading(joinedload, selectinload, subqueryload)의 동작 원리와 차이점을 설명하고, 각각이 생성하는 SQL 쿼리 패턴을 비교해주세요. 복잡한 관계를 가진 모델에서 어떤 로딩 전략을 선택해야 하는지, 그리고 relationship의 lazy 옵션들(select, joined, subquery, selectin, raise, dynamic)의 실무 활용 시나리오를 설명해주세요.
lazy loading은 관계 속성 접근 시 개별 쿼리를 실행하여 N+1 문제를 유발하고, joinedload는 LEFT OUTER JOIN으로 한 번에 로드하지만 일대다 관계에서 중복 행이 발생할 수 있습니다. selectinload는 IN 절을 사용한 별도 쿼리로 효율적이며, subqueryload는 서브쿼리를 사용해 관계 데이터를 가져옵니다. 일대다 관계가 많으면 selectinload가 적합하고, 다대일은 joinedload가 효율적입니다. lazy='dynamic'은 쿼리 객체를 반환하여 추가 필터링이 가능하고, lazy='raise'는 명시적 로딩을 강제하여 N+1을 방지합니다. 복잡한 관계에서는 query-specific loading을 사용해 상황별로 최적 전략을 적용하는 것이 좋습니다.
- • lazy loading은 N+1 문제 유발, joinedload는 JOIN 사용하나 중복 행 가능
- • selectinload는 IN 절로 효율적, 일대다 관계에 적합
- • lazy='dynamic'은 추가 필터링 가능, lazy='raise'는 N+1 방지
- • 관계 복잡도와 데이터 크기에 따라 query-specific loading으로 최적화
SQLAlchemy 2.0의 새로운 쿼리 스타일과 async 지원에서 relationship loading 전략은 어떻게 달라졌으며, 기존 1.x 코드를 마이그레이션할 때 주의할 점은 무엇인가요?
Q. pytest의 fixture 시스템에서 scope(function, class, module, session)의 동작 원리와 생명주기를 설명하고, autouse fixture와 conftest.py의 활용 전략을 설명해주세요. 또한 대규모 테스트 suite에서 fixture 간 의존성 관리와 setup/teardown 순서를 제어하는 방법, 그리고 비동기 fixture를 사용할 때의 주의사항을 설명해주세요.
pytest fixture의 scope는 fixture 인스턴스의 재사용 범위를 결정하며, function은 매 테스트마다, session은 전체 테스트 실행 동안 한 번만 생성됩니다. 데이터베이스 연결 같은 비용이 큰 리소스는 session이나 module scope를 사용하고, 테스트 간 격리가 필요하면 function을 사용합니다. autouse fixture는 명시적 요청 없이 자동 실행되며 로깅이나 환경 설정에 유용하고, conftest.py는 디렉토리 계층 구조로 fixture를 공유할 수 있습니다. fixture 간 의존성은 매개변수로 선언하며 pytest가 자동으로 의존성 그래프를 해결하고, 비동기 fixture는 pytest-asyncio 플러그인으로 지원되며 event loop scope와 일치시켜야 합니다.
- • scope는 fixture 재사용 범위 결정, 비용 큰 리소스는 session/module scope 사용
- • autouse는 자동 실행, conftest.py는 계층적 fixture 공유 제공
- • fixture 의존성은 매개변수로 선언, pytest가 자동 해결
- • 비동기 fixture는 pytest-asyncio 사용, event loop scope 일치 필요
마이크로서비스 환경에서 통합 테스트 시 여러 외부 서비스를 모킹해야 할 때, fixture를 사용한 test double 전략과 testcontainers를 활용한 실제 의존성 테스트 중 어느 것을 선호하시나요?
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!