Spring ๐ŸŒฑ 13

JWT ์ธ์ฆโˆ™์ธ๊ฐ€ ๊ตฌํ˜„ํ•˜๊ธฐ

JWT๋ž€ JWT(JSON Web Token)๋ž€? | JWT ๊ตฌ์„ฑ์š”์†Œ ํ† ํฐ์ด ํ•„์š”ํ•œ ์ด์œ ์™€ JWT ๋™์ž‘ ๋ฐฉ์‹ ์ธ์ฆ, ์ธ๊ฐ€ ํ๋ฆ„(+ํด๋ž˜์Šค ๋‹ค์ด์–ด๊ทธ๋žจ) JwtProvider ๊ตฌํ˜„ JwtProvider๋Š” Jwt๋ฅผ ์ƒ์„ฑ, ํŒŒ์‹ฑ, ๊ฒ€์ฆํ•˜๋Š” ํด๋ž˜์Šค๋‹ค. ๊ตฌํ˜„์—” jjwt ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค. jwt ๊ด€๋ จ ์ •๋ณด์ธ secret, expire-length๋ฅผ application.yml์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ค์ •ํ–ˆ๋‹ค. jwt: header: Authorization secret: (ํŠน์ • ๋ฌธ์ž์—ด์„ Base64๋กœ ์ธ์ฝ”๋”ฉํ•œ ๊ฐ’) access-token: expire-length: 3600 refresh-token: expire-length: 86400private final Key key; @Value("${jwt.access-token..

Spring ๐ŸŒฑ 2024.02.11

๋™์‹œ์„ฑ ์ด์Šˆ ํ•ด๊ฒฐํ•˜๊ธฐ : synchronized, MySQL Lock, Redis Lock

๋™์‹œ์„ฑ ๋ฌธ์ œ ํ•˜๋‚˜์˜ ๋ฐ์ดํ„ฐ์— ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ์ ‘๊ทผํ•˜๋ฉด์„œ ์ƒ๊ธฐ๋Š” ๋ฌธ์ œ๋ฅผ ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ผ๊ณ  ํ•œ๋‹ค. ํ•˜๋‚˜์˜ ์„ธ์…˜์ด ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ • ์ค‘์ผ๋•Œ, ๋‹ค๋ฅธ ์„ธ์…˜์—์„œ ์ˆ˜์ • ์ „์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•ด ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•จ์œผ๋กœ์จ ๋ฐ์ดํ„ฐ์˜ ์ •ํ•ฉ์„ฑ์ด ๊นจ์ง€๊ฒŒ ๋œ๋‹ค. ๋™์‹œ์„ฑ ๋ฌธ์ œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• ๋™์‹œ์„ฑ ๋ฌธ์ œ๋Š” ํ•˜๋‚˜์˜ ์„ธ์…˜์ด ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋™์•ˆ, ๋‹ค๋ฅธ ์„ธ์…˜์€ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ๋ชปํ•˜๊ฒŒ ํ•จ์œผ๋กœ์„œ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค. ์„ธ์…˜์˜ ๋…๋ฆฝ์ ์ธ ์‹คํ–‰์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ๋‹ค์Œ ์„ธ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค. 1. Application ๋ ˆ๋ฒจ์—์„œ synchronized ์‚ฌ์šฉ 2. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ Lock ์‚ฌ์šฉ 3. Redis์˜ Lock ์‚ฌ์šฉ ์žฌ๊ณ  ๊ฐ์†Œ ๋กœ์ง์„ ํ†ตํ•ด ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ์‚ดํŽด๋ณด๊ณ  ํ•ด๊ฒฐํ•ด๋ณด์ž. ๋ชฉ์ฐจ 0. ์žฌ๊ณ  ์‹œ์Šคํ…œ ๊ธฐ๋ณธ ๋กœ์ง 1. ๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ๋™์‹œ์„ฑ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š”..

Spring ๐ŸŒฑ 2024.02.11

๋™์‹œ์„ฑ ์ด์Šˆ ํ•ด๊ฒฐํ•˜๊ธฐ : ๋น„๊ด€์  ๋ฝ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์— @Transactional์„ ์‚ฌ์šฉํ•˜๋ฉด Lock wait timeout exceeded๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ด์œ 

๊ฒฐ๋ก ๋ถ€ํ„ฐ ๋งํ•˜์ž๋ฉด @Transactional์˜ ๋™์ž‘ ๋ฐฉ์‹ ๋•Œ๋ฌธ์ด๋‹ค. ๋ฌธ์ œ ์ƒํ™ฉ ์žฌ๊ณ ๋ฅผ ํ•˜๋‚˜ ๊ฐ์†Œ์‹œํ‚ค๋Š” ์š”์ฒญ์ด ๋™์‹œ์— 100๊ฐœ๊ฐ€ ๋“ค์–ด์˜จ ์ƒํ™ฉ์ด๋‹ค. ๋น„๊ด€์  ๋ฝ์„ ์‚ฌ์šฉํ•ด์„œ Race condition์„ ํ”ผํ•˜๋ คํ•œ๋‹ค. ์ด๋•Œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์— @Transactional์„ ๋ถ™์ด๋ฉด Lock wait timeout exceeded๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉฐ ํ…Œ์ŠคํŠธ์— ์‹คํŒจํ•œ๋‹ค. @SpringBootTest @Transactional class StockServiceTest { @Test public void ๋™์‹œ์—_100๊ฐœ์˜_์š”์ฒญ() throws InterruptedException { int threadCount = 100; ExecutorService executorService = Executors.newFixedThreadPool(32..

Spring ๐ŸŒฑ 2023.12.16

๋™์‹œ์„ฑ ์ด์Šˆ ํ•ด๊ฒฐํ•˜๊ธฐ : synchronized์™€ saveAndFlush

synchronized - synchronized๋ฅผ ๋ฉ”์†Œ๋“œ์— ๋ช…์‹œํ•ด์ฃผ๋ฉด ํ•˜๋‚˜์˜ ์Šค๋ ˆ๋“œ๋งŒ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. - ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ์Šค๋ ˆ๋“œ๊ฐ„ ๋ฐ์ดํ„ฐ ๋™๊ธฐํ™”๋ฅผ ์œ„ํ•ด ์ž๋ฐ”์—์„œ ์ œ๊ณตํ•˜๋Š” ํ‚ค์›Œ๋“œ๋‹ค. - synchronized๋Š” ํ˜„์žฌ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ์Šค๋ ˆ๋“œ๋ฅผ ์ œ์™ธํ•˜๊ณ  ๋‚˜๋จธ์ง€ ์Šค๋ ˆ๋“œ๋“ค์ด ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•  ์ˆ˜ ์—†๋„๋ก ๋ง‰์•„ ์ˆœ์ฐจ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค. ๋ณธ๋ฌธ ์ •์›์ด n๋ช…์ธ ๋…์„œ ๋ชจ์ž„์ด ์žˆ๋‹ค. ํ˜„์žฌ ๋ชจ์ž„์›์€ n-1๋ช…์œผ๋กœ, ํ•œ ๋ช…๋งŒ ๋” ๊ฐ€์ž…ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋•Œ ๋‘ ๋ช…์˜ ์œ ์ €๊ฐ€ ๋™์‹œ์— ๊ฐ€์ž…์„ ์‹œ๋„ํ•˜๋ฉด ๋‘˜ ์ค‘ ํ•œ ๋ช…๋งŒ ๊ฐ€์ž…์ด ๋˜์–ด์•ผ ํ•œ๋‹ค. ๊ฐ€์ž… ์‹œ, ๋ชจ์ž„ ์ •์›์„ ํ™•์ธํ•˜์—ฌ ๋‚จ์€ ์ž๋ฆฌ๊ฐ€ ์—†๋‹ค๋ฉด ์ •์› ์ดˆ๊ณผ ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค. ๋ชจ์ž„ ๊ฐ€์ž… ๊ธฐ๋Šฅ ์ „์ฒด ์ฝ”๋“œ - Club.java : ๋…์„œ ๋ชจ์ž„ ์—”ํ‹ฐํ‹ฐ - ClubServic..

Spring ๐ŸŒฑ 2023.12.01

UserSesseion ์žฌ๋กœ๋“œํ•˜๊ธฐ

์ตœ์ดˆ ์†Œ์…œ ๋กœ๊ทธ์ธ ์‹œ, ๋‹‰๋„ค์ž„์„ ์ž…๋ ฅ๋ฐ›๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋ ค ํ–ˆ๋‹ค. ๋งŒ์•ฝ ๋‹‰๋„ค์ž„์ด ์žˆ๋‹ค๋ฉด, ๋‹‰๋„ค์ž„์„ ์ž…๋ ฅ๋ฐ›๋Š” ํผ์„ ๋„์šฐ์ง€ ์•Š๋Š”๋‹ค. @PutMapping("/users/update") public RedirectView updateUserInfo(@LoginUser SessionUser sessionUser, SetNicknameForm nicknameForm, BindingResult result) { String nickname = nicknameForm.getNickname(); if (result.hasErrors()) { System.out.println("form error"); return new RedirectView("/home"); } userService.updateUserInfo(sess..

Spring ๐ŸŒฑ 2023.11.03

Dispatcher Servlet์ด๋ž€? ๋™์ž‘ ๋ฐฉ์‹๊ณผ ์žฅ์ 

๋””์ŠคํŒจ์ฒ˜ ์„œ๋ธ”๋ฆฟ์˜ dispatch๋Š” "๋ณด๋‚ด๋‹ค"๋ผ๋Š” ๋œป์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด๋Ÿฌํ•œ ๋‹จ์–ด๋ฅผ ํฌํ•จํ•˜๋Š” ๋””์ŠคํŒจ์ฒ˜ ์„œ๋ธ”๋ฆฟ์€ HTTP ํ”„๋กœํ† ์ฝœ๋กœ ๋“ค์–ด์˜ค๋Š” ๋ชจ๋“  ์š”์ฒญ์„ ๊ฐ€์žฅ ๋จผ์ € ๋ฐ›์•„ ์ ํ•ฉํ•œ ์ปจํŠธ๋กค๋Ÿฌ์— ์œ„์ž„ํ•ด์ฃผ๋Š” ํ”„๋ก ํŠธ ์ปจํŠธ๋กค๋Ÿฌ(Front Controller)๋ผ๊ณ  ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋””์ŠคํŒจ์ฒ˜ ์„œ๋ธ”๋ฆฟ์€ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด URI์— ๋งž๋Š” ํ•ธ๋“ค๋Ÿฌ์™€ ๋ทฐ๋ฅผ ์ฐพ๋Š”๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™์ด "/posts/save"์˜ URI ์ฃผ์†Œ๋กœ GET ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด ๋””์ŠคํŒจ์ฒ˜ ์„œ๋ธ”๋ฆฟ์€ ์•„๋ž˜ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ฐพ์•„ ์ž‘์—…์„ ์œ„์ž„ํ•œ๋‹ค. //PostController.java @GetMapping("/posts/save") public String loadPostForm() { return "post-save"; } Dispatcher-Servlet์˜ ์žฅ์  Spring..

Spring ๐ŸŒฑ 2023.09.25

[Spring JPA] Entity์— @Setter๋ฅผ ์ง€์–‘ํ•ด์•ผ ํ•˜๋Š” ์ด์œ 

1. ๋ฐ์ดํ„ฐ์˜ ๋ฌด๊ฒฐ์„ฑ์„ ๊นจ๋œจ๋ฆฐ๋‹ค. ์—”ํ‹ฐํ‹ฐ๋Š” ์ด์ƒ์ ์œผ๋กœ ๋ถˆ๋ณ€์„ฑ์„ ๊ฐ€์ง€๋„๋ก ์„ค๊ณ„๋˜์–ด์•ผ ํ•œ๋‹ค. @Setter๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์—”ํ‹ฐํ‹ฐ์˜ ๋ชจ๋“  ํ•„๋“œ์— ๋Œ€ํ•ด ์™ธ๋ถ€์—์„œ ์ˆ˜์ •์ด ๊ฐ€๋Šฅํ•ด์ง„๋‹ค. ๋”ฐ๋ผ์„œ ์—”ํ‹ฐํ‹ฐ์˜ ์ƒํƒœ๋ฅผ ๋ฌด์ž‘์œ„๋กœ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๋ฌดํšจํ•œ ๊ฐ’์ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ๋  ์ˆ˜ ์žˆ๋‹ค. 2. ๋ณ€๊ฒฝ ํฌ์ธํŠธ๋ฅผ ์ถ”์ ํ•˜๊ธฐ ์–ด๋ ค์›Œ์ง„๋‹ค. @Setter๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์–ด๋””์„œ๋“  ํ•„๋“œ์— ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•ด์ง€๊ธฐ ๋•Œ๋ฌธ์— ์–ด๋Š ์‹œ์ ์ด๋“  ์—”ํ‹ฐํ‹ฐ์˜ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ ์—”ํ‹ฐํ‹ฐ ์ƒํƒœ์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์–ด๋””์„œ ๊ฐ’์ด ๋ณ€๊ฒฝ๋œ ๊ฒƒ์ธ์ง€ ์ถ”์ ํ•˜๊ธฐ ์–ด๋ ค์›Œ์ ธ ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์–ด๋ ต๋‹ค. 3. ์‚ฌ์šฉ ์˜๋„๊ฐ€ ๋ถ„๋ช…ํ•˜์ง€ ์•Š๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด post.setTitle('title1')์ด๋ž€ ์ฝ”๋“œ๊ฐ€ ์žˆ์œผ๋ฉด ์ด๊ฒŒ ๊ฒŒ์‹œ๊ธ€์˜ ์ œ๋ชฉ์„ ์ƒ์„ฑํ•˜๋Š” ๊ฑด์ง€, ๋ณ€๊ฒฝํ•˜๋Š” ๊ฑด์ง€ ํŒŒ์•…ํ•˜๊ธฐ ์–ด๋ ต๋‹ค. ๋”ฐ๋ผ์„œ, ๋ฐ์ดํ„ฐ..

Spring ๐ŸŒฑ 2023.07.27

[JPA] ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ

์˜์†์„ฑ ์ปจํ…์ŠคํŠธ - ์—”ํ‹ฐํ‹ฐ๋ฅผ ์˜๊ตฌ ์ €์žฅํ•˜๋Š” ํ™˜๊ฒฝ์„ ์˜๋ฏธํ•œ๋‹ค. - EntityManager API๋ฅผ ํ†ตํ•ด ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ ‘๊ทผํ•˜์—ฌ ์—”ํ‹ฐํ‹ฐ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑ, ์กฐํšŒ, ์‚ญ์ œํ•  ์ˆ˜ ์žˆ๋‹ค. ์—”ํ‹ฐํ‹ฐ์˜ ์ƒ๋ช…์ฃผ๊ธฐ ๋น„์˜์† - ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์™€ ์ „ํ˜€ ๊ด€๊ณ„๊ฐ€ ์—†๋Š” ์ƒˆ๋กœ์šด ์ƒํƒœ๋ฅผ ์˜๋ฏธํ•œ๋‹ค. - new๋กœ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ–ˆ์„ ๋•Œ, ์ด ๊ฐ์ฒด๋Š” ๋น„์˜์† ์ƒํƒœ๋ผ๊ณ  ๋งํ•  ์ˆ˜ ์žˆ๋‹ค. Member member = new Member(); member.setId("member1"); member.setUsername("ํšŒ์›1"); ์˜์† - ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์˜ํ•ด ๊ด€๋ฆฌ๋˜๋Š” ์ƒํƒœ๋ฅผ ์˜๋ฏธํ•œ๋‹ค. - em.persist๋กœ ๊ฐ์ฒด๋ฅผ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ €์žฅํ•  ์ˆ˜ ์žˆ๋‹ค.(์˜์†ํ™”) * ์ด ์‹œ์ ์— ๊ฐ์ฒด๋ฅผ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ €์žฅํ•˜๋Š” ๊ฒƒ์ด์ง€ DB์— ์ €์žฅํ•˜๋Š” ๊ฑด ์•„๋‹ˆ๋‹ค!..

Spring ๐ŸŒฑ 2023.07.25

[Spring] ์‹ฑ๊ธ€ํ†ค ์ปจํ…Œ์ด๋„ˆ

์‹ฑ๊ธ€ํ†ค ํŒจํ„ด ๊ฐ์ฒด์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ๋”ฑ 1๊ฐœ๋งŒ ์ƒ์„ฑ๋˜์–ด ๊ณต์œ ๋˜๋Š” ํŒจํ„ด์„ ์˜๋ฏธํ•œ๋‹ค. ์ธ์Šคํ„ด์Šค๊ฐ€ ํ•˜๋‚˜์”ฉ๋งŒ ์ƒ์„ฑ๋˜๋ฏ€๋กœ ํšจ์œจ์ ์ธ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค. ๊ฐ์ฒด๊ฐ€ ํ˜ธ์ถœ๋์„ ๋•Œ, ์ด๋ฏธ ๋งŒ๋“ค์–ด๋†“์€ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ณต์œ ํ•จ์œผ๋กœ์จ ํšจ์œจ์ ์ธ ์žฌ์‚ฌ์šฉ ๋˜ํ•œ ๊ฐ€๋Šฅํ•˜๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์€ โ‘  ๊ตฌํ˜„ ์ฝ”๋“œ๊ฐ€ ๋งŽ๊ณ , โ‘ก ์˜์กด๊ด€๊ณ„์ƒ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๊ตฌ์ฒด ํด๋ž˜์Šค์— ์˜์กดํ•˜์—ฌ DIP, OCP๋ฅผ ์œ„๋ฐ˜ํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์œผ๋ฉฐ*, โ‘ข ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์–ด๋ ต๋‹จ ์ด์œ  ๋“ฑ์œผ๋กœ ์œ ์—ฐ์„ฑ์ด ๋–จ์–ด์ง„๋‹ค. public class SingletonService { public static final SingletonService instance = new SingletonService(); ... } * ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๊ตฌ์ฒด ํด๋ž˜์Šค(SingletonService)์— ์˜์กดํ•˜๋ฉฐ DIP, OCP๋ฅผ..

Spring ๐ŸŒฑ 2023.07.19