Go 미드레벨 CS 기초 기술면접
새 면접Q. Go의 Map 자료구조의 내부 구현 방식을 설명하고, 해시 충돌(Hash Collision) 해결 방법과 Map이 동시성 안전(Concurrency-Safe)하지 않은 이유를 설명해주세요. 또한 동시성 환경에서 Map을 안전하게 사용하는 방법들을 제시해주세요.
Go의 Map은 해시 테이블로 구현되어 있으며, 해시 충돌은 Chaining 방식으로 해결합니다. 각 버킷은 최대 8개의 key-value 쌍을 저장하며, 오버플로우 시 추가 버킷을 연결합니다. Map은 동시성 안전하지 않은데, 이는 성능 최적화를 위해 내부적으로 락을 사용하지 않기 때문입니다. 동시성 환경에서는 sync.Mutex로 보호하거나, sync.Map을 사용하거나, 채널을 통해 직렬화된 접근을 구현할 수 있습니다. sync.Map은 읽기가 많고 쓰기가 적은 경우에 특히 효율적입니다.
- • 해시 테이블 기반 구조와 버킷당 최대 8개 엔트리 저장
- • Chaining 방식의 해시 충돌 해결과 오버플로우 버킷
- • 성능을 위해 락이 없어 동시성 안전하지 않음
- • sync.Mutex, sync.Map, 채널 기반 직렬화 등의 해결 방법
sync.Map과 Mutex로 보호한 일반 Map의 성능 차이는 어떤 상황에서 발생하며, 각각을 선택하는 기준은 무엇인가요?
Q. TCP의 3-way Handshake와 4-way Handshake 과정을 설명하고, TIME_WAIT 상태의 목적과 이로 인해 발생할 수 있는 실무 문제(포트 고갈 등)를 설명해주세요. Go에서 HTTP 클라이언트를 구현할 때 Connection Pooling이 중요한 이유와 설정 방법을 제시해주세요.
3-way Handshake는 SYN, SYN-ACK, ACK 과정으로 연결을 수립하고, 4-way Handshake는 FIN, ACK, FIN, ACK 과정으로 연결을 종료합니다. TIME_WAIT 상태는 지연된 패킷을 처리하고 연결 종료를 보장하기 위해 2MSL 동안 유지되는데, 짧은 시간에 많은 연결을 생성/종료하면 포트 고갈 문제가 발생할 수 있습니다. Go의 http.Client는 기본적으로 Connection Pooling을 지원하며, http.Transport의 MaxIdleConns, MaxIdleConnsPerHost, IdleConnTimeout 등을 설정하여 연결 재사용을 최적화할 수 있습니다. 이를 통해 3-way Handshake 오버헤드를 줄이고 성능을 향상시킬 수 있습니다.
- • 3-way Handshake(SYN, SYN-ACK, ACK)와 4-way Handshake 과정
- • TIME_WAIT 상태의 목적과 2MSL 대기 시간
- • 짧은 시간 내 다수 연결 시 포트 고갈 문제
- • http.Transport의 Connection Pooling 설정을 통한 최적화
Keep-Alive 연결과 Connection Pooling의 관계를 설명하고, 마이크로서비스 환경에서 적절한 Connection Pool 크기를 결정하는 기준은 무엇인가요?
Q. B-Tree와 B+Tree의 구조적 차이를 설명하고, 데이터베이스 인덱스가 주로 B+Tree를 사용하는 이유를 설명해주세요. 또한 Go에서 대용량 데이터를 정렬할 때 메모리 제약이 있는 경우 External Sort를 구현하는 전략과 고려사항을 제시해주세요.
B-Tree는 모든 노드에 데이터를 저장하지만, B+Tree는 리프 노드에만 데이터를 저장하고 내부 노드는 인덱스만 가집니다. B+Tree의 리프 노드는 연결 리스트로 연결되어 있어 범위 검색에 유리하며, 내부 노드가 더 많은 키를 저장할 수 있어 트리 높이가 낮아 디스크 I/O가 적습니다. External Sort는 데이터를 메모리에 맞는 크기로 분할하여 각각 정렬한 후, 멀티웨이 머지를 수행하는 방식입니다. Go에서는 bufio를 활용한 효율적인 파일 I/O, heap을 이용한 k-way merge, 그리고 goroutine을 활용한 병렬 정렬 단계 구현을 고려해야 합니다.
- • B+Tree는 리프 노드에만 데이터 저장, 내부 노드는 인덱스만 보유
- • 리프 노드 연결 리스트로 범위 검색 최적화 및 낮은 트리 높이
- • External Sort의 분할-정렬-병합 전략
- • bufio, heap, goroutine을 활용한 효율적 구현
데이터베이스에서 인덱스 스캔(Index Scan)과 인덱스 온리 스캔(Index Only Scan)의 차이를 설명하고, 커버링 인덱스(Covering Index)의 개념과 장점은 무엇인가요?
Q. 프로세스와 스레드의 차이를 메모리 구조 관점에서 설명하고, 컨텍스트 스위칭(Context Switching) 비용이 프로세스 간과 스레드 간에 차이가 나는 이유를 설명해주세요. Go의 Goroutine이 OS 스레드보다 컨텍스트 스위칭이 빠른 이유를 GMP 모델과 연관지어 설명해주세요.
프로세스는 독립적인 메모리 공간(코드, 데이터, 힙, 스택)을 가지지만, 스레드는 같은 프로세스 내에서 코드, 데이터, 힙을 공유하고 스택만 독립적으로 가집니다. 프로세스 간 컨텍스트 스위칭은 메모리 맵 전환, TLB 플러시 등이 필요해 비용이 크지만, 스레드 간은 같은 메모리 공간을 사용하므로 상대적으로 가볍습니다. Goroutine은 유저 스페이스에서 관리되며 스택 크기가 작고(2KB 시작), GMP 모델에서 M(OS 스레드)이 P(프로세서)의 로컬 큐에서 G(Goroutine)를 스케줄링하므로 시스템 콜 없이 빠른 스위칭이 가능합니다.
- • 프로세스는 독립 메모리, 스레드는 스택만 독립적
- • 프로세스 스위칭은 메모리 맵 전환과 TLB 플러시로 비용이 큼
- • Goroutine은 유저 스페이스 관리와 작은 스택 크기
- • GMP 모델의 로컬 큐 스케줄링으로 시스템 콜 없는 빠른 전환
Go 런타임의 Work Stealing 알고리즘이 무엇이며, P의 로컬 큐가 비었을 때 어떤 과정을 거쳐 다른 P에서 Goroutine을 가져오나요?
Q. 데이터베이스 트랜잭션의 ACID 속성을 각각 설명하고, 트랜잭션 격리 수준(Isolation Level) 4단계를 설명해주세요. 각 격리 수준에서 발생할 수 있는 문제(Dirty Read, Non-Repeatable Read, Phantom Read)와 Go에서 database/sql 패키지를 사용할 때 트랜잭션과 연결 관리를 올바르게 하는 방법을 제시해주세요.
ACID는 Atomicity(원자성), Consistency(일관성), Isolation(격리성), Durability(지속성)을 의미합니다. 격리 수준은 Read Uncommitted(모든 문제 발생), Read Committed(Dirty Read 방지), Repeatable Read(Non-Repeatable Read 방지), Serializable(모든 문제 방지)로 구분됩니다. Dirty Read는 커밋되지 않은 데이터 읽기, Non-Repeatable Read는 같은 쿼리의 결과 불일치, Phantom Read는 범위 쿼리 시 새로운 행 출현입니다. Go에서는 db.BeginTx로 트랜잭션을 시작하고, defer tx.Rollback()과 명시적 tx.Commit()을 사용하며, context를 통해 타임아웃과 취소를 관리해야 합니다.
- • ACID: Atomicity, Consistency, Isolation, Durability
- • 격리 수준 4단계와 각각 방지되는 문제들
- • Dirty Read, Non-Repeatable Read, Phantom Read의 차이
- • BeginTx, defer Rollback, 명시적 Commit, context 활용
MySQL의 Repeatable Read에서 Phantom Read가 발생하지 않는 이유는 무엇이며, MVCC(Multi-Version Concurrency Control)가 어떻게 동작하나요?
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!