관련 Issue & PR
사과를 캐싱해볼까
도메인 특성
사과는 개수가 제한적이고, 변하지 않는 도메인이다.
일반 사과 1~9, 그리고 황금 사과가 1~9까지 있다.
게임마다 사과를 120개 생성한다 → 18개의 객체로 모든 게임을 처리할 수 있다!
불변 객체이므로, 동시성 문제에서도 자유롭다.
이 방법으로 힙 영역을 아끼면서도 매번 객체 생성에 걸리는 시간을 아낄 수 있다.
캐싱 방법
Enum은 어떨까?
Enum을 사용하면, 싱글턴이 자연스레 보장되어 안전하다.
하지만 이 상황에는 부적절하다고 판단된다.
예를 들어, 일반사과, 황금사과를 Enum으로 만들어놨는데, 폭탄사과라는 요구사항이 추가된다면?
폭탄사과 9개를 추가로 만들고, 기존 enum들과의 관계를 매핑해줘야 한다.
또, 로직 중 일반 사과를 → 황금사과로 만드는 행위도 존재하는데,
enum을 사용한다면 일반사과가 → 본인의 숫자에 해당하는 황금사과를 알아둬야 한다. (매핑이 필요)
굳이 이런 절차지향적인 코드를 만들어야 할까?
그래서 Enum 사용은 탈락이다.
정리하자면:
- 객체의 유연성이 떨어진다.
- 새로운 종류를 추가하기 어려우며, 객체를 동적으로 수정할 수 없다.
- enum 객체가 많아져 가독성이 떨어진다.
- enum 객체 사이의 관계가 아주 복잡해진다.
- 과거에 이런 시도를 해봤을 때, ‘전방 참조 문제’ 때문에 골머리 썩었던 기억이 난다.
Map은 어떨까?
Lazy Load 방식으로 객체를 캐싱하도록 하자.
생성된 객체들은 모두 static(shared) Map 변수에 한번만 저장하여 재사용한다.
이 때, 객체 저장이 동시에 일어날 수 있으므로 ConcurrentHashMap
을 사용하도록 하자.
상속으로 사과를 깔끔하게 표현하고 싶은데…
캐싱이 문제다. 부모에서 자식을 생성하면 → 데드락이 생길 수 있다.
부모 생성자 → 자식 생성자 → super() → 이런식으로 무한 루프에 빠질 수 있으며,
그렇다고 synchronized 키워드를 사용하면 데드락 발생 가능성이 있다.
→ 부모를 통해 캐시하지 말고, 각자 캐시하자.
어차피 getInstance()
는 외부에서 부른다.
역직렬화는 어떻게 대응?
캐싱해서 좋은 점은 객체를 다시 생성하지 않는 것이다.
그러나 AttributeConverter에서 Jackson을 사용해 역직렬화하고 있으므로 객체가 계속 생성된다.
따라서 새로운 방법을 강구해야 한다.
CustomDeserializer를 사용해 해결했다.
아쉬운 점
실험을 하지 못했다
이론적인 방법이 아니라, 테스트를 통해 실제로 영향이 있는지를 확인하고 착수했다면?
더 얻는 것이 많았을 것이다.
게임도 캐싱할 수 있다
도메인 특성
게임 보드도 사실 게임이 완료될 때까지 DB에 들어갈 필요가 없다.
DB에 들어갔다 나왔다 하는게 오버헤드가 더 크다.
이건 후에 더 개선해보자.