[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
λ°˜μ‘ν˜•