@Test // (1) Order의 getMessageTypes 메서드를 사용 할 때 불편하다 // 안좋은 캡슐화 publicvoidanti_message_test_01(){ final Order order = build("KAKAO,EMAIL,SMS"); final String[] split = order.getMessageTypes().split(",");
@Test // (2) KAKAO를 KAOKO 라고 잘못 입력했을 경우 publicvoidanti_message_test_02(){ final Order order = build("KAOKO,EMAIL,SMS"); final String[] split = order.getMessageTypes().split(",");
@Test // (3) 메시지에 KAKAO, EMAIL, SMS 처럼 공백이 들어 간다면 실패한다 publicvoidanti_message_test_03(){ final Order order = build("KAKAO, EMAIL, SMS"); final String[] split = order.getMessageTypes().split(",");
@Test // (4) 메시지가 없을 때 빈문자열("")을 보낼 경우 publicvoidanti_message_test_04(){ final Order order = build(""); final String[] split = order.getMessageTypes().split(",");
assertThat(split, hasItemInArray("")); }
@Test(expected = NullPointerException.class) // (5) 메시지가 없을 때 null 을 보낼 경우 publicvoidanti_message_test_05(){ final Order order = build(null); order.getMessageTypes().split(","); }
@Test // (6) 메시지가 중복으로 올경우 publicvoidanti_message_test_06(){ final Order order = build("KAKAO, KAKAO, KAKAO"); final String[] split = order.getMessageTypes().split(",");
가장 쉽게 생각할 수 있는 방법입니다. 단순히 getter 메서드를 이용해서 외부 객체가 사용하게 제공합니다. 이는 캡슐화에 엄청난 악영향을 미치게 됩니다.
우선 getMessageTypes() 메서드를 사용 하는 모든 곳에서 split() 메서드를 이용해서 메시지 타입을 배열로 만들어서 사용해야합니다.
(2) KAKAO를 KAOKO 라고 잘못 입력했을 경우
단순하게 String을 사용하기 때문에 type safe 하지 않습니다. KAKAO를 KAOKO로 잘못 입력해도 제대로 검증하기 어려우며 검증하는 로직을 추가 하더라도 해당 에러는 Runtime으로 넘어가게 됩니다. 이처럼 단순 문자열이면 이러한 단점을 갖게 됩니다.
(3) 메시지에 KAKAO, EMAIL, SMS 처럼 공백이 들어 간다면 실패한다
일반적으로 웹 개발을 하면 대부분의 요청은 컨트롤러에서 받게 됩니다. 이 때 KAKAO, EMAIL, SMS 문자열 사이에 빈 공백이 들어오게 되면 split() 함수가 제대로 동작하지 않습니다. 테스트 코드를 보면 EMAIL, SMS는 not(hasItemInArray..) 으로 검증됩니다.
물론 앞뒤 공백을 자르는 로직이 추가되면 되지만 이렇게 되면 점점 로직의 복잡도가 높아지게 됩니다.
(4) 메시지가 없을 때 빈문자열("")을 보낼 경우
컨트롤러 요청을 받을 때 받을 메시지 플랫폼이 없다면 "" 으로 받게 됩니다. 이 빈 공백 이라는 것이 의미하는 게 어떤 메시지 플랫폼도 선택하지 않았다는 의미로 해석되기는 어렵습니다. 문자열 자체는 이러한 타입에 적합하지 않기 때문에 메시지가 없을 때 어떤 식으로 처리해야 할지 고민하게 됩니다.
(5) 메시지가 없을 때 null 을 보낼 경우
빈 공백의 의미가 정확하지 않다고 생각했을 경우 null로 요청을 받게 되면 split() 메서드에서 RuntimeException이 발생하게 됩니다. 물론 로직을 추가해서 null인 경우를 처리할 수 있지만, 이것은 3번에서 언급했던 것처럼 계속 코드의 복잡성이 높아지게 됩니다.
(6) 메시지가 중복으로 올경우
메시지 플랫폼이 중복으로 넘어오게 될 실제 메시지가 중복으로 발송되기 때문에 로직을 추가 해야 됩니다.
테스트 코드의 중요성
캡슐화에서 벗어난 주제이기 때문에 테스트 코드에 대한 부가적인 설명해 드리는 것이 조금은 어색하지만, 테스트 코드의 중요성을 한번 언급하고 싶었습니다.
문제 있는 코드를 빨리 파악할 수 있다.
해당 기능의 테스트 코드를 작성하면 이 코드의 문제점을 가장 빠르게 파악할 수 있습니다. 저는 코드가 왜 캡슐화에 안 좋은 코드인지 테스트 코드를 통해서 알게 됐습니다. 위에서 언급한 1~5 문제들을 테스트 코드 작성 시 파악했고 리팩토링 작업을 진행했습니다.
테스트 코드는 냄세나는 코드를 빠르게 찾게 해줍니다. 이것도 테스트 코드의 엄청난 장점이라고 생각합니다.
@PostMapping public Order create(@RequestBody @Valid OrderRequest request){ final Order order = buildOrder(request); return orderRepository.save(order); } }
다시 한번 강조하지만 외부 객체에서는 저 문자열을 가져올 수 없을 뿐만 아니라 실제 데이터베이스에 문자열로 저장돼있는지 관심조차 가질 필요가 없습니다. getTypes() 메서드로 List형으로 외부에 제공해주기만 하면 됩니다. 이것이 캡슐화의 기본적 개념이라고 생각합니다.