최근 아키텍처에 관한 책을 읽고 있는데 레퍼런스로 나온 글 중 하나로 Hadi Hariri의 글 Refactoring to Functional–Why Class?을 번역했다. 이 글은 함수형으로 리펙토링하기라는 코틀린 연재 중 일부라서 그다지 공정한 느낌으로 쓰여진 글은 아니지만 객체지향이라는 패러다임에서 논쟁점이 되는 여러 부분을 잘 보여주고 있어 옮겨봤다.
함수형으로 리팩토링하기 – 왜 클래스죠?
대학에서
교수: 우린 실제 세계에서 객체로 둘러쌓여 있습니다. 이 객체는 자동차, 집, 기타 등등이 될 수 있죠. 그런 이유에서 객체 지향 프로그래밍에서 클래스를 통해 실제 세계에 존재하는 객체를 연결하는 방식이 매우 쉬운 이유입니다.
2주 후
제이크: 저 이 객체와 문제가 좀 있는데요. 도와주시겠어요?
교수: 물론이죠. 객체를 만드는데 도움이 되는 여러 일반적인 방법이 있는데 요약하자면 명사를 찾아요. 그리고 동사를 찾으면 클래스에서 사용할 수 있는 메소드가 될 수 있어요. 말하는 그대로죠.
제이크: 어 말씀한 내용이 합당하네요. 감사합니다.
신입 제이크
필: 제이크 씨, 당신이 작성한 클래스를 확인했습니다. 좀 크기가 큰 것 같은데요.
제이크: 죄송합니다. 어떤 부분이 문제죠?
필: 음… 너무 많은 책임을 갖는 게 문제에요. 너무 많은 일을 합니다.
제이크: 그리고요?
필: 잘 생각해보세요. 하나에 너무 많은 책임이 있으면 이 부분 하나가 시스템의 많은 부분과 연결되어 있다는 뜻이에요. 즉 이 클래스를 변경할 가능성도 상당히 높다는 뜻이고 그건 무언가를 고장내게 될 가능성 또한 높다는 의미죠. 거기다 단일 클래스를 1000줄이 넘도록 작성하면 물론 30줄 짜리 코드에 비해 이해하기 어려울 것이고요.
제이크: 맞는 말이네요.
필: 이 코드를 작은 클래스로 나누세요. 각각의 클래스는 한 가지 일만 하고 그 클래스 홀로 쓰여야 합니다.
1년 후
메리: 제이크 씨, 방금 당신이 작성한 클래스를 확인했는데요. 그다지 행동(behavior)이 많이 들어있지 않네요.
제이크: 네, 동작이 Customer
클래스에 속하는지 Accounts
클래스에 포함해야 하는지 확신이 없어서 CustomerService
라는 클래스를 별도로 만들어 거기에 넣었습니다.
메리: 네, 적당한 방법이네요. 하지만 Customer
클래스를 더 이상 클래스라고 보기 어려워졌어요. DTO에 더 가까워요.
제이크: DTO요?
메리: 네, 데이터 전달 객체(Data Transfer Object)요. 클래스와 비슷하긴 하지만 행동이 없는 경우에요.
제이크: 음, 그럼 구조체나 레코드에 가깝다는 말씀이시죠?
메리: 네, 그런 느낌이에요. 클래스를 만들 때는 행동이 있어야 해요. 그러지 않고서는 클래스라고 하기 어려워요. DTO죠.
제이크: 알겠습니다.
2년 후
메튜: 제이크 씨, 이 클래스를 봤는데 특정 구현과 결합(coupled)이 상당히 강하군요.
제이크: 네?
메튜: 음, 지금 Repository
를 Controller
내에서 생성하고 있어요. 이 부분은 어떻게 테스트하시겠어요.
제이크: 음… 시험용 데이터베이스를 사용하면 되지 않을까요?
메튜: 아뇨. 가장 먼저 해야 하는 부분은 프로그램을 클래스가 아닌 인터페이스를 사용하도록 하는 겁니다. 이 접근 방식이 특정 구현에 매여 있지 않은 코드를 장성하는 방법이에요. 그런 후에 의존성 주입을 사용해서 특정 구현을 전달해 사용하도록 하는겁니다. 그러면 구현을 언제든지 필요할 때 변경할 수 있게 되는 거죠.
제이크: 그렇군요.
메튜: 실무에서는 IoC 컨테이너를 사용해서 다른 클래스의 인스턴스를 연결하는 것이 가능할겁니다.
3년 후
프랜시스: 제이크 씨, 이 클래스에 너무 많은 의존성을 집어넣고 있군요.
제이크: 네, 그래도 IoC 컨테이너가 다 처리할겁니다.
프랜시스: 네, 저도 알고 있습니다. 하지만 가능하다고 해서 옳은 방법이라고 말하기는 어렵네요. 이 클래스는 여러 종류의 구현체를 사용할 수 있다고 하더라도 여전히 너무 많은 다른 클래스에 의존하고 있어요. 하나에서 최대 3개로 유지하도록 해요.
제이크: 네, 알겠습니다. 감사합니다.
4년 후
안나: 제이크 씨, 이 클래스 이름은 왜 Utils
인가요?
제이크: 음. 그 코드는 정말 어디에 놔야 할 지 알 수 없어서 그렇게 이름 붙였어요.
안나: 그래요. 이미 그런 코드를 위한 클래스가 있어요. RandomStuff
라는 이름이에요.
맥주 마시며
제이크: 피터, 내가 생각해봤는데 말이지. 학교에서 배울 땐 객체로 생각하고 명사를 분석하라는 등 기법을 얘기했는데 말야. 그러고 나서는 이름을 잘 붙였는지, 작게 작성했는지, 단일 책임으로 작성했는지, 너무 많은 의존성을 주입하고 있는 것은 아닌지 생각해야 한단 말이야. 이제 와서는 동시성에 좋지 않다고 상태를 갖지 않는 코드를 작성해야 한다고 말하지. 처음부터 궁금했는데 이럴거면 도대체 왜 클래스를 사용하는 걸까?
피터: 헛소리 하지 마 제이크. 만약 클래스가 없다면 어디에 함수를 선언할 수 있겠어? 맥주나 한 잔 더 마실래?
다음 시간에 계속.