0227 ~ 0305
# 0227 ~ 0305
# 0227 - Filter๋ฅผ ํตํ Logging
- LoggingFilter
@Component
public class LoggingFilter extends OncePerRequestFilter {
protected static final Logger log = LoggerFactory.getLogger(LoggingFilter.class);
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
MDC.put("traceId", UUID.randomUUID().toString());
StopWatch stopWatch = new StopWatch();
stopWatch.start();
if (isAsyncDispatch(request)) {
filterChain.doFilter(request, response);
} else {
doFilterWrapped(new RequestWrapper(request), new ResponseWrapper(response), filterChain);
}
stopWatch.stop();
log.info("Response Time : {}(ms)", stopWatch.getTotalTimeMillis());
MDC.clear();
}
protected void doFilterWrapped(RequestWrapper request, ContentCachingResponseWrapper response, FilterChain filterChain) throws ServletException, IOException {
try {
logRequest(request);
filterChain.doFilter(request, response);
} finally {
logResponse(response);
response.copyBodyToResponse();
}
}
private static void logRequest(RequestWrapper request) throws IOException {
String queryString = request.getQueryString();
log.info("Request : {} uri=[{}] content-type=[{}]",
request.getMethod(),
queryString == null ? request.getRequestURI() : request.getRequestURI() + queryString,
request.getContentType()
);
logPayload("Request", request.getContentType(), request.getInputStream());
}
private static void logResponse(ContentCachingResponseWrapper response) throws IOException {
logPayload("Response", response.getContentType(), response.getContentInputStream());
}
private static void logPayload(String prefix, String contentType, InputStream inputStream) throws IOException {
boolean visible = isVisible(MediaType.valueOf(contentType == null ? "application/json" : contentType));
if (visible) {
byte[] content = StreamUtils.copyToByteArray(inputStream);
if (content.length > 0) {
String contentString = new String(content);
log.info("{} Payload: {}", prefix, contentString);
}
} else {
log.info("{} Payload: Binary Content", prefix);
}
}
private static boolean isVisible(MediaType mediaType) {
final List<MediaType> VISIBLE_TYPES = Arrays.asList(
MediaType.valueOf("text/*"),
MediaType.APPLICATION_FORM_URLENCODED,
MediaType.APPLICATION_JSON,
MediaType.APPLICATION_XML,
MediaType.valueOf("application/*+json"),
MediaType.valueOf("application/*+xml"),
MediaType.MULTIPART_FORM_DATA
);
return VISIBLE_TYPES.stream()
.anyMatch(visibleType -> visibleType.includes(mediaType));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
- RequestWrapper
public class RequestWrapper extends HttpServletRequestWrapper {
private final byte[] cachedInputStream;
public RequestWrapper(HttpServletRequest request) throws IOException {
super(request);
InputStream requestInputStream = request.getInputStream();
this.cachedInputStream = StreamUtils.copyToByteArray(requestInputStream);
}
@Override
public ServletInputStream getInputStream() {
return new ServletInputStream() {
private final InputStream cachedBodyInputStream = new ByteArrayInputStream(cachedInputStream);
@Override
public boolean isFinished() {
try {
return cachedBodyInputStream.available() == 0;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
throw new UnsupportedOperationException();
}
@Override
public int read() throws IOException {
return cachedBodyInputStream.read();
}
};
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
- ResponseWrapper
public class ResponseWrapper extends ContentCachingResponseWrapper {
public ResponseWrapper(HttpServletResponse response) {
super(response);
}
}
2
3
4
5
# [ Filter์์ ์์ฒญ๋ HTTP ์ ๋ณด์ ์ ๊ทผํ๊ธฐ ]
ContentCachingRequestWrapper
ContentCachingRequestWrapper๋ฅผ ์์๋ฐ์ Logging Filter๋ฅผ ๋ง๋ ๋ค๋ฉด HTTP Request๋ฅผ ์ฌ๋ฌ ๋ฒ ์ฝ์ด๋ค์ผ ์ ์๋ค. ํ์ง๋ง Request PayLoad๋ฅผ ์ฝ์ง ๋ชปํ๊ณ , content-type์ด 'x-www-form-urlencoded'๋ง ์ง์ํ๋ค.HttpServletRequestWrapper
HttpServletRequestWrapper๋ฅผ ์์๋ฐ์์ HTTP Request์ ์์ฒญ๊ณผ Payload๋ฅผ ์ถ๋ ฅ
# 0302 - ํ ํ๋ฆฟ ๋ฉ์๋ ํจํด (Template Method Pattern)
์๊ณ ๋ฆฌ์ฆ์ ์ผ๋ถ ๋จ๊ณ๋ฅผ ์๋ธํด๋์ค์์ ์ ์ํ๋ค.
# ์๋
# [ ์๋ ]
- GoF๋ ๋ค์๊ณผ ๊ฐ์ด ์ดํจํด์ ์๋๋ฅผ ์ค๋ช ํ๋ค.
๊ฐ์ฒด์ ์ฐ์ฐ์๋ ์๊ณ ๋ฆฌ์ฆ์ ๋ผ๋๋ง์ ์ ์ํ๊ณ ๊ฐ ๋จ๊ณ์์ ์ํํ ๊ตฌ์ฒด์ ์ฒ๋ฆฌ๋ ์๋ธํด๋์ค ์ชฝ์ผ๋ก ๋ฏธ๋ฃน๋๋ค. ์๊ณ ๋ฆฌ์ฆ์ ๊ตฌ์กฐ ์์ฒด๋ ๊ทธ๋๋ก ๋๋ ์ฑ ์๊ณ ๋ฆฌ์ฆ ๊ฐ ๋จ๊ณ ์ฒ๋ฆฌ๋ฅผ ์๋ธํด๋์ค์์ ์ฌ์ ์ํ ์ ์๊ฒํฉ๋๋ค.
- ์ค์ฉ์ฃผ์ ๋์์ธ ํจํด์์๋ ์ด ํจํด์ด ์์ ๊ธฐ๋ฐ์ ํ๋ ์์ํฌ์์ ์ฌ์ฉ๋๋ค๊ณ ์ค๋ช ํ๋ค.
Template Method๋ ๋ณดํต ์์ ๊ธฐ๋ฐ์ ํ๋ ์์ํฌ์์ ์ฌ์ฉ๋๋ค. ํ๋ ์์ํฌ๋ ์์ ์ 90% ์ ๋๋ฅผ ๊ธฐ๋ฐ ํด๋์ค๋ฅผ ํตํด ์ ๊ณตํ๋ฉฐ, ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฉ๋์ ๋ง๊ฒ ๋ง์ถคํํ ํ์๊ฐ ์๋ ๋ถ๋ถ์ ์ถ์ ๋ฉ์๋๋ก ๋จ๊ฒจ๋๋๋ค. ์ด ๋ง์ ๊ณง ๊ธฐ๋ฐ ํด๋์ค๊ฐ ์ถ์ ํ ํ๋ฆฟ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค๋ ์๋ฏธ์ด๋ค. ์ฌ์ฉ์๋ ํด๋์ค๋ฅผ ์์ํ๊ณ ์ถ์ ๋ฉ์๋๋ฅผ ํ์์ ๋ง๊ฒ ๊ตฌํํจ์ผ๋ก์จ ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋ค.
- ํจํด์ ํ์ฉํ ๋ฆฌํฉํฐ๋ง์์๋ ๋ค์๊ณผ ๊ฐ์ด ์ค๋ช ํ๋ค.
ํ ํ๋ฆฟ ๋ฉ์๋๋ '์๊ณ ๋ฆฌ์ฆ์์ ๋ถ๋ณ์ ์ธ ๋ถ๋ถ์ ํ ๋ฒ๋ง ๊ตฌํํ๊ณ ๊ฐ๋ณ์ ์ธ ๋์์ ์๋ธํด๋์ค์์ ๊ตฌํํ ์ ์๋๋ก ๋จ๊ฒจ๋ ๊ฒ'์ ๋งํ๋ค. ์๋ธํด๋์ค์ ๋ถ๋ณ์ ์ธ ๋ถ๋ถ๊ณผ ๊ฐ๋ณ์ ์ธ ๋ถ๋ถ์ด ๋ค์์ฌ ์๋ค๋ฉด, ๋ถ๋ณ์ ์ธ ๋ถ๋ถ์ด ์ฌ๋ฌ ์๋ธํด๋์ค์์ ์ค๋ณต๋ ๊ฒ์ด๋ค. ์ด๋ฐ ์ฝ๋๋ฅผ Template Method ํจํด์ผ๋ก ๋ฆฌํฉํฐ๋งํ๋ฉด, ๋ถ๋ณ์ ์ธ ๋ถ๋ถ์ ๋ํ ๊ตฌํ์ ํ ๊ณณ์๋ง, ์ฆ ์ํผํด๋์ค ๋ฉ์๋ ๋ด์ ์ผ๋ฐํ๋ ์๊ณ ๋ฆฌ์ฆ์๋ง ์กด์ฌํ๊ฒ ๋๋ฏ๋ก ์ฝ๋ ์ค๋ณต์ด ์ฌ๋ผ์ง๋ค.
ํ ํ๋ฆฟ ๋ฉ์๋์ ๋ถ๋ณ์ ๋์์ ๋ค์์ ํฌํจํ๋ค.
- ์๊ณ ๋ฆฌ์ฆ์ ๊ตฌ์ฑํ๋ ๋ฉ์๋ ๋ชฉ๋ก๊ณผ ๊ทธ ํธ์ถ ์์
- ์๋ธํด๋์ค๊ฐ ๊ผญ ์ค๋ฒ๋ผ์ด๋ํด์ผ ํ ์ถ์ ๋ฉ์๋
- ์๋ธํด๋์ค๊ฐ ์ค๋ฒ๋ผ์ด๋ํด๋ ๋๋ ํ ๋ฉ์๋ hook method, ์ฆ ๊ตฌ์ฒด ๋ฉ์๋
# [ ์ฉ์ด ]
- ํ
ํ๋ฆฟ ๋ฉ์๋
- ํ์ ์ฒ๋ฆฌ ์ ์ฐจ๋ฅผ ์ ์ํ ๋ฉ์๋
- ์๋ธํด๋์ค๊ฐ ์ค๋ฒ๋ผ์ด๋ํ๋ ์ถ์ ๋ฉ์๋๋ค์ ์ฌ์ฉํ์ฌ ์๊ณ ๋ฆฌ์ฆ์ ์ ์ํ๋ ๋ฉ์๋
- ํ
์ฐ์ฐ(hook operation)
- ํ์ํ๋ค๋ฉด ์๋ธํด๋์ค์์ ํ์ฅํ ์ ์๋ ๊ธฐ๋ณธ์ ์ธ ํ๋์ ์ ๊ณตํ๋ ์ฐ์ฐ(๋ฉ์๋)
- ๊ธฐ๋ณธ์ ์ผ๋ก๋ ์๋ฌด ๋ด์ฉ๋ ์ ์ํ์ง ์๋๋ค
# [ ๊ตฌํ ํ ]
- ํ
ํ๋ฆฟ ๋ฉ์๋๊ฐ ํธ์ถํ๋ ๋ฉ์๋๋ค์ ํ
ํ๋ฆฟ ๋ฉ์๋๋ง ํธใ
ในํ ์ ์๊ฒ ํ๋ ๊ฒ์ ๊ณ ๋ คํ๋ค.
protected
์ ๊ทผ ์ ํ์ ์ฌ์ฉํ๋ฉด ๋๋ค
- ํ
ํ๋ฆฟ ๋ฉ์๋๋ ์ค๋ฒ๋ผ์ด๋ํ ์ ์๋๋ก ๊ตฌํํ๋ ๊ฒ์ ๊ณ ๋ คํ๋ค.
- Java๋ผ๋ฉด ํ ํ๋ฆฟ ๋ฉ์๋์ final์ ๋ฌ์์ฃผ๋ฉด ๋๋ค.
- ๊ตฌํํด์ผ ํ๋ abstract ๋ฉ์๋์ ์๊ฐ ๋๋ฌด ๋ง์์ง๋ ์๋๋ก ์ฃผ์ํ๋ค.
- ์ฌ์ ์ํ abstract ๋ฉ์๋๋ ์๋ณํ๊ธฐ ์ฝ๋๋ก ์ ๋์ฌ๋ฅผ ๋ถ์ฌ์ฃผ์
- ์๋ฅผ ๋ค์ด ๋ฉ์๋ ์ด๋ฆ์ด
Do
๋ก ์์ํ๋๋ก ํ๋ค.
- ์๋ฅผ ๋ค์ด ๋ฉ์๋ ์ด๋ฆ์ด
# ํค๋ ํผ์คํธ ๋์์ธ ํจํด์ ์์
public class Coffee {
// ์ปคํผ ๋ง๋๋ ๋ฐฉ๋ฒ
void prepareRecipe() {
boilWater();
brewCoffeeGrinds();
pourInCup();
addSugarAndMilk();
}
public void boilWater() {
System.out.println("๋ฌผ ๋์ด๋ ์ค");
}
public void brewCoffeeGrinds() {
System.out.println("ํํฐ๋ฅผ ํตํด์ ์ปคํผ๋ฅผ ์ฐ๋ ค๋ด๋ ์ค");
}
public void pourInCup() {
System.out.println("์ปต์ ๋ฐ๋ฅด๋ ์ค");
}
public void addSugarAndMilk() {
System.out.println("์คํ๊ณผ ์ฐ์ ๋ฅผ ์ถ๊ฐํ๋ ์ค");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Tea {
// ํ์ฐจ ๋ง๋๋ ๋ฐฉ๋ฒ
void prepareRecipe() {
boilWater();
steepTeaBag();
pourInCup();
addLemon();
}
public void boilWater() {
System.out.println("๋ฌผ ๋์ด๋ ์ค");
}
public void steepTeaBag() {
System.out.println("์ฐจ๋ฅผ ์ฐ๋ ค๋ด๋ ์ค");
}
public void pourInCup() {
System.out.println("์ปต์ ๋ฐ๋ฅด๋ ์ค");
}
public void addLemon() {
System.out.println("๋ ๋ชฌ์ ์ถ๊ฐํ๋ ์ค");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
ํ ํ๋ฆฟ ๋ฉ์๋ ํจํด์ ์์ ๋ ํด๋์ค๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ๋ฆฌํฉํ ๋งํ๋ค.
- ๊ณตํต์ ์ธ ๋ถ๋ถ์ ๋ฝ์ ์ถ์ ํด๋์ค๋ฅผ ๋ง๋ ๋ค.
- ์๊ณ ๋ฆฌ์ฆ์ ์ธ๋ถ ํญ๋ชฉ์์ ์ฐจ์ด๊ฐ ์๋ ๊ณณ์ ์ถ์ ๋ฉ์๋๋ก ์ ์ํ๋ค.
public abstract class CaffeineBeverage {
// ์๊ณ ๋ฆฌ์ฆ์ ๊ฐ๊ณ ์๋ ์ด ๋ฉ์๋๋ฅผ 'ํ
ํ๋ฆฟ ๋ฉ์๋'๋ผ ๋ถ๋ฅธ๋ค
final void prepareRecipe() {
boilWater();
brew();
pourInCup();
addCondiments();
}
abstract void brew(); // ์๋ธํด๋์ค์์ ๊ตฌํ
abstract void addCondiments(); // ์๋ธํด๋์ค์์ ๊ตฌํ
void boilWater() {
System.out.println("๋ฌผ ๋์ด๋ ์ค");
}
void pourInCup() {
System.out.println("์ปต์ ๋ฐ๋ฅด๋ ์ค");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Coffee extends CaffeineBeverage {
public void brew() {
System.out.println("ํํฐ๋ก ์ปคํผ๋ฅผ ์ฐ๋ ค๋ด๋ ์ค");
}
public void addCondiments() {
System.out.println("์คํ๊ณผ ์ปคํผ๋ฅผ ์ถ๊ฐํ๋ ์ค");
}
}
2
3
4
5
6
7
8
public class Tea extends CaffeineBeverage {
public void brew() {
System.out.println("์ฐจ๋ฅผ ์ฐ๋ ค๋ด๋ ์ค");
}
public void addCondiments() {
System.out.println("๋ ๋ชฌ์ ์ถ๊ฐํ๋ ์ค");
}
}
2
3
4
5
6
7
8
9
# [ hook ๋ฉ์๋ ]
- ์๋ธํด๋์ค ๊ตฌํ์ ์ตํต์ฑ์ ๋ฐํํ๊ธฐ ์ํ ๋ฉ์๋
- ์ถ์ ํด๋์ค์์ ์ ์ธํ์ง๋ง ๊ธฐ๋ณธ์ ์ธ ๋ด์ฉ๋ง ๊ตฌํ๋์ด ์๊ฑฐ๋ ๋ด์ฉ์ด ๋น์ด ์๋ ๋ฉ์๋
public abstract class CaffeineBeverage {
// ์๊ณ ๋ฆฌ์ฆ์ ๊ฐ๊ณ ์๋ ์ด ๋ฉ์๋๋ฅผ 'ํ
ํ๋ฆฟ ๋ฉ์๋'๋ผ ๋ถ๋ฅธ๋ค
final void prepareRecipe() {
boilWater();
brew();
pourInCup();
// ๊ณ ๊ฐ์ด ์ํ๋ ๊ฒฝ์ฐ์๋ง ์ฒจ๊ฐ๋ฌผ์ ๋ฃ๋๋ค
if (customerWantsCondiments()) {
addCondiments();
}
}
abstract void brew(); // ์๋ธํด๋์ค์์ ๊ตฌํ
abstract void addCondiments(); // ์๋ธํด๋์ค์์ ๊ตฌํ
void boilWater() {
System.out.println("๋ฌผ ๋์ด๋ ์ค");
}
void pourInCup() {
System.out.println("์ปต์ ๋ฐ๋ฅด๋ ์ค");
}
// ์ด ๋ฉ์๋๊ฐ hook ๋ฉ์๋
boolean customerWantsCondiments() {
return true;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# ๋๋ค๋ฅผ ์ฌ์ฉํด ์๋ธ ํด๋์ค ์ ๊ฑฐํ๊ธฐ
1๊ฐ์ ์ถ์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ ํ ํ๋ฆฟ ๋ฉ์๋๊ฐ ์๋ค.
abstract class OnlineBanking {
// template method
public void processCustomer(int id) {
Customer c = Database.getCustomerWithId(id);
makeCustomerHappy(c);
}
abstract void makeCustomerHappy(Customer c);
}
2
3
4
5
6
7
8
์ด ์ฝ๋๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด ์๋ธ ํด๋์ค๋ฅผ ์์ฑํด์ผ ํ๋ค.
class OnlineBankingKorea extends OnlineBanking {
@Override
void makeCustomerHappy(Customer c) {
System.out.println("์๋
ํ์ธ์ " + c.getName());
}
}
2
3
4
5
6
๋ค์๊ณผ ๊ฐ์ด ์ฌ์ฉํ ์ ์๋ค.
new OnlineBankingKorea().processCustomer(1337);
๊ตฌํํด์ผ ํ ์ถ์ ๋ฉ์๋๊ฐ ํ๋ ๋ฟ์ด๋ฏ๋ก ๋๋ค์ ์ฌ์ฉ์ ๊ณ ๋ คํด๋ณผ ์ ์๋ค.
OnlineBacking์์ abstract ํค์๋๋ฅผ ์ญ์ ํ๊ณ , processCustomer ๋ฉ์๋๊ฐ Consumer๋ฅผ ๋ฐ๋๋ก ์์ ํ๋ค.
class OnlineBanking {
public void processCustomer(int id, Consumer<Customer> makeCustomerHappy) {
Customer c = Database.getCustomerWithId(id);
makeCustomerHappy.accept(c);
}
}
2
3
4
5
6
์ด์ ์์ ์์ด OnlineBanking ํด๋์ค๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
new OnlineBanking()
.processCustomer(1337,
(Customer c) -> System.out.println("์๋
ํ์ธ์" + c.getName())
);
2
3
4
# ๊ณ ๋ คํ ์ ๋ค
- From: ์ค์ฉ์ฃผ์ ๋์์ธ ํจํด
Template Method ํจํด์ ๊ฐ๋ฅํ ์ ์ ํด ์ฌ์ฉํด์ผ ํ๋ค. ํด๋์ค ์์ฒด๊ฐ ์ ์ ์ผ๋ก ํ์ ํด๋์ค์ ์ปค์คํฐ๋ง์ด์ง์ ์์กดํ๋ ์ผ์ข ์ 'ํ๋ ์์ํฌ๊ฐ' ๋๋ฉด ์ด ์ญ์ ๋งค์ฐ ๋ถ์์ง๊ธฐ ์ฝ๊ธฐ ๋๋ฌธ์ด๋ค. ๊ธฐ๋ฐ ํด๋์ค๋ ๋งค์ฐ ๊นจ์ง๊ธฐ ์ฝ๋ค. ๋๋ MFC์์ ํ๋ก๊ทธ๋๋ฐ์ ํ ๋, ๋ง์ดํฌ๋ก์ํํธ๊ฐ ์๋ก์ด ๋ฒ์ ์ ์ผใน๋ฆฌ์ฆํ ๋๋ง๋ค ์ ์ฒด ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฌ์์ฑํด์ผ๋ง ํ๋ ์ ๋ชฝ์ ๋จ์ณ๋ฒ๋ฆด ์๊ฐ ์๋ค. ์ข ์ข ์ฝ๋๋ ์ ์ปดํ์ผ๋์ง๋ง, ๋ช๋ช ๊ธฐ๋ฐ ํด๋์ค์ ๋ฉ์๋๊ฐ ๋ณ๊ฒฝ๋์ด ํ๋ก๊ทธ๋จ์ด ์ ๋๋ก ์คํ๋์ง ์์๋ ๊ฒ์ด๋ค.
Template Method ํจํด์ ๋ํ '์ด๋์'๊ณผ 'ํจํด'์ฌ์ด๊ฐ ์ผ๋ง๋ ๊ฐ๊น์ธ ์ ์๋์ง๋ฅผ ๋ณด์ฌ์ฃผ๋ ์์ด๊ธฐ๋ ํ๋ค. Template Method ํจํด์ ๋คํ์ฑ์ ์กฐ๊ธ ์์ฉํ์ ๋ฟ ํจํด์ด๋ ์๊ด์ ํ์ดํ์ ์ฐ๊ธฐ์ ๋ถ์กฑํ๋ค๊ณ ์ฃผ์ฅํ ์๋ ์๋ ๊ฒ์ด๋ค.
- From: ํจํด์ ํ์ฉํ ๋ฆฌํฉํฐ๋ง
Template Method ํจํด์ ๊ตฌํํ ๋์ ์ค๋ฌด์ ์ธ ์ฃผ์์ฌํญ์ด ํ๋ ์๋๋ฐ, ์๋ธํด๋์ค์์ ์ค๋ฒ๋ผ์ด๋ํด์ผํ๋ ๋ฉ์๋๊ฐ ๋๋ฌด ๋ง์ผ๋ฉด ๊ณค๋ํ๋ค๋ ๊ฒ์ด๋ค. ์๋ธํด๋์ค๋ฅผ ๊ตฌํํ๊ธฐ๊ฐ ์ด๋ ค์์ง๊ธฐ ๋๋ฌธ์ด๋ค. ๊ทธ๋์ [Design Patterns]์์๋ ์๋ธํด๋์ค์์ ์ค๋ฒ๋ผ์ด๋ํด์ผ ํ๋ ์ถ์ ๋ฉ์๋์ ๊ฐ์๋ฅผ ์ต์ํํด์ผ ํ๋ค๊ณ ์ง์ ํ๋ค. ๊ทธ๋ฌ์ง ์์ผ๋ฉด ํ ํ๋ฆฟ ๋ฉ์๋์ ๋ด์ฉ์ ์์ธํ ์ดํด๋ณด์ง ์๊ณ ๋ ํ๋ก๊ทธ๋๋จธ๊ฐ ์ด๋ค ๋ฉ์๋๋ฅผ ์ค๋ฒ๋ผ์ด๋ํด์ผ ํ ์ง ์ฝ๊ฒ ์ ์ ์์ ๊ฒ์ด๋ค.
Java ๊ฐ์ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์์๋ ํ ํ๋ฆฟ ๋ฉ์๋๋ฅผ final๋ก ์ ์ธํด ์๋ธํด๋์ค๊ฐ ์ค์๋ก ์ค๋ฒ๋ผ์ด๋ํ๋ ๊ฒ์ ์๋ฐฉํ ์๋ ์๋ค. ๋จ, ์ด๋ฐ ๋ฐฉ๋ฒ์ ํด๋ผ์ด์ธํธ ์ฝ๋์์ ํ ํ๋ฆฟ ๋ฉ์๋์ ๋ถ๋ณ์ ์ธ ๋ถ๋ถ์ ์ ํ ๋ณ๊ฒฝํ ํ์๊ฐ ์๋ ๊ฒ์ด ํ์คํ ๋์๋ง ์ฌ์ฉํด์ผ ํ๋ค.
# 0304 - @Constraint, ConstraintValidator
์คํ๋ง์์๋ JSR 303 ๊ธฐ๋ฐ ์ด๋ ธํ ์ด์ ๊ธฐ๋ฐ์ผ๋ก ์ผ๊ด์ฑ ์๋ Validation์ ์งํํ ์ ์๋ค. ๊ธฐ๋ณธ์ผ๋ก ์ ๊ณตํ๋ ์ด๋ ธํ ์ด์ ๋ง๊ณ @Cnstraint๋ก ์ปค์คํ Validation์ ๋ง๋ค ์ ์๋ค.
# [ ์ฅ์ ]
- ์ผ๊ด์ฑ์๋ ์ฒ๋ฆฌ ๋ฐฉ๋ฒ - ๊ฒ์ฆ๋ฐฉ๋ฒ๊ณผ ๊ฒ์ฆ์์ ์ ๋ํด ํต์ผ์ฑ์ ๊ฐ์ง ์ ์๋ค. ํด๋น ์ปค์คํ
Validation์ ๊ตฌํํ๋ฉด
@NotNull
@Empty
์ ๊ฐ์ ๋จ๊ณ์ธ interceptor์์ ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํ๋ค. - ๊ฐ๊ฒฐํจ - ๋ก์ง ํ๋ฆ์ ๋ํ ์ปจํ ์คํธ๊ฐ ์์ถ๋ผ ์์ด ์ ์ฌ์ ์์ ์ฌ์ฉ๋๋ค๋ฉด ๋ถํ์ํ ๋ฐ๋ณต์ฝ๋๊ฐ ์ค์ด๋ ๋ค.
- ์ผ๊ด์ฑ ์๋ ErrorResponse - ConstraintViolationException ์๋ฌ๋ฅผ ํตํด ์๋ต์ ์ผ๊ด์ฑ์๊ฒ ๋ฆฌํดํ ์ ์๋ค.
# [ ๊ตฌํ ]
- ์ด๋ ธํ ์ด์ ์์ฑ(ํ๋ ์์ )
@Constraint(validatedBy = GenderValidator.class)
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidGender {
String message() default "์ฌ๋ฐ๋ฅธ ์ฑ๋ณ์ ์
๋ ฅํด์ฃผ์ธ์";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
2
3
4
5
6
7
8
- ConstraintValidator๋ฅผ ๊ตฌํํ Validator ์์ฑ
public class GenderValidtor implements ConstraintValidator<ValidGender, String>{
@Override
public boolean isValid(String gender, ConstraintValidatorContext context) {
try {
Gender.valueOf(gender.toUpperCase());
return true;
} catch (Exception e) {
return false;
}
}
}
2
3
4
5
6
7
8
9
10
11
12
- dto
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class CreateUserReqDto {
@Email(message = "์ฌ๋ฐ๋ฅธ ์ด๋ฉ์ผ์ ์
๋ ฅํด์ฃผ์ธ์")
private String email;
private String password;
@ValidGender(message = "์ฌ๋ฐ๋ฅธ ์ฑ๋ณ์ ์
๋ ฅํด์ฃผ์ธ์") <-- CustomValidator
private Gender gender;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
- ์ถ๊ฐ์์
@Constraint(validatedBy = NotEqualValidator.class)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface NotEqual {
String message() default "์ํ ์ญ๊ณผ ํํ ์ญ์ ๊ฐ์ ์ ์์ต๋๋ค.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String upStationId();
String downStationId();
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface List {
NotEqual[] value();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class NotEqualValidator implements ConstraintValidator<NotEqual, Object> {
private String upStationId;
private String downStationId;
@Override
public void initialize(NotEqual constraintAnnotation) {
this.upStationId = constraintAnnotation.upStationId();
this.downStationId = constraintAnnotation.downStationId();
}
@Override
public boolean isValid(Object object, ConstraintValidatorContext context) {
Object upStationValue = new BeanWrapperImpl(object).getPropertyValue(upStationId);
Object downStationValue = new BeanWrapperImpl(object).getPropertyValue(downStationId);
return !upStationValue.equals(downStationValue);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@NotEqual(upStationId = "upStationId", downStationId = "downStationId")
public class SectionRequest {
@NotNull
private Long upStationId;
@NotNull
private Long downStationId;
// ...
}
2
3
4
5
6
7
8
9
10
# [ ๊ฒฐ๋ก ]
์ปค์คํ
์ด๋
ธํ
์ด์
์ ์ ์ด์ฉํ๋ฉด ๋ถํ์ํ ๋ฐ๋ณต์ฝ๋๊ฐ ์ค์ด๋ค๊ณ , ๋น์ง๋์ค ๋ก์ง์ ๋ ์ง์ค ํ ์ ์๋ค๋ ์ฅ์ ์ด ์๋ค.
๋ค๋ง, ์ปค์คํ
์ด๋
ธํ
์ด์
์ ์๋์ ๋ชฉ์ ์ ๋ช
ํํ ํ์ฌ ๊ตฌ์ฑ์๊ฐ ๊ณต๊ฐ๋๋ฅผ ์ด๋ฃฌ ํ ์ถ๊ฐํ๋ ๊ฒ์ด ์ข๋ค.