From 2abbfa0afd1664f25ef749fa2de13b3fb99e3d7a Mon Sep 17 00:00:00 2001 From: devjun10 Date: Tue, 20 Feb 2024 04:16:14 +0900 Subject: [PATCH 01/26] =?UTF-8?q?feature:=20Handler=20=EC=96=B4=EB=85=B8?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=B6=94=EA=B0=80=20-=20Path?= =?UTF-8?q?=EB=A5=BC=20=EC=9D=BD=EC=96=B4=EC=84=9C=20Handler=EB=A5=BC=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=ED=95=98=EA=B8=B0=20=EC=9C=84=ED=95=B4=20?= =?UTF-8?q?=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20-=20@Controller=20=EC=96=B4=EB=85=B8=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81=20*Re?= =?UTF-8?q?tention,=20Target,=20Documented=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../annotation/Controller.java | 9 +++++ .../annotation/DeleteMapping.java | 36 +++++++++++++++++ .../annotation/GetMapping.java | 2 +- .../annotation/PostMapping.java | 36 +++++++++++++++++ .../annotation/PutMapping.java | 39 +++++++++++++++++++ 5 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 mvc/src/main/java/project/server/mvc/springframework/annotation/DeleteMapping.java create mode 100644 mvc/src/main/java/project/server/mvc/springframework/annotation/PostMapping.java create mode 100644 mvc/src/main/java/project/server/mvc/springframework/annotation/PutMapping.java diff --git a/mvc/src/main/java/project/server/mvc/springframework/annotation/Controller.java b/mvc/src/main/java/project/server/mvc/springframework/annotation/Controller.java index a21498c..d46a433 100644 --- a/mvc/src/main/java/project/server/mvc/springframework/annotation/Controller.java +++ b/mvc/src/main/java/project/server/mvc/springframework/annotation/Controller.java @@ -1,5 +1,14 @@ package project.server.mvc.springframework.annotation; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + @Component +@Documented +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) public @interface Controller { } diff --git a/mvc/src/main/java/project/server/mvc/springframework/annotation/DeleteMapping.java b/mvc/src/main/java/project/server/mvc/springframework/annotation/DeleteMapping.java new file mode 100644 index 0000000..ca854e2 --- /dev/null +++ b/mvc/src/main/java/project/server/mvc/springframework/annotation/DeleteMapping.java @@ -0,0 +1,36 @@ +package project.server.mvc.springframework.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import project.server.mvc.servlet.http.HttpMethod; + +@Documented +@Retention(RetentionPolicy.RUNTIME) +@RequestMapping(method = HttpMethod.DELETE) +@Target({ElementType.TYPE_USE, ElementType.METHOD}) +public @interface DeleteMapping { + + @AliasFor(annotation = RequestMapping.class) + String name() default ""; + + @AliasFor(annotation = RequestMapping.class) + String[] value() default {}; + + @AliasFor(annotation = RequestMapping.class) + String path() default ""; + + @AliasFor(annotation = RequestMapping.class) + String[] params() default {}; + + @AliasFor(annotation = RequestMapping.class) + String[] headers() default {}; + + @AliasFor(annotation = RequestMapping.class) + String[] consumes() default {}; + + @AliasFor(annotation = RequestMapping.class) + String[] produces() default {}; +} diff --git a/mvc/src/main/java/project/server/mvc/springframework/annotation/GetMapping.java b/mvc/src/main/java/project/server/mvc/springframework/annotation/GetMapping.java index f5dd177..83a1062 100644 --- a/mvc/src/main/java/project/server/mvc/springframework/annotation/GetMapping.java +++ b/mvc/src/main/java/project/server/mvc/springframework/annotation/GetMapping.java @@ -20,7 +20,7 @@ String[] value() default {}; @AliasFor(annotation = RequestMapping.class) - String[] path() default {}; + String path() default ""; @AliasFor(annotation = RequestMapping.class) String[] params() default {}; diff --git a/mvc/src/main/java/project/server/mvc/springframework/annotation/PostMapping.java b/mvc/src/main/java/project/server/mvc/springframework/annotation/PostMapping.java new file mode 100644 index 0000000..db2b6e5 --- /dev/null +++ b/mvc/src/main/java/project/server/mvc/springframework/annotation/PostMapping.java @@ -0,0 +1,36 @@ +package project.server.mvc.springframework.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import project.server.mvc.servlet.http.HttpMethod; + +@Documented +@Retention(RetentionPolicy.RUNTIME) +@RequestMapping(method = HttpMethod.POST) +@Target({ElementType.TYPE_USE, ElementType.METHOD}) +public @interface PostMapping { + + @AliasFor(annotation = RequestMapping.class) + String name() default ""; + + @AliasFor(annotation = RequestMapping.class) + String[] value() default {}; + + @AliasFor(annotation = RequestMapping.class) + String path() default ""; + + @AliasFor(annotation = RequestMapping.class) + String[] params() default {}; + + @AliasFor(annotation = RequestMapping.class) + String[] headers() default {}; + + @AliasFor(annotation = RequestMapping.class) + String[] consumes() default {}; + + @AliasFor(annotation = RequestMapping.class) + String[] produces() default {}; +} diff --git a/mvc/src/main/java/project/server/mvc/springframework/annotation/PutMapping.java b/mvc/src/main/java/project/server/mvc/springframework/annotation/PutMapping.java new file mode 100644 index 0000000..db3e8d4 --- /dev/null +++ b/mvc/src/main/java/project/server/mvc/springframework/annotation/PutMapping.java @@ -0,0 +1,39 @@ +package project.server.mvc.springframework.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import project.server.mvc.servlet.http.HttpMethod; + +@Documented +@Retention(RetentionPolicy.RUNTIME) +@RequestMapping(method = HttpMethod.PUT) +@Target({ElementType.TYPE_USE, ElementType.METHOD}) +public @interface PutMapping { + + @AliasFor(annotation = RequestMapping.class) + String name() default ""; + + @AliasFor(annotation = RequestMapping.class) + String[] value() default {}; + + @AliasFor(annotation = RequestMapping.class) + String[] put() default {}; + + @AliasFor(annotation = RequestMapping.class) + String path() default ""; + + @AliasFor(annotation = RequestMapping.class) + String[] params() default {}; + + @AliasFor(annotation = RequestMapping.class) + String[] headers() default {}; + + @AliasFor(annotation = RequestMapping.class) + String[] consumes() default {}; + + @AliasFor(annotation = RequestMapping.class) + String[] produces() default {}; +} From afb76b7e620ff48a5cbf9742b522a87e5d75781a Mon Sep 17 00:00:00 2001 From: devjun10 Date: Tue, 20 Feb 2024 04:17:32 +0900 Subject: [PATCH 02/26] =?UTF-8?q?refactor:=20=EB=B9=88=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D/=EB=B9=88=20=EC=A1=B0=ED=9A=8C=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/project/server/mvc/Application.java | 3 +- .../context/ApplicationContext.java | 32 +++++++++++++++---- .../context/ApplicationContextProvider.java | 4 --- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/mvc/src/main/java/project/server/mvc/Application.java b/mvc/src/main/java/project/server/mvc/Application.java index cca29a4..e5f42bc 100644 --- a/mvc/src/main/java/project/server/mvc/Application.java +++ b/mvc/src/main/java/project/server/mvc/Application.java @@ -21,8 +21,7 @@ public Application( } private void initContext(String packages) throws Exception { - ApplicationContext context; - context = new ApplicationContext(packages); + ApplicationContext context = new ApplicationContext(packages); ApplicationContextProvider provider = new ApplicationContextProvider(); provider.setApplicationContext(context); } diff --git a/mvc/src/main/java/project/server/mvc/springframework/context/ApplicationContext.java b/mvc/src/main/java/project/server/mvc/springframework/context/ApplicationContext.java index 43ff168..3e8a2a6 100644 --- a/mvc/src/main/java/project/server/mvc/springframework/context/ApplicationContext.java +++ b/mvc/src/main/java/project/server/mvc/springframework/context/ApplicationContext.java @@ -1,12 +1,15 @@ package project.server.mvc.springframework.context; +import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import static java.lang.reflect.Modifier.isStatic; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Predicate; @@ -116,12 +119,6 @@ private void registerNamedInstance(Class clazz) { } } - public static T getBean(String beanName) { - @SuppressWarnings("unchecked") - T bean = (T) ApplicationContext.nameKeyBeans.get(beanName); - return bean; - } - private void add(Class clazz) throws Exception { if (clazzKeyBeans.containsKey(clazz) || allBeans.contains(clazz)) { return; @@ -195,6 +192,29 @@ public static T getBean(Class clazz) { return clazz.cast(dependencyInjectedBeans.get(clazz)); } + public static void register(T t) { + dependencyInjectedBeans.put(t.getClass(), t); + } + + public static T getBean(String beanName) { + @SuppressWarnings("unchecked") + T bean = (T) ApplicationContext.nameKeyBeans.get(beanName); + return bean; + } + + public static Collection findByAnnotation(Class clazz) { + List beans = new ArrayList<>(); + for (Object bean : dependencyInjectedBeans.values()) { + Annotation[] annotations = bean.getClass().getAnnotations(); + for (Annotation annotation : annotations) { + if (annotation.annotationType().equals(clazz)) { + beans.add(bean); + } + } + } + return beans; + } + public static Collection getAllDependencyInjectedInstances() { return nameKeyBeans.values(); } diff --git a/mvc/src/main/java/project/server/mvc/springframework/context/ApplicationContextProvider.java b/mvc/src/main/java/project/server/mvc/springframework/context/ApplicationContextProvider.java index 4763c53..f0718fe 100644 --- a/mvc/src/main/java/project/server/mvc/springframework/context/ApplicationContextProvider.java +++ b/mvc/src/main/java/project/server/mvc/springframework/context/ApplicationContextProvider.java @@ -11,8 +11,4 @@ public void setApplicationContext(ApplicationContext applicationContext) { public static ApplicationContext getApplicationContext() { return applicationContext; } - - public static T getBean(String beanName) { - return ApplicationContext.getBean(beanName); - } } From 98e6bc95d4fa534eb0126105627e91d6ceccb467 Mon Sep 17 00:00:00 2001 From: devjun10 Date: Tue, 20 Feb 2024 04:18:37 +0900 Subject: [PATCH 03/26] =?UTF-8?q?feature:=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B0=9C=EB=B0=9C=20-=20Interceptor=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20*=EA=B3=B5=ED=86=B5=20=EC=84=B8=EC=85=98?= =?UTF-8?q?=20=EC=B2=B4=ED=81=AC=20=EB=A1=9C=EC=A7=81=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../configuration/WebConfiguration.java | 33 ++++++ .../SessionCheckHandlerInterceptor.java | 64 +++++++++++ .../user/application/UserUpdateUseCase.java | 7 ++ .../user/application/service/UserService.java | 13 ++- .../application/service/UserServiceProxy.java | 21 +++- .../UserInfoUpdateController.java | 44 ++++++++ ...> DispatcherServletAutoConfiguration.java} | 2 +- .../handler/HandlerInterceptor.java | 18 +++ .../web/InterceptorRegistration.java | 36 ++++++ .../web/InterceptorRegistry.java | 20 ++++ .../springframework/web/WebMvcConfigurer.java | 5 + .../handler/HandlerMappingInitializer.java | 104 ++++++++++++++++++ 12 files changed, 364 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/project/server/app/common/configuration/WebConfiguration.java create mode 100644 app/src/main/java/project/server/app/common/configuration/interceptor/SessionCheckHandlerInterceptor.java create mode 100644 app/src/main/java/project/server/app/core/web/user/application/UserUpdateUseCase.java create mode 100644 app/src/main/java/project/server/app/core/web/user/presentation/UserInfoUpdateController.java rename mvc/src/main/java/project/server/mvc/springframework/context/{ServletConfiguration.java => DispatcherServletAutoConfiguration.java} (88%) create mode 100644 mvc/src/main/java/project/server/mvc/springframework/handler/HandlerInterceptor.java create mode 100644 mvc/src/main/java/project/server/mvc/springframework/web/InterceptorRegistration.java create mode 100644 mvc/src/main/java/project/server/mvc/springframework/web/InterceptorRegistry.java create mode 100644 mvc/src/main/java/project/server/mvc/springframework/web/WebMvcConfigurer.java create mode 100644 mvc/src/main/java/project/server/mvc/springframework/web/servlet/handler/HandlerMappingInitializer.java diff --git a/app/src/main/java/project/server/app/common/configuration/WebConfiguration.java b/app/src/main/java/project/server/app/common/configuration/WebConfiguration.java new file mode 100644 index 0000000..3ffa592 --- /dev/null +++ b/app/src/main/java/project/server/app/common/configuration/WebConfiguration.java @@ -0,0 +1,33 @@ +package project.server.app.common.configuration; + +import project.server.app.common.configuration.interceptor.SessionCheckHandlerInterceptor; +import project.server.app.core.web.user.application.UserLoginUseCase; +import project.server.app.core.web.user.presentation.validator.UserValidator; +import project.server.mvc.springframework.annotation.Component; +import static project.server.mvc.springframework.context.ApplicationContext.getBean; +import static project.server.mvc.springframework.context.ApplicationContext.register; +import project.server.mvc.springframework.web.InterceptorRegistry; +import project.server.mvc.springframework.web.WebMvcConfigurer; + +@Component +public class WebConfiguration implements WebMvcConfigurer { + + private final UserValidator validator; + private final UserLoginUseCase loginUseCase; + + public WebConfiguration( + UserValidator validator, + UserLoginUseCase loginUseCase + ) { + this.validator = validator; + this.loginUseCase = loginUseCase; + register(new InterceptorRegistry()); + addInterceptors(getBean(InterceptorRegistry.class)); + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(new SessionCheckHandlerInterceptor(validator, loginUseCase)) + .addPathPatterns("/my-info.html", "/my-info"); + } +} diff --git a/app/src/main/java/project/server/app/common/configuration/interceptor/SessionCheckHandlerInterceptor.java b/app/src/main/java/project/server/app/common/configuration/interceptor/SessionCheckHandlerInterceptor.java new file mode 100644 index 0000000..2226cf3 --- /dev/null +++ b/app/src/main/java/project/server/app/common/configuration/interceptor/SessionCheckHandlerInterceptor.java @@ -0,0 +1,64 @@ +package project.server.app.common.configuration.interceptor; + +import lombok.extern.slf4j.Slf4j; +import project.server.app.common.login.LoginUser; +import project.server.app.common.login.Session; +import static project.server.app.common.utils.HeaderUtils.getSessionId; +import project.server.app.core.web.user.application.UserLoginUseCase; +import project.server.app.core.web.user.presentation.validator.UserValidator; +import project.server.mvc.servlet.HttpServletRequest; +import project.server.mvc.servlet.HttpServletResponse; +import project.server.mvc.springframework.handler.HandlerInterceptor; +import project.server.mvc.springframework.web.servlet.ModelAndView; + +@Slf4j +public class SessionCheckHandlerInterceptor implements HandlerInterceptor { + + private final UserValidator validator; + private final UserLoginUseCase loginUseCase; + + public SessionCheckHandlerInterceptor( + UserValidator validator, + UserLoginUseCase loginUseCase + ) { + this.validator = validator; + this.loginUseCase = loginUseCase; + } + + @Override + public boolean preHandle( + HttpServletRequest request, + HttpServletResponse response, + Object handler + ) { + Long sessionId = getSessionId(request.getCookies()); + validator.validateSessionId(sessionId, response); + + Session findSession = loginUseCase.findSessionById(sessionId); + log.info("Session:{}", findSession); + validator.validateSession(findSession, response); + + LoginUser loginUser = new LoginUser(findSession); + request.setAttribute("loginUser", loginUser); + return false; + } + + @Override + public void postHandle( + HttpServletRequest request, + HttpServletResponse response, + Object handler, + ModelAndView modelAndView + ) { + HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); + } + + @Override + public void afterCompletion( + HttpServletRequest request, + HttpServletResponse response, + Object handler + ) { + HandlerInterceptor.super.afterCompletion(request, response, handler); + } +} diff --git a/app/src/main/java/project/server/app/core/web/user/application/UserUpdateUseCase.java b/app/src/main/java/project/server/app/core/web/user/application/UserUpdateUseCase.java new file mode 100644 index 0000000..6d1e3d7 --- /dev/null +++ b/app/src/main/java/project/server/app/core/web/user/application/UserUpdateUseCase.java @@ -0,0 +1,7 @@ +package project.server.app.core.web.user.application; + +import project.server.app.common.login.LoginUser; + +public interface UserUpdateUseCase { + void update(LoginUser loginUser, String password); +} diff --git a/app/src/main/java/project/server/app/core/web/user/application/service/UserService.java b/app/src/main/java/project/server/app/core/web/user/application/service/UserService.java index 11c4f63..7e765f6 100644 --- a/app/src/main/java/project/server/app/core/web/user/application/service/UserService.java +++ b/app/src/main/java/project/server/app/core/web/user/application/service/UserService.java @@ -7,12 +7,13 @@ import project.server.app.core.web.user.application.UserDeleteUseCase; import project.server.app.core.web.user.application.UserSaveUseCase; import project.server.app.core.web.user.application.UserSearchUseCase; +import project.server.app.core.web.user.application.UserUpdateUseCase; import project.server.app.core.web.user.exception.DuplicatedUsernameException; import project.server.app.core.web.user.exception.UserNotFoundException; import project.server.mvc.springframework.annotation.Service; @Service -public class UserService implements UserSaveUseCase, UserSearchUseCase, UserDeleteUseCase { +public class UserService implements UserSaveUseCase, UserSearchUseCase, UserUpdateUseCase, UserDeleteUseCase { private final UserRepository userRepository; @@ -43,6 +44,16 @@ public User findById(Long userId) { return findUser; } + @Override + public void update( + LoginUser loginUser, + String password + ) { + User findUser = userRepository.findById(loginUser.getUserId()) + .orElseThrow(UserNotFoundException::new); + + } + @Override public void delete(LoginUser loginUser) { User findUser = userRepository.findById(loginUser.getUserId()) diff --git a/app/src/main/java/project/server/app/core/web/user/application/service/UserServiceProxy.java b/app/src/main/java/project/server/app/core/web/user/application/service/UserServiceProxy.java index d25301c..fc2cf7f 100644 --- a/app/src/main/java/project/server/app/core/web/user/application/service/UserServiceProxy.java +++ b/app/src/main/java/project/server/app/core/web/user/application/service/UserServiceProxy.java @@ -10,6 +10,7 @@ import project.server.app.core.web.user.application.UserDeleteUseCase; import project.server.app.core.web.user.application.UserSaveUseCase; import project.server.app.core.web.user.application.UserSearchUseCase; +import project.server.app.core.web.user.application.UserUpdateUseCase; import project.server.jdbc.core.exception.DataAccessException; import static project.server.jdbc.core.transaction.DefaultTransactionDefinition.createTransactionDefinition; import project.server.jdbc.core.transaction.PlatformTransactionManager; @@ -18,7 +19,7 @@ @Slf4j @Component -public class UserServiceProxy implements UserSaveUseCase, UserSearchUseCase, UserDeleteUseCase { +public class UserServiceProxy implements UserSaveUseCase, UserSearchUseCase, UserUpdateUseCase, UserDeleteUseCase { private final PlatformTransactionManager txManager; private final UserService target; @@ -70,6 +71,24 @@ public User findById(Long userId) { } } + @Override + public void update( + LoginUser loginUser, + String password + ) { + TransactionStatus txStatus = getTransactionStatus(true); + log.debug("txStatus:[{}]", txStatus.getTransaction()); + try { + target.update(loginUser, password); + txManager.commit(txStatus); + log.debug("Transaction finished."); + } catch (BusinessException | DataAccessException exception) { + txManager.rollback(txStatus); + log.error("{}", exception.getMessage()); + throw exception; + } + } + @Override public void delete(LoginUser loginUser) { TransactionStatus txStatus = getTransactionStatus(false); diff --git a/app/src/main/java/project/server/app/core/web/user/presentation/UserInfoUpdateController.java b/app/src/main/java/project/server/app/core/web/user/presentation/UserInfoUpdateController.java new file mode 100644 index 0000000..0cfe6a3 --- /dev/null +++ b/app/src/main/java/project/server/app/core/web/user/presentation/UserInfoUpdateController.java @@ -0,0 +1,44 @@ +package project.server.app.core.web.user.presentation; + +import lombok.extern.slf4j.Slf4j; +import project.server.app.common.login.LoginUser; +import project.server.app.core.web.user.application.UserLoginUseCase; +import project.server.app.core.web.user.application.UserUpdateUseCase; +import project.server.app.core.web.user.presentation.validator.UserValidator; +import project.server.mvc.servlet.HttpServletRequest; +import project.server.mvc.servlet.HttpServletResponse; +import static project.server.mvc.servlet.http.HttpStatus.OK; +import project.server.mvc.springframework.annotation.Controller; +import project.server.mvc.springframework.web.servlet.Handler; +import project.server.mvc.springframework.web.servlet.ModelAndView; + +@Slf4j +@Controller +public class UserInfoUpdateController implements Handler { + + private final UserValidator validator; + private final UserLoginUseCase loginUseCase; + private final UserUpdateUseCase userUpdateUseCase; + + public UserInfoUpdateController( + UserValidator validator, + UserLoginUseCase loginUseCase, + UserUpdateUseCase userUpdateUseCase + ) { + this.validator = validator; + this.loginUseCase = loginUseCase; + this.userUpdateUseCase = userUpdateUseCase; + } + + @Override + public ModelAndView process( + HttpServletRequest request, + HttpServletResponse response + ) { + LoginUser loginUser = (LoginUser) request.getAttribute("loginUser"); + userUpdateUseCase.update(loginUser, (String) request.getAttribute("password")); + + response.setStatus(OK); + return null; + } +} diff --git a/mvc/src/main/java/project/server/mvc/springframework/context/ServletConfiguration.java b/mvc/src/main/java/project/server/mvc/springframework/context/DispatcherServletAutoConfiguration.java similarity index 88% rename from mvc/src/main/java/project/server/mvc/springframework/context/ServletConfiguration.java rename to mvc/src/main/java/project/server/mvc/springframework/context/DispatcherServletAutoConfiguration.java index 937ce96..0cd1223 100644 --- a/mvc/src/main/java/project/server/mvc/springframework/context/ServletConfiguration.java +++ b/mvc/src/main/java/project/server/mvc/springframework/context/DispatcherServletAutoConfiguration.java @@ -5,7 +5,7 @@ import project.server.mvc.springframework.web.servlet.DispatcherServlet; @Configuration -public class ServletConfiguration { +public class DispatcherServletAutoConfiguration { @Bean public DispatcherServlet dispatcherServlet() { diff --git a/mvc/src/main/java/project/server/mvc/springframework/handler/HandlerInterceptor.java b/mvc/src/main/java/project/server/mvc/springframework/handler/HandlerInterceptor.java new file mode 100644 index 0000000..ac51f65 --- /dev/null +++ b/mvc/src/main/java/project/server/mvc/springframework/handler/HandlerInterceptor.java @@ -0,0 +1,18 @@ +package project.server.mvc.springframework.handler; + +import project.server.mvc.servlet.HttpServletRequest; +import project.server.mvc.servlet.HttpServletResponse; +import project.server.mvc.springframework.web.servlet.ModelAndView; + +public interface HandlerInterceptor { + + default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { + return true; + } + + default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { + } + + default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler) { + } +} diff --git a/mvc/src/main/java/project/server/mvc/springframework/web/InterceptorRegistration.java b/mvc/src/main/java/project/server/mvc/springframework/web/InterceptorRegistration.java new file mode 100644 index 0000000..c39615b --- /dev/null +++ b/mvc/src/main/java/project/server/mvc/springframework/web/InterceptorRegistration.java @@ -0,0 +1,36 @@ +package project.server.mvc.springframework.web; + +import java.util.ArrayList; +import java.util.List; +import project.server.mvc.springframework.handler.HandlerInterceptor; + +public class InterceptorRegistration { + + private final List urls = new ArrayList<>(); + private final HandlerInterceptor handlerInterceptor; + + public InterceptorRegistration(HandlerInterceptor handlerInterceptor) { + this.handlerInterceptor = handlerInterceptor; + } + + public List getUrls() { + return urls; + } + + public boolean contains(String url) { + return urls.contains(url); + } + + public HandlerInterceptor getHandlerInterceptor() { + return handlerInterceptor; + } + + public void addPathPatterns(String... url) { + this.urls.addAll(List.of(url)); + } + + @Override + public String toString() { + return String.format("urls:%s, interceptor:%s", urls, handlerInterceptor); + } +} diff --git a/mvc/src/main/java/project/server/mvc/springframework/web/InterceptorRegistry.java b/mvc/src/main/java/project/server/mvc/springframework/web/InterceptorRegistry.java new file mode 100644 index 0000000..3e67630 --- /dev/null +++ b/mvc/src/main/java/project/server/mvc/springframework/web/InterceptorRegistry.java @@ -0,0 +1,20 @@ +package project.server.mvc.springframework.web; + +import java.util.ArrayList; +import java.util.List; +import project.server.mvc.springframework.handler.HandlerInterceptor; + +public class InterceptorRegistry { + + private final List registrations = new ArrayList<>(); + + public List getInterceptors() { + return registrations; + } + + public InterceptorRegistration addInterceptor(HandlerInterceptor interceptor) { + InterceptorRegistration registration = new InterceptorRegistration(interceptor); + this.registrations.add(registration); + return registration; + } +} diff --git a/mvc/src/main/java/project/server/mvc/springframework/web/WebMvcConfigurer.java b/mvc/src/main/java/project/server/mvc/springframework/web/WebMvcConfigurer.java new file mode 100644 index 0000000..15ef681 --- /dev/null +++ b/mvc/src/main/java/project/server/mvc/springframework/web/WebMvcConfigurer.java @@ -0,0 +1,5 @@ +package project.server.mvc.springframework.web; + +public interface WebMvcConfigurer { + void addInterceptors(InterceptorRegistry registry); +} diff --git a/mvc/src/main/java/project/server/mvc/springframework/web/servlet/handler/HandlerMappingInitializer.java b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/handler/HandlerMappingInitializer.java new file mode 100644 index 0000000..3d15b26 --- /dev/null +++ b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/handler/HandlerMappingInitializer.java @@ -0,0 +1,104 @@ +package project.server.mvc.springframework.web.servlet.handler; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import project.server.mvc.servlet.http.HttpMethod; +import project.server.mvc.springframework.annotation.Controller; +import project.server.mvc.springframework.annotation.DeleteMapping; +import project.server.mvc.springframework.annotation.GetMapping; +import project.server.mvc.springframework.annotation.PostMapping; +import project.server.mvc.springframework.annotation.PutMapping; +import static project.server.mvc.springframework.context.ApplicationContext.findByAnnotation; +import project.server.mvc.springframework.handler.RequestMappingInfo; +import project.server.mvc.springframework.web.method.HandlerMethod; +import project.server.mvc.springframework.web.servlet.Handler; + +public class HandlerMappingInitializer { + + private Map registry = new HashMap<>(); + + public HandlerMappingInitializer() { + List handlers = findByAnnotation(Controller.class).stream() + .map(bean -> (Handler) bean) + .toList(); + + for (Handler handler : handlers) { + registerHandlerMethod(handler); + } + } + + private void registerHandlerMethod(Handler handler) { + Method[] methods = handler.getClass().getDeclaredMethods(); + for (Method method : methods) { + registerGetMapping(handler, method); + registerPostMapping(handler, method); + registerPutMapping(handler, method); + registerDeleteMapping(handler, method); + } + } + + private void registerGetMapping( + Handler handler, + Method method + ) { + if (method.isAnnotationPresent(GetMapping.class)) { + GetMapping getMapping = method.getAnnotation(GetMapping.class); + String url = getMapping.path(); + RequestMappingInfo requestMappingInfo = new RequestMappingInfo(HttpMethod.GET, url); + HandlerMethod handlerMethod = new HandlerMethod(handler); + registerMapping(requestMappingInfo, handlerMethod); + } + } + + private void registerPostMapping( + Handler handler, + Method method + ) { + if (method.isAnnotationPresent(PostMapping.class)) { + PostMapping getMapping = method.getAnnotation(PostMapping.class); + String url = getMapping.path(); + RequestMappingInfo requestMappingInfo = new RequestMappingInfo(HttpMethod.POST, url); + HandlerMethod handlerMethod = new HandlerMethod(handler); + registerMapping(requestMappingInfo, handlerMethod); + } + } + + private void registerPutMapping( + Handler handler, + Method method + ) { + if (method.isAnnotationPresent(PutMapping.class)) { + PutMapping getMapping = method.getAnnotation(PutMapping.class); + String url = getMapping.path(); + RequestMappingInfo requestMappingInfo = new RequestMappingInfo(HttpMethod.PUT, url); + HandlerMethod handlerMethod = new HandlerMethod(handler); + registerMapping(requestMappingInfo, handlerMethod); + } + } + + private void registerDeleteMapping( + Handler handler, + Method method + ) { + if (method.isAnnotationPresent(DeleteMapping.class)) { + DeleteMapping getMapping = method.getAnnotation(DeleteMapping.class); + String url = getMapping.path(); + RequestMappingInfo requestMappingInfo = new RequestMappingInfo(HttpMethod.DELETE, url); + HandlerMethod handlerMethod = new HandlerMethod(handler); + registerMapping(requestMappingInfo, handlerMethod); + } + } + + private void registerMapping( + RequestMappingInfo requestMappingInfo, + HandlerMethod handlerMethod + ) { + registry.put(requestMappingInfo, new AbstractHandlerMethodMapping.MappingRegistration(handlerMethod)); + } + + public Map getRegistry() { + return registry; + } +} From fecc57845fb14ae038d75ef0230f681cddf38890 Mon Sep 17 00:00:00 2001 From: devjun10 Date: Tue, 20 Feb 2024 05:07:46 +0900 Subject: [PATCH 04/26] =?UTF-8?q?refactor:=20Path=20=EB=AA=85=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/user/presentation/HomeController.java | 6 ++--- .../user/presentation/LoginController.java | 6 +++-- .../user/presentation/SignUpController.java | 6 +++-- .../presentation/UserDeleteController.java | 1 + .../UserInfoSearchController.java | 25 +++---------------- 5 files changed, 15 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/project/server/app/core/web/user/presentation/HomeController.java b/app/src/main/java/project/server/app/core/web/user/presentation/HomeController.java index a70b8d9..eb2d3fa 100644 --- a/app/src/main/java/project/server/app/core/web/user/presentation/HomeController.java +++ b/app/src/main/java/project/server/app/core/web/user/presentation/HomeController.java @@ -1,17 +1,17 @@ package project.server.app.core.web.user.presentation; +import project.server.mvc.servlet.HttpServletRequest; +import project.server.mvc.servlet.HttpServletResponse; import project.server.mvc.springframework.annotation.Controller; import project.server.mvc.springframework.annotation.GetMapping; import project.server.mvc.springframework.web.servlet.Handler; -import project.server.mvc.servlet.HttpServletRequest; -import project.server.mvc.servlet.HttpServletResponse; import project.server.mvc.springframework.web.servlet.ModelAndView; @Controller public class HomeController implements Handler { @Override - @GetMapping("/") + @GetMapping(path = "/") public ModelAndView process( HttpServletRequest request, HttpServletResponse response diff --git a/app/src/main/java/project/server/app/core/web/user/presentation/LoginController.java b/app/src/main/java/project/server/app/core/web/user/presentation/LoginController.java index 7b5f02e..9c8e587 100644 --- a/app/src/main/java/project/server/app/core/web/user/presentation/LoginController.java +++ b/app/src/main/java/project/server/app/core/web/user/presentation/LoginController.java @@ -9,6 +9,7 @@ import project.server.mvc.servlet.http.Cookie; import static project.server.mvc.servlet.http.HttpStatus.MOVE_PERMANENTLY; import project.server.mvc.springframework.annotation.Controller; +import project.server.mvc.springframework.annotation.PostMapping; import project.server.mvc.springframework.web.servlet.Handler; import project.server.mvc.springframework.web.servlet.ModelAndView; @@ -30,12 +31,13 @@ public LoginController( } @Override + @PostMapping(path = "/sign-in") public ModelAndView process( HttpServletRequest request, HttpServletResponse response ) { - String username = request.getAttribute("username"); - String password = request.getAttribute("password"); + String username = (String) request.getAttribute("username"); + String password = (String) request.getAttribute("password"); log.info("username: {}, password: {}", username, password); validator.validateLoginInfo(username, password); diff --git a/app/src/main/java/project/server/app/core/web/user/presentation/SignUpController.java b/app/src/main/java/project/server/app/core/web/user/presentation/SignUpController.java index efa1955..1ae5b51 100644 --- a/app/src/main/java/project/server/app/core/web/user/presentation/SignUpController.java +++ b/app/src/main/java/project/server/app/core/web/user/presentation/SignUpController.java @@ -7,6 +7,7 @@ import project.server.mvc.servlet.HttpServletResponse; import static project.server.mvc.servlet.http.HttpStatus.OK; import project.server.mvc.springframework.annotation.Controller; +import project.server.mvc.springframework.annotation.PostMapping; import project.server.mvc.springframework.annotation.RequestMapping; import project.server.mvc.springframework.web.servlet.Handler; import project.server.mvc.springframework.web.servlet.ModelAndView; @@ -28,12 +29,13 @@ public SignUpController( } @Override + @PostMapping(path = "/sign-up") public ModelAndView process( HttpServletRequest request, HttpServletResponse response ) { - String username = request.getAttribute("username"); - String password = request.getAttribute("password"); + String username = (String) request.getAttribute("username"); + String password = (String) request.getAttribute("password"); log.info("username: {}, password: {}", username, password); validator.validateLoginInfo(username, password); diff --git a/app/src/main/java/project/server/app/core/web/user/presentation/UserDeleteController.java b/app/src/main/java/project/server/app/core/web/user/presentation/UserDeleteController.java index d82de8c..6f02dbc 100644 --- a/app/src/main/java/project/server/app/core/web/user/presentation/UserDeleteController.java +++ b/app/src/main/java/project/server/app/core/web/user/presentation/UserDeleteController.java @@ -11,6 +11,7 @@ import project.server.mvc.servlet.HttpServletResponse; import static project.server.mvc.servlet.http.HttpStatus.NO_CONTENT; import project.server.mvc.springframework.annotation.Controller; +import project.server.mvc.springframework.annotation.DeleteMapping; import project.server.mvc.springframework.web.servlet.Handler; import project.server.mvc.springframework.web.servlet.ModelAndView; diff --git a/app/src/main/java/project/server/app/core/web/user/presentation/UserInfoSearchController.java b/app/src/main/java/project/server/app/core/web/user/presentation/UserInfoSearchController.java index 593022d..e9bbae2 100644 --- a/app/src/main/java/project/server/app/core/web/user/presentation/UserInfoSearchController.java +++ b/app/src/main/java/project/server/app/core/web/user/presentation/UserInfoSearchController.java @@ -2,12 +2,8 @@ import lombok.extern.slf4j.Slf4j; import project.server.app.common.login.LoginUser; -import project.server.app.common.login.Session; -import static project.server.app.common.utils.HeaderUtils.getSessionId; import project.server.app.core.domain.user.User; -import project.server.app.core.web.user.application.UserLoginUseCase; import project.server.app.core.web.user.application.UserSearchUseCase; -import project.server.app.core.web.user.presentation.validator.UserValidator; import project.server.mvc.servlet.HttpServletRequest; import project.server.mvc.servlet.HttpServletResponse; import static project.server.mvc.servlet.http.HttpStatus.OK; @@ -23,34 +19,19 @@ @RequestMapping("/users") public class UserInfoSearchController implements Handler { - private final UserValidator validator; - private final UserLoginUseCase loginUseCase; private final UserSearchUseCase userSearchUseCase; - public UserInfoSearchController( - UserValidator validator, - UserLoginUseCase loginUseCase, - UserSearchUseCase userSearchUseCase - ) { - this.validator = validator; - this.loginUseCase = loginUseCase; + public UserInfoSearchController(UserSearchUseCase userSearchUseCase) { this.userSearchUseCase = userSearchUseCase; } @Override - @GetMapping(path = "/users/{userId}") + @GetMapping(path = "/my-info") public ModelAndView process( HttpServletRequest request, HttpServletResponse response ) { - Long sessionId = getSessionId(request.getCookies()); - validator.validateSessionId(sessionId, response); - - Session findSession = loginUseCase.findSessionById(sessionId); - validator.validateSession(findSession, response); - - log.info("Session:{}", findSession); - LoginUser loginUser = new LoginUser(findSession); + LoginUser loginUser = (LoginUser) request.getAttribute("loginUser"); User findUser = userSearchUseCase.findById(loginUser.getUserId()); ModelMap modelMap = createModelMap(findUser); From 18841b9dd2155aca0f931e8bad4df86b97cb4bb8 Mon Sep 17 00:00:00 2001 From: devjun10 Date: Tue, 20 Feb 2024 05:08:17 +0900 Subject: [PATCH 05/26] =?UTF-8?q?refactor:=20Http=20setAttribute=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/mvc/servlet/HttpServletRequest.java | 4 +++- .../project/server/mvc/servlet/Request.java | 16 +++++++++------- .../server/mvc/servlet/http/RequestBody.java | 17 ++++++++++++----- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/mvc/src/main/java/project/server/mvc/servlet/HttpServletRequest.java b/mvc/src/main/java/project/server/mvc/servlet/HttpServletRequest.java index 23824df..8c0fca2 100644 --- a/mvc/src/main/java/project/server/mvc/servlet/HttpServletRequest.java +++ b/mvc/src/main/java/project/server/mvc/servlet/HttpServletRequest.java @@ -21,9 +21,11 @@ public interface HttpServletRequest extends ServletRequest { RequestBody getRequestBody(); - String getAttribute(String key); + Object getAttribute(String key); Cookies getCookies(); String getHeader(String key); + + void setAttribute(String key, Object value); } diff --git a/mvc/src/main/java/project/server/mvc/servlet/Request.java b/mvc/src/main/java/project/server/mvc/servlet/Request.java index 161006b..96fa23d 100644 --- a/mvc/src/main/java/project/server/mvc/servlet/Request.java +++ b/mvc/src/main/java/project/server/mvc/servlet/Request.java @@ -20,12 +20,6 @@ public class Request implements HttpServletRequest { private final HttpHeaders headers; private final RequestBody requestBody; - public Request(BufferedReader bufferedReader) throws IOException { - this.requestLine = parseRequestLine(bufferedReader); - this.headers = parseHttpHeaders(bufferedReader); - this.requestBody = parseRequestBody(bufferedReader); - } - public Request( RequestLine requestLine, HttpHeaders headers, @@ -103,7 +97,7 @@ public RequestBody getRequestBody() { } @Override - public String getAttribute(String key) { + public Object getAttribute(String key) { return requestBody.getAttribute(key); } @@ -117,6 +111,14 @@ public String getHeader(String key) { return headers.getHeaderValue(key); } + @Override + public void setAttribute( + String key, + Object value + ) { + requestBody.setAttribute(key, value); + } + @Override public String toString() { return String.format("%s%s%s", requestLine, headers, requestBody); diff --git a/mvc/src/main/java/project/server/mvc/servlet/http/RequestBody.java b/mvc/src/main/java/project/server/mvc/servlet/http/RequestBody.java index 9bb79a7..f1ca041 100644 --- a/mvc/src/main/java/project/server/mvc/servlet/http/RequestBody.java +++ b/mvc/src/main/java/project/server/mvc/servlet/http/RequestBody.java @@ -12,18 +12,18 @@ public class RequestBody { private static final int KEY = 0; private static final int VALUE = 1; - private final Map attributes; + private final Map attributes; public RequestBody(String body) { if (body == null || body.isBlank()) { - attributes = null; + attributes = new HashMap<>(); return; } this.attributes = parseBody(body); } - private Map parseBody(String body) { - Map attributes = new HashMap<>(); + private Map parseBody(String body) { + Map attributes = new HashMap<>(); String[] bodyArray = body.split(VALUES_DELIMITER); for (String element : bodyArray) { String[] pair = element.split(VALUE_DELIMITER); @@ -32,7 +32,14 @@ private Map parseBody(String body) { return attributes; } - public String getAttribute(String key) { + public void setAttribute( + String key, + Object value + ) { + attributes.put(key, value); + } + + public Object getAttribute(String key) { return attributes.get(key); } From 3afbe3a6b5920e490fc31b75c9fd978ded727ff7 Mon Sep 17 00:00:00 2001 From: devjun10 Date: Tue, 20 Feb 2024 05:08:48 +0900 Subject: [PATCH 06/26] =?UTF-8?q?refactor:=20DispatcherServlet=20=EB=B9=88?= =?UTF-8?q?=20=EC=B4=88=EA=B8=B0=ED=99=94=20=EB=B0=A9=EC=8B=9D=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20*=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EA=B8=B0=EB=B0=98=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/mvc/servlet/GenericServlet.java | 11 +++++ .../server/mvc/servlet/HttpServlet.java | 1 - .../web/method/HandlerMethod.java | 5 -- .../web/servlet/DispatcherServlet.java | 46 +++++++++++++++---- .../web/servlet/FrameworkServlet.java | 29 ++++++++---- .../web/servlet/HandlerExecutionChain.java | 27 ++++++++++- .../handler/AbstractHandlerMapping.java | 36 ++++++++++++--- .../handler/AbstractHandlerMethodMapping.java | 34 +++----------- .../method/RequestMappingHandlerMapping.java | 6 +-- .../resource/ResourceHttpRequestHandler.java | 2 + 10 files changed, 133 insertions(+), 64 deletions(-) diff --git a/mvc/src/main/java/project/server/mvc/servlet/GenericServlet.java b/mvc/src/main/java/project/server/mvc/servlet/GenericServlet.java index 07c8ba8..1646229 100644 --- a/mvc/src/main/java/project/server/mvc/servlet/GenericServlet.java +++ b/mvc/src/main/java/project/server/mvc/servlet/GenericServlet.java @@ -1,9 +1,20 @@ package project.server.mvc.servlet; +import static project.server.mvc.springframework.context.ApplicationContext.register; +import project.server.mvc.springframework.web.method.HandlerMethod; +import project.server.mvc.springframework.web.servlet.handler.HandlerMappingInitializer; +import project.server.mvc.springframework.web.servlet.mvc.method.RequestMappingHandlerMapping; +import project.server.mvc.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; +import project.server.mvc.springframework.web.servlet.resource.ResourceHttpRequestHandler; + public abstract class GenericServlet implements Servlet { @Override public void init() { + register(new HandlerMethod(new ResourceHttpRequestHandler())); + register(new HandlerMappingInitializer()); + register(new RequestMappingHandlerMapping()); + register(new RequestMappingHandlerAdapter()); } @Override diff --git a/mvc/src/main/java/project/server/mvc/servlet/HttpServlet.java b/mvc/src/main/java/project/server/mvc/servlet/HttpServlet.java index 18e318a..8122d3b 100644 --- a/mvc/src/main/java/project/server/mvc/servlet/HttpServlet.java +++ b/mvc/src/main/java/project/server/mvc/servlet/HttpServlet.java @@ -29,7 +29,6 @@ protected void service( } if (method.isPost()) { doPost(request, response); - return; } } diff --git a/mvc/src/main/java/project/server/mvc/springframework/web/method/HandlerMethod.java b/mvc/src/main/java/project/server/mvc/springframework/web/method/HandlerMethod.java index 7756f8e..a461cc6 100644 --- a/mvc/src/main/java/project/server/mvc/springframework/web/method/HandlerMethod.java +++ b/mvc/src/main/java/project/server/mvc/springframework/web/method/HandlerMethod.java @@ -2,7 +2,6 @@ import java.lang.reflect.Method; import java.util.Objects; -import project.server.mvc.springframework.web.servlet.resource.ResourceHttpRequestHandler; public class HandlerMethod { @@ -52,10 +51,6 @@ public Method getMethod() { return method; } - public boolean handleStaticResource() { - return handler instanceof ResourceHttpRequestHandler; - } - @Override public boolean equals(Object object) { if (this == object) { diff --git a/mvc/src/main/java/project/server/mvc/springframework/web/servlet/DispatcherServlet.java b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/DispatcherServlet.java index b97c77a..571caa7 100644 --- a/mvc/src/main/java/project/server/mvc/springframework/web/servlet/DispatcherServlet.java +++ b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/DispatcherServlet.java @@ -1,29 +1,28 @@ package project.server.mvc.springframework.web.servlet; import java.util.List; +import static java.util.List.of; import project.server.mvc.servlet.HttpServletRequest; import project.server.mvc.servlet.HttpServletResponse; import project.server.mvc.servlet.ServletException; +import static project.server.mvc.springframework.context.ApplicationContext.getBean; import project.server.mvc.springframework.web.servlet.mvc.method.RequestMappingHandlerMapping; import project.server.mvc.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; public class DispatcherServlet extends FrameworkServlet { - private final List handlerMappings; - private final List handlerAdapters; - private final List viewResolvers; - private final GlobalExceptionHandler exceptionHandler; + private List handlerMappings; + private List handlerAdapters; + private List viewResolvers; + private GlobalExceptionHandler exceptionHandler; public DispatcherServlet() { - this.handlerMappings = List.of(new RequestMappingHandlerMapping()); - this.handlerAdapters = List.of(new RequestMappingHandlerAdapter()); - this.viewResolvers = List.of(new BeanNameViewResolver()); - this.exceptionHandler = new GlobalExceptionHandler(); + init(); } @Override public void init() { - super.init(); + initStrategies(); } @Override @@ -44,6 +43,9 @@ private void doDispatch( return; } + // 구현 편의를 위해 반환타입을 void로 지정 + handler.applyPreHandle(request, response); + HandlerAdapter handlerAdapter = getHandlerAdapter(handler.getHandler()); ModelAndView modelAndView = handlerAdapter.handle(request, response, handler.getHandler()); processDispatchResult(request, response, modelAndView); @@ -107,6 +109,32 @@ protected View resolveViewName(String viewName) { return null; } + private void initStrategies() { + initHandlerMappings(); + initHandlerAdapters(); + initViewResolvers(); + initExceptionHandler(); + } + + private void initHandlerMappings() { + super.init(); + RequestMappingHandlerMapping bean = getBean(RequestMappingHandlerMapping.class); + this.handlerMappings = of(bean); + } + + private void initHandlerAdapters() { + RequestMappingHandlerAdapter bean = getBean(RequestMappingHandlerAdapter.class); + this.handlerAdapters = of(bean); + } + + private void initViewResolvers() { + this.viewResolvers = of(new BeanNameViewResolver()); + } + + private void initExceptionHandler() { + this.exceptionHandler = new GlobalExceptionHandler(); + } + @Override public void destroy() { super.destroy(); diff --git a/mvc/src/main/java/project/server/mvc/springframework/web/servlet/FrameworkServlet.java b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/FrameworkServlet.java index b935917..c9a06e4 100644 --- a/mvc/src/main/java/project/server/mvc/springframework/web/servlet/FrameworkServlet.java +++ b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/FrameworkServlet.java @@ -5,24 +5,27 @@ import project.server.mvc.servlet.HttpServletBean; import project.server.mvc.servlet.HttpServletRequest; import project.server.mvc.servlet.HttpServletResponse; +import static project.server.mvc.springframework.context.ApplicationContext.getBean; +import project.server.mvc.springframework.handler.HandlerInterceptor; +import project.server.mvc.springframework.web.InterceptorRegistration; +import project.server.mvc.springframework.web.InterceptorRegistry; import project.server.mvc.springframework.web.method.HandlerMethod; import project.server.mvc.springframework.web.servlet.resource.ResourceHttpRequestHandler; public abstract class FrameworkServlet extends HttpServletBean { - private static final HandlerMethod staticResourceHandlerMethod = new HandlerMethod(new ResourceHttpRequestHandler()); + private final List interceptors; + private final HandlerMethod staticResourceHandlerMethod; private static final String EMPTY_STRING = ""; private static final List staticResources = List.of(".js", ".css", ".favicon", ".jpg", ".jpeg", ".png"); private static final List excludeStaticResources = List.of("my-info.html"); - @Override - public void init() { - super.init(); - } + public FrameworkServlet() { + init(); + InterceptorRegistry registration = getBean(InterceptorRegistry.class); - @Override - public void destroy() { - super.destroy(); + this.interceptors = registration.getInterceptors(); + this.staticResourceHandlerMethod = getBean(HandlerMethod.class); } @Override @@ -30,14 +33,14 @@ public void doGet( HttpServletRequest request, HttpServletResponse response ) throws Exception { - if (isStaticResource(request)) { + if (isUnAuthStaticResource(request)) { processStaticRequest(request, response); return; } processRequest(request, response); } - private boolean isStaticResource(HttpServletRequest request) { + private boolean isUnAuthStaticResource(HttpServletRequest request) { String uri = request.getRequestUri(); String[] parsedUri = uri.split("/"); for (String eachUri : parsedUri) { @@ -61,6 +64,12 @@ private void processStaticRequest( HttpServletResponse response ) throws IOException { ResourceHttpRequestHandler handler = (ResourceHttpRequestHandler) staticResourceHandlerMethod.getHandler(); + for (InterceptorRegistration registration : interceptors) { + if (registration.contains(request.getRequestUri())) { + HandlerInterceptor interceptor = registration.getHandlerInterceptor(); + interceptor.preHandle(request, response, handler); + } + } handler.handleRequest(request, response); } diff --git a/mvc/src/main/java/project/server/mvc/springframework/web/servlet/HandlerExecutionChain.java b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/HandlerExecutionChain.java index 9d8f858..752501a 100644 --- a/mvc/src/main/java/project/server/mvc/springframework/web/servlet/HandlerExecutionChain.java +++ b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/HandlerExecutionChain.java @@ -1,11 +1,24 @@ package project.server.mvc.springframework.web.servlet; +import java.util.List; import java.util.Objects; +import project.server.mvc.servlet.HttpServletRequest; +import project.server.mvc.servlet.HttpServletResponse; +import project.server.mvc.springframework.handler.HandlerInterceptor; public class HandlerExecutionChain { + private List interceptors; private final Object handler; + public HandlerExecutionChain( + List interceptors, + Object handler + ) { + this.interceptors = interceptors; + this.handler = handler; + } + public HandlerExecutionChain(Object handler) { this.handler = handler; } @@ -14,6 +27,18 @@ public Object getHandler() { return handler; } + void applyPreHandle( + HttpServletRequest request, + HttpServletResponse response + ) { + if (interceptors == null || interceptors.isEmpty()) { + return; + } + for (HandlerInterceptor interceptor : interceptors) { + interceptor.preHandle(request, response, handler); + } + } + @Override public boolean equals(Object object) { if (this == object) { @@ -33,6 +58,6 @@ public int hashCode() { @Override public String toString() { - return String.format("%s", handler); + return String.format("handler:%s, interceptors:%s", handler, interceptors); } } diff --git a/mvc/src/main/java/project/server/mvc/springframework/web/servlet/handler/AbstractHandlerMapping.java b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/handler/AbstractHandlerMapping.java index 90b9c59..89ad8f0 100644 --- a/mvc/src/main/java/project/server/mvc/springframework/web/servlet/handler/AbstractHandlerMapping.java +++ b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/handler/AbstractHandlerMapping.java @@ -1,22 +1,44 @@ package project.server.mvc.springframework.web.servlet.handler; +import java.util.ArrayList; +import java.util.List; +import project.server.mvc.servlet.HttpServletRequest; +import static project.server.mvc.springframework.context.ApplicationContext.getBean; +import project.server.mvc.springframework.handler.HandlerInterceptor; +import project.server.mvc.springframework.web.InterceptorRegistration; +import project.server.mvc.springframework.web.InterceptorRegistry; import project.server.mvc.springframework.web.servlet.HandlerExecutionChain; import project.server.mvc.springframework.web.servlet.HandlerMapping; -import project.server.mvc.servlet.HttpServletRequest; public abstract class AbstractHandlerMapping implements HandlerMapping { - private Object defaultHandler; + private final List interceptors; + + public AbstractHandlerMapping() { + InterceptorRegistry registration = getBean(InterceptorRegistry.class); + this.interceptors = registration.getInterceptors(); + } @Override public HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { Object handler = getHandlerInternal(request); - return new HandlerExecutionChain(handler); - } - - public Object getDefaultHandler() { - return this.defaultHandler; + return getHandlerExecutionChain(handler, request); } protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception; + + protected HandlerExecutionChain getHandlerExecutionChain( + Object handler, + HttpServletRequest request + ) { + List interceptors = new ArrayList<>(); + for (InterceptorRegistration registration : this.interceptors) { + if (registration.contains(request.getRequestUri())) { + HandlerInterceptor interceptor = registration.getHandlerInterceptor(); + interceptors.add(interceptor); + return new HandlerExecutionChain(interceptors, handler); + } + } + return new HandlerExecutionChain(handler); + } } diff --git a/mvc/src/main/java/project/server/mvc/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java index df3698c..33e1831 100644 --- a/mvc/src/main/java/project/server/mvc/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java +++ b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java @@ -1,11 +1,8 @@ package project.server.mvc.springframework.web.servlet.handler; -import java.util.HashMap; import java.util.Map; -import java.util.Optional; import project.server.mvc.servlet.HttpServletRequest; -import project.server.mvc.servlet.http.HttpMethod; -import project.server.mvc.springframework.context.ApplicationContext; +import static project.server.mvc.springframework.context.ApplicationContext.getBean; import project.server.mvc.springframework.handler.RequestMappingInfo; import project.server.mvc.springframework.web.method.HandlerMethod; @@ -37,33 +34,14 @@ private HandlerMethod lookupHandlerMethod( return registration != null ? registration.handlerMethod : null; } - class MappingRegistry { + static class MappingRegistry { private final Map registry; public MappingRegistry() { - this.registry = new HashMap<>(); - Object homeController = Optional.ofNullable(ApplicationContext.getBean("HomeController")).get(); - Object signUpController = Optional.ofNullable(ApplicationContext.getBean("SignUpController")).get(); - Object loginUpController = Optional.ofNullable(ApplicationContext.getBean("LoginController")).get(); - Object userInfoController = Optional.ofNullable(ApplicationContext.getBean("UserInfoSearchController")).get(); - - registry.put( - new RequestMappingInfo(HttpMethod.GET, "/"), - new MappingRegistration(new HandlerMethod(homeController)) - ); - registry.put( - new RequestMappingInfo(HttpMethod.GET, "/my-info"), - new MappingRegistration(new HandlerMethod(userInfoController)) - ); - registry.put( - new RequestMappingInfo(HttpMethod.POST, "/sign-up"), - new MappingRegistration(new HandlerMethod(signUpController)) - ); - registry.put( - new RequestMappingInfo(HttpMethod.POST, "/sign-in"), - new MappingRegistration(new HandlerMethod(loginUpController)) - ); + Object bean = getBean(HandlerMappingInitializer.class); + HandlerMappingInitializer handlerMappingInitializer = (HandlerMappingInitializer) bean; + this.registry = handlerMappingInitializer.getRegistry(); } public MappingRegistration getMappingRegistration(RequestMappingInfo requestMappingInfo) { @@ -71,7 +49,7 @@ public MappingRegistration getMappingRegistration(RequestMappingInfo requestMapp } } - public static class MappingRegistration { + static class MappingRegistration { private final HandlerMethod handlerMethod; diff --git a/mvc/src/main/java/project/server/mvc/springframework/web/servlet/mvc/method/RequestMappingHandlerMapping.java b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/mvc/method/RequestMappingHandlerMapping.java index 789b38c..35713f6 100644 --- a/mvc/src/main/java/project/server/mvc/springframework/web/servlet/mvc/method/RequestMappingHandlerMapping.java +++ b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/mvc/method/RequestMappingHandlerMapping.java @@ -1,15 +1,15 @@ package project.server.mvc.springframework.web.servlet.mvc.method; -import project.server.mvc.springframework.web.servlet.HandlerExecutionChain; -import project.server.mvc.springframework.web.method.HandlerMethod; import project.server.mvc.servlet.HttpServletRequest; +import project.server.mvc.springframework.web.method.HandlerMethod; +import project.server.mvc.springframework.web.servlet.HandlerExecutionChain; public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping { @Override public HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { HandlerMethod handlerMethod = getHandlerInternal(request); - return new HandlerExecutionChain(handlerMethod); + return getHandlerExecutionChain(handlerMethod, request); } @Override diff --git a/mvc/src/main/java/project/server/mvc/springframework/web/servlet/resource/ResourceHttpRequestHandler.java b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/resource/ResourceHttpRequestHandler.java index 900bcf5..9738934 100644 --- a/mvc/src/main/java/project/server/mvc/springframework/web/servlet/resource/ResourceHttpRequestHandler.java +++ b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/resource/ResourceHttpRequestHandler.java @@ -21,6 +21,7 @@ public class ResourceHttpRequestHandler implements HttpRequestHandler { private static final String HTML = ".html"; private static final String STATIC_PREFIX = "static"; + @Override public void handleRequest( HttpServletRequest request, @@ -34,6 +35,7 @@ public void handleRequest( return; } } + InputStream inputStream = getInputStream(getFile(uri)); if (inputStream != null) { response(request, response, inputStream); From fb5b15ba935bd2dbedb53572f6b0f007a488a87e Mon Sep 17 00:00:00 2001 From: devjun10 Date: Wed, 21 Feb 2024 02:13:24 +0900 Subject: [PATCH 07/26] =?UTF-8?q?refactor:=20=EC=84=B8=EC=85=98=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/resources/static/index.html | 6 ++ app/src/main/resources/static/my-info.html | 82 ++++++++-------------- 2 files changed, 35 insertions(+), 53 deletions(-) diff --git a/app/src/main/resources/static/index.html b/app/src/main/resources/static/index.html index 75ffbb3..c879883 100644 --- a/app/src/main/resources/static/index.html +++ b/app/src/main/resources/static/index.html @@ -136,10 +136,16 @@

Infra

window.location.href = signInMenu.getAttribute('href'); } else { console.error('Server responded with non-OK status'); + localStorage.clear(); + document.cookie = 'sessionId=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;'; + window.location.href = "http://localhost:8086/"; } }) .catch((error) => { console.error('Error:', error); + localStorage.clear(); + document.cookie = 'sessionId=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;'; + window.location.href = "http://localhost:8086/"; }); }); } diff --git a/app/src/main/resources/static/my-info.html b/app/src/main/resources/static/my-info.html index ff75362..e1ef339 100644 --- a/app/src/main/resources/static/my-info.html +++ b/app/src/main/resources/static/my-info.html @@ -1,12 +1,10 @@ - 회원 상세정보 - Bootstrap - -
@@ -40,78 +37,57 @@

회원 상세정보

- +
ID를 입력해주세요.
- +
비밀번호를 입력해주세요.
-
- - -
- 이메일을 입력해주세요. -
-
-
- - -
- 주소를 입력해주세요. -
-
-
- - -
-
-
- - -
-
- -
- From be7ed0adb8f61513dba34a1f33b495fac01ed7b4 Mon Sep 17 00:00:00 2001 From: devjun10 Date: Tue, 20 Feb 2024 05:09:56 +0900 Subject: [PATCH 08/26] =?UTF-8?q?refactor:=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/mvc/tomcat/NioSocketWrapper.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/mvc/src/main/java/project/server/mvc/tomcat/NioSocketWrapper.java b/mvc/src/main/java/project/server/mvc/tomcat/NioSocketWrapper.java index 93c4f7d..2dad812 100644 --- a/mvc/src/main/java/project/server/mvc/tomcat/NioSocketWrapper.java +++ b/mvc/src/main/java/project/server/mvc/tomcat/NioSocketWrapper.java @@ -1,7 +1,6 @@ package project.server.mvc.tomcat; import java.io.IOException; -import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import static java.nio.charset.StandardCharsets.UTF_8; @@ -15,7 +14,7 @@ import project.server.mvc.servlet.http.HttpHeaders; import project.server.mvc.servlet.http.RequestBody; import project.server.mvc.servlet.http.RequestLine; -import static project.server.mvc.springframework.context.ApplicationContextProvider.getBean; +import static project.server.mvc.springframework.context.ApplicationContext.getBean; import project.server.mvc.springframework.web.servlet.DispatcherServlet; @Slf4j @@ -67,6 +66,7 @@ public void run() { } } } catch (Exception exception) { + responseError(); log.error("message: {}", exception.getMessage()); } } @@ -104,4 +104,15 @@ public HttpServletResponse getResponse() { public void flip() { buffer.flip(); } + + private void responseError() { + ByteBuffer headerBuffer = ByteBuffer.wrap(response.toString().getBytes(UTF_8)); + while (headerBuffer.hasRemaining()) { + try { + this.socketChannel.write(headerBuffer); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } } From c0c7c645a9b8a315a47b6a6d03656b227af22683 Mon Sep 17 00:00:00 2001 From: devjun10 Date: Tue, 26 Mar 2024 15:19:08 +0900 Subject: [PATCH 09/26] =?UTF-8?q?chore:=20task=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 359f72b..5d358e3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -92,5 +92,5 @@ task downloadYml { } tasks.named("downloadYml") { - dependsOn downloadYml + dependsOn compileJava } From 2956fb87fcd50b1d99456ab14b54d0f896f88b99 Mon Sep 17 00:00:00 2001 From: devjun10 Date: Tue, 26 Mar 2024 16:12:00 +0900 Subject: [PATCH 10/26] =?UTF-8?q?docs:=20README.md=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 요구사항 정리 --- README.md | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 62642bb..3b58c7d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # 🍃 Spring 서버 만들기 -해당 레포지토리는 자바 웹 프로그래밍 Next Step을 참조해 진행합니다. +해당 레포지토리는 [자바 웹 프로그래밍 Next Step](https://m.yes24.com/Goods/Detail/31869154) 및 [해당 레포지토리](https://github.com/next-step)를 +참조해 진행합니다. > html/css는 [해당 레포지토리](https://github.com/Origogi/DreamCoding-FE-Portfolio-Clone)를 참조했습니다. @@ -11,9 +12,8 @@ ## 📝 공통 요구사항 -1. 전체 미션은 7단계로 나뉘어져 있으며, 각 Step에는 `필수/선택 구현 사항`, `학습 목표`가 주어집니다. +1. 전체 미션은 4단계로 나뉘어져 있으며, 각 Step에는 `필수/선택 구현 사항`, `학습 목표`가 주어집니다. - 다음 단계로 넘어가기 위해서는 최소 1명 이상의 Approve가 필요합니다. - - 필수 스텝은 5단계 까지이며, 나머지 단계는 자율적으로 진행합니다. - 필수 구현사항은 반드시 구현해야 하며, 선택 구현 사항은 구현하지 않아도 됩니다. - 선택 구현사항에는 [선택] 이 명시 돼 있으며, 없다면 필수 구현사항입니다. - 현재 진행중인 Step이 완료되지 않으면, 다음 단계로 넘어갈 수 없습니다. @@ -50,8 +50,6 @@


-
-
## Step2. 로그인 기능을 구현한다. @@ -65,7 +63,6 @@ - [x] 개인 정보 상세 조회 기능을 개발한다. -

@@ -80,8 +77,6 @@


-
-
## Step3. 데이터베이스를 교체한다. @@ -96,7 +91,26 @@ ### 학습 목표 -1. 추상화에 대해 이해한다. +1. 추상화에 대해 학습한다. 2. 데이터베이스 통신 과정에 대해 이해한다. 3. 각 데이터베이스의 특징에 대해 이해한다. 4. 트랜잭션에 대해 학습한다. + +
+
+
+
+ +## Step4. 데이터 전송 방식 변경 + +매 번 정적 리소스를 가져오는 것은 비효율적이기 때문에, 한 페이지에서 일부 데이터만 변경할 수 있도록, 데이터 전송 방식을 변경한다. + +1. 모든 API에 적용할 필요 없으며, 개인정보 수정만 적용한다. +2. [선택] 코드를 리팩토링한다. + +
+
+ +### 학습 목표 + +1. 각 데이터 전송 방식에 대해 학습한다. From 023b4b569fc2cf8a4da1f862649eb844f0f31058 Mon Sep 17 00:00:00 2001 From: devjun10 Date: Tue, 26 Mar 2024 16:12:50 +0900 Subject: [PATCH 11/26] =?UTF-8?q?refactor:=20=EC=9D=B8=ED=84=B0=EC=85=89?= =?UTF-8?q?=ED=84=B0=20=EA=B2=BD=EB=A1=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/app/common/configuration/WebConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/project/server/app/common/configuration/WebConfiguration.java b/app/src/main/java/project/server/app/common/configuration/WebConfiguration.java index 3ffa592..9a1c4b6 100644 --- a/app/src/main/java/project/server/app/common/configuration/WebConfiguration.java +++ b/app/src/main/java/project/server/app/common/configuration/WebConfiguration.java @@ -28,6 +28,6 @@ public WebConfiguration( @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new SessionCheckHandlerInterceptor(validator, loginUseCase)) - .addPathPatterns("/my-info.html", "/my-info"); + .addPathPatterns("/my-info.html", "/my-info", "/api/users"); } } From 424e27705fd1a723a6dd55371837adff17a8f759 Mon Sep 17 00:00:00 2001 From: devjun10 Date: Tue, 26 Mar 2024 16:14:29 +0900 Subject: [PATCH 12/26] =?UTF-8?q?feat:=20Application/Json=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EC=82=AC=EC=9A=A9=EC=9E=90=20=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20API=20=EA=B0=9C=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/core/domain/user/UserRepository.java | 2 + .../user/application/service/UserService.java | 2 +- .../UserPersistenceRepository.java | 19 ++++++++- .../UserInfoUpdateController.java | 6 ++- app/src/main/resources/static/my-info.html | 4 +- .../server/jdbc/core/jdbc/JdbcHelper.java | 11 +++++ .../server/mvc/servlet/HttpServlet.java | 8 +++- .../mvc/servlet/HttpServletRequest.java | 8 +++- .../project/server/mvc/servlet/Request.java | 42 ++++++------------- .../server/mvc/servlet/ServletRequest.java | 6 ++- .../server/mvc/servlet/http/HttpHeaders.java | 13 ++++++ .../server/mvc/servlet/http/HttpMethod.java | 4 ++ .../server/mvc/servlet/http/RequestBody.java | 23 ++++++++++ .../server/mvc/servlet/http/RequestLine.java | 24 +++++++---- .../web/servlet/DispatcherServlet.java | 7 ++++ .../web/servlet/FrameworkServlet.java | 28 ++++++++----- .../web/servlet/MyInfoView.java | 2 +- .../handler/AbstractHandlerMethodMapping.java | 28 +++++++++++-- .../handler/HandlerMappingInitializer.java | 10 ++++- .../method/AbstractHandlerMethodAdapter.java | 3 ++ .../resource/ResourceHttpRequestHandler.java | 4 +- .../server/mvc/tomcat/NioSocketWrapper.java | 17 ++++++-- 22 files changed, 202 insertions(+), 69 deletions(-) diff --git a/app/src/main/java/project/server/app/core/domain/user/UserRepository.java b/app/src/main/java/project/server/app/core/domain/user/UserRepository.java index 600ba44..0ac4bc6 100644 --- a/app/src/main/java/project/server/app/core/domain/user/UserRepository.java +++ b/app/src/main/java/project/server/app/core/domain/user/UserRepository.java @@ -14,6 +14,8 @@ public interface UserRepository { Optional findByUsernameAndPassword(String username, String password); + void update(Long id, String password); + void delete(User user); List findAll(); diff --git a/app/src/main/java/project/server/app/core/web/user/application/service/UserService.java b/app/src/main/java/project/server/app/core/web/user/application/service/UserService.java index 7e765f6..731aed9 100644 --- a/app/src/main/java/project/server/app/core/web/user/application/service/UserService.java +++ b/app/src/main/java/project/server/app/core/web/user/application/service/UserService.java @@ -51,7 +51,7 @@ public void update( ) { User findUser = userRepository.findById(loginUser.getUserId()) .orElseThrow(UserNotFoundException::new); - + userRepository.update(findUser.getId(), password); } @Override diff --git a/app/src/main/java/project/server/app/core/web/user/persistence/UserPersistenceRepository.java b/app/src/main/java/project/server/app/core/web/user/persistence/UserPersistenceRepository.java index 3f73e41..4397c94 100644 --- a/app/src/main/java/project/server/app/core/web/user/persistence/UserPersistenceRepository.java +++ b/app/src/main/java/project/server/app/core/web/user/persistence/UserPersistenceRepository.java @@ -13,11 +13,12 @@ import project.server.app.core.domain.user.User; import project.server.app.core.domain.user.UserRepository; import project.server.jdbc.core.exception.DataAccessException; +import project.server.jdbc.core.jdbc.JdbcHelper; import static project.server.jdbc.core.jdbc.JdbcHelper.insert; import static project.server.jdbc.core.jdbc.JdbcHelper.selectAll; import static project.server.jdbc.core.jdbc.JdbcHelper.selectBy; import static project.server.jdbc.core.jdbc.JdbcHelper.truncate; -import static project.server.jdbc.core.jdbc.JdbcHelper.update; +import static project.server.jdbc.core.jdbc.JdbcHelper.updatePassword; import project.server.jdbc.core.jdbc.JdbcTemplate; import project.server.jdbc.core.jdbc.RowMapper; import project.server.mvc.springframework.annotation.Repository; @@ -88,9 +89,23 @@ public Optional findByUsernameAndPassword( ); } + @Override + public void update( + Long id, + String password + ) { + String sql = updatePassword(User.class, "password"); + jdbcTemplate.queryForObject(sql, pstmt -> { + pstmt.setString(1, password); + pstmt.setLong(2, id); + pstmt.executeUpdate(); + return null; + }); + } + @Override public void delete(User user) { - String sql = update(User.class, "deleted"); + String sql = JdbcHelper.update(User.class, "deleted"); jdbcTemplate.queryForObject(sql, pstmt -> { pstmt.setLong(1, user.getId()); pstmt.executeUpdate(); diff --git a/app/src/main/java/project/server/app/core/web/user/presentation/UserInfoUpdateController.java b/app/src/main/java/project/server/app/core/web/user/presentation/UserInfoUpdateController.java index 0cfe6a3..5505d58 100644 --- a/app/src/main/java/project/server/app/core/web/user/presentation/UserInfoUpdateController.java +++ b/app/src/main/java/project/server/app/core/web/user/presentation/UserInfoUpdateController.java @@ -8,12 +8,13 @@ import project.server.mvc.servlet.HttpServletRequest; import project.server.mvc.servlet.HttpServletResponse; import static project.server.mvc.servlet.http.HttpStatus.OK; -import project.server.mvc.springframework.annotation.Controller; +import project.server.mvc.springframework.annotation.PutMapping; +import project.server.mvc.springframework.annotation.RestController; import project.server.mvc.springframework.web.servlet.Handler; import project.server.mvc.springframework.web.servlet.ModelAndView; @Slf4j -@Controller +@RestController public class UserInfoUpdateController implements Handler { private final UserValidator validator; @@ -31,6 +32,7 @@ public UserInfoUpdateController( } @Override + @PutMapping(path = "/api/users") public ModelAndView process( HttpServletRequest request, HttpServletResponse response diff --git a/app/src/main/resources/static/my-info.html b/app/src/main/resources/static/my-info.html index e1ef339..d3c36b1 100644 --- a/app/src/main/resources/static/my-info.html +++ b/app/src/main/resources/static/my-info.html @@ -68,8 +68,8 @@

회원 상세정보

password: document.getElementById('password').value }; - fetch('URL', { - method: 'POST', + fetch('http://localhost:8086/api/users', { + method: 'PUT', headers: { 'Content-Type': 'application/json', }, diff --git a/jdbc/src/main/java/project/server/jdbc/core/jdbc/JdbcHelper.java b/jdbc/src/main/java/project/server/jdbc/core/jdbc/JdbcHelper.java index a259bfd..c9ed6ad 100644 --- a/jdbc/src/main/java/project/server/jdbc/core/jdbc/JdbcHelper.java +++ b/jdbc/src/main/java/project/server/jdbc/core/jdbc/JdbcHelper.java @@ -46,6 +46,17 @@ public static String update( + " = 'TRUE' WHERE id = ?"; } + public static String updatePassword( + Class clazz, + String fieldName + ) { + return "UPDATE " + + convertCamelToSnake(clazz.getSimpleName()) + + " SET " + + convertCamelToSnake(fieldName) + + " = ? WHERE id = ?"; + } + public static String selectAll(Class clazz) { return "SELECT * FROM " + convertCamelToSnake(clazz.getSimpleName()); diff --git a/mvc/src/main/java/project/server/mvc/servlet/HttpServlet.java b/mvc/src/main/java/project/server/mvc/servlet/HttpServlet.java index 8122d3b..8e7eae7 100644 --- a/mvc/src/main/java/project/server/mvc/servlet/HttpServlet.java +++ b/mvc/src/main/java/project/server/mvc/servlet/HttpServlet.java @@ -14,7 +14,6 @@ public void service( httpServletRequest = (HttpServletRequest) request; httpServletResponse = (HttpServletResponse) response; - service(httpServletRequest, httpServletResponse); } @@ -29,6 +28,10 @@ protected void service( } if (method.isPost()) { doPost(request, response); + return; + } + if (method.isPut()) { + doPut(request, response); } } @@ -37,4 +40,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t protected void doPost(HttpServletRequest request, HttpServletResponse response) throws Exception { } + + protected void doPut(HttpServletRequest request, HttpServletResponse response) throws Exception { + } } diff --git a/mvc/src/main/java/project/server/mvc/servlet/HttpServletRequest.java b/mvc/src/main/java/project/server/mvc/servlet/HttpServletRequest.java index 8c0fca2..b4b3043 100644 --- a/mvc/src/main/java/project/server/mvc/servlet/HttpServletRequest.java +++ b/mvc/src/main/java/project/server/mvc/servlet/HttpServletRequest.java @@ -1,5 +1,6 @@ package project.server.mvc.servlet; +import project.server.mvc.servlet.http.ContentType; import project.server.mvc.servlet.http.Cookies; import project.server.mvc.servlet.http.HttpMethod; import project.server.mvc.servlet.http.RequestBody; @@ -11,7 +12,10 @@ public interface HttpServletRequest extends ServletRequest { String getHost(); @Override - String getContentType(); + ContentType getContentType(); + + @Override + String getContentTypeAsString(); RequestLine getRequestLine(); @@ -28,4 +32,6 @@ public interface HttpServletRequest extends ServletRequest { String getHeader(String key); void setAttribute(String key, Object value); + + boolean isContentType(ContentType contentType); } diff --git a/mvc/src/main/java/project/server/mvc/servlet/Request.java b/mvc/src/main/java/project/server/mvc/servlet/Request.java index 96fa23d..313ec4e 100644 --- a/mvc/src/main/java/project/server/mvc/servlet/Request.java +++ b/mvc/src/main/java/project/server/mvc/servlet/Request.java @@ -1,9 +1,7 @@ package project.server.mvc.servlet; -import java.io.BufferedReader; -import java.io.IOException; -import java.util.ArrayList; import java.util.List; +import project.server.mvc.servlet.http.ContentType; import project.server.mvc.servlet.http.Cookies; import project.server.mvc.servlet.http.HttpHeader; import project.server.mvc.servlet.http.HttpHeaders; @@ -13,7 +11,6 @@ public class Request implements HttpServletRequest { - private static final String EMPTY_STRING = ""; private static final String HOST = "Host"; private final RequestLine requestLine; @@ -30,31 +27,6 @@ public Request( this.requestBody = requestBody; } - private RequestLine parseRequestLine(BufferedReader bufferedReader) throws IOException { - return new RequestLine(bufferedReader.readLine()); - } - - private HttpHeaders parseHttpHeaders(BufferedReader bufferedReader) throws IOException { - List headerLines = new ArrayList<>(); - String headerLine = ""; - - while (!EMPTY_STRING.equals(headerLine = bufferedReader.readLine())) { - headerLines.add(headerLine); - } - return new HttpHeaders(headerLines); - } - - private RequestBody parseRequestBody(BufferedReader bufferedReader) throws IOException { - int contentLength = headers.getContentLength(); - if (contentLength == 0) { - return null; - } - - char[] buffer = new char[contentLength]; - bufferedReader.read(buffer, 0, contentLength); - return new RequestBody(new String(buffer)); - } - @Override public String getHttpVersion() { return requestLine.getHttpVersionAsString(); @@ -72,10 +44,15 @@ public int getContentLength() { } @Override - public String getContentType() { + public ContentType getContentType() { return requestLine.getContentType(); } + @Override + public String getContentTypeAsString() { + return requestLine.getContentTypeAsValue(); + } + @Override public RequestLine getRequestLine() { return requestLine; @@ -119,6 +96,11 @@ public void setAttribute( requestBody.setAttribute(key, value); } + @Override + public boolean isContentType(ContentType contentType) { + return this.headers.isContentType(contentType); + } + @Override public String toString() { return String.format("%s%s%s", requestLine, headers, requestBody); diff --git a/mvc/src/main/java/project/server/mvc/servlet/ServletRequest.java b/mvc/src/main/java/project/server/mvc/servlet/ServletRequest.java index 37ce6d6..30a48ad 100644 --- a/mvc/src/main/java/project/server/mvc/servlet/ServletRequest.java +++ b/mvc/src/main/java/project/server/mvc/servlet/ServletRequest.java @@ -1,7 +1,11 @@ package project.server.mvc.servlet; +import project.server.mvc.servlet.http.ContentType; + public interface ServletRequest { int getContentLength(); - String getContentType(); + ContentType getContentType(); + + String getContentTypeAsString(); } diff --git a/mvc/src/main/java/project/server/mvc/servlet/http/HttpHeaders.java b/mvc/src/main/java/project/server/mvc/servlet/http/HttpHeaders.java index 019a0c2..984c0ff 100644 --- a/mvc/src/main/java/project/server/mvc/servlet/http/HttpHeaders.java +++ b/mvc/src/main/java/project/server/mvc/servlet/http/HttpHeaders.java @@ -6,7 +6,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import static java.util.stream.Collectors.joining; +import static project.server.mvc.servlet.http.ContentType.APPLICATION_JSON; public class HttpHeaders { @@ -128,4 +130,15 @@ private String joiningValues(List headers) { .map(HttpHeader::getValue) .collect(joining(HEADER_JOINING_DELIMITER)) + CARRIAGE_RETURN; } + + public boolean isContentType(ContentType contentType) { + List contentTypeHeader = headers.get(contentType.getValue()); + if (contentTypeHeader == null || contentTypeHeader.isEmpty()) { + return false; + } + Optional findApplicationJson = contentTypeHeader.stream() + .filter(header -> header.getValue().equals(APPLICATION_JSON.getValue())) + .findAny(); + return findApplicationJson.isPresent(); + } } diff --git a/mvc/src/main/java/project/server/mvc/servlet/http/HttpMethod.java b/mvc/src/main/java/project/server/mvc/servlet/http/HttpMethod.java index 3b4d354..623a989 100644 --- a/mvc/src/main/java/project/server/mvc/servlet/http/HttpMethod.java +++ b/mvc/src/main/java/project/server/mvc/servlet/http/HttpMethod.java @@ -32,4 +32,8 @@ public boolean isGet() { public boolean isPost() { return this.equals(POST); } + + public boolean isPut() { + return this.equals(PUT); + } } diff --git a/mvc/src/main/java/project/server/mvc/servlet/http/RequestBody.java b/mvc/src/main/java/project/server/mvc/servlet/http/RequestBody.java index f1ca041..bee5aad 100644 --- a/mvc/src/main/java/project/server/mvc/servlet/http/RequestBody.java +++ b/mvc/src/main/java/project/server/mvc/servlet/http/RequestBody.java @@ -22,6 +22,29 @@ public RequestBody(String body) { this.attributes = parseBody(body); } + public RequestBody(Map attributes) { + this.attributes = attributes; + } + + public static RequestBody createJsonRequestBody(String body) { + if (body == null || body.isBlank()) { + return new RequestBody(new HashMap<>()); + } + + String parsedBody = body.replaceAll("\"", "") + .replaceAll("\\{", "") + .replaceAll("}", ""); + + Map attribute = new HashMap<>(); + + String[] bodyArray = parsedBody.split(","); + for (String eachBody : bodyArray) { + String[] element = eachBody.split(":"); + attribute.put(element[KEY], element[VALUE]); + } + return new RequestBody(attribute); + } + private Map parseBody(String body) { Map attributes = new HashMap<>(); String[] bodyArray = body.split(VALUES_DELIMITER); diff --git a/mvc/src/main/java/project/server/mvc/servlet/http/RequestLine.java b/mvc/src/main/java/project/server/mvc/servlet/http/RequestLine.java index 8adfeb9..e090767 100644 --- a/mvc/src/main/java/project/server/mvc/servlet/http/RequestLine.java +++ b/mvc/src/main/java/project/server/mvc/servlet/http/RequestLine.java @@ -18,7 +18,7 @@ public class RequestLine { private final HttpMethod httpMethod; private final RequestUri requestUri; private final HttpVersion httpVersion; - private String contentType; + private ContentType contentType; public RequestLine(String startLine) { String[] startLineArray = startLine.split(DELIMITER); @@ -64,12 +64,12 @@ private boolean isStaticResource(String[] startLineArray) { return startLineArray.length >= 2; } - private String getContentType(String filePath) { + private ContentType getContentType(String filePath) { String contentType = URLConnection.guessContentTypeFromName(filePath); if (contentType == null) { contentType = OCTET_STREAM; } - return contentType; + return ContentType.findByType(contentType); } public HttpMethod getHttpMethod() { @@ -80,10 +80,22 @@ public String getRequestUri() { return requestUri.url(); } - public String getContentType() { + public ContentType getContentType() { return contentType; } + public String getHttpVersionAsString() { + return httpVersion.getValue(); + } + + public String getContentTypeAsValue() { + return contentType.getValue(); + } + + public boolean isDataFormat(ContentType contentType) { + return this.contentType.equals(contentType); + } + @Override public boolean equals(Object object) { if (this == object) { @@ -98,10 +110,6 @@ public boolean equals(Object object) { && httpVersion == that.httpVersion; } - public String getHttpVersionAsString() { - return httpVersion.getValue(); - } - @Override public int hashCode() { return Objects.hash(httpMethod, httpVersion); diff --git a/mvc/src/main/java/project/server/mvc/springframework/web/servlet/DispatcherServlet.java b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/DispatcherServlet.java index 571caa7..b75e021 100644 --- a/mvc/src/main/java/project/server/mvc/springframework/web/servlet/DispatcherServlet.java +++ b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/DispatcherServlet.java @@ -5,6 +5,7 @@ import project.server.mvc.servlet.HttpServletRequest; import project.server.mvc.servlet.HttpServletResponse; import project.server.mvc.servlet.ServletException; +import static project.server.mvc.servlet.http.ContentType.APPLICATION_JSON; import static project.server.mvc.springframework.context.ApplicationContext.getBean; import project.server.mvc.springframework.web.servlet.mvc.method.RequestMappingHandlerMapping; import project.server.mvc.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; @@ -84,6 +85,12 @@ private void processDispatchResult( HttpServletResponse response, ModelAndView modelAndView ) throws Exception { + if (modelAndView == null) { + if (request.isContentType(APPLICATION_JSON)) { + return; + } + throw new RuntimeException("올바르지 않은 요청입니다."); + } render(modelAndView, request, response); } diff --git a/mvc/src/main/java/project/server/mvc/springframework/web/servlet/FrameworkServlet.java b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/FrameworkServlet.java index c9a06e4..6c8ee17 100644 --- a/mvc/src/main/java/project/server/mvc/springframework/web/servlet/FrameworkServlet.java +++ b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/FrameworkServlet.java @@ -33,14 +33,30 @@ public void doGet( HttpServletRequest request, HttpServletResponse response ) throws Exception { - if (isUnAuthStaticResource(request)) { + if (isStaticResource(request)) { processStaticRequest(request, response); return; } processRequest(request, response); } - private boolean isUnAuthStaticResource(HttpServletRequest request) { + @Override + protected void doPost( + HttpServletRequest request, + HttpServletResponse response + ) throws Exception { + processRequest(request, response); + } + + @Override + protected void doPut( + HttpServletRequest request, + HttpServletResponse response + ) throws Exception { + processRequest(request, response); + } + + private boolean isStaticResource(HttpServletRequest request) { String uri = request.getRequestUri(); String[] parsedUri = uri.split("/"); for (String eachUri : parsedUri) { @@ -80,13 +96,5 @@ protected final void processRequest( doService(request, response); } - @Override - protected void doPost( - HttpServletRequest request, - HttpServletResponse response - ) throws Exception { - processRequest(request, response); - } - protected abstract void doService(HttpServletRequest request, HttpServletResponse response) throws Exception; } diff --git a/mvc/src/main/java/project/server/mvc/springframework/web/servlet/MyInfoView.java b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/MyInfoView.java index 3dfa7a9..448d51c 100644 --- a/mvc/src/main/java/project/server/mvc/springframework/web/servlet/MyInfoView.java +++ b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/MyInfoView.java @@ -51,7 +51,7 @@ private void setResponseHeader( HttpServletResponse response, int lengthOfBodyContent ) { - response.setHeader(CONTENT_TYPE, request.getContentType()); + response.setHeader(CONTENT_TYPE, request.getContentTypeAsString()); response.setHeader(CONTENT_LENGTH, valueOf(lengthOfBodyContent)); } diff --git a/mvc/src/main/java/project/server/mvc/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java index 33e1831..1eb2bbc 100644 --- a/mvc/src/main/java/project/server/mvc/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java +++ b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java @@ -1,6 +1,7 @@ package project.server.mvc.springframework.web.servlet.handler; import java.util.Map; +import java.util.Objects; import project.server.mvc.servlet.HttpServletRequest; import static project.server.mvc.springframework.context.ApplicationContext.getBean; import project.server.mvc.springframework.handler.RequestMappingInfo; @@ -39,9 +40,8 @@ static class MappingRegistry { private final Map registry; public MappingRegistry() { - Object bean = getBean(HandlerMappingInitializer.class); - HandlerMappingInitializer handlerMappingInitializer = (HandlerMappingInitializer) bean; - this.registry = handlerMappingInitializer.getRegistry(); + HandlerMappingInitializer bean = getBean(HandlerMappingInitializer.class); + this.registry = bean.getRegistry(); } public MappingRegistration getMappingRegistration(RequestMappingInfo requestMappingInfo) { @@ -60,5 +60,27 @@ public MappingRegistration(HandlerMethod handlerMethod) { public HandlerMethod getHandlerMethod() { return handlerMethod; } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object == null || getClass() != object.getClass()) { + return false; + } + MappingRegistration that = (MappingRegistration) object; + return handlerMethod.equals(that.handlerMethod); + } + + @Override + public int hashCode() { + return Objects.hash(handlerMethod); + } + + @Override + public String toString() { + return String.format("method: %s", handlerMethod); + } } } diff --git a/mvc/src/main/java/project/server/mvc/springframework/web/servlet/handler/HandlerMappingInitializer.java b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/handler/HandlerMappingInitializer.java index 3d15b26..004d631 100644 --- a/mvc/src/main/java/project/server/mvc/springframework/web/servlet/handler/HandlerMappingInitializer.java +++ b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/handler/HandlerMappingInitializer.java @@ -1,6 +1,7 @@ package project.server.mvc.springframework.web.servlet.handler; import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -10,6 +11,7 @@ import project.server.mvc.springframework.annotation.GetMapping; import project.server.mvc.springframework.annotation.PostMapping; import project.server.mvc.springframework.annotation.PutMapping; +import project.server.mvc.springframework.annotation.RestController; import static project.server.mvc.springframework.context.ApplicationContext.findByAnnotation; import project.server.mvc.springframework.handler.RequestMappingInfo; import project.server.mvc.springframework.web.method.HandlerMethod; @@ -20,10 +22,16 @@ public class HandlerMappingInitializer { private Map registry = new HashMap<>(); public HandlerMappingInitializer() { - List handlers = findByAnnotation(Controller.class).stream() + List handlers = new ArrayList<>(); + List restController = findByAnnotation(RestController.class).stream() + .map(bean -> (Handler) bean) + .toList(); + List controller = findByAnnotation(Controller.class).stream() .map(bean -> (Handler) bean) .toList(); + handlers.addAll(controller); + handlers.addAll(restController); for (Handler handler : handlers) { registerHandlerMethod(handler); } diff --git a/mvc/src/main/java/project/server/mvc/springframework/web/servlet/mvc/method/AbstractHandlerMethodAdapter.java b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/mvc/method/AbstractHandlerMethodAdapter.java index 53011a5..79cbb32 100644 --- a/mvc/src/main/java/project/server/mvc/springframework/web/servlet/mvc/method/AbstractHandlerMethodAdapter.java +++ b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/mvc/method/AbstractHandlerMethodAdapter.java @@ -39,6 +39,9 @@ private ModelAndView invokeHandlerMethod( log.info("args: {}", args); Object result = method.invoke(instance, args); log.info("result: {}", result); + if (result == null) { + return null; + } return (ModelAndView) result; } } diff --git a/mvc/src/main/java/project/server/mvc/springframework/web/servlet/resource/ResourceHttpRequestHandler.java b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/resource/ResourceHttpRequestHandler.java index 9738934..d31669a 100644 --- a/mvc/src/main/java/project/server/mvc/springframework/web/servlet/resource/ResourceHttpRequestHandler.java +++ b/mvc/src/main/java/project/server/mvc/springframework/web/servlet/resource/ResourceHttpRequestHandler.java @@ -3,8 +3,6 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; -import java.nio.ByteBuffer; -import java.nio.channels.SocketChannel; import project.server.mvc.servlet.HttpServletRequest; import project.server.mvc.servlet.HttpServletResponse; import static project.server.mvc.servlet.http.HttpStatus.NOT_FOUND; @@ -91,7 +89,7 @@ private void setResponseHeader( int lengthOfBodyContent ) throws IOException { response.getHttpHeaderLine(); - response.setHeader(CONTENT_TYPE, request.getContentType()); + response.setHeader(CONTENT_TYPE, request.getContentTypeAsString()); response.setHeader(CONTENT_LENGTH, String.valueOf(lengthOfBodyContent)); String header = response.getHttpHeaderLine(); diff --git a/mvc/src/main/java/project/server/mvc/tomcat/NioSocketWrapper.java b/mvc/src/main/java/project/server/mvc/tomcat/NioSocketWrapper.java index 2dad812..3ca6b76 100644 --- a/mvc/src/main/java/project/server/mvc/tomcat/NioSocketWrapper.java +++ b/mvc/src/main/java/project/server/mvc/tomcat/NioSocketWrapper.java @@ -11,8 +11,10 @@ import project.server.mvc.servlet.HttpServletResponse; import project.server.mvc.servlet.Request; import project.server.mvc.servlet.Response; +import static project.server.mvc.servlet.http.ContentType.APPLICATION_JSON; import project.server.mvc.servlet.http.HttpHeaders; import project.server.mvc.servlet.http.RequestBody; +import static project.server.mvc.servlet.http.RequestBody.createJsonRequestBody; import project.server.mvc.servlet.http.RequestLine; import static project.server.mvc.springframework.context.ApplicationContext.getBean; import project.server.mvc.springframework.web.servlet.DispatcherServlet; @@ -56,7 +58,6 @@ public void run() { try (SocketChannel channel = this.socketChannel) { HttpServletRequest request = createHttpServletRequest(lines, headerLines, requestBody); HttpServletResponse response = new Response(channel); - this.response = response; dispatcherServlet.service(request, response); @@ -90,9 +91,19 @@ private HttpServletRequest createHttpServletRequest( List headerLines, String requestBody ) { + RequestLine requestLine = new RequestLine(lines[START_LINE]); + HttpHeaders httpHeaders = new HttpHeaders(headerLines); + if (httpHeaders.isContentType(APPLICATION_JSON)) { + RequestBody body = createJsonRequestBody(requestBody); + return new Request( + requestLine, + httpHeaders, + body + ); + } return new Request( - new RequestLine(lines[START_LINE]), - new HttpHeaders(headerLines), + requestLine, + httpHeaders, new RequestBody(requestBody) ); } From fcef502fd43207f8c6da4eebd557e19591798b83 Mon Sep 17 00:00:00 2001 From: devjun10 Date: Tue, 26 Mar 2024 16:15:06 +0900 Subject: [PATCH 13/26] =?UTF-8?q?refactor:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20log=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/app/core/web/user/presentation/LoginController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/project/server/app/core/web/user/presentation/LoginController.java b/app/src/main/java/project/server/app/core/web/user/presentation/LoginController.java index 9c8e587..60cbd3f 100644 --- a/app/src/main/java/project/server/app/core/web/user/presentation/LoginController.java +++ b/app/src/main/java/project/server/app/core/web/user/presentation/LoginController.java @@ -38,7 +38,6 @@ public ModelAndView process( ) { String username = (String) request.getAttribute("username"); String password = (String) request.getAttribute("password"); - log.info("username: {}, password: {}", username, password); validator.validateLoginInfo(username, password); Session session = userLoginUseCase.login(username, password); From 49547fac6c1950c665c77e5be25f0c5234cd8e80 Mon Sep 17 00:00:00 2001 From: devjun10 Date: Tue, 26 Mar 2024 16:15:42 +0900 Subject: [PATCH 14/26] =?UTF-8?q?test:=20@AfterEach=20->=20@BeforeEach?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20-=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A0=84=20=EB=AA=A8=EB=93=A0=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=EB=A5=BC=20=EC=A0=9C=EA=B1=B0=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/app/test/integrationtest/IntegrationTestBase.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/test/java/project/server/app/test/integrationtest/IntegrationTestBase.java b/app/src/test/java/project/server/app/test/integrationtest/IntegrationTestBase.java index ec7c126..1b19f2d 100644 --- a/app/src/test/java/project/server/app/test/integrationtest/IntegrationTestBase.java +++ b/app/src/test/java/project/server/app/test/integrationtest/IntegrationTestBase.java @@ -1,6 +1,7 @@ package project.server.app.test.integrationtest; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import project.server.app.core.domain.user.UserRepository; import project.server.app.core.web.user.persistence.UserPersistenceRepository; import project.server.mvc.springframework.context.ApplicationContext; @@ -21,7 +22,7 @@ protected IntegrationTestBase() { } } - @AfterEach + @BeforeEach void setUp() { userRepository.clear(); } From 4350b67682676de538e4bd245d77af84268d78ef Mon Sep 17 00:00:00 2001 From: devjun10 Date: Tue, 26 Mar 2024 16:16:25 +0900 Subject: [PATCH 15/26] =?UTF-8?q?feat:=20Application/Json=EC=9D=84=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=ED=95=98=EA=B8=B0=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../springframework/annotation/RestController.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 mvc/src/main/java/project/server/mvc/springframework/annotation/RestController.java diff --git a/mvc/src/main/java/project/server/mvc/springframework/annotation/RestController.java b/mvc/src/main/java/project/server/mvc/springframework/annotation/RestController.java new file mode 100644 index 0000000..dc99738 --- /dev/null +++ b/mvc/src/main/java/project/server/mvc/springframework/annotation/RestController.java @@ -0,0 +1,14 @@ +package project.server.mvc.springframework.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Component +@Documented +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface RestController { +} From 045c6104ba27eba946d6221838fc8d6ef86ff7cc Mon Sep 17 00:00:00 2001 From: devjun10 Date: Tue, 26 Mar 2024 16:16:59 +0900 Subject: [PATCH 16/26] =?UTF-8?q?feat:=20=EC=9E=84=EC=8B=9C=20=EC=BB=A4?= =?UTF-8?q?=EB=B0=8B=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mvc/AbstractJsonHttpMessageConverter.java | 4 ++ .../server/mvc/HttpMessageConverter.java | 12 ++++++ .../server/mvc/JsonbHttpMessageConverter.java | 4 ++ .../server/mvc/servlet/http/ContentType.java | 40 +++++++++++++++++++ .../mvc/tomcat/AbstractJsseEndpoint.java | 13 ++++++ 5 files changed, 73 insertions(+) create mode 100644 mvc/src/main/java/project/server/mvc/AbstractJsonHttpMessageConverter.java create mode 100644 mvc/src/main/java/project/server/mvc/HttpMessageConverter.java create mode 100644 mvc/src/main/java/project/server/mvc/JsonbHttpMessageConverter.java create mode 100644 mvc/src/main/java/project/server/mvc/servlet/http/ContentType.java create mode 100644 mvc/src/main/java/project/server/mvc/tomcat/AbstractJsseEndpoint.java diff --git a/mvc/src/main/java/project/server/mvc/AbstractJsonHttpMessageConverter.java b/mvc/src/main/java/project/server/mvc/AbstractJsonHttpMessageConverter.java new file mode 100644 index 0000000..a59a274 --- /dev/null +++ b/mvc/src/main/java/project/server/mvc/AbstractJsonHttpMessageConverter.java @@ -0,0 +1,4 @@ +package project.server.mvc; + +public abstract class AbstractJsonHttpMessageConverter { +} diff --git a/mvc/src/main/java/project/server/mvc/HttpMessageConverter.java b/mvc/src/main/java/project/server/mvc/HttpMessageConverter.java new file mode 100644 index 0000000..5ee203b --- /dev/null +++ b/mvc/src/main/java/project/server/mvc/HttpMessageConverter.java @@ -0,0 +1,12 @@ +package project.server.mvc; + +import java.io.IOException; +import project.server.mvc.servlet.HttpServletRequest; +import project.server.mvc.servlet.HttpServletResponse; +import project.server.mvc.servlet.http.ContentType; + +public interface HttpMessageConverter { + boolean canWrite(ContentType contentType); + + void write(HttpServletRequest request, HttpServletResponse response) throws IOException; +} diff --git a/mvc/src/main/java/project/server/mvc/JsonbHttpMessageConverter.java b/mvc/src/main/java/project/server/mvc/JsonbHttpMessageConverter.java new file mode 100644 index 0000000..ca5d7cb --- /dev/null +++ b/mvc/src/main/java/project/server/mvc/JsonbHttpMessageConverter.java @@ -0,0 +1,4 @@ +package project.server.mvc; + +public class JsonbHttpMessageConverter extends AbstractJsonHttpMessageConverter { +} diff --git a/mvc/src/main/java/project/server/mvc/servlet/http/ContentType.java b/mvc/src/main/java/project/server/mvc/servlet/http/ContentType.java new file mode 100644 index 0000000..ed23da2 --- /dev/null +++ b/mvc/src/main/java/project/server/mvc/servlet/http/ContentType.java @@ -0,0 +1,40 @@ +package project.server.mvc.servlet.http; + +import java.util.Arrays; +import java.util.function.Predicate; + +public enum ContentType { + TEXT_HTML("html", "text/html"), + TEXT_CSS("css", "text/css"), + TEXT_JS("js", "text/javascript"), + IMAGE_JPEG("jpeg", "image/jpeg"), + IMAGE_PNG("png", "image/png"), + APPLICATION_JSON("", "application/json"), + APPLICATION_OCTET_STREAM("*", "application/octet-stream"); + + private final String type; + private final String value; + + ContentType( + String type, + String value + ) { + this.type = type; + this.value = value; + } + + public static ContentType findByType(String type) { + return Arrays.stream(values()) + .filter(equals(type)) + .findAny() + .orElseGet(() -> APPLICATION_OCTET_STREAM); + } + + private static Predicate equals(String type) { + return contentType -> contentType.value.equals(type); + } + + public String getValue() { + return value; + } +} diff --git a/mvc/src/main/java/project/server/mvc/tomcat/AbstractJsseEndpoint.java b/mvc/src/main/java/project/server/mvc/tomcat/AbstractJsseEndpoint.java new file mode 100644 index 0000000..d8799a5 --- /dev/null +++ b/mvc/src/main/java/project/server/mvc/tomcat/AbstractJsseEndpoint.java @@ -0,0 +1,13 @@ +package project.server.mvc.tomcat; + +import java.util.concurrent.ExecutorService; + +public abstract class AbstractJsseEndpoint extends AbstractEndpoint { + + public AbstractJsseEndpoint(ExecutorService executorService) { + super(executorService); + } + + @Override + abstract protected boolean setSocketOptions(U socket); +} From 53aa75f35432db90c92ecfee1d8a754ead9ff0eb Mon Sep 17 00:00:00 2001 From: devjun10 Date: Tue, 26 Mar 2024 17:31:51 +0900 Subject: [PATCH 17/26] =?UTF-8?q?refactor:=20=EA=B8=B0=EB=B3=B8=20?= =?UTF-8?q?=ED=8F=AC=ED=8A=B8=208080=EC=9C=BC=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/resources/static/index.html | 4 ++-- app/src/main/resources/static/my-info.html | 2 +- app/src/main/resources/static/sign-in.html | 13 ++++--------- .../java/project/server/mvc/tomcat/PortFinder.java | 2 +- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/app/src/main/resources/static/index.html b/app/src/main/resources/static/index.html index c879883..d4ff44a 100644 --- a/app/src/main/resources/static/index.html +++ b/app/src/main/resources/static/index.html @@ -138,14 +138,14 @@

Infra

console.error('Server responded with non-OK status'); localStorage.clear(); document.cookie = 'sessionId=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;'; - window.location.href = "http://localhost:8086/"; + window.location.href = "http://localhost:8080/"; } }) .catch((error) => { console.error('Error:', error); localStorage.clear(); document.cookie = 'sessionId=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;'; - window.location.href = "http://localhost:8086/"; + window.location.href = "http://localhost:8080/"; }); }); } diff --git a/app/src/main/resources/static/my-info.html b/app/src/main/resources/static/my-info.html index d3c36b1..c6f96ee 100644 --- a/app/src/main/resources/static/my-info.html +++ b/app/src/main/resources/static/my-info.html @@ -68,7 +68,7 @@

회원 상세정보

password: document.getElementById('password').value }; - fetch('http://localhost:8086/api/users', { + fetch('http://localhost:8080/api/users', { method: 'PUT', headers: { 'Content-Type': 'application/json', diff --git a/app/src/main/resources/static/sign-in.html b/app/src/main/resources/static/sign-in.html index dfd1138..b17f777 100644 --- a/app/src/main/resources/static/sign-in.html +++ b/app/src/main/resources/static/sign-in.html @@ -98,12 +98,12 @@

If you're first-com

document.querySelector('.sign-up-container form').addEventListener('submit', function (event) { event.preventDefault(); var xhr = new XMLHttpRequest(); - xhr.open("POST", "http://localhost:8086/sign-up", true); + xhr.open("POST", "http://localhost:8080/sign-up", true); xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.onload = function () { if (xhr.status === 200 || xhr.status === 301) { alert("회원가입이 완료되었습니다."); - window.location.href = "http://localhost:8086/sign-in.html"; + window.location.href = "http://localhost:8080/sign-in.html"; } else { alert("회원가입에 실패했습니다."); } @@ -121,17 +121,12 @@

If you're first-com

event.preventDefault(); var xhr = new XMLHttpRequest(); - xhr.open("POST", "http://localhost:8086/sign-in", true); + xhr.open("POST", "http://localhost:8080/sign-in", true); xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.onload = function () { if (xhr.status === 200 || xhr.status === 301) { - // var logined = xhr.getResponseHeader("logined"); - // if (logined) { localStorage.setItem("logined", true); - window.location.href = "http://localhost:8086/"; - // } else { - // alert("로그인에 실패했습니다."); - // } + window.location.href = "http://localhost:8080/"; } else { alert("Error: " + xhr.status); } diff --git a/mvc/src/main/java/project/server/mvc/tomcat/PortFinder.java b/mvc/src/main/java/project/server/mvc/tomcat/PortFinder.java index 0f779e3..6c0cc59 100644 --- a/mvc/src/main/java/project/server/mvc/tomcat/PortFinder.java +++ b/mvc/src/main/java/project/server/mvc/tomcat/PortFinder.java @@ -8,7 +8,7 @@ private PortFinder() { private static final int MIN_PORT_NUMBER = 1; private static final int MAX_PORT_NUMBER = 65535; - private static final int DEFAULT_PORT = 8086; + private static final int DEFAULT_PORT = 8080; public static int findPort(String[] args) { if (args == null || args.length == 0) { From 947a8c743ba13875779b43929453c639ae11348c Mon Sep 17 00:00:00 2001 From: devjun10 Date: Tue, 26 Mar 2024 17:34:41 +0900 Subject: [PATCH 18/26] =?UTF-8?q?docs:=20README.md=20=EB=82=B4=EC=9A=A9=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20-=20=EC=8B=A4=ED=96=89=20=EB=B0=A9?= =?UTF-8?q?=EB=B2=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3b58c7d..6def6b9 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,36 @@

+## 📝 프로그램 실행 + +application.yml 파일/설정 추가 후 데이터베이스 설정 값 등록. + +````yaml + spring: + datasource: + driver-class-name: ${DRIVER_CLASS_NAME} + url: ${URL} + username: ${USERNAME} + password: ${PASSWORD} +```` + +
+
+
+
+ +빌드 후 실행. + +```shell +./gradlew build +./gradlew bootJar +``` + +
+
+
+
+ ## 📝 공통 요구사항 1. 전체 미션은 4단계로 나뉘어져 있으며, 각 Step에는 `필수/선택 구현 사항`, `학습 목표`가 주어집니다. @@ -101,7 +131,7 @@

-## Step4. 데이터 전송 방식 변경 +## Step4. 데이터 전송 방식을 일부 변경한다. 매 번 정적 리소스를 가져오는 것은 비효율적이기 때문에, 한 페이지에서 일부 데이터만 변경할 수 있도록, 데이터 전송 방식을 변경한다. From eb1956884608450199ce03944761ab5eb3c64ad3 Mon Sep 17 00:00:00 2001 From: devjun10 Date: Tue, 26 Mar 2024 17:40:18 +0900 Subject: [PATCH 19/26] =?UTF-8?q?test:=20=EA=B9=A8=EC=A7=84=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EB=B3=B5=EA=B5=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/test/unittest/user/UserValidatorUnitTest.java | 9 +++++++++ .../mvc/test/unittest/http/RequestLineUnitTest.java | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/src/test/java/project/server/app/test/unittest/user/UserValidatorUnitTest.java b/app/src/test/java/project/server/app/test/unittest/user/UserValidatorUnitTest.java index 9559bac..62c19e7 100644 --- a/app/src/test/java/project/server/app/test/unittest/user/UserValidatorUnitTest.java +++ b/app/src/test/java/project/server/app/test/unittest/user/UserValidatorUnitTest.java @@ -72,4 +72,13 @@ void sessionIdValidateTest() { HttpServletResponse response = new Response(); assertDoesNotThrow(() -> validator.validateSessionId(1L, response)); } + + @Test + @DisplayName("세션이 존재하지 않으면 UnAuthorizedException이 발생한다.") + void sessionNullTest() { + HttpServletResponse response = new Response(); + assertThatThrownBy(() -> validator.validateSession(null, response)) + .isInstanceOf(RuntimeException.class) + .isExactlyInstanceOf(UnAuthorizedException.class); + } } diff --git a/mvc/src/test/java/project/server/mvc/test/unittest/http/RequestLineUnitTest.java b/mvc/src/test/java/project/server/mvc/test/unittest/http/RequestLineUnitTest.java index 1987071..6e60159 100644 --- a/mvc/src/test/java/project/server/mvc/test/unittest/http/RequestLineUnitTest.java +++ b/mvc/src/test/java/project/server/mvc/test/unittest/http/RequestLineUnitTest.java @@ -25,7 +25,7 @@ void requestLineParseTest() throws IOException { assertAll( () -> assertEquals(GET, requestLine.getHttpMethod()), () -> assertEquals("/index.html", requestLine.getRequestUri()), - () -> assertEquals("text/html", requestLine.getContentType()), + () -> assertEquals("text/html", requestLine.getContentTypeAsValue()), () -> assertEquals(HTTP_1_1.getValue(), "HTTP/1.1") ); } From 9959627bf5dd32ff6ea3fc8dcd78bc357e3892a2 Mon Sep 17 00:00:00 2001 From: devjun10 Date: Tue, 26 Mar 2024 17:40:32 +0900 Subject: [PATCH 20/26] =?UTF-8?q?refactor:=20CheckStyle,=20PMD=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/app/core/web/user/persistence/UserRowMapper.java | 4 ++-- .../mvc/springframework/context/ApplicationContext.java | 4 ++-- .../java/project/server/mvc/tomcat/AbstractJsseEndpoint.java | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/project/server/app/core/web/user/persistence/UserRowMapper.java b/app/src/main/java/project/server/app/core/web/user/persistence/UserRowMapper.java index 83b6361..4364f8a 100644 --- a/app/src/main/java/project/server/app/core/web/user/persistence/UserRowMapper.java +++ b/app/src/main/java/project/server/app/core/web/user/persistence/UserRowMapper.java @@ -24,8 +24,8 @@ public User mapRow(ResultSet rs) throws SQLException { String username = rs.getString(USERNAME); String password = rs.getString(PASSWORD); LocalDateTime createdAt = rs.getTimestamp(CREATED_AT).toLocalDateTime(); - LocalDateTime lastModifiedAt = rs.getTimestamp(LAST_MODIFIED_AT) != null ? - rs.getTimestamp(LAST_MODIFIED_AT).toLocalDateTime() : null; + LocalDateTime lastModifiedAt = rs.getTimestamp(LAST_MODIFIED_AT) != null + ? rs.getTimestamp(LAST_MODIFIED_AT).toLocalDateTime() : null; Deleted deleted = Deleted.valueOf(rs.getString(DELETED)); return new User(id, username, password, createdAt, lastModifiedAt, deleted); } diff --git a/mvc/src/main/java/project/server/mvc/springframework/context/ApplicationContext.java b/mvc/src/main/java/project/server/mvc/springframework/context/ApplicationContext.java index 3e8a2a6..4a2cdaa 100644 --- a/mvc/src/main/java/project/server/mvc/springframework/context/ApplicationContext.java +++ b/mvc/src/main/java/project/server/mvc/springframework/context/ApplicationContext.java @@ -192,8 +192,8 @@ public static T getBean(Class clazz) { return clazz.cast(dependencyInjectedBeans.get(clazz)); } - public static void register(T t) { - dependencyInjectedBeans.put(t.getClass(), t); + public static void register(T type) { + dependencyInjectedBeans.put(type.getClass(), type); } public static T getBean(String beanName) { diff --git a/mvc/src/main/java/project/server/mvc/tomcat/AbstractJsseEndpoint.java b/mvc/src/main/java/project/server/mvc/tomcat/AbstractJsseEndpoint.java index d8799a5..b4c7b92 100644 --- a/mvc/src/main/java/project/server/mvc/tomcat/AbstractJsseEndpoint.java +++ b/mvc/src/main/java/project/server/mvc/tomcat/AbstractJsseEndpoint.java @@ -9,5 +9,5 @@ public AbstractJsseEndpoint(ExecutorService executorService) { } @Override - abstract protected boolean setSocketOptions(U socket); + protected abstract boolean setSocketOptions(U socket); } From 8b65cdbd21fe1948ad2d7f0ee28eaefb695047fc Mon Sep 17 00:00:00 2001 From: devjun10 Date: Tue, 26 Mar 2024 17:42:14 +0900 Subject: [PATCH 21/26] =?UTF-8?q?docs:=20README.md=20=EB=82=B4=EC=9A=A9=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20-=20=EA=B0=9C=ED=96=89=20=ED=95=9C=20?= =?UTF-8?q?=EC=B9=B8=EC=94=A9=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 6def6b9..20de3e9 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

-## 📝 프로그램 실행 +## 💻 프로그램 실행 application.yml 파일/설정 추가 후 데이터베이스 설정 값 등록. @@ -31,7 +31,10 @@ application.yml 파일/설정 추가 후 데이터베이스 설정 값 등록. 빌드 후 실행. ```shell +# 빌드 ./gradlew build + +# 실행 ./gradlew bootJar ``` @@ -57,8 +60,6 @@ application.yml 파일/설정 추가 후 데이터베이스 설정 값 등록.


-
-
## Step1. 사용자 정보를 저장한다. @@ -69,7 +70,6 @@ application.yml 파일/설정 추가 후 데이터베이스 설정 값 등록. - [x] 데이터베이스는 애플리케이션 내부 인메모리 데이터베이스를 사용한다. - [x] 어떤 정보를 저장할 지는 자유롭게 정의한다. -

### 학습 목표 @@ -93,7 +93,6 @@ application.yml 파일/설정 추가 후 데이터베이스 설정 값 등록. - [x] 개인 정보 상세 조회 기능을 개발한다. -

### 학습 목표 @@ -116,7 +115,6 @@ application.yml 파일/설정 추가 후 데이터베이스 설정 값 등록. - RDB, Redis 등 2. JDBC 템플릿을 구현한다. -

### 학습 목표 @@ -138,7 +136,6 @@ application.yml 파일/설정 추가 후 데이터베이스 설정 값 등록. 1. 모든 API에 적용할 필요 없으며, 개인정보 수정만 적용한다. 2. [선택] 코드를 리팩토링한다. -

### 학습 목표 From fec3b99ef7b313420c8fcf6321cbce123c944b72 Mon Sep 17 00:00:00 2001 From: devjun10 Date: Wed, 3 Apr 2024 01:35:06 +0900 Subject: [PATCH 22/26] =?UTF-8?q?refactor:=20=EB=AF=B8=20=EB=B0=98?= =?UTF-8?q?=EC=98=81=EC=82=AC=ED=95=AD=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/project/server/mvc/servlet/http/ContentType.java | 2 +- .../java/project/server/mvc/servlet/http/HttpHeaders.java | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/mvc/src/main/java/project/server/mvc/servlet/http/ContentType.java b/mvc/src/main/java/project/server/mvc/servlet/http/ContentType.java index ed23da2..e98dbf8 100644 --- a/mvc/src/main/java/project/server/mvc/servlet/http/ContentType.java +++ b/mvc/src/main/java/project/server/mvc/servlet/http/ContentType.java @@ -9,7 +9,7 @@ public enum ContentType { TEXT_JS("js", "text/javascript"), IMAGE_JPEG("jpeg", "image/jpeg"), IMAGE_PNG("png", "image/png"), - APPLICATION_JSON("", "application/json"), + APPLICATION_JSON("application/json", "application/json"), APPLICATION_OCTET_STREAM("*", "application/octet-stream"); private final String type; diff --git a/mvc/src/main/java/project/server/mvc/servlet/http/HttpHeaders.java b/mvc/src/main/java/project/server/mvc/servlet/http/HttpHeaders.java index 984c0ff..e0d41c8 100644 --- a/mvc/src/main/java/project/server/mvc/servlet/http/HttpHeaders.java +++ b/mvc/src/main/java/project/server/mvc/servlet/http/HttpHeaders.java @@ -8,7 +8,6 @@ import java.util.Map; import java.util.Optional; import static java.util.stream.Collectors.joining; -import static project.server.mvc.servlet.http.ContentType.APPLICATION_JSON; public class HttpHeaders { @@ -132,12 +131,12 @@ private String joiningValues(List headers) { } public boolean isContentType(ContentType contentType) { - List contentTypeHeader = headers.get(contentType.getValue()); + List contentTypeHeader = headers.get("Content-Type"); if (contentTypeHeader == null || contentTypeHeader.isEmpty()) { return false; } Optional findApplicationJson = contentTypeHeader.stream() - .filter(header -> header.getValue().equals(APPLICATION_JSON.getValue())) + .filter(header -> header.getValue().equals(contentType.getValue())) .findAny(); return findApplicationJson.isPresent(); } From 431336514858564c37b8ac6e6e234183c080b78b Mon Sep 17 00:00:00 2001 From: devjun10 Date: Mon, 8 Apr 2024 05:45:14 +0900 Subject: [PATCH 23/26] =?UTF-8?q?chore:=20schema=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/resources/schema.sql | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 app/src/main/resources/schema.sql diff --git a/app/src/main/resources/schema.sql b/app/src/main/resources/schema.sql new file mode 100644 index 0000000..ccf0c3a --- /dev/null +++ b/app/src/main/resources/schema.sql @@ -0,0 +1,9 @@ +CREATE TABLE user +( + id BIGINT PRIMARY KEY NOT NULL AUTO_INCREMENT COMMENT 'PK', + username VARCHAR(40) NOT NULL COMMENT '사용자 이름', + password VARCHAR(255) NOT NULL COMMENT '패스워드', + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '생성일', + last_modified_at TIMESTAMP NULL DEFAULT NULL COMMENT '최종 수정일', + deleted VARCHAR(10) NOT NULL COMMENT '삭제 유무' +) engine 'InnoDB'; From c38b3d474baeaa8e3c9664ac002d3cc6cac5f70d Mon Sep 17 00:00:00 2001 From: devjun10 Date: Mon, 8 Apr 2024 05:47:12 +0900 Subject: [PATCH 24/26] =?UTF-8?q?docs:=20README.md=20=EB=82=B4=EC=9A=A9=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 20de3e9..7836344 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ ## 💻 프로그램 실행 -application.yml 파일/설정 추가 후 데이터베이스 설정 값 등록. +app 모듈 application.yml 파일/설정 추가 후 데이터베이스 설정 값 등록. test 디렉토리에도 추가. ````yaml spring: @@ -28,6 +28,25 @@ application.yml 파일/설정 추가 후 데이터베이스 설정 값 등록.

+데이터베이스 스키마 생성. app 모듈의 resource 패키지 참조. + +```sql +CREATE TABLE user +( + id BIGINT PRIMARY KEY NOT NULL AUTO_INCREMENT COMMENT 'PK', + username VARCHAR(40) NOT NULL COMMENT '사용자 이름', + password VARCHAR(255) NOT NULL COMMENT '패스워드', + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '생성일', + last_modified_at TIMESTAMP NULL DEFAULT NULL COMMENT '최종 수정일', + deleted VARCHAR(10) NOT NULL COMMENT '삭제 유무' +) engine 'InnoDB'; +``` + +
+
+
+
+ 빌드 후 실행. ```shell From a00907a8b85c68024ff8303f2b62d7b95f4a482f Mon Sep 17 00:00:00 2001 From: devjun10 Date: Fri, 10 May 2024 21:32:11 +0900 Subject: [PATCH 25/26] =?UTF-8?q?refactor:=20=EC=9D=BC=EA=B8=89=20?= =?UTF-8?q?=EC=BB=AC=EB=A0=89=EC=85=98=EC=97=90=EC=84=9C=20=EC=BF=A0?= =?UTF-8?q?=ED=82=A4=EB=A5=BC=20=EC=B6=94=EC=B6=9C=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/project/server/app/common/utils/HeaderUtils.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/project/server/app/common/utils/HeaderUtils.java b/app/src/main/java/project/server/app/common/utils/HeaderUtils.java index 779e571..93fcef9 100644 --- a/app/src/main/java/project/server/app/common/utils/HeaderUtils.java +++ b/app/src/main/java/project/server/app/common/utils/HeaderUtils.java @@ -1,8 +1,6 @@ package project.server.app.common.utils; -import java.util.Map; -import project.server.mvc.servlet.http.Cookie; -import project.server.mvc.servlet.http.Cookies; +import project.server.mvc.servlet.http.*; public final class HeaderUtils { @@ -13,8 +11,7 @@ private HeaderUtils() { } public static Long getSessionId(Cookies cookies) { - Map cookiesMap = cookies.getCookiesMap(); - Cookie findCookie = cookiesMap.get(SESSION_ID); + Cookie findCookie = cookies.get(SESSION_ID); if (findCookie != null) { return extractSessionId(findCookie); } From 1fccab2b239e5ebce674c33c9f28f9943b8b6c12 Mon Sep 17 00:00:00 2001 From: devjun10 Date: Sat, 22 Jun 2024 17:40:18 +0900 Subject: [PATCH 26/26] =?UTF-8?q?docs:=20README.md=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?-=20Step=20=EA=B4=84=ED=98=B8=20=EC=B6=94=EA=B0=80=20-=20Step4?= =?UTF-8?q?=20=ED=95=99=EC=8A=B5=20=EB=AA=A9=ED=91=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 7836344..15ec987 100644 --- a/README.md +++ b/README.md @@ -47,13 +47,10 @@ CREATE TABLE user

-빌드 후 실행. +빌드 후 프로그램을 실행합니다. ```shell -# 빌드 ./gradlew build - -# 실행 ./gradlew bootJar ``` @@ -80,7 +77,7 @@ CREATE TABLE user

-## Step1. 사용자 정보를 저장한다. +## [Step1] 사용자 정보를 저장한다. 네트워크로 부터 전송된 데이터를 파싱해 사용자 정보를 저장한다. @@ -100,7 +97,7 @@ CREATE TABLE user

-## Step2. 로그인 기능을 구현한다. +## [Step2] 로그인 기능을 구현한다. 사용자 정보를 바탕으로 로그인 기능을 구현한다. @@ -126,7 +123,7 @@ CREATE TABLE user

-## Step3. 데이터베이스를 교체한다. +## [Step3] 데이터베이스를 교체한다. 애플리케이션 내부에 저장하던 데이터를 외부 데이터베이스에 저장한다. @@ -148,7 +145,7 @@ CREATE TABLE user

-## Step4. 데이터 전송 방식을 일부 변경한다. +## [Step4] 데이터 전송 방식을 일부 변경한다. 매 번 정적 리소스를 가져오는 것은 비효율적이기 때문에, 한 페이지에서 일부 데이터만 변경할 수 있도록, 데이터 전송 방식을 변경한다. @@ -160,3 +157,4 @@ CREATE TABLE user ### 학습 목표 1. 각 데이터 전송 방식에 대해 학습한다. +2. 정적 리소스를 가져오는 비용을 최적화 하는 방법에 대해 학습한다.