배경

SW마에스트로, 소마는 학교 동기나 선배들한테 몇 번 들었던 것 같다. '지원이 좋은 만큼 경쟁이 빡세다.' 이런 말을 많이 들었던 것 같다. 내가 될 것이라는 확신은 딱히 없었다. 올해부터는 개발자 방향으로 가겠다 생각하며 의미 있는 프로젝트를 하고 싶다는 마음에 지원했다.

사실 코딩테스트는 엄청 자신이 있었다. 웹/앱 개발은 내가 해본 적이 없지만, 학교 전공은 정말 열심히 했고, 자료구조, 알고리즘에는 자신이 있었다. SCPC, UCPC 이런 것도 나가 봤지만, 이런 대회 우승 만큼의 알고리즘 실력은 아니긴 하다. 백준 골드 이하는 쉽게, 플레 이하는 조금 어렵게 푸는 정도다. 또 최근에 정보올림피아드 대비반의 코딩 강사를 1년 정도 했기 때문에 알고리즘에서 질 것 같지는 않았다.


서류

서류 질문은 아래와 같다

  1. SW분야의 전문성을 키우기 위해 몰입했던 경험과 도전이 무엇인지, 또한 이러한 성장과정을 통해 얻은 배움은 무엇인지를 서술하여 주시기 바랍니다.(최소 400자, 최대 1000자 입력가능)

  2. SW마에스트로 과정 참여를 통해 어떠한 프로젝트를 수행하고 싶은가요? 해당 프로젝트를 수행하기 위한 계획과 이루고자 하는 목표가 무엇인지 구체적으로 서술하여 주시기 바랍니다.(최소 400자, 최대 1000자 입력가능)

1번 문항에 대해 나는 학교 전공학점과 알고리즘을 소홀히 하지 않은 점, 군 복무 중에도 알고리즘을 풀었다는 것(근무 간 공백 시간에 머릿 속으로 알고리즘의 코드를 생각했었다...), 코딩 강사 활동, 시스템 연구실에서 공부했던 점들을 썼다.
2번 문항은 내가 얻고 싶은 게 무엇인지 솔직하게 썼다. 내가 개발하고 싶은 아이디어에 대한 소개도 조금 했던 것 같다.

없는 말을 지어내면 안되지만, 자신의 가치가 100인 것을 100이라고 곧이 곧대로 말하는 것은 바보라고 생각한다. 100을 120이라 포장해서 말해야 면접관도 이 친구가 100이라고 생각한다. 그리고 다른 경쟁자들도 100을 120이라고 포장할 테니까. 생각보다 주변의 사람들은 이런 것을 잘 못한다. 물론 내적인 성장을 하는 것이 훨씬 중요하지만, 자신을 포장할 때에는 포장에 심혈을 기울여야 한다고 생각한다.

사실 서류는 어느 정도 성의가 있으면 통과를 주는 편이긴 하다. 그래도 이후 면접 때 이 서류를 확인할 수 있기 때문에 더 정성을 들여 작성을 하는 것이 좋은 것 같다. 나는 2차 코테를 통과한 이후에 이 서류가 아쉽다는 생각을 조금 했었다. 열심히 작성하지 않아서가 아니라, 면접 때 말하고자 하는 내용과 맥락이 정확하게 맞지는 않아서이다.


1차 코테

코테는 알고리즘 4문제, SQL 1문제로 이루어져 있다. 예상했던 것보다 알고리즘 문제들이 쉬웠다. SQL도 난이도가 쉬운 편이였다. 연수생들을 선별해 내기에 적절하지 않은 난이도라고 생각했다.

알고리즘 문제들은 구현, 문자열, 그리디를 사용하는 쉬운 문제들이였다. 소마 코테는 기존에 많이 풀었던 백준에 비해 C++로는 조금 불리한 부분이 있었다. Parsing 문제들은 아무래도 Python으로 구현하면 시간이 많이 단축되긴 한다.

알고리즘 4문제를 푸는데 50분 정도 걸렸다. 하지만 SQL을 구현하는 문법이 생각나지 않아 나머지 1시간 10분중 15분을 남기고 제출했다. SQL 문제가 보통 쉽다고 하여 준비를 덜 했던 것이 위험한 선택이 될 수도 있었다. 다행히 5솔로 통과했다. 오픈카톡방에 의하면 4솔이 커트인 것 같다.


2차 코테

2차 코테는 1차에 비해 난이도가 많이 상향되었다. 알고리즘 1, 2, 3은 모두 풀었다. 각각 구현, DP, 분할정복으로 풀 수 있는 할 만한 난이도였던 것 같다. 4번 문제에 대한 해법이 잘 떠오르지 않아서 시간을 많이 낭비했다. 시험이 끝나고 나서야 이전에 풀었던 문제와 비슷하게 해법을 떠올릴 수 있었다. SQL 문제는 아무래도 준비를 많이 못해서 내가 아는 문법들로 완성하기에는 힘들었다. 3솔로 아슬아슬하게 코테를 통과했다. 오픈카톡방에 의하면 2솔이 커트인 것 같다.
2차 코테의 문제들은 기억나서 내가 푼 방식들을 간단히 적어 본다.

1. 워드 프로세서

Parsing 문제다.

Python이라면 split()을 쓰면 되고, 나는 C++로 쌩 구현을 했다. 이럴 때마다 Python이 사용하고 싶지만, 알고리즘은 C++로만 하겠다는 신념을 지켰다.

2. 사과 떨어지는 것 받기

사과가 격자판에서 위에서 아래로 떨어지고, 사과의 초기 상태가 주어진다. 바구니 2개가 좌우로 최대 한칸 움직이거나 제자리에 있을 수 있을 때, 사과를 가장 많이 받았을 때의 개수를 묻는 문제였다.

DP로 간단히 풀리는 문제다. 이런 문제는 백준에서 자주 구현해본 코드라면 쉽게할 수 있다. 코테를 준비하는 사람이라면 백준을 꾸준히 푸는 것은 필수라고 생각한다.
시간복잡도: O(t*n^2) (t는 격자판의 높이, n은 격자판의 너비)

3. 포화이진트리 회전

포화이진트리의 leaf node에 숫자가 입력으로 주어진다. Internal node는 회전이 가능하고 회전하면 그 노드를 root으로 두는 sub-tree가 말 그대로 회전을 한다. 회전을 원하는 만큼하여 leaf node의 숫자들을 이어붙였을 때 가장 큰 값이 되도록 결과를 출력해라.

분할정복으로 해결하면 된다. Merge sort을 코드로 구현해 봤다면 그 과정과 유사하게 문제를 해결할 수 있다. Merge sort은 재귀호출을 마친 두 정렬된 배열에 포인터를 두고 각 배열에서 작은 값을 하나씩 옮기는 방식으로 정렬되어 합쳐진 배열을 만든다. 이에 비해, 이 문제는 비교 과정에서 오른쪽 배열의 숫자값이 더 크다면 배열을 스왑해주면 된다. 여기서 왼쪽과 오른쪽의 배열을 스왑해주는 것이 모빌의 회전과는 엄연히 다른 행동이다. 하지만 개수를 세어주지 않아도 되기 때문에 이 회전에 의해 sub-tree들의 좌우를 다시 바꿔야 하는 행동은 sub-tree의 모든 internal node들에 대해 모빌 회전을 하는 행동으로 대체 가능하기 때문에 무시해도 무방하다.

위 문제에서 모빌의 최소 회전 횟수를 묻는 문제였다면 문제의 난이도가 훨씬 어려워질 수 있다. 집에 가서 복기해보면서 문제가 이랬다면 내가 풀 수 있었을까 고민을 해봤다. 고민을 오래 해보니 아래와 같은 방법이 가능할 것 같다.

  1. array representation으로 실제 모빌의 트리를 구현한다.
  2. Leaf node의 배열에 대해 merge sort을 진행하면서, 트리의 internal node에 회전의 여부를 bool으로 저장한다
  3. 다시 모든 노드에 대해 트리 순회를 하면서 노드의 bool 값을 부모 노드의 bool 값에 따라 뒤집어 가면서 변경한다. (여기서 bool값을 사용하는 이유는 한 internal node에 대한 회전 2n번은 0번, 2n+1번은 1번 회전하는 것과 동일한 효과이기 때문이다)
  4. 위에서 말했던 순회 중 회전이 필요한 internal node 에 대해 ans++를 해준다.

물론 위의 방법처럼 3, 4번을 진행하지 않고 2번에서 재귀 호출의 return 값을 통해 최소 모빌 회전 횟수를 구하는 방법도 있지만, 위의 방법이 코테라면 더 신속하게 구현할 수 있다고 생각한다. 코테는 최선의 풀이보다 시간 제한에 만족하는 빠른 풀이가 적절하니까.
시간복잡도: O(nlogn) (n은 leaf node의 개수)

4. 불 끄기

정사각형 격자판의 판에 불의 현재 상태가 꺼진 불 0, 켜진 불 1로 주어진다. 격자판의 세로와 가로의 인덱스 합이 홀수일 때 그 격자판을 누르면 + 모양으로 5개의 불이 꺼지고, 인덱스 합이 짝수이면 그 격자판을 누를 때 X자 모양으로 5개의 불이 꺼진다. 최소 횟수로 격자판을 눌러 모든 불을 끌 때 그 횟수를 출력하고, 불가능하다면 -1을 출력하라는 문제였다.
백준 14939번과 매우 유사한 문제다.

코테를 보는 당시에 내가 관찰한 것은 다음과 같았다.
1. 모든 격자판은 한 번 누르거나 안 누르는 것이 최적이다.
2. 일단 +모양으로 불이 꺼지는 인덱스에 불이 켜져있다면 이 격자판은 눌러야만 한다. (이 격자판 주변의 모든 격자판은 X모양으로 불이 꺼지고, 이를 통해 현재 격자판의 불을 끄지는 못한다)
여기까지 관찰하면 나이브하게 문제는 2^(n^2/2)의 시간이 걸린다. (n은 격자판 한 변의 칸 수)

하지만 14939의 풀이를 알았는데도, 이 문제를 풀지 못했다. 시간적으로도 부족했지만, 이 다음에 어떤 격자판들을 브루트포스로 적용해야 나머지 격자판들을 누를지 말지에 대한 결정이 되는지를 판단하지 못했다. 그래서 매우 아쉬웠다.
시간복잡도: O(2^n*n^2)

5. SQL

은행 계좌에서 입/출금 내역이 DB에 다음과 같은 정보와 함께 저장되어 있다.
{입/출금 여부, 입/출금 양, 요청 시간}
은행 계좌가 0원으로 시작했을 때 은행 계좌 잔액의 상태를
{시작 시간, 끝나는 시간, 앞의 시간 간격동안 은행 계좌 잔액}
이런 방식으로 시간 오름차순 정렬로 출력하는 SQL문을 짜라.

이 문제는 SQL문을 풀 시간은 부족해서 포기했다. 사실 SQL문을 실전에서 써 본 경험은 없기에 나에게는 어려운 SQL문제였다. 시간이 더 많이 주어졌다면 섭 쿼리문을 많이 엮어서 풀 수는 있었을 것 같다.


면접

2차 코테까지 붙으니, 제일 걱정되는 면접이 다가왔다. 앱/웹 개발 경험이 적다는 것이 나에게 큰 약점이었다. 그래서 면접에서 남들에게 질 것 같다는 불안감이 컸다. 그래서 이 면접 준비에 시간을 정말 많이 썼다.

우선 노션으로 자신의 소개페이지를 만들어야 한다. 주어진 기본 양식이 있지만, 나에게는 해당하지 않는 내용이 많았다. 첫째로, Tech Stacks에 다들 실무에 사용되는 언어를 적겠지만, 나는 학교 전공에 연계된 것들 외엔 일체 경험이 없었다. 그래서 백엔드 개발자를 희망한다는 것을 보여주기 위해 공부중인 언어들을 적었다. 실제 면접에서 이것 때문에 오히려 불리한 상황이 생겼다. 둘째로, 프로젝트 경험들에 적을만한 것이 없었다. 그래서 시스템 연구실 인턴 경험과 코딩 강사의 경험을 적었다. CS전공의 높은 학점도 어필을 했다.

100을 120으로 포장해야하지만, 없는 사실을 지어내면 안 된다. 분명 면접 질문으로 들어왔을 때 대답을 못한다면 포장한 20은 물론이고, 가지고 있던 50도 잃을 수도 있다. 이 선을 잘 지키려고 노력했던 것 같다.

노션 페이지를 만들고 나서 남은 1주일동안은 면접 질문에 대한 답변을 준비했다. 인터넷에 떠도는 소마 면접 질문들과 내 자소서, 개인소개 페이지 관련해서 질문을 총 67개 뽑고 그 질문들의 답을 적고 이를 말하는 것을 연습했다.

실제 면접장에 가서 나의 노션 페이지를 열고 발표를 시작했다. 중간쯤에 내 노션페이지가 일부 짤린 사실을 알게 되었다. 3초 쯤 정적이 있었다. 그냥 포기하고 머릿속에서 알고 있는 내용을 토대로 잘 마쳤다. 나중에 알고보니, 공유한 노션 페이지에 포함된 일부 페이지를 공유하지 않은 것이 문제가 된 것 같다. 이런 실수는 하지 말자.

기억나는 질문들은 아래와 같다.

단체 질문

Q. 개발 외에 어떤 취미를 갖고 있나요?

(이 질문은 긴장 풀으라는 의미로 하신 것 같다. 면접자 5명이 모두 대답하고 난 뒤에, 취미가 있어야 개발하다가 받는 스트레스에서 잘 회복한다고 하셨다)

Q. 최신 기술을 알기 위해 본인이 노력하는 방법이 있다면 얘기해보세요.

(네이버 컨퍼런스에 가서 들었던 적이 있는데, 개발 최신 트렌드를 얻기 위해서 본인이 직접 찾는 것이 어렵다면, 개발자와 네트워크를 잘 형성해서 트렌드를 잘 아는 사람과 친하게 지내라는 얘기를 들은 적이 있다. 이에 대해서 말했다)

Q. 팀원이 되고 싶나요 팀장이 되고 싶나요?

(지금은 팀장이 되었지만, 팀원이라고 답했었다. 개발에 대한 지식이 부족하기 때문에 팀장으로써 역할을 다하지 못할 것 같다고 솔직하게 답했다)

Q. 팀원이 되어서 팀장이 하고 싶지 않은 일을 시킨다면 어떻게 하실건가요?

(팀원의 효율적인 업무 분담이라면 참고 할 것이고, 그것이 아니라면 팀장에게 의사를 잘 전달할 것이라고 얘기했다. 무조건 참는 것도 좋지 않고, 불만이라고 무조건 표출하는 것도 좋지 않다고 생각한다)

개인 질문

Q. 연구실에서 자신이 얻게 된 것을 말해보세요.

(예측했던 것이라 준비한대로 잘 대답했다)

Q. DB에서 인덱싱을 활용한 저장 방식을 가진 예시를 하나 말해보세요.
String type과 integer type의 자료형을 DB에 저장할 때 어떻게 다른 지 설명해보세요.
트라이 아세요?

(이 면접관 분이 정말 공격적으로 질문하셨다. 첫번째 질문에 대답을 잘 했고, 두 번째 질문은 뭔가 답변이 마음에 안 드셨는지, 직접적으로 트라이를 아냐를 질문했다. 그 자리에서는 잠깐 고민하다가 자바 try except, 예외 처리를 물어보는 것이냐고 반문했다. 면접관은 면접자 모두에게 트라이 들어본 사람 있냐고 물어보셨고, 한 명이 손을 들었다. 면접관이 설명해보라고 하자, 그 면접자는 들어본 적만 있다고 답했다. 모두가 웃었다)

Q. 코틀린 관련 질문 하나가 들어왔는데 기억이 안난다.

(코틀린, 스프링부트는 공부를 시작하고 있다고 적었는데, 이게 문제가 되었다. 나는 공부를 하겠다는 의지를 보여주기 위해 적었다. 이 질문에 답을 못하니, 면접관은 "코틀린 책의 첫 장에 적혀 있을텐데?"라고 말했다. 이때 떨어졌구나 하고 생각했다)

Q. 소마에서 하고 싶은 프로젝트 소개를 해보세요.

(자소서에 적은 내 아이디어에 대해서 설명했다)

Q. 복싱 대회도 나가봤나요? 정보올림피아드 대비반 강사 어렵지 않나요? 5년후에는 창업, 취업, 코딩 강사중 무엇을 하고 있을 것 같나요?

(번외 질문들이다)

기술 질문에서 답을 잘 못해서 아무 질문이나 하는 것 같았다. 아니, 포트폴리오에 물어볼만한게 없어서 그렇구나 하고 생각했다. 노션에 일부 페이지가 짤린 것과 기술스택에 앞으로 공부할 것을 표기한 것이 후회됐다. 앞으로 공부할 것이라고 따로 표기하고 발표에서도 그렇게 언급했는데... 아쉬운 점도 많았지만 후련했다.

이렇게 자신에 대해 정리를 해본 적이 있나? 면접이라고는 고등학교 입학 면접이 마지막이였고, 성인이 되어서는 처음으로 면접 준비를 해봤다. 학교 3년과 군대 2년 동안의 나를 정리하는 것 같아서 정말 뜻깊은 일이었다. 떨어졌다고 생각하고 학교 공부를 했다.

결과는 합격이였다.

0개의 댓글