여러분이 어떤 마음 가짐으로 코딩 테스트에 임하면 좋을지 코딩 테스트 합격자 되기 저자들의 조언을 준비해보았습니다. 그럼 여행을 떠날 준비를 한다고 생각하면서 저와 함께 천천히 시작해봅시다.
1. 합격자가 꼭 되고 싶은 여러분
코딩 테스트를 조금 더 효율적으로 준비하기 위해서 타인의 풀이를 확인하고, 내가 생각한 테스트 케이스를 추가하는 것이 좋습니다. 그럼 구체적으로 왜 그런지 알아보겠습니다.
1.1 타인의 풀이를 보면 사고를 넓힐 수 있다
알고리즘 문제의 해결 방법은 하나가 아닙니다. 다른 사람이 작성한 코드를 보면 자연스럽게 다양한 문제 풀이 접근 방식이나 코딩 스킬을 습득할 수 있습니다. 문제를 푸는 데 어떤 알고리즘을 사용했는지, 입출력을 어떤 방식으로 처리했는지, 예외를 어떻게 처리했는지 등을 학습하기에 유용하죠. 한마디로 사고를 넓히기에 좋습니다.
1.2 나만의 테스트 케이스를 추가하는 건 좋은 알고리즘을 생각할 때 도움이 된다
나만의 테스트 케이스를 추가하는 것은 좋은 알고리즘을 떠올릴 때 도움이 됩니다. 보통 코딩 테스트 문제에서는 문제를 파악할 수 있는 수준에서 입출력의 예 또는 테스트 케이스라는 것을 줍니다. 다음 예를 봅시다.
입출력의 예
왼쪽의 number가 입력이고 result는 문제에서 요구한 출력입니다. 여러분은 이 입출력에 맞도록 코드를 작성할 겁니다. 중요한 것은 대부분 테스트 케이스는 문제를 설명하는 수준에서 제공되는 경우가 많다는 것입니다. 문제 분석 시 고려해야 할 중요 케이스나 실제 구현 시 실수하기 쉬운 사항을 항상 가정하진 않죠. 그래서 제 주변에는 문제에서 제공한 테스트 케이스에만 맞춰 코드를 작성해서 문제 채점 시 사용하는 테스트 케이스를 통과하지 못해 어려움을 겪는 사람이 많았습니다.
바로 이럴 때 ‘나만의 테스트 케이스 만들기’가 필요합니다. 충분한 시간을 들여 문제를 분석한 다음 코드로 구현하기 전에 여러 예외 상황을 충분히 확인할 수 있도록 나만의 테스트 케이스를 추가해보기 바랍니다. ‘우선 문제를 풀겠다’라는 급한 마음에 무작정 코딩부터 하면 오히려 더 많은 시간을 쓰게 됩니다. 시간은 곧 합격과 연결되어 있으므로 낭비해서는 안 되죠. 저는 코드를 작성하기 전, 즉, 문제를 분석하는 단계에서 충분히 예외 테스트 케이스를 추가해볼 것을 권합니다.
2. 아는 것과 모르는 것을 명확하게
본격적인 코딩 테스트를 공부하기 전에 여러분에게 ‘내가 아는 것과 모르는 것을 명확하게 구분하라’고 이야기하고 싶습니다. 왜 그럴까요? 알고리즘을 공부하기 어려운 이유는 공부하는 과정에서 아는 것과 모르는 것의 경계가 모호하기 때문입니다. 코딩 테스트를 준비하는 과정에서 많은 학생이 이런 말을 자주 하곤 합니다.
이는 ‘앎과 모름을 명확히 구분함’으로 해결할 수 있습니다. 그 전략에는 여러 방법이 있겠지만 제가 했던 방법 중 가장 효과적인 방법을 소개합니다.
첫 번째, 기록하라
문제를 푸는 과정에서 무엇이든 많이 기록해야 합니다. 모든 문제는 완벽하게 풀 수 없습니다. 하지만 중간까지는 가 볼 수 있죠. 여기서 중간까지 갔을 때 두 가지 행동을 선택할 수 있습니다.
- 못 푼다고 생각하고 그 자리에서 그만 두기
- (문제를 풀지는 못했지만) 어디까지 생각해봤는지 우선 기록해두기
만약 1번 선택지를 선택한다면 실력이 쉽게 오르지 않을 겁니다. 그래서 필자는 여기를 넘어서라고 이야기하고 싶습니다.
두 번째, 시험 보듯 공부하라
주기적으로 자체 시험을 보면 좋은 결과를 얻을 수 있습니다. 우선 시간에 대한 이야기를 하겠습니다. 시험을 준비하는 과정에서 시간을 간과하는 경우가 굉장히 많습니다. 하지만 시험이라는 것은주어진 시간을 효율적으로 사용하여 최대의 점수를 내는 것이 목표죠. 그러니 평소에 시간 배분 전략을 미리 연습한 사람과 그렇지 않은 사람은 결과가 많이 다를 겁니다. 그다음은 긴장입니다. 긴장도 훈련할 수 있습니다. 어떤 사람은 시험 때 긴장을 너무 많이 해서 그르치는 경우가 있습니다. 하지만 평소에 긴장감을 연습한다면 연습하지 않았을 때보다 더 좋은 결과를 얻을 수 있을 겁니다.
세 번째, 짧은 시간 공부해서는 절대 코딩 테스트를 통과할 수 없다
코딩 테스트는 짧은 시간에 준비할 수 없습니다. 알고리즘 역시 짧은 시간 안에 공부할 수 없는 과목이죠. SNS나 유튜브의 과장 광고에 현혹되지 마세요. 보통 이런 말을 많이 합니다.
- 6시간 안에 끝내기
- 하루 만에 끝내기
- 일주일 만에 끝내기
사실 수험생 입장에서 현혹되기 너무 쉬운 광고 문구들입니다. 하지만 저는 단호하게 이야기하겠습니다.
“이런 방법은 없습니다.”
현실을 명확하게 인지하세요. 그런 방법은 없습니다. 저는 코딩 테스트를 준비하는 분들에게 코딩 테스트는 최소 한 달에서 두 달 정도를 매우 집중해서 공부해야 한다고 이야기합니다.
네 번째, 나만의 언어로 요약하라
마지막으로는 이해한 뒤에는 반드시 요약해보기 바랍니다. 인간의 뇌는 굉장히 긍정적입니다. 그래서 남이 작성한 글을 보고 ‘내가 이해했다’라고 착각하기 쉽죠. 정말 이해했는지 확인하는 방법은 이해한 내용을 요약해보는 겁니다. 만약 요약을 잘할 수 있다면 실제 문제를 풀 때도 이해한 내용이 쉽게 떠오를 겁니다. 내가 공부한 개념을 나만의 언어로 요약하는 것에 초점을 맞추세요.
여기까지 잘 이해했다면 이제 코딩 테스트를 공부할 때가 되었습니다. 본격적으로 코딩 테스트를 준비해봅시다.
3. 코딩 테스트 코드 구현 노하우
코딩 테스트를 처음 공부하면 만나는 첫 난관은 코드 구현입니다. 자료구조나 알고리즘은 이론 지식이므로 공부하면 지식이 쌓이면서 실력이 늡니다. 하지만 코드 작성 노하우는 쉽게 늘지 않습니다. 여기서는 코딩 테스트에 유용한 코드 작성 노하우를 몇 가지 소개하겠습니다. 이런 노하우는 하루만에 습득하기 어렵습니다. 습관이 되어야 하므로 코드를 작성할 때마다 적용해보기 바랍니다.
3.1 파이썬
3.1.1 조기 반환
조기 반환(Early Return)은 코드 실행 과정이 함수 끝까지 도달하기 전에 반환하는 기법입니다. 이 방식은 코드의 가독성을 높여줄 뿐만 아니라 예외를 조금 더 깔끔하고 빠르게 처리할 수 있습니다.
def total_price(quantity, price):
total = quantity * price # ➊
if total > 100: # ➋ total이 100보다 크면
return total * 0.9 # ➌ 조기 반환
return total
print(total_price(4, 50))
➊ total에 quantity * price를 대입합니다. ➋ total의 값이 100보다 큰 경우 ➌ total에 0.9를 곱하고 반환합니다. 이렇게 하면 함수 자체를 조기에 종료할 수 있으므로 이후 예외에 대한 처리를 하지 않아도 됩니다.
3.1.2 보호 구문
보호 구문(Guard Clauses)은 본격적인 로직을 진행하기 전 예외 처리 코드를 추가하는 기법입니다. 예를 들어 조건문을 이용하여 초기에 입력값이 유효한지 검사하고 그렇지 않으면 바로 함수를 종료하는 보호 구문을 쓸 수 있습니다.
def calculate_average(numbers):
if numbers is None: # ➊ 값이 없으면 종료(예외)
return None
if not isinstance(numbers, list): # ➋ numbers가 리스트가 아니면 종료(예외)
return None
if len(numbers) == 0: # ➌ numbers의 길이가 0이면 종료(예외)
return None
total = sum(numbers) # ➍
average = total / len(numbers)
return average
이렇게 구현한 코드는 보호 구문 이후 구현부에서 입력값에 대한 예외를 고려하지 않아도 되므로 보기 좋습니다. 추가로 이런 습관을 들이면 처음부터 예외를 고려할 수 있어 코드를 더 안전하게 작성할 수 있게 됩니다. 코드를 보면 ➊, ➋, ➌에서 예외 처리를 하여 함수를 종료시킵니다. 여기서 예외를 잘 고려했다면 이후 코드에서는 ❹와 같이 원하는 동작 구현에만 집중하면 되겠죠.
3.1.3 합성 함수
합성 함수(Composite Method)는 2개 이상의 함수를 활용하여 함수를 추가로 만드는 기법입니다. 보통 합성 함수는 람다식을 활용합니다.
def add_three(x): # ➊
return x + 3
def square(x): # ➋
return x * x
composed_function = lambda x: square(add_three(x)) # ➌
print(composed_function(3)) # ➍ (3 + 3)^2 = 36
➊과 ➋에서 2개의 함수를 정의한 다음 ➌에서 이 두 함수를 합성합니다. 그 결과 ➍ 실제 호출하는 부분에서는 두 함수를 마치 하나의 함수처럼 활용하는 것을 볼 수 있습니다. 이렇게 코드를 구현하면 작은 기능을 분리해서 코드를 작성할 수 있으므로 관리자는 코드를 쉽게 관리할 수 있고, 함수를 사용하는 사용자는 코드를 쉽게 사용할 수 있습니다.
3.2 자바
3.2.1 조기 반환
public static void main(String[] args) {
System.out.println(totalPrice(4, 50));
}
static int totalPrice(int quantity, int price) {
int total = quantity * price; // ➊
if (total > 100) // ➋
return (int)(total * 0.9); // ❸
return total;
}
내용은 파이썬과 동일합니다.
3.2.2 보호 구문
보호 구문(Guard Clauses)은 본격적인 로직을 진행하기 전 예외 처리 코드를 추가하는 기법입니다. 예를 들어 조건문을 이용하여 초기에 입력값이 유효한지 검사하고 그렇지 않으면 바로 함수를 종료하는 보호 구문을 쓸 수 있습니다.
import java.util.List;
static double calculateAverage(List<Integer> numbers) {
if (numbers == null) // ➊ null 이면 종료(예외)
return 0;
if (numbers.isEmpty()) // ➋ 데이터가 없으면 종료(예외)
return 0;
int total = numbers.stream().mapToInt(i -> i).sum(); // ➌ 예외 처리 후 기능 구현
return (double) total / numbers.size();
}
코드를 보면 ➊, ➋에서 예외 처리를 하여 함수를 종료시킵니다. 여기서 예 외를 잘 고려했다면 이후 코드에서는 ➌과 같이 원하는 동작 구현에만 집중하면 되겠죠.
3.2.3 제네릭
제네릭(Generic)은 빌드 레벨에서 타입을 체크하여 타입 안정성을 제공하고, 타입 체크와 형변환을 생략할 수 있게 해주어 코드를 간결하게 만들어주는 기능입니다.
List list = new ArrayList();
list.add(10);
list.add("abc");
int sum1 = (int)list.get(0) + (int)list.get(1); // ➊ 런타임 오류 발생
List<Integer> genericList = new ArrayList<>();
genericList.add(10);
genericList.add("abc"); // ➋ 문법(빌드 레벨) 오류 발생
int sum2 = genericList.get(0) + genericList.get(1);
코드를 보면 ➊에서는 런타임 오류가 발생하고, ➋에서는 빌드 오류가 발생합니다. List를 정의할 때 <Integer>와 같이 타입을 강제하는 것을 제네릭이라고 합니다. 제네릭은 타입에 맞지 않는 데이터를 추가하려고 할 때 문법 오류를 발생시켜 개발자의 실수를 방지해줍니다.
따라서 ➊은 코드를 실행해야만 오류가 발생한다는 것을 알 수 있지만, ➋는 빌드 자체가 안되므로 런타임 버그를 방지할 수 있습니다. 또한 데이터에 접근하여 사용하려고 할 때 형변환을 할 필요가 없기 때문에 코드가 간결해집니다. 코딩 테스트에서는 여러 타입의 데이터를 하나의 컬렉션에 넣어야 하는 경우는 거의 없으므로 제네릭으로 타입을 강제하여 실수를 방지하는 것이 좋습니다.
4. 코딩 테스트 합격자 되기 저자들의 조언
4.1 박경록 파이썬 편 저자
여러분에게 꼭 말하고 싶은 내용을 전달하려고 하다 보니 “해야 한다”, “중요하다”와 같은 말만 반복해서 좀 무거운 이야기만 한 것 같네요. 학생 때 이런 말을 들으면 ‘참 하라는 것도 많네…’라고 생각했던 것 같은데 말이죠. 그래서 하나 더 생각나는 조언이 있다면 ‘휴식도 당당하게 하라’고 이야기하고 싶네요. 돌이켜보면 저도 코딩 테스트를 준비하면서 뭔가 쫓기는 느낌이 들 때가 많았던 것 같은데 그때 휴식이 왜 그렇게 죄짓는 것처럼 느껴졌는지 모르겠습니다. 여러분은 그렇게 생각하지 않기 바랍니다. 그리고 그렇게 해서 목표한 바를 다 이루셨으면 좋겠습니다.
4.2 김희성 자바 편 저자
코딩 테스트를 공부하고 준비하는 것은 힘들고 지루한 시간의 연속일 것입니다. 더군다나 취업 준비 과정 전체가 매일 반복되는 합격과 불합격 속에서 지치고 힘들다는 것은 7년 전의 저도 경험했기에 조금이나마 기억하고 있습니다. 2016년도 하반기에만 저는 50개가 넘는 이력서를 넣었습니다.
많게는 하루에 6~7개 기업에서 불합격 소식을 받기도 하고, 운좋게 [서류 전형 – 코딩 테스트 – 1차 면접 – 최종 면접]까지 봤던 기업에서 최종 면접 불합격 소식을 받았을 때 ‘이쯤에서 포기하고…마음이 가지는 않지만 일단 아무 곳이나 취업을 해야 하나?’라는 생각도 많이 했습니다. 하지만 3번의 도전 끝에 제가 원하던 회사의 코딩 테스트에 합격하게 되었고, 운좋게 얻은 면접 기회도 잘 살려서 최종 합격할 수 있었습니다.
모두가 저와 같을 수는 없겠지만 제가 항상 개발자가 되길 원하는 후배님들에게 하는 얘기가 있습니다. 첫째는 ‘도전해라’입니다. 어떤 분들은 본인의 능력을 너무 과소평가해서 ‘대기업 혹은 네카라쿠배 같은 회사는 당연히 떨어지겠지’라고 생각하고 이력서도 넣지 않는 분들이 많습니다. 우스갯소리로 서류 지원조차 하지 않아서 불합격하시는 분들을 보면 너무나 안타까웠습니다. 두 번째는 ‘기회를 놓치지 마라’입니다. 아직 준비가 덜 되었다는 생각이 들어도 원하는 기업의 채용 공고가 열리면 꼭 지원해 보기를 추천합니다. 자기소개서 및 이력서를 써보는 것도 경험이고, 코딩 테스트, 면접도 다 경험입니다. 실제 경험은 그 어떤 연습보다 강력한 무기가 될 수 있습니다. 한 번에 최종 합격까지 하면 더할 나위 없이 좋겠지만 만약 중간에 불합격하더라도 그 경험을 발판 삼아다음 기회에 더 잘 해내실 수 있을 겁니다.
제 짧은 인생 경험 상 안 해보고 포기한 건 후회로 남아도, 도전해보고 실패한 것은 후회로 남지 않았습니다. 그러니 꼭 도전하고 지원하세요. 여러분을 응원합니다.
저자 박경록
매일 퇴근과 점심 메뉴를 고민하는 9년차 시스템 S/W 개발자입니다. 수학, 알고리즘 같은 실생활과 가깝고도 먼 학문을 좋아하고, 명확하지만 개선 여지가 있는 문제들에 대해 논의하고 사고를 개선해 나가는 과정을 좋아합니다.
저자 김희성
현 42dot 백엔드 개발자. 이전에는 삼성SDS에서 소프트웨어 개발자, 쿠팡에서 풀스택 개발자로 근무했다. 특히 삼성SDS 시절에는 사내 SW역량테스트 강사로 활약했다. 귀찮은 거 싫어하고 집에서 자는 게 가장 좋은 백엔드 개발자다. 어려운 문제와 맞닥뜨렸을 때 더욱 불타오르는 타입. 새벽 시간에 코드짜는 걸 좋아하며 주말에 밤새 코딩하는 일을 즐기는 ESTJ.