Skip to content

Latest commit

 

History

History
176 lines (144 loc) · 5.73 KB

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

File metadata and controls

176 lines (144 loc) · 5.73 KB

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

핵심 정리

  • 더 적합한 타입이 있다면 문자열을 쓰려는 유혹을 뿌리쳐라
  • 잘못 사용하는 예시
    • 값 타입을 대신할 용도
    • 열거 타입을 대신할 요도
    • 혼합 타입을 대신할 용도
    • 권한을 나타내는 용도

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

case1) 값 타입을 대신하는 용도

  • 데이터를 받을 때, 진짜 문자열일 때만 문자열을 사용하라
  • 수치형일 때 > int, float, BigInteger
  • 예/아니오 > boolean

적절한 값 타입이 있다면 그것을 사용하라


case2) 열거 타입을 대신하는 용도(item34)

문자열 열거 패턴이라는 변형은 더 나쁘다
- 문자열 상수의 이름 대신 문자열 값을 그대로 하드 코딩
- 오타가 있어도 컴파일러는 확인할 길이 없어 런타임 버그
- 문자열 비교에 따른 성능 저하

그에 반해 열거 타입은 다음과 같은 장점이 있다
- 컴파일 타임 안전성을 보장한다
- 각자의 이름 공간이 있어 이름이 같은 상수도 평화롭게 공존한다. ex) 브로.콜리, 보더.콜리
- toString 메서드는 출력하기에 적합한 문자열을 내어준다


case3) 혼합 타입을 대신하는 용도

  • 여러 요소가 혼합된 데이터를 하나의 문자열로 쓰지 마라
String compoundKey = className + "#" + i.next();
  • 혼란의 여지가 있다 ex) 만약 복합키의 결과가 "#####" 였다면 어디까지가 className인가?
  • 파싱과정이 느리고, 귀찮고 오류 가능성이 커진다
  • equals, toString, compareTo를 제공하지 못하며 String 기능에 종속된다

case4) 권한을 표현하는 용도

ex) ThreadLocal : 스레드 지역변수 권한을 문자열로 표현하는 예시

ThreadLocal이란?

ThreadLocal이란

  • 프로세스 : 실행 중인 하나의 어플리케이션
  • 쓰레드 : 한 프로세스 내에서 동작되는 여러 실행 흐름
  • 멀티 쓰레드 환경에서 자원을 공유하면서 발생하는 문제가 있다
  • ThreadLocal은 오직 한 쓰레드에 의해 읽고 쓰여질 수 있는 변수를 의미한다
  • 즉, Map<Thread, Object>의 형태로 현재 쓰레드를 공간으로 읽고 쓸 수 있는 변수를 저장하는 작업이다

실제 ThreadLocal 구현의 일부

 public void set(T value) {
        set(Thread.currentThread(), value);
        if (TRACE_VTHREAD_LOCALS) {
            dumpStackIfVirtualThread();
        }
    }

 public T get() {
     return get(Thread.currentThread());
 }
    

간단한 사용방식

  1. ThreadLocal 객체를 생성한다.
  2. ThreadLocal.set() 메서드를 이용해서 현재 쓰레드의 로컬 변수에 값을 저장한다.
  3. ThreadLocal.get() 메서드를 이용해서 현재 쓰레드의 로컬 변수 값을 읽어온다.
  4. ThreadLocal.remove() 메서드를 이용해서 현재 쓰레드의 로컬 변수 값을 삭제한다
// 현재 쓰레드와 관련된 로컬 변수를 하나 생성한다.
ThreadLocal<UserInfo> local = new ThreadLocal<UserInfo>();

// 로컬 변수에 값 할당
local.set(currentUser);

// 이후 실행되는 코드는 쓰레드 로컬 변수 값을 사용
UserInfo userInfo = local.get();

reference)
https://javacan.tistory.com/entry/ThreadLocalUsage

public class ThreadLocal {
    private ThreadLocal() {} // 객체 생성 불가
    
    // 현 스레드의 값을 키로 구분해 저장
    public static void set(String key, Object value);
    
    // (키가 가리키는) 현 스레드의 값을 반환
    public static Object get(String key);
}

문제점 : 키가 전역 이름 공간에서 공유된다

  • 클라이언트의 키가 중복될 때 > 두 클라이언트 작업에 모두 오류가 발생한다
  • 키 hijacking 위험이 있다 > 위조하거나, 갈취당할 수 있다

개선방안 : 문자열 대신 위조 불가한 키(권한)를 사용하라

// 개선 1단계 : 위조 불가한 키 클래스 활용
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이 static 메서드일 필요가 없다 > Key의 메서드로 편입하자
// 개선 2단계 : Key 내부로 set/ get 메서드 편입
public class ThreadLocal {
    private ThreadLocal() {
    } // 객체 생성 불가

    public static class Key { //권한 
        Key() {
        }
        
        void set(Object value);
        Object get();
    }

    //위조 불가한 고유 키를 생성한다.
    public static Key getKey() {
        return new Key();
    }
}
  • 이렇게 되면 Key가 지역변수 구분을 위한 키가 아니라 그 자체가 스레드 지역변수가 된다.
  • 현재 ThreadLocal은 할일이 거의 없으므로 Key의 이름을 ThreadLocal로 바꾸자
// 개선 3단계 : 불필요한 톱레벨 클래스 제거
public class ThreadLocal {
    public ThreadLocal() {}
    public void set(Object value);
    public Object get();
}
  • 클라이언트가 Object를 실제 타입으로 형 변환하여 써야 한다
  • 타입 안전성을 위해서 매개변수화 타입을 선언하여 마지막 개선을 끝내자
// 개선 4단계 :  타입 안전성을 위한 매개변수화 타입 활
public class ThreadLocal<T> {
    public ThreadLocal() {}
    public void set(T value);
    public T get();
}