Go 주니어 코딩 기술면접
새 면접Q. 주어진 문자열이 회문(palindrome)인지 확인하는 함수를 구현한다고 가정합니다. 대소문자를 구분하지 않고, 공백과 특수문자는 무시해야 할 때 어떤 방식으로 접근하시겠습니까? 시간복잡도와 함께 설명해주세요.
먼저 strings 패키지의 ToLower를 사용해 모든 문자를 소문자로 변환하고, unicode.IsLetter나 정규식으로 알파벳만 추출합니다. 양 끝에서 시작하는 두 포인터를 사용해 중앙으로 이동하며 문자를 비교하는 투 포인터 기법을 사용합니다. 모든 문자가 일치하면 true를 반환하고, 하나라도 다르면 false를 반환합니다. 시간복잡도는 O(n)이며, 공간복잡도는 정제된 문자열을 저장하므로 O(n)입니다.
- • 문자열 정규화 (소문자 변환, 특수문자 제거)
- • 투 포인터 알고리즘 사용
- • 시간복잡도 O(n), 공간복잡도 O(n)
rune 슬라이스로 변환하지 않고 바이트 인덱싱으로 직접 접근할 때 한글이나 이모지 같은 멀티바이트 문자가 포함된 경우 어떤 문제가 발생할 수 있나요?
Q. 정수 슬라이스를 두 개의 슬라이스로 분할하는데, 첫 번째 슬라이스에는 짝수를, 두 번째 슬라이스에는 홀수를 담아야 합니다. 원본 슬라이스의 순서는 유지되어야 할 때 효율적인 구현 방법을 설명해주세요. append 사용 시 주의할 점도 함께 설명해주세요.
두 개의 빈 슬라이스를 생성하고 원본을 순회하며 각 요소를 모듈로 연산(% 2)으로 판별해 해당 슬라이스에 append합니다. 성능 최적화를 위해 make로 슬라이스를 생성할 때 적절한 용량(capacity)을 미리 할당하면 재할당 횟수를 줄일 수 있습니다. append 사용 시 용량 초과로 인한 재할당이 발생하면 기존 배열이 복사되므로, 예상 크기를 알 수 있다면 make([]int, 0, estimatedCap) 형태로 초기화하는 것이 좋습니다. 시간복잡도는 O(n)입니다.
- • 모듈로 연산으로 짝수/홀수 판별
- • make로 용량 미리 할당하여 성능 최적화
- • append의 재할당 메커니즘 이해
슬라이스의 용량이 부족해 재할당이 발생할 때 Go 런타임은 일반적으로 어떤 증가 전략을 사용하나요?
Q. 두 개의 문자열이 애너그램(anagram) 관계인지 판별하는 함수를 구현하려고 합니다. map을 활용한 접근 방법과 sorting을 활용한 접근 방법의 차이점을 설명하고, 각각의 시간복잡도를 비교해주세요.
map 방식은 각 문자의 출현 빈도를 map[rune]int에 저장하고 비교합니다. 첫 번째 문자열을 순회하며 카운트를 증가시키고, 두 번째 문자열을 순회하며 카운트를 감소시켜 최종적으로 모든 값이 0인지 확인합니다. 시간복잡도는 O(n)입니다. sorting 방식은 두 문자열을 rune 슬라이스로 변환 후 정렬하여 비교하며, 시간복잡도는 O(n log n)입니다. map 방식이 더 효율적이지만 추가 공간이 필요하고, sorting 방식은 구현이 간단하지만 느립니다.
- • map을 사용한 빈도 카운팅 O(n)
- • sorting 후 비교 O(n log n)
- • 공간복잡도와 시간복잡도 트레이드오프
Go에서 map은 thread-safe하지 않습니다. 여러 goroutine에서 동시에 같은 map에 접근해야 한다면 어떻게 처리해야 하나요?
Q. 피보나치 수열의 n번째 값을 구하는 함수를 재귀와 반복문 두 가지 방법으로 구현할 수 있습니다. 각 방법의 장단점과 시간복잡도를 비교하고, 실무에서는 어느 방법을 선호해야 하는지 설명해주세요.
재귀 방식은 코드가 직관적이고 수학적 정의와 유사하지만, 중복 계산으로 인해 시간복잡도가 O(2^n)으로 매우 비효율적이며 스택 오버플로우 위험이 있습니다. 반복문 방식은 두 변수로 이전 값들을 추적하며 O(n) 시간복잡도와 O(1) 공간복잡도로 효율적입니다. 실무에서는 반복문 방식을 선호하며, 재귀가 필요하다면 메모이제이션(동적 프로그래밍)을 적용해 O(n)으로 개선해야 합니다. Go는 꼬리 재귀 최적화를 지원하지 않으므로 깊은 재귀는 피해야 합니다.
- • 재귀: 직관적이지만 O(2^n)으로 비효율적
- • 반복문: O(n) 시간, O(1) 공간으로 효율적
- • Go는 꼬리 재귀 최적화 미지원
메모이제이션을 적용한 재귀 방식과 반복문 방식의 공간복잡도를 비교하면 어떤 차이가 있나요?
Q. 여러 개의 URL에서 데이터를 가져와야 하는데, 순차적으로 처리하면 시간이 오래 걸립니다. goroutine과 channel을 사용해 병렬로 처리하되, 최대 동시 요청 수를 제한하는 worker pool 패턴을 어떻게 구현하시겠습니까? 에러 처리와 타임아웃도 고려해서 설명해주세요.
작업을 담을 job channel과 결과를 담을 result channel을 생성하고, 지정된 수만큼 worker goroutine을 시작합니다. 각 worker는 job channel에서 URL을 받아 http.Client의 Timeout을 설정해 요청하고 결과를 result channel로 전송합니다. main goroutine에서는 모든 URL을 job channel에 전송한 후 close하고, sync.WaitGroup이나 결과 개수를 세어 모든 작업 완료를 대기합니다. context.WithTimeout을 사용해 전체 작업에 타임아웃을 적용하고, 각 결과는 데이터와 에러를 함께 담는 구조체로 반환해 에러를 처리합니다. worker 수를 제한함으로써 과도한 동시 연결을 방지합니다.
- • buffered channel로 job queue 구성
- • 고정된 수의 worker goroutine으로 동시성 제한
- • context와 http.Client Timeout으로 타임아웃 처리
- • 결과 구조체에 에러 포함하여 반환
worker pool의 worker 수를 결정할 때 고려해야 할 요소는 무엇이며, CPU 바운드 작업과 I/O 바운드 작업에서 적절한 worker 수가 어떻게 다를 수 있나요?
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!