chap 12 - JAVA-JIKIMI/SPRING-IN-ACTION-5 GitHub Wiki

์ด์ „์—๋Š” ์Šคํ”„๋ง WebFlux๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฆฌ์•กํ‹ฐ๋ธŒํ•˜๊ณ  ๋ธ”๋กœํ‚น์ด ์—†๋Š” ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์•˜๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ๊ฐ™์ด ์ž‘๋™๋˜๋Š” ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ๋„ ๋ธ”๋กœํ‚น์ด ์—†์–ด์•ผ ์ง„์ •ํ•œ ๋ธ”๋กœํ‚น ์—†๋Š” ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ๋  ์ˆ˜ ์žˆ๋‹ค.

๋งŒ์ผ ๋ธ”๋กœํ‚น ๋˜๋Š” ๋ฆฌํผ์ง€ํ„ฐ๋ฆฌ์— ์˜์กดํ•˜๋Š” ์Šคํ”„๋ง WebFlux ๋ฆฌ์•กํ‹ฐ๋ธŒ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค๋ฉด, ์ด ์ปจํŠธ๋กค๋Ÿฌ๋Š” ํ•ด๋‹น ๋ฆฌํผ์ง€ํ„ฐ๋ฆฌ์˜ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ์„ ๊ธฐ๋‹ค๋ฆฌ๋А๋ผ ๋ธ”๋กœํ‚น๋  ๊ฒƒ์ด๋‹ค.

๋”ฐ๋ผ์„œ ์ปจํŠธ๋กค๋Ÿฌ๋กœ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ด๋ฅด๊ธฐ๊นŒ์ง€ ๋ฐ์ดํ„ฐ์˜ ์ „์ฒด flow๊ฐ€ ๋ฆฌ์•กํ‹ฐ๋ธŒํ•˜๊ณ  ๋ธ”๋กœํ‚น๋˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค.

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ์˜ ๋ฆฌ์•กํ‹ฐ๋ธŒ ๊ฐœ๋… ์ดํ•ดํ•˜๊ธฐ

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ๋Š” Kay ๋ฆด๋ฆฌ์ฆˆ ํŠธ๋ ˆ์ธ๋ถ€ํ„ฐ Reactvie Repository์˜ ์ง€์›์„ ์ œ๊ณตํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ๋‹ค.

Reactvie Repository๋Š” ์นด์‚ฐ๋“œ๋ผ, ๋ชฝ๊ณ DB, ์นด์šฐ์น˜๋ฒ ์ด์Šค, Redis ๋“ฑ์„ ์ง€์›ํ•œ๋‹ค.

ํ•˜์ง€๋งŒ RDB๋‚˜ JPA๋Š” ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋ฐ, ์ด๋“ค์€ ํ‘œ์ค€ํ™”๋œ ๋น„๋™๊ธฐ API๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๋”ฐ๋ผ์„œ ์•ž์œผ๋กœ ์นด์‚ฐ๋“œ๋ผ์™€ ๋ชฝ๊ณ DB๋ฅผ ์ด์šฉํ•˜์—ฌ ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ ๋ฆฌ์•กํ‹ฐ๋ธŒ๋ฅผ ์‚ฌ์šฉํ•ด๋ณผ ๊ฒƒ์ด๋‹ค.


์Šคํ”„๋ง ๋ฐ์ดํ„ฐ ๋ฆฌ์•กํ‹ฐ๋ธŒ ๊ฐœ์š”

Reactvie Repository๋Š” ๋„๋ฉ”์ธ ํƒ€์ž…์ด๋‚˜ ์ปฌ๋ ‰์…˜ ๋Œ€์‹ , Mono๋‚˜ Flux๋ฅผ ์ธ์ž๋กœ ๋ฐ›๊ฑฐ๋‚˜ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ–๋Š”๋‹ค.


๋ฆฌ์•กํ‹ฐ๋ธŒ์™€ ๋ฆฌ์•กํ‹ฐ๋ธŒ๊ฐ€ ์•„๋‹Œ ํƒ€์ž… ๊ฐ„์˜ ๋ณ€ํ™˜

๊ธฐ์กด์— RDB๋ฅผ ์‚ฌ์šฉ์ค‘์ผ ๋•Œ๋„ Reactive Programming์„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

RDB๊ฐ€ ๋ธ”๋กœํ‚น ์—†๋Š” Reactive Query๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š๋”๋ผ๋„, ์šฐ์„  ๋ธ”๋กœํ‚น ๋˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€์„œ ๊ฐ€๋Šฅํ•œ ๋นจ๋ฆฌ ๋ฆฌ์•กํ‹ฐ๋ธŒ ํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ๋“ค์ด Reactive์˜ ์žฅ์ ์„ ํ™œ์šฉํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, RDB์™€ ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ํ•ด๋ณด์ž. ์ด ๊ฒฝ์šฐ OrderRepository๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์‹œ๊ทธ๋‹ˆ์ฒ˜์˜ ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋‹ค.

List<Order> findByUser(User user);

์ด findByUser()๋Š” ๋ธ”๋กœํ‚น ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค. ์™œ๋ƒํ•˜๋ฉด List๊ฐ€ Reactive ํƒ€์ž…์ด ์•„๋‹ˆ๋ฏ€๋กœ ์–ด๋–ค Reactive ์˜คํผ๋ ˆ์ด์…˜๋„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๊ฒŒ๋‹ค๊ฐ€ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ findByUser()๋ฅผ ํ˜ธ์ถœํ–ˆ๋‹ค๋ฉด ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌ์•กํ‹ฐ๋ธŒํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์–ด ํ™•์žฅ์„ฑ์„ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์—†๋‹ค.

์ด ๊ฒฝ์šฐ์—” ๊ฐ€๋Šฅํ•œ ๋นจ๋ฆฌ Reactive๊ฐ€ ์•„๋‹Œ List๋ฅผ Flux๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ๊ฒฐ๊ณผ๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค.

List<Order> orders = repo.findByUser(someUser);
Flux<Order> orderFlux = Flux.fromIterable(orders);

Mono๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ์—” ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ•˜๋ฉด ๋œ๋‹ค.

Order order = repo.findById(id);
Mono<Order> orderFlux = Mono.just(order);

์ด์ฒ˜๋Ÿผ Mono์˜ just()๋‚˜ Flux์˜ fromIterable(), fromArray(), fromStream()์„ ์‚ฌ์šฉํ•˜๋ฉด Repository์˜ Reactive๊ฐ€ ์•„๋‹Œ ๋ธ”๋กœํ‚น ์ฝ”๋“œ๋ฅผ ๊ฒฉ๋ฆฌ์‹œํ‚ค๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์–ด๋””์„œ๋“  Reactive ํƒ€์ž…์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด๋ฒˆ์—” ์ €์žฅํ•˜๋Š” ๊ฒฝ์šฐ์— ๋Œ€ํ•ด์„œ ์‚ดํŽด๋ณด์ž. Mono๋‚˜ Flux ๋ชจ๋‘ ์ž์‹ ๋“ค์ด ๋ฐœํ–‰ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋„๋ฉ”์ธ ํƒ€์ž…์ด๋‚˜ Iterable ํƒ€์ž…์œผ๋กœ ์ถ”์ถœํ•˜๋Š” ์˜คํผ๋ ˆ์ด์…˜์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

Taco taco = tacoMono.block();
tacoRepo.save(taco);

Iterable<Taco> tacos = tacoFlux.toIterable();
tacoRepo.saveAll(tacos);

Mono์˜ block()์ด๋‚˜ Flux์˜ toIterable()์€ ์ถ”์ถœ ์ž‘์—…์„ ํ•  ๋•Œ ๋ธ”๋กœํ‚น์ด ๋œ๋‹ค. ๋”ฐ๋ผ์„œ ์ด๋Ÿฐ์‹์˜ Mono์™€ Flux๋ฅผ ์‚ฌ์šฉ์„ ์ตœ์†Œํ™” ํ•ด์•ผ ํ•œ๋‹ค.


๋ธ”๋กœํ‚น๋˜๋Š” ํƒ€์ž…์„ ๋” Reactiveํ•˜๊ฒŒ ์ถ”์ถœ ํ•  ์ˆ˜๋„ ์žˆ๋‹ค. Mono๋‚˜ Flux๋ฅผ ๊ตฌ๋…ํ•˜๋ฉด์„œ ๋ฐœํ–‰๋˜๋Š” ์š”์†Œ ๊ฐ๊ฐ์— ๋Œ€ํ•ด ์›ํ•˜๋Š” ์˜คํผ๋ ˆ์ด์…˜์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

tacoFlux.subscribe(
        taco -> {
            tacoRepo.save(taco);
        }
);

tacoRepo์˜ save๋Š” ์—ฌ์ „ํžˆ ๋ธ”๋กœํ‚น ์˜คํผ๋ ˆ์ด์…˜์ด๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ Flux๋‚˜ Mono๊ฐ€ ๋ฐœํ–‰ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์†Œ๋น„ํ•˜๊ณ  ์ฒ˜๋ฆฌํ•˜๋Š” Reactive ๋ฐฉ์‹์˜ subscribe()๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ๋ธ”๋กœํ‚น ๋ฐฉ์‹์˜ ์ผ๊ด„์ฒ˜๋ฆฌ๋ณด๋‹ค๋Š” ๋” ๋ฐ”๋žŒ์งํ•˜๋‹ค.

๋ฆฌ์•กํ‹ฐ๋ธŒ ๋ชฝ๊ณ DB ๋ฆฌํผ์ง€ํ„ฐ๋ฆฌ ์ž‘์„ฑํ•˜๊ธฐ

NoSql ์ค‘ ํ•˜๋‚˜์ธ ๋ชฝ๊ณ DB๋Š” ๋ฌธ์„œํ˜• DB๋‹ค. ๋ชฝ๊ณ DB๋Š” BSON(Binary JSON) ํ˜•์‹์˜ ๋ฌธ์„œ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋ฉฐ, ๋‹ค๋ฅธ DB์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฟผ๋ฆฌํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฑฐ์˜ ์œ ์‚ฌํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ ๋ฌธ์„œ๋ฅผ ์ฟผ๋ฆฌํ•˜๊ฑฐ๋‚˜ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ชฝ๊ณ DB๋ฅผ ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์€ JPA๋ฅผ ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ ํฌ๊ฒŒ ๋‹ค๋ฅด์ง€ ์•Š๋‹ค.

์ฆ‰, ๋„๋ฉ”์ธ ํƒ€์ž…์„ ๋ฌธ์„œ ๊ตฌ์กฐ๋กœ ๋งคํ•‘ํ•˜๋Š” ์• ๋…ธํ…Œ์ด์…˜์„ ๋„๋ฉ”์ธ ํด๋ž˜์Šค์— ์ง€์ •ํ•œ๋‹ค.

๊ทธ๋ฆฌ๊ณ  JPA์™€ ๋™์ผํ•œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ชจ๋ธ์„ ๋”ฐ๋ฅด๋Š” Repository Interface๋ฅผ ์ž‘์„ฑํ•˜๋ฉด ๋œ๋‹ค.


์Šคํ”„๋ง ๋ฐ์ดํ„ฐ ๋ชฝ๊ณ DB ํ™œ์„ฑํ™”ํ•˜๊ธฐ ๋ฆฌ์•กํ‹ฐ๋ธŒ ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ ๋ชฝ๊ณ DB ์Šคํƒ€ํ„ฐ ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•˜์ž.

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>

์ด๋ ‡๊ฒŒ ๋นŒ๋“œ์— ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•˜๋ฉด ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ ๋ฆฌ์•กํ‹ฐ๋ธŒ ๋ชฝ๊ณ DB ์ง€์›์„ ํ™œ์„ฑํ™”ํ•˜๋Š” ์ž๋™-๊ตฌ์„ฑ์ด ์ˆ˜ํ–‰๋œ๋‹ค. (Repository Interface ์ž๋™ ๊ตฌํ˜„)

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ ๋ชฝ๊ณ DB๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ 27017 ํฌํŠธ๋ฅผ ๋ฆฌ์Šค๋‹ํ•œ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ํ…Œ์ŠคํŠธ์™€ ๊ฐœ๋ฐœ์— ํŽธ๋ฆฌํ•˜๋„๋ก in-memory ๋‚ด์žฅ ๋ชฝ๊ณ DB๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค. ์•„๋ž˜์™€ ๊ฐ™์ด Flapdoodle ์˜์กด์„ฑ์„ ๋นŒ๋“œ์— ์ถ”๊ฐ€ํ•œ๋‹ค.

<dependency>
   <groupId>de.flapdoodle.embed</groupId>
   <artifactId>de.flapdoodle.embed.mongo</artifactId>
</dependency>

๋„๋ฉ”์ธ ํƒ€์ž…์„ ๋ฌธ์„œ๋กœ ๋งคํ•‘ํ•˜๊ธฐ ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ ๋ชฝ๊ณ DB๋Š” ๋ชฝ๊ณ DB์— ์ €์žฅ๋˜๋Š” ๋ฌธ์„œ ๊ตฌ์กฐ๋กœ ๋„๋ฉ”์ธ ํƒ€์ž…์„ ๋งคํ•‘ํ•˜๋Š” ๋ฐ ์œ ์šฉํ•œ ์• ๋…ธํ…Œ์ด์…˜๋“ค์„ ์ œ๊ณตํ•œ๋‹ค.

๊ทธ ์ค‘ ์•„๋ž˜ 3๊ฐœ๊ฐ€ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ๋œ๋‹ค.

@Id : ์ง€์ •๋œ ์†์„ฑ์„ ๋ฌธ์„œ ID๋กœ ์ง€์ •ํ•œ๋‹ค. Serializable ํƒ€์ž…์ธ ์–ด๋–ค ์†์„ฑ์—๋„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

@Document : ์ง€์ •๋œ ๋„๋ฉ”์ธ ํƒ€์ž…์„ ๋ชฝ๊ณ DB์— ์ €์žฅ๋˜๋Š” ๋ฌธ์„œ๋กœ ์„ ์–ธํ•œ๋‹ค.

@Field : ๋ชฝ๊ณ DB์˜ ๋ฌธ์„œ์— ์†์„ฑ์„ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด ํ•„๋“œ ์ด๋ฆ„(๊ณผ ์„ ํƒ์ ์œผ๋กœ ์ˆœ์„œ)์„ ์ง€์ •ํ•œ๋‹ค. 
@Field๊ฐ€ ์ง€์ •๋˜์ง€ ์•Š์€ ๋„๋ฉ”์ธ ํƒ€์ž…์˜ ์†์„ฑ๋“ค์€ ํ•„๋“œ ์ด๋ฆ„๊ณผ ์†์„ฑ ์ด๋ฆ„์„ ๊ฐ™์€ ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผ๋œ๋‹ค.

์ด ์• ๋…ธํ…Œ์ด์…˜๋“ค์„ ์ด์šฉํ•˜์—ฌ Ingredient ํด๋ž˜์Šค๋ฅผ ์ž‘์„ฑํ•œ๋‹ค.

package tacos;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import lombok.AccessLevel;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;

@Data
@RequiredArgsConstructor
@NoArgsConstructor(access=AccessLevel.PRIVATE, force=true)
@Document
public class Ingredient {
  
  @Id
  private final String id;
  private final String name;
  private final Type type;
  
  public static enum Type {
    WRAP, PROTEIN, VEGGIES, CHEESE, SAUCE
  }

}

๋‹ค์Œ์€ Taco์˜ ๋ชฝ๊ณ DB ๋งคํ•‘์„ ์•Œ์•„๋ณด์ž.

package tacos;

import java.util.Date;
import java.util.List;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.rest.core.annotation.RestResource;

import lombok.Data;

@Data
@RestResource(rel = "tacos", path = "tacos")
@Document
public class Taco {

  @Id
  private String id;
  
  @NotNull
  @Size(min = 5, message = "Name must be at least 5 characters long")
  private String name;
  
  private Date createdAt = new Date();
  
  @Size(min=1, message="You must choose at least 1 ingredient")
  private List<Ingredient> ingredients;

}

ID๋กœ String ํƒ€์ž…์˜ ์†์„ฑ์„ ์‚ฌ์šฉํ•˜๋ฉด ์ด ์†์„ฑ๊ฐ’์ด DB์— ์ €์žฅ๋  ๋•Œ ๋ชฝ๊ณ DB๊ฐ€ ์ž๋™์œผ๋กœ ID ๊ฐ’์„ ์ง€์ •ํ•ด์ค€๋‹ค. (null์ผ ๊ฒฝ์šฐ์— ํ•œํ•จ)

๋ชฝ๊ณ DB ์• ๋…ธํ…Œ์ด์…˜์ด ์ง€์ •๋œ ๋‹ค์Œ์˜ Order ํด๋ž˜์Šค๋ฅผ ์‚ดํŽด๋ณด์ž.

package tacos;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import lombok.Data;
import org.springframework.data.mongodb.core.mapping.Field;

@Data
@Document
public class Order implements Serializable {
  private static final long serialVersionUID = 1L;

  @Id
  private String id;
  private Date placedAt = new Date();

  @Field("customer")
  private User user;

  private String deliveryName;

  private String deliveryStreet;

  private String deliveryCity;

  private String deliveryState;

  private String deliveryZip;

  private String ccNumber;

  private String ccExpiration;

  private String ccCVV;


  private List<Taco> tacos = new ArrayList<>();

  public void addDesign(Taco design) {
    this.tacos.add(design);
  }

}

customer ์—ด์„ ๋ฌธ์„œ์— ์ €์žฅํ•œ๋‹ค๋Š” ๊ฒƒ์„ ๋‚˜ํƒ€๋‚ด๊ธฐ ์œ„ํ•ด์„œ user ์†์„ฑ์— @FIeld๋ฅผ ์ง€์ •ํ•˜์˜€๋‹ค.

๋‹ค์Œ์œผ๋กœ User ๋„๋ฉ”์ธ ํด๋ž˜์Šค๋ฅผ ์ž‘์„ฑํ•˜์ž.

package tacos;
import java.util.Arrays;
import java.util.Collection;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.
                                          SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import lombok.AccessLevel;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;

@Data
@NoArgsConstructor(access=AccessLevel.PRIVATE, force=true)
@RequiredArgsConstructor
@Document
public class User implements UserDetails {

  private static final long serialVersionUID = 1L;

  @Id
  private String id;
  
  private final String username;
  
  private final String password;
  private final String fullname;
  private final String street;
  private final String city;
  private final String state;
  private final String zip;
  private final String phoneNumber;
  private final String email;
  
  @Override
  public Collection<? extends GrantedAuthority> getAuthorities() {
    return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));
  }

  @Override
  public boolean isAccountNonExpired() {
    return true;
  }

  @Override
  public boolean isAccountNonLocked() {
    return true;
  }

  @Override
  public boolean isCredentialsNonExpired() {
    return true;
  }

  @Override
  public boolean isEnabled() {
    return true;
  }

}

์ด์ œ๋Š” Repository Interface๋ฅผ ์ž‘์„ฑํ•˜์ž. ** ๋ฆฌ์•กํ‹ฐ๋ธŒ ๋ชฝ๊ณ DB ๋ฆฌํผ์ง€ํ„ฐ๋ฆฌ ์ธํ„ฐํŽ˜์ด์Šค ์ž‘์„ฑํ•˜๊ธฐ ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ ๋ชฝ๊ณ DB๋Š” ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ๊ณผ ์œ ์‚ฌํ•œ ์ž๋™ Repository ์ง€์›์„ ์ œ๊ณตํ•œ๋‹ค.

๋ชฝ๊ณ DB์˜ Reactvie Repository๋ฅผ ์ž‘์„ฑํ•  ๋•Œ๋Š” ReactiveCrudRepository๋‚˜ ReactiveMongoRepository๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋‹ค.

ReactiveCrudRepository๋Š” ์ƒˆ๋กœ์šด ๋ฌธ์„œ๋‚˜ ๊ธฐ์กด ๋ฌธ์„œ์˜ save() ๋ฉ”์„œ๋“œ์— ์˜์กดํ•˜๋Š” ๋ฐ˜๋ฉด, ReactiveMongoRepository๋Š” ์ƒˆ๋กœ์šด ๋ฌธ์„œ์˜ ์ €์žฅ์— ์ตœ์ ํ™”๋œ ์†Œ์ˆ˜์˜ ํŠน๋ณ„ํ•œ insert() ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

์šฐ์„ , Ingredient ๊ฐ์ฒด๋ฅผ ๋ฌธ์„œ๋กœ ์ €์žฅํ•˜๋Š” Repository๋ฅผ ์ •์˜ํ•˜์ž.

์‹์žฌ๋ฃŒ๋ฅผ ์ €์žฅํ•œ ๋ฌธ์„œ๋Š” ์ดˆ๊ธฐ์— ์‹์žฌ๋ฃŒ ๋ฐ์ดํ„ฐ๋ฅผ DB์— ์ถ”๊ฐ€ํ•  ๋•Œ ์ƒ์„ฑ๋˜๋ฉฐ, ์ด์™ธ์—๋Š” ๊ฑฐ์˜ ์ถ”๊ฐ€๋˜์ง€ ์•Š๋Š”๋‹ค.

๋”ฐ๋ผ์„œ ์ƒˆ๋กœ์šด ๋ฌธ์„œ์˜ ์ €์žฅ์— ์ตœ์ ํ™”๋œ ReactiveMongoRepository ๋ณด๋‹ค๋Š” ReactiveCrudRepository๋ฅผ ํ™•์žฅํ•ด์•ผ ํ•œ๋‹ค.

package tacos.data;

import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import org.springframework.web.bind.annotation.CrossOrigin;

import tacos.Ingredient;

@CrossOrigin(origins="*")
public interface IngredientRepository extends ReactiveCrudRepository<Ingredient, String> {

}

IngredientRepository๋Š” ReactiveRepository์ด๋ฏ€๋กœ ์ด๊ฒƒ์˜ ๋ฉ”์„œ๋“œ๋Š” ๊ทธ๋ƒฅ ๋„๋ฉ”์ธ ํƒ€์ž…์ด๋‚˜ ์ปฌ๋ ‰์…˜์ด ์•„๋‹Œ Flux๋‚˜ Mono ํƒ€์ž…์œผ๋กœ ๋„๋ฉ”์ธ ๊ฐ์ฒด๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, findAll() ๋ฉ”์„œ๋“œ๋Š” Iterable ๋Œ€์‹  Flux๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

๊ทธ๋ฆฌ๊ณ  findById() ๋ฉ”์„œ๋“œ๋Š” Optional ๋Œ€์‹  Mono๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

๋”ฐ๋ผ์„œ ์ด Reactive Repository๋Š” ์—”๋“œ-to-์—”๋“œ Reactive flow์˜ ์ผ๋ถ€๊ฐ€ ๋  ์ˆ˜์žˆ๋‹ค.


๋‹ค์Œ์€ ๋ชฝ๊ณ DB์˜ ๋ฌธ์„œ๋กœ Taco ๊ฐ์ฒด๋ฅผ ์ €์žฅํ•˜๋Š” Repository๋ฅผ ์ •์˜ํ•˜์ž.

์ƒ‰์žฌ๋ฃŒ ๋ฌธ์„œ์™€๋Š” ๋‹ค๋ฅด๊ฒŒ ํƒ€์ฝ” ๋ฌธ์„œ๋Š” ์ž์ฃผ ์ƒ์„ฑ๋œ๋‹ค.

๋”ฐ๋ผ์„œ ReactiveMongoRepository์˜ ์ตœ์ ํ™”๋œ insert() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

package tacos.data;

import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import reactor.core.publisher.Flux;
import tacos.Taco;


public interface TacoRepository extends ReactiveMongoRepository<Taco, String> {
    Flux<Taco> findByOrderByCreatedAtDesc();
}

ReactiveCrudRepository์— ๋น„ํ•ด ReactiveMongoRepository๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ์˜ ์œ ์ผํ•œ ๋‹จ์ ์€ ๋ฐ”๋กœ ๋ชฝ๊ณ DB์— ํŠนํ™”๋˜์–ด ์žˆ๋‹ค๋Š” ์ ์ด๋‹ค.

๊ทธ๋ž˜์„œ ๋‹ค๋ฅธ DB์—๋Š” ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค. ๋”ฐ๋ผ์„œ ์ด ๋‹จ์ ์„ ๊ฐ์•ˆํ•˜๊ณ  ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

TacoRepository์—๋Š” ์ƒˆ๋กœ์šด ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ๋‹ค. ์ด ๋ฉ”์„œ๋“œ๋Š” ์ตœ๊ทผ ์ƒ์„ฑ๋œ ํƒ€์ฝ”๋“ค์˜ ๋ฆฌ์ŠคํŠธ๋ฅผ ์กฐํšŒํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

findByOrderByCreatedAtDesc()๋Š” Flux๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

๋”ฐ๋ผ์„œ take() ์˜คํผ๋ ˆ์ด์…˜์„ ์ ์šฉํ•˜์—ฌ Flux์—์„œ ๋ฐœํ–‰๋˜๋Š” ์ฒ˜์Œ 12๊ฐœ์˜ Taco ๊ฐ์ฒด๋งŒ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ์ตœ๊ทผ ์ƒ์„ฑ๋œ ํƒ€์ฝ”๋“ค์„ ๋ณด์—ฌ์ฃผ๋Š” ์ปจํŠธ๋กค๋Ÿฌ์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

Flux<Taco> recents = repo.findByOrderByCreatedAtDesc().take(12);

์ด๋ฒˆ์—” OrderRepository ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์•Œ์•„๋ณด์ž.

package tacos.data;

import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import tacos.Order;

public interface OrderRepository extends ReactiveMongoRepository<Order, String> {

}

Order ๋ฌธ์„œ๋Š” ์ž์ฃผ ์ƒ์„ฑ๋  ๊ฒƒ์ด๋‹ค. ๋”ฐ๋ผ์„œ ReactiveMongoRepository๋ฅผ ํ™•์žฅํ•œ๋‹ค.

package tacos.data;

import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import reactor.core.publisher.Mono;
import tacos.User;

public interface UserRepository extends ReactiveMongoRepository<User, String> {

  Mono<User> findByUsername(String username);

}
โš ๏ธ **GitHub.com Fallback** โš ๏ธ