이벤트 및 로그 레거시 통합 하기 (진행형)

·2024년 12월 4일
0

회사이야기

목록 보기
118/118

거대한 레거시를 개선하는 작업을 진행 중이다.

작업의 이름은 통합 이벤트 라고 불리는 것을 작업중이다.

현재 업무 중인 포워딩 레포는 이제 4년차를 맞이했다.

흔히 코드의 생명은 2~3년이라고 하는데, 그러다보니 삐걱거리고 안되는 부분도 있고
신규 기획으로 인하여 추가 작업해야하는 부분이 있었는데...

바로 운송 상태에 대한 히스토리를 파악하는 것이였다.

말만 들으면 정말 간단하다.
API 한개마다 로그를 찍는 코드만 추가해주면 되니까

인 줄 알았는데, 그냥 구조가 너무 복잡했다.

코드를 작성한 사람들도 많아지고, 사람마다 스타일이 달랐기에 (컨벤션이 없었음 초반에..)
Event emitter를 그냥 자기 취향에 맞게 남발(...)하고 있던 것이였다.

근데 이걸 한개로 합치라는 퀘스트가 떨어졌다.

테이블 설계부터

일단 기존의 로그를 모두 담을 수 있는 테이블이 필요했다.

니들은 JSON 컬럼 쓰지마라

  1. 누가 변경했는지 알아야하고
  2. 어떤게 변경됐는지 알아야하고 (테이블 포함)
  3. 왜 변경됐는지 알아야하고
  4. 변경 전에는 무엇인지 알아야했다.

이 4가지를 복합적으로 세팅하여 테이블을 설계했는데
이 마저도 핑퐁이 정말 많았다(ㅋㅋ)

해당 작업은 기존의 포워딩의 키맨인 팀원과 나 2명이서 진행을 했는데
해당 레포를 잘 아는 팀원과, 이제 레포를 보기 시작한 나끝없는 논의로 진행을 했다.

그래서 어느정도 컬럼이 정리가 된 후, 생성을 한 다음에
논의를 계속 이어가다가 이러다간 끝이 나지 않을 것 같다고 (왜 변경했는지? 에 대한 컬럼의 네이밍을 정하는데 큰 혼란이 있었다. 근데 이건 현재 진행형일 수도 있음.)
일단 만들어보고 틀리면 고쳐보자! 라는 식으로 진행하기로 한다.

일단 이렇게 만들어보자 (1차)

좀 다양하게 정리가 됐다.

  1. 얼리 리턴이 있을 경우 컨트롤러에서 Event emitter를 쏘고, 아닐 경우 서비스에서 쏜다.
    • 컨트롤러에서도, 서비스에서도 쏠 수 있다는 가정하에 그럼 기준이라도 잡아보자! 라고 해서 낸 결론이다.
  2. 로그를 쌓는 경우 핸들러를 통해서 쌓자.
    • 로그를 쌓는 것은 레포에서 따오는게 아니라, 해당 모듈에서 저장하는 메소드를 통해 사용하자.
  3. 인터페이스의 경우 한개의 폴더에서 정의하자
    • 번잡하게 파일을 호출하는게 보이다보니 너무 불편한 마음이 들었다, 어짜피 모노레포라서 공용으로 사용하는 dto들을 모아놓는 곳이 있기에 그 곳에 자리를 잡기로 했다.
  4. 해당 이벤트 파일은 서비스를 호출하는 모듈 내부에 만든다.
    • 이렇게 안하면 나중에 의존성 꼬이면 답이 없기에..

그렇게 복잡하게 진행을 하고 있었는데........

정확하게 월요일날, 좀 당황스러운 코드를 맞이하게 된다.

3개의 핸들러를 연이어서 호출하는 코드를 PR 리뷰를 하는 과정에서 보게 됐다.

사실 평소에도 2개씩 붙어있는 걸 볼 때 마다 이마를 짚으면서 이게 맞나 진짜? 하고 있었는데
연속 3개가 따라락 붙어있는 것을 보고 그냥 이렇게 진행되는 것은 아닌 것 같다고 홀드를 요청하고

팀원과 논의를 하다보니, 사무실에서 하고 있어서 주변의 팀원분들이 들으면서 이런저런 생각을 잡아주고
어제(화요일) 정리가 좀 됐다.

일단 이렇게 만들어보...죠? (2차)

화요일날 ChatGPT와 다양한 레퍼런스를 보고 어느정도 결정을 하게 됐다.

일단 오버엔지니어링을 하지 않을 것.

이게 제일 중요하다고 생각했다.
솔직히 정답은 이벤트 드리븐 아키텍쳐를 차용하는 것이 맞다고 생각했다.

왜냐하면 서비스 하나에 발생하는 것들이 동시다발적으로 다 다른 엔티티를 건드려서 이게 한개의 메소드로 진행되기엔 쪼끔 애매모호한 감이 있었다.

근데 그럼 유지보수는 누가 할거고 그렇게 변경되는 과정에서의 번잡함은 누가 감당할 수 있지? 라는 생각과
일단 포워딩 도메인 특성상 API 콜이 진짜 엄-청 없다.

그러다보니 굳이 고오급 아키텍쳐를 구현하는 것은 시간적으로도 손해라는 판단을 세웠다.

아무튼 아래처럼 바꾸려고 현재 진행중이다.

  1. 트리거를 통하여 액션이 없고 로그만 저장을 해야하는 경우, 서비스 로직에서 처리한다.
    • 레코드 하나 저장하는 동작을 위해 이벤트로 별개로 빼는 것은 아니라고 생각했다, 솔직히 그거 하나 때문에 느려진다하면 걍 API가 잘못된거니까;
  2. 일단 이벤트의 경로를 한개로 합친다.
    • 위의 이미지를 보면 수많은 경로가 있다보니, 이것을 합치는 것을 기본으로 생각했다.
  3. 이벤트 체이닝이 최대한 발생하지 않도록 코드를 수정하자.
    • 서비스 -> 이벤트 핸들러 -> 핸들러에서 서비스로직 호출 -> 이벤트 핸들러.... 이런 로직이 몇개 있었는데, 그러다보니까 코드의 흐름을 파악하는데 너무나도 큰 어려움이 발생하더라.
  4. 1개의 핸들러만 사용하자, 그리고 그 안해서 N개의 액션을 분리하여 처리하자
    • N개의 핸들러를 보고 정신을 잃어버린 것과는 별개로, 코드를 읽어나가는데 너무 불편하다.

그래서 축을 자리 바로잡고 진행을 하려고 하고 있는데...

과연 이게 언제 끝날 수 있는 것인지 모르겠다...ㅋㅋㅋ

여기서 더 수정이 일어날 것인지
아니면 이대로 깔쌈하게 완료가 될 것인지

올해는 일단 못끝내고(이벤트가 150개가 넘고 이벤트 1개당 액션이 4~6개가 있는 경우도 몇 있다..)
언제 쯤 끝날 수 있을지 모르겠다.

잘 끝낼 순 있나?

이렇게 가볼까요 (3차)

뭔가 이상한 느낌이 많이 들었다.

모듈화를 시켜놨는데, 해당 이벤트 핸들러의 로직 상에서 수많은 모듈에 접근해야하는 불상사가 벌어지고 마는데....
머리를 붙잡고 있던 팀원과 나는 그렇게 헬프 콜을 쳤다.

그러면서 다양한 이야기가 나왔고, 원점으로 돌아갈 뻔 한 문제도 있었다...
아무튼 요약을 해보자면

  1. 이벤트는 하나만 쏘고, N개의 모듈에서 N개의 리스너를 사용한다.

    • 기존에는 한개의 핸들러에서 모든 로직을 작동하려고 했더니 꺼림직한 구조가 만들어졌던 것이다.
      그래서 각각 모듈마다 동일한 이벤트를 읽어내는 핸들러를 제작하여 관심사 분리를 분리하기로 했다. (근데 이거 잘 생각해보면 결국 큐....처럼 처리를 하는 방식이 된다 ㅋ...)
  2. targetGroup은 필요했었다.

    • 이 작업은 통합도 있지만, 히스토리 조회를 위하여 제작을 하던 것이였는데...
      그럴려고 최초의 설계에서는 targetGroup이 있었으나 명확한 용도를 특정하지 못하여 targetEntity로 변경하게 되었다.
      근데 계속 생각하다보니 targetGroup이 존재하는게 맞더라.
      테이블이 다르게 변경되더라도, 결국 흐름대로 화면에 표기해주려면 공통점이 있어야했는데... 그걸 targetGroup으로 구분을 했어야했다, 그래서 일단 컬럼 이름은 그대로 가기로 하고 그룹화가 되어있는 정보를 넣기로 했다.
  3. eventKey를 정확하게 명시하자.
    * 그룹화가 되어있다보니 변경된 이력을 간단하게 확인하려면 json 컬럼을 뒤집어 까야하는데..
    이러면 상당히 번거로워지다보니 eventkey를 정확하게 명시하도록 이야기를 했다.
    맨 처음에는 entity+(컬럼이름+@)+CUD로 가려다가 너무 많은 컬럼이 변경되면 답이 없으니
    entity+CUD로 가기로 일단 정리했다.

    하면 할수록 코드 베이스를 전체를 바라보면서 작업을 해야하다보니 정말 작업이 지지부진하다;

    그래서 둘이서 우에엥 하고 있는데, 뒤에서 CTO님이 원래 그렇게 가는거 맞다고
    큰 일 하고 있으니 다른거 안하고 그것만 하라길래 일단 ㅇㅋ.... 모드로 천천히 진행하고 있다.

    이거 몇차까지 우스갯소리로 이야기를 하고 있는데 5차 내외로 끝나면 좋겠다.

결론

사람들 앞에서 고민을 이야기해보자
러버덕이 되는 한이 있더라도 커다란 도움이 된다

진짜라니까?

profile
물류 서비스 Backend Software Developer

0개의 댓글