Service 리팩토링 회고

아이스__아메리·2023년 4월 21일
0

회고

목록 보기
2/2
post-thumbnail

리팩토링 진행 이유

Service - Impl 이해하고 사용하고 있는가

  1. 추상화를 꼭 적용해야 할까?
    • 아래의 코드로 예시를 보이겠다.
 
public class AdminMemberService implements MemberService	// 관리자 동작 구현체
public class CommonMemberService implements MemberService	// 일반회원 동작 구현체
  • AdminMemberService에서는 사용하지만 CommonMemberService 사용하지 않는 함수들은 비워둔 상태로 놔두어야만 한다. Admin과 일반 유저가 사용하는 함수가 전혀 다르다. 그리고 admin 서버가 따로 띄어져 있다.
  • 조회 로직만이라도 구현체로 바꾸고 싶었지만 조회조건이 달라 필요한 파라미터도 다르게 되어 해당 패턴으로 사용되지 않고 있고 AdminMemberService는 또다른 Service에 의해 상속되고 있어 1:1 관계가 유지중이다.
  • AOP Proxy 사용 - final 혹은 private 생성자를 이용해서 프록시를 만들지 못하게 되고 현재 Service 호출 시 private final 로 service를 선언하여 사용하지 못한다 + CGLIB이라는 대체제가 있다.
  1. 뒤에서도 설명하고 있지만 객체지향적 프로그래밍을 하기 위함이다. 확장에는 유연한 구조이지만 실제로 그 장점을 살려 개발된 부분은 단 한 곳이다.

  2. 제대로 사용하기에는 숙련도가 부족했다고 생각한다. 물론 이러한 이유로 인해 '인터페이스를 만들지 말자'가 아니라 인터페이스를 만들었던 이유를 잘 살리는게 더 바람직하다.

서비스단에서의만 인터페이스의 재사용성 불가

하나의 예를 들어 현재 이미지서비스로 모든 이미지를 등록한다 했을때, 이미지가 필요한 객체가 늘어날수록 분기처리가 많아지게 된다. 분기처리에 따른 코드 가독성은 Service뿐만 아니라 Controller단에도 영향을 미치기 때문에 오히려 복잡하게 꼬일수가 있다. 또한 새롭게 만드는데에 공수가 그렇게 많이 들지도 않는다.

비용, OCP 개방-폐쇄 원칙 위배하는 이유

  1. Service - Impl 패턴을 설계하는 이유는 인터페이스와 구현체를 분리함으로써 구현체를 독립적으로 두고, 구현체 클래스를 변경하거나 확장해도 이를 사용하는 클라이언트의 코드에 영향을 주지 않도록 하기 위함이다.
  2. 리팩토링을 진행하게 된 사실상 주요한 이유이다. Service - Impl 구조로 인하여 불필요한 코드가 약 400줄 이상과 Controller에서 Sevice 코드를 확인하기 위해서 두번의 클릭이 요구가 되는 등 개발하는 데 있어서 시간의 비용이 자꾸만 늘어났다.
  3. 추상화로 인하여 1인 개발 단계에서 읽기 좋은 코드가 아니였다. 내가 개발하지 않은 코드들을 빠르게 파악하기 위해 그리고 개발하는 동안 스트레스를 최소화하기 위해 리팩토링을 진행 하게 되었다.
  4. 객체지향 프로그래밍을 추구하는 것이 아니지만 혼자 개발, 운영을 하는데 있어서 객체지향 프로그래밍을 한다고 가장 싼 비용으로 돈을 벌어다 주지는 않는다. 어떤 방법이 돈을 쉽고 빠르게 벌어줄 것인가도 중요한 요인 중 하나이다.

그 외

  1. 서비스의 대부분을 개발했지만 퇴사자로 인해 내가 만든 코드, 로직을 포함하여 전체 코드 파악하는 것이 리팩토링하는 목적 중 하나이다.

작업 시 고려사항

  • 혼자 백엔드 개발 중이다.
  • 실제 운영 중인 서비스이기 때문에 각 단계별로 배포를 진행하고 하루의 경과 시간을 지켜본다.
  • 서버 적용 순서 1.관리자 서버 → 2.운영 서버 를 순으로 하여 이용자에게 휴먼 에러를 줄인다.
  • 추가적인 요청에 의해 코드 수정발생가능성이 있기 때문에 코드 병합 과정에서 필연적으로 충돌이 일어나기 때문에 배포순서가 달라질 가능성있다. (브랜치를 나누어 배포하면되지만 대규모 작업이라 생각되어 한번의 호흡으로 작업을 마무리 할 계획이다.)
  • impl 상속으로 구현된 서비스로직이 있어 관리자 서버는 해당 로직이 포함된 파일을 제외하고 작업한다. -> 해당 로직은 관련된 다른 로직과 통일성이 없다.

작업 방식

  1. Impl - Service 구조에서 Service 변경
  2. @Transactional(readOnly = true)@Transactional분리

@Transactional 재포장

  1. Service - Impl 구조를 합치는 과정에서 한가지의 작업을 더 추가하고 싶은 부분이 있었다. 전체 @Transactional(readOnly = true)를 감싸고 조회를 제외한 모든 로직에 @Transactional를 재포장 해주었다. 한개씩 비즈니스 로직을 추가하는데는 별로 신경이 안쓰였으나 개발 초기 단계에서나 코드를 다시 읽어볼 때 해당 코드들이 불필요해보였다.
  2. 그래서 일반로직을 다루는 Service와 QueryServcie로 나누어 매번 재포장하는 작업을 하지않게 만들었다.

작업 순서

총 2번의 배포를 진행 (순서가 달라질 가능성 있음)
- 관리자 서버 Service 통합 및 Transational 분리 → 운영 서버 Service 통합 및 Transational 분리

1번의 배포를 진행 (배포전의 테스트 상황이 여유로워 한번에 진행)

  • 관리자 서버 Service 통합 및 Transational 분리
    → 운영 서버 Service 통합

작업 일지

글자수 세기

git ls-files | xargs cat | wc -l  

1차 배포

작업 전 - 18194 -> 작업 후 - 18292
Service 통합 + @Transactional 분리

2차 배포

작업 전 - 18292 -> 작업 후 - 17997
Service 내 로직들이 많지 않아서 분리를 하지않아 Service 통합만 진행

느낀점

1차 배포 후

  • 내가 보는 가시적인 400줄 중에 대부분을 줄인다고 생각했는데 모든 서비스를 통합하지 못해 오히려 늘어났다. 그 줄까지 줄어든다면 작업 전 후로 줄수차이는 거의 없어 보인다.
  • 한 서비스에 많은 로직이 몰려서 읽기가 어려웠었는데 가벼워진만큼 함수들을 찾기가 쉬웠다.
  • @Override 어노테이션으로 쉽게 구별가능했는데 public/private로 구별해야되서 오히려 가독성이 떨어질거 같은 느낌이였다.(전혀 문제 없었음)
  • 위의 결과로 추가로 개발하게 되었을 때 리팩토링이 성공 유무를 알 수 있을거 같다.

2차 배포 후

  • 로직이 너무 적은편이라 @Transactional 분리를 하지 않았는데 리팩토링 직후 기능추가 배포하기전 확인 과정에서 코드가 금방 확인이 되어 편했다.
  • 로직이 많거나 Service 내 함수가 너무 많아졌을 때 @Transactional 분리를 진행하면 좋을거같다고 생각했다.
  • 중간중간 코드 줄수를 확인 했을때 로직이 별로 없을 때에는 오히려 코드 줄수가 늘어나는 경우도 있었기 때문에 오히려 중복되는 코드가 많아진 경우를 보아 특정 함수의 갯수로 판별하여 분리하는 작업을 진행하면 더 좋을거 같다.(현재 기준 27인치 모니터에서 스크롤 이동 두번하면 답답해져서 코드를 읽는데 불편해지면 분리)

최종 후기

  • 개발 시간이 확실히 많이 줄어들었다. 잠시지만 1인 개발 체제가 될 거라는 생각에 가장 먼저 시간 비용의 감소를 시켜 다행이라고 생각한다.
  • 신규 기획에서 가장 뚜렷한 비용 감소의 체감이 들었다. DB설계부터 이전 코드들의 파악, 비즈니스 로직 설계 등 설계나 패턴을 지키기 위해 다른 코드들을 자주 파악하는 하는 편인데 빠르게 파악 후 개발하여 프론트 두명과 협업이 아주 순조로웠다. (심지어 추가적인 개발도 진행할 수 있을정도로 여유도 가짐)
  • 객체지향적인 프로그래밍을 위해 다시 되돌아갈 계획 수립필요 - 언제까지나 혼자 개발하는 것도 아니고 기조가 다시 객체지향으로 바뀌게 되면 이 작업 오히려 부채에 해당하게 될거라는 것을 명심해야 한다.
    최근 MSA 아키텍쳐를 많이 사용하게되면서 Service-Impl 구조는 잘 사용하지않는다고 한다.
profile
츠케멘 좋아

0개의 댓글