1차 프로젝트때와 달리 프로젝트의 pm 을 맡아, "Agile in scrum" 방식으로 매일 stand-up meeting 을 진행하고 Trello 로 스케줄을 관리하며 프로젝트를 주도적으로 진행했다.
postman 으로 만든 API 들을 (요청할 uri, 요청 method, key 값, query params 값... 및 요청 예시) 와 함께 문서화하려고 노력했다.
프론트/백에서 겪는 blocker 를 혼자서만 해결하게 두지 않고 다같이 고민해보는 시간을 가졌다.
프론트/백 기능을 처음부터 떨어져서 각자 만들지 않았다. 같은 기능을 담당하는 프론트/백이 같이 모여서 만들면서, 서로가 데이터를 어떻게 처리할 것인지 로직을 충분히 설명한 이후에 코딩하는 방식을 고수했다.
만들다가 기능에서 이해가 되지 않는 부분이 있으면 바로바로 이야기 하도록 주도하여, 나중에 프론트/백 어느 한곳에서 기능을 다시 갈아엎어야하는 수고를 덜었다.
1차 때 하지 못했던 기능들을 최대한 많이 해보고 이해하려고 노력했다.
1차 프로젝트 때는 커머스 사이트라서 주문관리 때문에 모델링이 복잡했었다. 그래서 모델링에 대해 정말 많이 고민했었고 힘든 만큼 많이 배웠었다. 그 덕분에 2차 프로젝트는 수월하게 진행했다.
모델링은 user app, store app 두개로 나눠서 진행했다.
가게(stores) 테이블안에 주소(address) 정보를 넣으려고 하다보니 한 테이블 안에 정보가 너무 많기도 하고, 따로 독립적으로 addresses 로 테이블을 빼서 관리하는 것이 깔끔해 보였다.
그래서 처음엔 그냥 foreign key 로 address 가 stores 를 참조하도록 했다가,
Django 의 one-to-one field 에 대해 공부했던 것이 생각나서 바로 적용해보았다.
1차 때는 one-to-one 관계 설정을 안해봤었는데, one-to-one field 를 쓰다보니 왜 사용하는지 몸소 깨닫게되었다. store.address_set 으로 접근을 안해도 된다는 점이 너무 편했다.
나머지 테이블에 대해서는 필요한 부분에 one-to-many, many-to-many 관계를 정했다.
카카오 소셜로그인 시 처음에 백엔드에서 인가토큰, 엑세스 토큰, 사용자정보를 다 받았었다.
그렇게 되면 백엔드에서 로그인 페이지로 redirect 까지 시켜줘야 했다. 이 부분은 프론트엔드의 부분이기 때문에 최종적으로
-> 프론트에서 엑세스토큰을 받는 부분까지 처리한 뒤 토큰을 넘겨주면, 백엔드에서 해당 토큰으로 Kakao API 에 사용자정보를 요청하는 로직으로 변경하였다.
사용자정보를 받아 db에 정보가 없으면 회원가입을 시키고 없으면 그냥 or 로그인을 시킨 뒤 jwt 토큰을 발행해서 프론트에게 넘겨주었다.
또 만약에 통합회원 관리를 하려면 어떻게 해야하는가에 대한 궁금증이 있었는데, 그렇게 하려면
회원가입을 먼저 시키고 로그인을 하게 한 다음 소셜 로그인을 연동하는 방식으로 진행해야한다.
그래야만 어떤 유저인지 알 수가 있고,
그 때 소셜 로그인을 연동하면 해당 유저 object 를 불러와서 kakao_id 든 google_id 든 해당 field 에 id 값만 추가하면 되기 때문이다.
로직은 생각보다 간단했다.
지도의 중앙으로 부터 지도의 위,아래/양옆 변에 해당하는 좌표를 구해야했다.
그러려면 사용자의 지도 좌표에서 지도 맨 끝쪽 까지의 거리를 알아야 했다.
축척이 주어져있어서 cm 당 몇 m 인지 알 수 있었다.
모니터 해상도 및 픽셀을 기준으로 실제 사용자의 화면에서 지도가 어느정도 크기인지 구했다.
지도의 길이와 축척으로 지도에 나타난 실제 가로세로 길이를 구했다.
이후에 중앙으로부터 수직으로 지도 오른쪽 끝부분까지 500m 라는 것을 알았으면
중앙의 위도/경도 + 동쪽으로 500m 를 계산해야했는데, 이 부분을 찾기 위해 스택오버플로우를 뒤져가며 위도/경도 에 거리를 더했을 때 위도 경도를 구하는 공식을 찾으려고 노력했다.
위도 경도 사이의 거리를 구하는 모듈은 많았는데, 위도 경도에 거리를 더했을 때 위도 경도를 구하는 모듈은 찾기 힘들었다.
그래도 결국 찾아냈다. geopy 라는 모듈의 distance method 를 사용하여 해결했다.
동서남북의 boundary 를 구한 뒤, db 에 addresses 테이블을 Q 객체로 필터링 했다.
--> 한가지 문제점은 넘겨받는 인자 수가 5개나 된다는 것이다.
그래서 리팩토링 할 때 저 위도, 경도, 축척, 가로픽셀, 세로픽셀의 정보를 담고있는
object 를 만든 뒤 그 object 자체를 함수에 넘겨줄 예정이다.
그렇게 하면 훨씬 더 깔끔해질 것 같다.
이 때 프론트 단에서 DPI 값을 구하는 모듈을 결국 못찾아서 백엔드에서 직접 DPI 값을 역으로 조정하면서 적절한 값을 찾았다. 프론트 테스트 환경에서 200 이 딱 오차없이 맞아서 DPI 를 200 으로 고정했다.
haversine 모듈을 이용하여 위도/경도 간 거리를 구했다.
두 개의 API 를 만들기에는, 필터링을 해도 어차피 같은 data set 인데,
여러 API 를 만드는 것이 비효율적이라고 생각했다.
-> 같은 view 에서 가격범위, 업종, 리뷰 개수, 평점평균 에 따라 필터링 후
페이지네이션 까지 작성하고 파라미터만 다르게 받아서 처리하는 방식으로 로직을 구현했다.
필터링 과정에서 exclude(), annotate(), order_by(), field lookup 등 다양한 QuerySet API 를 활용하여 깔끔한 코드를 작성할 수 있었다.
또한 이 과정에서 select_related, prefetch_related 를 통해 DB 데이터를 메모리에 caching 하여 반복문 마다 쓸데없는 쿼리 수를 감소시켰다.
가게 이름, 가게 주소, 근처 지하철역, 업종 이름으로 필터링을 했다.
Q 객체를 사용하여 | 연산자로 필터링에 걸린 모든 store object 를 담았고, 중복된 object 들을 거르기 위해 distinct() 메소드를 사용했다.
select_related, prefetch_related 를 사용하여 for 문마다 쿼리가 db 를 hit 하는 횟수를 감소시켰다.
--> 이렇게하면 db 를 거치지 않고 문자인증을 할 수 있다.
우리가 외부 도움없이 스스로 이런 로직을 생각해내고 적용했다는 점이 정말 뿌듯했고, 잘 할 수 있다는 자신감을 심어준 기능이였다.
-> 빈번하게 사용되는 해당 가게의 리뷰 평점 평균과, 전체 리뷰 개수를 구하는 로직을
classmethod 로 만들어서 필요할 때마다 간편하게 사용했다.
-> 불필요한 반복을 줄이기 위해 Base 클래스 를 만든 뒤, 다른 클래스에서 Base 클래스를 상속했다.
-> setUp() 부분만 super() 로 메소드를 오버라이딩해서 사용했다.
-> 이 때 Base 클래스에 __test__ = False
를 안하면 테스트 실행마다
Base 클래스가 계속 테스트되기 때문에 꼭 설정해줘야한다.
1차 프로젝트와 비교했을 때 훨씬 성장한 나를 볼 수 있었다.
내가 구현해야 할 기능에 매몰되서 내 것만 하는 것이 아니라, 내가 좀 느리게 가더라도 팀원들을 도우며 같은 속도로 가려고 했다.
다만 아쉬운점은, 프로젝트 발표였는데 우리는 마지막에 욕심을 내서 추가로 기능을 구현하려다가 발표준비가 다른 팀들에 비해 조금 미흡했던 점이 걸렸고, 팀원들한테 미안했다.
정말 잘 했다고 생각했고 기능 구현도 많이 했는데 발표에서 전부 다 우리가 했던 것들을 자랑하지 못해서 살짝 아쉬웠다.
개발자로서 코딩을 잘하는 것이 물론 중요하지만,
잘 만들고 마지막에 만든 결과물을 어필하는 능력까지 개발자의 몫이고 중요한 덕목이라고 생각한다.
다음부터는 준비를 더 잘 해야겠다!
그리고 다른 팀원들은 속으로 우리 팀을 어떻게 평가할지는 모르겠지만 내가 생각할 때 우리는 목표를 위해서 잘 달려온 것 같다.
서로를 믿고 도와주고 힘들더라도 묵묵하게 각자 맡은 부분을 끝까지 잘 해낸 승옥 님, 성훈 님, 나은 님, 호열 님에게 감사하고
코드리뷰를 통해 많은 인사이트를 주신 지훈 멘토님께도 감사드린다.
좋은 프로젝트에 좋은 팀원들이여서 좋았다.
두 달동안 정말 바쁘게 달려와서 두달이란 시간이 정말 빠르게 흘러갔다는 걸 이제서야 체감한다.
앞으로 한달동안 협업을 하게 될 회사에서는 또 어떤 일들이 있을지 무엇을 또 새롭게 배울지 기대하는 마음으로 회고를 마무리한다.
고생하셨슴다 택향님~