0228 - 0313


# 0228 - 0313

# 0303 - Jackson - ๋‹คํ˜•์„ฑ ๊ด€๋ จ ์• ๋…ธํ…Œ์ด์…˜

Jackson์—์„œ๋Š” ๋‹คํ˜•์„ฑ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด ์„ธ๊ฐ€์ง€ ์• ๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•œ๋‹ค.

  • @JsonTypeInfo : ์ง๋ ฌํ™”์‹œ ํฌํ•จํ•  ํƒ€์ž… ์ •๋ณด์˜ ์„ธ๋ถ€ ์‚ฌํ•ญ์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.
  • @JsonSubTypes : ์• ๋…ธํ…Œ์ด์…˜์ด ๋‹ฌ๋ฆฐ ํƒ€์ž…์˜ ํ•˜์œ„ ํƒ€์ž…์„ ์ง€์ •ํ•œ๋‹ค.
  • @JsonTypeName : ์• ๋…ธํ…Œ์ด์…˜์ด ๋‹ฌ๋ฆฐ ํƒ€์ž…์˜ ๋…ผ๋ฆฌ์  ์ด๋ฆ„์„ ์ง€์ •ํ•œ๋‹ค.

์˜ˆ์‹œ

public class Zoo {
    public Animal animal;

    public Zoo(Animal animal) {
        this.animal = animal;
    }

    @JsonTypeInfo(
            use = JsonTypeInfo.Id.NAME,
            include = JsonTypeInfo.As.PROPERTY,
            property = "type")
    @JsonSubTypes({
            @JsonSubTypes.Type(value = Dog.class, name = "dog"),
            @JsonSubTypes.Type(value = Cat.class, name = "cat")
    })
    public static class Animal {
        public String name;
    }

    @JsonTypeName("dog")
    public static class Dog extends Animal {
        public double barkVolume;

        public Dog(String name) {
            super.name = name;
        }
    }

    @JsonTypeName("cat")
    public static class Cat extends Animal {
        public int lives;
        boolean likesCream;
    }
}
public class PolymorphicTests {
    @Test
    public void WhenPolymorphicBeanProvided_ThenSerialize_ExpectCorrect() throws JsonProcessingException {
        // when
        Zoo.Dog dog = new Zoo.Dog("lacy");
        Zoo zoo = new Zoo(dog);

        // then
        String json = new ObjectMapper().writeValueAsString(zoo);

        // expected
        System.out.println(json);
        assertThat(json, containsString("type"));
        assertThat(json, containsString("dog"));
    }
}

# 0307 - JDK Dynamic Proxy์™€ CGLIB

# Spring AOP๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋‘ ๊ฐ€์ง€ AOP Proxy

Spring AOP๋Š” ํ”„๋ก์‹œ ๊ธฐ๋ฐ˜์œผ๋กœ JDK Dynamic Proxy์™€ CGLIB์„ ํ™œ์šฉํ•˜์—ฌ AOP๋ฅผ ์ œ๊ณตํ•œ๋‹ค

# IoC ์ปจํ…Œ์ด๋„ˆ์™€ AOP Proxy์˜ ๊ด€๊ณ„

๋จผ์ € Spring AOP๋Š” Proxy์˜ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ๊ธฐ๋ฐ˜์œผ๋กœ AOP Proxy๋ฅผ ์ œ๊ณตํ•œ๋‹ค

image

๋‹ค์Œ ๊ทธ๋ฆผ์ฒ˜๋Ÿผ Spring AOP๋Š” ์‚ฌ์šฉ์ž์˜ ํŠน์ • ํ˜ธ์ถœ ์‹œ์ ์— IoC ์ปจํ…Œ์ด๋„ˆ์— ์˜ํ•ด AOP๋ฅผ ํ•  ์ˆ˜ ์žˆ๋Š” Proxy Bean์„ ์ƒ์„ฑํ•ด์ค€๋‹ค. ๋™์ ์œผ๋กœ ์ƒ์„ฑ๋œ Proxy Bean์€ ํƒ€๊นƒ์˜ ๋ฉ”์†Œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜๋Š” ์‹œ์ ์— ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•  ๋ฉ”์†Œ๋“œ๋ฅผ ์ž์ฒด์ ์œผ๋กœ ํŒ๋‹จํ•˜๊ณ  ๊ฐ€๋กœ์ฑ„์–ด ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ฃผ์ž…ํ•˜๋Š”๋ฐ, ์ด์ฒ˜๋Ÿผ ํ˜ธ์ถœ ์‹œ์ ์— ๋™์ ์œผ๋กœ ์œ„๋น™์„ ํ•œ๋‹ค ํ•˜์—ฌ ๋Ÿฐํƒ€์ž„ ์œ„๋น™(Runtime Weaving)์ด๋ผ ํ•œ๋‹ค.

๋”ฐ๋ผ์„œ Spring AOP๋Š” ๋Ÿฐํƒ€์ž„ ์œ„๋น™์˜ ๋ฐฉ์‹์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, Spring์—์„  ๋Ÿฐํƒ€์ž„ ์œ„๋น™์„ ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ƒํ™ฉ์— ๋”ฐ๋ผ JDK Dynamic Proxy์™€ CGLIB ๋ฐฉ์‹์„ ํ†ตํ•ด Proxy Bean์„ ์ƒ์„ฑ์„ ํ•ด์ค€๋‹ค.

# ๋‘ ๊ฐ€์ง€ AOP Proxy๋Š” ์–ด๋– ํ•œ ์ƒํ™ฉ์— ์ƒ์„ฑํ•˜๊ฒŒ ๋˜๋‚˜

Spring์€ AOP Proxy๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ณผ์ •์—์„œ ์ž์ฒด ๊ฒ€์ฆ ๋กœ์ง์„ ํ†ตํ•ด ๋‹ค๊นƒ์˜ ์ธํ„ฐํŽ˜์ด์Šค ์œ ๋ฌด๋ฅผ ํŒ๋‹จํ•œ๋‹ค.

Target์ด๋ž€ ํšก๋‹จ๊ธฐ๋Šฅ(Advice)์ด ์ ์šฉ๋  ๊ฐ์ฒด(Object)๋ฅผ ๋œปํ•œ๋‹ค.

image

์ด๋•Œ ๋งŒ์•ฝ ํƒ€๊นƒ์ด ํ•˜๋‚˜ ์ด์ƒ์˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๋Š” ํด๋ž˜์Šค๋ผ๋ฉด JDK Dynamic Proxy์˜ ๋ฐฉ์‹์œผ๋กœ ์ƒ์„ฑ๋˜๊ณ  ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์€ ํด๋ž˜์Šค๋ผ๋ฉด CGLIB์˜ ๋ฐฉ์‹์œผ๋กœ AOP Proxy๋ฅผ ์ƒ์„ฑํ•œ๋‹ค

# Spring AOP์˜ ๊ทผ๊ฐ„์ด ๋˜๋Š” JDK Dynamic Proxy ๋ฐฉ์‹

์šฐ์„  JDK Dynamic Proxy๋ž€ Java์˜ ๋ฆฌํ”Œ๋ ‰์…˜ ํŒจํ‚ค์ง€์— ์กด์žฌํ•˜๋Š” Proxy๋ผ๋Š” ํด๋ž˜์Šค๋ฅผ ํ†ตํ•ด ์ƒ์„ฑ๋œ Proxy ๊ฐ์ฒด๋ฅผ ์˜๋ฏธํ•œ๋‹ค. ๋ฆฌํ”Œ๋ž™์…˜์˜ Proxy ํด๋ž˜์Šค๊ฐ€ ๋™์ ์œผ๋กœ Proxy๋ฅผ ์ƒ์„ฑํ•ด์ค€๋‹คํ•˜์—ฌ JDK Dynamic Proxy๋ผ ๋ถ€๋ฅธ๋‹ค. ์ด ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ”„๋ก์‹œ๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด์„  ๋ช‡๊ฐ€์ง€ ์กฐ๊ฑด์ด ์žˆ์ง€๋งŒ, ๊ทธ ์ค‘ ํ•ต์‹ฌ์€ ํƒ€๊นƒ์˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ธฐ์ค€์œผ๋กœ Proxy๋ฅผ ์ƒ์„ฑํ•ด์ค€๋‹ค๋Š” ์ ์ด๋‹ค

๋ฌด์—‡๋ณด๋‹ค Spring AOP๋Š” JDK Dynamic Proxy๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ AOP ๊ธฐ์ˆ ์„ ๊ตฌํ˜„ํ–ˆ์„ ๋งŒํผ, JDK Dynamic Proxy๊ฐ€ ์–ด๋–ป๊ฒŒ Proxy๋ฅผ ์ƒ์„ฑํ•˜๋Š”์ง€์— ๋Œ€ํ•œ ๋ถ€๋ถ„์€ Spring AOP๋ฅผ ํ†ตํ•ด Aspect๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค๋ฉด ์ค‘์š”ํ•œ ๋ถ€๋ถ„์ด๋‹ค.

# JDK Dynamic Proxy์˜ Proxy

๋จผ์ € JDK Dynamic Proxy๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Proxy ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๊ฐ„๋‹จํ•˜๋‹ค

Object proxy = Proxy.newProxyInstance(ClassLoader       // ํด๋ž˜์Šค๋กœ๋”
                                    , Class<?>[]        // ํƒ€๊นƒ์˜ ์ธํ„ฐํŽ˜์ด์Šค
                                    , InvocationHandler // ํƒ€๊นƒ์˜ ์ •๋ณด๊ฐ€ ํฌํ•จ๋œ Handler
              										  );

๋‹จ์ˆœํžˆ ๋ฆฌํ”Œ๋ž™์…˜์˜ Proxy ํด๋ž˜์Šค์˜ newProxyInstance ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. JDK Dynamic Proxy๊ฐ€ ์ด ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ฐ€์ง€๊ณ  Proxy ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด์ฃผ๋Š” ๊ณผ์ •์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค

image

  1. ํƒ€๊นƒ์˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ž์ฒด์ ์ธ ๊ฒ€์ฆ ๋กœ์ง์„ ํ†ตํ•ด ProxyFactory์— ์˜ํ•ด ํƒ€๊นƒ์˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์†ํ•œ Proxy ๊ฐ์ฒด ์ƒ์„ฑ
  2. Proxy ๊ฐ์ฒด์— InvocationHandler๋ฅผ ํฌํ•จ์‹œ์ผœ ํ•˜๋‚˜์˜ ๊ฐ์ฒด๋กœ ๋ฐ˜ํ™˜

๋‹ค์Œ๊ณผ ๊ฐ™์ด Proxy๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ณผ์ •์—์„œ ํ•ต์‹ฌ์ ์ธ ๋ถ€๋ถ„์€, ๋ฌด์—‡๋ณด๋‹ค ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ธฐ์ค€์œผ๋กœ Proxy ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด์ค€๋‹ค๋Š” ์ ์ด๋‹ค. ๋”ฐ๋ผ์„œ ๊ตฌํ˜„์ฒด๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์†๋ฐ›์•„์•ผํ•˜๊ณ , @Autowired๋ฅผ ํ†ตํ•ด ์ƒ์„ฑ๋œ Proxy Bean์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„  ๋ฐ˜๋“œ์‹œ ์ธํ„ฐํŽ˜์ด์Šค์˜ ํƒ€์ž…์œผ๋กœ ์ง€์ •ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

์ด๋Ÿฌํ•œ Proxy์˜ ๊ตฌ์กฐ๋ฅผ ์ดํ•ดํ•˜์ง€ ๋ชปํ•œ๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ƒํ™ฉ์ด ๋ฒŒ์–ด์งˆ ์ˆ˜ ์žˆ๋‹ค.

@Controller
public class UserController{
  @Autowired
  private MemberService memberService; // <- Runtime Error ๋ฐœ์ƒ...
  ...
}

@Service
public class MemberService implements UserService{
  @Override
  public Map<String, Object> findUserId(Map<String, Object> params){
    ...isLogic
    return params;
  }
}

MemberService ํด๋ž˜์Šค๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์†๋ฐ›๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— Spring์€ JDK Dynamic Proxy ๋ฐฉ์‹์œผ๋กœ Proxy Bean์„ ์ƒ์„ฑํ•ด์ค€๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋Ÿฐํƒ€์ž„ ์ž…์…‰์…˜์ด ๋ฐœ์ƒํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ ๋Ÿฐํƒ€์ž„์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒ๋˜๋Š” ๋ถ€๋ถ€์€ @Autowired MemberService memberService ๋ถ€๋ถ„์ด๋‹ค.

JDK Dynamic Proxy๋Š” ์ธํ„ฐํŽ˜์ด์Šค ํƒ€์ž…์œผ๋กœ DI๋ฅผ ๋ฐ›์•„์ค˜์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, @Autowired private UserService๋กœ ํ˜•์‹์„ ๊ตฌ์„ฑํ•ด์ค˜์•ผ ํ•œ๋‹ค.

# ์ธํ„ฐํŽ˜์ด์Šค ๊ธฐ์ค€ ๊ทธ๋ฆฌ๊ณ  ๋‚ด๋ถ€์ ์ธ ๊ฒ€์ฆ ์ฝ”๋“œ

๋‹ค๋ฅธ ๊ด€์ ์—์„œ ๋ณด์ž๋ฉด JDK Dynamic Proxy๋Š” Proxy ํŒจํ„ด์˜ ๊ด€์ ์„ ๊ตฌํ˜„ํ•œ ๊ตฌํ˜„์ฒด๋ผ ํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด Proxy ํŒจํ„ด์€ ์ ‘๊ทผ์ œ์–ด์˜ ๋ชฉ์ ์œผ๋กœ Proxy๋ฅผ ๊ตฌ์„ฑํ•œ๋‹ค๋Š” ์ ใ…‡๋„ ์ค‘์š”ํ•˜์ง€๋งŒ, ๋ฌด์—‡๋ณด๋‹ค ์‚ฌ์šฉ์ž์˜ ์š”์ฒญ์ด ๊ธฐ์กด์˜ ํƒ€๊นƒ์„ ๊ทธ๋Œ€๋กœ ๋ฐ”๋ผ๋ณผ ์ˆ˜ ์žˆ๋„๋ก ํƒ€๊นƒ์— ๋Œ€ํ•œ ์œ„์ž„์ฝ”๋“œ Proxy ๊ฐ์ฒด์— ์ž‘์„ฑํ•ด์ค˜์•ผ ํ•œ๋‹ค. ์ƒ์„ฑ๋œ Proxy ๊ฐ์ฒด์˜ ํƒ€๊นƒ์— ๋Œ€ํ•œ ์œ„์ž„์ฝ”๋“œ๋Š” ๋ฐ”๋กœ InvocationHAndler์— ์ž‘์„ฑํ•ด์ค˜์•ผ ํ•œ๋‹ค.

image

๋”ฐ๋ผ์„œ ์‚ฌ์šฉ์ž์˜ ์š”์ฒญ์ด ์ตœ์ข…์ ์œผ๋กœ ์ƒ์„ฑ๋œ Proxy์˜ ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ํ˜ธ์ถœํ•  ๋•Œ ๋‚ด๋ถ€์ ์œผ๋กœ invoke์— ๋Œ€ํ•œ ๊ฒ€์ฆ๊ณผ์ •์ด ์ด๋ค„์ง„๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

public Object invoke(Object proxy, Method proxyMethod, Object[] args) throws Throwable {
  Method targetMethod = null;
  // ์ฃผ์ž…๋œ ํƒ€๊นƒ ๊ฐ์ฒด์— ๋Œ€ํ•œ ๊ฒ€์ฆ ์ฝ”๋“œ
  if (!cachedMethodMap.containsKey(proxyMethod)) {
    targetMethod = target.getClass().getMethod(proxyMethod.getName(), proxyMethod.getParameterTypes());
    cachedMethodMap.put(proxyMethod, targetMethod);
  } else {
    targetMethod = cachedMethodMap.get(proxyMethod);
  }

  // ํƒ€๊นƒ์˜ ๋ฉ”์†Œ๋“œ ์‹คํ–‰
  Ojbect retVal = targetMethod.invoke(target, args);
  return retVal;
}

์ด ๊ณผ์ •์—์„œ ๊ฒ€์ฆ๊ณผ์ •์ด ์ด๋ค„์ง€๋Š” ๊นŒ๋‹ญ์€ ๋‹ค๋ฆ„์•„๋‹Œ Proxy๊ฐ€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ธํ„ฐํŽ˜์ด์Šค์— ๋Œ€ํ•œ Proxy๋งŒ์„ ์ƒ์„ฑํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๋”ฐ๋ผ์„œ ๊ฐœ๋ฐœ์ž๊ฐ€ ํƒ€๊นƒ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ž˜ ๋ชป ์ฃผ์ž…ํ•  ๊ฒฝ์šฐ ๋Œ€๋น„ํ•˜์—ฌ JDK Dynamic Proxy๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ ์ฃผ์ž…๋œ ํƒ€๊นƒ์— ๋Œ€ํ•œ ๊ฒ€์ฆ์ฝ”๋“œ๋ฅผ ํ˜•์„ฑํ•˜๊ณ  ์žˆ๋‹ค.

# CGLib(Code Generator Library)

CGLib์€ Code Generator Library์˜ ์•ฝ์ž๋กœ, ํด๋ž˜์Šค์˜ ๋ฐ”์ดํŠธ์ฝ”๋“œ๋ฅผ ์กฐ์ž‘ํ•˜์—ฌ Proxy ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.

Spring์€ CGLib์„ ์‚ฌ์šฉํ•˜์—ฌ ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์•„๋‹Œ ํƒ€๊นƒ์˜ ํด๋ž˜์Šค์— ๋Œ€ํ•ด์„œ๋„ Proxy๋ฅผ ์ƒ์„ฑํ•ด์ค€๋‹ค. CGLib์€ Enhancer๋ผ๋Š” ํด๋ž˜์Šค๋ฅผ ํ†ตํ•ด Proxy๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

Enhancer enhancer = new Enhancer();
         enhancer.setSuperclass(MemberService.class); // ํƒ€๊นƒ ํด๋ž˜์Šค
         enhancer.setCallback(MethodInterceptor);     // Handler
Object proxy = enhancer.create(); // Proxy ์ƒ์„ฑ

image

๋‹ค์Œ๊ณผ ๊ฐ™์ด CGLib์€ ํƒ€๊นƒ์˜ ํด๋ž˜์Šค๋ฅผ ์ƒ์†๋ฐ›์•„ Proxy๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. ์ด ๊ณผ์ •์—์„œ CGLib์€ ํƒ€๊นƒ ํด๋ž˜์Šค์— ํฌํ•จ๋œ ๋ชจ๋“  ๋ฉ”์†Œ๋“œ๋ฅผ ์žฌ์ •์˜ํ•˜์—ฌ Proxy๋ฅผ ์ƒ์„ฑํ•ด์ค€๋‹ค.
์ด ๋•Œ๋ฌธ์— CGLib์€ Final ๋ฉ”์†Œ๋“œ ๋˜๋Š” ํด๋ž˜์Šค์— ๋Œ€ํ•ด ์žฌ์ •์˜๋ฅผ ํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ Proxy๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ์ง€๋งŒ, CGLib์€ ๋ฐ”์ดํŠธ ์ฝ”๋“œ๋กœ ์กฐ์ž‘ํ•˜์—ฌ Proxy๋ฅผ ์ƒ์„ฑํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ์„ฑ๋Šฅ์— ๋Œ€ํ•œ ๋ถ€๋ถ„์ด JDK Dynamic Proxy๋ณด๋‹ค ์ข‹๋‹ค.

# invoke์˜ ์ฐจ์ด, ์„ฑ๋Šฅ์˜ ์ฐจ์ด

์„ฑ๋Šฅ์˜ ์ฐจ์ด์˜ ๊ทผ๋ณธ์ ์ธ ์ด์œ ๋Š” CGLib์€ ํƒ€๊นƒ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ œ๊ณต ๋ฐ›๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๋”ฐ๋ผ์„œ CGLib์€ ์ œ๊ณต๋ฐ›์€ ํƒ€๊นƒ ํด๋ž˜์Šค์— ๋Œ€ํ•œ ๋ฐ”์ดํŠธ ์ฝ”๋“œ๋ฅผ ์กฐ์ž‘ํ•˜์—ฌ Proxy๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ๋•Œ๋ฌธ์—, Handler์•ˆ์—์„œ ํƒ€๊นƒ์˜ ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฝ”๋“œ๊ฐ€ ํ˜•์„ฑ๋œ๋‹ค.

public Object invoke(Object proxy, Method proxyMethod, Object[] args) throws Throwable {
  Method targetMethod = target.getClass().getMethod(proxyMethod.getName(), proxyMethod.getParameterTypes());
  Ojbect retVal = targetMethod.invoke(target, args);
  return retVal;
}
  1. ๋ฉ”์†Œ๋“œ๊ฐ€ ์ฒ˜์Œ ํ˜ธ์ถœ๋˜์—ˆ์„ ๋•Œ ๋™์ ์œผ๋กœ ํƒ€๊นƒ์˜ ํด๋ž˜์Šค์˜ ๋ฐ”์ดํŠธ ์ฝ”๋“œ๋ฅผ ์กฐ์ž‘
  2. ์ดํ›„ ํ˜ธ์ถœ์‹œ์—” ์กฐ์ž‘๋œ ๋ฐ”์ดํŠธ ์ฝ”๋“œ๋ฅผ ์žฌ์‚ฌ์šฉ

CGLib์€ ์„ฑ๋Šฅ์ด ์ข‹๊ธดํ•˜์ง€๋งŒ, Spring์€ JDK Dynamic Proxy๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ Proxy๋ฅผ ์ƒ์„ฑํ•ด์ฃผ๊ณ  ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ์–ด๋Š ์–ด๋Š ์‹œ์  ๋ถ€ํ„ฐ Spring Boot์—์„  ๋ฌธ์ œ๊ฐ€ ๋˜์—ˆ๋˜ ๋ถ€๋ถ„๋“ค์ด ๊ฐœ์„ ๋˜์–ด ์•ˆ์ •ํ™”๊ฐ€ ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— CGLib์„ ๋ฐฉ์‹์œผ๋กœ Proxy๋ฅผ ์ƒ์„ฑํ•ด์ฃผ๊ณ  ์žˆ๋‹ค.


# 0308 - ๋ฆฌํ”Œ๋ ‰์…˜ API (Reflection API)

# ๋ฆฌํ”Œ๋ ‰์…˜ API๋ž€?

  • ๊ตฌ์ฒด์ ์ธ ํด๋ž˜์Šค ํƒ€์ž…์„ ์•Œ์ง€ ๋ชปํ•ด๋„ ๊ทธ ํด๋ž˜์Šค์˜ ์ •๋ณด(๋ฉ”์„œ๋“œ. ํƒ€์ž…, ๋ณ€์ˆ˜ ๋“ฑ๋“ฑ)์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ์ž๋ฐ” API์ด๋‹ค.
  • ๋ฆฌํ”Œ๋ ‰์…˜ API๋Š” ์ž๋ฐ” ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ๋กœ๋“œ๋œ ํด๋ž˜์Šค์˜ ํ•„๋“œ/๋ฉ”์„œ๋“œ/์ƒ์„ฑ์ž๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ๊ฒŒ ์ง€์›ํ•œ๋‹ค.
  • ๋˜ํ•œ ํด๋ž˜์Šค์˜ ์ ‘๊ทผ ์ œํ•œ์ž์™€ ์ƒ๊ด€์—†์ด ํด๋ž˜์Šค์˜ ํ•„๋“œ/๋ฉ”์„œ๋“œ/์ƒ์„ฑ์ž๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ์ง€์›ํ•œ๋‹ค.

ํž™ ์˜์—ญ์— ๋กœ๋“œ๋œ Class ํƒ€์ž…์˜ ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด, ์›ํ•˜๋Š” ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ์ง€์›ํ•˜๊ณ , ์ธ์Šคํ„ด์Šค์˜ ํ•„๋“œ์™€ ๋ฉ”์„œ๋“œ๋ฅผ ์ ‘๊ทผ ์ œ์–ด์ž์™€ ์ƒ๊ด€์—†์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•˜๋Š” API.

# ํด๋ž˜์Šค์˜ ์ •๋ณด ์กฐํšŒ

  1. ์ง์ ‘ ์„ ์–ธ
Class<Book> bookClass = Book.class;
  1. ์ธ์Šคํ„ด์Šค๋ฅผ ์ด์šฉํ•œ getClass() ๋ฉ”์†Œ๋“œ
Book book = new Book();
Class<? extends Book> bookClassFromInstance = book.getClass();
  1. Class.forName() ๋ฉ”์†Œ๋“œ
try {
	//์ฃผ๋กœ JDBC ์˜ˆ์ œ์—์„œ ๋งŽ์ด ๋ด„
	Class<?> bookClassFromPackageString = Class.forName("org.example.Book");
} catch (ClassNotFoundException e) {
	e.printStackTrace();
}

# Class<?> ํƒ€์ž…์„ ์ด์šฉํ•œ ํด๋ž˜์Šค ์ •๋ณด ์กฐํšŒ

# ํ•„๋“œ ์ •๋ณด ์กฐํšŒ

  • (Field[]) getFields() : ํด๋ž˜์Šค์— ์„ ์–ธ๋œ ํ•„๋“œ๋“ค ๋ฐ˜ํ™˜(public ์ ‘๊ทผ์ง€์‹œ์ž๋งŒ)
  • (Field) getField(String name) : name์— ํ•ด๋‹นํ•˜๋Š” ํ•„๋“œ๋ฅผ ๋ฐ˜ํ™˜(์—†๋‹ค๋ฉด NoSuchFieldException)
  • (Field[]) getDeclaredFields() : ํด๋ž˜์Šค์— ์„ ์–ธ๋œ ๋ชจ๋“  ํ•„๋“œ๋“ค ๋ฐ˜ํ™˜(private ๊นŒ์ง€๋„ ํฌํ•จ)
  • (Field) getDeclaredField(String name) : name์— ํ•ด๋‹นํ•˜๋Š” ํ•„๋“œ๋ฅผ ๋ฐ˜ํ™˜(์—†์œผ๋ฉด NoSuchFieldEception, priavte ํฌํ•จ)

# ๋ฉ”์†Œ๋“œ ์ ‘๊ทผ

Method[] methods = bookClass.getMethods();
Arrays.stream(methods).forEach(System.out::println); //Object ์ƒ์† ๋ฉ”์†Œ๋“œ ํฌํ•จ.

# ์ƒ์„ฑ์ž ์ ‘๊ทผ

Constructor[] constructors = bookClass.getConstructors();
Arrays.stream(constructors).forEach(System.out::println);

# ๋ถ€๋ชจํด๋ž˜์Šค ์ ‘๊ทผ

Class<? super MyBook> superClass = MyBook.class.getSuperclass();
System.out.println(superClass);

# ์ธํ„ฐํŽ˜์ด์Šค ์ ‘๊ทผ

Class<?>[] implementsInterface = MyBook.class.getInterfaces();
Arrays.stream(implementsInterface).forEach(System.out::println);

# ๋ฆฌํ”Œ๋ ‰์…˜ API ์‚ฌ์šฉ์ด์œ 

์บก์Šํ™”์™€ ์€๋‹‰์„ฑ์„ ๋ฌด์‹œํ•˜๋ฉด์„œ๋„ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ๋Š”, ๋Ÿฐํƒ€์ž„์— ๋น„๋กœ์†Œ ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋˜์–ด์•ผ ํ•  ํ•„์š”์„ฑ์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์ธ๋‹ค.


# 0309 - QueryDSL ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

  • QueryDSL์€ HQL(Hibernate Query Language) ์ฟผ๋ฆฌ๋ฅผ ํƒ€์ž…์— ์•ˆ์ „ํ•˜๊ฒŒ ์ƒ์„ฑ ๋ฐ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ๋‹ค.
  • ์ž๋ฐ” ์ฝ”๋“œ ๊ธฐ๋ฐ˜์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•˜๊ฒŒ ๋„์™€์ค€๋‹ค.

# QueryDSL ์˜ˆ์ œ

# gradle ์„ค์ •

plugins {
    // ...
    id "com.ewerk.gradle.plugins.querydsl" version "1.0.10" // ์ถ”๊ฐ€
    // ...
}

// ...

dependencies {
    // ...
    implementation 'com.querydsl:querydsl-jpa' // ์ถ”๊ฐ€
    // ...
}

// ...

// queryDSL์ด ์ƒ์„ฑํ•˜๋Š” QClass ๊ฒฝ๋กœ ์„ค์ •
def querydslDir = "$buildDir/generated/querydsl"

querydsl {
    jpa = true
    querydslSourcesDir = querydslDir
}

sourceSets {
    main.java.srcDir querydslDir
}

configurations {
    querydsl.extendsFrom compileClasspath
}

compileQuerydsl {
    options.annotationProcessorPath = configurations.querydsl
}

# Qํด๋ž˜์Šค ๋งŒ๋“ค๊ธฐ

image Tasks -> other -> compileJava ์‹คํ–‰์‹œ ์ง€์ •ํ•ด๋‘” build/์ง€์ •๊ฒฝ๋กœ์— Qํด๋ž˜์Šค๊ฐ€ ์ƒ์„ฑ

# Config ์„ค์ •

@Configuration
public class QueryDSLConfig {

    @PersistenceContext
    private EntityManager entityManager;

    @Bean
    public JPAQueryFactory jpaQueryFactory() {
        return new JPAQueryFactory(entityManager);
    }
}

# ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

  1. Post ์—”ํ‹ฐํ‹ฐ
@Entity
public class Post {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String title;

    @Column(nullable = false)
    private String content;

    // ...
}
  1. PostRepository
public interface PostRepository extends JpaRepository<Post, Long> {
}
  1. PostRepositorySupport
@Repository
public class PostRepositorySupport extends QuerydslRepositorySupport {

    private final JPAQueryFactory jpaQueryFactory;

    public PostRepositorySupport(final JPAQueryFactory jpaQueryFactory) {
        super(Post.class);
        this.jpaQueryFactory = jpaQueryFactory;
    }

    public List<Post> findByTitle(final String title) {
        return jpaQueryFactory.selectFrom(post)
                .where(post.title.eq(title))
                .fetch();
    }
}
  • ์ด๋–„ selectFrom(post)์˜ post๋Š” ์•„๊นŒ ๋งŒ๋“  Qํด๋ž˜์Šค์—์„œ ์‚ฌ์šฉํ•œ๋‹ค

# Spring Data Jpa Custom Repository ์ ์šฉ ์‚ฌ์šฉ๋ฒ•

์œ„์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๋ฉด ํ•ญ์ƒ 2๊ฐœ์˜ Repository(QueryDSL์˜ Custom Repository, JpaRepository๋ฅผ ์ƒ์†ํ•œ Repository)๋ฅผ ์˜์กด์„ฑ์œผ๋กœ ๋ฐ›์•„์•ผํ•œ๋‹ค.

  1. CustomizedPostRepository
public interface CustomizedPostRepository {
    List<Post> findByTitle(final String title);
}
  1. CustomizedPostRepositoryImpl
public class CustomizedPostRepositoryImpl implements CustomizedPostRepository {

    private final JPAQueryFactory jpaQueryFactory;

    private CustomizedPostRepositoryImpl(final JPAQueryFactory jpaQueryFactory) {
        this.jpaQueryFactory = jpaQueryFactory;
    }

    @Override
    public List<Post> findByTitle(final String title) {
        return jpaQueryFactory.selectFrom(post)
                .where(post.title.eq(title))
                .fetch();
    }
}
  1. PostRepository
public interface PostRepository extends JpaRepository<Post, Long>, CustomizedPostRepository {
}

์ด๋ ‡๊ฒŒ ๊ตฌ์„ฑํ•˜๋ฉด CustomizedPostRepositoryImpl์˜ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. PostRepository๋Š” ์–ด๋–ป๊ฒŒ CustomizedPostRepository๋ฅผ ์ƒ์†๋ฐ›์•„์„œ CustomizedPostRepositoryImpl์˜ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์„๊นŒ?

Spring ๊ณต์‹๋ฌธ์„œ๋ฅผ ๋ณด๋ฉด ์ปค์Šคํ…€ํ•œ Repostory๋Š” ๋ณ„๋„์˜ ์„ค์ •์„ ํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด ~Impl์ ‘๋ฏธ์‚ฌ๋ฅผ ๋ถ™์—ฌ์•ผ๋งŒ ์Šคํ”„๋ง์ด ์ฐพ์„ ์ˆ˜ ์žˆ๋‹ค. ๊ด€๋ จํ•ด์„œ๋Š” spring-data์— ํฌํ•จ๋œ RepositoryConfigurationSourceSupportํด๋ž˜์Šค์™€ AnnotationRepositoryConfigurationSourceํด๋ž˜์Šค์˜ ๋‚ด๋ถ€ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด ์•Œ ์ˆ˜ ์žˆ๋‹ค.

The most important part of the class name that corresponds to the fragment interface is the Impl postfix.


# 0310 - OpenAPI์™€ Swagger/redoc๋ž€

# OpenAPI Spec

OpenAPI Specification(OAS)์€ RESTful API๋ฅผ ๊ธฐ์ˆ ํ•˜๋Š” ํ‘œ์ค€์œผ๋กœ ์„œ๋น„์Šค์—์„œ ์ œ๊ณตํ•˜๋Š” API์˜ ๊ธฐ๋Šฅ๊ณผ End Point๋ฅผ ๊ฐœ๋ฐœ์ž๋‚˜ ์‹œ์Šคํ…œ์ด ์ž๋™์œผ๋กœ ๋ฐœ๊ฒฌํ•˜๊ณ  ์ฒ˜๋ฆฌํ•˜๋Š”๋ฐ ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

OAS๋Š” json์ด๋‚˜ ymlํ˜•์‹์œผ๋กœ ๊ธฐ์ˆ ํ•ด์•ผ ํ•˜๋ฉฐ OASํŒŒ์ผ์„ ์ฝ์–ด์„œ ๋””ํ”Œ๋กœ์ด ํ•ด์ฃผ๋Š” ๋„๊ตฌ(ex. swagger-ui)๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ธŒ๋ผ์šฐ์ €์—์„œ ํŽธ๋ฆฌํ•˜๊ฒŒ API ๋ฌธ์„œ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

OAS๋Š” ์˜ˆ์ „์—๋Š” Swagger spec์œผ๋กœ ๋ถˆ๋ ธ์œผ๋ฉฐ 3.0 ๋ถ€ํ„ฐ OpenAPI 3.0 Specification (opens new window)๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ํ‘œ์ค€ํ™” ๋˜์—ˆ๋‹ค

# Swagger

Swagger๋Š” OpenAPI Spec์— ๋งž๊ฒŒ ๋””์ž์ธํ•˜๊ณ  ๋ฌธ์„œํ™”ํ•˜๊ณ  ๋นŒ๋“œํ•˜๊ธฐ ์œ„ํ•œ ๋„๊ตฌ๋“ค์˜ ๋ชจ์Œ์œผ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ตฌ์„ฑ์š”์†Œ๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ๋‹ค.

  • Swagger Editor - ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ฐ˜์˜ ํŽธ์ง‘๊ธฐ๋กœ OpenAPI spec์„ ์‰ฝ๊ฒŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค€๋‹ค.
  • Swagger UI - OpenAPI spec๋ฌธ์„œ๋ฅผ ๋””ํ”Œ๋กœ์ดํ•˜๊ณ  ๋ธŒ๋ผ์šฐ์ €์—์„œ ์˜ˆ์˜๊ฒŒ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค€๋‹ค. swagger-ui์™€ ๋น„์Šทํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ redoc์ด ์žˆ๋‹ค.
  • Swagger Codegen - OpenAPI spec์— ๋งž๊ฒŒ ์„œ๋ฒ„๋‚˜ ํด๋ผ์ด์–ธํŠธ์˜ stub code๋ฅผ ์ƒ์„ฑํ•ด์ค€๋‹ค. ๊ฐœ๋ฐœ์ž๋Š” ์ƒ์„ฑ๋œ ์ฝ”๋“œ์— ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์— ์ง‘์ค‘ํ•ด์„œ ๊ตฌํ˜„ํ•˜๋ฉด ๋œ๋‹ค.

# Redoc

redoc (opens new window)์€ OpenAPI spec ํŒŒ์ผ์„ ์ฝ์–ด์„œ ๋””ํ”Œ๋กœ์ดํ•ด์ฃผ๋Š” ๋„๊ตฌ๋กœ Swagger-UI์™€ ๋น„์Šทํ•œ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

์„ค์น˜์™€ ์‚ฌ์šฉ์ด ์•„์ฃผ ๊ฐ„ํŽธํ•œ ์žฅ์ ์ด ์žˆ์ง€๋งŒ swagger-ui๋ž‘ ๋‹ฌ๋ฆฌ ๋ธŒ๋ผ์šฐ์ €์—์„œ API ํ…Œ์ŠคํŠธ ๊ธฐ๋Šฅ์„ ํ•ด๋ณผ์ˆ˜๋Š” ์—†๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค.


# 0311 - ์ž๋ฐ” ๋กœ๊ทธ ํ”„๋ ˆ์ž„์›Œํฌ

# ๋กœ๊ทธ ์‹œ์Šคํ…œ

๋กœ๊ทธ ์‹œ์Šคํ…œ์€ ์†Œํ”„ํŠธ์›จ์–ด์˜ ์ด๋ฒคํŠธ๋ฅผ ๊ธฐ๋ก ํ•จ์œผ๋กœ์จ, ์†Œํ”„ํŠธ ์›จ์–ด ๋™์ž‘ ์ƒํƒœ๋ฅผ ํŒŒ์•…ํ•˜๊ณ  ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„๋•Œ ์ด ๋™์ž‘ ํŒŒ์•…์„ ์œ„ํ•ด์„œ ์†Œํ”„ํŠธ์›จ์–ด์˜ ๋ฌธ์ œ๋ฅผ ์ฐพ์•„๋‚ด๊ณ  ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋””์ž์ธ ๋˜์—ˆ๋‹ค.
์ฃผ๋กœ ๋กœ๊ทธ ํŒŒ์ผ์ด๋ผ๋Š” ํ˜•ํƒœ๋กœ ํ•˜๋‚˜์˜ ํŒŒ์ผ์— ์ด๋ฒคํŠธ๋“ค์„ ๊ธฐ๋กํ•˜์˜€๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์†Œํ”„ํŠธ์›จ์–ด ์Šคํƒ์ด OS, ๋ฏธ๋“ค์›จ์–ด, ์‚ฌ์šฉ์ž ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜(์ž๋ฐ”๋‚˜ ํŒŒ์ด์ฌ๋“ฑ์œผ๋กœ ๊ตฌํ˜„๋œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜)์œผ๋กœ ์ ์  ๋‹ค์ค‘ํ™”๋˜๊ณ  ์‹œ์Šคํ…œ์ด ๋Œ€ํ˜•ํ™” ๋˜๋ฉด์„œ ํ•œ๋Œ€๊ฐ€ ์•„๋‹ˆ๋ผ ์—ฌ๋Ÿฌ๋Œ€์˜ ์„œ๋ฒ„์— ๋กœ๊ทธ๋ฅผ ๊ธฐ๋กํ•˜๊ณ  ๋˜ํ•œ ๋งˆ์ดํฌ๋กœ ์„œ๋น„์Šค ์•„ํ‚คํ…์ฒ˜๋กœ ์ธํ•˜์—ฌ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ถ„์‚ฐ๋จ์— ๋”ฐ๋ผ์„œ ๋กœ๊ทธ๋ฅผ ์ˆ˜์ง‘ํ•ด์•ผํ•  ํฌ์ธํŠธ๊ฐ€ ๋งŽ์•„์ง€๊ฒŒ ๋˜์—ˆ๋”ฐ. ์ด๋กœ ์ธํ•ด์„œ ๋กœ๊ทธ ์‹œ์Šคํ…œ์ด ๋ถ„์‚ฐ ํ™˜๊ฒฝ์„ ์ง€์›ํ•ด์•ผ ํ•  ํ•„์š”๊ฐ€ ๋˜์—ˆ๊ณ , ๋‹จ์ˆœํžˆ ํŒŒ์ผ๋กœ ๋กœ๊ทธ๋ฅผ ๊ธฐ๋กํ•˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋Š” ์ด๋Ÿฌํ•œ ์—ฌ๋Ÿฌ์‹œ์Šคํ…œ๊ณผ ๋‹ค์ค‘ ๊ณ„์ธต์— ๋Œ€ํ•œ ๋ชจ๋‹ˆํ„ฐ๋ง์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค.

๋˜ํ•œ ๋ฐ์ดํ„ฐ ๋ถ„์„์˜ ์ค‘์š”์„ฑ์ด ๋Œ€๋‘๋จ์— ๋”ฐ๋ผ์„œ, ์—๋Ÿฌ๋“ฑ์˜ ๋™์ž‘ ํŒŒ์•„์„ฑ์˜ ๋กœ๊ทธ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์‚ฌ์šฉ์ž์˜ ์•คํ‹ฐ๋น„ํ‹ฐ๋ฅผ ์ˆ˜์ง‘ํ•˜์—ฌ ๋ฐ์ดํ„ฐ ๋ถ„์„์— ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘ ์—ญ์‹œ ๋กœ๊ทธ ์‹œ์Šคํ…œ์„ ํ†ตํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ๋‹ค.

# ์ข‹์€ ๋กœ๊ทธ ์‹œ์Šคํ…œ

  • ๋กœ๊ทธ ๋ฉ”์‹œ์ง€๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋™์ž‘์„ ์ž˜ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋„๋ก ์ถฉ๋ถ„ํžˆ ๊ตฌ์ฒด์ ์ด์–ด์•ผ ํ•œ๋‹ค.
  • ๋กœ๊ทธ ๋ฉ”์‹œ์ง€๋ฅผ ๊ธฐ๋กํ•˜๋Š”๋ฐ ์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ์—†์–ด์•ผ ํ•œ๋‹ค.
  • ์–ด๋– ํ•œ ๋ฐฐํฌ ํ™˜๊ฒฝ์ด๋ผ๋„ ๋กœ๊ทธ๋ฅผ ์ˆ˜์ง‘ํ•˜๊ณ  ์ €์žฅํ•  ์ˆ˜ ์žˆ๋„๋ก ์ถฉ๋ถ„ํžˆ ์œ ์—ฐํ•ด์•ผ ํ•œ๋‹ค. (๋ถ„์‚ฐ ํ™˜๊ฒฝ ์ง€์›, ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํƒ€ ์ง€์›๋“ฑ..)

# ์ž๋ฐ” ๋กœ๊น… ํ”„๋ ˆ์ž„์›Œํฌ

์ž๋ฐ”๋Š” ์—ญ์‚ฌ๊ฐ€ ์˜ค๋ž˜๋œ ๋งŒํผ ๋งŽ์€ ๋กœ๊น… ํ”„๋ ˆ์ž„์›์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค. log4j, logback, log4j2, apache common logging, SLF4J ๋“ฑ ๋‹ค์–‘ํ•œ ํ”„๋ ˆ์ž„์›Œํฌ๋“ค์ด ์žˆ๋‹ค.

# SLF4J

SLF4J๋Š” (Simple Logging Facade for Java)์˜ ์•ฝ์ž๋กœ ์ด๋ฆ„์ด ๋œปํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์ด ๋กœ๊น…์— ๋Œ€ํ•œ Facade ํŒจํ„ด์ด๋‹ค. SLF4J๋Š” ์ž์ฒด๊ฐ€ ๋กœ๊น… ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์•„๋‹ˆ๋ผ, ๋‹ค์–‘ํ•œ ๋กœ๊น… ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๊ฐ™์€ API๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ๋Š” ์ถ”์ƒํ™” ๊ณ„์ธต์ด๋‹ค. ๊ทธ๋ž˜์„œ ๋‹ค๋ฅธ ๋กœ๊ทธ ํ”„๋ ˆ์ž„์›Œํฌ์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š”๋ฐ, ๋ณดํ†ต Log4J, Logback, Log4J2๋“ฑ์ด ๋งŽ์ด ์‚ฌ์šฉ๋œ๋‹ค. ์ฆ‰ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ SLF4J API ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด์„œ ํ˜ธ์ถœํ•˜์ง€๋งŒ, ์‹ค์ œ๋กœ ํ˜ธ์ถœ๋˜๋Š” ๋กœ๊น… ํ”„๋ ˆ์ž„์›Œํฌ๋Š” ๋‹ค๋ฅธ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค๋Š” ์ด์•ผ๊ธฐ๋‹ค. ์ด๋ ‡๊ฒŒ ์ถ”์ƒํ™”๋ฅผ ํ†ตํ•ด์„œ ์šฉ๋„์™€ ๋ชฉ์ ์— ๋งž๊ฒŒ ๋‹ค๋ฅธ ๋กœ๊น…ํ”„๋ ˆ์ž„์›Œํฌ๋กœ ์‰ฝ๊ฒŒ ์ „ํ™˜์ด ๊ฐ€๋Šฅํ•จ์€ ๋ฌผ๋ก ์ด๊ณ , ๋กœ๊น…์— ํ•„์š”ํ•œ ์ฝ”๋“œ๋“ค์„ ์ถ”์ƒํ™”ํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์—, ํ›จ์”ฌ ์‰ฝ๊ณ  ๊ฐ„๋‹จํ•˜๊ฒŒ ๋กœ๊น…์ด ๊ฐ€๋Šฅํ•˜๋‹ค. apache common logging ์—ญ์‹œ, SLF4J์™€ ๊ฐ™์ด ๋‹ค๋ฅธ ๋กœ๊น… ํ”„๋ ˆ์ž„์›Œํฌ ๋“ค์„ ์ถ”์ƒํ™” ํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค.

# Log4J

Log4J๋Š” ์ด์ค‘์—์„œ ๊ฐ€์žฅ ์˜ค๋ž˜๋œ ๋กœ๊ทธํ”„๋ ˆ์ž„์›Œํฌ๋กœ ๋กœ๊ทธ ํ”„๋ ˆ์ž„์›Œํฌ์— ๋Œ€ํ•œ ์ดˆ๋ฐ˜ ๊ฐœ๋…์„ ์„ค์ •ํ–ˆ๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ํ˜„์žฌ๋Š” ๊ฐœ๋ฐœ์ด ์ค‘์ง€๋˜๊ณ , Log4J2๋กœ ์ƒˆ๋กœ์šด ๋ฒ„์ „์œผ๋กœ ๋ณ€๊ฒฝ๋˜์—ˆ๋‹ค.

# Logback

์•„๋งˆ ํ˜„์žฌ ๊ตญ๋‚ด์—์„œ ๊ฐ€์žฅ ๋„๋ฆฌ ๋งŽ์ด ์‚ฌ์šฉ๋˜๊ณ  ์žˆ๋Š” ๋กœ๊ทธ ํ”„๋ ˆ์ž„์›Œํฌ์ผ๊ฒƒ์ด๋‹ค. Log4J ๊ฐœ๋ฐœ์ž๊ฐ€ ๊ฐœ๋ฐœํ•œ ๋กœ๊ทธ ํ”„๋ ˆ์ž„์›Œํฌ๋กœ ์ฃผ๋กœ Log4J ์„ฑ๋Šฅ ๋ถ€๋ถ„์— ๋Œ€ํ•œ ๊ฐœ์„  ์ž‘์—…์ด ๋งŽ์ด ์ด๋ฃจ์–ด ์กŒ๋‹ค. SLF4J์™€ ๋„ค์ดํ‹ฐ๋ธŒ๋กœ ์—ฐ๋™์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

# Log4J2

๊ฐ€์žฅ ๊ทผ๋ž˜์— ๋‚˜์˜จ ํ”„๋ ˆ์ž„์›Œํฌ๋กœ Logback ๋ณด๋‹ค ํ›„์— ๋‚˜์˜ค๊ณ , ๊ฐ€์žฅ ๋น ๋ฅธ ์„ฑ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค. Logback๊ณผ SLF4J์‚ฌ์ด์˜ ์—ฐ๋™ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜์˜€์œผ๋ฉด ๋น„๋™๊ธฐ ๋กœ๊น…์„ ์ œ๊ณตํ•˜์—ฌ, ํŠนํžˆ ๋ฉ€ํ‹ฐ ์“ฐ๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ๋†’์€ ์„ฑ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค.

๋˜ํ•œ ๊ทผ๋ž˜์˜ ๋กœ๊น… ์‹œ์Šคํ…œ๋“ค์€ ๋กœ๊ทธ๋ฅผ ํŒŒ์ผ๋กœ ๊ธฐ๋กํ•˜๊ธฐ ๋ณด๋‹ค๋Š” ELK(Elastic Search)๋‚˜ Kafka๋“ฑ ์™ธ๋ถ€ ์‹œ์Šคํ…œ์œผ๋กœ ๋กœ๊ทธ๋ฅผ ์ „์†กํ•˜์—ฌ ๋ชจ์œผ๋Š” ํ˜•ํƒœ๋ฅผ ๋งŽ์ด ์ทจํ•˜๊ธฐ ๋–„๋ฌธ์— ์ด์— ๋Œ€ํ•œ ์—ฐ๋™์„ Appender๋ฅผ ํ†ตํ•ด์„œ ์ œ๊ณตํ•œ๋‹ค.

์ œ๊ณต๋˜๋Š” Appender๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • Console
  • File, RollingFile, MemoryMappedFile
  • Flume, Kafka, JDBC, JMS, Socket, ZeroMQ
  • SMTP(emails on errors, woo!)
  • ...much more
Last update: September 13, 2022 21:44
Contributors: ahnjs