Go 미드레벨 프레임워크 기술면접
새 면접Q. Go의 net/http 패키지로 HTTP 서버를 구현할 때, 각 요청이 별도의 Goroutine에서 처리되는 구조가 가져오는 장점과 단점을 설명하고, 동시 연결 수를 제한하거나 제어해야 하는 상황과 그 구현 방법을 제시해주세요.
net/http 서버는 각 요청마다 새로운 Goroutine을 생성하여 처리하므로 동시에 수천 개의 연결을 효율적으로 처리할 수 있고, 한 요청의 지연이 다른 요청에 영향을 주지 않습니다. 하지만 무제한 Goroutine 생성은 메모리 고갈과 시스템 자원 부족을 초래할 수 있습니다. Server.MaxConcurrentStreams나 http.Server의 ConnState 훅을 활용하거나, 세마포어 패턴(buffered channel)을 미들웨어로 구현하여 동시 처리 수를 제한할 수 있습니다. 또한 http.Server의 ReadTimeout, WriteTimeout, IdleTimeout 설정으로 리소스 누수를 방지해야 합니다.
- • 각 요청마다 별도 Goroutine 생성으로 높은 동시성 처리 가능
- • 무제한 Goroutine 생성 시 메모리 고갈 위험
- • 세마포어 패턴이나 미들웨어로 동시 연결 수 제한 가능
- • 타임아웃 설정으로 리소스 누수 방지 필수
http.Server의 Shutdown과 Close 메서드의 차이점과 Graceful Shutdown을 구현할 때 주의해야 할 점은 무엇인가요?
Q. Go 프로젝트에서 internal 디렉토리의 특별한 의미와 동작 방식을 설명하고, 이를 활용한 패키지 가시성 제어 전략과 실무에서 internal 패키지를 설계할 때 고려해야 할 사항들을 제시해주세요.
internal 디렉토리는 Go 컴파일러가 특별하게 취급하는 디렉토리로, internal의 부모 디렉토리와 그 하위에서만 import가 가능하며 외부 패키지에서는 접근할 수 없습니다. 이를 통해 공개 API와 내부 구현을 명확히 분리하고 의도하지 않은 외부 의존성을 방지할 수 있습니다. 실무에서는 공통 유틸리티, 비즈니스 로직, 데이터 모델 등을 internal에 배치하되, 너무 깊은 계층 구조는 피하고 명확한 책임 분리를 유지해야 합니다. 또한 써드파티 라이브러리로 공개할 코드는 internal에 두지 않도록 주의해야 합니다.
- • internal 디렉토리는 부모 디렉토리 범위 내에서만 import 가능
- • 공개 API와 내부 구현의 명확한 분리 가능
- • 의도하지 않은 외부 의존성 방지
- • 공통 로직과 비즈니스 로직을 internal에 배치하되 계층 구조는 단순하게 유지
pkg 디렉토리와 internal 디렉토리를 함께 사용하는 프로젝트 구조의 장점과, 각 디렉토리에 어떤 코드를 배치해야 하는지 설명해주세요.
Q. Go의 sync.Once, sync.WaitGroup, sync.Cond의 각각의 용도와 내부 동작 원리를 설명하고, 실무에서 각각을 사용하기 적합한 시나리오와 잘못 사용했을 때 발생할 수 있는 문제점들을 제시해주세요.
sync.Once는 초기화 함수를 정확히 한 번만 실행하도록 보장하며 내부적으로 atomic 연산과 mutex를 사용합니다. sync.WaitGroup은 여러 Goroutine의 완료를 대기할 때 사용하며 카운터 기반으로 동작합니다. sync.Cond는 조건 변수로 특정 조건이 만족될 때까지 Goroutine을 대기시키고 Signal/Broadcast로 깨웁니다. Once는 싱글톤 초기화나 설정 로딩에, WaitGroup은 병렬 작업 완료 대기에, Cond는 생산자-소비자 패턴에서 조건 기반 동기화에 적합합니다. WaitGroup을 복사하거나 Done 호출 수가 Add와 불일치하면 패닉이 발생하고, Cond는 Wait 전에 반드시 Lock을 획득해야 하며 spurious wakeup에 대비해 루프로 조건을 확인해야 합니다.
- • sync.Once는 atomic과 mutex로 정확히 한 번 실행 보장
- • sync.WaitGroup은 카운터 기반 병렬 작업 완료 대기
- • sync.Cond는 조건 변수로 조건 기반 동기화 제공
- • 각각의 잘못된 사용(복사, 카운터 불일치, Lock 누락)은 패닉이나 데드락 유발
sync.Pool의 동작 원리와 사용 시 주의사항, 그리고 어떤 상황에서 성능 개선 효과를 얻을 수 있는지 설명해주세요.
Q. Go에서 타입 임베딩(Type Embedding)의 개념과 동작 방식을 설명하고, 상속과의 차이점 및 메서드 오버라이딩이 발생할 때의 동작을 실무 관점에서 설명해주세요.
타입 임베딩은 구조체나 인터페이스 안에 다른 타입을 이름 없이 포함시키는 것으로, 임베디드 타입의 필드와 메서드가 외부 타입으로 승격됩니다. 이는 상속이 아닌 컴포지션이며, is-a 관계가 아닌 has-a 관계입니다. 외부 타입에서 같은 이름의 메서드를 정의하면 외부 타입의 메서드가 우선하며, 임베디드 타입의 메서드는 명시적으로 타입 이름을 지정해야 호출할 수 있습니다. 실무에서는 공통 기능을 재사용하거나 인터페이스 구현을 위임할 때 활용하며, 너무 깊은 임베딩 체인은 가독성을 해치므로 주의해야 합니다.
- • 타입 임베딩은 필드와 메서드를 외부 타입으로 승격시키는 컴포지션
- • 상속이 아닌 has-a 관계이며 다형성과는 다름
- • 같은 이름의 메서드는 외부 타입이 우선하며 섀도잉 발생
- • 공통 기능 재사용과 인터페이스 위임에 유용하나 과도한 사용은 지양
인터페이스 임베딩과 구조체 임베딩의 차이점, 그리고 인터페이스 임베딩을 활용한 인터페이스 분리 원칙(ISP) 적용 사례를 설명해주세요.
Q. Go에서 Escape Analysis가 무엇인지 설명하고, 변수가 힙에 할당되는 경우와 스택에 할당되는 경우를 구분하는 기준을 제시하며, 실무에서 불필요한 힙 할당을 줄이기 위한 코딩 패턴과 분석 방법을 설명해주세요.
Escape Analysis는 Go 컴파일러가 변수의 생명주기를 분석하여 스택 또는 힙 할당을 결정하는 최적화 기법입니다. 변수가 함수 범위를 벗어나 외부로 반환되거나, 인터페이스로 변환되거나, 크기가 너무 크거나, 클로저에 캡처되면 힙에 할당됩니다. 스택 할당은 GC 부담이 없고 빠르므로, 포인터 반환 대신 값 반환을 고려하고, 큰 구조체는 필드별로 분리하며, 인터페이스 사용을 최소화하는 것이 좋습니다. go build -gcflags='-m' 명령으로 escape analysis 결과를 확인하고, 벤치마크로 힙 할당 횟수를 측정하여 최적화 효과를 검증할 수 있습니다.
- • Escape Analysis는 변수의 생명주기 분석으로 스택/힙 할당 결정
- • 함수 범위 벗어남, 인터페이스 변환, 큰 크기, 클로저 캡처 시 힙 할당
- • 포인터 반환 최소화, 값 반환, 구조체 분리로 스택 할당 유도
- • go build -gcflags='-m'과 벤치마크로 할당 분석 및 최적화 검증
sync.Pool을 사용하여 힙 할당을 줄이는 경우와 그냥 스택 할당을 유도하는 것 중 어떤 접근이 더 효과적인지, 그리고 각각의 트레이드오프를 설명해주세요.
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!