작년초 면접 스터디를 할 당시에 스터디를 결성하셨던 분께서 직접 면접 스터디용 앱을 직접 만들어 주셨다. 당시에 코딩 경력이 1년 남짓 되는 코린이였기에(물론 아직도..) '와.. 이런 걸 직접 만들 생각을 한다고?', '나도 언젠가 필요한 걸 직접 만들어서 써봐야겠다!'라는 생각을 했었던 것이 기억난다. 그러다 마침 이번에 상반기 취업 준비를 위한 면접 스터디를 진행하게 되었다. 처음에는 작년에 사용했던 앱을 그대로 쓸까 생각했지만, 몇 가지 문제가 있었다.
- 당시 면접 스터디 진행 방식에 맞춰져 있는 기능
- 프론트만 구현되어 있었기 때문에 질문을 목데이터로 만들어서 수동으로 관리해야되는 점
- 편의상 로컬에서 사용해야 했기 때문에 누군가 항상 로컬에서 실행시킨 뒤에 화면공유로 진행해야 하는 점
그리고 무엇보다 가오가 상하지 않는가
1년의 시간이 흐른만큼 내가 성장했다면 그 때 그 분이 만드셨던 것보다 더 뛰어난 앱을 만들어내야 하지 않겠는가! 그런 생각이 들어서 스터디원들에게 내가 직접 만들어 오겠다고 호언장담을 해버렸다. 물론, 하루만에 만들 생각은 없었고, 설날 연휴를 이용하여 아주 기깔난 앱을 만들어 오리라 다짐했었다. 그런데 그게 하루가 되었다. 민족 대명절 설은 쉬는 게 맞다. 그래도 하루만에 만들었기 때문에 이 몹쓸 UI의 앱에도 나름의 경쟁력이 생겼다. 기존에 사용했던 앱은 사실 누가봐도 아기자기하고 깔끔한 디자인을 갖추고 있었기에 시간마저 오래 걸렸으면 제목에 쓸 말이 없었을 것 같다.
이 투박하면서도 기능을 중시한 직관적인 디자인의 버튼을 보고 있으면, 정말 가슴이 웅장해지는 것 같다. 누가 봐도 질문 목록으로 돌아갈 수 있을 것만 같다.
다음 면접 스터디 전까지 만들어 가겠다고 말을 해놨기 때문에 어찌저찌 컴퓨터를 키고 구현할 것들을 정리해 봤다. 총 세 페이지로 구현할 생각이었고, 면접 질문 목록 페이지, 면접 목록 페이지, 면접 진행 페이지가 필요했다.
- 각각의 질문들을 카테고리별로 분류 - MVP
- 이미 진행했던 질문들 완료되었음을 표시할 수 있어야 함 - MVP
- 질문 추가 삭제가 자유로워야 함 - MVP
스터디의 진행 방식은 질문 목록에서 새로운 질문들을 랜덤하게 뽑고, 다음 스터디까지 해당 질문에 대한 답변을 공부해와서 답변하는 방식으로 진행된다. 따라서 이미 진행했던 질문은 다음 번에 질문을 뽑을 땐 제외시킬 필요가 있어서 질문 목록은 완료 표시를 할 수 있는 체크리스트로 구현하려고 생각했다. 또한 우리가 모든 질문을 다 완벽하게 대비할 수는 없기 때문에 항상 추가 삭제는 자유로워야 하는 점을 고려했다.
- 면접 질문을 뽑으면 해당 질문 리스트가 저장되어 있어야 함 - MVP
- 완료된 면접과 진행중 면접을 구분할 수 있어야 함 - 선택
- 면접 카드만 보고 어떤 질문을 진행했던 면접인지 알 수 있으면 좋음 - 선택
스터디 진행 방식상 면접 질문을 선택하고 해당 질문들에 대한 모의 면접을 진행하기까지는 3일 정도의 텀이 있다. 그렇다면 질문을 고르는 순간 골라진 질문 목록을 저장해 둘 필요가 있었다. 그리고 스터디 활동 내용을 기록하는 차원에서도 데이터가 남아있으면 좋을 것 같다는 생각을 했다. 그리고 당연히 제일 최신 면접 질문이 현재 진행해야할 질문들이 되겠지만, 시각적으로 확실하게 완료된 면접과 진행중인 면접을 구분할 수 있으면 좋을 것 같았고, 마찬가지로 해당 면접에 어떤 질문들이 포함되어 있는지 간략하게라도 알 수 있으면 좀 더 편의성 측면에서 헷갈리지 않고 좋을 것 같았다.
- 면접 참여자를 입력으로 받아서 진행 - MVP
- 각 질문에 참여자 랜덤 배정 - MVP
- 각 질문 마다 해당 면접을 진행하며 물어봤던 꼬리질문이나 추가적으로 얘기를 나눴던 부분들을 댓글 형태로 남길 수 있으면 좋음 - 선택
매 스터디 마다 5개의 기술 질문과 1개의 기본 질문(인성, 경험)을 골라 준비하기로 했다. 그런데 스터디 진행할 때 모든 사람이 모든 질문에 답변하는 방식으로 진행하면 시간이 너무 오래 걸려서 기본 질문은 모두가 답변하는 것으로 하고, 기술 질문 5개 중 2개씩 랜덤으로 뽑아서 답변하기로 했다. 이 부분을 알고리즘으로 구현하는 것이 정말 별 거 아닌 것 처럼 보였었는데, 면접 진행 방식과 맞물려서 많은 골칫거리를 제공하기도 했다. 그리고 질문에 답변하는 사람 외에 다른 사람 모두가 꼬리 질문을 하기 때문에 꼬리 질문을 하며 추가적으로 알게 되는 내용들도 많았는데, 이런 내용들을 기록할 수 있는 댓글 기능이 있으면 좋을 것 같았다.
네이버 부스트캠프 당시에 Node.js를 공부하기도 했었고, 인턴을 나름 풀스택 개발자 포지션으로 갔었기 때문에 Node.js로 백엔드 코드를 짜보기도 했다. 그래서 처음엔 Node.js를 사용해서 백엔드를 구현하려고 생각을 했었다. 하지만 이내 DB는 어떤 걸 사용해야 하고, 배포는 어떻게 해야 하는지 도저히 하루만에 끝낼 수 있을 것 같지는 않았다. 그러던 중 예전에 부트캠프에서 쇼핑몰 만들기 과제에서 사용했던 Firebase가 생각났다. 사실 당시에 제대로 공부해서 쓰지는 않았고, 강의에서 알려주는 내용들 바탕으로 열심히 따라했었기 때문에 정확히 어떻게 쓰면 되겠다는 생각보다는 'Firebase로 굉장히 간편하게 백엔드를 구현할 수 있다'는 느낌만 남아있었다. 무엇보다 Firebase를 사용하면 따로 배포를 할 필요가 없다는 게 가장 큰 장점이었다. 그리고 굉장히 가벼운 앱이고, 많아봐야 동시 접속자가 10명도 안 되기 때문에 성능적으로 크게 문제될 것 같지도 않았다.
프론트엔드의 경우는 첫 메인페이지가 질문 목록 페이지가 될 것이었기 때문에 이 부분을 서버 사이드 렌더링으로 구현해 보고 싶어서 Next.js를 선택했다. 물론, Vercel로 간편하게 배포할 수 있다는 점도 큰 장점으로 작용했다.
이렇게 '딸깍 개발 기술스택'이 완성되었다.
이번에 Firebase를 써보면서 정말 편리하게 DB를 만들 수 있다는 것을 느꼈다. 단 몇 번의 클릭만으로 DB 생성이 가능하다! 그리고 항상 배너에 안내가 잘 나와있었던 것 같아서 배너를 잘 보면서 만들었던 것 같다.
우선 로그인을 하고나서 우측 상단의 Go to console을 누르거나 구글에 Firebase Console로 검색해서 들어가면 아래 페이지가 나오고, 프로젝트 만들기 버튼이 바로 있다.
(1) 딸깍
프로젝트 이름을 입력하고, AI 지원을 받을 것인지(설정하면 AI 채팅을 통해 Firebase 이용에 도움을 받을 수 있는 것 같다), 구글 분석 서비스를 사용할 것인지 등을 설정한 후 프로젝트가 생성된다.
다음으로 해당 프로젝트에 들어가 보면
(2) 딸깍
이렇게 배너에 앱에 Firebase를 추가하라는 안내가 뜬다. 내가 만들고 있는 앱은 웹 서비스였기 때문에 웹을 선택해줬다.
(3) 딸깍
앱 이름을 입력하고, 호스팅 설정은 안 했다. 호스팅 설정을 하게 되면 Firebase에서 배포도 해주는 것 같지만, 배포는 Vercel로 하는 게 간편해 보여서 이 부분은 생략했다.
(4) 딸깍
다음으로 Firebase SDK 추가에 대한 안내가 나오고, 개발중인 앱으로 가서 firebase를 설치하고, 아래의 코드를 작성해주면 Firebase를 사용할 준비가 끝난다. 이 코드는 나중에 프로젝트 설정에 들어가서 다시 확인할 수 있다. 회색으로 블러 처리된 부분은 환경 변수로 따로 설정해두고, 해당 환경 변수를 사용해주는 것이 좋다.
import { initializeApp } from "firebase/app";
const firebaseConfig = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
databaseURL: process.env.NEXT_PUBLIC_FIREBASE_DATABASE_URL,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,
};
const app = initializeApp(firebaseConfig);
그리고 나는 Realtime Database를 이용할 것이기 때문에
(5) 딸깍
Realtime Database를 선택해서 들어갔고,
(6) 딸깍
들어가면 또 배너에 친절하게 데이터베이스 만들기라는 버튼이 있다. 클릭하면 데이터베이스 설정 모달이 뜬다.
(7) 딸깍
(8) 딸깍
데이터베이스 위치와 보안 규칙을 설정하게 된다. 아시아권은 싱가포르 밖에 없었고, 아무래도 가까우면 좋지 않을까 싶어서 싱가포르를 선택했다. 보안 규칙은 크게 중요하지 않았기 때문에 테스트 모드를 선택했다.
이렇게 하고 나면, 정말로 Firebase의 Realtime Database를 사용하기 위한 설정은 끝이 난다. 생략된 부분까지 포함해서 도합 20번 내외의 '딸깍'으로 설정이 완료가 됐다.
"UX가 모든 사용자를 고려하지 않는다면, Some User Experience로 불려야 하지 않을까? 또는 SUX로?(Sucks와 동일한 발음)" -Billy Gregory
이 앱을 개발하면서 무엇보다 중요시 했던 것은 사용자의 편의성이었다. 우리가 노션에 질문리스트를 작성하고, GPT에게 랜덤으로 문제를 뽑아달라고 요청하고, 또 GPT에게 해당 문제에 스터디원을 적절하게 배정해달라고 요청하고, 면접을 진행하면서 이슈 사항을 노션에 기록하고, 이미 진행한 질문들을 따로 분류하는 일련의 과정들을 보다 편하게 진행할 수 있어야 했다.
그래서 유저의 입력을 받아 진행하는 기능을 최소화 했고, 최대한 버튼 클릭만으로 대부분의 진행이 가능하도록 구현했다.
질문 목록 페이지는 굉장히 단순하게 구현했다. 질문 리스트를 나열했고, 셀렉션을 이용해서 원하는 카테고리의 질문만 볼 수 있도록 구현했다. 각각의 리스트는 완료 상태 체크 표시가 가능하고 체크를 표시하거나 해제할 때마다 즉시 DB 데이터도 업데이트 되도록 구현했다. 또한 질문의 추가, 삭제가 가능하도록 구현했다.
질문 추가 모달의 경우 각각의 질문을 줄바꿈으로 구분하여 여러 개를 동시에 입력할 수도 있게 구현했다.
그리고 면접 시작 버튼을 누르면, 면접 진행 페이지로 이동하게 되는데, 면접 질문 목록에서 체크 표시가 되지 않은 질문들 중에 기본 질문 1개와 기술 질문 5개를 랜덤으로 뽑아준다. 이렇게 질문을 뽑아줌과 동시에 이 면접은 면접 목록에 자동으로 저장된다. 이렇게 생성된 면접들은 우측 상단에 마이페이지 아이콘을 클릭으로 연결되는 면접 목록 페이지에서 확인할 수 있다. 다음 단계로 넘어가기 위해서는 면접자 입력하기 버튼을 누르면 된다.
이렇게 한 명씩 입력할 수 있고, 일일이 면접자 추가 버튼을 누르게 하는 것도 불편할 것 같아서 엔터키로 입력할 수 있도록 구현했다. 입력된 면접자들로 면접을 진행하기 위해서는 면접 진행하기 버튼을 누르면 된다.
이런식으로 각각의 질문마다 배정된 면접자를 보여준다. 기본 질문의 경우 모두가 답하기 때문에 면접자 전원이 배정되고, 기술 질문의 경우 한 질문당 2명씩 배정이 된다. 또한 해당 질문에 대한 모의 면접을 진행하던 중 나온 꼬리 질문이나 추가적인 토론 내용, 그리고 참고할만한 내용 같은 것들을 댓글 형태로 남길 수 있도록 구현했다.
마지막 질문에서는 다음 질문 버튼 대신 면접 완료 버튼이 있고, 면접 완료 버튼을 누르면 면접을 종료할 수 있다.
각각의 면접들은 면접질문을 고르는 순간 자동으로 면접 목록에 저장이 된다. 면접을 저장할 때 제목 등을 입력하는 것도 굉장히 불편하다는 느낌이 들어서 자동으로 저장되도록 했다. 제목을 따로 입력받지 않기 때문에 타임스탬프를 이용해서 제목을 만들어 주고, 해당 면접에 포함된 질문의 내용을 일부 확인할 수 있도록 구현했다.
완료된 면접의 경우 면접 목록에서 완료 뱃지로 표시되며, 아직 완료되지 않은 면접은 진행중 뱃지로 표시된다. 완료된 면접도 해당 카드를 클릭해서 진행되었던 내용을 다시금 확인할 수 있다. 또한 면접 완료시에 완료된 면접에 포함되어 있던 질문들은 자동으로 완료 상태로 체크가 되도록 구현해두었기 때문에 다음 질문 생성 때는 포함되지 않는다.
단순히 앱의 목적 자체가 스터디원들끼리 같이 면접을 진행하고 진행 내용을 기록하는 용도였기 때문에 딱히 로그인 기능이 필요하지도 않았고, 단순한 CRUD를 구현하는 것이 전부인 앱이었다. 하지만, 그럼에도 마냥 순탄하지만은 않았던 것 같다. 개발을 진행하며 겪었던 몇 가지 이슈를 공유해보고자 한다.
위에는 마치 Firebase 홈페이지를 들어가면 누구나 간단히 프로젝트를 진행할 수 있을 것 처럼 써놓긴 했지만, 나름의 시행착오를 많이 겪었다. 처음 Firebase 홈페이지를 들어갔을 땐 뭐부터 해야할지 난감하기도 했고, Storage를 써야 되는건지, Realtime Database를 써야 되는건지, 호스팅은 뭐고, Authentication은 필수인건지... 등등 굉장히 많이 헤맸다. 하지만 늘 느끼는 것인데, 헤맬 때 블로그나 유튜브를 참고하는 것도 좋지만 가장 정확한 정보는 공식문서에 있다. Firebase도 마찬가지로 Firebase 공식문서에 들어가보면 Firebase 기초 사용법에 대한 안내가 굉장히 자세하게 나와있고, 이번에 느낀거지만 docs 문서임에도 번역이 굉장히 깔끔하고 폰트나 텍스트 크기 등 디자인 자체가 보기 어렵지 않게 잘 정리되어 있었다.
GPT와 Claude의 도움이 없었다면 이 모든 것을 하루만에 구현하기란 어려웠을 것이다. 물론 위의 저 투박한 버튼을 만들어준 것도 이녀석들이다.(제가 안 그랬어요. 얘네가 그랬어요) 구체적으로 어떤 모양의 아이콘을 지정해주지 않았더니 저런식으로 만들어줬다. 아직까지 알잘딱깔센의 수준에는 못 미치는듯 하다. 그래도 구체적으로 어떤 기능을 가진 어떤 형태의 UI를 구현한다고 했을 때 처음 답변으로 던져주는 코드의 퀄리티가 1년 전쯤에 비해서 많이 좋아진 것 같다고 느꼈다. 대부분 동작하는데 큰 문제는 없었고 정말 이상한 부분만 고쳤던 것 같다.
문제는 면접 진행 단계에서 질문마다 면접자를 배정하는 로직에 있었다. 면접 질문은 총 6개, 그 중 1개의 기본 질문에는 모든 면접자가 배정되고, 나머지 5개의 기술 질문에 면접자들이 고르게 배정되어야 했다. '고르게'라는 의미는 각 질문당 배정받은 면접자 수도 균일하면서 각 면접자당 배정된 질문수도 균일해야 했다. 딱히 떠오르는 효과적인 알고리즘이 없었기 때문에 AI에게 전적으로 부탁했다. 여기서 문제가 터지기 시작했다. 처음에 답변으로 받았던 로직이 너무 복잡했고, 로직을 이해하기도 어려웠다. 그래서 코드를 수정하는 것을 포기하고 동작을 확인 해보고 안 되는 부분을 다시 AI에게 맡기기를 반복했다. 그렇게 이해하기를 포기하고 AI의 톱니바퀴처럼 복사, 붙여넣기만 하던 나에게 경종을 울린 것은 유튜브였다. 유튜브에 BGM처럼 틀어놨던 다섯시간짜리 브레이킹베드 전시즌 몰아보기가 끝이 났다.
그렇게 정신을 다시 가다듬고, AI가 작성해 준 로직을 깔끔하게 지워버렸다. AI가 명확하게 처리를 못 했던 이유는 아마도 '면접자에게 2개의 기술 질문을 배정할지', '기술 질문에 2명의 면접자를 배정할지'가 모호했기 때문인 것 같다. 질문을 던지는 나로서도 어떻게 해야할지 결정을 못 내린 상태였기 때문에 AI는 이 두 가지 방식이 혼재하는 코드를 작성해줬다. 그리고 또 다른 이유는 AI가 최대한 효율적인 코드를 짜려고 했던 것 같다. 사실 면접자는 많아봐야 10명도 안될 것이다. 질문도 6개가 끝이고, 진행방식을 바꾼다고 하더라도 10개를 넘지 않을 것이다. 그렇기 때문에 알고리즘 효율이 크게 중요하지 않은 문제임에도 AI는 항상 숫자가 큰 경우까지 고려하려고 했던 것 같다.
우선 면접 진행을 질문 순서대로 진행할 것이기 때문에 데이터 구조에서 questions 데이터에 interviewees 배열을 가지고 있는 것이 좀 더 구현에 유리할 것으로 생각해서 질문에 면접자를 배정하는 방식을 채택했다. 그리고 로직은 단순하게 백트래킹을 이용해서 전체 면접자 중에서 2명을 뽑을 수 있는 조합을 전부 탐색해서 2명 조합 배열을 만들었다. 사실 백트래킹으로 배열을 만들게 되면 순서를 연속적으로 탐색하게 되기 때문에 결국 배정되는 방식도 면접자 순서대로 배정하게 돼서 면접자 입력 순서에 따라 질문 예측이 가능해지는 문제가 있다. 따라서 배정하기 전에 2명 조합 배열 자체를 섞어주는 셔플 함수를 만들어 랜덤성을 추가했다. 여기까지 하면 질문당 2명씩 배정하는 문제는 해결이 됐지만 각각의 면접자가 고르게 질문을 배정받는다는 보장은 없다. 그래서 이 부분을 해결하기 위해 2명을 배정하기 전에 2명의 조합 배열에서 2명이 이전까지 배정 받은 문제 수의 합이 최솟값인 조합만 필터링 해서 걸러내는 작업을 추가했다. 이렇게 면접자 배정 로직이 완성되었다.
내가 이깄다... 내가 이깄쓰!!
이 앱은 포트폴리오에 추가할 것도 아니고, 정말 면접 스터디만을 위해 만든, 내가 진짜 사용하기 위해 만든 첫 서비스이다! 그 부분이 감회가 굉장히 색다르고, 프론트엔드부터 백엔드까지 혼자서(AI의 도움은 받았지만) 개발하고 배포까지 했다는 점에서 굉장히 뿌듯한 프로젝트다.