1. ๊ฐ์
์์ ์คํ๋ง์ด ๋ง์ ๊ฐ์ฒด๋ค์ ์ฑ๊ธํค์ผ๋ก ๊ด๋ฆฌํ๊ธฐ ์ํด ์คํ๋ง ์ปจํ ์ด๋์ ๋ฑ๋กํ์ฌ ํ์ํ ์์ ์ ์ ๊ณตํ๋ค๊ณ ํ์ตํ๋ค.
ํ์ง๋ง ์ฑ๊ธํค์ผ๋ก ๋น์ ๊ฐ์ง๊ณ ์๋ค๋ ๊ฒ์, ๋ฆฌ์์ค๋ฅผ ์ ์ ํ๊ณ ์๋ค๋ ์๋ฏธ์ด๊ธฐ๋ ํ๋ค. ๊ทธ๋ ๋ค๋ฉด ๋ชจ๋ ๊ฐ์ฒด๋ฅผ ์ฑ๊ธํค์ผ๋ก ์ ์งํด์ผ ํ ํ์๊ฐ ์์๊น?
๊ทธ๋ฆฌ๊ณ ๊ฐ์ฒด๊ฐ ์์ฑ๋๊ณ ์๋ฉธ๋๋ ์๊ฐ์ ํ์ํ ๋์๋ค(์: ์ปค๋ฅ์ ์ฐ๊ฒฐ ๋ฑ)์ ์ด๋ป๊ฒ ๋ค๋ฃจ์ด์ผ ํ ๊น?
๋ง์ฝ ์๋น์ค๋ฅผ ๊ฐ๋ฐํ๋ ๋์ค, ์๋ฒ๋ก ์ค๋ ํน์ API์ ๋ํด UUID์ ์์ฒญ URL์ ๋ก๊น ํ๋ ๊ธฐ๋ฅ์ ๊ฐ๋ฐํ๋ค๊ณ ๊ฐ์ ํ๊ณ ์์ ๋ด์ฉ์ ๊ณต๋ถํด๋ณด๋๋ก ํ์.
2. ๋น ์ค์ฝํ
Hello ๋ฌธ์์ด์ ๋ฐํํ๋ ๊ฐ์์ ์์ฃผ์์ฃผ ์ค์ํ(…!) API ๊ฐ ์๋ค๊ณ ๊ฐ์ ํด๋ณด์. ํด๋น API ์ ์์ฒญ ์ปจํธ๋กค๋ฌ๋ ๋ค์๊ณผ ๊ฐ๋ค.
@RestController
@RequestMapping("/hello")
public class HelloController {
@GetMapping
public ResponseEntity<String> hello() {
//์์ฃผ ์ค์ํ ๋ก์ง
//...
return ResponseEntity.ok("Hello");
}
}
๊ทธ๋ฆฌ๊ณ ์ค์ ๋ก๊น ์ด ์คํ ๋ ๋น์ ์์ฑํ ํ ์คํ๋ง ์ปจํ ์ด๋์ ๋ฑ๋กํ๋ค.
@Component
public class MyLogger {
private String uuid;
private HttpServletRequest request;
public void setRequest(HttpServletRequest request) {
this.request = request;
}
public void setUuid() {
this.uuid = UUID.randomUUID().toString();
}
public void log() {
System.out.printf("[%s] %s\\n", uuid, request.getRequestURL().toString());
}
}
ํด๋น ๋ก๊ฑฐ๋ฅผ ๋ฑ๋กํ ์ธํฐ์ ํฐ๋ฅผ ๊ตฌํํ๋ค.
@Component
public class HelloInterceptor implements HandlerInterceptor {
private final MyLogger myLogger;
public HelloInterceptor(MyLogger myLogger) {
this.myLogger = myLogger;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
myLogger.setRequest(request);
myLogger.setUuid();
myLogger.log();
return true;
}
}
๋ง์ง๋ง์ผ๋ก ํด๋น ์ธํฐ์ ํฐ๋ฅผ ๋ฑ๋กํ๋ค.
@Component
public class HelloConfigurer implements WebMvcConfigurer {
private final HelloInterceptor helloInterceptor;
public HelloConfigurer(HelloInterceptorhelloInterceptor) {
this.helloInterceptor = helloInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistryregistry) {
registry.addInterceptor(helloInterceptor)
.addPathPatterns("/hello");
}
}
์ฌ๊ธฐ๊น์ง ์์ฑํ ํ ์ ํ๋ฆฌ์ผ์ด์
์ ์คํํ๋ฉด ๋ด๊ฐ ์์ฑํ MyLogger
๊ฐ ํ๋ฅญํ๊ฒ ์๋ํ๊ณ ์๋ ๋ฏ ๋ณด์ธ๋ค..!!!
ํ์ง๋ง ์ด ๋ก๊ฑฐ๋ ์ฑ๊ธํค์ผ๋ก ๊ด๋ฆฌ๋๊ณ ์๊ธฐ ๋๋ฌธ์ ์น๋ช ์ ์ธ ๋ฌธ์ ์ ์ด ๋ช๊ฐ์ง ์กด์ฌํ๋ค.
1. ์ฌ์ฉํ์ง ์์๋๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ ์ ์ค์ด๋ค.
2. Thread Safe ํ์ง ์๋ค.
์ ๋ณด๋ฉด ๋ก๊ฑฐ์ request ์ ๋ณด๋ฅผ ์ ๋ฌํ๋ ๊ณผ์ ๊ณผ ์ค์ ๋ก๊ทธ๋ฅผ ์ถ๋ ฅํ๋ ๊ณผ์ ์ฌ์ด์ ์๋ก ๋ค๋ฅธ ์ ์ ๊ฐ ์ ์ญ ๋ณ์(request, uuid) ์ ๊ฐ์ญ์ ์ผ์ผํฌ ์ฌ์ง๊ฐ ์ถฉ๋ถํ๋ค.
๊ทธ๋ ๋ค๋ฉด ํด๋น ๋ก๊ฑฐ๊ฐ ํ๋์ Request ์ ๋ํด ํ๋์ ์ธ์คํด์ค๋ฅผ ์์ฑํ๊ณ , ํด๋น ์์ฒญ์ด ๋๋ ํ ์ฌ๋ผ์ง๋ค๋ฉด ์์ ๋ ๊ฐ์ง ๋ฌธ์ ๊ฐ ๋์์ ํด๊ฒฐ ๋๋ค.
์ด๋ฅผ ์ํ ๊ธฐ๋ฅ์ด ๋ฐ๋ก Bean Scope ์ด๋ค.
Bean Scope ๋ ์คํ๋ง ๋น์ด ์คํ๋ง ์ปจํ ์คํธ ๋ด์์ ์์ฑ๋๊ณ ์ฌ๋ผ์ง๋ ์ผ์ข ์ ์๋ช ์ฃผ๊ธฐ์ธ๋ฐ ๋ค์๊ณผ ์ต์ ๋ค์ด ์๋ค.
์ค์ฝํ
- ์ฑ๊ธํค : ์คํ๋ง ๊ธฐ๋ณธ ์ค์ฝํ. ์คํ๋ง ์ปจํ ์ด๋์ ์์๊ณผ ์ข ๋ฃ๊น์ง ํจ๊ปํ๋ ๊ฐ์ฅ ๋์ ๋ฒ์์ด๋ค.
- ํ๋กํ ํ์ : ๋น์ ์์ฑ๊ณผ ์์กด๊ด๊ณ ์ฃผ์ ๊น์ง๋ง ๊ด์ฌํ๊ณ ๊ทธ ํ์๋ ๋์ด์ ๊ด๋ฆฌํ์ง ์๋๋ค.
- ์น ๊ด๋ จ ์ค์ฝํ๋ค
- request : ์น ์์ฒญ๊ณผ ์๋ช ์ฃผ๊ธฐ๋ฅผ ๊ฐ์ด ํ๋ ์ค์ฝํ์ด๋ค.
- session : ์น ์ธ์ ๊ณผ ์๋ช ์ฃผ๊ธฐ๋ฅผ ๊ฐ์ด ํ๋ ์ค์ฝํ์ด๋ค.
- application : ์น ์๋ธ๋ฆฟ ์ปจํ ์คํธ์ ์๋ช ์ฃผ๊ธฐ๋ฅผ ๊ฐ์ด ํ๋ ์ค์ฝํ์ด๋ค.
๊ทธ๋ ๋ค๋ฉด ์ด์ MyLogger
ํด๋์ค์ request
์ค์ฝํ๋ฅผ ์ง์ ํ์ฌ ์์ฒญ๊ณผ ์๋ช
์ฃผ๊ธฐ๋ฅผ ๋์ผํ๊ฒ ๋ง์ถ์ด ๋ณด์.
์ค์ฝํ๋ ๋ค์๊ณผ ๊ฐ์ด ์ง์ ํ ์ ์๋ค.
@Component
@Scope(value = "request")
public class MyLogger {
// ...
์ค์ฝํ๋ฅผ ์ง์ ํ ํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ธฐ๋ํ๋ฉด ์์๊ณผ ๋ค๋ฅด๊ฒ ์๋ฌ๋ฉ์์ง๊ฐ ์ถ๋ ฅ๋๋ฉด์ ๊ธฐ๋์กฐ์ฐจ ๋์ง ์๋ ๊ฒ์ ๋ณผ ์ ์๋ค. ์ด ๋ ์ถ๋ ฅ๋๋ ์๋ฌ ๋ฉ์์ง๋ ๋ค์๊ณผ ๊ฐ๋ค.
Error creating bean with name 'myLogger': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton;
์๋ฌ ๋ฉ์์ง๊ฐ ๋ช ์์ ์ธ ์์ค์ ๋์ด์ ํด๊ฒฐ ๋ฐฉ๋ฒ๊น์ง ๋ชจ๋ ์ ์ํด์ฃผ๊ณ ์๋ค ๐
์๋ฌ๋ฉ์์ง๋ MyLogger
๋น์ ๋ํ ์์กด์ฑ์ ํ์๋ก ํ๋ HelloIntercepter
๋ฅผ ์ด๊ธฐํ ํ๋ฉด์ ๋ฐ์ํ๋ค.
HelloIntercepter
๊ฐ์ฒด๋ ์ฑ๊ธํค ์ค์ฝํ์ด๊ธฐ ๋๋ฌธ์ ์คํ๋ง ์ปจํ
์ด๋๊ฐ ์์ฑ๋๋ ์์ (์ ํ๋ฆฌ์ผ์ด์
๊ธฐ๋ ์์ ) ์ ์์ฑ๋๊ณ ์์กด์ฑ์ ์ฃผ์
๋ฐ์์ผ ํ๋๋ฐ MyLogger
ํด๋์ค๋ request ์ค์ฝํ์ด๊ธฐ ๋๋ฌธ์ ์์ฒญ์ด ์ฌ๋๊น์ง ์์ฑ์ด ๋ ์ ์๋ค.
์ฆ ๋ ํ๋ ฅ๊ฐ์ฒด์ ์๋ช ์ฃผ๊ธฐ์ ๋ถ์ผ์น๋ก ์ธํด ๋ฐ์ํ๋ ๋ฌธ์ ์ธ๋ฐ ์ด ๋ ํ์ํ ๊ฒ์ด ๋ฐ๋ก ์์์๋ ๋ค๋ฃจ์๋ ํ๋ก์ ๊ฐ์ฒด์ด๋ค.
MyLogger
๊ฐ์ฒด๋ฅผ ์์๋ฐ์ ๊ฐ์ง ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ํ๋ก์ ๊ฐ์ฒด๋ฅผ HelloIntercepter
๊ฐ์ฒด์ ์ฃผ์
ํ๋ฉด ๋๋ค.
๊ทธ๋ฆฌ๊ณ ํด๋น ๊ณผ์ ์ ๋ค์๊ณผ ๊ฐ์ด ์ต์ ํ๋๋ก ๊ฐํธํ๊ฒ ์ง์ ๊ฐ๋ฅํ๋ค.
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyLogger {
//...
๊ทธ๋ฆฌ๊ณ ์ถ๊ฐ์ ์ผ๋ก HelloIntercepter
ํด๋์ค์ ์์ฑ์์๋ MyLogger
์ ํด๋์ค๋ฅผ ์ถ๋ ฅ ํด๋ณด์.
public HelloInterceptor(MyLogger myLogger) {
this.myLogger = myLogger;
System.out.println(myLogger.getClass());
}
๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด ๋ค๋ฅธ ํ๋ก์ ๊ฐ์ฒด๋ค๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก ํ๋ก์ ๊ฐ์ฒด ์์ฑ ๋ผ์ด๋ธ๋ฌ๋ฆฌ CGLIB ๋ฅผ ํตํด ์์ฑ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
์ด๋ฅผ ํตํด ์ฐ๋ฆฌ๊ฐ ์๊ตฌ์ฌํญ๋๋ก ๋ง๋ ์ ๊ท ๋ก๊น ๊ธฐ๋ฅ์ด ์ ๋์ํ๊ฒ ๋์๋ค.
3. ๋น ์๋ช ์ฃผ๊ธฐ ์ฝ๋ฐฑ
์์ ๋ง๋ ๋ก๊น ๊ธฐ๋ฅ์ ํ์ฌ ๋ง์กฑ์ค๋ฝ๊ฒ ์๋ํ๊ณ ์๋ ๋ฏ ๋ณด์ธ๋ค. ํ์ง๋ง ์์ง ๋ง์์ ๋ค์ง ์๋ ๋ถ๋ถ์ด ์๋ค.
๋ฐ๋ก ๊ฐ๋ณ ์์ฒญ์ ๋ํ UUID ๋ฅผ ์์ฑํ๋ ์ฒ๋ฆฌ๋ฅผ ๋ณ๋๋ก ํด์ฃผ์ด์ผ ํ๋ค๋ ์ ์ด๋ค.
๋ก๊ฑฐ ํด๋์ค๊ฐ ๋ชจ๋ Request ์ ๋ํด ์์ฑ๋๊ณ ์ฌ๋ผ์ง๋ค๋ฉด, ํด๋์ค๊ฐ ์์ฑ๋๋ ์์ ์ UUID ๋ฅผ ์ฃผ์ ํ ์ ์์ง ์์๊น?
๊ทธ๋ฆฌ๊ณ ๊ฐ์ฒด๊ฐ ์ปจํ ์ด๋์์ ์ ๊ฑฐ๋๋ ์์ ์ ํน์ ๋์(์: ํ์ผ ๊ธฐ๋ก ๋ฑ)์ ์ถ๊ฐํ๊ณ ์ถ๋ค๋ฉด ์ด๋ป๊ฒ ํด์ผ ํ ๊น?
๋น์ด ์์ฑ๋๊ณ ์ ๊ฑฐ๋ ๋ @PostConstruct
๋ฐ @PreDestroy
์ ๋
ธํ
์ด์
์ ์ฌ์ฉํ ์ ์๋ค.
ํด๋น ์ ๋ ธํ ์ด์ ์ ์ ์ฉํ์ฌ Logger ํด๋์ค๋ฅผ ๊ฐ์ ํ๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyLogger {
private String uuid;
private String log;
private HttpServletRequest request;
public void setRequest(HttpServletRequest request) {
this.request = request;
}
public void log() {
log = String.format("[%s] %s\\n", uuid, request.getRequestURL().toString());
System.out.print(log);
}
@PostConstruct
private void init() {
uuid = UUID.randomUUID().toString();
}
@PreDestroy
private void close() {
try {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("log.txt", true));
bos.write(log.getBytes());
bos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
์์ ์์ ์์ ๋น ์์ฑ์์ (@PostConstruct
์ฌ์ฉ ์์ ) ์ ์ผ์ด๋๋ ๋ก์ง์ ์ฌ์ค ์์ฑ์์์ ๋์ฒด ๋์ด๋ ๋ฌด๊ดํ๋ค.
ํ์ง๋ง ์คํ๋ง์ด ์ฃผ์ ํด์ผ ํ๋ ์์กด์ฑ์ด ํ์ํ๋ค๋ฉด ์์ฑ์์์ ์ฒ๋ฆฌํ ์๊ฐ ์๋ค.
์คํ๋ง์ ๋ค์๊ณผ ๊ฐ์ ์์๋ก ๋น์ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๋ค
- ์คํ๋ง ์ปจํ ์ด๋ ์์ฑ
- ์คํ๋ง ๋น ์์ฑ : ์์ฑ์ ๋์
- ์์กด๊ด๊ณ ์ฃผ์
- ์ด๊ธฐํ ์ฝ๋ฐฑ : PostConstruct ๋์
- ์ฌ์ฉ : ์ ์์ ์์ request ๋ฐ์ ์์
- ์๋ฉธ์ ์ฝ๋ฐฑ : PreDestory ๋์
- ์ข ๋ฃ
๊ทธ๋ฆฌ๊ณ ์ฃผ์ ๋ ์์กด์ฑ์ ํ์ฉํ์ฌ ์ธ๋ถ ์ปค๋ฅ์ ์ฐ๊ฒฐ๊ณผ ๊ฐ์ ๋ฌด๊ฑฐ์ด ๋์์ ์ฒ๋ฆฌ ํ๋ ๊ฒ์ ์์ฑ์์์ ์ฒ๋ฆฌํ๊ธฐ ๋ณด๋ค 4๋ฒ์ ์ด๊ธฐํ ์์ ์ ์คํํ๋ ๊ฒ์ด ์ ์ง๋ณด์ ์ธก๋ฉด์์ ์ ๋ฆฌํ๋ค.
์ด๊ธฐํ์ ์๋ฉธ ์ฝ๋ฐฑ์ ์ด์ฉํ๊ธฐ ์ํด์๋ ์ ์ ๋ ธํ ์ด์ ์ธ์๋ ๋ค์ ๋ฐฉ๋ฒ๋ ํ์ฉ ๊ฐ๋ฅํ๋ค.
- InitializingBean
, DisposableBean
์ธํฐํ์ด์ค ์์ (์คํ๋ง ์ ์ฉ)
- ๋น ๋ฑ๋ก ์ด๊ธฐํ ์๋ฉธ ๋ฉ์๋ ์ง์ @Bean(initMethod = "init", destroyMethod = "close")
์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋น ์ฌ์ฉ์ ํ์ฉ
4. ์ ๋ฆฌ
์์ ์คํ๋ง ์ปจํ ์ด๋๊ฐ ์ง์ํ๋ ํน์ํ ์๋ช ์ฃผ๊ธฐ์ ๋ํด ์์๋ณด์๋ค.
ํ์ง๋ง ๋๋ถ๋ถ์ ๊ฐ์ฒด๋ค์ ์คํ๋ง ์ปจํ ์ด๋์ ๋์ผํ ์๋ช ์ฃผ๊ธฐ๋ฅผ ๊ฐ์ง๋๊ฒ ์ ์ง๋ณด์ ์ธก๋ฉด์์ ์ข๋ค.
๋ช ํํ๊ฒ ์๋ช ์ฃผ๊ธฐ๊ฐ ์ ํด์ ธ์๊ณ , ์์ฃผ ํธ์ถ๋์ง ์์ ๋งํ ๋ก์ง์๋ง ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์ ๋ฏ ํ๋ค.