직방에선 디자인 패턴을 어떻게 이용하는가?

박준하
직방 기술 블로그
7 min readMay 11, 2021

--

디자인 패턴은 무엇일까요? 그리고 프로그래머는 왜 디자인 패턴을 꼭 알아야만 할까요? 간단한 프로젝트만 만든다면 쉽게 답하기 어려운 문제입니다. 프로젝트의 구조가 단순하거나, 로직이 적고 명료하다면 굳이 디자인 패턴에 심혈을 기울여 코드 구조를 짤 필요가 없기 때문이죠. 하지만 고려해야 할 요소들이 많아질수록 코드는 지저분해지기 쉬워지고, 재사용하기 어려워집니다. 스스로 열심히 생각해서 독특하고 창의적인 객체지향 방식의 코드를 짤 수도 있지만, 여러 사람이 협업하는 코드라면 한번에 알아듣기 힘든 코드는 좋은 코드라고 말할 수 없죠.

이런 점에서 잘 알려진 디자인 패턴을 적용하면 아래와 같은 장점이 있습니다.

  1. 다양한 사람들과 협업할 때 서로 쉽게 코드를 이해할 수 있고, 동료가 작성한 코드라도 빠르게 적응하고 사용할 수 습니다.
  2. 유지 보수 측면에서도 버그를 최소화할 수 있는 이점을 가져갑니다.
  3. 코드 재사용성도 높아집니다. 코드 작성에 드는 시간 또한 기존에 존재하는 패턴을 각자 상황에 맞게 그대로 적용하면 될 뿐이기 때문에 훨씬 개발 시간을 줄일 수 있다는 이점도 존재하죠.

디자인 패턴은 여러 개발자들이 각자의 경험을 토대로 만들어진 검증된 방식입니다. 모든 경우라고 단언할 수는 없지만, 각자가 생각한 방법보다는 좀 더 효율적일 가능성이 높습니다. 커뮤니케이션에서도 디자인 패턴을 적용하면 더욱 효과적입니다. 어떤 로직을 개발한다고 했을 때, “이 부분은 Singleton 패턴을 사용하죠” 라고 말한다면 해당 패턴을 알고 있는 개발자 사이에선 대략적인 코드의 모습을 바로 그려낼 수 있습니다. 따라서 추가 설명을 위한 의사소통 비용이 줄어 듭니다.

직방에서도 다양한 디자인 패턴을 활용하여 코드를 작성하고 있습니다. 오피스텔, 원룸, 아파트 등 여러 카테고리로 나누어져 있으나 사실 매물별로 그렇게까지 다른 작업을 하지 않는 경우 Factory pattern을 적용하여 더 깔끔한 코드를 만들기도 하구요. 이번에 신규로 도입한 문의기능 또한 여러 다양한 문의가 있을 수 있지만 Builder pattern과 Factory pattern을 조합해 깔끔하게 코드의 역할을 분리하기도 합니다. 이 글에선 직방에서 어떻게 패턴을 활용하는지, 그리고 패턴을 왜 활용하는지 간단하게 이야기해 보려고 해요.

사실 가장 빠르게 코드를 쓸 수 있는 방법은, 주어진 인풋과 아웃풋이 있다면 해당 인풋을 적절히 가공하여 빠르게 아웃풋으로 내놓는 것입니다. 예를 들면 다음과 같은 API가 필요할 수도 있어요.

매물의 id를 줄 테니, 해당 id별로 매물 타입을 파악해 원룸이면 가격 정보와 위치를, 오피스텔이면 빌딩의 이름과 건설연도에 해당하는 정보를 보여줘

아주 간단한 명령입니다. 직관적으로 여기서 우리는 해당 엔드포인트에서 DB를 뒤지고, id에 해당하는 물건을 가져왔으면 item.type과 같은 형태로 물건의 타입을 분리해낼 수 있어요. 그렇다면

이런 종류의 의사코드를 작성할 수 있습니다. 그런데 만약 ItemType 이 2개가 아니라 10개가 된다면 어떡해야 할까요? else if 를 8개 더 붙이면 될까요? 아니면 switch를 쓰면 될까요?

물론 앞서 말한 방법으로도 문제를 충분히 해결할 수는 있습니다. 하지만 해당 방법을 이용하면 결국 수정하는 위치가 일정하지 않고, 다양한 item.type을 쓰는 모든 지점을 접근해 고쳐야해요. item.type이 원룸인 경우 해당 부분의 코드를 고치기 위해서 동에 번쩍 서에 번쩍 코드를 옮겨다녀야 한다는 것이죠. 이건 개발자가 쉽게 놓칠 수 있는 구멍을 만들어내고, 하나의 책임만 다하는 service의 측면에서 봤을 때에도 좋지 않습니다.

또 저렇게 if를 이용한 부분은 다른 사람이 와서 이해하기에도 어려움이 있습니다. 지금이야 아주 직관적으로 item의 내용을 그대로 리턴한다지만, 만약 중간에 item.user의 authentication 내용이 들어간다던가, 아니면 또다른 추가적인 작업이 진행된다면 서비스 api 하나를 이해하는 데에만 많은 시간이 소모될 것입니다.

결론은, 간단한 서비스의 경우 이렇게 개발해도 무방하지만 다루는 객체가 복잡하고 써야 할 로직이 많아질수록 단순하게 이런 방식으로 if — else를 이용하여 작성하는 것은 지양해야 한다는 겁니다!

직방은 이런 형태의 문제를 주로 Factory pattern을 적용하여 해결하고 있습니다. Factory pattern은 그 구조가 이해하기 어렵지 않아 쉽게 사용할 수 있어요.

Factory 패턴은 다음과 같은 구조로 설명할 수 있습니다. 실제 로직을 쓰는 Service에서 Factory를 부르고, 해당 Factory는 들어간 변수에 따라 동일한 abstract class를 상속받는 여러 content를 상황에 맞게 불러주는 거죠.

아까의 경우로 돌아와봅시다. 해당 문제에 Factory pattern을 적용하면 어떻게 코드가 바뀔 수 있을까요?

ItemService

ItemFactory

BasicContent

ItemContent

OfficetelContent

이렇게 여러 개의 파일로 분리해낼 수 있습니다. 비록 파일은 많아졌지만, BasicContent를 이용해 사용할 함수의 명세를 정의하고, 각 구현은 실제 타입별 클래스에 모아 정리할 수 있기 때문에 유지보수 측면에선 훨씬 더 깔끔한 모습입니다.

실제 서비스에선 해당 팩토리를 불러내서 단순히 함수에 값만 집어넣기 때문에, 용도분리도 명확한 모습이구요. 이렇게 Factory pattern을 이용하면 유지보수가 용이하고 재사용성도 높은 퀄리티 높은 코드를 지속적으로 작성해낼 수 있습니다.

만약 여기서 해당 함수를 구현하는 데에 서비스 변수가 여러 가지 필요하다면 어떻게 할까요? 실제로 직방에서 문의하기를 눌렀을 때 문의하기의 내용에 따라 알림톡을 발송하는데요, 알림톡의 종류에 따라 수신자 번호, 내용, 가격 정보, 보내는 시간 등 다양한 정보를 필요로 합니다. 모든 정보를 chunk해서 위 Factory 방식처럼

이와 같은 방식으로 보낼 수도 있지만, chunk의 type이 지정되어 있지 않다는 점, 그리고 만약 해당 chunk를 model로 만든다 하더라도 많은 부분이 nullable이여야 하기 때문에 지저분하다는 점, 그리고 항상 chunk를 만드는 것은 redundent하다는 점 등이 문제가 됩니다. 이 경우, Builder pattern을 같이 고려할 수 있습니다.

Builder는 다음과 같은 생김새를 띠고 있습니다. Builder를 호출한 뒤, 해당 Builder에 존재하는 갖가지 setParam으로 실제 사용하는 Content class에 사용변수를 할당해 주는 것이죠.

왜인지는 모르지만 아까 만들었던 함수에 원룸의 경우는 매물을 올린 중개사의 핸드폰 번호가, 오피스텔의 경우는 중개사의 핸드폰 번호와 고객 정보까지 같이 보내야 한다고 해봐요. 그렇다면 Factory로 만드는 것 이상의 작업이 필요합니다.

ItemService

ItemFactoryBuilder

ContentAbstract

ItemContent

CommonFactory

다음과 같이 Factory에선 routing을 하고, 이를 builder 클래스를 통해 변수를 각각 넣어준 뒤 사용할 수 있습니다. 이를 통해서 각 변수를 깔끔하게 전달해줄 수 있고, 다양한 변수들을 몽땅 model로 만들어서 난잡하게 model을 쓸 필요도 사라지는거죠!

이렇게 다양한 디자인 패턴을 적용하여 쉽고 빠르게 효율적인 고품질 코드를 작성할 수 있습니다. 더 많은 디자인 패턴도 한번 찾아보시고, 각자 코드에 직접 적용해보세요!

--

--