Python 미드레벨 기술면접

Python 미드레벨 (3~7년) 코딩 · 알고리즘 5문항 조회수 117 · 2026-05-23 (토) 03:10:54
1 메모리 관리
Medium

Q. Python의 가비지 컬렉션(Garbage Collection)에서 사용하는 참조 카운팅(Reference Counting)과 순환 참조(Circular Reference) 문제를 설명하고, Python이 이를 해결하기 위해 사용하는 세대별 가비지 컬렉션(Generational GC) 메커니즘을 설명해주세요. 실무에서 메모리 누수가 의심될 때 어떻게 디버깅하는지도 함께 설명해주세요.

A. 모범답안

Python은 기본적으로 참조 카운팅을 사용하여 객체의 참조 수가 0이 되면 즉시 메모리를 해제합니다. 하지만 두 객체가 서로를 참조하는 순환 참조 상황에서는 참조 카운트가 0이 되지 않아 메모리 누수가 발생합니다. 이를 해결하기 위해 Python은 세대별 GC를 사용하며, 객체를 0세대, 1세대, 2세대로 분류하여 오래 살아남은 객체일수록 덜 자주 검사합니다. gc 모듈의 collect() 함수로 강제 수집이 가능하고, gc.get_objects()나 tracemalloc 모듈, objgraph 라이브러리를 사용하여 메모리 누수를 디버깅할 수 있습니다. 실무에서는 __del__ 메서드 사용을 지양하고 컨텍스트 매니저를 활용하는 것이 좋습니다.

핵심 포인트
  • • 참조 카운팅의 한계인 순환 참조 문제 이해
  • • 세대별 GC의 동작 원리 (0, 1, 2세대)
  • • gc 모듈과 tracemalloc을 활용한 메모리 디버깅 방법
Follow-up 질문

weakref 모듈을 사용하면 순환 참조 문제를 어떻게 예방할 수 있는지 설명해주세요.

2 동시성 프로그래밍
Hard

Q. Python의 asyncio에서 코루틴(Coroutine), 태스크(Task), 퓨처(Future)의 차이점을 설명하고, await, asyncio.create_task(), asyncio.gather()를 각각 언제 사용해야 하는지 설명해주세요. 또한 asyncio를 사용할 때 블로킹 I/O 작업을 만나면 어떻게 처리해야 하는지 실무 관점에서 설명해주세요.

A. 모범답안

코루틴은 async def로 정의된 비동기 함수 자체이고, 태스크는 코루틴을 이벤트 루프에 스케줄링한 것이며, 퓨처는 아직 완료되지 않은 작업의 결과를 나타내는 저수준 객체입니다. await는 단일 코루틴을 순차적으로 기다릴 때, create_task()는 백그라운드에서 병렬 실행할 때, gather()는 여러 코루틴을 동시에 실행하고 모든 결과를 기다릴 때 사용합니다. 블로킹 I/O 작업은 asyncio.to_thread()나 loop.run_in_executor()를 사용하여 별도 스레드에서 실행해야 이벤트 루프를 막지 않습니다. 실무에서는 requests 대신 aiohttp, psycopg2 대신 asyncpg처럼 비동기 라이브러리를 사용하는 것이 중요합니다.

핵심 포인트
  • • 코루틴, 태스크, 퓨처의 개념적 차이와 관계
  • • await, create_task, gather의 사용 시나리오 구분
  • • 블로킹 I/O를 run_in_executor로 처리하는 방법
Follow-up 질문

asyncio.gather()와 asyncio.wait()의 차이점과 각각의 사용 사례를 설명해주세요.

3 디자인 패턴
Medium

Q. Python에서 싱글톤 패턴(Singleton Pattern)을 구현하는 여러 가지 방법(모듈, __new__, 메타클래스, 데코레이터)을 설명하고, 각 방법의 장단점을 비교해주세요. 또한 멀티스레드 환경에서 싱글톤의 thread-safety를 보장하기 위한 방법과 실무에서 싱글톤 패턴을 사용할 때 주의할 점을 설명해주세요.

A. 모범답안

Python에서 싱글톤은 모듈 자체를 사용하는 것이 가장 pythonic하며, 모듈은 import 시 자동으로 싱글톤이 됩니다. __new__ 메서드를 오버라이드하면 인스턴스 생성을 제어할 수 있고, 메타클래스를 사용하면 클래스 생성 자체를 제어할 수 있으며, 데코레이터는 간단하지만 상속이 어렵습니다. 멀티스레드 환경에서는 threading.Lock을 사용한 더블 체크 락킹 패턴이나 __new__ 내부에서 락을 사용해야 합니다. 실무에서는 싱글톤이 전역 상태를 만들어 테스트를 어렵게 하므로, 의존성 주입을 사용하거나 정말 필요한 경우(로거, 설정 관리)에만 제한적으로 사용해야 합니다.

핵심 포인트
  • • 모듈, __new__, 메타클래스, 데코레이터 방식의 구현과 차이
  • • threading.Lock을 사용한 thread-safety 보장
  • • 싱글톤의 단점과 의존성 주입 대안
Follow-up 질문

Python의 Borg 패턴이 싱글톤과 어떻게 다르고, 어떤 상황에서 더 유용한지 설명해주세요.

4 성능 최적화
Hard

Q. Python에서 대용량 데이터를 처리할 때 제너레이터(Generator)와 이터레이터(Iterator)를 사용하는 이유를 메모리 효율성 관점에서 설명하고, yield와 yield from의 차이점을 설명해주세요. 또한 실무에서 제너레이터 표현식과 리스트 컴프리헨션 중 어떤 것을 선택해야 하는지 기준을 제시하고, 제너레이터를 사용할 때 주의해야 할 점을 설명해주세요.

A. 모범답안

제너레이터는 값을 한 번에 하나씩 생성하여 메모리에 전체 데이터를 올리지 않으므로 대용량 데이터 처리에 효율적입니다. yield는 단일 값을 생성하지만 yield from은 다른 이터러블을 위임하여 서브제너레이터의 값을 직접 전달합니다. 데이터를 한 번만 순회하거나 파이프라인 처리할 때는 제너레이터 표현식을, 여러 번 접근하거나 인덱싱이 필요하면 리스트 컴프리헨션을 사용합니다. 제너레이터는 한 번 소진되면 재사용할 수 없고, 예외 발생 시 상태 복구가 어려우며, 디버깅이 어렵다는 점을 주의해야 합니다. itertools 모듈과 함께 사용하면 더욱 강력한 데이터 처리 파이프라인을 구성할 수 있습니다.

핵심 포인트
  • • 제너레이터의 지연 평가와 메모리 효율성
  • • yield와 yield from의 동작 차이
  • • 제너레이터의 일회성과 재사용 불가 특성
Follow-up 질문

제너레이터에 send() 메서드를 사용하여 양방향 통신을 구현하는 코루틴 패턴에 대해 설명해주세요.

5 테스팅
Medium

Q. Python에서 unittest.mock의 Mock, MagicMock, patch의 차이점을 설명하고, 외부 API 호출이나 데이터베이스 접근을 테스트할 때 목(Mock) 객체를 어떻게 활용하는지 설명해주세요. 또한 patch를 데코레이터와 컨텍스트 매니저로 사용할 때의 차이점과 실무에서 목 객체 사용 시 주의할 점을 설명해주세요.

A. 모범답안

Mock은 기본 목 객체로 호출을 추적하고 반환값을 설정할 수 있으며, MagicMock은 매직 메서드(__len__, __iter__ 등)를 자동으로 지원하는 Mock의 서브클래스입니다. patch는 실제 객체를 목 객체로 대체하는 기능으로, 데코레이터는 테스트 함수 전체에, 컨텍스트 매니저는 특정 블록에만 적용됩니다. 외부 API는 requests.get을 패치하여 return_value로 응답을 설정하고, 데이터베이스는 쿼리 메서드를 패치하여 side_effect로 다양한 시나리오를 테스트합니다. 실무에서는 과도한 목 사용으로 실제 동작과 괴리되지 않도록 주의하고, assert_called_once_with로 정확한 인자 전달을 검증하며, 통합 테스트와 균형을 맞춰야 합니다.

핵심 포인트
  • • Mock과 MagicMock의 차이와 매직 메서드 지원
  • • patch의 데코레이터와 컨텍스트 매니저 사용법
  • • 목 객체의 return_value, side_effect 활용과 과도한 목 사용 지양
Follow-up 질문

pytest의 fixture와 monkeypatch를 사용한 테스트 방식이 unittest의 mock과 어떻게 다른지 설명해주세요.

댓글 0

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

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