[Spring] @Valid๋ฅผ ์ด์šฉํ•œ @RequestBody ๊ฒ€์ฆ / Exception Handling / ์ •๊ทœ ํ‘œํ˜„์‹

2022. 3. 28. 17:14ใ†Back-End ์ž‘์—…์‹ค/Spring Framework

728x90
๋ฐ˜์‘ํ˜•

 

๐Ÿš€ @Valid

    ๐Ÿ”ฝ  ๊ฐœ์š”

Spring์„ ์ด์šฉํ•˜์—ฌ `Controller`๋ฅผ ๋งŒ๋“ค๊ณ , ๊ฒŒ์‹œํŒ, ํšŒ์› ๊ฐ€์ž… ๋“ฑ Client์—์„œ ์ž…๋ ฅ๊ฐ’์„ ๋ฐ›์„ ๋•Œ๋Š” ํ•ญ์ƒ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ(Validation)์„ ํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ์ด์—์š”.

์ด ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋Š” Client (Front End)์—์„œ๋„ ํ•ด์•ผ ํ•˜์ง€๋งŒ, Server (Back End)์—์„œ๋„ ๊ผญ ์ง„ํ–‰์„ ํ•ด์ค˜์•ผ ํ•˜๋Š”๋ฐ, ๊ทธ ์ด์œ ๋Š” ์•…์˜์ ์ธ ์ด์šฉ์ž๊ฐ€ ์šฐํšŒ ๋ฐฉ๋ฒ•์„ ํ†ตํ•ด Server์—๊ฒŒ ์ž…๋ ฅ๊ฐ’์„ ์ž…๋ ฅํ•˜๋ ค๋Š” ์‹œ๋„๋ฅผ ํ†ตํ•ด Client์˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ๋ฌด๋ ฅํ™” ์‹œํ‚ฌ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ธ ๊ฒƒ์ด์—์š”.

์ด๋ฅผ ํ†ตํ•ด `SQL Injection`, `XSS(Cross Site Scripting)` ๋“ฑ์˜ ๊ณต๊ฒฉ ๋ฐฉ์–ด๋ฅผ ์œ„ํ•ด ๊ผญ! ์ž…๋ ฅ๊ฐ’์— ๋Œ€ํ•œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์‹ค์‹œํ•ด์ฃผ์–ด ์˜๋„ํ•˜์ง€ ์•Š์€ ๊ฐ’์ด ๋“ค์–ด์˜ค์ง€ ์•Š๊ฒŒ ๋ฐฉ์–ด ํ•ด์ค˜์•ผ ํ•˜๋Š” ๊ฒƒ์ด์—์š”.

`@Valid`๋ฅผ ์ด์šฉํ•˜๋ฉด `Service` ๋‹จ์ด ์•„๋‹Œ ๊ฐ์ฒด(Entity, VO, DTO)์•ˆ์—์„œ Client์—์„œ ๋“ค์–ด์˜ค๋Š” ๊ฐ’์— ๋Œ€ํ•ด ๊ฒ€์ฆ์„ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด์—์š”.

`Jakarata Bean Validation API Packages`์— ์žˆ๋Š” `javax.validation.constraints` Package์— ์žˆ๋Š” ๊ธฐ๋ณธ์ ์ธ ๊ฒ€์ฆ Annotaion์„ ์ด์šฉํ•  ๊ฒƒ์ด์—์š”.

`javax.validation.constraints`Package ์•ˆ์—๋Š” ์•„์ฃผ ๋งŽ์€ Annotaion์ด ์กด์žฌํ•˜๋Š”๋ฐ, `@Valid`๋ฅผ ์ด์šฉํ•œ ๊ฐ์ฒด ๊ฒ€์ฆ ์‹œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ด Annotaion์„ ์ด์šฉํ•œ๋‹ต๋‹ˆ๋‹ค.

 

๐Ÿ’ก ์ฐธ๊ณ  ์‚ฌํ•ญ
`SpringBoot`๊ฐ€ Version Update๋ฅผ ํ•˜๋ฉด์„œ `web` ์˜์กด์„ฑ ์•ˆ์— ์žˆ๋˜ `constraints package`๊ฐ€ ์•„์˜ˆ Module๋กœ ๋น ์ง

 

 

 

 

    ๐Ÿ”ฝ  ์˜์กด์„ฑ ์ถ”๊ฐ€

        ๐Ÿ“ฆ  Maven

pom.xml

 

 

        ๐Ÿ“ฆ  Gradle

build.gradle

 

 

 

 

 

 

    ๐Ÿ”ฝ  @Valid ์‚ฌ์šฉ

์œ„์˜ ๊ทธ๋ฆผ์„ ๋ณด๋ฉด 50๋ฒˆ์งธ ์ค„ ๋งค๊ฐœ๋ณ€์ˆ˜ ์ž…๋ ฅ๋ž€์— `@Valid`๋ฅผ ์„ ์–ธํ•œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด์—์š”.
์ด๋ ‡๊ฒŒ ํ•˜๋ฉด RequestBody๋กœ ๋“ค์–ด์˜ค๋Š” ๊ฐ์ฒด์— ๋Œ€ํ•œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์‹ค์‹œํ•˜๋ผ๊ณ  ์ง€์‹œ๋ฅผ ๋‚ด๋ ค์ค€ ๊ฒƒ์ด์—์š”.

์–ด๋–ค ๊ฒƒ์„ ๊ฒ€์‚ฌ๋ฅผ ํ•  ๊ฒƒ์ธ์ง€๋Š” Entity, VO, DTO ๋“ฑ์— ์ž‘์„ฑ์„ ํ•ด ์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค!

 

DuplicateByUserIDDTO.java

์ฃผ๋‹ˆํ•˜๋ž‘์€ ์œ„ ๊ทธ๋ฆผ๊ณผ ๊ฐ™์ด DTO ๊ฐ์ฒด๋ฅผ ์ •์˜ํ•˜๊ณ , username(ID) ํ•„๋“œ์— ์–ด๋–ป๊ฒŒ ๊ฐ’์„ ๋ฐ›์„์ง€์— ๋Œ€ํ•ด ์ •์˜ํ•ด ์ค€ ๊ฒƒ์ด์—์š”.

โ— @NotBlank : Null์ด ์•„๋‹Œ ๊ฐ’๋งŒ ํ—ˆ์šฉํ•˜๋ฉฐ, ๊ณต๋ฐฑ์ด ์•„๋‹Œ ๋ฌธ์ž ํ•˜๋‚˜ ์ด์ƒ์„ ํฌํ•จํ•ด์•ผ ํ—ˆ์šฉํ•œ๋‹ค.
โ— @Size(min = x, max = x) : min ๋ณด๋‹ค ํฌ๊ฑฐ๋‚˜, ๊ฐ™๊ณ , max ๋ณด๋‹ค ์ž‘๊ฑฐ๋‚˜, ๊ฐ™์€ ๋ฌธ์ž์—ด ๊ธธ์ด ๋‚ด์˜ ๊ฐ’๋งŒ ์„œ์šฉํ•œ๋‹ค.
โ— @Pattern(regex = xxx) : regex์— ์ž…๋ ฅ๋œ ์ •๊ทœ ํ‘œํ˜„์‹์— ๋งž๋Š” ๊ฐ’๋งŒ ์ž…๋ ฅ ํ—ˆ์šฉํ•œ๋‹ค.

 

์ผ๋‹จ ์œ„์™€ ๊ฐ™์ด ํ—ˆ์šฉ๋œ ๊ฐ’์„ ๋„ฃ์–ด์„œ ์š”์ฒญ์„ ๋ณด๋‚ด๊ฒŒ ๋˜๋ฉด ํ—ˆ์šฉ์ด ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด์—์š”.

 

๊ณต๋ฐฑ ๋ฌธ์ž ์ž…๋ ฅ ์‹œ

{
    "timestamp": 1648444363174,
    "status": 400,
    "error": "Bad Request",
    "trace": "org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public com.devcommunity.junyharang.common.constant.DefaultResponse com.devcommunity.junyharang.controller.member.UserController.duplicateUserID(com.devcommunity.junyharang.model.dto.user.DuplicateByUserIDDTO) with 2 errors: [Field error in object 'duplicateByUserIDDTO' on field 'username': rejected value [ ]; codes [NotBlank.duplicateByUserIDDTO.username,NotBlank.username,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [duplicateByUserIDDTO.username,username]; arguments []; default message [username]]; default message [๊ณต๋ฐฑ์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค]] [Field error in object 'duplicateByUserIDDTO' on field 'username': rejected value [ ]; codes [Pattern.duplicateByUserIDDTO.username,Pattern.username,Pattern.java.lang.String,Pattern]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [duplicateByUserIDDTO.username,username]; arguments []; default message [username],[Ljavax.validation.constraints.Pattern$Flag;@3bf02bec,^[0-9a-zA-Z]+$]; default message [\"^[0-9a-zA-Z]+$\"์™€ ์ผ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค]] \n\tat org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:141)\n\tat org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:122)\n\tat org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:179)\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:146)\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1067)\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)\n\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:681)\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:764)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:327)\n\tat org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:115)\n\tat org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:81)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:122)\n\tat org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:116)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:126)\n\tat org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:81)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:109)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:149)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103)\n\tat org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90)\n\tat org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:110)\n\tat org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:80)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:55)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:211)\n\tat org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:183)\n\tat org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)\n\tat org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:540)\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:359)\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399)\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:889)\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1735)\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\n\tat org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)\n\tat org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\n\tat java.base/java.lang.Thread.run(Thread.java:829)\n",
    "message": "Validation failed for object='duplicateByUserIDDTO'. Error count: 2",
    "errors": [
        {
            "codes": [
                "NotBlank.duplicateByUserIDDTO.username",
                "NotBlank.username",
                "NotBlank.java.lang.String",
                "NotBlank"
            ],
            "arguments": [
                {
                    "codes": [
                        "duplicateByUserIDDTO.username",
                        "username"
                    ],
                    "arguments": null,
                    "defaultMessage": "username",
                    "code": "username"
                }
            ],
            "defaultMessage": "๊ณต๋ฐฑ์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค",
            "objectName": "duplicateByUserIDDTO",
            "field": "username",
            "rejectedValue": " ",
            "bindingFailure": false,
            "code": "NotBlank"
        },
        {
            "codes": [
                "Pattern.duplicateByUserIDDTO.username",
                "Pattern.username",
                "Pattern.java.lang.String",
                "Pattern"
            ],
            "arguments": [
                {
                    "codes": [
                        "duplicateByUserIDDTO.username",
                        "username"
                    ],
                    "arguments": null,
                    "defaultMessage": "username",
                    "code": "username"
                },
                [],
                {
                    "arguments": null,
                    "defaultMessage": "^[0-9a-zA-Z]+$",
                    "codes": [
                        "^[0-9a-zA-Z]+$"
                    ]
                }
            ],
            "defaultMessage": "\"^[0-9a-zA-Z]+$\"์™€ ์ผ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค",
            "objectName": "duplicateByUserIDDTO",
            "field": "username",
            "rejectedValue": " ",
            "bindingFailure": false,
            "code": "Pattern"
        }
    ],
    "path": "/api/user/duplicate/userid"
}

 

์œ„ ๊ทธ๋ฆผ์€ ๊ณต๋ฐฑ์„ ์ž…๋ ฅ ํ–ˆ์„ ๋•Œ ๋ชจ์Šต์ธ ๊ฒƒ์ด์—์š”.

 

ํŠน์ˆ˜๋ฌธ์ž๋ฅผ ์„ž์–ด ์ž…๋ ฅ ํ–ˆ์„ ๊ฒฝ์šฐ

 

{
    "timestamp": 1648444453061,
    "status": 400,
    "error": "Bad Request",
    "trace": "org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public com.devcommunity.junyharang.common.constant.DefaultResponse com.devcommunity.junyharang.controller.member.UserController.duplicateUserID(com.devcommunity.junyharang.model.dto.user.DuplicateByUserIDDTO): [Field error in object 'duplicateByUserIDDTO' on field 'username': rejected value [#$%junyharang]; codes [Pattern.duplicateByUserIDDTO.username,Pattern.username,Pattern.java.lang.String,Pattern]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [duplicateByUserIDDTO.username,username]; arguments []; default message [username],[Ljavax.validation.constraints.Pattern$Flag;@3bf02bec,^[0-9a-zA-Z]+$]; default message [\"^[0-9a-zA-Z]+$\"์™€ ์ผ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค]] \n\tat org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:141)\n\tat org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:122)\n\tat org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:179)\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:146)\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1067)\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)\n\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:681)\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:764)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:327)\n\tat org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:115)\n\tat org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:81)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:122)\n\tat org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:116)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:126)\n\tat org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:81)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:109)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:149)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103)\n\tat org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90)\n\tat org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:110)\n\tat org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:80)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:55)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:211)\n\tat org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:183)\n\tat org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)\n\tat org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:540)\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:359)\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399)\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:889)\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1735)\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\n\tat org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)\n\tat org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\n\tat java.base/java.lang.Thread.run(Thread.java:829)\n",
    "message": "Validation failed for object='duplicateByUserIDDTO'. Error count: 1",
    "errors": [
        {
            "codes": [
                "Pattern.duplicateByUserIDDTO.username",
                "Pattern.username",
                "Pattern.java.lang.String",
                "Pattern"
            ],
            "arguments": [
                {
                    "codes": [
                        "duplicateByUserIDDTO.username",
                        "username"
                    ],
                    "arguments": null,
                    "defaultMessage": "username",
                    "code": "username"
                },
                [],
                {
                    "arguments": null,
                    "defaultMessage": "^[0-9a-zA-Z]+$",
                    "codes": [
                        "^[0-9a-zA-Z]+$"
                    ]
                }
            ],
            "defaultMessage": "\"^[0-9a-zA-Z]+$\"์™€ ์ผ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค",
            "objectName": "duplicateByUserIDDTO",
            "field": "username",
            "rejectedValue": "#$%junyharang",
            "bindingFailure": false,
            "code": "Pattern"
        }
    ],
    "path": "/api/user/duplicate/userid"
}

์ด๋ ‡๊ฒŒ ์ •ํ•ด ๋†“์€ ๊ทœ์น™์— ๋งž์ง€ ์•Š๋Š” ์ž…๋ ฅ๊ฐ’์ด ๋‚˜์˜ค๋ฉด HTTP Status Code 400(Bad Request - ๋‚˜์œ ์š”์ฒญ)์ด๋ผ๊ณ  ์•Œ๋ ค์ฃผ๋ฉด์„œ ์ž…๋ ฅ์„ ๋ฐ›์ง€ ์•Š๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด์—์š”.

์ง€๊ธˆ ์œ„์—์„œ ๋ณด์ด๋Š” `Response` ๋‚ด์šฉ์€ `Spring`์ด ์ž๋™์œผ๋กœ ์ผ์ • ๊ทœ๊ฒฉ์— ๋งž์ถฐ ์ƒ์„ฑํ•œ `Exception` ํ‹€์— ๋งž์ถฐ ์‘๋‹ต์„ ํ•ด์ฃผ๋Š” ๊ฒƒ์ด์—์š”.

๋”ฐ.๋ผ.์„œ `@Valid`์™€ ๊ฒ€์ฆ Annotaion๋งŒ ์ž˜ ์จ์ค€๋‹ค๋ฉด ์ž…๋ ฅ๊ฐ’์— ๋Œ€ํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด์—์š”.

 

 

 

 

 

    ๐Ÿ”ฝ  ๊ฒ€์ฆ์šฉ Annotaion

        ๐Ÿ“ฆ  ๋ฌธ์ž์—ด ์œ ๋ฌด ๊ฒ€์ฆ(@NotBlank, @NotEmpty, @NotNull)

์ข… ๋ฅ˜ null
ํ—ˆ์šฉ ์—ฌ๋ถ€
""
ํ—ˆ์šฉ ์—ฌ๋ถ€
" "
ํ—ˆ์šฉ ์—ฌ๋ถ€
๋‚ด  ์šฉ
@NotNull ํ—ˆ์šฉ ์•ˆํ•จ ํ—ˆ์šฉ ํ—ˆ์šฉ - type : ์–ด๋–ค Type์ด๋“  ํ—ˆ์šฉ.
- null์ด ์•„๋‹Œ ๊ฐ’๋งŒ ํ—ˆ์šฉ.
-๋ฐ˜๋“œ์‹œ ๊ฐ’์ด ์กด์žฌํ•ด์•ผ ํ•œ๋‹ค.
@NotEmpty ํ—ˆ์šฉ ์•ˆํ•จ ํ—ˆ์šฉ ์•ˆํ•จ ํ—ˆ์šฉ - type : CharSequence(length of character) Collection (collection size) Map (map size Array (array length)

- null์ด๊ฑฐ๋‚˜, empty(๋นˆ ๋ฌธ์ž์—ด)์ด ์•„๋‹ˆ์–ด์•ผ ํ—ˆ์šฉ.
- ๋ฐ˜๋“œ์‹œ ๊ฐ’์ด ์กด์žฌํ•ด์•ผ ํ•˜๊ณ , ๊ธธ์ด ํ˜น์€ ํฌ๊ธฐ๊ฐ€ 0๋ณด๋‹ค ์ปค์•ผ ํ•œ๋‹ค.
@NotBlank ํ—ˆ์šฉ ์•ˆํ•จ ํ—ˆ์šฉ ์•ˆํ•จ ํ—ˆ์šฉ ์•ˆํ•จ - null์ด ์•„๋‹Œ ๊ฐ’๋งŒ ํ—ˆ์šฉ.
- ๊ณต๋ฐฑ์ด ์•„๋‹Œ ๋ฌธ์ž ํ•˜๋‚˜ ์ด์ƒ ํฌํ•จํ•ด์•ผ ํ—ˆ์šฉ.
- ๋ฐ˜๋“œ์‹œ ๊ฐ’์ด ์กด์ œํ•˜๊ณ , ๊ณต๋ฐฑ ๋ฌธ์ž๋ฅผ ์ œ์™ธํ•œ ๊ธธ์ด๊ฐ€ 0๋ณด๋‹ค ์ปค์•ผ ํ•œ๋‹ค.

 

        ๐Ÿ“ฆ  ์ตœ๋Œ€ ์ตœ์†Œ์— ๋Œ€ํ•œ ๊ฒ€์ฆ

SupportType

โ— BigDecimal, BitInteger, CharSequence byte, short, int, long์— ๋Œ€์‘ํ•˜๋Š” Wrapper Class.
โ— double, float๋Š” rounding Error๋กœ ์ธํ•ด ๋ฏธ์ง€์›.
โ— null ๋˜ํ•œ ํ—ˆ์šฉ์œผ๋กœ ๊ฐ„์ฃผ.

 

์˜ˆ์‹œ ์ฝ”๋“œ

 

 

        ๐Ÿ“ฆ  ๋ฒ”์œ„ ๊ฐ’์— ๋Œ€ํ•œ ๊ฒ€์ฆ

SupportType

โ— BigDecimal, BitInteger, CharSequence byte, short, int, long, double, float์— ๋Œ€์‘ํ•˜๋Š” Wrapper Class.
โ— null ๋˜ํ•œ ํ—ˆ์šฉ์œผ๋กœ ๊ฐ„์ฃผ.

 

์ข…      ๋ฅ˜ ๋‚ด    ์šฉ
@Posive ์–‘์ˆ˜์ธ ๊ฐ’๋งŒ ํ—ˆ์šฉ.
@PosiveOrZero 0์ด๊ฑฐ๋‚˜, ์–‘์ˆ˜์ธ ๊ฐ’๋งŒ ํ—ˆ์šฉ.
@Negative ์Œ์ˆ˜์ธ ๊ฐ’๋งŒ ํ—ˆ์šฉ.
@NegativeOrZero 0์ด๊ฑฐ๋‚˜, ์Œ์ˆ˜์ธ ๊ฐ’๋งŒ ํ—ˆ์šฉ.

 

์˜ˆ์‹œ ์ฝ”๋“œ

 

 

        ๐Ÿ“ฆ  ์‹œ๊ฐ„ ๊ฐ’์— ๋Œ€ํ•œ ๊ฒ€์ฆ

SupportType

โ— java.util.Date, java.util.Calendar, java.time.instant, java.time.LocalDate, java.time.LocalDateTime, java.time.LocalTime, java.time.MonthDay, java.time.OffsetDateTime, java.time.OffsetTime, java.time.Year, java.time.YearMonty, java.time.ZonedDateTime, java.time.chrono.HijrahDate, java.time.chrono.JapaneseDate java.time.chrono.MinguoDate, java.time.chrono.ThaiBuddhistDate

โ— null ๋˜ํ•œ ํ—ˆ์šฉ์œผ๋กœ ๊ฐ„์ฃผ.

 

`Now`์˜ ๊ธฐ์ค€

`ClockProvider`์˜ `Virtual Machine`์— ๋”ฐ๋ผ ํ˜„์žฌ ์‹œ๊ฐ„์„ ์ •์˜. ํ•„์š”ํ•œ ๊ฒฝ์šฐ `default time zone`์„ ์ ์šฉ.

 

์˜ˆ์‹œ ์ฝ”๋“œ

 

 

        ๐Ÿ“ฆ  Email ๊ฒ€์ฆ

SupportType

โ— null ๋˜ํ•œ ํ—ˆ์šฉ์œผ๋กœ ๊ฐ„์ฃผ.

 

Validation

โ— @Email : ์˜ฌ๋ฐ”๋ฅธ ํ˜•์‹์˜ Email ์ฃผ์†Œ์—ฌ์•ผ ํ•˜๋ฉฐ, (`@`๊ฐ€ ๊ผญ ๋“ค์–ด๊ฐ€์•ผํ•˜๊ณ , Domain์— '.'๋Š” ๊ฒ€์‚ฌํ•˜์ง€ ์•Š์Œ)

 

์˜ˆ์‹œ ์ฝ”๋“œ

 

 

        ๐Ÿ“ฆ  ์ž๋ฆฟ์ˆ˜ ๊ฒ€์ฆ

SupportType

โ— BigDecimal, BigInteger, CharSequence byte, short, int, long์— ๋Œ€์‘ํ•˜๋Š” Wrapper Class.
โ— null๋„ ํ—ˆ์šฉ์œผ๋กœ ๊ฐ„์ฃผ

 

Validation

โ— @Digits : ํ—ˆ์šฉ๋œ ๋ฒ”์œ„ ๋‚ด ์ˆซ์ž๊ฐ’
   - Require : int integer = ์ด ์ˆซ์ž์— ํ—ˆ์šฉ๋˜๋Š” ์ตœ๋Œ€ ์ •์ˆ˜ ์ž๋ฆฟ์ˆ˜
   - Require : int fraction = ์ด ์ˆซ์ž์— ํ—ˆ์šฉ๋˜๋Š” ์ตœ๋Œ€ ์†Œ์ˆ˜ ์ž๋ฆฟ์ˆ˜ 

 

 

์˜ˆ์‹œ ์ฝ”๋“œ

 

 

 

        ๐Ÿ“ฆ  ์ฐธ๊ณผ ๊ฑฐ์ง“(Boolean) ๊ฒ€์ฆ

SupportType

โ— Boolean, boolean

 

Validation

์ข…   ๋ฅ˜ ๋‚ด   ์šฉ
@AssertTure ๊ฐ’์ด ํ•ญ์ƒ `Ture`๋งŒ ํ—ˆ์šฉ
@AssertFalse ๊ฐ’์ด ํ•ญ์ƒ `False`๋งŒ ํ—ˆ์šฉ

 

 

์˜ˆ์‹œ ์ฝ”๋“œ

 

 

 

        ๐Ÿ“ฆ  ํฌ๊ธฐ ๊ฒ€์ฆ

SupportType

โ— CharSequence (length of character sequence), Collection (collection size), Map (map size), Array(array length)

โ— null๋„ ํ—ˆ์šฉ์œผ๋กœ ๊ฐ„์ฃผ

 

Validation

์ข…   ๋ฅ˜ ๋‚ด   ์šฉ Option
@Size ์ด ํฌ๊ธฐ๊ฐ€ ์ง€์ •๋œ ๊ฒฝ๊ณ„(ํฌํ•จ) ์‚ฌ์ด์— ์žˆ๋Š” ๊ฐ’๋งŒ ํ—ˆ์šฉ int max : element์˜ ํฌ๊ธฐ๊ฐ€ ์ž‘๊ฑฐ๋‚˜ ๊ฐ™๋‹ค.
int min : element์˜ ํฌ๊ธฐ๊ฐ€ ํฌ๊ฑฐ๋‚˜ ๊ฐ™๋‹ค.

 

 

์˜ˆ์‹œ ์ฝ”๋“œ

 

 

 

        ๐Ÿ“ฆ  ์ •๊ทœ์‹ ๊ฒ€์ฆ

SupportType

โ— CharSequence
โ— null๋„ ํ—ˆ์šฉ์œผ๋กœ ๊ฐ„์ฃผ

 

Validation

์ข…   ๋ฅ˜ ๋‚ด   ์šฉ Option
@Pattern ์ง€์ •ํ•œ ์ •๊ทœ์‹๊ณผ ๋Œ€์‘๋˜๋Š” ๋ฌธ์ž์—ด๋งŒ ํ—ˆ์šฉ.
JAVA์˜ Pattern Package์˜ Convention์„ ๋”ฐ๋ฆ„.
String regxp : ์ •๊ทœ์‹ ๋ฌธ์ž์—ด ์ง€์ •.

 

 

์˜ˆ์‹œ ์ฝ”๋“œ

 

 

 

        ๐Ÿ“ฆ  ๊ทธ ๋ฐ–์˜ @Valid ์ •๋ฆฌ

์ข…   ๋ฅ˜ ์ง€์› Type ๋‚ด   ์šฉ
@Future Time Class Now ๋ณด๋‹ค ๋ฏธ๋ž˜์˜ ๋‚ ์งœ์™€ ์‹œ๊ฐ„๋งŒ ํ—ˆ์šฉ.
@FutureOrPresent Time Class Now์˜ ์‹œ๊ฐ„์ด๊ฑฐ๋‚˜, ๋ฏธ๋ž˜์˜ ๋‚ ์งœ, ์‹œ๊ฐ„๋งŒ ํ—ˆ์šฉ.
@Past Time Class Now๋ณด๋‹ค ๊ณผ๊ฑฐ์˜ ๋‚ ์งœ, ์‹œ๊ฐ„๋งŒ ํ—ˆ์šฉ.
@PastOrPrensent Time Class Now์˜ ์‹œ๊ฐ„์ด๊ฑฐ๋‚˜, ๊ณผ๊ฑฐ์˜ ๋‚ ์งœ, ์‹œ๊ฐ„๋งŒ ํ—ˆ์šฉ.

 

 

 

 

 

 

 

๐Ÿš€ @Valid Exception Handling

์šฐ๋ฆฌ๋Š” ๋ฐ”๋กœ ์œ„์—์„œ `Spring`์ด ์Šค์Šค๋กœ ๋งŒ๋“ค์–ด์„œ ๊ทœ์น™์— ๋งž์ง€ ์•Š์•˜์„ ๋•Œ, Exception์„ ๋˜์ง€๋Š” ๋ชจ์Šต์„ ๋ณธ ๊ฒƒ์ด์—์š”.
๊ทผ๋ฐ, ๋„ˆ๋ฌด ๊ธธ๊ณ , ๋ณต์žกํ•œ ๊ฒƒ์ด ๋„ˆ๋ฌด ๋งˆ์Œ์— ๋“ค์ง€ ์•Š๋Š” ๊ฒƒ์ด์—์š”. ์ด๋Ÿด ๋•Œ, ๊ฐœ๋ฐœ์ž๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ์›ํ•˜๋Š” Exception์„ ๋˜์งˆ ์ˆ˜ ์žˆ๋„๋ก `Exception Handling`์„ ํ•ด ์ค„ ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค.

 

2022-03-28 14:12:43.166  WARN 3432 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public com.devcommunity.junyharang.common.constant.DefaultResponse com.devcommunity.junyharang.controller.member.UserController.duplicateUserID(com.devcommunity.junyharang.model.dto.user.DuplicateByUserIDDTO) with 2 errors: [Field error in object 'duplicateByUserIDDTO' on field 'username': rejected value [ ]; codes [NotBlank.duplicateByUserIDDTO.username,NotBlank.username,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [duplicateByUserIDDTO.username,username]; arguments []; default message [username]]; default message [๊ณต๋ฐฑ์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค]] [Field error in object 'duplicateByUserIDDTO' on field 'username': rejected value [ ]; codes [Pattern.duplicateByUserIDDTO.username,Pattern.username,Pattern.java.lang.String,Pattern]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [duplicateByUserIDDTO.username,username]; arguments []; default message [username],[Ljavax.validation.constraints.Pattern$Flag;@3bf02bec,^[0-9a-zA-Z]+$]; default message ["^[0-9a-zA-Z]+$"์™€ ์ผ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค]] ]
๋ฐ˜์‘ํ˜•

 

์ž˜๋ชป๋œ ์ž…๋ ฅ๊ฐ’์ด ๋“ค์–ด์™”์„ ๋•Œ, ์œ„์™€ ๊ฐ™์ด `MethodArgumentNotValidException`์ด ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด์—์š”. ์ด๊ฒƒ์„ ์ด์šฉํ•˜์—ฌ Custom ํ•˜๊ฒŒ Exception์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค.

์ด Exception์„ Handling ํ•˜๊ณ ์ž ํ•˜๋Š” `Controller`์—์„œ ์ •์˜ํ•ด๋„ ๋˜์ง€๋งŒ, ์ฃผ๋‹ˆํ•˜๋ž‘์€ ์ „์—ญ์  ์‚ฌ์šฉ์„ ์œ„ํ•ด ์•„๋ž˜์™€ ๊ฐ™์ด `@ControllerAdvice`๋ฅผ ์ •์˜ํ•ด์„œ ์ด์šฉํ•  ๊ฒƒ์ด์—์š”.

ExceptionAdvisor.java

 

๋จผ์ € ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ`MethodArgumentNotValidException`์„ ๋ฐ›์•„ `@Valid`์— ๋Œ€ํ•œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ฐ€ ์‹คํŒจ ํ–ˆ์„ ๋•Œ, ํ•ด๋‹น Method๊ฐ€ ํ˜ธ์ถœ๋˜๊ฒŒ ํ•œ ๊ฒƒ์ด์—์š”.

`BindingResult`์— `FiedError`์—์„œ ๊ฒ€์ฆ์ด ์‹คํŒจํ•œ Field ์ด๋ฆ„(Member ๋ณ€์ˆ˜ ์ด๋ฆ„), ์‹คํŒจ์— ๋Œ€ํ•œ Message, ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ฐ€ ์‹คํŒจํ•œ ์ž…๋ ฅ๊ฐ’์„ ๊ฐ€์ ธ์™€ ์ถœ๋ ฅ๋  Message๋ฅผ ๋งŒ๋“ค์–ด ์ฃผ๊ณ  ์žˆ๋Š” ๊ฒƒ์ด์—์š”.

๊ทธ๋ฆฌ๊ณ  ๋‚˜์„œ `Return Type`์„ ๋ณด๋ฉด `ResponseEntity<DefaultResponse<String>>`์œผ๋กœ ํ•ด ๋‘์—ˆ๋Š”๋ฐ,

 

DefaultResponse.java

์ฃผ๋‹ˆํ•˜๋ž‘์€ ์‘๋‹ต(Reponse)๊ฐ€ ๋‚˜๊ฐˆ ๋•Œ, ๋ณด๋‹ค ์ •๊ฐˆํ•˜๊ณ , ํŽธ๋ฆฌํ•˜๊ฒŒ ์‘๋‹ต์„ ๋ณด๋‚ด๊ธฐ ์œ„ํ•ด ์œ„์™€ ๊ฐ™์€ Class ํ•˜๋‚˜๋ฅผ ๋ฏธ๋ฆฌ ๋งŒ๋“ค์–ด ๋‘” ๊ฒƒ์ด์—์š”. ์ด๊ฒƒ์— ๋งž์ถ”์–ด ์‘๋‹ต๊ฐ’์„ ๋ณด๋‚ด๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด Return Type์„ ์œ„์™€ ๊ฐ™์ด ํ•ด ์ค€ ๊ฒƒ์ด๋ž๋‹ˆ๋‹ค. 

`ExceptionAdvisor.java`์˜ return๋ฌธ์€ ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ์ฒ˜์Œ์— HTTP Status Code (400)์˜ value(Bad Request)๋ฅผ ์ž…๋ ฅ๋˜๊ฒŒ ํ•ด ์ฃผ์—ˆ๊ณ , ํ•œ๊ธ€๋กœ ๋ฌด์Šจ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋Š”์ง€ ์•Œ๋ ค์ฃผ๊ฒŒ ์ž…๋ ฅ์„ ํ•ด ์ฃผ๊ณ , ๋ฌธ์ œ์— ๋Œ€ํ•œ ์ƒ์„ธ ๋‚ด์šฉ์ด ์ถœ๋ ฅ๋˜๊ฒŒ ํ•ด์ฃผ๊ณ , Http Status Code 400์„ ์ž…๋ ฅํ•ด ์ฃผ์–ด Exception์ด ๋‚˜๊ฐˆ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด ์ค€ ๊ฒƒ์ด์—์š”.

์ด๋ ‡๊ฒŒ ํ•ด์„œ ๋‹ค์‹œ ํ‹€๋ฆฐ ์š”์ฒญ์„ ๋ณด๋‚ด๊ฒŒ ๋˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์ •๋ฆฌ๋œ Exception์ด ์‘๋‹ต๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค.

 

ํŠน์ˆ˜ ๋ฌธ์ž๋ฅผ ์„ž์–ด ์ž…๋ ฅ ํ–ˆ์„ ๋•Œ

 

 

 

 

 

 

 

๐Ÿš€ ์ •๊ทœ ํ‘œํ˜„์‹(Regualr Expression)

    ๐Ÿ”ฝ  ์ •๊ทœ ํ‘œํ˜„์‹ ์ •๋ฆฌํ‘œ

ํ‘œ  ํ˜„  ์‹ ์„ค      ๋ช…
^ ๋ฌธ์ž์—ด์˜ ์‹œ์ž‘์„ ์˜๋ฏธ
$ ๋ฌธ์ž์—ด์˜ ์ข…๋ฃŒ๋ฅผ ์˜๋ฏธ
. ์ž„์˜์˜ ํ•œ ๋ฌธ์ž
(๋ฌธ์ž์˜ ์ข…๋ฅ˜ ์ƒ๊ด€ ์—†์œผ๋‚˜, \ ๋Š” ๋„ฃ์„ ์ˆ˜ ์—†๋‹ค.)
* ์•ž ๋ฌธ์ž๊ฐ€ ์—†์„ ์ˆ˜๋„ ์žˆ๊ณ , ๋ฌดํ•œ์ •์œผ๋กœ ๋งŽ์„ ์ˆ˜๋„ ์žˆ์Œ์„ ์˜๋ฏธ
+ ์•ž ๋ฌธ์ž๊ฐ€ ํ•˜๋‚˜ ์ด์ƒ์„ ์˜๋ฏธ
? ์•ž ๋ฌธ์ž๊ฐ€ ์—†๊ฑฐ๋‚˜, ํ•˜๋‚˜ ์žˆ์Œ์Œ ์˜๋ฏธ
[] ๋ฌธ์ž์˜ ์ง‘ํ•ฉ์ด๋‚˜, ๋ฒ”์œ„๋ฅผ ์˜๋ฏธํ•˜๋ฉฐ, ๋‘ ๋ฌธ์ž ์‚ฌ์ด๋Š” - ๊ธฐํ˜ธ๋ฅผ ํ†ตํ•ด ๋ฒ”์œ„๋ฅผ ํ‘œํ˜„.
[] ์•ˆ์—์„œ ^๊ฐ€ ๋จผ์ € ์กด์žฌํ•˜๋ฉด `not`์„ ์˜๋ฏธ
[a-zA-Z] a~z, A~z ์ค‘ ํ•˜๋‚˜์˜ ๋ฌธ์ž ์˜๋ฏธ

[]๋Š” ํ•œ๊ฐœ์˜ ๋ฌธ์ž๋ฅผ ์˜๋ฏธํ•˜๊ธฐ๋„ ํ•จ.
[abc] a,b,c ์ค‘ ํ•˜๋‚˜์˜ ๋ฌธ์ž ์˜๋ฏธ
[^abc] a,b,c ์™ธ์— ํ•˜๋‚˜์˜ ๋ฌธ์ž ์˜๋ฏธ
{} ํšŸ์ˆ˜ ๋˜๋Š” ๋ฒ”์œ„๋ฅผ ์˜๋ฏธ
{n} ์ •ํ™•ํžˆ n๊ฐœ
{n,} ์ตœ์†Œํ•œ n๊ฐœ
{n,m} n๊ฐœ ๋ถ€ํ„ฐ m๊ฐœ๊นŒ์ง€
() ์†Œ๊ด„ํ˜ธ ์•ˆ์˜ ๋ฌธ์ž๋ฅผ ํ•˜๋‚˜์˜ ๋ฌธ์ž๋กœ ์ธ์‹ํ•˜๊ฒ ๋‹ค๋Š” ์˜๋ฏธ
| Pattern ์•ˆ์—์„œ or ์—ฐ์‚ฐ ์ˆ˜ํ–‰์„ ํ•˜๊ณ ์ž ํ•  ๋•Œ ์‚ฌ์šฉ
\s ๊ณต๋ฐฑ ๋ฌธ์ž ์˜๋ฏธ
\S ๊ณต๋ฐฑ ๋ฌธ์ž๊ฐ€ ์•„๋‹Œ ๋‚˜๋จธ์ง€ ๋ฌธ์ž ์˜๋ฏธ
\w Alphabet ๋˜๋Š” ์ˆซ์ž ๋˜๋Š” `์–ธ๋”๋ฐ”(_)`๋ฅผ ์˜๋ฏธ
\W Alphabet ๋˜๋Š” ์ˆซ์ž ๋˜๋Š” `์–ธ๋”๋ฐ”(_)`๋ฅผ ์ œ์™ธํ•œ ๋ฌธ์ž๋ฅผ ์˜๋ฏธ
\d ์ •์ˆ˜ [0-9]์™€ ๋˜‘๊ฐ™์€ ์˜๋ฏธ
\D ์ˆซ์ž๋ฅผ ์ œ์™ธํ•œ ๋ชจ๋“  ๋ฌธ์ž
\ ์ •๊ทœํ‘œํ˜„์‹ ์—ญ์Šฌ๋ž˜์‹œ( \ )๋Š” ํ™•์žฅ ๋ฌธ์ž๋ฅผ ์˜๋ฏธ.
์—ญ์Šฌ๋ž˜์‹œ ๋‹ค์Œ์— ์ผ๋ฐ˜ ๋ฌธ์ž๊ฐ€ ์˜ค๋ฉด ํŠน์ˆ˜๋ฌธ์ž๋กœ ์ทจ๊ธ‰ํ•˜๊ณ , ์—ญ์Šฌ๋ž˜์‹œ ๋‹ค์Œ์— ํŠน์ˆ˜๋ฌธ์ž๊ฐ€ ์˜ค๋ฉด ๊ทธ ๋ฌธ์ž ์ž์ฒด๋ฅผ ์˜๋ฏธ(์ผ๋ฐ˜ ๋ฌธ์ž๋กœ ์ทจ๊ธ‰)
(?i) ์•ž ๋ถ€๋ถ„ (?i)๋ผ๋Š” Option์„ ๋„ฃ์–ด์ฃผ๋ฉด ๋Œ€, ์†Œ๋ฌธ์ž๋ฅผ ๊ตฌ๋ถ„ํ•˜์ง€ ์•Š๊ฒ ๋‹ค๋Š” ์˜๋ฏธ

 

 

 

 

    ๐Ÿ”ฝ  ์ž์ฃผ ์“ฐ์ด๋Š” ์ •๊ทœ์‹ Pattern

๋ถ„   ๋ฅ˜ ์ • ๊ทœ ์‹  Pattern
์ˆซ์ž ^[0-9]+$
์˜(์†Œ, ๋Œ€)๋ฌธ์ž ^[a-zA-Z]+$
ํ•œ๊ธ€ ^[๊ฐ€-ํžฃ]+$
์˜์–ด, ์ˆซ์ž ์กฐํ•ฉ ^[a-zA-Z0-9]+$
๋น„๋ฐ€๋ฒˆํ˜ธ
(์ˆซ์ž, ์˜(์†Œ, ๋Œ€)๋ฌธ์ž ํฌํ•จ 6 ~ 12์ž๋ฆฌ ์ด๋‚ด)
^[a-zA-Z0-9]{6,12}$
๋น„๋ฐ€๋ฒˆํ˜ธ
(์ˆซ์ž, ์˜(์†Œ, ๋Œ€)๋ฌธ์ž, ํŠน์ˆ˜๋ฌธ์ž ํฌํ•จ 8~15์ž๋ฆฌ ์ด๋‚ด)
^.*(?=^.{8,15}$)(?=.*\d)(?=.*[a-zA-Z])(?=.*[!@#$%^&+=].*$
E-mail ^[0-9a-zA-Z]+@[0-9a-zA-Z]+\\.([a-z]+)*$
ํœด๋Œ€์ „ํ™” ๋ฒˆํ˜ธ ^01(?:0|1|[6-9])?(\\d{3}|\\d{4})?(\\d{4})$
์ผ๋ฐ˜์ „ํ™” ๋ฒˆํ˜ธ ^0(?:2-9)?(?:2-9)?(\\d{3}|\\d{4})?(\\d{4})$
์ฃผ๋ฏผ๋“ฑ๋ก ๋ฒˆํ˜ธ ^\d{6}\-[1-4]\d{6}$
File ํ™•์žฅ์ž ^\\S+(?i)(txt|pdf|hwp|xls|png|jpg)&
์ด ์ค‘ File ํ™•์žฅ์ž ^(.+?)(\\.tar)?\\.gz)$

 

        ๐Ÿ“ฆ  ํ•œ๊ธ€ ํฌํ•จ ์—ฌ๋ถ€ ํ™•์ธ

โ— `matches()`๋Š” `String`์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” Method.
โ— `*`์€ ๋งจ ์•ž์— ์˜ฌ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ์•ž์— `.`์„ ์„ ์–ธ.
โ— `[ใ„ฑ-ใ…Ž]` ์€ ใ„ฑ ๋ถ€ํ„ฐ ใ…Ž ๊นŒ์ง€ ์˜๋ฏธ.
โ— '[ใ…-ใ…ฃ]`๋Š” ใ… ๋ถ€ํ„ฐ ใ…ฃ ๊นŒ์ง€ ์˜๋ฏธ.
โ— '[๊ฐ€-ํžฃ]`์€ ํ•œ๊ธ€ ์ž, ๋ชจ์Œ์„ ์กฐํ•ฉํ•˜์—ฌ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ํ•œ๊ธ€ ์˜๋ฏธ.

โ— `+`๋Š” ํ•œ๊ธ€์ด ํ•˜๋‚˜ ์ด์ƒ ๋ฐ˜๋ณต๋จ์„ ์˜๋ฏธ.
โ— `.*`์€ ํ•œ ๊ธ€์ž ๋˜๋Š” ๋ฌธ์ž์—ด ์˜๋ฏธ.

 

 

        ๐Ÿ“ฆ  ํ•ธ๋“œํฐ ๋ฒˆํ˜ธ Pattern ๋งŒ๋“ค๊ธฐ

๋Œ€ํ•œ๋ฏผ๊ตญ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ํ•ธ๋“œํฐ ๋ฒˆํ˜ธ Pattern์„ ํ•œ๋ฒˆ ๋งŒ๋“ค์–ด ๋ณผ ๊ฒƒ์ด์—์š”.

์šฐ๋ฆฌ๋‚˜๋ผ์˜ ํ•ธ๋“œํฐ ๋ฒˆํ˜ธ๋Š” 3๊ฐœ๋กœ ๊ตฌ๋ถ„๋˜์–ด ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ์ด์—์š”.

์ฒซ๋ฒˆ์งธ ์ž๋ฆฌ์—๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฒˆํ˜ธ๋“ค์ด ์˜ฌ ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค.

010
011
016
017
018
019

์ง€๊ธˆ์€ `010`๋งŒ ์ด์šฉ๋˜๊ณ  ์žˆ์ง€๋งŒ, ์—…๋ฌด๋กœ ์ธํ•ด ํ•ธ๋“œํฐ ๋ฒˆํ˜ธ๋ฅผ ๋ฐ”๊พธ์ง€ ๋ชปํ•˜์…จ๋˜ ๋ถ„๋“ค์€ ์–ด์ฉŒ๋ฉด ์•„์ง๋„ ์˜›๋‚  `Pattern`์„ ์ด์šฉํ•˜๊ณ  ๊ณ„์‹ค์ง€ ๋ชจ๋ฅด๋Š” ๊ฒƒ์ด์—์š”.

 

๋‘๋ฒˆ์งธ ์ž๋ฆฌ๋Š” 3 ~ 4์ž๋ฆฌ์˜ ์ž„์˜์˜ ์ˆซ์ž๊ฐ€ ์˜ค๋ฉด ๋˜๋Š” ๊ฒƒ์ด์—์š”.

์„ธ๋ฒˆ์งธ ์ž๋ฆฌ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ 4์ž๋ฆฌ๋ฅผ ์ง€์ •ํ•˜๊ณ  ์žˆ์œผ๋‹ˆ ๊ทธ๊ฒƒ์„ ๋”ฐ๋ฅด๋„๋ก ํ•  ๊ฒƒ์ด์—์š”.

์ž๋ฆฟ์ˆ˜ ์‚ฌ์ด์— ์•„๋ฌด ํ‘œ๊ธฐ๋„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ฑฐ๋‚˜ `-`ํ˜น์€ `.`์œผ๋กœ ๊ตฌ๋ถ„ํ•˜๋Š” ๊ฒƒ์„ ํ—ˆ์šฉํ•˜๋Š” ๊ฑธ๋กœ ์ž‘์„ฑํ•ด ๋ณผ ๊ฒƒ์ด์—์š”.

์˜ˆ๋ฅผ ๋“ค์ž๋ฉด

01012345678
010-1234-5678
010.1234.5678

์ด๋ ‡๊ฒŒ ๋ง์ด์—์š”.

 

โ–บ ์ฒซ๋ฒˆ์งธ ์ž๋ฆฌ์˜ ์„ธ ์ž๋ฆฌ

`^01`

์ •๊ทœ ํ‘œํ˜„์‹ `Pattern`์„ ์˜๋ฏธํ•˜๋Š” `^`๋ฅผ ์‹œ์ž‘์œผ๋กœ `01`์„ ์ถ”๊ฐ€ํ•œ ๊ฒƒ์ด์—์š”.
๋ฌธ์ž์—ด์˜ ์‹œ์ž‘์„ `01`๋กœ ์‹œ์ž‘ํ• ๊ฑฐ์•ผ! ๋ผ๊ณ  ์•Œ๋ ค์ค€ ๊ฒƒ์ด์—์š”.

`01(?:0|1|[6-9])`

`01` ๋‹ค์Œ์— ์˜ค๋Š” ๊ฐ’์œผ๋กœ 0, 1 ๋˜๋Š” 6 ~ 9 ์‚ฌ์ด ๊ฐ’์ด ํ—ˆ์šฉ๋˜๋„๋ก ํ•œ ๊ฒƒ์ด์—์š”.

 

โ–บ ๋‘๋ฒˆ์งธ ์ž๋ฆฌ

`01(?:0|1|[6-9])(\\d{3}|\\d{4})`

3์ž๋ฆฌ ๋˜๋Š” 4์ž๋ฆฌ์˜ ์ž„์˜์˜ ์ˆซ์ž๋ฅผ ํ—ˆ์šฉ๋˜๋„๋ก ํ•œ ๊ฒƒ์ด์—์š”.

 

โ–บ ์„ธ๋ฒˆ์งธ ์ž๋ฆฌ

`01(?:0|1|[6-9])(\\d{3}|\\d{4})(\\d{4})$`

๋งˆ์ง€๋ง‰ 4์ž๋ฆฌ์˜ ์ˆซ์ž๋ฅผ ์ง€์ •ํ•œ ๊ฒƒ์ด์—์š”. ๊ทธ๋ฆฌ๊ณ , ํ‘œํ˜„์‹์˜ ์ข…๋ฃŒ๋ฅผ ์•Œ๋ฆฌ๋Š” `$`๋กœ ๋งˆ๋ฌด๋ฆฌ ํ•ด์ค€ ๊ฒƒ์ด์—์š”.

 

 

โ–บ ๊ตฌ๋ถ„๊ฐ’ ์ถ”๊ฐ€

ํ•ธ๋“œํฐ ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅ์‹œ `-`, `.`์„ ์ถ”๊ฐ€ํ•ด์„œ ์ž…๋ ฅํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์ข…์ข… ์žˆ๋Š” ๊ฒƒ์ด์—์š”.
์•„๋ฌด๋Ÿฐ ํ‘œ๊ธฐ๋ฅผ ํ•˜์ง€ ์•Š๊ณ , ์ˆซ์ž๋งŒ ์ž…๋ ฅ ๋ฐ›๊ฑฐ๋‚˜, ์œ„์˜ ํŠน์ˆ˜๋ฌธ์ž๋ฅผ ํ—ˆ์šฉํ•˜๋„๋ก ํ•ด ๋ณผ ๊ฒƒ์ด์—์š”.

`01(?:0|1|[6-9])[.-]?(\\d{3}|\\d{4})[.-]?(\\d{4})$`

`[.-]?`

์ด ๋œป์€ `.`,  `-` ๊ฐ’์ด ์—†๊ฑฐ๋‚˜, ๋‹จ ํ•œ๊ฐœ๋งŒ ์กด์žฌํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•˜๋Š” ๊ฒƒ์ด์—์š”.

 

 

 

 

 

    ๐Ÿ”ฝ  Pattern Class

โ— ์ •๊ทœ ํ‘œํ˜„์‹์— ๋Œ€ํ•œ ๋ฌธ์ž์—ด ๊ฒ€์ฆ.
โ— ๊ณต๊ฐœ๋œ ์ƒ์„ฑ์ž ๋ฏธ ์ œ๊ณต.
โ— `Pattern` ์ƒ์„ฑ ์‹œ `Pattern` ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ •์  `compile` Method ํ˜ธ์ถœ.
โ— 'matches()`๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๊ฒ€์ฆ
โ— regex
  - ์ •๊ทœ ํ‘œํ˜„์‹
โ— input
 - ๊ฒ€์ฆํ•  ๋ฌธ์ž์—ด

 

์˜ˆ์‹œ ์ฝ”๋“œ

 

 

 

    ๐Ÿ”ฝ  Matcher Class

โ— `Pattern`์„ ํ•ด์„ํ•˜๊ณ , ์ž…๋ ฅ ๋ฌธ์ž์—ด์— ๋Œ€ํ•ด ์ผ์น˜ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์—”์ง„.
โ— `Pattern` Class์™€ ๋™์ผํ•˜๊ฒŒ ๊ณต๊ฐœ๋œ ์ƒ์„ฑ์ž ๋ฏธ ์ •์˜.
โ— `Pattern` ๊ฐ์ฒด์˜ `matcher()`๋ฅผ ํ˜ธ์ถœํ•ด์„œ ๊ฒฐ๊ณผ๊ฐ’์„ ์–ป๋Š”๋‹ค.
โ— 'matches()`ํ™œ์šฉํ•˜์—ฌ ๊ฒ€์ฆ
โ— regex
  - ์ •๊ทœ ํ‘œํ˜„์‹
โ— input
 - ๊ฒ€์ฆํ•  ๋ฌธ์ž์—ด

 

 

 

์˜ˆ์‹œ ์ฝ”๋“œ

 

 

 

 


 

 

 

728x90
๋ฐ˜์‘ํ˜•