3/7/2024
개발 초기에는 단순한 정보를 숫자나 문자열과 같은 기본형의 간단한 데이터로 표현할 때가 많다.
그러나 개발 규모가 커지고 점차 기능이 복잡해지면서, 더이상 이러한 정보들이 단순하지 않아지면서 어려움이 생긴다.
User
의 멤버변수 phoneNumber
예를들어 사용자 클래스에 전화번호를 담는 멤버변수가 있다고 해보자.
public class User { ... private String phoneNumber; public String getPhoneNumber(){ return this.phoneNumber; } public void setPhoneNumber(String _phoneNumber){ this.phoneNumber = _phoneNumber; } }
우리나라 전화번호는 대개 000-0000-0000
의 형식으로 나타난다.
때문에 대부분 String
타입으로 설정할 것이다.
그런데, 누군가는 전화번호를 01012340000
과 같이 -
문구가 없는 형태를 선호할 수도 있지 않을까?
정하기 나름이다. 내부적으로는 '우리는 전화번호를 -
문구가 있는 형태 규정합니다.' 라고 하면 되고.
전화번호를 직접 입력하는 사용자들에게는 -
문구를 입력하도록 강제하거나 validation
과정을 거치면 된다.
그럼 이제 새로운 개발자가 들어올 때 해당 내용을 인지할 수 있도록 코드 규칙을 문서화 해두거나, 잘 알려주면 될 것이다.
하지만 사람은 잘 까먹고 실수하는 존재다.
누군가 실수로 -
가 없는 문자열을 넣기라도 하면 문제가 생기는 것을 막을 수 없다.
그렇다면 setter
에서 이를 방지하거나 변형해주는 코드를 넣어서 누군가 실수로 -
없이 입력 하는것을 사전에 차단할 수 있을 것이다.
그런데 서비스가 글로벌하게 확장되면서 서로 다른 국가의 전화번호도 입력해야 하는 상황이 되었다고 하자.
나라마다 전화번호의 형식이 다르기 때문에 이제는 위에서 정한 코드 규칙을 유지할 수 없다.
심지어 전화번호를 보고 어느 나라인지를 확인할 수 있어야 한다는 요구사항이 있어 더욱 복잡해졌다.
setter
코드 안에 나라마다 형식을 검증하는 로직을 만들고,
전화번호가 어느 나라의 형식인지 반환해주는 getter
도 만들어야 한다.
요구사항이 추가되고, 전화번호로 수행하는 기능이 복잡해질 때마다 User
클래스는 점점 관련된 코드로 가득 차 버릴 것이다.
Util
클래스 만들기Util
클래스를 만들어서 이를 해결할 수도 있다.
PhoneNumberUtil
클래스를 만들고 그 안에 각종 로직을 담당하는 메서드를 만드는 것이다.
public class PhoneNumberUtil { public static String getCountry(String phoneNumber){ ... (국적을 찾는 로직) return country; } public static boolean validation(String phoneNumber, String country){ ... (국적에 따른 형식 검증 로직) return result //true or false; } ... }
이렇게 하면 User
클래스에 작성되는 코드를 줄이고 결합도를 다소 낮출 수 있어 보인다.
하지만, 전화번호를 가지고 수행해야하는 대부분의 메서드에서는 PhoneNumberUtil
을 사용해야 한다.
문제는 PhoneNumberUtil
클래스는 User
의 phoneNumber
와 이름의 관계 외에는 전혀 프로그래밍적인 연결성이 존재하지 않는다는 것이다.
PhoneNumberUtil
클래스의 존재, 사용방법에 대해서 제대로 인지하지 못할 경우,
전화번호에 대한 요구사항을 수행하기 위한 중복 코드가 발생할 수 있는 것이다.
본 글에서 제시하고자 하는 또 다른 방법은 전화번호를 객체화 하는 것이다.
전화번호는 더이상 단순한 문자열이 아니다. 여러 의미를 담아야 하고, 또 변환해서 보여주어야 한다.
이제는 복잡한 기능을 담당해야 할 대상이 여전히 단순한 String
이기에 어려움이 있는 것이다.
방법은 간단하다.
PhoneNumber
클래스를 만들고, 요구사항에 대한 모든 내용을 해당 클래스에 구현하는 것이다.
그리고 User
클래스의 phoneNumber
는 이제 String
이 아닌, PhoneNumber
클래스의 인스턴스인 것이다.
public class User { ... private PhoneNumber phoneNumber; public PhoneNumber getPhoneNumber(){ return this.phoneNumber; } public void setPhoneNumber(String _phoneNumber){ this.phoneNumber = new PhoneNumber(_phoneNumber); } }
public class PhoneNumber { private String phoneNumber; private String country; public PhoneNumber(String _phoneNumber){ validation(_phoneNumber) this.country = findCountry(_phoneNumber) // 국적을 찾는 메서드 this.phoneNumber = _phoneNumber } public String toString(){ return this.phoneNumber; } public String getCountry(){ return this.country; } public boolean validation(String phoneNumber){ ... (국적에 따른 형식 검증 로직) return result //true or false; } ... }
User
클래스는 더이상 전화번호에 대한 기능을 위한 코드를 담고 있을 필요가 없다.
전화번호를 위한 기능은 User
클래스에서 찾을 필요도, 내가 혹시 모르고 있을 Util
클래스를 찾을 필요도 없다.
그저 User
클래스에서 getPhoneNumber
를 호출해 얻은 객체를 통해 모든 것을 이룰 수 있는 것이다.
안그래도 복잡하고 잘 지켜지지도 않는 코드 규칙 한 줄을 지울 수 있다는 효과도 있겠다.