실제 사용자가 사용하는 서비스는 어떨까? - 회고

영슈·2024년 11월 5일
1
post-thumbnail

해당 내용은 프로젝트 를 하며 느낀점들에 대해 회고하며 작성한 글입니다.
혹시, 궁금한 내용이 있다면 댓글로 또는 joyson5582@gmail.com로 남겨주세요!


현재, 저희팀은 우아한테크코스 예비 7기 생들을 대상으로 프로젝트를 시범 운영하고 있습니다.

코드 리뷰 매칭 플랫폼
CoReA로 완성하는 개발 성장의 퍼즐: 코드, 리뷰, 그리고 당신

CoReA(Code-Review-Area) 는 미션 코드리뷰 매칭 플랫폼입니다.
코드 리뷰, 서로 피드백을 하며 ‘함께 성장하기'를 만들어 나갑니다.

- 레포지토리 링크 : https://github.com/woowacourse-teams/2024-corea
- 사이트 링크 : https://code-review-area.com/

( 사용자 증가 폭입니다. 현재 300명을 넘어섰습니다! 🙇‍ )

이와같이 프로젝트를 개발하고 사용자들을 맞이하는 소중한 경험을 할 수 있었습니다.
그러면, 이런 사용자가 존재하는 서비스를 운영하며 느끼는 점들에 대해서 작성해보겠습니다.

정적인 기능 개발과 동적인 기능 개발의 마음가짐은 다르다

단순히 요구 사항이 주어진 프로젝트에 대해 기능 개발을 하는건 매우 쉬웠습니다.
그냥 패키지 새로 만들고 거기에 controller,service,domain 패키지 만들고 추가하면 되니까요.

하지만, 실제 초기 생각한 기능 개발이 아닌 추가되는 요구사항은 매우 어려움을 주는걸 느꼈습니다.
대부분이 기존 코드에서 수정 및 추가가 됩니다. 작업해야 하는 코드에 대한 이해도가 없으면 매우 어려워집니다.

그리고 매우 바쁩니다. 기존에는 기능 개발만 하면 됐다면 서버 관리,기능 수정 ,에러 대응 등의 일들이 추가로 생겼습니다.
여기에 피드백 반영과 원하는 기능 개발도 해야합니다.

사연없는 코드는 없다 ⭐️

해당 내용은 되게 많은 부분에서 느꼈습니다.
레벨 1~2간 가장 의미있게 배운건 객체지향적인 코드와 클린 코드입니다.
하지만, 프로젝트는 이를 완벽하게 챙길순 없었씁니다.

코드는 늘 도망가

요구사항들은 개발하는 시간을 기다려 주지 않습니다.
모두가 이게 최선이 아닌건 알지만, 이를 RC(Request Chnage) 를 하면 기한을 못 지킵니다.
이런 코드는 끊임없이 쌓여갑니다.
어느 순간, 다음으로 맡은 사람들은 코드를 건드리기 싫어집니다.

  • 내가 작성한 게 아니기에, 기능 이해가 부족하다.
  • 코드가 더럽고, 테스트가 빈약하다.

이에 대해서 코치님들에게도 물어보고, 전 리뷰어에게도 물어봤지만 내린 결론은
"어느 정도 더러움은 방치하되, 임계점이 다가오면 더러움을 느낀 사람이 리팩토링을 하자." 인거 같습니다.

특히, 모든 기능 개발을 대략 멈추고 진행하는 피트스탑 이 가능할까? 에 대한 생각이 든거 같아요.
그렇기에, 실제 사용자가 있고 이를 계속 개발해나가는 굴러가는 바퀴 교체하기 가 정말 개발자로서 성장에 도움이 된다고도 느꼈습니다.
( 기능 하나도, 더욱 조심스럽게 + 기존의 코드가 문제를 유발하지 않게 신경을 씁니다. )

객체지향이 DB 지향은 아니다.

위에도 말했듯 레벨 1,2 는 객체지향적인 코드의 연속이였습니다.
프로젝트에서도 최대한 코드를 객체지향적으로 작성하려고 했습니다.

하지만, DB 성능 향상 요구사항을 위해 인덱스를 테스트 + 쿼리 분석 을 하며 몇 곳에서 아쉬움을 느꼈습니다.

  • 엔티티가 다른 엔티티를 가지며 다양한 곳에서 N+1 발생
public void participate() {  
    if (memberRole.isReviewer()) {  
        room.increaseReviewerCount();  
        return;    
        }  
    if (memberRole.isBoth()) {  
        room.increaseBothCount();  
    }  
}

( 다른 정답이 있다고 생각하면? 사연없는 코드는 없다. 🥲 )

  • 성능 개선에 도움을 주지 않는다.

객체가 객체에 맞게 변수들을 가지게 있게 하려고 노력했습니다.
"방이 자신의 상태와 자신의 마감 기한, 개발 분야(안드로이드,백엔드 등등) 을 가지는건 당연하다" 라고 생각했습니다.
하지만, 이런 데이터들을 인덱스를 통해 성능 향상 시킬때 매우 어려움을 느꼈습니다. ( 특히, 엄청난 감소가 되지 않기도 했구요. 😭 )

물론, 이게 엔티티 분리 및 JOIN 을 통해 명확하게 해결 될지는 모르겠습니다.

결국 객체지향 만큼 중요한게 엔티티로서, 어떻게 동작할지도 생각해야 하는 것 같았습니다.
( 이를 해결하기 위해선 DDD 처럼 엔티티 - 도메인을 분리하는 것도 방법일거 같습니다. ☺️ )

테스트, QA 부족

바쁜 일정은 테스트와 QA 를 부족하게 만들었습니다.
사실, 바쁜 일정이라기 보다 더 복잡해진 기능과 추가되는 기능들에 대한 완벽한 테스트를 수행하지 못한게 맞을지도 모릅니다.

이번에 따끈하게 깨달은 내용으로 로직 중에서
코드 리뷰를 완료 -> 해당 대상에 대한 개발 피드백 작성 페이지로 이동하는 로직이 존재했습니다.
여기서, 프론트엔드가 잘못된 State 에서 값을 꺼내서 undefined 가 발생해서 페이지를 렌더링 하지 못하는 에러가 발생했습니다.
개발 서버에서 문제 유무를 확인 했으나 추가한지 얼마 안된 상대방에 대해 피드백을 남기지 않으면, 마스킹 처리를 한다. 라는 로직과 맞물려 문제가 발생했습니다.

가장 중요한 문제는 여기서 발생하게 됩니다.

사용자와 빠르게 연락할 창구가 반드시 존재해야 한다

해당 에러는 프로젝트 팀원들이 감지한게 아니라

350

350

사용자들이 문제가 발생해서 제출해준 후 알게 됐습니다.
특히, 저희에게 직접적으로 문의하지 않고 에러를 마주친 사용자들도 있을거 같습니다. 😭😭 ( 죄송합니다,, )

추가로, 이 역시 따끈한 에러로 리뷰 방에 참여하면 생성되는 엔티티
394개 중 한 사람만 중복 엔티티를 3개를 가져서 NonUniqueResultException 을 발생 시키고 있었습니다.
( 아직 정확한 발생 이유를 파악하지 못했네요 🥲 )

해당 내용을 로그로 확인 했지만 사용자에게 연락 할 방법이 없었습니다.
그래서, 깃허브 코멘트로 에러 이유 + 해결 계획을 알렸습니다.

이런 사유에서 명확하고, 빠르게 연락할 창구가 필요함을 느꼈습니다.
저희는 기존에 구글 폼을 통해 연락을 받았으나 구글 폼은 소통 창구로서는 적합하지 않음을 느꼈습니다.

  • 빠른 양방향 소통이 안된다. ( 이메일 통해 보내야 하므로 )
  • 구체적인 사례는 한번에 설명하기 어렵다.

현재는 빠른 소통을 위해 오픈 채팅방을 만들어 놓은 상태입니다.

서버는 생각보다 건재하다

서비스 런칭을 하기 전 가장 두려웠던 건 서버가 터지면 어떻게 하지...? 였습니다.
이를 대비하기 위해, 혹시 모르는 스페어용 인스턴스도 미리 만들어 놓고 홍보후에 모니터링도 계속 보고 있었습니다.

그런 걱정이 무색하게 서버는 현재까지도 안 터지고 있습니다. 🥲

인프라를 간단하게 설명하면
ALB 를 통해 로드밸런싱을 하고 있으며
small 유형 한대, micro 유형 한대로 이루어져 있습니다.
DB 는 RDS 를 통해 Reader-Writer 분리되어 있습니다.

우선, 힙 메모리는

small 기준

평균 30%에 근접했으며

micro기준

평균 50~60%에 근접했습니다.

요청들 역시도, 외부 API 를 사용하는 (로그인,리뷰 완료) 를 제외하고는 안정적인 피크를 보여줬습니다.
부하 테스트에서도 너무 많은 요청을 통해 Connection Timeout 은 발생 했어도 서버가 터지는 내용은 발견하지 못했습니다.

인덱스 및 캐싱 같은 최적화도 당장은 적용하지 않았습니다.
인덱스 테스트 결과, 100만개 가량의 데이터를 넣었을 떄는 3초 -> 0.5초 가량의 성능 향상을 봤으나
이게 정말 필요할까? 라는 부분들이 많이 존재했습니다.
( 당장 데이터가 100만개가 한번에 생성이 되는게 아니므로 )

( 너무 겁내지 않고 기능 완성과 런칭 해도 좋을거 같습니다 ☺️ )

기능 외 작업을 하는 시간을 줄이자

해당 내용은 제가 굉장히 신경을 많이 쓴 부분입니다. 개발자 생산성을 향상시키는 것이였습니다.

개발자는 사실, 기능 개발만 하지 않습니다. 프로젝트를 하며 매우 많은 일들을 하게 됩니다.

  • 정책을 어떻게 할지에 대한 회의 ( EX : 피드백은 방이 다 끝나면 보여줄까? )
  • 프로젝트 홍보를 어떻게 할지, 리뷰어는 어떻게 모집할지
  • 어떤 기능을 구현하고, 역할을 분배할지
  • 프론트엔드와 페어 프로그래밍 ( API DTO 는 어떻게, 경로는 어떻게 등등 )
  • 데이터,서버 배포 상태 확인 및 로그 확인

( 이외에도, 코치들이 준 요구사항 미션 때매 파묻혔습니다... )

이렇게 개발 내/외적으로 매우 많은 일을 하게 됩니다.
그래서, 매우 많은 부분에서 기능 외 작업 하는 시간을 줄이기 위해 시도 및 노력을 했습니다.

“이 정도는 뭐, 어쩔수 없지 않나?“, "또 해야해? 너무 귀찮은데" 와 같은 불만과 반복 작업을 할 수 밖에 없었습니다.  
반복된 작업들이 쌓여 팀원들의 시간과 체력을 낭비시키고, 결론적으로 프로젝트의 생상성을 저하시킨다고 생각합니다.

그렇기에, 개발자 생산성을 향상시키는 다양한 작업들을 시도 및 수행했습니다.

처음에는 굳이 필요한가?노력 대비 리턴이 별로다,, 라는 생각도 있었습니다.
하지만, 쌓이고 이 모든 것들이 팀원들의 시간을 줄여주는 결정적인 도구들이 되었다고 생각합니다.

  1. CI & CD 전략 선택하기 ( 부제 : CodePipeline 사용기 ) - 2024년 7월 27일
  2. 나만의 Workflow 파일 만들기 ( 부제 : 이슈 기반 PR 자동 생성기 ) - 2024년 7월 28일
  3. JPA 사용 중 쿼리 가로채기, 모든 컨트롤러 메소드 중 발생하는 쿼리 검사 - 2024년 8월 18일
  4. 깃허브 기초부터 시작하기(3) - 커스텀 명령어를 통해 효율성 향상하기 - 2024년 8월 30일
  5. 모니터링 이동기(2) - 프로메테우스,로키,그라파나 설치 & 연결 - 2024년 9월 22일
  6. 데이터 테스트를 위한 가짜 데이터 만들기 - Bash 스크립트 - 2024년 10월 8일
  7. PR 병합때 알림 받는 내용을 의미있게 with naver pr stats 워크플로우 - 2024년 10월 13일
  8. 서버가 배포되면, 슬랙으로 배포 결과와 배포한 커밋까지? with AWS CodePipeline - 2024년 11월 4일

이와같이 꾸준히 노력한 결과 매우 많은 부분에서 효율적으로 작업을 할 수 있었습니다.
( 내용들을 들어가면, 전부 다 팀 내 충분히 도입할 수 있는 것들입니다. )

+ 팀내, 긍정적인 피드백을 듣는것도 장점 중 하납니다. 🙂

현재는

600

이와같이 프론트엔드 개발자도 보기 쉬운 데이터 대시보드를 만들고 있습니다.

결론

이상으로 정말 긴 글이 끝났네요.
7월부터 시작해서 현재 끝을 향해 가고 있는 프로젝트인데 정말 재밌고 소중한 경험을 했습니다.

레벨2 부터 가지고 있던 현업은 어떻게 할까? 라는거에 대해 스스로 깨달아가는게 가장 행복했습니다.
( 특히, 이메일 정보도 개인 정보로 인식되는지 판례를 찾아보고 하는건 매우 신기한 경험일겁니다. ☺️ )

그중에서도 가장 큰 깨달음은 프로젝트를 런칭할때 모든게 완벽해야할 필요는 없다 인거 같습니다.
처음 서비스 개시를 할 때도 인덱스 최적화 안되있는데... 기능이 부족한데... 모니터링 경고 시스템도 없는데... 와 같은 많은 걱정을 앞두고 있었습니다.
하지만, 이런 걱정이 무색하게 프로젝트는 어찌어찌 돌아갑니다. ( 아직도, 개선 및 적용 안된 부분들도 많습니다. )

그리고, 사용자와 함께 프로젝트를 만들어나갈때 진정으로 성장을 할 수도 있다고 느꼈습니다.
한명의 사용자라도 만족스럽게, 행복하게 하기 위해 나아가봅시다! 🫡

긴 글 읽어주셔서 다시 한번 감사합니다. 🙇‍♂️

부록

해당 내용은 가볍게 느낀점들이라 부록으로 남겼습니다.
( 정말 기준이 없고, 순수 제 의견입니다. )

도메인 기반 패키징은 괜찮은가?

저희팀은 도메인 패키지 구조 를 사용했습니다. ( 각 도메인 패키지 내 service,controller,domain 소유 )
처음에는 더 도메인을 온전히 사용할 수 있고 구조를 유연하게 한다고 생각했습니다. ( 도메인간 패키지를 통해 분리 )
하지만, 어느 순간 서로 혼재되며 "어떤건 어디 도메인에, 어떤건 다른 도메인에" 위치되는게 존재했습니다.
이런 관점이라면 차라리 각 레이어들을 모두 같이 위치시키는 것도 좋은 방법이 될 수 있겠구나 생각이 들었습니다. 🙂
( 더 찾기 빠르고, 모호한 개념을 단순히 사용 가능하다. )

이쁜 코드가 개발 속도를 빠르게 만들어 주는가?

위에서도 말했듯이 지금 저희 프로젝트는 그렇게 이쁜 코드는 아니라고 생각합니다.
하지만, 이를 해결하기 위해선 "리팩토링과 재설계" 는 필연적으로 시간이 듭니다.
그리고, 피드백을 받으며 기능은 수정 및 변경이 매우 빈번하게 발생하게 됩니다.
-> 어디까지 이쁜 코드를 작성해야 할지, 시간을 써야할지 아직 고민입니다.

엔티티가 편의성 컬럼을 가져도 괜찮은가?

현재, 엔티티가 불필요한 컬럼들을 가지고 있습니다.

public class Room extends BaseTimeEntity {
    private int reviewerCount;

    private int bothCount;
	...
}

와 같이 count(*) 을 하면 해결되지만 중요한 값이 아닌점 + 불필요한 조회문을 발생시키기 싫다는 의견에 이를 배치했습니다.

public class MatchResult extends BaseTimeEntity {
    @Enumerated(EnumType.STRING)
    private ReviewStatus reviewStatus;
    ...
}

처럼 Review 라는 엔티티를 만들지 않고, Status 를 변경함으로써 해결합니다.
이 내용들은 조금 더 알아봐야 할 거 같습니다. 🥲

부록도 이상입니다~ 부록 내용에 대해 의견이나 생각이 있다면 남겨주셔도 좋아용 🫡

profile
Continuous Learning

0개의 댓글

관련 채용 정보