[๐Ÿ“ฐ ์œ„ํด๋ฆฌํŽ˜์ดํผ] Spring AOP & Controller vs RestController

han91ยท2026๋…„ 2์›” 9์ผ

[์œ„ํด๋ฆฌํŽ˜์ดํผ]

๋ชฉ๋ก ๋ณด๊ธฐ
6/11

๐Ÿ“Œ AOP(Aspect Oriented Programming)

๐Ÿง AOP?

ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ๋Š” ๋ณ„๊ฐœ์ธ ๊ณตํ†ต ๊ธฐ๋Šฅ(ํšก๋‹จ ๊ด€์‹ฌ์‚ฌ)์„ ํ•˜๋‚˜์˜ ๋ชจ๋“ˆ(Aspect)๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํŒจ๋Ÿฌ๋‹ค์ž„
-> ํ•ต์‹ฌ ๊ฐœ๋…์€ "๊ด€์‹ฌ์‚ฌ์˜ ๋ถ„๋ฆฌ(Seperation of Concerns)"

๐Ÿ‘‰ AOP๋ฅผ ์ ์šฉํ•˜๋ฉด ๊ณตํ†ต ๊ธฐ๋Šฅ์€ ํ•˜๋‚˜์˜ ๊ณณ์—์„œ ์ •์˜ํ•˜๊ณ , ํ•„์š”ํ•œ ํ•ต์‹ฌ ๋กœ์ง์— ์ž๋™์œผ๋กœ ๋ผ์›Œ๋„ฃ๋Š” ๋ฐฉ์‹์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Œ

๐Ÿง AOP๊ฐ€ ํ•„์š”ํ•œ ์ด์œ ?

1๏ธโƒฃ ์ค‘๋ณต ์ œ๊ฑฐ + ์œ ์ง€๋ณด์ˆ˜์„ฑ ํ–ฅ์ƒ

๊ฐ์ฒด์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋žจ(OOP)์€ ์—ญํ• ์— ๋”ฐ๋ผ ์ฑ…์ž„์„ ๋ถ„๋ฆฌํ•˜๋Š” ๋ฐ ํƒ์›”ํ•˜์ง€๋งŒ, ์‹ค์ œ ์‹œ์Šคํ…œ์„ ์„ค๊ณ„ํ•˜๋‹ค ๋ณด๋ฉด ๋ชจ๋“  ๋ชจ๋“ˆ์—์„œ ๋ฐ˜๋ณต์ ์œผ๋กœ ๋“ฑ์žฅํ•˜๋Š” ๋กœ์ง๋“ค์ด ์กด์žฌํ•จ

ex)

  • ๋กœ๊น…
  • ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ
  • ๋ณด์•ˆ ๊ฒ€์‚ฌ
  • ์‹คํ–‰ ์‹œ๊ฐ„ ์ธก์ •

๐Ÿ‘‰ ์ด๋Ÿฌํ•œ ์ฝ”๋“œ๋“ค์€ ์—ฌ๋Ÿฌ ํด๋ž˜์Šค์— ๋ถ„์‚ฐ๋˜์–ด ๋ฐ˜๋ณต๋˜๊ธฐ ์‰ฝ๊ณ , OOP๋งŒ์œผ๋กœ๋Š” ์ด ์ค‘๋ณต์„ ํšจ๊ณผ์ ์œผ๋กœ ์ œ๊ฑฐํ•˜๊ธฐ ์–ด๋ ต๊ธฐ ๋•Œ๋ฌธ
-> AOP๋Š” ์ด๋Ÿฐ ๊ณตํ†ต ๋กœ์ง์„ @Aspect๋กœ ๋นผ์„œ ํ•œ ๋ฒˆ์— ์ ์šฉ

2๏ธโƒฃ ๊ด€์‹ฌ์‚ฌ ๋ถ„๋ฆฌ(Seperation of Concerns)

๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง(์˜ˆ: "์ฑ„๋„ ๋ชฉ๋ก ์กฐํšŒ", "๋ฉ”์‹œ์ง€ ์ „์†ก")๊ณผ ์ธํ”„๋ผ/์šด์˜ ๋กœ์ง(์˜ˆ: "๊ถŒํ•œ ์ฒดํฌ", "ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ", "๋ชจ๋‹ˆํ„ฐ๋ง")์„ ๋ถ„๋ฆฌํ•จ

๐Ÿ‘ ๊ด€์‹ฌ์‚ฌ ๋ถ„๋ฆฌ๋ฅผ ํ–ˆ์„ ๋•Œ

  • ๋„๋ฉ”์ธ ์ฝ”๋“œ๊ฐ€ ๋” ์ฝ๊ธฐ ์‰ฌ์›Œ์ง
  • ํ…Œ์ŠคํŠธ๋„ ๋” ๊ฐ„๋‹จํ•ด์ง
  • "์šด์˜ ์ •์ฑ…"์„ ๋ฐ”๊พธ๊ธฐ ์‰ฌ์›Œ์ง

3๏ธโƒฃ ์ผ๊ด€์„ฑ(์ •์ฑ… ๊ฐ•์ œ)

"๋ชจ๋“  write ์š”์ฒญ์€ ํŠธ๋žœ์žญ์…˜์ด ์žˆ์–ด์•ผ ํ•œ๋‹ค", "๋ชจ๋“  API๋Š” ์š”์ฒญ ID๋ฅผ ๋‚จ๊ฒจ์•ผ ํ•œ๋‹ค" ๊ฐ™์€ ๊ทœ์น™์„ ๊ฐœ๋ฐœ์ž ์‹ค์ˆ˜๋กœ ๋น ๋œจ๋ฆฌ์ง€ ์•Š๋„๋ก ๊ฐ•์ œ๋กœ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Œ

4๏ธโƒฃ Spring์˜ ํ”„๋ก์‹œ ๊ธฐ๋ฐ˜ ๊ธฐ๋Šฅ๊ณผ ๊ถํ•ฉ์ด ์ข‹์Œ

Spring์˜ @Transactional ์ž์ฒด๊ฐ€ ๋Œ€ํ‘œ์ ์ธ AOP ํ™œ์šฉ์ž„
-> AOP๋Š” ์ด๋ฏธ Spring์—์„œ "๊ฒ€์ฆ๋œ ๋ฐฉ์‹"์ด๊ณ , ์‹ค์ œ ์‹ค๋ฌด์—์„œ๋„ ์ž์ฃผ ์“ฐ์ž„

๐Ÿ’ ์‹ค์ œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ ์‚ฌ๋ก€

์‚ฌ๋ก€ 1) โ€œAPI ์š”์ฒญ ๋‹จ์œ„ ๋กœ๊น… + ์‹คํ–‰์‹œ๊ฐ„ + ์š”์ฒญ ์ถ”์ IDโ€

์ƒํ™ฉ

  • ์šด์˜ ์ค‘ ์žฅ์• ๊ฐ€ ๋‚˜๋ฉด โ€œ์–ด๋–ค ์š”์ฒญ์ด ์–ด๋–ค ์„œ๋น„์Šค ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ–ˆ๊ณ , ์–ผ๋งˆ๋‚˜ ๊ฑธ๋ ธ๋Š”์ง€โ€๊ฐ€ ํ•„์š”ํ•จ
  • ์ปจํŠธ๋กค๋Ÿฌ/์„œ๋น„์Šค๋งˆ๋‹ค ๋กœ๊ทธ๋ฅผ ๋„ฃ์œผ๋ฉด ์ค‘๋ณต์ด ํญ๋ฐœ

AOP ์ ์šฉ

@Around๋กœ Controller ๋˜๋Š” Service ํŒจํ‚ค์ง€๋ฅผ ํฌ์ธํŠธ์ปท์œผ๋กœ ์žก์•„

  • ๋ฉ”์„œ๋“œ ์‹œ์ž‘/์ข…๋ฃŒ ๋กœ๊ทธ
  • ์‹คํ–‰ ์‹œ๊ฐ„(ms)
  • ์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ ์—๋Ÿฌ ๋กœ๊ทธ
  • (์„ ํƒ) MDC์— requestId ๋„ฃ์–ด์„œ ๋กœ๊ทธ๋ฅผ ๋ฌถ์Œ

ํšจ๊ณผ

  • ๋ชจ๋“  API๊ฐ€ ๋™์ผํ•œ ํฌ๋งท์œผ๋กœ ์ถ”์  ๊ฐ€๋Šฅ
  • ์„ฑ๋Šฅ ๋ณ‘๋ชฉ ์ง€์  ์ฐพ๊ธฐ๊ฐ€ ์‰ฌ์›€
  • ๊ฐœ๋ฐœ์ž๊ฐ€ ๋กœ๊ทธ๋ฅผ โ€œ๊นŒ๋จน๋Š”โ€ ๋ฌธ์ œ ์ œ๊ฑฐ

์‚ฌ๋ก€ 2) โ€œ๊ฐ์‚ฌ ๋กœ๊ทธ(Audit) ์ž๋™ ๊ธฐ๋กโ€

์ƒํ™ฉ

  • ๊ธˆ์œต/์‚ฌ๋‚ด์‹œ์Šคํ…œ์—์„œ โ€œ๋ˆ„๊ฐ€ ์–ด๋–ค ๋ฆฌ์†Œ์Šค๋ฅผ ์ƒ์„ฑ/์ˆ˜์ •/์‚ญ์ œํ–ˆ๋Š”์ง€โ€๋Š” ํ•„์ˆ˜์ธ ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Œ
  • ์„œ๋น„์Šค๋งˆ๋‹ค audit ํ…Œ์ด๋ธ” insert ์ฝ”๋“œ๋ฅผ ๋„ฃ๊ธฐ ์‹ซ์Œ

AOP ์ ์šฉ

@Auditable(action="UPDATE_USER") ๊ฐ™์€ ์• ๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜์œผ๋กœ
์‹คํ–‰ ์ „ํ›„์— ๋ณ€๊ฒฝ ๋Œ€์ƒ ID, ์‚ฌ์šฉ์ž ID, ๊ฒฐ๊ณผ(์„ฑ๊ณต/์‹คํŒจ), timestamp ๊ธฐ๋ก

ํšจ๊ณผ

  • ๊ฐ์‚ฌ ์ •์ฑ… ๋ณ€๊ฒฝ์ด ์‰ฌ์›€(ํ•„๋“œ ์ถ”๊ฐ€/๋งˆ์Šคํ‚น/์ €์žฅ ์œ„์น˜ ๋ณ€๊ฒฝ)
  • ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ๊นจ๋—ํ•ด์ง

๐Ÿ“Œ Controller vs RestController

๐Ÿ’ป Spring MVC์˜ ๊ธฐ๋ณธ ์š”์ฒญ ์ฒ˜๋ฆฌ ํ๋ฆ„

Client
  โ†“
DispatcherServlet (Front Controller)
  โ†“
HandlerMapping (์–ด๋–ค ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ์ฒ˜๋ฆฌํ• ์ง€ ์ฐพ์Œ)
  โ†“
Controller Method ์‹คํ–‰
  โ†“
HandlerAdapter
  โ†“
Return ๊ฐ’ ์ฒ˜๋ฆฌ
  โ†“
ViewResolver or HttpMessageConverter
  โ†“
Response

๐Ÿ‘‰ ์—ฌ๊ธฐ์„œ ์ค‘์š”ํ•œ ๋ถ„๊ธฐ์ ์ด ๋ฐ”๋กœ โ€œ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ์˜ ๋ฐ˜ํ™˜๊ฐ’์„ ์–ด๋–ป๊ฒŒ ํ•ด์„ํ•˜๋А๋ƒโ€์ด๊ณ , ๊ทธ ์ฐจ์ด๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒŒ @Controller vs @RestController

๐Ÿ’ @Controller ๊ธฐ๋ฐ˜ ์š”์ฒญ ์ฒ˜๋ฆฌ ๊ณผ์ •

@Controller๋Š” ์ „ํ†ต์ ์ธ MVC ํŒจํ„ด์—์„œ View(HTML ํ™”๋ฉด)๋ฅผ ๋ฐ˜ํ™˜ํ•  ๋•Œ ์ฃผ๋กœ ์‚ฌ์šฉ

๐Ÿ“Œ ํ๋ฆ„

  • ํด๋ผ์ด์–ธํŠธ ์š”์ฒญ โ†’ DispatcherServlet
  • HandlerMapping์ด @Controller ํด๋ž˜์Šค์˜ ๋ฉ”์„œ๋“œ ๋งคํ•‘ ํƒ์ƒ‰
  • ๋ฉ”์„œ๋“œ ์‹คํ–‰
  • ๋ฐ˜ํ™˜ ํƒ€์ž…์ด String โ†’ View ์ด๋ฆ„์œผ๋กœ ํ•ด์„
  • ViewResolver๊ฐ€ JSP, Thymeleaf ๊ฐ™์€ ํ…œํ”Œ๋ฆฟ ์ฐพ์Œ
  • View ๋ Œ๋”๋ง ํ›„ HTML ์‘๋‹ต

๐Ÿ” ์˜ˆ์‹œ ์ฝ”๋“œ

@Controller
@RequestMapping("/users")
public class UserController {

    @GetMapping("/{id}")
    public String getUser(@PathVariable Long id, Model model) {
        model.addAttribute("user", userService.findById(id));
        return "user-detail"; // View ์ด๋ฆ„
    }
}

ํŠน์ง•

  • ๋ฐ˜ํ™˜๊ฐ’ โ†’ ๋ทฐ ์ด๋ฆ„
  • Model์— ๋ฐ์ดํ„ฐ ๋‹ด์•„์„œ ํ™”๋ฉด ๋ Œ๋”๋ง
  • JSP / Thymeleaf ๊ฐ™์€ ํ…œํ”Œ๋ฆฟ ์—”์ง„๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉ
  • ์›น ํŽ˜์ด์ง€์šฉ ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง(SSR)์— ์ ํ•ฉ

๐Ÿ’ @RestController ๊ธฐ๋ฐ˜ ์š”์ฒญ ์ฒ˜๋ฆฌ ๊ณผ์ •

@RestController๋Š” REST API ์ „์šฉ ์ปจํŠธ๋กค๋Ÿฌ
์‚ฌ์‹ค ๋‚ด๋ถ€์ ์œผ๋กœ๋Š”:
@RestController = @Controller + @ResponseBody

๐Ÿ‘‰ ๋ฐ˜ํ™˜๊ฐ’์„ View๋กœ ํ•ด์„ํ•˜์ง€ ์•Š๊ณ  HTTP Response Body์— ๊ทธ๋Œ€๋กœ ์จ์ค๋‹ˆ๋‹ค.

๐Ÿ“Œ ํ๋ฆ„

  • ์š”์ฒญ โ†’ DispatcherServlet
  • HandlerMapping์ด ๋ฉ”์„œ๋“œ ์ฐพ์Œ
  • ๋ฉ”์„œ๋“œ ์‹คํ–‰
  • ๋ฐ˜ํ™˜ ๊ฐ์ฒด๋ฅผ HttpMessageConverter๊ฐ€ JSON/XML๋กœ ๋ณ€ํ™˜
  • Response Body๋กœ ์ „์†ก

๐Ÿ” ์˜ˆ์‹œ์ฝ”๋“œ

@RestController
@RequestMapping("/users")
public class UserApiController {

    @GetMapping("/{id}")
    public UserResponse getUser(@PathVariable Long id) {
        return userService.findUser(id);
    }
}

๐Ÿ‘‰ ๋ฐ˜ํ™˜ ๊ฐ์ฒด โ†’ Jackson ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ JSON์œผ๋กœ ๋ณ€ํ™˜:

{
  "id": 1,
  "name": "kim"
}

ํŠน์ง•

  • ๋ฐ˜ํ™˜๊ฐ’ = API ์‘๋‹ต ๋ฐ์ดํ„ฐ
  • ViewResolver ์•ˆ ํƒ
  • JSON ๊ธฐ๋ฐ˜ REST API์— ํŠนํ™”
  • ํ”„๋ก ํŠธ์—”๋“œ(React/Vue/์•ฑ)์™€ ํ†ต์‹ ํ•  ๋•Œ ์ฃผ๋ ฅ
profile
์ฒœ๋ฐฉ์ง€์ถ•์–ด๋ฆฌ๋‘ฅ์ ˆ๋น™๊ธ€๋น™๊ธ€๋Œ์•„๊ฐ€๋Š”๊ฐœ๋ฐœ์ž

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