파트너오피스 리뉴얼, 왜 우리는 리팩터링을 하였는가?
안녕하세요.
올리브영 파트너 스쿼드에서 백엔드 개발을 하고 있는 윤노트입니다.
파트너 스쿼드에서는 말 그대로 파트너사들이 사용하는 플랫폼을 개발하고 있습니다.
해당 내용에서는 우리는 왜 코드 개선을 왜 하게 되었는지
, 코드 개선을 함으로써 얻은 경험
을 공유 합니다.
🤔 잘 돌아가잖아! 근데 코드 개선하는 이유가 뭔데?
🔥 코드가 너무 중구난방이고 코드를 눈으로 쫓아가기 너무 힘들어요!
개인적으로 읽기 좋은 코드가 좋은 코드라고 생각합니다. 읽기가 너무 힘듭니다❗❗❗
🔥 축약어가 너무 많은데 이게 도대체 뭐야?
pnlt
==penalty
entr
==enterprise
onl_brnd_nm
==online_brand_name
간단하게 예로 설명할 용어들이다 처음보면 도대체 이게 무엇을 의미하는지 한 번에 알기가 어렵다.
🔥 기존 기능을 수정하려고 하는데 다른 곳에서도 쓰이는데 이거 수정해도 괜찮은 건가??
위 이미지는 설명 참고용입니다
EtEntrDlexInfoCondition 에 대해서 수정하려고 하는데, 사용하는 API가 4군데인데 1개의 API를 수정하기 위해 3개의 API를 확인해야 한다.
🔥 (코드 분석 후 ...) 뭐야?! 안쓰는 변수, 메서드 잖아?!
선언은 되어있는데 확인해보니 사용하지 않고 있는 변수, 메서드들이 많습니다.
🔥 코드 컨벤션도 다 다른데 어떤 걸 따라서 작성하는 게 좋을까 ?
가져오기 -
getXXX()
,searchXXX()
,findXX()
등록 -persistXX()
,saveXX()
,registXX()
,
수정 -updateXX()
,modifyXX()
삭제 -deleteXX()
,removeXX()
동작에 대한 명명이 일관되지 않아 가독성을 해치고 운영 및 리뷰때 효율적인 코드리뷰가 되지 않는다고 생각하였습니다.
🔥 코드 컨벤션도 이상한거 같기도 하고 축약어도 쓰면안되는데 일단 이렇게 되있었으니 그냥 쓰자!
👨🏻💻 동료 A : 이 생각을 한 순간부터 뭔가 꺼림찍합니다!
🤔 어떻게 코드 개선을 진행해볼까 ?!
처음 코드를 제대로 확인하기 전까지는 왜? 잘 돌아가는데 굳이 개선하려는 이유가 뭐지?
, 금방 바꿀 수 있을거 같은데?
, 여기 이렇게 저렇게 건드리면 될꺼야
와 같은 생각을 했습니다.
하지만 팀원들과 다같이 모여 직접 코드를 보고 자! 이제 코드 개선해보자
라고 생각하자마자 우리의 생각이 틀렸다는것을 알게되었고.
고민에 빠지게 되었습니다.
코드 개선을 하긴 할 건데, 도대체 이 코드를 어디서부터 어떻게 나눠서 진행 해야되지 ... 뭐부터 건드려 볼까??
결론은 결국에는 한번 건드려야 할 코드이기 때문에 이참에 다 변경하자!
였습니다.
코드 개선 순서는 다음과 같았습니다.
1. 코드 스타일 통일 - 모든 개발자가 작성하는 코드 스타일이 다르기 때문에 1~2명의 개발자가 개선작업을 진행 코드 리뷰를 통해 코드 스타일 컨벤션 통합
2. 범용적으로 사용되는 클래스 제거
3. 사용되지 않는 변수 및 메서드 제거
4. String 으로 관리하는 상태코드값에 대해서 Enum 변경 및 매직 넘버 변경
5. 불필요하게 호출되는 메서드 분석 및 비즈니스 로직 개선
1. 팀 코드 스타일 컨벤션 정의
가장 먼저 진행한 일은 코드 스타일 가이드
를 정리하는 것이었습니다. 개인마다 작성하는 클래스명, 변수명 등 스타일이 다르기 때문에 이러한 차이가 이후에 다시 한번 코드 개선을 진행하게끔 하는 관리 포인트가 될 수 있다고 생각하였습니다.
위 와 같이 총 10가지 내용으로 된 컨벤션과 총 61번의 코멘트와 함께 팀 코드 스타일 컨벤션이 정의되었습니다.
2.범용적으로 사용되는 클래스 제거
두 번째로는 범용적으로 사용하기 위해 만든 클래스를 개선 대상으로 진행하였습니다.
위 사진은 필드명은 블러처리를 하였으나 각자 다른 필드를 의미하는 변수입니다. 사진으로 다 보여 드리지는 못하였지만 EtEntrBaseEx가 상속과 함께 포함하는 필드가 158개였습니다.
EtEntrBaseEx - 70개, EtEntrBase - 64개, BaseCommonEntity - 24개
모든 기능과 필드를 하나의 클래스에 넣어서 처리하는 것은 운영 및 수정이 어렵고, 확장성과 재사용성을 저해할 수 있는 요소로 판단하여 각 기능에 대한 도메인으로 분리하였습니다.
VO로 묶을 수 있는 필드들에 대해서는 VO로 재정의하며, 더는 사용하지 않는 필드들은 사용 유무 확인 후 과감하게 제거하였으며, 도메인을 재정의하였습니다.
3. 사용하지 않는 변수 및 메서드 제거
세 번째는 사용하지 않는 변수와 메서드를 제거하는 일이었습니다 .
아래 코드는 실제코드가 아닌 샘플로 작성된 코드입니다! 실제로는 조금 더 많았습니다.. :)
위 사진처럼 코드를 분석해보니, 전혀 사용하지도 않는 변수 및 메서드들이 숨어있었고 해당 내용을 삭제하였습니다.
(IDE가 매우 친절하게 잘 알려주었다.. 😄 하지만 혹시 몰라서 2차적으로 확인 진행. )
4. String 으로 관리하는 상태코드값에 대해서 Enum 변경 및 매직 넘버 변경
아래 코드는 실제 코드를 보여드릴순 없어서 제가 비슷하게 만들어낸 샘플 코드입니다.
도대체 O-1000
, D-1000
, D-1001
는 무엇일까 고민하게 되었으며 상태를 의미하는 값들에 대해서는 Enum으로 변경작업을 진행하였습니다.
5. 불필요하게 호출되는 메서드 분석 및 비즈니스 로직 개선
(코드를 직접 기입할 수 없어 설명으로 대체하였습니다.)
개선전 코드의 경우 하나의 Service에 있는 하나의 메서드가 대략 110줄의 코드를 가지고, 변수 설정, 정책 확인, 조건에 따른 값 변경, 예외처리등
모든 책임을 가지고 로직을 처리하고 있었습니다.
따라서 비즈니스 로직에 대해서 정책, 변환 등의 작업을 따로 분리하여 책임분담을 하였으며 함축적으로 사용하고 있는 변수명들에 대해서도
풀어서 작성하여 정확히 해당 로직이 무엇을 하는 내용인지 명확하게 파악할 수 있도록 코드를 개선하였습니다.
👍 그래서 뭐가 좀 나아졌는데??
코드 라인 감소
특정 서비스 로직 파일에 대해서 라인수 확인 시 292 lines -> 119 lines로 60%의 개선된 파일들도 확인할 수 있었습니다.
😭 개선 전
🤗 개선 후
개선되기 전 패키지와, 개선된 후의 패키지의 전체 라인 비교 시 대략 30%의 코드 감소를 확인할 수 있었습니다.
😭 Package 전체 라인 개선 전
🤗 Package 전체 라인 개선 후
String 으로 관리하는 상태코드값에 대해서 Enum 변경
기존에 사용하던 상수들에 대해서 Enum 값으로 변경하면서 상태 값에 대한 내용을 직관적으로 확인할 수 있습니다.
아래 코드는 실제코드가 아닌 샘플로 작성된 코드입니다!
😭 코드값 개선 전
🤗 코드값 Enum으로 개선 후
코드량이 조금 증가하였지만 메서드 및 Enum 으로 변경하면서 재사용성과 코드를 직관적으로 해석할 수 있습니다.
@Lazy 제거
기존 애플리케이션 코드에서 @Lazy의 사용 이유는 자세히 알 수 없지만 추측해보면 @Lazy를 사용함으로 필요할때 Bean을 생성 및 주입하여 사용하려고 하여, 애플리케이션 시작 시 로드되는 클래스와 생성되는 bean의 개수를 줄이거나, Bean간의 복잡한 순환 참조 문제를 해결하려고 했을 것이라고 생각됩니다.
이번 코드 개선에서 제거하기로 한 이유는 다음과 같습니다.
런타임 성능 저하 가능성
예를 들어 @Lazy로 설정한 class의 객체가 생성되는데 5초 이상의 시간이 걸린다고 한다면
해당 서비스 객체가 초기화되지 않은 시점에서 사용하려고 한다면 응답시간에 영향을 줄 수 있습니다.
예상하지 못한 오류 발생
Bean이 제대로 생성되지 않게 된다면 애플리케이션 시작 시점이 아닌 사용 시점에 오류가 발생할 수 있기 때문에 문제가 발생할 포인트를 쉽게 찾지 못할 수 있다.
순환참조를 하는 Bean이 없다
Bean끼리 서로 참조해야하는 순환참조 시에도 해결방법으로 사용할 수 있지만, 우리가 개선하고자 하는 코드에는 순환참조를 하고 있는 Bean 자체가 없다.
Spring @Lazy 관련 내용 공식 사이트에서도 권장하지 않는다.
1. 공식 사이트에서도 위에 작성한 내용처럼 Bean이 잘못 구성되어있는 상태라면 해당 문제를 발견하지 못하고 시작되다가 사용되는 시점에서 문제가 발생하게 된다.
2. 모든 문제는 빠르게 파악하는 것이 좋다. 따라서 애플리케이션의 구동 시간이 조금 늦어지더라도 런타임 시점보다는 컴파일 시점에 확인하는 게 좋다.
3. 초기화 시점에 해당 Bean을 올릴 충분한 메모리가 없는 경우도 문제가 된다. (초기화 시점에 힙 크기를 조정하는 것이 좋다.)
😭 개선 전
🤗 개선 후
Request, Response 개선
Request, Response의 경우 개별 파일로 동작하여 서로 다른 API에서 같은 Response를 사용하여 하나의 API 스펙 변경 시 다른 API에서도 사이드 이펙트가 발생할만한 내용이 있었습니다.
따라서 하나의 API당 하나의 Request, Response를 짝지어서 사용하도록 개선하였으며. InnerClass로 만들어 xxx.Request
, xxx.Response
와 같이 사용하도록 변경하였습니다.
이러한 변경을 통해 기대하는 장점은 다음과 같다고 생각합니다.
1. 명확한 이해
하나의 API당 하나의 Request, Response로 구성되어있기 때문에 빠르게 로직을 이해하고 파악할 수 있다고 생각합니다.
2. 운영의 편리성
Request, Response가 API 하나에 매칭되기 때문에 새로운 기능의 추가, 수정 시 다른 API에서의 사이드이펙트를 걱정할 필요가 없으며.
유연하게 작업이 가능할 것으로 생각합니다.
🧑🏻💻 그렇다면 리팩터링을 통해 우리는 발전했을까?
코드 개선을 통해 우리는 한 발짝 더 나아갔는지 확인해보겠습니다.
서비스에 대한 이해도 상승 :)
글을 쓰고 있는 저의 경우 입사한 지 2개월 후 해당 코드개선을 진행하게 되었습니다.
큰 규모의 서비스를 1~2개월 안에 완전하게 파악한다는 것은 사실 어려운(?) 일이라고 생각합니다. 어디까지나 내 생각..
개선을 하기 전에는 서비스에서 사용하고 있는 API 또는 비즈니스 로직에 대해서 정확하게 이해를 하고 있지 못한상태였습니다. 코드 개선을 통해서 API의 내부에서 사용되는 로직과, 정책 등에 대해 다시 한 번 정확하게 파악할 수 있어 서비스에 대한 이해도가 상승하는 효과를 보았다고 할 수 있을 것 같습니다.
레벨업!
가끔 코드를 작성할 때 나는 이렇게 작성하였는데 이게 맞는 걸까?
, 이 방식보다 더 좋은 방식이 있을 것 같은데 어떻게 해야 할까?
와 같은 고민을 할 때가 있었습니다.
코드 개선하기 위해 팀원들과 리뷰를 많이 진행하면서 궁금증에 대한 해답을 얻을 수 있었고, 개선하기 위해 다양한 자료들을 찾아보며 더 좋은 코드를 작성하며 학습할 수 있었습니다.
또한 다양한 방식의 접근을 통해 우리가 놓친 부분에 대해서도 다양한 시각으로 바라 볼 수 있었습니다.
🧑🏻💻 마무리
이번 포스팅에서는 왜 리팩토링을 진행하게 되었고 2~3개월동안 어떠한 부분에 대해서 진행하였는지, 결과를 통해 얻은 경험에 대해서 공유할 수 있는 좋은 기회가 되었습니다. 블로그를 작성하는 시점에도 다양한 코드들이 고쳐지고 있으며 한번 더 내가 작성한 코드에 대해 돌아보는 시간을 갖게되었습니다.
앞으로 남아있는 레거시들에 대해서도 작지만 조금씩 변경하여 더 좋은 서비스, 좋은 코드를 만들어 내보겠습니다!
마지막으로 파트너 스쿼드의 김XX님의 인스타그램의 글을 가지고 왔습니다. 코드 리팩토링을 진행하면서 작성해주신 내용입니다.
오늘도 파트너 스쿼드는 가장 재밌게 성장
하고 있습니다!
다음 포스팅으로는 파트너오피스 리뉴얼, 기능도 중요하지만, 데이터가 더 중요한 이유!
에 대한 내용으로 찾아오도록 하겠습니다.
감사합니다!
이미지 출처
https://blog.naver.com/shin00512/221889311761 https://blog.naver.com/stripe2773/221348979597