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๋ฅผ ์ ๊ณตํ๋ค
๋ค์ ๊ทธ๋ฆผ์ฒ๋ผ 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)๋ฅผ ๋ปํ๋ค.
์ด๋ ๋ง์ฝ ํ๊น์ด ํ๋ ์ด์์ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๊ณ ์๋ ํด๋์ค๋ผ๋ฉด 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 ๊ฐ์ฒด๋ฅผ ์์ฑํด์ฃผ๋ ๊ณผ์ ์ ๋ค์๊ณผ ๊ฐ๋ค
- ํ๊น์ ์ธํฐํ์ด์ค๋ฅผ ์์ฒด์ ์ธ ๊ฒ์ฆ ๋ก์ง์ ํตํด ProxyFactory์ ์ํด ํ๊น์ ์ธํฐํ์ด์ค๋ฅผ ์์ํ Proxy ๊ฐ์ฒด ์์ฑ
- 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์ ์์ฑํด์ค์ผ ํ๋ค.
๋ฐ๋ผ์ ์ฌ์ฉ์์ ์์ฒญ์ด ์ต์ข ์ ์ผ๋ก ์์ฑ๋ 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 ์์ฑ
๋ค์๊ณผ ๊ฐ์ด 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;
}
- ๋ฉ์๋๊ฐ ์ฒ์ ํธ์ถ๋์์ ๋ ๋์ ์ผ๋ก ํ๊น์ ํด๋์ค์ ๋ฐ์ดํธ ์ฝ๋๋ฅผ ์กฐ์
- ์ดํ ํธ์ถ์์ ์กฐ์๋ ๋ฐ์ดํธ ์ฝ๋๋ฅผ ์ฌ์ฌ์ฉ
CGLib์ ์ฑ๋ฅ์ด ์ข๊ธดํ์ง๋ง, Spring์ JDK Dynamic Proxy๋ฅผ ๊ธฐ๋ฐ์ผ๋ก Proxy๋ฅผ ์์ฑํด์ฃผ๊ณ ์๋ค. ํ์ง๋ง ์ด๋ ์ด๋ ์์ ๋ถํฐ Spring Boot์์ ๋ฌธ์ ๊ฐ ๋์๋ ๋ถ๋ถ๋ค์ด ๊ฐ์ ๋์ด ์์ ํ๊ฐ ๋์๊ธฐ ๋๋ฌธ์ CGLib์ ๋ฐฉ์์ผ๋ก Proxy๋ฅผ ์์ฑํด์ฃผ๊ณ ์๋ค.
# 0308 - ๋ฆฌํ๋ ์ API (Reflection API)
# ๋ฆฌํ๋ ์ API๋?
- ๊ตฌ์ฒด์ ์ธ ํด๋์ค ํ์ ์ ์์ง ๋ชปํด๋ ๊ทธ ํด๋์ค์ ์ ๋ณด(๋ฉ์๋. ํ์ , ๋ณ์ ๋ฑ๋ฑ)์ ์ ๊ทผํ ์ ์๊ฒ ํด์ฃผ๋ ์๋ฐ API์ด๋ค.
- ๋ฆฌํ๋ ์ API๋ ์๋ฐ ์ฝ๋๋ฅผ ํตํด ๋ก๋๋ ํด๋์ค์ ํ๋/๋ฉ์๋/์์ฑ์๋ฅผ ์ฐพ์ ์ ์๊ฒ ์ง์ํ๋ค.
- ๋ํ ํด๋์ค์ ์ ๊ทผ ์ ํ์์ ์๊ด์์ด ํด๋์ค์ ํ๋/๋ฉ์๋/์์ฑ์๋ฅผ ์ฌ์ฉํ ์ ์๊ฒ ์ง์ํ๋ค.
ํ ์์ญ์ ๋ก๋๋ Class ํ์ ์ ๊ฐ์ฒด๋ฅผ ํตํด, ์ํ๋ ํด๋์ค์ ์ธ์คํด์ค๋ฅผ ์์ฑํ ์ ์๊ฒ ์ง์ํ๊ณ , ์ธ์คํด์ค์ ํ๋์ ๋ฉ์๋๋ฅผ ์ ๊ทผ ์ ์ด์์ ์๊ด์์ด ์ฌ์ฉํ ์ ์๋๋ก ์ง์ํ๋ API.
# ํด๋์ค์ ์ ๋ณด ์กฐํ
- ์ง์ ์ ์ธ
Class<Book> bookClass = Book.class;
- ์ธ์คํด์ค๋ฅผ ์ด์ฉํ getClass() ๋ฉ์๋
Book book = new Book();
Class<? extends Book> bookClassFromInstance = book.getClass();
- 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ํด๋์ค ๋ง๋ค๊ธฐ
Tasks -> other -> compileJava ์คํ์ ์ง์ ํด๋ build/์ง์ ๊ฒฝ๋ก์ Qํด๋์ค๊ฐ ์์ฑ
# Config ์ค์
@Configuration
public class QueryDSLConfig {
@PersistenceContext
private EntityManager entityManager;
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}
# ๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ
- 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;
// ...
}
- PostRepository
public interface PostRepository extends JpaRepository<Post, Long> {
}
- 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)๋ฅผ ์์กด์ฑ์ผ๋ก ๋ฐ์์ผํ๋ค.
- CustomizedPostRepository
public interface CustomizedPostRepository {
List<Post> findByTitle(final String title);
}
- 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();
}
}
- 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