이펙티브 자바, 쉽게 정리하기 - item 62. 다른 타입이 적절하다면 문자열 사용을 피하라
문자열을 잘못쓰는 사례들
- 데이터를 입력받을 때 무작정 문자열로 입력받는 사람이 있다.
- 숫자면 숫자, 예/아니오라면
boolean
과 같은 명확한 타입으로 받는게 더 좋다.
- 숫자면 숫자, 예/아니오라면
- 열거 타입을 문자열로 대신하는 경우도 있다.
- 혼합 타입을 문자열로 대신하는 경우도 있다.
- 권한을 문자열로 표기하는 경우도 있다.
혼합 타입을 남용하는 예
String compoundKey = className + "#" + i.next();
- 가운데
#
을 기준으로 파싱하려고 하는 의도가 살짝 보인다. #
이 만약className
에서 이용되거나i.next()
에서 이용되면 문제가 일어난다.- 문자열 파싱이라는 추가적인 성능 악영향까지 있다.
equals()
,toString()
,compareTo()
등의 메서드도 제공 불가능하다.- 3가지 필드를 보관할 수 있는 클래스를 만들지 않을 이유가 없다.
문자열로 권한을 잘못 구분하는 예
문자열로 권한 구분하기
public class ThreadLocal {
private ThreadLocal() {} // 객체 생성 불가
// 현 스레드의 값을 키로 구분해 저장한다.
public static void set(String key, Object value);
// 현 스레드의 값을 반환한다.
public static Object get(String key);
}
ThreadLocal 에 대한 설명 링크 를 미리 보고 가면 좋다.
ThreadLocal
에서 쓰는 변수를 문자열 키로 구분하는 예이다.- 스레드 구분용 키가 전역 이름 공간 (global namespace) 에서 공유된다는 뜻이다.
- 보안에 취약할 수 있다.
- 서로 다른 문자열 키를 사용함을 보장해야 하고 같은 키를 이용할 경우 충돌이 발생한다.
- 다른 클라이언트가 문자열만 알면 다른 스레드가 사용하는
ThreadLocal
변수를 가져올 수 있다.
Key
클래스로 권한 구분하기
public class ThreadLocal {
private ThreadLocal() {} // 생성자 막기
// 내부 `Key` 클래스를 이용하여 인스턴스에 무관한 키를 생성할 수 있게 만든다.
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);
}
- 이전까진 문자열 타입의
key
를 이용해 권한을 구분했는데, 이젠Key
클래스를 이용한다. - 문자열 기반에서 다른 스레드의
ThreadLocal
변수에 접근할 수 있었던 보안 취약점이 사라졌다. - 이제 정적인
set()
과get()
은 별로 필요 없고,Key
의 인스턴스 메서드를 쓰면 된다. Key
는 스레드 지역변수를 구분하기 위한 키보다는 스레드 지역변수 자체가 된다.Key
의 이름을 차라리ThreadLocal
로 바꾸어버리자.
난 사실 책을 보며 여기서
Key
를 어떻게 갑자기ThreadLocal
이라는 클래스로 바꾸어버리는지 이해가 잘 안됐다.
실제ThreadLocal
의 구현을 보면,get()
에서는 현재 스레드의ThreadLocalMap
을 찾아간다.ThreadLocalMap
이 쓰는Key
는ThreadLocal
인스턴스 그 자체를 사용한다.
이ThreadLocal
인스턴스는 결국 미래에 비공개 정적 필드로 사용할 것이기 때문에 스레드와 같은 생명주기를 갖게 되고 스레드는Thread
클래스 구현 내용을 보면 스레드가 생성됨과 동시에 자신의LocalThreadMap
을 생성하므로 자연스럽게 이 로직이 이어진다. 이 과정을 모르고 코드를 계속 봤으면 계속 이해가 안됐을 것이다.
public final class ThreadLocal {
public ThreadLocal();
public void set(Object value);
public Object get();
}
- 마지막 문제는
get()
으로 얻은Object
를 매번 형변환해야 한다는 것이다.
public final class ThreadLocal<T> {
public ThreadLocal();
public void set(Object value);
public T get();
}
- 마지막 문제도 해결되었다.
핵심 정리
- 문자열을 쓰기 전에 문자열보다 더 적절한 데이터 타입이 있는지 생각해보자.
- 문자열은 잘못 사용하면 번거롭고, 더 느리고, 오류 가능성도 크다.
- 기본, 열거, 혼합에 문자열 사용을 자제하자.
'Java > 이펙티브 자바' 카테고리의 다른 글
이펙티브 자바, 쉽게 정리하기 - item 64. 객체는 인터페이스를 사용해 참조하라 (0) | 2023.06.26 |
---|---|
이펙티브 자바, 쉽게 정리하기 - item 63. 문자열 연결은 느리니 주의하라 (0) | 2023.06.26 |
이펙티브 자바, 쉽게 정리하기 - item 61. 박싱된 기본 타입보다는 기본 타입을 사용하라 (0) | 2023.06.22 |
이펙티브 자바, 쉽게 정리하기 - item 60. 정확한 답이 필요하다면 float 과 double 은 피하라 (0) | 2023.06.22 |
이펙티브 자바, 쉽게 정리하기 - item 59. 라이브러리를 익히고 사용하라 (0) | 2023.06.22 |