2차 프로젝트 회고 및 refactoring 후기

Johnywhisky·2021년 6월 18일
0

회고록

목록 보기
2/2

Reprospective


아쉬웠던 선택과 집중

드디어 위코드에서 두 번째 프로젝트가 끝났다. 첫 프로젝트를 진행하며 기초적인 데이터베이스 모델링, 백엔드 API 로직 설계 그리고 프론트엔드와 백엔드의 통신 등 많은 경험치를 쌓을 수 있었고 이를 바탕으로 2차 프로젝트를 진행했다. 아니, 할 수 있을 줄 알았다. 이번 프로젝트를 진행하며 아쉬웠던 것이 많은데 그중 하나는 프로젝트를 완성하기 위한 올바른 우선순위를 선택하지 못했던 것과 외적, 내적 요인으로 인한 스트레스에 지나치게 흔들렸던 점이다.


첫 번째로 테스트 데이터에 시간 투자를 과하게 했었다.

이번 프로젝트의 꽃은 '지도'였기 때문에 그에 맞는 정확한 테스트 데이터 구축을 해야 한다는 강박에 가까운 생각에 잡혀 실제 지도 데이터와 건물 이름 등을 구하는데 많은 시간을 쏟았다. 그리고 대부분 데이터 파일은 CSV 형태로 제공되었었고 MySQL에 바로 장입하기 위한 희대의 뻘짓(무한 구글링을 해 보안 설정을 바꿔야 한다는 걸 알았지만 죽어도 안 바뀌는 탓에 결국 실패했다.)에도 많은 시간을 쏟았었다. 결국 6700여개의 실제 좌표 데이터를 정리할 수 있었다.


두 번째로 데이터 베이스 모델링을 가볍게 여겼다.

처음 모델링을 시작하면서 생각보다 복잡한 테이블 관계에 적지않은 에너지를 소모하게 되었었다. 일주일 동안 진행된 DB모델링 결과 중 하나인 밑의 모델은 사실 정확한 모델링을 구현한 것이 아니다 정규화를 할 수 있었지만 하지 않은 부분도 있고, 클레스, 속성 네이밍도 잘 안된 부분이 많았다.

Troble Shooting!! API 로직을 구성하고 나서 맞닥뜨린 가장 큰 문제는 '소요 시간'이었다. API에서 각 구 소재 모든 매물의 id를 배열로 가져와야했는데 로직 자체는 Nested Comprehension으로 특별할 것 없는 로직인데 문제는 쿼리 개수였다.

쿼리문 소요 시간은 개당 1000분의 1초도 채 되지 않는데 밑에와 같은 9000개의 쿼리가 생성되는 바람에 약 10여초가 소요 되는 불상사가 발생했다.

{'sql': 'SELECT `gu_types`.`id`, `gu_types`.`name`, `gu_types`.`latitude`, `gu_types`.`longitude` FROM `gu_types` WHERE `gu_types`.`id` = 12 LIMIT 21', 'time': '0.000'}

그래서 gu_type.id 을 gu_type_id 로 바꾸었더니 쿼리문 개수는 매물 개수와 똑같은 6765로 줄었고 그나마 6초 밑으로 줄어들었다.

{'sql': 'SELECT `dong_types`.`id`, `dong_types`.`name`, `dong_types`.`latitude`, `dong_types`.`longitude`, `dong_types`.`gu_type_id` FROM `dong_types` WHERE `dong_types`.`id` = 362 LIMIT 21', 'time': '0.000'}

한 번의 참조 과정을 줄인 것이 이렇게 40%라는 성능 약 40%의 성능 향상을 가져왔다는 것을 확인할 수 있었으며 데이터 양이 많아질수록 DB에 최소한으로 접근해 자원을 아껴야함을 몸소 느낄 수 있었다. 물론 내 코드는 소중하지만 아직 쓸모없는 6초짜리 API일 뿐이다. 결론적으로 지금의 모델링과 로직으로는 더이상 시간 단축이 어렵다 판단하여 모전반적으로 리팩토링 중이다.


마지막으로 조금만 솔직하게 말하자면 협업은 정말 어려운 일이다.

사람들은 흔히 이런 말을 많이 하는 것 같다.

수십 년간 서로 다른 환경에서 살아온 사람이 만나 새로운 연을 맺는 건 당연히 어렵다.

사람과의 소통 즉, 협업이 정말 어렵다는 걸 새삼 깨달았다. 특히 백엔드 간의 소통 부재는 다른 스트레스를 낳고 퍼포먼스가 저하되는 악순환을 낳게 되었다. 특히 각자의 코드가 메인 브랜치에 머지되는 순간 눈에 보이는 온갖 빨간색 에러 메시지에 멘탈이 안드로메다까지 날아가는 경험을 하기도 했었다. 그래도 pm 역을 자처한 동료의 수고와 헌신으로 우여곡절 끝에 어떻게 어떻게 프로젝트를 마무리 지었다. 앞으로 수많은 사람을 만나고 또 부딪힐 텐데 이번 프로젝트처럼 끝날 때까지 누군가의 도움을 받아 가며 진행할 수는 없을 테니...


Refactoring을 거치며

수정 사항들은

  1. Naming

모델이나 컬럼의 이름을 조금 더 직관적으로 이해할 수 있게 수정하였고, 특히 역참조를 자주 사용하는 관계에서는 related_name을 지정해 코드에 간결함을 더할 수 있었다.

  1. Normalization

모델링의 정규화가 아닌, 서버 구조의 정규화를 구현해보았다. 기업협업에 나간 동기들의 경험을 빌려와 나름대로 구축해보았으며 특히 자주 쓰이는 함수들은 따로 모듈화 시켜 사용할 수 있게 했다.

  1. Logic

가장 문제가 되었던 쿼리 개수 문제를 나름대로 해결했다. 결론부터 말 하자면 비슷한 상황에서 수천개의 쿼리에서 수백개에서 적게는 수십개 수준으로 줄어드는 결과를 볼 수 있었다.

이전에는 map_view에서 해당 구역에 매물 갯수와 매물의 id값을 모두 보냈었기 때문에 매번 수천개의 매물 쿼리를 확인하고 조건에 맞는 데이터만 출력해 보내주는 형식이었다. 그래야 room list를 확인할 때 다시 room id를 가져와서 id에 해당되는 정보만 뽑아 response를 보낼 수 있다는 생각이 있었기 때문이었다.(멍청했었지...) 그냥 갯수만 보내주면 되고, 다음에 다시 List 정보 요정이 들어오면 해당 구역을 잘 필터링해서 list를 뽑아주면 되는거였는데... 이것이 기존 코드 작성시에 놓쳤던 점이며 동시에 변화된 로직에서 가장 중요한 포인트인 필요한 것만 response로 보내주기이다.

  1. 회원 인증 인가

기존 프로젝트 진행 시 완전히 구현하지 못한 회원가입 로그인 기능을 추가했다. 일반 유저와 소셜 유저 간 계정 통합을 완전히 구현하지 못한 아쉬움이 조금 남지만... 이 부분은 조금 더 공부가 필요해 뒤로 남겨두었고, 로그인 시 JWT를 활용한 decorator를 이용해 회원 인증 기능을 구현했고, 토큰도 access token과 refresh token을 모두 발급 했다. 이 때 유효기간 설정에 조금 변화를 줘 시도해봤다.
많은 블로그를 참조해보면 토큰 유효시간을 다음과 같이 jwt의 payload에 함께 담아주고, 이후 인증 로직에서 현재 시각과 비교해 유효성 검사를 수행한다.

access_token = jwt.encode(
    {
        "user_id" : user_id,
        "exp"     : published_time + timedelta(hours = 6)
        },
    SECRET_KEY,
    algorithm = ALGORITHM)

하지만 나는 이번 리팩토링을 하며 그냥 단순하게 쿠키를 만료시켜도 되지 않을까란 고민을 했고, 그 결과 다음과 같이 로직을 구현했다.

res = HttpResponse("Sign In Success", status = 200)
res.set_cookie(
    key      = "access_token",
    value    = jwt.encode({"user_id" : user_id}, SECRET_KEY, algorithm = ALGORITHM),
    expires  = published_time + timedelta(hours = 6),
    httponly = True
    )

현업에서 사용 중인 방법을 구체적으로 모르기에 무엇이 맞는 방법인지는 모르겠으나 일단 만기 시간이 지나면 쿠키가 사라지는걸 확인했으니 성공적인 방법이긴 했다.
중요한건 현장에서 진짜 이렇게 구현하는지는... 모르겠다...

돌아보며 그리고 남은 과제들

리팩토링을 해야할 필요성을 느끼면서도 왜 해야하는지 모르겠는 그런 이중적인 생각에 밍기적거리며 시작한 리팩토링. 지난 한달 동안 해보니 그래도 처음 막코딩으로 끝날 수도 있었던 내 코드를 하나한 고치며 조금 더 효율적인 길을 고민해보고 sql과 디자인 패턴들을 공부하며 조금이나마 이해한 부분들을 적용시켜보려 노력했다는 점에서 스스로 한걸음 더 성장한 것 같아 꽤나 뿌듯함을 느낀다. 특히 누구의 도움을 받지 않고 최대한 스스로 찾고 또 찾고, 끊임 없이 고민하고 또 고민해야 내 실력 뿐만 아니라 서비스의 품질도 올라간다는 걸 확실히 체감할 수 있었다는 점에서 더욱더 뜻 깊은 시간이 아니었을까?
그래서 아직 구현하지 못한, 또 수정하지 못한 부분이 마음에 걸리고, 다음에 시간이 허락한다면 끝까지 리팩토링을 해야겠다. 특히 room_list를 제공하는 api는 방 매물 하나당 약 6~7개 정도의 쿼리를 호출하는데 정말 비효율적인 것 같다. 대부분의 테이블 관계가 O2O임을 감안하면 row query를 이용할 때 두개 내지는 세개 정도의 select문으로 충분할거라 판단되는데 이것을 ORM으로 어떻게 구현해 낼 수 있는지 아직 모르겠다. 그리고 계속 고민하고 고민해도 답이 보이지 않는 Restful... 계속해서 고민하고 수정해봐야지! 그밖에도 내가 담당하지 않은 부분의 API를 수정할 일도 남아있고 가장 중요한!! AWS를 이용한 배포환경 구축을 해봐야하는데!!! 일단 다음으로 미루고 지금부터는 취업 공부를 다시 시작할 예정이다.



Project Introduction

Description

  • 이 프로젝트는 나반 방 없어 팀 부동산 웹 앱 다방을 참조하여 진행한 프로젝트로 기간 및 인원에 맞춰 축소 기획하여 진행되었습니다.
  • 지도 및 매물 정보페이지매물 상세 정보 페이지 및 로그인 & 회원가입 모달 창을 중심으로 기능을 구현했습니다.
  • Frontend : 2명 / Backend : 2명
  • From 2021-04-26 to 2021-05-07

Refactoring

  • Search App 코드 및 User App 재구성
  • Backend : 1명
  • From 2021-06-16 to 2021-07-16

Features

  1. Main page 및 지도 page 검색 기능
  2. Q 객체를 이용한 필터링 기능
  3. Social 로그인 & 회원가입 기능
  4. 일반 회원가입 & 로그인 기능
  5. JWT Token을 이용한 유저 인증 기능
  6. 좋아요 기능

Used Stacks

  • Server : Django
  • Auth : Json Web Token
  • Encryption : Bcrypt
  • SSO : Kakao Social Log-in
  • ETC : Q object
  • Database : MySQL

Respository

Github Repo. Linked(refactoring repo)


Demonstration

Youtube Linked

profile
안녕하세요 :) 1년 차 Pythonist 백엔드 개발자 윤서준입니다.

1개의 댓글

comment-user-thumbnail
2021년 11월 29일

👍🏼👍🏼👍🏼✨✨✨👏🏼👏🏼👏🏼🔥🔥🔥🙆🏻‍♀️🙆🏻‍♀️🙆🏻‍♀️

답글 달기