MIMO 서비스
MIMO 서비스는 모임을 쉽고 편리하게, 투명하게라는 목적으로 만든 모임 플랫폼이었다. 가장 핵심적인 기능 두 가지를 이야기해보면 다음과 같다.
나는 여기에서 회원 기능과 모임 및 일정 기능을 맡았다. 해당 기능이 가장 빨리 만들어져야지 다른 기능을 만들기 시작할 수 있었기 때문이었다. 그리고, 다른 팀원에게는 코드 리뷰와 함께 코드를 개선할 방법에 대해서 제안을 하고자 했었다. 그 과정에서 제안한 내용 중 하나도 채팅을 팀원에게 제안한 내용을 기록으로 남겨둔 것이었다. 이후에는 계속 면접을 다니느라 정리를 못했었다.
아무튼 결과적으로 내가 만든 기능으로 나온 화면은 아래와 같다. 이 화면을 디자인을 담당하는 친구가 정말 고생을 했었다.


기획 과정에서의 어려움 - 초기 설득의 어려움
개인적으로 초기에 어려웠던 점은 팀원들을 설득하는 것이었다. 같이 백엔드하는 사람들은 먼저 나에게 연락이 왔었다. 1명은 어쩌다보니 고등학교 때 룸메이트가 같이 SSAFY에 있어서 연락이 왔었고, 1명은 같은 반이었던 사람, 1명은 룸메이트 친구랑 이전에 같은 반이었던 사람 이렇게 4명이서 했었다. 이전에 반에서 있었던 친구는 반에서 계속 내가 했었던 세미나에 참여도 했었고, 고등학교 때 친구도 있었어서 처음엔 사실 기술적인 설득이 어려울 거라 생각하진 않았었다.
그런데 뭐랄까... 몇 번 얘기를 해보니까. 팀원들이 생각하는 취업에서 포트폴리오를 만들기 위한 방향과 내가 생각하는 방향이 크게 다르다는 느낌이 들었었다. 팀원은 MSA 구조라던가, Kafka 같은 걸 써보고 Spring Batch 같은 걸 써서 포트폴리오를 만들고 싶었던 것 같았다. 그리고 그걸로 SSAFY에서 상을 받는 게 목표인 것 같았다.
하지만, 개인적으로 그러한 의견을 듣고 두 가지 의문이 들었었다. 첫 번째로는 저걸 하고 싶다고 하긴 하는데... 설까지 포함된 6주라는 기간 동안 정말 책임지고 할 수 있는 지를 파악하고 싶었다. 그리고 두 번째로는 SSAFY에서 상을 받는 프로젝트가 좋은 프로젝트라 생각하는 지를 알고 싶었다.
MSA와 Kafka를 우리 팀이 할 수 있을까..?
솔직하게 말하면 이 부분은 내가 모르기 때문에 자신이 없었다. 도전을 해보는 건 좋지만, 6주에 기획으로 이미 2주를 써버렸고 1주일은 설이 있어서 실제 구현 기간은 2주 정도였다. 그런데, 정확하게 모르고 간단하게 나마 프로젝트를 해본 경험이 아무도 없다면 나중에 아무런 기능도 만들지 못한 체로 끝날 가능성이 높다고 생각을 했다. 그래서 개인적으로 다른 방향으로 관점을 돌려보고 싶었다. 과거에 현직자 분 중에 스터디에서 세미나를 열어 취준생에게 다양한 공부에서 주의할 점에 대해서 소개해주시는 분이 있었는데, 그 분이 내 관점을 바꿔주셨던 것처럼 말이다.
첫 번째를 알고 싶어서 개인적으로 팀원들하고 특정 기능에 대해서 어떻게 구현할 건지 질문을 계속 해봤었다. 특히 어느정도 기획이 완성이 되고 채팅 관련 기능을 제안하고 팀원들의 반응을 보면서 Redis에 대한 개념에 대해 정확하게 이해하고 있는 지, DB 인덱스와 캐싱을 했을 때 어떻게 캐시 미스를 적게 나게 해서 동기화를 시킬 것인지를 고민해본 적이 있는 지 궁금했다.
팀원들의 반응을 보고 나서, MSA, Kafka는 조용히 묻어두는 게 좋을 것 같다는 생각이 들었다. 나도 취준생 신분이다보니 내 의견이 맞을거라는 확신을 못한다는 생각이 들었다. 그래서 개발자 출신인 컨설턴트님께 도움을 요청했었다. 어떤 내용을 고려했고, 이런 기능들을 만들고자 하는데 이렇게 만들려고 한다고 이야기를 했을 때 다행히도 모두 OK를 받았다. 보통 본인도 그렇게 예전에 처리를 했었다는 이야기를 해주셔서 다행히 이후에는 설득하는데 도움을 많이 받았다. 특히 기획 자체로서는 관심을 끌기는 어렵지만 디테일적으로 나중에 들어간다면 좋은 주제라고 이야기를 해주셨다.
그렇게, 컨설턴트님과의 면담이 끝나고 계속 특정 개념에 대해서 3시간이 넘게 동안 나에게 물어봤었고 답변하느라 정말 힘들었던 기억이 난다. 사실 팀원들이 모두 비전공자 인문 계열에 SSAFY에서 개발 경험을 처음 쌓은 친구들이었다. 그렇기 때문에, 어디서부터 설명해야 할 지 파악하려고 질문을 생각하는데 시간을 많이 썼었다. 대충 내역을 회고해보자면...
첫 번째는 Redis가 멀티쓰레드로 동작한다는 개념이 가장 기억에 남는다. ChatGPT가 그렇게 답변을 해준다고 해서 내가 잘못 아는 게 아니냐는 이야기를 들었었다. 그래서 ChatGPT에게 명령어 처리 동작은 싱글 쓰레드로 동작하고 백그라운드에서 리플리케이션 부분이나 영속성 관련된 부분이 멀티 쓰레드로 동작하는 거니 그렇게 다시 물어보라고 이야기를 했고 아키텍처 그림을 보여주었었다.

또한, 명령어 동작은 싱글 쓰레드라서 동기화를 신경 안써도 된다. 대신 명령어 하나를 길게 잡고 동기화 과정에서 늘어지면 안된다. 그러니 SCAN 방식처럼 동기화도 DB 인덱스를 사용해서 부분적으로 동기화를 시켜주고 잠깐 딜레이를 걸고 이후 부분을 진행하는 방식으로 진행해서 다른 명령어 차단을 최소화 시켜야 한다고 이야기를 했었다. TTL 부분에 있어서도 Lists 자료구조에서는 내부에는 못 걸고 key에 해당하는 Value로 된 List 전체에 걸린다라는 점도 설명을 했었고 공식 문서를 보면 명령어에 @Dangerous, @Slow 이런 부분과 시간 복잡도를 보고 사용하라는 이야기를 했었다.

두 번째는 스프링 서버와 관련된 부분이었다. 더 많은 요청을 처리하기 위해서 멀티 쓰레드로 처리로 바꿔야 한다고 한 팀원이 이야기하기에 이런 질문을 던졌었다. Tomcat에서 쓰레드 풀을 생성해서 처리하고 있는 지를 아는 지와 HikariCP에서 커넥션 풀을 생성하고 있는 걸 아는 지 각각의 기본 값이 몇 인지를 물었다. 그래서 서버에서 요청이 들어오면 쓰레드 풀에서 쓰레드를 꺼내서 요청을 잡고, DB를 조회할 때 커넥션 풀에서 가져와서 DB에 반영을 한 뒤 다시 반환되는 구조에 대해서 설명을 했었다. 그렇기 때문에 기본적으로 싱글 쓰레드처럼 코드를 짜지만, 쓰레드 로컬이나 공유되는 필드 같은 경우에는 Concurrent 라이브러리에서 제공되는 ConcurrentHashMap 같은 쓰레드 세이프한 자료구조로 사용해야만 race condition을 방지할 수 있다고 이야기를 했었다.
세 번째로는 캐시 미스의 위험성에 대한 설명이었다. 그 때, 왜 그냥 한꺼번에 비우지 않고 이렇게 귀찮게 나눠서 비우는 행동을 하느냐는 질문을 받았었다. 어떻게 보면 1번과 연관이 된 것이었다. 이건 정말 뭘 얘기해야 될 지 생각이 나지 않았었다. 캐시 미스가 일어나면 디스크에 접근할 가능성이 높아지고 그게 순간적으로 DB에 부하를 크게 가져온다는 걸 설명했었다. 그리고 인덱스를 잘 설정하면 정렬된 순서에 맞춰 이후 데이터를 가져오는 게 가능하다는 걸 설명했었다.
그래서 운영체제와 관련된 개념을 설명해서 이 질문을 풀었었던 거 같다. 운영체제에 보면 페이지 교체 알고리즘 부분에서 캐시 미스가 날 수록 디스크의 접근이 늘어나고, 메모리 접근과 디스크 비율에 따른 평균 시간을 계산해본 경험에 대해서 이야기를 했었다. 아래의 그래프를 보여주면서 캐시를 한꺼번에 비우면 그 캐시 히트율이 순간적으로 Cache Size를 전체 비율대비 확 줄인 것처럼 동작한다고 이야기를 했다. 그래서 한꺼번에 동기화를 하면 안된다고 설명했고 이 때 간신히 설득했었던 거 같다.

또한, 인덱스 부분은 B-Tree 구조부터 지역 공간성 등부터 설명을 쭉 별도로 했었고, B-Tree 아래의 특정 지점을 다음에 다시 넘겨주면 그 이후 데이터를 가져올 수 있는 이유에 대해서 설명을 했다.
두 번째는 상과 관련된 부분은 조금 자연스럽게 해결이 됐다. 상을 받고 싶은 거라면 기획에 대해서 다시 고민을 해보라고 피드백을 받았었고 기획을 새로 짰었다. 당시 나는 임원면접이 있어서 면접장에 있었는데, 계속 화상 회의를 하고 싶어하는 팀원이 있어서 그럼 기업에서 좋아할 회의 관련 솔루션을 제안해줄 수 있는 지 부탁을 했었다. 그래서, 아이데이션이나 No-PPT 같은 회사에서 도입하고자하는 몇 가지 방법론을 제시하고 면접이 끝난 뒤 다시 돌아왔다. 그렇게 아이데이션을 구현한다는 아이디어가 새로 나왔고 아이디어는 좋았지만, 컨설턴트님과의 기획 확정 과정에서 오히려 컨설턴트님이 반대를 했었다. 이전에 모임 관련 내용이 기술적으로 고민해볼 내용도 많고, 상과는 멀어지겠지만 이건 너무 백엔드가 할 게 없다는 이야기를 해주셨었다. 결국 그래서 모임 플랫폼을 결정하게 됐다... 오히려 컨설턴트님의 의견이 나랑 비슷해서 다행이란 생각이 들었다.
이 기간이 정말 오래 걸렸었다. 프로젝트 기간의 절반? 설 연휴까지 그래서 프로젝트가 지연되기는 했지만 그래도 꼭 필요한 과정이라고 생각을 했다. 이 과정에서 팀원들이 초반에 원했던 MSA와 Kafka를 도입 못했어서 조금 불만이 있었던 거 같다. 이 부분에 대해서는 미안하게 생각을 한다. 하지만 6주라는 기간 동안 어렵다고 생각해서 이 부분은 단호하게 행동했었고 기능 별로 고민해볼 거리와 어떻게 구조를 가져갈 지 계속해서 제안을 했었다. 하지만, 오히려 마지막 주차에는 프로젝트 규모가 커서 그런 지 일정에 쫓기면서 팀원들도 그 때 안해서 다행이다라는 이야기가 나왔어서 틀리지는 않은 행동이었던 거 같다.
구현 과정에서의 어려움
개인적으로 구현 과정에서 문제점도 꽤나 여러 가지가 있었다. 첫 번째는 버그를 수정할 때 프론트엔드 팀원에게 버그를 빠르게 수정해서 주고 싶어서인지 코드를 돌려보지 않은 채로 dev 브랜치에 팀원들이 병합하는 것이 문제였다.
특히나 SSAFY에서 제공해주는 서버 시스템이 막힌 게 많아서 DB커넥션 수부터 포트 허용까지 다 SSAFY 서버 팀에 연락을 해야됐었고 하루를 기다려야지 해줬어서 배포가 굉장히 늦어졌었다. 마일리지 시스템 담당하는 친구가 본인 코드 작성을 못하고 정말 고생을 많이 했었다. Jenkins 빌드 로그를 보는데 조금 안타까웠다. 이 부분은 예상을 못했다.
나중에 구현에 있어서 구현이랑 버그 수정에 있어 급한 마음은 이해를 했다. 하지만, 오죽했으면 프론트엔드 담당 친구가 깃랩에서 날아오면 그걸 가져오는 명령어를 입력하기가 너무 무서워서 나에게 "아아... OO야 나 이거 너무 무서워... 또 안되는 거 아니야?? 이거 해도 되는 거지??"라고 이야기를 했었다. 프론트엔드 친구에게 미안하다는 생각도 있지만 이 때마다 다른 팀원이 짠 코드에서 생긴 버그를 대신 찾아주느라 내 시간도 낭비가 심했었다.
결정적인 사건은 너무 오랫동안 최신화 시키지 않은 브랜치를 합치는 과정에서 특정 팀원의 코드가 완전히 날아가는 일이 생겼었다. 정말 일어나선 안될 일이 일어난 것이다. 이 때까지는 어떻게 별 말을 안하고 넘겼지만 이제는 코드 병합 부분을 조금 손을 봐야 될 필요가 있다 판단됐다.
두 번째는 하드코딩된 값들이 많았다. 예를 들면, 채팅방과 계좌의 경우 특정 값으로 하드코딩이 되어 있어서 다른 팀원들이 해봤을 땐, 돈 값이 안 바뀌고 채팅이 안 올라가는 문제가 있었다. 이 부분을 제거를 했어야 했는데 못했는지, 나도 같이 찾아주겠다고 디버거로 찍어보는데 의도하지 않은 값으로 넘어가는 게 있어서 몇 개 찾았었다.
세 번째는 변수 값 관련된 오류들이 많았다. 특히, 서비스 계층에서 데이터를 넘길 때 앞 뒤 파라미터가 원하는 값의 유형은 다르지만 타입이 같아서 문제가 생기는 경우가 많았고.. 이거도 디버거로 찍어서 몇 개 찾아줬던 거 같다.
개인적으로 팀원들과도 이야기를 하면서 다들 비슷하게 생각을 했고 아래처럼 규칙을 바꿨다.
다행인 건 이 과정에서 특히 남의 코드를 받고 spring 서버가 안 돌아가는 문제는 확실하게 줄어든 것 같았다. 규칙적인 거 외에도 몇 가지 코드적인 부분도 조금 바꿨었다.
첫 번째는 소셜 로그인으로만 테스트를 하게 처음에 로그인 부분을 구현해놔서 구글 ID가 하나인 친구들이 앞의 현상이 심했던 거 같았다. 이건 내가 잘못을 한 것 같았다. 평소에는 자체 로그인을 만들다가 이번에 OAuth2로 하다보니, 이 부분에 대해서 생각을 못했었다. 나는 계정이 여러 개라 번갈아가면서 테스트했었다. 하지만 그렇지 않은 팀원들에게도 구글 아이디를 번거롭게 만들지 않아도 여러 계정을 생성해서 접근할 수 있게 만들어줘야겠다 생각했다. 그래서 소셜 로그인을 안해도 할 수 있도록 "야메 로그인"이라는 임시 회원가입 겸 로그인 API를 만들었다.

두 번째는 애노테이션 형태로 제공되는 것들을 많이 추가를 했었다. ArgumentResolver로 파라미터 레벨에서 걸 수 있도록 만들어놓는 부분은 게스트 로직이나 아니면 DB가 아닌 레디스 로직을 타야되는 경우 등에 DB 접근을 안하는 @SimpleAuthUser 같은 부분을 만들어줬었다.

AOP를 적용해서는 계좌 담당하는 친구에게 분산락의 경우에는 로직이 어떻게 되고 반복이 되는 부분이 어디인지 설명을 해주고 만들어보라고 시켜서 @DistributedLock을 만들었었다. 이 외에는 권한 체크 관련된 Member만 가능, Leader만 가능 등에 대해서 만들어서 줬었다.

이건 초기에는 조금 팀원들이 실수를 했었었다. 특히 권한 체크 부분에서 Id 두 개 받는 경우도 있었는데 이걸 반대로 적어서 버그가 생겼던 적도 있었었다. 그래도 팀원도 그 이후로 파라미터를 꼭 API 별로 다시 확인을 하는 습관이 생겼고, 코드를 다시 봤을 때 내부가 깔끔해지고 어떤 권한 체크가 들어있는지 확인할 수 있어서 다음에 다른 사람이랑 프로젝트할 때 쓰겠다는 이야기를 들었었다. 이 부분은 솔직히 기분이 좋았다.
참고로 당장 쓰지 않는 팀원이 나중에 잊고 나한테 물어보기 전에 찾아보면 좋을 것 같았다. 팀 내 위키에 이렇게 advice랑 pointcut에 대한 설명을 붙여놨다. 하지만 팀원의 질문을 피하지는 못했다....

내가 반성해야 하는 부분
첫 번째는 API문서 관련된 부분이다. SSAFY에선 1학기 때 Swagger 사용을 권장한다. 그런데, 개인적으로 Swagger를 사용하면 Controller가 지저분해서 사용하기 싫었다. 반면, RestDocs를 쓰자고 하기에도 API 완성할 시간도 부족한데 테스트 코드까지 짜면서 하기에는 너무 시간이 빠듯했다. 그래서 Postman에서 응답 기록들을 쭉 저장해두면 AI로 만들어주는 기능을 활용했었다. 개인적으로 이렇게 작업을 해본 팀원이 없었어서 직접 프론트엔드 팀원들이 Java 코드를 보고 타입을 찾아서 사용했었다고 하는데, 이 부분은 좀 미안했다. 다음 만난 팀원은 더 잘 작성해줘야겠다.

이게 편하다고 생각하는 팀원도 있었지만 팀원마다 호불호가 조금은 갈렸던 거 같다. 그리고 팀원 별로 API 문서에 성공 케이스나 설명을 적는 정도가 달라서 여전히 자바 코드를 보는 프론트 팀원도 있었다.
프로젝트 API 문서 링크: https://documenter.getpostman.com/view/40705629/2sAYXBEyjn
두 번째는 리뷰 관련된 문제였었다. 아래처럼 계속 팀원들이 작성한 코드에 대해서 리뷰를 남기고, 어떤 부분을 바꿔야 하는 지, 내가 이해가 안 가는 부분은 어떤 코드인 건지 물어봤었다.

그런데, 개인적으로 팀원들의 코드를 보기가 너무 어려웠다. 항상 어떤 의도로 작성한 지 알기가 어렵다는 말과 함께 분리해달라는 말을 엄청 많이 했었다. 지금은 팀원이 분리해놨지만 처음 채팅 쪽은 Service 로직이 하나에 다 몰려 있어서 repository가 10개 가까이되는데 이름도 비슷비슷해서 정말 보기가 엄청 어려웠었다. 도저히 커밋 로그로는 못 보겠어서 직접 그 팀원의 브랜치에 들어가서 전체 코드를 보고 리뷰했었다. 그런데, 과거 리뷰했던 그 코드를 찾기가 어려워서 생략을 했지만... 코드를 이해하느라고 정말 힘들었다.
그래서 말을 어떻게 할까 엄청 고민을 했었다. 최대한 어떤 코드로 바꿔달라고, 어떤 식으로 개선될 수 있는 지 템플릿을 주면서 이야기를 했다. 안 그러면 그건 피드백이 아니라 내가 보기 어렵다고 기분 나쁜 감정만을 배출한 게 되기 때문이다. 아래는 예시 중 하나이다.

이거랑 5중 조인된 GPT의 향기가 나는 코드가 있었는데, 요소 별로 모아서 테이블 2개 조인 3개로 분리할 방법을 알려줬었다.
그리고 방법이 아닌 경우에는 개인적으로 메시지를 보내서 최대한 이야기를 했었다. 한 팀원 중에서 직설적으로 했었던 친구에게는 조금 미안하다.다른 건 다 좋은 데 그 팀원의 계속 변명하는 태도가 순간적으로 화가 나서, "너 코드 보는 내가 너무 힘들고 리뷰 예전에 하자고 해서 계속 하는 거야"라고 직접적으로 얘기를 했었다.
어떻게 1학기 친구들이랑 카공 끝나고 밥 먹을 때 같이 가고, 그 친구도 성격이 좋아서 크게 문제 없이 넘어갔지만 미안했다.
세 번짼 프로젝트 규모 관련된 문제였다. 솔직하게 말하면 프로젝트 규모가 컸다. 테이블 개수만 21개였다보니. 팀원들이 일정에 쫓기는 경우가 많았다. 특히 프론트엔드 친구들은 백엔드보다 인원도 적은데 과도하게 하다보니 프로젝트 3일 전부터는 밤을 새다가 몸살이 난 친구도 있었다. 이 부분은 정말 미안했고, 다음 프로젝트에서는 좀 쉬더라도 차라리 구현하는 부분 중 핵심만 만들어야겠다는 생각이 들었다.
그리고, 이벤트 구조로 나중에 코드를 짜봐야겠다는 생각도 들었다. 모임을 만들 때 계좌, 태그, 게시판, 채팅 등 정말 많은 요소가 한 번에 만들어졌었다. 내부 코드인데, 이것보다 더 많은 요소가 아래 숨겨져 있다... 이 부분 때문에 새로운 도메인이 합쳐질 때마다 버그가 발생하는데 다른 부분과도 연관이 되어있어서 다음엔 이런 구조를 만들지 않기 위한 방법을 좀 고민을 해봐야겠다.

이 외의 것들
어느정도 문제가 있었던 부분만 이야기를 했었는데, 팀원들에게 고마웠던 점들도 많았다. 개인적으로 프로젝트 기획 단계에서 초기에 면접을 계속 다니느라 거의 일주일 동안 안나왔었는데, 잘 기획해주고 정리해줘서 기획이 빨리 끝났었다.
팀원들이 밤에 남아있는 친구나 상태 안 좋은 친구들에게 음료수랑 빵 같은 걸 사주는 모습을 보면서 서로 잘 챙겨주는 모습이 보기 좋았던 거 같다.
이런 점에서는 개인적으로 힘들었지만 계속 힘을 내면서 했었던 거 같다.
그리고 팀원들에게 단호하게 말하거나 이해를 못한 부분에 대해서 설명할 때 뭔가 어느 순간부터 내가 말하는 게 답처럼 받아들여지는 느낌이 들었다. 이 때, 뭔가 위험하다는 느낌이 들었다. 나중에 알고 보면 내가 틀린 부분도 많은데 조금 부담스러웠다. 실제로 스터디에서 같이 구현한 개발 내용으로 얘기하는 친구가 있는데, 나중에 내가 말한 부분보다 더 좋은 방법도 있고 문제점에 대해서 알게 됐다. 역시 사람은 이해를 할 때 얘기를 해야지 된다.
내가 점점 순간적으로 오만하게 이야기를 하지 않을까도 경계해야겠다는 생각이 들었다. 뭐랄까... 어느 순간 나에 대한 지적이 사라지니까. 나 혼자만 말하는 기분이 들었다. 내가 그 친구들보다 개발 공부를 오래 해서 그렇지 사실 다른 사람도 충분히 가능하다고 생각을 하기 때문에 틀린 부분이 있는데, 나만 말하니까 뭔가 그런 느낌이 들었다. 정말 말 그대로 그냥 그런 느낌이다.
아무튼 프로젝트를 하면서 이번에는 기술적인 부분보단 외적인 부분을 더 많이 고민해본 것 같아서 난 개인적으로 만족을 한다. 또한, 프론트엔드 팀원이 추가적으로 더 구현을 해보고 싶은 것 같았다. 아쉬운 부분에 대해서 더 만들어볼 수 있도록 기존 파일 받아서 부족한 부분 더 확인할 수 있게 백엔드 환경을 세팅하는 문서나 만들어야겠다...