๐Ÿ’ซ Spring

โœ”๏ธ Actuator

โœ… ์—‘์ธ„์—์ดํ„ฐ๋ž€?
์šด์˜ ํ™˜๊ฒฝ์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์žฅ์• ์— ๋Œ€๋น„ํ•ด ์Šคํ”„๋ง ๋ถ€ํŠธ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ํ”„๋กœ๋•์…˜ ์ค€๋น„ ๊ธฐ๋Šฅ๋“ค์ด๋‹ค. ํ˜„์žฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‚ด์•„์žˆ๋Š”์ง€, ๋กœ๊ทธ ์ •๋ณด๊ฐ€ ์ •์ƒ ์„ค์ • ๋˜์—ˆ๋Š”์ง€, ์ปค๋„ฅ์…˜ ํ’€์ด ์–ผ๋งˆ๋‚˜ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ๋Š”์ง€ ๋“ฑ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ค€๋‹ค. ๋” ๋‚˜์•„๊ฐ€ ๋งˆ์ดํฌ๋กœ๋ฏธ์–ด, ํ”„๋กœ๋ฉ”ํ…Œ์šฐ์Šค, ๊ทธ๋ผํŒŒ๋‚˜ ๊ฐ™์€ ์ตœ๊ทผ ์œ ํ–‰ ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์Šคํ…œ๊ณผ ์‰ฌ์šด ์—ฐ๋™์„ ์ œ๊ณตํ•œ๋‹ค.

โœ… ์ค€๋น„
์—‘์ธ„์—์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” build.gradle์— ์•„๋ž˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผํ•œ๋‹ค.

implementation 'org.springframework.boot:spring-boot-starter-actuator'

ํ˜น์€ spring initializr์—์„œ ์ƒ์„ฑํ•  ๋•Œ actuator๋ฅผ ์ถ”๊ฐ€ํ•ด ์ค€๋‹ค.

โœ… ํ™•์ธ

http://localhost:8080/actuator

์—‘์ธ„์—์ดํ„ฐ๊ฐ€ ์ œ๋Œ€๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” Main ํด๋ž˜์Šค๋ฅผ ์‹คํ–‰ํ•œ๋’ค http://localhost:8080/actuator ๋กœ ์ ‘์†ํ•˜์—ฌ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

โœ… ์•ก์ธ„์—์ดํ„ฐ์˜ ๊ธฐ๋Šฅ๋“ค์„ ์›น์— ๋…ธ์ถœ

  • ์—‘์ธ„์—์ดํ„ฐ๋ฅผ ์ฒ˜์Œ ํ™•์ธํ•ด ๋ณด๋ฉด ๊ธฐ๋Šฅ์ด ๋ช‡ ๊ฐœ๋ฐ–์— ๋ณด์ด์ง€ ์•Š๋Š”๋‹ค. ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ๋“ค์„ ์›น ํ™˜๊ฒฝ์— ๋…ธ์ถœํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” application.yml ํŒŒ์ผ์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ถ”๊ฐ€ํ•ด์ค˜์•ผ ํ•œ๋‹ค. (application.properties ํŒŒ์ผ์„ application.yml๋กœ ๋ฐ”๊พธ๊ณ  ๊ทธ์— ๋งž๋Š” ๋ฌธ๋ฒ•์œผ๋กœ ์ž‘์„ฑํ•ด ์ฃผ๋ฉด ๋œ๋‹ค.)
management:  
	endpoints:    
    	web:      
        	exposure:        
            	include: "*"
  • ํŠน์ • ์—”๋“œํฌ์ธํŠธ๋ฅผ ํ™œ์„ฑํ™” ํ•˜๋ ค๋ฉด management.endpoint.{์—”๋“œํฌ์ธํŠธ๋ช…}.enabled=true ๋ฅผ ์ ์šฉํ•˜๋ฉด ๋œ๋‹ค.
    (endpoints๊ฐ€ ์•„๋‹Œ endpoint ์ž„์„ ์ฃผ์˜)

์ฐธ๊ณ ์ž๋ฃŒ : [Spring] ์Šคํ”„๋ง๋ถ€ํŠธ ์—‘์ธ„์—์ดํ„ฐ(Actuator)๋ž€?


โœ”๏ธ MVC

โœ… MVC๋ž€?
MVC๋Š” Model-View-Controller์˜ ์•ฝ์–ด๋กœ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์„ธ ๋ถ€๋ถ„์œผ๋กœ ๋‚˜๋ˆ„์–ด ์—ญํ• ์„ ๋ถ„๋ฆฌํ•˜๋Š” ๋””์ž์ธ ํŒจํ„ด์ด๋‹ค.

ํŒจํ„ด์—ญํ• 
Model๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ๋ฐ์ดํ„ฐ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ƒํƒœ๋ฅผ ์ž๋ฐ” ๊ฐ์ฒด๋กœ ํ‘œํ˜„ํ•˜๊ณ  ์กฐ์ž‘ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๋‹ด๋‹นํ•œ๋‹ค.
ViewHTML, CSS, JavaScript ๋“ฑ์„ ์‚ฌ์šฉํ•ด ์‚ฌ์šฉ์ž๊ฐ€ ๋ณผ ์ˆ˜ ์žˆ๋Š” ํ™”๋ฉด์„ ๊ตฌ์„ฑํ•˜๋ฉฐ, Thymeleaf์™€ ๊ฐ™์€ ํ…œํ”Œ๋ฆฟ ์—”์ง„์„ ํ†ตํ•ด ๋™์  ํ™”๋ฉด์„ ์ƒ์„ฑํ•œ๋‹ค.
Controllerใ…ค์‚ฌ์šฉ์ž์˜ ์š”์ฒญ์„ ๋ฐ›์•„ Model๊ณผ ์ƒํ˜ธ์ž‘์šฉํ•˜๊ณ , ์ฒ˜๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ View์— ์ „๋‹ฌํ•˜๋Š” ์ค‘๊ฐ„ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

โœ”๏ธ Thymeleaf

โœ… Thymeleaf ๋ž€
ํƒ€์ž„๋ฆฌํ”„(Thymeleaf)๋Š” Java ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ์— ์‚ฌ์šฉ๋˜๋Š” ์„œ๋ฒ„์‚ฌ์ด๋“œ ํ…œํ”Œ๋ฆฟ ์—”์ง„์ž…๋‹ˆ๋‹ค. HTML, XML, JavaScript, CSS ๋“ฑ ๋‹ค์–‘ํ•œ ํ˜•์‹์˜ ํŒŒ์ผ์„ ๋™์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜์—ฌ ๋ทฐ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ํŠนํžˆ, ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์™€ HTML์— ๋™์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ์ž…ํ•˜์—ฌ ์›น ํŽ˜์ด์ง€๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๋ฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.


โœ… service
๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋‹ด๋‹นํ•œ๋‹ค. ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ๋ถ„๋ฆฌ๋˜์–ด ์žฌ์‚ฌ์šฉ ๋ฐ ์œ ์ง€๋ณด์ˆ˜์— ์šฉ์ดํ•˜๋‹ค.
๋ณต์žกํ•œ ์—ฐ์‚ฐ, ์กฐ๊ฑด ๋ถ„๊ธฐ, ์™ธ๋ถ€ api ํ˜ธ์ถœ ๋“ฑ์˜ ๋กœ์ง์„ service ์— ์ž‘์„ฑํ•˜๋ฉด, ์ปจํŠธ๋กค๋Ÿฌ๋Š” ์š”์ฒญ-์‘๋‹ต์ฒ˜๋ฆฌ์—๋งŒ ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ์–ด ์ฝ”๋“œ๊ฐ€ ๋” ๊น”๋”ํ•˜๊ณ  ๋ช…ํ™•ํ•ด์ง„๋‹ค.

โ„น๏ธ ์ •๋ฆฌํ•˜๋ฉด Service ๊ณ„์ธต์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค:

  • ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋‹ด๋‹น
  • ์ปจํŠธ๋กค๋Ÿฌ์™€์˜ ์ฑ…์ž„ ๋ถ„๋ฆฌ
  • ์žฌ์‚ฌ์šฉ์„ฑ๊ณผ ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ ํ™•๋ณด

โœ… controller
์‚ฌ์šฉ์ž์˜ ์š”์ฒญ์„ ๋ฐ›์•„ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ , ๊ฒฐ๊ณผ๋ฅผ ๋ทฐ์— ์ „๋‹ฌํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค. ์ฆ‰, ์‚ฌ์šฉ์ž ์ž…๋ ฅ์„ ํ•ด์„ํ•˜๊ณ  ์ ์ ˆํ•œ ์„œ๋น„์Šค ๋กœ์ง์„ ํ˜ธ์ถœํ•˜๋ฉฐ, ๊ทธ ๊ฒฐ๊ณผ๋ฅผ Thymeleaf ๋ทฐ์— ๋„˜๊ฒจ์ฃผ๋Š” ์ค‘๊ฐ„ ๋‹ค๋ฆฌ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

โ„น๏ธ ์ปจํŠธ๋กค๋Ÿฌ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฑ…์ž„์„ ๊ฐ€์ง„๋‹ค:

  • HTTP ์š”์ฒญ ์ˆ˜์‹  ๋ฐ ์ฒ˜๋ฆฌ
  • ์ž…๋ ฅ๊ฐ’ ๊ฒ€์ฆ ๋ฐ ๋งคํ•‘
  • ์„œ๋น„์Šค ๊ณ„์ธต ํ˜ธ์ถœ
  • ๊ณ„์‚ฐ ๊ฒฐ๊ณผ ๋˜๋Š” ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ๋ชจ๋ธ์— ๋‹ด์•„ ๋ทฐ์— ์ „๋‹ฌ

โœ… Thymeleaf
์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ๊ฐ’์„ ํผ์„ ํ†ตํ•ด ์„œ๋ฒ„๋กœ ์ „์†กํ•˜๊ณ , ๊ฒฐ๊ณผ ๋˜๋Š” ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ๋™์ ์œผ๋กœ ํ™”๋ฉด์— ์ถœ๋ ฅํ•œ๋‹ค. th:action์„ ํ†ตํ•ด ์„œ๋ฒ„ ๊ฒฝ๋กœ๋ฅผ ์ง€์ •ํ•˜๊ณ , th:text, th:if ๋“ฑ์„ ์‚ฌ์šฉํ•ด ์กฐ๊ฑด๋ถ€ ๋ Œ๋”๋ง์„ ๊ตฌํ˜„ํ•œ๋‹ค.


โœ”๏ธ ๊ณ„์‚ฐ๊ธฐ ํ”„๋กœ๊ทธ๋žจ

โœ… ํŒŒ์ผ๊ตฌ์กฐ

๐Ÿ“ฆsrc
 โ”ฃ ๐Ÿ“‚main
 โ”ƒ โ”ฃ ๐Ÿ“‚java
 โ”ƒ โ”ƒ โ”— ๐Ÿ“‚com
 โ”ƒ โ”ƒ โ”ƒ โ”— ๐Ÿ“‚j797
 โ”ƒ โ”ƒ โ”ƒ โ”ƒ โ”— ๐Ÿ’ซcalculator
 โ”ƒ โ”ƒ โ”ƒ โ”ƒ โ”ƒ โ”ฃ โœ…controller
 โ”ƒ โ”ƒ โ”ƒ โ”ƒ โ”ƒ โ”ƒ โ”— ๐Ÿ“œCalculatorController.java
 โ”ƒ โ”ƒ โ”ƒ โ”ƒ โ”ƒ โ”ฃ โœ…service
 โ”ƒ โ”ƒ โ”ƒ โ”ƒ โ”ƒ โ”ƒ โ”— ๐Ÿ“œCalculatorService.java
 โ”ƒ โ”ƒ โ”ƒ โ”ƒ โ”ƒ โ”— ๐Ÿ“œCalculatorApplication.java
 โ”ƒ โ”— ๐Ÿ“‚resources
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚static
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚templates
 โ”ƒ โ”ƒ โ”ƒ โ”— โœ…calculator.html
 โ”ƒ โ”ƒ โ”— ๐Ÿ“œapplication.properties
 โ”— ๐Ÿ“‚test
 โ”ƒ โ”— ๐Ÿ“‚java
 โ”ƒ โ”ƒ โ”— ๐Ÿ“‚com
 โ”ƒ โ”ƒ โ”ƒ โ”— ๐Ÿ“‚j797
 โ”ƒ โ”ƒ โ”ƒ โ”ƒ โ”— ๐Ÿ“‚calculator
 โ”ƒ โ”ƒ โ”ƒ โ”ƒ โ”ƒ โ”— ๐Ÿ“œCalculatorApplicationTests.java

โœ… service
๊ณ„์‚ฐ๊ธฐ ์•ฑ๊ณผ ๊ฐ™์€ ๊ตฌ์กฐ์—์„œ๋Š” ์—ฐ์‚ฐ ๋กœ์ง์„ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ์•„๋‹Œ Service์— ์œ„์ž„ํ•จ์œผ๋กœ์จ, ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ์ด ์‰ฌ์›Œ์ง€๊ณ  ๋กœ์ง ๋ณ€๊ฒฝ ์‹œ ์˜ํ–ฅ ๋ฒ”์œ„๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค.

  • CalculatorService ํด๋ž˜์Šค์— calculate ๋ฉ”์†Œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค. ์ด ๋ฉ”์†Œ๋“œ๋Š” ๋‘ ๊ฐœ์˜ double ๊ฐ’์„ ๋ฐ›์•„ ์‚ฌ์น™์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๊ณ , ์—ฐ์‚ฐ์ž๊ฐ€ ์œ ํšจํ•˜์ง€ ์•Š๊ฑฐ๋‚˜ 0์œผ๋กœ ๋‚˜๋ˆ„๋Š” ๊ฒฝ์šฐ์—๋Š” IllegalArgumentException ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค.
@Service
public class CalculatorService {
    public double calculate(double num1, double num2, String operation) {
        switch (operation) {
            case "add":
                return num1 + num2;
            case "subtract":
                return num1 - num2;
            case "multiply":
                return num1 * num2;
            case "divide":
                if (num2 == 0) {
                    throw new IllegalArgumentException("0์œผ๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.");
                } else {
                    return num1 / num2;
                }
            default:
                throw new IllegalArgumentException("ํ—ˆ์šฉ๋˜์ง€ ์•Š์€ ์—ฐ์‚ฐ์ž์ž…๋‹ˆ๋‹ค." + operation);
        }
    }
}

โœ… controller
์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ์ˆซ์ž์™€ ์—ฐ์‚ฐ์ž๋Š” @RequestParam์„ ํ†ตํ•ด ์ปจํŠธ๋กค๋Ÿฌ๋กœ ์ „๋‹ฌ๋˜๋ฉฐ, ์ปจํŠธ๋กค๋Ÿฌ๋Š” ์ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ CalculatorService์˜ calculate ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. ๊ฒฐ๊ณผ๊ฐ’์ด๋‚˜ ์˜ˆ์™ธ ๋ฉ”์‹œ์ง€๋Š” Model ๊ฐ์ฒด์— ๋‹ด์•„ ํ…œํ”Œ๋ฆฟ์— ์ „๋‹ฌํ•˜์—ฌ ์‚ฌ์šฉ์ž์—๊ฒŒ ํ”ผ๋“œ๋ฐฑ์„ ์ œ๊ณตํ•œ๋‹ค.

  • @PostMapping("/calculate") ๋ฉ”์„œ๋“œ๋Š” ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ์ž…๋ ฅ๋ฐ›์€ ์ˆซ์ž์™€ ์—ฐ์‚ฐ์ž ๊ฐ’์„ @RequestParam์œผ๋กœ ๋ฐ›์•„ CalculatorService์˜ calculate ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. ๊ฒฐ๊ณผ๊ฐ’์€ Model ๊ฐ์ฒด์— ๋„˜๊ฒจ ๋ทฐ๋กœ ์ „๋‹ฌ๋œ๋‹ค.
@Controller
public class CalculatorController{
    private final CalculatorService calculatorService;

    <@Autowired
    public CalculatorController(CalculatorService calculatorService) {
        this.calculatorService = calculatorService;
    }

    @GetMapping("/")
    public String showCalculator() {
        return "calculator"; // ํƒ€์ž„๋ฆฌํ”„ html ๋ฐ˜ํ™˜
    }

    @PostMapping("/calculate")
    public String calculate(
        @RequestParam("num1") double num1,
        @RequestParam("num2") double num2,
        @RequestParam("operation") String operation,
        Model model
    ) {
        try {
            double result = calculatorService.calculate(num1, num2, operation);
            System.out.println("clicked!" + result);
            model.addAttribute("result", result);
        } catch(IllegalArgumentException e) {
            model.addAttribute("errorMessage", e.getMessage());
        }
        return "calculator";
    }
}

โœ… Thymeleaf

๊ณ„์‚ฐ๊ธฐ ์•ฑ์˜ ๋ทฐ๋Š” Thymeleaf ํ…œํ”Œ๋ฆฟ์œผ๋กœ ๊ตฌ์„ฑ๋˜๋ฉฐ, ์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ๊ฐ’์„ ํผ์„ ํ†ตํ•ด ์„œ๋ฒ„๋กœ ์ „์†กํ•˜๊ณ , ๊ฒฐ๊ณผ ๋˜๋Š” ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ๋™์ ์œผ๋กœ ํ™”๋ฉด์— ์ถœ๋ ฅํ•œ๋‹ค. th:action์„ ํ†ตํ•ด ์„œ๋ฒ„ ๊ฒฝ๋กœ๋ฅผ ์ง€์ •ํ•˜๊ณ , th:text, th:if ๋“ฑ์„ ์‚ฌ์šฉํ•ด ์กฐ๊ฑด๋ถ€ ๋ Œ๋”๋ง์„ ๊ตฌํ˜„ํ•œ๋‹ค.

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <title>Simple Calculator</title>
</head>
<body>
<h1>Simple Calculator</h1>
<form th:action="@{/calculate}" method="post">
  <!--id๋Š” label๊ณผ ๋งค์นญ, name์€ controller/@RequestParam๊ณผ ๋งค์นญ-->
  <label for="num1">Number 1:</label>
  <input type="number" step="any" name="num1" id="num1" required />

  <label for="num2">Number 2:</label>
  <input type="number" step="any" name="num2" id="num2" required />

  <label for="operation">Operation:</label>
  <select name="operation" id="operation">
    <!--value๋ฅผ ๋ฐ˜ํ™˜-->
    <option value="add">Add (+)</option>
    <option value="subtract">Subtract (-)</option>
    <option value="multiply">Multiply (ร—)</option>
    <option value="divide">Divide (รท)</option>
  </select>

  <button type="submit">Calculate</button>
</form>

<!--Model์— ๊ฐ’์ด ์žˆ์œผ๋ฉด ๊บผ๋‚ด์˜จ๋‹ค-->
<div th:if="${errorMessage}" class="error">
  <p th:text="${errorMessage}"></p>
</div>

<div th:if="${result}" class="result">
  <h2>Result: <span th:text="${result}"></span></h2>
</div>
</body>
</html>

์ „์ฒด์ฝ”๋“œ

profile
๐Ÿ—‚๏ธ hamstern

0๊ฐœ์˜ ๋Œ“๊ธ€