이 질문에 대한 계기
인프콘의 2024의 영상이 다시보기가 올라온 후 바로 토비님의 세션을 출근길에 들었다.
제목은 클린스프링 으로 이 영상에 대한 후기라기보다는 마지막에 나온 서비스 레이어에
인터페이스 사용 여부에대해서 답변을 해주신것이 최근 서비스클래스에 인터페이스를 사용하지않는
분들이 계시는데 그냥 사용을 하시라고 강조를 하신 부분에 내가 찔려서 질문을 드리고 싶었다.
평상시 인터페이스와 구현을 분리해 인터페이스를 사용면서도 인터페이스를 사용하면서 큰 생각없이 그냥 하나의
유지보수를 할것이 늘어난 기분이 들며 작업을 했던 기억이나는 와중에 평상시 챙겨보던 이번 인프콘 2024의 다른세션의 발표자이시기도 한 제미니님의 한 영상을 보았었다.
ServiceImpl 쓰지 말자 feat. interface를 소중히 - YouTube
이 영상에서는 인터페이스의 중요한 이름을 사용하고 구현체의 서비스에 impl을 붙이는걸 선호하지
않는다. 굳이 인터페이스를 작성을 해야하나? 라는 부분이 크게 공감이 되었고 평상시 들었던 생각에
기폭제가되어서 더 이상 나는 인터페이스를 사용하지않았다.
물론 회사에서는 기존 아키텍처가있기에 계속 사용을 했고 내가 따로 하는 사이드프로젝트 에서는
사용을 하지 않았다.
이런 많은 경험을 가진 개발자분들의 의견이 서로 다른? 모습에 궁금증이 막 생겨 평상시 있던 토비님의
디스코드에 아래와 같은 질문을 남겨 토비님의 세션에서 말씀을 못해주신 자세한 답변 을 듣고싶었다.
질문
오늘 출근길에 인프콘의 토비님의 세션을 다 듣고 궁금증이 남아 질문을 남겨봅니다. 마지막 부분에 서비스레이어의 구현체가 1개여도 그냥 무조건 인터페이스를 사용하라고 강조를 하셨는데 그거에 대한 이유를 여쭈어봐도 될까요?
제가 많은 프로젝트 경험이 있는게 아니어서 사실상 애플리케이션의 핵심인 서비스레이어의 구현체가 통체로바뀌는 경험을 해본적이 없어서 구현체가 전부 바뀌는 경우를 생각해서 확장성있고 편하게 변경을 하기위해서 라고 이유라고 추측중인데 토비님의 생각이 궁금합니다...!
토비님의 답변
우선 인터페이스의 구현체가 하나뿐이라는 전제 자체가 틀렸다는 묵직한 팩트를 말씀하시면서
답변을 시작하신다 ㅎㅎ
평상시 스프링을 사용하면서 아주 예전에 공부했던 AOP의 프록시방법에 대한 cglib에 대한 문제점이 있다고 말씀을 해주신다
@Transactional cglib 방식의 프록시 생성의 문제점
생성자의 두번 호출 문제는 해결
상속클래스의 생성자 호출시 자바에서는 수퍼클래스의 디폴트 생성자도 실행이된다.
요즘은 클래스에서 생성자를 의존성을 주입받아서 사용을 하는 방식으로 사용을 많이 하지만
레거시라던가 토비님의 말씀대로 중요한 초기화작업 진행시 문제가 발생할 수 있다.
토비님의 디스코드에 참여하고 계신 분께서 잘못된 정보를 정정해주셨습니다!
클래스의 final 지정 불가
이는 자바를 사용하는 나에게는 그렇게 크게 와닿지 않았지만 토비님께서 코틀린으로 비유해주신것에 확 와닿았다.
클래스의 기본이 final (이것도 처음 알았다....)이고 상속은 일일히 open 키워드를 붙여야한다니
게다가 프록시 생성이아예 안되는 문제가 있는 경우도 있다고 말씀을 하셨다.
이는 자바를 사용하다 코틀린으로 변경 시 엄청난 삽질을 할 게 바로 상상이 간다...
이는 Transactional 어노테이션만 사용해도 구현클래스가 최소2개가 되고 사용시 이런문제가 있다는 토비님의
답변에 생각지도 못한 답변이여서 정말 내가 모르는게 많구나라는 생각이 많이 든 부분이였다..
ISP 원칙 위반
요청자와(컨트롤러) 응답자의(서비스)의 역할로 보았을때 어떤 인터페이스(메소드)를 사용하라고
명확한 가이드를 주는 것이 인터페이스에 가장 큰 장점 중 하나라고 생각을 한다.
컨트롤러가 분리하지 않은 하나의 큰 서비스 클래스를 사용할 경우 의존관계 자체가 매우 엉키고
컨트롤러 클래스에서 알 필요가 없는 메소드들을 가져다 쓸 수가 있는 문제도 발생합니다.
소프트웨어에서는 접근같은 것을 자유롭게 풀어주는것보다는 요청자의 권한과 사용방식에 맞게만 해서
최소한만 열어주는것이 좋은 소프트웨어라고 생각을 한다.(의존성,인증 인가 등등)
컨트롤러는 서비스클래스 같은것인지 따로인지 알필요가없고 테스트가 간결해지며 람다식 하나만 필요하고 Mock처리도 필요가 없다고 말씀을 하신 것은 레거시 코드 활용전략 에서도 나오는 Fake(Stub) Java 객체를 만들어 테스트를 빨리 실행시키는 방법을 말씀하시는 것으로 나는 유추를 했다.
이는 Mocktio 관련 객체들이 필요가없어져 테스트 속도가 정말 빨라진다!
클래스의 적절한 책임
구현 서비스 클래스는 시간이 지나면서 기능이 많아지거나 의존하는 다른 서비스나 리포지토리 등등 늘어나는 시점에 분리를 하신다고한다. 인터페이스 단위로 쪼개면 잘 맞는다고 하시고 그럴 경우에도
컨트롤러쪽 코드는 수정할 게 없다고 하신다.
스프링 시큐리티로 비유를 하신것은 UserDetails , UserService 이 두가지의 인터페이스를 제공을 해 준다.
이로 인해 개발자들은 이를 상속해 프레임워크의 표준으로 개발을 한다.
하지만 토비님의 말씀처럼 사내에서 다른 표준을 만들어 분리해 개발을 할 수도 있고 user 정보를 기준으로 분리해
클래스를 관리 할수 있고 적절한 리팩토링을 할 수있는 장점이 있다.
마지막으로는 서비스 레이어의 중요성에 대해서 말씀을 해주시고 의존성 과 캡슐화에 대해 설명해주시면서 인터페이스를 사용하는 것이 더 자연스럽고 이런 이유로 인해 인터페이스를 사용하라고 말씀을 하신다고 작성해주셨다.
(의존성 관리가 정말 중요하죠...)
느낀 점
정말 아는 만큼 보인다는 것이 이 상황을 설명하기에 적절한 말인 것 같다.
저퀄리티 질문에 이런 고퀄리티 질문으로 답변해주신 토비님께 감사하다는 생각밖에 들지 않는다.
이 소프트웨어에서 실무 경험이 많은 분들의 답변은 정말 생각지도 못한것들을 답변 해 주시는 것들이
너무 많다. 좋게 생각하면 그만큼 배울 것들도 아직 많고 더 나은 개발자가 될 수 있다는 뜻이니 기분 좋게 생각해보자
마지막으로 다시 한번 정성스럽게 답변해 주신 토비님께 감사하다는 말씀을 드리고 싶습니다.
토비님의 답변을 내가 잘 이해하고 작성한게 맞나 라는 생각 을 하며 조심스럽게 디스코드에
공개적으로 링크를 남겼다.
토비님의 또 다른 팩트폭력? 다른 지식전달을 해 주실까라는 두려움과 기대 반반을 하며 디스코드의 알림을 기다리구있었다.
생각지도 못한 답변!!
짧고 묵직한 칭찬에 퇴근 후 기분좋은 하루의 마무리다...