Framework

Spring

Bean Scope

Bean์˜ ์‚ฌ์šฉ ๋ฒ”์œ„๋ฅผ ๋งํ•˜๋Š” Bean Scope์˜ ์ข…๋ฅ˜์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์ž

Bean์€ ์Šคํ”„๋ง์—์„œ ์‚ฌ์šฉํ•˜๋Š” POJO ๊ธฐ๋ฐ˜ ๊ฐ์ฒด๋‹ค.

์ƒํ™ฉ๊ณผ ํ•„์š”์— ๋”ฐ๋ผ Bean์„ ์‚ฌ์šฉํ•  ๋•Œ ํ•˜๋‚˜๋งŒ ๋งŒ๋“ค์–ด์•ผ ํ•  ์ˆ˜๋„ ์žˆ๊ณ , ์—ฌ๋Ÿฌ๊ฐœ๊ฐ€ ํ•„์š”ํ•  ๋•Œ๋„ ์žˆ๊ณ , ์–ด๋–ค ํ•œ ์‹œ์ ์—์„œ๋งŒ ์‚ฌ์šฉํ•ด์•ผํ•  ๋•Œ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ๋‹ค.

์ด๋ฅผ ์œ„ํ•ด Scope๋ฅผ ์„ค์ •ํ•ด์„œ Bean์˜ ์‚ฌ์šฉ ๋ฒ”์œ„๋ฅผ ๊ฐœ๋ฐœ์ž๊ฐ€ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

์šฐ์„  ๋”ฐ๋กœ ์„ค์ •์„ ํ•ด์ฃผ์ง€ ์•Š์œผ๋ฉด, Spring์—์„œ Bean์€ Singleton์œผ๋กœ ์ƒ์„ฑ๋œ๋‹ค. ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์ฒ˜๋Ÿผ ํŠน์ • ํƒ€์ž…์˜ Bean์„ ๋”ฑ ํ•˜๋‚˜๋งŒ ๋งŒ๋“ค๊ณ  ๋ชจ๋‘ ๊ณต์œ ํ•ด์„œ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•จ์ด๋‹ค. ๋ณดํ†ต์€ Bean์„ ์ด๋ ‡๊ฒŒ ํ•˜๋‚˜๋งŒ ๋งŒ๋“ค์–ด ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋Œ€๋ถ€๋ถ„์ด์ง€๋งŒ, ์š”๊ตฌ์‚ฌํ•ญ์ด๋‚˜ ๊ตฌํ˜„์— ๋”ฐ๋ผ ์•„๋‹ ์ˆ˜๋„ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

๋”ฐ๋ผ์„œ Bean Scope๋Š” ์‹ฑ๊ธ€ํ†ค ๋ง๊ณ ๋„ ์—ฌ๋Ÿฌ๊ฐ€์ง€๋ฅผ ์ง€์›ํ•ด์ค€๋‹ค.

โœจ Scope ์ข…๋ฅ˜

  • singleton

    ํ•ด๋‹น Bean์— ๋Œ€ํ•ด IoC ์ปจํ…Œ์ด๋„ˆ์—์„œ ๋‹จ ํ•˜๋‚˜์˜ ๊ฐ์ฒด๋กœ๋งŒ ์กด์žฌํ•œ๋‹ค.

  • prototype

    ํ•ด๋‹น Bean์— ๋Œ€ํ•ด ๋‹ค์ˆ˜์˜ ๊ฐ์ฒด๊ฐ€ ์กด์žฌํ•  ์ˆ˜ ์žˆ๋‹ค.

  • request

    ํ•ด๋‹น Bean์— ๋Œ€ํ•ด ํ•˜๋‚˜์˜ HTTP Request์˜ ๋ผ์ดํ”„์‚ฌ์ดํด์—์„œ ๋‹จ ํ•˜๋‚˜์˜ ๊ฐ์ฒด๋กœ๋งŒ ์กด์žฌํ•œ๋‹ค.

  • session

    ํ•ด๋‹น Bean์— ๋Œ€ํ•ด ํ•˜๋‚˜์˜ HTTP Session์˜ ๋ผ์ดํ”„์‚ฌ์ดํด์—์„œ ๋‹จ ํ•˜๋‚˜์˜ ๊ฐ์ฒด๋กœ๋งŒ ์กด์žฌํ•œ๋‹ค.

  • global session

    ํ•ด๋‹น Bean์— ๋Œ€ํ•ด ํ•˜๋‚˜์˜ Global HTTP Session์˜ ๋ผ์ดํ”„์‚ฌ์ดํด์—์„œ ๋‹จ ํ•˜๋‚˜์˜ ๊ฐ์ฒด๋กœ๋งŒ ์กด์žฌํ•œ๋‹ค.

request, session, global session์€ MVC ์›น ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋งŒ ์‚ฌ์šฉํ•จ

Scope๋“ค์€ Bean์œผ๋กœ ๋“ฑ๋กํ•˜๋Š” ํด๋ž˜์Šค์— ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ์„ค์ •ํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค.

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
 
@Scope("prototype")
@Component
public class UserController {
}

MVC Framework

์Šคํ”„๋ง MVC ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ๋™์ž‘ํ•˜๋Š” ์›๋ฆฌ๋ฅผ ์ดํ•ดํ•˜๊ณ  ์žˆ์–ด์•ผ ํ•œ๋‹ค

ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„œ๋ฒ„์—๊ฒŒ url์„ ํ†ตํ•ด ์š”์ฒญํ•  ๋•Œ ์ผ์–ด๋‚˜๋Š” ์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ์˜ ๋™์ž‘์„ ๊ทธ๋ฆผ์œผ๋กœ ํ‘œํ˜„ํ•œ ๊ฒƒ์ด๋‹ค.

MVC ์ง„ํ–‰ ๊ณผ์ •

  • ํด๋ผ์ด์–ธํŠธ๊ฐ€ url์„ ์š”์ฒญํ•˜๋ฉด, ์›น ๋ธŒ๋ผ์šฐ์ €์—์„œ ์Šคํ”„๋ง์œผ๋กœ request๊ฐ€ ๋ณด๋‚ด์ง„๋‹ค.

  • Dispatcher Servlet์ด request๋ฅผ ๋ฐ›์œผ๋ฉด, Handler Mapping์„ ํ†ตํ•ด ํ•ด๋‹น url์„ ๋‹ด๋‹นํ•˜๋Š” Controller๋ฅผ ํƒ์ƒ‰ ํ›„ ์ฐพ์•„๋‚ธ๋‹ค.

  • ์ฐพ์•„๋‚ธ Controller๋กœ request๋ฅผ ๋ณด๋‚ด์ฃผ๊ณ , ๋ณด๋‚ด์ฃผ๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ Model์„ ๊ตฌ์„ฑํ•œ๋‹ค.

  • Model์—์„œ๋Š” ํŽ˜์ด์ง€ ์ฒ˜๋ฆฌ์— ํ•„์š”ํ•œ ์ •๋ณด๋“ค์„ Database์— ์ ‘๊ทผํ•˜์—ฌ ์ฟผ๋ฆฌ๋ฌธ์„ ํ†ตํ•ด ๊ฐ€์ ธ์˜จ๋‹ค.

  • ๋ฐ์ดํ„ฐ๋ฅผ ํ†ตํ•ด ์–ป์€ Model ์ •๋ณด๋ฅผ Controller์—๊ฒŒ response ํ•ด์ฃผ๋ฉด, Controller๋Š” ์ด๋ฅผ ๋ฐ›์•„ Model์„ ์™„์„ฑ์‹œ์ผœ Dispatcher Servlet์—๊ฒŒ ์ „๋‹ฌํ•ด์ค€๋‹ค.

  • Dispatcher Servlet์€ View Resolver๋ฅผ ํ†ตํ•ด request์— ํ•ด๋‹นํ•˜๋Š” view ํŒŒ์ผ์„ ํƒ์ƒ‰ ํ›„ ๋ฐ›์•„๋‚ธ๋‹ค.

  • ๋ฐ›์•„๋‚ธ View ํŽ˜์ด์ง€ ํŒŒ์ผ์— Model์„ ๋ณด๋‚ธ ํ›„ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋ณด๋‚ผ ํŽ˜์ด์ง€๋ฅผ ์™„์„ฑ์‹œ์ผœ ๋ฐ›์•„๋‚ธ๋‹ค.

  • ์™„์„ฑ๋œ View ํŒŒ์ผ์„ ํด๋ผ์ด์–ธํŠธ์— responseํ•˜์—ฌ ํ™”๋ฉด์— ์ถœ๋ ฅํ•œ๋‹ค.

๊ตฌ์„ฑ ์š”์†Œ


โœจ Dispatcher Servlet

๋ชจ๋“  request๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ์ค‘์‹ฌ ์ปจํŠธ๋กค๋Ÿฌ๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค. ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ์—์„œ http ํ”„๋กœํ† ์ฝœ์„ ํ†ตํ•ด ๋“ค์–ด์˜ค๋Š” ๋ชจ๋“  request์— ๋Œ€ํ•ด ์ œ์ผ ์•ž๋‹จ์—์„œ ์ค‘์•™์ง‘์ค‘์‹์œผ๋กœ ์ฒ˜๋ฆฌํ•ด์ฃผ๋Š” ํ•ต์‹ฌ์ ์ธ ์—ญํ• ์„ ํ•œ๋‹ค.

๊ธฐ์กด์—๋Š” web.xml์— ๋ชจ๋‘ ๋“ฑ๋กํ•ด์ค˜์•ผ ํ–ˆ์ง€๋งŒ, ๋””์ŠคํŒจ์ฒ˜ ์„œ๋ธ”๋ฆฟ์ด ๋ชจ๋“  request๋ฅผ ํ•ธ๋“ค๋งํ•˜๋ฉด์„œ ์ž‘์—…์„ ํŽธ๋ฆฌํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค.

โœจ Handler Mapping

ํด๋ผ์ด์–ธํŠธ์˜ request url์„ ์–ด๋–ค ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•  ์ง€ ์ฐพ์•„์„œ Dispatcher Servlet์—๊ฒŒ ์ „๋‹ฌํ•ด์ฃผ๋Š” ์—ญํ• ์„ ๋‹ด๋‹นํ•œ๋‹ค.

์ปจํŠธ๋กค๋Ÿฌ ์ƒ์—์„œ url์„ ๋งคํ•‘์‹œํ‚ค๊ธฐ ์œ„ํ•ด @RequestMapping์„ ์‚ฌ์šฉํ•˜๋Š”๋ฐ, ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์ด๋ฅผ ์ฐพ์•„์ฃผ๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

โœจ Controller

์‹ค์งˆ์ ์ธ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ณณ์ด๋‹ค. Dispatcher Servlet์ด ํ”„๋ก ํŠธ ์ปจํŠธ๋กค๋Ÿฌ๋ผ๋ฉด, ์ด ๊ณณ์€ ๋ฐฑ์—”๋“œ ์ปจํŠธ๋กค๋Ÿฌ๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

๋ชจ๋ธ์˜ ์ฒ˜๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ๋‹ด์•„ Dispatcher Servlet์—๊ฒŒ ๋ฐ˜ํ™˜ํ•ด์ค€๋‹ค.

โœจ View Resolver

์ปจํŠธ๋กค๋Ÿฌ์˜ ์ฒ˜๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ๋งŒ๋“ค view๋ฅผ ๊ฒฐ์ •ํ•ด์ฃผ๋Š” ์—ญํ• ์„ ๋‹ด๋‹นํ•œ๋‹ค. ๋‹ค์–‘ํ•œ ์ข…๋ฅ˜๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ƒํ™ฉ์— ๋งž๊ฒŒ ํ™œ์šฉํ•˜๋ฉด ๋œ๋‹ค.

SpringApplication

์Šคํ”„๋ง ๋ถ€ํŠธ๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ ์‹คํ–‰ํ•  ๋•Œ Application ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ ๋‹ค.

ํด๋ž˜์Šค๋ช…์€ ๊ฐœ๋ฐœ์ž๊ฐ€ ํ”„๋กœ์ ํŠธ์— ๋งž๊ฒŒ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ํฐ ํ‹€์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

@SpringBootApplication
public class Application {

	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}

}

@SpringBootApplication ์–ด๋…ธํ…Œ์ด์…˜์„ ํ†ตํ•ด ์Šคํ”„๋ง Bean์„ ์ฝ์–ด์™€ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•ด์ค€๋‹ค.

์ด ์–ด๋…ธํ…Œ์ด์…˜์ด ์žˆ๋Š” ํŒŒ์ผ ์œ„์น˜๋ถ€ํ„ฐ ์„ค์ •๋“ค์„ ์ฝ์–ด๊ฐ€๋ฏ€๋กœ, ๋ฐ˜๋“œ์‹œ ํ”„๋กœ์ ํŠธ์˜ ์ตœ์ƒ๋‹จ์— ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค.

SpringApplication.run()์œผ๋กœ ํ•ด๋‹น ํด๋ž˜์Šค๋ฅผ runํ•˜๋ฉด, ๋‚ด์žฅ WAS๋ฅผ ์‹คํ–‰ํ•œ๋‹ค. ๋‚ด์žฅ WAS์˜ ์žฅ์ ์œผ๋กœ๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋”ฐ๋กœ ํ†ฐ์บฃ๊ณผ ๊ฐ™์€ ์™ธ๋ถ€ WAS๋ฅผ ์„ค์น˜ ํ›„ ์„ค์ •ํ•ด๋‘์ง€ ์•Š์•„๋„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋˜ํ•œ, ์™ธ์žฅ WAS๋ฅผ ์‚ฌ์šฉํ•  ์‹œ ์ด ํ”„๋กœ์ ํŠธ๋ฅผ ์‹คํ–‰์‹œํ‚ค๊ธฐ ์œ„ํ•œ ์„œ๋ฒ„์—์„œ ๋ชจ๋‘ ์™ธ์žฅ WAS์˜ ์ข…๋ฅ˜์™€ ๋ฒ„์ „, ์„ค์ •์„ ์ผ์น˜์‹œ์ผœ์•ผ๋งŒ ํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ๋‚ด์žฅ WAS๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋Ÿฐ ์‹ ๊ฒฝ์€ ์“ฐ์ง€ ์•Š์•„๋„ ๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋งค์šฐ ํŽธ๋ฆฌํ•˜๋‹ค.

์‹ค์ œ๋กœ ๋งŽ์€ ํšŒ์‚ฌ๋“ค์ด ์ด๋Ÿฐ ์žฅ์ ์„ ์‚ด๋ ค ๋‚ด์žฅ WAS๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๊ณ , ์ „ํ™˜ํ•˜๊ณ  ์žˆ๋‹ค.

Test Code

โœจ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•˜๋Š” ์ด์œ 

  • ๊ฐœ๋ฐœ๋‹จ๊ณ„ ์ดˆ๊ธฐ์— ๋ฌธ์ œ๋ฅผ ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ์Œ

  • ๋‚˜์ค‘์— ์ฝ”๋“œ๋ฅผ ๋ฆฌํŒฉํ† ๋งํ•˜๊ฑฐ๋‚˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์—…๊ทธ๋ ˆ์ด๋“œ ์‹œ ๊ธฐ์กด ๊ธฐ๋Šฅ์ด ์ž˜ ์ž‘๋™ํ•˜๋Š” ์ง€ ํ™•์ธ ๊ฐ€๋Šฅํ•จ

  • ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ๋ถˆํ™•์‹ค์„ฑ ๊ฐ์†Œ

๊ฐœ๋ฐœ ์ฝ”๋“œ ์ด์™ธ์— ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ์ผ์€ ๊ฐœ๋ฐœ ์‹œ๊ฐ„์ด ๋Š˜์–ด๋‚  ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ๋‚ด ์ฝ”๋“œ์— ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋Š” ์ง€ ๊ฒ€์ฆํ•  ๋•Œ, ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์ง€ ์•Š๊ณ  ์ง„ํ–‰ํ•œ๋‹ค๋ฉด ๋” ์‹œ๊ฐ„ ์†Œ๋ชจ๊ฐ€ ํด ๊ฒƒ์ด๋‹ค.

1. ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•œ ๋’ค ํ”„๋กœ๊ทธ๋žจ์„ ์‹คํ–‰ํ•˜์—ฌ ์„œ๋ฒ„๋ฅผ ํ‚จ๋‹ค. 2. API ํ”„๋กœ๊ทธ๋žจ(ex. Postman)์œผ๋กœ HTTP ์š”์ฒญ ํ›„ ๊ฒฐ๊ณผ๋ฅผ Print๋กœ ์ฐ์–ด์„œ ํ™•์ธํ•œ๋‹ค. 3. ๊ฒฐ๊ณผ๊ฐ€ ์˜ˆ์ƒ๊ณผ ๋‹ค๋ฅด๋ฉด, ๋‹ค์‹œ ํ”„๋กœ๊ทธ๋žจ์„ ์ข…๋ฃŒํ•œ ๋’ค ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜๊ณ  ๋ฐ˜๋ณตํ•œ๋‹ค.

์œ„์™€ ๊ฐ™์€ ๋ฐฉ์‹์ด ์–ผ๋งˆ๋‚˜ ๋ฐ˜๋ณต๋  ์ง€ ๋ชจ๋ฅธ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ํ•˜๋‚˜์˜ ๊ธฐ๋Šฅ๋งˆ๋‹ค ์ €๋ ‡๊ฒŒ ํ…Œ์ŠคํŠธ๋ฅผ ํ•˜๋ฉด ์„œ๋ฒ„๋ฅผ ํ‚ค๊ณ  ๋„๋Š” ์ž‘์—… ๋˜ํ•œ ๋„ˆ๋ฌด ๋น„ํšจ์œจ์ ์ด๋‹ค.

์ด ๋ฐ–์—๋„ Print๋กœ ๋ˆˆ์œผ๋กœ ๊ฒ€์ฆํ•˜๋Š” ๊ฒƒ๋„ ์–ด๋А์ •๋„ ์„ ์—์„œ ํ•œ๊ณ„๊ฐ€ ์žˆ๋‹ค. ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋Š” ์ž๋™์œผ๋กœ ๊ฒ€์ฆ์„ ํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ์„ฑ๊ณตํ•œ๋‹ค๋ฉด ์ˆ˜๋™์œผ๋กœ ๊ฒ€์ฆํ•  ํ•„์š” ์ž์ฒด๊ฐ€ ์—†์–ด์ง„๋‹ค.

์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์ด ์ถ”๊ฐ€๋˜์—ˆ์„ ๋•Œ๋„ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ๋งŒ์•ฝ ๊ธฐ์กด์˜ ์ฝ”๋“œ์— ์˜ํ–ฅ์ด ๊ฐ”๋‹ค๋ฉด ์–ด๋–ค ๋ถ€๋ถ„์„ ์ˆ˜์ •ํ•ด์•ผ ํ•˜๋Š” ์ง€ ์•Œ ์ˆ˜ ์žˆ๋Š” ์žฅ์ ๋„ ์กด์žฌํ•œ๋‹ค.

๋”ฐ๋ผ์„œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋Š” ๊ฐœ๋ฐœํ•˜๋Š” ๋ฐ ์žˆ์–ด์„œ ํ•„์ˆ˜์ ์ธ ๋ถ€๋ถ„์ด๋ฉฐ ๋ฐ˜๋“œ์‹œ ํ™œ์šฉํ•ด์•ผ ํ•œ๋‹ค.

โœจ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์˜ˆ์ œ

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;

@RunWith(SpringRunner.class)
@WebMvcTest(controllers = HomeController.class)
public class HomeControllerTest {

    @Autowired
    private MockMvc mvc;

    @Test
    public void home_return() throws Exception {
        //when
        String home = "home";

        //then
        mvc.perform(get("/home"))
                .andExpect(status().isOk())
                .andExpect(content().string(home));
    }
}
  1. @RunWith(SpringRunner.class)

ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•  ๋•Œ JUnit์— ๋‚ด์žฅ๋œ ์‹คํ–‰์ž ์™ธ์— ๋‹ค๋ฅธ ์‹คํ–‰์ž๋ฅผ ์‹คํ–‰์‹œํ‚จ๋‹ค.

์Šคํ”„๋ง ๋ถ€ํŠธ ํ…Œ์ŠคํŠธ์™€ JUnit ์‚ฌ์ด์˜ ์—ฐ๊ฒฐ์ž ์—ญํ• ์„ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.

  1. @WebMvcTest

์ปจํŠธ๋กค๋Ÿฌ๋งŒ ์‚ฌ์šฉํ•  ๋•Œ ์„ ์–ธ์ด ๊ฐ€๋Šฅํ•˜๋ฉฐ, Spring MVC์— ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๋Š” ์–ด๋…ธํ…Œ์ด์…˜์ด๋‹ค.

  1. @Autowired

์Šคํ”„๋ง์ด ๊ด€๋ฆฌํ•˜๋Š” Bean์„ ์ฃผ์ž…์‹œ์ผœ์ค€๋‹ค.

  1. MockMvc

์›น API๋ฅผ ํ…Œ์ŠคํŠธํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋ฉฐ, ์ด๋ฅผ ํ†ตํ•ด HTTP GET, POST, DELETE ๋“ฑ์— ๋Œ€ํ•œ API ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

  1. mvc.perform(get("/home"))

/home ์ฃผ์†Œ๋กœ HTTP GET ์š”์ฒญ์„ ํ•œ ์ƒํ™ฉ์ด๋‹ค.

  1. .andExpect(status().isOk())

๊ฒฐ๊ณผ๋ฅผ ๊ฒ€์ฆํ•˜๋Š” andExpect๋กœ, ์—ฌ๋Ÿฌ๊ฐœ๋ฅผ ๋ถ™์—ฌ์„œ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. status()๋Š” HTTP Header๋ฅผ ๊ฒ€์ฆํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๊ฒฐ๊ณผ์— ๋Œ€ํ•œ HTTP Status ์ƒํƒœ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. ํ˜„์žฌ isOK()๋Š” 200 ์ฝ”๋“œ๊ฐ€ ๋งž๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ์žˆ๋‹ค.

ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค๋ฉด์„œ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ๋“ค์„ ๊ตฌํ˜„ํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, ์ด์ฒ˜๋Ÿผ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋กœ ๊ฒฌ๊ณ ํ•œ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ๊ธฐ๋Šฅ๋ณ„ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋Š” ์Šต๊ด€์„ ๊ธธ๋Ÿฌ์•ผ ํ•œ๋‹ค.

JPA

Java Persistence API

๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ SQL์„ ์ž‘์„ฑํ•˜์ง€ ์•Š๊ณ , JPA API๋ฅผ ํ™œ์šฉํ•ด DB๋ฅผ ์ €์žฅํ•˜๊ณ  ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

JPA๋Š” ์˜ค๋Š˜๋‚  ์Šคํ”„๋ง์—์„œ ๋งŽ์ด ํ™œ์šฉ๋˜๊ณ  ์žˆ์ง€๋งŒ, ์Šคํ”„๋ง์ด ์ œ๊ณตํ•˜๋Š” API๊ฐ€ ์•„๋‹Œ ์ž๋ฐ”๊ฐ€ ์ œ๊ณตํ•˜๋Š” API๋‹ค.

์ž๋ฐ” ORM ๊ธฐ์ˆ ์— ๋Œ€ํ•œ ํ‘œ์ค€ ๋ช…์„ธ๋กœ, ์ž๋ฐ” ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์„ ์ •์˜ํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋‹ค.

โœจ ORM(Object Relational Mapping)

ORM ํ”„๋ ˆ์ž„์›Œํฌ๋Š” ์ž๋ฐ” ๊ฐ์ฒด์™€ ๊ด€๊ณ„ํ˜• DB๋ฅผ ๋งคํ•‘ํ•œ๋‹ค. ์ฆ‰, ๊ฐ์ฒด๊ฐ€ DB ํ…Œ์ด๋ธ”์ด ๋˜๋„๋ก ๋งŒ๋“ค์–ด์ฃผ๋Š” ๊ฒƒ์ด๋‹ค. ORM์„ ์‚ฌ์šฉํ•˜๋ฉด, SQL์„ ์ž‘์„ฑํ•˜์ง€ ์•Š์•„๋„ ์ง๊ด€์ ์ธ ๋ฉ”์†Œ๋“œ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค. ( ๊ฐœ๋ฐœ์ž์—๊ฒŒ ์ƒ์‚ฐ์„ฑ์„ ํ–ฅ์ƒ์‹œ์ผœ์ค„ ์ˆ˜ ์žˆ์Œ )

์ข…๋ฅ˜๋กœ๋Š” Hibernate, EclipseLink, DataNucleus ๋“ฑ์ด ์žˆ๋‹ค.

์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ๋Š” spring-boot-starter-data-jpa๋กœ ํŒจํ‚ค์ง€๋ฅผ ๊ฐ€์ ธ์™€ ์‚ฌ์šฉํ•˜๋ฉฐ, ์ด๋Š” Hibernate ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ํ™œ์šฉํ•œ๋‹ค.

JPA๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ JDBC ์‚ฌ์ด์—์„œ ๋™์ž‘ํ•˜๋ฉฐ, ๊ฐœ๋ฐœ์ž๊ฐ€ JPA๋ฅผ ํ™œ์šฉํ–ˆ์„ ๋•Œ JDBC API๋ฅผ ํ†ตํ•ด SQL์„ ํ˜ธ์ถœํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ํ˜ธ์ถœํ•˜๋Š” ์ „๊ฐœ๊ฐ€ ์ด๋ฃจ์–ด์ง„๋‹ค.

์ฆ‰, ๊ฐœ๋ฐœ์ž๋Š” JPA์˜ ํ™œ์šฉ๋ฒ•๋งŒ ์ตํžˆ๋ฉด DB ์ฟผ๋ฆฌ ๊ตฌํ˜„์—†์ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

โœจ JPA ํŠน์ง•

  1. ๊ฐ์ฒด ์ค‘์‹ฌ ๊ฐœ๋ฐœ ๊ฐ€๋Šฅ

    SQL ์ค‘์‹ฌ ๊ฐœ๋ฐœ์ด ์ด๋ฃจ์–ด์ง„๋‹ค๋ฉด, CRUD ์ž‘์—…์ด ๋ฐ˜๋ณตํ•ด์„œ ์ด๋ฃจ์–ด์ ธ์•ผํ•œ๋‹ค.

    ํ•˜๋‚˜์˜ ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑํ•ด์•ผํ•  ๋•Œ ์ด์— ํ•ด๋‹นํ•˜๋Š” CRUD๋ฅผ ์ „๋ถ€ ๋งŒ๋“ค์–ด์•ผ ํ•˜๋ฉฐ, ์ถ”ํ›„์— ์ปฌ๋Ÿผ์ด ์ƒ์„ฑ๋˜๋ฉด ๊ด€๋ จ SQL์„ ๋ชจ๋‘ ์ˆ˜์ •ํ•ด์•ผ ํ•˜๋Š” ๋ฒˆ๊ฑฐ๋กœ์›€์ด ์žˆ๋‹ค. ๋˜ํ•œ ๊ฐœ๋ฐœ ๊ณผ์ •์—์„œ ์‹ค์ˆ˜ํ•  ๊ฐ€๋Šฅ์„ฑ๋„ ๋†’์•„์ง„๋‹ค.

  2. ์ƒ์‚ฐ์„ฑ ์ฆ๊ฐ€

    SQL ์ฟผ๋ฆฌ๋ฅผ ์ง์ ‘ ์ƒ์„ฑํ•˜์ง€ ์•Š๊ณ , ๋งŒ๋“ค์–ด์ง„ ๊ฐ์ฒด์— JPA ๋ฉ”์†Œ๋“œ๋ฅผ ํ™œ์šฉํ•ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๋‹ค๋ฃจ๊ธฐ ๋•Œ๋ฌธ์— ๊ฐœ๋ฐœ์ž์—๊ฒŒ ๋งค์šฐ ํŽธ๋ฆฌ์„ฑ์„ ์ œ๊ณตํ•ด์ค€๋‹ค.

  3. ์œ ์ง€๋ณด์ˆ˜ ์šฉ์ด

    ์ฟผ๋ฆฌ ์ˆ˜์ •์ด ํ•„์š”ํ•  ๋•Œ, ์ด๋ฅผ ๋‹ด์•„์•ผ ํ•  DTO ํ•„๋“œ๋„ ๋ชจ๋‘ ๋ณ€๊ฒฝํ•ด์•ผ ํ•˜๋Š” ์ž‘์—…์ด ํ•„์š”ํ•˜์ง€๋งŒ JPA์—์„œ๋Š” ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค ์ •๋ณด๋งŒ ๋ณ€๊ฒฝํ•˜๋ฉด ๋˜๋ฏ€๋กœ ์œ ์ง€๋ณด์ˆ˜์— ์šฉ์ดํ•˜๋‹ค.

  4. ์„ฑ๋Šฅ ์ฆ๊ฐ€

    ์‚ฌ๋žŒ์ด ์ง์ ‘ SQL์„ ์งœ๋Š” ๊ฒƒ๊ณผ ๋น„๊ตํ•ด์„œ JPA๋Š” ๋™์ผํ•œ ์ฟผ๋ฆฌ์— ๋Œ€ํ•œ ์บ์‹œ ๊ธฐ๋Šฅ์„ ์ง€์›ํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ๋น„๊ต์  ๋†’์€ ์„ฑ๋Šฅ ํšจ์œจ์„ ๊ฒฝํ—˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

โœจ ์ œ์•ฝ์‚ฌํ•ญ

JPA๋Š” ๋ณต์žกํ•œ ์ฟผ๋ฆฌ๋ณด๋‹ค๋Š” ์‹ค์‹œ๊ฐ„ ์ฟผ๋ฆฌ์— ์ตœ์ ํ™”๋˜์–ด์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ํ†ต๊ณ„ ์ฒ˜๋ฆฌ์™€ ๊ฐ™์€ ๋ณต์žกํ•œ ์ž‘์—…์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋Š” ๊ธฐ์กด์˜ Mybatis์™€ ๊ฐ™์€ Mapper ๋ฐฉ์‹์ด ๋” ํšจ์œจ์ ์ผ ์ˆ˜ ์žˆ๋‹ค.

Spring์—์„œ๋Š” JPA์™€ Mybatis๋ฅผ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ์ƒํ™ฉ์— ๋งž๋Š” ๋ฐฉ์‹์„ ํƒํ•˜์—ฌ ๊ฐœ๋ฐœํ•˜๋ฉด ๋œ๋‹ค.

[Spring data JPA] ๋”ํ‹ฐ ์ฒดํ‚น(Dirty Checking)

ํŠธ๋žœ์žญ์…˜ ์•ˆ์—์„œ Entity์˜ ๋ณ€๊ฒฝ์ด ์ผ์–ด๋‚ฌ์„ ๋•Œ ๋ณ€๊ฒฝํ•œ ๋‚ด์šฉ์„ ์ž๋™์œผ๋กœ DB์— ๋ฐ˜์˜ํ•˜๋Š” ๊ฒƒ

ORM ๊ตฌํ˜„์ฒด ๊ฐœ๋ฐœ ์‹œ ๋”ํ‹ฐ ์ฒดํ‚น์ด๋ผ๋Š” ๋ง์„ ์ž์ฃผ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

๋”ํ‹ฐ ์ฒดํ‚น์ด ์–ด๋–ค ๊ฒƒ์„ ๋œปํ•˜๋Š” ์ง€ ๊ฐ„๋‹จํžˆ ์‚ดํŽด๋ณด์ž.

JPA๋กœ ๊ฐœ๋ฐœํ•˜๋Š” ๊ฒฝ์šฐ ๊ตฌํ˜„ํ•œ ํ•œ ๊ฐ€์ง€ ๊ธฐ๋Šฅ์„ ์˜ˆ๋กœ ๋“ค์–ด๋ณด์ž

โœจ ex) ์ฃผ๋ฌธ ์ทจ์†Œ ๊ธฐ๋Šฅ

@Transactional  
public void cancelOrder(Long orderId) {  
    //์ฃผ๋ฌธ ์—”ํ‹ฐํ‹ฐ ์กฐํšŒ  
    Order order = orderRepository.findOne(orderId);  

    //์ฃผ๋ฌธ ์ทจ์†Œ  
    order.cancel();  
}

orderId๋ฅผ ํ†ตํ•ด ์ฃผ๋ฌธ์„ ์ทจ์†Œํ•˜๋Š” ๋ฉ”์†Œ๋“œ๋‹ค. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ฐ˜์˜ํ•˜๊ธฐ ์œ„ํ•ด์„ , update์™€ ๊ฐ™์€ ์ฟผ๋ฆฌ๊ฐ€ ์žˆ์–ด์•ผํ•  ๊ฒƒ ๊ฐ™์€๋ฐ ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋‹ค.

ํ•˜์ง€๋งŒ, ์‹ค์ œ๋กœ ์ด ๋ฉ”์†Œ๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— update๊ฐ€ ์ž˜ ์ด๋ฃจ์–ด์ง„๋‹ค.

  • ํŠธ๋žœ์žญ์…˜ ์‹œ์ž‘

  • orderId๋กœ ์ฃผ๋ฌธ Entity ์กฐํšŒ

  • ํ•ด๋‹น Entity ์ฃผ๋ฌธ ์ทจ์†Œ ์ƒํƒœ๋กœ Update

  • ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹

์ด๋ฅผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”๋กœ '๋”ํ‹ฐ ์ฒดํ‚น(Dirty Checking)'์ด๋ผ๊ณ  ๋ณด๋ฉด ๋œ๋‹ค.

๊ทธ๋ƒฅ ๋”ํ‹ฐ ์ฒดํ‚น์˜ ๋‹จ์–ด๋งŒ ๊ฐ„๋‹จํžˆ ํ•ด์„ํ•˜๋ฉด ๋ณ€๊ฒฝ ๊ฐ์ง€๋กœ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์ข€ ๋” ์ž์„ธํžˆ ๋งํ•˜๋ฉด, Entity์—์„œ ๋ณ€๊ฒฝ์ด ์ผ์–ด๋‚œ ๊ฑธ ๊ฐ์ง€ํ•œ ๋’ค, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ฐ˜์˜์‹œ์ผœ์ค€๋‹ค๋Š” ์˜๋ฏธ๋‹ค. (๋ณ€๊ฒฝ์€ ์ตœ์ดˆ ์กฐํšŒ ์ƒํƒœ๊ฐ€ ๊ธฐ์ค€์ด๋‹ค)

Dirty : ์ƒํƒœ์˜ ๋ณ€ํ™”๊ฐ€ ์ƒ๊น€

Checking : ๊ฒ€์‚ฌ

JPA์—์„œ๋Š” ํŠธ๋žœ์žญ์…˜์ด ๋๋‚˜๋Š” ์‹œ์ ์— ๋ณ€ํ™”๊ฐ€ ์žˆ๋˜ ๋ชจ๋“  ์—”ํ‹ฐํ‹ฐ์˜ ๊ฐ์ฒด๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋กœ ์•Œ์•„์„œ ๋ฐ˜์˜์„ ์‹œ์ผœ์ค€๋‹ค. ์ฆ‰, ํŠธ๋žœ์žญ์…˜์˜ ๋งˆ์ง€๋ง‰ ์‹œ์ ์—์„œ ๋‹ค๋ฅธ ์ ์„ ๋ฐœ๊ฒฌํ–ˆ์„ ๋•Œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋กœ update ์ฟผ๋ฆฌ๋ฅผ ๋‚ ๋ ค์ฃผ๋Š” ๊ฒƒ์ด๋‹ค.

  • JPA์—์„œ Entity๋ฅผ ์กฐํšŒ

  • ์กฐํšŒ๋œ ์ƒํƒœ์˜ Entity์— ๋Œ€ํ•œ ์Šค๋ƒ…์ƒท ์ƒ์„ฑ

  • ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹ ํ›„ ํ•ด๋‹น ์Šค๋ƒ…์ƒท๊ณผ ํ˜„์žฌ Entity ์ƒํƒœ์˜ ๋‹ค๋ฅธ ์ ์„ ์ฒดํฌ

  • ๋‹ค๋ฅธ ์ ๋“ค์„ update ์ฟผ๋ฆฌ๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ „๋‹ฌ

์ด๋•Œ ๋”ํ‹ฐ ์ฒดํ‚น์„ ๊ฒ€์‚ฌํ•˜๋Š” ๋Œ€์ƒ์€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” Entity๋กœ๋งŒ ๋Œ€์ƒ์œผ๋กœ ํ•œ๋‹ค.

์ค€์˜์†, ๋น„์˜์† Entity๋Š” ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ์ง€๋ผ๋„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ฐ˜์˜์‹œํ‚ค์ง€ ์•Š๋Š”๋‹ค.

๊ธฐ๋ณธ์ ์œผ๋กœ ๋”ํ‹ฐ ์ฒดํ‚น์„ ์‹คํ–‰ํ•˜๋ฉด, SQL์—์„œ๋Š” ๋ณ€๊ฒฝ๋œ ์—”ํ‹ฐํ‹ฐ์˜ ๋ชจ๋“  ๋‚ด์šฉ์„ update ์ฟผ๋ฆฌ๋กœ ๋งŒ๋“ค์–ด ์ „๋‹ฌํ•˜๋Š”๋ฐ, ์ด๋•Œ ํ•„๋“œ๊ฐ€ ๋งŽ์•„์ง€๋ฉด ์ „์ฒด ํ•„๋“œ๋ฅผ updateํ•˜๋Š”๊ฒŒ ๋น„ํšจ์œจ์ ์ผ ์ˆ˜๋„ ์žˆ๋‹ค.

์ด๋•Œ๋Š” @DynamicUpdate๋ฅผ ํ•ด๋‹น Entity์— ์„ ์–ธํ•˜์—ฌ ๋ณ€๊ฒฝ ํ•„๋“œ๋งŒ ๋ฐ˜์˜์‹œํ‚ค๋„๋ก ๋งŒ๋“ค์–ด์ค„ ์ˆ˜ ์žˆ๋‹ค.

@Getter
@NoArgsConstructor
@Entity
@DynamicUpdate
public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String product;
}

Spring Security - ์ธ์ฆ ๋ฐ ๊ถŒํ•œ ๋ถ€์—ฌ

API์— ๊ถŒํ•œ ๊ธฐ๋Šฅ์ด ์—†์œผ๋ฉด, ์•„๋ฌด๋‚˜ ํšŒ์› ์ •๋ณด๋ฅผ ์กฐํšŒํ•˜๊ณ  ์ˆ˜์ •ํ•˜๊ณ  ์‚ญ์ œํ•  ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ด๋ฅผ ๋ง‰๊ธฐ ์œ„ํ•ด ์ธ์ฆ๋œ ์œ ์ €๋งŒ API๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์•ผํ•˜๋Š”๋ฐ, ์ด๋•Œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํ•ด๊ฒฐ ์ฑ… ์ค‘ ํ•˜๋‚˜๊ฐ€ Spring Security๋‹ค.

์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ๋Š” ์ธ์ฆ ๋ฐ ๊ถŒํ•œ ๋ถ€์—ฌ๋กœ ๋ฆฌ์†Œ์Šค ์‚ฌ์šฉ์„ ์ปจํŠธ๋กค ํ•  ์ˆ˜ ์žˆ๋Š” Spring Security๋ฅผ ์ œ๊ณตํ•œ๋‹ค. ์ด ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, ๋ณด์•ˆ ์ฒ˜๋ฆฌ๋ฅผ ์ž์ฒด์ ์œผ๋กœ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์•„๋„ ์‰ฝ๊ฒŒ ํ•„์š”ํ•œ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

Spring Security๋Š” ์Šคํ”„๋ง์˜ DispatcherServlet ์•ž๋‹จ์— Filter ํ˜•ํƒœ๋กœ ์œ„์น˜ํ•œ๋‹ค. Dispatcher๋กœ ๋„˜์–ด๊ฐ€๊ธฐ ์ „์— ์ด Filter๊ฐ€ ์š”์ฒญ์„ ๊ฐ€๋กœ์ฑ„์„œ, ํด๋ผ์ด์–ธํŠธ์˜ ๋ฆฌ์†Œ์Šค ์ ‘๊ทผ ๊ถŒํ•œ์„ ํ™•์ธํ•˜๊ณ , ์—†๋Š” ๊ฒฝ์šฐ์—๋Š” ์ธ์ฆ ์š”์ฒญ ํ™”๋ฉด์œผ๋กœ ์ž๋™ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธํ•œ๋‹ค.

โœจ Spring Security Filter

Filter์˜ ์ข…๋ฅ˜๋Š” ์ƒ๋‹นํžˆ ๋งŽ๋‹ค. ์œ„์—์„œ ์˜ˆ์‹œ๋กœ ๋“  ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ ์ ‘๊ทผ ๊ถŒํ•œ์ด ์—†์„ ๋•Œ ์ฒ˜๋ฆฌ๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ํ•„ํ„ฐ๋Š” UsernamePasswordAuthenticationFilter๋‹ค.

์ธ์ฆ ๊ถŒํ•œ์ด ์—†์„ ๋•Œ ์˜ค๋ฅ˜๋ฅผ JSON์œผ๋กœ ๋‚ด๋ ค์ฃผ๊ธฐ ์œ„ํ•ด ํ•ด๋‹น ํ•„ํ„ฐ๊ฐ€ ์‹คํ–‰๋˜๊ธฐ ์ „ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•  ๊ฒƒ์ด๋‹ค.

API ์ธ์ฆ ๋ฐ ๊ถŒํ•œ ๋ถ€์—ฌ๋ฅผ ์œ„ํ•œ ์ž‘์—… ์ˆœ์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

  1. ํšŒ์› ๊ฐ€์ž…, ๋กœ๊ทธ์ธ API ๊ตฌํ˜„

  2. ๋ฆฌ์†Œ์Šค ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ROLE_USER ๊ถŒํ•œ์„ ๊ฐ€์ž… ํšŒ์›์—๊ฒŒ ๋ถ€์—ฌ

  3. Spring Security ์„ค์ •์—์„œ ROLE_USER ๊ถŒํ•œ์„ ๊ฐ€์ง€๋ฉด ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๋„๋ก ์„ธํŒ…

  4. ๊ถŒํ•œ์ด ์žˆ๋Š” ํšŒ์›์ด ๋กœ๊ทธ์ธ ์„ฑ๊ณตํ•˜๋ฉด ๋ฆฌ์†Œ์Šค ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ JWT ํ† ํฐ ๋ฐœ๊ธ‰

  5. ํ•ด๋‹น ํšŒ์›์€ ๊ถŒํ•œ์ด ํ•„์š”ํ•œ API ์ ‘๊ทผ ์‹œ JWT ๋ณด์•ˆ ํ† ํฐ์„ ์‚ฌ์šฉ

์ด์ฒ˜๋Ÿผ ์ ‘๊ทผ ์ œํ•œ์ด ํ•„์š”ํ•œ API์—๋Š” ๋ณด์•ˆ ํ† ํฐ์„ ํ†ตํ•ด์„œ ์ด ์œ ์ €๊ฐ€ ๊ถŒํ•œ์ด ์žˆ๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ Spring Security๋ฅผ ํ†ตํ•ด ์ฒดํฌํ•˜๊ณ  ๋ฆฌ์†Œ์Šค๋ฅผ ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

โœจ Spring Security Configuration

์„œ๋ฒ„์— ๋ณด์•ˆ์„ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด Configuration์„ ๋งŒ๋“ ๋‹ค. ๊ธฐ์กด ์˜ˆ์‹œ์ฒ˜๋Ÿผ, USER์— ๋Œ€ํ•œ ๊ถŒํ•œ์„ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•œ ์ž‘์—…๋„ ์—ฌ๊ธฐ์„œ ์ง„ํ–‰๋œ๋‹ค.

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .httpBasic().disable() // rest api ์ด๋ฏ€๋กœ ๊ธฐ๋ณธ์„ค์ • ์‚ฌ์šฉ์•ˆํ•จ. ๊ธฐ๋ณธ์„ค์ •์€ ๋น„์ธ์ฆ์‹œ ๋กœ๊ทธ์ธํผ ํ™”๋ฉด์œผ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ
                .cors().configurationSource(corsConfigurationSource())
                .and()
                .csrf().disable() // rest api์ด๋ฏ€๋กœ csrf ๋ณด์•ˆ์ด ํ•„์š”์—†์œผ๋ฏ€๋กœ disable์ฒ˜๋ฆฌ.
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // jwt token์œผ๋กœ ์ธ์ฆํ•˜๋ฏ€๋กœ ์„ธ์…˜์€ ํ•„์š”์—†์œผ๋ฏ€๋กœ ์ƒ์„ฑ์•ˆํ•จ.
                .and()
                .authorizeRequests() // ๋‹ค์Œ ๋ฆฌํ€˜์ŠคํŠธ์— ๋Œ€ํ•œ ์‚ฌ์šฉ๊ถŒํ•œ ์ฒดํฌ
                .antMatchers("/*/signin", "/*/signin/**", "/*/signup", "/*/signup/**", "/social/**").permitAll() // ๊ฐ€์ž… ๋ฐ ์ธ์ฆ ์ฃผ์†Œ๋Š” ๋ˆ„๊ตฌ๋‚˜ ์ ‘๊ทผ๊ฐ€๋Šฅ
                .antMatchers(HttpMethod.GET, "home/**").permitAll() // home์œผ๋กœ ์‹œ์ž‘ํ•˜๋Š” GET์š”์ฒญ ๋ฆฌ์†Œ์Šค๋Š” ๋ˆ„๊ตฌ๋‚˜ ์ ‘๊ทผ๊ฐ€๋Šฅ
                .anyRequest().hasRole("USER") // ๊ทธ์™ธ ๋‚˜๋จธ์ง€ ์š”์ฒญ์€ ๋ชจ๋‘ ์ธ์ฆ๋œ ํšŒ์›๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅ
                .and()
                .addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class); // jwt token ํ•„ํ„ฐ๋ฅผ id/password ์ธ์ฆ ํ•„ํ„ฐ ์ „์— ๋„ฃ๋Š”๋‹ค

    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .httpBasic().disable() 
                .cors().configurationSource(corsConfigurationSource())
                .and()
                .csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) 
                .and()
                .authorizeRequests()
                .antMatchers("/*/signin", "/*/signin/**", "/*/signup", "/*/signup/**", "/social/**").permitAll() 
                .antMatchers(HttpMethod.GET, "home/**").permitAll()
                .anyRequest().hasRole("USER") 
                .and()
                .addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider),
                                         UsernamePasswordAuthenticationFilter.class); 

    }

Last updated