본문 바로가기

Conference

[Conference] 지속 성장 가능한 코드를 만들어가는 방법

컨퍼런스 영상 : 토스ㅣSLASH 22 - 지속 성장 가능한 코드를 만들어가는 방법

 

* 해당 블로그의 모든 내용과 사진은 위 영상을 참고하여 작성하였습니다.

 

해당 영상에서 소개하시는 토스 페이먼츠 개발자분들은 

코드의 품질에 대해 지속적으로 관심을 갖고, 확장 가능한 방식으로 코드를 관리하고있습니다.

또한 처음부터 최고의 설계나 품질을 유지하려는 것보다 최소규칙을 지켜 동작하는 소프트웨어를 빠르게 만들고, 코드에 관심을 두고 성장시켜나갑니다.

 

지속성장 가능한 코드를 만드는 방법

Import 문을 자주 보시나요?

 

요즘의 IDE에서는 켜지자마자 import 문을 접어놓는 경우가 많습니다. 

한번 import 문을 열어볼게요.

 

 

해당 영상의 예시코드입니다. 코드를 보았을때 이질감이나 이상한 부분이 보이시나요?

Package

코드에 대해 이야기를 나누기 전에 일상생활에서의 예시를 들어볼게요.

 

햄버거 세트를 포장한다고 가정하죠. 

햄버거를 포장지로 감싸고, 콜라를 컵에담고, 감자튀김을 곽에 담습니다.

하지만 이런 포장에대해 생각하지 않고 하나의 비닐봉지안에 햄버거, 콜라, 감자튀김을 담는다면 어떻게 될까요?

아마 먹지못할것입니다. 그럼 일반적으로 햄버거 포장, 콜라와 컵과 빨대, 감자튀김과 곽 으로 잘 포장이 되었다고 생각해볼게요.

그런데 갑작스럽게 국가 정책이 바뀌어 빨대를 쓰지못한다면 어떻게 될까요? 

그럼 콜라컵의 모양을 입대고 마시는 형태로 바꾸어야 할것입니다. 

이는 다시 말하면 패키지를 구성할때는 유연해야한다는 것입니다.

 

간단한 예제코드를 보겠습니다.

패키지 구조가 잘 정해졌다고 느끼시나요?

 

 

영상의 김재민님의 개인적인 아쉬운 부분은 카드에 대한 부분이 응집이 잘 이루어지지 않았다고 느꼇다고합니다.

 

 

카드 서비스는 개념적으로 카드에 대한 개념에 속해있는 부분인데, Card, CardPaymentRequest, CustomerCard와 같이 같은 개념의 클래스들을 import를 통해 사용해야 된다는것이 아쉽게 느껴집니다.

 

 

변경된 코드입니다. 

먼저 import 양이 줄어든것이 보입니다. 또한 Card 관련 클래스들의 import 문이 없어졌습니다.

 

기존 패키지가 역할에 따라 나누어져있었다면 현재는 개념에 따라 나누어져있습니다.

여기서 개념기준으로 클래스를 나누었을 때, 한 개념에 클래스가 너무 많다면 어떻게 해야할까요?

import 문을 줄이는게 중요하니 Package를 더 나누면 안될까요?

 

 

당연하게도 import 문을 줄이는데에 초점이 맞춰진다면 이것또한 응집을 깨는데에 작용할 것입니다.

카드에 대한 개념이 너무 커지고있다면 카드안에서의 더욱 세밀한 개념을 나눌필요가 있을겁니다.

 

해당 영상에서는 카드소유에 대한 개념을 새로이 만들어 개념을 세분화 하였습니다.

이럴경우 import이 하나 더 생기게 됩니다. 하지만 이럴경우 카드 하위에 새로운 개념이 생기는 것으로 인식이 가능해집니다.

이렇게 import문에서 신호를 느끼면 더 나은 Package를 구성해갈 수 있습니다.

Layer

해당 아키텍쳐는 토스페이먼츠의 개발자들이 공유하는 아키텍쳐입니다.

 

 

 

그림에서 알 수 있다싶이 Layer의 참조방향Layer 건너뛰지않기의 규칙입니다.

해당 핵심 규칙을 인지하고 Layer 속 코드를 관리해보겠습니다.

 

 

해당 코드는 Presentation Layer에서 Business Layer로 객체를 그대로 전달하고 있습니다.

 

 

위 코드는 Business Layer입니다. 

여기서 import 문에 눈이 가는데요. Layer는 역류하지 않아야한다라는 규칙에 어긋난거같습니다.

그럼 어떻게 해결할 수 있을까요?

 

 

해당 코드는 Presentation Layer에서 Business Layer로 전달할 때, 개념화된 클래스로 변환하여 전달하는 것을 볼 수 있습니다.

 

 

이제야 역순으로 흐르지 않는것을 볼 수 있고, MobileService 라는 서비스가 어떤 다른 개념(PaymentRequest, Store..)에 의존적인지도 한눈에 들어오고 있습니다. 

Module

해당 그림은 토스페이먼츠 개발자들이 공유하는 표준 Module 구조입니다.

 

 

화살표의 방향은 Gradle 구성에서 의존하는 방향을 의미합니다.

회색원은 외부기능을 확장할 때 새로운 Module을 만들어간다는 의미입니다.

 

해당 Module의 분리는 몇가지 장점이 있습니다.

기술을 격리 할 수 있게되고, 각 Module별로 테스트가 가능하며, 역할과 경계를 뚜렷하게 정의할 수 있습니다.

 

예제 코드를 보면서 이야기해보겠습니다.

 

 

해당 코드는 단일모듈입니다.

단일 모듈로 작업을 하게되면 Business 로직안에 특정 라이브러리에 대한 의존이 들어갈 수 있습니다.

여기서 라이브러리는 버전업데이트 또는 내부 요구사항에의해 변경이 될 수있습니다.

이럴때 Business 로직에 라이브러리가 침투되어있다면 Business 로직도 수정해야 하는 상황이 올 수 있습니다.

 

이런 라이브러리에 의존도 import 문으로 확인 할 수 있습니다.

또한 외부 의존을 줄이려면 Module을 분리하고 격리하면 의존성의 침투를 막을 수 있습니다.

 

 

위의 모듈 구조처럼 라이브러리에 대한 부분을 격리하여 사용한다면 위의 import문을 사용하지 못하게됩니다.

이렇게되면 Business 로직은 더욱 뚜렷해지고, 추후 라이브러리 교체시 Business 로직의 영향없이 작업할 수 있습니다.

 

다음 예시는 Store라는 클래스가 Presentation Layer의 클래스로 변환하는 작업을 하고있습니다.

 

 

그럼 아까 보았던 Layer를 역류하는 import문이 생기는데요. 이런 import는 StoreSerive 외에도 Stroe 클래스 내부에 또한 있을겁니다.

 

이럴때는 Presentation Layer 자체를 모듈화시켜 격리할 수도 있습니다.

 

 

위의 모듈에서 Payments API는 도메인을 가지고있는 형태로 존재합니다.

 

 

해당 그림처럼 모듈구조를 변경해보았습니다.

해당 모듈의 구조는 Payments API가 오직 도메인 Module만 알게하고 Storage Module이 어떻게 구성 되어있는지 아예 모르는 형태가 됩니다.

그러므로 Http Response로 Entity를 사용한다거나 도메인이 Http 스펙을 알고있는 문제를  만들지 않을 수 있습니다.

 

 

해당 모듈로 만들어진 코드입니다.

Business Layer에서 Presentation Layer에 존재하는 클래스에 import가 불가능해지고, Spring에 관한 import도 불가한 순수한 코드만이 남은것을 알 수 있습니다,