오늘 같이 일하는 친구와 커피를 마시다가 나눈 이야기다. Business Layer에 Interface를 만들어야 할까?
최근 대부분의 자바 웹 애플리케이션의 구조를 보면 Presentation Layer => Business Layer => Persistence Layer와 같이 구현하고 있다. 각 Layer별로 일정한 Naming Convention을 가지고 구현하는데 Business Layer는 Service라는 이름을 사용하는 것이 일반적이다.
이 Business Layer에 해당하는 Service 클래스에 Interface가 필요할까? 현재 진행하고 있는 프로젝트에서는 회원, 빌링과 같이 외부팀과 협업하는 부분은 반드시 Interface를 만들어 연동하고 있다. 하지만 우리팀 내부에서 제어할 수 있는 코드는 Interface를 만들도록 강제하지 않고 있다.
다른 곳은 어떤가? Business Layer에 반드시 Interface를 만들어야 할까? 내가 Business Layer에 Interface를 만들어야 할 것인지 의구심을 가지게 된 것은 Interface의 모든 메소드가 구현 클래스의 메소드와 1:1로 매칭된다는 것이다. 각각의 Interface를 역할에 맞춰 분리한다면 Interface를 분리하는 것에 의미가 있다고 생각한다. 예를 들어 사용자 서비스용 Interface, 관리툴 전용 Interface, 배치 전용 Interface, 모바일 전용 Interface와 같이 분리해서 관리한다면 의미가 있는 작업이라 생각한다. 하지만 대부분의 프로젝트를 보면 Interface 하나에 구현 클래스 하나의 구조로 진행하는데 굳이 Interface를 분리할 필요가 있을까라는 생각이 드는 것 또한 사실이다.
또한 각 Layer별 개발자가 다르다면 Interface를 만드는 것도 의미있는 작업이라 생각한다. 최초 Interface 구현과 구현 Class의 개발 시점이 달라질 수 있기 때문에 병렬 작업을 위해서는 Interface를 통해 분리하는 것이 의미 있는 작업이라 생각한다. 하지만 한명의 개발자가 Presentation Layer부터 Persistence Layer까지 모두 개발한다면 Interface를 만드는 효과는 떨어지리라 생각한다.
한가지 더 고려해야할 부분은 업무 복잡도도 있지 않을까 생각한다. 복잡도가 높은 경우에는 추상화를 통해 Interface를 정교하게 분리해 놓는 것이 좋은 것으로 생각한다. 하지만 복잡도가 낮은 경우에는 굳이 Interface까지 분리하지 않아도 괜찮지 않을까 생각한다.
프로그래밍에서 정답이 있겠는가? 각 프로젝트의 상황에 따라 적정한 원칙을 만들고 그 속에서 일하는 개발자들이 이 원칙을 지켜나간다면 그 방법이 가장 좋은 방법이지 않을까?
Service Layer의 Interface가 정말 필요한 것인지에 대해 같이 한번 이야기해 봤으면 좋겠다. 아마도 지금까지 자신이 경험한 정도에 따라 다양한 이야기들이 오갈 수 있지 않을까 생각한다.
9개의 의견 from SLiPP
Business Layer Interface를 당연하듯이 만들어왔었는 데 현재 저는 프로젝트에 따라 Layer의 양을 축소하면서 하고 있어요. 물론 팀으로 프로젝트를 진행하다보면 윗 분들이 웹 환경 구축에 따라 따라가야하지만요. ^^;
습관적 서비스단 클래스에서 인터페이스를 구현하는것(위에서 나온 인터페이스와 구현 클래스 메서드 1:1매칭 포함)은 당연히 지양되어야
한다고 생각하구요. 예를 들어, 단순히 글 쓰기 같은 행위도 DB에 데이터를 저장하던 것에서 다른 요구사항이 들어와서 파일에 데이터를
쓴다거나 혹은 기타 외부서버와 rest로 통신해서 데이터를 저장해야 할 경우, 기존에 구현클래스에서 가지고 있는 쓰기라는 행위 자체를
인터페이스로 올려서 추상화하고, 쓰기에 관한 다양한 저장행위에 대해선 추상화한 인터페이스를 구현해서 구현클래스로 만들면 좋겠다는
생각이 듭니다. 즉, 유연함과 확장성이 필요한 시점에 인터페이스를 쓰면 되지 않을까 싶네요.
음 저같은 경우 시스템 기본 구조 골격을 잡을때 비지니스 레이어에는 인터페이스 생성을 강제하는 편입니다.
물론 100개를 생성하면 케이스에 따라 인터페이스 형태로 구현해야 되는 경우는 약 30~40% 정도로 한정되어 지지만
비지니스 레이어에 운영과 개발서버에 고객사에서 요구하는 특별한 동작을 수행하는 특정 Class를 상속해야 하는 경우
(성능테스트 툴이라던가, 시스템 로그등)도 간혹 발생하고,
그렇게 구현하는 편이 향후 유지보수성을 위해서도 직관적이게 되더군요 (제 경험에 한정된 것일 수도 있습니다;)
(조금 수고스럽더라도, 그렇게 해 놓는 편이 보험을 들었다는 생각도 괜시리 들기두 하구요 ㅋㅋ)
Interface를 만드는 것 자체는 권장할 만 하죠... 누가 젤 처음 Interface : Class = 1 : 1 이라는 생각을 해 냈는진 모르겠지만... 참 편리하게 살려고 하면 아무 생각 없이 편하게 살 수 있는거 같아요. 전 1:1로 만들 바에야 버리라고 이야기 합니다...
아시다시피 우리 일상생활에서도 인터페이스는 다양하게 존재하고 활용되고 있습니다. 가령 USB용 포트 인터페이스를 장착하려는 TV, PC, CAR 등의 제품들이 그 인터페이스에 맞게끔만 규격을 지켜 만들게 된다면 USB 단자를 사용하여 특정한 물건이나 사물을 한정하지 않고 연결하여 사용할 수 있게 되는 거라 봅니다. 마찬가지로 개발에 있어 인터페이스의 특징 중 하나는 유연한 설계를 보장해 준다는 점일 것입니다. 대부분의 프로젝트에 있어서 확장이나 변경이 되지 않는 경우의 프로젝트는 없을 거라 봅니다. 비록 인터페이스 혹은 추상클래스가 구상 클래스와 1:1 매칭되는 구조라 할지라도 인스턴스 변수를 선언하거나 객체를 파라미터로 받는 메소드에 있어서 자식을 자식한테 담을 것인지 자식을 부모한테 담을 것인지의 문제는 꽤 중요하다고 봅니다. 물론 굳이 상속의 관점에서만 본다면 인터페이스가 아닌 구상클래스를 부모로 만들어서 사용해도 되지만 이것은 메소드를 강제할 수 없기 때문에 앞에서 예시한 서로의 규약 즉 약속이 흔들리게 되므로 별 이득이 없습니다. 이 차이는 코드를 어떻게 생산해 내고 구현할 것인가의 방법론적인 부분까지 포함할 수 밖에 없다고 생각합니다.
저 같은 경우는 인터페이스를 굳이 구상클래스와 분리된 별도의 영역으로 보지 않고 도메인영역의 VO 클래스를 제외한다면 구상클래스를 만들때 인터페이스를 항상 고려하게 됩니다. 좀 억지스러운 해석이 되겠지만 마치 뼈가 있어야 살을 붙이고 살을 어떻게 붙이느냐에 따라 사람, 원숭이 등과 같은 고등동물이 만들어질 수 있는 것처럼 일단 뼈를 만드는 것 부터 시작해야 한다면 구상클래스는 뼈의 역할을 할 수있는 인터페이스를 구현하여 완전한 자신의 구조를 완성해 나가는 것이 좋지 않을까 생각합니다.
헉... 전 습관적으로 서비스단에 인터페이스를 만드는 1인입니다ㅠㅠㅠㅠ 요구사항을 먼저 이해하고 그것들에 따라서 매개변수 타입과 리턴타입을 어떻게할지 정의한 뒤에 그것들을 기준으로 인터페이스에 메소드를 정의하고 구현은 이후에 진행합니다. 생각해보니 일정탓하며 거의 1:1매칭이 기본이 되네요;;
조금 다른 이야기이겠는데요, 제가 경험한 부분을 말하자면 (전 현재 게임을 관리하기 위한 운영툴을 전담하고 있으며 언제라도 DB의 모델들과 query들마저도 변경되고, 로직은 입맛대로(사업부, 게임기획팀, 서버개발자의) 바뀌는 환경에 있습니다. )
A라는 기능을 쓰기 위해 AInterface라는 서비스레이어 인터페이스를 정의합니다. 그리고 AImpl이라는 서비스레이어 구현체 클래스를 만들죠. 그런데 얼마후 AImpl 이라는 서비스레이어를 상속할만한 기능의 A'Impl 을 요구받게 되었습니다. 그래서 AImpl 을 extends 하여 A'Impl을 구현하였죠. (컨트롤레이어는 물론 A와 A' 별로 따로 구현하였고 spring DI를 위해 각기 AInterface를 인스턴스 변수로 선언하였습니다.)
또 얼마후 들어온 요구사항은 AImpl과 A'Impl의 비즈니스 로직이 변경되어야하는 걸 받았죠. (예를 들어 소량의 객체를 담고있는 리스트타입의 객체를 리턴해야할 때에 중복이 없어야 한다거나 sort를 달리해야 한다거나 등으로 새로운 쿼리를 작성하기 아까운 그런 것이죠;;; 사실 이럴때엔 많은 고민이 있었죠;;; DAO를 수정해야하나;;;) (물론 오버라이딩같은 방법이 있긴 하지만 나중에 또 다른 요구사항으로 인해 다른 걸 상속받아야 하는 클래스로 재설계 로직을 요구받았죠. 그땐 이미 A'Impl 클래스는 AImpl을 extends 하지 않고 AInterface를 implements하고 있어서 다행이었습니다;;)
전 A'Impl 구현 클래스가 AImpl 구현 클래스를 extends 하는 것을 걷어버리고 AInterface를 implements하여 AImpl을 다시 작성하였습니다. (물론 나중에 시간날적마다 게임기획문서와 게임플레이, DB를 뒤져가며 변하지 않을 것 같은 겹치는 로직만 따로 빼어 부모클래스로 만들었죠;;)
이런 경우의 개발이 얼마나 있을진 모르겠습니다만 제 경우엔 저러한 이유들로 service layer에 interface를 만들어두는 것이 유용하였습니다;;
Business Layer 혹은 Service Layer에 Interface를 만들려면, Interface 설계가 우선 나와야 합니다. 물론, 해당 설계는 변화에 대응하기 쉬운 (변화가 없을 수는 없으니) 방향으로 가야 겠죠. 해당 Interface와 "행위 그룹"이 다른 영역이 생긴다면, 다른 Interface를 만들고 다중 구현을 적용해야죠.
작은 회사, 큰 회사 가리지 않고 제가 겪었던 대부분의 프로젝트에서는, 이 부분에 대한 설명 및 선행 이해 없이, 책에 나와 있는 것을 그대로 적용하다 보니, 1:1 매칭이 되어 버리는 경우가 허다했습니다. 결국, 특정 행위를 추가하는데 있어서 Interface 건드리고, Class 건드리고, 컴파일 해 보고, 동작 확인(그게 Unit Test건 사용자가 직접 하건) 하고, 부하만 늘어나더군요. 자기가 뭘 하고 있는지 이해도 못하고.
따라서, 근원적인 부분에 대한 이해 없는 1:1 매칭 Interface는 과부하, 이상도 이하도 아니라는 결론을 내렸습니다. 1:1 매칭은 설명하기 귀찮고 힘들었던(이 부분 이해시키는데 굉장한 노력이 필요합니다.) Convention 담당자가 어쩔 수 없이 내리는 결론이죠. 대부분의 경우에는 "단 하나의 구현체를 사용하더라도 Interface 제작은 강제됩니다." 라는 구체적인 표현은 적지 않았을 듯 하네요. (뭐, 원칙적으로는 알아서 하는게 맞긴 하죠.)
@Kenny 님 의견처럼 Interface 설계가 우선해야 되는 것에 적극 공감합니다. @정수원 @김문수님의 의견처럼 미래의 변화를 위해 미리 Interface를 만들어 두는 것도 유용하다고 생각하다 생각하지만 단순히 미래에 필요할 것이라는 이유 때문에 만들면 지치는 경우가 많더라고요.
그 보다는 @Kenny님이 이야기한 것처럼 Interface에 대한 필요성을 느끼고 만들어 간다면 이 같은 지침이 줄어들지 않을까 생각합니다. 그런 부분에서 1:1로 매칭되는 Interface 설계는 대부분의 경우 Interface의 필요성을 제대로 느끼지 못해서 발생하는 것은 아닐까라는 생각도 해봅니다. Interface 설계를 우선한다면 다양한 형태의 모습이 나올 수 있지 않을까 생각하거든요.
@Kenny에게 질문.. Interface 우선 설계는 어떻게 하면 가능할까? Controller를 Mock 프레임워크를 활용해 구현하다보면 자연스럽게 Interface가 도출되는 경우가 있는데 이런 식의 접근이 괜찮다고 생각하냐? 이럴 경우 개발 방식은 Controller 개발 => Business Layer Interface 도출 => Business Layer Class 구현 => Persistence Layer Interface 도출과 같은 형태로 진행되어야 하는데 이 같은 접근 방식으로 구현해야 된다. 이 같은 접근 방식이 괜찮다고 생각하냐? 그렇다면 도메인 설계는 어느 시점에 하는 것이 좋을까? 애플리케이션을 개발할 때의 개발 순서에 대해 고민해 보지만 쉽지 않네. Growing Object Oriented Programming 보면 위와 같은 접근 방식으로 진행해도 괜찮겠다는 생각이 들지만 생각보다 쉽지 않은 Practice라는 생각도 든다. 이 주제를 새로운 질문으로 정리해서 한번 이야기해봐야겠다.
답이 없을 이야기를 절묘하게 토론꺼리로 끌어내는 능력! ^^
이론적으로는... 인터페이스 설계는 행위의 설계이고 도메인 설계는 실제 업무의 설계라고 생각했을 때....
도메인 설계가 먼저 진행되어서 도메인 설계를 기반으로 행위 도출하여 인터페이스 설계를 하고 ... 실제 구현할 프래임워크에 맞춰서 레이어를 나누는 게 좋지 않나요? ( 물론~ 저도 이렇게 해 본적은 없음. )
@자바지기 형이 말한 컨트롤러 드리븐 개발은 도메인 설계를 실제 조합하거나 컨트롤 할 수 있는 레이어부터 해결해 나가는 방식 아닐까?
개인적으로는 '도메인 설계' , '인터페이스' 라는 딱 하고 나오면 그 순간 '얼음' 이 되는 느낌일세~
의견을 남기기 위해서는 SLiPP 계정이 필요합니다.
안심하세요! 회원가입/로그인 후에도 작성하시던 내용은 안전하게 보존됩니다.
SLiPP 계정으로 로그인하세요.
또는, SNS 계정으로 로그인하세요.