반응형

https://arrangept.lockcept.kr 개발을 완료하였다. 해당 사이트는 입력된 단어들을 seed 단어를 기준으로 재배열 해주는 사이트이다. 실은 세상에 아무 쓸모도 없는 사이트이다. 그냥 나를 위해서 만들었다. 그래도 개발기 작성은 가치가 있을수도 있으니까, 글을 작성하기로 마음먹었다.

 

gpt가 만들어줌

 

개요

단어를 재정렬하여 보여준다, 라는 의미로 Arrange라는 동사를 채택하였다. 그리고 나의 닉네임인 lockcept을 합쳐서 Arrangept로 서비스 이름을 작명하였다. 게다가 요즘 화제인 gpt와도 이름이 비슷하여 쏙 마음에 들었다.

 

원래 이 사이트의 기능은 나의 로컬에 javascript로 구현 되어 있었다. 게임을 플레이 하기 위해서 어떤 캐릭터를 선택할지 결정이 힘들었던 나에게 캐릭터를 랜덤으로 뽑아주었으면 좋겠다는 니즈가 생겨서 만든 기능이다. 기왕이면 학교에서 선생님이 쓰던 대포쏘는 프로그램처럼 무작정 뽑기보다는, 캐릭터의 이름에 based된 정렬을 했으면 싶었다. 또한 시드 단어에 따라 같은 결과물을 뱉기를 원했다. 그런데 게임 캐릭터들을 시간에 따라 새로 생기기도 하므로 새로운 단어가 등장했을 때 기존 단어들은 순서를 유지하면서 그 사이에 자연스럽게 끼어들었으면 좋겠다고 생각했다. 그래서 생각 해 낸 것이 시드 단어와 특정 단어를 넣으면 0부터 1사이의 값을 뱉는 함수를 정의하는 것이었다.

 

함수

그 함수를 정의하는 과정은 상당히 너드같다. 수학적으로 옳은 방법이 아닐 수도 있지만 내 기분이 좋았으니 그냥 재미로 읽어주길 바란다. input word를 seed word랑 비교할 때에 각 알파벳의 거리를 기반으로 계산했다. 이 때 양 끝단에 있는 알파벳은 차별받을 수 있으므로 circular distance를 사용했다. 두 알파벳의 거리를 택시 거리로 합칠 것인가, 유클리드 거리로 합칠 것인가, 혹은 그 외의 임의의 증가함수로 변환할 것인가를 여러 가지 구현하여 선택할 수 있도록 했다(개인적으로 sin 함수가 예뻐보였다). 근데 문제가 있었는데, input word의 길이가 짧을 수록 유리할 수 있다는 점이었다. 평균을 내는 것이므로 기대값은 같았으나 “거리가 가장 짧은 단어”에는 확률상 짧은 단어가 채택될 가능성이 높았다. 그래서 표본의 평균과 표준편차를 계산해서 z값을 구했다(z로 끝나는 method들임). 그리고 세상의 모든 영어 단어를 정렬해서 결과물을 봤더니 긴 단어가 오히려 유리해지기도 했지만 봐줄만 했다. 재밌었으니 OK다.

 

Flutter

javascript로 로직을 구현한 적이 있었음에도 불구하고, React나 Typescript를 사용하지 않았다. 그 이유는 기존 flutter 사용 경험이 너무나도 좋았기 때문이다. 그러면서도 로컬에서 js 코드를 직접 실행하였을 때에 출력되는 결과와 일치하는 결과물을 출력하는 앱을 만드는 것이 가장 기본적인 목적이었다. 이번 앱을 만들면서 GPT를 적극 활용하였다. gpt 4o의 성능이 너무 발달되어서 5시간 이내로 모든 것을 완성할 수 있었는데, 특히 앱의 기본 틀을 만드는 과정에서 머리 속의 앱 인터페이스를 설명하였더니 멀끔한 형태의 코드를 출력하는 모습을 보고 감탄했다. GPT가 없을 때와 비교하자면 2시간 이상 아낀 것 같다. 특히나 뷰와 관련된 나의 구현 속도는 끔찍하기 때문에 더욱 효과적인 사용이었다.

 

랜덤함수 구현하기

seedString을 sha256 hash로 변환한다음 여기서 32bit int를 가져와서 MT19937의 seed로 쓰는 RNG를 채택했다. 아니, 과거의 내가 했었다. 다행히도 익명의 랜덤함수를 사용하지 않았었다. 심지어는 대부분의 javascript MT19937 라이브러리에 부동소수점 오차와 관련된 잘못된 점이 있었음을 분석한 적이 있었고, 이에 따라 flutter & dart에서도 생기는 문제를 해결하여 같은 결과물을 내는 코드를 구현할 수 있었다. Dart에 대해 깊이 알지는 못하지만 큰 int를 두 개 곱하면 자동으로 실수로 변환하여 곱셈을 하는 언어인 것 같다. 그래서 MT19937 알고리즘의 수행 결과가 일부 달랐다. 이를 해결하기 위하여 BigInt를 라이브러리에서 가져다가 사용하였다.

 

코드를 사진으로 첨부하기

 

거리함수 구현하기

알고리즘은 상단에서 설명하였으니 넘어간다. 기존 typescript 코드를 GPT에 붙여넣었더니 dart로 바로 바꿔줘서 실제 구현하는데에는 1분도 걸리지 않았다. 여러 method를 정의하였으니 이는 dropdown button으로 사용자가 선택할 수 있다. 물론 아무런 의미도 없다. 이름 예쁜거 쓰시길

 

단어 리스트 불러오기

부끄럽다

사용자가 원하는 영단어 리스트를 재정렬 할 수 있지만, 아무래도 나를 위한 사이트이므로 내가 주로 사용했던 단어 리스트를 불러 올 수 있도록 만들었다. 과거 게임을 플레이할 때 사용했던 리스트들을 미리 준비했고, 각각의 제목에 가장 마지막으로 추가된 캐릭터의 이름을 첨부하였다. 그리고 심심해서 세상의 영어단어들을 구글에서 잘 찾아서 리스트로 준비해봤다. 그런데 이게 웬걸, 73만개의 영어단어를 불러오려고 클릭하니 사이트가 터지더라. 문제를 해결하기 위해서 일단 View를 가려봤더니 계산은 1초내로 잘 한다. 따라서 73만개 단어를 보여주는게 문제라고 결론냈고, 10000글자 이상의 단어를 정렬할 경우 일부분만 표시하도록 하고 단어 목록을 수정할 수 없도록 바꾸었다. 이 과정에서 코드 구조를 일부 리팩토링하였는데 예전에 일하던 기억이 나서 흥미로웠다.

 

일부분만 표시하는 UI가 좀 못생겼다. 디자이너 구합니다

 

Generator 구현하기

랜덤 함수 만든 김에, rng 활용해서 수를 무한히 찍어내는 기능도 겸사겸사 만들어봤다.

로또 1등번호 유출

 

flutter 3.22와 material3

개발 도중에 아무 생각없이 flutter 버전을 업그레이드 했더니, ColorScheme 적용이 모두 풀려버렸다. 기존 버전은 기록에 남지 않아 알 수 없지만 업데이트 한 버전은 3.22이다. 찾아보니 3.16버전 부터 material3 design이 default가 되었다고 해서 겸사겸사 따르기로 했다. 사실 색상과 관련된 센스가 전혀 없다. 대충 seedColor로 Lockcept green을 넣고 다음 문서를 참고하여  DynamicSchemeVariant를 fidelity로 설정하였다.

https://api.flutter.dev/flutter/material/DynamicSchemeVariant.html

 

UX 개선

사용성을 개선할 만한 부분을 찾아보았다. 정렬 기능 부분에서 단어 프리셋을 불러오거나 설정을 변경하면 자동으로 정렬 버튼이 한 번 눌리도록 개선했다. 단순한 개선임에도 불구하고 사용성이 3배는 좋아진 기분이었다.

모든 곳에 알파벳 단어만을 입력한다고 가정하고 구현하여서, 그 외의 단어를 넣는 경우는 굳이 핸들링 하진 않았다.

 

배포

AWS를 통해 배포하였다. flutter build 결과물을 S3에 업로드하고, cloudfront와 Route53을 활용해서 arrangept.lockcept.kr 도메인을 달았다. 이 과정을 직접하기 귀찮아서 github에 push하면 자동으로 업로드 되도록 github action을 활용했다.

 

 

결론

기존에 해당 로직을 실행하기 위해서는 노트북을 열었어야만 했는데 웹사이트에 구현하고 나니까 기분이 좋다. 그리고 gpt가 많이 똑똑해져서 엄청 금방 개발 할 수 있었다. 롤하다가 챔피언 골라지고 싶으면 닉네임 넣고 추천받아보세요.

반응형