다른 타입이 적절하다면 문자열 사용을 피하라
책/Effective Java

다른 타입이 적절하다면 문자열 사용을 피하라

728x90
반응형

String(문자열) - Text를 표현하도록 설계되고, 자바가 잘 지원해주는 타입 

 

하지만 워낙 자바에서 너무 잘 지원해주어서, 원래의 의도대로 사용되지 않는 경향이 있으며,
이럴 때 문자열 사용을 피해야할 사례에 대해서 이야기한다. 

 

# 문자열을 쓰지 않아야할 사례 

1. 다른 값 타입을 대신해서 문자열을 사용하는 경우 

  • 받는 데이터가 수치형 - int, float, BigInteger ... 적당한 수치를 타입으로 변환해서 사용할 것 
  • Y/N 를 나타낼 수 있다면 적절한 열거 타입 혹은 boolean 타입을 사용할 것 

즉 기본 타입이든 참조 타입이든 적절한 값 타입이 있다면 그것을 사용하고, 없다면 새로 하나 작성할 것 

 

2. 열거 타입을 대신하여 문자열을 사용할 경우 

    • 아이템 34(int 상수 대신 열거 타입을 사용하라) -  상수를 열거할 때는 문자열보다는 열거 타입이 월등히 낫다. 

3. 혼합 타입을 대신하여 문자열을 사용할 경우 

String compoundKey = className + "#" + i.next();  // -> 부적절한 상황 

//if i.next = "#Blank" or className = "ClassA#"
  • 단점이 많은 방식
  • 위에 작성한 것처럼 혹여 두 요소를 구분해주는 문자 #이 두요소 중 하나에서 쓰이는 경우에는 혼란 야기 
    • 각 요소를 개별로 접근하여 문자열 파싱처리 -> 느려짐, 귀찮음, 오류 가능성 증가 
  • 또한 적절한 equals(), toString(), compareTo() 메서드 제공 불가, String의 기능에만 의존 
    • 따라서 전용 클래스를 새로 만드는 편이 나음 / 이런 클래스는 보통 private 정적 멤버 클래스로 선언(아이템 24) 

 

4. 문자열로 권한을 표현하는 경우 

 

잘못된 예시 (문자열을 통해 권한을 구분한 경우)

public class ThreadLocal {
    private ThreadLocal() {} //객체 생성 불가
    
    // 현 스레드의 값을 키로 구분해 저장한다.
    public static void set(String key, Object value);
    
    // (키가 가르키는) 현 스레드의 값을 반환한다.
    public static Object get(String key);
}
해당 방식의 문제 및 원인은 무엇일까

정답 

더보기

1. 스레드 구분용 문자열 키가 static namespace 에서 공유된다는 점 

  • 의도대로 동작하기 위해서는 각 클라이언트가 고유한 키를 제공해야함 
  • 하지만, 서로 소통이 불가한 상황에서 같은 키를 쓰기로 결정한다면, 의도치 않게 같은 변수를 공유하게 된다. (두 클라이언트 모두 제대로 기능 동작 X) 

2. 보안 취약 

  • 악의적인 클라이언트라면 의도적으로 같은 키를 사용하여 다른 클라이언트의 값을 가져올 수 있음 
  • 따라서 해당 API는 문자열 대신 위조할 수 없는 키를 사용하는 것으로 변경해야한다. 

해결 방법 

  • 문자열로 권한을 표현하는 것이 아닌 별도의 클래스를 통해서 분리하는 방법 
  • Key 클래스를 통해 권한 구분
public class ThreadLocal {
    private ThreadLocal() {} //객체 생성 불가

    public static class Key {
        key() {}
    }

    //위조 불가능한 고유 키를 생성한다.
    public static Key getKey() {
	  return new Key();
    }

    public static void set(Key key, Object value);
    public static Object get(Key key);
}

 

해당 코드 리팩토링 

  • set / get 메서드는 정적 메서드일 필요가 없다. → 따라서 Key의 인스턴스 메서드로 변경처리 
    • Key는 더 이상 스레드 지역변수를 구분하기 위한 키가 아닌 자체적으로 스레드 지역변수가 된다. 
  • 결과적으로 톱 레벨 클래스(ThreadLocal)는 별달리 하는 일이 없어지기 때문에 중첩 클래스 Key의 이름을 ThreadLocal로 변경 
  • 추가적으로 타입 안전성을 위해서라면 API get을 통해 Object 형변환을 하지 않아도 되게끔 처리 
// Key를 ThreadLocal로 변경
public final class ThreadLocal {
    public ThreadLocal();
    public void set(Object value);
    public Object get();
}

//매개변수화를 통한 타입 안전성 확보 
public final class ThreadLocal<T> {
    public ThreadLocal();
    public void set(T value);
    public T get();
}

 

정리 
적하반 데이터 타입이 있거나 새로 작성할 수 있다면 , 문자열보다는 해당 타입을 사용하도록 하자 
728x90
반응형