├── .gitignore ├── 2.1-Backend_Development_Environment_Configuration └── demo │ ├── .gitignore │ ├── build.gradle │ ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── demo │ │ │ ├── DemoApplication.java │ │ │ └── DemoModel.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── com │ └── example │ └── demo │ └── DemoApplicationTests.java ├── 2.2-Backend_Service_Architecture └── demo │ ├── .gitignore │ ├── build.gradle │ ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── demo │ │ │ ├── DemoApplication.java │ │ │ ├── DemoModel.java │ │ │ ├── controller │ │ │ ├── TestController.java │ │ │ └── TodoController.java │ │ │ ├── dto │ │ │ ├── ResponseDTO.java │ │ │ ├── TestRequestBodyDTO.java │ │ │ └── TodoDTO.java │ │ │ ├── model │ │ │ └── TodoEntity.java │ │ │ ├── persistence │ │ │ └── TodoRepository.java │ │ │ └── service │ │ │ └── TodoService.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── com │ └── example │ └── demo │ └── DemoApplicationTests.java ├── 2.3-Service_Development └── demo │ ├── .gitignore │ ├── build.gradle │ ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── demo │ │ │ ├── DemoApplication.java │ │ │ ├── DemoModel.java │ │ │ ├── controller │ │ │ ├── TestController.java │ │ │ └── TodoController.java │ │ │ ├── dto │ │ │ ├── ResponseDTO.java │ │ │ ├── TestRequestBodyDTO.java │ │ │ └── TodoDTO.java │ │ │ ├── model │ │ │ └── TodoEntity.java │ │ │ ├── persistence │ │ │ └── TodoRepository.java │ │ │ └── service │ │ │ └── TodoService.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── com │ └── example │ └── demo │ └── DemoApplicationTests.java ├── 3.1-Frontend_Development_Environment_Configuration └── todo-react-app │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt │ └── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── reportWebVitals.js │ └── setupTests.js ├── 3.2-Frontend_Development └── todo-react-app │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt │ └── src │ ├── AddTodo.js │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── Todo.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── reportWebVitals.js │ └── setupTests.js ├── 3.3-Service_Integration ├── demo │ ├── .gitignore │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── demo │ │ │ │ ├── DemoApplication.java │ │ │ │ ├── DemoModel.java │ │ │ │ ├── config │ │ │ │ └── WebMvcConfig.java │ │ │ │ ├── controller │ │ │ │ ├── TestController.java │ │ │ │ └── TodoController.java │ │ │ │ ├── dto │ │ │ │ ├── ResponseDTO.java │ │ │ │ ├── TestRequestBodyDTO.java │ │ │ │ └── TodoDTO.java │ │ │ │ ├── model │ │ │ │ └── TodoEntity.java │ │ │ │ ├── persistence │ │ │ │ └── TodoRepository.java │ │ │ │ └── service │ │ │ │ └── TodoService.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── com │ │ └── example │ │ └── demo │ │ └── DemoApplicationTests.java └── todo-react-app │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt │ └── src │ ├── AddTodo.js │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── Todo.js │ ├── app-config.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── reportWebVitals.js │ ├── service │ └── ApiService.js │ └── setupTests.js ├── 4.2-User_Layer └── demo │ ├── .gitignore │ ├── build.gradle │ ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── demo │ │ │ ├── DemoApplication.java │ │ │ ├── DemoModel.java │ │ │ ├── config │ │ │ └── WebMvcConfig.java │ │ │ ├── controller │ │ │ ├── TestController.java │ │ │ ├── TodoController.java │ │ │ └── UserController.java │ │ │ ├── dto │ │ │ ├── ResponseDTO.java │ │ │ ├── TestRequestBodyDTO.java │ │ │ ├── TodoDTO.java │ │ │ └── UserDTO.java │ │ │ ├── model │ │ │ ├── TodoEntity.java │ │ │ └── UserEntity.java │ │ │ ├── persistence │ │ │ ├── TodoRepository.java │ │ │ └── UserRepository.java │ │ │ └── service │ │ │ ├── TodoService.java │ │ │ └── UserService.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── com │ └── example │ └── demo │ └── DemoApplicationTests.java ├── 4.3-Spring_Security_Integration └── demo │ ├── .gitignore │ ├── build.gradle │ ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── demo │ │ │ ├── DemoApplication.java │ │ │ ├── DemoModel.java │ │ │ ├── config │ │ │ ├── WebMvcConfig.java │ │ │ └── WebSecurityConfig.java │ │ │ ├── controller │ │ │ ├── TestController.java │ │ │ ├── TodoController.java │ │ │ └── UserController.java │ │ │ ├── dto │ │ │ ├── ResponseDTO.java │ │ │ ├── TestRequestBodyDTO.java │ │ │ ├── TodoDTO.java │ │ │ └── UserDTO.java │ │ │ ├── model │ │ │ ├── TodoEntity.java │ │ │ └── UserEntity.java │ │ │ ├── persistence │ │ │ ├── TodoRepository.java │ │ │ └── UserRepository.java │ │ │ ├── security │ │ │ ├── ExampleServletFilter.java │ │ │ ├── JwtAuthenticationFilter.java │ │ │ └── TokenProvider.java │ │ │ └── service │ │ │ ├── TodoService.java │ │ │ └── UserService.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── com │ └── example │ └── demo │ └── DemoApplicationTests.java ├── 5.1-Frontend_Routing └── todo-react-app │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt │ └── src │ ├── AddTodo.js │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── AppRouter.js │ ├── Login.js │ ├── Todo.js │ ├── app-config.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── reportWebVitals.js │ ├── service │ └── ApiService.js │ └── setupTests.js ├── 5.2-Login_Page └── todo-react-app │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt │ └── src │ ├── AddTodo.js │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── AppRouter.js │ ├── Login.js │ ├── Todo.js │ ├── app-config.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── reportWebVitals.js │ ├── service │ └── ApiService.js │ └── setupTests.js ├── 5.3-Local_Storage_And_Access_Token └── todo-react-app │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt │ └── src │ ├── AddTodo.js │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── AppRouter.js │ ├── Login.js │ ├── Todo.js │ ├── app-config.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── reportWebVitals.js │ ├── service │ └── ApiService.js │ └── setupTests.js ├── 5.4-Logout_And_Fix_Glitch └── todo-react-app │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt │ └── src │ ├── AddTodo.js │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── AppRouter.js │ ├── Login.js │ ├── Todo.js │ ├── app-config.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── reportWebVitals.js │ ├── service │ └── ApiService.js │ └── setupTests.js ├── 5.5-Sign_Up_Page └── todo-react-app │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt │ └── src │ ├── AddTodo.js │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── AppRouter.js │ ├── Login.js │ ├── SignUp.js │ ├── Todo.js │ ├── app-config.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── reportWebVitals.js │ ├── service │ └── ApiService.js │ └── setupTests.js ├── 6.3-Backend_Deployment_With_AWS_ElasticBeanstalk ├── demo │ ├── .ebextensions │ │ └── application.config │ ├── .gitignore │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── demo │ │ │ │ ├── DemoApplication.java │ │ │ │ ├── DemoModel.java │ │ │ │ ├── config │ │ │ │ ├── WebMvcConfig.java │ │ │ │ └── WebSecurityConfig.java │ │ │ │ ├── controller │ │ │ │ ├── HealthCheckController.java │ │ │ │ ├── TestController.java │ │ │ │ ├── TodoController.java │ │ │ │ └── UserController.java │ │ │ │ ├── dto │ │ │ │ ├── ResponseDTO.java │ │ │ │ ├── TestRequestBodyDTO.java │ │ │ │ ├── TodoDTO.java │ │ │ │ └── UserDTO.java │ │ │ │ ├── model │ │ │ │ ├── TodoEntity.java │ │ │ │ └── UserEntity.java │ │ │ │ ├── persistence │ │ │ │ ├── TodoRepository.java │ │ │ │ └── UserRepository.java │ │ │ │ ├── security │ │ │ │ ├── ExampleServletFilter.java │ │ │ │ ├── JwtAuthenticationFilter.java │ │ │ │ └── TokenProvider.java │ │ │ │ └── service │ │ │ │ ├── TodoService.java │ │ │ │ └── UserService.java │ │ └── resources │ │ │ ├── application-dev.yaml │ │ │ └── application-prod.yaml │ │ └── test │ │ └── java │ │ └── com │ │ └── example │ │ └── demo │ │ └── DemoApplicationTests.java └── todo-react-app │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt │ └── src │ ├── AddTodo.js │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── AppRouter.js │ ├── Login.js │ ├── SignUp.js │ ├── Todo.js │ ├── app-config.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── reportWebVitals.js │ ├── service │ └── ApiService.js │ └── setupTests.js ├── 6.4-Frontend_Deployment_With_AWS_ElasticBeantalk ├── demo │ ├── .gitignore │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── demo │ │ │ │ ├── DemoApplication.java │ │ │ │ ├── DemoModel.java │ │ │ │ ├── config │ │ │ │ ├── WebMvcConfig.java │ │ │ │ └── WebSecurityConfig.java │ │ │ │ ├── controller │ │ │ │ ├── HealthCheckController.java │ │ │ │ ├── TestController.java │ │ │ │ ├── TodoController.java │ │ │ │ └── UserController.java │ │ │ │ ├── dto │ │ │ │ ├── ResponseDTO.java │ │ │ │ ├── TestRequestBodyDTO.java │ │ │ │ ├── TodoDTO.java │ │ │ │ └── UserDTO.java │ │ │ │ ├── model │ │ │ │ ├── TodoEntity.java │ │ │ │ └── UserEntity.java │ │ │ │ ├── persistence │ │ │ │ ├── TodoRepository.java │ │ │ │ └── UserRepository.java │ │ │ │ ├── security │ │ │ │ ├── ExampleServletFilter.java │ │ │ │ ├── JwtAuthenticationFilter.java │ │ │ │ └── TokenProvider.java │ │ │ │ └── service │ │ │ │ ├── TodoService.java │ │ │ │ └── UserService.java │ │ └── resources │ │ │ ├── application-dev.yaml │ │ │ └── application-prod.yaml │ │ └── test │ │ └── java │ │ └── com │ │ └── example │ │ └── demo │ │ └── DemoApplicationTests.java └── todo-react-app │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt │ └── src │ ├── AddTodo.js │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── AppRouter.js │ ├── Login.js │ ├── SignUp.js │ ├── Todo.js │ ├── app-config.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── reportWebVitals.js │ ├── service │ └── ApiService.js │ └── setupTests.js ├── 6.5-Route53-Domain-Configuration ├── demo │ ├── .gitignore │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── demo │ │ │ │ ├── DemoApplication.java │ │ │ │ ├── DemoModel.java │ │ │ │ ├── config │ │ │ │ ├── WebMvcConfig.java │ │ │ │ └── WebSecurityConfig.java │ │ │ │ ├── controller │ │ │ │ ├── HealthCheckController.java │ │ │ │ ├── TestController.java │ │ │ │ ├── TodoController.java │ │ │ │ └── UserController.java │ │ │ │ ├── dto │ │ │ │ ├── ResponseDTO.java │ │ │ │ ├── TestRequestBodyDTO.java │ │ │ │ ├── TodoDTO.java │ │ │ │ └── UserDTO.java │ │ │ │ ├── model │ │ │ │ ├── TodoEntity.java │ │ │ │ └── UserEntity.java │ │ │ │ ├── persistence │ │ │ │ ├── TodoRepository.java │ │ │ │ └── UserRepository.java │ │ │ │ ├── security │ │ │ │ ├── ExampleServletFilter.java │ │ │ │ ├── JwtAuthenticationFilter.java │ │ │ │ └── TokenProvider.java │ │ │ │ └── service │ │ │ │ ├── TodoService.java │ │ │ │ └── UserService.java │ │ └── resources │ │ │ ├── application-dev.yaml │ │ │ └── application-prod.yaml │ │ └── test │ │ └── java │ │ └── com │ │ └── example │ │ └── demo │ │ └── DemoApplicationTests.java └── todo-react-app │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt │ └── src │ ├── AddTodo.js │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── AppRouter.js │ ├── Login.js │ ├── SignUp.js │ ├── Todo.js │ ├── app-config.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── reportWebVitals.js │ ├── service │ └── ApiService.js │ └── setupTests.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | -------------------------------------------------------------------------------- /2.1-Backend_Development_Environment_Configuration/demo/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | -------------------------------------------------------------------------------- /2.1-Backend_Development_Environment_Configuration/demo/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.5.0-SNAPSHOT' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.example' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = '1.8' 10 | 11 | configurations { 12 | compileOnly { 13 | extendsFrom annotationProcessor 14 | } 15 | } 16 | 17 | repositories { 18 | mavenCentral() 19 | maven { url 'https://repo.spring.io/milestone' } 20 | maven { url 'https://repo.spring.io/snapshot' } 21 | } 22 | 23 | dependencies { 24 | implementation 'org.springframework.boot:spring-boot-starter-data-jpa' 25 | implementation 'org.springframework.boot:spring-boot-starter-web' 26 | compileOnly 'org.projectlombok:lombok' 27 | runtimeOnly 'com.h2database:h2' 28 | annotationProcessor 'org.projectlombok:lombok' 29 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 30 | // 31 | compile group: 'com.google.guava', name: 'guava', version: '28.1-jre' 32 | } 33 | 34 | test { 35 | useJUnitPlatform() 36 | } 37 | -------------------------------------------------------------------------------- /2.1-Backend_Development_Environment_Configuration/demo/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/2.1-Backend_Development_Environment_Configuration/demo/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /2.1-Backend_Development_Environment_Configuration/demo/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /2.1-Backend_Development_Environment_Configuration/demo/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { url 'https://repo.spring.io/milestone' } 4 | maven { url 'https://repo.spring.io/snapshot' } 5 | gradlePluginPortal() 6 | } 7 | } 8 | rootProject.name = 'demo' 9 | -------------------------------------------------------------------------------- /2.1-Backend_Development_Environment_Configuration/demo/src/main/java/com/example/demo/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class DemoApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(DemoApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /2.1-Backend_Development_Environment_Configuration/demo/src/main/java/com/example/demo/DemoModel.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import lombok.Builder; 4 | import lombok.NonNull; 5 | import lombok.RequiredArgsConstructor; 6 | 7 | @Builder 8 | @RequiredArgsConstructor 9 | public class DemoModel { 10 | 11 | @NonNull 12 | private String id; 13 | } 14 | -------------------------------------------------------------------------------- /2.1-Backend_Development_Environment_Configuration/demo/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /2.1-Backend_Development_Environment_Configuration/demo/src/test/java/com/example/demo/DemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class DemoApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /2.2-Backend_Service_Architecture/demo/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | -------------------------------------------------------------------------------- /2.2-Backend_Service_Architecture/demo/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.5.0-SNAPSHOT' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.example' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = '1.8' 10 | 11 | configurations { 12 | compileOnly { 13 | extendsFrom annotationProcessor 14 | } 15 | } 16 | 17 | repositories { 18 | mavenCentral() 19 | maven { url 'https://repo.spring.io/milestone' } 20 | maven { url 'https://repo.spring.io/snapshot' } 21 | } 22 | 23 | dependencies { 24 | implementation 'org.springframework.boot:spring-boot-starter-data-jpa' 25 | implementation 'org.springframework.boot:spring-boot-starter-web' 26 | compileOnly 'org.projectlombok:lombok' 27 | runtimeOnly 'com.h2database:h2' 28 | annotationProcessor 'org.projectlombok:lombok' 29 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 30 | // 31 | compile group: 'com.google.guava', name: 'guava', version: '28.1-jre' 32 | } 33 | 34 | test { 35 | useJUnitPlatform() 36 | } 37 | -------------------------------------------------------------------------------- /2.2-Backend_Service_Architecture/demo/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/2.2-Backend_Service_Architecture/demo/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /2.2-Backend_Service_Architecture/demo/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /2.2-Backend_Service_Architecture/demo/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { url 'https://repo.spring.io/milestone' } 4 | maven { url 'https://repo.spring.io/snapshot' } 5 | gradlePluginPortal() 6 | } 7 | } 8 | rootProject.name = 'demo' 9 | -------------------------------------------------------------------------------- /2.2-Backend_Service_Architecture/demo/src/main/java/com/example/demo/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class DemoApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(DemoApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /2.2-Backend_Service_Architecture/demo/src/main/java/com/example/demo/DemoModel.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import lombok.Builder; 4 | import lombok.NonNull; 5 | import lombok.RequiredArgsConstructor; 6 | 7 | @Builder 8 | @RequiredArgsConstructor 9 | public class DemoModel { 10 | 11 | @NonNull 12 | private String id; 13 | } 14 | -------------------------------------------------------------------------------- /2.2-Backend_Service_Architecture/demo/src/main/java/com/example/demo/controller/TodoController.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.controller; 2 | 3 | import com.example.demo.dto.ResponseDTO; 4 | import com.example.demo.dto.TodoDTO; 5 | import com.example.demo.model.TodoEntity; 6 | import com.example.demo.service.TodoService; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.http.ResponseEntity; 9 | import org.springframework.web.bind.annotation.DeleteMapping; 10 | import org.springframework.web.bind.annotation.GetMapping; 11 | import org.springframework.web.bind.annotation.PostMapping; 12 | import org.springframework.web.bind.annotation.PutMapping; 13 | import org.springframework.web.bind.annotation.RequestBody; 14 | import org.springframework.web.bind.annotation.RequestMapping; 15 | import org.springframework.web.bind.annotation.RestController; 16 | 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | import java.util.stream.Collectors; 20 | 21 | @RestController 22 | @RequestMapping("todo") 23 | public class TodoController { 24 | 25 | @Autowired 26 | private TodoService service; 27 | 28 | @GetMapping("/test") 29 | public ResponseEntity testTodo() { 30 | String str = service.testService(); // 테스트 서비스 사용 31 | List list = new ArrayList<>(); 32 | list.add(str); 33 | ResponseDTO response = ResponseDTO.builder().data(list).build(); 34 | // ResponseEntity.ok(response) 를 사용해도 상관 없음 35 | return ResponseEntity.ok().body(response); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /2.2-Backend_Service_Architecture/demo/src/main/java/com/example/demo/dto/ResponseDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.dto; 2 | 3 | import java.util.List; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | @Builder 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | @Data 13 | public class ResponseDTO { 14 | private String error; 15 | private List data; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /2.2-Backend_Service_Architecture/demo/src/main/java/com/example/demo/dto/TestRequestBodyDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.dto; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class TestRequestBodyDTO { 7 | private int id; 8 | private String message; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /2.2-Backend_Service_Architecture/demo/src/main/java/com/example/demo/dto/TodoDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.dto; 2 | 3 | import com.example.demo.model.TodoEntity; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | @Data 11 | public class TodoDTO { 12 | private String id; 13 | private String title; 14 | private boolean done; 15 | 16 | public TodoDTO(final TodoEntity entity) { 17 | this.id = entity.getId(); 18 | this.title = entity.getTitle(); 19 | this.done = entity.isDone(); 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /2.2-Backend_Service_Architecture/demo/src/main/java/com/example/demo/model/TodoEntity.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.model; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.GeneratedValue; 5 | import javax.persistence.Id; 6 | import javax.persistence.Table; 7 | 8 | import lombok.AllArgsConstructor; 9 | import lombok.Builder; 10 | import lombok.Data; 11 | import lombok.NoArgsConstructor; 12 | import org.hibernate.annotations.GenericGenerator; 13 | 14 | @Builder 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | @Data 18 | @Entity 19 | @Table(name = "Todo") 20 | public class TodoEntity { 21 | @Id 22 | @GeneratedValue(generator="system-uuid") 23 | @GenericGenerator(name="system-uuid", strategy = "uuid") 24 | private String id; 25 | private String userId; 26 | private String title; 27 | private boolean done; 28 | } 29 | -------------------------------------------------------------------------------- /2.2-Backend_Service_Architecture/demo/src/main/java/com/example/demo/persistence/TodoRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.persistence; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.stereotype.Repository; 5 | import com.example.demo.model.TodoEntity; 6 | 7 | import java.util.List; 8 | 9 | @Repository 10 | public interface TodoRepository extends JpaRepository{ 11 | List findByUserId(String userId); 12 | } 13 | -------------------------------------------------------------------------------- /2.2-Backend_Service_Architecture/demo/src/main/java/com/example/demo/service/TodoService.java: -------------------------------------------------------------------------------- 1 | 2 | package com.example.demo.service; 3 | 4 | import com.google.common.collect.Lists; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Service; 8 | import com.example.demo.model.TodoEntity; 9 | import com.example.demo.persistence.TodoRepository; 10 | 11 | import java.util.List; 12 | import java.util.Optional; 13 | 14 | @Slf4j 15 | @Service 16 | public class TodoService { 17 | 18 | @Autowired 19 | private TodoRepository repository; 20 | 21 | public String testService() { 22 | // TodoEntity 생성 23 | TodoEntity entity = TodoEntity.builder().title("My first todo item").build(); 24 | // TodoEntity 저장 25 | repository.save(entity); 26 | // TodoEntity 검색 27 | TodoEntity savedEntity = repository.findById(entity.getId()).get(); 28 | return savedEntity.getTitle(); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /2.2-Backend_Service_Architecture/demo/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /2.2-Backend_Service_Architecture/demo/src/test/java/com/example/demo/DemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class DemoApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /2.3-Service_Development/demo/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | -------------------------------------------------------------------------------- /2.3-Service_Development/demo/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.5.0-SNAPSHOT' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.example' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = '1.8' 10 | 11 | configurations { 12 | compileOnly { 13 | extendsFrom annotationProcessor 14 | } 15 | } 16 | 17 | repositories { 18 | mavenCentral() 19 | maven { url 'https://repo.spring.io/milestone' } 20 | maven { url 'https://repo.spring.io/snapshot' } 21 | } 22 | 23 | dependencies { 24 | implementation 'org.springframework.boot:spring-boot-starter-data-jpa' 25 | implementation 'org.springframework.boot:spring-boot-starter-web' 26 | compileOnly 'org.projectlombok:lombok' 27 | runtimeOnly 'com.h2database:h2' 28 | annotationProcessor 'org.projectlombok:lombok' 29 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 30 | // 31 | compile group: 'com.google.guava', name: 'guava', version: '28.1-jre' 32 | } 33 | 34 | test { 35 | useJUnitPlatform() 36 | } 37 | -------------------------------------------------------------------------------- /2.3-Service_Development/demo/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/2.3-Service_Development/demo/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /2.3-Service_Development/demo/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /2.3-Service_Development/demo/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { url 'https://repo.spring.io/milestone' } 4 | maven { url 'https://repo.spring.io/snapshot' } 5 | gradlePluginPortal() 6 | } 7 | } 8 | rootProject.name = 'demo' 9 | -------------------------------------------------------------------------------- /2.3-Service_Development/demo/src/main/java/com/example/demo/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class DemoApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(DemoApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /2.3-Service_Development/demo/src/main/java/com/example/demo/DemoModel.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import lombok.Builder; 4 | import lombok.NonNull; 5 | import lombok.RequiredArgsConstructor; 6 | 7 | @Builder 8 | @RequiredArgsConstructor 9 | public class DemoModel { 10 | 11 | @NonNull 12 | private String id; 13 | } 14 | -------------------------------------------------------------------------------- /2.3-Service_Development/demo/src/main/java/com/example/demo/dto/ResponseDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.dto; 2 | 3 | import java.util.List; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | @Builder 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | @Data 13 | public class ResponseDTO { 14 | private String error; 15 | private List data; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /2.3-Service_Development/demo/src/main/java/com/example/demo/dto/TestRequestBodyDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.dto; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class TestRequestBodyDTO { 7 | private int id; 8 | private String message; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /2.3-Service_Development/demo/src/main/java/com/example/demo/dto/TodoDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.dto; 2 | 3 | import com.example.demo.model.TodoEntity; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | @Data 11 | public class TodoDTO { 12 | private String id; 13 | private String title; 14 | private boolean done; 15 | 16 | public TodoDTO(final TodoEntity entity) { 17 | this.id = entity.getId(); 18 | this.title = entity.getTitle(); 19 | this.done = entity.isDone(); 20 | } 21 | 22 | public static TodoEntity toEntity(final TodoDTO dto) { 23 | return TodoEntity.builder() 24 | .id(dto.getId()) 25 | .title(dto.getTitle()) 26 | .done(dto.isDone()) 27 | .build(); 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /2.3-Service_Development/demo/src/main/java/com/example/demo/model/TodoEntity.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.model; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.GeneratedValue; 5 | import javax.persistence.Id; 6 | import javax.persistence.Table; 7 | 8 | import lombok.AllArgsConstructor; 9 | import lombok.Builder; 10 | import lombok.Data; 11 | import lombok.NoArgsConstructor; 12 | import org.hibernate.annotations.GenericGenerator; 13 | 14 | @Builder 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | @Data 18 | @Entity 19 | @Table(name = "Todo") 20 | public class TodoEntity { 21 | @Id 22 | @GeneratedValue(generator="system-uuid") 23 | @GenericGenerator(name="system-uuid", strategy = "uuid") 24 | private String id; 25 | private String userId; 26 | private String title; 27 | private boolean done; 28 | } 29 | -------------------------------------------------------------------------------- /2.3-Service_Development/demo/src/main/java/com/example/demo/persistence/TodoRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.persistence; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.stereotype.Repository; 5 | import com.example.demo.model.TodoEntity; 6 | 7 | import java.util.List; 8 | 9 | @Repository 10 | public interface TodoRepository extends JpaRepository{ 11 | List findByUserId(String userId); 12 | } 13 | -------------------------------------------------------------------------------- /2.3-Service_Development/demo/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /2.3-Service_Development/demo/src/test/java/com/example/demo/DemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class DemoApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /3.1-Frontend_Development_Environment_Configuration/todo-react-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /3.1-Frontend_Development_Environment_Configuration/todo-react-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo-react-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.11.9", 7 | "@testing-library/react": "^11.2.5", 8 | "@testing-library/user-event": "^12.8.1", 9 | "react": "^17.0.1", 10 | "react-dom": "^17.0.1", 11 | "react-scripts": "4.0.3", 12 | "web-vitals": "^1.1.0" 13 | }, 14 | "scripts": { 15 | "start": "react-scripts start", 16 | "build": "react-scripts build", 17 | "test": "react-scripts test", 18 | "eject": "react-scripts eject" 19 | }, 20 | "eslintConfig": { 21 | "extends": [ 22 | "react-app", 23 | "react-app/jest" 24 | ] 25 | }, 26 | "browserslist": { 27 | "production": [ 28 | ">0.2%", 29 | "not dead", 30 | "not op_mini all" 31 | ], 32 | "development": [ 33 | "last 1 chrome version", 34 | "last 1 firefox version", 35 | "last 1 safari version" 36 | ] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /3.1-Frontend_Development_Environment_Configuration/todo-react-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/3.1-Frontend_Development_Environment_Configuration/todo-react-app/public/favicon.ico -------------------------------------------------------------------------------- /3.1-Frontend_Development_Environment_Configuration/todo-react-app/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/3.1-Frontend_Development_Environment_Configuration/todo-react-app/public/logo192.png -------------------------------------------------------------------------------- /3.1-Frontend_Development_Environment_Configuration/todo-react-app/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/3.1-Frontend_Development_Environment_Configuration/todo-react-app/public/logo512.png -------------------------------------------------------------------------------- /3.1-Frontend_Development_Environment_Configuration/todo-react-app/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /3.1-Frontend_Development_Environment_Configuration/todo-react-app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /3.1-Frontend_Development_Environment_Configuration/todo-react-app/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /3.1-Frontend_Development_Environment_Configuration/todo-react-app/src/App.js: -------------------------------------------------------------------------------- 1 | import logo from './logo.svg'; 2 | import './App.css'; 3 | 4 | function App() { 5 | return ( 6 |
7 |
8 | logo 9 |

10 | Edit src/App.js and save to reload. 11 |

12 | 18 | Learn React 19 | 20 |
21 |
22 | ); 23 | } 24 | 25 | export default App; 26 | -------------------------------------------------------------------------------- /3.1-Frontend_Development_Environment_Configuration/todo-react-app/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /3.1-Frontend_Development_Environment_Configuration/todo-react-app/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /3.1-Frontend_Development_Environment_Configuration/todo-react-app/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "./index.css"; 4 | import App from "./App"; 5 | import reportWebVitals from "./reportWebVitals"; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById("root") 12 | ); 13 | // If you want to start measuring performance in your app, pass a function 14 | // to log results (for example: reportWebVitals(console.log)) 15 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 16 | reportWebVitals(); 17 | -------------------------------------------------------------------------------- /3.1-Frontend_Development_Environment_Configuration/todo-react-app/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /3.1-Frontend_Development_Environment_Configuration/todo-react-app/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /3.2-Frontend_Development/todo-react-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /3.2-Frontend_Development/todo-react-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo-react-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^4.11.3", 7 | "@material-ui/icons": "^4.11.2", 8 | "@testing-library/jest-dom": "^5.11.9", 9 | "@testing-library/react": "^11.2.5", 10 | "@testing-library/user-event": "^12.8.1", 11 | "react": "^17.0.1", 12 | "react-dom": "^17.0.1", 13 | "react-scripts": "4.0.3", 14 | "web-vitals": "^1.1.0" 15 | }, 16 | "scripts": { 17 | "start": "react-scripts start", 18 | "build": "react-scripts build", 19 | "test": "react-scripts test", 20 | "eject": "react-scripts eject" 21 | }, 22 | "eslintConfig": { 23 | "extends": [ 24 | "react-app", 25 | "react-app/jest" 26 | ] 27 | }, 28 | "browserslist": { 29 | "production": [ 30 | ">0.2%", 31 | "not dead", 32 | "not op_mini all" 33 | ], 34 | "development": [ 35 | "last 1 chrome version", 36 | "last 1 firefox version", 37 | "last 1 safari version" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /3.2-Frontend_Development/todo-react-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/3.2-Frontend_Development/todo-react-app/public/favicon.ico -------------------------------------------------------------------------------- /3.2-Frontend_Development/todo-react-app/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/3.2-Frontend_Development/todo-react-app/public/logo192.png -------------------------------------------------------------------------------- /3.2-Frontend_Development/todo-react-app/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/3.2-Frontend_Development/todo-react-app/public/logo512.png -------------------------------------------------------------------------------- /3.2-Frontend_Development/todo-react-app/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /3.2-Frontend_Development/todo-react-app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /3.2-Frontend_Development/todo-react-app/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /3.2-Frontend_Development/todo-react-app/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /3.2-Frontend_Development/todo-react-app/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /3.2-Frontend_Development/todo-react-app/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "./index.css"; 4 | import App from "./App"; 5 | import reportWebVitals from "./reportWebVitals"; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById("root") 12 | ); 13 | // If you want to start measuring performance in your app, pass a function 14 | // to log results (for example: reportWebVitals(console.log)) 15 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 16 | reportWebVitals(); 17 | -------------------------------------------------------------------------------- /3.2-Frontend_Development/todo-react-app/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /3.2-Frontend_Development/todo-react-app/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /3.3-Service_Integration/demo/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | -------------------------------------------------------------------------------- /3.3-Service_Integration/demo/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.5.0-SNAPSHOT' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.example' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = '1.8' 10 | 11 | configurations { 12 | compileOnly { 13 | extendsFrom annotationProcessor 14 | } 15 | } 16 | 17 | repositories { 18 | mavenCentral() 19 | maven { url 'https://repo.spring.io/milestone' } 20 | maven { url 'https://repo.spring.io/snapshot' } 21 | } 22 | 23 | dependencies { 24 | implementation 'org.springframework.boot:spring-boot-starter-data-jpa' 25 | implementation 'org.springframework.boot:spring-boot-starter-web' 26 | compileOnly 'org.projectlombok:lombok' 27 | runtimeOnly 'com.h2database:h2' 28 | annotationProcessor 'org.projectlombok:lombok' 29 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 30 | // 31 | compile group: 'com.google.guava', name: 'guava', version: '28.1-jre' 32 | } 33 | 34 | test { 35 | useJUnitPlatform() 36 | } 37 | -------------------------------------------------------------------------------- /3.3-Service_Integration/demo/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/3.3-Service_Integration/demo/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /3.3-Service_Integration/demo/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /3.3-Service_Integration/demo/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { url 'https://repo.spring.io/milestone' } 4 | maven { url 'https://repo.spring.io/snapshot' } 5 | gradlePluginPortal() 6 | } 7 | } 8 | rootProject.name = 'demo' 9 | -------------------------------------------------------------------------------- /3.3-Service_Integration/demo/src/main/java/com/example/demo/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class DemoApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(DemoApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /3.3-Service_Integration/demo/src/main/java/com/example/demo/DemoModel.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import lombok.Builder; 4 | import lombok.NonNull; 5 | import lombok.RequiredArgsConstructor; 6 | 7 | @Builder 8 | @RequiredArgsConstructor 9 | public class DemoModel { 10 | 11 | @NonNull 12 | private String id; 13 | } 14 | -------------------------------------------------------------------------------- /3.3-Service_Integration/demo/src/main/java/com/example/demo/config/WebMvcConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.config; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 6 | 7 | @Configuration // 스프링 빈으로 등록 8 | public class WebMvcConfig implements WebMvcConfigurer { 9 | private final long MAX_AGE_SECS = 3600; 10 | 11 | @Override 12 | public void addCorsMappings(CorsRegistry registry) { 13 | // 모든 경로에 대해 14 | registry.addMapping("/**") 15 | // Origin이 http:localhost:3000에 대해 16 | .allowedOrigins("http://localhost:3000") 17 | // GET, POST, PUT, PATCH, DELETE, OPTIONS 메서드를 허용한다. 18 | .allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS") 19 | .allowedHeaders("*") 20 | .allowCredentials(true) 21 | .maxAge(MAX_AGE_SECS); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /3.3-Service_Integration/demo/src/main/java/com/example/demo/dto/ResponseDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.dto; 2 | 3 | import java.util.List; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | @Builder 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | @Data 13 | public class ResponseDTO { 14 | private String error; 15 | private List data; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /3.3-Service_Integration/demo/src/main/java/com/example/demo/dto/TestRequestBodyDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.dto; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class TestRequestBodyDTO { 7 | private int id; 8 | private String message; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /3.3-Service_Integration/demo/src/main/java/com/example/demo/dto/TodoDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.dto; 2 | 3 | import com.example.demo.model.TodoEntity; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | @Data 11 | public class TodoDTO { 12 | private String id; 13 | private String title; 14 | private boolean done; 15 | 16 | public TodoDTO(final TodoEntity entity) { 17 | this.id = entity.getId(); 18 | this.title = entity.getTitle(); 19 | this.done = entity.isDone(); 20 | } 21 | 22 | public static TodoEntity toEntity(final TodoDTO dto) { 23 | return TodoEntity.builder() 24 | .id(dto.getId()) 25 | .title(dto.getTitle()) 26 | .done(dto.isDone()) 27 | .build(); 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /3.3-Service_Integration/demo/src/main/java/com/example/demo/model/TodoEntity.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.model; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.GeneratedValue; 5 | import javax.persistence.Id; 6 | import javax.persistence.Table; 7 | 8 | import lombok.AllArgsConstructor; 9 | import lombok.Builder; 10 | import lombok.Data; 11 | import lombok.NoArgsConstructor; 12 | import org.hibernate.annotations.GenericGenerator; 13 | 14 | @Builder 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | @Data 18 | @Entity 19 | @Table(name = "Todo") 20 | public class TodoEntity { 21 | @Id 22 | @GeneratedValue(generator="system-uuid") 23 | @GenericGenerator(name="system-uuid", strategy = "uuid") 24 | private String id; 25 | private String userId; 26 | private String title; 27 | private boolean done; 28 | } 29 | -------------------------------------------------------------------------------- /3.3-Service_Integration/demo/src/main/java/com/example/demo/persistence/TodoRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.persistence; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.stereotype.Repository; 5 | import com.example.demo.model.TodoEntity; 6 | 7 | import java.util.List; 8 | 9 | @Repository 10 | public interface TodoRepository extends JpaRepository{ 11 | List findByUserId(String userId); 12 | } 13 | -------------------------------------------------------------------------------- /3.3-Service_Integration/demo/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /3.3-Service_Integration/demo/src/test/java/com/example/demo/DemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class DemoApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /3.3-Service_Integration/todo-react-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /3.3-Service_Integration/todo-react-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo-react-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^4.11.3", 7 | "@material-ui/icons": "^4.11.2", 8 | "@testing-library/jest-dom": "^5.11.9", 9 | "@testing-library/react": "^11.2.5", 10 | "@testing-library/user-event": "^12.8.1", 11 | "react": "^17.0.1", 12 | "react-dom": "^17.0.1", 13 | "react-scripts": "4.0.3", 14 | "web-vitals": "^1.1.0" 15 | }, 16 | "scripts": { 17 | "start": "react-scripts start", 18 | "build": "react-scripts build", 19 | "test": "react-scripts test", 20 | "eject": "react-scripts eject" 21 | }, 22 | "eslintConfig": { 23 | "extends": [ 24 | "react-app", 25 | "react-app/jest" 26 | ] 27 | }, 28 | "browserslist": { 29 | "production": [ 30 | ">0.2%", 31 | "not dead", 32 | "not op_mini all" 33 | ], 34 | "development": [ 35 | "last 1 chrome version", 36 | "last 1 firefox version", 37 | "last 1 safari version" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /3.3-Service_Integration/todo-react-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/3.3-Service_Integration/todo-react-app/public/favicon.ico -------------------------------------------------------------------------------- /3.3-Service_Integration/todo-react-app/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/3.3-Service_Integration/todo-react-app/public/logo192.png -------------------------------------------------------------------------------- /3.3-Service_Integration/todo-react-app/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/3.3-Service_Integration/todo-react-app/public/logo512.png -------------------------------------------------------------------------------- /3.3-Service_Integration/todo-react-app/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /3.3-Service_Integration/todo-react-app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /3.3-Service_Integration/todo-react-app/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /3.3-Service_Integration/todo-react-app/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /3.3-Service_Integration/todo-react-app/src/app-config.js: -------------------------------------------------------------------------------- 1 | let backendHost; 2 | 3 | const hostname = window && window.location && window.location.hostname; 4 | 5 | if (hostname === "localhost") { 6 | backendHost = "http://localhost:8080"; 7 | } 8 | 9 | export const API_BASE_URL = `${backendHost}`; 10 | -------------------------------------------------------------------------------- /3.3-Service_Integration/todo-react-app/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /3.3-Service_Integration/todo-react-app/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "./index.css"; 4 | import App from "./App"; 5 | import reportWebVitals from "./reportWebVitals"; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById("root") 12 | ); 13 | // If you want to start measuring performance in your app, pass a function 14 | // to log results (for example: reportWebVitals(console.log)) 15 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 16 | reportWebVitals(); 17 | -------------------------------------------------------------------------------- /3.3-Service_Integration/todo-react-app/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /3.3-Service_Integration/todo-react-app/src/service/ApiService.js: -------------------------------------------------------------------------------- 1 | import { API_BASE_URL } from "../app-config"; 2 | 3 | export function call(api, method, request) { 4 | let options = { 5 | headers: new Headers({ 6 | "Content-Type": "application/json", 7 | }), 8 | url: API_BASE_URL + api, 9 | method: method, 10 | }; 11 | if (request) { 12 | // GET method 13 | options.body = JSON.stringify(request); 14 | } 15 | return fetch(options.url, options).then((response) => 16 | response.json().then((json) => { 17 | if (!response.ok) { 18 | // response.ok가 true이면 정상적인 리스폰스를 받은것, 아니면 에러 리스폰스를 받은것. 19 | return Promise.reject(json); 20 | } 21 | return json; 22 | }) 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /3.3-Service_Integration/todo-react-app/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /4.2-User_Layer/demo/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | -------------------------------------------------------------------------------- /4.2-User_Layer/demo/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.5.0-SNAPSHOT' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.example' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = '1.8' 10 | 11 | configurations { 12 | compileOnly { 13 | extendsFrom annotationProcessor 14 | } 15 | } 16 | 17 | repositories { 18 | mavenCentral() 19 | maven { url 'https://repo.spring.io/milestone' } 20 | maven { url 'https://repo.spring.io/snapshot' } 21 | } 22 | 23 | dependencies { 24 | implementation 'org.springframework.boot:spring-boot-starter-data-jpa' 25 | implementation 'org.springframework.boot:spring-boot-starter-web' 26 | implementation 'org.springframework.boot:spring-boot-starter-security' 27 | 28 | // https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt 29 | compile group: 'io.jsonwebtoken', name: 'jjwt', version: '0.9.1' 30 | 31 | compileOnly 'org.projectlombok:lombok' 32 | runtimeOnly 'com.h2database:h2' 33 | annotationProcessor 'org.projectlombok:lombok' 34 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 35 | // 36 | compile group: 'com.google.guava', name: 'guava', version: '28.1-jre' 37 | } 38 | 39 | test { 40 | useJUnitPlatform() 41 | } 42 | -------------------------------------------------------------------------------- /4.2-User_Layer/demo/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/4.2-User_Layer/demo/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /4.2-User_Layer/demo/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Mar 20 21:07:50 PDT 2021 2 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-all.zip 3 | distributionBase=GRADLE_USER_HOME 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /4.2-User_Layer/demo/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { url 'https://repo.spring.io/milestone' } 4 | maven { url 'https://repo.spring.io/snapshot' } 5 | gradlePluginPortal() 6 | } 7 | } 8 | rootProject.name = 'demo' 9 | -------------------------------------------------------------------------------- /4.2-User_Layer/demo/src/main/java/com/example/demo/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class DemoApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(DemoApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /4.2-User_Layer/demo/src/main/java/com/example/demo/DemoModel.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import lombok.Builder; 4 | import lombok.NonNull; 5 | import lombok.RequiredArgsConstructor; 6 | 7 | @Builder 8 | @RequiredArgsConstructor 9 | public class DemoModel { 10 | 11 | @NonNull 12 | private String id; 13 | } 14 | -------------------------------------------------------------------------------- /4.2-User_Layer/demo/src/main/java/com/example/demo/config/WebMvcConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.config; 2 | import org.springframework.context.annotation.Configuration; 3 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 4 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 5 | 6 | @Configuration 7 | public class WebMvcConfig implements WebMvcConfigurer { 8 | 9 | private final long MAX_AGE_SECS = 3600; 10 | 11 | @Override 12 | public void addCorsMappings(CorsRegistry registry) { 13 | registry.addMapping("/**") 14 | .allowedOrigins("http://localhost:3000") 15 | .allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS") 16 | .allowedHeaders("*") 17 | .allowCredentials(true) 18 | .maxAge(MAX_AGE_SECS); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /4.2-User_Layer/demo/src/main/java/com/example/demo/dto/ResponseDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.dto; 2 | 3 | import java.util.List; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | @Builder 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | @Data 13 | public class ResponseDTO { 14 | private String error; 15 | private List data; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /4.2-User_Layer/demo/src/main/java/com/example/demo/dto/TestRequestBodyDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.dto; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class TestRequestBodyDTO { 7 | private int id; 8 | private String message; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /4.2-User_Layer/demo/src/main/java/com/example/demo/dto/TodoDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.dto; 2 | 3 | import com.example.demo.model.TodoEntity; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | @Data 11 | public class TodoDTO { 12 | private String id; 13 | private String title; 14 | private boolean done; 15 | 16 | public TodoDTO(final TodoEntity entity) { 17 | this.id = entity.getId(); 18 | this.title = entity.getTitle(); 19 | this.done = entity.isDone(); 20 | } 21 | 22 | public static TodoEntity toEntity(final TodoDTO dto) { 23 | return TodoEntity.builder() 24 | .id(dto.getId()) 25 | .title(dto.getTitle()) 26 | .done(dto.isDone()) 27 | .build(); 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /4.2-User_Layer/demo/src/main/java/com/example/demo/dto/UserDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @Builder 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | public class UserDTO { 13 | private String token; 14 | private String email; 15 | private String username; 16 | private String password; 17 | private String id; 18 | } 19 | -------------------------------------------------------------------------------- /4.2-User_Layer/demo/src/main/java/com/example/demo/model/TodoEntity.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.model; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.GeneratedValue; 5 | import javax.persistence.Id; 6 | import javax.persistence.Table; 7 | 8 | import lombok.AllArgsConstructor; 9 | import lombok.Builder; 10 | import lombok.Data; 11 | import lombok.NoArgsConstructor; 12 | import org.hibernate.annotations.GenericGenerator; 13 | 14 | @Builder 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | @Data 18 | @Entity 19 | @Table(name = "Todo") 20 | public class TodoEntity { 21 | @Id 22 | @GeneratedValue(generator="system-uuid") 23 | @GenericGenerator(name="system-uuid", strategy = "uuid") 24 | private String id; 25 | private String userId; 26 | private String title; 27 | private boolean done; 28 | } 29 | -------------------------------------------------------------------------------- /4.2-User_Layer/demo/src/main/java/com/example/demo/model/UserEntity.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | import org.hibernate.annotations.GenericGenerator; 9 | 10 | import javax.persistence.Column; 11 | import javax.persistence.Entity; 12 | import javax.persistence.GeneratedValue; 13 | import javax.persistence.Id; 14 | import javax.persistence.Table; 15 | import javax.persistence.UniqueConstraint; 16 | 17 | @Data 18 | @Entity 19 | @Builder 20 | @NoArgsConstructor 21 | @AllArgsConstructor 22 | @Table(uniqueConstraints = {@UniqueConstraint(columnNames = "email")}) 23 | public class UserEntity { 24 | @Id 25 | @GeneratedValue(generator="system-uuid") 26 | @GenericGenerator(name="system-uuid", strategy = "uuid") 27 | private String id; // 유저에게 고유하게 부여되는 id. 28 | 29 | @Column(nullable = false) 30 | private String username; // 유저의 이름 31 | 32 | @Column(nullable = false) 33 | private String email; // 유저의 email, 아이디와 같은 기능을 한다. 34 | 35 | @Column(nullable = false) 36 | private String password; // 패스워드. null이 가능한 이유는 oAuth로 페이스북이나 트위터같은 제3의 어플리케이션을 통해 로그인 할 수 있게 하기 위함이다. 37 | } 38 | -------------------------------------------------------------------------------- /4.2-User_Layer/demo/src/main/java/com/example/demo/persistence/TodoRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.persistence; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.stereotype.Repository; 5 | import com.example.demo.model.TodoEntity; 6 | 7 | import java.util.List; 8 | 9 | @Repository 10 | public interface TodoRepository extends JpaRepository{ 11 | List findByUserId(String userId); 12 | } 13 | -------------------------------------------------------------------------------- /4.2-User_Layer/demo/src/main/java/com/example/demo/persistence/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.persistence; 2 | 3 | import com.example.demo.model.UserEntity; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface UserRepository extends JpaRepository { 9 | 10 | UserEntity findByEmail(String email); 11 | Boolean existsByEmail(String email); 12 | UserEntity findByEmailAndPassword(String email, String password); 13 | } 14 | -------------------------------------------------------------------------------- /4.2-User_Layer/demo/src/main/java/com/example/demo/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.service; 2 | 3 | import com.example.demo.model.UserEntity; 4 | import com.example.demo.persistence.UserRepository; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Service; 8 | 9 | @Slf4j 10 | @Service 11 | public class UserService { 12 | 13 | @Autowired 14 | private UserRepository userRepository; 15 | 16 | public UserEntity create(final UserEntity userEntity) { 17 | if(userEntity == null || userEntity.getEmail() == null ) { 18 | throw new RuntimeException("Invalid arguments"); 19 | } 20 | final String email = userEntity.getEmail(); 21 | if(userRepository.existsByEmail(email)) { 22 | log.warn("Email already exists {}", email); 23 | throw new RuntimeException("Email already exists"); 24 | } 25 | 26 | return userRepository.save(userEntity); 27 | } 28 | 29 | public UserEntity getByCredentials(final String email, final String password) { 30 | return userRepository.findByEmailAndPassword(email, password); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /4.2-User_Layer/demo/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /4.2-User_Layer/demo/src/test/java/com/example/demo/DemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class DemoApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /4.3-Spring_Security_Integration/demo/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | -------------------------------------------------------------------------------- /4.3-Spring_Security_Integration/demo/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.5.0-SNAPSHOT' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.example' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = '1.8' 10 | 11 | configurations { 12 | compileOnly { 13 | extendsFrom annotationProcessor 14 | } 15 | } 16 | 17 | repositories { 18 | mavenCentral() 19 | maven { url 'https://repo.spring.io/milestone' } 20 | maven { url 'https://repo.spring.io/snapshot' } 21 | } 22 | 23 | dependencies { 24 | implementation 'org.springframework.boot:spring-boot-starter-data-jpa' 25 | implementation 'org.springframework.boot:spring-boot-starter-web' 26 | implementation 'org.springframework.boot:spring-boot-starter-security' 27 | 28 | // https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt 29 | compile group: 'io.jsonwebtoken', name: 'jjwt', version: '0.9.1' 30 | 31 | compileOnly 'org.projectlombok:lombok' 32 | runtimeOnly 'com.h2database:h2' 33 | annotationProcessor 'org.projectlombok:lombok' 34 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 35 | // 36 | compile group: 'com.google.guava', name: 'guava', version: '28.1-jre' 37 | } 38 | 39 | test { 40 | useJUnitPlatform() 41 | } 42 | -------------------------------------------------------------------------------- /4.3-Spring_Security_Integration/demo/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/4.3-Spring_Security_Integration/demo/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /4.3-Spring_Security_Integration/demo/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Mar 20 21:07:50 PDT 2021 2 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-all.zip 3 | distributionBase=GRADLE_USER_HOME 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /4.3-Spring_Security_Integration/demo/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { url 'https://repo.spring.io/milestone' } 4 | maven { url 'https://repo.spring.io/snapshot' } 5 | gradlePluginPortal() 6 | } 7 | } 8 | rootProject.name = 'demo' 9 | -------------------------------------------------------------------------------- /4.3-Spring_Security_Integration/demo/src/main/java/com/example/demo/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class DemoApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(DemoApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /4.3-Spring_Security_Integration/demo/src/main/java/com/example/demo/DemoModel.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import lombok.Builder; 4 | import lombok.NonNull; 5 | import lombok.RequiredArgsConstructor; 6 | 7 | @Builder 8 | @RequiredArgsConstructor 9 | public class DemoModel { 10 | 11 | @NonNull 12 | private String id; 13 | } 14 | -------------------------------------------------------------------------------- /4.3-Spring_Security_Integration/demo/src/main/java/com/example/demo/config/WebMvcConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.config; 2 | import org.springframework.context.annotation.Configuration; 3 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 4 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 5 | 6 | @Configuration 7 | public class WebMvcConfig implements WebMvcConfigurer { 8 | 9 | private final long MAX_AGE_SECS = 3600; 10 | 11 | @Override 12 | public void addCorsMappings(CorsRegistry registry) { 13 | registry.addMapping("/**") 14 | .allowedOrigins("http://localhost:3000") 15 | .allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS") 16 | .allowedHeaders("*") 17 | .allowCredentials(true) 18 | .maxAge(MAX_AGE_SECS); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /4.3-Spring_Security_Integration/demo/src/main/java/com/example/demo/dto/ResponseDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.dto; 2 | 3 | import java.util.List; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | @Builder 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | @Data 13 | public class ResponseDTO { 14 | private String error; 15 | private List data; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /4.3-Spring_Security_Integration/demo/src/main/java/com/example/demo/dto/TestRequestBodyDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.dto; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class TestRequestBodyDTO { 7 | private int id; 8 | private String message; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /4.3-Spring_Security_Integration/demo/src/main/java/com/example/demo/dto/TodoDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.dto; 2 | 3 | import com.example.demo.model.TodoEntity; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | @Data 11 | public class TodoDTO { 12 | private String id; 13 | private String title; 14 | private boolean done; 15 | 16 | public TodoDTO(final TodoEntity entity) { 17 | this.id = entity.getId(); 18 | this.title = entity.getTitle(); 19 | this.done = entity.isDone(); 20 | } 21 | 22 | public static TodoEntity toEntity(final TodoDTO dto) { 23 | return TodoEntity.builder() 24 | .id(dto.getId()) 25 | .title(dto.getTitle()) 26 | .done(dto.isDone()) 27 | .build(); 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /4.3-Spring_Security_Integration/demo/src/main/java/com/example/demo/dto/UserDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @Builder 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | public class UserDTO { 13 | private String token; 14 | private String email; 15 | private String username; 16 | private String password; 17 | private String id; 18 | } 19 | -------------------------------------------------------------------------------- /4.3-Spring_Security_Integration/demo/src/main/java/com/example/demo/model/TodoEntity.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.model; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.GeneratedValue; 5 | import javax.persistence.Id; 6 | import javax.persistence.Table; 7 | 8 | import lombok.AllArgsConstructor; 9 | import lombok.Builder; 10 | import lombok.Data; 11 | import lombok.NoArgsConstructor; 12 | import org.hibernate.annotations.GenericGenerator; 13 | 14 | @Builder 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | @Data 18 | @Entity 19 | @Table(name = "Todo") 20 | public class TodoEntity { 21 | @Id 22 | @GeneratedValue(generator="system-uuid") 23 | @GenericGenerator(name="system-uuid", strategy = "uuid") 24 | private String id; 25 | private String userId; 26 | private String title; 27 | private boolean done; 28 | } 29 | -------------------------------------------------------------------------------- /4.3-Spring_Security_Integration/demo/src/main/java/com/example/demo/model/UserEntity.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | import org.hibernate.annotations.GenericGenerator; 9 | 10 | import javax.persistence.Column; 11 | import javax.persistence.Entity; 12 | import javax.persistence.GeneratedValue; 13 | import javax.persistence.Id; 14 | import javax.persistence.Table; 15 | import javax.persistence.UniqueConstraint; 16 | 17 | @Data 18 | @Entity 19 | @Builder 20 | @NoArgsConstructor 21 | @AllArgsConstructor 22 | @Table(uniqueConstraints = {@UniqueConstraint(columnNames = "email")}) 23 | public class UserEntity { 24 | @Id 25 | @GeneratedValue(generator="system-uuid") 26 | @GenericGenerator(name="system-uuid", strategy = "uuid") 27 | private String id; // 유저에게 고유하게 부여되는 id. 28 | 29 | @Column(nullable = false) 30 | private String username; // 유저의 이름 31 | 32 | @Column(nullable = false) 33 | private String email; // 유저의 email, 아이디와 같은 기능을 한다. 34 | 35 | @Column(nullable = false) 36 | private String password; // 패스워드. null이 가능한 이유는 oAuth로 페이스북이나 트위터같은 제3의 어플리케이션을 통해 로그인 할 수 있게 하기 위함이다. 37 | } 38 | -------------------------------------------------------------------------------- /4.3-Spring_Security_Integration/demo/src/main/java/com/example/demo/persistence/TodoRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.persistence; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.stereotype.Repository; 5 | import com.example.demo.model.TodoEntity; 6 | 7 | import java.util.List; 8 | 9 | @Repository 10 | public interface TodoRepository extends JpaRepository{ 11 | List findByUserId(String userId); 12 | } 13 | -------------------------------------------------------------------------------- /4.3-Spring_Security_Integration/demo/src/main/java/com/example/demo/persistence/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.persistence; 2 | 3 | import com.example.demo.model.UserEntity; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface UserRepository extends JpaRepository { 9 | 10 | UserEntity findByEmail(String email); 11 | Boolean existsByEmail(String email); 12 | UserEntity findByEmailAndPassword(String email, String password); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /4.3-Spring_Security_Integration/demo/src/main/java/com/example/demo/security/ExampleServletFilter.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.security; 2 | 3 | import org.springframework.util.StringUtils; 4 | 5 | import javax.servlet.FilterChain; 6 | import javax.servlet.ServletException; 7 | import javax.servlet.http.HttpFilter; 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | import java.io.IOException; 11 | 12 | /* 예제용 - 실제로 사용하지 않음. */ 13 | public class ExampleServletFilter extends HttpFilter { 14 | 15 | private TokenProvider tokenProvider; 16 | 17 | @Override 18 | protected void doFilter(HttpServletRequest request, 19 | HttpServletResponse response, 20 | FilterChain filterChain) 21 | throws IOException, ServletException { 22 | try { 23 | final String token = parseBearerToken(request); 24 | 25 | if (token != null && !token.equalsIgnoreCase("null")) { 26 | // userId 가져오기. 위조 된 경우 예외 처리 된다. 27 | String userId = tokenProvider.validateAndGetUserId(token); 28 | 29 | // 다음 ServletFilter 실행 30 | filterChain.doFilter(request, response); 31 | } 32 | } catch (Exception e) { 33 | // 예외 발생시 response를 403 Forbidden으로 설정. 34 | response.setStatus(HttpServletResponse.SC_FORBIDDEN); 35 | } 36 | } 37 | 38 | private String parseBearerToken(HttpServletRequest request) { 39 | // Http 리퀘스트의 헤더를 파싱해 Bearer 토큰을 리턴한다. 40 | String bearerToken = request.getHeader("Authorization"); 41 | 42 | if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { 43 | return bearerToken.substring(7); 44 | } 45 | return null; 46 | } 47 | } -------------------------------------------------------------------------------- /4.3-Spring_Security_Integration/demo/src/main/java/com/example/demo/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.service; 2 | 3 | import com.example.demo.model.UserEntity; 4 | import com.example.demo.persistence.UserRepository; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 8 | import org.springframework.security.crypto.password.PasswordEncoder; 9 | import org.springframework.stereotype.Service; 10 | 11 | import java.util.Optional; 12 | 13 | @Slf4j 14 | @Service 15 | public class UserService { 16 | 17 | @Autowired 18 | private UserRepository userRepository; 19 | 20 | public UserEntity create(final UserEntity userEntity) { 21 | if(userEntity == null || userEntity.getEmail() == null ) { 22 | throw new RuntimeException("Invalid arguments"); 23 | } 24 | final String email = userEntity.getEmail(); 25 | if(userRepository.existsByEmail(email)) { 26 | log.warn("Email already exists {}", email); 27 | throw new RuntimeException("Email already exists"); 28 | } 29 | 30 | return userRepository.save(userEntity); 31 | } 32 | 33 | public UserEntity getByCredentials(final String email, final String password, final PasswordEncoder encoder) { 34 | final UserEntity originalUser = userRepository.findByEmail(email); 35 | 36 | // matches 메서드를 이용해 패스워드가 같은지 확인 37 | if(originalUser != null && encoder.matches(password, originalUser.getPassword())) { 38 | return originalUser; 39 | } 40 | return null; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /4.3-Spring_Security_Integration/demo/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /4.3-Spring_Security_Integration/demo/src/test/java/com/example/demo/DemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class DemoApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /5.1-Frontend_Routing/todo-react-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /5.1-Frontend_Routing/todo-react-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo-react-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^4.11.3", 7 | "@material-ui/icons": "^4.11.2", 8 | "@testing-library/jest-dom": "^5.11.9", 9 | "@testing-library/react": "^11.2.5", 10 | "@testing-library/user-event": "^12.8.1", 11 | "react": "^17.0.1", 12 | "react-dom": "^17.0.1", 13 | "react-router-dom": "^5.2.0", 14 | "react-scripts": "4.0.3", 15 | "web-vitals": "^1.1.0" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "test": "react-scripts test", 21 | "eject": "react-scripts eject" 22 | }, 23 | "eslintConfig": { 24 | "extends": [ 25 | "react-app", 26 | "react-app/jest" 27 | ] 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.2%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /5.1-Frontend_Routing/todo-react-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/5.1-Frontend_Routing/todo-react-app/public/favicon.ico -------------------------------------------------------------------------------- /5.1-Frontend_Routing/todo-react-app/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/5.1-Frontend_Routing/todo-react-app/public/logo192.png -------------------------------------------------------------------------------- /5.1-Frontend_Routing/todo-react-app/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/5.1-Frontend_Routing/todo-react-app/public/logo512.png -------------------------------------------------------------------------------- /5.1-Frontend_Routing/todo-react-app/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /5.1-Frontend_Routing/todo-react-app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /5.1-Frontend_Routing/todo-react-app/src/AddTodo.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { TextField, Paper, Button, Grid } from "@material-ui/core"; 3 | 4 | class AddTodo extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this.state = { item: { title: "" } }; 8 | this.add = props.add; 9 | } 10 | 11 | onInputChange = (e) => { 12 | const thisItem = this.state.item; 13 | thisItem.title = e.target.value; 14 | this.setState({ item: thisItem }); 15 | console.log(thisItem); 16 | }; 17 | 18 | onButtonClick = () => { 19 | this.add(this.state.item); 20 | this.setState({ item: { title: "" } }); 21 | }; 22 | 23 | enterKeyEventHandler = (e) => { 24 | if (e.key === "Enter") { 25 | this.onButtonClick(); 26 | } 27 | }; 28 | 29 | render() { 30 | return ( 31 | 32 | 33 | 34 | 41 | 42 | 43 | 51 | 52 | 53 | 54 | ); 55 | } 56 | } 57 | 58 | export default AddTodo; 59 | -------------------------------------------------------------------------------- /5.1-Frontend_Routing/todo-react-app/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /5.1-Frontend_Routing/todo-react-app/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /5.1-Frontend_Routing/todo-react-app/src/AppRouter.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./index.css"; 3 | import App from "./App"; 4 | import Login from "./Login"; 5 | import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; 6 | import Box from "@material-ui/core/Box"; 7 | import Typography from "@material-ui/core/Typography"; 8 | 9 | function Copyright() { 10 | return ( 11 | 12 | {"Copyright © "} 13 | fsoftwareengineer, {new Date().getFullYear()} 14 | {"."} 15 | 16 | ); 17 | } 18 | 19 | class AppRouter extends React.Component { 20 | render() { 21 | return ( 22 |
23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 | 35 | 36 | 37 |
38 |
39 | ); 40 | } 41 | } 42 | 43 | export default AppRouter; 44 | -------------------------------------------------------------------------------- /5.1-Frontend_Routing/todo-react-app/src/Login.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | class Login extends React.Component { 4 | constructor(props) { 5 | super(props); 6 | } 7 | 8 | render() { 9 | return

로그인 페이지

; 10 | } 11 | } 12 | 13 | export default Login; 14 | -------------------------------------------------------------------------------- /5.1-Frontend_Routing/todo-react-app/src/app-config.js: -------------------------------------------------------------------------------- 1 | let backendHost; 2 | 3 | const hostname = window && window.location && window.location.hostname; 4 | 5 | if (hostname === "localhost") { 6 | backendHost = "http://localhost:8080"; 7 | } 8 | 9 | export const API_BASE_URL = `${backendHost}`; 10 | -------------------------------------------------------------------------------- /5.1-Frontend_Routing/todo-react-app/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /5.1-Frontend_Routing/todo-react-app/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "./index.css"; 4 | import AppRouter from "./AppRouter"; 5 | import reportWebVitals from "./reportWebVitals"; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById("root") 12 | ); 13 | // If you want to start measuring performance in your app, pass a function 14 | // to log results (for example: reportWebVitals(console.log)) 15 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 16 | reportWebVitals(); 17 | -------------------------------------------------------------------------------- /5.1-Frontend_Routing/todo-react-app/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /5.1-Frontend_Routing/todo-react-app/src/service/ApiService.js: -------------------------------------------------------------------------------- 1 | import { API_BASE_URL } from "../app-config"; 2 | 3 | export function call(api, method, request) { 4 | let options = { 5 | headers: new Headers({ 6 | "Content-Type": "application/json", 7 | }), 8 | url: API_BASE_URL + api, 9 | method: method, 10 | }; 11 | if (request) { 12 | // GET method 13 | options.body = JSON.stringify(request); 14 | } 15 | return fetch(options.url, options) 16 | .then((response) => 17 | response.json().then((json) => { 18 | if (!response.ok) { 19 | // response.ok가 true이면 정상적인 리스폰스를 받은것, 아니면 에러 리스폰스를 받은것. 20 | return Promise.reject(json); 21 | } 22 | return json; 23 | }) 24 | ) 25 | .catch((error) => { 26 | // 추가된 부분 27 | console.log(error.status); 28 | if (error.status === 403) { 29 | window.location.href = "/login"; // redirect 30 | } 31 | return Promise.reject(error); 32 | }); 33 | } 34 | -------------------------------------------------------------------------------- /5.1-Frontend_Routing/todo-react-app/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /5.2-Login_Page/todo-react-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /5.2-Login_Page/todo-react-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo-react-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^4.11.3", 7 | "@material-ui/icons": "^4.11.2", 8 | "@testing-library/jest-dom": "^5.11.9", 9 | "@testing-library/react": "^11.2.5", 10 | "@testing-library/user-event": "^12.8.1", 11 | "react": "^17.0.1", 12 | "react-dom": "^17.0.1", 13 | "react-router-dom": "^5.2.0", 14 | "react-scripts": "4.0.3", 15 | "web-vitals": "^1.1.0" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "test": "react-scripts test", 21 | "eject": "react-scripts eject" 22 | }, 23 | "eslintConfig": { 24 | "extends": [ 25 | "react-app", 26 | "react-app/jest" 27 | ] 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.2%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /5.2-Login_Page/todo-react-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/5.2-Login_Page/todo-react-app/public/favicon.ico -------------------------------------------------------------------------------- /5.2-Login_Page/todo-react-app/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/5.2-Login_Page/todo-react-app/public/logo192.png -------------------------------------------------------------------------------- /5.2-Login_Page/todo-react-app/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/5.2-Login_Page/todo-react-app/public/logo512.png -------------------------------------------------------------------------------- /5.2-Login_Page/todo-react-app/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /5.2-Login_Page/todo-react-app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /5.2-Login_Page/todo-react-app/src/AddTodo.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { TextField, Paper, Button, Grid } from "@material-ui/core"; 3 | 4 | class AddTodo extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this.state = { item: { title: "" } }; 8 | this.add = props.add; 9 | } 10 | 11 | onInputChange = (e) => { 12 | const thisItem = this.state.item; 13 | thisItem.title = e.target.value; 14 | this.setState({ item: thisItem }); 15 | console.log(thisItem); 16 | }; 17 | 18 | onButtonClick = () => { 19 | this.add(this.state.item); 20 | this.setState({ item: { title: "" } }); 21 | }; 22 | 23 | enterKeyEventHandler = (e) => { 24 | if (e.key === "Enter") { 25 | this.onButtonClick(); 26 | } 27 | }; 28 | 29 | render() { 30 | return ( 31 | 32 | 33 | 34 | 41 | 42 | 43 | 51 | 52 | 53 | 54 | ); 55 | } 56 | } 57 | 58 | export default AddTodo; 59 | -------------------------------------------------------------------------------- /5.2-Login_Page/todo-react-app/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /5.2-Login_Page/todo-react-app/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /5.2-Login_Page/todo-react-app/src/AppRouter.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./index.css"; 3 | import App from "./App"; 4 | import Login from "./Login"; 5 | import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; 6 | import Box from "@material-ui/core/Box"; 7 | import Typography from "@material-ui/core/Typography"; 8 | 9 | function Copyright() { 10 | return ( 11 | 12 | {"Copyright © "} 13 | fsoftwareengineer, {new Date().getFullYear()} 14 | {"."} 15 | 16 | ); 17 | } 18 | 19 | class AppRouter extends React.Component { 20 | render() { 21 | return ( 22 |
23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 | 35 | 36 | 37 |
38 |
39 | ); 40 | } 41 | } 42 | 43 | export default AppRouter; 44 | -------------------------------------------------------------------------------- /5.2-Login_Page/todo-react-app/src/app-config.js: -------------------------------------------------------------------------------- 1 | let backendHost; 2 | 3 | const hostname = window && window.location && window.location.hostname; 4 | 5 | if (hostname === "localhost") { 6 | backendHost = "http://localhost:8080"; 7 | } 8 | 9 | export const API_BASE_URL = `${backendHost}`; 10 | -------------------------------------------------------------------------------- /5.2-Login_Page/todo-react-app/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /5.2-Login_Page/todo-react-app/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "./index.css"; 4 | import AppRouter from "./AppRouter"; 5 | import reportWebVitals from "./reportWebVitals"; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById("root") 12 | ); 13 | // If you want to start measuring performance in your app, pass a function 14 | // to log results (for example: reportWebVitals(console.log)) 15 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 16 | reportWebVitals(); 17 | -------------------------------------------------------------------------------- /5.2-Login_Page/todo-react-app/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /5.2-Login_Page/todo-react-app/src/service/ApiService.js: -------------------------------------------------------------------------------- 1 | import { API_BASE_URL } from "../app-config"; 2 | 3 | export function call(api, method, request) { 4 | let options = { 5 | headers: new Headers({ 6 | "Content-Type": "application/json", 7 | }), 8 | url: API_BASE_URL + api, 9 | method: method, 10 | }; 11 | if (request) { 12 | // GET method 13 | options.body = JSON.stringify(request); 14 | } 15 | return fetch(options.url, options) 16 | .then((response) => 17 | response.json().then((json) => { 18 | if (!response.ok) { 19 | // response.ok가 true이면 정상적인 리스폰스를 받은것, 아니면 에러 리스폰스를 받은것. 20 | return Promise.reject(json); 21 | } 22 | return json; 23 | }) 24 | ) 25 | .catch((error) => { 26 | // 추가된 부분 27 | console.log(error.status); 28 | if (error.status === 403) { 29 | window.location.href = "/login"; // redirect 30 | } 31 | return Promise.reject(error); 32 | }); 33 | } 34 | 35 | export function signin(userDTO) { 36 | return call("/auth/signin", "POST", userDTO).then((response) => { 37 | if (response.token) { 38 | // token이 존재하는 경우 Todo 화면으로 리디렉트 39 | window.location.href = "/"; 40 | } 41 | }); 42 | } 43 | -------------------------------------------------------------------------------- /5.2-Login_Page/todo-react-app/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /5.3-Local_Storage_And_Access_Token/todo-react-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /5.3-Local_Storage_And_Access_Token/todo-react-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo-react-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^4.11.3", 7 | "@material-ui/icons": "^4.11.2", 8 | "@testing-library/jest-dom": "^5.11.9", 9 | "@testing-library/react": "^11.2.5", 10 | "@testing-library/user-event": "^12.8.1", 11 | "react": "^17.0.1", 12 | "react-dom": "^17.0.1", 13 | "react-router-dom": "^5.2.0", 14 | "react-scripts": "4.0.3", 15 | "web-vitals": "^1.1.0" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "test": "react-scripts test", 21 | "eject": "react-scripts eject" 22 | }, 23 | "eslintConfig": { 24 | "extends": [ 25 | "react-app", 26 | "react-app/jest" 27 | ] 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.2%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /5.3-Local_Storage_And_Access_Token/todo-react-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/5.3-Local_Storage_And_Access_Token/todo-react-app/public/favicon.ico -------------------------------------------------------------------------------- /5.3-Local_Storage_And_Access_Token/todo-react-app/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/5.3-Local_Storage_And_Access_Token/todo-react-app/public/logo192.png -------------------------------------------------------------------------------- /5.3-Local_Storage_And_Access_Token/todo-react-app/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/5.3-Local_Storage_And_Access_Token/todo-react-app/public/logo512.png -------------------------------------------------------------------------------- /5.3-Local_Storage_And_Access_Token/todo-react-app/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /5.3-Local_Storage_And_Access_Token/todo-react-app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /5.3-Local_Storage_And_Access_Token/todo-react-app/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /5.3-Local_Storage_And_Access_Token/todo-react-app/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /5.3-Local_Storage_And_Access_Token/todo-react-app/src/AppRouter.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./index.css"; 3 | import App from "./App"; 4 | import Login from "./Login"; 5 | import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; 6 | import Box from "@material-ui/core/Box"; 7 | import Typography from "@material-ui/core/Typography"; 8 | 9 | function Copyright() { 10 | return ( 11 | 12 | {"Copyright © "} 13 | fsoftwareengineer, {new Date().getFullYear()} 14 | {"."} 15 | 16 | ); 17 | } 18 | 19 | class AppRouter extends React.Component { 20 | render() { 21 | return ( 22 |
23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 | 35 | 36 | 37 |
38 |
39 | ); 40 | } 41 | } 42 | 43 | export default AppRouter; 44 | -------------------------------------------------------------------------------- /5.3-Local_Storage_And_Access_Token/todo-react-app/src/app-config.js: -------------------------------------------------------------------------------- 1 | let backendHost; 2 | 3 | const hostname = window && window.location && window.location.hostname; 4 | 5 | if (hostname === "localhost") { 6 | backendHost = "http://localhost:8080"; 7 | } 8 | 9 | export const API_BASE_URL = `${backendHost}`; 10 | -------------------------------------------------------------------------------- /5.3-Local_Storage_And_Access_Token/todo-react-app/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /5.3-Local_Storage_And_Access_Token/todo-react-app/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "./index.css"; 4 | import AppRouter from "./AppRouter"; 5 | import reportWebVitals from "./reportWebVitals"; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById("root") 12 | ); 13 | // If you want to start measuring performance in your app, pass a function 14 | // to log results (for example: reportWebVitals(console.log)) 15 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 16 | reportWebVitals(); 17 | -------------------------------------------------------------------------------- /5.3-Local_Storage_And_Access_Token/todo-react-app/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /5.3-Local_Storage_And_Access_Token/todo-react-app/src/service/ApiService.js: -------------------------------------------------------------------------------- 1 | import { API_BASE_URL } from "../app-config"; 2 | const ACCESS_TOKEN = "ACCESS_TOKEN"; 3 | 4 | export function call(api, method, request) { 5 | let headers = new Headers({ 6 | "Content-Type": "application/json", 7 | }); 8 | 9 | // 로컬 스토리지에서 ACCESS TOKEN 가져오기 10 | const accessToken = localStorage.getItem("ACCESS_TOKEN"); 11 | if (accessToken && accessToken !== null) { 12 | headers.append("Authorization", "Bearer " + accessToken); 13 | } 14 | 15 | let options = { 16 | headers: headers, 17 | url: API_BASE_URL + api, 18 | method: method, 19 | }; 20 | 21 | if (request) { 22 | // GET method 23 | options.body = JSON.stringify(request); 24 | } 25 | return fetch(options.url, options) 26 | .then((response) => 27 | response.json().then((json) => { 28 | if (!response.ok) { 29 | // response.ok가 true이면 정상적인 리스폰스를 받은것, 아니면 에러 리스폰스를 받은것. 30 | return Promise.reject(json); 31 | } 32 | return json; 33 | }) 34 | ) 35 | .catch((error) => { 36 | // 추가된 부분 37 | console.log(error.status); 38 | if (error.status === 403) { 39 | window.location.href = "/login"; // redirect 40 | } 41 | return Promise.reject(error); 42 | }); 43 | } 44 | 45 | export function signin(userDTO) { 46 | return call("/auth/signin", "POST", userDTO).then((response) => { 47 | if (response.token) { 48 | // 로컬 스토리지에 토큰 저장 49 | localStorage.setItem(ACCESS_TOKEN, response.token); 50 | // token이 존재하는 경우 Todo 화면으로 리디렉트 51 | window.location.href = "/"; 52 | } 53 | }); 54 | } 55 | -------------------------------------------------------------------------------- /5.3-Local_Storage_And_Access_Token/todo-react-app/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /5.4-Logout_And_Fix_Glitch/todo-react-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /5.4-Logout_And_Fix_Glitch/todo-react-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo-react-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^4.11.3", 7 | "@material-ui/icons": "^4.11.2", 8 | "@testing-library/jest-dom": "^5.11.9", 9 | "@testing-library/react": "^11.2.5", 10 | "@testing-library/user-event": "^12.8.1", 11 | "react": "^17.0.1", 12 | "react-dom": "^17.0.1", 13 | "react-router-dom": "^5.2.0", 14 | "react-scripts": "4.0.3", 15 | "web-vitals": "^1.1.0" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "test": "react-scripts test", 21 | "eject": "react-scripts eject" 22 | }, 23 | "eslintConfig": { 24 | "extends": [ 25 | "react-app", 26 | "react-app/jest" 27 | ] 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.2%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /5.4-Logout_And_Fix_Glitch/todo-react-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/5.4-Logout_And_Fix_Glitch/todo-react-app/public/favicon.ico -------------------------------------------------------------------------------- /5.4-Logout_And_Fix_Glitch/todo-react-app/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/5.4-Logout_And_Fix_Glitch/todo-react-app/public/logo192.png -------------------------------------------------------------------------------- /5.4-Logout_And_Fix_Glitch/todo-react-app/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/5.4-Logout_And_Fix_Glitch/todo-react-app/public/logo512.png -------------------------------------------------------------------------------- /5.4-Logout_And_Fix_Glitch/todo-react-app/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /5.4-Logout_And_Fix_Glitch/todo-react-app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /5.4-Logout_And_Fix_Glitch/todo-react-app/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /5.4-Logout_And_Fix_Glitch/todo-react-app/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /5.4-Logout_And_Fix_Glitch/todo-react-app/src/AppRouter.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./index.css"; 3 | import App from "./App"; 4 | import Login from "./Login"; 5 | import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; 6 | import Box from "@material-ui/core/Box"; 7 | import Typography from "@material-ui/core/Typography"; 8 | 9 | function Copyright() { 10 | return ( 11 | 12 | {"Copyright © "} 13 | fsoftwareengineer, {new Date().getFullYear()} 14 | {"."} 15 | 16 | ); 17 | } 18 | 19 | class AppRouter extends React.Component { 20 | render() { 21 | return ( 22 |
23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 | 35 | 36 | 37 |
38 |
39 | ); 40 | } 41 | } 42 | 43 | export default AppRouter; 44 | -------------------------------------------------------------------------------- /5.4-Logout_And_Fix_Glitch/todo-react-app/src/app-config.js: -------------------------------------------------------------------------------- 1 | let backendHost; 2 | 3 | const hostname = window && window.location && window.location.hostname; 4 | 5 | if (hostname === "localhost") { 6 | backendHost = "http://localhost:8080"; 7 | } 8 | 9 | export const API_BASE_URL = `${backendHost}`; 10 | -------------------------------------------------------------------------------- /5.4-Logout_And_Fix_Glitch/todo-react-app/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /5.4-Logout_And_Fix_Glitch/todo-react-app/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "./index.css"; 4 | import AppRouter from "./AppRouter"; 5 | import reportWebVitals from "./reportWebVitals"; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById("root") 12 | ); 13 | // If you want to start measuring performance in your app, pass a function 14 | // to log results (for example: reportWebVitals(console.log)) 15 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 16 | reportWebVitals(); 17 | -------------------------------------------------------------------------------- /5.4-Logout_And_Fix_Glitch/todo-react-app/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /5.4-Logout_And_Fix_Glitch/todo-react-app/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /5.5-Sign_Up_Page/todo-react-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /5.5-Sign_Up_Page/todo-react-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo-react-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^4.11.3", 7 | "@material-ui/icons": "^4.11.2", 8 | "@testing-library/jest-dom": "^5.11.9", 9 | "@testing-library/react": "^11.2.5", 10 | "@testing-library/user-event": "^12.8.1", 11 | "react": "^17.0.1", 12 | "react-dom": "^17.0.1", 13 | "react-router-dom": "^5.2.0", 14 | "react-scripts": "4.0.3", 15 | "web-vitals": "^1.1.0" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "test": "react-scripts test", 21 | "eject": "react-scripts eject" 22 | }, 23 | "eslintConfig": { 24 | "extends": [ 25 | "react-app", 26 | "react-app/jest" 27 | ] 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.2%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /5.5-Sign_Up_Page/todo-react-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/5.5-Sign_Up_Page/todo-react-app/public/favicon.ico -------------------------------------------------------------------------------- /5.5-Sign_Up_Page/todo-react-app/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/5.5-Sign_Up_Page/todo-react-app/public/logo192.png -------------------------------------------------------------------------------- /5.5-Sign_Up_Page/todo-react-app/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/5.5-Sign_Up_Page/todo-react-app/public/logo512.png -------------------------------------------------------------------------------- /5.5-Sign_Up_Page/todo-react-app/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /5.5-Sign_Up_Page/todo-react-app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /5.5-Sign_Up_Page/todo-react-app/src/AddTodo.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { TextField, Paper, Button, Grid } from "@material-ui/core"; 3 | 4 | class AddTodo extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this.state = { item: { title: "" } }; 8 | this.add = props.add; 9 | } 10 | 11 | onInputChange = (e) => { 12 | const thisItem = this.state.item; 13 | thisItem.title = e.target.value; 14 | this.setState({ item: thisItem }); 15 | console.log(thisItem); 16 | }; 17 | 18 | onButtonClick = () => { 19 | this.add(this.state.item); 20 | this.setState({ item: { title: "" } }); 21 | }; 22 | 23 | enterKeyEventHandler = (e) => { 24 | if (e.key === "Enter") { 25 | this.onButtonClick(); 26 | } 27 | }; 28 | 29 | render() { 30 | return ( 31 | 32 | 33 | 34 | 41 | 42 | 43 | 51 | 52 | 53 | 54 | ); 55 | } 56 | } 57 | 58 | export default AddTodo; 59 | -------------------------------------------------------------------------------- /5.5-Sign_Up_Page/todo-react-app/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /5.5-Sign_Up_Page/todo-react-app/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /5.5-Sign_Up_Page/todo-react-app/src/AppRouter.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./index.css"; 3 | import App from "./App"; 4 | import Login from "./Login"; 5 | import SignUp from "./SignUp"; 6 | import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; 7 | import Box from "@material-ui/core/Box"; 8 | import Typography from "@material-ui/core/Typography"; 9 | 10 | function Copyright() { 11 | return ( 12 | 13 | {"Copyright © "} 14 | fsoftwareengineer, {new Date().getFullYear()} 15 | {"."} 16 | 17 | ); 18 | } 19 | 20 | class AppRouter extends React.Component { 21 | render() { 22 | return ( 23 |
24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
38 | 39 | 40 | 41 |
42 |
43 | ); 44 | } 45 | } 46 | 47 | export default AppRouter; 48 | -------------------------------------------------------------------------------- /5.5-Sign_Up_Page/todo-react-app/src/app-config.js: -------------------------------------------------------------------------------- 1 | let backendHost; 2 | 3 | const hostname = window && window.location && window.location.hostname; 4 | 5 | if (hostname === "localhost") { 6 | backendHost = "http://localhost:8080"; 7 | } 8 | 9 | export const API_BASE_URL = `${backendHost}`; 10 | -------------------------------------------------------------------------------- /5.5-Sign_Up_Page/todo-react-app/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /5.5-Sign_Up_Page/todo-react-app/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "./index.css"; 4 | import AppRouter from "./AppRouter"; 5 | import reportWebVitals from "./reportWebVitals"; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById("root") 12 | ); 13 | // If you want to start measuring performance in your app, pass a function 14 | // to log results (for example: reportWebVitals(console.log)) 15 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 16 | reportWebVitals(); 17 | -------------------------------------------------------------------------------- /5.5-Sign_Up_Page/todo-react-app/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /5.5-Sign_Up_Page/todo-react-app/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/demo/.ebextensions/application.config: -------------------------------------------------------------------------------- 1 | option_settings: 2 | aws:elasticbeanstalk:application:environment: 3 | SPRING_PROFILES_ACTIVE: prod 4 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/demo/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | 39 | # Elastic Beanstalk Files 40 | .elasticbeanstalk/* 41 | !.elasticbeanstalk/*.cfg.yml 42 | !.elasticbeanstalk/*.global.yml 43 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/demo/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.5.0-SNAPSHOT' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.example' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = '1.8' 10 | 11 | configurations { 12 | compileOnly { 13 | extendsFrom annotationProcessor 14 | } 15 | } 16 | 17 | repositories { 18 | mavenCentral() 19 | maven { url 'https://repo.spring.io/milestone' } 20 | maven { url 'https://repo.spring.io/snapshot' } 21 | } 22 | 23 | dependencies { 24 | implementation 'org.springframework.boot:spring-boot-starter-data-jpa' 25 | implementation 'org.springframework.boot:spring-boot-starter-web' 26 | implementation 'org.springframework.boot:spring-boot-starter-security' 27 | 28 | // https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt 29 | compile group: 'io.jsonwebtoken', name: 'jjwt', version: '0.9.1' 30 | 31 | compileOnly 'org.projectlombok:lombok' 32 | runtimeOnly 'com.h2database:h2' 33 | runtimeOnly 'mysql:mysql-connector-java' 34 | annotationProcessor 'org.projectlombok:lombok' 35 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 36 | // 37 | compile group: 'com.google.guava', name: 'guava', version: '28.1-jre' 38 | } 39 | 40 | test { 41 | useJUnitPlatform() 42 | } 43 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/demo/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/demo/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/demo/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Mar 20 21:07:50 PDT 2021 2 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-all.zip 3 | distributionBase=GRADLE_USER_HOME 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/demo/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { url 'https://repo.spring.io/milestone' } 4 | maven { url 'https://repo.spring.io/snapshot' } 5 | gradlePluginPortal() 6 | } 7 | } 8 | rootProject.name = 'demo' 9 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/demo/src/main/java/com/example/demo/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class DemoApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(DemoApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/demo/src/main/java/com/example/demo/DemoModel.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import lombok.Builder; 4 | import lombok.NonNull; 5 | import lombok.RequiredArgsConstructor; 6 | 7 | @Builder 8 | @RequiredArgsConstructor 9 | public class DemoModel { 10 | 11 | @NonNull 12 | private String id; 13 | } 14 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/demo/src/main/java/com/example/demo/config/WebMvcConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.config; 2 | import org.springframework.context.annotation.Configuration; 3 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 4 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 5 | 6 | @Configuration 7 | public class WebMvcConfig implements WebMvcConfigurer { 8 | 9 | private final long MAX_AGE_SECS = 3600; 10 | 11 | @Override 12 | public void addCorsMappings(CorsRegistry registry) { 13 | registry.addMapping("/**") 14 | .allowedOrigins("http://localhost:3000") 15 | .allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS") 16 | .allowedHeaders("*") 17 | .allowCredentials(true) 18 | .maxAge(MAX_AGE_SECS); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/demo/src/main/java/com/example/demo/controller/HealthCheckController.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.controller; 2 | 3 | import org.springframework.web.bind.annotation.GetMapping; 4 | import org.springframework.web.bind.annotation.RestController; 5 | 6 | @RestController 7 | public class HealthCheckController { 8 | 9 | @GetMapping("/") 10 | public String healthCheck() { 11 | return "The service is up and running..."; 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/demo/src/main/java/com/example/demo/dto/ResponseDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.dto; 2 | 3 | import java.util.List; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | @Builder 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | @Data 13 | public class ResponseDTO { 14 | private String error; 15 | private List data; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/demo/src/main/java/com/example/demo/dto/TestRequestBodyDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.dto; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class TestRequestBodyDTO { 7 | private int id; 8 | private String message; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/demo/src/main/java/com/example/demo/dto/TodoDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.dto; 2 | 3 | import com.example.demo.model.TodoEntity; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | @Data 11 | public class TodoDTO { 12 | private String id; 13 | private String title; 14 | private boolean done; 15 | 16 | public TodoDTO(final TodoEntity entity) { 17 | this.id = entity.getId(); 18 | this.title = entity.getTitle(); 19 | this.done = entity.isDone(); 20 | } 21 | 22 | public static TodoEntity toEntity(final TodoDTO dto) { 23 | return TodoEntity.builder() 24 | .id(dto.getId()) 25 | .title(dto.getTitle()) 26 | .done(dto.isDone()) 27 | .build(); 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/demo/src/main/java/com/example/demo/dto/UserDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @Builder 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | public class UserDTO { 13 | private String token; 14 | private String email; 15 | private String username; 16 | private String password; 17 | private String id; 18 | } 19 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/demo/src/main/java/com/example/demo/model/TodoEntity.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.model; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.GeneratedValue; 5 | import javax.persistence.Id; 6 | import javax.persistence.Table; 7 | 8 | import lombok.AllArgsConstructor; 9 | import lombok.Builder; 10 | import lombok.Data; 11 | import lombok.NoArgsConstructor; 12 | import org.hibernate.annotations.GenericGenerator; 13 | 14 | @Builder 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | @Data 18 | @Entity 19 | @Table(name = "Todo") 20 | public class TodoEntity { 21 | @Id 22 | @GeneratedValue(generator="system-uuid") 23 | @GenericGenerator(name="system-uuid", strategy = "uuid") 24 | private String id; 25 | private String userId; 26 | private String title; 27 | private boolean done; 28 | } 29 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/demo/src/main/java/com/example/demo/model/UserEntity.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | import org.hibernate.annotations.GenericGenerator; 9 | 10 | import javax.persistence.Column; 11 | import javax.persistence.Entity; 12 | import javax.persistence.GeneratedValue; 13 | import javax.persistence.Id; 14 | import javax.persistence.Table; 15 | import javax.persistence.UniqueConstraint; 16 | 17 | @Data 18 | @Entity 19 | @Builder 20 | @NoArgsConstructor 21 | @AllArgsConstructor 22 | @Table(uniqueConstraints = {@UniqueConstraint(columnNames = "email")}) 23 | public class UserEntity { 24 | @Id 25 | @GeneratedValue(generator="system-uuid") 26 | @GenericGenerator(name="system-uuid", strategy = "uuid") 27 | private String id; // 유저에게 고유하게 부여되는 id. 28 | 29 | @Column(nullable = false) 30 | private String username; // 유저의 이름 31 | 32 | @Column(nullable = false) 33 | private String email; // 유저의 email, 아이디와 같은 기능을 한다. 34 | 35 | @Column(nullable = false) 36 | private String password; // 패스워드. null이 가능한 이유는 oAuth로 페이스북이나 트위터같은 제3의 어플리케이션을 통해 로그인 할 수 있게 하기 위함이다. 37 | } 38 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/demo/src/main/java/com/example/demo/persistence/TodoRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.persistence; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.stereotype.Repository; 5 | import com.example.demo.model.TodoEntity; 6 | 7 | import java.util.List; 8 | 9 | @Repository 10 | public interface TodoRepository extends JpaRepository{ 11 | List findByUserId(String userId); 12 | } 13 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/demo/src/main/java/com/example/demo/persistence/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.persistence; 2 | 3 | import com.example.demo.model.UserEntity; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface UserRepository extends JpaRepository { 9 | 10 | UserEntity findByEmail(String email); 11 | Boolean existsByEmail(String email); 12 | UserEntity findByEmailAndPassword(String email, String password); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/demo/src/main/java/com/example/demo/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.service; 2 | 3 | import com.example.demo.model.UserEntity; 4 | import com.example.demo.persistence.UserRepository; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 8 | import org.springframework.security.crypto.password.PasswordEncoder; 9 | import org.springframework.stereotype.Service; 10 | 11 | import java.util.Optional; 12 | 13 | @Slf4j 14 | @Service 15 | public class UserService { 16 | 17 | @Autowired 18 | private UserRepository userRepository; 19 | 20 | public UserEntity create(final UserEntity userEntity) { 21 | if(userEntity == null || userEntity.getEmail() == null ) { 22 | throw new RuntimeException("Invalid arguments"); 23 | } 24 | final String email = userEntity.getEmail(); 25 | if(userRepository.existsByEmail(email)) { 26 | log.warn("Email already exists {}", email); 27 | throw new RuntimeException("Email already exists"); 28 | } 29 | 30 | return userRepository.save(userEntity); 31 | } 32 | 33 | public UserEntity getByCredentials(final String email, final String password, final PasswordEncoder encoder) { 34 | final UserEntity originalUser = userRepository.findByEmail(email); 35 | 36 | // matches 메서드를 이용해 패스워드가 같은지 확인 37 | if(originalUser != null && encoder.matches(password, originalUser.getPassword())) { 38 | return originalUser; 39 | } 40 | return null; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/demo/src/main/resources/application-dev.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/demo/src/main/resources/application-dev.yaml -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/demo/src/main/resources/application-prod.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | port : 5000 3 | spring: 4 | jpa: 5 | database: MYSQL 6 | show-sql: true 7 | database-platform: org.hibernate.dialect.MySQL8Dialect 8 | hibernate: 9 | ddl-auto: update 10 | datasource: 11 | url: jdbc:mysql://${rds.hostname}:${rds.port}/${rds.db.name} 12 | username: ${rds.username} 13 | password: ${rds.password} 14 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/demo/src/test/java/com/example/demo/DemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class DemoApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/todo-react-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/todo-react-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo-react-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^4.11.3", 7 | "@material-ui/icons": "^4.11.2", 8 | "@testing-library/jest-dom": "^5.11.9", 9 | "@testing-library/react": "^11.2.5", 10 | "@testing-library/user-event": "^12.8.1", 11 | "react": "^17.0.1", 12 | "react-dom": "^17.0.1", 13 | "react-router-dom": "^5.2.0", 14 | "react-scripts": "4.0.3", 15 | "web-vitals": "^1.1.0" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "test": "react-scripts test", 21 | "eject": "react-scripts eject" 22 | }, 23 | "eslintConfig": { 24 | "extends": [ 25 | "react-app", 26 | "react-app/jest" 27 | ] 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.2%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/todo-react-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/todo-react-app/public/favicon.ico -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/todo-react-app/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/todo-react-app/public/logo192.png -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/todo-react-app/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/todo-react-app/public/logo512.png -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/todo-react-app/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/todo-react-app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/todo-react-app/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/todo-react-app/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/todo-react-app/src/AppRouter.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./index.css"; 3 | import App from "./App"; 4 | import Login from "./Login"; 5 | import SignUp from "./SignUp"; 6 | import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; 7 | import Box from "@material-ui/core/Box"; 8 | import Typography from "@material-ui/core/Typography"; 9 | 10 | function Copyright() { 11 | return ( 12 | 13 | {"Copyright © "} 14 | fsoftwareengineer, {new Date().getFullYear()} 15 | {"."} 16 | 17 | ); 18 | } 19 | 20 | class AppRouter extends React.Component { 21 | render() { 22 | return ( 23 |
24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
38 | 39 | 40 | 41 |
42 |
43 | ); 44 | } 45 | } 46 | 47 | export default AppRouter; 48 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/todo-react-app/src/app-config.js: -------------------------------------------------------------------------------- 1 | let backendHost; 2 | 3 | const hostname = window && window.location && window.location.hostname; 4 | 5 | if (hostname === "localhost") { 6 | //"http://localhost:8080"; 7 | backendHost = "http://prod-todo-backend.us-west-2.elasticbeanstalk.com"; 8 | } 9 | 10 | export const API_BASE_URL = `${backendHost}`; 11 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/todo-react-app/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/todo-react-app/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "./index.css"; 4 | import AppRouter from "./AppRouter"; 5 | import reportWebVitals from "./reportWebVitals"; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById("root") 12 | ); 13 | // If you want to start measuring performance in your app, pass a function 14 | // to log results (for example: reportWebVitals(console.log)) 15 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 16 | reportWebVitals(); 17 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/todo-react-app/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /6.3-Backend_Deployment_With_AWS_ElasticBeanstalk/todo-react-app/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/demo/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | 39 | # Elastic Beanstalk Files 40 | .elasticbeanstalk/* 41 | !.elasticbeanstalk/*.cfg.yml 42 | !.elasticbeanstalk/*.global.yml 43 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/demo/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.5.0-SNAPSHOT' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.example' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = '1.8' 10 | 11 | configurations { 12 | compileOnly { 13 | extendsFrom annotationProcessor 14 | } 15 | } 16 | 17 | repositories { 18 | mavenCentral() 19 | maven { url 'https://repo.spring.io/milestone' } 20 | maven { url 'https://repo.spring.io/snapshot' } 21 | } 22 | 23 | dependencies { 24 | implementation 'org.springframework.boot:spring-boot-starter-data-jpa' 25 | implementation 'org.springframework.boot:spring-boot-starter-web' 26 | implementation 'org.springframework.boot:spring-boot-starter-security' 27 | 28 | // https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt 29 | implementation group: 'io.jsonwebtoken', name: 'jjwt', version: '0.9.1' 30 | 31 | compileOnly 'org.projectlombok:lombok' 32 | runtimeOnly 'com.h2database:h2' 33 | runtimeOnly 'mysql:mysql-connector-java' 34 | annotationProcessor 'org.projectlombok:lombok' 35 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 36 | // 37 | implementation group: 'com.google.guava', name: 'guava', version: '28.1-jre' 38 | } 39 | 40 | test { 41 | useJUnitPlatform() 42 | } 43 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/demo/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/demo/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/demo/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Mar 20 21:07:50 PDT 2021 2 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-all.zip 3 | distributionBase=GRADLE_USER_HOME 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/demo/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { url 'https://repo.spring.io/milestone' } 4 | maven { url 'https://repo.spring.io/snapshot' } 5 | gradlePluginPortal() 6 | } 7 | } 8 | rootProject.name = 'demo' 9 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/demo/src/main/java/com/example/demo/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class DemoApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(DemoApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/demo/src/main/java/com/example/demo/DemoModel.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import lombok.Builder; 4 | import lombok.NonNull; 5 | import lombok.RequiredArgsConstructor; 6 | 7 | @Builder 8 | @RequiredArgsConstructor 9 | public class DemoModel { 10 | 11 | @NonNull 12 | private String id; 13 | } 14 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/demo/src/main/java/com/example/demo/config/WebMvcConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.config; 2 | import org.springframework.context.annotation.Configuration; 3 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 4 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 5 | 6 | @Configuration 7 | public class WebMvcConfig implements WebMvcConfigurer { 8 | 9 | private final long MAX_AGE_SECS = 3600; 10 | 11 | @Override 12 | public void addCorsMappings(CorsRegistry registry) { 13 | registry.addMapping("/**") 14 | .allowedOrigins("http://localhost:3000", "http://prod-todo-frontend.us-west-2.elasticbeanstalk.com") 15 | .allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS") 16 | .allowedHeaders("*") 17 | .allowCredentials(true) 18 | .maxAge(MAX_AGE_SECS); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/demo/src/main/java/com/example/demo/controller/HealthCheckController.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.controller; 2 | 3 | import org.springframework.web.bind.annotation.GetMapping; 4 | import org.springframework.web.bind.annotation.RestController; 5 | 6 | @RestController 7 | public class HealthCheckController { 8 | 9 | @GetMapping("/") 10 | public String healthCheck() { 11 | return "The service is up and running..."; 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/demo/src/main/java/com/example/demo/dto/ResponseDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.dto; 2 | 3 | import java.util.List; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | @Builder 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | @Data 13 | public class ResponseDTO { 14 | private String error; 15 | private List data; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/demo/src/main/java/com/example/demo/dto/TestRequestBodyDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.dto; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class TestRequestBodyDTO { 7 | private int id; 8 | private String message; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/demo/src/main/java/com/example/demo/dto/TodoDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.dto; 2 | 3 | import com.example.demo.model.TodoEntity; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | @Data 11 | public class TodoDTO { 12 | private String id; 13 | private String title; 14 | private boolean done; 15 | 16 | public TodoDTO(final TodoEntity entity) { 17 | this.id = entity.getId(); 18 | this.title = entity.getTitle(); 19 | this.done = entity.isDone(); 20 | } 21 | 22 | public static TodoEntity toEntity(final TodoDTO dto) { 23 | return TodoEntity.builder() 24 | .id(dto.getId()) 25 | .title(dto.getTitle()) 26 | .done(dto.isDone()) 27 | .build(); 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/demo/src/main/java/com/example/demo/dto/UserDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @Builder 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | public class UserDTO { 13 | private String token; 14 | private String email; 15 | private String username; 16 | private String password; 17 | private String id; 18 | } 19 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/demo/src/main/java/com/example/demo/model/TodoEntity.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.model; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.GeneratedValue; 5 | import javax.persistence.Id; 6 | import javax.persistence.Table; 7 | 8 | import lombok.AllArgsConstructor; 9 | import lombok.Builder; 10 | import lombok.Data; 11 | import lombok.NoArgsConstructor; 12 | import org.hibernate.annotations.GenericGenerator; 13 | 14 | @Builder 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | @Data 18 | @Entity 19 | @Table(name = "Todo") 20 | public class TodoEntity { 21 | @Id 22 | @GeneratedValue(generator="system-uuid") 23 | @GenericGenerator(name="system-uuid", strategy = "uuid") 24 | private String id; 25 | private String userId; 26 | private String title; 27 | private boolean done; 28 | } 29 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/demo/src/main/java/com/example/demo/model/UserEntity.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | import org.hibernate.annotations.GenericGenerator; 9 | 10 | import javax.persistence.Column; 11 | import javax.persistence.Entity; 12 | import javax.persistence.GeneratedValue; 13 | import javax.persistence.Id; 14 | import javax.persistence.Table; 15 | import javax.persistence.UniqueConstraint; 16 | 17 | @Data 18 | @Entity 19 | @Builder 20 | @NoArgsConstructor 21 | @AllArgsConstructor 22 | @Table(uniqueConstraints = {@UniqueConstraint(columnNames = "email")}) 23 | public class UserEntity { 24 | @Id 25 | @GeneratedValue(generator="system-uuid") 26 | @GenericGenerator(name="system-uuid", strategy = "uuid") 27 | private String id; // 유저에게 고유하게 부여되는 id. 28 | 29 | @Column(nullable = false) 30 | private String username; // 유저의 이름 31 | 32 | @Column(nullable = false) 33 | private String email; // 유저의 email, 아이디와 같은 기능을 한다. 34 | 35 | @Column(nullable = false) 36 | private String password; // 패스워드. null이 가능한 이유는 oAuth로 페이스북이나 트위터같은 제3의 어플리케이션을 통해 로그인 할 수 있게 하기 위함이다. 37 | } 38 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/demo/src/main/java/com/example/demo/persistence/TodoRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.persistence; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.stereotype.Repository; 5 | import com.example.demo.model.TodoEntity; 6 | 7 | import java.util.List; 8 | 9 | @Repository 10 | public interface TodoRepository extends JpaRepository{ 11 | List findByUserId(String userId); 12 | } 13 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/demo/src/main/java/com/example/demo/persistence/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.persistence; 2 | 3 | import com.example.demo.model.UserEntity; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface UserRepository extends JpaRepository { 9 | 10 | UserEntity findByEmail(String email); 11 | Boolean existsByEmail(String email); 12 | UserEntity findByEmailAndPassword(String email, String password); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/demo/src/main/java/com/example/demo/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.service; 2 | 3 | import com.example.demo.model.UserEntity; 4 | import com.example.demo.persistence.UserRepository; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 8 | import org.springframework.security.crypto.password.PasswordEncoder; 9 | import org.springframework.stereotype.Service; 10 | 11 | import java.util.Optional; 12 | 13 | @Slf4j 14 | @Service 15 | public class UserService { 16 | 17 | @Autowired 18 | private UserRepository userRepository; 19 | 20 | public UserEntity create(final UserEntity userEntity) { 21 | if(userEntity == null || userEntity.getEmail() == null ) { 22 | throw new RuntimeException("Invalid arguments"); 23 | } 24 | final String email = userEntity.getEmail(); 25 | if(userRepository.existsByEmail(email)) { 26 | log.warn("Email already exists {}", email); 27 | throw new RuntimeException("Email already exists"); 28 | } 29 | 30 | return userRepository.save(userEntity); 31 | } 32 | 33 | public UserEntity getByCredentials(final String email, final String password, final PasswordEncoder encoder) { 34 | final UserEntity originalUser = userRepository.findByEmail(email); 35 | 36 | // matches 메서드를 이용해 패스워드가 같은지 확인 37 | if(originalUser != null && encoder.matches(password, originalUser.getPassword())) { 38 | return originalUser; 39 | } 40 | return null; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/demo/src/main/resources/application-dev.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/demo/src/main/resources/application-dev.yaml -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/demo/src/main/resources/application-prod.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | port : 5000 3 | spring: 4 | jpa: 5 | database: MYSQL 6 | show-sql: true 7 | database-platform: org.hibernate.dialect.MySQL8Dialect 8 | hibernate: 9 | ddl-auto: update 10 | datasource: 11 | url: jdbc:mysql://${rds.hostname}:${rds.port}/${rds.db.name} 12 | username: ${rds.username} 13 | password: ${rds.password} 14 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/demo/src/test/java/com/example/demo/DemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class DemoApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/todo-react-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | # Elastic Beanstalk Files 26 | .elasticbeanstalk/* 27 | !.elasticbeanstalk/*.cfg.yml 28 | !.elasticbeanstalk/*.global.yml 29 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/todo-react-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo-react-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^4.11.3", 7 | "@material-ui/icons": "^4.11.2", 8 | "@testing-library/jest-dom": "^5.11.9", 9 | "@testing-library/react": "^11.2.5", 10 | "@testing-library/user-event": "^12.8.1", 11 | "react": "^17.0.1", 12 | "react-dom": "^17.0.1", 13 | "react-router-dom": "^5.2.0", 14 | "react-scripts": "4.0.3", 15 | "web-vitals": "^1.1.0" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "GENERATE_SOURCEMAP=false react-scripts build", 20 | "test": "react-scripts test", 21 | "eject": "react-scripts eject" 22 | }, 23 | "eslintConfig": { 24 | "extends": [ 25 | "react-app", 26 | "react-app/jest" 27 | ] 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.2%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/todo-react-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/todo-react-app/public/favicon.ico -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/todo-react-app/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/todo-react-app/public/logo192.png -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/todo-react-app/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/todo-react-app/public/logo512.png -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/todo-react-app/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/todo-react-app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/todo-react-app/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/todo-react-app/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/todo-react-app/src/AppRouter.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./index.css"; 3 | import App from "./App"; 4 | import Login from "./Login"; 5 | import SignUp from "./SignUp"; 6 | import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; 7 | import Box from "@material-ui/core/Box"; 8 | import Typography from "@material-ui/core/Typography"; 9 | 10 | function Copyright() { 11 | return ( 12 | 13 | {"Copyright © "} 14 | fsoftwareengineer, {new Date().getFullYear()} 15 | {"."} 16 | 17 | ); 18 | } 19 | 20 | class AppRouter extends React.Component { 21 | render() { 22 | return ( 23 |
24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
38 | 39 | 40 | 41 |
42 |
43 | ); 44 | } 45 | } 46 | 47 | export default AppRouter; 48 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/todo-react-app/src/app-config.js: -------------------------------------------------------------------------------- 1 | let backendHost; 2 | 3 | const hostname = window && window.location && window.location.hostname; 4 | 5 | if (hostname === "localhost") { 6 | backendHost = "http://localhost:8080"; 7 | } else { 8 | backendHost = "http://prod-todo-backend.us-west-2.elasticbeanstalk.com"; 9 | } 10 | 11 | export const API_BASE_URL = `${backendHost}`; 12 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/todo-react-app/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/todo-react-app/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "./index.css"; 4 | import AppRouter from "./AppRouter"; 5 | import reportWebVitals from "./reportWebVitals"; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById("root") 12 | ); 13 | // If you want to start measuring performance in your app, pass a function 14 | // to log results (for example: reportWebVitals(console.log)) 15 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 16 | reportWebVitals(); 17 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/todo-react-app/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /6.4-Frontend_Deployment_With_AWS_ElasticBeantalk/todo-react-app/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/demo/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | 39 | # Elastic Beanstalk Files 40 | .elasticbeanstalk/* 41 | !.elasticbeanstalk/*.cfg.yml 42 | !.elasticbeanstalk/*.global.yml 43 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/demo/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.5.0-SNAPSHOT' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.example' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = '1.8' 10 | 11 | configurations { 12 | compileOnly { 13 | extendsFrom annotationProcessor 14 | } 15 | } 16 | 17 | repositories { 18 | mavenCentral() 19 | maven { url 'https://repo.spring.io/milestone' } 20 | maven { url 'https://repo.spring.io/snapshot' } 21 | } 22 | 23 | dependencies { 24 | implementation 'org.springframework.boot:spring-boot-starter-data-jpa' 25 | implementation 'org.springframework.boot:spring-boot-starter-web' 26 | implementation 'org.springframework.boot:spring-boot-starter-security' 27 | 28 | // https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt 29 | implementation group: 'io.jsonwebtoken', name: 'jjwt', version: '0.9.1' 30 | 31 | compileOnly 'org.projectlombok:lombok' 32 | runtimeOnly 'com.h2database:h2' 33 | runtimeOnly 'mysql:mysql-connector-java' 34 | annotationProcessor 'org.projectlombok:lombok' 35 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 36 | // 37 | implementation group: 'com.google.guava', name: 'guava', version: '28.1-jre' 38 | } 39 | 40 | test { 41 | useJUnitPlatform() 42 | } 43 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/demo/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/6.5-Route53-Domain-Configuration/demo/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/demo/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Mar 20 21:07:50 PDT 2021 2 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-all.zip 3 | distributionBase=GRADLE_USER_HOME 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/demo/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { url 'https://repo.spring.io/milestone' } 4 | maven { url 'https://repo.spring.io/snapshot' } 5 | gradlePluginPortal() 6 | } 7 | } 8 | rootProject.name = 'demo' 9 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/demo/src/main/java/com/example/demo/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class DemoApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(DemoApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/demo/src/main/java/com/example/demo/DemoModel.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import lombok.Builder; 4 | import lombok.NonNull; 5 | import lombok.RequiredArgsConstructor; 6 | 7 | @Builder 8 | @RequiredArgsConstructor 9 | public class DemoModel { 10 | 11 | @NonNull 12 | private String id; 13 | } 14 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/demo/src/main/java/com/example/demo/config/WebMvcConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.config; 2 | import org.springframework.context.annotation.Configuration; 3 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 4 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 5 | 6 | @Configuration 7 | public class WebMvcConfig implements WebMvcConfigurer { 8 | 9 | private final long MAX_AGE_SECS = 3600; 10 | 11 | @Override 12 | public void addCorsMappings(CorsRegistry registry) { 13 | registry.addMapping("/**") 14 | .allowedOrigins("http://localhost:3000", "http://prod-todo-frontend.us-west-2.elasticbeanstalk.com") 15 | .allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS") 16 | .allowedHeaders("*") 17 | .allowCredentials(true) 18 | .maxAge(MAX_AGE_SECS); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/demo/src/main/java/com/example/demo/controller/HealthCheckController.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.controller; 2 | 3 | import org.springframework.web.bind.annotation.GetMapping; 4 | import org.springframework.web.bind.annotation.RestController; 5 | 6 | @RestController 7 | public class HealthCheckController { 8 | 9 | @GetMapping("/") 10 | public String healthCheck() { 11 | return "The service is up and running..."; 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/demo/src/main/java/com/example/demo/dto/ResponseDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.dto; 2 | 3 | import java.util.List; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | @Builder 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | @Data 13 | public class ResponseDTO { 14 | private String error; 15 | private List data; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/demo/src/main/java/com/example/demo/dto/TestRequestBodyDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.dto; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class TestRequestBodyDTO { 7 | private int id; 8 | private String message; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/demo/src/main/java/com/example/demo/dto/TodoDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.dto; 2 | 3 | import com.example.demo.model.TodoEntity; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | @Data 11 | public class TodoDTO { 12 | private String id; 13 | private String title; 14 | private boolean done; 15 | 16 | public TodoDTO(final TodoEntity entity) { 17 | this.id = entity.getId(); 18 | this.title = entity.getTitle(); 19 | this.done = entity.isDone(); 20 | } 21 | 22 | public static TodoEntity toEntity(final TodoDTO dto) { 23 | return TodoEntity.builder() 24 | .id(dto.getId()) 25 | .title(dto.getTitle()) 26 | .done(dto.isDone()) 27 | .build(); 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/demo/src/main/java/com/example/demo/dto/UserDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @Builder 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | public class UserDTO { 13 | private String token; 14 | private String email; 15 | private String username; 16 | private String password; 17 | private String id; 18 | } 19 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/demo/src/main/java/com/example/demo/model/TodoEntity.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.model; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.GeneratedValue; 5 | import javax.persistence.Id; 6 | import javax.persistence.Table; 7 | 8 | import lombok.AllArgsConstructor; 9 | import lombok.Builder; 10 | import lombok.Data; 11 | import lombok.NoArgsConstructor; 12 | import org.hibernate.annotations.GenericGenerator; 13 | 14 | @Builder 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | @Data 18 | @Entity 19 | @Table(name = "Todo") 20 | public class TodoEntity { 21 | @Id 22 | @GeneratedValue(generator="system-uuid") 23 | @GenericGenerator(name="system-uuid", strategy = "uuid") 24 | private String id; 25 | private String userId; 26 | private String title; 27 | private boolean done; 28 | } 29 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/demo/src/main/java/com/example/demo/model/UserEntity.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | import org.hibernate.annotations.GenericGenerator; 9 | 10 | import javax.persistence.Column; 11 | import javax.persistence.Entity; 12 | import javax.persistence.GeneratedValue; 13 | import javax.persistence.Id; 14 | import javax.persistence.Table; 15 | import javax.persistence.UniqueConstraint; 16 | 17 | @Data 18 | @Entity 19 | @Builder 20 | @NoArgsConstructor 21 | @AllArgsConstructor 22 | @Table(uniqueConstraints = {@UniqueConstraint(columnNames = "email")}) 23 | public class UserEntity { 24 | @Id 25 | @GeneratedValue(generator="system-uuid") 26 | @GenericGenerator(name="system-uuid", strategy = "uuid") 27 | private String id; // 유저에게 고유하게 부여되는 id. 28 | 29 | @Column(nullable = false) 30 | private String username; // 유저의 이름 31 | 32 | @Column(nullable = false) 33 | private String email; // 유저의 email, 아이디와 같은 기능을 한다. 34 | 35 | @Column(nullable = false) 36 | private String password; // 패스워드. null이 가능한 이유는 oAuth로 페이스북이나 트위터같은 제3의 어플리케이션을 통해 로그인 할 수 있게 하기 위함이다. 37 | } 38 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/demo/src/main/java/com/example/demo/persistence/TodoRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.persistence; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.stereotype.Repository; 5 | import com.example.demo.model.TodoEntity; 6 | 7 | import java.util.List; 8 | 9 | @Repository 10 | public interface TodoRepository extends JpaRepository{ 11 | List findByUserId(String userId); 12 | } 13 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/demo/src/main/java/com/example/demo/persistence/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.persistence; 2 | 3 | import com.example.demo.model.UserEntity; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface UserRepository extends JpaRepository { 9 | 10 | UserEntity findByEmail(String email); 11 | Boolean existsByEmail(String email); 12 | UserEntity findByEmailAndPassword(String email, String password); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/demo/src/main/java/com/example/demo/security/ExampleServletFilter.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.security; 2 | 3 | import org.springframework.util.StringUtils; 4 | 5 | import javax.servlet.FilterChain; 6 | import javax.servlet.ServletException; 7 | import javax.servlet.http.HttpFilter; 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | import java.io.IOException; 11 | 12 | /* 예제용 - 실제로 사용하지 않음. */ 13 | public class ExampleServletFilter extends HttpFilter { 14 | 15 | private TokenProvider tokenProvider; 16 | 17 | @Override 18 | protected void doFilter(HttpServletRequest request, 19 | HttpServletResponse response, 20 | FilterChain filterChain) 21 | throws IOException, ServletException { 22 | try { 23 | final String token = parseBearerToken(request); 24 | 25 | if (token != null && !token.equalsIgnoreCase("null")) { 26 | // userId 가져오기. 위조 된 경우 예외 처리 된다. 27 | String userId = tokenProvider.validateAndGetUserId(token); 28 | 29 | // 다음 ServletFilter 실행 30 | filterChain.doFilter(request, response); 31 | } 32 | } catch (Exception e) { 33 | // 예외 발생시 response를 403 Forbidden으로 설정. 34 | response.setStatus(HttpServletResponse.SC_FORBIDDEN); 35 | } 36 | } 37 | 38 | private String parseBearerToken(HttpServletRequest request) { 39 | // Http 리퀘스트의 헤더를 파싱해 Bearer 토큰을 리턴한다. 40 | String bearerToken = request.getHeader("Authorization"); 41 | 42 | if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { 43 | return bearerToken.substring(7); 44 | } 45 | return null; 46 | } 47 | } -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/demo/src/main/java/com/example/demo/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.service; 2 | 3 | import com.example.demo.model.UserEntity; 4 | import com.example.demo.persistence.UserRepository; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 8 | import org.springframework.security.crypto.password.PasswordEncoder; 9 | import org.springframework.stereotype.Service; 10 | 11 | import java.util.Optional; 12 | 13 | @Slf4j 14 | @Service 15 | public class UserService { 16 | 17 | @Autowired 18 | private UserRepository userRepository; 19 | 20 | public UserEntity create(final UserEntity userEntity) { 21 | if(userEntity == null || userEntity.getEmail() == null ) { 22 | throw new RuntimeException("Invalid arguments"); 23 | } 24 | final String email = userEntity.getEmail(); 25 | if(userRepository.existsByEmail(email)) { 26 | log.warn("Email already exists {}", email); 27 | throw new RuntimeException("Email already exists"); 28 | } 29 | 30 | return userRepository.save(userEntity); 31 | } 32 | 33 | public UserEntity getByCredentials(final String email, final String password, final PasswordEncoder encoder) { 34 | final UserEntity originalUser = userRepository.findByEmail(email); 35 | 36 | // matches 메서드를 이용해 패스워드가 같은지 확인 37 | if(originalUser != null && encoder.matches(password, originalUser.getPassword())) { 38 | return originalUser; 39 | } 40 | return null; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/demo/src/main/resources/application-dev.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/6.5-Route53-Domain-Configuration/demo/src/main/resources/application-dev.yaml -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/demo/src/main/resources/application-prod.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | port : 5000 3 | spring: 4 | jpa: 5 | database: MYSQL 6 | show-sql: true 7 | database-platform: org.hibernate.dialect.MySQL8Dialect 8 | hibernate: 9 | ddl-auto: update 10 | datasource: 11 | url: jdbc:mysql://${rds.hostname}:${rds.port}/${rds.db.name} 12 | username: ${rds.username} 13 | password: ${rds.password} 14 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/demo/src/test/java/com/example/demo/DemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class DemoApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/todo-react-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | # Elastic Beanstalk Files 26 | .elasticbeanstalk/* 27 | !.elasticbeanstalk/*.cfg.yml 28 | !.elasticbeanstalk/*.global.yml 29 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/todo-react-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo-react-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^4.11.3", 7 | "@material-ui/icons": "^4.11.2", 8 | "@testing-library/jest-dom": "^5.11.9", 9 | "@testing-library/react": "^11.2.5", 10 | "@testing-library/user-event": "^12.8.1", 11 | "react": "^17.0.1", 12 | "react-dom": "^17.0.1", 13 | "react-router-dom": "^5.2.0", 14 | "react-scripts": "4.0.3", 15 | "web-vitals": "^1.1.0" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "GENERATE_SOURCEMAP=false react-scripts build", 20 | "test": "react-scripts test", 21 | "eject": "react-scripts eject" 22 | }, 23 | "eslintConfig": { 24 | "extends": [ 25 | "react-app", 26 | "react-app/jest" 27 | ] 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.2%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/todo-react-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/6.5-Route53-Domain-Configuration/todo-react-app/public/favicon.ico -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/todo-react-app/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/6.5-Route53-Domain-Configuration/todo-react-app/public/logo192.png -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/todo-react-app/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsoftwareengineer/todo-application/61565eeb76a042be13b4f72d737065841aa305ce/6.5-Route53-Domain-Configuration/todo-react-app/public/logo512.png -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/todo-react-app/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/todo-react-app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/todo-react-app/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/todo-react-app/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/todo-react-app/src/AppRouter.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./index.css"; 3 | import App from "./App"; 4 | import Login from "./Login"; 5 | import SignUp from "./SignUp"; 6 | import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; 7 | import Box from "@material-ui/core/Box"; 8 | import Typography from "@material-ui/core/Typography"; 9 | 10 | function Copyright() { 11 | return ( 12 | 13 | {"Copyright © "} 14 | fsoftwareengineer, {new Date().getFullYear()} 15 | {"."} 16 | 17 | ); 18 | } 19 | 20 | class AppRouter extends React.Component { 21 | render() { 22 | return ( 23 |
24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
38 | 39 | 40 | 41 |
42 |
43 | ); 44 | } 45 | } 46 | 47 | export default AppRouter; 48 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/todo-react-app/src/app-config.js: -------------------------------------------------------------------------------- 1 | let backendHost; 2 | 3 | const hostname = window && window.location && window.location.hostname; 4 | 5 | if (hostname === "localhost") { 6 | backendHost = "http://localhost:8080"; 7 | } else { 8 | backendHost = "https://api.fsoftwareengineer.com"; 9 | } 10 | 11 | export const API_BASE_URL = `${backendHost}`; 12 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/todo-react-app/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/todo-react-app/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "./index.css"; 4 | import AppRouter from "./AppRouter"; 5 | import reportWebVitals from "./reportWebVitals"; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById("root") 12 | ); 13 | // If you want to start measuring performance in your app, pass a function 14 | // to log results (for example: reportWebVitals(console.log)) 15 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 16 | reportWebVitals(); 17 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/todo-react-app/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /6.5-Route53-Domain-Configuration/todo-react-app/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React.js, 스프링 부트, AWS로 배우는 웹 개발 101 2 | 3 | 도서 정보: http://www.acornpub.co.kr/book/reactjs-springboot 4 | 5 | ### 코드 다운로드 6 | 7 | ``` 8 | $ git clone https://github.com/fsoftwareengineer/todo-application.git 9 | ``` 10 | 11 | 함수형 컴포넌트로 프론트엔드 코드를 진행하고자 하신다면 아래와 같이 branch를 `functional-components`로 변경 해 주시기 바랍니다. 12 | 13 | ``` 14 | $ cd <프로젝트 디렉터리> 15 | $ git switch functional-components 16 | ``` 17 | 18 | ### 백엔드 코드 실행 19 | 20 | ``` 21 | $ cd <프로젝트 디렉터리>/demo 22 | $ ./gradlew bootRun 23 | ``` 24 | 25 | ### 프론트엔드 코드 실행 26 | 27 | 28 | ``` 29 | $ cd <프로젝트 디렉터리>/todo-react-app 30 | $ npm install 31 | $ npm start 32 | 33 | ``` 34 | 35 | ### 질문과 답변 36 | 질문이 있으신 경우 https://github.com/fsoftwareengineer/todo-application/discussions 의 질문고 답변에 게시글을 작성 해 주시기 바랍니다. 37 | --------------------------------------------------------------------------------