From 9bd11b9160e9dcd90fc20a37d70a3b78d67d8c77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=84=9C=EC=98=81?= Date: Tue, 8 Sep 2026 19:42:53 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20spring-tutorial-24rd=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitattributes | 3 + .gitignore | 43 +++ HELP.md | 27 ++ README.md | 4 +- build.gradle | 36 +++ gradle/wrapper/gradle-wrapper.properties | 9 + gradlew | 248 ++++++++++++++++++ gradlew.bat | 82 ++++++ settings.gradle | 1 + .../com/ceos24/spring_boot/Application.java | 34 +++ .../ceos24/spring_boot/HelloController.java | 13 + .../java/com/ceos24/spring_boot/Test.java | 15 ++ .../ceos24/spring_boot/TestController.java | 21 ++ .../ceos24/spring_boot/TestRepository.java | 7 + .../com/ceos24/spring_boot/TestService.java | 21 ++ src/main/resources/application.yaml | 18 ++ .../ceos24/spring_boot/ApplicationTests.java | 13 + .../spring_boot/HelloControllerTest.java | 33 +++ 18 files changed, 626 insertions(+), 2 deletions(-) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 HELP.md create mode 100644 build.gradle create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle create mode 100644 src/main/java/com/ceos24/spring_boot/Application.java create mode 100644 src/main/java/com/ceos24/spring_boot/HelloController.java create mode 100644 src/main/java/com/ceos24/spring_boot/Test.java create mode 100644 src/main/java/com/ceos24/spring_boot/TestController.java create mode 100644 src/main/java/com/ceos24/spring_boot/TestRepository.java create mode 100644 src/main/java/com/ceos24/spring_boot/TestService.java create mode 100644 src/main/resources/application.yaml create mode 100644 src/test/java/com/ceos24/spring_boot/ApplicationTests.java create mode 100644 src/test/java/com/ceos24/spring_boot/HelloControllerTest.java diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8af972c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +/gradlew text eol=lf +*.bat text eol=crlf +*.jar binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d70d5a9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,43 @@ +*# +*.iml +*.ipr +*.iws +*.jar +*.sw? +*~ +.#* +.*.md.html +.DS_Store +.attach_pid* +.classpath +.factorypath +.gradle +.metadata +.project +.recommenders +.settings +.springBeans +.vscode +/code +MANIFEST.MF +_site/ +activemq-data +bin +build +!/**/src/**/bin +!/**/src/**/build +build.log +dependency-reduced-pom.xml +dump.rdb +interpolated*.xml +lib/ +manifest.yml +out +overridedb.* +target +.flattened-pom.xml +secrets.yml +.gradletasknamecache +.sts4-cache + +.idea \ No newline at end of file diff --git a/HELP.md b/HELP.md new file mode 100644 index 0000000..6fa0292 --- /dev/null +++ b/HELP.md @@ -0,0 +1,27 @@ +# Read Me First +The following was discovered as part of building this project: + +* The original package name 'com.ceos24.spring-boot' is invalid and this project uses 'com.ceos24.spring_boot' instead. + +# 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/4.1.1/gradle-plugin) +* [Create an OCI image](https://docs.spring.io/spring-boot/4.1.1/gradle-plugin/packaging-oci-image.html) +* [Spring Web](https://docs.spring.io/spring-boot/4.1.1/reference/web/servlet.html) + +### Guides +The following guides illustrate how to use some features concretely: + +* [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/) +* [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/) +* [Building REST services with Spring](https://spring.io/guides/tutorials/rest/) + +### 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/README.md b/README.md index 4162bb5..74dd775 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ -# spring-tutorial-24th -CEOS 백엔드 24기 스프링 튜토리얼 +### 1. spring-tutorial-24th + diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..d8bd254 --- /dev/null +++ b/build.gradle @@ -0,0 +1,36 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '4.1.1' + id 'io.spring.dependency-management' version '1.1.7' +} + +group = 'com.ceos24' +version = '0.0.1-SNAPSHOT' + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-webmvc' + testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + + // 새롭게 추가된 부분 + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + runtimeOnly 'com.mysql:mysql-connector-j' + + testImplementation('org.springframework.boot:spring-boot-starter-test') +} + +tasks.named('test') { + useJUnitPlatform() +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ad7845b --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,9 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-bin.zip +networkTimeout=10000 +retries=0 +retryBackOffMs=500 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..249efbb --- /dev/null +++ b/gradlew @@ -0,0 +1,248 @@ +#!/bin/sh + +# +# Copyright © 2015 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. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# gradlew 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 gradlew +# +# 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/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/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 + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# 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 + + + +# 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 + if ! command -v java >/dev/null 2>&1 + then + 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 +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + 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" ) + + 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 + + +# 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"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# 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/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..a51ec4f --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,82 @@ +@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 +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem gradlew startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables, and ensure extensions are enabled +setlocal EnableExtensions + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +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. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +"%COMSPEC%" /c exit 1 + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +"%COMSPEC%" /c exit 1 + +:execute +@rem Setup the command line + + + +@rem Execute gradlew +@rem endlocal doesn't take effect until after the line is parsed and variables are expanded +@rem which allows us to clear the local environment before executing the java command +endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel + +:exitWithErrorLevel +@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts +"%COMSPEC%" /c exit %ERRORLEVEL% diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..19b5204 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'spring-boot' diff --git a/src/main/java/com/ceos24/spring_boot/Application.java b/src/main/java/com/ceos24/spring_boot/Application.java new file mode 100644 index 0000000..a4339ab --- /dev/null +++ b/src/main/java/com/ceos24/spring_boot/Application.java @@ -0,0 +1,34 @@ +package com.ceos24.spring_boot; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; + +import org.springframework.boot.CommandLineRunner; +import org.springframework.context.ApplicationContext; +import java.util.Arrays; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + + SpringApplication.run(Application.class, args); + } + + @Bean + public CommandLineRunner commandLineRunner (ApplicationContext ctx) { + return args -> { + System.out.println("Let's inspect the beans provided by Spring Boot:"); + + + //Spring Boot 에서 제공되는 Bean 확인 + String[] beanNames = ctx.getBeanDefinitionNames(); + Arrays.sort(beanNames); + for (String beanName : beanNames) { + System.out.println(beanName); + } + }; + } + +} diff --git a/src/main/java/com/ceos24/spring_boot/HelloController.java b/src/main/java/com/ceos24/spring_boot/HelloController.java new file mode 100644 index 0000000..20d761b --- /dev/null +++ b/src/main/java/com/ceos24/spring_boot/HelloController.java @@ -0,0 +1,13 @@ +package com.ceos24.spring_boot; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class HelloController { + + @GetMapping + public String hello(){ + return "Hello, Spring Boot!"; + } +} diff --git a/src/main/java/com/ceos24/spring_boot/Test.java b/src/main/java/com/ceos24/spring_boot/Test.java new file mode 100644 index 0000000..6440321 --- /dev/null +++ b/src/main/java/com/ceos24/spring_boot/Test.java @@ -0,0 +1,15 @@ +package com.ceos24.spring_boot; + + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import lombok.Data; + +@Data +@Entity +public class Test { + + @Id + private Long id; + private String name; +} diff --git a/src/main/java/com/ceos24/spring_boot/TestController.java b/src/main/java/com/ceos24/spring_boot/TestController.java new file mode 100644 index 0000000..66aa4ed --- /dev/null +++ b/src/main/java/com/ceos24/spring_boot/TestController.java @@ -0,0 +1,21 @@ +package com.ceos24.spring_boot; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping(value="/tests") +public class TestController { + + private final TestService testService; + + @GetMapping + public List getTests() { + return testService.findAllTests(); + } +} diff --git a/src/main/java/com/ceos24/spring_boot/TestRepository.java b/src/main/java/com/ceos24/spring_boot/TestRepository.java new file mode 100644 index 0000000..01a5d96 --- /dev/null +++ b/src/main/java/com/ceos24/spring_boot/TestRepository.java @@ -0,0 +1,7 @@ +package com.ceos24.spring_boot; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface TestRepository extends JpaRepository { + +} diff --git a/src/main/java/com/ceos24/spring_boot/TestService.java b/src/main/java/com/ceos24/spring_boot/TestService.java new file mode 100644 index 0000000..2e2c6d5 --- /dev/null +++ b/src/main/java/com/ceos24/spring_boot/TestService.java @@ -0,0 +1,21 @@ +package com.ceos24.spring_boot; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class TestService { + + private final TestRepository testRepository; + + /*Read All*/ + @Transactional(readOnly = true) + public List findAllTests() + { + return testRepository.findAll(); + } +} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml new file mode 100644 index 0000000..fbd24fa --- /dev/null +++ b/src/main/resources/application.yaml @@ -0,0 +1,18 @@ +spring: + datasource: + url: jdbc:mysql://localhost:3306/${database}?allowPublicKeyRetrieval=true&useSSL=false&characterEncoding=UTF-8 + username: root + password: ${password} + driver-class-name: com.mysql.cj.jdbc.Driver + + jpa: + hibernate: + ddl-auto: create + show-sql: true + properties: + hibernate: + format_sql: true + +logging: + level: + org.hibernate.SQL: debug \ No newline at end of file diff --git a/src/test/java/com/ceos24/spring_boot/ApplicationTests.java b/src/test/java/com/ceos24/spring_boot/ApplicationTests.java new file mode 100644 index 0000000..26bf847 --- /dev/null +++ b/src/test/java/com/ceos24/spring_boot/ApplicationTests.java @@ -0,0 +1,13 @@ +package com.ceos24.spring_boot; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class ApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/src/test/java/com/ceos24/spring_boot/HelloControllerTest.java b/src/test/java/com/ceos24/spring_boot/HelloControllerTest.java new file mode 100644 index 0000000..fe2a6c2 --- /dev/null +++ b/src/test/java/com/ceos24/spring_boot/HelloControllerTest.java @@ -0,0 +1,33 @@ +package com.ceos24.spring_boot; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + + +@SpringBootTest +@AutoConfigureMockMvc +public class HelloControllerTest { + + @Autowired + private MockMvc mvc; + + @DisplayName("HelloController의 hello() 메서드 테스트") + @Test + public void hello() throws Exception { + mvc.perform(get("/")) + .andExpect(status().isOk()) + .andExpect(result -> + { + String response = result.getResponse().getContentAsString(); + assert response.equals("Hello, Spring Boot!"); + }); + + } +} From 4c7d47445f5d312d523f6e23318d0799463b08ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=84=9C=EC=98=81?= Date: Wed, 9 Sep 2026 14:18:28 +0900 Subject: [PATCH 2/4] =?UTF-8?q?docs:=20README.md=20=EC=9E=91=EC=84=B1=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1338 ++++++++++++++++++++++++++++++++++++++++++++++++++++- img.png | Bin 0 -> 6198 bytes img_1.png | Bin 0 -> 1349 bytes 3 files changed, 1337 insertions(+), 1 deletion(-) create mode 100644 img.png create mode 100644 img_1.png diff --git a/README.md b/README.md index 74dd775..85ce32c 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,1338 @@ -### 1. spring-tutorial-24th +# 1. spring-tutorial-24th +![image.png](attachment:6caccc9e-9409-4233-b9f0-9deca044ae7b\:image.png) + +![image.png](attachment:24c454d2-fb10-49fb-b5ef-6198ba53e021\:image.png) + +## 2. spring이 지원하는 기술들 + +### 1 . POJO 란? + +**POJO :** Plain Old Java Project , 즉 순수한 오래된 자바 객체 + +```java +public class User { + private String name; + private int age; + + public User(String name, int age) { + this.name = name; + this.age = age; + } + + public String getName() { + return name; + } +} + +``` + +- 특정 클래스를 상속하거나 특별한 인터페이스를 구현하지 않아도 됨 . + += Java나 Java의 스펙에 정의된 것 이외에는 다른 기술에 얽매이지 않아야 함 . + +- Service 와 같은 클래스들도 기본적으로 어노테이션으로 관리함 + +```java +@Service +public class OrderService { + + @Transactional + public void createOrder() { + System.out.println("주문 생성"); + } +} + +``` + += 즉 ,비즈니스 로직이 프레임워크에 과하게 의존하지 않도록 만드는 것이 POJO 방식의 핵심 . + +--- + +#### POJO 이전의 방식 + +→ EJB ( Enterprise Java Beans ) 로 개발 . + +- EJB 컨테이너의 역할 : + +→ 트랜잭션 관리 ,보안, 동시성 처리 등을 대신 처리해줌 . + +**BUT )) 개발자가 비즈니스 객체를 만들 때 EJB가 정한 규칙을 반드시 따랐어야 했음** + +#### EJB의 문제점 + +- 특정 인터페이스와 클래스를 구현해야 함 +- EJB 컨테이너 없이는 실행과 테스트가 어려움 +- 코드가 EJB 기술에 강하게 의존함 +- 작은 기능을 개발하기에도 구조가 무거움 + +⇒ 이 문제점들을 타파하기 위해 Spring은 **POJO로 객체를 평범하게 작성한 뒤 필요한 기능을 컨테이너가 붙여주는 방식을 사용 !!** + +--- + +#### POJO의 장점 및 조건 + +- 조건 + - 특정 클래스를 상속 X + - 특정 인터페이스를 강제로 구현하지 않음 + - 특정 어노테이션에 과도하게 의존하지 않는다 . +- 장점 + - 프레임워크 의존성이 낮다 . + - 코드가 단순하다 . ( 불필요한 상속이나 구현 메서드 없이 비즈니스 로직에 집중할 수 있음. ) + - 테스트 하기 쉬움 ( 객체 직접 생성해서 단위 테스트 할 수 있음 ) + - 유지보수가 쉬움 + - 객체 지향 설계에 유리 +- POJO 인 경우 + +```java +public class OrderService { + + public void createOrder() { + System.out.println("주문을 생성합니다."); + } +} + +``` + +- POJO 가 아닌 경우 + +```text +public class OrderServiceBean implements SessionBean { + + public void createOrder() { + System.out.println("주문을 생성합니다."); + } + +``` + +--- + +### 2. Spring 삼각형 + +![image.png](attachment\:b3becc1d-eee1-4717-ab4e-4a5ab8e9dd7f\:image.png) + +**Spring이 POJO 개발을 가능하게 만드는 세 가지 핵심 기술** + +```mermaid +flowchart TB + POJO["POJO
평범한 Java 객체"] + DI["IoC / DI
객체 생성과 연결"] + AOP["AOP
공통 기능 분리"] + PSA["PSA
기술 차이 숨김"] + + DI --> POJO + AOP --> POJO + PSA --> POJO + +``` + +즉 , Spring의 핵심 = **평범한 Java 객체인 POJO를 유지하면서도 DI, AOP, PSA를 통해 엔터프라이즈 기능을 사용할 수 있게 하는 것** + +#### 1 ) IoC/ DI : 객체의 생성과 연결을 Spring이 담당 + +- **IoC(제어의 역전)**: 객체 생성과 관리의 주도권이 개발자에서 Spring으로 넘어감 + +⇒ 설계 원칙 : 외부에 객체의 생성/소멸 맡김 + +- **DI(의존성 주입)**: Spring이 필요한 객체를 외부에서 넣어줌 + +⇒ 디자인 패턴 / 구현 받음 ( 필요한 객체를 외부에서 주입받음 ) + +--- + +**IoC : 제어의 역전** + +객체의 생성/ 흐름 제어 권한을 **개발자가 아닌 외부 시스템 ( 프레임워크 , 컨테이너 )** 에 위임하는 방식 + +- 기존 + +```java +public class OrderService { + +private final OrderRepository orderRepository + = new MyOrderRepository(); + +} + +``` + +→ OrderService가 MyOrderRepository를 직접 생성하기 때문에 두 클래스가 강하게 결합됨 . + +**⇒ cf )) 이 코드는 왜 단위 테스트가 어려운가 ?** + +: 단위테스트에서는 보통 실제 DB 대신 가짜 객체를 사용함 . + +**문제점 : 변경에 열려 있고 확장에 닫혀 있다 ( OCP에 위배됨 )** + +→ 즉 , 개발자가 직접 객체를 생성하거나 소멸시키면 메모리 낭비도 생김. + +그러므로 객체의 생명주기 관리를 외부에 위임. + +- Spring + +```text +@Service +public class OrderService +{ + +private final OrderRepository orderRepository; + +public OrderService(OrderRepository orderRepository) +{ +this.orderRepository= orderRepository; + +} + + +``` + +⇒ 결합도가 낮아져 구현체 교체와 생명주기 관리가 편리해짐 . + +--- + +**Spring Container의 역할** + +→ 객체의 제어권을 가짐 ! + +`🌟Bean` : Spring 이 관리하는 객체 + +```text +개발자: 어떤 객체를 사용할지 설정 +Spring Container: 객체 생성 → 의존관계 연결 → 관리 → 소멸 + +``` + +**1 ) 객체 생성** + +```java +@Repository +public class MyOrderRepository { +} + +``` + +→ `@Service` 등과 같은 어노테이션이 붙은 클래스를 객체로 생성함 . + +**2 ) 객체 사이의 의존관계를 연결** + +```java +@Service +public class OrderService { + + private final OrderRepository repository; + + public OrderService(OrderRepository repository) { + this.repository = repository; + } +} + +``` + +`OrderService`에는 `OrderRepository`가 필요!! + +Spring Container는 자신이 관리하는 Bean 중에서 알맞은 객체를 찾아 생성자에 넣어줌. + +``` +Spring Container +├── MyOrderRepository Bean 생성 +└── OrderService Bean 생성 + └── MySqlOrderRepository Bean 주입 + +``` + +이것이 **DI(의존성 주입) !!** + +**3 ) 객체의 생명주기 관리** + +```text +Spring 실행 +→ Bean 생성 +→ 의존성 주입 +→ 초기화 +→ Bean 사용 +→ Spring 종료 +→ Bean 소멸 + +``` + +→ 개발자가 매번 직접 객체를 생성하거나 제거하지 않아도 됨. + +**4 ) 객체의 범위를 관리** + +→ **싱글톤**이 기본 ! ( 컨테이너 하나당 하나의 객체만 생성함 → 여러 곳에서 공유 ) + +**Spring Container의 종류** + +대표적으로 두 가지 인터페이스가 있음. + +| 종류 | 역할 | +| -------------------- | ----------------------------------------- | +| `BeanFactory` | Bean 생성과 DI 등 기본 기능 제공 | +| `ApplicationContext` | `BeanFactory` 기능 + 이벤트, 메시지, 환경설정 등 추가 기능 | + +실제 Spring 애플리케이션에서는 대부분 `ApplicationContext`를 Spring Container로 사용 !! + +```text +ApplicationContext context= +new AnnotationConfigApplicationContext(AppConfig.class); + +``` + +Spring Boot에서는 다음 코드가 실행될 때 `ApplicationContext`가 자동으로 만들어짐. + +```text +SpringApplication.run(Application.class,args); + +``` + +--- + +**IoC, DI와의 관계** + +- **IoC**: 객체의 생성·관리 권한을 개발자가 아니라 Spring Container가 가지는 설계 원칙 +- **DI**: Spring Container가 필요한 객체를 외부에서 넣어주는 구현 방법 +- **Spring Container**: IoC와 DI를 실제로 수행하는 주체 +- **Bean**: Spring Container가 생성하고 관리하는 객체 + +#### 3. 의존성 주입 방식 (DI) + +→ 제어의 역전 방법 중 하나로 , 외부 컨테이너가 생성한 객체를 주입받아서 사용하는 방식 + +**1. 생성자 주입 — 🌟🌟🌟가장 권장** + +```java +@Service +public class OrderService { + + private final OrderRepository repository; + + public OrderService(OrderRepository repository) { + this.repository = repository; + } +} + +``` + +- **특징:** 객체를 생성할 때 생성자로 의존성을 주입한다. +- **장점:** `final`을 사용할 수 있고, 의존성 누락을 방지하며 단위 테스트가 쉽다. + +--- + +**2. Setter 주입** + +```java +@Service +public class OrderService { + + private OrderRepository repository; + + @Autowired + public void setRepository(OrderRepository repository) { + this.repository = repository; + } +} + +``` + +- **특징:** 객체를 먼저 생성한 후 Setter로 의존성을 주입한다. +- **장점:** 객체 생성 이후에도 의존성을 변경하거나 선택적으로 주입할 수 있다. + +--- + +**3. 필드 주입** + +```java +@Service +public class OrderService { + + @Autowired + private OrderRepository repository; +} + +``` + +- **특징:** Spring이 필드에 직접 의존성을 주입한다. +- **장점:** 코드가 짧고 간단하다. +- **단점:** `final`을 사용할 수 없고, Spring 없이 단위 테스트하기 어렵다. + +| 방식 | 핵심 특징 | 핵심 장점 | +| --------------- | --------- | -------------------- | +| 생성자 주입 | 생성할 때 주입 | 불변성·필수 의존성·보장·테스트 용이 | +| Setter 주입 | 생성한 후 주입 | 의존성 변경 및 선택적 주입 가능 | +| 필드 주입 | 필드에 직접 주입 | 코드가 간단함 | + +**결론: 특별한 이유가 없다면 생성자 주입을 사용하면 된다.** + +--- + +#### cf ) 생성자로 의존성을 주입받는 방법 : 추가 예시 + +```java +@Component +public class Car { + + private final Tire tire; + + public Car(Tire tire) { + this.tire = tire; + } + + public void drive() { + tire.roll(); + } +} + +``` + +##### 1. 필수 의존성 보장 + +`Tire` 없이는 `Car`를 생성할 수 없다. + +```text +new Car(); // 컴파일 오류 +new Car(new KoreaTire()); // 정상 + +``` + +##### 2. 불변성 보장 + +`final`을 사용하므로 객체 생성 이후면 주입받은 `Tire`를 변경할 수 없다. + +```text +private final Tire tire; + +``` + +##### 3. 순환 참조를 빠르게 발견 + +```java +class Car { + Car(Driver driver) {} +} + +class Driver { + Driver(Car car) {} +} + +``` + +`Car`와 `Driver`가 서로를 필요로 하므로 Spring이 객체를 생성할 수 없다. 따라서 애플리케이션 시작 시점에 잘못된 의존관계를 발견할 수 있다. + +##### 4. POJO 단위 테스트가 쉬움 + +Spring을 실행하지 않고 Mock 객체를 직접 주입할 수 있다. + +```text +@Test +void 자동차_주행_테스트() { + Tire mockTire = mock(Tire.class); + Car car = new Car(mockTire); + + car.drive(); + + verify(mockTire).roll(); +} + +``` + +**핵심:** 생성자 주입은 의존성 누락과 변경을 방지하고, 순환 참조를 빠르게 발견하며, Mock을 사용한 단위 테스트를 쉽게 만든다. + +**++ )) 개발 편의성을 위해 라이브러리로 롬복 사용 !! 🌟🌟** + +(`@RequiredArgsConstructor` + `final` 조합) + +```java +@RequiredArgsConstructor +public class Car { + private final Tire tire; +} + +``` + +--- + +### **3. AOP** + +**Aspect - Oriented Programming ( 관점 지향 프로그래밍 )** + +⇒ 여러 클래스에 반복되는 **공통 기능을 핵심 비즈니스 로직에서 분리하는 프로그래밍 방식** + +- **관점이란 ?** + +어떤 기능을 구현할 때 , 그 기능을 핵심 / 부가 기능으로 구분해서 각각을 하나의 관점으로 보는 것을 말함 . + +#### AOP의 주요 용어 + +| 용어 | 의미 | +| ---------- | ----------------------------- | +| Aspect | 공통 기능을 모아놓은 클래스 | +| Advice | 실제로 수행할 공통 기능 | +| Pointcut | 공통 기능을 적용할 대상 지정 | +| Join Point | 공통 기능을 적용할 수 있는 지점 | +| Target | 실제 비즈니스 로직을 가진 객체 | +| Proxy | Target 앞에서 AOP 기능을 실행하는 대리 객체 | + +#### Advice의 종류 + +| 어노테이션 | 실행 시점 | +| ----------------- | ------------- | +| `@Before` | 메서드 실행 전 | +| `@After` | 메서드 실행 후 | +| `@AfterReturning` | 메서드가 정상 종료된 후 | +| `@AfterThrowing` | 예외 발생 후 | +| `@Around` | 메서드 실행 전후 | + +#### AOP를 사용하지 않은 경우 + +```text +public void createOrder() { + long start = System.currentTimeMillis(); + + // 핵심 기능 + System.out.println("주문 생성"); + + long end = System.currentTimeMillis(); + System.out.println("실행 시간: " + (end - start)); +} + +``` + +```text +public void pay() { + long start = System.currentTimeMillis(); + + // 핵심 기능 + System.out.println("결제 처리"); + + long end = System.currentTimeMillis(); + System.out.println("실행 시간: " + (end - start)); +} + +``` + +실행 시간 측정 코드가 모든 메서드에 반복되고, 핵심 로직과 부가 기능이 섞임. + +#### AOP를 적용한 경우 + +비즈니스 로직에는 핵심 기능만!! + +``` +@Service +public class OrderService { + + public void createOrder() { + System.out.println("주문 생성"); + } +} + +``` + +실행 시간 측정은 별도의 클래스에서 처리 + +``` +@Aspect //Aspect = 공통 기능 클래스 +@Component +public class TimeTraceAspect { + + //Advice = 실제로 수행할 공통 기능 + @Around( + "execution(* com.example..*(..))" + //PointCut = 공통 기능 적용 대상 지정 + ) + + public Object measureTime(ProceedingJoinPoint joinPoint) + throws Throwable { + + long start = System.currentTimeMillis(); + + try { + return joinPoint.proceed(); // 실제 Target 메서드 실행 + } finally { + long end = System.currentTimeMillis(); + System.out.println("실행 시간: " + (end - start)); + } + } +} + +``` + +< 실행 순서 > + +```text +프록시가 요청을 받음 +→ AOP의 실행 시간 측정 시작 +→ 실제 createOrder() 실행 +→ AOP의 실행 시간 측정 종료 + +``` + +#### Spring AOP의 동작 원리 + +→ 실제 객체 앞에 **프록시 객체**를 만들어둠 . + +```text +호출자 + ↓ +Proxy 객체 + ├── 공통 기능 실행 + ├── 실제 객체의 메서드 실행 + └── 공통 기능 마무리 + +``` + +실제로는 이렇게 동작하게 됨 . + +``` +public void createOrder() { + startTransaction(); + + try { + realOrderService.createOrder(); + commit(); + } catch (Exception e) { + rollback(); + } +} + +``` + +개발자가 이 코드를 직접 작성하지 않아도 Spring이 프록시를 통해 앞뒤에 붙여준다. + +**대표적인 AOP 활용:** **`@Transactional`** + +**AOP를 사용하는 기능** + +- 트랜잭션 관리 +- 로그 기록 +- 실행 시간 측정 +- 보안 및 권한 검사 +- 예외 처리 +- 모니터링 + +**AOP의 장점** + +- 공통 코드의 중복을 줄일 수 있다. +- 비즈니스 로직과 부가 기능을 분리할 수 있다. +- 공통 기능을 한 곳에서 수정할 수 있다. +- 핵심 로직의 가독성과 유지보수성이 좋아진다. + +**cf ) OOP와 AOP의 차이** + +**Object-Oriented Programming, 객체 지향 프로그래밍)** + +=프로그램을 여러 개의 **객체**로 나누고, 객체들이 서로 협력하도록 만드는 프로그래밍 방식 + +**핵심 비유 :** + +- **OOP:** 업무를 주문팀, 결제팀, 회원팀으로 나눈다. +- **AOP:** 모든 팀에 필요한 출퇴근 기록이나 보안 검사를 별도 관리팀이 담당한다. + +| 구분 | OOP | AOP | +| ----------- | --------------- | -------------- | +| 기준 | 역할과 책임 | 공통 관심사 | +| 나누는 대상 | 주문·결제·회원 등의 객체 | 로그·트랜잭션·보안 등 | +| 목적 | 핵심 기능을 객체별로 분리 | 중복되는 부가 기능을 분리 | +| 관계 | 프로그램의 기본 구조를 설계 | OOP의 중복 문제를 보완 | + +**한 줄 정리:** + +AOP는 로깅·트랜잭션처럼 여러 곳에서 반복되는 공통 기능을 별도로 분리하고, Spring 프록시가 실제 메서드 실행 전후에 그 기능을 적용하는 방식 !! + +--- + +### PSA ( Portable Service Abstraction ) + +→ 하나의 추상화로 여러 서비스를 묶어둔 것 . + +즉, 내부 기술이 달라져도 개발자가 사용하는 방법은 같게 만들어주는 것 + +**대표 예시:** **`@Transactional`** + +DBC와 JPA는 내부 트랜잭션 처리 방식이 다름. + +#### JDBC 방식 + +``` +Connection connection = dataSource.getConnection(); + +try { + connection.setAutoCommit(false); + + // 비즈니스 로직 실행 + + connection.commit(); +} catch (Exception e) { + connection.rollback(); +} + +``` + +#### Spring PSA 사용 + +``` +@Transactional +public void createOrder() { + orderRepository.save(order); +} + +``` + +BUT 개발자는 JDBC를 사용하든 JPA를 사용하든 동일하게 `@Transactional`만 사용하면 됨 !! + +```text +개발자 + ↓ +@Transactional + ↓ +Spring의 트랜잭션 추상화 + ↓ +JDBC / JPA / Hibernate에 맞는 실제 처리 + +``` + +⇒ Spring이 내부에서 사용하는 기술에 맞는 트랜잭션 관리자를 선택해서 처리해줌. + +#### PSA의 장점 + +- 기술마다 다른 사용법을 모두 알지 않아도 된다. +- 내부 기술이 변경되어도 비즈니스 코드의 변경이 적다. +- 특정 기술에 대한 의존성이 낮아진다. +- 코드를 일관된 방식으로 작성할 수 있다. + +### 3. Spring Bean + +#### 1. 스프링 빈 이란 ? + +→ Spring Container가 생성하고 관리하는 Java 객체 ( 즉 , IoC 컨테이너가 생성함 ) + +→ 클래스 위에 `@Component` 또는 설정 클래스 메서드에 `@Bean` 을 선언함 + +#### 2. 스프링 빈 라이프사이클 + +→ 생성 및 소멸 과정 + +```text +Bean 정보 등록 ( Bean Definition 등록 ) +→ 객체 생성 +→ 의존성 주입 +→ 초기화 +→ 사용 +→ 소멸 + +``` + +#### ① BeanDefinition 등록 + +Spring이 어떤 클래스를 Bean으로 생성할지에 대한 설계 정보를 등록한다. + +``` +Bean 이름: orderService +Bean 클래스: OrderService +Scope: singleton +Lazy 여부: false + +``` + +→ 아직 실제 객체가 만들어진 것이 아님 ! + +#### ② 객체 생성 + +BeanDefinition을 바탕으로 실제 객체를 생성한다. + +``` +new OrderService(); + +``` + +#### ③ 의존성 주입 + +생성자, Setter, 필드 등을 통해 필요한 Bean을 주입한다. + +``` +new OrderService(orderRepository); + +``` + +#### ④ 초기화 + +의존성 주입이 끝나면 초기화 메서드가 실행된다. + +``` +@Component +public class DatabaseClient { + + // ( 의존성 주입 완료 후 ) + @PostConstruct + public void init() { + System.out.println("Bean 초기화"); + } +} + +``` + +#### ⑤ Bean 사용 + +초기화가 완료된 Bean을 다른 객체에 주입하여 사용한다. + +#### ⑥ Bean 소멸 + +Spring Container가 종료될 때 소멸 메서드가 실행된다. + +``` +@Component +public class DatabaseClient { + + @PreDestroy + public void close() { + System.out.println("Bean 소멸"); + } +} + +``` + +#### 3. Bean Scope + +→ Bean 객체가 생성되고 유지되는 범위 ! + +#### 스코프의 종류 + +| Scope | 의미 | +| ------------- | ---------------------------- | +| `singleton` | Spring Container마다 하나의 객체 생성 | +| `prototype` | Bean을 요청할 때마다 새로운 객체 생성 | +| `request` | HTTP 요청마다 하나의 객체 생성 | +| `session` | HTTP 세션마다 하나의 객체 생성 | +| `application` | 웹 애플리케이션마다 하나 생성 | +| `websocket` | WebSocket 세션마다 하나 생성 | + +#### Singleton Scope + +Spring Bean의 기본 Scope ! + +``` +@Component +public class OrderService { +} + +``` + +``` +OrderService service1 = context.getBean(OrderService.class); +OrderService service2 = context.getBean(OrderService.class); + +System.out.println(service1 == service2); // true + +``` + +같은 Bean을 여러 번 요청해도 동일한 객체를 반환. + +싱글톤 Bean은 여러 요청이 공유하기 때문에 가급적 **변경 가능한 상태를 필드에 저장하지 않는 것**이 좋음 ! + +→ 무상태로 설계해야 굳 . ( 필드 저장하지 않고 파라미터로 넘기거나 결과 즉시 반환하는 방식 ) + +#### Prototype Scope + +요청할 때마다 새로운 Bean을 생성함 . + +``` +@Component +@Scope("prototype") +public class OrderService { +} + +``` + +``` +OrderService service1 = context.getBean(OrderService.class); +OrderService service2 = context.getBean(OrderService.class); + +System.out.println(service1 == service2); // false + +``` + +주의할 점 = Spring이 Prototype Bean의 **생성과 초기화까지만 관리하고, 소멸은 관리하지 않는다.** + +#### 4. Annotation 이란 ? + +클래스, 메서드, 필드 등에 추가하는 **메타데이터** + +``` +@Service +public class OrderService { +} + +``` + +`@Service` 자체가 객체를 생성하는 코드는 아님. + +Spring이 해당 표시를 읽고 `OrderService`를 Bean으로 등록 ! + +#### 대표적 Annotation + +| 어노테이션 | 주로 사용하는 계층 | +| ----------------- | ------------------- | +| `@Component` | 일반적인 Spring Bean | +| `@Controller` | MVC Controller | +| `@RestController` | REST API Controller | +| `@Service` | 비즈니스 로직 | +| `@Repository` | 데이터 접근 계층 | + +`@Service`와 `@Repository` 내부에는 `@Component`가 붙어 있다 . + +``` +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Component +public @interface Service { +} + +``` + +따라서 Spring은 `@Service`가 붙은 클래스도 Component로 인식함 + += **메타 어노테이션** + +#### 5. 어노테이션을 통한 Bean 등록 과정 + +``` +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} + +``` + +`@SpringBootApplication`은 크게 세 가지 어노테이션을 포함함 . + +``` +@SpringBootConfiguration +@EnableAutoConfiguration +@ComponentScan +public @interface SpringBootApplication { +} + +``` + +1. SpringApplication.run() +2. spring container 생성 +3. @ComponentScan 실행 +4. 컴포넌트 후보 탐색 +5. BeanDefinition 등록 +6. Bean 객체 생성 +7. 의존성 주입 및 초기화 + +#### 6. `@ComponentScan`의 탐색 과정 + +- 탐색 시작 위치 + +→ 아무 설정 없으면 `@ComponentScan` 붙은 클래스의 패키지부터 탐색 시작 + +⇒ 스캔 범위 밖에 있으면 Bean으로 발견되지 않으므로 실행 클래스를 항상 최상위 패키지에 ! + +```text +com.example +├── Application.java +├── controller +│ └── OrderController.java +├── service +│ └── OrderService.java +└── repository + └── OrderRepository.java + +``` + +#### ComponentScan의 내부 동작 과정 + +`@ComponentScan`은 지정한 패키지에서 컴포넌트를 찾아 Spring Bean으로 등록한다. + +```text +@ComponentScan("com.example") + +``` + +##### 동작 순서 + +``` +패키지 탐색 +→ @Component 계열 클래스 발견 +→ BeanDefinition 등록 +→ Bean 생성 +→ 의존성 주입 + +``` + +##### 1. 컴포넌트 탐색 + +Spring이 `com.example`과 하위 패키지에서 다음 어노테이션이 붙은 클래스를 찾는다. + +```text +@Component +@Service +@Repository +@Controller +@RestController + +``` + +예를 들어 다음 클래스가 발견된다. + +```java +@Service +public class OrderService { +} + +``` + +##### 2. BeanDefinition 등록 + +Spring은 발견한 클래스의 정보를 Container에 등록한다. + +``` +Bean 이름: orderService +Bean 타입: OrderService +Scope: singleton + +``` + +이때는 객체가 생성된 것이 아니라 **객체 생성에 필요한 설계 정보만 등록된 상태**다. + +##### 3. Bean 생성 및 의존성 주입 + +BeanDefinition을 바탕으로 실제 객체를 생성하고 연결한다. + +```text +OrderRepository repository = new OrderRepository(); +OrderService service = new OrderService(repository); + +``` + +이 과정을 Spring Container가 자동으로 처리한다. + +--- + +#### 같은 인터페이스의 구현체가 여러 개인 경우 + +```java +public interface PaymentService { + void pay(); +} + +``` + +```java +@Service +public class KakaoPaymentService implements PaymentService { + public void pay() { + System.out.println("카카오 결제"); + } +} + +``` + +```java +@Service +public class NaverPaymentService implements PaymentService { + public void pay() { + System.out.println("네이버 결제"); + } +} + +``` + +구현체가 두 개이므로 그냥 주입하면 Spring은 어떤 Bean을 넣을지 알 수 없다. + +```text +public OrderService(PaymentService paymentService) { + this.paymentService = paymentService; +} + +``` + +따라서 `NoUniqueBeanDefinitionException`이 발생한다. + +##### 방법 1: `@Qualifier` + +사용할 Bean을 정확히 지정한다. + +```text +public OrderService( + @Qualifier("kakaoPaymentService") + PaymentService paymentService +) { + this.paymentService = paymentService; +} + +``` + +##### 방법 2: `@Primary` + +기본으로 사용할 구현체를 지정한다. + +```java +@Primary +@Service +public class KakaoPaymentService implements PaymentService { +} + +``` + +이제 `PaymentService`를 주입하면 `KakaoPaymentService`가 선택된다. + +##### 방법 3: 모든 구현체 주입 + +```text +public PaymentManager( + List paymentServices +) { + this.paymentServices = paymentServices; +} + +``` + +같은 인터페이스를 구현한 모든 Bean을 사용할 때는 `List`로 주입받는다. + +⇒ 핵심 개념 정리 + +| 개념 | 핵심 | +| ---------------- | ------------------ | +| `@ComponentScan` | 패키지에서 컴포넌트를 탐색 | +| BeanDefinition | Bean을 만들기 위한 설계 정보 | +| `@Qualifier` | 사용할 Bean을 직접 지정 | +| `@Primary` | 기본으로 사용할 Bean 지정 | +| `List` | 같은 타입의 모든 Bean 주입 | + +### 4. Spring MVC + +#### 1. MVC 패턴과 Spring MVC + +--- + +##### 1 ) MVC 패턴이란 ? + +MVC는 애플리케이션을 세 역할로 나누는 **설계 패턴 ( 역할에 대한 설계 원칙 )** + +| 구성요소 | 역할 | Spring 예시 | +| ------------------ | --------------- | ------------------ | +| Model | 데이터와 비즈니스 결과 | DTO, Entity, Model | +| View | 사용자에게 보여줄 화면 | Thymeleaf, JSP | +| Controller | 요청을 받고 처리 흐름 제어 | `@Controller` | + +⇒ 비즈니스 로직과 UI를 분리해서 의존성을 낮춰서 유지보수 효율을 극대화 ! + +##### 2 ) Spring MVC 란 ? + +MVC 패턴을 실제 웹 애플리케이션에 사용할 수 있도록 구현한 웹 프레임워크 + +#### 2. Servlet + +--- + +##### 1 ) Servlet 이란 ? + +HTTP 요청을 받아서 Java 코드로 처리하고 응답을 만드는 객체 + +``` +public class HelloServlet extends HttpServlet { + + @Override + protected void doGet( + HttpServletRequest request, + HttpServletResponse response + ) throws IOException { + + response.getWriter().write("Hello"); + } +} + +``` + +Servlet은 혼자 실행되지 않고 **Servlet Container**가 생성과 호출, 생명주기를 관리 + +##### 2 ) Servlet 생명주기 + +``` +Servlet 생성 +→ init(): 최초 한 번 초기화 +→ service(): 요청이 올 때마다 호출 +→ destroy(): 서버 종료 시 호출 + +``` + +HTTP 요청 방식에 따라 `service()`가 적절한 메서드를 호출함. + +``` +GET → doGet() +POST → doPost() +PUT → doPut() +DELETE → doDelete() + +``` + +Spring MVC에서는 개발자가 Servlet을 요청마다 직접 만들지 않고, + +`DispatcherServlet`이라는 Servlet 하나가 요청을 받은 뒤 적절한 Controller로 전달해줌. + +#### 3. WAS와 TomCat + +--- + +##### 1 ) WAS 란? (Web Application Server ) + +HTTP 요청을 받아 Java 웹 애플리케이션을 실행할 수 있는 서버 환경 + +- 주요 역할 + - HTTP 요청과 응답 처리 + - Servlet 실행 + - 스레드 관리 + - 세션 관리 + - 보안 네트워크 관리 + - 웹 애플리케이션 배포 + +##### 2 ) TomCat 이란 ? + +대표적인 Servlet Container 이자 , Java 웹서버 . + +``` +브라우저 + ↓ HTTP 요청 +Tomcat + ↓ Servlet 호출 +DispatcherServlet + ↓ +Spring Controller + +``` + +🚩**Spring Boot** + +- 기본적으로 내장 Tomcat을 사용할 수 있음. +- 별도의 Tomcat 설치 없이 애플리케이션과 서버를 함께 실행! +- 기본 HTTP 포트는 `8080` + +```text +SpringApplication.run(Application.class, args); + +``` + +→ 이 코드 실행시 Spring Container 와 내장 TomCat도 함께 실행됨 + +##### 3 ) 웹 요청의 전체 흐름 + +```mermaid +flowchart TD + A["클라이언트 HTTP 요청"] --> B["Tomcat"] + B --> C["Filter Chain"] + C --> D["DispatcherServlet"] + D --> E["Controller"] + E --> F["Service와 Repository"] + F --> E + E --> G["JSON 또는 View"] + G --> A + +``` + +예시 요청 : + +**GET /members/1** + +< 처리 과정 > + +1. Tomcat이 HTTP 요청 수신 +2. HttpServletRequest와 HttpServletResponse 생성 +3. Filter Chain 실행 +4. DispatcherServlet 호출 +5. /members/1을 처리할 Controller 탐색 +6. MemberController.findMember(1) 실행 +7. 반환 객체를 JSON으로 변환 +8. Tomcat이 HTTP 응답 전송 + +#### **4 ) DispatcherServlet 이란 ?** + +**→ 모든 요청을 먼저 받고 적절한 Controller로 전달하는 Front Controller(중앙 컨트롤러) !** + +```text +요청 A ─┐ +요청 B ─┼→ DispatcherServlet → 적절한 Controller +요청 C ─┘ + +``` + +#### 5 ) DispatcherServlet의 주요 구성요소 + +| 구성요소 | 역할 | +| -------------------------- | ------------------------- | +| `HandlerMapping` | 요청을 처리할 Controller 메서드 탐색 | +| `HandlerAdapter` | 찾은 Controller 메서드를 실제로 호출 | +| `HandlerInterceptor` | Controller 호출 전후 공통 처리 | +| `HandlerExceptionResolver` | Controller에서 발생한 예외 처리 | +| `ViewResolver` | View 이름으로 실제 View 탐색 | +| `HttpMessageConverter` | Java 객체와 JSON 등을 변환 | + +#### Handler란? + +Spring MVC에서 Handler는 보통 실행할 Controller 메서드를 의미 !! + +``` +@GetMapping("/members/{id}") +public MemberResponse findMember(@PathVariable Long id) { +} + +``` + +--- + +#### 6 ) doDispatch ( ) 핵심 흐름 + +→ DispatcherServlet 에서 실제 요청 분배를 담당하는 핵심 메서드 + +- 핵심 순서 + +1. **핸들러 조회 – `getHandler()`** + `HandlerMapping`을 통해 요청 URL을 처리할 컨트롤러 메서드를 찾는다. +2. **핸들러 어댑터 조회 –** **`getHandlerAdapter()`** + 찾은 핸들러를 실제로 실행할 수 있는 `HandlerAdapter`를 선택한다. +3. **인터셉터 전처리 –** **`applyPreHandle()`** + 컨트롤러 실행 전에 `preHandle()`을 호출하여 인증, 권한, 로그 등을 처리한다. `false`가 반환되면 요청 처리를 중단한다. +4. **핸들러 실행 – `ha.handle()`** + `HandlerAdapter`가 요청 데이터를 매개변수로 변환하고 실제 컨트롤러 메서드를 호출한다. +5. **처리 결과 반환 –** **`ModelAndView`** + 일반 MVC에서는 컨트롤러 실행 결과를 Model 데이터와 View 정보가 담긴 `ModelAndView`로 반환한다. +6. **인터셉터 후처리 –** **`applyPostHandle()`** + 컨트롤러 실행 후, View를 렌더링하기 전에 `postHandle()`을 호출한다. +7. **결과 처리 –** **`processDispatchResult()`** + 예외가 발생했다면 `HandlerExceptionResolver`로 처리하고, 정상적인 경우 View를 렌더링한다. +8. **View 렌더링 및 응답** + `ViewResolver`가 논리적 View 이름을 실제 View로 변환하고, 완성된 HTML을 클라이언트에게 응답한다. +9. **요청 완료 처리 –** **`afterCompletion()`** + 응답 처리가 끝난 후 인터셉터의 `afterCompletion()`을 실행하여 로그 기록이나 리소스 정리 등의 작업을 수행한다. + +- 코드 + +```text +protected void doDispatch(request, response) { + + // 1. 요청을 처리할 Controller 찾기 (getHandler()) + HandlerExecutionChain handler = getHandler(request); + + // 2. Controller 호출 방법 찾기 + HandlerAdapter adapter = + getHandlerAdapter(handler.getHandler()); + + // 3. 인터셉터 실행 + handler.applyPreHandle(request, response); + + // 4. Controller 호출 + ModelAndView mv = + adapter.handle(request, response, handler); + + // 5. Controller 실행 후 인터셉터 + handler.applyPostHandle(request, response, mv); + + // 6. View 렌더링 또는 예외 처리 + processDispatchResult(request, response, handler, mv); +} + +``` diff --git a/img.png b/img.png new file mode 100644 index 0000000000000000000000000000000000000000..615ba575b30cfd94f320a1318a3ddc26addd5708 GIT binary patch literal 6198 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 literal 0 HcmV?d00001 diff --git a/img_1.png b/img_1.png new file mode 100644 index 0000000000000000000000000000000000000000..88593ae510bef69d7e3483130b6208ef10893c9b GIT binary patch literal 1349 zcmeAS@N?(olHy`uVBq!ia0y~yVAKGzJ2=>YWJpe#GXn!FkEe@cNJZS+ySH<1dr7zj zPJa*-)a3oKIElH^Q&#KX(j_fZ9tp|(VN{!8EceQ*bH3j-CYIwycS(uYP}jFYoW~UoXcHJ(JCA zMgt2_SV%)*ngioT7OoDUE}cfEBxD_@qGKCaE;}%)2tZVY@n1PV-`@V-{rksHGN?zJ z2AJ;93pfxJ#ye$o-GNyMGuDc&=(>FS_H7Hv2RW-*S6MVMT~k3ZeKp&vkR^40f0_ER zS=@Zp$Q02Hal_Wg1+SDNZyn!s=GdBF&!>O+EE{!i=d~EIf>~#oLg!0|_8aZ~b=`jd zlKJm1d^)%F*`|ieN7z>N#qi1Qog=g&x$i{P)^}0oav~4p=7_B@^*i~iLMG~-p?iuk z6YK4pIsxrxnl8y-wXwIaulf1usr~u*W`~1i@6-ZBm;ZYl^D4u0!Gr&A^O#KQ;`0Ri zqt`Ic5MIHYw~;U5-F(g~IscO9Y~o9ZEf-!PRlKV4oCyLqkzG29B>^GkW**B#rImi92RUK5vIQBrDBeoj!bzy>LZMU!4GI-uy? z{Nwi=WB#1P%?@9~4^Gp2dfzGfdsO(-Y17PaAFI3d46)P?-l zGxoZ!i2t86a|3H*6vwHZV1?N%SvBXAW`~8#-nL@ts@@p6;8*Z0U4P44yQceX^t&55 zeOkEr)Nk&lUti>#thIPkw%a~FeY?KSUzx{WmR|U}Ui!JF=70GMw{-%%Z}nX&dsLV{ z(|3LS_R3q=KmIhkzcTHj)$yFWf45|_ui}5AS^nVgv`0*#*5UPBm&$hBjE=pv(no&D zw!_)>bq@RcmK+lU$tBBJOBSJq0GMWoms%v%iA`tss-Bpq0r~mgrx;*a__tgvVXqg z-^bF;4riBr<+J721lqmSo-4EY+*__#W42YjceniX>%6Snd)t<6r{wWRX&YzyzLu?$ z3p&F3D&CasRfPA(l`s81?kHyxO|mhvy0-6zgv#@K(Cj46zRFMfoTVwFMaoo%v+oMn zc3e5ysO23#i9t^ho-?wiI&9s(;LYw#fd?V0SxyOx6!4ZB7<2mY z_4V8qm-b!)GyvUDiJZOtjy;Ci$?}eS3j3^P6 Date: Wed, 9 Sep 2026 14:22:28 +0900 Subject: [PATCH 3/4] =?UTF-8?q?fix:=20README.md=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 11 +++++------ image (12).png | Bin 0 -> 7617 bytes image (13).png | Bin 0 -> 1663 bytes img.png | Bin 6198 -> 0 bytes img_1.png | Bin 1349 -> 0 bytes 5 files changed, 5 insertions(+), 6 deletions(-) create mode 100644 image (12).png create mode 100644 image (13).png delete mode 100644 img.png delete mode 100644 img_1.png diff --git a/README.md b/README.md index 85ce32c..f6956e2 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,9 @@ # 1. spring-tutorial-24th -![image.png](attachment:6caccc9e-9409-4233-b9f0-9deca044ae7b\:image.png) +![image (12).png](image%20%2812%29.png) +![image (13).png](image%20%2813%29.png) -![image.png](attachment:24c454d2-fb10-49fb-b5ef-6198ba53e021\:image.png) - -## 2. spring이 지원하는 기술들 +# 2. spring이 지원하는 기술들 ### 1 . POJO 란? @@ -699,7 +698,7 @@ JDBC / JPA / Hibernate에 맞는 실제 처리 - 특정 기술에 대한 의존성이 낮아진다. - 코드를 일관된 방식으로 작성할 수 있다. -### 3. Spring Bean +# 3. Spring Bean #### 1. 스프링 빈 이란 ? @@ -1104,7 +1103,7 @@ public PaymentManager( | `@Primary` | 기본으로 사용할 Bean 지정 | | `List` | 같은 타입의 모든 Bean 주입 | -### 4. Spring MVC +# 4. Spring MVC #### 1. MVC 패턴과 Spring MVC diff --git a/image (12).png b/image (12).png new file mode 100644 index 0000000000000000000000000000000000000000..b879a12fde2e52b9302033bb51aaaa74519b7f90 GIT binary patch literal 7617 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 P(*WF@J)LMrLsNbS>s9z{ literal 0 HcmV?d00001 diff --git a/img.png b/img.png deleted file mode 100644 index 615ba575b30cfd94f320a1318a3ddc26addd5708..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6198 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 diff --git a/img_1.png b/img_1.png deleted file mode 100644 index 88593ae510bef69d7e3483130b6208ef10893c9b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1349 zcmeAS@N?(olHy`uVBq!ia0y~yVAKGzJ2=>YWJpe#GXn!FkEe@cNJZS+ySH<1dr7zj zPJa*-)a3oKIElH^Q&#KX(j_fZ9tp|(VN{!8EceQ*bH3j-CYIwycS(uYP}jFYoW~UoXcHJ(JCA zMgt2_SV%)*ngioT7OoDUE}cfEBxD_@qGKCaE;}%)2tZVY@n1PV-`@V-{rksHGN?zJ z2AJ;93pfxJ#ye$o-GNyMGuDc&=(>FS_H7Hv2RW-*S6MVMT~k3ZeKp&vkR^40f0_ER zS=@Zp$Q02Hal_Wg1+SDNZyn!s=GdBF&!>O+EE{!i=d~EIf>~#oLg!0|_8aZ~b=`jd zlKJm1d^)%F*`|ieN7z>N#qi1Qog=g&x$i{P)^}0oav~4p=7_B@^*i~iLMG~-p?iuk z6YK4pIsxrxnl8y-wXwIaulf1usr~u*W`~1i@6-ZBm;ZYl^D4u0!Gr&A^O#KQ;`0Ri zqt`Ic5MIHYw~;U5-F(g~IscO9Y~o9ZEf-!PRlKV4oCyLqkzG29B>^GkW**B#rImi92RUK5vIQBrDBeoj!bzy>LZMU!4GI-uy? z{Nwi=WB#1P%?@9~4^Gp2dfzGfdsO(-Y17PaAFI3d46)P?-l zGxoZ!i2t86a|3H*6vwHZV1?N%SvBXAW`~8#-nL@ts@@p6;8*Z0U4P44yQceX^t&55 zeOkEr)Nk&lUti>#thIPkw%a~FeY?KSUzx{WmR|U}Ui!JF=70GMw{-%%Z}nX&dsLV{ z(|3LS_R3q=KmIhkzcTHj)$yFWf45|_ui}5AS^nVgv`0*#*5UPBm&$hBjE=pv(no&D zw!_)>bq@RcmK+lU$tBBJOBSJq0GMWoms%v%iA`tss-Bpq0r~mgrx;*a__tgvVXqg z-^bF;4riBr<+J721lqmSo-4EY+*__#W42YjceniX>%6Snd)t<6r{wWRX&YzyzLu?$ z3p&F3D&CasRfPA(l`s81?kHyxO|mhvy0-6zgv#@K(Cj46zRFMfoTVwFMaoo%v+oMn zc3e5ysO23#i9t^ho-?wiI&9s(;LYw#fd?V0SxyOx6!4ZB7<2mY z_4V8qm-b!)GyvUDiJZOtjy;Ci$?}eS3j3^P6 Date: Wed, 9 Sep 2026 14:24:52 +0900 Subject: [PATCH 4/4] =?UTF-8?q?fix:=20README.md=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 12 +++++++++++- image (14).png | Bin 0 -> 31817 bytes 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 image (14).png diff --git a/README.md b/README.md index f6956e2..9e0aaea 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,9 @@ ![image (12).png](image%20%2812%29.png) ![image (13).png](image%20%2813%29.png) + + + # 2. spring이 지원하는 기술들 ### 1 . POJO 란? @@ -108,7 +111,7 @@ public class OrderServiceBean implements SessionBean { ### 2. Spring 삼각형 -![image.png](attachment\:b3becc1d-eee1-4717-ab4e-4a5ab8e9dd7f\:image.png) +![image (14).png](image%20%2814%29.png) **Spring이 POJO 개발을 가능하게 만드는 세 가지 핵심 기술** @@ -698,6 +701,10 @@ JDBC / JPA / Hibernate에 맞는 실제 처리 - 특정 기술에 대한 의존성이 낮아진다. - 코드를 일관된 방식으로 작성할 수 있다. + + + + # 3. Spring Bean #### 1. 스프링 빈 이란 ? @@ -1103,6 +1110,9 @@ public PaymentManager( | `@Primary` | 기본으로 사용할 Bean 지정 | | `List` | 같은 타입의 모든 Bean 주입 | + + + # 4. Spring MVC #### 1. MVC 패턴과 Spring MVC diff --git a/image (14).png b/image (14).png new file mode 100644 index 0000000000000000000000000000000000000000..942fcb9860bab03d35d8802226c381ff057adb5e GIT binary patch literal 31817 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 Tc`9K1H-(OdzIv9brSJa$WXUlr literal 0 HcmV?d00001