diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..1888ab3365 --- /dev/null +++ b/.gitignore @@ -0,0 +1,199 @@ +# Created by https://www.toptal.com/developers/gitignore/api/intellij,java,windows,gradle +# Edit at https://www.toptal.com/developers/gitignore?templates=intellij,java,windows,gradle + +.DS_Store + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +# Azure Toolkit for IntelliJ plugin +# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij +.idea/**/azureSettings.xml + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### Gradle ### +.gradle +**/build/ +!src/**/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Avoid ignore Gradle wrappper properties +!gradle-wrapper.properties + +# Cache of project +.gradletasknamecache + +# Eclipse Gradle plugin generated files +# Eclipse Core +.project +# JDT-specific (Eclipse Java Development Tools) +.classpath + +### Gradle Patch ### +# Java heap dump +*.hprof + +# End of https://www.toptal.com/developers/gitignore/api/intellij,java,windows,gradle!/.idea/ \ No newline at end of file diff --git a/README.md b/README.md index 43762768b5..cb1334ea4c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ # SpringBoot Basic Weekly Mission 스프링부트 basic 위클리미션을 코드리뷰하는 Repository입니다. -주차별 과제는 노션에서 확인하세요! -[노션에서 미션 확인가기](https://www.notion.so/backend-devcourse/Part1-3-38f57acca0dd490db11393701417943a) +리뷰 및 리팩토링 : https://github.com/prgrms-be-devcourse/springboot-basic/pull/671 diff --git a/springboot-basic/.gitignore b/springboot-basic/.gitignore new file mode 100644 index 0000000000..1ba57e5198 --- /dev/null +++ b/springboot-basic/.gitignore @@ -0,0 +1,199 @@ +# Created by https://www.toptal.com/developers/gitignore/api/intellij,java,windows,gradle +# Edit at https://www.toptal.com/developers/gitignore?templates=intellij,java,windows,gradle + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +application-*.yml + +# User-specific stuff +.idea +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +# Azure Toolkit for IntelliJ plugin +# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij +.idea/**/azureSettings.xml + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### Gradle ### +.gradle +**/build/ +!src/**/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Avoid ignore Gradle wrappper properties +!gradle-wrapper.properties + +# Cache of project +.gradletasknamecache + +# Eclipse Gradle plugin generated files +# Eclipse Core +.project +# JDT-specific (Eclipse Java Development Tools) +.classpath + +### Gradle Patch ### +# Java heap dump +*.hprof + +# End of https://www.toptal.com/developers/gitignore/api/intellij,java,windows,gradle!/.idea/ \ No newline at end of file diff --git a/springboot-basic/HELP.md b/springboot-basic/HELP.md new file mode 100644 index 0000000000..d268acd5d1 --- /dev/null +++ b/springboot-basic/HELP.md @@ -0,0 +1,20 @@ +# Read Me First +The following was discovered as part of building this project: + +* The JVM level was changed from '11' to '17', review the [JDK Version Range](https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-Versions#jdk-version-range) on the wiki for more details. + +# Getting Started + +### Reference Documentation +For further reference, please consider the following sections: + +* [Official Gradle documentation](https://docs.gradle.org) +* [Spring Boot Gradle Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/3.1.0/gradle-plugin/reference/html/) +* [Create an OCI image](https://docs.spring.io/spring-boot/docs/3.1.0/gradle-plugin/reference/html/#build-image) +* [Spring Boot DevTools](https://docs.spring.io/spring-boot/docs/3.1.0/reference/htmlsingle/#using.devtools) + +### Additional Links +These additional references should also help you: + +* [Gradle Build Scans – insights for your project's build](https://scans.gradle.com#gradle) + diff --git a/springboot-basic/build.gradle b/springboot-basic/build.gradle new file mode 100644 index 0000000000..a870bf0742 --- /dev/null +++ b/springboot-basic/build.gradle @@ -0,0 +1,38 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '2.7.5' + id 'io.spring.dependency-management' version '1.1.0' +} + +group = 'com.example' +version = '0.0.1-SNAPSHOT' + +java { + sourceCompatibility = '11' +} + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter' + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + implementation 'org.springframework.boot:spring-boot-starter-jdbc' + runtimeOnly 'com.mysql:mysql-connector-j' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' + implementation 'org.springdoc:springdoc-openapi-ui:1.6.8' +} + +tasks.named('test') { + useJUnitPlatform() +} diff --git a/springboot-basic/gradle/wrapper/gradle-wrapper.jar b/springboot-basic/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000..249e5832f0 Binary files /dev/null and b/springboot-basic/gradle/wrapper/gradle-wrapper.jar differ diff --git a/springboot-basic/gradle/wrapper/gradle-wrapper.properties b/springboot-basic/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..774fae8767 --- /dev/null +++ b/springboot-basic/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/springboot-basic/gradlew b/springboot-basic/gradlew new file mode 100755 index 0000000000..a69d9cb6c2 --- /dev/null +++ b/springboot-basic/gradlew @@ -0,0 +1,240 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/springboot-basic/gradlew.bat b/springboot-basic/gradlew.bat new file mode 100644 index 0000000000..53a6b238d4 --- /dev/null +++ b/springboot-basic/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/springboot-basic/settings.gradle b/springboot-basic/settings.gradle new file mode 100644 index 0000000000..20365b4d8f --- /dev/null +++ b/springboot-basic/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'springboot-basic' diff --git a/springboot-basic/src/main/java/com/example/commandlineapplication/CommandLineApplication.java b/springboot-basic/src/main/java/com/example/commandlineapplication/CommandLineApplication.java new file mode 100644 index 0000000000..73eecd6253 --- /dev/null +++ b/springboot-basic/src/main/java/com/example/commandlineapplication/CommandLineApplication.java @@ -0,0 +1,28 @@ +package com.example.commandlineapplication; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class CommandLineApplication implements CommandLineRunner { + + private static final Logger LOG = LoggerFactory.getLogger(CommandLineApplication.class); + private final ConsoleRunner consoleRunner; + + public CommandLineApplication(ConsoleRunner consoleRunner) { + this.consoleRunner = consoleRunner; + } + + public static void main(String[] args) { + LOG.info("voucher가 실행되었습니다."); + SpringApplication.run(CommandLineApplication.class, args); + } + + @Override + public void run(String... args) { + consoleRunner.run(); + } +} diff --git a/springboot-basic/src/main/java/com/example/commandlineapplication/ConsoleRunner.java b/springboot-basic/src/main/java/com/example/commandlineapplication/ConsoleRunner.java new file mode 100644 index 0000000000..d4a40c2fa4 --- /dev/null +++ b/springboot-basic/src/main/java/com/example/commandlineapplication/ConsoleRunner.java @@ -0,0 +1,36 @@ +package com.example.commandlineapplication; + +import com.example.commandlineapplication.domain.voucher.controller.VoucherController; +import com.example.commandlineapplication.global.io.Command; +import com.example.commandlineapplication.global.io.Console; +import lombok.RequiredArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class ConsoleRunner implements Runnable { + + private static final Logger LOG = LoggerFactory.getLogger(ConsoleRunner.class); + private final Console console; + private final VoucherController voucherController; + + @Override + public void run() { + boolean isRunning = true; + + while (isRunning) { + console.printMenu(); + try { + Command command = Command.find(console.input()); + + isRunning = voucherController.handleCommand(command); + } catch (Exception e) { + LOG.error(e.getMessage() + e); + return; + } + } + } +} + diff --git a/springboot-basic/src/main/java/com/example/commandlineapplication/domain/customer/Customer.java b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/customer/Customer.java new file mode 100644 index 0000000000..9c056afa19 --- /dev/null +++ b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/customer/Customer.java @@ -0,0 +1,50 @@ +package com.example.commandlineapplication.domain.customer; + +import java.util.UUID; + +public class Customer { + + private static final int CUSTOMER_NAME_LIMIT = 30; + private static final int CUSTOMER_EMAIL_LIMIT = 50; + private final UUID customerId; + private String customerName; + private String customerEmail; + + public Customer(UUID customerId, String customerName, String customerEmail) { + validateCustomerName(); + validateCustomerEmail(); + this.customerId = customerId; + this.customerName = customerName; + this.customerEmail = customerEmail; + } + + public UUID getCustomerId() { + return this.customerId; + } + + public String getCustomerName() { + return this.customerName; + } + + public String getCustomerEmail() { + return this.customerEmail; + } + + private void validateCustomerName() { + if (this.customerName.isBlank()) { + throw new IllegalArgumentException("이름은 빈칸이 될 수 없습니다."); + } + if (this.customerName.length() > CUSTOMER_NAME_LIMIT) { + throw new IllegalArgumentException("이름은 " + CUSTOMER_NAME_LIMIT + "자를 넘을 수 없습니다."); + } + } + + private void validateCustomerEmail() { + if (this.customerEmail.isBlank()) { + throw new RuntimeException("이메일은 빈칸이 될 수 없습니다."); + } + if (this.customerEmail.length() > CUSTOMER_EMAIL_LIMIT) { + throw new IllegalArgumentException("이름은 " + CUSTOMER_EMAIL_LIMIT + "자를 넘을 수 없습니다."); + } + } +} diff --git a/springboot-basic/src/main/java/com/example/commandlineapplication/domain/customer/repository/CustomerJdbcRepository.java b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/customer/repository/CustomerJdbcRepository.java new file mode 100644 index 0000000000..a7df967b14 --- /dev/null +++ b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/customer/repository/CustomerJdbcRepository.java @@ -0,0 +1,111 @@ +package com.example.commandlineapplication.domain.customer.repository; + +import com.example.commandlineapplication.domain.customer.Customer; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import javax.sql.DataSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; +import org.springframework.jdbc.core.namedparam.SqlParameterSource; +import org.springframework.stereotype.Repository; + +@Repository +public class CustomerJdbcRepository implements CustomerRepository { + + private static final Logger LOG = LoggerFactory.getLogger(CustomerJdbcRepository.class); + private static final int SUCCESS_EXECUTE = 1; + private final NamedParameterJdbcTemplate template; + + public CustomerJdbcRepository(DataSource dataSource) { + this.template = new NamedParameterJdbcTemplate(dataSource); + } + + private RowMapper rowMapper() { + return ((resultSet, rowMap) -> + new Customer( + UUID.fromString(resultSet.getString("customer_id")), + resultSet.getString("customer_name"), + resultSet.getString("customer_email") + ) + ); + } + + @Override + public void save(Customer customer) { + + String sql = "insert into customer values (:customerId, :customerName, :customerEmail)"; + + SqlParameterSource param = new MapSqlParameterSource() + .addValue("customerId", customer.getCustomerId().toString()) + .addValue("customer_name", customer.getCustomerName()) + .addValue("customer_email", customer.getCustomerEmail()); + + int saved = template.update(sql, param); + + if (saved != SUCCESS_EXECUTE) { + LOG.error("customer가 저장되지 않았습니다."); + throw new RuntimeException("customer가 저장되지 않았습니다."); + } + } + + @Override + public void update(Customer customer) { + String sql = "update customer set customer_name = :customerName, customer_email = :customerEmail where customer_id = :customerId"; + + SqlParameterSource param = new MapSqlParameterSource() + .addValue("customer_id", customer.getCustomerId().toString()) + .addValue("customerName", customer.getCustomerName()) + .addValue("customerEmail", customer.getCustomerEmail()); + + int updated = template.update(sql, param); + + if (updated != SUCCESS_EXECUTE) { + LOG.error("customer가 수정되지 않았습니다."); + throw new RuntimeException("customer가 수정되지 않았습니다."); + } + } + + @Override + public Optional findById(UUID customerId) { + String sql = "select * from customer where customer_id = :customerId"; + + try { + SqlParameterSource param = new MapSqlParameterSource() + .addValue("customerId", customerId.toString()); + + Customer customer = template.queryForObject(sql, param, rowMapper()); + + return Optional.ofNullable(customer); + } catch (EmptyResultDataAccessException e) { + LOG.error("customerId가 존재하지 않습니다.", e); + return Optional.empty(); + } + } + + @Override + public List findAll() { + String sql = "select * from customer"; + + return template.query(sql, rowMapper()); + } + + @Override + public void deleteById(UUID customerId) { + String sql = "delete from customer where customer_id = :customerId"; + + SqlParameterSource param = new MapSqlParameterSource() + .addValue("customerId", customerId.toString()); + + int deleted = template.update(sql, param); + + if (deleted != SUCCESS_EXECUTE) { + LOG.error("customer가 삭제되지 않았습니다."); + throw new RuntimeException("customer가 삭제되지 않았습니다."); + } + } +} diff --git a/springboot-basic/src/main/java/com/example/commandlineapplication/domain/customer/repository/CustomerRepository.java b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/customer/repository/CustomerRepository.java new file mode 100644 index 0000000000..9fe3031d51 --- /dev/null +++ b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/customer/repository/CustomerRepository.java @@ -0,0 +1,19 @@ +package com.example.commandlineapplication.domain.customer.repository; + +import com.example.commandlineapplication.domain.customer.Customer; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +public interface CustomerRepository { + + void save(Customer customer); + + void update(Customer customer); + + Optional findById(UUID customerId); + + List findAll(); + + void deleteById(UUID customerId); +} diff --git a/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/FixedAmountVoucher.java b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/FixedAmountVoucher.java new file mode 100644 index 0000000000..76da810c0d --- /dev/null +++ b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/FixedAmountVoucher.java @@ -0,0 +1,41 @@ +package com.example.commandlineapplication.domain.voucher; + +import java.util.UUID; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class FixedAmountVoucher extends Voucher { + + private static final long MIN_DISCOUNTED_PRICE = 0L; + private final UUID voucherId; + private final long discount; + + @Override + public UUID getVoucherId() { + return voucherId; + } + + @Override + public VoucherType getVoucherType() { + return VoucherType.FIXED; + } + + @Override + public long getDiscount() { + return discount; + } + + public double discountedPrice(long price) { + validateDiscountedPrice(price); + if (price < discount) { + return MIN_DISCOUNTED_PRICE; + } + return price - discount; + } + + private void validateDiscountedPrice(long price) { + if (price < MIN_DISCOUNTED_PRICE) { + throw new IllegalArgumentException("고정 할인 범위를 확인해주세요."); + } + } +} diff --git a/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/PercentDiscountVoucher.java b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/PercentDiscountVoucher.java new file mode 100644 index 0000000000..bc1d39e283 --- /dev/null +++ b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/PercentDiscountVoucher.java @@ -0,0 +1,40 @@ +package com.example.commandlineapplication.domain.voucher; + +import java.util.UUID; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class PercentDiscountVoucher extends Voucher { + + private static final double MAX_PERCENT = 100; + private static final long MIN_PERCENT = 0; + private final UUID voucherId; + private final long discountPercent; + + @Override + public UUID getVoucherId() { + return voucherId; + } + + @Override + public double discountedPrice(long price) { + validateDiscountPercent(); + return price - price * (discountPercent / MAX_PERCENT); + } + + private void validateDiscountPercent() { + if (discountPercent > MAX_PERCENT || discountPercent < MIN_PERCENT) { + throw new IllegalArgumentException("할인율 범위를 확인해주세요."); + } + } + + @Override + public VoucherType getVoucherType() { + return VoucherType.PERCENT; + } + + @Override + public long getDiscount() { + return discountPercent; + } +} diff --git a/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/Voucher.java b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/Voucher.java new file mode 100644 index 0000000000..7b1832c032 --- /dev/null +++ b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/Voucher.java @@ -0,0 +1,14 @@ +package com.example.commandlineapplication.domain.voucher; + +import java.util.UUID; + +public abstract class Voucher { + + public abstract UUID getVoucherId(); + + public abstract double discountedPrice(long beforeDiscount); + + public abstract VoucherType getVoucherType(); + + public abstract long getDiscount(); +} diff --git a/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/VoucherType.java b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/VoucherType.java new file mode 100644 index 0000000000..d84c910ceb --- /dev/null +++ b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/VoucherType.java @@ -0,0 +1,24 @@ +package com.example.commandlineapplication.domain.voucher; + +import java.util.Arrays; + +public enum VoucherType { + + FIXED, + PERCENT; + + public static VoucherType of(String inputVoucherType) { + return Arrays.stream(values()) + .filter(voucher -> voucher.isEquals(inputVoucherType, voucher)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("잘못된 바우처 타입입니다.")); + } + + private boolean isEquals(String inputVoucherType, VoucherType voucher) { + return voucher.getLowerCaseVoucherType().equalsIgnoreCase(inputVoucherType); + } + + public String getLowerCaseVoucherType() { + return this.name().toLowerCase(); + } +} diff --git a/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/controller/VoucherController.java b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/controller/VoucherController.java new file mode 100644 index 0000000000..0696dd2c78 --- /dev/null +++ b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/controller/VoucherController.java @@ -0,0 +1,41 @@ +package com.example.commandlineapplication.domain.voucher.controller; + +import com.example.commandlineapplication.domain.voucher.VoucherType; +import com.example.commandlineapplication.domain.voucher.service.VoucherService; +import com.example.commandlineapplication.global.io.Command; +import com.example.commandlineapplication.global.io.Console; +import java.util.UUID; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class VoucherController { + + private final Console console; + private final VoucherService voucherService; + + public Boolean handleCommand(Command command) { + switch (command) { + case CREATE: + VoucherType inputVoucherType = console.selectVoucherTypeOption(); + Long inputDiscount = console.selectDiscount(); + + voucherService.createVoucher(inputVoucherType, inputDiscount); + return true; + case DELETE: + console.printDeleteUUID(); + String inputUUID = console.input(); + + voucherService.deleteVoucher(UUID.fromString(inputUUID)); + return true; + case LIST: + voucherService.printHistory(); + return true; + case EXIT: + return false; + } + return false; + } + +} diff --git a/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/controller/VoucherRestController.java b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/controller/VoucherRestController.java new file mode 100644 index 0000000000..e0030ba243 --- /dev/null +++ b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/controller/VoucherRestController.java @@ -0,0 +1,71 @@ +package com.example.commandlineapplication.domain.voucher.controller; + +import com.example.commandlineapplication.domain.voucher.VoucherType; +import com.example.commandlineapplication.domain.voucher.dto.request.VoucherCreateRequest; +import com.example.commandlineapplication.domain.voucher.dto.response.VoucherResponse; +import com.example.commandlineapplication.domain.voucher.service.VoucherService; +import java.util.List; +import java.util.UUID; +import javax.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/v1") +@RequiredArgsConstructor +public class VoucherRestController { + + private final VoucherService voucherService; + + @PostMapping("/vouchers") + public ResponseEntity createVoucher( + @Valid @RequestBody VoucherCreateRequest voucherCreateRequest) { + + voucherService.createVoucher(voucherCreateRequest.getVoucherType(), + voucherCreateRequest.getDiscountAmount()); + + return ResponseEntity.status(HttpStatus.CREATED) + .body("voucher가 생성되었습니다. " + voucherCreateRequest.getVoucherId() + + voucherCreateRequest.getVoucherType()); + } + + @GetMapping("/vouchers/list") + public ResponseEntity> getVouchers() { + + return ResponseEntity.status(HttpStatus.OK) + .body(voucherService.findVouchers()); + } + + @GetMapping("/vouchers/{voucherId}") + public ResponseEntity getVoucher( + @Valid @PathVariable String voucherId) { + + return ResponseEntity.status(HttpStatus.OK) + .body(voucherService.getVoucher(voucherId)); + } + + @GetMapping("/vouchers/type/{voucherType}") + public ResponseEntity> getVouchersByVoucherType( + @Valid @PathVariable VoucherType voucherType) { + + return ResponseEntity.status(HttpStatus.OK) + .body(voucherService.getVouchersByVoucherType(voucherType)); + } + + @DeleteMapping("/vouchers/{voucherId}") + public ResponseEntity deleteVoucher(@Valid @PathVariable String voucherId) { + + voucherService.deleteVoucher(UUID.fromString(voucherId)); + + return ResponseEntity.status(HttpStatus.NO_CONTENT) + .body("삭제되었습니다."); + } +} diff --git a/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/controller/VoucherWebController.java b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/controller/VoucherWebController.java new file mode 100644 index 0000000000..5ddd07cf95 --- /dev/null +++ b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/controller/VoucherWebController.java @@ -0,0 +1,57 @@ +package com.example.commandlineapplication.domain.voucher.controller; + +import com.example.commandlineapplication.domain.voucher.dto.request.VoucherCreateRequest; +import com.example.commandlineapplication.domain.voucher.dto.response.VoucherResponse; +import com.example.commandlineapplication.domain.voucher.service.VoucherService; +import java.util.List; +import java.util.UUID; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; + +@Controller +@RequiredArgsConstructor +public class VoucherWebController { + + private final VoucherService voucherService; + + @GetMapping + public String home() { + return "home"; + } + + @GetMapping("/vouchers") + public String getVouchers(Model model) { + List vouchers = voucherService.findVouchers(); + model.addAttribute("vouchers", vouchers); + return "vouchers"; + } + + @GetMapping("/voucher/{voucherId}") + public String voucherDetailPage(Model model, @PathVariable String voucherId) { + VoucherResponse voucher = voucherService.getVoucher(voucherId); + model.addAttribute("voucher", voucher); + return "voucher-detail"; + } + + @GetMapping("/voucher/new") + public String newVoucherPage() { + return "voucher-new"; + } + + @PostMapping("/voucher/new") + public String createVoucher(VoucherCreateRequest voucherCreateRequest) { + voucherService.createVoucher(voucherCreateRequest.getVoucherType(), + voucherCreateRequest.getDiscountAmount()); + return "redirect:/vouchers"; + } + + @PostMapping("/voucher/delete/{voucherId}") + public String deleteVoucher(@PathVariable String voucherId) { + voucherService.deleteVoucher(UUID.fromString(voucherId)); + return "redirect:/vouchers"; + } +} diff --git a/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/dto/mapper/VoucherMapper.java b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/dto/mapper/VoucherMapper.java new file mode 100644 index 0000000000..49e3c7de2c --- /dev/null +++ b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/dto/mapper/VoucherMapper.java @@ -0,0 +1,31 @@ +package com.example.commandlineapplication.domain.voucher.dto.mapper; + +import com.example.commandlineapplication.domain.voucher.Voucher; +import com.example.commandlineapplication.domain.voucher.VoucherType; +import com.example.commandlineapplication.domain.voucher.dto.request.VoucherCreateRequest; +import com.example.commandlineapplication.domain.voucher.dto.response.VoucherResponse; +import java.util.UUID; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class VoucherMapper { + + public VoucherResponse voucherToResponse(Voucher voucher) { + return VoucherResponse.builder() + .voucherId(UUID.fromString(voucher.getVoucherId().toString())) + .discountAmount(voucher.getDiscount()) + .voucherType(voucher.getVoucherType()) + .build(); + } + + public VoucherCreateRequest toCreateRequest(UUID voucherId, VoucherType voucherType, + long discount) { + return VoucherCreateRequest.builder() + .voucherId(voucherId) + .discountAmount(discount) + .voucherType(voucherType) + .build(); + } +} \ No newline at end of file diff --git a/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/dto/request/VoucherCreateRequest.java b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/dto/request/VoucherCreateRequest.java new file mode 100644 index 0000000000..1689276933 --- /dev/null +++ b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/dto/request/VoucherCreateRequest.java @@ -0,0 +1,26 @@ +package com.example.commandlineapplication.domain.voucher.dto.request; + +import com.example.commandlineapplication.domain.voucher.VoucherType; +import java.util.UUID; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class VoucherCreateRequest { + + private UUID voucherId; + private VoucherType voucherType; + private Long discountAmount; + + public VoucherCreateRequest(VoucherType voucherType, Long discountAmount) { + this.voucherType = voucherType; + this.discountAmount = discountAmount; + } +} diff --git a/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/dto/response/VoucherResponse.java b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/dto/response/VoucherResponse.java new file mode 100644 index 0000000000..c9ebc5ee5a --- /dev/null +++ b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/dto/response/VoucherResponse.java @@ -0,0 +1,18 @@ +package com.example.commandlineapplication.domain.voucher.dto.response; + +import com.example.commandlineapplication.domain.voucher.VoucherType; +import java.util.UUID; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + + +@Getter +@Builder +@AllArgsConstructor +public class VoucherResponse { + + private final UUID voucherId; + private final VoucherType voucherType; + private final long discountAmount; +} diff --git a/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/repository/VoucherJdbcRepository.java b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/repository/VoucherJdbcRepository.java new file mode 100644 index 0000000000..6a612f86fe --- /dev/null +++ b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/repository/VoucherJdbcRepository.java @@ -0,0 +1,120 @@ +package com.example.commandlineapplication.domain.voucher.repository; + +import com.example.commandlineapplication.domain.voucher.Voucher; +import com.example.commandlineapplication.domain.voucher.VoucherType; +import com.example.commandlineapplication.domain.voucher.dto.mapper.VoucherMapper; +import com.example.commandlineapplication.domain.voucher.dto.request.VoucherCreateRequest; +import com.example.commandlineapplication.domain.voucher.service.VoucherFactory; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import lombok.RequiredArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Primary; +import org.springframework.dao.DataAccessException; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.dao.IncorrectResultSizeDataAccessException; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; +import org.springframework.jdbc.core.namedparam.SqlParameterSource; +import org.springframework.stereotype.Repository; + + +@Primary +@Repository +@RequiredArgsConstructor +public class VoucherJdbcRepository implements VoucherRepository { + + private static final Logger LOG = LoggerFactory.getLogger(VoucherJdbcRepository.class); + private static final int SUCCESS_EXECUTE = 1; + private final NamedParameterJdbcTemplate template; + private final VoucherFactory voucherFactory; + private final VoucherMapper voucherMapper; + + private RowMapper rowMapper() { + return (resultSet, rowMap) -> { + UUID voucherId = UUID.fromString((resultSet.getString("voucher_id"))); + VoucherType voucherType = VoucherType.valueOf(resultSet.getString("voucher_type")); + long discount = resultSet.getLong("voucher_discount"); + VoucherCreateRequest voucherCreateRequest = voucherMapper.toCreateRequest(voucherId, + voucherType, + discount); + + return voucherFactory.create(voucherCreateRequest); + }; + } + + @Override + public Optional findById(UUID voucherId) { + String sql = "select * from voucher where voucher_id = :voucherId"; + + try { + SqlParameterSource param = new MapSqlParameterSource() + .addValue("voucherId", voucherId.toString()); + + Voucher voucher = template.queryForObject(sql, param, rowMapper()); + + return Optional.ofNullable(voucher); + } catch (EmptyResultDataAccessException e) { + LOG.error("voucherId가 존재하지 않습니다. " + e.getMessage() + e); + return Optional.empty(); + } + } + + @Override + public Voucher insert(Voucher voucher) { + String sql = "insert into voucher values (:voucherId, :voucher_discount, :voucher_type)"; + + SqlParameterSource param = new MapSqlParameterSource() + .addValue("voucherId", voucher.getVoucherId().toString()) + .addValue("voucher_discount", voucher.getDiscount()) + .addValue("voucher_type", voucher.getVoucherType().name()); + + int saved = template.update(sql, param); + + if (saved != SUCCESS_EXECUTE) { + DataAccessException exception = new IncorrectResultSizeDataAccessException(SUCCESS_EXECUTE, + saved); + LOG.error(exception.getMessage(), exception); + throw exception; + } + + return voucher; + } + + @Override + public List findAll() { + String sql = "select * from voucher"; + + return template.query(sql, rowMapper()); + } + + @Override + public List findVouchersByVoucherType(VoucherType voucherType) { + String type = voucherType.name(); + String sql = "select * from voucher where voucher_type = :type"; + + return template.query(sql, Collections.singletonMap("type", type), + rowMapper()); + } + + @Override + public void deleteById(UUID voucherId) { + String sql = "delete from voucher where voucher_id = :voucherId"; + + SqlParameterSource param = new MapSqlParameterSource() + .addValue("voucherId", voucherId.toString()); + + int deleted = template.update(sql, param); + + if (deleted != SUCCESS_EXECUTE) { + DataAccessException exception = new IncorrectResultSizeDataAccessException(SUCCESS_EXECUTE, + deleted); + LOG.error(exception.getMessage(), exception); + throw exception; + } + } +} diff --git a/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/repository/VoucherMemoryRepository.java b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/repository/VoucherMemoryRepository.java new file mode 100644 index 0000000000..95b364d643 --- /dev/null +++ b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/repository/VoucherMemoryRepository.java @@ -0,0 +1,49 @@ +package com.example.commandlineapplication.domain.voucher.repository; + +import com.example.commandlineapplication.domain.voucher.Voucher; +import com.example.commandlineapplication.domain.voucher.VoucherType; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import org.springframework.stereotype.Repository; + +@Repository +public class VoucherMemoryRepository implements VoucherRepository { + + private final Map storage = new ConcurrentHashMap<>(); + + @Override + public Optional findById(UUID voucherId) { + return Optional.ofNullable(storage.get(voucherId)); + } + + @Override + public Voucher insert(Voucher voucher) { + storage.put(voucher.getVoucherId(), voucher); + return voucher; + } + + @Override + public List findAll() { + return storage.values() + .stream() + .collect(Collectors.toList()); + } + + @Override + public void deleteById(UUID voucherId) { + Voucher foundVoucher = findById(voucherId).orElseThrow(IllegalArgumentException::new); + storage.remove(foundVoucher.getVoucherId()); + } + + @Override + public List findVouchersByVoucherType(VoucherType voucherType) { + return storage.values() + .stream() + .filter(voucher -> voucher.getVoucherType() == voucherType) + .collect(Collectors.toList()); + } +} diff --git a/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/repository/VoucherRepository.java b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/repository/VoucherRepository.java new file mode 100644 index 0000000000..9e4a360872 --- /dev/null +++ b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/repository/VoucherRepository.java @@ -0,0 +1,20 @@ +package com.example.commandlineapplication.domain.voucher.repository; + +import com.example.commandlineapplication.domain.voucher.Voucher; +import com.example.commandlineapplication.domain.voucher.VoucherType; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +public interface VoucherRepository { + + Optional findById(UUID voucherId); + + Voucher insert(Voucher voucher); + + List findAll(); + + void deleteById(UUID voucherId); + + List findVouchersByVoucherType(VoucherType voucherType); +} diff --git a/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/service/VoucherFactory.java b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/service/VoucherFactory.java new file mode 100644 index 0000000000..c97368e7cf --- /dev/null +++ b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/service/VoucherFactory.java @@ -0,0 +1,58 @@ +package com.example.commandlineapplication.domain.voucher.service; + +import com.example.commandlineapplication.domain.voucher.FixedAmountVoucher; +import com.example.commandlineapplication.domain.voucher.PercentDiscountVoucher; +import com.example.commandlineapplication.domain.voucher.Voucher; +import com.example.commandlineapplication.domain.voucher.VoucherType; +import com.example.commandlineapplication.domain.voucher.dto.request.VoucherCreateRequest; +import java.util.UUID; +import lombok.RequiredArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class VoucherFactory { + + private static final Logger LOG = LoggerFactory.getLogger(VoucherFactory.class); + private static final long MAX_PERCENT = 100L; + private static final long MIN_PERCENT = 0L; + private static final long MIN_FIXED_AMOUNT = 0L; + + public Voucher create(VoucherCreateRequest request) { + return toVoucher(request.getVoucherType(), request.getVoucherId(), request.getDiscountAmount()); + } + + public Voucher toVoucher(VoucherType voucherType, UUID voucherId, long amount) { + switch (voucherType) { + case FIXED: + LOG.info("FixedAmountVoucher가 생성되었습니다."); + validateFixedAmount(amount); + return new FixedAmountVoucher(voucherId, amount); + + case PERCENT: + LOG.info("PercentDiscountVoucher가 생성되었습니다."); + validatePercentAmount(amount); + return new PercentDiscountVoucher(voucherId, amount); + + default: + LOG.warn("잘못된 바우처 유형입니다."); + throw new IllegalArgumentException("잘못된 바우처 유형입니다."); + } + } + + public void validatePercentAmount(long amount) { + if (amount < MIN_PERCENT || amount > MAX_PERCENT) { + LOG.warn("amount 범위를 확인해주세요."); + throw new IllegalArgumentException("amount 범위를 확인해주세요."); + } + } + + public void validateFixedAmount(long amount) { + if (amount < MIN_FIXED_AMOUNT) { + LOG.warn("amount는 음수가 될 수 없습니다."); + throw new IllegalArgumentException("amount 범위를 확인해주세요."); + } + } +} \ No newline at end of file diff --git a/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/service/VoucherService.java b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/service/VoucherService.java new file mode 100644 index 0000000000..cf359c2dcf --- /dev/null +++ b/springboot-basic/src/main/java/com/example/commandlineapplication/domain/voucher/service/VoucherService.java @@ -0,0 +1,79 @@ +package com.example.commandlineapplication.domain.voucher.service; + +import com.example.commandlineapplication.domain.voucher.Voucher; +import com.example.commandlineapplication.domain.voucher.VoucherType; +import com.example.commandlineapplication.domain.voucher.dto.mapper.VoucherMapper; +import com.example.commandlineapplication.domain.voucher.dto.request.VoucherCreateRequest; +import com.example.commandlineapplication.domain.voucher.dto.response.VoucherResponse; +import com.example.commandlineapplication.domain.voucher.repository.VoucherRepository; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + + +@Service +@RequiredArgsConstructor +public class VoucherService { + + private static final Logger LOG = LoggerFactory.getLogger(VoucherService.class); + private final VoucherRepository voucherRepository; + private final VoucherFactory voucherFactory; + private final VoucherMapper voucherMapper; + + @Transactional + public void createVoucher(VoucherType inputVoucherType, Long inputDiscount) { + VoucherCreateRequest voucherCreateRequest = voucherMapper.toCreateRequest(UUID.randomUUID(), + inputVoucherType, + inputDiscount); + Voucher voucher = voucherFactory.create(voucherCreateRequest); + + voucherRepository.insert(voucher); + } + + @Transactional + public void deleteVoucher(UUID voucherId) { + Voucher foundVoucher = voucherRepository.findById(voucherId) + .orElseThrow(IllegalArgumentException::new); + + voucherRepository.deleteById(foundVoucher.getVoucherId()); + } + + @Transactional(readOnly = true) + public List findVouchers() { + return voucherRepository.findAll() + .stream() + .map(voucherMapper::voucherToResponse) + .collect(Collectors.toList()); + } + + @Transactional(readOnly = true) + public List getVouchersByVoucherType(VoucherType voucherType) { + return voucherRepository.findVouchersByVoucherType(voucherType) + .stream() + .map(voucherMapper::voucherToResponse) + .collect(Collectors.toList()); + } + + @Transactional(readOnly = true) + public void printHistory() { + List vouchers = findVouchers(); + + for (VoucherResponse voucher : vouchers) { + System.out.println(voucher.getVoucherType() + " " + voucher.getVoucherId().toString() + " " + + voucher.getDiscountAmount()); + } + } + + @Transactional(readOnly = true) + public VoucherResponse getVoucher(String voucherId) { + Voucher foundVoucher = voucherRepository.findById(UUID.fromString(voucherId)) + .orElseThrow(IllegalArgumentException::new); + + return voucherMapper.voucherToResponse(foundVoucher); + } +} diff --git a/springboot-basic/src/main/java/com/example/commandlineapplication/global/io/Command.java b/springboot-basic/src/main/java/com/example/commandlineapplication/global/io/Command.java new file mode 100644 index 0000000000..df9aec4c16 --- /dev/null +++ b/springboot-basic/src/main/java/com/example/commandlineapplication/global/io/Command.java @@ -0,0 +1,29 @@ +package com.example.commandlineapplication.global.io; + +import java.util.Collections; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public enum Command { + + EXIT, + CREATE, + DELETE, + LIST; + + private static final Map commands = Collections.unmodifiableMap( + Stream.of(values()) + .collect(Collectors.toMap(Command::getLowerCaseCommand, Function.identity()))); + + public static Command find(String inputCommand) { + return Optional.ofNullable(commands.get(inputCommand)) + .orElseThrow(IllegalArgumentException::new); + } + + public String getLowerCaseCommand() { + return this.name().toLowerCase(); + } +} diff --git a/springboot-basic/src/main/java/com/example/commandlineapplication/global/io/Console.java b/springboot-basic/src/main/java/com/example/commandlineapplication/global/io/Console.java new file mode 100644 index 0000000000..1d32e8551b --- /dev/null +++ b/springboot-basic/src/main/java/com/example/commandlineapplication/global/io/Console.java @@ -0,0 +1,56 @@ +package com.example.commandlineapplication.global.io; + +import com.example.commandlineapplication.domain.voucher.VoucherType; +import java.util.Scanner; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class Console implements Input, Output { + + private static final Scanner scanner = new Scanner(System.in); + + @Override + public String input() { + return scanner.nextLine(); + } + + @Override + public Long getDiscount() { + return Long.valueOf(scanner.nextLine()); + } + + @Override + public void printMenu() { + System.out.println( + "=== Voucher Program ===\nType exit to exit the program.\nType create to create a new voucher.\nType delete to delete a voucher.\nType list to list all vouchers."); + } + + @Override + public void printCreateOption() { + System.out.println( + "Select voucher type " + VoucherType.PERCENT.getLowerCaseVoucherType() + " or " + + VoucherType.FIXED.getLowerCaseVoucherType() + "."); + } + + @Override + public void printDiscount() { + System.out.println("Input discount amount."); + } + + @Override + public void printDeleteUUID() { + System.out.println("Input UUID to delete"); + } + + public VoucherType selectVoucherTypeOption() { + printCreateOption(); + return VoucherType.of(input()); + } + + public Long selectDiscount() { + printDiscount(); + return getDiscount(); + } +} diff --git a/springboot-basic/src/main/java/com/example/commandlineapplication/global/io/Input.java b/springboot-basic/src/main/java/com/example/commandlineapplication/global/io/Input.java new file mode 100644 index 0000000000..9f26efb819 --- /dev/null +++ b/springboot-basic/src/main/java/com/example/commandlineapplication/global/io/Input.java @@ -0,0 +1,8 @@ +package com.example.commandlineapplication.global.io; + +public interface Input { + + String input(); + + Long getDiscount(); +} diff --git a/springboot-basic/src/main/java/com/example/commandlineapplication/global/io/Output.java b/springboot-basic/src/main/java/com/example/commandlineapplication/global/io/Output.java new file mode 100644 index 0000000000..9d30938f9e --- /dev/null +++ b/springboot-basic/src/main/java/com/example/commandlineapplication/global/io/Output.java @@ -0,0 +1,12 @@ +package com.example.commandlineapplication.global.io; + +public interface Output { + + void printMenu(); + + void printCreateOption(); + + void printDiscount(); + + void printDeleteUUID(); +} diff --git a/springboot-basic/src/main/resources/application.yml b/springboot-basic/src/main/resources/application.yml new file mode 100644 index 0000000000..6c68ef7b9e --- /dev/null +++ b/springboot-basic/src/main/resources/application.yml @@ -0,0 +1,3 @@ +spring: + profiles: + include: local diff --git a/springboot-basic/src/main/resources/logback.xml b/springboot-basic/src/main/resources/logback.xml new file mode 100644 index 0000000000..22ba11a1a6 --- /dev/null +++ b/springboot-basic/src/main/resources/logback.xml @@ -0,0 +1,29 @@ + + + + + + + logs/access-%d{yyyy-MM-dd}.log + + + + ${LOG_PATTERN} + + + + + + ${LOG_PATTERN} + + + + + + + + + + + + \ No newline at end of file diff --git a/springboot-basic/src/main/resources/schema.sql b/springboot-basic/src/main/resources/schema.sql new file mode 100644 index 0000000000..62d2309041 --- /dev/null +++ b/springboot-basic/src/main/resources/schema.sql @@ -0,0 +1,17 @@ +drop table if exists voucher cascade; +drop table if exists customer cascade; +create table customer +( + customer_id varchar(36) default (UUID()) primary key, + customer_name varchar(30) not null, + customer_email varchar(50) not null +); + +create table voucher +( + voucher_id varchar(36) default (UUID()) primary key, + voucher_discount integer(10) NOT NULL, + voucher_type varchar(20) NOT NULL +# customer_id binary(10), +# foreign key (customer_id) references customer (customer_id) +); \ No newline at end of file diff --git a/springboot-basic/src/main/resources/templates/home.html b/springboot-basic/src/main/resources/templates/home.html new file mode 100644 index 0000000000..e9f856dca9 --- /dev/null +++ b/springboot-basic/src/main/resources/templates/home.html @@ -0,0 +1,13 @@ + + + + + home + + +

Home

+ + + \ No newline at end of file diff --git a/springboot-basic/src/main/resources/templates/voucher-detail.html b/springboot-basic/src/main/resources/templates/voucher-detail.html new file mode 100644 index 0000000000..dada3f63cb --- /dev/null +++ b/springboot-basic/src/main/resources/templates/voucher-detail.html @@ -0,0 +1,37 @@ + + + + + + + + Voucher Detail + + +

Voucher Detail

+Back to VoucherList + + + + + + + + + + + + + + + +
VoucherIdVoucherTypeDiscount
+
+ +
+ + \ No newline at end of file diff --git a/springboot-basic/src/main/resources/templates/voucher-new.html b/springboot-basic/src/main/resources/templates/voucher-new.html new file mode 100644 index 0000000000..2a517f4bbb --- /dev/null +++ b/springboot-basic/src/main/resources/templates/voucher-new.html @@ -0,0 +1,33 @@ + + + + + + + + New Voucher + + +

Add New Voucher

+ +
+
+ +
+
+ + +
+ +
+ + + \ No newline at end of file diff --git a/springboot-basic/src/main/resources/templates/vouchers.html b/springboot-basic/src/main/resources/templates/vouchers.html new file mode 100644 index 0000000000..5249f07065 --- /dev/null +++ b/springboot-basic/src/main/resources/templates/vouchers.html @@ -0,0 +1,40 @@ + + + + + + + + + + + Voucher List + + +

Voucher List

+ + + + + + + + + + + + + + + + +
IDVoucher TypeDiscount
+ +
+ + diff --git a/springboot-basic/src/test/java/com/example/commandlineapplication/domain/voucher/service/VoucherServiceTest.java b/springboot-basic/src/test/java/com/example/commandlineapplication/domain/voucher/service/VoucherServiceTest.java new file mode 100644 index 0000000000..e576bba86d --- /dev/null +++ b/springboot-basic/src/test/java/com/example/commandlineapplication/domain/voucher/service/VoucherServiceTest.java @@ -0,0 +1,152 @@ +package com.example.commandlineapplication.domain.voucher.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; + +import com.example.commandlineapplication.domain.voucher.Voucher; +import com.example.commandlineapplication.domain.voucher.VoucherType; +import com.example.commandlineapplication.domain.voucher.dto.mapper.VoucherMapper; +import com.example.commandlineapplication.domain.voucher.dto.request.VoucherCreateRequest; +import com.example.commandlineapplication.domain.voucher.dto.response.VoucherResponse; +import com.example.commandlineapplication.domain.voucher.repository.VoucherRepository; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class VoucherServiceTest { + + private VoucherService voucherService; + private VoucherMapper voucherMapper; + private VoucherFactory voucherFactory; + @Mock + private VoucherRepository voucherRepository; + + @BeforeEach + void init() { + voucherService = new VoucherService(voucherRepository, new VoucherFactory(), + new VoucherMapper()); + voucherMapper = new VoucherMapper(); + voucherFactory = new VoucherFactory(); + } + + @ParameterizedTest + @CsvSource({ + "1", "10", "100" + }) + @DisplayName("FixedAmountVoucher 저장 성공") + void createFixedAmountVoucher(long amount) { + + //given + VoucherType voucherType = VoucherType.FIXED; + Voucher voucher = voucherFactory.create( + VoucherCreateRequest.builder() + .voucherId(UUID.randomUUID()) + .voucherType(VoucherType.FIXED) + .discountAmount(amount) + .build()); + + given(voucherRepository.insert(any())).willReturn(voucher); + + //when + voucherService.createVoucher(voucherType, amount); + + //then + then(voucherRepository).should().insert(any()); + } + + @ParameterizedTest + @CsvSource({ + "1", "10", "100" + }) + @DisplayName("PercentDiscountVoucher 저장 성공") + void createPercentDiscountVoucher(long amount) { + + //given + VoucherType voucherType = VoucherType.PERCENT; + Voucher voucher = voucherFactory.create( + VoucherCreateRequest.builder() + .voucherId(UUID.randomUUID()) + .voucherType(VoucherType.PERCENT) + .discountAmount(amount) + .build()); + + given(voucherRepository.insert(any())).willReturn(voucher); + + //when + voucherService.createVoucher(voucherType, amount); + + //then + then(voucherRepository).should().insert(any()); + } + + @Test + @DisplayName("Voucher 목록 조회 성공") + void getVouchers() { + + //given + Voucher voucher1 = voucherFactory.create( + VoucherCreateRequest.builder() + .voucherId(UUID.randomUUID()) + .voucherType(VoucherType.PERCENT) + .discountAmount(10L) + .build()); + + Voucher voucher2 = voucherFactory.create( + VoucherCreateRequest.builder() + .voucherId(UUID.randomUUID()) + .voucherType(VoucherType.FIXED) + .discountAmount(10L) + .build()); + + List voucherList = new ArrayList<>(); + voucherList.add(voucher1); + voucherList.add(voucher2); + + given(voucherRepository.findAll()).willReturn(voucherList); + + //when + List foundVoucherList = voucherService.findVouchers(); + + //then + List voucherResponseList = voucherList.stream() + .map(voucherMapper::voucherToResponse) + .collect(Collectors.toList()); + + assertThat(foundVoucherList).usingRecursiveFieldByFieldElementComparator() + .isEqualTo(voucherResponseList); + } + + @Test + @DisplayName("Voucher 삭제 성공") + void deleteVoucher() { + + //given + Voucher voucher = voucherFactory.create( + VoucherCreateRequest.builder() + .voucherId(UUID.randomUUID()) + .voucherType(VoucherType.PERCENT) + .discountAmount(10L) + .build()); + + given(voucherRepository.findById(any())).willReturn(Optional.of(voucher)); + + //when + voucherService.deleteVoucher(voucher.getVoucherId()); + + //then + then(voucherRepository).should().deleteById(any()); + } +} \ No newline at end of file diff --git a/springboot-basic/src/test/java/com/example/springbootbasic/commandLineApplicationTests.java b/springboot-basic/src/test/java/com/example/springbootbasic/commandLineApplicationTests.java new file mode 100644 index 0000000000..9b113251b4 --- /dev/null +++ b/springboot-basic/src/test/java/com/example/springbootbasic/commandLineApplicationTests.java @@ -0,0 +1,13 @@ +package com.example.springbootbasic; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class commandLineApplicationTests { + + @Test + void contextLoads() { + } + +}