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));
}
}
@RunWith(SpringRunner.class)
ํ ์คํธ๋ฅผ ์งํํ ๋ JUnit์ ๋ด์ฅ๋ ์คํ์ ์ธ์ ๋ค๋ฅธ ์คํ์๋ฅผ ์คํ์ํจ๋ค.
์คํ๋ง ๋ถํธ ํ ์คํธ์ JUnit ์ฌ์ด์ ์ฐ๊ฒฐ์ ์ญํ ์ ํ๋ค๊ณ ์๊ฐํ๋ฉด ๋๋ค.
@WebMvcTest
์ปจํธ๋กค๋ฌ๋ง ์ฌ์ฉํ ๋ ์ ์ธ์ด ๊ฐ๋ฅํ๋ฉฐ, Spring MVC์ ์ง์คํ ์ ์๋ ์ด๋ ธํ ์ด์ ์ด๋ค.
@Autowired
์คํ๋ง์ด ๊ด๋ฆฌํ๋ Bean์ ์ฃผ์ ์์ผ์ค๋ค.
MockMvc
์น API๋ฅผ ํ ์คํธํ ๋ ์ฌ์ฉํ๋ฉฐ, ์ด๋ฅผ ํตํด HTTP GET, POST, DELETE ๋ฑ์ ๋ํ API ํ ์คํธ๊ฐ ๊ฐ๋ฅํ๋ค.
mvc.perform(get("/home"))
/home
์ฃผ์๋ก HTTP GET ์์ฒญ์ ํ ์ํฉ์ด๋ค.
.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 ํน์ง
๊ฐ์ฒด ์ค์ฌ ๊ฐ๋ฐ ๊ฐ๋ฅ
SQL ์ค์ฌ ๊ฐ๋ฐ์ด ์ด๋ฃจ์ด์ง๋ค๋ฉด, CRUD ์์ ์ด ๋ฐ๋ณตํด์ ์ด๋ฃจ์ด์ ธ์ผํ๋ค.
ํ๋์ ํ ์ด๋ธ์ ์์ฑํด์ผํ ๋ ์ด์ ํด๋นํ๋ CRUD๋ฅผ ์ ๋ถ ๋ง๋ค์ด์ผ ํ๋ฉฐ, ์ถํ์ ์ปฌ๋ผ์ด ์์ฑ๋๋ฉด ๊ด๋ จ SQL์ ๋ชจ๋ ์์ ํด์ผ ํ๋ ๋ฒ๊ฑฐ๋ก์์ด ์๋ค. ๋ํ ๊ฐ๋ฐ ๊ณผ์ ์์ ์ค์ํ ๊ฐ๋ฅ์ฑ๋ ๋์์ง๋ค.
์์ฐ์ฑ ์ฆ๊ฐ
SQL ์ฟผ๋ฆฌ๋ฅผ ์ง์ ์์ฑํ์ง ์๊ณ , ๋ง๋ค์ด์ง ๊ฐ์ฒด์ JPA ๋ฉ์๋๋ฅผ ํ์ฉํด ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ๋ค๋ฃจ๊ธฐ ๋๋ฌธ์ ๊ฐ๋ฐ์์๊ฒ ๋งค์ฐ ํธ๋ฆฌ์ฑ์ ์ ๊ณตํด์ค๋ค.
์ ์ง๋ณด์ ์ฉ์ด
์ฟผ๋ฆฌ ์์ ์ด ํ์ํ ๋, ์ด๋ฅผ ๋ด์์ผ ํ DTO ํ๋๋ ๋ชจ๋ ๋ณ๊ฒฝํด์ผ ํ๋ ์์ ์ด ํ์ํ์ง๋ง JPA์์๋ ์ํฐํฐ ํด๋์ค ์ ๋ณด๋ง ๋ณ๊ฒฝํ๋ฉด ๋๋ฏ๋ก ์ ์ง๋ณด์์ ์ฉ์ดํ๋ค.
์ฑ๋ฅ ์ฆ๊ฐ
์ฌ๋์ด ์ง์ 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 ์ธ์ฆ ๋ฐ ๊ถํ ๋ถ์ฌ๋ฅผ ์ํ ์์ ์์๋ ์๋์ ๊ฐ์ด ๊ตฌ์ฑํ ์ ์๋ค.
ํ์ ๊ฐ์ , ๋ก๊ทธ์ธ API ๊ตฌํ
๋ฆฌ์์ค ์ ๊ทผ ๊ฐ๋ฅํ ROLE_USER ๊ถํ์ ๊ฐ์ ํ์์๊ฒ ๋ถ์ฌ
Spring Security ์ค์ ์์ ROLE_USER ๊ถํ์ ๊ฐ์ง๋ฉด ์ ๊ทผ ๊ฐ๋ฅํ๋๋ก ์ธํ
๊ถํ์ด ์๋ ํ์์ด ๋ก๊ทธ์ธ ์ฑ๊ณตํ๋ฉด ๋ฆฌ์์ค ์ ๊ทผ ๊ฐ๋ฅํ JWT ํ ํฐ ๋ฐ๊ธ
ํด๋น ํ์์ ๊ถํ์ด ํ์ํ 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