Frudy
7k
2019-05-17 09:25:59 작성 2019-05-17 09:37:36 수정됨
4
604

null, empty String에 대해 어떻게 대응하세요?


패러미터로 null 혹은 empty string이 넘어오면 어떻게 해야하는가?

이 문제는 작년 1월에 첫 프로그래밍을 배울 때부터 고민했던 문제였습니다.


갖고있는 원칙)

1. 어쨋든 프로그램은 안전하게 돌아가야 한다. (고작 NPE 때문에 시스템 다운될순 없죠)

2. Null or Empty String에 의해 버그가 생겼을 때 빠르게 찾을 수 있어야 한다.


구글링도 많이해봤으며... 

작년보다 나아진점은, NPE를 예방하는 방법만 6개정도 알고있는게 전부입니다.


name.equlas("minsu"); 대신에 "minsu".equals(name); 이런거요.


하지만, 메소드를 만드는 입장에서,

사용자가 모르고 null이나 empty string을 매개로 보내면 어쩌지?

라는 생각은 역시 지워지질 않습니다.


현재 생각은 다음과 같습니다.

1. 사용자가 null을 매개로 보내는건 분명 사용자도 원치않고, 의도치않았다. 라고 가정

2. 사용자가 empty string을 매개로 보내는건 분명 ...이하생략.

>> 그러므로 사용자가 원치않는 입력이 들어왔으니 예외를 throw하자. 알려주자. 잘못 넘어왔다고.


제가 갖고있는 예시 하나를 가져왔습니다.


	/**
	 * 종료구분자가 메소드 내에 여러개 있는경우 자동으로 종료 구분자위치를 정합니다.
	 * "insert into boardTable(column, column) values(value, value)"일 경우, "values("와 ")"가 넘어왔을 때 자동적으로 두번째 )를 지정합니다.
	 * 만약, "man(hong, 21) woman(lee, 20) girl(kim, 21)"와 "woman(", ")"가 넘어올 경우 "woman(" 이후에 나오는 첫번째 ")"를 지정합니다.
	 * 종료구분자 endString에 @가 포함되어있는경우 이 메소드를 사용할 수 없습니다.
	 */
	public static String autoSubstring(String source, String beginString, String endString) throws InvalidStringException
	{
		StringExceptionCheck.lengthZeroCheck(source, beginString, endString);
		StringExceptionCheck.notExistSeparatorCheck(source, beginString, endString);
		
		if(endString.contains(TEMPORARY_STRING) == true)
			throw new InvalidStringException("endString에 " + TEMPORARY_STRING + "이 포함되어 있는경우 autoSubstring()을 사용할 수 없습니다. 입력된 문자열 =  " + endString);
	
		int beginIndex = source.indexOf(beginString) + beginString.length();
		int endIndex = source.indexOf(endString);
		
		if(beginIndex > endIndex)
			endIndex = getAutoEndIndex(source, beginIndex, endString);
		
		else if(beginIndex == endIndex)
			throw new InvalidStringOrderException("종료구분자와 시작구분자의 index가 동일합니다.");
		
		else if(beginIndex < endIndex)
		{
//			가장 정상적인 경우라서 따로 실행문이 필요하지않습니다.
		}
		
		return source.substring(beginIndex, endIndex);
	}


그리고 그 check 메소드입니다.


	public static void lengthZeroCheck(String ...params) throws StringLengthZeroException
	{
		for(int i = 0 ; i < params.length ; i++)
		{
			if(params[i].length() == 0)
				throw new StringLengthZeroException((i + 1) + "번째 매개변수의 문자열 길이가 0입니다.");
		}
	}


어쨌든 구글링 자료를 읽어보면 다들

불필요한 체크를 해야한다는 단점이 생기니까 어쩌구 저쩌구 하는대,


결국 체크를 하긴 해야하는거 아닐까요? 무조건.


DAO클래스의 메소드도 그렇습니다.


DAO클래스를 작성하는 제 입장은...

DB에 select list 했더니 자료가 없어도, empty array나 empty List<E>를 반환해줍니다.

왜냐하면, 이 DAO클래스를 호출한 사용자가, null check를 깜빡하고 안했어도

NPE가 뜨지않기 위해서죠.


하지만 역지사지로, 제가 DAO클래스를 호출하여 사용하는 입장이라면 이야기가 달라집니다.

DAO를 설계한 동료가, select list했을 때 데이터가 없으면 어떻게 리턴해줄까요?


결국 제가내린 결론은 다음과 같습니다.


결론1. null이나 empty string이나, check를 반드시 해야한다.

근거1. 체크하는대 성능을 거의 잡아먹지않습니다.

근거2. java의 경우 (String ...params) 이런 표현으로 짧은 코드로 체크할 수 있습니다.


결론2. check해서 걸리면, 예외를 throw 해야한다. 구체적으로. 몇번째 매개변수가 그런지.

근거1. 프로그램이 복잡해질수록, NPE같은건 찾기가 더 오래걸립니다.

>> 얘가 어디서부터 갑자기 null이 들어와서 온갖 저모양 이꼴이 난건지 찾는건... 골때릴때가 많죠.


사용자는, 아하 내가 보낸 3번째 매개변수가 null이었구나,

얘가 어디서부터 null이 저장된거지? 로 디버깅을 시작할 수 있으니까요.


empty string은 미리 개인플젝에서 위 코드처럼 체크를 해봤었습니다.

채감은, null check보다 더 좋았습니다.


null은 NPE라도 뜨기 때문에 스택트레이스 보고 시간을 투자하면 찾습니다만...

얘는 스택트레이스에 예외가 뜨지않습니다. string.length = 0이라고 뜨고...

string.substring() 뭐 어떤 메소드를 사용해도 예외가 뜨지않죠.


그래서 예외가 발생하지않고 프로그램이 그냥 제 의도대로 안굴러갈 뿐입니다.

오히려 이런게 더 무서워요... 예외도 안뜨는대 작동이 의도대로 안될떄... 으으 소름

0
  • 답변 4

  • BK
    1k
    2019-05-17 12:24:02
  • CyanGlint
    847
    2019-05-17 14:40:46

    Optional조차도 코틀린의 nullable?.value ?: default 나

    C#의 nullable?.value ?? default 문법에 비하면 번거롭기 짝이 없어서...


    그래도 없는 것 보단 나으니까 쓰고는 싶은데 Java8 지원이 안 되는 환경이면

    정말 판타스틱한 기분이 됩니다 ㅎㅎ



    MIN API 24 (안드로이드7 누가) 미만에선 Java8도 못 쓰는 빌어먹을 안드로이드...ㅎ

    이젠 정말 코틀린뿐이야 ㅠㅠ


    더불어 null값인걸 확인했을 때 기본값을 사용할지 throw 할지 여부는 

    기본값으로도 실행 가능한 함수인지 여부에 달린 일인데 

    예를 들어 DateTime dt = null 일때 DateTime.Now 를 기본값으로 해도 문제 없는지 검토해보는거죠.

    (문제가 된다면 차라리 throw 해주는게 낫다고 생각합니다)


    이런 처리를 쉽게 하기 위해서 '기본값이 지정되어 있으니 null 넣을 것 같으면 차라리 값을 넣지 마라'고

    void Function(string param1, string param2="2", string param3="3") 와 같이

    default parameter를 지정하고 함수를 자동으로 override 해주는 문법이 있는데 

    (Funtion("1")로 호출하면 자동으로 Funtion("1", "2", "3")이 됨)


    이것도 자바에선 안 됩니다... 코틀린이나 C#에선 되는데!

    그래서 나온게 Builder Pattern이라는데 이것도 손이 더 가는건 어쩔수가 없어서

    이런 것들이 쌓이고 쌓이다보면 '자바 문법은 장황하고 불편하다'는 쓴소리에 살포시 동의하게되는...

  • Whislter
    395
    2019-05-18 00:25:34

    이건 원칙을 가질게 아니라, 유연하게 "대처"해야 하는 상황같은데요.

  • 286
    2019-05-21 15:11:34

    1. DB저장 필수값인데 빈값 => 예외발생

    2. DB저장 빈값가능 => 정상

    3. 수치계산 필수값 => 예외발생

    4. 조회조건 필수값 => 예외발생

    5. 조회조건 빈값으로 조회시 전체조회 => 정상.

    등등 업무요건에 맞게끔 구현해야 할것같아요.

  • 로그인을 하시면 답변을 등록할 수 있습니다.