2022년 5월 3일 화요일 팀 편성부터 2022년 5월 31일 화요일 발표까지, 4주하고도 하루의 BEB 3기의 마지막 프로젝트가 끝이났다.
마지막 프로젝트는 각자 자유롭게 아이디어를 제안하고 관심이 가는 아이디어에 참여해 팀이 이루어졌다. 나는 아이디어 발표 전날인 5월 2일, 운동을 하다 떠오른 온라인 저지를 적어냈고 두 분이 참여하겠다 하셔서 내가 팀장이 되어 셋이서 프로젝트를 진행하게 되었다.
처음 프로젝트 아이디어를 제안했을 때는 구체화되지 않은 단순한 형태였다.
이 날은 약 3시간 정도 회의를 하며 아이디어를 조금 더 구체화시켰다. 우리 사이트를 이용하는 유저들의 실력 향상과 유저들에게 알맞은 보상, 인센티브를 지급한다는 것을 목적으로 이런저런 아이디어를 이야기했다. 또 전체적인 구조를 그려보며 역할을 나누었다.
다행히 각자 맡아보고 싶은 영역이 명확해서 깔끔하게 나눌 수 있었다. 우리 서비스는 클라이언트, 채점 서버, 메인 서버, 데이터베이스, 컨트랙트로 구성하기로 했고 나는 채점 서버를 구현한 뒤에 메인 서버 작업으로 합류하기로 했다.
우리는 우선 가장 필수적인 기능들을 구현하기로 했다. 이외의 컨텐츠나 토큰 이코노미 등은 꾸준히 회의를 하며 정해가기로 했다. 필수 기능은 문제 출제, 문제 풀이, 채점, 마이페이지(지갑 연결 등)로 정리되었고 12일까지 이 기능들을 구현하기로 했다.
회의를 마치고 그날 저녁부터 바로 채점 서버 구현을 시작했다. 오픈 소스를 사용할 수도 있었지만 한 번 직접 만들어보고 싶었다. 그래서 일단은 자바스크립트 코드 채점을 목표로 하고 떠오르는 방법들을 하나하나 시도해보았다.
처음 아이디어는 request로 들어온 코드로 js 파일을 쓰고 호출해서 사용하는 것이었는데 테스트를 해보니 코드가 바뀌어 요청이 들어와도 가장 첫 요청에 들어온 코드로 쓰여진 파일로만 계속 결과가 나왔다. 파일의 내용이 바뀌어도 결과 값은 항상 처음 들어온 코드가 실행되고 나온 값이었다.
이런저런 검색 도중 import가 문제인가 싶어 동적으로 import하기 위해 import()를 사용했지만 결과는 같았다. 파일을 생성하고 결과를 출력한 뒤에 바로 삭제를 해보았지만 이 역시 처음 생성된 파일을 기억하고 있는 것인지 같은 결과만 나왔다. 파일을 읽어오는 부분을 함수화도 시켜보고 사용된 모든 변수를 null로 초기화도 시켜봤지만 모두 실패했다.
그렇게 4일이 되었고 그냥 오픈소스나 API를 사용할까하는 생각으로 검색을 하다가 repl it이라는 온라인 코딩 사이트의 API를 발견했지만 현재는 사라진 듯 했다.. 다시 한 번 채점 서버 구현을 시도하다가 생성하는 파일의 이름이 바뀌었을 때 원하는 결과가 나오는 것을 확인할 수 있었다. 그래서 Date() 함수를 사용해 호출되는 시점의 시간을 파일 이름으로 정해주어 해결할 수 있었다.
이후에는 테스트 케이스를 차례로 넣으며 결과를 얻어내기 위해 고민을 했는데 최대한 단순하게 해결하고 싶어서 메인 함수는 항상 이름이 solution 이도록 했다. 마지막 부분에 export 만 추가해 solution 함수를 호출하고 테스트 케이스를 넣어서 실행시키는 방식으로 해결하기 위해서 였다. 작동이 잘 되는 것을 확인하고서는 채점 서버는 메인 서버로부터 유저의 코드와 해당 문제의 테스트 케이스를 받아 채점 결과를 반환해줄 수 있도록 했다.
6일 회의에서 함께 백엔드를 맡은 팀원분이 타입스크립트로 구현해보는 것이 어떻겠냐는 제안을 받았고, 사실 나도 어느정도 그것에 대해 생각하고 있었기 때문에 바로 승낙을 했다. 그래서 7일, 8일에는 타입스크립트를 공부하며 채점 서버를 타입스크립트로 바꾸는 작업을 했다. 어설프지만 어떻게든 돌아가도록 만들기는 했다..
타입스크립트로 변환하는 과정에서 js 파일을 생성하고 가져오는 부분에서 package.json에 "type":"module"을 쓰면 에러가 나서 import를 require로 바꾸어야했다.
또 6일 회의에서는 우리 서비스에 대한 컨셉이 잡혔다. 어쩌다보니 콜로세움이라는 얘기가 나오게 되었고, 초기 아이디어였던 그룹 생성, 대회 주최가 아닌 출제되는 문제 하나하나에 토큰을 걸고 경쟁하는 방식의 문제 풀이가 우리의 메인 컨텐츠가 되었다. 하지만 고수들의 독식이나 난이도 문제, 문제 자체의 오류 등 넘어야할 산이 많다는 것 또한 회의에서 계속 언급되었다.
아직 채점 서버에서 유저의 코드가 무한 루프가 돌거나 실행 시간이 너무 오래걸릴 때의 처리를 하지 못하는 문제점을 해결하지는 못했지만 아주 단순하게 작동을 하기는 하니 (사실 해결할 방법이 떠오르지 않았던 것도 있다) 메인 서버로 넘어가 작업을 시작했다. 메인 기능인 문제 출제, 풀이 제출 API를 우선 구현하기로 했고 클라이언트 담당 팀원분과 이야기하며 API 문서를 작성했다.
회의에서는 컨텐츠에 대해 재미있는 아이디어도 더 나오고 (문제가 랜덤으로 출제되고 연속으로 많이 맞출수록 더 큰 보상을 획득하는 등의 컨텐츠) 부정 행위나, 오류가 있는 문제의 수정 등에 대한 논의가 이루어졌다. 사실 이런 부분들은 직접 서비스를 해본적이 없어 막연하고 마땅한 해결책을 내놓기가 어려웠다. 끊임없이 함께 고민해보기로 하며 우선은 기능 구현에 집중하기로 했다. 다 고민해놔도 기능 구현이 되지 않으면 소용이 없으니까.
문제 리스트 요청, 문제 상세 페이지 요청 API를 이어서 구현해 12일에는 아주 기본적인 틀을 가진 사이트가 만들어졌다. 클라이언트와 함께 연결해 테스트를 해보며 몇몇 버그를 잡고 이후 일정에 대해 회의를 진행했다.
우리 서비스의 토큰이 언제 생성되고 언제 소비되는지를 다시 한 번 정리했고 문제 풀이 사이트이기 때문에 토큰 가치의 변동을 최소화할 수 있는 적절한 토큰 발급과 소모, 소각이 필요할 것이라는 이야기를 나누었다. 다만 이것 역시 우리가 설계하는 것이 쉽지 않았다. 최대한 토큰을 소비할 수 있는 컨텐츠를 많이 준비해야할 것이고 과한 공급이 되지 않도록 적정량을 지급하도록 해야하지만 서비스를 직접 해보지 않아서인지 감이 잘 잡히지 않았다.
그래서 이번에도 역시 기능 구현에 초점을 맞추기로 했다. 하지만 고민은 멈추지 않았다.
콜로세움이라는 컨텐츠가 이미 정해진 상황에서 콜로세움에서 소비된 문제를 어떻게 할 것인가에 대한 논의가 이루어졌다. 소비된 문제는 연습문제로 전환할 수 있도록 하고 전환할 때 NFT화하면서 유저들이 연습문제를 풀이할 때 마다 출제자에게도 보상이 갈 수 있도록 구상을 했다.
콜로세움 문제는 문제에 진입할 때 토큰을 지불할 수 있도록 해야해고, 이를 위한 조금 복잡한 검증 과정 로직이 필요했다.
17일 회의에서는 콜로세움 문제에 재도전은 불가능하도록 하고 제한시간이 지나도 우승자가 아직 나오지 않았다면 풀이는 계속 가능하며, 보상은 없도록 하기로 했다. 18일까지는 계속 위 API를 구현하는데 힘썼다.
19일에는 컨트랙트가 준비되었다 해서 구현된 API들을 전체적으로 살펴보며 수정할 준비를 했다. 여기서 콜로세움과 연습문제를 분리시키는 것이 좋겠다는 생각이 들어 컨트랙트 함수를 집어넣으며 따로 분리시키는 작업을 동시에 진행하였다. 추가적으로 NFT, 연습문제 전환 요청 API와 피드백 API가 필요하다는 얘기가 나왔고 앞선 작업이 완료되는대로 구현하기로 했다. 전환이나 피드백은 너무 간단한 작업이어서 순식간에 완료할 수 있었다.
20일에는 컨트랙트 담당 팀원분이 토큰 지불 시 수수료 대납 함수 구현에 성공했다고 해서 콜로세움 문제 상세 페이지 요청 API 로직을 대폭 수정할 필요가 있었다. 서버에서 수수료 대납을 하겠다는 내용이 담긴 사인 요청을 클라이언트에 보내면 유저가 이에 사인을 해 다시 전송하고, 사인 객체를 받아 이를 사용해 토큰을 전송하면서 수수료를 대납하는 구조였다.
21일에는 콜로세움 풀이 제출 API, NFT 전환 요청 API, 연습 문제 전환 요청 API, 피드백 API가 남아있는 상황이었는데 아직 관련된 컨트랙트 함수가 준비되지 않아 이번에도 있다치고~ 구현을 시작했다. 또 아직 채점 서버 시간 초과 관련 문제를 해결하지 못했기 때문에 이 부분 역시 계속해서 고민하고 있었다.
22일에는 조금 큰 변경사항이 있었다. 연습 문제를 풀이할 때마다 보상으로 토큰이 왔다갔다하는 것이 수수료 부분에서 부담이 커서 DB에만 기록하는 중간 재화를 추가하기로 했다. 때문에 이를 기록하는 로그가 또 필요했고 연습 문제 관련된 디비 스키마와 API에 변화가 많이 생겼다. 또한 이 중간 재화를 토큰으로 환전해주는 API가 필요했기 때문에 이에 대한 구상도 필요해진 상황이었다. 연습 문제 출제자에 대한 보상 역시 같은 방식으로 처리하기로 했다.
하나하나 구현해가는 과정에서 NFT 전환 요청과 연습 문제 전환 요청 API는 하나로 합쳐서 처리하기로 해서 수정이 있었다. 환전 요청도 컨트랙트 함수가 아직이라 있다고 생각하고 구현했다.
24일에는 컨트랙트 함수를 제외한 API 뼈대를 완성하고 컨트랙트 담당 팀원분의 소식을 기다리는 동안 다시 채점 서버의 문제를 해결하기 위해 구글링을 시작했다. 자바스크립트의 label도 사용해보았지만 소득은 없었다. 아무리 찾아봐도 실행되고 있는 함수를 외부에서 죽이는 (작동을 멈추는) 방법을 찾을 수가 없었다. 그러던 와중 문득 학부때 배웠던 싱글, 멀티 스레드, 프로세스 같은 내용들이 떠올랐고 잘은 모르겠지만 이것이 지금 상황을 해결할 실마리가 되지 않을까 싶어 구글링을 시작했다. 생각보다 빨리 너무나 믿음직스러워보이는 Node의 모듈인 Child-Process 라는 것을 발견할 수 있었고 이것을 사용하면 될 것 같다는 강한 확신이 들었다. 하지만 사용법을 알기 위해서는 문서를 읽어볼 필요가 있었고 여기서 꽤 시간이 걸렸다. 이 작업은 다음날 점심즈음까지 이어졌고 마침내 시간 초과 코드까지 걸러낼 수 있는 채점 서버가 만들어졌다. 또한 이것을 활용하면 자바스크립트 뿐만 아니라 다른 언어로도 채점이 가능하도록 확장할 수 있을 것 같았다. 아쉽게도 시간이 없어서 이 부분은 나중에 따로 해볼 계획이다.
26일 회의에서 또 한 번 공정한 경쟁에 대한 이야기가 나오며 콜로세움 시스템에 변화를 주었다. 문제를 출제할 때 문제가 오픈되는 시간을 정할 수 있도록 했고 그 시간이 되었을 때 도전자가 2명 이상이 되지 않는다면 문제를 보류 상태로 바꾸고 참여자에겐 토큰을 환불해주는 형식으로 바꾸었다. 혼자 참여하고 보상을 획득하는 것이 취지에 맞지 않다는 의견과 최소 획득 보상의 확보, 공정성 등을 위한 개편이었다. 여기서 콜로세움 문제 상세 페이지 요청 API를 거의 새로 작성하는 수준의 작업을 수행해야했다. 또한 재출제를 위한 API가 필요해 추가 작업을 해야했다. 놀랍게도 27일 금요일까지 모든 구현과 테스트를 완료할 수 있었다.
주말이었지만 아직 배포가 남아있기에 각자 맡은 부분(클라이언트, 채점 서버, 메인 서버)을 배포해보기로 했다. 나는 채점서버를 배포했는데 한 달 동안의 작업 중 가장 힘든 시간이었다. 처음에는 헤로쿠를 통해 배포해보려 했는데 잘 되지 않아 포기하고 유튜브에서 보았던 AWS Elastic beanstalk 를 통해 배포할 수 있었다. 이 과정에서도 많은 시행착오가 있었지만 여러가지 시도 끝에 https로의 배포까지 겨우 성공할 수 있었다. 이 방법은 너무 고생하며 찾아낸 길이라 나중에 또 써먹을 수 있도록 정리해두었고 또 누군가 써먹을 수 있도록 블로깅도 해볼 계획이다. (제대로 한 것인지는 알 수 없지만..) 내가 먼저 배포에 성공해서 팀원분께도 방법을 공유해서 배포를 도왔는데 잘 되지 않아서 결국 메인 서버까지 내가 배포했다.
분명 배포 직전까지의 테스트에서는 모든게 완벽하다 생각했는데 배포를 하고나니 멀쩡했던 부분들에서 버그가 터져나오기 시작했다. 열심히 버그를 잡으며 발표 준비까지 하느라 정말 바쁜 이틀이었고 그래도 발표 전에는 버그들을 잘 잡아내며 무사히 발표를 마칠 수 있었다.
https://codecolosseum.netlify.app/
한 달 정도의 시간동안 팀원분들과의 마찰도 없었고 꾸준하게 소통하며, 정말 매끄럽게 잘 진행되었다고 생각한다. 우리 서비스에 대한 컨셉이나 토큰 이코노미 등에 대해 회의하는 것도 정말 재미있었고 괜찮은 결과물이 나왔다고 생각한다. 사실 온라인 저지를 구현하는 것에 대해 코드스테이츠 블록체인 과정을 듣기 전, 개발에 대해 아무것도 모를 때에도 한 번 해보고 싶다는 생각을 막연하게만 갖고 있었는데 이를 직접 구현한 것이 정말 기분 좋았다. 그때는 어떻게 만들어야할지 감도 못 잡던 상태였는데 이제는 내가 뭔가 할 수 있다는 생각이 들기도 한다.
아쉬운 점은 구현을 시작하기 전에 컨셉과 데이터 구조들이 좀 더 구체적으로 잡혔으면 좋았을 것 같다는 점이다. 구현 중간중간 새로운 데이터의 추가가 이루어지다보니 많이 지저분해진 느낌도 있고 그런 과정에서 구현이나 수정이 불필요하게 복잡해지는 경우가 생긴 것 같다. 또 함수화, 모듈화가 잘 되지 않은 것 같아 이 부분도 아쉬움이 남는다. 물론 처음 구현하면서부터 그렇게 예쁜 코드를 작성하는 것이 쉽지는 않다는 것은 알고있지만 확실히 버그를 잡거나 하는 과정에서 예쁜 코드의 필요성을 너무나도 절실히 느낄 수 있었다. 함수는 하나의 기능만 하도록 만들자..
남은 일정을 수행하며 마지막 프로젝트를 블록체인 없이 새롭게 구성해서 혼자 구현해볼 계획이다. 콜로세움 컨셉은 제외하고 그룹을 만들거나 하는 등의 기능과 새로운 기술 스택도 공부할 겸, 취준과 함께 말이다. 얼마전까지는 사실 부트캠프가 끝나면 한 두달 정도는 쉬면서 공부하고 할 생각이었는데 막상 이렇게 그 날이 오니 최대한 빠르게 취직해서 일이 하고 싶어졌다. 블록체인 과정을 들었고 여전히 '블록체인이 미래다'라는 생각에는 변함이 없지만 이번 프로젝트를 하며 당장에는 백엔드 포지션이 너무 재미있고 더 잘 맞는다는 느낌이 들어 이 부분에 좀 더 집중해서 공부하고 준비해볼 계획이다.