토스 - 지속 성장 가능한 코드를 만들어가는 방법

본 글은 토스ㅣSLASH 22 - 지속 성장 가능한 코드를 만들어가는 방법을 정리한 글입니다.

  • 토스페이먼츠는 코드에 대해 지속적으로 관심을 갖고 확장 가능한 방식으로 관리하고 있음
    • 처음부터 최고의 설계나 품질을 유지하는것 보다는 최소 규칙을 기반으로 동작하는 코드를 빠르게 만들고 코드를 성장시킴
      • 이를 위해서는 코드 한줄 한줄의 의미가 중요
    • 소프트웨어 운영에서 가장 중요한건 코드

code

  • 위 코드만 보면 어떤 느낌이 드는가?
    • 구현 내용은 없지만 생성자를 통해 의존 클래스 확인이 가능하고 대략 무엇을 할지 추측 알 수 있음
    • 이처럼 생성자는 무슨일을 할지 힌트를 줄 수 있음
      • 추가적으로 꼭 필요한 의존인지, 너무 과한 의존이 아닌지 생각해볼수 있음

code

  • 그렇다면 import 문을 보면?
  • 오늘 발표에서는 import문을 동해 어떤 부분이 아쉽고 어떤 방향으로 개선가능한지 다음과 같은 사항을 통해 얘기해봄

Package

code

  • 대략적으로 생성자를 통해 무엇을 할지 예상할 수 있음
  • addPayment 로직도 흐름이 보이는것 같음

code

  • import문을 함께 보면?
  • Card에 대한 응집이 잘 이루어지지 않았다고 느껴짐
    • CardService에서 Card관련(*Card*네이밍) 클래스 사용을 import를 통해 사용해야하는게 아쉬움
      • CardPaymentProcessor, CardVaildation, CardReader, Card, CardPaymentRequest, CustomerCard
    • 이유는 component, vo, service와 같이 역할별로 package가 구성되어있기 때문에

code

  • 위와 package 구조를 변견하면?
    • CardService가 의존하고 있는 클래스들이 CardService클래스 가까운곳에 응집되어있음
      • import문이 줄어듬
    • 역할이 아닌 개념끼리 더 뭉쳐진 느낌
  • 개념기준으로 package를 응집시켰을때 한 개념에 너무 많은 클래스가 포함되면 이것 또한 응집이 깨지는 결과를 초래함
    • 이경우 적절한 시기에 개념을 더 세분화해야함

code

  • 소유개념(card.owner)을 추가
    • 다른 package가 아닌 카드 하위에 새 개념을 추가함

Layer

  • layer속에서 코드를 관리할때 import문의 신호를 알아봄

layer

  • 위는 토스페이먼츠의 표준 layer 규칙
    • 위에서 아래로 순방향만 참조 가능
    • 참조 방향 역류 금지
    • 건너 뛰기 금지

  • presentation의 import는 별 문제 없어보임

  • business layer에서 presentation layer의 request객체를 그대로 받음
    • business layer -> presentation layer 참조 발생
    • 역류 금지 규칙이 지켜지지 않음

  • presentaiion layer에서 business layer로 전달할때 개념화된 클래스로 변환하여 전달

  • business layer -> presentation layer 참조가 제거됨
  • layer에 대한 잘못된 참조는 코드의 복잡도를 높이고 확장에 발복을 잡거나 문제를 만듬
  • import 또한 코드로써 계속 주의를 기울여야함

Module

  • module관계에서 import문의 역할

  • 위는 토스페이먼츠 표준 module 구조
    • 화살표는 Gradle 구성에서 의존하는 방향
    • runtime시 runnable한 module을 중심으로 의존성이 주입되어 실행됨
    • 회색원은 외부기능 확장에 대한 모듈
  • module분리의 장점
    • 기술 격리
    • Module별로 테스트가능
    • 역할과 경계를 뚜렷하게 정의 가능

  • 위처럼 단일 Module로 작업하면?
    • 비즈니스로직에 특정 라이브러리(외부에서 관리하는)의 의존이 침투
    • 버전업 또는 내부 요구사항에의해 라이브러리 관련 변경이 필요하면 비즈니스로직도 같이 수정되어야함
    • 토스페이먼츠 처럼 라이브러리에 대한 부분을 격리하면 의존성 침투를 막을 수 있음
      • 추후 라이브러리 교체시 비즈니스로직 수정없이 가능

  • Payments API는 도메인을 가지고 있고 API 서빙을 위해 Spring에 대한 의존성을 가지고 있음
  • 도메인 코드가 잘 정리되어있고 외부 의존성 격리를 잘 했다면 다음과 같은 구조로 변경가능

  • domain module이 payments module로 부터 분리 되었고 Spring과 멀어져 의존성이 격리
  • 선택에 따라 storage module 은닉가능
    • payments module -> storage module 의존시 runtime 의존
    • storage는 domain에 대한 구현체 역할만 수행
    • payments API는 domain module만 알고 storage module의 구성/구현체를 모름

  • 결과적으로 business layer에서 presentaiion layer에 존재하는 클래스 import 불가능
    • spring import 또한 불가능, 순수한 코드만 남음

마무리

  • 토스페이먼츠가 코드에 관심을 두는 이유
    • 통제, 제어
      • 소개된 3가지를 기반으로 코드를 통제
      • 이를 기반으로 소프트웨어를 제어
      • 이를 통해 지속 성장 가능한 소프트웨어 개발
        • 수명이 긴 소프트웨어 개발

정리

  • 역할이 아닌 개념(도메인)단위로 package구성을 하면 import문(다른 클래스의 의존성)을 보고 응집도 파악이 가능
  • import문 또한 코드임, 계속해서 주의를 기울여야함
  • 모듈 구성을 통해 외부의 변화로 부터 안전하고 통제가능한 비즈니스 코드 작성가능
updated_at 23-08-2022