프로그래밍수석 Perl 개발자, 백엔드/풀스택

Perl에서 클로저(closures)와 익명 서브프로그래밍에 대한 작업의 특징을 설명하세요. 이들을 올바르게 사용하는 방법, 미세한 부분이 발생하는 위치 및 메모리 누수를 방지하는 방법은 무엇인가요?

Hintsage AI 어시스턴트로 면접 통과

답변

Perl에서는 익명 서브프로그램과 클로저가 지원됩니다. 익명 서브프로그램은 이름 없이 sub를 통해 선언되며 코드에 대한 참조를 반환합니다. 클로저는 생성 시 존재했던 변수를 포함한 렉시컬 범위를 "포착하는" 서브프로그램입니다.

익명 서브프로그램 및 클로저의 예:

sub make_incrementer { my $inc = shift; return sub { $_[0] + $inc }; } my $inc10 = make_incrementer(10); print $inc10->(5); # 15 출력

클로저는 함수 팩토리, 제너레이터 및 콜백(예: map/grep, 이벤트 핸들러) 생성을 위해 활발히 사용됩니다.

중요한 점: 클로저가 변수를 참조하고, 그 변수가 다시 클로저를 가리키는 경우(직접적으로나 구조를 통해) 순환 참조가 발생하여 메모리 누수가 발생할 수 있습니다.

낚시 질문

클로저에 캡슐화된 변수는 언제 해제되나요? my 및 our의 행동에 차이가 있나요?

답변: 클로저 내부에 캡슐화된 my 변수는 클로저에 대한 링크가 하나라도 존재하는 한 살아 있습니다. 함수가 끝나도 해제되지 않으며, 그 생명 주기는 클로저의 생명 주기와 동일합니다. our 변수는 그와 같은 동작이 없으며, 모든 클로저에서 접근 가능하고 프로그램 종료 시 해제됩니다. 클로저를 삭제하는 것을 잊게 되면 메모리 누수를 초래할 수 있습니다.

문제 예제:

my $cref; { my $val = 5; $cref = sub { $val++ }; } # $val은 여전히 $cref가 살아 있는 한 존재합니다.

주제의 미세한 사항을 모르고 발생한 실제 오류 사례


이야기

서버 애플리케이션에서 각 사용자에게 콜백용 클로저 컨텍스트가 생성되었습니다. 그러나 클로저가 사용자 구조체를 참조하여 순환 참조를 일으켰습니다. 이로 인해 garbage collector가 로그아웃 후에도 사용자 객체를 정리하지 않아서 메모리 누수가 기하급수적으로 증가했습니다.


이야기

이벤트의 백그라운드 처리를 위한 데몬 애플리케이션에서 카운터로 클로저를 사용했지만 참조된 배열을 리셋하는 것을 잊었습니다. 결과적으로 몇 개의 잊은 클로저로 인해 오래된 메시지 데이터가 쌓여서 데몬이 고장나기 전까지 수동으로 힙을 정리해야 했습니다.


이야기

개발자가 클로저에서 our 변수를 사용하려고 했지만, "클로저된" 동작을 기대했으며 모든 클로저가 동일한 변수를 공유하게 되어 병렬 실행 시 데이터 경쟁이 발생했습니다. 이 버그는 다양한 매개변수로 동시에 작업하는 사용자에게 나타났습니다(잘못된 계산).