Spring / Web ์ค์ฝํ
by rlaehddnd0422์น ์ค์ฝํ
์ด์ ํฌ์คํ ์์ ์ค๋ช ํ ์ฑ๊ธํค, ํ๋กํ ํ์ ์ธ์ ํ๊ฐ์ง ์ค์ฝํ๊ฐ ๋ ์์ต๋๋ค. ๋ฐ๋ก ์น ํ๊ฒฝ์์ ์ฌ์ฉ๋๋ ์น ์ค์ฝํ์ ๋๋ค.
์น ์ค์ฝํ ํน์ง
1. ์น ํ๊ฒฝ์์๋ง ๋์
2. ํ๋กํ ํ์ ๊ณผ ๋ค๋ฅด๊ฒ ์คํ๋ง์ด ํด๋น ์ค์ฝํ์ ์ข ๋ฃ์์ ๊น์ง ๊ด๋ฆฌํ๊ธฐ ๋๋ฌธ์ ์ข ๋ฃ ๋ฉ์๋๊ฐ ํธ์ถ๋๋ค.
์น ์ค์ฝํ ์ข ๋ฅ
1. request : HTTP ์์ฒญ์ด ํ๋ ๋ค์ด์ค๊ณ ๋๊ฐ ๋ ๊น์ง ์ ์ง๋๋ ์ค์ฝํ, ๊ฐ HTTP ์์ฒญ๋ง๋ค ๋ณ๋์ ๋น ์ธ์คํด์ค๊ฐ ์์ฑ๋๊ณ ๊ด๋ฆฌ๋๋ค.
2. session : HTTP Session๊ณผ ๋์ผํ ์๋ช ์ฃผ๊ธฐ๋ฅผ ๊ฐ์ง๋ ์ค์ฝํ
3. application : ServletContext์ ๋์ผํ ์๋ช ์ฃผ๊ธฐ๋ฅผ ๊ฐ์ง๋ ์ค์ฝํ
4. websocket : ์น ์์ผ๊ณผ ๋์ผํ ์๋ช ์ฃผ๊ธฐ๋ฅผ ๊ฐ์ง๋ ์ค์ฝํ
์ด ํฌ์คํ ์์๋ request์ ๋ํ ์ค์ฝํ๋ฅผ ์๋ฅผ ๋ค์ด๋ณด๊ฒ ์ต๋๋ค.
request scope๋ HTTP ์์ฒญ์ด ๋ค์ด์ฌ ๋ ๋ง๋ค ๋น ์ธ์คํด์ค๊ฐ ์์ฑ๋๊ณ ๊ด๋ฆฌ๋๋ ์ค์ฝํ์ ๋๋ค.
๋์์ ์ฌ๋ฌ HTTP ์์ฒญ์ด ์ค๋ฉด ์ ํํ ์ด๋ค ์์ฒญ์ด ๋จ๊ธด ๋ก๊ทธ์ธ์ง ๊ตฌ๋ถํ๊ธฐ ์ํ ์์ ๋ฅผ request scope๋ฅผ ์ฌ์ฉํด์ ๋ง๋ค์ด ๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
์๋ฅผ๋ค์ด ํด๋ผ์ด์ธํธ A๊ฐ http ์์ฒญ์ ์๋ํ์ ๋,
[UUID] request scope bean created
[UUID] [requestURL] controller test
[UUID] [requestURL] service id = testId
[UUID] request scope bean closed
์ ๊ฐ์ ๋ก๊ทธ๋ฅผ ๋จ๊ฒจ๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
์ฌ๊ธฐ์ UUID๋ universally unique identifier๋ก ๊ฐ ํด๋ผ์ด์ธํธ๋ฅผ ์๋ณํ ์ ์๋ ๊ณ ์ ์์ด๋๋ก ์ ์ ๊ฐ ๊ฒน์น ์ผ์ด ์๋ค๊ณ ์๊ฐํ์๋ฉด ๋ฉ๋๋ค.
์ฐ์ Controller์ Service์์ Client ์ ์ฉ request scope๋ฅผ ์ ๊ณตํ๊ธฐ ์ํ Logger๋ฅผ ๋ง๋ค์ด์ผ๊ฒ ์ฃ .
package hello.core.common;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.UUID;
@Component
@Scope(value = "request")
public class MyLogger {
private String uuid;
private String requestURL;
public void setRequestURL(String requestURL) {
this.requestURL = requestURL;
}
public void log(String msg)
{
System.out.println("["+uuid+"]"+"["+requestURL+ "] "+ msg );
}
@PostConstruct
public void init()
{
uuid = UUID.randomUUID().toString();
System.out.println("["+uuid+"] request scope bean create : " + this);
}
@PreDestroy
public void close()
{
System.out.println("["+uuid+"] request scope bean close : " + this);
System.out.println();
}
}
- request Scope์ด๊ธฐ ๋๋ฌธ์ @Scope(value="request")๋ก ์ค์ ํด์ฃผ์์ต๋๋ค.
- ์ปจํ ์ด๋์ ๋ฑ๋กํ๊ธฐ ์ํด @Component๋ฅผ ์ค์ ํด์ฃผ์์ต๋๋ค.
- ๊ฐ logger๋ ๊ณ ์ ์ uuid์ requestURL๋ฅผ ๊ฐ์ง๊ธฐ ๋๋ฌธ์ privateํ๊ฒ ์ค์ ํด์ฃผ์์ต๋๋ค.
- logger๊ฐ ์ํํ๋ log ๋ฉ์๋๋ ์์ ์ uuid์ requestURL์ msg๋ฅผ ์ถ๋ ฅํฉ๋๋ค.
- ์์ฑ๋๋ ์์ ์ ์๋์ผ๋ก @PostConstruct ์ด๊ธฐํ ๋ฉ์๋๋ฅผ ์ฌ์ฉํด์ uuid๋ฅผ ์์ฑํด์ ์ ์ฅํด์ฃผ์์ต๋๋ค.
- ์๋ฉธ๋๋ ์์ ์ @ProDestroy๋ฅผ ์ฌ์ฉํด์ ์ข ๋ฃ ๋ฉ์์ง๋ฅผ ๋จ๊ฒผ์ต๋๋ค.
- requestURL์ ์ด ๋น์ด ์์ฑ๋๋ ์์ ์์ ์ ์ ์์ฃ ? ์์ฒญ์ด ์ด๋ URL์์ ๋ค์ด์ค๋์ง๋ ๋์ ์ผ๋ก ์ ํด์ง๋๊น์. ๊ทธ๋์ setter ์ฃผ์ ์ ํ์ต๋๋ค.
์ด์ Controller์ Service๋ฅผ ๋ง๋ค์ด๋ณด๊ฒ ์ต๋๋ค.
์ฐ์ Controller์ ๋๋ค.
package hello.core.web;
import hello.core.common.MyLogger;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
@Controller
@RequiredArgsConstructor
public class LogDemoController {
private final LogDemoService logDemoService;
private final MyLogger logger;
@RequestMapping("log-demo")
@ResponseBody
public String logDemo(HttpServletRequest request)
{
String requestURL = request.getRequestURL().toString();
logger.setRequestURL(requestURL);
logger.log("controller test");
logDemoService.logic("testID");
return "OK";
}
}
Controller๋ ๊ทธ๋ฆผ์ ๋ณด๋ฉด ์ ์ ์๋ฏ์ด Service์ Logger์ ๋ํ ์์กด๊ด๊ณ๋ฅผ ๊ฐ์ง๊ณ ์๊ธฐ ๋๋ฌธ์ ์์กด๊ด๊ณ ์ฃผ์ ์ final์ ๋ํ ์์กด๊ด๊ณ๋ฅผ ์๋์ผ๋ก ์ฃผ์ ํด์ฃผ๋ @RequiredArgsConstructor๋ก ์ค์ ํด์ฃผ์์ต๋๋ค.
@RequestMapping์ ์ฌ์ฉํด์ ์์ฒญ๋ฐ์ url์ ์ค์ ํด์ฃผ์์ต๋๋ค.
@ResponseBody
ํด๋ผ์ด์ธํธ -> ์๋ฒ๋ก ํต์ ํ๋ ๋ฉ์์ง๋ฅผ request ๋ฉ์์ง๋ผ๊ณ ํ๊ณ
์๋ฒ -> ํด๋ผ์ด์ธํธ๋ก ํต์ ํ๋ ๋ฉ์์ง๋ฅผ response ๋ฉ์์ง๋ผ๊ณ ํฉ๋๋ค.
์ฆ, log-demo URL ์์ฒญ์ด ์ค๋ฉด ์๋ฒ์์ ํด๋ผ์ด์ธํธ๋ก logDemo ๋ฉ์๋์ ๋ฆฌํดํํ์ธ String์ ๋ฆฌํดํด์ค๋๋ค.
logDemo method
- HttpServletRequest๋ฅผ ํตํด์ ์์ฒญ URL์ ๋ฐ์ ํ requestURL๊ฐ์ logger์ ์ ์ฅํด๋์์ต๋๋ค.
Service
package hello.core.web;
import hello.core.common.MyLogger;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class LogDemoService {
private final MyLogger logger;
public void logic(String testID) {
logger.log("service id = " + testID);
}
}
์๋น์ค์์๋ ์ค์ง Logger์๊ฒ๋ง ์์กด๊ด๊ณ๋ฅผ ๊ฐ๊ณ ์์ต๋๋ค.
Service ๋ก์ง์์ ์๋ ค์ฃผ๊ธฐ ์ํด @Service ์ด๋ ธํ ์ด์ ์ผ๋ก ๋ฑ๋กํ๊ณ , @Service ์ด๋ ธํ ์ด์ ์ ์ฐ๋ฉด ์ปดํฌ๋ํธ๋ก ์๋ ๋ฑ๋ก์ด ๋ฉ๋๋ค.
์ด์ ๋ชจ๋ ์ค๋น๊ฐ ๋๋ฌ์ต๋๋ค.
์ ํ๋ฆฌ์ผ์ด์ ์ ์คํํด๋ณด๊ฒ ์ต๋๋ค.
์คํํด๋ณด๋ฉด ๊ธฐ๋์ ๋ค๋ฅด๊ฒ ์คํํ๋ฉด ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค.
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;
request๊ฐ ํ์ฑํ ๋์์ง์๋ค.. ํ๋ก์๋ฅผ ๊ณ ๋ คํด๋ผ... ๋ง์ฝ ์ฑ๊ธํค์ผ๋ก๋ถํฐ ์ด์ฉ๊ตฌ์ ์ฉ๊ตฌ..
request์์ฒญ์ด ์ค์ ๋ก ๋ค์ด์์ผ ๋น์ด ์์ฑ๋ ์ ์๋๋ฐ ์ค์ ์์ฒญ์ด ์์ด ์ค๋ฅ๊ฐ ๋๋ ๋ฏ ํฉ๋๋ค.
ํ์ง๋ง ์์์ ๋ฐฐ์ด Provider๋ฅผ ์ฌ์ฉํด์ request๋ฅผ ๋ง๋ค์ด์ฃผ๋ฉด ๋๊ฒ ์ฃ ?
๋ง๋ค์ด๋ณด๊ฒ ์ต๋๋ค.
Controller
package hello.core.web;
import hello.core.common.MyLogger;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
@Controller
@RequiredArgsConstructor
public class LogDemoController {
private final LogDemoService logDemoService;
private final ObjectProvider<MyLogger> myloggerProvider;
@RequestMapping("log-demo")
@ResponseBody
public String logDemo(HttpServletRequest request)
{
String requestURL = request.getRequestURL().toString();
MyLogger logger = myloggerProvider.getObject();
logger.setRequestURL(requestURL);
logger.log("controller test");
logDemoService.logic("testID");
return "OK";
}
}
Service
package hello.core.web;
import hello.core.common.MyLogger;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class LogDemoService {
private final ObjectProvider<MyLogger> myloggerProvider;
public void logic(String testID) {
MyLogger logger = myloggerProvider.getObject();
logger.log("service id = " + testID);
}
}
์ ์ด์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์คํํด๋ณด๋ฉด
๋ก๊ทธ๊ฐ ๋จ๋๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
> ObjectProvider ๋๋ถ์ ObjectProvider.getObject()๋ฅผ ํธ์ถํ๋ ์์ ๊น์ง request scope ๋น์ ์์ฑ์ ์ง์ฐํ ์ ์์ต๋๋ค.
> ๋ ํธ์ถํ๋ ์์ ์๋ HTTP์์ฒญ์ด ์งํ์ค์ด๋ฏ๋ก request scope ๋น ์์ฑ์ด ์ ์ ์ฒ๋ฆฌ๋ฉ๋๋ค.
> ObjectProvider.getObject()๋ฅผ LogDemoController, LogDemoService์์ ๊ฐ๊ฐ ํ๋ฒ์ฉ ๋ฐ๋ก ํธ์ถํด๋ ๊ฐ์ HTTP์์ฒญ์ด๋ฉด ๊ฐ์ ์คํ๋ง ๋น์ด ๋ฐํ๋ฉ๋๋ค.
์ด ๋ฐฉ๋ฒ ์ธ์๋ ํ๋ก์ ๋ฐฉ๋ฒ์ด ์กด์ฌํ๋๋ฐ ํ๋ก์ ๋ฐฉ์์ ์ฌ์ฉํด๋ณด๊ฒ ์ต๋๋ค.
ํ๋ก์ ๋ฐฉ์
์ฝ๋๋ฅผ ๋จผ์ ์ดํด๋ณด๋ฉด
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyLogger {
MyLogger ํด๋์ค์ Scope ์ด๋ ธํ ์ด์ ์ ํ๋ผ๋ฏธํฐ๋ก proxyMode๋ฅผ ์ถ๊ฐํด์ฃผ๋ฉด ๋ฉ๋๋ค.
์ ์ฉ ๋์์ด ์ธํฐํ์ด์ค๊ฐ ์๋ ํด๋์ค๋ฉด TARGET_CLASS
์ ์ฉ ๋์์ด ์ธํฐํ์ด์ค๋ฉด INTERFACES๋ฅผ ์ ํํฉ๋๋ค.
์ด๋ ๊ฒ ํ๋ฉด MyLogger์ ๊ฐ์ง ํ๋ก์ ํด๋์ค๋ฅผ ๋ง๋ค์ด๋๊ณ HTTP request์ ์๊ด์์ด ๊ฐ์ง ํ๋ก์ ํด๋์ค๋ฅผ ๋ค๋ฅธ ๋น์ ๋ฏธ๋ฆฌ ์ฃผ์ ํด ๋ ์ ์์ต๋๋ค.
+ Service, Controller ๋จ๊ณ์ ObjectProvider๋ ์๋ MyLogger๋ก ๋ฐ๊ฟ์ฃผ์ด์ผ ํฉ๋๋ค.
์คํํด๋ณด๋ฉด ์ ๋์ํ๋ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค.
ํ๋ก์ ๋์ ์๋ฆฌ
@Scope์ proxyMode = ScopedProxyMode.TARGET_CLASS ๋ฅผ ์ค์ ํ๋ฉด ์คํ๋ง ์ปจํ ์ด๋๋ CGLIB๋ผ๋ ๋ฐ์ดํธ์ฝ๋๋ฅผ ์กฐ์ํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด์ MyLogger๋ฅผ ์์๋ฐ์ ๊ฐ์ง ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์์ฑํฉ๋๋ค.
Mylogger.getClass()๋ฅผ ์ถ๋ ฅํด๋ณด๋ฉด ์์ MyLogger ํด๋์ค๊ฐ ์๋๋ผ EnhancerBySpringCGLIB๋ผ๋ ํด๋์ค๋ก ๋ง๋ค์ด์ง ๊ฐ์ฒด๊ฐ ๋์ ๋ฑ๋ก๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
โถ๏ธ ์ด๋ ๊ฒ ๊ฐ์ง ํ๋ก์ ๊ฐ์ฒด๊ฐ ์คํ๋ง ์ปจํ ์ด๋์ ๋ฑ๋ก๋๊ณ , ์์กด๊ด๊ณ ์ฃผ์ ๋ ์ด ๊ฐ์ง ํ๋ก์ ๊ฐ์ฒด๊ฐ ์ฃผ์ ๋ฉ๋๋ค.
ํ์ง๋ง ๊ฐ์ง ํ๋ก์ ๊ฐ์ฒด๋ request๊ฐ ์ค๋ฉด ๊ทธ ๋ ๋ด๋ถ์์ ์ง์ง ๋น์ ์์ฒญํ๋ ์์ ๋ก์ง์ด ๋ค์ด์์ต๋๋ค!
๊ฐ์ง ํ๋ก์ ๊ฐ์ฒด๋ ๋ด๋ถ์ ์ง์ง logger๋ฅผ ์ฐพ๋ ๋ฐฉ๋ฒ์ ์๊ณ ์์ด์
ํด๋ผ์ด์ธํธ๊ฐ logger.logic()์ ํธ์ถํ๋ฉด ์ฌ์ค ํ๋ฉด์์ผ๋ก๋ ๊ฐ์ง ํ๋ก์ ๊ฐ์ฒด์ ๋ฉ์๋๋ฅผ ํธ์ถํ ๊ฒ์ด์ง๋ง,
๊ฐ์ง ํ๋ก์ ๊ฐ์ฒด๊ฐ request scope์ ์ง์ง logger.logic()์ ํธ์ถํฉ๋๋ค.
+ ๊ฐ์ง ํ๋ก์ ๊ฐ์ฒด๋ ์๋ณธ ํด๋์ค๋ฅผ ์์ ๋ฐ์์ ๋ง๋ค์ด์ก๊ธฐ ๋๋ฌธ์ ์ด ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ ํด๋ผ์ด์ธํธ ์ ์ฅ์์๋ ์ฌ์ค ์๋ณธ์ธ์ง ์๋์ง๋ ๋ชจ๋ฅด๊ฒ, ๋์ผํ๊ฒ ์ฌ์ฉํ ์ ์๋ค(๋คํ์ฑ)
๋์ ์ ๋ฆฌ
- CGLIB๋ผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ๋ด ํด๋์ค๋ฅผ ์์ ๋ฐ์ ๊ฐ์ง ํ๋ก์ ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด ์ฃผ์ ํ๋ค.
- ์ค์ ์์ฒญ์ด ์ค๋ฉด ์ค์ ๋น์ ์์ฒญํ๋ ์์ ๋ก์ง์ด ๋ค์ด์๋ค.
- ๊ฐ์ง ํ๋ก์ ๊ฐ์ฒด๋ ์ค์ request scope์ ๊ด๊ณ๊ฐ ์๊ณ , ๊ทธ๋ฅ ๊ฐ์ง์. ๋ด๋ถ์ ๋จ์ ์์ ๋ก์ง๋ง ์๊ณ ์ฑ๊ธํค์ฒ๋ผ ๋์.
ํน์ง ์ ๋ฆฌ
- ํ๋ก์ ๊ฐ์ฒด ๋๋ถ์ ํด๋ผ์ด์ธํธ๋ ๋ง์น ์ฑ๊ธํค ๋น์ ์ฌ์ฉํ๋ฏ ํธํ๊ฒ request scope๋ฅผ ์ฌ์ฉํ ์ ์์.
- Provider๋ฅผ ์ฌ์ฉํ๋, ํ๋ก์๋ฅผ ์ฌ์ฉํ๋ ํต์ฌ ์์ด๋์ด๋ ์ง์ง ๊ฐ์ฒด ์กฐํ๋ฅผ ๊ผญ ํ์ํ ์์ ๊น์ง ์ง์ฐ์ฒ๋ฆฌ ํ๋ค๋ ์ !
- ์ด๋ ธํ ์ด์ ์ค์ ๋ณ๊ฒฝ ๋ง์ผ๋ก ์๋ณธ ๊ฐ์ฒด๋ฅผ ํ๋ก์ ๊ฐ์ฒด๋ก ๋์ฒด ๊ฐ๋ฅ -> ๋คํ์ฑ๊ณผ DI ์ปจํ ์ด๋๊ฐ ๊ฐ์ง๋ ํฐ ์ฅ์ .
- ์น ์ค์ฝํ๊ฐ ์๋์ฌ๋ ํ๋ก์๋ ์ฌ์ฉ๊ฐ๋ฅ.
์ฃผ์์
- ์ฑ๊ธํค์ ์ฌ์ฉํ๋ ๊ฒ ๊ฐ์ง๋ง, ๋ค๋ฅด๊ฒ ๋์ํ๊ธฐ ๋๋ฌธ์ ๊ฒฐ๊ตญ ์ฃผ์ํด์ ์ฌ์ฉํ์.
- ์ด๋ฐ ํน๋ณํ scope๋ ๊ผญ ํ์ํ ๊ณณ์๋ง ์ต์ํํด์ ์ฌ์ฉํ์. ๋ฌด๋ถ๋ณํ๊ฒ ์ฌ์ฉํ๋ฉด ์ ์ง๋ณด์๊ฐ ์ด๋ ต๋ค.
<์ฐธ๊ณ ์๋ฃ>
https://mungto.tistory.com/436
https://cheershennah.tistory.com/179
'๐ Backend' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Spring Boot CRUD API ๊ตฌํ (1) | 2023.05.24 |
---|---|
Spring / ์ง๊ธ๊น์ง ์ฌ์ฉํ ์ด๋ ธํ ์ด์ ์ ๋ฆฌ @ (0) | 2023.02.20 |
Spring / Bean Scope (0) | 2023.02.11 |
Spring / ์คํ๋ง ๋น Life-cycle callback (0) | 2023.02.10 |
Spring / List,Map์ ์ด์ฉํ ๋์ผํ์ ๋น ์ฌ์ฉ, ์๋๊ณผ ์๋์ ์ฌ๋ฐ๋ฅธ ์ค๋ฌด ์ด์ ๊ธฐ์ค (0) | 2023.02.08 |
๋ธ๋ก๊ทธ์ ์ ๋ณด
Study Repository
rlaehddnd0422