β€’
8 min read

[Spring 곡뢀] Spring 핡심 원리 - 4

Table of Contents

Spring에 λŒ€ν•΄ 처음 κ³΅λΆ€ν•˜λŠ” μ‚¬λžŒμ΄ 정리λ₯Ό λͺ©μ μœΌλ‘œ μž‘μ„±ν•œ κΈ€μž…λ‹ˆλ‹€. μ˜€κ°œλ… λ“± 잘λͺ»λœ 뢀뢄이 μžˆμ„ 경우 λŒ“κΈ€λ‘œ 가감없이 μ§€μ ν•΄μ£Όμ„Έμš”! ν™•μΈν•˜λŠ” λŒ€λ‘œ μ •ν™•ν•œ 정보λ₯Ό κΈ°λ°˜ν•΄ λΉ λ₯΄κ²Œ μˆ˜μ •ν•  수 μžˆλ„λ‘ ν•˜κ² μŠ΅λ‹ˆλ‹€.

κ°œμš”

이번 Spring Boot μŠ€ν„°λ””λŠ” λ°°λ‹¬μ˜ 민쑱의 κΉ€μ˜ν•œλ‹˜κ»˜μ„œ μΈν”„λŸ°μ— μ˜¬λ €μ£Όμ‹œλŠ” μžλ°” μŠ€ν”„λ§ μ™„μ „ 정볡 μ‹œλ¦¬μ¦ˆλ₯Ό 기반으둜 μ§„ν–‰ν•œλ‹€. κ·Έ μ‹œλ¦¬μ¦ˆμ˜ κ°€μž₯ 첫 κ°•μ˜μΈ μŠ€ν”„λ§ μž…λ¬Έ - μ½”λ“œλ‘œ λ°°μš°λŠ” μŠ€ν”„λ§ λΆ€νŠΈ, μ›Ή MVC, DB μ ‘κ·Ό κΈ°μˆ μ„ 끝내고, λ‹€μŒ κ°•μ˜μΈ μŠ€ν”„λ§ 핡심 원리 - κΈ°λ³ΈνŽΈμ„ 정리해보렀 ν•œλ‹€.

이번 ν¬μŠ€νŠΈμ—μ„œλŠ” 싱글톀 μ»¨ν…Œμ΄λ„ˆμ— λŒ€ν•œ 것을 κ°„λ‹¨νžˆ 정리해보도둝 ν•˜κ² λ‹€.

싱글톀을 μ‚¬μš©ν•˜λŠ” 이유

싱글톀을 μ‚¬μš©ν•˜λŠ” 이유λ₯Ό λ– μ˜¬λ € 보기 μœ„ν•΄ μ—­μœΌλ‘œ 싱글톀을 μ‚¬μš©ν•˜μ§€ μ•Šμ„ λ•Œ λ°œμƒν•˜λŠ” λ¬Έμ œμ μ— λŒ€ν•΄μ„œ μ•Œμ•„λ³΄λ„λ‘ ν•˜μž. 객체지ν–₯ ν”„λ‘œκ·Έλž˜λ°μ— λŒ€ν•΄μ„œ, ν˜Ήμ€ μžλ°”μ— λŒ€ν•΄μ„œ λŒ€ν•™κ΅ κ°•μ˜μ—μ„œ 처음 μ ‘ν•œ λ§Žμ€ μ‚¬λžŒλ“€μ€ 싱글톀 νŒ¨ν„΄μ„ μ•ˆν‹°νŒ¨ν„΄μœΌλ‘œ μ•Œκ³  μžˆμ„ 것이닀.

μ‹€μ œλ‘œ κ·Έλ ‡λ‹€. 싱글톀 νŒ¨ν„΄μ—” μˆ˜λ§Žμ€ 단점이 μžˆλ‹€. 예λ₯Ό λ“€μ–΄,

  • μžλ°”μ—μ„œλŠ” 싱글톀 νŒ¨ν„΄μ„ κ΅¬ν˜„ν•˜κΈ° μœ„ν•΄ λΆ€κ°€μ μœΌλ‘œ μ½”λ“œλ₯Ό 내뢀에 더 μž‘μ„±ν•΄μ£Όμ–΄μ•Ό ν•œλ‹€.
  • μƒμ„±μžκ°€ private ν•˜κΈ° λ•Œλ¬Έμ— μžμ‹ 클래슀λ₯Ό λ§Œλ“€κΈ° μ–΄λ ΅λ‹€. 즉, 상속을 μ΄μš©ν•œ 좔상화에 λΆˆλ¦¬ν•˜λ‹€.
  • 사싀상 클래슀 λ‚΄λΆ€μ˜ μƒνƒœκ°€ staticν•œ μƒνƒœκ°€ 되기 λ•Œλ¬Έμ— μΊ‘μŠν™”μ˜ κ΄€μ μ—μ„œ 객체 지ν–₯의 μ² ν•™κ³Ό 거리가 μžˆλ‹€.
  • 싱글톀 객체 λ‚΄λΆ€μ—μ„œ μΈμŠ€ν„΄μŠ€λ₯Ό κΊΌλ‚΄κΈ° μœ„ν•΄μ„œ getInstance() λ₯Ό ν˜ΈμΆœν•΄μ•Ό ν•˜λŠ”λ°, 이 κ²ƒμ˜ 이름이 getInstance() κ°€ 될지 μ•„λ‹ˆλ©΄ get() 이 될지, 그리고 νŒŒλΌλ―Έν„°λ‘œλŠ” 뭘 전달할지 아무도 μ˜ˆμΈ‘ν•  수 μ—†λ‹€. 즉 μ‚¬μš©ν•˜λŠ” μͺ½μ—μ„œ κ΅¬ν˜„μ²΄μ— μ˜μ‘΄ν•˜κ²Œ λ˜μ–΄ DIPλ₯Ό μœ„λ°˜ν•˜κ²Œ λœλ‹€.

λ“±μ˜ 단점이 μžˆλ‹€.

κ·ΈλŸΌμ—λ„ λΆˆκ΅¬ν•˜κ³  싱글톀을 μ‚¬μš©ν•  수 밖에 μ—†λŠ” μ΄μœ λŠ” μ›Ή μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μœΌλ‘œ λ™μ‹œμ— λ§Žμ€ μš”μ²­μ΄ λ“€μ–΄μ˜€κΈ° λ•Œλ¬Έμ΄λ‹€. 더 μ—„λ°€ν•˜κ²Œ ν’€μ–΄λ³΄μžλ©΄, λ™μ‹œμ— λ“€μ–΄μ˜€λŠ” λ§Žμ€ μš”μ²­μ— λŒ€ν•΄μ„œ 각 μš”μ²­λ§ˆλ‹€ new ν‚€μ›Œλ“œλ₯Ό 톡해 객체λ₯Ό 생성할 경우 λ©”λͺ¨λ¦¬ λΉ„μš©κ³Ό GC λΉ„μš©μ΄ μΉ˜μ†Ÿκ²Œ 되기 λ•Œλ¬Έμ΄λ‹€. 예λ₯Ό λ“€μ–΄, μš”μ²­ 1000개면 객체 1000개, μš”μ²­ 10000개면 객체 10000개λ₯Ό 생성해야 ν•œλ‹€.

싱글톀 νŒ¨ν„΄μ„ μ‚¬μš©ν•˜λ©΄ μ΄λŸ¬ν•œ 객체 생성 및 λ©”λͺ¨λ¦¬ κ΄€λ¦¬μ˜ λ¬Έμ œμ—μ„œ 비ꡐ적 μžμœ λ‘œμ›Œμ§ˆ 수 μžˆλ‹€.

근데 단점이 μ°œμ°œν•œλ°?

ν™•μ‹€νžˆ 객체지ν–₯적의 κ΄€μ μ—μ„œ 싱글톀 νŒ¨ν„΄μ˜ 단점은 썩 μœ μΎŒν•˜κ²Œ λŠκ»΄μ§€μ§€ μ•ŠλŠ”λ‹€. μŠ€ν”„λ§μ€ κ°œλ°œμžλ“€μ—κ²Œ λΆˆμΎŒν•˜κ²Œ λ‹€κ°€μ˜¬ 수 μžˆλŠ” 이 단점듀을 ν”„λ ˆμž„μ›Œν¬ λ‹¨μ—μ„œ ν•΄κ²°ν•΄μ€€λ‹€.

μŠ€ν”„λ§ 싱글톀 μ»¨ν…Œμ΄λ„ˆ

μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” 싱글톀 νŒ¨ν„΄μ˜ 문제λ₯Ό ν•΄κ²°ν•˜λ©΄μ„œ 객체 μΈμŠ€ν„΄μŠ€λŠ” μ‹±κΈ€ν†€μœΌλ‘œ 관리해쀀닀. πŸ€” μ–΄λ–»κ²Œ?

  • μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” 싱글톀을 μ½”λ“œλ₯Ό ν†΅ν•΄μ„œ μ μš©ν•˜μ§€ μ•Šλ”λΌλ„ μ•Œμ•„μ„œ 객체 μΈμŠ€ν„΄μŠ€λ₯Ό μ‹±κΈ€ν†€μœΌλ‘œ κ΄€λ¦¬ν•œλ‹€.
    • μ• μ΄ˆμ— μ»¨ν…Œμ΄λ„ˆ 생성 κ³Όμ •μ—μ„œ μ»¨ν…Œμ΄λ„ˆλŠ” 객체λ₯Ό ν•˜λ‚˜λ§Œ μƒμ„±ν•΄μ„œ κ΄€λ¦¬ν•œλ‹€.
  • μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” μ΄λ ‡κ²Œ μƒμ„±λœ 싱글톀 객체듀을 κ΄€λ¦¬ν•˜λŠ” 싱글톀 μ»¨ν…Œμ΄λ„ˆ 역할을 ν•œλ‹€. μ΄λ ‡κ²Œ 싱글톀 객체λ₯Ό μƒμ„±ν•˜κ³  κ΄€λ¦¬ν•˜λŠ” 것을 싱글톀 λ ˆμ§€μŠ€νŠΈλ¦¬λΌκ³  ν•˜κΈ°λ„ ν•œλ‹€.
  • μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ˜ κΈ°λŠ₯에 νž˜μž…μ–΄, 개발자의 μž…μž₯μ—μ„œλŠ” 싱글톀 νŒ¨ν„΄μ˜ 단점을 ν•΄κ²°ν•˜λ©΄μ„œ κ·ΈλŒ€λ‘œ 싱글톀 νŒ¨ν„΄μ˜ μž₯점을 λˆ„λ¦΄ 수 μžˆλ‹€.
    • 싱글톀 νŒ¨ν„΄μ„ κ΅¬ν˜„ν•˜κΈ° μœ„ν•œ 좔가적인 μ½”λ“œλ₯Ό μž‘μ„±ν•˜μ§€ μ•Šμ•„λ„ λœλ‹€.
    • DIP, OCP, ν…ŒμŠ€νŠΈ, private μƒμ„±μžλ‘œλΆ€ν„° 자유둭게 싱글톀 νŒ¨ν„΄μ„ μ‚¬μš©ν•  수 있게 λœλ‹€.

λ¬Όλ‘  μ‹±κΈ€ν†€λ§Œ ν•΄μ£ΌλŠ”κ±΄ μ•„λ‹˜

μŠ€ν”„λ§μ€ λ¬Όλ‘  싱글톀 이외에도 μš”μ²­λ§ˆλ‹€ μƒˆλ‘œμš΄ 객체λ₯Ό μƒμ„±ν•΄μ„œ μ‚¬μš©ν•˜λŠ” 방법도 μ§€μ›ν•œλ‹€. μ΄λŠ” 빈 μŠ€μ½”ν”„μ— λŒ€ν•΄μ„œ ν•™μŠ΅ν•  λ•Œ λ‹€μ‹œ 닀뀄보도둝 ν•˜κ² λ‹€.

싱글톀 μ‚¬μš© μ‹œ μ£Όμ˜ν•΄μ•Ό ν•  점

μŠ€ν”„λ§μ„ μ‚¬μš©ν•˜λŠ” κ²ƒκ³ΌλŠ” λ¬΄κ΄€ν•˜κ²Œ, 싱글톀을 μ‚¬μš©ν•˜λŠ” 것 μžμ²΄μ— μžˆμ–΄μ„œ μ£Όμ˜ν•΄μ•Ό ν•  점은 μƒνƒœμ— λŒ€ν•œ 관리이닀. 객체 μΈμŠ€ν„΄μŠ€ ν•˜λ‚˜λ₯Ό κ³΅μœ ν•΄μ„œ μ‚¬μš©ν•˜λŠ” μ‹±κΈ€ν†€μ˜ νŠΉμ„± 상, 싱글톀 κ°μ²΄λŠ” μ ˆλŒ€ Stateful ν•˜κ²Œ μ„€κ³„ν•΄μ„œλŠ” μ•ˆλœλ‹€. Instance Field, Static Field λͺ¨λ‘ μ—†μ–΄μ•Ό ν•œλ‹€λŠ” μ˜λ―Έμ΄λ‹€. (사싀상 Instance FieldλŠ” μ‹±κΈ€ν†€μ—μ„œλŠ” JVM λŸ°νƒ€μž„μ— 올라갈 λ•Œ λ°”λ‘œ λ‘œλ”©λ˜μ§€ μ•Šμ„ 뿐이지, ν•œλ²ˆ λ‘œλ”©λœ μ΄ν›„μ—λŠ” Static Field와 차이λ₯Ό 가지지 μ•ŠλŠ”λ‹€.)

μƒνƒœκ°€ μœ μ§€λ˜λŠ” μ½”λ“œμ˜ 문제 상황

Stateful ν•˜κ²Œ μ„€κ³„ν•˜λ©΄ 특히 μ—¬λŸ¬κ°œμ˜ μ“°λ ˆλ“œκ°€ 같은 μΈμŠ€ν„΄μŠ€μ— μ ‘κ·Όν–ˆμ„ λ•Œ, 그리고 ν•΄λ‹Ή Stateλ₯Ό λ³€κ²½ν•˜λŠ” μ½”λ“œμ— ν•¨κ»˜ μ ‘κ·Όν•  λ•Œ μ‹¬κ°ν•œ 문제λ₯Ό μ•ΌκΈ°ν•  수 μžˆλ‹€. λ‹€μŒ μ˜ˆμ œμ™€ ν•¨κ»˜ μƒκ°ν•΄λ³΄μž.

public class StatefulService {
    private int price; // μƒνƒœλ₯Ό μœ μ§€ν•˜λŠ” ν•„λ“œ

    public void order(String name, int price) {
        System.out.println("name = " + name + " price = " + price);
        this.price = price; // μ—¬κΈ°κ°€ 문제!
    }

    public int getPrice() {
        return price;
    }
}

μœ„μ™€ 같은 싱글톀 μ„œλΉ„μŠ€κ°€ μžˆλ‹€κ³  μƒκ°ν•˜μž. 이제 이 μ„œλΉ„μŠ€λ₯Ό μ‚¬μš©ν•˜λŠ” λ‹€μŒμ˜ μ½”λ“œ μ‹œν€€μŠ€κ°€ μžˆλ‹€κ³  κ°€μ •ν•˜μž.

public int sampleSequence(String nameFromRequest, int priceFromDB) {
    this.statefulService.order(nameFromRequest, priceFromDB);
    return this.statefulService.getPrice(); // μ§„μ§œ 사고 λ°œμƒ
}

μ‹œν€€μŠ€λ₯Ό 보면 order 즉 주문을 ν•˜κ³ , 주문의 가격을 뢈러였고 μžˆλ‹€. μ£Όλ¬Έ ν›„ μ£Όλ¬Έν•œ μ‚¬λžŒμ—κ²Œ μ˜μˆ˜μ¦μ„ λ°œν–‰ν•΄μ£ΌλŠ” 것과 λ§ˆμ°¬κ°€μ§€μ˜ μƒν™©μœΌλ‘œ 생각할 μˆ˜λ„ μžˆλ‹€.

이 μ½”λ“œ μ‹œν€€μŠ€λ₯Ό 2개의 μ“°λ ˆλ“œ T1κ³Ό T2κ°€ μ ‘κ·Όν•œλ‹€κ³  κ°€μ •ν•˜μž. 그리고 μ–΄μ§Έμ €μ§Έ μ—¬λŸ¬ μŠ€μΌ€μ€„λ§κ³Ό μš”μΈμ— λ”°λΌμ„œ μ‹€ν–‰ μˆœμ„œκ°€ λ‹€μŒκ³Ό 같이 됐닀고 μƒκ°ν•΄λ³΄μž.

T1.order('μ—„μ²­λ‚œ λΆ€μž', 100000000);
T2.order('쑰금 λΆ€μž', 100);
T1.getPrice();
T2.getPrice();

그러면 μ–΄λ–»κ²Œ 될까? μ—„μ²­λ‚œ λΆ€μžμ—κ²ŒλŠ” 1μ–΅μ›μ˜ 영수증이 λ°œκΈ‰λ˜μ–΄μ•Ό ν•œλ‹€. ν•˜μ§€λ§Œ 쑰금 λΆ€μž μ“°λ ˆλ“œκ°€ 쀑간에 λ“€μ–΄μ™€μ„œ StatefulService 의 priceλ₯Ό 100μ›μœΌλ‘œ 변경해버렸닀. λ”°λΌμ„œ μ—„μ²­λ‚œ λΆ€μžμ™€ 쑰금 λΆ€μž λ‘˜ λ‹€ 100μ›μ˜ μ˜μˆ˜μ¦μ„ λ°œκΈ‰λ°›κ²Œ λœλ‹€. μœ μ € 2λͺ…μ˜ μƒν™©λ§Œ 해도 μ΄λ ‡κ²Œ μ–΄μ§€λŸ¬μš΄λ°, λ§Œμ•½ μ΄ˆλ‹Ή νŠΈλž˜ν”½μ΄ 1만인 μ‹€μ œ μ„œλΉ„μŠ€μ—μ„œ 이런 일이 일어났닀고 μƒκ°ν•˜λ©΄ κ·Έμ € μ–΄μ§€λŸ½κΈ°λ§Œ ν•˜λ‹€. κ·Έλƒ₯ νšŒμ‚¬ λ¬Έ λ‹«λŠ”κ±°λ‹€.

그럼 μ–΄λ–»κ²Œ?

무쑰건 Stateless ν•˜κ²Œ 섀계해야 ν•œλ‹€. global ν•˜κ²Œ μ ‘κ·Όν•  수 μžˆλŠ” stateλ₯Ό μ ˆλŒ€ λ§Œλ“€μ§€ 말자.

μ—¬λŸ¬ μ“°λ ˆλ“œκ°€ ν•˜λ‚˜μ˜ μžμ›μ— μ ‘κ·Όν•˜λŠ” 상황을 막지 λͺ»ν•˜λ©΄ μ‹¬κ°ν•œ λ¬Έμ œμ— 도달할 수 μžˆλ‹€.

κ°œμΈμ μœΌλ‘œλŠ” ThreadLocal λ“±μ˜ λ‹€λ₯Έ λ°©λ²•μœΌλ‘œ 문제λ₯Ό ν•΄κ²°ν•˜κΈ°λ³΄λ‹€ μ• μ΄ˆμ— ꡬ쑰λ₯Ό 잘 μž‘μ•„μ„œ 문제λ₯Ό λ°œμƒμ‹œν‚€μ§€ μ•ŠλŠ” 것이 μ΅œμ„ μΈ 것 κ°™λ‹€.

사싀 이것은 비단 μ‹±κΈ€ν†€μ˜ 문제 뿐 μ•„λ‹ˆλΌ, http 그리고 REST ν™˜κ²½μ—μ„œλ„ λ§ˆμ°¬κ°€μ§€μ΄λ‹€. 이 ν™˜κ²½μ—μ„œμ˜ μ„œλΉ„μŠ€λŠ” Scale out 상황 등을 κ³ λ €ν–ˆμ„ λ•Œμ—λ„ Stateless ν•˜κ²Œ μ„€κ³„ν•˜λŠ” 것이 μ •μ„μœΌλ‘œ μ•Œλ €μ Έ μžˆλ‹€.

@Configurationκ³Ό 싱글톀, 그리고 CGLib

개인적으둜 이 뢀뢄에 μžˆμ–΄μ„œ μ•„μ£Ό κΆκΈˆν•¨μ΄ λ§Žμ•˜λ‹€. κ°•μ˜ μƒμ—μ„œ μ‹€μŠ΅ν•˜λ©° μž‘μ„±ν•œ μ½”λ“œμ— μ˜ν•˜λ©΄ λ„μ €νžˆ 일반적인 μ‹€ν–‰ μˆœμ„œλ‘œλŠ” 이해할 수 μžˆλŠ” 뢀뢄이 μ•„λ‹ˆμ—ˆκΈ° λ•Œλ¬Έμ΄λ‹€. 그리고 κ·Έ 해결법에 λŒ€ν•΄μ„œλ„ ꡉμž₯히 λ¬˜ν•œ ν•΄κ²°μ±…μ΄λΌλŠ” 생각을 ν–ˆλ‹€.

λ­”κ°€ μ΄μƒν•œλ“―ν•œ AppConfig

κ°•μ˜μ—μ„œ λ‚˜μ˜€λŠ” 예제 μ½”λ“œλ₯Ό λ³΅λΆ™ν•˜λŠ” ν–‰μœ„λ₯Ό μ΅œλŒ€ν•œ λ°°μ œν•˜λ €κ³  μ‹ κ²½μ“°κ³  μžˆμ§€λ§Œ, AppConfig의 κ²½μš°μ—” ꡉμž₯히 일반적이기 λ•Œλ¬Έμ— κ·ΈλŒ€λ‘œ μ‚¬μš©ν•˜λ„λ‘ ν•˜κ² λ‹€.

@Configuration
public class AppConfig {
    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }

    @Bean
    public OrderService orderService() {
        return new OrderServiceImpl(
                memberRepository(),
                discountPolicy());
    }

    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }
    // ...rest
}

이 μ½”λ“œλ₯Ό 보면 memberRepository() κ°€ μ™ΈλΆ€ Beanμ—μ„œ μ£Όμž…μ„ μœ„ν•΄ 2번 ν˜ΈμΆœλ˜λŠ” 것을 λ³Ό 수 μžˆλ‹€.

  • memberService() μ—μ„œ
  • orderService() μ—μ„œ

각각 ν˜ΈμΆœλ˜μ–΄ 2번 ν˜ΈμΆœλœλ‹€.

이에 λ”ν•˜μ—¬, μ• μ΄ˆμ— MemberRepository 에 λŒ€ν•œ 섀정을 ν•˜κΈ° μœ„ν•΄ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆκ°€ κ΅¬λ™λ˜λ©΄μ„œ @Bean μ–΄λ…Έν…Œμ΄μ…˜μ„ μΈμ‹ν•˜μ—¬ μ²˜μŒμ— 1번 ν˜ΈμΆœλœλ‹€.

ν•˜μ§€λ§Œ μš°λ¦¬λŠ” μœ„μ—μ„œ μ–ΈκΈ‰ν–ˆλ“―, μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλ₯Ό λ―ΏκΈ° λ•Œλ¬Έμ— 클래슀λ₯Ό μž‘μ„±ν•  λ•Œ λ³„λ„λ‘œ 싱글톀 νŒ¨ν„΄μ„ κ΅¬ν˜„ν•˜κΈ° μœ„ν•œ μ½”λ“œλ₯Ό μž‘μ„±ν•˜μ§€ μ•Šμ•˜λ‹€. κ·Έλ ‡λ‹€λ©΄ memberRepository() λŠ” μ‹€ν–‰ μ‹œμ μ— λΆ„λͺ… 3번 μ‹€ν–‰λœλ‹€.

일반적인 μžλ°” λŸ°νƒ€μž„μ— μ˜ν•˜λ©΄ λ”°λΌμ„œ 각각의 memberRepository() λŠ” 각각의 MemoryMemberRepository μΈμŠ€ν„΄μŠ€λ₯Ό λ°˜ν™˜ν•΄μ•Ό ν•˜κ³ , 이에 따라 싱글톀 νŒ¨ν„΄μ„ 지킀지 λͺ»ν•˜κ²Œ λœλ‹€. ν•˜μ§€λ§Œ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλŠ” 이 μΈμŠ€ν„΄μŠ€λ“€μ΄ λͺ¨λ‘ 같은 싱글톀 μΈμŠ€ν„΄μŠ€μž„μ„ 보μž₯ν•œλ‹€. 이것이 μ–΄λ–»κ²Œ κ°€λŠ₯ν•œκ±ΈκΉŒ?

CGLIBκ³Ό λ°”μ΄νŠΈ μ½”λ“œ μ‘°μž‘

μœ„μ˜ μ½”λ“œλ₯Ό μ„€λͺ…ν•˜λ©΄μ„œ μ§šμ§€ μ•Šκ³  λ„˜μ–΄κ°„ ν•˜λ‚˜μ˜ μ–΄λ…Έν…Œμ΄μ…˜μ΄ μžˆλ‹€. λ°”λ‘œ @Configuration 이닀.

κΉŒμ§“κ²Œ 뭔데? κ·Έλƒ₯ μ„€μ • ν΄λž˜μŠ€λΌλŠ”κ±° μ•„λ‹ˆμ•Ό? 라고 μƒκ°ν–ˆμ—ˆλ‹€. 그런데, 생각해보면 ν”„λ ˆμž„μ›Œν¬ 그리고 DI, IoC λΌλŠ” κ΄€μ μ—μ„œ 섀정은 κ·Έ 자체둜 ꡉμž₯히 μ€‘μš”ν•œ 의미λ₯Ό 가지고 μžˆλ‹€. 이 섀정을 기반으둜 ν”„λ‘œμ νŠΈκ°€ ν”„λ ˆμž„μ›Œν¬ μœ„μ—μ„œ ꡬ동이 되기 λ•Œλ¬Έμ΄λ‹€. 그러면 이 @Configuration 은 싱글톀 μ»¨ν…Œμ΄λ„ˆλΌλŠ” λ¬Έλ§₯μ—μ„œλŠ” μ–΄λ–€ 역할을 ν•˜κ³  μžˆμ„κΉŒ?

@Configuration 이 뢙은 ν΄λž˜μŠ€λ„ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ—λŠ” ν•˜λ‚˜μ˜ μŠ€ν”„λ§ 빈(Spring Bean)으둜 λ“±λ‘λœλ‹€. 그런데, 이 ν΄λž˜μŠ€λŠ” μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ— 곧이 κ³§λŒ€λ‘œ μžμ‹ μ˜ μΈμŠ€ν„΄μŠ€κ°€ λ“±λ‘λ˜λŠ” 것이 μ•„λ‹ˆλΌ, CGLIB을 ν†΅ν•΄μ„œ λ°”μ΄νŠΈ μ½”λ“œκ°€ μ‘°μž‘λœ 클래슀(**.*.AppConfig$$EnhancerBySpringCGLIB$$somehash)κ°€ μΈμŠ€ν„΄μŠ€λ‘œ λ“±λ‘λœλ‹€. (참고둜 λ°”μ΄νŠΈ μ½”λ“œκ°€ μ‘°μž‘λœ ν΄λž˜μŠ€λŠ” μ›λž˜ AppConfig 클래슀λ₯Ό μƒμ†ν•˜μ˜€κΈ° λ•Œλ¬Έμ— AppConfig λ₯Ό μ΄μš©ν•΄μ„œ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλ‘œλΆ€ν„° μ‘°νšŒν•΄μ˜¬ 수 μžˆλ‹€.)

μ΄λ ‡κ²Œ λ°”μ΄νŠΈ μ½”λ“œκ°€ μ‘°μž‘λœ AppConfig λŠ” 내뢀에 μ„€μ •λ˜μ–΄ μžˆλŠ” μŠ€ν”„λ§ λΉˆμ„ κ°€μ Έμ˜¬ λ•Œ, μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ— 이미 λ“±λ‘λœ κ²½μš°κ°€ μžˆμ—ˆλŠ”μ§€λ₯Ό ν•œ 번 더 ν™•μΈν•˜λŠ” 절차λ₯Ό κ°€μ§€κ²Œ λœλ‹€. μ—†μœΌλ©΄ μƒμ„±ν•˜κ³ , 있으면 μžˆλŠ” 것을 κ·ΈλŒ€λ‘œ κ°€μ Έλ‹€ μ“°λŠ”, κ·Έμ•Όλ§λ‘œ λ°”μ΄νŠΈ μ½”λ“œ μ‘°μž‘μ„ ν†΅ν•΄μ„œ 싱글톀 νŒ¨ν„΄μ„ κ°•μ œλ‘œ κ΅¬ν˜„ν•˜λŠ” 것이닀.

μ‹€μ œλ‘œ, @Configuration μ–΄λ…Έν…Œμ΄μ…˜μ΄ μ—†λŠ” μƒνƒœλ‘œ μœ„μ˜ μ½”λ“œλ₯Ό μ‹€ν–‰ν•˜λ©΄, 일반적인 μžλ°” λŸ°νƒ€μž„μ—μ„œμ˜ μ‹€ν–‰ λ©”μ»€λ‹ˆμ¦˜λŒ€λ‘œ 3개의 MemoryMemberRepository μΈμŠ€ν„΄μŠ€κ°€ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ— λ“±λ‘λœλ‹€.

μ–΄λ–»κ²Œ 보면 κΈ°ν–‰μœΌλ‘œ λ³΄μ΄λŠ”λ°β€¦

μ²˜μŒμ— 이 방식을 λ³΄κ³ μ„œ ꡉμž₯ν•œ κΈ°ν–‰μ΄λΌλŠ” 생각을 ν–ˆλ‹€. κ·Έλ ‡μ§€λ§Œ, μžλ°”μ—μ„œ 싱글톀 νŒ¨ν„΄μœΌλ‘œ 클래슀λ₯Ό μ„ μ–Έν•  경우 μƒκΈ°λŠ” 단점에 λŒ€ν•΄μ„œ μƒκ°ν•˜κ³ λŠ” ν•΄λ‹Ή 방식에 λŒ€ν•΄μ„œ λ‚©λ“ν•˜κ²Œ λ˜μ—ˆλ‹€.

μœ— λΆ€λΆ„μ—μ„œ κΈ°μˆ ν•œ 싱글톀 νŒ¨ν„΄μ„ μžλ°” μ½”λ“œλ‘œ κ΅¬ν˜„ν•  경우 μƒκΈ°λŠ” 단점듀은 ν΄λž˜μŠ€μ— μ—¬λŸ¬κ°€μ§€ λ””μžμΈ νŒ¨ν„΄μ„ μ μš©ν•˜κΈ° μ–΄λ ΅κ²Œ λ§Œλ“€κ³ , λ‹¨μœ„ ν…ŒμŠ€νŠΈλ„ μˆ˜ν–‰ν•˜κΈ° μ–΄λ ΅κ²Œ λ§Œλ“ λ‹€. λ˜ν•œ κ°•μ œλ‘œ κ΅¬ν˜„μ²΄μ— μ˜μ‘΄ν•˜κ²Œ 되기 λ•Œλ¬Έμ— DIP, OCP도 μœ„λ°°ν•˜κ²Œ λœλ‹€.

μœ μ§€λ³΄μˆ˜ν•˜κΈ° 쒋은 μ†Œν”„νŠΈμ›¨μ–΄λ₯Ό κ°œλ°œν•  수 있게 ν•˜κΈ° μœ„ν•΄μ„œλŠ” μ΄λŸ¬ν•œ λ¬Έμ œλ“€μ„ λ°˜λ“œμ‹œ ν•΄κ²°ν•΄μ•Ό ν–ˆμ„ 것이닀. 이에 λŒ€ν•œ ν•΄κ²°μ±…μœΌλ‘œ μŠ€ν”„λ§ κ°œλ°œμžλ“€μ€ Proxy 객체λ₯Ό λ§Œλ“œλŠ” 것을 μ„ νƒν–ˆλ‹€. ν•˜μ§€λ§Œ Java Dynamic ProxyλŠ” λ°˜λ“œμ‹œ μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜λŠ” ν΄λž˜μŠ€μ— λŒ€ν•΄μ„œλ§Œ Reflection을 μ΄μš©ν•΄μ„œ Proxy 객체λ₯Ό 생성해야 ν•˜κΈ° λ•Œλ¬Έμ— μ΄λŸ¬ν•œ μƒν™©μ—λŠ” μ ν•©ν•˜μ§€ μ•Šλ‹€.

λ”°λΌμ„œ, ν΄λž˜μŠ€λ§ŒμœΌλ‘œλ„ Proxy 객체λ₯Ό 생성할 수 μžˆλŠ” CGLIB을 μ„ νƒν•œ 것이라고 λ‚©λ“ν•˜κ²Œ λ˜μ—ˆλ‹€.

아직 AOPλ‚˜ Java Reflection, Proxy νŒ¨ν„΄μ— λŒ€ν•œ 이해가 μ•½ν•˜κΈ° λ•Œλ¬Έμ— μ •ν™•ν•˜μ§€λŠ” μ•Šμ„ 수 μžˆμŠ΅λ‹ˆλ‹€. λŒ“κΈ€λ‘œ 잘λͺ»λœ 뢀뢄이 있으면 κΌ­ μ§€μ ν•΄μ£Όμ„Έμš”! μ•žμœΌλ‘œ κΎΈμ€€νžˆ κ³΅λΆ€ν•΄μ„œ, μ™„λ²½νžˆ 이해λ₯Ό ν•œ λ‹€μŒ κ³ μΉ˜λ„λ‘ ν•˜κ² μŠ΅λ‹ˆλ‹€.

결둠은

크게 κ³ λ―Όν•  ν•„μš”κ°€ μ—†λ‹€.

일단 이 정도 μ΄ν•΄λ„λ‘œ κ°œλ°œμ„ ν•΄μ•Ό ν•˜λŠ” 상황이라면 κ·Έλƒ₯ @Configuration μ–΄λ…Έν…Œμ΄μ…˜μ„ μ‚¬μš©ν•˜λ„λ‘ ν•˜μž.

λ‹€μŒμ€

이상, μŠ€ν”„λ§μ˜ 싱글톀 μ»¨ν…Œμ΄λ„ˆμ— λŒ€ν•œ λ‚΄μš©λ“€μ—μ„œ μ€‘μš”ν•˜λ‹€κ³  λŠλ‚€ 뢀뢄듀을 κ°„λž΅νžˆ 정리해 λ³΄μ•˜λ‹€. 이 뢀뢄에 λŒ€ν•΄μ„œ μ •ν™•νžˆ μ΄ν•΄ν•˜κΈ° μœ„ν•΄μ„œλŠ” 더 곡뢀해야 ν•˜λŠ” λ‚΄μš©μ΄ λ§Žλ‹€κ³  λŠλ‚€λ‹€. AOPλ‚˜ Proxy νŒ¨ν„΄μ— λŒ€ν•΄μ„œ κ³΅λΆ€ν•˜λŠ” 것은 물둠이고, μ‹€μ œλ‘œ μ΄λŸ¬ν•œ ν˜•νƒœλ‘œ μ½”λ“œλ₯Ό μž‘μ„±ν–ˆμ„ λ•Œ μ—¬λŸ¬κ°€μ§€ λ””μžμΈ νŒ¨ν„΄μ„ μ μš©ν•˜κ±°λ‚˜ μ•„λ‹ˆλ©΄ ν…ŒμŠ€νŠΈλ₯Ό μž‘μ„±ν•˜λŠ” 데에 이득이 μžˆλŠ”μ§€λ₯Ό λŠκ»΄λ³΄λŠ” 것이 μ€‘μš”ν•˜λ‹€λŠ” 생각이 λ“ λ‹€.

λ‹€μŒ ν¬μŠ€νŠΈμ—μ„œλŠ” μ»΄ν¬λ„ŒνŠΈ μŠ€μΊ”μ— λŒ€ν•œ λ‚΄μš©μ„ 닀뀄보도둝 ν•˜κ² λ‹€.

끝.