본문 바로가기

기타 프로젝트

[학식킹] 유학생을 위한 학식 메뉴 알리미 제작기

 

타지에서 배고프면 서럽잖아요

학식킹은 유학생을 위한 학식 메뉴 알리미이다. 타지에 혼자 나와 안 그래도 서러운 일 많을 유학생들이 끼니라도 잘 챙겨 먹고 다녔음 좋겠어서 만들게 됐다. 모순적이게도 이 앱을 개발할 때 나는 다이어트를 하는 중이었다. 우리 학교 학식 메뉴가 이렇게 잘 나오는지는 처음 알았다. 뚝배기돼지고기김치찌개, 떡볶이세트... 닭가슴살을 우물우물 씹으면서 '치킨마요덮밥'을 치는 내 모습이 좀 민망했다.

개발하는 이 주 간 밤을 정말 많이 샜다. 클라우드 기술이 아직 익숙지 않은 상태에서 기획, 디자인, 프론트엔드, 백엔드에 배포까지 한정된 시간 안에 끝내야 하다 보니까 시간의 밀도를 높이는 수밖에는 없었다. 다크서클은 늘었지만 포기하고 싶진 않았다. 매일 고민하고 해결하는 과정 안에서 눈에 보일 정도로 부쩍 실력이 성장한 것도 있었고, 무엇보다도 내가 직접 만든 앱을 많은 사람들 앞에서 선보일 수 있다는 것 자체가 너무나도 가슴 뛰는 일이어서 그랬다. 딱 오늘만 더 해보자, 진짜 오늘까지만 더 하자... 하다 보니 2주가 지났다. 대회 당일 아침에 어플이 완성됐다.

 

학식왕 김인하에서 학식킹으로

감사한 기회를 만나 AWS Rookies Championship에 초대 받았다. 해당 대회는 1년동안 AWS Cloud 관련 해커톤 등을 진행한 팀 중 우수한 성적을 거둔 팀들이 모여 경연을 펼치는 일종의 왕중왕전이라고 했다. 처음에는 올해 초 겨울방학에 개발했던 해커네컷을 주제로 나가려고 했었지만 그보다는 현재 개발하고 있는 학식왕 김인하를 고도화시키는 방향을 선택했다. 해커네컷은 이미 어느정도 완성되었지만 학식왕 김인하는 slack을 통해 오늘의 메뉴를 알려 주는 간단한 기능만 구현되어 있어서 기술적으로나 마케팅적으로 더욱 고도화시킬 여지가 많이 남아있을 것 같다고 생각되었기 때문이다.

사실 우리의 아이디어 뱅크 김병준 이사님께서 일찍이 학식왕 김인하를 보시고는 '유학생'이라는 키워드를 던져 주셨었다. 또 여러가지 기술적인 조언도 제공해 주셨었는데, 해당 부분을 중심으로 전체적인 업그레이드를 해 보고 싶었다.

이름도 바꿨다. 학식왕 김인하는 인하대학교에 한정되어 있는 구조이지만 좀 더 넓은 영역을 아우르고 싶어 학식킹이라는 이름을 지었다.

 

유학생을 위한 학식 메뉴 알리미, 학식킹

1. 종교적, 문화적 이유로 학생식당을 이용하기 어려운 유학생들을 위해 소고기, 돼지고기, 닭고기, 새우, 계란 포함 여부를 알려줌.

2. 다국어(한국어/영어)를 지원함.

3. 아직 한식이 낯설 외국인들을 위해 메뉴 이름 뿐만이 아니라 해당 메뉴에 대한 설명도 함께 제공함.

4. 기존의 Slack 기반 App을 벗어나 자체적인 UI를 제작함.

5. AWS에서 제공하는 Lambda, Dynamo DB, Event bridge, API gateway, S3 등의 서비스를 이용해 Serverless로 제작함.

 

학식킹 UI

 

 

학식킹 구조도

구조도에서도 알 수 있듯 학식킹은 모두 AWS의 Cloud 서비스를 이용하여 제작되었다. 감사하게도 AWS 측에서 Free Credit을 지원받았기 때문에 가능한 일이었다. 덕분에 AWS의 다양한 서비스들을 원없이 써 봤다. 이제 와서 생각하는 거지만, 아마도 AWS credit을 제공받지 못했다면 이렇게 다양한 시도를 해 볼 엄두도 못 냈을 뿐더러 2주라는 짧은 시간 내에 Front-end를 포함해 서버 구축까지 구현하지도 못했을 것이다. 늘 새로운 기회를 제공해 주시는 AWS의 김병준 이사님과 FiveWorks의 박진성 팀장님께 그랜절을 올리고 싶다.

 


기능 구현

1. 학식 메뉴 파싱(feat. Tabula-py)

학생식당, 교직원식당, 생활관식당 사이트에서 각각 메뉴를 파싱해 온다. 이 부분같은 경우에는 이미 학식왕 김인하를 제작하면서 완성해 두었던 기능이다. 해당 부분은 미리 블로그에 포스팅해 두었다.

https://eemune.tistory.com/54

 

[학식왕 김인하] 인하대학교 학식 메뉴 알리미 제작기(feat. AWS lambda, PDF 파일 파싱)

개발 배경 인하대학교에는 학생식당, 교직원식당, 그리고 기숙사식당이 있다. 각 식당은 매일 서로 다른 메뉴를 제공하는데, 어떤 식당에서 학식을 먹을지 결정하려면 학생식당/교직원식당/기

eemune.tistory.com

문제는 파싱한 정보를 Database에 저장하는 단계였다. 내가 선택한 서비스는 DynamoDB였다. dynamoDB는 대표적인 nosql 중 하나인데, 학식킹에서 사용하는 대부분의 기능들은 key를 사용해 db로부터 read하는 기능이었기 때문에 nosql 방식이 적절하다고 생각했다.

지금까지는 관계형 database만 사용해 봤기 때문에 nosql 방식이 무척 생소하고 어려웠다. global index는 뭐고, secondary index는 뭐고... 공부를 해봐도 잘 모르겠어서 처음에는 하나의 element를 찾아오기 위해 table 전체를 scan하기도 했었다. 그러다 보니 페이지 하나를 불러오는 데에 10초 이상이 걸리는 광기의 웹사이트가 완성됐다. 이를 고치려고 공식 사이트와 stackoverflow를 사막의 오아시스처럼 해멨다. 몇 시간 만에 알게 된 정보인데, dynamoDB에서는 primary key로는 '같다' operation만 할 수 있고 sort key로는 '크다/작다' 연산만 할 수 있다고 한다. primary key가 아닌 다른 column을 기준으로 element를 찾기 위해서는 보조 인덱스를 설정해 주면 된다. 이 부분을 몰라서 하루를 몽땅 dynamoDB에만 갈아 넣었었다.

 

2. 음식 설명을 자동으로 추가하는 기능 구현(Feat. ChatGPT)

부끄러운 얘기지만, 사실 남이 만든 API를 따와서 쓰는 경험은 처음이었다. '드디어 나두 개발자...?' 하면서 API를 호출하는 방법을 찾았었다. Authorization이 필요한 것 말고는 직접 API를 만들 때와 다를 만한 부분이 없어서 나름 수월하게 진행됐다. 신경 쓸 부분이 있다면 token을 env file로 관리해 주는 일이었는데, 이것도 난이도가 높진 않았던 것 같다.

사실 ChatGPT를 사용했을 때 정확도가 그리 높진 않았다. '김치찌개'에 대한 설명을 request하면 '상큼하고 달콤한 맛이 일품입니다' 라는 식의 설명을 내놓는 식이었다. 반면 clova studio를 이용했을 때는 상당히 높은 정확도를 보여 줬다. 하지만 clova studio는 개인이 아니라 기업에게만 API를 제공하기 때문에 어쩔 수 없이 ChatGPT를 이용했다.

3. 특정 재료 포함 여부를 자동으로 추론하는 기능 구현(Feat. 만개의 레시피)

핵심 타겟이 유학생이니만큼 비건이나 무슬림 학생들도 커버할 필요가 있었다. 이 때 음식 이름만 가지고 해당 음식에 들어가 있는 메뉴를 추론하는 방법에 대해 고민했다. ChatGPT나 식약청 API를 이용하려고 시도했지만 ChatGPT는 정확도가 아주 떨어지고 식약청 API는 음식의 종류가 한정되어있다는 부분에서 적합하지 못했다.

'달콤쌉쌀 돌미나리무침'처럼 자유로운 음식 이름으로 메뉴를 가져올 수 있는 방법이 필요했다. 광범위한 메뉴를 커버하려면 커뮤니티가 적합하다고 생각했고, 결국 선택한 건 '만개의 레시피' 사이트를 크롤링하는 거였다.

만개의 레시피 사이트를 beautiful soup으로 파싱하여 가장 정확도가 높은 메뉴의 레시피를 파싱해서 DB에 저장하도록 했다. 

 

3. 다국어 지원 기능(Feat. DeepL, i18next)

DeepL API를 이용해서 메뉴 이름과 설명을 번역했다. 번역이 상당히 정확해서 수월했다. 어려웠던 부분은 react 라이브러리를 이용하여 front-end를 구성할 때 다국어 기능을 지원하는 방법이었다. vanilla JS라면 그냥 주소를 나눠서 영어/한글 사이트를 따로 만들어 놨을 텐데 react를 이용할 때는 어떻게 할 수 있을지를 잘 몰라서 해멨던 기억이 난다. 한글로 다 만들어 둔 사이트를 처음부터 다시 영어로 파야 하나 하는 생각에 아찔했다.

찾아 보니 React에서 다국어 기능을 지원하는 라이브러리가 있다고 했다. i18next라는 모듈이다. 사용법만 익히면 간단했다.

 


Serverless 배포

1. 웹사이트를 이용한 UI 구현(Feat. React)

React를 이용해 웹사이트 전반을 구성했다. 좀 더 '어플같은' 느낌을 주고 싶어서 선택한 라이브러리였다. 저번에 개발한 향수 주문제작 플랫폼에 이어 두 번째 React 프로젝트였는데, 아직까지는 여러모로 불편한 부분이 많은 것 같다. 이번에 Front-end 개발에 있어서도 난관이 정말 많았다. 특히 styled-component를 이용해서 디자인을 먹이는 과정에서 애를 많이 먹었다. 기존에는 SCSS 문법을 이용해서 디자인에 관련한 모든 역사를... 이뤄 냈었는데, 이번 프로젝트는 styled-component 사용을 새로 도전해 봤다. typescript와 섞어 사용하다 보니 생각지도 못했던 오류들이 여기저기서 튀어나왔다. 특히 많이 본 오류는 'implicitly any type이 될 수 있다'는 오류와 'undefined로부터 reference하려고 시도하는 중이다'라는 오류였다. 주먹구구식으로 개발하는 것보다는 나은가 싶다가도 JS였으면 아무 문제 없었을 코드로 6시간동안 고생할 때면 싹 밀어 버리고 JS로 다시 개발하고 싶은 마음이 굴뚝같았다. 익숙해져야겠지...

 

2. Lambda를 이용한 API 구현(Feat. Lambda, DynamoDB, API gateway)

기존 학식왕 김인하에서도 lambda를 사용했었다. 학식 data를 crawling하고 그 data를  slack으로 send하는 기능을 위해 lamba를 이용했다. 이번엔 lambda를 이용해서 API를 구현하려고 노력했다.

여러가지 문제가 있었다. 우선 가장 큰 문제는 library의 크기였다. lamda는 최대 250mb의 layer를 지원하는데, tabula module같은 경우에는 JAVA를 함께 설치해야 하기 때문에 그 자체로 용량이 250mb를 초과했다. 이 문제를 해결하기 위해 온갖 똥꼬쇼를 다 했는데... 결론은 docker container를 이용하는 것밖에는 방법이 없다는 거였다. lambda with docker container는 10GB까지 지원한다고 한다.

또 API gateway를 연결하는 데에도 시간을 많이 쏟았다. 로컬에선 진작에 제대로 동작하는 코드를 쥐고 배포하기 위해서만 이틀 밤을 꼬박 샜다. CORS 관련 오류가 제일 많았다. 알게 된 정보는 다음과 같다. 

1. lambda url과 다이렉트로 소통하기 위해서는 GET 방식만 허용된다. POST 방식을 사용하고 싶다면 API gateway를 이용해야 한다.

2. 기본적으로 fetch의 no-cors option을 사용하면 503 error가 뜬다.

3. REST 통신을 사용하려면 반드시 response에 'status:200'이 포함되어야 한다. 안 그러면 오류가 난다.

4. react를 사용할 때 package.json에서 proxy를 설정하여 cors를 우회하는 것은 development 환경에서만 가능하다. depoly하려면 server(lambda)에서 header에 cors 관련 허용을 해 주어야 한다.

 

3. React App 배포(Feat. Amazone S3, Cloud Front)

lambda를 이용한 API 구현에서 워낙 고생을 했어서 시작부터 긴장하고 들어갔었다. 한 사흘 밤 샐 생각으로 착수했는데 2시간도 안 걸려서 오히려 허무했던 기억이 난다. build한 react app을 upload하는 건 ubuntu 환경에서 aws cli를 이용해 console로 deploy했고, 기타 설정은 AWS 사이트의 GUI를 이용했다. HTTP만으로도 괜찮다면 S3만으로 배포를 마무리하고 HTTPS가 필요하다면 Cloud Front를 연결해 주면 된다.

하나 주의할 점은 deploy를 해도 update 사항이 웹사이트에 바로 반영이 안 될 수 있다는 것이다. Cloud Front의 경우 24시간동안 기존 static web 정보를 자동으로 cache한다는 특징이 있다. 이 때는 기존의 Cache정보를 직접 삭제해 주면 정상적으로 update 된다.

 

 


후기

2023 AWS Rookies Championship 수상

따로 글을 포스팅할 예정이지만, 학식킹이 AWS Rookies Championship에서 수상했다. 대회에 나갔을 때 내 앞 순서였던 팀들의 엄청난 발표들을 듣고 무척 기가 죽었다. 내 차례가 왔을 때는 심장소리가 너무 커서 내 귀에 들릴 정도였다. 긴장한 상태로 발표를 시작해서 그랬는지 내가 무슨 말을 하는지도 모르고 발표했다. 김병준 이사님이 교회 권사님같았다고 했다.

결과는 의외로 수상이었다. 너무 떨어서 수상은 못 할 거라고 생각했는데, 무려 2등이었다! 쟁쟁한 다른 팀들을 두고 학식킹이 수상했다니 얼떨떨한 기분이었다. 사실 그동안 아무리 열심히 해도 눈에 보이는 성과가 없어서 힘들던 시즌이었는데, 이렇게 내가 만든 앱이 가치있다고 인정받으니 눈물났다. 앞으로도 이 날을 잊지 못할 것 같다. 

이 모든 영광을 밤낮으로 일하시는 김병준 이사님과 박진성 팀장님께 돌리고 싶다. 사실 '유학생을 위한 학식'이라는 키워드도 김병준 이사님께서 제공해 주신 거다. 늘,,, 늘 감사한 마음 뿐이다.

 

후속 계획

현재는 인하대학교 학생식당 메뉴만 지원하고 있지만 더 많은 대학의 학생식당을 지원하는 게 목표다. 또 유학생들의 커뮤니티가 부족한데, 학식킹 앱 차원에서 커뮤니티 기능도 제공하고 싶다. 아직은 실력이 부족하지만 여러모로 배워서 전국의 유학생들을 만나는 그날까지 달려보려고 한다!